MMC core:

- Add documentation for the mmc-test driver
  - Register the eMMC RPMB partition with the RPMB subsystem
  - Some various cleanups
 
 MMC host:
  - dw_mmc-rockchip: Add support for the RK3576 variant
  - renesas_sdhi: Add support for the RZ/V2H(P) variant
  - sdhci_am654: Add a retry mechanism for tuning
  - sdhci-atmel: Convert DT bindings to json schema
  - sdhci-of-dwcmshc: Add eMMC HW reset support for BlueField-3 SoC
  - sdhci-of-dwcmshc: Add support for the RK3576 variant
  - sdhci-of-dwcmshc: Add support for the Sophgo SG2042 variant
  - sdhci-of-ma35d1: Add new driver for the Nuvoton MA35D1 SDHCI
 
 Misc/Tee:
  - Add Replay Protected Memory Block (RPMB) subsystem
  - Let optee probe RPMB device using RPMB subsystem
 -----BEGIN PGP SIGNATURE-----
 
 iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmboQbkXHHVsZi5oYW5z
 c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjClXCBAAg1X6Q+4Mx4LZszDWeSDQfH4d
 dbD3RDz8vTBCGrOzZT5Ak+0d3aSeJv5Fxz2XTp4W8D5KXm8HdYC1x7c81StL104k
 yu5xG53u3D9qerwqaw/EGPqW9LUpsMRBRepbHlI9pH7hfmv9oqZXHl9hRnnDDrDJ
 FMNz9PrUdEUCk8RP9StyYIKUpe1VsIlBa1xNXXuDqt3d70UgX7odIOGOXdWv12CV
 zSB7Y7FlNBk1A/EJ4ptrfOc3RnCOtq1+j2D5TrOQ16rPC5Ud55XMNjrRir57nl3O
 ikd/hXUc7cklbYMv1K61aSAv0mTODkZ2P78buP/6dz1NUeFeoXkYfFR2eTeCDNdD
 H+TieLw2g5p0cDEq21K4V6ZyzC3kh8qhrza9EXVgFfg6WkbxHxFzSb9heOr1K8pn
 2yOLaDRlzHWPU6jOIMHeZgnvWOIVcIlHGu7qMKTDsnJh2Ot+HPjsNoToiWnCEvvs
 UbfWRfcdLCxkmqHS3dROJ8d6rPa1bDXy4/MZxjDzhKCarW1ZS2n4N55qeuM6Fb9s
 VjT43HlqgA/Jlo9zUDvkDG4Zblo22zQRBsyccqD8W4T+hnR917NA7/808ZogFdHX
 WjHYYWkCOjaZcVv5T9ZztVm0nTgeeM1/cAPyo8JlbVtBX2ZBJtiIW6bqWxGCw+Vw
 vxI5ht9AeVDTHixKK+U=
 =en1i
 -----END PGP SIGNATURE-----

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

Pull MMC updates from Ulf Hansson:
 "MMC core:
   - Add documentation for the mmc-test driver
   - Register the eMMC RPMB partition with the RPMB subsystem
   - Some various cleanups

  MMC host:
   - dw_mmc-rockchip: Add support for the RK3576 variant
   - renesas_sdhi: Add support for the RZ/V2H(P) variant
   - sdhci_am654: Add a retry mechanism for tuning
   - sdhci-atmel: Convert DT bindings to json schema
   - sdhci-of-dwcmshc:
       - Add eMMC HW reset support for BlueField-3 SoC
       - Add support for the RK3576 variant
       - Add support for the Sophgo SG2042 variant
   - sdhci-of-ma35d1: Add new driver for the Nuvoton MA35D1 SDHCI

  Misc/Tee:
   - Add Replay Protected Memory Block (RPMB) subsystem
   - Let optee probe RPMB device using RPMB subsystem"

* tag 'mmc-v6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (41 commits)
  mmc: core: Use dev_err_probe for deferred regulators
  optee: Fix a NULL vs IS_ERR() check
  mmc: sdhci_am654: Add prints to tuning algorithm
  mmc: sdhci_am654: Add retry tuning
  dt-bindings: mmc: Add support for rk3576 eMMC
  Documentation: mmc: Add mmc-test doc
  rpmb: fix error path in rpmb_dev_register()
  optee: add RPMB dependency
  mmc: block: add RPMB dependency
  mmc: core Convert UNSTUFF_BITS macro to inline function
  dt-bindings: mmc: sdhci-atmel: Convert to json schema
  mmc: core: Convert simple_stroul to kstroul
  mmc: core: Calculate size from pointer
  mmc: cqhci: Make use of cqhci_halted() routine
  mmc: core: Replace the argument of mmc_sd_switch() with defines
  mmc: dw_mmc-rockchip: Add support for rk3576 SoCs
  mmc: dw_mmc-rockchip: Add internal phase support
  dt-bindings: mmc: Add support for rk3576 dw-mshc
  mmc: sdhci-of-dwcmshc: Add hw_reset() support for BlueField-3 SoC
  mmc: core: remove left-over data structure declarations
  ...
This commit is contained in:
Linus Torvalds 2024-09-18 10:36:30 +02:00
commit 7fced2a78a
46 changed files with 2633 additions and 449 deletions

View File

@ -0,0 +1,15 @@
What: /sys/class/tee/tee{,priv}X/rpmb_routing_model
Date: May 2024
KernelVersion: 6.10
Contact: op-tee@lists.trustedfirmware.org
Description:
RPMB frames can be routed to the RPMB device via the
user-space daemon tee-supplicant or the RPMB subsystem
in the kernel. The value "user" means that the driver
will route the RPMB frames via user space. Conversely,
"kernel" means that the frames are routed via the RPMB
subsystem without assistance from tee-supplicant. It
should be assumed that RPMB frames are routed via user
space if the variable is absent. The primary purpose
of this variable is to let systemd know whether
tee-supplicant is needed in the early boot with initramfs.

View File

@ -0,0 +1,92 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mmc/atmel,sama5d2-sdhci.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Atmel SDHCI controller
maintainers:
- Aubin Constans <aubin.constans@microchip.com>
- Nicolas Ferre <nicolas.ferre@microchip.com>
description:
Bindings for the SDHCI controller found in Atmel/Microchip SoCs.
properties:
compatible:
oneOf:
- enum:
- atmel,sama5d2-sdhci
- microchip,sam9x60-sdhci
- items:
- enum:
- microchip,sam9x7-sdhci
- microchip,sama7g5-sdhci
- const: microchip,sam9x60-sdhci
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: hclock
- description: multclk
- description: baseclk
minItems: 2
clock-names:
items:
- const: hclock
- const: multclk
- const: baseclk
minItems: 2
microchip,sdcal-inverted:
type: boolean
description:
When present, polarity on the SDCAL SoC pin is inverted. The default
polarity for this signal is described in the datasheet. For instance on
SAMA5D2, the pin is usually tied to the GND with a resistor and a
capacitor (see "SDMMC I/O Calibration" chapter).
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
allOf:
- $ref: sdhci-common.yaml#
- if:
properties:
compatible:
contains:
enum:
- atmel,sama5d2-sdhci
then:
properties:
clocks:
minItems: 3
clock-names:
minItems: 3
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/clock/at91.h>
mmc@a0000000 {
compatible = "atmel,sama5d2-sdhci";
reg = <0xa0000000 0x300>;
interrupts = <31 IRQ_TYPE_LEVEL_HIGH 0>;
clocks = <&sdmmc0_hclk>, <&sdmmc0_gclk>, <&main>;
clock-names = "hclock", "multclk", "baseclk";
assigned-clocks = <&sdmmc0_gclk>;
assigned-clock-rates = <480000000>;
};

View File

@ -0,0 +1,87 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mmc/nuvoton,ma35d1-sdhci.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Nuvoton MA35D1 SD/SDIO/MMC Controller
maintainers:
- Shan-Chun Hung <shanchun1218@gmail.com>
allOf:
- $ref: sdhci-common.yaml#
properties:
compatible:
enum:
- nuvoton,ma35d1-sdhci
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
pinctrl-names:
minItems: 1
items:
- const: default
- const: state_uhs
pinctrl-0:
description:
Should contain default/high speed pin ctrl.
maxItems: 1
pinctrl-1:
description:
Should contain uhs mode pin ctrl.
maxItems: 1
resets:
maxItems: 1
nuvoton,sys:
$ref: /schemas/types.yaml#/definitions/phandle
description: phandle to access GCR (Global Control Register) registers.
required:
- compatible
- reg
- interrupts
- clocks
- pinctrl-names
- pinctrl-0
- resets
- nuvoton,sys
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
#include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
mmc@40190000 {
compatible = "nuvoton,ma35d1-sdhci";
reg = <0x0 0x40190000 0x0 0x2000>;
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk SDH1_GATE>;
pinctrl-names = "default", "state_uhs";
pinctrl-0 = <&pinctrl_sdhci1>;
pinctrl-1 = <&pinctrl_sdhci1_uhs>;
resets = <&sys MA35D1_RESET_SDH1>;
nuvoton,sys = <&sys>;
vqmmc-supply = <&sdhci1_vqmmc_regulator>;
bus-width = <8>;
max-frequency = <200000000>;
};
};

View File

@ -18,6 +18,7 @@ properties:
- renesas,sdhi-r7s9210 # SH-Mobile AG5 - renesas,sdhi-r7s9210 # SH-Mobile AG5
- renesas,sdhi-r8a73a4 # R-Mobile APE6 - renesas,sdhi-r8a73a4 # R-Mobile APE6
- renesas,sdhi-r8a7740 # R-Mobile A1 - renesas,sdhi-r8a7740 # R-Mobile A1
- renesas,sdhi-r9a09g057 # RZ/V2H(P)
- renesas,sdhi-sh73a0 # R-Mobile APE6 - renesas,sdhi-sh73a0 # R-Mobile APE6
- items: - items:
- enum: - enum:
@ -75,9 +76,13 @@ properties:
minItems: 1 minItems: 1
maxItems: 3 maxItems: 3
clocks: true clocks:
minItems: 1
maxItems: 4
clock-names: true clock-names:
minItems: 1
maxItems: 4
dmas: dmas:
minItems: 4 minItems: 4
@ -118,7 +123,9 @@ allOf:
properties: properties:
compatible: compatible:
contains: contains:
const: renesas,rzg2l-sdhi enum:
- renesas,sdhi-r9a09g057
- renesas,rzg2l-sdhi
then: then:
properties: properties:
clocks: clocks:

View File

@ -43,6 +43,8 @@ properties:
- rockchip,rv1108-dw-mshc - rockchip,rv1108-dw-mshc
- rockchip,rv1126-dw-mshc - rockchip,rv1126-dw-mshc
- const: rockchip,rk3288-dw-mshc - const: rockchip,rk3288-dw-mshc
# for Rockchip RK3576 with phase tuning inside the controller
- const: rockchip,rk3576-dw-mshc
reg: reg:
maxItems: 1 maxItems: 1

View File

@ -1,35 +0,0 @@
* Atmel SDHCI controller
This file documents the differences between the core properties in
Documentation/devicetree/bindings/mmc/mmc.txt and the properties used by the
sdhci-of-at91 driver.
Required properties:
- compatible: Must be "atmel,sama5d2-sdhci" or "microchip,sam9x60-sdhci"
or "microchip,sam9x7-sdhci", "microchip,sam9x60-sdhci".
- clocks: Phandlers to the clocks.
- clock-names: Must be "hclock", "multclk", "baseclk" for
"atmel,sama5d2-sdhci".
Must be "hclock", "multclk" for "microchip,sam9x60-sdhci".
Must be "hclock", "multclk" for "microchip,sam9x7-sdhci".
Optional properties:
- assigned-clocks: The same with "multclk".
- assigned-clock-rates The rate of "multclk" in order to not rely on the
gck configuration set by previous components.
- microchip,sdcal-inverted: when present, polarity on the SDCAL SoC pin is
inverted. The default polarity for this signal is described in the datasheet.
For instance on SAMA5D2, the pin is usually tied to the GND with a resistor
and a capacitor (see "SDMMC I/O Calibration" chapter).
Example:
mmc0: sdio-host@a0000000 {
compatible = "atmel,sama5d2-sdhci";
reg = <0xa0000000 0x300>;
interrupts = <31 IRQ_TYPE_LEVEL_HIGH 0>;
clocks = <&sdmmc0_hclk>, <&sdmmc0_gclk>, <&main>;
clock-names = "hclock", "multclk", "baseclk";
assigned-clocks = <&sdmmc0_gclk>;
assigned-clock-rates = <480000000>;
};

View File

@ -10,18 +10,20 @@ maintainers:
- Ulf Hansson <ulf.hansson@linaro.org> - Ulf Hansson <ulf.hansson@linaro.org>
- Jisheng Zhang <Jisheng.Zhang@synaptics.com> - Jisheng Zhang <Jisheng.Zhang@synaptics.com>
allOf:
- $ref: mmc-controller.yaml#
properties: properties:
compatible: compatible:
enum: oneOf:
- rockchip,rk3568-dwcmshc - items:
- rockchip,rk3588-dwcmshc - const: rockchip,rk3576-dwcmshc
- snps,dwcmshc-sdhci - const: rockchip,rk3588-dwcmshc
- sophgo,cv1800b-dwcmshc - enum:
- sophgo,sg2002-dwcmshc - rockchip,rk3568-dwcmshc
- thead,th1520-dwcmshc - rockchip,rk3588-dwcmshc
- snps,dwcmshc-sdhci
- sophgo,cv1800b-dwcmshc
- sophgo,sg2002-dwcmshc
- sophgo,sg2042-dwcmshc
- thead,th1520-dwcmshc
reg: reg:
maxItems: 1 maxItems: 1
@ -31,22 +33,14 @@ properties:
clocks: clocks:
minItems: 1 minItems: 1
items: maxItems: 5
- description: core clock
- description: bus clock for optional
- description: axi clock for rockchip specified
- description: block clock for rockchip specified
- description: timer clock for rockchip specified
clock-names: clock-names:
minItems: 1 minItems: 1
items: maxItems: 5
- const: core
- const: bus power-domains:
- const: axi maxItems: 1
- const: block
- const: timer
resets: resets:
maxItems: 5 maxItems: 5
@ -63,7 +57,6 @@ properties:
description: Specify the number of delay for tx sampling. description: Specify the number of delay for tx sampling.
$ref: /schemas/types.yaml#/definitions/uint8 $ref: /schemas/types.yaml#/definitions/uint8
required: required:
- compatible - compatible
- reg - reg
@ -71,6 +64,60 @@ required:
- clocks - clocks
- clock-names - clock-names
allOf:
- $ref: mmc-controller.yaml#
- if:
properties:
compatible:
contains:
const: sophgo,sg2042-dwcmshc
then:
properties:
clocks:
items:
- description: core clock
- description: bus clock
- description: timer clock
clock-names:
items:
- const: core
- const: bus
- const: timer
else:
properties:
clocks:
minItems: 1
items:
- description: core clock
- description: bus clock for optional
- description: axi clock for rockchip specified
- description: block clock for rockchip specified
- description: timer clock for rockchip specified
clock-names:
minItems: 1
items:
- const: core
- const: bus
- const: axi
- const: block
- const: timer
- if:
properties:
compatible:
contains:
const: rockchip,rk3576-dwcmshc
then:
required:
- power-domains
else:
properties:
power-domains: false
unevaluatedProperties: false unevaluatedProperties: false
examples: examples:

View File

@ -10,4 +10,5 @@ MMC/SD/SDIO card support
mmc-dev-attrs mmc-dev-attrs
mmc-dev-parts mmc-dev-parts
mmc-async-req mmc-async-req
mmc-test
mmc-tools mmc-tools

View File

@ -0,0 +1,299 @@
.. SPDX-License-Identifier: GPL-2.0
========================
MMC Test Framework
========================
Overview
========
The `mmc_test` framework is designed to test the performance and reliability of host controller drivers and all devices handled by the MMC subsystem. This includes not only MMC devices but also SD cards and other devices supported by the subsystem.
The framework provides a variety of tests to evaluate different aspects of the host controller and device interactions, such as read and write performance, data integrity, and error handling. These tests help ensure that the host controller drivers and devices operate correctly under various conditions.
The `mmc_test` framework is particularly useful for:
- Verifying the functionality and performance of MMC and SD host controller drivers.
- Ensuring compatibility and reliability of MMC and SD devices.
- Identifying and diagnosing issues in the MMC subsystem.
The results of the tests are logged in the kernel log, providing detailed information about the test outcomes and any encountered issues.
Note: whatever is on your card will be overwritten by these tests.
Initialization
==============
To use the ``mmc_test`` framework, follow these steps:
1. **Enable the MMC Test Framework**:
Ensure that the ``CONFIG_MMC_TEST`` kernel configuration option is enabled. This can be done by configuring the kernel:
.. code-block:: none
make menuconfig
Navigate to:
Device Drivers --->
<*> MMC/SD/SDIO card support --->
[*] MMC host test driver
Alternatively, you can enable it directly in the kernel configuration file:
.. code-block:: none
echo "CONFIG_MMC_TEST=y" >> .config
Rebuild and install the kernel if necessary.
2. **Load the MMC Test Module**:
If the ``mmc_test`` framework is built as a module, you need to load it using ``modprobe``:
.. code-block:: none
modprobe mmc_test
Binding the MMC Card for Testing
================================
To enable MMC testing, you need to unbind the MMC card from the ``mmcblk`` driver and bind it to the ``mmc_test`` driver. This allows the ``mmc_test`` framework to take control of the MMC card for testing purposes.
1. Identify the MMC card:
.. code-block:: sh
ls /sys/bus/mmc/devices/
This will list the MMC devices, such as ``mmc0:0001``.
2. Unbind the MMC card from the ``mmcblk`` driver:
.. code-block:: sh
echo 'mmc0:0001' > /sys/bus/mmc/drivers/mmcblk/unbind
3. Bind the MMC card to the ``mmc_test`` driver:
.. code-block:: sh
echo 'mmc0:0001' > /sys/bus/mmc/drivers/mmc_test/bind
After binding, you should see a line in the kernel log indicating that the card has been claimed for testing:
.. code-block:: none
mmc_test mmc0:0001: Card claimed for testing.
Usage - Debugfs Entries
=======================
Once the ``mmc_test`` framework is enabled, you can interact with the following debugfs entries located in ``/sys/kernel/debug/mmc0/mmc0:0001``:
1. **test**:
This file is used to run specific tests. Write the test number to this file to execute a test.
.. code-block:: sh
echo <test_number> > /sys/kernel/debug/mmc0/mmc0:0001/test
The test result is indicated in the kernel log info. You can view the kernel log using the `dmesg` command or by checking the log file in `/var/log/`.
.. code-block:: sh
dmesg | grep mmc0
Example:
To run test number 4 (Basic read with data verification):
.. code-block:: sh
echo 4 > /sys/kernel/debug/mmc0/mmc0:0001/test
Check the kernel log for the result:
.. code-block:: sh
dmesg | grep mmc0
2. **testlist**:
This file lists all available tests. You can read this file to see the list of tests and their corresponding numbers.
.. code-block:: sh
cat /sys/kernel/debug/mmc0/mmc0:0001/testlist
The available tests are listed in the table below:
+------+--------------------------------+---------------------------------------------+
| Test | Test Name | Test Description |
+======+================================+=============================================+
| 0 | Run all tests | Runs all available tests |
+------+--------------------------------+---------------------------------------------+
| 1 | Basic write | Performs a basic write operation of a |
| | | single 512-Byte block to the MMC card |
| | | without data verification. |
+------+--------------------------------+---------------------------------------------+
| 2 | Basic read | Same for read |
+------+--------------------------------+---------------------------------------------+
| 3 | Basic write | Performs a basic write operation of a |
| | (with data verification) | single 512-Byte block to the MMC card |
| | | with data verification by reading back |
| | | the written data and comparing it. |
+------+--------------------------------+---------------------------------------------+
| 4 | Basic read | Same for read |
| | (with data verification) | |
+------+--------------------------------+---------------------------------------------+
| 5 | Multi-block write | Performs a multi-block write operation of |
| | | 8 blocks (each 512 bytes) to the MMC card. |
+------+--------------------------------+---------------------------------------------+
| 6 | Multi-block read | Same for read |
+------+--------------------------------+---------------------------------------------+
| 7 | Power of two block writes | Performs write operations with block sizes |
| | | that are powers of two, starting from 1 |
| | | byte up to 256 bytes, to the MMC card. |
+------+--------------------------------+---------------------------------------------+
| 8 | Power of two block reads | Same for read |
+------+--------------------------------+---------------------------------------------+
| 9 | Weird sized block writes | Performs write operations with varying |
| | | block sizes starting from 3 bytes and |
| | | increasing by 7 bytes each iteration, up |
| | | to 511 bytes, to the MMC card. |
+------+--------------------------------+---------------------------------------------+
| 10 | Weird sized block reads | same for read |
+------+--------------------------------+---------------------------------------------+
| 11 | Badly aligned write | Performs write operations with buffers |
| | | starting at different alignments (0 to 7 |
| | | bytes offset) to test how the MMC card |
| | | handles unaligned data transfers. |
+------+--------------------------------+---------------------------------------------+
| 12 | Badly aligned read | same for read |
+------+--------------------------------+---------------------------------------------+
| 13 | Badly aligned multi-block write| same for multi-write |
+------+--------------------------------+---------------------------------------------+
| 14 | Badly aligned multi-block read | same for multi-read |
+------+--------------------------------+---------------------------------------------+
| 15 | Proper xfer_size at write | intentionally create a broken transfer by |
| | (Start failure) | modifying the MMC request in a way that it |
| | | will not perform as expected, e.g. use |
| | | MMC_WRITE_BLOCK for a multi-block transfer |
+------+--------------------------------+---------------------------------------------+
| 16 | Proper xfer_size at read | same for read |
| | (Start failure) | |
+------+--------------------------------+---------------------------------------------+
| 17 | Proper xfer_size at write | same for 2 blocks |
| | (Midway failure) | |
+------+--------------------------------+---------------------------------------------+
| 18 | Proper xfer_size at read | same for read |
| | (Midway failure) | |
+------+--------------------------------+---------------------------------------------+
| 19 | Highmem write | use a high memory page |
+------+--------------------------------+---------------------------------------------+
| 20 | Highmem read | same for read |
+------+--------------------------------+---------------------------------------------+
| 21 | Multi-block highmem write | same for multi-write |
+------+--------------------------------+---------------------------------------------+
| 22 | Multi-block highmem read | same for mult-read |
+------+--------------------------------+---------------------------------------------+
| 23 | Best-case read performance | Performs 512K sequential read (non sg) |
+------+--------------------------------+---------------------------------------------+
| 24 | Best-case write performance | same for write |
+------+--------------------------------+---------------------------------------------+
| 25 | Best-case read performance | Same using sg |
| | (Into scattered pages) | |
+------+--------------------------------+---------------------------------------------+
| 26 | Best-case write performance | same for write |
| | (From scattered pages) | |
+------+--------------------------------+---------------------------------------------+
| 27 | Single read performance | By transfer size |
+------+--------------------------------+---------------------------------------------+
| 28 | Single write performance | By transfer size |
+------+--------------------------------+---------------------------------------------+
| 29 | Single trim performance | By transfer size |
+------+--------------------------------+---------------------------------------------+
| 30 | Consecutive read performance | By transfer size |
+------+--------------------------------+---------------------------------------------+
| 31 | Consecutive write performance | By transfer size |
+------+--------------------------------+---------------------------------------------+
| 32 | Consecutive trim performance | By transfer size |
+------+--------------------------------+---------------------------------------------+
| 33 | Random read performance | By transfer size |
+------+--------------------------------+---------------------------------------------+
| 34 | Random write performance | By transfer size |
+------+--------------------------------+---------------------------------------------+
| 35 | Large sequential read | Into scattered pages |
+------+--------------------------------+---------------------------------------------+
| 36 | Large sequential write | From scattered pages |
+------+--------------------------------+---------------------------------------------+
| 37 | Write performance | With blocking req 4k to 4MB |
+------+--------------------------------+---------------------------------------------+
| 38 | Write performance | With non-blocking req 4k to 4MB |
+------+--------------------------------+---------------------------------------------+
| 39 | Read performance | With blocking req 4k to 4MB |
+------+--------------------------------+---------------------------------------------+
| 40 | Read performance | With non-blocking req 4k to 4MB |
+------+--------------------------------+---------------------------------------------+
| 41 | Write performance | Blocking req 1 to 512 sg elems |
+------+--------------------------------+---------------------------------------------+
| 42 | Write performance | Non-blocking req 1 to 512 sg elems |
+------+--------------------------------+---------------------------------------------+
| 43 | Read performance | Blocking req 1 to 512 sg elems |
+------+--------------------------------+---------------------------------------------+
| 44 | Read performance | Non-blocking req 1 to 512 sg elems |
+------+--------------------------------+---------------------------------------------+
| 45 | Reset test | |
+------+--------------------------------+---------------------------------------------+
| 46 | Commands during read | No Set Block Count (CMD23) |
+------+--------------------------------+---------------------------------------------+
| 47 | Commands during write | No Set Block Count (CMD23) |
+------+--------------------------------+---------------------------------------------+
| 48 | Commands during read | Use Set Block Count (CMD23) |
+------+--------------------------------+---------------------------------------------+
| 49 | Commands during write | Use Set Block Count (CMD23) |
+------+--------------------------------+---------------------------------------------+
| 50 | Commands during non-blocking | Read - use Set Block Count (CMD23) |
+------+--------------------------------+---------------------------------------------+
| 51 | Commands during non-blocking | Write - use Set Block Count (CMD23) |
+------+--------------------------------+---------------------------------------------+
Test Results
============
The results of the tests are logged in the kernel log. Each test logs the start, end, and result of the test. The possible results are:
- **OK**: The test completed successfully.
- **FAILED**: The test failed.
- **UNSUPPORTED (by host)**: The test is unsupported by the host.
- **UNSUPPORTED (by card)**: The test is unsupported by the card.
- **ERROR**: An error occurred during the test.
Example Kernel Log Output
=========================
When running a test, you will see log entries similar to the following in the kernel log:
.. code-block:: none
[ 1234.567890] mmc0: Starting tests of card mmc0:0001...
[ 1234.567891] mmc0: Test case 4. Basic read (with data verification)...
[ 1234.567892] mmc0: Result: OK
[ 1234.567893] mmc0: Tests completed.
In this example, test case 4 (Basic read with data verification) was executed, and the result was OK.
Contributing
============
Contributions to the `mmc_test` framework are welcome. Please follow the standard Linux kernel contribution guidelines and submit patches to the appropriate maintainers.
Contact
=======
For more information or to report issues, please contact the MMC subsystem maintainers.

