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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJXnibNAAoJEP4mhCVzWIwpKIQQAM2iTx5ROuOe3/CmWEAv5phg XpgmuWz/R/KQH1wyDor4zEJUE5mUYpvSg9vSCByYnVwHpoNKg4R0t0OJZJSN05Z0 njdKCtkVXlRaQitX3tiFQazkMLaQER0tRrqrHjYZYkBgr8DnSEf00aueSGshrr/p g/JE7Z+eGVJx6d3Lvd24NMVZYSGbVq5lY/JTbURkKR5C9OhEe5GMfUsw1M+aVhUo pvxeGVYuIr4WsP+c+roa6czTO8VMrsd71syLDCweWMk+GOHvaSjpzvdC7iFGAl4B CoN6f3LTpJGpDCBWnqp/yQMvrMsR1SKlrwOUph4XEAXZCaz5WtF8+vhQaK7gwYGO RiyIIh04Bup+gzY6w0Mwgsw0jAkw5ahXl6xknD58q/sPEdlKe0ATyxl0oUwDntc0 957BtmOvChYG13Q5pW6VNef0jKW6LOU+brJKEGxtr8x9OPeCiQOBO/GCUTg3IwA5 ohylmjS+/6G3G4bgGvfXyGvObOsdAh3RQ8g7/1pVb0hQobDhxO5PjtTqVtelfFJk PRdKKIWhROiNMrpVLZnL7+OLkusEQ4s0Lmq08T1dVXvV+hLITwyTyWYmBr6Kb51B 2x3DO7PcfEyytSn3TjPjdp7Wax+19YL+tMmu2QAlXv3iHSKFAH17nLk0bPO6VOWZ FHzKKutm4TwZJ/fyyiqP =wzSC -----END PGP SIGNATURE----- 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:
commit
07f00f06ba
|
@ -9,8 +9,12 @@ Device Tree Bindings for the Arasan SDHCI Controller
|
|||
[4] Documentation/devicetree/bindings/phy/phy-bindings.txt
|
||||
|
||||
Required Properties:
|
||||
- compatible: Compatibility string. Must be 'arasan,sdhci-8.9a' or
|
||||
'arasan,sdhci-4.9a' or 'arasan,sdhci-5.1'
|
||||
- compatible: Compatibility string. One of:
|
||||
- "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.
|
||||
- clocks: From clock bindings: Handles to clock inputs.
|
||||
- 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.
|
||||
- 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:
|
||||
sdhci@e0100000 {
|
||||
compatible = "arasan,sdhci-8.9a";
|
||||
|
@ -42,3 +57,19 @@ Example:
|
|||
phys = <&emmc_phy>;
|
||||
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>;
|
||||
};
|
|
@ -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
|
||||
required, first cell specifies minimum slot voltage (mV), second cell
|
||||
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.
|
||||
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
|
||||
|
|
|
@ -46,8 +46,12 @@ Optional properties:
|
|||
- 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_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
|
||||
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
|
||||
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
|
||||
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:
|
||||
|
||||
|
||||
|
@ -20,6 +27,8 @@ grf: syscon@ff770000 {
|
|||
emmcphy: phy@f780 {
|
||||
compatible = "rockchip,rk3399-emmc-phy";
|
||||
reg = <0xf780 0x20>;
|
||||
clocks = <&sdhci>;
|
||||
clock-names = "emmcclk";
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -28,6 +28,8 @@ All attributes are read-only.
|
|||
preferred_erase_size Preferred erase size
|
||||
raw_rpmb_size_mult RPMB partition size
|
||||
rel_sectors Reliable write sector count
|
||||
ocr Operation Conditions Register
|
||||
dsr Driver Stage Register
|
||||
|
||||
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
|
||||
T: git git://git.linaro.org/people/ulf.hansson/mmc.git
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/mmc/
|
||||
F: drivers/mmc/
|
||||
F: include/linux/mmc/
|
||||
F: include/uapi/linux/mmc/
|
||||
|
@ -10355,6 +10356,13 @@ F: tools/testing/selftests/seccomp/*
|
|||
K: \bsecure_computing
|
||||
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
|
||||
M: Ben Dooks <ben-linux@fluff.org>
|
||||
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) &&
|
||||
(prq->cmd_flags & REQ_META) &&
|
||||
(rq_data_dir(prq) == WRITE) &&
|
||||
((brq->data.blocks * brq->data.blksz) >=
|
||||
card->ext_csd.data_tag_unit_size);
|
||||
blk_rq_bytes(prq) >= card->ext_csd.data_tag_unit_size;
|
||||
/* Argument of CMD23 */
|
||||
packed_cmd_hdr[(i * 2)] = cpu_to_le32(
|
||||
(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
|
||||
* multiple read or write is allowed
|
||||
*/
|
||||
if ((brq->data.blocks & 0x07) &&
|
||||
(card->ext_csd.data_sector_size == 4096)) {
|
||||
if (mmc_large_sector(card) &&
|
||||
!IS_ALIGNED(blk_rq_sectors(rqc), 8)) {
|
||||
pr_err("%s: Transfer size is not 4KB sector size aligned\n",
|
||||
req->rq_disk->disk_name);
|
||||
mq_rq = mq->mqrq_cur;
|
||||
|
@ -2501,12 +2500,6 @@ force_ro_fail:
|
|||
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[] =
|
||||
{
|
||||
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 " : "",
|
||||
type);
|
||||
} 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_card_uhs(card) ? "ultra high speed " :
|
||||
(mmc_card_hs(card) ? "high speed " : ""),
|
||||
mmc_card_hs400(card) ? "HS400 " :
|
||||
(mmc_card_hs200(card) ? "HS200 " : ""),
|
||||
mmc_card_hs400es(card) ? "Enhanced strobe " : "",
|
||||
mmc_card_ddr52(card) ? "DDR " : "",
|
||||
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.timing = MMC_TIMING_LEGACY;
|
||||
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);
|
||||
}
|
||||
|
@ -1925,17 +1934,15 @@ void mmc_init_erase(struct mmc_card *card)
|
|||
* to that size and alignment.
|
||||
*
|
||||
* For SD cards that define Allocation Unit size, limit erases to one
|
||||
* Allocation Unit at a time. For MMC cards that define High Capacity
|
||||
* Erase Size, whether it is switched on or not, limit to that size.
|
||||
* Otherwise just have a stab at a good value. For modern cards it
|
||||
* will end up being 4MiB. Note that if the value is too small, it
|
||||
* can end up taking longer to erase.
|
||||
* Allocation Unit at a time.
|
||||
* For MMC, have a stab at ai good value and for modern cards it will
|
||||
* end up being 4MiB. Note that if the value is too small, it can end
|
||||
* up taking longer to erase. Also note, erase_size is already set to
|
||||
* High Capacity Erase Size if available when this function is called.
|
||||
*/
|
||||
if (mmc_card_sd(card) && card->ssr.au) {
|
||||
card->pref_erase = card->ssr.au;
|
||||
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) {
|
||||
sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >> 11;
|
||||
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)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
unsigned int qty = 0;
|
||||
unsigned int qty = 0, busy_timeout = 0;
|
||||
bool use_r1b_resp = false;
|
||||
unsigned long timeout;
|
||||
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));
|
||||
cmd.opcode = MMC_ERASE;
|
||||
cmd.arg = arg;
|
||||
cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
|
||||
cmd.busy_timeout = mmc_erase_timeout(card, arg, qty);
|
||||
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.busy_timeout = busy_timeout;
|
||||
use_r1b_resp = true;
|
||||
}
|
||||
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
||||
if (err) {
|
||||
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))
|
||||
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 {
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
cmd.opcode = MMC_SEND_STATUS;
|
||||
|
@ -2321,23 +2350,41 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card,
|
|||
unsigned int arg)
|
||||
{
|
||||
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;
|
||||
|
||||
if (card->erase_shift)
|
||||
if (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;
|
||||
else
|
||||
min_qty = card->pref_erase;
|
||||
} else {
|
||||
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 {
|
||||
y = 0;
|
||||
for (x = 1; x && x <= max_qty && max_qty - x >= qty; x <<= 1) {
|
||||
timeout = mmc_erase_timeout(card, arg, qty + x);
|
||||
if (timeout > host->max_busy_timeout)
|
||||
|
||||
if (qty + x > min_qty &&
|
||||
timeout > host->max_busy_timeout)
|
||||
break;
|
||||
|
||||
if (timeout < last_timeout)
|
||||
break;
|
||||
last_timeout = timeout;
|
||||
|
@ -2491,17 +2538,21 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
|
|||
|
||||
mmc_go_idle(host);
|
||||
|
||||
mmc_send_if_cond(host, host->ocr_avail);
|
||||
if (!(host->caps2 & MMC_CAP2_NO_SD))
|
||||
mmc_send_if_cond(host, host->ocr_avail);
|
||||
|
||||
/* Order's important: probe SDIO, then SD, then MMC */
|
||||
if (!(host->caps2 & MMC_CAP2_NO_SDIO))
|
||||
if (!mmc_attach_sdio(host))
|
||||
return 0;
|
||||
|
||||
if (!mmc_attach_sd(host))
|
||||
return 0;
|
||||
if (!mmc_attach_mmc(host))
|
||||
return 0;
|
||||
if (!(host->caps2 & MMC_CAP2_NO_SD))
|
||||
if (!mmc_attach_sd(host))
|
||||
return 0;
|
||||
|
||||
if (!(host->caps2 & MMC_CAP2_NO_MMC))
|
||||
if (!mmc_attach_mmc(host))
|
||||
return 0;
|
||||
|
||||
mmc_power_off(host);
|
||||
return -EIO;
|
||||
|
|
|
@ -148,7 +148,8 @@ static int mmc_ios_show(struct seq_file *s, void *data)
|
|||
str = "mmc HS200";
|
||||
break;
|
||||
case MMC_TIMING_MMC_HS400:
|
||||
str = "mmc HS400";
|
||||
str = mmc_card_hs400es(host->card) ?
|
||||
"mmc HS400 enhanced strobe" : "mmc HS400";
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
if (of_property_read_bool(np, "mmc-hs400-1_2v"))
|
||||
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);
|
||||
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,
|
||||
};
|
||||
|
||||
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) \
|
||||
({ \
|
||||
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;
|
||||
}
|
||||
|
||||
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.hs200_max_dtr = hs200_max_dtr;
|
||||
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];
|
||||
|
||||
/* 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[1] = ext_csd[EXT_CSD_SEC_CNT + 1];
|
||||
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);
|
||||
}
|
||||
|
||||
card->ext_csd.strobe_support = ext_csd[EXT_CSD_STROBE_SUPPORT];
|
||||
card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
|
||||
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;
|
||||
|
||||
/* 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.man_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 */
|
||||
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;
|
||||
if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2)
|
||||
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(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(ocr, "%08x\n", card->ocr);
|
||||
|
||||
static ssize_t mmc_fwrev_show(struct device *dev,
|
||||
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 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[] = {
|
||||
&dev_attr_cid.attr,
|
||||
&dev_attr_csd.attr,
|
||||
|
@ -762,6 +801,8 @@ static struct attribute *mmc_std_attrs[] = {
|
|||
&dev_attr_enhanced_area_size.attr,
|
||||
&dev_attr_raw_rpmb_size_mult.attr,
|
||||
&dev_attr_rel_sectors.attr,
|
||||
&dev_attr_ocr.attr,
|
||||
&dev_attr_dsr.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(mmc_std);
|
||||
|
@ -959,6 +1000,19 @@ static int mmc_select_bus_width(struct mmc_card *card)
|
|||
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
|
||||
*/
|
||||
|
@ -969,9 +1023,11 @@ static int mmc_select_hs(struct mmc_card *card)
|
|||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
|
||||
card->ext_csd.generic_cmd6_time,
|
||||
true, true, true);
|
||||
if (!err)
|
||||
true, false, true);
|
||||
if (!err) {
|
||||
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
||||
err = mmc_switch_status(card);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -1047,23 +1103,9 @@ static int mmc_select_hs_ddr(struct mmc_card *card)
|
|||
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)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
bool send_status = true;
|
||||
unsigned int max_dtr;
|
||||
int err = 0;
|
||||
u8 val;
|
||||
|
@ -1075,19 +1117,12 @@ static int mmc_select_hs400(struct mmc_card *card)
|
|||
host->ios.bus_width == MMC_BUS_WIDTH_8))
|
||||
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 */
|
||||
val = EXT_CSD_TIMING_HS;
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, val,
|
||||
card->ext_csd.generic_cmd6_time,
|
||||
true, send_status, true);
|
||||
true, false, true);
|
||||
if (err) {
|
||||
pr_err("%s: switch to high-speed from hs200 failed, err:%d\n",
|
||||
mmc_hostname(host), err);
|
||||
|
@ -1097,11 +1132,13 @@ static int mmc_select_hs400(struct mmc_card *card)
|
|||
/* Set host controller to HS timing */
|
||||
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
||||
|
||||
if (!send_status) {
|
||||
err = mmc_switch_status(card);
|
||||
if (err)
|
||||
goto out_err;
|
||||
}
|
||||
/* Reduce frequency to HS frequency */
|
||||
max_dtr = card->ext_csd.hs_max_dtr;
|
||||
mmc_set_clock(host, max_dtr);
|
||||
|
||||
err = mmc_switch_status(card);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
/* Switch card to DDR */
|
||||
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,
|
||||
EXT_CSD_HS_TIMING, val,
|
||||
card->ext_csd.generic_cmd6_time,
|
||||
true, send_status, true);
|
||||
true, false, true);
|
||||
if (err) {
|
||||
pr_err("%s: switch to hs400 failed, err:%d\n",
|
||||
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_bus_speed(card);
|
||||
|
||||
if (!send_status) {
|
||||
err = mmc_switch_status(card);
|
||||
if (err)
|
||||
goto out_err;
|
||||
}
|
||||
err = mmc_switch_status(card);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -1153,14 +1188,10 @@ int mmc_hs200_to_hs400(struct mmc_card *card)
|
|||
int mmc_hs400_to_hs200(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
bool send_status = true;
|
||||
unsigned int max_dtr;
|
||||
int err;
|
||||
u8 val;
|
||||
|
||||
if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
|
||||
send_status = false;
|
||||
|
||||
/* Reduce frequency to HS */
|
||||
max_dtr = card->ext_csd.hs_max_dtr;
|
||||
mmc_set_clock(host, max_dtr);
|
||||
|
@ -1169,49 +1200,43 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
|
|||
val = EXT_CSD_TIMING_HS;
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
|
||||
val, card->ext_csd.generic_cmd6_time,
|
||||
true, send_status, true);
|
||||
true, false, true);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
|
||||
|
||||
if (!send_status) {
|
||||
err = mmc_switch_status(card);
|
||||
if (err)
|
||||
goto out_err;
|
||||
}
|
||||
err = mmc_switch_status(card);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
/* Switch HS DDR to HS */
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
|
||||
EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time,
|
||||
true, send_status, true);
|
||||
true, false, true);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
mmc_set_timing(host, MMC_TIMING_MMC_HS);
|
||||
|
||||
if (!send_status) {
|
||||
err = mmc_switch_status(card);
|
||||
if (err)
|
||||
goto out_err;
|
||||
}
|
||||
err = mmc_switch_status(card);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
/* Switch HS to HS200 */
|
||||
val = EXT_CSD_TIMING_HS200 |
|
||||
card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
|
||||
val, card->ext_csd.generic_cmd6_time, true,
|
||||
send_status, true);
|
||||
val, card->ext_csd.generic_cmd6_time,
|
||||
true, false, true);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
mmc_set_timing(host, MMC_TIMING_MMC_HS200);
|
||||
|
||||
if (!send_status) {
|
||||
err = mmc_switch_status(card);
|
||||
if (err)
|
||||
goto out_err;
|
||||
}
|
||||
err = mmc_switch_status(card);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
mmc_set_bus_speed(card);
|
||||
|
||||
|
@ -1223,6 +1248,78 @@ out_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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
bool send_status = true;
|
||||
unsigned int old_timing, old_signal_voltage;
|
||||
int err = -EINVAL;
|
||||
u8 val;
|
||||
|
@ -1268,34 +1364,30 @@ static int mmc_select_hs200(struct mmc_card *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
|
||||
* switch to HS200 mode if bus width is set successfully.
|
||||
*/
|
||||
err = mmc_select_bus_width(card);
|
||||
if (err >= 0) {
|
||||
if (err > 0) {
|
||||
val = EXT_CSD_TIMING_HS200 |
|
||||
card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, val,
|
||||
card->ext_csd.generic_cmd6_time,
|
||||
true, send_status, true);
|
||||
true, false, true);
|
||||
if (err)
|
||||
goto err;
|
||||
old_timing = host->ios.timing;
|
||||
mmc_set_timing(host, MMC_TIMING_MMC_HS200);
|
||||
if (!send_status) {
|
||||
err = mmc_switch_status(card);
|
||||
/*
|
||||
* mmc_select_timing() assumes timing has not changed if
|
||||
* it is a switch error.
|
||||
*/
|
||||
if (err == -EBADMSG)
|
||||
mmc_set_timing(host, old_timing);
|
||||
}
|
||||
|
||||
err = mmc_switch_status(card);
|
||||
/*
|
||||
* mmc_select_timing() assumes timing has not changed if
|
||||
* it is a switch error.
|
||||
*/
|
||||
if (err == -EBADMSG)
|
||||
mmc_set_timing(host, old_timing);
|
||||
}
|
||||
err:
|
||||
if (err) {
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -1319,7 +1411,9 @@ static int mmc_select_timing(struct mmc_card *card)
|
|||
if (!mmc_can_ext_csd(card))
|
||||
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);
|
||||
else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS)
|
||||
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)) {
|
||||
/* Select the desired bus width optionally */
|
||||
err = mmc_select_bus_width(card);
|
||||
if (err >= 0) {
|
||||
if (err > 0) {
|
||||
err = mmc_select_hs_ddr(card);
|
||||
if (err)
|
||||
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
|
||||
* 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,
|
||||
EXT_CSD_CACHE_CTRL, 1,
|
||||
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;
|
||||
bool use_r1b_resp = use_busy_signal;
|
||||
bool expired = false;
|
||||
bool busy = false;
|
||||
|
||||
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;
|
||||
|
||||
/* 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 {
|
||||
/*
|
||||
* Due to the possibility of being preempted after
|
||||
* sending the status command, check the expiration
|
||||
* time first.
|
||||
*/
|
||||
expired = time_after(jiffies, timeout);
|
||||
if (send_status) {
|
||||
/*
|
||||
* Due to the possibility of being preempted after
|
||||
* sending the status command, check the expiration
|
||||
* time first.
|
||||
*/
|
||||
expired = time_after(jiffies, timeout);
|
||||
err = __mmc_send_status(card, &status, ignore_crc);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
|
||||
break;
|
||||
if (host->ops->card_busy) {
|
||||
if (!host->ops->card_busy(host))
|
||||
break;
|
||||
busy = true;
|
||||
}
|
||||
if (mmc_host_is_spi(host))
|
||||
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
|
||||
* rely on waiting for the stated timeout to be sufficient.
|
||||
*/
|
||||
if (!send_status) {
|
||||
if (!send_status && !host->ops->card_busy) {
|
||||
mmc_delay(timeout_ms);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* 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",
|
||||
mmc_hostname(host), __func__);
|
||||
err = -ETIMEDOUT;
|
||||
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);
|
||||
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_device == card->cis.device ||
|
||||
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) {
|
||||
dev_dbg(&card->dev, "calling %pf\n", f->vendor_fixup);
|
||||
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(oemid, "0x%04x\n", card->cid.oemid);
|
||||
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[] = {
|
||||
&dev_attr_cid.attr,
|
||||
&dev_attr_csd.attr,
|
||||
|
@ -690,6 +707,8 @@ static struct attribute *sd_std_attrs[] = {
|
|||
&dev_attr_name.attr,
|
||||
&dev_attr_oemid.attr,
|
||||
&dev_attr_serial.attr,
|
||||
&dev_attr_ocr.attr,
|
||||
&dev_attr_dsr.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(sd_std);
|
||||
|
|
|
@ -122,6 +122,7 @@ config MMC_SDHCI_OF_ARASAN
|
|||
tristate "SDHCI OF support for the Arasan SDHCI controllers"
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on OF
|
||||
depends on COMMON_CLK
|
||||
help
|
||||
This selects the Arasan Secure Digital Host Controller Interface
|
||||
(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.
|
||||
|
||||
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
|
||||
tristate "SDHCI support for Fujitsu Semiconductor F_SDH30"
|
||||
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 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_HLWD) += sdhci-of-hlwd.o
|
||||
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
|
||||
obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
|
||||
obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
|
||||
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
|
||||
obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o
|
||||
obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o
|
||||
obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o
|
||||
|
||||
ifeq ($(CONFIG_CB710_DEBUG),y)
|
||||
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
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,12 @@ struct k3_priv {
|
|||
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)
|
||||
{
|
||||
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 = {
|
||||
.caps = dw_mci_hi6220_caps,
|
||||
.switch_voltage = dw_mci_hi6220_switch_voltage,
|
||||
.set_ios = dw_mci_hi6220_set_ios,
|
||||
.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 */
|
||||
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,
|
||||
"rockchip,rk3288-dw-mshc"))
|
||||
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 */
|
||||
static unsigned long dw_mci_rk3288_dwmmc_caps[4] = {
|
||||
MMC_CAP_ERASE | MMC_CAP_CMD23,
|
||||
MMC_CAP_ERASE | MMC_CAP_CMD23,
|
||||
MMC_CAP_ERASE | MMC_CAP_CMD23,
|
||||
MMC_CAP_ERASE | MMC_CAP_CMD23,
|
||||
MMC_CAP_CMD23,
|
||||
MMC_CAP_CMD23,
|
||||
MMC_CAP_CMD23,
|
||||
MMC_CAP_CMD23,
|
||||
};
|
||||
|
||||
static const struct dw_mci_drv_data rk2928_drv_data = {
|
||||
|
|
|
@ -44,11 +44,11 @@
|
|||
/* Common flag combinations */
|
||||
#define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \
|
||||
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 | \
|
||||
SDMMC_INT_RESP_ERR)
|
||||
SDMMC_INT_RESP_ERR | SDMMC_INT_HLE)
|
||||
#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_RECV_STATUS 2
|
||||
#define DW_MCI_DMA_THRESHOLD 16
|
||||
|
@ -92,7 +92,7 @@ struct idmac_desc {
|
|||
|
||||
__le32 des1; /* Buffer sizes */
|
||||
#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 */
|
||||
|
||||
|
@ -105,6 +105,7 @@ struct idmac_desc {
|
|||
static bool dw_mci_reset(struct dw_mci *host);
|
||||
static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset);
|
||||
static int dw_mci_card_busy(struct mmc_host *mmc);
|
||||
static int dw_mci_get_cd(struct mmc_host *mmc);
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
static int dw_mci_req_show(struct seq_file *s, void *v)
|
||||
|
@ -898,23 +899,35 @@ done:
|
|||
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;
|
||||
u32 blksz_depth, fifo_depth;
|
||||
u16 thld_size;
|
||||
|
||||
WARN_ON(!(data->flags & MMC_DATA_READ));
|
||||
u8 enable;
|
||||
|
||||
/*
|
||||
* CDTHRCTL doesn't exist prior to 240A (in fact that register offset is
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* 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 &&
|
||||
host->timing != MMC_TIMING_MMC_HS400 &&
|
||||
host->timing != MMC_TIMING_UHS_SDR104)
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
|
||||
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)
|
||||
|
@ -1005,12 +1018,12 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
|
|||
host->sg = NULL;
|
||||
host->data = data;
|
||||
|
||||
if (data->flags & MMC_DATA_READ) {
|
||||
if (data->flags & MMC_DATA_READ)
|
||||
host->dir_status = DW_MCI_RECV_STATUS;
|
||||
dw_mci_ctrl_rd_thld(host, data);
|
||||
} else {
|
||||
else
|
||||
host->dir_status = DW_MCI_SEND_STATUS;
|
||||
}
|
||||
|
||||
dw_mci_ctrl_thld(host, data);
|
||||
|
||||
if (dw_mci_submit_data_dma(host, data)) {
|
||||
if (host->data->flags & MMC_DATA_READ)
|
||||
|
@ -1099,12 +1112,11 @@ 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;
|
||||
|
||||
if ((clock << div) != slot->__clk_old || force_clkinit)
|
||||
dev_info(&slot->mmc->class_dev,
|
||||
"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
|
||||
slot->id, host->bus_hz, clock,
|
||||
div ? ((host->bus_hz / div) >> 1) :
|
||||
host->bus_hz, div);
|
||||
dev_info(&slot->mmc->class_dev,
|
||||
"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
|
||||
slot->id, host->bus_hz, clock,
|
||||
div ? ((host->bus_hz / div) >> 1) :
|
||||
host->bus_hz, div);
|
||||
|
||||
/* disable clock */
|
||||
mci_writel(host, CLKENA, 0);
|
||||
|
@ -1127,9 +1139,6 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
|
|||
|
||||
/* inform CIU */
|
||||
mci_send_cmd(slot, sdmmc_cmd_bits, 0);
|
||||
|
||||
/* keep the clock with reflecting clock dividor */
|
||||
slot->__clk_old = clock << div;
|
||||
}
|
||||
|
||||
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
|
||||
* request wouldn't fail until another card was inserted.
|
||||
*/
|
||||
spin_lock_bh(&host->lock);
|
||||
|
||||
if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
|
||||
spin_unlock_bh(&host->lock);
|
||||
if (!dw_mci_get_cd(mmc)) {
|
||||
mrq->cmd->error = -ENOMEDIUM;
|
||||
mmc_request_done(mmc, mrq);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_bh(&host->lock);
|
||||
|
||||
dw_mci_queue_request(host, slot, mrq);
|
||||
|
||||
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);
|
||||
|
||||
/* Use platform get_cd function, else try onboard card detect */
|
||||
if ((mmc->caps & MMC_CAP_NEEDS_POLL) ||
|
||||
(mmc->caps & MMC_CAP_NONREMOVABLE))
|
||||
if ((mmc->caps & MMC_CAP_NEEDS_POLL) || !mmc_card_is_removable(mmc))
|
||||
present = 1;
|
||||
else if (gpio_cd >= 0)
|
||||
present = gpio_cd;
|
||||
|
@ -1761,6 +1769,33 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|||
}
|
||||
|
||||
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);
|
||||
send_stop_abort(host, data);
|
||||
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
|
||||
* within the given time in reading data state.
|
||||
*/
|
||||
if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) &&
|
||||
(host->dir_status == DW_MCI_RECV_STATUS))
|
||||
if (host->dir_status == DW_MCI_RECV_STATUS)
|
||||
dw_mci_set_drto(host);
|
||||
break;
|
||||
}
|
||||
|
@ -1844,8 +1878,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|||
* interrupt doesn't come within the given time.
|
||||
* in reading data state.
|
||||
*/
|
||||
if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) &&
|
||||
(host->dir_status == DW_MCI_RECV_STATUS))
|
||||
if (host->dir_status == DW_MCI_RECV_STATUS)
|
||||
dw_mci_set_drto(host);
|
||||
break;
|
||||
}
|
||||
|
@ -2411,8 +2444,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
|||
}
|
||||
|
||||
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);
|
||||
if (!host->data_status)
|
||||
|
@ -2474,7 +2506,8 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
|||
mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_TI |
|
||||
SDMMC_IDMAC_INT_RI);
|
||||
mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_NI);
|
||||
host->dma_ops->complete((void *)host);
|
||||
if (!test_bit(EVENT_DATA_ERROR, &host->pending_events))
|
||||
host->dma_ops->complete((void *)host);
|
||||
}
|
||||
} else {
|
||||
pending = mci_readl(host, IDSTS);
|
||||
|
@ -2482,7 +2515,8 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
|||
mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI |
|
||||
SDMMC_IDMAC_INT_RI);
|
||||
mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
|
||||
host->dma_ops->complete((void *)host);
|
||||
if (!test_bit(EVENT_DATA_ERROR, &host->pending_events))
|
||||
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)
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
if (dw_mci_get_cd(mmc))
|
||||
set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
||||
else
|
||||
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
||||
dw_mci_get_cd(mmc);
|
||||
|
||||
ret = mmc_add_host(mmc);
|
||||
if (ret)
|
||||
|
@ -3006,11 +3043,8 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
setup_timer(&host->cmd11_timer,
|
||||
dw_mci_cmd11_timer, (unsigned long)host);
|
||||
|
||||
host->quirks = host->pdata->quirks;
|
||||
|
||||
if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO)
|
||||
setup_timer(&host->dto_timer,
|
||||
dw_mci_dto_timer, (unsigned long)host);
|
||||
setup_timer(&host->dto_timer,
|
||||
dw_mci_dto_timer, (unsigned long)host);
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
spin_lock_init(&host->irq_lock);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#define _DW_MMC_H_
|
||||
|
||||
#define DW_MMC_240A 0x240a
|
||||
#define DW_MMC_280A 0x280a
|
||||
|
||||
#define SDMMC_CTRL 0x000
|
||||
#define SDMMC_PWREN 0x004
|
||||
|
@ -175,7 +176,10 @@
|
|||
/* Version ID register define */
|
||||
#define SDMMC_GET_VERID(x) ((x) & 0xFFFF)
|
||||
/* 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)
|
||||
/* All ctrl reset bits */
|
||||
#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
|
||||
* &struct dw_mci.
|
||||
* @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.
|
||||
* @id: Number of this slot.
|
||||
* @sdio_id: Number of this slot in the SDIO interrupt registers.
|
||||
|
@ -262,7 +263,6 @@ struct dw_mci_slot {
|
|||
struct list_head queue_node;
|
||||
|
||||
unsigned int clock;
|
||||
unsigned int __clk_old;
|
||||
|
||||
unsigned long flags;
|
||||
#define DW_MMC_CARD_PRESENT 0
|
||||
|
|
|
@ -287,6 +287,11 @@ struct msdc_save_para {
|
|||
u32 emmc50_cfg0;
|
||||
};
|
||||
|
||||
struct msdc_tune_para {
|
||||
u32 iocon;
|
||||
u32 pad_tune;
|
||||
};
|
||||
|
||||
struct msdc_delay_phase {
|
||||
u8 maxlen;
|
||||
u8 start;
|
||||
|
@ -326,7 +331,10 @@ struct msdc_host {
|
|||
unsigned char timing;
|
||||
bool vqmmc_enabled;
|
||||
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_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)
|
||||
|
@ -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);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -781,7 +801,13 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
|
|||
}
|
||||
|
||||
if (!sbc_error && !(events & MSDC_INT_CMDRDY)) {
|
||||
msdc_reset_hw(host);
|
||||
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);
|
||||
if (events & MSDC_INT_RSPCRCERR) {
|
||||
cmd->error = -EILSEQ;
|
||||
host->error |= REQ_CMD_EIO;
|
||||
|
@ -865,7 +891,11 @@ static void msdc_start_command(struct msdc_host *host,
|
|||
static void msdc_cmd_next(struct msdc_host *host,
|
||||
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);
|
||||
else if (cmd == mrq->sbc)
|
||||
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 */
|
||||
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!");
|
||||
}
|
||||
|
||||
|
@ -1296,7 +1328,7 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
|
|||
{
|
||||
struct msdc_host *host = mmc_priv(mmc);
|
||||
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;
|
||||
int cmd_err;
|
||||
int i;
|
||||
|
@ -1309,6 +1341,11 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
|
|||
if (!cmd_err)
|
||||
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);
|
||||
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)
|
||||
fall_delay |= (1 << i);
|
||||
}
|
||||
|
||||
final_rise_delay = get_best_delay(host, rise_delay);
|
||||
final_fall_delay = get_best_delay(host, fall_delay);
|
||||
|
||||
skip_fall:
|
||||
final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
|
||||
if (final_maxlen == final_rise_delay.maxlen) {
|
||||
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);
|
||||
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;
|
||||
int i, ret;
|
||||
|
||||
|
@ -1355,6 +1391,11 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
|
|||
if (!ret)
|
||||
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_W_DSPL);
|
||||
|
@ -1365,14 +1406,10 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
|
|||
if (!ret)
|
||||
fall_delay |= (1 << i);
|
||||
}
|
||||
|
||||
final_rise_delay = get_best_delay(host, rise_delay);
|
||||
final_fall_delay = get_best_delay(host, fall_delay);
|
||||
|
||||
skip_fall:
|
||||
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) {
|
||||
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_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");
|
||||
return ret;
|
||||
}
|
||||
ret = msdc_tune_data(mmc, opcode);
|
||||
if (ret == -EIO)
|
||||
dev_err(host->dev, "Tune data fail!\n");
|
||||
if (host->hs400_mode == false) {
|
||||
ret = msdc_tune_data(mmc, opcode);
|
||||
if (ret == -EIO)
|
||||
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;
|
||||
}
|
||||
|
||||
static int msdc_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct msdc_host *host = mmc_priv(mmc);
|
||||
host->hs400_mode = true;
|
||||
|
||||
writel(host->hs400_ds_delay, host->base + PAD_DS_TUNE);
|
||||
return 0;
|
||||
|
|
|
@ -1065,7 +1065,7 @@ static int mxcmci_probe(struct platform_device *pdev)
|
|||
|
||||
if (pdata)
|
||||
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"))
|
||||
dat3_card_detect = true;
|
||||
|
||||
|
|
|
@ -38,7 +38,6 @@ struct realtek_pci_sdmmc {
|
|||
struct rtsx_pcr *pcr;
|
||||
struct mmc_host *mmc;
|
||||
struct mmc_request *mrq;
|
||||
struct workqueue_struct *workq;
|
||||
#define SDMMC_WORKQ_NAME "rtsx_pci_sdmmc_workq"
|
||||
|
||||
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);
|
||||
|
||||
if (rsp_type == SD_RSP_TYPE_R1b)
|
||||
timeout = 3000;
|
||||
timeout = cmd->busy_timeout ? cmd->busy_timeout : 3000;
|
||||
|
||||
if (cmd->opcode == SD_SWITCH_VOLTAGE) {
|
||||
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))
|
||||
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,
|
||||
|
@ -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->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
|
||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
|
||||
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
|
||||
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_ERASE;
|
||||
mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
|
||||
mmc->max_current_330 = 400;
|
||||
mmc->max_current_180 = 800;
|
||||
|
@ -1404,11 +1403,6 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
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->mmc = mmc;
|
||||
host->pdev = pdev;
|
||||
|
@ -1462,9 +1456,7 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
|
|||
mmc_remove_host(mmc);
|
||||
host->eject = true;
|
||||
|
||||
flush_workqueue(host->workq);
|
||||
destroy_workqueue(host->workq);
|
||||
host->workq = NULL;
|
||||
flush_work(&host->work);
|
||||
|
||||
mmc_free_host(mmc);
|
||||
|
||||
|
|
|
@ -1365,7 +1365,7 @@ static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
|
|||
.no_detect = 1,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
|
||||
|
||||
static int s3cmci_cpufreq_transition(struct notifier_block *nb,
|
||||
unsigned long val, void *data)
|
||||
|
|
|
@ -74,7 +74,7 @@ struct s3cmci_host {
|
|||
struct dentry *debug_regs;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
|
||||
struct notifier_block freq_transition;
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -532,11 +532,6 @@ static int sdhci_acpi_resume(struct device *dev)
|
|||
return sdhci_resume_host(c->host);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define sdhci_acpi_suspend NULL
|
||||
#define sdhci_acpi_resume NULL
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -560,8 +555,7 @@ static int sdhci_acpi_runtime_resume(struct device *dev)
|
|||
#endif
|
||||
|
||||
static const struct dev_pm_ops sdhci_acpi_pm_ops = {
|
||||
.suspend = sdhci_acpi_suspend,
|
||||
.resume = sdhci_acpi_resume,
|
||||
SET_SYSTEM_SLEEP_PM_OPS(sdhci_acpi_suspend, sdhci_acpi_resume)
|
||||
SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend,
|
||||
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",
|
||||
(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",
|
||||
(mmc_gpio_get_cd(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;
|
||||
|
||||
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 (host->mmc->caps & MMC_CAP_NONREMOVABLE) {
|
||||
if (!mmc_card_is_removable(host->mmc)) {
|
||||
ret = sdhci_bcm_kona_sd_card_emulate(host, 1);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
|
@ -326,7 +326,7 @@ err_pltfm_free:
|
|||
static struct platform_driver sdhci_bcm_kona_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-kona",
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
.pm = &sdhci_pltfm_pmops,
|
||||
.of_match_table = sdhci_bcm_kona_of_match,
|
||||
},
|
||||
.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");
|
|
@ -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 = {
|
||||
.driver = {
|
||||
.name = "sdhci-cns3xxx",
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
.pm = &sdhci_pltfm_pmops,
|
||||
},
|
||||
.probe = sdhci_cns3xxx_probe,
|
||||
.remove = sdhci_pltfm_unregister,
|
||||
|
|
|
@ -117,7 +117,7 @@ MODULE_DEVICE_TABLE(of, sdhci_dove_of_match_table);
|
|||
static struct platform_driver sdhci_dove_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-dove",
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
.pm = &sdhci_pltfm_pmops,
|
||||
.of_match_table = sdhci_dove_of_match_table,
|
||||
},
|
||||
.probe = sdhci_dove_probe,
|
||||
|
|
|
@ -39,11 +39,13 @@
|
|||
#define ESDHC_VENDOR_SPEC_VSELECT (1 << 1)
|
||||
#define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8)
|
||||
#define ESDHC_WTMK_LVL 0x44
|
||||
#define ESDHC_WTMK_DEFAULT_VAL 0x10401040
|
||||
#define ESDHC_MIX_CTRL 0x48
|
||||
#define ESDHC_MIX_CTRL_DDREN (1 << 3)
|
||||
#define ESDHC_MIX_CTRL_AC23EN (1 << 7)
|
||||
#define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22)
|
||||
#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_HS400_EN (1 << 26)
|
||||
/* Bits 3 and 6 are not SDHCI standard definitions */
|
||||
|
@ -75,7 +77,8 @@
|
|||
#define ESDHC_TUNING_CTRL 0xcc
|
||||
#define ESDHC_STD_TUNING_EN (1 << 24)
|
||||
/* 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_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 */
|
||||
val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
|
||||
| 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)
|
||||
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);
|
||||
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
|
||||
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;
|
||||
else
|
||||
new_val |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
|
||||
} else {
|
||||
new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||
new_val &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
|
||||
}
|
||||
writel(new_val , host->ioaddr + ESDHC_MIX_CTRL);
|
||||
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
|
||||
u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
|
||||
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||
u32 tuning_ctrl;
|
||||
if (val & SDHCI_CTRL_TUNED_CLK) {
|
||||
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||
} else {
|
||||
v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||
m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
|
||||
m &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
|
||||
}
|
||||
|
||||
if (val & SDHCI_CTRL_EXEC_TUNING) {
|
||||
v |= ESDHC_MIX_CTRL_EXE_TUNE;
|
||||
m |= ESDHC_MIX_CTRL_FBCLK_SEL;
|
||||
tuning_ctrl = readl(host->ioaddr + ESDHC_TUNING_CTRL);
|
||||
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);
|
||||
m |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
|
||||
} else {
|
||||
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 &= ~ESDHC_MIX_CTRL_EXE_TUNE;
|
||||
reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
|
||||
writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
}
|
||||
|
||||
|
@ -838,6 +840,11 @@ static void esdhc_set_strobe_dll(struct sdhci_host *host)
|
|||
u32 v;
|
||||
|
||||
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 */
|
||||
writel(ESDHC_STROBE_DLL_CTRL_RESET,
|
||||
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;
|
||||
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
@ -957,6 +966,62 @@ static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
|
|||
.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
|
||||
static int
|
||||
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;
|
||||
|
||||
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))
|
||||
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))
|
||||
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)
|
||||
/* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
|
||||
host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK
|
||||
| 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)) {
|
||||
writel(0x10401040, host->ioaddr + ESDHC_WTMK_LVL);
|
||||
|
||||
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
||||
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))
|
||||
host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
|
||||
|
||||
/*
|
||||
* errata ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
|
||||
* TO1.1, it's harmless for MX6SL
|
||||
*/
|
||||
writel(readl(host->ioaddr + 0x6c) | BIT(7),
|
||||
host->ioaddr + 0x6c);
|
||||
/* clear tuning bits in case ROM has set it already */
|
||||
writel(0x0, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
writel(0x0, host->ioaddr + SDHCI_ACMD12_ERR);
|
||||
writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
|
||||
}
|
||||
|
||||
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
|
||||
sdhci_esdhc_ops.platform_execute_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)
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
|
||||
|
||||
|
@ -1212,6 +1248,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
if (err)
|
||||
goto disable_clk;
|
||||
|
||||
sdhci_esdhc_imx_hwinit(host);
|
||||
|
||||
err = sdhci_add_host(host);
|
||||
if (err)
|
||||
goto disable_clk;
|
||||
|
@ -1255,6 +1293,25 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
|
|||
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
|
||||
static int sdhci_esdhc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
|
@ -1291,7 +1348,7 @@ static int sdhci_esdhc_runtime_resume(struct device *dev)
|
|||
#endif
|
||||
|
||||
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,
|
||||
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 = {
|
||||
.pdata = &sdhci_iproc_pltfm_data,
|
||||
.caps = 0x05E90000,
|
||||
.caps1 = 0x00000064,
|
||||
.caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT)
|
||||
& 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,
|
||||
};
|
||||
|
||||
|
@ -251,7 +260,7 @@ static struct platform_driver sdhci_iproc_driver = {
|
|||
.driver = {
|
||||
.name = "sdhci-iproc",
|
||||
.of_match_table = sdhci_iproc_of_match,
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
.pm = &sdhci_pltfm_pmops,
|
||||
},
|
||||
.probe = sdhci_iproc_probe,
|
||||
.remove = sdhci_pltfm_unregister,
|
||||
|
|
|
@ -32,6 +32,21 @@
|
|||
#define CORE_POWER 0x0
|
||||
#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 CORE_DLL_LOCK BIT(7)
|
||||
#define CORE_DLL_EN BIT(16)
|
||||
|
@ -56,6 +71,7 @@
|
|||
struct sdhci_msm_host {
|
||||
struct platform_device *pdev;
|
||||
void __iomem *core_mem; /* MSM SDCC mapped address */
|
||||
int pwr_irq; /* power irq */
|
||||
struct clk *clk; /* main SD/MMC bus clock */
|
||||
struct clk *pclk; /* SDHC peripheral bus clock */
|
||||
struct clk *bus_clk; /* SDHC bus voter clock */
|
||||
|
@ -410,6 +426,85 @@ retry:
|
|||
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[] = {
|
||||
{ .compatible = "qcom,sdhci-msm-v4" },
|
||||
{},
|
||||
|
@ -422,11 +517,13 @@ static const struct sdhci_ops sdhci_msm_ops = {
|
|||
.reset = sdhci_reset,
|
||||
.set_clock = sdhci_set_clock,
|
||||
.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 = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||
SDHCI_QUIRK_NO_CARD_NO_RESET |
|
||||
SDHCI_QUIRK_SINGLE_POWER_WRITE,
|
||||
.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");
|
||||
if (IS_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;
|
||||
}
|
||||
|
||||
|
@ -545,6 +642,22 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
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);
|
||||
if (ret)
|
||||
goto clk_disable;
|
||||
|
|
|
@ -19,27 +19,136 @@
|
|||
* your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regmap.h>
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
#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_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT)
|
||||
#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
|
||||
* @clk_ahb: Pointer to the AHB clock
|
||||
* @phy: Pointer to the generic phy
|
||||
* @host: Pointer to the main SDHCI host structure.
|
||||
* @clk_ahb: Pointer to the AHB clock
|
||||
* @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_host *host;
|
||||
struct clk *clk_ahb;
|
||||
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)
|
||||
{
|
||||
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 = {
|
||||
.set_clock = sdhci_arasan_set_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,
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
const struct of_device_id *match;
|
||||
struct device_node *node;
|
||||
struct clk *clk_xin;
|
||||
struct sdhci_host *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);
|
||||
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");
|
||||
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);
|
||||
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);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "parsing dt failed (%u)\n", ret);
|
||||
goto clk_disable_all;
|
||||
goto unreg_clk;
|
||||
}
|
||||
|
||||
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)) {
|
||||
ret = PTR_ERR(sdhci_arasan->phy);
|
||||
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);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "phy_init err.\n");
|
||||
goto clk_disable_all;
|
||||
goto unreg_clk;
|
||||
}
|
||||
|
||||
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");
|
||||
goto err_phy_power;
|
||||
}
|
||||
|
||||
host->mmc_host_ops.hs400_enhanced_strobe =
|
||||
sdhci_arasan_hs400_enhanced_strobe;
|
||||
}
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
|
@ -259,6 +569,8 @@ err_add_host:
|
|||
err_phy_power:
|
||||
if (!IS_ERR(sdhci_arasan->phy))
|
||||
phy_exit(sdhci_arasan->phy);
|
||||
unreg_clk:
|
||||
sdhci_arasan_unregister_sdclk(&pdev->dev);
|
||||
clk_disable_all:
|
||||
clk_disable_unprepare(clk_xin);
|
||||
clk_dis_ahb:
|
||||
|
@ -281,6 +593,8 @@ static int sdhci_arasan_remove(struct platform_device *pdev)
|
|||
phy_exit(sdhci_arasan->phy);
|
||||
}
|
||||
|
||||
sdhci_arasan_unregister_sdclk(&pdev->dev);
|
||||
|
||||
ret = sdhci_pltfm_unregister(pdev);
|
||||
|
||||
clk_disable_unprepare(clk_ahb);
|
||||
|
@ -288,14 +602,6 @@ static int sdhci_arasan_remove(struct platform_device *pdev)
|
|||
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 = {
|
||||
.driver = {
|
||||
.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
|
||||
* 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) {
|
||||
host->mmc->caps |= MMC_CAP_NEEDS_POLL;
|
||||
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);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static u32 esdhc_proctl;
|
||||
static int esdhc_of_suspend(struct device *dev)
|
||||
{
|
||||
|
@ -504,16 +504,12 @@ static int esdhc_of_resume(struct device *dev)
|
|||
}
|
||||
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
|
||||
|
||||
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 = {
|
||||
.read_l = esdhc_be_readl,
|
||||
.read_w = esdhc_be_readw,
|
||||
|
@ -657,7 +653,7 @@ static struct platform_driver sdhci_esdhc_driver = {
|
|||
.driver = {
|
||||
.name = "sdhci-esdhc",
|
||||
.of_match_table = sdhci_esdhc_of_match,
|
||||
.pm = ESDHC_PMOPS,
|
||||
.pm = &esdhc_of_dev_pm_ops,
|
||||
},
|
||||
.probe = sdhci_esdhc_probe,
|
||||
.remove = sdhci_pltfm_unregister,
|
||||
|
|
|
@ -85,7 +85,7 @@ static struct platform_driver sdhci_hlwd_driver = {
|
|||
.driver = {
|
||||
.name = "sdhci-hlwd",
|
||||
.of_match_table = sdhci_hlwd_of_match,
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
.pm = &sdhci_pltfm_pmops,
|
||||
},
|
||||
.probe = sdhci_hlwd_probe,
|
||||
.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 INTEL_MRFL_EMMC_0 0
|
||||
#define INTEL_MRFL_EMMC_1 1
|
||||
#define INTEL_MRFLD_EMMC_0 0
|
||||
#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) &&
|
||||
(PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_1))
|
||||
if ((PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFLD_EMMC_0) &&
|
||||
(PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFLD_EMMC_1))
|
||||
/* SD support is not ready yet */
|
||||
return -ENODEV;
|
||||
|
||||
|
@ -435,12 +435,12 @@ static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot)
|
|||
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,
|
||||
.quirks2 = SDHCI_QUIRK2_BROKEN_HS200 |
|
||||
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
.allow_runtime_pm = true,
|
||||
.probe_slot = intel_mrfl_mmc_probe_slot,
|
||||
.probe_slot = intel_mrfld_mmc_probe_slot,
|
||||
};
|
||||
|
||||
/* O2Micro extra registers */
|
||||
|
@ -1104,10 +1104,10 @@ static const struct pci_device_id pci_ids[] = {
|
|||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTEL,
|
||||
.device = PCI_DEVICE_ID_INTEL_MRFL_MMC,
|
||||
.device = PCI_DEVICE_ID_INTEL_MRFLD_MMC,
|
||||
.subvendor = 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)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
|
@ -1496,7 +1495,9 @@ static int sdhci_pci_resume(struct device *dev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int sdhci_pci_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
|
@ -1562,17 +1563,10 @@ static int sdhci_pci_runtime_resume(struct device *dev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* CONFIG_PM */
|
||||
|
||||
#define sdhci_pci_suspend NULL
|
||||
#define sdhci_pci_resume NULL
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops sdhci_pci_pm_ops = {
|
||||
.suspend = sdhci_pci_suspend,
|
||||
.resume = sdhci_pci_resume,
|
||||
SET_SYSTEM_SLEEP_PM_OPS(sdhci_pci_suspend, sdhci_pci_resume)
|
||||
SET_RUNTIME_PM_OPS(sdhci_pci_runtime_suspend,
|
||||
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)
|
||||
{
|
||||
pm_runtime_put_noidle(dev);
|
||||
pm_runtime_allow(dev);
|
||||
pm_suspend_ignore_children(dev, 1);
|
||||
pm_runtime_set_autosuspend_delay(dev, 50);
|
||||
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)
|
||||
|
@ -1810,15 +1805,13 @@ static int sdhci_pci_probe(struct pci_dev *pdev,
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = pci_enable_device(pdev);
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
chip = kzalloc(sizeof(struct sdhci_pci_chip), GFP_KERNEL);
|
||||
if (!chip) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->pdev = pdev;
|
||||
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) {
|
||||
ret = chip->fixes->probe(chip);
|
||||
if (ret)
|
||||
goto free;
|
||||
return ret;
|
||||
}
|
||||
|
||||
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)) {
|
||||
for (i--; i >= 0; i--)
|
||||
sdhci_pci_remove_slot(chip->slots[i]);
|
||||
ret = PTR_ERR(slot);
|
||||
goto free;
|
||||
return PTR_ERR(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);
|
||||
|
||||
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)
|
||||
{
|
||||
int i;
|
||||
struct sdhci_pci_chip *chip;
|
||||
struct sdhci_pci_chip *chip = pci_get_drvdata(pdev);
|
||||
|
||||
chip = pci_get_drvdata(pdev);
|
||||
if (chip->allow_runtime_pm)
|
||||
sdhci_pci_runtime_pm_forbid(&pdev->dev);
|
||||
|
||||
if (chip) {
|
||||
if (chip->allow_runtime_pm)
|
||||
sdhci_pci_runtime_pm_forbid(&pdev->dev);
|
||||
|
||||
for (i = 0; i < chip->num_slots; i++)
|
||||
sdhci_pci_remove_slot(chip->slots[i]);
|
||||
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
kfree(chip);
|
||||
}
|
||||
|
||||
pci_disable_device(pdev);
|
||||
for (i = 0; i < chip->num_slots; i++)
|
||||
sdhci_pci_remove_slot(chip->slots[i]);
|
||||
}
|
||||
|
||||
static struct pci_driver sdhci_driver = {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#define PCI_DEVICE_ID_INTEL_BSW_EMMC 0x2294
|
||||
#define PCI_DEVICE_ID_INTEL_BSW_SDIO 0x2295
|
||||
#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_SDIO1 0x08fa
|
||||
#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);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int sdhci_pltfm_suspend(struct device *dev)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int sdhci_pltfm_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
|
||||
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);
|
||||
|
||||
return sdhci_resume_host(host);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_pltfm_resume);
|
||||
#endif
|
||||
|
||||
const struct dev_pm_ops sdhci_pltfm_pmops = {
|
||||
.suspend = sdhci_pltfm_suspend,
|
||||
.resume = sdhci_pltfm_resume,
|
||||
SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_pltfm_resume)
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(sdhci_pltfm_pmops);
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#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;
|
||||
#define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)
|
||||
#else
|
||||
#define SDHCI_PLTFM_PMOPS NULL
|
||||
#endif
|
||||
|
||||
#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */
|
||||
|
|
|
@ -252,7 +252,7 @@ static struct platform_driver sdhci_pxav2_driver = {
|
|||
.driver = {
|
||||
.name = "sdhci-pxav2",
|
||||
.of_match_table = of_match_ptr(sdhci_pxav2_of_match),
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
.pm = &sdhci_pltfm_pmops,
|
||||
},
|
||||
.probe = sdhci_pxav2_probe,
|
||||
.remove = sdhci_pxav2_remove,
|
||||
|
|
|
@ -583,24 +583,17 @@ static int sdhci_pxav3_runtime_resume(struct device *dev)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static const struct dev_pm_ops sdhci_pxav3_pmops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume)
|
||||
SET_RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend,
|
||||
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 = {
|
||||
.driver = {
|
||||
.name = "sdhci-pxav3",
|
||||
.of_match_table = of_match_ptr(sdhci_pxav3_of_match),
|
||||
.pm = SDHCI_PXAV3_PMOPS,
|
||||
.pm = &sdhci_pxav3_pmops,
|
||||
},
|
||||
.probe = sdhci_pxav3_probe,
|
||||
.remove = sdhci_pxav3_remove,
|
||||
|
|
|
@ -714,19 +714,12 @@ static int sdhci_s3c_runtime_resume(struct device *dev)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static const struct dev_pm_ops sdhci_s3c_pmops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(sdhci_s3c_suspend, sdhci_s3c_resume)
|
||||
SET_RUNTIME_PM_OPS(sdhci_s3c_runtime_suspend, sdhci_s3c_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
#define SDHCI_S3C_PMOPS (&sdhci_s3c_pmops)
|
||||
|
||||
#else
|
||||
#define SDHCI_S3C_PMOPS NULL
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212)
|
||||
static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = {
|
||||
.no_divider = true,
|
||||
|
@ -765,7 +758,7 @@ static struct platform_driver sdhci_s3c_driver = {
|
|||
.driver = {
|
||||
.name = "s3c-sdhci",
|
||||
.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);
|
||||
}
|
||||
#endif
|
||||
|
||||
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[] = {
|
||||
{ .compatible = "sirf,prima2-sdhc" },
|
||||
|
@ -274,9 +274,7 @@ static struct platform_driver sdhci_sirf_driver = {
|
|||
.driver = {
|
||||
.name = "sdhci-sirf",
|
||||
.of_match_table = sdhci_sirf_of_match,
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.pm = &sdhci_sirf_pm_ops,
|
||||
#endif
|
||||
},
|
||||
.probe = sdhci_sirf_probe,
|
||||
.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);
|
||||
|
||||
if (mhost->caps & MMC_CAP_NONREMOVABLE)
|
||||
if (!mmc_card_is_removable(mhost))
|
||||
cconf3 |= ST_MMC_CCONFIG_EMMC_SLOT_TYPE;
|
||||
else
|
||||
/* CARD _D ET_CTRL */
|
||||
|
|
|
@ -148,28 +148,37 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
|
|||
return;
|
||||
|
||||
misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
|
||||
/* Erratum: Enable SDHCI spec v3.00 support */
|
||||
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
|
||||
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
|
||||
/* Advertise UHS modes as supported by host */
|
||||
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
|
||||
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50;
|
||||
else
|
||||
misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR50;
|
||||
if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
|
||||
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
|
||||
else
|
||||
misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_DDR50;
|
||||
if (soc_data->nvquirks & NVQUIRK_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);
|
||||
|
||||
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 (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50)
|
||||
clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_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 */
|
||||
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
|
||||
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
|
||||
/* Advertise UHS modes as supported by host */
|
||||
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
|
||||
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50;
|
||||
if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
|
||||
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
|
||||
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104)
|
||||
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104;
|
||||
if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50)
|
||||
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);
|
||||
|
||||
if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
|
||||
|
@ -474,7 +483,7 @@ static struct platform_driver sdhci_tegra_driver = {
|
|||
.driver = {
|
||||
.name = "sdhci-tegra",
|
||||
.of_match_table = sdhci_tegra_dt_match,
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
.pm = &sdhci_pltfm_pmops,
|
||||
},
|
||||
.probe = sdhci_tegra_probe,
|
||||
.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_REMOVE 0x00000080
|
||||
#define SDHCI_INT_CARD_INT 0x00000100
|
||||
#define SDHCI_INT_RETUNE 0x00001000
|
||||
#define SDHCI_INT_ERROR 0x00008000
|
||||
#define SDHCI_INT_TIMEOUT 0x00010000
|
||||
#define SDHCI_INT_CRC 0x00020000
|
||||
|
@ -186,6 +187,7 @@
|
|||
#define SDHCI_CAN_DO_ADMA1 0x00100000
|
||||
#define SDHCI_CAN_DO_HISPD 0x00200000
|
||||
#define SDHCI_CAN_DO_SDMA 0x00400000
|
||||
#define SDHCI_CAN_DO_SUSPEND 0x00800000
|
||||
#define SDHCI_CAN_VDD_330 0x01000000
|
||||
#define SDHCI_CAN_VDD_300 0x02000000
|
||||
#define SDHCI_CAN_VDD_180 0x04000000
|
||||
|
@ -314,6 +316,9 @@ struct sdhci_adma2_64_desc {
|
|||
*/
|
||||
#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 {
|
||||
COOKIE_UNMAPPED,
|
||||
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_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
|
||||
#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 */
|
||||
|
||||
|
@ -460,12 +468,13 @@ struct sdhci_host {
|
|||
bool runtime_suspended; /* Host is runtime suspended */
|
||||
bool bus_on; /* Bus power prevents runtime suspend */
|
||||
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 *data_cmd; /* Current data command */
|
||||
struct mmc_data *data; /* Current data request */
|
||||
unsigned int data_early:1; /* Data finished before cmd */
|
||||
unsigned int busy_handle:1; /* Handling the order of Busy-end */
|
||||
|
||||
struct sg_mapping_iter sg_miter; /* SG state for PIO */
|
||||
unsigned int blocks; /* remaining PIO blocks */
|
||||
|
@ -486,9 +495,11 @@ struct sdhci_host {
|
|||
struct tasklet_struct finish_tasklet; /* Tasklet structures */
|
||||
|
||||
struct timer_list timer; /* Timer for timeouts */
|
||||
struct timer_list data_timer; /* Timer for data timeouts */
|
||||
|
||||
u32 caps; /* Alternative CAPABILITY_0 */
|
||||
u32 caps1; /* Alternative CAPABILITY_1 */
|
||||
u32 caps; /* CAPABILITY_0 */
|
||||
u32 caps1; /* CAPABILITY_1 */
|
||||
bool read_caps; /* Capability flags have been read */
|
||||
|
||||
unsigned int ocr_avail_sdio; /* OCR bit masks */
|
||||
unsigned int ocr_avail_sd;
|
||||
|
@ -508,6 +519,8 @@ struct sdhci_host {
|
|||
unsigned int tuning_count; /* Timer count for re-tuning */
|
||||
unsigned int tuning_mode; /* Re-tuning mode supported by host */
|
||||
#define SDHCI_TUNING_MODE_1 0
|
||||
#define SDHCI_TUNING_MODE_2 1
|
||||
#define SDHCI_TUNING_MODE_3 2
|
||||
|
||||
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_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 void sdhci_remove_host(struct sdhci_host *host, int dead);
|
||||
extern void sdhci_send_command(struct sdhci_host *host,
|
||||
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)
|
||||
{
|
||||
return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED);
|
||||
|
|
|
@ -222,7 +222,7 @@ static struct platform_driver sdhci_f_sdh30_driver = {
|
|||
.driver = {
|
||||
.name = "f_sdh30",
|
||||
.of_match_table = f_sdh30_dt_ids,
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
.pm = &sdhci_pltfm_pmops,
|
||||
},
|
||||
.probe = sdhci_f_sdh30_probe,
|
||||
.remove = sdhci_f_sdh30_remove,
|
||||
|
|
|
@ -574,7 +574,7 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
|
|||
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);
|
||||
for (timeout = 10000000; timeout; timeout--) {
|
||||
for (timeout = 10000; timeout; timeout--) {
|
||||
if (!(sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1)
|
||||
& STS1_CMDSEQ))
|
||||
break;
|
||||
|
@ -819,10 +819,12 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
|
|||
tmp |= CMD_SET_RTYP_NO;
|
||||
break;
|
||||
case MMC_RSP_R1:
|
||||
case MMC_RSP_R1B:
|
||||
case MMC_RSP_R3:
|
||||
tmp |= CMD_SET_RTYP_6B;
|
||||
break;
|
||||
case MMC_RSP_R1B:
|
||||
tmp |= CMD_SET_RBSY | CMD_SET_RTYP_6B;
|
||||
break;
|
||||
case MMC_RSP_R2:
|
||||
tmp |= CMD_SET_RTYP_17B;
|
||||
break;
|
||||
|
@ -830,17 +832,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
|
|||
dev_err(dev, "Unsupported response type.\n");
|
||||
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 */
|
||||
if (data) {
|
||||
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;
|
||||
u32 opc = cmd->opcode;
|
||||
u32 mask;
|
||||
u32 mask = 0;
|
||||
unsigned long flags;
|
||||
|
||||
switch (opc) {
|
||||
/* 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:
|
||||
if (cmd->flags & MMC_RSP_BUSY)
|
||||
mask = MASK_START_CMD | MASK_MRBSYE;
|
||||
break;
|
||||
default:
|
||||
else
|
||||
mask = MASK_START_CMD | MASK_MCRSPE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (host->ccs_enable)
|
||||
mask |= MASK_MCCSTO;
|
||||
|
@ -1009,22 +991,6 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
host->state = STATE_REQUEST;
|
||||
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;
|
||||
|
||||
sh_mmcif_start_cmd(host, mrq);
|
||||
|
@ -1488,6 +1454,9 @@ static int sh_mmcif_probe(struct platform_device *pdev)
|
|||
sh_mmcif_init_ocr(host);
|
||||
|
||||
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)
|
||||
mmc->caps |= pd->caps;
|
||||
mmc->max_segs = 32;
|
||||
|
|
|
@ -39,6 +39,12 @@
|
|||
|
||||
#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)
|
||||
|
||||
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
|
||||
*/
|
||||
switch (sd_ctrl_read16(host, CTL_VERSION)) {
|
||||
case 0x490C:
|
||||
case SDHI_VER_GEN2_SDR50:
|
||||
val = (width == 32) ? 0x0001 : 0x0000;
|
||||
break;
|
||||
case 0xCB0D:
|
||||
case SDHI_VER_GEN2_SDR104:
|
||||
val = (width == 32) ? 0x0000 : 0x0001;
|
||||
break;
|
||||
case 0xCC10: /* Gen3, SD only */
|
||||
case 0xCD10: /* Gen3, SD + MMC */
|
||||
case SDHI_VER_GEN3_SD:
|
||||
case SDHI_VER_GEN3_SDMMC:
|
||||
if (width == 64)
|
||||
val = 0x0000;
|
||||
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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
|
@ -1086,7 +1086,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
|
|||
|
||||
_host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||
|
||||
mmc->caps & MMC_CAP_NEEDS_POLL ||
|
||||
mmc->caps & MMC_CAP_NONREMOVABLE ||
|
||||
!mmc_card_is_removable(mmc) ||
|
||||
mmc->slot.cd_irq >= 0);
|
||||
|
||||
if (tmio_mmc_clk_enable(_host) < 0) {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -31,42 +32,64 @@
|
|||
((val) << (shift) | (mask) << ((shift) + 16))
|
||||
|
||||
/* Register definition */
|
||||
#define GRF_EMMCPHY_CON0 0x0
|
||||
#define GRF_EMMCPHY_CON1 0x4
|
||||
#define GRF_EMMCPHY_CON2 0x8
|
||||
#define GRF_EMMCPHY_CON3 0xc
|
||||
#define GRF_EMMCPHY_CON4 0x10
|
||||
#define GRF_EMMCPHY_CON5 0x14
|
||||
#define GRF_EMMCPHY_CON6 0x18
|
||||
#define GRF_EMMCPHY_STATUS 0x20
|
||||
#define GRF_EMMCPHY_CON0 0x0
|
||||
#define GRF_EMMCPHY_CON1 0x4
|
||||
#define GRF_EMMCPHY_CON2 0x8
|
||||
#define GRF_EMMCPHY_CON3 0xc
|
||||
#define GRF_EMMCPHY_CON4 0x10
|
||||
#define GRF_EMMCPHY_CON5 0x14
|
||||
#define GRF_EMMCPHY_CON6 0x18
|
||||
#define GRF_EMMCPHY_STATUS 0x20
|
||||
|
||||
#define PHYCTRL_PDB_MASK 0x1
|
||||
#define PHYCTRL_PDB_SHIFT 0x0
|
||||
#define PHYCTRL_PDB_PWR_ON 0x1
|
||||
#define PHYCTRL_PDB_PWR_OFF 0x0
|
||||
#define PHYCTRL_ENDLL_MASK 0x1
|
||||
#define PHYCTRL_ENDLL_SHIFT 0x1
|
||||
#define PHYCTRL_ENDLL_ENABLE 0x1
|
||||
#define PHYCTRL_ENDLL_DISABLE 0x0
|
||||
#define PHYCTRL_CALDONE_MASK 0x1
|
||||
#define PHYCTRL_CALDONE_SHIFT 0x6
|
||||
#define PHYCTRL_CALDONE_DONE 0x1
|
||||
#define PHYCTRL_CALDONE_GOING 0x0
|
||||
#define PHYCTRL_DLLRDY_MASK 0x1
|
||||
#define PHYCTRL_DLLRDY_SHIFT 0x5
|
||||
#define PHYCTRL_DLLRDY_DONE 0x1
|
||||
#define PHYCTRL_DLLRDY_GOING 0x0
|
||||
#define PHYCTRL_PDB_MASK 0x1
|
||||
#define PHYCTRL_PDB_SHIFT 0x0
|
||||
#define PHYCTRL_PDB_PWR_ON 0x1
|
||||
#define PHYCTRL_PDB_PWR_OFF 0x0
|
||||
#define PHYCTRL_ENDLL_MASK 0x1
|
||||
#define PHYCTRL_ENDLL_SHIFT 0x1
|
||||
#define PHYCTRL_ENDLL_ENABLE 0x1
|
||||
#define PHYCTRL_ENDLL_DISABLE 0x0
|
||||
#define PHYCTRL_CALDONE_MASK 0x1
|
||||
#define PHYCTRL_CALDONE_SHIFT 0x6
|
||||
#define PHYCTRL_CALDONE_DONE 0x1
|
||||
#define PHYCTRL_CALDONE_GOING 0x0
|
||||
#define PHYCTRL_DLLRDY_MASK 0x1
|
||||
#define PHYCTRL_DLLRDY_SHIFT 0x5
|
||||
#define PHYCTRL_DLLRDY_DONE 0x1
|
||||
#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 {
|
||||
unsigned int reg_offset;
|
||||
struct regmap *reg_base;
|
||||
struct clk *emmcclk;
|
||||
};
|
||||
|
||||
static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
|
||||
bool on_off)
|
||||
static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
|
||||
{
|
||||
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
|
||||
unsigned int caldone;
|
||||
unsigned int dllrdy;
|
||||
unsigned int freqsel = PHYCTRL_FREQSEL_200M;
|
||||
unsigned long rate;
|
||||
unsigned long timeout;
|
||||
|
||||
/*
|
||||
* 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)
|
||||
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
|
||||
* 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;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
|
||||
HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,
|
||||
PHYCTRL_ENDLL_MASK,
|
||||
PHYCTRL_ENDLL_SHIFT));
|
||||
|
||||
/*
|
||||
* After enable analog DLL circuits, we need extra 10.2us
|
||||
* for dll to be ready for work.
|
||||
* We turned on the DLL even though the rate was 0 because we the
|
||||
* 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);
|
||||
regmap_read(rk_phy->reg_base,
|
||||
rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
|
||||
&dllrdy);
|
||||
dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
|
||||
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,
|
||||
rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
|
||||
&dllrdy);
|
||||
dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
|
||||
if (dllrdy == PHYCTRL_DLLRDY_DONE)
|
||||
break;
|
||||
} while (!time_after(jiffies, timeout));
|
||||
|
||||
if (dllrdy != PHYCTRL_DLLRDY_DONE) {
|
||||
pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n");
|
||||
return -ETIMEDOUT;
|
||||
|
@ -135,33 +237,82 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
|
|||
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);
|
||||
int ret = 0;
|
||||
|
||||
/* Power down emmc phy analog blocks */
|
||||
ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF);
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* We purposely get the clock here and not in probe to avoid the
|
||||
* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 */
|
||||
ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON);
|
||||
}
|
||||
|
||||
static const struct phy_ops ops = {
|
||||
.init = rockchip_emmc_phy_init,
|
||||
.exit = rockchip_emmc_phy_exit,
|
||||
.power_on = rockchip_emmc_phy_power_on,
|
||||
.power_off = rockchip_emmc_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
|
|
|
@ -95,6 +95,7 @@ struct mmc_ext_csd {
|
|||
u8 raw_partition_support; /* 160 */
|
||||
u8 raw_rpmb_size_mult; /* 168 */
|
||||
u8 raw_erased_mem_count; /* 181 */
|
||||
u8 strobe_support; /* 184 */
|
||||
u8 raw_ext_csd_structure; /* 194 */
|
||||
u8 raw_card_type; /* 196 */
|
||||
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_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_BROKEN_HPI (1<<13) /* Disable broken HPI support */
|
||||
|
||||
|
||||
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 */
|
||||
u16 cis_vendor, cis_device;
|
||||
|
||||
/* for MMC cards */
|
||||
unsigned int ext_csd_rev;
|
||||
|
||||
void (*vendor_fixup)(struct mmc_card *card, int data);
|
||||
int data;
|
||||
};
|
||||
|
@ -361,11 +366,20 @@ struct mmc_fixup {
|
|||
#define CID_OEMID_ANY ((unsigned short) -1)
|
||||
#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 _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \
|
||||
_cis_vendor, _cis_device, \
|
||||
_fixup, _data) \
|
||||
_fixup, _data, _ext_csd_rev) \
|
||||
{ \
|
||||
.name = (_name), \
|
||||
.manfid = (_manfid), \
|
||||
|
@ -376,23 +390,30 @@ struct mmc_fixup {
|
|||
.cis_device = (_cis_device), \
|
||||
.vendor_fixup = (_fixup), \
|
||||
.data = (_data), \
|
||||
.ext_csd_rev = (_ext_csd_rev), \
|
||||
}
|
||||
|
||||
#define MMC_FIXUP_REV(_name, _manfid, _oemid, _rev_start, _rev_end, \
|
||||
_fixup, _data) \
|
||||
_fixup, _data, _ext_csd_rev) \
|
||||
_FIXUP_EXT(_name, _manfid, \
|
||||
_oemid, _rev_start, _rev_end, \
|
||||
SDIO_ANY_ID, SDIO_ANY_ID, \
|
||||
_fixup, _data) \
|
||||
_fixup, _data, _ext_csd_rev) \
|
||||
|
||||
#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) \
|
||||
_FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY, \
|
||||
CID_OEMID_ANY, 0, -1ull, \
|
||||
_vendor, _device, \
|
||||
_fixup, _data) \
|
||||
_fixup, _data, EXT_CSD_REV_ANY) \
|
||||
|
||||
#define cid_rev(hwrev, fwrev, year, month) \
|
||||
(((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;
|
||||
}
|
||||
|
||||
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_id(c) (dev_name(&(c)->dev))
|
||||
|
||||
|
|
|
@ -112,7 +112,6 @@ struct dw_mci_dma_slave {
|
|||
* @part_buf: Simple buffer for partial fifo reads/writes.
|
||||
* @push_data: Pointer to FIFO push 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.
|
||||
* @irq_flags: The flags 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 (*pull_data)(struct dw_mci *host, void *buf, int cnt);
|
||||
|
||||
/* Workaround flags */
|
||||
u32 quirks;
|
||||
|
||||
bool vqmmc_enabled;
|
||||
unsigned long irq_flags; /* IRQ flags */
|
||||
int irq;
|
||||
|
@ -242,17 +238,12 @@ struct dw_mci_dma_ops {
|
|||
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;
|
||||
|
||||
/* Board platform data */
|
||||
struct dw_mci_board {
|
||||
u32 num_slots;
|
||||
|
||||
u32 quirks; /* Workaround / Quirk flags */
|
||||
unsigned int bus_hz; /* Clock speed at the cclk_in pad */
|
||||
|
||||
u32 caps; /* Capabilities */
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <linux/mmc/core.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/pm.h>
|
||||
|
||||
struct mmc_ios {
|
||||
|
@ -77,6 +78,8 @@ struct mmc_ios {
|
|||
#define MMC_SET_DRIVER_TYPE_A 1
|
||||
#define MMC_SET_DRIVER_TYPE_C 2
|
||||
#define MMC_SET_DRIVER_TYPE_D 3
|
||||
|
||||
bool enhanced_strobe; /* hs400es selection */
|
||||
};
|
||||
|
||||
struct mmc_host_ops {
|
||||
|
@ -143,6 +146,9 @@ struct mmc_host_ops {
|
|||
|
||||
/* Prepare HS400 target operating frequency depending host driver */
|
||||
int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
|
||||
/* Prepare 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,
|
||||
unsigned int max_dtr, int host_drv,
|
||||
int card_drv, int *drv_type);
|
||||
|
@ -302,6 +308,9 @@ struct mmc_host {
|
|||
#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_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 */
|
||||
|
||||
|
@ -513,6 +522,11 @@ static inline bool mmc_card_hs400(struct mmc_card *card)
|
|||
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);
|
||||
|
||||
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_ERASED_MEM_CONT 181 /* RO */
|
||||
#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_POWER_CLASS 187 /* R/W */
|
||||
#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 (EXT_CSD_CARD_TYPE_HS400_1_8V | \
|
||||
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_4 1 /* Card is in 4 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_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_HS 1 /* High speed */
|
||||
|
|
|
@ -46,5 +46,6 @@ struct esdhc_platform_data {
|
|||
bool support_vsel;
|
||||
unsigned int delay_line;
|
||||
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 */
|
||||
|
|
Loading…
Reference in New Issue