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
  - sdhci-msm: 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
 -----BEGIN PGP SIGNATURE-----
 
 iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmjafZQXHHVsZi5oYW5z
 c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCmSng//QMGmS6uWiPeVG237VwtZ0qir
 XB7N4H0ig0wa/Qxqc9soXrBDmlKKVFaf/VvZOsqKHy3QjDGV+i6a2WVj+rao4vzi
 /M3jmJK+vKGjNah/jbZEu7hUMY22buiPiRbUUv3UUmpBTxqXdsyTX+hmHJdiRQ7B
 jC5GGPMbv1wMUj3ivkuwwlabRYFMy1QwFfDZxQzhEI4Codfs1P0nr2PFvCJ/r8nq
 TsPYkDWevxItnyxSkaDK7bK4yGFOKnnhf7aj1d4NMS6AX2FH3QPDP09rmdHwrdZb
 prgbccQusxoK2zJFwOqoTucYeNmhXgyAvuoj9kGtBkzbyTjorh2fdXwzGAnAkYqZ
 ygoIvq/lPQj6ppXd5BTfldAt69/Yyj6tyWuVMJ2EdoL0yfO2xPv0lnUdrN73ATZB
 jaRJdvF9EiGSfG2DzzK0W1CTpKvHg4pRA6InGgok1xIVM68iOwQcBDcU9D3tDkZo
 4HpuEGVJnso0q9xh0dO36HmryKsQge5jfq3mTe3Y+kHTteWYMh7VO4FhZL+AV2fp
 LDL1aLn21Q71TH+aqYA6DVn3wrXWkhg9U2Uz//SU4lv18o3Fc47GJBtbvb0nEqXX
 1BUJ5QcAeNMHo2Xm7yc5YwBpghCTCZDJOogzyXaQW0nbMvn+11X4gCREYgNBP7cv
 mrygbFCHLJDKscE0ZkY=
 =D2yD
 -----END PGP SIGNATURE-----

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
commit 2d27453624
71 changed files with 754 additions and 364 deletions

View File

@ -90,6 +90,7 @@ required:
allOf:
- $ref: sdhci-common.yaml#
- $ref: mmc-controller-common.yaml#
unevaluatedProperties: false

View File

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

View File

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

View File

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

View File

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

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;

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;

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

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

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

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)

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

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

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.

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,
};
/*

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

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

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

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

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);
}
/*

View File

@ -200,7 +200,6 @@ static int sdio_bus_probe(struct device *dev)
atomic_dec(&func->card->sdio_funcs_probed);
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
pm_runtime_put_noidle(dev);
dev_pm_domain_detach(dev, false);
return ret;
}
@ -231,8 +230,6 @@ static void sdio_bus_remove(struct device *dev)
/* Then undo the runtime PM settings in sdio_bus_probe() */
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
pm_runtime_put_sync(dev);
dev_pm_domain_detach(dev, false);
}
static const struct dev_pm_ops sdio_bus_pm_ops = {

View File

@ -56,7 +56,7 @@ config MMC_STM32_SDMMC
config MMC_PXA
tristate "Intel PXA25x/26x/27x Multimedia Card Interface support"
depends on ARCH_PXA
depends on ARCH_PXA || COMPILE_TEST
help
This selects the Intel(R) PXA(R) Multimedia card Interface.
If you have a PXA(R) platform with a Multimedia Card slot,
@ -359,7 +359,7 @@ config MMC_SDHCI_S3C
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
often referred to as the HSMMC block in some of the Samsung
S3C6410, S5Pv210 and Exynos (Exynso4210, Exynos4412) SoCs.
If you have a controller with this interface (thereforeyou build for
@ -401,7 +401,7 @@ config MMC_SDHCI_SPEAR
depends on OF
help
This selects the Secure Digital Host Controller Interface (SDHCI)
often referrered to as the HSMMC block in some of the ST SPEAR range
often referred to as the HSMMC block in some of the ST SPEAR range
of SoC
If you have a controller with this interface, say Y or M here.
@ -608,7 +608,7 @@ config MMC_SDHCI_MSM
config MMC_MXC
tristate "Freescale i.MX21/27/31 or MPC512x Multimedia Card support"
depends on ARCH_MXC || PPC_MPC512x
depends on ARCH_MXC || PPC_MPC512x || COMPILE_TEST
help
This selects the Freescale i.MX21, i.MX27, i.MX31 or MPC512x
Multimedia Card Interface. If you have an i.MX or MPC512x platform
@ -866,7 +866,8 @@ config MMC_DW_PCI
config MMC_DW_ROCKCHIP
tristate "Rockchip specific extensions for Synopsys DW Memory Card Interface"
depends on MMC_DW && ARCH_ROCKCHIP
depends on MMC_DW
depends on ARCH_ROCKCHIP || COMPILE_TEST
select MMC_DW_PLTFM
help
This selects support for Rockchip SoC specific extensions to the
@ -948,7 +949,7 @@ config MMC_USHC
config MMC_WMT
tristate "Wondermedia SD/MMC Host Controller support"
depends on ARCH_VT8500
depends on ARCH_VT8500 || COMPILE_TEST
default y
help
This selects support for the SD/MMC Host Controller on
@ -1115,6 +1116,7 @@ config MMC_LOONGSON2
tristate "Loongson-2K SD/SDIO/eMMC Host Interface support"
depends on LOONGARCH || COMPILE_TEST
depends on HAS_DMA
select REGMAP_MMIO
help
This selects support for the SD/SDIO/eMMC Host Controller on
Loongson-2K series CPUs.

View File

@ -1129,7 +1129,6 @@ static void alcor_pci_sdmmc_drv_remove(struct platform_device *pdev)
mmc_remove_host(mmc);
}
#ifdef CONFIG_PM_SLEEP
static int alcor_pci_sdmmc_suspend(struct device *dev)
{
struct alcor_sdmmc_host *host = dev_get_drvdata(dev);
@ -1150,10 +1149,9 @@ static int alcor_pci_sdmmc_resume(struct device *dev)
return 0;
}
#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(alcor_mmc_pm_ops, alcor_pci_sdmmc_suspend,
alcor_pci_sdmmc_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(alcor_mmc_pm_ops, alcor_pci_sdmmc_suspend,
alcor_pci_sdmmc_resume);
static const struct platform_device_id alcor_pci_sdmmc_ids[] = {
{
@ -1171,7 +1169,7 @@ static struct platform_driver alcor_pci_sdmmc_driver = {
.driver = {
.name = DRV_NAME_ALCOR_PCI_SDMMC,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &alcor_mmc_pm_ops
.pm = pm_sleep_ptr(&alcor_mmc_pm_ops),
},
};
module_platform_driver(alcor_pci_sdmmc_driver);

View File

@ -2622,7 +2622,6 @@ static void atmci_remove(struct platform_device *pdev)
pm_runtime_put_noidle(dev);
}
#ifdef CONFIG_PM
static int atmci_runtime_suspend(struct device *dev)
{
struct atmel_mci *host = dev_get_drvdata(dev);
@ -2642,12 +2641,10 @@ static int atmci_runtime_resume(struct device *dev)
return clk_prepare_enable(host->mck);
}
#endif
static const struct dev_pm_ops atmci_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(atmci_runtime_suspend, atmci_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
RUNTIME_PM_OPS(atmci_runtime_suspend, atmci_runtime_resume, NULL)
};
static struct platform_driver atmci_driver = {
@ -2657,7 +2654,7 @@ static struct platform_driver atmci_driver = {
.name = "atmel_mci",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = atmci_dt_ids,
.pm = &atmci_dev_pm_ops,
.pm = pm_ptr(&atmci_dev_pm_ops),
},
};
module_platform_driver(atmci_driver);

View File

@ -1150,10 +1150,9 @@ static void au1xmmc_remove(struct platform_device *pdev)
}
}
#ifdef CONFIG_PM
static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state)
static int au1xmmc_suspend(struct device *dev)
{
struct au1xmmc_host *host = platform_get_drvdata(pdev);
struct au1xmmc_host *host = dev_get_drvdata(dev);
__raw_writel(0, HOST_CONFIG2(host));
__raw_writel(0, HOST_CONFIG(host));
@ -1164,27 +1163,24 @@ static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
static int au1xmmc_resume(struct platform_device *pdev)
static int au1xmmc_resume(struct device *dev)
{
struct au1xmmc_host *host = platform_get_drvdata(pdev);
struct au1xmmc_host *host = dev_get_drvdata(dev);
au1xmmc_reset_controller(host);
return 0;
}
#else
#define au1xmmc_suspend NULL
#define au1xmmc_resume NULL
#endif
static DEFINE_SIMPLE_DEV_PM_OPS(au1xmmc_pmops, au1xmmc_suspend, au1xmmc_resume);
static struct platform_driver au1xmmc_driver = {
.probe = au1xmmc_probe,
.remove = au1xmmc_remove,
.suspend = au1xmmc_suspend,
.resume = au1xmmc_resume,
.driver = {
.name = DRIVER_NAME,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = pm_sleep_ptr(&au1xmmc_pmops),
},
};

View File

@ -664,25 +664,25 @@ static const struct mmc_host_ops cb710_mmc_host = {
.get_cd = cb710_mmc_get_cd,
};
#ifdef CONFIG_PM
static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state)
static int cb710_mmc_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct cb710_slot *slot = cb710_pdev_to_slot(pdev);
cb710_mmc_enable_irq(slot, 0, ~0);
return 0;
}
static int cb710_mmc_resume(struct platform_device *pdev)
static int cb710_mmc_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct cb710_slot *slot = cb710_pdev_to_slot(pdev);
cb710_mmc_enable_irq(slot, 0, ~0);
return 0;
}
#endif /* CONFIG_PM */
static DEFINE_SIMPLE_DEV_PM_OPS(cb710_mmc_pmops, cb710_mmc_suspend, cb710_mmc_resume);
static int cb710_mmc_init(struct platform_device *pdev)
{
@ -767,13 +767,12 @@ static void cb710_mmc_exit(struct platform_device *pdev)
}
static struct platform_driver cb710_mmc_driver = {
.driver.name = "cb710-mmc",
.driver = {
.name = "cb710-mmc",
.pm = pm_sleep_ptr(&cb710_mmc_pmops),
},
.probe = cb710_mmc_init,
.remove = cb710_mmc_exit,
#ifdef CONFIG_PM
.suspend = cb710_mmc_suspend,
.resume = cb710_mmc_resume,
#endif
};
module_platform_driver(cb710_mmc_driver);