View File

@ -19974,6 +19974,13 @@ T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-de2-rotate.yaml F: Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-de2-rotate.yaml
F: drivers/media/platform/sunxi/sun8i-rotate/ F: drivers/media/platform/sunxi/sun8i-rotate/
RPMB SUBSYSTEM
M: Jens Wiklander <jens.wiklander@linaro.org>
L: linux-kernel@vger.kernel.org
S: Supported
F: drivers/misc/rpmb-core.c
F: include/linux/rpmb.h
RPMSG TTY DRIVER RPMSG TTY DRIVER
M: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com> M: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
L: linux-remoteproc@vger.kernel.org L: linux-remoteproc@vger.kernel.org
@ -22580,6 +22587,7 @@ M: Jens Wiklander <jens.wiklander@linaro.org>
R: Sumit Garg <sumit.garg@linaro.org> R: Sumit Garg <sumit.garg@linaro.org>
L: op-tee@lists.trustedfirmware.org L: op-tee@lists.trustedfirmware.org
S: Maintained S: Maintained
F: Documentation/ABI/testing/sysfs-class-tee
F: Documentation/driver-api/tee.rst F: Documentation/driver-api/tee.rst
F: Documentation/tee/ F: Documentation/tee/
F: Documentation/userspace-api/tee.rst F: Documentation/userspace-api/tee.rst

View File

@ -104,6 +104,16 @@ config PHANTOM
If you choose to build module, its name will be phantom. If unsure, If you choose to build module, its name will be phantom. If unsure,
say N here. say N here.
config RPMB
tristate "RPMB partition interface"
depends on MMC
help
Unified RPMB unit interface for RPMB capable devices such as eMMC and
UFS. Provides interface for in-kernel security controllers to access
RPMB unit.
If unsure, select N.
config TIFM_CORE config TIFM_CORE
tristate "TI Flash Media interface support" tristate "TI Flash Media interface support"
depends on PCI depends on PCI

View File

@ -15,6 +15,7 @@ obj-$(CONFIG_LKDTM) += lkdtm/
obj-$(CONFIG_TIFM_CORE) += tifm_core.o obj-$(CONFIG_TIFM_CORE) += tifm_core.o
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
obj-$(CONFIG_PHANTOM) += phantom.o obj-$(CONFIG_PHANTOM) += phantom.o
obj-$(CONFIG_RPMB) += rpmb-core.o
obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o
obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o
obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o

231
drivers/misc/rpmb-core.c Normal file
View File

@ -0,0 +1,231 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright(c) 2015 - 2019 Intel Corporation. All rights reserved.
* Copyright(c) 2021 - 2024 Linaro Ltd.
*/
#include <linux/device.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/rpmb.h>
#include <linux/slab.h>
static DEFINE_IDA(rpmb_ida);
static DEFINE_MUTEX(rpmb_mutex);
/**
* rpmb_dev_get() - increase rpmb device ref counter
* @rdev: rpmb device
*/
struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev)
{
if (rdev)
get_device(&rdev->dev);
return rdev;
}
EXPORT_SYMBOL_GPL(rpmb_dev_get);
/**
* rpmb_dev_put() - decrease rpmb device ref counter
* @rdev: rpmb device
*/
void rpmb_dev_put(struct rpmb_dev *rdev)
{
if (rdev)
put_device(&rdev->dev);
}
EXPORT_SYMBOL_GPL(rpmb_dev_put);
/**
* rpmb_route_frames() - route rpmb frames to rpmb device
* @rdev: rpmb device
* @req: rpmb request frames
* @req_len: length of rpmb request frames in bytes
* @rsp: rpmb response frames
* @rsp_len: length of rpmb response frames in bytes
*
* Returns: < 0 on failure
*/
int rpmb_route_frames(struct rpmb_dev *rdev, u8 *req,
unsigned int req_len, u8 *rsp, unsigned int rsp_len)
{
if (!req || !req_len || !rsp || !rsp_len)
return -EINVAL;
return rdev->descr.route_frames(rdev->dev.parent, req, req_len,
rsp, rsp_len);
}
EXPORT_SYMBOL_GPL(rpmb_route_frames);
static void rpmb_dev_release(struct device *dev)
{
struct rpmb_dev *rdev = to_rpmb_dev(dev);
mutex_lock(&rpmb_mutex);
ida_simple_remove(&rpmb_ida, rdev->id);
mutex_unlock(&rpmb_mutex);
kfree(rdev->descr.dev_id);
kfree(rdev);
}
static struct class rpmb_class = {
.name = "rpmb",
.dev_release = rpmb_dev_release,
};
/**
* rpmb_dev_find_device() - return first matching rpmb device
* @start: rpmb device to begin with
* @data: data for the match function
* @match: the matching function
*
* Iterate over registered RPMB devices, and call @match() for each passing
* it the RPMB device and @data.
*
* The return value of @match() is checked for each call. If it returns
* anything other 0, break and return the found RPMB device.
*
* It's the callers responsibility to call rpmb_dev_put() on the returned
* device, when it's done with it.
*
* Returns: a matching rpmb device or NULL on failure
*/
struct rpmb_dev *rpmb_dev_find_device(const void *data,
const struct rpmb_dev *start,
int (*match)(struct device *dev,
const void *data))
{
struct device *dev;
const struct device *start_dev = NULL;
if (start)
start_dev = &start->dev;
dev = class_find_device(&rpmb_class, start_dev, data, match);
return dev ? to_rpmb_dev(dev) : NULL;
}
EXPORT_SYMBOL_GPL(rpmb_dev_find_device);
int rpmb_interface_register(struct class_interface *intf)
{
intf->class = &rpmb_class;
return class_interface_register(intf);
}
EXPORT_SYMBOL_GPL(rpmb_interface_register);
void rpmb_interface_unregister(struct class_interface *intf)
{
class_interface_unregister(intf);
}
EXPORT_SYMBOL_GPL(rpmb_interface_unregister);
/**
* rpmb_dev_unregister() - unregister RPMB partition from the RPMB subsystem
* @rdev: the rpmb device to unregister
*
* This function should be called from the release function of the
* underlying device used when the RPMB device was registered.
*
* Returns: < 0 on failure
*/
int rpmb_dev_unregister(struct rpmb_dev *rdev)
{
if (!rdev)
return -EINVAL;
device_del(&rdev->dev);
rpmb_dev_put(rdev);
return 0;
}
EXPORT_SYMBOL_GPL(rpmb_dev_unregister);
/**
* rpmb_dev_register - register RPMB partition with the RPMB subsystem
* @dev: storage device of the rpmb device
* @descr: RPMB device description
*
* While registering the RPMB partition extract needed device information
* while needed resources are available.
*
* Returns: a pointer to a 'struct rpmb_dev' or an ERR_PTR on failure
*/
struct rpmb_dev *rpmb_dev_register(struct device *dev,
struct rpmb_descr *descr)
{
struct rpmb_dev *rdev;
int ret;
if (!dev || !descr || !descr->route_frames || !descr->dev_id ||
!descr->dev_id_len)
return ERR_PTR(-EINVAL);
rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
if (!rdev)
return ERR_PTR(-ENOMEM);
rdev->descr = *descr;
rdev->descr.dev_id = kmemdup(descr->dev_id, descr->dev_id_len,
GFP_KERNEL);
if (!rdev->descr.dev_id) {
ret = -ENOMEM;
goto err_free_rdev;
}
mutex_lock(&rpmb_mutex);
ret = ida_simple_get(&rpmb_ida, 0, 0, GFP_KERNEL);
mutex_unlock(&rpmb_mutex);
if (ret < 0)
goto err_free_dev_id;
rdev->id = ret;
dev_set_name(&rdev->dev, "rpmb%d", rdev->id);
rdev->dev.class = &rpmb_class;
rdev->dev.parent = dev;
ret = device_register(&rdev->dev);
if (ret) {
put_device(&rdev->dev);
return ERR_PTR(ret);
}
dev_dbg(&rdev->dev, "registered device\n");
return rdev;
err_free_dev_id:
kfree(rdev->descr.dev_id);
err_free_rdev:
kfree(rdev);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(rpmb_dev_register);
static int __init rpmb_init(void)
{
int ret;
ret = class_register(&rpmb_class);
if (ret) {
pr_err("couldn't create class\n");
return ret;
}
ida_init(&rpmb_ida);
return 0;
}
static void __exit rpmb_exit(void)
{
ida_destroy(&rpmb_ida);
class_unregister(&rpmb_class);
}
subsys_initcall(rpmb_init);
module_exit(rpmb_exit);
MODULE_AUTHOR("Jens Wiklander <jens.wiklander@linaro.org>");
MODULE_DESCRIPTION("RPMB class");
MODULE_LICENSE("GPL");

View File

@ -37,6 +37,7 @@ config PWRSEQ_SIMPLE
config MMC_BLOCK config MMC_BLOCK
tristate "MMC block device driver" tristate "MMC block device driver"
depends on BLOCK depends on BLOCK
depends on RPMB || !RPMB
imply IOSCHED_BFQ imply IOSCHED_BFQ
default y default y
help help

View File

@ -33,6 +33,7 @@
#include <linux/cdev.h> #include <linux/cdev.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/string.h>
#include <linux/string_helpers.h> #include <linux/string_helpers.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/capability.h> #include <linux/capability.h>
@ -40,6 +41,7 @@
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/rpmb.h>
#include <linux/mmc/ioctl.h> #include <linux/mmc/ioctl.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
@ -76,6 +78,48 @@ MODULE_ALIAS("mmc:block");
#define MMC_EXTRACT_INDEX_FROM_ARG(x) ((x & 0x00FF0000) >> 16) #define MMC_EXTRACT_INDEX_FROM_ARG(x) ((x & 0x00FF0000) >> 16)
#define MMC_EXTRACT_VALUE_FROM_ARG(x) ((x & 0x0000FF00) >> 8) #define MMC_EXTRACT_VALUE_FROM_ARG(x) ((x & 0x0000FF00) >> 8)
/**
* struct rpmb_frame - rpmb frame as defined by eMMC 5.1 (JESD84-B51)
*
* @stuff : stuff bytes
* @key_mac : The authentication key or the message authentication
* code (MAC) depending on the request/response type.
* The MAC will be delivered in the last (or the only)
* block of data.
* @data : Data to be written or read by signed access.
* @nonce : Random number generated by the host for the requests
* and copied to the response by the RPMB engine.
* @write_counter: Counter value for the total amount of the successful
* authenticated data write requests made by the host.
* @addr : Address of the data to be programmed to or read
* from the RPMB. Address is the serial number of
* the accessed block (half sector 256B).
* @block_count : Number of blocks (half sectors, 256B) requested to be
* read/programmed.
* @result : Includes information about the status of the write counter
* (valid, expired) and result of the access made to the RPMB.
* @req_resp : Defines the type of request and response to/from the memory.
*
* The stuff bytes and big-endian properties are modeled to fit to the spec.
*/
struct rpmb_frame {
u8 stuff[196];
u8 key_mac[32];
u8 data[256];
u8 nonce[16];
__be32 write_counter;
__be16 addr;
__be16 block_count;
__be16 result;
__be16 req_resp;
} __packed;
#define RPMB_PROGRAM_KEY 0x1 /* Program RPMB Authentication Key */
#define RPMB_GET_WRITE_COUNTER 0x2 /* Read RPMB write counter */
#define RPMB_WRITE_DATA 0x3 /* Write data to RPMB partition */
#define RPMB_READ_DATA 0x4 /* Read data from RPMB partition */
#define RPMB_RESULT_READ 0x5 /* Read result request (Internal) */
static DEFINE_MUTEX(block_mutex); static DEFINE_MUTEX(block_mutex);
/* /*
@ -155,6 +199,7 @@ static const struct bus_type mmc_rpmb_bus_type = {
* @id: unique device ID number * @id: unique device ID number
* @part_index: partition index (0 on first) * @part_index: partition index (0 on first)
* @md: parent MMC block device * @md: parent MMC block device
* @rdev: registered RPMB device
* @node: list item, so we can put this device on a list * @node: list item, so we can put this device on a list
*/ */
struct mmc_rpmb_data { struct mmc_rpmb_data {
@ -163,6 +208,7 @@ struct mmc_rpmb_data {
int id; int id;
unsigned int part_index; unsigned int part_index;
struct mmc_blk_data *md; struct mmc_blk_data *md;
struct rpmb_dev *rdev;
struct list_head node; struct list_head node;
}; };
@ -307,10 +353,10 @@ static ssize_t force_ro_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
int ret; int ret;
char *end;
struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev)); struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
unsigned long set = simple_strtoul(buf, &end, 0); unsigned long set;
if (end == buf) {
if (kstrtoul(buf, 0, &set)) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
@ -2484,7 +2530,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
return ERR_PTR(devidx); return ERR_PTR(devidx);
} }
md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); md = kzalloc(sizeof(*md), GFP_KERNEL);
if (!md) { if (!md) {
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
@ -2670,7 +2716,6 @@ static int mmc_rpmb_chrdev_open(struct inode *inode, struct file *filp)
get_device(&rpmb->dev); get_device(&rpmb->dev);
filp->private_data = rpmb; filp->private_data = rpmb;
mmc_blk_get(rpmb->md->disk);
return nonseekable_open(inode, filp); return nonseekable_open(inode, filp);
} }
@ -2680,7 +2725,6 @@ static int mmc_rpmb_chrdev_release(struct inode *inode, struct file *filp)
struct mmc_rpmb_data *rpmb = container_of(inode->i_cdev, struct mmc_rpmb_data *rpmb = container_of(inode->i_cdev,
struct mmc_rpmb_data, chrdev); struct mmc_rpmb_data, chrdev);
mmc_blk_put(rpmb->md);
put_device(&rpmb->dev); put_device(&rpmb->dev);
return 0; return 0;
@ -2701,10 +2745,165 @@ static void mmc_blk_rpmb_device_release(struct device *dev)
{ {
struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev); struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev);
rpmb_dev_unregister(rpmb->rdev);
mmc_blk_put(rpmb->md);
ida_free(&mmc_rpmb_ida, rpmb->id); ida_free(&mmc_rpmb_ida, rpmb->id);
kfree(rpmb); kfree(rpmb);
} }
static void free_idata(struct mmc_blk_ioc_data **idata, unsigned int cmd_count)
{
unsigned int n;
for (n = 0; n < cmd_count; n++)
kfree(idata[n]);
kfree(idata);
}
static struct mmc_blk_ioc_data **alloc_idata(struct mmc_rpmb_data *rpmb,
unsigned int cmd_count)
{
struct mmc_blk_ioc_data **idata;
unsigned int n;
idata = kcalloc(cmd_count, sizeof(*idata), GFP_KERNEL);
if (!idata)
return NULL;
for (n = 0; n < cmd_count; n++) {
idata[n] = kcalloc(1, sizeof(**idata), GFP_KERNEL);
if (!idata[n]) {
free_idata(idata, n);
return NULL;
}
idata[n]->rpmb = rpmb;
}
return idata;
}
static void set_idata(struct mmc_blk_ioc_data *idata, u32 opcode,
int write_flag, u8 *buf, unsigned int buf_bytes)
{
/*
* The size of an RPMB frame must match what's expected by the
* hardware.
*/
BUILD_BUG_ON(sizeof(struct rpmb_frame) != 512);
idata->ic.opcode = opcode;
idata->ic.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
idata->ic.write_flag = write_flag;
idata->ic.blksz = sizeof(struct rpmb_frame);
idata->ic.blocks = buf_bytes / idata->ic.blksz;
idata->buf = buf;
idata->buf_bytes = buf_bytes;
}
static int mmc_route_rpmb_frames(struct device *dev, u8 *req,
unsigned int req_len, u8 *resp,
unsigned int resp_len)
{
struct rpmb_frame *frm = (struct rpmb_frame *)req;
struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev);
struct mmc_blk_data *md = rpmb->md;
struct mmc_blk_ioc_data **idata;
struct mmc_queue_req *mq_rq;
unsigned int cmd_count;
struct request *rq;
u16 req_type;
bool write;
int ret;
if (IS_ERR(md->queue.card))
return PTR_ERR(md->queue.card);
if (req_len < sizeof(*frm))
return -EINVAL;
req_type = be16_to_cpu(frm->req_resp);
switch (req_type) {
case RPMB_PROGRAM_KEY:
if (req_len != sizeof(struct rpmb_frame) ||
resp_len != sizeof(struct rpmb_frame))
return -EINVAL;
write = true;
break;
case RPMB_GET_WRITE_COUNTER:
if (req_len != sizeof(struct rpmb_frame) ||
resp_len != sizeof(struct rpmb_frame))
return -EINVAL;
write = false;
break;
case RPMB_WRITE_DATA:
if (req_len % sizeof(struct rpmb_frame) ||
resp_len != sizeof(struct rpmb_frame))
return -EINVAL;
write = true;
break;
case RPMB_READ_DATA:
if (req_len != sizeof(struct rpmb_frame) ||
resp_len % sizeof(struct rpmb_frame))
return -EINVAL;
write = false;
break;
default:
return -EINVAL;
}
if (write)
cmd_count = 3;
else
cmd_count = 2;
idata = alloc_idata(rpmb, cmd_count);
if (!idata)
return -ENOMEM;
if (write) {
struct rpmb_frame *frm = (struct rpmb_frame *)resp;
/* Send write request frame(s) */
set_idata(idata[0], MMC_WRITE_MULTIPLE_BLOCK,
1 | MMC_CMD23_ARG_REL_WR, req, req_len);
/* Send result request frame */
memset(frm, 0, sizeof(*frm));
frm->req_resp = cpu_to_be16(RPMB_RESULT_READ);
set_idata(idata[1], MMC_WRITE_MULTIPLE_BLOCK, 1, resp,
resp_len);
/* Read response frame */
set_idata(idata[2], MMC_READ_MULTIPLE_BLOCK, 0, resp, resp_len);
} else {
/* Send write request frame(s) */
set_idata(idata[0], MMC_WRITE_MULTIPLE_BLOCK, 1, req, req_len);
/* Read response frame */
set_idata(idata[1], MMC_READ_MULTIPLE_BLOCK, 0, resp, resp_len);
}
rq = blk_mq_alloc_request(md->queue.queue, REQ_OP_DRV_OUT, 0);
if (IS_ERR(rq)) {
ret = PTR_ERR(rq);
goto out;
}
mq_rq = req_to_mmc_queue_req(rq);
mq_rq->drv_op = MMC_DRV_OP_IOCTL_RPMB;
mq_rq->drv_op_result = -EIO;
mq_rq->drv_op_data = idata;
mq_rq->ioc_count = cmd_count;
blk_execute_rq(rq, false);
ret = req_to_mmc_queue_req(rq)->drv_op_result;
blk_mq_free_request(rq);
out:
free_idata(idata, cmd_count);
return ret;
}
static int mmc_blk_alloc_rpmb_part(struct mmc_card *card, static int mmc_blk_alloc_rpmb_part(struct mmc_card *card,
struct mmc_blk_data *md, struct mmc_blk_data *md,
unsigned int part_index, unsigned int part_index,
@ -2739,6 +2938,7 @@ static int mmc_blk_alloc_rpmb_part(struct mmc_card *card,
rpmb->dev.release = mmc_blk_rpmb_device_release; rpmb->dev.release = mmc_blk_rpmb_device_release;
device_initialize(&rpmb->dev); device_initialize(&rpmb->dev);
dev_set_drvdata(&rpmb->dev, rpmb); dev_set_drvdata(&rpmb->dev, rpmb);
mmc_blk_get(md->disk);
rpmb->md = md; rpmb->md = md;
cdev_init(&rpmb->chrdev, &mmc_rpmb_fileops); cdev_init(&rpmb->chrdev, &mmc_rpmb_fileops);
@ -3000,6 +3200,42 @@ static void mmc_blk_remove_debugfs(struct mmc_card *card,
#endif /* CONFIG_DEBUG_FS */ #endif /* CONFIG_DEBUG_FS */
static void mmc_blk_rpmb_add(struct mmc_card *card)
{
struct mmc_blk_data *md = dev_get_drvdata(&card->dev);
struct mmc_rpmb_data *rpmb;
struct rpmb_dev *rdev;
unsigned int n;
u32 cid[4];
struct rpmb_descr descr = {
.type = RPMB_TYPE_EMMC,
.route_frames = mmc_route_rpmb_frames,
.reliable_wr_count = card->ext_csd.enhanced_rpmb_supported ?
2 : 32,
.capacity = card->ext_csd.raw_rpmb_size_mult,
.dev_id = (void *)cid,
.dev_id_len = sizeof(cid),
};
/*
* Provice CID as an octet array. The CID needs to be interpreted
* when used as input to derive the RPMB key since some fields
* will change due to firmware updates.
*/
for (n = 0; n < 4; n++)
cid[n] = be32_to_cpu((__force __be32)card->raw_cid[n]);
list_for_each_entry(rpmb, &md->rpmbs, node) {
rdev = rpmb_dev_register(&rpmb->dev, &descr);
if (IS_ERR(rdev)) {
pr_warn("%s: could not register RPMB device\n",
dev_name(&rpmb->dev));
continue;
}
rpmb->rdev = rdev;
}
}
static int mmc_blk_probe(struct mmc_card *card) static int mmc_blk_probe(struct mmc_card *card)
{ {
struct mmc_blk_data *md; struct mmc_blk_data *md;
@ -3045,6 +3281,8 @@ static int mmc_blk_probe(struct mmc_card *card)
pm_runtime_enable(&card->dev); pm_runtime_enable(&card->dev);
} }
mmc_blk_rpmb_add(card);
return 0; return 0;
out: out:

View File

@ -51,20 +51,6 @@ static const unsigned int taac_mant[] = {
35, 40, 45, 50, 55, 60, 70, 80, 35, 40, 45, 50, 55, 60, 70, 80,
}; };
#define UNSTUFF_BITS(resp,start,size) \
({ \
const int __size = size; \
const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \
const int __off = 3 - ((start) / 32); \
const int __shft = (start) & 31; \
u32 __res; \
\
__res = resp[__off] >> __shft; \
if (__size + __shft > 32) \
__res |= resp[__off-1] << ((32 - __shft) % 32); \
__res & __mask; \
})
/* /*
* Given the decoded CSD structure, decode the raw CID to our CID structure. * Given the decoded CSD structure, decode the raw CID to our CID structure.
*/ */
@ -85,36 +71,36 @@ static int mmc_decode_cid(struct mmc_card *card)
switch (card->csd.mmca_vsn) { switch (card->csd.mmca_vsn) {
case 0: /* MMC v1.0 - v1.2 */ case 0: /* MMC v1.0 - v1.2 */
case 1: /* MMC v1.4 */ case 1: /* MMC v1.4 */
card->cid.manfid = UNSTUFF_BITS(resp, 104, 24); card->cid.manfid = unstuff_bits(resp, 104, 24);
card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); card->cid.prod_name[0] = unstuff_bits(resp, 96, 8);
card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); card->cid.prod_name[1] = unstuff_bits(resp, 88, 8);
card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); card->cid.prod_name[2] = unstuff_bits(resp, 80, 8);
card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); card->cid.prod_name[3] = unstuff_bits(resp, 72, 8);
card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); card->cid.prod_name[4] = unstuff_bits(resp, 64, 8);
card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); card->cid.prod_name[5] = unstuff_bits(resp, 56, 8);
card->cid.prod_name[6] = UNSTUFF_BITS(resp, 48, 8); card->cid.prod_name[6] = unstuff_bits(resp, 48, 8);
card->cid.hwrev = UNSTUFF_BITS(resp, 44, 4); card->cid.hwrev = unstuff_bits(resp, 44, 4);
card->cid.fwrev = UNSTUFF_BITS(resp, 40, 4); card->cid.fwrev = unstuff_bits(resp, 40, 4);
card->cid.serial = UNSTUFF_BITS(resp, 16, 24); card->cid.serial = unstuff_bits(resp, 16, 24);
card->cid.month = UNSTUFF_BITS(resp, 12, 4); card->cid.month = unstuff_bits(resp, 12, 4);
card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; card->cid.year = unstuff_bits(resp, 8, 4) + 1997;
break; break;
case 2: /* MMC v2.0 - v2.2 */ case 2: /* MMC v2.0 - v2.2 */
case 3: /* MMC v3.1 - v3.3 */ case 3: /* MMC v3.1 - v3.3 */
case 4: /* MMC v4 */ case 4: /* MMC v4 */
card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); card->cid.manfid = unstuff_bits(resp, 120, 8);
card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); card->cid.oemid = unstuff_bits(resp, 104, 16);
card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); card->cid.prod_name[0] = unstuff_bits(resp, 96, 8);
card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); card->cid.prod_name[1] = unstuff_bits(resp, 88, 8);
card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); card->cid.prod_name[2] = unstuff_bits(resp, 80, 8);
card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); card->cid.prod_name[3] = unstuff_bits(resp, 72, 8);
card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); card->cid.prod_name[4] = unstuff_bits(resp, 64, 8);
card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); card->cid.prod_name[5] = unstuff_bits(resp, 56, 8);
card->cid.prv = UNSTUFF_BITS(resp, 48, 8); card->cid.prv = unstuff_bits(resp, 48, 8);
card->cid.serial = UNSTUFF_BITS(resp, 16, 32); card->cid.serial = unstuff_bits(resp, 16, 32);
card->cid.month = UNSTUFF_BITS(resp, 12, 4); card->cid.month = unstuff_bits(resp, 12, 4);
card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; card->cid.year = unstuff_bits(resp, 8, 4) + 1997;
break; break;
default: default:
@ -161,43 +147,43 @@ static int mmc_decode_csd(struct mmc_card *card)
* v1.2 has extra information in bits 15, 11 and 10. * v1.2 has extra information in bits 15, 11 and 10.
* We also support eMMC v4.4 & v4.41. * We also support eMMC v4.4 & v4.41.
*/ */
csd->structure = UNSTUFF_BITS(resp, 126, 2); csd->structure = unstuff_bits(resp, 126, 2);
if (csd->structure == 0) { if (csd->structure == 0) {
pr_err("%s: unrecognised CSD structure version %d\n", pr_err("%s: unrecognised CSD structure version %d\n",
mmc_hostname(card->host), csd->structure); mmc_hostname(card->host), csd->structure);
return -EINVAL; return -EINVAL;
} }
csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4); csd->mmca_vsn = unstuff_bits(resp, 122, 4);
m = UNSTUFF_BITS(resp, 115, 4); m = unstuff_bits(resp, 115, 4);
e = UNSTUFF_BITS(resp, 112, 3); e = unstuff_bits(resp, 112, 3);
csd->taac_ns = (taac_exp[e] * taac_mant[m] + 9) / 10; csd->taac_ns = (taac_exp[e] * taac_mant[m] + 9) / 10;
csd->taac_clks = UNSTUFF_BITS(resp, 104, 8) * 100; csd->taac_clks = unstuff_bits(resp, 104, 8) * 100;
m = UNSTUFF_BITS(resp, 99, 4); m = unstuff_bits(resp, 99, 4);
e = UNSTUFF_BITS(resp, 96, 3); e = unstuff_bits(resp, 96, 3);
csd->max_dtr = tran_exp[e] * tran_mant[m]; csd->max_dtr = tran_exp[e] * tran_mant[m];
csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); csd->cmdclass = unstuff_bits(resp, 84, 12);
e = UNSTUFF_BITS(resp, 47, 3); e = unstuff_bits(resp, 47, 3);
m = UNSTUFF_BITS(resp, 62, 12); m = unstuff_bits(resp, 62, 12);
csd->capacity = (1 + m) << (e + 2); csd->capacity = (1 + m) << (e + 2);
csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); csd->read_blkbits = unstuff_bits(resp, 80, 4);
csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->read_partial = unstuff_bits(resp, 79, 1);
csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); csd->write_misalign = unstuff_bits(resp, 78, 1);
csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); csd->read_misalign = unstuff_bits(resp, 77, 1);
csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1); csd->dsr_imp = unstuff_bits(resp, 76, 1);
csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->r2w_factor = unstuff_bits(resp, 26, 3);
csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_blkbits = unstuff_bits(resp, 22, 4);
csd->write_partial = UNSTUFF_BITS(resp, 21, 1); csd->write_partial = unstuff_bits(resp, 21, 1);
if (csd->write_blkbits >= 9) { if (csd->write_blkbits >= 9) {
a = UNSTUFF_BITS(resp, 42, 5); a = unstuff_bits(resp, 42, 5);
b = UNSTUFF_BITS(resp, 37, 5); b = unstuff_bits(resp, 37, 5);
csd->erase_size = (a + 1) * (b + 1); csd->erase_size = (a + 1) * (b + 1);
csd->erase_size <<= csd->write_blkbits - 9; csd->erase_size <<= csd->write_blkbits - 9;
csd->wp_grp_size = UNSTUFF_BITS(resp, 32, 5); csd->wp_grp_size = unstuff_bits(resp, 32, 5);
} }
return 0; return 0;

