mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 03:11:11 -04:00
Merge tag 'mmc-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC updates from Ulf Hansson:
"MMC core:
- Add NXP vendor and IW61x device IDs for WiFi chips over SDIO
- Add quirk for incorrect manufacturing date
- Add support for manufacturing date beyond 2025
- Optimize support for secure erase/trim for some Kingston eMMCs
- Remove support for the legacy "enable-sdio-wakeup" DT property
- Use single block writes in the retry path
MMC host:
- dw_mmc:
- A great amount of cleanups/simplifications to improve the code
- Add clk_phase_map support
- Remove mshc DT alias support
- dw_mmc-rockchip:
- Fix runtime PM support for internal phase
- Add support for the RV1103B variant
- loongson2:
- Add support for the Loongson-2K0300 SD/SDIO/eMMC controller
- mtk-sd:
- Add support for the MT8189 variant
- renesas_sdhi_core:
- Add support for selecting an optional mux
- rtsx_pci_sdmmc:
- Simplify voltage switch handling
- sdhci:
- Stop advertising the driver in dmesg
- sdhci-esdhc-imx:
- Add 1-bit bus width support
- Add support for the NXP S32N79 variant
- sdhci-msm:
- Add support for the IPQ5210 and IPQ9650 variants
- Add support for wrapped keys
- Enable ICE for CQE-capable controllers with non-CQE cards
- sdhci-of-arasan:
- Add support for the Axiado AX3000 variant
- sdhci-of-aspeed:
- Add support for the AST2700 variant
- sdhci-of-bst:
- Add driver for the Black Sesame Technologies C1200 controller
- sdhci-of-dwcmshc:
- Add support for the Canaan K230 variant
- Add support for the HPE GSC variant
- Prevent clock glitches to avoid malfunction
- sdhci-of-k1:
- Add support for the K3 variant
mux core/consumers:
- core:
- Add helper functions for getting optional and selected mux-state
- i2c-omap:
- Convert to devm_mux_state_get_optional_selected()
- phy-renesas:
- Convert to devm_mux_state_get_optional_selected()
- phy-can-transceiver:
- Convert to devm_mux_state_get_optional()"
* tag 'mmc-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (131 commits)
mmc: sdhci-msm: Fix the wrapped key handling
mmc: sdhci-of-dwcmshc: Disable clock before DLL configuration
mmc: core: Simplify with scoped for each OF child loop
mmc: core: Optimize size of struct mmc_queue_req
mmc: vub300: clean up module init
mmc: vub300: rename probe error labels
mmc: dw_mmc: Remove dw_mci_start_request wrapper and rename core function
mmc: dw_mmc: Inline dw_mci_queue_request() into dw_mci_request()
mmc: block: Use MQRQ_XFER_SINGLE_BLOCK for both read and write recovery
mmc: mmc_test: Replace hard-coded values with macros and consolidate test parameters
mmc: block: Convert to use DEFINE_SIMPLE_DEV_PM_OPS()
mmc: core: Replace the hard-coded shift value 9 with SECTOR_SHIFT
mmc: sdhci-dwcmshc: Refactor Rockchip platform data for controller revisions
mmc: core: Switch to use pm_ptr() for mmc_host_class_dev_pm_ops
mmc: core: Remove legacy 'enable-sdio-wakeup' DT property support
mmc: mmc_test: use kzalloc_flex
mmc: mtk-sd: disable new_tx/rx and modify related settings for mt8189
dt-bindings: mmc: hisilicon,hi3660-dw-mshc: Convert to DT schema
dt-bindings: mmc: sdhci-msm: add IPQ9650 compatible
mmc: block: use single block write in retry
...
This commit is contained in:
@@ -19,6 +19,10 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- amlogic,t7-mmc
|
||||
- const: amlogic,meson-axg-mmc
|
||||
- const: amlogic,meson-axg-mmc
|
||||
- items:
|
||||
- const: amlogic,meson-gx-mmc
|
||||
|
||||
@@ -106,6 +106,9 @@ properties:
|
||||
description:
|
||||
For this device it is strongly suggested to include
|
||||
arasan,soc-ctl-syscon.
|
||||
- items:
|
||||
- const: axiado,ax3000-sdhci-5.1-emmc # Axiado AX3000 eMMC controller
|
||||
- const: arasan,sdhci-5.1
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@@ -121,6 +124,8 @@ properties:
|
||||
- const: clk_ahb
|
||||
- const: gate
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
@@ -11,7 +11,7 @@ maintainers:
|
||||
- Ulf Hansson <ulf.hansson@linaro.org>
|
||||
|
||||
description:
|
||||
The ARM PrimeCells MMCI PL180 and PL181 provides an interface for
|
||||
The ARM PrimeCell MMCI PL180 and PL181 provides an interface for
|
||||
reading and writing to MultiMedia and SD cards alike. Over the years
|
||||
vendors have use the VHDL code from ARM to create derivative MMC/SD/SDIO
|
||||
host controllers with very similar characteristics.
|
||||
|
||||
@@ -22,10 +22,15 @@ description: |+
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- aspeed,ast2400-sd-controller
|
||||
- aspeed,ast2500-sd-controller
|
||||
- aspeed,ast2600-sd-controller
|
||||
oneOf:
|
||||
- enum:
|
||||
- aspeed,ast2400-sd-controller
|
||||
- aspeed,ast2500-sd-controller
|
||||
- aspeed,ast2600-sd-controller
|
||||
- items:
|
||||
- const: aspeed,ast2700-sd-controller
|
||||
- const: aspeed,ast2600-sd-controller
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: Common configuration registers
|
||||
@@ -38,6 +43,9 @@ properties:
|
||||
maxItems: 1
|
||||
description: The SD/SDIO controller clock gate
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
patternProperties:
|
||||
"^sdhci@[0-9a-f]+$":
|
||||
type: object
|
||||
@@ -46,10 +54,15 @@ patternProperties:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- aspeed,ast2400-sdhci
|
||||
- aspeed,ast2500-sdhci
|
||||
- aspeed,ast2600-sdhci
|
||||
oneOf:
|
||||
- enum:
|
||||
- aspeed,ast2400-sdhci
|
||||
- aspeed,ast2500-sdhci
|
||||
- aspeed,ast2600-sdhci
|
||||
- items:
|
||||
- const: aspeed,ast2700-sdhci
|
||||
- const: aspeed,ast2600-sdhci
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: The SDHCI registers
|
||||
@@ -78,6 +91,18 @@ required:
|
||||
- ranges
|
||||
- clocks
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: aspeed,ast2700-sd-controller
|
||||
then:
|
||||
required:
|
||||
- resets
|
||||
else:
|
||||
properties:
|
||||
resets: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/aspeed-clock.h>
|
||||
|
||||
@@ -26,9 +26,14 @@ properties:
|
||||
reg:
|
||||
minItems: 1
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description:
|
||||
|
||||
70
Documentation/devicetree/bindings/mmc/bst,c1200-sdhci.yaml
Normal file
70
Documentation/devicetree/bindings/mmc/bst,c1200-sdhci.yaml
Normal file
@@ -0,0 +1,70 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/bst,c1200-sdhci.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Black Sesame Technologies DWCMSHC SDHCI Controller
|
||||
|
||||
maintainers:
|
||||
- Ge Gordon <gordon.ge@bst.ai>
|
||||
|
||||
allOf:
|
||||
- $ref: sdhci-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: bst,c1200-sdhci
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: Core SDHCI registers
|
||||
- description: CRM registers
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: core
|
||||
|
||||
memory-region:
|
||||
maxItems: 1
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
bus {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
mmc@22200000 {
|
||||
compatible = "bst,c1200-sdhci";
|
||||
reg = <0x0 0x22200000 0x0 0x1000>,
|
||||
<0x0 0x23006000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clk_mmc>;
|
||||
clock-names = "core";
|
||||
memory-region = <&mmc0_reserved>;
|
||||
max-frequency = <200000000>;
|
||||
bus-width = <8>;
|
||||
non-removable;
|
||||
dma-coherent;
|
||||
};
|
||||
};
|
||||
@@ -134,8 +134,6 @@ allOf:
|
||||
items:
|
||||
- description: Host controller registers
|
||||
- description: Elba byte-lane enable register for writes
|
||||
required:
|
||||
- resets
|
||||
else:
|
||||
properties:
|
||||
reg:
|
||||
|
||||
@@ -35,6 +35,7 @@ properties:
|
||||
- fsl,imx8mm-usdhc
|
||||
- fsl,imxrt1050-usdhc
|
||||
- nxp,s32g2-usdhc
|
||||
- nxp,s32n79-usdhc
|
||||
- items:
|
||||
- const: fsl,imx50-esdhc
|
||||
- const: fsl,imx53-esdhc
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/hisilicon,hi3660-dw-mshc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Hisilicon specific extensions to the Synopsys Designware Mobile Storage Host Controller
|
||||
|
||||
maintainers:
|
||||
- Zhangfei Gao <zhangfei.gao@linaro.org>
|
||||
|
||||
description:
|
||||
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 specific
|
||||
extensions to the Synopsys Designware Mobile Storage Host Controller.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/mmc/synopsys-dw-mshc-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- hisilicon,hi3660-dw-mshc
|
||||
- hisilicon,hi4511-dw-mshc
|
||||
- hisilicon,hi6220-dw-mshc
|
||||
- items:
|
||||
- const: hisilicon,hi3670-dw-mshc
|
||||
- const: hisilicon,hi3660-dw-mshc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: card interface unit clock
|
||||
- description: bus interface unit clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: ciu
|
||||
- const: biu
|
||||
|
||||
hisilicon,peripheral-syscon:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description: phandle of syscon used to control peripheral.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/hi3620-clock.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
mmc@fcd03000 {
|
||||
compatible = "hisilicon,hi4511-dw-mshc";
|
||||
reg = <0xfcd03000 0x1000>;
|
||||
interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>;
|
||||
clock-names = "ciu", "biu";
|
||||
vmmc-supply = <&ldo12>;
|
||||
fifo-depth = <0x100>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>;
|
||||
bus-width = <4>;
|
||||
disable-wp;
|
||||
cd-gpios = <&gpio10 3 GPIO_ACTIVE_HIGH>;
|
||||
cap-mmc-highspeed;
|
||||
cap-sd-highspeed;
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/clock/hi6220-clock.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
mmc@f723e000 {
|
||||
compatible = "hisilicon,hi6220-dw-mshc";
|
||||
reg = <0x0 0xf723e000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clock_sys HI6220_MMC1_CIUCLK>,
|
||||
<&clock_sys HI6220_MMC1_CLK>;
|
||||
clock-names = "ciu", "biu";
|
||||
bus-width = <4>;
|
||||
disable-wp;
|
||||
cap-sd-highspeed;
|
||||
sd-uhs-sdr12;
|
||||
sd-uhs-sdr25;
|
||||
card-detect-delay = <200>;
|
||||
hisilicon,peripheral-syscon = <&ao_ctrl>;
|
||||
cd-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>;
|
||||
pinctrl-names = "default", "idle";
|
||||
pinctrl-0 = <&sd_pmx_func &sd_clk_cfg_func &sd_cfg_func>;
|
||||
pinctrl-1 = <&sd_pmx_idle &sd_clk_cfg_idle &sd_cfg_idle>;
|
||||
vqmmc-supply = <&ldo7>;
|
||||
vmmc-supply = <&ldo10>;
|
||||
};
|
||||
};
|
||||
@@ -1,73 +0,0 @@
|
||||
* Hisilicon 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 specific
|
||||
extensions to the Synopsys Designware Mobile Storage Host Controller.
|
||||
|
||||
Required Properties:
|
||||
|
||||
* compatible: should be one of the following.
|
||||
- "hisilicon,hi3660-dw-mshc": for controllers with hi3660 specific extensions.
|
||||
- "hisilicon,hi3670-dw-mshc", "hisilicon,hi3660-dw-mshc": for controllers
|
||||
with hi3670 specific extensions.
|
||||
- "hisilicon,hi4511-dw-mshc": for controllers with hi4511 specific extensions.
|
||||
- "hisilicon,hi6220-dw-mshc": for controllers with hi6220 specific extensions.
|
||||
|
||||
Optional Properties:
|
||||
- hisilicon,peripheral-syscon: phandle of syscon used to control peripheral.
|
||||
|
||||
Example:
|
||||
|
||||
/* for Hi3620 */
|
||||
|
||||
/* SoC portion */
|
||||
dwmmc_0: dwmmc0@fcd03000 {
|
||||
compatible = "hisilicon,hi4511-dw-mshc";
|
||||
reg = <0xfcd03000 0x1000>;
|
||||
interrupts = <0 16 4>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>;
|
||||
clock-names = "ciu", "biu";
|
||||
};
|
||||
|
||||
/* Board portion */
|
||||
dwmmc0@fcd03000 {
|
||||
vmmc-supply = <&ldo12>;
|
||||
fifo-depth = <0x100>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>;
|
||||
bus-width = <4>;
|
||||
disable-wp;
|
||||
cd-gpios = <&gpio10 3 0>;
|
||||
cap-mmc-highspeed;
|
||||
cap-sd-highspeed;
|
||||
};
|
||||
|
||||
/* for Hi6220 */
|
||||
|
||||
dwmmc_1: dwmmc1@f723e000 {
|
||||
compatible = "hisilicon,hi6220-dw-mshc";
|
||||
bus-width = <0x4>;
|
||||
disable-wp;
|
||||
cap-sd-highspeed;
|
||||
sd-uhs-sdr12;
|
||||
sd-uhs-sdr25;
|
||||
card-detect-delay = <200>;
|
||||
hisilicon,peripheral-syscon = <&ao_ctrl>;
|
||||
reg = <0x0 0xf723e000 0x0 0x1000>;
|
||||
interrupts = <0x0 0x49 0x4>;
|
||||
clocks = <&clock_sys HI6220_MMC1_CIUCLK>, <&clock_sys HI6220_MMC1_CLK>;
|
||||
clock-names = "ciu", "biu";
|
||||
cd-gpios = <&gpio1 0 1>;
|
||||
pinctrl-names = "default", "idle";
|
||||
pinctrl-0 = <&sd_pmx_func &sd_clk_cfg_func &sd_cfg_func>;
|
||||
pinctrl-1 = <&sd_pmx_idle &sd_clk_cfg_idle &sd_cfg_idle>;
|
||||
vqmmc-supply = <&ldo7>;
|
||||
vmmc-supply = <&ldo10>;
|
||||
};
|
||||
@@ -22,6 +22,7 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- loongson,ls2k0300-mmc
|
||||
- loongson,ls2k0500-mmc
|
||||
- loongson,ls2k1000-mmc
|
||||
- loongson,ls2k2000-mmc
|
||||
|
||||
@@ -25,6 +25,7 @@ properties:
|
||||
- mediatek,mt8135-mmc
|
||||
- mediatek,mt8173-mmc
|
||||
- mediatek,mt8183-mmc
|
||||
- mediatek,mt8189-mmc
|
||||
- mediatek,mt8196-mmc
|
||||
- mediatek,mt8516-mmc
|
||||
- items:
|
||||
@@ -192,6 +193,7 @@ allOf:
|
||||
- mediatek,mt8183-mmc
|
||||
- mediatek,mt8186-mmc
|
||||
- mediatek,mt8188-mmc
|
||||
- mediatek,mt8189-mmc
|
||||
- mediatek,mt8195-mmc
|
||||
- mediatek,mt8196-mmc
|
||||
- mediatek,mt8516-mmc
|
||||
@@ -240,6 +242,7 @@ allOf:
|
||||
- mediatek,mt7986-mmc
|
||||
- mediatek,mt7988-mmc
|
||||
- mediatek,mt8183-mmc
|
||||
- mediatek,mt8189-mmc
|
||||
- mediatek,mt8196-mmc
|
||||
then:
|
||||
properties:
|
||||
|
||||
@@ -106,6 +106,11 @@ properties:
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
mux-states:
|
||||
description:
|
||||
mux controller node to route the SD/SDIO/eMMC signals from SoC to cards.
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
@@ -275,6 +280,7 @@ examples:
|
||||
max-frequency = <195000000>;
|
||||
power-domains = <&sysc R8A7790_PD_ALWAYS_ON>;
|
||||
resets = <&cpg 314>;
|
||||
mux-states = <&mux 0>;
|
||||
};
|
||||
|
||||
sdhi1: mmc@ee120000 {
|
||||
|
||||
@@ -47,6 +47,10 @@ properties:
|
||||
- rockchip,rv1126-dw-mshc
|
||||
- const: rockchip,rk3288-dw-mshc
|
||||
# for Rockchip RK3576 with phase tuning inside the controller
|
||||
- items:
|
||||
- enum:
|
||||
- rockchip,rv1103b-dw-mshc
|
||||
- const: rockchip,rk3576-dw-mshc
|
||||
- const: rockchip,rk3576-dw-mshc
|
||||
|
||||
reg:
|
||||
|
||||
@@ -38,10 +38,12 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,ipq5018-sdhci
|
||||
- qcom,ipq5210-sdhci
|
||||
- qcom,ipq5332-sdhci
|
||||
- qcom,ipq5424-sdhci
|
||||
- qcom,ipq6018-sdhci
|
||||
- qcom,ipq9574-sdhci
|
||||
- qcom,ipq9650-sdhci
|
||||
- qcom,kaanapali-sdhci
|
||||
- qcom,milos-sdhci
|
||||
- qcom,qcm2290-sdhci
|
||||
|
||||
@@ -23,6 +23,9 @@ properties:
|
||||
- const: sophgo,sg2044-dwcmshc
|
||||
- const: sophgo,sg2042-dwcmshc
|
||||
- enum:
|
||||
- canaan,k230-emmc
|
||||
- canaan,k230-sdio
|
||||
- hpe,gsc-dwcmshc
|
||||
- rockchip,rk3568-dwcmshc
|
||||
- rockchip,rk3588-dwcmshc
|
||||
- snps,dwcmshc-sdhci
|
||||
@@ -50,11 +53,18 @@ properties:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
minItems: 4
|
||||
maxItems: 5
|
||||
|
||||
reset-names:
|
||||
minItems: 4
|
||||
maxItems: 5
|
||||
|
||||
canaan,usb-phy:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description: Phandle to the Canaan K230 USB PHY node required for
|
||||
k230-emmc/sdio.
|
||||
|
||||
rockchip,txclk-tapnum:
|
||||
description: Specify the number of delay for tx sampling.
|
||||
$ref: /schemas/types.yaml#/definitions/uint8
|
||||
@@ -77,6 +87,17 @@ properties:
|
||||
description: Specifies the drive impedance in Ohm.
|
||||
enum: [33, 40, 50, 66, 100]
|
||||
|
||||
hpe,gxp-sysreg:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
items:
|
||||
- items:
|
||||
- description: phandle to HPE GXP SoC system register block (syscon)
|
||||
- description: offset of the MSHCCS register within the syscon block
|
||||
description:
|
||||
Phandle to the HPE GXP SoC system register block (syscon) and
|
||||
offset of the MSHCCS register used to configure clock
|
||||
synchronisation for HS200 tuning.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@@ -87,6 +108,47 @@ required:
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml#
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- canaan,k230-emmc
|
||||
- canaan,k230-sdio
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 5
|
||||
clock-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: bus
|
||||
- const: axi
|
||||
- const: block
|
||||
- const: timer
|
||||
required:
|
||||
- canaan,usb-phy
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: hpe,gsc-dwcmshc
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: core clock
|
||||
clock-names:
|
||||
items:
|
||||
- const: core
|
||||
required:
|
||||
- hpe,gxp-sysreg
|
||||
else:
|
||||
properties:
|
||||
hpe,gxp-sysreg: false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
@@ -146,6 +208,7 @@ allOf:
|
||||
else:
|
||||
properties:
|
||||
resets:
|
||||
minItems: 5
|
||||
maxItems: 5
|
||||
reset-names:
|
||||
items:
|
||||
|
||||
@@ -14,7 +14,9 @@ allOf:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: spacemit,k1-sdhci
|
||||
enum:
|
||||
- spacemit,k1-sdhci
|
||||
- spacemit,k3-sdhci
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@@ -32,6 +34,16 @@ properties:
|
||||
- const: core
|
||||
- const: io
|
||||
|
||||
resets:
|
||||
items:
|
||||
- description: axi reset, connect to AXI bus, shared by all controllers
|
||||
- description: sdh reset, connect to individual controller separately
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: axi
|
||||
- const: sdh
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
@@ -2640,7 +2640,9 @@ R: BST Linux Kernel Upstream Group <bst-upstream@bstai.top>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/arm/bst.yaml
|
||||
F: Documentation/devicetree/bindings/mmc/bst,c1200-sdhci.yaml
|
||||
F: arch/arm64/boot/dts/bst/
|
||||
F: drivers/mmc/host/sdhci-of-bst.c
|
||||
|
||||
ARM/CALXEDA HIGHBANK ARCHITECTURE
|
||||
M: Andre Przywara <andre.przywara@arm.com>
|
||||
|
||||
@@ -20,8 +20,8 @@ / {
|
||||
compatible = "hisilicon,hi3660-hikey960", "hisilicon,hi3660";
|
||||
|
||||
aliases {
|
||||
mshc1 = &dwmmc1;
|
||||
mshc2 = &dwmmc2;
|
||||
mmc1 = &dwmmc1;
|
||||
mmc2 = &dwmmc2;
|
||||
serial0 = &uart0;
|
||||
serial1 = &uart1;
|
||||
serial2 = &uart2;
|
||||
|
||||
@@ -19,8 +19,8 @@ / {
|
||||
compatible = "hisilicon,hi3670-hikey970", "hisilicon,hi3670";
|
||||
|
||||
aliases {
|
||||
mshc1 = &dwmmc1;
|
||||
mshc2 = &dwmmc2;
|
||||
mmc1 = &dwmmc1;
|
||||
mmc2 = &dwmmc2;
|
||||
serial0 = &uart0;
|
||||
serial1 = &uart1;
|
||||
serial2 = &uart2;
|
||||
|
||||
@@ -1453,27 +1453,16 @@ omap_i2c_probe(struct platform_device *pdev)
|
||||
(1000 * omap->speed / 8);
|
||||
}
|
||||
|
||||
if (of_property_present(node, "mux-states")) {
|
||||
struct mux_state *mux_state;
|
||||
|
||||
mux_state = devm_mux_state_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(mux_state)) {
|
||||
r = PTR_ERR(mux_state);
|
||||
dev_dbg(&pdev->dev, "failed to get I2C mux: %d\n", r);
|
||||
goto err_put_pm;
|
||||
}
|
||||
omap->mux_state = mux_state;
|
||||
r = mux_state_select(omap->mux_state);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "failed to select I2C mux: %d\n", r);
|
||||
goto err_put_pm;
|
||||
}
|
||||
omap->mux_state = devm_mux_state_get_optional_selected(&pdev->dev, NULL);
|
||||
if (IS_ERR(omap->mux_state)) {
|
||||
r = PTR_ERR(omap->mux_state);
|
||||
goto err_put_pm;
|
||||
}
|
||||
|
||||
/* reset ASAP, clearing any IRQs */
|
||||
r = omap_i2c_init(omap);
|
||||
if (r)
|
||||
goto err_mux_state_deselect;
|
||||
goto err_put_pm;
|
||||
|
||||
if (omap->rev < OMAP_I2C_OMAP1_REV_2)
|
||||
r = devm_request_irq(&pdev->dev, omap->irq, omap_i2c_omap1_isr,
|
||||
@@ -1515,9 +1504,6 @@ omap_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
err_unuse_clocks:
|
||||
omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0);
|
||||
err_mux_state_deselect:
|
||||
if (omap->mux_state)
|
||||
mux_state_deselect(omap->mux_state);
|
||||
err_put_pm:
|
||||
pm_runtime_put_sync(omap->dev);
|
||||
err_disable_pm:
|
||||
|
||||
@@ -1401,6 +1401,9 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq,
|
||||
rq_data_dir(req) == WRITE &&
|
||||
(md->flags & MMC_BLK_REL_WR);
|
||||
|
||||
if (mqrq->flags & MQRQ_XFER_SINGLE_BLOCK)
|
||||
recovery_mode = 1;
|
||||
|
||||
memset(brq, 0, sizeof(struct mmc_blk_request));
|
||||
|
||||
mmc_crypto_prepare_req(mqrq);
|
||||
@@ -1455,7 +1458,7 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq,
|
||||
* sectors can be read successfully.
|
||||
*/
|
||||
if (recovery_mode)
|
||||
brq->data.blocks = queue_physical_block_size(mq->queue) >> 9;
|
||||
brq->data.blocks = queue_physical_block_size(mq->queue) >> SECTOR_SHIFT;
|
||||
|
||||
/*
|
||||
* Some controllers have HW issues while operating
|
||||
@@ -1540,10 +1543,12 @@ static void mmc_blk_cqe_complete_rq(struct mmc_queue *mq, struct request *req)
|
||||
err = 0;
|
||||
|
||||
if (err) {
|
||||
if (mqrq->retries++ < MMC_CQE_RETRIES)
|
||||
if (mqrq->retries++ < MMC_CQE_RETRIES) {
|
||||
mqrq->flags |= MQRQ_XFER_SINGLE_BLOCK;
|
||||
blk_mq_requeue_request(req, true);
|
||||
else
|
||||
} else {
|
||||
blk_mq_end_request(req, BLK_STS_IOERR);
|
||||
}
|
||||
} else if (mrq->data) {
|
||||
if (blk_update_request(req, BLK_STS_OK, mrq->data->bytes_xfered))
|
||||
blk_mq_requeue_request(req, true);
|
||||
@@ -1776,63 +1781,6 @@ static int mmc_blk_fix_state(struct mmc_card *card, struct request *req)
|
||||
return err;
|
||||
}
|
||||
|
||||
#define MMC_READ_SINGLE_RETRIES 2
|
||||
|
||||
/* Single (native) sector read during recovery */
|
||||
static void mmc_blk_read_single(struct mmc_queue *mq, struct request *req)
|
||||
{
|
||||
struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req);
|
||||
struct mmc_request *mrq = &mqrq->brq.mrq;
|
||||
struct mmc_card *card = mq->card;
|
||||
struct mmc_host *host = card->host;
|
||||
blk_status_t error = BLK_STS_OK;
|
||||
size_t bytes_per_read = queue_physical_block_size(mq->queue);
|
||||
|
||||
do {
|
||||
u32 status;
|
||||
int err;
|
||||
int retries = 0;
|
||||
|
||||
while (retries++ <= MMC_READ_SINGLE_RETRIES) {
|
||||
mmc_blk_rw_rq_prep(mqrq, card, 1, mq);
|
||||
|
||||
mmc_wait_for_req(host, mrq);
|
||||
|
||||
err = mmc_send_status(card, &status);
|
||||
if (err)
|
||||
goto error_exit;
|
||||
|
||||
if (!mmc_host_is_spi(host) &&
|
||||
!mmc_ready_for_data(status)) {
|
||||
err = mmc_blk_fix_state(card, req);
|
||||
if (err)
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
if (!mrq->cmd->error)
|
||||
break;
|
||||
}
|
||||
|
||||
if (mrq->cmd->error ||
|
||||
mrq->data->error ||
|
||||
(!mmc_host_is_spi(host) &&
|
||||
(mrq->cmd->resp[0] & CMD_ERRORS || status & CMD_ERRORS)))
|
||||
error = BLK_STS_IOERR;
|
||||
else
|
||||
error = BLK_STS_OK;
|
||||
|
||||
} while (blk_update_request(req, error, bytes_per_read));
|
||||
|
||||
return;
|
||||
|
||||
error_exit:
|
||||
mrq->data->bytes_xfered = 0;
|
||||
blk_update_request(req, BLK_STS_IOERR, bytes_per_read);
|
||||
/* Let it try the remaining request again */
|
||||
if (mqrq->retries > MMC_MAX_RETRIES - 1)
|
||||
mqrq->retries = MMC_MAX_RETRIES - 1;
|
||||
}
|
||||
|
||||
static inline bool mmc_blk_oor_valid(struct mmc_blk_request *brq)
|
||||
{
|
||||
return !!brq->mrq.sbc;
|
||||
@@ -1968,13 +1916,6 @@ static void mmc_blk_mq_rw_recovery(struct mmc_queue *mq, struct request *req)
|
||||
mqrq->retries = MMC_MAX_RETRIES - MMC_DATA_RETRIES;
|
||||
return;
|
||||
}
|
||||
|
||||
if (rq_data_dir(req) == READ && brq->data.blocks >
|
||||
queue_physical_block_size(mq->queue) >> 9) {
|
||||
/* Read one (native) sector at a time */
|
||||
mmc_blk_read_single(mq, req);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool mmc_blk_rq_error(struct mmc_blk_request *brq)
|
||||
@@ -2085,6 +2026,7 @@ static void mmc_blk_mq_complete_rq(struct mmc_queue *mq, struct request *req)
|
||||
} else if (!blk_rq_bytes(req)) {
|
||||
__blk_mq_end_request(req, BLK_STS_IOERR);
|
||||
} else if (mqrq->retries++ < MMC_MAX_RETRIES) {
|
||||
mqrq->flags |= MQRQ_XFER_SINGLE_BLOCK;
|
||||
blk_mq_requeue_request(req, true);
|
||||
} else {
|
||||
if (mmc_card_removed(mq->card))
|
||||
@@ -3017,14 +2959,14 @@ static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)
|
||||
*/
|
||||
ret = mmc_blk_alloc_rpmb_part(card, md,
|
||||
card->part[idx].part_cfg,
|
||||
card->part[idx].size >> 9,
|
||||
card->part[idx].size >> SECTOR_SHIFT,
|
||||
card->part[idx].name);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (card->part[idx].size) {
|
||||
ret = mmc_blk_alloc_part(card, md,
|
||||
card->part[idx].part_cfg,
|
||||
card->part[idx].size >> 9,
|
||||
card->part[idx].size >> SECTOR_SHIFT,
|
||||
card->part[idx].force_ro,
|
||||
card->part[idx].name,
|
||||
card->part[idx].area_type);
|
||||
@@ -3354,7 +3296,6 @@ static void mmc_blk_shutdown(struct mmc_card *card)
|
||||
_mmc_blk_suspend(card);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mmc_blk_suspend(struct device *dev)
|
||||
{
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
@@ -3380,14 +3321,13 @@ static int mmc_blk_resume(struct device *dev)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mmc_blk_pm_ops, mmc_blk_suspend, mmc_blk_resume);
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(mmc_blk_pm_ops, mmc_blk_suspend, mmc_blk_resume);
|
||||
|
||||
static struct mmc_driver mmc_driver = {
|
||||
.drv = {
|
||||
.name = "mmcblk",
|
||||
.pm = &mmc_blk_pm_ops,
|
||||
.pm = pm_sleep_ptr(&mmc_blk_pm_ops),
|
||||
},
|
||||
.probe = mmc_blk_probe,
|
||||
.remove = mmc_blk_remove,
|
||||
|
||||
@@ -89,6 +89,7 @@ struct mmc_fixup {
|
||||
#define CID_MANFID_MICRON 0x13
|
||||
#define CID_MANFID_SAMSUNG 0x15
|
||||
#define CID_MANFID_APACER 0x27
|
||||
#define CID_MANFID_SANDISK_MMC 0x45
|
||||
#define CID_MANFID_SWISSBIT 0x5D
|
||||
#define CID_MANFID_KINGSTON 0x70
|
||||
#define CID_MANFID_HYNIX 0x90
|
||||
@@ -305,4 +306,14 @@ static inline int mmc_card_no_uhs_ddr50_tuning(const struct mmc_card *c)
|
||||
return c->quirks & MMC_QUIRK_NO_UHS_DDR50_TUNING;
|
||||
}
|
||||
|
||||
static inline int mmc_card_broken_mdt(const struct mmc_card *c)
|
||||
{
|
||||
return c->quirks & MMC_QUIRK_BROKEN_MDT;
|
||||
}
|
||||
|
||||
static inline int mmc_card_fixed_secure_erase_trim_time(const struct mmc_card *c)
|
||||
{
|
||||
return c->quirks & MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -97,7 +97,8 @@ static void mmc_should_fail_request(struct mmc_host *host,
|
||||
return;
|
||||
|
||||
data->error = data_errors[get_random_u32_below(ARRAY_SIZE(data_errors))];
|
||||
data->bytes_xfered = get_random_u32_below(data->bytes_xfered >> 9) << 9;
|
||||
data->bytes_xfered = get_random_u32_below(data->bytes_xfered >> SECTOR_SHIFT)
|
||||
<< SECTOR_SHIFT;
|
||||
}
|
||||
|
||||
#else /* CONFIG_FAIL_MMC_REQUEST */
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
|
||||
static DEFINE_IDA(mmc_host_ida);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mmc_host_class_prepare(struct device *dev)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
@@ -60,15 +59,10 @@ static void mmc_host_class_complete(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mmc_host_class_dev_pm_ops = {
|
||||
.prepare = mmc_host_class_prepare,
|
||||
.complete = mmc_host_class_complete,
|
||||
.prepare = pm_sleep_ptr(mmc_host_class_prepare),
|
||||
.complete = pm_sleep_ptr(mmc_host_class_complete),
|
||||
};
|
||||
|
||||
#define MMC_HOST_CLASS_DEV_PM_OPS (&mmc_host_class_dev_pm_ops)
|
||||
#else
|
||||
#define MMC_HOST_CLASS_DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static void mmc_host_classdev_release(struct device *dev)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
@@ -90,7 +84,7 @@ static const struct class mmc_host_class = {
|
||||
.name = "mmc_host",
|
||||
.dev_release = mmc_host_classdev_release,
|
||||
.shutdown_pre = mmc_host_classdev_shutdown,
|
||||
.pm = MMC_HOST_CLASS_DEV_PM_OPS,
|
||||
.pm = pm_ptr(&mmc_host_class_dev_pm_ops),
|
||||
};
|
||||
|
||||
int mmc_register_host_class(void)
|
||||
@@ -379,8 +373,7 @@ int mmc_of_parse(struct mmc_host *host)
|
||||
host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE_IN_SUSPEND;
|
||||
if (device_property_read_bool(dev, "keep-power-in-suspend"))
|
||||
host->pm_caps |= MMC_PM_KEEP_POWER;
|
||||
if (device_property_read_bool(dev, "wakeup-source") ||
|
||||
device_property_read_bool(dev, "enable-sdio-wakeup")) /* legacy */
|
||||
if (device_property_read_bool(dev, "wakeup-source"))
|
||||
host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
|
||||
if (device_property_read_bool(dev, "mmc-ddr-3_3v"))
|
||||
host->caps |= MMC_CAP_3_3V_DDR;
|
||||
@@ -624,12 +617,24 @@ static int mmc_validate_host_caps(struct mmc_host *host)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* UHS/DDR/HS200 modes require at least 4-bit bus */
|
||||
if (!(caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) &&
|
||||
((caps & (MMC_CAP_UHS | MMC_CAP_DDR)) || (caps2 & MMC_CAP2_HS200))) {
|
||||
dev_warn(dev, "drop UHS/DDR/HS200 support since 1-bit bus only\n");
|
||||
caps &= ~(MMC_CAP_UHS | MMC_CAP_DDR);
|
||||
caps2 &= ~MMC_CAP2_HS200;
|
||||
}
|
||||
|
||||
/* HS400 and HS400ES modes require 8-bit bus */
|
||||
if (caps2 & (MMC_CAP2_HS400_ES | MMC_CAP2_HS400) &&
|
||||
!(caps & MMC_CAP_8_BIT_DATA) && !(caps2 & MMC_CAP2_NO_MMC)) {
|
||||
dev_warn(dev, "drop HS400 support since no 8-bit bus\n");
|
||||
host->caps2 = caps2 & ~MMC_CAP2_HS400_ES & ~MMC_CAP2_HS400;
|
||||
caps2 &= ~(MMC_CAP2_HS400_ES | MMC_CAP2_HS400);
|
||||
}
|
||||
|
||||
host->caps = caps;
|
||||
host->caps2 = caps2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,11 +56,7 @@ static inline int mmc_host_can_access_boot(struct mmc_host *host)
|
||||
|
||||
static inline int mmc_host_can_uhs(struct mmc_host *host)
|
||||
{
|
||||
return host->caps &
|
||||
(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
|
||||
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
|
||||
MMC_CAP_UHS_DDR50) &&
|
||||
host->caps & MMC_CAP_4_BIT_DATA;
|
||||
return host->caps & MMC_CAP_UHS;
|
||||
}
|
||||
|
||||
static inline bool mmc_card_hs200(struct mmc_card *card)
|
||||
|
||||
@@ -671,7 +671,19 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
card->ext_csd.enhanced_rpmb_supported =
|
||||
(card->ext_csd.rel_param &
|
||||
EXT_CSD_WR_REL_PARAM_EN_RPMB_REL_WR);
|
||||
|
||||
if (card->ext_csd.rev >= 9) {
|
||||
/* Adjust production date as per JEDEC JESD84-B51B September 2025 */
|
||||
if (card->cid.year < 2023)
|
||||
card->cid.year += 16;
|
||||
} else {
|
||||
/* Handle vendors with broken MDT reporting */
|
||||
if (mmc_card_broken_mdt(card) && card->cid.year >= 2010 &&
|
||||
card->cid.year <= 2012)
|
||||
card->cid.year += 16;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
* Limit the test area size to the maximum MMC HC erase group size. Note that
|
||||
* the maximum SD allocation unit size is just 4MiB.
|
||||
*/
|
||||
#define TEST_AREA_MAX_SIZE (128 * 1024 * 1024)
|
||||
#define TEST_AREA_MAX_SIZE SZ_128M
|
||||
|
||||
/**
|
||||
* struct mmc_test_pages - pages allocated by 'alloc_pages()'.
|
||||
@@ -51,12 +51,12 @@ struct mmc_test_pages {
|
||||
|
||||
/**
|
||||
* struct mmc_test_mem - allocated memory.
|
||||
* @arr: array of allocations
|
||||
* @cnt: number of allocations
|
||||
* @arr: array of allocations
|
||||
*/
|
||||
struct mmc_test_mem {
|
||||
struct mmc_test_pages *arr;
|
||||
unsigned int cnt;
|
||||
struct mmc_test_pages arr[] __counted_by(cnt);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -135,21 +135,22 @@ struct mmc_test_dbgfs_file {
|
||||
* struct mmc_test_card - test information.
|
||||
* @card: card under test
|
||||
* @scratch: transfer buffer
|
||||
* @buffer: transfer buffer
|
||||
* @highmem: buffer for highmem tests
|
||||
* @area: information for performance tests
|
||||
* @gr: pointer to results of current testcase
|
||||
* @buffer: transfer buffer
|
||||
*/
|
||||
struct mmc_test_card {
|
||||
struct mmc_card *card;
|
||||
|
||||
u8 scratch[BUFFER_SIZE];
|
||||
u8 *buffer;
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
struct page *highmem;
|
||||
#endif
|
||||
struct mmc_test_area area;
|
||||
struct mmc_test_general_result *gr;
|
||||
|
||||
u8 buffer[];
|
||||
};
|
||||
|
||||
enum mmc_test_prep_media {
|
||||
@@ -168,6 +169,11 @@ struct mmc_test_multiple_rw {
|
||||
enum mmc_test_prep_media prepare;
|
||||
};
|
||||
|
||||
static unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
|
||||
1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
|
||||
|
||||
static unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
|
||||
1 << 7, 1 << 8, 1 << 9};
|
||||
/*******************************************************************/
|
||||
/* General helper functions */
|
||||
/*******************************************************************/
|
||||
@@ -315,7 +321,6 @@ static void mmc_test_free_mem(struct mmc_test_mem *mem)
|
||||
while (mem->cnt--)
|
||||
__free_pages(mem->arr[mem->cnt].page,
|
||||
mem->arr[mem->cnt].order);
|
||||
kfree(mem->arr);
|
||||
kfree(mem);
|
||||
}
|
||||
|
||||
@@ -348,14 +353,10 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz,
|
||||
if (max_segs > max_page_cnt)
|
||||
max_segs = max_page_cnt;
|
||||
|
||||
mem = kzalloc_obj(*mem);
|
||||
mem = kzalloc_flex(*mem, arr, max_segs);
|
||||
if (!mem)
|
||||
return NULL;
|
||||
|
||||
mem->arr = kzalloc_objs(*mem->arr, max_segs);
|
||||
if (!mem->arr)
|
||||
goto out_free;
|
||||
|
||||
while (max_page_cnt) {
|
||||
struct page *page;
|
||||
unsigned int order;
|
||||
@@ -506,7 +507,7 @@ static unsigned int mmc_test_rate(uint64_t bytes, struct timespec64 *ts)
|
||||
uint64_t ns;
|
||||
|
||||
ns = timespec64_to_ns(ts);
|
||||
bytes *= 1000000000;
|
||||
bytes *= NSEC_PER_SEC;
|
||||
|
||||
while (ns > UINT_MAX) {
|
||||
bytes >>= 1;
|
||||
@@ -552,7 +553,7 @@ static void mmc_test_save_transfer_result(struct mmc_test_card *test,
|
||||
static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes,
|
||||
struct timespec64 *ts1, struct timespec64 *ts2)
|
||||
{
|
||||
unsigned int rate, iops, sectors = bytes >> 9;
|
||||
unsigned int rate, iops, sectors = bytes >> SECTOR_SHIFT;
|
||||
struct timespec64 ts;
|
||||
|
||||
ts = timespec64_sub(*ts2, *ts1);
|
||||
@@ -577,7 +578,7 @@ static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes,
|
||||
unsigned int count, struct timespec64 *ts1,
|
||||
struct timespec64 *ts2)
|
||||
{
|
||||
unsigned int rate, iops, sectors = bytes >> 9;
|
||||
unsigned int rate, iops, sectors = bytes >> SECTOR_SHIFT;
|
||||
uint64_t tot = bytes * count;
|
||||
struct timespec64 ts;
|
||||
|
||||
@@ -1378,7 +1379,7 @@ static int mmc_test_area_map(struct mmc_test_card *test, unsigned long sz,
|
||||
int err;
|
||||
unsigned int sg_len = 0;
|
||||
|
||||
t->blocks = sz >> 9;
|
||||
t->blocks = sz >> SECTOR_SHIFT;
|
||||
|
||||
if (max_scatter) {
|
||||
err = mmc_test_map_sg_max_scatter(t->mem, sz, t->sg,
|
||||
@@ -1461,7 +1462,7 @@ static int mmc_test_area_io_seq(struct mmc_test_card *test, unsigned long sz,
|
||||
else
|
||||
for (i = 0; i < count && ret == 0; i++) {
|
||||
ret = mmc_test_area_transfer(test, dev_addr, write);
|
||||
dev_addr += sz >> 9;
|
||||
dev_addr += sz >> SECTOR_SHIFT;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
@@ -1504,7 +1505,7 @@ static int mmc_test_area_erase(struct mmc_test_card *test)
|
||||
if (!mmc_card_can_erase(test->card))
|
||||
return 0;
|
||||
|
||||
return mmc_erase(test->card, t->dev_addr, t->max_sz >> 9,
|
||||
return mmc_erase(test->card, t->dev_addr, t->max_sz >> SECTOR_SHIFT,
|
||||
MMC_ERASE_ARG);
|
||||
}
|
||||
|
||||
@@ -1532,7 +1533,7 @@ static int mmc_test_area_cleanup(struct mmc_test_card *test)
|
||||
static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
|
||||
{
|
||||
struct mmc_test_area *t = &test->area;
|
||||
unsigned long min_sz = 64 * 1024, sz;
|
||||
unsigned long min_sz = SZ_64K, sz;
|
||||
int ret;
|
||||
|
||||
ret = mmc_test_set_blksize(test, 512);
|
||||
@@ -1540,9 +1541,9 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
|
||||
return ret;
|
||||
|
||||
/* Make the test area size about 4MiB */
|
||||
sz = (unsigned long)test->card->pref_erase << 9;
|
||||
sz = (unsigned long)test->card->pref_erase << SECTOR_SHIFT;
|
||||
t->max_sz = sz;
|
||||
while (t->max_sz < 4 * 1024 * 1024)
|
||||
while (t->max_sz < SZ_4M)
|
||||
t->max_sz += sz;
|
||||
while (t->max_sz > TEST_AREA_MAX_SIZE && t->max_sz > sz)
|
||||
t->max_sz -= sz;
|
||||
@@ -1552,8 +1553,8 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
|
||||
t->max_seg_sz -= t->max_seg_sz % 512;
|
||||
|
||||
t->max_tfr = t->max_sz;
|
||||
if (t->max_tfr >> 9 > test->card->host->max_blk_count)
|
||||
t->max_tfr = test->card->host->max_blk_count << 9;
|
||||
if (t->max_tfr >> SECTOR_SHIFT > test->card->host->max_blk_count)
|
||||
t->max_tfr = test->card->host->max_blk_count << SECTOR_SHIFT;
|
||||
if (t->max_tfr > test->card->host->max_req_size)
|
||||
t->max_tfr = test->card->host->max_req_size;
|
||||
if (t->max_tfr / t->max_seg_sz > t->max_segs)
|
||||
@@ -1583,7 +1584,7 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
|
||||
}
|
||||
|
||||
t->dev_addr = mmc_test_capacity(test->card) / 2;
|
||||
t->dev_addr -= t->dev_addr % (t->max_sz >> 9);
|
||||
t->dev_addr -= t->dev_addr % (t->max_sz >> SECTOR_SHIFT);
|
||||
|
||||
if (erase) {
|
||||
ret = mmc_test_area_erase(test);
|
||||
@@ -1688,7 +1689,7 @@ static int mmc_test_profile_read_perf(struct mmc_test_card *test)
|
||||
int ret;
|
||||
|
||||
for (sz = 512; sz < t->max_tfr; sz <<= 1) {
|
||||
dev_addr = t->dev_addr + (sz >> 9);
|
||||
dev_addr = t->dev_addr + (sz >> SECTOR_SHIFT);
|
||||
ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -1712,7 +1713,7 @@ static int mmc_test_profile_write_perf(struct mmc_test_card *test)
|
||||
if (ret)
|
||||
return ret;
|
||||
for (sz = 512; sz < t->max_tfr; sz <<= 1) {
|
||||
dev_addr = t->dev_addr + (sz >> 9);
|
||||
dev_addr = t->dev_addr + (sz >> SECTOR_SHIFT);
|
||||
ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -1743,9 +1744,9 @@ static int mmc_test_profile_trim_perf(struct mmc_test_card *test)
|
||||
return RESULT_UNSUP_HOST;
|
||||
|
||||
for (sz = 512; sz < t->max_sz; sz <<= 1) {
|
||||
dev_addr = t->dev_addr + (sz >> 9);
|
||||
dev_addr = t->dev_addr + (sz >> SECTOR_SHIFT);
|
||||
ktime_get_ts64(&ts1);
|
||||
ret = mmc_erase(test->card, dev_addr, sz >> 9, MMC_TRIM_ARG);
|
||||
ret = mmc_erase(test->card, dev_addr, sz >> SECTOR_SHIFT, MMC_TRIM_ARG);
|
||||
if (ret)
|
||||
return ret;
|
||||
ktime_get_ts64(&ts2);
|
||||
@@ -1753,7 +1754,7 @@ static int mmc_test_profile_trim_perf(struct mmc_test_card *test)
|
||||
}
|
||||
dev_addr = t->dev_addr;
|
||||
ktime_get_ts64(&ts1);
|
||||
ret = mmc_erase(test->card, dev_addr, sz >> 9, MMC_TRIM_ARG);
|
||||
ret = mmc_erase(test->card, dev_addr, sz >> SECTOR_SHIFT, MMC_TRIM_ARG);
|
||||
if (ret)
|
||||
return ret;
|
||||
ktime_get_ts64(&ts2);
|
||||
@@ -1775,7 +1776,7 @@ static int mmc_test_seq_read_perf(struct mmc_test_card *test, unsigned long sz)
|
||||
ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_addr += (sz >> 9);
|
||||
dev_addr += (sz >> SECTOR_SHIFT);
|
||||
}
|
||||
ktime_get_ts64(&ts2);
|
||||
mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
|
||||
@@ -1817,7 +1818,7 @@ static int mmc_test_seq_write_perf(struct mmc_test_card *test, unsigned long sz)
|
||||
ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_addr += (sz >> 9);
|
||||
dev_addr += (sz >> SECTOR_SHIFT);
|
||||
}
|
||||
ktime_get_ts64(&ts2);
|
||||
mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
|
||||
@@ -1870,11 +1871,11 @@ static int mmc_test_profile_seq_trim_perf(struct mmc_test_card *test)
|
||||
dev_addr = t->dev_addr;
|
||||
ktime_get_ts64(&ts1);
|
||||
for (i = 0; i < cnt; i++) {
|
||||
ret = mmc_erase(test->card, dev_addr, sz >> 9,
|
||||
ret = mmc_erase(test->card, dev_addr, sz >> SECTOR_SHIFT,
|
||||
MMC_TRIM_ARG);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_addr += (sz >> 9);
|
||||
dev_addr += (sz >> SECTOR_SHIFT);
|
||||
}
|
||||
ktime_get_ts64(&ts2);
|
||||
mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
|
||||
@@ -1901,7 +1902,7 @@ static int mmc_test_rnd_perf(struct mmc_test_card *test, int write, int print,
|
||||
struct timespec64 ts1, ts2, ts;
|
||||
int ret;
|
||||
|
||||
ssz = sz >> 9;
|
||||
ssz = sz >> SECTOR_SHIFT;
|
||||
|
||||
rnd_addr = mmc_test_capacity(test->card) / 4;
|
||||
range1 = rnd_addr / test->card->pref_erase;
|
||||
@@ -2017,10 +2018,10 @@ static int mmc_test_seq_perf(struct mmc_test_card *test, int write,
|
||||
sz = max_tfr;
|
||||
}
|
||||
|
||||
ssz = sz >> 9;
|
||||
ssz = sz >> SECTOR_SHIFT;
|
||||
dev_addr = mmc_test_capacity(test->card) / 4;
|
||||
if (tot_sz > dev_addr << 9)
|
||||
tot_sz = dev_addr << 9;
|
||||
if (tot_sz > dev_addr << SECTOR_SHIFT)
|
||||
tot_sz = dev_addr << SECTOR_SHIFT;
|
||||
cnt = tot_sz / sz;
|
||||
dev_addr &= 0xffff0000; /* Round to 64MiB boundary */
|
||||
|
||||
@@ -2044,17 +2045,17 @@ static int mmc_test_large_seq_perf(struct mmc_test_card *test, int write)
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
ret = mmc_test_seq_perf(test, write, 10 * 1024 * 1024, 1);
|
||||
ret = mmc_test_seq_perf(test, write, 10 * SZ_1M, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
for (i = 0; i < 5; i++) {
|
||||
ret = mmc_test_seq_perf(test, write, 100 * 1024 * 1024, 1);
|
||||
ret = mmc_test_seq_perf(test, write, 100 * SZ_1M, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
for (i = 0; i < 3; i++) {
|
||||
ret = mmc_test_seq_perf(test, write, 1000 * 1024 * 1024, 1);
|
||||
ret = mmc_test_seq_perf(test, write, 1000 * SZ_1M, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@@ -2157,7 +2158,7 @@ static int mmc_test_rw_multiple_sg_len(struct mmc_test_card *test,
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < rw->len && ret == 0; i++) {
|
||||
ret = mmc_test_rw_multiple(test, rw, 512 * 1024, rw->size,
|
||||
ret = mmc_test_rw_multiple(test, rw, SZ_512K, rw->size,
|
||||
rw->sg_len[i]);
|
||||
if (ret)
|
||||
break;
|
||||
@@ -2170,8 +2171,6 @@ static int mmc_test_rw_multiple_sg_len(struct mmc_test_card *test,
|
||||
*/
|
||||
static int mmc_test_profile_mult_write_blocking_perf(struct mmc_test_card *test)
|
||||
{
|
||||
unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
|
||||
1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
|
||||
struct mmc_test_multiple_rw test_data = {
|
||||
.bs = bs,
|
||||
.size = TEST_AREA_MAX_SIZE,
|
||||
@@ -2189,8 +2188,6 @@ static int mmc_test_profile_mult_write_blocking_perf(struct mmc_test_card *test)
|
||||
*/
|
||||
static int mmc_test_profile_mult_write_nonblock_perf(struct mmc_test_card *test)
|
||||
{
|
||||
unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
|
||||
1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
|
||||
struct mmc_test_multiple_rw test_data = {
|
||||
.bs = bs,
|
||||
.size = TEST_AREA_MAX_SIZE,
|
||||
@@ -2208,8 +2205,6 @@ static int mmc_test_profile_mult_write_nonblock_perf(struct mmc_test_card *test)
|
||||
*/
|
||||
static int mmc_test_profile_mult_read_blocking_perf(struct mmc_test_card *test)
|
||||
{
|
||||
unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
|
||||
1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
|
||||
struct mmc_test_multiple_rw test_data = {
|
||||
.bs = bs,
|
||||
.size = TEST_AREA_MAX_SIZE,
|
||||
@@ -2227,8 +2222,6 @@ static int mmc_test_profile_mult_read_blocking_perf(struct mmc_test_card *test)
|
||||
*/
|
||||
static int mmc_test_profile_mult_read_nonblock_perf(struct mmc_test_card *test)
|
||||
{
|
||||
unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
|
||||
1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
|
||||
struct mmc_test_multiple_rw test_data = {
|
||||
.bs = bs,
|
||||
.size = TEST_AREA_MAX_SIZE,
|
||||
@@ -2246,8 +2239,6 @@ static int mmc_test_profile_mult_read_nonblock_perf(struct mmc_test_card *test)
|
||||
*/
|
||||
static int mmc_test_profile_sglen_wr_blocking_perf(struct mmc_test_card *test)
|
||||
{
|
||||
unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
|
||||
1 << 7, 1 << 8, 1 << 9};
|
||||
struct mmc_test_multiple_rw test_data = {
|
||||
.sg_len = sg_len,
|
||||
.size = TEST_AREA_MAX_SIZE,
|
||||
@@ -2265,8 +2256,6 @@ static int mmc_test_profile_sglen_wr_blocking_perf(struct mmc_test_card *test)
|
||||
*/
|
||||
static int mmc_test_profile_sglen_wr_nonblock_perf(struct mmc_test_card *test)
|
||||
{
|
||||
unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
|
||||
1 << 7, 1 << 8, 1 << 9};
|
||||
struct mmc_test_multiple_rw test_data = {
|
||||
.sg_len = sg_len,
|
||||
.size = TEST_AREA_MAX_SIZE,
|
||||
@@ -2284,8 +2273,6 @@ static int mmc_test_profile_sglen_wr_nonblock_perf(struct mmc_test_card *test)
|
||||
*/
|
||||
static int mmc_test_profile_sglen_r_blocking_perf(struct mmc_test_card *test)
|
||||
{
|
||||
unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
|
||||
1 << 7, 1 << 8, 1 << 9};
|
||||
struct mmc_test_multiple_rw test_data = {
|
||||
.sg_len = sg_len,
|
||||
.size = TEST_AREA_MAX_SIZE,
|
||||
@@ -2303,8 +2290,6 @@ static int mmc_test_profile_sglen_r_blocking_perf(struct mmc_test_card *test)
|
||||
*/
|
||||
static int mmc_test_profile_sglen_r_nonblock_perf(struct mmc_test_card *test)
|
||||
{
|
||||
unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
|
||||
1 << 7, 1 << 8, 1 << 9};
|
||||
struct mmc_test_multiple_rw test_data = {
|
||||
.sg_len = sg_len,
|
||||
.size = TEST_AREA_MAX_SIZE,
|
||||
@@ -2456,7 +2441,7 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
if (repeat_cmd && (t->blocks + 1) << 9 > t->max_tfr)
|
||||
if (repeat_cmd && (t->blocks + 1) << SECTOR_SHIFT > t->max_tfr)
|
||||
pr_info("%s: %d commands completed during transfer of %u blocks\n",
|
||||
mmc_hostname(test->card->host), count, t->blocks);
|
||||
|
||||
@@ -3099,7 +3084,7 @@ static ssize_t mtf_test_write(struct file *file, const char __user *buf,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
test = kzalloc_obj(*test);
|
||||
test = kzalloc_flex(*test, buffer, BUFFER_SIZE);
|
||||
if (!test)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -3111,7 +3096,6 @@ static ssize_t mtf_test_write(struct file *file, const char __user *buf,
|
||||
|
||||
test->card = card;
|
||||
|
||||
test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
test->highmem = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, BUFFER_ORDER);
|
||||
if (!test->highmem) {
|
||||
@@ -3120,17 +3104,14 @@ static ssize_t mtf_test_write(struct file *file, const char __user *buf,
|
||||
}
|
||||
#endif
|
||||
|
||||
if (test->buffer) {
|
||||
mutex_lock(&mmc_test_lock);
|
||||
mmc_test_run(test, testcase);
|
||||
mutex_unlock(&mmc_test_lock);
|
||||
}
|
||||
mutex_lock(&mmc_test_lock);
|
||||
mmc_test_run(test, testcase);
|
||||
mutex_unlock(&mmc_test_lock);
|
||||
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
__free_pages(test->highmem, BUFFER_ORDER);
|
||||
free_test_buffer:
|
||||
#endif
|
||||
kfree(test->buffer);
|
||||
kfree(test);
|
||||
|
||||
return count;
|
||||
|
||||
@@ -184,8 +184,13 @@ static void mmc_queue_setup_discard(struct mmc_card *card,
|
||||
return;
|
||||
|
||||
lim->max_hw_discard_sectors = max_discard;
|
||||
if (mmc_card_can_secure_erase_trim(card))
|
||||
lim->max_secure_erase_sectors = max_discard;
|
||||
if (mmc_card_can_secure_erase_trim(card)) {
|
||||
if (mmc_card_fixed_secure_erase_trim_time(card))
|
||||
lim->max_secure_erase_sectors = UINT_MAX >> card->erase_shift;
|
||||
else
|
||||
lim->max_secure_erase_sectors = max_discard;
|
||||
}
|
||||
|
||||
if (mmc_card_can_trim(card) && card->erased_byte == 0)
|
||||
lim->max_write_zeroes_sectors = max_discard;
|
||||
|
||||
|
||||
@@ -61,14 +61,17 @@ enum mmc_drv_op {
|
||||
MMC_DRV_OP_GET_EXT_CSD,
|
||||
};
|
||||
|
||||
#define MQRQ_XFER_SINGLE_BLOCK BIT(0)
|
||||
|
||||
struct mmc_queue_req {
|
||||
struct mmc_blk_request brq;
|
||||
struct scatterlist *sg;
|
||||
enum mmc_drv_op drv_op;
|
||||
int drv_op_result;
|
||||
void *drv_op_data;
|
||||
unsigned int ioc_count;
|
||||
int retries;
|
||||
u8 ioc_count;
|
||||
u8 retries;
|
||||
u8 flags;
|
||||
};
|
||||
|
||||
struct mmc_queue {
|
||||
|
||||
@@ -153,6 +153,15 @@ static const struct mmc_fixup __maybe_unused mmc_blk_fixups[] = {
|
||||
MMC_FIXUP("M62704", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc,
|
||||
MMC_QUIRK_TRIM_BROKEN),
|
||||
|
||||
/*
|
||||
* On Some Kingston eMMCs, secure erase/trim time is independent
|
||||
* of erase size, fixed at approximately 2 seconds.
|
||||
*/
|
||||
MMC_FIXUP("IY2964", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc,
|
||||
MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME),
|
||||
MMC_FIXUP("IB2932", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc,
|
||||
MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME),
|
||||
|
||||
END_FIXUP
|
||||
};
|
||||
|
||||
@@ -170,6 +179,9 @@ static const struct mmc_fixup __maybe_unused mmc_ext_csd_fixups[] = {
|
||||
MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY, CID_MANFID_NUMONYX,
|
||||
0x014e, add_quirk, MMC_QUIRK_BROKEN_HPI, 6),
|
||||
|
||||
MMC_FIXUP(CID_NAME_ANY, CID_MANFID_SANDISK_MMC, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BROKEN_MDT),
|
||||
|
||||
END_FIXUP
|
||||
};
|
||||
|
||||
@@ -213,14 +225,9 @@ static const struct mmc_fixup __maybe_unused sdio_card_init_methods[] = {
|
||||
static inline bool mmc_fixup_of_compatible_match(struct mmc_card *card,
|
||||
const char *compatible)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
for_each_child_of_node(mmc_dev(card->host)->of_node, np) {
|
||||
if (of_device_is_compatible(np, compatible)) {
|
||||
of_node_put(np);
|
||||
for_each_child_of_node_scoped(mmc_dev(card->host)->of_node, np)
|
||||
if (of_device_is_compatible(np, compatible))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -163,10 +163,8 @@ int sdio_set_block_size(struct sdio_func *func, unsigned blksz)
|
||||
if (blksz > func->card->host->max_blk_size)
|
||||
return -EINVAL;
|
||||
|
||||
if (blksz == 0) {
|
||||
blksz = min(func->max_blksize, func->card->host->max_blk_size);
|
||||
blksz = min(blksz, 512u);
|
||||
}
|
||||
if (blksz == 0)
|
||||
blksz = min3(func->max_blksize, func->card->host->max_blk_size, 512u);
|
||||
|
||||
ret = mmc_io_rw_direct(func->card, 1, 0,
|
||||
SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE,
|
||||
|
||||
@@ -429,6 +429,20 @@ config MMC_SDHCI_BCM_KONA
|
||||
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
config MMC_SDHCI_BST
|
||||
tristate "SDHCI support for Black Sesame Technologies BST C1200 controller"
|
||||
depends on ARCH_BST || COMPILE_TEST
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on OF
|
||||
help
|
||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||
for Black Sesame Technologies BST C1200 SoC. The controller is
|
||||
based on Synopsys DesignWare Cores Mobile Storage Controller but
|
||||
requires platform-specific workarounds for hardware limitations.
|
||||
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_F_SDH30
|
||||
tristate "SDHCI support for Fujitsu Semiconductor F_SDH30"
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
@@ -1044,7 +1058,7 @@ config MMC_MTK
|
||||
|
||||
config MMC_SDHCI_MICROCHIP_PIC32
|
||||
tristate "Microchip PIC32MZDA SDHCI support"
|
||||
depends on MMC_SDHCI && PIC32MZDA && MMC_SDHCI_PLTFM
|
||||
depends on MMC_SDHCI && MMC_SDHCI_PLTFM && (PIC32MZDA || COMPILE_TEST)
|
||||
help
|
||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||
for PIC32MZDA platform.
|
||||
|
||||
@@ -13,6 +13,7 @@ obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
|
||||
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
|
||||
obj-$(CONFIG_MMC_SDHCI_UHS2) += sdhci-uhs2.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
|
||||
obj-$(CONFIG_MMC_SDHCI_BST) += sdhci-of-bst.o
|
||||
sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \
|
||||
sdhci-pci-dwc-mshc.o sdhci-pci-gli.o
|
||||
obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
|
||||
|
||||
@@ -629,14 +629,13 @@ static int atmci_of_init(struct atmel_mci *host)
|
||||
{
|
||||
struct device *dev = host->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *cnp;
|
||||
u32 slot_id;
|
||||
int err;
|
||||
|
||||
if (!np)
|
||||
return dev_err_probe(dev, -EINVAL, "device node not found\n");
|
||||
|
||||
for_each_child_of_node(np, cnp) {
|
||||
for_each_child_of_node_scoped(np, cnp) {
|
||||
if (of_property_read_u32(cnp, "reg", &slot_id)) {
|
||||
dev_warn(dev, "reg property is missing for %pOF\n", cnp);
|
||||
continue;
|
||||
@@ -645,7 +644,6 @@ static int atmci_of_init(struct atmel_mci *host)
|
||||
if (slot_id >= ATMCI_MAX_NR_SLOTS) {
|
||||
dev_warn(dev, "can't have more than %d slots\n",
|
||||
ATMCI_MAX_NR_SLOTS);
|
||||
of_node_put(cnp);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -658,10 +656,8 @@ static int atmci_of_init(struct atmel_mci *host)
|
||||
"cd", GPIOD_IN, "cd-gpios");
|
||||
err = PTR_ERR_OR_ZERO(host->pdata[slot_id].detect_pin);
|
||||
if (err) {
|
||||
if (err != -ENOENT) {
|
||||
of_node_put(cnp);
|
||||
if (err != -ENOENT)
|
||||
return err;
|
||||
}
|
||||
host->pdata[slot_id].detect_pin = NULL;
|
||||
}
|
||||
|
||||
@@ -673,10 +669,8 @@ static int atmci_of_init(struct atmel_mci *host)
|
||||
"wp", GPIOD_IN, "wp-gpios");
|
||||
err = PTR_ERR_OR_ZERO(host->pdata[slot_id].wp_pin);
|
||||
if (err) {
|
||||
if (err != -ENOENT) {
|
||||
of_node_put(cnp);
|
||||
if (err != -ENOENT)
|
||||
return err;
|
||||
}
|
||||
host->pdata[slot_id].wp_pin = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ static void octeon_mmc_dmar_fixup_done(struct cvm_mmc_host *host)
|
||||
|
||||
static int octeon_mmc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *cn, *node = pdev->dev.of_node;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct cvm_mmc_host *host;
|
||||
void __iomem *base;
|
||||
int mmc_irq[9];
|
||||
@@ -268,7 +268,7 @@ static int octeon_mmc_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
i = 0;
|
||||
for_each_child_of_node(node, cn) {
|
||||
for_each_child_of_node_scoped(node, cn) {
|
||||
host->slot_pdev[i] =
|
||||
of_platform_device_create(cn, NULL, &pdev->dev);
|
||||
if (!host->slot_pdev[i]) {
|
||||
@@ -279,7 +279,6 @@ static int octeon_mmc_probe(struct platform_device *pdev)
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Error populating slots\n");
|
||||
octeon_mmc_set_shared_power(host, 0);
|
||||
of_node_put(cn);
|
||||
goto error;
|
||||
}
|
||||
i++;
|
||||
|
||||
@@ -905,9 +905,7 @@ static void cvm_mmc_set_clock(struct cvm_mmc_slot *slot, unsigned int clock)
|
||||
{
|
||||
struct mmc_host *mmc = slot->mmc;
|
||||
|
||||
clock = min(clock, mmc->f_max);
|
||||
clock = max(clock, mmc->f_min);
|
||||
slot->clock = clock;
|
||||
slot->clock = clamp(clock, mmc->f_min, mmc->f_max);
|
||||
}
|
||||
|
||||
static int cvm_mmc_init_lowlevel(struct cvm_mmc_slot *slot)
|
||||
|
||||
@@ -73,7 +73,7 @@ static struct platform_driver dw_mci_bluefield_pltfm_driver = {
|
||||
.name = "dwmmc_bluefield",
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.of_match_table = dw_mci_bluefield_match,
|
||||
.pm = &dw_mci_pltfm_pmops,
|
||||
.pm = pm_ptr(&dw_mci_pmops),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -185,8 +185,8 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
|
||||
* HOLD register should be bypassed in case there is no phase shift
|
||||
* applied on CMD/DATA that is sent to the card.
|
||||
*/
|
||||
if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel) && host->slot)
|
||||
set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->slot->flags);
|
||||
if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel))
|
||||
set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->flags);
|
||||
}
|
||||
|
||||
static int dw_mci_exynos_runtime_resume(struct device *dev)
|
||||
@@ -530,11 +530,10 @@ static s8 dw_mci_exynos_get_best_clksmpl(u8 candidates)
|
||||
return loc;
|
||||
}
|
||||
|
||||
static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
|
||||
static int dw_mci_exynos_execute_tuning(struct dw_mci *host, u32 opcode)
|
||||
{
|
||||
struct dw_mci *host = slot->host;
|
||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||
struct mmc_host *mmc = slot->mmc;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
u8 start_smpl, smpl, candidates = 0;
|
||||
s8 found;
|
||||
int ret = 0;
|
||||
|
||||
@@ -57,11 +57,9 @@ static void dw_mci_hi3798cv200_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||
clk_set_phase(priv->drive_clk, 135);
|
||||
}
|
||||
|
||||
static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot,
|
||||
u32 opcode)
|
||||
static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci *host, u32 opcode)
|
||||
{
|
||||
static const int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 };
|
||||
struct dw_mci *host = slot->host;
|
||||
struct hi3798cv200_priv *priv = host->priv;
|
||||
int raise_point = -1, fall_point = -1;
|
||||
int err, prev_err = -1;
|
||||
@@ -72,7 +70,7 @@ static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot,
|
||||
clk_set_phase(priv->sample_clk, degrees[i]);
|
||||
mci_writel(host, RINTSTS, ALL_INT_CLR);
|
||||
|
||||
err = mmc_send_tuning(slot->mmc, opcode, NULL);
|
||||
err = mmc_send_tuning(host->mmc, opcode, NULL);
|
||||
if (!err)
|
||||
found = 1;
|
||||
|
||||
|
||||
@@ -30,13 +30,12 @@ struct dw_mci_hi3798mv200_priv {
|
||||
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];
|
||||
struct mmc_clk_phase phase = host->phase_map.phase[ios->timing];
|
||||
u32 val;
|
||||
|
||||
val = mci_readl(host, ENABLE_SHIFT);
|
||||
@@ -74,25 +73,24 @@ static void dw_mci_hi3798mv200_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||
}
|
||||
}
|
||||
|
||||
static inline int dw_mci_hi3798mv200_enable_tuning(struct dw_mci_slot *slot)
|
||||
static inline int dw_mci_hi3798mv200_enable_tuning(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_hi3798mv200_priv *priv = slot->host->priv;
|
||||
struct dw_mci_hi3798mv200_priv *priv = 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)
|
||||
static inline int dw_mci_hi3798mv200_disable_tuning(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_hi3798mv200_priv *priv = slot->host->priv;
|
||||
struct dw_mci_hi3798mv200_priv *priv = 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,
|
||||
static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci *host,
|
||||
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;
|
||||
@@ -101,7 +99,7 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot,
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
ret = dw_mci_hi3798mv200_enable_tuning(slot);
|
||||
ret = dw_mci_hi3798mv200_enable_tuning(host);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -115,7 +113,7 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot,
|
||||
*
|
||||
* Treat edge(flip) found as an error too.
|
||||
*/
|
||||
err = mmc_send_tuning(slot->mmc, opcode, NULL);
|
||||
err = mmc_send_tuning(host->mmc, opcode, NULL);
|
||||
regval = mci_readl(host, TUNING_CTRL);
|
||||
if (err || (regval & SDMMC_TUNING_FIND_EDGE))
|
||||
err = 1;
|
||||
@@ -136,7 +134,7 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot,
|
||||
}
|
||||
|
||||
tuning_out:
|
||||
ret = dw_mci_hi3798mv200_disable_tuning(slot);
|
||||
ret = dw_mci_hi3798mv200_disable_tuning(host);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -159,9 +157,9 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot,
|
||||
* 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];
|
||||
host->phase_map.phase[MMC_TIMING_MMC_HS200].in_deg = degrees[mid];
|
||||
host->phase_map.phase[MMC_TIMING_MMC_HS400].in_deg = degrees[mid];
|
||||
host->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",
|
||||
@@ -186,8 +184,6 @@ static int dw_mci_hi3798mv200_init(struct dw_mci *host)
|
||||
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),
|
||||
|
||||
@@ -53,7 +53,6 @@
|
||||
#define USE_DLY_MAX_SMPL (14)
|
||||
|
||||
struct k3_priv {
|
||||
int ctrl_id;
|
||||
u32 cur_speed;
|
||||
struct regmap *reg;
|
||||
};
|
||||
@@ -127,26 +126,17 @@ static int dw_mci_hi6220_parse_dt(struct dw_mci *host)
|
||||
if (IS_ERR(priv->reg))
|
||||
priv->reg = NULL;
|
||||
|
||||
priv->ctrl_id = of_alias_get_id(host->dev->of_node, "mshc");
|
||||
if (priv->ctrl_id < 0)
|
||||
priv->ctrl_id = 0;
|
||||
|
||||
if (priv->ctrl_id >= TIMING_MODE)
|
||||
return -EINVAL;
|
||||
|
||||
host->priv = priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_hi6220_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
static int dw_mci_hi6220_switch_voltage(struct dw_mci *host, struct mmc_ios *ios)
|
||||
{
|
||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||
struct k3_priv *priv;
|
||||
struct dw_mci *host;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
int min_uv, max_uv;
|
||||
int ret;
|
||||
|
||||
host = slot->host;
|
||||
priv = host->priv;
|
||||
|
||||
if (!priv || !priv->reg)
|
||||
@@ -199,7 +189,7 @@ static void dw_mci_hi6220_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||
host->bus_hz = clk_get_rate(host->biu_clk);
|
||||
}
|
||||
|
||||
static int dw_mci_hi6220_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
|
||||
static int dw_mci_hi6220_execute_tuning(struct dw_mci *host, u32 opcode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -213,7 +203,7 @@ static const struct dw_mci_drv_data hi6220_data = {
|
||||
.execute_tuning = dw_mci_hi6220_execute_tuning,
|
||||
};
|
||||
|
||||
static void dw_mci_hs_set_timing(struct dw_mci *host, int timing,
|
||||
static int dw_mci_hs_set_timing(struct dw_mci *host, int timing,
|
||||
int smpl_phase)
|
||||
{
|
||||
u32 drv_phase;
|
||||
@@ -222,10 +212,10 @@ static void dw_mci_hs_set_timing(struct dw_mci *host, int timing,
|
||||
u32 enable_shift = 0;
|
||||
u32 reg_value;
|
||||
int ctrl_id;
|
||||
struct k3_priv *priv;
|
||||
|
||||
priv = host->priv;
|
||||
ctrl_id = priv->ctrl_id;
|
||||
ctrl_id = host->mmc->index;
|
||||
if (ctrl_id >= TIMING_MODE)
|
||||
return -EINVAL;
|
||||
|
||||
drv_phase = hs_timing_cfg[ctrl_id][timing].drv_phase;
|
||||
smpl_dly = hs_timing_cfg[ctrl_id][timing].smpl_dly;
|
||||
@@ -262,6 +252,8 @@ static void dw_mci_hs_set_timing(struct dw_mci *host, int timing,
|
||||
|
||||
/* We should delay 1ms wait for timing setting finished. */
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_hi3660_init(struct dw_mci *host)
|
||||
@@ -269,10 +261,9 @@ static int dw_mci_hi3660_init(struct dw_mci *host)
|
||||
mci_writel(host, CDTHRCTL, SDMMC_SET_THLD(SDCARD_RD_THRESHOLD,
|
||||
SDMMC_CARD_RD_THR_EN));
|
||||
|
||||
dw_mci_hs_set_timing(host, MMC_TIMING_LEGACY, -1);
|
||||
host->bus_hz /= (GENCLK_DIV + 1);
|
||||
|
||||
return 0;
|
||||
return dw_mci_hs_set_timing(host, MMC_TIMING_LEGACY, -1);
|
||||
}
|
||||
|
||||
static int dw_mci_set_sel18(struct dw_mci *host, bool set)
|
||||
@@ -364,11 +355,10 @@ static int dw_mci_get_best_clksmpl(unsigned int sample_flag)
|
||||
return middle_range;
|
||||
}
|
||||
|
||||
static int dw_mci_hi3660_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
|
||||
static int dw_mci_hi3660_execute_tuning(struct dw_mci *host, u32 opcode)
|
||||
{
|
||||
int i = 0;
|
||||
struct dw_mci *host = slot->host;
|
||||
struct mmc_host *mmc = slot->mmc;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
int smpl_phase = 0;
|
||||
u32 tuning_sample_flag = 0;
|
||||
int best_clksmpl = 0;
|
||||
@@ -398,21 +388,19 @@ static int dw_mci_hi3660_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_hi3660_switch_voltage(struct mmc_host *mmc,
|
||||
static int dw_mci_hi3660_switch_voltage(struct dw_mci *host,
|
||||
struct mmc_ios *ios)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||
struct k3_priv *priv;
|
||||
struct dw_mci *host;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
int ret = 0;
|
||||
|
||||
host = slot->host;
|
||||
priv = host->priv;
|
||||
|
||||
if (!priv || !priv->reg)
|
||||
return 0;
|
||||
|
||||
if (priv->ctrl_id == DWMMC_SDIO_ID)
|
||||
if (mmc->index == DWMMC_SDIO_ID)
|
||||
return 0;
|
||||
|
||||
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
|
||||
@@ -460,11 +448,6 @@ static int dw_mci_k3_probe(struct platform_device *pdev)
|
||||
return dw_mci_pltfm_register(pdev, drv_data);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dw_mci_k3_dev_pm_ops = {
|
||||
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
|
||||
RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver dw_mci_k3_pltfm_driver = {
|
||||
.probe = dw_mci_k3_probe,
|
||||
.remove = dw_mci_pltfm_remove,
|
||||
@@ -472,7 +455,7 @@ static struct platform_driver dw_mci_k3_pltfm_driver = {
|
||||
.name = "dwmmc_k3",
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.of_match_table = dw_mci_k3_match,
|
||||
.pm = pm_ptr(&dw_mci_k3_dev_pm_ops),
|
||||
.pm = pm_ptr(&dw_mci_pmops),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -10,13 +10,14 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-epf.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include "dw_mmc.h"
|
||||
#include "dw_mmc-pltfm.h"
|
||||
|
||||
#define PCI_BAR_NO 2
|
||||
#define SYNOPSYS_DW_MCI_VENDOR_ID 0x700
|
||||
#define SYNOPSYS_DW_MCI_DEVICE_ID 0x1107
|
||||
/* Defining the Capabilities */
|
||||
@@ -24,11 +25,8 @@
|
||||
MMC_CAP_SD_HIGHSPEED | MMC_CAP_8_BIT_DATA |\
|
||||
MMC_CAP_SDIO_IRQ)
|
||||
|
||||
static struct dw_mci_board pci_board_data = {
|
||||
.caps = DW_MCI_CAPABILITIES,
|
||||
.bus_hz = 33 * 1000 * 1000,
|
||||
.detect_delay_ms = 200,
|
||||
.fifo_depth = 32,
|
||||
static const struct dw_mci_drv_data pci_drv_data = {
|
||||
.common_caps = DW_MCI_CAPABILITIES,
|
||||
};
|
||||
|
||||
static int dw_mci_pci_probe(struct pci_dev *pdev,
|
||||
@@ -41,20 +39,20 @@ static int dw_mci_pci_probe(struct pci_dev *pdev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
|
||||
if (!host)
|
||||
return -ENOMEM;
|
||||
host = dw_mci_alloc_host(&pdev->dev);
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
host->irq = pdev->irq;
|
||||
host->irq_flags = IRQF_SHARED;
|
||||
host->dev = &pdev->dev;
|
||||
host->pdata = &pci_board_data;
|
||||
host->fifo_depth = 32;
|
||||
host->detect_delay_ms = 200;
|
||||
host->bus_hz = 33 * 1000 * 1000;
|
||||
host->drv_data = &pci_drv_data;
|
||||
|
||||
ret = pcim_iomap_regions(pdev, 1 << PCI_BAR_NO, pci_name(pdev));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
host->regs = pcim_iomap_table(pdev)[PCI_BAR_NO];
|
||||
host->regs = pcim_iomap_region(pdev, BAR_2, pci_name(pdev));
|
||||
if (IS_ERR(host->regs))
|
||||
return PTR_ERR(host->regs);
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
@@ -74,11 +72,6 @@ static void dw_mci_pci_remove(struct pci_dev *pdev)
|
||||
dw_mci_remove(host);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dw_mci_pci_dev_pm_ops = {
|
||||
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
|
||||
RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct pci_device_id dw_mci_pci_id[] = {
|
||||
{ PCI_DEVICE(SYNOPSYS_DW_MCI_VENDOR_ID, SYNOPSYS_DW_MCI_DEVICE_ID) },
|
||||
{}
|
||||
@@ -91,7 +84,7 @@ static struct pci_driver dw_mci_pci_driver = {
|
||||
.probe = dw_mci_pci_probe,
|
||||
.remove = dw_mci_pci_remove,
|
||||
.driver = {
|
||||
.pm = pm_ptr(&dw_mci_pci_dev_pm_ops),
|
||||
.pm = pm_ptr(&dw_mci_pmops),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -33,18 +33,16 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
|
||||
struct dw_mci *host;
|
||||
struct resource *regs;
|
||||
|
||||
host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
|
||||
if (!host)
|
||||
return -ENOMEM;
|
||||
host = dw_mci_alloc_host(&pdev->dev);
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
host->irq = platform_get_irq(pdev, 0);
|
||||
if (host->irq < 0)
|
||||
return host->irq;
|
||||
|
||||
host->drv_data = drv_data;
|
||||
host->dev = &pdev->dev;
|
||||
host->irq_flags = 0;
|
||||
host->pdata = pdev->dev.platform_data;
|
||||
|
||||
host->regs = devm_platform_get_and_ioremap_resource(pdev, 0, ®s);
|
||||
if (IS_ERR(host->regs))
|
||||
@@ -58,24 +56,16 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);
|
||||
|
||||
const struct dev_pm_ops dw_mci_pltfm_pmops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
|
||||
dw_mci_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);
|
||||
|
||||
static int dw_mci_socfpga_priv_init(struct dw_mci *host)
|
||||
{
|
||||
struct device_node *np = host->dev->of_node;
|
||||
struct mmc_clk_phase phase;
|
||||
struct regmap *sys_mgr_base_addr;
|
||||
u32 clk_phase[2] = {0}, reg_offset, reg_shift;
|
||||
int i, rc, hs_timing;
|
||||
u32 reg_offset, reg_shift;
|
||||
int hs_timing;
|
||||
|
||||
rc = of_property_read_variable_u32_array(np, "clk-phase-sd-hs", &clk_phase[0], 2, 0);
|
||||
if (rc < 0)
|
||||
phase = host->phase_map.phase[MMC_TIMING_SD_HS];
|
||||
if (!phase.valid)
|
||||
return 0;
|
||||
|
||||
sys_mgr_base_addr = altr_sysmgr_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon");
|
||||
@@ -87,10 +77,10 @@ static int dw_mci_socfpga_priv_init(struct dw_mci *host)
|
||||
of_property_read_u32_index(np, "altr,sysmgr-syscon", 1, ®_offset);
|
||||
of_property_read_u32_index(np, "altr,sysmgr-syscon", 2, ®_shift);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clk_phase); i++)
|
||||
clk_phase[i] /= SOCFPGA_DW_MMC_CLK_PHASE_STEP;
|
||||
phase.in_deg /= SOCFPGA_DW_MMC_CLK_PHASE_STEP;
|
||||
phase.out_deg /= SOCFPGA_DW_MMC_CLK_PHASE_STEP;
|
||||
|
||||
hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1], reg_shift);
|
||||
hs_timing = SYSMGR_SDMMC_CTRL_SET(phase.in_deg, phase.out_deg, reg_shift);
|
||||
regmap_write(sys_mgr_base_addr, reg_offset, hs_timing);
|
||||
|
||||
return 0;
|
||||
@@ -136,7 +126,7 @@ static struct platform_driver dw_mci_pltfm_driver = {
|
||||
.name = "dw_mmc",
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.of_match_table = dw_mci_pltfm_match,
|
||||
.pm = &dw_mci_pltfm_pmops,
|
||||
.pm = pm_ptr(&dw_mci_pmops),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -11,6 +11,6 @@
|
||||
extern int dw_mci_pltfm_register(struct platform_device *pdev,
|
||||
const struct dw_mci_drv_data *drv_data);
|
||||
extern void dw_mci_pltfm_remove(struct platform_device *pdev);
|
||||
extern const struct dev_pm_ops dw_mci_pltfm_pmops;
|
||||
extern const struct dev_pm_ops dw_mci_pmops;
|
||||
|
||||
#endif /* _DW_MMC_PLTFM_H_ */
|
||||
|
||||
@@ -179,7 +179,8 @@ static int rockchip_mmc_set_phase(struct dw_mci *host, bool sample, int degrees)
|
||||
static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||
{
|
||||
struct dw_mci_rockchip_priv_data *priv = host->priv;
|
||||
int ret;
|
||||
struct mmc_clk_phase phase = host->phase_map.phase[ios->timing];
|
||||
int ret, sample_phase, drv_phase;
|
||||
unsigned int cclkin;
|
||||
u32 bus_hz;
|
||||
|
||||
@@ -213,8 +214,15 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||
}
|
||||
|
||||
/* Make sure we use phases which we can enumerate with */
|
||||
if (!IS_ERR(priv->sample_clk) && ios->timing <= MMC_TIMING_SD_HS)
|
||||
rockchip_mmc_set_phase(host, true, priv->default_sample_phase);
|
||||
if (!IS_ERR(priv->sample_clk)) {
|
||||
/* Keep backward compatibility */
|
||||
if (ios->timing <= MMC_TIMING_SD_HS) {
|
||||
sample_phase = phase.valid ? phase.in_deg : priv->default_sample_phase;
|
||||
rockchip_mmc_set_phase(host, true, sample_phase);
|
||||
} else if (phase.valid) {
|
||||
rockchip_mmc_set_phase(host, true, phase.in_deg);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the drive phase offset based on speed mode to achieve hold times.
|
||||
@@ -243,15 +251,13 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||
* same results, for instance).
|
||||
*/
|
||||
if (!IS_ERR(priv->drv_clk)) {
|
||||
int phase;
|
||||
|
||||
/*
|
||||
* In almost all cases a 90 degree phase offset will provide
|
||||
* sufficient hold times across all valid input clock rates
|
||||
* assuming delay_o is not absurd for a given SoC. We'll use
|
||||
* that as a default.
|
||||
*/
|
||||
phase = 90;
|
||||
drv_phase = 90;
|
||||
|
||||
switch (ios->timing) {
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
@@ -261,7 +267,7 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||
* to get the same timings.
|
||||
*/
|
||||
if (ios->bus_width == MMC_BUS_WIDTH_8)
|
||||
phase = 180;
|
||||
drv_phase = 180;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR104:
|
||||
case MMC_TIMING_MMC_HS200:
|
||||
@@ -273,22 +279,24 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||
* SoCs measured this seems to be OK, but it doesn't
|
||||
* hurt to give margin here, so we use 180.
|
||||
*/
|
||||
phase = 180;
|
||||
drv_phase = 180;
|
||||
break;
|
||||
}
|
||||
|
||||
rockchip_mmc_set_phase(host, false, phase);
|
||||
/* Use out phase from phase map first */
|
||||
if (phase.valid)
|
||||
drv_phase = phase.out_deg;
|
||||
rockchip_mmc_set_phase(host, false, drv_phase);
|
||||
}
|
||||
}
|
||||
|
||||
#define TUNING_ITERATION_TO_PHASE(i, num_phases) \
|
||||
(DIV_ROUND_UP((i) * 360, num_phases))
|
||||
|
||||
static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
|
||||
static int dw_mci_rk3288_execute_tuning(struct dw_mci *host, u32 opcode)
|
||||
{
|
||||
struct dw_mci *host = slot->host;
|
||||
struct dw_mci_rockchip_priv_data *priv = host->priv;
|
||||
struct mmc_host *mmc = slot->mmc;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
int ret = 0;
|
||||
int i;
|
||||
bool v, prev_v = 0, first_v;
|
||||
@@ -476,8 +484,8 @@ static int dw_mci_rockchip_init(struct dw_mci *host)
|
||||
struct dw_mci_rockchip_priv_data *priv = host->priv;
|
||||
int ret, i;
|
||||
|
||||
/* It is slot 8 on Rockchip SoCs */
|
||||
host->sdio_id0 = 8;
|
||||
/* SDIO irq is the 8th on Rockchip SoCs */
|
||||
host->sdio_irq = 8;
|
||||
|
||||
if (of_device_is_compatible(host->dev->of_node, "rockchip,rk3288-dw-mshc")) {
|
||||
host->bus_hz /= RK3288_CLKGEN_DIV;
|
||||
|
||||
@@ -53,11 +53,10 @@ static void dw_mci_starfive_set_sample_phase(struct dw_mci *host, u32 smpl_phase
|
||||
mdelay(1);
|
||||
}
|
||||
|
||||
static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot,
|
||||
static int dw_mci_starfive_execute_tuning(struct dw_mci *host,
|
||||
u32 opcode)
|
||||
{
|
||||
static const int grade = MAX_DELAY_CHAIN;
|
||||
struct dw_mci *host = slot->host;
|
||||
int smpl_phase, smpl_raise = -1, smpl_fall = -1;
|
||||
int ret;
|
||||
|
||||
@@ -65,7 +64,7 @@ static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot,
|
||||
dw_mci_starfive_set_sample_phase(host, smpl_phase);
|
||||
mci_writel(host, RINTSTS, ALL_INT_CLR);
|
||||
|
||||
ret = mmc_send_tuning(slot->mmc, opcode, NULL);
|
||||
ret = mmc_send_tuning(host->mmc, opcode, NULL);
|
||||
|
||||
if (!ret && smpl_raise < 0) {
|
||||
smpl_raise = smpl_phase;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -43,8 +43,6 @@ enum dw_mci_cookie {
|
||||
COOKIE_MAPPED, /* mapped by prepare_data() of dwmmc */
|
||||
};
|
||||
|
||||
struct mmc_data;
|
||||
|
||||
enum {
|
||||
TRANS_MODE_PIO = 0,
|
||||
TRANS_MODE_IDMAC,
|
||||
@@ -57,14 +55,14 @@ struct dw_mci_dma_slave {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dw_mci - MMC controller state shared between all slots
|
||||
* struct dw_mci - MMC controller state
|
||||
* @lock: Spinlock protecting the queue and associated data.
|
||||
* @irq_lock: Spinlock protecting the INTMASK setting.
|
||||
* @regs: Pointer to MMIO registers.
|
||||
* @fifo_reg: Pointer to MMIO registers for data FIFO
|
||||
* @sg: Scatterlist entry currently being processed by PIO code, if any.
|
||||
* @sg_miter: PIO mapping scatterlist iterator.
|
||||
* @mrq: The request currently being processed on @slot,
|
||||
* @mrq: The request currently being processed on @host,
|
||||
* or NULL if the controller is idle.
|
||||
* @cmd: The command currently being sent to the card, or NULL.
|
||||
* @data: The data currently being transferred, or NULL if no data
|
||||
@@ -78,7 +76,7 @@ struct dw_mci_dma_slave {
|
||||
* @dma_64bit_address: Whether DMA supports 64-bit address mode or not.
|
||||
* @sg_dma: Bus address of DMA buffer.
|
||||
* @sg_cpu: Virtual address of DMA buffer.
|
||||
* @dma_ops: Pointer to platform-specific DMA callbacks.
|
||||
* @dma_ops: Pointer to DMA callbacks.
|
||||
* @cmd_status: Snapshot of SR taken upon completion of the current
|
||||
* @ring_size: Buffer size for idma descriptors.
|
||||
* command. Only valid when EVENT_CMD_COMPLETE is pending.
|
||||
@@ -96,7 +94,6 @@ struct dw_mci_dma_slave {
|
||||
* @completed_events: Bitmask of events which the state machine has
|
||||
* processed.
|
||||
* @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.
|
||||
* @current_speed: Configured rate of the controller.
|
||||
@@ -104,12 +101,10 @@ struct dw_mci_dma_slave {
|
||||
* @fifoth_val: The value of FIFOTH register.
|
||||
* @verid: Denote Version ID.
|
||||
* @dev: Device associated with the MMC controller.
|
||||
* @pdata: Platform data associated with the MMC controller.
|
||||
* @drv_data: Driver specific data for identified variant of the controller
|
||||
* @priv: Implementation defined private data.
|
||||
* @biu_clk: Pointer to bus interface unit clock instance.
|
||||
* @ciu_clk: Pointer to card interface unit clock instance.
|
||||
* @slot: Slots sharing this MMC controller.
|
||||
* @fifo_depth: depth of FIFO.
|
||||
* @data_addr_override: override fifo reg offset with this value.
|
||||
* @wm_aligned: force fifo watermark equal with data length in PIO mode.
|
||||
@@ -121,23 +116,28 @@ struct dw_mci_dma_slave {
|
||||
* @push_data: Pointer to FIFO push function.
|
||||
* @pull_data: Pointer to FIFO pull function.
|
||||
* @quirks: Set of quirks that apply to specific versions of the IP.
|
||||
* @vqmmc_enabled: Status of vqmmc, should be true or false.
|
||||
* @irq_flags: The flags to be passed to request_irq.
|
||||
* @irq: The irq value to be passed to request_irq.
|
||||
* @sdio_id0: Number of slot0 in the SDIO interrupt registers.
|
||||
* @sdio_irq: SDIO interrupt bit in interrupt registers.
|
||||
* @cmd11_timer: Timer for SD3.0 voltage switch over scheme.
|
||||
* @cto_timer: Timer for broken command transfer over scheme.
|
||||
* @dto_timer: Timer for broken data transfer over scheme.
|
||||
* @mmc: The mmc_host representing this dw_mci.
|
||||
* @flags: Random state bits associated with the host.
|
||||
* @ctype: Card type for this host.
|
||||
* @clock: Clock rate configured by set_ios(). Protected by host->lock.
|
||||
* @clk_old: The last clock value that was requested from core.
|
||||
* @pdev: platform_device registered
|
||||
* @rstc: Reset controller for this host.
|
||||
* @detect_delay_ms: Delay in mS before detecting cards after interrupt.
|
||||
* @phase_map: The map for recording in and out phases for each timing
|
||||
*
|
||||
* Locking
|
||||
* =======
|
||||
*
|
||||
* @lock is a softirq-safe spinlock protecting @queue as well as
|
||||
* @slot, @mrq and @state. These must always be updated
|
||||
* @lock is a softirq-safe spinlock protecting as well as
|
||||
* @mrq and @state. These must always be updated
|
||||
* at the same time while holding @lock.
|
||||
* The @mrq field of struct dw_mci_slot is also protected by @lock,
|
||||
* and must always be written at the same time as the slot is added to
|
||||
* @queue.
|
||||
*
|
||||
* @irq_lock is an irq-safe spinlock protecting the INTMASK register
|
||||
* to allow the interrupt handler to modify it directly. Held for only long
|
||||
@@ -199,7 +199,6 @@ struct dw_mci {
|
||||
unsigned long pending_events;
|
||||
unsigned long completed_events;
|
||||
enum dw_mci_state state;
|
||||
struct list_head queue;
|
||||
|
||||
u32 bus_hz;
|
||||
u32 current_speed;
|
||||
@@ -207,7 +206,6 @@ struct dw_mci {
|
||||
u32 fifoth_val;
|
||||
u16 verid;
|
||||
struct device *dev;
|
||||
struct dw_mci_board *pdata;
|
||||
const struct dw_mci_drv_data *drv_data;
|
||||
void *priv;
|
||||
struct clk *biu_clk;
|
||||
@@ -228,11 +226,10 @@ struct dw_mci {
|
||||
void (*pull_data)(struct dw_mci *host, void *buf, int cnt);
|
||||
|
||||
u32 quirks;
|
||||
bool vqmmc_enabled;
|
||||
unsigned long irq_flags; /* IRQ flags */
|
||||
int irq;
|
||||
|
||||
int sdio_id0;
|
||||
int sdio_irq;
|
||||
|
||||
struct timer_list cmd11_timer;
|
||||
struct timer_list cto_timer;
|
||||
@@ -242,6 +239,19 @@ struct dw_mci {
|
||||
struct fault_attr fail_data_crc;
|
||||
struct hrtimer fault_timer;
|
||||
#endif
|
||||
struct mmc_host *mmc;
|
||||
unsigned long flags;
|
||||
#define DW_MMC_CARD_NEED_INIT 0
|
||||
#define DW_MMC_CARD_NO_LOW_PWR 1
|
||||
#define DW_MMC_CARD_NO_USE_HOLD 2
|
||||
#define DW_MMC_CARD_NEEDS_POLL 3
|
||||
u32 ctype;
|
||||
unsigned int clock;
|
||||
unsigned int clk_old;
|
||||
struct platform_device *pdev;
|
||||
struct reset_control *rstc;
|
||||
u32 detect_delay_ms;
|
||||
struct mmc_clk_phase_map phase_map;
|
||||
};
|
||||
|
||||
/* DMA ops for Internal/External DMAC interface */
|
||||
@@ -255,30 +265,6 @@ struct dw_mci_dma_ops {
|
||||
void (*exit)(struct dw_mci *host);
|
||||
};
|
||||
|
||||
struct dma_pdata;
|
||||
|
||||
/* Board platform data */
|
||||
struct dw_mci_board {
|
||||
unsigned int bus_hz; /* Clock speed at the cclk_in pad */
|
||||
|
||||
u32 caps; /* Capabilities */
|
||||
u32 caps2; /* More capabilities */
|
||||
u32 pm_caps; /* PM capabilities */
|
||||
/*
|
||||
* Override fifo depth. If 0, autodetect it from the FIFOTH register,
|
||||
* but note that this may not be reliable after a bootloader has used
|
||||
* it.
|
||||
*/
|
||||
unsigned int fifo_depth;
|
||||
|
||||
/* delay in mS before detecting cards after interrupt */
|
||||
u32 detect_delay_ms;
|
||||
|
||||
struct reset_control *rstc;
|
||||
struct dw_mci_dma_ops *dma_ops;
|
||||
struct dma_pdata *data;
|
||||
};
|
||||
|
||||
/* Support for longer data read timeout */
|
||||
#define DW_MMC_QUIRK_EXTENDED_TMOUT BIT(0)
|
||||
/* Force 32-bit access to the FIFO */
|
||||
@@ -396,7 +382,6 @@ struct dw_mci_board {
|
||||
#define SDMMC_INT_CMD_DONE BIT(2)
|
||||
#define SDMMC_INT_RESP_ERR BIT(1)
|
||||
#define SDMMC_INT_CD BIT(0)
|
||||
#define SDMMC_INT_ERROR 0xbfc2
|
||||
/* Command register defines */
|
||||
#define SDMMC_CMD_START BIT(31)
|
||||
#define SDMMC_CMD_USE_HOLD_REG BIT(29)
|
||||
@@ -505,84 +490,17 @@ static inline void mci_fifo_l_writeq(void __iomem *addr, u64 value)
|
||||
#define mci_writel(dev, reg, value) \
|
||||
writel_relaxed((value), (dev)->regs + SDMMC_##reg)
|
||||
|
||||
/* 16-bit FIFO access macros */
|
||||
#define mci_readw(dev, reg) \
|
||||
readw_relaxed((dev)->regs + SDMMC_##reg)
|
||||
#define mci_writew(dev, reg, value) \
|
||||
writew_relaxed((value), (dev)->regs + SDMMC_##reg)
|
||||
|
||||
/* 64-bit FIFO access macros */
|
||||
#ifdef readq
|
||||
#define mci_readq(dev, reg) \
|
||||
readq_relaxed((dev)->regs + SDMMC_##reg)
|
||||
#define mci_writeq(dev, reg, value) \
|
||||
writeq_relaxed((value), (dev)->regs + SDMMC_##reg)
|
||||
#else
|
||||
/*
|
||||
* Dummy readq implementation for architectures that don't define it.
|
||||
*
|
||||
* We would assume that none of these architectures would configure
|
||||
* the IP block with a 64bit FIFO width, so this code will never be
|
||||
* executed on those machines. Defining these macros here keeps the
|
||||
* rest of the code free from ifdefs.
|
||||
*/
|
||||
#define mci_readq(dev, reg) \
|
||||
(*(volatile u64 __force *)((dev)->regs + SDMMC_##reg))
|
||||
#define mci_writeq(dev, reg, value) \
|
||||
(*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value))
|
||||
|
||||
#ifndef readq
|
||||
#define __raw_writeq(__value, __reg) \
|
||||
(*(volatile u64 __force *)(__reg) = (__value))
|
||||
#define __raw_readq(__reg) (*(volatile u64 __force *)(__reg))
|
||||
#endif
|
||||
|
||||
extern struct dw_mci *dw_mci_alloc_host(struct device *device);
|
||||
extern int dw_mci_probe(struct dw_mci *host);
|
||||
extern void dw_mci_remove(struct dw_mci *host);
|
||||
#ifdef CONFIG_PM
|
||||
extern int dw_mci_runtime_suspend(struct device *device);
|
||||
extern int dw_mci_runtime_resume(struct device *device);
|
||||
#else
|
||||
static inline int dw_mci_runtime_suspend(struct device *device) { return -EOPNOTSUPP; }
|
||||
static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTSUPP; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct dw_mci_slot - MMC slot state
|
||||
* @mmc: The mmc_host representing this slot.
|
||||
* @host: The MMC controller this slot is using.
|
||||
* @ctype: Card type for this slot.
|
||||
* @mrq: mmc_request currently being processed or waiting to be
|
||||
* processed, or NULL when the slot is idle.
|
||||
* @queue_node: List node for placing this node in the @queue list of
|
||||
* &struct dw_mci.
|
||||
* @clock: Clock rate configured by set_ios(). Protected by host->lock.
|
||||
* @__clk_old: The last clock value that was requested from core.
|
||||
* Keeping track of this helps us to avoid spamming the console.
|
||||
* @flags: Random state bits associated with the slot.
|
||||
* @id: Number of this slot.
|
||||
* @sdio_id: Number of this slot in the SDIO interrupt registers.
|
||||
*/
|
||||
struct dw_mci_slot {
|
||||
struct mmc_host *mmc;
|
||||
struct dw_mci *host;
|
||||
|
||||
u32 ctype;
|
||||
|
||||
struct mmc_request *mrq;
|
||||
struct list_head queue_node;
|
||||
|
||||
unsigned int clock;
|
||||
unsigned int __clk_old;
|
||||
|
||||
unsigned long flags;
|
||||
#define DW_MMC_CARD_PRESENT 0
|
||||
#define DW_MMC_CARD_NEED_INIT 1
|
||||
#define DW_MMC_CARD_NO_LOW_PWR 2
|
||||
#define DW_MMC_CARD_NO_USE_HOLD 3
|
||||
#define DW_MMC_CARD_NEEDS_POLL 4
|
||||
int id;
|
||||
int sdio_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* dw_mci driver data - dw-mshc implementation specific driver data.
|
||||
@@ -609,10 +527,10 @@ struct dw_mci_drv_data {
|
||||
int (*init)(struct dw_mci *host);
|
||||
void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
|
||||
int (*parse_dt)(struct dw_mci *host);
|
||||
int (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode);
|
||||
int (*execute_tuning)(struct dw_mci *host, u32 opcode);
|
||||
int (*prepare_hs400_tuning)(struct dw_mci *host,
|
||||
struct mmc_ios *ios);
|
||||
int (*switch_voltage)(struct mmc_host *mmc,
|
||||
int (*switch_voltage)(struct dw_mci *host,
|
||||
struct mmc_ios *ios);
|
||||
void (*set_data_timeout)(struct dw_mci *host,
|
||||
unsigned int timeout_ns);
|
||||
|
||||
@@ -1052,7 +1052,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
||||
host = mmc_priv(mmc);
|
||||
|
||||
/* Default if no match is JZ4740 */
|
||||
host->version = (enum jz4740_mmc_version)device_get_match_data(&pdev->dev);
|
||||
host->version = (unsigned long)device_get_match_data(&pdev->dev);
|
||||
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret)
|
||||
|
||||
@@ -189,6 +189,12 @@
|
||||
#define LOONGSON2_MMC_DLLVAL_TIMEOUT_US 4000
|
||||
#define LOONGSON2_MMC_TXFULL_TIMEOUT_US 500
|
||||
|
||||
/*
|
||||
* Due to a hardware design flaw, the Loongson-2K0300 may fail to recognize the
|
||||
* CMD48 (SD_READ_EXTR_SINGLE) interrupt.
|
||||
*/
|
||||
#define LOONGSON2_MMC_CMD48_QUIRK BIT(0)
|
||||
|
||||
/* Loongson-2K1000 SDIO2 DMA routing register */
|
||||
#define LS2K1000_SDIO_DMA_MASK GENMASK(17, 15)
|
||||
#define LS2K1000_DMA0_CONF 0x0
|
||||
@@ -245,6 +251,7 @@ struct loongson2_mmc_host {
|
||||
};
|
||||
|
||||
struct loongson2_mmc_pdata {
|
||||
u32 flags;
|
||||
const struct regmap_config *regmap_config;
|
||||
void (*reorder_cmd_data)(struct loongson2_mmc_host *host, struct mmc_command *cmd);
|
||||
void (*fix_data_timeout)(struct loongson2_mmc_host *host, struct mmc_command *cmd);
|
||||
@@ -568,6 +575,12 @@ static void loongson2_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
struct loongson2_mmc_host *host = mmc_priv(mmc);
|
||||
|
||||
if ((host->pdata->flags & LOONGSON2_MMC_CMD48_QUIRK) &&
|
||||
mrq->cmd->opcode == SD_READ_EXTR_SINGLE) {
|
||||
mmc_request_done(mmc, mrq);
|
||||
return;
|
||||
}
|
||||
|
||||
host->cmd_is_stop = 0;
|
||||
host->mrq = mrq;
|
||||
loongson2_mmc_send_request(mmc);
|
||||
@@ -703,14 +716,6 @@ static int ls2k0500_mmc_set_external_dma(struct loongson2_mmc_host *host,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct loongson2_mmc_pdata ls2k0500_mmc_pdata = {
|
||||
.regmap_config = &ls2k0500_mmc_regmap_config,
|
||||
.reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data,
|
||||
.setting_dma = ls2k0500_mmc_set_external_dma,
|
||||
.prepare_dma = loongson2_mmc_prepare_external_dma,
|
||||
.release_dma = loongson2_mmc_release_external_dma,
|
||||
};
|
||||
|
||||
static int ls2k1000_mmc_set_external_dma(struct loongson2_mmc_host *host,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
@@ -735,14 +740,6 @@ static int ls2k1000_mmc_set_external_dma(struct loongson2_mmc_host *host,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct loongson2_mmc_pdata ls2k1000_mmc_pdata = {
|
||||
.regmap_config = &ls2k0500_mmc_regmap_config,
|
||||
.reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data,
|
||||
.setting_dma = ls2k1000_mmc_set_external_dma,
|
||||
.prepare_dma = loongson2_mmc_prepare_external_dma,
|
||||
.release_dma = loongson2_mmc_release_external_dma,
|
||||
};
|
||||
|
||||
static const struct regmap_config ls2k2000_mmc_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
@@ -846,7 +843,6 @@ static int ls2k2000_mmc_set_internal_dma(struct loongson2_mmc_host *host,
|
||||
if (!host->sg_cpu)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(host->sg_cpu, 0, PAGE_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -856,7 +852,36 @@ static void loongson2_mmc_release_internal_dma(struct loongson2_mmc_host *host,
|
||||
dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
|
||||
}
|
||||
|
||||
static struct loongson2_mmc_pdata ls2k0300_mmc_pdata = {
|
||||
.flags = LOONGSON2_MMC_CMD48_QUIRK,
|
||||
.regmap_config = &ls2k2000_mmc_regmap_config,
|
||||
.reorder_cmd_data = ls2k2000_mmc_reorder_cmd_data,
|
||||
.fix_data_timeout = ls2k2000_mmc_fix_data_timeout,
|
||||
.setting_dma = ls2k2000_mmc_set_internal_dma,
|
||||
.prepare_dma = loongson2_mmc_prepare_internal_dma,
|
||||
.release_dma = loongson2_mmc_release_internal_dma,
|
||||
};
|
||||
|
||||
static struct loongson2_mmc_pdata ls2k0500_mmc_pdata = {
|
||||
.flags = 0,
|
||||
.regmap_config = &ls2k0500_mmc_regmap_config,
|
||||
.reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data,
|
||||
.setting_dma = ls2k0500_mmc_set_external_dma,
|
||||
.prepare_dma = loongson2_mmc_prepare_external_dma,
|
||||
.release_dma = loongson2_mmc_release_external_dma,
|
||||
};
|
||||
|
||||
static struct loongson2_mmc_pdata ls2k1000_mmc_pdata = {
|
||||
.flags = 0,
|
||||
.regmap_config = &ls2k0500_mmc_regmap_config,
|
||||
.reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data,
|
||||
.setting_dma = ls2k1000_mmc_set_external_dma,
|
||||
.prepare_dma = loongson2_mmc_prepare_external_dma,
|
||||
.release_dma = loongson2_mmc_release_external_dma,
|
||||
};
|
||||
|
||||
static struct loongson2_mmc_pdata ls2k2000_mmc_pdata = {
|
||||
.flags = 0,
|
||||
.regmap_config = &ls2k2000_mmc_regmap_config,
|
||||
.reorder_cmd_data = ls2k2000_mmc_reorder_cmd_data,
|
||||
.fix_data_timeout = ls2k2000_mmc_fix_data_timeout,
|
||||
@@ -985,6 +1010,7 @@ static void loongson2_mmc_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct of_device_id loongson2_mmc_of_ids[] = {
|
||||
{ .compatible = "loongson,ls2k0300-mmc", .data = &ls2k0300_mmc_pdata },
|
||||
{ .compatible = "loongson,ls2k0500-mmc", .data = &ls2k0500_mmc_pdata },
|
||||
{ .compatible = "loongson,ls2k1000-mmc", .data = &ls2k1000_mmc_pdata },
|
||||
{ .compatible = "loongson,ls2k2000-mmc", .data = &ls2k2000_mmc_pdata },
|
||||
|
||||
@@ -203,9 +203,10 @@
|
||||
#define SDC_CFG_DTOC GENMASK(31, 24) /* RW */
|
||||
|
||||
/* SDC_STS mask */
|
||||
#define SDC_STS_SDCBUSY BIT(0) /* RW */
|
||||
#define SDC_STS_CMDBUSY BIT(1) /* RW */
|
||||
#define SDC_STS_SWR_COMPL BIT(31) /* RW */
|
||||
#define SDC_STS_SDCBUSY BIT(0) /* RW */
|
||||
#define SDC_STS_CMDBUSY BIT(1) /* RW */
|
||||
#define SDC_STS_SPM_RESOURCE_RELEASE BIT(3) /* RW */
|
||||
#define SDC_STS_SWR_COMPL BIT(31) /* RW */
|
||||
|
||||
/* SDC_ADV_CFG0 mask */
|
||||
#define SDC_DAT1_IRQ_TRIGGER BIT(19) /* RW */
|
||||
@@ -448,6 +449,7 @@ struct mtk_mmc_compatible {
|
||||
bool use_internal_cd;
|
||||
bool support_new_tx;
|
||||
bool support_new_rx;
|
||||
bool support_spm_res_release;
|
||||
};
|
||||
|
||||
struct msdc_tune_para {
|
||||
@@ -673,6 +675,25 @@ static const struct mtk_mmc_compatible mt8516_compat = {
|
||||
.stop_dly_sel = 3,
|
||||
};
|
||||
|
||||
static const struct mtk_mmc_compatible mt8189_compat = {
|
||||
.clk_div_bits = 12,
|
||||
.recheck_sdio_irq = false,
|
||||
.hs400_tune = false,
|
||||
.needs_top_base = true,
|
||||
.pad_tune_reg = MSDC_PAD_TUNE0,
|
||||
.async_fifo = true,
|
||||
.data_tune = false,
|
||||
.busy_check = true,
|
||||
.stop_clk_fix = true,
|
||||
.stop_dly_sel = 3,
|
||||
.pop_en_cnt = 8,
|
||||
.enhance_rx = true,
|
||||
.support_64g = true,
|
||||
.support_new_tx = false,
|
||||
.support_new_rx = false,
|
||||
.support_spm_res_release = true,
|
||||
};
|
||||
|
||||
static const struct mtk_mmc_compatible mt8196_compat = {
|
||||
.clk_div_bits = 12,
|
||||
.recheck_sdio_irq = false,
|
||||
@@ -703,6 +724,7 @@ static const struct of_device_id msdc_of_ids[] = {
|
||||
{ .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat},
|
||||
{ .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat},
|
||||
{ .compatible = "mediatek,mt8183-mmc", .data = &mt8183_compat},
|
||||
{ .compatible = "mediatek,mt8189-mmc", .data = &mt8189_compat},
|
||||
{ .compatible = "mediatek,mt8196-mmc", .data = &mt8196_compat},
|
||||
{ .compatible = "mediatek,mt8516-mmc", .data = &mt8516_compat},
|
||||
|
||||
@@ -3296,6 +3318,10 @@ static int msdc_runtime_suspend(struct device *dev)
|
||||
|
||||
__msdc_enable_sdio_irq(host, 0);
|
||||
}
|
||||
|
||||
if (host->dev_comp->support_spm_res_release)
|
||||
sdr_set_bits(host->base + SDC_STS, SDC_STS_SPM_RESOURCE_RELEASE);
|
||||
|
||||
msdc_gate_clock(host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mux/consumer.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pinctrl/pinctrl-state.h>
|
||||
#include <linux/platform_data/tmio.h>
|
||||
@@ -1062,6 +1063,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
||||
struct regulator_dev *rdev;
|
||||
struct renesas_sdhi_dma *dma_priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct mux_state *mux_state;
|
||||
struct tmio_mmc_host *host;
|
||||
struct renesas_sdhi *priv;
|
||||
int num_irqs, irq, ret, i;
|
||||
@@ -1116,6 +1118,10 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
||||
"state_uhs");
|
||||
}
|
||||
|
||||
mux_state = devm_mux_state_get_optional_selected(&pdev->dev, NULL);
|
||||
if (IS_ERR(mux_state))
|
||||
return PTR_ERR(mux_state);
|
||||
|
||||
host = tmio_mmc_host_alloc(pdev, mmc_data);
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
@@ -456,19 +456,15 @@ static int renesas_sdhi_sys_dmac_probe(struct platform_device *pdev)
|
||||
of_device_get_match_data(&pdev->dev), NULL);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops renesas_sdhi_sys_dmac_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
|
||||
tmio_mmc_host_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
static DEFINE_RUNTIME_DEV_PM_OPS(renesas_sdhi_sys_dmac_dev_pm_ops,
|
||||
tmio_mmc_host_runtime_suspend,
|
||||
tmio_mmc_host_runtime_resume, NULL);
|
||||
|
||||
static struct platform_driver renesas_sys_dmac_sdhi_driver = {
|
||||
.driver = {
|
||||
.name = "sh_mobile_sdhi",
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.pm = &renesas_sdhi_sys_dmac_dev_pm_ops,
|
||||
.pm = pm_ptr(&renesas_sdhi_sys_dmac_dev_pm_ops),
|
||||
.of_match_table = renesas_sdhi_sys_dmac_of_match,
|
||||
},
|
||||
.probe = renesas_sdhi_sys_dmac_probe,
|
||||
|
||||
@@ -1181,79 +1181,6 @@ static int sdmmc_get_cd(struct mmc_host *mmc)
|
||||
return cd;
|
||||
}
|
||||
|
||||
static int sd_wait_voltage_stable_1(struct realtek_pci_sdmmc *host)
|
||||
{
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
int err;
|
||||
u8 stat;
|
||||
|
||||
/* Reference to Signal Voltage Switch Sequence in SD spec.
|
||||
* Wait for a period of time so that the card can drive SD_CMD and
|
||||
* SD_DAT[3:0] to low after sending back CMD11 response.
|
||||
*/
|
||||
mdelay(1);
|
||||
|
||||
/* SD_CMD, SD_DAT[3:0] should be driven to low by card;
|
||||
* If either one of SD_CMD,SD_DAT[3:0] is not low,
|
||||
* abort the voltage switch sequence;
|
||||
*/
|
||||
err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (stat & (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
|
||||
SD_DAT1_STATUS | SD_DAT0_STATUS))
|
||||
return -EINVAL;
|
||||
|
||||
/* Stop toggle SD clock */
|
||||
err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
|
||||
0xFF, SD_CLK_FORCE_STOP);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_wait_voltage_stable_2(struct realtek_pci_sdmmc *host)
|
||||
{
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
int err;
|
||||
u8 stat, mask, val;
|
||||
|
||||
/* Wait 1.8V output of voltage regulator in card stable */
|
||||
msleep(50);
|
||||
|
||||
/* Toggle SD clock again */
|
||||
err = rtsx_pci_write_register(pcr, SD_BUS_STAT, 0xFF, SD_CLK_TOGGLE_EN);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Wait for a period of time so that the card can drive
|
||||
* SD_DAT[3:0] to high at 1.8V
|
||||
*/
|
||||
msleep(20);
|
||||
|
||||
/* SD_CMD, SD_DAT[3:0] should be pulled high by host */
|
||||
err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mask = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
|
||||
SD_DAT1_STATUS | SD_DAT0_STATUS;
|
||||
val = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
|
||||
SD_DAT1_STATUS | SD_DAT0_STATUS;
|
||||
if ((stat & mask) != val) {
|
||||
dev_dbg(sdmmc_dev(host),
|
||||
"%s: SD_BUS_STAT = 0x%x\n", __func__, stat);
|
||||
rtsx_pci_write_register(pcr, SD_BUS_STAT,
|
||||
SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
|
||||
rtsx_pci_write_register(pcr, CARD_CLK_EN, 0xFF, 0);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct realtek_pci_sdmmc *host = mmc_priv(mmc);
|
||||
@@ -1281,7 +1208,9 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
voltage = OUTPUT_1V8;
|
||||
|
||||
if (voltage == OUTPUT_1V8) {
|
||||
err = sd_wait_voltage_stable_1(host);
|
||||
/* Stop toggle SD clock */
|
||||
err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
|
||||
0xFF, SD_CLK_FORCE_STOP);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
@@ -1290,16 +1219,11 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
if (voltage == OUTPUT_1V8) {
|
||||
err = sd_wait_voltage_stable_2(host);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
/* Stop toggle SD clock in idle */
|
||||
err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
|
||||
SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
|
||||
if (err < 0)
|
||||
rtsx_pci_write_register(pcr, SD_BUS_STAT,
|
||||
SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
|
||||
|
||||
mutex_unlock(&pcr->pcr_mutex);
|
||||
|
||||
|
||||
@@ -216,6 +216,8 @@
|
||||
#define ESDHC_FLAG_DUMMY_PAD BIT(19)
|
||||
|
||||
#define ESDHC_AUTO_TUNING_WINDOW 3
|
||||
/* 100ms timeout for data inhibit */
|
||||
#define ESDHC_DATA_INHIBIT_WAIT_US 100000
|
||||
|
||||
enum wp_types {
|
||||
ESDHC_WP_NONE, /* no WP, neither controller nor gpio */
|
||||
@@ -321,6 +323,14 @@ static struct esdhc_soc_data usdhc_s32g2_data = {
|
||||
.quirks = SDHCI_QUIRK_NO_LED,
|
||||
};
|
||||
|
||||
static struct esdhc_soc_data usdhc_s32n79_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,
|
||||
.quirks = SDHCI_QUIRK_NO_LED,
|
||||
};
|
||||
|
||||
static struct esdhc_soc_data usdhc_imx7ulp_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
|
||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
||||
@@ -408,6 +418,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx95-usdhc", .data = &usdhc_imx95_data, },
|
||||
{ .compatible = "fsl,imxrt1050-usdhc", .data = &usdhc_imxrt1050_data, },
|
||||
{ .compatible = "nxp,s32g2-usdhc", .data = &usdhc_s32g2_data, },
|
||||
{ .compatible = "nxp,s32n79-usdhc", .data = &usdhc_s32n79_data, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
|
||||
@@ -1453,6 +1464,22 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
|
||||
|
||||
static void esdhc_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
u32 present_state;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* For data or full reset, ensure any active data transfer completes
|
||||
* before resetting to avoid system hang.
|
||||
*/
|
||||
if (mask & (SDHCI_RESET_DATA | SDHCI_RESET_ALL)) {
|
||||
ret = readl_poll_timeout_atomic(host->ioaddr + ESDHC_PRSSTAT, present_state,
|
||||
!(present_state & SDHCI_DATA_INHIBIT), 2,
|
||||
ESDHC_DATA_INHIBIT_WAIT_US);
|
||||
if (ret == -ETIMEDOUT)
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"timeout waiting for data transfer completion\n");
|
||||
}
|
||||
|
||||
sdhci_and_cqhci_reset(host, mask);
|
||||
|
||||
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
||||
@@ -1795,8 +1822,6 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
|
||||
|
||||
of_property_read_u32(np, "fsl,strobe-dll-delay-target",
|
||||
&boarddata->strobe_dll_delay_target);
|
||||
if (of_property_read_bool(np, "no-1-8-v"))
|
||||
host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
|
||||
|
||||
if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
|
||||
boarddata->delay_line = 0;
|
||||
@@ -1815,9 +1840,7 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* HS400/HS400ES require 8 bit bus */
|
||||
if (!(host->mmc->caps & MMC_CAP_8_BIT_DATA))
|
||||
host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES);
|
||||
sdhci_get_property(pdev);
|
||||
|
||||
if (mmc_gpio_get_cd(host->mmc) >= 0)
|
||||
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||
|
||||
@@ -157,6 +157,17 @@
|
||||
#define CQHCI_VENDOR_CFG1 0xA00
|
||||
#define CQHCI_VENDOR_DIS_RST_ON_CQ_EN (0x3 << 13)
|
||||
|
||||
/* non command queue crypto enable register*/
|
||||
#define NONCQ_CRYPTO_PARM 0x70
|
||||
#define NONCQ_CRYPTO_DUN 0x74
|
||||
|
||||
#define DISABLE_CRYPTO BIT(15)
|
||||
#define CRYPTO_GENERAL_ENABLE BIT(1)
|
||||
#define HC_VENDOR_SPECIFIC_FUNC4 0x260
|
||||
|
||||
#define ICE_HCI_PARAM_CCI GENMASK(7, 0)
|
||||
#define ICE_HCI_PARAM_CE GENMASK(8, 8)
|
||||
|
||||
struct sdhci_msm_offset {
|
||||
u32 core_hc_mode;
|
||||
u32 core_mci_data_cnt;
|
||||
@@ -300,6 +311,7 @@ struct sdhci_msm_host {
|
||||
u32 dll_config;
|
||||
u32 ddr_config;
|
||||
bool vqmmc_enabled;
|
||||
bool non_cqe_ice_init_done;
|
||||
};
|
||||
|
||||
static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host)
|
||||
@@ -1914,11 +1926,6 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
|
||||
if (IS_ERR_OR_NULL(ice))
|
||||
return PTR_ERR_OR_ZERO(ice);
|
||||
|
||||
if (qcom_ice_get_supported_key_type(ice) != BLK_CRYPTO_KEY_TYPE_RAW) {
|
||||
dev_warn(dev, "Wrapped keys not supported. Disabling inline encryption support.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
msm_host->ice = ice;
|
||||
|
||||
/* Initialize the blk_crypto_profile */
|
||||
@@ -1932,7 +1939,7 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
|
||||
|
||||
profile->ll_ops = sdhci_msm_crypto_ops;
|
||||
profile->max_dun_bytes_supported = 4;
|
||||
profile->key_types_supported = BLK_CRYPTO_KEY_TYPE_RAW;
|
||||
profile->key_types_supported = qcom_ice_get_supported_key_type(ice);
|
||||
profile->dev = dev;
|
||||
|
||||
/*
|
||||
@@ -2012,9 +2019,111 @@ static int sdhci_msm_ice_keyslot_evict(struct blk_crypto_profile *profile,
|
||||
return qcom_ice_evict_key(msm_host->ice, slot);
|
||||
}
|
||||
|
||||
static int sdhci_msm_ice_derive_sw_secret(struct blk_crypto_profile *profile,
|
||||
const u8 *eph_key, size_t eph_key_size,
|
||||
u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE])
|
||||
{
|
||||
struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile);
|
||||
|
||||
return qcom_ice_derive_sw_secret(msm_host->ice, eph_key, eph_key_size,
|
||||
sw_secret);
|
||||
}
|
||||
|
||||
static int sdhci_msm_ice_import_key(struct blk_crypto_profile *profile,
|
||||
const u8 *raw_key, size_t raw_key_size,
|
||||
u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
|
||||
{
|
||||
struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile);
|
||||
|
||||
return qcom_ice_import_key(msm_host->ice, raw_key, raw_key_size, lt_key);
|
||||
}
|
||||
|
||||
static int sdhci_msm_ice_generate_key(struct blk_crypto_profile *profile,
|
||||
u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
|
||||
{
|
||||
struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile);
|
||||
|
||||
return qcom_ice_generate_key(msm_host->ice, lt_key);
|
||||
}
|
||||
|
||||
static int sdhci_msm_ice_prepare_key(struct blk_crypto_profile *profile,
|
||||
const u8 *lt_key, size_t lt_key_size,
|
||||
u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
|
||||
{
|
||||
struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile);
|
||||
|
||||
return qcom_ice_prepare_key(msm_host->ice, lt_key, lt_key_size, eph_key);
|
||||
}
|
||||
|
||||
static void sdhci_msm_non_cqe_ice_init(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
struct mmc_host *mmc = msm_host->mmc;
|
||||
struct cqhci_host *cq_host = mmc->cqe_private;
|
||||
u32 config;
|
||||
|
||||
config = sdhci_readl(host, HC_VENDOR_SPECIFIC_FUNC4);
|
||||
config &= ~DISABLE_CRYPTO;
|
||||
sdhci_writel(host, config, HC_VENDOR_SPECIFIC_FUNC4);
|
||||
config = cqhci_readl(cq_host, CQHCI_CFG);
|
||||
config |= CRYPTO_GENERAL_ENABLE;
|
||||
cqhci_writel(cq_host, config, CQHCI_CFG);
|
||||
}
|
||||
|
||||
static void sdhci_msm_ice_cfg(struct sdhci_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
struct mmc_host *mmc = msm_host->mmc;
|
||||
struct cqhci_host *cq_host = mmc->cqe_private;
|
||||
unsigned int crypto_params = 0;
|
||||
int key_index;
|
||||
|
||||
if (mrq->crypto_ctx) {
|
||||
if (!msm_host->non_cqe_ice_init_done) {
|
||||
sdhci_msm_non_cqe_ice_init(host);
|
||||
msm_host->non_cqe_ice_init_done = true;
|
||||
}
|
||||
|
||||
key_index = mrq->crypto_key_slot;
|
||||
crypto_params = FIELD_PREP(ICE_HCI_PARAM_CE, 1) |
|
||||
FIELD_PREP(ICE_HCI_PARAM_CCI, key_index);
|
||||
|
||||
cqhci_writel(cq_host, crypto_params, NONCQ_CRYPTO_PARM);
|
||||
cqhci_writel(cq_host, lower_32_bits(mrq->crypto_ctx->bc_dun[0]),
|
||||
NONCQ_CRYPTO_DUN);
|
||||
} else {
|
||||
cqhci_writel(cq_host, crypto_params, NONCQ_CRYPTO_PARM);
|
||||
}
|
||||
|
||||
/* Ensure crypto configuration is written before proceeding */
|
||||
wmb();
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle non-CQE MMC requests with ICE crypto support.
|
||||
* Configures ICE registers before passing the request to
|
||||
* the standard SDHCI handler.
|
||||
*/
|
||||
static void sdhci_msm_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
|
||||
/* Only need to handle non-CQE crypto requests in this path */
|
||||
if (mmc->caps2 & MMC_CAP2_CRYPTO)
|
||||
sdhci_msm_ice_cfg(host, mrq);
|
||||
|
||||
sdhci_request(mmc, mrq);
|
||||
}
|
||||
|
||||
static const struct blk_crypto_ll_ops sdhci_msm_crypto_ops = {
|
||||
.keyslot_program = sdhci_msm_ice_keyslot_program,
|
||||
.keyslot_evict = sdhci_msm_ice_keyslot_evict,
|
||||
.derive_sw_secret = sdhci_msm_ice_derive_sw_secret,
|
||||
.import_key = sdhci_msm_ice_import_key,
|
||||
.generate_key = sdhci_msm_ice_generate_key,
|
||||
.prepare_key = sdhci_msm_ice_prepare_key,
|
||||
};
|
||||
|
||||
#else /* CONFIG_MMC_CRYPTO */
|
||||
@@ -2762,6 +2871,9 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
||||
|
||||
msm_host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_NEED_RSP_BUSY;
|
||||
|
||||
#ifdef CONFIG_MMC_CRYPTO
|
||||
host->mmc_host_ops.request = sdhci_msm_request;
|
||||
#endif
|
||||
/* Set the timeout value to max possible */
|
||||
host->max_timeout_count = 0xF;
|
||||
|
||||
|
||||
@@ -152,8 +152,7 @@ struct sdhci_arasan_clk_ops {
|
||||
* @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw.
|
||||
* @sampleclk_hw: Struct for the clock we might provide to a PHY.
|
||||
* @sampleclk: Pointer to normal 'struct clock' for sampleclk_hw.
|
||||
* @clk_phase_in: Array of Input Clock Phase Delays for all speed modes
|
||||
* @clk_phase_out: Array of Output Clock Phase Delays for all speed modes
|
||||
* @phase_map: Struct for mmc_clk_phase_map provided.
|
||||
* @set_clk_delays: Function pointer for setting Clock Delays
|
||||
* @clk_of_data: Platform specific runtime clock data storage pointer
|
||||
*/
|
||||
@@ -162,8 +161,7 @@ struct sdhci_arasan_clk_data {
|
||||
struct clk *sdcardclk;
|
||||
struct clk_hw sampleclk_hw;
|
||||
struct clk *sampleclk;
|
||||
int clk_phase_in[MMC_TIMING_MMC_HS400 + 1];
|
||||
int clk_phase_out[MMC_TIMING_MMC_HS400 + 1];
|
||||
struct mmc_clk_phase_map phase_map;
|
||||
void (*set_clk_delays)(struct sdhci_host *host);
|
||||
void *clk_of_data;
|
||||
};
|
||||
@@ -1249,36 +1247,9 @@ static void sdhci_arasan_set_clk_delays(struct sdhci_host *host)
|
||||
struct sdhci_arasan_clk_data *clk_data = &sdhci_arasan->clk_data;
|
||||
|
||||
clk_set_phase(clk_data->sampleclk,
|
||||
clk_data->clk_phase_in[host->timing]);
|
||||
clk_data->phase_map.phase[host->timing].in_deg);
|
||||
clk_set_phase(clk_data->sdcardclk,
|
||||
clk_data->clk_phase_out[host->timing]);
|
||||
}
|
||||
|
||||
static void arasan_dt_read_clk_phase(struct device *dev,
|
||||
struct sdhci_arasan_clk_data *clk_data,
|
||||
unsigned int timing, const char *prop)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
u32 clk_phase[2] = {0};
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Read Tap Delay values from DT, if the DT does not contain the
|
||||
* Tap Values then use the pre-defined values.
|
||||
*/
|
||||
ret = of_property_read_variable_u32_array(np, prop, &clk_phase[0],
|
||||
2, 0);
|
||||
if (ret < 0) {
|
||||
dev_dbg(dev, "Using predefined clock phase for %s = %d %d\n",
|
||||
prop, clk_data->clk_phase_in[timing],
|
||||
clk_data->clk_phase_out[timing]);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The values read are Input and Output Clock Delays in order */
|
||||
clk_data->clk_phase_in[timing] = clk_phase[0];
|
||||
clk_data->clk_phase_out[timing] = clk_phase[1];
|
||||
clk_data->phase_map.phase[host->timing].out_deg);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1315,8 +1286,8 @@ static void arasan_dt_parse_clk_phases(struct device *dev,
|
||||
}
|
||||
|
||||
for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) {
|
||||
clk_data->clk_phase_in[i] = zynqmp_iclk_phase[i];
|
||||
clk_data->clk_phase_out[i] = zynqmp_oclk_phase[i];
|
||||
clk_data->phase_map.phase[i].in_deg = zynqmp_iclk_phase[i];
|
||||
clk_data->phase_map.phase[i].out_deg = zynqmp_oclk_phase[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1327,8 +1298,8 @@ static void arasan_dt_parse_clk_phases(struct device *dev,
|
||||
VERSAL_OCLK_PHASE;
|
||||
|
||||
for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) {
|
||||
clk_data->clk_phase_in[i] = versal_iclk_phase[i];
|
||||
clk_data->clk_phase_out[i] = versal_oclk_phase[i];
|
||||
clk_data->phase_map.phase[i].in_deg = versal_iclk_phase[i];
|
||||
clk_data->phase_map.phase[i].out_deg = versal_oclk_phase[i];
|
||||
}
|
||||
}
|
||||
if (of_device_is_compatible(dev->of_node, "xlnx,versal-net-emmc")) {
|
||||
@@ -1338,32 +1309,12 @@ static void arasan_dt_parse_clk_phases(struct device *dev,
|
||||
VERSAL_NET_EMMC_OCLK_PHASE;
|
||||
|
||||
for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) {
|
||||
clk_data->clk_phase_in[i] = versal_net_iclk_phase[i];
|
||||
clk_data->clk_phase_out[i] = versal_net_oclk_phase[i];
|
||||
clk_data->phase_map.phase[i].in_deg = versal_net_iclk_phase[i];
|
||||
clk_data->phase_map.phase[i].out_deg = versal_net_oclk_phase[i];
|
||||
}
|
||||
}
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY,
|
||||
"clk-phase-legacy");
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS,
|
||||
"clk-phase-mmc-hs");
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_SD_HS,
|
||||
"clk-phase-sd-hs");
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR12,
|
||||
"clk-phase-uhs-sdr12");
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR25,
|
||||
"clk-phase-uhs-sdr25");
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR50,
|
||||
"clk-phase-uhs-sdr50");
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR104,
|
||||
"clk-phase-uhs-sdr104");
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_DDR50,
|
||||
"clk-phase-uhs-ddr50");
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_DDR52,
|
||||
"clk-phase-mmc-ddr52");
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS200,
|
||||
"clk-phase-mmc-hs200");
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS400,
|
||||
"clk-phase-mmc-hs400");
|
||||
|
||||
mmc_of_parse_clk_phase(dev, &clk_data->phase_map);
|
||||
}
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_arasan_pdata = {
|
||||
@@ -1512,6 +1463,17 @@ static struct sdhci_arasan_of_data intel_keembay_sdio_data = {
|
||||
.clk_ops = &arasan_clk_ops,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_arasan_axiado_pdata = {
|
||||
.ops = &sdhci_arasan_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_BROKEN_CQE,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data sdhci_arasan_axiado_data = {
|
||||
.pdata = &sdhci_arasan_axiado_pdata,
|
||||
.clk_ops = &arasan_clk_ops,
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_arasan_of_match[] = {
|
||||
/* SoC-specific compatible strings w/ soc_ctl_map */
|
||||
{
|
||||
@@ -1538,6 +1500,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
|
||||
.compatible = "intel,keembay-sdhci-5.1-sdio",
|
||||
.data = &intel_keembay_sdio_data,
|
||||
},
|
||||
{
|
||||
.compatible = "axiado,ax3000-sdhci-5.1-emmc",
|
||||
.data = &sdhci_arasan_axiado_data,
|
||||
},
|
||||
/* Generic compatible below here */
|
||||
{
|
||||
.compatible = "arasan,sdhci-8.9a",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
@@ -519,7 +520,8 @@ static struct platform_driver aspeed_sdhci_driver = {
|
||||
static int aspeed_sdc_probe(struct platform_device *pdev)
|
||||
|
||||
{
|
||||
struct device_node *parent, *child;
|
||||
struct reset_control *reset;
|
||||
struct device_node *parent;
|
||||
struct aspeed_sdc *sdc;
|
||||
int ret;
|
||||
|
||||
@@ -529,6 +531,10 @@ static int aspeed_sdc_probe(struct platform_device *pdev)
|
||||
|
||||
spin_lock_init(&sdc->lock);
|
||||
|
||||
reset = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL);
|
||||
if (IS_ERR(reset))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(reset), "unable to acquire reset\n");
|
||||
|
||||
sdc->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(sdc->clk))
|
||||
return PTR_ERR(sdc->clk);
|
||||
@@ -548,12 +554,11 @@ static int aspeed_sdc_probe(struct platform_device *pdev)
|
||||
dev_set_drvdata(&pdev->dev, sdc);
|
||||
|
||||
parent = pdev->dev.of_node;
|
||||
for_each_available_child_of_node(parent, child) {
|
||||
for_each_available_child_of_node_scoped(parent, child) {
|
||||
struct platform_device *cpdev;
|
||||
|
||||
cpdev = of_platform_device_create(child, NULL, &pdev->dev);
|
||||
if (!cpdev) {
|
||||
of_node_put(child);
|
||||
ret = -ENODEV;
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
523
drivers/mmc/host/sdhci-of-bst.c
Normal file
523
drivers/mmc/host/sdhci-of-bst.c
Normal file
@@ -0,0 +1,523 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* SDHCI driver for Black Sesame Technologies C1200 controller
|
||||
*
|
||||
* Copyright (c) 2025 Black Sesame Technologies
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include "sdhci.h"
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
/* SDHCI register extensions */
|
||||
#define SDHCI_CLOCK_PLL_EN 0x0008
|
||||
#define SDHCI_VENDOR_PTR_R 0xE8
|
||||
|
||||
/* BST-specific tuning parameters */
|
||||
#define BST_TUNING_COUNT 0x20
|
||||
|
||||
/* Synopsys vendor specific registers */
|
||||
#define SDHC_EMMC_CTRL_R_OFFSET 0x2C
|
||||
#define MBIU_CTRL 0x510
|
||||
|
||||
/* MBIU burst control bits */
|
||||
#define BURST_INCR16_EN BIT(3)
|
||||
#define BURST_INCR8_EN BIT(2)
|
||||
#define BURST_INCR4_EN BIT(1)
|
||||
#define BURST_EN (BURST_INCR16_EN | BURST_INCR8_EN | BURST_INCR4_EN)
|
||||
#define MBIU_BURST_MASK GENMASK(3, 0)
|
||||
|
||||
/* CRM (Clock/Reset/Management) register offsets */
|
||||
#define SDEMMC_CRM_BCLK_DIV_CTRL 0x08
|
||||
#define SDEMMC_CRM_TIMER_DIV_CTRL 0x0C
|
||||
#define SDEMMC_CRM_RX_CLK_CTRL 0x14
|
||||
#define SDEMMC_CRM_VOL_CTRL 0x1C
|
||||
#define REG_WR_PROTECT 0x88
|
||||
#define DELAY_CHAIN_SEL 0x94
|
||||
|
||||
/* CRM register values and bit definitions */
|
||||
#define REG_WR_PROTECT_KEY 0x1234abcd
|
||||
#define BST_VOL_STABLE_ON BIT(7)
|
||||
#define BST_TIMER_DIV_MASK GENMASK(7, 0)
|
||||
#define BST_TIMER_DIV_VAL 0x20
|
||||
#define BST_TIMER_LOAD_BIT BIT(8)
|
||||
#define BST_BCLK_EN_BIT BIT(10)
|
||||
#define BST_RX_UPDATE_BIT BIT(11)
|
||||
#define BST_EMMC_CTRL_RST_N BIT(2) /* eMMC card reset control */
|
||||
|
||||
/* Clock frequency limits */
|
||||
#define BST_DEFAULT_MAX_FREQ 200000000UL /* 200 MHz */
|
||||
#define BST_DEFAULT_MIN_FREQ 400000UL /* 400 kHz */
|
||||
|
||||
/* Clock control bit definitions */
|
||||
#define BST_CLOCK_DIV_MASK GENMASK(7, 0)
|
||||
#define BST_CLOCK_DIV_SHIFT 8
|
||||
#define BST_BCLK_DIV_MASK GENMASK(9, 0)
|
||||
|
||||
/* Clock frequency thresholds */
|
||||
#define BST_CLOCK_THRESHOLD_LOW 1500
|
||||
|
||||
/* Clock stability polling parameters */
|
||||
#define BST_CLK_STABLE_POLL_US 1000 /* Poll interval in microseconds */
|
||||
#define BST_CLK_STABLE_TIMEOUT_US 20000 /* Timeout for internal clock stabilization (us) */
|
||||
|
||||
struct sdhci_bst_priv {
|
||||
void __iomem *crm_reg_base;
|
||||
};
|
||||
|
||||
union sdhci_bst_rx_ctrl {
|
||||
struct {
|
||||
u32 rx_revert:1,
|
||||
rx_clk_sel_sec:1,
|
||||
rx_clk_div:4,
|
||||
rx_clk_phase_inner:2,
|
||||
rx_clk_sel_first:1,
|
||||
rx_clk_phase_out:2,
|
||||
rx_clk_en:1,
|
||||
res0:20;
|
||||
};
|
||||
u32 reg;
|
||||
};
|
||||
|
||||
static u32 sdhci_bst_crm_read(struct sdhci_pltfm_host *pltfm_host, u32 offset)
|
||||
{
|
||||
struct sdhci_bst_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
return readl(priv->crm_reg_base + offset);
|
||||
}
|
||||
|
||||
static void sdhci_bst_crm_write(struct sdhci_pltfm_host *pltfm_host, u32 offset, u32 value)
|
||||
{
|
||||
struct sdhci_bst_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
writel(value, priv->crm_reg_base + offset);
|
||||
}
|
||||
|
||||
static int sdhci_bst_wait_int_clk(struct sdhci_host *host)
|
||||
{
|
||||
u16 clk;
|
||||
|
||||
if (read_poll_timeout(sdhci_readw, clk, (clk & SDHCI_CLOCK_INT_STABLE),
|
||||
BST_CLK_STABLE_POLL_US, BST_CLK_STABLE_TIMEOUT_US, false,
|
||||
host, SDHCI_CLOCK_CONTROL))
|
||||
return -EBUSY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int sdhci_bst_get_max_clock(struct sdhci_host *host)
|
||||
{
|
||||
return BST_DEFAULT_MAX_FREQ;
|
||||
}
|
||||
|
||||
static unsigned int sdhci_bst_get_min_clock(struct sdhci_host *host)
|
||||
{
|
||||
return BST_DEFAULT_MIN_FREQ;
|
||||
}
|
||||
|
||||
static void sdhci_bst_enable_clk(struct sdhci_host *host, unsigned int clk)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
unsigned int div;
|
||||
u32 val;
|
||||
union sdhci_bst_rx_ctrl rx_reg;
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
|
||||
/* Calculate clock divider based on target frequency */
|
||||
if (clk == 0) {
|
||||
div = 0;
|
||||
} else if (clk < BST_DEFAULT_MIN_FREQ) {
|
||||
/* Below minimum: use max divider to get closest to min freq */
|
||||
div = BST_DEFAULT_MAX_FREQ / BST_DEFAULT_MIN_FREQ;
|
||||
} else if (clk <= BST_DEFAULT_MAX_FREQ) {
|
||||
/* Normal range: calculate divider directly */
|
||||
div = BST_DEFAULT_MAX_FREQ / clk;
|
||||
} else {
|
||||
/* Above maximum: no division needed */
|
||||
div = 1;
|
||||
}
|
||||
|
||||
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
clk &= ~SDHCI_CLOCK_CARD_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
clk &= ~SDHCI_CLOCK_PLL_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL);
|
||||
val &= ~BST_TIMER_LOAD_BIT;
|
||||
sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL, val);
|
||||
|
||||
val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL);
|
||||
val &= ~BST_TIMER_DIV_MASK;
|
||||
val |= BST_TIMER_DIV_VAL;
|
||||
sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL, val);
|
||||
|
||||
val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL);
|
||||
val |= BST_TIMER_LOAD_BIT;
|
||||
sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL, val);
|
||||
|
||||
val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL);
|
||||
val &= ~BST_RX_UPDATE_BIT;
|
||||
sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, val);
|
||||
|
||||
rx_reg.reg = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL);
|
||||
|
||||
rx_reg.rx_revert = 0;
|
||||
rx_reg.rx_clk_sel_sec = 1;
|
||||
rx_reg.rx_clk_div = 4;
|
||||
rx_reg.rx_clk_phase_inner = 2;
|
||||
rx_reg.rx_clk_sel_first = 0;
|
||||
rx_reg.rx_clk_phase_out = 2;
|
||||
|
||||
sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, rx_reg.reg);
|
||||
|
||||
val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL);
|
||||
val |= BST_RX_UPDATE_BIT;
|
||||
sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, val);
|
||||
|
||||
/* Disable clock first */
|
||||
val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL);
|
||||
val &= ~BST_BCLK_EN_BIT;
|
||||
sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val);
|
||||
|
||||
/* Setup clock divider */
|
||||
val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL);
|
||||
val &= ~BST_BCLK_DIV_MASK;
|
||||
val |= div;
|
||||
sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val);
|
||||
|
||||
/* Enable clock */
|
||||
val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL);
|
||||
val |= BST_BCLK_EN_BIT;
|
||||
sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val);
|
||||
|
||||
/* RMW the clock divider bits to avoid clobbering other fields */
|
||||
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
clk &= ~(BST_CLOCK_DIV_MASK << BST_CLOCK_DIV_SHIFT);
|
||||
clk |= (div & BST_CLOCK_DIV_MASK) << BST_CLOCK_DIV_SHIFT;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
clk |= SDHCI_CLOCK_PLL_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
clk |= SDHCI_CLOCK_CARD_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
clk |= SDHCI_CLOCK_INT_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
}
|
||||
|
||||
static void sdhci_bst_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
/* Turn off card/internal/PLL clocks when clock==0 to avoid idle power */
|
||||
u32 clk_reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
if (!clock) {
|
||||
clk_reg &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN | SDHCI_CLOCK_PLL_EN);
|
||||
sdhci_writew(host, clk_reg, SDHCI_CLOCK_CONTROL);
|
||||
return;
|
||||
}
|
||||
sdhci_bst_enable_clk(host, clock);
|
||||
}
|
||||
|
||||
/*
|
||||
* sdhci_bst_reset - Reset the SDHCI host controller with special
|
||||
* handling for eMMC card reset control.
|
||||
*/
|
||||
static void sdhci_bst_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
u16 vendor_ptr, emmc_ctrl_reg;
|
||||
u32 reg;
|
||||
|
||||
if (host->mmc->caps2 & MMC_CAP2_NO_SD) {
|
||||
vendor_ptr = sdhci_readw(host, SDHCI_VENDOR_PTR_R);
|
||||
emmc_ctrl_reg = vendor_ptr + SDHC_EMMC_CTRL_R_OFFSET;
|
||||
|
||||
reg = sdhci_readw(host, emmc_ctrl_reg);
|
||||
reg &= ~BST_EMMC_CTRL_RST_N;
|
||||
sdhci_writew(host, reg, emmc_ctrl_reg);
|
||||
sdhci_reset(host, mask);
|
||||
usleep_range(10, 20);
|
||||
reg = sdhci_readw(host, emmc_ctrl_reg);
|
||||
reg |= BST_EMMC_CTRL_RST_N;
|
||||
sdhci_writew(host, reg, emmc_ctrl_reg);
|
||||
} else {
|
||||
sdhci_reset(host, mask);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set timeout control register to maximum value (0xE) */
|
||||
static void sdhci_bst_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
{
|
||||
sdhci_writeb(host, 0xE, SDHCI_TIMEOUT_CONTROL);
|
||||
}
|
||||
|
||||
/*
|
||||
* sdhci_bst_set_power - Set power mode and voltage, also configures
|
||||
* MBIU burst mode control based on power state.
|
||||
*/
|
||||
static void sdhci_bst_set_power(struct sdhci_host *host, unsigned char mode,
|
||||
unsigned short vdd)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
u32 reg;
|
||||
u32 val;
|
||||
|
||||
sdhci_set_power(host, mode, vdd);
|
||||
|
||||
if (mode == MMC_POWER_OFF) {
|
||||
/* Disable MBIU burst mode */
|
||||
reg = sdhci_readw(host, MBIU_CTRL);
|
||||
reg &= ~BURST_EN; /* Clear all burst enable bits */
|
||||
sdhci_writew(host, reg, MBIU_CTRL);
|
||||
|
||||
/* Disable CRM BCLK */
|
||||
val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL);
|
||||
val &= ~BST_BCLK_EN_BIT;
|
||||
sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val);
|
||||
|
||||
/* Disable RX clock */
|
||||
val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL);
|
||||
val &= ~BST_RX_UPDATE_BIT;
|
||||
sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, val);
|
||||
|
||||
/* Turn off voltage stable power */
|
||||
val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_VOL_CTRL);
|
||||
val &= ~BST_VOL_STABLE_ON;
|
||||
sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_VOL_CTRL, val);
|
||||
} else {
|
||||
/* Configure burst mode only when powered on */
|
||||
reg = sdhci_readw(host, MBIU_CTRL);
|
||||
reg &= ~MBIU_BURST_MASK; /* Clear burst related bits */
|
||||
reg |= BURST_EN; /* Enable burst mode for better bandwidth */
|
||||
sdhci_writew(host, reg, MBIU_CTRL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* sdhci_bst_execute_tuning - Execute tuning procedure by trying different
|
||||
* delay chain values and selecting the optimal one.
|
||||
*/
|
||||
static int sdhci_bst_execute_tuning(struct sdhci_host *host, u32 opcode)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
int ret = 0, error;
|
||||
int first_start = -1, first_end = -1, best = 0;
|
||||
int second_start = -1, second_end = -1, has_failure = 0;
|
||||
int i;
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
|
||||
for (i = 0; i < BST_TUNING_COUNT; i++) {
|
||||
/* Protected write */
|
||||
sdhci_bst_crm_write(pltfm_host, REG_WR_PROTECT, REG_WR_PROTECT_KEY);
|
||||
/* Write tuning value */
|
||||
sdhci_bst_crm_write(pltfm_host, DELAY_CHAIN_SEL, (1ul << i) - 1);
|
||||
|
||||
/* Wait for internal clock stable before tuning */
|
||||
if (sdhci_bst_wait_int_clk(host)) {
|
||||
dev_err(mmc_dev(host->mmc), "Internal clock never stabilised\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = mmc_send_tuning(host->mmc, opcode, &error);
|
||||
if (ret != 0) {
|
||||
has_failure = 1;
|
||||
} else {
|
||||
if (has_failure == 0) {
|
||||
if (first_start == -1)
|
||||
first_start = i;
|
||||
first_end = i;
|
||||
} else {
|
||||
if (second_start == -1)
|
||||
second_start = i;
|
||||
second_end = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate best tuning value */
|
||||
if (first_end - first_start >= second_end - second_start)
|
||||
best = ((first_end - first_start) >> 1) + first_start;
|
||||
else
|
||||
best = ((second_end - second_start) >> 1) + second_start;
|
||||
|
||||
if (best < 0)
|
||||
best = 0;
|
||||
|
||||
sdhci_bst_crm_write(pltfm_host, DELAY_CHAIN_SEL, (1ul << best) - 1);
|
||||
/* Confirm internal clock stable after setting best tuning value */
|
||||
if (sdhci_bst_wait_int_clk(host)) {
|
||||
dev_err(mmc_dev(host->mmc), "Internal clock never stabilised\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Enable voltage stable power for voltage switch */
|
||||
static void sdhci_bst_voltage_switch(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
/* Enable voltage stable power */
|
||||
sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_VOL_CTRL, BST_VOL_STABLE_ON);
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_bst_ops = {
|
||||
.set_clock = sdhci_bst_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
.get_min_clock = sdhci_bst_get_min_clock,
|
||||
.get_max_clock = sdhci_bst_get_max_clock,
|
||||
.reset = sdhci_bst_reset,
|
||||
.set_power = sdhci_bst_set_power,
|
||||
.set_timeout = sdhci_bst_set_timeout,
|
||||
.platform_execute_tuning = sdhci_bst_execute_tuning,
|
||||
.voltage_switch = sdhci_bst_voltage_switch,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_bst_pdata = {
|
||||
.ops = &sdhci_bst_ops,
|
||||
.quirks = SDHCI_QUIRK_BROKEN_ADMA |
|
||||
SDHCI_QUIRK_DELAY_AFTER_POWER |
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_INVERTED_WRITE_PROTECT,
|
||||
.quirks2 = SDHCI_QUIRK2_BROKEN_DDR50 |
|
||||
SDHCI_QUIRK2_TUNING_WORK_AROUND |
|
||||
SDHCI_QUIRK2_ACMD23_BROKEN,
|
||||
};
|
||||
|
||||
static void sdhci_bst_free_bounce_buffer(struct sdhci_host *host)
|
||||
{
|
||||
if (host->bounce_buffer) {
|
||||
dma_free_coherent(mmc_dev(host->mmc), host->bounce_buffer_size,
|
||||
host->bounce_buffer, host->bounce_addr);
|
||||
host->bounce_buffer = NULL;
|
||||
}
|
||||
of_reserved_mem_device_release(mmc_dev(host->mmc));
|
||||
}
|
||||
|
||||
static int sdhci_bst_alloc_bounce_buffer(struct sdhci_host *host)
|
||||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
unsigned int bounce_size;
|
||||
int ret;
|
||||
|
||||
/* Fixed SRAM bounce size to 32KB: verified config under 32-bit DMA addressing limit */
|
||||
bounce_size = SZ_32K;
|
||||
|
||||
ret = of_reserved_mem_device_init_by_idx(mmc_dev(mmc), mmc_dev(mmc)->of_node, 0);
|
||||
if (ret) {
|
||||
dev_err(mmc_dev(mmc), "Failed to initialize reserved memory\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
host->bounce_buffer = dma_alloc_coherent(mmc_dev(mmc), bounce_size,
|
||||
&host->bounce_addr, GFP_KERNEL);
|
||||
if (!host->bounce_buffer) {
|
||||
of_reserved_mem_device_release(mmc_dev(mmc));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
host->bounce_buffer_size = bounce_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdhci_bst_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_bst_priv *priv;
|
||||
int err;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_bst_pdata, sizeof(struct sdhci_bst_priv));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
priv = sdhci_pltfm_priv(pltfm_host); /* Get platform private data */
|
||||
|
||||
err = mmc_of_parse(host->mmc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
|
||||
/* Get CRM registers from the second reg entry */
|
||||
priv->crm_reg_base = devm_platform_ioremap_resource(pdev, 1);
|
||||
if (IS_ERR(priv->crm_reg_base)) {
|
||||
err = PTR_ERR(priv->crm_reg_base);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Silicon constraints for BST C1200:
|
||||
* - System RAM base is 0x800000000 (above 32-bit addressable range)
|
||||
* - The eMMC controller DMA engine is limited to 32-bit addressing
|
||||
* - SMMU cannot be used on this path due to hardware design flaws
|
||||
* - These are fixed in silicon and cannot be changed in software
|
||||
*
|
||||
* Bus/controller mapping:
|
||||
* - No registers are available to reprogram the address mapping
|
||||
* - The 32-bit DMA limit is a hard constraint of the controller IP
|
||||
*
|
||||
* Given these constraints, an SRAM-based bounce buffer in the 32-bit
|
||||
* address space is required to enable eMMC DMA on this platform.
|
||||
*/
|
||||
err = sdhci_bst_alloc_bounce_buffer(host);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to allocate bounce buffer: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = sdhci_add_host(host);
|
||||
if (err)
|
||||
goto err_free_bounce_buffer;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_bounce_buffer:
|
||||
sdhci_bst_free_bounce_buffer(host);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void sdhci_bst_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
|
||||
sdhci_bst_free_bounce_buffer(host);
|
||||
sdhci_pltfm_remove(pdev);
|
||||
}
|
||||
|
||||
static const struct of_device_id sdhci_bst_ids[] = {
|
||||
{ .compatible = "bst,c1200-sdhci" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_bst_ids);
|
||||
|
||||
static struct platform_driver sdhci_bst_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-bst",
|
||||
.of_match_table = sdhci_bst_ids,
|
||||
},
|
||||
.probe = sdhci_bst_probe,
|
||||
.remove = sdhci_bst_remove,
|
||||
};
|
||||
module_platform_driver(sdhci_bst_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Black Sesame Technologies SDHCI driver (BST)");
|
||||
MODULE_AUTHOR("Black Sesame Technologies Co., Ltd.");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -40,7 +40,10 @@
|
||||
#define DWCMSHC_AREA1_MASK GENMASK(11, 0)
|
||||
/* Offset inside the vendor area 1 */
|
||||
#define DWCMSHC_HOST_CTRL3 0x8
|
||||
#define DWCMSHC_HOST_CTRL3_CMD_CONFLICT BIT(0)
|
||||
#define DWCMSHC_EMMC_CONTROL 0x2c
|
||||
/* HPE GSC SoC MSHCCS register */
|
||||
#define HPE_GSC_MSHCCS_SCGSYNCDIS BIT(18)
|
||||
#define DWCMSHC_CARD_IS_EMMC BIT(0)
|
||||
#define DWCMSHC_ENHANCED_STROBE BIT(8)
|
||||
#define DWCMSHC_EMMC_ATCTRL 0x40
|
||||
@@ -128,9 +131,11 @@
|
||||
#define PHY_CNFG_PHY_PWRGOOD_MASK BIT_MASK(1) /* bit [1] */
|
||||
#define PHY_CNFG_PAD_SP_MASK GENMASK(19, 16) /* bits [19:16] */
|
||||
#define PHY_CNFG_PAD_SP 0x0c /* PMOS TX drive strength */
|
||||
#define PHY_CNFG_PAD_SP_k230 0x09 /* PMOS TX drive strength for k230 */
|
||||
#define PHY_CNFG_PAD_SP_SG2042 0x09 /* PMOS TX drive strength for SG2042 */
|
||||
#define PHY_CNFG_PAD_SN_MASK GENMASK(23, 20) /* bits [23:20] */
|
||||
#define PHY_CNFG_PAD_SN 0x0c /* NMOS TX drive strength */
|
||||
#define PHY_CNFG_PAD_SN_k230 0x08 /* NMOS TX drive strength for k230 */
|
||||
#define PHY_CNFG_PAD_SN_SG2042 0x08 /* NMOS TX drive strength for SG2042 */
|
||||
|
||||
/* PHY command/response pad settings */
|
||||
@@ -153,14 +158,21 @@
|
||||
#define PHY_PAD_RXSEL_3V3 0x2 /* Receiver type select for 3.3V */
|
||||
|
||||
#define PHY_PAD_WEAKPULL_MASK GENMASK(4, 3) /* bits [4:3] */
|
||||
#define PHY_PAD_WEAKPULL_DISABLED 0x0 /* Weak pull up and pull down disabled */
|
||||
#define PHY_PAD_WEAKPULL_PULLUP 0x1 /* Weak pull up enabled */
|
||||
#define PHY_PAD_WEAKPULL_PULLDOWN 0x2 /* Weak pull down enabled */
|
||||
|
||||
#define PHY_PAD_TXSLEW_CTRL_P_MASK GENMASK(8, 5) /* bits [8:5] */
|
||||
#define PHY_PAD_TXSLEW_CTRL_P 0x3 /* Slew control for P-Type pad TX */
|
||||
#define PHY_PAD_TXSLEW_CTRL_P_k230 0x2 /* Slew control for P-Type pad TX for k230 */
|
||||
#define PHY_PAD_TXSLEW_CTRL_N_MASK GENMASK(12, 9) /* bits [12:9] */
|
||||
#define PHY_PAD_TXSLEW_CTRL_N 0x3 /* Slew control for N-Type pad TX */
|
||||
#define PHY_PAD_TXSLEW_CTRL_N_SG2042 0x2 /* Slew control for N-Type pad TX for SG2042 */
|
||||
#define PHY_PAD_TXSLEW_CTRL_N_k230 0x2 /* Slew control for N-Type pad TX for k230 */
|
||||
|
||||
/* PHY Common DelayLine config settings */
|
||||
#define PHY_COMMDL_CNFG (DWC_MSHC_PTR_PHY_R + 0x1c)
|
||||
#define PHY_COMMDL_CNFG_DLSTEP_SEL BIT(0) /* DelayLine outputs on PAD enabled */
|
||||
|
||||
/* PHY CLK delay line settings */
|
||||
#define PHY_SDCLKDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x1d)
|
||||
@@ -174,7 +186,10 @@
|
||||
#define PHY_SDCLKDL_DC_HS400 0x18 /* delay code for HS400 mode */
|
||||
|
||||
#define PHY_SMPLDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x20)
|
||||
#define PHY_SMPLDL_CNFG_EXTDLY_EN BIT(0)
|
||||
#define PHY_SMPLDL_CNFG_BYPASS_EN BIT(1)
|
||||
#define PHY_SMPLDL_CNFG_INPSEL_MASK GENMASK(3, 2) /* bits [3:2] */
|
||||
#define PHY_SMPLDL_CNFG_INPSEL 0x3 /* delay line input source */
|
||||
|
||||
/* PHY drift_cclk_rx delay line configuration setting */
|
||||
#define PHY_ATDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x21)
|
||||
@@ -224,9 +239,20 @@
|
||||
SDHCI_TRNS_BLK_CNT_EN | \
|
||||
SDHCI_TRNS_DMA)
|
||||
|
||||
#define to_pltfm_data(priv, name) \
|
||||
container_of((priv)->dwcmshc_pdata, struct name##_pltfm_data, dwcmshc_pdata)
|
||||
|
||||
/* SMC call for BlueField-3 eMMC RST_N */
|
||||
#define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007
|
||||
|
||||
/* Canaan specific Registers */
|
||||
#define SD0_CTRL 0x00
|
||||
#define SD0_HOST_REG_VOL_STABLE BIT(4)
|
||||
#define SD0_CARD_WRITE_PROT BIT(6)
|
||||
#define SD1_CTRL 0x08
|
||||
#define SD1_HOST_REG_VOL_STABLE BIT(0)
|
||||
#define SD1_CARD_WRITE_PROT BIT(2)
|
||||
|
||||
/* Eswin specific Registers */
|
||||
#define EIC7700_CARD_CLK_STABLE BIT(28)
|
||||
#define EIC7700_INT_BCLK_STABLE BIT(16)
|
||||
@@ -252,14 +278,8 @@
|
||||
#define PHY_DELAY_CODE_EMMC 0x17
|
||||
#define PHY_DELAY_CODE_SD 0x55
|
||||
|
||||
enum dwcmshc_rk_type {
|
||||
DWCMSHC_RK3568,
|
||||
DWCMSHC_RK3588,
|
||||
};
|
||||
|
||||
struct rk35xx_priv {
|
||||
struct reset_control *reset;
|
||||
enum dwcmshc_rk_type devtype;
|
||||
u8 txclk_tapnum;
|
||||
};
|
||||
|
||||
@@ -268,6 +288,11 @@ struct eic7700_priv {
|
||||
unsigned int drive_impedance;
|
||||
};
|
||||
|
||||
struct k230_priv {
|
||||
/* Canaan k230 specific */
|
||||
struct regmap *hi_sys_regmap;
|
||||
};
|
||||
|
||||
#define DWCMSHC_MAX_OTHER_CLKS 3
|
||||
|
||||
struct dwcmshc_priv {
|
||||
@@ -278,6 +303,7 @@ struct dwcmshc_priv {
|
||||
int num_other_clks;
|
||||
struct clk_bulk_data other_clks[DWCMSHC_MAX_OTHER_CLKS];
|
||||
|
||||
const struct dwcmshc_pltfm_data *dwcmshc_pdata;
|
||||
void *priv; /* pointer to SoC private stuff */
|
||||
u16 delay_line;
|
||||
u16 flags;
|
||||
@@ -290,6 +316,24 @@ struct dwcmshc_pltfm_data {
|
||||
void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
|
||||
};
|
||||
|
||||
struct k230_pltfm_data {
|
||||
struct dwcmshc_pltfm_data dwcmshc_pdata;
|
||||
bool is_emmc;
|
||||
u32 ctrl_reg;
|
||||
u32 vol_stable_bit;
|
||||
u32 write_prot_bit;
|
||||
};
|
||||
|
||||
struct rockchip_pltfm_data {
|
||||
struct dwcmshc_pltfm_data dwcmshc_pdata;
|
||||
/*
|
||||
* The controller hardware has two known revisions documented internally:
|
||||
* - Revision 0: Exclusively used by RK3566 and RK3568 SoCs.
|
||||
* - Revision 1: Implemented in all other Rockchip SoCs, including RK3576, RK3588, etc.
|
||||
*/
|
||||
int revision;
|
||||
};
|
||||
|
||||
static void dwcmshc_enable_card_clk(struct sdhci_host *host)
|
||||
{
|
||||
u16 ctrl;
|
||||
@@ -709,6 +753,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
||||
struct rk35xx_priv *priv = dwc_priv->priv;
|
||||
const struct rockchip_pltfm_data *rockchip_pdata = to_pltfm_data(dwc_priv, rockchip);
|
||||
u8 txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT;
|
||||
u32 extra, reg;
|
||||
int err;
|
||||
@@ -738,12 +783,15 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
extra |= BIT(4);
|
||||
sdhci_writel(host, extra, reg);
|
||||
|
||||
/* Disable clock while config DLL */
|
||||
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
if (clock <= 52000000) {
|
||||
if (host->mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
|
||||
host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"Can't reduce the clock below 52MHz in HS200/HS400 mode");
|
||||
return;
|
||||
goto enable_clk;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -763,7 +811,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
DLL_STRBIN_DELAY_NUM_SEL |
|
||||
DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET;
|
||||
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN);
|
||||
return;
|
||||
goto enable_clk;
|
||||
}
|
||||
|
||||
/* Reset DLL */
|
||||
@@ -776,7 +824,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
* we must set it in higher speed mode.
|
||||
*/
|
||||
extra = DWCMSHC_EMMC_DLL_DLYENA;
|
||||
if (priv->devtype == DWCMSHC_RK3568)
|
||||
if (rockchip_pdata->revision == 0)
|
||||
extra |= DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL;
|
||||
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK);
|
||||
|
||||
@@ -790,7 +838,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
500 * USEC_PER_MSEC);
|
||||
if (err) {
|
||||
dev_err(mmc_dev(host->mmc), "DLL lock timeout!\n");
|
||||
return;
|
||||
goto enable_clk;
|
||||
}
|
||||
|
||||
extra = 0x1 << 16 | /* tune clock stop en */
|
||||
@@ -802,7 +850,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
host->mmc->ios.timing == MMC_TIMING_MMC_HS400)
|
||||
txclk_tapnum = priv->txclk_tapnum;
|
||||
|
||||
if ((priv->devtype == DWCMSHC_RK3588) && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
|
||||
if (rockchip_pdata->revision == 1 && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
|
||||
txclk_tapnum = DLL_TXCLK_TAPNUM_90_DEGREES;
|
||||
|
||||
extra = DLL_CMDOUT_SRC_CLK_NEG |
|
||||
@@ -823,6 +871,16 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
DLL_STRBIN_TAPNUM_DEFAULT |
|
||||
DLL_STRBIN_TAPNUM_FROM_SW;
|
||||
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN);
|
||||
|
||||
enable_clk:
|
||||
/*
|
||||
* The sdclk frequency select bits in SDHCI_CLOCK_CONTROL are not functional
|
||||
* on Rockchip's SDHCI implementation. Instead, the clock frequency is fully
|
||||
* controlled via external clk provider by calling clk_set_rate(). Consequently,
|
||||
* passing 0 to sdhci_enable_clk() only re-enables the already-configured clock,
|
||||
* which matches the hardware's actual behavior.
|
||||
*/
|
||||
sdhci_enable_clk(host, 0);
|
||||
}
|
||||
|
||||
static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
|
||||
@@ -858,11 +916,6 @@ static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host,
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "rockchip,rk3588-dwcmshc"))
|
||||
priv->devtype = DWCMSHC_RK3588;
|
||||
else
|
||||
priv->devtype = DWCMSHC_RK3568;
|
||||
|
||||
priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc));
|
||||
if (IS_ERR(priv->reset)) {
|
||||
err = PTR_ERR(priv->reset);
|
||||
@@ -1245,6 +1298,126 @@ static int sg2042_init(struct device *dev, struct sdhci_host *host,
|
||||
ARRAY_SIZE(clk_ids), clk_ids);
|
||||
}
|
||||
|
||||
/*
|
||||
* HPE GSC-specific vendor configuration: disable command conflict check
|
||||
* and program Auto-Tuning Control register.
|
||||
*/
|
||||
static void dwcmshc_hpe_vendor_specific(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 atctrl;
|
||||
u8 extra;
|
||||
|
||||
extra = sdhci_readb(host, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3);
|
||||
extra &= ~DWCMSHC_HOST_CTRL3_CMD_CONFLICT;
|
||||
sdhci_writeb(host, extra, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3);
|
||||
|
||||
atctrl = AT_CTRL_AT_EN | AT_CTRL_SWIN_TH_EN | AT_CTRL_TUNE_CLK_STOP_EN |
|
||||
FIELD_PREP(AT_CTRL_PRE_CHANGE_DLY_MASK, 3) |
|
||||
FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY) |
|
||||
FIELD_PREP(AT_CTRL_SWIN_TH_VAL_MASK, 2);
|
||||
sdhci_writel(host, atctrl, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
|
||||
}
|
||||
|
||||
static void dwcmshc_hpe_set_emmc(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
||||
u16 ctrl;
|
||||
|
||||
ctrl = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
|
||||
ctrl |= DWCMSHC_CARD_IS_EMMC;
|
||||
sdhci_writew(host, ctrl, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
|
||||
}
|
||||
|
||||
static void dwcmshc_hpe_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
dwcmshc_reset(host, mask);
|
||||
dwcmshc_hpe_vendor_specific(host);
|
||||
dwcmshc_hpe_set_emmc(host);
|
||||
}
|
||||
|
||||
static void dwcmshc_hpe_set_uhs_signaling(struct sdhci_host *host, unsigned int timing)
|
||||
{
|
||||
dwcmshc_set_uhs_signaling(host, timing);
|
||||
dwcmshc_hpe_set_emmc(host);
|
||||
}
|
||||
|
||||
/*
|
||||
* HPE GSC eMMC controller clock setup.
|
||||
*
|
||||
* The GSC SoC wires the freq_sel field of SDHCI_CLOCK_CONTROL directly to a
|
||||
* clock mux rather than a divider. Force freq_sel = 1 when running at
|
||||
* 200 MHz (HS200) so the mux selects the correct clock source.
|
||||
*/
|
||||
static void dwcmshc_hpe_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
u16 clk;
|
||||
|
||||
host->mmc->actual_clock = 0;
|
||||
|
||||
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
if (clock == 0)
|
||||
return;
|
||||
|
||||
clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
|
||||
|
||||
if (host->mmc->actual_clock == 200000000)
|
||||
clk |= (1 << SDHCI_DIVIDER_SHIFT);
|
||||
|
||||
sdhci_enable_clk(host, clk);
|
||||
}
|
||||
|
||||
/*
|
||||
* HPE GSC eMMC controller init.
|
||||
*
|
||||
* The GSC SoC requires configuring MSHCCS. Bit 18 (SCGSyncDis) disables clock
|
||||
* synchronisation for phase-select values going to the HS200 RX delay lines,
|
||||
* allowing the card clock to be stopped while the delay selection settles and
|
||||
* the phase shift is applied. This must be used together with the ATCTRL
|
||||
* settings programmed in dwcmshc_hpe_vendor_specific():
|
||||
* AT_CTRL_R.TUNE_CLK_STOP_EN = 0x1
|
||||
* AT_CTRL_R.POST_CHANGE_DLY = 0x3
|
||||
* AT_CTRL_R.PRE_CHANGE_DLY = 0x3
|
||||
*
|
||||
* The DTS node provides a syscon phandle ('hpe,gxp-sysreg') with the
|
||||
* MSHCCS register offset as an argument.
|
||||
*/
|
||||
static int dwcmshc_hpe_gsc_init(struct device *dev, struct sdhci_host *host,
|
||||
struct dwcmshc_priv *dwc_priv)
|
||||
{
|
||||
unsigned int reg_offset;
|
||||
struct regmap *soc_ctrl;
|
||||
int ret;
|
||||
|
||||
/* Disable cmd conflict check and configure auto-tuning */
|
||||
dwcmshc_hpe_vendor_specific(host);
|
||||
|
||||
/* Look up the GXP sysreg syscon and MSHCCS offset */
|
||||
soc_ctrl = syscon_regmap_lookup_by_phandle_args(dev->of_node,
|
||||
"hpe,gxp-sysreg",
|
||||
1, ®_offset);
|
||||
if (IS_ERR(soc_ctrl)) {
|
||||
dev_err(dev, "failed to get hpe,gxp-sysreg syscon\n");
|
||||
return PTR_ERR(soc_ctrl);
|
||||
}
|
||||
|
||||
/* Set SCGSyncDis (bit 18) to disable sync on HS200 RX delay lines */
|
||||
ret = regmap_update_bits(soc_ctrl, reg_offset,
|
||||
HPE_GSC_MSHCCS_SCGSYNCDIS,
|
||||
HPE_GSC_MSHCCS_SCGSYNCDIS);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set SCGSyncDis in MSHCCS\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sdhci_enable_v4_mode(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
@@ -1656,6 +1829,181 @@ static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcm
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwcmshc_k230_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
u16 clk;
|
||||
|
||||
sdhci_set_clock(host, clock);
|
||||
|
||||
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
/*
|
||||
* It is necessary to enable SDHCI_PROG_CLOCK_MODE. This is a
|
||||
* vendor-specific quirk. If this is not done, the eMMC will be
|
||||
* unable to read or write.
|
||||
*/
|
||||
clk |= SDHCI_PROG_CLOCK_MODE;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
}
|
||||
|
||||
static void sdhci_k230_config_phy_delay(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 val;
|
||||
|
||||
sdhci_writeb(host, PHY_COMMDL_CNFG_DLSTEP_SEL, PHY_COMMDL_CNFG);
|
||||
sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R);
|
||||
sdhci_writeb(host, PHY_SDCLKDL_DC_INITIAL, PHY_SDCLKDL_DC_R);
|
||||
|
||||
val = PHY_SMPLDL_CNFG_EXTDLY_EN;
|
||||
val |= FIELD_PREP(PHY_SMPLDL_CNFG_INPSEL_MASK, PHY_SMPLDL_CNFG_INPSEL);
|
||||
sdhci_writeb(host, val, PHY_SMPLDL_CNFG_R);
|
||||
|
||||
sdhci_writeb(host, FIELD_PREP(PHY_ATDL_CNFG_INPSEL_MASK, PHY_ATDL_CNFG_INPSEL),
|
||||
PHY_ATDL_CNFG_R);
|
||||
|
||||
val = sdhci_readl(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
|
||||
val |= AT_CTRL_TUNE_CLK_STOP_EN;
|
||||
val |= FIELD_PREP(AT_CTRL_PRE_CHANGE_DLY_MASK, AT_CTRL_PRE_CHANGE_DLY);
|
||||
val |= FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY);
|
||||
sdhci_writel(host, val, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
|
||||
sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
|
||||
}
|
||||
|
||||
static int dwcmshc_k230_phy_init(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 rxsel;
|
||||
u32 val;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
/* reset phy */
|
||||
sdhci_writew(host, 0, PHY_CNFG_R);
|
||||
|
||||
/* Disable the clock */
|
||||
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
rxsel = dwc_priv->flags & FLAG_IO_FIXED_1V8 ? PHY_PAD_RXSEL_1V8 : PHY_PAD_RXSEL_3V3;
|
||||
|
||||
val = rxsel;
|
||||
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P_k230);
|
||||
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_k230);
|
||||
val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
|
||||
|
||||
sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
|
||||
sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
|
||||
sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
|
||||
|
||||
val = rxsel;
|
||||
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P_k230);
|
||||
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_k230);
|
||||
sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
|
||||
|
||||
val = rxsel;
|
||||
val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN);
|
||||
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P_k230);
|
||||
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_k230);
|
||||
sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
|
||||
|
||||
sdhci_k230_config_phy_delay(host);
|
||||
|
||||
/* Wait max 150 ms */
|
||||
ret = read_poll_timeout(sdhci_readl, reg,
|
||||
(reg & FIELD_PREP(PHY_CNFG_PHY_PWRGOOD_MASK, 1)),
|
||||
10, 150000, false, host, PHY_CNFG_R);
|
||||
if (ret) {
|
||||
dev_err(mmc_dev(host->mmc), "READ PHY PWRGOOD timeout!\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
reg = FIELD_PREP(PHY_CNFG_PAD_SN_MASK, PHY_CNFG_PAD_SN_k230) |
|
||||
FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP_k230);
|
||||
sdhci_writel(host, reg, PHY_CNFG_R);
|
||||
|
||||
/* de-assert the phy */
|
||||
reg |= PHY_CNFG_RSTN_DEASSERT;
|
||||
sdhci_writel(host, reg, PHY_CNFG_R);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwcmshc_k230_sdhci_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
||||
const struct k230_pltfm_data *k230_pdata = to_pltfm_data(dwc_priv, k230);
|
||||
u8 emmc_ctrl;
|
||||
|
||||
dwcmshc_reset(host, mask);
|
||||
|
||||
if (mask != SDHCI_RESET_ALL)
|
||||
return;
|
||||
|
||||
emmc_ctrl = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
|
||||
sdhci_writeb(host, emmc_ctrl, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
|
||||
|
||||
if (k230_pdata->is_emmc)
|
||||
dwcmshc_k230_phy_init(host);
|
||||
else
|
||||
sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3);
|
||||
}
|
||||
|
||||
static int dwcmshc_k230_init(struct device *dev, struct sdhci_host *host,
|
||||
struct dwcmshc_priv *dwc_priv)
|
||||
{
|
||||
const struct k230_pltfm_data *k230_pdata = to_pltfm_data(dwc_priv, k230);
|
||||
static const char * const clk_ids[] = {"block", "timer", "axi"};
|
||||
struct device_node *usb_phy_node;
|
||||
struct k230_priv *k230_priv;
|
||||
u32 data;
|
||||
int ret;
|
||||
|
||||
k230_priv = devm_kzalloc(dev, sizeof(struct k230_priv), GFP_KERNEL);
|
||||
if (!k230_priv)
|
||||
return -ENOMEM;
|
||||
|
||||
dwc_priv->priv = k230_priv;
|
||||
|
||||
usb_phy_node = of_parse_phandle(dev->of_node, "canaan,usb-phy", 0);
|
||||
if (!usb_phy_node)
|
||||
return dev_err_probe(dev, -ENODEV, "Failed to find canaan,usb-phy phandle\n");
|
||||
|
||||
k230_priv->hi_sys_regmap = device_node_to_regmap(usb_phy_node);
|
||||
of_node_put(usb_phy_node);
|
||||
|
||||
if (IS_ERR(k230_priv->hi_sys_regmap))
|
||||
return dev_err_probe(dev, PTR_ERR(k230_priv->hi_sys_regmap),
|
||||
"Failed to get k230-usb-phy regmap\n");
|
||||
|
||||
ret = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv,
|
||||
ARRAY_SIZE(clk_ids), clk_ids);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to get/enable k230 mmc other clocks\n");
|
||||
|
||||
if (k230_pdata->is_emmc) {
|
||||
host->flags &= ~SDHCI_SIGNALING_330;
|
||||
dwc_priv->flags |= FLAG_IO_FIXED_1V8;
|
||||
} else {
|
||||
host->mmc->caps |= MMC_CAP_SD_HIGHSPEED;
|
||||
host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
|
||||
}
|
||||
|
||||
ret = regmap_read(k230_priv->hi_sys_regmap, k230_pdata->ctrl_reg, &data);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to read control reg 0x%x\n",
|
||||
k230_pdata->ctrl_reg);
|
||||
|
||||
data |= k230_pdata->write_prot_bit | k230_pdata->vol_stable_bit;
|
||||
ret = regmap_write(k230_priv->hi_sys_regmap, k230_pdata->ctrl_reg, data);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to write control reg 0x%x\n",
|
||||
k230_pdata->ctrl_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_dwcmshc_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
@@ -1743,6 +2091,15 @@ static const struct sdhci_ops sdhci_dwcmshc_eic7700_ops = {
|
||||
.platform_execute_tuning = sdhci_eic7700_executing_tuning,
|
||||
};
|
||||
|
||||
static const struct sdhci_ops sdhci_dwcmshc_k230_ops = {
|
||||
.set_clock = dwcmshc_k230_sdhci_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.set_uhs_signaling = dwcmshc_set_uhs_signaling,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.reset = dwcmshc_k230_sdhci_reset,
|
||||
.adma_write_desc = dwcmshc_adma_write_desc,
|
||||
};
|
||||
|
||||
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = {
|
||||
.pdata = {
|
||||
.ops = &sdhci_dwcmshc_ops,
|
||||
@@ -1771,30 +2128,52 @@ static const struct cqhci_host_ops rk35xx_cqhci_ops = {
|
||||
.set_tran_desc = dwcmshc_set_tran_desc,
|
||||
};
|
||||
|
||||
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
|
||||
.pdata = {
|
||||
.ops = &sdhci_dwcmshc_rk35xx_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
||||
static const struct rockchip_pltfm_data sdhci_dwcmshc_rk3568_pdata = {
|
||||
.dwcmshc_pdata = {
|
||||
.pdata = {
|
||||
.ops = &sdhci_dwcmshc_rk35xx_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
||||
},
|
||||
.cqhci_host_ops = &rk35xx_cqhci_ops,
|
||||
.init = dwcmshc_rk35xx_init,
|
||||
.postinit = dwcmshc_rk35xx_postinit,
|
||||
},
|
||||
.cqhci_host_ops = &rk35xx_cqhci_ops,
|
||||
.init = dwcmshc_rk35xx_init,
|
||||
.postinit = dwcmshc_rk35xx_postinit,
|
||||
.revision = 0,
|
||||
};
|
||||
|
||||
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk3576_pdata = {
|
||||
.pdata = {
|
||||
.ops = &sdhci_dwcmshc_rk35xx_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
||||
static const struct rockchip_pltfm_data sdhci_dwcmshc_rk3576_pdata = {
|
||||
.dwcmshc_pdata = {
|
||||
.pdata = {
|
||||
.ops = &sdhci_dwcmshc_rk35xx_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
||||
},
|
||||
.cqhci_host_ops = &rk35xx_cqhci_ops,
|
||||
.init = dwcmshc_rk35xx_init,
|
||||
.postinit = dwcmshc_rk3576_postinit,
|
||||
},
|
||||
.cqhci_host_ops = &rk35xx_cqhci_ops,
|
||||
.init = dwcmshc_rk35xx_init,
|
||||
.postinit = dwcmshc_rk3576_postinit,
|
||||
.revision = 1,
|
||||
};
|
||||
|
||||
static const struct rockchip_pltfm_data sdhci_dwcmshc_rk3588_pdata = {
|
||||
.dwcmshc_pdata = {
|
||||
.pdata = {
|
||||
.ops = &sdhci_dwcmshc_rk35xx_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
||||
},
|
||||
.cqhci_host_ops = &rk35xx_cqhci_ops,
|
||||
.init = dwcmshc_rk35xx_init,
|
||||
.postinit = dwcmshc_rk35xx_postinit,
|
||||
},
|
||||
.revision = 1,
|
||||
};
|
||||
|
||||
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_th1520_pdata = {
|
||||
@@ -1834,6 +2213,55 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_eic7700_pdata = {
|
||||
.init = eic7700_init,
|
||||
};
|
||||
|
||||
static const struct k230_pltfm_data k230_emmc_data = {
|
||||
.dwcmshc_pdata = {
|
||||
.pdata = {
|
||||
.ops = &sdhci_dwcmshc_k230_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
|
||||
},
|
||||
.init = dwcmshc_k230_init,
|
||||
},
|
||||
.is_emmc = true,
|
||||
.ctrl_reg = SD0_CTRL,
|
||||
.vol_stable_bit = SD0_HOST_REG_VOL_STABLE,
|
||||
.write_prot_bit = SD0_CARD_WRITE_PROT,
|
||||
};
|
||||
|
||||
static const struct k230_pltfm_data k230_sdio_data = {
|
||||
.dwcmshc_pdata = {
|
||||
.pdata = {
|
||||
.ops = &sdhci_dwcmshc_k230_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
|
||||
},
|
||||
.init = dwcmshc_k230_init,
|
||||
},
|
||||
.is_emmc = false,
|
||||
.ctrl_reg = SD1_CTRL,
|
||||
.vol_stable_bit = SD1_HOST_REG_VOL_STABLE,
|
||||
.write_prot_bit = SD1_CARD_WRITE_PROT,
|
||||
};
|
||||
|
||||
static const struct sdhci_ops sdhci_dwcmshc_hpe_ops = {
|
||||
.set_clock = dwcmshc_hpe_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.set_uhs_signaling = dwcmshc_hpe_set_uhs_signaling,
|
||||
.get_max_clock = dwcmshc_get_max_clock,
|
||||
.reset = dwcmshc_hpe_reset,
|
||||
.adma_write_desc = dwcmshc_adma_write_desc,
|
||||
.irq = dwcmshc_cqe_irq_handler,
|
||||
};
|
||||
|
||||
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_hpe_gsc_pdata = {
|
||||
.pdata = {
|
||||
.ops = &sdhci_dwcmshc_hpe_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
},
|
||||
.init = dwcmshc_hpe_gsc_init,
|
||||
};
|
||||
|
||||
static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
|
||||
.enable = dwcmshc_sdhci_cqe_enable,
|
||||
.disable = sdhci_cqe_disable,
|
||||
@@ -1906,9 +2334,17 @@ static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *
|
||||
}
|
||||
|
||||
static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
|
||||
{
|
||||
.compatible = "canaan,k230-emmc",
|
||||
.data = &k230_emmc_data.dwcmshc_pdata,
|
||||
},
|
||||
{
|
||||
.compatible = "canaan,k230-sdio",
|
||||
.data = &k230_sdio_data.dwcmshc_pdata,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3588-dwcmshc",
|
||||
.data = &sdhci_dwcmshc_rk35xx_pdata,
|
||||
.data = &sdhci_dwcmshc_rk3588_pdata,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3576-dwcmshc",
|
||||
@@ -1916,7 +2352,7 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3568-dwcmshc",
|
||||
.data = &sdhci_dwcmshc_rk35xx_pdata,
|
||||
.data = &sdhci_dwcmshc_rk3568_pdata,
|
||||
},
|
||||
{
|
||||
.compatible = "snps,dwcmshc-sdhci",
|
||||
@@ -1942,6 +2378,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
|
||||
.compatible = "eswin,eic7700-dwcmshc",
|
||||
.data = &sdhci_dwcmshc_eic7700_pdata,
|
||||
},
|
||||
{
|
||||
.compatible = "hpe,gsc-dwcmshc",
|
||||
.data = &sdhci_dwcmshc_hpe_gsc_pdata,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
|
||||
@@ -1988,6 +2428,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
priv = sdhci_pltfm_priv(pltfm_host);
|
||||
priv->dwcmshc_pdata = pltfm_data;
|
||||
|
||||
if (dev->of_node) {
|
||||
pltfm_host->clk = devm_clk_get(dev, "core");
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "sdhci.h"
|
||||
@@ -223,6 +224,21 @@ static inline int spacemit_sdhci_get_clocks(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int spacemit_sdhci_get_resets(struct device *dev)
|
||||
{
|
||||
struct reset_control *rst;
|
||||
|
||||
rst = devm_reset_control_get_optional_shared_deasserted(dev, "axi");
|
||||
if (IS_ERR(rst))
|
||||
return PTR_ERR(rst);
|
||||
|
||||
rst = devm_reset_control_get_optional_exclusive_deasserted(dev, "sdh");
|
||||
if (IS_ERR(rst))
|
||||
return PTR_ERR(rst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops spacemit_sdhci_ops = {
|
||||
.get_max_clock = spacemit_sdhci_clk_get_max_clock,
|
||||
.reset = spacemit_sdhci_reset,
|
||||
@@ -243,8 +259,20 @@ static const struct sdhci_pltfm_data spacemit_sdhci_k1_pdata = {
|
||||
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data spacemit_sdhci_k3_pdata = {
|
||||
.ops = &spacemit_sdhci_ops,
|
||||
.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|
||||
SDHCI_QUIRK_32BIT_ADMA_SIZE |
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
};
|
||||
|
||||
static const struct of_device_id spacemit_sdhci_of_match[] = {
|
||||
{ .compatible = "spacemit,k1-sdhci" },
|
||||
{ .compatible = "spacemit,k1-sdhci", .data = &spacemit_sdhci_k1_pdata },
|
||||
{ .compatible = "spacemit,k3-sdhci", .data = &spacemit_sdhci_k3_pdata },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spacemit_sdhci_of_match);
|
||||
@@ -255,10 +283,13 @@ static int spacemit_sdhci_probe(struct platform_device *pdev)
|
||||
struct spacemit_sdhci_host *sdhst;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_host *host;
|
||||
const struct sdhci_pltfm_data *data;
|
||||
struct mmc_host_ops *mops;
|
||||
int ret;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &spacemit_sdhci_k1_pdata, sizeof(*sdhst));
|
||||
data = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
host = sdhci_pltfm_init(pdev, data, sizeof(*sdhst));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
@@ -284,6 +315,10 @@ static int spacemit_sdhci_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err_pltfm;
|
||||
|
||||
ret = spacemit_sdhci_get_resets(dev);
|
||||
if (ret)
|
||||
goto err_pltfm;
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto err_pltfm;
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_qos.h>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Support of SDHCI platform devices for Microchip PIC32.
|
||||
*
|
||||
@@ -5,10 +6,6 @@
|
||||
* Andrei Pistirica, Paul Thacker
|
||||
*
|
||||
* Inspired by sdhci-pltfm.c
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
|
||||
@@ -95,13 +95,6 @@ void sdhci_get_property(struct platform_device *pdev)
|
||||
sdhci_get_compatibility(pdev);
|
||||
|
||||
device_property_read_u32(dev, "clock-frequency", &pltfm_host->clock);
|
||||
|
||||
if (device_property_present(dev, "keep-power-in-suspend"))
|
||||
host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
|
||||
|
||||
if (device_property_read_bool(dev, "wakeup-source") ||
|
||||
device_property_read_bool(dev, "enable-sdio-wakeup")) /* legacy */
|
||||
host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_get_property);
|
||||
|
||||
@@ -215,19 +208,6 @@ const struct dev_pm_ops sdhci_pltfm_pmops = {
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(sdhci_pltfm_pmops);
|
||||
|
||||
static int __init sdhci_pltfm_drv_init(void)
|
||||
{
|
||||
pr_info("sdhci-pltfm: SDHCI platform and OF driver helper\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(sdhci_pltfm_drv_init);
|
||||
|
||||
static void __exit sdhci_pltfm_drv_exit(void)
|
||||
{
|
||||
}
|
||||
module_exit(sdhci_pltfm_drv_exit);
|
||||
|
||||
MODULE_DESCRIPTION("SDHCI platform and OF driver helper");
|
||||
MODULE_AUTHOR("Intel Corporation");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -1126,7 +1126,7 @@ static irqreturn_t sdhci_uhs2_thread_irq(int irq, void *dev_id)
|
||||
|
||||
/*****************************************************************************\
|
||||
* *
|
||||
* Driver init/exit *
|
||||
* Driver init *
|
||||
* *
|
||||
\*****************************************************************************/
|
||||
|
||||
@@ -1138,17 +1138,6 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init sdhci_uhs2_mod_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
module_init(sdhci_uhs2_mod_init);
|
||||
|
||||
static void __exit sdhci_uhs2_mod_exit(void)
|
||||
{
|
||||
}
|
||||
module_exit(sdhci_uhs2_mod_exit);
|
||||
|
||||
/*****************************************************************************\
|
||||
*
|
||||
* Device allocation/registration *
|
||||
|
||||
@@ -4193,6 +4193,12 @@ static void sdhci_allocate_bounce_buffer(struct sdhci_host *host)
|
||||
unsigned int bounce_size;
|
||||
int ret;
|
||||
|
||||
/* Drivers may have already allocated the buffer */
|
||||
if (host->bounce_buffer) {
|
||||
bounce_size = host->bounce_buffer_size;
|
||||
max_blocks = bounce_size / 512;
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* Cap the bounce buffer at 64KB. Using a bigger bounce buffer
|
||||
* has diminishing returns, this is probably because SD/MMC
|
||||
@@ -4241,6 +4247,7 @@ static void sdhci_allocate_bounce_buffer(struct sdhci_host *host)
|
||||
|
||||
host->bounce_buffer_size = bounce_size;
|
||||
|
||||
out:
|
||||
/* Lie about this since we're bouncing */
|
||||
mmc->max_segs = max_blocks;
|
||||
mmc->max_seg_size = bounce_size;
|
||||
@@ -5004,22 +5011,6 @@ EXPORT_SYMBOL_GPL(sdhci_remove_host);
|
||||
* *
|
||||
\*****************************************************************************/
|
||||
|
||||
static int __init sdhci_drv_init(void)
|
||||
{
|
||||
pr_info(DRIVER_NAME
|
||||
": Secure Digital Host Controller Interface driver\n");
|
||||
pr_info(DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit sdhci_drv_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(sdhci_drv_init);
|
||||
module_exit(sdhci_drv_exit);
|
||||
|
||||
module_param(debug_quirks, uint, 0444);
|
||||
module_param(debug_quirks2, uint, 0444);
|
||||
|
||||
|
||||
@@ -193,9 +193,7 @@ static void tifm_sd_transfer_data(struct tifm_sd *host)
|
||||
|
||||
pg = sg_page(&sg[host->sg_pos]) + (off >> PAGE_SHIFT);
|
||||
p_off = offset_in_page(off);
|
||||
p_cnt = PAGE_SIZE - p_off;
|
||||
p_cnt = min(p_cnt, cnt);
|
||||
p_cnt = min(p_cnt, t_size);
|
||||
p_cnt = min3(PAGE_SIZE - p_off, cnt, t_size);
|
||||
|
||||
if (r_data->flags & MMC_DATA_READ)
|
||||
tifm_sd_read_fifo(host, pg, p_off, p_cnt);
|
||||
|
||||
@@ -2107,19 +2107,19 @@ static int vub300_probe(struct usb_interface *interface,
|
||||
command_out_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!command_out_urb) {
|
||||
retval = -ENOMEM;
|
||||
goto error0;
|
||||
goto err_put_udev;
|
||||
}
|
||||
command_res_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!command_res_urb) {
|
||||
retval = -ENOMEM;
|
||||
goto error1;
|
||||
goto err_free_out_urb;
|
||||
}
|
||||
/* this also allocates memory for our VUB300 mmc host device */
|
||||
mmc = mmc_alloc_host(sizeof(*vub300), &udev->dev);
|
||||
if (!mmc) {
|
||||
retval = -ENOMEM;
|
||||
dev_err(&udev->dev, "not enough memory for the mmc_host\n");
|
||||
goto error4;
|
||||
goto err_free_res_urb;
|
||||
}
|
||||
/* MMC core transfer sizes tunable parameters */
|
||||
mmc->caps = 0;
|
||||
@@ -2336,10 +2336,11 @@ static int vub300_probe(struct usb_interface *interface,
|
||||
interface_to_InterfaceNumber(interface));
|
||||
retval = mmc_add_host(mmc);
|
||||
if (retval)
|
||||
goto error6;
|
||||
goto err_delete_timer;
|
||||
|
||||
return 0;
|
||||
error6:
|
||||
|
||||
err_delete_timer:
|
||||
timer_delete_sync(&vub300->inactivity_timer);
|
||||
err_free_host:
|
||||
mmc_free_host(mmc);
|
||||
@@ -2347,12 +2348,13 @@ static int vub300_probe(struct usb_interface *interface,
|
||||
* and hence also frees vub300
|
||||
* which is contained at the end of struct mmc
|
||||
*/
|
||||
error4:
|
||||
err_free_res_urb:
|
||||
usb_free_urb(command_res_urb);
|
||||
error1:
|
||||
err_free_out_urb:
|
||||
usb_free_urb(command_out_urb);
|
||||
error0:
|
||||
err_put_udev:
|
||||
usb_put_dev(udev);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -2427,37 +2429,36 @@ static int __init vub300_init(void)
|
||||
|
||||
pr_info("VUB300 Driver rom wait states = %02X irqpoll timeout = %04X",
|
||||
firmware_rom_wait_states, 0x0FFFF & firmware_irqpoll_timeout);
|
||||
|
||||
cmndworkqueue = create_singlethread_workqueue("kvub300c");
|
||||
if (!cmndworkqueue) {
|
||||
pr_err("not enough memory for the REQUEST workqueue");
|
||||
result = -ENOMEM;
|
||||
goto out1;
|
||||
}
|
||||
if (!cmndworkqueue)
|
||||
return -ENOMEM;
|
||||
|
||||
pollworkqueue = create_singlethread_workqueue("kvub300p");
|
||||
if (!pollworkqueue) {
|
||||
pr_err("not enough memory for the IRQPOLL workqueue");
|
||||
result = -ENOMEM;
|
||||
goto out2;
|
||||
goto err_destroy_cmdwq;
|
||||
}
|
||||
|
||||
deadworkqueue = create_singlethread_workqueue("kvub300d");
|
||||
if (!deadworkqueue) {
|
||||
pr_err("not enough memory for the EXPIRED workqueue");
|
||||
result = -ENOMEM;
|
||||
goto out3;
|
||||
goto err_destroy_pollwq;
|
||||
}
|
||||
|
||||
result = usb_register(&vub300_driver);
|
||||
if (result) {
|
||||
pr_err("usb_register failed. Error number %d", result);
|
||||
goto out4;
|
||||
}
|
||||
if (result)
|
||||
goto err_destroy_deadwq;
|
||||
|
||||
return 0;
|
||||
out4:
|
||||
|
||||
err_destroy_deadwq:
|
||||
destroy_workqueue(deadworkqueue);
|
||||
out3:
|
||||
err_destroy_pollwq:
|
||||
destroy_workqueue(pollworkqueue);
|
||||
out2:
|
||||
err_destroy_cmdwq:
|
||||
destroy_workqueue(cmndworkqueue);
|
||||
out1:
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,21 @@
|
||||
#
|
||||
|
||||
config MULTIPLEXER
|
||||
tristate
|
||||
bool
|
||||
|
||||
config MUX_CORE
|
||||
bool "Generic Multiplexer Support"
|
||||
select MULTIPLEXER
|
||||
help
|
||||
This framework is designed to abstract multiplexer handling for
|
||||
devices via various GPIO-, MMIO/Regmap or specific multiplexer
|
||||
controller chips.
|
||||
|
||||
If unsure, say no.
|
||||
|
||||
if MULTIPLEXER
|
||||
|
||||
menu "Multiplexer drivers"
|
||||
depends on MULTIPLEXER
|
||||
|
||||
config MUX_ADG792A
|
||||
tristate "Analog Devices ADG792A/ADG792G Multiplexers"
|
||||
@@ -60,3 +71,5 @@ config MUX_MMIO
|
||||
be called mux-mmio.
|
||||
|
||||
endmenu
|
||||
|
||||
endif # MULTIPLEXER
|
||||
|
||||
@@ -46,6 +46,16 @@ static const struct class mux_class = {
|
||||
.name = "mux",
|
||||
};
|
||||
|
||||
/**
|
||||
* struct devm_mux_state_state - Tracks managed resources for mux-state objects.
|
||||
* @mstate: Pointer to a mux state.
|
||||
* @exit: An optional callback to execute before free.
|
||||
*/
|
||||
struct devm_mux_state_state {
|
||||
struct mux_state *mstate;
|
||||
int (*exit)(struct mux_state *mstate);
|
||||
};
|
||||
|
||||
static DEFINE_IDA(mux_ida);
|
||||
|
||||
static int __init mux_init(void)
|
||||
@@ -516,17 +526,19 @@ static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np)
|
||||
return dev ? to_mux_chip(dev) : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* mux_get() - Get the mux-control for a device.
|
||||
* @dev: The device that needs a mux-control.
|
||||
* @mux_name: The name identifying the mux-control.
|
||||
* @state: Pointer to where the requested state is returned, or NULL when
|
||||
* the required multiplexer states are handled by other means.
|
||||
* @optional: Whether to return NULL and silence errors when mux doesn't exist.
|
||||
*
|
||||
* Return: A pointer to the mux-control, or an ERR_PTR with a negative errno.
|
||||
* Return: Pointer to the mux-control on success, an ERR_PTR with a negative
|
||||
* errno on error, or NULL if optional is true and mux doesn't exist.
|
||||
*/
|
||||
static struct mux_control *mux_get(struct device *dev, const char *mux_name,
|
||||
unsigned int *state)
|
||||
unsigned int *state, bool optional)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct of_phandle_args args;
|
||||
@@ -542,7 +554,9 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
|
||||
else
|
||||
index = of_property_match_string(np, "mux-control-names",
|
||||
mux_name);
|
||||
if (index < 0) {
|
||||
if (index < 0 && optional) {
|
||||
return NULL;
|
||||
} else if (index < 0) {
|
||||
dev_err(dev, "mux controller '%s' not found\n",
|
||||
mux_name);
|
||||
return ERR_PTR(index);
|
||||
@@ -558,8 +572,12 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
|
||||
"mux-controls", "#mux-control-cells",
|
||||
index, &args);
|
||||
if (ret) {
|
||||
if (optional && ret == -ENOENT)
|
||||
return NULL;
|
||||
|
||||
dev_err(dev, "%pOF: failed to get mux-%s %s(%i)\n",
|
||||
np, state ? "state" : "control", mux_name ?: "", index);
|
||||
np, state ? "state" : "control",
|
||||
mux_name ?: "", index);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
@@ -617,10 +635,29 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
|
||||
*/
|
||||
struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
|
||||
{
|
||||
return mux_get(dev, mux_name, NULL);
|
||||
struct mux_control *mux = mux_get(dev, mux_name, NULL, false);
|
||||
|
||||
if (!mux)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
return mux;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mux_control_get);
|
||||
|
||||
/**
|
||||
* mux_control_get_optional() - Get the optional mux-control for a device.
|
||||
* @dev: The device that needs a mux-control.
|
||||
* @mux_name: The name identifying the mux-control.
|
||||
*
|
||||
* Return: Pointer to the mux-control on success, an ERR_PTR with a negative
|
||||
* errno on error, or NULL if mux doesn't exist.
|
||||
*/
|
||||
struct mux_control *mux_control_get_optional(struct device *dev, const char *mux_name)
|
||||
{
|
||||
return mux_get(dev, mux_name, NULL, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mux_control_get_optional);
|
||||
|
||||
/**
|
||||
* mux_control_put() - Put away the mux-control for good.
|
||||
* @mux: The mux-control to put away.
|
||||
@@ -670,14 +707,16 @@ struct mux_control *devm_mux_control_get(struct device *dev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_mux_control_get);
|
||||
|
||||
/*
|
||||
/**
|
||||
* mux_state_get() - Get the mux-state for a device.
|
||||
* @dev: The device that needs a mux-state.
|
||||
* @mux_name: The name identifying the mux-state.
|
||||
* @optional: Whether to return NULL and silence errors when mux doesn't exist.
|
||||
*
|
||||
* Return: A pointer to the mux-state, or an ERR_PTR with a negative errno.
|
||||
* Return: Pointer to the mux-state on success, an ERR_PTR with a negative
|
||||
* errno on error, or NULL if optional is true and mux doesn't exist.
|
||||
*/
|
||||
static struct mux_state *mux_state_get(struct device *dev, const char *mux_name)
|
||||
static struct mux_state *mux_state_get(struct device *dev, const char *mux_name, bool optional)
|
||||
{
|
||||
struct mux_state *mstate;
|
||||
|
||||
@@ -685,12 +724,15 @@ static struct mux_state *mux_state_get(struct device *dev, const char *mux_name)
|
||||
if (!mstate)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mstate->mux = mux_get(dev, mux_name, &mstate->state);
|
||||
mstate->mux = mux_get(dev, mux_name, &mstate->state, optional);
|
||||
if (IS_ERR(mstate->mux)) {
|
||||
int err = PTR_ERR(mstate->mux);
|
||||
|
||||
kfree(mstate);
|
||||
return ERR_PTR(err);
|
||||
} else if (!mstate->mux) {
|
||||
kfree(mstate);
|
||||
return optional ? NULL : ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
return mstate;
|
||||
@@ -710,9 +752,66 @@ static void mux_state_put(struct mux_state *mstate)
|
||||
|
||||
static void devm_mux_state_release(struct device *dev, void *res)
|
||||
{
|
||||
struct mux_state *mstate = *(struct mux_state **)res;
|
||||
struct devm_mux_state_state *devm_state = res;
|
||||
|
||||
if (devm_state->exit)
|
||||
devm_state->exit(devm_state->mstate);
|
||||
|
||||
mux_state_put(devm_state->mstate);
|
||||
}
|
||||
|
||||
/**
|
||||
* __devm_mux_state_get() - Get the optional mux-state for a device,
|
||||
* with resource management.
|
||||
* @dev: The device that needs a mux-state.
|
||||
* @mux_name: The name identifying the mux-state.
|
||||
* @optional: Whether to return NULL and silence errors when mux doesn't exist.
|
||||
* @init: Optional function pointer for mux-state object initialisation.
|
||||
* @exit: Optional function pointer for mux-state object cleanup on release.
|
||||
*
|
||||
* Return: Pointer to the mux-state on success, an ERR_PTR with a negative
|
||||
* errno on error, or NULL if optional is true and mux doesn't exist.
|
||||
*/
|
||||
static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mux_name,
|
||||
bool optional,
|
||||
int (*init)(struct mux_state *mstate),
|
||||
int (*exit)(struct mux_state *mstate))
|
||||
{
|
||||
struct devm_mux_state_state *devm_state;
|
||||
struct mux_state *mstate;
|
||||
int ret;
|
||||
|
||||
mstate = mux_state_get(dev, mux_name, optional);
|
||||
if (IS_ERR(mstate))
|
||||
return ERR_CAST(mstate);
|
||||
else if (optional && !mstate)
|
||||
return NULL;
|
||||
else if (!mstate)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
devm_state = devres_alloc(devm_mux_state_release, sizeof(*devm_state), GFP_KERNEL);
|
||||
if (!devm_state) {
|
||||
ret = -ENOMEM;
|
||||
goto err_devres_alloc;
|
||||
}
|
||||
|
||||
if (init) {
|
||||
ret = init(mstate);
|
||||
if (ret)
|
||||
goto err_mux_state_init;
|
||||
}
|
||||
|
||||
devm_state->mstate = mstate;
|
||||
devm_state->exit = exit;
|
||||
devres_add(dev, devm_state);
|
||||
|
||||
return mstate;
|
||||
|
||||
err_mux_state_init:
|
||||
devres_free(devm_state);
|
||||
err_devres_alloc:
|
||||
mux_state_put(mstate);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -722,29 +821,70 @@ static void devm_mux_state_release(struct device *dev, void *res)
|
||||
* @mux_name: The name identifying the mux-control.
|
||||
*
|
||||
* Return: Pointer to the mux-state, or an ERR_PTR with a negative errno.
|
||||
*
|
||||
* The mux-state will automatically be freed on release.
|
||||
*/
|
||||
struct mux_state *devm_mux_state_get(struct device *dev,
|
||||
const char *mux_name)
|
||||
struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name)
|
||||
{
|
||||
struct mux_state **ptr, *mstate;
|
||||
|
||||
ptr = devres_alloc(devm_mux_state_release, sizeof(*ptr), GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mstate = mux_state_get(dev, mux_name);
|
||||
if (IS_ERR(mstate)) {
|
||||
devres_free(ptr);
|
||||
return mstate;
|
||||
}
|
||||
|
||||
*ptr = mstate;
|
||||
devres_add(dev, ptr);
|
||||
|
||||
return mstate;
|
||||
return __devm_mux_state_get(dev, mux_name, false, NULL, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_mux_state_get);
|
||||
|
||||
/**
|
||||
* devm_mux_state_get_optional() - Get the optional mux-state for a device,
|
||||
* with resource management.
|
||||
* @dev: The device that needs a mux-state.
|
||||
* @mux_name: The name identifying the mux-state.
|
||||
*
|
||||
* Return: Pointer to the mux-state on success, an ERR_PTR with a negative
|
||||
* errno on error, or NULL if mux doesn't exist.
|
||||
*
|
||||
* The mux-state will automatically be freed on release.
|
||||
*/
|
||||
struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name)
|
||||
{
|
||||
return __devm_mux_state_get(dev, mux_name, true, NULL, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_mux_state_get_optional);
|
||||
|
||||
/**
|
||||
* devm_mux_state_get_selected() - Get the mux-state for a device, with
|
||||
* resource management.
|
||||
* @dev: The device that needs a mux-state.
|
||||
* @mux_name: The name identifying the mux-state.
|
||||
*
|
||||
* Return: Pointer to the mux-state, or an ERR_PTR with a negative errno.
|
||||
*
|
||||
* The returned mux-state (if valid) is already selected.
|
||||
*
|
||||
* The mux-state will automatically be deselected and freed on release.
|
||||
*/
|
||||
struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name)
|
||||
{
|
||||
return __devm_mux_state_get(dev, mux_name, false, mux_state_select, mux_state_deselect);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_mux_state_get_selected);
|
||||
|
||||
/**
|
||||
* devm_mux_state_get_optional_selected() - Get the optional mux-state for
|
||||
* a device, with resource management.
|
||||
* @dev: The device that needs a mux-state.
|
||||
* @mux_name: The name identifying the mux-state.
|
||||
*
|
||||
* Return: Pointer to the mux-state on success, an ERR_PTR with a negative
|
||||
* errno on error, or NULL if mux doesn't exist.
|
||||
*
|
||||
* The returned mux-state (if valid) is already selected.
|
||||
*
|
||||
* The mux-state will automatically be deselected and freed on release.
|
||||
*/
|
||||
struct mux_state *devm_mux_state_get_optional_selected(struct device *dev,
|
||||
const char *mux_name)
|
||||
{
|
||||
return __devm_mux_state_get(dev, mux_name, true, mux_state_select, mux_state_deselect);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_mux_state_get_optional_selected);
|
||||
|
||||
/*
|
||||
* Using subsys_initcall instead of module_init here to try to ensure - for
|
||||
* the non-modular case - that the subsystem is initialized when mux consumers
|
||||
|
||||
@@ -126,16 +126,6 @@ static const struct of_device_id can_transceiver_phy_ids[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, can_transceiver_phy_ids);
|
||||
|
||||
/* Temporary wrapper until the multiplexer subsystem supports optional muxes */
|
||||
static inline struct mux_state *
|
||||
devm_mux_state_get_optional(struct device *dev, const char *mux_name)
|
||||
{
|
||||
if (!of_property_present(dev->of_node, "mux-states"))
|
||||
return NULL;
|
||||
|
||||
return devm_mux_state_get(dev, mux_name);
|
||||
}
|
||||
|
||||
static struct phy *can_transceiver_phy_xlate(struct device *dev,
|
||||
const struct of_phandle_args *args)
|
||||
{
|
||||
|
||||
@@ -939,21 +939,6 @@ static int rcar_gen3_phy_usb2_vbus_regulator_register(struct rcar_gen3_chan *cha
|
||||
return rcar_gen3_phy_usb2_vbus_regulator_get_exclusive_enable(channel, enable);
|
||||
}
|
||||
|
||||
/* Temporary wrapper until the multiplexer subsystem supports optional muxes */
|
||||
static inline struct mux_state *
|
||||
devm_mux_state_get_optional(struct device *dev, const char *mux_name)
|
||||
{
|
||||
if (!of_property_present(dev->of_node, "mux-states"))
|
||||
return NULL;
|
||||
|
||||
return devm_mux_state_get(dev, mux_name);
|
||||
}
|
||||
|
||||
static void rcar_gen3_phy_mux_state_deselect(void *data)
|
||||
{
|
||||
mux_state_deselect(data);
|
||||
}
|
||||
|
||||
static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@@ -1036,20 +1021,9 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
||||
phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]);
|
||||
}
|
||||
|
||||
mux_state = devm_mux_state_get_optional(dev, NULL);
|
||||
mux_state = devm_mux_state_get_optional_selected(dev, NULL);
|
||||
if (IS_ERR(mux_state))
|
||||
return PTR_ERR(mux_state);
|
||||
if (mux_state) {
|
||||
ret = mux_state_select(mux_state);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to select USB mux\n");
|
||||
|
||||
ret = devm_add_action_or_reset(dev, rcar_gen3_phy_mux_state_deselect,
|
||||
mux_state);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to register USB mux state deselect\n");
|
||||
}
|
||||
return dev_err_probe(dev, PTR_ERR(mux_state), "Failed to get USB mux\n");
|
||||
|
||||
if (channel->phy_data->no_adp_ctrl && channel->is_otg_channel) {
|
||||
ret = rcar_gen3_phy_usb2_vbus_regulator_register(channel);
|
||||
|
||||
@@ -329,6 +329,8 @@ struct mmc_card {
|
||||
#define MMC_QUIRK_BROKEN_CACHE_FLUSH (1<<16) /* Don't flush cache until the write has occurred */
|
||||
#define MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY (1<<17) /* Disable broken SD poweroff notify support */
|
||||
#define MMC_QUIRK_NO_UHS_DDR50_TUNING (1<<18) /* Disable DDR50 tuning */
|
||||
#define MMC_QUIRK_BROKEN_MDT (1<<19) /* Wrong manufacturing year */
|
||||
#define MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME (1<<20) /* Secure erase/trim time is fixed regardless of size */
|
||||
|
||||
bool written_flag; /* Indicates eMMC has been written since power on */
|
||||
bool reenable_cmdq; /* Re-enable Command Queue */
|
||||
|
||||
@@ -117,6 +117,9 @@
|
||||
#define SDIO_VENDOR_ID_MICROCHIP_WILC 0x0296
|
||||
#define SDIO_DEVICE_ID_MICROCHIP_WILC1000 0x5347
|
||||
|
||||
#define SDIO_VENDOR_ID_NXP 0x0471
|
||||
#define SDIO_DEVICE_ID_NXP_IW61X 0x0205
|
||||
|
||||
#define SDIO_VENDOR_ID_REALTEK 0x024c
|
||||
#define SDIO_DEVICE_ID_REALTEK_RTW8723BS 0xb723
|
||||
#define SDIO_DEVICE_ID_REALTEK_RTW8821BS 0xb821
|
||||
|
||||
@@ -16,6 +16,8 @@ struct device;
|
||||
struct mux_control;
|
||||
struct mux_state;
|
||||
|
||||
#if IS_ENABLED(CONFIG_MULTIPLEXER)
|
||||
|
||||
unsigned int mux_control_states(struct mux_control *mux);
|
||||
int __must_check mux_control_select_delay(struct mux_control *mux,
|
||||
unsigned int state,
|
||||
@@ -54,11 +56,109 @@ int mux_control_deselect(struct mux_control *mux);
|
||||
int mux_state_deselect(struct mux_state *mstate);
|
||||
|
||||
struct mux_control *mux_control_get(struct device *dev, const char *mux_name);
|
||||
struct mux_control *mux_control_get_optional(struct device *dev, const char *mux_name);
|
||||
void mux_control_put(struct mux_control *mux);
|
||||
|
||||
struct mux_control *devm_mux_control_get(struct device *dev,
|
||||
const char *mux_name);
|
||||
struct mux_state *devm_mux_state_get(struct device *dev,
|
||||
const char *mux_name);
|
||||
struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name);
|
||||
struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name);
|
||||
struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name);
|
||||
struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name);
|
||||
struct mux_state *devm_mux_state_get_optional_selected(struct device *dev, const char *mux_name);
|
||||
|
||||
#else
|
||||
|
||||
static inline unsigned int mux_control_states(struct mux_control *mux)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int __must_check mux_control_select_delay(struct mux_control *mux,
|
||||
unsigned int state, unsigned int delay_us)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline int __must_check mux_state_select_delay(struct mux_state *mstate,
|
||||
unsigned int delay_us)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline int __must_check mux_control_try_select_delay(struct mux_control *mux,
|
||||
unsigned int state,
|
||||
unsigned int delay_us)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline int __must_check mux_state_try_select_delay(struct mux_state *mstate,
|
||||
unsigned int delay_us)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int __must_check mux_control_select(struct mux_control *mux,
|
||||
unsigned int state)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int __must_check mux_state_select(struct mux_state *mstate)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int __must_check mux_control_try_select(struct mux_control *mux,
|
||||
unsigned int state)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int __must_check mux_state_try_select(struct mux_state *mstate)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int mux_control_deselect(struct mux_control *mux)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline int mux_state_deselect(struct mux_state *mstate)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
static inline struct mux_control *mux_control_get_optional(struct device *dev,
|
||||
const char *mux_name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline void mux_control_put(struct mux_control *mux) {}
|
||||
|
||||
static inline struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
static inline struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
static inline struct mux_state *devm_mux_state_get_optional(struct device *dev,
|
||||
const char *mux_name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline struct mux_state *devm_mux_state_get_selected(struct device *dev,
|
||||
const char *mux_name)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
static inline struct mux_state *devm_mux_state_get_optional_selected(struct device *dev,
|
||||
const char *mux_name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MULTIPLEXER */
|
||||
|
||||
#endif /* _LINUX_MUX_CONSUMER_H */
|
||||
|
||||
Reference in New Issue
Block a user