View File

@ -588,7 +588,7 @@ static void mmc_davinci_request(struct mmc_host *mmc, struct mmc_request *req)
cpu_relax();
}
if (mmcst1 & MMCST1_BUSY) {
dev_err(mmc_dev(host->mmc), "still BUSY? bad ... \n");
dev_err(mmc_dev(host->mmc), "still BUSY? bad ...\n");
req->cmd->error = -ETIMEDOUT;
mmc_request_done(mmc, req);
return;
@ -1347,7 +1347,6 @@ static void davinci_mmcsd_remove(struct platform_device *pdev)
clk_disable_unprepare(host->clk);
}
#ifdef CONFIG_PM
static int davinci_mmcsd_suspend(struct device *dev)
{
struct mmc_davinci_host *host = dev_get_drvdata(dev);
@ -1373,21 +1372,14 @@ static int davinci_mmcsd_resume(struct device *dev)
return 0;
}
static const struct dev_pm_ops davinci_mmcsd_pm = {
.suspend = davinci_mmcsd_suspend,
.resume = davinci_mmcsd_resume,
};
#define davinci_mmcsd_pm_ops (&davinci_mmcsd_pm)
#else
#define davinci_mmcsd_pm_ops NULL
#endif
static DEFINE_SIMPLE_DEV_PM_OPS(davinci_mmcsd_pm_ops,
davinci_mmcsd_suspend, davinci_mmcsd_resume);
static struct platform_driver davinci_mmcsd_driver = {
.driver = {
.name = "davinci_mmc",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = davinci_mmcsd_pm_ops,
.pm = pm_sleep_ptr(&davinci_mmcsd_pm_ops),
.of_match_table = davinci_mmc_dt_ids,
},
.probe = davinci_mmcsd_probe,

View File

@ -189,7 +189,6 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->slot->flags);
}
#ifdef CONFIG_PM
static int dw_mci_exynos_runtime_resume(struct device *dev)
{
struct dw_mci *host = dev_get_drvdata(dev);
@ -203,9 +202,7 @@ static int dw_mci_exynos_runtime_resume(struct device *dev)
return ret;
}
#endif /* CONFIG_PM */
#ifdef CONFIG_PM_SLEEP
/**
* dw_mci_exynos_suspend_noirq - Exynos-specific suspend code
* @dev: Device to suspend (this device)
@ -265,7 +262,6 @@ static int dw_mci_exynos_resume_noirq(struct device *dev)
return 0;
}
#endif /* CONFIG_PM_SLEEP */
static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing)
{
@ -712,11 +708,8 @@ static void dw_mci_exynos_remove(struct platform_device *pdev)
}
static const struct dev_pm_ops dw_mci_exynos_pmops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend_noirq,
dw_mci_exynos_resume_noirq)
SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
dw_mci_exynos_runtime_resume,
NULL)
NOIRQ_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend_noirq, dw_mci_exynos_resume_noirq)
RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_exynos_runtime_resume, NULL)
};
static struct platform_driver dw_mci_exynos_pltfm_driver = {
@ -726,7 +719,7 @@ static struct platform_driver dw_mci_exynos_pltfm_driver = {
.name = "dwmmc_exynos",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = dw_mci_exynos_match,
.pm = &dw_mci_exynos_pmops,
.pm = pm_ptr(&dw_mci_exynos_pmops),
},
};

View File

@ -461,11 +461,8 @@ static int dw_mci_k3_probe(struct platform_device *pdev)
}
static const struct dev_pm_ops dw_mci_k3_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
dw_mci_runtime_resume,
NULL)
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL)
};
static struct platform_driver dw_mci_k3_pltfm_driver = {
@ -475,7 +472,7 @@ static struct platform_driver dw_mci_k3_pltfm_driver = {
.name = "dwmmc_k3",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = dw_mci_k3_match,
.pm = &dw_mci_k3_dev_pm_ops,
.pm = pm_ptr(&dw_mci_k3_dev_pm_ops),
},
};

View File

@ -75,11 +75,8 @@ static void dw_mci_pci_remove(struct pci_dev *pdev)
}
static const struct dev_pm_ops dw_mci_pci_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
dw_mci_runtime_resume,
NULL)
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL)
};
static const struct pci_device_id dw_mci_pci_id[] = {
@ -94,7 +91,7 @@ static struct pci_driver dw_mci_pci_driver = {
.probe = dw_mci_pci_probe,
.remove = dw_mci_pci_remove,
.driver = {
.pm = &dw_mci_pci_dev_pm_ops,
.pm = pm_ptr(&dw_mci_pci_dev_pm_ops),
},
};

View File

@ -568,11 +568,8 @@ static void dw_mci_rockchip_remove(struct platform_device *pdev)
}
static const struct dev_pm_ops dw_mci_rockchip_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
dw_mci_runtime_resume,
NULL)
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL)
};
static struct platform_driver dw_mci_rockchip_pltfm_driver = {
@ -582,7 +579,7 @@ static struct platform_driver dw_mci_rockchip_pltfm_driver = {
.name = "dwmmc_rockchip",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = dw_mci_rockchip_match,
.pm = &dw_mci_rockchip_dev_pm_ops,
.pm = pm_ptr(&dw_mci_rockchip_dev_pm_ops),
},
};

View File

@ -541,6 +541,9 @@ extern void dw_mci_remove(struct dw_mci *host);
#ifdef CONFIG_PM
extern int dw_mci_runtime_suspend(struct device *device);
extern int dw_mci_runtime_resume(struct device *device);
#else
static inline int dw_mci_runtime_suspend(struct device *device) { return -EOPNOTSUPP; }
static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTSUPP; }
#endif
/**

View File

@ -84,10 +84,8 @@ static int meson_mx_sdhc_gate_clk_hw_register(struct device *dev,
return ret;
clk_bulk_data[bulk_index].clk = devm_clk_hw_get_clk(dev, hw, name_suffix);
if (IS_ERR(clk_bulk_data[bulk_index].clk))
return PTR_ERR(clk_bulk_data[bulk_index].clk);
return 0;
return PTR_ERR_OR_ZERO(clk_bulk_data[bulk_index].clk);
}
int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base,

View File

@ -563,10 +563,10 @@ mmc_spi_setup_data_message(struct mmc_spi_host *host, bool multiple, bool write)
* the next token (next data block, or STOP_TRAN). We can try to
* minimize I/O ops by using a single read to collect end-of-busy.
*/
if (multiple || write) {
if (write) {
t = &host->early_status;
memset(t, 0, sizeof(*t));
t->len = write ? sizeof(scratch->status) : 1;
t->len = sizeof(scratch->status);
t->tx_buf = host->ones;
t->rx_buf = scratch->status;
t->cs_change = 1;

View File

@ -2516,7 +2516,6 @@ static void mmci_remove(struct amba_device *dev)
}
}
#ifdef CONFIG_PM
static void mmci_save(struct mmci_host *host)
{
unsigned long flags;
@ -2581,12 +2580,10 @@ static int mmci_runtime_resume(struct device *dev)
return 0;
}
#endif
static const struct dev_pm_ops mmci_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(mmci_runtime_suspend, mmci_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
RUNTIME_PM_OPS(mmci_runtime_suspend, mmci_runtime_resume, NULL)
};
static const struct amba_id mmci_ids[] = {
@ -2675,7 +2672,7 @@ MODULE_DEVICE_TABLE(amba, mmci_ids);
static struct amba_driver mmci_driver = {
.drv = {
.name = DRIVER_NAME,
.pm = &mmci_dev_pm_ops,
.pm = pm_ptr(&mmci_dev_pm_ops),
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.probe = mmci_probe,

View File

@ -3278,7 +3278,7 @@ static void msdc_restore_reg(struct msdc_host *host)
__msdc_enable_sdio_irq(host, 1);
}
static int __maybe_unused msdc_runtime_suspend(struct device *dev)
static int msdc_runtime_suspend(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct msdc_host *host = mmc_priv(mmc);
@ -3300,7 +3300,7 @@ static int __maybe_unused msdc_runtime_suspend(struct device *dev)
return 0;
}
static int __maybe_unused msdc_runtime_resume(struct device *dev)
static int msdc_runtime_resume(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct msdc_host *host = mmc_priv(mmc);
@ -3323,7 +3323,7 @@ static int __maybe_unused msdc_runtime_resume(struct device *dev)
return 0;
}
static int __maybe_unused msdc_suspend(struct device *dev)
static int msdc_suspend(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct msdc_host *host = mmc_priv(mmc);
@ -3348,7 +3348,7 @@ static int __maybe_unused msdc_suspend(struct device *dev)
return pm_runtime_force_suspend(dev);
}
static int __maybe_unused msdc_resume(struct device *dev)
static int msdc_resume(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct msdc_host *host = mmc_priv(mmc);
@ -3360,8 +3360,8 @@ static int __maybe_unused msdc_resume(struct device *dev)
}
static const struct dev_pm_ops msdc_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(msdc_suspend, msdc_resume)
SET_RUNTIME_PM_OPS(msdc_runtime_suspend, msdc_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(msdc_suspend, msdc_resume)
RUNTIME_PM_OPS(msdc_runtime_suspend, msdc_runtime_resume, NULL)
};
static struct platform_driver mt_msdc_driver = {
@ -3371,7 +3371,7 @@ static struct platform_driver mt_msdc_driver = {
.name = "mtk-msdc",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = msdc_of_ids,
.pm = &msdc_dev_pm_ops,
.pm = pm_ptr(&msdc_dev_pm_ops),
},
};

View File

@ -680,7 +680,6 @@ static void mxs_mmc_remove(struct platform_device *pdev)
clk_disable_unprepare(ssp->clk);
}
#ifdef CONFIG_PM_SLEEP
static int mxs_mmc_suspend(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
@ -699,9 +698,8 @@ static int mxs_mmc_resume(struct device *dev)
return clk_prepare_enable(ssp->clk);
}
#endif
static SIMPLE_DEV_PM_OPS(mxs_mmc_pm_ops, mxs_mmc_suspend, mxs_mmc_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(mxs_mmc_pm_ops, mxs_mmc_suspend, mxs_mmc_resume);
static struct platform_driver mxs_mmc_driver = {
.probe = mxs_mmc_probe,
@ -709,7 +707,7 @@ static struct platform_driver mxs_mmc_driver = {
.driver = {
.name = DRIVER_NAME,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &mxs_mmc_pm_ops,
.pm = pm_sleep_ptr(&mxs_mmc_pm_ops),
.of_match_table = mxs_mmc_dt_ids,
},
};

View File

@ -620,8 +620,6 @@ static void omap_hsmmc_set_bus_mode(struct omap_hsmmc_host *host)
OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
}
#ifdef CONFIG_PM
/*
* Restore the MMC host context, if it was lost as result of a
* power state change.
@ -689,6 +687,7 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
return 0;
}
#ifdef CONFIG_PM
/*
* Save the MMC host context (store the number of power state changes so far).
*/
@ -1990,7 +1989,6 @@ static void omap_hsmmc_remove(struct platform_device *pdev)
clk_disable_unprepare(host->dbclk);
}
#ifdef CONFIG_PM_SLEEP
static int omap_hsmmc_suspend(struct device *dev)
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
@ -2032,9 +2030,7 @@ static int omap_hsmmc_resume(struct device *dev)
pm_runtime_put_autosuspend(host->dev);
return 0;
}
#endif
#ifdef CONFIG_PM
static int omap_hsmmc_runtime_suspend(struct device *dev)
{
struct omap_hsmmc_host *host;
@ -2102,11 +2098,10 @@ 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)
SET_RUNTIME_PM_OPS(omap_hsmmc_runtime_suspend, omap_hsmmc_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(omap_hsmmc_suspend, omap_hsmmc_resume)
RUNTIME_PM_OPS(omap_hsmmc_runtime_suspend, omap_hsmmc_runtime_resume, NULL)
};
static struct platform_driver omap_hsmmc_driver = {
@ -2115,7 +2110,7 @@ static struct platform_driver omap_hsmmc_driver = {
.driver = {
.name = DRIVER_NAME,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &omap_hsmmc_dev_pm_ops,
.pm = pm_ptr(&omap_hsmmc_dev_pm_ops),
.of_match_table = of_match_ptr(omap_mmc_of_match),
},
};

View File

@ -222,7 +222,11 @@ static void renesas_sdhi_set_clock(struct tmio_mmc_host *host,
clk &= ~0xff;
}
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & CLK_CTL_DIV_MASK);
clock = clk & CLK_CTL_DIV_MASK;
if (clock != CLK_CTL_DIV_MASK)
host->mmc->actual_clock /= (1 << (ffs(clock) + 1));
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clock);
if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2))
usleep_range(10000, 11000);

View File