View File

@ -56,5 +56,19 @@ int mmc_cmdq_enable(struct mmc_card *card);
int mmc_cmdq_disable(struct mmc_card *card); int mmc_cmdq_disable(struct mmc_card *card);
int mmc_sanitize(struct mmc_card *card, unsigned int timeout_ms); int mmc_sanitize(struct mmc_card *card, unsigned int timeout_ms);
static inline u32 unstuff_bits(const u32 *resp, int start, int size)
{
const int __size = size;
const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1;
const int __off = 3 - (start / 32);
const int __shft = start & 31;
u32 __res = resp[__off] >> __shft;
if (__size + __shft > 32)
__res |= resp[__off - 1] << ((32 - __shft) % 32);
return __res & __mask;
}
#endif #endif

View File

@ -255,7 +255,9 @@ int mmc_regulator_get_supply(struct mmc_host *mmc)
if (IS_ERR(mmc->supply.vmmc)) { if (IS_ERR(mmc->supply.vmmc)) {
if (PTR_ERR(mmc->supply.vmmc) == -EPROBE_DEFER) if (PTR_ERR(mmc->supply.vmmc) == -EPROBE_DEFER)
return -EPROBE_DEFER; return dev_err_probe(dev, -EPROBE_DEFER,
"vmmc regulator not available\n");
dev_dbg(dev, "No vmmc regulator found\n"); dev_dbg(dev, "No vmmc regulator found\n");
} else { } else {
ret = mmc_regulator_get_ocrmask(mmc->supply.vmmc); ret = mmc_regulator_get_ocrmask(mmc->supply.vmmc);
@ -267,7 +269,9 @@ int mmc_regulator_get_supply(struct mmc_host *mmc)
if (IS_ERR(mmc->supply.vqmmc)) { if (IS_ERR(mmc->supply.vqmmc)) {
if (PTR_ERR(mmc->supply.vqmmc) == -EPROBE_DEFER) if (PTR_ERR(mmc->supply.vqmmc) == -EPROBE_DEFER)
return -EPROBE_DEFER; return dev_err_probe(dev, -EPROBE_DEFER,
"vqmmc regulator not available\n");
dev_dbg(dev, "No vqmmc regulator found\n"); dev_dbg(dev, "No vqmmc regulator found\n");
} }

View File

@ -56,20 +56,6 @@ static const unsigned int sd_au_size[] = {
SZ_16M / 512, (SZ_16M + SZ_8M) / 512, SZ_32M / 512, SZ_64M / 512, SZ_16M / 512, (SZ_16M + SZ_8M) / 512, SZ_32M / 512, SZ_64M / 512,
}; };
#define UNSTUFF_BITS(resp,start,size) \
({ \
const int __size = size; \
const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \
const int __off = 3 - ((start) / 32); \
const int __shft = (start) & 31; \
u32 __res; \
\
__res = resp[__off] >> __shft; \
if (__size + __shft > 32) \
__res |= resp[__off-1] << ((32 - __shft) % 32); \
__res & __mask; \
})
#define SD_POWEROFF_NOTIFY_TIMEOUT_MS 1000 #define SD_POWEROFF_NOTIFY_TIMEOUT_MS 1000
#define SD_WRITE_EXTR_SINGLE_TIMEOUT_MS 1000 #define SD_WRITE_EXTR_SINGLE_TIMEOUT_MS 1000
@ -95,18 +81,18 @@ void mmc_decode_cid(struct mmc_card *card)
* SD doesn't currently have a version field so we will * SD doesn't currently have a version field so we will
* have to assume we can parse this. * have to assume we can parse this.
*/ */
card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); card->cid.manfid = unstuff_bits(resp, 120, 8);
card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); card->cid.oemid = unstuff_bits(resp, 104, 16);
card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); card->cid.prod_name[0] = unstuff_bits(resp, 96, 8);
card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); card->cid.prod_name[1] = unstuff_bits(resp, 88, 8);
card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); card->cid.prod_name[2] = unstuff_bits(resp, 80, 8);
card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); card->cid.prod_name[3] = unstuff_bits(resp, 72, 8);
card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); card->cid.prod_name[4] = unstuff_bits(resp, 64, 8);
card->cid.hwrev = UNSTUFF_BITS(resp, 60, 4); card->cid.hwrev = unstuff_bits(resp, 60, 4);
card->cid.fwrev = UNSTUFF_BITS(resp, 56, 4); card->cid.fwrev = unstuff_bits(resp, 56, 4);
card->cid.serial = UNSTUFF_BITS(resp, 24, 32); card->cid.serial = unstuff_bits(resp, 24, 32);
card->cid.year = UNSTUFF_BITS(resp, 12, 8); card->cid.year = unstuff_bits(resp, 12, 8);
card->cid.month = UNSTUFF_BITS(resp, 8, 4); card->cid.month = unstuff_bits(resp, 8, 4);
card->cid.year += 2000; /* SD cards year offset */ card->cid.year += 2000; /* SD cards year offset */
} }
@ -120,41 +106,41 @@ static int mmc_decode_csd(struct mmc_card *card)
unsigned int e, m, csd_struct; unsigned int e, m, csd_struct;
u32 *resp = card->raw_csd; u32 *resp = card->raw_csd;
csd_struct = UNSTUFF_BITS(resp, 126, 2); csd_struct = unstuff_bits(resp, 126, 2);
switch (csd_struct) { switch (csd_struct) {
case 0: case 0:
m = UNSTUFF_BITS(resp, 115, 4); m = unstuff_bits(resp, 115, 4);
e = UNSTUFF_BITS(resp, 112, 3); e = unstuff_bits(resp, 112, 3);
csd->taac_ns = (taac_exp[e] * taac_mant[m] + 9) / 10; csd->taac_ns = (taac_exp[e] * taac_mant[m] + 9) / 10;
csd->taac_clks = UNSTUFF_BITS(resp, 104, 8) * 100; csd->taac_clks = unstuff_bits(resp, 104, 8) * 100;
m = UNSTUFF_BITS(resp, 99, 4); m = unstuff_bits(resp, 99, 4);
e = UNSTUFF_BITS(resp, 96, 3); e = unstuff_bits(resp, 96, 3);
csd->max_dtr = tran_exp[e] * tran_mant[m]; csd->max_dtr = tran_exp[e] * tran_mant[m];
csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); csd->cmdclass = unstuff_bits(resp, 84, 12);
e = UNSTUFF_BITS(resp, 47, 3); e = unstuff_bits(resp, 47, 3);
m = UNSTUFF_BITS(resp, 62, 12); m = unstuff_bits(resp, 62, 12);
csd->capacity = (1 + m) << (e + 2); csd->capacity = (1 + m) << (e + 2);
csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); csd->read_blkbits = unstuff_bits(resp, 80, 4);
csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->read_partial = unstuff_bits(resp, 79, 1);
csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); csd->write_misalign = unstuff_bits(resp, 78, 1);
csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); csd->read_misalign = unstuff_bits(resp, 77, 1);
csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1); csd->dsr_imp = unstuff_bits(resp, 76, 1);
csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->r2w_factor = unstuff_bits(resp, 26, 3);
csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_blkbits = unstuff_bits(resp, 22, 4);
csd->write_partial = UNSTUFF_BITS(resp, 21, 1); csd->write_partial = unstuff_bits(resp, 21, 1);
if (UNSTUFF_BITS(resp, 46, 1)) { if (unstuff_bits(resp, 46, 1)) {
csd->erase_size = 1; csd->erase_size = 1;
} else if (csd->write_blkbits >= 9) { } else if (csd->write_blkbits >= 9) {
csd->erase_size = UNSTUFF_BITS(resp, 39, 7) + 1; csd->erase_size = unstuff_bits(resp, 39, 7) + 1;
csd->erase_size <<= csd->write_blkbits - 9; csd->erase_size <<= csd->write_blkbits - 9;
} }
if (UNSTUFF_BITS(resp, 13, 1)) if (unstuff_bits(resp, 13, 1))
mmc_card_set_readonly(card); mmc_card_set_readonly(card);
break; break;
case 1: case 1:
@ -169,17 +155,17 @@ static int mmc_decode_csd(struct mmc_card *card)
csd->taac_ns = 0; /* Unused */ csd->taac_ns = 0; /* Unused */
csd->taac_clks = 0; /* Unused */ csd->taac_clks = 0; /* Unused */
m = UNSTUFF_BITS(resp, 99, 4); m = unstuff_bits(resp, 99, 4);
e = UNSTUFF_BITS(resp, 96, 3); e = unstuff_bits(resp, 96, 3);
csd->max_dtr = tran_exp[e] * tran_mant[m]; csd->max_dtr = tran_exp[e] * tran_mant[m];
csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); csd->cmdclass = unstuff_bits(resp, 84, 12);
csd->c_size = UNSTUFF_BITS(resp, 48, 22); csd->c_size = unstuff_bits(resp, 48, 22);
/* SDXC cards have a minimum C_SIZE of 0x00FFFF */ /* SDXC cards have a minimum C_SIZE of 0x00FFFF */
if (csd->c_size >= 0xFFFF) if (csd->c_size >= 0xFFFF)
mmc_card_set_ext_capacity(card); mmc_card_set_ext_capacity(card);
m = UNSTUFF_BITS(resp, 48, 22); m = unstuff_bits(resp, 48, 22);
csd->capacity = (1 + m) << 10; csd->capacity = (1 + m) << 10;
csd->read_blkbits = 9; csd->read_blkbits = 9;
@ -191,7 +177,7 @@ static int mmc_decode_csd(struct mmc_card *card)
csd->write_partial = 0; csd->write_partial = 0;
csd->erase_size = 1; csd->erase_size = 1;
if (UNSTUFF_BITS(resp, 13, 1)) if (unstuff_bits(resp, 13, 1))
mmc_card_set_readonly(card); mmc_card_set_readonly(card);
break; break;
default: default:
@ -217,33 +203,33 @@ static int mmc_decode_scr(struct mmc_card *card)
resp[3] = card->raw_scr[1]; resp[3] = card->raw_scr[1];
resp[2] = card->raw_scr[0]; resp[2] = card->raw_scr[0];
scr_struct = UNSTUFF_BITS(resp, 60, 4); scr_struct = unstuff_bits(resp, 60, 4);
if (scr_struct != 0) { if (scr_struct != 0) {
pr_err("%s: unrecognised SCR structure version %d\n", pr_err("%s: unrecognised SCR structure version %d\n",
mmc_hostname(card->host), scr_struct); mmc_hostname(card->host), scr_struct);
return -EINVAL; return -EINVAL;
} }
scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); scr->sda_vsn = unstuff_bits(resp, 56, 4);
scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); scr->bus_widths = unstuff_bits(resp, 48, 4);
if (scr->sda_vsn == SCR_SPEC_VER_2) if (scr->sda_vsn == SCR_SPEC_VER_2)
/* Check if Physical Layer Spec v3.0 is supported */ /* Check if Physical Layer Spec v3.0 is supported */
scr->sda_spec3 = UNSTUFF_BITS(resp, 47, 1); scr->sda_spec3 = unstuff_bits(resp, 47, 1);
if (scr->sda_spec3) { if (scr->sda_spec3) {
scr->sda_spec4 = UNSTUFF_BITS(resp, 42, 1); scr->sda_spec4 = unstuff_bits(resp, 42, 1);
scr->sda_specx = UNSTUFF_BITS(resp, 38, 4); scr->sda_specx = unstuff_bits(resp, 38, 4);
} }
if (UNSTUFF_BITS(resp, 55, 1)) if (unstuff_bits(resp, 55, 1))
card->erased_byte = 0xFF; card->erased_byte = 0xFF;
else else
card->erased_byte = 0x0; card->erased_byte = 0x0;
if (scr->sda_spec4) if (scr->sda_spec4)
scr->cmds = UNSTUFF_BITS(resp, 32, 4); scr->cmds = unstuff_bits(resp, 32, 4);
else if (scr->sda_spec3) else if (scr->sda_spec3)
scr->cmds = UNSTUFF_BITS(resp, 32, 2); scr->cmds = unstuff_bits(resp, 32, 2);
/* SD Spec says: any SD Card shall set at least bits 0 and 2 */ /* SD Spec says: any SD Card shall set at least bits 0 and 2 */
if (!(scr->bus_widths & SD_SCR_BUS_WIDTH_1) || if (!(scr->bus_widths & SD_SCR_BUS_WIDTH_1) ||
@ -289,17 +275,17 @@ static int mmc_read_ssr(struct mmc_card *card)
kfree(raw_ssr); kfree(raw_ssr);
/* /*
* UNSTUFF_BITS only works with four u32s so we have to offset the * unstuff_bits only works with four u32s so we have to offset the
* bitfield positions accordingly. * bitfield positions accordingly.
*/ */
au = UNSTUFF_BITS(card->raw_ssr, 428 - 384, 4); au = unstuff_bits(card->raw_ssr, 428 - 384, 4);
if (au) { if (au) {
if (au <= 9 || card->scr.sda_spec3) { if (au <= 9 || card->scr.sda_spec3) {
card->ssr.au = sd_au_size[au]; card->ssr.au = sd_au_size[au];
es = UNSTUFF_BITS(card->raw_ssr, 408 - 384, 16); es = unstuff_bits(card->raw_ssr, 408 - 384, 16);
et = UNSTUFF_BITS(card->raw_ssr, 402 - 384, 6); et = unstuff_bits(card->raw_ssr, 402 - 384, 6);
if (es && et) { if (es && et) {
eo = UNSTUFF_BITS(card->raw_ssr, 400 - 384, 2); eo = unstuff_bits(card->raw_ssr, 400 - 384, 2);
card->ssr.erase_timeout = (et * 1000) / es; card->ssr.erase_timeout = (et * 1000) / es;
card->ssr.erase_offset = eo * 1000; card->ssr.erase_offset = eo * 1000;
} }
@ -313,7 +299,7 @@ static int mmc_read_ssr(struct mmc_card *card)
* starting SD5.1 discard is supported if DISCARD_SUPPORT (b313) is set * starting SD5.1 discard is supported if DISCARD_SUPPORT (b313) is set
*/ */
resp[3] = card->raw_ssr[6]; resp[3] = card->raw_ssr[6];
discard_support = UNSTUFF_BITS(resp, 313 - 288, 1); discard_support = unstuff_bits(resp, 313 - 288, 1);
card->erase_arg = (card->scr.sda_specx && discard_support) ? card->erase_arg = (card->scr.sda_specx && discard_support) ?
SD_DISCARD_ARG : SD_ERASE_ARG; SD_DISCARD_ARG : SD_ERASE_ARG;
@ -346,7 +332,7 @@ static int mmc_read_switch(struct mmc_card *card)
* The argument does not matter, as the support bits do not * The argument does not matter, as the support bits do not
* change with the arguments. * change with the arguments.
*/ */
err = mmc_sd_switch(card, 0, 0, 0, status); err = mmc_sd_switch(card, SD_SWITCH_CHECK, 0, 0, status);
if (err) { if (err) {
/* /*
* If the host or the card can't do the switch, * If the host or the card can't do the switch,
@ -402,7 +388,8 @@ int mmc_sd_switch_hs(struct mmc_card *card)
if (!status) if (!status)
return -ENOMEM; return -ENOMEM;
err = mmc_sd_switch(card, 1, 0, HIGH_SPEED_BUS_SPEED, status); err = mmc_sd_switch(card, SD_SWITCH_SET, 0,
HIGH_SPEED_BUS_SPEED, status);
if (err) if (err)
goto out; goto out;
@ -434,7 +421,8 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
card_drv_type, &drv_type); card_drv_type, &drv_type);
if (drive_strength) { if (drive_strength) {
err = mmc_sd_switch(card, 1, 2, drive_strength, status); err = mmc_sd_switch(card, SD_SWITCH_SET, 2,
drive_strength, status);
if (err) if (err)
return err; return err;
if ((status[15] & 0xF) != drive_strength) { if ((status[15] & 0xF) != drive_strength) {
@ -514,7 +502,7 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
return 0; return 0;
} }
err = mmc_sd_switch(card, 1, 0, card->sd_bus_speed, status); err = mmc_sd_switch(card, SD_SWITCH_SET, 0, card->sd_bus_speed, status);
if (err) if (err)
return err; return err;
@ -605,7 +593,8 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
current_limit = SD_SET_CURRENT_LIMIT_200; current_limit = SD_SET_CURRENT_LIMIT_200;
if (current_limit != SD_SET_CURRENT_NO_CHANGE) { if (current_limit != SD_SET_CURRENT_NO_CHANGE) {
err = mmc_sd_switch(card, 1, 3, current_limit, status); err = mmc_sd_switch(card, SD_SWITCH_SET, 3,
current_limit, status);
if (err) if (err)
return err; return err;

View File

@ -336,14 +336,13 @@ int mmc_app_send_scr(struct mmc_card *card)
return 0; return 0;
} }
int mmc_sd_switch(struct mmc_card *card, int mode, int group, int mmc_sd_switch(struct mmc_card *card, bool mode, int group,
u8 value, u8 *resp) u8 value, u8 *resp)
{ {
u32 cmd_args; u32 cmd_args;
/* NOTE: caller guarantees resp is heap-allocated */ /* NOTE: caller guarantees resp is heap-allocated */
mode = !!mode;
value &= 0xF; value &= 0xF;
cmd_args = mode << 31 | 0x00FFFFFF; cmd_args = mode << 31 | 0x00FFFFFF;
cmd_args &= ~(0xF << (group * 4)); cmd_args &= ~(0xF << (group * 4));

View File

@ -252,6 +252,18 @@ config MMC_SDHCI_OF_SPARX5
If unsure, say N. If unsure, say N.
config MMC_SDHCI_OF_MA35D1
tristate "SDHCI OF support for the MA35D1 SDHCI controller"
depends on ARCH_MA35 || COMPILE_TEST
depends on MMC_SDHCI_PLTFM
help
This selects the MA35D1 Secure Digital Host Controller Interface.
The controller supports SD/MMC/SDIO devices.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
config MMC_SDHCI_CADENCE config MMC_SDHCI_CADENCE
tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller" tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller"
depends on MMC_SDHCI_PLTFM depends on MMC_SDHCI_PLTFM

View File

@ -88,6 +88,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
obj-$(CONFIG_MMC_SDHCI_OF_DWCMSHC) += sdhci-of-dwcmshc.o obj-$(CONFIG_MMC_SDHCI_OF_DWCMSHC) += sdhci-of-dwcmshc.o
obj-$(CONFIG_MMC_SDHCI_OF_SPARX5) += sdhci-of-sparx5.o obj-$(CONFIG_MMC_SDHCI_OF_SPARX5) += sdhci-of-sparx5.o
obj-$(CONFIG_MMC_SDHCI_OF_MA35D1) += sdhci-of-ma35d1.o
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
obj-$(CONFIG_MMC_SDHCI_NPCM) += sdhci-npcm.o obj-$(CONFIG_MMC_SDHCI_NPCM) += sdhci-npcm.o

View File

@ -33,6 +33,11 @@ struct cqhci_slot {
#define CQHCI_HOST_OTHER BIT(4) #define CQHCI_HOST_OTHER BIT(4)
}; };
static bool cqhci_halted(struct cqhci_host *cq_host)
{
return cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT;
}
static inline u8 *get_desc(struct cqhci_host *cq_host, u8 tag) static inline u8 *get_desc(struct cqhci_host *cq_host, u8 tag)
{ {
return cq_host->desc_base + (tag * cq_host->slot_sz); return cq_host->desc_base + (tag * cq_host->slot_sz);
@ -282,7 +287,7 @@ static void __cqhci_enable(struct cqhci_host *cq_host)
cqhci_writel(cq_host, cqcfg, CQHCI_CFG); cqhci_writel(cq_host, cqcfg, CQHCI_CFG);
if (cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT) if (cqhci_halted(cq_host))
cqhci_writel(cq_host, 0, CQHCI_CTL); cqhci_writel(cq_host, 0, CQHCI_CTL);
mmc->cqe_on = true; mmc->cqe_on = true;
@ -617,7 +622,7 @@ static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
cqhci_writel(cq_host, 0, CQHCI_CTL); cqhci_writel(cq_host, 0, CQHCI_CTL);
mmc->cqe_on = true; mmc->cqe_on = true;
pr_debug("%s: cqhci: CQE on\n", mmc_hostname(mmc)); pr_debug("%s: cqhci: CQE on\n", mmc_hostname(mmc));
if (cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT) { if (cqhci_halted(cq_host)) {
pr_err("%s: cqhci: CQE failed to exit halt state\n", pr_err("%s: cqhci: CQE failed to exit halt state\n",
mmc_hostname(mmc)); mmc_hostname(mmc));
} }
@ -953,11 +958,6 @@ static bool cqhci_clear_all_tasks(struct mmc_host *mmc, unsigned int timeout)
return ret; return ret;
} }
static bool cqhci_halted(struct cqhci_host *cq_host)
{
return cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT;
}
static bool cqhci_halt(struct mmc_host *mmc, unsigned int timeout) static bool cqhci_halt(struct mmc_host *mmc, unsigned int timeout)
{ {
struct cqhci_host *cq_host = mmc->cqe_private; struct cqhci_host *cq_host = mmc->cqe_private;

View File

@ -15,7 +15,17 @@
#include "dw_mmc.h" #include "dw_mmc.h"
#include "dw_mmc-pltfm.h" #include "dw_mmc-pltfm.h"
#define RK3288_CLKGEN_DIV 2 #define RK3288_CLKGEN_DIV 2
#define SDMMC_TIMING_CON0 0x130
#define SDMMC_TIMING_CON1 0x134
#define ROCKCHIP_MMC_DELAY_SEL BIT(10)
#define ROCKCHIP_MMC_DEGREE_MASK 0x3
#define ROCKCHIP_MMC_DEGREE_OFFSET 1
#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2
#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
#define HIWORD_UPDATE(val, mask, shift) \
((val) << (shift) | (mask) << ((shift) + 16))
static const unsigned int freqs[] = { 100000, 200000, 300000, 400000 }; static const unsigned int freqs[] = { 100000, 200000, 300000, 400000 };
@ -24,8 +34,143 @@ struct dw_mci_rockchip_priv_data {
struct clk *sample_clk; struct clk *sample_clk;
int default_sample_phase; int default_sample_phase;
int num_phases; int num_phases;
bool internal_phase;
}; };
/*
* Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to
* simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg.
*/
static int rockchip_mmc_get_internal_phase(struct dw_mci *host, bool sample)
{
unsigned long rate = clk_get_rate(host->ciu_clk);
u32 raw_value;
u16 degrees;
u32 delay_num = 0;
/* Constant signal, no measurable phase shift */
if (!rate)
return 0;
if (sample)
raw_value = mci_readl(host, TIMING_CON1);
else
raw_value = mci_readl(host, TIMING_CON0);
raw_value >>= ROCKCHIP_MMC_DEGREE_OFFSET;
degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
if (raw_value & ROCKCHIP_MMC_DELAY_SEL) {
/* degrees/delaynum * 1000000 */
unsigned long factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) *
36 * (rate / 10000);
delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
degrees += DIV_ROUND_CLOSEST(delay_num * factor, 1000000);
}
return degrees % 360;
}
static int rockchip_mmc_get_phase(struct dw_mci *host, bool sample)
{
struct dw_mci_rockchip_priv_data *priv = host->priv;
struct clk *clock = sample ? priv->sample_clk : priv->drv_clk;
if (priv->internal_phase)
return rockchip_mmc_get_internal_phase(host, sample);
else
return clk_get_phase(clock);
}
static int rockchip_mmc_set_internal_phase(struct dw_mci *host, bool sample, int degrees)
{
unsigned long rate = clk_get_rate(host->ciu_clk);
u8 nineties, remainder;
u8 delay_num;
u32 raw_value;
u32 delay;
/*
* The below calculation is based on the output clock from
* MMC host to the card, which expects the phase clock inherits
* the clock rate from its parent, namely the output clock
* provider of MMC host. However, things may go wrong if
* (1) It is orphan.
* (2) It is assigned to the wrong parent.
*
* This check help debug the case (1), which seems to be the
* most likely problem we often face and which makes it difficult
* for people to debug unstable mmc tuning results.
*/
if (!rate) {
dev_err(host->dev, "%s: invalid clk rate\n", __func__);
return -EINVAL;
}
nineties = degrees / 90;
remainder = (degrees % 90);
/*
* Due to the inexact nature of the "fine" delay, we might
* actually go non-monotonic. We don't go _too_ monotonic
* though, so we should be OK. Here are options of how we may
* work:
*
* Ideally we end up with:
* 1.0, 2.0, ..., 69.0, 70.0, ..., 89.0, 90.0
*
* On one extreme (if delay is actually 44ps):
* .73, 1.5, ..., 50.6, 51.3, ..., 65.3, 90.0
* The other (if delay is actually 77ps):
* 1.3, 2.6, ..., 88.6. 89.8, ..., 114.0, 90
*
* It's possible we might make a delay that is up to 25
* degrees off from what we think we're making. That's OK
* though because we should be REALLY far from any bad range.
*/
/*
* Convert to delay; do a little extra work to make sure we
* don't overflow 32-bit / 64-bit numbers.
*/
delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */
delay *= remainder;
delay = DIV_ROUND_CLOSEST(delay,
(rate / 1000) * 36 *
(ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10));
delay_num = (u8) min_t(u32, delay, 255);
raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0;
raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
raw_value |= nineties;
if (sample)
mci_writel(host, TIMING_CON1, HIWORD_UPDATE(raw_value, 0x07ff, 1));
else
mci_writel(host, TIMING_CON0, HIWORD_UPDATE(raw_value, 0x07ff, 1));
dev_dbg(host->dev, "set %s_phase(%d) delay_nums=%u actual_degrees=%d\n",
sample ? "sample" : "drv", degrees, delay_num,
rockchip_mmc_get_phase(host, sample)
);
return 0;
}
static int rockchip_mmc_set_phase(struct dw_mci *host, bool sample, int degrees)
{
struct dw_mci_rockchip_priv_data *priv = host->priv;
struct clk *clock = sample ? priv->sample_clk : priv->drv_clk;
if (priv->internal_phase)
return rockchip_mmc_set_internal_phase(host, sample, degrees);
else
return clk_set_phase(clock, degrees);
}
static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{ {
struct dw_mci_rockchip_priv_data *priv = host->priv; struct dw_mci_rockchip_priv_data *priv = host->priv;
@ -64,7 +209,7 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
/* Make sure we use phases which we can enumerate with */ /* Make sure we use phases which we can enumerate with */
if (!IS_ERR(priv->sample_clk) && ios->timing <= MMC_TIMING_SD_HS) if (!IS_ERR(priv->sample_clk) && ios->timing <= MMC_TIMING_SD_HS)
clk_set_phase(priv->sample_clk, priv->default_sample_phase); rockchip_mmc_set_phase(host, true, priv->default_sample_phase);
/* /*
* Set the drive phase offset based on speed mode to achieve hold times. * Set the drive phase offset based on speed mode to achieve hold times.
@ -127,7 +272,7 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
break; break;
} }
clk_set_phase(priv->drv_clk, phase); rockchip_mmc_set_phase(host, false, phase);
} }
} }
@ -151,6 +296,7 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
int longest_range_len = -1; int longest_range_len = -1;
int longest_range = -1; int longest_range = -1;
int middle_phase; int middle_phase;
int phase;
if (IS_ERR(priv->sample_clk)) { if (IS_ERR(priv->sample_clk)) {
dev_err(host->dev, "Tuning clock (sample_clk) not defined.\n"); dev_err(host->dev, "Tuning clock (sample_clk) not defined.\n");
@ -164,8 +310,10 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
/* Try each phase and extract good ranges */ /* Try each phase and extract good ranges */
for (i = 0; i < priv->num_phases; ) { for (i = 0; i < priv->num_phases; ) {
clk_set_phase(priv->sample_clk, rockchip_mmc_set_phase(host, true,
TUNING_ITERATION_TO_PHASE(i, priv->num_phases)); TUNING_ITERATION_TO_PHASE(
i,
priv->num_phases));
v = !mmc_send_tuning(mmc, opcode, NULL); v = !mmc_send_tuning(mmc, opcode, NULL);
@ -211,7 +359,8 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
} }
if (ranges[0].start == 0 && ranges[0].end == priv->num_phases - 1) { if (ranges[0].start == 0 && ranges[0].end == priv->num_phases - 1) {
clk_set_phase(priv->sample_clk, priv->default_sample_phase); rockchip_mmc_set_phase(host, true, priv->default_sample_phase);
dev_info(host->dev, "All phases work, using default phase %d.", dev_info(host->dev, "All phases work, using default phase %d.",
priv->default_sample_phase); priv->default_sample_phase);
goto free; goto free;
@ -248,19 +397,17 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
middle_phase = ranges[longest_range].start + longest_range_len / 2; middle_phase = ranges[longest_range].start + longest_range_len / 2;
middle_phase %= priv->num_phases; middle_phase %= priv->num_phases;
dev_info(host->dev, "Successfully tuned phase to %d\n", phase = TUNING_ITERATION_TO_PHASE(middle_phase, priv->num_phases);
TUNING_ITERATION_TO_PHASE(middle_phase, priv->num_phases)); dev_info(host->dev, "Successfully tuned phase to %d\n", phase);
clk_set_phase(priv->sample_clk, rockchip_mmc_set_phase(host, true, phase);
TUNING_ITERATION_TO_PHASE(middle_phase,
priv->num_phases));
free: free:
kfree(ranges); kfree(ranges);
return ret; return ret;
} }
static int dw_mci_rk3288_parse_dt(struct dw_mci *host) static int dw_mci_common_parse_dt(struct dw_mci *host)
{ {
struct device_node *np = host->dev->of_node; struct device_node *np = host->dev->of_node;
struct dw_mci_rockchip_priv_data *priv; struct dw_mci_rockchip_priv_data *priv;
@ -270,13 +417,29 @@ static int dw_mci_rk3288_parse_dt(struct dw_mci *host)
return -ENOMEM; return -ENOMEM;
if (of_property_read_u32(np, "rockchip,desired-num-phases", if (of_property_read_u32(np, "rockchip,desired-num-phases",
&priv->num_phases)) &priv->num_phases))
priv->num_phases = 360; priv->num_phases = 360;
if (of_property_read_u32(np, "rockchip,default-sample-phase", if (of_property_read_u32(np, "rockchip,default-sample-phase",
&priv->default_sample_phase)) &priv->default_sample_phase))
priv->default_sample_phase = 0; priv->default_sample_phase = 0;
host->priv = priv;
return 0;
}
static int dw_mci_rk3288_parse_dt(struct dw_mci *host)
{
struct dw_mci_rockchip_priv_data *priv;
int err;
err = dw_mci_common_parse_dt(host);
if (err)
return err;
priv = host->priv;
priv->drv_clk = devm_clk_get(host->dev, "ciu-drive"); priv->drv_clk = devm_clk_get(host->dev, "ciu-drive");
if (IS_ERR(priv->drv_clk)) if (IS_ERR(priv->drv_clk))
dev_dbg(host->dev, "ciu-drive not available\n"); dev_dbg(host->dev, "ciu-drive not available\n");
@ -285,7 +448,21 @@ static int dw_mci_rk3288_parse_dt(struct dw_mci *host)
if (IS_ERR(priv->sample_clk)) if (IS_ERR(priv->sample_clk))
dev_dbg(host->dev, "ciu-sample not available\n"); dev_dbg(host->dev, "ciu-sample not available\n");
host->priv = priv; priv->internal_phase = false;
return 0;
}
static int dw_mci_rk3576_parse_dt(struct dw_mci *host)
{
struct dw_mci_rockchip_priv_data *priv;
int err = dw_mci_common_parse_dt(host);
if (err)
return err;
priv = host->priv;
priv->internal_phase = true;
return 0; return 0;
} }
@ -331,11 +508,21 @@ static const struct dw_mci_drv_data rk3288_drv_data = {
.init = dw_mci_rockchip_init, .init = dw_mci_rockchip_init,
}; };
static const struct dw_mci_drv_data rk3576_drv_data = {
.common_caps = MMC_CAP_CMD23,
.set_ios = dw_mci_rk3288_set_ios,
.execute_tuning = dw_mci_rk3288_execute_tuning,
.parse_dt = dw_mci_rk3576_parse_dt,
.init = dw_mci_rockchip_init,
};
static const struct of_device_id dw_mci_rockchip_match[] = { static const struct of_device_id dw_mci_rockchip_match[] = {
{ .compatible = "rockchip,rk2928-dw-mshc", { .compatible = "rockchip,rk2928-dw-mshc",
.data = &rk2928_drv_data }, .data = &rk2928_drv_data },
{ .compatible = "rockchip,rk3288-dw-mshc", { .compatible = "rockchip,rk3288-dw-mshc",
.data = &rk3288_drv_data }, .data = &rk3288_drv_data },
{ .compatible = "rockchip,rk3576-dw-mshc",
.data = &rk3576_drv_data },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, dw_mci_rockchip_match); MODULE_DEVICE_TABLE(of, dw_mci_rockchip_match);

View File

@ -795,14 +795,13 @@ static void msdc_unprepare_data(struct msdc_host *host, struct mmc_data *data)
static u64 msdc_timeout_cal(struct msdc_host *host, u64 ns, u64 clks) static u64 msdc_timeout_cal(struct msdc_host *host, u64 ns, u64 clks)
{ {
struct mmc_host *mmc = mmc_from_priv(host); struct mmc_host *mmc = mmc_from_priv(host);
u64 timeout, clk_ns; u64 timeout;
u32 mode = 0; u32 clk_ns, mode = 0;
if (mmc->actual_clock == 0) { if (mmc->actual_clock == 0) {
timeout = 0; timeout = 0;
} else { } else {
clk_ns = 1000000000ULL; clk_ns = 1000000000U / mmc->actual_clock;
do_div(clk_ns, mmc->actual_clock);
timeout = ns + clk_ns - 1; timeout = ns + clk_ns - 1;
do_div(timeout, clk_ns); do_div(timeout, clk_ns);
timeout += clks; timeout += clks;
@ -831,7 +830,7 @@ static void msdc_set_timeout(struct msdc_host *host, u64 ns, u64 clks)
timeout = msdc_timeout_cal(host, ns, clks); timeout = msdc_timeout_cal(host, ns, clks);
sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC,
(u32)(timeout > 255 ? 255 : timeout)); min_t(u32, timeout, 255));
} }
static void msdc_set_busy_timeout(struct msdc_host *host, u64 ns, u64 clks) static void msdc_set_busy_timeout(struct msdc_host *host, u64 ns, u64 clks)
@ -840,7 +839,7 @@ static void msdc_set_busy_timeout(struct msdc_host *host, u64 ns, u64 clks)
timeout = msdc_timeout_cal(host, ns, clks); timeout = msdc_timeout_cal(host, ns, clks);
sdr_set_field(host->base + SDC_CFG, SDC_CFG_WRDTOC, sdr_set_field(host->base + SDC_CFG, SDC_CFG_WRDTOC,
(u32)(timeout > 8191 ? 8191 : timeout)); min_t(u32, timeout, 8191));
} }
static void msdc_gate_clock(struct msdc_host *host) static void msdc_gate_clock(struct msdc_host *host)

View File

@ -285,6 +285,7 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
{ .compatible = "renesas,sdhi-r8a77990", .data = &of_r8a77990_compatible, }, { .compatible = "renesas,sdhi-r8a77990", .data = &of_r8a77990_compatible, },
{ .compatible = "renesas,sdhi-r8a77995", .data = &of_rcar_gen3_nohs400_compatible, }, { .compatible = "renesas,sdhi-r8a77995", .data = &of_rcar_gen3_nohs400_compatible, },
{ .compatible = "renesas,sdhi-r9a09g011", .data = &of_rzg2l_compatible, }, { .compatible = "renesas,sdhi-r9a09g011", .data = &of_rzg2l_compatible, },
{ .compatible = "renesas,sdhi-r9a09g057", .data = &of_rzg2l_compatible, },
{ .compatible = "renesas,rzg2l-sdhi", .data = &of_rzg2l_compatible, }, { .compatible = "renesas,rzg2l-sdhi", .data = &of_rzg2l_compatible, },
{ .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, }, { .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, },
{ .compatible = "renesas,rcar-gen4-sdhi", .data = &of_rcar_gen3_compatible, }, { .compatible = "renesas,rcar-gen4-sdhi", .data = &of_rcar_gen3_compatible, },

View File

@ -8,6 +8,7 @@
*/ */
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/arm-smccc.h>
#include <linux/bitfield.h> #include <linux/bitfield.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
@ -108,18 +109,20 @@
#define DLL_LOCK_WO_TMOUT(x) \ #define DLL_LOCK_WO_TMOUT(x) \
((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \ ((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \
(((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0)) (((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0))
#define RK35xx_MAX_CLKS 3
/* PHY register area pointer */ /* PHY register area pointer */
#define DWC_MSHC_PTR_PHY_R 0x300 #define DWC_MSHC_PTR_PHY_R 0x300
/* PHY general configuration */ /* PHY general configuration */
#define PHY_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x00) #define PHY_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x00)
#define PHY_CNFG_RSTN_DEASSERT 0x1 /* Deassert PHY reset */ #define PHY_CNFG_RSTN_DEASSERT 0x1 /* Deassert PHY reset */
#define PHY_CNFG_PAD_SP_MASK GENMASK(19, 16) /* bits [19:16] */ #define PHY_CNFG_PHY_PWRGOOD_MASK BIT_MASK(1) /* bit [1] */
#define PHY_CNFG_PAD_SP 0x0c /* PMOS TX drive strength */ #define PHY_CNFG_PAD_SP_MASK GENMASK(19, 16) /* bits [19:16] */
#define PHY_CNFG_PAD_SN_MASK GENMASK(23, 20) /* bits [23:20] */ #define PHY_CNFG_PAD_SP 0x0c /* PMOS TX drive strength */
#define PHY_CNFG_PAD_SN 0x0c /* NMOS TX drive strength */ #define PHY_CNFG_PAD_SP_SG2042 0x09 /* PMOS TX drive strength for SG2042 */
#define PHY_CNFG_PAD_SN_MASK GENMASK(23, 20) /* bits [23:20] */
#define PHY_CNFG_PAD_SN 0x0c /* NMOS TX drive strength */
#define PHY_CNFG_PAD_SN_SG2042 0x08 /* NMOS TX drive strength for SG2042 */
/* PHY command/response pad settings */ /* PHY command/response pad settings */
#define PHY_CMDPAD_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x04) #define PHY_CMDPAD_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x04)
@ -148,10 +151,12 @@
#define PHY_PAD_TXSLEW_CTRL_P 0x3 /* Slew control for P-Type pad TX */ #define PHY_PAD_TXSLEW_CTRL_P 0x3 /* Slew control for P-Type pad TX */
#define PHY_PAD_TXSLEW_CTRL_N_MASK GENMASK(12, 9) /* bits [12:9] */ #define PHY_PAD_TXSLEW_CTRL_N_MASK GENMASK(12, 9) /* bits [12:9] */
#define PHY_PAD_TXSLEW_CTRL_N 0x3 /* Slew control for N-Type pad TX */ #define PHY_PAD_TXSLEW_CTRL_N 0x3 /* Slew control for N-Type pad TX */
#define PHY_PAD_TXSLEW_CTRL_N_SG2042 0x2 /* Slew control for N-Type pad TX for SG2042 */
/* PHY CLK delay line settings */ /* PHY CLK delay line settings */
#define PHY_SDCLKDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x1d) #define PHY_SDCLKDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x1d)
#define PHY_SDCLKDL_CNFG_UPDATE BIT(4) /* set before writing to SDCLKDL_DC */ #define PHY_SDCLKDL_CNFG_EXTDLY_EN BIT(0)
#define PHY_SDCLKDL_CNFG_UPDATE BIT(4) /* set before writing to SDCLKDL_DC */
/* PHY CLK delay line delay code */ /* PHY CLK delay line delay code */
#define PHY_SDCLKDL_DC_R (DWC_MSHC_PTR_PHY_R + 0x1e) #define PHY_SDCLKDL_DC_R (DWC_MSHC_PTR_PHY_R + 0x1e)
@ -159,10 +164,14 @@
#define PHY_SDCLKDL_DC_DEFAULT 0x32 /* default delay code */ #define PHY_SDCLKDL_DC_DEFAULT 0x32 /* default delay code */
#define PHY_SDCLKDL_DC_HS400 0x18 /* delay code for HS400 mode */ #define PHY_SDCLKDL_DC_HS400 0x18 /* delay code for HS400 mode */
#define PHY_SMPLDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x20)
#define PHY_SMPLDL_CNFG_BYPASS_EN BIT(1)
/* PHY drift_cclk_rx delay line configuration setting */ /* PHY drift_cclk_rx delay line configuration setting */
#define PHY_ATDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x21) #define PHY_ATDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x21)
#define PHY_ATDL_CNFG_INPSEL_MASK GENMASK(3, 2) /* bits [3:2] */ #define PHY_ATDL_CNFG_INPSEL_MASK GENMASK(3, 2) /* bits [3:2] */
#define PHY_ATDL_CNFG_INPSEL 0x3 /* delay line input source */ #define PHY_ATDL_CNFG_INPSEL 0x3 /* delay line input source */
#define PHY_ATDL_CNFG_INPSEL_SG2042 0x2 /* delay line input source for SG2042 */
/* PHY DLL control settings */ /* PHY DLL control settings */
#define PHY_DLL_CTRL_R (DWC_MSHC_PTR_PHY_R + 0x24) #define PHY_DLL_CTRL_R (DWC_MSHC_PTR_PHY_R + 0x24)
@ -193,29 +202,69 @@
SDHCI_TRNS_BLK_CNT_EN | \ SDHCI_TRNS_BLK_CNT_EN | \
SDHCI_TRNS_DMA) SDHCI_TRNS_DMA)
/* SMC call for BlueField-3 eMMC RST_N */
#define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007
enum dwcmshc_rk_type { enum dwcmshc_rk_type {
DWCMSHC_RK3568, DWCMSHC_RK3568,
DWCMSHC_RK3588, DWCMSHC_RK3588,
}; };
struct rk35xx_priv { struct rk35xx_priv {
/* Rockchip specified optional clocks */
struct clk_bulk_data rockchip_clks[RK35xx_MAX_CLKS];
struct reset_control *reset; struct reset_control *reset;
enum dwcmshc_rk_type devtype; enum dwcmshc_rk_type devtype;
u8 txclk_tapnum; u8 txclk_tapnum;
}; };
#define DWCMSHC_MAX_OTHER_CLKS 3
struct dwcmshc_priv { struct dwcmshc_priv {
struct clk *bus_clk; struct clk *bus_clk;
int vendor_specific_area1; /* P_VENDOR_SPECIFIC_AREA1 reg */ int vendor_specific_area1; /* P_VENDOR_SPECIFIC_AREA1 reg */
int vendor_specific_area2; /* P_VENDOR_SPECIFIC_AREA2 reg */ int vendor_specific_area2; /* P_VENDOR_SPECIFIC_AREA2 reg */
int num_other_clks;
struct clk_bulk_data other_clks[DWCMSHC_MAX_OTHER_CLKS];
void *priv; /* pointer to SoC private stuff */ void *priv; /* pointer to SoC private stuff */
u16 delay_line; u16 delay_line;
u16 flags; u16 flags;
}; };
struct dwcmshc_pltfm_data {
const struct sdhci_pltfm_data pdata;
int (*init)(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
};
static int dwcmshc_get_enable_other_clks(struct device *dev,
struct dwcmshc_priv *priv,
int num_clks,
const char * const clk_ids[])
{
int err;
if (num_clks > DWCMSHC_MAX_OTHER_CLKS)
return -EINVAL;
for (int i = 0; i < num_clks; i++)
priv->other_clks[i].id = clk_ids[i];
err = devm_clk_bulk_get_optional(dev, num_clks, priv->other_clks);
if (err) {
dev_err(dev, "failed to get clocks %d\n", err);
return err;
}
err = clk_bulk_prepare_enable(num_clks, priv->other_clks);
if (err)
dev_err(dev, "failed to enable clocks %d\n", err);
priv->num_other_clks = num_clks;
return err;
}
/* /*
* If DMA addr spans 128MB boundary, we split the DMA transfer into two * If DMA addr spans 128MB boundary, we split the DMA transfer into two
* so that each DMA transfer doesn't exceed the boundary. * so that each DMA transfer doesn't exceed the boundary.
@ -681,6 +730,63 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
sdhci_reset(host, mask); sdhci_reset(host, mask);
} }
static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host,
struct dwcmshc_priv *dwc_priv)
{
static const char * const clk_ids[] = {"axi", "block", "timer"};
struct rk35xx_priv *priv;
int err;
priv = devm_kzalloc(dev, sizeof(struct rk35xx_priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
if (of_device_is_compatible(dev->of_node, "rockchip,rk3588-dwcmshc"))
priv->devtype = DWCMSHC_RK3588;
else
priv->devtype = DWCMSHC_RK3568;
priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc));
if (IS_ERR(priv->reset)) {
err = PTR_ERR(priv->reset);
dev_err(mmc_dev(host->mmc), "failed to get reset control %d\n", err);
return err;
}
err = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv,
ARRAY_SIZE(clk_ids), clk_ids);
if (err)
return err;
if (of_property_read_u8(mmc_dev(host->mmc)->of_node, "rockchip,txclk-tapnum",
&priv->txclk_tapnum))
priv->txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT;
/* Disable cmd conflict check */
sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3);
/* Reset previous settings */
sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK);
sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_STRBIN);
dwc_priv->priv = priv;
return 0;
}
static void dwcmshc_rk35xx_postinit(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
{
/*
* Don't support highspeed bus mode with low clk speed as we
* cannot use DLL for this condition.
*/
if (host->mmc->f_max <= 52000000) {
dev_info(mmc_dev(host->mmc), "Disabling HS200/HS400, frequency too low (%d)\n",
host->mmc->f_max);
host->mmc->caps2 &= ~(MMC_CAP2_HS200 | MMC_CAP2_HS400);
host->mmc->caps &= ~(MMC_CAP_3_3V_DDR | MMC_CAP_1_8V_DDR);
}
}
static int th1520_execute_tuning(struct sdhci_host *host, u32 opcode) static int th1520_execute_tuning(struct sdhci_host *host, u32 opcode)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -755,6 +861,35 @@ static void th1520_sdhci_reset(struct sdhci_host *host, u8 mask)
} }
} }
static int th1520_init(struct device *dev,
struct sdhci_host *host,
struct dwcmshc_priv *dwc_priv)
{
dwc_priv->delay_line = PHY_SDCLKDL_DC_DEFAULT;
if (device_property_read_bool(dev, "mmc-ddr-1_8v") ||
device_property_read_bool(dev, "mmc-hs200-1_8v") ||
device_property_read_bool(dev, "mmc-hs400-1_8v"))
dwc_priv->flags |= FLAG_IO_FIXED_1V8;
else
dwc_priv->flags &= ~FLAG_IO_FIXED_1V8;
/*
* start_signal_voltage_switch() will try 3.3V first
* then 1.8V. Use SDHCI_SIGNALING_180 rather than
* SDHCI_SIGNALING_330 to avoid setting voltage to 3.3V
* in sdhci_start_signal_voltage_switch().
*/
if (dwc_priv->flags & FLAG_IO_FIXED_1V8) {
host->flags &= ~SDHCI_SIGNALING_330;
host->flags |= SDHCI_SIGNALING_180;
}
sdhci_enable_v4_mode(host);
return 0;
}
static void cv18xx_sdhci_reset(struct sdhci_host *host, u8 mask) static void cv18xx_sdhci_reset(struct sdhci_host *host, u8 mask)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -891,6 +1026,85 @@ static int cv18xx_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
return ret; return ret;
} }
static inline void sg2042_sdhci_phy_init(struct sdhci_host *host)
{
u32 val;
/* Asset phy reset & set tx drive strength */
val = sdhci_readl(host, PHY_CNFG_R);
val &= ~PHY_CNFG_RSTN_DEASSERT;
val |= FIELD_PREP(PHY_CNFG_PHY_PWRGOOD_MASK, 1);
val |= FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP_SG2042);
val |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, PHY_CNFG_PAD_SN_SG2042);
sdhci_writel(host, val, PHY_CNFG_R);
/* Configure phy pads */
val = PHY_PAD_RXSEL_3V3;
val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
val = PHY_PAD_RXSEL_3V3;
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
val = PHY_PAD_RXSEL_3V3;
val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
/* Configure delay line */
/* Enable fixed delay */
sdhci_writeb(host, PHY_SDCLKDL_CNFG_EXTDLY_EN, PHY_SDCLKDL_CNFG_R);
/*
* Set delay line.
* Its recommended that bit UPDATE_DC[4] is 1 when SDCLKDL_DC is being written.
* Ensure UPDATE_DC[4] is '0' when not updating code.
*/
val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R);
val |= PHY_SDCLKDL_CNFG_UPDATE;
sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
/* Add 10 * 70ps = 0.7ns for output delay */
sdhci_writeb(host, 10, PHY_SDCLKDL_DC_R);
val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R);
val &= ~(PHY_SDCLKDL_CNFG_UPDATE);
sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
/* Set SMPLDL_CNFG, Bypass */
sdhci_writeb(host, PHY_SMPLDL_CNFG_BYPASS_EN, PHY_SMPLDL_CNFG_R);
/* Set ATDL_CNFG, tuning clk not use for init */
val = FIELD_PREP(PHY_ATDL_CNFG_INPSEL_MASK, PHY_ATDL_CNFG_INPSEL_SG2042);
sdhci_writeb(host, val, PHY_ATDL_CNFG_R);
/* Deasset phy reset */
val = sdhci_readl(host, PHY_CNFG_R);
val |= PHY_CNFG_RSTN_DEASSERT;
sdhci_writel(host, val, PHY_CNFG_R);
}
static void sg2042_sdhci_reset(struct sdhci_host *host, u8 mask)
{
sdhci_reset(host, mask);
if (mask & SDHCI_RESET_ALL)
sg2042_sdhci_phy_init(host);
}
static int sg2042_init(struct device *dev, struct sdhci_host *host,
struct dwcmshc_priv *dwc_priv)
{
static const char * const clk_ids[] = {"timer"};
return dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv,
ARRAY_SIZE(clk_ids), clk_ids);
}
static const struct sdhci_ops sdhci_dwcmshc_ops = { static const struct sdhci_ops sdhci_dwcmshc_ops = {
.set_clock = sdhci_set_clock, .set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width, .set_bus_width = sdhci_set_bus_width,
@ -901,6 +1115,29 @@ static const struct sdhci_ops sdhci_dwcmshc_ops = {
.irq = dwcmshc_cqe_irq_handler, .irq = dwcmshc_cqe_irq_handler,
}; };
#ifdef CONFIG_ACPI
static void dwcmshc_bf3_hw_reset(struct sdhci_host *host)
{
struct arm_smccc_res res = { 0 };
arm_smccc_smc(BLUEFIELD_SMC_SET_EMMC_RST_N, 0, 0, 0, 0, 0, 0, 0, &res);
if (res.a0)
pr_err("%s: RST_N failed.\n", mmc_hostname(host->mmc));
}
static const struct sdhci_ops sdhci_dwcmshc_bf3_ops = {
.set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width,
.set_uhs_signaling = dwcmshc_set_uhs_signaling,
.get_max_clock = dwcmshc_get_max_clock,
.reset = sdhci_reset,
.adma_write_desc = dwcmshc_adma_write_desc,
.irq = dwcmshc_cqe_irq_handler,
.hw_reset = dwcmshc_bf3_hw_reset,
};
#endif
static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = { static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = {
.set_clock = dwcmshc_rk3568_set_clock, .set_clock = dwcmshc_rk3568_set_clock,
.set_bus_width = sdhci_set_bus_width, .set_bus_width = sdhci_set_bus_width,
@ -932,39 +1169,71 @@ static const struct sdhci_ops sdhci_dwcmshc_cv18xx_ops = {
.platform_execute_tuning = cv18xx_sdhci_execute_tuning, .platform_execute_tuning = cv18xx_sdhci_execute_tuning,
}; };
static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = { static const struct sdhci_ops sdhci_dwcmshc_sg2042_ops = {
.ops = &sdhci_dwcmshc_ops, .set_clock = sdhci_set_clock,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .set_bus_width = sdhci_set_bus_width,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .set_uhs_signaling = dwcmshc_set_uhs_signaling,
.get_max_clock = dwcmshc_get_max_clock,
.reset = sg2042_sdhci_reset,
.adma_write_desc = dwcmshc_adma_write_desc,
.platform_execute_tuning = th1520_execute_tuning,
};
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = {
.pdata = {
.ops = &sdhci_dwcmshc_ops,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
},
}; };
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
static const struct sdhci_pltfm_data sdhci_dwcmshc_bf3_pdata = { static const struct dwcmshc_pltfm_data sdhci_dwcmshc_bf3_pdata = {
.ops = &sdhci_dwcmshc_ops, .pdata = {
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .ops = &sdhci_dwcmshc_bf3_ops,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
SDHCI_QUIRK2_ACMD23_BROKEN, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_ACMD23_BROKEN,
},
}; };
#endif #endif
static const struct sdhci_pltfm_data sdhci_dwcmshc_rk35xx_pdata = { static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
.ops = &sdhci_dwcmshc_rk35xx_ops, .pdata = {
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | .ops = &sdhci_dwcmshc_rk35xx_ops,
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
},
.init = dwcmshc_rk35xx_init,
.postinit = dwcmshc_rk35xx_postinit,
}; };
static const struct sdhci_pltfm_data sdhci_dwcmshc_th1520_pdata = { static const struct dwcmshc_pltfm_data sdhci_dwcmshc_th1520_pdata = {
.ops = &sdhci_dwcmshc_th1520_ops, .pdata = {
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .ops = &sdhci_dwcmshc_th1520_ops,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
},
.init = th1520_init,
}; };
static const struct sdhci_pltfm_data sdhci_dwcmshc_cv18xx_pdata = { static const struct dwcmshc_pltfm_data sdhci_dwcmshc_cv18xx_pdata = {
.ops = &sdhci_dwcmshc_cv18xx_ops, .pdata = {
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .ops = &sdhci_dwcmshc_cv18xx_ops,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
},
};
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_sg2042_pdata = {
.pdata = {
.ops = &sdhci_dwcmshc_sg2042_ops,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
},
.init = sg2042_init,
}; };
static const struct cqhci_host_ops dwcmshc_cqhci_ops = { static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
@ -1034,61 +1303,6 @@ dsbl_cqe_caps:
host->mmc->caps2 &= ~(MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD); host->mmc->caps2 &= ~(MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD);
} }
static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
{
int err;
struct rk35xx_priv *priv = dwc_priv->priv;
priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc));
if (IS_ERR(priv->reset)) {
err = PTR_ERR(priv->reset);
dev_err(mmc_dev(host->mmc), "failed to get reset control %d\n", err);
return err;
}
priv->rockchip_clks[0].id = "axi";
priv->rockchip_clks[1].id = "block";
priv->rockchip_clks[2].id = "timer";
err = devm_clk_bulk_get_optional(mmc_dev(host->mmc), RK35xx_MAX_CLKS,
priv->rockchip_clks);
if (err) {
dev_err(mmc_dev(host->mmc), "failed to get clocks %d\n", err);
return err;
}
err = clk_bulk_prepare_enable(RK35xx_MAX_CLKS, priv->rockchip_clks);
if (err) {
dev_err(mmc_dev(host->mmc), "failed to enable clocks %d\n", err);
return err;
}
if (of_property_read_u8(mmc_dev(host->mmc)->of_node, "rockchip,txclk-tapnum",
&priv->txclk_tapnum))
priv->txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT;
/* Disable cmd conflict check */
sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3);
/* Reset previous settings */
sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK);
sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_STRBIN);
return 0;
}
static void dwcmshc_rk35xx_postinit(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
{
/*
* Don't support highspeed bus mode with low clk speed as we
* cannot use DLL for this condition.
*/
if (host->mmc->f_max <= 52000000) {
dev_info(mmc_dev(host->mmc), "Disabling HS200/HS400, frequency too low (%d)\n",
host->mmc->f_max);
host->mmc->caps2 &= ~(MMC_CAP2_HS200 | MMC_CAP2_HS400);
host->mmc->caps &= ~(MMC_CAP_3_3V_DDR | MMC_CAP_1_8V_DDR);
}
}
static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
{ {
.compatible = "rockchip,rk3588-dwcmshc", .compatible = "rockchip,rk3588-dwcmshc",
@ -1114,6 +1328,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
.compatible = "thead,th1520-dwcmshc", .compatible = "thead,th1520-dwcmshc",
.data = &sdhci_dwcmshc_th1520_pdata, .data = &sdhci_dwcmshc_th1520_pdata,
}, },
{
.compatible = "sophgo,sg2042-dwcmshc",
.data = &sdhci_dwcmshc_sg2042_pdata,
},
{}, {},
}; };
MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
@ -1135,8 +1353,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
struct sdhci_pltfm_host *pltfm_host; struct sdhci_pltfm_host *pltfm_host;
struct sdhci_host *host; struct sdhci_host *host;
struct dwcmshc_priv *priv; struct dwcmshc_priv *priv;
struct rk35xx_priv *rk_priv = NULL; const struct dwcmshc_pltfm_data *pltfm_data;
const struct sdhci_pltfm_data *pltfm_data;
int err; int err;
u32 extra, caps; u32 extra, caps;
@ -1146,7 +1363,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
return -ENODEV; return -ENODEV;
} }
host = sdhci_pltfm_init(pdev, pltfm_data, host = sdhci_pltfm_init(pdev, &pltfm_data->pdata,
sizeof(struct dwcmshc_priv)); sizeof(struct dwcmshc_priv));
if (IS_ERR(host)) if (IS_ERR(host))
return PTR_ERR(host); return PTR_ERR(host);
@ -1191,49 +1408,12 @@ static int dwcmshc_probe(struct platform_device *pdev)
host->mmc_host_ops.hs400_enhanced_strobe = dwcmshc_hs400_enhanced_strobe; host->mmc_host_ops.hs400_enhanced_strobe = dwcmshc_hs400_enhanced_strobe;
host->mmc_host_ops.execute_tuning = dwcmshc_execute_tuning; host->mmc_host_ops.execute_tuning = dwcmshc_execute_tuning;
if (pltfm_data == &sdhci_dwcmshc_rk35xx_pdata) { if (pltfm_data->init) {
rk_priv = devm_kzalloc(&pdev->dev, sizeof(struct rk35xx_priv), GFP_KERNEL); err = pltfm_data->init(&pdev->dev, host, priv);
if (!rk_priv) {
err = -ENOMEM;
goto err_clk;
}
if (of_device_is_compatible(pdev->dev.of_node, "rockchip,rk3588-dwcmshc"))
rk_priv->devtype = DWCMSHC_RK3588;
else
rk_priv->devtype = DWCMSHC_RK3568;
priv->priv = rk_priv;
err = dwcmshc_rk35xx_init(host, priv);
if (err) if (err)
goto err_clk; goto err_clk;
} }
if (pltfm_data == &sdhci_dwcmshc_th1520_pdata) {
priv->delay_line = PHY_SDCLKDL_DC_DEFAULT;
if (device_property_read_bool(dev, "mmc-ddr-1_8v") ||
device_property_read_bool(dev, "mmc-hs200-1_8v") ||
device_property_read_bool(dev, "mmc-hs400-1_8v"))
priv->flags |= FLAG_IO_FIXED_1V8;
else
priv->flags &= ~FLAG_IO_FIXED_1V8;
/*
* start_signal_voltage_switch() will try 3.3V first
* then 1.8V. Use SDHCI_SIGNALING_180 rather than
* SDHCI_SIGNALING_330 to avoid setting voltage to 3.3V
* in sdhci_start_signal_voltage_switch().
*/
if (priv->flags & FLAG_IO_FIXED_1V8) {
host->flags &= ~SDHCI_SIGNALING_330;
host->flags |= SDHCI_SIGNALING_180;
}
sdhci_enable_v4_mode(host);
}
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
if (pltfm_data == &sdhci_dwcmshc_bf3_pdata) if (pltfm_data == &sdhci_dwcmshc_bf3_pdata)
sdhci_enable_v4_mode(host); sdhci_enable_v4_mode(host);
@ -1261,8 +1441,8 @@ static int dwcmshc_probe(struct platform_device *pdev)
dwcmshc_cqhci_init(host, pdev); dwcmshc_cqhci_init(host, pdev);
} }
if (rk_priv) if (pltfm_data->postinit)
dwcmshc_rk35xx_postinit(host, priv); pltfm_data->postinit(host, priv);
err = __sdhci_add_host(host); err = __sdhci_add_host(host);
if (err) if (err)
@ -1280,9 +1460,7 @@ err_rpm:
err_clk: err_clk:
clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(pltfm_host->clk);
clk_disable_unprepare(priv->bus_clk); clk_disable_unprepare(priv->bus_clk);
if (rk_priv) clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
rk_priv->rockchip_clks);
free_pltfm: free_pltfm:
sdhci_pltfm_free(pdev); sdhci_pltfm_free(pdev);
return err; return err;
@ -1304,7 +1482,6 @@ static void dwcmshc_remove(struct platform_device *pdev)
struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
struct rk35xx_priv *rk_priv = priv->priv;
pm_runtime_get_sync(&pdev->dev); pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
@ -1316,9 +1493,7 @@ static void dwcmshc_remove(struct platform_device *pdev)
clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(pltfm_host->clk);
clk_disable_unprepare(priv->bus_clk); clk_disable_unprepare(priv->bus_clk);
if (rk_priv) clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
rk_priv->rockchip_clks);
sdhci_pltfm_free(pdev); sdhci_pltfm_free(pdev);
} }
@ -1328,7 +1503,6 @@ static int dwcmshc_suspend(struct device *dev)
struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
struct rk35xx_priv *rk_priv = priv->priv;
int ret; int ret;
pm_runtime_resume(dev); pm_runtime_resume(dev);
@ -1347,9 +1521,7 @@ static int dwcmshc_suspend(struct device *dev)
if (!IS_ERR(priv->bus_clk)) if (!IS_ERR(priv->bus_clk))
clk_disable_unprepare(priv->bus_clk); clk_disable_unprepare(priv->bus_clk);
if (rk_priv) clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
rk_priv->rockchip_clks);
return ret; return ret;
} }
@ -1359,7 +1531,6 @@ static int dwcmshc_resume(struct device *dev)
struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
struct rk35xx_priv *rk_priv = priv->priv;
int ret; int ret;
ret = clk_prepare_enable(pltfm_host->clk); ret = clk_prepare_enable(pltfm_host->clk);
@ -1372,29 +1543,24 @@ static int dwcmshc_resume(struct device *dev)
goto disable_clk; goto disable_clk;
} }
if (rk_priv) { ret = clk_bulk_prepare_enable(priv->num_other_clks, priv->other_clks);
ret = clk_bulk_prepare_enable(RK35xx_MAX_CLKS, if (ret)
rk_priv->rockchip_clks); goto disable_bus_clk;
if (ret)
goto disable_bus_clk;
}
ret = sdhci_resume_host(host); ret = sdhci_resume_host(host);
if (ret) if (ret)
goto disable_rockchip_clks; goto disable_other_clks;
if (host->mmc->caps2 & MMC_CAP2_CQE) { if (host->mmc->caps2 & MMC_CAP2_CQE) {
ret = cqhci_resume(host->mmc); ret = cqhci_resume(host->mmc);
if (ret) if (ret)
goto disable_rockchip_clks; goto disable_other_clks;
} }
return 0; return 0;
disable_rockchip_clks: disable_other_clks:
if (rk_priv) clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
rk_priv->rockchip_clks);
disable_bus_clk: disable_bus_clk:
if (!IS_ERR(priv->bus_clk)) if (!IS_ERR(priv->bus_clk))
clk_disable_unprepare(priv->bus_clk); clk_disable_unprepare(priv->bus_clk);

