diff options
157 files changed, 9438 insertions, 651 deletions
diff --git a/Documentation/devicetree/bindings/arm/apple/apple,pmgr.yaml b/Documentation/devicetree/bindings/arm/apple/apple,pmgr.yaml index 5001f4d5a0dc..b88f41a225a3 100644 --- a/Documentation/devicetree/bindings/arm/apple/apple,pmgr.yaml +++ b/Documentation/devicetree/bindings/arm/apple/apple,pmgr.yaml @@ -20,19 +20,26 @@ properties: pattern: "^power-management@[0-9a-f]+$" compatible: - items: - - enum: - - apple,s5l8960x-pmgr - - apple,t7000-pmgr - - apple,s8000-pmgr - - apple,t8010-pmgr - - apple,t8015-pmgr - - apple,t8103-pmgr - - apple,t8112-pmgr - - apple,t6000-pmgr - - const: apple,pmgr - - const: syscon - - const: simple-mfd + oneOf: + - items: + - enum: + # Do not add additional SoC to this list. + - apple,s5l8960x-pmgr + - apple,t7000-pmgr + - apple,s8000-pmgr + - apple,t8010-pmgr + - apple,t8015-pmgr + - apple,t8103-pmgr + - apple,t8112-pmgr + - apple,t6000-pmgr + - const: apple,pmgr + - const: syscon + - const: simple-mfd + - items: + - const: apple,t6020-pmgr + - const: apple,t8103-pmgr + - const: syscon + - const: simple-mfd reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/cache/andestech,ax45mp-cache.yaml b/Documentation/devicetree/bindings/cache/andestech,ax45mp-cache.yaml index 4de5bb2e5f24..b135ffa4ab6b 100644 --- a/Documentation/devicetree/bindings/cache/andestech,ax45mp-cache.yaml +++ b/Documentation/devicetree/bindings/cache/andestech,ax45mp-cache.yaml @@ -47,7 +47,7 @@ properties: const: 2 cache-sets: - const: 1024 + enum: [1024, 2048] cache-size: enum: [131072, 262144, 524288, 1048576, 2097152] @@ -81,6 +81,10 @@ allOf: const: 2048 cache-size: const: 2097152 + else: + properties: + cache-sets: + const: 1024 examples: - | diff --git a/Documentation/devicetree/bindings/clock/apple,nco.yaml b/Documentation/devicetree/bindings/clock/apple,nco.yaml index 8b8411dc42f6..080454f56721 100644 --- a/Documentation/devicetree/bindings/clock/apple,nco.yaml +++ b/Documentation/devicetree/bindings/clock/apple,nco.yaml @@ -19,12 +19,17 @@ description: | properties: compatible: - items: - - enum: - - apple,t6000-nco - - apple,t8103-nco - - apple,t8112-nco - - const: apple,nco + oneOf: + - items: + - const: apple,t6020-nco + - const: apple,t8103-nco + - items: + - enum: + # Do not add additional SoC to this list. + - apple,t6000-nco + - apple,t8103-nco + - apple,t8112-nco + - const: apple,nco clocks: description: diff --git a/Documentation/devicetree/bindings/cpufreq/apple,cluster-cpufreq.yaml b/Documentation/devicetree/bindings/cpufreq/apple,cluster-cpufreq.yaml index 896276b8c6bb..b51913a81791 100644 --- a/Documentation/devicetree/bindings/cpufreq/apple,cluster-cpufreq.yaml +++ b/Documentation/devicetree/bindings/cpufreq/apple,cluster-cpufreq.yaml @@ -35,6 +35,9 @@ properties: - const: apple,t7000-cluster-cpufreq - const: apple,s5l8960x-cluster-cpufreq - const: apple,s5l8960x-cluster-cpufreq + - items: + - const: apple,t6020-cluster-cpufreq + - const: apple,t8112-cluster-cpufreq reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/dma/apple,admac.yaml b/Documentation/devicetree/bindings/dma/apple,admac.yaml index ab193bc8bdbb..6a200cbd7d02 100644 --- a/Documentation/devicetree/bindings/dma/apple,admac.yaml +++ b/Documentation/devicetree/bindings/dma/apple,admac.yaml @@ -22,12 +22,17 @@ allOf: properties: compatible: - items: - - enum: - - apple,t6000-admac - - apple,t8103-admac - - apple,t8112-admac - - const: apple,admac + oneOf: + - items: + - const: apple,t6020-admac + - const: apple,t8103-admac + - items: + - enum: + # Do not add additional SoC to this list. + - apple,t6000-admac + - apple,t8103-admac + - apple,t8112-admac + - const: apple,admac reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml index abbd62f1fed0..be817fd9cc34 100644 --- a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml +++ b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml @@ -27,7 +27,7 @@ anyOf: properties: $nodename: - const: scmi + pattern: '^scmi(-[0-9]+)?$' compatible: oneOf: diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml index b913192219e4..ef97faac7e47 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml @@ -36,6 +36,7 @@ properties: - qcom,scm-msm8226 - qcom,scm-msm8660 - qcom,scm-msm8916 + - qcom,scm-msm8937 - qcom,scm-msm8953 - qcom,scm-msm8960 - qcom,scm-msm8974 @@ -134,6 +135,7 @@ allOf: - qcom,scm-msm8226 - qcom,scm-msm8660 - qcom,scm-msm8916 + - qcom,scm-msm8937 - qcom,scm-msm8953 - qcom,scm-msm8960 - qcom,scm-msm8974 @@ -177,6 +179,7 @@ allOf: - qcom,scm-mdm9607 - qcom,scm-msm8226 - qcom,scm-msm8916 + - qcom,scm-msm8937 - qcom,scm-msm8953 - qcom,scm-msm8974 - qcom,scm-msm8976 diff --git a/Documentation/devicetree/bindings/gpu/apple,agx.yaml b/Documentation/devicetree/bindings/gpu/apple,agx.yaml index 51629b3833b0..05af942ad174 100644 --- a/Documentation/devicetree/bindings/gpu/apple,agx.yaml +++ b/Documentation/devicetree/bindings/gpu/apple,agx.yaml @@ -16,11 +16,17 @@ properties: - apple,agx-g13g - apple,agx-g13s - apple,agx-g14g + - apple,agx-g14s - items: - enum: - apple,agx-g13c - apple,agx-g13d - const: apple,agx-g13s + - items: + - enum: + - apple,agx-g14c + - apple,agx-g14d + - const: apple,agx-g14s reg: items: diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-geni-qcom.yaml b/Documentation/devicetree/bindings/i2c/qcom,i2c-geni-qcom.yaml index 9f66a3bb1f80..51534953a69c 100644 --- a/Documentation/devicetree/bindings/i2c/qcom,i2c-geni-qcom.yaml +++ b/Documentation/devicetree/bindings/i2c/qcom,i2c-geni-qcom.yaml @@ -75,6 +75,7 @@ required: allOf: - $ref: /schemas/i2c/i2c-controller.yaml# + - $ref: /schemas/soc/qcom/qcom,se-common-props.yaml# - if: properties: compatible: diff --git a/Documentation/devicetree/bindings/interrupt-controller/apple,aic2.yaml b/Documentation/devicetree/bindings/interrupt-controller/apple,aic2.yaml index 2bde6cc6fe0a..ee5a0dfff437 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/apple,aic2.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/apple,aic2.yaml @@ -34,6 +34,7 @@ properties: - enum: - apple,t8112-aic - apple,t6000-aic + - apple,t6020-aic - const: apple,aic2 interrupt-controller: true diff --git a/Documentation/devicetree/bindings/iommu/apple,dart.yaml b/Documentation/devicetree/bindings/iommu/apple,dart.yaml index 7adb1de455a5..47ec7fa52c3a 100644 --- a/Documentation/devicetree/bindings/iommu/apple,dart.yaml +++ b/Documentation/devicetree/bindings/iommu/apple,dart.yaml @@ -22,11 +22,15 @@ description: |+ properties: compatible: - enum: - - apple,t8103-dart - - apple,t8103-usb4-dart - - apple,t8110-dart - - apple,t6000-dart + oneOf: + - enum: + - apple,t8103-dart + - apple,t8103-usb4-dart + - apple,t8110-dart + - apple,t6000-dart + - items: + - const: apple,t6020-dart + - const: apple,t8110-dart reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iommu/apple,sart.yaml b/Documentation/devicetree/bindings/iommu/apple,sart.yaml index e87c1520fea6..88e66d4b13c6 100644 --- a/Documentation/devicetree/bindings/iommu/apple,sart.yaml +++ b/Documentation/devicetree/bindings/iommu/apple,sart.yaml @@ -30,10 +30,13 @@ properties: compatible: oneOf: - items: - - const: apple,t8112-sart + - enum: + - apple,t6020-sart + - apple,t8112-sart - const: apple,t6000-sart - enum: - apple,t6000-sart + - apple,t8015-sart - apple,t8103-sart reg: diff --git a/Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml b/Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml index 474c1a0f99f3..28985cc62c25 100644 --- a/Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml +++ b/Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml @@ -31,9 +31,17 @@ properties: - apple,t8103-asc-mailbox - apple,t8112-asc-mailbox - apple,t6000-asc-mailbox + - apple,t6020-asc-mailbox - const: apple,asc-mailbox-v4 - description: + An older ASC mailbox interface found on T2 and A11 that is also + used for the NVMe coprocessor and the system management + controller. + items: + - const: apple,t8015-asc-mailbox + + - description: M3 mailboxes are an older variant with a slightly different MMIO interface still found on the M1. It is used for the Thunderbolt co-processors. diff --git a/Documentation/devicetree/bindings/memory-controllers/brcm,brcmstb-memc-ddr.yaml b/Documentation/devicetree/bindings/memory-controllers/brcm,brcmstb-memc-ddr.yaml index b935894bd4fc..3328c8df8190 100644 --- a/Documentation/devicetree/bindings/memory-controllers/brcm,brcmstb-memc-ddr.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/brcm,brcmstb-memc-ddr.yaml @@ -42,6 +42,10 @@ properties: items: - const: brcm,brcmstb-memc-ddr-rev-b.1.x - const: brcm,brcmstb-memc-ddr + - description: Revision 0.x controllers + items: + - const: brcm,brcmstb-memc-ddr-rev-a.0.0 + - const: brcm,brcmstb-memc-ddr reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.yaml b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.yaml index bc8477e7ab19..4e4fb4acd7f9 100644 --- a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.yaml @@ -33,6 +33,9 @@ properties: items: - description: EMC general interrupt + "#interconnect-cells": + const: 0 + memory-region: maxItems: 1 description: @@ -44,6 +47,11 @@ properties: description: phandle of the memory controller node + operating-points-v2: + description: + Should contain freqs and voltages and opp-supported-hw property, which + is a bitfield indicating SoC speedo ID mask. + required: - compatible - reg @@ -79,4 +87,7 @@ examples: interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>; memory-region = <&emc_table>; nvidia,memory-controller = <&mc>; + operating-points-v2 = <&dvfs_opp_table>; + + #interconnect-cells = <0>; }; diff --git a/Documentation/devicetree/bindings/mfd/apple,smc.yaml b/Documentation/devicetree/bindings/mfd/apple,smc.yaml index 8a10e270d421..5429538f7e2e 100644 --- a/Documentation/devicetree/bindings/mfd/apple,smc.yaml +++ b/Documentation/devicetree/bindings/mfd/apple,smc.yaml @@ -15,12 +15,17 @@ description: properties: compatible: - items: - - enum: - - apple,t6000-smc - - apple,t8103-smc - - apple,t8112-smc - - const: apple,smc + oneOf: + - items: + - const: apple,t6020-smc + - const: apple,t8103-smc + - items: + - enum: + # Do not add additional SoC to this list. + - apple,t6000-smc + - apple,t8103-smc + - apple,t8112-smc + - const: apple,smc reg: items: diff --git a/Documentation/devicetree/bindings/net/bluetooth/brcm,bcm4377-bluetooth.yaml b/Documentation/devicetree/bindings/net/bluetooth/brcm,bcm4377-bluetooth.yaml index 37cb39a3a62e..fd78258d71b4 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/brcm,bcm4377-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/brcm,bcm4377-bluetooth.yaml @@ -23,6 +23,7 @@ properties: - pci14e4,5fa0 # BCM4377 - pci14e4,5f69 # BCM4378 - pci14e4,5f71 # BCM4387 + - pci14e4,5f72 # BCM4388 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml b/Documentation/devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml index 7c8100e59a6c..3be757678764 100644 --- a/Documentation/devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml +++ b/Documentation/devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml @@ -53,6 +53,7 @@ properties: - pci14e4,4488 # BCM4377 - pci14e4,4425 # BCM4378 - pci14e4,4433 # BCM4387 + - pci14e4,4434 # BCM4388 - pci14e4,449d # BCM43752 reg: diff --git a/Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml b/Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml index fc6555724e18..4c0b1f90aff8 100644 --- a/Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml +++ b/Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml @@ -11,12 +11,18 @@ maintainers: properties: compatible: - items: - - enum: - - apple,t8103-nvme-ans2 - - apple,t8112-nvme-ans2 - - apple,t6000-nvme-ans2 - - const: apple,nvme-ans2 + oneOf: + - const: apple,t8015-nvme-ans2 + - items: + - const: apple,t6020-nvme-ans2 + - const: apple,t8103-nvme-ans2 + - items: + - enum: + # Do not add additional SoC to this list. + - apple,t8103-nvme-ans2 + - apple,t8112-nvme-ans2 + - apple,t6000-nvme-ans2 + - const: apple,nvme-ans2 reg: items: @@ -67,20 +73,20 @@ if: compatible: contains: enum: - - apple,t8103-nvme-ans2 - - apple,t8112-nvme-ans2 + - apple,t6000-nvme-ans2 + - apple,t6020-nvme-ans2 then: properties: power-domains: - maxItems: 2 + minItems: 3 power-domain-names: - maxItems: 2 + minItems: 3 else: properties: power-domains: - minItems: 3 + maxItems: 2 power-domain-names: - minItems: 3 + maxItems: 2 required: - compatible diff --git a/Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml index 63737d858944..665ec79a69f1 100644 --- a/Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml @@ -16,17 +16,22 @@ description: | properties: compatible: - items: - - enum: - - apple,s5l8960x-pinctrl - - apple,t7000-pinctrl - - apple,s8000-pinctrl - - apple,t8010-pinctrl - - apple,t8015-pinctrl - - apple,t8103-pinctrl - - apple,t8112-pinctrl - - apple,t6000-pinctrl - - const: apple,pinctrl + oneOf: + - items: + - const: apple,t6020-pinctrl + - const: apple,t8103-pinctrl + - items: + # Do not add additional SoC to this list. + - enum: + - apple,s5l8960x-pinctrl + - apple,t7000-pinctrl + - apple,s8000-pinctrl + - apple,t8010-pinctrl + - apple,t8015-pinctrl + - apple,t8103-pinctrl + - apple,t8112-pinctrl + - apple,t6000-pinctrl + - const: apple,pinctrl reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml b/Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml index 6e9a670eaf56..caf151880999 100644 --- a/Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml +++ b/Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml @@ -29,17 +29,22 @@ description: | properties: compatible: - items: - - enum: - - apple,s5l8960x-pmgr-pwrstate - - apple,t7000-pmgr-pwrstate - - apple,s8000-pmgr-pwrstate - - apple,t8010-pmgr-pwrstate - - apple,t8015-pmgr-pwrstate - - apple,t8103-pmgr-pwrstate - - apple,t8112-pmgr-pwrstate - - apple,t6000-pmgr-pwrstate - - const: apple,pmgr-pwrstate + oneOf: + - items: + - enum: + # Do not add additional SoC to this list. + - apple,s5l8960x-pmgr-pwrstate + - apple,t7000-pmgr-pwrstate + - apple,s8000-pmgr-pwrstate + - apple,t8010-pmgr-pwrstate + - apple,t8015-pmgr-pwrstate + - apple,t8103-pmgr-pwrstate + - apple,t8112-pmgr-pwrstate + - apple,t6000-pmgr-pwrstate + - const: apple,pmgr-pwrstate + - items: + - const: apple,t6020-pmgr-pwrstate + - const: apple,t8103-pmgr-pwrstate reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/reset/brcm,bcm6345-reset.yaml b/Documentation/devicetree/bindings/reset/brcm,bcm6345-reset.yaml index 00150b93fca0..b8a320bb1776 100644 --- a/Documentation/devicetree/bindings/reset/brcm,bcm6345-reset.yaml +++ b/Documentation/devicetree/bindings/reset/brcm,bcm6345-reset.yaml @@ -13,7 +13,9 @@ maintainers: properties: compatible: - const: brcm,bcm6345-reset + enum: + - brcm,bcm6345-reset + - brcm,bcm63xx-ephy-ctrl reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/serial/qcom,serial-geni-qcom.yaml b/Documentation/devicetree/bindings/serial/qcom,serial-geni-qcom.yaml index dd33794b3534..ed7b3909d87d 100644 --- a/Documentation/devicetree/bindings/serial/qcom,serial-geni-qcom.yaml +++ b/Documentation/devicetree/bindings/serial/qcom,serial-geni-qcom.yaml @@ -12,6 +12,7 @@ maintainers: allOf: - $ref: /schemas/serial/serial.yaml# + - $ref: /schemas/soc/qcom/qcom,se-common-props.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml index 036562eb5140..26d9bc773ec5 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml @@ -28,7 +28,7 @@ description: | SLEEP - Triggered by F/W WAKE - Triggered by F/W CONTROL - Triggered by F/W - See also:: <dt-bindings/soc/qcom,rpmh-rsc.h> + See also: <dt-bindings/soc/qcom,rpmh-rsc.h> The order in which they are described in the DT, should match the hardware configuration. diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,se-common-props.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,se-common-props.yaml new file mode 100644 index 000000000000..6a34f05a07e8 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,se-common-props.yaml @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/qcom/qcom,se-common-props.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: QUP Peripheral-specific properties for I2C, SPI and SERIAL bus + +description: + The Generic Interface (GENI) based Qualcomm Universal Peripheral (QUP) is + a programmable module that supports a wide range of serial interfaces + such as UART, SPI, I2C, I3C, etc. This defines the common properties used + across QUP-supported peripherals. + +maintainers: + - Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com> + - Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com> + +properties: + qcom,enable-gsi-dma: + $ref: /schemas/types.yaml#/definitions/flag + description: + Configure the Serial Engine (SE) to transfer data in QCOM GPI DMA mode. + By default, FIFO mode (PIO/CPU DMA) will be selected. + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml b/Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml index cb22637091e8..c694926e56ef 100644 --- a/Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml +++ b/Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml @@ -36,6 +36,7 @@ properties: - items: - enum: - google,gs101-usi + - samsung,exynos2200-usi - samsung,exynosautov9-usi - samsung,exynosautov920-usi - const: samsung,exynos850-usi diff --git a/Documentation/devicetree/bindings/sound/apple,mca.yaml b/Documentation/devicetree/bindings/sound/apple,mca.yaml index 5c6ec08c7d24..2beb725118ad 100644 --- a/Documentation/devicetree/bindings/sound/apple,mca.yaml +++ b/Documentation/devicetree/bindings/sound/apple,mca.yaml @@ -19,12 +19,17 @@ allOf: properties: compatible: - items: - - enum: - - apple,t6000-mca - - apple,t8103-mca - - apple,t8112-mca - - const: apple,mca + oneOf: + - items: + - const: apple,t6020-mca + - const: apple,t8103-mca + - items: + - enum: + # Do not add additional SoC to this list. + - apple,t6000-mca + - apple,t8103-mca + - apple,t8112-mca + - const: apple,mca reg: items: diff --git a/Documentation/devicetree/bindings/spi/apple,spi.yaml b/Documentation/devicetree/bindings/spi/apple,spi.yaml index 7bef605a2963..9356b9c337c8 100644 --- a/Documentation/devicetree/bindings/spi/apple,spi.yaml +++ b/Documentation/devicetree/bindings/spi/apple,spi.yaml @@ -14,12 +14,16 @@ maintainers: properties: compatible: - items: - - enum: - - apple,t8103-spi - - apple,t8112-spi - - apple,t6000-spi - - const: apple,spi + oneOf: + - items: + - const: apple,t6020-spi + - const: apple,t8103-spi + - items: + - enum: + - apple,t8103-spi + - apple,t8112-spi + - apple,t6000-spi + - const: apple,spi reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml b/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml index 2e20ca313ec1..d12c5a060ed0 100644 --- a/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml +++ b/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml @@ -25,6 +25,7 @@ description: allOf: - $ref: /schemas/spi/spi-controller.yaml# + - $ref: /schemas/soc/qcom/qcom,se-common-props.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/spmi/apple,spmi.yaml b/Documentation/devicetree/bindings/spmi/apple,spmi.yaml index 16bd7eb2b7af..ba524f1eb704 100644 --- a/Documentation/devicetree/bindings/spmi/apple,spmi.yaml +++ b/Documentation/devicetree/bindings/spmi/apple,spmi.yaml @@ -16,12 +16,20 @@ allOf: properties: compatible: - items: - - enum: - - apple,t8103-spmi - - apple,t6000-spmi - - apple,t8112-spmi - - const: apple,spmi + oneOf: + - items: + - enum: + - apple,t6020-spmi + - apple,t8012-spmi + - apple,t8015-spmi + - const: apple,t8103-spmi + - items: + - enum: + # Do not add additional SoC to this list. + - apple,t8103-spmi + - apple,t6000-spmi + - apple,t8112-spmi + - const: apple,spmi reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sram/qcom,imem.yaml b/Documentation/devicetree/bindings/sram/qcom,imem.yaml index 72d35e30c439..6a627c57ae2f 100644 --- a/Documentation/devicetree/bindings/sram/qcom,imem.yaml +++ b/Documentation/devicetree/bindings/sram/qcom,imem.yaml @@ -18,6 +18,7 @@ properties: items: - enum: - qcom,apq8064-imem + - qcom,ipq5424-imem - qcom,msm8226-imem - qcom,msm8974-imem - qcom,msm8976-imem diff --git a/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml b/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml index 310832fa8c28..05602678c070 100644 --- a/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/apple,wdt.yaml @@ -14,17 +14,22 @@ allOf: properties: compatible: - items: - - enum: - - apple,s5l8960x-wdt - - apple,t7000-wdt - - apple,s8000-wdt - - apple,t8010-wdt - - apple,t8015-wdt - - apple,t8103-wdt - - apple,t8112-wdt - - apple,t6000-wdt - - const: apple,wdt + oneOf: + - items: + - const: apple,t6020-wdt + - const: apple,t8103-wdt + - items: + - enum: + # Do not add additional SoC to this list. + - apple,s5l8960x-wdt + - apple,t7000-wdt + - apple,s8000-wdt + - apple,t8010-wdt + - apple,t8015-wdt + - apple,t8103-wdt + - apple,t8112-wdt + - apple,t6000-wdt + - const: apple,wdt reg: maxItems: 1 diff --git a/Documentation/tee/index.rst b/Documentation/tee/index.rst index 4be6e69d7837..62afb7ee9b52 100644 --- a/Documentation/tee/index.rst +++ b/Documentation/tee/index.rst @@ -11,6 +11,7 @@ TEE Subsystem op-tee amd-tee ts-tee + qtee .. only:: subproject and html diff --git a/Documentation/tee/qtee.rst b/Documentation/tee/qtee.rst new file mode 100644 index 000000000000..2fa2c1bf6384 --- /dev/null +++ b/Documentation/tee/qtee.rst @@ -0,0 +1,96 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================================= +QTEE (Qualcomm Trusted Execution Environment) +============================================= + +The QTEE driver handles communication with Qualcomm TEE [1]. + +The lowest level of communication with QTEE builds on the ARM SMC Calling +Convention (SMCCC) [2], which is the foundation for QTEE's Secure Channel +Manager (SCM) [3] used internally by the driver. + +In a QTEE-based system, services are represented as objects with a series of +operations that can be called to produce results, including other objects. + +When an object is hosted within QTEE, executing its operations is referred +to as "direct invocation". QTEE can also invoke objects hosted in the non-secure +world using a method known as "callback request". + +The SCM provides two functions to support direct invocation and callback requests: + +- QCOM_SCM_SMCINVOKE_INVOKE: Used for direct invocation. It can return either + a result or initiate a callback request. +- QCOM_SCM_SMCINVOKE_CB_RSP: Used to submit a response to a callback request + triggered by a previous direct invocation. + +The QTEE Transport Message [4] is stacked on top of the SCM driver functions. + +A message consists of two buffers shared with QTEE: inbound and outbound +buffers. The inbound buffer is used for direct invocation, and the outbound +buffer is used to make callback requests. This picture shows the contents of +a QTEE transport message:: + + +---------------------+ + | v + +-----------------+-------+-------+------+--------------------------+ + | qcomtee_msg_ |object | buffer | | + | object_invoke | id | offset, size | | (inbound buffer) + +-----------------+-------+--------------+--------------------------+ + <---- header -----><---- arguments ------><- in/out buffer payload -> + + +-----------+ + | v + +-----------------+-------+-------+------+----------------------+ + | qcomtee_msg_ |object | buffer | | + | callback | id | offset, size | | (outbound buffer) + +-----------------+-------+--------------+----------------------+ + +Each buffer is started with a header and array of arguments. + +QTEE Transport Message supports four types of arguments: + +- Input Object (IO) is an object parameter to the current invocation + or callback request. +- Output Object (OO) is an object parameter from the current invocation + or callback request. +- Input Buffer (IB) is (offset, size) pair to the inbound or outbound region + to store parameter to the current invocation or callback request. +- Output Buffer (OB) is (offset, size) pair to the inbound or outbound region + to store parameter from the current invocation or callback request. + +Picture of the relationship between the different components in the QTEE +architecture:: + + User space Kernel Secure world + ~~~~~~~~~~ ~~~~~~ ~~~~~~~~~~~~ + +--------+ +----------+ +--------------+ + | Client | |callback | | Trusted | + +--------+ |server | | Application | + /\ +----------+ +--------------+ + || +----------+ /\ /\ + || |callback | || || + || |server | || \/ + || +----------+ || +--------------+ + || /\ || | TEE Internal | + || || || | API | + \/ \/ \/ +--------+--------+ +--------------+ + +---------------------+ | TEE | QTEE | | QTEE | + | libqcomtee [5] | | subsys | driver | | Trusted OS | + +-------+-------------+--+----+-------+----+-------------+--------------+ + | Generic TEE API | | QTEE MSG | + | IOCTL (TEE_IOC_*) | | SMCCC (QCOM_SCM_SMCINVOKE_*) | + +-----------------------------+ +---------------------------------+ + +References +========== + +[1] https://docs.qualcomm.com/bundle/publicresource/topics/80-70015-11/qualcomm-trusted-execution-environment.html + +[2] http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html + +[3] drivers/firmware/qcom/qcom_scm.c + +[4] drivers/tee/qcomtee/qcomtee_msg.h + +[5] https://github.com/quic/quic-teec diff --git a/MAINTAINERS b/MAINTAINERS index e06a4ba02126..62a169218355 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21038,6 +21038,13 @@ F: Documentation/networking/device_drivers/cellular/qualcomm/rmnet.rst F: drivers/net/ethernet/qualcomm/rmnet/ F: include/linux/if_rmnet.h +QUALCOMM TEE (QCOMTEE) DRIVER +M: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com> +L: linux-arm-msm@vger.kernel.org +S: Maintained +F: Documentation/tee/qtee.rst +F: drivers/tee/qcomtee/ + QUALCOMM TRUST ZONE MEMORY ALLOCATOR M: Bartosz Golaszewski <bartosz.golaszewski@linaro.org> L: linux-arm-msm@vger.kernel.org @@ -21739,7 +21746,7 @@ F: drivers/thermal/renesas/rzg3e_thermal.c RESET CONTROLLER FRAMEWORK M: Philipp Zabel <p.zabel@pengutronix.de> S: Maintained -T: git git://git.pengutronix.de/git/pza/linux +T: git https://git.pengutronix.de/git/pza/linux.git F: Documentation/devicetree/bindings/reset/ F: Documentation/driver-api/reset.rst F: drivers/reset/ diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index 959f79d73b40..13173795c43d 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -138,20 +138,6 @@ config ARCH_EXYNOS help This enables support for ARMv8 based Samsung Exynos SoC family. -config ARCH_SPARX5 - bool "Microchip Sparx5 SoC family" - select PINCTRL - select DW_APB_TIMER_OF - help - This enables support for the Microchip Sparx5 ARMv8-based - SoC family of TSN-capable gigabit switches. - - The SparX-5 Ethernet switch family provides a rich set of - switching features such as advanced TCAM-based VLAN and QoS - processing enabling delivery of differentiated services, and - security through TCAM-based frame processing using versatile - content aware processor (VCAP). - config ARCH_K3 bool "Texas Instruments Inc. K3 multicore SoC architecture" select SOC_TI @@ -193,6 +179,43 @@ config ARCH_MESON This enables support for the arm64 based Amlogic SoCs such as the s905, S905X/D, S912, A113X/D or S905X/D2 +menu "Microchip SoC support" + +config ARCH_MICROCHIP + bool + +config ARCH_LAN969X + bool "Microchip LAN969X SoC family" + select PINCTRL + select DW_APB_TIMER_OF + select ARCH_MICROCHIP + help + This enables support for the Microchip LAN969X ARMv8-based + SoC family of TSN-capable gigabit switches. + + The LAN969X Ethernet switch family provides a rich set of + switching features such as advanced TCAM-based VLAN and QoS + processing enabling delivery of differentiated services, and + security through TCAM-based frame processing using versatile + content aware processor (VCAP). + +config ARCH_SPARX5 + bool "Microchip Sparx5 SoC family" + select PINCTRL + select DW_APB_TIMER_OF + select ARCH_MICROCHIP + help + This enables support for the Microchip Sparx5 ARMv8-based + SoC family of TSN-capable gigabit switches. + + The SparX-5 Ethernet switch family provides a rich set of + switching features such as advanced TCAM-based VLAN and QoS + processing enabling delivery of differentiated services, and + security through TCAM-based frame processing using versatile + content aware processor (VCAP). + +endmenu + config ARCH_MMP bool "Marvell MMP SoC Family" select PINCTRL diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 364eef32ddcc..c4e61843d9d9 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -232,7 +232,6 @@ config QE_GPIO bool "QE GPIO support" depends on QUICC_ENGINE select GPIOLIB - select OF_GPIO_MM_GPIOCHIP help Say Y here if you're going to use hardware that connects to the QE GPIOs. diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c index c1c0a4759c7e..25845c04e562 100644 --- a/drivers/bus/fsl-mc/fsl-mc-bus.c +++ b/drivers/bus/fsl-mc/fsl-mc-bus.c @@ -176,8 +176,8 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, { struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); - return sprintf(buf, "fsl-mc:v%08Xd%s\n", mc_dev->obj_desc.vendor, - mc_dev->obj_desc.type); + return sysfs_emit(buf, "fsl-mc:v%08Xd%s\n", mc_dev->obj_desc.vendor, + mc_dev->obj_desc.type); } static DEVICE_ATTR_RO(modalias); @@ -203,7 +203,7 @@ static ssize_t driver_override_show(struct device *dev, { struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", mc_dev->driver_override); + return sysfs_emit(buf, "%s\n", mc_dev->driver_override); } static DEVICE_ATTR_RW(driver_override); @@ -1104,6 +1104,9 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) * Get physical address of MC portal for the root DPRC: */ plat_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!plat_res) + return -EINVAL; + mc_portal_phys_addr = plat_res->start; mc_portal_size = resource_size(plat_res); mc_portal_base_phys_addr = mc_portal_phys_addr & ~0x3ffffff; diff --git a/drivers/cache/sifive_ccache.c b/drivers/cache/sifive_ccache.c index e1a283805ea7..a86800b123b9 100644 --- a/drivers/cache/sifive_ccache.c +++ b/drivers/cache/sifive_ccache.c @@ -151,16 +151,16 @@ static void ccache_flush_range(phys_addr_t start, size_t len) if (!len) return; - mb(); + mb(); /* complete earlier memory accesses before the cache flush */ for (line = ALIGN_DOWN(start, SIFIVE_CCACHE_LINE_SIZE); line < end; line += SIFIVE_CCACHE_LINE_SIZE) { #ifdef CONFIG_32BIT - writel(line >> 4, ccache_base + SIFIVE_CCACHE_FLUSH32); + writel_relaxed(line >> 4, ccache_base + SIFIVE_CCACHE_FLUSH32); #else - writeq(line, ccache_base + SIFIVE_CCACHE_FLUSH64); + writeq_relaxed(line, ccache_base + SIFIVE_CCACHE_FLUSH64); #endif - mb(); } + mb(); /* issue later memory accesses after the cache flush */ } static const struct riscv_nonstd_cache_ops ccache_mgmt_ops __initconst = { diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index c85827843447..e316cbc5baa9 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -77,7 +77,7 @@ config HW_RANDOM_AIROHA config HW_RANDOM_ATMEL tristate "Atmel Random Number Generator support" - depends on (ARCH_AT91 || COMPILE_TEST) + depends on (ARCH_MICROCHIP || COMPILE_TEST) default HW_RANDOM help This driver provides kernel-side support for the Random Number diff --git a/drivers/clk/clk-rp1.c b/drivers/clk/clk-rp1.c index afff90d48734..fd144755b879 100644 --- a/drivers/clk/clk-rp1.c +++ b/drivers/clk/clk-rp1.c @@ -368,6 +368,11 @@ struct rp1_clk_desc { struct clk_divider div; }; +static struct rp1_clk_desc *clk_audio_core; +static struct rp1_clk_desc *clk_audio; +static struct rp1_clk_desc *clk_i2s; +static struct clk_hw *clk_xosc; + static inline void clockman_write(struct rp1_clockman *clockman, u32 reg, u32 val) { @@ -475,7 +480,6 @@ static int rp1_pll_core_set_rate(struct clk_hw *hw, struct rp1_clk_desc *pll_core = container_of(hw, struct rp1_clk_desc, hw); struct rp1_clockman *clockman = pll_core->clockman; const struct rp1_pll_core_data *data = pll_core->data; - unsigned long calc_rate; u32 fbdiv_int, fbdiv_frac; /* Disable dividers to start with. */ @@ -484,8 +488,8 @@ static int rp1_pll_core_set_rate(struct clk_hw *hw, clockman_write(clockman, data->fbdiv_frac_reg, 0); spin_unlock(&clockman->regs_lock); - calc_rate = get_pll_core_divider(hw, rate, parent_rate, - &fbdiv_int, &fbdiv_frac); + get_pll_core_divider(hw, rate, parent_rate, + &fbdiv_int, &fbdiv_frac); spin_lock(&clockman->regs_lock); clockman_write(clockman, data->pwr_reg, fbdiv_frac ? 0 : PLL_PWR_DSMPD); @@ -497,8 +501,6 @@ static int rp1_pll_core_set_rate(struct clk_hw *hw, if (WARN_ON_ONCE(parent_rate > (rate / 16))) return -ERANGE; - pll_core->cached_rate = calc_rate; - spin_lock(&clockman->regs_lock); /* Don't need to divide ref unless parent_rate > (output freq / 16) */ clockman_write(clockman, data->cs_reg, @@ -530,13 +532,16 @@ static unsigned long rp1_pll_core_recalc_rate(struct clk_hw *hw, return calc_rate; } -static long rp1_pll_core_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int rp1_pll_core_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { u32 fbdiv_int, fbdiv_frac; - return get_pll_core_divider(hw, rate, *parent_rate, - &fbdiv_int, &fbdiv_frac); + req->rate = get_pll_core_divider(hw, req->rate, req->best_parent_rate, + &fbdiv_int, + &fbdiv_frac); + + return 0; } static void get_pll_prim_dividers(unsigned long rate, unsigned long parent_rate, @@ -614,14 +619,20 @@ static unsigned long rp1_pll_recalc_rate(struct clk_hw *hw, return DIV_ROUND_CLOSEST(parent_rate, prim_div1 * prim_div2); } -static long rp1_pll_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int rp1_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { + struct clk_hw *clk_audio_hw = &clk_audio->hw; u32 div1, div2; - get_pll_prim_dividers(rate, *parent_rate, &div1, &div2); + if (hw == clk_audio_hw && clk_audio->cached_rate == req->rate) + req->best_parent_rate = clk_audio_core->cached_rate; - return DIV_ROUND_CLOSEST(*parent_rate, div1 * div2); + get_pll_prim_dividers(req->rate, req->best_parent_rate, &div1, &div2); + + req->rate = DIV_ROUND_CLOSEST(req->best_parent_rate, div1 * div2); + + return 0; } static int rp1_pll_ph_is_on(struct clk_hw *hw) @@ -671,13 +682,15 @@ static unsigned long rp1_pll_ph_recalc_rate(struct clk_hw *hw, return parent_rate / data->fixed_divider; } -static long rp1_pll_ph_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int rp1_pll_ph_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct rp1_clk_desc *pll_ph = container_of(hw, struct rp1_clk_desc, hw); const struct rp1_pll_ph_data *data = pll_ph->data; - return *parent_rate / data->fixed_divider; + req->rate = req->best_parent_rate / data->fixed_divider; + + return 0; } static int rp1_pll_divider_is_on(struct clk_hw *hw) @@ -754,11 +767,12 @@ static unsigned long rp1_pll_divider_recalc_rate(struct clk_hw *hw, return clk_divider_ops.recalc_rate(hw, parent_rate); } -static long rp1_pll_divider_round_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long *parent_rate) +static int rp1_pll_divider_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - return clk_divider_ops.round_rate(hw, rate, parent_rate); + req->rate = clk_divider_ops.determine_rate(hw, req); + + return 0; } static int rp1_clock_is_on(struct clk_hw *hw) @@ -964,6 +978,59 @@ static int rp1_clock_set_rate(struct clk_hw *hw, unsigned long rate, return rp1_clock_set_rate_and_parent(hw, rate, parent_rate, 0xff); } +static unsigned long calc_core_pll_rate(struct clk_hw *pll_hw, + unsigned long target_rate, + int *pdiv_prim, int *pdiv_clk) +{ + static const int prim_divs[] = { + 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, + 18, 20, 21, 24, 25, 28, 30, 35, 36, 42, 49, + }; + const unsigned long xosc_rate = clk_hw_get_rate(clk_xosc); + const unsigned long core_min = xosc_rate * 16; + const unsigned long core_max = 2400000000; + int best_div_prim = 1, best_div_clk = 1; + unsigned long best_rate = core_max + 1; + unsigned long core_rate = 0; + int div_int, div_frac; + u64 div; + int i; + + /* Given the target rate, choose a set of divisors/multipliers */ + for (i = 0; i < ARRAY_SIZE(prim_divs); i++) { + int div_prim = prim_divs[i]; + int div_clk; + + for (div_clk = 1; div_clk <= 256; div_clk++) { + core_rate = target_rate * div_clk * div_prim; + if (core_rate >= core_min) { + if (core_rate < best_rate) { + best_rate = core_rate; + best_div_prim = div_prim; + best_div_clk = div_clk; + } + break; + } + } + } + + if (best_rate < core_max) { + div = ((best_rate << 24) + xosc_rate / 2) / xosc_rate; + div_int = div >> 24; + div_frac = div % (1 << 24); + core_rate = (xosc_rate * ((div_int << 24) + div_frac) + (1 << 23)) >> 24; + } else { + core_rate = 0; + } + + if (pdiv_prim) + *pdiv_prim = best_div_prim; + if (pdiv_clk) + *pdiv_clk = best_div_clk; + + return core_rate; +} + static void rp1_clock_choose_div_and_prate(struct clk_hw *hw, int parent_idx, unsigned long rate, @@ -972,12 +1039,35 @@ static void rp1_clock_choose_div_and_prate(struct clk_hw *hw, { struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); const struct rp1_clock_data *data = clock->data; + struct clk_hw *clk_audio_hw = &clk_audio->hw; + struct clk_hw *clk_i2s_hw = &clk_i2s->hw; struct clk_hw *parent; u32 div; u64 tmp; parent = clk_hw_get_parent_by_index(hw, parent_idx); + if (hw == clk_i2s_hw && clk_i2s->cached_rate == rate && parent == clk_audio_hw) { + *prate = clk_audio->cached_rate; + *calc_rate = rate; + return; + } + + if (hw == clk_i2s_hw && parent == clk_audio_hw) { + unsigned long core_rate, audio_rate, i2s_rate; + int div_prim, div_clk; + + core_rate = calc_core_pll_rate(parent, rate, &div_prim, &div_clk); + audio_rate = DIV_ROUND_CLOSEST(core_rate, div_prim); + i2s_rate = DIV_ROUND_CLOSEST(audio_rate, div_clk); + clk_audio_core->cached_rate = core_rate; + clk_audio->cached_rate = audio_rate; + clk_i2s->cached_rate = i2s_rate; + *prate = audio_rate; + *calc_rate = i2s_rate; + return; + } + *prate = clk_hw_get_rate(parent); div = rp1_clock_choose_div(rate, *prate, data); @@ -1062,19 +1152,47 @@ static int rp1_clock_determine_rate(struct clk_hw *hw, return 0; } +static int rp1_varsrc_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + + /* + * "varsrc" exists purely to let clock dividers know the frequency + * of an externally-managed clock source (such as MIPI DSI byte-clock) + * which may change at run-time as a side-effect of some other driver. + */ + clock->cached_rate = rate; + return 0; +} + +static unsigned long rp1_varsrc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + + return clock->cached_rate; +} + +static int rp1_varsrc_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return 0; +} + static const struct clk_ops rp1_pll_core_ops = { .is_prepared = rp1_pll_core_is_on, .prepare = rp1_pll_core_on, .unprepare = rp1_pll_core_off, .set_rate = rp1_pll_core_set_rate, .recalc_rate = rp1_pll_core_recalc_rate, - .round_rate = rp1_pll_core_round_rate, + .determine_rate = rp1_pll_core_determine_rate, }; static const struct clk_ops rp1_pll_ops = { .set_rate = rp1_pll_set_rate, .recalc_rate = rp1_pll_recalc_rate, - .round_rate = rp1_pll_round_rate, + .determine_rate = rp1_pll_determine_rate, }; static const struct clk_ops rp1_pll_ph_ops = { @@ -1082,7 +1200,7 @@ static const struct clk_ops rp1_pll_ph_ops = { .prepare = rp1_pll_ph_on, .unprepare = rp1_pll_ph_off, .recalc_rate = rp1_pll_ph_recalc_rate, - .round_rate = rp1_pll_ph_round_rate, + .determine_rate = rp1_pll_ph_determine_rate, }; static const struct clk_ops rp1_pll_divider_ops = { @@ -1091,7 +1209,7 @@ static const struct clk_ops rp1_pll_divider_ops = { .unprepare = rp1_pll_divider_off, .set_rate = rp1_pll_divider_set_rate, .recalc_rate = rp1_pll_divider_recalc_rate, - .round_rate = rp1_pll_divider_round_rate, + .determine_rate = rp1_pll_divider_determine_rate, }; static const struct clk_ops rp1_clk_ops = { @@ -1106,6 +1224,12 @@ static const struct clk_ops rp1_clk_ops = { .determine_rate = rp1_clock_determine_rate, }; +static const struct clk_ops rp1_varsrc_ops = { + .set_rate = rp1_varsrc_set_rate, + .recalc_rate = rp1_varsrc_recalc_rate, + .determine_rate = rp1_varsrc_determine_rate, +}; + static struct clk_hw *rp1_register_pll(struct rp1_clockman *clockman, struct rp1_clk_desc *desc) { @@ -1241,6 +1365,36 @@ static struct rp1_clk_desc pll_sys_desc = REGISTER_PLL( ) ); +static struct rp1_clk_desc pll_audio_desc = REGISTER_PLL( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_audio", + (const struct clk_parent_data[]) { + { .hw = &pll_audio_core_desc.hw } + }, + &rp1_pll_ops, + CLK_SET_RATE_PARENT + ), + CLK_DATA(rp1_pll_data, + .ctrl_reg = PLL_AUDIO_PRIM, + .fc0_src = FC_NUM(4, 2), + ) +); + +static struct rp1_clk_desc pll_video_desc = REGISTER_PLL( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_video", + (const struct clk_parent_data[]) { + { .hw = &pll_video_core_desc.hw } + }, + &rp1_pll_ops, + 0 + ), + CLK_DATA(rp1_pll_data, + .ctrl_reg = PLL_VIDEO_PRIM, + .fc0_src = FC_NUM(3, 2), + ) +); + static struct rp1_clk_desc pll_sys_sec_desc = REGISTER_PLL_DIV( .hw.init = CLK_HW_INIT_PARENTS_DATA( "pll_sys_sec", @@ -1256,16 +1410,42 @@ static struct rp1_clk_desc pll_sys_sec_desc = REGISTER_PLL_DIV( ) ); +static struct rp1_clk_desc pll_video_sec_desc = REGISTER_PLL_DIV( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_video_sec", + (const struct clk_parent_data[]) { + { .hw = &pll_video_core_desc.hw } + }, + &rp1_pll_divider_ops, + 0 + ), + CLK_DATA(rp1_pll_data, + .ctrl_reg = PLL_VIDEO_SEC, + .fc0_src = FC_NUM(5, 3), + ) +); + +static const struct clk_parent_data clk_eth_tsu_parents[] = { + { .index = 0 }, + { .hw = &pll_video_sec_desc.hw }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, +}; + static struct rp1_clk_desc clk_eth_tsu_desc = REGISTER_CLK( .hw.init = CLK_HW_INIT_PARENTS_DATA( "clk_eth_tsu", - (const struct clk_parent_data[]) { { .index = 0 } }, + clk_eth_tsu_parents, &rp1_clk_ops, 0 ), CLK_DATA(rp1_clock_data, .num_std_parents = 0, - .num_aux_parents = 1, + .num_aux_parents = 8, .ctrl_reg = CLK_ETH_TSU_CTRL, .div_int_reg = CLK_ETH_TSU_DIV_INT, .sel_reg = CLK_ETH_TSU_SEL, @@ -1278,6 +1458,7 @@ static struct rp1_clk_desc clk_eth_tsu_desc = REGISTER_CLK( static const struct clk_parent_data clk_eth_parents[] = { { .hw = &pll_sys_sec_desc.div.hw }, { .hw = &pll_sys_desc.hw }, + { .hw = &pll_video_sec_desc.hw }, }; static struct rp1_clk_desc clk_eth_desc = REGISTER_CLK( @@ -1289,7 +1470,7 @@ static struct rp1_clk_desc clk_eth_desc = REGISTER_CLK( ), CLK_DATA(rp1_clock_data, .num_std_parents = 0, - .num_aux_parents = 2, + .num_aux_parents = 3, .ctrl_reg = CLK_ETH_CTRL, .div_int_reg = CLK_ETH_DIV_INT, .sel_reg = CLK_ETH_SEL, @@ -1342,6 +1523,756 @@ static struct rp1_clk_desc pll_sys_pri_ph_desc = REGISTER_PLL( ) ); +static struct rp1_clk_desc pll_audio_pri_ph_desc = REGISTER_PLL( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_audio_pri_ph", + (const struct clk_parent_data[]) { + { .hw = &pll_audio_desc.hw } + }, + &rp1_pll_ph_ops, + 0 + ), + CLK_DATA(rp1_pll_ph_data, + .ph_reg = PLL_AUDIO_PRIM, + .fixed_divider = 2, + .phase = RP1_PLL_PHASE_0, + .fc0_src = FC_NUM(5, 1), + ) +); + +static struct rp1_clk_desc pll_video_pri_ph_desc = REGISTER_PLL( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_video_pri_ph", + (const struct clk_parent_data[]) { + { .hw = &pll_video_desc.hw } + }, + &rp1_pll_ph_ops, + 0 + ), + CLK_DATA(rp1_pll_ph_data, + .ph_reg = PLL_VIDEO_PRIM, + .fixed_divider = 2, + .phase = RP1_PLL_PHASE_0, + .fc0_src = FC_NUM(4, 3), + ) +); + +static struct rp1_clk_desc pll_audio_sec_desc = REGISTER_PLL_DIV( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_audio_sec", + (const struct clk_parent_data[]) { + { .hw = &pll_audio_core_desc.hw } + }, + &rp1_pll_divider_ops, + 0 + ), + CLK_DATA(rp1_pll_data, + .ctrl_reg = PLL_AUDIO_SEC, + .fc0_src = FC_NUM(6, 2), + ) +); + +static struct rp1_clk_desc pll_audio_tern_desc = REGISTER_PLL_DIV( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_audio_tern", + (const struct clk_parent_data[]) { + { .hw = &pll_audio_core_desc.hw } + }, + &rp1_pll_divider_ops, + 0 + ), + CLK_DATA(rp1_pll_data, + .ctrl_reg = PLL_AUDIO_TERN, + .fc0_src = FC_NUM(6, 2), + ) +); + +static struct rp1_clk_desc clk_slow_sys_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_slow_sys", + (const struct clk_parent_data[]) { { .index = 0 } }, + &rp1_clk_ops, + CLK_IS_CRITICAL + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 1, + .num_aux_parents = 0, + .ctrl_reg = CLK_SLOW_SYS_CTRL, + .div_int_reg = CLK_SLOW_SYS_DIV_INT, + .sel_reg = CLK_SLOW_SYS_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 50 * HZ_PER_MHZ, + .fc0_src = FC_NUM(1, 4), + .clk_src_mask = 0x1, + ) +); + +static const struct clk_parent_data clk_dma_parents[] = { + { .hw = &pll_sys_pri_ph_desc.hw }, + { .hw = &pll_video_desc.hw }, + { .index = 0 }, +}; + +static struct rp1_clk_desc clk_dma_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_dma", + clk_dma_parents, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 3, + .ctrl_reg = CLK_DMA_CTRL, + .div_int_reg = CLK_DMA_DIV_INT, + .sel_reg = CLK_DMA_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 100 * HZ_PER_MHZ, + .fc0_src = FC_NUM(2, 2), + ) +); + +static const struct clk_parent_data clk_uart_parents[] = { + { .hw = &pll_sys_pri_ph_desc.hw }, + { .hw = &pll_video_desc.hw }, + { .index = 0 }, +}; + +static struct rp1_clk_desc clk_uart_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_uart", + clk_uart_parents, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 3, + .ctrl_reg = CLK_UART_CTRL, + .div_int_reg = CLK_UART_DIV_INT, + .sel_reg = CLK_UART_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 100 * HZ_PER_MHZ, + .fc0_src = FC_NUM(6, 7), + ) +); + +static const struct clk_parent_data clk_pwm0_parents[] = { + { .index = -1 }, + { .hw = &pll_video_sec_desc.hw }, + { .index = 0 }, +}; + +static struct rp1_clk_desc clk_pwm0_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_pwm0", + clk_pwm0_parents, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 3, + .ctrl_reg = CLK_PWM0_CTRL, + .div_int_reg = CLK_PWM0_DIV_INT, + .div_frac_reg = CLK_PWM0_DIV_FRAC, + .sel_reg = CLK_PWM0_SEL, + .div_int_max = DIV_INT_16BIT_MAX, + .max_freq = 76800 * HZ_PER_KHZ, + .fc0_src = FC_NUM(0, 5), + ) +); + +static const struct clk_parent_data clk_pwm1_parents[] = { + { .index = -1 }, + { .hw = &pll_video_sec_desc.hw }, + { .index = 0 }, +}; + +static struct rp1_clk_desc clk_pwm1_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_pwm1", + clk_pwm1_parents, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 3, + .ctrl_reg = CLK_PWM1_CTRL, + .div_int_reg = CLK_PWM1_DIV_INT, + .div_frac_reg = CLK_PWM1_DIV_FRAC, + .sel_reg = CLK_PWM1_SEL, + .div_int_max = DIV_INT_16BIT_MAX, + .max_freq = 76800 * HZ_PER_KHZ, + .fc0_src = FC_NUM(1, 5), + ) +); + +static const struct clk_parent_data clk_audio_in_parents[] = { + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .hw = &pll_video_sec_desc.hw }, + { .index = 0 }, +}; + +static struct rp1_clk_desc clk_audio_in_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_audio_in", + clk_audio_in_parents, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 5, + .ctrl_reg = CLK_AUDIO_IN_CTRL, + .div_int_reg = CLK_AUDIO_IN_DIV_INT, + .sel_reg = CLK_AUDIO_IN_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 76800 * HZ_PER_KHZ, + .fc0_src = FC_NUM(2, 5), + ) +); + +static const struct clk_parent_data clk_audio_out_parents[] = { + { .index = -1 }, + { .index = -1 }, + { .hw = &pll_video_sec_desc.hw }, + { .index = 0 }, +}; + +static struct rp1_clk_desc clk_audio_out_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_audio_out", + clk_audio_out_parents, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 4, + .ctrl_reg = CLK_AUDIO_OUT_CTRL, + .div_int_reg = CLK_AUDIO_OUT_DIV_INT, + .sel_reg = CLK_AUDIO_OUT_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 153600 * HZ_PER_KHZ, + .fc0_src = FC_NUM(3, 5), + ) +); + +static const struct clk_parent_data clk_i2s_parents[] = { + { .index = 0 }, + { .hw = &pll_audio_desc.hw }, + { .hw = &pll_audio_sec_desc.hw }, +}; + +static struct rp1_clk_desc clk_i2s_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_i2s", + clk_i2s_parents, + &rp1_clk_ops, + CLK_SET_RATE_PARENT + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 3, + .ctrl_reg = CLK_I2S_CTRL, + .div_int_reg = CLK_I2S_DIV_INT, + .sel_reg = CLK_I2S_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 50 * HZ_PER_MHZ, + .fc0_src = FC_NUM(4, 4), + ) +); + +static struct rp1_clk_desc clk_mipi0_cfg_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_mipi0_cfg", + (const struct clk_parent_data[]) { { .index = 0 } }, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 1, + .ctrl_reg = CLK_MIPI0_CFG_CTRL, + .div_int_reg = CLK_MIPI0_CFG_DIV_INT, + .sel_reg = CLK_MIPI0_CFG_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 50 * HZ_PER_MHZ, + .fc0_src = FC_NUM(4, 5), + ) +); + +static struct rp1_clk_desc clk_mipi1_cfg_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_mipi1_cfg", + (const struct clk_parent_data[]) { { .index = 0 } }, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 1, + .ctrl_reg = CLK_MIPI1_CFG_CTRL, + .div_int_reg = CLK_MIPI1_CFG_DIV_INT, + .sel_reg = CLK_MIPI1_CFG_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 50 * HZ_PER_MHZ, + .fc0_src = FC_NUM(5, 6), + .clk_src_mask = 0x1, + ) +); + +static struct rp1_clk_desc clk_adc_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_adc", + (const struct clk_parent_data[]) { { .index = 0 } }, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 1, + .ctrl_reg = CLK_ADC_CTRL, + .div_int_reg = CLK_ADC_DIV_INT, + .sel_reg = CLK_ADC_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 50 * HZ_PER_MHZ, + .fc0_src = FC_NUM(5, 5), + ) +); + +static struct rp1_clk_desc clk_sdio_timer_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_sdio_timer", + (const struct clk_parent_data[]) { { .index = 0 } }, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 1, + .ctrl_reg = CLK_SDIO_TIMER_CTRL, + .div_int_reg = CLK_SDIO_TIMER_DIV_INT, + .sel_reg = CLK_SDIO_TIMER_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 50 * HZ_PER_MHZ, + .fc0_src = FC_NUM(3, 4), + ) +); + +static struct rp1_clk_desc clk_sdio_alt_src_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_sdio_alt_src", + (const struct clk_parent_data[]) { + { .hw = &pll_sys_desc.hw } + }, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 1, + .ctrl_reg = CLK_SDIO_ALT_SRC_CTRL, + .div_int_reg = CLK_SDIO_ALT_SRC_DIV_INT, + .sel_reg = CLK_SDIO_ALT_SRC_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 200 * HZ_PER_MHZ, + .fc0_src = FC_NUM(5, 4), + ) +); + +static const struct clk_parent_data clk_dpi_parents[] = { + { .hw = &pll_sys_desc.hw }, + { .hw = &pll_video_sec_desc.hw }, + { .hw = &pll_video_desc.hw }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, +}; + +static struct rp1_clk_desc clk_dpi_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_dpi", + clk_dpi_parents, + &rp1_clk_ops, + CLK_SET_RATE_NO_REPARENT /* Let DPI driver set parent */ + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 8, + .ctrl_reg = VIDEO_CLK_DPI_CTRL, + .div_int_reg = VIDEO_CLK_DPI_DIV_INT, + .sel_reg = VIDEO_CLK_DPI_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 200 * HZ_PER_MHZ, + .fc0_src = FC_NUM(1, 6), + ) +); + +static const struct clk_parent_data clk_gp0_parents[] = { + { .index = 0 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .hw = &pll_sys_desc.hw }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .hw = &clk_i2s_desc.hw }, + { .hw = &clk_adc_desc.hw }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .hw = &clk_sys_desc.hw }, +}; + +static struct rp1_clk_desc clk_gp0_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_gp0", + clk_gp0_parents, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 16, + .oe_mask = BIT(0), + .ctrl_reg = CLK_GP0_CTRL, + .div_int_reg = CLK_GP0_DIV_INT, + .div_frac_reg = CLK_GP0_DIV_FRAC, + .sel_reg = CLK_GP0_SEL, + .div_int_max = DIV_INT_16BIT_MAX, + .max_freq = 100 * HZ_PER_MHZ, + .fc0_src = FC_NUM(0, 1), + ) +); + +static const struct clk_parent_data clk_gp1_parents[] = { + { .hw = &clk_sdio_timer_desc.hw }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .hw = &pll_sys_pri_ph_desc.hw }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .hw = &clk_adc_desc.hw }, + { .hw = &clk_dpi_desc.hw }, + { .hw = &clk_pwm0_desc.hw }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, +}; + +static struct rp1_clk_desc clk_gp1_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_gp1", + clk_gp1_parents, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 16, + .oe_mask = BIT(1), + .ctrl_reg = CLK_GP1_CTRL, + .div_int_reg = CLK_GP1_DIV_INT, + .div_frac_reg = CLK_GP1_DIV_FRAC, + .sel_reg = CLK_GP1_SEL, + .div_int_max = DIV_INT_16BIT_MAX, + .max_freq = 100 * HZ_PER_MHZ, + .fc0_src = FC_NUM(1, 1), + ) +); + +static struct rp1_clk_desc clksrc_mipi0_dsi_byteclk_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clksrc_mipi0_dsi_byteclk", + (const struct clk_parent_data[]) { { .index = 0 } }, + &rp1_varsrc_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 1, + .num_aux_parents = 0, + ) +); + +static struct rp1_clk_desc clksrc_mipi1_dsi_byteclk_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clksrc_mipi1_dsi_byteclk", + (const struct clk_parent_data[]) { { .index = 0 } }, + &rp1_varsrc_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 1, + .num_aux_parents = 0, + ) +); + +static const struct clk_parent_data clk_mipi0_dpi_parents[] = { + { .hw = &pll_sys_desc.hw }, + { .hw = &pll_video_sec_desc.hw }, + { .hw = &pll_video_desc.hw }, + { .hw = &clksrc_mipi0_dsi_byteclk_desc.hw }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, +}; + +static struct rp1_clk_desc clk_mipi0_dpi_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_mipi0_dpi", + clk_mipi0_dpi_parents, + &rp1_clk_ops, + CLK_SET_RATE_NO_REPARENT /* Let DSI driver set parent */ + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 8, + .ctrl_reg = VIDEO_CLK_MIPI0_DPI_CTRL, + .div_int_reg = VIDEO_CLK_MIPI0_DPI_DIV_INT, + .div_frac_reg = VIDEO_CLK_MIPI0_DPI_DIV_FRAC, + .sel_reg = VIDEO_CLK_MIPI0_DPI_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 200 * HZ_PER_MHZ, + .fc0_src = FC_NUM(2, 6), + ) +); + +static const struct clk_parent_data clk_mipi1_dpi_parents[] = { + { .hw = &pll_sys_desc.hw }, + { .hw = &pll_video_sec_desc.hw }, + { .hw = &pll_video_desc.hw }, + { .hw = &clksrc_mipi1_dsi_byteclk_desc.hw }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, +}; + +static struct rp1_clk_desc clk_mipi1_dpi_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_mipi1_dpi", + clk_mipi1_dpi_parents, + &rp1_clk_ops, + CLK_SET_RATE_NO_REPARENT /* Let DSI driver set parent */ + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 8, + .ctrl_reg = VIDEO_CLK_MIPI1_DPI_CTRL, + .div_int_reg = VIDEO_CLK_MIPI1_DPI_DIV_INT, + .div_frac_reg = VIDEO_CLK_MIPI1_DPI_DIV_FRAC, + .sel_reg = VIDEO_CLK_MIPI1_DPI_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 200 * HZ_PER_MHZ, + .fc0_src = FC_NUM(3, 6), + ) +); + +static const struct clk_parent_data clk_gp2_parents[] = { + { .hw = &clk_sdio_alt_src_desc.hw }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .hw = &pll_sys_sec_desc.hw }, + { .index = -1 }, + { .hw = &pll_video_desc.hw }, + { .hw = &clk_audio_in_desc.hw }, + { .hw = &clk_dpi_desc.hw }, + { .hw = &clk_pwm0_desc.hw }, + { .hw = &clk_pwm1_desc.hw }, + { .hw = &clk_mipi0_dpi_desc.hw }, + { .hw = &clk_mipi1_cfg_desc.hw }, + { .hw = &clk_sys_desc.hw }, +}; + +static struct rp1_clk_desc clk_gp2_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_gp2", + clk_gp2_parents, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 16, + .oe_mask = BIT(2), + .ctrl_reg = CLK_GP2_CTRL, + .div_int_reg = CLK_GP2_DIV_INT, + .div_frac_reg = CLK_GP2_DIV_FRAC, + .sel_reg = CLK_GP2_SEL, + .div_int_max = DIV_INT_16BIT_MAX, + .max_freq = 100 * HZ_PER_MHZ, + .fc0_src = FC_NUM(2, 1), + ) +); + +static const struct clk_parent_data clk_gp3_parents[] = { + { .index = 0 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .hw = &pll_video_pri_ph_desc.hw }, + { .hw = &clk_audio_out_desc.hw }, + { .index = -1 }, + { .index = -1 }, + { .hw = &clk_mipi1_dpi_desc.hw }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, +}; + +static struct rp1_clk_desc clk_gp3_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_gp3", + clk_gp3_parents, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 16, + .oe_mask = BIT(3), + .ctrl_reg = CLK_GP3_CTRL, + .div_int_reg = CLK_GP3_DIV_INT, + .div_frac_reg = CLK_GP3_DIV_FRAC, + .sel_reg = CLK_GP3_SEL, + .div_int_max = DIV_INT_16BIT_MAX, + .max_freq = 100 * HZ_PER_MHZ, + .fc0_src = FC_NUM(3, 1), + ) +); + +static const struct clk_parent_data clk_gp4_parents[] = { + { .index = 0 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .hw = &pll_video_sec_desc.hw }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .hw = &clk_mipi0_cfg_desc.hw }, + { .hw = &clk_uart_desc.hw }, + { .index = -1 }, + { .index = -1 }, + { .hw = &clk_sys_desc.hw }, +}; + +static struct rp1_clk_desc clk_gp4_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_gp4", + clk_gp4_parents, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 16, + .oe_mask = BIT(4), + .ctrl_reg = CLK_GP4_CTRL, + .div_int_reg = CLK_GP4_DIV_INT, + .div_frac_reg = CLK_GP4_DIV_FRAC, + .sel_reg = CLK_GP4_SEL, + .div_int_max = DIV_INT_16BIT_MAX, + .max_freq = 100 * HZ_PER_MHZ, + .fc0_src = FC_NUM(4, 1), + ) +); + +static const struct clk_parent_data clk_vec_parents[] = { + { .hw = &pll_sys_pri_ph_desc.hw }, + { .hw = &pll_video_sec_desc.hw }, + { .hw = &pll_video_desc.hw }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, +}; + +static struct rp1_clk_desc clk_vec_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_vec", + clk_vec_parents, + &rp1_clk_ops, + CLK_SET_RATE_NO_REPARENT /* Let VEC driver set parent */ + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 8, + .ctrl_reg = VIDEO_CLK_VEC_CTRL, + .div_int_reg = VIDEO_CLK_VEC_DIV_INT, + .sel_reg = VIDEO_CLK_VEC_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 108 * HZ_PER_MHZ, + .fc0_src = FC_NUM(0, 6), + ) +); + +static const struct clk_parent_data clk_gp5_parents[] = { + { .index = 0 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .hw = &pll_video_sec_desc.hw }, + { .hw = &clk_eth_tsu_desc.hw }, + { .index = -1 }, + { .hw = &clk_vec_desc.hw }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, +}; + +static struct rp1_clk_desc clk_gp5_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_gp5", + clk_gp5_parents, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 16, + .oe_mask = BIT(5), + .ctrl_reg = CLK_GP5_CTRL, + .div_int_reg = CLK_GP5_DIV_INT, + .div_frac_reg = CLK_GP5_DIV_FRAC, + .sel_reg = CLK_GP5_SEL, + .div_int_max = DIV_INT_16BIT_MAX, + .max_freq = 100 * HZ_PER_MHZ, + .fc0_src = FC_NUM(5, 1), + ) +); + static struct rp1_clk_desc *const clk_desc_array[] = { [RP1_PLL_SYS_CORE] = &pll_sys_core_desc, [RP1_PLL_AUDIO_CORE] = &pll_audio_core_desc, @@ -1352,6 +2283,38 @@ static struct rp1_clk_desc *const clk_desc_array[] = { [RP1_CLK_SYS] = &clk_sys_desc, [RP1_PLL_SYS_PRI_PH] = &pll_sys_pri_ph_desc, [RP1_PLL_SYS_SEC] = &pll_sys_sec_desc, + [RP1_PLL_AUDIO] = &pll_audio_desc, + [RP1_PLL_VIDEO] = &pll_video_desc, + [RP1_PLL_AUDIO_PRI_PH] = &pll_audio_pri_ph_desc, + [RP1_PLL_VIDEO_PRI_PH] = &pll_video_pri_ph_desc, + [RP1_PLL_AUDIO_SEC] = &pll_audio_sec_desc, + [RP1_PLL_VIDEO_SEC] = &pll_video_sec_desc, + [RP1_PLL_AUDIO_TERN] = &pll_audio_tern_desc, + [RP1_CLK_SLOW_SYS] = &clk_slow_sys_desc, + [RP1_CLK_DMA] = &clk_dma_desc, + [RP1_CLK_UART] = &clk_uart_desc, + [RP1_CLK_PWM0] = &clk_pwm0_desc, + [RP1_CLK_PWM1] = &clk_pwm1_desc, + [RP1_CLK_AUDIO_IN] = &clk_audio_in_desc, + [RP1_CLK_AUDIO_OUT] = &clk_audio_out_desc, + [RP1_CLK_I2S] = &clk_i2s_desc, + [RP1_CLK_MIPI0_CFG] = &clk_mipi0_cfg_desc, + [RP1_CLK_MIPI1_CFG] = &clk_mipi1_cfg_desc, + [RP1_CLK_ADC] = &clk_adc_desc, + [RP1_CLK_SDIO_TIMER] = &clk_sdio_timer_desc, + [RP1_CLK_SDIO_ALT_SRC] = &clk_sdio_alt_src_desc, + [RP1_CLK_GP0] = &clk_gp0_desc, + [RP1_CLK_GP1] = &clk_gp1_desc, + [RP1_CLK_GP2] = &clk_gp2_desc, + [RP1_CLK_GP3] = &clk_gp3_desc, + [RP1_CLK_GP4] = &clk_gp4_desc, + [RP1_CLK_GP5] = &clk_gp5_desc, + [RP1_CLK_VEC] = &clk_vec_desc, + [RP1_CLK_DPI] = &clk_dpi_desc, + [RP1_CLK_MIPI0_DPI] = &clk_mipi0_dpi_desc, + [RP1_CLK_MIPI1_DPI] = &clk_mipi1_dpi_desc, + [RP1_CLK_MIPI0_DSI_BYTECLOCK] = &clksrc_mipi0_dsi_byteclk_desc, + [RP1_CLK_MIPI1_DSI_BYTECLOCK] = &clksrc_mipi1_dsi_byteclk_desc, }; static const struct regmap_range rp1_reg_ranges[] = { @@ -1466,6 +2429,11 @@ static int rp1_clk_probe(struct platform_device *pdev) hws[i] = desc->clk_register(clockman, desc); } + clk_audio_core = &pll_audio_core_desc; + clk_audio = &pll_audio_desc; + clk_i2s = &clk_i2s_desc; + clk_xosc = clk_hw_get_parent_by_index(&clk_i2s->hw, 0); + platform_set_drvdata(pdev, clockman); return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index c7a1060ba57a..76ea9273d1dc 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -439,7 +439,7 @@ config CRYPTO_DEV_ATMEL_AUTHENC config CRYPTO_DEV_ATMEL_AES tristate "Support for Atmel AES hw accelerator" - depends on ARCH_AT91 || COMPILE_TEST + depends on ARCH_MICROCHIP || COMPILE_TEST select CRYPTO_AES select CRYPTO_AEAD select CRYPTO_SKCIPHER diff --git a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c index 3cbe87d4a464..8ab49924f8b7 100644 --- a/drivers/dma-buf/dma-heap.c +++ b/drivers/dma-buf/dma-heap.c @@ -11,6 +11,7 @@ #include <linux/dma-buf.h> #include <linux/dma-heap.h> #include <linux/err.h> +#include <linux/export.h> #include <linux/list.h> #include <linux/nospec.h> #include <linux/syscalls.h> @@ -202,6 +203,7 @@ void *dma_heap_get_drvdata(struct dma_heap *heap) { return heap->priv; } +EXPORT_SYMBOL_NS_GPL(dma_heap_get_drvdata, "DMA_BUF_HEAP"); /** * dma_heap_get_name - get heap name @@ -214,6 +216,7 @@ const char *dma_heap_get_name(struct dma_heap *heap) { return heap->name; } +EXPORT_SYMBOL_NS_GPL(dma_heap_get_name, "DMA_BUF_HEAP"); /** * dma_heap_add - adds a heap to dmabuf heaps @@ -303,6 +306,7 @@ err0: kfree(heap); return err_ret; } +EXPORT_SYMBOL_NS_GPL(dma_heap_add, "DMA_BUF_HEAP"); static char *dma_heap_devnode(const struct device *dev, umode_t *mode) { diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index 24e59ddf85e7..c7698cfaa4e8 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -401,8 +401,8 @@ static void scmi_device_release(struct device *dev) static void __scmi_device_destroy(struct scmi_device *scmi_dev) { - pr_debug("(%s) Destroying SCMI device '%s' for protocol 0x%x (%s)\n", - of_node_full_name(scmi_dev->dev.parent->of_node), + pr_debug("(%pOF) Destroying SCMI device '%s' for protocol 0x%x (%s)\n", + scmi_dev->dev.parent->of_node, dev_name(&scmi_dev->dev), scmi_dev->protocol_id, scmi_dev->name); @@ -474,9 +474,8 @@ __scmi_device_create(struct device_node *np, struct device *parent, if (retval) goto put_dev; - pr_debug("(%s) Created SCMI device '%s' for protocol 0x%x (%s)\n", - of_node_full_name(parent->of_node), - dev_name(&scmi_dev->dev), protocol, name); + pr_debug("(%pOF) Created SCMI device '%s' for protocol 0x%x (%s)\n", + parent->of_node, dev_name(&scmi_dev->dev), protocol, name); return scmi_dev; put_dev: @@ -493,8 +492,8 @@ _scmi_device_create(struct device_node *np, struct device *parent, sdev = __scmi_device_create(np, parent, protocol, name); if (!sdev) - pr_err("(%s) Failed to create device for protocol 0x%x (%s)\n", - of_node_full_name(parent->of_node), protocol, name); + pr_err("(%pOF) Failed to create device for protocol 0x%x (%s)\n", + parent->of_node, protocol, name); return sdev; } diff --git a/drivers/firmware/arm_scmi/quirks.c b/drivers/firmware/arm_scmi/quirks.c index 03960aca3610..03848283c2a0 100644 --- a/drivers/firmware/arm_scmi/quirks.c +++ b/drivers/firmware/arm_scmi/quirks.c @@ -71,6 +71,7 @@ */ #include <linux/ctype.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/export.h> #include <linux/hashtable.h> @@ -89,9 +90,9 @@ struct scmi_quirk { bool enabled; const char *name; - char *vendor; - char *sub_vendor_id; - char *impl_ver_range; + const char *vendor; + const char *sub_vendor_id; + const char *impl_ver_range; u32 start_range; u32 end_range; struct static_key_false *key; @@ -217,7 +218,7 @@ static unsigned int scmi_quirk_signature(const char *vend, const char *sub_vend) static int scmi_quirk_range_parse(struct scmi_quirk *quirk) { - const char *last, *first = quirk->impl_ver_range; + const char *last, *first __free(kfree) = NULL; size_t len; char *sep; int ret; @@ -228,8 +229,12 @@ static int scmi_quirk_range_parse(struct scmi_quirk *quirk) if (!len) return 0; + first = kmemdup(quirk->impl_ver_range, len + 1, GFP_KERNEL); + if (!first) + return -ENOMEM; + last = first + len - 1; - sep = strchr(quirk->impl_ver_range, '-'); + sep = strchr(first, '-'); if (sep) *sep = '\0'; diff --git a/drivers/firmware/arm_scmi/transports/mailbox.c b/drivers/firmware/arm_scmi/transports/mailbox.c index bd041c99b92b..ae0f67e6cc45 100644 --- a/drivers/firmware/arm_scmi/transports/mailbox.c +++ b/drivers/firmware/arm_scmi/transports/mailbox.c @@ -127,8 +127,8 @@ static int mailbox_chan_validate(struct device *cdev, int *a2p_rx_chan, (num_mb == 1 && num_sh != 1) || (num_mb == 3 && num_sh != 2) || (num_mb == 4 && num_sh != 2)) { dev_warn(cdev, - "Invalid channel descriptor for '%s' - mbs:%d shm:%d\n", - of_node_full_name(np), num_mb, num_sh); + "Invalid channel descriptor for '%pOF' - mbs:%d shm:%d\n", + np, num_mb, num_sh); return -EINVAL; } @@ -140,8 +140,7 @@ static int mailbox_chan_validate(struct device *cdev, int *a2p_rx_chan, of_parse_phandle(np, "shmem", 1); if (!np_tx || !np_rx || np_tx == np_rx) { - dev_warn(cdev, "Invalid shmem descriptor for '%s'\n", - of_node_full_name(np)); + dev_warn(cdev, "Invalid shmem descriptor for '%pOF'\n", np); ret = -EINVAL; } } diff --git a/drivers/firmware/arm_scmi/transports/optee.c b/drivers/firmware/arm_scmi/transports/optee.c index 3949a877e17d..dc0f46340153 100644 --- a/drivers/firmware/arm_scmi/transports/optee.c +++ b/drivers/firmware/arm_scmi/transports/optee.c @@ -498,7 +498,7 @@ static void scmi_optee_mark_txdone(struct scmi_chan_info *cinfo, int ret, mutex_unlock(&channel->mu); } -static struct scmi_transport_ops scmi_optee_ops = { +static const struct scmi_transport_ops scmi_optee_ops = { .chan_available = scmi_optee_chan_available, .chan_setup = scmi_optee_chan_setup, .chan_free = scmi_optee_chan_free, diff --git a/drivers/firmware/arm_scmi/transports/virtio.c b/drivers/firmware/arm_scmi/transports/virtio.c index cb934db9b2b4..326c4a93e44b 100644 --- a/drivers/firmware/arm_scmi/transports/virtio.c +++ b/drivers/firmware/arm_scmi/transports/virtio.c @@ -871,6 +871,9 @@ static int scmi_vio_probe(struct virtio_device *vdev) /* Ensure initialized scmi_vdev is visible */ smp_store_mb(scmi_vdev, vdev); + /* Set device ready */ + virtio_device_ready(vdev); + ret = platform_driver_register(&scmi_virtio_driver); if (ret) { vdev->priv = NULL; diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c index a8915d3b4df5..700a3f24f4ef 100644 --- a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c +++ b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c @@ -25,7 +25,10 @@ enum scmi_imx_misc_protocol_cmd { SCMI_IMX_MISC_CTRL_SET = 0x3, SCMI_IMX_MISC_CTRL_GET = 0x4, + SCMI_IMX_MISC_DISCOVER_BUILD_INFO = 0x6, SCMI_IMX_MISC_CTRL_NOTIFY = 0x8, + SCMI_IMX_MISC_CFG_INFO_GET = 0xC, + SCMI_IMX_MISC_BOARD_INFO = 0xE, }; struct scmi_imx_misc_info { @@ -65,6 +68,27 @@ struct scmi_imx_misc_ctrl_get_out { __le32 val[]; }; +struct scmi_imx_misc_buildinfo_out { + __le32 buildnum; + __le32 buildcommit; +#define MISC_MAX_BUILDDATE 16 + u8 builddate[MISC_MAX_BUILDDATE]; +#define MISC_MAX_BUILDTIME 16 + u8 buildtime[MISC_MAX_BUILDTIME]; +}; + +struct scmi_imx_misc_board_info_out { + __le32 attributes; +#define MISC_MAX_BRDNAME 16 + u8 brdname[MISC_MAX_BRDNAME]; +}; + +struct scmi_imx_misc_cfg_info_out { + __le32 msel; +#define MISC_MAX_CFGNAME 16 + u8 cfgname[MISC_MAX_CFGNAME]; +}; + static int scmi_imx_misc_attributes_get(const struct scmi_protocol_handle *ph, struct scmi_imx_misc_info *mi) { @@ -272,6 +296,81 @@ static int scmi_imx_misc_ctrl_set(const struct scmi_protocol_handle *ph, return ret; } +static int scmi_imx_misc_build_info_discover(const struct scmi_protocol_handle *ph) +{ + char date[MISC_MAX_BUILDDATE], time[MISC_MAX_BUILDTIME]; + struct scmi_imx_misc_buildinfo_out *out; + struct scmi_xfer *t; + int ret; + + ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_DISCOVER_BUILD_INFO, 0, + sizeof(*out), &t); + if (ret) + return ret; + + ret = ph->xops->do_xfer(ph, t); + if (!ret) { + out = t->rx.buf; + strscpy(date, out->builddate, MISC_MAX_BUILDDATE); + strscpy(time, out->buildtime, MISC_MAX_BUILDTIME); + dev_info(ph->dev, "SM Version\t= Build %u, Commit %08x %s %s\n", + le32_to_cpu(out->buildnum), le32_to_cpu(out->buildcommit), + date, time); + } + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int scmi_imx_misc_board_info(const struct scmi_protocol_handle *ph) +{ + struct scmi_imx_misc_board_info_out *out; + char name[MISC_MAX_BRDNAME]; + struct scmi_xfer *t; + int ret; + + ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_BOARD_INFO, 0, sizeof(*out), &t); + if (ret) + return ret; + + ret = ph->xops->do_xfer(ph, t); + if (!ret) { + out = t->rx.buf; + strscpy(name, out->brdname, MISC_MAX_BRDNAME); + dev_info(ph->dev, "Board\t\t= %s, attr=0x%08x\n", + name, le32_to_cpu(out->attributes)); + } + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int scmi_imx_misc_cfg_info_get(const struct scmi_protocol_handle *ph) +{ + struct scmi_imx_misc_cfg_info_out *out; + char name[MISC_MAX_CFGNAME]; + struct scmi_xfer *t; + int ret; + + ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CFG_INFO_GET, 0, sizeof(*out), &t); + if (ret) + return ret; + + ret = ph->xops->do_xfer(ph, t); + if (!ret) { + out = t->rx.buf; + strscpy(name, out->cfgname, MISC_MAX_CFGNAME); + dev_info(ph->dev, "SM Config\t= %s, mSel = %u\n", + name, le32_to_cpu(out->msel)); + } + + ph->xops->xfer_put(ph, t); + + return ret; +} + static const struct scmi_imx_misc_proto_ops scmi_imx_misc_proto_ops = { .misc_ctrl_set = scmi_imx_misc_ctrl_set, .misc_ctrl_get = scmi_imx_misc_ctrl_get, @@ -299,6 +398,18 @@ static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph) if (ret) return ret; + ret = scmi_imx_misc_build_info_discover(ph); + if (ret && ret != -EOPNOTSUPP) + return ret; + + ret = scmi_imx_misc_board_info(ph); + if (ret && ret != -EOPNOTSUPP) + return ret; + + ret = scmi_imx_misc_cfg_info_get(ph); + if (ret && ret != -EOPNOTSUPP) + return ret; + return ph->set_priv(ph, minfo, version); } diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx95.rst b/drivers/firmware/arm_scmi/vendors/imx/imx95.rst index 4e246a78a042..741f4eace350 100644 --- a/drivers/firmware/arm_scmi/vendors/imx/imx95.rst +++ b/drivers/firmware/arm_scmi/vendors/imx/imx95.rst @@ -1660,6 +1660,7 @@ protocol_id: 0x84 |Name |Description | +--------------------+---------------------------------------------------------+ |int32 status |SUCCESS: system log return | +| |NOT_SUPPORTED: system log not available | +--------------------+---------------------------------------------------------+ |uint32 numLogflags |Descriptor for the log data returned by this call. | | |Bits[31:20] Number of remaining log words. | @@ -1670,6 +1671,30 @@ protocol_id: 0x84 |uint32 syslog[N] |Log data array, N is defined in bits[11:0] of numLogflags| +--------------------+---------------------------------------------------------+ +MISC_BOARD_INFO +~~~~~~~~~~~~~~~ + +message_id: 0xE +protocol_id: 0x84 + ++--------------------+---------------------------------------------------------+ +|Return values | ++--------------------+---------------------------------------------------------+ +|Name |Description | ++--------------------+---------------------------------------------------------+ +|int32 status |SUCCESS: config name return | +| |NOT_SUPPORTED: name not available | ++--------------------+---------------------------------------------------------+ +|uint32 attributes |Board-specific attributes reserved for future expansion | +| |without breaking backwards compatibility. The firmware | +| |sets the value to 0 | ++--------------------+---------------------------------------------------------+ +|uint8 boardname[16] |Board name. NULL terminated ASCII string, up to 16 bytes | +| |in length. This is System Manager(SM) firmware-exported | +| |board-name and may not align with the board name in the | +| |device tree. | ++--------------------+---------------------------------------------------------+ + NEGOTIATE_PROTOCOL_VERSION ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/drivers/firmware/arm_scmi/voltage.c b/drivers/firmware/arm_scmi/voltage.c index fda6a1573609..17127880e10a 100644 --- a/drivers/firmware/arm_scmi/voltage.c +++ b/drivers/firmware/arm_scmi/voltage.c @@ -393,7 +393,7 @@ static int scmi_voltage_domains_num_get(const struct scmi_protocol_handle *ph) return vinfo->num_domains; } -static struct scmi_voltage_proto_ops voltage_proto_ops = { +static const struct scmi_voltage_proto_ops voltage_proto_ops = { .num_domains_get = scmi_voltage_domains_num_get, .info_get = scmi_voltage_info_get, .config_set = scmi_voltage_config_set, diff --git a/drivers/firmware/broadcom/bcm47xx_sprom.c b/drivers/firmware/broadcom/bcm47xx_sprom.c index 14fbcd11657c..fdcd3a07abcd 100644 --- a/drivers/firmware/broadcom/bcm47xx_sprom.c +++ b/drivers/firmware/broadcom/bcm47xx_sprom.c @@ -404,7 +404,7 @@ static void bcm47xx_sprom_fill_auto(struct ssb_sprom *sprom, ENTRY(0x00000700, u8, pre, "noiselvl5gua1", noiselvl5gua[1], 0, fb); ENTRY(0x00000700, u8, pre, "noiselvl5gua2", noiselvl5gua[2], 0, fb); } -#undef ENTRY /* It's specififc, uses local variable, don't use it (again). */ +#undef ENTRY /* It's specific, uses local variable, don't use it (again). */ static void bcm47xx_fill_sprom_path_r4589(struct ssb_sprom *sprom, const char *prefix, bool fallback) diff --git a/drivers/firmware/meson/Kconfig b/drivers/firmware/meson/Kconfig index f2fdd3756648..179f5d46d8dd 100644 --- a/drivers/firmware/meson/Kconfig +++ b/drivers/firmware/meson/Kconfig @@ -5,7 +5,7 @@ config MESON_SM tristate "Amlogic Secure Monitor driver" depends on ARCH_MESON || COMPILE_TEST - default y + default ARCH_MESON depends on ARM64_4K_PAGES help Say y here to enable the Amlogic secure monitor driver diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c index f25a9746249b..3ab67aaa9e5d 100644 --- a/drivers/firmware/meson/meson_sm.c +++ b/drivers/firmware/meson/meson_sm.c @@ -232,11 +232,16 @@ EXPORT_SYMBOL(meson_sm_call_write); struct meson_sm_firmware *meson_sm_get(struct device_node *sm_node) { struct platform_device *pdev = of_find_device_by_node(sm_node); + struct meson_sm_firmware *fw; if (!pdev) return NULL; - return platform_get_drvdata(pdev); + fw = platform_get_drvdata(pdev); + + put_device(&pdev->dev); + + return fw; } EXPORT_SYMBOL_GPL(meson_sm_get); diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 26cd0458aacd..e777b7cb9b12 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -1119,7 +1119,7 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, if (ret) { dev_err(__scm->dev, "Assign memory protection call failed %d\n", ret); - return -EINVAL; + return ret; } *srcvm = next_vm; @@ -1994,11 +1994,14 @@ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = { { .compatible = "asus,vivobook-s15" }, { .compatible = "asus,zenbook-a14-ux3407qa" }, { .compatible = "asus,zenbook-a14-ux3407ra" }, + { .compatible = "dell,inspiron-14-plus-7441" }, + { .compatible = "dell,latitude-7455" }, { .compatible = "dell,xps13-9345" }, { .compatible = "hp,elitebook-ultra-g1q" }, { .compatible = "hp,omnibook-x14" }, { .compatible = "huawei,gaokun3" }, { .compatible = "lenovo,flex-5g" }, + { .compatible = "lenovo,thinkbook-16" }, { .compatible = "lenovo,thinkpad-t14s" }, { .compatible = "lenovo,thinkpad-x13s", }, { .compatible = "lenovo,yoga-slim7x" }, @@ -2006,6 +2009,7 @@ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = { { .compatible = "microsoft,blackrock" }, { .compatible = "microsoft,romulus13", }, { .compatible = "microsoft,romulus15", }, + { .compatible = "qcom,hamoa-iot-evk" }, { .compatible = "qcom,sc8180x-primus" }, { .compatible = "qcom,x1e001de-devkit" }, { .compatible = "qcom,x1e80100-crd" }, @@ -2094,6 +2098,122 @@ static int qcom_scm_qseecom_init(struct qcom_scm *scm) #endif /* CONFIG_QCOM_QSEECOM */ /** + * qcom_scm_qtee_invoke_smc() - Invoke a QTEE object. + * @inbuf: start address of memory area used for inbound buffer. + * @inbuf_size: size of the memory area used for inbound buffer. + * @outbuf: start address of memory area used for outbound buffer. + * @outbuf_size: size of the memory area used for outbound buffer. + * @result: result of QTEE object invocation. + * @response_type: response type returned by QTEE. + * + * @response_type determines how the contents of @inbuf and @outbuf + * should be processed. + * + * Return: On success, return 0 or <0 on failure. + */ +int qcom_scm_qtee_invoke_smc(phys_addr_t inbuf, size_t inbuf_size, + phys_addr_t outbuf, size_t outbuf_size, + u64 *result, u64 *response_type) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_SMCINVOKE, + .cmd = QCOM_SCM_SMCINVOKE_INVOKE, + .owner = ARM_SMCCC_OWNER_TRUSTED_OS, + .args[0] = inbuf, + .args[1] = inbuf_size, + .args[2] = outbuf, + .args[3] = outbuf_size, + .arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_RW, QCOM_SCM_VAL, + QCOM_SCM_RW, QCOM_SCM_VAL), + }; + struct qcom_scm_res res; + int ret; + + ret = qcom_scm_call(__scm->dev, &desc, &res); + if (ret) + return ret; + + if (response_type) + *response_type = res.result[0]; + + if (result) + *result = res.result[1]; + + return 0; +} +EXPORT_SYMBOL(qcom_scm_qtee_invoke_smc); + +/** + * qcom_scm_qtee_callback_response() - Submit response for callback request. + * @buf: start address of memory area used for outbound buffer. + * @buf_size: size of the memory area used for outbound buffer. + * @result: Result of QTEE object invocation. + * @response_type: Response type returned by QTEE. + * + * @response_type determines how the contents of @buf should be processed. + * + * Return: On success, return 0 or <0 on failure. + */ +int qcom_scm_qtee_callback_response(phys_addr_t buf, size_t buf_size, + u64 *result, u64 *response_type) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_SMCINVOKE, + .cmd = QCOM_SCM_SMCINVOKE_CB_RSP, + .owner = ARM_SMCCC_OWNER_TRUSTED_OS, + .args[0] = buf, + .args[1] = buf_size, + .arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_RW, QCOM_SCM_VAL), + }; + struct qcom_scm_res res; + int ret; + + ret = qcom_scm_call(__scm->dev, &desc, &res); + if (ret) + return ret; + + if (response_type) + *response_type = res.result[0]; + + if (result) + *result = res.result[1]; + + return 0; +} +EXPORT_SYMBOL(qcom_scm_qtee_callback_response); + +static void qcom_scm_qtee_free(void *data) +{ + struct platform_device *qtee_dev = data; + + platform_device_unregister(qtee_dev); +} + +static void qcom_scm_qtee_init(struct qcom_scm *scm) +{ + struct platform_device *qtee_dev; + u64 result, response_type; + int ret; + + /* + * Probe for smcinvoke support. This will fail due to invalid buffers, + * but first, it checks whether the call is supported in QTEE syscall + * handler. If it is not supported, -EIO is returned. + */ + ret = qcom_scm_qtee_invoke_smc(0, 0, 0, 0, &result, &response_type); + if (ret == -EIO) + return; + + /* Setup QTEE interface device. */ + qtee_dev = platform_device_register_data(scm->dev, "qcomtee", + PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(qtee_dev)) + return; + + devm_add_action_or_reset(scm->dev, qcom_scm_qtee_free, qtee_dev); +} + +/** * qcom_scm_is_available() - Checks if SCM is available */ bool qcom_scm_is_available(void) @@ -2325,6 +2445,9 @@ static int qcom_scm_probe(struct platform_device *pdev) ret = qcom_scm_qseecom_init(scm); WARN(ret < 0, "failed to initialize qseecom: %d\n", ret); + /* Initialize the QTEE object interface. */ + qcom_scm_qtee_init(scm); + return 0; } diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h index 0e8dd838099e..a56c8212cc0c 100644 --- a/drivers/firmware/qcom/qcom_scm.h +++ b/drivers/firmware/qcom/qcom_scm.h @@ -156,6 +156,13 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev); #define QCOM_SCM_SVC_GPU 0x28 #define QCOM_SCM_SVC_GPU_INIT_REGS 0x01 +/* ARM_SMCCC_OWNER_TRUSTED_OS calls */ + +#define QCOM_SCM_SVC_SMCINVOKE 0x06 +#define QCOM_SCM_SMCINVOKE_INVOKE_LEGACY 0x00 +#define QCOM_SCM_SMCINVOKE_CB_RSP 0x01 +#define QCOM_SCM_SMCINVOKE_INVOKE 0x02 + /* common error codes */ #define QCOM_SCM_V2_EBUSY -12 #define QCOM_SCM_ENOMEM -5 diff --git a/drivers/firmware/qcom/qcom_tzmem.c b/drivers/firmware/qcom/qcom_tzmem.c index ea0a35355657..9f232e53115e 100644 --- a/drivers/firmware/qcom/qcom_tzmem.c +++ b/drivers/firmware/qcom/qcom_tzmem.c @@ -77,6 +77,7 @@ static bool qcom_tzmem_using_shm_bridge; /* List of machines that are known to not support SHM bridge correctly. */ static const char *const qcom_tzmem_blacklist[] = { + "qcom,sc7180", /* hang in rmtfs memory assignment */ "qcom,sc8180x", "qcom,sdm670", /* failure in GPU firmware loading */ "qcom,sdm845", /* reset in rmtfs memory assignment */ @@ -109,7 +110,19 @@ notsupp: return 0; } -static int qcom_tzmem_init_area(struct qcom_tzmem_area *area) +/** + * qcom_tzmem_shm_bridge_create() - Create a SHM bridge. + * @paddr: Physical address of the memory to share. + * @size: Size of the memory to share. + * @handle: Handle to the SHM bridge. + * + * On platforms that support SHM bridge, this function creates a SHM bridge + * for the given memory region with QTEE. The handle returned by this function + * must be passed to qcom_tzmem_shm_bridge_delete() to free the SHM bridge. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +int qcom_tzmem_shm_bridge_create(phys_addr_t paddr, size_t size, u64 *handle) { u64 pfn_and_ns_perm, ipfn_and_s_perm, size_and_flags; int ret; @@ -117,17 +130,49 @@ static int qcom_tzmem_init_area(struct qcom_tzmem_area *area) if (!qcom_tzmem_using_shm_bridge) return 0; - pfn_and_ns_perm = (u64)area->paddr | QCOM_SCM_PERM_RW; - ipfn_and_s_perm = (u64)area->paddr | QCOM_SCM_PERM_RW; - size_and_flags = area->size | (1 << QCOM_SHM_BRIDGE_NUM_VM_SHIFT); + pfn_and_ns_perm = paddr | QCOM_SCM_PERM_RW; + ipfn_and_s_perm = paddr | QCOM_SCM_PERM_RW; + size_and_flags = size | (1 << QCOM_SHM_BRIDGE_NUM_VM_SHIFT); + + ret = qcom_scm_shm_bridge_create(pfn_and_ns_perm, ipfn_and_s_perm, + size_and_flags, QCOM_SCM_VMID_HLOS, + handle); + if (ret) { + dev_err(qcom_tzmem_dev, + "SHM Bridge failed: ret %d paddr 0x%pa, size %zu\n", + ret, &paddr, size); + + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_tzmem_shm_bridge_create); + +/** + * qcom_tzmem_shm_bridge_delete() - Delete a SHM bridge. + * @handle: Handle to the SHM bridge. + * + * On platforms that support SHM bridge, this function deletes the SHM bridge + * for the given memory region. The handle must be the same as the one + * returned by qcom_tzmem_shm_bridge_create(). + */ +void qcom_tzmem_shm_bridge_delete(u64 handle) +{ + if (qcom_tzmem_using_shm_bridge) + qcom_scm_shm_bridge_delete(handle); +} +EXPORT_SYMBOL_GPL(qcom_tzmem_shm_bridge_delete); + +static int qcom_tzmem_init_area(struct qcom_tzmem_area *area) +{ + int ret; u64 *handle __free(kfree) = kzalloc(sizeof(*handle), GFP_KERNEL); if (!handle) return -ENOMEM; - ret = qcom_scm_shm_bridge_create(pfn_and_ns_perm, ipfn_and_s_perm, - size_and_flags, QCOM_SCM_VMID_HLOS, - handle); + ret = qcom_tzmem_shm_bridge_create(area->paddr, area->size, handle); if (ret) return ret; @@ -140,10 +185,7 @@ static void qcom_tzmem_cleanup_area(struct qcom_tzmem_area *area) { u64 *handle = area->priv; - if (!qcom_tzmem_using_shm_bridge) - return; - - qcom_scm_shm_bridge_delete(*handle); + qcom_tzmem_shm_bridge_delete(*handle); kfree(handle); } diff --git a/drivers/firmware/samsung/exynos-acpm-pmic.c b/drivers/firmware/samsung/exynos-acpm-pmic.c index 39b33a356ebd..961d7599e422 100644 --- a/drivers/firmware/samsung/exynos-acpm-pmic.c +++ b/drivers/firmware/samsung/exynos-acpm-pmic.c @@ -4,7 +4,9 @@ * Copyright 2020 Google LLC. * Copyright 2024 Linaro Ltd. */ +#include <linux/array_size.h> #include <linux/bitfield.h> +#include <linux/errno.h> #include <linux/firmware/samsung/exynos-acpm-protocol.h> #include <linux/ktime.h> #include <linux/types.h> @@ -33,6 +35,19 @@ enum exynos_acpm_pmic_func { ACPM_PMIC_BULK_WRITE, }; +static const int acpm_pmic_linux_errmap[] = { + [0] = 0, /* ACPM_PMIC_SUCCESS */ + [1] = -EACCES, /* Read register can't be accessed or issues to access it. */ + [2] = -EACCES, /* Write register can't be accessed or issues to access it. */ +}; + +static int acpm_pmic_to_linux_err(int err) +{ + if (err >= 0 && err < ARRAY_SIZE(acpm_pmic_linux_errmap)) + return acpm_pmic_linux_errmap[err]; + return -EIO; +} + static inline u32 acpm_pmic_set_bulk(u32 data, unsigned int i) { return (data & ACPM_PMIC_BULK_MASK) << (ACPM_PMIC_BULK_SHIFT * i); @@ -79,7 +94,7 @@ int acpm_pmic_read_reg(const struct acpm_handle *handle, *buf = FIELD_GET(ACPM_PMIC_VALUE, xfer.rxd[1]); - return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); + return acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1])); } static void acpm_pmic_init_bulk_read_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, @@ -110,7 +125,7 @@ int acpm_pmic_bulk_read(const struct acpm_handle *handle, if (ret) return ret; - ret = FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); + ret = acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1])); if (ret) return ret; @@ -150,7 +165,7 @@ int acpm_pmic_write_reg(const struct acpm_handle *handle, if (ret) return ret; - return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); + return acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1])); } static void acpm_pmic_init_bulk_write_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, @@ -190,7 +205,7 @@ int acpm_pmic_bulk_write(const struct acpm_handle *handle, if (ret) return ret; - return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); + return acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1])); } static void acpm_pmic_init_update_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, @@ -220,5 +235,5 @@ int acpm_pmic_update_reg(const struct acpm_handle *handle, if (ret) return ret; - return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); + return acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1])); } diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index ae5fd1936ad3..49fd2ae01055 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -2015,6 +2015,47 @@ fail: return ret; } +/** + * ti_sci_cmd_lpm_abort() - Abort entry to LPM by clearing selection of LPM to enter + * @dev: Device pointer corresponding to the SCI entity + * + * Return: 0 if all went well, else returns appropriate error value. + */ +static int ti_sci_cmd_lpm_abort(struct device *dev) +{ + struct ti_sci_info *info = dev_get_drvdata(dev); + struct ti_sci_msg_hdr *req; + struct ti_sci_msg_hdr *resp; + struct ti_sci_xfer *xfer; + int ret = 0; + + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_LPM_ABORT, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "Message alloc failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_hdr *)xfer->xfer_buf; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "Mbox send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_hdr *)xfer->xfer_buf; + + if (!ti_sci_is_response_ack(resp)) + ret = -ENODEV; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + + return ret; +} + static int ti_sci_cmd_core_reboot(const struct ti_sci_handle *handle) { struct ti_sci_info *info; @@ -3739,11 +3780,22 @@ static int __maybe_unused ti_sci_resume_noirq(struct device *dev) return 0; } +static void __maybe_unused ti_sci_pm_complete(struct device *dev) +{ + struct ti_sci_info *info = dev_get_drvdata(dev); + + if (info->fw_caps & MSG_FLAG_CAPS_LPM_ABORT) { + if (ti_sci_cmd_lpm_abort(dev)) + dev_err(dev, "LPM clear selection failed.\n"); + } +} + static const struct dev_pm_ops ti_sci_pm_ops = { #ifdef CONFIG_PM_SLEEP .suspend = ti_sci_suspend, .suspend_noirq = ti_sci_suspend_noirq, .resume_noirq = ti_sci_resume_noirq, + .complete = ti_sci_pm_complete, #endif }; @@ -3876,10 +3928,11 @@ static int ti_sci_probe(struct platform_device *pdev) } ti_sci_msg_cmd_query_fw_caps(&info->handle, &info->fw_caps); - dev_dbg(dev, "Detected firmware capabilities: %s%s%s\n", + dev_dbg(dev, "Detected firmware capabilities: %s%s%s%s\n", info->fw_caps & MSG_FLAG_CAPS_GENERIC ? "Generic" : "", info->fw_caps & MSG_FLAG_CAPS_LPM_PARTIAL_IO ? " Partial-IO" : "", - info->fw_caps & MSG_FLAG_CAPS_LPM_DM_MANAGED ? " DM-Managed" : "" + info->fw_caps & MSG_FLAG_CAPS_LPM_DM_MANAGED ? " DM-Managed" : "", + info->fw_caps & MSG_FLAG_CAPS_LPM_ABORT ? " LPM-Abort" : "" ); ti_sci_setup_ops(info); diff --git a/drivers/firmware/ti_sci.h b/drivers/firmware/ti_sci.h index 053387d7baa0..701c416b2e78 100644 --- a/drivers/firmware/ti_sci.h +++ b/drivers/firmware/ti_sci.h @@ -42,6 +42,7 @@ #define TI_SCI_MSG_SET_IO_ISOLATION 0x0307 #define TI_SCI_MSG_LPM_SET_DEVICE_CONSTRAINT 0x0309 #define TI_SCI_MSG_LPM_SET_LATENCY_CONSTRAINT 0x030A +#define TI_SCI_MSG_LPM_ABORT 0x0311 /* Resource Management Requests */ #define TI_SCI_MSG_GET_RESOURCE_RANGE 0x1500 @@ -147,6 +148,7 @@ struct ti_sci_msg_req_reboot { * MSG_FLAG_CAPS_GENERIC: Generic capability (LPM not supported) * MSG_FLAG_CAPS_LPM_PARTIAL_IO: Partial IO in LPM * MSG_FLAG_CAPS_LPM_DM_MANAGED: LPM can be managed by DM + * MSG_FLAG_CAPS_LPM_ABORT: Abort entry to LPM * * Response to a generic message with message type TI_SCI_MSG_QUERY_FW_CAPS * providing currently available SOC/firmware capabilities. SoC that don't @@ -157,6 +159,7 @@ struct ti_sci_msg_resp_query_fw_caps { #define MSG_FLAG_CAPS_GENERIC TI_SCI_MSG_FLAG(0) #define MSG_FLAG_CAPS_LPM_PARTIAL_IO TI_SCI_MSG_FLAG(4) #define MSG_FLAG_CAPS_LPM_DM_MANAGED TI_SCI_MSG_FLAG(5) +#define MSG_FLAG_CAPS_LPM_ABORT TI_SCI_MSG_FLAG(9) #define MSG_MASK_CAPS_LPM GENMASK_ULL(4, 1) u64 fw_caps; } __packed; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index e0c2c2ab0aa3..ae8fcc864060 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -415,7 +415,7 @@ config I2C_ASPEED config I2C_AT91 tristate "Atmel AT91 I2C Two-Wire interface (TWI)" - depends on ARCH_AT91 || COMPILE_TEST + depends on ARCH_MICROCHIP || COMPILE_TEST help This supports the use of the I2C interface on Atmel AT91 processors. diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index ff2289b52c84..95a577764d5c 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -870,7 +870,13 @@ static int geni_i2c_probe(struct platform_device *pdev) goto err_clk; } proto = geni_se_read_proto(&gi2c->se); - if (proto != GENI_SE_I2C) { + if (proto == GENI_SE_INVALID_PROTO) { + ret = geni_load_se_firmware(&gi2c->se, GENI_SE_I2C); + if (ret) { + dev_err_probe(dev, ret, "i2c firmware load failed ret: %d\n", ret); + goto err_resources; + } + } else if (proto != GENI_SE_I2C) { ret = dev_err_probe(dev, -ENXIO, "Invalid proto %d\n", proto); goto err_resources; } diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index 66a18830e66d..862d0718f694 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -136,8 +136,8 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, ret = qcom_mdt_load(dev, mdt, fwname, VENUS_PAS_ID, mem_va, *mem_phys, *mem_size, NULL); else - ret = qcom_mdt_load_no_init(dev, mdt, fwname, VENUS_PAS_ID, - mem_va, *mem_phys, *mem_size, NULL); + ret = qcom_mdt_load_no_init(dev, mdt, fwname, mem_va, + *mem_phys, *mem_size, NULL); memunmap(mem_va); err_release_fw: diff --git a/drivers/memory/samsung/exynos-srom.c b/drivers/memory/samsung/exynos-srom.c index e73dd330af47..d913fb901973 100644 --- a/drivers/memory/samsung/exynos-srom.c +++ b/drivers/memory/samsung/exynos-srom.c @@ -121,20 +121,18 @@ static int exynos_srom_probe(struct platform_device *pdev) return -ENOMEM; srom->dev = dev; - srom->reg_base = of_iomap(np, 0); - if (!srom->reg_base) { + srom->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(srom->reg_base)) { dev_err(&pdev->dev, "iomap of exynos srom controller failed\n"); - return -ENOMEM; + return PTR_ERR(srom->reg_base); } platform_set_drvdata(pdev, srom); srom->reg_offset = exynos_srom_alloc_reg_dump(exynos_srom_offsets, ARRAY_SIZE(exynos_srom_offsets)); - if (!srom->reg_offset) { - iounmap(srom->reg_base); + if (!srom->reg_offset) return -ENOMEM; - } for_each_child_of_node(np, child) { if (exynos_srom_configure_bank(srom, child)) { diff --git a/drivers/memory/stm32_omm.c b/drivers/memory/stm32_omm.c index bee2ecc8c2b9..5d06623f3f68 100644 --- a/drivers/memory/stm32_omm.c +++ b/drivers/memory/stm32_omm.c @@ -238,7 +238,7 @@ static int stm32_omm_configure(struct device *dev) if (mux & CR_MUXEN) { ret = of_property_read_u32(dev->of_node, "st,omm-req2ack-ns", &req2ack); - if (!ret && !req2ack) { + if (!ret && req2ack) { req2ack = DIV_ROUND_UP(req2ack, NSEC_PER_SEC / clk_rate_max) - 1; if (req2ack > 256) diff --git a/drivers/memory/tegra/tegra210.c b/drivers/memory/tegra/tegra210.c index 8ab6498dbe7d..cfa61dd88557 100644 --- a/drivers/memory/tegra/tegra210.c +++ b/drivers/memory/tegra/tegra210.c @@ -9,11 +9,11 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { { - .id = 0x00, + .id = TEGRA210_MC_PTCR, .name = "ptcr", .swgroup = TEGRA_SWGROUP_PTC, }, { - .id = 0x01, + .id = TEGRA210_MC_DISPLAY0A, .name = "display0a", .swgroup = TEGRA_SWGROUP_DC, .regs = { @@ -29,7 +29,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x02, + .id = TEGRA210_MC_DISPLAY0AB, .name = "display0ab", .swgroup = TEGRA_SWGROUP_DCB, .regs = { @@ -45,7 +45,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x03, + .id = TEGRA210_MC_DISPLAY0B, .name = "display0b", .swgroup = TEGRA_SWGROUP_DC, .regs = { @@ -61,7 +61,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x04, + .id = TEGRA210_MC_DISPLAY0BB, .name = "display0bb", .swgroup = TEGRA_SWGROUP_DCB, .regs = { @@ -77,7 +77,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x05, + .id = TEGRA210_MC_DISPLAY0C, .name = "display0c", .swgroup = TEGRA_SWGROUP_DC, .regs = { @@ -93,7 +93,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x06, + .id = TEGRA210_MC_DISPLAY0CB, .name = "display0cb", .swgroup = TEGRA_SWGROUP_DCB, .regs = { @@ -109,7 +109,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x0e, + .id = TEGRA210_MC_AFIR, .name = "afir", .swgroup = TEGRA_SWGROUP_AFI, .regs = { @@ -125,7 +125,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x0f, + .id = TEGRA210_MC_AVPCARM7R, .name = "avpcarm7r", .swgroup = TEGRA_SWGROUP_AVPC, .regs = { @@ -141,7 +141,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x10, + .id = TEGRA210_MC_DISPLAYHC, .name = "displayhc", .swgroup = TEGRA_SWGROUP_DC, .regs = { @@ -157,7 +157,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x11, + .id = TEGRA210_MC_DISPLAYHCB, .name = "displayhcb", .swgroup = TEGRA_SWGROUP_DCB, .regs = { @@ -173,7 +173,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x15, + .id = TEGRA210_MC_HDAR, .name = "hdar", .swgroup = TEGRA_SWGROUP_HDA, .regs = { @@ -189,7 +189,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x16, + .id = TEGRA210_MC_HOST1XDMAR, .name = "host1xdmar", .swgroup = TEGRA_SWGROUP_HC, .regs = { @@ -205,7 +205,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x17, + .id = TEGRA210_MC_HOST1XR, .name = "host1xr", .swgroup = TEGRA_SWGROUP_HC, .regs = { @@ -221,7 +221,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x1c, + .id = TEGRA210_MC_NVENCSRD, .name = "nvencsrd", .swgroup = TEGRA_SWGROUP_NVENC, .regs = { @@ -237,7 +237,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x1d, + .id = TEGRA210_MC_PPCSAHBDMAR, .name = "ppcsahbdmar", .swgroup = TEGRA_SWGROUP_PPCS, .regs = { @@ -253,7 +253,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x1e, + .id = TEGRA210_MC_PPCSAHBSLVR, .name = "ppcsahbslvr", .swgroup = TEGRA_SWGROUP_PPCS, .regs = { @@ -269,7 +269,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x1f, + .id = TEGRA210_MC_SATAR, .name = "satar", .swgroup = TEGRA_SWGROUP_SATA, .regs = { @@ -285,7 +285,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x27, + .id = TEGRA210_MC_MPCORER, .name = "mpcorer", .swgroup = TEGRA_SWGROUP_MPCORE, .regs = { @@ -297,7 +297,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x2b, + .id = TEGRA210_MC_NVENCSWR, .name = "nvencswr", .swgroup = TEGRA_SWGROUP_NVENC, .regs = { @@ -313,7 +313,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x31, + .id = TEGRA210_MC_AFIW, .name = "afiw", .swgroup = TEGRA_SWGROUP_AFI, .regs = { @@ -329,7 +329,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x32, + .id = TEGRA210_MC_AVPCARM7W, .name = "avpcarm7w", .swgroup = TEGRA_SWGROUP_AVPC, .regs = { @@ -345,7 +345,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x35, + .id = TEGRA210_MC_HDAW, .name = "hdaw", .swgroup = TEGRA_SWGROUP_HDA, .regs = { @@ -361,7 +361,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x36, + .id = TEGRA210_MC_HOST1XW, .name = "host1xw", .swgroup = TEGRA_SWGROUP_HC, .regs = { @@ -377,7 +377,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x39, + .id = TEGRA210_MC_MPCOREW, .name = "mpcorew", .swgroup = TEGRA_SWGROUP_MPCORE, .regs = { @@ -389,7 +389,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x3b, + .id = TEGRA210_MC_PPCSAHBDMAW, .name = "ppcsahbdmaw", .swgroup = TEGRA_SWGROUP_PPCS, .regs = { @@ -405,7 +405,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x3c, + .id = TEGRA210_MC_PPCSAHBSLVW, .name = "ppcsahbslvw", .swgroup = TEGRA_SWGROUP_PPCS, .regs = { @@ -421,7 +421,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x3d, + .id = TEGRA210_MC_SATAW, .name = "sataw", .swgroup = TEGRA_SWGROUP_SATA, .regs = { @@ -437,7 +437,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x44, + .id = TEGRA210_MC_ISPRA, .name = "ispra", .swgroup = TEGRA_SWGROUP_ISP2, .regs = { @@ -453,7 +453,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x46, + .id = TEGRA210_MC_ISPWA, .name = "ispwa", .swgroup = TEGRA_SWGROUP_ISP2, .regs = { @@ -469,7 +469,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x47, + .id = TEGRA210_MC_ISPWB, .name = "ispwb", .swgroup = TEGRA_SWGROUP_ISP2, .regs = { @@ -485,7 +485,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x4a, + .id = TEGRA210_MC_XUSB_HOSTR, .name = "xusb_hostr", .swgroup = TEGRA_SWGROUP_XUSB_HOST, .regs = { @@ -501,7 +501,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x4b, + .id = TEGRA210_MC_XUSB_HOSTW, .name = "xusb_hostw", .swgroup = TEGRA_SWGROUP_XUSB_HOST, .regs = { @@ -517,7 +517,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x4c, + .id = TEGRA210_MC_XUSB_DEVR, .name = "xusb_devr", .swgroup = TEGRA_SWGROUP_XUSB_DEV, .regs = { @@ -533,7 +533,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x4d, + .id = TEGRA210_MC_XUSB_DEVW, .name = "xusb_devw", .swgroup = TEGRA_SWGROUP_XUSB_DEV, .regs = { @@ -549,7 +549,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x4e, + .id = TEGRA210_MC_ISPRAB, .name = "isprab", .swgroup = TEGRA_SWGROUP_ISP2B, .regs = { @@ -565,7 +565,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x50, + .id = TEGRA210_MC_ISPWAB, .name = "ispwab", .swgroup = TEGRA_SWGROUP_ISP2B, .regs = { @@ -581,7 +581,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x51, + .id = TEGRA210_MC_ISPWBB, .name = "ispwbb", .swgroup = TEGRA_SWGROUP_ISP2B, .regs = { @@ -597,7 +597,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x54, + .id = TEGRA210_MC_TSECSRD, .name = "tsecsrd", .swgroup = TEGRA_SWGROUP_TSEC, .regs = { @@ -613,7 +613,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x55, + .id = TEGRA210_MC_TSECSWR, .name = "tsecswr", .swgroup = TEGRA_SWGROUP_TSEC, .regs = { @@ -629,7 +629,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x56, + .id = TEGRA210_MC_A9AVPSCR, .name = "a9avpscr", .swgroup = TEGRA_SWGROUP_A9AVP, .regs = { @@ -645,7 +645,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x57, + .id = TEGRA210_MC_A9AVPSCW, .name = "a9avpscw", .swgroup = TEGRA_SWGROUP_A9AVP, .regs = { @@ -661,7 +661,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x58, + .id = TEGRA210_MC_GPUSRD, .name = "gpusrd", .swgroup = TEGRA_SWGROUP_GPU, .regs = { @@ -678,7 +678,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x59, + .id = TEGRA210_MC_GPUSWR, .name = "gpuswr", .swgroup = TEGRA_SWGROUP_GPU, .regs = { @@ -695,7 +695,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x5a, + .id = TEGRA210_MC_DISPLAYT, .name = "displayt", .swgroup = TEGRA_SWGROUP_DC, .regs = { @@ -711,7 +711,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x60, + .id = TEGRA210_MC_SDMMCRA, .name = "sdmmcra", .swgroup = TEGRA_SWGROUP_SDMMC1A, .regs = { @@ -727,7 +727,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x61, + .id = TEGRA210_MC_SDMMCRAA, .name = "sdmmcraa", .swgroup = TEGRA_SWGROUP_SDMMC2A, .regs = { @@ -743,7 +743,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x62, + .id = TEGRA210_MC_SDMMCR, .name = "sdmmcr", .swgroup = TEGRA_SWGROUP_SDMMC3A, .regs = { @@ -759,7 +759,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x63, + .id = TEGRA210_MC_SDMMCRAB, .swgroup = TEGRA_SWGROUP_SDMMC4A, .name = "sdmmcrab", .regs = { @@ -775,7 +775,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x64, + .id = TEGRA210_MC_SDMMCWA, .name = "sdmmcwa", .swgroup = TEGRA_SWGROUP_SDMMC1A, .regs = { @@ -791,7 +791,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x65, + .id = TEGRA210_MC_SDMMCWAA, .name = "sdmmcwaa", .swgroup = TEGRA_SWGROUP_SDMMC2A, .regs = { @@ -807,7 +807,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x66, + .id = TEGRA210_MC_SDMMCW, .name = "sdmmcw", .swgroup = TEGRA_SWGROUP_SDMMC3A, .regs = { @@ -823,7 +823,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x67, + .id = TEGRA210_MC_SDMMCWAB, .name = "sdmmcwab", .swgroup = TEGRA_SWGROUP_SDMMC4A, .regs = { @@ -839,7 +839,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x6c, + .id = TEGRA210_MC_VICSRD, .name = "vicsrd", .swgroup = TEGRA_SWGROUP_VIC, .regs = { @@ -855,7 +855,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x6d, + .id = TEGRA210_MC_VICSWR, .name = "vicswr", .swgroup = TEGRA_SWGROUP_VIC, .regs = { @@ -871,7 +871,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x72, + .id = TEGRA210_MC_VIW, .name = "viw", .swgroup = TEGRA_SWGROUP_VI, .regs = { @@ -887,7 +887,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x73, + .id = TEGRA210_MC_DISPLAYD, .name = "displayd", .swgroup = TEGRA_SWGROUP_DC, .regs = { @@ -903,7 +903,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x78, + .id = TEGRA210_MC_NVDECSRD, .name = "nvdecsrd", .swgroup = TEGRA_SWGROUP_NVDEC, .regs = { @@ -919,7 +919,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x79, + .id = TEGRA210_MC_NVDECSWR, .name = "nvdecswr", .swgroup = TEGRA_SWGROUP_NVDEC, .regs = { @@ -935,7 +935,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x7a, + .id = TEGRA210_MC_APER, .name = "aper", .swgroup = TEGRA_SWGROUP_APE, .regs = { @@ -951,7 +951,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x7b, + .id = TEGRA210_MC_APEW, .name = "apew", .swgroup = TEGRA_SWGROUP_APE, .regs = { @@ -967,7 +967,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x7e, + .id = TEGRA210_MC_NVJPGRD, .name = "nvjpgsrd", .swgroup = TEGRA_SWGROUP_NVJPG, .regs = { @@ -983,7 +983,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x7f, + .id = TEGRA210_MC_NVJPGWR, .name = "nvjpgswr", .swgroup = TEGRA_SWGROUP_NVJPG, .regs = { @@ -999,7 +999,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x80, + .id = TEGRA210_MC_SESRD, .name = "sesrd", .swgroup = TEGRA_SWGROUP_SE, .regs = { @@ -1015,7 +1015,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x81, + .id = TEGRA210_MC_SESRD, .name = "seswr", .swgroup = TEGRA_SWGROUP_SE, .regs = { @@ -1031,7 +1031,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x82, + .id = TEGRA210_MC_AXIAPR, .name = "axiapr", .swgroup = TEGRA_SWGROUP_AXIAP, .regs = { @@ -1047,7 +1047,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x83, + .id = TEGRA210_MC_AXIAPW, .name = "axiapw", .swgroup = TEGRA_SWGROUP_AXIAP, .regs = { @@ -1063,7 +1063,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x84, + .id = TEGRA210_MC_ETRR, .name = "etrr", .swgroup = TEGRA_SWGROUP_ETR, .regs = { @@ -1079,7 +1079,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x85, + .id = TEGRA210_MC_ETRR, .name = "etrw", .swgroup = TEGRA_SWGROUP_ETR, .regs = { @@ -1095,7 +1095,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x86, + .id = TEGRA210_MC_TSECSRDB, .name = "tsecsrdb", .swgroup = TEGRA_SWGROUP_TSECB, .regs = { @@ -1111,7 +1111,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x87, + .id = TEGRA210_MC_TSECSWRB, .name = "tsecswrb", .swgroup = TEGRA_SWGROUP_TSECB, .regs = { @@ -1127,7 +1127,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x88, + .id = TEGRA210_MC_GPUSRD2, .name = "gpusrd2", .swgroup = TEGRA_SWGROUP_GPU, .regs = { @@ -1144,7 +1144,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = { }, }, }, { - .id = 0x89, + .id = TEGRA210_MC_GPUSWR2, .name = "gpuswr2", .swgroup = TEGRA_SWGROUP_GPU, .regs = { diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 67b54e0fd452..6cec1858947b 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -139,7 +139,7 @@ config MFD_AAT2870_CORE config MFD_AT91_USART tristate "AT91 USART Driver" select MFD_CORE - depends on ARCH_AT91 || ARCH_LAN969X || COMPILE_TEST + depends on ARCH_MICROCHIP || COMPILE_TEST help Select this to get support for AT91 USART IP. This is a wrapper over at91-usart-serial driver and usart-spi-driver. Only one function diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c index 3b983f4e3268..b30527c402f6 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.c +++ b/drivers/net/wireless/ath/ath12k/ahb.c @@ -414,7 +414,7 @@ static int ath12k_ahb_power_up(struct ath12k_base *ab) goto err_fw2; } - ret = qcom_mdt_load_no_init(dev, fw2, fw2_name, pasid, mem_region, mem_phys, + ret = qcom_mdt_load_no_init(dev, fw2, fw2_name, mem_region, mem_phys, mem_size, &mem_phys); if (ret) { ath12k_err(ab, "Failed to load MDT segments: %d\n", ret); diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c index 1286c31320e6..f35d3f71d14f 100644 --- a/drivers/nvme/host/apple.c +++ b/drivers/nvme/host/apple.c @@ -35,7 +35,6 @@ #include "nvme.h" #define APPLE_ANS_BOOT_TIMEOUT USEC_PER_SEC -#define APPLE_ANS_MAX_QUEUE_DEPTH 64 #define APPLE_ANS_COPROC_CPU_CONTROL 0x44 #define APPLE_ANS_COPROC_CPU_CONTROL_RUN BIT(4) @@ -75,6 +74,8 @@ #define APPLE_NVME_AQ_DEPTH 2 #define APPLE_NVME_AQ_MQ_TAG_DEPTH (APPLE_NVME_AQ_DEPTH - 1) +#define APPLE_NVME_IOSQES 7 + /* * These can be higher, but we need to ensure that any command doesn't * require an sg allocation that needs more than a page of data. @@ -142,6 +143,7 @@ struct apple_nvme_queue { u32 __iomem *sq_db; u32 __iomem *cq_db; + u16 sq_tail; u16 cq_head; u8 cq_phase; @@ -166,11 +168,17 @@ struct apple_nvme_iod { struct scatterlist *sg; }; +struct apple_nvme_hw { + bool has_lsq_nvmmu; + u32 max_queue_depth; +}; + struct apple_nvme { struct device *dev; void __iomem *mmio_coproc; void __iomem *mmio_nvme; + const struct apple_nvme_hw *hw; struct device **pd_dev; struct device_link **pd_link; @@ -215,10 +223,12 @@ static inline struct apple_nvme *queue_to_apple_nvme(struct apple_nvme_queue *q) static unsigned int apple_nvme_queue_depth(struct apple_nvme_queue *q) { - if (q->is_adminq) + struct apple_nvme *anv = queue_to_apple_nvme(q); + + if (q->is_adminq && anv->hw->has_lsq_nvmmu) return APPLE_NVME_AQ_DEPTH; - return APPLE_ANS_MAX_QUEUE_DEPTH; + return anv->hw->max_queue_depth; } static void apple_nvme_rtkit_crashed(void *cookie, const void *crashlog, size_t crashlog_size) @@ -280,7 +290,28 @@ static void apple_nvmmu_inval(struct apple_nvme_queue *q, unsigned int tag) "NVMMU TCB invalidation failed\n"); } -static void apple_nvme_submit_cmd(struct apple_nvme_queue *q, +static void apple_nvme_submit_cmd_t8015(struct apple_nvme_queue *q, + struct nvme_command *cmd) +{ + struct apple_nvme *anv = queue_to_apple_nvme(q); + + spin_lock_irq(&anv->lock); + + if (q->is_adminq) + memcpy(&q->sqes[q->sq_tail], cmd, sizeof(*cmd)); + else + memcpy((void *)q->sqes + (q->sq_tail << APPLE_NVME_IOSQES), + cmd, sizeof(*cmd)); + + if (++q->sq_tail == anv->hw->max_queue_depth) + q->sq_tail = 0; + + writel(q->sq_tail, q->sq_db); + spin_unlock_irq(&anv->lock); +} + + +static void apple_nvme_submit_cmd_t8103(struct apple_nvme_queue *q, struct nvme_command *cmd) { struct apple_nvme *anv = queue_to_apple_nvme(q); @@ -590,7 +621,8 @@ static inline void apple_nvme_handle_cqe(struct apple_nvme_queue *q, __u16 command_id = READ_ONCE(cqe->command_id); struct request *req; - apple_nvmmu_inval(q, command_id); + if (anv->hw->has_lsq_nvmmu) + apple_nvmmu_inval(q, command_id); req = nvme_find_rq(apple_nvme_queue_tagset(anv, q), command_id); if (unlikely(!req)) { @@ -685,7 +717,7 @@ static int apple_nvme_create_cq(struct apple_nvme *anv) c.create_cq.opcode = nvme_admin_create_cq; c.create_cq.prp1 = cpu_to_le64(anv->ioq.cq_dma_addr); c.create_cq.cqid = cpu_to_le16(1); - c.create_cq.qsize = cpu_to_le16(APPLE_ANS_MAX_QUEUE_DEPTH - 1); + c.create_cq.qsize = cpu_to_le16(anv->hw->max_queue_depth - 1); c.create_cq.cq_flags = cpu_to_le16(NVME_QUEUE_PHYS_CONTIG | NVME_CQ_IRQ_ENABLED); c.create_cq.irq_vector = cpu_to_le16(0); @@ -713,7 +745,7 @@ static int apple_nvme_create_sq(struct apple_nvme *anv) c.create_sq.opcode = nvme_admin_create_sq; c.create_sq.prp1 = cpu_to_le64(anv->ioq.sq_dma_addr); c.create_sq.sqid = cpu_to_le16(1); - c.create_sq.qsize = cpu_to_le16(APPLE_ANS_MAX_QUEUE_DEPTH - 1); + c.create_sq.qsize = cpu_to_le16(anv->hw->max_queue_depth - 1); c.create_sq.sq_flags = cpu_to_le16(NVME_QUEUE_PHYS_CONTIG); c.create_sq.cqid = cpu_to_le16(1); @@ -765,7 +797,12 @@ static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx, } nvme_start_request(req); - apple_nvme_submit_cmd(q, cmnd); + + if (anv->hw->has_lsq_nvmmu) + apple_nvme_submit_cmd_t8103(q, cmnd); + else + apple_nvme_submit_cmd_t8015(q, cmnd); + return BLK_STS_OK; out_free_cmd: @@ -970,11 +1007,13 @@ static const struct blk_mq_ops apple_nvme_mq_ops = { static void apple_nvme_init_queue(struct apple_nvme_queue *q) { unsigned int depth = apple_nvme_queue_depth(q); + struct apple_nvme *anv = queue_to_apple_nvme(q); q->cq_head = 0; q->cq_phase = 1; - memset(q->tcbs, 0, - APPLE_ANS_MAX_QUEUE_DEPTH * sizeof(struct apple_nvmmu_tcb)); + if (anv->hw->has_lsq_nvmmu) + memset(q->tcbs, 0, anv->hw->max_queue_depth + * sizeof(struct apple_nvmmu_tcb)); memset(q->cqes, 0, depth * sizeof(struct nvme_completion)); WRITE_ONCE(q->enabled, true); wmb(); /* ensure the first interrupt sees the initialization */ @@ -1069,49 +1108,55 @@ static void apple_nvme_reset_work(struct work_struct *work) dma_set_max_seg_size(anv->dev, 0xffffffff); - /* - * Enable NVMMU and linear submission queues. - * While we could keep those disabled and pretend this is slightly - * more common NVMe controller we'd still need some quirks (e.g. - * sq entries will be 128 bytes) and Apple might drop support for - * that mode in the future. - */ - writel(APPLE_ANS_LINEAR_SQ_EN, - anv->mmio_nvme + APPLE_ANS_LINEAR_SQ_CTRL); + if (anv->hw->has_lsq_nvmmu) { + /* + * Enable NVMMU and linear submission queues which is required + * since T6000. + */ + writel(APPLE_ANS_LINEAR_SQ_EN, + anv->mmio_nvme + APPLE_ANS_LINEAR_SQ_CTRL); - /* Allow as many pending command as possible for both queues */ - writel(APPLE_ANS_MAX_QUEUE_DEPTH | (APPLE_ANS_MAX_QUEUE_DEPTH << 16), - anv->mmio_nvme + APPLE_ANS_MAX_PEND_CMDS_CTRL); + /* Allow as many pending command as possible for both queues */ + writel(anv->hw->max_queue_depth + | (anv->hw->max_queue_depth << 16), anv->mmio_nvme + + APPLE_ANS_MAX_PEND_CMDS_CTRL); - /* Setup the NVMMU for the maximum admin and IO queue depth */ - writel(APPLE_ANS_MAX_QUEUE_DEPTH - 1, - anv->mmio_nvme + APPLE_NVMMU_NUM_TCBS); + /* Setup the NVMMU for the maximum admin and IO queue depth */ + writel(anv->hw->max_queue_depth - 1, + anv->mmio_nvme + APPLE_NVMMU_NUM_TCBS); - /* - * This is probably a chicken bit: without it all commands where any PRP - * is set to zero (including those that don't use that field) fail and - * the co-processor complains about "completed with err BAD_CMD-" or - * a "NULL_PRP_PTR_ERR" in the syslog - */ - writel(readl(anv->mmio_nvme + APPLE_ANS_UNKNOWN_CTRL) & - ~APPLE_ANS_PRP_NULL_CHECK, - anv->mmio_nvme + APPLE_ANS_UNKNOWN_CTRL); + /* + * This is probably a chicken bit: without it all commands + * where any PRP is set to zero (including those that don't use + * that field) fail and the co-processor complains about + * "completed with err BAD_CMD-" or a "NULL_PRP_PTR_ERR" in the + * syslog + */ + writel(readl(anv->mmio_nvme + APPLE_ANS_UNKNOWN_CTRL) & + ~APPLE_ANS_PRP_NULL_CHECK, + anv->mmio_nvme + APPLE_ANS_UNKNOWN_CTRL); + } /* Setup the admin queue */ - aqa = APPLE_NVME_AQ_DEPTH - 1; + if (anv->hw->has_lsq_nvmmu) + aqa = APPLE_NVME_AQ_DEPTH - 1; + else + aqa = anv->hw->max_queue_depth - 1; aqa |= aqa << 16; writel(aqa, anv->mmio_nvme + NVME_REG_AQA); writeq(anv->adminq.sq_dma_addr, anv->mmio_nvme + NVME_REG_ASQ); writeq(anv->adminq.cq_dma_addr, anv->mmio_nvme + NVME_REG_ACQ); - /* Setup NVMMU for both queues */ - writeq(anv->adminq.tcb_dma_addr, - anv->mmio_nvme + APPLE_NVMMU_ASQ_TCB_BASE); - writeq(anv->ioq.tcb_dma_addr, - anv->mmio_nvme + APPLE_NVMMU_IOSQ_TCB_BASE); + if (anv->hw->has_lsq_nvmmu) { + /* Setup NVMMU for both queues */ + writeq(anv->adminq.tcb_dma_addr, + anv->mmio_nvme + APPLE_NVMMU_ASQ_TCB_BASE); + writeq(anv->ioq.tcb_dma_addr, + anv->mmio_nvme + APPLE_NVMMU_IOSQ_TCB_BASE); + } anv->ctrl.sqsize = - APPLE_ANS_MAX_QUEUE_DEPTH - 1; /* 0's based queue depth */ + anv->hw->max_queue_depth - 1; /* 0's based queue depth */ anv->ctrl.cap = readq(anv->mmio_nvme + NVME_REG_CAP); dev_dbg(anv->dev, "Enabling controller now"); @@ -1282,8 +1327,9 @@ static int apple_nvme_alloc_tagsets(struct apple_nvme *anv) * both queues. The admin queue gets the first APPLE_NVME_AQ_DEPTH which * must be marked as reserved in the IO queue. */ - anv->tagset.reserved_tags = APPLE_NVME_AQ_DEPTH; - anv->tagset.queue_depth = APPLE_ANS_MAX_QUEUE_DEPTH - 1; + if (anv->hw->has_lsq_nvmmu) + anv->tagset.reserved_tags = APPLE_NVME_AQ_DEPTH; + anv->tagset.queue_depth = anv->hw->max_queue_depth - 1; anv->tagset.timeout = NVME_IO_TIMEOUT; anv->tagset.numa_node = NUMA_NO_NODE; anv->tagset.cmd_size = sizeof(struct apple_nvme_iod); @@ -1307,6 +1353,7 @@ static int apple_nvme_queue_alloc(struct apple_nvme *anv, struct apple_nvme_queue *q) { unsigned int depth = apple_nvme_queue_depth(q); + size_t iosq_size; q->cqes = dmam_alloc_coherent(anv->dev, depth * sizeof(struct nvme_completion), @@ -1314,22 +1361,28 @@ static int apple_nvme_queue_alloc(struct apple_nvme *anv, if (!q->cqes) return -ENOMEM; - q->sqes = dmam_alloc_coherent(anv->dev, - depth * sizeof(struct nvme_command), + if (anv->hw->has_lsq_nvmmu) + iosq_size = depth * sizeof(struct nvme_command); + else + iosq_size = depth << APPLE_NVME_IOSQES; + + q->sqes = dmam_alloc_coherent(anv->dev, iosq_size, &q->sq_dma_addr, GFP_KERNEL); if (!q->sqes) return -ENOMEM; - /* - * We need the maximum queue depth here because the NVMMU only has a - * single depth configuration shared between both queues. - */ - q->tcbs = dmam_alloc_coherent(anv->dev, - APPLE_ANS_MAX_QUEUE_DEPTH * - sizeof(struct apple_nvmmu_tcb), - &q->tcb_dma_addr, GFP_KERNEL); - if (!q->tcbs) - return -ENOMEM; + if (anv->hw->has_lsq_nvmmu) { + /* + * We need the maximum queue depth here because the NVMMU only + * has a single depth configuration shared between both queues. + */ + q->tcbs = dmam_alloc_coherent(anv->dev, + anv->hw->max_queue_depth * + sizeof(struct apple_nvmmu_tcb), + &q->tcb_dma_addr, GFP_KERNEL); + if (!q->tcbs) + return -ENOMEM; + } /* * initialize phase to make sure the allocated and empty memory @@ -1413,6 +1466,12 @@ static struct apple_nvme *apple_nvme_alloc(struct platform_device *pdev) anv->adminq.is_adminq = true; platform_set_drvdata(pdev, anv); + anv->hw = of_device_get_match_data(&pdev->dev); + if (!anv->hw) { + ret = -ENODEV; + goto put_dev; + } + ret = apple_nvme_attach_genpd(anv); if (ret < 0) { dev_err_probe(dev, ret, "Failed to attach power domains"); @@ -1444,10 +1503,17 @@ static struct apple_nvme *apple_nvme_alloc(struct platform_device *pdev) goto put_dev; } - anv->adminq.sq_db = anv->mmio_nvme + APPLE_ANS_LINEAR_ASQ_DB; - anv->adminq.cq_db = anv->mmio_nvme + APPLE_ANS_ACQ_DB; - anv->ioq.sq_db = anv->mmio_nvme + APPLE_ANS_LINEAR_IOSQ_DB; - anv->ioq.cq_db = anv->mmio_nvme + APPLE_ANS_IOCQ_DB; + if (anv->hw->has_lsq_nvmmu) { + anv->adminq.sq_db = anv->mmio_nvme + APPLE_ANS_LINEAR_ASQ_DB; + anv->adminq.cq_db = anv->mmio_nvme + APPLE_ANS_ACQ_DB; + anv->ioq.sq_db = anv->mmio_nvme + APPLE_ANS_LINEAR_IOSQ_DB; + anv->ioq.cq_db = anv->mmio_nvme + APPLE_ANS_IOCQ_DB; + } else { + anv->adminq.sq_db = anv->mmio_nvme + NVME_REG_DBS; + anv->adminq.cq_db = anv->mmio_nvme + APPLE_ANS_ACQ_DB; + anv->ioq.sq_db = anv->mmio_nvme + NVME_REG_DBS + 8; + anv->ioq.cq_db = anv->mmio_nvme + APPLE_ANS_IOCQ_DB; + } anv->sart = devm_apple_sart_get(dev); if (IS_ERR(anv->sart)) { @@ -1625,8 +1691,19 @@ static int apple_nvme_suspend(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(apple_nvme_pm_ops, apple_nvme_suspend, apple_nvme_resume); +static const struct apple_nvme_hw apple_nvme_t8015_hw = { + .has_lsq_nvmmu = false, + .max_queue_depth = 16, +}; + +static const struct apple_nvme_hw apple_nvme_t8103_hw = { + .has_lsq_nvmmu = true, + .max_queue_depth = 64, +}; + static const struct of_device_id apple_nvme_of_match[] = { - { .compatible = "apple,nvme-ans2" }, + { .compatible = "apple,t8015-nvme-ans2", .data = &apple_nvme_t8015_hw }, + { .compatible = "apple,nvme-ans2", .data = &apple_nvme_t8103_hw }, {}, }; MODULE_DEVICE_TABLE(of, apple_nvme_of_match); diff --git a/drivers/pinctrl/pinctrl-apple-gpio.c b/drivers/pinctrl/pinctrl-apple-gpio.c index a09daa72bfe4..e1a7bc8cf765 100644 --- a/drivers/pinctrl/pinctrl-apple-gpio.c +++ b/drivers/pinctrl/pinctrl-apple-gpio.c @@ -515,6 +515,7 @@ static int apple_gpio_pinctrl_probe(struct platform_device *pdev) } static const struct of_device_id apple_gpio_pinctrl_of_match[] = { + { .compatible = "apple,t8103-pinctrl", }, { .compatible = "apple,pinctrl", }, { } }; diff --git a/drivers/pmdomain/apple/pmgr-pwrstate.c b/drivers/pmdomain/apple/pmgr-pwrstate.c index 9467235110f4..82c33cf727a8 100644 --- a/drivers/pmdomain/apple/pmgr-pwrstate.c +++ b/drivers/pmdomain/apple/pmgr-pwrstate.c @@ -306,6 +306,7 @@ err_remove: } static const struct of_device_id apple_pmgr_ps_of_match[] = { + { .compatible = "apple,t8103-pmgr-pwrstate" }, { .compatible = "apple,pmgr-pwrstate" }, {} }; diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index 94af77baa7a1..e98b7e03162c 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -317,7 +317,7 @@ static int adsp_load(struct rproc *rproc, const struct firmware *fw) struct qcom_adsp *adsp = rproc->priv; int ret; - ret = qcom_mdt_load_no_init(adsp->dev, fw, rproc->firmware, 0, + ret = qcom_mdt_load_no_init(adsp->dev, fw, rproc->firmware, adsp->mem_region, adsp->mem_phys, adsp->mem_size, &adsp->mem_reloc); if (ret) diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 02e29171cbbe..55a7da801183 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -242,9 +242,8 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) goto release_dtb_firmware; ret = qcom_mdt_load_no_init(pas->dev, pas->dtb_firmware, pas->dtb_firmware_name, - pas->dtb_pas_id, pas->dtb_mem_region, - pas->dtb_mem_phys, pas->dtb_mem_size, - &pas->dtb_mem_reloc); + pas->dtb_mem_region, pas->dtb_mem_phys, + pas->dtb_mem_size, &pas->dtb_mem_reloc); if (ret) goto release_dtb_metadata; } @@ -307,7 +306,7 @@ static int qcom_pas_start(struct rproc *rproc) if (ret) goto disable_px_supply; - ret = qcom_mdt_load_no_init(pas->dev, pas->firmware, rproc->firmware, pas->pas_id, + ret = qcom_mdt_load_no_init(pas->dev, pas->firmware, rproc->firmware, pas->mem_region, pas->mem_phys, pas->mem_size, &pas->mem_reloc); if (ret) diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c index 93648734a2f2..07c88623f597 100644 --- a/drivers/remoteproc/qcom_q6v5_wcss.c +++ b/drivers/remoteproc/qcom_q6v5_wcss.c @@ -757,7 +757,7 @@ static int q6v5_wcss_load(struct rproc *rproc, const struct firmware *fw) int ret; ret = qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware, - 0, wcss->mem_region, wcss->mem_phys, + wcss->mem_region, wcss->mem_phys, wcss->mem_size, &wcss->mem_reloc); if (ret) return ret; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 635eef469ab7..78b7078478d4 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -22,6 +22,13 @@ config RESET_A10SR This option enables support for the external reset functions for peripheral PHYs on the Altera Arria10 System Resource Chip. +config RESET_ASPEED + tristate "ASPEED Reset Driver" + depends on ARCH_ASPEED || COMPILE_TEST + select AUXILIARY_BUS + help + This enables the reset controller driver for AST2700. + config RESET_ATH79 bool "AR71xx Reset Driver" if COMPILE_TEST default ATH79 diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index a917d2522e8d..f7934f9fb90b 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -6,6 +6,7 @@ obj-y += starfive/ obj-y += sti/ obj-y += tegra/ obj-$(CONFIG_RESET_A10SR) += reset-a10sr.o +obj-$(CONFIG_RESET_ASPEED) += reset-aspeed.o obj-$(CONFIG_RESET_ATH79) += reset-ath79.o obj-$(CONFIG_RESET_AXS10X) += reset-axs10x.o obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o diff --git a/drivers/reset/reset-aspeed.c b/drivers/reset/reset-aspeed.c new file mode 100644 index 000000000000..dd2f860a69d7 --- /dev/null +++ b/drivers/reset/reset-aspeed.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 ASPEED Technology Inc. + */ + +#include <linux/auxiliary_bus.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/reset-controller.h> +#include <linux/slab.h> + +#include <dt-bindings/reset/aspeed,ast2700-scu.h> + +#define SCU0_RESET_CTRL1 0x200 +#define SCU0_RESET_CTRL2 0x220 +#define SCU1_RESET_CTRL1 0x200 +#define SCU1_RESET_CTRL2 0x220 +#define SCU1_PCIE3_CTRL 0x908 + +struct ast2700_reset_signal { + bool dedicated_clr; /* dedicated reset clr offset */ + u32 offset, bit; +}; + +struct aspeed_reset_info { + unsigned int nr_resets; + const struct ast2700_reset_signal *signal; +}; + +struct aspeed_reset { + struct reset_controller_dev rcdev; + struct aspeed_reset_info *info; + spinlock_t lock; /* Protect read-modify-write cycle */ + void __iomem *base; +}; + +static const struct ast2700_reset_signal ast2700_reset0_signals[] = { + [SCU0_RESET_SDRAM] = { true, SCU0_RESET_CTRL1, BIT(0) }, + [SCU0_RESET_DDRPHY] = { true, SCU0_RESET_CTRL1, BIT(1) }, + [SCU0_RESET_RSA] = { true, SCU0_RESET_CTRL1, BIT(2) }, + [SCU0_RESET_SHA3] = { true, SCU0_RESET_CTRL1, BIT(3) }, + [SCU0_RESET_HACE] = { true, SCU0_RESET_CTRL1, BIT(4) }, + [SCU0_RESET_SOC] = { true, SCU0_RESET_CTRL1, BIT(5) }, + [SCU0_RESET_VIDEO] = { true, SCU0_RESET_CTRL1, BIT(6) }, + [SCU0_RESET_2D] = { true, SCU0_RESET_CTRL1, BIT(7) }, + [SCU0_RESET_PCIS] = { true, SCU0_RESET_CTRL1, BIT(8) }, + [SCU0_RESET_RVAS0] = { true, SCU0_RESET_CTRL1, BIT(9) }, + [SCU0_RESET_RVAS1] = { true, SCU0_RESET_CTRL1, BIT(10) }, + [SCU0_RESET_SM3] = { true, SCU0_RESET_CTRL1, BIT(11) }, + [SCU0_RESET_SM4] = { true, SCU0_RESET_CTRL1, BIT(12) }, + [SCU0_RESET_CRT0] = { true, SCU0_RESET_CTRL1, BIT(13) }, + [SCU0_RESET_ECC] = { true, SCU0_RESET_CTRL1, BIT(14) }, + [SCU0_RESET_DP_PCI] = { true, SCU0_RESET_CTRL1, BIT(15) }, + [SCU0_RESET_UFS] = { true, SCU0_RESET_CTRL1, BIT(16) }, + [SCU0_RESET_EMMC] = { true, SCU0_RESET_CTRL1, BIT(17) }, + [SCU0_RESET_PCIE1RST] = { true, SCU0_RESET_CTRL1, BIT(18) }, + [SCU0_RESET_PCIE1RSTOE] = { true, SCU0_RESET_CTRL1, BIT(19) }, + [SCU0_RESET_PCIE0RST] = { true, SCU0_RESET_CTRL1, BIT(20) }, + [SCU0_RESET_PCIE0RSTOE] = { true, SCU0_RESET_CTRL1, BIT(21) }, + [SCU0_RESET_JTAG] = { true, SCU0_RESET_CTRL1, BIT(22) }, + [SCU0_RESET_MCTP0] = { true, SCU0_RESET_CTRL1, BIT(23) }, + [SCU0_RESET_MCTP1] = { true, SCU0_RESET_CTRL1, BIT(24) }, + [SCU0_RESET_XDMA0] = { true, SCU0_RESET_CTRL1, BIT(25) }, + [SCU0_RESET_XDMA1] = { true, SCU0_RESET_CTRL1, BIT(26) }, + [SCU0_RESET_H2X1] = { true, SCU0_RESET_CTRL1, BIT(27) }, + [SCU0_RESET_DP] = { true, SCU0_RESET_CTRL1, BIT(28) }, + [SCU0_RESET_DP_MCU] = { true, SCU0_RESET_CTRL1, BIT(29) }, + [SCU0_RESET_SSP] = { true, SCU0_RESET_CTRL1, BIT(30) }, + [SCU0_RESET_H2X0] = { true, SCU0_RESET_CTRL1, BIT(31) }, + [SCU0_RESET_PORTA_VHUB] = { true, SCU0_RESET_CTRL2, BIT(0) }, + [SCU0_RESET_PORTA_PHY3] = { true, SCU0_RESET_CTRL2, BIT(1) }, + [SCU0_RESET_PORTA_XHCI] = { true, SCU0_RESET_CTRL2, BIT(2) }, + [SCU0_RESET_PORTB_VHUB] = { true, SCU0_RESET_CTRL2, BIT(3) }, + [SCU0_RESET_PORTB_PHY3] = { true, SCU0_RESET_CTRL2, BIT(4) }, + [SCU0_RESET_PORTB_XHCI] = { true, SCU0_RESET_CTRL2, BIT(5) }, + [SCU0_RESET_PORTA_VHUB_EHCI] = { true, SCU0_RESET_CTRL2, BIT(6) }, + [SCU0_RESET_PORTB_VHUB_EHCI] = { true, SCU0_RESET_CTRL2, BIT(7) }, + [SCU0_RESET_UHCI] = { true, SCU0_RESET_CTRL2, BIT(8) }, + [SCU0_RESET_TSP] = { true, SCU0_RESET_CTRL2, BIT(9) }, + [SCU0_RESET_E2M0] = { true, SCU0_RESET_CTRL2, BIT(10) }, + [SCU0_RESET_E2M1] = { true, SCU0_RESET_CTRL2, BIT(11) }, + [SCU0_RESET_VLINK] = { true, SCU0_RESET_CTRL2, BIT(12) }, +}; + +static const struct ast2700_reset_signal ast2700_reset1_signals[] = { + [SCU1_RESET_LPC0] = { true, SCU1_RESET_CTRL1, BIT(0) }, + [SCU1_RESET_LPC1] = { true, SCU1_RESET_CTRL1, BIT(1) }, + [SCU1_RESET_MII] = { true, SCU1_RESET_CTRL1, BIT(2) }, + [SCU1_RESET_PECI] = { true, SCU1_RESET_CTRL1, BIT(3) }, + [SCU1_RESET_PWM] = { true, SCU1_RESET_CTRL1, BIT(4) }, + [SCU1_RESET_MAC0] = { true, SCU1_RESET_CTRL1, BIT(5) }, + [SCU1_RESET_MAC1] = { true, SCU1_RESET_CTRL1, BIT(6) }, + [SCU1_RESET_MAC2] = { true, SCU1_RESET_CTRL1, BIT(7) }, + [SCU1_RESET_ADC] = { true, SCU1_RESET_CTRL1, BIT(8) }, + [SCU1_RESET_SD] = { true, SCU1_RESET_CTRL1, BIT(9) }, + [SCU1_RESET_ESPI0] = { true, SCU1_RESET_CTRL1, BIT(10) }, + [SCU1_RESET_ESPI1] = { true, SCU1_RESET_CTRL1, BIT(11) }, + [SCU1_RESET_JTAG1] = { true, SCU1_RESET_CTRL1, BIT(12) }, + [SCU1_RESET_SPI0] = { true, SCU1_RESET_CTRL1, BIT(13) }, + [SCU1_RESET_SPI1] = { true, SCU1_RESET_CTRL1, BIT(14) }, + [SCU1_RESET_SPI2] = { true, SCU1_RESET_CTRL1, BIT(15) }, + [SCU1_RESET_I3C0] = { true, SCU1_RESET_CTRL1, BIT(16) }, + [SCU1_RESET_I3C1] = { true, SCU1_RESET_CTRL1, BIT(17) }, + [SCU1_RESET_I3C2] = { true, SCU1_RESET_CTRL1, BIT(18) }, + [SCU1_RESET_I3C3] = { true, SCU1_RESET_CTRL1, BIT(19) }, + [SCU1_RESET_I3C4] = { true, SCU1_RESET_CTRL1, BIT(20) }, + [SCU1_RESET_I3C5] = { true, SCU1_RESET_CTRL1, BIT(21) }, + [SCU1_RESET_I3C6] = { true, SCU1_RESET_CTRL1, BIT(22) }, + [SCU1_RESET_I3C7] = { true, SCU1_RESET_CTRL1, BIT(23) }, + [SCU1_RESET_I3C8] = { true, SCU1_RESET_CTRL1, BIT(24) }, + [SCU1_RESET_I3C9] = { true, SCU1_RESET_CTRL1, BIT(25) }, + [SCU1_RESET_I3C10] = { true, SCU1_RESET_CTRL1, BIT(26) }, + [SCU1_RESET_I3C11] = { true, SCU1_RESET_CTRL1, BIT(27) }, + [SCU1_RESET_I3C12] = { true, SCU1_RESET_CTRL1, BIT(28) }, + [SCU1_RESET_I3C13] = { true, SCU1_RESET_CTRL1, BIT(29) }, + [SCU1_RESET_I3C14] = { true, SCU1_RESET_CTRL1, BIT(30) }, + [SCU1_RESET_I3C15] = { true, SCU1_RESET_CTRL1, BIT(31) }, + [SCU1_RESET_MCU0] = { true, SCU1_RESET_CTRL2, BIT(0) }, + [SCU1_RESET_MCU1] = { true, SCU1_RESET_CTRL2, BIT(1) }, + [SCU1_RESET_H2A_SPI1] = { true, SCU1_RESET_CTRL2, BIT(2) }, + [SCU1_RESET_H2A_SPI2] = { true, SCU1_RESET_CTRL2, BIT(3) }, + [SCU1_RESET_UART0] = { true, SCU1_RESET_CTRL2, BIT(4) }, + [SCU1_RESET_UART1] = { true, SCU1_RESET_CTRL2, BIT(5) }, + [SCU1_RESET_UART2] = { true, SCU1_RESET_CTRL2, BIT(6) }, + [SCU1_RESET_UART3] = { true, SCU1_RESET_CTRL2, BIT(7) }, + [SCU1_RESET_I2C_FILTER] = { true, SCU1_RESET_CTRL2, BIT(8) }, + [SCU1_RESET_CALIPTRA] = { true, SCU1_RESET_CTRL2, BIT(9) }, + [SCU1_RESET_XDMA] = { true, SCU1_RESET_CTRL2, BIT(10) }, + [SCU1_RESET_FSI] = { true, SCU1_RESET_CTRL2, BIT(12) }, + [SCU1_RESET_CAN] = { true, SCU1_RESET_CTRL2, BIT(13) }, + [SCU1_RESET_MCTP] = { true, SCU1_RESET_CTRL2, BIT(14) }, + [SCU1_RESET_I2C] = { true, SCU1_RESET_CTRL2, BIT(15) }, + [SCU1_RESET_UART6] = { true, SCU1_RESET_CTRL2, BIT(16) }, + [SCU1_RESET_UART7] = { true, SCU1_RESET_CTRL2, BIT(17) }, + [SCU1_RESET_UART8] = { true, SCU1_RESET_CTRL2, BIT(18) }, + [SCU1_RESET_UART9] = { true, SCU1_RESET_CTRL2, BIT(19) }, + [SCU1_RESET_LTPI0] = { true, SCU1_RESET_CTRL2, BIT(20) }, + [SCU1_RESET_VGAL] = { true, SCU1_RESET_CTRL2, BIT(21) }, + [SCU1_RESET_LTPI1] = { true, SCU1_RESET_CTRL2, BIT(22) }, + [SCU1_RESET_ACE] = { true, SCU1_RESET_CTRL2, BIT(23) }, + [SCU1_RESET_E2M] = { true, SCU1_RESET_CTRL2, BIT(24) }, + [SCU1_RESET_UHCI] = { true, SCU1_RESET_CTRL2, BIT(25) }, + [SCU1_RESET_PORTC_USB2UART] = { true, SCU1_RESET_CTRL2, BIT(26) }, + [SCU1_RESET_PORTC_VHUB_EHCI] = { true, SCU1_RESET_CTRL2, BIT(27) }, + [SCU1_RESET_PORTD_USB2UART] = { true, SCU1_RESET_CTRL2, BIT(28) }, + [SCU1_RESET_PORTD_VHUB_EHCI] = { true, SCU1_RESET_CTRL2, BIT(29) }, + [SCU1_RESET_H2X] = { true, SCU1_RESET_CTRL2, BIT(30) }, + [SCU1_RESET_I3CDMA] = { true, SCU1_RESET_CTRL2, BIT(31) }, + [SCU1_RESET_PCIE2RST] = { false, SCU1_PCIE3_CTRL, BIT(0) }, +}; + +static inline struct aspeed_reset *to_aspeed_reset(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct aspeed_reset, rcdev); +} + +static int aspeed_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct aspeed_reset *rc = to_aspeed_reset(rcdev); + void __iomem *reg_offset = rc->base + rc->info->signal[id].offset; + + if (rc->info->signal[id].dedicated_clr) { + writel(rc->info->signal[id].bit, reg_offset); + } else { + guard(spinlock_irqsave)(&rc->lock); + writel(readl(reg_offset) & ~rc->info->signal[id].bit, reg_offset); + } + + return 0; +} + +static int aspeed_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct aspeed_reset *rc = to_aspeed_reset(rcdev); + void __iomem *reg_offset = rc->base + rc->info->signal[id].offset; + + if (rc->info->signal[id].dedicated_clr) { + writel(rc->info->signal[id].bit, reg_offset + 0x04); + } else { + guard(spinlock_irqsave)(&rc->lock); + writel(readl(reg_offset) | rc->info->signal[id].bit, reg_offset); + } + + return 0; +} + +static int aspeed_reset_status(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct aspeed_reset *rc = to_aspeed_reset(rcdev); + void __iomem *reg_offset = rc->base + rc->info->signal[id].offset; + + return (readl(reg_offset) & rc->info->signal[id].bit) ? 1 : 0; +} + +static const struct reset_control_ops aspeed_reset_ops = { + .assert = aspeed_reset_assert, + .deassert = aspeed_reset_deassert, + .status = aspeed_reset_status, +}; + +static int aspeed_reset_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct aspeed_reset *reset; + struct device *dev = &adev->dev; + + reset = devm_kzalloc(dev, sizeof(*reset), GFP_KERNEL); + if (!reset) + return -ENOMEM; + + spin_lock_init(&reset->lock); + + reset->info = (struct aspeed_reset_info *)id->driver_data; + reset->rcdev.owner = THIS_MODULE; + reset->rcdev.nr_resets = reset->info->nr_resets; + reset->rcdev.ops = &aspeed_reset_ops; + reset->rcdev.of_node = dev->parent->of_node; + reset->rcdev.dev = dev; + reset->rcdev.of_reset_n_cells = 1; + reset->base = (void __iomem *)adev->dev.platform_data; + + return devm_reset_controller_register(dev, &reset->rcdev); +} + +static const struct aspeed_reset_info ast2700_reset0_info = { + .nr_resets = ARRAY_SIZE(ast2700_reset0_signals), + .signal = ast2700_reset0_signals, +}; + +static const struct aspeed_reset_info ast2700_reset1_info = { + .nr_resets = ARRAY_SIZE(ast2700_reset1_signals), + .signal = ast2700_reset1_signals, +}; + +static const struct auxiliary_device_id aspeed_reset_ids[] = { + { .name = "clk_ast2700.reset0", .driver_data = (kernel_ulong_t)&ast2700_reset0_info }, + { .name = "clk_ast2700.reset1", .driver_data = (kernel_ulong_t)&ast2700_reset1_info }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, aspeed_reset_ids); + +static struct auxiliary_driver aspeed_reset_driver = { + .probe = aspeed_reset_probe, + .id_table = aspeed_reset_ids, +}; + +module_auxiliary_driver(aspeed_reset_driver); + +MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); +MODULE_DESCRIPTION("ASPEED SoC Reset Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/reset/reset-bcm6345.c b/drivers/reset/reset-bcm6345.c index aa9353439e70..56518f7bfbb3 100644 --- a/drivers/reset/reset-bcm6345.c +++ b/drivers/reset/reset-bcm6345.c @@ -119,6 +119,7 @@ static int bcm6345_reset_probe(struct platform_device *pdev) static const struct of_device_id bcm6345_reset_of_match[] = { { .compatible = "brcm,bcm6345-reset" }, + { .compatible = "brcm,bcm63xx-ephy-ctrl" }, { /* sentinel */ }, }; diff --git a/drivers/reset/reset-intel-gw.c b/drivers/reset/reset-intel-gw.c index a5a01388ae7f..a5ce3350cb5e 100644 --- a/drivers/reset/reset-intel-gw.c +++ b/drivers/reset/reset-intel-gw.c @@ -40,7 +40,6 @@ static const struct regmap_config intel_rcu_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .fast_io = true, }; /* diff --git a/drivers/reset/reset-qcom-pdc.c b/drivers/reset/reset-qcom-pdc.c index dce1fc1a68ad..ae2b5aba7a59 100644 --- a/drivers/reset/reset-qcom-pdc.c +++ b/drivers/reset/reset-qcom-pdc.c @@ -36,7 +36,6 @@ static const struct regmap_config pdc_regmap_config = { .reg_stride = 4, .val_bits = 32, .max_register = 0x20000, - .fast_io = true, }; static const struct qcom_pdc_reset_map sdm845_pdc_resets[] = { diff --git a/drivers/reset/reset-th1520.c b/drivers/reset/reset-th1520.c index 7874f0693e1b..14d964a9c6b6 100644 --- a/drivers/reset/reset-th1520.c +++ b/drivers/reset/reset-th1520.c @@ -14,10 +14,20 @@ /* register offset in VOSYS_REGMAP */ #define TH1520_GPU_RST_CFG 0x0 #define TH1520_GPU_RST_CFG_MASK GENMASK(1, 0) +#define TH1520_DPU_RST_CFG 0x4 +#define TH1520_DSI0_RST_CFG 0x8 +#define TH1520_DSI1_RST_CFG 0xc +#define TH1520_HDMI_RST_CFG 0x14 /* register values */ #define TH1520_GPU_SW_GPU_RST BIT(0) #define TH1520_GPU_SW_CLKGEN_RST BIT(1) +#define TH1520_DPU_SW_DPU_HRST BIT(0) +#define TH1520_DPU_SW_DPU_ARST BIT(1) +#define TH1520_DPU_SW_DPU_CRST BIT(2) +#define TH1520_DSI_SW_DSI_PRST BIT(0) +#define TH1520_HDMI_SW_MAIN_RST BIT(0) +#define TH1520_HDMI_SW_PRST BIT(1) struct th1520_reset_priv { struct reset_controller_dev rcdev; @@ -37,7 +47,35 @@ static const struct th1520_reset_map th1520_resets[] = { [TH1520_RESET_ID_GPU_CLKGEN] = { .bit = TH1520_GPU_SW_CLKGEN_RST, .reg = TH1520_GPU_RST_CFG, - } + }, + [TH1520_RESET_ID_DPU_AHB] = { + .bit = TH1520_DPU_SW_DPU_HRST, + .reg = TH1520_DPU_RST_CFG, + }, + [TH1520_RESET_ID_DPU_AXI] = { + .bit = TH1520_DPU_SW_DPU_ARST, + .reg = TH1520_DPU_RST_CFG, + }, + [TH1520_RESET_ID_DPU_CORE] = { + .bit = TH1520_DPU_SW_DPU_CRST, + .reg = TH1520_DPU_RST_CFG, + }, + [TH1520_RESET_ID_DSI0_APB] = { + .bit = TH1520_DSI_SW_DSI_PRST, + .reg = TH1520_DSI0_RST_CFG, + }, + [TH1520_RESET_ID_DSI1_APB] = { + .bit = TH1520_DSI_SW_DSI_PRST, + .reg = TH1520_DSI1_RST_CFG, + }, + [TH1520_RESET_ID_HDMI] = { + .bit = TH1520_HDMI_SW_MAIN_RST, + .reg = TH1520_HDMI_RST_CFG, + }, + [TH1520_RESET_ID_HDMI_APB] = { + .bit = TH1520_HDMI_SW_PRST, + .reg = TH1520_HDMI_RST_CFG, + }, }; static inline struct th1520_reset_priv * @@ -78,7 +116,6 @@ static const struct regmap_config th1520_reset_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, - .fast_io = true, }; static int th1520_reset_probe(struct platform_device *pdev) diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig index 6388cbe1e56b..ad6736889231 100644 --- a/drivers/soc/apple/Kconfig +++ b/drivers/soc/apple/Kconfig @@ -8,7 +8,6 @@ config APPLE_MAILBOX tristate "Apple SoC mailboxes" depends on PM depends on ARCH_APPLE || (64BIT && COMPILE_TEST) - default ARCH_APPLE help Apple SoCs have various co-processors required for certain peripherals to work (NVMe, display controller, etc.). This @@ -21,7 +20,6 @@ config APPLE_RTKIT tristate "Apple RTKit co-processor IPC protocol" depends on APPLE_MAILBOX depends on ARCH_APPLE || COMPILE_TEST - default ARCH_APPLE help Apple SoCs such as the M1 come with various co-processors running their proprietary RTKit operating system. This option enables support @@ -33,7 +31,6 @@ config APPLE_RTKIT config APPLE_SART tristate "Apple SART DMA address filter" depends on ARCH_APPLE || COMPILE_TEST - default ARCH_APPLE help Apple SART is a simple DMA address filter used on Apple SoCs such as the M1. It is usually required for the NVMe coprocessor which does diff --git a/drivers/soc/apple/mailbox.c b/drivers/soc/apple/mailbox.c index 49a0955e82d6..8f29108dc69a 100644 --- a/drivers/soc/apple/mailbox.c +++ b/drivers/soc/apple/mailbox.c @@ -47,6 +47,9 @@ #define APPLE_ASC_MBOX_I2A_RECV0 0x830 #define APPLE_ASC_MBOX_I2A_RECV1 0x838 +#define APPLE_T8015_MBOX_A2I_CONTROL 0x108 +#define APPLE_T8015_MBOX_I2A_CONTROL 0x10c + #define APPLE_M3_MBOX_CONTROL_FULL BIT(16) #define APPLE_M3_MBOX_CONTROL_EMPTY BIT(17) @@ -382,6 +385,21 @@ static int apple_mbox_probe(struct platform_device *pdev) return 0; } +static const struct apple_mbox_hw apple_mbox_t8015_hw = { + .control_full = APPLE_ASC_MBOX_CONTROL_FULL, + .control_empty = APPLE_ASC_MBOX_CONTROL_EMPTY, + + .a2i_control = APPLE_T8015_MBOX_A2I_CONTROL, + .a2i_send0 = APPLE_ASC_MBOX_A2I_SEND0, + .a2i_send1 = APPLE_ASC_MBOX_A2I_SEND1, + + .i2a_control = APPLE_T8015_MBOX_I2A_CONTROL, + .i2a_recv0 = APPLE_ASC_MBOX_I2A_RECV0, + .i2a_recv1 = APPLE_ASC_MBOX_I2A_RECV1, + + .has_irq_controls = false, +}; + static const struct apple_mbox_hw apple_mbox_asc_hw = { .control_full = APPLE_ASC_MBOX_CONTROL_FULL, .control_empty = APPLE_ASC_MBOX_CONTROL_EMPTY, @@ -418,6 +436,7 @@ static const struct apple_mbox_hw apple_mbox_m3_hw = { static const struct of_device_id apple_mbox_of_match[] = { { .compatible = "apple,asc-mailbox-v4", .data = &apple_mbox_asc_hw }, + { .compatible = "apple,t8015-asc-mailbox", .data = &apple_mbox_t8015_hw }, { .compatible = "apple,m3-mailbox-v2", .data = &apple_mbox_m3_hw }, {} }; diff --git a/drivers/soc/apple/sart.c b/drivers/soc/apple/sart.c index afa111736899..4ff1942b82a7 100644 --- a/drivers/soc/apple/sart.c +++ b/drivers/soc/apple/sart.c @@ -25,8 +25,17 @@ #define APPLE_SART_MAX_ENTRIES 16 -/* This is probably a bitfield but the exact meaning of each bit is unknown. */ -#define APPLE_SART_FLAGS_ALLOW 0xff +/* SARTv0 registers */ +#define APPLE_SART0_CONFIG(idx) (0x00 + 4 * (idx)) +#define APPLE_SART0_CONFIG_FLAGS GENMASK(28, 24) +#define APPLE_SART0_CONFIG_SIZE GENMASK(18, 0) +#define APPLE_SART0_CONFIG_SIZE_SHIFT 12 +#define APPLE_SART0_CONFIG_SIZE_MAX GENMASK(18, 0) + +#define APPLE_SART0_PADDR(idx) (0x40 + 4 * (idx)) +#define APPLE_SART0_PADDR_SHIFT 12 + +#define APPLE_SART0_FLAGS_ALLOW 0xf /* SARTv2 registers */ #define APPLE_SART2_CONFIG(idx) (0x00 + 4 * (idx)) @@ -38,6 +47,8 @@ #define APPLE_SART2_PADDR(idx) (0x40 + 4 * (idx)) #define APPLE_SART2_PADDR_SHIFT 12 +#define APPLE_SART2_FLAGS_ALLOW 0xff + /* SARTv3 registers */ #define APPLE_SART3_CONFIG(idx) (0x00 + 4 * (idx)) @@ -48,11 +59,15 @@ #define APPLE_SART3_SIZE_SHIFT 12 #define APPLE_SART3_SIZE_MAX GENMASK(29, 0) +#define APPLE_SART3_FLAGS_ALLOW 0xff + struct apple_sart_ops { void (*get_entry)(struct apple_sart *sart, int index, u8 *flags, phys_addr_t *paddr, size_t *size); void (*set_entry)(struct apple_sart *sart, int index, u8 flags, phys_addr_t paddr_shifted, size_t size_shifted); + /* This is probably a bitfield but the exact meaning of each bit is unknown. */ + unsigned int flags_allow; unsigned int size_shift; unsigned int paddr_shift; size_t size_max; @@ -68,6 +83,39 @@ struct apple_sart { unsigned long used_entries; }; +static void sart0_get_entry(struct apple_sart *sart, int index, u8 *flags, + phys_addr_t *paddr, size_t *size) +{ + u32 cfg = readl(sart->regs + APPLE_SART0_CONFIG(index)); + phys_addr_t paddr_ = readl(sart->regs + APPLE_SART0_PADDR(index)); + size_t size_ = FIELD_GET(APPLE_SART0_CONFIG_SIZE, cfg); + + *flags = FIELD_GET(APPLE_SART0_CONFIG_FLAGS, cfg); + *size = size_ << APPLE_SART0_CONFIG_SIZE_SHIFT; + *paddr = paddr_ << APPLE_SART0_PADDR_SHIFT; +} + +static void sart0_set_entry(struct apple_sart *sart, int index, u8 flags, + phys_addr_t paddr_shifted, size_t size_shifted) +{ + u32 cfg; + + cfg = FIELD_PREP(APPLE_SART0_CONFIG_FLAGS, flags); + cfg |= FIELD_PREP(APPLE_SART0_CONFIG_SIZE, size_shifted); + + writel(paddr_shifted, sart->regs + APPLE_SART0_PADDR(index)); + writel(cfg, sart->regs + APPLE_SART0_CONFIG(index)); +} + +static struct apple_sart_ops sart_ops_v0 = { + .get_entry = sart0_get_entry, + .set_entry = sart0_set_entry, + .flags_allow = APPLE_SART0_FLAGS_ALLOW, + .size_shift = APPLE_SART0_CONFIG_SIZE_SHIFT, + .paddr_shift = APPLE_SART0_PADDR_SHIFT, + .size_max = APPLE_SART0_CONFIG_SIZE_MAX, +}; + static void sart2_get_entry(struct apple_sart *sart, int index, u8 *flags, phys_addr_t *paddr, size_t *size) { @@ -95,6 +143,7 @@ static void sart2_set_entry(struct apple_sart *sart, int index, u8 flags, static struct apple_sart_ops sart_ops_v2 = { .get_entry = sart2_get_entry, .set_entry = sart2_set_entry, + .flags_allow = APPLE_SART2_FLAGS_ALLOW, .size_shift = APPLE_SART2_CONFIG_SIZE_SHIFT, .paddr_shift = APPLE_SART2_PADDR_SHIFT, .size_max = APPLE_SART2_CONFIG_SIZE_MAX, @@ -122,6 +171,7 @@ static void sart3_set_entry(struct apple_sart *sart, int index, u8 flags, static struct apple_sart_ops sart_ops_v3 = { .get_entry = sart3_get_entry, .set_entry = sart3_set_entry, + .flags_allow = APPLE_SART3_FLAGS_ALLOW, .size_shift = APPLE_SART3_SIZE_SHIFT, .paddr_shift = APPLE_SART3_PADDR_SHIFT, .size_max = APPLE_SART3_SIZE_MAX, @@ -233,7 +283,7 @@ int apple_sart_add_allowed_region(struct apple_sart *sart, phys_addr_t paddr, if (test_and_set_bit(i, &sart->used_entries)) continue; - ret = sart_set_entry(sart, i, APPLE_SART_FLAGS_ALLOW, paddr, + ret = sart_set_entry(sart, i, sart->ops->flags_allow, paddr, size); if (ret) { dev_dbg(sart->dev, @@ -314,6 +364,10 @@ static const struct of_device_id apple_sart_of_match[] = { .compatible = "apple,t8103-sart", .data = &sart_ops_v2, }, + { + .compatible = "apple,t8015-sart", + .data = &sart_ops_v0, + }, {} }; MODULE_DEVICE_TABLE(of, apple_sart_of_match); diff --git a/drivers/soc/aspeed/aspeed-lpc-ctrl.c b/drivers/soc/aspeed/aspeed-lpc-ctrl.c index ee58151bd69e..b7dbb12bd095 100644 --- a/drivers/soc/aspeed/aspeed-lpc-ctrl.c +++ b/drivers/soc/aspeed/aspeed-lpc-ctrl.c @@ -10,6 +10,7 @@ #include <linux/mm.h> #include <linux/module.h> #include <linux/of_address.h> +#include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <linux/poll.h> #include <linux/regmap.h> @@ -254,17 +255,8 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, lpc_ctrl); /* If memory-region is described in device tree then store */ - node = of_parse_phandle(dev->of_node, "memory-region", 0); - if (!node) { - dev_dbg(dev, "Didn't find reserved memory\n"); - } else { - rc = of_address_to_resource(node, 0, &resm); - of_node_put(node); - if (rc) { - dev_err(dev, "Couldn't address to resource for reserved memory\n"); - return -ENXIO; - } - + rc = of_reserved_mem_region_to_resource(dev->of_node, 0, &resm); + if (!rc) { lpc_ctrl->mem_size = resource_size(&resm); lpc_ctrl->mem_base = resm.start; diff --git a/drivers/soc/aspeed/aspeed-p2a-ctrl.c b/drivers/soc/aspeed/aspeed-p2a-ctrl.c index 6cc943744e12..3be2e1b1085b 100644 --- a/drivers/soc/aspeed/aspeed-p2a-ctrl.c +++ b/drivers/soc/aspeed/aspeed-p2a-ctrl.c @@ -19,7 +19,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/of.h> -#include <linux/of_address.h> +#include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/slab.h> @@ -334,7 +334,6 @@ static int aspeed_p2a_ctrl_probe(struct platform_device *pdev) struct aspeed_p2a_ctrl *misc_ctrl; struct device *dev; struct resource resm; - struct device_node *node; int rc = 0; dev = &pdev->dev; @@ -346,15 +345,8 @@ static int aspeed_p2a_ctrl_probe(struct platform_device *pdev) mutex_init(&misc_ctrl->tracking); /* optional. */ - node = of_parse_phandle(dev->of_node, "memory-region", 0); - if (node) { - rc = of_address_to_resource(node, 0, &resm); - of_node_put(node); - if (rc) { - dev_err(dev, "Couldn't address to resource for reserved memory\n"); - return -ENODEV; - } - + rc = of_reserved_mem_region_to_resource(dev->of_node, 0, &resm); + if (!rc) { misc_ctrl->mem_size = resource_size(&resm); misc_ctrl->mem_base = resm.start; } diff --git a/drivers/soc/aspeed/aspeed-socinfo.c b/drivers/soc/aspeed/aspeed-socinfo.c index 3f759121dc00..67e9ac3d08ec 100644 --- a/drivers/soc/aspeed/aspeed-socinfo.c +++ b/drivers/soc/aspeed/aspeed-socinfo.c @@ -27,6 +27,10 @@ static struct { { "AST2620", 0x05010203 }, { "AST2605", 0x05030103 }, { "AST2625", 0x05030403 }, + /* AST2700 */ + { "AST2750", 0x06000003 }, + { "AST2700", 0x06000103 }, + { "AST2720", 0x06000203 }, }; static const char *siliconid_to_name(u32 siliconid) diff --git a/drivers/soc/fsl/qbman/qman_test_stash.c b/drivers/soc/fsl/qbman/qman_test_stash.c index f4d3c2146f4f..6f7597950aa3 100644 --- a/drivers/soc/fsl/qbman/qman_test_stash.c +++ b/drivers/soc/fsl/qbman/qman_test_stash.c @@ -103,7 +103,7 @@ static int on_all_cpus(int (*fn)(void)) { int cpu; - for_each_cpu(cpu, cpu_online_mask) { + for_each_online_cpu(cpu) { struct bstrap bstrap = { .fn = fn, .started = ATOMIC_INIT(0) diff --git a/drivers/soc/fsl/qe/gpio.c b/drivers/soc/fsl/qe/gpio.c index 8df1e8fa86a5..c54154b404df 100644 --- a/drivers/soc/fsl/qe/gpio.c +++ b/drivers/soc/fsl/qe/gpio.c @@ -12,18 +12,19 @@ #include <linux/spinlock.h> #include <linux/err.h> #include <linux/io.h> -#include <linux/of.h> -#include <linux/gpio/legacy-of-mm-gpiochip.h> #include <linux/gpio/consumer.h> #include <linux/gpio/driver.h> #include <linux/slab.h> #include <linux/export.h> -#include <linux/property.h> +#include <linux/platform_device.h> #include <soc/fsl/qe/qe.h> +#define PIN_MASK(gpio) (1UL << (QE_PIO_PINS - 1 - (gpio))) + struct qe_gpio_chip { - struct of_mm_gpio_chip mm_gc; + struct gpio_chip gc; + void __iomem *regs; spinlock_t lock; /* shadowed data register to clear/set bits safely */ @@ -33,11 +34,9 @@ struct qe_gpio_chip { struct qe_pio_regs saved_regs; }; -static void qe_gpio_save_regs(struct of_mm_gpio_chip *mm_gc) +static void qe_gpio_save_regs(struct qe_gpio_chip *qe_gc) { - struct qe_gpio_chip *qe_gc = - container_of(mm_gc, struct qe_gpio_chip, mm_gc); - struct qe_pio_regs __iomem *regs = mm_gc->regs; + struct qe_pio_regs __iomem *regs = qe_gc->regs; qe_gc->cpdata = ioread32be(®s->cpdata); qe_gc->saved_regs.cpdata = qe_gc->cpdata; @@ -50,20 +49,19 @@ static void qe_gpio_save_regs(struct of_mm_gpio_chip *mm_gc) static int qe_gpio_get(struct gpio_chip *gc, unsigned int gpio) { - struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); - struct qe_pio_regs __iomem *regs = mm_gc->regs; - u32 pin_mask = 1 << (QE_PIO_PINS - 1 - gpio); + struct qe_gpio_chip *qe_gc = gpiochip_get_data(gc); + struct qe_pio_regs __iomem *regs = qe_gc->regs; + u32 pin_mask = PIN_MASK(gpio); return !!(ioread32be(®s->cpdata) & pin_mask); } static int qe_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) { - struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); struct qe_gpio_chip *qe_gc = gpiochip_get_data(gc); - struct qe_pio_regs __iomem *regs = mm_gc->regs; + struct qe_pio_regs __iomem *regs = qe_gc->regs; unsigned long flags; - u32 pin_mask = 1 << (QE_PIO_PINS - 1 - gpio); + u32 pin_mask = PIN_MASK(gpio); spin_lock_irqsave(&qe_gc->lock, flags); @@ -82,9 +80,8 @@ static int qe_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) static int qe_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { - struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); struct qe_gpio_chip *qe_gc = gpiochip_get_data(gc); - struct qe_pio_regs __iomem *regs = mm_gc->regs; + struct qe_pio_regs __iomem *regs = qe_gc->regs; unsigned long flags; int i; @@ -95,9 +92,9 @@ static int qe_gpio_set_multiple(struct gpio_chip *gc, break; if (__test_and_clear_bit(i, mask)) { if (test_bit(i, bits)) - qe_gc->cpdata |= (1U << (QE_PIO_PINS - 1 - i)); + qe_gc->cpdata |= PIN_MASK(i); else - qe_gc->cpdata &= ~(1U << (QE_PIO_PINS - 1 - i)); + qe_gc->cpdata &= ~PIN_MASK(i); } } @@ -110,13 +107,12 @@ static int qe_gpio_set_multiple(struct gpio_chip *gc, static int qe_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) { - struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); struct qe_gpio_chip *qe_gc = gpiochip_get_data(gc); unsigned long flags; spin_lock_irqsave(&qe_gc->lock, flags); - __par_io_config_pin(mm_gc->regs, gpio, QE_PIO_DIR_IN, 0, 0, 0); + __par_io_config_pin(qe_gc->regs, gpio, QE_PIO_DIR_IN, 0, 0, 0); spin_unlock_irqrestore(&qe_gc->lock, flags); @@ -125,7 +121,6 @@ static int qe_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) static int qe_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) { - struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); struct qe_gpio_chip *qe_gc = gpiochip_get_data(gc); unsigned long flags; @@ -133,7 +128,7 @@ static int qe_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) spin_lock_irqsave(&qe_gc->lock, flags); - __par_io_config_pin(mm_gc->regs, gpio, QE_PIO_DIR_OUT, 0, 0, 0); + __par_io_config_pin(qe_gc->regs, gpio, QE_PIO_DIR_OUT, 0, 0, 0); spin_unlock_irqrestore(&qe_gc->lock, flags); @@ -239,7 +234,7 @@ EXPORT_SYMBOL(qe_pin_free); void qe_pin_set_dedicated(struct qe_pin *qe_pin) { struct qe_gpio_chip *qe_gc = qe_pin->controller; - struct qe_pio_regs __iomem *regs = qe_gc->mm_gc.regs; + struct qe_pio_regs __iomem *regs = qe_gc->regs; struct qe_pio_regs *sregs = &qe_gc->saved_regs; int pin = qe_pin->num; u32 mask1 = 1 << (QE_PIO_PINS - (pin + 1)); @@ -268,7 +263,6 @@ void qe_pin_set_dedicated(struct qe_pin *qe_pin) iowrite32be(qe_gc->cpdata, ®s->cpdata); qe_clrsetbits_be32(®s->cpodr, mask1, sregs->cpodr & mask1); - spin_unlock_irqrestore(&qe_gc->lock, flags); } EXPORT_SYMBOL(qe_pin_set_dedicated); @@ -283,7 +277,7 @@ EXPORT_SYMBOL(qe_pin_set_dedicated); void qe_pin_set_gpio(struct qe_pin *qe_pin) { struct qe_gpio_chip *qe_gc = qe_pin->controller; - struct qe_pio_regs __iomem *regs = qe_gc->mm_gc.regs; + struct qe_pio_regs __iomem *regs = qe_gc->regs; unsigned long flags; spin_lock_irqsave(&qe_gc->lock, flags); @@ -295,45 +289,62 @@ void qe_pin_set_gpio(struct qe_pin *qe_pin) } EXPORT_SYMBOL(qe_pin_set_gpio); -static int __init qe_add_gpiochips(void) +static int qe_gpio_probe(struct platform_device *ofdev) { - struct device_node *np; - - for_each_compatible_node(np, NULL, "fsl,mpc8323-qe-pario-bank") { - int ret; - struct qe_gpio_chip *qe_gc; - struct of_mm_gpio_chip *mm_gc; - struct gpio_chip *gc; - - qe_gc = kzalloc(sizeof(*qe_gc), GFP_KERNEL); - if (!qe_gc) { - ret = -ENOMEM; - goto err; - } + struct device *dev = &ofdev->dev; + struct device_node *np = dev->of_node; + struct qe_gpio_chip *qe_gc; + struct gpio_chip *gc; - spin_lock_init(&qe_gc->lock); - - mm_gc = &qe_gc->mm_gc; - gc = &mm_gc->gc; - - mm_gc->save_regs = qe_gpio_save_regs; - gc->ngpio = QE_PIO_PINS; - gc->direction_input = qe_gpio_dir_in; - gc->direction_output = qe_gpio_dir_out; - gc->get = qe_gpio_get; - gc->set = qe_gpio_set; - gc->set_multiple = qe_gpio_set_multiple; - - ret = of_mm_gpiochip_add_data(np, mm_gc, qe_gc); - if (ret) - goto err; - continue; -err: - pr_err("%pOF: registration failed with status %d\n", - np, ret); - kfree(qe_gc); - /* try others anyway */ - } - return 0; + qe_gc = devm_kzalloc(dev, sizeof(*qe_gc), GFP_KERNEL); + if (!qe_gc) + return -ENOMEM; + + spin_lock_init(&qe_gc->lock); + + gc = &qe_gc->gc; + + gc->base = -1; + gc->ngpio = QE_PIO_PINS; + gc->direction_input = qe_gpio_dir_in; + gc->direction_output = qe_gpio_dir_out; + gc->get = qe_gpio_get; + gc->set = qe_gpio_set; + gc->set_multiple = qe_gpio_set_multiple; + gc->parent = dev; + gc->owner = THIS_MODULE; + + gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", np); + if (!gc->label) + return -ENOMEM; + + qe_gc->regs = devm_of_iomap(dev, np, 0, NULL); + if (IS_ERR(qe_gc->regs)) + return PTR_ERR(qe_gc->regs); + + qe_gpio_save_regs(qe_gc); + + return devm_gpiochip_add_data(dev, gc, qe_gc); +} + +static const struct of_device_id qe_gpio_match[] = { + { + .compatible = "fsl,mpc8323-qe-pario-bank", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, qe_gpio_match); + +static struct platform_driver qe_gpio_driver = { + .probe = qe_gpio_probe, + .driver = { + .name = "qe-gpio", + .of_match_table = qe_gpio_match, + }, +}; + +static int __init qe_gpio_init(void) +{ + return platform_driver_register(&qe_gpio_driver); } -arch_initcall(qe_add_gpiochips); +arch_initcall(qe_gpio_init); diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c index 65ff45fdcac7..006fec47ea10 100644 --- a/drivers/soc/hisilicon/kunpeng_hccs.c +++ b/drivers/soc/hisilicon/kunpeng_hccs.c @@ -1464,7 +1464,7 @@ static ssize_t dec_lane_of_type_store(struct kobject *kobj, struct kobj_attribut goto out; if (!all_in_idle) { ret = -EBUSY; - dev_err(hdev->dev, "please don't decrese lanes on high load with %s, ret = %d.\n", + dev_err(hdev->dev, "please don't decrease lanes on high load with %s, ret = %d.\n", hccs_port_type_to_name(hdev, port_type), ret); goto out; } diff --git a/drivers/soc/mediatek/mtk-svs.c b/drivers/soc/mediatek/mtk-svs.c index 7c349a94b45c..f45537546553 100644 --- a/drivers/soc/mediatek/mtk-svs.c +++ b/drivers/soc/mediatek/mtk-svs.c @@ -2165,10 +2165,18 @@ static struct device *svs_add_device_link(struct svs_platform *svsp, return dev; } +static void svs_put_device(void *_dev) +{ + struct device *dev = _dev; + + put_device(dev); +} + static int svs_mt8192_platform_probe(struct svs_platform *svsp) { struct device *dev; u32 idx; + int ret; svsp->rst = devm_reset_control_get_optional(svsp->dev, "svs_rst"); if (IS_ERR(svsp->rst)) @@ -2179,6 +2187,7 @@ static int svs_mt8192_platform_probe(struct svs_platform *svsp) if (IS_ERR(dev)) return dev_err_probe(svsp->dev, PTR_ERR(dev), "failed to get lvts device\n"); + put_device(dev); for (idx = 0; idx < svsp->bank_max; idx++) { struct svs_bank *svsb = &svsp->banks[idx]; @@ -2188,6 +2197,7 @@ static int svs_mt8192_platform_probe(struct svs_platform *svsp) case SVSB_SWID_CPU_LITTLE: case SVSB_SWID_CPU_BIG: svsb->opp_dev = get_cpu_device(bdata->cpu_id); + get_device(svsb->opp_dev); break; case SVSB_SWID_CCI: svsb->opp_dev = svs_add_device_link(svsp, "cci"); @@ -2207,6 +2217,11 @@ static int svs_mt8192_platform_probe(struct svs_platform *svsp) return dev_err_probe(svsp->dev, PTR_ERR(svsb->opp_dev), "failed to get OPP device for bank %d\n", idx); + + ret = devm_add_action_or_reset(svsp->dev, svs_put_device, + svsb->opp_dev); + if (ret) + return ret; } return 0; @@ -2216,11 +2231,13 @@ static int svs_mt8183_platform_probe(struct svs_platform *svsp) { struct device *dev; u32 idx; + int ret; dev = svs_add_device_link(svsp, "thermal-sensor"); if (IS_ERR(dev)) return dev_err_probe(svsp->dev, PTR_ERR(dev), "failed to get thermal device\n"); + put_device(dev); for (idx = 0; idx < svsp->bank_max; idx++) { struct svs_bank *svsb = &svsp->banks[idx]; @@ -2230,6 +2247,7 @@ static int svs_mt8183_platform_probe(struct svs_platform *svsp) case SVSB_SWID_CPU_LITTLE: case SVSB_SWID_CPU_BIG: svsb->opp_dev = get_cpu_device(bdata->cpu_id); + get_device(svsb->opp_dev); break; case SVSB_SWID_CCI: svsb->opp_dev = svs_add_device_link(svsp, "cci"); @@ -2246,6 +2264,11 @@ static int svs_mt8183_platform_probe(struct svs_platform *svsp) return dev_err_probe(svsp->dev, PTR_ERR(svsb->opp_dev), "failed to get OPP device for bank %d\n", idx); + + ret = devm_add_action_or_reset(svsp->dev, svs_put_device, + svsb->opp_dev); + if (ret) + return ret; } return 0; diff --git a/drivers/soc/qcom/icc-bwmon.c b/drivers/soc/qcom/icc-bwmon.c index 3dfa448bf8cf..597f9025e422 100644 --- a/drivers/soc/qcom/icc-bwmon.c +++ b/drivers/soc/qcom/icc-bwmon.c @@ -656,6 +656,9 @@ static irqreturn_t bwmon_intr_thread(int irq, void *dev_id) if (IS_ERR(target_opp) && PTR_ERR(target_opp) == -ERANGE) target_opp = dev_pm_opp_find_bw_floor(bwmon->dev, &bw_kbps, 0); + if (IS_ERR(target_opp)) + return IRQ_HANDLED; + bwmon->target_kbps = bw_kbps; bw_kbps--; diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 192edc3f64dc..857ead56b37d 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -4409,7 +4409,6 @@ static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, u8 index .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .fast_io = true, }; base = devm_platform_ioremap_resource(pdev, index); diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index 5710ac0c07a8..a5c80d4fcc36 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -304,7 +304,7 @@ out: } EXPORT_SYMBOL_GPL(qcom_mdt_pas_init); -static bool qcom_mdt_bins_are_split(const struct firmware *fw, const char *fw_name) +static bool qcom_mdt_bins_are_split(const struct firmware *fw) { const struct elf32_phdr *phdrs; const struct elf32_hdr *ehdr; @@ -333,9 +333,9 @@ static bool qcom_mdt_bins_are_split(const struct firmware *fw, const char *fw_na } static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, - const char *fw_name, int pas_id, void *mem_region, + const char *fw_name, void *mem_region, phys_addr_t mem_phys, size_t mem_size, - phys_addr_t *reloc_base, bool pas_init) + phys_addr_t *reloc_base) { const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; @@ -355,7 +355,7 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, if (!mdt_header_valid(fw)) return -EINVAL; - is_split = qcom_mdt_bins_are_split(fw, fw_name); + is_split = qcom_mdt_bins_are_split(fw); ehdr = (struct elf32_hdr *)fw->data; phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); @@ -460,8 +460,8 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, if (ret) return ret; - return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, - mem_size, reloc_base, true); + return __qcom_mdt_load(dev, fw, firmware, mem_region, mem_phys, + mem_size, reloc_base); } EXPORT_SYMBOL_GPL(qcom_mdt_load); @@ -470,7 +470,6 @@ EXPORT_SYMBOL_GPL(qcom_mdt_load); * @dev: device handle to associate resources with * @fw: firmware object for the mdt file * @firmware: name of the firmware, for construction of segment file names - * @pas_id: PAS identifier * @mem_region: allocated memory region to load firmware into * @mem_phys: physical address of allocated memory region * @mem_size: size of the allocated memory region @@ -479,12 +478,11 @@ EXPORT_SYMBOL_GPL(qcom_mdt_load); * Returns 0 on success, negative errno otherwise. */ int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, - const char *firmware, int pas_id, - void *mem_region, phys_addr_t mem_phys, + const char *firmware, void *mem_region, phys_addr_t mem_phys, size_t mem_size, phys_addr_t *reloc_base) { - return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, - mem_size, reloc_base, false); + return __qcom_mdt_load(dev, fw, firmware, mem_region, mem_phys, + mem_size, reloc_base); } EXPORT_SYMBOL_GPL(qcom_mdt_load_no_init); diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c index 3c3b796333a6..cd1779b6a91a 100644 --- a/drivers/soc/qcom/qcom-geni-se.c +++ b/drivers/soc/qcom/qcom-geni-se.c @@ -1,11 +1,16 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. +/* + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ /* Disable MMIO tracing to prevent excessive logging of unwanted MMIO traces */ #define __DISABLE_TRACE_MMIO__ #include <linux/acpi.h> +#include <linux/bitfield.h> #include <linux/clk.h> +#include <linux/firmware.h> #include <linux/slab.h> #include <linux/dma-mapping.h> #include <linux/io.h> @@ -110,22 +115,94 @@ struct geni_se_desc { static const char * const icc_path_names[] = {"qup-core", "qup-config", "qup-memory"}; -#define QUP_HW_VER_REG 0x4 +static const char * const protocol_name[] = { "None", "SPI", "UART", "I2C", "I3C", "SPI SLAVE" }; + +/** + * struct se_fw_hdr - Serial Engine firmware configuration header + * + * This structure defines the SE firmware header, which together with the + * firmware payload is stored in individual ELF segments. + * + * @magic: Set to 'SEFW'. + * @version: Structure version number. + * @core_version: QUPV3 hardware version. + * @serial_protocol: Encoded in GENI_FW_REVISION. + * @fw_version: Firmware version, from GENI_FW_REVISION. + * @cfg_version: Configuration version, from GENI_INIT_CFG_REVISION. + * @fw_size_in_items: Number of 32-bit words in GENI_FW_RAM. + * @fw_offset: Byte offset to GENI_FW_RAM array. + * @cfg_size_in_items: Number of GENI_FW_CFG index/value pairs. + * @cfg_idx_offset: Byte offset to GENI_FW_CFG index array. + * @cfg_val_offset: Byte offset to GENI_FW_CFG values array. + */ +struct se_fw_hdr { + __le32 magic; + __le32 version; + __le32 core_version; + __le16 serial_protocol; + __le16 fw_version; + __le16 cfg_version; + __le16 fw_size_in_items; + __le16 fw_offset; + __le16 cfg_size_in_items; + __le16 cfg_idx_offset; + __le16 cfg_val_offset; +}; + +/*Magic numbers*/ +#define SE_MAGIC_NUM 0x57464553 + +#define MAX_GENI_CFG_RAMn_CNT 455 + +#define MI_PBT_NON_PAGED_SEGMENT 0x0 +#define MI_PBT_HASH_SEGMENT 0x2 +#define MI_PBT_NOTUSED_SEGMENT 0x3 +#define MI_PBT_SHARED_SEGMENT 0x4 + +#define MI_PBT_FLAG_PAGE_MODE BIT(20) +#define MI_PBT_FLAG_SEGMENT_TYPE GENMASK(26, 24) +#define MI_PBT_FLAG_ACCESS_TYPE GENMASK(23, 21) + +#define MI_PBT_PAGE_MODE_VALUE(x) FIELD_GET(MI_PBT_FLAG_PAGE_MODE, x) + +#define MI_PBT_SEGMENT_TYPE_VALUE(x) FIELD_GET(MI_PBT_FLAG_SEGMENT_TYPE, x) + +#define MI_PBT_ACCESS_TYPE_VALUE(x) FIELD_GET(MI_PBT_FLAG_ACCESS_TYPE, x) + +#define M_COMMON_GENI_M_IRQ_EN (GENMASK(6, 1) | \ + M_IO_DATA_DEASSERT_EN | \ + M_IO_DATA_ASSERT_EN | M_RX_FIFO_RD_ERR_EN | \ + M_RX_FIFO_WR_ERR_EN | M_TX_FIFO_RD_ERR_EN | \ + M_TX_FIFO_WR_ERR_EN) + +/* Common QUPV3 registers */ +#define QUPV3_HW_VER_REG 0x4 +#define QUPV3_SE_AHB_M_CFG 0x118 +#define QUPV3_COMMON_CFG 0x120 +#define QUPV3_COMMON_CGC_CTRL 0x21c + +/* QUPV3_COMMON_CFG fields */ +#define FAST_SWITCH_TO_HIGH_DISABLE BIT(0) + +/* QUPV3_SE_AHB_M_CFG fields */ +#define AHB_M_CLK_CGC_ON BIT(0) + +/* QUPV3_COMMON_CGC_CTRL fields */ +#define COMMON_CSR_SLV_CLK_CGC_ON BIT(0) /* Common SE registers */ -#define GENI_INIT_CFG_REVISION 0x0 -#define GENI_S_INIT_CFG_REVISION 0x4 -#define GENI_OUTPUT_CTRL 0x24 -#define GENI_CGC_CTRL 0x28 -#define GENI_CLK_CTRL_RO 0x60 -#define GENI_FW_S_REVISION_RO 0x6c +#define SE_GENI_INIT_CFG_REVISION 0x0 +#define SE_GENI_S_INIT_CFG_REVISION 0x4 +#define SE_GENI_CGC_CTRL 0x28 +#define SE_GENI_CLK_CTRL_RO 0x60 +#define SE_GENI_FW_S_REVISION_RO 0x6c +#define SE_GENI_CFG_REG0 0x100 #define SE_GENI_BYTE_GRAN 0x254 #define SE_GENI_TX_PACKING_CFG0 0x260 #define SE_GENI_TX_PACKING_CFG1 0x264 #define SE_GENI_RX_PACKING_CFG0 0x284 #define SE_GENI_RX_PACKING_CFG1 0x288 -#define SE_GENI_M_GP_LENGTH 0x910 -#define SE_GENI_S_GP_LENGTH 0x914 +#define SE_GENI_S_IRQ_ENABLE 0x644 #define SE_DMA_TX_PTR_L 0xc30 #define SE_DMA_TX_PTR_H 0xc34 #define SE_DMA_TX_ATTR 0xc38 @@ -142,12 +219,20 @@ static const char * const icc_path_names[] = {"qup-core", "qup-config", #define SE_DMA_RX_IRQ_EN 0xd48 #define SE_DMA_RX_IRQ_EN_SET 0xd4c #define SE_DMA_RX_IRQ_EN_CLR 0xd50 -#define SE_DMA_RX_LEN_IN 0xd54 #define SE_DMA_RX_MAX_BURST 0xd5c #define SE_DMA_RX_FLUSH 0xd60 #define SE_GSI_EVENT_EN 0xe18 #define SE_IRQ_EN 0xe1c #define SE_DMA_GENERAL_CFG 0xe30 +#define SE_GENI_FW_REVISION 0x1000 +#define SE_GENI_S_FW_REVISION 0x1004 +#define SE_GENI_CFG_RAMN 0x1010 +#define SE_GENI_CLK_CTRL 0x2000 +#define SE_DMA_IF_EN 0x2004 +#define SE_FIFO_IF_DISABLE 0x2008 + +/* GENI_FW_REVISION_RO fields */ +#define FW_REV_VERSION_MSK GENMASK(7, 0) /* GENI_OUTPUT_CTRL fields */ #define DEFAULT_IO_OUTPUT_CTRL_MSK GENMASK(6, 0) @@ -179,13 +264,22 @@ static const char * const icc_path_names[] = {"qup-core", "qup-config", /* SE_DMA_GENERAL_CFG */ #define DMA_RX_CLK_CGC_ON BIT(0) #define DMA_TX_CLK_CGC_ON BIT(1) -#define DMA_AHB_SLV_CFG_ON BIT(2) +#define DMA_AHB_SLV_CLK_CGC_ON BIT(2) #define AHB_SEC_SLV_CLK_CGC_ON BIT(3) #define DUMMY_RX_NON_BUFFERABLE BIT(4) #define RX_DMA_ZERO_PADDING_EN BIT(5) #define RX_DMA_IRQ_DELAY_MSK GENMASK(8, 6) #define RX_DMA_IRQ_DELAY_SHFT 6 +/* GENI_CLK_CTRL fields */ +#define SER_CLK_SEL BIT(0) + +/* GENI_DMA_IF_EN fields */ +#define DMA_IF_EN BIT(0) + +#define geni_setbits32(_addr, _v) writel(readl(_addr) | (_v), _addr) +#define geni_clrbits32(_addr, _v) writel(readl(_addr) & ~(_v), _addr) + /** * geni_se_get_qup_hw_version() - Read the QUP wrapper Hardware version * @se: Pointer to the corresponding serial engine. @@ -196,7 +290,7 @@ u32 geni_se_get_qup_hw_version(struct geni_se *se) { struct geni_wrapper *wrapper = se->wrapper; - return readl_relaxed(wrapper->base + QUP_HW_VER_REG); + return readl_relaxed(wrapper->base + QUPV3_HW_VER_REG); } EXPORT_SYMBOL_GPL(geni_se_get_qup_hw_version); @@ -220,12 +314,12 @@ static void geni_se_io_init(void __iomem *base) { u32 val; - val = readl_relaxed(base + GENI_CGC_CTRL); + val = readl_relaxed(base + SE_GENI_CGC_CTRL); val |= DEFAULT_CGC_EN; - writel_relaxed(val, base + GENI_CGC_CTRL); + writel_relaxed(val, base + SE_GENI_CGC_CTRL); val = readl_relaxed(base + SE_DMA_GENERAL_CFG); - val |= AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON; + val |= AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CLK_CGC_ON; val |= DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON; writel_relaxed(val, base + SE_DMA_GENERAL_CFG); @@ -658,9 +752,12 @@ int geni_se_clk_freq_match(struct geni_se *se, unsigned long req_freq, } EXPORT_SYMBOL_GPL(geni_se_clk_freq_match); -#define GENI_SE_DMA_DONE_EN BIT(0) -#define GENI_SE_DMA_EOT_EN BIT(1) -#define GENI_SE_DMA_AHB_ERR_EN BIT(2) +#define GENI_SE_DMA_DONE_EN BIT(0) +#define GENI_SE_DMA_EOT_EN BIT(1) +#define GENI_SE_DMA_AHB_ERR_EN BIT(2) +#define GENI_SE_DMA_RESET_DONE_EN BIT(3) +#define GENI_SE_DMA_FLUSH_DONE BIT(4) + #define GENI_SE_DMA_EOT_BUF BIT(0) /** @@ -891,6 +988,377 @@ int geni_icc_disable(struct geni_se *se) } EXPORT_SYMBOL_GPL(geni_icc_disable); +/** + * geni_find_protocol_fw() - Locate and validate SE firmware for a protocol. + * @dev: Pointer to the device structure. + * @fw: Pointer to the firmware image. + * @protocol: Expected serial engine protocol type. + * + * Identifies the appropriate firmware image or configuration required for a + * specific communication protocol instance running on a Qualcomm GENI + * controller. + * + * Return: pointer to a valid 'struct se_fw_hdr' if found, or NULL otherwise. + */ +static struct se_fw_hdr *geni_find_protocol_fw(struct device *dev, const struct firmware *fw, + enum geni_se_protocol_type protocol) +{ + const struct elf32_hdr *ehdr; + const struct elf32_phdr *phdrs; + const struct elf32_phdr *phdr; + struct se_fw_hdr *sefw; + u32 fw_end, cfg_idx_end, cfg_val_end; + u16 fw_size; + int i; + + if (!fw || fw->size < sizeof(struct elf32_hdr)) + return NULL; + + ehdr = (const struct elf32_hdr *)fw->data; + phdrs = (const struct elf32_phdr *)(fw->data + ehdr->e_phoff); + + /* + * The firmware is expected to have at least two program headers (segments). + * One for metadata and the other for the actual protocol-specific firmware. + */ + if (ehdr->e_phnum < 2) { + dev_err(dev, "Invalid firmware: less than 2 program headers\n"); + return NULL; + } + + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = &phdrs[i]; + + if (fw->size < phdr->p_offset + phdr->p_filesz) { + dev_err(dev, "Firmware size (%zu) < expected offset (%u) + size (%u)\n", + fw->size, phdr->p_offset, phdr->p_filesz); + return NULL; + } + + if (phdr->p_type != PT_LOAD || !phdr->p_memsz) + continue; + + if (MI_PBT_PAGE_MODE_VALUE(phdr->p_flags) != MI_PBT_NON_PAGED_SEGMENT || + MI_PBT_SEGMENT_TYPE_VALUE(phdr->p_flags) == MI_PBT_HASH_SEGMENT || + MI_PBT_ACCESS_TYPE_VALUE(phdr->p_flags) == MI_PBT_NOTUSED_SEGMENT || + MI_PBT_ACCESS_TYPE_VALUE(phdr->p_flags) == MI_PBT_SHARED_SEGMENT) + continue; + + if (phdr->p_filesz < sizeof(struct se_fw_hdr)) + continue; + + sefw = (struct se_fw_hdr *)(fw->data + phdr->p_offset); + fw_size = le16_to_cpu(sefw->fw_size_in_items); + fw_end = le16_to_cpu(sefw->fw_offset) + fw_size * sizeof(u32); + cfg_idx_end = le16_to_cpu(sefw->cfg_idx_offset) + + le16_to_cpu(sefw->cfg_size_in_items) * sizeof(u8); + cfg_val_end = le16_to_cpu(sefw->cfg_val_offset) + + le16_to_cpu(sefw->cfg_size_in_items) * sizeof(u32); + + if (le32_to_cpu(sefw->magic) != SE_MAGIC_NUM || le32_to_cpu(sefw->version) != 1) + continue; + + if (le32_to_cpu(sefw->serial_protocol) != protocol) + continue; + + if (fw_size % 2 != 0) { + fw_size++; + sefw->fw_size_in_items = cpu_to_le16(fw_size); + } + + if (fw_size >= MAX_GENI_CFG_RAMn_CNT) { + dev_err(dev, + "Firmware size (%u) exceeds max allowed RAMn count (%u)\n", + fw_size, MAX_GENI_CFG_RAMn_CNT); + continue; + } + + if (fw_end > phdr->p_filesz || cfg_idx_end > phdr->p_filesz || + cfg_val_end > phdr->p_filesz) { + dev_err(dev, "Truncated or corrupt SE FW segment found at index %d\n", i); + continue; + } + + return sefw; + } + + dev_err(dev, "Failed to get %s protocol firmware\n", protocol_name[protocol]); + return NULL; +} + +/** + * geni_configure_xfer_mode() - Set the transfer mode. + * @se: Pointer to the concerned serial engine. + * @mode: SE data transfer mode. + * + * Set the transfer mode to either FIFO or DMA according to the mode specified + * by the protocol driver. + * + * Return: 0 if successful, otherwise return an error value. + */ +static int geni_configure_xfer_mode(struct geni_se *se, enum geni_se_xfer_mode mode) +{ + /* Configure SE FIFO, DMA or GSI mode. */ + switch (mode) { + case GENI_GPI_DMA: + geni_setbits32(se->base + SE_GENI_DMA_MODE_EN, GENI_DMA_MODE_EN); + writel(0x0, se->base + SE_IRQ_EN); + writel(DMA_RX_EVENT_EN | DMA_TX_EVENT_EN | GENI_M_EVENT_EN | GENI_S_EVENT_EN, + se->base + SE_GSI_EVENT_EN); + break; + + case GENI_SE_FIFO: + geni_clrbits32(se->base + SE_GENI_DMA_MODE_EN, GENI_DMA_MODE_EN); + writel(DMA_RX_IRQ_EN | DMA_TX_IRQ_EN | GENI_M_IRQ_EN | GENI_S_IRQ_EN, + se->base + SE_IRQ_EN); + writel(0x0, se->base + SE_GSI_EVENT_EN); + break; + + case GENI_SE_DMA: + geni_setbits32(se->base + SE_GENI_DMA_MODE_EN, GENI_DMA_MODE_EN); + writel(DMA_RX_IRQ_EN | DMA_TX_IRQ_EN | GENI_M_IRQ_EN | GENI_S_IRQ_EN, + se->base + SE_IRQ_EN); + writel(0x0, se->base + SE_GSI_EVENT_EN); + break; + + default: + dev_err(se->dev, "Invalid geni-se transfer mode: %d\n", mode); + return -EINVAL; + } + return 0; +} + +/** + * geni_enable_interrupts() - Enable interrupts. + * @se: Pointer to the concerned serial engine. + * + * Enable the required interrupts during the firmware load process. + */ +static void geni_enable_interrupts(struct geni_se *se) +{ + u32 val; + + /* Enable required interrupts. */ + writel(M_COMMON_GENI_M_IRQ_EN, se->base + SE_GENI_M_IRQ_EN); + + val = S_CMD_OVERRUN_EN | S_ILLEGAL_CMD_EN | S_CMD_CANCEL_EN | S_CMD_ABORT_EN | + S_GP_IRQ_0_EN | S_GP_IRQ_1_EN | S_GP_IRQ_2_EN | S_GP_IRQ_3_EN | + S_RX_FIFO_WR_ERR_EN | S_RX_FIFO_RD_ERR_EN; + writel(val, se->base + SE_GENI_S_IRQ_ENABLE); + + /* DMA mode configuration. */ + val = GENI_SE_DMA_RESET_DONE_EN | GENI_SE_DMA_AHB_ERR_EN | GENI_SE_DMA_DONE_EN; + writel(val, se->base + SE_DMA_TX_IRQ_EN_SET); + val = GENI_SE_DMA_FLUSH_DONE | GENI_SE_DMA_RESET_DONE_EN | GENI_SE_DMA_AHB_ERR_EN | + GENI_SE_DMA_DONE_EN; + writel(val, se->base + SE_DMA_RX_IRQ_EN_SET); +} + +/** + * geni_write_fw_revision() - Write the firmware revision. + * @se: Pointer to the concerned serial engine. + * @serial_protocol: serial protocol type. + * @fw_version: QUP firmware version. + * + * Write the firmware revision and protocol into the respective register. + */ +static void geni_write_fw_revision(struct geni_se *se, u16 serial_protocol, u16 fw_version) +{ + u32 reg; + + reg = FIELD_PREP(FW_REV_PROTOCOL_MSK, serial_protocol); + reg |= FIELD_PREP(FW_REV_VERSION_MSK, fw_version); + + writel(reg, se->base + SE_GENI_FW_REVISION); + writel(reg, se->base + SE_GENI_S_FW_REVISION); +} + +/** + * geni_load_se_fw() - Load Serial Engine specific firmware. + * @se: Pointer to the concerned serial engine. + * @fw: Pointer to the firmware structure. + * @mode: SE data transfer mode. + * @protocol: Protocol type to be used with the SE (e.g., UART, SPI, I2C). + * + * Load the protocol firmware into the IRAM of the Serial Engine. + * + * Return: 0 if successful, otherwise return an error value. + */ +static int geni_load_se_fw(struct geni_se *se, const struct firmware *fw, + enum geni_se_xfer_mode mode, enum geni_se_protocol_type protocol) +{ + const u32 *fw_data, *cfg_val_arr; + const u8 *cfg_idx_arr; + u32 i, reg_value; + int ret; + struct se_fw_hdr *hdr; + + hdr = geni_find_protocol_fw(se->dev, fw, protocol); + if (!hdr) + return -EINVAL; + + fw_data = (const u32 *)((u8 *)hdr + le16_to_cpu(hdr->fw_offset)); + cfg_idx_arr = (const u8 *)hdr + le16_to_cpu(hdr->cfg_idx_offset); + cfg_val_arr = (const u32 *)((u8 *)hdr + le16_to_cpu(hdr->cfg_val_offset)); + + ret = geni_icc_set_bw(se); + if (ret) + return ret; + + ret = geni_icc_enable(se); + if (ret) + return ret; + + ret = geni_se_resources_on(se); + if (ret) + goto out_icc_disable; + + /* + * Disable high-priority interrupts until all currently executing + * low-priority interrupts have been fully handled. + */ + geni_setbits32(se->wrapper->base + QUPV3_COMMON_CFG, FAST_SWITCH_TO_HIGH_DISABLE); + + /* Set AHB_M_CLK_CGC_ON to indicate hardware controls se-wrapper cgc clock. */ + geni_setbits32(se->wrapper->base + QUPV3_SE_AHB_M_CFG, AHB_M_CLK_CGC_ON); + + /* Let hardware to control common cgc. */ + geni_setbits32(se->wrapper->base + QUPV3_COMMON_CGC_CTRL, COMMON_CSR_SLV_CLK_CGC_ON); + + /* + * Setting individual bits in GENI_OUTPUT_CTRL activates corresponding output lines, + * allowing the hardware to drive data as configured. + */ + writel(0x0, se->base + GENI_OUTPUT_CTRL); + + /* Set SCLK and HCLK to program RAM */ + geni_setbits32(se->base + SE_GENI_CGC_CTRL, PROG_RAM_SCLK_OFF | PROG_RAM_HCLK_OFF); + writel(0x0, se->base + SE_GENI_CLK_CTRL); + geni_clrbits32(se->base + SE_GENI_CGC_CTRL, PROG_RAM_SCLK_OFF | PROG_RAM_HCLK_OFF); + + /* Enable required clocks for DMA CSR, TX and RX. */ + reg_value = AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CLK_CGC_ON | + DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON; + geni_setbits32(se->base + SE_DMA_GENERAL_CFG, reg_value); + + /* Let hardware control CGC by default. */ + writel(DEFAULT_CGC_EN, se->base + SE_GENI_CGC_CTRL); + + /* Set version of the configuration register part of firmware. */ + writel(le16_to_cpu(hdr->cfg_version), se->base + SE_GENI_INIT_CFG_REVISION); + writel(le16_to_cpu(hdr->cfg_version), se->base + SE_GENI_S_INIT_CFG_REVISION); + + /* Configure GENI primitive table. */ + for (i = 0; i < le16_to_cpu(hdr->cfg_size_in_items); i++) + writel(cfg_val_arr[i], + se->base + SE_GENI_CFG_REG0 + (cfg_idx_arr[i] * sizeof(u32))); + + /* Configure condition for assertion of RX_RFR_WATERMARK condition. */ + reg_value = geni_se_get_rx_fifo_depth(se); + writel(reg_value - 2, se->base + SE_GENI_RX_RFR_WATERMARK_REG); + + /* Let hardware control CGC */ + geni_setbits32(se->base + GENI_OUTPUT_CTRL, DEFAULT_IO_OUTPUT_CTRL_MSK); + + ret = geni_configure_xfer_mode(se, mode); + if (ret) + goto out_resources_off; + + geni_enable_interrupts(se); + + geni_write_fw_revision(se, le16_to_cpu(hdr->serial_protocol), le16_to_cpu(hdr->fw_version)); + + /* Program RAM address space. */ + memcpy_toio(se->base + SE_GENI_CFG_RAMN, fw_data, + le16_to_cpu(hdr->fw_size_in_items) * sizeof(u32)); + + /* Put default values on GENI's output pads. */ + writel_relaxed(0x1, se->base + GENI_FORCE_DEFAULT_REG); + + /* Toggle SCLK/HCLK from high to low to finalize RAM programming and apply config. */ + geni_setbits32(se->base + SE_GENI_CGC_CTRL, PROG_RAM_SCLK_OFF | PROG_RAM_HCLK_OFF); + geni_setbits32(se->base + SE_GENI_CLK_CTRL, SER_CLK_SEL); + geni_clrbits32(se->base + SE_GENI_CGC_CTRL, PROG_RAM_SCLK_OFF | PROG_RAM_HCLK_OFF); + + /* Serial engine DMA interface is enabled. */ + geni_setbits32(se->base + SE_DMA_IF_EN, DMA_IF_EN); + + /* Enable or disable FIFO interface of the serial engine. */ + if (mode == GENI_SE_FIFO) + geni_clrbits32(se->base + SE_FIFO_IF_DISABLE, FIFO_IF_DISABLE); + else + geni_setbits32(se->base + SE_FIFO_IF_DISABLE, FIFO_IF_DISABLE); + +out_resources_off: + geni_se_resources_off(se); + +out_icc_disable: + geni_icc_disable(se); + return ret; +} + +/** + * geni_load_se_firmware() - Load firmware for SE based on protocol + * @se: Pointer to the concerned serial engine. + * @protocol: Protocol type to be used with the SE (e.g., UART, SPI, I2C). + * + * Retrieves the firmware name from device properties and sets the transfer mode + * (FIFO or GSI DMA) based on device tree configuration. Enforces FIFO mode for + * UART protocol due to lack of GSI DMA support. Requests the firmware and loads + * it into the SE. + * + * Return: 0 on success, negative error code on failure. + */ +int geni_load_se_firmware(struct geni_se *se, enum geni_se_protocol_type protocol) +{ + const char *fw_name; + const struct firmware *fw; + enum geni_se_xfer_mode mode = GENI_SE_FIFO; + int ret; + + if (protocol >= ARRAY_SIZE(protocol_name)) { + dev_err(se->dev, "Invalid geni-se protocol: %d", protocol); + return -EINVAL; + } + + ret = device_property_read_string(se->wrapper->dev, "firmware-name", &fw_name); + if (ret) { + dev_err(se->dev, "Failed to read firmware-name property: %d\n", ret); + return -EINVAL; + } + + if (of_property_read_bool(se->dev->of_node, "qcom,enable-gsi-dma")) + mode = GENI_GPI_DMA; + + /* GSI mode is not supported by the UART driver; therefore, setting FIFO mode */ + if (protocol == GENI_SE_UART) + mode = GENI_SE_FIFO; + + ret = request_firmware(&fw, fw_name, se->dev); + if (ret) { + if (ret == -ENOENT) + return -EPROBE_DEFER; + + dev_err(se->dev, "Failed to request firmware '%s' for protocol %d: ret: %d\n", + fw_name, protocol, ret); + return ret; + } + + ret = geni_load_se_fw(se, fw, mode, protocol); + release_firmware(fw); + + if (ret) { + dev_err(se->dev, "Failed to load SE firmware for protocol %d: ret: %d\n", + protocol, ret); + return ret; + } + + dev_dbg(se->dev, "Firmware load for %s protocol is successful for xfer mode: %d\n", + protocol_name[protocol], mode); + return 0; +} +EXPORT_SYMBOL_GPL(geni_load_se_firmware); + static int geni_se_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; diff --git a/drivers/soc/qcom/qcom_pd_mapper.c b/drivers/soc/qcom/qcom_pd_mapper.c index 3abea241b1c4..6384f271953d 100644 --- a/drivers/soc/qcom/qcom_pd_mapper.c +++ b/drivers/soc/qcom/qcom_pd_mapper.c @@ -584,6 +584,7 @@ static const struct of_device_id qcom_pdm_domains[] __maybe_unused = { { .compatible = "qcom,sm8450", .data = sm8350_domains, }, { .compatible = "qcom,sm8550", .data = sm8550_domains, }, { .compatible = "qcom,sm8650", .data = sm8550_domains, }, + { .compatible = "qcom,sm8750", .data = sm8550_domains, }, { .compatible = "qcom,x1e80100", .data = x1e80100_domains, }, { .compatible = "qcom,x1p42100", .data = x1e80100_domains, }, {}, diff --git a/drivers/soc/qcom/ramp_controller.c b/drivers/soc/qcom/ramp_controller.c index 349bdfbc61ef..15782bed2925 100644 --- a/drivers/soc/qcom/ramp_controller.c +++ b/drivers/soc/qcom/ramp_controller.c @@ -229,7 +229,6 @@ static const struct regmap_config qrc_regmap_config = { .reg_stride = 4, .val_bits = 32, .max_register = 0x68, - .fast_io = true, }; static const struct reg_sequence msm8976_cfg_dfs_sid[] = { diff --git a/drivers/soc/qcom/rpm_master_stats.c b/drivers/soc/qcom/rpm_master_stats.c index 49e4f9457279..c7788337e164 100644 --- a/drivers/soc/qcom/rpm_master_stats.c +++ b/drivers/soc/qcom/rpm_master_stats.c @@ -78,7 +78,7 @@ static int master_stats_probe(struct platform_device *pdev) if (count < 0) return count; - data = devm_kzalloc(dev, count * sizeof(*data), GFP_KERNEL); + data = devm_kcalloc(dev, count, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index fdab2b1067db..c6f7d5c9c493 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -453,13 +453,10 @@ static irqreturn_t tcs_tx_done(int irq, void *p) trace_rpmh_tx_done(drv, i, req); - /* - * If wake tcs was re-purposed for sending active - * votes, clear AMC trigger & enable modes and + /* Clear AMC trigger & enable modes and * disable interrupt for this TCS */ - if (!drv->tcs[ACTIVE_TCS].num_tcs) - __tcs_set_trigger(drv, i, false); + __tcs_set_trigger(drv, i, false); skip: /* Reclaim the TCS */ write_tcs_reg(drv, drv->regs[RSC_DRV_CMD_ENABLE], i, 0); diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index cf425930539e..c4c45f15dca4 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -898,7 +898,7 @@ static u32 qcom_smem_get_item_count(struct qcom_smem *smem) if (IS_ERR_OR_NULL(ptable)) return SMEM_ITEM_COUNT; - info = (struct smem_info *)&ptable->entry[ptable->num_entries]; + info = (struct smem_info *)&ptable->entry[le32_to_cpu(ptable->num_entries)]; if (memcmp(info->magic, SMEM_INFO_MAGIC, sizeof(info->magic))) return SMEM_ITEM_COUNT; diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 719b7f4f376f..340a1ff7e92b 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -39,6 +39,10 @@ config ARCH_RCAR_GEN4 bool select ARCH_RCAR_GEN3 +config ARCH_RCAR_GEN5 + bool + select ARCH_RCAR_GEN4 + config ARCH_RMOBILE bool select PM @@ -348,6 +352,14 @@ config ARCH_R8A779H0 help This enables support for the Renesas R-Car V4M SoC. +config ARCH_R8A78000 + bool "ARM64 Platform support for R8A78000 (R-Car X5H)" + default y if ARCH_RENESAS + default ARCH_RENESAS + select ARCH_RCAR_GEN5 + help + This enables support for the Renesas R-Car X5H SoC. + config ARCH_R9A07G043 bool "ARM64 Platform support for R9A07G043U (RZ/G2UL)" default y if ARCH_RENESAS @@ -449,6 +461,7 @@ config RST_RCAR config SYSC_RZ bool "System controller for RZ SoCs" if COMPILE_TEST + select MFD_SYSCON config SYSC_R9A08G045 bool "Renesas System controller support for R9A08G045 (RZ/G3S)" if COMPILE_TEST diff --git a/drivers/soc/renesas/r9a08g045-sysc.c b/drivers/soc/renesas/r9a08g045-sysc.c index f4db1431e036..0504d4e68761 100644 --- a/drivers/soc/renesas/r9a08g045-sysc.c +++ b/drivers/soc/renesas/r9a08g045-sysc.c @@ -20,4 +20,5 @@ static const struct rz_sysc_soc_id_init_data rzg3s_sysc_soc_id_init_data __initc const struct rz_sysc_init_data rzg3s_sysc_init_data __initconst = { .soc_id_init_data = &rzg3s_sysc_soc_id_init_data, + .max_register = 0xe20, }; diff --git a/drivers/soc/renesas/r9a09g047-sys.c b/drivers/soc/renesas/r9a09g047-sys.c index cd2eb7782cfe..2e8426c03050 100644 --- a/drivers/soc/renesas/r9a09g047-sys.c +++ b/drivers/soc/renesas/r9a09g047-sys.c @@ -64,4 +64,5 @@ static const struct rz_sysc_soc_id_init_data rzg3e_sys_soc_id_init_data __initco const struct rz_sysc_init_data rzg3e_sys_init_data = { .soc_id_init_data = &rzg3e_sys_soc_id_init_data, + .max_register = 0x170c, }; diff --git a/drivers/soc/renesas/r9a09g057-sys.c b/drivers/soc/renesas/r9a09g057-sys.c index 4c21cc29edbc..e3390e7c7fe5 100644 --- a/drivers/soc/renesas/r9a09g057-sys.c +++ b/drivers/soc/renesas/r9a09g057-sys.c @@ -64,4 +64,5 @@ static const struct rz_sysc_soc_id_init_data rzv2h_sys_soc_id_init_data __initco const struct rz_sysc_init_data rzv2h_sys_init_data = { .soc_id_init_data = &rzv2h_sys_soc_id_init_data, + .max_register = 0x170c, }; diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index df2b38417b80..1eb52356b996 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -36,6 +36,10 @@ static const struct renesas_family fam_rcar_gen4 __initconst __maybe_unused = { .name = "R-Car Gen4", }; +static const struct renesas_family fam_rcar_gen5 __initconst __maybe_unused = { + .name = "R-Car Gen5", +}; + static const struct renesas_family fam_rmobile __initconst __maybe_unused = { .name = "R-Mobile", .reg = 0xe600101c, /* CCCR (Common Chip Code Register) */ @@ -266,6 +270,11 @@ static const struct renesas_soc soc_rcar_v4m __initconst __maybe_unused = { .id = 0x5d, }; +static const struct renesas_soc soc_rcar_x5h __initconst __maybe_unused = { + .family = &fam_rcar_gen5, + .id = 0x60, +}; + static const struct renesas_soc soc_shmobile_ag5 __initconst __maybe_unused = { .family = &fam_shmobile, .id = 0x37, @@ -378,6 +387,9 @@ static const struct of_device_id renesas_socs[] __initconst __maybe_unused = { #ifdef CONFIG_ARCH_R8A779H0 { .compatible = "renesas,r8a779h0", .data = &soc_rcar_v4m }, #endif +#ifdef CONFIG_ARCH_R8A78000 + { .compatible = "renesas,r8a78000", .data = &soc_rcar_x5h }, +#endif #ifdef CONFIG_ARCH_R9A07G043 #ifdef CONFIG_RISCV { .compatible = "renesas,r9a07g043", .data = &soc_rz_five }, diff --git a/drivers/soc/renesas/rz-sysc.c b/drivers/soc/renesas/rz-sysc.c index ffa65fb4dade..9f79e299e6f4 100644 --- a/drivers/soc/renesas/rz-sysc.c +++ b/drivers/soc/renesas/rz-sysc.c @@ -5,9 +5,13 @@ * Copyright (C) 2024 Renesas Electronics Corp. */ +#include <linux/cleanup.h> #include <linux/io.h> +#include <linux/mfd/syscon.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> #include <linux/sys_soc.h> #include "rz-sysc.h" @@ -100,14 +104,23 @@ MODULE_DEVICE_TABLE(of, rz_sysc_match); static int rz_sysc_probe(struct platform_device *pdev) { + const struct rz_sysc_init_data *data; const struct of_device_id *match; struct device *dev = &pdev->dev; + struct regmap *regmap; struct rz_sysc *sysc; + int ret; + + struct regmap_config *regmap_cfg __free(kfree) = kzalloc(sizeof(*regmap_cfg), GFP_KERNEL); + if (!regmap_cfg) + return -ENOMEM; match = of_match_node(rz_sysc_match, dev->of_node); if (!match) return -ENODEV; + data = match->data; + sysc = devm_kzalloc(dev, sizeof(*sysc), GFP_KERNEL); if (!sysc) return -ENOMEM; @@ -117,7 +130,22 @@ static int rz_sysc_probe(struct platform_device *pdev) return PTR_ERR(sysc->base); sysc->dev = dev; - return rz_sysc_soc_init(sysc, match); + ret = rz_sysc_soc_init(sysc, match); + if (ret) + return ret; + + regmap_cfg->name = "rz_sysc_regs"; + regmap_cfg->reg_bits = 32; + regmap_cfg->reg_stride = 4; + regmap_cfg->val_bits = 32; + regmap_cfg->fast_io = true; + regmap_cfg->max_register = data->max_register; + + regmap = devm_regmap_init_mmio(dev, sysc->base, regmap_cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return of_syscon_register_regmap(dev->of_node, regmap); } static struct platform_driver rz_sysc_driver = { diff --git a/drivers/soc/renesas/rz-sysc.h b/drivers/soc/renesas/rz-sysc.h index 56bc047a1bff..8eec355d5d56 100644 --- a/drivers/soc/renesas/rz-sysc.h +++ b/drivers/soc/renesas/rz-sysc.h @@ -34,9 +34,11 @@ struct rz_sysc_soc_id_init_data { /** * struct rz_sysc_init_data - RZ SYSC initialization data * @soc_id_init_data: RZ SYSC SoC ID initialization data + * @max_register: Maximum SYSC register offset to be used by the regmap config */ struct rz_sysc_init_data { const struct rz_sysc_soc_id_init_data *soc_id_init_data; + u32 max_register; }; extern const struct rz_sysc_init_data rzg3e_sys_init_data; diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c index a77288f49d24..22c50ca2aa79 100644 --- a/drivers/soc/samsung/exynos-pmu.c +++ b/drivers/soc/samsung/exynos-pmu.c @@ -7,7 +7,9 @@ #include <linux/array_size.h> #include <linux/arm-smccc.h> +#include <linux/bitmap.h> #include <linux/cpuhotplug.h> +#include <linux/cpu_pm.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/mfd/core.h> @@ -15,6 +17,7 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/delay.h> +#include <linux/reboot.h> #include <linux/regmap.h> #include <linux/soc/samsung/exynos-regs-pmu.h> @@ -35,6 +38,15 @@ struct exynos_pmu_context { const struct exynos_pmu_data *pmu_data; struct regmap *pmureg; struct regmap *pmuintrgen; + /* + * Serialization lock for CPU hot plug and cpuidle ACPM hint + * programming. Also protects in_cpuhp, sys_insuspend & sys_inreboot + * flags. + */ + raw_spinlock_t cpupm_lock; + unsigned long *in_cpuhp; + bool sys_insuspend; + bool sys_inreboot; }; void __iomem *pmu_base_addr; @@ -221,6 +233,15 @@ static const struct regmap_config regmap_smccfg = { .reg_read = tensor_sec_reg_read, .reg_write = tensor_sec_reg_write, .reg_update_bits = tensor_sec_update_bits, + .use_raw_spinlock = true, +}; + +static const struct regmap_config regmap_pmu_intr = { + .name = "pmu_intr_gen", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .use_raw_spinlock = true, }; static const struct exynos_pmu_data gs101_pmu_data = { @@ -330,13 +351,19 @@ struct regmap *exynos_get_pmu_regmap_by_phandle(struct device_node *np, EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap_by_phandle); /* - * CPU_INFORM register hint values which are used by - * EL3 firmware (el3mon). + * CPU_INFORM register "hint" values are required to be programmed in addition to + * the standard PSCI calls to have functional CPU hotplug and CPU idle states. + * This is required to workaround limitations in the el3mon/ACPM firmware. */ #define CPU_INFORM_CLEAR 0 #define CPU_INFORM_C2 1 -static int gs101_cpuhp_pmu_online(unsigned int cpu) +/* + * __gs101_cpu_pmu_ prefix functions are common code shared by CPU PM notifiers + * (CPUIdle) and CPU hotplug callbacks. Functions should be called with IRQs + * disabled and cpupm_lock held. + */ +static int __gs101_cpu_pmu_online(unsigned int cpu) { unsigned int cpuhint = smp_processor_id(); u32 reg, mask; @@ -358,10 +385,48 @@ static int gs101_cpuhp_pmu_online(unsigned int cpu) return 0; } -static int gs101_cpuhp_pmu_offline(unsigned int cpu) +/* Called from CPU PM notifier (CPUIdle code path) with IRQs disabled */ +static int gs101_cpu_pmu_online(void) +{ + int cpu; + + raw_spin_lock(&pmu_context->cpupm_lock); + + if (pmu_context->sys_inreboot) { + raw_spin_unlock(&pmu_context->cpupm_lock); + return NOTIFY_OK; + } + + cpu = smp_processor_id(); + __gs101_cpu_pmu_online(cpu); + raw_spin_unlock(&pmu_context->cpupm_lock); + + return NOTIFY_OK; +} + +/* Called from CPU hot plug callback with IRQs enabled */ +static int gs101_cpuhp_pmu_online(unsigned int cpu) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&pmu_context->cpupm_lock, flags); + + __gs101_cpu_pmu_online(cpu); + /* + * Mark this CPU as having finished the hotplug. + * This means this CPU can now enter C2 idle state. + */ + clear_bit(cpu, pmu_context->in_cpuhp); + raw_spin_unlock_irqrestore(&pmu_context->cpupm_lock, flags); + + return 0; +} + +/* Common function shared by both CPU hot plug and CPUIdle */ +static int __gs101_cpu_pmu_offline(unsigned int cpu) { - u32 reg, mask; unsigned int cpuhint = smp_processor_id(); + u32 reg, mask; /* set cpu inform hint */ regmap_write(pmu_context->pmureg, GS101_CPU_INFORM(cpuhint), @@ -379,6 +444,165 @@ static int gs101_cpuhp_pmu_offline(unsigned int cpu) regmap_read(pmu_context->pmuintrgen, GS101_GRP1_INTR_BID_UPEND, ®); regmap_write(pmu_context->pmuintrgen, GS101_GRP1_INTR_BID_CLEAR, reg & mask); + + return 0; +} + +/* Called from CPU PM notifier (CPUIdle code path) with IRQs disabled */ +static int gs101_cpu_pmu_offline(void) +{ + int cpu; + + raw_spin_lock(&pmu_context->cpupm_lock); + cpu = smp_processor_id(); + + if (test_bit(cpu, pmu_context->in_cpuhp)) { + raw_spin_unlock(&pmu_context->cpupm_lock); + return NOTIFY_BAD; + } + + /* Ignore CPU_PM_ENTER event in reboot or suspend sequence. */ + if (pmu_context->sys_insuspend || pmu_context->sys_inreboot) { + raw_spin_unlock(&pmu_context->cpupm_lock); + return NOTIFY_OK; + } + + __gs101_cpu_pmu_offline(cpu); + raw_spin_unlock(&pmu_context->cpupm_lock); + + return NOTIFY_OK; +} + +/* Called from CPU hot plug callback with IRQs enabled */ +static int gs101_cpuhp_pmu_offline(unsigned int cpu) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&pmu_context->cpupm_lock, flags); + /* + * Mark this CPU as entering hotplug. So as not to confuse + * ACPM the CPU entering hotplug should not enter C2 idle state. + */ + set_bit(cpu, pmu_context->in_cpuhp); + __gs101_cpu_pmu_offline(cpu); + + raw_spin_unlock_irqrestore(&pmu_context->cpupm_lock, flags); + + return 0; +} + +static int gs101_cpu_pm_notify_callback(struct notifier_block *self, + unsigned long action, void *v) +{ + switch (action) { + case CPU_PM_ENTER: + return gs101_cpu_pmu_offline(); + + case CPU_PM_EXIT: + return gs101_cpu_pmu_online(); + } + + return NOTIFY_OK; +} + +static struct notifier_block gs101_cpu_pm_notifier = { + .notifier_call = gs101_cpu_pm_notify_callback, + /* + * We want to be called first, as the ACPM hint and handshake is what + * puts the CPU into C2. + */ + .priority = INT_MAX +}; + +static int exynos_cpupm_reboot_notifier(struct notifier_block *nb, + unsigned long event, void *v) +{ + unsigned long flags; + + switch (event) { + case SYS_POWER_OFF: + case SYS_RESTART: + raw_spin_lock_irqsave(&pmu_context->cpupm_lock, flags); + pmu_context->sys_inreboot = true; + raw_spin_unlock_irqrestore(&pmu_context->cpupm_lock, flags); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block exynos_cpupm_reboot_nb = { + .priority = INT_MAX, + .notifier_call = exynos_cpupm_reboot_notifier, +}; + +static int setup_cpuhp_and_cpuidle(struct device *dev) +{ + struct device_node *intr_gen_node; + struct resource intrgen_res; + void __iomem *virt_addr; + int ret, cpu; + + intr_gen_node = of_parse_phandle(dev->of_node, + "google,pmu-intr-gen-syscon", 0); + if (!intr_gen_node) { + /* + * To maintain support for older DTs that didn't specify syscon + * phandle just issue a warning rather than fail to probe. + */ + dev_warn(dev, "pmu-intr-gen syscon unavailable\n"); + return 0; + } + + /* + * To avoid lockdep issues (CPU PM notifiers use raw spinlocks) create + * a mmio regmap for pmu-intr-gen that uses raw spinlocks instead of + * syscon provided regmap. + */ + ret = of_address_to_resource(intr_gen_node, 0, &intrgen_res); + of_node_put(intr_gen_node); + + virt_addr = devm_ioremap(dev, intrgen_res.start, + resource_size(&intrgen_res)); + if (!virt_addr) + return -ENOMEM; + + pmu_context->pmuintrgen = devm_regmap_init_mmio(dev, virt_addr, + ®map_pmu_intr); + if (IS_ERR(pmu_context->pmuintrgen)) { + dev_err(dev, "failed to initialize pmu-intr-gen regmap\n"); + return PTR_ERR(pmu_context->pmuintrgen); + } + + /* register custom mmio regmap with syscon */ + ret = of_syscon_register_regmap(intr_gen_node, + pmu_context->pmuintrgen); + if (ret) + return ret; + + pmu_context->in_cpuhp = devm_bitmap_zalloc(dev, num_possible_cpus(), + GFP_KERNEL); + if (!pmu_context->in_cpuhp) + return -ENOMEM; + + raw_spin_lock_init(&pmu_context->cpupm_lock); + pmu_context->sys_inreboot = false; + pmu_context->sys_insuspend = false; + + /* set PMU to power on */ + for_each_online_cpu(cpu) + gs101_cpuhp_pmu_online(cpu); + + /* register CPU hotplug callbacks */ + cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "soc/exynos-pmu:prepare", + gs101_cpuhp_pmu_online, NULL); + + cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/exynos-pmu:online", + NULL, gs101_cpuhp_pmu_offline); + + /* register CPU PM notifiers for cpuidle */ + cpu_pm_register_notifier(&gs101_cpu_pm_notifier); + register_reboot_notifier(&exynos_cpupm_reboot_nb); return 0; } @@ -435,23 +659,9 @@ static int exynos_pmu_probe(struct platform_device *pdev) pmu_context->dev = dev; if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_cpuhp) { - pmu_context->pmuintrgen = syscon_regmap_lookup_by_phandle(dev->of_node, - "google,pmu-intr-gen-syscon"); - if (IS_ERR(pmu_context->pmuintrgen)) { - /* - * To maintain support for older DTs that didn't specify syscon phandle - * just issue a warning rather than fail to probe. - */ - dev_warn(&pdev->dev, "pmu-intr-gen syscon unavailable\n"); - } else { - cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, - "soc/exynos-pmu:prepare", - gs101_cpuhp_pmu_online, NULL); - - cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, - "soc/exynos-pmu:online", - NULL, gs101_cpuhp_pmu_offline); - } + ret = setup_cpuhp_and_cpuidle(dev); + if (ret) + return ret; } if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_init) @@ -471,10 +681,32 @@ static int exynos_pmu_probe(struct platform_device *pdev) return 0; } +static int exynos_cpupm_suspend_noirq(struct device *dev) +{ + raw_spin_lock(&pmu_context->cpupm_lock); + pmu_context->sys_insuspend = true; + raw_spin_unlock(&pmu_context->cpupm_lock); + return 0; +} + +static int exynos_cpupm_resume_noirq(struct device *dev) +{ + raw_spin_lock(&pmu_context->cpupm_lock); + pmu_context->sys_insuspend = false; + raw_spin_unlock(&pmu_context->cpupm_lock); + return 0; +} + +static const struct dev_pm_ops cpupm_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(exynos_cpupm_suspend_noirq, + exynos_cpupm_resume_noirq) +}; + static struct platform_driver exynos_pmu_driver = { .driver = { .name = "exynos-pmu", .of_match_table = exynos_pmu_of_device_ids, + .pm = pm_sleep_ptr(&cpupm_pm_ops), }, .probe = exynos_pmu_probe, }; diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c index 2781a091a6a6..446b9fc1f175 100644 --- a/drivers/soc/sunxi/sunxi_sram.c +++ b/drivers/soc/sunxi/sunxi_sram.c @@ -12,6 +12,7 @@ #include <linux/debugfs.h> #include <linux/io.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> @@ -310,6 +311,10 @@ static const struct sunxi_sramc_variant sun50i_h616_sramc_variant = { .has_ths_offset = true, }; +static const struct sunxi_sramc_variant sun55i_a523_sramc_variant = { + .num_emac_clocks = 2, +}; + #define SUNXI_SRAM_THS_OFFSET_REG 0x0 #define SUNXI_SRAM_EMAC_CLOCK_REG 0x30 #define SUNXI_SYS_LDO_CTRL_REG 0x150 @@ -363,6 +368,7 @@ static int __init sunxi_sram_probe(struct platform_device *pdev) const struct sunxi_sramc_variant *variant; struct device *dev = &pdev->dev; struct regmap *regmap; + int ret; sram_dev = &pdev->dev; @@ -380,6 +386,10 @@ static int __init sunxi_sram_probe(struct platform_device *pdev) regmap = devm_regmap_init_mmio(dev, base, &sunxi_sram_regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); + + ret = of_syscon_register_regmap(dev->of_node, regmap); + if (ret) + return ret; } of_platform_populate(dev->of_node, NULL, NULL, dev); @@ -430,6 +440,10 @@ static const struct of_device_id sunxi_sram_dt_match[] = { .compatible = "allwinner,sun50i-h616-system-control", .data = &sun50i_h616_sramc_variant, }, + { + .compatible = "allwinner,sun55i-a523-system-control", + .data = &sun55i_a523_sramc_variant, + }, { }, }; MODULE_DEVICE_TABLE(of, sunxi_sram_dt_match); diff --git a/drivers/soc/tegra/fuse/fuse-tegra30.c b/drivers/soc/tegra/fuse/fuse-tegra30.c index e24ab5f7d2bf..524fa1b0cd3d 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra30.c +++ b/drivers/soc/tegra/fuse/fuse-tegra30.c @@ -117,6 +117,124 @@ const struct tegra_fuse_soc tegra30_fuse_soc = { #endif #ifdef CONFIG_ARCH_TEGRA_114_SOC +static const struct nvmem_cell_info tegra114_fuse_cells[] = { + { + .name = "tsensor-cpu1", + .offset = 0x084, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "tsensor-cpu2", + .offset = 0x088, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "tsensor-common", + .offset = 0x08c, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "tsensor-cpu0", + .offset = 0x098, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "xusb-pad-calibration", + .offset = 0x0f0, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "tsensor-cpu3", + .offset = 0x12c, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "tsensor-gpu", + .offset = 0x154, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "tsensor-mem0", + .offset = 0x158, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "tsensor-mem1", + .offset = 0x15c, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "tsensor-pllx", + .offset = 0x160, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, +}; + +static const struct nvmem_cell_lookup tegra114_fuse_lookups[] = { + { + .nvmem_name = "fuse", + .cell_name = "xusb-pad-calibration", + .dev_id = "7009f000.padctl", + .con_id = "calibration", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-common", + .dev_id = "700e2000.thermal-sensor", + .con_id = "common", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-cpu0", + .dev_id = "700e2000.thermal-sensor", + .con_id = "cpu0", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-cpu1", + .dev_id = "700e2000.thermal-sensor", + .con_id = "cpu1", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-cpu2", + .dev_id = "700e2000.thermal-sensor", + .con_id = "cpu2", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-cpu3", + .dev_id = "700e2000.thermal-sensor", + .con_id = "cpu3", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-mem0", + .dev_id = "700e2000.thermal-sensor", + .con_id = "mem0", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-mem1", + .dev_id = "700e2000.thermal-sensor", + .con_id = "mem1", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-gpu", + .dev_id = "700e2000.thermal-sensor", + .con_id = "gpu", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-pllx", + .dev_id = "700e2000.thermal-sensor", + .con_id = "pllx", + }, +}; + static const struct tegra_fuse_info tegra114_fuse_info = { .read = tegra30_fuse_read, .size = 0x2a0, @@ -127,6 +245,10 @@ const struct tegra_fuse_soc tegra114_fuse_soc = { .init = tegra30_fuse_init, .speedo_init = tegra114_init_speedo_data, .info = &tegra114_fuse_info, + .lookups = tegra114_fuse_lookups, + .num_lookups = ARRAY_SIZE(tegra114_fuse_lookups), + .cells = tegra114_fuse_cells, + .num_cells = ARRAY_SIZE(tegra114_fuse_cells), .soc_attr_group = &tegra_soc_attr_group, .clk_suspend_on = false, }; diff --git a/drivers/soc/ti/k3-socinfo.c b/drivers/soc/ti/k3-socinfo.c index d716be113c84..50c170a995f9 100644 --- a/drivers/soc/ti/k3-socinfo.c +++ b/drivers/soc/ti/k3-socinfo.c @@ -66,6 +66,10 @@ static const char * const j721e_rev_string_map[] = { "1.0", "1.1", "2.0", }; +static const char * const am62lx_rev_string_map[] = { + "1.0", "1.1", +}; + static int k3_chipinfo_partno_to_names(unsigned int partno, struct soc_device_attribute *soc_dev_attr) @@ -92,6 +96,12 @@ k3_chipinfo_variant_to_sr(unsigned int partno, unsigned int variant, soc_dev_attr->revision = kasprintf(GFP_KERNEL, "SR%s", j721e_rev_string_map[variant]); break; + case JTAG_ID_PARTNO_AM62LX: + if (variant >= ARRAY_SIZE(am62lx_rev_string_map)) + goto err_unknown_variant; + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "SR%s", + am62lx_rev_string_map[variant]); + break; default: variant++; soc_dev_attr->revision = kasprintf(GFP_KERNEL, "SR%x.0", diff --git a/drivers/soc/ti/pruss.c b/drivers/soc/ti/pruss.c index d7634bf5413a..038576805bfa 100644 --- a/drivers/soc/ti/pruss.c +++ b/drivers/soc/ti/pruss.c @@ -449,7 +449,7 @@ static int pruss_of_setup_memories(struct device *dev, struct pruss *pruss) pruss->mem_regions[i].pa = res.start; pruss->mem_regions[i].size = resource_size(&res); - dev_dbg(dev, "memory %8s: pa %pa size 0x%zx va %pK\n", + dev_dbg(dev, "memory %8s: pa %pa size 0x%zx va %p\n", mem_names[i], &pruss->mem_regions[i].pa, pruss->mem_regions[i].size, pruss->mem_regions[i].va); } diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index e8a39e304c7e..4d8f00c850c1 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -164,7 +164,7 @@ config SPI_ASPEED_SMC config SPI_ATMEL tristate "Atmel SPI Controller" - depends on ARCH_AT91 || COMPILE_TEST + depends on ARCH_MICROCHIP || COMPILE_TEST depends on OF help This selects a driver for the Atmel SPI Controller, present on diff --git a/drivers/spi/spi-apple.c b/drivers/spi/spi-apple.c index 6273352a2b28..2fee7057ecc9 100644 --- a/drivers/spi/spi-apple.c +++ b/drivers/spi/spi-apple.c @@ -511,6 +511,7 @@ static int apple_spi_probe(struct platform_device *pdev) } static const struct of_device_id apple_spi_of_match[] = { + { .compatible = "apple,t8103-spi", }, { .compatible = "apple,spi", }, {} }; diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 768d7482102a..a0d8d3425c6c 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -671,6 +671,12 @@ static int spi_geni_init(struct spi_geni_master *mas) goto out_pm; } spi_slv_setup(mas); + } else if (proto == GENI_SE_INVALID_PROTO) { + ret = geni_load_se_firmware(se, GENI_SE_SPI); + if (ret) { + dev_err(mas->dev, "spi master firmware load failed ret: %d\n", ret); + goto out_pm; + } } else if (proto != GENI_SE_SPI) { dev_err(mas->dev, "Invalid proto %d\n", proto); goto out_pm; diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig index 61b507c18780..98c3ad083940 100644 --- a/drivers/tee/Kconfig +++ b/drivers/tee/Kconfig @@ -3,8 +3,7 @@ menuconfig TEE tristate "Trusted Execution Environment support" depends on HAVE_ARM_SMCCC || COMPILE_TEST || CPU_SUP_AMD - select CRYPTO - select CRYPTO_SHA1 + select CRYPTO_LIB_SHA1 select DMA_SHARED_BUFFER select GENERIC_ALLOCATOR help @@ -13,8 +12,14 @@ menuconfig TEE if TEE +config TEE_DMABUF_HEAPS + bool + depends on HAS_DMA && DMABUF_HEAPS + default y + source "drivers/tee/optee/Kconfig" source "drivers/tee/amdtee/Kconfig" source "drivers/tee/tstee/Kconfig" +source "drivers/tee/qcomtee/Kconfig" endif diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile index 5488cba30bd2..3239b91dee96 100644 --- a/drivers/tee/Makefile +++ b/drivers/tee/Makefile @@ -1,8 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TEE) += tee.o tee-objs += tee_core.o +tee-objs += tee_heap.o tee-objs += tee_shm.o tee-objs += tee_shm_pool.o obj-$(CONFIG_OPTEE) += optee/ obj-$(CONFIG_AMDTEE) += amdtee/ obj-$(CONFIG_ARM_TSTEE) += tstee/ +obj-$(CONFIG_QCOMTEE) += qcomtee/ diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig index 7bb7990d0b07..50d2051f7f20 100644 --- a/drivers/tee/optee/Kconfig +++ b/drivers/tee/optee/Kconfig @@ -25,3 +25,8 @@ config OPTEE_INSECURE_LOAD_IMAGE Additional documentation on kernel security risks are at Documentation/tee/op-tee.rst. + +config OPTEE_STATIC_PROTMEM_POOL + bool + depends on HAS_IOMEM && TEE_DMABUF_HEAPS + default y diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile index a6eff388d300..ad7049c1c107 100644 --- a/drivers/tee/optee/Makefile +++ b/drivers/tee/optee/Makefile @@ -4,6 +4,7 @@ optee-objs += core.o optee-objs += call.o optee-objs += notif.o optee-objs += rpc.o +optee-objs += protmem.o optee-objs += supp.o optee-objs += device.o optee-objs += smc_abi.o diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index c75fddc83576..5b62139714ce 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -56,6 +56,13 @@ int optee_rpmb_intf_rdev(struct notifier_block *intf, unsigned long action, return 0; } +int optee_set_dma_mask(struct optee *optee, u_int pa_width) +{ + u64 mask = DMA_BIT_MASK(min(64, pa_width)); + + return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask); +} + static void optee_bus_scan(struct work_struct *work) { WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP)); @@ -72,7 +79,7 @@ static ssize_t rpmb_routing_model_show(struct device *dev, else s = "user"; - return scnprintf(buf, PAGE_SIZE, "%s\n", s); + return sysfs_emit(buf, "%s\n", s); } static DEVICE_ATTR_RO(rpmb_routing_model); diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c index a963eed70c1d..bf8390789ecf 100644 --- a/drivers/tee/optee/ffa_abi.c +++ b/drivers/tee/optee/ffa_abi.c @@ -649,6 +649,124 @@ static int optee_ffa_do_call_with_arg(struct tee_context *ctx, return optee_ffa_yielding_call(ctx, &data, rpc_arg, system_thread); } +static int do_call_lend_protmem(struct optee *optee, u64 cookie, u32 use_case) +{ + struct optee_shm_arg_entry *entry; + struct optee_msg_arg *msg_arg; + struct tee_shm *shm; + u_int offs; + int rc; + + msg_arg = optee_get_msg_arg(optee->ctx, 1, &entry, &shm, &offs); + if (IS_ERR(msg_arg)) + return PTR_ERR(msg_arg); + + msg_arg->cmd = OPTEE_MSG_CMD_ASSIGN_PROTMEM; + msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; + msg_arg->params[0].u.value.a = cookie; + msg_arg->params[0].u.value.b = use_case; + + rc = optee->ops->do_call_with_arg(optee->ctx, shm, offs, false); + if (rc) + goto out; + if (msg_arg->ret != TEEC_SUCCESS) { + rc = -EINVAL; + goto out; + } + +out: + optee_free_msg_arg(optee->ctx, entry, offs); + return rc; +} + +static int optee_ffa_lend_protmem(struct optee *optee, struct tee_shm *protmem, + u32 *mem_attrs, unsigned int ma_count, + u32 use_case) +{ + struct ffa_device *ffa_dev = optee->ffa.ffa_dev; + const struct ffa_mem_ops *mem_ops = ffa_dev->ops->mem_ops; + const struct ffa_msg_ops *msg_ops = ffa_dev->ops->msg_ops; + struct ffa_send_direct_data data; + struct ffa_mem_region_attributes *mem_attr; + struct ffa_mem_ops_args args = { + .use_txbuf = true, + .tag = use_case, + }; + struct page *page; + struct scatterlist sgl; + unsigned int n; + int rc; + + mem_attr = kcalloc(ma_count, sizeof(*mem_attr), GFP_KERNEL); + for (n = 0; n < ma_count; n++) { + mem_attr[n].receiver = mem_attrs[n] & U16_MAX; + mem_attr[n].attrs = mem_attrs[n] >> 16; + } + args.attrs = mem_attr; + args.nattrs = ma_count; + + page = phys_to_page(protmem->paddr); + sg_init_table(&sgl, 1); + sg_set_page(&sgl, page, protmem->size, 0); + + args.sg = &sgl; + rc = mem_ops->memory_lend(&args); + kfree(mem_attr); + if (rc) + return rc; + + rc = do_call_lend_protmem(optee, args.g_handle, use_case); + if (rc) + goto err_reclaim; + + rc = optee_shm_add_ffa_handle(optee, protmem, args.g_handle); + if (rc) + goto err_unreg; + + protmem->sec_world_id = args.g_handle; + + return 0; + +err_unreg: + data = (struct ffa_send_direct_data){ + .data0 = OPTEE_FFA_RELEASE_PROTMEM, + .data1 = (u32)args.g_handle, + .data2 = (u32)(args.g_handle >> 32), + }; + msg_ops->sync_send_receive(ffa_dev, &data); +err_reclaim: + mem_ops->memory_reclaim(args.g_handle, 0); + return rc; +} + +static int optee_ffa_reclaim_protmem(struct optee *optee, + struct tee_shm *protmem) +{ + struct ffa_device *ffa_dev = optee->ffa.ffa_dev; + const struct ffa_msg_ops *msg_ops = ffa_dev->ops->msg_ops; + const struct ffa_mem_ops *mem_ops = ffa_dev->ops->mem_ops; + u64 global_handle = protmem->sec_world_id; + struct ffa_send_direct_data data = { + .data0 = OPTEE_FFA_RELEASE_PROTMEM, + .data1 = (u32)global_handle, + .data2 = (u32)(global_handle >> 32) + }; + int rc; + + optee_shm_rem_ffa_handle(optee, global_handle); + protmem->sec_world_id = 0; + + rc = msg_ops->sync_send_receive(ffa_dev, &data); + if (rc) + pr_err("Release SHM id 0x%llx rc %d\n", global_handle, rc); + + rc = mem_ops->memory_reclaim(global_handle, 0); + if (rc) + pr_err("mem_reclaim: 0x%llx %d", global_handle, rc); + + return rc; +} + /* * 6. Driver initialization * @@ -819,6 +937,8 @@ static const struct optee_ops optee_ffa_ops = { .do_call_with_arg = optee_ffa_do_call_with_arg, .to_msg_param = optee_ffa_to_msg_param, .from_msg_param = optee_ffa_from_msg_param, + .lend_protmem = optee_ffa_lend_protmem, + .reclaim_protmem = optee_ffa_reclaim_protmem, }; static void optee_ffa_remove(struct ffa_device *ffa_dev) @@ -891,6 +1011,25 @@ err: return rc; } +static int optee_ffa_protmem_pool_init(struct optee *optee, u32 sec_caps) +{ + enum tee_dma_heap_id id = TEE_DMA_HEAP_SECURE_VIDEO_PLAY; + struct tee_protmem_pool *pool; + int rc = 0; + + if (sec_caps & OPTEE_FFA_SEC_CAP_PROTMEM) { + pool = optee_protmem_alloc_dyn_pool(optee, id); + if (IS_ERR(pool)) + return PTR_ERR(pool); + + rc = tee_device_register_dma_heap(optee->teedev, id, pool); + if (rc) + pool->ops->destroy_pool(pool); + } + + return rc; +} + static int optee_ffa_probe(struct ffa_device *ffa_dev) { const struct ffa_notifier_ops *notif_ops; @@ -941,7 +1080,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) optee); if (IS_ERR(teedev)) { rc = PTR_ERR(teedev); - goto err_free_pool; + goto err_free_shm_pool; } optee->teedev = teedev; @@ -988,6 +1127,9 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) rc); } + if (optee_ffa_protmem_pool_init(optee, sec_caps)) + pr_info("Protected memory service not available\n"); + rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); if (rc) goto err_unregister_devices; @@ -1018,7 +1160,7 @@ err_unreg_supp_teedev: tee_device_unregister(optee->supp_teedev); err_unreg_teedev: tee_device_unregister(optee->teedev); -err_free_pool: +err_free_shm_pool: tee_shm_pool_free(pool); err_free_optee: kfree(optee); diff --git a/drivers/tee/optee/optee_ffa.h b/drivers/tee/optee/optee_ffa.h index 257735ae5b56..cc257e7956a3 100644 --- a/drivers/tee/optee/optee_ffa.h +++ b/drivers/tee/optee/optee_ffa.h @@ -81,7 +81,7 @@ * as the second MSG arg struct for * OPTEE_FFA_YIELDING_CALL_WITH_ARG. * Bit[31:8]: Reserved (MBZ) - * w5: Bitfield of secure world capabilities OPTEE_FFA_SEC_CAP_* below, + * w5: Bitfield of OP-TEE capabilities OPTEE_FFA_SEC_CAP_* * w6: The maximum secure world notification number * w7: Not used (MBZ) */ @@ -94,6 +94,8 @@ #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) +/* OP-TEE supports Protected Memory for secure data path */ +#define OPTEE_FFA_SEC_CAP_PROTMEM BIT(3) #define OPTEE_FFA_EXCHANGE_CAPABILITIES OPTEE_FFA_BLOCKING_CALL(2) @@ -108,7 +110,7 @@ * * Return register usage: * w3: Error code, 0 on success - * w4-w7: Note used (MBZ) + * w4-w7: Not used (MBZ) */ #define OPTEE_FFA_UNREGISTER_SHM OPTEE_FFA_BLOCKING_CALL(3) @@ -119,16 +121,31 @@ * Call register usage: * w3: Service ID, OPTEE_FFA_ENABLE_ASYNC_NOTIF * w4: Notification value to request bottom half processing, should be - * less than OPTEE_FFA_MAX_ASYNC_NOTIF_VALUE. + * less than OPTEE_FFA_MAX_ASYNC_NOTIF_VALUE * w5-w7: Not used (MBZ) * * Return register usage: * w3: Error code, 0 on success - * w4-w7: Note used (MBZ) + * w4-w7: Not used (MBZ) */ #define OPTEE_FFA_ENABLE_ASYNC_NOTIF OPTEE_FFA_BLOCKING_CALL(5) -#define OPTEE_FFA_MAX_ASYNC_NOTIF_VALUE 64 +#define OPTEE_FFA_MAX_ASYNC_NOTIF_VALUE 64 + +/* + * Release Protected memory + * + * Call register usage: + * w3: Service ID, OPTEE_FFA_RECLAIM_PROTMEM + * w4: Shared memory handle, lower bits + * w5: Shared memory handle, higher bits + * w6-w7: Not used (MBZ) + * + * Return register usage: + * w3: Error code, 0 on success + * w4-w7: Note used (MBZ) + */ +#define OPTEE_FFA_RELEASE_PROTMEM OPTEE_FFA_BLOCKING_CALL(8) /* * Call with struct optee_msg_arg as argument in the supplied shared memory diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h index e8840a82b983..838e1d4a22f0 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h @@ -133,13 +133,13 @@ struct optee_msg_param_rmem { }; /** - * struct optee_msg_param_fmem - ffa memory reference parameter + * struct optee_msg_param_fmem - FF-A memory reference parameter * @offs_lower: Lower bits of offset into shared memory reference * @offs_upper: Upper bits of offset into shared memory reference * @internal_offs: Internal offset into the first page of shared memory * reference * @size: Size of the buffer - * @global_id: Global identifier of Shared memory + * @global_id: Global identifier of the shared memory */ struct optee_msg_param_fmem { u32 offs_low; @@ -165,7 +165,7 @@ struct optee_msg_param_value { * @attr: attributes * @tmem: parameter by temporary memory reference * @rmem: parameter by registered memory reference - * @fmem: parameter by ffa registered memory reference + * @fmem: parameter by FF-A registered memory reference * @value: parameter by opaque value * @octets: parameter by octet string * @@ -297,6 +297,18 @@ struct optee_msg_arg { #define OPTEE_MSG_FUNCID_GET_OS_REVISION 0x0001 /* + * Values used in OPTEE_MSG_CMD_LEND_PROTMEM below + * OPTEE_MSG_PROTMEM_RESERVED Reserved + * OPTEE_MSG_PROTMEM_SECURE_VIDEO_PLAY Secure Video Playback + * OPTEE_MSG_PROTMEM_TRUSTED_UI Trused UI + * OPTEE_MSG_PROTMEM_SECURE_VIDEO_RECORD Secure Video Recording + */ +#define OPTEE_MSG_PROTMEM_RESERVED 0 +#define OPTEE_MSG_PROTMEM_SECURE_VIDEO_PLAY 1 +#define OPTEE_MSG_PROTMEM_TRUSTED_UI 2 +#define OPTEE_MSG_PROTMEM_SECURE_VIDEO_RECORD 3 + +/* * Do a secure call with struct optee_msg_arg as argument * The OPTEE_MSG_CMD_* below defines what goes in struct optee_msg_arg::cmd * @@ -337,15 +349,63 @@ struct optee_msg_arg { * OPTEE_MSG_CMD_STOP_ASYNC_NOTIF informs secure world that from now is * normal world unable to process asynchronous notifications. Typically * used when the driver is shut down. + * + * OPTEE_MSG_CMD_LEND_PROTMEM lends protected memory. The passed normal + * physical memory is protected from normal world access. The memory + * should be unmapped prior to this call since it becomes inaccessible + * during the request. + * Parameters are passed as: + * [in] param[0].attr OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + * [in] param[0].u.value.a OPTEE_MSG_PROTMEM_* defined above + * [in] param[1].attr OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + * [in] param[1].u.tmem.buf_ptr physical address + * [in] param[1].u.tmem.size size + * [in] param[1].u.tmem.shm_ref holds protected memory reference + * + * OPTEE_MSG_CMD_RECLAIM_PROTMEM reclaims a previously lent protected + * memory reference. The physical memory is accessible by the normal world + * after this function has return and can be mapped again. The information + * is passed as: + * [in] param[0].attr OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + * [in] param[0].u.value.a holds protected memory cookie + * + * OPTEE_MSG_CMD_GET_PROTMEM_CONFIG get configuration for a specific + * protected memory use case. Parameters are passed as: + * [in] param[0].attr OPTEE_MSG_ATTR_TYPE_VALUE_INOUT + * [in] param[0].value.a OPTEE_MSG_PROTMEM_* + * [in] param[1].attr OPTEE_MSG_ATTR_TYPE_{R,F}MEM_OUTPUT + * [in] param[1].u.{r,f}mem Buffer or NULL + * [in] param[1].u.{r,f}mem.size Provided size of buffer or 0 for query + * output for the protected use case: + * [out] param[0].value.a Minimal size of protected memory + * [out] param[0].value.b Required alignment of size and start of + * protected memory + * [out] param[0].value.c PA width, max 64 + * [out] param[1].{r,f}mem.size Size of output data + * [out] param[1].{r,f}mem If non-NULL, contains an array of + * uint32_t memory attributes that must be + * included when lending memory for this + * use case + * + * OPTEE_MSG_CMD_ASSIGN_PROTMEM assigns use-case to protected memory + * previously lent using the FFA_LEND framework ABI. Parameters are passed + * as: + * [in] param[0].attr OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + * [in] param[0].u.value.a holds protected memory cookie + * [in] param[0].u.value.b OPTEE_MSG_PROTMEM_* defined above */ -#define OPTEE_MSG_CMD_OPEN_SESSION 0 -#define OPTEE_MSG_CMD_INVOKE_COMMAND 1 -#define OPTEE_MSG_CMD_CLOSE_SESSION 2 -#define OPTEE_MSG_CMD_CANCEL 3 -#define OPTEE_MSG_CMD_REGISTER_SHM 4 -#define OPTEE_MSG_CMD_UNREGISTER_SHM 5 -#define OPTEE_MSG_CMD_DO_BOTTOM_HALF 6 -#define OPTEE_MSG_CMD_STOP_ASYNC_NOTIF 7 -#define OPTEE_MSG_FUNCID_CALL_WITH_ARG 0x0004 +#define OPTEE_MSG_CMD_OPEN_SESSION 0 +#define OPTEE_MSG_CMD_INVOKE_COMMAND 1 +#define OPTEE_MSG_CMD_CLOSE_SESSION 2 +#define OPTEE_MSG_CMD_CANCEL 3 +#define OPTEE_MSG_CMD_REGISTER_SHM 4 +#define OPTEE_MSG_CMD_UNREGISTER_SHM 5 +#define OPTEE_MSG_CMD_DO_BOTTOM_HALF 6 +#define OPTEE_MSG_CMD_STOP_ASYNC_NOTIF 7 +#define OPTEE_MSG_CMD_LEND_PROTMEM 8 +#define OPTEE_MSG_CMD_RECLAIM_PROTMEM 9 +#define OPTEE_MSG_CMD_GET_PROTMEM_CONFIG 10 +#define OPTEE_MSG_CMD_ASSIGN_PROTMEM 11 +#define OPTEE_MSG_FUNCID_CALL_WITH_ARG 0x0004 #endif /* _OPTEE_MSG_H */ diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 9526087f0e68..db9ea673fbca 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -176,9 +176,14 @@ struct optee; * @do_call_with_arg: enters OP-TEE in secure world * @to_msg_param: converts from struct tee_param to OPTEE_MSG parameters * @from_msg_param: converts from OPTEE_MSG parameters to struct tee_param + * @lend_protmem: lends physically contiguous memory as restricted + * memory, inaccessible by the kernel + * @reclaim_protmem: reclaims restricted memory previously lent with + * @lend_protmem() and makes it accessible by the + * kernel again * * These OPs are only supposed to be used internally in the OP-TEE driver - * as a way of abstracting the different methogs of entering OP-TEE in + * as a way of abstracting the different methods of entering OP-TEE in * secure world. */ struct optee_ops { @@ -191,6 +196,10 @@ struct optee_ops { int (*from_msg_param)(struct optee *optee, struct tee_param *params, size_t num_params, const struct optee_msg_param *msg_params); + int (*lend_protmem)(struct optee *optee, struct tee_shm *protmem, + u32 *mem_attr, unsigned int ma_count, + u32 use_case); + int (*reclaim_protmem)(struct optee *optee, struct tee_shm *protmem); }; /** @@ -274,6 +283,8 @@ struct optee_call_ctx { extern struct blocking_notifier_head optee_rpmb_intf_added; +int optee_set_dma_mask(struct optee *optee, u_int pa_width); + int optee_notif_init(struct optee *optee, u_int max_key); void optee_notif_uninit(struct optee *optee); int optee_notif_wait(struct optee *optee, u_int key, u32 timeout); @@ -285,6 +296,8 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, void optee_supp_init(struct optee_supp *supp); void optee_supp_uninit(struct optee_supp *supp); void optee_supp_release(struct optee_supp *supp); +struct tee_protmem_pool *optee_protmem_alloc_dyn_pool(struct optee *optee, + enum tee_dma_heap_id id); int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, struct tee_param *param); diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h index 879426300821..accf76a99288 100644 --- a/drivers/tee/optee/optee_smc.h +++ b/drivers/tee/optee/optee_smc.h @@ -264,7 +264,6 @@ struct optee_smc_get_shm_config_result { #define OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM BIT(0) /* Secure world can communicate via previously unregistered shared memory */ #define OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM BIT(1) - /* * Secure world supports commands "register/unregister shared memory", * secure world accepts command buffers located in any parts of non-secure RAM @@ -280,6 +279,10 @@ struct optee_smc_get_shm_config_result { #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) +/* Secure world supports protected memory */ +#define OPTEE_SMC_SEC_CAP_PROTMEM BIT(8) +/* Secure world supports dynamic protected memory */ +#define OPTEE_SMC_SEC_CAP_DYNAMIC_PROTMEM BIT(9) #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9 #define OPTEE_SMC_EXCHANGE_CAPABILITIES \ @@ -451,6 +454,38 @@ struct optee_smc_disable_shm_cache_result { /* See OPTEE_SMC_CALL_WITH_REGD_ARG above */ #define OPTEE_SMC_FUNCID_CALL_WITH_REGD_ARG 19 +/* + * Get protected memory config + * + * Returns the protected memory config. + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC_GET_PROTMEM_CONFIG + * a2-6 Not used, must be zero + * a7 Hypervisor Client ID register + * + * Have config return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1 Physical address of start of protected memory + * a2 Size of protected memory + * a3 PA width, max 64 + * a4-7 Preserved + * + * Not available register usage: + * a0 OPTEE_SMC_RETURN_ENOTAVAIL + * a1-3 Not used + * a4-7 Preserved + */ +#define OPTEE_SMC_FUNCID_GET_PROTMEM_CONFIG 20 +#define OPTEE_SMC_GET_PROTMEM_CONFIG \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_PROTMEM_CONFIG) + +struct optee_smc_get_protmem_config_result { + unsigned long status; + unsigned long start; + unsigned long size; + unsigned long pa_width; +}; /* * Resume from RPC (for example after processing a foreign interrupt) diff --git a/drivers/tee/optee/protmem.c b/drivers/tee/optee/protmem.c new file mode 100644 index 000000000000..2eba48d5ac73 --- /dev/null +++ b/drivers/tee/optee/protmem.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2025, Linaro Limited + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/errno.h> +#include <linux/genalloc.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/tee_core.h> +#include <linux/types.h> +#include "optee_private.h" + +struct optee_protmem_dyn_pool { + struct tee_protmem_pool pool; + struct gen_pool *gen_pool; + struct optee *optee; + size_t page_count; + u32 *mem_attrs; + u_int mem_attr_count; + refcount_t refcount; + u32 use_case; + struct tee_shm *protmem; + /* Protects when initializing and tearing down this struct */ + struct mutex mutex; +}; + +static struct optee_protmem_dyn_pool * +to_protmem_dyn_pool(struct tee_protmem_pool *pool) +{ + return container_of(pool, struct optee_protmem_dyn_pool, pool); +} + +static int init_dyn_protmem(struct optee_protmem_dyn_pool *rp) +{ + int rc; + + rp->protmem = tee_shm_alloc_dma_mem(rp->optee->ctx, rp->page_count); + if (IS_ERR(rp->protmem)) { + rc = PTR_ERR(rp->protmem); + goto err_null_protmem; + } + + /* + * TODO unmap the memory range since the physical memory will + * become inaccesible after the lend_protmem() call. + * + * If the platform supports a hypervisor at EL2, it will unmap the + * intermediate physical memory for us and stop cache pre-fetch of + * the memory. + */ + rc = rp->optee->ops->lend_protmem(rp->optee, rp->protmem, + rp->mem_attrs, + rp->mem_attr_count, rp->use_case); + if (rc) + goto err_put_shm; + rp->protmem->flags |= TEE_SHM_DYNAMIC; + + rp->gen_pool = gen_pool_create(PAGE_SHIFT, -1); + if (!rp->gen_pool) { + rc = -ENOMEM; + goto err_reclaim; + } + + rc = gen_pool_add(rp->gen_pool, rp->protmem->paddr, + rp->protmem->size, -1); + if (rc) + goto err_free_pool; + + refcount_set(&rp->refcount, 1); + return 0; + +err_free_pool: + gen_pool_destroy(rp->gen_pool); + rp->gen_pool = NULL; +err_reclaim: + rp->optee->ops->reclaim_protmem(rp->optee, rp->protmem); +err_put_shm: + tee_shm_put(rp->protmem); +err_null_protmem: + rp->protmem = NULL; + return rc; +} + +static int get_dyn_protmem(struct optee_protmem_dyn_pool *rp) +{ + int rc = 0; + + if (!refcount_inc_not_zero(&rp->refcount)) { + mutex_lock(&rp->mutex); + if (rp->gen_pool) { + /* + * Another thread has already initialized the pool + * before us, or the pool was just about to be torn + * down. Either way we only need to increase the + * refcount and we're done. + */ + refcount_inc(&rp->refcount); + } else { + rc = init_dyn_protmem(rp); + } + mutex_unlock(&rp->mutex); + } + + return rc; +} + +static void release_dyn_protmem(struct optee_protmem_dyn_pool *rp) +{ + gen_pool_destroy(rp->gen_pool); + rp->gen_pool = NULL; + + rp->optee->ops->reclaim_protmem(rp->optee, rp->protmem); + rp->protmem->flags &= ~TEE_SHM_DYNAMIC; + + WARN(refcount_read(&rp->protmem->refcount) != 1, "Unexpected refcount"); + tee_shm_put(rp->protmem); + rp->protmem = NULL; +} + +static void put_dyn_protmem(struct optee_protmem_dyn_pool *rp) +{ + if (refcount_dec_and_test(&rp->refcount)) { + mutex_lock(&rp->mutex); + if (rp->gen_pool) + release_dyn_protmem(rp); + mutex_unlock(&rp->mutex); + } +} + +static int protmem_pool_op_dyn_alloc(struct tee_protmem_pool *pool, + struct sg_table *sgt, size_t size, + size_t *offs) +{ + struct optee_protmem_dyn_pool *rp = to_protmem_dyn_pool(pool); + size_t sz = ALIGN(size, PAGE_SIZE); + phys_addr_t pa; + int rc; + + rc = get_dyn_protmem(rp); + if (rc) + return rc; + + pa = gen_pool_alloc(rp->gen_pool, sz); + if (!pa) { + rc = -ENOMEM; + goto err_put; + } + + rc = sg_alloc_table(sgt, 1, GFP_KERNEL); + if (rc) + goto err_free; + + sg_set_page(sgt->sgl, phys_to_page(pa), size, 0); + *offs = pa - rp->protmem->paddr; + + return 0; +err_free: + gen_pool_free(rp->gen_pool, pa, size); +err_put: + put_dyn_protmem(rp); + + return rc; +} + +static void protmem_pool_op_dyn_free(struct tee_protmem_pool *pool, + struct sg_table *sgt) +{ + struct optee_protmem_dyn_pool *rp = to_protmem_dyn_pool(pool); + struct scatterlist *sg; + int i; + + for_each_sgtable_sg(sgt, sg, i) + gen_pool_free(rp->gen_pool, sg_phys(sg), sg->length); + sg_free_table(sgt); + put_dyn_protmem(rp); +} + +static int protmem_pool_op_dyn_update_shm(struct tee_protmem_pool *pool, + struct sg_table *sgt, size_t offs, + struct tee_shm *shm, + struct tee_shm **parent_shm) +{ + struct optee_protmem_dyn_pool *rp = to_protmem_dyn_pool(pool); + + *parent_shm = rp->protmem; + + return 0; +} + +static void pool_op_dyn_destroy_pool(struct tee_protmem_pool *pool) +{ + struct optee_protmem_dyn_pool *rp = to_protmem_dyn_pool(pool); + + mutex_destroy(&rp->mutex); + kfree(rp); +} + +static struct tee_protmem_pool_ops protmem_pool_ops_dyn = { + .alloc = protmem_pool_op_dyn_alloc, + .free = protmem_pool_op_dyn_free, + .update_shm = protmem_pool_op_dyn_update_shm, + .destroy_pool = pool_op_dyn_destroy_pool, +}; + +static int get_protmem_config(struct optee *optee, u32 use_case, + size_t *min_size, u_int *pa_width, + u32 *mem_attrs, u_int *ma_count) +{ + struct tee_param params[2] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT, + .u.value.a = use_case, + }, + [1] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT, + }, + }; + struct optee_shm_arg_entry *entry; + struct tee_shm *shm_param = NULL; + struct optee_msg_arg *msg_arg; + struct tee_shm *shm; + u_int offs; + int rc; + + if (mem_attrs && *ma_count) { + params[1].u.memref.size = *ma_count * sizeof(*mem_attrs); + shm_param = tee_shm_alloc_priv_buf(optee->ctx, + params[1].u.memref.size); + if (IS_ERR(shm_param)) + return PTR_ERR(shm_param); + params[1].u.memref.shm = shm_param; + } + + msg_arg = optee_get_msg_arg(optee->ctx, ARRAY_SIZE(params), &entry, + &shm, &offs); + if (IS_ERR(msg_arg)) { + rc = PTR_ERR(msg_arg); + goto out_free_shm; + } + msg_arg->cmd = OPTEE_MSG_CMD_GET_PROTMEM_CONFIG; + + rc = optee->ops->to_msg_param(optee, msg_arg->params, + ARRAY_SIZE(params), params); + if (rc) + goto out_free_msg; + + rc = optee->ops->do_call_with_arg(optee->ctx, shm, offs, false); + if (rc) + goto out_free_msg; + if (msg_arg->ret && msg_arg->ret != TEEC_ERROR_SHORT_BUFFER) { + rc = -EINVAL; + goto out_free_msg; + } + + rc = optee->ops->from_msg_param(optee, params, ARRAY_SIZE(params), + msg_arg->params); + if (rc) + goto out_free_msg; + + if (!msg_arg->ret && mem_attrs && + *ma_count < params[1].u.memref.size / sizeof(*mem_attrs)) { + rc = -EINVAL; + goto out_free_msg; + } + + *min_size = params[0].u.value.a; + *pa_width = params[0].u.value.c; + *ma_count = params[1].u.memref.size / sizeof(*mem_attrs); + + if (msg_arg->ret == TEEC_ERROR_SHORT_BUFFER) { + rc = -ENOSPC; + goto out_free_msg; + } + + if (mem_attrs) + memcpy(mem_attrs, tee_shm_get_va(shm_param, 0), + params[1].u.memref.size); + +out_free_msg: + optee_free_msg_arg(optee->ctx, entry, offs); +out_free_shm: + if (shm_param) + tee_shm_free(shm_param); + return rc; +} + +struct tee_protmem_pool *optee_protmem_alloc_dyn_pool(struct optee *optee, + enum tee_dma_heap_id id) +{ + struct optee_protmem_dyn_pool *rp; + size_t min_size; + u_int pa_width; + int rc; + + rp = kzalloc(sizeof(*rp), GFP_KERNEL); + if (!rp) + return ERR_PTR(-ENOMEM); + rp->use_case = id; + + rc = get_protmem_config(optee, id, &min_size, &pa_width, NULL, + &rp->mem_attr_count); + if (rc) { + if (rc != -ENOSPC) + goto err; + rp->mem_attrs = kcalloc(rp->mem_attr_count, + sizeof(*rp->mem_attrs), GFP_KERNEL); + if (!rp->mem_attrs) { + rc = -ENOMEM; + goto err; + } + rc = get_protmem_config(optee, id, &min_size, &pa_width, + rp->mem_attrs, &rp->mem_attr_count); + if (rc) + goto err_kfree_eps; + } + + rc = optee_set_dma_mask(optee, pa_width); + if (rc) + goto err_kfree_eps; + + rp->pool.ops = &protmem_pool_ops_dyn; + rp->optee = optee; + rp->page_count = min_size / PAGE_SIZE; + mutex_init(&rp->mutex); + + return &rp->pool; + +err_kfree_eps: + kfree(rp->mem_attrs); +err: + kfree(rp); + return ERR_PTR(rc); +} diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c index 26f8f7bbbe56..0be663fcd52b 100644 --- a/drivers/tee/optee/smc_abi.c +++ b/drivers/tee/optee/smc_abi.c @@ -965,6 +965,70 @@ static int optee_smc_do_call_with_arg(struct tee_context *ctx, return rc; } +static int optee_smc_lend_protmem(struct optee *optee, struct tee_shm *protmem, + u32 *mem_attrs, unsigned int ma_count, + u32 use_case) +{ + struct optee_shm_arg_entry *entry; + struct optee_msg_arg *msg_arg; + struct tee_shm *shm; + u_int offs; + int rc; + + msg_arg = optee_get_msg_arg(optee->ctx, 2, &entry, &shm, &offs); + if (IS_ERR(msg_arg)) + return PTR_ERR(msg_arg); + + msg_arg->cmd = OPTEE_MSG_CMD_LEND_PROTMEM; + msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; + msg_arg->params[0].u.value.a = use_case; + msg_arg->params[1].attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; + msg_arg->params[1].u.tmem.buf_ptr = protmem->paddr; + msg_arg->params[1].u.tmem.size = protmem->size; + msg_arg->params[1].u.tmem.shm_ref = (u_long)protmem; + + rc = optee->ops->do_call_with_arg(optee->ctx, shm, offs, false); + if (rc) + goto out; + if (msg_arg->ret != TEEC_SUCCESS) { + rc = -EINVAL; + goto out; + } + protmem->sec_world_id = (u_long)protmem; + +out: + optee_free_msg_arg(optee->ctx, entry, offs); + return rc; +} + +static int optee_smc_reclaim_protmem(struct optee *optee, + struct tee_shm *protmem) +{ + struct optee_shm_arg_entry *entry; + struct optee_msg_arg *msg_arg; + struct tee_shm *shm; + u_int offs; + int rc; + + msg_arg = optee_get_msg_arg(optee->ctx, 1, &entry, &shm, &offs); + if (IS_ERR(msg_arg)) + return PTR_ERR(msg_arg); + + msg_arg->cmd = OPTEE_MSG_CMD_RECLAIM_PROTMEM; + msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; + msg_arg->params[0].u.rmem.shm_ref = (u_long)protmem; + + rc = optee->ops->do_call_with_arg(optee->ctx, shm, offs, false); + if (rc) + goto out; + if (msg_arg->ret != TEEC_SUCCESS) + rc = -EINVAL; + +out: + optee_free_msg_arg(optee->ctx, entry, offs); + return rc; +} + /* * 5. Asynchronous notification */ @@ -1216,6 +1280,8 @@ static const struct optee_ops optee_ops = { .do_call_with_arg = optee_smc_do_call_with_arg, .to_msg_param = optee_to_msg_param, .from_msg_param = optee_from_msg_param, + .lend_protmem = optee_smc_lend_protmem, + .reclaim_protmem = optee_smc_reclaim_protmem, }; static int enable_async_notif(optee_invoke_fn *invoke_fn) @@ -1583,6 +1649,74 @@ static inline int optee_load_fw(struct platform_device *pdev, } #endif +static struct tee_protmem_pool *static_protmem_pool_init(struct optee *optee) +{ +#if IS_ENABLED(CONFIG_OPTEE_STATIC_PROTMEM_POOL) + union { + struct arm_smccc_res smccc; + struct optee_smc_get_protmem_config_result result; + } res; + struct tee_protmem_pool *pool; + void *p; + int rc; + + optee->smc.invoke_fn(OPTEE_SMC_GET_PROTMEM_CONFIG, 0, 0, 0, 0, + 0, 0, 0, &res.smccc); + if (res.result.status != OPTEE_SMC_RETURN_OK) + return ERR_PTR(-EINVAL); + + rc = optee_set_dma_mask(optee, res.result.pa_width); + if (rc) + return ERR_PTR(rc); + + /* + * Map the memory as uncached to make sure the kernel can work with + * __pfn_to_page() and friends since that's needed when passing the + * protected DMA-buf to a device. The memory should otherwise not + * be touched by the kernel since it's likely to cause an external + * abort due to the protection status. + */ + p = devm_memremap(&optee->teedev->dev, res.result.start, + res.result.size, MEMREMAP_WC); + if (IS_ERR(p)) + return p; + + pool = tee_protmem_static_pool_alloc(res.result.start, res.result.size); + if (IS_ERR(pool)) + devm_memunmap(&optee->teedev->dev, p); + + return pool; +#else + return ERR_PTR(-EINVAL); +#endif +} + +static int optee_protmem_pool_init(struct optee *optee) +{ + bool protm = optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_PROTMEM; + bool dyn_protm = optee->smc.sec_caps & + OPTEE_SMC_SEC_CAP_DYNAMIC_PROTMEM; + enum tee_dma_heap_id heap_id = TEE_DMA_HEAP_SECURE_VIDEO_PLAY; + struct tee_protmem_pool *pool = ERR_PTR(-EINVAL); + int rc = -EINVAL; + + if (!protm && !dyn_protm) + return 0; + + if (protm) + pool = static_protmem_pool_init(optee); + if (dyn_protm && IS_ERR(pool)) + pool = optee_protmem_alloc_dyn_pool(optee, heap_id); + if (IS_ERR(pool)) + return PTR_ERR(pool); + + rc = tee_device_register_dma_heap(optee->teedev, heap_id, pool); + if (rc) + pool->ops->destroy_pool(pool); + + return rc; +} + static int optee_probe(struct platform_device *pdev) { optee_invoke_fn *invoke_fn; @@ -1678,7 +1812,7 @@ static int optee_probe(struct platform_device *pdev) optee = kzalloc(sizeof(*optee), GFP_KERNEL); if (!optee) { rc = -ENOMEM; - goto err_free_pool; + goto err_free_shm_pool; } optee->ops = &optee_ops; @@ -1751,6 +1885,9 @@ static int optee_probe(struct platform_device *pdev) pr_info("Asynchronous notifications enabled\n"); } + if (optee_protmem_pool_init(optee)) + pr_info("Protected memory service not available\n"); + /* * Ensure that there are no pre-existing shm objects before enabling * the shm cache so that there's no chance of receiving an invalid @@ -1802,7 +1939,7 @@ err_unreg_teedev: tee_device_unregister(optee->teedev); err_free_optee: kfree(optee); -err_free_pool: +err_free_shm_pool: tee_shm_pool_free(pool); if (memremaped_shm) memunmap(memremaped_shm); diff --git a/drivers/tee/qcomtee/Kconfig b/drivers/tee/qcomtee/Kconfig new file mode 100644 index 000000000000..927686abceb1 --- /dev/null +++ b/drivers/tee/qcomtee/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Qualcomm Trusted Execution Environment Configuration +config QCOMTEE + tristate "Qualcomm TEE Support" + depends on !CPU_BIG_ENDIAN + select QCOM_SCM + select QCOM_TZMEM_MODE_SHMBRIDGE + help + This option enables the Qualcomm Trusted Execution Environment (QTEE) + driver. It provides an API to access services offered by QTEE and + its loaded Trusted Applications (TAs). Additionally, it facilitates + the export of userspace services provided by supplicants to QTEE. diff --git a/drivers/tee/qcomtee/Makefile b/drivers/tee/qcomtee/Makefile new file mode 100644 index 000000000000..7c466c9f32af --- /dev/null +++ b/drivers/tee/qcomtee/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_QCOMTEE) += qcomtee.o +qcomtee-objs += async.o +qcomtee-objs += call.o +qcomtee-objs += core.o +qcomtee-objs += mem_obj.o +qcomtee-objs += primordial_obj.o +qcomtee-objs += shm.o +qcomtee-objs += user_obj.o diff --git a/drivers/tee/qcomtee/async.c b/drivers/tee/qcomtee/async.c new file mode 100644 index 000000000000..31bff4309e67 --- /dev/null +++ b/drivers/tee/qcomtee/async.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "qcomtee.h" + +#define QCOMTEE_ASYNC_VERSION_1_0 0x00010000U /* Maj: 0x0001, Min: 0x0000. */ +#define QCOMTEE_ASYNC_VERSION_1_1 0x00010001U /* Maj: 0x0001, Min: 0x0001. */ +#define QCOMTEE_ASYNC_VERSION_1_2 0x00010002U /* Maj: 0x0001, Min: 0x0002. */ +#define QCOMTEE_ASYNC_VERSION_CURRENT QCOMTEE_ASYNC_VERSION_1_2 + +#define QCOMTEE_ASYNC_VERSION_MAJOR(n) upper_16_bits(n) +#define QCOMTEE_ASYNC_VERSION_MINOR(n) lower_16_bits(n) + +#define QCOMTEE_ASYNC_VERSION_CURRENT_MAJOR \ + QCOMTEE_ASYNC_VERSION_MAJOR(QCOMTEE_ASYNC_VERSION_CURRENT) +#define QCOMTEE_ASYNC_VERSION_CURRENT_MINOR \ + QCOMTEE_ASYNC_VERSION_MINOR(QCOMTEE_ASYNC_VERSION_CURRENT) + +/** + * struct qcomtee_async_msg_hdr - Asynchronous message header format. + * @version: current async protocol version of the remote endpoint. + * @op: async operation. + * + * @version specifies the endpoint's (QTEE or driver) supported async protocol. + * For example, if QTEE sets @version to %QCOMTEE_ASYNC_VERSION_1_1, QTEE + * handles operations supported in %QCOMTEE_ASYNC_VERSION_1_1 or + * %QCOMTEE_ASYNC_VERSION_1_0. @op determines the message format. + */ +struct qcomtee_async_msg_hdr { + u32 version; + u32 op; +}; + +/* Size of an empty async message. */ +#define QCOMTEE_ASYNC_MSG_ZERO sizeof(struct qcomtee_async_msg_hdr) + +/** + * struct qcomtee_async_release_msg - Release asynchronous message. + * @hdr: message header as &struct qcomtee_async_msg_hdr. + * @counts: number of objects in @object_ids. + * @object_ids: array of object IDs that should be released. + * + * Available in Maj = 0x0001, Min >= 0x0000. + */ +struct qcomtee_async_release_msg { + struct qcomtee_async_msg_hdr hdr; + u32 counts; + u32 object_ids[] __counted_by(counts); +}; + +/** + * qcomtee_get_async_buffer() - Get the start of the asynchronous message. + * @oic: context used for the current invocation. + * @async_buffer: return buffer to extract from or fill in async messages. + * + * If @oic is used for direct object invocation, the whole outbound buffer + * is available for the async message. If @oic is used for a callback request, + * the tail of the outbound buffer (after the callback request message) is + * available for the async message. + * + * The start of the async buffer is aligned, see qcomtee_msg_offset_align(). + */ +static void qcomtee_get_async_buffer(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_buffer *async_buffer) +{ + struct qcomtee_msg_callback *msg; + unsigned int offset; + int i; + + if (!(oic->flags & QCOMTEE_OIC_FLAG_BUSY)) { + /* The outbound buffer is empty. Using the whole buffer. */ + offset = 0; + } else { + msg = (struct qcomtee_msg_callback *)oic->out_msg.addr; + + /* Start offset in a message for buffer arguments. */ + offset = qcomtee_msg_buffer_args(struct qcomtee_msg_callback, + qcomtee_msg_args(msg)); + + /* Add size of IB arguments. */ + qcomtee_msg_for_each_input_buffer(i, msg) + offset += qcomtee_msg_offset_align(msg->args[i].b.size); + + /* Add size of OB arguments. */ + qcomtee_msg_for_each_output_buffer(i, msg) + offset += qcomtee_msg_offset_align(msg->args[i].b.size); + } + + async_buffer->addr = oic->out_msg.addr + offset; + async_buffer->size = oic->out_msg.size - offset; +} + +/** + * async_release() - Process QTEE async release requests. + * @oic: context used for the current invocation. + * @msg: async message for object release. + * @size: size of the async buffer available. + * + * Return: Size of the outbound buffer used when processing @msg. + */ +static size_t async_release(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_async_msg_hdr *async_msg, + size_t size) +{ + struct qcomtee_async_release_msg *msg; + struct qcomtee_object *object; + int i; + + msg = (struct qcomtee_async_release_msg *)async_msg; + + for (i = 0; i < msg->counts; i++) { + object = qcomtee_idx_erase(oic, msg->object_ids[i]); + qcomtee_object_put(object); + } + + return struct_size(msg, object_ids, msg->counts); +} + +/** + * qcomtee_fetch_async_reqs() - Fetch and process asynchronous messages. + * @oic: context used for the current invocation. + * + * Calls handlers to process the requested operations in the async message. + * Currently, only supports async release requests. + */ +void qcomtee_fetch_async_reqs(struct qcomtee_object_invoke_ctx *oic) +{ + struct qcomtee_async_msg_hdr *async_msg; + struct qcomtee_buffer async_buffer; + size_t consumed, used = 0; + u16 major_ver; + + qcomtee_get_async_buffer(oic, &async_buffer); + + while (async_buffer.size - used > QCOMTEE_ASYNC_MSG_ZERO) { + async_msg = (struct qcomtee_async_msg_hdr *)(async_buffer.addr + + used); + /* + * QTEE assumes that the unused space of the async buffer is + * zeroed; so if version is zero, the buffer is unused. + */ + if (async_msg->version == 0) + goto out; + + major_ver = QCOMTEE_ASYNC_VERSION_MAJOR(async_msg->version); + /* Major version mismatch is a compatibility break. */ + if (major_ver != QCOMTEE_ASYNC_VERSION_CURRENT_MAJOR) { + pr_err("Async message version mismatch (%u != %u)\n", + major_ver, QCOMTEE_ASYNC_VERSION_CURRENT_MAJOR); + + goto out; + } + + switch (async_msg->op) { + case QCOMTEE_MSG_OBJECT_OP_RELEASE: + consumed = async_release(oic, async_msg, + async_buffer.size - used); + break; + default: + pr_err("Unsupported async message %u\n", async_msg->op); + goto out; + } + + /* Supported operation but unable to parse the message. */ + if (!consumed) { + pr_err("Unable to parse async message for op %u\n", + async_msg->op); + goto out; + } + + /* Next async message. */ + used += qcomtee_msg_offset_align(consumed); + } + +out: + /* Reset the async buffer so async requests do not loop to QTEE. */ + memzero_explicit(async_buffer.addr, async_buffer.size); +} diff --git a/drivers/tee/qcomtee/call.c b/drivers/tee/qcomtee/call.c new file mode 100644 index 000000000000..cc17a48d0ab7 --- /dev/null +++ b/drivers/tee/qcomtee/call.c @@ -0,0 +1,820 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/slab.h> +#include <linux/tee.h> +#include <linux/platform_device.h> +#include <linux/xarray.h> + +#include "qcomtee.h" + +static int find_qtee_object(struct qcomtee_object **object, unsigned long id, + struct qcomtee_context_data *ctxdata) +{ + int err = 0; + + guard(rcu)(); + /* Object release is RCU protected. */ + *object = idr_find(&ctxdata->qtee_objects_idr, id); + if (!qcomtee_object_get(*object)) + err = -EINVAL; + + return err; +} + +static void del_qtee_object(unsigned long id, + struct qcomtee_context_data *ctxdata) +{ + struct qcomtee_object *object; + + scoped_guard(mutex, &ctxdata->qtee_lock) + object = idr_remove(&ctxdata->qtee_objects_idr, id); + + qcomtee_object_put(object); +} + +/** + * qcomtee_context_add_qtee_object() - Add a QTEE object to the context. + * @param: TEE parameter representing @object. + * @object: QTEE object. + * @ctx: context to add the object. + * + * It assumes @object is %QCOMTEE_OBJECT_TYPE_TEE and the caller has already + * issued qcomtee_object_get() for @object. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +int qcomtee_context_add_qtee_object(struct tee_param *param, + struct qcomtee_object *object, + struct tee_context *ctx) +{ + int ret; + struct qcomtee_context_data *ctxdata = ctx->data; + + scoped_guard(mutex, &ctxdata->qtee_lock) + ret = idr_alloc(&ctxdata->qtee_objects_idr, object, 0, 0, + GFP_KERNEL); + if (ret < 0) + return ret; + + param->u.objref.id = ret; + /* QTEE Object: QCOMTEE_OBJREF_FLAG_TEE set. */ + param->u.objref.flags = QCOMTEE_OBJREF_FLAG_TEE; + + return 0; +} + +/* Retrieve the QTEE object added with qcomtee_context_add_qtee_object(). */ +int qcomtee_context_find_qtee_object(struct qcomtee_object **object, + struct tee_param *param, + struct tee_context *ctx) +{ + struct qcomtee_context_data *ctxdata = ctx->data; + + return find_qtee_object(object, param->u.objref.id, ctxdata); +} + +/** + * qcomtee_context_del_qtee_object() - Delete a QTEE object from the context. + * @param: TEE parameter representing @object. + * @ctx: context for deleting the object. + * + * The @param has been initialized by qcomtee_context_add_qtee_object(). + */ +void qcomtee_context_del_qtee_object(struct tee_param *param, + struct tee_context *ctx) +{ + struct qcomtee_context_data *ctxdata = ctx->data; + /* 'qtee_objects_idr' stores QTEE objects only. */ + if (param->u.objref.flags & QCOMTEE_OBJREF_FLAG_TEE) + del_qtee_object(param->u.objref.id, ctxdata); +} + +/** + * qcomtee_objref_to_arg() - Convert OBJREF parameter to QTEE argument. + * @arg: QTEE argument. + * @param: TEE parameter. + * @ctx: context in which the conversion should happen. + * + * It assumes @param is an OBJREF. + * It does not set @arg.type; the caller should initialize it to a correct + * &enum qcomtee_arg_type value. It gets the object's refcount in @arg; + * the caller should manage to put it afterward. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +int qcomtee_objref_to_arg(struct qcomtee_arg *arg, struct tee_param *param, + struct tee_context *ctx) +{ + int err = -EINVAL; + + arg->o = NULL_QCOMTEE_OBJECT; + /* param is a NULL object: */ + if (param->u.objref.id == TEE_OBJREF_NULL) + return 0; + + /* param is a callback object: */ + if (param->u.objref.flags & QCOMTEE_OBJREF_FLAG_USER) + err = qcomtee_user_param_to_object(&arg->o, param, ctx); + /* param is a QTEE object: */ + else if (param->u.objref.flags & QCOMTEE_OBJREF_FLAG_TEE) + err = qcomtee_context_find_qtee_object(&arg->o, param, ctx); + /* param is a memory object: */ + else if (param->u.objref.flags & QCOMTEE_OBJREF_FLAG_MEM) + err = qcomtee_memobj_param_to_object(&arg->o, param, ctx); + + /* + * For callback objects, call qcomtee_object_get() to keep a temporary + * copy for the driver, as these objects are released asynchronously + * and may disappear even before returning from QTEE. + * + * - For direct object invocations, the matching put is called in + * qcomtee_object_invoke() when parsing the QTEE response. + * - For callback responses, put is called in qcomtee_user_object_notify() + * after QTEE has received its copies. + */ + + if (!err && (typeof_qcomtee_object(arg->o) == QCOMTEE_OBJECT_TYPE_CB)) + qcomtee_object_get(arg->o); + + return err; +} + +/** + * qcomtee_objref_from_arg() - Convert QTEE argument to OBJREF param. + * @param: TEE parameter. + * @arg: QTEE argument. + * @ctx: context in which the conversion should happen. + * + * It assumes @arg is of %QCOMTEE_ARG_TYPE_IO or %QCOMTEE_ARG_TYPE_OO. + * It does not set @param.attr; the caller should initialize it to a + * correct type. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +int qcomtee_objref_from_arg(struct tee_param *param, struct qcomtee_arg *arg, + struct tee_context *ctx) +{ + struct qcomtee_object *object = arg->o; + + switch (typeof_qcomtee_object(object)) { + case QCOMTEE_OBJECT_TYPE_NULL: + param->u.objref.id = TEE_OBJREF_NULL; + + return 0; + case QCOMTEE_OBJECT_TYPE_CB: + /* object is a callback object: */ + if (is_qcomtee_user_object(object)) + return qcomtee_user_param_from_object(param, object, + ctx); + /* object is a memory object: */ + else if (is_qcomtee_memobj_object(object)) + return qcomtee_memobj_param_from_object(param, object, + ctx); + + break; + case QCOMTEE_OBJECT_TYPE_TEE: + return qcomtee_context_add_qtee_object(param, object, ctx); + + case QCOMTEE_OBJECT_TYPE_ROOT: + default: + break; + } + + return -EINVAL; +} + +/** + * qcomtee_params_to_args() - Convert TEE parameters to QTEE arguments. + * @u: QTEE arguments. + * @params: TEE parameters. + * @num_params: number of elements in the parameter array. + * @ctx: context in which the conversion should happen. + * + * It assumes @u has at least @num_params + 1 entries and has been initialized + * with %QCOMTEE_ARG_TYPE_INV as &struct qcomtee_arg.type. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +static int qcomtee_params_to_args(struct qcomtee_arg *u, + struct tee_param *params, int num_params, + struct tee_context *ctx) +{ + int i; + + for (i = 0; i < num_params; i++) { + switch (params[i].attr) { + case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT: + u[i].flags = QCOMTEE_ARG_FLAGS_UADDR; + u[i].b.uaddr = params[i].u.ubuf.uaddr; + u[i].b.size = params[i].u.ubuf.size; + + if (params[i].attr == + TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT) + u[i].type = QCOMTEE_ARG_TYPE_IB; + else /* TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT */ + u[i].type = QCOMTEE_ARG_TYPE_OB; + + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT: + u[i].type = QCOMTEE_ARG_TYPE_IO; + if (qcomtee_objref_to_arg(&u[i], ¶ms[i], ctx)) + goto out_failed; + + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT: + u[i].type = QCOMTEE_ARG_TYPE_OO; + u[i].o = NULL_QCOMTEE_OBJECT; + break; + default: + goto out_failed; + } + } + + return 0; + +out_failed: + /* Undo qcomtee_objref_to_arg(). */ + for (i--; i >= 0; i--) { + if (u[i].type != QCOMTEE_ARG_TYPE_IO) + continue; + + qcomtee_user_object_set_notify(u[i].o, false); + /* See docs for qcomtee_objref_to_arg() for double put. */ + if (typeof_qcomtee_object(u[i].o) == QCOMTEE_OBJECT_TYPE_CB) + qcomtee_object_put(u[i].o); + + qcomtee_object_put(u[i].o); + } + + return -EINVAL; +} + +/** + * qcomtee_params_from_args() - Convert QTEE arguments to TEE parameters. + * @params: TEE parameters. + * @u: QTEE arguments. + * @num_params: number of elements in the parameter array. + * @ctx: context in which the conversion should happen. + * + * @u should have already been initialized by qcomtee_params_to_args(). + * This also represents the end of a QTEE invocation that started with + * qcomtee_params_to_args() by releasing %QCOMTEE_ARG_TYPE_IO objects. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +static int qcomtee_params_from_args(struct tee_param *params, + struct qcomtee_arg *u, int num_params, + struct tee_context *ctx) +{ + int i, np; + + qcomtee_arg_for_each(np, u) { + switch (u[np].type) { + case QCOMTEE_ARG_TYPE_OB: + /* TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT */ + params[np].u.ubuf.size = u[np].b.size; + + break; + case QCOMTEE_ARG_TYPE_IO: + /* IEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT */ + qcomtee_object_put(u[np].o); + + break; + case QCOMTEE_ARG_TYPE_OO: + /* TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT */ + if (qcomtee_objref_from_arg(¶ms[np], &u[np], ctx)) + goto out_failed; + + break; + case QCOMTEE_ARG_TYPE_IB: + default: + break; + } + } + + return 0; + +out_failed: + /* Undo qcomtee_objref_from_arg(). */ + for (i = 0; i < np; i++) { + if (params[i].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT) + qcomtee_context_del_qtee_object(¶ms[i], ctx); + } + + /* Release any IO and OO objects not processed. */ + for (; u[i].type && i < num_params; i++) { + if (u[i].type == QCOMTEE_ARG_TYPE_OO || + u[i].type == QCOMTEE_ARG_TYPE_IO) + qcomtee_object_put(u[i].o); + } + + return -EINVAL; +} + +/* TEE Device Ops. */ + +static int qcomtee_params_check(struct tee_param *params, int num_params) +{ + int io = 0, oo = 0, ib = 0, ob = 0; + int i; + + /* QTEE can accept 64 arguments. */ + if (num_params > QCOMTEE_ARGS_MAX) + return -EINVAL; + + /* Supported parameter types. */ + for (i = 0; i < num_params; i++) { + switch (params[i].attr) { + case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT: + ib++; + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT: + ob++; + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT: + io++; + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT: + oo++; + break; + default: + return -EINVAL; + } + } + + /* QTEE can accept 16 arguments of each supported types. */ + if (io > QCOMTEE_ARGS_PER_TYPE || oo > QCOMTEE_ARGS_PER_TYPE || + ib > QCOMTEE_ARGS_PER_TYPE || ob > QCOMTEE_ARGS_PER_TYPE) + return -EINVAL; + + return 0; +} + +/* Check if an operation on ROOT_QCOMTEE_OBJECT from userspace is permitted. */ +static int qcomtee_root_object_check(u32 op, struct tee_param *params, + int num_params) +{ + /* Some privileged operations recognized by QTEE. */ + if (op == QCOMTEE_ROOT_OP_NOTIFY_DOMAIN_CHANGE || + op == QCOMTEE_ROOT_OP_ADCI_ACCEPT || + op == QCOMTEE_ROOT_OP_ADCI_SHUTDOWN) + return -EINVAL; + + /* + * QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS is to register with QTEE + * by passing a credential object as input OBJREF. TEE_OBJREF_NULL as a + * credential object represents a privileged client for QTEE and + * is used by the kernel only. + */ + if (op == QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS && num_params == 2) { + if (params[0].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT && + params[1].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT) { + if (params[0].u.objref.id == TEE_OBJREF_NULL) + return -EINVAL; + } + } + + return 0; +} + +/** + * qcomtee_object_invoke() - Invoke a QTEE object. + * @ctx: TEE context. + * @arg: ioctl arguments. + * @params: parameters for the object. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +static int qcomtee_object_invoke(struct tee_context *ctx, + struct tee_ioctl_object_invoke_arg *arg, + struct tee_param *params) +{ + struct qcomtee_object_invoke_ctx *oic __free(kfree) = NULL; + struct qcomtee_context_data *ctxdata = ctx->data; + struct qcomtee_arg *u __free(kfree) = NULL; + struct qcomtee_object *object; + int i, ret, result; + + if (qcomtee_params_check(params, arg->num_params)) + return -EINVAL; + + /* First, handle reserved operations: */ + if (arg->op == QCOMTEE_MSG_OBJECT_OP_RELEASE) { + del_qtee_object(arg->id, ctxdata); + + return 0; + } + + /* Otherwise, invoke a QTEE object: */ + oic = qcomtee_object_invoke_ctx_alloc(ctx); + if (!oic) + return -ENOMEM; + + /* +1 for ending QCOMTEE_ARG_TYPE_INV. */ + u = kcalloc(arg->num_params + 1, sizeof(*u), GFP_KERNEL); + if (!u) + return -ENOMEM; + + /* Get an object to invoke. */ + if (arg->id == TEE_OBJREF_NULL) { + /* Use ROOT if TEE_OBJREF_NULL is invoked. */ + if (qcomtee_root_object_check(arg->op, params, arg->num_params)) + return -EINVAL; + + object = ROOT_QCOMTEE_OBJECT; + } else if (find_qtee_object(&object, arg->id, ctxdata)) { + return -EINVAL; + } + + ret = qcomtee_params_to_args(u, params, arg->num_params, ctx); + if (ret) + goto out; + + ret = qcomtee_object_do_invoke(oic, object, arg->op, u, &result); + if (ret) { + qcomtee_arg_for_each_input_object(i, u) { + qcomtee_user_object_set_notify(u[i].o, false); + qcomtee_object_put(u[i].o); + } + + goto out; + } + + /* Prase QTEE response and put driver's object copies: */ + + if (!result) { + /* Assume service is UNAVAIL if unable to process the result. */ + if (qcomtee_params_from_args(params, u, arg->num_params, ctx)) + result = QCOMTEE_MSG_ERROR_UNAVAIL; + } else { + /* + * qcomtee_params_to_args() gets a copy of IO for the driver to + * make sure they do not get released while in the middle of + * invocation. On success (!result), qcomtee_params_from_args() + * puts them; Otherwise, put them here. + */ + qcomtee_arg_for_each_input_object(i, u) + qcomtee_object_put(u[i].o); + } + + arg->ret = result; +out: + qcomtee_object_put(object); + + return ret; +} + +/** + * qcomtee_supp_recv() - Wait for a request for the supplicant. + * @ctx: TEE context. + * @op: requested operation on the object. + * @num_params: number of elements in the parameter array. + * @params: parameters for @op. + * + * The first parameter is a meta %TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT. + * On input, it provides a user buffer. This buffer is used for parameters of + * type %TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT in qcomtee_cb_params_from_args(). + * On output, the object ID and request ID are stored in the meta parameter. + * + * @num_params is updated to the number of parameters that actually exist + * in @params on return. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +static int qcomtee_supp_recv(struct tee_context *ctx, u32 *op, u32 *num_params, + struct tee_param *params) +{ + struct qcomtee_user_object_request_data data; + void __user *uaddr; + size_t ubuf_size; + int i, ret; + + if (!*num_params) + return -EINVAL; + + /* First parameter should be an INOUT + meta parameter. */ + if (params->attr != + (TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT | TEE_IOCTL_PARAM_ATTR_META)) + return -EINVAL; + + /* Other parameters are none. */ + for (i = 1; i < *num_params; i++) + if (params[i].attr) + return -EINVAL; + + if (!IS_ALIGNED(params->u.value.a, 8)) + return -EINVAL; + + /* User buffer and size from meta parameter. */ + uaddr = u64_to_user_ptr(params->u.value.a); + ubuf_size = params->u.value.b; + /* Process TEE parameters. +/-1 to ignore the meta parameter. */ + ret = qcomtee_user_object_select(ctx, params + 1, *num_params - 1, + uaddr, ubuf_size, &data); + if (ret) + return ret; + + params->u.value.a = data.object_id; + params->u.value.b = data.id; + params->u.value.c = 0; + *op = data.op; + *num_params = data.np + 1; + + return 0; +} + +/** + * qcomtee_supp_send() - Submit a response for a request. + * @ctx: TEE context. + * @errno: return value for the request. + * @num_params: number of elements in the parameter array. + * @params: returned parameters. + * + * The first parameter is a meta %TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT. + * It specifies the request ID this response belongs to. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +static int qcomtee_supp_send(struct tee_context *ctx, u32 errno, u32 num_params, + struct tee_param *params) +{ + int req_id; + + if (!num_params) + return -EINVAL; + + /* First parameter should be an OUTPUT + meta parameter. */ + if (params->attr != (TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT | + TEE_IOCTL_PARAM_ATTR_META)) + return -EINVAL; + + req_id = params->u.value.a; + /* Process TEE parameters. +/-1 to ignore the meta parameter. */ + return qcomtee_user_object_submit(ctx, params + 1, num_params - 1, + req_id, errno); +} + +static int qcomtee_open(struct tee_context *ctx) +{ + struct qcomtee_context_data *ctxdata __free(kfree) = NULL; + + ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL); + if (!ctxdata) + return -ENOMEM; + + /* + * In the QTEE driver, the same context is used to refcount resources + * shared by QTEE. For example, teedev_ctx_get() is called for any + * instance of callback objects (see qcomtee_user_param_to_object()). + * + * Maintain a copy of teedev for QTEE as it serves as a direct user of + * this context. The teedev will be released in the context's release(). + * + * tee_device_unregister() will remain blocked until all contexts + * are released. This includes contexts owned by the user, which are + * closed by teedev_close_context(), as well as those owned by QTEE + * closed by teedev_ctx_put() in object's release(). + */ + if (!tee_device_get(ctx->teedev)) + return -EINVAL; + + idr_init(&ctxdata->qtee_objects_idr); + mutex_init(&ctxdata->qtee_lock); + idr_init(&ctxdata->reqs_idr); + INIT_LIST_HEAD(&ctxdata->reqs_list); + mutex_init(&ctxdata->reqs_lock); + init_completion(&ctxdata->req_c); + + ctx->data = no_free_ptr(ctxdata); + + return 0; +} + +/* Gets called when the user closes the device */ +static void qcomtee_close_context(struct tee_context *ctx) +{ + struct qcomtee_context_data *ctxdata = ctx->data; + struct qcomtee_object *object; + int id; + + /* Process QUEUED or PROCESSING requests. */ + qcomtee_requests_destroy(ctxdata); + /* Release QTEE objects. */ + idr_for_each_entry(&ctxdata->qtee_objects_idr, object, id) + qcomtee_object_put(object); +} + +/* Gets called when the final reference to the context goes away. */ +static void qcomtee_release(struct tee_context *ctx) +{ + struct qcomtee_context_data *ctxdata = ctx->data; + + idr_destroy(&ctxdata->qtee_objects_idr); + idr_destroy(&ctxdata->reqs_idr); + kfree(ctxdata); + + /* There is nothing shared in this context with QTEE. */ + tee_device_put(ctx->teedev); +} + +static void qcomtee_get_version(struct tee_device *teedev, + struct tee_ioctl_version_data *vers) +{ + struct tee_ioctl_version_data v = { + .impl_id = TEE_IMPL_ID_QTEE, + .gen_caps = TEE_GEN_CAP_OBJREF, + }; + + *vers = v; +} + +/** + * qcomtee_get_qtee_feature_list() - Query QTEE features versions. + * @ctx: TEE context. + * @id: ID of the feature to query. + * @version: version of the feature. + * + * Used to query the verion of features supported by QTEE. + */ +static void qcomtee_get_qtee_feature_list(struct tee_context *ctx, u32 id, + u32 *version) +{ + struct qcomtee_object_invoke_ctx *oic __free(kfree); + struct qcomtee_object *client_env, *service; + struct qcomtee_arg u[3] = { 0 }; + int result; + + oic = qcomtee_object_invoke_ctx_alloc(ctx); + if (!oic) + return; + + client_env = qcomtee_object_get_client_env(oic); + if (client_env == NULL_QCOMTEE_OBJECT) + return; + + /* Get ''FeatureVersions Service'' object. */ + service = qcomtee_object_get_service(oic, client_env, + QCOMTEE_FEATURE_VER_UID); + if (service == NULL_QCOMTEE_OBJECT) + goto out_failed; + + /* IB: Feature to query. */ + u[0].b.addr = &id; + u[0].b.size = sizeof(id); + u[0].type = QCOMTEE_ARG_TYPE_IB; + + /* OB: Version returned. */ + u[1].b.addr = version; + u[1].b.size = sizeof(*version); + u[1].type = QCOMTEE_ARG_TYPE_OB; + + qcomtee_object_do_invoke(oic, service, QCOMTEE_FEATURE_VER_OP_GET, u, + &result); + +out_failed: + qcomtee_object_put(service); + qcomtee_object_put(client_env); +} + +static const struct tee_driver_ops qcomtee_ops = { + .get_version = qcomtee_get_version, + .open = qcomtee_open, + .close_context = qcomtee_close_context, + .release = qcomtee_release, + .object_invoke_func = qcomtee_object_invoke, + .supp_recv = qcomtee_supp_recv, + .supp_send = qcomtee_supp_send, +}; + +static const struct tee_desc qcomtee_desc = { + .name = "qcomtee", + .ops = &qcomtee_ops, + .owner = THIS_MODULE, +}; + +static int qcomtee_probe(struct platform_device *pdev) +{ + struct workqueue_struct *async_wq; + struct tee_device *teedev; + struct tee_shm_pool *pool; + struct tee_context *ctx; + struct qcomtee *qcomtee; + int err; + + qcomtee = kzalloc(sizeof(*qcomtee), GFP_KERNEL); + if (!qcomtee) + return -ENOMEM; + + pool = qcomtee_shm_pool_alloc(); + if (IS_ERR(pool)) { + err = PTR_ERR(pool); + + goto err_free_qcomtee; + } + + teedev = tee_device_alloc(&qcomtee_desc, NULL, pool, qcomtee); + if (IS_ERR(teedev)) { + err = PTR_ERR(teedev); + + goto err_pool_destroy; + } + + qcomtee->teedev = teedev; + qcomtee->pool = pool; + err = tee_device_register(qcomtee->teedev); + if (err) + goto err_unreg_teedev; + + platform_set_drvdata(pdev, qcomtee); + /* Start async wq. */ + async_wq = alloc_ordered_workqueue("qcomtee_wq", 0); + if (!async_wq) { + err = -ENOMEM; + + goto err_unreg_teedev; + } + + qcomtee->wq = async_wq; + /* Driver context used for async operations of teedev. */ + ctx = teedev_open(qcomtee->teedev); + if (IS_ERR(ctx)) { + err = PTR_ERR(ctx); + + goto err_dest_wq; + } + + qcomtee->ctx = ctx; + /* Init Object table. */ + qcomtee->xa_last_id = 0; + xa_init_flags(&qcomtee->xa_local_objects, XA_FLAGS_ALLOC); + /* Get QTEE verion. */ + qcomtee_get_qtee_feature_list(qcomtee->ctx, + QCOMTEE_FEATURE_VER_OP_GET_QTEE_ID, + &qcomtee->qtee_version); + + pr_info("QTEE version %u.%u.%u\n", + QTEE_VERSION_GET_MAJOR(qcomtee->qtee_version), + QTEE_VERSION_GET_MINOR(qcomtee->qtee_version), + QTEE_VERSION_GET_PATCH(qcomtee->qtee_version)); + + return 0; + +err_dest_wq: + destroy_workqueue(qcomtee->wq); +err_unreg_teedev: + tee_device_unregister(qcomtee->teedev); +err_pool_destroy: + tee_shm_pool_free(pool); +err_free_qcomtee: + kfree(qcomtee); + + return err; +} + +/** + * qcomtee_remove() - Device Removal Routine. + * @pdev: platform device information struct. + * + * It is called by the platform subsystem to alert the driver that it should + * release the device. + * + * QTEE does not provide an API to inform it about a callback object going away. + * However, when releasing QTEE objects, any callback object sent to QTEE + * previously would be released by QTEE as part of the object release. + */ +static void qcomtee_remove(struct platform_device *pdev) +{ + struct qcomtee *qcomtee = platform_get_drvdata(pdev); + + teedev_close_context(qcomtee->ctx); + /* Wait for RELEASE operations to be processed for QTEE objects. */ + tee_device_unregister(qcomtee->teedev); + destroy_workqueue(qcomtee->wq); + tee_shm_pool_free(qcomtee->pool); + kfree(qcomtee); +} + +static const struct platform_device_id qcomtee_ids[] = { { "qcomtee", 0 }, {} }; +MODULE_DEVICE_TABLE(platform, qcomtee_ids); + +static struct platform_driver qcomtee_platform_driver = { + .probe = qcomtee_probe, + .remove = qcomtee_remove, + .driver = { + .name = "qcomtee", + }, + .id_table = qcomtee_ids, +}; + +module_platform_driver(qcomtee_platform_driver); + +MODULE_AUTHOR("Qualcomm"); +MODULE_DESCRIPTION("QTEE driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tee/qcomtee/core.c b/drivers/tee/qcomtee/core.c new file mode 100644 index 000000000000..783acc59cfa9 --- /dev/null +++ b/drivers/tee/qcomtee/core.c @@ -0,0 +1,915 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/firmware/qcom/qcom_scm.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/xarray.h> + +#include "qcomtee.h" + +/* QTEE root object. */ +struct qcomtee_object qcomtee_object_root = { + .name = "root", + .object_type = QCOMTEE_OBJECT_TYPE_ROOT, + .info.qtee_id = QCOMTEE_MSG_OBJECT_ROOT, +}; + +/* Next argument of type @type after index @i. */ +int qcomtee_next_arg_type(struct qcomtee_arg *u, int i, + enum qcomtee_arg_type type) +{ + while (u[i].type != QCOMTEE_ARG_TYPE_INV && u[i].type != type) + i++; + return i; +} + +/* + * QTEE expects IDs with QCOMTEE_MSG_OBJECT_NS_BIT set for objects of + * QCOMTEE_OBJECT_TYPE_CB type. The first ID with QCOMTEE_MSG_OBJECT_NS_BIT + * set is reserved for the primordial object. + */ +#define QCOMTEE_OBJECT_PRIMORDIAL (QCOMTEE_MSG_OBJECT_NS_BIT) +#define QCOMTEE_OBJECT_ID_START (QCOMTEE_OBJECT_PRIMORDIAL + 1) +#define QCOMTEE_OBJECT_ID_END (U32_MAX) + +#define QCOMTEE_OBJECT_SET(p, type, ...) \ + __QCOMTEE_OBJECT_SET(p, type, ##__VA_ARGS__, 0UL) +#define __QCOMTEE_OBJECT_SET(p, type, optr, ...) \ + do { \ + (p)->object_type = (type); \ + (p)->info.qtee_id = (unsigned long)(optr); \ + } while (0) + +static struct qcomtee_object * +qcomtee_qtee_object_alloc(struct qcomtee_object_invoke_ctx *oic, + unsigned int object_id) +{ + struct qcomtee *qcomtee = tee_get_drvdata(oic->ctx->teedev); + struct qcomtee_object *object; + + object = kzalloc(sizeof(*object), GFP_KERNEL); + if (!object) + return NULL_QCOMTEE_OBJECT; + + /* If failed, "no-name". */ + object->name = kasprintf(GFP_KERNEL, "qcomtee-%u", object_id); + QCOMTEE_OBJECT_SET(object, QCOMTEE_OBJECT_TYPE_TEE, object_id); + kref_init(&object->refcount); + /* A QTEE object requires a context for async operations. */ + object->info.qcomtee_async_ctx = qcomtee->ctx; + teedev_ctx_get(object->info.qcomtee_async_ctx); + + return object; +} + +static void qcomtee_qtee_object_free(struct qcomtee_object *object) +{ + /* See qcomtee_qtee_object_alloc(). */ + teedev_ctx_put(object->info.qcomtee_async_ctx); + + kfree(object->name); + kfree(object); +} + +static void qcomtee_do_release_qtee_object(struct work_struct *work) +{ + struct qcomtee_object *object; + struct qcomtee *qcomtee; + int ret, result; + + /* RELEASE does not require any argument. */ + struct qcomtee_arg args[] = { { .type = QCOMTEE_ARG_TYPE_INV } }; + + object = container_of(work, struct qcomtee_object, work); + qcomtee = tee_get_drvdata(object->info.qcomtee_async_ctx->teedev); + /* Get the TEE context used for asynchronous operations. */ + qcomtee->oic.ctx = object->info.qcomtee_async_ctx; + + ret = qcomtee_object_do_invoke_internal(&qcomtee->oic, object, + QCOMTEE_MSG_OBJECT_OP_RELEASE, + args, &result); + + /* Is it safe to retry the release? */ + if (ret && ret != -ENODEV) { + queue_work(qcomtee->wq, &object->work); + } else { + if (ret || result) + pr_err("%s release failed, ret = %d (%x)\n", + qcomtee_object_name(object), ret, result); + qcomtee_qtee_object_free(object); + } +} + +static void qcomtee_release_qtee_object(struct qcomtee_object *object) +{ + struct qcomtee *qcomtee = + tee_get_drvdata(object->info.qcomtee_async_ctx->teedev); + + INIT_WORK(&object->work, qcomtee_do_release_qtee_object); + queue_work(qcomtee->wq, &object->work); +} + +static void qcomtee_object_release(struct kref *refcount) +{ + struct qcomtee_object *object; + const char *name; + + object = container_of(refcount, struct qcomtee_object, refcount); + + /* + * qcomtee_object_get() is called in a RCU read lock. synchronize_rcu() + * to avoid releasing the object while it is being accessed in + * qcomtee_object_get(). + */ + synchronize_rcu(); + + switch (typeof_qcomtee_object(object)) { + case QCOMTEE_OBJECT_TYPE_TEE: + qcomtee_release_qtee_object(object); + + break; + case QCOMTEE_OBJECT_TYPE_CB: + name = object->name; + + if (object->ops->release) + object->ops->release(object); + + kfree_const(name); + + break; + case QCOMTEE_OBJECT_TYPE_ROOT: + case QCOMTEE_OBJECT_TYPE_NULL: + default: + break; + } +} + +/** + * qcomtee_object_get() - Increase the object's reference count. + * @object: object to increase the reference count. + * + * Context: The caller should hold RCU read lock. + */ +int qcomtee_object_get(struct qcomtee_object *object) +{ + if (object != &qcomtee_primordial_object && + object != NULL_QCOMTEE_OBJECT && + object != ROOT_QCOMTEE_OBJECT) + return kref_get_unless_zero(&object->refcount); + + return 0; +} + +/** + * qcomtee_object_put() - Decrease the object's reference count. + * @object: object to decrease the reference count. + */ +void qcomtee_object_put(struct qcomtee_object *object) +{ + if (object != &qcomtee_primordial_object && + object != NULL_QCOMTEE_OBJECT && + object != ROOT_QCOMTEE_OBJECT) + kref_put(&object->refcount, qcomtee_object_release); +} + +static int qcomtee_idx_alloc(struct qcomtee_object_invoke_ctx *oic, u32 *idx, + struct qcomtee_object *object) +{ + struct qcomtee *qcomtee = tee_get_drvdata(oic->ctx->teedev); + + /* Every ID allocated here has QCOMTEE_MSG_OBJECT_NS_BIT set. */ + return xa_alloc_cyclic(&qcomtee->xa_local_objects, idx, object, + XA_LIMIT(QCOMTEE_OBJECT_ID_START, + QCOMTEE_OBJECT_ID_END), + &qcomtee->xa_last_id, GFP_KERNEL); +} + +struct qcomtee_object *qcomtee_idx_erase(struct qcomtee_object_invoke_ctx *oic, + u32 idx) +{ + struct qcomtee *qcomtee = tee_get_drvdata(oic->ctx->teedev); + + if (idx < QCOMTEE_OBJECT_ID_START || idx > QCOMTEE_OBJECT_ID_END) + return NULL_QCOMTEE_OBJECT; + + return xa_erase(&qcomtee->xa_local_objects, idx); +} + +/** + * qcomtee_object_id_get() - Get an ID for an object to send to QTEE. + * @oic: context to use for the invocation. + * @object: object to assign an ID. + * @object_id: object ID. + * + * Called on the path to QTEE to construct the message; see + * qcomtee_prepare_msg() and qcomtee_update_msg(). + * + * Return: On success, returns 0; on failure, returns < 0. + */ +static int qcomtee_object_id_get(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object *object, + unsigned int *object_id) +{ + u32 idx; + + switch (typeof_qcomtee_object(object)) { + case QCOMTEE_OBJECT_TYPE_CB: + if (qcomtee_idx_alloc(oic, &idx, object) < 0) + return -ENOSPC; + + *object_id = idx; + + break; + case QCOMTEE_OBJECT_TYPE_ROOT: + case QCOMTEE_OBJECT_TYPE_TEE: + *object_id = object->info.qtee_id; + + break; + case QCOMTEE_OBJECT_TYPE_NULL: + *object_id = QCOMTEE_MSG_OBJECT_NULL; + + break; + } + + return 0; +} + +/* Release object ID assigned in qcomtee_object_id_get. */ +static void qcomtee_object_id_put(struct qcomtee_object_invoke_ctx *oic, + unsigned int object_id) +{ + qcomtee_idx_erase(oic, object_id); +} + +/** + * qcomtee_local_object_get() - Get the object referenced by the ID. + * @oic: context to use for the invocation. + * @object_id: object ID. + * + * It is called on the path from QTEE. + * It is called on behalf of QTEE to obtain an instance of an object + * for a given ID. It increases the object's reference count on success. + * + * Return: On error, returns %NULL_QCOMTEE_OBJECT. + * On success, returns the object. + */ +static struct qcomtee_object * +qcomtee_local_object_get(struct qcomtee_object_invoke_ctx *oic, + unsigned int object_id) +{ + struct qcomtee *qcomtee = tee_get_drvdata(oic->ctx->teedev); + struct qcomtee_object *object; + + if (object_id == QCOMTEE_OBJECT_PRIMORDIAL) + return &qcomtee_primordial_object; + + guard(rcu)(); + object = xa_load(&qcomtee->xa_local_objects, object_id); + /* It already checks for %NULL_QCOMTEE_OBJECT. */ + qcomtee_object_get(object); + + return object; +} + +/** + * qcomtee_object_user_init() - Initialize an object for the user. + * @object: object to initialize. + * @ot: type of object as &enum qcomtee_object_type. + * @ops: instance of callbacks. + * @fmt: name assigned to the object. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +int qcomtee_object_user_init(struct qcomtee_object *object, + enum qcomtee_object_type ot, + struct qcomtee_object_operations *ops, + const char *fmt, ...) +{ + va_list ap; + int ret; + + kref_init(&object->refcount); + QCOMTEE_OBJECT_SET(object, QCOMTEE_OBJECT_TYPE_NULL); + + va_start(ap, fmt); + switch (ot) { + case QCOMTEE_OBJECT_TYPE_NULL: + ret = 0; + + break; + case QCOMTEE_OBJECT_TYPE_CB: + object->ops = ops; + if (!object->ops->dispatch) + return -EINVAL; + + /* If failed, "no-name". */ + object->name = kvasprintf_const(GFP_KERNEL, fmt, ap); + QCOMTEE_OBJECT_SET(object, QCOMTEE_OBJECT_TYPE_CB); + + ret = 0; + break; + case QCOMTEE_OBJECT_TYPE_ROOT: + case QCOMTEE_OBJECT_TYPE_TEE: + default: + ret = -EINVAL; + } + va_end(ap); + + return ret; +} + +/** + * qcomtee_object_type() - Returns the type of object represented by an ID. + * @object_id: object ID for the object. + * + * Similar to typeof_qcomtee_object(), but instead of receiving an object as + * an argument, it receives an object ID. It is used internally on the return + * path from QTEE. + * + * Return: Returns the type of object referenced by @object_id. + */ +static enum qcomtee_object_type qcomtee_object_type(unsigned int object_id) +{ + if (object_id == QCOMTEE_MSG_OBJECT_NULL) + return QCOMTEE_OBJECT_TYPE_NULL; + + if (object_id & QCOMTEE_MSG_OBJECT_NS_BIT) + return QCOMTEE_OBJECT_TYPE_CB; + + return QCOMTEE_OBJECT_TYPE_TEE; +} + +/** + * qcomtee_object_qtee_init() - Initialize an object for QTEE. + * @oic: context to use for the invocation. + * @object: object returned. + * @object_id: object ID received from QTEE. + * + * Return: On failure, returns < 0 and sets @object to %NULL_QCOMTEE_OBJECT. + * On success, returns 0 + */ +static int qcomtee_object_qtee_init(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object **object, + unsigned int object_id) +{ + int ret = 0; + + switch (qcomtee_object_type(object_id)) { + case QCOMTEE_OBJECT_TYPE_NULL: + *object = NULL_QCOMTEE_OBJECT; + + break; + case QCOMTEE_OBJECT_TYPE_CB: + *object = qcomtee_local_object_get(oic, object_id); + if (*object == NULL_QCOMTEE_OBJECT) + ret = -EINVAL; + + break; + + default: /* QCOMTEE_OBJECT_TYPE_TEE */ + *object = qcomtee_qtee_object_alloc(oic, object_id); + if (*object == NULL_QCOMTEE_OBJECT) + ret = -ENOMEM; + + break; + } + + return ret; +} + +/* + * ''Marshaling API'' + * qcomtee_prepare_msg - Prepare the inbound buffer for sending to QTEE + * qcomtee_update_args - Parse the QTEE response in the inbound buffer + * qcomtee_prepare_args - Parse the QTEE request from the outbound buffer + * qcomtee_update_msg - Update the outbound buffer with the response for QTEE + */ + +static int qcomtee_prepare_msg(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object *object, u32 op, + struct qcomtee_arg *u) +{ + struct qcomtee_msg_object_invoke *msg; + unsigned int object_id; + int i, ib, ob, io, oo; + size_t offset; + + /* Use the input message buffer in 'oic'. */ + msg = oic->in_msg.addr; + + /* Start offset in a message for buffer arguments. */ + offset = qcomtee_msg_buffer_args(struct qcomtee_msg_object_invoke, + qcomtee_args_len(u)); + + /* Get the ID of the object being invoked. */ + if (qcomtee_object_id_get(oic, object, &object_id)) + return -ENOSPC; + + ib = 0; + qcomtee_arg_for_each_input_buffer(i, u) { + void *msgptr; /* Address of buffer payload: */ + /* Overflow already checked in qcomtee_msg_buffers_alloc(). */ + msg->args[ib].b.offset = offset; + msg->args[ib].b.size = u[i].b.size; + + msgptr = qcomtee_msg_offset_to_ptr(msg, offset); + /* Userspace client or kernel client!? */ + if (!(u[i].flags & QCOMTEE_ARG_FLAGS_UADDR)) + memcpy(msgptr, u[i].b.addr, u[i].b.size); + else if (copy_from_user(msgptr, u[i].b.uaddr, u[i].b.size)) + return -EINVAL; + + offset += qcomtee_msg_offset_align(u[i].b.size); + ib++; + } + + ob = ib; + qcomtee_arg_for_each_output_buffer(i, u) { + /* Overflow already checked in qcomtee_msg_buffers_alloc(). */ + msg->args[ob].b.offset = offset; + msg->args[ob].b.size = u[i].b.size; + + offset += qcomtee_msg_offset_align(u[i].b.size); + ob++; + } + + io = ob; + qcomtee_arg_for_each_input_object(i, u) { + if (qcomtee_object_id_get(oic, u[i].o, &msg->args[io].o)) { + qcomtee_object_id_put(oic, object_id); + for (io--; io >= ob; io--) + qcomtee_object_id_put(oic, msg->args[io].o); + + return -ENOSPC; + } + + io++; + } + + oo = io; + qcomtee_arg_for_each_output_object(i, u) + oo++; + + /* Set object, operation, and argument counts. */ + qcomtee_msg_init(msg, object_id, op, ib, ob, io, oo); + + return 0; +} + +/** + * qcomtee_update_args() - Parse the QTEE response in the inbound buffer. + * @u: array of arguments for the invocation. + * @oic: context to use for the invocation. + * + * @u must be the same as the one used in qcomtee_prepare_msg() when + * initializing the inbound buffer. + * + * On failure, it continues processing the QTEE message. The caller should + * do the necessary cleanup, including calling qcomtee_object_put() + * on the output objects. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +static int qcomtee_update_args(struct qcomtee_arg *u, + struct qcomtee_object_invoke_ctx *oic) +{ + struct qcomtee_msg_object_invoke *msg; + int i, ib, ob, io, oo; + int ret = 0; + + /* Use the input message buffer in 'oic'. */ + msg = oic->in_msg.addr; + + ib = 0; + qcomtee_arg_for_each_input_buffer(i, u) + ib++; + + ob = ib; + qcomtee_arg_for_each_output_buffer(i, u) { + void *msgptr; /* Address of buffer payload: */ + /* QTEE can override the size to a smaller value. */ + u[i].b.size = msg->args[ob].b.size; + + msgptr = qcomtee_msg_offset_to_ptr(msg, msg->args[ob].b.offset); + /* Userspace client or kernel client!? */ + if (!(u[i].flags & QCOMTEE_ARG_FLAGS_UADDR)) + memcpy(u[i].b.addr, msgptr, u[i].b.size); + else if (copy_to_user(u[i].b.uaddr, msgptr, u[i].b.size)) + ret = -EINVAL; + + ob++; + } + + io = ob; + qcomtee_arg_for_each_input_object(i, u) + io++; + + oo = io; + qcomtee_arg_for_each_output_object(i, u) { + if (qcomtee_object_qtee_init(oic, &u[i].o, msg->args[oo].o)) + ret = -EINVAL; + + oo++; + } + + return ret; +} + +/** + * qcomtee_prepare_args() - Parse the QTEE request from the outbound buffer. + * @oic: context to use for the invocation. + * + * It initializes &qcomtee_object_invoke_ctx->u based on the QTEE request in + * the outbound buffer. It sets %QCOMTEE_ARG_TYPE_INV at the end of the array. + * + * On failure, it continues processing the QTEE message. The caller should + * do the necessary cleanup, including calling qcomtee_object_put() + * on the input objects. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +static int qcomtee_prepare_args(struct qcomtee_object_invoke_ctx *oic) +{ + struct qcomtee_msg_callback *msg; + int i, ret = 0; + + /* Use the output message buffer in 'oic'. */ + msg = oic->out_msg.addr; + + qcomtee_msg_for_each_input_buffer(i, msg) { + oic->u[i].b.addr = + qcomtee_msg_offset_to_ptr(msg, msg->args[i].b.offset); + oic->u[i].b.size = msg->args[i].b.size; + oic->u[i].type = QCOMTEE_ARG_TYPE_IB; + } + + qcomtee_msg_for_each_output_buffer(i, msg) { + oic->u[i].b.addr = + qcomtee_msg_offset_to_ptr(msg, msg->args[i].b.offset); + oic->u[i].b.size = msg->args[i].b.size; + oic->u[i].type = QCOMTEE_ARG_TYPE_OB; + } + + qcomtee_msg_for_each_input_object(i, msg) { + if (qcomtee_object_qtee_init(oic, &oic->u[i].o, msg->args[i].o)) + ret = -EINVAL; + + oic->u[i].type = QCOMTEE_ARG_TYPE_IO; + } + + qcomtee_msg_for_each_output_object(i, msg) + oic->u[i].type = QCOMTEE_ARG_TYPE_OO; + + /* End of Arguments. */ + oic->u[i].type = QCOMTEE_ARG_TYPE_INV; + + return ret; +} + +static int qcomtee_update_msg(struct qcomtee_object_invoke_ctx *oic) +{ + struct qcomtee_msg_callback *msg; + int i, ib, ob, io, oo; + + /* Use the output message buffer in 'oic'. */ + msg = oic->out_msg.addr; + + ib = 0; + qcomtee_arg_for_each_input_buffer(i, oic->u) + ib++; + + ob = ib; + qcomtee_arg_for_each_output_buffer(i, oic->u) { + /* Only reduce size; never increase it. */ + if (msg->args[ob].b.size < oic->u[i].b.size) + return -EINVAL; + + msg->args[ob].b.size = oic->u[i].b.size; + ob++; + } + + io = ob; + qcomtee_arg_for_each_input_object(i, oic->u) + io++; + + oo = io; + qcomtee_arg_for_each_output_object(i, oic->u) { + if (qcomtee_object_id_get(oic, oic->u[i].o, &msg->args[oo].o)) { + for (oo--; oo >= io; oo--) + qcomtee_object_id_put(oic, msg->args[oo].o); + + return -ENOSPC; + } + + oo++; + } + + return 0; +} + +/* Invoke a callback object. */ +static void qcomtee_cb_object_invoke(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_msg_callback *msg) +{ + int i, errno; + u32 op; + + /* Get the object being invoked. */ + unsigned int object_id = msg->cxt; + struct qcomtee_object *object; + + /* QTEE cannot invoke a NULL object or objects it hosts. */ + if (qcomtee_object_type(object_id) == QCOMTEE_OBJECT_TYPE_NULL || + qcomtee_object_type(object_id) == QCOMTEE_OBJECT_TYPE_TEE) { + errno = -EINVAL; + goto out; + } + + object = qcomtee_local_object_get(oic, object_id); + if (object == NULL_QCOMTEE_OBJECT) { + errno = -EINVAL; + goto out; + } + + oic->object = object; + + /* Filter bits used by transport. */ + op = msg->op & QCOMTEE_MSG_OBJECT_OP_MASK; + + switch (op) { + case QCOMTEE_MSG_OBJECT_OP_RELEASE: + qcomtee_object_id_put(oic, object_id); + qcomtee_object_put(object); + errno = 0; + + break; + case QCOMTEE_MSG_OBJECT_OP_RETAIN: + qcomtee_object_get(object); + errno = 0; + + break; + default: + errno = qcomtee_prepare_args(oic); + if (errno) { + /* Release any object that arrived as input. */ + qcomtee_arg_for_each_input_buffer(i, oic->u) + qcomtee_object_put(oic->u[i].o); + + break; + } + + errno = object->ops->dispatch(oic, object, op, oic->u); + if (!errno) { + /* On success, notify at the appropriate time. */ + oic->flags |= QCOMTEE_OIC_FLAG_NOTIFY; + } + } + +out: + + oic->errno = errno; +} + +static int +qcomtee_object_invoke_ctx_invoke(struct qcomtee_object_invoke_ctx *oic, + int *result, u64 *res_type) +{ + phys_addr_t out_msg_paddr; + phys_addr_t in_msg_paddr; + int ret; + u64 res; + + tee_shm_get_pa(oic->out_shm, 0, &out_msg_paddr); + tee_shm_get_pa(oic->in_shm, 0, &in_msg_paddr); + if (!(oic->flags & QCOMTEE_OIC_FLAG_BUSY)) + ret = qcom_scm_qtee_invoke_smc(in_msg_paddr, oic->in_msg.size, + out_msg_paddr, oic->out_msg.size, + &res, res_type); + else + ret = qcom_scm_qtee_callback_response(out_msg_paddr, + oic->out_msg.size, + &res, res_type); + + if (ret) + pr_err("QTEE returned with %d.\n", ret); + else + *result = (int)res; + + return ret; +} + +/** + * qcomtee_qtee_objects_put() - Put the callback objects in the argument array. + * @u: array of arguments. + * + * When qcomtee_object_do_invoke_internal() is successfully invoked, + * QTEE takes ownership of the callback objects. If the invocation fails, + * qcomtee_object_do_invoke_internal() calls qcomtee_qtee_objects_put() + * to mimic the release of callback objects by QTEE. + */ +static void qcomtee_qtee_objects_put(struct qcomtee_arg *u) +{ + int i; + + qcomtee_arg_for_each_input_object(i, u) { + if (typeof_qcomtee_object(u[i].o) == QCOMTEE_OBJECT_TYPE_CB) + qcomtee_object_put(u[i].o); + } +} + +/** + * qcomtee_object_do_invoke_internal() - Submit an invocation for an object. + * @oic: context to use for the current invocation. + * @object: object being invoked. + * @op: requested operation on the object. + * @u: array of arguments for the current invocation. + * @result: result returned from QTEE. + * + * The caller is responsible for keeping track of the refcount for each + * object, including @object. On return, the caller loses ownership of all + * input objects of type %QCOMTEE_OBJECT_TYPE_CB. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +int qcomtee_object_do_invoke_internal(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object *object, u32 op, + struct qcomtee_arg *u, int *result) +{ + struct qcomtee_msg_callback *cb_msg; + struct qcomtee_object *qto; + int i, ret, errno; + u64 res_type; + + /* Allocate inbound and outbound buffers. */ + ret = qcomtee_msg_buffers_alloc(oic, u); + if (ret) { + qcomtee_qtee_objects_put(u); + + return ret; + } + + ret = qcomtee_prepare_msg(oic, object, op, u); + if (ret) { + qcomtee_qtee_objects_put(u); + + goto out; + } + + /* Use input message buffer in 'oic'. */ + cb_msg = oic->out_msg.addr; + + while (1) { + if (oic->flags & QCOMTEE_OIC_FLAG_BUSY) { + errno = oic->errno; + if (!errno) + errno = qcomtee_update_msg(oic); + qcomtee_msg_set_result(cb_msg, errno); + } + + /* Invoke the remote object. */ + ret = qcomtee_object_invoke_ctx_invoke(oic, result, &res_type); + /* Return form callback objects result submission: */ + if (oic->flags & QCOMTEE_OIC_FLAG_BUSY) { + qto = oic->object; + if (qto) { + if (oic->flags & QCOMTEE_OIC_FLAG_NOTIFY) { + if (qto->ops->notify) + qto->ops->notify(oic, qto, + errno || ret); + } + + /* Get is in qcomtee_cb_object_invoke(). */ + qcomtee_object_put(qto); + } + + oic->object = NULL_QCOMTEE_OBJECT; + oic->flags &= ~(QCOMTEE_OIC_FLAG_BUSY | + QCOMTEE_OIC_FLAG_NOTIFY); + } + + if (ret) { + /* + * Unable to finished the invocation. + * If QCOMTEE_OIC_FLAG_SHARED is not set, put + * QCOMTEE_OBJECT_TYPE_CB input objects. + */ + if (!(oic->flags & QCOMTEE_OIC_FLAG_SHARED)) + qcomtee_qtee_objects_put(u); + else + ret = -ENODEV; + + goto out; + + } else { + /* + * QTEE obtained ownership of QCOMTEE_OBJECT_TYPE_CB + * input objects in 'u'. On further failure, QTEE is + * responsible for releasing them. + */ + oic->flags |= QCOMTEE_OIC_FLAG_SHARED; + } + + /* Is it a callback request? */ + if (res_type != QCOMTEE_RESULT_INBOUND_REQ_NEEDED) { + /* + * Parse results. If failed, assume the service + * was unavailable (i.e. QCOMTEE_MSG_ERROR_UNAVAIL) + * and put output objects to initiate cleanup. + */ + if (!*result && qcomtee_update_args(u, oic)) { + *result = QCOMTEE_MSG_ERROR_UNAVAIL; + qcomtee_arg_for_each_output_object(i, u) + qcomtee_object_put(u[i].o); + } + + break; + + } else { + oic->flags |= QCOMTEE_OIC_FLAG_BUSY; + qcomtee_fetch_async_reqs(oic); + qcomtee_cb_object_invoke(oic, cb_msg); + } + } + + qcomtee_fetch_async_reqs(oic); +out: + qcomtee_msg_buffers_free(oic); + + return ret; +} + +int qcomtee_object_do_invoke(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object *object, u32 op, + struct qcomtee_arg *u, int *result) +{ + /* User can not set bits used by transport. */ + if (op & ~QCOMTEE_MSG_OBJECT_OP_MASK) + return -EINVAL; + + /* User can only invoke QTEE hosted objects. */ + if (typeof_qcomtee_object(object) != QCOMTEE_OBJECT_TYPE_TEE && + typeof_qcomtee_object(object) != QCOMTEE_OBJECT_TYPE_ROOT) + return -EINVAL; + + /* User cannot directly issue these operations to QTEE. */ + if (op == QCOMTEE_MSG_OBJECT_OP_RELEASE || + op == QCOMTEE_MSG_OBJECT_OP_RETAIN) + return -EINVAL; + + return qcomtee_object_do_invoke_internal(oic, object, op, u, result); +} + +/** + * qcomtee_object_get_client_env() - Get a privileged client env. object. + * @oic: context to use for the current invocation. + * + * The caller should call qcomtee_object_put() on the returned object + * to release it. + * + * Return: On error, returns %NULL_QCOMTEE_OBJECT. + * On success, returns the object. + */ +struct qcomtee_object * +qcomtee_object_get_client_env(struct qcomtee_object_invoke_ctx *oic) +{ + struct qcomtee_arg u[3] = { 0 }; + int ret, result; + + u[0].o = NULL_QCOMTEE_OBJECT; + u[0].type = QCOMTEE_ARG_TYPE_IO; + u[1].type = QCOMTEE_ARG_TYPE_OO; + ret = qcomtee_object_do_invoke(oic, ROOT_QCOMTEE_OBJECT, + QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS, u, + &result); + if (ret || result) + return NULL_QCOMTEE_OBJECT; + + return u[1].o; +} + +struct qcomtee_object * +qcomtee_object_get_service(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object *client_env, u32 uid) +{ + struct qcomtee_arg u[3] = { 0 }; + int ret, result; + + u[0].b.addr = &uid; + u[0].b.size = sizeof(uid); + u[0].type = QCOMTEE_ARG_TYPE_IB; + u[1].type = QCOMTEE_ARG_TYPE_OO; + ret = qcomtee_object_do_invoke(oic, client_env, QCOMTEE_CLIENT_ENV_OPEN, + u, &result); + + if (ret || result) + return NULL_QCOMTEE_OBJECT; + + return u[1].o; +} diff --git a/drivers/tee/qcomtee/mem_obj.c b/drivers/tee/qcomtee/mem_obj.c new file mode 100644 index 000000000000..228a3e30a31b --- /dev/null +++ b/drivers/tee/qcomtee/mem_obj.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/firmware/qcom/qcom_scm.h> +#include <linux/mm.h> + +#include "qcomtee.h" + +/** + * DOC: Memory and Mapping Objects + * + * QTEE uses memory objects for memory sharing with Linux. + * A memory object can be a standard dma_buf or a contiguous memory range, + * e.g., tee_shm. A memory object should support one operation: map. When + * invoked by QTEE, a mapping object is generated. A mapping object supports + * one operation: unmap. + * + * (1) To map a memory object, QTEE invokes the primordial object with + * %QCOMTEE_OBJECT_OP_MAP_REGION operation; see + * qcomtee_primordial_obj_dispatch(). + * (2) To unmap a memory object, QTEE releases the mapping object which + * calls qcomtee_mem_object_release(). + * + * The map operation is implemented in the primordial object as a privileged + * operation instead of qcomtee_mem_object_dispatch(). Otherwise, on + * platforms without shm_bridge, a user can trick QTEE into writing to the + * kernel memory by passing a user object as a memory object and returning a + * random physical address as the result of the mapping request. + */ + +struct qcomtee_mem_object { + struct qcomtee_object object; + struct tee_shm *shm; + /* QTEE requires these felids to be page aligned. */ + phys_addr_t paddr; /* Physical address of range. */ + size_t size; /* Size of the range. */ +}; + +#define to_qcomtee_mem_object(o) \ + container_of((o), struct qcomtee_mem_object, object) + +static struct qcomtee_object_operations qcomtee_mem_object_ops; + +/* Is it a memory object using tee_shm? */ +int is_qcomtee_memobj_object(struct qcomtee_object *object) +{ + return object != NULL_QCOMTEE_OBJECT && + typeof_qcomtee_object(object) == QCOMTEE_OBJECT_TYPE_CB && + object->ops == &qcomtee_mem_object_ops; +} + +static int qcomtee_mem_object_dispatch(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object *object, u32 op, + struct qcomtee_arg *args) +{ + return -EINVAL; +} + +static void qcomtee_mem_object_release(struct qcomtee_object *object) +{ + struct qcomtee_mem_object *mem_object = to_qcomtee_mem_object(object); + + /* Matching get is in qcomtee_memobj_param_to_object(). */ + tee_shm_put(mem_object->shm); + kfree(mem_object); +} + +static struct qcomtee_object_operations qcomtee_mem_object_ops = { + .release = qcomtee_mem_object_release, + .dispatch = qcomtee_mem_object_dispatch, +}; + +/** + * qcomtee_memobj_param_to_object() - OBJREF parameter to &struct qcomtee_object. + * @object: object returned. + * @param: TEE parameter. + * @ctx: context in which the conversion should happen. + * + * @param is an OBJREF with %QCOMTEE_OBJREF_FLAG_MEM flags. + * + * Return: On success return 0 or <0 on failure. + */ +int qcomtee_memobj_param_to_object(struct qcomtee_object **object, + struct tee_param *param, + struct tee_context *ctx) +{ + struct qcomtee_mem_object *mem_object __free(kfree) = NULL; + struct tee_shm *shm; + int err; + + mem_object = kzalloc(sizeof(*mem_object), GFP_KERNEL); + if (!mem_object) + return -ENOMEM; + + shm = tee_shm_get_from_id(ctx, param->u.objref.id); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + /* mem-object wrapping the memref. */ + err = qcomtee_object_user_init(&mem_object->object, + QCOMTEE_OBJECT_TYPE_CB, + &qcomtee_mem_object_ops, "tee-shm-%d", + shm->id); + if (err) { + tee_shm_put(shm); + + return err; + } + + mem_object->paddr = shm->paddr; + mem_object->size = shm->size; + mem_object->shm = shm; + + *object = &no_free_ptr(mem_object)->object; + + return 0; +} + +/* Reverse what qcomtee_memobj_param_to_object() does. */ +int qcomtee_memobj_param_from_object(struct tee_param *param, + struct qcomtee_object *object, + struct tee_context *ctx) +{ + struct qcomtee_mem_object *mem_object; + + mem_object = to_qcomtee_mem_object(object); + /* Sure if the memobj is in a same context it is originated from. */ + if (mem_object->shm->ctx != ctx) + return -EINVAL; + + param->u.objref.id = mem_object->shm->id; + param->u.objref.flags = QCOMTEE_OBJREF_FLAG_MEM; + + /* Passing shm->id to userspace; drop the reference. */ + qcomtee_object_put(object); + + return 0; +} + +/** + * qcomtee_mem_object_map() - Map a memory object. + * @object: memory object. + * @map_object: created mapping object. + * @mem_paddr: physical address of the memory. + * @mem_size: size of the memory. + * @perms: QTEE access permissions. + * + * Return: On success return 0 or <0 on failure. + */ +int qcomtee_mem_object_map(struct qcomtee_object *object, + struct qcomtee_object **map_object, u64 *mem_paddr, + u64 *mem_size, u32 *perms) +{ + struct qcomtee_mem_object *mem_object = to_qcomtee_mem_object(object); + + /* Reuses the memory object as a mapping object by re-sharing it. */ + qcomtee_object_get(&mem_object->object); + + *map_object = &mem_object->object; + *mem_paddr = mem_object->paddr; + *mem_size = mem_object->size; + *perms = QCOM_SCM_PERM_RW; + + return 0; +} diff --git a/drivers/tee/qcomtee/primordial_obj.c b/drivers/tee/qcomtee/primordial_obj.c new file mode 100644 index 000000000000..b6f811e83b11 --- /dev/null +++ b/drivers/tee/qcomtee/primordial_obj.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/delay.h> +#include "qcomtee.h" + +/** + * DOC: Primordial Object + * + * After boot, the kernel provides a static object of type + * %QCOMTEE_OBJECT_TYPE_CB called the primordial object. This object is used + * for native kernel services or privileged operations. + * + * We support: + * - %QCOMTEE_OBJECT_OP_MAP_REGION to map a memory object and return mapping + * object and mapping information (see qcomtee_mem_object_map()). + * - %QCOMTEE_OBJECT_OP_YIELD to yield by the thread running in QTEE. + * - %QCOMTEE_OBJECT_OP_SLEEP to wait for a period of time. + */ + +#define QCOMTEE_OBJECT_OP_MAP_REGION 0 +#define QCOMTEE_OBJECT_OP_YIELD 1 +#define QCOMTEE_OBJECT_OP_SLEEP 2 + +/* Mapping information format as expected by QTEE. */ +struct qcomtee_mapping_info { + u64 paddr; + u64 len; + u32 perms; +} __packed; + +static int +qcomtee_primordial_obj_dispatch(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object *primordial_object_unused, + u32 op, struct qcomtee_arg *args) +{ + struct qcomtee_mapping_info *map_info; + struct qcomtee_object *mem_object; + struct qcomtee_object *map_object; + int err = 0; + + switch (op) { + case QCOMTEE_OBJECT_OP_YIELD: + cond_resched(); + /* No output object. */ + oic->data = NULL; + + break; + case QCOMTEE_OBJECT_OP_SLEEP: + /* Check message format matched QCOMTEE_OBJECT_OP_SLEEP op. */ + if (qcomtee_args_len(args) != 1 || + args[0].type != QCOMTEE_ARG_TYPE_IB || + args[0].b.size < sizeof(u32)) + return -EINVAL; + + msleep(*(u32 *)(args[0].b.addr)); + /* No output object. */ + oic->data = NULL; + + break; + case QCOMTEE_OBJECT_OP_MAP_REGION: + if (qcomtee_args_len(args) != 3 || + args[0].type != QCOMTEE_ARG_TYPE_OB || + args[1].type != QCOMTEE_ARG_TYPE_IO || + args[2].type != QCOMTEE_ARG_TYPE_OO || + args[0].b.size < sizeof(struct qcomtee_mapping_info)) + return -EINVAL; + + map_info = args[0].b.addr; + mem_object = args[1].o; + + qcomtee_mem_object_map(mem_object, &map_object, + &map_info->paddr, &map_info->len, + &map_info->perms); + + args[2].o = map_object; + /* One output object; pass it for cleanup to notify. */ + oic->data = map_object; + + qcomtee_object_put(mem_object); + + break; + default: + err = -EINVAL; + } + + return err; +} + +/* Called after submitting the callback response. */ +static void qcomtee_primordial_obj_notify(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object *unused, + int err) +{ + struct qcomtee_object *object = oic->data; + + /* If err, QTEE did not obtain mapping object. Drop it. */ + if (object && err) + qcomtee_object_put(object); +} + +static struct qcomtee_object_operations qcomtee_primordial_obj_ops = { + .dispatch = qcomtee_primordial_obj_dispatch, + .notify = qcomtee_primordial_obj_notify, +}; + +struct qcomtee_object qcomtee_primordial_object = { + .name = "primordial", + .object_type = QCOMTEE_OBJECT_TYPE_CB, + .ops = &qcomtee_primordial_obj_ops +}; diff --git a/drivers/tee/qcomtee/qcomtee.h b/drivers/tee/qcomtee/qcomtee.h new file mode 100644 index 000000000000..f39bf63fd1c2 --- /dev/null +++ b/drivers/tee/qcomtee/qcomtee.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef QCOMTEE_H +#define QCOMTEE_H + +#include <linux/kobject.h> +#include <linux/tee_core.h> + +#include "qcomtee_msg.h" +#include "qcomtee_object.h" + +/* Flags relating to object reference. */ +#define QCOMTEE_OBJREF_FLAG_TEE BIT(0) +#define QCOMTEE_OBJREF_FLAG_USER BIT(1) +#define QCOMTEE_OBJREF_FLAG_MEM BIT(2) + +/** + * struct qcomtee - Main service struct. + * @teedev: client device. + * @pool: shared memory pool. + * @ctx: driver private context. + * @oic: context to use for the current driver invocation. + * @wq: workqueue for QTEE async operations. + * @xa_local_objects: array of objects exported to QTEE. + * @xa_last_id: next ID to allocate. + * @qtee_version: QTEE version. + */ +struct qcomtee { + struct tee_device *teedev; + struct tee_shm_pool *pool; + struct tee_context *ctx; + struct qcomtee_object_invoke_ctx oic; + struct workqueue_struct *wq; + struct xarray xa_local_objects; + u32 xa_last_id; + u32 qtee_version; +}; + +void qcomtee_fetch_async_reqs(struct qcomtee_object_invoke_ctx *oic); +struct qcomtee_object *qcomtee_idx_erase(struct qcomtee_object_invoke_ctx *oic, + u32 idx); + +struct tee_shm_pool *qcomtee_shm_pool_alloc(void); +void qcomtee_msg_buffers_free(struct qcomtee_object_invoke_ctx *oic); +int qcomtee_msg_buffers_alloc(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_arg *u); + +/** + * qcomtee_object_do_invoke_internal() - Submit an invocation for an object. + * @oic: context to use for the current invocation. + * @object: object being invoked. + * @op: requested operation on the object. + * @u: array of arguments for the current invocation. + * @result: result returned from QTEE. + * + * The caller is responsible for keeping track of the refcount for each + * object, including @object. On return, the caller loses ownership of all + * input objects of type %QCOMTEE_OBJECT_TYPE_CB. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +int qcomtee_object_do_invoke_internal(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object *object, u32 op, + struct qcomtee_arg *u, int *result); + +/** + * struct qcomtee_context_data - Clients' or supplicants' context. + * @qtee_objects_idr: QTEE objects in this context. + * @qtee_lock: mutex for @qtee_objects_idr. + * @reqs_idr: requests in this context that hold ID. + * @reqs_list: FIFO for requests in PROCESSING or QUEUED state. + * @reqs_lock: mutex for @reqs_idr, @reqs_list and request states. + * @req_c: completion used when the supplicant is waiting for requests. + * @released: state of this context. + */ +struct qcomtee_context_data { + struct idr qtee_objects_idr; + /* Synchronize access to @qtee_objects_idr. */ + struct mutex qtee_lock; + + struct idr reqs_idr; + struct list_head reqs_list; + /* Synchronize access to @reqs_idr, @reqs_list and updating requests states. */ + struct mutex reqs_lock; + + struct completion req_c; + + bool released; +}; + +int qcomtee_context_add_qtee_object(struct tee_param *param, + struct qcomtee_object *object, + struct tee_context *ctx); +int qcomtee_context_find_qtee_object(struct qcomtee_object **object, + struct tee_param *param, + struct tee_context *ctx); +void qcomtee_context_del_qtee_object(struct tee_param *param, + struct tee_context *ctx); + +int qcomtee_objref_to_arg(struct qcomtee_arg *arg, struct tee_param *param, + struct tee_context *ctx); +int qcomtee_objref_from_arg(struct tee_param *param, struct qcomtee_arg *arg, + struct tee_context *ctx); + +/* OBJECTS: */ + +/* (1) User Object API. */ + +int is_qcomtee_user_object(struct qcomtee_object *object); +void qcomtee_user_object_set_notify(struct qcomtee_object *object, bool notify); +void qcomtee_requests_destroy(struct qcomtee_context_data *ctxdata); +int qcomtee_user_param_to_object(struct qcomtee_object **object, + struct tee_param *param, + struct tee_context *ctx); +int qcomtee_user_param_from_object(struct tee_param *param, + struct qcomtee_object *object, + struct tee_context *ctx); + +/** + * struct qcomtee_user_object_request_data - Data for user object request. + * @id: ID assigned to the request. + * @object_id: Object ID being invoked by QTEE. + * @op: Requested operation on object. + * @np: Number of parameters in the request. + */ +struct qcomtee_user_object_request_data { + int id; + u64 object_id; + u32 op; + int np; +}; + +int qcomtee_user_object_select(struct tee_context *ctx, + struct tee_param *params, int num_params, + void __user *uaddr, size_t size, + struct qcomtee_user_object_request_data *data); +int qcomtee_user_object_submit(struct tee_context *ctx, + struct tee_param *params, int num_params, + int req_id, int errno); + +/* (2) Primordial Object. */ +extern struct qcomtee_object qcomtee_primordial_object; + +/* (3) Memory Object API. */ + +/* Is it a memory object using tee_shm? */ +int is_qcomtee_memobj_object(struct qcomtee_object *object); + +/** + * qcomtee_memobj_param_to_object() - OBJREF parameter to &struct qcomtee_object. + * @object: object returned. + * @param: TEE parameter. + * @ctx: context in which the conversion should happen. + * + * @param is an OBJREF with %QCOMTEE_OBJREF_FLAG_MEM flags. + * + * Return: On success return 0 or <0 on failure. + */ +int qcomtee_memobj_param_to_object(struct qcomtee_object **object, + struct tee_param *param, + struct tee_context *ctx); + +/* Reverse what qcomtee_memobj_param_to_object() does. */ +int qcomtee_memobj_param_from_object(struct tee_param *param, + struct qcomtee_object *object, + struct tee_context *ctx); + +/** + * qcomtee_mem_object_map() - Map a memory object. + * @object: memory object. + * @map_object: created mapping object. + * @mem_paddr: physical address of the memory. + * @mem_size: size of the memory. + * @perms: QTEE access permissions. + * + * Return: On success return 0 or <0 on failure. + */ +int qcomtee_mem_object_map(struct qcomtee_object *object, + struct qcomtee_object **map_object, u64 *mem_paddr, + u64 *mem_size, u32 *perms); + +#endif /* QCOMTEE_H */ diff --git a/drivers/tee/qcomtee/qcomtee_msg.h b/drivers/tee/qcomtee/qcomtee_msg.h new file mode 100644 index 000000000000..878f70178a5b --- /dev/null +++ b/drivers/tee/qcomtee/qcomtee_msg.h @@ -0,0 +1,304 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef QCOMTEE_MSG_H +#define QCOMTEE_MSG_H + +#include <linux/bitfield.h> + +/** + * DOC: ''Qualcomm TEE'' (QTEE) Transport Message + * + * There are two buffers shared with QTEE: inbound and outbound buffers. + * The inbound buffer is used for direct object invocation, and the outbound + * buffer is used to make a request from QTEE to the kernel; i.e., a callback + * request. + * + * The unused tail of the outbound buffer is also used for sending and + * receiving asynchronous messages. An asynchronous message is independent of + * the current object invocation (i.e., contents of the inbound buffer) or + * callback request (i.e., the head of the outbound buffer); see + * qcomtee_get_async_buffer(). It is used by endpoints (QTEE or kernel) as an + * optimization to reduce the number of context switches between the secure and + * non-secure worlds. + * + * For instance, QTEE never sends an explicit callback request to release an + * object in the kernel. Instead, it sends asynchronous release messages in the + * outbound buffer when QTEE returns from the previous direct object invocation, + * or appends asynchronous release messages after the current callback request. + * + * QTEE supports two types of arguments in a message: buffer and object + * arguments. Depending on the direction of data flow, they could be input + * buffer (IO) to QTEE, output buffer (OB) from QTEE, input object (IO) to QTEE, + * or output object (OO) from QTEE. Object arguments hold object IDs. Buffer + * arguments hold (offset, size) pairs into the inbound or outbound buffers. + * + * QTEE holds an object table for objects it hosts and exposes to the kernel. + * An object ID is an index to the object table in QTEE. + * + * For the direct object invocation message format in the inbound buffer, see + * &struct qcomtee_msg_object_invoke. For the callback request message format + * in the outbound buffer, see &struct qcomtee_msg_callback. For the message + * format for asynchronous messages in the outbound buffer, see + * &struct qcomtee_async_msg_hdr. + */ + +/** + * define QCOMTEE_MSG_OBJECT_NS_BIT - Non-secure bit + * + * Object ID is a globally unique 32-bit number. IDs referencing objects + * in the kernel should have %QCOMTEE_MSG_OBJECT_NS_BIT set. + */ +#define QCOMTEE_MSG_OBJECT_NS_BIT BIT(31) + +/* Static object IDs recognized by QTEE. */ +#define QCOMTEE_MSG_OBJECT_NULL (0U) +#define QCOMTEE_MSG_OBJECT_ROOT (1U) + +/* Definitions from QTEE as part of the transport protocol. */ + +/* qcomtee_msg_arg is an argument as recognized by QTEE. */ +union qcomtee_msg_arg { + struct { + u32 offset; + u32 size; + } b; + u32 o; +}; + +/* BI and BO payloads in QTEE messages should be at 64-bit boundaries. */ +#define qcomtee_msg_offset_align(o) ALIGN((o), sizeof(u64)) + +/* Operations for objects are 32-bit. Transport uses the upper 16 bits. */ +#define QCOMTEE_MSG_OBJECT_OP_MASK GENMASK(15, 0) + +/* Reserved Operation IDs sent to QTEE: */ +/* QCOMTEE_MSG_OBJECT_OP_RELEASE - Reduces the refcount and releases the object. + * QCOMTEE_MSG_OBJECT_OP_RETAIN - Increases the refcount. + * + * These operation IDs are valid for all objects. + */ + +#define QCOMTEE_MSG_OBJECT_OP_RELEASE (QCOMTEE_MSG_OBJECT_OP_MASK - 0) +#define QCOMTEE_MSG_OBJECT_OP_RETAIN (QCOMTEE_MSG_OBJECT_OP_MASK - 1) + +/* Subset of operations supported by QTEE root object. */ + +#define QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS 5 +#define QCOMTEE_ROOT_OP_NOTIFY_DOMAIN_CHANGE 4 +#define QCOMTEE_ROOT_OP_ADCI_ACCEPT 8 +#define QCOMTEE_ROOT_OP_ADCI_SHUTDOWN 9 + +/* Subset of operations supported by client_env object. */ + +#define QCOMTEE_CLIENT_ENV_OPEN 0 + +/* List of available QTEE service UIDs and subset of operations. */ + +#define QCOMTEE_FEATURE_VER_UID 2033 +#define QCOMTEE_FEATURE_VER_OP_GET 0 +/* Get QTEE version number. */ +#define QCOMTEE_FEATURE_VER_OP_GET_QTEE_ID 10 +#define QTEE_VERSION_GET_MAJOR(x) (((x) >> 22) & 0xffU) +#define QTEE_VERSION_GET_MINOR(x) (((x) >> 12) & 0xffU) +#define QTEE_VERSION_GET_PATCH(x) ((x) >> 0 & 0xfffU) + +/* Response types as returned from qcomtee_object_invoke_ctx_invoke(). */ + +/* The message contains a callback request. */ +#define QCOMTEE_RESULT_INBOUND_REQ_NEEDED 3 + +/** + * struct qcomtee_msg_object_invoke - Direct object invocation message. + * @ctx: object ID hosted in QTEE. + * @op: operation for the object. + * @counts: number of different types of arguments in @args. + * @args: array of arguments. + * + * @counts consists of 4 * 4-bit fields. Bits 0 - 3 represent the number of + * input buffers, bits 4 - 7 represent the number of output buffers, + * bits 8 - 11 represent the number of input objects, and bits 12 - 15 + * represent the number of output objects. The remaining bits should be zero. + * + * 15 12 11 8 7 4 3 0 + * +----------------+----------------+----------------+----------------+ + * | #OO objects | #IO objects | #OB buffers | #IB buffers | + * +----------------+----------------+----------------+----------------+ + * + * The maximum number of arguments of each type is defined by + * %QCOMTEE_ARGS_PER_TYPE. + */ +struct qcomtee_msg_object_invoke { + u32 cxt; + u32 op; + u32 counts; + union qcomtee_msg_arg args[]; +}; + +/* Bit masks for the four 4-bit nibbles holding the counts. */ +#define QCOMTEE_MASK_IB GENMASK(3, 0) +#define QCOMTEE_MASK_OB GENMASK(7, 4) +#define QCOMTEE_MASK_IO GENMASK(11, 8) +#define QCOMTEE_MASK_OO GENMASK(15, 12) + +/** + * struct qcomtee_msg_callback - Callback request message. + * @result: result of operation @op on the object referenced by @cxt. + * @cxt: object ID hosted in the kernel. + * @op: operation for the object. + * @counts: number of different types of arguments in @args. + * @args: array of arguments. + * + * For details of @counts, see &qcomtee_msg_object_invoke.counts. + */ +struct qcomtee_msg_callback { + u32 result; + u32 cxt; + u32 op; + u32 counts; + union qcomtee_msg_arg args[]; +}; + +/* Offset in the message for the beginning of the buffer argument's contents. */ +#define qcomtee_msg_buffer_args(t, n) \ + qcomtee_msg_offset_align(struct_size_t(t, args, n)) +/* Pointer to the beginning of a buffer argument's content at an offset. */ +#define qcomtee_msg_offset_to_ptr(m, off) ((void *)&((char *)(m))[(off)]) + +/* Some helpers to manage msg.counts. */ + +static inline unsigned int qcomtee_msg_num_ib(u32 counts) +{ + return FIELD_GET(QCOMTEE_MASK_IB, counts); +} + +static inline unsigned int qcomtee_msg_num_ob(u32 counts) +{ + return FIELD_GET(QCOMTEE_MASK_OB, counts); +} + +static inline unsigned int qcomtee_msg_num_io(u32 counts) +{ + return FIELD_GET(QCOMTEE_MASK_IO, counts); +} + +static inline unsigned int qcomtee_msg_num_oo(u32 counts) +{ + return FIELD_GET(QCOMTEE_MASK_OO, counts); +} + +static inline unsigned int qcomtee_msg_idx_ib(u32 counts) +{ + return 0; +} + +static inline unsigned int qcomtee_msg_idx_ob(u32 counts) +{ + return qcomtee_msg_num_ib(counts); +} + +static inline unsigned int qcomtee_msg_idx_io(u32 counts) +{ + return qcomtee_msg_idx_ob(counts) + qcomtee_msg_num_ob(counts); +} + +static inline unsigned int qcomtee_msg_idx_oo(u32 counts) +{ + return qcomtee_msg_idx_io(counts) + qcomtee_msg_num_io(counts); +} + +#define qcomtee_msg_for_each(i, first, num) \ + for ((i) = (first); (i) < (first) + (num); (i)++) + +#define qcomtee_msg_for_each_input_buffer(i, m) \ + qcomtee_msg_for_each(i, qcomtee_msg_idx_ib((m)->counts), \ + qcomtee_msg_num_ib((m)->counts)) + +#define qcomtee_msg_for_each_output_buffer(i, m) \ + qcomtee_msg_for_each(i, qcomtee_msg_idx_ob((m)->counts), \ + qcomtee_msg_num_ob((m)->counts)) + +#define qcomtee_msg_for_each_input_object(i, m) \ + qcomtee_msg_for_each(i, qcomtee_msg_idx_io((m)->counts), \ + qcomtee_msg_num_io((m)->counts)) + +#define qcomtee_msg_for_each_output_object(i, m) \ + qcomtee_msg_for_each(i, qcomtee_msg_idx_oo((m)->counts), \ + qcomtee_msg_num_oo((m)->counts)) + +/* Sum of arguments in a message. */ +#define qcomtee_msg_args(m) \ + (qcomtee_msg_idx_oo((m)->counts) + qcomtee_msg_num_oo((m)->counts)) + +static inline void qcomtee_msg_init(struct qcomtee_msg_object_invoke *msg, + u32 cxt, u32 op, int in_buffer, + int out_buffer, int in_object, + int out_object) +{ + u32 counts = 0; + + counts |= (in_buffer & 0xfU); + counts |= ((out_buffer - in_buffer) & 0xfU) << 4; + counts |= ((in_object - out_buffer) & 0xfU) << 8; + counts |= ((out_object - in_object) & 0xfU) << 12; + + msg->cxt = cxt; + msg->op = op; + msg->counts = counts; +} + +/* Generic error codes. */ +#define QCOMTEE_MSG_OK 0 /* non-specific success code. */ +#define QCOMTEE_MSG_ERROR 1 /* non-specific error. */ +#define QCOMTEE_MSG_ERROR_INVALID 2 /* unsupported/unrecognized request. */ +#define QCOMTEE_MSG_ERROR_SIZE_IN 3 /* supplied buffer/string too large. */ +#define QCOMTEE_MSG_ERROR_SIZE_OUT 4 /* supplied output buffer too small. */ +#define QCOMTEE_MSG_ERROR_USERBASE 10 /* start of user-defined error range. */ + +/* Transport layer error codes. */ +#define QCOMTEE_MSG_ERROR_DEFUNCT -90 /* object no longer exists. */ +#define QCOMTEE_MSG_ERROR_ABORT -91 /* calling thread must exit. */ +#define QCOMTEE_MSG_ERROR_BADOBJ -92 /* invalid object context. */ +#define QCOMTEE_MSG_ERROR_NOSLOTS -93 /* caller's object table full. */ +#define QCOMTEE_MSG_ERROR_MAXARGS -94 /* too many args. */ +#define QCOMTEE_MSG_ERROR_MAXDATA -95 /* buffers too large. */ +#define QCOMTEE_MSG_ERROR_UNAVAIL -96 /* the request could not be processed. */ +#define QCOMTEE_MSG_ERROR_KMEM -97 /* kernel out of memory. */ +#define QCOMTEE_MSG_ERROR_REMOTE -98 /* local method sent to remote object. */ +#define QCOMTEE_MSG_ERROR_BUSY -99 /* Object is busy. */ +#define QCOMTEE_MSG_ERROR_TIMEOUT -103 /* Call Back Object invocation timed out. */ + +static inline void qcomtee_msg_set_result(struct qcomtee_msg_callback *cb_msg, + int err) +{ + if (!err) { + cb_msg->result = QCOMTEE_MSG_OK; + } else if (err < 0) { + /* If err < 0, then it is a transport error. */ + switch (err) { + case -ENOMEM: + cb_msg->result = QCOMTEE_MSG_ERROR_KMEM; + break; + case -ENODEV: + cb_msg->result = QCOMTEE_MSG_ERROR_DEFUNCT; + break; + case -ENOSPC: + case -EBUSY: + cb_msg->result = QCOMTEE_MSG_ERROR_BUSY; + break; + case -EBADF: + case -EINVAL: + cb_msg->result = QCOMTEE_MSG_ERROR_UNAVAIL; + break; + default: + cb_msg->result = QCOMTEE_MSG_ERROR; + } + } else { + /* If err > 0, then it is user defined error, pass it as is. */ + cb_msg->result = err; + } +} + +#endif /* QCOMTEE_MSG_H */ diff --git a/drivers/tee/qcomtee/qcomtee_object.h b/drivers/tee/qcomtee/qcomtee_object.h new file mode 100644 index 000000000000..5221449be7db --- /dev/null +++ b/drivers/tee/qcomtee/qcomtee_object.h @@ -0,0 +1,316 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef QCOMTEE_OBJECT_H +#define QCOMTEE_OBJECT_H + +#include <linux/completion.h> +#include <linux/kref.h> +#include <linux/slab.h> +#include <linux/workqueue.h> + +struct qcomtee_object; + +/** + * DOC: Overview + * + * qcomtee_object provides object refcounting, ID allocation for objects hosted + * in the kernel, and necessary message marshaling for Qualcomm TEE (QTEE). + * + * To invoke an object in QTEE, the user calls qcomtee_object_do_invoke() + * while passing an instance of &struct qcomtee_object and the requested + * operation + arguments. + * + * After boot, QTEE provides a static object %ROOT_QCOMTEE_OBJECT (type of + * %QCOMTEE_OBJECT_TYPE_ROOT). The root object is invoked to pass the user's + * credentials and obtain other instances of &struct qcomtee_object (type of + * %QCOMTEE_OBJECT_TYPE_TEE) that represent services and TAs in QTEE; + * see &enum qcomtee_object_type. + * + * The objects received from QTEE are refcounted. So the owner of these objects + * can issue qcomtee_object_get() to increase the refcount and pass objects + * to other clients, or issue qcomtee_object_put() to decrease the refcount + * and release the resources in QTEE. + * + * The kernel can host services accessible to QTEE. A driver should embed + * an instance of &struct qcomtee_object in the struct it wants to export to + * QTEE (this is called a callback object). It issues qcomtee_object_user_init() + * to set the dispatch() operation for the callback object and set its type + * to %QCOMTEE_OBJECT_TYPE_CB. + * + * core.c holds an object table for callback objects. An object ID is assigned + * to each callback object, which is an index to the object table. QTEE uses + * these IDs to reference or invoke callback objects. + * + * If QTEE invokes a callback object in the kernel, the dispatch() operation is + * called in the context of the thread that originally called + * qcomtee_object_do_invoke(). + */ + +/** + * enum qcomtee_object_type - Object types. + * @QCOMTEE_OBJECT_TYPE_TEE: object hosted on QTEE. + * @QCOMTEE_OBJECT_TYPE_CB: object hosted on kernel. + * @QCOMTEE_OBJECT_TYPE_ROOT: 'primordial' object. + * @QCOMTEE_OBJECT_TYPE_NULL: NULL object. + * + * The primordial object is used for bootstrapping the IPC connection between + * the kernel and QTEE. It is invoked by the kernel when it wants to get a + * 'client env'. + */ +enum qcomtee_object_type { + QCOMTEE_OBJECT_TYPE_TEE, + QCOMTEE_OBJECT_TYPE_CB, + QCOMTEE_OBJECT_TYPE_ROOT, + QCOMTEE_OBJECT_TYPE_NULL, +}; + +/** + * enum qcomtee_arg_type - Type of QTEE argument. + * @QCOMTEE_ARG_TYPE_INV: invalid type. + * @QCOMTEE_ARG_TYPE_OB: output buffer (OB). + * @QCOMTEE_ARG_TYPE_OO: output object (OO). + * @QCOMTEE_ARG_TYPE_IB: input buffer (IB). + * @QCOMTEE_ARG_TYPE_IO: input object (IO). + * + * Use the invalid type to specify the end of the argument array. + */ +enum qcomtee_arg_type { + QCOMTEE_ARG_TYPE_INV = 0, + QCOMTEE_ARG_TYPE_OB, + QCOMTEE_ARG_TYPE_OO, + QCOMTEE_ARG_TYPE_IB, + QCOMTEE_ARG_TYPE_IO, + QCOMTEE_ARG_TYPE_NR, +}; + +/** + * define QCOMTEE_ARGS_PER_TYPE - Maximum arguments of a specific type. + * + * The QTEE transport protocol limits the maximum number of arguments of + * a specific type (i.e., IB, OB, IO, and OO). + */ +#define QCOMTEE_ARGS_PER_TYPE 16 + +/* Maximum arguments that can fit in a QTEE message, ignoring the type. */ +#define QCOMTEE_ARGS_MAX (QCOMTEE_ARGS_PER_TYPE * (QCOMTEE_ARG_TYPE_NR - 1)) + +struct qcomtee_buffer { + union { + void *addr; + void __user *uaddr; + }; + size_t size; +}; + +/** + * struct qcomtee_arg - Argument for QTEE object invocation. + * @type: type of argument as &enum qcomtee_arg_type. + * @flags: extra flags. + * @b: address and size if the type of argument is a buffer. + * @o: object instance if the type of argument is an object. + * + * &qcomtee_arg.flags only accepts %QCOMTEE_ARG_FLAGS_UADDR for now, which + * states that &qcomtee_arg.b contains a userspace address in uaddr. + */ +struct qcomtee_arg { + enum qcomtee_arg_type type; +/* 'b.uaddr' holds a __user address. */ +#define QCOMTEE_ARG_FLAGS_UADDR BIT(0) + unsigned int flags; + union { + struct qcomtee_buffer b; + struct qcomtee_object *o; + }; +}; + +static inline int qcomtee_args_len(struct qcomtee_arg *args) +{ + int i = 0; + + while (args[i].type != QCOMTEE_ARG_TYPE_INV) + i++; + return i; +} + +/* Context is busy (callback is in progress). */ +#define QCOMTEE_OIC_FLAG_BUSY BIT(1) +/* Context needs to notify the current object. */ +#define QCOMTEE_OIC_FLAG_NOTIFY BIT(2) +/* Context has shared state with QTEE. */ +#define QCOMTEE_OIC_FLAG_SHARED BIT(3) + +/** + * struct qcomtee_object_invoke_ctx - QTEE context for object invocation. + * @ctx: TEE context for this invocation. + * @flags: flags for the invocation context. + * @errno: error code for the invocation. + * @object: current object invoked in this callback context. + * @u: array of arguments for the current invocation (+1 for ending arg). + * @in_msg: inbound buffer shared with QTEE. + * @out_msg: outbound buffer shared with QTEE. + * @in_shm: TEE shm allocated for inbound buffer. + * @out_shm: TEE shm allocated for outbound buffer. + * @data: extra data attached to this context. + */ +struct qcomtee_object_invoke_ctx { + struct tee_context *ctx; + unsigned long flags; + int errno; + + struct qcomtee_object *object; + struct qcomtee_arg u[QCOMTEE_ARGS_MAX + 1]; + + struct qcomtee_buffer in_msg; + struct qcomtee_buffer out_msg; + struct tee_shm *in_shm; + struct tee_shm *out_shm; + + void *data; +}; + +static inline struct qcomtee_object_invoke_ctx * +qcomtee_object_invoke_ctx_alloc(struct tee_context *ctx) +{ + struct qcomtee_object_invoke_ctx *oic; + + oic = kzalloc(sizeof(*oic), GFP_KERNEL); + if (oic) + oic->ctx = ctx; + return oic; +} + +/** + * qcomtee_object_do_invoke() - Submit an invocation for an object. + * @oic: context to use for the current invocation. + * @object: object being invoked. + * @op: requested operation on the object. + * @u: array of arguments for the current invocation. + * @result: result returned from QTEE. + * + * The caller is responsible for keeping track of the refcount for each object, + * including @object. On return, the caller loses ownership of all input + * objects of type %QCOMTEE_OBJECT_TYPE_CB. + * + * @object can be of %QCOMTEE_OBJECT_TYPE_ROOT or %QCOMTEE_OBJECT_TYPE_TEE. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +int qcomtee_object_do_invoke(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object *object, u32 op, + struct qcomtee_arg *u, int *result); + +/** + * struct qcomtee_object_operations - Callback object operations. + * @release: release the object if QTEE is not using it. + * @dispatch: dispatch the operation requested by QTEE. + * @notify: report the status of any pending response submitted by @dispatch. + */ +struct qcomtee_object_operations { + void (*release)(struct qcomtee_object *object); + int (*dispatch)(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object *object, u32 op, + struct qcomtee_arg *args); + void (*notify)(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object *object, int err); +}; + +/** + * struct qcomtee_object - QTEE or kernel object. + * @name: object name. + * @refcount: reference counter. + * @object_type: object type as &enum qcomtee_object_type. + * @info: extra information for the object. + * @ops: callback operations for objects of type %QCOMTEE_OBJECT_TYPE_CB. + * @work: work for async operations on the object. + * + * @work is used for releasing objects of %QCOMTEE_OBJECT_TYPE_TEE type. + */ +struct qcomtee_object { + const char *name; + struct kref refcount; + + enum qcomtee_object_type object_type; + struct object_info { + unsigned long qtee_id; + /* TEE context for QTEE object async requests. */ + struct tee_context *qcomtee_async_ctx; + } info; + + struct qcomtee_object_operations *ops; + struct work_struct work; +}; + +/* Static instances of qcomtee_object objects. */ +#define NULL_QCOMTEE_OBJECT ((struct qcomtee_object *)(0)) +extern struct qcomtee_object qcomtee_object_root; +#define ROOT_QCOMTEE_OBJECT (&qcomtee_object_root) + +static inline enum qcomtee_object_type +typeof_qcomtee_object(struct qcomtee_object *object) +{ + if (object == NULL_QCOMTEE_OBJECT) + return QCOMTEE_OBJECT_TYPE_NULL; + return object->object_type; +} + +static inline const char *qcomtee_object_name(struct qcomtee_object *object) +{ + if (object == NULL_QCOMTEE_OBJECT) + return "null"; + + if (!object->name) + return "no-name"; + return object->name; +} + +/** + * qcomtee_object_user_init() - Initialize an object for the user. + * @object: object to initialize. + * @ot: type of object as &enum qcomtee_object_type. + * @ops: instance of callbacks. + * @fmt: name assigned to the object. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +int qcomtee_object_user_init(struct qcomtee_object *object, + enum qcomtee_object_type ot, + struct qcomtee_object_operations *ops, + const char *fmt, ...) __printf(4, 5); + +/* Object release is RCU protected. */ +int qcomtee_object_get(struct qcomtee_object *object); +void qcomtee_object_put(struct qcomtee_object *object); + +#define qcomtee_arg_for_each(i, args) \ + for (i = 0; args[i].type != QCOMTEE_ARG_TYPE_INV; i++) + +/* Next argument of type @type after index @i. */ +int qcomtee_next_arg_type(struct qcomtee_arg *u, int i, + enum qcomtee_arg_type type); + +/* Iterate over argument of given type. */ +#define qcomtee_arg_for_each_type(i, args, at) \ + for (i = qcomtee_next_arg_type(args, 0, at); \ + args[i].type != QCOMTEE_ARG_TYPE_INV; \ + i = qcomtee_next_arg_type(args, i + 1, at)) + +#define qcomtee_arg_for_each_input_buffer(i, args) \ + qcomtee_arg_for_each_type(i, args, QCOMTEE_ARG_TYPE_IB) +#define qcomtee_arg_for_each_output_buffer(i, args) \ + qcomtee_arg_for_each_type(i, args, QCOMTEE_ARG_TYPE_OB) +#define qcomtee_arg_for_each_input_object(i, args) \ + qcomtee_arg_for_each_type(i, args, QCOMTEE_ARG_TYPE_IO) +#define qcomtee_arg_for_each_output_object(i, args) \ + qcomtee_arg_for_each_type(i, args, QCOMTEE_ARG_TYPE_OO) + +struct qcomtee_object * +qcomtee_object_get_client_env(struct qcomtee_object_invoke_ctx *oic); + +struct qcomtee_object * +qcomtee_object_get_service(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object *client_env, u32 uid); + +#endif /* QCOMTEE_OBJECT_H */ diff --git a/drivers/tee/qcomtee/shm.c b/drivers/tee/qcomtee/shm.c new file mode 100644 index 000000000000..580bd25f98ed --- /dev/null +++ b/drivers/tee/qcomtee/shm.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/firmware/qcom/qcom_tzmem.h> +#include <linux/mm.h> + +#include "qcomtee.h" + +/** + * define MAX_OUTBOUND_BUFFER_SIZE - Maximum size of outbound buffers. + * + * The size of outbound buffer depends on QTEE callback requests. + */ +#define MAX_OUTBOUND_BUFFER_SIZE SZ_4K + +/** + * define MAX_INBOUND_BUFFER_SIZE - Maximum size of the inbound buffer. + * + * The size of the inbound buffer depends on the user's requests, + * specifically the number of IB and OB arguments. If an invocation + * requires a size larger than %MAX_INBOUND_BUFFER_SIZE, the user should + * consider using another form of shared memory with QTEE. + */ +#define MAX_INBOUND_BUFFER_SIZE SZ_4M + +/** + * qcomtee_msg_buffers_alloc() - Allocate inbound and outbound buffers. + * @oic: context to use for the current invocation. + * @u: array of arguments for the current invocation. + * + * It calculates the size of inbound and outbound buffers based on the + * arguments in @u. It allocates the buffers from the teedev pool. + * + * Return: On success, returns 0. On error, returns < 0. + */ +int qcomtee_msg_buffers_alloc(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_arg *u) +{ + struct tee_context *ctx = oic->ctx; + struct tee_shm *shm; + size_t size; + int i; + + /* Start offset in a message for buffer arguments. */ + size = qcomtee_msg_buffer_args(struct qcomtee_msg_object_invoke, + qcomtee_args_len(u)); + if (size > MAX_INBOUND_BUFFER_SIZE) + return -EINVAL; + + /* Add size of IB arguments. */ + qcomtee_arg_for_each_input_buffer(i, u) { + size = size_add(size, qcomtee_msg_offset_align(u[i].b.size)); + if (size > MAX_INBOUND_BUFFER_SIZE) + return -EINVAL; + } + + /* Add size of OB arguments. */ + qcomtee_arg_for_each_output_buffer(i, u) { + size = size_add(size, qcomtee_msg_offset_align(u[i].b.size)); + if (size > MAX_INBOUND_BUFFER_SIZE) + return -EINVAL; + } + + shm = tee_shm_alloc_priv_buf(ctx, size); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + /* Allocate inbound buffer. */ + oic->in_shm = shm; + shm = tee_shm_alloc_priv_buf(ctx, MAX_OUTBOUND_BUFFER_SIZE); + if (IS_ERR(shm)) { + tee_shm_free(oic->in_shm); + + return PTR_ERR(shm); + } + /* Allocate outbound buffer. */ + oic->out_shm = shm; + + oic->in_msg.addr = tee_shm_get_va(oic->in_shm, 0); + oic->in_msg.size = tee_shm_get_size(oic->in_shm); + oic->out_msg.addr = tee_shm_get_va(oic->out_shm, 0); + oic->out_msg.size = tee_shm_get_size(oic->out_shm); + /* QTEE assume unused buffers are zeroed. */ + memzero_explicit(oic->in_msg.addr, oic->in_msg.size); + memzero_explicit(oic->out_msg.addr, oic->out_msg.size); + + return 0; +} + +void qcomtee_msg_buffers_free(struct qcomtee_object_invoke_ctx *oic) +{ + tee_shm_free(oic->in_shm); + tee_shm_free(oic->out_shm); +} + +/* Dynamic shared memory pool based on tee_dyn_shm_alloc_helper(). */ + +static int qcomtee_shm_register(struct tee_context *ctx, struct tee_shm *shm, + struct page **pages, size_t num_pages, + unsigned long start) +{ + return qcom_tzmem_shm_bridge_create(shm->paddr, shm->size, + &shm->sec_world_id); +} + +static int qcomtee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm) +{ + qcom_tzmem_shm_bridge_delete(shm->sec_world_id); + + return 0; +} + +static int pool_op_alloc(struct tee_shm_pool *pool, struct tee_shm *shm, + size_t size, size_t align) +{ + return tee_dyn_shm_alloc_helper(shm, size, align, qcomtee_shm_register); +} + +static void pool_op_free(struct tee_shm_pool *pool, struct tee_shm *shm) +{ + tee_dyn_shm_free_helper(shm, qcomtee_shm_unregister); +} + +static void pool_op_destroy_pool(struct tee_shm_pool *pool) +{ + kfree(pool); +} + +static const struct tee_shm_pool_ops pool_ops = { + .alloc = pool_op_alloc, + .free = pool_op_free, + .destroy_pool = pool_op_destroy_pool, +}; + +struct tee_shm_pool *qcomtee_shm_pool_alloc(void) +{ + struct tee_shm_pool *pool; + + pool = kzalloc(sizeof(*pool), GFP_KERNEL); + if (!pool) + return ERR_PTR(-ENOMEM); + + pool->ops = &pool_ops; + + return pool; +} diff --git a/drivers/tee/qcomtee/user_obj.c b/drivers/tee/qcomtee/user_obj.c new file mode 100644 index 000000000000..0139905f2684 --- /dev/null +++ b/drivers/tee/qcomtee/user_obj.c @@ -0,0 +1,692 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/slab.h> +#include <linux/uaccess.h> + +#include "qcomtee.h" + +/** + * DOC: User Objects aka Supplicants + * + * Any userspace process with access to the TEE device file can behave as a + * supplicant by creating a user object. Any TEE parameter of type OBJREF with + * %QCOMTEE_OBJREF_FLAG_USER flag set is considered a user object. + * + * A supplicant uses qcomtee_user_object_select() (i.e. TEE_IOC_SUPPL_RECV) to + * receive a QTEE user object request and qcomtee_user_object_submit() + * (i.e. TEE_IOC_SUPPL_SEND) to submit a response. QTEE expects to receive the + * response, including OB and OO in a specific order in the message; parameters + * submitted with qcomtee_user_object_submit() should maintain this order. + */ + +/** + * struct qcomtee_user_object - User object. + * @object: &struct qcomtee_object representing the user object. + * @ctx: context for which the user object is defined. + * @object_id: object ID in @ctx. + * @notify: notify on release. + * + * Any object managed in userspace is represented by this struct. + * If @notify is set, a notification message is sent back to userspace + * upon release. + */ +struct qcomtee_user_object { + struct qcomtee_object object; + struct tee_context *ctx; + u64 object_id; + bool notify; +}; + +#define to_qcomtee_user_object(o) \ + container_of((o), struct qcomtee_user_object, object) + +static struct qcomtee_object_operations qcomtee_user_object_ops; + +/* Is it a user object? */ +int is_qcomtee_user_object(struct qcomtee_object *object) +{ + return object != NULL_QCOMTEE_OBJECT && + typeof_qcomtee_object(object) == QCOMTEE_OBJECT_TYPE_CB && + object->ops == &qcomtee_user_object_ops; +} + +/* Set the user object's 'notify on release' flag. */ +void qcomtee_user_object_set_notify(struct qcomtee_object *object, bool notify) +{ + if (is_qcomtee_user_object(object)) + to_qcomtee_user_object(object)->notify = notify; +} + +/* Supplicant Requests: */ + +/** + * enum qcomtee_req_state - Current state of request. + * @QCOMTEE_REQ_QUEUED: Request is waiting for supplicant. + * @QCOMTEE_REQ_PROCESSING: Request has been picked by the supplicant. + * @QCOMTEE_REQ_PROCESSED: Response has been submitted for the request. + */ +enum qcomtee_req_state { + QCOMTEE_REQ_QUEUED = 1, + QCOMTEE_REQ_PROCESSING, + QCOMTEE_REQ_PROCESSED, +}; + +/* User requests sent to supplicants. */ +struct qcomtee_ureq { + enum qcomtee_req_state state; + + /* User Request: */ + int req_id; + u64 object_id; + u32 op; + struct qcomtee_arg *args; + int errno; + + struct list_head node; + struct completion c; /* Completion for whoever wait. */ +}; + +/* + * Placeholder for a PROCESSING request in qcomtee_context.reqs_idr. + * + * If the thread that calls qcomtee_object_invoke() dies and the supplicant + * is processing the request, replace the entry in qcomtee_context.reqs_idr + * with empty_ureq. This ensures that (1) the req_id remains busy and is not + * reused, and (2) the supplicant fails to submit the response and performs + * the necessary rollback. + */ +static struct qcomtee_ureq empty_ureq = { .state = QCOMTEE_REQ_PROCESSING }; + +/* Enqueue a user request for a context and assign a request ID. */ +static int ureq_enqueue(struct qcomtee_context_data *ctxdata, + struct qcomtee_ureq *ureq) +{ + int ret; + + guard(mutex)(&ctxdata->reqs_lock); + /* Supplicant is dying. */ + if (ctxdata->released) + return -ENODEV; + + /* Allocate an ID and queue the request. */ + ret = idr_alloc(&ctxdata->reqs_idr, ureq, 0, 0, GFP_KERNEL); + if (ret < 0) + return ret; + + ureq->req_id = ret; + ureq->state = QCOMTEE_REQ_QUEUED; + list_add_tail(&ureq->node, &ctxdata->reqs_list); + + return 0; +} + +/** + * ureq_dequeue() - Dequeue a user request from a context. + * @ctxdata: context data for a context to dequeue the request. + * @req_id: ID of the request to be dequeued. + * + * It dequeues a user request and releases its request ID. + * + * Context: The caller should hold &qcomtee_context_data->reqs_lock. + * Return: Returns the user request associated with this ID; otherwise, NULL. + */ +static struct qcomtee_ureq *ureq_dequeue(struct qcomtee_context_data *ctxdata, + int req_id) +{ + struct qcomtee_ureq *ureq; + + ureq = idr_remove(&ctxdata->reqs_idr, req_id); + if (ureq == &empty_ureq || !ureq) + return NULL; + + list_del(&ureq->node); + + return ureq; +} + +/** + * ureq_select() - Select the next request in a context. + * @ctxdata: context data for a context to pop a request. + * @ubuf_size: size of the available buffer for UBUF parameters. + * @num_params: number of entries for the TEE parameter array. + * + * It checks if @num_params is large enough to fit the next request arguments. + * It checks if @ubuf_size is large enough to fit IB buffer arguments. + * + * Context: The caller should hold &qcomtee_context_data->reqs_lock. + * Return: On success, returns a request; + * on failure, returns NULL and ERR_PTR. + */ +static struct qcomtee_ureq *ureq_select(struct qcomtee_context_data *ctxdata, + size_t ubuf_size, int num_params) +{ + struct qcomtee_ureq *req, *ureq = NULL; + struct qcomtee_arg *u; + int i; + + /* Find the a queued request. */ + list_for_each_entry(req, &ctxdata->reqs_list, node) { + if (req->state == QCOMTEE_REQ_QUEUED) { + ureq = req; + break; + } + } + + if (!ureq) + return NULL; + + u = ureq->args; + /* (1) Is there enough TEE parameters? */ + if (num_params < qcomtee_args_len(u)) + return ERR_PTR(-EINVAL); + /* (2) Is there enough space to pass input buffers? */ + qcomtee_arg_for_each_input_buffer(i, u) { + ubuf_size = size_sub(ubuf_size, u[i].b.size); + if (ubuf_size == SIZE_MAX) + return ERR_PTR(-EINVAL); + + ubuf_size = round_down(ubuf_size, 8); + } + + return ureq; +} + +/* Gets called when the user closes the device. */ +void qcomtee_requests_destroy(struct qcomtee_context_data *ctxdata) +{ + struct qcomtee_ureq *req, *ureq; + + guard(mutex)(&ctxdata->reqs_lock); + /* So ureq_enqueue() refuses new requests from QTEE. */ + ctxdata->released = true; + /* ureqs in reqs_list are in QUEUED or PROCESSING (!= empty_ureq) state. */ + list_for_each_entry_safe(ureq, req, &ctxdata->reqs_list, node) { + ureq_dequeue(ctxdata, ureq->req_id); + + if (ureq->op != QCOMTEE_MSG_OBJECT_OP_RELEASE) { + ureq->state = QCOMTEE_REQ_PROCESSED; + ureq->errno = -ENODEV; + + complete(&ureq->c); + } else { + kfree(ureq); + } + } +} + +/* User Object API. */ + +/* User object dispatcher. */ +static int qcomtee_user_object_dispatch(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object *object, u32 op, + struct qcomtee_arg *args) +{ + struct qcomtee_user_object *uo = to_qcomtee_user_object(object); + struct qcomtee_context_data *ctxdata = uo->ctx->data; + struct qcomtee_ureq *ureq __free(kfree) = NULL; + int errno; + + ureq = kzalloc(sizeof(*ureq), GFP_KERNEL); + if (!ureq) + return -ENOMEM; + + init_completion(&ureq->c); + ureq->object_id = uo->object_id; + ureq->op = op; + ureq->args = args; + + /* Queue the request. */ + if (ureq_enqueue(ctxdata, ureq)) + return -ENODEV; + /* Wakeup supplicant to process it. */ + complete(&ctxdata->req_c); + + /* + * Wait for the supplicant to process the request. Wait as KILLABLE + * in case the supplicant and invoke thread are both running from the + * same process, the supplicant crashes, or the shutdown sequence + * starts with supplicant dies first; otherwise, it stuck indefinitely. + * + * If the supplicant processes long-running requests, also use + * TASK_FREEZABLE to allow the device to safely suspend if needed. + */ + if (!wait_for_completion_state(&ureq->c, + TASK_KILLABLE | TASK_FREEZABLE)) { + errno = ureq->errno; + if (!errno) + oic->data = no_free_ptr(ureq); + } else { + enum qcomtee_req_state prev_state; + + errno = -ENODEV; + + scoped_guard(mutex, &ctxdata->reqs_lock) { + prev_state = ureq->state; + /* Replace with empty_ureq to keep req_id reserved. */ + if (prev_state == QCOMTEE_REQ_PROCESSING) { + list_del(&ureq->node); + idr_replace(&ctxdata->reqs_idr, + &empty_ureq, ureq->req_id); + + /* Remove as supplicant has never seen this request. */ + } else if (prev_state == QCOMTEE_REQ_QUEUED) { + ureq_dequeue(ctxdata, ureq->req_id); + } + } + + /* Supplicant did some work, do not discard it. */ + if (prev_state == QCOMTEE_REQ_PROCESSED) { + errno = ureq->errno; + if (!errno) + oic->data = no_free_ptr(ureq); + } + } + + return errno; +} + +/* Gets called after submitting the dispatcher response. */ +static void qcomtee_user_object_notify(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object *unused_object, + int err) +{ + struct qcomtee_ureq *ureq = oic->data; + struct qcomtee_arg *u = ureq->args; + int i; + + /* + * If err, there was a transport issue, and QTEE did not receive the + * response for the dispatcher. Release the callback object created for + * QTEE, in addition to the copies of objects kept for the drivers. + */ + qcomtee_arg_for_each_output_object(i, u) { + if (err && + (typeof_qcomtee_object(u[i].o) == QCOMTEE_OBJECT_TYPE_CB)) + qcomtee_object_put(u[i].o); + qcomtee_object_put(u[i].o); + } + + kfree(ureq); +} + +static void qcomtee_user_object_release(struct qcomtee_object *object) +{ + struct qcomtee_user_object *uo = to_qcomtee_user_object(object); + struct qcomtee_context_data *ctxdata = uo->ctx->data; + struct qcomtee_ureq *ureq; + + /* RELEASE does not require any argument. */ + static struct qcomtee_arg args[] = { { .type = QCOMTEE_ARG_TYPE_INV } }; + + if (!uo->notify) + goto out_no_notify; + + ureq = kzalloc(sizeof(*ureq), GFP_KERNEL); + if (!ureq) + goto out_no_notify; + + /* QUEUE a release request: */ + ureq->object_id = uo->object_id; + ureq->op = QCOMTEE_MSG_OBJECT_OP_RELEASE; + ureq->args = args; + if (ureq_enqueue(ctxdata, ureq)) { + kfree(ureq); + /* Ignore the notification if it cannot be queued. */ + goto out_no_notify; + } + + complete(&ctxdata->req_c); + +out_no_notify: + teedev_ctx_put(uo->ctx); + kfree(uo); +} + +static struct qcomtee_object_operations qcomtee_user_object_ops = { + .release = qcomtee_user_object_release, + .notify = qcomtee_user_object_notify, + .dispatch = qcomtee_user_object_dispatch, +}; + +/** + * qcomtee_user_param_to_object() - OBJREF parameter to &struct qcomtee_object. + * @object: object returned. + * @param: TEE parameter. + * @ctx: context in which the conversion should happen. + * + * @param is an OBJREF with %QCOMTEE_OBJREF_FLAG_USER flags. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +int qcomtee_user_param_to_object(struct qcomtee_object **object, + struct tee_param *param, + struct tee_context *ctx) +{ + struct qcomtee_user_object *user_object __free(kfree) = NULL; + int err; + + user_object = kzalloc(sizeof(*user_object), GFP_KERNEL); + if (!user_object) + return -ENOMEM; + + user_object->ctx = ctx; + user_object->object_id = param->u.objref.id; + /* By default, always notify userspace upon release. */ + user_object->notify = true; + err = qcomtee_object_user_init(&user_object->object, + QCOMTEE_OBJECT_TYPE_CB, + &qcomtee_user_object_ops, "uo-%llu", + param->u.objref.id); + if (err) + return err; + /* Matching teedev_ctx_put() is in qcomtee_user_object_release(). */ + teedev_ctx_get(ctx); + + *object = &no_free_ptr(user_object)->object; + + return 0; +} + +/* Reverse what qcomtee_user_param_to_object() does. */ +int qcomtee_user_param_from_object(struct tee_param *param, + struct qcomtee_object *object, + struct tee_context *ctx) +{ + struct qcomtee_user_object *uo; + + uo = to_qcomtee_user_object(object); + /* Ensure the object is in the same context as the caller. */ + if (uo->ctx != ctx) + return -EINVAL; + + param->u.objref.id = uo->object_id; + param->u.objref.flags = QCOMTEE_OBJREF_FLAG_USER; + + /* User objects are valid in userspace; do not keep a copy. */ + qcomtee_object_put(object); + + return 0; +} + +/** + * qcomtee_cb_params_from_args() - Convert QTEE arguments to TEE parameters. + * @params: TEE parameters. + * @u: QTEE arguments. + * @num_params: number of elements in the parameter array. + * @ubuf_addr: user buffer for arguments of type %QCOMTEE_ARG_TYPE_IB. + * @ubuf_size: size of the user buffer. + * @ctx: context in which the conversion should happen. + * + * It expects @params to have enough entries for @u. Entries in @params are of + * %TEE_IOCTL_PARAM_ATTR_TYPE_NONE. + * + * Return: On success, returns the number of input parameters; + * on failure, returns < 0. + */ +static int qcomtee_cb_params_from_args(struct tee_param *params, + struct qcomtee_arg *u, int num_params, + void __user *ubuf_addr, size_t ubuf_size, + struct tee_context *ctx) +{ + int i, np; + void __user *uaddr; + + qcomtee_arg_for_each(i, u) { + switch (u[i].type) { + case QCOMTEE_ARG_TYPE_IB: + params[i].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT; + + /* Underflow already checked in ureq_select(). */ + ubuf_size = round_down(ubuf_size - u[i].b.size, 8); + uaddr = (void __user *)(ubuf_addr + ubuf_size); + + params[i].u.ubuf.uaddr = uaddr; + params[i].u.ubuf.size = u[i].b.size; + if (copy_to_user(params[i].u.ubuf.uaddr, u[i].b.addr, + u[i].b.size)) + goto out_failed; + + break; + case QCOMTEE_ARG_TYPE_OB: + params[i].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT; + /* Let the user knows the maximum size QTEE expects. */ + params[i].u.ubuf.size = u[i].b.size; + + break; + case QCOMTEE_ARG_TYPE_IO: + params[i].attr = TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT; + if (qcomtee_objref_from_arg(¶ms[i], &u[i], ctx)) + goto out_failed; + + break; + case QCOMTEE_ARG_TYPE_OO: + params[i].attr = + TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT; + + break; + default: /* Never get here! */ + goto out_failed; + } + } + + return i; + +out_failed: + /* Undo qcomtee_objref_from_arg(). */ + for (np = i; np >= 0; np--) { + if (params[np].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT) + qcomtee_context_del_qtee_object(¶ms[np], ctx); + } + + /* Release any IO objects not processed. */ + for (; u[i].type; i++) { + if (u[i].type == QCOMTEE_ARG_TYPE_IO) + qcomtee_object_put(u[i].o); + } + + return -EINVAL; +} + +/** + * qcomtee_cb_params_to_args() - Convert TEE parameters to QTEE arguments. + * @u: QTEE arguments. + * @params: TEE parameters. + * @num_params: number of elements in the parameter array. + * @ctx: context in which the conversion should happen. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +static int qcomtee_cb_params_to_args(struct qcomtee_arg *u, + struct tee_param *params, int num_params, + struct tee_context *ctx) +{ + int i; + + qcomtee_arg_for_each(i, u) { + switch (u[i].type) { + case QCOMTEE_ARG_TYPE_IB: + if (params[i].attr != + TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT) + goto out_failed; + + break; + case QCOMTEE_ARG_TYPE_OB: + if (params[i].attr != + TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT) + goto out_failed; + + /* Client can not send more data than requested. */ + if (params[i].u.ubuf.size > u[i].b.size) + goto out_failed; + + if (copy_from_user(u[i].b.addr, params[i].u.ubuf.uaddr, + params[i].u.ubuf.size)) + goto out_failed; + + u[i].b.size = params[i].u.ubuf.size; + + break; + case QCOMTEE_ARG_TYPE_IO: + if (params[i].attr != + TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT) + goto out_failed; + + break; + case QCOMTEE_ARG_TYPE_OO: + if (params[i].attr != + TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT) + goto out_failed; + + if (qcomtee_objref_to_arg(&u[i], ¶ms[i], ctx)) + goto out_failed; + + break; + default: /* Never get here! */ + goto out_failed; + } + } + + return 0; + +out_failed: + /* Undo qcomtee_objref_to_arg(). */ + for (i--; i >= 0; i--) { + if (u[i].type != QCOMTEE_ARG_TYPE_OO) + continue; + + qcomtee_user_object_set_notify(u[i].o, false); + if (typeof_qcomtee_object(u[i].o) == QCOMTEE_OBJECT_TYPE_CB) + qcomtee_object_put(u[i].o); + + qcomtee_object_put(u[i].o); + } + + return -EINVAL; +} + +/** + * qcomtee_user_object_select() - Select a request for a user object. + * @ctx: context to look for a user object. + * @params: parameters for @op. + * @num_params: number of elements in the parameter array. + * @uaddr: user buffer for output UBUF parameters. + * @size: size of user buffer @uaddr. + * @data: information for the selected request. + * + * @params is filled along with @data for the selected request. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +int qcomtee_user_object_select(struct tee_context *ctx, + struct tee_param *params, int num_params, + void __user *uaddr, size_t size, + struct qcomtee_user_object_request_data *data) +{ + struct qcomtee_context_data *ctxdata = ctx->data; + struct qcomtee_ureq *ureq; + int ret; + + /* + * Hold the reqs_lock not only for ureq_select() and updating the ureq + * state to PROCESSING but for the entire duration of ureq access. + * This prevents qcomtee_user_object_dispatch() from freeing + * ureq while it is still in use, if client dies. + */ + + while (1) { + scoped_guard(mutex, &ctxdata->reqs_lock) { + ureq = ureq_select(ctxdata, size, num_params); + if (!ureq) + goto wait_for_request; + + if (IS_ERR(ureq)) + return PTR_ERR(ureq); + + /* Processing the request 'QUEUED -> PROCESSING'. */ + ureq->state = QCOMTEE_REQ_PROCESSING; + /* ''Prepare user request:'' */ + data->id = ureq->req_id; + data->object_id = ureq->object_id; + data->op = ureq->op; + ret = qcomtee_cb_params_from_args(params, ureq->args, + num_params, uaddr, + size, ctx); + if (ret >= 0) + goto done_request; + + /* Something is wrong with the request: */ + ureq_dequeue(ctxdata, data->id); + /* Send error to QTEE. */ + ureq->state = QCOMTEE_REQ_PROCESSED; + ureq->errno = ret; + + complete(&ureq->c); + } + + continue; +wait_for_request: + /* Wait for a new QUEUED request. */ + if (wait_for_completion_interruptible(&ctxdata->req_c)) + return -ERESTARTSYS; + } + +done_request: + /* No one is waiting for the response. */ + if (data->op == QCOMTEE_MSG_OBJECT_OP_RELEASE) { + scoped_guard(mutex, &ctxdata->reqs_lock) + ureq_dequeue(ctxdata, data->id); + kfree(ureq); + } + + data->np = ret; + + return 0; +} + +/** + * qcomtee_user_object_submit() - Submit a response for a user object. + * @ctx: context to look for a user object. + * @params: returned parameters. + * @num_params: number of elements in the parameter array. + * @req_id: request ID for the response. + * @errno: result of user object invocation. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +int qcomtee_user_object_submit(struct tee_context *ctx, + struct tee_param *params, int num_params, + int req_id, int errno) +{ + struct qcomtee_context_data *ctxdata = ctx->data; + struct qcomtee_ureq *ureq; + + /* See comments for reqs_lock in qcomtee_user_object_select(). */ + guard(mutex)(&ctxdata->reqs_lock); + + ureq = ureq_dequeue(ctxdata, req_id); + if (!ureq) + return -EINVAL; + + ureq->state = QCOMTEE_REQ_PROCESSED; + + if (!errno) + ureq->errno = qcomtee_cb_params_to_args(ureq->args, params, + num_params, ctx); + else + ureq->errno = errno; + /* Return errno if qcomtee_cb_params_to_args() failed; otherwise 0. */ + if (!errno && ureq->errno) + errno = ureq->errno; + else + errno = 0; + + /* Send result to QTEE. */ + complete(&ureq->c); + + return errno; +} diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index acc7998758ad..d65d47cc154e 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -14,7 +14,6 @@ #include <linux/slab.h> #include <linux/tee_core.h> #include <linux/uaccess.h> -#include <crypto/hash.h> #include <crypto/sha1.h> #include "tee_private.h" @@ -80,6 +79,7 @@ void teedev_ctx_get(struct tee_context *ctx) kref_get(&ctx->refcount); } +EXPORT_SYMBOL_GPL(teedev_ctx_get); static void teedev_ctx_release(struct kref *ref) { @@ -97,11 +97,15 @@ void teedev_ctx_put(struct tee_context *ctx) kref_put(&ctx->refcount, teedev_ctx_release); } +EXPORT_SYMBOL_GPL(teedev_ctx_put); void teedev_close_context(struct tee_context *ctx) { struct tee_device *teedev = ctx->teedev; + if (teedev->desc->ops->close_context) + teedev->desc->ops->close_context(ctx); + teedev_ctx_put(ctx); tee_device_put(teedev); } @@ -142,58 +146,22 @@ static int tee_release(struct inode *inode, struct file *filp) * This implements section (for SHA-1): * 4.3. Algorithm for Creating a Name-Based UUID */ -static int uuid_v5(uuid_t *uuid, const uuid_t *ns, const void *name, - size_t size) +static void uuid_v5(uuid_t *uuid, const uuid_t *ns, const void *name, + size_t size) { unsigned char hash[SHA1_DIGEST_SIZE]; - struct crypto_shash *shash = NULL; - struct shash_desc *desc = NULL; - int rc; - - shash = crypto_alloc_shash("sha1", 0, 0); - if (IS_ERR(shash)) { - rc = PTR_ERR(shash); - pr_err("shash(sha1) allocation failed\n"); - return rc; - } - - desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(shash), - GFP_KERNEL); - if (!desc) { - rc = -ENOMEM; - goto out_free_shash; - } - - desc->tfm = shash; - - rc = crypto_shash_init(desc); - if (rc < 0) - goto out_free_desc; + struct sha1_ctx ctx; - rc = crypto_shash_update(desc, (const u8 *)ns, sizeof(*ns)); - if (rc < 0) - goto out_free_desc; - - rc = crypto_shash_update(desc, (const u8 *)name, size); - if (rc < 0) - goto out_free_desc; - - rc = crypto_shash_final(desc, hash); - if (rc < 0) - goto out_free_desc; + sha1_init(&ctx); + sha1_update(&ctx, (const u8 *)ns, sizeof(*ns)); + sha1_update(&ctx, (const u8 *)name, size); + sha1_final(&ctx, hash); memcpy(uuid->b, hash, UUID_SIZE); /* Tag for version 5 */ uuid->b[6] = (hash[6] & 0x0F) | 0x50; uuid->b[8] = (hash[8] & 0x3F) | 0x80; - -out_free_desc: - kfree(desc); - -out_free_shash: - crypto_free_shash(shash); - return rc; } int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, @@ -203,7 +171,7 @@ int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, kgid_t grp = INVALID_GID; char *name = NULL; int name_len; - int rc; + int rc = 0; if (connection_method == TEE_IOCTL_LOGIN_PUBLIC || connection_method == TEE_IOCTL_LOGIN_REE_KERNEL) { @@ -260,7 +228,7 @@ int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, goto out_free_name; } - rc = uuid_v5(uuid, &tee_client_uuid_ns, name, name_len); + uuid_v5(uuid, &tee_client_uuid_ns, name, name_len); out_free_name: kfree(name); @@ -354,6 +322,113 @@ tee_ioctl_shm_register(struct tee_context *ctx, return ret; } +static int +tee_ioctl_shm_register_fd(struct tee_context *ctx, + struct tee_ioctl_shm_register_fd_data __user *udata) +{ + struct tee_ioctl_shm_register_fd_data data; + struct tee_shm *shm; + long ret; + + if (copy_from_user(&data, udata, sizeof(data))) + return -EFAULT; + + /* Currently no input flags are supported */ + if (data.flags) + return -EINVAL; + + shm = tee_shm_register_fd(ctx, data.fd); + if (IS_ERR(shm)) + return -EINVAL; + + data.id = shm->id; + data.flags = shm->flags; + data.size = shm->size; + + if (copy_to_user(udata, &data, sizeof(data))) + ret = -EFAULT; + else + ret = tee_shm_get_fd(shm); + + /* + * When user space closes the file descriptor the shared memory + * should be freed or if tee_shm_get_fd() failed then it will + * be freed immediately. + */ + tee_shm_put(shm); + return ret; +} + +static int param_from_user_memref(struct tee_context *ctx, + struct tee_param_memref *memref, + struct tee_ioctl_param *ip) +{ + struct tee_shm *shm; + size_t offs = 0; + + /* + * If a NULL pointer is passed to a TA in the TEE, + * the ip.c IOCTL parameters is set to TEE_MEMREF_NULL + * indicating a NULL memory reference. + */ + if (ip->c != TEE_MEMREF_NULL) { + /* + * If we fail to get a pointer to a shared + * memory object (and increase the ref count) + * from an identifier we return an error. All + * pointers that has been added in params have + * an increased ref count. It's the callers + * responibility to do tee_shm_put() on all + * resolved pointers. + */ + shm = tee_shm_get_from_id(ctx, ip->c); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + /* + * Ensure offset + size does not overflow + * offset and does not overflow the size of + * the referred shared memory object. + */ + if ((ip->a + ip->b) < ip->a || + (ip->a + ip->b) > shm->size) { + tee_shm_put(shm); + return -EINVAL; + } + + if (shm->flags & TEE_SHM_DMA_BUF) { + struct tee_shm_dmabuf_ref *ref; + + ref = container_of(shm, struct tee_shm_dmabuf_ref, shm); + if (ref->parent_shm) { + /* + * The shm already has one reference to + * ref->parent_shm so we are clear of 0. + * We're getting another reference since + * this shm will be used in the parameter + * list instead of the shm we got with + * tee_shm_get_from_id() above. + */ + refcount_inc(&ref->parent_shm->refcount); + tee_shm_put(shm); + shm = ref->parent_shm; + offs = ref->offset; + } + } + } else if (ctx->cap_memref_null) { + /* Pass NULL pointer to OP-TEE */ + shm = NULL; + } else { + return -EINVAL; + } + + memref->shm_offs = ip->a + offs; + memref->size = ip->b; + memref->shm = shm; + + return 0; +} + static int params_from_user(struct tee_context *ctx, struct tee_param *params, size_t num_params, struct tee_ioctl_param __user *uparams) @@ -361,8 +436,8 @@ static int params_from_user(struct tee_context *ctx, struct tee_param *params, size_t n; for (n = 0; n < num_params; n++) { - struct tee_shm *shm; struct tee_ioctl_param ip; + int rc; if (copy_from_user(&ip, uparams + n, sizeof(ip))) return -EFAULT; @@ -375,6 +450,7 @@ static int params_from_user(struct tee_context *ctx, struct tee_param *params, switch (ip.attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT: break; case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: @@ -382,48 +458,29 @@ static int params_from_user(struct tee_context *ctx, struct tee_param *params, params[n].u.value.b = ip.b; params[n].u.value.c = ip.c; break; + case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INOUT: + params[n].u.ubuf.uaddr = u64_to_user_ptr(ip.a); + params[n].u.ubuf.size = ip.b; + + if (!access_ok(params[n].u.ubuf.uaddr, + params[n].u.ubuf.size)) + return -EFAULT; + + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT: + params[n].u.objref.id = ip.a; + params[n].u.objref.flags = ip.b; + break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: - /* - * If a NULL pointer is passed to a TA in the TEE, - * the ip.c IOCTL parameters is set to TEE_MEMREF_NULL - * indicating a NULL memory reference. - */ - if (ip.c != TEE_MEMREF_NULL) { - /* - * If we fail to get a pointer to a shared - * memory object (and increase the ref count) - * from an identifier we return an error. All - * pointers that has been added in params have - * an increased ref count. It's the callers - * responibility to do tee_shm_put() on all - * resolved pointers. - */ - shm = tee_shm_get_from_id(ctx, ip.c); - if (IS_ERR(shm)) - return PTR_ERR(shm); - - /* - * Ensure offset + size does not overflow - * offset and does not overflow the size of - * the referred shared memory object. - */ - if ((ip.a + ip.b) < ip.a || - (ip.a + ip.b) > shm->size) { - tee_shm_put(shm); - return -EINVAL; - } - } else if (ctx->cap_memref_null) { - /* Pass NULL pointer to OP-TEE */ - shm = NULL; - } else { - return -EINVAL; - } - - params[n].u.memref.shm_offs = ip.a; - params[n].u.memref.size = ip.b; - params[n].u.memref.shm = shm; + rc = param_from_user_memref(ctx, ¶ms[n].u.memref, + &ip); + if (rc) + return rc; break; default: /* Unknown attribute */ @@ -450,6 +507,17 @@ static int params_to_user(struct tee_ioctl_param __user *uparams, put_user(p->u.value.c, &up->c)) return -EFAULT; break; + case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INOUT: + if (put_user((u64)p->u.ubuf.size, &up->b)) + return -EFAULT; + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT: + if (put_user(p->u.objref.id, &up->a) || + put_user(p->u.objref.flags, &up->b)) + return -EFAULT; + break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: if (put_user((u64)p->u.memref.size, &up->b)) @@ -602,6 +670,66 @@ out: return rc; } +static int tee_ioctl_object_invoke(struct tee_context *ctx, + struct tee_ioctl_buf_data __user *ubuf) +{ + int rc; + size_t n; + struct tee_ioctl_buf_data buf; + struct tee_ioctl_object_invoke_arg __user *uarg; + struct tee_ioctl_object_invoke_arg arg; + struct tee_ioctl_param __user *uparams = NULL; + struct tee_param *params = NULL; + + if (!ctx->teedev->desc->ops->object_invoke_func) + return -EINVAL; + + if (copy_from_user(&buf, ubuf, sizeof(buf))) + return -EFAULT; + + if (buf.buf_len > TEE_MAX_ARG_SIZE || + buf.buf_len < sizeof(struct tee_ioctl_object_invoke_arg)) + return -EINVAL; + + uarg = u64_to_user_ptr(buf.buf_ptr); + if (copy_from_user(&arg, uarg, sizeof(arg))) + return -EFAULT; + + if (sizeof(arg) + TEE_IOCTL_PARAM_SIZE(arg.num_params) != buf.buf_len) + return -EINVAL; + + if (arg.num_params) { + params = kcalloc(arg.num_params, sizeof(struct tee_param), + GFP_KERNEL); + if (!params) + return -ENOMEM; + uparams = uarg->params; + rc = params_from_user(ctx, params, arg.num_params, uparams); + if (rc) + goto out; + } + + rc = ctx->teedev->desc->ops->object_invoke_func(ctx, &arg, params); + if (rc) + goto out; + + if (put_user(arg.ret, &uarg->ret)) { + rc = -EFAULT; + goto out; + } + rc = params_to_user(uparams, arg.num_params, params); +out: + if (params) { + /* Decrease ref count for all valid shared memory pointers */ + for (n = 0; n < arg.num_params; n++) + if (tee_param_is_memref(params + n) && + params[n].u.memref.shm) + tee_shm_put(params[n].u.memref.shm); + kfree(params); + } + return rc; +} + static int tee_ioctl_cancel(struct tee_context *ctx, struct tee_ioctl_cancel_arg __user *uarg) { @@ -650,6 +778,19 @@ static int params_to_supp(struct tee_context *ctx, ip.b = p->u.value.b; ip.c = p->u.value.c; break; + case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INOUT: + ip.a = (__force unsigned long)p->u.ubuf.uaddr; + ip.b = p->u.ubuf.size; + ip.c = 0; + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT: + ip.a = p->u.objref.id; + ip.b = p->u.objref.flags; + ip.c = 0; + break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: @@ -752,6 +893,21 @@ static int params_from_supp(struct tee_param *params, size_t num_params, p->u.value.b = ip.b; p->u.value.c = ip.c; break; + case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INOUT: + p->u.ubuf.uaddr = u64_to_user_ptr(ip.a); + p->u.ubuf.size = ip.b; + + if (!access_ok(params[n].u.ubuf.uaddr, + params[n].u.ubuf.size)) + return -EFAULT; + + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT: + p->u.objref.id = ip.a; + p->u.objref.flags = ip.b; + break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: /* @@ -828,10 +984,14 @@ static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return tee_ioctl_shm_alloc(ctx, uarg); case TEE_IOC_SHM_REGISTER: return tee_ioctl_shm_register(ctx, uarg); + case TEE_IOC_SHM_REGISTER_FD: + return tee_ioctl_shm_register_fd(ctx, uarg); case TEE_IOC_OPEN_SESSION: return tee_ioctl_open_session(ctx, uarg); case TEE_IOC_INVOKE: return tee_ioctl_invoke(ctx, uarg); + case TEE_IOC_OBJECT_INVOKE: + return tee_ioctl_object_invoke(ctx, uarg); case TEE_IOC_CANCEL: return tee_ioctl_cancel(ctx, uarg); case TEE_IOC_CLOSE_SESSION: @@ -889,7 +1049,7 @@ struct tee_device *tee_device_alloc(const struct tee_desc *teedesc, if (!teedesc || !teedesc->name || !teedesc->ops || !teedesc->ops->get_version || !teedesc->ops->open || - !teedesc->ops->release || !pool) + !teedesc->ops->release) return ERR_PTR(-EINVAL); teedev = kzalloc(sizeof(*teedev), GFP_KERNEL); @@ -977,7 +1137,7 @@ static ssize_t implementation_id_show(struct device *dev, struct tee_ioctl_version_data vers; teedev->desc->ops->get_version(teedev, &vers); - return scnprintf(buf, PAGE_SIZE, "%d\n", vers.impl_id); + return sysfs_emit(buf, "%d\n", vers.impl_id); } static DEVICE_ATTR_RO(implementation_id); @@ -1038,6 +1198,7 @@ void tee_device_put(struct tee_device *teedev) } mutex_unlock(&teedev->mutex); } +EXPORT_SYMBOL_GPL(tee_device_put); bool tee_device_get(struct tee_device *teedev) { @@ -1050,6 +1211,7 @@ bool tee_device_get(struct tee_device *teedev) mutex_unlock(&teedev->mutex); return true; } +EXPORT_SYMBOL_GPL(tee_device_get); /** * tee_device_unregister() - Removes a TEE device @@ -1064,6 +1226,8 @@ void tee_device_unregister(struct tee_device *teedev) if (!teedev) return; + tee_device_put_all_dma_heaps(teedev); + if (teedev->flags & TEE_DEVICE_FLAG_REGISTERED) cdev_device_del(&teedev->cdev, &teedev->dev); @@ -1287,3 +1451,5 @@ MODULE_AUTHOR("Linaro"); MODULE_DESCRIPTION("TEE Driver"); MODULE_VERSION("1.0"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("DMA_BUF"); +MODULE_IMPORT_NS("DMA_BUF_HEAP"); diff --git a/drivers/tee/tee_heap.c b/drivers/tee/tee_heap.c new file mode 100644 index 000000000000..d8d7735cdffb --- /dev/null +++ b/drivers/tee/tee_heap.c @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2025, Linaro Limited + */ + +#include <linux/dma-buf.h> +#include <linux/dma-heap.h> +#include <linux/genalloc.h> +#include <linux/module.h> +#include <linux/scatterlist.h> +#include <linux/slab.h> +#include <linux/tee_core.h> +#include <linux/xarray.h> + +#include "tee_private.h" + +struct tee_dma_heap { + struct dma_heap *heap; + enum tee_dma_heap_id id; + struct kref kref; + struct tee_protmem_pool *pool; + struct tee_device *teedev; + bool shutting_down; + /* Protects pool, teedev, and shutting_down above */ + struct mutex mu; +}; + +struct tee_heap_buffer { + struct tee_dma_heap *heap; + size_t size; + size_t offs; + struct sg_table table; +}; + +struct tee_heap_attachment { + struct sg_table table; + struct device *dev; +}; + +struct tee_protmem_static_pool { + struct tee_protmem_pool pool; + struct gen_pool *gen_pool; + phys_addr_t pa_base; +}; + +#if IS_ENABLED(CONFIG_TEE_DMABUF_HEAPS) +static DEFINE_XARRAY_ALLOC(tee_dma_heap); + +static void tee_heap_release(struct kref *kref) +{ + struct tee_dma_heap *h = container_of(kref, struct tee_dma_heap, kref); + + h->pool->ops->destroy_pool(h->pool); + tee_device_put(h->teedev); + h->pool = NULL; + h->teedev = NULL; +} + +static void put_tee_heap(struct tee_dma_heap *h) +{ + kref_put(&h->kref, tee_heap_release); +} + +static void get_tee_heap(struct tee_dma_heap *h) +{ + kref_get(&h->kref); +} + +static int copy_sg_table(struct sg_table *dst, struct sg_table *src) +{ + struct scatterlist *dst_sg; + struct scatterlist *src_sg; + int ret; + int i; + + ret = sg_alloc_table(dst, src->orig_nents, GFP_KERNEL); + if (ret) + return ret; + + dst_sg = dst->sgl; + for_each_sgtable_sg(src, src_sg, i) { + sg_set_page(dst_sg, sg_page(src_sg), src_sg->length, + src_sg->offset); + dst_sg = sg_next(dst_sg); + } + + return 0; +} + +static int tee_heap_attach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) +{ + struct tee_heap_buffer *buf = dmabuf->priv; + struct tee_heap_attachment *a; + int ret; + + a = kzalloc(sizeof(*a), GFP_KERNEL); + if (!a) + return -ENOMEM; + + ret = copy_sg_table(&a->table, &buf->table); + if (ret) { + kfree(a); + return ret; + } + + a->dev = attachment->dev; + attachment->priv = a; + + return 0; +} + +static void tee_heap_detach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) +{ + struct tee_heap_attachment *a = attachment->priv; + + sg_free_table(&a->table); + kfree(a); +} + +static struct sg_table * +tee_heap_map_dma_buf(struct dma_buf_attachment *attachment, + enum dma_data_direction direction) +{ + struct tee_heap_attachment *a = attachment->priv; + int ret; + + ret = dma_map_sgtable(attachment->dev, &a->table, direction, + DMA_ATTR_SKIP_CPU_SYNC); + if (ret) + return ERR_PTR(ret); + + return &a->table; +} + +static void tee_heap_unmap_dma_buf(struct dma_buf_attachment *attachment, + struct sg_table *table, + enum dma_data_direction direction) +{ + struct tee_heap_attachment *a = attachment->priv; + + WARN_ON(&a->table != table); + + dma_unmap_sgtable(attachment->dev, table, direction, + DMA_ATTR_SKIP_CPU_SYNC); +} + +static void tee_heap_buf_free(struct dma_buf *dmabuf) +{ + struct tee_heap_buffer *buf = dmabuf->priv; + + buf->heap->pool->ops->free(buf->heap->pool, &buf->table); + mutex_lock(&buf->heap->mu); + put_tee_heap(buf->heap); + mutex_unlock(&buf->heap->mu); + kfree(buf); +} + +static const struct dma_buf_ops tee_heap_buf_ops = { + .attach = tee_heap_attach, + .detach = tee_heap_detach, + .map_dma_buf = tee_heap_map_dma_buf, + .unmap_dma_buf = tee_heap_unmap_dma_buf, + .release = tee_heap_buf_free, +}; + +static struct dma_buf *tee_dma_heap_alloc(struct dma_heap *heap, + unsigned long len, u32 fd_flags, + u64 heap_flags) +{ + struct tee_dma_heap *h = dma_heap_get_drvdata(heap); + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct tee_device *teedev = NULL; + struct tee_heap_buffer *buf; + struct tee_protmem_pool *pool; + struct dma_buf *dmabuf; + int rc; + + mutex_lock(&h->mu); + if (h->teedev) { + teedev = h->teedev; + pool = h->pool; + get_tee_heap(h); + } + mutex_unlock(&h->mu); + + if (!teedev) + return ERR_PTR(-EINVAL); + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) { + dmabuf = ERR_PTR(-ENOMEM); + goto err; + } + buf->size = len; + buf->heap = h; + + rc = pool->ops->alloc(pool, &buf->table, len, &buf->offs); + if (rc) { + dmabuf = ERR_PTR(rc); + goto err_kfree; + } + + exp_info.ops = &tee_heap_buf_ops; + exp_info.size = len; + exp_info.priv = buf; + exp_info.flags = fd_flags; + dmabuf = dma_buf_export(&exp_info); + if (IS_ERR(dmabuf)) + goto err_protmem_free; + + return dmabuf; + +err_protmem_free: + pool->ops->free(pool, &buf->table); +err_kfree: + kfree(buf); +err: + mutex_lock(&h->mu); + put_tee_heap(h); + mutex_unlock(&h->mu); + return dmabuf; +} + +static const struct dma_heap_ops tee_dma_heap_ops = { + .allocate = tee_dma_heap_alloc, +}; + +static const char *heap_id_2_name(enum tee_dma_heap_id id) +{ + switch (id) { + case TEE_DMA_HEAP_SECURE_VIDEO_PLAY: + return "protected,secure-video"; + case TEE_DMA_HEAP_TRUSTED_UI: + return "protected,trusted-ui"; + case TEE_DMA_HEAP_SECURE_VIDEO_RECORD: + return "protected,secure-video-record"; + default: + return NULL; + } +} + +static int alloc_dma_heap(struct tee_device *teedev, enum tee_dma_heap_id id, + struct tee_protmem_pool *pool) +{ + struct dma_heap_export_info exp_info = { + .ops = &tee_dma_heap_ops, + .name = heap_id_2_name(id), + }; + struct tee_dma_heap *h; + int rc; + + if (!exp_info.name) + return -EINVAL; + + if (xa_reserve(&tee_dma_heap, id, GFP_KERNEL)) { + if (!xa_load(&tee_dma_heap, id)) + return -EEXIST; + return -ENOMEM; + } + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return -ENOMEM; + h->id = id; + kref_init(&h->kref); + h->teedev = teedev; + h->pool = pool; + mutex_init(&h->mu); + + exp_info.priv = h; + h->heap = dma_heap_add(&exp_info); + if (IS_ERR(h->heap)) { + rc = PTR_ERR(h->heap); + kfree(h); + + return rc; + } + + /* "can't fail" due to the call to xa_reserve() above */ + return WARN_ON(xa_is_err(xa_store(&tee_dma_heap, id, h, GFP_KERNEL))); +} + +int tee_device_register_dma_heap(struct tee_device *teedev, + enum tee_dma_heap_id id, + struct tee_protmem_pool *pool) +{ + struct tee_dma_heap *h; + int rc; + + if (!tee_device_get(teedev)) + return -EINVAL; + + h = xa_load(&tee_dma_heap, id); + if (h) { + mutex_lock(&h->mu); + if (h->teedev) { + rc = -EBUSY; + } else { + kref_init(&h->kref); + h->shutting_down = false; + h->teedev = teedev; + h->pool = pool; + rc = 0; + } + mutex_unlock(&h->mu); + } else { + rc = alloc_dma_heap(teedev, id, pool); + } + + if (rc) { + tee_device_put(teedev); + dev_err(&teedev->dev, "can't register DMA heap id %d (%s)\n", + id, heap_id_2_name(id)); + } + + return rc; +} +EXPORT_SYMBOL_GPL(tee_device_register_dma_heap); + +void tee_device_put_all_dma_heaps(struct tee_device *teedev) +{ + struct tee_dma_heap *h; + u_long i; + + xa_for_each(&tee_dma_heap, i, h) { + if (h) { + mutex_lock(&h->mu); + if (h->teedev == teedev && !h->shutting_down) { + h->shutting_down = true; + put_tee_heap(h); + } + mutex_unlock(&h->mu); + } + } +} +EXPORT_SYMBOL_GPL(tee_device_put_all_dma_heaps); + +int tee_heap_update_from_dma_buf(struct tee_device *teedev, + struct dma_buf *dmabuf, size_t *offset, + struct tee_shm *shm, + struct tee_shm **parent_shm) +{ + struct tee_heap_buffer *buf; + int rc; + + /* The DMA-buf must be from our heap */ + if (dmabuf->ops != &tee_heap_buf_ops) + return -EINVAL; + + buf = dmabuf->priv; + /* The buffer must be from the same teedev */ + if (buf->heap->teedev != teedev) + return -EINVAL; + + shm->size = buf->size; + + rc = buf->heap->pool->ops->update_shm(buf->heap->pool, &buf->table, + buf->offs, shm, parent_shm); + if (!rc && *parent_shm) + *offset = buf->offs; + + return rc; +} +#else +int tee_device_register_dma_heap(struct tee_device *teedev __always_unused, + enum tee_dma_heap_id id __always_unused, + struct tee_protmem_pool *pool __always_unused) +{ + return -EINVAL; +} +EXPORT_SYMBOL_GPL(tee_device_register_dma_heap); + +void +tee_device_put_all_dma_heaps(struct tee_device *teedev __always_unused) +{ +} +EXPORT_SYMBOL_GPL(tee_device_put_all_dma_heaps); + +int tee_heap_update_from_dma_buf(struct tee_device *teedev __always_unused, + struct dma_buf *dmabuf __always_unused, + size_t *offset __always_unused, + struct tee_shm *shm __always_unused, + struct tee_shm **parent_shm __always_unused) +{ + return -EINVAL; +} +#endif + +static struct tee_protmem_static_pool * +to_protmem_static_pool(struct tee_protmem_pool *pool) +{ + return container_of(pool, struct tee_protmem_static_pool, pool); +} + +static int protmem_pool_op_static_alloc(struct tee_protmem_pool *pool, + struct sg_table *sgt, size_t size, + size_t *offs) +{ + struct tee_protmem_static_pool *stp = to_protmem_static_pool(pool); + phys_addr_t pa; + int ret; + + pa = gen_pool_alloc(stp->gen_pool, size); + if (!pa) + return -ENOMEM; + + ret = sg_alloc_table(sgt, 1, GFP_KERNEL); + if (ret) { + gen_pool_free(stp->gen_pool, pa, size); + return ret; + } + + sg_set_page(sgt->sgl, phys_to_page(pa), size, 0); + *offs = pa - stp->pa_base; + + return 0; +} + +static void protmem_pool_op_static_free(struct tee_protmem_pool *pool, + struct sg_table *sgt) +{ + struct tee_protmem_static_pool *stp = to_protmem_static_pool(pool); + struct scatterlist *sg; + int i; + + for_each_sgtable_sg(sgt, sg, i) + gen_pool_free(stp->gen_pool, sg_phys(sg), sg->length); + sg_free_table(sgt); +} + +static int protmem_pool_op_static_update_shm(struct tee_protmem_pool *pool, + struct sg_table *sgt, size_t offs, + struct tee_shm *shm, + struct tee_shm **parent_shm) +{ + struct tee_protmem_static_pool *stp = to_protmem_static_pool(pool); + + shm->paddr = stp->pa_base + offs; + *parent_shm = NULL; + + return 0; +} + +static void protmem_pool_op_static_destroy_pool(struct tee_protmem_pool *pool) +{ + struct tee_protmem_static_pool *stp = to_protmem_static_pool(pool); + + gen_pool_destroy(stp->gen_pool); + kfree(stp); +} + +static struct tee_protmem_pool_ops protmem_pool_ops_static = { + .alloc = protmem_pool_op_static_alloc, + .free = protmem_pool_op_static_free, + .update_shm = protmem_pool_op_static_update_shm, + .destroy_pool = protmem_pool_op_static_destroy_pool, +}; + +struct tee_protmem_pool *tee_protmem_static_pool_alloc(phys_addr_t paddr, + size_t size) +{ + const size_t page_mask = PAGE_SIZE - 1; + struct tee_protmem_static_pool *stp; + int rc; + + /* Check it's page aligned */ + if ((paddr | size) & page_mask) + return ERR_PTR(-EINVAL); + + if (!pfn_valid(PHYS_PFN(paddr))) + return ERR_PTR(-EINVAL); + + stp = kzalloc(sizeof(*stp), GFP_KERNEL); + if (!stp) + return ERR_PTR(-ENOMEM); + + stp->gen_pool = gen_pool_create(PAGE_SHIFT, -1); + if (!stp->gen_pool) { + rc = -ENOMEM; + goto err_free; + } + + rc = gen_pool_add(stp->gen_pool, paddr, size, -1); + if (rc) + goto err_free_pool; + + stp->pool.ops = &protmem_pool_ops_static; + stp->pa_base = paddr; + return &stp->pool; + +err_free_pool: + gen_pool_destroy(stp->gen_pool); +err_free: + kfree(stp); + + return ERR_PTR(rc); +} +EXPORT_SYMBOL_GPL(tee_protmem_static_pool_alloc); diff --git a/drivers/tee/tee_private.h b/drivers/tee/tee_private.h index 9bc50605227c..6bde688bfcb1 100644 --- a/drivers/tee/tee_private.h +++ b/drivers/tee/tee_private.h @@ -8,20 +8,28 @@ #include <linux/cdev.h> #include <linux/completion.h> #include <linux/device.h> +#include <linux/dma-buf.h> #include <linux/kref.h> #include <linux/mutex.h> #include <linux/types.h> -int tee_shm_get_fd(struct tee_shm *shm); - -bool tee_device_get(struct tee_device *teedev); -void tee_device_put(struct tee_device *teedev); +/* extra references appended to shm object for registered shared memory */ +struct tee_shm_dmabuf_ref { + struct tee_shm shm; + size_t offset; + struct dma_buf *dmabuf; + struct tee_shm *parent_shm; +}; -void teedev_ctx_get(struct tee_context *ctx); -void teedev_ctx_put(struct tee_context *ctx); +int tee_shm_get_fd(struct tee_shm *shm); struct tee_shm *tee_shm_alloc_user_buf(struct tee_context *ctx, size_t size); struct tee_shm *tee_shm_register_user_buf(struct tee_context *ctx, unsigned long addr, size_t length); +int tee_heap_update_from_dma_buf(struct tee_device *teedev, + struct dma_buf *dmabuf, size_t *offset, + struct tee_shm *shm, + struct tee_shm **parent_shm); + #endif /*TEE_PRIVATE_H*/ diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index 2a7d253d9c55..4a47de4bb2e5 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -4,6 +4,9 @@ */ #include <linux/anon_inodes.h> #include <linux/device.h> +#include <linux/dma-buf.h> +#include <linux/dma-mapping.h> +#include <linux/highmem.h> #include <linux/idr.h> #include <linux/io.h> #include <linux/mm.h> @@ -12,9 +15,14 @@ #include <linux/tee_core.h> #include <linux/uaccess.h> #include <linux/uio.h> -#include <linux/highmem.h> #include "tee_private.h" +struct tee_shm_dma_mem { + struct tee_shm shm; + dma_addr_t dma_addr; + struct page *page; +}; + static void shm_put_kernel_pages(struct page **pages, size_t page_count) { size_t n; @@ -45,7 +53,24 @@ static void release_registered_pages(struct tee_shm *shm) static void tee_shm_release(struct tee_device *teedev, struct tee_shm *shm) { - if (shm->flags & TEE_SHM_POOL) { + void *p = shm; + + if (shm->flags & TEE_SHM_DMA_MEM) { +#if IS_ENABLED(CONFIG_TEE_DMABUF_HEAPS) + struct tee_shm_dma_mem *dma_mem; + + dma_mem = container_of(shm, struct tee_shm_dma_mem, shm); + p = dma_mem; + dma_free_pages(&teedev->dev, shm->size, dma_mem->page, + dma_mem->dma_addr, DMA_BIDIRECTIONAL); +#endif + } else if (shm->flags & TEE_SHM_DMA_BUF) { + struct tee_shm_dmabuf_ref *ref; + + ref = container_of(shm, struct tee_shm_dmabuf_ref, shm); + p = ref; + dma_buf_put(ref->dmabuf); + } else if (shm->flags & TEE_SHM_POOL) { teedev->pool->ops->free(teedev->pool, shm); } else if (shm->flags & TEE_SHM_DYNAMIC) { int rc = teedev->desc->ops->shm_unregister(shm->ctx, shm); @@ -59,7 +84,7 @@ static void tee_shm_release(struct tee_device *teedev, struct tee_shm *shm) teedev_ctx_put(shm->ctx); - kfree(shm); + kfree(p); tee_device_put(teedev); } @@ -169,7 +194,7 @@ struct tee_shm *tee_shm_alloc_user_buf(struct tee_context *ctx, size_t size) * tee_client_invoke_func(). The memory allocated is later freed with a * call to tee_shm_free(). * - * @returns a pointer to 'struct tee_shm' + * @returns a pointer to 'struct tee_shm' on success, and ERR_PTR on failure */ struct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size) { @@ -179,6 +204,62 @@ struct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size) } EXPORT_SYMBOL_GPL(tee_shm_alloc_kernel_buf); +struct tee_shm *tee_shm_register_fd(struct tee_context *ctx, int fd) +{ + struct tee_shm_dmabuf_ref *ref; + int rc; + + if (!tee_device_get(ctx->teedev)) + return ERR_PTR(-EINVAL); + + teedev_ctx_get(ctx); + + ref = kzalloc(sizeof(*ref), GFP_KERNEL); + if (!ref) { + rc = -ENOMEM; + goto err_put_tee; + } + + refcount_set(&ref->shm.refcount, 1); + ref->shm.ctx = ctx; + ref->shm.id = -1; + ref->shm.flags = TEE_SHM_DMA_BUF; + + ref->dmabuf = dma_buf_get(fd); + if (IS_ERR(ref->dmabuf)) { + rc = PTR_ERR(ref->dmabuf); + goto err_kfree_ref; + } + + rc = tee_heap_update_from_dma_buf(ctx->teedev, ref->dmabuf, + &ref->offset, &ref->shm, + &ref->parent_shm); + if (rc) + goto err_put_dmabuf; + + mutex_lock(&ref->shm.ctx->teedev->mutex); + ref->shm.id = idr_alloc(&ref->shm.ctx->teedev->idr, &ref->shm, + 1, 0, GFP_KERNEL); + mutex_unlock(&ref->shm.ctx->teedev->mutex); + if (ref->shm.id < 0) { + rc = ref->shm.id; + goto err_put_dmabuf; + } + + return &ref->shm; + +err_put_dmabuf: + dma_buf_put(ref->dmabuf); +err_kfree_ref: + kfree(ref); +err_put_tee: + teedev_ctx_put(ctx); + tee_device_put(ctx->teedev); + + return ERR_PTR(rc); +} +EXPORT_SYMBOL_GPL(tee_shm_register_fd); + /** * tee_shm_alloc_priv_buf() - Allocate shared memory for a privately shared * kernel buffer @@ -203,6 +284,71 @@ struct tee_shm *tee_shm_alloc_priv_buf(struct tee_context *ctx, size_t size) } EXPORT_SYMBOL_GPL(tee_shm_alloc_priv_buf); +#if IS_ENABLED(CONFIG_TEE_DMABUF_HEAPS) +/** + * tee_shm_alloc_dma_mem() - Allocate DMA memory as shared memory object + * @ctx: Context that allocates the shared memory + * @page_count: Number of pages + * + * The allocated memory is expected to be lent (made inaccessible to the + * kernel) to the TEE while it's used and returned (accessible to the + * kernel again) before it's freed. + * + * This function should normally only be used internally in the TEE + * drivers. + * + * @returns a pointer to 'struct tee_shm' + */ +struct tee_shm *tee_shm_alloc_dma_mem(struct tee_context *ctx, + size_t page_count) +{ + struct tee_device *teedev = ctx->teedev; + struct tee_shm_dma_mem *dma_mem; + dma_addr_t dma_addr; + struct page *page; + + if (!tee_device_get(teedev)) + return ERR_PTR(-EINVAL); + + page = dma_alloc_pages(&teedev->dev, page_count * PAGE_SIZE, + &dma_addr, DMA_BIDIRECTIONAL, GFP_KERNEL); + if (!page) + goto err_put_teedev; + + dma_mem = kzalloc(sizeof(*dma_mem), GFP_KERNEL); + if (!dma_mem) + goto err_free_pages; + + refcount_set(&dma_mem->shm.refcount, 1); + dma_mem->shm.ctx = ctx; + dma_mem->shm.paddr = page_to_phys(page); + dma_mem->dma_addr = dma_addr; + dma_mem->page = page; + dma_mem->shm.size = page_count * PAGE_SIZE; + dma_mem->shm.flags = TEE_SHM_DMA_MEM; + + teedev_ctx_get(ctx); + + return &dma_mem->shm; + +err_free_pages: + dma_free_pages(&teedev->dev, page_count * PAGE_SIZE, page, dma_addr, + DMA_BIDIRECTIONAL); +err_put_teedev: + tee_device_put(teedev); + + return ERR_PTR(-ENOMEM); +} +EXPORT_SYMBOL_GPL(tee_shm_alloc_dma_mem); +#else +struct tee_shm *tee_shm_alloc_dma_mem(struct tee_context *ctx, + size_t page_count) +{ + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(tee_shm_alloc_dma_mem); +#endif + int tee_dyn_shm_alloc_helper(struct tee_shm *shm, size_t size, size_t align, int (*shm_register)(struct tee_context *ctx, struct tee_shm *shm, @@ -321,6 +467,14 @@ register_shm_helper(struct tee_context *ctx, struct iov_iter *iter, u32 flags, if (unlikely(len <= 0)) { ret = len ? ERR_PTR(len) : ERR_PTR(-ENOMEM); goto err_free_shm_pages; + } else if (DIV_ROUND_UP(len + off, PAGE_SIZE) != num_pages) { + /* + * If we only got a few pages, update to release the + * correct amount below. + */ + shm->num_pages = len / PAGE_SIZE; + ret = ERR_PTR(-ENOMEM); + goto err_put_shm_pages; } /* @@ -444,6 +598,9 @@ static int tee_shm_fop_mmap(struct file *filp, struct vm_area_struct *vma) /* Refuse sharing shared memory provided by application */ if (shm->flags & TEE_SHM_USER_MAPPED) return -EINVAL; + /* Refuse sharing registered DMA_bufs with the application */ + if (shm->flags & TEE_SHM_DMA_BUF) + return -EINVAL; /* check for overflowing the buffer's size */ if (vma->vm_pgoff + vma_pages(vma) > shm->size >> PAGE_SHIFT) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 44427415a80d..724ad4f3cbee 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -128,7 +128,7 @@ config SERIAL_SB1250_DUART_CONSOLE config SERIAL_ATMEL bool "AT91 on-chip serial port support" depends on COMMON_CLK - depends on ARCH_AT91 || ARCH_LAN969X || COMPILE_TEST + depends on ARCH_MICROCHIP || COMPILE_TEST select SERIAL_CORE select SERIAL_MCTRL_GPIO if GPIOLIB select MFD_AT91_USART diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 32ec632fd080..3f5b87c4cc54 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1200,7 +1200,13 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport) int ret; proto = geni_se_read_proto(&port->se); - if (proto != GENI_SE_UART) { + if (proto == GENI_SE_INVALID_PROTO) { + ret = geni_load_se_firmware(&port->se, GENI_SE_UART); + if (ret) { + dev_err(uport->dev, "UART firmware load failed ret: %d\n", ret); + return ret; + } + } else if (proto != GENI_SE_UART) { dev_err(uport->dev, "Invalid FW loaded, proto: %d\n", proto); return -ENXIO; } diff --git a/include/dt-bindings/clock/raspberrypi,rp1-clocks.h b/include/dt-bindings/clock/raspberrypi,rp1-clocks.h index 248efb895f35..7915fb8197bf 100644 --- a/include/dt-bindings/clock/raspberrypi,rp1-clocks.h +++ b/include/dt-bindings/clock/raspberrypi,rp1-clocks.h @@ -58,4 +58,8 @@ #define RP1_PLL_VIDEO_PRI_PH 43 #define RP1_PLL_AUDIO_TERN 44 +/* MIPI clocks managed by the DSI driver */ +#define RP1_CLK_MIPI0_DSI_BYTECLOCK 45 +#define RP1_CLK_MIPI1_DSI_BYTECLOCK 46 + #endif diff --git a/include/dt-bindings/memory/tegra210-mc.h b/include/dt-bindings/memory/tegra210-mc.h index 5e082547f179..881bf78aa8b2 100644 --- a/include/dt-bindings/memory/tegra210-mc.h +++ b/include/dt-bindings/memory/tegra210-mc.h @@ -75,4 +75,78 @@ #define TEGRA210_MC_RESET_ETR 28 #define TEGRA210_MC_RESET_TSECB 29 +#define TEGRA210_MC_PTCR 0 +#define TEGRA210_MC_DISPLAY0A 1 +#define TEGRA210_MC_DISPLAY0AB 2 +#define TEGRA210_MC_DISPLAY0B 3 +#define TEGRA210_MC_DISPLAY0BB 4 +#define TEGRA210_MC_DISPLAY0C 5 +#define TEGRA210_MC_DISPLAY0CB 6 +#define TEGRA210_MC_AFIR 14 +#define TEGRA210_MC_AVPCARM7R 15 +#define TEGRA210_MC_DISPLAYHC 16 +#define TEGRA210_MC_DISPLAYHCB 17 +#define TEGRA210_MC_HDAR 21 +#define TEGRA210_MC_HOST1XDMAR 22 +#define TEGRA210_MC_HOST1XR 23 +#define TEGRA210_MC_NVENCSRD 28 +#define TEGRA210_MC_PPCSAHBDMAR 29 +#define TEGRA210_MC_PPCSAHBSLVR 30 +#define TEGRA210_MC_SATAR 31 +#define TEGRA210_MC_MPCORER 39 +#define TEGRA210_MC_NVENCSWR 43 +#define TEGRA210_MC_AFIW 49 +#define TEGRA210_MC_AVPCARM7W 50 +#define TEGRA210_MC_HDAW 53 +#define TEGRA210_MC_HOST1XW 54 +#define TEGRA210_MC_MPCOREW 57 +#define TEGRA210_MC_PPCSAHBDMAW 59 +#define TEGRA210_MC_PPCSAHBSLVW 60 +#define TEGRA210_MC_SATAW 61 +#define TEGRA210_MC_ISPRA 68 +#define TEGRA210_MC_ISPWA 70 +#define TEGRA210_MC_ISPWB 71 +#define TEGRA210_MC_XUSB_HOSTR 74 +#define TEGRA210_MC_XUSB_HOSTW 75 +#define TEGRA210_MC_XUSB_DEVR 76 +#define TEGRA210_MC_XUSB_DEVW 77 +#define TEGRA210_MC_ISPRAB 78 +#define TEGRA210_MC_ISPWAB 80 +#define TEGRA210_MC_ISPWBB 81 +#define TEGRA210_MC_TSECSRD 84 +#define TEGRA210_MC_TSECSWR 85 +#define TEGRA210_MC_A9AVPSCR 86 +#define TEGRA210_MC_A9AVPSCW 87 +#define TEGRA210_MC_GPUSRD 88 +#define TEGRA210_MC_GPUSWR 89 +#define TEGRA210_MC_DISPLAYT 90 +#define TEGRA210_MC_SDMMCRA 96 +#define TEGRA210_MC_SDMMCRAA 97 +#define TEGRA210_MC_SDMMCR 98 +#define TEGRA210_MC_SDMMCRAB 99 +#define TEGRA210_MC_SDMMCWA 100 +#define TEGRA210_MC_SDMMCWAA 101 +#define TEGRA210_MC_SDMMCW 102 +#define TEGRA210_MC_SDMMCWAB 103 +#define TEGRA210_MC_VICSRD 108 +#define TEGRA210_MC_VICSWR 109 +#define TEGRA210_MC_VIW 114 +#define TEGRA210_MC_DISPLAYD 115 +#define TEGRA210_MC_NVDECSRD 120 +#define TEGRA210_MC_NVDECSWR 121 +#define TEGRA210_MC_APER 122 +#define TEGRA210_MC_APEW 123 +#define TEGRA210_MC_NVJPGRD 126 +#define TEGRA210_MC_NVJPGWR 127 +#define TEGRA210_MC_SESRD 128 +#define TEGRA210_MC_SESWR 129 +#define TEGRA210_MC_AXIAPR 130 +#define TEGRA210_MC_AXIAPW 131 +#define TEGRA210_MC_ETRR 132 +#define TEGRA210_MC_ETRW 133 +#define TEGRA210_MC_TSECSRDB 134 +#define TEGRA210_MC_TSECSWRB 135 +#define TEGRA210_MC_GPUSRD2 136 +#define TEGRA210_MC_GPUSWR2 137 + #endif diff --git a/include/dt-bindings/reset/thead,th1520-reset.h b/include/dt-bindings/reset/thead,th1520-reset.h index 00459f160489..ee799286c175 100644 --- a/include/dt-bindings/reset/thead,th1520-reset.h +++ b/include/dt-bindings/reset/thead,th1520-reset.h @@ -12,5 +12,12 @@ #define TH1520_RESET_ID_NPU 2 #define TH1520_RESET_ID_WDT0 3 #define TH1520_RESET_ID_WDT1 4 +#define TH1520_RESET_ID_DPU_AHB 5 +#define TH1520_RESET_ID_DPU_AXI 6 +#define TH1520_RESET_ID_DPU_CORE 7 +#define TH1520_RESET_ID_DSI0_APB 8 +#define TH1520_RESET_ID_DSI1_APB 9 +#define TH1520_RESET_ID_HDMI 10 +#define TH1520_RESET_ID_HDMI_APB 11 #endif /* _DT_BINDINGS_TH1520_RESET_H */ diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h index 0f667bf1d4d9..a55ca771286b 100644 --- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -175,4 +175,10 @@ static inline int qcom_scm_qseecom_app_send(u32 app_id, #endif /* CONFIG_QCOM_QSEECOM */ +int qcom_scm_qtee_invoke_smc(phys_addr_t inbuf, size_t inbuf_size, + phys_addr_t outbuf, size_t outbuf_size, + u64 *result, u64 *response_type); +int qcom_scm_qtee_callback_response(phys_addr_t buf, size_t buf_size, + u64 *result, u64 *response_type); + #endif diff --git a/include/linux/firmware/qcom/qcom_tzmem.h b/include/linux/firmware/qcom/qcom_tzmem.h index b83b63a0c049..48ac0e5454c7 100644 --- a/include/linux/firmware/qcom/qcom_tzmem.h +++ b/include/linux/firmware/qcom/qcom_tzmem.h @@ -53,4 +53,19 @@ DEFINE_FREE(qcom_tzmem, void *, if (_T) qcom_tzmem_free(_T)) phys_addr_t qcom_tzmem_to_phys(void *ptr); +#if IS_ENABLED(CONFIG_QCOM_TZMEM_MODE_SHMBRIDGE) +int qcom_tzmem_shm_bridge_create(phys_addr_t paddr, size_t size, u64 *handle); +void qcom_tzmem_shm_bridge_delete(u64 handle); +#else +static inline int qcom_tzmem_shm_bridge_create(phys_addr_t paddr, + size_t size, u64 *handle) +{ + return 0; +} + +static inline void qcom_tzmem_shm_bridge_delete(u64 handle) +{ +} +#endif + #endif /* __QCOM_TZMEM */ diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 688466a0e816..aafaac1496b0 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -153,7 +153,7 @@ struct scmi_perf_domain_info { * for a given device * @fast_switch_rate_limit: gets the minimum time (us) required between * successive fast_switching requests - * @power_scale_mw_get: indicates if the power values provided are in milliWatts + * @power_scale_get: indicates if the power values provided are in milliWatts * or in some other (abstract) scale */ struct scmi_perf_proto_ops { diff --git a/include/linux/soc/qcom/geni-se.h b/include/linux/soc/qcom/geni-se.h index 2996a3c28ef3..0a984e2579fe 100644 --- a/include/linux/soc/qcom/geni-se.h +++ b/include/linux/soc/qcom/geni-se.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _LINUX_QCOM_GENI_SE @@ -36,6 +37,7 @@ enum geni_se_protocol_type { GENI_SE_I2C, GENI_SE_I3C, GENI_SE_SPI_SLAVE, + GENI_SE_INVALID_PROTO = 255, }; struct geni_wrapper; @@ -531,5 +533,7 @@ void geni_icc_set_tag(struct geni_se *se, u32 tag); int geni_icc_enable(struct geni_se *se); int geni_icc_disable(struct geni_se *se); + +int geni_load_se_firmware(struct geni_se *se, enum geni_se_protocol_type protocol); #endif #endif diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h index 9e8e60421192..8ea8230579a2 100644 --- a/include/linux/soc/qcom/mdt_loader.h +++ b/include/linux/soc/qcom/mdt_loader.h @@ -24,7 +24,7 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, phys_addr_t *reloc_base); int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, - const char *fw_name, int pas_id, void *mem_region, + const char *fw_name, void *mem_region, phys_addr_t mem_phys, size_t mem_size, phys_addr_t *reloc_base); void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len, @@ -54,9 +54,8 @@ static inline int qcom_mdt_load(struct device *dev, const struct firmware *fw, static inline int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, - const char *fw_name, int pas_id, - void *mem_region, phys_addr_t mem_phys, - size_t mem_size, + const char *fw_name, void *mem_region, + phys_addr_t mem_phys, size_t mem_size, phys_addr_t *reloc_base) { return -ENODEV; diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h index a38494d6b5f4..1f3e5dad6d0d 100644 --- a/include/linux/tee_core.h +++ b/include/linux/tee_core.h @@ -8,9 +8,11 @@ #include <linux/cdev.h> #include <linux/device.h> +#include <linux/dma-buf.h> #include <linux/idr.h> #include <linux/kref.h> #include <linux/list.h> +#include <linux/scatterlist.h> #include <linux/tee.h> #include <linux/tee_drv.h> #include <linux/types.h> @@ -26,10 +28,19 @@ #define TEE_SHM_USER_MAPPED BIT(1) /* Memory mapped in user space */ #define TEE_SHM_POOL BIT(2) /* Memory allocated from pool */ #define TEE_SHM_PRIV BIT(3) /* Memory private to TEE driver */ +#define TEE_SHM_DMA_BUF BIT(4) /* Memory with dma-buf handle */ +#define TEE_SHM_DMA_MEM BIT(5) /* Memory allocated with */ + /* dma_alloc_pages() */ #define TEE_DEVICE_FLAG_REGISTERED 0x1 #define TEE_MAX_DEV_NAME_LEN 32 +enum tee_dma_heap_id { + TEE_DMA_HEAP_SECURE_VIDEO_PLAY = 1, + TEE_DMA_HEAP_TRUSTED_UI, + TEE_DMA_HEAP_SECURE_VIDEO_RECORD, +}; + /** * struct tee_device - TEE Device representation * @name: name of device @@ -65,22 +76,30 @@ struct tee_device { /** * struct tee_driver_ops - driver operations vtable * @get_version: returns version of driver - * @open: called when the device file is opened - * @release: release this open file + * @open: called for a context when the device file is opened + * @close_context: called when the device file is closed + * @release: called to release the context * @open_session: open a new session * @close_session: close a session * @system_session: declare session as a system session * @invoke_func: invoke a trusted function + * @object_invoke_func: invoke a TEE object * @cancel_req: request cancel of an ongoing invoke or open * @supp_recv: called for supplicant to get a command * @supp_send: called for supplicant to send a response * @shm_register: register shared memory buffer in TEE * @shm_unregister: unregister shared memory buffer in TEE + * + * The context given to @open might last longer than the device file if it is + * tied to other resources in the TEE driver. @close_context is called when the + * client closes the device file, even if there are existing references to the + * context. The TEE driver can use @close_context to start cleaning up. */ struct tee_driver_ops { void (*get_version)(struct tee_device *teedev, struct tee_ioctl_version_data *vers); int (*open)(struct tee_context *ctx); + void (*close_context)(struct tee_context *ctx); void (*release)(struct tee_context *ctx); int (*open_session)(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, @@ -90,6 +109,9 @@ struct tee_driver_ops { int (*invoke_func)(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, struct tee_param *param); + int (*object_invoke_func)(struct tee_context *ctx, + struct tee_ioctl_object_invoke_arg *arg, + struct tee_param *param); int (*cancel_req)(struct tee_context *ctx, u32 cancel_id, u32 session); int (*supp_recv)(struct tee_context *ctx, u32 *func, u32 *num_params, struct tee_param *param); @@ -117,6 +139,36 @@ struct tee_desc { }; /** + * struct tee_protmem_pool - protected memory pool + * @ops: operations + * + * This is an abstract interface where this struct is expected to be + * embedded in another struct specific to the implementation. + */ +struct tee_protmem_pool { + const struct tee_protmem_pool_ops *ops; +}; + +/** + * struct tee_protmem_pool_ops - protected memory pool operations + * @alloc: called when allocating protected memory + * @free: called when freeing protected memory + * @update_shm: called when registering a dma-buf to update the @shm + * with physical address of the buffer or to return the + * @parent_shm of the memory pool + * @destroy_pool: called when destroying the pool + */ +struct tee_protmem_pool_ops { + int (*alloc)(struct tee_protmem_pool *pool, struct sg_table *sgt, + size_t size, size_t *offs); + void (*free)(struct tee_protmem_pool *pool, struct sg_table *sgt); + int (*update_shm)(struct tee_protmem_pool *pool, struct sg_table *sgt, + size_t offs, struct tee_shm *shm, + struct tee_shm **parent_shm); + void (*destroy_pool)(struct tee_protmem_pool *pool); +}; + +/** * tee_device_alloc() - Allocate a new struct tee_device instance * @teedesc: Descriptor for this driver * @dev: Parent device for this device @@ -154,6 +206,29 @@ int tee_device_register(struct tee_device *teedev); */ void tee_device_unregister(struct tee_device *teedev); +int tee_device_register_dma_heap(struct tee_device *teedev, + enum tee_dma_heap_id id, + struct tee_protmem_pool *pool); +void tee_device_put_all_dma_heaps(struct tee_device *teedev); + +/** + * tee_device_get() - Increment the user count for a tee_device + * @teedev: Pointer to the tee_device + * + * If tee_device_unregister() has been called and the final user of @teedev + * has already released the device, this function will fail to prevent new users + * from accessing the device during the unregistration process. + * + * Returns: true if @teedev remains valid, otherwise false + */ +bool tee_device_get(struct tee_device *teedev); + +/** + * tee_device_put() - Decrease the user count for a tee_device + * @teedev: pointer to the tee_device + */ +void tee_device_put(struct tee_device *teedev); + /** * tee_device_set_dev_groups() - Set device attribute groups * @teedev: Device to register @@ -230,6 +305,16 @@ static inline void tee_shm_pool_free(struct tee_shm_pool *pool) } /** + * tee_protmem_static_pool_alloc() - Create a protected memory manager + * @paddr: Physical address of start of pool + * @size: Size in bytes of the pool + * + * @returns pointer to a 'struct tee_protmem_pool' or an ERR_PTR on failure. + */ +struct tee_protmem_pool *tee_protmem_static_pool_alloc(phys_addr_t paddr, + size_t size); + +/** * tee_get_drvdata() - Return driver_data pointer * @returns the driver_data pointer supplied to tee_register(). */ @@ -244,6 +329,9 @@ void *tee_get_drvdata(struct tee_device *teedev); */ struct tee_shm *tee_shm_alloc_priv_buf(struct tee_context *ctx, size_t size); +struct tee_shm *tee_shm_alloc_dma_mem(struct tee_context *ctx, + size_t page_count); + int tee_dyn_shm_alloc_helper(struct tee_shm *shm, size_t size, size_t align, int (*shm_register)(struct tee_context *ctx, struct tee_shm *shm, @@ -315,4 +403,25 @@ struct tee_context *teedev_open(struct tee_device *teedev); */ void teedev_close_context(struct tee_context *ctx); +/** + * teedev_ctx_get() - Increment the reference count of a context + * @ctx: Pointer to the context + * + * This function increases the refcount of the context, which is tied to + * resources shared by the same tee_device. During the unregistration process, + * the context may remain valid even after tee_device_unregister() has returned. + * + * Users should ensure that the context's refcount is properly decreased before + * calling tee_device_put(), typically within the context's release() function. + * Alternatively, users can call tee_device_get() and teedev_ctx_get() together + * and release them simultaneously (see shm_alloc_helper()). + */ +void teedev_ctx_get(struct tee_context *ctx); + +/** + * teedev_ctx_put() - Decrease reference count on a context + * @ctx: pointer to the context + */ +void teedev_ctx_put(struct tee_context *ctx); + #endif /*__TEE_CORE_H*/ diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index a54c203000ed..88a6f9697c89 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -82,6 +82,16 @@ struct tee_param_memref { struct tee_shm *shm; }; +struct tee_param_ubuf { + void __user *uaddr; + size_t size; +}; + +struct tee_param_objref { + u64 id; + u64 flags; +}; + struct tee_param_value { u64 a; u64 b; @@ -92,6 +102,8 @@ struct tee_param { u64 attr; union { struct tee_param_memref memref; + struct tee_param_objref objref; + struct tee_param_ubuf ubuf; struct tee_param_value value; } u; }; @@ -117,6 +129,16 @@ struct tee_shm *tee_shm_register_kernel_buf(struct tee_context *ctx, void *addr, size_t length); /** + * tee_shm_register_fd() - Register shared memory from file descriptor + * + * @ctx: Context that allocates the shared memory + * @fd: Shared memory file descriptor reference + * + * @returns a pointer to 'struct tee_shm' on success, and ERR_PTR on failure + */ +struct tee_shm *tee_shm_register_fd(struct tee_context *ctx, int fd); + +/** * tee_shm_free() - Free shared memory * @shm: Handle to shared memory to free */ diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index d0430bee8292..386ad36f1a0a 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -42,14 +42,16 @@ #define TEE_IOC_MAGIC 0xa4 #define TEE_IOC_BASE 0 -#define TEE_MAX_ARG_SIZE 1024 +#define TEE_MAX_ARG_SIZE 4096 #define TEE_GEN_CAP_GP (1 << 0)/* GlobalPlatform compliant TEE */ #define TEE_GEN_CAP_PRIVILEGED (1 << 1)/* Privileged device (for supplicant) */ #define TEE_GEN_CAP_REG_MEM (1 << 2)/* Supports registering shared memory */ #define TEE_GEN_CAP_MEMREF_NULL (1 << 3)/* NULL MemRef support */ +#define TEE_GEN_CAP_OBJREF (1 << 4)/* Supports generic object reference */ -#define TEE_MEMREF_NULL (__u64)(-1) /* NULL MemRef Buffer */ +#define TEE_MEMREF_NULL ((__u64)(-1)) /* NULL MemRef Buffer */ +#define TEE_OBJREF_NULL ((__u64)(-1)) /* NULL ObjRef Object */ /* * TEE Implementation ID @@ -57,6 +59,7 @@ #define TEE_IMPL_ID_OPTEE 1 #define TEE_IMPL_ID_AMDTEE 2 #define TEE_IMPL_ID_TSTEE 3 +#define TEE_IMPL_ID_QTEE 4 /* * OP-TEE specific capabilities @@ -152,6 +155,20 @@ struct tee_ioctl_buf_data { #define TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT 7 /* input and output */ /* + * These defines userspace buffer parameters. + */ +#define TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT 8 +#define TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT 9 +#define TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INOUT 10 /* input and output */ + +/* + * These defines object reference parameters. + */ +#define TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT 11 +#define TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT 12 +#define TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT 13 + +/* * Mask for the type part of the attribute, leaves room for more types */ #define TEE_IOCTL_PARAM_ATTR_TYPE_MASK 0xff @@ -186,14 +203,18 @@ struct tee_ioctl_buf_data { /** * struct tee_ioctl_param - parameter * @attr: attributes - * @a: if a memref, offset into the shared memory object, else a value parameter - * @b: if a memref, size of the buffer, else a value parameter + * @a: if a memref, offset into the shared memory object, + * else if a ubuf, address of the user buffer, + * else if an objref, object identifier, else a value parameter + * @b: if a memref or ubuf, size of the buffer, + * else if objref, flags for the object, else a value parameter * @c: if a memref, shared memory identifier, else a value parameter * - * @attr & TEE_PARAM_ATTR_TYPE_MASK indicates if memref or value is used in - * the union. TEE_PARAM_ATTR_TYPE_VALUE_* indicates value and - * TEE_PARAM_ATTR_TYPE_MEMREF_* indicates memref. TEE_PARAM_ATTR_TYPE_NONE - * indicates that none of the members are used. + * @attr & TEE_PARAM_ATTR_TYPE_MASK indicates if memref, ubuf, or value is + * used in the union. TEE_PARAM_ATTR_TYPE_VALUE_* indicates value, + * TEE_PARAM_ATTR_TYPE_MEMREF_* indicates memref, TEE_PARAM_ATTR_TYPE_UBUF_* + * indicates ubuf, and TEE_PARAM_ATTR_TYPE_OBJREF_* indicates objref. + * TEE_PARAM_ATTR_TYPE_NONE indicates that none of the members are used. * * Shared memory is allocated with TEE_IOC_SHM_ALLOC which returns an * identifier representing the shared memory object. A memref can reference @@ -379,6 +400,37 @@ struct tee_ioctl_shm_register_data { }; /** + * struct tee_ioctl_shm_register_fd_data - Shared memory registering argument + * @fd: [in] File descriptor identifying dmabuf reference + * @size: [out] Size of referenced memory + * @flags: [in] Flags to/from allocation. + * @id: [out] Identifier of the shared memory + * + * The flags field should currently be zero as input. Updated by the call + * with actual flags as defined by TEE_IOCTL_SHM_* above. + * This structure is used as argument for TEE_IOC_SHM_REGISTER_FD below. + */ +struct tee_ioctl_shm_register_fd_data { + __s64 fd; + __u64 size; + __u32 flags; + __s32 id; +}; + +/** + * TEE_IOC_SHM_REGISTER_FD - register a shared memory from a file descriptor + * + * Returns a file descriptor on success or < 0 on failure + * + * The returned file descriptor refers to the shared memory object in the + * kernel. The supplied file deccriptor can be closed if it's not needed + * for other purposes. The shared memory is freed when the descriptor is + * closed. + */ +#define TEE_IOC_SHM_REGISTER_FD _IOWR(TEE_IOC_MAGIC, TEE_IOC_BASE + 8, \ + struct tee_ioctl_shm_register_fd_data) + +/** * TEE_IOC_SHM_REGISTER - Register shared memory argument * * Registers shared memory between the user space process and secure OS. @@ -401,4 +453,23 @@ struct tee_ioctl_shm_register_data { * munmap(): unmaps previously shared memory */ +/** + * struct tee_ioctl_invoke_func_arg - Invokes an object in a Trusted Application + * @id: [in] Object id + * @op: [in] Object operation, specific to the object + * @ret: [out] return value + * @num_params: [in] number of parameters following this struct + */ +struct tee_ioctl_object_invoke_arg { + __u64 id; + __u32 op; + __u32 ret; + __u32 num_params; + /* num_params tells the actual number of element in params */ + struct tee_ioctl_param params[]; +}; + +#define TEE_IOC_OBJECT_INVOKE _IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 10, \ + struct tee_ioctl_buf_data) + #endif /*__TEE_H*/ diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index 5dd24ab90d0f..c4dcb2b54591 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -1191,6 +1191,7 @@ static void apple_mca_remove(struct platform_device *pdev) } static const struct of_device_id apple_mca_of_match[] = { + { .compatible = "apple,t8103-mca", }, { .compatible = "apple,mca", }, {} }; |