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:
Linus Torvalds 2021-11-01 18:55:12 -07:00
commit 8a73c77c80
43 changed files with 670 additions and 410 deletions

View File

@ -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>;
};

View File

@ -17,6 +17,7 @@ properties:
compatible:
items:
- enum:
- microchip,mpfs-sd4hc
- socionext,uniphier-sd4hc
- const: cdns,sd4hc

View File

@ -34,6 +34,7 @@ properties:
- fsl,imx6ull-usdhc
- fsl,imx7d-usdhc
- fsl,imx7ulp-usdhc
- nxp,s32g2-usdhc
- items:
- enum:
- fsl,imx8mm-usdhc

View File

@ -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 = <&reg_vcc3v3>;
bus-width = <8>;
non-removable;
mmccard: mmccard@0 {
reg = <0>;
compatible = "mmc-card";
broken-hpi;
};
};

View 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;
};
};
...

View File

@ -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

View File

@ -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:

View File

@ -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"

View File

@ -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";

View File

@ -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
=========

View File

@ -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>

View File

@ -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:

View File

@ -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:

View File

@ -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;

View File

@ -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

View File

@ -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:

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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 = {

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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");

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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 */

View File

@ -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