View File

@ -0,0 +1,314 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2024 Nuvoton Technology Corp.
*
* Author: Shan-Chun Hung <shanchun1218@gmail.com>
*/
#include <linux/align.h>
#include <linux/array_size.h>
#include <linux/bits.h>
#include <linux/build_bug.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/math.h>
#include <linux/mfd/syscon.h>
#include <linux/minmax.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/sizes.h>
#include <linux/types.h>
#include "sdhci-pltfm.h"
#include "sdhci.h"
#define MA35_SYS_MISCFCR0 0x070
#define MA35_SDHCI_MSHCCTL 0x508
#define MA35_SDHCI_MBIUCTL 0x510
#define MA35_SDHCI_CMD_CONFLICT_CHK BIT(0)
#define MA35_SDHCI_INCR_MSK GENMASK(3, 0)
#define MA35_SDHCI_INCR16 BIT(3)
#define MA35_SDHCI_INCR8 BIT(2)
struct ma35_priv {
struct reset_control *rst;
struct pinctrl *pinctrl;
struct pinctrl_state *pins_uhs;
struct pinctrl_state *pins_default;
};
struct ma35_restore_data {
u32 reg;
u32 width;
};
static const struct ma35_restore_data restore_data[] = {
{ SDHCI_CLOCK_CONTROL, sizeof(u32)},
{ SDHCI_BLOCK_SIZE, sizeof(u32)},
{ SDHCI_INT_ENABLE, sizeof(u32)},
{ SDHCI_SIGNAL_ENABLE, sizeof(u32)},
{ SDHCI_AUTO_CMD_STATUS, sizeof(u32)},
{ SDHCI_HOST_CONTROL, sizeof(u32)},
{ SDHCI_TIMEOUT_CONTROL, sizeof(u8) },
{ MA35_SDHCI_MSHCCTL, sizeof(u16)},
{ MA35_SDHCI_MBIUCTL, sizeof(u16)},
};
/*
* If DMA addr spans 128MB boundary, we split the DMA transfer into two
* so that each DMA transfer doesn't exceed the boundary.
*/
static void ma35_adma_write_desc(struct sdhci_host *host, void **desc, dma_addr_t addr, int len,
unsigned int cmd)
{
int tmplen, offset;
if (likely(!len || (ALIGN(addr, SZ_128M) == ALIGN(addr + len - 1, SZ_128M)))) {
sdhci_adma_write_desc(host, desc, addr, len, cmd);
return;
}
offset = addr & (SZ_128M - 1);
tmplen = SZ_128M - offset;
sdhci_adma_write_desc(host, desc, addr, tmplen, cmd);
addr += tmplen;
len -= tmplen;
sdhci_adma_write_desc(host, desc, addr, len, cmd);
}
static void ma35_set_clock(struct sdhci_host *host, unsigned int clock)
{
u32 ctl;
/*
* If the clock frequency exceeds MMC_HIGH_52_MAX_DTR,
* disable command conflict check.
*/
ctl = sdhci_readw(host, MA35_SDHCI_MSHCCTL);
if (clock > MMC_HIGH_52_MAX_DTR)
ctl &= ~MA35_SDHCI_CMD_CONFLICT_CHK;
else
ctl |= MA35_SDHCI_CMD_CONFLICT_CHK;
sdhci_writew(host, ctl, MA35_SDHCI_MSHCCTL);
sdhci_set_clock(host, clock);
}
static int ma35_start_signal_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct ma35_priv *priv = sdhci_pltfm_priv(pltfm_host);
switch (ios->signal_voltage) {
case MMC_SIGNAL_VOLTAGE_180:
if (!IS_ERR(priv->pinctrl) && !IS_ERR(priv->pins_uhs))
pinctrl_select_state(priv->pinctrl, priv->pins_uhs);
break;
case MMC_SIGNAL_VOLTAGE_330:
if (!IS_ERR(priv->pinctrl) && !IS_ERR(priv->pins_default))
pinctrl_select_state(priv->pinctrl, priv->pins_default);
break;
default:
dev_err(mmc_dev(host->mmc), "Unsupported signal voltage!\n");
return -EINVAL;
}
return sdhci_start_signal_voltage_switch(mmc, ios);
}
static void ma35_voltage_switch(struct sdhci_host *host)
{
/* Wait for 5ms after set 1.8V signal enable bit */
fsleep(5000);
}
static int ma35_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct ma35_priv *priv = sdhci_pltfm_priv(pltfm_host);
int idx;
u32 regs[ARRAY_SIZE(restore_data)] = {};
/*
* Limitations require a reset of SD/eMMC before tuning and
* saving the registers before resetting, then restoring
* after the reset.
*/
for (idx = 0; idx < ARRAY_SIZE(restore_data); idx++) {
if (restore_data[idx].width == sizeof(u32))
regs[idx] = sdhci_readl(host, restore_data[idx].reg);
else if (restore_data[idx].width == sizeof(u16))
regs[idx] = sdhci_readw(host, restore_data[idx].reg);
else if (restore_data[idx].width == sizeof(u8))
regs[idx] = sdhci_readb(host, restore_data[idx].reg);
}
reset_control_assert(priv->rst);
reset_control_deassert(priv->rst);
for (idx = 0; idx < ARRAY_SIZE(restore_data); idx++) {
if (restore_data[idx].width == sizeof(u32))
sdhci_writel(host, regs[idx], restore_data[idx].reg);
else if (restore_data[idx].width == sizeof(u16))
sdhci_writew(host, regs[idx], restore_data[idx].reg);
else if (restore_data[idx].width == sizeof(u8))
sdhci_writeb(host, regs[idx], restore_data[idx].reg);
}
return sdhci_execute_tuning(mmc, opcode);
}
static const struct sdhci_ops sdhci_ma35_ops = {
.set_clock = ma35_set_clock,
.set_bus_width = sdhci_set_bus_width,
.set_uhs_signaling = sdhci_set_uhs_signaling,
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
.reset = sdhci_reset,
.adma_write_desc = ma35_adma_write_desc,
.voltage_switch = ma35_voltage_switch,
};
static const struct sdhci_pltfm_data sdhci_ma35_pdata = {
.ops = &sdhci_ma35_ops,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_BROKEN_DDR50 |
SDHCI_QUIRK2_ACMD23_BROKEN,
};
static int ma35_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_host *host;
struct ma35_priv *priv;
int err;
u32 extra, ctl;
host = sdhci_pltfm_init(pdev, &sdhci_ma35_pdata, sizeof(struct ma35_priv));
if (IS_ERR(host))
return PTR_ERR(host);
/* Extra adma table cnt for cross 128M boundary handling. */
extra = DIV_ROUND_UP_ULL(dma_get_required_mask(dev), SZ_128M);
extra = min(extra, SDHCI_MAX_SEGS);
host->adma_table_cnt += extra;
pltfm_host = sdhci_priv(host);
priv = sdhci_pltfm_priv(pltfm_host);
pltfm_host->clk = devm_clk_get_optional_enabled(dev, NULL);
if (IS_ERR(pltfm_host->clk)) {
err = dev_err_probe(dev, PTR_ERR(pltfm_host->clk), "failed to get clk\n");
goto err_sdhci;
}
err = mmc_of_parse(host->mmc);
if (err)
goto err_sdhci;
priv->rst = devm_reset_control_get_exclusive(dev, NULL);
if (IS_ERR(priv->rst)) {
err = dev_err_probe(dev, PTR_ERR(priv->rst), "failed to get reset control\n");
goto err_sdhci;
}
sdhci_get_of_property(pdev);
priv->pinctrl = devm_pinctrl_get(dev);
if (!IS_ERR(priv->pinctrl)) {
priv->pins_default = pinctrl_lookup_state(priv->pinctrl, "default");
priv->pins_uhs = pinctrl_lookup_state(priv->pinctrl, "state_uhs");
pinctrl_select_state(priv->pinctrl, priv->pins_default);
}
if (!(host->quirks2 & SDHCI_QUIRK2_NO_1_8_V)) {
struct regmap *regmap;
u32 reg;
regmap = syscon_regmap_lookup_by_phandle(dev_of_node(dev), "nuvoton,sys");
if (!IS_ERR(regmap)) {
/* Enable SDHCI voltage stable for 1.8V */
regmap_read(regmap, MA35_SYS_MISCFCR0, &reg);
reg |= BIT(17);
regmap_write(regmap, MA35_SYS_MISCFCR0, reg);
}
host->mmc_host_ops.start_signal_voltage_switch =
ma35_start_signal_voltage_switch;
}
host->mmc_host_ops.execute_tuning = ma35_execute_tuning;
err = sdhci_add_host(host);
if (err)
goto err_sdhci;
/*
* Split data into chunks of 16 or 8 bytes for transmission.
* Each chunk transfer is guaranteed to be uninterrupted on the bus.
* This likely corresponds to the AHB bus DMA burst size.
*/
ctl = sdhci_readw(host, MA35_SDHCI_MBIUCTL);
ctl &= ~MA35_SDHCI_INCR_MSK;
ctl |= MA35_SDHCI_INCR16 | MA35_SDHCI_INCR8;
sdhci_writew(host, ctl, MA35_SDHCI_MBIUCTL);
return 0;
err_sdhci:
sdhci_pltfm_free(pdev);
return err;
}
static void ma35_disable_card_clk(struct sdhci_host *host)
{
u16 ctrl;
ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
if (ctrl & SDHCI_CLOCK_CARD_EN) {
ctrl &= ~SDHCI_CLOCK_CARD_EN;
sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
}
}
static void ma35_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
sdhci_remove_host(host, 0);
ma35_disable_card_clk(host);
sdhci_pltfm_free(pdev);
}
static const struct of_device_id sdhci_ma35_dt_ids[] = {
{ .compatible = "nuvoton,ma35d1-sdhci" },
{}
};
static struct platform_driver sdhci_ma35_driver = {
.driver = {
.name = "sdhci-ma35",
.of_match_table = sdhci_ma35_dt_ids,
},
.probe = ma35_probe,
.remove_new = ma35_remove,
};
module_platform_driver(sdhci_ma35_driver);
MODULE_DESCRIPTION("SDHCI platform driver for Nuvoton MA35");
MODULE_AUTHOR("Shan-Chun Hung <shanchun1218@gmail.com>");
MODULE_LICENSE("GPL");

