MMC host:

- Convert from using tasklet to the BH workqueue
  - dw_mmc-bluefield: Add support for eMMC HW reset
  - mmc_spi: Allow spi controllers incapable of lower than 400kHz
  - sdhci: Rework code to eliminate SDHCI_QUIRK_UNSTABLE_RO_DETECT
  - sdhci-brcmstb: Add support for the BCM2712 variant
  - sdhci-esdhc-imx: Disable card-detect as system wakeup on S32G platforms
  - sdhci-msm: Add support for the SDX75 variant
  - sdhci-of-dwcmshc: Enable CQE support for some Rockchip variants
  - sdhci-of-esdhc: Convert DT-bindings to yaml
  - sdhci-sprd: Convert DT-bindings to yaml
 
 MEMSTICK:
  - rtsx_pci_ms: Remove the unused Realtek PCI memstick driver
 -----BEGIN PGP SIGNATURE-----
 
 iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmaU/CMXHHVsZi5oYW5z
 c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCnXrxAA0qSVIuFN/BwEGKMke0BTmzRS
 KX4qFLqi+IfOyKfFqD0+trE2LJ2oh7CnfV/FSmW8+45rlsaoTVVRZPyrllYuwDC0
 OC3PeWBpzgYvmlilqHxsgl9McWG1F4mItgBvo0MxQFy26pwFiiQY3dBHjVchVwoZ
 VUq83rZr+cCgkriMrT31k2K0wwODTVEDxdKbmSwBSbYozPLyJkFediP64KH4sTF2
 gDe1mM++ZQgn6IC+sDpia6SH0WFnZKuUZlf08WU4l2TjgcNVAeIxsllh6nYZSgj8
 +nxEuICXyO8qYleCRBWb9s5EgNUl1P0dZmfDviHs+4ORbrie+4lngoDdqVraXz4g
 uy2QFYjZqfptmt4rJpUwtp2HbbX5eslV6rfS1uSkoYzcsTjQNv74Fgniek1qSXGh
 9aF7U1lliwWs0W/Aquom3vw+6edFrTjJ3TC/la5dZwkx4qRndAVFpFiyvxKCp5yZ
 FtpjUn7s80CAhxVTCVgdl4jCRgwZzG0j+t5/YrSwEw1h8vWYY/WwioyFDjoxkY+N
 gv63vr+CfzKCcCyWN3ROaYnSVn9GDQkwaX2zJJTi+7ozRj6t09/Jp+Yi5GuXpn3/
 Zktk35dSxCIF/DL8C1qwV1WWkOLQCLY7JGSWF41VDMkHMcw4+Xkp5YeynuktFEVt
 33AfOn/ul139Xd1MrXg=
 =0Oa4
 -----END PGP SIGNATURE-----

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

Pull MMC updates from Ulf Hansson:
 "MMC host:
   - Convert from using tasklet to the BH workqueue
   - dw_mmc-bluefield: Add support for eMMC HW reset
   - mmc_spi: Allow spi controllers incapable of lower than 400kHz
   - sdhci: Rework code to eliminate SDHCI_QUIRK_UNSTABLE_RO_DETECT
   - sdhci-brcmstb: Add support for the BCM2712 variant
   - sdhci-esdhc-imx: Disable card-detect as system wakeup on S32G platforms
   - sdhci-msm: Add support for the SDX75 variant
   - sdhci-of-dwcmshc: Enable CQE support for some Rockchip variants
   - sdhci-of-esdhc: Convert DT-bindings to yaml
   - sdhci-sprd: Convert DT-bindings to yaml

  MEMSTICK:
   - rtsx_pci_ms: Remove the unused Realtek PCI memstick driver"

* tag 'mmc-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (26 commits)
  MAINTAINERS: add 's32@nxp.com' as relevant mailing list for 'sdhci-esdhc-imx' driver
  mmc: sdhci-esdhc-imx: obtain the 'per' clock rate after its enablement
  mmc: sdhci-esdhc-imx: disable card detect wake for S32G based platforms
  dt-bindings: mmc: sdhci-sprd: convert to YAML
  mmc: davinci_mmc: report all possible bus widths
  mmc: dw_mmc-bluefield: Add support for eMMC HW reset
  mmc: dw_mmc: Add support for platform specific eMMC HW reset
  mmc: sdhci_am654: Constify struct regmap_config
  mmc: Convert from tasklet to BH workqueue
  mmc: sdhi: Convert from tasklet to BH workqueue
  mmc: mmc_spi: allow for spi controllers incapable of getting as low as 400k
  memstick: rtsx_pci_ms: Remove Realtek PCI memstick driver
  MAINTAINERS: drop entry for VIA SD/MMC controller
  mmc: tmio: Remove obsolete .set_pwr() callback()
  mfd: tmio: Remove obsolete .set_clk_div() callback
  mmc: sdhci-brcmstb: Add ARCH_BCM2835 option
  mmc: sdhci: Eliminate SDHCI_QUIRK_UNSTABLE_RO_DETECT
  dt-bindings: mmc: Convert fsl-esdhc.txt to yaml
  dt-bindings: mmc: mmc-spi-slot: Change voltage-ranges to uint32-matrix
  mmc: add missing MODULE_DESCRIPTION() macros
  ...
This commit is contained in:
Linus Torvalds 2024-07-15 17:48:12 -07:00
commit 3f32ab146c
50 changed files with 556 additions and 993 deletions

View File

@ -23,6 +23,12 @@ properties:
- raspberrypi,4-model-b
- const: brcm,bcm2711
- description: BCM2712 based Boards
items:
- enum:
- raspberrypi,5-model-b
- const: brcm,bcm2712
- description: BCM2835 based Boards
items:
- enum:

View File

@ -51,6 +51,9 @@ properties:
set when controller's internal DMA engine cannot access the DRAM memory,
like on the G12A dedicated SDIO controller.
power-domains:
maxItems: 1
required:
- compatible
- reg

View File

@ -20,6 +20,7 @@ properties:
- const: brcm,sdhci-brcmstb
- items:
- enum:
- brcm,bcm2712-sdhci
- brcm,bcm74165b0-sdhci
- brcm,bcm7445-sdhci
- brcm,bcm7425-sdhci

View File

@ -0,0 +1,105 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mmc/fsl,esdhc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Freescale Enhanced Secure Digital Host Controller (eSDHC)
description:
The Enhanced Secure Digital Host Controller provides an interface
for MMC, SD, and SDIO types of memory cards.
maintainers:
- Frank Li <Frank.Li@nxp.com>
properties:
compatible:
items:
- enum:
- fsl,mpc8536-esdhc
- fsl,mpc8378-esdhc
- fsl,p2020-esdhc
- fsl,p4080-esdhc
- fsl,t1040-esdhc
- fsl,t4240-esdhc
- fsl,ls1012a-esdhc
- fsl,ls1028a-esdhc
- fsl,ls1088a-esdhc
- fsl,ls1043a-esdhc
- fsl,ls1046a-esdhc
- fsl,ls2080a-esdhc
- const: fsl,esdhc
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
clock-frequency:
$ref: /schemas/types.yaml#/definitions/uint32
description: specifies eSDHC base clock frequency.
sdhci,wp-inverted:
$ref: /schemas/types.yaml#/definitions/flag
deprecated: true
description:
specifies that eSDHC controller reports
inverted write-protect state; New devices should use the generic
"wp-inverted" property.
sdhci,1-bit-only:
$ref: /schemas/types.yaml#/definitions/flag
deprecated: true
description:
specifies that a controller can only handle
1-bit data transfers. New devices should use the generic
"bus-width = <1>" property.
sdhci,auto-cmd12:
$ref: /schemas/types.yaml#/definitions/flag
description:
specifies that a controller can only handle auto CMD12.
voltage-ranges:
$ref: /schemas/types.yaml#/definitions/uint32-matrix
items:
items:
- description: specifies minimum slot voltage (mV).
- description: specifies maximum slot voltage (mV).
minItems: 1
maxItems: 8
dma-coherent: true
little-endian:
$ref: /schemas/types.yaml#/definitions/flag
description:
If the host controller is little-endian mode, specify
this property. The default endian mode is big-endian.
required:
- compatible
- reg
- interrupts
allOf:
- $ref: sdhci-common.yaml#
unevaluatedProperties: false
examples:
- |
mmc@2e000 {
compatible = "fsl,mpc8378-esdhc", "fsl,esdhc";
reg = <0x2e000 0x1000>;
interrupts = <42 0x8>;
interrupt-parent = <&ipic>;
/* Filled in by U-Boot */
clock-frequency = <100000000>;
voltage-ranges = <3300 3300>;
};

View File

@ -1,52 +0,0 @@
* Freescale Enhanced Secure Digital Host Controller (eSDHC)
The Enhanced Secure Digital Host Controller provides an interface
for MMC, SD, and SDIO types of memory cards.
This file documents differences between the core properties described
by mmc.txt and the properties used by the sdhci-esdhc driver.
Required properties:
- compatible : should be "fsl,esdhc", or "fsl,<chip>-esdhc".
Possible compatibles for PowerPC:
"fsl,mpc8536-esdhc"
"fsl,mpc8378-esdhc"
"fsl,p2020-esdhc"
"fsl,p4080-esdhc"
"fsl,t1040-esdhc"
"fsl,t4240-esdhc"
Possible compatibles for ARM:
"fsl,ls1012a-esdhc"
"fsl,ls1028a-esdhc"
"fsl,ls1088a-esdhc"
"fsl,ls1043a-esdhc"
"fsl,ls1046a-esdhc"
"fsl,ls2080a-esdhc"
- clock-frequency : specifies eSDHC base clock frequency.
Optional properties:
- sdhci,wp-inverted : specifies that eSDHC controller reports
inverted write-protect state; New devices should use the generic
"wp-inverted" property.
- sdhci,1-bit-only : specifies that a controller can only handle
1-bit data transfers. New devices should use the generic
"bus-width = <1>" property.
- sdhci,auto-cmd12: specifies that a controller can only handle auto
CMD12.
- voltage-ranges : two cells are required, first cell specifies minimum
slot voltage (mV), second cell specifies maximum slot voltage (mV).
Several ranges could be specified.
- little-endian : If the host controller is little-endian mode, specify
this property. The default endian mode is big-endian.
Example:
sdhci@2e000 {
compatible = "fsl,mpc8378-esdhc", "fsl,esdhc";
reg = <0x2e000 0x1000>;
interrupts = <42 0x8>;
interrupt-parent = <&ipic>;
/* Filled in by U-Boot */
clock-frequency = <0>;
voltage-ranges = <3300 3300>;
};

View File

@ -27,17 +27,19 @@ properties:
maxItems: 1
voltage-ranges:
$ref: /schemas/types.yaml#/definitions/uint32-array
$ref: /schemas/types.yaml#/definitions/uint32-matrix
description: |
Two cells are required, first cell specifies minimum slot voltage (mV),
second cell specifies maximum slot voltage (mV).
items:
- description: |
value for minimum slot voltage in mV
default: 3200
- description: |
value for maximum slot voltage in mV
default: 3400
items:
- description: |
value for minimum slot voltage in mV
default: 3200
- description: |
value for maximum slot voltage in mV
default: 3400
maxItems: 1
gpios:
description: |

View File

@ -51,6 +51,7 @@ properties:
- qcom,sdm845-sdhci
- qcom,sdx55-sdhci
- qcom,sdx65-sdhci
- qcom,sdx75-sdhci
- qcom,sm6115-sdhci
- qcom,sm6125-sdhci
- qcom,sm6350-sdhci

View File

