Commit 7fced2a7 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 2471d2b3 cd3689b6
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.
# 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>;
};
# 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>;
};
};
...@@ -18,6 +18,7 @@ properties: ...@@ -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: ...@@ -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: ...@@ -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:
......
...@@ -43,6 +43,8 @@ properties: ...@@ -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
......
* 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>;
};
...@@ -10,18 +10,20 @@ maintainers: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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:
......
...@@ -10,4 +10,5 @@ MMC/SD/SDIO card support ...@@ -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
This diff is collapsed.
...@@ -19974,6 +19974,13 @@ T: git git://linuxtv.org/media_tree.git ...@@ -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> ...@@ -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
......
...@@ -104,6 +104,16 @@ config PHANTOM ...@@ -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
......
...@@ -15,6 +15,7 @@ obj-$(CONFIG_LKDTM) += lkdtm/ ...@@ -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
......
// 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");
...@@ -37,6 +37,7 @@ config PWRSEQ_SIMPLE ...@@ -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
......
...@@ -33,6 +33,7 @@ ...@@ -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 @@ ...@@ -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"); ...@@ -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 = { ...@@ -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 { ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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:
......
...@@ -51,20 +51,6 @@ static const unsigned int taac_mant[] = { ...@@ -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) ...@@ -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) ...@@ -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;
......
...@@ -56,5 +56,19 @@ int mmc_cmdq_enable(struct mmc_card *card); ...@@ -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
...@@ -255,7 +255,9 @@ int mmc_regulator_get_supply(struct mmc_host *mmc) ...@@ -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) ...@@ -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");
} }
......
...@@ -56,20 +56,6 @@ static const unsigned int sd_au_size[] = { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
......
...@@ -336,14 +336,13 @@ int mmc_app_send_scr(struct mmc_card *card) ...@@ -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));
......
...@@ -252,6 +252,18 @@ config MMC_SDHCI_OF_SPARX5 ...@@ -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
......
...@@ -88,6 +88,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o ...@@ -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
......
...@@ -33,6 +33,11 @@ struct cqhci_slot { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
......
...@@ -15,7 +15,17 @@ ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 = { ...@@ -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);
......
...@@ -795,14 +795,13 @@ static void msdc_unprepare_data(struct msdc_host *host, struct mmc_data *data) ...@@ -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) ...@@ -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) ...@@ -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)
......
...@@ -285,6 +285,7 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = { ...@@ -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, },
......
This diff is collapsed.
// 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");
...@@ -126,7 +126,7 @@ static void pxav1_request_done(struct sdhci_host *host, struct mmc_request *mrq) ...@@ -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 */
......
...@@ -86,6 +86,7 @@ ...@@ -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 { ...@@ -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) ...@@ -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 ...@@ -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 ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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;
......
...@@ -895,8 +895,8 @@ static void tmio_mmc_power_on(struct tmio_mmc_host *host, unsigned short vdd) ...@@ -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) ...@@ -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);
......
...@@ -4,6 +4,7 @@ config OPTEE ...@@ -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.
......
...@@ -10,17 +10,85 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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)
......
...@@ -43,6 +43,13 @@ static int get_devices(struct tee_context *ctx, u32 session, ...@@ -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;
......
...@@ -7,6 +7,7 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) ...@@ -974,6 +986,8 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
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);
......
...@@ -92,6 +92,8 @@ ...@@ -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)
......
...@@ -7,7 +7,9 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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); ...@@ -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);
......
...@@ -104,4 +104,39 @@ ...@@ -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*/
...@@ -278,6 +278,8 @@ struct optee_smc_get_shm_config_result { ...@@ -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 \
......
...@@ -7,6 +7,7 @@ ...@@ -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) ...@@ -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, ...@@ -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);
} }
......
...@@ -20,6 +20,7 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ static int optee_probe(struct platform_device *pdev) ...@@ -1779,6 +1791,8 @@ static int optee_probe(struct platform_device *pdev)
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);
......
...@@ -40,10 +40,7 @@ static const uuid_t tee_client_uuid_ns = UUID_INIT(0x58ac9ca0, 0x2086, 0x4683, ...@@ -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 @@ struct tee_device *tee_device_alloc(const struct tee_desc *teedesc, ...@@ -965,6 +962,13 @@ struct tee_device *tee_device_alloc(const struct tee_desc *teedesc,
} }
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[] = { ...@@ -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) ...@@ -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,
......
...@@ -11,18 +11,6 @@ ...@@ -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;
......
...@@ -264,16 +264,6 @@ struct mmc_cqe_ops { ...@@ -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 { ...@@ -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, ...@@ -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);
......
/* 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__ */
...@@ -154,6 +154,18 @@ int tee_device_register(struct tee_device *teedev); ...@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment