Merge tag 'mmc-v6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc

Pull MMC updates from Ulf Hansson:
 "MMC core:
   - Improve RPMB frame handling code
   - Add support for a new max-sd-hs-hz DT property to limit frequency
   - Add support to manage regulator-under-voltage events
   - Support regulator-under-voltage for eMMC to mitigate data
     corruptions
   - Add mmc_read_tuning() to allow a host to validate its tuning
     sequence
   - Add some helpers to align checks for CMD23 support
   - Read the CCCR register for SDIO over SPI rather than the
     unsupported CMD7

  MMC host:
   - Add COMPILE_TEST option for a couple of drivers
   - Convert drivers to use the modern PM macros
   - dw_mmc-exynos: Enable support for the Exynos8890 variant
   - mmc_spi: Don't use crc ack during multiple block read
   - renesas_sdhi: Enable 64-bit polling mode for R-Car gen3 and
     RZ/G2L SoCs
   - rtsx_usb: Add support for over-current-protection
   - sdhci-cadence: Add support for multi-block read gap tuning
   - sdhci-msm:
       - Add support for tuning for SDR50 mode for SD cards
       - Enable support for the Lemans variant
   - sdhci-pci: Disable SD card clock before update for a few
     Intel platforms
   - sdhci-pxav3: Add support for UHS pinctrl settings
   - tmio: Add 64-bit read/write support in polling mode

  MEMSTICK:
   - Convert to use timeouts to prevent indefinite waiting
   - rtsx_usb: Add support for over-current-protection"

* tag 'mmc-v6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (77 commits)
  dt-bindings: mmc: samsung,exynos-dw-mshc: add specific compatible for exynos8890
  mmc: select REGMAP_MMIO with MMC_LOONGSON2
  mmc: add COMPILE_TEST to multiple drivers
  mmc: core: Improve RPMB frame handling code
  mmc: core: Fix variable shadowing in mmc_route_rpmb_frames()
  mmc: core: Parse and use the new max-sd-hs-hz DT property
  dt-bindings: mmc: controller: Add max-sd-hs-hz property
  mmc: sdhci-msm: Enable tuning for SDR50 mode for SD card
  dt-bindings: mmc: sdhci-msm: Document the Lemans compatible
  mmc: sh_mmcif: Remove dummy PM resume callback
  dt-bindings: mmc: sdhci-pxa: Add minItems to pinctrl-names
  mmc: sdio: Drop dev_pm_domain_detach() call
  mmc: dw_mmc-rockchip: use modern PM macros
  mmc: dw_mmc-pci: use modern PM macros
  mmc: dw_mmc-k3: use modern PM macros
  mmc: dw_mmc: exynos: use modern PM macros
  mmc: via-sdmmc: use modern PM macros
  mmc: sdhci-msm: use modern PM macros
  mmc: mtk-sd: use modern PM macros
  mmc: wmt-sdmmc: use modern PM macros
  ...