@ -1,67 +0,0 @@
* Spreadtrum SDHCI controller (sdhci-sprd)
The Secure Digital (SD) Host controller on Spreadtrum SoCs provides an interface
for MMC, SD and SDIO types of cards.
This file documents differences between the core properties in mmc.txt
and the properties used by the sdhci-sprd driver.
Required properties:
- compatible: Should contain "sprd,sdhci-r11".
- reg: physical base address of the controller and length.
- interrupts: Interrupts used by the SDHCI controller.
- clocks: Should contain phandle for the clock feeding the SDHCI controller
- clock-names: Should contain the following:
"sdio" - SDIO source clock (required)
"enable" - gate clock which used for enabling/disabling the device (required)
"2x_enable" - gate clock controlling the device for some special platforms (optional)
Optional properties:
- assigned-clocks: the same with "sdio" clock
- assigned-clock-parents: the default parent of "sdio" clock
- pinctrl-names: should be "default", "state_uhs"
- pinctrl-0: should contain default/high speed pin control
- pinctrl-1: should contain uhs mode pin control
PHY DLL delays are used to delay the data valid window, and align the window
to sampling clock. PHY DLL delays can be configured by following properties,
and each property contains 4 cells which are used to configure the clock data
write line delay value, clock read command line delay value, clock read data
positive edge delay value and clock read data negative edge delay value.
Each cell's delay value unit is cycle of the PHY clock.
- sprd,phy-delay-legacy: Delay value for legacy timing.
- sprd,phy-delay-sd-highspeed: Delay value for SD high-speed timing.
- sprd,phy-delay-sd-uhs-sdr50: Delay value for SD UHS SDR50 timing.
- sprd,phy-delay-sd-uhs-sdr104: Delay value for SD UHS SDR50 timing.
- sprd,phy-delay-mmc-highspeed: Delay value for MMC high-speed timing.
- sprd,phy-delay-mmc-ddr52: Delay value for MMC DDR52 timing.
- sprd,phy-delay-mmc-hs200: Delay value for MMC HS200 timing.
- sprd,phy-delay-mmc-hs400: Delay value for MMC HS400 timing.
- sprd,phy-delay-mmc-hs400es: Delay value for MMC HS400 enhanced strobe timing.
Examples:
sdio0: sdio@20600000 {
compatible = "sprd,sdhci-r11";
reg = <0 0x20600000 0 0x1000>;
interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
clock-names = "sdio", "enable";
clocks = <&ap_clk CLK_EMMC_2X>,
<&apahb_gate CLK_EMMC_EB>;
assigned-clocks = <&ap_clk CLK_EMMC_2X>;
assigned-clock-parents = <&rpll CLK_RPLL_390M>;
pinctrl-names = "default", "state_uhs";
pinctrl-0 = <&sd0_pins_default>;
pinctrl-1 = <&sd0_pins_uhs>;
sprd,phy-delay-sd-uhs-sdr104 = <0x3f 0x7f 0x2e 0x2e>;
bus-width = <8>;
non-removable;
no-sdio;
no-sd;
cap-mmc-hw-reset;
status = "okay";
};

View File

@ -0,0 +1,112 @@
# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mmc/sprd,sdhci-r11.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Spreadtrum SDHCI controller
maintainers:
- Orson Zhai <orsonzhai@gmail.com>
- Baolin Wang <baolin.wang7@gmail.com>
- Chunyan Zhang <zhang.lyra@gmail.com>
properties:
compatible:
const: sprd,sdhci-r11
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
minItems: 2
items:
- description: SDIO source clock
- description: gate clock for enabling/disabling the device
- description: gate clock controlling the device for some special platforms (optional)
clock-names:
minItems: 2
items:
- const: sdio
- const: enable
- const: 2x_enable
pinctrl-0:
description: default/high speed pin control
maxItems: 1
pinctrl-1:
description: UHS mode pin control
maxItems: 1
pinctrl-names:
minItems: 1
items:
- const: default
- const: state_uhs
patternProperties:
"^sprd,phy-delay-(legacy|mmc-(ddr52|highspeed|hs[24]00|hs400es)|sd-(highspeed|uhs-sdr(50|104)))$":
$ref: /schemas/types.yaml#/definitions/uint32-array
items:
- description: clock data write line delay value
- description: clock read command line delay value
- description: clock read data positive edge delay value
- description: clock read data negative edge delay value
description:
PHY DLL delays are used to delay the data valid window, and align
the window to the sampling clock. Each cell's delay value unit is
cycle of the PHY clock.
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
allOf:
- $ref: sdhci-common.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/sprd,sc9860-clk.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
mmc@50430000 {
compatible = "sprd,sdhci-r11";
reg = <0x50430000 0x1000>;
interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&aon_prediv CLK_EMMC_2X>,
<&apahb_gate CLK_EMMC_EB>,
<&aon_gate CLK_EMMC_2X_EN>;
clock-names = "sdio", "enable", "2x_enable";
pinctrl-0 = <&sd0_pins_default>;
pinctrl-1 = <&sd0_pins_uhs>;
pinctrl-names = "default", "state_uhs";
bus-width = <8>;
cap-mmc-hw-reset;
mmc-hs400-enhanced-strobe;
mmc-hs400-1_8v;
mmc-hs200-1_8v;
mmc-ddr-1_8v;
non-removable;
no-sdio;
no-sd;
sprd,phy-delay-mmc-ddr52 = <0x3f 0x75 0x14 0x14>;
sprd,phy-delay-mmc-hs200 = <0x0 0x8c 0x8c 0x8c>;
sprd,phy-delay-mmc-hs400 = <0x44 0x7f 0x2e 0x2e>;
sprd,phy-delay-mmc-hs400es = <0x3f 0x3f 0x2e 0x2e>;
};
...

View File

@ -20156,6 +20156,7 @@ SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) NXP i.MX DRIVER
M: Haibo Chen <haibo.chen@nxp.com>
L: imx@lists.linux.dev
L: linux-mmc@vger.kernel.org
L: s32@nxp.com
S: Maintained
F: drivers/mmc/host/sdhci-esdhc-imx.c
@ -23702,12 +23703,6 @@ M: Kevin Brace <kevinbrace@bracecomputerlab.com>
S: Maintained
F: drivers/net/ethernet/via/via-rhine.c
VIA SD/MMC CARD CONTROLLER DRIVER
M: Bruce Chang <brucechang@via.com.tw>
M: Harald Welte <HaraldWelte@viatech.com>
S: Maintained
F: drivers/mmc/host/via-sdmmc.c
VIA UNICHROME(PRO)/CHROME9 FRAMEBUFFER DRIVER
M: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
L: linux-fbdev@vger.kernel.org

View File

@ -44,16 +44,6 @@ config MEMSTICK_R592
To compile this driver as a module, choose M here: the module will
be called r592.
config MEMSTICK_REALTEK_PCI
tristate "Realtek PCI-E Memstick Card Interface Driver"
depends on MISC_RTSX_PCI
help
Say Y here to include driver code to support Memstick card interface
of Realtek PCI-E card reader
To compile this driver as a module, choose M here: the module will
be called rtsx_pci_ms.
config MEMSTICK_REALTEK_USB
tristate "Realtek USB Memstick Card Interface Driver"
depends on MISC_RTSX_USB

View File

@ -6,5 +6,4 @@
obj-$(CONFIG_MEMSTICK_TIFM_MS) += tifm_ms.o
obj-$(CONFIG_MEMSTICK_JMICRON_38X) += jmb38x_ms.o
obj-$(CONFIG_MEMSTICK_R592) += r592.o
obj-$(CONFIG_MEMSTICK_REALTEK_PCI) += rtsx_pci_ms.o
obj-$(CONFIG_MEMSTICK_REALTEK_USB) += rtsx_usb_ms.o

View File

