MMC core:

- Drop the use of BLK_BOUNCE_HIGH
  - Fix partition switch for GP3
  - Remove usage of the deprecated ida_simple API
 
 MMC host:
  - cqhci: Update bouncing email-addresses in MAINTAINERS
  - davinci_mmc: Use sg_miter for PIO
  - dw_mmc-hi3798cv200: Convert the DT bindings to YAML
  - dw_mmc-hi3798mv200: Add driver for the new dw_mmc variant
  - fsl-imx-esdhc: A couple of corrections/updates to the DT bindings
  - meson-mx-sdhc: Drop use of the ->card_hw_reset() callback
  - moxart-mmc: Use sg_miter for PIO
  - moxart-mmc: Fix accounting for DMA transfers
  - mvsdio: Use sg_miter for PIO
  - mxcmmc: Use sg_miter for PIO
  - omap: Use sg_miter for PIO
  - renesas,sdhi: Add support for R-Car V4M variant
  - sdhci-esdhc-mcf: Use sg_miter for swapping
  - sdhci-of-dwcmshc: Add support for Sophgo CV1800B and SG2002 variants
  - sh_mmcif: Use sg_miter for PIO
  - tmio: Avoid concurrent runs of mmc_request_done()
 -----BEGIN PGP SIGNATURE-----
 
 iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmXu/nkXHHVsZi5oYW5z
 c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCnffw/+L4sj1IzK8vwK24/vQyk2W4Aw
 V4Q6i3rmfxoWRIECsZNdfhBZxvRsDEhb5OZ/piFBrm1RtKdTqxnOp64mu8DayO2E
 Fa9HyKoKRSY20WMr2YAuUp+Kx4gY33SqJCdq/SjqI/dY3QMWvj5uET8Z6GM+zago
 YmD5d3ku4Te7r3ChJABxshbuMyaES4vOSHkW7jcEDEyOacXgBAGHjs8MkWVsNQsD
 NMsD3CzF3VXaJhVW9r3eZMDEbhJoDFLuwplYVs2t8SyxjitHI7eBA91OtrG9TMkC
 W7mvf4pXAYj6mU2wxuMKquqIEnLPjcXdqvym4kkqG7+D89nIe5w2iDcGMH33COi8
 uW/PJ4t/slkMQL9InPkjLS3m1O5X9Y+doJTF/12HAk9oEI8s9JXNbpNxZYiGVLI0
 EAG1bB+h6pW9wlyMWA2u6SIhe5TXStSAGrzDKBrWYt5IdKE4Qrn28tzWxpH2olks
 r8XzQcNMqB7KWhvmO9hSr/F13Qs5472nzsF7lNMQAGp4G3qrehAtd4DJt3xl6KE7
 A2G4nVh/gGBbBu4AWaMvw7l+Gdiqov9qvO1KGUZPJiH3zkjLwdz0u4PT3Fwha34P
 NEaro18+/yovw5Tawgnc5TEsIwbGDT3ymkpIEqMAdcNTY8XIBp+ccd+65t2LSa2S
 gP73V+Em1XGBKfvTI0k=
 =ME/e
 -----END PGP SIGNATURE-----

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

Pull MMC updates from Ulf Hansson:
 "MMC core:
   - Drop the use of BLK_BOUNCE_HIGH
   - Fix partition switch for GP3
   - Remove usage of the deprecated ida_simple API

  MMC host:
   - cqhci: Update bouncing email-addresses in MAINTAINERS
   - davinci_mmc: Use sg_miter for PIO
   - dw_mmc-hi3798cv200: Convert the DT bindings to YAML
   - dw_mmc-hi3798mv200: Add driver for the new dw_mmc variant
   - fsl-imx-esdhc: A couple of corrections/updates to the DT bindings
   - meson-mx-sdhc: Drop use of the ->card_hw_reset() callback
   - moxart-mmc: Use sg_miter for PIO
   - moxart-mmc: Fix accounting for DMA transfers
   - mvsdio: Use sg_miter for PIO
   - mxcmmc: Use sg_miter for PIO
   - omap: Use sg_miter for PIO
   - renesas,sdhi: Add support for R-Car V4M variant
   - sdhci-esdhc-mcf: Use sg_miter for swapping
   - sdhci-of-dwcmshc: Add support for Sophgo CV1800B and SG2002 variants
   - sh_mmcif: Use sg_miter for PIO
   - tmio: Avoid concurrent runs of mmc_request_done()"

* tag 'mmc-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (44 commits)
  mmc: core: make mmc_host_class constant
  mmc: core: Fix switch on gp3 partition
  mmc: tmio: comment the ERR_PTR usage in this driver
  mmc: mmc_spi: Don't mention DMA direction
  mmc: dw_mmc: Remove unused of_gpio.h
  mmc: dw_mmc: add support for hi3798mv200
  dt-bindings: mmc: hisilicon,hi3798cv200-dw-mshc: add Hi3798MV200 binding
  dt-bindings: mmc: dw-mshc-hi3798cv200: convert to YAML
  mmc: dw_mmc-hi3798cv200: remove MODULE_ALIAS()
  mmc: core: Use a struct device* as in-param to mmc_of_parse_clk_phase()
  mmc: wmt-sdmmc: remove an incorrect release_mem_region() call in the .remove function
  mmc: tmio: avoid concurrent runs of mmc_request_done()
  dt-bindings: mmc: fsl-imx-mmc: Document the required clocks
  mmc: sh_mmcif: Advance sg_miter before reading blocks
  mmc: sh_mmcif: sg_miter must not be atomic
  mmc: sdhci-esdhc-mcf: Flag the sg_miter as atomic
  dt-bindings: mmc: fsl-imx-esdhc: add default and 100mhz state
  mmc: core: constify the struct device_type usage
  mmc: sdhci-of-dwcmshc: Add support for Sophgo CV1800B and SG2002
  dt-bindings: mmc: sdhci-of-dwcmhsc: Add Sophgo CV1800B and SG2002 support
  ...
This commit is contained in:
Linus Torvalds 2024-03-13 10:59:28 -07:00
commit 245b6f3239
41 changed files with 791 additions and 319 deletions

View file

@ -55,8 +55,9 @@ properties:
- enum:
- fsl,imx8mn-usdhc
- fsl,imx8mp-usdhc
- fsl,imx93-usdhc
- fsl,imx8ulp-usdhc
- fsl,imx93-usdhc
- fsl,imx95-usdhc
- const: fsl,imx8mm-usdhc
- items:
- enum:
@ -162,6 +163,9 @@ properties:
- const: ahb
- const: per
iommus:
maxItems: 1
power-domains:
maxItems: 1
@ -173,6 +177,11 @@ properties:
- const: state_100mhz
- const: state_200mhz
- const: sleep
- minItems: 2
items:
- const: default
- const: state_100mhz
- const: sleep
- minItems: 1
items:
- const: default

View file

@ -24,6 +24,14 @@ properties:
reg:
maxItems: 1
clocks:
maxItems: 2
clock-names:
items:
- const: ipg
- const: per
interrupts:
maxItems: 1
@ -34,6 +42,8 @@ properties:
const: rx-tx
required:
- clocks
- clock-names
- compatible
- reg
- interrupts
@ -46,6 +56,8 @@ examples:
compatible = "fsl,imx27-mmc", "fsl,imx21-mmc";
reg = <0x10014000 0x1000>;
interrupts = <11>;
clocks = <&clks 29>, <&clks 60>;
clock-names = "ipg", "per";
dmas = <&dma 7>;
dma-names = "rx-tx";
bus-width = <4>;

View file

@ -1,40 +0,0 @@
* Hisilicon Hi3798CV200 specific extensions to the Synopsys Designware Mobile
Storage Host Controller
Read synopsys-dw-mshc.txt for more details
The Synopsys designware mobile storage host controller is used to interface
a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
differences between the core Synopsys dw mshc controller properties described
by synopsys-dw-mshc.txt and the properties used by the Hisilicon Hi3798CV200
specific extensions to the Synopsys Designware Mobile Storage Host Controller.
Required Properties:
- compatible: Should contain "hisilicon,hi3798cv200-dw-mshc".
- clocks: A list of phandle + clock-specifier pairs for the clocks listed
in clock-names.
- clock-names: Should contain the following:
"ciu" - The ciu clock described in synopsys-dw-mshc.txt.
"biu" - The biu clock described in synopsys-dw-mshc.txt.
"ciu-sample" - Hi3798CV200 extended phase clock for ciu sampling.
"ciu-drive" - Hi3798CV200 extended phase clock for ciu driving.
Example:
emmc: mmc@9830000 {
compatible = "hisilicon,hi3798cv200-dw-mshc";
reg = <0x9830000 0x10000>;
interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&crg HISTB_MMC_CIU_CLK>,
<&crg HISTB_MMC_BIU_CLK>,
<&crg HISTB_MMC_SAMPLE_CLK>,
<&crg HISTB_MMC_DRV_CLK>;
clock-names = "ciu", "biu", "ciu-sample", "ciu-drive";
fifo-depth = <256>;
clock-frequency = <200000000>;
cap-mmc-highspeed;
mmc-ddr-1_8v;
mmc-hs200-1_8v;
non-removable;
bus-width = <8>;
};

View file