This commit is contained in:
Linus Torvalds
2025-10-01 11:54:40 -07:00
71 changed files with 754 additions and 364 deletions
@@ -90,6 +90,7 @@ required:
allOf:
- $ref: sdhci-common.yaml#
- $ref: mmc-controller-common.yaml#
unevaluatedProperties: false
@@ -93,6 +93,14 @@ properties:
minimum: 400000
maximum: 384000000
max-sd-hs-hz:
description: |
Maximum frequency (in Hz) to be used for SD cards operating in
High-Speed (HS) mode.
minimum: 400000
maximum: 50000000
default: 50000000
disable-wp:
$ref: /schemas/types.yaml#/definitions/flag
description:
@@ -31,6 +31,7 @@ properties:
- samsung,exynos5433-dw-mshc-smu
- samsung,exynos7885-dw-mshc-smu
- samsung,exynos850-dw-mshc-smu
- samsung,exynos8890-dw-mshc-smu
- samsung,exynos8895-dw-mshc-smu
- const: samsung,exynos7-dw-mshc-smu
@@ -48,6 +48,7 @@ properties:
- qcom,qcs615-sdhci
- qcom,qcs8300-sdhci
- qcom,qdu1000-sdhci
- qcom,sa8775p-sdhci
- qcom,sar2130p-sdhci
- qcom,sc7180-sdhci
- qcom,sc7280-sdhci
@@ -44,12 +44,29 @@ allOf:
items:
- const: default
- const: state_cmd_gpio
pinctrl-0:
description:
Should contain default pinctrl.
minItems: 1
pinctrl-1:
description:
Should switch CMD pin to GPIO mode as a high output.
- if:
properties:
compatible:
contains:
const: mrvl,pxav3-mmc
then:
properties:
pinctrl-names:
description:
Optional for increasing stability of the controller at fast bus clocks.
items:
- const: default
- const: state_uhs
minItems: 1
pinctrl-1:
description:
Should switch the drive strength of the data pins to high.
properties:
compatible:
@@ -82,6 +99,14 @@ properties:
- const: io
- const: core
pinctrl-names: true
pinctrl-0:
description:
Should contain default pinctrl.
pinctrl-1: true
mrvl,clk-delay-cycles:
description: Specify a number of cycles to delay for tuning.
$ref: /schemas/types.yaml#/definitions/uint32
+6 -2
View File
@@ -370,7 +370,9 @@ int memstick_set_rw_addr(struct memstick_dev *card)
{
card->next_request = h_memstick_set_rw_addr;
memstick_new_req(card->host);
wait_for_completion(&card->mrq_complete);
if (!wait_for_completion_timeout(&card->mrq_complete,
msecs_to_jiffies(500)))
card->current_mrq.error = -ETIMEDOUT;
return card->current_mrq.error;
}
@@ -404,7 +406,9 @@ static struct memstick_dev *memstick_alloc_card(struct memstick_host *host)
card->next_request = h_memstick_read_dev_id;
memstick_new_req(host);
wait_for_completion(&card->mrq_complete);
if (!wait_for_completion_timeout(&card->mrq_complete,
msecs_to_jiffies(500)))
card->current_mrq.error = -ETIMEDOUT;
if (card->current_mrq.error)
goto err_out;
+4 -1
View File
@@ -216,7 +216,10 @@ static int ms_power_off(struct rtsx_usb_ms *host)
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_EN, MS_CLK_EN, 0);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE, MS_OUTPUT_EN, 0);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
POWER_MASK, POWER_OFF);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
POWER_MASK | LDO3318_PWR_MASK, POWER_OFF | LDO_SUSPEND);
err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
if (err < 0)
return err;
+7
View File
@@ -552,6 +552,10 @@ static int rtsx_usb_reset_chip(struct rtsx_ucr *ucr)
ret = rtsx_usb_send_cmd(ucr, MODE_C, 100);
if (ret)
return ret;
/* config OCP */
rtsx_usb_write_register(ucr, OCPCTL, MS_OCP_DETECT_EN, MS_OCP_DETECT_EN);
rtsx_usb_write_register(ucr, OCPPARA1, 0xF0, 0x50);
rtsx_usb_write_register(ucr, OCPPARA2, 0x7, 0x3);
/* config non-crystal mode */
rtsx_usb_read_register(ucr, CFG_MODE, &val);
@@ -722,6 +726,9 @@ static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message)
if (val & (SD_CD | MS_CD)) {
device_for_each_child(&intf->dev, NULL, rtsx_usb_resume_child);
return -EAGAIN;
} else {
/* if the card does not exists, clear OCP status */
rtsx_usb_write_register(ucr, OCPCTL, MS_OCP_CLEAR, MS_OCP_CLEAR);
}
} else {
/* There is an ongoing operation*/
+19 -27
View File
@@ -121,6 +121,10 @@ struct rpmb_frame {
#define RPMB_READ_DATA 0x4 /* Read data from RPMB partition */
#define RPMB_RESULT_READ 0x5 /* Read result request (Internal) */
#define RPMB_FRAME_SIZE sizeof(struct rpmb_frame)
#define CHECK_SIZE_NEQ(val) ((val) != sizeof(struct rpmb_frame))
#define CHECK_SIZE_ALIGNED(val) IS_ALIGNED((val), sizeof(struct rpmb_frame))
static DEFINE_MUTEX(block_mutex);
/*
@@ -1768,8 +1772,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
* these, while retaining features like reliable writes.
*/
if ((md->flags & MMC_BLK_CMD23) && mmc_op_multi(brq->cmd.opcode) &&
(do_rel_wr || !(card->quirks & MMC_QUIRK_BLK_NO_CMD23) ||
do_data_tag)) {
(do_rel_wr || !mmc_card_blk_no_cmd23(card) || do_data_tag)) {
brq->sbc.opcode = MMC_SET_BLOCK_COUNT;
brq->sbc.arg = brq->data.blocks |
(do_rel_wr ? (1 << 31) : 0) |
@@ -2618,13 +2621,8 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
*/
md->read_only = mmc_blk_readonly(card);
if (mmc_host_can_cmd23(card->host)) {
if ((mmc_card_mmc(card) &&
card->csd.mmca_vsn >= CSD_SPEC_VER_3) ||
(mmc_card_sd(card) && !mmc_card_ult_capacity(card) &&
card->scr.cmds & SD_SCR_CMD23_SUPPORT))
md->flags |= MMC_BLK_CMD23;
}
if (mmc_host_can_cmd23(card->host) && mmc_card_can_cmd23(card))
md->flags |= MMC_BLK_CMD23;
if (md->flags & MMC_BLK_CMD23 &&
((card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN) ||
@@ -2864,12 +2862,12 @@ static void set_idata(struct mmc_blk_ioc_data *idata, u32 opcode,
* The size of an RPMB frame must match what's expected by the
* hardware.
*/
BUILD_BUG_ON(sizeof(struct rpmb_frame) != 512);
static_assert(!CHECK_SIZE_NEQ(512), "RPMB frame size must be 512 bytes");
idata->ic.opcode = opcode;
idata->ic.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
idata->ic.write_flag = write_flag;
idata->ic.blksz = sizeof(struct rpmb_frame);
idata->ic.blksz = RPMB_FRAME_SIZE;
idata->ic.blocks = buf_bytes / idata->ic.blksz;
idata->buf = buf;
idata->buf_bytes = buf_bytes;
@@ -2893,32 +2891,28 @@ static int mmc_route_rpmb_frames(struct device *dev, u8 *req,
if (IS_ERR(md->queue.card))
return PTR_ERR(md->queue.card);
if (req_len < sizeof(*frm))
if (req_len < RPMB_FRAME_SIZE)
return -EINVAL;
req_type = be16_to_cpu(frm->req_resp);
switch (req_type) {
case RPMB_PROGRAM_KEY:
if (req_len != sizeof(struct rpmb_frame) ||
resp_len != sizeof(struct rpmb_frame))
if (CHECK_SIZE_NEQ(req_len) || CHECK_SIZE_NEQ(resp_len))
return -EINVAL;
write = true;
break;
case RPMB_GET_WRITE_COUNTER:
if (req_len != sizeof(struct rpmb_frame) ||
resp_len != sizeof(struct rpmb_frame))
if (CHECK_SIZE_NEQ(req_len) || CHECK_SIZE_NEQ(resp_len))
return -EINVAL;
write = false;
break;
case RPMB_WRITE_DATA:
if (req_len % sizeof(struct rpmb_frame) ||
resp_len != sizeof(struct rpmb_frame))
if (!CHECK_SIZE_ALIGNED(req_len) || CHECK_SIZE_NEQ(resp_len))
return -EINVAL;
write = true;
break;
case RPMB_READ_DATA:
if (req_len != sizeof(struct rpmb_frame) ||
resp_len % sizeof(struct rpmb_frame))
if (CHECK_SIZE_NEQ(req_len) || !CHECK_SIZE_ALIGNED(resp_len))
return -EINVAL;
write = false;
break;
@@ -2926,25 +2920,23 @@ static int mmc_route_rpmb_frames(struct device *dev, u8 *req,
return -EINVAL;
}
if (write)
cmd_count = 3;
else
cmd_count = 2;
/* Write operations require 3 commands, read operations require 2 */
cmd_count = write ? 3 : 2;
idata = alloc_idata(rpmb, cmd_count);
if (!idata)
return -ENOMEM;
if (write) {
struct rpmb_frame *frm = (struct rpmb_frame *)resp;
struct rpmb_frame *resp_frm = (struct rpmb_frame *)resp;
/* Send write request frame(s) */
set_idata(idata[0], MMC_WRITE_MULTIPLE_BLOCK,
1 | MMC_CMD23_ARG_REL_WR, req, req_len);
/* Send result request frame */
memset(frm, 0, sizeof(*frm));
frm->req_resp = cpu_to_be16(RPMB_RESULT_READ);
memset(resp_frm, 0, RPMB_FRAME_SIZE);
resp_frm->req_resp = cpu_to_be16(RPMB_RESULT_READ);
set_idata(idata[1], MMC_WRITE_MULTIPLE_BLOCK, 1, resp,
resp_len);
+12
View File
@@ -19,6 +19,7 @@
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include "core.h"
#include "card.h"
@@ -383,6 +384,14 @@ int mmc_add_card(struct mmc_card *card)
mmc_card_set_present(card);
/*
* Register for undervoltage notification if the card supports
* power-off notification, enabling emergency shutdowns.
*/
if (mmc_card_mmc(card) &&
card->ext_csd.power_off_notification == EXT_CSD_POWER_ON)
mmc_regulator_register_undervoltage_notifier(card->host);
return 0;
}
@@ -394,6 +403,9 @@ void mmc_remove_card(struct mmc_card *card)
{
struct mmc_host *host = card->host;
if (mmc_card_present(card))
mmc_regulator_unregister_undervoltage_notifier(host);
mmc_remove_card_debugfs(card);
if (mmc_card_present(card)) {
+7 -2
View File
@@ -245,14 +245,19 @@ static inline int mmc_blksz_for_byte_mode(const struct mmc_card *c)
return c->quirks & MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
}
static inline int mmc_card_nonstd_func_interface(const struct mmc_card *c)
{
return c->quirks & MMC_QUIRK_NONSTD_FUNC_IF;
}
static inline int mmc_card_disable_cd(const struct mmc_card *c)
{
return c->quirks & MMC_QUIRK_DISABLE_CD;
}
static inline int mmc_card_nonstd_func_interface(const struct mmc_card *c)
static inline int mmc_card_blk_no_cmd23(const struct mmc_card *c)
{
return c->quirks & MMC_QUIRK_NONSTD_FUNC_IF;
return c->quirks & MMC_QUIRK_BLK_NO_CMD23;
}
static inline int mmc_card_broken_byte_mode_512(const struct mmc_card *c)
+32
View File
@@ -1398,6 +1398,29 @@ void mmc_power_cycle(struct mmc_host *host, u32 ocr)
mmc_power_up(host, ocr);
}
/**
* mmc_handle_undervoltage - Handle an undervoltage event on the MMC bus
* @host: The MMC host that detected the undervoltage condition
*
* This function is called when an undervoltage event is detected on one of
* the MMC regulators.
*
* Returns: 0 on success or a negative error code on failure.
*/
int mmc_handle_undervoltage(struct mmc_host *host)
{
/* Stop the host to prevent races with card removal */
__mmc_stop_host(host);
if (!host->bus_ops || !host->bus_ops->handle_undervoltage)
return 0;
dev_warn(mmc_dev(host), "%s: Undervoltage detected, initiating emergency stop\n",
mmc_hostname(host));
return host->bus_ops->handle_undervoltage(host);
}
/*
* Assign a mmc bus handler to a host. Only one bus handler may control a
* host at any given time.
@@ -1875,6 +1898,15 @@ bool mmc_card_can_secure_erase_trim(struct mmc_card *card)
}
EXPORT_SYMBOL(mmc_card_can_secure_erase_trim);
bool mmc_card_can_cmd23(struct mmc_card *card)
{
return ((mmc_card_mmc(card) &&
card->csd.mmca_vsn >= CSD_SPEC_VER_3) ||
(mmc_card_sd(card) && !mmc_card_ult_capacity(card) &&
card->scr.cmds & SD_SCR_CMD23_SUPPORT));
}
EXPORT_SYMBOL(mmc_card_can_cmd23);
int mmc_erase_group_aligned(struct mmc_card *card, sector_t from,
unsigned int nr)
{
+6
View File
@@ -31,6 +31,7 @@ struct mmc_bus_ops {
int (*sw_reset)(struct mmc_host *);
bool (*cache_enabled)(struct mmc_host *);
int (*flush_cache)(struct mmc_host *);
int (*handle_undervoltage)(struct mmc_host *host);
};
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
@@ -59,6 +60,10 @@ void mmc_power_off(struct mmc_host *host);
void mmc_power_cycle(struct mmc_host *host, u32 ocr);
void mmc_set_initial_state(struct mmc_host *host);
u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max);
int mmc_handle_undervoltage(struct mmc_host *host);
void mmc_regulator_register_undervoltage_notifier(struct mmc_host *host);
void mmc_regulator_unregister_undervoltage_notifier(struct mmc_host *host);
void mmc_undervoltage_workfn(struct work_struct *work);
static inline void mmc_delay(unsigned int ms)
{
@@ -123,6 +128,7 @@ bool mmc_card_can_trim(struct mmc_card *card);
bool mmc_card_can_discard(struct mmc_card *card);
bool mmc_card_can_sanitize(struct mmc_card *card);
bool mmc_card_can_secure_erase_trim(struct mmc_card *card);
bool mmc_card_can_cmd23(struct mmc_card *card);
int mmc_erase_group_aligned(struct mmc_card *card, sector_t from, unsigned int nr);
unsigned int mmc_calc_max_discard(struct mmc_card *card);
+4
View File
@@ -302,6 +302,8 @@ int mmc_of_parse(struct mmc_host *host)
/* f_max is obtained from the optional "max-frequency" property */
device_property_read_u32(dev, "max-frequency", &host->f_max);
device_property_read_u32(dev, "max-sd-hs-hz", &host->max_sd_hs_hz);
/*
* Configure CD and WP pins. They are both by default active low to
* match the SDHCI spec. If GPIOs are provided for CD and / or WP, the
@@ -564,6 +566,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
INIT_WORK(&host->sdio_irq_work, sdio_irq_work);
timer_setup(&host->retune_timer, mmc_retune_timer, 0);
INIT_WORK(&host->supply.uv_work, mmc_undervoltage_workfn);
/*
* By default, hosts do not support SGIO or large requests.
* They have to set these according to their abilities.
+67 -3
View File
@@ -36,6 +36,7 @@
enum mmc_poweroff_type {
MMC_POWEROFF_SUSPEND,
MMC_POWEROFF_SHUTDOWN,
MMC_POWEROFF_UNDERVOLTAGE,
MMC_POWEROFF_UNBIND,
};
@@ -2132,9 +2133,15 @@ static int _mmc_suspend(struct mmc_host *host, enum mmc_poweroff_type pm_type)
if (mmc_card_suspended(host->card))
goto out;
err = _mmc_flush_cache(host);
if (err)
goto out;
/*
* For the undervoltage case, we care more about device integrity.
* Avoid cache flush and notify the device to power off quickly.
*/
if (pm_type != MMC_POWEROFF_UNDERVOLTAGE) {
err = _mmc_flush_cache(host);
if (err)
goto out;
}
if (mmc_card_can_poweroff_notify(host->card) &&
mmc_host_can_poweroff_notify(host, pm_type))
@@ -2212,6 +2219,13 @@ static int mmc_shutdown(struct mmc_host *host)
{
int err = 0;
/*
* In case of undervoltage, the card will be powered off (removed) by
* _mmc_handle_undervoltage()
*/
if (mmc_card_removed(host->card))
return 0;
/*
* If the card remains suspended at this point and it was done by using
* the sleep-cmd (CMD5), we may need to re-initialize it first, to allow
@@ -2302,6 +2316,55 @@ static int _mmc_hw_reset(struct mmc_host *host)
return mmc_init_card(host, card->ocr, card);
}
/**
* _mmc_handle_undervoltage - Handle an undervoltage event for MMC/eMMC devices
* @host: MMC host structure
*
* This function is triggered when an undervoltage condition is detected.
* It attempts to transition the device into a low-power or safe state to
* prevent data corruption.
*
* Steps performed:
* - Perform an emergency suspend using EXT_CSD_POWER_OFF_SHORT if possible.
* - If power-off notify is not supported, fallback mechanisms like sleep or
* deselecting the card are attempted.
* - Cache flushing is skipped to reduce execution time.
* - Mark the card as removed to prevent further interactions after
* undervoltage.
*
* Note: This function does not handle host claiming or releasing. The caller
* must ensure that the host is properly claimed before calling this
* function and released afterward.
*
* Returns: 0 on success, or a negative error code if any step fails.
*/
static int _mmc_handle_undervoltage(struct mmc_host *host)
{
struct mmc_card *card = host->card;
int err;
/*
* Perform an emergency suspend to power off the eMMC quickly.
* This ensures the device enters a safe state before power is lost.
* We first attempt EXT_CSD_POWER_OFF_SHORT, but if power-off notify
* is not supported, we fall back to sleep mode or deselecting the card.
* Cache flushing is skipped to minimize delay.
*/
err = _mmc_suspend(host, MMC_POWEROFF_UNDERVOLTAGE);
if (err)
pr_err("%s: undervoltage suspend failed: %pe\n",
mmc_hostname(host), ERR_PTR(err));
/*
* Mark the card as removed to prevent further operations.
* This ensures the system does not attempt to access the device
* after an undervoltage event, avoiding potential corruption.
*/
mmc_card_set_removed(card);
return err;
}
static const struct mmc_bus_ops mmc_ops = {
.remove = mmc_remove,
.detect = mmc_detect,
@@ -2314,6 +2377,7 @@ static const struct mmc_bus_ops mmc_ops = {
.hw_reset = _mmc_hw_reset,
.cache_enabled = _mmc_cache_enabled,
.flush_cache = _mmc_flush_cache,
.handle_undervoltage = _mmc_handle_undervoltage,
};
/*
+72
View File
@@ -1077,3 +1077,75 @@ int mmc_sanitize(struct mmc_card *card, unsigned int timeout_ms)
return err;
}
EXPORT_SYMBOL_GPL(mmc_sanitize);
/**
* mmc_read_tuning() - read data blocks from the mmc
* @host: mmc host doing the read
* @blksz: data block size
* @blocks: number of blocks to read
*
* Read one or more blocks of data from the beginning of the mmc. This is a
* low-level helper for tuning operation. It is assumed that CMD23 can be used
* for multi-block read if the host supports it.
*
* Note: Allocate and free a temporary buffer to store the data read. The data
* is not available outside of the function, only the status of the read
* operation.
*
* Return: 0 in case of success, otherwise -EIO / -ENOMEM / -E2BIG
*/
int mmc_read_tuning(struct mmc_host *host, unsigned int blksz, unsigned int blocks)
{
struct mmc_request mrq = {};
struct mmc_command sbc = {};
struct mmc_command cmd = {};
struct mmc_command stop = {};
struct mmc_data data = {};
struct scatterlist sg;
void *buf;
unsigned int len;
if (blocks > 1) {
if (mmc_host_can_cmd23(host)) {
mrq.sbc = &sbc;
sbc.opcode = MMC_SET_BLOCK_COUNT;
sbc.arg = blocks;
sbc.flags = MMC_RSP_R1 | MMC_CMD_AC;
}
cmd.opcode = MMC_READ_MULTIPLE_BLOCK;
mrq.stop = &stop;
stop.opcode = MMC_STOP_TRANSMISSION;
stop.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
} else {
cmd.opcode = MMC_READ_SINGLE_BLOCK;
}
mrq.cmd = &cmd;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
mrq.data = &data;
data.flags = MMC_DATA_READ;
data.blksz = blksz;
data.blocks = blocks;
data.blk_addr = 0;
data.sg = &sg;
data.sg_len = 1;
data.timeout_ns = 1000000000;
if (check_mul_overflow(blksz, blocks, &len))
return -E2BIG;
buf = kmalloc(len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
sg_init_one(&sg, buf, len);
mmc_wait_for_req(host, &mrq);
kfree(buf);
if (sbc.error || cmd.error || data.error)
return -EIO;
return 0;
}
EXPORT_SYMBOL_GPL(mmc_read_tuning);
+2 -8
View File
@@ -180,20 +180,14 @@ static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size)
return mmc_set_blocklen(test->card, size);
}
static bool mmc_test_card_cmd23(struct mmc_card *card)
{
return mmc_card_mmc(card) ||
(mmc_card_sd(card) && card->scr.cmds & SD_SCR_CMD23_SUPPORT);
}
static void mmc_test_prepare_sbc(struct mmc_test_card *test,
struct mmc_request *mrq, unsigned int blocks)
{
struct mmc_card *card = test->card;
if (!mrq->sbc || !mmc_host_can_cmd23(card->host) ||
!mmc_test_card_cmd23(card) || !mmc_op_multi(mrq->cmd->opcode) ||
(card->quirks & MMC_QUIRK_BLK_NO_CMD23)) {
!mmc_card_can_cmd23(card) || !mmc_op_multi(mrq->cmd->opcode) ||
mmc_card_blk_no_cmd23(card)) {
mrq->sbc = NULL;
return;
}
+77
View File
@@ -7,6 +7,7 @@
#include <linux/err.h>
#include <linux/log2.h>
#include <linux/regulator/consumer.h>
#include <linux/workqueue.h>
#include <linux/mmc/host.h>
@@ -262,6 +263,82 @@ static inline int mmc_regulator_get_ocrmask(struct regulator *supply)
#endif /* CONFIG_REGULATOR */
/* To be called from a high-priority workqueue */
void mmc_undervoltage_workfn(struct work_struct *work)
{
struct mmc_supply *supply;
struct mmc_host *host;
supply = container_of(work, struct mmc_supply, uv_work);
host = container_of(supply, struct mmc_host, supply);
mmc_handle_undervoltage(host);
}
static int mmc_handle_regulator_event(struct notifier_block *nb,
unsigned long event, void *data)
{
struct mmc_supply *supply = container_of(nb, struct mmc_supply,
vmmc_nb);
struct mmc_host *host = container_of(supply, struct mmc_host, supply);
unsigned long flags;
switch (event) {
case REGULATOR_EVENT_UNDER_VOLTAGE:
spin_lock_irqsave(&host->lock, flags);
if (host->undervoltage) {
spin_unlock_irqrestore(&host->lock, flags);
return NOTIFY_OK;
}
host->undervoltage = true;
spin_unlock_irqrestore(&host->lock, flags);
queue_work(system_highpri_wq, &host->supply.uv_work);
break;
default:
return NOTIFY_DONE;
}
return NOTIFY_OK;
}
/**
* mmc_regulator_register_undervoltage_notifier - Register for undervoltage
* events
* @host: MMC host
*
* To be called by a bus driver when a card supporting graceful shutdown
* is attached.
*/
void mmc_regulator_register_undervoltage_notifier(struct mmc_host *host)
{
int ret;
if (IS_ERR_OR_NULL(host->supply.vmmc))
return;
host->supply.vmmc_nb.notifier_call = mmc_handle_regulator_event;
ret = regulator_register_notifier(host->supply.vmmc,
&host->supply.vmmc_nb);
if (ret)
dev_warn(mmc_dev(host), "Failed to register vmmc notifier: %d\n", ret);
}
/**
* mmc_regulator_unregister_undervoltage_notifier - Unregister undervoltage
* notifier
* @host: MMC host
*/
void mmc_regulator_unregister_undervoltage_notifier(struct mmc_host *host)
{
if (IS_ERR_OR_NULL(host->supply.vmmc))
return;
regulator_unregister_notifier(host->supply.vmmc, &host->supply.vmmc_nb);
cancel_work_sync(&host->supply.uv_work);
}
/**
* mmc_regulator_get_supply - try to get VMMC and VQMMC regulators for a host
* @mmc: the host to regulate
+1 -1
View File
@@ -359,7 +359,7 @@ static int mmc_read_switch(struct mmc_card *card)
}
if (status[13] & SD_MODE_HIGH_SPEED)
card->sw_caps.hs_max_dtr = HIGH_SPEED_MAX_DTR;
card->sw_caps.hs_max_dtr = card->host->max_sd_hs_hz ?: HIGH_SPEED_MAX_DTR;
if (card->scr.sda_spec3) {
card->sw_caps.sd3_bus_mode = status[13];
+5 -1
View File
@@ -945,7 +945,11 @@ static void mmc_sdio_remove(struct mmc_host *host)
*/
static int mmc_sdio_alive(struct mmc_host *host)
{
return mmc_select_card(host->card);
if (!mmc_host_is_spi(host))
return mmc_select_card(host->card);
else
return mmc_io_rw_direct(host->card, 0, 0, SDIO_CCCR_CCCR, 0,
NULL);
}
/*

Some files were not shown because too many files have changed in this diff Show More