View File

@ -126,7 +126,7 @@ static void pxav1_request_done(struct sdhci_host *host, struct mmc_request *mrq)
struct sdhci_pxav2_host *pxav2_host; struct sdhci_pxav2_host *pxav2_host;
/* If this is an SDIO command, perform errata workaround for silicon bug */ /* If this is an SDIO command, perform errata workaround for silicon bug */
if (mrq->cmd && !mrq->cmd->error && if (!mrq->cmd->error &&
(mrq->cmd->opcode == SD_IO_RW_DIRECT || (mrq->cmd->opcode == SD_IO_RW_DIRECT ||
mrq->cmd->opcode == SD_IO_RW_EXTENDED)) { mrq->cmd->opcode == SD_IO_RW_EXTENDED)) {
/* Reset data port */ /* Reset data port */

View File

@ -86,6 +86,7 @@
#define CLOCK_TOO_SLOW_HZ 50000000 #define CLOCK_TOO_SLOW_HZ 50000000
#define SDHCI_AM654_AUTOSUSPEND_DELAY -1 #define SDHCI_AM654_AUTOSUSPEND_DELAY -1
#define RETRY_TUNING_MAX 10
/* Command Queue Host Controller Interface Base address */ /* Command Queue Host Controller Interface Base address */
#define SDHCI_AM654_CQE_BASE_ADDR 0x200 #define SDHCI_AM654_CQE_BASE_ADDR 0x200
@ -151,6 +152,7 @@ struct sdhci_am654_data {
u32 flags; u32 flags;
u32 quirks; u32 quirks;
bool dll_enable; bool dll_enable;
u32 tuning_loop;
#define SDHCI_AM654_QUIRK_FORCE_CDTEST BIT(0) #define SDHCI_AM654_QUIRK_FORCE_CDTEST BIT(0)
}; };
@ -443,7 +445,7 @@ static u32 sdhci_am654_cqhci_irq(struct sdhci_host *host, u32 intmask)
#define ITAPDLY_LENGTH 32 #define ITAPDLY_LENGTH 32
#define ITAPDLY_LAST_INDEX (ITAPDLY_LENGTH - 1) #define ITAPDLY_LAST_INDEX (ITAPDLY_LENGTH - 1)
static u32 sdhci_am654_calculate_itap(struct sdhci_host *host, struct window static int sdhci_am654_calculate_itap(struct sdhci_host *host, struct window
*fail_window, u8 num_fails, bool circular_buffer) *fail_window, u8 num_fails, bool circular_buffer)
{ {
u8 itap = 0, start_fail = 0, end_fail = 0, pass_length = 0; u8 itap = 0, start_fail = 0, end_fail = 0, pass_length = 0;
@ -453,12 +455,16 @@ static u32 sdhci_am654_calculate_itap(struct sdhci_host *host, struct window
int prev_fail_end = -1; int prev_fail_end = -1;
u8 i; u8 i;
if (!num_fails) if (!num_fails) {
return ITAPDLY_LAST_INDEX >> 1; /* Retry tuning */
dev_dbg(dev, "No failing region found, retry tuning\n");
return -1;
}
if (fail_window->length == ITAPDLY_LENGTH) { if (fail_window->length == ITAPDLY_LENGTH) {
dev_err(dev, "No passing ITAPDLY, return 0\n"); /* Retry tuning */
return 0; dev_dbg(dev, "No passing itapdly, retry tuning\n");
return -1;
} }
first_fail_start = fail_window->start; first_fail_start = fail_window->start;
@ -494,13 +500,14 @@ static u32 sdhci_am654_calculate_itap(struct sdhci_host *host, struct window
return (itap > ITAPDLY_LAST_INDEX) ? ITAPDLY_LAST_INDEX >> 1 : itap; return (itap > ITAPDLY_LAST_INDEX) ? ITAPDLY_LAST_INDEX >> 1 : itap;
} }
static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host, static int sdhci_am654_do_tuning(struct sdhci_host *host,
u32 opcode) u32 opcode)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
unsigned char timing = host->mmc->ios.timing; unsigned char timing = host->mmc->ios.timing;
struct window fail_window[ITAPDLY_LENGTH]; struct window fail_window[ITAPDLY_LENGTH];
struct device *dev = mmc_dev(host->mmc);
u8 curr_pass, itap; u8 curr_pass, itap;
u8 fail_index = 0; u8 fail_index = 0;
u8 prev_pass = 1; u8 prev_pass = 1;
@ -521,6 +528,7 @@ static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host,
if (!curr_pass) { if (!curr_pass) {
fail_window[fail_index].end = itap; fail_window[fail_index].end = itap;
fail_window[fail_index].length++; fail_window[fail_index].length++;
dev_dbg(dev, "Failed itapdly=%d\n", itap);
} }
if (curr_pass && !prev_pass) if (curr_pass && !prev_pass)
@ -532,13 +540,34 @@ static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host,
if (fail_window[fail_index].length != 0) if (fail_window[fail_index].length != 0)
fail_index++; fail_index++;
itap = sdhci_am654_calculate_itap(host, fail_window, fail_index, return sdhci_am654_calculate_itap(host, fail_window, fail_index,
sdhci_am654->dll_enable); sdhci_am654->dll_enable);
}
sdhci_am654_write_itapdly(sdhci_am654, itap, sdhci_am654->itap_del_ena[timing]); static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host,
u32 opcode)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
unsigned char timing = host->mmc->ios.timing;
struct device *dev = mmc_dev(host->mmc);
int itapdly;
do {
itapdly = sdhci_am654_do_tuning(host, opcode);
if (itapdly >= 0)
break;
} while (++sdhci_am654->tuning_loop < RETRY_TUNING_MAX);
if (itapdly < 0) {
dev_err(dev, "Failed to find itapdly, fail tuning\n");
return -1;
}
dev_dbg(dev, "Passed tuning, final itapdly=%d\n", itapdly);
sdhci_am654_write_itapdly(sdhci_am654, itapdly, sdhci_am654->itap_del_ena[timing]);
/* Save ITAPDLY */ /* Save ITAPDLY */
sdhci_am654->itap_del_sel[timing] = itap; sdhci_am654->itap_del_sel[timing] = itapdly;
return 0; return 0;
} }
@ -742,6 +771,9 @@ static int sdhci_am654_init(struct sdhci_host *host)
regmap_update_bits(sdhci_am654->base, CTL_CFG_3, TUNINGFORSDR50_MASK, regmap_update_bits(sdhci_am654->base, CTL_CFG_3, TUNINGFORSDR50_MASK,
TUNINGFORSDR50_MASK); TUNINGFORSDR50_MASK);
/* Use to re-execute tuning */
sdhci_am654->tuning_loop = 0;
ret = sdhci_setup_host(host); ret = sdhci_setup_host(host);
if (ret) if (ret)
return ret; return ret;

