mirror of
https://github.com/lkl/linux.git
synced 2025-12-19 16:13:19 +09:00
Merge tag 'mmc-v5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC and MEMSTICK updates from Ulf Hansson: "MMC core: - Update maintainer and URL for the mmc-utils - Set default label for slot-gpio in case of no con-id - Convert MMC card DT bindings to a schema - Add optional host specific tuning support for eMMC HS400 - Add error handling of add_disk() MMC host: - mtk-sd: Add host specific tuning support for eMMC HS400 - mtk-sd: Make DMA handling more robust - dw_mmc: Prevent hangs for some data writes - dw_mmc: Move away from using the ->init_card() callback - mxs-mmc: Manage the regulator in the error path and in ->remove() - sdhci-cadence: Add support for the Microchip MPFS variant - sdhci-esdhc-imx: Add support for the NXP S32G2 variant - sdhci-of-arasan: Add support for the Intel Thunder Bay variant - sdhci-omap: Prepare to support more SoCs - sdhci-omap: Add support for omap3 and omap4 variants - sdhci-omap: Add support for power management - sdhci-omap: Add support for system wakeups - sdhci-msm: Add support for the msm8226 variant - sdhci-sprd: Verify that the DLL locks according to spec MEMSTICK: - Add error handling of add_disk() - A couple of small fixes and improvements" * tag 'mmc-v5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (60 commits) docs: mmc: update maintainer name and URL mmc: dw_mmc: exynos: Fix spelling mistake "candiates" -> candidates MAINTAINERS: drop obsolete file pattern in SDHCI DRIVER section mmc: sdhci-esdhc-imx: add NXP S32G2 support dt-bindings: mmc: fsl-imx-esdhc: add NXP S32G2 support mmc: dw_mmc: Drop use of ->init_card() callback mmc: sdhci-omap: Fix build if CONFIG_PM_SLEEP is not set mmc: sdhci-omap: Remove forward declaration of sdhci_omap_context_save() memstick: r592: Fix a UAF bug when removing the driver mmc: mxs-mmc: disable regulator on error and in the remove function mmc: sdhci-omap: Configure optional wakeirq mmc: sdhci-omap: Allow SDIO card power off and enable aggressive PM mmc: sdhci-omap: Implement PM runtime functions mmc: sdhci-omap: Add omap_offset to support omap3 and earlier mmc: sdhci-omap: Handle voltages to add support omap4 dt-bindings: sdhci-omap: Update binding for legacy SoCs mmc: sdhci-pci: Remove dead code (rst_n_gpio et al) mmc: sdhci-pci: Remove dead code (cd_gpio, cd_irq et al) mmc: sdhci-pci: Remove dead code (struct sdhci_pci_data et al) mmc: sdhci: Remove unused prototype declaration in the header ...
This commit is contained in:
@@ -88,6 +88,12 @@ properties:
|
|||||||
description:
|
description:
|
||||||
For this device it is strongly suggested to include
|
For this device it is strongly suggested to include
|
||||||
arasan,soc-ctl-syscon.
|
arasan,soc-ctl-syscon.
|
||||||
|
- items:
|
||||||
|
- const: intel,thunderbay-sdhci-5.1 # Intel Thunder Bay eMMC PHY
|
||||||
|
- const: arasan,sdhci-5.1
|
||||||
|
description:
|
||||||
|
For this device it is strongly suggested to include
|
||||||
|
clock-output-names and '#clock-cells'.
|
||||||
|
|
||||||
reg:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
@@ -153,7 +159,6 @@ properties:
|
|||||||
The MIO bank number in which the command and data lines are configured.
|
The MIO bank number in which the command and data lines are configured.
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
clock-output-names: [ '#clock-cells' ]
|
|
||||||
'#clock-cells': [ clock-output-names ]
|
'#clock-cells': [ clock-output-names ]
|
||||||
|
|
||||||
required:
|
required:
|
||||||
@@ -301,3 +306,22 @@ examples:
|
|||||||
<&scmi_clk KEEM_BAY_PSS_SD0>;
|
<&scmi_clk KEEM_BAY_PSS_SD0>;
|
||||||
arasan,soc-ctl-syscon = <&sd0_phy_syscon>;
|
arasan,soc-ctl-syscon = <&sd0_phy_syscon>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
- |
|
||||||
|
#define EMMC_XIN_CLK
|
||||||
|
#define EMMC_AXI_CLK
|
||||||
|
#define TBH_PSS_EMMC_RST_N
|
||||||
|
mmc@80420000 {
|
||||||
|
compatible = "intel,thunderbay-sdhci-5.1", "arasan,sdhci-5.1";
|
||||||
|
interrupts = <GIC_SPI 714 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
reg = <0x80420000 0x400>;
|
||||||
|
clocks = <&scmi_clk EMMC_XIN_CLK>,
|
||||||
|
<&scmi_clk EMMC_AXI_CLK>;
|
||||||
|
clock-names = "clk_xin", "clk_ahb";
|
||||||
|
phys = <&emmc_phy>;
|
||||||
|
phy-names = "phy_arasan";
|
||||||
|
assigned-clocks = <&scmi_clk EMMC_XIN_CLK>;
|
||||||
|
clock-output-names = "emmc_cardclock";
|
||||||
|
resets = <&rst_pss1 TBH_PSS_EMMC_RST_N>;
|
||||||
|
#clock-cells = <0x0>;
|
||||||
|
};
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ properties:
|
|||||||
compatible:
|
compatible:
|
||||||
items:
|
items:
|
||||||
- enum:
|
- enum:
|
||||||
|
- microchip,mpfs-sd4hc
|
||||||
- socionext,uniphier-sd4hc
|
- socionext,uniphier-sd4hc
|
||||||
- const: cdns,sd4hc
|
- const: cdns,sd4hc
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ properties:
|
|||||||
- fsl,imx6ull-usdhc
|
- fsl,imx6ull-usdhc
|
||||||
- fsl,imx7d-usdhc
|
- fsl,imx7d-usdhc
|
||||||
- fsl,imx7ulp-usdhc
|
- fsl,imx7ulp-usdhc
|
||||||
|
- nxp,s32g2-usdhc
|
||||||
- items:
|
- items:
|
||||||
- enum:
|
- enum:
|
||||||
- fsl,imx8mm-usdhc
|
- fsl,imx8mm-usdhc
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
mmc-card / eMMC bindings
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
This documents describes the devicetree bindings for a mmc-host controller
|
|
||||||
child node describing a mmc-card / an eMMC, see "Use of Function subnodes"
|
|
||||||
in mmc.txt
|
|
||||||
|
|
||||||
Required properties:
|
|
||||||
-compatible : Must be "mmc-card"
|
|
||||||
-reg : Must be <0>
|
|
||||||
|
|
||||||
Optional properties:
|
|
||||||
-broken-hpi : Use this to indicate that the mmc-card has a broken hpi
|
|
||||||
implementation, and that hpi should not be used
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
&mmc2 {
|
|
||||||
pinctrl-names = "default";
|
|
||||||
pinctrl-0 = <&mmc2_pins_a>;
|
|
||||||
vmmc-supply = <®_vcc3v3>;
|
|
||||||
bus-width = <8>;
|
|
||||||
non-removable;
|
|
||||||
|
|
||||||
mmccard: mmccard@0 {
|
|
||||||
reg = <0>;
|
|
||||||
compatible = "mmc-card";
|
|
||||||
broken-hpi;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
48
Documentation/devicetree/bindings/mmc/mmc-card.yaml
Normal file
48
Documentation/devicetree/bindings/mmc/mmc-card.yaml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/mmc/mmc-card.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: MMC Card / eMMC Generic Device Tree Bindings
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Ulf Hansson <ulf.hansson@linaro.org>
|
||||||
|
|
||||||
|
description: |
|
||||||
|
This documents describes the devicetree bindings for a mmc-host controller
|
||||||
|
child node describing a mmc-card / an eMMC.
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
const: mmc-card
|
||||||
|
|
||||||
|
reg:
|
||||||
|
const: 0
|
||||||
|
|
||||||
|
broken-hpi:
|
||||||
|
$ref: /schemas/types.yaml#/definitions/flag
|
||||||
|
description:
|
||||||
|
Use this to indicate that the mmc-card has a broken hpi
|
||||||
|
implementation, and that hpi should not be used.
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
mmc {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
card@0 {
|
||||||
|
compatible = "mmc-card";
|
||||||
|
reg = <0>;
|
||||||
|
broken-hpi;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
...
|
||||||
@@ -333,12 +333,6 @@ patternProperties:
|
|||||||
subnode describes. A value of 0 denotes the memory SD
|
subnode describes. A value of 0 denotes the memory SD
|
||||||
function, values from 1 to 7 denote the SDIO functions.
|
function, values from 1 to 7 denote the SDIO functions.
|
||||||
|
|
||||||
broken-hpi:
|
|
||||||
$ref: /schemas/types.yaml#/definitions/flag
|
|
||||||
description:
|
|
||||||
Use this to indicate that the mmc-card has a broken hpi
|
|
||||||
implementation, and that hpi should not be used.
|
|
||||||
|
|
||||||
required:
|
required:
|
||||||
- reg
|
- reg
|
||||||
|
|
||||||
|
|||||||
@@ -119,6 +119,18 @@ properties:
|
|||||||
If present, HS400 command responses are sampled on rising edges.
|
If present, HS400 command responses are sampled on rising edges.
|
||||||
If not present, HS400 command responses are sampled on falling edges.
|
If not present, HS400 command responses are sampled on falling edges.
|
||||||
|
|
||||||
|
mediatek,hs400-ds-dly3:
|
||||||
|
$ref: /schemas/types.yaml#/definitions/uint32
|
||||||
|
description:
|
||||||
|
Gear of the third delay line for DS for input data latch in data
|
||||||
|
pad macro, there are 32 stages from 0 to 31.
|
||||||
|
For different corner IC, the time is different about one step, it is
|
||||||
|
about 100ps.
|
||||||
|
The value is confirmed by doing scan and calibration to find a best
|
||||||
|
value with corner IC and it is valid only for HS400 mode.
|
||||||
|
minimum: 0
|
||||||
|
maximum: 31
|
||||||
|
|
||||||
mediatek,latch-ck:
|
mediatek,latch-ck:
|
||||||
$ref: /schemas/types.yaml#/definitions/uint32
|
$ref: /schemas/types.yaml#/definitions/uint32
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ Required properties:
|
|||||||
string is added to support this change - "qcom,sdhci-msm-v5".
|
string is added to support this change - "qcom,sdhci-msm-v5".
|
||||||
full compatible strings with SoC and version:
|
full compatible strings with SoC and version:
|
||||||
"qcom,apq8084-sdhci", "qcom,sdhci-msm-v4"
|
"qcom,apq8084-sdhci", "qcom,sdhci-msm-v4"
|
||||||
|
"qcom,msm8226-sdhci", "qcom,sdhci-msm-v4"
|
||||||
"qcom,msm8974-sdhci", "qcom,sdhci-msm-v4"
|
"qcom,msm8974-sdhci", "qcom,sdhci-msm-v4"
|
||||||
"qcom,msm8916-sdhci", "qcom,sdhci-msm-v4"
|
"qcom,msm8916-sdhci", "qcom,sdhci-msm-v4"
|
||||||
"qcom,msm8992-sdhci", "qcom,sdhci-msm-v4"
|
"qcom,msm8992-sdhci", "qcom,sdhci-msm-v4"
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ Refer to mmc.txt for standard MMC bindings.
|
|||||||
For UHS devices which require tuning, the device tree should have a "cpu_thermal" node which maps to the appropriate thermal zone. This is used to get the temperature of the zone during tuning.
|
For UHS devices which require tuning, the device tree should have a "cpu_thermal" node which maps to the appropriate thermal zone. This is used to get the temperature of the zone during tuning.
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: Should be "ti,dra7-sdhci" for DRA7 and DRA72 controllers
|
- compatible: Should be "ti,omap2430-sdhci" for omap2430 controllers
|
||||||
|
Should be "ti,omap3-sdhci" for omap3 controllers
|
||||||
|
Should be "ti,omap4-sdhci" for omap4 and ti81 controllers
|
||||||
|
Should be "ti,omap5-sdhci" for omap5 controllers
|
||||||
|
Should be "ti,dra7-sdhci" for DRA7 and DRA72 controllers
|
||||||
Should be "ti,k2g-sdhci" for K2G
|
Should be "ti,k2g-sdhci" for K2G
|
||||||
Should be "ti,am335-sdhci" for am335x controllers
|
Should be "ti,am335-sdhci" for am335x controllers
|
||||||
Should be "ti,am437-sdhci" for am437x controllers
|
Should be "ti,am437-sdhci" for am437x controllers
|
||||||
@@ -24,6 +28,9 @@ Optional properties:
|
|||||||
DMA specifiers listed in dmas. The string naming is to be "tx"
|
DMA specifiers listed in dmas. The string naming is to be "tx"
|
||||||
and "rx" for TX and RX DMA requests, respectively.
|
and "rx" for TX and RX DMA requests, respectively.
|
||||||
|
|
||||||
|
Deprecated properties:
|
||||||
|
- ti,non-removable: Compatible with the generic non-removable property
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
mmc1: mmc@4809c000 {
|
mmc1: mmc@4809c000 {
|
||||||
compatible = "ti,dra7-sdhci";
|
compatible = "ti,dra7-sdhci";
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
MMC tools introduction
|
MMC tools introduction
|
||||||
======================
|
======================
|
||||||
|
|
||||||
There is one MMC test tools called mmc-utils, which is maintained by Chris Ball,
|
There is one MMC test tools called mmc-utils, which is maintained by Ulf Hansson,
|
||||||
you can find it at the below public git repository:
|
you can find it at the below public git repository:
|
||||||
|
|
||||||
https://git.kernel.org/cgit/linux/kernel/git/cjb/mmc-utils.git/
|
https://git.kernel.org/pub/scm/utils/mmc/mmc-utils.git
|
||||||
|
|
||||||
Functions
|
Functions
|
||||||
=========
|
=========
|
||||||
|
|||||||
@@ -16839,7 +16839,6 @@ M: Adrian Hunter <adrian.hunter@intel.com>
|
|||||||
L: linux-mmc@vger.kernel.org
|
L: linux-mmc@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/mmc/host/sdhci*
|
F: drivers/mmc/host/sdhci*
|
||||||
F: include/linux/mmc/sdhci*
|
|
||||||
|
|
||||||
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) MICROCHIP DRIVER
|
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) MICROCHIP DRIVER
|
||||||
M: Eugen Hristev <eugen.hristev@microchip.com>
|
M: Eugen Hristev <eugen.hristev@microchip.com>
|
||||||
|
|||||||
@@ -1736,7 +1736,7 @@ static int msb_init_card(struct memstick_dev *card)
|
|||||||
msb->pages_in_block = boot_block->attr.block_size * 2;
|
msb->pages_in_block = boot_block->attr.block_size * 2;
|
||||||
msb->block_size = msb->page_size * msb->pages_in_block;
|
msb->block_size = msb->page_size * msb->pages_in_block;
|
||||||
|
|
||||||
if (msb->page_size > PAGE_SIZE) {
|
if ((size_t)msb->page_size > PAGE_SIZE) {
|
||||||
/* this isn't supported by linux at all, anyway*/
|
/* this isn't supported by linux at all, anyway*/
|
||||||
dbg("device page %d size isn't supported", msb->page_size);
|
dbg("device page %d size isn't supported", msb->page_size);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@@ -2156,10 +2156,14 @@ static int msb_init_disk(struct memstick_dev *card)
|
|||||||
set_disk_ro(msb->disk, 1);
|
set_disk_ro(msb->disk, 1);
|
||||||
|
|
||||||
msb_start(card);
|
msb_start(card);
|
||||||
device_add_disk(&card->dev, msb->disk, NULL);
|
rc = device_add_disk(&card->dev, msb->disk, NULL);
|
||||||
|
if (rc)
|
||||||
|
goto out_cleanup_disk;
|
||||||
dbg("Disk added");
|
dbg("Disk added");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out_cleanup_disk:
|
||||||
|
blk_cleanup_disk(msb->disk);
|
||||||
out_free_tag_set:
|
out_free_tag_set:
|
||||||
blk_mq_free_tag_set(&msb->tag_set);
|
blk_mq_free_tag_set(&msb->tag_set);
|
||||||
out_release_id:
|
out_release_id:
|
||||||
|
|||||||
@@ -1239,10 +1239,14 @@ static int mspro_block_init_disk(struct memstick_dev *card)
|
|||||||
set_capacity(msb->disk, capacity);
|
set_capacity(msb->disk, capacity);
|
||||||
dev_dbg(&card->dev, "capacity set %ld\n", capacity);
|
dev_dbg(&card->dev, "capacity set %ld\n", capacity);
|
||||||
|
|
||||||
device_add_disk(&card->dev, msb->disk, NULL);
|
rc = device_add_disk(&card->dev, msb->disk, NULL);
|
||||||
|
if (rc)
|
||||||
|
goto out_cleanup_disk;
|
||||||
msb->active = 1;
|
msb->active = 1;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out_cleanup_disk:
|
||||||
|
blk_cleanup_disk(msb->disk);
|
||||||
out_free_tag_set:
|
out_free_tag_set:
|
||||||
blk_mq_free_tag_set(&msb->tag_set);
|
blk_mq_free_tag_set(&msb->tag_set);
|
||||||
out_release_id:
|
out_release_id:
|
||||||
|
|||||||
@@ -882,7 +882,7 @@ static struct memstick_host *jmb38x_ms_alloc_host(struct jmb38x_ms *jm, int cnt)
|
|||||||
|
|
||||||
iounmap(host->addr);
|
iounmap(host->addr);
|
||||||
err_out_free:
|
err_out_free:
|
||||||
kfree(msh);
|
memstick_free_host(msh);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -927,8 +927,7 @@ static int jmb38x_ms_probe(struct pci_dev *pdev,
|
|||||||
goto err_out_int;
|
goto err_out_int;
|
||||||
}
|
}
|
||||||
|
|
||||||
jm = kzalloc(sizeof(struct jmb38x_ms)
|
jm = kzalloc(struct_size(jm, hosts, cnt), GFP_KERNEL);
|
||||||
+ cnt * sizeof(struct memstick_host *), GFP_KERNEL);
|
|
||||||
if (!jm) {
|
if (!jm) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto err_out_int;
|
goto err_out_int;
|
||||||
|
|||||||
@@ -838,15 +838,15 @@ static void r592_remove(struct pci_dev *pdev)
|
|||||||
}
|
}
|
||||||
memstick_remove_host(dev->host);
|
memstick_remove_host(dev->host);
|
||||||
|
|
||||||
|
if (dev->dummy_dma_page)
|
||||||
|
dma_free_coherent(&pdev->dev, PAGE_SIZE, dev->dummy_dma_page,
|
||||||
|
dev->dummy_dma_page_physical_address);
|
||||||
|
|
||||||
free_irq(dev->irq, dev);
|
free_irq(dev->irq, dev);
|
||||||
iounmap(dev->mmio);
|
iounmap(dev->mmio);
|
||||||
pci_release_regions(pdev);
|
pci_release_regions(pdev);
|
||||||
pci_disable_device(pdev);
|
pci_disable_device(pdev);
|
||||||
memstick_free_host(dev->host);
|
memstick_free_host(dev->host);
|
||||||
|
|
||||||
if (dev->dummy_dma_page)
|
|
||||||
dma_free_coherent(&pdev->dev, PAGE_SIZE, dev->dummy_dma_page,
|
|
||||||
dev->dummy_dma_page_physical_address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
|||||||
@@ -2442,9 +2442,14 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
|
|||||||
/* used in ->open, must be set before add_disk: */
|
/* used in ->open, must be set before add_disk: */
|
||||||
if (area_type == MMC_BLK_DATA_AREA_MAIN)
|
if (area_type == MMC_BLK_DATA_AREA_MAIN)
|
||||||
dev_set_drvdata(&card->dev, md);
|
dev_set_drvdata(&card->dev, md);
|
||||||
device_add_disk(md->parent, md->disk, mmc_disk_attr_groups);
|
ret = device_add_disk(md->parent, md->disk, mmc_disk_attr_groups);
|
||||||
|
if (ret)
|
||||||
|
goto err_cleanup_queue;
|
||||||
return md;
|
return md;
|
||||||
|
|
||||||
|
err_cleanup_queue:
|
||||||
|
blk_cleanup_queue(md->disk->queue);
|
||||||
|
blk_mq_free_tag_set(&md->queue.tag_set);
|
||||||
err_kfree:
|
err_kfree:
|
||||||
kfree(md);
|
kfree(md);
|
||||||
out:
|
out:
|
||||||
|
|||||||
@@ -1224,6 +1224,14 @@ static int mmc_select_hs400(struct mmc_card *card)
|
|||||||
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
|
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
|
||||||
mmc_set_bus_speed(card);
|
mmc_set_bus_speed(card);
|
||||||
|
|
||||||
|
if (host->ops->execute_hs400_tuning) {
|
||||||
|
mmc_retune_disable(host);
|
||||||
|
err = host->ops->execute_hs400_tuning(host, card);
|
||||||
|
mmc_retune_enable(host);
|
||||||
|
if (err)
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
if (host->ops->hs400_complete)
|
if (host->ops->hs400_complete)
|
||||||
host->ops->hs400_complete(host);
|
host->ops->hs400_complete(host);
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
|
|||||||
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
|
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
|
||||||
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
|
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
|
||||||
int mmc_can_ext_csd(struct mmc_card *card);
|
int mmc_can_ext_csd(struct mmc_card *card);
|
||||||
int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd);
|
|
||||||
int mmc_switch_status(struct mmc_card *card, bool crc_err_fatal);
|
int mmc_switch_status(struct mmc_card *card, bool crc_err_fatal);
|
||||||
bool mmc_prepare_busy_cmd(struct mmc_host *host, struct mmc_command *cmd,
|
bool mmc_prepare_busy_cmd(struct mmc_host *host, struct mmc_command *cmd,
|
||||||
unsigned int timeout_ms);
|
unsigned int timeout_ms);
|
||||||
|
|||||||
@@ -39,24 +39,24 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
|
|||||||
|
|
||||||
int mmc_gpio_alloc(struct mmc_host *host)
|
int mmc_gpio_alloc(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
struct mmc_gpio *ctx = devm_kzalloc(host->parent,
|
const char *devname = dev_name(host->parent);
|
||||||
sizeof(*ctx), GFP_KERNEL);
|
struct mmc_gpio *ctx;
|
||||||
|
|
||||||
if (ctx) {
|
ctx = devm_kzalloc(host->parent, sizeof(*ctx), GFP_KERNEL);
|
||||||
ctx->cd_debounce_delay_ms = 200;
|
if (!ctx)
|
||||||
ctx->cd_label = devm_kasprintf(host->parent, GFP_KERNEL,
|
return -ENOMEM;
|
||||||
"%s cd", dev_name(host->parent));
|
|
||||||
if (!ctx->cd_label)
|
|
||||||
return -ENOMEM;
|
|
||||||
ctx->ro_label = devm_kasprintf(host->parent, GFP_KERNEL,
|
|
||||||
"%s ro", dev_name(host->parent));
|
|
||||||
if (!ctx->ro_label)
|
|
||||||
return -ENOMEM;
|
|
||||||
host->slot.handler_priv = ctx;
|
|
||||||
host->slot.cd_irq = -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx ? 0 : -ENOMEM;
|
ctx->cd_debounce_delay_ms = 200;
|
||||||
|
ctx->cd_label = devm_kasprintf(host->parent, GFP_KERNEL, "%s cd", devname);
|
||||||
|
if (!ctx->cd_label)
|
||||||
|
return -ENOMEM;
|
||||||
|
ctx->ro_label = devm_kasprintf(host->parent, GFP_KERNEL, "%s ro", devname);
|
||||||
|
if (!ctx->ro_label)
|
||||||
|
return -ENOMEM;
|
||||||
|
host->slot.handler_priv = ctx;
|
||||||
|
host->slot.cd_irq = -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mmc_gpio_get_ro(struct mmc_host *host)
|
int mmc_gpio_get_ro(struct mmc_host *host)
|
||||||
@@ -178,6 +178,10 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
|
|||||||
if (IS_ERR(desc))
|
if (IS_ERR(desc))
|
||||||
return PTR_ERR(desc);
|
return PTR_ERR(desc);
|
||||||
|
|
||||||
|
/* Update default label if no con_id provided */
|
||||||
|
if (!con_id)
|
||||||
|
gpiod_set_consumer_name(desc, ctx->cd_label);
|
||||||
|
|
||||||
if (debounce) {
|
if (debounce) {
|
||||||
ret = gpiod_set_debounce(desc, debounce);
|
ret = gpiod_set_debounce(desc, debounce);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@@ -226,6 +230,10 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
|||||||
if (IS_ERR(desc))
|
if (IS_ERR(desc))
|
||||||
return PTR_ERR(desc);
|
return PTR_ERR(desc);
|
||||||
|
|
||||||
|
/* Update default label if no con_id provided */
|
||||||
|
if (!con_id)
|
||||||
|
gpiod_set_consumer_name(desc, ctx->ro_label);
|
||||||
|
|
||||||
if (debounce) {
|
if (debounce) {
|
||||||
ret = gpiod_set_debounce(desc, debounce);
|
ret = gpiod_set_debounce(desc, debounce);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
|||||||
@@ -315,15 +315,17 @@ config MMC_SDHCI_TEGRA
|
|||||||
If unsure, say N.
|
If unsure, say N.
|
||||||
|
|
||||||
config MMC_SDHCI_S3C
|
config MMC_SDHCI_S3C
|
||||||
tristate "SDHCI support on Samsung S3C SoC"
|
tristate "SDHCI support on Samsung S3C/S5P/Exynos SoC"
|
||||||
depends on MMC_SDHCI
|
depends on MMC_SDHCI
|
||||||
depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
|
depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||||
often referrered to as the HSMMC block in some of the Samsung S3C
|
often referrered to as the HSMMC block in some of the Samsung S3C
|
||||||
range of SoC.
|
(S3C2416, S3C2443, S3C6410), S5Pv210 and Exynos (Exynso4210,
|
||||||
|
Exynos4412) SoCs.
|
||||||
|
|
||||||
If you have a controller with this interface, say Y or M here.
|
If you have a controller with this interface (thereforeyou build for
|
||||||
|
such Samsung SoC), say Y or M here.
|
||||||
|
|
||||||
If unsure, say N.
|
If unsure, say N.
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ obj-$(CONFIG_MMC_SDHCI) += sdhci.o
|
|||||||
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
|
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
|
||||||
sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \
|
sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \
|
||||||
sdhci-pci-dwc-mshc.o sdhci-pci-gli.o
|
sdhci-pci-dwc-mshc.o sdhci-pci-gli.o
|
||||||
obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o
|
|
||||||
obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
|
obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
|
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o
|
obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o
|
||||||
|
|||||||
@@ -902,8 +902,8 @@ static bool cqhci_timeout(struct mmc_host *mmc, struct mmc_request *mrq,
|
|||||||
spin_unlock_irqrestore(&cq_host->lock, flags);
|
spin_unlock_irqrestore(&cq_host->lock, flags);
|
||||||
|
|
||||||
if (timed_out) {
|
if (timed_out) {
|
||||||
pr_err("%s: cqhci: timeout for tag %d\n",
|
pr_err("%s: cqhci: timeout for tag %d, qcnt %d\n",
|
||||||
mmc_hostname(mmc), tag);
|
mmc_hostname(mmc), tag, cq_host->qcnt);
|
||||||
cqhci_dumpregs(cq_host);
|
cqhci_dumpregs(cq_host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -442,14 +442,14 @@ static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
|
|||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates)
|
static s8 dw_mci_exynos_get_best_clksmpl(u8 candidates)
|
||||||
{
|
{
|
||||||
const u8 iter = 8;
|
const u8 iter = 8;
|
||||||
u8 __c;
|
u8 __c;
|
||||||
s8 i, loc = -1;
|
s8 i, loc = -1;
|
||||||
|
|
||||||
for (i = 0; i < iter; i++) {
|
for (i = 0; i < iter; i++) {
|
||||||
__c = ror8(candiates, i);
|
__c = ror8(candidates, i);
|
||||||
if ((__c & 0xc7) == 0xc7) {
|
if ((__c & 0xc7) == 0xc7) {
|
||||||
loc = i;
|
loc = i;
|
||||||
goto out;
|
goto out;
|
||||||
@@ -457,7 +457,7 @@ static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < iter; i++) {
|
for (i = 0; i < iter; i++) {
|
||||||
__c = ror8(candiates, i);
|
__c = ror8(candidates, i);
|
||||||
if ((__c & 0x83) == 0x83) {
|
if ((__c & 0x83) == 0x83) {
|
||||||
loc = i;
|
loc = i;
|
||||||
goto out;
|
goto out;
|
||||||
@@ -466,11 +466,11 @@ static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If there is no cadiates value, then it needs to return -EIO.
|
* If there is no cadiates value, then it needs to return -EIO.
|
||||||
* If there are candiates values and don't find bset clk sample value,
|
* If there are candidates values and don't find bset clk sample value,
|
||||||
* then use a first candiates clock sample value.
|
* then use a first candidates clock sample value.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < iter; i++) {
|
for (i = 0; i < iter; i++) {
|
||||||
__c = ror8(candiates, i);
|
__c = ror8(candidates, i);
|
||||||
if ((__c & 0x1) == 0x1) {
|
if ((__c & 0x1) == 0x1) {
|
||||||
loc = i;
|
loc = i;
|
||||||
goto out;
|
goto out;
|
||||||
@@ -485,7 +485,7 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
|
|||||||
struct dw_mci *host = slot->host;
|
struct dw_mci *host = slot->host;
|
||||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||||
struct mmc_host *mmc = slot->mmc;
|
struct mmc_host *mmc = slot->mmc;
|
||||||
u8 start_smpl, smpl, candiates = 0;
|
u8 start_smpl, smpl, candidates = 0;
|
||||||
s8 found;
|
s8 found;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
@@ -496,18 +496,18 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
|
|||||||
smpl = dw_mci_exynos_move_next_clksmpl(host);
|
smpl = dw_mci_exynos_move_next_clksmpl(host);
|
||||||
|
|
||||||
if (!mmc_send_tuning(mmc, opcode, NULL))
|
if (!mmc_send_tuning(mmc, opcode, NULL))
|
||||||
candiates |= (1 << smpl);
|
candidates |= (1 << smpl);
|
||||||
|
|
||||||
} while (start_smpl != smpl);
|
} while (start_smpl != smpl);
|
||||||
|
|
||||||
found = dw_mci_exynos_get_best_clksmpl(candiates);
|
found = dw_mci_exynos_get_best_clksmpl(candidates);
|
||||||
if (found >= 0) {
|
if (found >= 0) {
|
||||||
dw_mci_exynos_set_clksmpl(host, found);
|
dw_mci_exynos_set_clksmpl(host, found);
|
||||||
priv->tuned_sample = found;
|
priv->tuned_sample = found;
|
||||||
} else {
|
} else {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
dev_warn(&mmc->class_dev,
|
dev_warn(&mmc->class_dev,
|
||||||
"There is no candiates value about clksmpl!\n");
|
"There is no candidates value about clksmpl!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -1611,37 +1611,32 @@ static void dw_mci_hw_reset(struct mmc_host *mmc)
|
|||||||
usleep_range(200, 300);
|
usleep_range(200, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dw_mci_init_card(struct mmc_host *mmc, struct mmc_card *card)
|
static void dw_mci_prepare_sdio_irq(struct dw_mci_slot *slot, bool prepare)
|
||||||
{
|
{
|
||||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
|
||||||
struct dw_mci *host = slot->host;
|
struct dw_mci *host = slot->host;
|
||||||
|
const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id;
|
||||||
|
u32 clk_en_a_old;
|
||||||
|
u32 clk_en_a;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Low power mode will stop the card clock when idle. According to the
|
* Low power mode will stop the card clock when idle. According to the
|
||||||
* description of the CLKENA register we should disable low power mode
|
* description of the CLKENA register we should disable low power mode
|
||||||
* for SDIO cards if we need SDIO interrupts to work.
|
* for SDIO cards if we need SDIO interrupts to work.
|
||||||
*/
|
*/
|
||||||
if (mmc->caps & MMC_CAP_SDIO_IRQ) {
|
|
||||||
const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id;
|
|
||||||
u32 clk_en_a_old;
|
|
||||||
u32 clk_en_a;
|
|
||||||
|
|
||||||
clk_en_a_old = mci_readl(host, CLKENA);
|
clk_en_a_old = mci_readl(host, CLKENA);
|
||||||
|
if (prepare) {
|
||||||
|
set_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
|
||||||
|
clk_en_a = clk_en_a_old & ~clken_low_pwr;
|
||||||
|
} else {
|
||||||
|
clear_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
|
||||||
|
clk_en_a = clk_en_a_old | clken_low_pwr;
|
||||||
|
}
|
||||||
|
|
||||||
if (card->type == MMC_TYPE_SDIO ||
|
if (clk_en_a != clk_en_a_old) {
|
||||||
card->type == MMC_TYPE_SD_COMBO) {
|
mci_writel(host, CLKENA, clk_en_a);
|
||||||
set_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
|
mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT,
|
||||||
clk_en_a = clk_en_a_old & ~clken_low_pwr;
|
0);
|
||||||
} else {
|
|
||||||
clear_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
|
|
||||||
clk_en_a = clk_en_a_old | clken_low_pwr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clk_en_a != clk_en_a_old) {
|
|
||||||
mci_writel(host, CLKENA, clk_en_a);
|
|
||||||
mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
|
|
||||||
SDMMC_CMD_PRV_DAT_WAIT, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1669,6 +1664,7 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
|
|||||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||||
struct dw_mci *host = slot->host;
|
struct dw_mci *host = slot->host;
|
||||||
|
|
||||||
|
dw_mci_prepare_sdio_irq(slot, enb);
|
||||||
__dw_mci_enable_sdio_irq(slot, enb);
|
__dw_mci_enable_sdio_irq(slot, enb);
|
||||||
|
|
||||||
/* Avoid runtime suspending the device when SDIO IRQ is enabled */
|
/* Avoid runtime suspending the device when SDIO IRQ is enabled */
|
||||||
@@ -1790,7 +1786,6 @@ static const struct mmc_host_ops dw_mci_ops = {
|
|||||||
.execute_tuning = dw_mci_execute_tuning,
|
.execute_tuning = dw_mci_execute_tuning,
|
||||||
.card_busy = dw_mci_card_busy,
|
.card_busy = dw_mci_card_busy,
|
||||||
.start_signal_voltage_switch = dw_mci_switch_voltage,
|
.start_signal_voltage_switch = dw_mci_switch_voltage,
|
||||||
.init_card = dw_mci_init_card,
|
|
||||||
.prepare_hs400_tuning = dw_mci_prepare_hs400_tuning,
|
.prepare_hs400_tuning = dw_mci_prepare_hs400_tuning,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2086,7 +2081,8 @@ static void dw_mci_tasklet_func(struct tasklet_struct *t)
|
|||||||
* delayed. Allowing the transfer to take place
|
* delayed. Allowing the transfer to take place
|
||||||
* avoids races and keeps things simple.
|
* avoids races and keeps things simple.
|
||||||
*/
|
*/
|
||||||
if (err != -ETIMEDOUT) {
|
if (err != -ETIMEDOUT &&
|
||||||
|
host->dir_status == DW_MCI_RECV_STATUS) {
|
||||||
state = STATE_SENDING_DATA;
|
state = STATE_SENDING_DATA;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1394,6 +1394,10 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
|
|||||||
} else if (host->variant->busy_timeout && busy_resp &&
|
} else if (host->variant->busy_timeout && busy_resp &&
|
||||||
status & MCI_DATATIMEOUT) {
|
status & MCI_DATATIMEOUT) {
|
||||||
cmd->error = -ETIMEDOUT;
|
cmd->error = -ETIMEDOUT;
|
||||||
|
/*
|
||||||
|
* This will wake up mmci_irq_thread() which will issue
|
||||||
|
* a hardware reset of the MMCI block.
|
||||||
|
*/
|
||||||
host->irq_action = IRQ_WAKE_THREAD;
|
host->irq_action = IRQ_WAKE_THREAD;
|
||||||
} else {
|
} else {
|
||||||
cmd->resp[0] = readl(base + MMCIRESPONSE0);
|
cmd->resp[0] = readl(base + MMCIRESPONSE0);
|
||||||
|
|||||||
@@ -566,37 +566,37 @@ static int moxart_probe(struct platform_device *pdev)
|
|||||||
if (!mmc) {
|
if (!mmc) {
|
||||||
dev_err(dev, "mmc_alloc_host failed\n");
|
dev_err(dev, "mmc_alloc_host failed\n");
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto out_mmc;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = of_address_to_resource(node, 0, &res_mmc);
|
ret = of_address_to_resource(node, 0, &res_mmc);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "of_address_to_resource failed\n");
|
dev_err(dev, "of_address_to_resource failed\n");
|
||||||
goto out;
|
goto out_mmc;
|
||||||
}
|
}
|
||||||
|
|
||||||
irq = irq_of_parse_and_map(node, 0);
|
irq = irq_of_parse_and_map(node, 0);
|
||||||
if (irq <= 0) {
|
if (irq <= 0) {
|
||||||
dev_err(dev, "irq_of_parse_and_map failed\n");
|
dev_err(dev, "irq_of_parse_and_map failed\n");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out_mmc;
|
||||||
}
|
}
|
||||||
|
|
||||||
clk = devm_clk_get(dev, NULL);
|
clk = devm_clk_get(dev, NULL);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk)) {
|
||||||
ret = PTR_ERR(clk);
|
ret = PTR_ERR(clk);
|
||||||
goto out;
|
goto out_mmc;
|
||||||
}
|
}
|
||||||
|
|
||||||
reg_mmc = devm_ioremap_resource(dev, &res_mmc);
|
reg_mmc = devm_ioremap_resource(dev, &res_mmc);
|
||||||
if (IS_ERR(reg_mmc)) {
|
if (IS_ERR(reg_mmc)) {
|
||||||
ret = PTR_ERR(reg_mmc);
|
ret = PTR_ERR(reg_mmc);
|
||||||
goto out;
|
goto out_mmc;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = mmc_of_parse(mmc);
|
ret = mmc_of_parse(mmc);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out_mmc;
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
host = mmc_priv(mmc);
|
||||||
host->mmc = mmc;
|
host->mmc = mmc;
|
||||||
@@ -621,6 +621,14 @@ static int moxart_probe(struct platform_device *pdev)
|
|||||||
ret = -EPROBE_DEFER;
|
ret = -EPROBE_DEFER;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
if (!IS_ERR(host->dma_chan_tx)) {
|
||||||
|
dma_release_channel(host->dma_chan_tx);
|
||||||
|
host->dma_chan_tx = NULL;
|
||||||
|
}
|
||||||
|
if (!IS_ERR(host->dma_chan_rx)) {
|
||||||
|
dma_release_channel(host->dma_chan_rx);
|
||||||
|
host->dma_chan_rx = NULL;
|
||||||
|
}
|
||||||
dev_dbg(dev, "PIO mode transfer enabled\n");
|
dev_dbg(dev, "PIO mode transfer enabled\n");
|
||||||
host->have_dma = false;
|
host->have_dma = false;
|
||||||
} else {
|
} else {
|
||||||
@@ -675,6 +683,11 @@ static int moxart_probe(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
if (!IS_ERR_OR_NULL(host->dma_chan_tx))
|
||||||
|
dma_release_channel(host->dma_chan_tx);
|
||||||
|
if (!IS_ERR_OR_NULL(host->dma_chan_rx))
|
||||||
|
dma_release_channel(host->dma_chan_rx);
|
||||||
|
out_mmc:
|
||||||
if (mmc)
|
if (mmc)
|
||||||
mmc_free_host(mmc);
|
mmc_free_host(mmc);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -687,9 +700,9 @@ static int moxart_remove(struct platform_device *pdev)
|
|||||||
|
|
||||||
dev_set_drvdata(&pdev->dev, NULL);
|
dev_set_drvdata(&pdev->dev, NULL);
|
||||||
|
|
||||||
if (!IS_ERR(host->dma_chan_tx))
|
if (!IS_ERR_OR_NULL(host->dma_chan_tx))
|
||||||
dma_release_channel(host->dma_chan_tx);
|
dma_release_channel(host->dma_chan_tx);
|
||||||
if (!IS_ERR(host->dma_chan_rx))
|
if (!IS_ERR_OR_NULL(host->dma_chan_rx))
|
||||||
dma_release_channel(host->dma_chan_rx);
|
dma_release_channel(host->dma_chan_rx);
|
||||||
mmc_remove_host(mmc);
|
mmc_remove_host(mmc);
|
||||||
mmc_free_host(mmc);
|
mmc_free_host(mmc);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
@@ -258,6 +259,7 @@
|
|||||||
#define MSDC_PAD_TUNE_RD_SEL (0x1 << 13) /* RW */
|
#define MSDC_PAD_TUNE_RD_SEL (0x1 << 13) /* RW */
|
||||||
#define MSDC_PAD_TUNE_CMD_SEL (0x1 << 21) /* RW */
|
#define MSDC_PAD_TUNE_CMD_SEL (0x1 << 21) /* RW */
|
||||||
|
|
||||||
|
#define PAD_DS_TUNE_DLY_SEL (0x1 << 0) /* RW */
|
||||||
#define PAD_DS_TUNE_DLY1 (0x1f << 2) /* RW */
|
#define PAD_DS_TUNE_DLY1 (0x1f << 2) /* RW */
|
||||||
#define PAD_DS_TUNE_DLY2 (0x1f << 7) /* RW */
|
#define PAD_DS_TUNE_DLY2 (0x1f << 7) /* RW */
|
||||||
#define PAD_DS_TUNE_DLY3 (0x1f << 12) /* RW */
|
#define PAD_DS_TUNE_DLY3 (0x1f << 12) /* RW */
|
||||||
@@ -301,6 +303,11 @@
|
|||||||
#define PAD_CMD_RD_RXDLY_SEL (0x1 << 11) /* RW */
|
#define PAD_CMD_RD_RXDLY_SEL (0x1 << 11) /* RW */
|
||||||
#define PAD_CMD_TX_DLY (0x1f << 12) /* RW */
|
#define PAD_CMD_TX_DLY (0x1f << 12) /* RW */
|
||||||
|
|
||||||
|
/* EMMC50_PAD_DS_TUNE mask */
|
||||||
|
#define PAD_DS_DLY_SEL (0x1 << 16) /* RW */
|
||||||
|
#define PAD_DS_DLY1 (0x1f << 10) /* RW */
|
||||||
|
#define PAD_DS_DLY3 (0x1f << 0) /* RW */
|
||||||
|
|
||||||
#define REQ_CMD_EIO (0x1 << 0)
|
#define REQ_CMD_EIO (0x1 << 0)
|
||||||
#define REQ_CMD_TMO (0x1 << 1)
|
#define REQ_CMD_TMO (0x1 << 1)
|
||||||
#define REQ_DAT_ERR (0x1 << 2)
|
#define REQ_DAT_ERR (0x1 << 2)
|
||||||
@@ -448,11 +455,13 @@ struct msdc_host {
|
|||||||
bool vqmmc_enabled;
|
bool vqmmc_enabled;
|
||||||
u32 latch_ck;
|
u32 latch_ck;
|
||||||
u32 hs400_ds_delay;
|
u32 hs400_ds_delay;
|
||||||
|
u32 hs400_ds_dly3;
|
||||||
u32 hs200_cmd_int_delay; /* cmd internal delay for HS200/SDR104 */
|
u32 hs200_cmd_int_delay; /* cmd internal delay for HS200/SDR104 */
|
||||||
u32 hs400_cmd_int_delay; /* cmd internal delay for HS400 */
|
u32 hs400_cmd_int_delay; /* cmd internal delay for HS400 */
|
||||||
bool hs400_cmd_resp_sel_rising;
|
bool hs400_cmd_resp_sel_rising;
|
||||||
/* cmd response sample selection for HS400 */
|
/* cmd response sample selection for HS400 */
|
||||||
bool hs400_mode; /* current eMMC will run at hs400 mode */
|
bool hs400_mode; /* current eMMC will run at hs400 mode */
|
||||||
|
bool hs400_tuning; /* hs400 mode online tuning */
|
||||||
bool internal_cd; /* Use internal card-detect logic */
|
bool internal_cd; /* Use internal card-detect logic */
|
||||||
bool cqhci; /* support eMMC hw cmdq */
|
bool cqhci; /* support eMMC hw cmdq */
|
||||||
struct msdc_save_para save_para; /* used when gate HCLK */
|
struct msdc_save_para save_para; /* used when gate HCLK */
|
||||||
@@ -961,7 +970,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 msdc_cmd_find_resp(struct msdc_host *host,
|
static inline u32 msdc_cmd_find_resp(struct msdc_host *host,
|
||||||
struct mmc_request *mrq, struct mmc_command *cmd)
|
struct mmc_command *cmd)
|
||||||
{
|
{
|
||||||
u32 resp;
|
u32 resp;
|
||||||
|
|
||||||
@@ -997,7 +1006,7 @@ static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host,
|
|||||||
* stop << 14 | rw << 13 | dtype << 11 | rsptyp << 7 | brk << 6 | opcode
|
* stop << 14 | rw << 13 | dtype << 11 | rsptyp << 7 | brk << 6 | opcode
|
||||||
*/
|
*/
|
||||||
u32 opcode = cmd->opcode;
|
u32 opcode = cmd->opcode;
|
||||||
u32 resp = msdc_cmd_find_resp(host, mrq, cmd);
|
u32 resp = msdc_cmd_find_resp(host, cmd);
|
||||||
u32 rawcmd = (opcode & 0x3f) | ((resp & 0x7) << 7);
|
u32 rawcmd = (opcode & 0x3f) | ((resp & 0x7) << 7);
|
||||||
|
|
||||||
host->cmd_rsp = resp;
|
host->cmd_rsp = resp;
|
||||||
@@ -1043,8 +1052,8 @@ static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host,
|
|||||||
return rawcmd;
|
return rawcmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void msdc_start_data(struct msdc_host *host, struct mmc_request *mrq,
|
static void msdc_start_data(struct msdc_host *host, struct mmc_command *cmd,
|
||||||
struct mmc_command *cmd, struct mmc_data *data)
|
struct mmc_data *data)
|
||||||
{
|
{
|
||||||
bool read;
|
bool read;
|
||||||
|
|
||||||
@@ -1112,8 +1121,7 @@ static void msdc_recheck_sdio_irq(struct msdc_host *host)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void msdc_track_cmd_data(struct msdc_host *host,
|
static void msdc_track_cmd_data(struct msdc_host *host, struct mmc_command *cmd)
|
||||||
struct mmc_command *cmd, struct mmc_data *data)
|
|
||||||
{
|
{
|
||||||
if (host->error)
|
if (host->error)
|
||||||
dev_dbg(host->dev, "%s: cmd=%d arg=%08X; host->error=0x%08X\n",
|
dev_dbg(host->dev, "%s: cmd=%d arg=%08X; host->error=0x%08X\n",
|
||||||
@@ -1134,7 +1142,7 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
|
|||||||
host->mrq = NULL;
|
host->mrq = NULL;
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
msdc_track_cmd_data(host, mrq->cmd, mrq->data);
|
msdc_track_cmd_data(host, mrq->cmd);
|
||||||
if (mrq->data)
|
if (mrq->data)
|
||||||
msdc_unprepare_data(host, mrq->data);
|
msdc_unprepare_data(host, mrq->data);
|
||||||
if (host->error)
|
if (host->error)
|
||||||
@@ -1190,7 +1198,8 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
|
|||||||
if (!sbc_error && !(events & MSDC_INT_CMDRDY)) {
|
if (!sbc_error && !(events & MSDC_INT_CMDRDY)) {
|
||||||
if (events & MSDC_INT_CMDTMO ||
|
if (events & MSDC_INT_CMDTMO ||
|
||||||
(cmd->opcode != MMC_SEND_TUNING_BLOCK &&
|
(cmd->opcode != MMC_SEND_TUNING_BLOCK &&
|
||||||
cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200))
|
cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200 &&
|
||||||
|
!host->hs400_tuning))
|
||||||
/*
|
/*
|
||||||
* should not clear fifo/interrupt as the tune data
|
* should not clear fifo/interrupt as the tune data
|
||||||
* may have alreay come when cmd19/cmd21 gets response
|
* may have alreay come when cmd19/cmd21 gets response
|
||||||
@@ -1287,7 +1296,8 @@ static void msdc_cmd_next(struct msdc_host *host,
|
|||||||
if ((cmd->error &&
|
if ((cmd->error &&
|
||||||
!(cmd->error == -EILSEQ &&
|
!(cmd->error == -EILSEQ &&
|
||||||
(cmd->opcode == MMC_SEND_TUNING_BLOCK ||
|
(cmd->opcode == MMC_SEND_TUNING_BLOCK ||
|
||||||
cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))) ||
|
cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200 ||
|
||||||
|
host->hs400_tuning))) ||
|
||||||
(mrq->sbc && mrq->sbc->error))
|
(mrq->sbc && mrq->sbc->error))
|
||||||
msdc_request_done(host, mrq);
|
msdc_request_done(host, mrq);
|
||||||
else if (cmd == mrq->sbc)
|
else if (cmd == mrq->sbc)
|
||||||
@@ -1295,7 +1305,7 @@ static void msdc_cmd_next(struct msdc_host *host,
|
|||||||
else if (!cmd->data)
|
else if (!cmd->data)
|
||||||
msdc_request_done(host, mrq);
|
msdc_request_done(host, mrq);
|
||||||
else
|
else
|
||||||
msdc_start_data(host, mrq, cmd, cmd->data);
|
msdc_start_data(host, cmd, cmd->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
@@ -2251,6 +2261,67 @@ static int msdc_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int msdc_execute_hs400_tuning(struct mmc_host *mmc, struct mmc_card *card)
|
||||||
|
{
|
||||||
|
struct msdc_host *host = mmc_priv(mmc);
|
||||||
|
struct msdc_delay_phase dly1_delay;
|
||||||
|
u32 val, result_dly1 = 0;
|
||||||
|
u8 *ext_csd;
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
if (host->top_base) {
|
||||||
|
sdr_set_bits(host->top_base + EMMC50_PAD_DS_TUNE,
|
||||||
|
PAD_DS_DLY_SEL);
|
||||||
|
if (host->hs400_ds_dly3)
|
||||||
|
sdr_set_field(host->top_base + EMMC50_PAD_DS_TUNE,
|
||||||
|
PAD_DS_DLY3, host->hs400_ds_dly3);
|
||||||
|
} else {
|
||||||
|
sdr_set_bits(host->base + PAD_DS_TUNE, PAD_DS_TUNE_DLY_SEL);
|
||||||
|
if (host->hs400_ds_dly3)
|
||||||
|
sdr_set_field(host->base + PAD_DS_TUNE,
|
||||||
|
PAD_DS_TUNE_DLY3, host->hs400_ds_dly3);
|
||||||
|
}
|
||||||
|
|
||||||
|
host->hs400_tuning = true;
|
||||||
|
for (i = 0; i < PAD_DELAY_MAX; i++) {
|
||||||
|
if (host->top_base)
|
||||||
|
sdr_set_field(host->top_base + EMMC50_PAD_DS_TUNE,
|
||||||
|
PAD_DS_DLY1, i);
|
||||||
|
else
|
||||||
|
sdr_set_field(host->base + PAD_DS_TUNE,
|
||||||
|
PAD_DS_TUNE_DLY1, i);
|
||||||
|
ret = mmc_get_ext_csd(card, &ext_csd);
|
||||||
|
if (!ret)
|
||||||
|
result_dly1 |= (1 << i);
|
||||||
|
}
|
||||||
|
host->hs400_tuning = false;
|
||||||
|
|
||||||
|
dly1_delay = get_best_delay(host, result_dly1);
|
||||||
|
if (dly1_delay.maxlen == 0) {
|
||||||
|
dev_err(host->dev, "Failed to get DLY1 delay!\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (host->top_base)
|
||||||
|
sdr_set_field(host->top_base + EMMC50_PAD_DS_TUNE,
|
||||||
|
PAD_DS_DLY1, dly1_delay.final_phase);
|
||||||
|
else
|
||||||
|
sdr_set_field(host->base + PAD_DS_TUNE,
|
||||||
|
PAD_DS_TUNE_DLY1, dly1_delay.final_phase);
|
||||||
|
|
||||||
|
if (host->top_base)
|
||||||
|
val = readl(host->top_base + EMMC50_PAD_DS_TUNE);
|
||||||
|
else
|
||||||
|
val = readl(host->base + PAD_DS_TUNE);
|
||||||
|
|
||||||
|
dev_info(host->dev, "Fianl PAD_DS_TUNE: 0x%x\n", val);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
dev_err(host->dev, "Failed to tuning DS pin delay!\n");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
static void msdc_hw_reset(struct mmc_host *mmc)
|
static void msdc_hw_reset(struct mmc_host *mmc)
|
||||||
{
|
{
|
||||||
struct msdc_host *host = mmc_priv(mmc);
|
struct msdc_host *host = mmc_priv(mmc);
|
||||||
@@ -2330,6 +2401,7 @@ static void msdc_cqe_enable(struct mmc_host *mmc)
|
|||||||
static void msdc_cqe_disable(struct mmc_host *mmc, bool recovery)
|
static void msdc_cqe_disable(struct mmc_host *mmc, bool recovery)
|
||||||
{
|
{
|
||||||
struct msdc_host *host = mmc_priv(mmc);
|
struct msdc_host *host = mmc_priv(mmc);
|
||||||
|
unsigned int val = 0;
|
||||||
|
|
||||||
/* disable cmdq irq */
|
/* disable cmdq irq */
|
||||||
sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INT_CMDQ);
|
sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INT_CMDQ);
|
||||||
@@ -2339,6 +2411,9 @@ static void msdc_cqe_disable(struct mmc_host *mmc, bool recovery)
|
|||||||
if (recovery) {
|
if (recovery) {
|
||||||
sdr_set_field(host->base + MSDC_DMA_CTRL,
|
sdr_set_field(host->base + MSDC_DMA_CTRL,
|
||||||
MSDC_DMA_CTRL_STOP, 1);
|
MSDC_DMA_CTRL_STOP, 1);
|
||||||
|
if (WARN_ON(readl_poll_timeout(host->base + MSDC_DMA_CFG, val,
|
||||||
|
!(val & MSDC_DMA_CFG_STS), 1, 3000)))
|
||||||
|
return;
|
||||||
msdc_reset_hw(host);
|
msdc_reset_hw(host);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2377,6 +2452,7 @@ static const struct mmc_host_ops mt_msdc_ops = {
|
|||||||
.card_busy = msdc_card_busy,
|
.card_busy = msdc_card_busy,
|
||||||
.execute_tuning = msdc_execute_tuning,
|
.execute_tuning = msdc_execute_tuning,
|
||||||
.prepare_hs400_tuning = msdc_prepare_hs400_tuning,
|
.prepare_hs400_tuning = msdc_prepare_hs400_tuning,
|
||||||
|
.execute_hs400_tuning = msdc_execute_hs400_tuning,
|
||||||
.hw_reset = msdc_hw_reset,
|
.hw_reset = msdc_hw_reset,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2396,6 +2472,9 @@ static void msdc_of_property_parse(struct platform_device *pdev,
|
|||||||
of_property_read_u32(pdev->dev.of_node, "hs400-ds-delay",
|
of_property_read_u32(pdev->dev.of_node, "hs400-ds-delay",
|
||||||
&host->hs400_ds_delay);
|
&host->hs400_ds_delay);
|
||||||
|
|
||||||
|
of_property_read_u32(pdev->dev.of_node, "mediatek,hs400-ds-dly3",
|
||||||
|
&host->hs400_ds_dly3);
|
||||||
|
|
||||||
of_property_read_u32(pdev->dev.of_node, "mediatek,hs200-cmd-int-delay",
|
of_property_read_u32(pdev->dev.of_node, "mediatek,hs200-cmd-int-delay",
|
||||||
&host->hs200_cmd_int_delay);
|
&host->hs200_cmd_int_delay);
|
||||||
|
|
||||||
|
|||||||
@@ -552,6 +552,11 @@ static const struct of_device_id mxs_mmc_dt_ids[] = {
|
|||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, mxs_mmc_dt_ids);
|
MODULE_DEVICE_TABLE(of, mxs_mmc_dt_ids);
|
||||||
|
|
||||||
|
static void mxs_mmc_regulator_disable(void *regulator)
|
||||||
|
{
|
||||||
|
regulator_disable(regulator);
|
||||||
|
}
|
||||||
|
|
||||||
static int mxs_mmc_probe(struct platform_device *pdev)
|
static int mxs_mmc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
@@ -591,6 +596,11 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||||||
"Failed to enable vmmc regulator: %d\n", ret);
|
"Failed to enable vmmc regulator: %d\n", ret);
|
||||||
goto out_mmc_free;
|
goto out_mmc_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = devm_add_action_or_reset(&pdev->dev, mxs_mmc_regulator_disable,
|
||||||
|
reg_vmmc);
|
||||||
|
if (ret)
|
||||||
|
goto out_mmc_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssp->clk = devm_clk_get(&pdev->dev, NULL);
|
ssp->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
|||||||
@@ -702,11 +702,6 @@ static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)
|
static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -1515,7 +1510,7 @@ static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
|
|||||||
* REVISIT: should be moved to sdio core and made more
|
* REVISIT: should be moved to sdio core and made more
|
||||||
* general e.g. by expanding the DT bindings of child nodes
|
* general e.g. by expanding the DT bindings of child nodes
|
||||||
* to provide a mechanism to provide this information:
|
* to provide a mechanism to provide this information:
|
||||||
* Documentation/devicetree/bindings/mmc/mmc-card.txt
|
* Documentation/devicetree/bindings/mmc/mmc-card.yaml
|
||||||
*/
|
*/
|
||||||
|
|
||||||
np = of_get_compatible_child(np, "ti,wl1251");
|
np = of_get_compatible_child(np, "ti,wl1251");
|
||||||
@@ -2086,6 +2081,7 @@ static int omap_hsmmc_resume(struct device *dev)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
static int omap_hsmmc_runtime_suspend(struct device *dev)
|
static int omap_hsmmc_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct omap_hsmmc_host *host;
|
struct omap_hsmmc_host *host;
|
||||||
@@ -2153,11 +2149,11 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
|
|||||||
spin_unlock_irqrestore(&host->irq_lock, flags);
|
spin_unlock_irqrestore(&host->irq_lock, flags);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static const struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
|
static const struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
|
||||||
SET_SYSTEM_SLEEP_PM_OPS(omap_hsmmc_suspend, omap_hsmmc_resume)
|
SET_SYSTEM_SLEEP_PM_OPS(omap_hsmmc_suspend, omap_hsmmc_resume)
|
||||||
.runtime_suspend = omap_hsmmc_runtime_suspend,
|
SET_RUNTIME_PM_OPS(omap_hsmmc_runtime_suspend, omap_hsmmc_runtime_resume, NULL)
|
||||||
.runtime_resume = omap_hsmmc_runtime_resume,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_driver omap_hsmmc_driver = {
|
static struct platform_driver omap_hsmmc_driver = {
|
||||||
|
|||||||
@@ -362,23 +362,11 @@ static inline bool sdhci_acpi_no_fixup_child_power(struct acpi_device *adev)
|
|||||||
static int bxt_get_cd(struct mmc_host *mmc)
|
static int bxt_get_cd(struct mmc_host *mmc)
|
||||||
{
|
{
|
||||||
int gpio_cd = mmc_gpio_get_cd(mmc);
|
int gpio_cd = mmc_gpio_get_cd(mmc);
|
||||||
struct sdhci_host *host = mmc_priv(mmc);
|
|
||||||
unsigned long flags;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (!gpio_cd)
|
if (!gpio_cd)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
return sdhci_get_cd_nogpio(mmc);
|
||||||
|
|
||||||
if (host->flags & SDHCI_DEVICE_DEAD)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
ret = !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT);
|
|
||||||
out:
|
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int intel_probe_slot(struct platform_device *pdev, struct acpi_device *adev)
|
static int intel_probe_slot(struct platform_device *pdev, struct acpi_device *adev)
|
||||||
|
|||||||
@@ -196,6 +196,9 @@
|
|||||||
*/
|
*/
|
||||||
#define ESDHC_FLAG_BROKEN_AUTO_CMD23 BIT(16)
|
#define ESDHC_FLAG_BROKEN_AUTO_CMD23 BIT(16)
|
||||||
|
|
||||||
|
/* ERR004536 is not applicable for the IP */
|
||||||
|
#define ESDHC_FLAG_SKIP_ERR004536 BIT(17)
|
||||||
|
|
||||||
enum wp_types {
|
enum wp_types {
|
||||||
ESDHC_WP_NONE, /* no WP, neither controller nor gpio */
|
ESDHC_WP_NONE, /* no WP, neither controller nor gpio */
|
||||||
ESDHC_WP_CONTROLLER, /* mmc controller internal WP */
|
ESDHC_WP_CONTROLLER, /* mmc controller internal WP */
|
||||||
@@ -289,6 +292,13 @@ static const struct esdhc_soc_data usdhc_imx7d_data = {
|
|||||||
| ESDHC_FLAG_BROKEN_AUTO_CMD23,
|
| ESDHC_FLAG_BROKEN_AUTO_CMD23,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct esdhc_soc_data usdhc_s32g2_data = {
|
||||||
|
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
|
||||||
|
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
||||||
|
| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
|
||||||
|
| ESDHC_FLAG_SKIP_ERR004536,
|
||||||
|
};
|
||||||
|
|
||||||
static struct esdhc_soc_data usdhc_imx7ulp_data = {
|
static struct esdhc_soc_data usdhc_imx7ulp_data = {
|
||||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
||||||
@@ -347,6 +357,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = {
|
|||||||
{ .compatible = "fsl,imx7ulp-usdhc", .data = &usdhc_imx7ulp_data, },
|
{ .compatible = "fsl,imx7ulp-usdhc", .data = &usdhc_imx7ulp_data, },
|
||||||
{ .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, },
|
{ .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, },
|
||||||
{ .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx8mm_data, },
|
{ .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx8mm_data, },
|
||||||
|
{ .compatible = "nxp,s32g2-usdhc", .data = &usdhc_s32g2_data, },
|
||||||
{ /* sentinel */ }
|
{ /* sentinel */ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
|
MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
|
||||||
@@ -1375,8 +1386,10 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
|
|||||||
* erratum ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
|
* erratum ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
|
||||||
* TO1.1, it's harmless for MX6SL
|
* TO1.1, it's harmless for MX6SL
|
||||||
*/
|
*/
|
||||||
writel(readl(host->ioaddr + 0x6c) & ~BIT(7),
|
if (!(imx_data->socdata->flags & ESDHC_FLAG_SKIP_ERR004536)) {
|
||||||
host->ioaddr + 0x6c);
|
writel(readl(host->ioaddr + 0x6c) & ~BIT(7),
|
||||||
|
host->ioaddr + 0x6c);
|
||||||
|
}
|
||||||
|
|
||||||
/* disable DLL_CTRL delay line settings */
|
/* disable DLL_CTRL delay line settings */
|
||||||
writel(0x0, host->ioaddr + ESDHC_DLL_CTRL);
|
writel(0x0, host->ioaddr + ESDHC_DLL_CTRL);
|
||||||
|
|||||||
@@ -191,6 +191,13 @@ static const struct sdhci_arasan_soc_ctl_map intel_lgm_sdxc_soc_ctl_map = {
|
|||||||
.hiword_update = false,
|
.hiword_update = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct sdhci_arasan_soc_ctl_map thunderbay_soc_ctl_map = {
|
||||||
|
.baseclkfreq = { .reg = 0x0, .width = 8, .shift = 14 },
|
||||||
|
.clockmultiplier = { .reg = 0x4, .width = 8, .shift = 14 },
|
||||||
|
.support64b = { .reg = 0x4, .width = 1, .shift = 24 },
|
||||||
|
.hiword_update = false,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct sdhci_arasan_soc_ctl_map intel_keembay_soc_ctl_map = {
|
static const struct sdhci_arasan_soc_ctl_map intel_keembay_soc_ctl_map = {
|
||||||
.baseclkfreq = { .reg = 0x0, .width = 8, .shift = 14 },
|
.baseclkfreq = { .reg = 0x0, .width = 8, .shift = 14 },
|
||||||
.clockmultiplier = { .reg = 0x4, .width = 8, .shift = 14 },
|
.clockmultiplier = { .reg = 0x4, .width = 8, .shift = 14 },
|
||||||
@@ -456,6 +463,15 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = {
|
|||||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct sdhci_pltfm_data sdhci_arasan_thunderbay_pdata = {
|
||||||
|
.ops = &sdhci_arasan_cqe_ops,
|
||||||
|
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||||
|
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||||
|
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
|
||||||
|
SDHCI_QUIRK2_STOP_WITH_TC |
|
||||||
|
SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400,
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
/**
|
/**
|
||||||
* sdhci_arasan_suspend - Suspend method for the driver
|
* sdhci_arasan_suspend - Suspend method for the driver
|
||||||
@@ -1132,6 +1148,12 @@ static struct sdhci_arasan_of_data sdhci_arasan_generic_data = {
|
|||||||
.clk_ops = &arasan_clk_ops,
|
.clk_ops = &arasan_clk_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct sdhci_arasan_of_data sdhci_arasan_thunderbay_data = {
|
||||||
|
.soc_ctl_map = &thunderbay_soc_ctl_map,
|
||||||
|
.pdata = &sdhci_arasan_thunderbay_pdata,
|
||||||
|
.clk_ops = &arasan_clk_ops,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct sdhci_pltfm_data sdhci_keembay_emmc_pdata = {
|
static const struct sdhci_pltfm_data sdhci_keembay_emmc_pdata = {
|
||||||
.ops = &sdhci_arasan_cqe_ops,
|
.ops = &sdhci_arasan_cqe_ops,
|
||||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||||
@@ -1265,6 +1287,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
|
|||||||
.compatible = "intel,keembay-sdhci-5.1-sdio",
|
.compatible = "intel,keembay-sdhci-5.1-sdio",
|
||||||
.data = &intel_keembay_sdio_data,
|
.data = &intel_keembay_sdio_data,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.compatible = "intel,thunderbay-sdhci-5.1",
|
||||||
|
.data = &sdhci_arasan_thunderbay_data,
|
||||||
|
},
|
||||||
/* Generic compatible below here */
|
/* Generic compatible below here */
|
||||||
{
|
{
|
||||||
.compatible = "arasan,sdhci-8.9a",
|
.compatible = "arasan,sdhci-8.9a",
|
||||||
@@ -1626,7 +1652,8 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
if (of_device_is_compatible(np, "intel,keembay-sdhci-5.1-emmc") ||
|
if (of_device_is_compatible(np, "intel,keembay-sdhci-5.1-emmc") ||
|
||||||
of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sd") ||
|
of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sd") ||
|
||||||
of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sdio")) {
|
of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sdio") ||
|
||||||
|
of_device_is_compatible(np, "intel,thunderbay-sdhci-5.1")) {
|
||||||
sdhci_arasan_update_clockmultiplier(host, 0x0);
|
sdhci_arasan_update_clockmultiplier(host, 0x0);
|
||||||
sdhci_arasan_update_support64b(host, 0x0);
|
sdhci_arasan_update_support64b(host, 0x0);
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,10 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/pm_wakeirq.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
#include <linux/sys_soc.h>
|
#include <linux/sys_soc.h>
|
||||||
@@ -21,7 +23,14 @@
|
|||||||
|
|
||||||
#include "sdhci-pltfm.h"
|
#include "sdhci-pltfm.h"
|
||||||
|
|
||||||
#define SDHCI_OMAP_CON 0x12c
|
/*
|
||||||
|
* Note that the register offsets used here are from omap_regs
|
||||||
|
* base which is 0x100 for omap4 and later, and 0 for omap3 and
|
||||||
|
* earlier.
|
||||||
|
*/
|
||||||
|
#define SDHCI_OMAP_SYSCONFIG 0x10
|
||||||
|
|
||||||
|
#define SDHCI_OMAP_CON 0x2c
|
||||||
#define CON_DW8 BIT(5)
|
#define CON_DW8 BIT(5)
|
||||||
#define CON_DMA_MASTER BIT(20)
|
#define CON_DMA_MASTER BIT(20)
|
||||||
#define CON_DDR BIT(19)
|
#define CON_DDR BIT(19)
|
||||||
@@ -31,20 +40,20 @@
|
|||||||
#define CON_INIT BIT(1)
|
#define CON_INIT BIT(1)
|
||||||
#define CON_OD BIT(0)
|
#define CON_OD BIT(0)
|
||||||
|
|
||||||
#define SDHCI_OMAP_DLL 0x0134
|
#define SDHCI_OMAP_DLL 0x34
|
||||||
#define DLL_SWT BIT(20)
|
#define DLL_SWT BIT(20)
|
||||||
#define DLL_FORCE_SR_C_SHIFT 13
|
#define DLL_FORCE_SR_C_SHIFT 13
|
||||||
#define DLL_FORCE_SR_C_MASK (0x7f << DLL_FORCE_SR_C_SHIFT)
|
#define DLL_FORCE_SR_C_MASK (0x7f << DLL_FORCE_SR_C_SHIFT)
|
||||||
#define DLL_FORCE_VALUE BIT(12)
|
#define DLL_FORCE_VALUE BIT(12)
|
||||||
#define DLL_CALIB BIT(1)
|
#define DLL_CALIB BIT(1)
|
||||||
|
|
||||||
#define SDHCI_OMAP_CMD 0x20c
|
#define SDHCI_OMAP_CMD 0x10c
|
||||||
|
|
||||||
#define SDHCI_OMAP_PSTATE 0x0224
|
#define SDHCI_OMAP_PSTATE 0x124
|
||||||
#define PSTATE_DLEV_DAT0 BIT(20)
|
#define PSTATE_DLEV_DAT0 BIT(20)
|
||||||
#define PSTATE_DATI BIT(1)
|
#define PSTATE_DATI BIT(1)
|
||||||
|
|
||||||
#define SDHCI_OMAP_HCTL 0x228
|
#define SDHCI_OMAP_HCTL 0x128
|
||||||
#define HCTL_SDBP BIT(8)
|
#define HCTL_SDBP BIT(8)
|
||||||
#define HCTL_SDVS_SHIFT 9
|
#define HCTL_SDVS_SHIFT 9
|
||||||
#define HCTL_SDVS_MASK (0x7 << HCTL_SDVS_SHIFT)
|
#define HCTL_SDVS_MASK (0x7 << HCTL_SDVS_SHIFT)
|
||||||
@@ -52,26 +61,28 @@
|
|||||||
#define HCTL_SDVS_30 (0x6 << HCTL_SDVS_SHIFT)
|
#define HCTL_SDVS_30 (0x6 << HCTL_SDVS_SHIFT)
|
||||||
#define HCTL_SDVS_18 (0x5 << HCTL_SDVS_SHIFT)
|
#define HCTL_SDVS_18 (0x5 << HCTL_SDVS_SHIFT)
|
||||||
|
|
||||||
#define SDHCI_OMAP_SYSCTL 0x22c
|
#define SDHCI_OMAP_SYSCTL 0x12c
|
||||||
#define SYSCTL_CEN BIT(2)
|
#define SYSCTL_CEN BIT(2)
|
||||||
#define SYSCTL_CLKD_SHIFT 6
|
#define SYSCTL_CLKD_SHIFT 6
|
||||||
#define SYSCTL_CLKD_MASK 0x3ff
|
#define SYSCTL_CLKD_MASK 0x3ff
|
||||||
|
|
||||||
#define SDHCI_OMAP_STAT 0x230
|
#define SDHCI_OMAP_STAT 0x130
|
||||||
|
|
||||||
#define SDHCI_OMAP_IE 0x234
|
#define SDHCI_OMAP_IE 0x134
|
||||||
#define INT_CC_EN BIT(0)
|
#define INT_CC_EN BIT(0)
|
||||||
|
|
||||||
#define SDHCI_OMAP_AC12 0x23c
|
#define SDHCI_OMAP_ISE 0x138
|
||||||
|
|
||||||
|
#define SDHCI_OMAP_AC12 0x13c
|
||||||
#define AC12_V1V8_SIGEN BIT(19)
|
#define AC12_V1V8_SIGEN BIT(19)
|
||||||
#define AC12_SCLK_SEL BIT(23)
|
#define AC12_SCLK_SEL BIT(23)
|
||||||
|
|
||||||
#define SDHCI_OMAP_CAPA 0x240
|
#define SDHCI_OMAP_CAPA 0x140
|
||||||
#define CAPA_VS33 BIT(24)
|
#define CAPA_VS33 BIT(24)
|
||||||
#define CAPA_VS30 BIT(25)
|
#define CAPA_VS30 BIT(25)
|
||||||
#define CAPA_VS18 BIT(26)
|
#define CAPA_VS18 BIT(26)
|
||||||
|
|
||||||
#define SDHCI_OMAP_CAPA2 0x0244
|
#define SDHCI_OMAP_CAPA2 0x144
|
||||||
#define CAPA2_TSDR50 BIT(13)
|
#define CAPA2_TSDR50 BIT(13)
|
||||||
|
|
||||||
#define SDHCI_OMAP_TIMEOUT 1 /* 1 msec */
|
#define SDHCI_OMAP_TIMEOUT 1 /* 1 msec */
|
||||||
@@ -89,7 +100,8 @@
|
|||||||
#define SDHCI_OMAP_SPECIAL_RESET BIT(1)
|
#define SDHCI_OMAP_SPECIAL_RESET BIT(1)
|
||||||
|
|
||||||
struct sdhci_omap_data {
|
struct sdhci_omap_data {
|
||||||
u32 offset;
|
int omap_offset; /* Offset for omap regs from base */
|
||||||
|
u32 offset; /* Offset for SDHCI regs from base */
|
||||||
u8 flags;
|
u8 flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -107,12 +119,19 @@ struct sdhci_omap_host {
|
|||||||
|
|
||||||
struct pinctrl *pinctrl;
|
struct pinctrl *pinctrl;
|
||||||
struct pinctrl_state **pinctrl_state;
|
struct pinctrl_state **pinctrl_state;
|
||||||
|
int wakeirq;
|
||||||
bool is_tuning;
|
bool is_tuning;
|
||||||
|
|
||||||
|
/* Offset for omap specific registers from base */
|
||||||
|
int omap_offset;
|
||||||
|
|
||||||
/* Omap specific context save */
|
/* Omap specific context save */
|
||||||
u32 con;
|
u32 con;
|
||||||
u32 hctl;
|
u32 hctl;
|
||||||
u32 sysctl;
|
u32 sysctl;
|
||||||
u32 capa;
|
u32 capa;
|
||||||
|
u32 ie;
|
||||||
|
u32 ise;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host);
|
static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host);
|
||||||
@@ -121,13 +140,13 @@ static void sdhci_omap_stop_clock(struct sdhci_omap_host *omap_host);
|
|||||||
static inline u32 sdhci_omap_readl(struct sdhci_omap_host *host,
|
static inline u32 sdhci_omap_readl(struct sdhci_omap_host *host,
|
||||||
unsigned int offset)
|
unsigned int offset)
|
||||||
{
|
{
|
||||||
return readl(host->base + offset);
|
return readl(host->base + host->omap_offset + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void sdhci_omap_writel(struct sdhci_omap_host *host,
|
static inline void sdhci_omap_writel(struct sdhci_omap_host *host,
|
||||||
unsigned int offset, u32 data)
|
unsigned int offset, u32 data)
|
||||||
{
|
{
|
||||||
writel(data, host->base + offset);
|
writel(data, host->base + host->omap_offset + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdhci_omap_set_pbias(struct sdhci_omap_host *omap_host,
|
static int sdhci_omap_set_pbias(struct sdhci_omap_host *omap_host,
|
||||||
@@ -172,7 +191,7 @@ static int sdhci_omap_set_pbias(struct sdhci_omap_host *omap_host,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int sdhci_omap_enable_iov(struct sdhci_omap_host *omap_host,
|
static int sdhci_omap_enable_iov(struct sdhci_omap_host *omap_host,
|
||||||
unsigned int iov)
|
unsigned int iov_pbias)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct sdhci_host *host = omap_host->host;
|
struct sdhci_host *host = omap_host->host;
|
||||||
@@ -183,14 +202,15 @@ static int sdhci_omap_enable_iov(struct sdhci_omap_host *omap_host,
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||||
ret = regulator_set_voltage(mmc->supply.vqmmc, iov, iov);
|
/* Pick the right voltage to allow 3.0V for 3.3V nominal PBIAS */
|
||||||
if (ret) {
|
ret = mmc_regulator_set_vqmmc(mmc, &mmc->ios);
|
||||||
|
if (ret < 0) {
|
||||||
dev_err(mmc_dev(mmc), "vqmmc set voltage failed\n");
|
dev_err(mmc_dev(mmc), "vqmmc set voltage failed\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = sdhci_omap_set_pbias(omap_host, true, iov);
|
ret = sdhci_omap_set_pbias(omap_host, true, iov_pbias);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@@ -200,16 +220,28 @@ static int sdhci_omap_enable_iov(struct sdhci_omap_host *omap_host,
|
|||||||
static void sdhci_omap_conf_bus_power(struct sdhci_omap_host *omap_host,
|
static void sdhci_omap_conf_bus_power(struct sdhci_omap_host *omap_host,
|
||||||
unsigned char signal_voltage)
|
unsigned char signal_voltage)
|
||||||
{
|
{
|
||||||
u32 reg;
|
u32 reg, capa;
|
||||||
ktime_t timeout;
|
ktime_t timeout;
|
||||||
|
|
||||||
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL);
|
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL);
|
||||||
reg &= ~HCTL_SDVS_MASK;
|
reg &= ~HCTL_SDVS_MASK;
|
||||||
|
|
||||||
if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
|
switch (signal_voltage) {
|
||||||
reg |= HCTL_SDVS_33;
|
case MMC_SIGNAL_VOLTAGE_330:
|
||||||
else
|
capa = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
|
||||||
|
if (capa & CAPA_VS33)
|
||||||
|
reg |= HCTL_SDVS_33;
|
||||||
|
else if (capa & CAPA_VS30)
|
||||||
|
reg |= HCTL_SDVS_30;
|
||||||
|
else
|
||||||
|
dev_warn(omap_host->dev, "misconfigured CAPA: %08x\n",
|
||||||
|
capa);
|
||||||
|
break;
|
||||||
|
case MMC_SIGNAL_VOLTAGE_180:
|
||||||
|
default:
|
||||||
reg |= HCTL_SDVS_18;
|
reg |= HCTL_SDVS_18;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, reg);
|
sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, reg);
|
||||||
|
|
||||||
@@ -527,28 +559,32 @@ static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc,
|
|||||||
|
|
||||||
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
|
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
|
||||||
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
|
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
|
||||||
if (!(reg & CAPA_VS33))
|
if (!(reg & (CAPA_VS30 | CAPA_VS33)))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (reg & CAPA_VS30)
|
||||||
|
iov = IOV_3V0;
|
||||||
|
else
|
||||||
|
iov = IOV_3V3;
|
||||||
|
|
||||||
sdhci_omap_conf_bus_power(omap_host, ios->signal_voltage);
|
sdhci_omap_conf_bus_power(omap_host, ios->signal_voltage);
|
||||||
|
|
||||||
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
|
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
|
||||||
reg &= ~AC12_V1V8_SIGEN;
|
reg &= ~AC12_V1V8_SIGEN;
|
||||||
sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
|
sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
|
||||||
|
|
||||||
iov = IOV_3V3;
|
|
||||||
} else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
|
} else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
|
||||||
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
|
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
|
||||||
if (!(reg & CAPA_VS18))
|
if (!(reg & CAPA_VS18))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
iov = IOV_1V8;
|
||||||
|
|
||||||
sdhci_omap_conf_bus_power(omap_host, ios->signal_voltage);
|
sdhci_omap_conf_bus_power(omap_host, ios->signal_voltage);
|
||||||
|
|
||||||
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
|
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
|
||||||
reg |= AC12_V1V8_SIGEN;
|
reg |= AC12_V1V8_SIGEN;
|
||||||
sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
|
sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
|
||||||
|
|
||||||
iov = IOV_1V8;
|
|
||||||
} else {
|
} else {
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
@@ -682,7 +718,24 @@ static void sdhci_omap_set_power(struct sdhci_host *host, unsigned char mode,
|
|||||||
{
|
{
|
||||||
struct mmc_host *mmc = host->mmc;
|
struct mmc_host *mmc = host->mmc;
|
||||||
|
|
||||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
|
if (!IS_ERR(mmc->supply.vmmc))
|
||||||
|
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MMCHS_HL_HWINFO has the MADMA_EN bit set if the controller instance
|
||||||
|
* is connected to L3 interconnect and is bus master capable. Note that
|
||||||
|
* the MMCHS_HL_HWINFO register is in the module registers before the
|
||||||
|
* omap registers and sdhci registers. The offset can vary for omap
|
||||||
|
* registers depending on the SoC. Do not use sdhci_omap_readl() here.
|
||||||
|
*/
|
||||||
|
static bool sdhci_omap_has_adma(struct sdhci_omap_host *omap_host, int offset)
|
||||||
|
{
|
||||||
|
/* MMCHS_HL_HWINFO register is only available on omap4 and later */
|
||||||
|
if (offset < 0x200)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return readl(omap_host->base + 4) & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdhci_omap_enable_dma(struct sdhci_host *host)
|
static int sdhci_omap_enable_dma(struct sdhci_host *host)
|
||||||
@@ -792,6 +845,11 @@ static void sdhci_omap_reset(struct sdhci_host *host, u8 mask)
|
|||||||
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
|
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
|
||||||
unsigned long limit = MMC_TIMEOUT_US;
|
unsigned long limit = MMC_TIMEOUT_US;
|
||||||
unsigned long i = 0;
|
unsigned long i = 0;
|
||||||
|
u32 sysc;
|
||||||
|
|
||||||
|
/* Save target module sysconfig configured by SoC PM layer */
|
||||||
|
if (mask & SDHCI_RESET_ALL)
|
||||||
|
sysc = sdhci_omap_readl(omap_host, SDHCI_OMAP_SYSCONFIG);
|
||||||
|
|
||||||
/* Don't reset data lines during tuning operation */
|
/* Don't reset data lines during tuning operation */
|
||||||
if (omap_host->is_tuning)
|
if (omap_host->is_tuning)
|
||||||
@@ -811,10 +869,15 @@ static void sdhci_omap_reset(struct sdhci_host *host, u8 mask)
|
|||||||
dev_err(mmc_dev(host->mmc),
|
dev_err(mmc_dev(host->mmc),
|
||||||
"Timeout waiting on controller reset in %s\n",
|
"Timeout waiting on controller reset in %s\n",
|
||||||
__func__);
|
__func__);
|
||||||
return;
|
|
||||||
|
goto restore_sysc;
|
||||||
}
|
}
|
||||||
|
|
||||||
sdhci_reset(host, mask);
|
sdhci_reset(host, mask);
|
||||||
|
|
||||||
|
restore_sysc:
|
||||||
|
if (mask & SDHCI_RESET_ALL)
|
||||||
|
sdhci_omap_writel(omap_host, SDHCI_OMAP_SYSCONFIG, sysc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CMD_ERR_MASK (SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX |\
|
#define CMD_ERR_MASK (SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX |\
|
||||||
@@ -877,34 +940,73 @@ static struct sdhci_ops sdhci_omap_ops = {
|
|||||||
.set_timeout = sdhci_omap_set_timeout,
|
.set_timeout = sdhci_omap_set_timeout,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int sdhci_omap_set_capabilities(struct sdhci_omap_host *omap_host)
|
static unsigned int sdhci_omap_regulator_get_caps(struct device *dev,
|
||||||
|
const char *name)
|
||||||
{
|
{
|
||||||
u32 reg;
|
struct regulator *reg;
|
||||||
int ret = 0;
|
unsigned int caps = 0;
|
||||||
struct device *dev = omap_host->dev;
|
|
||||||
struct regulator *vqmmc;
|
|
||||||
|
|
||||||
vqmmc = regulator_get(dev, "vqmmc");
|
reg = regulator_get(dev, name);
|
||||||
if (IS_ERR(vqmmc)) {
|
if (IS_ERR(reg))
|
||||||
ret = PTR_ERR(vqmmc);
|
return ~0U;
|
||||||
goto reg_put;
|
|
||||||
}
|
if (regulator_is_supported_voltage(reg, 1700000, 1950000))
|
||||||
|
caps |= SDHCI_CAN_VDD_180;
|
||||||
|
if (regulator_is_supported_voltage(reg, 2700000, 3150000))
|
||||||
|
caps |= SDHCI_CAN_VDD_300;
|
||||||
|
if (regulator_is_supported_voltage(reg, 3150000, 3600000))
|
||||||
|
caps |= SDHCI_CAN_VDD_330;
|
||||||
|
|
||||||
|
regulator_put(reg);
|
||||||
|
|
||||||
|
return caps;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdhci_omap_set_capabilities(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
struct device *dev = omap_host->dev;
|
||||||
|
const u32 mask = SDHCI_CAN_VDD_180 | SDHCI_CAN_VDD_300 | SDHCI_CAN_VDD_330;
|
||||||
|
unsigned int pbias, vqmmc, caps = 0;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
pbias = sdhci_omap_regulator_get_caps(dev, "pbias");
|
||||||
|
vqmmc = sdhci_omap_regulator_get_caps(dev, "vqmmc");
|
||||||
|
caps = pbias & vqmmc;
|
||||||
|
|
||||||
|
if (pbias != ~0U && vqmmc == ~0U)
|
||||||
|
dev_warn(dev, "vqmmc regulator missing for pbias\n");
|
||||||
|
else if (caps == ~0U)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Quirk handling to allow 3.0V vqmmc with a valid 3.3V PBIAS. This is
|
||||||
|
* needed for 3.0V ldo9_reg on omap5 at least.
|
||||||
|
*/
|
||||||
|
if (pbias != ~0U && (pbias & SDHCI_CAN_VDD_330) &&
|
||||||
|
(vqmmc & SDHCI_CAN_VDD_300))
|
||||||
|
caps |= SDHCI_CAN_VDD_330;
|
||||||
|
|
||||||
/* voltage capabilities might be set by boot loader, clear it */
|
/* voltage capabilities might be set by boot loader, clear it */
|
||||||
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
|
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
|
||||||
reg &= ~(CAPA_VS18 | CAPA_VS30 | CAPA_VS33);
|
reg &= ~(CAPA_VS18 | CAPA_VS30 | CAPA_VS33);
|
||||||
|
|
||||||
if (regulator_is_supported_voltage(vqmmc, IOV_3V3, IOV_3V3))
|
if (caps & SDHCI_CAN_VDD_180)
|
||||||
reg |= CAPA_VS33;
|
|
||||||
if (regulator_is_supported_voltage(vqmmc, IOV_1V8, IOV_1V8))
|
|
||||||
reg |= CAPA_VS18;
|
reg |= CAPA_VS18;
|
||||||
|
|
||||||
|
if (caps & SDHCI_CAN_VDD_300)
|
||||||
|
reg |= CAPA_VS30;
|
||||||
|
|
||||||
|
if (caps & SDHCI_CAN_VDD_330)
|
||||||
|
reg |= CAPA_VS33;
|
||||||
|
|
||||||
sdhci_omap_writel(omap_host, SDHCI_OMAP_CAPA, reg);
|
sdhci_omap_writel(omap_host, SDHCI_OMAP_CAPA, reg);
|
||||||
|
|
||||||
reg_put:
|
host->caps &= ~mask;
|
||||||
regulator_put(vqmmc);
|
host->caps |= caps;
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct sdhci_pltfm_data sdhci_omap_pdata = {
|
static const struct sdhci_pltfm_data sdhci_omap_pdata = {
|
||||||
@@ -920,26 +1022,56 @@ static const struct sdhci_pltfm_data sdhci_omap_pdata = {
|
|||||||
.ops = &sdhci_omap_ops,
|
.ops = &sdhci_omap_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct sdhci_omap_data omap2430_data = {
|
||||||
|
.omap_offset = 0,
|
||||||
|
.offset = 0x100,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sdhci_omap_data omap3_data = {
|
||||||
|
.omap_offset = 0,
|
||||||
|
.offset = 0x100,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sdhci_omap_data omap4_data = {
|
||||||
|
.omap_offset = 0x100,
|
||||||
|
.offset = 0x200,
|
||||||
|
.flags = SDHCI_OMAP_SPECIAL_RESET,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sdhci_omap_data omap5_data = {
|
||||||
|
.omap_offset = 0x100,
|
||||||
|
.offset = 0x200,
|
||||||
|
.flags = SDHCI_OMAP_SPECIAL_RESET,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct sdhci_omap_data k2g_data = {
|
static const struct sdhci_omap_data k2g_data = {
|
||||||
|
.omap_offset = 0x100,
|
||||||
.offset = 0x200,
|
.offset = 0x200,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_omap_data am335_data = {
|
static const struct sdhci_omap_data am335_data = {
|
||||||
|
.omap_offset = 0x100,
|
||||||
.offset = 0x200,
|
.offset = 0x200,
|
||||||
.flags = SDHCI_OMAP_SPECIAL_RESET,
|
.flags = SDHCI_OMAP_SPECIAL_RESET,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_omap_data am437_data = {
|
static const struct sdhci_omap_data am437_data = {
|
||||||
|
.omap_offset = 0x100,
|
||||||
.offset = 0x200,
|
.offset = 0x200,
|
||||||
.flags = SDHCI_OMAP_SPECIAL_RESET,
|
.flags = SDHCI_OMAP_SPECIAL_RESET,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_omap_data dra7_data = {
|
static const struct sdhci_omap_data dra7_data = {
|
||||||
|
.omap_offset = 0x100,
|
||||||
.offset = 0x200,
|
.offset = 0x200,
|
||||||
.flags = SDHCI_OMAP_REQUIRE_IODELAY,
|
.flags = SDHCI_OMAP_REQUIRE_IODELAY,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id omap_sdhci_match[] = {
|
static const struct of_device_id omap_sdhci_match[] = {
|
||||||
|
{ .compatible = "ti,omap2430-sdhci", .data = &omap2430_data },
|
||||||
|
{ .compatible = "ti,omap3-sdhci", .data = &omap3_data },
|
||||||
|
{ .compatible = "ti,omap4-sdhci", .data = &omap4_data },
|
||||||
|
{ .compatible = "ti,omap5-sdhci", .data = &omap5_data },
|
||||||
{ .compatible = "ti,dra7-sdhci", .data = &dra7_data },
|
{ .compatible = "ti,dra7-sdhci", .data = &dra7_data },
|
||||||
{ .compatible = "ti,k2g-sdhci", .data = &k2g_data },
|
{ .compatible = "ti,k2g-sdhci", .data = &k2g_data },
|
||||||
{ .compatible = "ti,am335-sdhci", .data = &am335_data },
|
{ .compatible = "ti,am335-sdhci", .data = &am335_data },
|
||||||
@@ -1122,6 +1254,8 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
|||||||
omap_host->power_mode = MMC_POWER_UNDEFINED;
|
omap_host->power_mode = MMC_POWER_UNDEFINED;
|
||||||
omap_host->timing = MMC_TIMING_LEGACY;
|
omap_host->timing = MMC_TIMING_LEGACY;
|
||||||
omap_host->flags = data->flags;
|
omap_host->flags = data->flags;
|
||||||
|
omap_host->omap_offset = data->omap_offset;
|
||||||
|
omap_host->con = -EINVAL; /* Prevent invalid restore on first resume */
|
||||||
host->ioaddr += offset;
|
host->ioaddr += offset;
|
||||||
host->mapbase = regs->start + offset;
|
host->mapbase = regs->start + offset;
|
||||||
|
|
||||||
@@ -1172,6 +1306,8 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
|||||||
* SYSCONFIG register of omap devices. The callback will be invoked
|
* SYSCONFIG register of omap devices. The callback will be invoked
|
||||||
* as part of pm_runtime_get_sync.
|
* as part of pm_runtime_get_sync.
|
||||||
*/
|
*/
|
||||||
|
pm_runtime_use_autosuspend(dev);
|
||||||
|
pm_runtime_set_autosuspend_delay(dev, 50);
|
||||||
pm_runtime_enable(dev);
|
pm_runtime_enable(dev);
|
||||||
ret = pm_runtime_resume_and_get(dev);
|
ret = pm_runtime_resume_and_get(dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
@@ -1179,10 +1315,10 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
|||||||
goto err_rpm_disable;
|
goto err_rpm_disable;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = sdhci_omap_set_capabilities(omap_host);
|
ret = sdhci_omap_set_capabilities(host);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "failed to set system capabilities\n");
|
dev_err(dev, "failed to set system capabilities\n");
|
||||||
goto err_put_sync;
|
goto err_rpm_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
host->mmc_host_ops.start_signal_voltage_switch =
|
host->mmc_host_ops.start_signal_voltage_switch =
|
||||||
@@ -1192,16 +1328,28 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
|||||||
host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning;
|
host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning;
|
||||||
host->mmc_host_ops.enable_sdio_irq = sdhci_omap_enable_sdio_irq;
|
host->mmc_host_ops.enable_sdio_irq = sdhci_omap_enable_sdio_irq;
|
||||||
|
|
||||||
/* Switch to external DMA only if there is the "dmas" property */
|
/*
|
||||||
if (of_find_property(dev->of_node, "dmas", NULL))
|
* Switch to external DMA only if there is the "dmas" property and
|
||||||
|
* ADMA is not available on the controller instance.
|
||||||
|
*/
|
||||||
|
if (device_property_present(dev, "dmas") &&
|
||||||
|
!sdhci_omap_has_adma(omap_host, offset))
|
||||||
sdhci_switch_external_dma(host, true);
|
sdhci_switch_external_dma(host, true);
|
||||||
|
|
||||||
|
if (device_property_read_bool(dev, "ti,non-removable")) {
|
||||||
|
dev_warn_once(dev, "using old ti,non-removable property\n");
|
||||||
|
mmc->caps |= MMC_CAP_NONREMOVABLE;
|
||||||
|
}
|
||||||
|
|
||||||
/* R1B responses is required to properly manage HW busy detection. */
|
/* R1B responses is required to properly manage HW busy detection. */
|
||||||
mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
|
mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
|
||||||
|
|
||||||
|
/* Allow card power off and runtime PM for eMMC/SD card devices */
|
||||||
|
mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_AGGRESSIVE_PM;
|
||||||
|
|
||||||
ret = sdhci_setup_host(host);
|
ret = sdhci_setup_host(host);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_put_sync;
|
goto err_rpm_put;
|
||||||
|
|
||||||
ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host);
|
ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host);
|
||||||
if (ret)
|
if (ret)
|
||||||
@@ -1211,15 +1359,38 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto err_cleanup_host;
|
goto err_cleanup_host;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SDIO devices can use the dat1 pin as a wake-up interrupt. Some
|
||||||
|
* devices like wl1xxx, use an out-of-band GPIO interrupt instead.
|
||||||
|
*/
|
||||||
|
omap_host->wakeirq = of_irq_get_byname(dev->of_node, "wakeup");
|
||||||
|
if (omap_host->wakeirq == -EPROBE_DEFER) {
|
||||||
|
ret = -EPROBE_DEFER;
|
||||||
|
goto err_cleanup_host;
|
||||||
|
}
|
||||||
|
if (omap_host->wakeirq > 0) {
|
||||||
|
device_init_wakeup(dev, true);
|
||||||
|
ret = dev_pm_set_dedicated_wake_irq(dev, omap_host->wakeirq);
|
||||||
|
if (ret) {
|
||||||
|
device_init_wakeup(dev, false);
|
||||||
|
goto err_cleanup_host;
|
||||||
|
}
|
||||||
|
host->mmc->pm_caps |= MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ;
|
||||||
|
}
|
||||||
|
|
||||||
|
pm_runtime_mark_last_busy(dev);
|
||||||
|
pm_runtime_put_autosuspend(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_cleanup_host:
|
err_cleanup_host:
|
||||||
sdhci_cleanup_host(host);
|
sdhci_cleanup_host(host);
|
||||||
|
|
||||||
err_put_sync:
|
err_rpm_put:
|
||||||
pm_runtime_put_sync(dev);
|
pm_runtime_mark_last_busy(dev);
|
||||||
|
pm_runtime_put_autosuspend(dev);
|
||||||
err_rpm_disable:
|
err_rpm_disable:
|
||||||
|
pm_runtime_dont_use_autosuspend(dev);
|
||||||
pm_runtime_disable(dev);
|
pm_runtime_disable(dev);
|
||||||
|
|
||||||
err_pltfm_free:
|
err_pltfm_free:
|
||||||
@@ -1232,64 +1403,81 @@ static int sdhci_omap_remove(struct platform_device *pdev)
|
|||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
pm_runtime_get_sync(dev);
|
||||||
sdhci_remove_host(host, true);
|
sdhci_remove_host(host, true);
|
||||||
|
device_init_wakeup(dev, false);
|
||||||
|
dev_pm_clear_wake_irq(dev);
|
||||||
|
pm_runtime_dont_use_autosuspend(dev);
|
||||||
pm_runtime_put_sync(dev);
|
pm_runtime_put_sync(dev);
|
||||||
pm_runtime_disable(dev);
|
/* Ensure device gets disabled despite userspace sysfs config */
|
||||||
|
pm_runtime_force_suspend(dev);
|
||||||
sdhci_pltfm_free(pdev);
|
sdhci_pltfm_free(pdev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#ifdef CONFIG_PM_SLEEP
|
|
||||||
static void sdhci_omap_context_save(struct sdhci_omap_host *omap_host)
|
#ifdef CONFIG_PM
|
||||||
|
static void __maybe_unused sdhci_omap_context_save(struct sdhci_omap_host *omap_host)
|
||||||
{
|
{
|
||||||
omap_host->con = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
|
omap_host->con = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
|
||||||
omap_host->hctl = sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL);
|
omap_host->hctl = sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL);
|
||||||
|
omap_host->sysctl = sdhci_omap_readl(omap_host, SDHCI_OMAP_SYSCTL);
|
||||||
omap_host->capa = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
|
omap_host->capa = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
|
||||||
|
omap_host->ie = sdhci_omap_readl(omap_host, SDHCI_OMAP_IE);
|
||||||
|
omap_host->ise = sdhci_omap_readl(omap_host, SDHCI_OMAP_ISE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdhci_omap_context_restore(struct sdhci_omap_host *omap_host)
|
/* Order matters here, HCTL must be restored in two phases */
|
||||||
|
static void __maybe_unused sdhci_omap_context_restore(struct sdhci_omap_host *omap_host)
|
||||||
{
|
{
|
||||||
sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, omap_host->con);
|
|
||||||
sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, omap_host->hctl);
|
sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, omap_host->hctl);
|
||||||
sdhci_omap_writel(omap_host, SDHCI_OMAP_CAPA, omap_host->capa);
|
sdhci_omap_writel(omap_host, SDHCI_OMAP_CAPA, omap_host->capa);
|
||||||
|
sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, omap_host->hctl);
|
||||||
|
|
||||||
|
sdhci_omap_writel(omap_host, SDHCI_OMAP_SYSCTL, omap_host->sysctl);
|
||||||
|
sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, omap_host->con);
|
||||||
|
sdhci_omap_writel(omap_host, SDHCI_OMAP_IE, omap_host->ie);
|
||||||
|
sdhci_omap_writel(omap_host, SDHCI_OMAP_ISE, omap_host->ise);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __maybe_unused sdhci_omap_suspend(struct device *dev)
|
static int __maybe_unused sdhci_omap_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
|
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
|
||||||
sdhci_suspend_host(host);
|
sdhci_runtime_suspend_host(host);
|
||||||
|
|
||||||
sdhci_omap_context_save(omap_host);
|
sdhci_omap_context_save(omap_host);
|
||||||
|
|
||||||
pinctrl_pm_select_idle_state(dev);
|
pinctrl_pm_select_idle_state(dev);
|
||||||
|
|
||||||
pm_runtime_force_suspend(dev);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __maybe_unused sdhci_omap_resume(struct device *dev)
|
static int __maybe_unused sdhci_omap_runtime_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
|
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
|
||||||
pm_runtime_force_resume(dev);
|
|
||||||
|
|
||||||
pinctrl_pm_select_default_state(dev);
|
pinctrl_pm_select_default_state(dev);
|
||||||
|
|
||||||
sdhci_omap_context_restore(omap_host);
|
if (omap_host->con != -EINVAL)
|
||||||
|
sdhci_omap_context_restore(omap_host);
|
||||||
|
|
||||||
sdhci_resume_host(host);
|
sdhci_runtime_resume_host(host, 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
static SIMPLE_DEV_PM_OPS(sdhci_omap_dev_pm_ops, sdhci_omap_suspend,
|
|
||||||
sdhci_omap_resume);
|
static const struct dev_pm_ops sdhci_omap_dev_pm_ops = {
|
||||||
|
SET_RUNTIME_PM_OPS(sdhci_omap_runtime_suspend,
|
||||||
|
sdhci_omap_runtime_resume, NULL)
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||||
|
pm_runtime_force_resume)
|
||||||
|
};
|
||||||
|
|
||||||
static struct platform_driver sdhci_omap_driver = {
|
static struct platform_driver sdhci_omap_driver = {
|
||||||
.probe = sdhci_omap_probe,
|
.probe = sdhci_omap_probe,
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/mmc/host.h>
|
|
||||||
#include <linux/mmc/mmc.h>
|
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/iopoll.h>
|
#include <linux/iopoll.h>
|
||||||
@@ -26,11 +24,13 @@
|
|||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/pm_qos.h>
|
#include <linux/pm_qos.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
#include <linux/mmc/slot-gpio.h>
|
|
||||||
#include <linux/mmc/sdhci-pci-data.h>
|
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/dmi.h>
|
#include <linux/dmi.h>
|
||||||
|
|
||||||
|
#include <linux/mmc/host.h>
|
||||||
|
#include <linux/mmc/mmc.h>
|
||||||
|
#include <linux/mmc/slot-gpio.h>
|
||||||
|
|
||||||
#ifdef CONFIG_X86
|
#ifdef CONFIG_X86
|
||||||
#include <asm/iosf_mbi.h>
|
#include <asm/iosf_mbi.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -345,73 +345,6 @@ static int pch_hc_probe_slot(struct sdhci_pci_slot *slot)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
|
||||||
|
|
||||||
static irqreturn_t sdhci_pci_sd_cd(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
struct sdhci_pci_slot *slot = dev_id;
|
|
||||||
struct sdhci_host *host = slot->host;
|
|
||||||
|
|
||||||
mmc_detect_change(host->mmc, msecs_to_jiffies(200));
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot)
|
|
||||||
{
|
|
||||||
int err, irq, gpio = slot->cd_gpio;
|
|
||||||
|
|
||||||
slot->cd_gpio = -EINVAL;
|
|
||||||
slot->cd_irq = -EINVAL;
|
|
||||||
|
|
||||||
if (!gpio_is_valid(gpio))
|
|
||||||
return;
|
|
||||||
|
|
||||||
err = devm_gpio_request(&slot->chip->pdev->dev, gpio, "sd_cd");
|
|
||||||
if (err < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
err = gpio_direction_input(gpio);
|
|
||||||
if (err < 0)
|
|
||||||
goto out_free;
|
|
||||||
|
|
||||||
irq = gpio_to_irq(gpio);
|
|
||||||
if (irq < 0)
|
|
||||||
goto out_free;
|
|
||||||
|
|
||||||
err = request_irq(irq, sdhci_pci_sd_cd, IRQF_TRIGGER_RISING |
|
|
||||||
IRQF_TRIGGER_FALLING, "sd_cd", slot);
|
|
||||||
if (err)
|
|
||||||
goto out_free;
|
|
||||||
|
|
||||||
slot->cd_gpio = gpio;
|
|
||||||
slot->cd_irq = irq;
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
out_free:
|
|
||||||
devm_gpio_free(&slot->chip->pdev->dev, gpio);
|
|
||||||
out:
|
|
||||||
dev_warn(&slot->chip->pdev->dev, "failed to setup card detect wake up\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot)
|
|
||||||
{
|
|
||||||
if (slot->cd_irq >= 0)
|
|
||||||
free_irq(slot->cd_irq, slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static inline void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
{
|
{
|
||||||
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE;
|
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE;
|
||||||
@@ -616,24 +549,6 @@ static int intel_select_drive_strength(struct mmc_card *card,
|
|||||||
return intel_host->drv_strength;
|
return intel_host->drv_strength;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdhci_get_cd_nogpio(struct mmc_host *mmc)
|
|
||||||
{
|
|
||||||
struct sdhci_host *host = mmc_priv(mmc);
|
|
||||||
unsigned long flags;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
|
||||||
|
|
||||||
if (host->flags & SDHCI_DEVICE_DEAD)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
ret = !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT);
|
|
||||||
out:
|
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bxt_get_cd(struct mmc_host *mmc)
|
static int bxt_get_cd(struct mmc_host *mmc)
|
||||||
{
|
{
|
||||||
int gpio_cd = mmc_gpio_get_cd(mmc);
|
int gpio_cd = mmc_gpio_get_cd(mmc);
|
||||||
@@ -2000,21 +1915,6 @@ int sdhci_pci_enable_dma(struct sdhci_host *host)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdhci_pci_gpio_hw_reset(struct sdhci_host *host)
|
|
||||||
{
|
|
||||||
struct sdhci_pci_slot *slot = sdhci_priv(host);
|
|
||||||
int rst_n_gpio = slot->rst_n_gpio;
|
|
||||||
|
|
||||||
if (!gpio_is_valid(rst_n_gpio))
|
|
||||||
return;
|
|
||||||
gpio_set_value_cansleep(rst_n_gpio, 0);
|
|
||||||
/* For eMMC, minimum is 1us but give it 10us for good measure */
|
|
||||||
udelay(10);
|
|
||||||
gpio_set_value_cansleep(rst_n_gpio, 1);
|
|
||||||
/* For eMMC, minimum is 200us but give it 300us for good measure */
|
|
||||||
usleep_range(300, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sdhci_pci_hw_reset(struct sdhci_host *host)
|
static void sdhci_pci_hw_reset(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
struct sdhci_pci_slot *slot = sdhci_priv(host);
|
struct sdhci_pci_slot *slot = sdhci_priv(host);
|
||||||
@@ -2145,26 +2045,8 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
|
|||||||
|
|
||||||
slot->chip = chip;
|
slot->chip = chip;
|
||||||
slot->host = host;
|
slot->host = host;
|
||||||
slot->rst_n_gpio = -EINVAL;
|
|
||||||
slot->cd_gpio = -EINVAL;
|
|
||||||
slot->cd_idx = -1;
|
slot->cd_idx = -1;
|
||||||
|
|
||||||
/* Retrieve platform data if there is any */
|
|
||||||
if (*sdhci_pci_get_data)
|
|
||||||
slot->data = sdhci_pci_get_data(pdev, slotno);
|
|
||||||
|
|
||||||
if (slot->data) {
|
|
||||||
if (slot->data->setup) {
|
|
||||||
ret = slot->data->setup(slot->data);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(&pdev->dev, "platform setup failed\n");
|
|
||||||
goto free;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
slot->rst_n_gpio = slot->data->rst_n_gpio;
|
|
||||||
slot->cd_gpio = slot->data->cd_gpio;
|
|
||||||
}
|
|
||||||
|
|
||||||
host->hw_name = "PCI";
|
host->hw_name = "PCI";
|
||||||
host->ops = chip->fixes && chip->fixes->ops ?
|
host->ops = chip->fixes && chip->fixes->ops ?
|
||||||
chip->fixes->ops :
|
chip->fixes->ops :
|
||||||
@@ -2188,17 +2070,6 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gpio_is_valid(slot->rst_n_gpio)) {
|
|
||||||
if (!devm_gpio_request(&pdev->dev, slot->rst_n_gpio, "eMMC_reset")) {
|
|
||||||
gpio_direction_output(slot->rst_n_gpio, 1);
|
|
||||||
slot->host->mmc->caps |= MMC_CAP_HW_RESET;
|
|
||||||
slot->hw_reset = sdhci_pci_gpio_hw_reset;
|
|
||||||
} else {
|
|
||||||
dev_warn(&pdev->dev, "failed to request rst_n_gpio\n");
|
|
||||||
slot->rst_n_gpio = -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
host->mmc->pm_caps = MMC_PM_KEEP_POWER;
|
host->mmc->pm_caps = MMC_PM_KEEP_POWER;
|
||||||
host->mmc->slotno = slotno;
|
host->mmc->slotno = slotno;
|
||||||
host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP;
|
host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP;
|
||||||
@@ -2233,15 +2104,11 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto remove;
|
goto remove;
|
||||||
|
|
||||||
sdhci_pci_add_own_cd(slot);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the chip needs a separate GPIO for card detect to wake up
|
* Check if the chip needs a separate GPIO for card detect to wake up
|
||||||
* from runtime suspend. If it is not there, don't allow runtime PM.
|
* from runtime suspend. If it is not there, don't allow runtime PM.
|
||||||
* Note sdhci_pci_add_own_cd() sets slot->cd_gpio to -EINVAL on failure.
|
|
||||||
*/
|
*/
|
||||||
if (chip->fixes && chip->fixes->own_cd_for_runtime_pm &&
|
if (chip->fixes && chip->fixes->own_cd_for_runtime_pm && slot->cd_idx < 0)
|
||||||
!gpio_is_valid(slot->cd_gpio) && slot->cd_idx < 0)
|
|
||||||
chip->allow_runtime_pm = false;
|
chip->allow_runtime_pm = false;
|
||||||
|
|
||||||
return slot;
|
return slot;
|
||||||
@@ -2251,10 +2118,6 @@ remove:
|
|||||||
chip->fixes->remove_slot(slot, 0);
|
chip->fixes->remove_slot(slot, 0);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (slot->data && slot->data->cleanup)
|
|
||||||
slot->data->cleanup(slot->data);
|
|
||||||
|
|
||||||
free:
|
|
||||||
sdhci_free_host(host);
|
sdhci_free_host(host);
|
||||||
|
|
||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
@@ -2265,8 +2128,6 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
|
|||||||
int dead;
|
int dead;
|
||||||
u32 scratch;
|
u32 scratch;
|
||||||
|
|
||||||
sdhci_pci_remove_own_cd(slot);
|
|
||||||
|
|
||||||
dead = 0;
|
dead = 0;
|
||||||
scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS);
|
scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS);
|
||||||
if (scratch == (u32)-1)
|
if (scratch == (u32)-1)
|
||||||
@@ -2277,9 +2138,6 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
|
|||||||
if (slot->chip->fixes && slot->chip->fixes->remove_slot)
|
if (slot->chip->fixes && slot->chip->fixes->remove_slot)
|
||||||
slot->chip->fixes->remove_slot(slot, dead);
|
slot->chip->fixes->remove_slot(slot, dead);
|
||||||
|
|
||||||
if (slot->data && slot->data->cleanup)
|
|
||||||
slot->data->cleanup(slot->data);
|
|
||||||
|
|
||||||
sdhci_free_host(slot->host);
|
sdhci_free_host(slot->host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/mmc/sdhci-pci-data.h>
|
|
||||||
|
|
||||||
struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, int slotno);
|
|
||||||
EXPORT_SYMBOL_GPL(sdhci_pci_get_data);
|
|
||||||
@@ -489,7 +489,7 @@ static void sdhci_pci_o2_enable_msi(struct sdhci_pci_chip *chip,
|
|||||||
|
|
||||||
ret = pci_find_capability(chip->pdev, PCI_CAP_ID_MSI);
|
ret = pci_find_capability(chip->pdev, PCI_CAP_ID_MSI);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
pr_info("%s: unsupport msi, use INTx irq\n",
|
pr_info("%s: unsupported MSI, use INTx irq\n",
|
||||||
mmc_hostname(host->mmc));
|
mmc_hostname(host->mmc));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,11 +156,6 @@ struct sdhci_pci_fixes {
|
|||||||
struct sdhci_pci_slot {
|
struct sdhci_pci_slot {
|
||||||
struct sdhci_pci_chip *chip;
|
struct sdhci_pci_chip *chip;
|
||||||
struct sdhci_host *host;
|
struct sdhci_host *host;
|
||||||
struct sdhci_pci_data *data;
|
|
||||||
|
|
||||||
int rst_n_gpio;
|
|
||||||
int cd_gpio;
|
|
||||||
int cd_irq;
|
|
||||||
|
|
||||||
int cd_idx;
|
int cd_idx;
|
||||||
bool cd_override_level;
|
bool cd_override_level;
|
||||||
|
|||||||
@@ -791,4 +791,3 @@ module_platform_driver(sdhci_s3c_driver);
|
|||||||
MODULE_DESCRIPTION("Samsung SDHCI (HSMMC) glue");
|
MODULE_DESCRIPTION("Samsung SDHCI (HSMMC) glue");
|
||||||
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
|
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
MODULE_ALIAS("platform:s3c-sdhci");
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/highmem.h>
|
#include <linux/highmem.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
@@ -39,6 +40,9 @@
|
|||||||
#define SDHCI_SPRD_BIT_POSRD_DLY_INV BIT(21)
|
#define SDHCI_SPRD_BIT_POSRD_DLY_INV BIT(21)
|
||||||
#define SDHCI_SPRD_BIT_NEGRD_DLY_INV BIT(29)
|
#define SDHCI_SPRD_BIT_NEGRD_DLY_INV BIT(29)
|
||||||
|
|
||||||
|
#define SDHCI_SPRD_REG_32_DLL_STS0 0x210
|
||||||
|
#define SDHCI_SPRD_DLL_LOCKED BIT(18)
|
||||||
|
|
||||||
#define SDHCI_SPRD_REG_32_BUSY_POSI 0x250
|
#define SDHCI_SPRD_REG_32_BUSY_POSI 0x250
|
||||||
#define SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN BIT(25)
|
#define SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN BIT(25)
|
||||||
#define SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN BIT(24)
|
#define SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN BIT(24)
|
||||||
@@ -256,6 +260,15 @@ static void sdhci_sprd_enable_phy_dll(struct sdhci_host *host)
|
|||||||
sdhci_writel(host, tmp, SDHCI_SPRD_REG_32_DLL_CFG);
|
sdhci_writel(host, tmp, SDHCI_SPRD_REG_32_DLL_CFG);
|
||||||
/* wait 1ms */
|
/* wait 1ms */
|
||||||
usleep_range(1000, 1250);
|
usleep_range(1000, 1250);
|
||||||
|
|
||||||
|
if (read_poll_timeout(sdhci_readl, tmp, (tmp & SDHCI_SPRD_DLL_LOCKED),
|
||||||
|
2000, USEC_PER_SEC, false, host, SDHCI_SPRD_REG_32_DLL_STS0)) {
|
||||||
|
pr_err("%s: DLL locked fail!\n", mmc_hostname(host->mmc));
|
||||||
|
pr_info("%s: DLL_STS0 : 0x%x, DLL_CFG : 0x%x\n",
|
||||||
|
mmc_hostname(host->mmc),
|
||||||
|
sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_STS0),
|
||||||
|
sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_CFG));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdhci_sprd_set_clock(struct sdhci_host *host, unsigned int clock)
|
static void sdhci_sprd_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||||
|
|||||||
@@ -930,7 +930,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd,
|
|||||||
struct mmc_data *data;
|
struct mmc_data *data;
|
||||||
unsigned target_timeout, current_timeout;
|
unsigned target_timeout, current_timeout;
|
||||||
|
|
||||||
*too_big = true;
|
*too_big = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the host controller provides us with an incorrect timeout
|
* If the host controller provides us with an incorrect timeout
|
||||||
@@ -941,7 +941,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd,
|
|||||||
if (host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL)
|
if (host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL)
|
||||||
return host->max_timeout_count;
|
return host->max_timeout_count;
|
||||||
|
|
||||||
/* Unspecified command, asume max */
|
/* Unspecified command, assume max */
|
||||||
if (cmd == NULL)
|
if (cmd == NULL)
|
||||||
return host->max_timeout_count;
|
return host->max_timeout_count;
|
||||||
|
|
||||||
@@ -968,17 +968,14 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd,
|
|||||||
while (current_timeout < target_timeout) {
|
while (current_timeout < target_timeout) {
|
||||||
count++;
|
count++;
|
||||||
current_timeout <<= 1;
|
current_timeout <<= 1;
|
||||||
if (count > host->max_timeout_count)
|
if (count > host->max_timeout_count) {
|
||||||
|
if (!(host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT))
|
||||||
|
DBG("Too large timeout 0x%x requested for CMD%d!\n",
|
||||||
|
count, cmd->opcode);
|
||||||
|
count = host->max_timeout_count;
|
||||||
|
*too_big = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count > host->max_timeout_count) {
|
|
||||||
if (!(host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT))
|
|
||||||
DBG("Too large timeout 0x%x requested for CMD%d!\n",
|
|
||||||
count, cmd->opcode);
|
|
||||||
count = host->max_timeout_count;
|
|
||||||
} else {
|
|
||||||
*too_big = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
@@ -2428,6 +2425,25 @@ static int sdhci_get_cd(struct mmc_host *mmc)
|
|||||||
return !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT);
|
return !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int sdhci_get_cd_nogpio(struct mmc_host *mmc)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
unsigned long flags;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
|
if (host->flags & SDHCI_DEVICE_DEAD)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT);
|
||||||
|
out:
|
||||||
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(sdhci_get_cd_nogpio);
|
||||||
|
|
||||||
static int sdhci_check_ro(struct sdhci_host *host)
|
static int sdhci_check_ro(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
@@ -3238,7 +3254,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
|
|||||||
-ETIMEDOUT :
|
-ETIMEDOUT :
|
||||||
-EILSEQ;
|
-EILSEQ;
|
||||||
|
|
||||||
if (mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
|
if (sdhci_auto_cmd23(host, mrq)) {
|
||||||
mrq->sbc->error = err;
|
mrq->sbc->error = err;
|
||||||
__sdhci_finish_mrq(host, mrq);
|
__sdhci_finish_mrq(host, mrq);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -750,7 +750,6 @@ static inline void *sdhci_priv(struct sdhci_host *host)
|
|||||||
return host->private;
|
return host->private;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sdhci_card_detect(struct sdhci_host *host);
|
|
||||||
void __sdhci_read_caps(struct sdhci_host *host, const u16 *ver,
|
void __sdhci_read_caps(struct sdhci_host *host, const u16 *ver,
|
||||||
const u32 *caps, const u32 *caps1);
|
const u32 *caps, const u32 *caps1);
|
||||||
int sdhci_setup_host(struct sdhci_host *host);
|
int sdhci_setup_host(struct sdhci_host *host);
|
||||||
@@ -775,6 +774,7 @@ void sdhci_set_power_and_bus_voltage(struct sdhci_host *host,
|
|||||||
unsigned short vdd);
|
unsigned short vdd);
|
||||||
void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
|
void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
|
||||||
unsigned short vdd);
|
unsigned short vdd);
|
||||||
|
int sdhci_get_cd_nogpio(struct mmc_host *mmc);
|
||||||
void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq);
|
void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq);
|
||||||
int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
|
int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
|
||||||
void sdhci_set_bus_width(struct sdhci_host *host, int width);
|
void sdhci_set_bus_width(struct sdhci_host *host, int width);
|
||||||
|
|||||||
@@ -162,6 +162,9 @@ struct mmc_host_ops {
|
|||||||
/* Prepare HS400 target operating frequency depending host driver */
|
/* Prepare HS400 target operating frequency depending host driver */
|
||||||
int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
|
int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
|
||||||
|
|
||||||
|
/* Execute HS400 tuning depending host driver */
|
||||||
|
int (*execute_hs400_tuning)(struct mmc_host *host, struct mmc_card *card);
|
||||||
|
|
||||||
/* Prepare switch to DDR during the HS400 init sequence */
|
/* Prepare switch to DDR during the HS400 init sequence */
|
||||||
int (*hs400_prepare_ddr)(struct mmc_host *host);
|
int (*hs400_prepare_ddr)(struct mmc_host *host);
|
||||||
|
|
||||||
@@ -634,5 +637,6 @@ static inline enum dma_data_direction mmc_get_dma_dir(struct mmc_data *data)
|
|||||||
|
|
||||||
int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error);
|
int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error);
|
||||||
int mmc_send_abort_tuning(struct mmc_host *host, u32 opcode);
|
int mmc_send_abort_tuning(struct mmc_host *host, u32 opcode);
|
||||||
|
int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd);
|
||||||
|
|
||||||
#endif /* LINUX_MMC_HOST_H */
|
#endif /* LINUX_MMC_HOST_H */
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
|
||||||
#ifndef LINUX_MMC_SDHCI_PCI_DATA_H
|
|
||||||
#define LINUX_MMC_SDHCI_PCI_DATA_H
|
|
||||||
|
|
||||||
struct pci_dev;
|
|
||||||
|
|
||||||
struct sdhci_pci_data {
|
|
||||||
struct pci_dev *pdev;
|
|
||||||
int slotno;
|
|
||||||
int rst_n_gpio; /* Set to -EINVAL if unused */
|
|
||||||
int cd_gpio; /* Set to -EINVAL if unused */
|
|
||||||
int (*setup)(struct sdhci_pci_data *data);
|
|
||||||
void (*cleanup)(struct sdhci_pci_data *data);
|
|
||||||
};
|
|
||||||
|
|
||||||
extern struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev,
|
|
||||||
int slotno);
|
|
||||||
#endif
|
|
||||||
Reference in New Issue
Block a user