@ -1,638 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* Realtek PCI-Express Memstick Card Interface driver
*
* Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
*
* Author:
* Wei WANG <wei_wang@realsil.com.cn>
*/
#include <linux/module.h>
#include <linux/highmem.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/memstick.h>
#include <linux/rtsx_pci.h>
#include <asm/unaligned.h>
struct realtek_pci_ms {
struct platform_device *pdev;
struct rtsx_pcr *pcr;
struct memstick_host *msh;
struct memstick_request *req;
struct mutex host_mutex;
struct work_struct handle_req;
u8 ssc_depth;
unsigned int clock;
unsigned char ifmode;
bool eject;
};
static inline struct device *ms_dev(struct realtek_pci_ms *host)
{
return &(host->pdev->dev);
}
static inline void ms_clear_error(struct realtek_pci_ms *host)
{
rtsx_pci_write_register(host->pcr, CARD_STOP,
MS_STOP | MS_CLR_ERR, MS_STOP | MS_CLR_ERR);
}
#ifdef DEBUG
static void ms_print_debug_regs(struct realtek_pci_ms *host)
{
struct rtsx_pcr *pcr = host->pcr;
u16 i;
u8 *ptr;
/* Print MS host internal registers */
rtsx_pci_init_cmd(pcr);
for (i = 0xFD40; i <= 0xFD44; i++)
rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0);
for (i = 0xFD52; i <= 0xFD69; i++)
rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0);
rtsx_pci_send_cmd(pcr, 100);
ptr = rtsx_pci_get_cmd_data(pcr);
for (i = 0xFD40; i <= 0xFD44; i++)
dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
for (i = 0xFD52; i <= 0xFD69; i++)
dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
}
#else
#define ms_print_debug_regs(host)
#endif
static int ms_power_on(struct realtek_pci_ms *host)
{
struct rtsx_pcr *pcr = host->pcr;
int err;
rtsx_pci_init_cmd(pcr);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, 0x07, MS_MOD_SEL);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE,
CARD_SHARE_MASK, CARD_SHARE_48_MS);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN,
MS_CLK_EN, MS_CLK_EN);
err = rtsx_pci_send_cmd(pcr, 100);
if (err < 0)
return err;
err = rtsx_pci_card_pull_ctl_enable(pcr, RTSX_MS_CARD);
if (err < 0)
return err;
err = rtsx_pci_card_power_on(pcr, RTSX_MS_CARD);
if (err < 0)
return err;
/* Wait ms power stable */
msleep(150);
err = rtsx_pci_write_register(pcr, CARD_OE,
MS_OUTPUT_EN, MS_OUTPUT_EN);
if (err < 0)
return err;
return 0;
}
static int ms_power_off(struct realtek_pci_ms *host)
{
struct rtsx_pcr *pcr = host->pcr;
int err;
rtsx_pci_init_cmd(pcr);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, MS_CLK_EN, 0);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_OE, MS_OUTPUT_EN, 0);
err = rtsx_pci_send_cmd(pcr, 100);
if (err < 0)
return err;
err = rtsx_pci_card_power_off(pcr, RTSX_MS_CARD);
if (err < 0)
return err;
return rtsx_pci_card_pull_ctl_disable(pcr, RTSX_MS_CARD);
}
static int ms_transfer_data(struct realtek_pci_ms *host, unsigned char data_dir,
u8 tpc, u8 cfg, struct scatterlist *sg)
{
struct rtsx_pcr *pcr = host->pcr;
int err;
unsigned int length = sg->length;
u16 sec_cnt = (u16)(length / 512);
u8 val, trans_mode, dma_dir;
struct memstick_dev *card = host->msh->card;
bool pro_card = card->id.type == MEMSTICK_TYPE_PRO;
dev_dbg(ms_dev(host), "%s: tpc = 0x%02x, data_dir = %s, length = %d\n",
__func__, tpc, (data_dir == READ) ? "READ" : "WRITE",
length);
if (data_dir == READ) {
dma_dir = DMA_DIR_FROM_CARD;
trans_mode = pro_card ? MS_TM_AUTO_READ : MS_TM_NORMAL_READ;
} else {
dma_dir = DMA_DIR_TO_CARD;
trans_mode = pro_card ? MS_TM_AUTO_WRITE : MS_TM_NORMAL_WRITE;
}
rtsx_pci_init_cmd(pcr);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
if (pro_card) {
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_SECTOR_CNT_H,
0xFF, (u8)(sec_cnt >> 8));
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_SECTOR_CNT_L,
0xFF, (u8)sec_cnt);
}
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0,
DMA_DONE_INT, DMA_DONE_INT);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3, 0xFF, (u8)(length >> 24));
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2, 0xFF, (u8)(length >> 16));
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1, 0xFF, (u8)(length >> 8));
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)length);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL,
0x03 | DMA_PACK_SIZE_MASK, dma_dir | DMA_EN | DMA_512);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
0x01, RING_BUFFER);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER,
0xFF, MS_TRANSFER_START | trans_mode);
rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER,
MS_TRANSFER_END, MS_TRANSFER_END);
rtsx_pci_send_cmd_no_wait(pcr);
err = rtsx_pci_transfer_data(pcr, sg, 1, data_dir == READ, 10000);
if (err < 0) {
ms_clear_error(host);
return err;
}
rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val);
if (pro_card) {
if (val & (MS_INT_CMDNK | MS_INT_ERR |
MS_CRC16_ERR | MS_RDY_TIMEOUT))
return -EIO;
} else {
if (val & (MS_CRC16_ERR | MS_RDY_TIMEOUT))
return -EIO;
}
return 0;
}
static int ms_write_bytes(struct realtek_pci_ms *host, u8 tpc,
u8 cfg, u8 cnt, u8 *data, u8 *int_reg)
{
struct rtsx_pcr *pcr = host->pcr;
int err, i;
dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc);
if (!data)
return -EINVAL;
rtsx_pci_init_cmd(pcr);
for (i = 0; i < cnt; i++)
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
PPBUF_BASE2 + i, 0xFF, data[i]);
if (cnt % 2)
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
PPBUF_BASE2 + i, 0xFF, 0xFF);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
0x01, PINGPONG_BUFFER);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER,
0xFF, MS_TRANSFER_START | MS_TM_WRITE_BYTES);
rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER,
MS_TRANSFER_END, MS_TRANSFER_END);
if (int_reg)
rtsx_pci_add_cmd(pcr, READ_REG_CMD, MS_TRANS_CFG, 0, 0);
err = rtsx_pci_send_cmd(pcr, 5000);
if (err < 0) {
u8 val;
rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val);
dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val);
if (int_reg)
*int_reg = val & 0x0F;
ms_print_debug_regs(host);
ms_clear_error(host);
if (!(tpc & 0x08)) {
if (val & MS_CRC16_ERR)
return -EIO;
} else {
if (!(val & 0x80)) {
if (val & (MS_INT_ERR | MS_INT_CMDNK))
return -EIO;
}
}
return -ETIMEDOUT;
}
if (int_reg) {
u8 *ptr = rtsx_pci_get_cmd_data(pcr) + 1;
*int_reg = *ptr & 0x0F;
}
return 0;
}
static int ms_read_bytes(struct realtek_pci_ms *host, u8 tpc,
u8 cfg, u8 cnt, u8 *data, u8 *int_reg)
{
struct rtsx_pcr *pcr = host->pcr;
int err, i;
u8 *ptr;
dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc);
if (!data)
return -EINVAL;
rtsx_pci_init_cmd(pcr);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
0x01, PINGPONG_BUFFER);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER,
0xFF, MS_TRANSFER_START | MS_TM_READ_BYTES);
rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER,
MS_TRANSFER_END, MS_TRANSFER_END);
for (i = 0; i < cnt - 1; i++)
rtsx_pci_add_cmd(pcr, READ_REG_CMD, PPBUF_BASE2 + i, 0, 0);
if (cnt % 2)
rtsx_pci_add_cmd(pcr, READ_REG_CMD, PPBUF_BASE2 + cnt, 0, 0);
else
rtsx_pci_add_cmd(pcr, READ_REG_CMD,
PPBUF_BASE2 + cnt - 1, 0, 0);
if (int_reg)
rtsx_pci_add_cmd(pcr, READ_REG_CMD, MS_TRANS_CFG, 0, 0);
err = rtsx_pci_send_cmd(pcr, 5000);
if (err < 0) {
u8 val;
rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val);
dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val);
if (int_reg)
*int_reg = val & 0x0F;
ms_print_debug_regs(host);
ms_clear_error(host);
if (!(tpc & 0x08)) {
if (val & MS_CRC16_ERR)
return -EIO;
} else {
if (!(val & 0x80)) {
if (val & (MS_INT_ERR | MS_INT_CMDNK))
return -EIO;
}
}
return -ETIMEDOUT;
}
ptr = rtsx_pci_get_cmd_data(pcr) + 1;
for (i = 0; i < cnt; i++)
data[i] = *ptr++;
if (int_reg)
*int_reg = *ptr & 0x0F;
return 0;
}
static int rtsx_pci_ms_issue_cmd(struct realtek_pci_ms *host)
{
struct memstick_request *req = host->req;
int err = 0;
u8 cfg = 0, int_reg;
dev_dbg(ms_dev(host), "%s\n", __func__);
if (req->need_card_int) {
if (host->ifmode != MEMSTICK_SERIAL)
cfg = WAIT_INT;
}
if (req->long_data) {
err = ms_transfer_data(host, req->data_dir,
req->tpc, cfg, &(req->sg));
} else {
if (req->data_dir == READ) {
err = ms_read_bytes(host, req->tpc, cfg,
req->data_len, req->data, &int_reg);
} else {
err = ms_write_bytes(host, req->tpc, cfg,
req->data_len, req->data, &int_reg);
}
}
if (err < 0)
return err;
if (req->need_card_int && (host->ifmode == MEMSTICK_SERIAL)) {
err = ms_read_bytes(host, MS_TPC_GET_INT,
NO_WAIT_INT, 1, &int_reg, NULL);
if (err < 0)
return err;
}
if (req->need_card_int) {
dev_dbg(ms_dev(host), "int_reg: 0x%02x\n", int_reg);
if (int_reg & MS_INT_CMDNK)
req->int_reg |= MEMSTICK_INT_CMDNAK;
if (int_reg & MS_INT_BREQ)
req->int_reg |= MEMSTICK_INT_BREQ;
if (int_reg & MS_INT_ERR)
req->int_reg |= MEMSTICK_INT_ERR;
if (int_reg & MS_INT_CED)
req->int_reg |= MEMSTICK_INT_CED;
}
return 0;
}
static void rtsx_pci_ms_handle_req(struct work_struct *work)
{
struct realtek_pci_ms *host = container_of(work,
struct realtek_pci_ms, handle_req);
struct rtsx_pcr *pcr = host->pcr;
struct memstick_host *msh = host->msh;
int rc;
mutex_lock(&pcr->pcr_mutex);
rtsx_pci_start_run(pcr);
rtsx_pci_switch_clock(host->pcr, host->clock, host->ssc_depth,
false, true, false);
rtsx_pci_write_register(pcr, CARD_SELECT, 0x07, MS_MOD_SEL);
rtsx_pci_write_register(pcr, CARD_SHARE_MODE,
CARD_SHARE_MASK, CARD_SHARE_48_MS);
if (!host->req) {
do {
rc = memstick_next_req(msh, &host->req);
dev_dbg(ms_dev(host), "next req %d\n", rc);
if (!rc)
host->req->error = rtsx_pci_ms_issue_cmd(host);
} while (!rc);
}
mutex_unlock(&pcr->pcr_mutex);
}
static void rtsx_pci_ms_request(struct memstick_host *msh)
{
struct realtek_pci_ms *host = memstick_priv(msh);
dev_dbg(ms_dev(host), "--> %s\n", __func__);
if (rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD))
return;
schedule_work(&host->handle_req);
}
static int rtsx_pci_ms_set_param(struct memstick_host *msh,
enum memstick_param param, int value)
{
struct realtek_pci_ms *host = memstick_priv(msh);
struct rtsx_pcr *pcr = host->pcr;
unsigned int clock = 0;
u8 ssc_depth = 0;
int err;
dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n",
__func__, param, value);
err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD);
if (err)
return err;
switch (param) {
case MEMSTICK_POWER:
if (value == MEMSTICK_POWER_ON)
err = ms_power_on(host);
else if (value == MEMSTICK_POWER_OFF)
err = ms_power_off(host);
else
return -EINVAL;
break;
case MEMSTICK_INTERFACE:
if (value == MEMSTICK_SERIAL) {
clock = 19000000;
ssc_depth = RTSX_SSC_DEPTH_500K;
err = rtsx_pci_write_register(pcr, MS_CFG, 0x58,
MS_BUS_WIDTH_1 | PUSH_TIME_DEFAULT);
if (err < 0)
return err;
} else if (value == MEMSTICK_PAR4) {
clock = 39000000;
ssc_depth = RTSX_SSC_DEPTH_1M;
err = rtsx_pci_write_register(pcr, MS_CFG,
0x58, MS_BUS_WIDTH_4 | PUSH_TIME_ODD);
if (err < 0)
return err;
} else {
return -EINVAL;
}
err = rtsx_pci_switch_clock(pcr, clock,
ssc_depth, false, true, false);
if (err < 0)
return err;
host->ssc_depth = ssc_depth;
host->clock = clock;
host->ifmode = value;
break;
}
return 0;
}
#ifdef CONFIG_PM
static int rtsx_pci_ms_suspend(struct platform_device *pdev, pm_message_t state)
{
struct realtek_pci_ms *host = platform_get_drvdata(pdev);
struct memstick_host *msh = host->msh;
dev_dbg(ms_dev(host), "--> %s\n", __func__);
memstick_suspend_host(msh);
return 0;
}
static int rtsx_pci_ms_resume(struct platform_device *pdev)
{
struct realtek_pci_ms *host = platform_get_drvdata(pdev);
struct memstick_host *msh = host->msh;
dev_dbg(ms_dev(host), "--> %s\n", __func__);
memstick_resume_host(msh);
return 0;
}
#else /* CONFIG_PM */
#define rtsx_pci_ms_suspend NULL
#define rtsx_pci_ms_resume NULL
#endif /* CONFIG_PM */
static void rtsx_pci_ms_card_event(struct platform_device *pdev)
{
struct realtek_pci_ms *host = platform_get_drvdata(pdev);
memstick_detect_change(host->msh);
}
static int rtsx_pci_ms_drv_probe(struct platform_device *pdev)
{
struct memstick_host *msh;
struct realtek_pci_ms *host;
struct rtsx_pcr *pcr;
struct pcr_handle *handle = pdev->dev.platform_data;
int rc;
if (!handle)
return -ENXIO;
pcr = handle->pcr;
if (!pcr)
return -ENXIO;
dev_dbg(&(pdev->dev),
": Realtek PCI-E Memstick controller found\n");
msh = memstick_alloc_host(sizeof(*host), &pdev->dev);
if (!msh)
return -ENOMEM;
host = memstick_priv(msh);
host->pcr = pcr;
host->msh = msh;
host->pdev = pdev;
platform_set_drvdata(pdev, host);
pcr->slots[RTSX_MS_CARD].p_dev = pdev;
pcr->slots[RTSX_MS_CARD].card_event = rtsx_pci_ms_card_event;
mutex_init(&host->host_mutex);
INIT_WORK(&host->handle_req, rtsx_pci_ms_handle_req);
msh->request = rtsx_pci_ms_request;
msh->set_param = rtsx_pci_ms_set_param;
msh->caps = MEMSTICK_CAP_PAR4;
rc = memstick_add_host(msh);
if (rc) {
memstick_free_host(msh);
return rc;
}
return 0;
}
static void rtsx_pci_ms_drv_remove(struct platform_device *pdev)
{
struct realtek_pci_ms *host = platform_get_drvdata(pdev);
struct rtsx_pcr *pcr;
struct memstick_host *msh;
int rc;
pcr = host->pcr;
pcr->slots[RTSX_MS_CARD].p_dev = NULL;
pcr->slots[RTSX_MS_CARD].card_event = NULL;
msh = host->msh;
host->eject = true;
cancel_work_sync(&host->handle_req);
mutex_lock(&host->host_mutex);
if (host->req) {
dev_dbg(&(pdev->dev),
"%s: Controller removed during transfer\n",
dev_name(&msh->dev));
rtsx_pci_complete_unfinished_transfer(pcr);
host->req->error = -ENOMEDIUM;
do {
rc = memstick_next_req(msh, &host->req);
if (!rc)
host->req->error = -ENOMEDIUM;
} while (!rc);
}
mutex_unlock(&host->host_mutex);
memstick_remove_host(msh);
memstick_free_host(msh);
dev_dbg(&(pdev->dev),
": Realtek PCI-E Memstick controller has been removed\n");
}
static struct platform_device_id rtsx_pci_ms_ids[] = {
{
.name = DRV_NAME_RTSX_PCI_MS,
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(platform, rtsx_pci_ms_ids);
static struct platform_driver rtsx_pci_ms_driver = {
.probe = rtsx_pci_ms_drv_probe,
.remove_new = rtsx_pci_ms_drv_remove,
.id_table = rtsx_pci_ms_ids,
.suspend = rtsx_pci_ms_suspend,
.resume = rtsx_pci_ms_resume,
.driver = {
.name = DRV_NAME_RTSX_PCI_MS,
},
};
module_platform_driver(rtsx_pci_ms_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Wei WANG <wei_wang@realsil.com.cn>");
MODULE_DESCRIPTION("Realtek PCI-E Memstick Card Host Driver");

View File

@ -2362,4 +2362,5 @@ static void __exit mmc_exit(void)
subsys_initcall(mmc_init);
module_exit(mmc_exit);
MODULE_DESCRIPTION("MMC core driver");
MODULE_LICENSE("GPL");

View File

@ -115,4 +115,5 @@ static struct platform_driver mmc_pwrseq_emmc_driver = {
};
module_platform_driver(mmc_pwrseq_emmc_driver);
MODULE_DESCRIPTION("Hardware reset support for eMMC");
MODULE_LICENSE("GPL v2");

View File

@ -130,4 +130,5 @@ static struct platform_driver mmc_pwrseq_sd8787_driver = {
};
module_platform_driver(mmc_pwrseq_sd8787_driver);
MODULE_DESCRIPTION("Power sequence support for Marvell SD8787 BT + Wifi chip");
MODULE_LICENSE("GPL v2");

View File

@ -159,4 +159,5 @@ static struct platform_driver mmc_pwrseq_simple_driver = {
};
module_platform_driver(mmc_pwrseq_simple_driver);
MODULE_DESCRIPTION("Simple power sequence management for MMC");
MODULE_LICENSE("GPL v2");

View File

@ -1162,4 +1162,5 @@ module_init(sdio_uart_init);
module_exit(sdio_uart_exit);
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("SDIO UART/GPS driver");
MODULE_LICENSE("GPL");

View File

@ -1016,7 +1016,7 @@ config MMC_SDHCI_MICROCHIP_PIC32
config MMC_SDHCI_BRCMSTB
tristate "Broadcom SDIO/SD/MMC support"
depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
depends on ARCH_BRCMSTB || ARCH_BCM2835 || BMIPS_GENERIC || COMPILE_TEST
depends on MMC_SDHCI_PLTFM
select MMC_CQHCI
default ARCH_BRCMSTB || BMIPS_GENERIC

View File

@ -33,6 +33,7 @@
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/consumer.h>
#include <linux/workqueue.h>
#include <asm/cacheflush.h>
#include <asm/io.h>
@ -272,12 +273,12 @@ struct atmel_mci_dma {
* EVENT_DATA_ERROR is pending.
* @stop_cmdr: Value to be loaded into CMDR when the stop command is
* to be sent.
* @tasklet: Tasklet running the request state machine.
* @bh_work: Work running the request state machine.
* @pending_events: Bitmask of events flagged by the interrupt handler
* to be processed by the tasklet.
* to be processed by the work.
* @completed_events: Bitmask of events which the state machine has
* processed.
* @state: Tasklet state.
* @state: Work state.
* @queue: List of slots waiting for access to the controller.
* @need_clock_update: Update the clock rate before the next request.
* @need_reset: Reset controller before next request.
@ -352,7 +353,7 @@ struct atmel_mci {
u32 data_status;
u32 stop_cmdr;
struct tasklet_struct tasklet;
struct work_struct bh_work;
unsigned long pending_events;
unsigned long completed_events;
enum atmel_mci_state state;
@ -735,7 +736,7 @@ static void atmci_timeout_timer(struct timer_list *t)
host->need_reset = 1;
host->state = STATE_END_REQUEST;
smp_wmb();
tasklet_schedule(&host->tasklet);
queue_work(system_bh_wq, &host->bh_work);
}
static inline unsigned int atmci_ns_to_clocks(struct atmel_mci *host,
@ -958,7 +959,7 @@ static void atmci_pdc_complete(struct atmel_mci *host)
dev_dbg(dev, "(%s) set pending xfer complete\n", __func__);
atmci_set_pending(host, EVENT_XFER_COMPLETE);
tasklet_schedule(&host->tasklet);
queue_work(system_bh_wq, &host->bh_work);
}
static void atmci_dma_cleanup(struct atmel_mci *host)
@ -972,7 +973,7 @@ static void atmci_dma_cleanup(struct atmel_mci *host)
}
/*
* This function is called by the DMA driver from tasklet context.
* This function is called by the DMA driver from bh context.
*/
static void atmci_dma_complete(void *arg)
{
@ -995,7 +996,7 @@ static void atmci_dma_complete(void *arg)
if (data) {
dev_dbg(dev, "(%s) set pending xfer complete\n", __func__);
atmci_set_pending(host, EVENT_XFER_COMPLETE);
tasklet_schedule(&host->tasklet);
queue_work(system_bh_wq, &host->bh_work);
/*
* Regardless of what the documentation says, we have
@ -1008,7 +1009,7 @@ static void atmci_dma_complete(void *arg)
* haven't seen all the potential error bits yet.
*
* The interrupt handler will schedule a different
* tasklet to finish things up when the data transfer
* bh work to finish things up when the data transfer
* is completely done.
*
* We may not complete the mmc request here anyway
@ -1745,9 +1746,9 @@ static void atmci_detect_change(struct timer_list *t)
}
}
static void atmci_tasklet_func(struct tasklet_struct *t)
static void atmci_work_func(struct work_struct *t)
{
struct atmel_mci *host = from_tasklet(host, t, tasklet);
struct atmel_mci *host = from_work(host, t, bh_work);
struct mmc_request *mrq = host->mrq;
struct mmc_data *data = host->data;
struct device *dev = host->dev;
@ -1759,7 +1760,7 @@ static void atmci_tasklet_func(struct tasklet_struct *t)
state = host->state;
dev_vdbg(dev, "tasklet: state %u pending/completed/mask %lx/%lx/%x\n",
dev_vdbg(dev, "bh_work: state %u pending/completed/mask %lx/%lx/%x\n",
state, host->pending_events, host->completed_events,
atmci_readl(host, ATMCI_IMR));
@ -2118,7 +2119,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
dev_dbg(dev, "set pending data error\n");
smp_wmb();
atmci_set_pending(host, EVENT_DATA_ERROR);
tasklet_schedule(&host->tasklet);
queue_work(system_bh_wq, &host->bh_work);
}
if (pending & ATMCI_TXBUFE) {
@ -2187,7 +2188,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
smp_wmb();
dev_dbg(dev, "set pending notbusy\n");
atmci_set_pending(host, EVENT_NOTBUSY);
tasklet_schedule(&host->tasklet);
queue_work(system_bh_wq, &host->bh_work);
}
if (pending & ATMCI_NOTBUSY) {
@ -2196,7 +2197,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
smp_wmb();
dev_dbg(dev, "set pending notbusy\n");
atmci_set_pending(host, EVENT_NOTBUSY);
tasklet_schedule(&host->tasklet);
queue_work(system_bh_wq, &host->bh_work);
}
if (pending & ATMCI_RXRDY)
@ -2211,7 +2212,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
smp_wmb();
dev_dbg(dev, "set pending cmd rdy\n");
atmci_set_pending(host, EVENT_CMD_RDY);
tasklet_schedule(&host->tasklet);
queue_work(system_bh_wq, &host->bh_work);
}
if (pending & (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB))
@ -2487,7 +2488,7 @@ static int atmci_probe(struct platform_device *pdev)
host->mapbase = regs->start;
tasklet_setup(&host->tasklet, atmci_tasklet_func);
INIT_WORK(&host->bh_work, atmci_work_func);
ret = request_irq(irq, atmci_interrupt, 0, dev_name(dev), host);
if (ret) {

View File

@ -42,6 +42,7 @@
#include <linux/leds.h>
#include <linux/mmc/host.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <asm/io.h>
#include <asm/mach-au1x00/au1000.h>
@ -113,8 +114,8 @@ struct au1xmmc_host {
int irq;
struct tasklet_struct finish_task;
struct tasklet_struct data_task;
struct work_struct finish_bh_work;
struct work_struct data_bh_work;
struct au1xmmc_platform_data *platdata;
struct platform_device *pdev;
struct resource *ioarea;
@ -253,9 +254,9 @@ static void au1xmmc_finish_request(struct au1xmmc_host *host)
mmc_request_done(host->mmc, mrq);
}
static void au1xmmc_tasklet_finish(struct tasklet_struct *t)
static void au1xmmc_finish_bh_work(struct work_struct *t)
{
struct au1xmmc_host *host = from_tasklet(host, t, finish_task);
struct au1xmmc_host *host = from_work(host, t, finish_bh_work);
au1xmmc_finish_request(host);
}
@ -363,9 +364,9 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
au1xmmc_finish_request(host);
}
static void au1xmmc_tasklet_data(struct tasklet_struct *t)
static void au1xmmc_data_bh_work(struct work_struct *t)
{
struct au1xmmc_host *host = from_tasklet(host, t, data_task);
struct au1xmmc_host *host = from_work(host, t, data_bh_work);
u32 status = __raw_readl(HOST_STATUS(host));
au1xmmc_data_complete(host, status);
@ -425,7 +426,7 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host)
if (host->flags & HOST_F_STOP)
SEND_STOP(host);
tasklet_schedule(&host->data_task);
queue_work(system_bh_wq, &host->data_bh_work);
}
}
@ -505,7 +506,7 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)
if (host->flags & HOST_F_STOP)
SEND_STOP(host);
tasklet_schedule(&host->data_task);
queue_work(system_bh_wq, &host->data_bh_work);
}
}
@ -561,7 +562,7 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
if (!trans || cmd->error) {
IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF);
tasklet_schedule(&host->finish_task);
queue_work(system_bh_wq, &host->finish_bh_work);
return;
}
@ -797,7 +798,7 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id)
IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH);
/* IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF); */
tasklet_schedule(&host->finish_task);
queue_work(system_bh_wq, &host->finish_bh_work);
}
#if 0
else if (status & SD_STATUS_DD) {
@ -806,7 +807,7 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id)
au1xmmc_receive_pio(host);
else {
au1xmmc_data_complete(host, status);
/* tasklet_schedule(&host->data_task); */
/* queue_work(system_bh_wq, &host->data_bh_work); */
}
}
#endif
@ -854,7 +855,7 @@ static void au1xmmc_dbdma_callback(int irq, void *dev_id)
if (host->flags & HOST_F_STOP)
SEND_STOP(host);
tasklet_schedule(&host->data_task);
queue_work(system_bh_wq, &host->data_bh_work);
}
static int au1xmmc_dbdma_init(struct au1xmmc_host *host)
@ -1039,9 +1040,9 @@ static int au1xmmc_probe(struct platform_device *pdev)
if (host->platdata)
mmc->caps &= ~(host->platdata->mask_host_caps);
tasklet_setup(&host->data_task, au1xmmc_tasklet_data);
INIT_WORK(&host->data_bh_work, au1xmmc_data_bh_work);
tasklet_setup(&host->finish_task, au1xmmc_tasklet_finish);
INIT_WORK(&host->finish_bh_work, au1xmmc_finish_bh_work);
if (has_dbdma()) {
ret = au1xmmc_dbdma_init(host);
@ -1091,8 +1092,8 @@ out5:
if (host->flags & HOST_F_DBDMA)
au1xmmc_dbdma_shutdown(host);
tasklet_kill(&host->data_task);
tasklet_kill(&host->finish_task);
cancel_work_sync(&host->data_bh_work);
cancel_work_sync(&host->finish_bh_work);
if (host->platdata && host->platdata->cd_setup &&
!(mmc->caps & MMC_CAP_NEEDS_POLL))
@ -1135,8 +1136,8 @@ static void au1xmmc_remove(struct platform_device *pdev)
__raw_writel(0, HOST_CONFIG2(host));
wmb(); /* drain writebuffer */
tasklet_kill(&host->data_task);
tasklet_kill(&host->finish_task);
cancel_work_sync(&host->data_bh_work);
cancel_work_sync(&host->finish_bh_work);
if (host->flags & HOST_F_DBDMA)
au1xmmc_dbdma_shutdown(host);

View File

@ -493,7 +493,7 @@ static void cb710_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
if (!cb710_mmc_command(mmc, mrq->cmd) && mrq->stop)
cb710_mmc_command(mmc, mrq->stop);
tasklet_schedule(&reader->finish_req_tasklet);
queue_work(system_bh_wq, &reader->finish_req_bh_work);
}
static int cb710_mmc_powerup(struct cb710_slot *slot)
@ -646,10 +646,10 @@ static int cb710_mmc_irq_handler(struct cb710_slot *slot)
return 1;
}
static void cb710_mmc_finish_request_tasklet(struct tasklet_struct *t)
static void cb710_mmc_finish_request_bh_work(struct work_struct *t)
{
struct cb710_mmc_reader *reader = from_tasklet(reader, t,
finish_req_tasklet);
struct cb710_mmc_reader *reader = from_work(reader, t,
finish_req_bh_work);
struct mmc_request *mrq = reader->mrq;
reader->mrq = NULL;
@ -718,8 +718,8 @@ static int cb710_mmc_init(struct platform_device *pdev)
reader = mmc_priv(mmc);
tasklet_setup(&reader->finish_req_tasklet,
cb710_mmc_finish_request_tasklet);
INIT_WORK(&reader->finish_req_bh_work,
cb710_mmc_finish_request_bh_work);
spin_lock_init(&reader->irq_lock);
cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
@ -763,7 +763,7 @@ static void cb710_mmc_exit(struct platform_device *pdev)
cb710_write_port_32(slot, CB710_MMC_CONFIG_PORT, 0);
cb710_write_port_16(slot, CB710_MMC_CONFIGB_PORT, 0);
tasklet_kill(&reader->finish_req_tasklet);
cancel_work_sync(&reader->finish_req_bh_work);
mmc_free_host(mmc);
}

View File

@ -8,10 +8,11 @@
#define LINUX_CB710_MMC_H
#include <linux/cb710.h>
#include <linux/workqueue.h>
/* per-MMC-reader structure */
struct cb710_mmc_reader {
struct tasklet_struct finish_req_tasklet;
struct work_struct finish_req_bh_work;
struct mmc_request *mrq;
spinlock_t irq_lock;
unsigned char last_power_mode;

View File

@ -1187,7 +1187,7 @@ static int davinci_mmcsd_probe(struct platform_device *pdev)
struct mmc_davinci_host *host = NULL;
struct mmc_host *mmc = NULL;
struct resource *r, *mem = NULL;
int ret, irq;
int ret, irq, bus_width;
size_t mem_size;
const struct platform_device_id *id_entry;
@ -1317,9 +1317,14 @@ static int davinci_mmcsd_probe(struct platform_device *pdev)
rename_region(mem, mmc_hostname(mmc));
if (mmc->caps & MMC_CAP_8_BIT_DATA)
bus_width = 8;
else if (mmc->caps & MMC_CAP_4_BIT_DATA)
bus_width = 4;
else
bus_width = 1;
dev_info(mmc_dev(host->mmc), "Using %s, %d-bit mode\n",
host->use_dma ? "DMA" : "PIO",
(mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1);
host->use_dma ? "DMA" : "PIO", bus_width);
return 0;

View File

@ -3,6 +3,7 @@
* Copyright (C) 2018 Mellanox Technologies.
*/
#include <linux/arm-smccc.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/mmc/host.h>
@ -20,6 +21,9 @@
#define BLUEFIELD_UHS_REG_EXT_SAMPLE 2
#define BLUEFIELD_UHS_REG_EXT_DRIVE 4
/* SMC call for RST_N */
#define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007
static void dw_mci_bluefield_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{
u32 reg;
@ -34,8 +38,20 @@ static void dw_mci_bluefield_set_ios(struct dw_mci *host, struct mmc_ios *ios)
mci_writel(host, UHS_REG_EXT, reg);
}
static void dw_mci_bluefield_hw_reset(struct dw_mci *host)
{
struct arm_smccc_res res = { 0 };
arm_smccc_smc(BLUEFIELD_SMC_SET_EMMC_RST_N, 0, 0, 0, 0, 0, 0, 0,
&res);
if (res.a0)
pr_err("RST_N failed.\n");
}
static const struct dw_mci_drv_data bluefield_drv_data = {
.set_ios = dw_mci_bluefield_set_ios
.set_ios = dw_mci_bluefield_set_ios,
.hw_reset = dw_mci_bluefield_hw_reset
};
static const struct of_device_id dw_mci_bluefield_match[] = {

View File

@ -493,7 +493,7 @@ static void dw_mci_dmac_complete_dma(void *arg)
*/
if (data) {
set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
tasklet_schedule(&host->tasklet);
queue_work(system_bh_wq, &host->bh_work);
}
}
@ -1617,6 +1617,7 @@ static void dw_mci_hw_reset(struct mmc_host *mmc)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
const struct dw_mci_drv_data *drv_data = host->drv_data;
int reset;
if (host->use_dma == TRANS_MODE_IDMAC)
@ -1626,6 +1627,11 @@ static void dw_mci_hw_reset(struct mmc_host *mmc)
SDMMC_CTRL_FIFO_RESET))
return;
if (drv_data && drv_data->hw_reset) {
drv_data->hw_reset(host);
return;
}
/*
* According to eMMC spec, card reset procedure:
* tRstW >= 1us: RST_n pulse width
@ -1834,7 +1840,7 @@ static enum hrtimer_restart dw_mci_fault_timer(struct hrtimer *t)
if (!host->data_status) {
host->data_status = SDMMC_INT_DCRC;
set_bit(EVENT_DATA_ERROR, &host->pending_events);
tasklet_schedule(&host->tasklet);
queue_work(system_bh_wq, &host->bh_work);
}
spin_unlock_irqrestore(&host->irq_lock, flags);
@ -2056,9 +2062,9 @@ static bool dw_mci_clear_pending_data_complete(struct dw_mci *host)
return true;
}
static void dw_mci_tasklet_func(struct tasklet_struct *t)
static void dw_mci_work_func(struct work_struct *t)
{
struct dw_mci *host = from_tasklet(host, t, tasklet);
struct dw_mci *host = from_work(host, t, bh_work);
struct mmc_data *data;
struct mmc_command *cmd;
struct mmc_request *mrq;
@ -2113,7 +2119,7 @@ static void dw_mci_tasklet_func(struct tasklet_struct *t)
* will waste a bit of time (we already know
* the command was bad), it can't cause any
* errors since it's possible it would have
* taken place anyway if this tasklet got
* taken place anyway if this bh work got
* delayed. Allowing the transfer to take place
* avoids races and keeps things simple.
*/
@ -2706,7 +2712,7 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
smp_wmb(); /* drain writebuffer */
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
tasklet_schedule(&host->tasklet);
queue_work(system_bh_wq, &host->bh_work);
dw_mci_start_fault_timer(host);
}
@ -2774,7 +2780,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
set_bit(EVENT_DATA_COMPLETE,
&host->pending_events);
tasklet_schedule(&host->tasklet);
queue_work(system_bh_wq, &host->bh_work);
spin_unlock(&host->irq_lock);
}
@ -2793,7 +2799,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
dw_mci_read_data_pio(host, true);
}
set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
tasklet_schedule(&host->tasklet);
queue_work(system_bh_wq, &host->bh_work);
spin_unlock(&host->irq_lock);
}
@ -3098,7 +3104,7 @@ static void dw_mci_cmd11_timer(struct timer_list *t)
host->cmd_status = SDMMC_INT_RTO;
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
tasklet_schedule(&host->tasklet);
queue_work(system_bh_wq, &host->bh_work);
}
static void dw_mci_cto_timer(struct timer_list *t)
@ -3144,7 +3150,7 @@ static void dw_mci_cto_timer(struct timer_list *t)
*/
host->cmd_status = SDMMC_INT_RTO;
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
tasklet_schedule(&host->tasklet);
queue_work(system_bh_wq, &host->bh_work);
break;
default:
dev_warn(host->dev, "Unexpected command timeout, state %d\n",
@ -3195,7 +3201,7 @@ static void dw_mci_dto_timer(struct timer_list *t)
host->data_status = SDMMC_INT_DRTO;
set_bit(EVENT_DATA_ERROR, &host->pending_events);
set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
tasklet_schedule(&host->tasklet);
queue_work(system_bh_wq, &host->bh_work);
break;
default:
dev_warn(host->dev, "Unexpected data timeout, state %d\n",
@ -3435,7 +3441,7 @@ int dw_mci_probe(struct dw_mci *host)
else
host->fifo_reg = host->regs + DATA_240A_OFFSET;
tasklet_setup(&host->tasklet, dw_mci_tasklet_func);
INIT_WORK(&host->bh_work, dw_mci_work_func);
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
host->irq_flags, "dw-mci", host);
if (ret)

View File

@ -17,6 +17,7 @@
#include <linux/fault-inject.h>
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
enum dw_mci_state {
STATE_IDLE = 0,
@ -89,12 +90,12 @@ struct dw_mci_dma_slave {
* @stop_cmdr: Value to be loaded into CMDR when the stop command is
* to be sent.
* @dir_status: Direction of current transfer.
* @tasklet: Tasklet running the request state machine.
* @bh_work: Work running the request state machine.
* @pending_events: Bitmask of events flagged by the interrupt handler
* to be processed by the tasklet.
* to be processed by bh work.
* @completed_events: Bitmask of events which the state machine has
* processed.
* @state: Tasklet state.
* @state: BH work state.
* @queue: List of slots waiting for access to the controller.
* @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
* rate and timeout calculations.
@ -194,7 +195,7 @@ struct dw_mci {
u32 data_status;
u32 stop_cmdr;
u32 dir_status;
struct tasklet_struct tasklet;
struct work_struct bh_work;
unsigned long pending_events;
unsigned long completed_events;
enum dw_mci_state state;
@ -565,6 +566,7 @@ struct dw_mci_slot {
* @execute_tuning: implementation specific tuning procedure.
* @set_data_timeout: implementation specific timeout.
* @get_drto_clks: implementation specific cycle count for data read timeout.
* @hw_reset: implementation specific HW reset.
*
* Provide controller implementation specific extensions. The usage of this
* data structure is fully optional and usage of each member in this structure
@ -585,5 +587,6 @@ struct dw_mci_drv_data {
void (*set_data_timeout)(struct dw_mci *host,
unsigned int timeout_ns);
u32 (*get_drto_clks)(struct dw_mci *host);
void (*hw_reset)(struct dw_mci *host);
};
#endif /* _DW_MMC_H_ */

View File

@ -1208,7 +1208,10 @@ static int mmc_spi_probe(struct spi_device *spi)
* that's the only reason not to use a few MHz for f_min (until
* the upper layer reads the target frequency from the CSD).
*/
mmc->f_min = 400000;
if (spi->controller->min_speed_hz > 400000)
dev_warn(&spi->dev,"Controller unable to reduce bus clock to 400 KHz\n");
mmc->f_min = max(spi->controller->min_speed_hz, 400000);
mmc->f_max = spi->max_speed_hz;
host = mmc_priv(mmc);

View File

@ -19,6 +19,7 @@
#include <linux/mmc/core.h>
#include <linux/mmc/host.h>
MODULE_DESCRIPTION("OpenFirmware bindings for the MMC-over-SPI driver");
MODULE_LICENSE("GPL");
struct of_mmc_spi {

View File

@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_data/mmc-omap.h>
#include <linux/workqueue.h>
#define OMAP_MMC_REG_CMD 0x00
@ -105,7 +106,7 @@ struct mmc_omap_slot {
u16 power_mode;
unsigned int fclk_freq;
struct tasklet_struct cover_tasklet;
struct work_struct cover_bh_work;
struct timer_list cover_timer;
unsigned cover_open;
@ -873,18 +874,18 @@ void omap_mmc_notify_cover_event(struct device *dev, int num, int is_closed)
sysfs_notify(&slot->mmc->class_dev.kobj, NULL, "cover_switch");
}
tasklet_hi_schedule(&slot->cover_tasklet);
queue_work(system_bh_highpri_wq, &slot->cover_bh_work);
}
static void mmc_omap_cover_timer(struct timer_list *t)
{
struct mmc_omap_slot *slot = from_timer(slot, t, cover_timer);
tasklet_schedule(&slot->cover_tasklet);
queue_work(system_bh_wq, &slot->cover_bh_work);
}
static void mmc_omap_cover_handler(struct tasklet_struct *t)
static void mmc_omap_cover_bh_handler(struct work_struct *t)
{
struct mmc_omap_slot *slot = from_tasklet(slot, t, cover_tasklet);
struct mmc_omap_slot *slot = from_work(slot, t, cover_bh_work);
int cover_open = mmc_omap_cover_is_open(slot);
mmc_detect_change(slot->mmc, 0);
@ -1314,7 +1315,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
if (slot->pdata->get_cover_state != NULL) {
timer_setup(&slot->cover_timer, mmc_omap_cover_timer, 0);
tasklet_setup(&slot->cover_tasklet, mmc_omap_cover_handler);
INIT_WORK(&slot->cover_bh_work, mmc_omap_cover_bh_handler);
}
r = mmc_add_host(mmc);
@ -1333,7 +1334,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
&dev_attr_cover_switch);
if (r < 0)
goto err_remove_slot_name;
tasklet_schedule(&slot->cover_tasklet);
queue_work(system_bh_wq, &slot->cover_bh_work);
}
return 0;
@ -1356,7 +1357,7 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
if (slot->pdata->get_cover_state != NULL)
device_remove_file(&mmc->class_dev, &dev_attr_cover_switch);
tasklet_kill(&slot->cover_tasklet);
cancel_work_sync(&slot->cover_bh_work);
del_timer_sync(&slot->cover_timer);
flush_workqueue(slot->host->mmc_omap_wq);

View File

@ -11,6 +11,7 @@
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include "tmio_mmc.h"
struct renesas_sdhi_scc {
@ -67,7 +68,7 @@ struct renesas_sdhi_dma {
dma_filter_fn filter;
void (*enable)(struct tmio_mmc_host *host, bool enable);
struct completion dma_dataend;
struct tasklet_struct dma_complete;
struct work_struct dma_complete;
};
struct renesas_sdhi {
@ -93,6 +94,7 @@ struct renesas_sdhi {
unsigned int tap_set;
struct reset_control *rstc;
struct tmio_mmc_host *host;
};
#define host_to_priv(host) \

View File

@ -970,6 +970,8 @@ int renesas_sdhi_probe(struct platform_device *pdev,
if (IS_ERR(host))
return PTR_ERR(host);
priv->host = host;
if (of_data) {
mmc_data->flags |= of_data->tmio_flags;
mmc_data->ocr_mask = of_data->tmio_ocr_mask;
@ -1162,4 +1164,5 @@ void renesas_sdhi_remove(struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(renesas_sdhi_remove);
MODULE_DESCRIPTION("Renesas SDHI core driver");
MODULE_LICENSE("GPL v2");

View File

@ -337,7 +337,7 @@ static bool renesas_sdhi_internal_dmac_dma_irq(struct tmio_mmc_host *host)
writel(status ^ dma_irqs, host->ctl + DM_CM_INFO1);
set_bit(SDHI_DMA_END_FLAG_DMA, &dma_priv->end_flags);
if (test_bit(SDHI_DMA_END_FLAG_ACCESS, &dma_priv->end_flags))
tasklet_schedule(&dma_priv->dma_complete);
queue_work(system_bh_wq, &dma_priv->dma_complete);
}
return status & dma_irqs;
@ -352,7 +352,7 @@ renesas_sdhi_internal_dmac_dataend_dma(struct tmio_mmc_host *host)
set_bit(SDHI_DMA_END_FLAG_ACCESS, &dma_priv->end_flags);
if (test_bit(SDHI_DMA_END_FLAG_DMA, &dma_priv->end_flags) ||
host->data->error)
tasklet_schedule(&dma_priv->dma_complete);
queue_work(system_bh_wq, &dma_priv->dma_complete);
}
/*
@ -440,9 +440,9 @@ force_pio:
renesas_sdhi_internal_dmac_enable_dma(host, false);
}
static void renesas_sdhi_internal_dmac_issue_tasklet_fn(unsigned long arg)
static void renesas_sdhi_internal_dmac_issue_work_fn(struct work_struct *work)
{
struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
struct tmio_mmc_host *host = from_work(host, work, dma_issue);
struct renesas_sdhi *priv = host_to_priv(host);
tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND);
@ -454,7 +454,7 @@ static void renesas_sdhi_internal_dmac_issue_tasklet_fn(unsigned long arg)
/* on CMD errors, simulate DMA end immediately */
set_bit(SDHI_DMA_END_FLAG_DMA, &priv->dma_priv.end_flags);
if (test_bit(SDHI_DMA_END_FLAG_ACCESS, &priv->dma_priv.end_flags))
tasklet_schedule(&priv->dma_priv.dma_complete);
queue_work(system_bh_wq, &priv->dma_priv.dma_complete);
}
}
@ -484,9 +484,11 @@ static bool renesas_sdhi_internal_dmac_complete(struct tmio_mmc_host *host)
return true;
}
static void renesas_sdhi_internal_dmac_complete_tasklet_fn(unsigned long arg)
static void renesas_sdhi_internal_dmac_complete_work_fn(struct work_struct *work)
{
struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
struct renesas_sdhi_dma *dma_priv = from_work(dma_priv, work, dma_complete);
struct renesas_sdhi *priv = container_of(dma_priv, typeof(*priv), dma_priv);
struct tmio_mmc_host *host = priv->host;
spin_lock_irq(&host->lock);
if (!renesas_sdhi_internal_dmac_complete(host))
@ -544,12 +546,10 @@ renesas_sdhi_internal_dmac_request_dma(struct tmio_mmc_host *host,
/* Each value is set to non-zero to assume "enabling" each DMA */
host->chan_rx = host->chan_tx = (void *)0xdeadbeaf;
tasklet_init(&priv->dma_priv.dma_complete,
renesas_sdhi_internal_dmac_complete_tasklet_fn,
(unsigned long)host);
tasklet_init(&host->dma_issue,
renesas_sdhi_internal_dmac_issue_tasklet_fn,
(unsigned long)host);
INIT_WORK(&priv->dma_priv.dma_complete,
renesas_sdhi_internal_dmac_complete_work_fn);
INIT_WORK(&host->dma_issue,
renesas_sdhi_internal_dmac_issue_work_fn);
/* Add pre_req and post_req */
host->ops.pre_req = renesas_sdhi_internal_dmac_pre_req;

View File

@ -312,9 +312,9 @@ static void renesas_sdhi_sys_dmac_start_dma(struct tmio_mmc_host *host,
}
}
static void renesas_sdhi_sys_dmac_issue_tasklet_fn(unsigned long priv)
static void renesas_sdhi_sys_dmac_issue_work_fn(struct work_struct *work)
{
struct tmio_mmc_host *host = (struct tmio_mmc_host *)priv;
struct tmio_mmc_host *host = from_work(host, work, dma_issue);
struct dma_chan *chan = NULL;
spin_lock_irq(&host->lock);
@ -401,9 +401,8 @@ static void renesas_sdhi_sys_dmac_request_dma(struct tmio_mmc_host *host,
goto ebouncebuf;
init_completion(&priv->dma_priv.dma_dataend);
tasklet_init(&host->dma_issue,
renesas_sdhi_sys_dmac_issue_tasklet_fn,
(unsigned long)host);
INIT_WORK(&host->dma_issue,
renesas_sdhi_sys_dmac_issue_work_fn);
}
renesas_sdhi_sys_dmac_enable_dma(host, true);

View File

@ -107,7 +107,7 @@ static void sdhci_bcm_kona_sd_init(struct sdhci_host *host)
* Software emulation of the SD card insertion/removal. Set insert=1 for insert
* and insert=0 for removal. The card detection is done by GPIO. For Broadcom
* IP to function properly the bit 0 of CORESTAT register needs to be set/reset
* to generate the CD IRQ handled in sdhci.c which schedules card_tasklet.
* to generate the CD IRQ handled in sdhci.c
*/
static int sdhci_bcm_kona_sd_card_emulate(struct sdhci_host *host, int insert)
{

View File

@ -31,6 +31,21 @@
#define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
#define SDIO_CFG_CQ_CAPABILITY 0x4c
#define SDIO_CFG_CQ_CAPABILITY_FMUL GENMASK(13, 12)
#define SDIO_CFG_CTRL 0x0
#define SDIO_CFG_CTRL_SDCD_N_TEST_EN BIT(31)
#define SDIO_CFG_CTRL_SDCD_N_TEST_LEV BIT(30)
#define SDIO_CFG_MAX_50MHZ_MODE 0x1ac
#define SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE BIT(31)
#define SDIO_CFG_MAX_50MHZ_MODE_ENABLE BIT(0)
#define MMC_CAP_HSE_MASK (MMC_CAP2_HSX00_1_2V | MMC_CAP2_HSX00_1_8V)
/* Select all SD UHS type I SDR speed above 50MB/s */
#define MMC_CAP_UHS_I_SDR_MASK (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104)
struct sdhci_brcmstb_priv {
void __iomem *cfg_regs;
unsigned int flags;
@ -39,6 +54,7 @@ struct sdhci_brcmstb_priv {
};
struct brcmstb_match_priv {
void (*cfginit)(struct sdhci_host *host);
void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios);
struct sdhci_ops *ops;
const unsigned int flags;
@ -169,6 +185,33 @@ static void sdhci_brcmstb_set_uhs_signaling(struct sdhci_host *host,
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
}
static void sdhci_brcmstb_cfginit_2712(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host);
u32 reg;
/*
* If we support a speed that requires tuning,
* then select the delay line PHY as the clock source.
*/
if ((host->mmc->caps & MMC_CAP_UHS_I_SDR_MASK) || (host->mmc->caps2 & MMC_CAP_HSE_MASK)) {
reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_MAX_50MHZ_MODE);
reg &= ~SDIO_CFG_MAX_50MHZ_MODE_ENABLE;
reg |= SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE;
writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_MAX_50MHZ_MODE);
}
if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
(host->mmc->caps & MMC_CAP_NEEDS_POLL)) {
/* Force presence */
reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_CTRL);
reg &= ~SDIO_CFG_CTRL_SDCD_N_TEST_LEV;
reg |= SDIO_CFG_CTRL_SDCD_N_TEST_EN;
writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_CTRL);
}
}
static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc)
{
sdhci_dumpregs(mmc_priv(mmc));
@ -201,6 +244,14 @@ static struct sdhci_ops sdhci_brcmstb_ops = {
.set_uhs_signaling = sdhci_set_uhs_signaling,
};
static struct sdhci_ops sdhci_brcmstb_ops_2712 = {
.set_clock = sdhci_set_clock,
.set_power = sdhci_set_power_and_bus_voltage,
.set_bus_width = sdhci_set_bus_width,
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
};
static struct sdhci_ops sdhci_brcmstb_ops_7216 = {
.set_clock = sdhci_brcmstb_set_clock,
.set_bus_width = sdhci_set_bus_width,
@ -215,6 +266,11 @@ static struct sdhci_ops sdhci_brcmstb_ops_74165b0 = {
.set_uhs_signaling = sdhci_brcmstb_set_uhs_signaling,
};
static const struct brcmstb_match_priv match_priv_2712 = {
.cfginit = sdhci_brcmstb_cfginit_2712,
.ops = &sdhci_brcmstb_ops_2712,
};
static struct brcmstb_match_priv match_priv_7425 = {
.flags = BRCMSTB_MATCH_FLAGS_NO_64BIT |
BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT,
@ -239,6 +295,7 @@ static struct brcmstb_match_priv match_priv_74165b0 = {
};
static const struct of_device_id __maybe_unused sdhci_brcm_of_match[] = {
{ .compatible = "brcm,bcm2712-sdhci", .data = &match_priv_2712 },
{ .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 },
{ .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 },
{ .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 },
@ -371,6 +428,9 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
(host->mmc->caps2 & MMC_CAP2_HS400_ES))
host->mmc_host_ops.hs400_enhanced_strobe = match_priv->hs400es;
if (match_priv->cfginit)
match_priv->cfginit(host);
/*
* Supply the existing CAPS, but clear the UHS modes. This
* will allow these modes to be specified by device tree

View File

@ -201,6 +201,9 @@
/* ERR004536 is not applicable for the IP */
#define ESDHC_FLAG_SKIP_ERR004536 BIT(17)
/* The IP does not have GPIO CD wake capabilities */
#define ESDHC_FLAG_SKIP_CD_WAKE BIT(18)
enum wp_types {
ESDHC_WP_NONE, /* no WP, neither controller nor gpio */
ESDHC_WP_CONTROLLER, /* mmc controller internal WP */
@ -298,7 +301,7 @@ static struct esdhc_soc_data usdhc_s32g2_data = {
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
| ESDHC_FLAG_SKIP_ERR004536,
| ESDHC_FLAG_SKIP_ERR004536 | ESDHC_FLAG_SKIP_CD_WAKE,
};
static struct esdhc_soc_data usdhc_imx7ulp_data = {
@ -1706,7 +1709,6 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
}
pltfm_host->clk = imx_data->clk_per;
pltfm_host->clock = clk_get_rate(pltfm_host->clk);
err = clk_prepare_enable(imx_data->clk_per);
if (err)
goto free_sdhci;
@ -1717,6 +1719,13 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
if (err)
goto disable_ipg_clk;
pltfm_host->clock = clk_get_rate(pltfm_host->clk);
if (!pltfm_host->clock) {
dev_err(mmc_dev(host->mmc), "could not get clk rate\n");
err = -EINVAL;
goto disable_ahb_clk;
}
imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(imx_data->pinctrl))
dev_warn(mmc_dev(host->mmc), "could not get pinctrl\n");
@ -1726,7 +1735,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR;
/* GPIO CD can be set as a wakeup source */
host->mmc->caps |= MMC_CAP_CD_WAKE;
if (!(imx_data->socdata->flags & ESDHC_FLAG_SKIP_CD_WAKE))
host->mmc->caps |= MMC_CAP_CD_WAKE;
if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;

View File

@ -908,6 +908,7 @@ static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = {
.get_max_clock = rk35xx_get_max_clock,
.reset = rk35xx_sdhci_reset,
.adma_write_desc = dwcmshc_adma_write_desc,
.irq = dwcmshc_cqe_irq_handler,
};
static const struct sdhci_ops sdhci_dwcmshc_th1520_ops = {

View File

@ -1319,6 +1319,23 @@ static const struct sdhci_pci_fixes sdhci_intel_mrfld_mmc = {
.probe_slot = intel_mrfld_mmc_probe_slot,
};
#define JMB388_SAMPLE_COUNT 5
static int jmicron_jmb388_get_ro(struct mmc_host *mmc)
{
int i, ro_count;
ro_count = 0;
for (i = 0; i < JMB388_SAMPLE_COUNT; i++) {
if (sdhci_get_ro(mmc) > 0) {
if (++ro_count > JMB388_SAMPLE_COUNT / 2)
return 1;
}
msleep(30);
}
return 0;
}
static int jmicron_pmos(struct sdhci_pci_chip *chip, int on)
{
u8 scratch;
@ -1403,11 +1420,6 @@ static int jmicron_probe(struct sdhci_pci_chip *chip)
return ret;
}
/* quirk for unsable RO-detection on JM388 chips */
if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_SD ||
chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD)
chip->quirks |= SDHCI_QUIRK_UNSTABLE_RO_DETECT;
return 0;
}
@ -1462,6 +1474,11 @@ static int jmicron_probe_slot(struct sdhci_pci_slot *slot)
slot->host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST;
/* Handle unstable RO-detection on JM388 chips */
if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_SD ||
slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD)
slot->host->mmc_host_ops.get_ro = jmicron_jmb388_get_ro;
return 0;
}

