forked from Minki/linux
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 -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmF/zfQXHHVsZi5oYW5z c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCk9HxAAj2hKiYcYz6xOAPEUrXLu5/CD A9TRQmatURQTk7WioLCtTvuitkz/gjtJNuSWJDgbjlePfIeXugR0WSTf4j/V1Opb EoCy//aRFKC62MUMhiS0KDITU7xgfV9k0Tx4drjF1hw4Gg355pmSJaGLh6sGqZxj Rxk3A/evd/A2wyoQKHK9/hn2BshxClRaAK/K8Y4zDv0iXUkNXETF18rDxdAHtleb tDOBLQkgKw71GbRS6ln3ueo7LFwNmTsKjlFdQ0dYJ15i1f5QtYGQ1OSiVT6PLN04 PIX9CfkTOqFq+HoOPKudbS63Fz0YNhOMk/bY9ZV1fRQRvh+R03cvjy9Wv+xdvPcU sxbT5+ci95pBXO7WDqPMSnlvVi004m8xu+lMbFhLd7lZO4unjNuMw69m2hUPon5M oz/bOQ/7FJ9sWQ1J6PFQ/5VB+d0gs4ySxJusQ0shkTfhghHyPa+ezppcuOwbmaof Ac2R1J+sfNVn2za1mAydGEED/+cLi88GPR4FSEJbHxKSjogqz+A5pFKORCKN0xki HhB0moTODDPe3/jzTlDMyqv1FSGea8d3hWvxNr1GTGolGQ/P+42HugOS1Uv5Hz8C ufPHzSMZ6bXaeOfdHRDTV4OBiiPSrSGllNdfk8gkWj5FuowpszBRT6C3Tsb8aSUz NL0Ve3qqQa8arH8H+Wg= =W+gH -----END PGP SIGNATURE----- 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:
commit
8a73c77c80
@ -88,6 +88,12 @@ properties:
|
||||
description:
|
||||
For this device it is strongly suggested to include
|
||||
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:
|
||||
maxItems: 1
|
||||
@ -153,7 +159,6 @@ properties:
|
||||
The MIO bank number in which the command and data lines are configured.
|
||||
|
||||
dependencies:
|
||||
clock-output-names: [ '#clock-cells' ]
|
||||
'#clock-cells': [ clock-output-names ]
|
||||
|
||||
required:
|
||||
@ -301,3 +306,22 @@ examples:
|
||||
<&scmi_clk KEEM_BAY_PSS_SD0>;
|
||||
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:
|
||||
items:
|
||||
- enum:
|
||||
- microchip,mpfs-sd4hc
|
||||
- socionext,uniphier-sd4hc
|
||||
- const: cdns,sd4hc
|
||||
|
||||
|
@ -34,6 +34,7 @@ properties:
|
||||
- fsl,imx6ull-usdhc
|
||||
- fsl,imx7d-usdhc
|
||||
- fsl,imx7ulp-usdhc
|
||||
- nxp,s32g2-usdhc
|
||||
- items:
|
||||
- enum:
|
||||
- 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
|
||||
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:
|
||||
- reg
|
||||
|
||||
|
@ -119,6 +119,18 @@ properties:
|
||||
If present, HS400 command responses are sampled on rising 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:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
|
@ -13,6 +13,7 @@ Required properties:
|
||||
string is added to support this change - "qcom,sdhci-msm-v5".
|
||||
full compatible strings with SoC and version:
|
||||
"qcom,apq8084-sdhci", "qcom,sdhci-msm-v4"
|
||||
"qcom,msm8226-sdhci", "qcom,sdhci-msm-v4"
|
||||
"qcom,msm8974-sdhci", "qcom,sdhci-msm-v4"
|
||||
"qcom,msm8916-sdhci", "qcom,sdhci-msm-v4"
|
||||
"qcom,msm8992-sdhci", "qcom,sdhci-msm-v4"
|
||||
|
@ -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.
|
||||
|
||||
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,am335-sdhci" for am335x 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"
|
||||
and "rx" for TX and RX DMA requests, respectively.
|
||||
|
||||
Deprecated properties:
|
||||
- ti,non-removable: Compatible with the generic non-removable property
|
||||
|
||||
Example:
|
||||
mmc1: mmc@4809c000 {
|
||||
compatible = "ti,dra7-sdhci";
|
||||
|
@ -2,10 +2,10 @@
|
||||
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:
|
||||
|
||||
https://git.kernel.org/cgit/linux/kernel/git/cjb/mmc-utils.git/
|
||||
https://git.kernel.org/pub/scm/utils/mmc/mmc-utils.git
|
||||
|
||||
Functions
|
||||
=========
|
||||
|
@ -16839,7 +16839,6 @@ M: Adrian Hunter <adrian.hunter@intel.com>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/mmc/host/sdhci*
|
||||
F: include/linux/mmc/sdhci*
|
||||
|
||||
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) MICROCHIP DRIVER
|
||||
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->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*/
|
||||
dbg("device page %d size isn't supported", msb->page_size);
|
||||
return -EINVAL;
|
||||
@ -2156,10 +2156,14 @@ static int msb_init_disk(struct memstick_dev *card)
|
||||
set_disk_ro(msb->disk, 1);
|
||||
|
||||
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");
|
||||
return 0;
|
||||
|
||||
out_cleanup_disk:
|
||||
blk_cleanup_disk(msb->disk);
|
||||
out_free_tag_set:
|
||||
blk_mq_free_tag_set(&msb->tag_set);
|
||||
out_release_id:
|
||||
|
@ -1239,10 +1239,14 @@ static int mspro_block_init_disk(struct memstick_dev *card)
|
||||
set_capacity(msb->disk, 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;
|
||||
return 0;
|
||||
|
||||
out_cleanup_disk:
|
||||
blk_cleanup_disk(msb->disk);
|
||||
out_free_tag_set:
|
||||
blk_mq_free_tag_set(&msb->tag_set);
|
||||
out_release_id:
|
||||
|
@ -882,7 +882,7 @@ static struct memstick_host *jmb38x_ms_alloc_host(struct jmb38x_ms *jm, int cnt)
|
||||
|
||||
iounmap(host->addr);
|
||||
err_out_free:
|
||||
kfree(msh);
|
||||
memstick_free_host(msh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -927,8 +927,7 @@ static int jmb38x_ms_probe(struct pci_dev *pdev,
|
||||
goto err_out_int;
|
||||
}
|
||||
|
||||
jm = kzalloc(sizeof(struct jmb38x_ms)
|
||||
+ cnt * sizeof(struct memstick_host *), GFP_KERNEL);
|
||||
jm = kzalloc(struct_size(jm, hosts, cnt), GFP_KERNEL);
|
||||
if (!jm) {
|
||||
rc = -ENOMEM;
|
||||
goto err_out_int;
|
||||
|
@ -838,15 +838,15 @@ static void r592_remove(struct pci_dev *pdev)
|
||||
}
|
||||
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);
|
||||
iounmap(dev->mmio);
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
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
|
||||
|
@ -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: */
|
||||
if (area_type == MMC_BLK_DATA_AREA_MAIN)
|
||||
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;
|
||||
|
||||
err_cleanup_queue:
|
||||
blk_cleanup_queue(md->disk->queue);
|
||||
blk_mq_free_tag_set(&md->queue.tag_set);
|
||||
err_kfree:
|
||||
kfree(md);
|
||||
out:
|
||||
|
@ -1224,6 +1224,14 @@ static int mmc_select_hs400(struct mmc_card *card)
|
||||
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
|
||||
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)
|
||||
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_bus_test(struct mmc_card *card, u8 bus_width);
|
||||
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);
|
||||
bool mmc_prepare_busy_cmd(struct mmc_host *host, struct mmc_command *cmd,
|
||||
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)
|
||||
{
|
||||
struct mmc_gpio *ctx = devm_kzalloc(host->parent,
|
||||
sizeof(*ctx), GFP_KERNEL);
|
||||
const char *devname = dev_name(host->parent);
|
||||
struct mmc_gpio *ctx;
|
||||
|
||||
if (ctx) {
|
||||
ctx->cd_debounce_delay_ms = 200;
|
||||
ctx->cd_label = devm_kasprintf(host->parent, GFP_KERNEL,
|
||||
"%s cd", dev_name(host->parent));
|
||||
if (!ctx->cd_label)
|
||||
return -ENOMEM;
|
||||
ctx->ro_label = devm_kasprintf(host->parent, GFP_KERNEL,
|
||||
"%s ro", dev_name(host->parent));
|
||||
if (!ctx->ro_label)
|
||||
return -ENOMEM;
|
||||
host->slot.handler_priv = ctx;
|
||||
host->slot.cd_irq = -EINVAL;
|
||||
}
|
||||
ctx = devm_kzalloc(host->parent, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
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)
|
||||
@ -178,6 +178,10 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
|
||||
if (IS_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) {
|
||||
ret = gpiod_set_debounce(desc, debounce);
|
||||
if (ret < 0)
|
||||
@ -226,6 +230,10 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
||||
if (IS_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) {
|
||||
ret = gpiod_set_debounce(desc, debounce);
|
||||
if (ret < 0)
|
||||
|
@ -315,15 +315,17 @@ config MMC_SDHCI_TEGRA
|
||||
If unsure, say N.
|
||||
|
||||
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 PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
|
||||
help
|
||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||
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.
|
||||
|
||||
|
@ -14,7 +14,6 @@ obj-$(CONFIG_MMC_SDHCI) += sdhci.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-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_PXAV3) += sdhci-pxav3.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);
|
||||
|
||||
if (timed_out) {
|
||||
pr_err("%s: cqhci: timeout for tag %d\n",
|
||||
mmc_hostname(mmc), tag);
|
||||
pr_err("%s: cqhci: timeout for tag %d, qcnt %d\n",
|
||||
mmc_hostname(mmc), tag, cq_host->qcnt);
|
||||
cqhci_dumpregs(cq_host);
|
||||
}
|
||||
|
||||
|
@ -442,14 +442,14 @@ static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
|
||||
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;
|
||||
u8 __c;
|
||||
s8 i, loc = -1;
|
||||
|
||||
for (i = 0; i < iter; i++) {
|
||||
__c = ror8(candiates, i);
|
||||
__c = ror8(candidates, i);
|
||||
if ((__c & 0xc7) == 0xc7) {
|
||||
loc = i;
|
||||
goto out;
|
||||
@ -457,7 +457,7 @@ static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates)
|
||||
}
|
||||
|
||||
for (i = 0; i < iter; i++) {
|
||||
__c = ror8(candiates, i);
|
||||
__c = ror8(candidates, i);
|
||||
if ((__c & 0x83) == 0x83) {
|
||||
loc = i;
|
||||
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 are candiates values and don't find bset clk sample value,
|
||||
* then use a first candiates clock sample value.
|
||||
* If there are candidates values and don't find bset clk sample value,
|
||||
* then use a first candidates clock sample value.
|
||||
*/
|
||||
for (i = 0; i < iter; i++) {
|
||||
__c = ror8(candiates, i);
|
||||
__c = ror8(candidates, i);
|
||||
if ((__c & 0x1) == 0x1) {
|
||||
loc = i;
|
||||
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_exynos_priv_data *priv = host->priv;
|
||||
struct mmc_host *mmc = slot->mmc;
|
||||
u8 start_smpl, smpl, candiates = 0;
|
||||
u8 start_smpl, smpl, candidates = 0;
|
||||
s8 found;
|
||||
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);
|
||||
|
||||
if (!mmc_send_tuning(mmc, opcode, NULL))
|
||||
candiates |= (1 << smpl);
|
||||
candidates |= (1 << smpl);
|
||||
|
||||
} while (start_smpl != smpl);
|
||||
|
||||
found = dw_mci_exynos_get_best_clksmpl(candiates);
|
||||
found = dw_mci_exynos_get_best_clksmpl(candidates);
|
||||
if (found >= 0) {
|
||||
dw_mci_exynos_set_clksmpl(host, found);
|
||||
priv->tuned_sample = found;
|
||||
} else {
|
||||
ret = -EIO;
|
||||
dev_warn(&mmc->class_dev,
|
||||
"There is no candiates value about clksmpl!\n");
|
||||
"There is no candidates value about clksmpl!\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -1611,37 +1611,32 @@ static void dw_mci_hw_reset(struct mmc_host *mmc)
|
||||
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;
|
||||
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
|
||||
* description of the CLKENA register we should disable low power mode
|
||||
* 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 ||
|
||||
card->type == MMC_TYPE_SD_COMBO) {
|
||||
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 (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);
|
||||
}
|
||||
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 *host = slot->host;
|
||||
|
||||
dw_mci_prepare_sdio_irq(slot, enb);
|
||||
__dw_mci_enable_sdio_irq(slot, enb);
|
||||
|
||||
/* 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,
|
||||
.card_busy = dw_mci_card_busy,
|
||||
.start_signal_voltage_switch = dw_mci_switch_voltage,
|
||||
.init_card = dw_mci_init_card,
|
||||
.prepare_hs400_tuning = dw_mci_prepare_hs400_tuning,
|
||||
};
|
||||
|
||||
@ -2086,7 +2081,8 @@ static void dw_mci_tasklet_func(struct tasklet_struct *t)
|
||||
* delayed. Allowing the transfer to take place
|
||||
* avoids races and keeps things simple.
|
||||
*/
|
||||
if (err != -ETIMEDOUT) {
|
||||
if (err != -ETIMEDOUT &&
|
||||
host->dir_status == DW_MCI_RECV_STATUS) {
|
||||
state = STATE_SENDING_DATA;
|
||||
continue;
|
||||
}
|
||||
|
@ -1394,6 +1394,10 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
|
||||
} else if (host->variant->busy_timeout && busy_resp &&
|
||||
status & MCI_DATATIMEOUT) {
|
||||
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;
|
||||
} else {
|
||||
cmd->resp[0] = readl(base + MMCIRESPONSE0);
|
||||
|
@ -566,37 +566,37 @@ static int moxart_probe(struct platform_device *pdev)
|
||||
if (!mmc) {
|
||||
dev_err(dev, "mmc_alloc_host failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
goto out_mmc;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(node, 0, &res_mmc);
|
||||
if (ret) {
|
||||
dev_err(dev, "of_address_to_resource failed\n");
|
||||
goto out;
|
||||
goto out_mmc;
|
||||
}
|
||||
|
||||
irq = irq_of_parse_and_map(node, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(dev, "irq_of_parse_and_map failed\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
goto out_mmc;
|
||||
}
|
||||
|
||||
clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
ret = PTR_ERR(clk);
|
||||
goto out;
|
||||
goto out_mmc;
|
||||
}
|
||||
|
||||
reg_mmc = devm_ioremap_resource(dev, &res_mmc);
|
||||
if (IS_ERR(reg_mmc)) {
|
||||
ret = PTR_ERR(reg_mmc);
|
||||
goto out;
|
||||
goto out_mmc;
|
||||
}
|
||||
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret)
|
||||
goto out;
|
||||
goto out_mmc;
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
host->mmc = mmc;
|
||||
@ -621,6 +621,14 @@ static int moxart_probe(struct platform_device *pdev)
|
||||
ret = -EPROBE_DEFER;
|
||||
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");
|
||||
host->have_dma = false;
|
||||
} else {
|
||||
@ -675,6 +683,11 @@ static int moxart_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
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)
|
||||
mmc_free_host(mmc);
|
||||
return ret;
|
||||
@ -687,9 +700,9 @@ static int moxart_remove(struct platform_device *pdev)
|
||||
|
||||
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);
|
||||
if (!IS_ERR(host->dma_chan_rx))
|
||||
if (!IS_ERR_OR_NULL(host->dma_chan_rx))
|
||||
dma_release_channel(host->dma_chan_rx);
|
||||
mmc_remove_host(mmc);
|
||||
mmc_free_host(mmc);
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of_address.h>
|
||||
@ -258,6 +259,7 @@
|
||||
#define MSDC_PAD_TUNE_RD_SEL (0x1 << 13) /* 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_DLY2 (0x1f << 7) /* 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_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_TMO (0x1 << 1)
|
||||
#define REQ_DAT_ERR (0x1 << 2)
|
||||
@ -448,11 +455,13 @@ struct msdc_host {
|
||||
bool vqmmc_enabled;
|
||||
u32 latch_ck;
|
||||
u32 hs400_ds_delay;
|
||||
u32 hs400_ds_dly3;
|
||||
u32 hs200_cmd_int_delay; /* cmd internal delay for HS200/SDR104 */
|
||||
u32 hs400_cmd_int_delay; /* cmd internal delay for HS400 */
|
||||
bool hs400_cmd_resp_sel_rising;
|
||||
/* cmd response sample selection for HS400 */
|
||||
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 cqhci; /* support eMMC hw cmdq */
|
||||
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,
|
||||
struct mmc_request *mrq, struct mmc_command *cmd)
|
||||
struct mmc_command *cmd)
|
||||
{
|
||||
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
|
||||
*/
|
||||
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);
|
||||
|
||||
host->cmd_rsp = resp;
|
||||
@ -1043,8 +1052,8 @@ static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host,
|
||||
return rawcmd;
|
||||
}
|
||||
|
||||
static void msdc_start_data(struct msdc_host *host, struct mmc_request *mrq,
|
||||
struct mmc_command *cmd, struct mmc_data *data)
|
||||
static void msdc_start_data(struct msdc_host *host, struct mmc_command *cmd,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
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,
|
||||
struct mmc_command *cmd, struct mmc_data *data)
|
||||
static void msdc_track_cmd_data(struct msdc_host *host, struct mmc_command *cmd)
|
||||
{
|
||||
if (host->error)
|
||||
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;
|
||||
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)
|
||||
msdc_unprepare_data(host, mrq->data);
|
||||
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 (events & MSDC_INT_CMDTMO ||
|
||||
(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
|
||||
* 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 &&
|
||||
!(cmd->error == -EILSEQ &&
|
||||
(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))
|
||||
msdc_request_done(host, mrq);
|
||||
else if (cmd == mrq->sbc)
|
||||
@ -1295,7 +1305,7 @@ static void msdc_cmd_next(struct msdc_host *host,
|
||||
else if (!cmd->data)
|
||||
msdc_request_done(host, mrq);
|
||||
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)
|
||||
@ -2251,6 +2261,67 @@ static int msdc_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
struct msdc_host *host = mmc_priv(mmc);
|
||||
unsigned int val = 0;
|
||||
|
||||
/* disable cmdq irq */
|
||||
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) {
|
||||
sdr_set_field(host->base + MSDC_DMA_CTRL,
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -2377,6 +2452,7 @@ static const struct mmc_host_ops mt_msdc_ops = {
|
||||
.card_busy = msdc_card_busy,
|
||||
.execute_tuning = msdc_execute_tuning,
|
||||
.prepare_hs400_tuning = msdc_prepare_hs400_tuning,
|
||||
.execute_hs400_tuning = msdc_execute_hs400_tuning,
|
||||
.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",
|
||||
&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",
|
||||
&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);
|
||||
|
||||
static void mxs_mmc_regulator_disable(void *regulator)
|
||||
{
|
||||
regulator_disable(regulator);
|
||||
}
|
||||
|
||||
static int mxs_mmc_probe(struct platform_device *pdev)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
|
@ -702,11 +702,6 @@ static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)
|
||||
|
||||
#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)
|
||||
{
|
||||
}
|
||||
@ -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
|
||||
* general e.g. by expanding the DT bindings of child nodes
|
||||
* 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");
|
||||
@ -2086,6 +2081,7 @@ static int omap_hsmmc_resume(struct device *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int omap_hsmmc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(omap_hsmmc_suspend, omap_hsmmc_resume)
|
||||
.runtime_suspend = omap_hsmmc_runtime_suspend,
|
||||
.runtime_resume = omap_hsmmc_runtime_resume,
|
||||
SET_RUNTIME_PM_OPS(omap_hsmmc_runtime_suspend, omap_hsmmc_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
int gpio_cd = mmc_gpio_get_cd(mmc);
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
if (!gpio_cd)
|
||||
return 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;
|
||||
return sdhci_get_cd_nogpio(mmc);
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
/* ERR004536 is not applicable for the IP */
|
||||
#define ESDHC_FLAG_SKIP_ERR004536 BIT(17)
|
||||
|
||||
enum wp_types {
|
||||
ESDHC_WP_NONE, /* no WP, neither controller nor gpio */
|
||||
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,
|
||||
};
|
||||
|
||||
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 = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||
| 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,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, },
|
||||
{ .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx8mm_data, },
|
||||
{ .compatible = "nxp,s32g2-usdhc", .data = &usdhc_s32g2_data, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
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
|
||||
* TO1.1, it's harmless for MX6SL
|
||||
*/
|
||||
writel(readl(host->ioaddr + 0x6c) & ~BIT(7),
|
||||
host->ioaddr + 0x6c);
|
||||
if (!(imx_data->socdata->flags & ESDHC_FLAG_SKIP_ERR004536)) {
|
||||
writel(readl(host->ioaddr + 0x6c) & ~BIT(7),
|
||||
host->ioaddr + 0x6c);
|
||||
}
|
||||
|
||||
/* disable DLL_CTRL delay line settings */
|
||||
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,
|
||||
};
|
||||
|
||||
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 = {
|
||||
.baseclkfreq = { .reg = 0x0, .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,
|
||||
};
|
||||
|
||||
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
|
||||
/**
|
||||
* 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,
|
||||
};
|
||||
|
||||
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 = {
|
||||
.ops = &sdhci_arasan_cqe_ops,
|
||||
.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",
|
||||
.data = &intel_keembay_sdio_data,
|
||||
},
|
||||
{
|
||||
.compatible = "intel,thunderbay-sdhci-5.1",
|
||||
.data = &sdhci_arasan_thunderbay_data,
|
||||
},
|
||||
/* Generic compatible below here */
|
||||
{
|
||||
.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") ||
|
||||
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_support64b(host, 0x0);
|
||||
|
||||
|
@ -12,8 +12,10 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/sys_soc.h>
|
||||
@ -21,7 +23,14 @@
|
||||
|
||||
#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_DMA_MASTER BIT(20)
|
||||
#define CON_DDR BIT(19)
|
||||
@ -31,20 +40,20 @@
|
||||
#define CON_INIT BIT(1)
|
||||
#define CON_OD BIT(0)
|
||||
|
||||
#define SDHCI_OMAP_DLL 0x0134
|
||||
#define SDHCI_OMAP_DLL 0x34
|
||||
#define DLL_SWT BIT(20)
|
||||
#define DLL_FORCE_SR_C_SHIFT 13
|
||||
#define DLL_FORCE_SR_C_MASK (0x7f << DLL_FORCE_SR_C_SHIFT)
|
||||
#define DLL_FORCE_VALUE BIT(12)
|
||||
#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_DATI BIT(1)
|
||||
|
||||
#define SDHCI_OMAP_HCTL 0x228
|
||||
#define SDHCI_OMAP_HCTL 0x128
|
||||
#define HCTL_SDBP BIT(8)
|
||||
#define HCTL_SDVS_SHIFT 9
|
||||
#define HCTL_SDVS_MASK (0x7 << HCTL_SDVS_SHIFT)
|
||||
@ -52,26 +61,28 @@
|
||||
#define HCTL_SDVS_30 (0x6 << 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_CLKD_SHIFT 6
|
||||
#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 SDHCI_OMAP_AC12 0x23c
|
||||
#define SDHCI_OMAP_ISE 0x138
|
||||
|
||||
#define SDHCI_OMAP_AC12 0x13c
|
||||
#define AC12_V1V8_SIGEN BIT(19)
|
||||
#define AC12_SCLK_SEL BIT(23)
|
||||
|
||||
#define SDHCI_OMAP_CAPA 0x240
|
||||
#define SDHCI_OMAP_CAPA 0x140
|
||||
#define CAPA_VS33 BIT(24)
|
||||
#define CAPA_VS30 BIT(25)
|
||||
#define CAPA_VS18 BIT(26)
|
||||
|
||||
#define SDHCI_OMAP_CAPA2 0x0244
|
||||
#define SDHCI_OMAP_CAPA2 0x144
|
||||
#define CAPA2_TSDR50 BIT(13)
|
||||
|
||||
#define SDHCI_OMAP_TIMEOUT 1 /* 1 msec */
|
||||
@ -89,7 +100,8 @@
|
||||
#define SDHCI_OMAP_SPECIAL_RESET BIT(1)
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
@ -107,12 +119,19 @@ struct sdhci_omap_host {
|
||||
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state **pinctrl_state;
|
||||
int wakeirq;
|
||||
bool is_tuning;
|
||||
|
||||
/* Offset for omap specific registers from base */
|
||||
int omap_offset;
|
||||
|
||||
/* Omap specific context save */
|
||||
u32 con;
|
||||
u32 hctl;
|
||||
u32 sysctl;
|
||||
u32 capa;
|
||||
u32 ie;
|
||||
u32 ise;
|
||||
};
|
||||
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
@ -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,
|
||||
unsigned int iov)
|
||||
unsigned int iov_pbias)
|
||||
{
|
||||
int ret;
|
||||
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;
|
||||
|
||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||
ret = regulator_set_voltage(mmc->supply.vqmmc, iov, iov);
|
||||
if (ret) {
|
||||
/* Pick the right voltage to allow 3.0V for 3.3V nominal PBIAS */
|
||||
ret = mmc_regulator_set_vqmmc(mmc, &mmc->ios);
|
||||
if (ret < 0) {
|
||||
dev_err(mmc_dev(mmc), "vqmmc set voltage failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = sdhci_omap_set_pbias(omap_host, true, iov);
|
||||
ret = sdhci_omap_set_pbias(omap_host, true, iov_pbias);
|
||||
if (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,
|
||||
unsigned char signal_voltage)
|
||||
{
|
||||
u32 reg;
|
||||
u32 reg, capa;
|
||||
ktime_t timeout;
|
||||
|
||||
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL);
|
||||
reg &= ~HCTL_SDVS_MASK;
|
||||
|
||||
if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
|
||||
reg |= HCTL_SDVS_33;
|
||||
else
|
||||
switch (signal_voltage) {
|
||||
case MMC_SIGNAL_VOLTAGE_330:
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
||||
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) {
|
||||
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
|
||||
if (!(reg & CAPA_VS33))
|
||||
if (!(reg & (CAPA_VS30 | CAPA_VS33)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (reg & CAPA_VS30)
|
||||
iov = IOV_3V0;
|
||||
else
|
||||
iov = IOV_3V3;
|
||||
|
||||
sdhci_omap_conf_bus_power(omap_host, ios->signal_voltage);
|
||||
|
||||
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
|
||||
reg &= ~AC12_V1V8_SIGEN;
|
||||
sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
|
||||
|
||||
iov = IOV_3V3;
|
||||
} else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
|
||||
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
|
||||
if (!(reg & CAPA_VS18))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
iov = IOV_1V8;
|
||||
|
||||
sdhci_omap_conf_bus_power(omap_host, ios->signal_voltage);
|
||||
|
||||
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
|
||||
reg |= AC12_V1V8_SIGEN;
|
||||
sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
|
||||
|
||||
iov = IOV_1V8;
|
||||
} else {
|
||||
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;
|
||||
|
||||
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)
|
||||
@ -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);
|
||||
unsigned long limit = MMC_TIMEOUT_US;
|
||||
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 */
|
||||
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),
|
||||
"Timeout waiting on controller reset in %s\n",
|
||||
__func__);
|
||||
return;
|
||||
|
||||
goto restore_sysc;
|
||||
}
|
||||
|
||||
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 |\
|
||||
@ -877,34 +940,73 @@ static struct sdhci_ops sdhci_omap_ops = {
|
||||
.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;
|
||||
int ret = 0;
|
||||
struct device *dev = omap_host->dev;
|
||||
struct regulator *vqmmc;
|
||||
struct regulator *reg;
|
||||
unsigned int caps = 0;
|
||||
|
||||
vqmmc = regulator_get(dev, "vqmmc");
|
||||
if (IS_ERR(vqmmc)) {
|
||||
ret = PTR_ERR(vqmmc);
|
||||
goto reg_put;
|
||||
}
|
||||
reg = regulator_get(dev, name);
|
||||
if (IS_ERR(reg))
|
||||
return ~0U;
|
||||
|
||||
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 */
|
||||
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
|
||||
reg &= ~(CAPA_VS18 | CAPA_VS30 | CAPA_VS33);
|
||||
|
||||
if (regulator_is_supported_voltage(vqmmc, IOV_3V3, IOV_3V3))
|
||||
reg |= CAPA_VS33;
|
||||
if (regulator_is_supported_voltage(vqmmc, IOV_1V8, IOV_1V8))
|
||||
if (caps & SDHCI_CAN_VDD_180)
|
||||
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);
|
||||
|
||||
reg_put:
|
||||
regulator_put(vqmmc);
|
||||
host->caps &= ~mask;
|
||||
host->caps |= caps;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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 = {
|
||||
.omap_offset = 0x100,
|
||||
.offset = 0x200,
|
||||
};
|
||||
|
||||
static const struct sdhci_omap_data am335_data = {
|
||||
.omap_offset = 0x100,
|
||||
.offset = 0x200,
|
||||
.flags = SDHCI_OMAP_SPECIAL_RESET,
|
||||
};
|
||||
|
||||
static const struct sdhci_omap_data am437_data = {
|
||||
.omap_offset = 0x100,
|
||||
.offset = 0x200,
|
||||
.flags = SDHCI_OMAP_SPECIAL_RESET,
|
||||
};
|
||||
|
||||
static const struct sdhci_omap_data dra7_data = {
|
||||
.omap_offset = 0x100,
|
||||
.offset = 0x200,
|
||||
.flags = SDHCI_OMAP_REQUIRE_IODELAY,
|
||||
};
|
||||
|
||||
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,k2g-sdhci", .data = &k2g_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->timing = MMC_TIMING_LEGACY;
|
||||
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->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
|
||||
* as part of pm_runtime_get_sync.
|
||||
*/
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, 50);
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret) {
|
||||
@ -1179,10 +1315,10 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
||||
goto err_rpm_disable;
|
||||
}
|
||||
|
||||
ret = sdhci_omap_set_capabilities(omap_host);
|
||||
ret = sdhci_omap_set_capabilities(host);
|
||||
if (ret) {
|
||||
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 =
|
||||
@ -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.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);
|
||||
|
||||
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. */
|
||||
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);
|
||||
if (ret)
|
||||
goto err_put_sync;
|
||||
goto err_rpm_put;
|
||||
|
||||
ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host);
|
||||
if (ret)
|
||||
@ -1211,15 +1359,38 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
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;
|
||||
|
||||
err_cleanup_host:
|
||||
sdhci_cleanup_host(host);
|
||||
|
||||
err_put_sync:
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
err_rpm_put:
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
err_rpm_disable:
|
||||
pm_runtime_dont_use_autosuspend(dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
err_pltfm_free:
|
||||
@ -1232,64 +1403,81 @@ static int sdhci_omap_remove(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
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_disable(dev);
|
||||
/* Ensure device gets disabled despite userspace sysfs config */
|
||||
pm_runtime_force_suspend(dev);
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
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->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->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_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_pltfm_host *pltfm_host = sdhci_priv(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);
|
||||
|
||||
pinctrl_pm_select_idle_state(dev);
|
||||
|
||||
pm_runtime_force_suspend(dev);
|
||||
|
||||
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_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
pm_runtime_force_resume(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;
|
||||
}
|
||||
#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 = {
|
||||
.probe = sdhci_omap_probe,
|
||||
|
@ -17,8 +17,6 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
@ -26,11 +24,13 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/mmc/sdhci-pci-data.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/dmi.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
#include <asm/iosf_mbi.h>
|
||||
#endif
|
||||
@ -345,73 +345,6 @@ static int pch_hc_probe_slot(struct sdhci_pci_slot *slot)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int gpio_cd = mmc_gpio_get_cd(mmc);
|
||||
@ -2000,21 +1915,6 @@ int sdhci_pci_enable_dma(struct sdhci_host *host)
|
||||
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)
|
||||
{
|
||||
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->host = host;
|
||||
slot->rst_n_gpio = -EINVAL;
|
||||
slot->cd_gpio = -EINVAL;
|
||||
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->ops = chip->fixes && chip->fixes->ops ?
|
||||
chip->fixes->ops :
|
||||
@ -2188,17 +2070,6 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
|
||||
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->slotno = slotno;
|
||||
host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP;
|
||||
@ -2233,15 +2104,11 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
|
||||
if (ret)
|
||||
goto remove;
|
||||
|
||||
sdhci_pci_add_own_cd(slot);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* Note sdhci_pci_add_own_cd() sets slot->cd_gpio to -EINVAL on failure.
|
||||
*/
|
||||
if (chip->fixes && chip->fixes->own_cd_for_runtime_pm &&
|
||||
!gpio_is_valid(slot->cd_gpio) && slot->cd_idx < 0)
|
||||
if (chip->fixes && chip->fixes->own_cd_for_runtime_pm && slot->cd_idx < 0)
|
||||
chip->allow_runtime_pm = false;
|
||||
|
||||
return slot;
|
||||
@ -2251,10 +2118,6 @@ remove:
|
||||
chip->fixes->remove_slot(slot, 0);
|
||||
|
||||
cleanup:
|
||||
if (slot->data && slot->data->cleanup)
|
||||
slot->data->cleanup(slot->data);
|
||||
|
||||
free:
|
||||
sdhci_free_host(host);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
@ -2265,8 +2128,6 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
|
||||
int dead;
|
||||
u32 scratch;
|
||||
|
||||
sdhci_pci_remove_own_cd(slot);
|
||||
|
||||
dead = 0;
|
||||
scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS);
|
||||
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)
|
||||
slot->chip->fixes->remove_slot(slot, dead);
|
||||
|
||||
if (slot->data && slot->data->cleanup)
|
||||
slot->data->cleanup(slot->data);
|
||||
|
||||
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);
|
||||
if (!ret) {
|
||||
pr_info("%s: unsupport msi, use INTx irq\n",
|
||||
pr_info("%s: unsupported MSI, use INTx irq\n",
|
||||
mmc_hostname(host->mmc));
|
||||
return;
|
||||
}
|
||||
|
@ -156,11 +156,6 @@ struct sdhci_pci_fixes {
|
||||
struct sdhci_pci_slot {
|
||||
struct sdhci_pci_chip *chip;
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pci_data *data;
|
||||
|
||||
int rst_n_gpio;
|
||||
int cd_gpio;
|
||||
int cd_irq;
|
||||
|
||||
int cd_idx;
|
||||
bool cd_override_level;
|
||||
|
@ -791,4 +791,3 @@ module_platform_driver(sdhci_s3c_driver);
|
||||
MODULE_DESCRIPTION("Samsung SDHCI (HSMMC) glue");
|
||||
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:s3c-sdhci");
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
@ -39,6 +40,9 @@
|
||||
#define SDHCI_SPRD_BIT_POSRD_DLY_INV BIT(21)
|
||||
#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_BIT_OUTR_CLK_AUTO_EN BIT(25)
|
||||
#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);
|
||||
/* wait 1ms */
|
||||
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)
|
||||
|
@ -930,7 +930,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd,
|
||||
struct mmc_data *data;
|
||||
unsigned target_timeout, current_timeout;
|
||||
|
||||
*too_big = true;
|
||||
*too_big = false;
|
||||
|
||||
/*
|
||||
* 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)
|
||||
return host->max_timeout_count;
|
||||
|
||||
/* Unspecified command, asume max */
|
||||
/* Unspecified command, assume max */
|
||||
if (cmd == NULL)
|
||||
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) {
|
||||
count++;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
@ -2428,6 +2425,25 @@ static int sdhci_get_cd(struct mmc_host *mmc)
|
||||
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)
|
||||
{
|
||||
unsigned long flags;
|
||||
@ -3238,7 +3254,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
|
||||
-ETIMEDOUT :
|
||||
-EILSEQ;
|
||||
|
||||
if (mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
|
||||
if (sdhci_auto_cmd23(host, mrq)) {
|
||||
mrq->sbc->error = err;
|
||||
__sdhci_finish_mrq(host, mrq);
|
||||
return;
|
||||
|
@ -750,7 +750,6 @@ static inline void *sdhci_priv(struct sdhci_host *host)
|
||||
return host->private;
|
||||
}
|
||||
|
||||
void sdhci_card_detect(struct sdhci_host *host);
|
||||
void __sdhci_read_caps(struct sdhci_host *host, const u16 *ver,
|
||||
const u32 *caps, const u32 *caps1);
|
||||
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);
|
||||
void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
|
||||
unsigned short vdd);
|
||||
int sdhci_get_cd_nogpio(struct mmc_host *mmc);
|
||||
void sdhci_request(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);
|
||||
|
@ -162,6 +162,9 @@ struct mmc_host_ops {
|
||||
/* Prepare HS400 target operating frequency depending host driver */
|
||||
int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
|
||||
|
||||
/* 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 */
|
||||
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_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 */
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user