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:
commit
245b6f3239
@ -55,8 +55,9 @@ properties:
|
|||||||
- enum:
|
- enum:
|
||||||
- fsl,imx8mn-usdhc
|
- fsl,imx8mn-usdhc
|
||||||
- fsl,imx8mp-usdhc
|
- fsl,imx8mp-usdhc
|
||||||
- fsl,imx93-usdhc
|
|
||||||
- fsl,imx8ulp-usdhc
|
- fsl,imx8ulp-usdhc
|
||||||
|
- fsl,imx93-usdhc
|
||||||
|
- fsl,imx95-usdhc
|
||||||
- const: fsl,imx8mm-usdhc
|
- const: fsl,imx8mm-usdhc
|
||||||
- items:
|
- items:
|
||||||
- enum:
|
- enum:
|
||||||
@ -162,6 +163,9 @@ properties:
|
|||||||
- const: ahb
|
- const: ahb
|
||||||
- const: per
|
- const: per
|
||||||
|
|
||||||
|
iommus:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
power-domains:
|
power-domains:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
||||||
@ -173,6 +177,11 @@ properties:
|
|||||||
- const: state_100mhz
|
- const: state_100mhz
|
||||||
- const: state_200mhz
|
- const: state_200mhz
|
||||||
- const: sleep
|
- const: sleep
|
||||||
|
- minItems: 2
|
||||||
|
items:
|
||||||
|
- const: default
|
||||||
|
- const: state_100mhz
|
||||||
|
- const: sleep
|
||||||
- minItems: 1
|
- minItems: 1
|
||||||
items:
|
items:
|
||||||
- const: default
|
- const: default
|
||||||
|
@ -24,6 +24,14 @@ properties:
|
|||||||
reg:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
||||||
|
clocks:
|
||||||
|
maxItems: 2
|
||||||
|
|
||||||
|
clock-names:
|
||||||
|
items:
|
||||||
|
- const: ipg
|
||||||
|
- const: per
|
||||||
|
|
||||||
interrupts:
|
interrupts:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
||||||
@ -34,6 +42,8 @@ properties:
|
|||||||
const: rx-tx
|
const: rx-tx
|
||||||
|
|
||||||
required:
|
required:
|
||||||
|
- clocks
|
||||||
|
- clock-names
|
||||||
- compatible
|
- compatible
|
||||||
- reg
|
- reg
|
||||||
- interrupts
|
- interrupts
|
||||||
@ -46,6 +56,8 @@ examples:
|
|||||||
compatible = "fsl,imx27-mmc", "fsl,imx21-mmc";
|
compatible = "fsl,imx27-mmc", "fsl,imx21-mmc";
|
||||||
reg = <0x10014000 0x1000>;
|
reg = <0x10014000 0x1000>;
|
||||||
interrupts = <11>;
|
interrupts = <11>;
|
||||||
|
clocks = <&clks 29>, <&clks 60>;
|
||||||
|
clock-names = "ipg", "per";
|
||||||
dmas = <&dma 7>;
|
dmas = <&dma 7>;
|
||||||
dma-names = "rx-tx";
|
dma-names = "rx-tx";
|
||||||
bus-width = <4>;
|
bus-width = <4>;
|
||||||
|
@ -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>;
|
|
||||||
};
|
|
@ -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>;
|
||||||
|
};
|
@ -67,6 +67,7 @@ properties:
|
|||||||
- renesas,sdhi-r8a779a0 # R-Car V3U
|
- renesas,sdhi-r8a779a0 # R-Car V3U
|
||||||
- renesas,sdhi-r8a779f0 # R-Car S4-8
|
- renesas,sdhi-r8a779f0 # R-Car S4-8
|
||||||
- renesas,sdhi-r8a779g0 # R-Car V4H
|
- renesas,sdhi-r8a779g0 # R-Car V4H
|
||||||
|
- renesas,sdhi-r8a779h0 # R-Car V4M
|
||||||
- const: renesas,rcar-gen4-sdhi # R-Car Gen4
|
- const: renesas,rcar-gen4-sdhi # R-Car Gen4
|
||||||
|
|
||||||
reg:
|
reg:
|
||||||
|
@ -19,6 +19,8 @@ properties:
|
|||||||
- rockchip,rk3568-dwcmshc
|
- rockchip,rk3568-dwcmshc
|
||||||
- rockchip,rk3588-dwcmshc
|
- rockchip,rk3588-dwcmshc
|
||||||
- snps,dwcmshc-sdhci
|
- snps,dwcmshc-sdhci
|
||||||
|
- sophgo,cv1800b-dwcmshc
|
||||||
|
- sophgo,sg2002-dwcmshc
|
||||||
- thead,th1520-dwcmshc
|
- thead,th1520-dwcmshc
|
||||||
|
|
||||||
reg:
|
reg:
|
||||||
|
@ -7809,8 +7809,8 @@ F: drivers/media/usb/em28xx/
|
|||||||
|
|
||||||
EMMC CMDQ HOST CONTROLLER INTERFACE (CQHCI) DRIVER
|
EMMC CMDQ HOST CONTROLLER INTERFACE (CQHCI) DRIVER
|
||||||
M: Adrian Hunter <adrian.hunter@intel.com>
|
M: Adrian Hunter <adrian.hunter@intel.com>
|
||||||
M: Ritesh Harjani <riteshh@codeaurora.org>
|
M: Asutosh Das <quic_asutoshd@quicinc.com>
|
||||||
M: Asutosh Das <asutoshd@codeaurora.org>
|
R: Ritesh Harjani <ritesh.list@gmail.com>
|
||||||
L: linux-mmc@vger.kernel.org
|
L: linux-mmc@vger.kernel.org
|
||||||
S: Supported
|
S: Supported
|
||||||
F: drivers/mmc/host/cqhci*
|
F: drivers/mmc/host/cqhci*
|
||||||
|
@ -164,7 +164,7 @@ static struct attribute *memstick_dev_attrs[] = {
|
|||||||
};
|
};
|
||||||
ATTRIBUTE_GROUPS(memstick_dev);
|
ATTRIBUTE_GROUPS(memstick_dev);
|
||||||
|
|
||||||
static struct bus_type memstick_bus_type = {
|
static const struct bus_type memstick_bus_type = {
|
||||||
.name = "memstick",
|
.name = "memstick",
|
||||||
.dev_groups = memstick_dev_groups,
|
.dev_groups = memstick_dev_groups,
|
||||||
.match = memstick_bus_match,
|
.match = memstick_bus_match,
|
||||||
|
@ -144,7 +144,7 @@ struct mmc_blk_data {
|
|||||||
static dev_t mmc_rpmb_devt;
|
static dev_t mmc_rpmb_devt;
|
||||||
|
|
||||||
/* Bus type for RPMB character devices */
|
/* 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",
|
.name = "mmc_rpmb",
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -206,7 +206,7 @@ static void mmc_blk_kref_release(struct kref *ref)
|
|||||||
int devidx;
|
int devidx;
|
||||||
|
|
||||||
devidx = mmc_get_devidx(md->disk);
|
devidx = mmc_get_devidx(md->disk);
|
||||||
ida_simple_remove(&mmc_blk_ida, devidx);
|
ida_free(&mmc_blk_ida, devidx);
|
||||||
|
|
||||||
mutex_lock(&open_lock);
|
mutex_lock(&open_lock);
|
||||||
md->disk->private_data = NULL;
|
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,
|
static int mmc_blk_part_switch_pre(struct mmc_card *card,
|
||||||
unsigned int part_type)
|
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;
|
int ret = 0;
|
||||||
|
|
||||||
if ((part_type & mask) == mask) {
|
if ((part_type & mask) == rpmb) {
|
||||||
if (card->ext_csd.cmdq_en) {
|
if (card->ext_csd.cmdq_en) {
|
||||||
ret = mmc_cmdq_disable(card);
|
ret = mmc_cmdq_disable(card);
|
||||||
if (ret)
|
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,
|
static int mmc_blk_part_switch_post(struct mmc_card *card,
|
||||||
unsigned int part_type)
|
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;
|
int ret = 0;
|
||||||
|
|
||||||
if ((part_type & mask) == mask) {
|
if ((part_type & mask) == rpmb) {
|
||||||
mmc_retune_unpause(card->host);
|
mmc_retune_unpause(card->host);
|
||||||
if (card->reenable_cmdq && !card->ext_csd.cmdq_en)
|
if (card->reenable_cmdq && !card->ext_csd.cmdq_en)
|
||||||
ret = mmc_cmdq_enable(card);
|
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 cache_enabled = false;
|
||||||
bool fua_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) {
|
if (devidx < 0) {
|
||||||
/*
|
/*
|
||||||
* We get -ENOSPC because there are no more any available
|
* 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:
|
err_kfree:
|
||||||
kfree(md);
|
kfree(md);
|
||||||
out:
|
out:
|
||||||
ida_simple_remove(&mmc_blk_ida, devidx);
|
ida_free(&mmc_blk_ida, devidx);
|
||||||
return ERR_PTR(ret);
|
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);
|
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);
|
kfree(rpmb);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2719,13 +2721,13 @@ static int mmc_blk_alloc_rpmb_part(struct mmc_card *card,
|
|||||||
struct mmc_rpmb_data *rpmb;
|
struct mmc_rpmb_data *rpmb;
|
||||||
|
|
||||||
/* This creates the minor number for the RPMB char device */
|
/* 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)
|
if (devidx < 0)
|
||||||
return devidx;
|
return devidx;
|
||||||
|
|
||||||
rpmb = kzalloc(sizeof(*rpmb), GFP_KERNEL);
|
rpmb = kzalloc(sizeof(*rpmb), GFP_KERNEL);
|
||||||
if (!rpmb) {
|
if (!rpmb) {
|
||||||
ida_simple_remove(&mmc_rpmb_ida, devidx);
|
ida_free(&mmc_rpmb_ida, devidx);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
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",
|
.name = "mmc",
|
||||||
.dev_groups = mmc_dev_groups,
|
.dev_groups = mmc_dev_groups,
|
||||||
.uevent = mmc_bus_uevent,
|
.uevent = mmc_bus_uevent,
|
||||||
@ -272,7 +272,7 @@ static void mmc_release_card(struct device *dev)
|
|||||||
/*
|
/*
|
||||||
* Allocate and initialise a new MMC card structure.
|
* 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;
|
struct mmc_card *card;
|
||||||
|
|
||||||
|
@ -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)
|
static DEVICE_ATTR(name, S_IRUGO, mmc_##name##_show, NULL)
|
||||||
|
|
||||||
struct mmc_card *mmc_alloc_card(struct mmc_host *host,
|
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);
|
int mmc_add_card(struct mmc_card *card);
|
||||||
void mmc_remove_card(struct mmc_card *card);
|
void mmc_remove_card(struct mmc_card *card);
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ static void mmc_host_classdev_release(struct device *dev)
|
|||||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||||
wakeup_source_unregister(host->ws);
|
wakeup_source_unregister(host->ws);
|
||||||
if (of_alias_get_id(host->parent->of_node, "mmc") < 0)
|
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);
|
kfree(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ static int mmc_host_classdev_shutdown(struct device *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct class mmc_host_class = {
|
static const struct class mmc_host_class = {
|
||||||
.name = "mmc_host",
|
.name = "mmc_host",
|
||||||
.dev_release = mmc_host_classdev_release,
|
.dev_release = mmc_host_classdev_release,
|
||||||
.shutdown_pre = mmc_host_classdev_shutdown,
|
.shutdown_pre = mmc_host_classdev_shutdown,
|
||||||
@ -234,10 +234,8 @@ static void mmc_of_parse_timing_phase(struct device *dev, const char *prop,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
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",
|
mmc_of_parse_timing_phase(dev, "clk-phase-legacy",
|
||||||
&map->phase[MMC_TIMING_LEGACY]);
|
&map->phase[MMC_TIMING_LEGACY]);
|
||||||
mmc_of_parse_timing_phase(dev, "clk-phase-mmc-hs",
|
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();
|
min_idx = mmc_first_nonreserved_index();
|
||||||
max_idx = 0;
|
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) {
|
if (index < 0) {
|
||||||
kfree(host);
|
kfree(host);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -883,7 +883,7 @@ static struct attribute *mmc_std_attrs[] = {
|
|||||||
};
|
};
|
||||||
ATTRIBUTE_GROUPS(mmc_std);
|
ATTRIBUTE_GROUPS(mmc_std);
|
||||||
|
|
||||||
static struct device_type mmc_type = {
|
static const struct device_type mmc_type = {
|
||||||
.groups = mmc_std_groups,
|
.groups = mmc_std_groups,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -353,9 +353,6 @@ static struct gendisk *mmc_alloc_disk(struct mmc_queue *mq,
|
|||||||
if (mmc_can_erase(card))
|
if (mmc_can_erase(card))
|
||||||
mmc_queue_setup_discard(card, &lim);
|
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);
|
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)
|
if (mmc_card_mmc(card) && card->ext_csd.data_sector_size)
|
||||||
|
@ -805,7 +805,7 @@ static const struct attribute_group sd_std_group = {
|
|||||||
};
|
};
|
||||||
__ATTRIBUTE_GROUPS(sd_std);
|
__ATTRIBUTE_GROUPS(sd_std);
|
||||||
|
|
||||||
struct device_type sd_type = {
|
const struct device_type sd_type = {
|
||||||
.groups = sd_std_groups,
|
.groups = sd_std_groups,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
extern struct device_type sd_type;
|
extern const struct device_type sd_type;
|
||||||
|
|
||||||
struct mmc_host;
|
struct mmc_host;
|
||||||
struct mmc_card;
|
struct mmc_card;
|
||||||
|
@ -66,7 +66,7 @@ static struct attribute *sdio_std_attrs[] = {
|
|||||||
};
|
};
|
||||||
ATTRIBUTE_GROUPS(sdio_std);
|
ATTRIBUTE_GROUPS(sdio_std);
|
||||||
|
|
||||||
static struct device_type sdio_type = {
|
static const struct device_type sdio_type = {
|
||||||
.groups = sdio_std_groups,
|
.groups = sdio_std_groups,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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",
|
.name = "sdio",
|
||||||
.dev_groups = sdio_dev_groups,
|
.dev_groups = sdio_dev_groups,
|
||||||
.match = sdio_bus_match,
|
.match = sdio_bus_match,
|
||||||
|
@ -798,6 +798,15 @@ config MMC_DW_HI3798CV200
|
|||||||
Synopsys DesignWare Memory Card Interface driver. Select this option
|
Synopsys DesignWare Memory Card Interface driver. Select this option
|
||||||
for platforms based on HiSilicon Hi3798CV200 SoC.
|
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
|
config MMC_DW_K3
|
||||||
tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
|
tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
|
||||||
depends on MMC_DW
|
depends on MMC_DW
|
||||||
|
@ -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_BLUEFIELD) += dw_mmc-bluefield.o
|
||||||
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
|
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
|
||||||
obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.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_K3) += dw_mmc-k3.o
|
||||||
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
|
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
|
||||||
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o
|
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o
|
||||||
|
@ -180,12 +180,6 @@ struct mmc_davinci_host {
|
|||||||
#define DAVINCI_MMC_DATADIR_WRITE 2
|
#define DAVINCI_MMC_DATADIR_WRITE 2
|
||||||
unsigned char data_dir;
|
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;
|
u32 bytes_left;
|
||||||
|
|
||||||
struct dma_chan *dma_tx;
|
struct dma_chan *dma_tx;
|
||||||
@ -196,8 +190,8 @@ struct mmc_davinci_host {
|
|||||||
bool active_request;
|
bool active_request;
|
||||||
|
|
||||||
/* For PIO we walk scatterlists one segment at a time. */
|
/* For PIO we walk scatterlists one segment at a time. */
|
||||||
|
struct sg_mapping_iter sg_miter;
|
||||||
unsigned int sg_len;
|
unsigned int sg_len;
|
||||||
struct scatterlist *sg;
|
|
||||||
|
|
||||||
/* Version of the MMC/SD controller */
|
/* Version of the MMC/SD controller */
|
||||||
u8 version;
|
u8 version;
|
||||||
@ -213,30 +207,22 @@ struct mmc_davinci_host {
|
|||||||
static irqreturn_t mmc_davinci_irq(int irq, void *dev_id);
|
static irqreturn_t mmc_davinci_irq(int irq, void *dev_id);
|
||||||
|
|
||||||
/* PIO only */
|
/* 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,
|
static void davinci_fifo_data_trans(struct mmc_davinci_host *host,
|
||||||
unsigned int n)
|
unsigned int n)
|
||||||
{
|
{
|
||||||
|
struct sg_mapping_iter *sgm = &host->sg_miter;
|
||||||
u8 *p;
|
u8 *p;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
if (host->buffer_bytes_left == 0) {
|
/*
|
||||||
host->sg = sg_next(host->data->sg);
|
* By adjusting sgm->consumed this will give a pointer to the
|
||||||
mmc_davinci_sg_to_buf(host);
|
* current index into the sgm.
|
||||||
|
*/
|
||||||
|
if (!sg_miter_next(sgm)) {
|
||||||
|
dev_err(mmc_dev(host->mmc), "ran out of sglist prematurely\n");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
p = sgm->addr;
|
||||||
p = host->buffer;
|
|
||||||
if (n > host->buffer_bytes_left)
|
|
||||||
n = host->buffer_bytes_left;
|
|
||||||
host->buffer_bytes_left -= n;
|
|
||||||
host->bytes_left -= n;
|
|
||||||
|
|
||||||
/* NOTE: we never transfer more than rw_threshold bytes
|
/* NOTE: we never transfer more than rw_threshold bytes
|
||||||
* to/from the fifo here; there's no I/O overlap.
|
* 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);
|
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,
|
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 fifo_lev = (rw_threshold == 32) ? MMCFIFOCTL_FIFOLEV : 0;
|
||||||
int timeout;
|
int timeout;
|
||||||
struct mmc_data *data = req->data;
|
struct mmc_data *data = req->data;
|
||||||
|
unsigned int flags = SG_MITER_ATOMIC; /* Used from IRQ */
|
||||||
|
|
||||||
if (host->version == MMC_CTLR_VERSION_2)
|
if (host->version == MMC_CTLR_VERSION_2)
|
||||||
fifo_lev = (rw_threshold == 64) ? MMCFIFOCTL_FIFOLEV : 0;
|
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 */
|
/* Configure the FIFO */
|
||||||
if (data->flags & MMC_DATA_WRITE) {
|
if (data->flags & MMC_DATA_WRITE) {
|
||||||
|
flags |= SG_MITER_FROM_SG;
|
||||||
host->data_dir = DAVINCI_MMC_DATADIR_WRITE;
|
host->data_dir = DAVINCI_MMC_DATADIR_WRITE;
|
||||||
writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR | MMCFIFOCTL_FIFORST,
|
writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR | MMCFIFOCTL_FIFORST,
|
||||||
host->base + DAVINCI_MMCFIFOCTL);
|
host->base + DAVINCI_MMCFIFOCTL);
|
||||||
writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR,
|
writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR,
|
||||||
host->base + DAVINCI_MMCFIFOCTL);
|
host->base + DAVINCI_MMCFIFOCTL);
|
||||||
} else {
|
} else {
|
||||||
|
flags |= SG_MITER_TO_SG;
|
||||||
host->data_dir = DAVINCI_MMC_DATADIR_READ;
|
host->data_dir = DAVINCI_MMC_DATADIR_READ;
|
||||||
writel(fifo_lev | MMCFIFOCTL_FIFODIR_RD | MMCFIFOCTL_FIFORST,
|
writel(fifo_lev | MMCFIFOCTL_FIFODIR_RD | MMCFIFOCTL_FIFORST,
|
||||||
host->base + DAVINCI_MMCFIFOCTL);
|
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->base + DAVINCI_MMCFIFOCTL);
|
||||||
}
|
}
|
||||||
|
|
||||||
host->buffer = NULL;
|
|
||||||
host->bytes_left = data->blocks * data->blksz;
|
host->bytes_left = data->blocks * data->blksz;
|
||||||
|
|
||||||
/* For now we try to use DMA whenever we won't need partial FIFO
|
/* 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 {
|
} else {
|
||||||
/* Revert to CPU Copy */
|
/* Revert to CPU Copy */
|
||||||
host->sg_len = data->sg_len;
|
host->sg_len = data->sg_len;
|
||||||
host->sg = host->data->sg;
|
sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
|
||||||
mmc_davinci_sg_to_buf(host);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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, 1);
|
||||||
mmc_davinci_reset_ctrl(host, 0);
|
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)
|
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) {
|
if (qstatus & MMCST0_DATDNE) {
|
||||||
/* All blocks sent/received, and CRC checks passed */
|
/* All blocks sent/received, and CRC checks passed */
|
||||||
if (data != NULL) {
|
if (data != NULL) {
|
||||||
if ((host->do_dma == 0) && (host->bytes_left > 0)) {
|
if (!host->do_dma) {
|
||||||
/* if datasize < rw_threshold
|
if (host->bytes_left > 0)
|
||||||
* no RX ints are generated
|
/* if datasize < rw_threshold
|
||||||
*/
|
* no RX ints are generated
|
||||||
davinci_fifo_data_trans(host, host->bytes_left);
|
*/
|
||||||
|
davinci_fifo_data_trans(host, host->bytes_left);
|
||||||
|
sg_miter_stop(&host->sg_miter);
|
||||||
}
|
}
|
||||||
end_transfer = 1;
|
end_transfer = 1;
|
||||||
data->bytes_xfered = data->blocks * data->blksz;
|
data->bytes_xfered = data->blocks * data->blksz;
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/mmc.h>
|
#include <linux/mmc/mmc.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_gpio.h>
|
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
@ -201,4 +201,3 @@ module_platform_driver(dw_mci_hi3798cv200_driver);
|
|||||||
|
|
||||||
MODULE_DESCRIPTION("HiSilicon Hi3798CV200 Specific DW-MSHC Driver Extension");
|
MODULE_DESCRIPTION("HiSilicon Hi3798CV200 Specific DW-MSHC Driver Extension");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
MODULE_ALIAS("platform:dwmmc_hi3798cv200");
|
|
||||||
|
251
drivers/mmc/host/dw_mmc-hi3798mv200.c
Normal file
251
drivers/mmc/host/dw_mmc-hi3798mv200.c
Normal 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");
|
@ -35,7 +35,6 @@
|
|||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_gpio.h>
|
|
||||||
#include <linux/mmc/slot-gpio.h>
|
#include <linux/mmc/slot-gpio.h>
|
||||||
|
|
||||||
#include "dw_mmc.h"
|
#include "dw_mmc.h"
|
||||||
|
@ -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,
|
static int meson_mx_sdhc_gate_clk_hw_register(struct device *dev,
|
||||||
const char *name_suffix,
|
const char *name_suffix,
|
||||||
struct clk_hw *parent,
|
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 };
|
struct clk_parent_data parent_data = { .hw = parent };
|
||||||
|
int ret;
|
||||||
|
|
||||||
return meson_mx_sdhc_clk_hw_register(dev, name_suffix, &parent_data, 1,
|
ret = meson_mx_sdhc_clk_hw_register(dev, name_suffix, &parent_data, 1,
|
||||||
&clk_gate_ops, hw);
|
&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,
|
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;
|
clkc_data->mod_clk_en.bit_idx = 15;
|
||||||
ret = meson_mx_sdhc_gate_clk_hw_register(dev, "mod_clk_on",
|
ret = meson_mx_sdhc_gate_clk_hw_register(dev, "mod_clk_on",
|
||||||
&clkc_data->div.hw,
|
&clkc_data->div.hw,
|
||||||
&clkc_data->mod_clk_en.hw);
|
&clkc_data->mod_clk_en.hw,
|
||||||
|
clk_bulk_data, 0);
|
||||||
if (ret)
|
if (ret)
|
||||||
return 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;
|
clkc_data->tx_clk_en.bit_idx = 14;
|
||||||
ret = meson_mx_sdhc_gate_clk_hw_register(dev, "tx_clk_on",
|
ret = meson_mx_sdhc_gate_clk_hw_register(dev, "tx_clk_on",
|
||||||
&clkc_data->div.hw,
|
&clkc_data->div.hw,
|
||||||
&clkc_data->tx_clk_en.hw);
|
&clkc_data->tx_clk_en.hw,
|
||||||
|
clk_bulk_data, 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
return 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;
|
clkc_data->rx_clk_en.bit_idx = 13;
|
||||||
ret = meson_mx_sdhc_gate_clk_hw_register(dev, "rx_clk_on",
|
ret = meson_mx_sdhc_gate_clk_hw_register(dev, "rx_clk_on",
|
||||||
&clkc_data->div.hw,
|
&clkc_data->div.hw,
|
||||||
&clkc_data->rx_clk_en.hw);
|
&clkc_data->rx_clk_en.hw,
|
||||||
|
clk_bulk_data, 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
return 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;
|
clkc_data->sd_clk_en.bit_idx = 12;
|
||||||
ret = meson_mx_sdhc_gate_clk_hw_register(dev, "sd_clk_on",
|
ret = meson_mx_sdhc_gate_clk_hw_register(dev, "sd_clk_on",
|
||||||
&clkc_data->div.hw,
|
&clkc_data->div.hw,
|
||||||
&clkc_data->sd_clk_en.hw);
|
&clkc_data->sd_clk_en.hw,
|
||||||
if (ret)
|
clk_bulk_data, 3);
|
||||||
return 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;
|
|
||||||
}
|
}
|
||||||
|
@ -65,10 +65,8 @@ static const struct regmap_config meson_mx_sdhc_regmap_config = {
|
|||||||
.max_register = MESON_SDHC_CLK2,
|
.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 |
|
regmap_write(host->regmap, MESON_SDHC_SRST, MESON_SDHC_SRST_MAIN_CTRL |
|
||||||
MESON_SDHC_SRST_RXFIFO | MESON_SDHC_SRST_TXFIFO |
|
MESON_SDHC_SRST_RXFIFO | MESON_SDHC_SRST_TXFIFO |
|
||||||
MESON_SDHC_SRST_DPHY_RX | MESON_SDHC_SRST_DPHY_TX |
|
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),
|
dev_warn(mmc_dev(mmc),
|
||||||
"Failed to poll for CMD_BUSY while processing CMD%d\n",
|
"Failed to poll for CMD_BUSY while processing CMD%d\n",
|
||||||
host->cmd->opcode);
|
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,
|
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),
|
dev_warn(mmc_dev(mmc),
|
||||||
"Failed to poll for ESTA[13:11] while processing CMD%d\n",
|
"Failed to poll for ESTA[13:11] while processing CMD%d\n",
|
||||||
host->cmd->opcode);
|
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 = {
|
static const struct mmc_host_ops meson_mx_sdhc_ops = {
|
||||||
.card_hw_reset = meson_mx_sdhc_hw_reset,
|
|
||||||
.request = meson_mx_sdhc_request,
|
.request = meson_mx_sdhc_request,
|
||||||
.set_ios = meson_mx_sdhc_set_ios,
|
.set_ios = meson_mx_sdhc_set_ios,
|
||||||
.card_busy = meson_mx_sdhc_card_busy,
|
.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)
|
if (cmd->error == -EIO || cmd->error == -ETIMEDOUT)
|
||||||
meson_mx_sdhc_hw_reset(host->mmc);
|
meson_mx_sdhc_reset(host);
|
||||||
else if (cmd->data)
|
else if (cmd->data)
|
||||||
/*
|
/*
|
||||||
* Clear the FIFOs after completing data transfers to prevent
|
* 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);
|
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,
|
regmap_write(host->regmap, MESON_SDHC_CTRL,
|
||||||
FIELD_PREP(MESON_SDHC_CTRL_RX_PERIOD, 0xf) |
|
FIELD_PREP(MESON_SDHC_CTRL_RX_PERIOD, 0xf) |
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/bio.h>
|
#include <linux/bio.h>
|
||||||
#include <linux/dma-direction.h>
|
|
||||||
#include <linux/crc7.h>
|
#include <linux/crc7.h>
|
||||||
#include <linux/crc-itu-t.h>
|
#include <linux/crc-itu-t.h>
|
||||||
#include <linux/scatterlist.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.
|
* so we explicitly initialize it to all ones on RX paths.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
mmc_spi_setup_data_message(
|
mmc_spi_setup_data_message(struct mmc_spi_host *host, bool multiple, bool write)
|
||||||
struct mmc_spi_host *host,
|
|
||||||
bool multiple,
|
|
||||||
enum dma_data_direction direction)
|
|
||||||
{
|
{
|
||||||
struct spi_transfer *t;
|
struct spi_transfer *t;
|
||||||
struct scratch *scratch = host->data;
|
struct scratch *scratch = host->data;
|
||||||
@ -523,7 +519,7 @@ mmc_spi_setup_data_message(
|
|||||||
/* for reads, readblock() skips 0xff bytes before finding
|
/* for reads, readblock() skips 0xff bytes before finding
|
||||||
* the token; for writes, this transfer issues that token.
|
* the token; for writes, this transfer issues that token.
|
||||||
*/
|
*/
|
||||||
if (direction == DMA_TO_DEVICE) {
|
if (write) {
|
||||||
t = &host->token;
|
t = &host->token;
|
||||||
memset(t, 0, sizeof(*t));
|
memset(t, 0, sizeof(*t));
|
||||||
t->len = 1;
|
t->len = 1;
|
||||||
@ -547,7 +543,7 @@ mmc_spi_setup_data_message(
|
|||||||
t = &host->crc;
|
t = &host->crc;
|
||||||
memset(t, 0, sizeof(*t));
|
memset(t, 0, sizeof(*t));
|
||||||
t->len = 2;
|
t->len = 2;
|
||||||
if (direction == DMA_TO_DEVICE) {
|
if (write) {
|
||||||
/* the actual CRC may get written later */
|
/* the actual CRC may get written later */
|
||||||
t->tx_buf = &scratch->crc_val;
|
t->tx_buf = &scratch->crc_val;
|
||||||
} else {
|
} else {
|
||||||
@ -570,10 +566,10 @@ mmc_spi_setup_data_message(
|
|||||||
* the next token (next data block, or STOP_TRAN). We can try to
|
* 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.
|
* 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;
|
t = &host->early_status;
|
||||||
memset(t, 0, sizeof(*t));
|
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->tx_buf = host->ones;
|
||||||
t->rx_buf = scratch->status;
|
t->rx_buf = scratch->status;
|
||||||
t->cs_change = 1;
|
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_device *spi = host->spi;
|
||||||
struct spi_transfer *t;
|
struct spi_transfer *t;
|
||||||
enum dma_data_direction direction = mmc_get_dma_dir(data);
|
|
||||||
struct scatterlist *sg;
|
struct scatterlist *sg;
|
||||||
unsigned n_sg;
|
unsigned n_sg;
|
||||||
bool multiple = (data->blocks > 1);
|
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;
|
u32 clock_rate;
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
|
|
||||||
mmc_spi_setup_data_message(host, multiple, direction);
|
mmc_spi_setup_data_message(host, multiple, write);
|
||||||
t = &host->t;
|
t = &host->t;
|
||||||
|
|
||||||
if (t->speed_hz)
|
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 */
|
/* allow pio too; we don't allow highmem */
|
||||||
kmap_addr = kmap(sg_page(sg));
|
kmap_addr = kmap(sg_page(sg));
|
||||||
if (direction == DMA_TO_DEVICE)
|
if (write)
|
||||||
t->tx_buf = kmap_addr + sg->offset;
|
t->tx_buf = kmap_addr + sg->offset;
|
||||||
else
|
else
|
||||||
t->rx_buf = kmap_addr + sg->offset;
|
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);
|
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);
|
status = mmc_spi_writeblock(host, t, timeout);
|
||||||
else
|
else
|
||||||
status = mmc_spi_readblock(host, t, timeout);
|
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 */
|
/* discard mappings */
|
||||||
if (direction == DMA_FROM_DEVICE)
|
if (write)
|
||||||
|
/* nothing to do */;
|
||||||
|
else
|
||||||
flush_dcache_page(sg_page(sg));
|
flush_dcache_page(sg_page(sg));
|
||||||
kunmap(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)
|
* that can affect the STOP_TRAN logic. Complete (and current)
|
||||||
* MMC specs should sort that out before Linux starts using CMD23.
|
* 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;
|
struct scratch *scratch = host->data;
|
||||||
int tmp;
|
int tmp;
|
||||||
const unsigned statlen = sizeof(scratch->status);
|
const unsigned statlen = sizeof(scratch->status);
|
||||||
|
@ -131,12 +131,10 @@ struct moxart_host {
|
|||||||
struct dma_async_tx_descriptor *tx_desc;
|
struct dma_async_tx_descriptor *tx_desc;
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct mmc_request *mrq;
|
struct mmc_request *mrq;
|
||||||
struct scatterlist *cur_sg;
|
|
||||||
struct completion dma_complete;
|
struct completion dma_complete;
|
||||||
struct completion pio_complete;
|
struct completion pio_complete;
|
||||||
|
|
||||||
u32 num_sg;
|
struct sg_mapping_iter sg_miter;
|
||||||
u32 data_remain;
|
|
||||||
u32 data_len;
|
u32 data_len;
|
||||||
u32 fifo_width;
|
u32 fifo_width;
|
||||||
u32 timeout;
|
u32 timeout;
|
||||||
@ -148,35 +146,6 @@ struct moxart_host {
|
|||||||
bool is_removed;
|
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,
|
static int moxart_wait_for_status(struct moxart_host *host,
|
||||||
u32 mask, u32 *status)
|
u32 mask, u32 *status)
|
||||||
{
|
{
|
||||||
@ -254,6 +223,11 @@ static void moxart_dma_complete(void *param)
|
|||||||
complete(&host->dma_complete);
|
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)
|
static void moxart_transfer_dma(struct mmc_data *data, struct moxart_host *host)
|
||||||
{
|
{
|
||||||
u32 len, dir_slave;
|
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);
|
dma_async_issue_pending(dma_chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
data->bytes_xfered += host->data_remain;
|
|
||||||
|
|
||||||
wait_for_completion_interruptible_timeout(&host->dma_complete,
|
wait_for_completion_interruptible_timeout(&host->dma_complete,
|
||||||
host->timeout);
|
host->timeout);
|
||||||
|
|
||||||
|
data->bytes_xfered = host->data_len;
|
||||||
|
|
||||||
dma_unmap_sg(dma_chan->device->dev,
|
dma_unmap_sg(dma_chan->device->dev,
|
||||||
data->sg, data->sg_len,
|
data->sg, data->sg_len,
|
||||||
mmc_get_dma_dir(data));
|
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)
|
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;
|
struct mmc_data *data = host->mrq->cmd->data;
|
||||||
u32 *sgp, len = 0, remain, status;
|
u32 *sgp, len = 0, remain, status;
|
||||||
|
|
||||||
if (host->data_len == data->bytes_xfered)
|
if (host->data_len == data->bytes_xfered)
|
||||||
return;
|
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) {
|
if (data->flags & MMC_DATA_WRITE) {
|
||||||
while (remain > 0) {
|
while (remain > 0) {
|
||||||
@ -326,6 +314,7 @@ static void moxart_transfer_pio(struct moxart_host *host)
|
|||||||
sgp++;
|
sgp++;
|
||||||
len += 4;
|
len += 4;
|
||||||
}
|
}
|
||||||
|
sgm->consumed += len;
|
||||||
remain -= len;
|
remain -= len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,22 +331,22 @@ static void moxart_transfer_pio(struct moxart_host *host)
|
|||||||
sgp++;
|
sgp++;
|
||||||
len += 4;
|
len += 4;
|
||||||
}
|
}
|
||||||
|
sgm->consumed += len;
|
||||||
remain -= len;
|
remain -= len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data->bytes_xfered += host->data_remain - remain;
|
data->bytes_xfered += sgm->consumed;
|
||||||
host->data_remain = remain;
|
if (host->data_len == data->bytes_xfered) {
|
||||||
|
|
||||||
if (host->data_len != data->bytes_xfered)
|
|
||||||
moxart_next_sg(host);
|
|
||||||
else
|
|
||||||
complete(&host->pio_complete);
|
complete(&host->pio_complete);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void moxart_prepare_data(struct moxart_host *host)
|
static void moxart_prepare_data(struct moxart_host *host)
|
||||||
{
|
{
|
||||||
struct mmc_data *data = host->mrq->cmd->data;
|
struct mmc_data *data = host->mrq->cmd->data;
|
||||||
|
unsigned int flags = SG_MITER_ATOMIC; /* Used from IRQ */
|
||||||
u32 datactrl;
|
u32 datactrl;
|
||||||
int blksz_bits;
|
int blksz_bits;
|
||||||
|
|
||||||
@ -368,15 +357,19 @@ static void moxart_prepare_data(struct moxart_host *host)
|
|||||||
blksz_bits = ffs(data->blksz) - 1;
|
blksz_bits = ffs(data->blksz) - 1;
|
||||||
BUG_ON(1 << blksz_bits != data->blksz);
|
BUG_ON(1 << blksz_bits != data->blksz);
|
||||||
|
|
||||||
moxart_init_sg(host, data);
|
|
||||||
|
|
||||||
datactrl = DCR_DATA_EN | (blksz_bits & DCR_BLK_SIZE);
|
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;
|
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;
|
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(DCR_DATA_FIFO_RESET, host->base + REG_DATA_CONTROL);
|
||||||
writel(MASK_DATA | FIFO_URUN | FIFO_ORUN, host->base + REG_CLEAR);
|
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);
|
moxart_send_command(host, host->mrq->cmd);
|
||||||
|
|
||||||
if (mrq->cmd->data) {
|
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);
|
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:
|
request_done:
|
||||||
|
if (!moxart_use_dma(host))
|
||||||
|
sg_miter_stop(&host->sg_miter);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
mmc_request_done(host->mmc, mrq);
|
mmc_request_done(host->mmc, mrq);
|
||||||
}
|
}
|
||||||
|
@ -38,8 +38,9 @@ struct mvsd_host {
|
|||||||
unsigned int xfer_mode;
|
unsigned int xfer_mode;
|
||||||
unsigned int intr_en;
|
unsigned int intr_en;
|
||||||
unsigned int ctrl;
|
unsigned int ctrl;
|
||||||
|
bool use_pio;
|
||||||
|
struct sg_mapping_iter sg_miter;
|
||||||
unsigned int pio_size;
|
unsigned int pio_size;
|
||||||
void *pio_ptr;
|
|
||||||
unsigned int sg_frags;
|
unsigned int sg_frags;
|
||||||
unsigned int ns_per_clk;
|
unsigned int ns_per_clk;
|
||||||
unsigned int clock;
|
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
|
* data when the buffer is not aligned on a 64 byte
|
||||||
* boundary.
|
* 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_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)
|
if (!nodma)
|
||||||
dev_dbg(host->dev, "fallback to PIO for data at 0x%p size %d\n",
|
dev_dbg(host->dev, "fallback to PIO for data\n");
|
||||||
host->pio_ptr, host->pio_size);
|
host->use_pio = true;
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
dma_addr_t phys_addr;
|
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);
|
phys_addr = sg_dma_address(data->sg);
|
||||||
mvsd_write(MVSD_SYS_ADDR_LOW, (u32)phys_addr & 0xffff);
|
mvsd_write(MVSD_SYS_ADDR_LOW, (u32)phys_addr & 0xffff);
|
||||||
mvsd_write(MVSD_SYS_ADDR_HI, (u32)phys_addr >> 16);
|
mvsd_write(MVSD_SYS_ADDR_HI, (u32)phys_addr >> 16);
|
||||||
|
host->use_pio = false;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -288,8 +297,8 @@ static u32 mvsd_finish_data(struct mvsd_host *host, struct mmc_data *data,
|
|||||||
{
|
{
|
||||||
void __iomem *iobase = host->base;
|
void __iomem *iobase = host->base;
|
||||||
|
|
||||||
if (host->pio_ptr) {
|
if (host->use_pio) {
|
||||||
host->pio_ptr = NULL;
|
sg_miter_stop(&host->sg_miter);
|
||||||
host->pio_size = 0;
|
host->pio_size = 0;
|
||||||
} else {
|
} else {
|
||||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_frags,
|
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)
|
static irqreturn_t mvsd_irq(int irq, void *dev)
|
||||||
{
|
{
|
||||||
struct mvsd_host *host = dev;
|
struct mvsd_host *host = dev;
|
||||||
|
struct sg_mapping_iter *sgm = &host->sg_miter;
|
||||||
void __iomem *iobase = host->base;
|
void __iomem *iobase = host->base;
|
||||||
u32 intr_status, intr_done_mask;
|
u32 intr_status, intr_done_mask;
|
||||||
int irq_handled = 0;
|
int irq_handled = 0;
|
||||||
|
u16 *p;
|
||||||
|
int s;
|
||||||
|
|
||||||
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
|
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
|
||||||
dev_dbg(host->dev, "intr 0x%04x intr_en 0x%04x hw_state 0x%04x\n",
|
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);
|
spin_lock(&host->lock);
|
||||||
|
|
||||||
/* PIO handling, if needed. Messy business... */
|
/* 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 &
|
(intr_status & host->intr_en &
|
||||||
(MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W))) {
|
(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)) {
|
while (s >= 32 && (intr_status & MVSD_NOR_RX_FIFO_8W)) {
|
||||||
readsw(iobase + MVSD_FIFO, p, 16);
|
readsw(iobase + MVSD_FIFO, p, 16);
|
||||||
p += 16;
|
p += 16;
|
||||||
s -= 32;
|
s -= 32;
|
||||||
|
sgm->consumed += 32;
|
||||||
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
|
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++);
|
||||||
put_unaligned(mvsd_read(MVSD_FIFO), p++);
|
put_unaligned(mvsd_read(MVSD_FIFO), p++);
|
||||||
s -= 4;
|
s -= 4;
|
||||||
|
sgm->consumed += 4;
|
||||||
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
|
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
|
||||||
}
|
}
|
||||||
if (s && s < 4 && (intr_status & MVSD_NOR_RX_READY)) {
|
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[0] = mvsd_read(MVSD_FIFO);
|
||||||
val[1] = mvsd_read(MVSD_FIFO);
|
val[1] = mvsd_read(MVSD_FIFO);
|
||||||
memcpy(p, ((void *)&val) + 4 - s, s);
|
memcpy(p, ((void *)&val) + 4 - s, s);
|
||||||
|
sgm->consumed += s;
|
||||||
s = 0;
|
s = 0;
|
||||||
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
|
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 &=
|
host->intr_en &=
|
||||||
~(MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W);
|
~(MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W);
|
||||||
mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
|
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",
|
dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n",
|
||||||
s, intr_status, mvsd_read(MVSD_HW_STATE));
|
s, intr_status, mvsd_read(MVSD_HW_STATE));
|
||||||
host->pio_ptr = p;
|
|
||||||
host->pio_size = s;
|
|
||||||
irq_handled = 1;
|
irq_handled = 1;
|
||||||
} else if (host->pio_size &&
|
} else if (host->use_pio &&
|
||||||
(intr_status & host->intr_en &
|
(intr_status & host->intr_en &
|
||||||
(MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W))) {
|
(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
|
* The TX_FIFO_8W bit is unreliable. When set, bursting
|
||||||
* 16 halfwords all at once in the FIFO drops data. Actually
|
* 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++));
|
||||||
mvsd_write(MVSD_FIFO, get_unaligned(p++));
|
mvsd_write(MVSD_FIFO, get_unaligned(p++));
|
||||||
s -= 4;
|
s -= 4;
|
||||||
|
sgm->consumed += 4;
|
||||||
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
|
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
|
||||||
}
|
}
|
||||||
if (s < 4) {
|
if (s < 4) {
|
||||||
@ -439,10 +473,13 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
|
|||||||
memcpy(((void *)&val) + 4 - s, p, s);
|
memcpy(((void *)&val) + 4 - s, p, s);
|
||||||
mvsd_write(MVSD_FIFO, val[0]);
|
mvsd_write(MVSD_FIFO, val[0]);
|
||||||
mvsd_write(MVSD_FIFO, val[1]);
|
mvsd_write(MVSD_FIFO, val[1]);
|
||||||
|
sgm->consumed += s;
|
||||||
s = 0;
|
s = 0;
|
||||||
intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
|
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 &=
|
host->intr_en &=
|
||||||
~(MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W);
|
~(MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W);
|
||||||
mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
|
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",
|
dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n",
|
||||||
s, intr_status, mvsd_read(MVSD_HW_STATE));
|
s, intr_status, mvsd_read(MVSD_HW_STATE));
|
||||||
host->pio_ptr = p;
|
|
||||||
host->pio_size = s;
|
|
||||||
irq_handled = 1;
|
irq_handled = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,11 +266,18 @@ static inline void buffer_swap32(u32 *buf, int len)
|
|||||||
|
|
||||||
static void mxcmci_swap_buffers(struct mmc_data *data)
|
static void mxcmci_swap_buffers(struct mmc_data *data)
|
||||||
{
|
{
|
||||||
struct scatterlist *sg;
|
struct sg_mapping_iter sgm;
|
||||||
int i;
|
u32 *buf;
|
||||||
|
|
||||||
for_each_sg(data->sg, sg, data->sg_len, i)
|
sg_miter_start(&sgm, data->sg, data->sg_len,
|
||||||
buffer_swap32(sg_virt(sg), sg->length);
|
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
|
#else
|
||||||
static inline void mxcmci_swap_buffers(struct mmc_data *data) {}
|
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);
|
} 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;
|
unsigned int stat;
|
||||||
u32 *buf = _buf;
|
|
||||||
|
|
||||||
while (bytes > 3) {
|
while (bytes > 3) {
|
||||||
stat = mxcmci_poll_status(host,
|
stat = mxcmci_poll_status(host,
|
||||||
@ -555,10 +561,9 @@ static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
|
|||||||
return 0;
|
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;
|
unsigned int stat;
|
||||||
u32 *buf = _buf;
|
|
||||||
|
|
||||||
while (bytes > 3) {
|
while (bytes > 3) {
|
||||||
stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
|
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)
|
static int mxcmci_transfer_data(struct mxcmci_host *host)
|
||||||
{
|
{
|
||||||
struct mmc_data *data = host->req->data;
|
struct mmc_data *data = host->req->data;
|
||||||
struct scatterlist *sg;
|
struct sg_mapping_iter sgm;
|
||||||
int stat, i;
|
int stat;
|
||||||
|
u32 *buf;
|
||||||
|
|
||||||
host->data = data;
|
host->data = data;
|
||||||
host->datasize = 0;
|
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) {
|
if (data->flags & MMC_DATA_READ) {
|
||||||
for_each_sg(data->sg, sg, data->sg_len, i) {
|
while (sg_miter_next(&sgm)) {
|
||||||
stat = mxcmci_pull(host, sg_virt(sg), sg->length);
|
buf = sgm.addr;
|
||||||
|
stat = mxcmci_pull(host, buf, sgm.length);
|
||||||
if (stat)
|
if (stat)
|
||||||
return stat;
|
goto transfer_error;
|
||||||
host->datasize += sg->length;
|
host->datasize += sgm.length;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for_each_sg(data->sg, sg, data->sg_len, i) {
|
while (sg_miter_next(&sgm)) {
|
||||||
stat = mxcmci_push(host, sg_virt(sg), sg->length);
|
buf = sgm.addr;
|
||||||
|
stat = mxcmci_push(host, buf, sgm.length);
|
||||||
if (stat)
|
if (stat)
|
||||||
return stat;
|
goto transfer_error;
|
||||||
host->datasize += sg->length;
|
host->datasize += sgm.length;
|
||||||
}
|
}
|
||||||
stat = mxcmci_poll_status(host, STATUS_WRITE_OP_DONE);
|
stat = mxcmci_poll_status(host, STATUS_WRITE_OP_DONE);
|
||||||
if (stat)
|
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)
|
static void mxcmci_datawork(struct work_struct *work)
|
||||||
|
@ -148,10 +148,8 @@ struct mmc_omap_host {
|
|||||||
struct work_struct send_stop_work;
|
struct work_struct send_stop_work;
|
||||||
struct mmc_data *stop_data;
|
struct mmc_data *stop_data;
|
||||||
|
|
||||||
|
struct sg_mapping_iter sg_miter;
|
||||||
unsigned int sg_len;
|
unsigned int sg_len;
|
||||||
int sg_idx;
|
|
||||||
u16 * buffer;
|
|
||||||
u32 buffer_bytes_left;
|
|
||||||
u32 total_bytes_left;
|
u32 total_bytes_left;
|
||||||
|
|
||||||
unsigned features;
|
unsigned features;
|
||||||
@ -456,6 +454,8 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
|
|||||||
{
|
{
|
||||||
if (host->dma_in_use)
|
if (host->dma_in_use)
|
||||||
mmc_omap_release_dma(host, data, data->error);
|
mmc_omap_release_dma(host, data, data->error);
|
||||||
|
else
|
||||||
|
sg_miter_stop(&host->sg_miter);
|
||||||
|
|
||||||
host->data = NULL;
|
host->data = NULL;
|
||||||
host->sg_len = 0;
|
host->sg_len = 0;
|
||||||
@ -651,19 +651,6 @@ mmc_omap_cmd_timer(struct timer_list *t)
|
|||||||
spin_unlock_irqrestore(&host->slot_lock, flags);
|
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
|
static void
|
||||||
mmc_omap_clk_timer(struct timer_list *t)
|
mmc_omap_clk_timer(struct timer_list *t)
|
||||||
{
|
{
|
||||||
@ -676,33 +663,37 @@ mmc_omap_clk_timer(struct timer_list *t)
|
|||||||
static void
|
static void
|
||||||
mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
|
mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
|
||||||
{
|
{
|
||||||
|
struct sg_mapping_iter *sgm = &host->sg_miter;
|
||||||
int n, nwords;
|
int n, nwords;
|
||||||
|
u16 *buffer;
|
||||||
|
|
||||||
if (host->buffer_bytes_left == 0) {
|
if (!sg_miter_next(sgm)) {
|
||||||
host->sg_idx++;
|
/* This should not happen */
|
||||||
BUG_ON(host->sg_idx == host->sg_len);
|
dev_err(mmc_dev(host->mmc), "ran out of scatterlist prematurely\n");
|
||||||
mmc_omap_sg_to_buf(host);
|
return;
|
||||||
}
|
}
|
||||||
|
buffer = sgm->addr;
|
||||||
|
|
||||||
n = 64;
|
n = 64;
|
||||||
if (n > host->buffer_bytes_left)
|
if (n > sgm->length)
|
||||||
n = host->buffer_bytes_left;
|
n = sgm->length;
|
||||||
|
if (n > host->total_bytes_left)
|
||||||
|
n = host->total_bytes_left;
|
||||||
|
|
||||||
/* Round up to handle odd number of bytes to transfer */
|
/* Round up to handle odd number of bytes to transfer */
|
||||||
nwords = DIV_ROUND_UP(n, 2);
|
nwords = DIV_ROUND_UP(n, 2);
|
||||||
|
|
||||||
host->buffer_bytes_left -= n;
|
sgm->consumed = n;
|
||||||
host->total_bytes_left -= n;
|
host->total_bytes_left -= n;
|
||||||
host->data->bytes_xfered += n;
|
host->data->bytes_xfered += n;
|
||||||
|
|
||||||
if (write) {
|
if (write) {
|
||||||
__raw_writesw(host->virt_base + OMAP_MMC_REG(host, DATA),
|
__raw_writesw(host->virt_base + OMAP_MMC_REG(host, DATA),
|
||||||
host->buffer, nwords);
|
buffer, nwords);
|
||||||
} else {
|
} else {
|
||||||
__raw_readsw(host->virt_base + OMAP_MMC_REG(host, DATA),
|
__raw_readsw(host->virt_base + OMAP_MMC_REG(host, DATA),
|
||||||
host->buffer, nwords);
|
buffer, nwords);
|
||||||
}
|
}
|
||||||
|
|
||||||
host->buffer += nwords;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_MMC_DEBUG
|
#ifdef CONFIG_MMC_DEBUG
|
||||||
@ -956,6 +947,7 @@ static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_reque
|
|||||||
static void
|
static void
|
||||||
mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
|
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;
|
struct mmc_data *data = req->data;
|
||||||
int i, use_dma = 1, block_size;
|
int i, use_dma = 1, block_size;
|
||||||
struct scatterlist *sg;
|
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) {
|
if (use_dma) {
|
||||||
enum dma_data_direction dma_data_dir;
|
enum dma_data_direction dma_data_dir;
|
||||||
struct dma_async_tx_descriptor *tx;
|
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);
|
OMAP_MMC_WRITE(host, BUF, 0x1f1f);
|
||||||
host->total_bytes_left = data->blocks * block_size;
|
host->total_bytes_left = data->blocks * block_size;
|
||||||
host->sg_len = sg_len;
|
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;
|
host->dma_in_use = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#ifndef RENESAS_SDHI_H
|
#ifndef RENESAS_SDHI_H
|
||||||
#define RENESAS_SDHI_H
|
#define RENESAS_SDHI_H
|
||||||
|
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include "tmio_mmc.h"
|
#include "tmio_mmc.h"
|
||||||
|
|
||||||
@ -63,7 +64,7 @@ struct renesas_sdhi_of_data_with_quirks {
|
|||||||
struct renesas_sdhi_dma {
|
struct renesas_sdhi_dma {
|
||||||
unsigned long end_flags;
|
unsigned long end_flags;
|
||||||
enum dma_slave_buswidth dma_buswidth;
|
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);
|
void (*enable)(struct tmio_mmc_host *host, bool enable);
|
||||||
struct completion dma_dataend;
|
struct completion dma_dataend;
|
||||||
struct tasklet_struct dma_complete;
|
struct tasklet_struct dma_complete;
|
||||||
|
@ -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,
|
static void esdhc_mcf_request_done(struct sdhci_host *host,
|
||||||
struct mmc_request *mrq)
|
struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
struct scatterlist *sg;
|
struct sg_mapping_iter sgm;
|
||||||
u32 *buffer;
|
u32 *buffer;
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!mrq->data || !mrq->data->bytes_xfered)
|
if (!mrq->data || !mrq->data->bytes_xfered)
|
||||||
goto exit_done;
|
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
|
* On mcf5441x there is no hw sdma option/flag to select the dma
|
||||||
* transfer endiannes. A swap after the transfer is needed.
|
* transfer endiannes. A swap after the transfer is needed.
|
||||||
*/
|
*/
|
||||||
for_each_sg(mrq->data->sg, sg, mrq->data->sg_len, i) {
|
sg_miter_start(&sgm, mrq->data->sg, mrq->data->sg_len,
|
||||||
buffer = (u32 *)sg_virt(sg);
|
SG_MITER_ATOMIC | SG_MITER_TO_SG | SG_MITER_FROM_SG);
|
||||||
esdhc_mcf_buffer_swap32(buffer, sg->length);
|
while (sg_miter_next(&sgm)) {
|
||||||
|
buffer = sgm.addr;
|
||||||
|
esdhc_mcf_buffer_swap32(buffer, sgm.length);
|
||||||
}
|
}
|
||||||
|
sg_miter_stop(&sgm);
|
||||||
|
|
||||||
exit_done:
|
exit_done:
|
||||||
mmc_request_done(host->mmc, mrq);
|
mmc_request_done(host->mmc, mrq);
|
||||||
|
@ -435,7 +435,7 @@ static int aspeed_sdhci_probe(struct platform_device *pdev)
|
|||||||
goto err_sdhci_add;
|
goto err_sdhci_add;
|
||||||
|
|
||||||
if (dev->phase_desc)
|
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);
|
ret = sdhci_add_host(host);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -52,6 +52,20 @@
|
|||||||
#define AT_CTRL_SWIN_TH_VAL_MASK GENMASK(31, 24) /* bits [31:24] */
|
#define AT_CTRL_SWIN_TH_VAL_MASK GENMASK(31, 24) /* bits [31:24] */
|
||||||
#define AT_CTRL_SWIN_TH_VAL 0x9 /* sampling window threshold */
|
#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 */
|
/* Rockchip specific Registers */
|
||||||
#define DWCMSHC_EMMC_DLL_CTRL 0x800
|
#define DWCMSHC_EMMC_DLL_CTRL 0x800
|
||||||
#define DWCMSHC_EMMC_DLL_RXCLK 0x804
|
#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 = {
|
static const struct sdhci_ops sdhci_dwcmshc_ops = {
|
||||||
.set_clock = sdhci_set_clock,
|
.set_clock = sdhci_set_clock,
|
||||||
.set_bus_width = sdhci_set_bus_width,
|
.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,
|
.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 = {
|
static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
|
||||||
.ops = &sdhci_dwcmshc_ops,
|
.ops = &sdhci_dwcmshc_ops,
|
||||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
.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,
|
.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)
|
static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
@ -768,6 +826,14 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
|
|||||||
.compatible = "snps,dwcmshc-sdhci",
|
.compatible = "snps,dwcmshc-sdhci",
|
||||||
.data = &sdhci_dwcmshc_pdata,
|
.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",
|
.compatible = "thead,th1520-dwcmshc",
|
||||||
.data = &sdhci_dwcmshc_th1520_pdata,
|
.data = &sdhci_dwcmshc_th1520_pdata,
|
||||||
|
@ -227,14 +227,12 @@ struct sh_mmcif_host {
|
|||||||
bool dying;
|
bool dying;
|
||||||
long timeout;
|
long timeout;
|
||||||
void __iomem *addr;
|
void __iomem *addr;
|
||||||
u32 *pio_ptr;
|
|
||||||
spinlock_t lock; /* protect sh_mmcif_host::state */
|
spinlock_t lock; /* protect sh_mmcif_host::state */
|
||||||
enum sh_mmcif_state state;
|
enum sh_mmcif_state state;
|
||||||
enum sh_mmcif_wait_for wait_for;
|
enum sh_mmcif_wait_for wait_for;
|
||||||
struct delayed_work timeout_work;
|
struct delayed_work timeout_work;
|
||||||
size_t blocksize;
|
size_t blocksize;
|
||||||
int sg_idx;
|
struct sg_mapping_iter sg_miter;
|
||||||
int sg_blkidx;
|
|
||||||
bool power;
|
bool power;
|
||||||
bool ccs_enable; /* Command Completion Signal support */
|
bool ccs_enable; /* Command Completion Signal support */
|
||||||
bool clk_ctrl2_enable;
|
bool clk_ctrl2_enable;
|
||||||
@ -600,32 +598,17 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
|
|||||||
return ret;
|
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,
|
static void sh_mmcif_single_read(struct sh_mmcif_host *host,
|
||||||
struct mmc_request *mrq)
|
struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
|
struct mmc_data *data = mrq->data;
|
||||||
|
|
||||||
host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
|
host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
|
||||||
BLOCK_SIZE_MASK) + 3;
|
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;
|
host->wait_for = MMCIF_WAIT_FOR_READ;
|
||||||
|
|
||||||
/* buf read enable */
|
/* 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)
|
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 device *dev = sh_mmcif_host_to_dev(host);
|
||||||
struct mmc_data *data = host->mrq->data;
|
struct mmc_data *data = host->mrq->data;
|
||||||
u32 *p = sg_virt(data->sg);
|
u32 *p;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (host->sd_error) {
|
if (host->sd_error) {
|
||||||
|
sg_miter_stop(sgm);
|
||||||
data->error = sh_mmcif_error_manage(host);
|
data->error = sh_mmcif_error_manage(host);
|
||||||
dev_dbg(dev, "%s(): %d\n", __func__, data->error);
|
dev_dbg(dev, "%s(): %d\n", __func__, data->error);
|
||||||
return false;
|
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++)
|
for (i = 0; i < host->blocksize / 4; i++)
|
||||||
*p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA);
|
*p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA);
|
||||||
|
|
||||||
|
sg_miter_stop(&host->sg_miter);
|
||||||
|
|
||||||
/* buffer read end */
|
/* buffer read end */
|
||||||
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFRE);
|
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFRE);
|
||||||
host->wait_for = MMCIF_WAIT_FOR_READ_END;
|
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,
|
static void sh_mmcif_multi_read(struct sh_mmcif_host *host,
|
||||||
struct mmc_request *mrq)
|
struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
|
struct sg_mapping_iter *sgm = &host->sg_miter;
|
||||||
struct mmc_data *data = mrq->data;
|
struct mmc_data *data = mrq->data;
|
||||||
|
|
||||||
if (!data->sg_len || !data->sg->length)
|
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) &
|
host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
|
||||||
BLOCK_SIZE_MASK;
|
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->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);
|
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sh_mmcif_mread_block(struct sh_mmcif_host *host)
|
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 device *dev = sh_mmcif_host_to_dev(host);
|
||||||
struct mmc_data *data = host->mrq->data;
|
struct mmc_data *data = host->mrq->data;
|
||||||
u32 *p = host->pio_ptr;
|
u32 *p;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (host->sd_error) {
|
if (host->sd_error) {
|
||||||
|
sg_miter_stop(sgm);
|
||||||
data->error = sh_mmcif_error_manage(host);
|
data->error = sh_mmcif_error_manage(host);
|
||||||
dev_dbg(dev, "%s(): %d\n", __func__, data->error);
|
dev_dbg(dev, "%s(): %d\n", __func__, data->error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
BUG_ON(!data->sg->length);
|
p = sgm->addr;
|
||||||
|
|
||||||
for (i = 0; i < host->blocksize / 4; i++)
|
for (i = 0; i < host->blocksize / 4; i++)
|
||||||
*p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA);
|
*p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA);
|
||||||
|
|
||||||
if (!sh_mmcif_next_block(host, p))
|
sgm->consumed = host->blocksize;
|
||||||
return false;
|
|
||||||
|
|
||||||
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
|
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
|
||||||
|
|
||||||
|
if (!sg_miter_next(sgm)) {
|
||||||
|
sg_miter_stop(sgm);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sh_mmcif_single_write(struct sh_mmcif_host *host,
|
static void sh_mmcif_single_write(struct sh_mmcif_host *host,
|
||||||
struct mmc_request *mrq)
|
struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
|
struct mmc_data *data = mrq->data;
|
||||||
|
|
||||||
host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
|
host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
|
||||||
BLOCK_SIZE_MASK) + 3;
|
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;
|
host->wait_for = MMCIF_WAIT_FOR_WRITE;
|
||||||
|
|
||||||
/* buf write enable */
|
/* 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)
|
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 device *dev = sh_mmcif_host_to_dev(host);
|
||||||
struct mmc_data *data = host->mrq->data;
|
struct mmc_data *data = host->mrq->data;
|
||||||
u32 *p = sg_virt(data->sg);
|
u32 *p;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (host->sd_error) {
|
if (host->sd_error) {
|
||||||
|
sg_miter_stop(sgm);
|
||||||
data->error = sh_mmcif_error_manage(host);
|
data->error = sh_mmcif_error_manage(host);
|
||||||
dev_dbg(dev, "%s(): %d\n", __func__, data->error);
|
dev_dbg(dev, "%s(): %d\n", __func__, data->error);
|
||||||
return false;
|
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++)
|
for (i = 0; i < host->blocksize / 4; i++)
|
||||||
sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++);
|
sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++);
|
||||||
|
|
||||||
|
sg_miter_stop(&host->sg_miter);
|
||||||
|
|
||||||
/* buffer write end */
|
/* buffer write end */
|
||||||
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE);
|
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE);
|
||||||
host->wait_for = MMCIF_WAIT_FOR_WRITE_END;
|
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,
|
static void sh_mmcif_multi_write(struct sh_mmcif_host *host,
|
||||||
struct mmc_request *mrq)
|
struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
|
struct sg_mapping_iter *sgm = &host->sg_miter;
|
||||||
struct mmc_data *data = mrq->data;
|
struct mmc_data *data = mrq->data;
|
||||||
|
|
||||||
if (!data->sg_len || !data->sg->length)
|
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) &
|
host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
|
||||||
BLOCK_SIZE_MASK;
|
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->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);
|
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host)
|
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 device *dev = sh_mmcif_host_to_dev(host);
|
||||||
struct mmc_data *data = host->mrq->data;
|
struct mmc_data *data = host->mrq->data;
|
||||||
u32 *p = host->pio_ptr;
|
u32 *p;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (host->sd_error) {
|
if (host->sd_error) {
|
||||||
|
sg_miter_stop(sgm);
|
||||||
data->error = sh_mmcif_error_manage(host);
|
data->error = sh_mmcif_error_manage(host);
|
||||||
dev_dbg(dev, "%s(): %d\n", __func__, data->error);
|
dev_dbg(dev, "%s(): %d\n", __func__, data->error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
BUG_ON(!data->sg->length);
|
p = sgm->addr;
|
||||||
|
|
||||||
for (i = 0; i < host->blocksize / 4; i++)
|
for (i = 0; i < host->blocksize / 4; i++)
|
||||||
sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++);
|
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;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
|
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
|
||||||
|
|
||||||
|
@ -259,6 +259,8 @@ static void tmio_mmc_reset_work(struct work_struct *work)
|
|||||||
else
|
else
|
||||||
mrq->cmd->error = -ETIMEDOUT;
|
mrq->cmd->error = -ETIMEDOUT;
|
||||||
|
|
||||||
|
/* No new calls yet, but disallow concurrent tmio_mmc_done_work() */
|
||||||
|
host->mrq = ERR_PTR(-EBUSY);
|
||||||
host->cmd = NULL;
|
host->cmd = NULL;
|
||||||
host->data = NULL;
|
host->data = NULL;
|
||||||
|
|
||||||
@ -970,6 +972,7 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Disallow new mrqs and work handlers to run */
|
||||||
host->mrq = ERR_PTR(-EBUSY);
|
host->mrq = ERR_PTR(-EBUSY);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
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",
|
"%s.%d: IOS interrupted: clk %u, mode %u",
|
||||||
current->comm, task_pid_nr(current),
|
current->comm, task_pid_nr(current),
|
||||||
ios->clock, ios->power_mode);
|
ios->clock, ios->power_mode);
|
||||||
host->mrq = NULL;
|
|
||||||
|
|
||||||
|
/* Ready for new mrqs */
|
||||||
|
host->mrq = NULL;
|
||||||
host->clk_cache = ios->clock;
|
host->clk_cache = ios->clock;
|
||||||
|
|
||||||
mutex_unlock(&host->ios_lock);
|
mutex_unlock(&host->ios_lock);
|
||||||
|
@ -1284,8 +1284,6 @@ static int wbsd_scan(struct wbsd_host *host)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) {
|
for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) {
|
||||||
id = 0xFFFF;
|
|
||||||
|
|
||||||
host->config = config_ports[i];
|
host->config = config_ports[i];
|
||||||
host->unlock_code = unlock_codes[j];
|
host->unlock_code = unlock_codes[j];
|
||||||
|
|
||||||
|
@ -883,7 +883,6 @@ static void wmt_mci_remove(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct wmt_mci_priv *priv;
|
struct wmt_mci_priv *priv;
|
||||||
struct resource *res;
|
|
||||||
u32 reg_tmp;
|
u32 reg_tmp;
|
||||||
|
|
||||||
mmc = platform_get_drvdata(pdev);
|
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_disable_unprepare(priv->clk_sdmmc);
|
||||||
clk_put(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);
|
mmc_free_host(mmc);
|
||||||
|
|
||||||
dev_info(&pdev->dev, "WMT MCI device removed\n");
|
dev_info(&pdev->dev, "WMT MCI device removed\n");
|
||||||
|
@ -539,7 +539,7 @@ struct mmc_host *devm_mmc_alloc_host(struct device *dev, int extra);
|
|||||||
int mmc_add_host(struct mmc_host *);
|
int mmc_add_host(struct mmc_host *);
|
||||||
void mmc_remove_host(struct mmc_host *);
|
void mmc_remove_host(struct mmc_host *);
|
||||||
void mmc_free_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);
|
struct mmc_clk_phase_map *map);
|
||||||
int mmc_of_parse(struct mmc_host *host);
|
int mmc_of_parse(struct mmc_host *host);
|
||||||
int mmc_of_parse_voltage(struct mmc_host *host, u32 *mask);
|
int mmc_of_parse_voltage(struct mmc_host *host, u32 *mask);
|
||||||
|
Loading…
Reference in New Issue
Block a user