View File

@ -2513,8 +2513,9 @@ out:
}
EXPORT_SYMBOL_GPL(sdhci_get_cd_nogpio);
static int sdhci_check_ro(struct sdhci_host *host)
int sdhci_get_ro(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
bool allow_invert = false;
int is_readonly;
@ -2522,10 +2523,10 @@ static int sdhci_check_ro(struct sdhci_host *host)
is_readonly = 0;
} else if (host->ops->get_ro) {
is_readonly = host->ops->get_ro(host);
} else if (mmc_can_gpio_ro(host->mmc)) {
is_readonly = mmc_gpio_get_ro(host->mmc);
} else if (mmc_can_gpio_ro(mmc)) {
is_readonly = mmc_gpio_get_ro(mmc);
/* Do not invert twice */
allow_invert = !(host->mmc->caps2 & MMC_CAP2_RO_ACTIVE_HIGH);
allow_invert = !(mmc->caps2 & MMC_CAP2_RO_ACTIVE_HIGH);
} else {
is_readonly = !(sdhci_readl(host, SDHCI_PRESENT_STATE)
& SDHCI_WRITE_PROTECT);
@ -2539,27 +2540,7 @@ static int sdhci_check_ro(struct sdhci_host *host)
return is_readonly;
}
#define SAMPLE_COUNT 5
static int sdhci_get_ro(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
int i, ro_count;
if (!(host->quirks & SDHCI_QUIRK_UNSTABLE_RO_DETECT))
return sdhci_check_ro(host);
ro_count = 0;
for (i = 0; i < SAMPLE_COUNT; i++) {
if (sdhci_check_ro(host)) {
if (++ro_count > SAMPLE_COUNT / 2)
return 1;
}
msleep(30);
}
return 0;
}
EXPORT_SYMBOL_GPL(sdhci_get_ro);
static void sdhci_hw_reset(struct mmc_host *mmc)
{

View File

@ -437,8 +437,6 @@ struct sdhci_host {
#define SDHCI_QUIRK_NO_HISPD_BIT (1<<29)
/* Controller treats ADMA descriptors with length 0000h incorrectly */
#define SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC (1<<30)
/* The read-only detection via SDHCI_PRESENT_STATE register is unstable */
#define SDHCI_QUIRK_UNSTABLE_RO_DETECT (1<<31)
unsigned int quirks2; /* More deviations from spec. */
@ -788,6 +786,7 @@ void sdhci_set_power_and_bus_voltage(struct sdhci_host *host,
void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
unsigned short vdd);
int sdhci_get_cd_nogpio(struct mmc_host *mmc);
int sdhci_get_ro(struct mmc_host *mmc);
void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq);
int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
void sdhci_set_bus_width(struct sdhci_host *host, int width);

View File

@ -90,7 +90,7 @@
/* Command Queue Host Controller Interface Base address */
#define SDHCI_AM654_CQE_BASE_ADDR 0x200
static struct regmap_config sdhci_am654_regmap_config = {
static const struct regmap_config sdhci_am654_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,

View File

@ -13,6 +13,7 @@
#include <linux/highmem.h>
#include <linux/scatterlist.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include <asm/io.h>
#define DRIVER_NAME "tifm_sd"
@ -97,7 +98,7 @@ struct tifm_sd {
unsigned int clk_div;
unsigned long timeout_jiffies;
struct tasklet_struct finish_tasklet;
struct work_struct finish_bh_work;
struct timer_list timer;
struct mmc_request *req;
@ -463,7 +464,7 @@ static void tifm_sd_check_status(struct tifm_sd *host)
}
}
finish_request:
tasklet_schedule(&host->finish_tasklet);
queue_work(system_bh_wq, &host->finish_bh_work);
}
/* Called from interrupt handler */
@ -723,9 +724,9 @@ err_out:
mmc_request_done(mmc, mrq);
}
static void tifm_sd_end_cmd(struct tasklet_struct *t)
static void tifm_sd_end_cmd(struct work_struct *t)
{
struct tifm_sd *host = from_tasklet(host, t, finish_tasklet);
struct tifm_sd *host = from_work(host, t, finish_bh_work);
struct tifm_dev *sock = host->dev;
struct mmc_host *mmc = tifm_get_drvdata(sock);
struct mmc_request *mrq;
@ -960,7 +961,7 @@ static int tifm_sd_probe(struct tifm_dev *sock)
*/
mmc->max_busy_timeout = TIFM_MMCSD_REQ_TIMEOUT_MS;
tasklet_setup(&host->finish_tasklet, tifm_sd_end_cmd);
INIT_WORK(&host->finish_bh_work, tifm_sd_end_cmd);
timer_setup(&host->timer, tifm_sd_abort, 0);
mmc->ops = &tifm_sd_ops;
@ -999,7 +1000,7 @@ static void tifm_sd_remove(struct tifm_dev *sock)
writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE);
spin_unlock_irqrestore(&sock->lock, flags);
tasklet_kill(&host->finish_tasklet);
cancel_work_sync(&host->finish_bh_work);
spin_lock_irqsave(&sock->lock, flags);
if (host->req) {
@ -1009,7 +1010,7 @@ static void tifm_sd_remove(struct tifm_dev *sock)
host->req->cmd->error = -ENOMEDIUM;
if (host->req->stop)
host->req->stop->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet);
queue_work(system_bh_wq, &host->finish_bh_work);
}
spin_unlock_irqrestore(&sock->lock, flags);
mmc_remove_host(mmc);

