Commit 40aa597c authored by Linus Torvalds's avatar Linus Torvalds

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

Pull MMC updates from Ulf Hansson:
" MMC core:
   - Enable host caps to be modified via debugfs to test speed-modes
   - Improve random I/O writes for 4k buffers for hsq enabled hosts

  MMC host:
   - atmel-mci/sdhci-of-at91: Aubin Constans takes over as maintainer
   - dw_mmc-starfive: Re-work tuning support
   - meson-gx: Fix bogus IRQ when using CMD_CFG_ERROR
   - mmci: Use peripheral flow control for the STM32 variant
   - renesas,sdhi: Add support for the RZ/G3S variant
   - sdhci-esdhc-imx: Optimize the manual tuning logic
   - sdhci-msm: Add support for the SM8650 variant
   - sdhci-npcm: Add driver to support the Nuvoton NPCM BMC variant
   - sdhci-pci-gli: Add workaround to allow GL9750 to enter ASPM L1.2"

* tag 'mmc-v6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (25 commits)
  dt-bindings: mmc: sdhci-msm: document the SM8650 SDHCI Controller
  mmc: meson-gx: Remove setting of CMD_CFG_ERROR
  MAINTAINERS: mmc: take over as maintainer of MCI & SDHCI MICROCHIP DRIVERS
  mmc: jz4740: Use device_get_match_data()
  mmc: sdhci-npcm: Add NPCM SDHCI driver
  dt-bindings: mmc: npcm,sdhci: Document NPCM SDHCI controller
  mmc: sdhci-pltfm: Make driver OF independent
  mmc: sdhci-pltfm: Drop unnecessary error messages in sdhci_pltfm_init()
  mmc: sdhci-pci: Switch to use acpi_evaluate_dsm_typed()
  mmc: debugfs: Allow host caps to be modified
  mmc: core: Always reselect card type
  mmc: mmci: use peripheral flow control for STM32
  mmc: vub300: replace deprecated strncpy with strscpy
  memstick: jmb38x_ms: Annotate struct jmb38x_ms with __counted_by
  mmc: starfive: Change tuning implementation
  dt-bindings: mmc: starfive: Remove properties from required
  mmc: hsq: Improve random I/O write performance for 4k buffers
  mmc: core: Allow dynamical updates of the number of requests for hsq
  mmc: sdhci-pci-gli: A workaround to allow GL9750 to enter ASPM L1.2
  dt-bindings: mmc: renesas,sdhi: Document RZ/G3S support
  ...