@ -0,0 +1,97 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mmc/hisilicon,hi3798cv200-dw-mshc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Hisilicon HiSTB SoCs specific extensions to the Synopsys DWMMC controller
maintainers:
- Yang Xiwen <forbidden405@outlook.com>
properties:
compatible:
enum:
- hisilicon,hi3798cv200-dw-mshc
- hisilicon,hi3798mv200-dw-mshc
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: bus interface unit clock
- description: card interface unit clock
- description: card input sample phase clock
- description: controller output drive phase clock
clock-names:
items:
- const: ciu
- const: biu
- const: ciu-sample
- const: ciu-drive
hisilicon,sap-dll-reg:
$ref: /schemas/types.yaml#/definitions/phandle-array
description: |
DWMMC core on Hi3798MV2x SoCs has a delay-locked-loop(DLL) attached to card data input path.
It is integrated into CRG core on the SoC and has to be controlled during tuning.
items:
- description: A phandle pointed to the CRG syscon node
- description: Sample DLL register offset in CRG address space
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
allOf:
- $ref: synopsys-dw-mshc-common.yaml#
- if:
properties:
compatible:
contains:
const: hisilicon,hi3798mv200-dw-mshc
then:
required:
- hisilicon,sap-dll-reg
else:
properties:
hisilicon,sap-dll-reg: false
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/histb-clock.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
mmc@9830000 {
compatible = "hisilicon,hi3798cv200-dw-mshc";
reg = <0x9830000 0x10000>;
interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&crg HISTB_MMC_CIU_CLK>,
<&crg HISTB_MMC_BIU_CLK>,
<&crg HISTB_MMC_SAMPLE_CLK>,
<&crg HISTB_MMC_DRV_CLK>;
clock-names = "ciu", "biu", "ciu-sample", "ciu-drive";
resets = <&crg 0xa0 4>;
reset-names = "reset";
pinctrl-names = "default";
pinctrl-0 = <&emmc_pins_1 &emmc_pins_2
&emmc_pins_3 &emmc_pins_4>;
fifo-depth = <256>;
clock-frequency = <200000000>;
cap-mmc-highspeed;
mmc-ddr-1_8v;
mmc-hs200-1_8v;
non-removable;
bus-width = <8>;
};

View file

@ -67,6 +67,7 @@ properties:
- renesas,sdhi-r8a779a0 # R-Car V3U
- renesas,sdhi-r8a779f0 # R-Car S4-8
- renesas,sdhi-r8a779g0 # R-Car V4H
- renesas,sdhi-r8a779h0 # R-Car V4M
- const: renesas,rcar-gen4-sdhi # R-Car Gen4
reg:

View file

@ -19,6 +19,8 @@ properties:
- rockchip,rk3568-dwcmshc
- rockchip,rk3588-dwcmshc
- snps,dwcmshc-sdhci
- sophgo,cv1800b-dwcmshc
- sophgo,sg2002-dwcmshc
- thead,th1520-dwcmshc
reg:

View file

@ -7809,8 +7809,8 @@ F: drivers/media/usb/em28xx/
EMMC CMDQ HOST CONTROLLER INTERFACE (CQHCI) DRIVER
M: Adrian Hunter <adrian.hunter@intel.com>
M: Ritesh Harjani <riteshh@codeaurora.org>
M: Asutosh Das <asutoshd@codeaurora.org>
M: Asutosh Das <quic_asutoshd@quicinc.com>
R: Ritesh Harjani <ritesh.list@gmail.com>
L: linux-mmc@vger.kernel.org
S: Supported
F: drivers/mmc/host/cqhci*

View file