View File

@ -895,8 +895,8 @@ static void tmio_mmc_power_on(struct tmio_mmc_host *host, unsigned short vdd)
* It seems, VccQ should be switched on after Vcc, this is also what the * It seems, VccQ should be switched on after Vcc, this is also what the
* omap_hsmmc.c driver does. * omap_hsmmc.c driver does.
*/ */
if (!IS_ERR(mmc->supply.vqmmc) && !ret) { if (!ret) {
ret = regulator_enable(mmc->supply.vqmmc); ret = mmc_regulator_enable_vqmmc(mmc);
usleep_range(200, 300); usleep_range(200, 300);
} }
@ -909,8 +909,7 @@ static void tmio_mmc_power_off(struct tmio_mmc_host *host)
{ {
struct mmc_host *mmc = host->mmc; struct mmc_host *mmc = host->mmc;
if (!IS_ERR(mmc->supply.vqmmc)) mmc_regulator_disable_vqmmc(mmc);
regulator_disable(mmc->supply.vqmmc);
if (!IS_ERR(mmc->supply.vmmc)) if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);

View File

@ -4,6 +4,7 @@ config OPTEE
tristate "OP-TEE" tristate "OP-TEE"
depends on HAVE_ARM_SMCCC depends on HAVE_ARM_SMCCC
depends on MMU depends on MMU
depends on RPMB || !RPMB
help help
This implements the OP-TEE Trusted Execution Environment (TEE) This implements the OP-TEE Trusted Execution Environment (TEE)
driver. driver.

View File

@ -10,17 +10,85 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/rpmb.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/tee_core.h> #include <linux/tee_core.h>
#include <linux/types.h> #include <linux/types.h>
#include "optee_private.h" #include "optee_private.h"
struct blocking_notifier_head optee_rpmb_intf_added =
BLOCKING_NOTIFIER_INIT(optee_rpmb_intf_added);
static int rpmb_add_dev(struct device *dev)
{
blocking_notifier_call_chain(&optee_rpmb_intf_added, 0,
to_rpmb_dev(dev));
return 0;
}
static struct class_interface rpmb_class_intf = {
.add_dev = rpmb_add_dev,
};
void optee_bus_scan_rpmb(struct work_struct *work)
{
struct optee *optee = container_of(work, struct optee,
rpmb_scan_bus_work);
int ret;
if (!optee->rpmb_scan_bus_done) {
ret = optee_enumerate_devices(PTA_CMD_GET_DEVICES_RPMB);
optee->rpmb_scan_bus_done = !ret;
if (ret && ret != -ENODEV)
pr_info("Scanning for RPMB device: ret %d\n", ret);
}
}
int optee_rpmb_intf_rdev(struct notifier_block *intf, unsigned long action,
void *data)
{
struct optee *optee = container_of(intf, struct optee, rpmb_intf);
schedule_work(&optee->rpmb_scan_bus_work);
return 0;
}
static void optee_bus_scan(struct work_struct *work) static void optee_bus_scan(struct work_struct *work)
{ {
WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP)); WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
} }
static ssize_t rpmb_routing_model_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct optee *optee = dev_get_drvdata(dev);
const char *s;
if (optee->in_kernel_rpmb_routing)
s = "kernel";
else
s = "user";
return scnprintf(buf, PAGE_SIZE, "%s\n", s);
}
static DEVICE_ATTR_RO(rpmb_routing_model);
static struct attribute *optee_dev_attrs[] = {
&dev_attr_rpmb_routing_model.attr,
NULL
};
ATTRIBUTE_GROUPS(optee_dev);
void optee_set_dev_group(struct optee *optee)
{
tee_device_set_dev_groups(optee->teedev, optee_dev_groups);
tee_device_set_dev_groups(optee->supp_teedev, optee_dev_groups);
}
int optee_open(struct tee_context *ctx, bool cap_memref_null) int optee_open(struct tee_context *ctx, bool cap_memref_null)
{ {
struct optee_context_data *ctxdata; struct optee_context_data *ctxdata;
@ -97,6 +165,9 @@ void optee_release_supp(struct tee_context *ctx)
void optee_remove_common(struct optee *optee) void optee_remove_common(struct optee *optee)
{ {
blocking_notifier_chain_unregister(&optee_rpmb_intf_added,
&optee->rpmb_intf);
cancel_work_sync(&optee->rpmb_scan_bus_work);
/* Unregister OP-TEE specific client devices on TEE bus */ /* Unregister OP-TEE specific client devices on TEE bus */
optee_unregister_devices(); optee_unregister_devices();
@ -113,13 +184,18 @@ void optee_remove_common(struct optee *optee)
tee_shm_pool_free(optee->pool); tee_shm_pool_free(optee->pool);
optee_supp_uninit(&optee->supp); optee_supp_uninit(&optee->supp);
mutex_destroy(&optee->call_queue.mutex); mutex_destroy(&optee->call_queue.mutex);
rpmb_dev_put(optee->rpmb_dev);
mutex_destroy(&optee->rpmb_dev_mutex);
} }
static int smc_abi_rc; static int smc_abi_rc;
static int ffa_abi_rc; static int ffa_abi_rc;
static bool intf_is_regged;
static int __init optee_core_init(void) static int __init optee_core_init(void)
{ {
int rc;
/* /*
* The kernel may have crashed at the same time that all available * The kernel may have crashed at the same time that all available
* secure world threads were suspended and we cannot reschedule the * secure world threads were suspended and we cannot reschedule the
@ -130,18 +206,36 @@ static int __init optee_core_init(void)
if (is_kdump_kernel()) if (is_kdump_kernel())
return -ENODEV; return -ENODEV;
if (IS_REACHABLE(CONFIG_RPMB)) {
rc = rpmb_interface_register(&rpmb_class_intf);
if (rc)
return rc;
intf_is_regged = true;
}
smc_abi_rc = optee_smc_abi_register(); smc_abi_rc = optee_smc_abi_register();
ffa_abi_rc = optee_ffa_abi_register(); ffa_abi_rc = optee_ffa_abi_register();
/* If both failed there's no point with this module */ /* If both failed there's no point with this module */
if (smc_abi_rc && ffa_abi_rc) if (smc_abi_rc && ffa_abi_rc) {
if (IS_REACHABLE(CONFIG_RPMB)) {
rpmb_interface_unregister(&rpmb_class_intf);
intf_is_regged = false;
}
return smc_abi_rc; return smc_abi_rc;
}
return 0; return 0;
} }
module_init(optee_core_init); module_init(optee_core_init);
static void __exit optee_core_exit(void) static void __exit optee_core_exit(void)
{ {
if (IS_REACHABLE(CONFIG_RPMB) && intf_is_regged) {
rpmb_interface_unregister(&rpmb_class_intf);
intf_is_regged = false;
}
if (!smc_abi_rc) if (!smc_abi_rc)
optee_smc_abi_unregister(); optee_smc_abi_unregister();
if (!ffa_abi_rc) if (!ffa_abi_rc)

View File

@ -43,6 +43,13 @@ static int get_devices(struct tee_context *ctx, u32 session,
ret = tee_client_invoke_func(ctx, &inv_arg, param); ret = tee_client_invoke_func(ctx, &inv_arg, param);
if ((ret < 0) || ((inv_arg.ret != TEEC_SUCCESS) && if ((ret < 0) || ((inv_arg.ret != TEEC_SUCCESS) &&
(inv_arg.ret != TEEC_ERROR_SHORT_BUFFER))) { (inv_arg.ret != TEEC_ERROR_SHORT_BUFFER))) {
/*
* TEE_ERROR_STORAGE_NOT_AVAILABLE is returned when getting
* the list of device TAs that depends on RPMB but a usable
* RPMB device isn't found.
*/
if (inv_arg.ret == TEE_ERROR_STORAGE_NOT_AVAILABLE)
return -ENODEV;
pr_err("PTA_CMD_GET_DEVICES invoke function err: %x\n", pr_err("PTA_CMD_GET_DEVICES invoke function err: %x\n",
inv_arg.ret); inv_arg.ret);
return -EINVAL; return -EINVAL;

View File

@ -7,6 +7,7 @@
#include <linux/arm_ffa.h> #include <linux/arm_ffa.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/rpmb.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -909,6 +910,10 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
optee->ffa.bottom_half_value = U32_MAX; optee->ffa.bottom_half_value = U32_MAX;
optee->rpc_param_count = rpc_param_count; optee->rpc_param_count = rpc_param_count;
if (IS_REACHABLE(CONFIG_RPMB) &&
(sec_caps & OPTEE_FFA_SEC_CAP_RPMB_PROBE))
optee->in_kernel_rpmb_routing = true;
teedev = tee_device_alloc(&optee_ffa_clnt_desc, NULL, optee->pool, teedev = tee_device_alloc(&optee_ffa_clnt_desc, NULL, optee->pool,
optee); optee);
if (IS_ERR(teedev)) { if (IS_ERR(teedev)) {
@ -925,6 +930,8 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
} }
optee->supp_teedev = teedev; optee->supp_teedev = teedev;
optee_set_dev_group(optee);
rc = tee_device_register(optee->teedev); rc = tee_device_register(optee->teedev);
if (rc) if (rc)
goto err_unreg_supp_teedev; goto err_unreg_supp_teedev;
@ -940,6 +947,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
optee_cq_init(&optee->call_queue, 0); optee_cq_init(&optee->call_queue, 0);
optee_supp_init(&optee->supp); optee_supp_init(&optee->supp);
optee_shm_arg_cache_init(optee, arg_cache_flags); optee_shm_arg_cache_init(optee, arg_cache_flags);
mutex_init(&optee->rpmb_dev_mutex);
ffa_dev_set_drvdata(ffa_dev, optee); ffa_dev_set_drvdata(ffa_dev, optee);
ctx = teedev_open(optee->teedev); ctx = teedev_open(optee->teedev);
if (IS_ERR(ctx)) { if (IS_ERR(ctx)) {
@ -961,6 +969,10 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
if (rc) if (rc)
goto err_unregister_devices; goto err_unregister_devices;
INIT_WORK(&optee->rpmb_scan_bus_work, optee_bus_scan_rpmb);
optee->rpmb_intf.notifier_call = optee_rpmb_intf_rdev;
blocking_notifier_chain_register(&optee_rpmb_intf_added,
&optee->rpmb_intf);
pr_info("initialized driver\n"); pr_info("initialized driver\n");
return 0; return 0;
@ -974,6 +986,8 @@ err_close_ctx:
teedev_close_context(ctx); teedev_close_context(ctx);
err_rhashtable_free: err_rhashtable_free:
rhashtable_free_and_destroy(&optee->ffa.global_ids, rh_free_fn, NULL); rhashtable_free_and_destroy(&optee->ffa.global_ids, rh_free_fn, NULL);
rpmb_dev_put(optee->rpmb_dev);
mutex_destroy(&optee->rpmb_dev_mutex);
optee_supp_uninit(&optee->supp); optee_supp_uninit(&optee->supp);
mutex_destroy(&optee->call_queue.mutex); mutex_destroy(&optee->call_queue.mutex);
mutex_destroy(&optee->ffa.mutex); mutex_destroy(&optee->ffa.mutex);

View File

@ -92,6 +92,8 @@
#define OPTEE_FFA_SEC_CAP_ARG_OFFSET BIT(0) #define OPTEE_FFA_SEC_CAP_ARG_OFFSET BIT(0)
/* OP-TEE supports asynchronous notification via FF-A */ /* OP-TEE supports asynchronous notification via FF-A */
#define OPTEE_FFA_SEC_CAP_ASYNC_NOTIF BIT(1) #define OPTEE_FFA_SEC_CAP_ASYNC_NOTIF BIT(1)
/* OP-TEE supports probing for RPMB device if needed */
#define OPTEE_FFA_SEC_CAP_RPMB_PROBE BIT(2)
#define OPTEE_FFA_EXCHANGE_CAPABILITIES OPTEE_FFA_BLOCKING_CALL(2) #define OPTEE_FFA_EXCHANGE_CAPABILITIES OPTEE_FFA_BLOCKING_CALL(2)

View File

@ -7,7 +7,9 @@
#define OPTEE_PRIVATE_H #define OPTEE_PRIVATE_H
#include <linux/arm-smccc.h> #include <linux/arm-smccc.h>
#include <linux/notifier.h>
#include <linux/rhashtable.h> #include <linux/rhashtable.h>
#include <linux/rpmb.h>
#include <linux/semaphore.h> #include <linux/semaphore.h>
#include <linux/tee_core.h> #include <linux/tee_core.h>
#include <linux/types.h> #include <linux/types.h>
@ -20,6 +22,7 @@
/* Some Global Platform error codes used in this driver */ /* Some Global Platform error codes used in this driver */
#define TEEC_SUCCESS 0x00000000 #define TEEC_SUCCESS 0x00000000
#define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006 #define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006
#define TEEC_ERROR_ITEM_NOT_FOUND 0xFFFF0008
#define TEEC_ERROR_NOT_SUPPORTED 0xFFFF000A #define TEEC_ERROR_NOT_SUPPORTED 0xFFFF000A
#define TEEC_ERROR_COMMUNICATION 0xFFFF000E #define TEEC_ERROR_COMMUNICATION 0xFFFF000E
#define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C #define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C
@ -28,6 +31,7 @@
/* API Return Codes are from the GP TEE Internal Core API Specification */ /* API Return Codes are from the GP TEE Internal Core API Specification */
#define TEE_ERROR_TIMEOUT 0xFFFF3001 #define TEE_ERROR_TIMEOUT 0xFFFF3001
#define TEE_ERROR_STORAGE_NOT_AVAILABLE 0xF0100003
#define TEEC_ORIGIN_COMMS 0x00000002 #define TEEC_ORIGIN_COMMS 0x00000002
@ -200,6 +204,12 @@ struct optee_ops {
* @notif: notification synchronization struct * @notif: notification synchronization struct
* @supp: supplicant synchronization struct for RPC to supplicant * @supp: supplicant synchronization struct for RPC to supplicant
* @pool: shared memory pool * @pool: shared memory pool
* @mutex: mutex protecting @rpmb_dev
* @rpmb_dev: current RPMB device or NULL
* @rpmb_scan_bus_done flag if device registation of RPMB dependent devices
* was already done
* @rpmb_scan_bus_work workq to for an RPMB device and to scan optee bus
* and register RPMB dependent optee drivers
* @rpc_param_count: If > 0 number of RPC parameters to make room for * @rpc_param_count: If > 0 number of RPC parameters to make room for
* @scan_bus_done flag if device registation was already done. * @scan_bus_done flag if device registation was already done.
* @scan_bus_work workq to scan optee bus and register optee drivers * @scan_bus_work workq to scan optee bus and register optee drivers
@ -218,9 +228,16 @@ struct optee {
struct optee_notif notif; struct optee_notif notif;
struct optee_supp supp; struct optee_supp supp;
struct tee_shm_pool *pool; struct tee_shm_pool *pool;
/* Protects rpmb_dev pointer */
struct mutex rpmb_dev_mutex;
struct rpmb_dev *rpmb_dev;
struct notifier_block rpmb_intf;
unsigned int rpc_param_count; unsigned int rpc_param_count;
bool scan_bus_done; bool scan_bus_done;
bool rpmb_scan_bus_done;
bool in_kernel_rpmb_routing;
struct work_struct scan_bus_work; struct work_struct scan_bus_work;
struct work_struct rpmb_scan_bus_work;
}; };
struct optee_session { struct optee_session {
@ -253,6 +270,8 @@ struct optee_call_ctx {
size_t num_entries; size_t num_entries;
}; };
extern struct blocking_notifier_head optee_rpmb_intf_added;
int optee_notif_init(struct optee *optee, u_int max_key); int optee_notif_init(struct optee *optee, u_int max_key);
void optee_notif_uninit(struct optee *optee); void optee_notif_uninit(struct optee *optee);
int optee_notif_wait(struct optee *optee, u_int key, u32 timeout); int optee_notif_wait(struct optee *optee, u_int key, u32 timeout);
@ -283,9 +302,14 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session);
#define PTA_CMD_GET_DEVICES 0x0 #define PTA_CMD_GET_DEVICES 0x0
#define PTA_CMD_GET_DEVICES_SUPP 0x1 #define PTA_CMD_GET_DEVICES_SUPP 0x1
#define PTA_CMD_GET_DEVICES_RPMB 0x2
int optee_enumerate_devices(u32 func); int optee_enumerate_devices(u32 func);
void optee_unregister_devices(void); void optee_unregister_devices(void);
void optee_bus_scan_rpmb(struct work_struct *work);
int optee_rpmb_intf_rdev(struct notifier_block *intf, unsigned long action,
void *data);
void optee_set_dev_group(struct optee *optee);
void optee_remove_common(struct optee *optee); void optee_remove_common(struct optee *optee);
int optee_open(struct tee_context *ctx, bool cap_memref_null); int optee_open(struct tee_context *ctx, bool cap_memref_null);
void optee_release(struct tee_context *ctx); void optee_release(struct tee_context *ctx);

View File

@ -104,4 +104,39 @@
/* I2C master control flags */ /* I2C master control flags */
#define OPTEE_RPC_I2C_FLAGS_TEN_BIT BIT(0) #define OPTEE_RPC_I2C_FLAGS_TEN_BIT BIT(0)
/*
* Reset RPMB probing
*
* Releases an eventually already used RPMB devices and starts over searching
* for RPMB devices. Returns the kind of shared memory to use in subsequent
* OPTEE_RPC_CMD_RPMB_PROBE_NEXT and OPTEE_RPC_CMD_RPMB calls.
*
* [out] value[0].a OPTEE_RPC_SHM_TYPE_*, the parameter for
* OPTEE_RPC_CMD_SHM_ALLOC
*/
#define OPTEE_RPC_CMD_RPMB_PROBE_RESET 22
/*
* Probe next RPMB device
*
* [out] value[0].a Type of RPMB device, OPTEE_RPC_RPMB_*
* [out] value[0].b EXT CSD-slice 168 "RPMB Size"
* [out] value[0].c EXT CSD-slice 222 "Reliable Write Sector Count"
* [out] memref[1] Buffer with the raw CID
*/
#define OPTEE_RPC_CMD_RPMB_PROBE_NEXT 23
/* Type of RPMB device */
#define OPTEE_RPC_RPMB_EMMC 0
#define OPTEE_RPC_RPMB_UFS 1
#define OPTEE_RPC_RPMB_NVME 2
/*
* Replay Protected Memory Block access
*
* [in] memref[0] Frames to device
* [out] memref[1] Frames from device
*/
#define OPTEE_RPC_CMD_RPMB_FRAMES 24
#endif /*__OPTEE_RPC_CMD_H*/ #endif /*__OPTEE_RPC_CMD_H*/

View File

@ -278,6 +278,8 @@ struct optee_smc_get_shm_config_result {
#define OPTEE_SMC_SEC_CAP_ASYNC_NOTIF BIT(5) #define OPTEE_SMC_SEC_CAP_ASYNC_NOTIF BIT(5)
/* Secure world supports pre-allocating RPC arg struct */ /* Secure world supports pre-allocating RPC arg struct */
#define OPTEE_SMC_SEC_CAP_RPC_ARG BIT(6) #define OPTEE_SMC_SEC_CAP_RPC_ARG BIT(6)
/* Secure world supports probing for RPMB device if needed */
#define OPTEE_SMC_SEC_CAP_RPMB_PROBE BIT(7)
#define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9 #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9
#define OPTEE_SMC_EXCHANGE_CAPABILITIES \ #define OPTEE_SMC_EXCHANGE_CAPABILITIES \

View File

@ -7,6 +7,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/rpmb.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/tee_core.h> #include <linux/tee_core.h>
#include "optee_private.h" #include "optee_private.h"
@ -261,6 +262,154 @@ void optee_rpc_cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm)
optee_supp_thrd_req(ctx, OPTEE_RPC_CMD_SHM_FREE, 1, &param); optee_supp_thrd_req(ctx, OPTEE_RPC_CMD_SHM_FREE, 1, &param);
} }
static void handle_rpc_func_rpmb_probe_reset(struct tee_context *ctx,
struct optee *optee,
struct optee_msg_arg *arg)
{
struct tee_param params[1];
if (arg->num_params != ARRAY_SIZE(params) ||
optee->ops->from_msg_param(optee, params, arg->num_params,
arg->params) ||
params[0].attr != TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT) {
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
return;
}
params[0].u.value.a = OPTEE_RPC_SHM_TYPE_KERNEL;
params[0].u.value.b = 0;
params[0].u.value.c = 0;
if (optee->ops->to_msg_param(optee, arg->params,
arg->num_params, params)) {
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
return;
}
mutex_lock(&optee->rpmb_dev_mutex);
rpmb_dev_put(optee->rpmb_dev);
optee->rpmb_dev = NULL;
mutex_unlock(&optee->rpmb_dev_mutex);
arg->ret = TEEC_SUCCESS;
}
static int rpmb_type_to_rpc_type(enum rpmb_type rtype)
{
switch (rtype) {
case RPMB_TYPE_EMMC:
return OPTEE_RPC_RPMB_EMMC;
case RPMB_TYPE_UFS:
return OPTEE_RPC_RPMB_UFS;
case RPMB_TYPE_NVME:
return OPTEE_RPC_RPMB_NVME;
default:
return -1;
}
}
static int rpc_rpmb_match(struct device *dev, const void *data)
{
struct rpmb_dev *rdev = to_rpmb_dev(dev);
return rpmb_type_to_rpc_type(rdev->descr.type) >= 0;
}
static void handle_rpc_func_rpmb_probe_next(struct tee_context *ctx,
struct optee *optee,
struct optee_msg_arg *arg)
{
struct rpmb_dev *rdev;
struct tee_param params[2];
void *buf;
if (arg->num_params != ARRAY_SIZE(params) ||
optee->ops->from_msg_param(optee, params, arg->num_params,
arg->params) ||
params[0].attr != TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT ||
params[1].attr != TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT) {
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
return;
}
buf = tee_shm_get_va(params[1].u.memref.shm,
params[1].u.memref.shm_offs);
if (IS_ERR(buf)) {
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
return;
}
mutex_lock(&optee->rpmb_dev_mutex);
rdev = rpmb_dev_find_device(NULL, optee->rpmb_dev, rpc_rpmb_match);
rpmb_dev_put(optee->rpmb_dev);
optee->rpmb_dev = rdev;
mutex_unlock(&optee->rpmb_dev_mutex);
if (!rdev) {
arg->ret = TEEC_ERROR_ITEM_NOT_FOUND;
return;
}
if (params[1].u.memref.size < rdev->descr.dev_id_len) {
arg->ret = TEEC_ERROR_SHORT_BUFFER;
return;
}
memcpy(buf, rdev->descr.dev_id, rdev->descr.dev_id_len);
params[1].u.memref.size = rdev->descr.dev_id_len;
params[0].u.value.a = rpmb_type_to_rpc_type(rdev->descr.type);
params[0].u.value.b = rdev->descr.capacity;
params[0].u.value.c = rdev->descr.reliable_wr_count;
if (optee->ops->to_msg_param(optee, arg->params,
arg->num_params, params)) {
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
return;
}
arg->ret = TEEC_SUCCESS;
}
static void handle_rpc_func_rpmb_frames(struct tee_context *ctx,
struct optee *optee,
struct optee_msg_arg *arg)
{
struct tee_param params[2];
struct rpmb_dev *rdev;
void *p0, *p1;
mutex_lock(&optee->rpmb_dev_mutex);
rdev = rpmb_dev_get(optee->rpmb_dev);
mutex_unlock(&optee->rpmb_dev_mutex);
if (!rdev) {
arg->ret = TEEC_ERROR_ITEM_NOT_FOUND;
return;
}
if (arg->num_params != ARRAY_SIZE(params) ||
optee->ops->from_msg_param(optee, params, arg->num_params,
arg->params) ||
params[0].attr != TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT ||
params[1].attr != TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT) {
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
goto out;
}
p0 = tee_shm_get_va(params[0].u.memref.shm,
params[0].u.memref.shm_offs);
p1 = tee_shm_get_va(params[1].u.memref.shm,
params[1].u.memref.shm_offs);
if (rpmb_route_frames(rdev, p0, params[0].u.memref.size, p1,
params[1].u.memref.size)) {
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
goto out;
}
if (optee->ops->to_msg_param(optee, arg->params,
arg->num_params, params)) {
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
goto out;
}
arg->ret = TEEC_SUCCESS;
out:
rpmb_dev_put(rdev);
}
void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee, void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee,
struct optee_msg_arg *arg) struct optee_msg_arg *arg)
{ {
@ -277,6 +426,34 @@ void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee,
case OPTEE_RPC_CMD_I2C_TRANSFER: case OPTEE_RPC_CMD_I2C_TRANSFER:
handle_rpc_func_cmd_i2c_transfer(ctx, arg); handle_rpc_func_cmd_i2c_transfer(ctx, arg);
break; break;
/*
* optee->in_kernel_rpmb_routing true means that OP-TEE supports
* in-kernel RPMB routing _and_ that the RPMB subsystem is
* reachable. This is reported to user space with
* rpmb_routing_model=kernel in sysfs.
*
* rpmb_routing_model=kernel is also a promise to user space that
* RPMB access will not require supplicant support, hence the
* checks below.
*/
case OPTEE_RPC_CMD_RPMB_PROBE_RESET:
if (optee->in_kernel_rpmb_routing)
handle_rpc_func_rpmb_probe_reset(ctx, optee, arg);
else
handle_rpc_supp_cmd(ctx, optee, arg);
break;
case OPTEE_RPC_CMD_RPMB_PROBE_NEXT:
if (optee->in_kernel_rpmb_routing)
handle_rpc_func_rpmb_probe_next(ctx, optee, arg);
else
handle_rpc_supp_cmd(ctx, optee, arg);
break;
case OPTEE_RPC_CMD_RPMB_FRAMES:
if (optee->in_kernel_rpmb_routing)
handle_rpc_func_rpmb_frames(ctx, optee, arg);
else
handle_rpc_supp_cmd(ctx, optee, arg);
break;
default: default:
handle_rpc_supp_cmd(ctx, optee, arg); handle_rpc_supp_cmd(ctx, optee, arg);
} }

View File

@ -20,6 +20,7 @@
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/rpmb.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/string.h> #include <linux/string.h>
@ -1685,6 +1686,10 @@ static int optee_probe(struct platform_device *pdev)
optee->smc.sec_caps = sec_caps; optee->smc.sec_caps = sec_caps;
optee->rpc_param_count = rpc_param_count; optee->rpc_param_count = rpc_param_count;
if (IS_REACHABLE(CONFIG_RPMB) &&
(sec_caps & OPTEE_SMC_SEC_CAP_RPMB_PROBE))
optee->in_kernel_rpmb_routing = true;
teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee); teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee);
if (IS_ERR(teedev)) { if (IS_ERR(teedev)) {
rc = PTR_ERR(teedev); rc = PTR_ERR(teedev);
@ -1699,6 +1704,8 @@ static int optee_probe(struct platform_device *pdev)
} }
optee->supp_teedev = teedev; optee->supp_teedev = teedev;
optee_set_dev_group(optee);
rc = tee_device_register(optee->teedev); rc = tee_device_register(optee->teedev);
if (rc) if (rc)
goto err_unreg_supp_teedev; goto err_unreg_supp_teedev;
@ -1712,6 +1719,7 @@ static int optee_probe(struct platform_device *pdev)
optee->smc.memremaped_shm = memremaped_shm; optee->smc.memremaped_shm = memremaped_shm;
optee->pool = pool; optee->pool = pool;
optee_shm_arg_cache_init(optee, arg_cache_flags); optee_shm_arg_cache_init(optee, arg_cache_flags);
mutex_init(&optee->rpmb_dev_mutex);
platform_set_drvdata(pdev, optee); platform_set_drvdata(pdev, optee);
ctx = teedev_open(optee->teedev); ctx = teedev_open(optee->teedev);
@ -1766,6 +1774,10 @@ static int optee_probe(struct platform_device *pdev)
if (rc) if (rc)
goto err_disable_shm_cache; goto err_disable_shm_cache;
INIT_WORK(&optee->rpmb_scan_bus_work, optee_bus_scan_rpmb);
optee->rpmb_intf.notifier_call = optee_rpmb_intf_rdev;
blocking_notifier_chain_register(&optee_rpmb_intf_added,
&optee->rpmb_intf);
pr_info("initialized driver\n"); pr_info("initialized driver\n");
return 0; return 0;
@ -1779,6 +1791,8 @@ err_notif_uninit:
err_close_ctx: err_close_ctx:
teedev_close_context(ctx); teedev_close_context(ctx);
err_supp_uninit: err_supp_uninit:
rpmb_dev_put(optee->rpmb_dev);
mutex_destroy(&optee->rpmb_dev_mutex);
optee_shm_arg_cache_uninit(optee); optee_shm_arg_cache_uninit(optee);
optee_supp_uninit(&optee->supp); optee_supp_uninit(&optee->supp);
mutex_destroy(&optee->call_queue.mutex); mutex_destroy(&optee->call_queue.mutex);

View File

@ -40,10 +40,7 @@ static const uuid_t tee_client_uuid_ns = UUID_INIT(0x58ac9ca0, 0x2086, 0x4683,
static DECLARE_BITMAP(dev_mask, TEE_NUM_DEVICES); static DECLARE_BITMAP(dev_mask, TEE_NUM_DEVICES);
static DEFINE_SPINLOCK(driver_lock); static DEFINE_SPINLOCK(driver_lock);
static const struct class tee_class = { static const struct class tee_class;
.name = "tee",
};
static dev_t tee_devt; static dev_t tee_devt;
struct tee_context *teedev_open(struct tee_device *teedev) struct tee_context *teedev_open(struct tee_device *teedev)
@ -965,6 +962,13 @@ err:
} }
EXPORT_SYMBOL_GPL(tee_device_alloc); EXPORT_SYMBOL_GPL(tee_device_alloc);
void tee_device_set_dev_groups(struct tee_device *teedev,
const struct attribute_group **dev_groups)
{
teedev->dev.groups = dev_groups;
}
EXPORT_SYMBOL_GPL(tee_device_set_dev_groups);
static ssize_t implementation_id_show(struct device *dev, static ssize_t implementation_id_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
@ -983,6 +987,11 @@ static struct attribute *tee_dev_attrs[] = {
ATTRIBUTE_GROUPS(tee_dev); ATTRIBUTE_GROUPS(tee_dev);
static const struct class tee_class = {
.name = "tee",
.dev_groups = tee_dev_groups,
};
/** /**
* tee_device_register() - Registers a TEE device * tee_device_register() - Registers a TEE device
* @teedev: Device to register * @teedev: Device to register
@ -1001,8 +1010,6 @@ int tee_device_register(struct tee_device *teedev)
return -EINVAL; return -EINVAL;
} }
teedev->dev.groups = tee_dev_groups;
rc = cdev_device_add(&teedev->cdev, &teedev->dev); rc = cdev_device_add(&teedev->cdev, &teedev->dev);
if (rc) { if (rc) {
dev_err(&teedev->dev, dev_err(&teedev->dev,

View File

@ -11,18 +11,6 @@
struct mmc_data; struct mmc_data;
struct mmc_request; struct mmc_request;
enum mmc_blk_status {
MMC_BLK_SUCCESS = 0,
MMC_BLK_PARTIAL,
MMC_BLK_CMD_ERR,
MMC_BLK_RETRY,
MMC_BLK_ABORT,
MMC_BLK_DATA_ERR,
MMC_BLK_ECC_ERR,
MMC_BLK_NOMEDIUM,
MMC_BLK_NEW_REQUEST,
};
struct mmc_command { struct mmc_command {
u32 opcode; u32 opcode;
u32 arg; u32 arg;

View File

@ -264,16 +264,6 @@ struct mmc_cqe_ops {
void (*cqe_recovery_finish)(struct mmc_host *host); void (*cqe_recovery_finish)(struct mmc_host *host);
}; };
struct mmc_async_req {
/* active mmc request */
struct mmc_request *mrq;
/*
* Check error status of completed mmc request.
* Returns 0 if success otherwise non zero.
*/
enum mmc_blk_status (*err_check)(struct mmc_card *, struct mmc_async_req *);
};
/** /**
* struct mmc_slot - MMC slot functions * struct mmc_slot - MMC slot functions
* *
@ -291,20 +281,6 @@ struct mmc_slot {
void *handler_priv; void *handler_priv;
}; };
/**
* mmc_context_info - synchronization details for mmc context
* @is_done_rcv wake up reason was done request
* @is_new_req wake up reason was new request
* @is_waiting_last_req mmc context waiting for single running request
* @wait wait queue
*/
struct mmc_context_info {
bool is_done_rcv;
bool is_new_req;
bool is_waiting_last_req;
wait_queue_head_t wait;
};
struct regulator; struct regulator;
struct mmc_pwrseq; struct mmc_pwrseq;
@ -672,7 +648,8 @@ static inline void mmc_debugfs_err_stats_inc(struct mmc_host *host,
host->err_stats[stat] += 1; host->err_stats[stat] += 1;
} }
int mmc_sd_switch(struct mmc_card *card, int mode, int group, u8 value, u8 *resp); int mmc_sd_switch(struct mmc_card *card, bool mode, int group,
u8 value, u8 *resp);
int mmc_send_status(struct mmc_card *card, u32 *status); int mmc_send_status(struct mmc_card *card, u32 *status);
int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error); int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error);
int mmc_send_abort_tuning(struct mmc_host *host, u32 opcode); int mmc_send_abort_tuning(struct mmc_host *host, u32 opcode);

