A :c:type:``struct blk_keyslot_manager`` should be set up by device drivers in
the ``request_queue`` of the device. The device driver needs to call
-``blk_ksm_init`` on the ``blk_keyslot_manager``, which specifying the number of
-keyslots supported by the hardware.
+``blk_ksm_init`` (or its resource-managed variant ``devm_blk_ksm_init``) on the
+``blk_keyslot_manager``, while specifying the number of keyslots supported by
+the hardware.
The device driver also needs to tell the KSM how to actually manipulate the
IE hardware in the device to do things like programming the crypto key into
"should have" at the point in time when the function is called. This is useful
e.g. if a device loses all its keys on runtime power down/up.
-``blk_ksm_destroy`` should be called to free up all resources used by a keyslot
-manager upon ``blk_ksm_init``, once the ``blk_keyslot_manager`` is no longer
-needed.
-
+If the driver used ``blk_ksm_init`` instead of ``devm_blk_ksm_init``, then
+``blk_ksm_destroy`` should be called to free up all resources used by a
+``blk_keyslot_manager`` once it is no longer needed.
Layered Devices
===============
- const: allwinner,sun9i-a80-mmc
- const: allwinner,sun50i-a64-emmc
- const: allwinner,sun50i-a64-mmc
+ - const: allwinner,sun50i-a100-emmc
+ - const: allwinner,sun50i-a100-mmc
- items:
- const: allwinner,sun8i-a83t-mmc
- const: allwinner,sun7i-a20-mmc
- items:
- const: allwinner,sun50i-h6-mmc
- const: allwinner,sun50i-a64-mmc
+ - items:
+ - const: allwinner,sun50i-h616-emmc
+ - const: allwinner,sun50i-a100-emmc
+ - items:
+ - const: allwinner,sun50i-h616-mmc
+ - const: allwinner,sun50i-a100-mmc
reg:
maxItems: 1
--- /dev/null
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mmc/arm,pl18x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ARM PrimeCell MultiMedia Card Interface (MMCI) PL180 and PL181
+
+maintainers:
+ - Linus Walleij <linus.walleij@linaro.org>
+ - Ulf Hansson <ulf.hansson@linaro.org>
+
+description:
+ The ARM PrimeCells 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.
+
+allOf:
+ - $ref: /schemas/arm/primecell.yaml#
+ - $ref: mmc-controller.yaml#
+
+# We need a select here so we don't match all nodes with 'arm,primecell'
+select:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - arm,pl180
+ - arm,pl181
+ - arm,pl18x
+ required:
+ - compatible
+
+properties:
+ compatible:
+ oneOf:
+ - description: The first version of the block, simply called
+ PL180 and found in the ARM Integrator IM/PD1 logic module.
+ items:
+ - const: arm,pl180
+ - const: arm,primecell
+ - description: The improved version of the block, found in the
+ ARM Versatile and later reference designs. Further revisions
+ exist but get detected at runtime by reading some magic numbers
+ in the PrimeCell ID registers.
+ items:
+ - const: arm,pl181
+ - const: arm,primecell
+ - description: Wildcard entry that will let the operating system
+ inspect the PrimeCell ID registers to determine which hardware
+ variant of PL180 or PL181 this is.
+ items:
+ - const: arm,pl18x
+ - const: arm,primecell
+
+ clocks:
+ description: One or two clocks, the "apb_pclk" and the "MCLK"
+ which is the core block clock. The names are not compulsory.
+ minItems: 1
+ maxItems: 2
+
+ power-domains: true
+
+ resets:
+ maxItems: 1
+
+ reg:
+ description: the MMIO memory window must be exactly 4KB (0x1000) and the
+ layout should provide the PrimeCell ID registers so that the device can
+ be discovered. On ST Micro variants, a second register window may be
+ defined if a delay block is present and used for tuning.
+
+ interrupts:
+ description: The first interrupt is the command interrupt and corresponds
+ to the event at the end of a command. The second interrupt is the
+ PIO (polled I/O) interrupt and occurs when the FIFO needs to be
+ emptied as part of a bulk read from the card. Some variants have these
+ two interrupts wired into the same line (logic OR) and in that case
+ only one interrupt may be provided.
+ minItems: 1
+ maxItems: 2
+
+ st,sig-dir-dat0:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: ST Micro-specific property, bus signal direction pins used for
+ DAT[0].
+
+ st,sig-dir-dat2:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: ST Micro-specific property, bus signal direction pins used for
+ DAT[2].
+
+ st,sig-dir-dat31:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: ST Micro-specific property, bus signal direction pins used for
+ DAT[3] and DAT[1].
+
+ st,sig-dir-dat74:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: ST Micro-specific property, bus signal direction pins used for
+ DAT[7] and DAT[4].
+
+ st,sig-dir-cmd:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: ST Micro-specific property, CMD signal direction used for
+ pin CMD.
+
+ st,sig-pin-fbclk:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: ST Micro-specific property, feedback clock FBCLK signal pin
+ in use.
+
+ st,sig-dir:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: ST Micro-specific property, signal direction polarity used for
+ pins CMD, DAT[0], DAT[1], DAT[2] and DAT[3].
+
+ st,neg-edge:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: ST Micro-specific property, data and command phase relation,
+ generated on the sd clock falling edge.
+
+ st,use-ckin:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: ST Micro-specific property, use CKIN pin from an external
+ driver to sample the receive data (for example with a voltage switch
+ transceiver).
+
+ st,cmd-gpios:
+ maxItems: 1
+ description:
+ The GPIO matching the CMD pin.
+
+ st,ck-gpios:
+ maxItems: 1
+ description:
+ The GPIO matching the CK pin.
+
+ st,ckin-gpios:
+ maxItems: 1
+ description:
+ The GPIO matching the CKIN pin.
+
+dependencies:
+ st,cmd-gpios: [ "st,use-ckin" ]
+ st,ck-gpios: [ "st,use-ckin" ]
+ st,ckin-gpios: [ "st,use-ckin" ]
+
+unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/gpio/gpio.h>
+
+ mmc@5000 {
+ compatible = "arm,pl180", "arm,primecell";
+ reg = <0x5000 0x1000>;
+ interrupts-extended = <&vic 22 &sic 1>;
+ clocks = <&xtal24mhz>, <&pclk>;
+ clock-names = "mclk", "apb_pclk";
+ };
+
+ mmc@80126000 {
+ compatible = "arm,pl18x", "arm,primecell";
+ reg = <0x80126000 0x1000>;
+ interrupts = <0 60 IRQ_TYPE_LEVEL_HIGH>;
+ dmas = <&dma 29 0 0x2>, <&dma 29 0 0x0>;
+ dma-names = "rx", "tx";
+ clocks = <&prcc_kclk 1 5>, <&prcc_pclk 1 5>;
+ clock-names = "sdi", "apb_pclk";
+ max-frequency = <100000000>;
+ bus-width = <4>;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+ cd-gpios = <&gpio2 31 0x4>;
+ st,sig-dir-dat0;
+ st,sig-dir-dat2;
+ st,sig-dir-cmd;
+ st,sig-pin-fbclk;
+ vmmc-supply = <&ab8500_ldo_aux3_reg>;
+ vqmmc-supply = <&vmmci>;
+ };
+
+ mmc@101f6000 {
+ compatible = "arm,pl18x", "arm,primecell";
+ reg = <0x101f6000 0x1000>;
+ clocks = <&sdiclk>, <&pclksdi>;
+ clock-names = "mclk", "apb_pclk";
+ interrupt-parent = <&vica>;
+ interrupts = <22>;
+ max-frequency = <400000>;
+ bus-width = <4>;
+ cap-mmc-highspeed;
+ cap-sd-highspeed;
+ full-pwr-cycle;
+ st,sig-dir-dat0;
+ st,sig-dir-dat2;
+ st,sig-dir-dat31;
+ st,sig-dir-cmd;
+ st,sig-pin-fbclk;
+ vmmc-supply = <&vmmc_regulator>;
+ };
+
+ mmc@52007000 {
+ compatible = "arm,pl18x", "arm,primecell";
+ arm,primecell-periphid = <0x10153180>;
+ reg = <0x52007000 0x1000>;
+ interrupts = <49>;
+ interrupt-names = "cmd_irq";
+ clocks = <&rcc 0>;
+ clock-names = "apb_pclk";
+ resets = <&rcc 1>;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+ max-frequency = <120000000>;
+ };
- "marvell,armada-3700-sdhci": For controllers on Armada-3700 SoC.
Must provide a second register area and marvell,pad-type.
- "marvell,armada-ap806-sdhci": For controllers on Armada AP806.
+ - "marvell,armada-ap807-sdhci": For controllers on Armada AP807.
- "marvell,armada-cp110-sdhci": For controllers on Armada CP110.
- clocks:
+++ /dev/null
-* ARM PrimeCell MultiMedia Card Interface (MMCI) PL180/1
-
-The ARM PrimeCell MMCI PL180 and PL181 provides an interface for
-reading and writing to MultiMedia and SD cards alike.
-
-This file documents differences between the core properties described
-by mmc.txt and the properties used by the mmci driver. Using "st" as
-the prefix for a property, indicates support by the ST Micro variant.
-
-Required properties:
-- compatible : contains "arm,pl18x", "arm,primecell".
-- vmmc-supply : phandle to the regulator device tree node, mentioned
- as the VCC/VDD supply in the eMMC/SD specs.
-
-Optional properties:
-- arm,primecell-periphid : contains the PrimeCell Peripheral ID, it overrides
- the ID provided by the HW
-- resets : phandle to internal reset line.
- Should be defined for sdmmc variant.
-- vqmmc-supply : phandle to the regulator device tree node, mentioned
- as the VCCQ/VDD_IO supply in the eMMC/SD specs.
-specific for ux500 variant:
-- st,sig-dir-dat0 : bus signal direction pin used for DAT[0].
-- st,sig-dir-dat2 : bus signal direction pin used for DAT[2].
-- st,sig-dir-dat31 : bus signal direction pin used for DAT[3] and DAT[1].
-- st,sig-dir-dat74 : bus signal direction pin used for DAT[4] to DAT[7].
-- st,sig-dir-cmd : cmd signal direction pin used for CMD.
-- st,sig-pin-fbclk : feedback clock signal pin used.
-
-specific for sdmmc variant:
-- reg : a second base register may be defined if a delay
- block is present and used for tuning.
-- st,sig-dir : signal direction polarity used for cmd, dat0 dat123.
-- st,neg-edge : data & command phase relation, generated on
- sd clock falling edge.
-- st,use-ckin : use ckin pin from an external driver to sample
- the receive data (example: with voltage
- switch transceiver).
-
-Deprecated properties:
-- mmc-cap-mmc-highspeed : indicates whether MMC is high speed capable.
-- mmc-cap-sd-highspeed : indicates whether SD is high speed capable.
-
-Example:
-
-sdi0_per1@80126000 {
- compatible = "arm,pl18x", "arm,primecell";
- reg = <0x80126000 0x1000>;
- interrupts = <0 60 IRQ_TYPE_LEVEL_HIGH>;
-
- dmas = <&dma 29 0 0x2>, /* Logical - DevToMem */
- <&dma 29 0 0x0>; /* Logical - MemToDev */
- dma-names = "rx", "tx";
-
- clocks = <&prcc_kclk 1 5>, <&prcc_pclk 1 5>;
- clock-names = "sdi", "apb_pclk";
-
- max-frequency = <100000000>;
- bus-width = <4>;
- cap-sd-highspeed;
- cap-mmc-highspeed;
- cd-gpios = <&gpio2 31 0x4>; // 95
- st,sig-dir-dat0;
- st,sig-dir-dat2;
- st,sig-dir-cmd;
- st,sig-pin-fbclk;
-
- vmmc-supply = <&ab8500_ldo_aux3_reg>;
- vqmmc-supply = <&vmmci>;
-
- pinctrl-names = "default", "sleep";
- pinctrl-0 = <&sdi0_default_mode>;
- pinctrl-1 = <&sdi0_sleep_mode>;
-};
- renesas,sdhi-r8a77980 # R-Car V3H
- renesas,sdhi-r8a77990 # R-Car E3
- renesas,sdhi-r8a77995 # R-Car D3
+ - renesas,sdhi-r8a779a0 # R-Car V3U
- const: renesas,rcar-gen3-sdhi # R-Car Gen3 or RZ/G2
reg:
properties:
compatible:
- enum:
- - ti,am654-sdhci-5.1
- - ti,j721e-sdhci-8bit
- - ti,j721e-sdhci-4bit
- - ti,j7200-sdhci-8bit
- - ti,j721e-sdhci-4bit
+ oneOf:
+ - const: ti,am654-sdhci-5.1
+ - const: ti,j721e-sdhci-8bit
+ - const: ti,j721e-sdhci-4bit
+ - const: ti,j721e-sdhci-4bit
+ - const: ti,am64-sdhci-8bit
+ - const: ti,am64-sdhci-4bit
+ - items:
+ - const: ti,j7200-sdhci-8bit
+ - const: ti,j721e-sdhci-8bit
+ - items:
+ - const: ti,j7200-sdhci-4bit
+ - const: ti,j721e-sdhci-4bit
reg:
maxItems: 2
"qcom,msm8916-sdhci", "qcom,sdhci-msm-v4"
"qcom,msm8992-sdhci", "qcom,sdhci-msm-v4"
"qcom,msm8996-sdhci", "qcom,sdhci-msm-v4"
- "qcom,sm8250-sdhci", "qcom,sdhci-msm-v5"
- "qcom,sdm845-sdhci", "qcom,sdhci-msm-v5"
"qcom,qcs404-sdhci", "qcom,sdhci-msm-v5"
"qcom,sc7180-sdhci", "qcom,sdhci-msm-v5";
+ "qcom,sdm845-sdhci", "qcom,sdhci-msm-v5"
+ "qcom,sdx55-sdhci", "qcom,sdhci-msm-v5";
+ "qcom,sm8250-sdhci", "qcom,sdhci-msm-v5"
NOTE that some old device tree files may be floating around that only
have the string "qcom,sdhci-msm-v4" without the SoC compatible string
but doing that should be considered a deprecated practice.
- SD Core register map (required for controllers earlier than msm-v5)
- CQE register map (Optional, CQE support is present on SDHC instance meant
for eMMC and version v4.2 and above)
+ - Inline Crypto Engine register map (optional)
- reg-names: When CQE register map is supplied, below reg-names are required
- "hc" for Host controller register map
- "core" for SD core register map
- "cqhci" for CQE register map
+ - "ice" for Inline Crypto Engine register map (optional)
- interrupts: Should contain an interrupt-specifiers for the interrupts:
- Host controller interrupt (required)
- pinctrl-names: Should contain only one value - "default".
"xo" - TCXO clock (optional)
"cal" - reference clock for RCLK delay calibration (optional)
"sleep" - sleep clock for RCLK delay calibration (optional)
+ "ice" - clock for Inline Crypto Engine (optional)
- qcom,ddr-config: Certain chipsets and platforms require particular settings
for the DDR_CONFIG register. Use this field to specify the register
+++ /dev/null
-* SiRFprimII/marco/atlas6 SDHCI Controller
-
-This file documents differences between the core properties in mmc.txt
-and the properties used by the sdhci-sirf driver.
-
-Required properties:
-- compatible: sirf,prima2-sdhc
-
-Optional properties:
-- cd-gpios: card detect gpio, with zero flags.
-
-Example:
-
- sd0: sdhci@56000000 {
- compatible = "sirf,prima2-sdhc";
- reg = <0xcd000000 0x100000>;
- cd-gpios = <&gpio 6 0>;
- };
+++ /dev/null
-* ZTE specific extensions to the Synopsys Designware Mobile Storage
- Host Controller
-
-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 ZTE specific
-extensions to the Synopsys Designware Mobile Storage Host Controller.
-
-Required Properties:
-
-* compatible: should be
- - "zte,zx296718-dw-mshc": for ZX SoCs
-
-Example:
-
- mmc1: mmc@1110000 {
- compatible = "zte,zx296718-dw-mshc";
- reg = <0x01110000 0x1000>;
- interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
- fifo-depth = <32>;
- data-addr = <0x200>;
- fifo-watermark-aligned;
- bus-width = <4>;
- clock-frequency = <50000000>;
- clocks = <&topcrm SD0_AHB>, <&topcrm SD0_WCLK>;
- clock-names = "biu", "ciu";
- max-frequency = <50000000>;
- cap-sdio-irq;
- cap-sd-highspeed;
- };
F: drivers/irqchip/irq-aspeed-scu-ic.c
F: include/dt-bindings/interrupt-controller/aspeed-scu-ic.h
+ASPEED SD/MMC DRIVER
+M: Andrew Jeffery <andrew@aj.id.au>
+L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers)
+L: openbmc@lists.ozlabs.org (moderated for non-subscribers)
+L: linux-mmc@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml
+F: drivers/mmc/host/sdhci-of-aspeed*
+
ASPEED VIDEO ENGINE DRIVER
M: Eddie James <eajames@linux.ibm.com>
L: linux-media@vger.kernel.org
#define pr_fmt(fmt) "blk-crypto: " fmt
#include <linux/keyslot-manager.h>
+#include <linux/device.h>
#include <linux/atomic.h>
#include <linux/mutex.h>
#include <linux/pm_runtime.h>
}
EXPORT_SYMBOL_GPL(blk_ksm_init);
+static void blk_ksm_destroy_callback(void *ksm)
+{
+ blk_ksm_destroy(ksm);
+}
+
+/**
+ * devm_blk_ksm_init() - Resource-managed blk_ksm_init()
+ * @dev: The device which owns the blk_keyslot_manager.
+ * @ksm: The blk_keyslot_manager to initialize.
+ * @num_slots: The number of key slots to manage.
+ *
+ * Like blk_ksm_init(), but causes blk_ksm_destroy() to be called automatically
+ * on driver detach.
+ *
+ * Return: 0 on success, or else a negative error code.
+ */
+int devm_blk_ksm_init(struct device *dev, struct blk_keyslot_manager *ksm,
+ unsigned int num_slots)
+{
+ int err = blk_ksm_init(ksm, num_slots);
+
+ if (err)
+ return err;
+
+ return devm_add_action_or_reset(dev, blk_ksm_destroy_callback, ksm);
+}
+EXPORT_SYMBOL_GPL(devm_blk_ksm_init);
+
static inline struct hlist_head *
blk_ksm_hash_bucket_for_key(struct blk_keyslot_manager *ksm,
const struct blk_crypto_key *key)
* qcom_scm_ice_invalidate_key() - Invalidate an inline encryption key
* @index: the keyslot to invalidate
*
- * The UFSHCI standard defines a standard way to do this, but it doesn't work on
- * these SoCs; only this SCM call does.
+ * The UFSHCI and eMMC standards define a standard way to do this, but it
+ * doesn't work on these SoCs; only this SCM call does.
+ *
+ * It is assumed that the SoC has only one ICE instance being used, as this SCM
+ * call doesn't specify which ICE instance the keyslot belongs to.
*
* Return: 0 on success; -errno on failure.
*/
* units, e.g. 1 = 512 bytes, 8 = 4096 bytes, etc.
*
* Program a key into a keyslot of Qualcomm ICE (Inline Crypto Engine), where it
- * can then be used to encrypt/decrypt UFS I/O requests inline.
+ * can then be used to encrypt/decrypt UFS or eMMC I/O requests inline.
+ *
+ * The UFSHCI and eMMC standards define a standard way to do this, but it
+ * doesn't work on these SoCs; only this SCM call does.
*
- * The UFSHCI standard defines a standard way to do this, but it doesn't work on
- * these SoCs; only this SCM call does.
+ * It is assumed that the SoC has only one ICE instance being used, as this SCM
+ * call doesn't specify which ICE instance the keyslot belongs to.
*
* Return: 0 on success; -errno on failure.
*/
This driver is only of interest to those developing or
testing a host driver. Most people should say N here.
+config MMC_CRYPTO
+ bool "MMC Crypto Engine Support"
+ depends on BLK_INLINE_ENCRYPTION
+ help
+ Enable Crypto Engine Support in MMC.
+ Enabling this makes it possible for the kernel to use the crypto
+ capabilities of the MMC device (if present) to perform crypto
+ operations on data being transferred to/from the device.
mmc_block-objs := block.o queue.o
obj-$(CONFIG_MMC_TEST) += mmc_test.o
obj-$(CONFIG_SDIO_UART) += sdio_uart.o
+mmc_core-$(CONFIG_MMC_CRYPTO) += crypto.o
#include "block.h"
#include "core.h"
#include "card.h"
+#include "crypto.h"
#include "host.h"
#include "bus.h"
#include "mmc_ops.h"
memset(brq, 0, sizeof(struct mmc_blk_request));
+ mmc_crypto_prepare_req(mqrq);
+
brq->mrq.data = &brq->data;
brq->mrq.tag = req->tag;
#include "core.h"
#include "card.h"
+#include "crypto.h"
#include "bus.h"
#include "host.h"
#include "sdio_bus.h"
host->cqe_ops->cqe_recovery_start(host);
memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = MMC_STOP_TRANSMISSION,
- cmd.flags = MMC_RSP_R1B | MMC_CMD_AC,
+ cmd.opcode = MMC_STOP_TRANSMISSION;
+ cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
cmd.flags &= ~MMC_RSP_CRC; /* Ignore CRC */
- cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT,
+ cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT;
mmc_wait_for_cmd(host, &cmd, 0);
memset(&cmd, 0, sizeof(cmd));
cmd.arg = 1; /* Discard entire queue */
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
cmd.flags &= ~MMC_RSP_CRC; /* Ignore CRC */
- cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT,
+ cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT;
err = mmc_wait_for_cmd(host, &cmd, 0);
host->cqe_ops->cqe_recovery_finish(host);
host->ops->hs400_enhanced_strobe(host, &host->ios);
mmc_set_ios(host);
+
+ mmc_crypto_set_initial_state(host);
}
/**
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MMC crypto engine (inline encryption) support
+ *
+ * Copyright 2020 Google LLC
+ */
+
+#include <linux/blk-crypto.h>
+#include <linux/mmc/host.h>
+
+#include "core.h"
+#include "crypto.h"
+#include "queue.h"
+
+void mmc_crypto_set_initial_state(struct mmc_host *host)
+{
+ /* Reset might clear all keys, so reprogram all the keys. */
+ if (host->caps2 & MMC_CAP2_CRYPTO)
+ blk_ksm_reprogram_all_keys(&host->ksm);
+}
+
+void mmc_crypto_setup_queue(struct request_queue *q, struct mmc_host *host)
+{
+ if (host->caps2 & MMC_CAP2_CRYPTO)
+ blk_ksm_register(&host->ksm, q);
+}
+EXPORT_SYMBOL_GPL(mmc_crypto_setup_queue);
+
+void mmc_crypto_prepare_req(struct mmc_queue_req *mqrq)
+{
+ struct request *req = mmc_queue_req_to_req(mqrq);
+ struct mmc_request *mrq = &mqrq->brq.mrq;
+
+ if (!req->crypt_keyslot)
+ return;
+
+ mrq->crypto_enabled = true;
+ mrq->crypto_key_slot = blk_ksm_get_slot_idx(req->crypt_keyslot);
+
+ /*
+ * For now we assume that all MMC drivers set max_dun_bytes_supported=4,
+ * which is the limit for CQHCI crypto. So all DUNs should be 32-bit.
+ */
+ WARN_ON_ONCE(req->crypt_ctx->bc_dun[0] > U32_MAX);
+
+ mrq->data_unit_num = req->crypt_ctx->bc_dun[0];
+}
+EXPORT_SYMBOL_GPL(mmc_crypto_prepare_req);
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * MMC crypto engine (inline encryption) support
+ *
+ * Copyright 2020 Google LLC
+ */
+
+#ifndef _MMC_CORE_CRYPTO_H
+#define _MMC_CORE_CRYPTO_H
+
+struct mmc_host;
+struct mmc_queue_req;
+struct request_queue;
+
+#ifdef CONFIG_MMC_CRYPTO
+
+void mmc_crypto_set_initial_state(struct mmc_host *host);
+
+void mmc_crypto_setup_queue(struct request_queue *q, struct mmc_host *host);
+
+void mmc_crypto_prepare_req(struct mmc_queue_req *mqrq);
+
+#else /* CONFIG_MMC_CRYPTO */
+
+static inline void mmc_crypto_set_initial_state(struct mmc_host *host)
+{
+}
+
+static inline void mmc_crypto_setup_queue(struct request_queue *q,
+ struct mmc_host *host)
+{
+}
+
+static inline void mmc_crypto_prepare_req(struct mmc_queue_req *mqrq)
+{
+}
+
+#endif /* !CONFIG_MMC_CRYPTO */
+
+#endif /* _MMC_CORE_CRYPTO_H */
#include <linux/mmc/slot-gpio.h>
#include "core.h"
+#include "crypto.h"
#include "host.h"
#include "slot-gpio.h"
#include "pwrseq.h"
mmc_retune_needed(host);
}
+static void mmc_of_parse_timing_phase(struct device *dev, const char *prop,
+ struct mmc_clk_phase *phase)
+{
+ int degrees[2] = {0};
+ int rc;
+
+ rc = device_property_read_u32_array(dev, prop, degrees, 2);
+ phase->valid = !rc;
+ if (phase->valid) {
+ phase->in_deg = degrees[0];
+ phase->out_deg = degrees[1];
+ }
+}
+
+void
+mmc_of_parse_clk_phase(struct mmc_host *host, struct mmc_clk_phase_map *map)
+{
+ struct device *dev = host->parent;
+
+ mmc_of_parse_timing_phase(dev, "clk-phase-legacy",
+ &map->phase[MMC_TIMING_LEGACY]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-mmc-hs",
+ &map->phase[MMC_TIMING_MMC_HS]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-sd-hs",
+ &map->phase[MMC_TIMING_SD_HS]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-uhs-sdr12",
+ &map->phase[MMC_TIMING_UHS_SDR12]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-uhs-sdr25",
+ &map->phase[MMC_TIMING_UHS_SDR25]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-uhs-sdr50",
+ &map->phase[MMC_TIMING_UHS_SDR50]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-uhs-sdr104",
+ &map->phase[MMC_TIMING_UHS_SDR104]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-uhs-ddr50",
+ &map->phase[MMC_TIMING_UHS_DDR50]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-mmc-ddr52",
+ &map->phase[MMC_TIMING_MMC_DDR52]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-mmc-hs200",
+ &map->phase[MMC_TIMING_MMC_HS200]);
+ mmc_of_parse_timing_phase(dev, "clk-phase-mmc-hs400",
+ &map->phase[MMC_TIMING_MMC_HS400]);
+}
+EXPORT_SYMBOL(mmc_of_parse_clk_phase);
+
/**
* mmc_of_parse() - parse host's device-tree node
* @host: host whose node should be parsed.
goto free_card;
if (err) {
- err = 0;
/*
* Just disable enhanced area off & sz
* will try to enable ERASE_GROUP_DEF
pr_warn("%s: Enabling HPI failed\n",
mmc_hostname(card->host));
card->ext_csd.hpi_en = 0;
- err = 0;
} else {
card->ext_csd.hpi_en = 1;
}
pr_warn("%s: Cache is supported, but failed to turn on (%d)\n",
mmc_hostname(card->host), err);
card->ext_csd.cache_ctrl = 0;
- err = 0;
} else {
card->ext_csd.cache_ctrl = 1;
}
mmc_hostname(card->host));
card->ext_csd.cmdq_support = false;
card->ext_csd.cmdq_depth = 0;
- err = 0;
}
}
/*
static int mmc_can_sleep(struct mmc_card *card)
{
- return (card && card->ext_csd.rev >= 3);
+ return card->ext_csd.rev >= 3;
}
static int mmc_sleep(struct mmc_host *host)
return 0;
}
-static int mmc_spi_send_csd(struct mmc_card *card, u32 *csd)
+static int mmc_spi_send_csd(struct mmc_host *host, u32 *csd)
{
int ret, i;
__be32 *csd_tmp;
if (!csd_tmp)
return -ENOMEM;
- ret = mmc_send_cxd_data(card, card->host, MMC_SEND_CSD, csd_tmp, 16);
+ ret = mmc_send_cxd_data(NULL, host, MMC_SEND_CSD, csd_tmp, 16);
if (ret)
goto err;
int mmc_send_csd(struct mmc_card *card, u32 *csd)
{
if (mmc_host_is_spi(card->host))
- return mmc_spi_send_csd(card, csd);
+ return mmc_spi_send_csd(card->host, csd);
return mmc_send_cxd_native(card->host, card->rca << 16, csd,
MMC_SEND_CSD);
* Fill the first couple of sectors of the card with known data
* so that bad reads/writes can be detected
*/
-static int __mmc_test_prepare(struct mmc_test_card *test, int write)
+static int __mmc_test_prepare(struct mmc_test_card *test, int write, int val)
{
int ret, i;
return ret;
if (write)
- memset(test->buffer, 0xDF, 512);
+ memset(test->buffer, val, 512);
else {
for (i = 0; i < 512; i++)
test->buffer[i] = i;
static int mmc_test_prepare_write(struct mmc_test_card *test)
{
- return __mmc_test_prepare(test, 1);
+ return __mmc_test_prepare(test, 1, 0xDF);
}
static int mmc_test_prepare_read(struct mmc_test_card *test)
{
- return __mmc_test_prepare(test, 0);
+ return __mmc_test_prepare(test, 0, 0);
}
static int mmc_test_cleanup(struct mmc_test_card *test)
{
- int ret, i;
-
- ret = mmc_test_set_blksize(test, 512);
- if (ret)
- return ret;
-
- memset(test->buffer, 0, 512);
-
- for (i = 0; i < BUFFER_SIZE / 512; i++) {
- ret = mmc_test_buffer_transfer(test, test->buffer, i, 512, 1);
- if (ret)
- return ret;
- }
-
- return 0;
+ return __mmc_test_prepare(test, 1, 0);
}
/*******************************************************************/
if (mmc_can_erase(test->card) &&
tdata->prepare & MMC_TEST_PREP_ERASE) {
ret = mmc_erase(test->card, dev_addr,
- size / 512, MMC_SECURE_ERASE_ARG);
+ size / 512, test->card->erase_arg);
if (ret)
ret = mmc_erase(test->card, dev_addr,
size / 512, MMC_ERASE_ARG);
mmc_test_free_dbgfs_file(card);
}
-static void mmc_test_shutdown(struct mmc_card *card)
-{
-}
-
static struct mmc_driver mmc_driver = {
.drv = {
.name = "mmc_test",
},
.probe = mmc_test_probe,
.remove = mmc_test_remove,
- .shutdown = mmc_test_shutdown,
};
static int __init mmc_test_init(void)
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/freezer.h>
-#include <linux/kthread.h>
#include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
#include <linux/backing-dev.h>
#include "block.h"
#include "core.h"
#include "card.h"
+#include "crypto.h"
#include "host.h"
#define MMC_DMA_MAP_MERGE_SEGMENTS 512
{
if ((mq->cqe_busy & MMC_CQE_DCMD_BUSY) && !mmc_cqe_dcmd_busy(mq))
mq->cqe_busy &= ~MMC_CQE_DCMD_BUSY;
-
- mq->cqe_busy &= ~MMC_CQE_QUEUE_FULL;
}
static inline bool mmc_cqe_can_dcmd(struct mmc_host *host)
mutex_init(&mq->complete_lock);
init_waitqueue_head(&mq->wait);
+
+ mmc_crypto_setup_queue(mq->queue, host);
}
static inline bool mmc_merge_capable(struct mmc_host *host)
int in_flight[MMC_ISSUE_MAX];
unsigned int cqe_busy;
#define MMC_CQE_DCMD_BUSY BIT(0)
-#define MMC_CQE_QUEUE_FULL BIT(1)
bool busy;
bool use_cqe;
bool recovery_needed;
return err;
}
-int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card)
+int mmc_sd_get_csd(struct mmc_card *card)
{
int err;
}
if (!oldcard) {
- err = mmc_sd_get_csd(host, card);
+ err = mmc_sd_get_csd(card);
if (err)
goto free_card;
struct mmc_card;
int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr);
-int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card);
+int mmc_sd_get_csd(struct mmc_card *card);
void mmc_decode_cid(struct mmc_card *card);
int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
bool reinit);
* Read CSD, before selecting the card
*/
if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
- err = mmc_sd_get_csd(host, card);
+ err = mmc_sd_get_csd(card);
if (err)
goto remove;
If unsure, say N.
+config MMC_SDHCI_OF_ASPEED_TEST
+ bool "Tests for the ASPEED SDHCI driver"
+ depends on MMC_SDHCI_OF_ASPEED && KUNIT=y
+ help
+ Enable KUnit tests for the ASPEED SDHCI driver. Select this
+ option only if you will boot the kernel for the purpose of running
+ unit tests (e.g. under UML or qemu).
+
+ The KUnit tests generally exercise parts of the driver that do not
+ directly touch the hardware, for example, the phase correction
+ calculations.
+
+ If unsure, say N.
+
config MMC_SDHCI_OF_AT91
tristate "SDHCI OF support for the Atmel SDMMC controller"
depends on MMC_SDHCI_PLTFM
If unsure, say N.
-config MMC_SDHCI_SIRF
- tristate "SDHCI support on CSR SiRFprimaII and SiRFmarco SoCs"
- depends on ARCH_SIRF || COMPILE_TEST
- depends on MMC_SDHCI_PLTFM
- select MMC_SDHCI_IO_ACCESSORS
- help
- This selects the SDHCI support for SiRF System-on-Chip devices.
-
- If you have a controller with this interface, say Y or M here.
-
- If unsure, say N.
-
config MMC_SDHCI_PXAV3
tristate "Marvell MMP2 SD Host Controller support (PXAV3)"
depends on CLKDEV_LOOKUP
depends on MMC_SDHCI_PLTFM
select MMC_SDHCI_IO_ACCESSORS
select MMC_CQHCI
+ select QCOM_SCM if MMC_CRYPTO && ARCH_QCOM
help
This selects the Secure Digital Host Controller Interface (SDHCI)
support present in Qualcomm SOCs. The controller supports
If you have an DAVINCI board with a Multimedia Card slot,
say Y or M here. If unsure, say N.
-config MMC_GOLDFISH
- tristate "goldfish qemu Multimedia Card Interface support"
- depends on GOLDFISH || COMPILE_TEST
- help
- This selects the Goldfish Multimedia card Interface emulation
- found on the Goldfish Android virtual device emulation.
-
config MMC_SPI
tristate "MMC/SD/SDIO over SPI"
depends on SPI_MASTER
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on RK3066, RK3188 and RK3288 SoC's.
-config MMC_DW_ZX
- tristate "ZTE specific extensions for Synopsys DW Memory Card Interface"
- depends on MMC_DW && ARCH_ZX
- select MMC_DW_PLTFM
- help
- This selects support for ZTE SoC specific extensions to the
- Synopsys DesignWare Memory Card Interface driver. Select this option
- for platforms based on ZX296718 SoC's.
-
config MMC_SH_MMCIF
tristate "SuperH Internal MMCIF support"
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
-obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o
obj-$(CONFIG_MMC_SDHCI_F_SDH30) += sdhci_f_sdh30.o
obj-$(CONFIG_MMC_SDHCI_MILBEAUT) += sdhci-milbeaut.o
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o
obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o
-obj-$(CONFIG_MMC_GOLDFISH) += android-goldfish.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
ifeq ($(CONFIG_OF),y)
obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o
-obj-$(CONFIG_MMC_DW_ZX) += dw_mmc-zx.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
obj-$(CONFIG_MMC_VUB300) += vub300.o
obj-$(CONFIG_MMC_SDHCI_OMAP) += sdhci-omap.o
obj-$(CONFIG_MMC_SDHCI_SPRD) += sdhci-sprd.o
obj-$(CONFIG_MMC_CQHCI) += cqhci.o
+cqhci-y += cqhci-core.o
+cqhci-$(CONFIG_MMC_CRYPTO) += cqhci-crypto.o
obj-$(CONFIG_MMC_HSQ) += mmc_hsq.o
ifeq ($(CONFIG_CB710_DEBUG),y)
+++ /dev/null
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright 2007, Google Inc.
- * Copyright 2012, Intel Inc.
- *
- * based on omap.c driver, which was
- * Copyright (C) 2004 Nokia Corporation
- * Written by Tuukka Tikkanen and Juha Yrjölä <juha.yrjola@nokia.com>
- * Misc hacks here and there by Tony Lindgren <tony@atomide.com>
- * Other hacks (DMA, SD, etc) by David Brownell
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/major.h>
-
-#include <linux/types.h>
-#include <linux/pci.h>
-#include <linux/interrupt.h>
-
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/errno.h>
-#include <linux/hdreg.h>
-#include <linux/kdev_t.h>
-#include <linux/blkdev.h>
-#include <linux/mutex.h>
-#include <linux/scatterlist.h>
-#include <linux/mmc/mmc.h>
-#include <linux/mmc/host.h>
-#include <linux/mmc/card.h>
-
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/dma-mapping.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-#include <linux/timer.h>
-#include <linux/clk.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#include <asm/types.h>
-#include <linux/uaccess.h>
-
-#define DRIVER_NAME "goldfish_mmc"
-
-#define BUFFER_SIZE 16384
-
-#define GOLDFISH_MMC_READ(host, addr) (readl(host->reg_base + addr))
-#define GOLDFISH_MMC_WRITE(host, addr, x) (writel(x, host->reg_base + addr))
-
-enum {
- /* status register */
- MMC_INT_STATUS = 0x00,
- /* set this to enable IRQ */
- MMC_INT_ENABLE = 0x04,
- /* set this to specify buffer address */
- MMC_SET_BUFFER = 0x08,
-
- /* MMC command number */
- MMC_CMD = 0x0C,
-
- /* MMC argument */
- MMC_ARG = 0x10,
-
- /* MMC response (or R2 bits 0 - 31) */
- MMC_RESP_0 = 0x14,
-
- /* MMC R2 response bits 32 - 63 */
- MMC_RESP_1 = 0x18,
-
- /* MMC R2 response bits 64 - 95 */
- MMC_RESP_2 = 0x1C,
-
- /* MMC R2 response bits 96 - 127 */
- MMC_RESP_3 = 0x20,
-
- MMC_BLOCK_LENGTH = 0x24,
- MMC_BLOCK_COUNT = 0x28,
-
- /* MMC state flags */
- MMC_STATE = 0x2C,
-
- /* MMC_INT_STATUS bits */
-
- MMC_STAT_END_OF_CMD = 1U << 0,
- MMC_STAT_END_OF_DATA = 1U << 1,
- MMC_STAT_STATE_CHANGE = 1U << 2,
- MMC_STAT_CMD_TIMEOUT = 1U << 3,
-
- /* MMC_STATE bits */
- MMC_STATE_INSERTED = 1U << 0,
- MMC_STATE_READ_ONLY = 1U << 1,
-};
-
-/*
- * Command types
- */
-#define OMAP_MMC_CMDTYPE_BC 0
-#define OMAP_MMC_CMDTYPE_BCR 1
-#define OMAP_MMC_CMDTYPE_AC 2
-#define OMAP_MMC_CMDTYPE_ADTC 3
-
-
-struct goldfish_mmc_host {
- struct mmc_request *mrq;
- struct mmc_command *cmd;
- struct mmc_data *data;
- struct device *dev;
- unsigned char id; /* 16xx chips have 2 MMC blocks */
- void *virt_base;
- unsigned int phys_base;
- int irq;
- unsigned char bus_mode;
- unsigned char hw_bus_mode;
-
- unsigned int sg_len;
- unsigned dma_done:1;
- unsigned dma_in_use:1;
-
- void __iomem *reg_base;
-};
-
-static inline int
-goldfish_mmc_cover_is_open(struct goldfish_mmc_host *host)
-{
- return 0;
-}
-
-static ssize_t
-goldfish_mmc_show_cover_switch(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct goldfish_mmc_host *host = dev_get_drvdata(dev);
-
- return sprintf(buf, "%s\n", goldfish_mmc_cover_is_open(host) ? "open" :
- "closed");
-}
-
-static DEVICE_ATTR(cover_switch, S_IRUGO, goldfish_mmc_show_cover_switch, NULL);
-
-static void
-goldfish_mmc_start_command(struct goldfish_mmc_host *host, struct mmc_command *cmd)
-{
- u32 cmdreg;
- u32 resptype;
- u32 cmdtype;
-
- host->cmd = cmd;
-
- resptype = 0;
- cmdtype = 0;
-
- /* Our hardware needs to know exact type */
- switch (mmc_resp_type(cmd)) {
- case MMC_RSP_NONE:
- break;
- case MMC_RSP_R1:
- case MMC_RSP_R1B:
- /* resp 1, 1b, 6, 7 */
- resptype = 1;
- break;
- case MMC_RSP_R2:
- resptype = 2;
- break;
- case MMC_RSP_R3:
- resptype = 3;
- break;
- default:
- dev_err(mmc_dev(mmc_from_priv(host)),
- "Invalid response type: %04x\n", mmc_resp_type(cmd));
- break;
- }
-
- if (mmc_cmd_type(cmd) == MMC_CMD_ADTC)
- cmdtype = OMAP_MMC_CMDTYPE_ADTC;
- else if (mmc_cmd_type(cmd) == MMC_CMD_BC)
- cmdtype = OMAP_MMC_CMDTYPE_BC;
- else if (mmc_cmd_type(cmd) == MMC_CMD_BCR)
- cmdtype = OMAP_MMC_CMDTYPE_BCR;
- else
- cmdtype = OMAP_MMC_CMDTYPE_AC;
-
- cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12);
-
- if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)
- cmdreg |= 1 << 6;
-
- if (cmd->flags & MMC_RSP_BUSY)
- cmdreg |= 1 << 11;
-
- if (host->data && !(host->data->flags & MMC_DATA_WRITE))
- cmdreg |= 1 << 15;
-
- GOLDFISH_MMC_WRITE(host, MMC_ARG, cmd->arg);
- GOLDFISH_MMC_WRITE(host, MMC_CMD, cmdreg);
-}
-
-static void goldfish_mmc_xfer_done(struct goldfish_mmc_host *host,
- struct mmc_data *data)
-{
- if (host->dma_in_use) {
- enum dma_data_direction dma_data_dir;
-
- dma_data_dir = mmc_get_dma_dir(data);
-
- if (dma_data_dir == DMA_FROM_DEVICE) {
- /*
- * We don't really have DMA, so we need
- * to copy from our platform driver buffer
- */
- sg_copy_from_buffer(data->sg, 1, host->virt_base,
- data->sg->length);
- }
- host->data->bytes_xfered += data->sg->length;
- dma_unmap_sg(mmc_dev(mmc_from_priv(host)), data->sg,
- host->sg_len, dma_data_dir);
- }
-
- host->data = NULL;
- host->sg_len = 0;
-
- /*
- * NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing
- * dozens of requests until the card finishes writing data.
- * It'd be cheaper to just wait till an EOFB interrupt arrives...
- */
-
- if (!data->stop) {
- host->mrq = NULL;
- mmc_request_done(mmc_from_priv(host), data->mrq);
- return;
- }
-
- goldfish_mmc_start_command(host, data->stop);
-}
-
-static void goldfish_mmc_end_of_data(struct goldfish_mmc_host *host,
- struct mmc_data *data)
-{
- if (!host->dma_in_use) {
- goldfish_mmc_xfer_done(host, data);
- return;
- }
- if (host->dma_done)
- goldfish_mmc_xfer_done(host, data);
-}
-
-static void goldfish_mmc_cmd_done(struct goldfish_mmc_host *host,
- struct mmc_command *cmd)
-{
- host->cmd = NULL;
- if (cmd->flags & MMC_RSP_PRESENT) {
- if (cmd->flags & MMC_RSP_136) {
- /* response type 2 */
- cmd->resp[3] =
- GOLDFISH_MMC_READ(host, MMC_RESP_0);
- cmd->resp[2] =
- GOLDFISH_MMC_READ(host, MMC_RESP_1);
- cmd->resp[1] =
- GOLDFISH_MMC_READ(host, MMC_RESP_2);
- cmd->resp[0] =
- GOLDFISH_MMC_READ(host, MMC_RESP_3);
- } else {
- /* response types 1, 1b, 3, 4, 5, 6 */
- cmd->resp[0] =
- GOLDFISH_MMC_READ(host, MMC_RESP_0);
- }
- }
-
- if (host->data == NULL || cmd->error) {
- host->mrq = NULL;
- mmc_request_done(mmc_from_priv(host), cmd->mrq);
- }
-}
-
-static irqreturn_t goldfish_mmc_irq(int irq, void *dev_id)
-{
- struct goldfish_mmc_host *host = (struct goldfish_mmc_host *)dev_id;
- u16 status;
- int end_command = 0;
- int end_transfer = 0;
- int state_changed = 0;
- int cmd_timeout = 0;
-
- while ((status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS)) != 0) {
- GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status);
-
- if (status & MMC_STAT_END_OF_CMD)
- end_command = 1;
-
- if (status & MMC_STAT_END_OF_DATA)
- end_transfer = 1;
-
- if (status & MMC_STAT_STATE_CHANGE)
- state_changed = 1;
-
- if (status & MMC_STAT_CMD_TIMEOUT) {
- end_command = 0;
- cmd_timeout = 1;
- }
- }
-
- if (cmd_timeout) {
- struct mmc_request *mrq = host->mrq;
- mrq->cmd->error = -ETIMEDOUT;
- host->mrq = NULL;
- mmc_request_done(mmc_from_priv(host), mrq);
- }
-
- if (end_command)
- goldfish_mmc_cmd_done(host, host->cmd);
-
- if (end_transfer) {
- host->dma_done = 1;
- goldfish_mmc_end_of_data(host, host->data);
- } else if (host->data != NULL) {
- /*
- * WORKAROUND -- after porting this driver from 2.6 to 3.4,
- * during device initialization, cases where host->data is
- * non-null but end_transfer is false would occur. Doing
- * nothing in such cases results in no further interrupts,
- * and initialization failure.
- * TODO -- find the real cause.
- */
- host->dma_done = 1;
- goldfish_mmc_end_of_data(host, host->data);
- }
-
- if (state_changed) {
- u32 state = GOLDFISH_MMC_READ(host, MMC_STATE);
- pr_info("%s: Card detect now %d\n", __func__,
- (state & MMC_STATE_INSERTED));
- mmc_detect_change(mmc_from_priv(host), 0);
- }
-
- if (!end_command && !end_transfer && !state_changed && !cmd_timeout) {
- status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS);
- dev_info(mmc_dev(mmc_from_priv(host)), "spurious irq 0x%04x\n",
- status);
- if (status != 0) {
- GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status);
- GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE, 0);
- }
- }
-
- return IRQ_HANDLED;
-}
-
-static void goldfish_mmc_prepare_data(struct goldfish_mmc_host *host,
- struct mmc_request *req)
-{
- struct mmc_data *data = req->data;
- int block_size;
- unsigned sg_len;
- enum dma_data_direction dma_data_dir;
-
- host->data = data;
- if (data == NULL) {
- GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, 0);
- GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, 0);
- host->dma_in_use = 0;
- return;
- }
-
- block_size = data->blksz;
-
- GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, data->blocks - 1);
- GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, block_size - 1);
-
- /*
- * Cope with calling layer confusion; it issues "single
- * block" writes using multi-block scatterlists.
- */
- sg_len = (data->blocks == 1) ? 1 : data->sg_len;
-
- dma_data_dir = mmc_get_dma_dir(data);
-
- host->sg_len = dma_map_sg(mmc_dev(mmc_from_priv(host)), data->sg,
- sg_len, dma_data_dir);
- host->dma_done = 0;
- host->dma_in_use = 1;
-
- if (dma_data_dir == DMA_TO_DEVICE) {
- /*
- * We don't really have DMA, so we need to copy to our
- * platform driver buffer
- */
- sg_copy_to_buffer(data->sg, 1, host->virt_base,
- data->sg->length);
- }
-}
-
-static void goldfish_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
-{
- struct goldfish_mmc_host *host = mmc_priv(mmc);
-
- WARN_ON(host->mrq != NULL);
-
- host->mrq = req;
- goldfish_mmc_prepare_data(host, req);
- goldfish_mmc_start_command(host, req->cmd);
-}
-
-static void goldfish_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
-{
- struct goldfish_mmc_host *host = mmc_priv(mmc);
-
- host->bus_mode = ios->bus_mode;
- host->hw_bus_mode = host->bus_mode;
-}
-
-static int goldfish_mmc_get_ro(struct mmc_host *mmc)
-{
- uint32_t state;
- struct goldfish_mmc_host *host = mmc_priv(mmc);
-
- state = GOLDFISH_MMC_READ(host, MMC_STATE);
- return ((state & MMC_STATE_READ_ONLY) != 0);
-}
-
-static const struct mmc_host_ops goldfish_mmc_ops = {
- .request = goldfish_mmc_request,
- .set_ios = goldfish_mmc_set_ios,
- .get_ro = goldfish_mmc_get_ro,
-};
-
-static int goldfish_mmc_probe(struct platform_device *pdev)
-{
- struct mmc_host *mmc;
- struct goldfish_mmc_host *host = NULL;
- struct resource *res;
- int ret = 0;
- int irq;
- dma_addr_t buf_addr;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- irq = platform_get_irq(pdev, 0);
- if (res == NULL || irq < 0)
- return -ENXIO;
-
- mmc = mmc_alloc_host(sizeof(struct goldfish_mmc_host), &pdev->dev);
- if (mmc == NULL) {
- ret = -ENOMEM;
- goto err_alloc_host_failed;
- }
-
- host = mmc_priv(mmc);
-
- pr_err("mmc: Mapping %lX to %lX\n", (long)res->start, (long)res->end);
- host->reg_base = ioremap(res->start, resource_size(res));
- if (host->reg_base == NULL) {
- ret = -ENOMEM;
- goto ioremap_failed;
- }
- host->virt_base = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE,
- &buf_addr, GFP_KERNEL);
-
- if (host->virt_base == 0) {
- ret = -ENOMEM;
- goto dma_alloc_failed;
- }
- host->phys_base = buf_addr;
-
- host->id = pdev->id;
- host->irq = irq;
-
- mmc->ops = &goldfish_mmc_ops;
- mmc->f_min = 400000;
- mmc->f_max = 24000000;
- mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->caps = MMC_CAP_4_BIT_DATA;
- mmc->caps2 = MMC_CAP2_NO_SDIO;
-
- /* Use scatterlist DMA to reduce per-transfer costs.
- * NOTE max_seg_size assumption that small blocks aren't
- * normally used (except e.g. for reading SD registers).
- */
- mmc->max_segs = 32;
- mmc->max_blk_size = 2048; /* MMC_BLOCK_LENGTH is 11 bits (+1) */
- mmc->max_blk_count = 2048; /* MMC_BLOCK_COUNT is 11 bits (+1) */
- mmc->max_req_size = BUFFER_SIZE;
- mmc->max_seg_size = mmc->max_req_size;
-
- ret = request_irq(host->irq, goldfish_mmc_irq, 0, DRIVER_NAME, host);
- if (ret) {
- dev_err(&pdev->dev, "Failed IRQ Adding goldfish MMC\n");
- goto err_request_irq_failed;
- }
-
- host->dev = &pdev->dev;
- platform_set_drvdata(pdev, host);
-
- ret = device_create_file(&pdev->dev, &dev_attr_cover_switch);
- if (ret)
- dev_warn(mmc_dev(mmc), "Unable to create sysfs attributes\n");
-
- GOLDFISH_MMC_WRITE(host, MMC_SET_BUFFER, host->phys_base);
- GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE,
- MMC_STAT_END_OF_CMD | MMC_STAT_END_OF_DATA |
- MMC_STAT_STATE_CHANGE | MMC_STAT_CMD_TIMEOUT);
-
- mmc_add_host(mmc);
- return 0;
-
-err_request_irq_failed:
- dma_free_coherent(&pdev->dev, BUFFER_SIZE, host->virt_base,
- host->phys_base);
-dma_alloc_failed:
- iounmap(host->reg_base);
-ioremap_failed:
- mmc_free_host(mmc);
-err_alloc_host_failed:
- return ret;
-}
-
-static int goldfish_mmc_remove(struct platform_device *pdev)
-{
- struct goldfish_mmc_host *host = platform_get_drvdata(pdev);
- struct mmc_host *mmc = mmc_from_priv(host);
-
- BUG_ON(host == NULL);
-
- mmc_remove_host(mmc);
- free_irq(host->irq, host);
- dma_free_coherent(&pdev->dev, BUFFER_SIZE, host->virt_base, host->phys_base);
- iounmap(host->reg_base);
- mmc_free_host(mmc);
- return 0;
-}
-
-static struct platform_driver goldfish_mmc_driver = {
- .probe = goldfish_mmc_probe,
- .remove = goldfish_mmc_remove,
- .driver = {
- .name = DRIVER_NAME,
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- },
-};
-
-module_platform_driver(goldfish_mmc_driver);
-MODULE_LICENSE("GPL v2");
}
}
-static void atmci_tasklet_func(unsigned long priv)
+static void atmci_tasklet_func(struct tasklet_struct *t)
{
- struct atmel_mci *host = (struct atmel_mci *)priv;
+ struct atmel_mci *host = from_tasklet(host, t, tasklet);
struct mmc_request *mrq = host->mrq;
struct mmc_data *data = host->data;
enum atmel_mci_state state = host->state;
dev_info(&host->pdev->dev,
"version: 0x%x\n", version);
- host->caps.has_dma_conf_reg = 0;
- host->caps.has_pdc = 1;
- host->caps.has_cfg_reg = 0;
- host->caps.has_cstor_reg = 0;
- host->caps.has_highspeed = 0;
- host->caps.has_rwproof = 0;
- host->caps.has_odd_clk_div = 0;
- host->caps.has_bad_data_ordering = 1;
- host->caps.need_reset_after_xfer = 1;
- host->caps.need_blksz_mul_4 = 1;
- host->caps.need_notbusy_for_read_ops = 0;
+ host->caps.has_dma_conf_reg = false;
+ host->caps.has_pdc = true;
+ host->caps.has_cfg_reg = false;
+ host->caps.has_cstor_reg = false;
+ host->caps.has_highspeed = false;
+ host->caps.has_rwproof = false;
+ host->caps.has_odd_clk_div = false;
+ host->caps.has_bad_data_ordering = true;
+ host->caps.need_reset_after_xfer = true;
+ host->caps.need_blksz_mul_4 = true;
+ host->caps.need_notbusy_for_read_ops = false;
/* keep only major version number */
switch (version & 0xf00) {
case 0x600:
case 0x500:
- host->caps.has_odd_clk_div = 1;
+ host->caps.has_odd_clk_div = true;
fallthrough;
case 0x400:
case 0x300:
- host->caps.has_dma_conf_reg = 1;
- host->caps.has_pdc = 0;
- host->caps.has_cfg_reg = 1;
- host->caps.has_cstor_reg = 1;
- host->caps.has_highspeed = 1;
+ host->caps.has_dma_conf_reg = true;
+ host->caps.has_pdc = false;
+ host->caps.has_cfg_reg = true;
+ host->caps.has_cstor_reg = true;
+ host->caps.has_highspeed = true;
fallthrough;
case 0x200:
- host->caps.has_rwproof = 1;
- host->caps.need_blksz_mul_4 = 0;
- host->caps.need_notbusy_for_read_ops = 1;
+ host->caps.has_rwproof = true;
+ host->caps.need_blksz_mul_4 = false;
+ host->caps.need_notbusy_for_read_ops = true;
fallthrough;
case 0x100:
- host->caps.has_bad_data_ordering = 0;
- host->caps.need_reset_after_xfer = 0;
+ host->caps.has_bad_data_ordering = false;
+ host->caps.need_reset_after_xfer = false;
fallthrough;
case 0x0:
break;
default:
- host->caps.has_pdc = 0;
+ host->caps.has_pdc = false;
dev_warn(&host->pdev->dev,
"Unmanaged mci version, set minimum capabilities\n");
break;
host->mapbase = regs->start;
- tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)host);
+ tasklet_setup(&host->tasklet, atmci_tasklet_func);
ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host);
if (ret) {
mmc_request_done(host->mmc, mrq);
}
-static void au1xmmc_tasklet_finish(unsigned long param)
+static void au1xmmc_tasklet_finish(struct tasklet_struct *t)
{
- struct au1xmmc_host *host = (struct au1xmmc_host *) param;
+ struct au1xmmc_host *host = from_tasklet(host, t, finish_task);
au1xmmc_finish_request(host);
}
au1xmmc_finish_request(host);
}
-static void au1xmmc_tasklet_data(unsigned long param)
+static void au1xmmc_tasklet_data(struct tasklet_struct *t)
{
- struct au1xmmc_host *host = (struct au1xmmc_host *)param;
+ struct au1xmmc_host *host = from_tasklet(host, t, data_task);
u32 status = __raw_readl(HOST_STATUS(host));
au1xmmc_data_complete(host, status);
if (host->platdata)
mmc->caps &= ~(host->platdata->mask_host_caps);
- tasklet_init(&host->data_task, au1xmmc_tasklet_data,
- (unsigned long)host);
+ tasklet_setup(&host->data_task, au1xmmc_tasklet_data);
- tasklet_init(&host->finish_task, au1xmmc_tasklet_finish,
- (unsigned long)host);
+ tasklet_setup(&host->finish_task, au1xmmc_tasklet_finish);
if (has_dbdma()) {
ret = au1xmmc_dbdma_init(host);
{
struct cvm_mmc_host *host = dev_id;
struct mmc_request *req;
- unsigned long flags = 0;
u64 emm_int, rsp_sts;
bool host_done;
if (host->need_irq_handler_lock)
- spin_lock_irqsave(&host->irq_handler_lock, flags);
+ spin_lock(&host->irq_handler_lock);
else
__acquire(&host->irq_handler_lock);
host->release_bus(host);
out:
if (host->need_irq_handler_lock)
- spin_unlock_irqrestore(&host->irq_handler_lock, flags);
+ spin_unlock(&host->irq_handler_lock);
else
__release(&host->irq_handler_lock);
return IRQ_RETVAL(emm_int != 0);
return 1;
}
-static void cb710_mmc_finish_request_tasklet(unsigned long data)
+static void cb710_mmc_finish_request_tasklet(struct tasklet_struct *t)
{
- struct mmc_host *mmc = (void *)data;
- struct cb710_mmc_reader *reader = mmc_priv(mmc);
+ struct cb710_mmc_reader *reader = from_tasklet(reader, t,
+ finish_req_tasklet);
struct mmc_request *mrq = reader->mrq;
reader->mrq = NULL;
- mmc_request_done(mmc, mrq);
+ mmc_request_done(mmc_from_priv(reader), mrq);
}
static const struct mmc_host_ops cb710_mmc_host = {
reader = mmc_priv(mmc);
- tasklet_init(&reader->finish_req_tasklet,
- cb710_mmc_finish_request_tasklet, (unsigned long)mmc);
+ tasklet_setup(&reader->finish_req_tasklet,
+ cb710_mmc_finish_request_tasklet);
spin_lock_init(&reader->irq_lock);
cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
#include <linux/mmc/card.h>
#include "cqhci.h"
+#include "cqhci-crypto.h"
#define DCMD_SLOT 31
#define NUM_SLOTS 32
if (cq_host->caps & CQHCI_TASK_DESC_SZ_128)
cqcfg |= CQHCI_TASK_DESC_SZ;
+ if (mmc->caps2 & MMC_CAP2_CRYPTO)
+ cqcfg |= CQHCI_CRYPTO_GENERAL_ENABLE;
+
cqhci_writel(cq_host, cqcfg, CQHCI_CFG);
cqhci_writel(cq_host, lower_32_bits(cq_host->desc_dma_base),
}
static void cqhci_prep_task_desc(struct mmc_request *mrq,
- u64 *data, bool intr)
+ struct cqhci_host *cq_host, int tag)
{
+ __le64 *task_desc = (__le64 __force *)get_desc(cq_host, tag);
u32 req_flags = mrq->data->flags;
+ u64 desc0;
- *data = CQHCI_VALID(1) |
+ desc0 = CQHCI_VALID(1) |
CQHCI_END(1) |
- CQHCI_INT(intr) |
+ CQHCI_INT(1) |
CQHCI_ACT(0x5) |
CQHCI_FORCED_PROG(!!(req_flags & MMC_DATA_FORCED_PRG)) |
CQHCI_DATA_TAG(!!(req_flags & MMC_DATA_DAT_TAG)) |
CQHCI_BLK_COUNT(mrq->data->blocks) |
CQHCI_BLK_ADDR((u64)mrq->data->blk_addr);
- pr_debug("%s: cqhci: tag %d task descriptor 0x%016llx\n",
- mmc_hostname(mrq->host), mrq->tag, (unsigned long long)*data);
+ task_desc[0] = cpu_to_le64(desc0);
+
+ if (cq_host->caps & CQHCI_TASK_DESC_SZ_128) {
+ u64 desc1 = cqhci_crypto_prep_task_desc(mrq);
+
+ task_desc[1] = cpu_to_le64(desc1);
+
+ pr_debug("%s: cqhci: tag %d task descriptor 0x%016llx%016llx\n",
+ mmc_hostname(mrq->host), mrq->tag, desc1, desc0);
+ } else {
+ pr_debug("%s: cqhci: tag %d task descriptor 0x%016llx\n",
+ mmc_hostname(mrq->host), mrq->tag, desc0);
+ }
}
static int cqhci_dma_map(struct mmc_host *host, struct mmc_request *mrq)
static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
int err = 0;
- u64 data = 0;
- u64 *task_desc = NULL;
int tag = cqhci_tag(mrq);
struct cqhci_host *cq_host = mmc->cqe_private;
unsigned long flags;
}
if (mrq->data) {
- task_desc = (__le64 __force *)get_desc(cq_host, tag);
- cqhci_prep_task_desc(mrq, &data, 1);
- *task_desc = cpu_to_le64(data);
+ cqhci_prep_task_desc(mrq, cq_host, tag);
+
err = cqhci_prep_tran_desc(mrq, cq_host, tag);
if (err) {
pr_err("%s: cqhci: failed to setup tx desc: %d\n",
struct cqhci_host *cq_host = mmc->cqe_private;
struct cqhci_slot *slot;
u32 terri;
+ u32 tdpe;
int tag;
spin_lock(&cq_host->lock);
}
}
+ /*
+ * Handle ICCE ("Invalid Crypto Configuration Error"). This should
+ * never happen, since the block layer ensures that all crypto-enabled
+ * I/O requests have a valid keyslot before they reach the driver.
+ *
+ * Note that GCE ("General Crypto Error") is different; it already got
+ * handled above by checking TERRI.
+ */
+ if (status & CQHCI_IS_ICCE) {
+ tdpe = cqhci_readl(cq_host, CQHCI_TDPE);
+ WARN_ONCE(1,
+ "%s: cqhci: invalid crypto configuration error. IRQ status: 0x%08x TDPE: 0x%08x\n",
+ mmc_hostname(mmc), status, tdpe);
+ while (tdpe != 0) {
+ tag = __ffs(tdpe);
+ tdpe &= ~(1 << tag);
+ slot = &cq_host->slot[tag];
+ if (!slot->mrq)
+ continue;
+ slot->flags = cqhci_error_flags(data_error, cmd_error);
+ cqhci_recovery_needed(mmc, slot->mrq, true);
+ }
+ }
+
if (!cq_host->recovery_halt) {
/*
* The only way to guarantee forward progress is to mark at
pr_debug("%s: cqhci: IRQ status: 0x%08x\n", mmc_hostname(mmc), status);
- if ((status & CQHCI_IS_RED) || cmd_error || data_error)
+ if ((status & (CQHCI_IS_RED | CQHCI_IS_GCE | CQHCI_IS_ICCE)) ||
+ cmd_error || data_error)
cqhci_error_irq(mmc, status, cmd_error, data_error);
if (status & CQHCI_IS_TCC) {
goto out_err;
}
+ err = cqhci_crypto_init(cq_host);
+ if (err) {
+ pr_err("%s: CQHCI crypto initialization failed\n",
+ mmc_hostname(mmc));
+ goto out_err;
+ }
+
spin_lock_init(&cq_host->lock);
init_completion(&cq_host->halt_comp);
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * CQHCI crypto engine (inline encryption) support
+ *
+ * Copyright 2020 Google LLC
+ */
+
+#include <linux/blk-crypto.h>
+#include <linux/keyslot-manager.h>
+#include <linux/mmc/host.h>
+
+#include "cqhci-crypto.h"
+
+/* Map from blk-crypto modes to CQHCI crypto algorithm IDs and key sizes */
+static const struct cqhci_crypto_alg_entry {
+ enum cqhci_crypto_alg alg;
+ enum cqhci_crypto_key_size key_size;
+} cqhci_crypto_algs[BLK_ENCRYPTION_MODE_MAX] = {
+ [BLK_ENCRYPTION_MODE_AES_256_XTS] = {
+ .alg = CQHCI_CRYPTO_ALG_AES_XTS,
+ .key_size = CQHCI_CRYPTO_KEY_SIZE_256,
+ },
+};
+
+static inline struct cqhci_host *
+cqhci_host_from_ksm(struct blk_keyslot_manager *ksm)
+{
+ struct mmc_host *mmc = container_of(ksm, struct mmc_host, ksm);
+
+ return mmc->cqe_private;
+}
+
+static int cqhci_crypto_program_key(struct cqhci_host *cq_host,
+ const union cqhci_crypto_cfg_entry *cfg,
+ int slot)
+{
+ u32 slot_offset = cq_host->crypto_cfg_register + slot * sizeof(*cfg);
+ int i;
+
+ if (cq_host->ops->program_key)
+ return cq_host->ops->program_key(cq_host, cfg, slot);
+
+ /* Clear CFGE */
+ cqhci_writel(cq_host, 0, slot_offset + 16 * sizeof(cfg->reg_val[0]));
+
+ /* Write the key */
+ for (i = 0; i < 16; i++) {
+ cqhci_writel(cq_host, le32_to_cpu(cfg->reg_val[i]),
+ slot_offset + i * sizeof(cfg->reg_val[0]));
+ }
+ /* Write dword 17 */
+ cqhci_writel(cq_host, le32_to_cpu(cfg->reg_val[17]),
+ slot_offset + 17 * sizeof(cfg->reg_val[0]));
+ /* Write dword 16, which includes the new value of CFGE */
+ cqhci_writel(cq_host, le32_to_cpu(cfg->reg_val[16]),
+ slot_offset + 16 * sizeof(cfg->reg_val[0]));
+ return 0;
+}
+
+static int cqhci_crypto_keyslot_program(struct blk_keyslot_manager *ksm,
+ const struct blk_crypto_key *key,
+ unsigned int slot)
+
+{
+ struct cqhci_host *cq_host = cqhci_host_from_ksm(ksm);
+ const union cqhci_crypto_cap_entry *ccap_array =
+ cq_host->crypto_cap_array;
+ const struct cqhci_crypto_alg_entry *alg =
+ &cqhci_crypto_algs[key->crypto_cfg.crypto_mode];
+ u8 data_unit_mask = key->crypto_cfg.data_unit_size / 512;
+ int i;
+ int cap_idx = -1;
+ union cqhci_crypto_cfg_entry cfg = {};
+ int err;
+
+ BUILD_BUG_ON(CQHCI_CRYPTO_KEY_SIZE_INVALID != 0);
+ for (i = 0; i < cq_host->crypto_capabilities.num_crypto_cap; i++) {
+ if (ccap_array[i].algorithm_id == alg->alg &&
+ ccap_array[i].key_size == alg->key_size &&
+ (ccap_array[i].sdus_mask & data_unit_mask)) {
+ cap_idx = i;
+ break;
+ }
+ }
+ if (WARN_ON(cap_idx < 0))
+ return -EOPNOTSUPP;
+
+ cfg.data_unit_size = data_unit_mask;
+ cfg.crypto_cap_idx = cap_idx;
+ cfg.config_enable = CQHCI_CRYPTO_CONFIGURATION_ENABLE;
+
+ if (ccap_array[cap_idx].algorithm_id == CQHCI_CRYPTO_ALG_AES_XTS) {
+ /* In XTS mode, the blk_crypto_key's size is already doubled */
+ memcpy(cfg.crypto_key, key->raw, key->size/2);
+ memcpy(cfg.crypto_key + CQHCI_CRYPTO_KEY_MAX_SIZE/2,
+ key->raw + key->size/2, key->size/2);
+ } else {
+ memcpy(cfg.crypto_key, key->raw, key->size);
+ }
+
+ err = cqhci_crypto_program_key(cq_host, &cfg, slot);
+
+ memzero_explicit(&cfg, sizeof(cfg));
+ return err;
+}
+
+static int cqhci_crypto_clear_keyslot(struct cqhci_host *cq_host, int slot)
+{
+ /*
+ * Clear the crypto cfg on the device. Clearing CFGE
+ * might not be sufficient, so just clear the entire cfg.
+ */
+ union cqhci_crypto_cfg_entry cfg = {};
+
+ return cqhci_crypto_program_key(cq_host, &cfg, slot);
+}
+
+static int cqhci_crypto_keyslot_evict(struct blk_keyslot_manager *ksm,
+ const struct blk_crypto_key *key,
+ unsigned int slot)
+{
+ struct cqhci_host *cq_host = cqhci_host_from_ksm(ksm);
+
+ return cqhci_crypto_clear_keyslot(cq_host, slot);
+}
+
+/*
+ * The keyslot management operations for CQHCI crypto.
+ *
+ * Note that the block layer ensures that these are never called while the host
+ * controller is runtime-suspended. However, the CQE won't necessarily be
+ * "enabled" when these are called, i.e. CQHCI_ENABLE might not be set in the
+ * CQHCI_CFG register. But the hardware allows that.
+ */
+static const struct blk_ksm_ll_ops cqhci_ksm_ops = {
+ .keyslot_program = cqhci_crypto_keyslot_program,
+ .keyslot_evict = cqhci_crypto_keyslot_evict,
+};
+
+static enum blk_crypto_mode_num
+cqhci_find_blk_crypto_mode(union cqhci_crypto_cap_entry cap)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cqhci_crypto_algs); i++) {
+ BUILD_BUG_ON(CQHCI_CRYPTO_KEY_SIZE_INVALID != 0);
+ if (cqhci_crypto_algs[i].alg == cap.algorithm_id &&
+ cqhci_crypto_algs[i].key_size == cap.key_size)
+ return i;
+ }
+ return BLK_ENCRYPTION_MODE_INVALID;
+}
+
+/**
+ * cqhci_crypto_init - initialize CQHCI crypto support
+ * @cq_host: a cqhci host
+ *
+ * If the driver previously set MMC_CAP2_CRYPTO and the CQE declares
+ * CQHCI_CAP_CS, initialize the crypto support. This involves reading the
+ * crypto capability registers, initializing the keyslot manager, clearing all
+ * keyslots, and enabling 128-bit task descriptors.
+ *
+ * Return: 0 if crypto was initialized or isn't supported; whether
+ * MMC_CAP2_CRYPTO remains set indicates which one of those cases it is.
+ * Also can return a negative errno value on unexpected error.
+ */
+int cqhci_crypto_init(struct cqhci_host *cq_host)
+{
+ struct mmc_host *mmc = cq_host->mmc;
+ struct device *dev = mmc_dev(mmc);
+ struct blk_keyslot_manager *ksm = &mmc->ksm;
+ unsigned int num_keyslots;
+ unsigned int cap_idx;
+ enum blk_crypto_mode_num blk_mode_num;
+ unsigned int slot;
+ int err = 0;
+
+ if (!(mmc->caps2 & MMC_CAP2_CRYPTO) ||
+ !(cqhci_readl(cq_host, CQHCI_CAP) & CQHCI_CAP_CS))
+ goto out;
+
+ cq_host->crypto_capabilities.reg_val =
+ cpu_to_le32(cqhci_readl(cq_host, CQHCI_CCAP));
+
+ cq_host->crypto_cfg_register =
+ (u32)cq_host->crypto_capabilities.config_array_ptr * 0x100;
+
+ cq_host->crypto_cap_array =
+ devm_kcalloc(dev, cq_host->crypto_capabilities.num_crypto_cap,
+ sizeof(cq_host->crypto_cap_array[0]), GFP_KERNEL);
+ if (!cq_host->crypto_cap_array) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * CCAP.CFGC is off by one, so the actual number of crypto
+ * configurations (a.k.a. keyslots) is CCAP.CFGC + 1.
+ */
+ num_keyslots = cq_host->crypto_capabilities.config_count + 1;
+
+ err = devm_blk_ksm_init(dev, ksm, num_keyslots);
+ if (err)
+ goto out;
+
+ ksm->ksm_ll_ops = cqhci_ksm_ops;
+ ksm->dev = dev;
+
+ /* Unfortunately, CQHCI crypto only supports 32 DUN bits. */
+ ksm->max_dun_bytes_supported = 4;
+
+ /*
+ * Cache all the crypto capabilities and advertise the supported crypto
+ * modes and data unit sizes to the block layer.
+ */
+ for (cap_idx = 0; cap_idx < cq_host->crypto_capabilities.num_crypto_cap;
+ cap_idx++) {
+ cq_host->crypto_cap_array[cap_idx].reg_val =
+ cpu_to_le32(cqhci_readl(cq_host,
+ CQHCI_CRYPTOCAP +
+ cap_idx * sizeof(__le32)));
+ blk_mode_num = cqhci_find_blk_crypto_mode(
+ cq_host->crypto_cap_array[cap_idx]);
+ if (blk_mode_num == BLK_ENCRYPTION_MODE_INVALID)
+ continue;
+ ksm->crypto_modes_supported[blk_mode_num] |=
+ cq_host->crypto_cap_array[cap_idx].sdus_mask * 512;
+ }
+
+ /* Clear all the keyslots so that we start in a known state. */
+ for (slot = 0; slot < num_keyslots; slot++)
+ cqhci_crypto_clear_keyslot(cq_host, slot);
+
+ /* CQHCI crypto requires the use of 128-bit task descriptors. */
+ cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
+
+ return 0;
+
+out:
+ mmc->caps2 &= ~MMC_CAP2_CRYPTO;
+ return err;
+}
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * CQHCI crypto engine (inline encryption) support
+ *
+ * Copyright 2020 Google LLC
+ */
+
+#ifndef LINUX_MMC_CQHCI_CRYPTO_H
+#define LINUX_MMC_CQHCI_CRYPTO_H
+
+#include <linux/mmc/host.h>
+
+#include "cqhci.h"
+
+#ifdef CONFIG_MMC_CRYPTO
+
+int cqhci_crypto_init(struct cqhci_host *host);
+
+/*
+ * Returns the crypto bits that should be set in bits 64-127 of the
+ * task descriptor.
+ */
+static inline u64 cqhci_crypto_prep_task_desc(struct mmc_request *mrq)
+{
+ if (!mrq->crypto_enabled)
+ return 0;
+
+ return CQHCI_CRYPTO_ENABLE_BIT |
+ CQHCI_CRYPTO_KEYSLOT(mrq->crypto_key_slot) |
+ mrq->data_unit_num;
+}
+
+#else /* CONFIG_MMC_CRYPTO */
+
+static inline int cqhci_crypto_init(struct cqhci_host *host)
+{
+ return 0;
+}
+
+static inline u64 cqhci_crypto_prep_task_desc(struct mmc_request *mrq)
+{
+ return 0;
+}
+
+#endif /* !CONFIG_MMC_CRYPTO */
+
+#endif /* LINUX_MMC_CQHCI_CRYPTO_H */
/* capabilities */
#define CQHCI_CAP 0x04
+#define CQHCI_CAP_CS 0x10000000 /* Crypto Support */
+
/* configuration */
#define CQHCI_CFG 0x08
#define CQHCI_DCMD 0x00001000
#define CQHCI_TASK_DESC_SZ 0x00000100
+#define CQHCI_CRYPTO_GENERAL_ENABLE 0x00000002
#define CQHCI_ENABLE 0x00000001
/* control */
#define CQHCI_IS_TCC BIT(1)
#define CQHCI_IS_RED BIT(2)
#define CQHCI_IS_TCL BIT(3)
+#define CQHCI_IS_GCE BIT(4) /* General Crypto Error */
+#define CQHCI_IS_ICCE BIT(5) /* Invalid Crypto Config Error */
-#define CQHCI_IS_MASK (CQHCI_IS_TCC | CQHCI_IS_RED)
+#define CQHCI_IS_MASK (CQHCI_IS_TCC | CQHCI_IS_RED | \
+ CQHCI_IS_GCE | CQHCI_IS_ICCE)
/* interrupt status enable */
#define CQHCI_ISTE 0x14
/* task clear */
#define CQHCI_TCLR 0x38
+/* task descriptor processing error */
+#define CQHCI_TDPE 0x3c
+
/* send status config 1 */
#define CQHCI_SSC1 0x40
#define CQHCI_SSC1_CBC_MASK GENMASK(19, 16)
/* command response argument */
#define CQHCI_CRA 0x5C
+/* crypto capabilities */
+#define CQHCI_CCAP 0x100
+#define CQHCI_CRYPTOCAP 0x104
+
#define CQHCI_INT_ALL 0xF
#define CQHCI_IC_DEFAULT_ICCTH 31
#define CQHCI_IC_DEFAULT_ICTOVAL 1
#define CQHCI_CMD_TIMING(x) (((x) & 1) << 22)
#define CQHCI_RESP_TYPE(x) (((x) & 0x3) << 23)
+/* crypto task descriptor fields (for bits 64-127 of task descriptor) */
+#define CQHCI_CRYPTO_ENABLE_BIT (1ULL << 47)
+#define CQHCI_CRYPTO_KEYSLOT(x) ((u64)(x) << 32)
+
/* transfer descriptor fields */
#define CQHCI_DAT_LENGTH(x) (((x) & 0xFFFF) << 16)
#define CQHCI_DAT_ADDR_LO(x) (((x) & 0xFFFFFFFF) << 32)
#define CQHCI_DAT_ADDR_HI(x) (((x) & 0xFFFFFFFF) << 0)
+/* CCAP - Crypto Capability 100h */
+union cqhci_crypto_capabilities {
+ __le32 reg_val;
+ struct {
+ u8 num_crypto_cap;
+ u8 config_count;
+ u8 reserved;
+ u8 config_array_ptr;
+ };
+};
+
+enum cqhci_crypto_key_size {
+ CQHCI_CRYPTO_KEY_SIZE_INVALID = 0,
+ CQHCI_CRYPTO_KEY_SIZE_128 = 1,
+ CQHCI_CRYPTO_KEY_SIZE_192 = 2,
+ CQHCI_CRYPTO_KEY_SIZE_256 = 3,
+ CQHCI_CRYPTO_KEY_SIZE_512 = 4,
+};
+
+enum cqhci_crypto_alg {
+ CQHCI_CRYPTO_ALG_AES_XTS = 0,
+ CQHCI_CRYPTO_ALG_BITLOCKER_AES_CBC = 1,
+ CQHCI_CRYPTO_ALG_AES_ECB = 2,
+ CQHCI_CRYPTO_ALG_ESSIV_AES_CBC = 3,
+};
+
+/* x-CRYPTOCAP - Crypto Capability X */
+union cqhci_crypto_cap_entry {
+ __le32 reg_val;
+ struct {
+ u8 algorithm_id;
+ u8 sdus_mask; /* Supported data unit size mask */
+ u8 key_size;
+ u8 reserved;
+ };
+};
+
+#define CQHCI_CRYPTO_CONFIGURATION_ENABLE (1 << 7)
+#define CQHCI_CRYPTO_KEY_MAX_SIZE 64
+/* x-CRYPTOCFG - Crypto Configuration X */
+union cqhci_crypto_cfg_entry {
+ __le32 reg_val[32];
+ struct {
+ u8 crypto_key[CQHCI_CRYPTO_KEY_MAX_SIZE];
+ u8 data_unit_size;
+ u8 crypto_cap_idx;
+ u8 reserved_1;
+ u8 config_enable;
+ u8 reserved_multi_host;
+ u8 reserved_2;
+ u8 vsb[2];
+ u8 reserved_3[56];
+ };
+};
+
struct cqhci_host_ops;
struct mmc_host;
struct mmc_request;
struct completion halt_comp;
wait_queue_head_t wait_queue;
struct cqhci_slot *slot;
+
+#ifdef CONFIG_MMC_CRYPTO
+ union cqhci_crypto_capabilities crypto_capabilities;
+ union cqhci_crypto_cap_entry *crypto_cap_array;
+ u32 crypto_cfg_register;
+#endif
};
struct cqhci_host_ops {
u64 *data);
void (*pre_enable)(struct mmc_host *mmc);
void (*post_disable)(struct mmc_host *mmc);
+#ifdef CONFIG_MMC_CRYPTO
+ int (*program_key)(struct cqhci_host *cq_host,
+ const union cqhci_crypto_cfg_entry *cfg, int slot);
+#endif
};
static inline void cqhci_writel(struct cqhci_host *host, u32 val, int reg)
+++ /dev/null
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * ZX Specific Extensions for Synopsys DW Multimedia Card Interface driver
- *
- * Copyright (C) 2016, Linaro Ltd.
- * Copyright (C) 2016, ZTE Corp.
- */
-
-#include <linux/clk.h>
-#include <linux/mfd/syscon.h>
-#include <linux/mmc/host.h>
-#include <linux/mmc/mmc.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/regmap.h>
-#include <linux/slab.h>
-
-#include "dw_mmc.h"
-#include "dw_mmc-pltfm.h"
-#include "dw_mmc-zx.h"
-
-struct dw_mci_zx_priv_data {
- struct regmap *sysc_base;
-};
-
-enum delay_type {
- DELAY_TYPE_READ, /* read dqs delay */
- DELAY_TYPE_CLK, /* clk sample delay */
-};
-
-static int dw_mci_zx_emmc_set_delay(struct dw_mci *host, unsigned int delay,
- enum delay_type dflag)
-{
- struct dw_mci_zx_priv_data *priv = host->priv;
- struct regmap *sysc_base = priv->sysc_base;
- unsigned int clksel;
- unsigned int loop = 1000;
- int ret;
-
- if (!sysc_base)
- return -EINVAL;
-
- ret = regmap_update_bits(sysc_base, LB_AON_EMMC_CFG_REG0,
- PARA_HALF_CLK_MODE | PARA_DLL_BYPASS_MODE |
- PARA_PHASE_DET_SEL_MASK |
- PARA_DLL_LOCK_NUM_MASK |
- DLL_REG_SET | PARA_DLL_START_MASK,
- PARA_DLL_START(4) | PARA_DLL_LOCK_NUM(4));
- if (ret)
- return ret;
-
- ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG1, &clksel);
- if (ret)
- return ret;
-
- if (dflag == DELAY_TYPE_CLK) {
- clksel &= ~CLK_SAMP_DELAY_MASK;
- clksel |= CLK_SAMP_DELAY(delay);
- } else {
- clksel &= ~READ_DQS_DELAY_MASK;
- clksel |= READ_DQS_DELAY(delay);
- }
-
- regmap_write(sysc_base, LB_AON_EMMC_CFG_REG1, clksel);
- regmap_update_bits(sysc_base, LB_AON_EMMC_CFG_REG0,
- PARA_DLL_START_MASK | PARA_DLL_LOCK_NUM_MASK |
- DLL_REG_SET,
- PARA_DLL_START(4) | PARA_DLL_LOCK_NUM(4) |
- DLL_REG_SET);
-
- do {
- ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG2, &clksel);
- if (ret)
- return ret;
-
- } while (--loop && !(clksel & ZX_DLL_LOCKED));
-
- if (!loop) {
- dev_err(host->dev, "Error: %s dll lock fail\n", __func__);
- return -EIO;
- }
-
- return 0;
-}
-
-static int dw_mci_zx_emmc_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
-{
- struct dw_mci *host = slot->host;
- struct mmc_host *mmc = slot->mmc;
- int ret, len = 0, start = 0, end = 0, delay, best = 0;
-
- for (delay = 1; delay < 128; delay++) {
- ret = dw_mci_zx_emmc_set_delay(host, delay, DELAY_TYPE_CLK);
- if (!ret && mmc_send_tuning(mmc, opcode, NULL)) {
- if (start >= 0) {
- end = delay - 1;
- /* check and update longest good range */
- if ((end - start) > len) {
- best = (start + end) >> 1;
- len = end - start;
- }
- }
- start = -1;
- end = 0;
- continue;
- }
- if (start < 0)
- start = delay;
- }
-
- if (start >= 0) {
- end = delay - 1;
- if ((end - start) > len) {
- best = (start + end) >> 1;
- len = end - start;
- }
- }
- if (best < 0)
- return -EIO;
-
- dev_info(host->dev, "%s best range: start %d end %d\n", __func__,
- start, end);
- return dw_mci_zx_emmc_set_delay(host, best, DELAY_TYPE_CLK);
-}
-
-static int dw_mci_zx_prepare_hs400_tuning(struct dw_mci *host,
- struct mmc_ios *ios)
-{
- int ret;
-
- /* config phase shift as 90 degree */
- ret = dw_mci_zx_emmc_set_delay(host, 32, DELAY_TYPE_READ);
- if (ret < 0)
- return -EIO;
-
- return 0;
-}
-
-static int dw_mci_zx_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
-{
- struct dw_mci *host = slot->host;
-
- if (host->verid == 0x290a) /* only for emmc */
- return dw_mci_zx_emmc_execute_tuning(slot, opcode);
- /* TODO: Add 0x210a dedicated tuning for sd/sdio */
-
- return 0;
-}
-
-static int dw_mci_zx_parse_dt(struct dw_mci *host)
-{
- struct device_node *np = host->dev->of_node;
- struct device_node *node;
- struct dw_mci_zx_priv_data *priv;
- struct regmap *sysc_base;
-
- /* syscon is needed only by emmc */
- node = of_parse_phandle(np, "zte,aon-syscon", 0);
- if (node) {
- sysc_base = syscon_node_to_regmap(node);
- of_node_put(node);
-
- if (IS_ERR(sysc_base))
- return dev_err_probe(host->dev, PTR_ERR(sysc_base),
- "Can't get syscon\n");
- } else {
- return 0;
- }
-
- priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- priv->sysc_base = sysc_base;
- host->priv = priv;
-
- return 0;
-}
-
-static unsigned long zx_dwmmc_caps[3] = {
- MMC_CAP_CMD23,
- MMC_CAP_CMD23,
- MMC_CAP_CMD23,
-};
-
-static const struct dw_mci_drv_data zx_drv_data = {
- .caps = zx_dwmmc_caps,
- .num_caps = ARRAY_SIZE(zx_dwmmc_caps),
- .execute_tuning = dw_mci_zx_execute_tuning,
- .prepare_hs400_tuning = dw_mci_zx_prepare_hs400_tuning,
- .parse_dt = dw_mci_zx_parse_dt,
-};
-
-static const struct of_device_id dw_mci_zx_match[] = {
- { .compatible = "zte,zx296718-dw-mshc", .data = &zx_drv_data},
- {},
-};
-MODULE_DEVICE_TABLE(of, dw_mci_zx_match);
-
-static int dw_mci_zx_probe(struct platform_device *pdev)
-{
- const struct dw_mci_drv_data *drv_data;
- const struct of_device_id *match;
-
- match = of_match_node(dw_mci_zx_match, pdev->dev.of_node);
- drv_data = match->data;
-
- return dw_mci_pltfm_register(pdev, drv_data);
-}
-
-static const struct dev_pm_ops dw_mci_zx_dev_pm_ops = {
- 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)
-};
-
-static struct platform_driver dw_mci_zx_pltfm_driver = {
- .probe = dw_mci_zx_probe,
- .remove = dw_mci_pltfm_remove,
- .driver = {
- .name = "dwmmc_zx",
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .of_match_table = dw_mci_zx_match,
- .pm = &dw_mci_zx_dev_pm_ops,
- },
-};
-
-module_platform_driver(dw_mci_zx_pltfm_driver);
-
-MODULE_DESCRIPTION("ZTE emmc/sd driver");
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _DW_MMC_ZX_H_
-#define _DW_MMC_ZX_H_
-
-/* ZX296718 SoC specific DLL register offset. */
-#define LB_AON_EMMC_CFG_REG0 0x1B0
-#define LB_AON_EMMC_CFG_REG1 0x1B4
-#define LB_AON_EMMC_CFG_REG2 0x1B8
-
-/* LB_AON_EMMC_CFG_REG0 register defines */
-#define PARA_DLL_START(x) ((x) & 0xFF)
-#define PARA_DLL_START_MASK 0xFF
-#define DLL_REG_SET BIT(8)
-#define PARA_DLL_LOCK_NUM(x) (((x) & 7) << 16)
-#define PARA_DLL_LOCK_NUM_MASK (7 << 16)
-#define PARA_PHASE_DET_SEL(x) (((x) & 7) << 20)
-#define PARA_PHASE_DET_SEL_MASK (7 << 20)
-#define PARA_DLL_BYPASS_MODE BIT(23)
-#define PARA_HALF_CLK_MODE BIT(24)
-
-/* LB_AON_EMMC_CFG_REG1 register defines */
-#define READ_DQS_DELAY(x) ((x) & 0x7F)
-#define READ_DQS_DELAY_MASK (0x7F)
-#define READ_DQS_BYPASS_MODE BIT(7)
-#define CLK_SAMP_DELAY(x) (((x) & 0x7F) << 8)
-#define CLK_SAMP_DELAY_MASK (0x7F << 8)
-#define CLK_SAMP_BYPASS_MODE BIT(15)
-
-/* LB_AON_EMMC_CFG_REG2 register defines */
-#define ZX_DLL_LOCKED BIT(2)
-
-#endif /* _DW_MMC_ZX_H_ */
return true;
}
-static void dw_mci_tasklet_func(unsigned long priv)
+static void dw_mci_tasklet_func(struct tasklet_struct *t)
{
- struct dw_mci *host = (struct dw_mci *)priv;
+ struct dw_mci *host = from_tasklet(host, t, tasklet);
struct mmc_data *data;
struct mmc_command *cmd;
struct mmc_request *mrq;
else
host->fifo_reg = host->regs + DATA_240A_OFFSET;
- tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
+ tasklet_setup(&host->tasklet, dw_mci_tasklet_func);
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
host->irq_flags, "dw-mci", host);
if (ret)
enum jz4740_mmc_version version;
int irq;
- int card_detect_irq;
void __iomem *base;
struct resource *mem_res;
struct mmc_data *data = mrq->data;
struct scatterlist *sg;
int i;
- bool use_desc_chain_mode = true;
/*
* When Controller DMA cannot directly access DDR memory, disable
if (host->dram_access_quirk)
return;
- /*
- * Broken SDIO with AP6255-based WiFi on Khadas VIM Pro has been
- * reported. For some strange reason this occurs in descriptor
- * chain mode only. So let's fall back to bounce buffer mode
- * for command SD_IO_RW_EXTENDED.
- */
- if (mrq->cmd->opcode == SD_IO_RW_EXTENDED)
- return;
+ if (data->blocks > 1) {
+ /*
+ * In block mode DMA descriptor format, "length" field indicates
+ * number of blocks and there is no way to pass DMA size that
+ * is not multiple of SDIO block size, making it impossible to
+ * tie more than one memory buffer with single SDIO block.
+ * Block mode sg buffer size should be aligned with SDIO block
+ * size, otherwise chain mode could not be used.
+ */
+ for_each_sg(data->sg, sg, data->sg_len, i) {
+ if (sg->length % data->blksz) {
+ WARN_ONCE(1, "unaligned sg len %u blksize %u\n",
+ sg->length, data->blksz);
+ return;
+ }
+ }
+ }
- for_each_sg(data->sg, sg, data->sg_len, i)
+ for_each_sg(data->sg, sg, data->sg_len, i) {
/* check for 8 byte alignment */
- if (sg->offset & 7) {
+ if (sg->offset % 8) {
WARN_ONCE(1, "unaligned scatterlist buffer\n");
- use_desc_chain_mode = false;
- break;
+ return;
}
+ }
- if (use_desc_chain_mode)
- data->host_cookie |= SD_EMMC_DESC_CHAIN_MODE;
+ data->host_cookie |= SD_EMMC_DESC_CHAIN_MODE;
}
static inline bool meson_mmc_desc_chain_mode(const struct mmc_data *data)
#include <linux/types.h>
#include <linux/pinctrl/consumer.h>
#include <linux/reset.h>
+#include <linux/gpio/consumer.h>
#include <asm/div64.h>
#include <asm/io.h>
.start_signal_voltage_switch = mmci_sig_volt_switch,
};
+static void mmci_probe_level_translator(struct mmc_host *mmc)
+{
+ struct device *dev = mmc_dev(mmc);
+ struct mmci_host *host = mmc_priv(mmc);
+ struct gpio_desc *cmd_gpio;
+ struct gpio_desc *ck_gpio;
+ struct gpio_desc *ckin_gpio;
+ int clk_hi, clk_lo;
+
+ /*
+ * Assume the level translator is present if st,use-ckin is set.
+ * This is to cater for DTs which do not implement this test.
+ */
+ host->clk_reg_add |= MCI_STM32_CLK_SELCKIN;
+
+ cmd_gpio = gpiod_get(dev, "st,cmd", GPIOD_OUT_HIGH);
+ if (IS_ERR(cmd_gpio))
+ goto exit_cmd;
+
+ ck_gpio = gpiod_get(dev, "st,ck", GPIOD_OUT_HIGH);
+ if (IS_ERR(ck_gpio))
+ goto exit_ck;
+
+ ckin_gpio = gpiod_get(dev, "st,ckin", GPIOD_IN);
+ if (IS_ERR(ckin_gpio))
+ goto exit_ckin;
+
+ /* All GPIOs are valid, test whether level translator works */
+
+ /* Sample CKIN */
+ clk_hi = !!gpiod_get_value(ckin_gpio);
+
+ /* Set CK low */
+ gpiod_set_value(ck_gpio, 0);
+
+ /* Sample CKIN */
+ clk_lo = !!gpiod_get_value(ckin_gpio);
+
+ /* Tristate all */
+ gpiod_direction_input(cmd_gpio);
+ gpiod_direction_input(ck_gpio);
+
+ /* Level translator is present if CK signal is propagated to CKIN */
+ if (!clk_hi || clk_lo) {
+ host->clk_reg_add &= ~MCI_STM32_CLK_SELCKIN;
+ dev_warn(dev,
+ "Level translator inoperable, CK signal not detected on CKIN, disabling.\n");
+ }
+
+ gpiod_put(ckin_gpio);
+
+exit_ckin:
+ gpiod_put(ck_gpio);
+exit_ck:
+ gpiod_put(cmd_gpio);
+exit_cmd:
+ pinctrl_select_default_state(dev);
+}
+
static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc)
{
struct mmci_host *host = mmc_priv(mmc);
if (of_get_property(np, "st,neg-edge", NULL))
host->clk_reg_add |= MCI_STM32_CLK_NEGEDGE;
if (of_get_property(np, "st,use-ckin", NULL))
- host->clk_reg_add |= MCI_STM32_CLK_SELCKIN;
+ mmci_probe_level_translator(mmc);
if (of_get_property(np, "mmc-cap-mmc-highspeed", NULL))
mmc->caps |= MMC_CAP_MMC_HIGHSPEED;
if (!mmc)
return -ENOMEM;
- ret = mmci_of_parse(np, mmc);
- if (ret)
- goto host_free;
-
host = mmc_priv(mmc);
host->mmc = mmc;
host->mmc_ops = &mmci_ops;
mmc->ops = &mmci_ops;
+ ret = mmci_of_parse(np, mmc);
+ if (ret)
+ goto host_free;
+
/*
* Some variant (STM32) doesn't have opendrain bit, nevertheless
* pins can be set accordingly using pinctrl
static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
{
unsigned long flags;
- bool ret;
- ret = cancel_delayed_work(&host->req_timeout);
- if (!ret) {
- /* delay work already running */
- return;
- }
+ /*
+ * No need check the return value of cancel_delayed_work, as only ONE
+ * path will go here!
+ */
+ cancel_delayed_work(&host->req_timeout);
+
spin_lock_irqsave(&host->lock, flags);
host->mrq = NULL;
spin_unlock_irqrestore(&host->lock, flags);
bool done = false;
bool sbc_error;
unsigned long flags;
- u32 *rsp = cmd->resp;
+ u32 *rsp;
if (mrq->sbc && cmd == mrq->cmd &&
(events & (MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR
if (done)
return true;
+ rsp = cmd->resp;
sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask);
static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
struct mmc_request *mrq, struct mmc_data *data)
{
- struct mmc_command *stop = data->stop;
+ struct mmc_command *stop;
unsigned long flags;
bool done;
unsigned int check_data = events &
if (done)
return true;
+ stop = data->stop;
if (check_data || (stop && stop->error)) {
dev_dbg(host->dev, "DMA status: 0x%8X\n",
ret = mmc_of_parse(mmc);
if (ret)
- goto out_clk_disable;
+ goto out_free_dma;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
tasklet_schedule(&slot->cover_tasklet);
}
-static void mmc_omap_cover_handler(unsigned long param)
+static void mmc_omap_cover_handler(struct tasklet_struct *t)
{
- struct mmc_omap_slot *slot = (struct mmc_omap_slot *)param;
+ struct mmc_omap_slot *slot = from_tasklet(slot, t, cover_tasklet);
int cover_open = mmc_omap_cover_is_open(slot);
mmc_detect_change(slot->mmc, 0);
if (slot->pdata->get_cover_state != NULL) {
timer_setup(&slot->cover_timer, mmc_omap_cover_timer, 0);
- tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler,
- (unsigned long)slot);
+ tasklet_setup(&slot->cover_tasklet, mmc_omap_cover_handler);
}
r = mmc_add_host(mmc);
struct regulator *pbias;
bool pbias_enabled;
void __iomem *base;
- int vqmmc_enabled;
+ bool vqmmc_enabled;
resource_size_t mapbase;
spinlock_t irq_lock; /* Prevent races with irq handler */
unsigned int dma_len;
dev_err(mmc_dev(mmc), "vmmc_aux reg enable failed\n");
goto err_vqmmc;
}
- host->vqmmc_enabled = 1;
+ host->vqmmc_enabled = true;
}
return 0;
dev_err(mmc_dev(mmc), "vmmc_aux reg disable failed\n");
return ret;
}
- host->vqmmc_enabled = 0;
+ host->vqmmc_enabled = false;
}
if (!IS_ERR(mmc->supply.vmmc)) {
return 0;
if (power_on) {
- if (host->pbias_enabled == 0) {
+ if (!host->pbias_enabled) {
ret = regulator_enable(host->pbias);
if (ret) {
dev_err(host->dev, "pbias reg enable fail\n");
return ret;
}
- host->pbias_enabled = 1;
+ host->pbias_enabled = true;
}
} else {
- if (host->pbias_enabled == 1) {
+ if (host->pbias_enabled) {
ret = regulator_disable(host->pbias);
if (ret) {
dev_err(host->dev, "pbias reg disable fail\n");
return ret;
}
- host->pbias_enabled = 0;
+ host->pbias_enabled = false;
}
}
host->base = base + pdata->reg_offset;
host->power_mode = MMC_POWER_OFF;
host->next_data.cookie = 1;
- host->pbias_enabled = 0;
- host->vqmmc_enabled = 0;
+ host->pbias_enabled = false;
+ host->vqmmc_enabled = false;
platform_set_drvdata(pdev, host);
owl_host->irq = platform_get_irq(pdev, 0);
if (owl_host->irq < 0) {
ret = -EINVAL;
- goto err_free_host;
+ goto err_release_channel;
}
ret = devm_request_irq(&pdev->dev, owl_host->irq, owl_irq_handler,
if (ret) {
dev_err(&pdev->dev, "Failed to request irq %d\n",
owl_host->irq);
- goto err_free_host;
+ goto err_release_channel;
}
ret = mmc_add_host(mmc);
if (ret) {
dev_err(&pdev->dev, "Failed to add host\n");
- goto err_free_host;
+ goto err_release_channel;
}
dev_dbg(&pdev->dev, "Owl MMC Controller Initialized\n");
return 0;
+err_release_channel:
+ dma_release_channel(owl_host->dma);
err_free_host:
mmc_free_host(mmc);
mmc_remove_host(mmc);
disable_irq(owl_host->irq);
+ dma_release_channel(owl_host->dma);
mmc_free_host(mmc);
return 0;
return false;
}
-static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host)
+static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host,
+ struct mmc_request *mrq)
{
struct renesas_sdhi *priv = host_to_priv(host);
bool use_4tap = priv->quirks && priv->quirks->hs400_4taps;
+ bool ret = false;
/*
* Skip checking SCC errors when running on 4 taps in HS400 mode as
if (mmc_doing_tune(host->mmc))
return false;
+ if (((mrq->cmd->error == -ETIMEDOUT) ||
+ (mrq->data && mrq->data->error == -ETIMEDOUT)) &&
+ ((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
+ (host->ops.get_cd && host->ops.get_cd(host->mmc))))
+ ret |= true;
+
if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL) &
SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN)
- return renesas_sdhi_auto_correction(host);
+ ret |= renesas_sdhi_auto_correction(host);
+ else
+ ret |= renesas_sdhi_manual_correction(host, use_4tap);
- return renesas_sdhi_manual_correction(host, use_4tap);
+ return ret;
}
static int renesas_sdhi_wait_idle(struct tmio_mmc_host *host, u32 bit)
#define INFO2_DTRANERR1 BIT(17)
#define INFO2_DTRANERR0 BIT(16)
+enum renesas_sdhi_dma_cookie {
+ COOKIE_UNMAPPED,
+ COOKIE_PRE_MAPPED,
+ COOKIE_MAPPED,
+};
+
/*
* Specification of this driver:
* - host->chan_{rx,tx} will be used as a flag of enabling/disabling the dma
tasklet_schedule(&priv->dma_priv.dma_complete);
}
+/*
+ * renesas_sdhi_internal_dmac_map() will be called with two difference
+ * sg pointers in two mmc_data by .pre_req(), but tmio host can have a single
+ * sg_ptr only. So, renesas_sdhi_internal_dmac_{un}map() should use a sg
+ * pointer in a mmc_data instead of host->sg_ptr.
+ */
+static void
+renesas_sdhi_internal_dmac_unmap(struct tmio_mmc_host *host,
+ struct mmc_data *data,
+ enum renesas_sdhi_dma_cookie cookie)
+{
+ bool unmap = cookie == COOKIE_UNMAPPED ? (data->host_cookie != cookie) :
+ (data->host_cookie == cookie);
+
+ if (unmap) {
+ dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
+ mmc_get_dma_dir(data));
+ data->host_cookie = COOKIE_UNMAPPED;
+ }
+}
+
+static bool
+renesas_sdhi_internal_dmac_map(struct tmio_mmc_host *host,
+ struct mmc_data *data,
+ enum renesas_sdhi_dma_cookie cookie)
+{
+ if (data->host_cookie == COOKIE_PRE_MAPPED)
+ return true;
+
+ if (!dma_map_sg(&host->pdev->dev, data->sg, data->sg_len,
+ mmc_get_dma_dir(data)))
+ return false;
+
+ data->host_cookie = cookie;
+
+ /* This DMAC cannot handle if buffer is not 128-bytes alignment */
+ if (!IS_ALIGNED(sg_dma_address(data->sg), 128)) {
+ renesas_sdhi_internal_dmac_unmap(host, data, cookie);
+ return false;
+ }
+
+ return true;
+}
+
static void
renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
struct mmc_data *data)
if (!test_bit(SDHI_INTERNAL_DMAC_ADDR_MODE_FIXED_ONLY, &global_flags))
dtran_mode |= DTRAN_MODE_ADDR_MODE;
- if (!dma_map_sg(&host->pdev->dev, sg, host->sg_len,
- mmc_get_dma_dir(data)))
+ if (!renesas_sdhi_internal_dmac_map(host, data, COOKIE_MAPPED))
goto force_pio;
- /* This DMAC cannot handle if buffer is not 8-bytes alignment */
- if (!IS_ALIGNED(sg_dma_address(sg), 8))
- goto force_pio_with_unmap;
-
if (data->flags & MMC_DATA_READ) {
dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
if (test_bit(SDHI_INTERNAL_DMAC_ONE_RX_ONLY, &global_flags) &&
return;
force_pio_with_unmap:
- dma_unmap_sg(&host->pdev->dev, sg, host->sg_len, mmc_get_dma_dir(data));
+ renesas_sdhi_internal_dmac_unmap(host, data, COOKIE_UNMAPPED);
force_pio:
renesas_sdhi_internal_dmac_enable_dma(host, false);
dir = DMA_TO_DEVICE;
renesas_sdhi_internal_dmac_enable_dma(host, false);
- dma_unmap_sg(&host->pdev->dev, host->sg_ptr, host->sg_len, dir);
+ renesas_sdhi_internal_dmac_unmap(host, host->data, COOKIE_MAPPED);
if (dir == DMA_FROM_DEVICE)
clear_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags);
renesas_sdhi_internal_dmac_complete(host);
}
+static void renesas_sdhi_internal_dmac_post_req(struct mmc_host *mmc,
+ struct mmc_request *mrq,
+ int err)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ struct mmc_data *data = mrq->data;
+
+ if (!data)
+ return;
+
+ renesas_sdhi_internal_dmac_unmap(host, data, COOKIE_UNMAPPED);
+}
+
+static void renesas_sdhi_internal_dmac_pre_req(struct mmc_host *mmc,
+ struct mmc_request *mrq)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ struct mmc_data *data = mrq->data;
+
+ if (!data)
+ return;
+
+ data->host_cookie = COOKIE_UNMAPPED;
+ renesas_sdhi_internal_dmac_map(host, data, COOKIE_PRE_MAPPED);
+}
+
static void
renesas_sdhi_internal_dmac_request_dma(struct tmio_mmc_host *host,
struct tmio_mmc_data *pdata)
tasklet_init(&host->dma_issue,
renesas_sdhi_internal_dmac_issue_tasklet_fn,
(unsigned long)host);
+
+ /* Add pre_req and post_req */
+ host->ops.pre_req = renesas_sdhi_internal_dmac_pre_req;
+ host->ops.post_req = renesas_sdhi_internal_dmac_post_req;
}
static void
if (host->power_state == SDMMC_POWER_ON)
return 0;
+ msleep(100);
+
rtsx_pci_init_cmd(pcr);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE,
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
if (pcr->rtd3_en)
mmc->caps = mmc->caps | MMC_CAP_AGGRESSIVE_PM;
- mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
+ mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE |
+ MMC_CAP2_NO_SDIO;
mmc->max_current_330 = 400;
mmc->max_current_180 = 800;
mmc->ops = &realtek_pci_sdmmc_ops;
enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
}
-static void pio_tasklet(unsigned long data)
+static void pio_tasklet(struct tasklet_struct *t)
{
- struct s3cmci_host *host = (struct s3cmci_host *) data;
+ struct s3cmci_host *host = from_tasklet(host, t, pio_tasklet);
s3cmci_disable_irq(host, true);
host->pdata = pdev->dev.platform_data;
spin_lock_init(&host->complete_lock);
- tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
+ tasklet_setup(&host->pio_tasklet, pio_tasklet);
if (host->is2440) {
host->sdiimsk = S3C2440_SDIIMSK;
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
- int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
+ int dead;
pm_runtime_get_sync(&pdev->dev);
+ dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
MODULE_DEVICE_TABLE(of, sdhci_iproc_of_match);
#ifdef CONFIG_ACPI
+/*
+ * This is a duplicate of bcm2835_(pltfrm_)data without caps quirks
+ * which are provided by the ACPI table.
+ */
+static const struct sdhci_pltfm_data sdhci_bcm_arasan_data = {
+ .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+ SDHCI_QUIRK_NO_HISPD_BIT,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ .ops = &sdhci_iproc_32only_ops,
+};
+
+static const struct sdhci_iproc_data bcm_arasan_data = {
+ .pdata = &sdhci_bcm_arasan_data,
+};
+
static const struct acpi_device_id sdhci_iproc_acpi_ids[] = {
{ .id = "BRCM5871", .driver_data = (kernel_ulong_t)&iproc_cygnus_data },
{ .id = "BRCM5872", .driver_data = (kernel_ulong_t)&iproc_data },
+ { .id = "BCM2847", .driver_data = (kernel_ulong_t)&bcm_arasan_data },
+ { .id = "BRCME88C", .driver_data = (kernel_ulong_t)&bcm2711_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(acpi, sdhci_iproc_acpi_ids);
#include <linux/pm_opp.h>
#include <linux/slab.h>
#include <linux/iopoll.h>
+#include <linux/qcom_scm.h>
#include <linux/regulator/consumer.h>
#include <linux/interconnect.h>
#include <linux/pinctrl/consumer.h>
struct sdhci_msm_host {
struct platform_device *pdev;
void __iomem *core_mem; /* MSM SDCC mapped address */
+ void __iomem *ice_mem; /* MSM ICE mapped address (if available) */
int pwr_irq; /* power irq */
struct clk *bus_clk; /* SDHC bus voter clock */
struct clk *xo_clk; /* TCXO clk needed for FLL feature of cm_dll*/
- struct clk_bulk_data bulk_clks[4]; /* core, iface, cal, sleep clocks */
+ /* core, iface, cal, sleep, and ice clocks */
+ struct clk_bulk_data bulk_clks[5];
unsigned long clk_rate;
struct mmc_host *mmc;
struct opp_table *opp_table;
writel_relaxed(val, host->ioaddr + offset);
}
-static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host,
- unsigned int clock)
+static unsigned int msm_get_clock_mult_for_bus_mode(struct sdhci_host *host)
{
struct mmc_ios ios = host->mmc->ios;
/*
ios.timing == MMC_TIMING_MMC_DDR52 ||
ios.timing == MMC_TIMING_MMC_HS400 ||
host->flags & SDHCI_HS400_TUNING)
- clock *= 2;
- return clock;
+ return 2;
+ return 1;
}
static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
struct mmc_ios curr_ios = host->mmc->ios;
struct clk *core_clk = msm_host->bulk_clks[0].clk;
+ unsigned long achieved_rate;
+ unsigned int desired_rate;
+ unsigned int mult;
int rc;
- clock = msm_get_clock_rate_for_bus_mode(host, clock);
- rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), clock);
+ mult = msm_get_clock_mult_for_bus_mode(host);
+ desired_rate = clock * mult;
+ rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), desired_rate);
if (rc) {
pr_err("%s: Failed to set clock at rate %u at timing %d\n",
- mmc_hostname(host->mmc), clock,
- curr_ios.timing);
+ mmc_hostname(host->mmc), desired_rate, curr_ios.timing);
return;
}
- msm_host->clk_rate = clock;
+
+ /*
+ * Qualcomm clock drivers by default round clock _up_ if they can't
+ * make the requested rate. This is not good for SD. Yell if we
+ * encounter it.
+ */
+ achieved_rate = clk_get_rate(core_clk);
+ if (achieved_rate > desired_rate)
+ pr_warn("%s: Card appears overclocked; req %u Hz, actual %lu Hz\n",
+ mmc_hostname(host->mmc), desired_rate, achieved_rate);
+ host->mmc->actual_clock = achieved_rate / mult;
+
+ /* Stash the rate we requested to use in sdhci_msm_runtime_resume() */
+ msm_host->clk_rate = desired_rate;
+
pr_debug("%s: Setting clock at rate %lu at timing %d\n",
- mmc_hostname(host->mmc), clk_get_rate(core_clk),
- curr_ios.timing);
+ mmc_hostname(host->mmc), achieved_rate, curr_ios.timing);
}
/* Platform specific tuning */
static void __sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
{
u16 clk;
- /*
- * Keep actual_clock as zero -
- * - since there is no divider used so no need of having actual_clock.
- * - MSM controller uses SDCLK for data timeout calculation. If
- * actual_clock is zero, host->clock is taken for calculation.
- */
- host->mmc->actual_clock = 0;
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
if (!clock) {
- msm_host->clk_rate = clock;
+ host->mmc->actual_clock = msm_host->clk_rate = 0;
goto out;
}
/*****************************************************************************\
* *
+ * Inline Crypto Engine (ICE) support *
+ * *
+\*****************************************************************************/
+
+#ifdef CONFIG_MMC_CRYPTO
+
+#define AES_256_XTS_KEY_SIZE 64
+
+/* QCOM ICE registers */
+
+#define QCOM_ICE_REG_VERSION 0x0008
+
+#define QCOM_ICE_REG_FUSE_SETTING 0x0010
+#define QCOM_ICE_FUSE_SETTING_MASK 0x1
+#define QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK 0x2
+#define QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK 0x4
+
+#define QCOM_ICE_REG_BIST_STATUS 0x0070
+#define QCOM_ICE_BIST_STATUS_MASK 0xF0000000
+
+#define QCOM_ICE_REG_ADVANCED_CONTROL 0x1000
+
+#define sdhci_msm_ice_writel(host, val, reg) \
+ writel((val), (host)->ice_mem + (reg))
+#define sdhci_msm_ice_readl(host, reg) \
+ readl((host)->ice_mem + (reg))
+
+static bool sdhci_msm_ice_supported(struct sdhci_msm_host *msm_host)
+{
+ struct device *dev = mmc_dev(msm_host->mmc);
+ u32 regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_VERSION);
+ int major = regval >> 24;
+ int minor = (regval >> 16) & 0xFF;
+ int step = regval & 0xFFFF;
+
+ /* For now this driver only supports ICE version 3. */
+ if (major != 3) {
+ dev_warn(dev, "Unsupported ICE version: v%d.%d.%d\n",
+ major, minor, step);
+ return false;
+ }
+
+ dev_info(dev, "Found QC Inline Crypto Engine (ICE) v%d.%d.%d\n",
+ major, minor, step);
+
+ /* If fuses are blown, ICE might not work in the standard way. */
+ regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_FUSE_SETTING);
+ if (regval & (QCOM_ICE_FUSE_SETTING_MASK |
+ QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK |
+ QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK)) {
+ dev_warn(dev, "Fuses are blown; ICE is unusable!\n");
+ return false;
+ }
+ return true;
+}
+
+static inline struct clk *sdhci_msm_ice_get_clk(struct device *dev)
+{
+ return devm_clk_get(dev, "ice");
+}
+
+static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
+ struct cqhci_host *cq_host)
+{
+ struct mmc_host *mmc = msm_host->mmc;
+ struct device *dev = mmc_dev(mmc);
+ struct resource *res;
+ int err;
+
+ if (!(cqhci_readl(cq_host, CQHCI_CAP) & CQHCI_CAP_CS))
+ return 0;
+
+ res = platform_get_resource_byname(msm_host->pdev, IORESOURCE_MEM,
+ "ice");
+ if (!res) {
+ dev_warn(dev, "ICE registers not found\n");
+ goto disable;
+ }
+
+ if (!qcom_scm_ice_available()) {
+ dev_warn(dev, "ICE SCM interface not found\n");
+ goto disable;
+ }
+
+ msm_host->ice_mem = devm_ioremap_resource(dev, res);
+ if (IS_ERR(msm_host->ice_mem)) {
+ err = PTR_ERR(msm_host->ice_mem);
+ dev_err(dev, "Failed to map ICE registers; err=%d\n", err);
+ return err;
+ }
+
+ if (!sdhci_msm_ice_supported(msm_host))
+ goto disable;
+
+ mmc->caps2 |= MMC_CAP2_CRYPTO;
+ return 0;
+
+disable:
+ dev_warn(dev, "Disabling inline encryption support\n");
+ return 0;
+}
+
+static void sdhci_msm_ice_low_power_mode_enable(struct sdhci_msm_host *msm_host)
+{
+ u32 regval;
+
+ regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_ADVANCED_CONTROL);
+ /*
+ * Enable low power mode sequence
+ * [0]-0, [1]-0, [2]-0, [3]-E, [4]-0, [5]-0, [6]-0, [7]-0
+ */
+ regval |= 0x7000;
+ sdhci_msm_ice_writel(msm_host, regval, QCOM_ICE_REG_ADVANCED_CONTROL);
+}
+
+static void sdhci_msm_ice_optimization_enable(struct sdhci_msm_host *msm_host)
+{
+ u32 regval;
+
+ /* ICE Optimizations Enable Sequence */
+ regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_ADVANCED_CONTROL);
+ regval |= 0xD807100;
+ /* ICE HPG requires delay before writing */
+ udelay(5);
+ sdhci_msm_ice_writel(msm_host, regval, QCOM_ICE_REG_ADVANCED_CONTROL);
+ udelay(5);
+}
+
+/*
+ * Wait until the ICE BIST (built-in self-test) has completed.
+ *
+ * This may be necessary before ICE can be used.
+ *
+ * Note that we don't really care whether the BIST passed or failed; we really
+ * just want to make sure that it isn't still running. This is because (a) the
+ * BIST is a FIPS compliance thing that never fails in practice, (b) ICE is
+ * documented to reject crypto requests if the BIST fails, so we needn't do it
+ * in software too, and (c) properly testing storage encryption requires testing
+ * the full storage stack anyway, and not relying on hardware-level self-tests.
+ */
+static int sdhci_msm_ice_wait_bist_status(struct sdhci_msm_host *msm_host)
+{
+ u32 regval;
+ int err;
+
+ err = readl_poll_timeout(msm_host->ice_mem + QCOM_ICE_REG_BIST_STATUS,
+ regval, !(regval & QCOM_ICE_BIST_STATUS_MASK),
+ 50, 5000);
+ if (err)
+ dev_err(mmc_dev(msm_host->mmc),
+ "Timed out waiting for ICE self-test to complete\n");
+ return err;
+}
+
+static void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host)
+{
+ if (!(msm_host->mmc->caps2 & MMC_CAP2_CRYPTO))
+ return;
+ sdhci_msm_ice_low_power_mode_enable(msm_host);
+ sdhci_msm_ice_optimization_enable(msm_host);
+ sdhci_msm_ice_wait_bist_status(msm_host);
+}
+
+static int __maybe_unused sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
+{
+ if (!(msm_host->mmc->caps2 & MMC_CAP2_CRYPTO))
+ return 0;
+ return sdhci_msm_ice_wait_bist_status(msm_host);
+}
+
+/*
+ * Program a key into a QC ICE keyslot, or evict a keyslot. QC ICE requires
+ * vendor-specific SCM calls for this; it doesn't support the standard way.
+ */
+static int sdhci_msm_program_key(struct cqhci_host *cq_host,
+ const union cqhci_crypto_cfg_entry *cfg,
+ int slot)
+{
+ struct device *dev = mmc_dev(cq_host->mmc);
+ union cqhci_crypto_cap_entry cap;
+ union {
+ u8 bytes[AES_256_XTS_KEY_SIZE];
+ u32 words[AES_256_XTS_KEY_SIZE / sizeof(u32)];
+ } key;
+ int i;
+ int err;
+
+ if (!(cfg->config_enable & CQHCI_CRYPTO_CONFIGURATION_ENABLE))
+ return qcom_scm_ice_invalidate_key(slot);
+
+ /* Only AES-256-XTS has been tested so far. */
+ cap = cq_host->crypto_cap_array[cfg->crypto_cap_idx];
+ if (cap.algorithm_id != CQHCI_CRYPTO_ALG_AES_XTS ||
+ cap.key_size != CQHCI_CRYPTO_KEY_SIZE_256) {
+ dev_err_ratelimited(dev,
+ "Unhandled crypto capability; algorithm_id=%d, key_size=%d\n",
+ cap.algorithm_id, cap.key_size);
+ return -EINVAL;
+ }
+
+ memcpy(key.bytes, cfg->crypto_key, AES_256_XTS_KEY_SIZE);
+
+ /*
+ * The SCM call byte-swaps the 32-bit words of the key. So we have to
+ * do the same, in order for the final key be correct.
+ */
+ for (i = 0; i < ARRAY_SIZE(key.words); i++)
+ __cpu_to_be32s(&key.words[i]);
+
+ err = qcom_scm_ice_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE,
+ QCOM_SCM_ICE_CIPHER_AES_256_XTS,
+ cfg->data_unit_size);
+ memzero_explicit(&key, sizeof(key));
+ return err;
+}
+#else /* CONFIG_MMC_CRYPTO */
+static inline struct clk *sdhci_msm_ice_get_clk(struct device *dev)
+{
+ return NULL;
+}
+
+static inline int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
+ struct cqhci_host *cq_host)
+{
+ return 0;
+}
+
+static inline void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host)
+{
+}
+
+static inline int __maybe_unused
+sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
+{
+ return 0;
+}
+#endif /* !CONFIG_MMC_CRYPTO */
+
+/*****************************************************************************\
+ * *
* MSM Command Queue Engine (CQE) *
* *
\*****************************************************************************/
return 0;
}
+static void sdhci_msm_cqe_enable(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+
+ sdhci_cqe_enable(mmc);
+ sdhci_msm_ice_enable(msm_host);
+}
+
static void sdhci_msm_cqe_disable(struct mmc_host *mmc, bool recovery)
{
struct sdhci_host *host = mmc_priv(mmc);
}
static const struct cqhci_host_ops sdhci_msm_cqhci_ops = {
- .enable = sdhci_cqe_enable,
+ .enable = sdhci_msm_cqe_enable,
.disable = sdhci_msm_cqe_disable,
+#ifdef CONFIG_MMC_CRYPTO
+ .program_key = sdhci_msm_program_key,
+#endif
};
static int sdhci_msm_cqe_add_host(struct sdhci_host *host,
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
+ ret = sdhci_msm_ice_init(msm_host, cq_host);
+ if (ret)
+ goto cleanup;
+
ret = cqhci_init(cq_host, host->mmc, dma64);
if (ret) {
dev_err(&pdev->dev, "%s: CQE init: failed (%d)\n",
clk = NULL;
msm_host->bulk_clks[3].clk = clk;
+ clk = sdhci_msm_ice_get_clk(&pdev->dev);
+ if (IS_ERR(clk))
+ clk = NULL;
+ msm_host->bulk_clks[4].clk = clk;
+
ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks),
msm_host->bulk_clks);
if (ret)
* Whenever core-clock is gated dynamically, it's needed to
* restore the SDR DLL settings when the clock is ungated.
*/
- if (msm_host->restore_dll_config && msm_host->clk_rate)
+ if (msm_host->restore_dll_config && msm_host->clk_rate) {
ret = sdhci_msm_restore_sdr_dll_config(host);
+ if (ret)
+ return ret;
+ }
dev_pm_opp_set_rate(dev, msm_host->clk_rate);
- return ret;
+ return sdhci_msm_ice_resume(msm_host);
}
static const struct dev_pm_ops sdhci_msm_pm_ops = {
/**
* sdhci_arasan_update_support64b - Set SUPPORT_64B (64-bit System Bus Support)
+ * @host: The sdhci_host
+ * @value: The value to write
*
* This should be set based on the System Address Bus.
* 0: the Core supports only 32-bit System Address Bus.
* 1: the Core supports 64-bit System Address Bus.
*
- * NOTES:
- * - For Keem Bay, it is required to clear this bit. Its default value is 1'b1.
- * Keem Bay does not support 64-bit access.
- *
- * @host: The sdhci_host
- * @value: The value to write
+ * NOTE:
+ * For Keem Bay, it is required to clear this bit. Its default value is 1'b1.
+ * Keem Bay does not support 64-bit access.
*/
static void sdhci_arasan_update_support64b(struct sdhci_host *host, u32 value)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
- const struct sdhci_arasan_soc_ctl_map *soc_ctl_map =
- sdhci_arasan->soc_ctl_map;
+ const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
/* Having a map is optional */
+ soc_ctl_map = sdhci_arasan->soc_ctl_map;
if (!soc_ctl_map)
return;
static int sdhci_arasan_probe(struct platform_device *pdev)
{
int ret;
- const struct of_device_id *match;
struct device_node *node;
struct clk *clk_xin;
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
struct sdhci_arasan_data *sdhci_arasan;
- struct device_node *np = pdev->dev.of_node;
const struct sdhci_arasan_of_data *data;
- match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node);
- data = match->data;
+ data = of_device_get_match_data(dev);
host = sdhci_pltfm_init(pdev, data->pdata, sizeof(*sdhci_arasan));
if (IS_ERR(host))
sdhci_arasan->soc_ctl_map = data->soc_ctl_map;
sdhci_arasan->clk_ops = data->clk_ops;
- node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0);
+ node = of_parse_phandle(np, "arasan,soc-ctl-syscon", 0);
if (node) {
sdhci_arasan->soc_ctl_base = syscon_node_to_regmap(node);
of_node_put(node);
if (IS_ERR(sdhci_arasan->soc_ctl_base)) {
- ret = dev_err_probe(&pdev->dev,
+ ret = dev_err_probe(dev,
PTR_ERR(sdhci_arasan->soc_ctl_base),
"Can't get syscon\n");
goto err_pltfm_free;
}
}
- sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb");
+ sdhci_arasan->clk_ahb = devm_clk_get(dev, "clk_ahb");
if (IS_ERR(sdhci_arasan->clk_ahb)) {
- dev_err(&pdev->dev, "clk_ahb clock not found.\n");
- ret = PTR_ERR(sdhci_arasan->clk_ahb);
+ ret = dev_err_probe(dev, PTR_ERR(sdhci_arasan->clk_ahb),
+ "clk_ahb clock not found.\n");
goto err_pltfm_free;
}
- clk_xin = devm_clk_get(&pdev->dev, "clk_xin");
+ clk_xin = devm_clk_get(dev, "clk_xin");
if (IS_ERR(clk_xin)) {
- dev_err(&pdev->dev, "clk_xin clock not found.\n");
- ret = PTR_ERR(clk_xin);
+ ret = dev_err_probe(dev, PTR_ERR(clk_xin), "clk_xin clock not found.\n");
goto err_pltfm_free;
}
ret = clk_prepare_enable(sdhci_arasan->clk_ahb);
if (ret) {
- dev_err(&pdev->dev, "Unable to enable AHB clock.\n");
+ dev_err(dev, "Unable to enable AHB clock.\n");
goto err_pltfm_free;
}
ret = clk_prepare_enable(clk_xin);
if (ret) {
- dev_err(&pdev->dev, "Unable to enable SD clock.\n");
+ dev_err(dev, "Unable to enable SD clock.\n");
goto clk_dis_ahb;
}
pltfm_host->clk = clk_xin;
- if (of_device_is_compatible(pdev->dev.of_node,
- "rockchip,rk3399-sdhci-5.1"))
+ if (of_device_is_compatible(np, "rockchip,rk3399-sdhci-5.1"))
sdhci_arasan_update_clockmultiplier(host, 0x0);
if (of_device_is_compatible(np, "intel,keembay-sdhci-5.1-emmc") ||
sdhci_arasan_update_baseclkfreq(host);
- ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev);
+ ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, dev);
if (ret)
goto clk_disable_all;
arasan_zynqmp_execute_tuning;
}
- arasan_dt_parse_clk_phases(&pdev->dev, &sdhci_arasan->clk_data);
+ arasan_dt_parse_clk_phases(dev, &sdhci_arasan->clk_data);
ret = mmc_of_parse(host->mmc);
if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "parsing dt failed (%d)\n", ret);
+ ret = dev_err_probe(dev, ret, "parsing dt failed.\n");
goto unreg_clk;
}
sdhci_arasan->phy = ERR_PTR(-ENODEV);
- if (of_device_is_compatible(pdev->dev.of_node,
- "arasan,sdhci-5.1")) {
- sdhci_arasan->phy = devm_phy_get(&pdev->dev,
- "phy_arasan");
+ if (of_device_is_compatible(np, "arasan,sdhci-5.1")) {
+ sdhci_arasan->phy = devm_phy_get(dev, "phy_arasan");
if (IS_ERR(sdhci_arasan->phy)) {
- ret = PTR_ERR(sdhci_arasan->phy);
- dev_err(&pdev->dev, "No phy for arasan,sdhci-5.1.\n");
+ ret = dev_err_probe(dev, PTR_ERR(sdhci_arasan->phy),
+ "No phy for arasan,sdhci-5.1.\n");
goto unreg_clk;
}
ret = phy_init(sdhci_arasan->phy);
if (ret < 0) {
- dev_err(&pdev->dev, "phy_init err.\n");
+ dev_err(dev, "phy_init err.\n");
goto unreg_clk;
}
if (!IS_ERR(sdhci_arasan->phy))
phy_exit(sdhci_arasan->phy);
unreg_clk:
- sdhci_arasan_unregister_sdclk(&pdev->dev);
+ sdhci_arasan_unregister_sdclk(dev);
clk_disable_all:
clk_disable_unprepare(clk_xin);
clk_dis_ahb:
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (C) 2020 IBM Corp. */
+
+#include <kunit/test.h>
+
+static void aspeed_sdhci_phase_ddr52(struct kunit *test)
+{
+ int rate = 52000000;
+
+ KUNIT_EXPECT_EQ(test, 0,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 0));
+ KUNIT_EXPECT_EQ(test, 0,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 1));
+ KUNIT_EXPECT_EQ(test, 1,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 2));
+ KUNIT_EXPECT_EQ(test, 1,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 3));
+ KUNIT_EXPECT_EQ(test, 2,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 4));
+ KUNIT_EXPECT_EQ(test, 3,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 5));
+ KUNIT_EXPECT_EQ(test, 14,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 23));
+ KUNIT_EXPECT_EQ(test, 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 24));
+ KUNIT_EXPECT_EQ(test, 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 25));
+
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 0,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 180));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 0,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 181));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 1,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 182));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 1,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 183));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 2,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 184));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 3,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 185));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 14,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 203));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 204));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 205));
+}
+
+static void aspeed_sdhci_phase_hs200(struct kunit *test)
+{
+ int rate = 200000000;
+
+ KUNIT_EXPECT_EQ(test, 0,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 0));
+ KUNIT_EXPECT_EQ(test, 0,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 5));
+ KUNIT_EXPECT_EQ(test, 1,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 6));
+ KUNIT_EXPECT_EQ(test, 1,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 7));
+ KUNIT_EXPECT_EQ(test, 14,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 89));
+ KUNIT_EXPECT_EQ(test, 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 90));
+ KUNIT_EXPECT_EQ(test, 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 91));
+ KUNIT_EXPECT_EQ(test, 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 96));
+
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 180));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 185));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 1,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 186));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 1,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 187));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 14,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 269));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 270));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 271));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 276));
+}
+
+static struct kunit_case aspeed_sdhci_test_cases[] = {
+ KUNIT_CASE(aspeed_sdhci_phase_ddr52),
+ KUNIT_CASE(aspeed_sdhci_phase_hs200),
+ {}
+};
+
+static struct kunit_suite aspeed_sdhci_test_suite = {
+ .name = "sdhci-of-aspeed",
+ .test_cases = aspeed_sdhci_test_cases,
+};
+
+static struct kunit_suite *aspeed_sdc_test_suite_array[] = {
+ &aspeed_sdhci_test_suite,
+ NULL,
+};
+
+static struct kunit_suite **aspeed_sdc_test_suites
+ __used __section(".kunit_test_suites") = aspeed_sdc_test_suite_array;
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
+#include <linux/math64.h>
#include <linux/mmc/host.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include "sdhci-pltfm.h"
-#define ASPEED_SDC_INFO 0x00
-#define ASPEED_SDC_S1MMC8 BIT(25)
-#define ASPEED_SDC_S0MMC8 BIT(24)
+#define ASPEED_SDC_INFO 0x00
+#define ASPEED_SDC_S1_MMC8 BIT(25)
+#define ASPEED_SDC_S0_MMC8 BIT(24)
+#define ASPEED_SDC_PHASE 0xf4
+#define ASPEED_SDC_S1_PHASE_IN GENMASK(25, 21)
+#define ASPEED_SDC_S0_PHASE_IN GENMASK(20, 16)
+#define ASPEED_SDC_S1_PHASE_OUT GENMASK(15, 11)
+#define ASPEED_SDC_S1_PHASE_IN_EN BIT(10)
+#define ASPEED_SDC_S1_PHASE_OUT_EN GENMASK(9, 8)
+#define ASPEED_SDC_S0_PHASE_OUT GENMASK(7, 3)
+#define ASPEED_SDC_S0_PHASE_IN_EN BIT(2)
+#define ASPEED_SDC_S0_PHASE_OUT_EN GENMASK(1, 0)
+#define ASPEED_SDC_PHASE_MAX 31
struct aspeed_sdc {
struct clk *clk;
void __iomem *regs;
};
+struct aspeed_sdhci_tap_param {
+ bool valid;
+
+#define ASPEED_SDHCI_TAP_PARAM_INVERT_CLK BIT(4)
+ u8 in;
+ u8 out;
+};
+
+struct aspeed_sdhci_tap_desc {
+ u32 tap_mask;
+ u32 enable_mask;
+ u8 enable_value;
+};
+
+struct aspeed_sdhci_phase_desc {
+ struct aspeed_sdhci_tap_desc in;
+ struct aspeed_sdhci_tap_desc out;
+};
+
+struct aspeed_sdhci_pdata {
+ unsigned int clk_div_start;
+ const struct aspeed_sdhci_phase_desc *phase_desc;
+ size_t nr_phase_descs;
+};
+
struct aspeed_sdhci {
+ const struct aspeed_sdhci_pdata *pdata;
struct aspeed_sdc *parent;
u32 width_mask;
+ struct mmc_clk_phase_map phase_map;
+ const struct aspeed_sdhci_phase_desc *phase_desc;
};
static void aspeed_sdc_configure_8bit_mode(struct aspeed_sdc *sdc,
spin_unlock(&sdc->lock);
}
+static u32
+aspeed_sdc_set_phase_tap(const struct aspeed_sdhci_tap_desc *desc,
+ u8 tap, bool enable, u32 reg)
+{
+ reg &= ~(desc->enable_mask | desc->tap_mask);
+ if (enable) {
+ reg |= tap << __ffs(desc->tap_mask);
+ reg |= desc->enable_value << __ffs(desc->enable_mask);
+ }
+
+ return reg;
+}
+
+static void
+aspeed_sdc_set_phase_taps(struct aspeed_sdc *sdc,
+ const struct aspeed_sdhci_phase_desc *desc,
+ const struct aspeed_sdhci_tap_param *taps)
+{
+ u32 reg;
+
+ spin_lock(&sdc->lock);
+ reg = readl(sdc->regs + ASPEED_SDC_PHASE);
+
+ reg = aspeed_sdc_set_phase_tap(&desc->in, taps->in, taps->valid, reg);
+ reg = aspeed_sdc_set_phase_tap(&desc->out, taps->out, taps->valid, reg);
+
+ writel(reg, sdc->regs + ASPEED_SDC_PHASE);
+ spin_unlock(&sdc->lock);
+}
+
+#define PICOSECONDS_PER_SECOND 1000000000000ULL
+#define ASPEED_SDHCI_NR_TAPS 15
+/* Measured value with *handwave* environmentals and static loading */
+#define ASPEED_SDHCI_MAX_TAP_DELAY_PS 1253
+static int aspeed_sdhci_phase_to_tap(struct device *dev, unsigned long rate_hz,
+ int phase_deg)
+{
+ u64 phase_period_ps;
+ u64 prop_delay_ps;
+ u64 clk_period_ps;
+ unsigned int tap;
+ u8 inverted;
+
+ phase_deg %= 360;
+
+ if (phase_deg >= 180) {
+ inverted = ASPEED_SDHCI_TAP_PARAM_INVERT_CLK;
+ phase_deg -= 180;
+ dev_dbg(dev,
+ "Inverting clock to reduce phase correction from %d to %d degrees\n",
+ phase_deg + 180, phase_deg);
+ } else {
+ inverted = 0;
+ }
+
+ prop_delay_ps = ASPEED_SDHCI_MAX_TAP_DELAY_PS / ASPEED_SDHCI_NR_TAPS;
+ clk_period_ps = div_u64(PICOSECONDS_PER_SECOND, (u64)rate_hz);
+ phase_period_ps = div_u64((u64)phase_deg * clk_period_ps, 360ULL);
+
+ tap = div_u64(phase_period_ps, prop_delay_ps);
+ if (tap > ASPEED_SDHCI_NR_TAPS) {
+ dev_warn(dev,
+ "Requested out of range phase tap %d for %d degrees of phase compensation at %luHz, clamping to tap %d\n",
+ tap, phase_deg, rate_hz, ASPEED_SDHCI_NR_TAPS);
+ tap = ASPEED_SDHCI_NR_TAPS;
+ }
+
+ return inverted | tap;
+}
+
+static void
+aspeed_sdhci_phases_to_taps(struct device *dev, unsigned long rate,
+ const struct mmc_clk_phase *phases,
+ struct aspeed_sdhci_tap_param *taps)
+{
+ taps->valid = phases->valid;
+
+ if (!phases->valid)
+ return;
+
+ taps->in = aspeed_sdhci_phase_to_tap(dev, rate, phases->in_deg);
+ taps->out = aspeed_sdhci_phase_to_tap(dev, rate, phases->out_deg);
+}
+
+static void
+aspeed_sdhci_configure_phase(struct sdhci_host *host, unsigned long rate)
+{
+ struct aspeed_sdhci_tap_param _taps = {0}, *taps = &_taps;
+ struct mmc_clk_phase *params;
+ struct aspeed_sdhci *sdhci;
+ struct device *dev;
+
+ dev = host->mmc->parent;
+ sdhci = sdhci_pltfm_priv(sdhci_priv(host));
+
+ if (!sdhci->phase_desc)
+ return;
+
+ params = &sdhci->phase_map.phase[host->timing];
+ aspeed_sdhci_phases_to_taps(dev, rate, params, taps);
+ aspeed_sdc_set_phase_taps(sdhci->parent, sdhci->phase_desc, taps);
+ dev_dbg(dev,
+ "Using taps [%d, %d] for [%d, %d] degrees of phase correction at %luHz (%d)\n",
+ taps->in & ASPEED_SDHCI_NR_TAPS,
+ taps->out & ASPEED_SDHCI_NR_TAPS,
+ params->in_deg, params->out_deg, rate, host->timing);
+}
+
static void aspeed_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host;
- unsigned long parent;
+ unsigned long parent, bus;
+ struct aspeed_sdhci *sdhci;
int div;
u16 clk;
pltfm_host = sdhci_priv(host);
+ sdhci = sdhci_pltfm_priv(pltfm_host);
+
parent = clk_get_rate(pltfm_host->clk);
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
if (WARN_ON(clock > host->max_clk))
clock = host->max_clk;
- for (div = 2; div < 256; div *= 2) {
- if ((parent / div) <= clock)
+ /*
+ * Regarding the AST2600:
+ *
+ * If (EMMC12C[7:6], EMMC12C[15:8] == 0) then
+ * period of SDCLK = period of SDMCLK.
+ *
+ * If (EMMC12C[7:6], EMMC12C[15:8] != 0) then
+ * period of SDCLK = period of SDMCLK * 2 * (EMMC12C[7:6], EMMC[15:8])
+ *
+ * If you keep EMMC12C[7:6] = 0 and EMMC12C[15:8] as one-hot,
+ * 0x1/0x2/0x4/etc, you will find it is compatible to AST2400 or AST2500
+ *
+ * Keep the one-hot behaviour for backwards compatibility except for
+ * supporting the value 0 in (EMMC12C[7:6], EMMC12C[15:8]), and capture
+ * the 0-value capability in clk_div_start.
+ */
+ for (div = sdhci->pdata->clk_div_start; div < 256; div *= 2) {
+ bus = parent / div;
+ if (bus <= clock)
break;
}
+
div >>= 1;
clk = div << SDHCI_DIVIDER_SHIFT;
+ aspeed_sdhci_configure_phase(host, bus);
+
sdhci_enable_clk(host, clk);
}
static int aspeed_sdhci_probe(struct platform_device *pdev)
{
+ const struct aspeed_sdhci_pdata *aspeed_pdata;
struct sdhci_pltfm_host *pltfm_host;
struct aspeed_sdhci *dev;
struct sdhci_host *host;
int slot;
int ret;
+ aspeed_pdata = of_device_get_match_data(&pdev->dev);
+ if (!aspeed_pdata) {
+ dev_err(&pdev->dev, "Missing platform configuration data\n");
+ return -EINVAL;
+ }
+
host = sdhci_pltfm_init(pdev, &aspeed_sdhci_pdata, sizeof(*dev));
if (IS_ERR(host))
return PTR_ERR(host);
pltfm_host = sdhci_priv(host);
dev = sdhci_pltfm_priv(pltfm_host);
+ dev->pdata = aspeed_pdata;
dev->parent = dev_get_drvdata(pdev->dev.parent);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
else if (slot >= 2)
return -EINVAL;
- dev_info(&pdev->dev, "Configuring for slot %d\n", slot);
- dev->width_mask = !slot ? ASPEED_SDC_S0MMC8 : ASPEED_SDC_S1MMC8;
+ if (slot < dev->pdata->nr_phase_descs) {
+ dev->phase_desc = &dev->pdata->phase_desc[slot];
+ } else {
+ dev_info(&pdev->dev,
+ "Phase control not supported for slot %d\n", slot);
+ dev->phase_desc = NULL;
+ }
+
+ dev->width_mask = !slot ? ASPEED_SDC_S0_MMC8 : ASPEED_SDC_S1_MMC8;
+
+ dev_info(&pdev->dev, "Configured for slot %d\n", slot);
sdhci_get_of_property(pdev);
if (ret)
goto err_sdhci_add;
+ if (dev->phase_desc)
+ mmc_of_parse_clk_phase(host->mmc, &dev->phase_map);
+
ret = sdhci_add_host(host);
if (ret)
goto err_sdhci_add;
return 0;
}
+static const struct aspeed_sdhci_pdata ast2400_sdhci_pdata = {
+ .clk_div_start = 2,
+};
+
+static const struct aspeed_sdhci_phase_desc ast2600_sdhci_phase[] = {
+ /* SDHCI/Slot 0 */
+ [0] = {
+ .in = {
+ .tap_mask = ASPEED_SDC_S0_PHASE_IN,
+ .enable_mask = ASPEED_SDC_S0_PHASE_IN_EN,
+ .enable_value = 1,
+ },
+ .out = {
+ .tap_mask = ASPEED_SDC_S0_PHASE_OUT,
+ .enable_mask = ASPEED_SDC_S0_PHASE_OUT_EN,
+ .enable_value = 3,
+ },
+ },
+ /* SDHCI/Slot 1 */
+ [1] = {
+ .in = {
+ .tap_mask = ASPEED_SDC_S1_PHASE_IN,
+ .enable_mask = ASPEED_SDC_S1_PHASE_IN_EN,
+ .enable_value = 1,
+ },
+ .out = {
+ .tap_mask = ASPEED_SDC_S1_PHASE_OUT,
+ .enable_mask = ASPEED_SDC_S1_PHASE_OUT_EN,
+ .enable_value = 3,
+ },
+ },
+};
+
+static const struct aspeed_sdhci_pdata ast2600_sdhci_pdata = {
+ .clk_div_start = 1,
+ .phase_desc = ast2600_sdhci_phase,
+ .nr_phase_descs = ARRAY_SIZE(ast2600_sdhci_phase),
+};
+
static const struct of_device_id aspeed_sdhci_of_match[] = {
- { .compatible = "aspeed,ast2400-sdhci", },
- { .compatible = "aspeed,ast2500-sdhci", },
- { .compatible = "aspeed,ast2600-sdhci", },
+ { .compatible = "aspeed,ast2400-sdhci", .data = &ast2400_sdhci_pdata, },
+ { .compatible = "aspeed,ast2500-sdhci", .data = &ast2400_sdhci_pdata, },
+ { .compatible = "aspeed,ast2600-sdhci", .data = &ast2600_sdhci_pdata, },
{ }
};
.remove = aspeed_sdc_remove,
};
+#if defined(CONFIG_MMC_SDHCI_OF_ASPEED_TEST)
+#include "sdhci-of-aspeed-test.c"
+
+static inline int aspeed_sdc_tests_init(void)
+{
+ return __kunit_test_suites_init(aspeed_sdc_test_suites);
+}
+
+static inline void aspeed_sdc_tests_exit(void)
+{
+ __kunit_test_suites_exit(aspeed_sdc_test_suites);
+}
+#else
+static inline int aspeed_sdc_tests_init(void)
+{
+ return 0;
+}
+
+static inline void aspeed_sdc_tests_exit(void)
+{
+}
+#endif
+
static int __init aspeed_sdc_init(void)
{
int rc;
rc = platform_driver_register(&aspeed_sdc_driver);
if (rc < 0)
- platform_driver_unregister(&aspeed_sdhci_driver);
+ goto cleanup_sdhci;
+
+ rc = aspeed_sdc_tests_init();
+ if (rc < 0) {
+ platform_driver_unregister(&aspeed_sdc_driver);
+ goto cleanup_sdhci;
+ }
+
+ return 0;
+
+cleanup_sdhci:
+ platform_driver_unregister(&aspeed_sdhci_driver);
return rc;
}
static void __exit aspeed_sdc_exit(void)
{
+ aspeed_sdc_tests_exit();
+
platform_driver_unregister(&aspeed_sdc_driver);
platform_driver_unregister(&aspeed_sdhci_driver);
}
static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
.ops = &sdhci_dwcmshc_ops,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
};
static int dwcmshc_probe(struct platform_device *pdev)
#define GLI_9763E_VHS_REV_W 0x2
#define PCIE_GLI_9763E_MB 0x888
#define GLI_9763E_MB_CMDQ_OFF BIT(19)
+#define GLI_9763E_MB_ERP_ON BIT(7)
#define PCIE_GLI_9763E_SCR 0x8E0
#define GLI_9763E_SCR_AXI_REQ BIT(9)
+#define PCIE_GLI_9763E_CFG2 0x8A4
+#define GLI_9763E_CFG2_L1DLY GENMASK(28, 19)
+#define GLI_9763E_CFG2_L1DLY_MAX 0x3FF
+
#define PCIE_GLI_9763E_MMC_CTRL 0x960
#define GLI_9763E_HS400_SLOW BIT(3)
+#define PCIE_GLI_9763E_CLKRXDLY 0x934
+#define GLI_9763E_HS400_RXDLY GENMASK(31, 28)
+#define GLI_9763E_HS400_RXDLY_5 0x5
+
#define SDHCI_GLI_9763E_CQE_BASE_ADDR 0x200
#define GLI_9763E_CQE_TRNS_MODE (SDHCI_TRNS_MULTI | \
SDHCI_TRNS_BLK_CNT_EN | \
value &= ~GLI_9763E_HS400_SLOW;
pci_write_config_dword(pdev, PCIE_GLI_9763E_MMC_CTRL, value);
+ pci_read_config_dword(pdev, PCIE_GLI_9763E_CFG2, &value);
+ value &= ~GLI_9763E_CFG2_L1DLY;
+ /* set ASPM L1 entry delay to 260us */
+ value |= FIELD_PREP(GLI_9763E_CFG2_L1DLY, GLI_9763E_CFG2_L1DLY_MAX);
+ pci_write_config_dword(pdev, PCIE_GLI_9763E_CFG2, value);
+
+ pci_read_config_dword(pdev, PCIE_GLI_9763E_CLKRXDLY, &value);
+ value &= ~GLI_9763E_HS400_RXDLY;
+ value |= FIELD_PREP(GLI_9763E_HS400_RXDLY, GLI_9763E_HS400_RXDLY_5);
+ pci_write_config_dword(pdev, PCIE_GLI_9763E_CLKRXDLY, value);
+
pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
value &= ~GLI_9763E_VHS_REV;
value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R);
pci_read_config_dword(pdev, PCIE_GLI_9763E_MB, &value);
if (!(value & GLI_9763E_MB_CMDQ_OFF))
- host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
+ if (value & GLI_9763E_MB_ERP_ON)
+ host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
gli_pcie_enable_msi(slot);
host->mmc_host_ops.hs400_enhanced_strobe =
#define O2_SD_ADMA2 0xE7
#define O2_SD_INF_MOD 0xF1
#define O2_SD_MISC_CTRL4 0xFC
+#define O2_SD_MISC_CTRL 0x1C0
+#define O2_SD_PWR_FORCE_L0 0x0002
#define O2_SD_TUNING_CTRL 0x300
#define O2_SD_PLL_SETTING 0x304
#define O2_SD_MISC_SETTING 0x308
{
struct sdhci_host *host = mmc_priv(mmc);
int current_bus_width = 0;
+ u32 scratch32 = 0;
+ u16 scratch = 0;
/*
* This handler only implements the eMMC tuning that is specific to
if (WARN_ON((opcode != MMC_SEND_TUNING_BLOCK_HS200) &&
(opcode != MMC_SEND_TUNING_BLOCK)))
return -EINVAL;
+
+ /* Force power mode enter L0 */
+ scratch = sdhci_readw(host, O2_SD_MISC_CTRL);
+ scratch |= O2_SD_PWR_FORCE_L0;
+ sdhci_writew(host, scratch, O2_SD_MISC_CTRL);
+
+ /* wait DLL lock, timeout value 5ms */
+ if (readx_poll_timeout(sdhci_o2_pll_dll_wdt_control, host,
+ scratch32, (scratch32 & O2_DLL_LOCK_STATUS), 1, 5000))
+ pr_warn("%s: DLL can't lock in 5ms after force L0 during tuning.\n",
+ mmc_hostname(host->mmc));
/*
* Judge the tuning reason, whether caused by dll shift
* If cause by dll shift, should call sdhci_o2_dll_recovery
sdhci_set_bus_width(host, current_bus_width);
}
+ /* Cancel force power mode enter L0 */
+ scratch = sdhci_readw(host, O2_SD_MISC_CTRL);
+ scratch &= ~(O2_SD_PWR_FORCE_L0);
+ sdhci_writew(host, scratch, O2_SD_MISC_CTRL);
+
sdhci_reset(host, SDHCI_RESET_CMD);
sdhci_reset(host, SDHCI_RESET_DATA);
+++ /dev/null
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SDHCI support for SiRF primaII and marco SoCs
- *
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
- */
-
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/mmc/host.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/mmc/slot-gpio.h>
-#include "sdhci-pltfm.h"
-
-#define SDHCI_CLK_DELAY_SETTING 0x4C
-#define SDHCI_SIRF_8BITBUS BIT(3)
-#define SIRF_TUNING_COUNT 16384
-
-static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width)
-{
- u8 ctrl;
-
- ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
- ctrl &= ~(SDHCI_CTRL_4BITBUS | SDHCI_SIRF_8BITBUS);
-
- /*
- * CSR atlas7 and prima2 SD host version is not 3.0
- * 8bit-width enable bit of CSR SD hosts is 3,
- * while stardard hosts use bit 5
- */
- if (width == MMC_BUS_WIDTH_8)
- ctrl |= SDHCI_SIRF_8BITBUS;
- else if (width == MMC_BUS_WIDTH_4)
- ctrl |= SDHCI_CTRL_4BITBUS;
-
- sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
-}
-
-static u32 sdhci_sirf_readl_le(struct sdhci_host *host, int reg)
-{
- u32 val = readl(host->ioaddr + reg);
-
- if (unlikely((reg == SDHCI_CAPABILITIES_1) &&
- (host->mmc->caps & MMC_CAP_UHS_SDR50))) {
- /* fake CAP_1 register */
- val = SDHCI_SUPPORT_DDR50 |
- SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING;
- }
-
- if (unlikely(reg == SDHCI_SLOT_INT_STATUS)) {
- u32 prss = val;
- /* fake chips as V3.0 host conreoller */
- prss &= ~(0xFF << 16);
- val = prss | (SDHCI_SPEC_300 << 16);
- }
- return val;
-}
-
-static u16 sdhci_sirf_readw_le(struct sdhci_host *host, int reg)
-{
- u16 ret = 0;
-
- ret = readw(host->ioaddr + reg);
-
- if (unlikely(reg == SDHCI_HOST_VERSION)) {
- ret = readw(host->ioaddr + SDHCI_HOST_VERSION);
- ret |= SDHCI_SPEC_300;
- }
-
- return ret;
-}
-
-static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode)
-{
- int tuning_seq_cnt = 3;
- int phase;
- u8 tuned_phase_cnt = 0;
- int rc = 0, longest_range = 0;
- int start = -1, end = 0, tuning_value = -1, range = 0;
- u16 clock_setting;
- struct mmc_host *mmc = host->mmc;
-
- clock_setting = sdhci_readw(host, SDHCI_CLK_DELAY_SETTING);
- clock_setting &= ~0x3fff;
-
-retry:
- phase = 0;
- tuned_phase_cnt = 0;
- do {
- sdhci_writel(host,
- clock_setting | phase,
- SDHCI_CLK_DELAY_SETTING);
-
- if (!mmc_send_tuning(mmc, opcode, NULL)) {
- /* Tuning is successful at this tuning point */
- tuned_phase_cnt++;
- dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
- mmc_hostname(mmc), phase);
- if (start == -1)
- start = phase;
- end = phase;
- range++;
- if (phase == (SIRF_TUNING_COUNT - 1)
- && range > longest_range)
- tuning_value = (start + end) / 2;
- } else {
- dev_dbg(mmc_dev(mmc), "%s: Found bad phase = %d\n",
- mmc_hostname(mmc), phase);
- if (range > longest_range) {
- tuning_value = (start + end) / 2;
- longest_range = range;
- }
- start = -1;
- end = range = 0;
- }
- } while (++phase < SIRF_TUNING_COUNT);
-
- if (tuned_phase_cnt && tuning_value > 0) {
- /*
- * Finally set the selected phase in delay
- * line hw block.
- */
- phase = tuning_value;
- sdhci_writel(host,
- clock_setting | phase,
- SDHCI_CLK_DELAY_SETTING);
-
- dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
- mmc_hostname(mmc), phase);
- } else {
- if (--tuning_seq_cnt)
- goto retry;
- /* Tuning failed */
- dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n",
- mmc_hostname(mmc));
- rc = -EIO;
- }
-
- return rc;
-}
-
-static const struct sdhci_ops sdhci_sirf_ops = {
- .read_l = sdhci_sirf_readl_le,
- .read_w = sdhci_sirf_readw_le,
- .platform_execute_tuning = sdhci_sirf_execute_tuning,
- .set_clock = sdhci_set_clock,
- .get_max_clock = sdhci_pltfm_clk_get_max_clock,
- .set_bus_width = sdhci_sirf_set_bus_width,
- .reset = sdhci_reset,
- .set_uhs_signaling = sdhci_set_uhs_signaling,
-};
-
-static const struct sdhci_pltfm_data sdhci_sirf_pdata = {
- .ops = &sdhci_sirf_ops,
- .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
- SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
- SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
- SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
-};
-
-static int sdhci_sirf_probe(struct platform_device *pdev)
-{
- struct sdhci_host *host;
- struct sdhci_pltfm_host *pltfm_host;
- struct clk *clk;
- int ret;
-
- clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "unable to get clock");
- return PTR_ERR(clk);
- }
-
- host = sdhci_pltfm_init(pdev, &sdhci_sirf_pdata, 0);
- if (IS_ERR(host))
- return PTR_ERR(host);
-
- pltfm_host = sdhci_priv(host);
- pltfm_host->clk = clk;
-
- sdhci_get_of_property(pdev);
-
- ret = clk_prepare_enable(pltfm_host->clk);
- if (ret)
- goto err_clk_prepare;
-
- ret = sdhci_add_host(host);
- if (ret)
- goto err_sdhci_add;
-
- /*
- * We must request the IRQ after sdhci_add_host(), as the tasklet only
- * gets setup in sdhci_add_host() and we oops.
- */
- ret = mmc_gpiod_request_cd(host->mmc, "cd", 0, false, 0);
- if (ret == -EPROBE_DEFER)
- goto err_request_cd;
- if (!ret)
- mmc_gpiod_request_cd_irq(host->mmc);
-
- return 0;
-
-err_request_cd:
- sdhci_remove_host(host, 0);
-err_sdhci_add:
- clk_disable_unprepare(pltfm_host->clk);
-err_clk_prepare:
- sdhci_pltfm_free(pdev);
- return ret;
-}
-
-static const struct of_device_id sdhci_sirf_of_match[] = {
- { .compatible = "sirf,prima2-sdhc" },
- { }
-};
-MODULE_DEVICE_TABLE(of, sdhci_sirf_of_match);
-
-static struct platform_driver sdhci_sirf_driver = {
- .driver = {
- .name = "sdhci-sirf",
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .of_match_table = sdhci_sirf_of_match,
- .pm = &sdhci_pltfm_pmops,
- },
- .probe = sdhci_sirf_probe,
- .remove = sdhci_pltfm_unregister,
-};
-
-module_platform_driver(sdhci_sirf_driver);
-
-MODULE_DESCRIPTION("SDHCI driver for SiRFprimaII/SiRFmarco");
-MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
-MODULE_LICENSE("GPL v2");
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
- struct mmc_host *mmc = host->mmc;
- mmc_remove_host(mmc);
+ sdhci_remove_host(host, 0);
+
clk_disable_unprepare(sprd_host->clk_sdio);
clk_disable_unprepare(sprd_host->clk_enable);
clk_disable_unprepare(sprd_host->clk_2x_enable);
- mmc_free_host(mmc);
+ sdhci_pltfm_free(pdev);
return 0;
}
static const struct of_device_id sdhci_xenon_dt_ids[] = {
{ .compatible = "marvell,armada-ap806-sdhci", .data = (void *)XENON_AP806},
+ { .compatible = "marvell,armada-ap807-sdhci", .data = (void *)XENON_AP807},
{ .compatible = "marvell,armada-cp110-sdhci", .data = (void *)XENON_CP110},
{ .compatible = "marvell,armada-3700-sdhci", .data = (void *)XENON_A3700},
{}
.flags = IOMUX_PRESENT,
};
+static const struct sdhci_pltfm_data sdhci_am64_8bit_pdata = {
+ .ops = &sdhci_j721e_8bit_ops,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+};
+
+static const struct sdhci_am654_driver_data sdhci_am64_8bit_drvdata = {
+ .pdata = &sdhci_am64_8bit_pdata,
+ .flags = DLL_PRESENT | DLL_CALIB,
+};
+
+static const struct sdhci_pltfm_data sdhci_am64_4bit_pdata = {
+ .ops = &sdhci_j721e_4bit_ops,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+};
+
+static const struct sdhci_am654_driver_data sdhci_am64_4bit_drvdata = {
+ .pdata = &sdhci_am64_4bit_pdata,
+ .flags = IOMUX_PRESENT,
+};
+
static const struct soc_device_attribute sdhci_am654_devices[] = {
{ .family = "AM65X",
.revision = "SR1.0",
.compatible = "ti,j721e-sdhci-4bit",
.data = &sdhci_j721e_4bit_drvdata,
},
+ {
+ .compatible = "ti,am64-sdhci-8bit",
+ .data = &sdhci_am64_8bit_drvdata,
+ },
+ {
+ .compatible = "ti,am64-sdhci-4bit",
+ .data = &sdhci_am64_4bit_drvdata,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sdhci_am654_of_match);
struct sunxi_mmc_cfg {
u32 idma_des_size_bits;
+ u32 idma_des_shift;
const struct sunxi_mmc_clk_delay *clk_delays;
/* does the IP block support autocalibration? */
/* Enable CEATA support */
mmc_writel(host, REG_FUNS, SDXC_CEATA_ON);
/* Set DMA descriptor list base address */
- mmc_writel(host, REG_DLBA, host->sg_dma);
+ mmc_writel(host, REG_DLBA, host->sg_dma >> host->cfg->idma_des_shift);
rval = mmc_readl(host, REG_GCTRL);
rval |= SDXC_INTERRUPT_ENABLE_BIT;
next_desc += sizeof(struct sunxi_idma_des);
pdes[i].buf_addr_ptr1 =
- cpu_to_le32(sg_dma_address(&data->sg[i]));
- pdes[i].buf_addr_ptr2 = cpu_to_le32((u32)next_desc);
+ cpu_to_le32(sg_dma_address(&data->sg[i]) >>
+ host->cfg->idma_des_shift);
+ pdes[i].buf_addr_ptr2 = cpu_to_le32((u32)next_desc >>
+ host->cfg->idma_des_shift);
}
pdes[0].config |= cpu_to_le32(SDXC_IDMAC_DES0_FD);
.needs_new_timings = true,
};
+static const struct sunxi_mmc_cfg sun50i_a100_cfg = {
+ .idma_des_size_bits = 16,
+ .idma_des_shift = 2,
+ .clk_delays = NULL,
+ .can_calibrate = true,
+ .mask_data0 = true,
+ .needs_new_timings = true,
+};
+
+static const struct sunxi_mmc_cfg sun50i_a100_emmc_cfg = {
+ .idma_des_size_bits = 13,
+ .idma_des_shift = 2,
+ .clk_delays = NULL,
+ .can_calibrate = true,
+ .needs_new_timings = true,
+};
+
static const struct of_device_id sunxi_mmc_of_match[] = {
{ .compatible = "allwinner,sun4i-a10-mmc", .data = &sun4i_a10_cfg },
{ .compatible = "allwinner,sun5i-a13-mmc", .data = &sun5i_a13_cfg },
{ .compatible = "allwinner,sun9i-a80-mmc", .data = &sun9i_a80_cfg },
{ .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg },
{ .compatible = "allwinner,sun50i-a64-emmc", .data = &sun50i_a64_emmc_cfg },
+ { .compatible = "allwinner,sun50i-a100-mmc", .data = &sun50i_a100_cfg },
+ { .compatible = "allwinner,sun50i-a100-emmc", .data = &sun50i_a100_emmc_cfg },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);
#endif
static const struct dev_pm_ops sunxi_mmc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend,
sunxi_mmc_runtime_resume,
NULL)
mmc_request_done(mmc, mrq);
}
-static void tifm_sd_end_cmd(unsigned long data)
+static void tifm_sd_end_cmd(struct tasklet_struct *t)
{
- struct tifm_sd *host = (struct tifm_sd*)data;
+ struct tifm_sd *host = from_tasklet(host, t, finish_tasklet);
struct tifm_dev *sock = host->dev;
struct mmc_host *mmc = tifm_get_drvdata(sock);
struct mmc_request *mrq;
*/
mmc->max_busy_timeout = TIFM_MMCSD_REQ_TIMEOUT_MS;
- tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd,
- (unsigned long)host);
+ tasklet_setup(&host->finish_tasklet, tifm_sd_end_cmd);
timer_setup(&host->timer, tifm_sd_abort, 0);
mmc->ops = &tifm_sd_ops;
unsigned int direction, int blk_size);
int (*write16_hook)(struct tmio_mmc_host *host, int addr);
void (*reset)(struct tmio_mmc_host *host);
- bool (*check_retune)(struct tmio_mmc_host *host);
+ bool (*check_retune)(struct tmio_mmc_host *host, struct mmc_request *mrq);
void (*fixup_request)(struct tmio_mmc_host *host, struct mmc_request *mrq);
unsigned int (*get_timeout_cycles)(struct tmio_mmc_host *host);
if (!data)
goto out;
- if (stat & TMIO_STAT_CRCFAIL || stat & TMIO_STAT_STOPBIT_ERR ||
- stat & TMIO_STAT_TXUNDERRUN)
+ if (stat & TMIO_STAT_DATATIMEOUT)
+ data->error = -ETIMEDOUT;
+ else if (stat & TMIO_STAT_CRCFAIL || stat & TMIO_STAT_STOPBIT_ERR ||
+ stat & TMIO_STAT_TXUNDERRUN)
data->error = -EILSEQ;
if (host->dma_on && (data->flags & MMC_DATA_WRITE)) {
u32 status = sd_ctrl_read16_and_16_as_32(host, CTL_STATUS);
}
/* Error means retune, but executed command was still successful */
- if (host->check_retune && host->check_retune(host))
+ if (host->check_retune && host->check_retune(host, mrq))
mmc_retune_needed(host->mmc);
/* If SET_BLOCK_COUNT, continue with main command */
}
/* external DMA engine */
-static void uniphier_sd_external_dma_issue(unsigned long arg)
+static void uniphier_sd_external_dma_issue(struct tasklet_struct *t)
{
- struct tmio_mmc_host *host = (void *)arg;
+ struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue);
struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
uniphier_sd_dma_endisable(host, 1);
host->chan_rx = chan;
host->chan_tx = chan;
- tasklet_init(&host->dma_issue, uniphier_sd_external_dma_issue,
- (unsigned long)host);
+ tasklet_setup(&host->dma_issue, uniphier_sd_external_dma_issue);
}
static void uniphier_sd_external_dma_release(struct tmio_mmc_host *host)
.dataend = uniphier_sd_external_dma_dataend,
};
-static void uniphier_sd_internal_dma_issue(unsigned long arg)
+static void uniphier_sd_internal_dma_issue(struct tasklet_struct *t)
{
- struct tmio_mmc_host *host = (void *)arg;
+ struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue);
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
host->chan_tx = (void *)0xdeadbeaf;
- tasklet_init(&host->dma_issue, uniphier_sd_internal_dma_issue,
- (unsigned long)host);
+ tasklet_setup(&host->dma_issue, uniphier_sd_internal_dma_issue);
}
static void uniphier_sd_internal_dma_release(struct tmio_mmc_host *host)
ret = mmc_add_host(mmc);
if (ret < 0)
- goto e_clk_off;
+ goto e_release_dma;
return 0;
+e_release_dma:
+ usdhi6_dma_release(host);
e_clk_off:
clk_disable_unprepare(host->clk);
e_free_mmc:
spin_unlock_irqrestore(&sdhost->lock, flags);
}
-static void via_sdc_tasklet_finish(unsigned long param)
+static void via_sdc_tasklet_finish(struct tasklet_struct *t)
{
- struct via_crdr_mmc_host *host;
+ struct via_crdr_mmc_host *host = from_tasklet(host, t, finish_tasklet);
unsigned long flags;
struct mmc_request *mrq;
- host = (struct via_crdr_mmc_host *)param;
-
spin_lock_irqsave(&host->lock, flags);
del_timer(&host->timer);
INIT_WORK(&host->carddet_work, via_sdc_card_detect);
- tasklet_init(&host->finish_tasklet, via_sdc_tasklet_finish,
- (unsigned long)host);
+ tasklet_setup(&host->finish_tasklet, via_sdc_tasklet_finish);
addrbase = host->sdhc_mmiobase;
writel(0x0, addrbase + VIA_CRDR_SDINTMASK);
return host->mrq->cmd->data;
}
-static void wbsd_tasklet_card(unsigned long param)
+static void wbsd_tasklet_card(struct tasklet_struct *t)
{
- struct wbsd_host *host = (struct wbsd_host *)param;
+ struct wbsd_host *host = from_tasklet(host, t, card_tasklet);
u8 csr;
int delay = -1;
mmc_detect_change(host->mmc, msecs_to_jiffies(delay));
}
-static void wbsd_tasklet_fifo(unsigned long param)
+static void wbsd_tasklet_fifo(struct tasklet_struct *t)
{
- struct wbsd_host *host = (struct wbsd_host *)param;
+ struct wbsd_host *host = from_tasklet(host, t, fifo_tasklet);
struct mmc_data *data;
spin_lock(&host->lock);
spin_unlock(&host->lock);
}
-static void wbsd_tasklet_crc(unsigned long param)
+static void wbsd_tasklet_crc(struct tasklet_struct *t)
{
- struct wbsd_host *host = (struct wbsd_host *)param;
+ struct wbsd_host *host = from_tasklet(host, t, crc_tasklet);
struct mmc_data *data;
spin_lock(&host->lock);
spin_unlock(&host->lock);
}
-static void wbsd_tasklet_timeout(unsigned long param)
+static void wbsd_tasklet_timeout(struct tasklet_struct *t)
{
- struct wbsd_host *host = (struct wbsd_host *)param;
+ struct wbsd_host *host = from_tasklet(host, t, timeout_tasklet);
struct mmc_data *data;
spin_lock(&host->lock);
spin_unlock(&host->lock);
}
-static void wbsd_tasklet_finish(unsigned long param)
+static void wbsd_tasklet_finish(struct tasklet_struct *t)
{
- struct wbsd_host *host = (struct wbsd_host *)param;
+ struct wbsd_host *host = from_tasklet(host, t, finish_tasklet);
struct mmc_data *data;
spin_lock(&host->lock);
/*
* Set up tasklets. Must be done before requesting interrupt.
*/
- tasklet_init(&host->card_tasklet, wbsd_tasklet_card,
- (unsigned long)host);
- tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo,
- (unsigned long)host);
- tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc,
- (unsigned long)host);
- tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout,
- (unsigned long)host);
- tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish,
- (unsigned long)host);
+ tasklet_setup(&host->card_tasklet, wbsd_tasklet_card);
+ tasklet_setup(&host->fifo_tasklet, wbsd_tasklet_fifo);
+ tasklet_setup(&host->crc_tasklet, wbsd_tasklet_crc);
+ tasklet_setup(&host->timeout_tasklet, wbsd_tasklet_timeout);
+ tasklet_setup(&host->finish_tasklet, wbsd_tasklet_finish);
/*
* Allocate interrupt.
}
/* The actual number of configurations supported is (CFGC+1) */
- err = blk_ksm_init(&hba->ksm,
- hba->crypto_capabilities.config_count + 1);
+ err = devm_blk_ksm_init(hba->dev, &hba->ksm,
+ hba->crypto_capabilities.config_count + 1);
if (err)
goto out_free_caps;
if (hba->caps & UFSHCD_CAP_CRYPTO)
blk_ksm_register(&hba->ksm, q);
}
-
-void ufshcd_crypto_destroy_keyslot_manager(struct ufs_hba *hba)
-{
- blk_ksm_destroy(&hba->ksm);
-}
void ufshcd_crypto_setup_rq_keyslot_manager(struct ufs_hba *hba,
struct request_queue *q);
-void ufshcd_crypto_destroy_keyslot_manager(struct ufs_hba *hba);
-
#else /* CONFIG_SCSI_UFS_CRYPTO */
static inline void ufshcd_prepare_lrbp_crypto(struct request *rq,
static inline void ufshcd_crypto_setup_rq_keyslot_manager(struct ufs_hba *hba,
struct request_queue *q) { }
-static inline void ufshcd_crypto_destroy_keyslot_manager(struct ufs_hba *hba)
-{ }
-
#endif /* CONFIG_SCSI_UFS_CRYPTO */
#endif /* _UFSHCD_CRYPTO_H */
*/
void ufshcd_dealloc_host(struct ufs_hba *hba)
{
- ufshcd_crypto_destroy_keyslot_manager(hba);
scsi_host_put(hba->host);
}
EXPORT_SYMBOL_GPL(ufshcd_dealloc_host);
int blk_ksm_init(struct blk_keyslot_manager *ksm, unsigned int num_slots);
+int devm_blk_ksm_init(struct device *dev, struct blk_keyslot_manager *ksm,
+ unsigned int num_slots);
+
blk_status_t blk_ksm_get_slot_for_key(struct blk_keyslot_manager *ksm,
const struct blk_crypto_key *key,
struct blk_ksm_keyslot **slot_ptr);
struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
unsigned int nr_parts;
- unsigned int bouncesz; /* Bounce buffer size */
struct workqueue_struct *complete_wq; /* Private workqueue */
};
bool cap_cmd_during_tfr;
int tag;
+
+#ifdef CONFIG_MMC_CRYPTO
+ bool crypto_enabled;
+ int crypto_key_slot;
+ u32 data_unit_num;
+#endif
};
struct mmc_card;
#include <linux/mmc/card.h>
#include <linux/mmc/pm.h>
#include <linux/dma-direction.h>
+#include <linux/keyslot-manager.h>
struct mmc_ios {
unsigned int clock; /* clock rate */
bool enhanced_strobe; /* hs400es selection */
};
+struct mmc_clk_phase {
+ bool valid;
+ u16 in_deg;
+ u16 out_deg;
+};
+
+#define MMC_NUM_CLK_PHASES (MMC_TIMING_MMC_HS400 + 1)
+struct mmc_clk_phase_map {
+ struct mmc_clk_phase phase[MMC_NUM_CLK_PHASES];
+};
+
struct mmc_host;
struct mmc_host_ops {
#define MMC_CAP2_CQE_DCMD (1 << 24) /* CQE can issue a direct command */
#define MMC_CAP2_AVOID_3_3V (1 << 25) /* Host must negotiate down from 3.3V */
#define MMC_CAP2_MERGE_CAPABLE (1 << 26) /* Host can merge a segment over the segment size */
+#ifdef CONFIG_MMC_CRYPTO
+#define MMC_CAP2_CRYPTO (1 << 27) /* Host supports inline encryption */
+#else
+#define MMC_CAP2_CRYPTO 0
+#endif
int fixed_drv_type; /* fixed driver type for non-removable media */
unsigned int doing_retune:1; /* re-tuning in progress */
unsigned int retune_now:1; /* do re-tuning at next req */
unsigned int retune_paused:1; /* re-tuning is temporarily disabled */
- unsigned int use_blk_mq:1; /* use blk-mq */
unsigned int retune_crc_disable:1; /* don't trigger retune upon crc */
unsigned int can_dma_map_merge:1; /* merging can be used */
bool cqe_enabled;
bool cqe_on;
+ /* Inline encryption support */
+#ifdef CONFIG_MMC_CRYPTO
+ struct blk_keyslot_manager ksm;
+#endif
+
/* Host Software Queue support */
bool hsq_enabled;
int mmc_add_host(struct mmc_host *);
void mmc_remove_host(struct mmc_host *);
void mmc_free_host(struct mmc_host *);
+void mmc_of_parse_clk_phase(struct mmc_host *host,
+ struct mmc_clk_phase_map *map);
int mmc_of_parse(struct mmc_host *host);
int mmc_of_parse_voltage(struct device_node *np, u32 *mask);
const char *name;
u32 ocr_mask;
- /* Card detection IRQs */
- int card_detect_irq;
+ /* Card detection */
int (*card_detect)(struct device *dev, int slot);
unsigned int ban_openended:1;