@ -164,7 +164,7 @@ static struct attribute *memstick_dev_attrs[] = {
};
ATTRIBUTE_GROUPS(memstick_dev);
static struct bus_type memstick_bus_type = {
static const struct bus_type memstick_bus_type = {
.name = "memstick",
.dev_groups = memstick_dev_groups,
.match = memstick_bus_match,

View file

@ -144,7 +144,7 @@ struct mmc_blk_data {
static dev_t mmc_rpmb_devt;
/* Bus type for RPMB character devices */
static struct bus_type mmc_rpmb_bus_type = {
static const struct bus_type mmc_rpmb_bus_type = {
.name = "mmc_rpmb",
};
@ -206,7 +206,7 @@ static void mmc_blk_kref_release(struct kref *ref)
int devidx;
devidx = mmc_get_devidx(md->disk);
ida_simple_remove(&mmc_blk_ida, devidx);
ida_free(&mmc_blk_ida, devidx);
mutex_lock(&open_lock);
md->disk->private_data = NULL;
@ -874,10 +874,11 @@ static const struct block_device_operations mmc_bdops = {
static int mmc_blk_part_switch_pre(struct mmc_card *card,
unsigned int part_type)
{
const unsigned int mask = EXT_CSD_PART_CONFIG_ACC_RPMB;
const unsigned int mask = EXT_CSD_PART_CONFIG_ACC_MASK;
const unsigned int rpmb = EXT_CSD_PART_CONFIG_ACC_RPMB;
int ret = 0;
if ((part_type & mask) == mask) {
if ((part_type & mask) == rpmb) {
if (card->ext_csd.cmdq_en) {
ret = mmc_cmdq_disable(card);
if (ret)
@ -892,10 +893,11 @@ static int mmc_blk_part_switch_pre(struct mmc_card *card,
static int mmc_blk_part_switch_post(struct mmc_card *card,
unsigned int part_type)
{
const unsigned int mask = EXT_CSD_PART_CONFIG_ACC_RPMB;
const unsigned int mask = EXT_CSD_PART_CONFIG_ACC_MASK;
const unsigned int rpmb = EXT_CSD_PART_CONFIG_ACC_RPMB;
int ret = 0;
if ((part_type & mask) == mask) {
if ((part_type & mask) == rpmb) {
mmc_retune_unpause(card->host);
if (card->reenable_cmdq && !card->ext_csd.cmdq_en)
ret = mmc_cmdq_enable(card);
@ -2467,7 +2469,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
bool cache_enabled = false;
bool fua_enabled = false;
devidx = ida_simple_get(&mmc_blk_ida, 0, max_devices, GFP_KERNEL);
devidx = ida_alloc_max(&mmc_blk_ida, max_devices - 1, GFP_KERNEL);
if (devidx < 0) {
/*
* We get -ENOSPC because there are no more any available
@ -2577,7 +2579,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
err_kfree:
kfree(md);
out:
ida_simple_remove(&mmc_blk_ida, devidx);
ida_free(&mmc_blk_ida, devidx);
return ERR_PTR(ret);
}
@ -2703,7 +2705,7 @@ static void mmc_blk_rpmb_device_release(struct device *dev)
{
struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev);
ida_simple_remove(&mmc_rpmb_ida, rpmb->id);
ida_free(&mmc_rpmb_ida, rpmb->id);
kfree(rpmb);
}
@ -2719,13 +2721,13 @@ static int mmc_blk_alloc_rpmb_part(struct mmc_card *card,
struct mmc_rpmb_data *rpmb;
/* This creates the minor number for the RPMB char device */
devidx = ida_simple_get(&mmc_rpmb_ida, 0, max_devices, GFP_KERNEL);
devidx = ida_alloc_max(&mmc_rpmb_ida, max_devices - 1, GFP_KERNEL);
if (devidx < 0)
return devidx;
rpmb = kzalloc(sizeof(*rpmb), GFP_KERNEL);
if (!rpmb) {
ida_simple_remove(&mmc_rpmb_ida, devidx);
ida_free(&mmc_rpmb_ida, devidx);
return -ENOMEM;
}

View file

@ -214,7 +214,7 @@ static const struct dev_pm_ops mmc_bus_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(mmc_bus_suspend, mmc_bus_resume)
};
static struct bus_type mmc_bus_type = {
static const struct bus_type mmc_bus_type = {
.name = "mmc",
.dev_groups = mmc_dev_groups,
.uevent = mmc_bus_uevent,
@ -272,7 +272,7 @@ static void mmc_release_card(struct device *dev)
/*
* Allocate and initialise a new MMC card structure.
*/
struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)
struct mmc_card *mmc_alloc_card(struct mmc_host *host, const struct device_type *type)
{
struct mmc_card *card;

View file

@ -23,7 +23,7 @@ static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *a
static DEVICE_ATTR(name, S_IRUGO, mmc_##name##_show, NULL)
struct mmc_card *mmc_alloc_card(struct mmc_host *host,
struct device_type *type);
const struct device_type *type);
int mmc_add_card(struct mmc_card *card);
void mmc_remove_card(struct mmc_card *card);

View file

@ -76,7 +76,7 @@ static void mmc_host_classdev_release(struct device *dev)
struct mmc_host *host = cls_dev_to_mmc_host(dev);
wakeup_source_unregister(host->ws);
if (of_alias_get_id(host->parent->of_node, "mmc") < 0)
ida_simple_remove(&mmc_host_ida, host->index);
ida_free(&mmc_host_ida, host->index);
kfree(host);
}
@ -88,7 +88,7 @@ static int mmc_host_classdev_shutdown(struct device *dev)
return 0;
}
static struct class mmc_host_class = {
static const struct class mmc_host_class = {
.name = "mmc_host",
.dev_release = mmc_host_classdev_release,
.shutdown_pre = mmc_host_classdev_shutdown,
@ -234,10 +234,8 @@ static void mmc_of_parse_timing_phase(struct device *dev, const char *prop,
}
void
mmc_of_parse_clk_phase(struct mmc_host *host, struct mmc_clk_phase_map *map)
mmc_of_parse_clk_phase(struct device *dev, struct mmc_clk_phase_map *map)
{
struct device *dev = host->parent;
mmc_of_parse_timing_phase(dev, "clk-phase-legacy",
&map->phase[MMC_TIMING_LEGACY]);
mmc_of_parse_timing_phase(dev, "clk-phase-mmc-hs",
@ -538,7 +536,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
min_idx = mmc_first_nonreserved_index();
max_idx = 0;
index = ida_simple_get(&mmc_host_ida, min_idx, max_idx, GFP_KERNEL);
index = ida_alloc_range(&mmc_host_ida, min_idx, max_idx - 1,
GFP_KERNEL);
if (index < 0) {
kfree(host);
return NULL;

View file

@ -883,7 +883,7 @@ static struct attribute *mmc_std_attrs[] = {
};
ATTRIBUTE_GROUPS(mmc_std);
static struct device_type mmc_type = {
static const struct device_type mmc_type = {
.groups = mmc_std_groups,
};

View file

@ -353,9 +353,6 @@ static struct gendisk *mmc_alloc_disk(struct mmc_queue *mq,
if (mmc_can_erase(card))
mmc_queue_setup_discard(card, &lim);
if (!mmc_dev(host)->dma_mask || !*mmc_dev(host)->dma_mask)
lim.bounce = BLK_BOUNCE_HIGH;
lim.max_hw_sectors = min(host->max_blk_count, host->max_req_size / 512);
if (mmc_card_mmc(card) && card->ext_csd.data_sector_size)

View file

@ -805,7 +805,7 @@ static const struct attribute_group sd_std_group = {
};
__ATTRIBUTE_GROUPS(sd_std);
struct device_type sd_type = {
const struct device_type sd_type = {
.groups = sd_std_groups,
};

View file

@ -4,7 +4,7 @@
#include <linux/types.h>
extern struct device_type sd_type;
extern const struct device_type sd_type;
struct mmc_host;
struct mmc_card;

View file

@ -66,7 +66,7 @@ static struct attribute *sdio_std_attrs[] = {
};
ATTRIBUTE_GROUPS(sdio_std);
static struct device_type sdio_type = {
static const struct device_type sdio_type = {
.groups = sdio_std_groups,
};

View file

@ -244,7 +244,7 @@ static const struct dev_pm_ops sdio_bus_pm_ops = {
)
};
static struct bus_type sdio_bus_type = {
static const struct bus_type sdio_bus_type = {
.name = "sdio",
.dev_groups = sdio_dev_groups,
.match = sdio_bus_match,

View file

@ -798,6 +798,15 @@ config MMC_DW_HI3798CV200
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on HiSilicon Hi3798CV200 SoC.
config MMC_DW_HI3798MV200
tristate "Hi3798MV200 specific extensions for Synopsys DW Memory Card Interface"
depends on MMC_DW
select MMC_DW_PLTFM
help
This selects support for HiSilicon Hi3798MV200 SoC specific extensions to the
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on HiSilicon Hi3798MV200 SoC.
config MMC_DW_K3
tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
depends on MMC_DW

View file

@ -51,6 +51,7 @@ obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
obj-$(CONFIG_MMC_DW_BLUEFIELD) += dw_mmc-bluefield.o
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o
obj-$(CONFIG_MMC_DW_HI3798MV200) += dw_mmc-hi3798mv200.o
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o

View file

@ -180,12 +180,6 @@ struct mmc_davinci_host {
#define DAVINCI_MMC_DATADIR_WRITE 2
unsigned char data_dir;
/* buffer is used during PIO of one scatterlist segment, and
* is updated along with buffer_bytes_left. bytes_left applies
* to all N blocks of the PIO transfer.
*/
u8 *buffer;
u32 buffer_bytes_left;
u32 bytes_left;
struct dma_chan *dma_tx;
@ -196,8 +190,8 @@ struct mmc_davinci_host {
bool active_request;
/* For PIO we walk scatterlists one segment at a time. */
struct sg_mapping_iter sg_miter;
unsigned int sg_len;
struct scatterlist *sg;
/* Version of the MMC/SD controller */
u8 version;
@ -213,30 +207,22 @@ struct mmc_davinci_host {
static irqreturn_t mmc_davinci_irq(int irq, void *dev_id);
/* PIO only */
static void mmc_davinci_sg_to_buf(struct mmc_davinci_host *host)
{
host->buffer_bytes_left = sg_dma_len(host->sg);
host->buffer = sg_virt(host->sg);
if (host->buffer_bytes_left > host->bytes_left)
host->buffer_bytes_left = host->bytes_left;
}
static void davinci_fifo_data_trans(struct mmc_davinci_host *host,
unsigned int n)
{
struct sg_mapping_iter *sgm = &host->sg_miter;
u8 *p;
unsigned int i;
if (host->buffer_bytes_left == 0) {
host->sg = sg_next(host->data->sg);
mmc_davinci_sg_to_buf(host);
/*
* By adjusting sgm->consumed this will give a pointer to the
* current index into the sgm.
*/
if (!sg_miter_next(sgm)) {
dev_err(mmc_dev(host->mmc), "ran out of sglist prematurely\n");
return;
}
p = host->buffer;
if (n > host->buffer_bytes_left)
n = host->buffer_bytes_left;
host->buffer_bytes_left -= n;
host->bytes_left -= n;
p = sgm->addr;
/* NOTE: we never transfer more than rw_threshold bytes
* to/from the fifo here; there's no I/O overlap.
@ -261,7 +247,9 @@ static void davinci_fifo_data_trans(struct mmc_davinci_host *host,
p = p + (n & 3);
}
}
host->buffer = p;
sgm->consumed = n;
host->bytes_left -= n;
}
static void mmc_davinci_start_command(struct mmc_davinci_host *host,
@ -517,6 +505,7 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req)
int fifo_lev = (rw_threshold == 32) ? MMCFIFOCTL_FIFOLEV : 0;
int timeout;
struct mmc_data *data = req->data;
unsigned int flags = SG_MITER_ATOMIC; /* Used from IRQ */
if (host->version == MMC_CTLR_VERSION_2)
fifo_lev = (rw_threshold == 64) ? MMCFIFOCTL_FIFOLEV : 0;
@ -545,12 +534,14 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req)
/* Configure the FIFO */
if (data->flags & MMC_DATA_WRITE) {
flags |= SG_MITER_FROM_SG;
host->data_dir = DAVINCI_MMC_DATADIR_WRITE;
writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR | MMCFIFOCTL_FIFORST,
host->base + DAVINCI_MMCFIFOCTL);
writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR,
host->base + DAVINCI_MMCFIFOCTL);
} else {
flags |= SG_MITER_TO_SG;
host->data_dir = DAVINCI_MMC_DATADIR_READ;
writel(fifo_lev | MMCFIFOCTL_FIFODIR_RD | MMCFIFOCTL_FIFORST,
host->base + DAVINCI_MMCFIFOCTL);
@ -558,7 +549,6 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req)
host->base + DAVINCI_MMCFIFOCTL);
}
host->buffer = NULL;
host->bytes_left = data->blocks * data->blksz;
/* For now we try to use DMA whenever we won't need partial FIFO
@ -576,8 +566,7 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req)
} else {
/* Revert to CPU Copy */
host->sg_len = data->sg_len;
host->sg = host->data->sg;
mmc_davinci_sg_to_buf(host);
sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
}
}
@ -843,6 +832,8 @@ davinci_abort_data(struct mmc_davinci_host *host, struct mmc_data *data)
{
mmc_davinci_reset_ctrl(host, 1);
mmc_davinci_reset_ctrl(host, 0);
if (!host->do_dma)
sg_miter_stop(&host->sg_miter);
}
static irqreturn_t mmc_davinci_sdio_irq(int irq, void *dev_id)
@ -919,11 +910,13 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
if (qstatus & MMCST0_DATDNE) {
/* All blocks sent/received, and CRC checks passed */
if (data != NULL) {
if ((host->do_dma == 0) && (host->bytes_left > 0)) {
/* if datasize < rw_threshold
* no RX ints are generated
*/
davinci_fifo_data_trans(host, host->bytes_left);
if (!host->do_dma) {
if (host->bytes_left > 0)
/* if datasize < rw_threshold
* no RX ints are generated
*/
davinci_fifo_data_trans(host, host->bytes_left);
sg_miter_stop(&host->sg_miter);
}
end_transfer = 1;
data->bytes_xfered = data->blocks * data->blksz;

View file

@ -11,7 +11,6 @@
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>

View file

@ -201,4 +201,3 @@ module_platform_driver(dw_mci_hi3798cv200_driver);
MODULE_DESCRIPTION("HiSilicon Hi3798CV200 Specific DW-MSHC Driver Extension");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:dwmmc_hi3798cv200");

View file

@ -0,0 +1,251 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Modified from dw_mmc-hi3798cv200.c
*
* Copyright (c) 2024 Yang Xiwen <forbidden405@outlook.com>
* Copyright (c) 2018 HiSilicon Technologies Co., Ltd.
*/
#include <linux/clk.h>
#include <linux/mfd/syscon.h>
#include <linux/mmc/host.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include "dw_mmc.h"
#include "dw_mmc-pltfm.h"
#define SDMMC_TUNING_CTRL 0x118
#define SDMMC_TUNING_FIND_EDGE BIT(5)
#define ALL_INT_CLR 0x1ffff
/* DLL ctrl reg */
#define SAP_DLL_CTRL_DLLMODE BIT(16)
struct dw_mci_hi3798mv200_priv {
struct clk *sample_clk;
struct clk *drive_clk;
struct regmap *crg_reg;
u32 sap_dll_offset;
struct mmc_clk_phase_map phase_map;
};
static void dw_mci_hi3798mv200_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{
struct dw_mci_hi3798mv200_priv *priv = host->priv;
struct mmc_clk_phase phase = priv->phase_map.phase[ios->timing];
u32 val;
val = mci_readl(host, ENABLE_SHIFT);
if (ios->timing == MMC_TIMING_MMC_DDR52
|| ios->timing == MMC_TIMING_UHS_DDR50)
val |= SDMMC_ENABLE_PHASE;
else
val &= ~SDMMC_ENABLE_PHASE;
mci_writel(host, ENABLE_SHIFT, val);
val = mci_readl(host, DDR_REG);
if (ios->timing == MMC_TIMING_MMC_HS400)
val |= SDMMC_DDR_HS400;
else
val &= ~SDMMC_DDR_HS400;
mci_writel(host, DDR_REG, val);
if (clk_set_rate(host->ciu_clk, ios->clock))
dev_warn(host->dev, "Failed to set rate to %u\n", ios->clock);
else
/*
* CLK_MUX_ROUND_NEAREST is enabled for this clock
* The actual clock rate is not what we set, but a rounded value
* so we should get the rate once again
*/
host->bus_hz = clk_get_rate(host->ciu_clk);
if (phase.valid) {
clk_set_phase(priv->drive_clk, phase.out_deg);
clk_set_phase(priv->sample_clk, phase.in_deg);
} else {
dev_warn(host->dev,
"The phase entry for timing mode %d is missing in device tree.\n",
ios->timing);
}
}
static inline int dw_mci_hi3798mv200_enable_tuning(struct dw_mci_slot *slot)
{
struct dw_mci_hi3798mv200_priv *priv = slot->host->priv;
return regmap_clear_bits(priv->crg_reg, priv->sap_dll_offset, SAP_DLL_CTRL_DLLMODE);
}
static inline int dw_mci_hi3798mv200_disable_tuning(struct dw_mci_slot *slot)
{
struct dw_mci_hi3798mv200_priv *priv = slot->host->priv;
return regmap_set_bits(priv->crg_reg, priv->sap_dll_offset, SAP_DLL_CTRL_DLLMODE);
}
static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot,
u32 opcode)
{
static const int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 };
struct dw_mci *host = slot->host;
struct dw_mci_hi3798mv200_priv *priv = host->priv;
int raise_point = -1, fall_point = -1, mid;
int err, prev_err = -1;
int found = 0;
int regval;
int i;
int ret;
ret = dw_mci_hi3798mv200_enable_tuning(slot);
if (ret < 0)
return ret;
for (i = 0; i < ARRAY_SIZE(degrees); i++) {
clk_set_phase(priv->sample_clk, degrees[i]);
mci_writel(host, RINTSTS, ALL_INT_CLR);
/*
* HiSilicon implemented a tuning mechanism.
* It needs special interaction with the DLL.
*
* Treat edge(flip) found as an error too.
*/
err = mmc_send_tuning(slot->mmc, opcode, NULL);
regval = mci_readl(host, TUNING_CTRL);
if (err || (regval & SDMMC_TUNING_FIND_EDGE))
err = 1;
else
found = 1;
if (i > 0) {
if (err && !prev_err)
fall_point = i - 1;
if (!err && prev_err)
raise_point = i;
}
if (raise_point != -1 && fall_point != -1)
goto tuning_out;
prev_err = err;
err = 0;
}
tuning_out:
ret = dw_mci_hi3798mv200_disable_tuning(slot);
if (ret < 0)
return ret;
if (found) {
if (raise_point == -1)
raise_point = 0;
if (fall_point == -1)
fall_point = ARRAY_SIZE(degrees) - 1;
if (fall_point < raise_point) {
if ((raise_point + fall_point) >
(ARRAY_SIZE(degrees) - 1))
mid = fall_point / 2;
else
mid = (raise_point + ARRAY_SIZE(degrees) - 1) / 2;
} else {
mid = (raise_point + fall_point) / 2;
}
/*
* We don't care what timing we are tuning for,
* simply use the same phase for all timing needs tuning.
*/
priv->phase_map.phase[MMC_TIMING_MMC_HS200].in_deg = degrees[mid];
priv->phase_map.phase[MMC_TIMING_MMC_HS400].in_deg = degrees[mid];
priv->phase_map.phase[MMC_TIMING_UHS_SDR104].in_deg = degrees[mid];
clk_set_phase(priv->sample_clk, degrees[mid]);
dev_dbg(host->dev, "Tuning clk_sample[%d, %d], set[%d]\n",
raise_point, fall_point, degrees[mid]);
ret = 0;
} else {
dev_err(host->dev, "No valid clk_sample shift!\n");
ret = -EINVAL;
}
mci_writel(host, RINTSTS, ALL_INT_CLR);
return ret;
}
static int dw_mci_hi3798mv200_init(struct dw_mci *host)
{
struct dw_mci_hi3798mv200_priv *priv;
struct device_node *np = host->dev->of_node;
int ret;
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
mmc_of_parse_clk_phase(host->dev, &priv->phase_map);
priv->sample_clk = devm_clk_get_enabled(host->dev, "ciu-sample");
if (IS_ERR(priv->sample_clk))
return dev_err_probe(host->dev, PTR_ERR(priv->sample_clk),
"failed to get enabled ciu-sample clock\n");
priv->drive_clk = devm_clk_get_enabled(host->dev, "ciu-drive");
if (IS_ERR(priv->drive_clk))
return dev_err_probe(host->dev, PTR_ERR(priv->drive_clk),
"failed to get enabled ciu-drive clock\n");
priv->crg_reg = syscon_regmap_lookup_by_phandle(np, "hisilicon,sap-dll-reg");
if (IS_ERR(priv->crg_reg))
return dev_err_probe(host->dev, PTR_ERR(priv->crg_reg),
"failed to get CRG reg\n");
ret = of_property_read_u32_index(np, "hisilicon,sap-dll-reg", 1, &priv->sap_dll_offset);
if (ret)
return dev_err_probe(host->dev, ret, "failed to get sample DLL register offset\n");
host->priv = priv;
return 0;
}
static const struct dw_mci_drv_data hi3798mv200_data = {
.common_caps = MMC_CAP_CMD23,
.init = dw_mci_hi3798mv200_init,
.set_ios = dw_mci_hi3798mv200_set_ios,
.execute_tuning = dw_mci_hi3798mv200_execute_tuning_mix_mode,
};
static const struct of_device_id dw_mci_hi3798mv200_match[] = {
{ .compatible = "hisilicon,hi3798mv200-dw-mshc" },
{},
};
static int dw_mci_hi3798mv200_probe(struct platform_device *pdev)
{
return dw_mci_pltfm_register(pdev, &hi3798mv200_data);
}
static void dw_mci_hi3798mv200_remove(struct platform_device *pdev)
{
dw_mci_pltfm_remove(pdev);
}
MODULE_DEVICE_TABLE(of, dw_mci_hi3798mv200_match);
static struct platform_driver dw_mci_hi3798mv200_driver = {
.probe = dw_mci_hi3798mv200_probe,
.remove_new = dw_mci_hi3798mv200_remove,
.driver = {
.name = "dwmmc_hi3798mv200",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = dw_mci_hi3798mv200_match,
},
};
module_platform_driver(dw_mci_hi3798mv200_driver);
MODULE_DESCRIPTION("HiSilicon Hi3798MV200 Specific DW-MSHC Driver Extension");
MODULE_LICENSE("GPL");

View file

@ -35,7 +35,6 @@
#include <linux/bitops.h>
#include <linux/regulator/consumer.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/mmc/slot-gpio.h>
#include "dw_mmc.h"

View file

@ -71,12 +71,23 @@ static int meson_mx_sdhc_clk_hw_register(struct device *dev,
static int meson_mx_sdhc_gate_clk_hw_register(struct device *dev,
const char *name_suffix,
struct clk_hw *parent,
struct clk_hw *hw)
struct clk_hw *hw,
struct clk_bulk_data *clk_bulk_data,
u8 bulk_index)
{
struct clk_parent_data parent_data = { .hw = parent };
int ret;
return meson_mx_sdhc_clk_hw_register(dev, name_suffix, &parent_data, 1,
&clk_gate_ops, hw);
ret = meson_mx_sdhc_clk_hw_register(dev, name_suffix, &parent_data, 1,
&clk_gate_ops, hw);
if (ret)
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;
}
int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base,
@ -115,7 +126,8 @@ int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base,
clkc_data->mod_clk_en.bit_idx = 15;
ret = meson_mx_sdhc_gate_clk_hw_register(dev, "mod_clk_on",
&clkc_data->div.hw,
&clkc_data->mod_clk_en.hw);
&clkc_data->mod_clk_en.hw,
clk_bulk_data, 0);
if (ret)
return ret;
@ -123,7 +135,8 @@ int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base,
clkc_data->tx_clk_en.bit_idx = 14;
ret = meson_mx_sdhc_gate_clk_hw_register(dev, "tx_clk_on",
&clkc_data->div.hw,
&clkc_data->tx_clk_en.hw);
&clkc_data->tx_clk_en.hw,
clk_bulk_data, 1);
if (ret)
return ret;
@ -131,7 +144,8 @@ int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base,
clkc_data->rx_clk_en.bit_idx = 13;
ret = meson_mx_sdhc_gate_clk_hw_register(dev, "rx_clk_on",
&clkc_data->div.hw,
&clkc_data->rx_clk_en.hw);
&clkc_data->rx_clk_en.hw,
clk_bulk_data, 2);
if (ret)
return ret;
@ -139,18 +153,7 @@ int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base,
clkc_data->sd_clk_en.bit_idx = 12;
ret = meson_mx_sdhc_gate_clk_hw_register(dev, "sd_clk_on",
&clkc_data->div.hw,
&clkc_data->sd_clk_en.hw);
if (ret)
return ret;
/*
* TODO: Replace clk_hw.clk with devm_clk_hw_get_clk() once that is
* available.
*/
clk_bulk_data[0].clk = clkc_data->mod_clk_en.hw.clk;
clk_bulk_data[1].clk = clkc_data->sd_clk_en.hw.clk;
clk_bulk_data[2].clk = clkc_data->tx_clk_en.hw.clk;
clk_bulk_data[3].clk = clkc_data->rx_clk_en.hw.clk;
return 0;
&clkc_data->sd_clk_en.hw,
clk_bulk_data, 3);
return ret;
}

View file

@ -65,10 +65,8 @@ static const struct regmap_config meson_mx_sdhc_regmap_config = {
.max_register = MESON_SDHC_CLK2,
};
static void meson_mx_sdhc_hw_reset(struct mmc_host *mmc)
static void meson_mx_sdhc_reset(struct meson_mx_sdhc_host *host)
{
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
regmap_write(host->regmap, MESON_SDHC_SRST, MESON_SDHC_SRST_MAIN_CTRL |
MESON_SDHC_SRST_RXFIFO | MESON_SDHC_SRST_TXFIFO |
MESON_SDHC_SRST_DPHY_RX | MESON_SDHC_SRST_DPHY_TX |
@ -116,7 +114,7 @@ static void meson_mx_sdhc_wait_cmd_ready(struct mmc_host *mmc)
dev_warn(mmc_dev(mmc),
"Failed to poll for CMD_BUSY while processing CMD%d\n",
host->cmd->opcode);
meson_mx_sdhc_hw_reset(mmc);
meson_mx_sdhc_reset(host);
}
ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, esta,
@ -127,7 +125,7 @@ static void meson_mx_sdhc_wait_cmd_ready(struct mmc_host *mmc)
dev_warn(mmc_dev(mmc),
"Failed to poll for ESTA[13:11] while processing CMD%d\n",
host->cmd->opcode);
meson_mx_sdhc_hw_reset(mmc);
meson_mx_sdhc_reset(host);
}
}
@ -495,7 +493,6 @@ static int meson_mx_sdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
}
static const struct mmc_host_ops meson_mx_sdhc_ops = {
.card_hw_reset = meson_mx_sdhc_hw_reset,
.request = meson_mx_sdhc_request,
.set_ios = meson_mx_sdhc_set_ios,
.card_busy = meson_mx_sdhc_card_busy,
@ -618,7 +615,7 @@ static irqreturn_t meson_mx_sdhc_irq_thread(int irq, void *irq_data)
}
if (cmd->error == -EIO || cmd->error == -ETIMEDOUT)
meson_mx_sdhc_hw_reset(host->mmc);
meson_mx_sdhc_reset(host);
else if (cmd->data)
/*
* Clear the FIFOs after completing data transfers to prevent
@ -728,7 +725,7 @@ static void meson_mx_sdhc_init_hw(struct mmc_host *mmc)
{
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
meson_mx_sdhc_hw_reset(mmc);
meson_mx_sdhc_reset(host);
regmap_write(host->regmap, MESON_SDHC_CTRL,
FIELD_PREP(MESON_SDHC_CTRL_RX_PERIOD, 0xf) |

View file

@ -15,7 +15,6 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/dma-direction.h>
#include <linux/crc7.h>
#include <linux/crc-itu-t.h>
#include <linux/scatterlist.h>
@ -510,10 +509,7 @@ mmc_spi_command_send(struct mmc_spi_host *host,
* so we explicitly initialize it to all ones on RX paths.
*/
static void
mmc_spi_setup_data_message(
struct mmc_spi_host *host,
bool multiple,
enum dma_data_direction direction)
mmc_spi_setup_data_message(struct mmc_spi_host *host, bool multiple, bool write)
{
struct spi_transfer *t;
struct scratch *scratch = host->data;
@ -523,7 +519,7 @@ mmc_spi_setup_data_message(
/* for reads, readblock() skips 0xff bytes before finding
* the token; for writes, this transfer issues that token.
*/
if (direction == DMA_TO_DEVICE) {
if (write) {
t = &host->token;
memset(t, 0, sizeof(*t));
t->len = 1;
@ -547,7 +543,7 @@ mmc_spi_setup_data_message(
t = &host->crc;
memset(t, 0, sizeof(*t));
t->len = 2;
if (direction == DMA_TO_DEVICE) {
if (write) {
/* the actual CRC may get written later */
t->tx_buf = &scratch->crc_val;
} else {
@ -570,10 +566,10 @@ mmc_spi_setup_data_message(
* 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 || direction == DMA_TO_DEVICE) {
if (multiple || write) {
t = &host->early_status;
memset(t, 0, sizeof(*t));
t->len = (direction == DMA_TO_DEVICE) ? sizeof(scratch->status) : 1;
t->len = write ? sizeof(scratch->status) : 1;
t->tx_buf = host->ones;
t->rx_buf = scratch->status;
t->cs_change = 1;
@ -777,15 +773,15 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd,
{
struct spi_device *spi = host->spi;
struct spi_transfer *t;
enum dma_data_direction direction = mmc_get_dma_dir(data);
struct scatterlist *sg;
unsigned n_sg;
bool multiple = (data->blocks > 1);
const char *write_or_read = (direction == DMA_TO_DEVICE) ? "write" : "read";
bool write = (data->flags & MMC_DATA_WRITE);
const char *write_or_read = write ? "write" : "read";
u32 clock_rate;
unsigned long timeout;
mmc_spi_setup_data_message(host, multiple, direction);
mmc_spi_setup_data_message(host, multiple, write);
t = &host->t;
if (t->speed_hz)
@ -807,7 +803,7 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd,
/* allow pio too; we don't allow highmem */
kmap_addr = kmap(sg_page(sg));
if (direction == DMA_TO_DEVICE)
if (write)
t->tx_buf = kmap_addr + sg->offset;
else
t->rx_buf = kmap_addr + sg->offset;
@ -818,7 +814,7 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd,
dev_dbg(&spi->dev, " %s block, %d bytes\n", write_or_read, t->len);
if (direction == DMA_TO_DEVICE)
if (write)
status = mmc_spi_writeblock(host, t, timeout);
else
status = mmc_spi_readblock(host, t, timeout);
@ -833,7 +829,9 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd,
}
/* discard mappings */
if (direction == DMA_FROM_DEVICE)
if (write)
/* nothing to do */;
else
flush_dcache_page(sg_page(sg));
kunmap(sg_page(sg));
@ -850,7 +848,7 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd,
* that can affect the STOP_TRAN logic. Complete (and current)
* MMC specs should sort that out before Linux starts using CMD23.
*/
if (direction == DMA_TO_DEVICE && multiple) {
if (write && multiple) {
struct scratch *scratch = host->data;
int tmp;
const unsigned statlen = sizeof(scratch->status);

View file

@ -131,12 +131,10 @@ struct moxart_host {
struct dma_async_tx_descriptor *tx_desc;
struct mmc_host *mmc;
struct mmc_request *mrq;
struct scatterlist *cur_sg;
struct completion dma_complete;
struct completion pio_complete;
u32 num_sg;
u32 data_remain;
struct sg_mapping_iter sg_miter;
u32 data_len;
u32 fifo_width;
u32 timeout;
@ -148,35 +146,6 @@ struct moxart_host {
bool is_removed;
};
static inline void moxart_init_sg(struct moxart_host *host,
struct mmc_data *data)
{
host->cur_sg = data->sg;
host->num_sg = data->sg_len;
host->data_remain = host->cur_sg->length;
if (host->data_remain > host->data_len)
host->data_remain = host->data_len;
}
static inline int moxart_next_sg(struct moxart_host *host)
{
int remain;
struct mmc_data *data = host->mrq->cmd->data;
host->cur_sg++;
host->num_sg--;
if (host->num_sg > 0) {
host->data_remain = host->cur_sg->length;
remain = host->data_len - data->bytes_xfered;
if (remain > 0 && remain < host->data_remain)
host->data_remain = remain;
}
return host->num_sg;
}
static int moxart_wait_for_status(struct moxart_host *host,
u32 mask, u32 *status)
{
@ -254,6 +223,11 @@ static void moxart_dma_complete(void *param)
complete(&host->dma_complete);
}
static bool moxart_use_dma(struct moxart_host *host)
{
return (host->data_len > host->fifo_width) && host->have_dma;
}
static void moxart_transfer_dma(struct mmc_data *data, struct moxart_host *host)
{
u32 len, dir_slave;
@ -291,11 +265,11 @@ static void moxart_transfer_dma(struct mmc_data *data, struct moxart_host *host)
dma_async_issue_pending(dma_chan);
}
data->bytes_xfered += host->data_remain;
wait_for_completion_interruptible_timeout(&host->dma_complete,
host->timeout);
data->bytes_xfered = host->data_len;
dma_unmap_sg(dma_chan->device->dev,
data->sg, data->sg_len,
mmc_get_dma_dir(data));
@ -304,14 +278,28 @@ static void moxart_transfer_dma(struct mmc_data *data, struct moxart_host *host)
static void moxart_transfer_pio(struct moxart_host *host)
{
struct sg_mapping_iter *sgm = &host->sg_miter;
struct mmc_data *data = host->mrq->cmd->data;
u32 *sgp, len = 0, remain, status;
if (host->data_len == data->bytes_xfered)
return;
sgp = sg_virt(host->cur_sg);
remain = host->data_remain;
/*
* By updating sgm->consumes this will get a proper pointer into the
* buffer at any time.
*/
if (!sg_miter_next(sgm)) {
/* This shold not happen */
dev_err(mmc_dev(host->mmc), "ran out of scatterlist prematurely\n");
data->error = -EINVAL;
complete(&host->pio_complete);
return;
}
sgp = sgm->addr;
remain = sgm->length;
if (remain > host->data_len)
remain = host->data_len;
if (data->flags & MMC_DATA_WRITE) {
while (remain > 0) {
@ -326,6 +314,7 @@ static void moxart_transfer_pio(struct moxart_host *host)
sgp++;
len += 4;
}
sgm->consumed += len;
remain -= len;
}
@ -342,22 +331,22 @@ static void moxart_transfer_pio(struct moxart_host *host)
sgp++;
len += 4;
}
sgm->consumed += len;
remain -= len;
}
}
data->bytes_xfered += host->data_remain - remain;
host->data_remain = remain;
if (host->data_len != data->bytes_xfered)
moxart_next_sg(host);
else
data->bytes_xfered += sgm->consumed;
if (host->data_len == data->bytes_xfered) {
complete(&host->pio_complete);
return;
}
}
static void moxart_prepare_data(struct moxart_host *host)
{
struct mmc_data *data = host->mrq->cmd->data;
unsigned int flags = SG_MITER_ATOMIC; /* Used from IRQ */
u32 datactrl;
int blksz_bits;
@ -368,15 +357,19 @@ static void moxart_prepare_data(struct moxart_host *host)
blksz_bits = ffs(data->blksz) - 1;
BUG_ON(1 << blksz_bits != data->blksz);
moxart_init_sg(host, data);
datactrl = DCR_DATA_EN | (blksz_bits & DCR_BLK_SIZE);
if (data->flags & MMC_DATA_WRITE)
if (data->flags & MMC_DATA_WRITE) {
flags |= SG_MITER_FROM_SG;
datactrl |= DCR_DATA_WRITE;
} else {
flags |= SG_MITER_TO_SG;
}
if ((host->data_len > host->fifo_width) && host->have_dma)
if (moxart_use_dma(host))
datactrl |= DCR_DMA_EN;
else
sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
writel(DCR_DATA_FIFO_RESET, host->base + REG_DATA_CONTROL);
writel(MASK_DATA | FIFO_URUN | FIFO_ORUN, host->base + REG_CLEAR);
@ -407,7 +400,7 @@ static void moxart_request(struct mmc_host *mmc, struct mmc_request *mrq)
moxart_send_command(host, host->mrq->cmd);
if (mrq->cmd->data) {
if ((host->data_len > host->fifo_width) && host->have_dma) {
if (moxart_use_dma(host)) {
writel(CARD_CHANGE, host->base + REG_INTERRUPT_MASK);
@ -449,6 +442,9 @@ static void moxart_request(struct mmc_host *mmc, struct mmc_request *mrq)
}
request_done:
if (!moxart_use_dma(host))
sg_miter_stop(&host->sg_miter);
spin_unlock_irqrestore(&host->lock, flags);
mmc_request_done(host->mmc, mrq);
}

View file

@ -38,8 +38,9 @@ struct mvsd_host {
unsigned int xfer_mode;
unsigned int intr_en;
unsigned int ctrl;
bool use_pio;
struct sg_mapping_iter sg_miter;
unsigned int pio_size;
void *pio_ptr;
unsigned int sg_frags;
unsigned int ns_per_clk;
unsigned int clock;
@ -114,11 +115,18 @@ static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)
* data when the buffer is not aligned on a 64 byte
* boundary.
*/
unsigned int miter_flags = SG_MITER_ATOMIC; /* Used from IRQ */
if (data->flags & MMC_DATA_READ)
miter_flags |= SG_MITER_TO_SG;
else
miter_flags |= SG_MITER_FROM_SG;
host->pio_size = data->blocks * data->blksz;
host->pio_ptr = sg_virt(data->sg);
sg_miter_start(&host->sg_miter, data->sg, data->sg_len, miter_flags);
if (!nodma)
dev_dbg(host->dev, "fallback to PIO for data at 0x%p size %d\n",
host->pio_ptr, host->pio_size);
dev_dbg(host->dev, "fallback to PIO for data\n");
host->use_pio = true;
return 1;
} else {
dma_addr_t phys_addr;
@ -129,6 +137,7 @@ static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)
phys_addr = sg_dma_address(data->sg);
mvsd_write(MVSD_SYS_ADDR_LOW, (u32)phys_addr & 0xffff);
mvsd_write(MVSD_SYS_ADDR_HI, (u32)phys_addr >> 16);
host->use_pio = false;
return 0;
}
}
@ -288,8 +297,8 @@ static u32 mvsd_finish_data(struct mvsd_host *host, struct mmc_data *data,
{
void __iomem *iobase = host->base;
if (host->pio_ptr) {
host->pio_ptr = NULL;
if (host->use_pio) {
sg_miter_stop(&host->sg_miter);
host->pio_size = 0;
} else {
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_frags,
@ -344,9 +353,12 @@ static u32 mvsd_finish_data(struct mvsd_host *host, struct mmc_data *data,
static irqreturn_t mvsd_irq(int irq, void *dev)
{
struct mvsd_host *host = dev;
struct sg_mapping_iter *sgm = &host->sg_miter;
void __iomem *iobase = host->base;
u32 intr_status, intr_done_mask;
int irq_handled = 0;
u16 *p;
int s;
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
dev_dbg(host->dev, "intr 0x%04x intr_en 0x%04x hw_state 0x%04x\n",
@ -370,15 +382,36 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
spin_lock(&host->lock);
/* PIO handling, if needed. Messy business... */
if (host->pio_size &&
if (host->use_pio) {
/*
* As we set sgm->consumed this always gives a valid buffer
* position.
*/
if (!sg_miter_next(sgm)) {
/* This should not happen */
dev_err(host->dev, "ran out of scatter segments\n");
spin_unlock(&host->lock);
host->intr_en &=
~(MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W |
MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W);
mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
return IRQ_HANDLED;
}
p = sgm->addr;
s = sgm->length;
if (s > host->pio_size)
s = host->pio_size;
}
if (host->use_pio &&
(intr_status & host->intr_en &
(MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W))) {
u16 *p = host->pio_ptr;
int s = host->pio_size;
while (s >= 32 && (intr_status & MVSD_NOR_RX_FIFO_8W)) {
readsw(iobase + MVSD_FIFO, p, 16);
p += 16;
s -= 32;
sgm->consumed += 32;
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
}
/*
@ -391,6 +424,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
put_unaligned(mvsd_read(MVSD_FIFO), p++);
put_unaligned(mvsd_read(MVSD_FIFO), p++);
s -= 4;
sgm->consumed += 4;
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
}
if (s && s < 4 && (intr_status & MVSD_NOR_RX_READY)) {
@ -398,10 +432,13 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
val[0] = mvsd_read(MVSD_FIFO);
val[1] = mvsd_read(MVSD_FIFO);
memcpy(p, ((void *)&val) + 4 - s, s);
sgm->consumed += s;
s = 0;
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
}
if (s == 0) {
/* PIO transfer done */
host->pio_size -= sgm->consumed;
if (host->pio_size == 0) {
host->intr_en &=
~(MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W);
mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
@ -413,14 +450,10 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
}
dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n",
s, intr_status, mvsd_read(MVSD_HW_STATE));
host->pio_ptr = p;
host->pio_size = s;
irq_handled = 1;
} else if (host->pio_size &&
} else if (host->use_pio &&
(intr_status & host->intr_en &
(MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W))) {
u16 *p = host->pio_ptr;
int s = host->pio_size;
/*
* The TX_FIFO_8W bit is unreliable. When set, bursting
* 16 halfwords all at once in the FIFO drops data. Actually
@ -431,6 +464,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
mvsd_write(MVSD_FIFO, get_unaligned(p++));
mvsd_write(MVSD_FIFO, get_unaligned(p++));
s -= 4;
sgm->consumed += 4;
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
}
if (s < 4) {
@ -439,10 +473,13 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
memcpy(((void *)&val) + 4 - s, p, s);
mvsd_write(MVSD_FIFO, val[0]);
mvsd_write(MVSD_FIFO, val[1]);
sgm->consumed += s;
s = 0;
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
}
if (s == 0) {
/* PIO transfer done */
host->pio_size -= sgm->consumed;
if (host->pio_size == 0) {
host->intr_en &=
~(MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W);
mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
@ -450,8 +487,6 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
}
dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n",
s, intr_status, mvsd_read(MVSD_HW_STATE));
host->pio_ptr = p;
host->pio_size = s;
irq_handled = 1;
}

View file

@ -266,11 +266,18 @@ static inline void buffer_swap32(u32 *buf, int len)
static void mxcmci_swap_buffers(struct mmc_data *data)
{
struct scatterlist *sg;
int i;
struct sg_mapping_iter sgm;
u32 *buf;
for_each_sg(data->sg, sg, data->sg_len, i)
buffer_swap32(sg_virt(sg), sg->length);
sg_miter_start(&sgm, data->sg, data->sg_len,
SG_MITER_TO_SG | SG_MITER_FROM_SG);
while (sg_miter_next(&sgm)) {
buf = sgm.addr;
buffer_swap32(buf, sgm.length);
}
sg_miter_stop(&sgm);
}
#else
static inline void mxcmci_swap_buffers(struct mmc_data *data) {}
@ -526,10 +533,9 @@ static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask)
} while (1);
}
static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
static int mxcmci_pull(struct mxcmci_host *host, u32 *buf, int bytes)
{
unsigned int stat;
u32 *buf = _buf;
while (bytes > 3) {
stat = mxcmci_poll_status(host,
@ -555,10 +561,9 @@ static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
return 0;
}
static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes)
static int mxcmci_push(struct mxcmci_host *host, u32 *buf, int bytes)
{
unsigned int stat;
u32 *buf = _buf;
while (bytes > 3) {
stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
@ -586,31 +591,39 @@ static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes)
static int mxcmci_transfer_data(struct mxcmci_host *host)
{
struct mmc_data *data = host->req->data;
struct scatterlist *sg;
int stat, i;
struct sg_mapping_iter sgm;
int stat;
u32 *buf;
host->data = data;
host->datasize = 0;
sg_miter_start(&sgm, data->sg, data->sg_len,
(data->flags & MMC_DATA_READ) ? SG_MITER_TO_SG : SG_MITER_FROM_SG);
if (data->flags & MMC_DATA_READ) {
for_each_sg(data->sg, sg, data->sg_len, i) {
stat = mxcmci_pull(host, sg_virt(sg), sg->length);
while (sg_miter_next(&sgm)) {
buf = sgm.addr;
stat = mxcmci_pull(host, buf, sgm.length);
if (stat)
return stat;
host->datasize += sg->length;
goto transfer_error;
host->datasize += sgm.length;
}
} else {
for_each_sg(data->sg, sg, data->sg_len, i) {
stat = mxcmci_push(host, sg_virt(sg), sg->length);
while (sg_miter_next(&sgm)) {
buf = sgm.addr;
stat = mxcmci_push(host, buf, sgm.length);
if (stat)
return stat;
host->datasize += sg->length;
goto transfer_error;
host->datasize += sgm.length;
}
stat = mxcmci_poll_status(host, STATUS_WRITE_OP_DONE);
if (stat)
return stat;
goto transfer_error;
}
return 0;
transfer_error:
sg_miter_stop(&sgm);
return stat;
}
static void mxcmci_datawork(struct work_struct *work)

View file

@ -148,10 +148,8 @@ struct mmc_omap_host {
struct work_struct send_stop_work;
struct mmc_data *stop_data;
struct sg_mapping_iter sg_miter;
unsigned int sg_len;
int sg_idx;
u16 * buffer;
u32 buffer_bytes_left;
u32 total_bytes_left;
unsigned features;
@ -456,6 +454,8 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
{
if (host->dma_in_use)
mmc_omap_release_dma(host, data, data->error);
else
sg_miter_stop(&host->sg_miter);
host->data = NULL;
host->sg_len = 0;
@ -651,19 +651,6 @@ mmc_omap_cmd_timer(struct timer_list *t)
spin_unlock_irqrestore(&host->slot_lock, flags);
}
/* PIO only */
static void
mmc_omap_sg_to_buf(struct mmc_omap_host *host)
{
struct scatterlist *sg;
sg = host->data->sg + host->sg_idx;
host->buffer_bytes_left = sg->length;
host->buffer = sg_virt(sg);
if (host->buffer_bytes_left > host->total_bytes_left)
host->buffer_bytes_left = host->total_bytes_left;
}
static void
mmc_omap_clk_timer(struct timer_list *t)
{
@ -676,33 +663,37 @@ mmc_omap_clk_timer(struct timer_list *t)
static void
mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
{
struct sg_mapping_iter *sgm = &host->sg_miter;
int n, nwords;
u16 *buffer;
if (host->buffer_bytes_left == 0) {
host->sg_idx++;
BUG_ON(host->sg_idx == host->sg_len);
mmc_omap_sg_to_buf(host);
if (!sg_miter_next(sgm)) {
/* This should not happen */
dev_err(mmc_dev(host->mmc), "ran out of scatterlist prematurely\n");
return;
}
buffer = sgm->addr;
n = 64;
if (n > host->buffer_bytes_left)
n = host->buffer_bytes_left;
if (n > sgm->length)
n = sgm->length;
if (n > host->total_bytes_left)
n = host->total_bytes_left;
/* Round up to handle odd number of bytes to transfer */
nwords = DIV_ROUND_UP(n, 2);
host->buffer_bytes_left -= n;
sgm->consumed = n;
host->total_bytes_left -= n;
host->data->bytes_xfered += n;
if (write) {
__raw_writesw(host->virt_base + OMAP_MMC_REG(host, DATA),
host->buffer, nwords);
buffer, nwords);
} else {
__raw_readsw(host->virt_base + OMAP_MMC_REG(host, DATA),
host->buffer, nwords);
buffer, nwords);
}
host->buffer += nwords;
}
#ifdef CONFIG_MMC_DEBUG
@ -956,6 +947,7 @@ static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_reque
static void
mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
{
unsigned int miter_flags = SG_MITER_ATOMIC; /* Used from IRQ */
struct mmc_data *data = req->data;
int i, use_dma = 1, block_size;
struct scatterlist *sg;
@ -990,7 +982,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
}
}
host->sg_idx = 0;
if (use_dma) {
enum dma_data_direction dma_data_dir;
struct dma_async_tx_descriptor *tx;
@ -1071,7 +1062,11 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
OMAP_MMC_WRITE(host, BUF, 0x1f1f);
host->total_bytes_left = data->blocks * block_size;
host->sg_len = sg_len;
mmc_omap_sg_to_buf(host);
if (data->flags & MMC_DATA_READ)
miter_flags |= SG_MITER_TO_SG;
else
miter_flags |= SG_MITER_FROM_SG;
sg_miter_start(&host->sg_miter, data->sg, data->sg_len, miter_flags);
host->dma_in_use = 0;
}

View file

@ -9,6 +9,7 @@
#ifndef RENESAS_SDHI_H
#define RENESAS_SDHI_H
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
#include "tmio_mmc.h"
@ -63,7 +64,7 @@ struct renesas_sdhi_of_data_with_quirks {
struct renesas_sdhi_dma {
unsigned long end_flags;
enum dma_slave_buswidth dma_buswidth;
bool (*filter)(struct dma_chan *chan, void *arg);
dma_filter_fn filter;
void (*enable)(struct tmio_mmc_host *host, bool enable);
struct completion dma_dataend;
struct tasklet_struct dma_complete;

View file

@ -299,9 +299,8 @@ static void esdhc_mcf_pltfm_set_bus_width(struct sdhci_host *host, int width)
static void esdhc_mcf_request_done(struct sdhci_host *host,
struct mmc_request *mrq)
{
struct scatterlist *sg;
struct sg_mapping_iter sgm;
u32 *buffer;
int i;
if (!mrq->data || !mrq->data->bytes_xfered)
goto exit_done;
@ -313,10 +312,13 @@ static void esdhc_mcf_request_done(struct sdhci_host *host,
* On mcf5441x there is no hw sdma option/flag to select the dma
* transfer endiannes. A swap after the transfer is needed.
*/
for_each_sg(mrq->data->sg, sg, mrq->data->sg_len, i) {
buffer = (u32 *)sg_virt(sg);
esdhc_mcf_buffer_swap32(buffer, sg->length);
sg_miter_start(&sgm, mrq->data->sg, mrq->data->sg_len,
SG_MITER_ATOMIC | SG_MITER_TO_SG | SG_MITER_FROM_SG);
while (sg_miter_next(&sgm)) {
buffer = sgm.addr;
esdhc_mcf_buffer_swap32(buffer, sgm.length);
}
sg_miter_stop(&sgm);
exit_done:
mmc_request_done(host->mmc, mrq);

View file

@ -435,7 +435,7 @@ static int aspeed_sdhci_probe(struct platform_device *pdev)
goto err_sdhci_add;
if (dev->phase_desc)
mmc_of_parse_clk_phase(host->mmc, &dev->phase_map);
mmc_of_parse_clk_phase(&pdev->dev, &dev->phase_map);
ret = sdhci_add_host(host);
if (ret)

View file

@ -52,6 +52,20 @@
#define AT_CTRL_SWIN_TH_VAL_MASK GENMASK(31, 24) /* bits [31:24] */
#define AT_CTRL_SWIN_TH_VAL 0x9 /* sampling window threshold */
/* Sophgo CV18XX specific Registers */
#define CV18XX_SDHCI_MSHC_CTRL 0x00
#define CV18XX_EMMC_FUNC_EN BIT(0)
#define CV18XX_LATANCY_1T BIT(1)
#define CV18XX_SDHCI_PHY_TX_RX_DLY 0x40
#define CV18XX_PHY_TX_DLY_MSK GENMASK(6, 0)
#define CV18XX_PHY_TX_SRC_MSK GENMASK(9, 8)
#define CV18XX_PHY_TX_SRC_INVERT_CLK_TX 0x1
#define CV18XX_PHY_RX_DLY_MSK GENMASK(22, 16)
#define CV18XX_PHY_RX_SRC_MSK GENMASK(25, 24)
#define CV18XX_PHY_RX_SRC_INVERT_RX_CLK 0x1
#define CV18XX_SDHCI_PHY_CONFIG 0x4c
#define CV18XX_PHY_TX_BPS BIT(0)
/* Rockchip specific Registers */
#define DWCMSHC_EMMC_DLL_CTRL 0x800
#define DWCMSHC_EMMC_DLL_RXCLK 0x804
@ -642,6 +656,35 @@ static void th1520_sdhci_reset(struct sdhci_host *host, u8 mask)
}
}
static void cv18xx_sdhci_reset(struct sdhci_host *host, u8 mask)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
u32 val, emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
sdhci_reset(host, mask);
if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
val = sdhci_readl(host, priv->vendor_specific_area1 + CV18XX_SDHCI_MSHC_CTRL);
val |= CV18XX_EMMC_FUNC_EN;
sdhci_writel(host, val, priv->vendor_specific_area1 + CV18XX_SDHCI_MSHC_CTRL);
}
val = sdhci_readl(host, priv->vendor_specific_area1 + CV18XX_SDHCI_MSHC_CTRL);
val |= CV18XX_LATANCY_1T;
sdhci_writel(host, val, priv->vendor_specific_area1 + CV18XX_SDHCI_MSHC_CTRL);
val = sdhci_readl(host, priv->vendor_specific_area1 + CV18XX_SDHCI_PHY_CONFIG);
val |= CV18XX_PHY_TX_BPS;
sdhci_writel(host, val, priv->vendor_specific_area1 + CV18XX_SDHCI_PHY_CONFIG);
val = (FIELD_PREP(CV18XX_PHY_TX_DLY_MSK, 0) |
FIELD_PREP(CV18XX_PHY_TX_SRC_MSK, CV18XX_PHY_TX_SRC_INVERT_CLK_TX) |
FIELD_PREP(CV18XX_PHY_RX_DLY_MSK, 0) |
FIELD_PREP(CV18XX_PHY_RX_SRC_MSK, CV18XX_PHY_RX_SRC_INVERT_RX_CLK));
sdhci_writel(host, val, priv->vendor_specific_area1 + CV18XX_SDHCI_PHY_TX_RX_DLY);
}
static const struct sdhci_ops sdhci_dwcmshc_ops = {
.set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width,
@ -671,6 +714,15 @@ static const struct sdhci_ops sdhci_dwcmshc_th1520_ops = {
.platform_execute_tuning = &th1520_execute_tuning,
};
static const struct sdhci_ops sdhci_dwcmshc_cv18xx_ops = {
.set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width,
.set_uhs_signaling = dwcmshc_set_uhs_signaling,
.get_max_clock = dwcmshc_get_max_clock,
.reset = cv18xx_sdhci_reset,
.adma_write_desc = dwcmshc_adma_write_desc,
};
static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
.ops = &sdhci_dwcmshc_ops,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
@ -700,6 +752,12 @@ static const struct sdhci_pltfm_data sdhci_dwcmshc_th1520_pdata = {
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
};
static const struct sdhci_pltfm_data sdhci_dwcmshc_cv18xx_pdata = {
.ops = &sdhci_dwcmshc_cv18xx_ops,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
};
static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
{
int err;
@ -768,6 +826,14 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
.compatible = "snps,dwcmshc-sdhci",
.data = &sdhci_dwcmshc_pdata,
},
{
.compatible = "sophgo,cv1800b-dwcmshc",
.data = &sdhci_dwcmshc_cv18xx_pdata,
},
{
.compatible = "sophgo,sg2002-dwcmshc",
.data = &sdhci_dwcmshc_cv18xx_pdata,
},
{
.compatible = "thead,th1520-dwcmshc",
.data = &sdhci_dwcmshc_th1520_pdata,

View file

@ -227,14 +227,12 @@ struct sh_mmcif_host {
bool dying;
long timeout;
void __iomem *addr;
u32 *pio_ptr;
spinlock_t lock; /* protect sh_mmcif_host::state */
enum sh_mmcif_state state;
enum sh_mmcif_wait_for wait_for;
struct delayed_work timeout_work;
size_t blocksize;
int sg_idx;
int sg_blkidx;
struct sg_mapping_iter sg_miter;
bool power;
bool ccs_enable; /* Command Completion Signal support */
bool clk_ctrl2_enable;
@ -600,32 +598,17 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
return ret;
}
static bool sh_mmcif_next_block(struct sh_mmcif_host *host, u32 *p)
{
struct mmc_data *data = host->mrq->data;
host->sg_blkidx += host->blocksize;
/* data->sg->length must be a multiple of host->blocksize? */
BUG_ON(host->sg_blkidx > data->sg->length);
if (host->sg_blkidx == data->sg->length) {
host->sg_blkidx = 0;
if (++host->sg_idx < data->sg_len)
host->pio_ptr = sg_virt(++data->sg);
} else {
host->pio_ptr = p;
}
return host->sg_idx != data->sg_len;
}
static void sh_mmcif_single_read(struct sh_mmcif_host *host,
struct mmc_request *mrq)
{
struct mmc_data *data = mrq->data;
host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
BLOCK_SIZE_MASK) + 3;
sg_miter_start(&host->sg_miter, data->sg, data->sg_len,
SG_MITER_TO_SG);
host->wait_for = MMCIF_WAIT_FOR_READ;
/* buf read enable */
@ -634,20 +617,32 @@ static void sh_mmcif_single_read(struct sh_mmcif_host *host,
static bool sh_mmcif_read_block(struct sh_mmcif_host *host)
{
struct sg_mapping_iter *sgm = &host->sg_miter;
struct device *dev = sh_mmcif_host_to_dev(host);
struct mmc_data *data = host->mrq->data;
u32 *p = sg_virt(data->sg);
u32 *p;
int i;
if (host->sd_error) {
sg_miter_stop(sgm);
data->error = sh_mmcif_error_manage(host);
dev_dbg(dev, "%s(): %d\n", __func__, data->error);
return false;
}
if (!sg_miter_next(sgm)) {
/* This should not happen on single blocks */
sg_miter_stop(sgm);
return false;
}
p = sgm->addr;
for (i = 0; i < host->blocksize / 4; i++)
*p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA);
sg_miter_stop(&host->sg_miter);
/* buffer read end */
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFRE);
host->wait_for = MMCIF_WAIT_FOR_READ_END;
@ -658,6 +653,7 @@ static bool sh_mmcif_read_block(struct sh_mmcif_host *host)
static void sh_mmcif_multi_read(struct sh_mmcif_host *host,
struct mmc_request *mrq)
{
struct sg_mapping_iter *sgm = &host->sg_miter;
struct mmc_data *data = mrq->data;
if (!data->sg_len || !data->sg->length)
@ -666,46 +662,63 @@ static void sh_mmcif_multi_read(struct sh_mmcif_host *host,
host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
BLOCK_SIZE_MASK;
sg_miter_start(sgm, data->sg, data->sg_len,
SG_MITER_TO_SG);
/* Advance to the first sglist entry */
if (!sg_miter_next(sgm)) {
sg_miter_stop(sgm);
return;
}
host->wait_for = MMCIF_WAIT_FOR_MREAD;
host->sg_idx = 0;
host->sg_blkidx = 0;
host->pio_ptr = sg_virt(data->sg);
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
}
static bool sh_mmcif_mread_block(struct sh_mmcif_host *host)
{
struct sg_mapping_iter *sgm = &host->sg_miter;
struct device *dev = sh_mmcif_host_to_dev(host);
struct mmc_data *data = host->mrq->data;
u32 *p = host->pio_ptr;
u32 *p;
int i;
if (host->sd_error) {
sg_miter_stop(sgm);
data->error = sh_mmcif_error_manage(host);
dev_dbg(dev, "%s(): %d\n", __func__, data->error);
return false;
}
BUG_ON(!data->sg->length);
p = sgm->addr;
for (i = 0; i < host->blocksize / 4; i++)
*p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA);
if (!sh_mmcif_next_block(host, p))
return false;
sgm->consumed = host->blocksize;
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
if (!sg_miter_next(sgm)) {
sg_miter_stop(sgm);
return false;
}
return true;
}
static void sh_mmcif_single_write(struct sh_mmcif_host *host,
struct mmc_request *mrq)
{
struct mmc_data *data = mrq->data;
host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
BLOCK_SIZE_MASK) + 3;
sg_miter_start(&host->sg_miter, data->sg, data->sg_len,
SG_MITER_FROM_SG);
host->wait_for = MMCIF_WAIT_FOR_WRITE;
/* buf write enable */
@ -714,20 +727,32 @@ static void sh_mmcif_single_write(struct sh_mmcif_host *host,
static bool sh_mmcif_write_block(struct sh_mmcif_host *host)
{
struct sg_mapping_iter *sgm = &host->sg_miter;
struct device *dev = sh_mmcif_host_to_dev(host);
struct mmc_data *data = host->mrq->data;
u32 *p = sg_virt(data->sg);
u32 *p;
int i;
if (host->sd_error) {
sg_miter_stop(sgm);
data->error = sh_mmcif_error_manage(host);
dev_dbg(dev, "%s(): %d\n", __func__, data->error);
return false;
}
if (!sg_miter_next(sgm)) {
/* This should not happen on single blocks */
sg_miter_stop(sgm);
return false;
}
p = sgm->addr;
for (i = 0; i < host->blocksize / 4; i++)
sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++);
sg_miter_stop(&host->sg_miter);
/* buffer write end */
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE);
host->wait_for = MMCIF_WAIT_FOR_WRITE_END;
@ -738,6 +763,7 @@ static bool sh_mmcif_write_block(struct sh_mmcif_host *host)
static void sh_mmcif_multi_write(struct sh_mmcif_host *host,
struct mmc_request *mrq)
{
struct sg_mapping_iter *sgm = &host->sg_miter;
struct mmc_data *data = mrq->data;
if (!data->sg_len || !data->sg->length)
@ -746,34 +772,46 @@ static void sh_mmcif_multi_write(struct sh_mmcif_host *host,
host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
BLOCK_SIZE_MASK;
sg_miter_start(sgm, data->sg, data->sg_len,
SG_MITER_FROM_SG);
/* Advance to the first sglist entry */
if (!sg_miter_next(sgm)) {
sg_miter_stop(sgm);
return;
}
host->wait_for = MMCIF_WAIT_FOR_MWRITE;
host->sg_idx = 0;
host->sg_blkidx = 0;
host->pio_ptr = sg_virt(data->sg);
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
}
static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host)
{
struct sg_mapping_iter *sgm = &host->sg_miter;
struct device *dev = sh_mmcif_host_to_dev(host);
struct mmc_data *data = host->mrq->data;
u32 *p = host->pio_ptr;
u32 *p;
int i;
if (host->sd_error) {
sg_miter_stop(sgm);
data->error = sh_mmcif_error_manage(host);
dev_dbg(dev, "%s(): %d\n", __func__, data->error);
return false;
}
BUG_ON(!data->sg->length);
p = sgm->addr;
for (i = 0; i < host->blocksize / 4; i++)
sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++);
if (!sh_mmcif_next_block(host, p))
sgm->consumed = host->blocksize;
if (!sg_miter_next(sgm)) {
sg_miter_stop(sgm);
return false;
}
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);

View file

@ -259,6 +259,8 @@ static void tmio_mmc_reset_work(struct work_struct *work)
else
mrq->cmd->error = -ETIMEDOUT;
/* No new calls yet, but disallow concurrent tmio_mmc_done_work() */
host->mrq = ERR_PTR(-EBUSY);
host->cmd = NULL;
host->data = NULL;
@ -970,6 +972,7 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
return;
}
/* Disallow new mrqs and work handlers to run */
host->mrq = ERR_PTR(-EBUSY);
spin_unlock_irqrestore(&host->lock, flags);
@ -1004,8 +1007,9 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
"%s.%d: IOS interrupted: clk %u, mode %u",
current->comm, task_pid_nr(current),
ios->clock, ios->power_mode);
host->mrq = NULL;
/* Ready for new mrqs */
host->mrq = NULL;
host->clk_cache = ios->clock;
mutex_unlock(&host->ios_lock);

View file

@ -1284,8 +1284,6 @@ static int wbsd_scan(struct wbsd_host *host)
continue;
for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) {
id = 0xFFFF;
host->config = config_ports[i];
host->unlock_code = unlock_codes[j];

View file

@ -883,7 +883,6 @@ static void wmt_mci_remove(struct platform_device *pdev)
{
struct mmc_host *mmc;
struct wmt_mci_priv *priv;
struct resource *res;
u32 reg_tmp;
mmc = platform_get_drvdata(pdev);
@ -911,9 +910,6 @@ static void wmt_mci_remove(struct platform_device *pdev)
clk_disable_unprepare(priv->clk_sdmmc);
clk_put(priv->clk_sdmmc);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, resource_size(res));
mmc_free_host(mmc);
dev_info(&pdev->dev, "WMT MCI device removed\n");

View file

@ -539,7 +539,7 @@ struct mmc_host *devm_mmc_alloc_host(struct device *dev, int extra);
int mmc_add_host(struct mmc_host *);
void mmc_remove_host(struct mmc_host *);
void mmc_free_host(struct mmc_host *);
void mmc_of_parse_clk_phase(struct mmc_host *host,
void mmc_of_parse_clk_phase(struct device *dev,
struct mmc_clk_phase_map *map);
int mmc_of_parse(struct mmc_host *host);
int mmc_of_parse_voltage(struct mmc_host *host, u32 *mask);