123
include/linux/rpmb.h Normal file
View File

@ -0,0 +1,123 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2015-2019 Intel Corp. All rights reserved
* Copyright (C) 2021-2022 Linaro Ltd
*/
#ifndef __RPMB_H__
#define __RPMB_H__
#include <linux/device.h>
#include <linux/types.h>
/**
* enum rpmb_type - type of underlying storage technology
*
* @RPMB_TYPE_EMMC : emmc (JESD84-B50.1)
* @RPMB_TYPE_UFS : UFS (JESD220)
* @RPMB_TYPE_NVME : NVM Express
*/
enum rpmb_type {
RPMB_TYPE_EMMC,
RPMB_TYPE_UFS,
RPMB_TYPE_NVME,
};
/**
* struct rpmb_descr - RPMB description provided by the underlying block device
*
* @type : block device type
* @route_frames : routes frames to and from the RPMB device
* @dev_id : unique device identifier read from the hardware
* @dev_id_len : length of unique device identifier
* @reliable_wr_count: number of sectors that can be written in one access
* @capacity : capacity of the device in units of 128K
*
* @dev_id is intended to be used as input when deriving the authenticaion key.
*/
struct rpmb_descr {
enum rpmb_type type;
int (*route_frames)(struct device *dev, u8 *req, unsigned int req_len,
u8 *resp, unsigned int resp_len);
u8 *dev_id;
size_t dev_id_len;
u16 reliable_wr_count;
u16 capacity;
};
/**
* struct rpmb_dev - device which can support RPMB partition
*
* @dev : device
* @id : device_id
* @list_node : linked list node
* @descr : RPMB description
*/
struct rpmb_dev {
struct device dev;
int id;
struct list_head list_node;
struct rpmb_descr descr;
};
#define to_rpmb_dev(x) container_of((x), struct rpmb_dev, dev)
#if IS_ENABLED(CONFIG_RPMB)
struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev);
void rpmb_dev_put(struct rpmb_dev *rdev);
struct rpmb_dev *rpmb_dev_find_device(const void *data,
const struct rpmb_dev *start,
int (*match)(struct device *dev,
const void *data));
int rpmb_interface_register(struct class_interface *intf);
void rpmb_interface_unregister(struct class_interface *intf);
struct rpmb_dev *rpmb_dev_register(struct device *dev,
struct rpmb_descr *descr);
int rpmb_dev_unregister(struct rpmb_dev *rdev);
int rpmb_route_frames(struct rpmb_dev *rdev, u8 *req,
unsigned int req_len, u8 *resp, unsigned int resp_len);
#else
static inline struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev)
{
return NULL;
}
static inline void rpmb_dev_put(struct rpmb_dev *rdev) { }
static inline struct rpmb_dev *
rpmb_dev_find_device(const void *data, const struct rpmb_dev *start,
int (*match)(struct device *dev, const void *data))
{
return NULL;
}
static inline int rpmb_interface_register(struct class_interface *intf)
{
return -EOPNOTSUPP;
}
static inline void rpmb_interface_unregister(struct class_interface *intf)
{
}
static inline struct rpmb_dev *
rpmb_dev_register(struct device *dev, struct rpmb_descr *descr)
{
return NULL;
}
static inline int rpmb_dev_unregister(struct rpmb_dev *dev)
{
return 0;
}
static inline int rpmb_route_frames(struct rpmb_dev *rdev, u8 *req,
unsigned int req_len, u8 *resp,
unsigned int resp_len)
{
return -EOPNOTSUPP;
}
#endif /* CONFIG_RPMB */
#endif /* __RPMB_H__ */

View File

@ -154,6 +154,18 @@ int tee_device_register(struct tee_device *teedev);
*/ */
void tee_device_unregister(struct tee_device *teedev); void tee_device_unregister(struct tee_device *teedev);
/**
* tee_device_set_dev_groups() - Set device attribute groups
* @teedev: Device to register
* @dev_groups: Attribute groups
*
* Assigns the provided @dev_groups to the @teedev to be registered later
* with tee_device_register(). Calling this function is optional, but if
* it's called it must be called before tee_device_register().
*/
void tee_device_set_dev_groups(struct tee_device *teedev,
const struct attribute_group **dev_groups);
/** /**
* tee_session_calc_client_uuid() - Calculates client UUID for session * tee_session_calc_client_uuid() - Calculates client UUID for session
* @uuid: Resulting UUID * @uuid: Resulting UUID