View File

@ -21,6 +21,7 @@
#include <linux/scatterlist.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#define CTL_SD_CMD 0x00
#define CTL_ARG_REG 0x04
@ -139,9 +140,6 @@ struct tmio_mmc_host {
struct mmc_host *mmc;
struct mmc_host_ops ops;
/* Callbacks for clock / power control */
void (*set_pwr)(struct platform_device *host, int state);
/* pio related stuff */
struct scatterlist *sg_ptr;
struct scatterlist *sg_orig;
@ -156,7 +154,7 @@ struct tmio_mmc_host {
bool dma_on;
struct dma_chan *chan_rx;
struct dma_chan *chan_tx;
struct tasklet_struct dma_issue;
struct work_struct dma_issue;
struct scatterlist bounce_sg;
u8 *bounce_buf;

View File

@ -608,7 +608,7 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat)
} else {
tmio_mmc_disable_mmc_irqs(host,
TMIO_MASK_READOP);
tasklet_schedule(&host->dma_issue);
queue_work(system_bh_wq, &host->dma_issue);
}
} else {
if (!host->dma_on) {
@ -616,7 +616,7 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat)
} else {
tmio_mmc_disable_mmc_irqs(host,
TMIO_MASK_WRITEOP);
tasklet_schedule(&host->dma_issue);
queue_work(system_bh_wq, &host->dma_issue);
}
}
} else {
@ -880,9 +880,6 @@ static void tmio_mmc_power_on(struct tmio_mmc_host *host, unsigned short vdd)
/* .set_ios() is returning void, so, no chance to report an error */
if (host->set_pwr)
host->set_pwr(host->pdev, 1);
if (!IS_ERR(mmc->supply.vmmc)) {
ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
/*
@ -916,9 +913,6 @@ static void tmio_mmc_power_off(struct tmio_mmc_host *host)
if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
if (host->set_pwr)
host->set_pwr(host->pdev, 0);
}
static unsigned int tmio_mmc_get_timeout_cycles(struct tmio_mmc_host *host)
@ -1160,8 +1154,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
if (pdata->flags & TMIO_MMC_USE_BUSY_TIMEOUT && !_host->get_timeout_cycles)
_host->get_timeout_cycles = tmio_mmc_get_timeout_cycles;
_host->set_pwr = pdata->set_pwr;
ret = tmio_mmc_init_ocr(_host);
if (ret < 0)
return ret;
@ -1319,4 +1311,5 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
EXPORT_SYMBOL_GPL(tmio_mmc_host_runtime_resume);
#endif
MODULE_DESCRIPTION("TMIO MMC core driver");
MODULE_LICENSE("GPL v2");

View File

@ -90,9 +90,9 @@ static void uniphier_sd_dma_endisable(struct tmio_mmc_host *host, int enable)
}
/* external DMA engine */
static void uniphier_sd_external_dma_issue(struct tasklet_struct *t)
static void uniphier_sd_external_dma_issue(struct work_struct *t)
{
struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue);
struct tmio_mmc_host *host = from_work(host, t, dma_issue);
struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
uniphier_sd_dma_endisable(host, 1);
@ -199,7 +199,7 @@ static void uniphier_sd_external_dma_request(struct tmio_mmc_host *host,
host->chan_rx = chan;
host->chan_tx = chan;
tasklet_setup(&host->dma_issue, uniphier_sd_external_dma_issue);
INIT_WORK(&host->dma_issue, uniphier_sd_external_dma_issue);
}
static void uniphier_sd_external_dma_release(struct tmio_mmc_host *host)
@ -236,9 +236,9 @@ static const struct tmio_mmc_dma_ops uniphier_sd_external_dma_ops = {
.dataend = uniphier_sd_external_dma_dataend,
};
static void uniphier_sd_internal_dma_issue(struct tasklet_struct *t)
static void uniphier_sd_internal_dma_issue(struct work_struct *t)
{
struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue);
struct tmio_mmc_host *host = from_work(host, t, dma_issue);
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
@ -317,7 +317,7 @@ static void uniphier_sd_internal_dma_request(struct tmio_mmc_host *host,
host->chan_tx = (void *)0xdeadbeaf;
tasklet_setup(&host->dma_issue, uniphier_sd_internal_dma_issue);
INIT_WORK(&host->dma_issue, uniphier_sd_internal_dma_issue);
}
static void uniphier_sd_internal_dma_release(struct tmio_mmc_host *host)

View File

@ -12,6 +12,7 @@
#include <linux/interrupt.h>
#include <linux/mmc/host.h>
#include <linux/workqueue.h>
#define DRV_NAME "via_sdmmc"
@ -307,7 +308,7 @@ struct via_crdr_mmc_host {
struct sdhcreg pm_sdhc_reg;
struct work_struct carddet_work;
struct tasklet_struct finish_tasklet;
struct work_struct finish_bh_work;
struct timer_list timer;
spinlock_t lock;
@ -643,7 +644,7 @@ static void via_sdc_finish_data(struct via_crdr_mmc_host *host)
if (data->stop)
via_sdc_send_command(host, data->stop);
else
tasklet_schedule(&host->finish_tasklet);
queue_work(system_bh_wq, &host->finish_bh_work);
}
static void via_sdc_finish_command(struct via_crdr_mmc_host *host)
@ -653,7 +654,7 @@ static void via_sdc_finish_command(struct via_crdr_mmc_host *host)
host->cmd->error = 0;
if (!host->cmd->data)
tasklet_schedule(&host->finish_tasklet);
queue_work(system_bh_wq, &host->finish_bh_work);
host->cmd = NULL;
}
@ -682,7 +683,7 @@ static void via_sdc_request(struct mmc_host *mmc, struct mmc_request *mrq)
status = readw(host->sdhc_mmiobase + VIA_CRDR_SDSTATUS);
if (!(status & VIA_CRDR_SDSTS_SLOTG) || host->reject) {
host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet);
queue_work(system_bh_wq, &host->finish_bh_work);
} else {
via_sdc_send_command(host, mrq->cmd);
}
@ -848,7 +849,7 @@ static void via_sdc_cmd_isr(struct via_crdr_mmc_host *host, u16 intmask)
host->cmd->error = -EILSEQ;
if (host->cmd->error)
tasklet_schedule(&host->finish_tasklet);
queue_work(system_bh_wq, &host->finish_bh_work);
else if (intmask & VIA_CRDR_SDSTS_CRD)
via_sdc_finish_command(host);
}
@ -955,16 +956,16 @@ static void via_sdc_timeout(struct timer_list *t)
sdhost->cmd->error = -ETIMEDOUT;
else
sdhost->mrq->cmd->error = -ETIMEDOUT;
tasklet_schedule(&sdhost->finish_tasklet);
queue_work(system_bh_wq, &sdhost->finish_bh_work);
}
}
spin_unlock_irqrestore(&sdhost->lock, flags);
}
static void via_sdc_tasklet_finish(struct tasklet_struct *t)
static void via_sdc_finish_bh_work(struct work_struct *t)
{
struct via_crdr_mmc_host *host = from_tasklet(host, t, finish_tasklet);
struct via_crdr_mmc_host *host = from_work(host, t, finish_bh_work);
unsigned long flags;
struct mmc_request *mrq;
@ -1005,7 +1006,7 @@ static void via_sdc_card_detect(struct work_struct *work)
pr_err("%s: Card removed during transfer!\n",
mmc_hostname(host->mmc));
host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet);
queue_work(system_bh_wq, &host->finish_bh_work);
}
spin_unlock_irqrestore(&host->lock, flags);
@ -1051,7 +1052,7 @@ static void via_init_mmc_host(struct via_crdr_mmc_host *host)
INIT_WORK(&host->carddet_work, via_sdc_card_detect);
tasklet_setup(&host->finish_tasklet, via_sdc_tasklet_finish);
INIT_WORK(&host->finish_bh_work, via_sdc_finish_bh_work);
addrbase = host->sdhc_mmiobase;
writel(0x0, addrbase + VIA_CRDR_SDINTMASK);
@ -1193,7 +1194,7 @@ static void via_sd_remove(struct pci_dev *pcidev)
sdhost->mrq->cmd->error = -ENOMEDIUM;
if (sdhost->mrq->stop)
sdhost->mrq->stop->error = -ENOMEDIUM;
tasklet_schedule(&sdhost->finish_tasklet);
queue_work(system_bh_wq, &sdhost->finish_bh_work);
}
spin_unlock_irqrestore(&sdhost->lock, flags);
@ -1203,7 +1204,7 @@ static void via_sd_remove(struct pci_dev *pcidev)
del_timer_sync(&sdhost->timer);
tasklet_kill(&sdhost->finish_tasklet);
cancel_work_sync(&sdhost->finish_bh_work);
/* switch off power */
gatt = readb(sdhost->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT);