parents 0364249d 5428a40a
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mmc/npcm,sdhci.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NPCM SDHCI Controller
maintainers:
- Tomer Maimon <tmaimon77@gmail.com>
allOf:
- $ref: mmc-controller.yaml#
properties:
compatible:
enum:
- nuvoton,npcm750-sdhci
- nuvoton,npcm845-sdhci
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
required:
- compatible
- reg
- interrupts
- clocks
unevaluatedProperties: false
examples:
- |
mmc@f0840000 {
compatible = "nuvoton,npcm750-sdhci";
reg = <0xf0840000 0x200>;
interrupts = <0 27 4>;
clocks = <&clk 4>;
};
......@@ -59,6 +59,7 @@ properties:
- renesas,sdhi-r9a07g043 # RZ/G2UL
- renesas,sdhi-r9a07g044 # RZ/G2{L,LC}
- renesas,sdhi-r9a07g054 # RZ/V2L
- renesas,sdhi-r9a08g045 # RZ/G3S
- renesas,sdhi-r9a09g011 # RZ/V2M
- const: renesas,rcar-gen3-sdhi # R-Car Gen3 or RZ/G2
- items:
......@@ -122,6 +123,7 @@ allOf:
- renesas,sdhi-r9a07g043
- renesas,sdhi-r9a07g044
- renesas,sdhi-r9a07g054
- renesas,sdhi-r9a08g045
- renesas,sdhi-r9a09g011
then:
properties:
......
......@@ -58,6 +58,7 @@ properties:
- qcom,sm8350-sdhci
- qcom,sm8450-sdhci
- qcom,sm8550-sdhci
- qcom,sm8650-sdhci
- const: qcom,sdhci-msm-v5 # for sdcc version 5.0
reg:
......@@ -85,10 +86,10 @@ properties:
- const: iface
- const: core
- const: xo
- const: ice
- const: bus
- const: cal
- const: sleep
- enum: [ice, bus, cal, sleep]
- enum: [ice, bus, cal, sleep]
- enum: [ice, bus, cal, sleep]
- enum: [ice, bus, cal, sleep]
dma-coherent: true
......
......@@ -55,7 +55,6 @@ required:
- clocks
- clock-names
- interrupts
- starfive,sysreg
unevaluatedProperties: false
......@@ -73,5 +72,4 @@ examples:
fifo-depth = <32>;
fifo-watermark-aligned;
data-addr = <0>;
starfive,sysreg = <&sys_syscon 0x14 0x1a 0x7c000000>;
};
......@@ -14110,7 +14110,7 @@ F: Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml
F: drivers/iio/adc/mcp3911.c
MICROCHIP MMC/SD/SDIO MCI DRIVER
M: Ludovic Desroches <ludovic.desroches@microchip.com>
M: Aubin Constans <aubin.constans@microchip.com>
S: Maintained
F: drivers/mmc/host/atmel-mci.c
......@@ -19335,7 +19335,8 @@ F: Documentation/devicetree/bindings/mmc/sdhci-common.yaml
F: drivers/mmc/host/sdhci*
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) MICROCHIP DRIVER
M: Eugen Hristev <eugen.hristev@microchip.com>
M: Aubin Constans <aubin.constans@microchip.com>
R: Eugen Hristev <eugen.hristev@collabora.com>
L: linux-mmc@vger.kernel.org
S: Supported
F: drivers/mmc/host/sdhci-of-at91.c
......
......@@ -66,7 +66,7 @@ struct jmb38x_ms_host {
struct jmb38x_ms {
struct pci_dev *pdev;
int host_cnt;
struct memstick_host *hosts[];
struct memstick_host *hosts[] __counted_by(host_cnt);
};
#define BLOCK_COUNT_MASK 0xffff0000
......
......@@ -12,9 +12,12 @@
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/fault-inject.h>
#include <linux/time.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include "core.h"
#include "card.h"
......@@ -298,6 +301,49 @@ static const struct file_operations mmc_err_stats_fops = {
.release = single_release,
};
static int mmc_caps_get(void *data, u64 *val)
{
*val = *(u32 *)data;
return 0;
}
static int mmc_caps_set(void *data, u64 val)
{
u32 *caps = data;
u32 diff = *caps ^ val;
u32 allowed = MMC_CAP_AGGRESSIVE_PM |
MMC_CAP_SD_HIGHSPEED |
MMC_CAP_MMC_HIGHSPEED |
MMC_CAP_UHS |
MMC_CAP_DDR;
if (diff & ~allowed)
return -EINVAL;
*caps = val;
return 0;
}
static int mmc_caps2_set(void *data, u64 val)
{
u32 allowed = MMC_CAP2_HSX00_1_8V | MMC_CAP2_HSX00_1_2V;
u32 *caps = data;
u32 diff = *caps ^ val;
if (diff & ~allowed)
return -EINVAL;
*caps = val;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(mmc_caps_fops, mmc_caps_get, mmc_caps_set,
"0x%08llx\n");
DEFINE_DEBUGFS_ATTRIBUTE(mmc_caps2_fops, mmc_caps_get, mmc_caps2_set,
"0x%08llx\n");
void mmc_add_host_debugfs(struct mmc_host *host)
{
struct dentry *root;
......@@ -306,8 +352,9 @@ void mmc_add_host_debugfs(struct mmc_host *host)
host->debugfs_root = root;
debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops);
debugfs_create_x32("caps", S_IRUSR, root, &host->caps);
debugfs_create_x32("caps2", S_IRUSR, root, &host->caps2);
debugfs_create_file("caps", 0600, root, &host->caps, &mmc_caps_fops);
debugfs_create_file("caps2", 0600, root, &host->caps2,
&mmc_caps2_fops);
debugfs_create_file_unsafe("clock", S_IRUSR | S_IWUSR, root, host,
&mmc_clock_fops);
......
......@@ -419,7 +419,6 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
card->ext_csd.strobe_support = ext_csd[EXT_CSD_STROBE_SUPPORT];
card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
mmc_select_card_type(card);
card->ext_csd.raw_s_a_timeout = ext_csd[EXT_CSD_S_A_TIMEOUT];
card->ext_csd.raw_erase_timeout_mult =
......@@ -1732,6 +1731,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
mmc_set_erase_size(card);
}
/*
* Reselect the card type since host caps could have been changed when
* debugging even if the card is not new.
*/
mmc_select_card_type(card);
/* Enable ERASE_GRP_DEF. This bit is lost after a reset or power off. */
if (card->ext_csd.rev >= 3) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
......
......@@ -260,11 +260,7 @@ static blk_status_t mmc_mq_queue_rq(struct blk_mq_hw_ctx *hctx,
}
break;
case MMC_ISSUE_ASYNC:
/*
* For MMC host software queue, we only allow 2 requests in
* flight to avoid a long latency.
*/
if (host->hsq_enabled && mq->in_flight[issue_type] > 2) {
if (host->hsq_enabled && mq->in_flight[issue_type] > host->hsq_depth) {
spin_unlock_irq(&mq->lock);
return BLK_STS_RESOURCE;
}
......
......@@ -429,6 +429,14 @@ config MMC_SDHCI_IPROC
If unsure, say N.
config MMC_SDHCI_NPCM
tristate "Secure Digital Host Controller Interface support for NPCM"
depends on ARCH_NPCM || COMPILE_TEST
depends on MMC_SDHCI_PLTFM
help
This provides support for the SD/eMMC controller found in
NPCM BMC family SoCs.
config MMC_MESON_GX
tristate "Amlogic S905/GX*/AXG SD/MMC Host Controller support"
depends on ARCH_MESON|| COMPILE_TEST
......@@ -677,9 +685,9 @@ config MMC_SDHI_SYS_DMAC
config MMC_SDHI_INTERNAL_DMAC
tristate "DMA for SDHI SD/SDIO controllers using on-chip bus mastering"
depends on ARM64 || ARCH_R7S9210 || ARCH_R8A77470 || COMPILE_TEST
depends on ARCH_RENESAS || COMPILE_TEST
depends on MMC_SDHI
default MMC_SDHI if (ARM64 || ARCH_R7S9210 || ARCH_R8A77470)
default MMC_SDHI if ARCH_RENESAS
help
This provides DMA support for SDHI SD/SDIO controllers
using on-chip bus mastering. This supports the controllers
......
......@@ -89,6 +89,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_DWCMSHC) += sdhci-of-dwcmshc.o
obj-$(CONFIG_MMC_SDHCI_OF_SPARX5) += sdhci-of-sparx5.o
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
obj-$(CONFIG_MMC_SDHCI_NPCM) += sdhci-npcm.o
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o
obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o
......
......@@ -227,6 +227,7 @@ struct mci_slot_pdata {
/**
* struct mci_platform_data - board-specific MMC/SDcard configuration
* @dma_slave: DMA slave interface to use in data transfers.
* @dma_filter: Filtering function to filter the DMA channel
* @slot: Per-slot configuration data.
*/
struct mci_platform_data {
......@@ -674,8 +675,10 @@ atmci_of_init(struct platform_device *pdev)
"cd", GPIOD_IN, "cd-gpios");
err = PTR_ERR_OR_ZERO(pdata->slot[slot_id].detect_pin);
if (err) {
if (err != -ENOENT)
if (err != -ENOENT) {
of_node_put(cnp);
return ERR_PTR(err);
}
pdata->slot[slot_id].detect_pin = NULL;
}
......@@ -687,8 +690,10 @@ atmci_of_init(struct platform_device *pdev)
"wp", GPIOD_IN, "wp-gpios");
err = PTR_ERR_OR_ZERO(pdata->slot[slot_id].wp_pin);
if (err) {
if (err != -ENOENT)
if (err != -ENOENT) {
of_node_put(cnp);
return ERR_PTR(err);
}
pdata->slot[slot_id].wp_pin = NULL;
}
}
......
......@@ -5,6 +5,7 @@
* Copyright (c) 2022 StarFive Technology Co., Ltd.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/mfd/syscon.h>
......@@ -20,13 +21,7 @@
#define ALL_INT_CLR 0x1ffff
#define MAX_DELAY_CHAIN 32
struct starfive_priv {
struct device *dev;
struct regmap *reg_syscon;
u32 syscon_offset;
u32 syscon_shift;
u32 syscon_mask;
};
#define STARFIVE_SMPL_PHASE GENMASK(20, 16)
static void dw_mci_starfive_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{
......@@ -44,117 +39,65 @@ static void dw_mci_starfive_set_ios(struct dw_mci *host, struct mmc_ios *ios)
}
}
static void dw_mci_starfive_set_sample_phase(struct dw_mci *host, u32 smpl_phase)
{
/* change driver phase and sample phase */
u32 reg_value = mci_readl(host, UHS_REG_EXT);
/* In UHS_REG_EXT, only 5 bits valid in DRV_PHASE and SMPL_PHASE */
reg_value &= ~STARFIVE_SMPL_PHASE;
reg_value |= FIELD_PREP(STARFIVE_SMPL_PHASE, smpl_phase);
mci_writel(host, UHS_REG_EXT, reg_value);
/* We should delay 1ms wait for timing setting finished. */
mdelay(1);
}
static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot,
u32 opcode)
{
static const int grade = MAX_DELAY_CHAIN;
struct dw_mci *host = slot->host;
struct starfive_priv *priv = host->priv;
int rise_point = -1, fall_point = -1;
int err, prev_err = 0;
int i;
bool found = 0;
u32 regval;
/*
* Use grade as the max delay chain, and use the rise_point and
* fall_point to ensure the best sampling point of a data input
* signals.
*/
for (i = 0; i < grade; i++) {
regval = i << priv->syscon_shift;
err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset,
priv->syscon_mask, regval);
if (err)
return err;
int smpl_phase, smpl_raise = -1, smpl_fall = -1;
int ret;
for (smpl_phase = 0; smpl_phase < grade; smpl_phase++) {
dw_mci_starfive_set_sample_phase(host, smpl_phase);
mci_writel(host, RINTSTS, ALL_INT_CLR);
err = mmc_send_tuning(slot->mmc, opcode, NULL);
if (!err)
found = 1;
ret = mmc_send_tuning(slot->mmc, opcode, NULL);
if (i > 0) {
if (err && !prev_err)
fall_point = i - 1;
if (!err && prev_err)
rise_point = i;
if (!ret && smpl_raise < 0) {
smpl_raise = smpl_phase;
} else if (ret && smpl_raise >= 0) {
smpl_fall = smpl_phase - 1;
break;
}
if (rise_point != -1 && fall_point != -1)
goto tuning_out;
prev_err = err;
err = 0;
}
tuning_out:
if (found) {
if (rise_point == -1)
rise_point = 0;
if (fall_point == -1)
fall_point = grade - 1;
if (fall_point < rise_point) {
if ((rise_point + fall_point) >
(grade - 1))
i = fall_point / 2;
else
i = (rise_point + grade - 1) / 2;
} else {
i = (rise_point + fall_point) / 2;
}
regval = i << priv->syscon_shift;
err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset,
priv->syscon_mask, regval);
if (err)
return err;
mci_writel(host, RINTSTS, ALL_INT_CLR);
if (smpl_phase >= grade)
smpl_fall = grade - 1;
dev_info(host->dev, "Found valid delay chain! use it [delay=%d]\n", i);
} else {
if (smpl_raise < 0) {
smpl_phase = 0;
dev_err(host->dev, "No valid delay chain! use default\n");
err = -EINVAL;
ret = -EINVAL;
goto out;
}
mci_writel(host, RINTSTS, ALL_INT_CLR);
return err;
}
static int dw_mci_starfive_parse_dt(struct dw_mci *host)
{
struct of_phandle_args args;
struct starfive_priv *priv;
int ret;
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
smpl_phase = (smpl_raise + smpl_fall) / 2;
dev_dbg(host->dev, "Found valid delay chain! use it [delay=%d]\n", smpl_phase);
ret = 0;
ret = of_parse_phandle_with_fixed_args(host->dev->of_node,
"starfive,sysreg", 3, 0, &args);
if (ret) {
dev_err(host->dev, "Failed to parse starfive,sysreg\n");
return -EINVAL;
}
priv->reg_syscon = syscon_node_to_regmap(args.np);
of_node_put(args.np);
if (IS_ERR(priv->reg_syscon))
return PTR_ERR(priv->reg_syscon);
priv->syscon_offset = args.args[0];
priv->syscon_shift = args.args[1];
priv->syscon_mask = args.args[2];
host->priv = priv;
return 0;
out:
dw_mci_starfive_set_sample_phase(host, smpl_phase);
mci_writel(host, RINTSTS, ALL_INT_CLR);
return ret;
}
static const struct dw_mci_drv_data starfive_data = {
.common_caps = MMC_CAP_CMD23,
.set_ios = dw_mci_starfive_set_ios,
.parse_dt = dw_mci_starfive_parse_dt,
.execute_tuning = dw_mci_starfive_execute_tuning,
};
......
......@@ -18,9 +18,10 @@
#include <linux/mmc/host.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/scatterlist.h>
......@@ -1040,7 +1041,6 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
int ret;
struct mmc_host *mmc;
struct jz4740_mmc_host *host;
const struct of_device_id *match;
mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev);
if (!mmc) {
......@@ -1050,13 +1050,8 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
host = mmc_priv(mmc);
match = of_match_device(jz4740_mmc_of_match, &pdev->dev);
if (match) {
host->version = (enum jz4740_mmc_version)match->data;
} else {
/* JZ4740 should be the only one using legacy probe */
host->version = JZ_MMC_JZ4740;
}
/* Default if no match is JZ4740 */
host->version = (enum jz4740_mmc_version)device_get_match_data(&pdev->dev);
ret = mmc_of_parse(mmc);
if (ret) {
......@@ -1200,7 +1195,7 @@ static struct platform_driver jz4740_mmc_driver = {
.driver = {
.name = "jz4740-mmc",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = of_match_ptr(jz4740_mmc_of_match),
.of_match_table = jz4740_mmc_of_match,
.pm = pm_sleep_ptr(&jz4740_mmc_pm_ops),
},
};
......
......@@ -801,7 +801,6 @@ static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd)
cmd_cfg |= FIELD_PREP(CMD_CFG_CMD_INDEX_MASK, cmd->opcode);
cmd_cfg |= CMD_CFG_OWNER; /* owned by CPU */
cmd_cfg |= CMD_CFG_ERROR; /* stop in case of error */
meson_mmc_set_response_bits(cmd, &cmd_cfg);
......
......@@ -21,6 +21,25 @@ static void mmc_hsq_retry_handler(struct work_struct *work)
mmc->ops->request(mmc, hsq->mrq);
}
static void mmc_hsq_modify_threshold(struct mmc_hsq *hsq)
{
struct mmc_host *mmc = hsq->mmc;
struct mmc_request *mrq;
unsigned int tag, need_change = 0;
mmc->hsq_depth = HSQ_NORMAL_DEPTH;
for (tag = 0; tag < HSQ_NUM_SLOTS; tag++) {
mrq = hsq->slot[tag].mrq;
if (mrq && mrq->data &&
(mrq->data->blksz * mrq->data->blocks == 4096) &&
(mrq->data->flags & MMC_DATA_WRITE) &&
(++need_change == 2)) {
mmc->hsq_depth = HSQ_PERFORMANCE_DEPTH;
break;
}
}
}
static void mmc_hsq_pump_requests(struct mmc_hsq *hsq)
{
struct mmc_host *mmc = hsq->mmc;
......@@ -42,6 +61,8 @@ static void mmc_hsq_pump_requests(struct mmc_hsq *hsq)
return;
}
mmc_hsq_modify_threshold(hsq);
slot = &hsq->slot[hsq->next_tag];
hsq->mrq = slot->mrq;
hsq->qcnt--;
......@@ -337,6 +358,7 @@ int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc)
hsq->mmc = mmc;
hsq->mmc->cqe_private = hsq;
mmc->cqe_ops = &mmc_hsq_ops;
mmc->hsq_depth = HSQ_NORMAL_DEPTH;
for (i = 0; i < HSQ_NUM_SLOTS; i++)
hsq->tag_slot[i] = HSQ_INVALID_TAG;
......
......@@ -5,6 +5,17 @@
#define HSQ_NUM_SLOTS 64
#define HSQ_INVALID_TAG HSQ_NUM_SLOTS
/*
* For MMC host software queue, we only allow 2 requests in
* flight to avoid a long latency.
*/
#define HSQ_NORMAL_DEPTH 2
/*
* For 4k random writes, we allow hsq_depth to increase to 5
* for better performance.
*/
#define HSQ_PERFORMANCE_DEPTH 5
struct hsq_slot {
struct mmc_request *mrq;
};
......
......@@ -249,6 +249,7 @@ static struct variant_data variant_stm32 = {
.f_max = 48000000,
.pwrreg_clkgate = true,
.pwrreg_nopower = true,
.dma_flow_controller = true,
.init = mmci_variant_init,
};
......@@ -1015,7 +1016,7 @@ static int _mmci_dmae_prep_data(struct mmci_host *host, struct mmc_data *data,
.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
.src_maxburst = variant->fifohalfsize >> 2, /* # of words */
.dst_maxburst = variant->fifohalfsize >> 2, /* # of words */
.device_fc = false,
.device_fc = variant->dma_flow_controller,
};
struct dma_chan *chan;
struct dma_device *device;
......
......@@ -332,6 +332,7 @@ enum mmci_busy_state {
* @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register
* @dma_lli: true if variant has dma link list feature.
* @stm32_idmabsize_mask: stm32 sdmmc idma buffer size.
* @dma_flow_controller: use peripheral as flow controller for DMA.
*/
struct variant_data {
unsigned int clkreg;
......@@ -378,6 +379,7 @@ struct variant_data {
u8 dma_lli:1;
u32 stm32_idmabsize_mask;
u32 stm32_idmabsize_align;
bool dma_flow_controller;
void (*init)(struct mmci_host *host);
};
......
......@@ -1154,32 +1154,52 @@ static void esdhc_post_tuning(struct sdhci_host *host)
writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
}
/*
* find the largest pass window, and use the average delay of this
* largest window to get the best timing.
*/
static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
{
int min, max, avg, ret;
int win_length, target_min, target_max, target_win_length;
/* find the mininum delay first which can pass tuning */
min = ESDHC_TUNE_CTRL_MIN;
while (min < ESDHC_TUNE_CTRL_MAX) {
esdhc_prepare_tuning(host, min);
if (!mmc_send_tuning(host->mmc, opcode, NULL))
break;
min += ESDHC_TUNE_CTRL_STEP;
}
/* find the maxinum delay which can not pass tuning */
max = min + ESDHC_TUNE_CTRL_STEP;
max = ESDHC_TUNE_CTRL_MIN;
target_win_length = 0;
while (max < ESDHC_TUNE_CTRL_MAX) {
esdhc_prepare_tuning(host, max);
if (mmc_send_tuning(host->mmc, opcode, NULL)) {
max -= ESDHC_TUNE_CTRL_STEP;
break;
/* find the mininum delay first which can pass tuning */
while (min < ESDHC_TUNE_CTRL_MAX) {
esdhc_prepare_tuning(host, min);
if (!mmc_send_tuning(host->mmc, opcode, NULL))
break;
min += ESDHC_TUNE_CTRL_STEP;
}
max += ESDHC_TUNE_CTRL_STEP;
/* find the maxinum delay which can not pass tuning */
max = min + ESDHC_TUNE_CTRL_STEP;
while (max < ESDHC_TUNE_CTRL_MAX) {
esdhc_prepare_tuning(host, max);
if (mmc_send_tuning(host->mmc, opcode, NULL)) {
max -= ESDHC_TUNE_CTRL_STEP;
break;
}
max += ESDHC_TUNE_CTRL_STEP;
}
win_length = max - min + 1;
/* get the largest pass window */
if (win_length > target_win_length) {
target_win_length = win_length;
target_min = min;
target_max = max;
}
/* continue to find the next pass window */
min = max + ESDHC_TUNE_CTRL_STEP;
}
/* use average delay to get the best timing */
avg = (min + max) / 2;
avg = (target_min + target_max) / 2;
esdhc_prepare_tuning(host, avg);
ret = mmc_send_tuning(host->mmc, opcode, NULL);
esdhc_post_tuning(host);
......
// SPDX-License-Identifier: GPL-2.0+
/*
* NPCM SDHC MMC host controller driver.
*
* Copyright (c) 2023 Nuvoton Technology corporation.
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
#include "sdhci-pltfm.h"
static const struct sdhci_pltfm_data npcm7xx_sdhci_pdata = {
.quirks = SDHCI_QUIRK_DELAY_AFTER_POWER,
.quirks2 = SDHCI_QUIRK2_STOP_WITH_TC |
SDHCI_QUIRK2_NO_1_8_V,
};
static const struct sdhci_pltfm_data npcm8xx_sdhci_pdata = {
.quirks = SDHCI_QUIRK_DELAY_AFTER_POWER,
.quirks2 = SDHCI_QUIRK2_STOP_WITH_TC,
};
static int npcm_sdhci_probe(struct platform_device *pdev)
{
const struct sdhci_pltfm_data *data;
struct sdhci_pltfm_host *pltfm_host;
struct device *dev = &pdev->dev;
struct sdhci_host *host;
u32 caps;
int ret;
data = of_device_get_match_data(dev);
if (!data)
return -EINVAL;
host = sdhci_pltfm_init(pdev, data, 0);
if (IS_ERR(host))
return PTR_ERR(host);
pltfm_host = sdhci_priv(host);
pltfm_host->clk = devm_clk_get_optional_enabled(dev, NULL);
if (IS_ERR(pltfm_host->clk)) {
ret = PTR_ERR(pltfm_host->clk);
goto err_sdhci;
}
caps = sdhci_readl(host, SDHCI_CAPABILITIES);
if (caps & SDHCI_CAN_DO_8BIT)
host->mmc->caps |= MMC_CAP_8_BIT_DATA;
ret = mmc_of_parse(host->mmc);
if (ret)
goto err_sdhci;
ret = sdhci_add_host(host);
if (ret)
goto err_sdhci;
return 0;
err_sdhci:
sdhci_pltfm_free(pdev);
return ret;
}
static const struct of_device_id npcm_sdhci_of_match[] = {
{ .compatible = "nuvoton,npcm750-sdhci", .data = &npcm7xx_sdhci_pdata },
{ .compatible = "nuvoton,npcm845-sdhci", .data = &npcm8xx_sdhci_pdata },
{ }
};
MODULE_DEVICE_TABLE(of, npcm_sdhci_of_match);
static struct platform_driver npcm_sdhci_driver = {
.driver = {
.name = "npcm-sdhci",
.of_match_table = npcm_sdhci_of_match,
.pm = &sdhci_pltfm_pmops,
},
.probe = npcm_sdhci_probe,
.remove_new = sdhci_pltfm_remove,
};
module_platform_driver(npcm_sdhci_driver);
MODULE_DESCRIPTION("NPCM Secure Digital Host Controller Interface driver");
MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
MODULE_LICENSE("GPL");
......@@ -483,11 +483,12 @@ static int __intel_dsm(struct intel_host *intel_host, struct device *dev,
int err = 0;
size_t len;
obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), &intel_dsm_guid, 0, fn, NULL);
obj = acpi_evaluate_dsm_typed(ACPI_HANDLE(dev), &intel_dsm_guid, 0, fn, NULL,
ACPI_TYPE_BUFFER);
if (!obj)
return -EOPNOTSUPP;
if (obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 1) {
if (obj->buffer.length < 1) {
err = -EINVAL;
goto out;
}
......
......@@ -25,6 +25,9 @@
#define GLI_9750_WT_EN_ON 0x1
#define GLI_9750_WT_EN_OFF 0x0
#define PCI_GLI_9750_PM_CTRL 0xFC
#define PCI_GLI_9750_PM_STATE GENMASK(1, 0)
#define SDHCI_GLI_9750_CFG2 0x848
#define SDHCI_GLI_9750_CFG2_L1DLY GENMASK(28, 24)
#define GLI_9750_CFG2_L1DLY_VALUE 0x1F
......@@ -536,8 +539,12 @@ static void sdhci_gl9750_set_clock(struct sdhci_host *host, unsigned int clock)
static void gl9750_hw_setting(struct sdhci_host *host)
{
struct sdhci_pci_slot *slot = sdhci_priv(host);
struct pci_dev *pdev;
u32 value;
pdev = slot->chip->pdev;
gl9750_wt_on(host);
value = sdhci_readl(host, SDHCI_GLI_9750_CFG2);
......@@ -547,6 +554,13 @@ static void gl9750_hw_setting(struct sdhci_host *host)
GLI_9750_CFG2_L1DLY_VALUE);
sdhci_writel(host, value, SDHCI_GLI_9750_CFG2);
/* toggle PM state to allow GL9750 to enter ASPM L1.2 */
pci_read_config_dword(pdev, PCI_GLI_9750_PM_CTRL, &value);
value |= PCI_GLI_9750_PM_STATE;
pci_write_config_dword(pdev, PCI_GLI_9750_PM_CTRL, value);
value &= ~PCI_GLI_9750_PM_STATE;
pci_write_config_dword(pdev, PCI_GLI_9750_PM_CTRL, value);
gl9750_wt_off(host);
}
......
......@@ -19,7 +19,6 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/of.h>
#ifdef CONFIG_PPC
#include <asm/machdep.h>
#endif
......@@ -56,19 +55,16 @@ static bool sdhci_wp_inverted(struct device *dev)
static void sdhci_get_compatibility(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct sdhci_host *host = platform_get_drvdata(pdev);
struct device_node *np = pdev->dev.of_node;
if (!np)
return;
if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc"))
if (device_is_compatible(dev, "fsl,p2020-rev1-esdhc"))
host->quirks |= SDHCI_QUIRK_BROKEN_DMA;
if (of_device_is_compatible(np, "fsl,p2020-esdhc") ||
of_device_is_compatible(np, "fsl,p1010-esdhc") ||
of_device_is_compatible(np, "fsl,t4240-esdhc") ||
of_device_is_compatible(np, "fsl,mpc8536-esdhc"))
if (device_is_compatible(dev, "fsl,p2020-esdhc") ||
device_is_compatible(dev, "fsl,p1010-esdhc") ||
device_is_compatible(dev, "fsl,t4240-esdhc") ||
device_is_compatible(dev, "fsl,mpc8536-esdhc"))
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
}
......@@ -115,26 +111,21 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
{
struct sdhci_host *host;
void __iomem *ioaddr;
int irq, ret;
int irq;
ioaddr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ioaddr)) {
ret = PTR_ERR(ioaddr);
goto err;
}
if (IS_ERR(ioaddr))
return ERR_CAST(ioaddr);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
goto err;
}
if (irq < 0)
return ERR_PTR(irq);
host = sdhci_alloc_host(&pdev->dev,
sizeof(struct sdhci_pltfm_host) + priv_size);
if (IS_ERR(host)) {
ret = PTR_ERR(host);
goto err;
dev_err(&pdev->dev, "%s failed %pe\n", __func__, host);
return ERR_CAST(host);
}
host->ioaddr = ioaddr;
......@@ -152,9 +143,6 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
platform_set_drvdata(pdev, host);
return host;
err:
dev_err(&pdev->dev, "%s failed %d\n", __func__, ret);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(sdhci_pltfm_init);
......
......@@ -512,7 +512,7 @@ static void new_system_port_status(struct vub300_mmc_host *vub300)
vub300->card_present = 1;
vub300->bus_width = 0;
if (disable_offload_processing)
strncpy(vub300->vub_name, "EMPTY Processing Disabled",
strscpy(vub300->vub_name, "EMPTY Processing Disabled",
sizeof(vub300->vub_name));
else
vub300->vub_name[0] = 0;
......@@ -1216,7 +1216,7 @@ static void __download_offload_pseudocode(struct vub300_mmc_host *vub300,
dev_err(&vub300->udev->dev,
"corrupt offload pseudocode in firmware %s\n",
vub300->vub_name);
strncpy(vub300->vub_name, "corrupt offload pseudocode",
strscpy(vub300->vub_name, "corrupt offload pseudocode",
sizeof(vub300->vub_name));
return;
}
......@@ -1250,7 +1250,7 @@ static void __download_offload_pseudocode(struct vub300_mmc_host *vub300,
"not enough memory for xfer buffer to send"
" INTERRUPT_PSEUDOCODE for %s %s\n", fw->data,
vub300->vub_name);
strncpy(vub300->vub_name,
strscpy(vub300->vub_name,
"SDIO interrupt pseudocode download failed",
sizeof(vub300->vub_name));
return;
......@@ -1259,7 +1259,7 @@ static void __download_offload_pseudocode(struct vub300_mmc_host *vub300,
dev_err(&vub300->udev->dev,
"corrupt interrupt pseudocode in firmware %s %s\n",
fw->data, vub300->vub_name);
strncpy(vub300->vub_name, "corrupt interrupt pseudocode",
strscpy(vub300->vub_name, "corrupt interrupt pseudocode",
sizeof(vub300->vub_name));
return;
}
......@@ -1293,7 +1293,7 @@ static void __download_offload_pseudocode(struct vub300_mmc_host *vub300,
"not enough memory for xfer buffer to send"
" TRANSFER_PSEUDOCODE for %s %s\n", fw->data,
vub300->vub_name);
strncpy(vub300->vub_name,
strscpy(vub300->vub_name,
"SDIO transfer pseudocode download failed",
sizeof(vub300->vub_name));
return;
......@@ -1302,7 +1302,7 @@ static void __download_offload_pseudocode(struct vub300_mmc_host *vub300,
dev_err(&vub300->udev->dev,
"corrupt transfer pseudocode in firmware %s %s\n",
fw->data, vub300->vub_name);
strncpy(vub300->vub_name, "corrupt transfer pseudocode",
strscpy(vub300->vub_name, "corrupt transfer pseudocode",
sizeof(vub300->vub_name));
return;
}
......@@ -1336,13 +1336,13 @@ static void __download_offload_pseudocode(struct vub300_mmc_host *vub300,
dev_err(&vub300->udev->dev,
"corrupt dynamic registers in firmware %s\n",
vub300->vub_name);
strncpy(vub300->vub_name, "corrupt dynamic registers",
strscpy(vub300->vub_name, "corrupt dynamic registers",
sizeof(vub300->vub_name));
return;
}
copy_error_message:
strncpy(vub300->vub_name, "SDIO pseudocode download failed",
strscpy(vub300->vub_name, "SDIO pseudocode download failed",
sizeof(vub300->vub_name));
}
......@@ -1370,11 +1370,11 @@ static void download_offload_pseudocode(struct vub300_mmc_host *vub300)
vub300->vub_name);
retval = request_firmware(&fw, vub300->vub_name, &card->dev);
if (retval < 0) {
strncpy(vub300->vub_name, "vub_default.bin",
strscpy(vub300->vub_name, "vub_default.bin",
sizeof(vub300->vub_name));
retval = request_firmware(&fw, vub300->vub_name, &card->dev);
if (retval < 0) {
strncpy(vub300->vub_name,
strscpy(vub300->vub_name,
"no SDIO offload firmware found",
sizeof(vub300->vub_name));
} else {
......@@ -1758,7 +1758,7 @@ static void vub300_cmndwork_thread(struct work_struct *work)
* has been already downloaded to the VUB300 chip
*/
} else if (0 == vub300->mmc->card->sdio_funcs) {
strncpy(vub300->vub_name, "SD memory device",
strscpy(vub300->vub_name, "SD memory device",
sizeof(vub300->vub_name));
} else {
download_offload_pseudocode(vub300);
......
......@@ -526,6 +526,7 @@ struct mmc_host {
/* Host Software Queue support */
bool hsq_enabled;
int hsq_depth;
u32 err_stats[MMC_ERR_MAX];
unsigned long private[] ____cacheline_aligned;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment