You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
Merge tag 'mmc-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC and MEMSTICK updates from Ulf Hansson: "MMC core: - Fix hanging on I/O during system suspend for removable cards - Set read only for SD cards with permanent write protect bit - Power cycle the SD/SDIO card if CMD11 fails for UHS voltage - Issue a cache flush for eMMC only when it's enabled - Adopt to updated cache ctrl settings for eMMC from MMC ioctls - Use use device property API when parsing voltages - Don't retry eMMC sanitize cmds - Use the timeout from the MMC ioctl for eMMC santize cmds MMC host: - mmc_spi: Make of_mmc_spi.c resource provider agnostic - mmc_spi: Use polling for card detect even without voltage-ranges - sdhci: Check for reset prior to DMA address unmap - sdhci-acpi: Add support for the AMDI0041 eMMC controller variant - sdhci-esdhc-imx: Depending on OF Kconfig and cleanup code - sdhci-pci: Add PCI IDs for Intel LKF - sdhci-pci: Fix initialization of some SD cards for Intel BYT - sdhci-pci-gli: Various improvements for GL97xx variants - sdhci-of-dwcmshc: Enable support for MMC_CAP_WAIT_WHILE_BUSY - sdhci-of-dwcmshc: Add ACPI support for BlueField-3 SoC - sdhci-of-dwcmshc: Add Rockchip platform support - tmio/renesas_sdhi: Extend support for reset and use a reset controller - tmio/renesas_sdhi: Enable support for MMC_CAP_WAIT_WHILE_BUSY - tmio/renesas_sdhi: Various improvements MEMSTICK: - Minor improvements/cleanups" * tag 'mmc-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (79 commits) mmc: block: Issue a cache flush only when it's enabled memstick: r592: ignore kfifo_out() return code again mmc: block: Update ext_csd.cache_ctrl if it was written mmc: mmc_spi: Make of_mmc_spi.c resource provider agnostic mmc: mmc_spi: Use already parsed IRQ mmc: mmc_spi: Drop unused NO_IRQ definition mmc: mmc_spi: Set up polling even if voltage-ranges is not present mmc: core: Convert mmc_of_parse_voltage() to use device property API mmc: core: Correct descriptions in mmc_of_parse() mmc: dw_mmc-rockchip: Just set default sample value for legacy mode mmc: sdhci-s3c: constify uses of driver/match data mmc: sdhci-s3c: correct kerneldoc of sdhci_s3c_drv_data mmc: sdhci-s3c: simplify getting of_device_id match data mmc: tmio: always restore irq register mmc: sdhci-pci-gli: Enlarge ASPM L1 entry delay of GL975x mmc: core: Let eMMC sanitize not retry in case of timeout/failure mmc: core: Add a retries parameter to __mmc_switch function memstick: r592: remove unused variable mmc: sdhci-st: Remove unnecessary error log mmc: sdhci-msm: Remove unnecessary error log ...
This commit is contained in:
63
Documentation/devicetree/bindings/mmc/brcm,iproc-sdhci.yaml
Normal file
63
Documentation/devicetree/bindings/mmc/brcm,iproc-sdhci.yaml
Normal file
@@ -0,0 +1,63 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/brcm,iproc-sdhci.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Broadcom IPROC SDHCI controller
|
||||
|
||||
maintainers:
|
||||
- Ray Jui <ray.jui@broadcom.com>
|
||||
- Scott Branden <scott.branden@broadcom.com>
|
||||
- Nicolas Saenz Julienne <nsaenz@kernel.org>
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- brcm,bcm2835-sdhci
|
||||
- brcm,bcm2711-emmc2
|
||||
- brcm,sdhci-iproc-cygnus
|
||||
- brcm,sdhci-iproc
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description:
|
||||
Handle to core clock for the sdhci controller.
|
||||
|
||||
sdhci,auto-cmd12:
|
||||
type: boolean
|
||||
description: Specifies that controller should use auto CMD12
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/bcm-cygnus.h>
|
||||
|
||||
mmc@18041000 {
|
||||
compatible = "brcm,sdhci-iproc-cygnus";
|
||||
reg = <0x18041000 0x100>;
|
||||
interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&lcpll0_clks BCM_CYGNUS_LCPLL0_SDIO_CLK>;
|
||||
bus-width = <4>;
|
||||
sdhci,auto-cmd12;
|
||||
no-1-8-v;
|
||||
};
|
||||
...
|
||||
@@ -1,37 +0,0 @@
|
||||
Broadcom IPROC SDHCI controller
|
||||
|
||||
This file documents differences between the core properties described
|
||||
by mmc.txt and the properties that represent the IPROC SDHCI controller.
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be one of the following
|
||||
"brcm,bcm2835-sdhci"
|
||||
"brcm,bcm2711-emmc2"
|
||||
"brcm,sdhci-iproc-cygnus"
|
||||
"brcm,sdhci-iproc"
|
||||
|
||||
Use brcm2835-sdhci for the eMMC controller on the BCM2835 (Raspberry Pi) and
|
||||
bcm2711-emmc2 for the additional eMMC2 controller on BCM2711.
|
||||
|
||||
Use sdhci-iproc-cygnus for Broadcom SDHCI Controllers
|
||||
restricted to 32bit host accesses to SDHCI registers.
|
||||
|
||||
Use sdhci-iproc for Broadcom SDHCI Controllers that allow standard
|
||||
8, 16, 32-bit host access to SDHCI register.
|
||||
|
||||
- clocks : The clock feeding the SDHCI controller.
|
||||
|
||||
Optional properties:
|
||||
- sdhci,auto-cmd12: specifies that controller should use auto CMD12.
|
||||
|
||||
Example:
|
||||
|
||||
sdhci0: sdhci@18041000 {
|
||||
compatible = "brcm,sdhci-iproc-cygnus";
|
||||
reg = <0x18041000 0x100>;
|
||||
interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&lcpll0_clks BCM_CYGNUS_LCPLL0_SDIO_CLK>;
|
||||
bus-width = <4>;
|
||||
sdhci,auto-cmd12;
|
||||
no-1-8-v;
|
||||
};
|
||||
@@ -103,6 +103,26 @@ properties:
|
||||
Only eMMC HS400 mode need to take care of this property.
|
||||
default: 0
|
||||
|
||||
clocks:
|
||||
maxItems: 3
|
||||
description:
|
||||
Handle clocks for the sdhc controller.
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: ipg
|
||||
- const: ahb
|
||||
- const: per
|
||||
|
||||
pinctrl-names:
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
items:
|
||||
- const: default
|
||||
- const: state_100mhz
|
||||
- const: state_200mhz
|
||||
- const: sleep
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
@@ -5,11 +5,11 @@ by mmc.txt and the properties used by the mmc_spi driver.
|
||||
|
||||
Required properties:
|
||||
- spi-max-frequency : maximum frequency for this device (Hz).
|
||||
- voltage-ranges : two cells are required, first cell specifies minimum
|
||||
slot voltage (mV), second cell specifies maximum slot voltage (mV).
|
||||
Several ranges could be specified.
|
||||
|
||||
Optional properties:
|
||||
- voltage-ranges : two cells are required, first cell specifies minimum
|
||||
slot voltage (mV), second cell specifies maximum slot voltage (mV).
|
||||
Several ranges could be specified. If not provided, 3.2v..3.4v is assumed.
|
||||
- gpios : may specify GPIOs in this order: Card-Detect GPIO,
|
||||
Write-Protect GPIO. Note that this does not follow the
|
||||
binding from mmc.txt, for historical reasons.
|
||||
|
||||
@@ -31,6 +31,7 @@ properties:
|
||||
- const: mediatek,mt2701-mmc
|
||||
- items:
|
||||
- const: mediatek,mt8192-mmc
|
||||
- const: mediatek,mt8195-mmc
|
||||
- const: mediatek,mt8183-mmc
|
||||
|
||||
clocks:
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
* Synopsys DesignWare Cores Mobile Storage Host Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one of the following:
|
||||
"snps,dwcmshc-sdhci"
|
||||
- reg: offset and length of the register set for the device.
|
||||
- interrupts: a single interrupt specifier.
|
||||
- clocks: Array of clocks required for SDHCI; requires at least one for
|
||||
core clock.
|
||||
- clock-names: Array of names corresponding to clocks property; shall be
|
||||
"core" for core clock and "bus" for optional bus clock.
|
||||
|
||||
Example:
|
||||
sdhci2: sdhci@aa0000 {
|
||||
compatible = "snps,dwcmshc-sdhci";
|
||||
reg = <0xaa0000 0x1000>;
|
||||
interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&emmcclk>;
|
||||
bus-width = <8>;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/snps,dwcmshc-sdhci.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Synopsys Designware Mobile Storage Host Controller Binding
|
||||
|
||||
maintainers:
|
||||
- Ulf Hansson <ulf.hansson@linaro.org>
|
||||
- Jisheng Zhang <Jisheng.Zhang@synaptics.com>
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- rockchip,rk3568-dwcmshc
|
||||
- snps,dwcmshc-sdhci
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Offset and length of the register set for the device
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: core clock
|
||||
- description: bus clock for optional
|
||||
- description: axi clock for rockchip specified
|
||||
- description: block clock for rockchip specified
|
||||
- description: timer clock for rockchip specified
|
||||
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: core
|
||||
- const: bus
|
||||
- const: axi
|
||||
- const: block
|
||||
- const: timer
|
||||
|
||||
rockchip,txclk-tapnum:
|
||||
description: Specify the number of delay for tx sampling.
|
||||
$ref: /schemas/types.yaml#/definitions/uint8
|
||||
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
mmc@fe310000 {
|
||||
compatible = "rockchip,rk3568-dwcmshc";
|
||||
reg = <0xfe310000 0x10000>;
|
||||
interrupts = <0 25 0x4>;
|
||||
clocks = <&cru 17>, <&cru 18>, <&cru 19>, <&cru 20>, <&cru 21>;
|
||||
clock-names = "core", "bus", "axi", "block", "timer";
|
||||
bus-width = <8>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
- |
|
||||
mmc@aa0000 {
|
||||
compatible = "snps,dwcmshc-sdhci";
|
||||
reg = <0xaa000 0x1000>;
|
||||
interrupts = <0 25 0x4>;
|
||||
clocks = <&cru 17>, <&cru 18>;
|
||||
clock-names = "core", "bus";
|
||||
bus-width = <8>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
...
|
||||
@@ -331,18 +331,17 @@ static int h_memstick_read_dev_id(struct memstick_dev *card,
|
||||
sizeof(struct ms_id_register));
|
||||
*mrq = &card->current_mrq;
|
||||
return 0;
|
||||
} else {
|
||||
if (!(*mrq)->error) {
|
||||
memcpy(&id_reg, (*mrq)->data, sizeof(id_reg));
|
||||
card->id.match_flags = MEMSTICK_MATCH_ALL;
|
||||
card->id.type = id_reg.type;
|
||||
card->id.category = id_reg.category;
|
||||
card->id.class = id_reg.class;
|
||||
dev_dbg(&card->dev, "if_mode = %02x\n", id_reg.if_mode);
|
||||
}
|
||||
complete(&card->mrq_complete);
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (!(*mrq)->error) {
|
||||
memcpy(&id_reg, (*mrq)->data, sizeof(id_reg));
|
||||
card->id.match_flags = MEMSTICK_MATCH_ALL;
|
||||
card->id.type = id_reg.type;
|
||||
card->id.category = id_reg.category;
|
||||
card->id.class = id_reg.class;
|
||||
dev_dbg(&card->dev, "if_mode = %02x\n", id_reg.if_mode);
|
||||
}
|
||||
complete(&card->mrq_complete);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static int h_memstick_set_rw_addr(struct memstick_dev *card,
|
||||
|
||||
@@ -1382,7 +1382,8 @@ static int mspro_block_resume(struct memstick_dev *card)
|
||||
|
||||
new_msb->card = card;
|
||||
memstick_set_drvdata(card, new_msb);
|
||||
if (mspro_block_init_card(card))
|
||||
rc = mspro_block_init_card(card);
|
||||
if (rc)
|
||||
goto out_free;
|
||||
|
||||
for (cnt = 0; new_msb->attr_group.attrs[cnt]
|
||||
|
||||
@@ -359,13 +359,15 @@ static void r592_write_fifo_pio(struct r592_device *dev,
|
||||
/* Flushes the temporary FIFO used to make aligned DWORD writes */
|
||||
static void r592_flush_fifo_write(struct r592_device *dev)
|
||||
{
|
||||
int ret;
|
||||
u8 buffer[4] = { 0 };
|
||||
int len;
|
||||
|
||||
if (kfifo_is_empty(&dev->pio_fifo))
|
||||
return;
|
||||
|
||||
len = kfifo_out(&dev->pio_fifo, buffer, 4);
|
||||
ret = kfifo_out(&dev->pio_fifo, buffer, 4);
|
||||
/* intentionally ignore __must_check return code */
|
||||
(void)ret;
|
||||
r592_write_reg_raw_be(dev, R592_FIFO_PIO, *(u32 *)buffer);
|
||||
}
|
||||
|
||||
|
||||
@@ -539,7 +539,7 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
||||
|
||||
if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_SANITIZE_START) &&
|
||||
(cmd.opcode == MMC_SWITCH))
|
||||
return mmc_sanitize(card);
|
||||
return mmc_sanitize(card, idata->ic.cmd_timeout_ms);
|
||||
|
||||
mmc_wait_for_req(card->host, &mrq);
|
||||
|
||||
@@ -572,6 +572,18 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
||||
main_md->part_curr = value & EXT_CSD_PART_CONFIG_ACC_MASK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure to update CACHE_CTRL in case it was changed. The cache
|
||||
* will get turned back on if the card is re-initialized, e.g.
|
||||
* suspend/resume or hw reset in recovery.
|
||||
*/
|
||||
if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_CACHE_CTRL) &&
|
||||
(cmd.opcode == MMC_SWITCH)) {
|
||||
u8 value = MMC_EXTRACT_VALUE_FROM_ARG(cmd.arg) & 1;
|
||||
|
||||
card->ext_csd.cache_ctrl = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to the SD specs, some commands require a delay after
|
||||
* issuing the command.
|
||||
@@ -947,7 +959,7 @@ static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host,
|
||||
md->reset_done |= type;
|
||||
err = mmc_hw_reset(host);
|
||||
/* Ensure we switch back to the correct partition */
|
||||
if (err != -EOPNOTSUPP) {
|
||||
if (err) {
|
||||
struct mmc_blk_data *main_md =
|
||||
dev_get_drvdata(&host->card->dev);
|
||||
int part_err;
|
||||
@@ -1933,8 +1945,9 @@ static void mmc_blk_hsq_req_done(struct mmc_request *mrq)
|
||||
void mmc_blk_mq_complete(struct request *req)
|
||||
{
|
||||
struct mmc_queue *mq = req->q->queuedata;
|
||||
struct mmc_host *host = mq->card->host;
|
||||
|
||||
if (mq->use_cqe)
|
||||
if (host->cqe_enabled)
|
||||
mmc_blk_cqe_complete_rq(mq, req);
|
||||
else if (likely(!blk_should_fake_timeout(req->q)))
|
||||
mmc_blk_mq_complete_rq(mq, req);
|
||||
@@ -2179,7 +2192,7 @@ out_post_req:
|
||||
|
||||
static int mmc_blk_wait_for_idle(struct mmc_queue *mq, struct mmc_host *host)
|
||||
{
|
||||
if (mq->use_cqe)
|
||||
if (host->cqe_enabled)
|
||||
return host->cqe_ops->cqe_wait_for_idle(host);
|
||||
|
||||
return mmc_blk_rw_wait(mq, NULL);
|
||||
@@ -2224,11 +2237,15 @@ enum mmc_issued mmc_blk_mq_issue_rq(struct mmc_queue *mq, struct request *req)
|
||||
case MMC_ISSUE_ASYNC:
|
||||
switch (req_op(req)) {
|
||||
case REQ_OP_FLUSH:
|
||||
if (!mmc_cache_enabled(host)) {
|
||||
blk_mq_end_request(req, BLK_STS_OK);
|
||||
return MMC_REQ_FINISHED;
|
||||
}
|
||||
ret = mmc_blk_cqe_issue_flush(mq, req);
|
||||
break;
|
||||
case REQ_OP_READ:
|
||||
case REQ_OP_WRITE:
|
||||
if (mq->use_cqe)
|
||||
if (host->cqe_enabled)
|
||||
ret = mmc_blk_cqe_issue_rw_rq(mq, req);
|
||||
else
|
||||
ret = mmc_blk_mq_issue_rw_rq(mq, req);
|
||||
@@ -2261,6 +2278,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
|
||||
{
|
||||
struct mmc_blk_data *md;
|
||||
int devidx, ret;
|
||||
char cap_str[10];
|
||||
|
||||
devidx = ida_simple_get(&mmc_blk_ida, 0, max_devices, GFP_KERNEL);
|
||||
if (devidx < 0) {
|
||||
@@ -2365,6 +2383,12 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
|
||||
blk_queue_write_cache(md->queue.queue, true, true);
|
||||
}
|
||||
|
||||
string_get_size((u64)size, 512, STRING_UNITS_2,
|
||||
cap_str, sizeof(cap_str));
|
||||
pr_info("%s: %s %s %s %s\n",
|
||||
md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
|
||||
cap_str, md->read_only ? "(ro)" : "");
|
||||
|
||||
return md;
|
||||
|
||||
err_putdisk:
|
||||
@@ -2407,7 +2431,6 @@ static int mmc_blk_alloc_part(struct mmc_card *card,
|
||||
const char *subname,
|
||||
int area_type)
|
||||
{
|
||||
char cap_str[10];
|
||||
struct mmc_blk_data *part_md;
|
||||
|
||||
part_md = mmc_blk_alloc_req(card, disk_to_dev(md->disk), size, default_ro,
|
||||
@@ -2417,11 +2440,6 @@ static int mmc_blk_alloc_part(struct mmc_card *card,
|
||||
part_md->part_type = part_type;
|
||||
list_add(&part_md->part, &md->part);
|
||||
|
||||
string_get_size((u64)get_capacity(part_md->disk), 512, STRING_UNITS_2,
|
||||
cap_str, sizeof(cap_str));
|
||||
pr_info("%s: %s %s partition %u %s\n",
|
||||
part_md->disk->disk_name, mmc_card_id(card),
|
||||
mmc_card_name(card), part_md->part_type, cap_str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2558,9 +2576,8 @@ static int mmc_blk_alloc_rpmb_part(struct mmc_card *card,
|
||||
string_get_size((u64)size, 512, STRING_UNITS_2,
|
||||
cap_str, sizeof(cap_str));
|
||||
|
||||
pr_info("%s: %s %s partition %u %s, chardev (%d:%d)\n",
|
||||
rpmb_name, mmc_card_id(card),
|
||||
mmc_card_name(card), EXT_CSD_PART_CONFIG_ACC_RPMB, cap_str,
|
||||
pr_info("%s: %s %s %s, chardev (%d:%d)\n",
|
||||
rpmb_name, mmc_card_id(card), mmc_card_name(card), cap_str,
|
||||
MAJOR(mmc_rpmb_devt), rpmb->id);
|
||||
|
||||
return 0;
|
||||
@@ -2876,7 +2893,7 @@ static void mmc_blk_remove_debugfs(struct mmc_card *card,
|
||||
static int mmc_blk_probe(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_blk_data *md, *part_md;
|
||||
char cap_str[10];
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* Check that the card supports the command class(es) we need.
|
||||
@@ -2888,31 +2905,30 @@ static int mmc_blk_probe(struct mmc_card *card)
|
||||
|
||||
card->complete_wq = alloc_workqueue("mmc_complete",
|
||||
WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
|
||||
if (unlikely(!card->complete_wq)) {
|
||||
if (!card->complete_wq) {
|
||||
pr_err("Failed to create mmc completion workqueue");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
md = mmc_blk_alloc(card);
|
||||
if (IS_ERR(md))
|
||||
return PTR_ERR(md);
|
||||
if (IS_ERR(md)) {
|
||||
ret = PTR_ERR(md);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
string_get_size((u64)get_capacity(md->disk), 512, STRING_UNITS_2,
|
||||
cap_str, sizeof(cap_str));
|
||||
pr_info("%s: %s %s %s %s\n",
|
||||
md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
|
||||
cap_str, md->read_only ? "(ro)" : "");
|
||||
|
||||
if (mmc_blk_alloc_parts(card, md))
|
||||
ret = mmc_blk_alloc_parts(card, md);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
dev_set_drvdata(&card->dev, md);
|
||||
|
||||
if (mmc_add_disk(md))
|
||||
ret = mmc_add_disk(md);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
list_for_each_entry(part_md, &md->part, part) {
|
||||
if (mmc_add_disk(part_md))
|
||||
ret = mmc_add_disk(part_md);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -2933,10 +2949,12 @@ static int mmc_blk_probe(struct mmc_card *card)
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
out:
|
||||
mmc_blk_remove_parts(card, md);
|
||||
mmc_blk_remove_req(md);
|
||||
return 0;
|
||||
out_free:
|
||||
destroy_workqueue(card->complete_wq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mmc_blk_remove(struct mmc_card *card)
|
||||
|
||||
@@ -1207,7 +1207,7 @@ int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr)
|
||||
|
||||
err = mmc_wait_for_cmd(host, &cmd, 0);
|
||||
if (err)
|
||||
return err;
|
||||
goto power_cycle;
|
||||
|
||||
if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR))
|
||||
return -EIO;
|
||||
@@ -1377,63 +1377,13 @@ void mmc_power_cycle(struct mmc_host *host, u32 ocr)
|
||||
mmc_power_up(host, ocr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup when the last reference to the bus operator is dropped.
|
||||
*/
|
||||
static void __mmc_release_bus(struct mmc_host *host)
|
||||
{
|
||||
WARN_ON(!host->bus_dead);
|
||||
|
||||
host->bus_ops = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Increase reference count of bus operator
|
||||
*/
|
||||
static inline void mmc_bus_get(struct mmc_host *host)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
host->bus_refs++;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Decrease reference count of bus operator and free it if
|
||||
* it is the last reference.
|
||||
*/
|
||||
static inline void mmc_bus_put(struct mmc_host *host)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
host->bus_refs--;
|
||||
if ((host->bus_refs == 0) && host->bus_ops)
|
||||
__mmc_release_bus(host);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Assign a mmc bus handler to a host. Only one bus handler may control a
|
||||
* host at any given time.
|
||||
*/
|
||||
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
WARN_ON(host->bus_ops);
|
||||
WARN_ON(host->bus_refs);
|
||||
|
||||
host->bus_ops = ops;
|
||||
host->bus_refs = 1;
|
||||
host->bus_dead = 0;
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1441,18 +1391,7 @@ void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
|
||||
*/
|
||||
void mmc_detach_bus(struct mmc_host *host)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
WARN_ON(!host->claimed);
|
||||
WARN_ON(!host->bus_ops);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
host->bus_dead = 1;
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
mmc_bus_put(host);
|
||||
host->bus_ops = NULL;
|
||||
}
|
||||
|
||||
void _mmc_detect_change(struct mmc_host *host, unsigned long delay, bool cd_irq)
|
||||
@@ -2080,18 +2019,7 @@ int mmc_hw_reset(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!host->card)
|
||||
return -EINVAL;
|
||||
|
||||
mmc_bus_get(host);
|
||||
if (!host->bus_ops || host->bus_dead || !host->bus_ops->hw_reset) {
|
||||
mmc_bus_put(host);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
ret = host->bus_ops->hw_reset(host);
|
||||
mmc_bus_put(host);
|
||||
|
||||
if (ret < 0)
|
||||
pr_warn("%s: tried to HW reset card, got error %d\n",
|
||||
mmc_hostname(host), ret);
|
||||
@@ -2104,18 +2032,10 @@ int mmc_sw_reset(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!host->card)
|
||||
return -EINVAL;
|
||||
|
||||
mmc_bus_get(host);
|
||||
if (!host->bus_ops || host->bus_dead || !host->bus_ops->sw_reset) {
|
||||
mmc_bus_put(host);
|
||||
if (!host->bus_ops->sw_reset)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
ret = host->bus_ops->sw_reset(host);
|
||||
mmc_bus_put(host);
|
||||
|
||||
if (ret)
|
||||
pr_warn("%s: tried to SW reset card, got error %d\n",
|
||||
mmc_hostname(host), ret);
|
||||
@@ -2263,32 +2183,15 @@ void mmc_rescan(struct work_struct *work)
|
||||
host->trigger_card_event = false;
|
||||
}
|
||||
|
||||
mmc_bus_get(host);
|
||||
|
||||
/* Verify a registered card to be functional, else remove it. */
|
||||
if (host->bus_ops && !host->bus_dead)
|
||||
if (host->bus_ops)
|
||||
host->bus_ops->detect(host);
|
||||
|
||||
host->detect_change = 0;
|
||||
|
||||
/*
|
||||
* Let mmc_bus_put() free the bus/bus_ops if we've found that
|
||||
* the card is no longer present.
|
||||
*/
|
||||
mmc_bus_put(host);
|
||||
mmc_bus_get(host);
|
||||
|
||||
/* if there still is a card present, stop here */
|
||||
if (host->bus_ops != NULL) {
|
||||
mmc_bus_put(host);
|
||||
if (host->bus_ops != NULL)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only we can add a new handler, so it's safe to
|
||||
* release the lock here.
|
||||
*/
|
||||
mmc_bus_put(host);
|
||||
|
||||
mmc_claim_host(host);
|
||||
if (mmc_card_is_removable(host) && host->ops->get_cd &&
|
||||
@@ -2351,98 +2254,21 @@ void mmc_stop_host(struct mmc_host *host)
|
||||
/* clear pm flags now and let card drivers set them as needed */
|
||||
host->pm_flags = 0;
|
||||
|
||||
mmc_bus_get(host);
|
||||
if (host->bus_ops && !host->bus_dead) {
|
||||
if (host->bus_ops) {
|
||||
/* Calling bus_ops->remove() with a claimed host can deadlock */
|
||||
host->bus_ops->remove(host);
|
||||
mmc_claim_host(host);
|
||||
mmc_detach_bus(host);
|
||||
mmc_power_off(host);
|
||||
mmc_release_host(host);
|
||||
mmc_bus_put(host);
|
||||
return;
|
||||
}
|
||||
mmc_bus_put(host);
|
||||
|
||||
mmc_claim_host(host);
|
||||
mmc_power_off(host);
|
||||
mmc_release_host(host);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/* Do the card removal on suspend if card is assumed removeable
|
||||
* Do that in pm notifier while userspace isn't yet frozen, so we will be able
|
||||
to sync the card.
|
||||
*/
|
||||
static int mmc_pm_notify(struct notifier_block *notify_block,
|
||||
unsigned long mode, void *unused)
|
||||
{
|
||||
struct mmc_host *host = container_of(
|
||||
notify_block, struct mmc_host, pm_notify);
|
||||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
switch (mode) {
|
||||
case PM_HIBERNATION_PREPARE:
|
||||
case PM_SUSPEND_PREPARE:
|
||||
case PM_RESTORE_PREPARE:
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
host->rescan_disable = 1;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
cancel_delayed_work_sync(&host->detect);
|
||||
|
||||
if (!host->bus_ops)
|
||||
break;
|
||||
|
||||
/* Validate prerequisites for suspend */
|
||||
if (host->bus_ops->pre_suspend)
|
||||
err = host->bus_ops->pre_suspend(host);
|
||||
if (!err)
|
||||
break;
|
||||
|
||||
if (!mmc_card_is_removable(host)) {
|
||||
dev_warn(mmc_dev(host),
|
||||
"pre_suspend failed for non-removable host: "
|
||||
"%d\n", err);
|
||||
/* Avoid removing non-removable hosts */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calling bus_ops->remove() with a claimed host can deadlock */
|
||||
host->bus_ops->remove(host);
|
||||
mmc_claim_host(host);
|
||||
mmc_detach_bus(host);
|
||||
mmc_power_off(host);
|
||||
mmc_release_host(host);
|
||||
host->pm_flags = 0;
|
||||
break;
|
||||
|
||||
case PM_POST_SUSPEND:
|
||||
case PM_POST_HIBERNATION:
|
||||
case PM_POST_RESTORE:
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
host->rescan_disable = 0;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
_mmc_detect_change(host, 0, false);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mmc_register_pm_notifier(struct mmc_host *host)
|
||||
{
|
||||
host->pm_notify.notifier_call = mmc_pm_notify;
|
||||
register_pm_notifier(&host->pm_notify);
|
||||
}
|
||||
|
||||
void mmc_unregister_pm_notifier(struct mmc_host *host)
|
||||
{
|
||||
unregister_pm_notifier(&host->pm_notify);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init mmc_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -29,6 +29,7 @@ struct mmc_bus_ops {
|
||||
int (*shutdown)(struct mmc_host *);
|
||||
int (*hw_reset)(struct mmc_host *);
|
||||
int (*sw_reset)(struct mmc_host *);
|
||||
bool (*cache_enabled)(struct mmc_host *);
|
||||
};
|
||||
|
||||
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
|
||||
@@ -93,14 +94,6 @@ int mmc_execute_tuning(struct mmc_card *card);
|
||||
int mmc_hs200_to_hs400(struct mmc_card *card);
|
||||
int mmc_hs400_to_hs200(struct mmc_card *card);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
void mmc_register_pm_notifier(struct mmc_host *host);
|
||||
void mmc_unregister_pm_notifier(struct mmc_host *host);
|
||||
#else
|
||||
static inline void mmc_register_pm_notifier(struct mmc_host *host) { }
|
||||
static inline void mmc_unregister_pm_notifier(struct mmc_host *host) { }
|
||||
#endif
|
||||
|
||||
void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq);
|
||||
bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq);
|
||||
|
||||
@@ -171,4 +164,12 @@ static inline void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq,
|
||||
host->ops->post_req(host, mrq, err);
|
||||
}
|
||||
|
||||
static inline bool mmc_cache_enabled(struct mmc_host *host)
|
||||
{
|
||||
if (host->bus_ops->cache_enabled)
|
||||
return host->bus_ops->cache_enabled(host);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -35,6 +35,42 @@
|
||||
|
||||
static DEFINE_IDA(mmc_host_ida);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mmc_host_class_prepare(struct device *dev)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
|
||||
/*
|
||||
* It's safe to access the bus_ops pointer, as both userspace and the
|
||||
* workqueue for detecting cards are frozen at this point.
|
||||
*/
|
||||
if (!host->bus_ops)
|
||||
return 0;
|
||||
|
||||
/* Validate conditions for system suspend. */
|
||||
if (host->bus_ops->pre_suspend)
|
||||
return host->bus_ops->pre_suspend(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mmc_host_class_complete(struct device *dev)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
|
||||
_mmc_detect_change(host, 0, false);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mmc_host_class_dev_pm_ops = {
|
||||
.prepare = mmc_host_class_prepare,
|
||||
.complete = mmc_host_class_complete,
|
||||
};
|
||||
|
||||
#define MMC_HOST_CLASS_DEV_PM_OPS (&mmc_host_class_dev_pm_ops)
|
||||
#else
|
||||
#define MMC_HOST_CLASS_DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static void mmc_host_classdev_release(struct device *dev)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
@@ -46,6 +82,7 @@ static void mmc_host_classdev_release(struct device *dev)
|
||||
static struct class mmc_host_class = {
|
||||
.name = "mmc_host",
|
||||
.dev_release = mmc_host_classdev_release,
|
||||
.pm = MMC_HOST_CLASS_DEV_PM_OPS,
|
||||
};
|
||||
|
||||
int mmc_register_host_class(void)
|
||||
@@ -209,8 +246,8 @@ mmc_of_parse_clk_phase(struct mmc_host *host, struct mmc_clk_phase_map *map)
|
||||
EXPORT_SYMBOL(mmc_of_parse_clk_phase);
|
||||
|
||||
/**
|
||||
* mmc_of_parse() - parse host's device-tree node
|
||||
* @host: host whose node should be parsed.
|
||||
* mmc_of_parse() - parse host's device properties
|
||||
* @host: host whose properties should be parsed.
|
||||
*
|
||||
* To keep the rest of the MMC subsystem unaware of whether DT has been
|
||||
* used to to instantiate and configure this host instance or not, we
|
||||
@@ -379,44 +416,62 @@ EXPORT_SYMBOL(mmc_of_parse);
|
||||
|
||||
/**
|
||||
* mmc_of_parse_voltage - return mask of supported voltages
|
||||
* @np: The device node need to be parsed.
|
||||
* @host: host whose properties should be parsed.
|
||||
* @mask: mask of voltages available for MMC/SD/SDIO
|
||||
*
|
||||
* Parse the "voltage-ranges" DT property, returning zero if it is not
|
||||
* Parse the "voltage-ranges" property, returning zero if it is not
|
||||
* found, negative errno if the voltage-range specification is invalid,
|
||||
* or one if the voltage-range is specified and successfully parsed.
|
||||
*/
|
||||
int mmc_of_parse_voltage(struct device_node *np, u32 *mask)
|
||||
int mmc_of_parse_voltage(struct mmc_host *host, u32 *mask)
|
||||
{
|
||||
const u32 *voltage_ranges;
|
||||
const char *prop = "voltage-ranges";
|
||||
struct device *dev = host->parent;
|
||||
u32 *voltage_ranges;
|
||||
int num_ranges, i;
|
||||
int ret;
|
||||
|
||||
voltage_ranges = of_get_property(np, "voltage-ranges", &num_ranges);
|
||||
if (!voltage_ranges) {
|
||||
pr_debug("%pOF: voltage-ranges unspecified\n", np);
|
||||
if (!device_property_present(dev, prop)) {
|
||||
dev_dbg(dev, "%s unspecified\n", prop);
|
||||
return 0;
|
||||
}
|
||||
num_ranges = num_ranges / sizeof(*voltage_ranges) / 2;
|
||||
|
||||
ret = device_property_count_u32(dev, prop);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
num_ranges = ret / 2;
|
||||
if (!num_ranges) {
|
||||
pr_err("%pOF: voltage-ranges empty\n", np);
|
||||
dev_err(dev, "%s empty\n", prop);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
voltage_ranges = kcalloc(2 * num_ranges, sizeof(*voltage_ranges), GFP_KERNEL);
|
||||
if (!voltage_ranges)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = device_property_read_u32_array(dev, prop, voltage_ranges, 2 * num_ranges);
|
||||
if (ret) {
|
||||
kfree(voltage_ranges);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_ranges; i++) {
|
||||
const int j = i * 2;
|
||||
u32 ocr_mask;
|
||||
|
||||
ocr_mask = mmc_vddrange_to_ocrmask(
|
||||
be32_to_cpu(voltage_ranges[j]),
|
||||
be32_to_cpu(voltage_ranges[j + 1]));
|
||||
ocr_mask = mmc_vddrange_to_ocrmask(voltage_ranges[j + 0],
|
||||
voltage_ranges[j + 1]);
|
||||
if (!ocr_mask) {
|
||||
pr_err("%pOF: voltage-range #%d is invalid\n",
|
||||
np, i);
|
||||
dev_err(dev, "range #%d in %s is invalid\n", i, prop);
|
||||
kfree(voltage_ranges);
|
||||
return -EINVAL;
|
||||
}
|
||||
*mask |= ocr_mask;
|
||||
}
|
||||
|
||||
kfree(voltage_ranges);
|
||||
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_of_parse_voltage);
|
||||
@@ -538,8 +593,6 @@ int mmc_add_host(struct mmc_host *host)
|
||||
#endif
|
||||
|
||||
mmc_start_host(host);
|
||||
mmc_register_pm_notifier(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -555,7 +608,6 @@ EXPORT_SYMBOL(mmc_add_host);
|
||||
*/
|
||||
void mmc_remove_host(struct mmc_host *host)
|
||||
{
|
||||
mmc_unregister_pm_notifier(host);
|
||||
mmc_stop_host(host);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
@@ -1068,7 +1068,7 @@ static int mmc_select_hs(struct mmc_card *card)
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
|
||||
card->ext_csd.generic_cmd6_time, MMC_TIMING_MMC_HS,
|
||||
true, true);
|
||||
true, true, MMC_CMD_RETRIES);
|
||||
if (err)
|
||||
pr_warn("%s: switch to high-speed failed, err:%d\n",
|
||||
mmc_hostname(card->host), err);
|
||||
@@ -1100,7 +1100,7 @@ static int mmc_select_hs_ddr(struct mmc_card *card)
|
||||
ext_csd_bits,
|
||||
card->ext_csd.generic_cmd6_time,
|
||||
MMC_TIMING_MMC_DDR52,
|
||||
true, true);
|
||||
true, true, MMC_CMD_RETRIES);
|
||||
if (err) {
|
||||
pr_err("%s: switch to bus width %d ddr failed\n",
|
||||
mmc_hostname(host), 1 << bus_width);
|
||||
@@ -1168,7 +1168,7 @@ static int mmc_select_hs400(struct mmc_card *card)
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, val,
|
||||
card->ext_csd.generic_cmd6_time, 0,
|
||||
false, true);
|
||||
false, true, MMC_CMD_RETRIES);
|
||||
if (err) {
|
||||
pr_err("%s: switch to high-speed from hs200 failed, err:%d\n",
|
||||
mmc_hostname(host), err);
|
||||
@@ -1210,7 +1210,7 @@ static int mmc_select_hs400(struct mmc_card *card)
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, val,
|
||||
card->ext_csd.generic_cmd6_time, 0,
|
||||
false, true);
|
||||
false, true, MMC_CMD_RETRIES);
|
||||
if (err) {
|
||||
pr_err("%s: switch to hs400 failed, err:%d\n",
|
||||
mmc_hostname(host), err);
|
||||
@@ -1256,7 +1256,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
|
||||
val = EXT_CSD_TIMING_HS;
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
|
||||
val, card->ext_csd.generic_cmd6_time, 0,
|
||||
false, true);
|
||||
false, true, MMC_CMD_RETRIES);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
@@ -1272,7 +1272,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
|
||||
/* Switch HS DDR to HS */
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
|
||||
EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time,
|
||||
0, false, true);
|
||||
0, false, true, MMC_CMD_RETRIES);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
@@ -1287,7 +1287,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
|
||||
card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
|
||||
val, card->ext_csd.generic_cmd6_time, 0,
|
||||
false, true);
|
||||
false, true, MMC_CMD_RETRIES);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
@@ -1371,7 +1371,7 @@ static int mmc_select_hs400es(struct mmc_card *card)
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
|
||||
card->ext_csd.generic_cmd6_time, 0,
|
||||
false, true);
|
||||
false, true, MMC_CMD_RETRIES);
|
||||
if (err) {
|
||||
pr_err("%s: switch to hs for hs400es failed, err:%d\n",
|
||||
mmc_hostname(host), err);
|
||||
@@ -1405,7 +1405,7 @@ static int mmc_select_hs400es(struct mmc_card *card)
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, val,
|
||||
card->ext_csd.generic_cmd6_time, 0,
|
||||
false, true);
|
||||
false, true, MMC_CMD_RETRIES);
|
||||
if (err) {
|
||||
pr_err("%s: switch to hs400es failed, err:%d\n",
|
||||
mmc_hostname(host), err);
|
||||
@@ -1470,7 +1470,7 @@ static int mmc_select_hs200(struct mmc_card *card)
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, val,
|
||||
card->ext_csd.generic_cmd6_time, 0,
|
||||
false, true);
|
||||
false, true, MMC_CMD_RETRIES);
|
||||
if (err)
|
||||
goto err;
|
||||
old_timing = host->ios.timing;
|
||||
@@ -1975,7 +1975,7 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
|
||||
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_POWER_OFF_NOTIFICATION,
|
||||
notify_type, timeout, 0, false, false);
|
||||
notify_type, timeout, 0, false, false, MMC_CMD_RETRIES);
|
||||
if (err)
|
||||
pr_err("%s: Power Off Notification timed out, %u\n",
|
||||
mmc_hostname(card->host), timeout);
|
||||
@@ -2029,6 +2029,12 @@ static void mmc_detect(struct mmc_host *host)
|
||||
}
|
||||
}
|
||||
|
||||
static bool _mmc_cache_enabled(struct mmc_host *host)
|
||||
{
|
||||
return host->card->ext_csd.cache_size > 0 &&
|
||||
host->card->ext_csd.cache_ctrl & 1;
|
||||
}
|
||||
|
||||
static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
|
||||
{
|
||||
int err = 0;
|
||||
@@ -2208,6 +2214,7 @@ static const struct mmc_bus_ops mmc_ops = {
|
||||
.alive = mmc_alive,
|
||||
.shutdown = mmc_shutdown,
|
||||
.hw_reset = _mmc_hw_reset,
|
||||
.cache_enabled = _mmc_cache_enabled,
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -296,61 +296,40 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_spi_send_csd(struct mmc_host *host, u32 *csd)
|
||||
static int mmc_spi_send_cxd(struct mmc_host *host, u32 *cxd, u32 opcode)
|
||||
{
|
||||
int ret, i;
|
||||
__be32 *csd_tmp;
|
||||
__be32 *cxd_tmp;
|
||||
|
||||
csd_tmp = kzalloc(16, GFP_KERNEL);
|
||||
if (!csd_tmp)
|
||||
cxd_tmp = kzalloc(16, GFP_KERNEL);
|
||||
if (!cxd_tmp)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = mmc_send_cxd_data(NULL, host, MMC_SEND_CSD, csd_tmp, 16);
|
||||
ret = mmc_send_cxd_data(NULL, host, opcode, cxd_tmp, 16);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
csd[i] = be32_to_cpu(csd_tmp[i]);
|
||||
cxd[i] = be32_to_cpu(cxd_tmp[i]);
|
||||
|
||||
err:
|
||||
kfree(csd_tmp);
|
||||
kfree(cxd_tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mmc_send_csd(struct mmc_card *card, u32 *csd)
|
||||
{
|
||||
if (mmc_host_is_spi(card->host))
|
||||
return mmc_spi_send_csd(card->host, csd);
|
||||
return mmc_spi_send_cxd(card->host, csd, MMC_SEND_CSD);
|
||||
|
||||
return mmc_send_cxd_native(card->host, card->rca << 16, csd,
|
||||
MMC_SEND_CSD);
|
||||
}
|
||||
|
||||
static int mmc_spi_send_cid(struct mmc_host *host, u32 *cid)
|
||||
{
|
||||
int ret, i;
|
||||
__be32 *cid_tmp;
|
||||
|
||||
cid_tmp = kzalloc(16, GFP_KERNEL);
|
||||
if (!cid_tmp)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = mmc_send_cxd_data(NULL, host, MMC_SEND_CID, cid_tmp, 16);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
cid[i] = be32_to_cpu(cid_tmp[i]);
|
||||
|
||||
err:
|
||||
kfree(cid_tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mmc_send_cid(struct mmc_host *host, u32 *cid)
|
||||
{
|
||||
if (mmc_host_is_spi(host))
|
||||
return mmc_spi_send_cid(host, cid);
|
||||
return mmc_spi_send_cxd(host, cid, MMC_SEND_CID);
|
||||
|
||||
return mmc_send_cxd_native(host, 0, cid, MMC_ALL_SEND_CID);
|
||||
}
|
||||
@@ -553,12 +532,13 @@ int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms,
|
||||
* @timing: new timing to change to
|
||||
* @send_status: send status cmd to poll for busy
|
||||
* @retry_crc_err: retry when CRC errors when polling with CMD13 for busy
|
||||
* @retries: number of retries
|
||||
*
|
||||
* Modifies the EXT_CSD register for selected card.
|
||||
*/
|
||||
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
unsigned int timeout_ms, unsigned char timing,
|
||||
bool send_status, bool retry_crc_err)
|
||||
bool send_status, bool retry_crc_err, unsigned int retries)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
int err;
|
||||
@@ -598,7 +578,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1;
|
||||
}
|
||||
|
||||
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
|
||||
err = mmc_wait_for_cmd(host, &cmd, retries);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@@ -633,7 +613,7 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
unsigned int timeout_ms)
|
||||
{
|
||||
return __mmc_switch(card, set, index, value, timeout_ms, 0,
|
||||
true, false);
|
||||
true, false, MMC_CMD_RETRIES);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_switch);
|
||||
|
||||
@@ -988,9 +968,7 @@ int mmc_flush_cache(struct mmc_card *card)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (mmc_card_mmc(card) &&
|
||||
(card->ext_csd.cache_size > 0) &&
|
||||
(card->ext_csd.cache_ctrl & 1)) {
|
||||
if (mmc_cache_enabled(card->host)) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_FLUSH_CACHE, 1,
|
||||
MMC_CACHE_FLUSH_TIMEOUT_MS);
|
||||
@@ -1031,7 +1009,7 @@ int mmc_cmdq_disable(struct mmc_card *card)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_cmdq_disable);
|
||||
|
||||
int mmc_sanitize(struct mmc_card *card)
|
||||
int mmc_sanitize(struct mmc_card *card, unsigned int timeout_ms)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
int err;
|
||||
@@ -1041,12 +1019,15 @@ int mmc_sanitize(struct mmc_card *card)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (!timeout_ms)
|
||||
timeout_ms = MMC_SANITIZE_TIMEOUT_MS;
|
||||
|
||||
pr_debug("%s: Sanitize in progress...\n", mmc_hostname(host));
|
||||
|
||||
mmc_retune_hold(host);
|
||||
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_SANITIZE_START,
|
||||
1, MMC_SANITIZE_TIMEOUT_MS);
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_SANITIZE_START,
|
||||
1, timeout_ms, 0, true, false, 0);
|
||||
if (err)
|
||||
pr_err("%s: Sanitize failed err=%d\n", mmc_hostname(host), err);
|
||||
|
||||
|
||||
@@ -39,14 +39,14 @@ int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms,
|
||||
enum mmc_busy_cmd busy_cmd);
|
||||
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
unsigned int timeout_ms, unsigned char timing,
|
||||
bool send_status, bool retry_crc_err);
|
||||
bool send_status, bool retry_crc_err, unsigned int retries);
|
||||
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
unsigned int timeout_ms);
|
||||
void mmc_run_bkops(struct mmc_card *card);
|
||||
int mmc_flush_cache(struct mmc_card *card);
|
||||
int mmc_cmdq_enable(struct mmc_card *card);
|
||||
int mmc_cmdq_disable(struct mmc_card *card);
|
||||
int mmc_sanitize(struct mmc_card *card);
|
||||
int mmc_sanitize(struct mmc_card *card, unsigned int timeout_ms);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ enum mmc_issue_type mmc_issue_type(struct mmc_queue *mq, struct request *req)
|
||||
{
|
||||
struct mmc_host *host = mq->card->host;
|
||||
|
||||
if (mq->use_cqe && !host->hsq_enabled)
|
||||
if (host->cqe_enabled && !host->hsq_enabled)
|
||||
return mmc_cqe_issue_type(host, req);
|
||||
|
||||
if (req_op(req) == REQ_OP_READ || req_op(req) == REQ_OP_WRITE)
|
||||
@@ -127,7 +127,7 @@ static enum blk_eh_timer_return mmc_mq_timed_out(struct request *req,
|
||||
bool ignore_tout;
|
||||
|
||||
spin_lock_irqsave(&mq->lock, flags);
|
||||
ignore_tout = mq->recovery_needed || !mq->use_cqe || host->hsq_enabled;
|
||||
ignore_tout = mq->recovery_needed || !host->cqe_enabled || host->hsq_enabled;
|
||||
spin_unlock_irqrestore(&mq->lock, flags);
|
||||
|
||||
return ignore_tout ? BLK_EH_RESET_TIMER : mmc_cqe_timed_out(req);
|
||||
@@ -144,7 +144,7 @@ static void mmc_mq_recovery_handler(struct work_struct *work)
|
||||
|
||||
mq->in_recovery = true;
|
||||
|
||||
if (mq->use_cqe && !host->hsq_enabled)
|
||||
if (host->cqe_enabled && !host->hsq_enabled)
|
||||
mmc_blk_cqe_recovery(mq);
|
||||
else
|
||||
mmc_blk_mq_recovery(mq);
|
||||
@@ -315,7 +315,7 @@ static blk_status_t mmc_mq_queue_rq(struct blk_mq_hw_ctx *hctx,
|
||||
if (get_card)
|
||||
mmc_get_card(card, &mq->ctx);
|
||||
|
||||
if (mq->use_cqe) {
|
||||
if (host->cqe_enabled) {
|
||||
host->retune_now = host->need_retune && cqe_retune_ok &&
|
||||
!host->hold_retune;
|
||||
}
|
||||
@@ -430,7 +430,6 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card)
|
||||
int ret;
|
||||
|
||||
mq->card = card;
|
||||
mq->use_cqe = host->cqe_enabled;
|
||||
|
||||
spin_lock_init(&mq->lock);
|
||||
|
||||
@@ -440,7 +439,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card)
|
||||
* The queue depth for CQE must match the hardware because the request
|
||||
* tag is used to index the hardware queue.
|
||||
*/
|
||||
if (mq->use_cqe && !host->hsq_enabled)
|
||||
if (host->cqe_enabled && !host->hsq_enabled)
|
||||
mq->tag_set.queue_depth =
|
||||
min_t(int, card->ext_csd.cmdq_depth, host->cqe_qdepth);
|
||||
else
|
||||
|
||||
@@ -82,7 +82,6 @@ struct mmc_queue {
|
||||
unsigned int cqe_busy;
|
||||
#define MMC_CQE_DCMD_BUSY BIT(0)
|
||||
bool busy;
|
||||
bool use_cqe;
|
||||
bool recovery_needed;
|
||||
bool in_recovery;
|
||||
bool rw_wait;
|
||||
|
||||
@@ -135,6 +135,9 @@ static int mmc_decode_csd(struct mmc_card *card)
|
||||
csd->erase_size = UNSTUFF_BITS(resp, 39, 7) + 1;
|
||||
csd->erase_size <<= csd->write_blkbits - 9;
|
||||
}
|
||||
|
||||
if (UNSTUFF_BITS(resp, 13, 1))
|
||||
mmc_card_set_readonly(card);
|
||||
break;
|
||||
case 1:
|
||||
/*
|
||||
@@ -169,6 +172,9 @@ static int mmc_decode_csd(struct mmc_card *card)
|
||||
csd->write_blkbits = 9;
|
||||
csd->write_partial = 0;
|
||||
csd->erase_size = 1;
|
||||
|
||||
if (UNSTUFF_BITS(resp, 13, 1))
|
||||
mmc_card_set_readonly(card);
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: unrecognised CSD structure version %d\n",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user