@ -107,7 +107,8 @@ static const struct renesas_sdhi_of_data of_data_rza2 = {
static const struct renesas_sdhi_of_data of_data_rcar_gen3 = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 |
TMIO_MMC_64BIT_DATA_PORT,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY,
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT | MMC_CAP2_MERGE_CAPABLE,

View File

@ -48,7 +48,7 @@ struct rtsx_usb_sdmmc {
bool ddr_mode;
unsigned char power_mode;
u16 ocp_stat;
#ifdef RTSX_USB_USE_LEDS_CLASS
struct led_classdev led;
char led_name[32];
@ -789,12 +789,20 @@ static int sdmmc_get_cd(struct mmc_host *mmc)
if (err)
goto no_card;
/* get OCP status */
host->ocp_stat = (val >> 4) & 0x03;
if (val & SD_CD) {
host->card_exist = true;
return 1;
}
no_card:
/* clear OCP status */
if (host->ocp_stat & (MS_OCP_NOW | MS_OCP_EVER)) {
rtsx_usb_write_register(ucr, OCPCTL, MS_OCP_CLEAR, MS_OCP_CLEAR);
host->ocp_stat = 0;
}
host->card_exist = false;
return 0;
}
@ -818,7 +826,11 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
cmd->error = -ENOMEDIUM;
goto finish_detect_card;
}
/* check OCP stat */
if (host->ocp_stat & (MS_OCP_NOW | MS_OCP_EVER)) {
cmd->error = -ENOMEDIUM;
goto finish_detect_card;
}
mutex_lock(&ucr->dev_mutex);
mutex_lock(&host->host_mutex);
@ -952,6 +964,10 @@ static int sd_power_on(struct rtsx_usb_sdmmc *host)
struct rtsx_ucr *ucr = host->ucr;
int err;
if (host->ocp_stat & (MS_OCP_NOW | MS_OCP_EVER)) {
dev_dbg(sdmmc_dev(host), "over current\n");
return -EIO;
}
dev_dbg(sdmmc_dev(host), "%s\n", __func__);
rtsx_usb_init_cmd(ucr);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL);
@ -977,9 +993,19 @@ static int sd_power_on(struct rtsx_usb_sdmmc *host)
usleep_range(800, 1000);
rtsx_usb_init_cmd(ucr);
/* WA OCP issue: after OCP, there were problems with reopen card power */
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, POWER_MASK, POWER_ON);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, FPDCTL, SSC_POWER_MASK, SSC_POWER_DOWN);
err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
if (err)
return err;
msleep(20);
rtsx_usb_write_register(ucr, FPDCTL, SSC_POWER_MASK, SSC_POWER_ON);
usleep_range(180, 200);
rtsx_usb_init_cmd(ucr);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
POWER_MASK|LDO3318_PWR_MASK, POWER_ON|LDO_ON);
LDO3318_PWR_MASK, LDO_ON);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE,
SD_OUTPUT_EN, SD_OUTPUT_EN);
@ -1332,6 +1358,7 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host)
mmc->max_req_size = 524288;
host->power_mode = MMC_POWER_OFF;
host->ocp_stat = 0;
}
static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev)
@ -1428,7 +1455,6 @@ static void rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev)
": Realtek USB SD/MMC module has been removed\n");
}
#ifdef CONFIG_PM
static int rtsx_usb_sdmmc_runtime_suspend(struct device *dev)
{
struct rtsx_usb_sdmmc *host = dev_get_drvdata(dev);
@ -1446,11 +1472,9 @@ static int rtsx_usb_sdmmc_runtime_resume(struct device *dev)
mmc_detect_change(host->mmc, 0);
return 0;
}
#endif
static const struct dev_pm_ops rtsx_usb_sdmmc_dev_pm_ops = {
SET_RUNTIME_PM_OPS(rtsx_usb_sdmmc_runtime_suspend,
rtsx_usb_sdmmc_runtime_resume, NULL)
RUNTIME_PM_OPS(rtsx_usb_sdmmc_runtime_suspend, rtsx_usb_sdmmc_runtime_resume, NULL)
};
static const struct platform_device_id rtsx_usb_sdmmc_ids[] = {
@ -1469,7 +1493,7 @@ static struct platform_driver rtsx_usb_sdmmc_driver = {
.driver = {
.name = "rtsx_usb_sdmmc",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &rtsx_usb_sdmmc_dev_pm_ops,
.pm = pm_ptr(&rtsx_usb_sdmmc_dev_pm_ops),
},
};
module_platform_driver(rtsx_usb_sdmmc_driver);

View File

@ -973,8 +973,7 @@ static void sdhci_acpi_remove(struct platform_device *pdev)
c->slot->free_slot(pdev);
}
static void __maybe_unused sdhci_acpi_reset_signal_voltage_if_needed(
struct device *dev)
static void sdhci_acpi_reset_signal_voltage_if_needed(struct device *dev)
{
struct sdhci_acpi_host *c = dev_get_drvdata(dev);
struct sdhci_host *host = c->host;
@ -989,8 +988,6 @@ static void __maybe_unused sdhci_acpi_reset_signal_voltage_if_needed(
}
}
#ifdef CONFIG_PM_SLEEP
static int sdhci_acpi_suspend(struct device *dev)
{
struct sdhci_acpi_host *c = dev_get_drvdata(dev);
@ -1017,10 +1014,6 @@ static int sdhci_acpi_resume(struct device *dev)
return sdhci_resume_host(c->host);
}
#endif
#ifdef CONFIG_PM
static int sdhci_acpi_runtime_suspend(struct device *dev)
{
struct sdhci_acpi_host *c = dev_get_drvdata(dev);
@ -1045,12 +1038,9 @@ static int sdhci_acpi_runtime_resume(struct device *dev)
return 0;
}
#endif
static const struct dev_pm_ops sdhci_acpi_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(sdhci_acpi_suspend, sdhci_acpi_resume)
SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend,
sdhci_acpi_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(sdhci_acpi_suspend, sdhci_acpi_resume)
RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend, sdhci_acpi_runtime_resume, NULL)
};
static struct platform_driver sdhci_acpi_driver = {
@ -1058,7 +1048,7 @@ static struct platform_driver sdhci_acpi_driver = {
.name = "sdhci-acpi",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.acpi_match_table = sdhci_acpi_ids,
.pm = &sdhci_acpi_pm_ops,
.pm = pm_ptr(&sdhci_acpi_pm_ops),
},
.probe = sdhci_acpi_probe,
.remove = sdhci_acpi_remove,

View File

@ -496,7 +496,6 @@ static void sdhci_brcmstb_shutdown(struct platform_device *pdev)
MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match);
#ifdef CONFIG_PM_SLEEP
static int sdhci_brcmstb_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -540,17 +539,14 @@ static int sdhci_brcmstb_resume(struct device *dev)
return ret;
}
#endif
static const struct dev_pm_ops sdhci_brcmstb_pmops = {
SET_SYSTEM_SLEEP_PM_OPS(sdhci_brcmstb_suspend, sdhci_brcmstb_resume)
};
static DEFINE_SIMPLE_DEV_PM_OPS(sdhci_brcmstb_pmops, sdhci_brcmstb_suspend, sdhci_brcmstb_resume);
static struct platform_driver sdhci_brcmstb_driver = {
.driver = {
.name = "sdhci-brcmstb",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &sdhci_brcmstb_pmops,
.pm = pm_sleep_ptr(&sdhci_brcmstb_pmops),
.of_match_table = of_match_ptr(sdhci_brcm_of_match),
},
.probe = sdhci_brcmstb_probe,

View File

@ -36,6 +36,24 @@
#define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
#define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6
/* Read block gap */
#define SDHCI_CDNS_HRS37 0x94 /* interface mode select */
#define SDHCI_CDNS_HRS37_MODE_DS 0x0
#define SDHCI_CDNS_HRS37_MODE_HS 0x1
#define SDHCI_CDNS_HRS37_MODE_UDS_SDR12 0x8
#define SDHCI_CDNS_HRS37_MODE_UDS_SDR25 0x9
#define SDHCI_CDNS_HRS37_MODE_UDS_SDR50 0xa
#define SDHCI_CDNS_HRS37_MODE_UDS_SDR104 0xb
#define SDHCI_CDNS_HRS37_MODE_UDS_DDR50 0xc
#define SDHCI_CDNS_HRS37_MODE_MMC_LEGACY 0x20
#define SDHCI_CDNS_HRS37_MODE_MMC_SDR 0x21
#define SDHCI_CDNS_HRS37_MODE_MMC_DDR 0x22
#define SDHCI_CDNS_HRS37_MODE_MMC_HS200 0x23
#define SDHCI_CDNS_HRS37_MODE_MMC_HS400 0x24
#define SDHCI_CDNS_HRS37_MODE_MMC_HS400ES 0x25
#define SDHCI_CDNS_HRS38 0x98 /* Read block gap coefficient */
#define SDHCI_CDNS_HRS38_BLKGAP_MAX 0xf
/* SRS - Slot Register Set (SDHCI-compatible) */
#define SDHCI_CDNS_SRS_BASE 0x200
@ -251,6 +269,43 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)
return 0;
}
/**
* sdhci_cdns_tune_blkgap() - tune multi-block read gap
* @mmc: MMC host
*
* Tune delay used in multi block read. To do so,
* try sending multi-block read command with incremented gap, unless
* it succeeds.
*
* Return: error code
*/
static int sdhci_cdns_tune_blkgap(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_cdns_priv *priv = sdhci_pltfm_priv(pltfm_host);
void __iomem *hrs37_reg = priv->hrs_addr + SDHCI_CDNS_HRS37;
void __iomem *hrs38_reg = priv->hrs_addr + SDHCI_CDNS_HRS38;
int ret;
u32 gap;
/* Currently only needed in HS200 mode */
if (host->timing != MMC_TIMING_MMC_HS200)
return 0;
writel(SDHCI_CDNS_HRS37_MODE_MMC_HS200, hrs37_reg);
for (gap = 0; gap <= SDHCI_CDNS_HRS38_BLKGAP_MAX; gap++) {
writel(gap, hrs38_reg);
ret = mmc_read_tuning(mmc, 512, 32);
if (!ret)
break;
}
dev_dbg(mmc_dev(mmc), "read block gap tune %s, gap %d\n", ret ? "failed" : "OK", gap);
return ret;
}
/*
* In SD mode, software must not use the hardware tuning and instead perform
* an almost identical procedure to eMMC.
@ -261,6 +316,7 @@ static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode)
int max_streak = 0;
int end_of_streak = 0;
int i;
int ret;
/*
* Do not execute tuning for UHS_SDR50 or UHS_DDR50.
@ -288,7 +344,11 @@ static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode)
return -EIO;
}
return sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2);
ret = sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2);
if (ret)
return ret;
return sdhci_cdns_tune_blkgap(host->mmc);
}
static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
@ -551,7 +611,6 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
return sdhci_add_host(host);
}
#ifdef CONFIG_PM_SLEEP
static int sdhci_cdns_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -578,11 +637,8 @@ static int sdhci_cdns_resume(struct device *dev)
return ret;
}
#endif
static const struct dev_pm_ops sdhci_cdns_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_cdns_resume)
};
static DEFINE_SIMPLE_DEV_PM_OPS(sdhci_cdns_pm_ops, sdhci_pltfm_suspend, sdhci_cdns_resume);
static const struct of_device_id sdhci_cdns_match[] = {
{
@ -606,7 +662,7 @@ static struct platform_driver sdhci_cdns_driver = {
.driver = {
.name = "sdhci-cdns",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &sdhci_cdns_pm_ops,
.pm = pm_sleep_ptr(&sdhci_cdns_pm_ops),
.of_match_table = sdhci_cdns_match,
},
.probe = sdhci_cdns_probe,

View File

@ -1650,7 +1650,6 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
}
}
#ifdef CONFIG_PM_SLEEP
static void sdhc_esdhc_tuning_save(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -1707,7 +1706,6 @@ static void sdhc_esdhc_tuning_restore(struct sdhci_host *host)
host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
}
}
#endif
static void esdhc_cqe_enable(struct mmc_host *mmc)
{
@ -2016,7 +2014,6 @@ static void sdhci_esdhc_imx_remove(struct platform_device *pdev)
cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
}
#ifdef CONFIG_PM_SLEEP
static int sdhci_esdhc_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -2112,9 +2109,7 @@ static int sdhci_esdhc_resume(struct device *dev)
return ret;
}
#endif
#ifdef CONFIG_PM
static int sdhci_esdhc_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -2188,12 +2183,10 @@ static int sdhci_esdhc_runtime_resume(struct device *dev)
cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
return err;
}
#endif
static const struct dev_pm_ops sdhci_esdhc_pmops = {
SET_SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume)
SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
sdhci_esdhc_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume)
RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend, sdhci_esdhc_runtime_resume, NULL)
};
static struct platform_driver sdhci_esdhc_imx_driver = {
@ -2201,7 +2194,7 @@ static struct platform_driver sdhci_esdhc_imx_driver = {
.name = "sdhci-esdhc-imx",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = imx_esdhc_dt_ids,
.pm = &sdhci_esdhc_pmops,
.pm = pm_ptr(&sdhci_esdhc_pmops),
},
.probe = sdhci_esdhc_imx_probe,
.remove = sdhci_esdhc_imx_remove,

View File

@ -81,6 +81,7 @@
#define CORE_IO_PAD_PWR_SWITCH_EN BIT(15)
#define CORE_IO_PAD_PWR_SWITCH BIT(16)
#define CORE_HC_SELECT_IN_EN BIT(18)
#define CORE_HC_SELECT_IN_SDR50 (4 << 19)
#define CORE_HC_SELECT_IN_HS400 (6 << 19)
#define CORE_HC_SELECT_IN_MASK (7 << 19)
@ -1133,6 +1134,10 @@ static bool sdhci_msm_is_tuning_needed(struct sdhci_host *host)
{
struct mmc_ios *ios = &host->mmc->ios;
if (ios->timing == MMC_TIMING_UHS_SDR50 &&
host->flags & SDHCI_SDR50_NEEDS_TUNING)
return true;
/*
* Tuning is required for SDR104, HS200 and HS400 cards and
* if clock frequency is greater than 100MHz in these modes.
@ -1201,6 +1206,8 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
struct mmc_ios ios = host->mmc->ios;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
const struct sdhci_msm_offset *msm_offset = msm_host->offset;
u32 config;
if (!sdhci_msm_is_tuning_needed(host)) {
msm_host->use_cdr = false;
@ -1217,6 +1224,14 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
*/
msm_host->tuning_done = 0;
if (ios.timing == MMC_TIMING_UHS_SDR50 &&
host->flags & SDHCI_SDR50_NEEDS_TUNING) {
config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec);
config &= ~CORE_HC_SELECT_IN_MASK;
config |= CORE_HC_SELECT_IN_EN | CORE_HC_SELECT_IN_SDR50;
writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec);
}
/*
* For HS400 tuning in HS200 timing requires:
* - select MCLK/2 in VENDOR_SPEC
@ -1943,7 +1958,7 @@ static void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host)
qcom_ice_enable(msm_host->ice);
}
static __maybe_unused int sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
static int sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
{
if (msm_host->mmc->caps2 & MMC_CAP2_CRYPTO)
return qcom_ice_resume(msm_host->ice);
@ -1951,7 +1966,7 @@ static __maybe_unused int sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
return 0;
}
static __maybe_unused int sdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host)
static int sdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host)
{
if (msm_host->mmc->caps2 & MMC_CAP2_CRYPTO)
return qcom_ice_suspend(msm_host->ice);
@ -2011,13 +2026,13 @@ static inline void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host)
{
}
static inline __maybe_unused int
static inline int
sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
{
return 0;
}
static inline __maybe_unused int
static inline int
sdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host)
{
return 0;
@ -2801,7 +2816,7 @@ static void sdhci_msm_remove(struct platform_device *pdev)
clk_disable_unprepare(msm_host->bus_clk);
}
static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev)
static int sdhci_msm_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -2820,7 +2835,7 @@ static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev)
return sdhci_msm_ice_suspend(msm_host);
}
static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev)
static int sdhci_msm_runtime_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -2856,11 +2871,8 @@ static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops sdhci_msm_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend,
sdhci_msm_runtime_resume,
NULL)
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, sdhci_msm_runtime_resume, NULL)
};
static struct platform_driver sdhci_msm_driver = {
@ -2869,7 +2881,7 @@ static struct platform_driver sdhci_msm_driver = {
.driver = {
.name = "sdhci_msm",
.of_match_table = sdhci_msm_dt_match,
.pm = &sdhci_msm_pm_ops,
.pm = pm_ptr(&sdhci_msm_pm_ops),
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};

View File

@ -605,7 +605,6 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = {
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
};
#ifdef CONFIG_PM_SLEEP
/**
* sdhci_arasan_suspend - Suspend method for the driver
* @dev: Address of the device structure
@ -699,10 +698,9 @@ static int sdhci_arasan_resume(struct device *dev)
return 0;
}
#endif /* ! CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
sdhci_arasan_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
sdhci_arasan_resume);
/**
* sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate
@ -2080,7 +2078,7 @@ static struct platform_driver sdhci_arasan_driver = {
.name = "sdhci-arasan",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sdhci_arasan_of_match,
.pm = &sdhci_arasan_dev_pm_ops,
.pm = pm_sleep_ptr(&sdhci_arasan_dev_pm_ops),
},
.probe = sdhci_arasan_probe,
.remove = sdhci_arasan_remove,

View File

@ -229,7 +229,6 @@ static int sdhci_at91_set_clks_presets(struct device *dev)
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int sdhci_at91_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -243,9 +242,7 @@ static int sdhci_at91_suspend(struct device *dev)
return ret;
}
#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_PM
static int sdhci_at91_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -302,13 +299,10 @@ static int sdhci_at91_runtime_resume(struct device *dev)
sdhci_runtime_resume_host(host, 0);
return 0;
}
#endif /* CONFIG_PM */
static const struct dev_pm_ops sdhci_at91_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(sdhci_at91_suspend, pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(sdhci_at91_runtime_suspend,
sdhci_at91_runtime_resume,
NULL)
SYSTEM_SLEEP_PM_OPS(sdhci_at91_suspend, pm_runtime_force_resume)
RUNTIME_PM_OPS(sdhci_at91_runtime_suspend, sdhci_at91_runtime_resume, NULL)
};
static int sdhci_at91_probe(struct platform_device *pdev)
@ -460,7 +454,7 @@ static struct platform_driver sdhci_at91_driver = {
.name = "sdhci-at91",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sdhci_at91_dt_match,
.pm = &sdhci_at91_dev_pm_ops,
.pm = pm_ptr(&sdhci_at91_dev_pm_ops),
},
.probe = sdhci_at91_probe,
.remove = sdhci_at91_remove,

