Merge tag 'mmc-v4.8' of git://git.linaro.org/people/ulf.hansson/mmc
Pull MMC updates from Ulf Hansson: "MMC core: - A couple of changes to improve the support for erase/discard/trim cmds - Add eMMC HS400 enhanced strobe support - Show OCR and DSR registers in SYSFS for MMC/SD cards - Correct and improve busy detection logic for MMC switch (CMD6) cmds - Disable HPI cmds for certain broken Hynix eMMC cards - Allow MMC hosts to specify non-support for SD and MMC cmds - Some minor additional fixes MMC host: - sdhci: Re-works, fixes and clean-ups - sdhci: Add HW auto re-tuning support - sdhci: Re-factor code to prepare for adding support for eMMC CMDQ - sdhci-esdhc-imx: Fixes and clean-ups - sdhci-esdhc-imx: Update system PM support - sdhci-esdhc-imx: Enable HW auto re-tuning - sdhci-bcm2835: Remove driver as sdhci-iproc is used instead - sdhci-brcmstb: Add new driver for Broadcom BRCMSTB SoCs - sdhci-msm: Add support for UHS cards - sdhci-tegra: Improve support for UHS cards - sdhci-of-arasan: Update phy support for Rockchip SoCs - sdhci-of-arasan: Deploy enhanced strobe support - dw_mmc: Some fixes and clean-ups - dw_mmc: Enable support for erase/discard/trim cmds - dw_mmc: Enable CMD23 support - mediatek: Some fixes related to the eMMC HS400 support - sh_mmcif: Improve support for HW busy detection - rtsx_pci: Enable support for erase/discard/trim cmds" * tag 'mmc-v4.8' of git://git.linaro.org/people/ulf.hansson/mmc: (135 commits) mmc: rtsx_pci: Remove deprecated create_singlethread_workqueue mmc: rtsx_pci: Enable MMC_CAP_ERASE to allow erase/discard/trim requests mmc: rtsx_pci: Use the provided busy timeout from the mmc core mmc: sdhci-pltfm: Drop define for SDHCI_PLTFM_PMOPS mmc: sdhci-pltfm: Convert to use the SET_SYSTEM_SLEEP_PM_OPS mmc: sdhci-pltfm: Make sdhci_pltfm_suspend|resume() static mmc: sdhci-esdhc-imx: Use common sdhci_suspend|resume_host() mmc: sdhci-esdhc-imx: Assign system PM ops within #ifdef CONFIG_PM_SLEEP mmc: sdhci-sirf: Remove non needed #ifdef CONFIG_PM* for dev_pm_ops mmc: sdhci-s3c: Remove non needed #ifdef CONFIG_PM for dev_pm_ops mmc: sdhci-pxav3: Remove non needed #ifdef CONFIG_PM for dev_pm_ops mmc: sdhci-of-esdhc: Simplify code by using SIMPLE_DEV_PM_OPS mmc: sdhci-acpi: Simplify code by using SET_SYSTEM_SLEEP_PM_OPS mmc: sdhci-pci-core: Simplify code by using SET_SYSTEM_SLEEP_PM_OPS mmc: Change the max discard sectors and erase response when HW busy detect phy: rockchip-emmc: Wait even longer for the DLL to lock phy: rockchip-emmc: Be tolerant to card clock of 0 in power on mmc: sdhci-of-arasan: Revert: Always power the PHY off/on when clock changes mmc: sdhci-msm: Add support for UHS cards mmc: sdhci-msm: Add set_uhs_signaling() implementation ...
This commit is contained in:
@@ -9,8 +9,12 @@ Device Tree Bindings for the Arasan SDHCI Controller
|
|||||||
[4] Documentation/devicetree/bindings/phy/phy-bindings.txt
|
[4] Documentation/devicetree/bindings/phy/phy-bindings.txt
|
||||||
|
|
||||||
Required Properties:
|
Required Properties:
|
||||||
- compatible: Compatibility string. Must be 'arasan,sdhci-8.9a' or
|
- compatible: Compatibility string. One of:
|
||||||
'arasan,sdhci-4.9a' or 'arasan,sdhci-5.1'
|
- "arasan,sdhci-8.9a": generic Arasan SDHCI 8.9a PHY
|
||||||
|
- "arasan,sdhci-4.9a": generic Arasan SDHCI 4.9a PHY
|
||||||
|
- "arasan,sdhci-5.1": generic Arasan SDHCI 5.1 PHY
|
||||||
|
- "rockchip,rk3399-sdhci-5.1", "arasan,sdhci-5.1": rk3399 eMMC PHY
|
||||||
|
For this device it is strongly suggested to include arasan,soc-ctl-syscon.
|
||||||
- reg: From mmc bindings: Register location and length.
|
- reg: From mmc bindings: Register location and length.
|
||||||
- clocks: From clock bindings: Handles to clock inputs.
|
- clocks: From clock bindings: Handles to clock inputs.
|
||||||
- clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb"
|
- clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb"
|
||||||
@@ -22,6 +26,17 @@ Required Properties for "arasan,sdhci-5.1":
|
|||||||
- phys: From PHY bindings: Phandle for the Generic PHY for arasan.
|
- phys: From PHY bindings: Phandle for the Generic PHY for arasan.
|
||||||
- phy-names: MUST be "phy_arasan".
|
- phy-names: MUST be "phy_arasan".
|
||||||
|
|
||||||
|
Optional Properties:
|
||||||
|
- arasan,soc-ctl-syscon: A phandle to a syscon device (see ../mfd/syscon.txt)
|
||||||
|
used to access core corecfg registers. Offsets of registers in this
|
||||||
|
syscon are determined based on the main compatible string for the device.
|
||||||
|
- clock-output-names: If specified, this will be the name of the card clock
|
||||||
|
which will be exposed by this device. Required if #clock-cells is
|
||||||
|
specified.
|
||||||
|
- #clock-cells: If specified this should be the value <0>. With this property
|
||||||
|
in place we will export a clock representing the Card Clock. This clock
|
||||||
|
is expected to be consumed by our PHY. You must also specify
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
sdhci@e0100000 {
|
sdhci@e0100000 {
|
||||||
compatible = "arasan,sdhci-8.9a";
|
compatible = "arasan,sdhci-8.9a";
|
||||||
@@ -42,3 +57,19 @@ Example:
|
|||||||
phys = <&emmc_phy>;
|
phys = <&emmc_phy>;
|
||||||
phy-names = "phy_arasan";
|
phy-names = "phy_arasan";
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
|
sdhci: sdhci@fe330000 {
|
||||||
|
compatible = "rockchip,rk3399-sdhci-5.1", "arasan,sdhci-5.1";
|
||||||
|
reg = <0x0 0xfe330000 0x0 0x10000>;
|
||||||
|
interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
clocks = <&cru SCLK_EMMC>, <&cru ACLK_EMMC>;
|
||||||
|
clock-names = "clk_xin", "clk_ahb";
|
||||||
|
arasan,soc-ctl-syscon = <&grf>;
|
||||||
|
assigned-clocks = <&cru SCLK_EMMC>;
|
||||||
|
assigned-clock-rates = <200000000>;
|
||||||
|
clock-output-names = "emmc_cardclock";
|
||||||
|
phys = <&emmc_phy>;
|
||||||
|
phy-names = "phy_arasan";
|
||||||
|
#clock-cells = <0>;
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
@@ -1,18 +0,0 @@
|
|||||||
Broadcom BCM2835 SDHCI controller
|
|
||||||
|
|
||||||
This file documents differences between the core properties described
|
|
||||||
by mmc.txt and the properties that represent the BCM2835 controller.
|
|
||||||
|
|
||||||
Required properties:
|
|
||||||
- compatible : Should be "brcm,bcm2835-sdhci".
|
|
||||||
- clocks : The clock feeding the SDHCI controller.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
sdhci: sdhci {
|
|
||||||
compatible = "brcm,bcm2835-sdhci";
|
|
||||||
reg = <0x7e300000 0x100>;
|
|
||||||
interrupts = <2 30>;
|
|
||||||
clocks = <&clk_mmc>;
|
|
||||||
bus-width = <4>;
|
|
||||||
};
|
|
36
Documentation/devicetree/bindings/mmc/brcm,bcm7425-sdhci.txt
Normal file
36
Documentation/devicetree/bindings/mmc/brcm,bcm7425-sdhci.txt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
* BROADCOM BRCMSTB/BMIPS SDHCI Controller
|
||||||
|
|
||||||
|
This file documents differences between the core properties in mmc.txt
|
||||||
|
and the properties used by the sdhci-brcmstb driver.
|
||||||
|
|
||||||
|
NOTE: The driver disables all UHS speed modes by default and depends
|
||||||
|
on Device Tree properties to enable them for SoC/Board combinations
|
||||||
|
that support them.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: "brcm,bcm7425-sdhci"
|
||||||
|
|
||||||
|
Refer to clocks/clock-bindings.txt for generic clock consumer properties.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
sdhci@f03e0100 {
|
||||||
|
compatible = "brcm,bcm7425-sdhci";
|
||||||
|
reg = <0xf03e0000 0x100>;
|
||||||
|
interrupts = <0x0 0x26 0x0>;
|
||||||
|
sdhci,auto-cmd12;
|
||||||
|
clocks = <&sw_sdio>;
|
||||||
|
sd-uhs-sdr50;
|
||||||
|
sd-uhs-ddr50;
|
||||||
|
};
|
||||||
|
|
||||||
|
sdhci@f03e0300 {
|
||||||
|
non-removable;
|
||||||
|
bus-width = <0x8>;
|
||||||
|
compatible = "brcm,bcm7425-sdhci";
|
||||||
|
reg = <0xf03e0200 0x100>;
|
||||||
|
interrupts = <0x0 0x27 0x0>;
|
||||||
|
sdhci,auto-cmd12;
|
||||||
|
clocks = <sw_sdio>;
|
||||||
|
mmc-hs200-1_8v;
|
||||||
|
};
|
@@ -28,6 +28,8 @@ Optional properties:
|
|||||||
transparent level shifters on the outputs of the controller. Two cells are
|
transparent level shifters on the outputs of the controller. Two cells are
|
||||||
required, first cell specifies minimum slot voltage (mV), second cell
|
required, first cell specifies minimum slot voltage (mV), second cell
|
||||||
specifies maximum slot voltage (mV). Several ranges could be specified.
|
specifies maximum slot voltage (mV). Several ranges could be specified.
|
||||||
|
- fsl,tuning-start-tap: Specify the start dealy cell point when send first CMD19
|
||||||
|
in tuning procedure.
|
||||||
- fsl,tuning-step: Specify the increasing delay cell steps in tuning procedure.
|
- fsl,tuning-step: Specify the increasing delay cell steps in tuning procedure.
|
||||||
The uSDHC use one delay cell as default increasing step to do tuning process.
|
The uSDHC use one delay cell as default increasing step to do tuning process.
|
||||||
This property allows user to change the tuning step to more than one delay
|
This property allows user to change the tuning step to more than one delay
|
||||||
|
@@ -46,8 +46,12 @@ Optional properties:
|
|||||||
- mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported
|
- mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported
|
||||||
- mmc-hs400-1_8v: eMMC HS400 mode(1.8V I/O) is supported
|
- mmc-hs400-1_8v: eMMC HS400 mode(1.8V I/O) is supported
|
||||||
- mmc-hs400-1_2v: eMMC HS400 mode(1.2V I/O) is supported
|
- mmc-hs400-1_2v: eMMC HS400 mode(1.2V I/O) is supported
|
||||||
|
- mmc-hs400-enhanced-strobe: eMMC HS400 enhanced strobe mode is supported
|
||||||
- dsr: Value the card's (optional) Driver Stage Register (DSR) should be
|
- dsr: Value the card's (optional) Driver Stage Register (DSR) should be
|
||||||
programmed with. Valid range: [0 .. 0xffff].
|
programmed with. Valid range: [0 .. 0xffff].
|
||||||
|
- no-sdio: controller is limited to send sdio cmd during initialization
|
||||||
|
- no-sd: controller is limited to send sd cmd during initialization
|
||||||
|
- no-mmc: controller is limited to send mmc cmd during initialization
|
||||||
|
|
||||||
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
|
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
|
||||||
polarity properties, we have to fix the meaning of the "normal" and "inverted"
|
polarity properties, we have to fix the meaning of the "normal" and "inverted"
|
||||||
|
@@ -7,6 +7,13 @@ Required properties:
|
|||||||
- reg: PHY register address offset and length in "general
|
- reg: PHY register address offset and length in "general
|
||||||
register files"
|
register files"
|
||||||
|
|
||||||
|
Optional clocks using the clock bindings (see ../clock/clock-bindings.txt),
|
||||||
|
specified by name:
|
||||||
|
- clock-names: Should contain "emmcclk". Although this is listed as optional
|
||||||
|
(because most boards can get basic functionality without having
|
||||||
|
access to it), it is strongly suggested.
|
||||||
|
- clocks: Should have a phandle to the card clock exported by the SDHCI driver.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|
||||||
@@ -20,6 +27,8 @@ grf: syscon@ff770000 {
|
|||||||
emmcphy: phy@f780 {
|
emmcphy: phy@f780 {
|
||||||
compatible = "rockchip,rk3399-emmc-phy";
|
compatible = "rockchip,rk3399-emmc-phy";
|
||||||
reg = <0xf780 0x20>;
|
reg = <0xf780 0x20>;
|
||||||
|
clocks = <&sdhci>;
|
||||||
|
clock-names = "emmcclk";
|
||||||
#phy-cells = <0>;
|
#phy-cells = <0>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -28,6 +28,8 @@ All attributes are read-only.
|
|||||||
preferred_erase_size Preferred erase size
|
preferred_erase_size Preferred erase size
|
||||||
raw_rpmb_size_mult RPMB partition size
|
raw_rpmb_size_mult RPMB partition size
|
||||||
rel_sectors Reliable write sector count
|
rel_sectors Reliable write sector count
|
||||||
|
ocr Operation Conditions Register
|
||||||
|
dsr Driver Stage Register
|
||||||
|
|
||||||
Note on Erase Size and Preferred Erase Size:
|
Note on Erase Size and Preferred Erase Size:
|
||||||
|
|
||||||
|
@@ -7863,6 +7863,7 @@ M: Ulf Hansson <ulf.hansson@linaro.org>
|
|||||||
L: linux-mmc@vger.kernel.org
|
L: linux-mmc@vger.kernel.org
|
||||||
T: git git://git.linaro.org/people/ulf.hansson/mmc.git
|
T: git git://git.linaro.org/people/ulf.hansson/mmc.git
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
F: Documentation/devicetree/bindings/mmc/
|
||||||
F: drivers/mmc/
|
F: drivers/mmc/
|
||||||
F: include/linux/mmc/
|
F: include/linux/mmc/
|
||||||
F: include/uapi/linux/mmc/
|
F: include/uapi/linux/mmc/
|
||||||
@@ -10355,6 +10356,13 @@ F: tools/testing/selftests/seccomp/*
|
|||||||
K: \bsecure_computing
|
K: \bsecure_computing
|
||||||
K: \bTIF_SECCOMP\b
|
K: \bTIF_SECCOMP\b
|
||||||
|
|
||||||
|
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) Broadcom BRCMSTB DRIVER
|
||||||
|
M: Al Cooper <alcooperx@gmail.com>
|
||||||
|
L: linux-mmc@vger.kernel.org
|
||||||
|
L: bcm-kernel-feedback-list@broadcom.com
|
||||||
|
S: Maintained
|
||||||
|
F: drivers/mmc/host/sdhci-brcmstb*
|
||||||
|
|
||||||
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) SAMSUNG DRIVER
|
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) SAMSUNG DRIVER
|
||||||
M: Ben Dooks <ben-linux@fluff.org>
|
M: Ben Dooks <ben-linux@fluff.org>
|
||||||
M: Jaehoon Chung <jh80.chung@samsung.com>
|
M: Jaehoon Chung <jh80.chung@samsung.com>
|
||||||
|
@@ -1801,8 +1801,7 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
|
|||||||
do_data_tag = (card->ext_csd.data_tag_unit_size) &&
|
do_data_tag = (card->ext_csd.data_tag_unit_size) &&
|
||||||
(prq->cmd_flags & REQ_META) &&
|
(prq->cmd_flags & REQ_META) &&
|
||||||
(rq_data_dir(prq) == WRITE) &&
|
(rq_data_dir(prq) == WRITE) &&
|
||||||
((brq->data.blocks * brq->data.blksz) >=
|
blk_rq_bytes(prq) >= card->ext_csd.data_tag_unit_size;
|
||||||
card->ext_csd.data_tag_unit_size);
|
|
||||||
/* Argument of CMD23 */
|
/* Argument of CMD23 */
|
||||||
packed_cmd_hdr[(i * 2)] = cpu_to_le32(
|
packed_cmd_hdr[(i * 2)] = cpu_to_le32(
|
||||||
(do_rel_wr ? MMC_CMD23_ARG_REL_WR : 0) |
|
(do_rel_wr ? MMC_CMD23_ARG_REL_WR : 0) |
|
||||||
@@ -1977,8 +1976,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
|||||||
* When 4KB native sector is enabled, only 8 blocks
|
* When 4KB native sector is enabled, only 8 blocks
|
||||||
* multiple read or write is allowed
|
* multiple read or write is allowed
|
||||||
*/
|
*/
|
||||||
if ((brq->data.blocks & 0x07) &&
|
if (mmc_large_sector(card) &&
|
||||||
(card->ext_csd.data_sector_size == 4096)) {
|
!IS_ALIGNED(blk_rq_sectors(rqc), 8)) {
|
||||||
pr_err("%s: Transfer size is not 4KB sector size aligned\n",
|
pr_err("%s: Transfer size is not 4KB sector size aligned\n",
|
||||||
req->rq_disk->disk_name);
|
req->rq_disk->disk_name);
|
||||||
mq_rq = mq->mqrq_cur;
|
mq_rq = mq->mqrq_cur;
|
||||||
@@ -2501,12 +2500,6 @@ force_ro_fail:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CID_MANFID_SANDISK 0x2
|
|
||||||
#define CID_MANFID_TOSHIBA 0x11
|
|
||||||
#define CID_MANFID_MICRON 0x13
|
|
||||||
#define CID_MANFID_SAMSUNG 0x15
|
|
||||||
#define CID_MANFID_KINGSTON 0x70
|
|
||||||
|
|
||||||
static const struct mmc_fixup blk_fixups[] =
|
static const struct mmc_fixup blk_fixups[] =
|
||||||
{
|
{
|
||||||
MMC_FIXUP("SEM02G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
MMC_FIXUP("SEM02G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||||
|
@@ -332,12 +332,13 @@ int mmc_add_card(struct mmc_card *card)
|
|||||||
mmc_card_ddr52(card) ? "DDR " : "",
|
mmc_card_ddr52(card) ? "DDR " : "",
|
||||||
type);
|
type);
|
||||||
} else {
|
} else {
|
||||||
pr_info("%s: new %s%s%s%s%s card at address %04x\n",
|
pr_info("%s: new %s%s%s%s%s%s card at address %04x\n",
|
||||||
mmc_hostname(card->host),
|
mmc_hostname(card->host),
|
||||||
mmc_card_uhs(card) ? "ultra high speed " :
|
mmc_card_uhs(card) ? "ultra high speed " :
|
||||||
(mmc_card_hs(card) ? "high speed " : ""),
|
(mmc_card_hs(card) ? "high speed " : ""),
|
||||||
mmc_card_hs400(card) ? "HS400 " :
|
mmc_card_hs400(card) ? "HS400 " :
|
||||||
(mmc_card_hs200(card) ? "HS200 " : ""),
|
(mmc_card_hs200(card) ? "HS200 " : ""),
|
||||||
|
mmc_card_hs400es(card) ? "Enhanced strobe " : "",
|
||||||
mmc_card_ddr52(card) ? "DDR " : "",
|
mmc_card_ddr52(card) ? "DDR " : "",
|
||||||
uhs_bus_speed_mode, type, card->rca);
|
uhs_bus_speed_mode, type, card->rca);
|
||||||
}
|
}
|
||||||
|
@@ -1127,6 +1127,15 @@ void mmc_set_initial_state(struct mmc_host *host)
|
|||||||
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
||||||
host->ios.timing = MMC_TIMING_LEGACY;
|
host->ios.timing = MMC_TIMING_LEGACY;
|
||||||
host->ios.drv_type = 0;
|
host->ios.drv_type = 0;
|
||||||
|
host->ios.enhanced_strobe = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure we are in non-enhanced strobe mode before we
|
||||||
|
* actually enable it in ext_csd.
|
||||||
|
*/
|
||||||
|
if ((host->caps2 & MMC_CAP2_HS400_ES) &&
|
||||||
|
host->ops->hs400_enhanced_strobe)
|
||||||
|
host->ops->hs400_enhanced_strobe(host, &host->ios);
|
||||||
|
|
||||||
mmc_set_ios(host);
|
mmc_set_ios(host);
|
||||||
}
|
}
|
||||||
@@ -1925,17 +1934,15 @@ void mmc_init_erase(struct mmc_card *card)
|
|||||||
* to that size and alignment.
|
* to that size and alignment.
|
||||||
*
|
*
|
||||||
* For SD cards that define Allocation Unit size, limit erases to one
|
* For SD cards that define Allocation Unit size, limit erases to one
|
||||||
* Allocation Unit at a time. For MMC cards that define High Capacity
|
* Allocation Unit at a time.
|
||||||
* Erase Size, whether it is switched on or not, limit to that size.
|
* For MMC, have a stab at ai good value and for modern cards it will
|
||||||
* Otherwise just have a stab at a good value. For modern cards it
|
* end up being 4MiB. Note that if the value is too small, it can end
|
||||||
* will end up being 4MiB. Note that if the value is too small, it
|
* up taking longer to erase. Also note, erase_size is already set to
|
||||||
* can end up taking longer to erase.
|
* High Capacity Erase Size if available when this function is called.
|
||||||
*/
|
*/
|
||||||
if (mmc_card_sd(card) && card->ssr.au) {
|
if (mmc_card_sd(card) && card->ssr.au) {
|
||||||
card->pref_erase = card->ssr.au;
|
card->pref_erase = card->ssr.au;
|
||||||
card->erase_shift = ffs(card->ssr.au) - 1;
|
card->erase_shift = ffs(card->ssr.au) - 1;
|
||||||
} else if (card->ext_csd.hc_erase_size) {
|
|
||||||
card->pref_erase = card->ext_csd.hc_erase_size;
|
|
||||||
} else if (card->erase_size) {
|
} else if (card->erase_size) {
|
||||||
sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >> 11;
|
sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >> 11;
|
||||||
if (sz < 128)
|
if (sz < 128)
|
||||||
@@ -2060,7 +2067,8 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
|||||||
unsigned int to, unsigned int arg)
|
unsigned int to, unsigned int arg)
|
||||||
{
|
{
|
||||||
struct mmc_command cmd = {0};
|
struct mmc_command cmd = {0};
|
||||||
unsigned int qty = 0;
|
unsigned int qty = 0, busy_timeout = 0;
|
||||||
|
bool use_r1b_resp = false;
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
@@ -2128,8 +2136,22 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
|||||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||||
cmd.opcode = MMC_ERASE;
|
cmd.opcode = MMC_ERASE;
|
||||||
cmd.arg = arg;
|
cmd.arg = arg;
|
||||||
|
busy_timeout = mmc_erase_timeout(card, arg, qty);
|
||||||
|
/*
|
||||||
|
* If the host controller supports busy signalling and the timeout for
|
||||||
|
* the erase operation does not exceed the max_busy_timeout, we should
|
||||||
|
* use R1B response. Or we need to prevent the host from doing hw busy
|
||||||
|
* detection, which is done by converting to a R1 response instead.
|
||||||
|
*/
|
||||||
|
if (card->host->max_busy_timeout &&
|
||||||
|
busy_timeout > card->host->max_busy_timeout) {
|
||||||
|
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||||
|
} else {
|
||||||
cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
|
cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
|
||||||
cmd.busy_timeout = mmc_erase_timeout(card, arg, qty);
|
cmd.busy_timeout = busy_timeout;
|
||||||
|
use_r1b_resp = true;
|
||||||
|
}
|
||||||
|
|
||||||
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_err("mmc_erase: erase error %d, status %#x\n",
|
pr_err("mmc_erase: erase error %d, status %#x\n",
|
||||||
@@ -2141,7 +2163,14 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
|||||||
if (mmc_host_is_spi(card->host))
|
if (mmc_host_is_spi(card->host))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
timeout = jiffies + msecs_to_jiffies(MMC_CORE_TIMEOUT_MS);
|
/*
|
||||||
|
* In case of when R1B + MMC_CAP_WAIT_WHILE_BUSY is used, the polling
|
||||||
|
* shall be avoided.
|
||||||
|
*/
|
||||||
|
if ((card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
timeout = jiffies + msecs_to_jiffies(busy_timeout);
|
||||||
do {
|
do {
|
||||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||||
cmd.opcode = MMC_SEND_STATUS;
|
cmd.opcode = MMC_SEND_STATUS;
|
||||||
@@ -2321,23 +2350,41 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card,
|
|||||||
unsigned int arg)
|
unsigned int arg)
|
||||||
{
|
{
|
||||||
struct mmc_host *host = card->host;
|
struct mmc_host *host = card->host;
|
||||||
unsigned int max_discard, x, y, qty = 0, max_qty, timeout;
|
unsigned int max_discard, x, y, qty = 0, max_qty, min_qty, timeout;
|
||||||
unsigned int last_timeout = 0;
|
unsigned int last_timeout = 0;
|
||||||
|
|
||||||
if (card->erase_shift)
|
if (card->erase_shift) {
|
||||||
max_qty = UINT_MAX >> card->erase_shift;
|
max_qty = UINT_MAX >> card->erase_shift;
|
||||||
else if (mmc_card_sd(card))
|
min_qty = card->pref_erase >> card->erase_shift;
|
||||||
|
} else if (mmc_card_sd(card)) {
|
||||||
max_qty = UINT_MAX;
|
max_qty = UINT_MAX;
|
||||||
else
|
min_qty = card->pref_erase;
|
||||||
|
} else {
|
||||||
max_qty = UINT_MAX / card->erase_size;
|
max_qty = UINT_MAX / card->erase_size;
|
||||||
|
min_qty = card->pref_erase / card->erase_size;
|
||||||
|
}
|
||||||
|
|
||||||
/* Find the largest qty with an OK timeout */
|
/*
|
||||||
|
* We should not only use 'host->max_busy_timeout' as the limitation
|
||||||
|
* when deciding the max discard sectors. We should set a balance value
|
||||||
|
* to improve the erase speed, and it can not get too long timeout at
|
||||||
|
* the same time.
|
||||||
|
*
|
||||||
|
* Here we set 'card->pref_erase' as the minimal discard sectors no
|
||||||
|
* matter what size of 'host->max_busy_timeout', but if the
|
||||||
|
* 'host->max_busy_timeout' is large enough for more discard sectors,
|
||||||
|
* then we can continue to increase the max discard sectors until we
|
||||||
|
* get a balance value.
|
||||||
|
*/
|
||||||
do {
|
do {
|
||||||
y = 0;
|
y = 0;
|
||||||
for (x = 1; x && x <= max_qty && max_qty - x >= qty; x <<= 1) {
|
for (x = 1; x && x <= max_qty && max_qty - x >= qty; x <<= 1) {
|
||||||
timeout = mmc_erase_timeout(card, arg, qty + x);
|
timeout = mmc_erase_timeout(card, arg, qty + x);
|
||||||
if (timeout > host->max_busy_timeout)
|
|
||||||
|
if (qty + x > min_qty &&
|
||||||
|
timeout > host->max_busy_timeout)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (timeout < last_timeout)
|
if (timeout < last_timeout)
|
||||||
break;
|
break;
|
||||||
last_timeout = timeout;
|
last_timeout = timeout;
|
||||||
@@ -2491,6 +2538,7 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
|
|||||||
|
|
||||||
mmc_go_idle(host);
|
mmc_go_idle(host);
|
||||||
|
|
||||||
|
if (!(host->caps2 & MMC_CAP2_NO_SD))
|
||||||
mmc_send_if_cond(host, host->ocr_avail);
|
mmc_send_if_cond(host, host->ocr_avail);
|
||||||
|
|
||||||
/* Order's important: probe SDIO, then SD, then MMC */
|
/* Order's important: probe SDIO, then SD, then MMC */
|
||||||
@@ -2498,8 +2546,11 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
|
|||||||
if (!mmc_attach_sdio(host))
|
if (!mmc_attach_sdio(host))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (!(host->caps2 & MMC_CAP2_NO_SD))
|
||||||
if (!mmc_attach_sd(host))
|
if (!mmc_attach_sd(host))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (!(host->caps2 & MMC_CAP2_NO_MMC))
|
||||||
if (!mmc_attach_mmc(host))
|
if (!mmc_attach_mmc(host))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@@ -148,7 +148,8 @@ static int mmc_ios_show(struct seq_file *s, void *data)
|
|||||||
str = "mmc HS200";
|
str = "mmc HS200";
|
||||||
break;
|
break;
|
||||||
case MMC_TIMING_MMC_HS400:
|
case MMC_TIMING_MMC_HS400:
|
||||||
str = "mmc HS400";
|
str = mmc_card_hs400es(host->card) ?
|
||||||
|
"mmc HS400 enhanced strobe" : "mmc HS400";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
str = "invalid";
|
str = "invalid";
|
||||||
|
@@ -313,6 +313,14 @@ int mmc_of_parse(struct mmc_host *host)
|
|||||||
host->caps2 |= MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR;
|
host->caps2 |= MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR;
|
||||||
if (of_property_read_bool(np, "mmc-hs400-1_2v"))
|
if (of_property_read_bool(np, "mmc-hs400-1_2v"))
|
||||||
host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;
|
host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;
|
||||||
|
if (of_property_read_bool(np, "mmc-hs400-enhanced-strobe"))
|
||||||
|
host->caps2 |= MMC_CAP2_HS400_ES;
|
||||||
|
if (of_property_read_bool(np, "no-sdio"))
|
||||||
|
host->caps2 |= MMC_CAP2_NO_SDIO;
|
||||||
|
if (of_property_read_bool(np, "no-sd"))
|
||||||
|
host->caps2 |= MMC_CAP2_NO_SD;
|
||||||
|
if (of_property_read_bool(np, "no-mmc"))
|
||||||
|
host->caps2 |= MMC_CAP2_NO_MMC;
|
||||||
|
|
||||||
host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr);
|
host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr);
|
||||||
if (host->dsr_req && (host->dsr & ~0xffff)) {
|
if (host->dsr_req && (host->dsr & ~0xffff)) {
|
||||||
|
@@ -45,6 +45,17 @@ static const unsigned int tacc_mant[] = {
|
|||||||
35, 40, 45, 50, 55, 60, 70, 80,
|
35, 40, 45, 50, 55, 60, 70, 80,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct mmc_fixup mmc_ext_csd_fixups[] = {
|
||||||
|
/*
|
||||||
|
* Certain Hynix eMMC 4.41 cards might get broken when HPI feature
|
||||||
|
* is used so disable the HPI feature for such buggy cards.
|
||||||
|
*/
|
||||||
|
MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY, CID_MANFID_HYNIX,
|
||||||
|
0x014a, add_quirk, MMC_QUIRK_BROKEN_HPI, 5),
|
||||||
|
|
||||||
|
END_FIXUP
|
||||||
|
};
|
||||||
|
|
||||||
#define UNSTUFF_BITS(resp,start,size) \
|
#define UNSTUFF_BITS(resp,start,size) \
|
||||||
({ \
|
({ \
|
||||||
const int __size = size; \
|
const int __size = size; \
|
||||||
@@ -235,6 +246,11 @@ static void mmc_select_card_type(struct mmc_card *card)
|
|||||||
avail_type |= EXT_CSD_CARD_TYPE_HS400_1_2V;
|
avail_type |= EXT_CSD_CARD_TYPE_HS400_1_2V;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((caps2 & MMC_CAP2_HS400_ES) &&
|
||||||
|
card->ext_csd.strobe_support &&
|
||||||
|
(avail_type & EXT_CSD_CARD_TYPE_HS400))
|
||||||
|
avail_type |= EXT_CSD_CARD_TYPE_HS400ES;
|
||||||
|
|
||||||
card->ext_csd.hs_max_dtr = hs_max_dtr;
|
card->ext_csd.hs_max_dtr = hs_max_dtr;
|
||||||
card->ext_csd.hs200_max_dtr = hs200_max_dtr;
|
card->ext_csd.hs200_max_dtr = hs200_max_dtr;
|
||||||
card->mmc_avail_type = avail_type;
|
card->mmc_avail_type = avail_type;
|
||||||
@@ -370,6 +386,9 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
|||||||
*/
|
*/
|
||||||
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
|
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
|
||||||
|
|
||||||
|
/* fixup device after ext_csd revision field is updated */
|
||||||
|
mmc_fixup_device(card, mmc_ext_csd_fixups);
|
||||||
|
|
||||||
card->ext_csd.raw_sectors[0] = ext_csd[EXT_CSD_SEC_CNT + 0];
|
card->ext_csd.raw_sectors[0] = ext_csd[EXT_CSD_SEC_CNT + 0];
|
||||||
card->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1];
|
card->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1];
|
||||||
card->ext_csd.raw_sectors[2] = ext_csd[EXT_CSD_SEC_CNT + 2];
|
card->ext_csd.raw_sectors[2] = ext_csd[EXT_CSD_SEC_CNT + 2];
|
||||||
@@ -386,6 +405,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
|||||||
mmc_card_set_blockaddr(card);
|
mmc_card_set_blockaddr(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
card->ext_csd.strobe_support = ext_csd[EXT_CSD_STROBE_SUPPORT];
|
||||||
card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
|
card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
|
||||||
mmc_select_card_type(card);
|
mmc_select_card_type(card);
|
||||||
|
|
||||||
@@ -500,7 +520,8 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
|||||||
card->cid.year += 16;
|
card->cid.year += 16;
|
||||||
|
|
||||||
/* check whether the eMMC card supports BKOPS */
|
/* check whether the eMMC card supports BKOPS */
|
||||||
if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
|
if (!mmc_card_broken_hpi(card) &&
|
||||||
|
ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
|
||||||
card->ext_csd.bkops = 1;
|
card->ext_csd.bkops = 1;
|
||||||
card->ext_csd.man_bkops_en =
|
card->ext_csd.man_bkops_en =
|
||||||
(ext_csd[EXT_CSD_BKOPS_EN] &
|
(ext_csd[EXT_CSD_BKOPS_EN] &
|
||||||
@@ -513,7 +534,8 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* check whether the eMMC card supports HPI */
|
/* check whether the eMMC card supports HPI */
|
||||||
if (!broken_hpi && (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1)) {
|
if (!mmc_card_broken_hpi(card) &&
|
||||||
|
!broken_hpi && (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1)) {
|
||||||
card->ext_csd.hpi = 1;
|
card->ext_csd.hpi = 1;
|
||||||
if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2)
|
if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2)
|
||||||
card->ext_csd.hpi_cmd = MMC_STOP_TRANSMISSION;
|
card->ext_csd.hpi_cmd = MMC_STOP_TRANSMISSION;
|
||||||
@@ -727,6 +749,7 @@ MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
|
|||||||
MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
|
MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
|
||||||
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
|
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
|
||||||
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
|
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
|
||||||
|
MMC_DEV_ATTR(ocr, "%08x\n", card->ocr);
|
||||||
|
|
||||||
static ssize_t mmc_fwrev_show(struct device *dev,
|
static ssize_t mmc_fwrev_show(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
@@ -744,6 +767,22 @@ static ssize_t mmc_fwrev_show(struct device *dev,
|
|||||||
|
|
||||||
static DEVICE_ATTR(fwrev, S_IRUGO, mmc_fwrev_show, NULL);
|
static DEVICE_ATTR(fwrev, S_IRUGO, mmc_fwrev_show, NULL);
|
||||||
|
|
||||||
|
static ssize_t mmc_dsr_show(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||||
|
struct mmc_host *host = card->host;
|
||||||
|
|
||||||
|
if (card->csd.dsr_imp && host->dsr_req)
|
||||||
|
return sprintf(buf, "0x%x\n", host->dsr);
|
||||||
|
else
|
||||||
|
/* return default DSR value */
|
||||||
|
return sprintf(buf, "0x%x\n", 0x404);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL);
|
||||||
|
|
||||||
static struct attribute *mmc_std_attrs[] = {
|
static struct attribute *mmc_std_attrs[] = {
|
||||||
&dev_attr_cid.attr,
|
&dev_attr_cid.attr,
|
||||||
&dev_attr_csd.attr,
|
&dev_attr_csd.attr,
|
||||||
@@ -762,6 +801,8 @@ static struct attribute *mmc_std_attrs[] = {
|
|||||||
&dev_attr_enhanced_area_size.attr,
|
&dev_attr_enhanced_area_size.attr,
|
||||||
&dev_attr_raw_rpmb_size_mult.attr,
|
&dev_attr_raw_rpmb_size_mult.attr,
|
||||||
&dev_attr_rel_sectors.attr,
|
&dev_attr_rel_sectors.attr,
|
||||||
|
&dev_attr_ocr.attr,
|
||||||
|
&dev_attr_dsr.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
ATTRIBUTE_GROUPS(mmc_std);
|
ATTRIBUTE_GROUPS(mmc_std);
|
||||||
@@ -959,6 +1000,19 @@ static int mmc_select_bus_width(struct mmc_card *card)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Caller must hold re-tuning */
|
||||||
|
static int mmc_switch_status(struct mmc_card *card)
|
||||||
|
{
|
||||||
|
u32 status;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = mmc_send_status(card, &status);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return mmc_switch_status_error(card->host, status);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Switch to the high-speed mode
|
* Switch to the high-speed mode
|
||||||
*/
|
*/
|
||||||
@@ -969,9 +1023,11 @@ static int mmc_select_hs(struct mmc_card *card)
|
|||||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
|
EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
|
||||||
card->ext_csd.generic_cmd6_time,
|
card->ext_csd.generic_cmd6_time,
|
||||||
true, true, true);
|
true, false, true);
|
||||||
if (!err)
|
if (!err) {
|
||||||
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
||||||
|
err = mmc_switch_status(card);
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -1047,23 +1103,9 @@ static int mmc_select_hs_ddr(struct mmc_card *card)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Caller must hold re-tuning */
|
|
||||||
static int mmc_switch_status(struct mmc_card *card)
|
|
||||||
{
|
|
||||||
u32 status;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = mmc_send_status(card, &status);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
return mmc_switch_status_error(card->host, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mmc_select_hs400(struct mmc_card *card)
|
static int mmc_select_hs400(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
struct mmc_host *host = card->host;
|
struct mmc_host *host = card->host;
|
||||||
bool send_status = true;
|
|
||||||
unsigned int max_dtr;
|
unsigned int max_dtr;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
u8 val;
|
u8 val;
|
||||||
@@ -1075,19 +1117,12 @@ static int mmc_select_hs400(struct mmc_card *card)
|
|||||||
host->ios.bus_width == MMC_BUS_WIDTH_8))
|
host->ios.bus_width == MMC_BUS_WIDTH_8))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
|
|
||||||
send_status = false;
|
|
||||||
|
|
||||||
/* Reduce frequency to HS frequency */
|
|
||||||
max_dtr = card->ext_csd.hs_max_dtr;
|
|
||||||
mmc_set_clock(host, max_dtr);
|
|
||||||
|
|
||||||
/* Switch card to HS mode */
|
/* Switch card to HS mode */
|
||||||
val = EXT_CSD_TIMING_HS;
|
val = EXT_CSD_TIMING_HS;
|
||||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
EXT_CSD_HS_TIMING, val,
|
EXT_CSD_HS_TIMING, val,
|
||||||
card->ext_csd.generic_cmd6_time,
|
card->ext_csd.generic_cmd6_time,
|
||||||
true, send_status, true);
|
true, false, true);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_err("%s: switch to high-speed from hs200 failed, err:%d\n",
|
pr_err("%s: switch to high-speed from hs200 failed, err:%d\n",
|
||||||
mmc_hostname(host), err);
|
mmc_hostname(host), err);
|
||||||
@@ -1097,11 +1132,13 @@ static int mmc_select_hs400(struct mmc_card *card)
|
|||||||
/* Set host controller to HS timing */
|
/* Set host controller to HS timing */
|
||||||
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
||||||
|
|
||||||
if (!send_status) {
|
/* Reduce frequency to HS frequency */
|
||||||
|
max_dtr = card->ext_csd.hs_max_dtr;
|
||||||
|
mmc_set_clock(host, max_dtr);
|
||||||
|
|
||||||
err = mmc_switch_status(card);
|
err = mmc_switch_status(card);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
|
||||||
|
|
||||||
/* Switch card to DDR */
|
/* Switch card to DDR */
|
||||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
@@ -1120,7 +1157,7 @@ static int mmc_select_hs400(struct mmc_card *card)
|
|||||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
EXT_CSD_HS_TIMING, val,
|
EXT_CSD_HS_TIMING, val,
|
||||||
card->ext_csd.generic_cmd6_time,
|
card->ext_csd.generic_cmd6_time,
|
||||||
true, send_status, true);
|
true, false, true);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_err("%s: switch to hs400 failed, err:%d\n",
|
pr_err("%s: switch to hs400 failed, err:%d\n",
|
||||||
mmc_hostname(host), err);
|
mmc_hostname(host), err);
|
||||||
@@ -1131,11 +1168,9 @@ static int mmc_select_hs400(struct mmc_card *card)
|
|||||||
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
|
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
|
||||||
mmc_set_bus_speed(card);
|
mmc_set_bus_speed(card);
|
||||||
|
|
||||||
if (!send_status) {
|
|
||||||
err = mmc_switch_status(card);
|
err = mmc_switch_status(card);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -1153,14 +1188,10 @@ int mmc_hs200_to_hs400(struct mmc_card *card)
|
|||||||
int mmc_hs400_to_hs200(struct mmc_card *card)
|
int mmc_hs400_to_hs200(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
struct mmc_host *host = card->host;
|
struct mmc_host *host = card->host;
|
||||||
bool send_status = true;
|
|
||||||
unsigned int max_dtr;
|
unsigned int max_dtr;
|
||||||
int err;
|
int err;
|
||||||
u8 val;
|
u8 val;
|
||||||
|
|
||||||
if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
|
|
||||||
send_status = false;
|
|
||||||
|
|
||||||
/* Reduce frequency to HS */
|
/* Reduce frequency to HS */
|
||||||
max_dtr = card->ext_csd.hs_max_dtr;
|
max_dtr = card->ext_csd.hs_max_dtr;
|
||||||
mmc_set_clock(host, max_dtr);
|
mmc_set_clock(host, max_dtr);
|
||||||
@@ -1169,49 +1200,43 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
|
|||||||
val = EXT_CSD_TIMING_HS;
|
val = EXT_CSD_TIMING_HS;
|
||||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
|
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
|
||||||
val, card->ext_csd.generic_cmd6_time,
|
val, card->ext_csd.generic_cmd6_time,
|
||||||
true, send_status, true);
|
true, false, true);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
|
|
||||||
mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
|
mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
|
||||||
|
|
||||||
if (!send_status) {
|
|
||||||
err = mmc_switch_status(card);
|
err = mmc_switch_status(card);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
|
||||||
|
|
||||||
/* Switch HS DDR to HS */
|
/* Switch HS DDR to HS */
|
||||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
|
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
|
||||||
EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time,
|
EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time,
|
||||||
true, send_status, true);
|
true, false, true);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
|
|
||||||
mmc_set_timing(host, MMC_TIMING_MMC_HS);
|
mmc_set_timing(host, MMC_TIMING_MMC_HS);
|
||||||
|
|
||||||
if (!send_status) {
|
|
||||||
err = mmc_switch_status(card);
|
err = mmc_switch_status(card);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
|
||||||
|
|
||||||
/* Switch HS to HS200 */
|
/* Switch HS to HS200 */
|
||||||
val = EXT_CSD_TIMING_HS200 |
|
val = EXT_CSD_TIMING_HS200 |
|
||||||
card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
|
card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
|
||||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
|
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
|
||||||
val, card->ext_csd.generic_cmd6_time, true,
|
val, card->ext_csd.generic_cmd6_time,
|
||||||
send_status, true);
|
true, false, true);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
|
|
||||||
mmc_set_timing(host, MMC_TIMING_MMC_HS200);
|
mmc_set_timing(host, MMC_TIMING_MMC_HS200);
|
||||||
|
|
||||||
if (!send_status) {
|
|
||||||
err = mmc_switch_status(card);
|
err = mmc_switch_status(card);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
|
||||||
|
|
||||||
mmc_set_bus_speed(card);
|
mmc_set_bus_speed(card);
|
||||||
|
|
||||||
@@ -1223,6 +1248,78 @@ out_err:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mmc_select_hs400es(struct mmc_card *card)
|
||||||
|
{
|
||||||
|
struct mmc_host *host = card->host;
|
||||||
|
int err = 0;
|
||||||
|
u8 val;
|
||||||
|
|
||||||
|
if (!(host->caps & MMC_CAP_8_BIT_DATA)) {
|
||||||
|
err = -ENOTSUPP;
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mmc_select_bus_width(card);
|
||||||
|
if (err < 0)
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
|
/* Switch card to HS mode */
|
||||||
|
err = mmc_select_hs(card);
|
||||||
|
if (err) {
|
||||||
|
pr_err("%s: switch to high-speed failed, err:%d\n",
|
||||||
|
mmc_hostname(host), err);
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mmc_switch_status(card);
|
||||||
|
if (err)
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
|
/* Switch card to DDR with strobe bit */
|
||||||
|
val = EXT_CSD_DDR_BUS_WIDTH_8 | EXT_CSD_BUS_WIDTH_STROBE;
|
||||||
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
|
EXT_CSD_BUS_WIDTH,
|
||||||
|
val,
|
||||||
|
card->ext_csd.generic_cmd6_time);
|
||||||
|
if (err) {
|
||||||
|
pr_err("%s: switch to bus width for hs400es failed, err:%d\n",
|
||||||
|
mmc_hostname(host), err);
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Switch card to HS400 */
|
||||||
|
val = EXT_CSD_TIMING_HS400 |
|
||||||
|
card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
|
||||||
|
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
|
EXT_CSD_HS_TIMING, val,
|
||||||
|
card->ext_csd.generic_cmd6_time,
|
||||||
|
true, false, true);
|
||||||
|
if (err) {
|
||||||
|
pr_err("%s: switch to hs400es failed, err:%d\n",
|
||||||
|
mmc_hostname(host), err);
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set host controller to HS400 timing and frequency */
|
||||||
|
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
|
||||||
|
|
||||||
|
/* Controller enable enhanced strobe function */
|
||||||
|
host->ios.enhanced_strobe = true;
|
||||||
|
if (host->ops->hs400_enhanced_strobe)
|
||||||
|
host->ops->hs400_enhanced_strobe(host, &host->ios);
|
||||||
|
|
||||||
|
err = mmc_switch_status(card);
|
||||||
|
if (err)
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_err:
|
||||||
|
pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host),
|
||||||
|
__func__, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static void mmc_select_driver_type(struct mmc_card *card)
|
static void mmc_select_driver_type(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
int card_drv_type, drive_strength, drv_type;
|
int card_drv_type, drive_strength, drv_type;
|
||||||
@@ -1250,7 +1347,6 @@ static void mmc_select_driver_type(struct mmc_card *card)
|
|||||||
static int mmc_select_hs200(struct mmc_card *card)
|
static int mmc_select_hs200(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
struct mmc_host *host = card->host;
|
struct mmc_host *host = card->host;
|
||||||
bool send_status = true;
|
|
||||||
unsigned int old_timing, old_signal_voltage;
|
unsigned int old_timing, old_signal_voltage;
|
||||||
int err = -EINVAL;
|
int err = -EINVAL;
|
||||||
u8 val;
|
u8 val;
|
||||||
@@ -1268,26 +1364,23 @@ static int mmc_select_hs200(struct mmc_card *card)
|
|||||||
|
|
||||||
mmc_select_driver_type(card);
|
mmc_select_driver_type(card);
|
||||||
|
|
||||||
if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
|
|
||||||
send_status = false;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the bus width(4 or 8) with host's support and
|
* Set the bus width(4 or 8) with host's support and
|
||||||
* switch to HS200 mode if bus width is set successfully.
|
* switch to HS200 mode if bus width is set successfully.
|
||||||
*/
|
*/
|
||||||
err = mmc_select_bus_width(card);
|
err = mmc_select_bus_width(card);
|
||||||
if (err >= 0) {
|
if (err > 0) {
|
||||||
val = EXT_CSD_TIMING_HS200 |
|
val = EXT_CSD_TIMING_HS200 |
|
||||||
card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
|
card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
|
||||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
EXT_CSD_HS_TIMING, val,
|
EXT_CSD_HS_TIMING, val,
|
||||||
card->ext_csd.generic_cmd6_time,
|
card->ext_csd.generic_cmd6_time,
|
||||||
true, send_status, true);
|
true, false, true);
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
goto err;
|
||||||
old_timing = host->ios.timing;
|
old_timing = host->ios.timing;
|
||||||
mmc_set_timing(host, MMC_TIMING_MMC_HS200);
|
mmc_set_timing(host, MMC_TIMING_MMC_HS200);
|
||||||
if (!send_status) {
|
|
||||||
err = mmc_switch_status(card);
|
err = mmc_switch_status(card);
|
||||||
/*
|
/*
|
||||||
* mmc_select_timing() assumes timing has not changed if
|
* mmc_select_timing() assumes timing has not changed if
|
||||||
@@ -1296,7 +1389,6 @@ static int mmc_select_hs200(struct mmc_card *card)
|
|||||||
if (err == -EBADMSG)
|
if (err == -EBADMSG)
|
||||||
mmc_set_timing(host, old_timing);
|
mmc_set_timing(host, old_timing);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
err:
|
err:
|
||||||
if (err) {
|
if (err) {
|
||||||
/* fall back to the old signal voltage, if fails report error */
|
/* fall back to the old signal voltage, if fails report error */
|
||||||
@@ -1310,7 +1402,7 @@ err:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Activate High Speed or HS200 mode if supported.
|
* Activate High Speed, HS200 or HS400ES mode if supported.
|
||||||
*/
|
*/
|
||||||
static int mmc_select_timing(struct mmc_card *card)
|
static int mmc_select_timing(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
@@ -1319,7 +1411,9 @@ static int mmc_select_timing(struct mmc_card *card)
|
|||||||
if (!mmc_can_ext_csd(card))
|
if (!mmc_can_ext_csd(card))
|
||||||
goto bus_speed;
|
goto bus_speed;
|
||||||
|
|
||||||
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
|
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES)
|
||||||
|
err = mmc_select_hs400es(card);
|
||||||
|
else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
|
||||||
err = mmc_select_hs200(card);
|
err = mmc_select_hs200(card);
|
||||||
else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS)
|
else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS)
|
||||||
err = mmc_select_hs(card);
|
err = mmc_select_hs(card);
|
||||||
@@ -1583,7 +1677,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||||||
} else if (mmc_card_hs(card)) {
|
} else if (mmc_card_hs(card)) {
|
||||||
/* Select the desired bus width optionally */
|
/* Select the desired bus width optionally */
|
||||||
err = mmc_select_bus_width(card);
|
err = mmc_select_bus_width(card);
|
||||||
if (err >= 0) {
|
if (err > 0) {
|
||||||
err = mmc_select_hs_ddr(card);
|
err = mmc_select_hs_ddr(card);
|
||||||
if (err)
|
if (err)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
@@ -1616,7 +1710,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||||||
* If cache size is higher than 0, this indicates
|
* If cache size is higher than 0, this indicates
|
||||||
* the existence of cache and it can be turned on.
|
* the existence of cache and it can be turned on.
|
||||||
*/
|
*/
|
||||||
if (card->ext_csd.cache_size > 0) {
|
if (!mmc_card_broken_hpi(card) &&
|
||||||
|
card->ext_csd.cache_size > 0) {
|
||||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||||
EXT_CSD_CACHE_CTRL, 1,
|
EXT_CSD_CACHE_CTRL, 1,
|
||||||
card->ext_csd.generic_cmd6_time);
|
card->ext_csd.generic_cmd6_time);
|
||||||
|
@@ -480,6 +480,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
|||||||
u32 status = 0;
|
u32 status = 0;
|
||||||
bool use_r1b_resp = use_busy_signal;
|
bool use_r1b_resp = use_busy_signal;
|
||||||
bool expired = false;
|
bool expired = false;
|
||||||
|
bool busy = false;
|
||||||
|
|
||||||
mmc_retune_hold(host);
|
mmc_retune_hold(host);
|
||||||
|
|
||||||
@@ -533,21 +534,26 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
|||||||
timeout_ms = MMC_OPS_TIMEOUT_MS;
|
timeout_ms = MMC_OPS_TIMEOUT_MS;
|
||||||
|
|
||||||
/* Must check status to be sure of no errors. */
|
/* Must check status to be sure of no errors. */
|
||||||
timeout = jiffies + msecs_to_jiffies(timeout_ms);
|
timeout = jiffies + msecs_to_jiffies(timeout_ms) + 1;
|
||||||
do {
|
do {
|
||||||
if (send_status) {
|
|
||||||
/*
|
/*
|
||||||
* Due to the possibility of being preempted after
|
* Due to the possibility of being preempted after
|
||||||
* sending the status command, check the expiration
|
* sending the status command, check the expiration
|
||||||
* time first.
|
* time first.
|
||||||
*/
|
*/
|
||||||
expired = time_after(jiffies, timeout);
|
expired = time_after(jiffies, timeout);
|
||||||
|
if (send_status) {
|
||||||
err = __mmc_send_status(card, &status, ignore_crc);
|
err = __mmc_send_status(card, &status, ignore_crc);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
|
if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
|
||||||
break;
|
break;
|
||||||
|
if (host->ops->card_busy) {
|
||||||
|
if (!host->ops->card_busy(host))
|
||||||
|
break;
|
||||||
|
busy = true;
|
||||||
|
}
|
||||||
if (mmc_host_is_spi(host))
|
if (mmc_host_is_spi(host))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -556,19 +562,20 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
|||||||
* does'nt support MMC_CAP_WAIT_WHILE_BUSY, then we can only
|
* does'nt support MMC_CAP_WAIT_WHILE_BUSY, then we can only
|
||||||
* rely on waiting for the stated timeout to be sufficient.
|
* rely on waiting for the stated timeout to be sufficient.
|
||||||
*/
|
*/
|
||||||
if (!send_status) {
|
if (!send_status && !host->ops->card_busy) {
|
||||||
mmc_delay(timeout_ms);
|
mmc_delay(timeout_ms);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Timeout if the device never leaves the program state. */
|
/* Timeout if the device never leaves the program state. */
|
||||||
if (expired && R1_CURRENT_STATE(status) == R1_STATE_PRG) {
|
if (expired &&
|
||||||
|
(R1_CURRENT_STATE(status) == R1_STATE_PRG || busy)) {
|
||||||
pr_err("%s: Card stuck in programming state! %s\n",
|
pr_err("%s: Card stuck in programming state! %s\n",
|
||||||
mmc_hostname(host), __func__);
|
mmc_hostname(host), __func__);
|
||||||
err = -ETIMEDOUT;
|
err = -ETIMEDOUT;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
|
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG || busy);
|
||||||
|
|
||||||
err = mmc_switch_status_error(host, status);
|
err = mmc_switch_status_error(host, status);
|
||||||
out:
|
out:
|
||||||
|
@@ -72,6 +72,8 @@ void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table)
|
|||||||
f->cis_vendor == (u16) SDIO_ANY_ID) &&
|
f->cis_vendor == (u16) SDIO_ANY_ID) &&
|
||||||
(f->cis_device == card->cis.device ||
|
(f->cis_device == card->cis.device ||
|
||||||
f->cis_device == (u16) SDIO_ANY_ID) &&
|
f->cis_device == (u16) SDIO_ANY_ID) &&
|
||||||
|
(f->ext_csd_rev == EXT_CSD_REV_ANY ||
|
||||||
|
f->ext_csd_rev == card->ext_csd.rev) &&
|
||||||
rev >= f->rev_start && rev <= f->rev_end) {
|
rev >= f->rev_start && rev <= f->rev_end) {
|
||||||
dev_dbg(&card->dev, "calling %pf\n", f->vendor_fixup);
|
dev_dbg(&card->dev, "calling %pf\n", f->vendor_fixup);
|
||||||
f->vendor_fixup(card, f->data);
|
f->vendor_fixup(card, f->data);
|
||||||
|
@@ -675,8 +675,25 @@ MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
|
|||||||
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
|
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
|
||||||
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
|
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
|
||||||
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
|
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
|
||||||
|
MMC_DEV_ATTR(ocr, "%08x\n", card->ocr);
|
||||||
|
|
||||||
|
|
||||||
|
static ssize_t mmc_dsr_show(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||||
|
struct mmc_host *host = card->host;
|
||||||
|
|
||||||
|
if (card->csd.dsr_imp && host->dsr_req)
|
||||||
|
return sprintf(buf, "0x%x\n", host->dsr);
|
||||||
|
else
|
||||||
|
/* return default DSR value */
|
||||||
|
return sprintf(buf, "0x%x\n", 0x404);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL);
|
||||||
|
|
||||||
static struct attribute *sd_std_attrs[] = {
|
static struct attribute *sd_std_attrs[] = {
|
||||||
&dev_attr_cid.attr,
|
&dev_attr_cid.attr,
|
||||||
&dev_attr_csd.attr,
|
&dev_attr_csd.attr,
|
||||||
@@ -690,6 +707,8 @@ static struct attribute *sd_std_attrs[] = {
|
|||||||
&dev_attr_name.attr,
|
&dev_attr_name.attr,
|
||||||
&dev_attr_oemid.attr,
|
&dev_attr_oemid.attr,
|
||||||
&dev_attr_serial.attr,
|
&dev_attr_serial.attr,
|
||||||
|
&dev_attr_ocr.attr,
|
||||||
|
&dev_attr_dsr.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
ATTRIBUTE_GROUPS(sd_std);
|
ATTRIBUTE_GROUPS(sd_std);
|
||||||
|
@@ -122,6 +122,7 @@ config MMC_SDHCI_OF_ARASAN
|
|||||||
tristate "SDHCI OF support for the Arasan SDHCI controllers"
|
tristate "SDHCI OF support for the Arasan SDHCI controllers"
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
depends on OF
|
depends on OF
|
||||||
|
depends on COMMON_CLK
|
||||||
help
|
help
|
||||||
This selects the Arasan Secure Digital Host Controller Interface
|
This selects the Arasan Secure Digital Host Controller Interface
|
||||||
(SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC.
|
(SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC.
|
||||||
@@ -296,17 +297,6 @@ config MMC_SDHCI_BCM_KONA
|
|||||||
|
|
||||||
If you have a controller with this interface, say Y or M here.
|
If you have a controller with this interface, say Y or M here.
|
||||||
|
|
||||||
config MMC_SDHCI_BCM2835
|
|
||||||
tristate "SDHCI platform support for the BCM2835 SD/MMC Controller"
|
|
||||||
depends on ARCH_BCM2835
|
|
||||||
depends on MMC_SDHCI_PLTFM
|
|
||||||
select MMC_SDHCI_IO_ACCESSORS
|
|
||||||
help
|
|
||||||
This selects the BCM2835 SD/MMC controller. If you have a BCM2835
|
|
||||||
platform with SD or MMC devices, say Y or M here.
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
config MMC_SDHCI_F_SDH30
|
config MMC_SDHCI_F_SDH30
|
||||||
tristate "SDHCI support for Fujitsu Semiconductor F_SDH30"
|
tristate "SDHCI support for Fujitsu Semiconductor F_SDH30"
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
@@ -798,3 +788,13 @@ config MMC_SDHCI_MICROCHIP_PIC32
|
|||||||
If you have a controller with this interface, say Y or M here.
|
If you have a controller with this interface, say Y or M here.
|
||||||
|
|
||||||
If unsure, say N.
|
If unsure, say N.
|
||||||
|
config MMC_SDHCI_BRCMSTB
|
||||||
|
tristate "Broadcom SDIO/SD/MMC support"
|
||||||
|
depends on ARCH_BRCMSTB || BMIPS_GENERIC
|
||||||
|
depends on MMC_SDHCI_PLTFM
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
This selects support for the SDIO/SD/MMC Host Controller on
|
||||||
|
Broadcom STB SoCs.
|
||||||
|
|
||||||
|
If unsure, say Y.
|
||||||
|
@@ -71,11 +71,11 @@ obj-$(CONFIG_MMC_SDHCI_OF_AT91) += sdhci-of-at91.o
|
|||||||
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
|
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.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_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_IPROC) += sdhci-iproc.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
|
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o
|
obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o
|
obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o
|
||||||
|
obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o
|
||||||
|
|
||||||
ifeq ($(CONFIG_CB710_DEBUG),y)
|
ifeq ($(CONFIG_CB710_DEBUG),y)
|
||||||
CFLAGS-cb710-mmc += -DDEBUG
|
CFLAGS-cb710-mmc += -DDEBUG
|
||||||
|
@@ -157,7 +157,7 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
|
|||||||
* HOLD register should be bypassed in case there is no phase shift
|
* HOLD register should be bypassed in case there is no phase shift
|
||||||
* applied on CMD/DATA that is sent to the card.
|
* applied on CMD/DATA that is sent to the card.
|
||||||
*/
|
*/
|
||||||
if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel))
|
if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel) && host->cur_slot)
|
||||||
set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->cur_slot->flags);
|
set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->cur_slot->flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -32,6 +32,12 @@ struct k3_priv {
|
|||||||
struct regmap *reg;
|
struct regmap *reg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static unsigned long dw_mci_hi6220_caps[] = {
|
||||||
|
MMC_CAP_CMD23,
|
||||||
|
MMC_CAP_CMD23,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@@ -126,6 +132,7 @@ static void dw_mci_hi6220_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const struct dw_mci_drv_data hi6220_data = {
|
static const struct dw_mci_drv_data hi6220_data = {
|
||||||
|
.caps = dw_mci_hi6220_caps,
|
||||||
.switch_voltage = dw_mci_hi6220_switch_voltage,
|
.switch_voltage = dw_mci_hi6220_switch_voltage,
|
||||||
.set_ios = dw_mci_hi6220_set_ios,
|
.set_ios = dw_mci_hi6220_set_ios,
|
||||||
.parse_dt = dw_mci_hi6220_parse_dt,
|
.parse_dt = dw_mci_hi6220_parse_dt,
|
||||||
|
@@ -285,9 +285,6 @@ static int dw_mci_rockchip_init(struct dw_mci *host)
|
|||||||
/* It is slot 8 on Rockchip SoCs */
|
/* It is slot 8 on Rockchip SoCs */
|
||||||
host->sdio_id0 = 8;
|
host->sdio_id0 = 8;
|
||||||
|
|
||||||
/* It needs this quirk on all Rockchip SoCs */
|
|
||||||
host->pdata->quirks |= DW_MCI_QUIRK_BROKEN_DTO;
|
|
||||||
|
|
||||||
if (of_device_is_compatible(host->dev->of_node,
|
if (of_device_is_compatible(host->dev->of_node,
|
||||||
"rockchip,rk3288-dw-mshc"))
|
"rockchip,rk3288-dw-mshc"))
|
||||||
host->bus_hz /= RK3288_CLKGEN_DIV;
|
host->bus_hz /= RK3288_CLKGEN_DIV;
|
||||||
@@ -297,10 +294,10 @@ static int dw_mci_rockchip_init(struct dw_mci *host)
|
|||||||
|
|
||||||
/* Common capabilities of RK3288 SoC */
|
/* Common capabilities of RK3288 SoC */
|
||||||
static unsigned long dw_mci_rk3288_dwmmc_caps[4] = {
|
static unsigned long dw_mci_rk3288_dwmmc_caps[4] = {
|
||||||
MMC_CAP_ERASE | MMC_CAP_CMD23,
|
MMC_CAP_CMD23,
|
||||||
MMC_CAP_ERASE | MMC_CAP_CMD23,
|
MMC_CAP_CMD23,
|
||||||
MMC_CAP_ERASE | MMC_CAP_CMD23,
|
MMC_CAP_CMD23,
|
||||||
MMC_CAP_ERASE | MMC_CAP_CMD23,
|
MMC_CAP_CMD23,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct dw_mci_drv_data rk2928_drv_data = {
|
static const struct dw_mci_drv_data rk2928_drv_data = {
|
||||||
|
@@ -44,11 +44,11 @@
|
|||||||
/* Common flag combinations */
|
/* Common flag combinations */
|
||||||
#define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \
|
#define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \
|
||||||
SDMMC_INT_HTO | SDMMC_INT_SBE | \
|
SDMMC_INT_HTO | SDMMC_INT_SBE | \
|
||||||
SDMMC_INT_EBE)
|
SDMMC_INT_EBE | SDMMC_INT_HLE)
|
||||||
#define DW_MCI_CMD_ERROR_FLAGS (SDMMC_INT_RTO | SDMMC_INT_RCRC | \
|
#define DW_MCI_CMD_ERROR_FLAGS (SDMMC_INT_RTO | SDMMC_INT_RCRC | \
|
||||||
SDMMC_INT_RESP_ERR)
|
SDMMC_INT_RESP_ERR | SDMMC_INT_HLE)
|
||||||
#define DW_MCI_ERROR_FLAGS (DW_MCI_DATA_ERROR_FLAGS | \
|
#define DW_MCI_ERROR_FLAGS (DW_MCI_DATA_ERROR_FLAGS | \
|
||||||
DW_MCI_CMD_ERROR_FLAGS | SDMMC_INT_HLE)
|
DW_MCI_CMD_ERROR_FLAGS)
|
||||||
#define DW_MCI_SEND_STATUS 1
|
#define DW_MCI_SEND_STATUS 1
|
||||||
#define DW_MCI_RECV_STATUS 2
|
#define DW_MCI_RECV_STATUS 2
|
||||||
#define DW_MCI_DMA_THRESHOLD 16
|
#define DW_MCI_DMA_THRESHOLD 16
|
||||||
@@ -92,7 +92,7 @@ struct idmac_desc {
|
|||||||
|
|
||||||
__le32 des1; /* Buffer sizes */
|
__le32 des1; /* Buffer sizes */
|
||||||
#define IDMAC_SET_BUFFER1_SIZE(d, s) \
|
#define IDMAC_SET_BUFFER1_SIZE(d, s) \
|
||||||
((d)->des1 = ((d)->des1 & 0x03ffe000) | ((s) & 0x1fff))
|
((d)->des1 = ((d)->des1 & cpu_to_le32(0x03ffe000)) | (cpu_to_le32((s) & 0x1fff)))
|
||||||
|
|
||||||
__le32 des2; /* buffer 1 physical address */
|
__le32 des2; /* buffer 1 physical address */
|
||||||
|
|
||||||
@@ -105,6 +105,7 @@ struct idmac_desc {
|
|||||||
static bool dw_mci_reset(struct dw_mci *host);
|
static bool dw_mci_reset(struct dw_mci *host);
|
||||||
static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset);
|
static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset);
|
||||||
static int dw_mci_card_busy(struct mmc_host *mmc);
|
static int dw_mci_card_busy(struct mmc_host *mmc);
|
||||||
|
static int dw_mci_get_cd(struct mmc_host *mmc);
|
||||||
|
|
||||||
#if defined(CONFIG_DEBUG_FS)
|
#if defined(CONFIG_DEBUG_FS)
|
||||||
static int dw_mci_req_show(struct seq_file *s, void *v)
|
static int dw_mci_req_show(struct seq_file *s, void *v)
|
||||||
@@ -898,23 +899,35 @@ done:
|
|||||||
mci_writel(host, FIFOTH, fifoth_val);
|
mci_writel(host, FIFOTH, fifoth_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data)
|
static void dw_mci_ctrl_thld(struct dw_mci *host, struct mmc_data *data)
|
||||||
{
|
{
|
||||||
unsigned int blksz = data->blksz;
|
unsigned int blksz = data->blksz;
|
||||||
u32 blksz_depth, fifo_depth;
|
u32 blksz_depth, fifo_depth;
|
||||||
u16 thld_size;
|
u16 thld_size;
|
||||||
|
u8 enable;
|
||||||
WARN_ON(!(data->flags & MMC_DATA_READ));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CDTHRCTL doesn't exist prior to 240A (in fact that register offset is
|
* CDTHRCTL doesn't exist prior to 240A (in fact that register offset is
|
||||||
* in the FIFO region, so we really shouldn't access it).
|
* in the FIFO region, so we really shouldn't access it).
|
||||||
*/
|
*/
|
||||||
if (host->verid < DW_MMC_240A)
|
if (host->verid < DW_MMC_240A ||
|
||||||
|
(host->verid < DW_MMC_280A && data->flags & MMC_DATA_WRITE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Card write Threshold is introduced since 2.80a
|
||||||
|
* It's used when HS400 mode is enabled.
|
||||||
|
*/
|
||||||
|
if (data->flags & MMC_DATA_WRITE &&
|
||||||
|
!(host->timing != MMC_TIMING_MMC_HS400))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (data->flags & MMC_DATA_WRITE)
|
||||||
|
enable = SDMMC_CARD_WR_THR_EN;
|
||||||
|
else
|
||||||
|
enable = SDMMC_CARD_RD_THR_EN;
|
||||||
|
|
||||||
if (host->timing != MMC_TIMING_MMC_HS200 &&
|
if (host->timing != MMC_TIMING_MMC_HS200 &&
|
||||||
host->timing != MMC_TIMING_MMC_HS400 &&
|
|
||||||
host->timing != MMC_TIMING_UHS_SDR104)
|
host->timing != MMC_TIMING_UHS_SDR104)
|
||||||
goto disable;
|
goto disable;
|
||||||
|
|
||||||
@@ -930,11 +943,11 @@ static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data)
|
|||||||
* Currently just choose blksz.
|
* Currently just choose blksz.
|
||||||
*/
|
*/
|
||||||
thld_size = blksz;
|
thld_size = blksz;
|
||||||
mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(thld_size, 1));
|
mci_writel(host, CDTHRCTL, SDMMC_SET_THLD(thld_size, enable));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
disable:
|
disable:
|
||||||
mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(0, 0));
|
mci_writel(host, CDTHRCTL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
|
static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
|
||||||
@@ -1005,12 +1018,12 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
|
|||||||
host->sg = NULL;
|
host->sg = NULL;
|
||||||
host->data = data;
|
host->data = data;
|
||||||
|
|
||||||
if (data->flags & MMC_DATA_READ) {
|
if (data->flags & MMC_DATA_READ)
|
||||||
host->dir_status = DW_MCI_RECV_STATUS;
|
host->dir_status = DW_MCI_RECV_STATUS;
|
||||||
dw_mci_ctrl_rd_thld(host, data);
|
else
|
||||||
} else {
|
|
||||||
host->dir_status = DW_MCI_SEND_STATUS;
|
host->dir_status = DW_MCI_SEND_STATUS;
|
||||||
}
|
|
||||||
|
dw_mci_ctrl_thld(host, data);
|
||||||
|
|
||||||
if (dw_mci_submit_data_dma(host, data)) {
|
if (dw_mci_submit_data_dma(host, data)) {
|
||||||
if (host->data->flags & MMC_DATA_READ)
|
if (host->data->flags & MMC_DATA_READ)
|
||||||
@@ -1099,7 +1112,6 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
|
|||||||
|
|
||||||
div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0;
|
div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0;
|
||||||
|
|
||||||
if ((clock << div) != slot->__clk_old || force_clkinit)
|
|
||||||
dev_info(&slot->mmc->class_dev,
|
dev_info(&slot->mmc->class_dev,
|
||||||
"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
|
"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
|
||||||
slot->id, host->bus_hz, clock,
|
slot->id, host->bus_hz, clock,
|
||||||
@@ -1127,9 +1139,6 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
|
|||||||
|
|
||||||
/* inform CIU */
|
/* inform CIU */
|
||||||
mci_send_cmd(slot, sdmmc_cmd_bits, 0);
|
mci_send_cmd(slot, sdmmc_cmd_bits, 0);
|
||||||
|
|
||||||
/* keep the clock with reflecting clock dividor */
|
|
||||||
slot->__clk_old = clock << div;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
host->current_speed = clock;
|
host->current_speed = clock;
|
||||||
@@ -1253,15 +1262,15 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||||||
* atomic, otherwise the card could be removed in between and the
|
* atomic, otherwise the card could be removed in between and the
|
||||||
* request wouldn't fail until another card was inserted.
|
* request wouldn't fail until another card was inserted.
|
||||||
*/
|
*/
|
||||||
spin_lock_bh(&host->lock);
|
|
||||||
|
|
||||||
if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
|
if (!dw_mci_get_cd(mmc)) {
|
||||||
spin_unlock_bh(&host->lock);
|
|
||||||
mrq->cmd->error = -ENOMEDIUM;
|
mrq->cmd->error = -ENOMEDIUM;
|
||||||
mmc_request_done(mmc, mrq);
|
mmc_request_done(mmc, mrq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_lock_bh(&host->lock);
|
||||||
|
|
||||||
dw_mci_queue_request(host, slot, mrq);
|
dw_mci_queue_request(host, slot, mrq);
|
||||||
|
|
||||||
spin_unlock_bh(&host->lock);
|
spin_unlock_bh(&host->lock);
|
||||||
@@ -1451,8 +1460,7 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
|
|||||||
int gpio_cd = mmc_gpio_get_cd(mmc);
|
int gpio_cd = mmc_gpio_get_cd(mmc);
|
||||||
|
|
||||||
/* Use platform get_cd function, else try onboard card detect */
|
/* Use platform get_cd function, else try onboard card detect */
|
||||||
if ((mmc->caps & MMC_CAP_NEEDS_POLL) ||
|
if ((mmc->caps & MMC_CAP_NEEDS_POLL) || !mmc_card_is_removable(mmc))
|
||||||
(mmc->caps & MMC_CAP_NONREMOVABLE))
|
|
||||||
present = 1;
|
present = 1;
|
||||||
else if (gpio_cd >= 0)
|
else if (gpio_cd >= 0)
|
||||||
present = gpio_cd;
|
present = gpio_cd;
|
||||||
@@ -1761,6 +1769,33 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cmd->data && err) {
|
if (cmd->data && err) {
|
||||||
|
/*
|
||||||
|
* During UHS tuning sequence, sending the stop
|
||||||
|
* command after the response CRC error would
|
||||||
|
* throw the system into a confused state
|
||||||
|
* causing all future tuning phases to report
|
||||||
|
* failure.
|
||||||
|
*
|
||||||
|
* In such case controller will move into a data
|
||||||
|
* transfer state after a response error or
|
||||||
|
* response CRC error. Let's let that finish
|
||||||
|
* before trying to send a stop, so we'll go to
|
||||||
|
* STATE_SENDING_DATA.
|
||||||
|
*
|
||||||
|
* Although letting the data transfer take place
|
||||||
|
* will waste a bit of time (we already know
|
||||||
|
* the command was bad), it can't cause any
|
||||||
|
* errors since it's possible it would have
|
||||||
|
* taken place anyway if this tasklet got
|
||||||
|
* delayed. Allowing the transfer to take place
|
||||||
|
* avoids races and keeps things simple.
|
||||||
|
*/
|
||||||
|
if ((err != -ETIMEDOUT) &&
|
||||||
|
(cmd->opcode == MMC_SEND_TUNING_BLOCK)) {
|
||||||
|
state = STATE_SENDING_DATA;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
dw_mci_stop_dma(host);
|
dw_mci_stop_dma(host);
|
||||||
send_stop_abort(host, data);
|
send_stop_abort(host, data);
|
||||||
state = STATE_SENDING_STOP;
|
state = STATE_SENDING_STOP;
|
||||||
@@ -1801,8 +1836,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|||||||
* If all data-related interrupts don't come
|
* If all data-related interrupts don't come
|
||||||
* within the given time in reading data state.
|
* within the given time in reading data state.
|
||||||
*/
|
*/
|
||||||
if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) &&
|
if (host->dir_status == DW_MCI_RECV_STATUS)
|
||||||
(host->dir_status == DW_MCI_RECV_STATUS))
|
|
||||||
dw_mci_set_drto(host);
|
dw_mci_set_drto(host);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1844,8 +1878,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|||||||
* interrupt doesn't come within the given time.
|
* interrupt doesn't come within the given time.
|
||||||
* in reading data state.
|
* in reading data state.
|
||||||
*/
|
*/
|
||||||
if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) &&
|
if (host->dir_status == DW_MCI_RECV_STATUS)
|
||||||
(host->dir_status == DW_MCI_RECV_STATUS))
|
|
||||||
dw_mci_set_drto(host);
|
dw_mci_set_drto(host);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2411,7 +2444,6 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pending & SDMMC_INT_DATA_OVER) {
|
if (pending & SDMMC_INT_DATA_OVER) {
|
||||||
if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO)
|
|
||||||
del_timer(&host->dto_timer);
|
del_timer(&host->dto_timer);
|
||||||
|
|
||||||
mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
|
mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
|
||||||
@@ -2474,6 +2506,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
|||||||
mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_TI |
|
mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_TI |
|
||||||
SDMMC_IDMAC_INT_RI);
|
SDMMC_IDMAC_INT_RI);
|
||||||
mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_NI);
|
mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_NI);
|
||||||
|
if (!test_bit(EVENT_DATA_ERROR, &host->pending_events))
|
||||||
host->dma_ops->complete((void *)host);
|
host->dma_ops->complete((void *)host);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -2482,6 +2515,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
|||||||
mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI |
|
mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI |
|
||||||
SDMMC_IDMAC_INT_RI);
|
SDMMC_IDMAC_INT_RI);
|
||||||
mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
|
mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
|
||||||
|
if (!test_bit(EVENT_DATA_ERROR, &host->pending_events))
|
||||||
host->dma_ops->complete((void *)host);
|
host->dma_ops->complete((void *)host);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2570,6 +2604,12 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|||||||
if (host->pdata->caps)
|
if (host->pdata->caps)
|
||||||
mmc->caps = host->pdata->caps;
|
mmc->caps = host->pdata->caps;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Support MMC_CAP_ERASE by default.
|
||||||
|
* It needs to use trim/discard/erase commands.
|
||||||
|
*/
|
||||||
|
mmc->caps |= MMC_CAP_ERASE;
|
||||||
|
|
||||||
if (host->pdata->pm_caps)
|
if (host->pdata->pm_caps)
|
||||||
mmc->pm_caps = host->pdata->pm_caps;
|
mmc->pm_caps = host->pdata->pm_caps;
|
||||||
|
|
||||||
@@ -2616,10 +2656,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|||||||
mmc->max_seg_size = mmc->max_req_size;
|
mmc->max_seg_size = mmc->max_req_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dw_mci_get_cd(mmc))
|
dw_mci_get_cd(mmc);
|
||||||
set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
|
||||||
else
|
|
||||||
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
|
||||||
|
|
||||||
ret = mmc_add_host(mmc);
|
ret = mmc_add_host(mmc);
|
||||||
if (ret)
|
if (ret)
|
||||||
@@ -3006,9 +3043,6 @@ int dw_mci_probe(struct dw_mci *host)
|
|||||||
setup_timer(&host->cmd11_timer,
|
setup_timer(&host->cmd11_timer,
|
||||||
dw_mci_cmd11_timer, (unsigned long)host);
|
dw_mci_cmd11_timer, (unsigned long)host);
|
||||||
|
|
||||||
host->quirks = host->pdata->quirks;
|
|
||||||
|
|
||||||
if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO)
|
|
||||||
setup_timer(&host->dto_timer,
|
setup_timer(&host->dto_timer,
|
||||||
dw_mci_dto_timer, (unsigned long)host);
|
dw_mci_dto_timer, (unsigned long)host);
|
||||||
|
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
#define _DW_MMC_H_
|
#define _DW_MMC_H_
|
||||||
|
|
||||||
#define DW_MMC_240A 0x240a
|
#define DW_MMC_240A 0x240a
|
||||||
|
#define DW_MMC_280A 0x280a
|
||||||
|
|
||||||
#define SDMMC_CTRL 0x000
|
#define SDMMC_CTRL 0x000
|
||||||
#define SDMMC_PWREN 0x004
|
#define SDMMC_PWREN 0x004
|
||||||
@@ -175,7 +176,10 @@
|
|||||||
/* Version ID register define */
|
/* Version ID register define */
|
||||||
#define SDMMC_GET_VERID(x) ((x) & 0xFFFF)
|
#define SDMMC_GET_VERID(x) ((x) & 0xFFFF)
|
||||||
/* Card read threshold */
|
/* Card read threshold */
|
||||||
#define SDMMC_SET_RD_THLD(v, x) (((v) & 0xFFF) << 16 | (x))
|
#define SDMMC_SET_THLD(v, x) (((v) & 0xFFF) << 16 | (x))
|
||||||
|
#define SDMMC_CARD_WR_THR_EN BIT(2)
|
||||||
|
#define SDMMC_CARD_RD_THR_EN BIT(0)
|
||||||
|
/* UHS-1 register defines */
|
||||||
#define SDMMC_UHS_18V BIT(0)
|
#define SDMMC_UHS_18V BIT(0)
|
||||||
/* All ctrl reset bits */
|
/* All ctrl reset bits */
|
||||||
#define SDMMC_CTRL_ALL_RESET_FLAGS \
|
#define SDMMC_CTRL_ALL_RESET_FLAGS \
|
||||||
@@ -245,9 +249,6 @@ extern int dw_mci_resume(struct dw_mci *host);
|
|||||||
* @queue_node: List node for placing this node in the @queue list of
|
* @queue_node: List node for placing this node in the @queue list of
|
||||||
* &struct dw_mci.
|
* &struct dw_mci.
|
||||||
* @clock: Clock rate configured by set_ios(). Protected by host->lock.
|
* @clock: Clock rate configured by set_ios(). Protected by host->lock.
|
||||||
* @__clk_old: The last updated clock with reflecting clock divider.
|
|
||||||
* Keeping track of this helps us to avoid spamming the console
|
|
||||||
* with CONFIG_MMC_CLKGATE.
|
|
||||||
* @flags: Random state bits associated with the slot.
|
* @flags: Random state bits associated with the slot.
|
||||||
* @id: Number of this slot.
|
* @id: Number of this slot.
|
||||||
* @sdio_id: Number of this slot in the SDIO interrupt registers.
|
* @sdio_id: Number of this slot in the SDIO interrupt registers.
|
||||||
@@ -262,7 +263,6 @@ struct dw_mci_slot {
|
|||||||
struct list_head queue_node;
|
struct list_head queue_node;
|
||||||
|
|
||||||
unsigned int clock;
|
unsigned int clock;
|
||||||
unsigned int __clk_old;
|
|
||||||
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
#define DW_MMC_CARD_PRESENT 0
|
#define DW_MMC_CARD_PRESENT 0
|
||||||
|
@@ -287,6 +287,11 @@ struct msdc_save_para {
|
|||||||
u32 emmc50_cfg0;
|
u32 emmc50_cfg0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct msdc_tune_para {
|
||||||
|
u32 iocon;
|
||||||
|
u32 pad_tune;
|
||||||
|
};
|
||||||
|
|
||||||
struct msdc_delay_phase {
|
struct msdc_delay_phase {
|
||||||
u8 maxlen;
|
u8 maxlen;
|
||||||
u8 start;
|
u8 start;
|
||||||
@@ -326,7 +331,10 @@ struct msdc_host {
|
|||||||
unsigned char timing;
|
unsigned char timing;
|
||||||
bool vqmmc_enabled;
|
bool vqmmc_enabled;
|
||||||
u32 hs400_ds_delay;
|
u32 hs400_ds_delay;
|
||||||
|
bool hs400_mode; /* current eMMC will run at hs400 mode */
|
||||||
struct msdc_save_para save_para; /* used when gate HCLK */
|
struct msdc_save_para save_para; /* used when gate HCLK */
|
||||||
|
struct msdc_tune_para def_tune_para; /* default tune setting */
|
||||||
|
struct msdc_tune_para saved_tune_para; /* tune result of CMD21/CMD19 */
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sdr_set_bits(void __iomem *reg, u32 bs)
|
static void sdr_set_bits(void __iomem *reg, u32 bs)
|
||||||
@@ -582,6 +590,18 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
|
|||||||
msdc_set_timeout(host, host->timeout_ns, host->timeout_clks);
|
msdc_set_timeout(host, host->timeout_ns, host->timeout_clks);
|
||||||
sdr_set_bits(host->base + MSDC_INTEN, flags);
|
sdr_set_bits(host->base + MSDC_INTEN, flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mmc_select_hs400() will drop to 50Mhz and High speed mode,
|
||||||
|
* tune result of hs200/200Mhz is not suitable for 50Mhz
|
||||||
|
*/
|
||||||
|
if (host->sclk <= 52000000) {
|
||||||
|
writel(host->def_tune_para.iocon, host->base + MSDC_IOCON);
|
||||||
|
writel(host->def_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
|
||||||
|
} else {
|
||||||
|
writel(host->saved_tune_para.iocon, host->base + MSDC_IOCON);
|
||||||
|
writel(host->saved_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
|
||||||
|
}
|
||||||
|
|
||||||
dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->sclk, timing);
|
dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->sclk, timing);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -781,6 +801,12 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!sbc_error && !(events & MSDC_INT_CMDRDY)) {
|
if (!sbc_error && !(events & MSDC_INT_CMDRDY)) {
|
||||||
|
if (cmd->opcode != MMC_SEND_TUNING_BLOCK &&
|
||||||
|
cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200)
|
||||||
|
/*
|
||||||
|
* should not clear fifo/interrupt as the tune data
|
||||||
|
* may have alreay come.
|
||||||
|
*/
|
||||||
msdc_reset_hw(host);
|
msdc_reset_hw(host);
|
||||||
if (events & MSDC_INT_RSPCRCERR) {
|
if (events & MSDC_INT_RSPCRCERR) {
|
||||||
cmd->error = -EILSEQ;
|
cmd->error = -EILSEQ;
|
||||||
@@ -865,7 +891,11 @@ static void msdc_start_command(struct msdc_host *host,
|
|||||||
static void msdc_cmd_next(struct msdc_host *host,
|
static void msdc_cmd_next(struct msdc_host *host,
|
||||||
struct mmc_request *mrq, struct mmc_command *cmd)
|
struct mmc_request *mrq, struct mmc_command *cmd)
|
||||||
{
|
{
|
||||||
if (cmd->error || (mrq->sbc && mrq->sbc->error))
|
if ((cmd->error &&
|
||||||
|
!(cmd->error == -EILSEQ &&
|
||||||
|
(cmd->opcode == MMC_SEND_TUNING_BLOCK ||
|
||||||
|
cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))) ||
|
||||||
|
(mrq->sbc && mrq->sbc->error))
|
||||||
msdc_request_done(host, mrq);
|
msdc_request_done(host, mrq);
|
||||||
else if (cmd == mrq->sbc)
|
else if (cmd == mrq->sbc)
|
||||||
msdc_start_command(host, mrq, mrq->cmd);
|
msdc_start_command(host, mrq, mrq->cmd);
|
||||||
@@ -1158,6 +1188,8 @@ static void msdc_init_hw(struct msdc_host *host)
|
|||||||
/* Configure to default data timeout */
|
/* Configure to default data timeout */
|
||||||
sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, 3);
|
sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, 3);
|
||||||
|
|
||||||
|
host->def_tune_para.iocon = readl(host->base + MSDC_IOCON);
|
||||||
|
host->def_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
|
||||||
dev_dbg(host->dev, "init hardware done!");
|
dev_dbg(host->dev, "init hardware done!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1296,7 +1328,7 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
|
|||||||
{
|
{
|
||||||
struct msdc_host *host = mmc_priv(mmc);
|
struct msdc_host *host = mmc_priv(mmc);
|
||||||
u32 rise_delay = 0, fall_delay = 0;
|
u32 rise_delay = 0, fall_delay = 0;
|
||||||
struct msdc_delay_phase final_rise_delay, final_fall_delay;
|
struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,};
|
||||||
u8 final_delay, final_maxlen;
|
u8 final_delay, final_maxlen;
|
||||||
int cmd_err;
|
int cmd_err;
|
||||||
int i;
|
int i;
|
||||||
@@ -1309,6 +1341,11 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
|
|||||||
if (!cmd_err)
|
if (!cmd_err)
|
||||||
rise_delay |= (1 << i);
|
rise_delay |= (1 << i);
|
||||||
}
|
}
|
||||||
|
final_rise_delay = get_best_delay(host, rise_delay);
|
||||||
|
/* if rising edge has enough margin, then do not scan falling edge */
|
||||||
|
if (final_rise_delay.maxlen >= 10 ||
|
||||||
|
(final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
|
||||||
|
goto skip_fall;
|
||||||
|
|
||||||
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
|
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
|
||||||
for (i = 0; i < PAD_DELAY_MAX; i++) {
|
for (i = 0; i < PAD_DELAY_MAX; i++) {
|
||||||
@@ -1318,10 +1355,9 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
|
|||||||
if (!cmd_err)
|
if (!cmd_err)
|
||||||
fall_delay |= (1 << i);
|
fall_delay |= (1 << i);
|
||||||
}
|
}
|
||||||
|
|
||||||
final_rise_delay = get_best_delay(host, rise_delay);
|
|
||||||
final_fall_delay = get_best_delay(host, fall_delay);
|
final_fall_delay = get_best_delay(host, fall_delay);
|
||||||
|
|
||||||
|
skip_fall:
|
||||||
final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
|
final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
|
||||||
if (final_maxlen == final_rise_delay.maxlen) {
|
if (final_maxlen == final_rise_delay.maxlen) {
|
||||||
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
|
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
|
||||||
@@ -1342,7 +1378,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
|
|||||||
{
|
{
|
||||||
struct msdc_host *host = mmc_priv(mmc);
|
struct msdc_host *host = mmc_priv(mmc);
|
||||||
u32 rise_delay = 0, fall_delay = 0;
|
u32 rise_delay = 0, fall_delay = 0;
|
||||||
struct msdc_delay_phase final_rise_delay, final_fall_delay;
|
struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,};
|
||||||
u8 final_delay, final_maxlen;
|
u8 final_delay, final_maxlen;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
@@ -1355,6 +1391,11 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
|
|||||||
if (!ret)
|
if (!ret)
|
||||||
rise_delay |= (1 << i);
|
rise_delay |= (1 << i);
|
||||||
}
|
}
|
||||||
|
final_rise_delay = get_best_delay(host, rise_delay);
|
||||||
|
/* if rising edge has enough margin, then do not scan falling edge */
|
||||||
|
if (final_rise_delay.maxlen >= 10 ||
|
||||||
|
(final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
|
||||||
|
goto skip_fall;
|
||||||
|
|
||||||
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
|
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
|
||||||
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
|
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
|
||||||
@@ -1365,14 +1406,10 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
|
|||||||
if (!ret)
|
if (!ret)
|
||||||
fall_delay |= (1 << i);
|
fall_delay |= (1 << i);
|
||||||
}
|
}
|
||||||
|
|
||||||
final_rise_delay = get_best_delay(host, rise_delay);
|
|
||||||
final_fall_delay = get_best_delay(host, fall_delay);
|
final_fall_delay = get_best_delay(host, fall_delay);
|
||||||
|
|
||||||
|
skip_fall:
|
||||||
final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
|
final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
|
||||||
/* Rising edge is more stable, prefer to use it */
|
|
||||||
if (final_rise_delay.maxlen >= 10)
|
|
||||||
final_maxlen = final_rise_delay.maxlen;
|
|
||||||
if (final_maxlen == final_rise_delay.maxlen) {
|
if (final_maxlen == final_rise_delay.maxlen) {
|
||||||
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
|
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
|
||||||
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
|
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
|
||||||
@@ -1402,16 +1439,21 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||||||
dev_err(host->dev, "Tune response fail!\n");
|
dev_err(host->dev, "Tune response fail!\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
if (host->hs400_mode == false) {
|
||||||
ret = msdc_tune_data(mmc, opcode);
|
ret = msdc_tune_data(mmc, opcode);
|
||||||
if (ret == -EIO)
|
if (ret == -EIO)
|
||||||
dev_err(host->dev, "Tune data fail!\n");
|
dev_err(host->dev, "Tune data fail!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
host->saved_tune_para.iocon = readl(host->base + MSDC_IOCON);
|
||||||
|
host->saved_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int msdc_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
|
static int msdc_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
struct msdc_host *host = mmc_priv(mmc);
|
struct msdc_host *host = mmc_priv(mmc);
|
||||||
|
host->hs400_mode = true;
|
||||||
|
|
||||||
writel(host->hs400_ds_delay, host->base + PAD_DS_TUNE);
|
writel(host->hs400_ds_delay, host->base + PAD_DS_TUNE);
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -1065,7 +1065,7 @@ static int mxcmci_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
if (pdata)
|
if (pdata)
|
||||||
dat3_card_detect = pdata->dat3_card_detect;
|
dat3_card_detect = pdata->dat3_card_detect;
|
||||||
else if (!(mmc->caps & MMC_CAP_NONREMOVABLE)
|
else if (mmc_card_is_removable(mmc)
|
||||||
&& !of_property_read_bool(pdev->dev.of_node, "cd-gpios"))
|
&& !of_property_read_bool(pdev->dev.of_node, "cd-gpios"))
|
||||||
dat3_card_detect = true;
|
dat3_card_detect = true;
|
||||||
|
|
||||||
|
@@ -38,7 +38,6 @@ struct realtek_pci_sdmmc {
|
|||||||
struct rtsx_pcr *pcr;
|
struct rtsx_pcr *pcr;
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct mmc_request *mrq;
|
struct mmc_request *mrq;
|
||||||
struct workqueue_struct *workq;
|
|
||||||
#define SDMMC_WORKQ_NAME "rtsx_pci_sdmmc_workq"
|
#define SDMMC_WORKQ_NAME "rtsx_pci_sdmmc_workq"
|
||||||
|
|
||||||
struct work_struct work;
|
struct work_struct work;
|
||||||
@@ -244,7 +243,7 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
|
|||||||
stat_idx = sd_status_index(rsp_type);
|
stat_idx = sd_status_index(rsp_type);
|
||||||
|
|
||||||
if (rsp_type == SD_RSP_TYPE_R1b)
|
if (rsp_type == SD_RSP_TYPE_R1b)
|
||||||
timeout = 3000;
|
timeout = cmd->busy_timeout ? cmd->busy_timeout : 3000;
|
||||||
|
|
||||||
if (cmd->opcode == SD_SWITCH_VOLTAGE) {
|
if (cmd->opcode == SD_SWITCH_VOLTAGE) {
|
||||||
err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
|
err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
|
||||||
@@ -885,7 +884,7 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||||||
if (sd_rw_cmd(mrq->cmd) || sdio_extblock_cmd(mrq->cmd, data))
|
if (sd_rw_cmd(mrq->cmd) || sdio_extblock_cmd(mrq->cmd, data))
|
||||||
host->using_cookie = sd_pre_dma_transfer(host, data, false);
|
host->using_cookie = sd_pre_dma_transfer(host, data, false);
|
||||||
|
|
||||||
queue_work(host->workq, &host->work);
|
schedule_work(&host->work);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sd_set_bus_width(struct realtek_pci_sdmmc *host,
|
static int sd_set_bus_width(struct realtek_pci_sdmmc *host,
|
||||||
@@ -1360,7 +1359,7 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host)
|
|||||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
|
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
|
||||||
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
|
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
|
||||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
|
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
|
||||||
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
|
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_ERASE;
|
||||||
mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
|
mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
|
||||||
mmc->max_current_330 = 400;
|
mmc->max_current_330 = 400;
|
||||||
mmc->max_current_180 = 800;
|
mmc->max_current_180 = 800;
|
||||||
@@ -1404,11 +1403,6 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
host = mmc_priv(mmc);
|
||||||
host->workq = create_singlethread_workqueue(SDMMC_WORKQ_NAME);
|
|
||||||
if (!host->workq) {
|
|
||||||
mmc_free_host(mmc);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
host->pcr = pcr;
|
host->pcr = pcr;
|
||||||
host->mmc = mmc;
|
host->mmc = mmc;
|
||||||
host->pdev = pdev;
|
host->pdev = pdev;
|
||||||
@@ -1462,9 +1456,7 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
|
|||||||
mmc_remove_host(mmc);
|
mmc_remove_host(mmc);
|
||||||
host->eject = true;
|
host->eject = true;
|
||||||
|
|
||||||
flush_workqueue(host->workq);
|
flush_work(&host->work);
|
||||||
destroy_workqueue(host->workq);
|
|
||||||
host->workq = NULL;
|
|
||||||
|
|
||||||
mmc_free_host(mmc);
|
mmc_free_host(mmc);
|
||||||
|
|
||||||
|
@@ -1365,7 +1365,7 @@ static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
|
|||||||
.no_detect = 1,
|
.no_detect = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_CPU_FREQ
|
#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
|
||||||
|
|
||||||
static int s3cmci_cpufreq_transition(struct notifier_block *nb,
|
static int s3cmci_cpufreq_transition(struct notifier_block *nb,
|
||||||
unsigned long val, void *data)
|
unsigned long val, void *data)
|
||||||
|
@@ -74,7 +74,7 @@ struct s3cmci_host {
|
|||||||
struct dentry *debug_regs;
|
struct dentry *debug_regs;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_CPU_FREQ
|
#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
|
||||||
struct notifier_block freq_transition;
|
struct notifier_block freq_transition;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
@@ -532,11 +532,6 @@ static int sdhci_acpi_resume(struct device *dev)
|
|||||||
return sdhci_resume_host(c->host);
|
return sdhci_resume_host(c->host);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define sdhci_acpi_suspend NULL
|
|
||||||
#define sdhci_acpi_resume NULL
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
@@ -560,8 +555,7 @@ static int sdhci_acpi_runtime_resume(struct device *dev)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct dev_pm_ops sdhci_acpi_pm_ops = {
|
static const struct dev_pm_ops sdhci_acpi_pm_ops = {
|
||||||
.suspend = sdhci_acpi_suspend,
|
SET_SYSTEM_SLEEP_PM_OPS(sdhci_acpi_suspend, sdhci_acpi_resume)
|
||||||
.resume = sdhci_acpi_resume,
|
|
||||||
SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend,
|
SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend,
|
||||||
sdhci_acpi_runtime_resume, NULL)
|
sdhci_acpi_runtime_resume, NULL)
|
||||||
};
|
};
|
||||||
|
@@ -264,12 +264,12 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
dev_dbg(dev, "non-removable=%c\n",
|
dev_dbg(dev, "non-removable=%c\n",
|
||||||
(host->mmc->caps & MMC_CAP_NONREMOVABLE) ? 'Y' : 'N');
|
mmc_card_is_removable(host->mmc) ? 'N' : 'Y');
|
||||||
dev_dbg(dev, "cd_gpio %c, wp_gpio %c\n",
|
dev_dbg(dev, "cd_gpio %c, wp_gpio %c\n",
|
||||||
(mmc_gpio_get_cd(host->mmc) != -ENOSYS) ? 'Y' : 'N',
|
(mmc_gpio_get_cd(host->mmc) != -ENOSYS) ? 'Y' : 'N',
|
||||||
(mmc_gpio_get_ro(host->mmc) != -ENOSYS) ? 'Y' : 'N');
|
(mmc_gpio_get_ro(host->mmc) != -ENOSYS) ? 'Y' : 'N');
|
||||||
|
|
||||||
if (host->mmc->caps & MMC_CAP_NONREMOVABLE)
|
if (!mmc_card_is_removable(host->mmc))
|
||||||
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||||
|
|
||||||
dev_dbg(dev, "is_8bit=%c\n",
|
dev_dbg(dev, "is_8bit=%c\n",
|
||||||
@@ -288,7 +288,7 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* if device is eMMC, emulate card insert right here */
|
/* if device is eMMC, emulate card insert right here */
|
||||||
if (host->mmc->caps & MMC_CAP_NONREMOVABLE) {
|
if (!mmc_card_is_removable(host->mmc)) {
|
||||||
ret = sdhci_bcm_kona_sd_card_emulate(host, 1);
|
ret = sdhci_bcm_kona_sd_card_emulate(host, 1);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev,
|
dev_err(dev,
|
||||||
@@ -326,7 +326,7 @@ err_pltfm_free:
|
|||||||
static struct platform_driver sdhci_bcm_kona_driver = {
|
static struct platform_driver sdhci_bcm_kona_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-kona",
|
.name = "sdhci-kona",
|
||||||
.pm = SDHCI_PLTFM_PMOPS,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
.of_match_table = sdhci_bcm_kona_of_match,
|
.of_match_table = sdhci_bcm_kona_of_match,
|
||||||
},
|
},
|
||||||
.probe = sdhci_bcm_kona_probe,
|
.probe = sdhci_bcm_kona_probe,
|
||||||
|
@@ -1,204 +0,0 @@
|
|||||||
/*
|
|
||||||
* BCM2835 SDHCI
|
|
||||||
* Copyright (C) 2012 Stephen Warren
|
|
||||||
* Based on U-Boot's MMC driver for the BCM2835 by Oleksandr Tymoshenko & me
|
|
||||||
* Portions of the code there were obviously based on the Linux kernel at:
|
|
||||||
* git://github.com/raspberrypi/linux.git rpi-3.6.y
|
|
||||||
* commit f5b930b "Main bcm2708 linux port" signed-off-by Dom Cobley.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/mmc/host.h>
|
|
||||||
#include "sdhci-pltfm.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 400KHz is max freq for card ID etc. Use that as min card clock. We need to
|
|
||||||
* know the min to enable static calculation of max BCM2835_SDHCI_WRITE_DELAY.
|
|
||||||
*/
|
|
||||||
#define MIN_FREQ 400000
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The Arasan has a bugette whereby it may lose the content of successive
|
|
||||||
* writes to registers that are within two SD-card clock cycles of each other
|
|
||||||
* (a clock domain crossing problem). It seems, however, that the data
|
|
||||||
* register does not have this problem, which is just as well - otherwise we'd
|
|
||||||
* have to nobble the DMA engine too.
|
|
||||||
*
|
|
||||||
* This should probably be dynamically calculated based on the actual card
|
|
||||||
* frequency. However, this is the longest we'll have to wait, and doesn't
|
|
||||||
* seem to slow access down too much, so the added complexity doesn't seem
|
|
||||||
* worth it for now.
|
|
||||||
*
|
|
||||||
* 1/MIN_FREQ is (max) time per tick of eMMC clock.
|
|
||||||
* 2/MIN_FREQ is time for two ticks.
|
|
||||||
* Multiply by 1000000 to get uS per two ticks.
|
|
||||||
* *1000000 for uSecs.
|
|
||||||
* +1 for hack rounding.
|
|
||||||
*/
|
|
||||||
#define BCM2835_SDHCI_WRITE_DELAY (((2 * 1000000) / MIN_FREQ) + 1)
|
|
||||||
|
|
||||||
struct bcm2835_sdhci {
|
|
||||||
u32 shadow;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void bcm2835_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
|
|
||||||
{
|
|
||||||
writel(val, host->ioaddr + reg);
|
|
||||||
|
|
||||||
udelay(BCM2835_SDHCI_WRITE_DELAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32 bcm2835_sdhci_readl(struct sdhci_host *host, int reg)
|
|
||||||
{
|
|
||||||
u32 val = readl(host->ioaddr + reg);
|
|
||||||
|
|
||||||
if (reg == SDHCI_CAPABILITIES)
|
|
||||||
val |= SDHCI_CAN_VDD_330;
|
|
||||||
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bcm2835_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
|
|
||||||
{
|
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
|
||||||
struct bcm2835_sdhci *bcm2835_host = sdhci_pltfm_priv(pltfm_host);
|
|
||||||
u32 oldval = (reg == SDHCI_COMMAND) ? bcm2835_host->shadow :
|
|
||||||
bcm2835_sdhci_readl(host, reg & ~3);
|
|
||||||
u32 word_num = (reg >> 1) & 1;
|
|
||||||
u32 word_shift = word_num * 16;
|
|
||||||
u32 mask = 0xffff << word_shift;
|
|
||||||
u32 newval = (oldval & ~mask) | (val << word_shift);
|
|
||||||
|
|
||||||
if (reg == SDHCI_TRANSFER_MODE)
|
|
||||||
bcm2835_host->shadow = newval;
|
|
||||||
else
|
|
||||||
bcm2835_sdhci_writel(host, newval, reg & ~3);
|
|
||||||
}
|
|
||||||
|
|
||||||
static u16 bcm2835_sdhci_readw(struct sdhci_host *host, int reg)
|
|
||||||
{
|
|
||||||
u32 val = bcm2835_sdhci_readl(host, (reg & ~3));
|
|
||||||
u32 word_num = (reg >> 1) & 1;
|
|
||||||
u32 word_shift = word_num * 16;
|
|
||||||
u32 word = (val >> word_shift) & 0xffff;
|
|
||||||
|
|
||||||
return word;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bcm2835_sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
|
|
||||||
{
|
|
||||||
u32 oldval = bcm2835_sdhci_readl(host, reg & ~3);
|
|
||||||
u32 byte_num = reg & 3;
|
|
||||||
u32 byte_shift = byte_num * 8;
|
|
||||||
u32 mask = 0xff << byte_shift;
|
|
||||||
u32 newval = (oldval & ~mask) | (val << byte_shift);
|
|
||||||
|
|
||||||
bcm2835_sdhci_writel(host, newval, reg & ~3);
|
|
||||||
}
|
|
||||||
|
|
||||||
static u8 bcm2835_sdhci_readb(struct sdhci_host *host, int reg)
|
|
||||||
{
|
|
||||||
u32 val = bcm2835_sdhci_readl(host, (reg & ~3));
|
|
||||||
u32 byte_num = reg & 3;
|
|
||||||
u32 byte_shift = byte_num * 8;
|
|
||||||
u32 byte = (val >> byte_shift) & 0xff;
|
|
||||||
|
|
||||||
return byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned int bcm2835_sdhci_get_min_clock(struct sdhci_host *host)
|
|
||||||
{
|
|
||||||
return MIN_FREQ;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct sdhci_ops bcm2835_sdhci_ops = {
|
|
||||||
.write_l = bcm2835_sdhci_writel,
|
|
||||||
.write_w = bcm2835_sdhci_writew,
|
|
||||||
.write_b = bcm2835_sdhci_writeb,
|
|
||||||
.read_l = bcm2835_sdhci_readl,
|
|
||||||
.read_w = bcm2835_sdhci_readw,
|
|
||||||
.read_b = bcm2835_sdhci_readb,
|
|
||||||
.set_clock = sdhci_set_clock,
|
|
||||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
|
||||||
.get_min_clock = bcm2835_sdhci_get_min_clock,
|
|
||||||
.set_bus_width = sdhci_set_bus_width,
|
|
||||||
.reset = sdhci_reset,
|
|
||||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct sdhci_pltfm_data bcm2835_sdhci_pdata = {
|
|
||||||
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
|
||||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
|
|
||||||
.ops = &bcm2835_sdhci_ops,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int bcm2835_sdhci_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct sdhci_host *host;
|
|
||||||
struct bcm2835_sdhci *bcm2835_host;
|
|
||||||
struct sdhci_pltfm_host *pltfm_host;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
host = sdhci_pltfm_init(pdev, &bcm2835_sdhci_pdata,
|
|
||||||
sizeof(*bcm2835_host));
|
|
||||||
if (IS_ERR(host))
|
|
||||||
return PTR_ERR(host);
|
|
||||||
|
|
||||||
pltfm_host = sdhci_priv(host);
|
|
||||||
|
|
||||||
pltfm_host->clk = devm_clk_get(&pdev->dev, NULL);
|
|
||||||
if (IS_ERR(pltfm_host->clk)) {
|
|
||||||
ret = PTR_ERR(pltfm_host->clk);
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
ret = clk_prepare_enable(pltfm_host->clk);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(&pdev->dev, "failed to enable host clk\n");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = sdhci_add_host(host);
|
|
||||||
if (ret)
|
|
||||||
goto err_clk;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
err_clk:
|
|
||||||
clk_disable_unprepare(pltfm_host->clk);
|
|
||||||
err:
|
|
||||||
sdhci_pltfm_free(pdev);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct of_device_id bcm2835_sdhci_of_match[] = {
|
|
||||||
{ .compatible = "brcm,bcm2835-sdhci" },
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, bcm2835_sdhci_of_match);
|
|
||||||
|
|
||||||
static struct platform_driver bcm2835_sdhci_driver = {
|
|
||||||
.driver = {
|
|
||||||
.name = "sdhci-bcm2835",
|
|
||||||
.of_match_table = bcm2835_sdhci_of_match,
|
|
||||||
.pm = SDHCI_PLTFM_PMOPS,
|
|
||||||
},
|
|
||||||
.probe = bcm2835_sdhci_probe,
|
|
||||||
.remove = sdhci_pltfm_unregister,
|
|
||||||
};
|
|
||||||
module_platform_driver(bcm2835_sdhci_driver);
|
|
||||||
|
|
||||||
MODULE_DESCRIPTION("BCM2835 SDHCI driver");
|
|
||||||
MODULE_AUTHOR("Stephen Warren");
|
|
||||||
MODULE_LICENSE("GPL v2");
|
|
143
drivers/mmc/host/sdhci-brcmstb.c
Normal file
143
drivers/mmc/host/sdhci-brcmstb.c
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* sdhci-brcmstb.c Support for SDHCI on Broadcom BRCMSTB SoC's
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Broadcom Corporation
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/mmc/host.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
|
||||||
|
#include "sdhci-pltfm.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
|
||||||
|
static int sdhci_brcmstb_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
int res;
|
||||||
|
|
||||||
|
res = sdhci_suspend_host(host);
|
||||||
|
if (res)
|
||||||
|
return res;
|
||||||
|
clk_disable_unprepare(pltfm_host->clk);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdhci_brcmstb_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = clk_prepare_enable(pltfm_host->clk);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
return sdhci_resume_host(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
|
static SIMPLE_DEV_PM_OPS(sdhci_brcmstb_pmops, sdhci_brcmstb_suspend,
|
||||||
|
sdhci_brcmstb_resume);
|
||||||
|
|
||||||
|
static const struct sdhci_ops sdhci_brcmstb_ops = {
|
||||||
|
.set_clock = sdhci_set_clock,
|
||||||
|
.set_bus_width = sdhci_set_bus_width,
|
||||||
|
.reset = sdhci_reset,
|
||||||
|
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct sdhci_pltfm_data sdhci_brcmstb_pdata = {
|
||||||
|
.ops = &sdhci_brcmstb_ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int sdhci_brcmstb_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host;
|
||||||
|
struct sdhci_pltfm_host *pltfm_host;
|
||||||
|
struct clk *clk;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(clk)) {
|
||||||
|
dev_err(&pdev->dev, "Clock not found in Device Tree\n");
|
||||||
|
clk = NULL;
|
||||||
|
}
|
||||||
|
res = clk_prepare_enable(clk);
|
||||||
|
if (res)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
host = sdhci_pltfm_init(pdev, &sdhci_brcmstb_pdata, 0);
|
||||||
|
if (IS_ERR(host)) {
|
||||||
|
res = PTR_ERR(host);
|
||||||
|
goto err_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable MMC_CAP2_HC_ERASE_SZ for better max discard calculations */
|
||||||
|
host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ;
|
||||||
|
|
||||||
|
sdhci_get_of_property(pdev);
|
||||||
|
mmc_of_parse(host->mmc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Supply the existing CAPS, but clear the UHS modes. This
|
||||||
|
* will allow these modes to be specified by device tree
|
||||||
|
* properties through mmc_of_parse().
|
||||||
|
*/
|
||||||
|
host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
|
||||||
|
host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
|
||||||
|
host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
|
||||||
|
SDHCI_SUPPORT_DDR50);
|
||||||
|
host->quirks |= SDHCI_QUIRK_MISSING_CAPS |
|
||||||
|
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
||||||
|
|
||||||
|
res = sdhci_add_host(host);
|
||||||
|
if (res)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
pltfm_host = sdhci_priv(host);
|
||||||
|
pltfm_host->clk = clk;
|
||||||
|
return res;
|
||||||
|
|
||||||
|
err:
|
||||||
|
sdhci_pltfm_free(pdev);
|
||||||
|
err_clk:
|
||||||
|
clk_disable_unprepare(clk);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id sdhci_brcm_of_match[] = {
|
||||||
|
{ .compatible = "brcm,bcm7425-sdhci" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver sdhci_brcmstb_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "sdhci-brcmstb",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.pm = &sdhci_brcmstb_pmops,
|
||||||
|
.of_match_table = of_match_ptr(sdhci_brcm_of_match),
|
||||||
|
},
|
||||||
|
.probe = sdhci_brcmstb_probe,
|
||||||
|
.remove = sdhci_pltfm_unregister,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(sdhci_brcmstb_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("SDHCI driver for Broadcom BRCMSTB SoCs");
|
||||||
|
MODULE_AUTHOR("Broadcom");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
@@ -101,7 +101,7 @@ static int sdhci_cns3xxx_probe(struct platform_device *pdev)
|
|||||||
static struct platform_driver sdhci_cns3xxx_driver = {
|
static struct platform_driver sdhci_cns3xxx_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-cns3xxx",
|
.name = "sdhci-cns3xxx",
|
||||||
.pm = SDHCI_PLTFM_PMOPS,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
},
|
},
|
||||||
.probe = sdhci_cns3xxx_probe,
|
.probe = sdhci_cns3xxx_probe,
|
||||||
.remove = sdhci_pltfm_unregister,
|
.remove = sdhci_pltfm_unregister,
|
||||||
|
@@ -117,7 +117,7 @@ MODULE_DEVICE_TABLE(of, sdhci_dove_of_match_table);
|
|||||||
static struct platform_driver sdhci_dove_driver = {
|
static struct platform_driver sdhci_dove_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-dove",
|
.name = "sdhci-dove",
|
||||||
.pm = SDHCI_PLTFM_PMOPS,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
.of_match_table = sdhci_dove_of_match_table,
|
.of_match_table = sdhci_dove_of_match_table,
|
||||||
},
|
},
|
||||||
.probe = sdhci_dove_probe,
|
.probe = sdhci_dove_probe,
|
||||||
|
@@ -39,11 +39,13 @@
|
|||||||
#define ESDHC_VENDOR_SPEC_VSELECT (1 << 1)
|
#define ESDHC_VENDOR_SPEC_VSELECT (1 << 1)
|
||||||
#define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8)
|
#define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8)
|
||||||
#define ESDHC_WTMK_LVL 0x44
|
#define ESDHC_WTMK_LVL 0x44
|
||||||
|
#define ESDHC_WTMK_DEFAULT_VAL 0x10401040
|
||||||
#define ESDHC_MIX_CTRL 0x48
|
#define ESDHC_MIX_CTRL 0x48
|
||||||
#define ESDHC_MIX_CTRL_DDREN (1 << 3)
|
#define ESDHC_MIX_CTRL_DDREN (1 << 3)
|
||||||
#define ESDHC_MIX_CTRL_AC23EN (1 << 7)
|
#define ESDHC_MIX_CTRL_AC23EN (1 << 7)
|
||||||
#define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22)
|
#define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22)
|
||||||
#define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23)
|
#define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23)
|
||||||
|
#define ESDHC_MIX_CTRL_AUTO_TUNE_EN (1 << 24)
|
||||||
#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
|
#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
|
||||||
#define ESDHC_MIX_CTRL_HS400_EN (1 << 26)
|
#define ESDHC_MIX_CTRL_HS400_EN (1 << 26)
|
||||||
/* Bits 3 and 6 are not SDHCI standard definitions */
|
/* Bits 3 and 6 are not SDHCI standard definitions */
|
||||||
@@ -75,7 +77,8 @@
|
|||||||
#define ESDHC_TUNING_CTRL 0xcc
|
#define ESDHC_TUNING_CTRL 0xcc
|
||||||
#define ESDHC_STD_TUNING_EN (1 << 24)
|
#define ESDHC_STD_TUNING_EN (1 << 24)
|
||||||
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
|
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
|
||||||
#define ESDHC_TUNING_START_TAP 0x1
|
#define ESDHC_TUNING_START_TAP_DEFAULT 0x1
|
||||||
|
#define ESDHC_TUNING_START_TAP_MASK 0xff
|
||||||
#define ESDHC_TUNING_STEP_MASK 0x00070000
|
#define ESDHC_TUNING_STEP_MASK 0x00070000
|
||||||
#define ESDHC_TUNING_STEP_SHIFT 16
|
#define ESDHC_TUNING_STEP_SHIFT 16
|
||||||
|
|
||||||
@@ -299,7 +302,8 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
|
|||||||
/* imx6q/dl does not have cap_1 register, fake one */
|
/* imx6q/dl does not have cap_1 register, fake one */
|
||||||
val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
|
val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
|
||||||
| SDHCI_SUPPORT_SDR50
|
| SDHCI_SUPPORT_SDR50
|
||||||
| SDHCI_USE_SDR50_TUNING;
|
| SDHCI_USE_SDR50_TUNING
|
||||||
|
| (SDHCI_TUNING_MODE_3 << SDHCI_RETUNING_MODE_SHIFT);
|
||||||
|
|
||||||
if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
|
if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
|
||||||
val |= SDHCI_SUPPORT_HS400;
|
val |= SDHCI_SUPPORT_HS400;
|
||||||
@@ -469,32 +473,29 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
|||||||
writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
|
writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
|
||||||
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
|
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
|
||||||
new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
if (val & SDHCI_CTRL_TUNED_CLK)
|
if (val & SDHCI_CTRL_TUNED_CLK) {
|
||||||
new_val |= ESDHC_MIX_CTRL_SMPCLK_SEL;
|
new_val |= ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||||
else
|
new_val |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
|
||||||
|
} else {
|
||||||
new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
|
new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||||
|
new_val &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
|
||||||
|
}
|
||||||
writel(new_val , host->ioaddr + ESDHC_MIX_CTRL);
|
writel(new_val , host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
|
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
|
||||||
u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
|
u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
|
||||||
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
u32 tuning_ctrl;
|
|
||||||
if (val & SDHCI_CTRL_TUNED_CLK) {
|
if (val & SDHCI_CTRL_TUNED_CLK) {
|
||||||
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
|
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||||
} else {
|
} else {
|
||||||
v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
|
v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||||
m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
|
m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
|
||||||
|
m &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (val & SDHCI_CTRL_EXEC_TUNING) {
|
if (val & SDHCI_CTRL_EXEC_TUNING) {
|
||||||
v |= ESDHC_MIX_CTRL_EXE_TUNE;
|
v |= ESDHC_MIX_CTRL_EXE_TUNE;
|
||||||
m |= ESDHC_MIX_CTRL_FBCLK_SEL;
|
m |= ESDHC_MIX_CTRL_FBCLK_SEL;
|
||||||
tuning_ctrl = readl(host->ioaddr + ESDHC_TUNING_CTRL);
|
m |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
|
||||||
tuning_ctrl |= ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP;
|
|
||||||
if (imx_data->boarddata.tuning_step) {
|
|
||||||
tuning_ctrl &= ~ESDHC_TUNING_STEP_MASK;
|
|
||||||
tuning_ctrl |= imx_data->boarddata.tuning_step << ESDHC_TUNING_STEP_SHIFT;
|
|
||||||
}
|
|
||||||
writel(tuning_ctrl, host->ioaddr + ESDHC_TUNING_CTRL);
|
|
||||||
} else {
|
} else {
|
||||||
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
|
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
|
||||||
}
|
}
|
||||||
@@ -751,6 +752,7 @@ static void esdhc_post_tuning(struct sdhci_host *host)
|
|||||||
|
|
||||||
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
|
reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
|
||||||
|
reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
|
||||||
writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
|
writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -838,6 +840,11 @@ static void esdhc_set_strobe_dll(struct sdhci_host *host)
|
|||||||
u32 v;
|
u32 v;
|
||||||
|
|
||||||
if (host->mmc->actual_clock > ESDHC_STROBE_DLL_CLK_FREQ) {
|
if (host->mmc->actual_clock > ESDHC_STROBE_DLL_CLK_FREQ) {
|
||||||
|
/* disable clock before enabling strobe dll */
|
||||||
|
writel(readl(host->ioaddr + ESDHC_VENDOR_SPEC) &
|
||||||
|
~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
|
||||||
|
host->ioaddr + ESDHC_VENDOR_SPEC);
|
||||||
|
|
||||||
/* force a reset on strobe dll */
|
/* force a reset on strobe dll */
|
||||||
writel(ESDHC_STROBE_DLL_CTRL_RESET,
|
writel(ESDHC_STROBE_DLL_CTRL_RESET,
|
||||||
host->ioaddr + ESDHC_STROBE_DLL_CTRL);
|
host->ioaddr + ESDHC_STROBE_DLL_CTRL);
|
||||||
@@ -899,6 +906,8 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
|
|||||||
m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN;
|
m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN;
|
||||||
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
|
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
imx_data->is_ddr = 1;
|
imx_data->is_ddr = 1;
|
||||||
|
/* update clock after enable DDR for strobe DLL lock */
|
||||||
|
host->ops->set_clock(host, host->clock);
|
||||||
esdhc_set_strobe_dll(host);
|
esdhc_set_strobe_dll(host);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -957,6 +966,62 @@ static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
|
|||||||
.ops = &sdhci_esdhc_ops,
|
.ops = &sdhci_esdhc_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
if (esdhc_is_usdhc(imx_data)) {
|
||||||
|
/*
|
||||||
|
* The imx6q ROM code will change the default watermark
|
||||||
|
* level setting to something insane. Change it back here.
|
||||||
|
*/
|
||||||
|
writel(ESDHC_WTMK_DEFAULT_VAL, host->ioaddr + ESDHC_WTMK_LVL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ROM code will change the bit burst_length_enable setting
|
||||||
|
* to zero if this usdhc is choosed to boot system. Change
|
||||||
|
* it back here, otherwise it will impact the performance a
|
||||||
|
* lot. This bit is used to enable/disable the burst length
|
||||||
|
* for the external AHB2AXI bridge, it's usefully especially
|
||||||
|
* for INCR transfer because without burst length indicator,
|
||||||
|
* the AHB2AXI bridge does not know the burst length in
|
||||||
|
* advance. And without burst length indicator, AHB INCR
|
||||||
|
* transfer can only be converted to singles on the AXI side.
|
||||||
|
*/
|
||||||
|
writel(readl(host->ioaddr + SDHCI_HOST_CONTROL)
|
||||||
|
| ESDHC_BURST_LEN_EN_INCR,
|
||||||
|
host->ioaddr + SDHCI_HOST_CONTROL);
|
||||||
|
/*
|
||||||
|
* errata ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
|
||||||
|
* TO1.1, it's harmless for MX6SL
|
||||||
|
*/
|
||||||
|
writel(readl(host->ioaddr + 0x6c) | BIT(7),
|
||||||
|
host->ioaddr + 0x6c);
|
||||||
|
|
||||||
|
/* disable DLL_CTRL delay line settings */
|
||||||
|
writel(0x0, host->ioaddr + ESDHC_DLL_CTRL);
|
||||||
|
|
||||||
|
if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
|
||||||
|
tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
|
||||||
|
tmp |= ESDHC_STD_TUNING_EN |
|
||||||
|
ESDHC_TUNING_START_TAP_DEFAULT;
|
||||||
|
if (imx_data->boarddata.tuning_start_tap) {
|
||||||
|
tmp &= ~ESDHC_TUNING_START_TAP_MASK;
|
||||||
|
tmp |= imx_data->boarddata.tuning_start_tap;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imx_data->boarddata.tuning_step) {
|
||||||
|
tmp &= ~ESDHC_TUNING_STEP_MASK;
|
||||||
|
tmp |= imx_data->boarddata.tuning_step
|
||||||
|
<< ESDHC_TUNING_STEP_SHIFT;
|
||||||
|
}
|
||||||
|
writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
static int
|
static int
|
||||||
sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
|
sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
|
||||||
@@ -975,6 +1040,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
|
|||||||
boarddata->wp_type = ESDHC_WP_GPIO;
|
boarddata->wp_type = ESDHC_WP_GPIO;
|
||||||
|
|
||||||
of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
|
of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
|
||||||
|
of_property_read_u32(np, "fsl,tuning-start-tap",
|
||||||
|
&boarddata->tuning_start_tap);
|
||||||
|
|
||||||
if (of_find_property(np, "no-1-8-v", NULL))
|
if (of_find_property(np, "no-1-8-v", NULL))
|
||||||
boarddata->support_vsel = false;
|
boarddata->support_vsel = false;
|
||||||
@@ -1147,58 +1214,27 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||||||
if (IS_ERR(imx_data->pins_default))
|
if (IS_ERR(imx_data->pins_default))
|
||||||
dev_warn(mmc_dev(host->mmc), "could not get default state\n");
|
dev_warn(mmc_dev(host->mmc), "could not get default state\n");
|
||||||
|
|
||||||
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
|
||||||
|
|
||||||
if (imx_data->socdata->flags & ESDHC_FLAG_ENGCM07207)
|
if (imx_data->socdata->flags & ESDHC_FLAG_ENGCM07207)
|
||||||
/* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
|
/* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
|
||||||
host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK
|
host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK
|
||||||
| SDHCI_QUIRK_BROKEN_ADMA;
|
| SDHCI_QUIRK_BROKEN_ADMA;
|
||||||
|
|
||||||
/*
|
|
||||||
* The imx6q ROM code will change the default watermark level setting
|
|
||||||
* to something insane. Change it back here.
|
|
||||||
*/
|
|
||||||
if (esdhc_is_usdhc(imx_data)) {
|
if (esdhc_is_usdhc(imx_data)) {
|
||||||
writel(0x10401040, host->ioaddr + ESDHC_WTMK_LVL);
|
|
||||||
|
|
||||||
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
||||||
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
||||||
|
|
||||||
/*
|
|
||||||
* ROM code will change the bit burst_length_enable setting
|
|
||||||
* to zero if this usdhc is choosed to boot system. Change
|
|
||||||
* it back here, otherwise it will impact the performance a
|
|
||||||
* lot. This bit is used to enable/disable the burst length
|
|
||||||
* for the external AHB2AXI bridge, it's usefully especially
|
|
||||||
* for INCR transfer because without burst length indicator,
|
|
||||||
* the AHB2AXI bridge does not know the burst length in
|
|
||||||
* advance. And without burst length indicator, AHB INCR
|
|
||||||
* transfer can only be converted to singles on the AXI side.
|
|
||||||
*/
|
|
||||||
writel(readl(host->ioaddr + SDHCI_HOST_CONTROL)
|
|
||||||
| ESDHC_BURST_LEN_EN_INCR,
|
|
||||||
host->ioaddr + SDHCI_HOST_CONTROL);
|
|
||||||
|
|
||||||
if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
|
if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
|
||||||
host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
|
host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
|
||||||
|
|
||||||
/*
|
/* clear tuning bits in case ROM has set it already */
|
||||||
* errata ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
|
writel(0x0, host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
* TO1.1, it's harmless for MX6SL
|
writel(0x0, host->ioaddr + SDHCI_ACMD12_ERR);
|
||||||
*/
|
writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
|
||||||
writel(readl(host->ioaddr + 0x6c) | BIT(7),
|
|
||||||
host->ioaddr + 0x6c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
|
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
|
||||||
sdhci_esdhc_ops.platform_execute_tuning =
|
sdhci_esdhc_ops.platform_execute_tuning =
|
||||||
esdhc_executing_tuning;
|
esdhc_executing_tuning;
|
||||||
|
|
||||||
if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
|
|
||||||
writel(readl(host->ioaddr + ESDHC_TUNING_CTRL) |
|
|
||||||
ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP,
|
|
||||||
host->ioaddr + ESDHC_TUNING_CTRL);
|
|
||||||
|
|
||||||
if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
|
if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
|
||||||
host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
|
host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
|
||||||
|
|
||||||
@@ -1212,6 +1248,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||||||
if (err)
|
if (err)
|
||||||
goto disable_clk;
|
goto disable_clk;
|
||||||
|
|
||||||
|
sdhci_esdhc_imx_hwinit(host);
|
||||||
|
|
||||||
err = sdhci_add_host(host);
|
err = sdhci_add_host(host);
|
||||||
if (err)
|
if (err)
|
||||||
goto disable_clk;
|
goto disable_clk;
|
||||||
@@ -1255,6 +1293,25 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int sdhci_esdhc_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
return sdhci_suspend_host(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdhci_esdhc_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
/* re-initialize hw state in case it's lost in low power mode */
|
||||||
|
sdhci_esdhc_imx_hwinit(host);
|
||||||
|
|
||||||
|
return sdhci_resume_host(host);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static int sdhci_esdhc_runtime_suspend(struct device *dev)
|
static int sdhci_esdhc_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
@@ -1291,7 +1348,7 @@ static int sdhci_esdhc_runtime_resume(struct device *dev)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct dev_pm_ops sdhci_esdhc_pmops = {
|
static const struct dev_pm_ops sdhci_esdhc_pmops = {
|
||||||
SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_pltfm_resume)
|
SET_SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume)
|
||||||
SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
|
SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
|
||||||
sdhci_esdhc_runtime_resume, NULL)
|
sdhci_esdhc_runtime_resume, NULL)
|
||||||
};
|
};
|
||||||
|
@@ -164,8 +164,17 @@ static const struct sdhci_pltfm_data sdhci_iproc_pltfm_data = {
|
|||||||
|
|
||||||
static const struct sdhci_iproc_data iproc_data = {
|
static const struct sdhci_iproc_data iproc_data = {
|
||||||
.pdata = &sdhci_iproc_pltfm_data,
|
.pdata = &sdhci_iproc_pltfm_data,
|
||||||
.caps = 0x05E90000,
|
.caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT)
|
||||||
.caps1 = 0x00000064,
|
& SDHCI_MAX_BLOCK_MASK) |
|
||||||
|
SDHCI_CAN_VDD_330 |
|
||||||
|
SDHCI_CAN_VDD_180 |
|
||||||
|
SDHCI_CAN_DO_SUSPEND |
|
||||||
|
SDHCI_CAN_DO_HISPD |
|
||||||
|
SDHCI_CAN_DO_ADMA2 |
|
||||||
|
SDHCI_CAN_DO_SDMA,
|
||||||
|
.caps1 = SDHCI_DRIVER_TYPE_C |
|
||||||
|
SDHCI_DRIVER_TYPE_D |
|
||||||
|
SDHCI_SUPPORT_DDR50,
|
||||||
.mmc_caps = MMC_CAP_1_8V_DDR,
|
.mmc_caps = MMC_CAP_1_8V_DDR,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -251,7 +260,7 @@ static struct platform_driver sdhci_iproc_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-iproc",
|
.name = "sdhci-iproc",
|
||||||
.of_match_table = sdhci_iproc_of_match,
|
.of_match_table = sdhci_iproc_of_match,
|
||||||
.pm = SDHCI_PLTFM_PMOPS,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
},
|
},
|
||||||
.probe = sdhci_iproc_probe,
|
.probe = sdhci_iproc_probe,
|
||||||
.remove = sdhci_pltfm_unregister,
|
.remove = sdhci_pltfm_unregister,
|
||||||
|
@@ -32,6 +32,21 @@
|
|||||||
#define CORE_POWER 0x0
|
#define CORE_POWER 0x0
|
||||||
#define CORE_SW_RST BIT(7)
|
#define CORE_SW_RST BIT(7)
|
||||||
|
|
||||||
|
#define CORE_PWRCTL_STATUS 0xdc
|
||||||
|
#define CORE_PWRCTL_MASK 0xe0
|
||||||
|
#define CORE_PWRCTL_CLEAR 0xe4
|
||||||
|
#define CORE_PWRCTL_CTL 0xe8
|
||||||
|
#define CORE_PWRCTL_BUS_OFF BIT(0)
|
||||||
|
#define CORE_PWRCTL_BUS_ON BIT(1)
|
||||||
|
#define CORE_PWRCTL_IO_LOW BIT(2)
|
||||||
|
#define CORE_PWRCTL_IO_HIGH BIT(3)
|
||||||
|
#define CORE_PWRCTL_BUS_SUCCESS BIT(0)
|
||||||
|
#define CORE_PWRCTL_IO_SUCCESS BIT(2)
|
||||||
|
#define REQ_BUS_OFF BIT(0)
|
||||||
|
#define REQ_BUS_ON BIT(1)
|
||||||
|
#define REQ_IO_LOW BIT(2)
|
||||||
|
#define REQ_IO_HIGH BIT(3)
|
||||||
|
#define INT_MASK 0xf
|
||||||
#define MAX_PHASES 16
|
#define MAX_PHASES 16
|
||||||
#define CORE_DLL_LOCK BIT(7)
|
#define CORE_DLL_LOCK BIT(7)
|
||||||
#define CORE_DLL_EN BIT(16)
|
#define CORE_DLL_EN BIT(16)
|
||||||
@@ -56,6 +71,7 @@
|
|||||||
struct sdhci_msm_host {
|
struct sdhci_msm_host {
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
void __iomem *core_mem; /* MSM SDCC mapped address */
|
void __iomem *core_mem; /* MSM SDCC mapped address */
|
||||||
|
int pwr_irq; /* power irq */
|
||||||
struct clk *clk; /* main SD/MMC bus clock */
|
struct clk *clk; /* main SD/MMC bus clock */
|
||||||
struct clk *pclk; /* SDHC peripheral bus clock */
|
struct clk *pclk; /* SDHC peripheral bus clock */
|
||||||
struct clk *bus_clk; /* SDHC bus voter clock */
|
struct clk *bus_clk; /* SDHC bus voter clock */
|
||||||
@@ -410,6 +426,85 @@ retry:
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
|
||||||
|
unsigned int uhs)
|
||||||
|
{
|
||||||
|
struct mmc_host *mmc = host->mmc;
|
||||||
|
u16 ctrl_2;
|
||||||
|
|
||||||
|
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||||
|
/* Select Bus Speed Mode for host */
|
||||||
|
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
|
||||||
|
switch (uhs) {
|
||||||
|
case MMC_TIMING_UHS_SDR12:
|
||||||
|
ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
|
||||||
|
break;
|
||||||
|
case MMC_TIMING_UHS_SDR25:
|
||||||
|
ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
|
||||||
|
break;
|
||||||
|
case MMC_TIMING_UHS_SDR50:
|
||||||
|
ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
|
||||||
|
break;
|
||||||
|
case MMC_TIMING_MMC_HS200:
|
||||||
|
case MMC_TIMING_UHS_SDR104:
|
||||||
|
ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
|
||||||
|
break;
|
||||||
|
case MMC_TIMING_UHS_DDR50:
|
||||||
|
case MMC_TIMING_MMC_DDR52:
|
||||||
|
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When clock frequency is less than 100MHz, the feedback clock must be
|
||||||
|
* provided and DLL must not be used so that tuning can be skipped. To
|
||||||
|
* provide feedback clock, the mode selection can be any value less
|
||||||
|
* than 3'b011 in bits [2:0] of HOST CONTROL2 register.
|
||||||
|
*/
|
||||||
|
if (host->clock <= 100000000 &&
|
||||||
|
(uhs == MMC_TIMING_MMC_HS400 ||
|
||||||
|
uhs == MMC_TIMING_MMC_HS200 ||
|
||||||
|
uhs == MMC_TIMING_UHS_SDR104))
|
||||||
|
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
|
||||||
|
|
||||||
|
dev_dbg(mmc_dev(mmc), "%s: clock=%u uhs=%u ctrl_2=0x%x\n",
|
||||||
|
mmc_hostname(host->mmc), host->clock, uhs, ctrl_2);
|
||||||
|
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_msm_voltage_switch(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
u32 irq_status, irq_ack = 0;
|
||||||
|
|
||||||
|
irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS);
|
||||||
|
irq_status &= INT_MASK;
|
||||||
|
|
||||||
|
writel_relaxed(irq_status, msm_host->core_mem + CORE_PWRCTL_CLEAR);
|
||||||
|
|
||||||
|
if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF))
|
||||||
|
irq_ack |= CORE_PWRCTL_BUS_SUCCESS;
|
||||||
|
if (irq_status & (CORE_PWRCTL_IO_LOW | CORE_PWRCTL_IO_HIGH))
|
||||||
|
irq_ack |= CORE_PWRCTL_IO_SUCCESS;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The driver has to acknowledge the interrupt, switch voltages and
|
||||||
|
* report back if it succeded or not to this register. The voltage
|
||||||
|
* switches are handled by the sdhci core, so just report success.
|
||||||
|
*/
|
||||||
|
writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = (struct sdhci_host *)data;
|
||||||
|
|
||||||
|
sdhci_msm_voltage_switch(host);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct of_device_id sdhci_msm_dt_match[] = {
|
static const struct of_device_id sdhci_msm_dt_match[] = {
|
||||||
{ .compatible = "qcom,sdhci-msm-v4" },
|
{ .compatible = "qcom,sdhci-msm-v4" },
|
||||||
{},
|
{},
|
||||||
@@ -422,11 +517,13 @@ static const struct sdhci_ops sdhci_msm_ops = {
|
|||||||
.reset = sdhci_reset,
|
.reset = sdhci_reset,
|
||||||
.set_clock = sdhci_set_clock,
|
.set_clock = sdhci_set_clock,
|
||||||
.set_bus_width = sdhci_set_bus_width,
|
.set_bus_width = sdhci_set_bus_width,
|
||||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
.set_uhs_signaling = sdhci_msm_set_uhs_signaling,
|
||||||
|
.voltage_switch = sdhci_msm_voltage_switch,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_pltfm_data sdhci_msm_pdata = {
|
static const struct sdhci_pltfm_data sdhci_msm_pdata = {
|
||||||
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||||
|
SDHCI_QUIRK_NO_CARD_NO_RESET |
|
||||||
SDHCI_QUIRK_SINGLE_POWER_WRITE,
|
SDHCI_QUIRK_SINGLE_POWER_WRITE,
|
||||||
.ops = &sdhci_msm_ops,
|
.ops = &sdhci_msm_ops,
|
||||||
};
|
};
|
||||||
@@ -473,7 +570,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||||||
msm_host->pclk = devm_clk_get(&pdev->dev, "iface");
|
msm_host->pclk = devm_clk_get(&pdev->dev, "iface");
|
||||||
if (IS_ERR(msm_host->pclk)) {
|
if (IS_ERR(msm_host->pclk)) {
|
||||||
ret = PTR_ERR(msm_host->pclk);
|
ret = PTR_ERR(msm_host->pclk);
|
||||||
dev_err(&pdev->dev, "Perpheral clk setup failed (%d)\n", ret);
|
dev_err(&pdev->dev, "Peripheral clk setup failed (%d)\n", ret);
|
||||||
goto bus_clk_disable;
|
goto bus_clk_disable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,6 +642,22 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||||||
CORE_VENDOR_SPEC_CAPABILITIES0);
|
CORE_VENDOR_SPEC_CAPABILITIES0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Setup IRQ for handling power/voltage tasks with PMIC */
|
||||||
|
msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq");
|
||||||
|
if (msm_host->pwr_irq < 0) {
|
||||||
|
dev_err(&pdev->dev, "Get pwr_irq failed (%d)\n",
|
||||||
|
msm_host->pwr_irq);
|
||||||
|
goto clk_disable;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL,
|
||||||
|
sdhci_msm_pwr_irq, IRQF_ONESHOT,
|
||||||
|
dev_name(&pdev->dev), host);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Request IRQ failed (%d)\n", ret);
|
||||||
|
goto clk_disable;
|
||||||
|
}
|
||||||
|
|
||||||
ret = sdhci_add_host(host);
|
ret = sdhci_add_host(host);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto clk_disable;
|
goto clk_disable;
|
||||||
|
@@ -19,27 +19,136 @@
|
|||||||
* your option) any later version.
|
* your option) any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk-provider.h>
|
||||||
|
#include <linux/mfd/syscon.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/phy/phy.h>
|
#include <linux/phy/phy.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include "sdhci-pltfm.h"
|
#include "sdhci-pltfm.h"
|
||||||
|
|
||||||
#define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c
|
#define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c
|
||||||
|
#define SDHCI_ARASAN_VENDOR_REGISTER 0x78
|
||||||
|
|
||||||
|
#define VENDOR_ENHANCED_STROBE BIT(0)
|
||||||
#define CLK_CTRL_TIMEOUT_SHIFT 16
|
#define CLK_CTRL_TIMEOUT_SHIFT 16
|
||||||
#define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT)
|
#define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT)
|
||||||
#define CLK_CTRL_TIMEOUT_MIN_EXP 13
|
#define CLK_CTRL_TIMEOUT_MIN_EXP 13
|
||||||
|
|
||||||
|
/*
|
||||||
|
* On some SoCs the syscon area has a feature where the upper 16-bits of
|
||||||
|
* each 32-bit register act as a write mask for the lower 16-bits. This allows
|
||||||
|
* atomic updates of the register without locking. This macro is used on SoCs
|
||||||
|
* that have that feature.
|
||||||
|
*/
|
||||||
|
#define HIWORD_UPDATE(val, mask, shift) \
|
||||||
|
((val) << (shift) | (mask) << ((shift) + 16))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct sdhci_arasan_soc_ctl_field - Field used in sdhci_arasan_soc_ctl_map
|
||||||
|
*
|
||||||
|
* @reg: Offset within the syscon of the register containing this field
|
||||||
|
* @width: Number of bits for this field
|
||||||
|
* @shift: Bit offset within @reg of this field (or -1 if not avail)
|
||||||
|
*/
|
||||||
|
struct sdhci_arasan_soc_ctl_field {
|
||||||
|
u32 reg;
|
||||||
|
u16 width;
|
||||||
|
s16 shift;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct sdhci_arasan_soc_ctl_map - Map in syscon to corecfg registers
|
||||||
|
*
|
||||||
|
* It's up to the licensee of the Arsan IP block to make these available
|
||||||
|
* somewhere if needed. Presumably these will be scattered somewhere that's
|
||||||
|
* accessible via the syscon API.
|
||||||
|
*
|
||||||
|
* @baseclkfreq: Where to find corecfg_baseclkfreq
|
||||||
|
* @hiword_update: If true, use HIWORD_UPDATE to access the syscon
|
||||||
|
*/
|
||||||
|
struct sdhci_arasan_soc_ctl_map {
|
||||||
|
struct sdhci_arasan_soc_ctl_field baseclkfreq;
|
||||||
|
bool hiword_update;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct sdhci_arasan_data
|
* struct sdhci_arasan_data
|
||||||
|
* @host: Pointer to the main SDHCI host structure.
|
||||||
* @clk_ahb: Pointer to the AHB clock
|
* @clk_ahb: Pointer to the AHB clock
|
||||||
* @phy: Pointer to the generic phy
|
* @phy: Pointer to the generic phy
|
||||||
|
* @sdcardclk_hw: Struct for the clock we might provide to a PHY.
|
||||||
|
* @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw.
|
||||||
|
* @soc_ctl_base: Pointer to regmap for syscon for soc_ctl registers.
|
||||||
|
* @soc_ctl_map: Map to get offsets into soc_ctl registers.
|
||||||
*/
|
*/
|
||||||
struct sdhci_arasan_data {
|
struct sdhci_arasan_data {
|
||||||
|
struct sdhci_host *host;
|
||||||
struct clk *clk_ahb;
|
struct clk *clk_ahb;
|
||||||
struct phy *phy;
|
struct phy *phy;
|
||||||
|
|
||||||
|
struct clk_hw sdcardclk_hw;
|
||||||
|
struct clk *sdcardclk;
|
||||||
|
|
||||||
|
struct regmap *soc_ctl_base;
|
||||||
|
const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = {
|
||||||
|
.baseclkfreq = { .reg = 0xf000, .width = 8, .shift = 8 },
|
||||||
|
.hiword_update = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sdhci_arasan_syscon_write - Write to a field in soc_ctl registers
|
||||||
|
*
|
||||||
|
* This function allows writing to fields in sdhci_arasan_soc_ctl_map.
|
||||||
|
* Note that if a field is specified as not available (shift < 0) then
|
||||||
|
* this function will silently return an error code. It will be noisy
|
||||||
|
* and print errors for any other (unexpected) errors.
|
||||||
|
*
|
||||||
|
* @host: The sdhci_host
|
||||||
|
* @fld: The field to write to
|
||||||
|
* @val: The value to write
|
||||||
|
*/
|
||||||
|
static int sdhci_arasan_syscon_write(struct sdhci_host *host,
|
||||||
|
const struct sdhci_arasan_soc_ctl_field *fld,
|
||||||
|
u32 val)
|
||||||
|
{
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
struct regmap *soc_ctl_base = sdhci_arasan->soc_ctl_base;
|
||||||
|
u32 reg = fld->reg;
|
||||||
|
u16 width = fld->width;
|
||||||
|
s16 shift = fld->shift;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Silently return errors for shift < 0 so caller doesn't have
|
||||||
|
* to check for fields which are optional. For fields that
|
||||||
|
* are required then caller needs to do something special
|
||||||
|
* anyway.
|
||||||
|
*/
|
||||||
|
if (shift < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (sdhci_arasan->soc_ctl_map->hiword_update)
|
||||||
|
ret = regmap_write(soc_ctl_base, reg,
|
||||||
|
HIWORD_UPDATE(val, GENMASK(width, 0),
|
||||||
|
shift));
|
||||||
|
else
|
||||||
|
ret = regmap_update_bits(soc_ctl_base, reg,
|
||||||
|
GENMASK(shift + width, shift),
|
||||||
|
val << shift);
|
||||||
|
|
||||||
|
/* Yell about (unexpected) regmap errors */
|
||||||
|
if (ret)
|
||||||
|
pr_warn("%s: Regmap write fail: %d\n",
|
||||||
|
mmc_hostname(host->mmc), ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host)
|
static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
u32 div;
|
u32 div;
|
||||||
@@ -79,6 +188,21 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sdhci_arasan_hs400_enhanced_strobe(struct mmc_host *mmc,
|
||||||
|
struct mmc_ios *ios)
|
||||||
|
{
|
||||||
|
u32 vendor;
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
|
vendor = readl(host->ioaddr + SDHCI_ARASAN_VENDOR_REGISTER);
|
||||||
|
if (ios->enhanced_strobe)
|
||||||
|
vendor |= VENDOR_ENHANCED_STROBE;
|
||||||
|
else
|
||||||
|
vendor &= ~VENDOR_ENHANCED_STROBE;
|
||||||
|
|
||||||
|
writel(vendor, host->ioaddr + SDHCI_ARASAN_VENDOR_REGISTER);
|
||||||
|
}
|
||||||
|
|
||||||
static struct sdhci_ops sdhci_arasan_ops = {
|
static struct sdhci_ops sdhci_arasan_ops = {
|
||||||
.set_clock = sdhci_arasan_set_clock,
|
.set_clock = sdhci_arasan_set_clock,
|
||||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||||
@@ -172,9 +296,168 @@ static int sdhci_arasan_resume(struct device *dev)
|
|||||||
static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
|
static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
|
||||||
sdhci_arasan_resume);
|
sdhci_arasan_resume);
|
||||||
|
|
||||||
|
static const struct of_device_id sdhci_arasan_of_match[] = {
|
||||||
|
/* SoC-specific compatible strings w/ soc_ctl_map */
|
||||||
|
{
|
||||||
|
.compatible = "rockchip,rk3399-sdhci-5.1",
|
||||||
|
.data = &rk3399_soc_ctl_map,
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Generic compatible below here */
|
||||||
|
{ .compatible = "arasan,sdhci-8.9a" },
|
||||||
|
{ .compatible = "arasan,sdhci-5.1" },
|
||||||
|
{ .compatible = "arasan,sdhci-4.9a" },
|
||||||
|
|
||||||
|
{ /* sentinel */ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate
|
||||||
|
*
|
||||||
|
* Return the current actual rate of the SD card clock. This can be used
|
||||||
|
* to communicate with out PHY.
|
||||||
|
*
|
||||||
|
* @hw: Pointer to the hardware clock structure.
|
||||||
|
* @parent_rate The parent rate (should be rate of clk_xin).
|
||||||
|
* Returns the card clock rate.
|
||||||
|
*/
|
||||||
|
static unsigned long sdhci_arasan_sdcardclk_recalc_rate(struct clk_hw *hw,
|
||||||
|
unsigned long parent_rate)
|
||||||
|
|
||||||
|
{
|
||||||
|
struct sdhci_arasan_data *sdhci_arasan =
|
||||||
|
container_of(hw, struct sdhci_arasan_data, sdcardclk_hw);
|
||||||
|
struct sdhci_host *host = sdhci_arasan->host;
|
||||||
|
|
||||||
|
return host->mmc->actual_clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct clk_ops arasan_sdcardclk_ops = {
|
||||||
|
.recalc_rate = sdhci_arasan_sdcardclk_recalc_rate,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq
|
||||||
|
*
|
||||||
|
* The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin. This
|
||||||
|
* function can be used to make that happen.
|
||||||
|
*
|
||||||
|
* NOTES:
|
||||||
|
* - Many existing devices don't seem to do this and work fine. To keep
|
||||||
|
* compatibility for old hardware where the device tree doesn't provide a
|
||||||
|
* register map, this function is a noop if a soc_ctl_map hasn't been provided
|
||||||
|
* for this platform.
|
||||||
|
* - It's assumed that clk_xin is not dynamic and that we use the SDHCI divider
|
||||||
|
* to achieve lower clock rates. That means that this function is called once
|
||||||
|
* at probe time and never called again.
|
||||||
|
*
|
||||||
|
* @host: The sdhci_host
|
||||||
|
*/
|
||||||
|
static void sdhci_arasan_update_baseclkfreq(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
const struct sdhci_arasan_soc_ctl_map *soc_ctl_map =
|
||||||
|
sdhci_arasan->soc_ctl_map;
|
||||||
|
u32 mhz = DIV_ROUND_CLOSEST(clk_get_rate(pltfm_host->clk), 1000000);
|
||||||
|
|
||||||
|
/* Having a map is optional */
|
||||||
|
if (!soc_ctl_map)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If we have a map, we expect to have a syscon */
|
||||||
|
if (!sdhci_arasan->soc_ctl_base) {
|
||||||
|
pr_warn("%s: Have regmap, but no soc-ctl-syscon\n",
|
||||||
|
mmc_hostname(host->mmc));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdhci_arasan_syscon_write(host, &soc_ctl_map->baseclkfreq, mhz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sdhci_arasan_register_sdclk - Register the sdclk for a PHY to use
|
||||||
|
*
|
||||||
|
* Some PHY devices need to know what the actual card clock is. In order for
|
||||||
|
* them to find out, we'll provide a clock through the common clock framework
|
||||||
|
* for them to query.
|
||||||
|
*
|
||||||
|
* Note: without seriously re-architecting SDHCI's clock code and testing on
|
||||||
|
* all platforms, there's no way to create a totally beautiful clock here
|
||||||
|
* with all clock ops implemented. Instead, we'll just create a clock that can
|
||||||
|
* be queried and set the CLK_GET_RATE_NOCACHE attribute to tell common clock
|
||||||
|
* framework that we're doing things behind its back. This should be sufficient
|
||||||
|
* to create nice clean device tree bindings and later (if needed) we can try
|
||||||
|
* re-architecting SDHCI if we see some benefit to it.
|
||||||
|
*
|
||||||
|
* @sdhci_arasan: Our private data structure.
|
||||||
|
* @clk_xin: Pointer to the functional clock
|
||||||
|
* @dev: Pointer to our struct device.
|
||||||
|
* Returns 0 on success and error value on error
|
||||||
|
*/
|
||||||
|
static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan,
|
||||||
|
struct clk *clk_xin,
|
||||||
|
struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_node *np = dev->of_node;
|
||||||
|
struct clk_init_data sdcardclk_init;
|
||||||
|
const char *parent_clk_name;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Providing a clock to the PHY is optional; no error if missing */
|
||||||
|
if (!of_find_property(np, "#clock-cells", NULL))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = of_property_read_string_index(np, "clock-output-names", 0,
|
||||||
|
&sdcardclk_init.name);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent_clk_name = __clk_get_name(clk_xin);
|
||||||
|
sdcardclk_init.parent_names = &parent_clk_name;
|
||||||
|
sdcardclk_init.num_parents = 1;
|
||||||
|
sdcardclk_init.flags = CLK_GET_RATE_NOCACHE;
|
||||||
|
sdcardclk_init.ops = &arasan_sdcardclk_ops;
|
||||||
|
|
||||||
|
sdhci_arasan->sdcardclk_hw.init = &sdcardclk_init;
|
||||||
|
sdhci_arasan->sdcardclk =
|
||||||
|
devm_clk_register(dev, &sdhci_arasan->sdcardclk_hw);
|
||||||
|
sdhci_arasan->sdcardclk_hw.init = NULL;
|
||||||
|
|
||||||
|
ret = of_clk_add_provider(np, of_clk_src_simple_get,
|
||||||
|
sdhci_arasan->sdcardclk);
|
||||||
|
if (ret)
|
||||||
|
dev_err(dev, "Failed to add clock provider\n");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sdhci_arasan_unregister_sdclk - Undoes sdhci_arasan_register_sdclk()
|
||||||
|
*
|
||||||
|
* Should be called any time we're exiting and sdhci_arasan_register_sdclk()
|
||||||
|
* returned success.
|
||||||
|
*
|
||||||
|
* @dev: Pointer to our struct device.
|
||||||
|
*/
|
||||||
|
static void sdhci_arasan_unregister_sdclk(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_node *np = dev->of_node;
|
||||||
|
|
||||||
|
if (!of_find_property(np, "#clock-cells", NULL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
of_clk_del_provider(dev->of_node);
|
||||||
|
}
|
||||||
|
|
||||||
static int sdhci_arasan_probe(struct platform_device *pdev)
|
static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
const struct of_device_id *match;
|
||||||
|
struct device_node *node;
|
||||||
struct clk *clk_xin;
|
struct clk *clk_xin;
|
||||||
struct sdhci_host *host;
|
struct sdhci_host *host;
|
||||||
struct sdhci_pltfm_host *pltfm_host;
|
struct sdhci_pltfm_host *pltfm_host;
|
||||||
@@ -187,6 +470,24 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
pltfm_host = sdhci_priv(host);
|
pltfm_host = sdhci_priv(host);
|
||||||
sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
sdhci_arasan->host = host;
|
||||||
|
|
||||||
|
match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node);
|
||||||
|
sdhci_arasan->soc_ctl_map = match->data;
|
||||||
|
|
||||||
|
node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0);
|
||||||
|
if (node) {
|
||||||
|
sdhci_arasan->soc_ctl_base = syscon_node_to_regmap(node);
|
||||||
|
of_node_put(node);
|
||||||
|
|
||||||
|
if (IS_ERR(sdhci_arasan->soc_ctl_base)) {
|
||||||
|
ret = PTR_ERR(sdhci_arasan->soc_ctl_base);
|
||||||
|
if (ret != -EPROBE_DEFER)
|
||||||
|
dev_err(&pdev->dev, "Can't get syscon: %d\n",
|
||||||
|
ret);
|
||||||
|
goto err_pltfm_free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb");
|
sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb");
|
||||||
if (IS_ERR(sdhci_arasan->clk_ahb)) {
|
if (IS_ERR(sdhci_arasan->clk_ahb)) {
|
||||||
@@ -217,10 +518,16 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
|||||||
sdhci_get_of_property(pdev);
|
sdhci_get_of_property(pdev);
|
||||||
pltfm_host->clk = clk_xin;
|
pltfm_host->clk = clk_xin;
|
||||||
|
|
||||||
|
sdhci_arasan_update_baseclkfreq(host);
|
||||||
|
|
||||||
|
ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev);
|
||||||
|
if (ret)
|
||||||
|
goto clk_disable_all;
|
||||||
|
|
||||||
ret = mmc_of_parse(host->mmc);
|
ret = mmc_of_parse(host->mmc);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "parsing dt failed (%u)\n", ret);
|
dev_err(&pdev->dev, "parsing dt failed (%u)\n", ret);
|
||||||
goto clk_disable_all;
|
goto unreg_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
sdhci_arasan->phy = ERR_PTR(-ENODEV);
|
sdhci_arasan->phy = ERR_PTR(-ENODEV);
|
||||||
@@ -231,13 +538,13 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
|||||||
if (IS_ERR(sdhci_arasan->phy)) {
|
if (IS_ERR(sdhci_arasan->phy)) {
|
||||||
ret = PTR_ERR(sdhci_arasan->phy);
|
ret = PTR_ERR(sdhci_arasan->phy);
|
||||||
dev_err(&pdev->dev, "No phy for arasan,sdhci-5.1.\n");
|
dev_err(&pdev->dev, "No phy for arasan,sdhci-5.1.\n");
|
||||||
goto clk_disable_all;
|
goto unreg_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = phy_init(sdhci_arasan->phy);
|
ret = phy_init(sdhci_arasan->phy);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "phy_init err.\n");
|
dev_err(&pdev->dev, "phy_init err.\n");
|
||||||
goto clk_disable_all;
|
goto unreg_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = phy_power_on(sdhci_arasan->phy);
|
ret = phy_power_on(sdhci_arasan->phy);
|
||||||
@@ -245,6 +552,9 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
|||||||
dev_err(&pdev->dev, "phy_power_on err.\n");
|
dev_err(&pdev->dev, "phy_power_on err.\n");
|
||||||
goto err_phy_power;
|
goto err_phy_power;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
host->mmc_host_ops.hs400_enhanced_strobe =
|
||||||
|
sdhci_arasan_hs400_enhanced_strobe;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = sdhci_add_host(host);
|
ret = sdhci_add_host(host);
|
||||||
@@ -259,6 +569,8 @@ err_add_host:
|
|||||||
err_phy_power:
|
err_phy_power:
|
||||||
if (!IS_ERR(sdhci_arasan->phy))
|
if (!IS_ERR(sdhci_arasan->phy))
|
||||||
phy_exit(sdhci_arasan->phy);
|
phy_exit(sdhci_arasan->phy);
|
||||||
|
unreg_clk:
|
||||||
|
sdhci_arasan_unregister_sdclk(&pdev->dev);
|
||||||
clk_disable_all:
|
clk_disable_all:
|
||||||
clk_disable_unprepare(clk_xin);
|
clk_disable_unprepare(clk_xin);
|
||||||
clk_dis_ahb:
|
clk_dis_ahb:
|
||||||
@@ -281,6 +593,8 @@ static int sdhci_arasan_remove(struct platform_device *pdev)
|
|||||||
phy_exit(sdhci_arasan->phy);
|
phy_exit(sdhci_arasan->phy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sdhci_arasan_unregister_sdclk(&pdev->dev);
|
||||||
|
|
||||||
ret = sdhci_pltfm_unregister(pdev);
|
ret = sdhci_pltfm_unregister(pdev);
|
||||||
|
|
||||||
clk_disable_unprepare(clk_ahb);
|
clk_disable_unprepare(clk_ahb);
|
||||||
@@ -288,14 +602,6 @@ static int sdhci_arasan_remove(struct platform_device *pdev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id sdhci_arasan_of_match[] = {
|
|
||||||
{ .compatible = "arasan,sdhci-8.9a" },
|
|
||||||
{ .compatible = "arasan,sdhci-5.1" },
|
|
||||||
{ .compatible = "arasan,sdhci-4.9a" },
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
|
|
||||||
|
|
||||||
static struct platform_driver sdhci_arasan_driver = {
|
static struct platform_driver sdhci_arasan_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-arasan",
|
.name = "sdhci-arasan",
|
||||||
|
@@ -288,7 +288,7 @@ static int sdhci_at91_probe(struct platform_device *pdev)
|
|||||||
* Disable SDHCI_QUIRK_BROKEN_CARD_DETECTION to be sure nobody tries
|
* Disable SDHCI_QUIRK_BROKEN_CARD_DETECTION to be sure nobody tries
|
||||||
* to enable polling via device tree with broken-cd property.
|
* to enable polling via device tree with broken-cd property.
|
||||||
*/
|
*/
|
||||||
if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) &&
|
if (mmc_card_is_removable(host->mmc) &&
|
||||||
mmc_gpio_get_cd(host->mmc) < 0) {
|
mmc_gpio_get_cd(host->mmc) < 0) {
|
||||||
host->mmc->caps |= MMC_CAP_NEEDS_POLL;
|
host->mmc->caps |= MMC_CAP_NEEDS_POLL;
|
||||||
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||||
|
@@ -481,7 +481,7 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask)
|
|||||||
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM_SLEEP
|
||||||
static u32 esdhc_proctl;
|
static u32 esdhc_proctl;
|
||||||
static int esdhc_of_suspend(struct device *dev)
|
static int esdhc_of_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
@@ -504,16 +504,12 @@ static int esdhc_of_resume(struct device *dev)
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct dev_pm_ops esdhc_pmops = {
|
|
||||||
.suspend = esdhc_of_suspend,
|
|
||||||
.resume = esdhc_of_resume,
|
|
||||||
};
|
|
||||||
#define ESDHC_PMOPS (&esdhc_pmops)
|
|
||||||
#else
|
|
||||||
#define ESDHC_PMOPS NULL
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_ops,
|
||||||
|
esdhc_of_suspend,
|
||||||
|
esdhc_of_resume);
|
||||||
|
|
||||||
static const struct sdhci_ops sdhci_esdhc_be_ops = {
|
static const struct sdhci_ops sdhci_esdhc_be_ops = {
|
||||||
.read_l = esdhc_be_readl,
|
.read_l = esdhc_be_readl,
|
||||||
.read_w = esdhc_be_readw,
|
.read_w = esdhc_be_readw,
|
||||||
@@ -657,7 +653,7 @@ static struct platform_driver sdhci_esdhc_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-esdhc",
|
.name = "sdhci-esdhc",
|
||||||
.of_match_table = sdhci_esdhc_of_match,
|
.of_match_table = sdhci_esdhc_of_match,
|
||||||
.pm = ESDHC_PMOPS,
|
.pm = &esdhc_of_dev_pm_ops,
|
||||||
},
|
},
|
||||||
.probe = sdhci_esdhc_probe,
|
.probe = sdhci_esdhc_probe,
|
||||||
.remove = sdhci_pltfm_unregister,
|
.remove = sdhci_pltfm_unregister,
|
||||||
|
@@ -85,7 +85,7 @@ static struct platform_driver sdhci_hlwd_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-hlwd",
|
.name = "sdhci-hlwd",
|
||||||
.of_match_table = sdhci_hlwd_of_match,
|
.of_match_table = sdhci_hlwd_of_match,
|
||||||
.pm = SDHCI_PLTFM_PMOPS,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
},
|
},
|
||||||
.probe = sdhci_hlwd_probe,
|
.probe = sdhci_hlwd_probe,
|
||||||
.remove = sdhci_pltfm_unregister,
|
.remove = sdhci_pltfm_unregister,
|
||||||
|
@@ -419,13 +419,13 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Define Host controllers for Intel Merrifield platform */
|
/* Define Host controllers for Intel Merrifield platform */
|
||||||
#define INTEL_MRFL_EMMC_0 0
|
#define INTEL_MRFLD_EMMC_0 0
|
||||||
#define INTEL_MRFL_EMMC_1 1
|
#define INTEL_MRFLD_EMMC_1 1
|
||||||
|
|
||||||
static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot)
|
static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
{
|
{
|
||||||
if ((PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_0) &&
|
if ((PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFLD_EMMC_0) &&
|
||||||
(PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_1))
|
(PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFLD_EMMC_1))
|
||||||
/* SD support is not ready yet */
|
/* SD support is not ready yet */
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
@@ -435,12 +435,12 @@ static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = {
|
static const struct sdhci_pci_fixes sdhci_intel_mrfld_mmc = {
|
||||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||||
.quirks2 = SDHCI_QUIRK2_BROKEN_HS200 |
|
.quirks2 = SDHCI_QUIRK2_BROKEN_HS200 |
|
||||||
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||||
.allow_runtime_pm = true,
|
.allow_runtime_pm = true,
|
||||||
.probe_slot = intel_mrfl_mmc_probe_slot,
|
.probe_slot = intel_mrfld_mmc_probe_slot,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* O2Micro extra registers */
|
/* O2Micro extra registers */
|
||||||
@@ -1104,10 +1104,10 @@ static const struct pci_device_id pci_ids[] = {
|
|||||||
|
|
||||||
{
|
{
|
||||||
.vendor = PCI_VENDOR_ID_INTEL,
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
.device = PCI_DEVICE_ID_INTEL_MRFL_MMC,
|
.device = PCI_DEVICE_ID_INTEL_MRFLD_MMC,
|
||||||
.subvendor = PCI_ANY_ID,
|
.subvendor = PCI_ANY_ID,
|
||||||
.subdevice = PCI_ANY_ID,
|
.subdevice = PCI_ANY_ID,
|
||||||
.driver_data = (kernel_ulong_t)&sdhci_intel_mrfl_mmc,
|
.driver_data = (kernel_ulong_t)&sdhci_intel_mrfld_mmc,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -1413,8 +1413,7 @@ static const struct sdhci_ops sdhci_pci_ops = {
|
|||||||
* *
|
* *
|
||||||
\*****************************************************************************/
|
\*****************************************************************************/
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
|
||||||
static int sdhci_pci_suspend(struct device *dev)
|
static int sdhci_pci_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pci_dev *pdev = to_pci_dev(dev);
|
struct pci_dev *pdev = to_pci_dev(dev);
|
||||||
@@ -1496,7 +1495,9 @@ static int sdhci_pci_resume(struct device *dev)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
static int sdhci_pci_runtime_suspend(struct device *dev)
|
static int sdhci_pci_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pci_dev *pdev = to_pci_dev(dev);
|
struct pci_dev *pdev = to_pci_dev(dev);
|
||||||
@@ -1562,17 +1563,10 @@ static int sdhci_pci_runtime_resume(struct device *dev)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#else /* CONFIG_PM */
|
|
||||||
|
|
||||||
#define sdhci_pci_suspend NULL
|
|
||||||
#define sdhci_pci_resume NULL
|
|
||||||
|
|
||||||
#endif /* CONFIG_PM */
|
|
||||||
|
|
||||||
static const struct dev_pm_ops sdhci_pci_pm_ops = {
|
static const struct dev_pm_ops sdhci_pci_pm_ops = {
|
||||||
.suspend = sdhci_pci_suspend,
|
SET_SYSTEM_SLEEP_PM_OPS(sdhci_pci_suspend, sdhci_pci_resume)
|
||||||
.resume = sdhci_pci_resume,
|
|
||||||
SET_RUNTIME_PM_OPS(sdhci_pci_runtime_suspend,
|
SET_RUNTIME_PM_OPS(sdhci_pci_runtime_suspend,
|
||||||
sdhci_pci_runtime_resume, NULL)
|
sdhci_pci_runtime_resume, NULL)
|
||||||
};
|
};
|
||||||
@@ -1760,11 +1754,12 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
|
|||||||
|
|
||||||
static void sdhci_pci_runtime_pm_allow(struct device *dev)
|
static void sdhci_pci_runtime_pm_allow(struct device *dev)
|
||||||
{
|
{
|
||||||
pm_runtime_put_noidle(dev);
|
pm_suspend_ignore_children(dev, 1);
|
||||||
pm_runtime_allow(dev);
|
|
||||||
pm_runtime_set_autosuspend_delay(dev, 50);
|
pm_runtime_set_autosuspend_delay(dev, 50);
|
||||||
pm_runtime_use_autosuspend(dev);
|
pm_runtime_use_autosuspend(dev);
|
||||||
pm_suspend_ignore_children(dev, 1);
|
pm_runtime_allow(dev);
|
||||||
|
/* Stay active until mmc core scans for a card */
|
||||||
|
pm_runtime_put_noidle(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdhci_pci_runtime_pm_forbid(struct device *dev)
|
static void sdhci_pci_runtime_pm_forbid(struct device *dev)
|
||||||
@@ -1810,15 +1805,13 @@ static int sdhci_pci_probe(struct pci_dev *pdev,
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = pci_enable_device(pdev);
|
ret = pcim_enable_device(pdev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
chip = kzalloc(sizeof(struct sdhci_pci_chip), GFP_KERNEL);
|
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
||||||
if (!chip) {
|
if (!chip)
|
||||||
ret = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
chip->pdev = pdev;
|
chip->pdev = pdev;
|
||||||
chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data;
|
chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data;
|
||||||
@@ -1834,7 +1827,7 @@ static int sdhci_pci_probe(struct pci_dev *pdev,
|
|||||||
if (chip->fixes && chip->fixes->probe) {
|
if (chip->fixes && chip->fixes->probe) {
|
||||||
ret = chip->fixes->probe(chip);
|
ret = chip->fixes->probe(chip);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto free;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
slots = chip->num_slots; /* Quirk may have changed this */
|
slots = chip->num_slots; /* Quirk may have changed this */
|
||||||
@@ -1844,8 +1837,7 @@ static int sdhci_pci_probe(struct pci_dev *pdev,
|
|||||||
if (IS_ERR(slot)) {
|
if (IS_ERR(slot)) {
|
||||||
for (i--; i >= 0; i--)
|
for (i--; i >= 0; i--)
|
||||||
sdhci_pci_remove_slot(chip->slots[i]);
|
sdhci_pci_remove_slot(chip->slots[i]);
|
||||||
ret = PTR_ERR(slot);
|
return PTR_ERR(slot);
|
||||||
goto free;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chip->slots[i] = slot;
|
chip->slots[i] = slot;
|
||||||
@@ -1855,35 +1847,18 @@ static int sdhci_pci_probe(struct pci_dev *pdev,
|
|||||||
sdhci_pci_runtime_pm_allow(&pdev->dev);
|
sdhci_pci_runtime_pm_allow(&pdev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
free:
|
|
||||||
pci_set_drvdata(pdev, NULL);
|
|
||||||
kfree(chip);
|
|
||||||
|
|
||||||
err:
|
|
||||||
pci_disable_device(pdev);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdhci_pci_remove(struct pci_dev *pdev)
|
static void sdhci_pci_remove(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
struct sdhci_pci_chip *chip;
|
struct sdhci_pci_chip *chip = pci_get_drvdata(pdev);
|
||||||
|
|
||||||
chip = pci_get_drvdata(pdev);
|
|
||||||
|
|
||||||
if (chip) {
|
|
||||||
if (chip->allow_runtime_pm)
|
if (chip->allow_runtime_pm)
|
||||||
sdhci_pci_runtime_pm_forbid(&pdev->dev);
|
sdhci_pci_runtime_pm_forbid(&pdev->dev);
|
||||||
|
|
||||||
for (i = 0; i < chip->num_slots; i++)
|
for (i = 0; i < chip->num_slots; i++)
|
||||||
sdhci_pci_remove_slot(chip->slots[i]);
|
sdhci_pci_remove_slot(chip->slots[i]);
|
||||||
|
|
||||||
pci_set_drvdata(pdev, NULL);
|
|
||||||
kfree(chip);
|
|
||||||
}
|
|
||||||
|
|
||||||
pci_disable_device(pdev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct pci_driver sdhci_driver = {
|
static struct pci_driver sdhci_driver = {
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
#define PCI_DEVICE_ID_INTEL_BSW_EMMC 0x2294
|
#define PCI_DEVICE_ID_INTEL_BSW_EMMC 0x2294
|
||||||
#define PCI_DEVICE_ID_INTEL_BSW_SDIO 0x2295
|
#define PCI_DEVICE_ID_INTEL_BSW_SDIO 0x2295
|
||||||
#define PCI_DEVICE_ID_INTEL_BSW_SD 0x2296
|
#define PCI_DEVICE_ID_INTEL_BSW_SD 0x2296
|
||||||
#define PCI_DEVICE_ID_INTEL_MRFL_MMC 0x1190
|
#define PCI_DEVICE_ID_INTEL_MRFLD_MMC 0x1190
|
||||||
#define PCI_DEVICE_ID_INTEL_CLV_SDIO0 0x08f9
|
#define PCI_DEVICE_ID_INTEL_CLV_SDIO0 0x08f9
|
||||||
#define PCI_DEVICE_ID_INTEL_CLV_SDIO1 0x08fa
|
#define PCI_DEVICE_ID_INTEL_CLV_SDIO1 0x08fa
|
||||||
#define PCI_DEVICE_ID_INTEL_CLV_SDIO2 0x08fb
|
#define PCI_DEVICE_ID_INTEL_CLV_SDIO2 0x08fb
|
||||||
|
@@ -215,29 +215,26 @@ int sdhci_pltfm_unregister(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sdhci_pltfm_unregister);
|
EXPORT_SYMBOL_GPL(sdhci_pltfm_unregister);
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM_SLEEP
|
||||||
int sdhci_pltfm_suspend(struct device *dev)
|
static int sdhci_pltfm_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||||
|
|
||||||
return sdhci_suspend_host(host);
|
return sdhci_suspend_host(host);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sdhci_pltfm_suspend);
|
|
||||||
|
|
||||||
int sdhci_pltfm_resume(struct device *dev)
|
static int sdhci_pltfm_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||||
|
|
||||||
return sdhci_resume_host(host);
|
return sdhci_resume_host(host);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sdhci_pltfm_resume);
|
#endif
|
||||||
|
|
||||||
const struct dev_pm_ops sdhci_pltfm_pmops = {
|
const struct dev_pm_ops sdhci_pltfm_pmops = {
|
||||||
.suspend = sdhci_pltfm_suspend,
|
SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_pltfm_resume)
|
||||||
.resume = sdhci_pltfm_resume,
|
|
||||||
};
|
};
|
||||||
EXPORT_SYMBOL_GPL(sdhci_pltfm_pmops);
|
EXPORT_SYMBOL_GPL(sdhci_pltfm_pmops);
|
||||||
#endif /* CONFIG_PM */
|
|
||||||
|
|
||||||
static int __init sdhci_pltfm_drv_init(void)
|
static int __init sdhci_pltfm_drv_init(void)
|
||||||
{
|
{
|
||||||
|
@@ -109,13 +109,6 @@ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
|
|||||||
return (void *)host->private;
|
return (void *)host->private;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
|
||||||
extern int sdhci_pltfm_suspend(struct device *dev);
|
|
||||||
extern int sdhci_pltfm_resume(struct device *dev);
|
|
||||||
extern const struct dev_pm_ops sdhci_pltfm_pmops;
|
extern const struct dev_pm_ops sdhci_pltfm_pmops;
|
||||||
#define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)
|
|
||||||
#else
|
|
||||||
#define SDHCI_PLTFM_PMOPS NULL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */
|
#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */
|
||||||
|
@@ -252,7 +252,7 @@ static struct platform_driver sdhci_pxav2_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-pxav2",
|
.name = "sdhci-pxav2",
|
||||||
.of_match_table = of_match_ptr(sdhci_pxav2_of_match),
|
.of_match_table = of_match_ptr(sdhci_pxav2_of_match),
|
||||||
.pm = SDHCI_PLTFM_PMOPS,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
},
|
},
|
||||||
.probe = sdhci_pxav2_probe,
|
.probe = sdhci_pxav2_probe,
|
||||||
.remove = sdhci_pxav2_remove,
|
.remove = sdhci_pxav2_remove,
|
||||||
|
@@ -583,24 +583,17 @@ static int sdhci_pxav3_runtime_resume(struct device *dev)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
|
||||||
static const struct dev_pm_ops sdhci_pxav3_pmops = {
|
static const struct dev_pm_ops sdhci_pxav3_pmops = {
|
||||||
SET_SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume)
|
SET_SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume)
|
||||||
SET_RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend,
|
SET_RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend,
|
||||||
sdhci_pxav3_runtime_resume, NULL)
|
sdhci_pxav3_runtime_resume, NULL)
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SDHCI_PXAV3_PMOPS (&sdhci_pxav3_pmops)
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define SDHCI_PXAV3_PMOPS NULL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct platform_driver sdhci_pxav3_driver = {
|
static struct platform_driver sdhci_pxav3_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-pxav3",
|
.name = "sdhci-pxav3",
|
||||||
.of_match_table = of_match_ptr(sdhci_pxav3_of_match),
|
.of_match_table = of_match_ptr(sdhci_pxav3_of_match),
|
||||||
.pm = SDHCI_PXAV3_PMOPS,
|
.pm = &sdhci_pxav3_pmops,
|
||||||
},
|
},
|
||||||
.probe = sdhci_pxav3_probe,
|
.probe = sdhci_pxav3_probe,
|
||||||
.remove = sdhci_pxav3_remove,
|
.remove = sdhci_pxav3_remove,
|
||||||
|
@@ -714,19 +714,12 @@ static int sdhci_s3c_runtime_resume(struct device *dev)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
|
||||||
static const struct dev_pm_ops sdhci_s3c_pmops = {
|
static const struct dev_pm_ops sdhci_s3c_pmops = {
|
||||||
SET_SYSTEM_SLEEP_PM_OPS(sdhci_s3c_suspend, sdhci_s3c_resume)
|
SET_SYSTEM_SLEEP_PM_OPS(sdhci_s3c_suspend, sdhci_s3c_resume)
|
||||||
SET_RUNTIME_PM_OPS(sdhci_s3c_runtime_suspend, sdhci_s3c_runtime_resume,
|
SET_RUNTIME_PM_OPS(sdhci_s3c_runtime_suspend, sdhci_s3c_runtime_resume,
|
||||||
NULL)
|
NULL)
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SDHCI_S3C_PMOPS (&sdhci_s3c_pmops)
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define SDHCI_S3C_PMOPS NULL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212)
|
#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212)
|
||||||
static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = {
|
static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = {
|
||||||
.no_divider = true,
|
.no_divider = true,
|
||||||
@@ -765,7 +758,7 @@ static struct platform_driver sdhci_s3c_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = "s3c-sdhci",
|
.name = "s3c-sdhci",
|
||||||
.of_match_table = of_match_ptr(sdhci_s3c_dt_match),
|
.of_match_table = of_match_ptr(sdhci_s3c_dt_match),
|
||||||
.pm = SDHCI_S3C_PMOPS,
|
.pm = &sdhci_s3c_pmops,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -260,9 +260,9 @@ static int sdhci_sirf_resume(struct device *dev)
|
|||||||
|
|
||||||
return sdhci_resume_host(host);
|
return sdhci_resume_host(host);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static SIMPLE_DEV_PM_OPS(sdhci_sirf_pm_ops, sdhci_sirf_suspend, sdhci_sirf_resume);
|
static SIMPLE_DEV_PM_OPS(sdhci_sirf_pm_ops, sdhci_sirf_suspend, sdhci_sirf_resume);
|
||||||
#endif
|
|
||||||
|
|
||||||
static const struct of_device_id sdhci_sirf_of_match[] = {
|
static const struct of_device_id sdhci_sirf_of_match[] = {
|
||||||
{ .compatible = "sirf,prima2-sdhc" },
|
{ .compatible = "sirf,prima2-sdhc" },
|
||||||
@@ -274,9 +274,7 @@ static struct platform_driver sdhci_sirf_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-sirf",
|
.name = "sdhci-sirf",
|
||||||
.of_match_table = sdhci_sirf_of_match,
|
.of_match_table = sdhci_sirf_of_match,
|
||||||
#ifdef CONFIG_PM_SLEEP
|
|
||||||
.pm = &sdhci_sirf_pm_ops,
|
.pm = &sdhci_sirf_pm_ops,
|
||||||
#endif
|
|
||||||
},
|
},
|
||||||
.probe = sdhci_sirf_probe,
|
.probe = sdhci_sirf_probe,
|
||||||
.remove = sdhci_pltfm_unregister,
|
.remove = sdhci_pltfm_unregister,
|
||||||
|
@@ -183,7 +183,7 @@ static void st_mmcss_cconfig(struct device_node *np, struct sdhci_host *host)
|
|||||||
|
|
||||||
writel_relaxed(cconf2, host->ioaddr + ST_MMC_CCONFIG_REG_2);
|
writel_relaxed(cconf2, host->ioaddr + ST_MMC_CCONFIG_REG_2);
|
||||||
|
|
||||||
if (mhost->caps & MMC_CAP_NONREMOVABLE)
|
if (!mmc_card_is_removable(mhost))
|
||||||
cconf3 |= ST_MMC_CCONFIG_EMMC_SLOT_TYPE;
|
cconf3 |= ST_MMC_CCONFIG_EMMC_SLOT_TYPE;
|
||||||
else
|
else
|
||||||
/* CARD _D ET_CTRL */
|
/* CARD _D ET_CTRL */
|
||||||
|
@@ -148,28 +148,37 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
|
misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
|
||||||
|
clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
|
||||||
|
|
||||||
|
misc_ctrl &= ~(SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 |
|
||||||
|
SDHCI_MISC_CTRL_ENABLE_SDR50 |
|
||||||
|
SDHCI_MISC_CTRL_ENABLE_DDR50 |
|
||||||
|
SDHCI_MISC_CTRL_ENABLE_SDR104);
|
||||||
|
|
||||||
|
clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the board does not define a regulator for the SDHCI
|
||||||
|
* IO voltage, then don't advertise support for UHS modes
|
||||||
|
* even if the device supports it because the IO voltage
|
||||||
|
* cannot be configured.
|
||||||
|
*/
|
||||||
|
if (!IS_ERR(host->mmc->supply.vqmmc)) {
|
||||||
/* Erratum: Enable SDHCI spec v3.00 support */
|
/* Erratum: Enable SDHCI spec v3.00 support */
|
||||||
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
|
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
|
||||||
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
|
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
|
||||||
/* Advertise UHS modes as supported by host */
|
/* Advertise UHS modes as supported by host */
|
||||||
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
|
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
|
||||||
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50;
|
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50;
|
||||||
else
|
|
||||||
misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR50;
|
|
||||||
if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
|
if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
|
||||||
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
|
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
|
||||||
else
|
|
||||||
misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_DDR50;
|
|
||||||
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104)
|
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104)
|
||||||
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104;
|
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104;
|
||||||
else
|
|
||||||
misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104;
|
|
||||||
sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
|
|
||||||
|
|
||||||
clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
|
|
||||||
clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE;
|
|
||||||
if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50)
|
if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50)
|
||||||
clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE;
|
clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
|
||||||
sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
|
sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
|
||||||
|
|
||||||
if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
|
if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
|
||||||
@@ -474,7 +483,7 @@ static struct platform_driver sdhci_tegra_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-tegra",
|
.name = "sdhci-tegra",
|
||||||
.of_match_table = sdhci_tegra_dt_match,
|
.of_match_table = sdhci_tegra_dt_match,
|
||||||
.pm = SDHCI_PLTFM_PMOPS,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
},
|
},
|
||||||
.probe = sdhci_tegra_probe,
|
.probe = sdhci_tegra_probe,
|
||||||
.remove = sdhci_pltfm_unregister,
|
.remove = sdhci_pltfm_unregister,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -128,6 +128,7 @@
|
|||||||
#define SDHCI_INT_CARD_INSERT 0x00000040
|
#define SDHCI_INT_CARD_INSERT 0x00000040
|
||||||
#define SDHCI_INT_CARD_REMOVE 0x00000080
|
#define SDHCI_INT_CARD_REMOVE 0x00000080
|
||||||
#define SDHCI_INT_CARD_INT 0x00000100
|
#define SDHCI_INT_CARD_INT 0x00000100
|
||||||
|
#define SDHCI_INT_RETUNE 0x00001000
|
||||||
#define SDHCI_INT_ERROR 0x00008000
|
#define SDHCI_INT_ERROR 0x00008000
|
||||||
#define SDHCI_INT_TIMEOUT 0x00010000
|
#define SDHCI_INT_TIMEOUT 0x00010000
|
||||||
#define SDHCI_INT_CRC 0x00020000
|
#define SDHCI_INT_CRC 0x00020000
|
||||||
@@ -186,6 +187,7 @@
|
|||||||
#define SDHCI_CAN_DO_ADMA1 0x00100000
|
#define SDHCI_CAN_DO_ADMA1 0x00100000
|
||||||
#define SDHCI_CAN_DO_HISPD 0x00200000
|
#define SDHCI_CAN_DO_HISPD 0x00200000
|
||||||
#define SDHCI_CAN_DO_SDMA 0x00400000
|
#define SDHCI_CAN_DO_SDMA 0x00400000
|
||||||
|
#define SDHCI_CAN_DO_SUSPEND 0x00800000
|
||||||
#define SDHCI_CAN_VDD_330 0x01000000
|
#define SDHCI_CAN_VDD_330 0x01000000
|
||||||
#define SDHCI_CAN_VDD_300 0x02000000
|
#define SDHCI_CAN_VDD_300 0x02000000
|
||||||
#define SDHCI_CAN_VDD_180 0x04000000
|
#define SDHCI_CAN_VDD_180 0x04000000
|
||||||
@@ -314,6 +316,9 @@ struct sdhci_adma2_64_desc {
|
|||||||
*/
|
*/
|
||||||
#define SDHCI_MAX_SEGS 128
|
#define SDHCI_MAX_SEGS 128
|
||||||
|
|
||||||
|
/* Allow for a a command request and a data request at the same time */
|
||||||
|
#define SDHCI_MAX_MRQS 2
|
||||||
|
|
||||||
enum sdhci_cookie {
|
enum sdhci_cookie {
|
||||||
COOKIE_UNMAPPED,
|
COOKIE_UNMAPPED,
|
||||||
COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */
|
COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */
|
||||||
@@ -447,6 +452,9 @@ struct sdhci_host {
|
|||||||
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
|
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
|
||||||
#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
|
#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
|
||||||
#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */
|
#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */
|
||||||
|
#define SDHCI_SIGNALING_330 (1<<14) /* Host is capable of 3.3V signaling */
|
||||||
|
#define SDHCI_SIGNALING_180 (1<<15) /* Host is capable of 1.8V signaling */
|
||||||
|
#define SDHCI_SIGNALING_120 (1<<16) /* Host is capable of 1.2V signaling */
|
||||||
|
|
||||||
unsigned int version; /* SDHCI spec. version */
|
unsigned int version; /* SDHCI spec. version */
|
||||||
|
|
||||||
@@ -460,12 +468,13 @@ struct sdhci_host {
|
|||||||
bool runtime_suspended; /* Host is runtime suspended */
|
bool runtime_suspended; /* Host is runtime suspended */
|
||||||
bool bus_on; /* Bus power prevents runtime suspend */
|
bool bus_on; /* Bus power prevents runtime suspend */
|
||||||
bool preset_enabled; /* Preset is enabled */
|
bool preset_enabled; /* Preset is enabled */
|
||||||
|
bool pending_reset; /* Cmd/data reset is pending */
|
||||||
|
|
||||||
struct mmc_request *mrq; /* Current request */
|
struct mmc_request *mrqs_done[SDHCI_MAX_MRQS]; /* Requests done */
|
||||||
struct mmc_command *cmd; /* Current command */
|
struct mmc_command *cmd; /* Current command */
|
||||||
|
struct mmc_command *data_cmd; /* Current data command */
|
||||||
struct mmc_data *data; /* Current data request */
|
struct mmc_data *data; /* Current data request */
|
||||||
unsigned int data_early:1; /* Data finished before cmd */
|
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 */
|
struct sg_mapping_iter sg_miter; /* SG state for PIO */
|
||||||
unsigned int blocks; /* remaining PIO blocks */
|
unsigned int blocks; /* remaining PIO blocks */
|
||||||
@@ -486,9 +495,11 @@ struct sdhci_host {
|
|||||||
struct tasklet_struct finish_tasklet; /* Tasklet structures */
|
struct tasklet_struct finish_tasklet; /* Tasklet structures */
|
||||||
|
|
||||||
struct timer_list timer; /* Timer for timeouts */
|
struct timer_list timer; /* Timer for timeouts */
|
||||||
|
struct timer_list data_timer; /* Timer for data timeouts */
|
||||||
|
|
||||||
u32 caps; /* Alternative CAPABILITY_0 */
|
u32 caps; /* CAPABILITY_0 */
|
||||||
u32 caps1; /* Alternative CAPABILITY_1 */
|
u32 caps1; /* CAPABILITY_1 */
|
||||||
|
bool read_caps; /* Capability flags have been read */
|
||||||
|
|
||||||
unsigned int ocr_avail_sdio; /* OCR bit masks */
|
unsigned int ocr_avail_sdio; /* OCR bit masks */
|
||||||
unsigned int ocr_avail_sd;
|
unsigned int ocr_avail_sd;
|
||||||
@@ -508,6 +519,8 @@ struct sdhci_host {
|
|||||||
unsigned int tuning_count; /* Timer count for re-tuning */
|
unsigned int tuning_count; /* Timer count for re-tuning */
|
||||||
unsigned int tuning_mode; /* Re-tuning mode supported by host */
|
unsigned int tuning_mode; /* Re-tuning mode supported by host */
|
||||||
#define SDHCI_TUNING_MODE_1 0
|
#define SDHCI_TUNING_MODE_1 0
|
||||||
|
#define SDHCI_TUNING_MODE_2 1
|
||||||
|
#define SDHCI_TUNING_MODE_3 2
|
||||||
|
|
||||||
unsigned long private[0] ____cacheline_aligned;
|
unsigned long private[0] ____cacheline_aligned;
|
||||||
};
|
};
|
||||||
@@ -645,11 +658,20 @@ static inline void *sdhci_priv(struct sdhci_host *host)
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern void sdhci_card_detect(struct sdhci_host *host);
|
extern void sdhci_card_detect(struct sdhci_host *host);
|
||||||
|
extern void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps,
|
||||||
|
u32 *caps1);
|
||||||
|
extern int sdhci_setup_host(struct sdhci_host *host);
|
||||||
|
extern int __sdhci_add_host(struct sdhci_host *host);
|
||||||
extern int sdhci_add_host(struct sdhci_host *host);
|
extern int sdhci_add_host(struct sdhci_host *host);
|
||||||
extern void sdhci_remove_host(struct sdhci_host *host, int dead);
|
extern void sdhci_remove_host(struct sdhci_host *host, int dead);
|
||||||
extern void sdhci_send_command(struct sdhci_host *host,
|
extern void sdhci_send_command(struct sdhci_host *host,
|
||||||
struct mmc_command *cmd);
|
struct mmc_command *cmd);
|
||||||
|
|
||||||
|
static inline void sdhci_read_caps(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
__sdhci_read_caps(host, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host)
|
static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED);
|
return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED);
|
||||||
|
@@ -222,7 +222,7 @@ static struct platform_driver sdhci_f_sdh30_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = "f_sdh30",
|
.name = "f_sdh30",
|
||||||
.of_match_table = f_sdh30_dt_ids,
|
.of_match_table = f_sdh30_dt_ids,
|
||||||
.pm = SDHCI_PLTFM_PMOPS,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
},
|
},
|
||||||
.probe = sdhci_f_sdh30_probe,
|
.probe = sdhci_f_sdh30_probe,
|
||||||
.remove = sdhci_f_sdh30_remove,
|
.remove = sdhci_f_sdh30_remove,
|
||||||
|
@@ -574,7 +574,7 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
|
|||||||
if (state1 & STS1_CMDSEQ) {
|
if (state1 & STS1_CMDSEQ) {
|
||||||
sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, CMD_CTRL_BREAK);
|
sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, CMD_CTRL_BREAK);
|
||||||
sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, ~CMD_CTRL_BREAK);
|
sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, ~CMD_CTRL_BREAK);
|
||||||
for (timeout = 10000000; timeout; timeout--) {
|
for (timeout = 10000; timeout; timeout--) {
|
||||||
if (!(sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1)
|
if (!(sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1)
|
||||||
& STS1_CMDSEQ))
|
& STS1_CMDSEQ))
|
||||||
break;
|
break;
|
||||||
@@ -819,10 +819,12 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
|
|||||||
tmp |= CMD_SET_RTYP_NO;
|
tmp |= CMD_SET_RTYP_NO;
|
||||||
break;
|
break;
|
||||||
case MMC_RSP_R1:
|
case MMC_RSP_R1:
|
||||||
case MMC_RSP_R1B:
|
|
||||||
case MMC_RSP_R3:
|
case MMC_RSP_R3:
|
||||||
tmp |= CMD_SET_RTYP_6B;
|
tmp |= CMD_SET_RTYP_6B;
|
||||||
break;
|
break;
|
||||||
|
case MMC_RSP_R1B:
|
||||||
|
tmp |= CMD_SET_RBSY | CMD_SET_RTYP_6B;
|
||||||
|
break;
|
||||||
case MMC_RSP_R2:
|
case MMC_RSP_R2:
|
||||||
tmp |= CMD_SET_RTYP_17B;
|
tmp |= CMD_SET_RTYP_17B;
|
||||||
break;
|
break;
|
||||||
@@ -830,17 +832,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
|
|||||||
dev_err(dev, "Unsupported response type.\n");
|
dev_err(dev, "Unsupported response type.\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
switch (opc) {
|
|
||||||
/* RBSY */
|
|
||||||
case MMC_SLEEP_AWAKE:
|
|
||||||
case MMC_SWITCH:
|
|
||||||
case MMC_STOP_TRANSMISSION:
|
|
||||||
case MMC_SET_WRITE_PROT:
|
|
||||||
case MMC_CLR_WRITE_PROT:
|
|
||||||
case MMC_ERASE:
|
|
||||||
tmp |= CMD_SET_RBSY;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* WDAT / DATW */
|
/* WDAT / DATW */
|
||||||
if (data) {
|
if (data) {
|
||||||
tmp |= CMD_SET_WDAT;
|
tmp |= CMD_SET_WDAT;
|
||||||
@@ -925,23 +917,13 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
|
|||||||
{
|
{
|
||||||
struct mmc_command *cmd = mrq->cmd;
|
struct mmc_command *cmd = mrq->cmd;
|
||||||
u32 opc = cmd->opcode;
|
u32 opc = cmd->opcode;
|
||||||
u32 mask;
|
u32 mask = 0;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
switch (opc) {
|
if (cmd->flags & MMC_RSP_BUSY)
|
||||||
/* response busy check */
|
|
||||||
case MMC_SLEEP_AWAKE:
|
|
||||||
case MMC_SWITCH:
|
|
||||||
case MMC_STOP_TRANSMISSION:
|
|
||||||
case MMC_SET_WRITE_PROT:
|
|
||||||
case MMC_CLR_WRITE_PROT:
|
|
||||||
case MMC_ERASE:
|
|
||||||
mask = MASK_START_CMD | MASK_MRBSYE;
|
mask = MASK_START_CMD | MASK_MRBSYE;
|
||||||
break;
|
else
|
||||||
default:
|
|
||||||
mask = MASK_START_CMD | MASK_MCRSPE;
|
mask = MASK_START_CMD | MASK_MCRSPE;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (host->ccs_enable)
|
if (host->ccs_enable)
|
||||||
mask |= MASK_MCCSTO;
|
mask |= MASK_MCCSTO;
|
||||||
@@ -1009,22 +991,6 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||||||
host->state = STATE_REQUEST;
|
host->state = STATE_REQUEST;
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
switch (mrq->cmd->opcode) {
|
|
||||||
/* MMCIF does not support SD/SDIO command */
|
|
||||||
case MMC_SLEEP_AWAKE: /* = SD_IO_SEND_OP_COND (5) */
|
|
||||||
case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */
|
|
||||||
if ((mrq->cmd->flags & MMC_CMD_MASK) != MMC_CMD_BCR)
|
|
||||||
break;
|
|
||||||
case MMC_APP_CMD:
|
|
||||||
case SD_IO_RW_DIRECT:
|
|
||||||
host->state = STATE_IDLE;
|
|
||||||
mrq->cmd->error = -ETIMEDOUT;
|
|
||||||
mmc_request_done(mmc, mrq);
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
host->mrq = mrq;
|
host->mrq = mrq;
|
||||||
|
|
||||||
sh_mmcif_start_cmd(host, mrq);
|
sh_mmcif_start_cmd(host, mrq);
|
||||||
@@ -1488,6 +1454,9 @@ static int sh_mmcif_probe(struct platform_device *pdev)
|
|||||||
sh_mmcif_init_ocr(host);
|
sh_mmcif_init_ocr(host);
|
||||||
|
|
||||||
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_WAIT_WHILE_BUSY;
|
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_WAIT_WHILE_BUSY;
|
||||||
|
mmc->caps2 |= MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
|
||||||
|
mmc->max_busy_timeout = 10000;
|
||||||
|
|
||||||
if (pd && pd->caps)
|
if (pd && pd->caps)
|
||||||
mmc->caps |= pd->caps;
|
mmc->caps |= pd->caps;
|
||||||
mmc->max_segs = 32;
|
mmc->max_segs = 32;
|
||||||
|
@@ -39,6 +39,12 @@
|
|||||||
|
|
||||||
#define EXT_ACC 0xe4
|
#define EXT_ACC 0xe4
|
||||||
|
|
||||||
|
#define SDHI_VER_GEN2_SDR50 0x490c
|
||||||
|
/* very old datasheets said 0x490c for SDR104, too. They are wrong! */
|
||||||
|
#define SDHI_VER_GEN2_SDR104 0xcb0d
|
||||||
|
#define SDHI_VER_GEN3_SD 0xcc10
|
||||||
|
#define SDHI_VER_GEN3_SDMMC 0xcd10
|
||||||
|
|
||||||
#define host_to_priv(host) container_of((host)->pdata, struct sh_mobile_sdhi, mmc_data)
|
#define host_to_priv(host) container_of((host)->pdata, struct sh_mobile_sdhi, mmc_data)
|
||||||
|
|
||||||
struct sh_mobile_sdhi_of_data {
|
struct sh_mobile_sdhi_of_data {
|
||||||
@@ -109,14 +115,14 @@ static void sh_mobile_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
|
|||||||
* sh_mobile_sdhi_of_data :: dma_buswidth
|
* sh_mobile_sdhi_of_data :: dma_buswidth
|
||||||
*/
|
*/
|
||||||
switch (sd_ctrl_read16(host, CTL_VERSION)) {
|
switch (sd_ctrl_read16(host, CTL_VERSION)) {
|
||||||
case 0x490C:
|
case SDHI_VER_GEN2_SDR50:
|
||||||
val = (width == 32) ? 0x0001 : 0x0000;
|
val = (width == 32) ? 0x0001 : 0x0000;
|
||||||
break;
|
break;
|
||||||
case 0xCB0D:
|
case SDHI_VER_GEN2_SDR104:
|
||||||
val = (width == 32) ? 0x0000 : 0x0001;
|
val = (width == 32) ? 0x0000 : 0x0001;
|
||||||
break;
|
break;
|
||||||
case 0xCC10: /* Gen3, SD only */
|
case SDHI_VER_GEN3_SD:
|
||||||
case 0xCD10: /* Gen3, SD + MMC */
|
case SDHI_VER_GEN3_SDMMC:
|
||||||
if (width == 64)
|
if (width == 64)
|
||||||
val = 0x0000;
|
val = 0x0000;
|
||||||
else if (width == 32)
|
else if (width == 32)
|
||||||
|
@@ -259,7 +259,7 @@ static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr,
|
|||||||
|
|
||||||
static inline void sd_ctrl_write32_as_16_and_16(struct tmio_mmc_host *host, int addr, u32 val)
|
static inline void sd_ctrl_write32_as_16_and_16(struct tmio_mmc_host *host, int addr, u32 val)
|
||||||
{
|
{
|
||||||
writew(val, host->ctl + (addr << host->bus_shift));
|
writew(val & 0xffff, host->ctl + (addr << host->bus_shift));
|
||||||
writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift));
|
writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1086,7 +1086,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
|
|||||||
|
|
||||||
_host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||
|
_host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||
|
||||||
mmc->caps & MMC_CAP_NEEDS_POLL ||
|
mmc->caps & MMC_CAP_NEEDS_POLL ||
|
||||||
mmc->caps & MMC_CAP_NONREMOVABLE ||
|
!mmc_card_is_removable(mmc) ||
|
||||||
mmc->slot.cd_irq >= 0);
|
mmc->slot.cd_irq >= 0);
|
||||||
|
|
||||||
if (tmio_mmc_clk_enable(_host) < 0) {
|
if (tmio_mmc_clk_enable(_host) < 0) {
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/mfd/syscon.h>
|
#include <linux/mfd/syscon.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
@@ -56,17 +57,39 @@
|
|||||||
#define PHYCTRL_DLLRDY_SHIFT 0x5
|
#define PHYCTRL_DLLRDY_SHIFT 0x5
|
||||||
#define PHYCTRL_DLLRDY_DONE 0x1
|
#define PHYCTRL_DLLRDY_DONE 0x1
|
||||||
#define PHYCTRL_DLLRDY_GOING 0x0
|
#define PHYCTRL_DLLRDY_GOING 0x0
|
||||||
|
#define PHYCTRL_FREQSEL_200M 0x0
|
||||||
|
#define PHYCTRL_FREQSEL_50M 0x1
|
||||||
|
#define PHYCTRL_FREQSEL_100M 0x2
|
||||||
|
#define PHYCTRL_FREQSEL_150M 0x3
|
||||||
|
#define PHYCTRL_FREQSEL_MASK 0x3
|
||||||
|
#define PHYCTRL_FREQSEL_SHIFT 0xc
|
||||||
|
#define PHYCTRL_DR_MASK 0x7
|
||||||
|
#define PHYCTRL_DR_SHIFT 0x4
|
||||||
|
#define PHYCTRL_DR_50OHM 0x0
|
||||||
|
#define PHYCTRL_DR_33OHM 0x1
|
||||||
|
#define PHYCTRL_DR_66OHM 0x2
|
||||||
|
#define PHYCTRL_DR_100OHM 0x3
|
||||||
|
#define PHYCTRL_DR_40OHM 0x4
|
||||||
|
#define PHYCTRL_OTAPDLYENA 0x1
|
||||||
|
#define PHYCTRL_OTAPDLYENA_MASK 0x1
|
||||||
|
#define PHYCTRL_OTAPDLYENA_SHIFT 0xb
|
||||||
|
#define PHYCTRL_OTAPDLYSEL_MASK 0xf
|
||||||
|
#define PHYCTRL_OTAPDLYSEL_SHIFT 0x7
|
||||||
|
|
||||||
struct rockchip_emmc_phy {
|
struct rockchip_emmc_phy {
|
||||||
unsigned int reg_offset;
|
unsigned int reg_offset;
|
||||||
struct regmap *reg_base;
|
struct regmap *reg_base;
|
||||||
|
struct clk *emmcclk;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
|
static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
|
||||||
bool on_off)
|
|
||||||
{
|
{
|
||||||
|
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
|
||||||
unsigned int caldone;
|
unsigned int caldone;
|
||||||
unsigned int dllrdy;
|
unsigned int dllrdy;
|
||||||
|
unsigned int freqsel = PHYCTRL_FREQSEL_200M;
|
||||||
|
unsigned long rate;
|
||||||
|
unsigned long timeout;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Keep phyctrl_pdb and phyctrl_endll low to allow
|
* Keep phyctrl_pdb and phyctrl_endll low to allow
|
||||||
@@ -87,6 +110,43 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
|
|||||||
if (on_off == PHYCTRL_PDB_PWR_OFF)
|
if (on_off == PHYCTRL_PDB_PWR_OFF)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
rate = clk_get_rate(rk_phy->emmcclk);
|
||||||
|
|
||||||
|
if (rate != 0) {
|
||||||
|
unsigned long ideal_rate;
|
||||||
|
unsigned long diff;
|
||||||
|
|
||||||
|
switch (rate) {
|
||||||
|
case 1 ... 74999999:
|
||||||
|
ideal_rate = 50000000;
|
||||||
|
freqsel = PHYCTRL_FREQSEL_50M;
|
||||||
|
break;
|
||||||
|
case 75000000 ... 124999999:
|
||||||
|
ideal_rate = 100000000;
|
||||||
|
freqsel = PHYCTRL_FREQSEL_100M;
|
||||||
|
break;
|
||||||
|
case 125000000 ... 174999999:
|
||||||
|
ideal_rate = 150000000;
|
||||||
|
freqsel = PHYCTRL_FREQSEL_150M;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ideal_rate = 200000000;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
diff = (rate > ideal_rate) ?
|
||||||
|
rate - ideal_rate : ideal_rate - rate;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In order for tuning delays to be accurate we need to be
|
||||||
|
* pretty spot on for the DLL range, so warn if we're too
|
||||||
|
* far off. Also warn if we're above the 200 MHz max. Don't
|
||||||
|
* warn for really slow rates since we won't be tuning then.
|
||||||
|
*/
|
||||||
|
if ((rate > 50000000 && diff > 15000000) || (rate > 200000000))
|
||||||
|
dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* According to the user manual, calpad calibration
|
* According to the user manual, calpad calibration
|
||||||
* cycle takes more than 2us without the minimal recommended
|
* cycle takes more than 2us without the minimal recommended
|
||||||
@@ -113,20 +173,62 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
|
|||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set the frequency of the DLL operation */
|
||||||
|
regmap_write(rk_phy->reg_base,
|
||||||
|
rk_phy->reg_offset + GRF_EMMCPHY_CON0,
|
||||||
|
HIWORD_UPDATE(freqsel, PHYCTRL_FREQSEL_MASK,
|
||||||
|
PHYCTRL_FREQSEL_SHIFT));
|
||||||
|
|
||||||
|
/* Turn on the DLL */
|
||||||
regmap_write(rk_phy->reg_base,
|
regmap_write(rk_phy->reg_base,
|
||||||
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
|
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
|
||||||
HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,
|
HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,
|
||||||
PHYCTRL_ENDLL_MASK,
|
PHYCTRL_ENDLL_MASK,
|
||||||
PHYCTRL_ENDLL_SHIFT));
|
PHYCTRL_ENDLL_SHIFT));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* After enable analog DLL circuits, we need extra 10.2us
|
* We turned on the DLL even though the rate was 0 because we the
|
||||||
* for dll to be ready for work.
|
* clock might be turned on later. ...but we can't wait for the DLL
|
||||||
|
* to lock when the rate is 0 because it will never lock with no
|
||||||
|
* input clock.
|
||||||
|
*
|
||||||
|
* Technically we should be checking the lock later when the clock
|
||||||
|
* is turned on, but for now we won't.
|
||||||
*/
|
*/
|
||||||
udelay(11);
|
if (rate == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* After enabling analog DLL circuits docs say that we need 10.2 us if
|
||||||
|
* our source clock is at 50 MHz and that lock time scales linearly
|
||||||
|
* with clock speed. If we are powering on the PHY and the card clock
|
||||||
|
* is super slow (like 100 kHZ) this could take as long as 5.1 ms as
|
||||||
|
* per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms
|
||||||
|
* Hopefully we won't be running at 100 kHz, but we should still make
|
||||||
|
* sure we wait long enough.
|
||||||
|
*
|
||||||
|
* NOTE: There appear to be corner cases where the DLL seems to take
|
||||||
|
* extra long to lock for reasons that aren't understood. In some
|
||||||
|
* extreme cases we've seen it take up to over 10ms (!). We'll be
|
||||||
|
* generous and give it 50ms. We still busy wait here because:
|
||||||
|
* - In most cases it should be super fast.
|
||||||
|
* - This is not called lots during normal operation so it shouldn't
|
||||||
|
* be a power or performance problem to busy wait. We expect it
|
||||||
|
* only at boot / resume. In both cases, eMMC is probably on the
|
||||||
|
* critical path so busy waiting a little extra time should be OK.
|
||||||
|
*/
|
||||||
|
timeout = jiffies + msecs_to_jiffies(50);
|
||||||
|
do {
|
||||||
|
udelay(1);
|
||||||
|
|
||||||
regmap_read(rk_phy->reg_base,
|
regmap_read(rk_phy->reg_base,
|
||||||
rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
|
rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
|
||||||
&dllrdy);
|
&dllrdy);
|
||||||
dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
|
dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
|
||||||
|
if (dllrdy == PHYCTRL_DLLRDY_DONE)
|
||||||
|
break;
|
||||||
|
} while (!time_after(jiffies, timeout));
|
||||||
|
|
||||||
if (dllrdy != PHYCTRL_DLLRDY_DONE) {
|
if (dllrdy != PHYCTRL_DLLRDY_DONE) {
|
||||||
pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n");
|
pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n");
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
@@ -135,33 +237,82 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rockchip_emmc_phy_power_off(struct phy *phy)
|
static int rockchip_emmc_phy_init(struct phy *phy)
|
||||||
{
|
{
|
||||||
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
|
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* Power down emmc phy analog blocks */
|
/*
|
||||||
ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF);
|
* We purposely get the clock here and not in probe to avoid the
|
||||||
if (ret)
|
* circular dependency problem. We expect:
|
||||||
|
* - PHY driver to probe
|
||||||
|
* - SDHCI driver to start probe
|
||||||
|
* - SDHCI driver to register it's clock
|
||||||
|
* - SDHCI driver to get the PHY
|
||||||
|
* - SDHCI driver to init the PHY
|
||||||
|
*
|
||||||
|
* The clock is optional, so upon any error we just set to NULL.
|
||||||
|
*
|
||||||
|
* NOTE: we don't do anything special for EPROBE_DEFER here. Given the
|
||||||
|
* above expected use case, EPROBE_DEFER isn't sensible to expect, so
|
||||||
|
* it's just like any other error.
|
||||||
|
*/
|
||||||
|
rk_phy->emmcclk = clk_get(&phy->dev, "emmcclk");
|
||||||
|
if (IS_ERR(rk_phy->emmcclk)) {
|
||||||
|
dev_dbg(&phy->dev, "Error getting emmcclk: %d\n", ret);
|
||||||
|
rk_phy->emmcclk = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_emmc_phy_exit(struct phy *phy)
|
||||||
|
{
|
||||||
|
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
|
||||||
|
|
||||||
|
clk_put(rk_phy->emmcclk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rockchip_emmc_phy_power_off(struct phy *phy)
|
||||||
|
{
|
||||||
|
/* Power down emmc phy analog blocks */
|
||||||
|
return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_OFF);
|
||||||
|
}
|
||||||
|
|
||||||
static int rockchip_emmc_phy_power_on(struct phy *phy)
|
static int rockchip_emmc_phy_power_on(struct phy *phy)
|
||||||
{
|
{
|
||||||
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
|
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
|
||||||
int ret = 0;
|
|
||||||
|
/* Drive impedance: 50 Ohm */
|
||||||
|
regmap_write(rk_phy->reg_base,
|
||||||
|
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
|
||||||
|
HIWORD_UPDATE(PHYCTRL_DR_50OHM,
|
||||||
|
PHYCTRL_DR_MASK,
|
||||||
|
PHYCTRL_DR_SHIFT));
|
||||||
|
|
||||||
|
/* Output tap delay: enable */
|
||||||
|
regmap_write(rk_phy->reg_base,
|
||||||
|
rk_phy->reg_offset + GRF_EMMCPHY_CON0,
|
||||||
|
HIWORD_UPDATE(PHYCTRL_OTAPDLYENA,
|
||||||
|
PHYCTRL_OTAPDLYENA_MASK,
|
||||||
|
PHYCTRL_OTAPDLYENA_SHIFT));
|
||||||
|
|
||||||
|
/* Output tap delay */
|
||||||
|
regmap_write(rk_phy->reg_base,
|
||||||
|
rk_phy->reg_offset + GRF_EMMCPHY_CON0,
|
||||||
|
HIWORD_UPDATE(4,
|
||||||
|
PHYCTRL_OTAPDLYSEL_MASK,
|
||||||
|
PHYCTRL_OTAPDLYSEL_SHIFT));
|
||||||
|
|
||||||
/* Power up emmc phy analog blocks */
|
/* Power up emmc phy analog blocks */
|
||||||
ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON);
|
return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct phy_ops ops = {
|
static const struct phy_ops ops = {
|
||||||
|
.init = rockchip_emmc_phy_init,
|
||||||
|
.exit = rockchip_emmc_phy_exit,
|
||||||
.power_on = rockchip_emmc_phy_power_on,
|
.power_on = rockchip_emmc_phy_power_on,
|
||||||
.power_off = rockchip_emmc_phy_power_off,
|
.power_off = rockchip_emmc_phy_power_off,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
@@ -95,6 +95,7 @@ struct mmc_ext_csd {
|
|||||||
u8 raw_partition_support; /* 160 */
|
u8 raw_partition_support; /* 160 */
|
||||||
u8 raw_rpmb_size_mult; /* 168 */
|
u8 raw_rpmb_size_mult; /* 168 */
|
||||||
u8 raw_erased_mem_count; /* 181 */
|
u8 raw_erased_mem_count; /* 181 */
|
||||||
|
u8 strobe_support; /* 184 */
|
||||||
u8 raw_ext_csd_structure; /* 194 */
|
u8 raw_ext_csd_structure; /* 194 */
|
||||||
u8 raw_card_type; /* 196 */
|
u8 raw_card_type; /* 196 */
|
||||||
u8 raw_driver_strength; /* 197 */
|
u8 raw_driver_strength; /* 197 */
|
||||||
@@ -279,6 +280,7 @@ struct mmc_card {
|
|||||||
#define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */
|
#define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */
|
||||||
#define MMC_QUIRK_BROKEN_IRQ_POLLING (1<<11) /* Polling SDIO_CCCR_INTx could create a fake interrupt */
|
#define MMC_QUIRK_BROKEN_IRQ_POLLING (1<<11) /* Polling SDIO_CCCR_INTx could create a fake interrupt */
|
||||||
#define MMC_QUIRK_TRIM_BROKEN (1<<12) /* Skip trim */
|
#define MMC_QUIRK_TRIM_BROKEN (1<<12) /* Skip trim */
|
||||||
|
#define MMC_QUIRK_BROKEN_HPI (1<<13) /* Disable broken HPI support */
|
||||||
|
|
||||||
|
|
||||||
unsigned int erase_size; /* erase size in sectors */
|
unsigned int erase_size; /* erase size in sectors */
|
||||||
@@ -353,6 +355,9 @@ struct mmc_fixup {
|
|||||||
/* SDIO-specfic fields. You can use SDIO_ANY_ID here of course */
|
/* SDIO-specfic fields. You can use SDIO_ANY_ID here of course */
|
||||||
u16 cis_vendor, cis_device;
|
u16 cis_vendor, cis_device;
|
||||||
|
|
||||||
|
/* for MMC cards */
|
||||||
|
unsigned int ext_csd_rev;
|
||||||
|
|
||||||
void (*vendor_fixup)(struct mmc_card *card, int data);
|
void (*vendor_fixup)(struct mmc_card *card, int data);
|
||||||
int data;
|
int data;
|
||||||
};
|
};
|
||||||
@@ -361,11 +366,20 @@ struct mmc_fixup {
|
|||||||
#define CID_OEMID_ANY ((unsigned short) -1)
|
#define CID_OEMID_ANY ((unsigned short) -1)
|
||||||
#define CID_NAME_ANY (NULL)
|
#define CID_NAME_ANY (NULL)
|
||||||
|
|
||||||
|
#define EXT_CSD_REV_ANY (-1u)
|
||||||
|
|
||||||
|
#define CID_MANFID_SANDISK 0x2
|
||||||
|
#define CID_MANFID_TOSHIBA 0x11
|
||||||
|
#define CID_MANFID_MICRON 0x13
|
||||||
|
#define CID_MANFID_SAMSUNG 0x15
|
||||||
|
#define CID_MANFID_KINGSTON 0x70
|
||||||
|
#define CID_MANFID_HYNIX 0x90
|
||||||
|
|
||||||
#define END_FIXUP { NULL }
|
#define END_FIXUP { NULL }
|
||||||
|
|
||||||
#define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \
|
#define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \
|
||||||
_cis_vendor, _cis_device, \
|
_cis_vendor, _cis_device, \
|
||||||
_fixup, _data) \
|
_fixup, _data, _ext_csd_rev) \
|
||||||
{ \
|
{ \
|
||||||
.name = (_name), \
|
.name = (_name), \
|
||||||
.manfid = (_manfid), \
|
.manfid = (_manfid), \
|
||||||
@@ -376,23 +390,30 @@ struct mmc_fixup {
|
|||||||
.cis_device = (_cis_device), \
|
.cis_device = (_cis_device), \
|
||||||
.vendor_fixup = (_fixup), \
|
.vendor_fixup = (_fixup), \
|
||||||
.data = (_data), \
|
.data = (_data), \
|
||||||
|
.ext_csd_rev = (_ext_csd_rev), \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MMC_FIXUP_REV(_name, _manfid, _oemid, _rev_start, _rev_end, \
|
#define MMC_FIXUP_REV(_name, _manfid, _oemid, _rev_start, _rev_end, \
|
||||||
_fixup, _data) \
|
_fixup, _data, _ext_csd_rev) \
|
||||||
_FIXUP_EXT(_name, _manfid, \
|
_FIXUP_EXT(_name, _manfid, \
|
||||||
_oemid, _rev_start, _rev_end, \
|
_oemid, _rev_start, _rev_end, \
|
||||||
SDIO_ANY_ID, SDIO_ANY_ID, \
|
SDIO_ANY_ID, SDIO_ANY_ID, \
|
||||||
_fixup, _data) \
|
_fixup, _data, _ext_csd_rev) \
|
||||||
|
|
||||||
#define MMC_FIXUP(_name, _manfid, _oemid, _fixup, _data) \
|
#define MMC_FIXUP(_name, _manfid, _oemid, _fixup, _data) \
|
||||||
MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data)
|
MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data, \
|
||||||
|
EXT_CSD_REV_ANY)
|
||||||
|
|
||||||
|
#define MMC_FIXUP_EXT_CSD_REV(_name, _manfid, _oemid, _fixup, _data, \
|
||||||
|
_ext_csd_rev) \
|
||||||
|
MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data, \
|
||||||
|
_ext_csd_rev)
|
||||||
|
|
||||||
#define SDIO_FIXUP(_vendor, _device, _fixup, _data) \
|
#define SDIO_FIXUP(_vendor, _device, _fixup, _data) \
|
||||||
_FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY, \
|
_FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY, \
|
||||||
CID_OEMID_ANY, 0, -1ull, \
|
CID_OEMID_ANY, 0, -1ull, \
|
||||||
_vendor, _device, \
|
_vendor, _device, \
|
||||||
_fixup, _data) \
|
_fixup, _data, EXT_CSD_REV_ANY) \
|
||||||
|
|
||||||
#define cid_rev(hwrev, fwrev, year, month) \
|
#define cid_rev(hwrev, fwrev, year, month) \
|
||||||
(((u64) hwrev) << 40 | \
|
(((u64) hwrev) << 40 | \
|
||||||
@@ -511,6 +532,11 @@ static inline int mmc_card_broken_irq_polling(const struct mmc_card *c)
|
|||||||
return c->quirks & MMC_QUIRK_BROKEN_IRQ_POLLING;
|
return c->quirks & MMC_QUIRK_BROKEN_IRQ_POLLING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int mmc_card_broken_hpi(const struct mmc_card *c)
|
||||||
|
{
|
||||||
|
return c->quirks & MMC_QUIRK_BROKEN_HPI;
|
||||||
|
}
|
||||||
|
|
||||||
#define mmc_card_name(c) ((c)->cid.prod_name)
|
#define mmc_card_name(c) ((c)->cid.prod_name)
|
||||||
#define mmc_card_id(c) (dev_name(&(c)->dev))
|
#define mmc_card_id(c) (dev_name(&(c)->dev))
|
||||||
|
|
||||||
|
@@ -112,7 +112,6 @@ struct dw_mci_dma_slave {
|
|||||||
* @part_buf: Simple buffer for partial fifo reads/writes.
|
* @part_buf: Simple buffer for partial fifo reads/writes.
|
||||||
* @push_data: Pointer to FIFO push function.
|
* @push_data: Pointer to FIFO push function.
|
||||||
* @pull_data: Pointer to FIFO pull function.
|
* @pull_data: Pointer to FIFO pull function.
|
||||||
* @quirks: Set of quirks that apply to specific versions of the IP.
|
|
||||||
* @vqmmc_enabled: Status of vqmmc, should be true or false.
|
* @vqmmc_enabled: Status of vqmmc, should be true or false.
|
||||||
* @irq_flags: The flags to be passed to request_irq.
|
* @irq_flags: The flags to be passed to request_irq.
|
||||||
* @irq: The irq value to be passed to request_irq.
|
* @irq: The irq value to be passed to request_irq.
|
||||||
@@ -218,9 +217,6 @@ struct dw_mci {
|
|||||||
void (*push_data)(struct dw_mci *host, void *buf, int cnt);
|
void (*push_data)(struct dw_mci *host, void *buf, int cnt);
|
||||||
void (*pull_data)(struct dw_mci *host, void *buf, int cnt);
|
void (*pull_data)(struct dw_mci *host, void *buf, int cnt);
|
||||||
|
|
||||||
/* Workaround flags */
|
|
||||||
u32 quirks;
|
|
||||||
|
|
||||||
bool vqmmc_enabled;
|
bool vqmmc_enabled;
|
||||||
unsigned long irq_flags; /* IRQ flags */
|
unsigned long irq_flags; /* IRQ flags */
|
||||||
int irq;
|
int irq;
|
||||||
@@ -242,17 +238,12 @@ struct dw_mci_dma_ops {
|
|||||||
void (*exit)(struct dw_mci *host);
|
void (*exit)(struct dw_mci *host);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* IP Quirks/flags. */
|
|
||||||
/* Timer for broken data transfer over scheme */
|
|
||||||
#define DW_MCI_QUIRK_BROKEN_DTO BIT(0)
|
|
||||||
|
|
||||||
struct dma_pdata;
|
struct dma_pdata;
|
||||||
|
|
||||||
/* Board platform data */
|
/* Board platform data */
|
||||||
struct dw_mci_board {
|
struct dw_mci_board {
|
||||||
u32 num_slots;
|
u32 num_slots;
|
||||||
|
|
||||||
u32 quirks; /* Workaround / Quirk flags */
|
|
||||||
unsigned int bus_hz; /* Clock speed at the cclk_in pad */
|
unsigned int bus_hz; /* Clock speed at the cclk_in pad */
|
||||||
|
|
||||||
u32 caps; /* Capabilities */
|
u32 caps; /* Capabilities */
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <linux/mmc/core.h>
|
#include <linux/mmc/core.h>
|
||||||
#include <linux/mmc/card.h>
|
#include <linux/mmc/card.h>
|
||||||
|
#include <linux/mmc/mmc.h>
|
||||||
#include <linux/mmc/pm.h>
|
#include <linux/mmc/pm.h>
|
||||||
|
|
||||||
struct mmc_ios {
|
struct mmc_ios {
|
||||||
@@ -77,6 +78,8 @@ struct mmc_ios {
|
|||||||
#define MMC_SET_DRIVER_TYPE_A 1
|
#define MMC_SET_DRIVER_TYPE_A 1
|
||||||
#define MMC_SET_DRIVER_TYPE_C 2
|
#define MMC_SET_DRIVER_TYPE_C 2
|
||||||
#define MMC_SET_DRIVER_TYPE_D 3
|
#define MMC_SET_DRIVER_TYPE_D 3
|
||||||
|
|
||||||
|
bool enhanced_strobe; /* hs400es selection */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mmc_host_ops {
|
struct mmc_host_ops {
|
||||||
@@ -143,6 +146,9 @@ struct mmc_host_ops {
|
|||||||
|
|
||||||
/* Prepare HS400 target operating frequency depending host driver */
|
/* Prepare HS400 target operating frequency depending host driver */
|
||||||
int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
|
int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
|
||||||
|
/* Prepare enhanced strobe depending host driver */
|
||||||
|
void (*hs400_enhanced_strobe)(struct mmc_host *host,
|
||||||
|
struct mmc_ios *ios);
|
||||||
int (*select_drive_strength)(struct mmc_card *card,
|
int (*select_drive_strength)(struct mmc_card *card,
|
||||||
unsigned int max_dtr, int host_drv,
|
unsigned int max_dtr, int host_drv,
|
||||||
int card_drv, int *drv_type);
|
int card_drv, int *drv_type);
|
||||||
@@ -302,6 +308,9 @@ struct mmc_host {
|
|||||||
#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
|
#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
|
||||||
#define MMC_CAP2_NO_WRITE_PROTECT (1 << 18) /* No physical write protect pin, assume that card is always read-write */
|
#define MMC_CAP2_NO_WRITE_PROTECT (1 << 18) /* No physical write protect pin, assume that card is always read-write */
|
||||||
#define MMC_CAP2_NO_SDIO (1 << 19) /* Do not send SDIO commands during initialization */
|
#define MMC_CAP2_NO_SDIO (1 << 19) /* Do not send SDIO commands during initialization */
|
||||||
|
#define MMC_CAP2_HS400_ES (1 << 20) /* Host supports enhanced strobe */
|
||||||
|
#define MMC_CAP2_NO_SD (1 << 21) /* Do not send SD commands during initialization */
|
||||||
|
#define MMC_CAP2_NO_MMC (1 << 22) /* Do not send (e)MMC commands during initialization */
|
||||||
|
|
||||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||||
|
|
||||||
@@ -513,6 +522,11 @@ static inline bool mmc_card_hs400(struct mmc_card *card)
|
|||||||
return card->host->ios.timing == MMC_TIMING_MMC_HS400;
|
return card->host->ios.timing == MMC_TIMING_MMC_HS400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool mmc_card_hs400es(struct mmc_card *card)
|
||||||
|
{
|
||||||
|
return card->host->ios.enhanced_strobe;
|
||||||
|
}
|
||||||
|
|
||||||
void mmc_retune_timer_stop(struct mmc_host *host);
|
void mmc_retune_timer_stop(struct mmc_host *host);
|
||||||
|
|
||||||
static inline void mmc_retune_needed(struct mmc_host *host)
|
static inline void mmc_retune_needed(struct mmc_host *host)
|
||||||
|
@@ -297,6 +297,7 @@ struct _mmc_csd {
|
|||||||
#define EXT_CSD_PART_CONFIG 179 /* R/W */
|
#define EXT_CSD_PART_CONFIG 179 /* R/W */
|
||||||
#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */
|
#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */
|
||||||
#define EXT_CSD_BUS_WIDTH 183 /* R/W */
|
#define EXT_CSD_BUS_WIDTH 183 /* R/W */
|
||||||
|
#define EXT_CSD_STROBE_SUPPORT 184 /* RO */
|
||||||
#define EXT_CSD_HS_TIMING 185 /* R/W */
|
#define EXT_CSD_HS_TIMING 185 /* R/W */
|
||||||
#define EXT_CSD_POWER_CLASS 187 /* R/W */
|
#define EXT_CSD_POWER_CLASS 187 /* R/W */
|
||||||
#define EXT_CSD_REV 192 /* RO */
|
#define EXT_CSD_REV 192 /* RO */
|
||||||
@@ -380,12 +381,14 @@ struct _mmc_csd {
|
|||||||
#define EXT_CSD_CARD_TYPE_HS400_1_2V (1<<7) /* Card can run at 200MHz DDR, 1.2V */
|
#define EXT_CSD_CARD_TYPE_HS400_1_2V (1<<7) /* Card can run at 200MHz DDR, 1.2V */
|
||||||
#define EXT_CSD_CARD_TYPE_HS400 (EXT_CSD_CARD_TYPE_HS400_1_8V | \
|
#define EXT_CSD_CARD_TYPE_HS400 (EXT_CSD_CARD_TYPE_HS400_1_8V | \
|
||||||
EXT_CSD_CARD_TYPE_HS400_1_2V)
|
EXT_CSD_CARD_TYPE_HS400_1_2V)
|
||||||
|
#define EXT_CSD_CARD_TYPE_HS400ES (1<<8) /* Card can run at HS400ES */
|
||||||
|
|
||||||
#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
|
#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
|
||||||
#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
|
#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
|
||||||
#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */
|
#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */
|
||||||
#define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */
|
#define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */
|
||||||
#define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */
|
#define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */
|
||||||
|
#define EXT_CSD_BUS_WIDTH_STROBE BIT(7) /* Enhanced strobe mode */
|
||||||
|
|
||||||
#define EXT_CSD_TIMING_BC 0 /* Backwards compatility */
|
#define EXT_CSD_TIMING_BC 0 /* Backwards compatility */
|
||||||
#define EXT_CSD_TIMING_HS 1 /* High speed */
|
#define EXT_CSD_TIMING_HS 1 /* High speed */
|
||||||
|
@@ -46,5 +46,6 @@ struct esdhc_platform_data {
|
|||||||
bool support_vsel;
|
bool support_vsel;
|
||||||
unsigned int delay_line;
|
unsigned int delay_line;
|
||||||
unsigned int tuning_step; /* The delay cell steps in tuning procedure */
|
unsigned int tuning_step; /* The delay cell steps in tuning procedure */
|
||||||
|
unsigned int tuning_start_tap; /* The start delay cell point in tuning procedure */
|
||||||
};
|
};
|
||||||
#endif /* __ASM_ARCH_IMX_ESDHC_H */
|
#endif /* __ASM_ARCH_IMX_ESDHC_H */
|
||||||
|
Reference in New Issue
Block a user