View File

@ -459,7 +459,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
* FIFO threshold interrupts properly.
*/
if ((data->blocks * data->blksz - data->bytes_xfered) < 16)
tasklet_schedule(&host->fifo_tasklet);
queue_work(system_bh_wq, &host->fifo_bh_work);
}
static void wbsd_fill_fifo(struct wbsd_host *host)
@ -524,7 +524,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
* 'FIFO empty' under certain conditions. So we
* need to be a bit more pro-active.
*/
tasklet_schedule(&host->fifo_tasklet);
queue_work(system_bh_wq, &host->fifo_bh_work);
}
static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
@ -746,7 +746,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
struct mmc_command *cmd;
/*
* Disable tasklets to avoid a deadlock.
* Disable bh works to avoid a deadlock.
*/
spin_lock_bh(&host->lock);
@ -821,7 +821,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
* Dirty fix for hardware bug.
*/
if (host->dma == -1)
tasklet_schedule(&host->fifo_tasklet);
queue_work(system_bh_wq, &host->fifo_bh_work);
spin_unlock_bh(&host->lock);
@ -961,13 +961,13 @@ static void wbsd_reset_ignore(struct timer_list *t)
* Card status might have changed during the
* blackout.
*/
tasklet_schedule(&host->card_tasklet);
queue_work(system_bh_wq, &host->card_bh_work);
spin_unlock_bh(&host->lock);
}
/*
* Tasklets
* BH Works
*/
static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
@ -987,9 +987,9 @@ static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
return host->mrq->cmd->data;
}
static void wbsd_tasklet_card(struct tasklet_struct *t)
static void wbsd_card_bh_work(struct work_struct *t)
{
struct wbsd_host *host = from_tasklet(host, t, card_tasklet);
struct wbsd_host *host = from_work(host, t, card_bh_work);
u8 csr;
int delay = -1;
@ -1020,7 +1020,7 @@ static void wbsd_tasklet_card(struct tasklet_struct *t)
wbsd_reset(host);
host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet);
queue_work(system_bh_wq, &host->finish_bh_work);
}
delay = 0;
@ -1036,9 +1036,9 @@ static void wbsd_tasklet_card(struct tasklet_struct *t)
mmc_detect_change(host->mmc, msecs_to_jiffies(delay));
}
static void wbsd_tasklet_fifo(struct tasklet_struct *t)
static void wbsd_fifo_bh_work(struct work_struct *t)
{
struct wbsd_host *host = from_tasklet(host, t, fifo_tasklet);
struct wbsd_host *host = from_work(host, t, fifo_bh_work);
struct mmc_data *data;
spin_lock(&host->lock);
@ -1060,16 +1060,16 @@ static void wbsd_tasklet_fifo(struct tasklet_struct *t)
*/
if (host->num_sg == 0) {
wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
tasklet_schedule(&host->finish_tasklet);
queue_work(system_bh_wq, &host->finish_bh_work);
}
end:
spin_unlock(&host->lock);
}
static void wbsd_tasklet_crc(struct tasklet_struct *t)
static void wbsd_crc_bh_work(struct work_struct *t)
{
struct wbsd_host *host = from_tasklet(host, t, crc_tasklet);
struct wbsd_host *host = from_work(host, t, crc_bh_work);
struct mmc_data *data;
spin_lock(&host->lock);
@ -1085,15 +1085,15 @@ static void wbsd_tasklet_crc(struct tasklet_struct *t)
data->error = -EILSEQ;
tasklet_schedule(&host->finish_tasklet);
queue_work(system_bh_wq, &host->finish_bh_work);
end:
spin_unlock(&host->lock);
}
static void wbsd_tasklet_timeout(struct tasklet_struct *t)
static void wbsd_timeout_bh_work(struct work_struct *t)
{
struct wbsd_host *host = from_tasklet(host, t, timeout_tasklet);
struct wbsd_host *host = from_work(host, t, timeout_bh_work);
struct mmc_data *data;
spin_lock(&host->lock);
@ -1109,15 +1109,15 @@ static void wbsd_tasklet_timeout(struct tasklet_struct *t)
data->error = -ETIMEDOUT;
tasklet_schedule(&host->finish_tasklet);
queue_work(system_bh_wq, &host->finish_bh_work);
end:
spin_unlock(&host->lock);
}
static void wbsd_tasklet_finish(struct tasklet_struct *t)
static void wbsd_finish_bh_work(struct work_struct *t)
{
struct wbsd_host *host = from_tasklet(host, t, finish_tasklet);
struct wbsd_host *host = from_work(host, t, finish_bh_work);
struct mmc_data *data;
spin_lock(&host->lock);
@ -1156,18 +1156,18 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id)
host->isr |= isr;
/*
* Schedule tasklets as needed.
* Schedule bh work as needed.
*/
if (isr & WBSD_INT_CARD)
tasklet_schedule(&host->card_tasklet);
queue_work(system_bh_wq, &host->card_bh_work);
if (isr & WBSD_INT_FIFO_THRE)
tasklet_schedule(&host->fifo_tasklet);
queue_work(system_bh_wq, &host->fifo_bh_work);
if (isr & WBSD_INT_CRC)
tasklet_hi_schedule(&host->crc_tasklet);
queue_work(system_bh_highpri_wq, &host->crc_bh_work);
if (isr & WBSD_INT_TIMEOUT)
tasklet_hi_schedule(&host->timeout_tasklet);
queue_work(system_bh_highpri_wq, &host->timeout_bh_work);
if (isr & WBSD_INT_TC)
tasklet_schedule(&host->finish_tasklet);
queue_work(system_bh_wq, &host->finish_bh_work);
return IRQ_HANDLED;
}
@ -1443,13 +1443,13 @@ static int wbsd_request_irq(struct wbsd_host *host, int irq)
int ret;
/*
* Set up tasklets. Must be done before requesting interrupt.
* Set up bh works. Must be done before requesting interrupt.
*/
tasklet_setup(&host->card_tasklet, wbsd_tasklet_card);
tasklet_setup(&host->fifo_tasklet, wbsd_tasklet_fifo);
tasklet_setup(&host->crc_tasklet, wbsd_tasklet_crc);
tasklet_setup(&host->timeout_tasklet, wbsd_tasklet_timeout);
tasklet_setup(&host->finish_tasklet, wbsd_tasklet_finish);
INIT_WORK(&host->card_bh_work, wbsd_card_bh_work);
INIT_WORK(&host->fifo_bh_work, wbsd_fifo_bh_work);
INIT_WORK(&host->crc_bh_work, wbsd_crc_bh_work);
INIT_WORK(&host->timeout_bh_work, wbsd_timeout_bh_work);
INIT_WORK(&host->finish_bh_work, wbsd_finish_bh_work);
/*
* Allocate interrupt.
@ -1472,11 +1472,11 @@ static void wbsd_release_irq(struct wbsd_host *host)
host->irq = 0;
tasklet_kill(&host->card_tasklet);
tasklet_kill(&host->fifo_tasklet);
tasklet_kill(&host->crc_tasklet);
tasklet_kill(&host->timeout_tasklet);
tasklet_kill(&host->finish_tasklet);
cancel_work_sync(&host->card_bh_work);
cancel_work_sync(&host->fifo_bh_work);
cancel_work_sync(&host->crc_bh_work);
cancel_work_sync(&host->timeout_bh_work);
cancel_work_sync(&host->finish_bh_work);
}
/*

View File

@ -171,11 +171,11 @@ struct wbsd_host
int irq; /* Interrupt */
int dma; /* DMA channel */
struct tasklet_struct card_tasklet; /* Tasklet structures */
struct tasklet_struct fifo_tasklet;
struct tasklet_struct crc_tasklet;
struct tasklet_struct timeout_tasklet;
struct tasklet_struct finish_tasklet;
struct work_struct card_bh_work; /* Work structures */
struct work_struct fifo_bh_work;
struct work_struct crc_bh_work;
struct work_struct timeout_bh_work;
struct work_struct finish_bh_work;
struct timer_list ignore_timer; /* Ignore detection timer */
};

View File

@ -100,8 +100,6 @@ struct tmio_mmc_data {
dma_addr_t dma_rx_offset;
unsigned int max_blk_count;
unsigned short max_segs;
void (*set_pwr)(struct platform_device *host, int state);
void (*set_clk_div)(struct platform_device *host, int state);
};
/*