View File

@ -1499,7 +1499,6 @@ static void dwcmshc_remove(struct platform_device *pdev)
clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
}
#ifdef CONFIG_PM_SLEEP
static int dwcmshc_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -1570,9 +1569,6 @@ static int dwcmshc_resume(struct device *dev)
clk_disable_unprepare(pltfm_host->clk);
return ret;
}
#endif
#ifdef CONFIG_PM
static void dwcmshc_enable_card_clk(struct sdhci_host *host)
{
@ -1603,12 +1599,9 @@ static int dwcmshc_runtime_resume(struct device *dev)
return 0;
}
#endif
static const struct dev_pm_ops dwcmshc_pmops = {
SET_SYSTEM_SLEEP_PM_OPS(dwcmshc_suspend, dwcmshc_resume)
SET_RUNTIME_PM_OPS(dwcmshc_runtime_suspend,
dwcmshc_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(dwcmshc_suspend, dwcmshc_resume)
RUNTIME_PM_OPS(dwcmshc_runtime_suspend, dwcmshc_runtime_resume, NULL)
};
static struct platform_driver sdhci_dwcmshc_driver = {
@ -1617,7 +1610,7 @@ static struct platform_driver sdhci_dwcmshc_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sdhci_dwcmshc_dt_ids,
.acpi_match_table = ACPI_PTR(sdhci_dwcmshc_acpi_ids),
.pm = &dwcmshc_pmops,
.pm = pm_ptr(&dwcmshc_pmops),
},
.probe = dwcmshc_probe,
.remove = dwcmshc_remove,

View File

@ -1234,7 +1234,6 @@ static u32 esdhc_irq(struct sdhci_host *host, u32 intmask)
return intmask;
}
#ifdef CONFIG_PM_SLEEP
static u32 esdhc_proctl;
static int esdhc_of_suspend(struct device *dev)
{
@ -1260,11 +1259,8 @@ static int esdhc_of_resume(struct device *dev)
}
return ret;
}
#endif
static SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_ops,
esdhc_of_suspend,
esdhc_of_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_ops, esdhc_of_suspend, esdhc_of_resume);
static const struct sdhci_ops sdhci_esdhc_be_ops = {
.read_l = esdhc_be_readl,
@ -1511,7 +1507,7 @@ static struct platform_driver sdhci_esdhc_driver = {
.name = "sdhci-esdhc",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sdhci_esdhc_of_match,
.pm = &esdhc_of_dev_pm_ops,
.pm = pm_sleep_ptr(&esdhc_of_dev_pm_ops),
},
.probe = sdhci_esdhc_probe,
.remove = sdhci_pltfm_remove,

View File

@ -1400,8 +1400,7 @@ static void sdhci_omap_remove(struct platform_device *pdev)
pm_runtime_force_suspend(dev);
}
#ifdef CONFIG_PM
static void __maybe_unused sdhci_omap_context_save(struct sdhci_omap_host *omap_host)
static void 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);
@ -1412,7 +1411,7 @@ static void __maybe_unused sdhci_omap_context_save(struct sdhci_omap_host *omap_
}
/* Order matters here, HCTL must be restored in two phases */
static void __maybe_unused sdhci_omap_context_restore(struct sdhci_omap_host *omap_host)
static void sdhci_omap_context_restore(struct sdhci_omap_host *omap_host)
{
sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, omap_host->hctl);
sdhci_omap_writel(omap_host, SDHCI_OMAP_CAPA, omap_host->capa);
@ -1424,7 +1423,7 @@ static void __maybe_unused sdhci_omap_context_restore(struct sdhci_omap_host *om
sdhci_omap_writel(omap_host, SDHCI_OMAP_ISE, omap_host->ise);
}
static int __maybe_unused sdhci_omap_runtime_suspend(struct device *dev)
static int sdhci_omap_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -1443,7 +1442,7 @@ static int __maybe_unused sdhci_omap_runtime_suspend(struct device *dev)
return 0;
}
static int __maybe_unused sdhci_omap_runtime_resume(struct device *dev)
static int sdhci_omap_runtime_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -1458,13 +1457,10 @@ static int __maybe_unused sdhci_omap_runtime_resume(struct device *dev)
return 0;
}
#endif
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)
RUNTIME_PM_OPS(sdhci_omap_runtime_suspend, sdhci_omap_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver sdhci_omap_driver = {
@ -1473,7 +1469,7 @@ static struct platform_driver sdhci_omap_driver = {
.driver = {
.name = "sdhci-omap",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &sdhci_omap_dev_pm_ops,
.pm = pm_ptr(&sdhci_omap_dev_pm_ops),
.of_match_table = omap_sdhci_match,
},
};

View File

@ -679,8 +679,19 @@ static int intel_start_signal_voltage_switch(struct mmc_host *mmc,
return 0;
}
static void sdhci_intel_set_clock(struct sdhci_host *host, unsigned int clock)
{
u16 clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
/* Stop card clock separately to avoid glitches on clock line */
if (clk & SDHCI_CLOCK_CARD_EN)
sdhci_writew(host, clk & ~SDHCI_CLOCK_CARD_EN, SDHCI_CLOCK_CONTROL);
sdhci_set_clock(host, clock);
}
static const struct sdhci_ops sdhci_intel_byt_ops = {
.set_clock = sdhci_set_clock,
.set_clock = sdhci_intel_set_clock,
.set_power = sdhci_intel_set_power,
.enable_dma = sdhci_pci_enable_dma,
.set_bus_width = sdhci_set_bus_width,
@ -690,7 +701,7 @@ static const struct sdhci_ops sdhci_intel_byt_ops = {
};
static const struct sdhci_ops sdhci_intel_glk_ops = {
.set_clock = sdhci_set_clock,
.set_clock = sdhci_intel_set_clock,
.set_power = sdhci_intel_set_power,
.enable_dma = sdhci_pci_enable_dma,
.set_bus_width = sdhci_set_bus_width,

View File

@ -20,9 +20,11 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/mbus.h>
#include <linux/units.h>
#include "sdhci.h"
#include "sdhci-pltfm.h"
@ -51,6 +53,9 @@ struct sdhci_pxa {
struct clk *clk_io;
u8 power_mode;
void __iomem *sdio3_conf_reg;
struct pinctrl *pinctrl;
struct pinctrl_state *pins_default;
struct pinctrl_state *pins_uhs;
};
/*
@ -313,8 +318,20 @@ static void pxav3_set_power(struct sdhci_host *host, unsigned char mode,
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
}
static void pxav3_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pltfm_host *phost = sdhci_priv(host);
struct sdhci_pxa *pxa = sdhci_pltfm_priv(phost);
struct pinctrl_state *pins = clock < 100 * HZ_PER_MHZ ? pxa->pins_default : pxa->pins_uhs;
if (pins)
pinctrl_select_state(pxa->pinctrl, pins);
sdhci_set_clock(host, clock);
}
static const struct sdhci_ops pxav3_sdhci_ops = {
.set_clock = sdhci_set_clock,
.set_clock = pxav3_set_clock,
.set_power = pxav3_set_power,
.platform_send_init_74_clocks = pxav3_gen_init_74_clocks,
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
@ -366,6 +383,19 @@ static inline struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev)
}
#endif
static struct pinctrl_state *pxav3_lookup_pinstate(struct device *dev, struct pinctrl *pinctrl,
const char *name)
{
struct pinctrl_state *pins = pinctrl_lookup_state(pinctrl, name);
if (IS_ERR(pins)) {
dev_dbg(dev, "could not get pinstate '%s': %ld\n", name, PTR_ERR(pins));
return NULL;
}
return pins;
}
static int sdhci_pxav3_probe(struct platform_device *pdev)
{
struct sdhci_pltfm_host *pltfm_host;
@ -440,6 +470,15 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
host->mmc->pm_caps |= pdata->pm_caps;
}
pxa->pinctrl = devm_pinctrl_get(dev);
if (!IS_ERR(pxa->pinctrl)) {
pxa->pins_default = pxav3_lookup_pinstate(dev, pxa->pinctrl, "default");
if (pxa->pins_default)
pxa->pins_uhs = pxav3_lookup_pinstate(dev, pxa->pinctrl, "state_uhs");
} else {
dev_dbg(dev, "could not get pinctrl handle: %ld\n", PTR_ERR(pxa->pinctrl));
}
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS);
@ -484,7 +523,6 @@ static void sdhci_pxav3_remove(struct platform_device *pdev)
clk_disable_unprepare(pxa->clk_core);
}
#ifdef CONFIG_PM_SLEEP
static int sdhci_pxav3_suspend(struct device *dev)
{
int ret;
@ -510,9 +548,7 @@ static int sdhci_pxav3_resume(struct device *dev)
return ret;
}
#endif
#ifdef CONFIG_PM
static int sdhci_pxav3_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -544,12 +580,10 @@ static int sdhci_pxav3_runtime_resume(struct device *dev)
sdhci_runtime_resume_host(host, 0);
return 0;
}
#endif
static const struct dev_pm_ops sdhci_pxav3_pmops = {
SET_SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume)
SET_RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend,
sdhci_pxav3_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume)
RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend, sdhci_pxav3_runtime_resume, NULL)
};
static struct platform_driver sdhci_pxav3_driver = {
@ -557,7 +591,7 @@ static struct platform_driver sdhci_pxav3_driver = {
.name = "sdhci-pxav3",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = of_match_ptr(sdhci_pxav3_of_match),
.pm = &sdhci_pxav3_pmops,
.pm = pm_ptr(&sdhci_pxav3_pmops),
},
.probe = sdhci_pxav3_probe,
.remove = sdhci_pxav3_remove,

View File

@ -681,7 +681,6 @@ static void sdhci_s3c_remove(struct platform_device *pdev)
clk_disable_unprepare(sc->clk_io);
}
#ifdef CONFIG_PM_SLEEP
static int sdhci_s3c_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -698,9 +697,7 @@ static int sdhci_s3c_resume(struct device *dev)
return sdhci_resume_host(host);
}
#endif
#ifdef CONFIG_PM
static int sdhci_s3c_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -730,12 +727,10 @@ static int sdhci_s3c_runtime_resume(struct device *dev)
sdhci_runtime_resume_host(host, 0);
return 0;
}
#endif
static const struct dev_pm_ops sdhci_s3c_pmops = {
SET_SYSTEM_SLEEP_PM_OPS(sdhci_s3c_suspend, sdhci_s3c_resume)
SET_RUNTIME_PM_OPS(sdhci_s3c_runtime_suspend, sdhci_s3c_runtime_resume,
NULL)
SYSTEM_SLEEP_PM_OPS(sdhci_s3c_suspend, sdhci_s3c_resume)
RUNTIME_PM_OPS(sdhci_s3c_runtime_suspend, sdhci_s3c_runtime_resume, NULL)
};
static const struct platform_device_id sdhci_s3c_driver_ids[] = {
@ -770,7 +765,7 @@ static struct platform_driver sdhci_s3c_driver = {
.name = "s3c-sdhci",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = of_match_ptr(sdhci_s3c_dt_match),
.pm = &sdhci_s3c_pmops,
.pm = pm_ptr(&sdhci_s3c_pmops),
},
};

View File

@ -130,7 +130,6 @@ static void sdhci_remove(struct platform_device *pdev)
clk_disable_unprepare(sdhci->clk);
}
#ifdef CONFIG_PM_SLEEP
static int sdhci_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -161,9 +160,8 @@ static int sdhci_resume(struct device *dev)
return sdhci_resume_host(host);
}
#endif
static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume);
static const struct of_device_id sdhci_spear_id_table[] = {
{ .compatible = "st,spear300-sdhci" },
@ -175,7 +173,7 @@ static struct platform_driver sdhci_driver = {
.driver = {
.name = "sdhci",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &sdhci_pm_ops,
.pm = pm_sleep_ptr(&sdhci_pm_ops),
.of_match_table = sdhci_spear_id_table,
},
.probe = sdhci_probe,

View File

@ -903,7 +903,6 @@ static const struct of_device_id sdhci_sprd_of_match[] = {
};
MODULE_DEVICE_TABLE(of, sdhci_sprd_of_match);
#ifdef CONFIG_PM
static int sdhci_sprd_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -950,13 +949,10 @@ static int sdhci_sprd_runtime_resume(struct device *dev)
return ret;
}
#endif
static const struct dev_pm_ops sdhci_sprd_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(sdhci_sprd_runtime_suspend,
sdhci_sprd_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
RUNTIME_PM_OPS(sdhci_sprd_runtime_suspend, sdhci_sprd_runtime_resume, NULL)
};
static struct platform_driver sdhci_sprd_driver = {
@ -966,7 +962,7 @@ static struct platform_driver sdhci_sprd_driver = {
.name = "sdhci_sprd_r11",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sdhci_sprd_of_match,
.pm = &sdhci_sprd_pm_ops,
.pm = pm_ptr(&sdhci_sprd_pm_ops),
},
};
module_platform_driver(sdhci_sprd_driver);

View File

@ -445,7 +445,6 @@ static void sdhci_st_remove(struct platform_device *pdev)
reset_control_assert(rstc);
}
#ifdef CONFIG_PM_SLEEP
static int sdhci_st_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -492,9 +491,8 @@ static int sdhci_st_resume(struct device *dev)
return sdhci_resume_host(host);
}
#endif
static SIMPLE_DEV_PM_OPS(sdhci_st_pmops, sdhci_st_suspend, sdhci_st_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(sdhci_st_pmops, sdhci_st_suspend, sdhci_st_resume);
static const struct of_device_id st_sdhci_match[] = {
{ .compatible = "st,sdhci" },
@ -509,7 +507,7 @@ static struct platform_driver sdhci_st_driver = {
.driver = {
.name = "sdhci-st",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &sdhci_st_pmops,
.pm = pm_sleep_ptr(&sdhci_st_pmops),
.of_match_table = st_sdhci_match,
},
};

View File

@ -1831,7 +1831,7 @@ static void sdhci_tegra_remove(struct platform_device *pdev)
clk_disable_unprepare(tegra_host->tmclk);
}
static int __maybe_unused sdhci_tegra_runtime_suspend(struct device *dev)
static int sdhci_tegra_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -1841,7 +1841,7 @@ static int __maybe_unused sdhci_tegra_runtime_suspend(struct device *dev)
return 0;
}
static int __maybe_unused sdhci_tegra_runtime_resume(struct device *dev)
static int sdhci_tegra_runtime_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -1849,7 +1849,6 @@ static int __maybe_unused sdhci_tegra_runtime_resume(struct device *dev)
return clk_prepare_enable(pltfm_host->clk);
}
#ifdef CONFIG_PM_SLEEP
static int sdhci_tegra_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -1910,12 +1909,10 @@ static int sdhci_tegra_resume(struct device *dev)
pm_runtime_force_suspend(dev);
return ret;
}
#endif
static const struct dev_pm_ops sdhci_tegra_dev_pm_ops = {
SET_RUNTIME_PM_OPS(sdhci_tegra_runtime_suspend, sdhci_tegra_runtime_resume,
NULL)
SET_SYSTEM_SLEEP_PM_OPS(sdhci_tegra_suspend, sdhci_tegra_resume)
RUNTIME_PM_OPS(sdhci_tegra_runtime_suspend, sdhci_tegra_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(sdhci_tegra_suspend, sdhci_tegra_resume)
};
static struct platform_driver sdhci_tegra_driver = {
@ -1923,7 +1920,7 @@ static struct platform_driver sdhci_tegra_driver = {
.name = "sdhci-tegra",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sdhci_tegra_dt_match,
.pm = &sdhci_tegra_dev_pm_ops,
.pm = pm_ptr(&sdhci_tegra_dev_pm_ops),
},
.probe = sdhci_tegra_probe,
.remove = sdhci_tegra_remove,

View File

@ -622,7 +622,6 @@ static void xenon_remove(struct platform_device *pdev)
clk_disable_unprepare(pltfm_host->clk);
}
#ifdef CONFIG_PM_SLEEP
static int xenon_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -635,9 +634,7 @@ static int xenon_suspend(struct device *dev)
priv->restore_needed = true;
return ret;
}
#endif
#ifdef CONFIG_PM
static int xenon_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -685,14 +682,10 @@ static int xenon_runtime_resume(struct device *dev)
clk_disable_unprepare(pltfm_host->clk);
return ret;
}
#endif /* CONFIG_PM */
static const struct dev_pm_ops sdhci_xenon_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(xenon_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(xenon_runtime_suspend,
xenon_runtime_resume,
NULL)
SYSTEM_SLEEP_PM_OPS(xenon_suspend, pm_runtime_force_resume)
RUNTIME_PM_OPS(xenon_runtime_suspend, xenon_runtime_resume, NULL)
};
static const struct of_device_id sdhci_xenon_dt_ids[] = {
@ -721,7 +714,7 @@ static struct platform_driver sdhci_xenon_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sdhci_xenon_dt_ids,
.acpi_match_table = ACPI_PTR(sdhci_xenon_acpi_ids),
.pm = &sdhci_xenon_dev_pm_ops,
.pm = pm_ptr(&sdhci_xenon_dev_pm_ops),
},
.probe = xenon_probe,
.remove = xenon_remove,

View File

@ -880,6 +880,13 @@ int sdhci_suspend_host(struct sdhci_host *host);
int sdhci_resume_host(struct sdhci_host *host);
void sdhci_runtime_suspend_host(struct sdhci_host *host);
void sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset);
#else
static inline bool sdhci_enable_irq_wakeups(struct sdhci_host *host) { return false; }
static inline void sdhci_disable_irq_wakeups(struct sdhci_host *host) {}
static inline int sdhci_suspend_host(struct sdhci_host *host) { return -EOPNOTSUPP; }
static inline int sdhci_resume_host(struct sdhci_host *host) { return -EOPNOTSUPP; }
static inline void sdhci_runtime_suspend_host(struct sdhci_host *host) {}
static inline void sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset) {}
#endif
void sdhci_cqe_enable(struct mmc_host *mmc);

View File

@ -95,7 +95,6 @@ static const struct regmap_config sdhci_am654_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.fast_io = true,
};
struct timing_data {
@ -1036,7 +1035,6 @@ static void sdhci_am654_remove(struct platform_device *pdev)
pm_runtime_put_noidle(dev);
}
#ifdef CONFIG_PM
static int sdhci_am654_restore(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -1124,20 +1122,17 @@ static int sdhci_am654_runtime_resume(struct device *dev)
return 0;
}
#endif
static const struct dev_pm_ops sdhci_am654_dev_pm_ops = {
SET_RUNTIME_PM_OPS(sdhci_am654_runtime_suspend,
sdhci_am654_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
RUNTIME_PM_OPS(sdhci_am654_runtime_suspend, sdhci_am654_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver sdhci_am654_driver = {
.driver = {
.name = "sdhci-am654",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &sdhci_am654_dev_pm_ops,
.pm = pm_ptr(&sdhci_am654_dev_pm_ops),
.of_match_table = sdhci_am654_of_match,
},
.probe = sdhci_am654_probe,

View File

@ -1568,7 +1568,6 @@ static void sh_mmcif_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
#ifdef CONFIG_PM_SLEEP
static int sh_mmcif_suspend(struct device *dev)
{
struct sh_mmcif_host *host = dev_get_drvdata(dev);
@ -1580,15 +1579,7 @@ static int sh_mmcif_suspend(struct device *dev)
return 0;
}
static int sh_mmcif_resume(struct device *dev)
{
return 0;
}
#endif
static const struct dev_pm_ops sh_mmcif_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume)
};
static DEFINE_SIMPLE_DEV_PM_OPS(sh_mmcif_dev_pm_ops, sh_mmcif_suspend, NULL);
static struct platform_driver sh_mmcif_driver = {
.probe = sh_mmcif_probe,
@ -1596,7 +1587,7 @@ static struct platform_driver sh_mmcif_driver = {
.driver = {
.name = DRIVER_NAME,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &sh_mmcif_dev_pm_ops,
.pm = pm_sleep_ptr(&sh_mmcif_dev_pm_ops),
.of_match_table = sh_mmcif_of_match,
},
};

View File

@ -1495,7 +1495,6 @@ static void sunxi_mmc_remove(struct platform_device *pdev)
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
}
#ifdef CONFIG_PM
static int sunxi_mmc_runtime_resume(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
@ -1530,14 +1529,10 @@ static int sunxi_mmc_runtime_suspend(struct device *dev)
return 0;
}
#endif
static const struct dev_pm_ops sunxi_mmc_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend,
sunxi_mmc_runtime_resume,
NULL)
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend, sunxi_mmc_runtime_resume, NULL)
};
static struct platform_driver sunxi_mmc_driver = {
@ -1545,7 +1540,7 @@ static struct platform_driver sunxi_mmc_driver = {
.name = "sunxi-mmc",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sunxi_mmc_of_match,
.pm = &sunxi_mmc_pm_ops,
.pm = pm_ptr(&sunxi_mmc_pm_ops),
},
.probe = sunxi_mmc_probe,
.remove = sunxi_mmc_remove,

View File

@ -16,6 +16,7 @@
#include <linux/dmaengine.h>
#include <linux/highmem.h>
#include <linux/io.h>
#include <linux/mutex.h>
#include <linux/pagemap.h>
#include <linux/scatterlist.h>
@ -242,6 +243,20 @@ static inline void sd_ctrl_read32_rep(struct tmio_mmc_host *host, int addr,
ioread32_rep(host->ctl + (addr << host->bus_shift), buf, count);
}
#ifdef CONFIG_64BIT
static inline void sd_ctrl_read64_rep(struct tmio_mmc_host *host, int addr,
u64 *buf, int count)
{
readsq(host->ctl + (addr << host->bus_shift), buf, count);
}
static inline void sd_ctrl_write64_rep(struct tmio_mmc_host *host, int addr,
const u64 *buf, int count)
{
writesq(host->ctl + (addr << host->bus_shift), buf, count);
}
#endif
static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr,
u16 val)
{

View File

@ -349,6 +349,39 @@ static void tmio_mmc_transfer_data(struct tmio_mmc_host *host,
/*
* Transfer the data
*/
#ifdef CONFIG_64BIT
if (host->pdata->flags & TMIO_MMC_64BIT_DATA_PORT) {
u64 *buf64 = (u64 *)buf;
u64 data = 0;
if (count >= 8) {
if (is_read)
sd_ctrl_read64_rep(host, CTL_SD_DATA_PORT,
buf64, count >> 3);
else
sd_ctrl_write64_rep(host, CTL_SD_DATA_PORT,
buf64, count >> 3);
}
/* if count was multiple of 8 */
if (!(count & 0x7))
return;
buf64 += count >> 3;
count %= 8;
if (is_read) {
sd_ctrl_read64_rep(host, CTL_SD_DATA_PORT, &data, 1);
memcpy(buf64, &data, count);
} else {
memcpy(&data, buf64, count);
sd_ctrl_write64_rep(host, CTL_SD_DATA_PORT, &data, 1);
}
return;
}
#endif
if (host->pdata->flags & TMIO_MMC_32BIT_DATA_PORT) {
u32 data = 0;
u32 *buf32 = (u32 *)buf;

View File

@ -567,7 +567,6 @@ static void toshsd_powerdown(struct toshsd_host *host)
pci_write_config_byte(host->pdev, SD_PCICFG_CLKSTOP, 0);
}
#ifdef CONFIG_PM_SLEEP
static int toshsd_pm_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
@ -599,7 +598,6 @@ static int toshsd_pm_resume(struct device *dev)
return 0;
}
#endif /* CONFIG_PM_SLEEP */
static int toshsd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
@ -688,16 +686,14 @@ static void toshsd_remove(struct pci_dev *pdev)
pci_disable_device(pdev);
}
static const struct dev_pm_ops toshsd_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(toshsd_pm_suspend, toshsd_pm_resume)
};
static DEFINE_SIMPLE_DEV_PM_OPS(toshsd_pm_ops, toshsd_pm_suspend, toshsd_pm_resume);
static struct pci_driver toshsd_driver = {
.name = DRIVER_NAME,
.id_table = pci_ids,
.probe = toshsd_probe,
.remove = toshsd_remove,
.driver.pm = &toshsd_pm_ops,
.driver.pm = pm_sleep_ptr(&toshsd_pm_ops),
};
module_pci_driver(toshsd_driver);

View File

@ -1218,7 +1218,7 @@ static void via_sd_remove(struct pci_dev *pcidev)
pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device);
}
static void __maybe_unused via_init_sdc_pm(struct via_crdr_mmc_host *host)
static void via_init_sdc_pm(struct via_crdr_mmc_host *host)
{
struct sdhcreg *pm_sdhcreg;
void __iomem *addrbase;
@ -1252,7 +1252,7 @@ static void __maybe_unused via_init_sdc_pm(struct via_crdr_mmc_host *host)
via_print_sdchc(host);
}
static int __maybe_unused via_sd_suspend(struct device *dev)
static int via_sd_suspend(struct device *dev)
{
struct via_crdr_mmc_host *host;
unsigned long flags;
@ -1269,7 +1269,7 @@ static int __maybe_unused via_sd_suspend(struct device *dev)
return 0;
}
static int __maybe_unused via_sd_resume(struct device *dev)
static int via_sd_resume(struct device *dev)
{
struct via_crdr_mmc_host *sdhost;
u8 gatt;
@ -1295,14 +1295,14 @@ static int __maybe_unused via_sd_resume(struct device *dev)
return 0;
}
static SIMPLE_DEV_PM_OPS(via_sd_pm_ops, via_sd_suspend, via_sd_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(via_sd_pm_ops, via_sd_suspend, via_sd_resume);
static struct pci_driver via_sd_driver = {
.name = DRV_NAME,
.id_table = via_ids,
.probe = via_sd_probe,
.remove = via_sd_remove,
.driver.pm = &via_sd_pm_ops,
.driver.pm = pm_sleep_ptr(&via_sd_pm_ops),
};
module_pci_driver(via_sd_driver);

View File

@ -911,7 +911,6 @@ static void wmt_mci_remove(struct platform_device *pdev)
dev_info(&pdev->dev, "WMT MCI device removed\n");
}
#ifdef CONFIG_PM
static int wmt_mci_suspend(struct device *dev)
{
u32 reg_tmp;
@ -963,18 +962,7 @@ static int wmt_mci_resume(struct device *dev)
return 0;
}
static const struct dev_pm_ops wmt_mci_pm = {
.suspend = wmt_mci_suspend,
.resume = wmt_mci_resume,
};
#define wmt_mci_pm_ops (&wmt_mci_pm)
#else /* !CONFIG_PM */
#define wmt_mci_pm_ops NULL
#endif
static DEFINE_SIMPLE_DEV_PM_OPS(wmt_mci_pm_ops, wmt_mci_suspend, wmt_mci_resume);
static struct platform_driver wmt_mci_driver = {
.probe = wmt_mci_probe,
@ -982,7 +970,7 @@ static struct platform_driver wmt_mci_driver = {
.driver = {
.name = DRIVER_NAME,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = wmt_mci_pm_ops,
.pm = pm_sleep_ptr(&wmt_mci_pm_ops),
.of_match_table = wmt_mci_dt_ids,
},
};

View File

@ -337,11 +337,15 @@ struct mmc_slot {
struct regulator;
struct mmc_pwrseq;
struct notifier_block;
struct mmc_supply {
struct regulator *vmmc; /* Card power supply */
struct regulator *vqmmc; /* Optional Vccq supply */
struct regulator *vqmmc2; /* Optional supply for phy */
struct notifier_block vmmc_nb; /* Notifier for vmmc */
struct work_struct uv_work; /* Undervoltage work */
};
struct mmc_ctx {
@ -494,6 +498,13 @@ struct mmc_host {
unsigned int can_dma_map_merge:1; /* merging can be used */
unsigned int vqmmc_enabled:1; /* vqmmc regulator is enabled */
/*
* Indicates if an undervoltage event has already been handled.
* This prevents repeated regulator notifiers from triggering
* multiple REGULATOR_EVENT_UNDER_VOLTAGE events.
*/
unsigned int undervoltage:1; /* Undervoltage state */
int rescan_disable; /* disable card detection */
int rescan_entered; /* used with nonremovable devices */
@ -565,6 +576,7 @@ struct mmc_host {
int hsq_depth;
u32 err_stats[MMC_ERR_MAX];
u32 max_sd_hs_hz;
unsigned long private[] ____cacheline_aligned;
};
@ -743,5 +755,6 @@ int mmc_send_status(struct mmc_card *card, u32 *status);
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);
int mmc_read_tuning(struct mmc_host *host, unsigned int blksz, unsigned int blocks);
#endif /* LINUX_MMC_HOST_H */

View File

@ -47,6 +47,9 @@
/* Some controllers have a CBSY bit */
#define TMIO_MMC_HAVE_CBSY BIT(11)
/* Some controllers have a 64-bit wide data port register */
#define TMIO_MMC_64BIT_DATA_PORT BIT(12)
struct tmio_mmc_data {
void *chan_priv_tx;
void *chan_priv_rx;

View File

@ -99,6 +99,17 @@ extern int rtsx_usb_card_exclusive_check(struct rtsx_ucr *ucr, int card);
#define CD_MASK (SD_CD | MS_CD | XD_CD)
#define SD_WP 0x08
/* OCPCTL */
#define MS_OCP_DETECT_EN 0x08
#define MS_OCP_INT_EN 0x04
#define MS_OCP_INT_CLR 0x02
#define MS_OCP_CLEAR 0x01
/* OCPSTAT */
#define MS_OCP_DETECT 0x80
#define MS_OCP_NOW 0x02
#define MS_OCP_EVER 0x01
/* reader command field offset & parameters */
#define READ_REG_CMD 0
#define WRITE_REG_CMD 1