mirror of
https://github.com/torvalds/linux.git
synced 2026-03-07 23:04:33 +01:00
soc: driver updates for 7.0
There are are a number of to firmware drivers, in particular the TEE
subsystem:
- a bus callback for TEE firmware that device drivers can register to
- sysfs support for tee firmware information
- minor updates to platform specific TEE drivers for AMD, NXP, Qualcomm
and the generic optee driver
- ARM SCMI firmware refactoring to improve the protocol discover
among other fixes and cleanups
- ARM FF-A firmware interoperability improvements
The reset controller and memory controller subsystems gain support for
additional hardware platforms from Mediatek, Renesas, NXP, Canaan and
SpacemiT.
Most of the other changes are for random drivers/soc code. Among
a number of cleanups and newly added hardware support, including:
- Mediatek MT8196 DVFS power management and mailbox support
- Qualcomm SCM firmware and MDT loader refactoring, as part of
the new Glymur platform support.
- NXP i.MX9 System Manager firmware support for accessing the
syslog
- Minor updates for TI, Renesas, Samsung, Apple, Marvell and AMD
SoCs.
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmmLWqkACgkQmmx57+YA
GNmbPRAAt4qSMkLvVjGkjjuI3phIjEk9oCNBWDNRUtwdTpnP97cbFXI0jVo2vG7X
aKatSjHeqYe66QT1YFW+JXjJiPXQxtlZtquHomcShHQRd/fS34A1ip9l0sR/RbFV
qP8qMBWWlyND0DsUcE7ymRX/j74yOobdcjG/vG48X81wFxxFOHoNSwItRx5zIq+6
3KenZMoP6+uEQk4uq1USpr0mYAoIUPl0opV9UCjVUSzmYdYW2l/pJWlIqFuj8QZ7
X09qQg070GtYMSOwZfPwvSeV01tEZevIDW7ZF6tJuUNiHE8QMOUoNEWXj1L7hhOY
PUWfDJSKenzzqhMZzM0Vbds/MtGkG7BtCGjJc6zGTaDtO4ucBZcF3tL2KA5k6kp1
9domNvrAT4HGPC1DeWYYRf5RWe63253ev+UDFgEHIwdxj5jE9q9YrnRNvuKvTHcZ
FTYIiBakSMwnjVQt5x9Cm3F1JuQ7LpIN2Fkrq3AY3m85+mHnjdSWVN8jCB9Ko3xX
91ONHnxVyC2bU47yVmLXugWO1MhNeNKuZFKcgqlGImeBixEhT43P8j9b3NeMTVyA
P5iVMZw9om/3XtF5hVHz5ABHlOcFCY5QcKsSJZ9PMyPnjUyzg7Ige7pkZZknNJnb
wcYDQr/sEn83WU6uRRLm3qDbWmGR5tYFcgYYa9U4e8tsO7ZXaqw=
=rUYH
-----END PGP SIGNATURE-----
Merge tag 'soc-drivers-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
Pull SoC driver updates from Arnd Bergmann:
"There are are a number of to firmware drivers, in particular the TEE
subsystem:
- a bus callback for TEE firmware that device drivers can register to
- sysfs support for tee firmware information
- minor updates to platform specific TEE drivers for AMD, NXP,
Qualcomm and the generic optee driver
- ARM SCMI firmware refactoring to improve the protocol discover
among other fixes and cleanups
- ARM FF-A firmware interoperability improvements
The reset controller and memory controller subsystems gain support for
additional hardware platforms from Mediatek, Renesas, NXP, Canaan and
SpacemiT.
Most of the other changes are for random drivers/soc code. Among a
number of cleanups and newly added hardware support, including:
- Mediatek MT8196 DVFS power management and mailbox support
- Qualcomm SCM firmware and MDT loader refactoring, as part of the
new Glymur platform support.
- NXP i.MX9 System Manager firmware support for accessing the syslog
- Minor updates for TI, Renesas, Samsung, Apple, Marvell and AMD
SoCs"
* tag 'soc-drivers-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (171 commits)
bus: fsl-mc: fix an error handling in fsl_mc_device_add()
reset: spacemit: Add SpacemiT K3 reset driver
reset: spacemit: Extract common K1 reset code
reset: Create subdirectory for SpacemiT drivers
dt-bindings: soc: spacemit: Add K3 reset support and IDs
reset: canaan: k230: drop OF dependency and enable by default
reset: rzg2l-usbphy-ctrl: Add suspend/resume support
reset: rzg2l-usbphy-ctrl: Propagate the return value of regmap_field_update_bits()
reset: gpio: check the return value of gpiod_set_value_cansleep()
reset: imx8mp-audiomix: Support i.MX8ULP SIM LPAV
reset: imx8mp-audiomix: Extend the driver usage
reset: imx8mp-audiomix: Switch to using regmap API
reset: imx8mp-audiomix: Drop unneeded macros
soc: fsl: qe: qe_ports_ic: Consolidate chained IRQ handler install/remove
soc: mediatek: mtk-cmdq: Add mminfra_offset adjustment for DRAM addresses
soc: mediatek: mtk-cmdq: Extend cmdq_pkt_write API for SoCs without subsys ID
soc: mediatek: mtk-cmdq: Add pa_base parsing for hardware without subsys ID support
soc: mediatek: mtk-cmdq: Add cmdq_get_mbox_priv() in cmdq_pkt_create()
mailbox: mtk-cmdq: Add driver data to support for MT8196
mailbox: mtk-cmdq: Add mminfra_offset configuration for DRAM transaction
...
This commit is contained in:
commit
bdbddf72a2
134 changed files with 5125 additions and 1463 deletions
|
|
@ -13,3 +13,13 @@ Description:
|
|||
space if the variable is absent. The primary purpose
|
||||
of this variable is to let systemd know whether
|
||||
tee-supplicant is needed in the early boot with initramfs.
|
||||
|
||||
What: /sys/class/tee/tee{,priv}X/revision
|
||||
Date: Jan 2026
|
||||
KernelVersion: 6.19
|
||||
Contact: op-tee@lists.trustedfirmware.org
|
||||
Description:
|
||||
Read-only revision string reported by the TEE driver. This is
|
||||
for diagnostics only and must not be used to infer feature
|
||||
support. Use TEE_IOC_VERSION for capability and compatibility
|
||||
checks.
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ description: |
|
|||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,glymur-llcc
|
||||
- qcom,ipq5424-llcc
|
||||
- qcom,kaanapali-llcc
|
||||
- qcom,qcs615-llcc
|
||||
|
|
@ -46,11 +47,11 @@ properties:
|
|||
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 10
|
||||
maxItems: 14
|
||||
|
||||
reg-names:
|
||||
minItems: 1
|
||||
maxItems: 10
|
||||
maxItems: 14
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
|
@ -84,6 +85,47 @@ allOf:
|
|||
items:
|
||||
- const: llcc0_base
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,glymur-llcc
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- description: LLCC0 base register region
|
||||
- description: LLCC1 base register region
|
||||
- description: LLCC2 base register region
|
||||
- description: LLCC3 base register region
|
||||
- description: LLCC4 base register region
|
||||
- description: LLCC5 base register region
|
||||
- description: LLCC6 base register region
|
||||
- description: LLCC7 base register region
|
||||
- description: LLCC8 base register region
|
||||
- description: LLCC9 base register region
|
||||
- description: LLCC10 base register region
|
||||
- description: LLCC11 base register region
|
||||
- description: LLCC broadcast base register region
|
||||
- description: LLCC broadcast AND register region
|
||||
reg-names:
|
||||
items:
|
||||
- const: llcc0_base
|
||||
- const: llcc1_base
|
||||
- const: llcc2_base
|
||||
- const: llcc3_base
|
||||
- const: llcc4_base
|
||||
- const: llcc5_base
|
||||
- const: llcc6_base
|
||||
- const: llcc7_base
|
||||
- const: llcc8_base
|
||||
- const: llcc9_base
|
||||
- const: llcc10_base
|
||||
- const: llcc11_base
|
||||
- const: llcc_broadcast_base
|
||||
- const: llcc_broadcast_and_base
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ properties:
|
|||
- qcom,ipq5424-trng
|
||||
- qcom,ipq9574-trng
|
||||
- qcom,kaanapali-trng
|
||||
- qcom,milos-trng
|
||||
- qcom,qcs615-trng
|
||||
- qcom,qcs8300-trng
|
||||
- qcom,sa8255p-trng
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/interrupt-controller/fsl,qe-ports-ic.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Freescale QUICC Engine I/O Ports Interrupt Controller
|
||||
|
||||
maintainers:
|
||||
- Christophe Leroy (CS GROUP) <chleroy@kernel.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- fsl,mpc8323-qe-ports-ic
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
'#address-cells':
|
||||
const: 0
|
||||
|
||||
'#interrupt-cells':
|
||||
const: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupt-controller
|
||||
- '#address-cells'
|
||||
- '#interrupt-cells'
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
interrupt-controller@c00 {
|
||||
compatible = "fsl,mpc8323-qe-ports-ic";
|
||||
reg = <0xc00 0x18>;
|
||||
interrupt-controller;
|
||||
#address-cells = <0>;
|
||||
#interrupt-cells = <1>;
|
||||
interrupts = <74 0x8>;
|
||||
interrupt-parent = <&ipic>;
|
||||
};
|
||||
|
|
@ -27,6 +27,8 @@ properties:
|
|||
items:
|
||||
- enum:
|
||||
- qcom,glymur-pdc
|
||||
- qcom,kaanapali-pdc
|
||||
- qcom,milos-pdc
|
||||
- qcom,qcs615-pdc
|
||||
- qcom,qcs8300-pdc
|
||||
- qcom,qdu1000-pdc
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/memory-controllers/ddr/jedec,ddr4.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: DDR4 SDRAM compliant to JEDEC JESD79-4D
|
||||
|
||||
maintainers:
|
||||
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||
|
||||
allOf:
|
||||
- $ref: jedec,sdram-props.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- pattern: "^ddr4-[0-9a-f]{4},[a-z]{1,20}-[0-9a-f]{2}$"
|
||||
- const: jedec,ddr4
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- density
|
||||
- io-width
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
ddr {
|
||||
compatible = "ddr4-00ff,azaz-ff", "jedec,ddr4";
|
||||
density = <8192>;
|
||||
io-width = <8>;
|
||||
};
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/memory-controllers/ddr/jedec,lpddr-props.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Common properties for LPDDR types
|
||||
|
||||
description:
|
||||
Different LPDDR types generally use the same properties and only differ in the
|
||||
range of legal values for each. This file defines the common parts that can be
|
||||
reused for each type. Nodes using this schema should generally be nested under
|
||||
an LPDDR channel node.
|
||||
|
||||
maintainers:
|
||||
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
description:
|
||||
Compatible strings can be either explicit vendor names and part numbers
|
||||
(e.g. elpida,ECB240ABACN), or generated strings of the form
|
||||
lpddrX-YY,ZZZZ where X is the LPDDR version, YY is the manufacturer ID
|
||||
(from MR5) and ZZZZ is the revision ID (from MR6 and MR7). Both IDs are
|
||||
formatted in lower case hexadecimal representation with leading zeroes.
|
||||
The latter form can be useful when LPDDR nodes are created at runtime by
|
||||
boot firmware that doesn't have access to static part number information.
|
||||
|
||||
reg:
|
||||
description:
|
||||
The rank number of this LPDDR rank when used as a subnode to an LPDDR
|
||||
channel.
|
||||
minimum: 0
|
||||
maximum: 3
|
||||
|
||||
revision-id:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
description:
|
||||
Revision IDs read from Mode Register 6 and 7. One byte per uint32 cell (i.e. <MR6 MR7>).
|
||||
maxItems: 2
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 255
|
||||
|
||||
density:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
Density in megabits of SDRAM chip. Decoded from Mode Register 8.
|
||||
enum:
|
||||
- 64
|
||||
- 128
|
||||
- 256
|
||||
- 512
|
||||
- 1024
|
||||
- 2048
|
||||
- 3072
|
||||
- 4096
|
||||
- 6144
|
||||
- 8192
|
||||
- 12288
|
||||
- 16384
|
||||
- 24576
|
||||
- 32768
|
||||
|
||||
io-width:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
IO bus width in bits of SDRAM chip. Decoded from Mode Register 8.
|
||||
enum:
|
||||
- 8
|
||||
- 16
|
||||
- 32
|
||||
|
||||
additionalProperties: true
|
||||
|
|
@ -10,7 +10,7 @@ maintainers:
|
|||
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||
|
||||
allOf:
|
||||
- $ref: jedec,lpddr-props.yaml#
|
||||
- $ref: jedec,sdram-props.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ maintainers:
|
|||
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||
|
||||
allOf:
|
||||
- $ref: jedec,lpddr-props.yaml#
|
||||
- $ref: jedec,sdram-props.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ maintainers:
|
|||
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||
|
||||
allOf:
|
||||
- $ref: jedec,lpddr-props.yaml#
|
||||
- $ref: jedec,sdram-props.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ maintainers:
|
|||
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||
|
||||
allOf:
|
||||
- $ref: jedec,lpddr-props.yaml#
|
||||
- $ref: jedec,sdram-props.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
|
|
|||
|
|
@ -1,23 +1,28 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/memory-controllers/ddr/jedec,lpddr-channel.yaml#
|
||||
$id: http://devicetree.org/schemas/memory-controllers/ddr/jedec,sdram-channel.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: LPDDR channel with chip/rank topology description
|
||||
title: SDRAM channel with chip/rank topology description
|
||||
|
||||
description:
|
||||
An LPDDR channel is a completely independent set of LPDDR pins (DQ, CA, CS,
|
||||
CK, etc.) that connect one or more LPDDR chips to a host system. The main
|
||||
purpose of this node is to overall LPDDR topology of the system, including the
|
||||
amount of individual LPDDR chips and the ranks per chip.
|
||||
A memory channel of SDRAM memory like DDR SDRAM or LPDDR SDRAM is a completely
|
||||
independent set of pins (DQ, CA, CS, CK, etc.) that connect one or more memory
|
||||
chips to a host system. The main purpose of this node is to overall memory
|
||||
topology of the system, including the amount of individual memory chips and
|
||||
the ranks per chip.
|
||||
|
||||
maintainers:
|
||||
- Julius Werner <jwerner@chromium.org>
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "sdram-channel-[0-9]+$"
|
||||
|
||||
compatible:
|
||||
enum:
|
||||
- jedec,ddr4-channel
|
||||
- jedec,lpddr2-channel
|
||||
- jedec,lpddr3-channel
|
||||
- jedec,lpddr4-channel
|
||||
|
|
@ -26,14 +31,14 @@ properties:
|
|||
io-width:
|
||||
description:
|
||||
The number of DQ pins in the channel. If this number is different
|
||||
from (a multiple of) the io-width of the LPDDR chip, that means that
|
||||
from (a multiple of) the io-width of the SDRAM chip, that means that
|
||||
multiple instances of that type of chip are wired in parallel on this
|
||||
channel (with the channel's DQ pins split up between the different
|
||||
chips, and the CA, CS, etc. pins of the different chips all shorted
|
||||
together). This means that the total physical memory controlled by a
|
||||
channel is equal to the sum of the densities of each rank on the
|
||||
connected LPDDR chip, times the io-width of the channel divided by
|
||||
the io-width of the LPDDR chip.
|
||||
connected SDRAM chip, times the io-width of the channel divided by
|
||||
the io-width of the SDRAM chip.
|
||||
enum:
|
||||
- 8
|
||||
- 16
|
||||
|
|
@ -51,8 +56,8 @@ patternProperties:
|
|||
"^rank@[0-9]+$":
|
||||
type: object
|
||||
description:
|
||||
Each physical LPDDR chip may have one or more ranks. Ranks are
|
||||
internal but fully independent sub-units of the chip. Each LPDDR bus
|
||||
Each physical SDRAM chip may have one or more ranks. Ranks are
|
||||
internal but fully independent sub-units of the chip. Each SDRAM bus
|
||||
transaction on the channel targets exactly one rank, based on the
|
||||
state of the CS pins. Different ranks may have different densities and
|
||||
timing requirements.
|
||||
|
|
@ -60,6 +65,15 @@ patternProperties:
|
|||
- reg
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: jedec,ddr4-channel
|
||||
then:
|
||||
patternProperties:
|
||||
"^rank@[0-9]+$":
|
||||
$ref: /schemas/memory-controllers/ddr/jedec,ddr4.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
|
@ -107,7 +121,7 @@ additionalProperties: false
|
|||
|
||||
examples:
|
||||
- |
|
||||
lpddr-channel0 {
|
||||
sdram-channel-0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "jedec,lpddr3-channel";
|
||||
|
|
@ -122,7 +136,7 @@ examples:
|
|||
};
|
||||
};
|
||||
|
||||
lpddr-channel1 {
|
||||
sdram-channel-1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "jedec,lpddr4-channel";
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/memory-controllers/ddr/jedec,sdram-props.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Common properties for SDRAM types
|
||||
|
||||
description:
|
||||
Different SDRAM types generally use the same properties and only differ in the
|
||||
range of legal values for each. This file defines the common parts that can be
|
||||
reused for each type. Nodes using this schema should generally be nested under
|
||||
a SDRAM channel node.
|
||||
|
||||
maintainers:
|
||||
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
description: |
|
||||
Compatible strings can be either explicit vendor names and part numbers
|
||||
(e.g. elpida,ECB240ABACN), or generated strings of the form
|
||||
lpddrX-YY,ZZZZ or ddrX-YYYY,AAAA...-ZZ where X, Y, and Z are lowercase
|
||||
hexadecimal with leading zeroes, and A is lowercase ASCII.
|
||||
For LPDDR and DDR SDRAM, X is the SDRAM version (2, 3, 4, etc.).
|
||||
For LPDDR SDRAM:
|
||||
- YY is the manufacturer ID (from MR5), 1 byte
|
||||
- ZZZZ is the revision ID (from MR6 and MR7), 2 bytes
|
||||
For DDR4 SDRAM with SPD, according to JEDEC SPD4.1.2.L-6:
|
||||
- YYYY is the manufacturer ID, 2 bytes, from bytes 320 and 321
|
||||
- AAAA... is the part number, 20 bytes (20 chars) from bytes 329 to 348
|
||||
without trailing spaces
|
||||
- ZZ is the revision ID, 1 byte, from byte 349
|
||||
The former form is useful when the SDRAM vendor and part number are
|
||||
known, for example, when memory is soldered on the board. The latter
|
||||
form is useful when SDRAM nodes are created at runtime by boot firmware
|
||||
that doesn't have access to static part number information.
|
||||
|
||||
reg:
|
||||
description:
|
||||
The rank number of this memory rank when used as a subnode to an memory
|
||||
channel.
|
||||
minimum: 0
|
||||
maximum: 3
|
||||
|
||||
revision-id:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
description: |
|
||||
SDRAM revision ID:
|
||||
- LPDDR SDRAM, decoded from Mode Registers 6 and 7, always 2 bytes.
|
||||
- DDR4 SDRAM, decoded from the SPD from byte 349 according to
|
||||
JEDEC SPD4.1.2.L-6, always 1 byte.
|
||||
One byte per uint32 cell (e.g., <MR6 MR7>).
|
||||
maxItems: 2
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 255
|
||||
|
||||
density:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: |
|
||||
Density of the SDRAM chip in megabits:
|
||||
- LPDDR SDRAM, decoded from Mode Register 8.
|
||||
- DDR4 SDRAM, decoded from the SPD from bits 3-0 of byte 4 according to
|
||||
JEDEC SPD4.1.2.L-6.
|
||||
enum:
|
||||
- 64
|
||||
- 128
|
||||
- 256
|
||||
- 512
|
||||
- 1024
|
||||
- 2048
|
||||
- 3072
|
||||
- 4096
|
||||
- 6144
|
||||
- 8192
|
||||
- 12288
|
||||
- 16384
|
||||
- 24576
|
||||
- 32768
|
||||
|
||||
io-width:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: |
|
||||
I/O bus width in bits of the SDRAM chip:
|
||||
- LPDDR SDRAM, decoded from Mode Register 8.
|
||||
- DDR4 SDRAM, decoded from the SPD from bits 2-0 of byte 12 according to
|
||||
JEDEC SPD4.1.2.L-6.
|
||||
enum:
|
||||
- 8
|
||||
- 16
|
||||
- 32
|
||||
|
||||
additionalProperties: true
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/nvmem/google,gs101-otp.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Google GS101 OTP Controller
|
||||
|
||||
maintainers:
|
||||
- Tudor Ambarus <tudor.ambarus@linaro.org>
|
||||
|
||||
description: |
|
||||
OTP controller drives a NVMEM memory where system or user specific data
|
||||
can be stored. The OTP controller register space is of interest as well
|
||||
because it contains dedicated registers where it stores the Product ID
|
||||
and the Chip ID (apart other things like TMU or ASV info).
|
||||
|
||||
allOf:
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: google,gs101-otp
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: pclk
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/google,gs101.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
efuse@10000000 {
|
||||
compatible = "google,gs101-otp";
|
||||
reg = <0x10000000 0xf084>;
|
||||
clocks = <&cmu_misc CLK_GOUT_MISC_OTP_CON_TOP_PCLK>;
|
||||
clock-names = "pclk";
|
||||
interrupts = <GIC_SPI 752 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
|
@ -44,6 +44,9 @@ properties:
|
|||
- const: stop-ack
|
||||
- const: shutdown-ack
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ properties:
|
|||
maxItems: 1
|
||||
description: DVFSRC common register address and length.
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Clock that drives the DVFSRC MCU
|
||||
|
||||
regulators:
|
||||
type: object
|
||||
$ref: /schemas/regulator/mediatek,mt6873-dvfsrc-regulator.yaml#
|
||||
|
|
@ -50,6 +54,7 @@ additionalProperties: false
|
|||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/mt8195-clk.h>
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
|
@ -57,6 +62,7 @@ examples:
|
|||
system-controller@10012000 {
|
||||
compatible = "mediatek,mt8195-dvfsrc";
|
||||
reg = <0 0x10012000 0 0x1000>;
|
||||
clocks = <&topckgen CLK_TOP_DVFSRC>;
|
||||
|
||||
regulators {
|
||||
compatible = "mediatek,mt8195-dvfsrc-regulator";
|
||||
|
|
|
|||
|
|
@ -9,28 +9,6 @@ title: Samsung Exynos SoC series Power Management Unit (PMU)
|
|||
maintainers:
|
||||
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||
|
||||
# Custom select to avoid matching all nodes with 'syscon'
|
||||
select:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- google,gs101-pmu
|
||||
- samsung,exynos3250-pmu
|
||||
- samsung,exynos4210-pmu
|
||||
- samsung,exynos4212-pmu
|
||||
- samsung,exynos4412-pmu
|
||||
- samsung,exynos5250-pmu
|
||||
- samsung,exynos5260-pmu
|
||||
- samsung,exynos5410-pmu
|
||||
- samsung,exynos5420-pmu
|
||||
- samsung,exynos5433-pmu
|
||||
- samsung,exynos7-pmu
|
||||
- samsung,exynos850-pmu
|
||||
- samsung-s5pv210-pmu
|
||||
required:
|
||||
- compatible
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
|
|
@ -52,6 +30,7 @@ properties:
|
|||
- const: syscon
|
||||
- items:
|
||||
- enum:
|
||||
- axis,artpec9-pmu
|
||||
- samsung,exynos2200-pmu
|
||||
- samsung,exynos7870-pmu
|
||||
- samsung,exynos7885-pmu
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ maintainers:
|
|||
- Haylen Chu <heylenay@4d2.org>
|
||||
|
||||
description:
|
||||
System controllers found on SpacemiT K1 SoC, which are capable of
|
||||
System controllers found on SpacemiT K1/K3 SoC, which are capable of
|
||||
clock, reset and power-management functions.
|
||||
|
||||
properties:
|
||||
|
|
@ -46,6 +46,12 @@ properties:
|
|||
|
||||
"#reset-cells":
|
||||
const: 1
|
||||
description: |
|
||||
ID of the reset controller line. Valid IDs are defined in corresponding
|
||||
files:
|
||||
|
||||
For SpacemiT K1, see include/dt-bindings/clock/spacemit,k1-syscon.h
|
||||
For SpacemiT K3, see include/dt-bindings/reset/spacemit,k3-resets.h
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ properties:
|
|||
- nvidia,tegra186-sysram
|
||||
- nvidia,tegra194-sysram
|
||||
- nvidia,tegra234-sysram
|
||||
- qcom,kaanapali-imem
|
||||
- qcom,rpm-msg-ram
|
||||
- rockchip,rk3288-pmu-sram
|
||||
|
||||
|
|
@ -89,6 +90,7 @@ patternProperties:
|
|||
- arm,juno-scp-shmem
|
||||
- arm,scmi-shmem
|
||||
- arm,scp-shmem
|
||||
- qcom,pil-reloc-info
|
||||
- renesas,smp-sram
|
||||
- rockchip,rk3066-smp-sram
|
||||
- samsung,exynos4210-sysram
|
||||
|
|
|
|||
|
|
@ -43,24 +43,12 @@ snippet would look like::
|
|||
MODULE_DEVICE_TABLE(tee, client_id_table);
|
||||
|
||||
static struct tee_client_driver client_driver = {
|
||||
.probe = client_probe,
|
||||
.remove = client_remove,
|
||||
.id_table = client_id_table,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.bus = &tee_bus_type,
|
||||
.probe = client_probe,
|
||||
.remove = client_remove,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init client_init(void)
|
||||
{
|
||||
return driver_register(&client_driver.driver);
|
||||
}
|
||||
|
||||
static void __exit client_exit(void)
|
||||
{
|
||||
driver_unregister(&client_driver.driver);
|
||||
}
|
||||
|
||||
module_init(client_init);
|
||||
module_exit(client_exit);
|
||||
module_tee_client_driver(client_driver);
|
||||
|
|
|
|||
|
|
@ -19545,14 +19545,14 @@ F: drivers/net/phy/ncn*
|
|||
|
||||
OP-TEE DRIVER
|
||||
M: Jens Wiklander <jens.wiklander@linaro.org>
|
||||
L: op-tee@lists.trustedfirmware.org
|
||||
L: op-tee@lists.trustedfirmware.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-optee-devices
|
||||
F: drivers/tee/optee/
|
||||
|
||||
OP-TEE RANDOM NUMBER GENERATOR (RNG) DRIVER
|
||||
M: Sumit Garg <sumit.garg@kernel.org>
|
||||
L: op-tee@lists.trustedfirmware.org
|
||||
L: op-tee@lists.trustedfirmware.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: drivers/char/hw_random/optee-rng.c
|
||||
|
||||
|
|
@ -25677,7 +25677,7 @@ F: include/media/i2c/tw9910.h
|
|||
TEE SUBSYSTEM
|
||||
M: Jens Wiklander <jens.wiklander@linaro.org>
|
||||
R: Sumit Garg <sumit.garg@kernel.org>
|
||||
L: op-tee@lists.trustedfirmware.org
|
||||
L: op-tee@lists.trustedfirmware.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-class-tee
|
||||
F: Documentation/driver-api/tee.rst
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ config OMAP_INTERCONNECT
|
|||
|
||||
config OMAP_OCP2SCP
|
||||
tristate "OMAP OCP2SCP DRIVER"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
||||
help
|
||||
Driver to enable ocp2scp module which transforms ocp interface
|
||||
protocol to scp protocol. In OMAP4, USB PHY is connected via
|
||||
|
|
|
|||
|
|
@ -137,6 +137,35 @@ static int fsl_mc_bus_uevent(const struct device *dev, struct kobj_uevent_env *e
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_mc_probe(struct device *dev)
|
||||
{
|
||||
struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
|
||||
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
|
||||
|
||||
if (mc_drv->probe)
|
||||
return mc_drv->probe(mc_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsl_mc_remove(struct device *dev)
|
||||
{
|
||||
struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
|
||||
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
|
||||
|
||||
if (mc_drv->remove)
|
||||
mc_drv->remove(mc_dev);
|
||||
}
|
||||
|
||||
static void fsl_mc_shutdown(struct device *dev)
|
||||
{
|
||||
struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
|
||||
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
|
||||
|
||||
if (dev->driver && mc_drv->shutdown)
|
||||
mc_drv->shutdown(mc_dev);
|
||||
}
|
||||
|
||||
static int fsl_mc_dma_configure(struct device *dev)
|
||||
{
|
||||
const struct device_driver *drv = READ_ONCE(dev->driver);
|
||||
|
|
@ -202,8 +231,12 @@ static ssize_t driver_override_show(struct device *dev,
|
|||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
|
||||
ssize_t len;
|
||||
|
||||
return sysfs_emit(buf, "%s\n", mc_dev->driver_override);
|
||||
device_lock(dev);
|
||||
len = sysfs_emit(buf, "%s\n", mc_dev->driver_override);
|
||||
device_unlock(dev);
|
||||
return len;
|
||||
}
|
||||
static DEVICE_ATTR_RW(driver_override);
|
||||
|
||||
|
|
@ -314,6 +347,9 @@ const struct bus_type fsl_mc_bus_type = {
|
|||
.name = "fsl-mc",
|
||||
.match = fsl_mc_bus_match,
|
||||
.uevent = fsl_mc_bus_uevent,
|
||||
.probe = fsl_mc_probe,
|
||||
.remove = fsl_mc_remove,
|
||||
.shutdown = fsl_mc_shutdown,
|
||||
.dma_configure = fsl_mc_dma_configure,
|
||||
.dma_cleanup = fsl_mc_dma_cleanup,
|
||||
.dev_groups = fsl_mc_dev_groups,
|
||||
|
|
@ -434,42 +470,6 @@ static const struct device_type *fsl_mc_get_device_type(const char *type)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int fsl_mc_driver_probe(struct device *dev)
|
||||
{
|
||||
struct fsl_mc_driver *mc_drv;
|
||||
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
|
||||
int error;
|
||||
|
||||
mc_drv = to_fsl_mc_driver(dev->driver);
|
||||
|
||||
error = mc_drv->probe(mc_dev);
|
||||
if (error < 0) {
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_err(dev, "%s failed: %d\n", __func__, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_mc_driver_remove(struct device *dev)
|
||||
{
|
||||
struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
|
||||
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
|
||||
|
||||
mc_drv->remove(mc_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsl_mc_driver_shutdown(struct device *dev)
|
||||
{
|
||||
struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
|
||||
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
|
||||
|
||||
mc_drv->shutdown(mc_dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* __fsl_mc_driver_register - registers a child device driver with the
|
||||
* MC bus
|
||||
|
|
@ -486,15 +486,6 @@ int __fsl_mc_driver_register(struct fsl_mc_driver *mc_driver,
|
|||
mc_driver->driver.owner = owner;
|
||||
mc_driver->driver.bus = &fsl_mc_bus_type;
|
||||
|
||||
if (mc_driver->probe)
|
||||
mc_driver->driver.probe = fsl_mc_driver_probe;
|
||||
|
||||
if (mc_driver->remove)
|
||||
mc_driver->driver.remove = fsl_mc_driver_remove;
|
||||
|
||||
if (mc_driver->shutdown)
|
||||
mc_driver->driver.shutdown = fsl_mc_driver_shutdown;
|
||||
|
||||
error = driver_register(&mc_driver->driver);
|
||||
if (error < 0) {
|
||||
pr_err("driver_register() failed for %s: %d\n",
|
||||
|
|
@ -905,11 +896,7 @@ int fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc,
|
|||
return 0;
|
||||
|
||||
error_cleanup_dev:
|
||||
kfree(mc_dev->regions);
|
||||
if (mc_bus)
|
||||
kfree(mc_bus);
|
||||
else
|
||||
kfree(mc_dev);
|
||||
put_device(&mc_dev->dev);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,15 +17,6 @@
|
|||
#define OCP2SCP_TIMING 0x18
|
||||
#define SYNC2_MASK 0xf
|
||||
|
||||
static int ocp2scp_remove_devices(struct device *dev, void *c)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
platform_device_unregister(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_ocp2scp_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
|
@ -79,7 +70,7 @@ err1:
|
|||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
err0:
|
||||
device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices);
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -87,7 +78,7 @@ err0:
|
|||
static void omap_ocp2scp_remove(struct platform_device *pdev)
|
||||
{
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices);
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
|
|
|||
|
|
@ -292,7 +292,6 @@ static void qcom_ebi2_setup_chipselect(struct device_node *np,
|
|||
static int qcom_ebi2_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *child;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
void __iomem *ebi2_base;
|
||||
|
|
@ -348,15 +347,13 @@ static int qcom_ebi2_probe(struct platform_device *pdev)
|
|||
writel(val, ebi2_base);
|
||||
|
||||
/* Walk over the child nodes and see what chipselects we use */
|
||||
for_each_available_child_of_node(np, child) {
|
||||
for_each_available_child_of_node_scoped(np, child) {
|
||||
u32 csindex;
|
||||
|
||||
/* Figure out the chipselect */
|
||||
ret = of_property_read_u32(child, "reg", &csindex);
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (csindex > 5) {
|
||||
dev_err(dev,
|
||||
|
|
|
|||
|
|
@ -208,9 +208,9 @@ static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
|
|||
return (ver->impl_id == TEE_IMPL_ID_OPTEE);
|
||||
}
|
||||
|
||||
static int optee_rng_probe(struct device *dev)
|
||||
static int optee_rng_probe(struct tee_client_device *rng_device)
|
||||
{
|
||||
struct tee_client_device *rng_device = to_tee_client_device(dev);
|
||||
struct device *dev = &rng_device->dev;
|
||||
int ret = 0, err = -ENODEV;
|
||||
struct tee_ioctl_open_session_arg sess_arg;
|
||||
|
||||
|
|
@ -258,12 +258,10 @@ out_ctx:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int optee_rng_remove(struct device *dev)
|
||||
static void optee_rng_remove(struct tee_client_device *tee_dev)
|
||||
{
|
||||
tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
|
||||
tee_client_close_context(pvt_data.ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct tee_client_device_id optee_rng_id_table[] = {
|
||||
|
|
@ -275,27 +273,15 @@ static const struct tee_client_device_id optee_rng_id_table[] = {
|
|||
MODULE_DEVICE_TABLE(tee, optee_rng_id_table);
|
||||
|
||||
static struct tee_client_driver optee_rng_driver = {
|
||||
.probe = optee_rng_probe,
|
||||
.remove = optee_rng_remove,
|
||||
.id_table = optee_rng_id_table,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.bus = &tee_bus_type,
|
||||
.probe = optee_rng_probe,
|
||||
.remove = optee_rng_remove,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init optee_rng_mod_init(void)
|
||||
{
|
||||
return driver_register(&optee_rng_driver.driver);
|
||||
}
|
||||
|
||||
static void __exit optee_rng_mod_exit(void)
|
||||
{
|
||||
driver_unregister(&optee_rng_driver.driver);
|
||||
}
|
||||
|
||||
module_init(optee_rng_mod_init);
|
||||
module_exit(optee_rng_mod_exit);
|
||||
module_tee_client_driver(optee_rng_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Sumit Garg <sumit.garg@linaro.org>");
|
||||
|
|
|
|||
|
|
@ -163,13 +163,13 @@ static int ftpm_tee_match(struct tee_ioctl_version_data *ver, const void *data)
|
|||
}
|
||||
|
||||
/**
|
||||
* ftpm_tee_probe() - initialize the fTPM
|
||||
* ftpm_tee_probe_generic() - initialize the fTPM
|
||||
* @dev: the device description.
|
||||
*
|
||||
* Return:
|
||||
* On success, 0. On failure, -errno.
|
||||
*/
|
||||
static int ftpm_tee_probe(struct device *dev)
|
||||
static int ftpm_tee_probe_generic(struct device *dev)
|
||||
{
|
||||
int rc;
|
||||
struct tpm_chip *chip;
|
||||
|
|
@ -251,21 +251,28 @@ out_tee_session:
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int ftpm_tee_probe(struct tee_client_device *tcdev)
|
||||
{
|
||||
struct device *dev = &tcdev->dev;
|
||||
|
||||
return ftpm_tee_probe_generic(dev);
|
||||
}
|
||||
|
||||
static int ftpm_plat_tee_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
return ftpm_tee_probe(dev);
|
||||
return ftpm_tee_probe_generic(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* ftpm_tee_remove() - remove the TPM device
|
||||
* ftpm_tee_remove_generic() - remove the TPM device
|
||||
* @dev: the device description.
|
||||
*
|
||||
* Return:
|
||||
* 0 always.
|
||||
*/
|
||||
static int ftpm_tee_remove(struct device *dev)
|
||||
static void ftpm_tee_remove_generic(struct device *dev)
|
||||
{
|
||||
struct ftpm_tee_private *pvt_data = dev_get_drvdata(dev);
|
||||
|
||||
|
|
@ -285,15 +292,20 @@ static int ftpm_tee_remove(struct device *dev)
|
|||
tee_client_close_context(pvt_data->ctx);
|
||||
|
||||
/* memory allocated with devm_kzalloc() is freed automatically */
|
||||
}
|
||||
|
||||
return 0;
|
||||
static void ftpm_tee_remove(struct tee_client_device *tcdev)
|
||||
{
|
||||
struct device *dev = &tcdev->dev;
|
||||
|
||||
ftpm_tee_remove_generic(dev);
|
||||
}
|
||||
|
||||
static void ftpm_plat_tee_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
ftpm_tee_remove(dev);
|
||||
ftpm_tee_remove_generic(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -335,12 +347,11 @@ static const struct tee_client_device_id optee_ftpm_id_table[] = {
|
|||
MODULE_DEVICE_TABLE(tee, optee_ftpm_id_table);
|
||||
|
||||
static struct tee_client_driver ftpm_tee_driver = {
|
||||
.probe = ftpm_tee_probe,
|
||||
.remove = ftpm_tee_remove,
|
||||
.id_table = optee_ftpm_id_table,
|
||||
.driver = {
|
||||
.name = "optee-ftpm",
|
||||
.bus = &tee_bus_type,
|
||||
.probe = ftpm_tee_probe,
|
||||
.remove = ftpm_tee_remove,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -352,7 +363,7 @@ static int __init ftpm_mod_init(void)
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = driver_register(&ftpm_tee_driver.driver);
|
||||
rc = tee_client_driver_register(&ftpm_tee_driver);
|
||||
if (rc) {
|
||||
platform_driver_unregister(&ftpm_tee_plat_driver);
|
||||
return rc;
|
||||
|
|
@ -364,7 +375,7 @@ static int __init ftpm_mod_init(void)
|
|||
static void __exit ftpm_mod_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ftpm_tee_plat_driver);
|
||||
driver_unregister(&ftpm_tee_driver.driver);
|
||||
tee_client_driver_unregister(&ftpm_tee_driver);
|
||||
}
|
||||
|
||||
module_init(ftpm_mod_init);
|
||||
|
|
|
|||
|
|
@ -454,7 +454,7 @@ int qcom_cc_probe_by_index(struct platform_device *pdev, int index,
|
|||
|
||||
base = devm_platform_ioremap_resource(pdev, index);
|
||||
if (IS_ERR(base))
|
||||
return -ENOMEM;
|
||||
return PTR_ERR(base);
|
||||
|
||||
regmap = devm_regmap_init_mmio(&pdev->dev, base, desc->config);
|
||||
if (IS_ERR(regmap))
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
* #1 wait-for-interrupt
|
||||
* #2 wait-for-interrupt and RAM self refresh
|
||||
*
|
||||
* Maintainer: Michal Simek <michal.simek@xilinx.com>
|
||||
* Maintainer: Michal Simek <michal.simek@amd.com>
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
|
|
|
|||
|
|
@ -246,6 +246,11 @@ static int ffa_features(u32 func_feat_id, u32 input_props,
|
|||
}
|
||||
|
||||
#define PARTITION_INFO_GET_RETURN_COUNT_ONLY BIT(0)
|
||||
#define FFA_SUPPORTS_GET_COUNT_ONLY(version) ((version) > FFA_VERSION_1_0)
|
||||
#define FFA_PART_INFO_HAS_SIZE_IN_RESP(version) ((version) > FFA_VERSION_1_0)
|
||||
#define FFA_PART_INFO_HAS_UUID_IN_RESP(version) ((version) > FFA_VERSION_1_0)
|
||||
#define FFA_PART_INFO_HAS_EXEC_STATE_IN_RESP(version) \
|
||||
((version) > FFA_VERSION_1_0)
|
||||
|
||||
/* buffer must be sizeof(struct ffa_partition_info) * num_partitions */
|
||||
static int
|
||||
|
|
@ -255,7 +260,7 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
|
|||
int idx, count, flags = 0, sz, buf_sz;
|
||||
ffa_value_t partition_info;
|
||||
|
||||
if (drv_info->version > FFA_VERSION_1_0 &&
|
||||
if (FFA_SUPPORTS_GET_COUNT_ONLY(drv_info->version) &&
|
||||
(!buffer || !num_partitions)) /* Just get the count for now */
|
||||
flags = PARTITION_INFO_GET_RETURN_COUNT_ONLY;
|
||||
|
||||
|
|
@ -273,12 +278,11 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
|
|||
|
||||
count = partition_info.a2;
|
||||
|
||||
if (drv_info->version > FFA_VERSION_1_0) {
|
||||
if (FFA_PART_INFO_HAS_SIZE_IN_RESP(drv_info->version)) {
|
||||
buf_sz = sz = partition_info.a3;
|
||||
if (sz > sizeof(*buffer))
|
||||
buf_sz = sizeof(*buffer);
|
||||
} else {
|
||||
/* FFA_VERSION_1_0 lacks size in the response */
|
||||
buf_sz = sz = 8;
|
||||
}
|
||||
|
||||
|
|
@ -981,10 +985,27 @@ static void __do_sched_recv_cb(u16 part_id, u16 vcpu, bool is_per_vcpu)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Map logical ID index to the u16 index within the packed ID list.
|
||||
*
|
||||
* For native responses (FF-A width == kernel word size), IDs are
|
||||
* tightly packed: idx -> idx.
|
||||
*
|
||||
* For 32-bit responses on a 64-bit kernel, each 64-bit register
|
||||
* contributes 4 x u16 values but only the lower 2 are defined; the
|
||||
* upper 2 are garbage. This mapping skips those upper halves:
|
||||
* 0,1,2,3,4,5,... -> 0,1,4,5,8,9,...
|
||||
*/
|
||||
static int list_idx_to_u16_idx(int idx, bool is_native_resp)
|
||||
{
|
||||
return is_native_resp ? idx : idx + 2 * (idx >> 1);
|
||||
}
|
||||
|
||||
static void ffa_notification_info_get(void)
|
||||
{
|
||||
int idx, list, max_ids, lists_cnt, ids_processed, ids_count[MAX_IDS_64];
|
||||
bool is_64b_resp;
|
||||
int ids_processed, ids_count[MAX_IDS_64];
|
||||
int idx, list, max_ids, lists_cnt;
|
||||
bool is_64b_resp, is_native_resp;
|
||||
ffa_value_t ret;
|
||||
u64 id_list;
|
||||
|
||||
|
|
@ -1001,6 +1022,7 @@ static void ffa_notification_info_get(void)
|
|||
}
|
||||
|
||||
is_64b_resp = (ret.a0 == FFA_FN64_SUCCESS);
|
||||
is_native_resp = (ret.a0 == FFA_FN_NATIVE(SUCCESS));
|
||||
|
||||
ids_processed = 0;
|
||||
lists_cnt = FIELD_GET(NOTIFICATION_INFO_GET_ID_COUNT, ret.a2);
|
||||
|
|
@ -1017,12 +1039,16 @@ static void ffa_notification_info_get(void)
|
|||
|
||||
/* Process IDs */
|
||||
for (list = 0; list < lists_cnt; list++) {
|
||||
int u16_idx;
|
||||
u16 vcpu_id, part_id, *packed_id_list = (u16 *)&ret.a3;
|
||||
|
||||
if (ids_processed >= max_ids - 1)
|
||||
break;
|
||||
|
||||
part_id = packed_id_list[ids_processed++];
|
||||
u16_idx = list_idx_to_u16_idx(ids_processed,
|
||||
is_native_resp);
|
||||
part_id = packed_id_list[u16_idx];
|
||||
ids_processed++;
|
||||
|
||||
if (ids_count[list] == 1) { /* Global Notification */
|
||||
__do_sched_recv_cb(part_id, 0, false);
|
||||
|
|
@ -1034,7 +1060,10 @@ static void ffa_notification_info_get(void)
|
|||
if (ids_processed >= max_ids - 1)
|
||||
break;
|
||||
|
||||
vcpu_id = packed_id_list[ids_processed++];
|
||||
u16_idx = list_idx_to_u16_idx(ids_processed,
|
||||
is_native_resp);
|
||||
vcpu_id = packed_id_list[u16_idx];
|
||||
ids_processed++;
|
||||
|
||||
__do_sched_recv_cb(part_id, vcpu_id, true);
|
||||
}
|
||||
|
|
@ -1706,7 +1735,7 @@ static int ffa_setup_partitions(void)
|
|||
struct ffa_device *ffa_dev;
|
||||
struct ffa_partition_info *pbuf, *tpbuf;
|
||||
|
||||
if (drv_info->version == FFA_VERSION_1_0) {
|
||||
if (!FFA_PART_INFO_HAS_UUID_IN_RESP(drv_info->version)) {
|
||||
ret = bus_register_notifier(&ffa_bus_type, &ffa_bus_nb);
|
||||
if (ret)
|
||||
pr_err("Failed to register FF-A bus notifiers\n");
|
||||
|
|
@ -1733,7 +1762,7 @@ static int ffa_setup_partitions(void)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (drv_info->version > FFA_VERSION_1_0 &&
|
||||
if (FFA_PART_INFO_HAS_EXEC_STATE_IN_RESP(drv_info->version) &&
|
||||
!(tpbuf->properties & FFA_PARTITION_AARCH64_EXEC))
|
||||
ffa_mode_32bit_set(ffa_dev);
|
||||
|
||||
|
|
@ -2068,6 +2097,7 @@ static int __init ffa_init(void)
|
|||
|
||||
pr_err("failed to setup partitions\n");
|
||||
ffa_notifications_cleanup();
|
||||
ffa_rxtx_unmap(drv_info->vm_id);
|
||||
free_pages:
|
||||
if (drv_info->tx_buffer)
|
||||
free_pages_exact(drv_info->tx_buffer, rxtx_bufsz);
|
||||
|
|
|
|||
|
|
@ -375,18 +375,13 @@ static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph)
|
|||
{
|
||||
int id, ret;
|
||||
u8 *prot_imp;
|
||||
u32 version;
|
||||
char name[SCMI_SHORT_NAME_MAX_SIZE];
|
||||
struct device *dev = ph->dev;
|
||||
struct scmi_revision_info *rev = scmi_revision_area_get(ph);
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rev->major_ver = PROTOCOL_REV_MAJOR(version);
|
||||
rev->minor_ver = PROTOCOL_REV_MINOR(version);
|
||||
ph->set_priv(ph, rev, version);
|
||||
rev->major_ver = PROTOCOL_REV_MAJOR(ph->version);
|
||||
rev->minor_ver = PROTOCOL_REV_MINOR(ph->version);
|
||||
ph->set_priv(ph, rev);
|
||||
|
||||
ret = scmi_base_attributes_get(ph);
|
||||
if (ret)
|
||||
|
|
|
|||
|
|
@ -157,7 +157,6 @@ struct scmi_clock_rate_notify_payld {
|
|||
};
|
||||
|
||||
struct clock_info {
|
||||
u32 version;
|
||||
int num_clocks;
|
||||
int max_async_req;
|
||||
bool notify_rate_changed_cmd;
|
||||
|
|
@ -346,8 +345,7 @@ scmi_clock_get_permissions(const struct scmi_protocol_handle *ph, u32 clk_id,
|
|||
}
|
||||
|
||||
static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, struct clock_info *cinfo,
|
||||
u32 version)
|
||||
u32 clk_id, struct clock_info *cinfo)
|
||||
{
|
||||
int ret;
|
||||
u32 attributes;
|
||||
|
|
@ -370,7 +368,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
|
|||
attributes = le32_to_cpu(attr->attributes);
|
||||
strscpy(clk->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);
|
||||
/* clock_enable_latency field is present only since SCMI v3.1 */
|
||||
if (PROTOCOL_REV_MAJOR(version) >= 0x2)
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2)
|
||||
latency = le32_to_cpu(attr->clock_enable_latency);
|
||||
clk->enable_latency = latency ? : U32_MAX;
|
||||
}
|
||||
|
|
@ -381,7 +379,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
|
|||
* If supported overwrite short name with the extended one;
|
||||
* on error just carry on and use already provided short name.
|
||||
*/
|
||||
if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x2) {
|
||||
if (!ret && PROTOCOL_REV_MAJOR(ph->version) >= 0x2) {
|
||||
if (SUPPORTS_EXTENDED_NAMES(attributes))
|
||||
ph->hops->extended_name_get(ph, CLOCK_NAME_GET, clk_id,
|
||||
NULL, clk->name,
|
||||
|
|
@ -393,7 +391,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
|
|||
if (cinfo->notify_rate_change_requested_cmd &&
|
||||
SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes))
|
||||
clk->rate_change_requested_notifications = true;
|
||||
if (PROTOCOL_REV_MAJOR(version) >= 0x3) {
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3) {
|
||||
if (SUPPORTS_PARENT_CLOCK(attributes))
|
||||
scmi_clock_possible_parents(ph, clk_id, clk);
|
||||
if (SUPPORTS_GET_PERMISSIONS(attributes))
|
||||
|
|
@ -1068,16 +1066,11 @@ static const struct scmi_protocol_events clk_protocol_events = {
|
|||
|
||||
static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
u32 version;
|
||||
int clkid, ret;
|
||||
struct clock_info *cinfo;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(ph->dev, "Clock Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
|
||||
|
||||
cinfo = devm_kzalloc(ph->dev, sizeof(*cinfo), GFP_KERNEL);
|
||||
if (!cinfo)
|
||||
|
|
@ -1095,12 +1088,12 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
|
|||
for (clkid = 0; clkid < cinfo->num_clocks; clkid++) {
|
||||
struct scmi_clock_info *clk = cinfo->clk + clkid;
|
||||
|
||||
ret = scmi_clock_attributes_get(ph, clkid, cinfo, version);
|
||||
ret = scmi_clock_attributes_get(ph, clkid, cinfo);
|
||||
if (!ret)
|
||||
scmi_clock_describe_rates_get(ph, clkid, clk);
|
||||
}
|
||||
|
||||
if (PROTOCOL_REV_MAJOR(version) >= 0x3) {
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3) {
|
||||
cinfo->clock_config_set = scmi_clock_config_set_v2;
|
||||
cinfo->clock_config_get = scmi_clock_config_get_v2;
|
||||
} else {
|
||||
|
|
@ -1108,8 +1101,7 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
|
|||
cinfo->clock_config_get = scmi_clock_config_get;
|
||||
}
|
||||
|
||||
cinfo->version = version;
|
||||
return ph->set_priv(ph, cinfo, version);
|
||||
return ph->set_priv(ph, cinfo);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_clock = {
|
||||
|
|
|
|||
|
|
@ -1627,17 +1627,15 @@ static int version_get(const struct scmi_protocol_handle *ph, u32 *version)
|
|||
*
|
||||
* @ph: A reference to the protocol handle.
|
||||
* @priv: The private data to set.
|
||||
* @version: The detected protocol version for the core to register.
|
||||
*
|
||||
* Return: 0 on Success
|
||||
*/
|
||||
static int scmi_set_protocol_priv(const struct scmi_protocol_handle *ph,
|
||||
void *priv, u32 version)
|
||||
void *priv)
|
||||
{
|
||||
struct scmi_protocol_instance *pi = ph_to_pi(ph);
|
||||
|
||||
pi->priv = priv;
|
||||
pi->version = version;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1657,7 +1655,6 @@ static void *scmi_get_protocol_priv(const struct scmi_protocol_handle *ph)
|
|||
}
|
||||
|
||||
static const struct scmi_xfer_ops xfer_ops = {
|
||||
.version_get = version_get,
|
||||
.xfer_get_init = xfer_get_init,
|
||||
.reset_rx_to_maxsz = reset_rx_to_maxsz,
|
||||
.do_xfer = do_xfer,
|
||||
|
|
@ -2112,6 +2109,76 @@ static int scmi_protocol_version_negotiate(struct scmi_protocol_handle *ph)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_protocol_version_initialize - Initialize protocol version
|
||||
* @dev: A device reference.
|
||||
* @pi: A reference to the protocol instance being initialized
|
||||
*
|
||||
* At first retrieve the newest protocol version supported by the platform for
|
||||
* this specific protoocol.
|
||||
*
|
||||
* Negotiation is attempted only when the platform advertised a protocol
|
||||
* version newer than the most recent version known to this agent, since
|
||||
* backward compatibility is NOT assured in general between versions.
|
||||
*
|
||||
* Failing to negotiate a fallback version or to query supported version at
|
||||
* all will result in an attempt to use the newest version known to this agent
|
||||
* even though compatibility is NOT assured.
|
||||
*
|
||||
* Versions are defined as:
|
||||
*
|
||||
* pi->version: the version supported by the platform as returned by the query.
|
||||
* pi->proto->supported_version: the newest version supported by this agent
|
||||
* for this protocol.
|
||||
* pi->negotiated_version: The version successfully negotiated with the platform.
|
||||
* ph->version: The final version effectively chosen for this session.
|
||||
*/
|
||||
static void scmi_protocol_version_initialize(struct device *dev,
|
||||
struct scmi_protocol_instance *pi)
|
||||
{
|
||||
struct scmi_protocol_handle *ph = &pi->ph;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Query and store platform supported protocol version: this is usually
|
||||
* the newest version the platfom can support.
|
||||
*/
|
||||
ret = version_get(ph, &pi->version);
|
||||
if (ret) {
|
||||
dev_warn(dev,
|
||||
"Failed to query supported version for protocol 0x%X.\n",
|
||||
pi->proto->id);
|
||||
goto best_effort;
|
||||
}
|
||||
|
||||
/* Need to negotiate at all ? */
|
||||
if (pi->version <= pi->proto->supported_version) {
|
||||
ph->version = pi->version;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Attempt negotiation */
|
||||
ret = scmi_protocol_version_negotiate(ph);
|
||||
if (!ret) {
|
||||
ph->version = pi->negotiated_version;
|
||||
dev_info(dev,
|
||||
"Protocol 0x%X successfully negotiated version 0x%X\n",
|
||||
pi->proto->id, ph->version);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_warn(dev,
|
||||
"Detected UNSUPPORTED higher version 0x%X for protocol 0x%X.\n",
|
||||
pi->version, pi->proto->id);
|
||||
|
||||
best_effort:
|
||||
/* Fallback to use newest version known to this agent */
|
||||
ph->version = pi->proto->supported_version;
|
||||
dev_warn(dev,
|
||||
"Trying version 0x%X. Backward compatibility is NOT assured.\n",
|
||||
ph->version);
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_alloc_init_protocol_instance - Allocate and initialize a protocol
|
||||
* instance descriptor.
|
||||
|
|
@ -2157,6 +2224,13 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
|
|||
pi->ph.set_priv = scmi_set_protocol_priv;
|
||||
pi->ph.get_priv = scmi_get_protocol_priv;
|
||||
refcount_set(&pi->users, 1);
|
||||
|
||||
/*
|
||||
* Initialize effectively used protocol version performing any
|
||||
* possibly needed negotiations.
|
||||
*/
|
||||
scmi_protocol_version_initialize(handle->dev, pi);
|
||||
|
||||
/* proto->init is assured NON NULL by scmi_protocol_register */
|
||||
ret = pi->proto->instance_init(&pi->ph);
|
||||
if (ret)
|
||||
|
|
@ -2184,22 +2258,6 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
|
|||
devres_close_group(handle->dev, pi->gid);
|
||||
dev_dbg(handle->dev, "Initialized protocol: 0x%X\n", pi->proto->id);
|
||||
|
||||
if (pi->version > proto->supported_version) {
|
||||
ret = scmi_protocol_version_negotiate(&pi->ph);
|
||||
if (!ret) {
|
||||
dev_info(handle->dev,
|
||||
"Protocol 0x%X successfully negotiated version 0x%X\n",
|
||||
proto->id, pi->negotiated_version);
|
||||
} else {
|
||||
dev_warn(handle->dev,
|
||||
"Detected UNSUPPORTED higher version 0x%X for protocol 0x%X.\n",
|
||||
pi->version, pi->proto->id);
|
||||
dev_warn(handle->dev,
|
||||
"Trying version 0x%X. Backward compatibility is NOT assured.\n",
|
||||
pi->proto->supported_version);
|
||||
}
|
||||
}
|
||||
|
||||
return pi;
|
||||
|
||||
clean:
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
/* Updated only after ALL the mandatory features for that version are merged */
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x40000
|
||||
|
||||
#define MAX_OPPS 32
|
||||
#define MAX_OPPS 64
|
||||
|
||||
enum scmi_performance_protocol_cmd {
|
||||
PERF_DOMAIN_ATTRIBUTES = 0x3,
|
||||
|
|
@ -178,7 +178,6 @@ struct perf_dom_info {
|
|||
})
|
||||
|
||||
struct scmi_perf_info {
|
||||
u32 version;
|
||||
u16 num_domains;
|
||||
enum scmi_power_scale power_scale;
|
||||
u64 stats_addr;
|
||||
|
|
@ -215,7 +214,7 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph,
|
|||
|
||||
if (POWER_SCALE_IN_MILLIWATT(flags))
|
||||
pi->power_scale = SCMI_POWER_MILLIWATTS;
|
||||
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3)
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3)
|
||||
if (POWER_SCALE_IN_MICROWATT(flags))
|
||||
pi->power_scale = SCMI_POWER_MICROWATTS;
|
||||
|
||||
|
|
@ -251,8 +250,7 @@ static void scmi_perf_xa_destroy(void *data)
|
|||
static int
|
||||
scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
struct perf_dom_info *dom_info,
|
||||
bool notify_lim_cmd, bool notify_lvl_cmd,
|
||||
u32 version)
|
||||
bool notify_lim_cmd, bool notify_lvl_cmd)
|
||||
{
|
||||
int ret;
|
||||
u32 flags;
|
||||
|
|
@ -280,7 +278,7 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
|||
dom_info->perf_level_notify =
|
||||
SUPPORTS_PERF_LEVEL_NOTIFY(flags);
|
||||
dom_info->perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS(flags);
|
||||
if (PROTOCOL_REV_MAJOR(version) >= 0x4)
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x4)
|
||||
dom_info->level_indexing_mode =
|
||||
SUPPORTS_LEVEL_INDEXING(flags);
|
||||
dom_info->rate_limit_us = le32_to_cpu(attr->rate_limit_us) &
|
||||
|
|
@ -323,7 +321,7 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
|||
* If supported overwrite short name with the extended one;
|
||||
* on error just carry on and use already provided short name.
|
||||
*/
|
||||
if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
|
||||
if (!ret && PROTOCOL_REV_MAJOR(ph->version) >= 0x3 &&
|
||||
SUPPORTS_EXTENDED_NAMES(flags))
|
||||
ph->hops->extended_name_get(ph, PERF_DOMAIN_NAME_GET,
|
||||
dom_info->id, NULL, dom_info->info.name,
|
||||
|
|
@ -345,19 +343,14 @@ static int opp_cmp_func(const void *opp1, const void *opp2)
|
|||
return t1->perf - t2->perf;
|
||||
}
|
||||
|
||||
struct scmi_perf_ipriv {
|
||||
u32 version;
|
||||
struct perf_dom_info *perf_dom;
|
||||
};
|
||||
|
||||
static void iter_perf_levels_prepare_message(void *message,
|
||||
unsigned int desc_index,
|
||||
const void *priv)
|
||||
{
|
||||
struct scmi_msg_perf_describe_levels *msg = message;
|
||||
const struct scmi_perf_ipriv *p = priv;
|
||||
const struct perf_dom_info *perf_dom = priv;
|
||||
|
||||
msg->domain = cpu_to_le32(p->perf_dom->id);
|
||||
msg->domain = cpu_to_le32(perf_dom->id);
|
||||
/* Set the number of OPPs to be skipped/already read */
|
||||
msg->level_index = cpu_to_le32(desc_index);
|
||||
}
|
||||
|
|
@ -445,21 +438,21 @@ iter_perf_levels_process_response(const struct scmi_protocol_handle *ph,
|
|||
{
|
||||
int ret;
|
||||
struct scmi_opp *opp;
|
||||
struct scmi_perf_ipriv *p = priv;
|
||||
struct perf_dom_info *perf_dom = priv;
|
||||
|
||||
opp = &p->perf_dom->opp[p->perf_dom->opp_count];
|
||||
if (PROTOCOL_REV_MAJOR(p->version) <= 0x3)
|
||||
ret = process_response_opp(ph->dev, p->perf_dom, opp,
|
||||
opp = &perf_dom->opp[perf_dom->opp_count];
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) <= 0x3)
|
||||
ret = process_response_opp(ph->dev, perf_dom, opp,
|
||||
st->loop_idx, response);
|
||||
else
|
||||
ret = process_response_opp_v4(ph->dev, p->perf_dom, opp,
|
||||
ret = process_response_opp_v4(ph->dev, perf_dom, opp,
|
||||
st->loop_idx, response);
|
||||
|
||||
/* Skip BAD duplicates received from firmware */
|
||||
if (ret)
|
||||
return ret == -EBUSY ? 0 : ret;
|
||||
|
||||
p->perf_dom->opp_count++;
|
||||
perf_dom->opp_count++;
|
||||
|
||||
dev_dbg(ph->dev, "Level %d Power %d Latency %dus Ifreq %d Index %d\n",
|
||||
opp->perf, opp->power, opp->trans_latency_us,
|
||||
|
|
@ -470,7 +463,7 @@ iter_perf_levels_process_response(const struct scmi_protocol_handle *ph,
|
|||
|
||||
static int
|
||||
scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph,
|
||||
struct perf_dom_info *perf_dom, u32 version)
|
||||
struct perf_dom_info *perf_dom)
|
||||
{
|
||||
int ret;
|
||||
void *iter;
|
||||
|
|
@ -479,15 +472,11 @@ scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph,
|
|||
.update_state = iter_perf_levels_update_state,
|
||||
.process_response = iter_perf_levels_process_response,
|
||||
};
|
||||
struct scmi_perf_ipriv ppriv = {
|
||||
.version = version,
|
||||
.perf_dom = perf_dom,
|
||||
};
|
||||
|
||||
iter = ph->hops->iter_response_init(ph, &ops, MAX_OPPS,
|
||||
PERF_DESCRIBE_LEVELS,
|
||||
sizeof(struct scmi_msg_perf_describe_levels),
|
||||
&ppriv);
|
||||
perf_dom);
|
||||
if (IS_ERR(iter))
|
||||
return PTR_ERR(iter);
|
||||
|
||||
|
|
@ -576,7 +565,6 @@ static int __scmi_perf_limits_set(const struct scmi_protocol_handle *ph,
|
|||
static int scmi_perf_limits_set(const struct scmi_protocol_handle *ph,
|
||||
u32 domain, u32 max_perf, u32 min_perf)
|
||||
{
|
||||
struct scmi_perf_info *pi = ph->get_priv(ph);
|
||||
struct perf_dom_info *dom;
|
||||
|
||||
dom = scmi_perf_domain_lookup(ph, domain);
|
||||
|
|
@ -586,7 +574,7 @@ static int scmi_perf_limits_set(const struct scmi_protocol_handle *ph,
|
|||
if (!dom->set_limits)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3 && !max_perf && !min_perf)
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3 && !max_perf && !min_perf)
|
||||
return -EINVAL;
|
||||
|
||||
if (dom->level_indexing_mode) {
|
||||
|
|
@ -1281,22 +1269,15 @@ static const struct scmi_protocol_events perf_protocol_events = {
|
|||
static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
int domain, ret;
|
||||
u32 version;
|
||||
struct scmi_perf_info *pinfo;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(ph->dev, "Performance Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
|
||||
|
||||
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
|
||||
if (!pinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
pinfo->version = version;
|
||||
|
||||
ret = scmi_perf_attributes_get(ph, pinfo);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -1311,8 +1292,8 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
|
|||
|
||||
dom->id = domain;
|
||||
scmi_perf_domain_attributes_get(ph, dom, pinfo->notify_lim_cmd,
|
||||
pinfo->notify_lvl_cmd, version);
|
||||
scmi_perf_describe_levels_get(ph, dom, version);
|
||||
pinfo->notify_lvl_cmd);
|
||||
scmi_perf_describe_levels_get(ph, dom);
|
||||
|
||||
if (dom->perf_fastchannels)
|
||||
scmi_perf_domain_init_fc(ph, dom);
|
||||
|
|
@ -1322,7 +1303,7 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ph->set_priv(ph, pinfo, version);
|
||||
return ph->set_priv(ph, pinfo);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_perf = {
|
||||
|
|
|
|||
|
|
@ -117,7 +117,6 @@ struct scmi_pin_info {
|
|||
};
|
||||
|
||||
struct scmi_pinctrl_info {
|
||||
u32 version;
|
||||
int nr_groups;
|
||||
int nr_functions;
|
||||
int nr_pins;
|
||||
|
|
@ -596,11 +595,19 @@ static int scmi_pinctrl_pin_free(const struct scmi_protocol_handle *ph, u32 pin)
|
|||
}
|
||||
|
||||
static int scmi_pinctrl_get_group_info(const struct scmi_protocol_handle *ph,
|
||||
u32 selector,
|
||||
struct scmi_group_info *group)
|
||||
u32 selector)
|
||||
{
|
||||
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
|
||||
struct scmi_group_info *group;
|
||||
int ret;
|
||||
|
||||
if (selector >= pi->nr_groups)
|
||||
return -EINVAL;
|
||||
|
||||
group = &pi->groups[selector];
|
||||
if (group->present)
|
||||
return 0;
|
||||
|
||||
ret = scmi_pinctrl_attributes(ph, GROUP_TYPE, selector, group->name,
|
||||
&group->nr_pins);
|
||||
if (ret)
|
||||
|
|
@ -632,21 +639,14 @@ static int scmi_pinctrl_get_group_name(const struct scmi_protocol_handle *ph,
|
|||
u32 selector, const char **name)
|
||||
{
|
||||
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
|
||||
int ret;
|
||||
|
||||
if (!name)
|
||||
return -EINVAL;
|
||||
|
||||
if (selector >= pi->nr_groups || pi->nr_groups == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!pi->groups[selector].present) {
|
||||
int ret;
|
||||
|
||||
ret = scmi_pinctrl_get_group_info(ph, selector,
|
||||
&pi->groups[selector]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = scmi_pinctrl_get_group_info(ph, selector);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*name = pi->groups[selector].name;
|
||||
|
||||
|
|
@ -658,21 +658,14 @@ static int scmi_pinctrl_group_pins_get(const struct scmi_protocol_handle *ph,
|
|||
u32 *nr_pins)
|
||||
{
|
||||
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
|
||||
int ret;
|
||||
|
||||
if (!pins || !nr_pins)
|
||||
return -EINVAL;
|
||||
|
||||
if (selector >= pi->nr_groups || pi->nr_groups == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!pi->groups[selector].present) {
|
||||
int ret;
|
||||
|
||||
ret = scmi_pinctrl_get_group_info(ph, selector,
|
||||
&pi->groups[selector]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = scmi_pinctrl_get_group_info(ph, selector);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*pins = pi->groups[selector].group_pins;
|
||||
*nr_pins = pi->groups[selector].nr_pins;
|
||||
|
|
@ -681,11 +674,19 @@ static int scmi_pinctrl_group_pins_get(const struct scmi_protocol_handle *ph,
|
|||
}
|
||||
|
||||
static int scmi_pinctrl_get_function_info(const struct scmi_protocol_handle *ph,
|
||||
u32 selector,
|
||||
struct scmi_function_info *func)
|
||||
u32 selector)
|
||||
{
|
||||
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
|
||||
struct scmi_function_info *func;
|
||||
int ret;
|
||||
|
||||
if (selector >= pi->nr_functions)
|
||||
return -EINVAL;
|
||||
|
||||
func = &pi->functions[selector];
|
||||
if (func->present)
|
||||
return 0;
|
||||
|
||||
ret = scmi_pinctrl_attributes(ph, FUNCTION_TYPE, selector, func->name,
|
||||
&func->nr_groups);
|
||||
if (ret)
|
||||
|
|
@ -716,21 +717,14 @@ static int scmi_pinctrl_get_function_name(const struct scmi_protocol_handle *ph,
|
|||
u32 selector, const char **name)
|
||||
{
|
||||
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
|
||||
int ret;
|
||||
|
||||
if (!name)
|
||||
return -EINVAL;
|
||||
|
||||
if (selector >= pi->nr_functions || pi->nr_functions == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!pi->functions[selector].present) {
|
||||
int ret;
|
||||
|
||||
ret = scmi_pinctrl_get_function_info(ph, selector,
|
||||
&pi->functions[selector]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = scmi_pinctrl_get_function_info(ph, selector);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*name = pi->functions[selector].name;
|
||||
return 0;
|
||||
|
|
@ -742,21 +736,14 @@ scmi_pinctrl_function_groups_get(const struct scmi_protocol_handle *ph,
|
|||
const u32 **groups)
|
||||
{
|
||||
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
|
||||
int ret;
|
||||
|
||||
if (!groups || !nr_groups)
|
||||
return -EINVAL;
|
||||
|
||||
if (selector >= pi->nr_functions || pi->nr_functions == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!pi->functions[selector].present) {
|
||||
int ret;
|
||||
|
||||
ret = scmi_pinctrl_get_function_info(ph, selector,
|
||||
&pi->functions[selector]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = scmi_pinctrl_get_function_info(ph, selector);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*groups = pi->functions[selector].groups;
|
||||
*nr_groups = pi->functions[selector].nr_groups;
|
||||
|
|
@ -771,13 +758,19 @@ static int scmi_pinctrl_mux_set(const struct scmi_protocol_handle *ph,
|
|||
}
|
||||
|
||||
static int scmi_pinctrl_get_pin_info(const struct scmi_protocol_handle *ph,
|
||||
u32 selector, struct scmi_pin_info *pin)
|
||||
u32 selector)
|
||||
{
|
||||
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
|
||||
struct scmi_pin_info *pin;
|
||||
int ret;
|
||||
|
||||
if (!pin)
|
||||
if (selector >= pi->nr_pins)
|
||||
return -EINVAL;
|
||||
|
||||
pin = &pi->pins[selector];
|
||||
if (pin->present)
|
||||
return 0;
|
||||
|
||||
ret = scmi_pinctrl_attributes(ph, PIN_TYPE, selector, pin->name, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -790,20 +783,14 @@ static int scmi_pinctrl_get_pin_name(const struct scmi_protocol_handle *ph,
|
|||
u32 selector, const char **name)
|
||||
{
|
||||
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
|
||||
int ret;
|
||||
|
||||
if (!name)
|
||||
return -EINVAL;
|
||||
|
||||
if (selector >= pi->nr_pins)
|
||||
return -EINVAL;
|
||||
|
||||
if (!pi->pins[selector].present) {
|
||||
int ret;
|
||||
|
||||
ret = scmi_pinctrl_get_pin_info(ph, selector, &pi->pins[selector]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = scmi_pinctrl_get_pin_info(ph, selector);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*name = pi->pins[selector].name;
|
||||
|
||||
|
|
@ -843,15 +830,10 @@ static const struct scmi_pinctrl_proto_ops pinctrl_proto_ops = {
|
|||
static int scmi_pinctrl_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
int ret;
|
||||
u32 version;
|
||||
struct scmi_pinctrl_info *pinfo;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(ph->dev, "Pinctrl Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
|
||||
|
||||
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
|
||||
if (!pinfo)
|
||||
|
|
@ -876,9 +858,7 @@ static int scmi_pinctrl_protocol_init(const struct scmi_protocol_handle *ph)
|
|||
if (!pinfo->functions)
|
||||
return -ENOMEM;
|
||||
|
||||
pinfo->version = version;
|
||||
|
||||
return ph->set_priv(ph, pinfo, version);
|
||||
return ph->set_priv(ph, pinfo);
|
||||
}
|
||||
|
||||
static int scmi_pinctrl_protocol_deinit(const struct scmi_protocol_handle *ph)
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ struct power_dom_info {
|
|||
};
|
||||
|
||||
struct scmi_power_info {
|
||||
u32 version;
|
||||
bool notify_state_change_cmd;
|
||||
int num_domains;
|
||||
u64 stats_addr;
|
||||
|
|
@ -109,7 +108,7 @@ static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph,
|
|||
static int
|
||||
scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain, struct power_dom_info *dom_info,
|
||||
u32 version, bool notify_state_change_cmd)
|
||||
bool notify_state_change_cmd)
|
||||
{
|
||||
int ret;
|
||||
u32 flags;
|
||||
|
|
@ -141,7 +140,7 @@ scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
|||
* If supported overwrite short name with the extended one;
|
||||
* on error just carry on and use already provided short name.
|
||||
*/
|
||||
if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
|
||||
if (!ret && PROTOCOL_REV_MAJOR(ph->version) >= 0x3 &&
|
||||
SUPPORTS_EXTENDED_NAMES(flags)) {
|
||||
ph->hops->extended_name_get(ph, POWER_DOMAIN_NAME_GET,
|
||||
domain, NULL, dom_info->name,
|
||||
|
|
@ -323,15 +322,10 @@ static const struct scmi_protocol_events power_protocol_events = {
|
|||
static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
int domain, ret;
|
||||
u32 version;
|
||||
struct scmi_power_info *pinfo;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(ph->dev, "Power Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
|
||||
|
||||
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
|
||||
if (!pinfo)
|
||||
|
|
@ -349,13 +343,11 @@ static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
|
|||
for (domain = 0; domain < pinfo->num_domains; domain++) {
|
||||
struct power_dom_info *dom = pinfo->dom_info + domain;
|
||||
|
||||
scmi_power_domain_attributes_get(ph, domain, dom, version,
|
||||
scmi_power_domain_attributes_get(ph, domain, dom,
|
||||
pinfo->notify_state_change_cmd);
|
||||
}
|
||||
|
||||
pinfo->version = version;
|
||||
|
||||
return ph->set_priv(ph, pinfo, version);
|
||||
return ph->set_priv(ph, pinfo);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_power = {
|
||||
|
|
|
|||
|
|
@ -122,7 +122,6 @@ struct scmi_powercap_state {
|
|||
};
|
||||
|
||||
struct powercap_info {
|
||||
u32 version;
|
||||
int num_domains;
|
||||
bool notify_cap_cmd;
|
||||
bool notify_measurements_cmd;
|
||||
|
|
@ -434,7 +433,7 @@ static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
|
|||
}
|
||||
|
||||
/* Save the last explicitly set non-zero powercap value */
|
||||
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 && !ret && power_cap)
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2 && !ret && power_cap)
|
||||
pi->states[domain_id].last_pcap = power_cap;
|
||||
|
||||
return ret;
|
||||
|
|
@ -454,7 +453,7 @@ static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
|
|||
return -EINVAL;
|
||||
|
||||
/* Just log the last set request if acting on a disabled domain */
|
||||
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 &&
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2 &&
|
||||
!pi->states[domain_id].enabled) {
|
||||
pi->states[domain_id].last_pcap = power_cap;
|
||||
return 0;
|
||||
|
|
@ -635,7 +634,7 @@ static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph,
|
|||
u32 power_cap;
|
||||
struct powercap_info *pi = ph->get_priv(ph);
|
||||
|
||||
if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) < 0x2)
|
||||
return -EINVAL;
|
||||
|
||||
if (enable == pi->states[domain_id].enabled)
|
||||
|
|
@ -676,7 +675,7 @@ static int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph,
|
|||
struct powercap_info *pi = ph->get_priv(ph);
|
||||
|
||||
*enable = true;
|
||||
if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) < 0x2)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
|
@ -961,15 +960,10 @@ static int
|
|||
scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
int domain, ret;
|
||||
u32 version;
|
||||
struct powercap_info *pinfo;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(ph->dev, "Powercap Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
|
||||
|
||||
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
|
||||
if (!pinfo)
|
||||
|
|
@ -1006,7 +1000,7 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
|
|||
&pinfo->powercaps[domain].fc_info);
|
||||
|
||||
/* Grab initial state when disable is supported. */
|
||||
if (PROTOCOL_REV_MAJOR(version) >= 0x2) {
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2) {
|
||||
ret = __scmi_powercap_cap_get(ph,
|
||||
&pinfo->powercaps[domain],
|
||||
&pinfo->states[domain].last_pcap);
|
||||
|
|
@ -1018,8 +1012,7 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
|
|||
}
|
||||
}
|
||||
|
||||
pinfo->version = version;
|
||||
return ph->set_priv(ph, pinfo, version);
|
||||
return ph->set_priv(ph, pinfo);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_powercap = {
|
||||
|
|
|
|||
|
|
@ -159,6 +159,9 @@ struct scmi_proto_helpers_ops;
|
|||
* struct scmi_protocol_handle - Reference to an initialized protocol instance
|
||||
*
|
||||
* @dev: A reference to the associated SCMI instance device (handle->dev).
|
||||
* @version: The protocol version currently effectively in use by this
|
||||
* initialized instance of the protocol as determined at the end of
|
||||
* any possibly needed negotiations performed by the core.
|
||||
* @xops: A reference to a struct holding refs to the core xfer operations that
|
||||
* can be used by the protocol implementation to generate SCMI messages.
|
||||
* @set_priv: A method to set protocol private data for this instance.
|
||||
|
|
@ -177,10 +180,10 @@ struct scmi_proto_helpers_ops;
|
|||
*/
|
||||
struct scmi_protocol_handle {
|
||||
struct device *dev;
|
||||
unsigned int version;
|
||||
const struct scmi_xfer_ops *xops;
|
||||
const struct scmi_proto_helpers_ops *hops;
|
||||
int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv,
|
||||
u32 version);
|
||||
int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
|
||||
void *(*get_priv)(const struct scmi_protocol_handle *ph);
|
||||
};
|
||||
|
||||
|
|
@ -287,7 +290,6 @@ struct scmi_proto_helpers_ops {
|
|||
|
||||
/**
|
||||
* struct scmi_xfer_ops - References to the core SCMI xfer operations.
|
||||
* @version_get: Get this version protocol.
|
||||
* @xfer_get_init: Initialize one struct xfer if any xfer slot is free.
|
||||
* @reset_rx_to_maxsz: Reset rx size to max transport size.
|
||||
* @do_xfer: Do the SCMI transfer.
|
||||
|
|
@ -300,7 +302,6 @@ struct scmi_proto_helpers_ops {
|
|||
* another protocol.
|
||||
*/
|
||||
struct scmi_xfer_ops {
|
||||
int (*version_get)(const struct scmi_protocol_handle *ph, u32 *version);
|
||||
int (*xfer_get_init)(const struct scmi_protocol_handle *ph, u8 msg_id,
|
||||
size_t tx_size, size_t rx_size,
|
||||
struct scmi_xfer **p);
|
||||
|
|
|
|||
|
|
@ -65,7 +65,6 @@ struct reset_dom_info {
|
|||
};
|
||||
|
||||
struct scmi_reset_info {
|
||||
u32 version;
|
||||
int num_domains;
|
||||
bool notify_reset_cmd;
|
||||
struct reset_dom_info *dom_info;
|
||||
|
|
@ -98,10 +97,20 @@ static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct reset_dom_info *
|
||||
scmi_reset_domain_lookup(const struct scmi_protocol_handle *ph, u32 domain)
|
||||
{
|
||||
struct scmi_reset_info *pi = ph->get_priv(ph);
|
||||
|
||||
if (domain >= pi->num_domains)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return pi->dom_info + domain;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_reset_info *pinfo,
|
||||
u32 domain, u32 version)
|
||||
struct scmi_reset_info *pinfo, u32 domain)
|
||||
{
|
||||
int ret;
|
||||
u32 attributes;
|
||||
|
|
@ -137,7 +146,7 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
|||
* If supported overwrite short name with the extended one;
|
||||
* on error just carry on and use already provided short name.
|
||||
*/
|
||||
if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
|
||||
if (!ret && PROTOCOL_REV_MAJOR(ph->version) >= 0x3 &&
|
||||
SUPPORTS_EXTENDED_NAMES(attributes))
|
||||
ph->hops->extended_name_get(ph, RESET_DOMAIN_NAME_GET, domain,
|
||||
NULL, dom_info->name,
|
||||
|
|
@ -156,20 +165,25 @@ static int scmi_reset_num_domains_get(const struct scmi_protocol_handle *ph)
|
|||
static const char *
|
||||
scmi_reset_name_get(const struct scmi_protocol_handle *ph, u32 domain)
|
||||
{
|
||||
struct scmi_reset_info *pi = ph->get_priv(ph);
|
||||
struct reset_dom_info *dom_info;
|
||||
|
||||
struct reset_dom_info *dom = pi->dom_info + domain;
|
||||
dom_info = scmi_reset_domain_lookup(ph, domain);
|
||||
if (IS_ERR(dom_info))
|
||||
return "unknown";
|
||||
|
||||
return dom->name;
|
||||
return dom_info->name;
|
||||
}
|
||||
|
||||
static int scmi_reset_latency_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain)
|
||||
{
|
||||
struct scmi_reset_info *pi = ph->get_priv(ph);
|
||||
struct reset_dom_info *dom = pi->dom_info + domain;
|
||||
struct reset_dom_info *dom_info;
|
||||
|
||||
return dom->latency_us;
|
||||
dom_info = scmi_reset_domain_lookup(ph, domain);
|
||||
if (IS_ERR(dom_info))
|
||||
return PTR_ERR(dom_info);
|
||||
|
||||
return dom_info->latency_us;
|
||||
}
|
||||
|
||||
static int scmi_domain_reset(const struct scmi_protocol_handle *ph, u32 domain,
|
||||
|
|
@ -178,14 +192,13 @@ static int scmi_domain_reset(const struct scmi_protocol_handle *ph, u32 domain,
|
|||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_reset_domain_reset *dom;
|
||||
struct scmi_reset_info *pi = ph->get_priv(ph);
|
||||
struct reset_dom_info *rdom;
|
||||
struct reset_dom_info *dom_info;
|
||||
|
||||
if (domain >= pi->num_domains)
|
||||
return -EINVAL;
|
||||
dom_info = scmi_reset_domain_lookup(ph, domain);
|
||||
if (IS_ERR(dom_info))
|
||||
return PTR_ERR(dom_info);
|
||||
|
||||
rdom = pi->dom_info + domain;
|
||||
if (rdom->async_reset && flags & AUTONOMOUS_RESET)
|
||||
if (dom_info->async_reset && flags & AUTONOMOUS_RESET)
|
||||
flags |= ASYNCHRONOUS_RESET;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, RESET, sizeof(*dom), 0, &t);
|
||||
|
|
@ -238,15 +251,16 @@ static const struct scmi_reset_proto_ops reset_proto_ops = {
|
|||
static bool scmi_reset_notify_supported(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id)
|
||||
{
|
||||
struct reset_dom_info *dom;
|
||||
struct scmi_reset_info *pi = ph->get_priv(ph);
|
||||
struct reset_dom_info *dom_info;
|
||||
|
||||
if (evt_id != SCMI_EVENT_RESET_ISSUED || src_id >= pi->num_domains)
|
||||
if (evt_id != SCMI_EVENT_RESET_ISSUED)
|
||||
return false;
|
||||
|
||||
dom = pi->dom_info + src_id;
|
||||
dom_info = scmi_reset_domain_lookup(ph, src_id);
|
||||
if (IS_ERR(dom_info))
|
||||
return false;
|
||||
|
||||
return dom->reset_notify;
|
||||
return dom_info->reset_notify;
|
||||
}
|
||||
|
||||
static int scmi_reset_notify(const struct scmi_protocol_handle *ph,
|
||||
|
|
@ -340,15 +354,10 @@ static const struct scmi_protocol_events reset_protocol_events = {
|
|||
static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
int domain, ret;
|
||||
u32 version;
|
||||
struct scmi_reset_info *pinfo;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(ph->dev, "Reset Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
|
||||
|
||||
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
|
||||
if (!pinfo)
|
||||
|
|
@ -364,10 +373,9 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
|
|||
return -ENOMEM;
|
||||
|
||||
for (domain = 0; domain < pinfo->num_domains; domain++)
|
||||
scmi_reset_domain_attributes_get(ph, pinfo, domain, version);
|
||||
scmi_reset_domain_attributes_get(ph, pinfo, domain);
|
||||
|
||||
pinfo->version = version;
|
||||
return ph->set_priv(ph, pinfo, version);
|
||||
return ph->set_priv(ph, pinfo);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_reset = {
|
||||
|
|
|
|||
|
|
@ -214,7 +214,6 @@ struct scmi_sensor_update_notify_payld {
|
|||
};
|
||||
|
||||
struct sensors_info {
|
||||
u32 version;
|
||||
bool notify_trip_point_cmd;
|
||||
bool notify_continuos_update_cmd;
|
||||
int num_sensors;
|
||||
|
|
@ -524,8 +523,7 @@ scmi_sensor_axis_extended_names_get(const struct scmi_protocol_handle *ph,
|
|||
}
|
||||
|
||||
static int scmi_sensor_axis_description(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_sensor_info *s,
|
||||
u32 version)
|
||||
struct scmi_sensor_info *s)
|
||||
{
|
||||
int ret;
|
||||
void *iter;
|
||||
|
|
@ -555,7 +553,7 @@ static int scmi_sensor_axis_description(const struct scmi_protocol_handle *ph,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (PROTOCOL_REV_MAJOR(version) >= 0x3 &&
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3 &&
|
||||
apriv.any_axes_support_extended_names)
|
||||
ret = scmi_sensor_axis_extended_names_get(ph, s);
|
||||
|
||||
|
|
@ -621,7 +619,7 @@ iter_sens_descr_process_response(const struct scmi_protocol_handle *ph,
|
|||
s->type = SENSOR_TYPE(attrh);
|
||||
/* Use pre-allocated pool wherever possible */
|
||||
s->intervals.desc = s->intervals.prealloc_pool;
|
||||
if (si->version == SCMIv2_SENSOR_PROTOCOL) {
|
||||
if (ph->version == SCMIv2_SENSOR_PROTOCOL) {
|
||||
s->intervals.segmented = false;
|
||||
s->intervals.count = 1;
|
||||
/*
|
||||
|
|
@ -659,7 +657,7 @@ iter_sens_descr_process_response(const struct scmi_protocol_handle *ph,
|
|||
* one; on error just carry on and use already provided
|
||||
* short name.
|
||||
*/
|
||||
if (PROTOCOL_REV_MAJOR(si->version) >= 0x3 &&
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3 &&
|
||||
SUPPORTS_EXTENDED_NAMES(attrl))
|
||||
ph->hops->extended_name_get(ph, SENSOR_NAME_GET, s->id,
|
||||
NULL, s->name, SCMI_MAX_STR_SIZE);
|
||||
|
|
@ -683,7 +681,7 @@ iter_sens_descr_process_response(const struct scmi_protocol_handle *ph,
|
|||
}
|
||||
|
||||
if (s->num_axis > 0)
|
||||
ret = scmi_sensor_axis_description(ph, s, si->version);
|
||||
ret = scmi_sensor_axis_description(ph, s);
|
||||
|
||||
st->priv = ((u8 *)sdesc + dsize);
|
||||
|
||||
|
|
@ -1148,21 +1146,15 @@ static const struct scmi_protocol_events sensor_protocol_events = {
|
|||
|
||||
static int scmi_sensors_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
u32 version;
|
||||
int ret;
|
||||
struct sensors_info *sinfo;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(ph->dev, "Sensor Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
|
||||
|
||||
sinfo = devm_kzalloc(ph->dev, sizeof(*sinfo), GFP_KERNEL);
|
||||
if (!sinfo)
|
||||
return -ENOMEM;
|
||||
sinfo->version = version;
|
||||
|
||||
ret = scmi_sensor_attributes_get(ph, sinfo);
|
||||
if (ret)
|
||||
|
|
@ -1176,7 +1168,7 @@ static int scmi_sensors_protocol_init(const struct scmi_protocol_handle *ph)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ph->set_priv(ph, sinfo, version);
|
||||
return ph->set_priv(ph, sinfo);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_sensors = {
|
||||
|
|
|
|||
|
|
@ -196,7 +196,6 @@ static void __iomem *shmem_setup_iomap(struct scmi_chan_info *cinfo,
|
|||
struct resource *res,
|
||||
struct scmi_shmem_io_ops **ops)
|
||||
{
|
||||
struct device_node *shmem __free(device_node);
|
||||
const char *desc = tx ? "Tx" : "Rx";
|
||||
int ret, idx = tx ? 0 : 1;
|
||||
struct device *cdev = cinfo->dev;
|
||||
|
|
@ -205,7 +204,9 @@ static void __iomem *shmem_setup_iomap(struct scmi_chan_info *cinfo,
|
|||
void __iomem *addr;
|
||||
u32 reg_io_width;
|
||||
|
||||
shmem = of_parse_phandle(cdev->of_node, "shmem", idx);
|
||||
struct device_node *shmem __free(device_node) = of_parse_phandle(cdev->of_node,
|
||||
"shmem", idx);
|
||||
|
||||
if (!shmem)
|
||||
return IOMEM_ERR_PTR(-ENODEV);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ struct scmi_system_power_state_notifier_payld {
|
|||
};
|
||||
|
||||
struct scmi_system_info {
|
||||
u32 version;
|
||||
bool graceful_timeout_supported;
|
||||
bool power_state_notify_cmd;
|
||||
};
|
||||
|
|
@ -141,29 +140,22 @@ static const struct scmi_protocol_events system_protocol_events = {
|
|||
|
||||
static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
int ret;
|
||||
u32 version;
|
||||
struct scmi_system_info *pinfo;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(ph->dev, "System Power Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
|
||||
|
||||
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
|
||||
if (!pinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
pinfo->version = version;
|
||||
if (PROTOCOL_REV_MAJOR(pinfo->version) >= 0x2)
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2)
|
||||
pinfo->graceful_timeout_supported = true;
|
||||
|
||||
if (!ph->hops->protocol_msg_check(ph, SYSTEM_POWER_STATE_NOTIFY, NULL))
|
||||
pinfo->power_state_notify_cmd = true;
|
||||
|
||||
return ph->set_priv(ph, pinfo, version);
|
||||
return ph->set_priv(ph, pinfo);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_system = {
|
||||
|
|
|
|||
|
|
@ -529,8 +529,9 @@ static const struct of_device_id scmi_of_match[] = {
|
|||
DEFINE_SCMI_TRANSPORT_DRIVER(scmi_optee, scmi_optee_driver, scmi_optee_desc,
|
||||
scmi_of_match, core);
|
||||
|
||||
static int scmi_optee_service_probe(struct device *dev)
|
||||
static int scmi_optee_service_probe(struct tee_client_device *scmi_pta)
|
||||
{
|
||||
struct device *dev = &scmi_pta->dev;
|
||||
struct scmi_optee_agent *agent;
|
||||
struct tee_context *tee_ctx;
|
||||
int ret;
|
||||
|
|
@ -578,24 +579,22 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_optee_service_remove(struct device *dev)
|
||||
static void scmi_optee_service_remove(struct tee_client_device *scmi_pta)
|
||||
{
|
||||
struct scmi_optee_agent *agent = scmi_optee_private;
|
||||
|
||||
if (!scmi_optee_private)
|
||||
return -EINVAL;
|
||||
return;
|
||||
|
||||
platform_driver_unregister(&scmi_optee_driver);
|
||||
|
||||
if (!list_empty(&scmi_optee_private->channel_list))
|
||||
return -EBUSY;
|
||||
return;
|
||||
|
||||
/* Ensure cleared reference is visible before resources are released */
|
||||
smp_store_mb(scmi_optee_private, NULL);
|
||||
|
||||
tee_client_close_context(agent->tee_ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct tee_client_device_id scmi_optee_service_id[] = {
|
||||
|
|
@ -609,26 +608,15 @@ static const struct tee_client_device_id scmi_optee_service_id[] = {
|
|||
MODULE_DEVICE_TABLE(tee, scmi_optee_service_id);
|
||||
|
||||
static struct tee_client_driver scmi_optee_service_driver = {
|
||||
.id_table = scmi_optee_service_id,
|
||||
.driver = {
|
||||
.probe = scmi_optee_service_probe,
|
||||
.remove = scmi_optee_service_remove,
|
||||
.id_table = scmi_optee_service_id,
|
||||
.driver = {
|
||||
.name = "scmi-optee",
|
||||
.bus = &tee_bus_type,
|
||||
.probe = scmi_optee_service_probe,
|
||||
.remove = scmi_optee_service_remove,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init scmi_transport_optee_init(void)
|
||||
{
|
||||
return driver_register(&scmi_optee_service_driver.driver);
|
||||
}
|
||||
module_init(scmi_transport_optee_init);
|
||||
|
||||
static void __exit scmi_transport_optee_exit(void)
|
||||
{
|
||||
driver_unregister(&scmi_optee_service_driver.driver);
|
||||
}
|
||||
module_exit(scmi_transport_optee_exit);
|
||||
module_tee_client_driver(scmi_optee_service_driver);
|
||||
|
||||
MODULE_AUTHOR("Etienne Carriere <etienne.carriere@foss.st.com>");
|
||||
MODULE_DESCRIPTION("SCMI OPTEE Transport driver");
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ enum scmi_imx_bbm_protocol_cmd {
|
|||
#define SCMI_IMX_BBM_EVENT_RTC_MASK GENMASK(31, 24)
|
||||
|
||||
struct scmi_imx_bbm_info {
|
||||
u32 version;
|
||||
int nr_rtc;
|
||||
int nr_gpr;
|
||||
};
|
||||
|
|
@ -345,16 +344,11 @@ static const struct scmi_imx_bbm_proto_ops scmi_imx_bbm_proto_ops = {
|
|||
|
||||
static int scmi_imx_bbm_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
u32 version;
|
||||
int ret;
|
||||
struct scmi_imx_bbm_info *binfo;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(ph->dev, "NXP SM BBM Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
|
||||
|
||||
binfo = devm_kzalloc(ph->dev, sizeof(*binfo), GFP_KERNEL);
|
||||
if (!binfo)
|
||||
|
|
@ -364,7 +358,7 @@ static int scmi_imx_bbm_protocol_init(const struct scmi_protocol_handle *ph)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ph->set_priv(ph, binfo, version);
|
||||
return ph->set_priv(ph, binfo);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_imx_bbm = {
|
||||
|
|
|
|||
|
|
@ -233,15 +233,10 @@ static int scmi_imx_cpu_attributes_get(const struct scmi_protocol_handle *ph,
|
|||
static int scmi_imx_cpu_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
struct scmi_imx_cpu_info *info;
|
||||
u32 version;
|
||||
int ret, i;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(ph->dev, "NXP SM CPU Protocol Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
|
||||
|
||||
info = devm_kzalloc(ph->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
|
|
@ -257,7 +252,7 @@ static int scmi_imx_cpu_protocol_init(const struct scmi_protocol_handle *ph)
|
|||
return ret;
|
||||
}
|
||||
|
||||
return ph->set_priv(ph, info, version);
|
||||
return ph->set_priv(ph, info);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_imx_cpu = {
|
||||
|
|
|
|||
|
|
@ -226,15 +226,10 @@ static int scmi_imx_lmm_protocol_attributes_get(const struct scmi_protocol_handl
|
|||
static int scmi_imx_lmm_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
struct scmi_imx_lmm_priv *info;
|
||||
u32 version;
|
||||
int ret;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(ph->dev, "NXP SM LMM Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
|
||||
|
||||
info = devm_kzalloc(ph->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
|
|
@ -244,7 +239,7 @@ static int scmi_imx_lmm_protocol_init(const struct scmi_protocol_handle *ph)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ph->set_priv(ph, info, version);
|
||||
return ph->set_priv(ph, info);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_imx_lmm = {
|
||||
|
|
|
|||
|
|
@ -28,11 +28,11 @@ enum scmi_imx_misc_protocol_cmd {
|
|||
SCMI_IMX_MISC_DISCOVER_BUILD_INFO = 0x6,
|
||||
SCMI_IMX_MISC_CTRL_NOTIFY = 0x8,
|
||||
SCMI_IMX_MISC_CFG_INFO_GET = 0xC,
|
||||
SCMI_IMX_MISC_SYSLOG_GET = 0xD,
|
||||
SCMI_IMX_MISC_BOARD_INFO = 0xE,
|
||||
};
|
||||
|
||||
struct scmi_imx_misc_info {
|
||||
u32 version;
|
||||
u32 nr_dev_ctrl;
|
||||
u32 nr_brd_ctrl;
|
||||
u32 nr_reason;
|
||||
|
|
@ -89,6 +89,19 @@ struct scmi_imx_misc_cfg_info_out {
|
|||
u8 cfgname[MISC_MAX_CFGNAME];
|
||||
};
|
||||
|
||||
struct scmi_imx_misc_syslog_in {
|
||||
__le32 flags;
|
||||
__le32 index;
|
||||
};
|
||||
|
||||
#define REMAINING(x) le32_get_bits((x), GENMASK(31, 20))
|
||||
#define RETURNED(x) le32_get_bits((x), GENMASK(11, 0))
|
||||
|
||||
struct scmi_imx_misc_syslog_out {
|
||||
__le32 numlogflags;
|
||||
__le32 syslog[];
|
||||
};
|
||||
|
||||
static int scmi_imx_misc_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_imx_misc_info *mi)
|
||||
{
|
||||
|
|
@ -371,24 +384,88 @@ static int scmi_imx_misc_cfg_info_get(const struct scmi_protocol_handle *ph)
|
|||
return ret;
|
||||
}
|
||||
|
||||
struct scmi_imx_misc_syslog_ipriv {
|
||||
u32 *array;
|
||||
u16 *size;
|
||||
};
|
||||
|
||||
static void iter_misc_syslog_prepare_message(void *message, u32 desc_index,
|
||||
const void *priv)
|
||||
{
|
||||
struct scmi_imx_misc_syslog_in *msg = message;
|
||||
|
||||
msg->flags = cpu_to_le32(0);
|
||||
msg->index = cpu_to_le32(desc_index);
|
||||
}
|
||||
|
||||
static int iter_misc_syslog_update_state(struct scmi_iterator_state *st,
|
||||
const void *response, void *priv)
|
||||
{
|
||||
const struct scmi_imx_misc_syslog_out *r = response;
|
||||
struct scmi_imx_misc_syslog_ipriv *p = priv;
|
||||
|
||||
st->num_returned = RETURNED(r->numlogflags);
|
||||
st->num_remaining = REMAINING(r->numlogflags);
|
||||
*p->size = st->num_returned + st->num_remaining;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
iter_misc_syslog_process_response(const struct scmi_protocol_handle *ph,
|
||||
const void *response,
|
||||
struct scmi_iterator_state *st, void *priv)
|
||||
{
|
||||
const struct scmi_imx_misc_syslog_out *r = response;
|
||||
struct scmi_imx_misc_syslog_ipriv *p = priv;
|
||||
|
||||
p->array[st->desc_index + st->loop_idx] =
|
||||
le32_to_cpu(r->syslog[st->loop_idx]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_imx_misc_syslog_get(const struct scmi_protocol_handle *ph, u16 *size,
|
||||
void *array)
|
||||
{
|
||||
struct scmi_iterator_ops ops = {
|
||||
.prepare_message = iter_misc_syslog_prepare_message,
|
||||
.update_state = iter_misc_syslog_update_state,
|
||||
.process_response = iter_misc_syslog_process_response,
|
||||
};
|
||||
struct scmi_imx_misc_syslog_ipriv ipriv = {
|
||||
.array = array,
|
||||
.size = size,
|
||||
};
|
||||
void *iter;
|
||||
|
||||
if (!array || !size || !*size)
|
||||
return -EINVAL;
|
||||
|
||||
iter = ph->hops->iter_response_init(ph, &ops, *size, SCMI_IMX_MISC_SYSLOG_GET,
|
||||
sizeof(struct scmi_imx_misc_syslog_in),
|
||||
&ipriv);
|
||||
if (IS_ERR(iter))
|
||||
return PTR_ERR(iter);
|
||||
|
||||
/* If firmware return NOT SUPPORTED, propagate value to caller */
|
||||
return ph->hops->iter_response_run(iter);
|
||||
}
|
||||
|
||||
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,
|
||||
.misc_ctrl_req_notify = scmi_imx_misc_ctrl_notify,
|
||||
.misc_syslog = scmi_imx_misc_syslog_get,
|
||||
};
|
||||
|
||||
static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
struct scmi_imx_misc_info *minfo;
|
||||
u32 version;
|
||||
int ret;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(ph->dev, "NXP SM MISC Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
|
||||
|
||||
minfo = devm_kzalloc(ph->dev, sizeof(*minfo), GFP_KERNEL);
|
||||
if (!minfo)
|
||||
|
|
@ -410,7 +487,7 @@ static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph)
|
|||
if (ret && ret != -EOPNOTSUPP)
|
||||
return ret;
|
||||
|
||||
return ph->set_priv(ph, minfo, version);
|
||||
return ph->set_priv(ph, minfo);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_imx_misc = {
|
||||
|
|
|
|||
|
|
@ -66,7 +66,6 @@ struct scmi_resp_voltage_level_set_complete {
|
|||
};
|
||||
|
||||
struct voltage_info {
|
||||
unsigned int version;
|
||||
unsigned int num_domains;
|
||||
struct scmi_voltage_info *domains;
|
||||
};
|
||||
|
|
@ -243,7 +242,7 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph,
|
|||
* If supported overwrite short name with the extended one;
|
||||
* on error just carry on and use already provided short name.
|
||||
*/
|
||||
if (PROTOCOL_REV_MAJOR(vinfo->version) >= 0x2) {
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2) {
|
||||
if (SUPPORTS_EXTENDED_NAMES(attributes))
|
||||
ph->hops->extended_name_get(ph,
|
||||
VOLTAGE_DOMAIN_NAME_GET,
|
||||
|
|
@ -405,20 +404,14 @@ static const struct scmi_voltage_proto_ops voltage_proto_ops = {
|
|||
static int scmi_voltage_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
int ret;
|
||||
u32 version;
|
||||
struct voltage_info *vinfo;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(ph->dev, "Voltage Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
|
||||
|
||||
vinfo = devm_kzalloc(ph->dev, sizeof(*vinfo), GFP_KERNEL);
|
||||
if (!vinfo)
|
||||
return -ENOMEM;
|
||||
vinfo->version = version;
|
||||
|
||||
ret = scmi_protocol_attributes_get(ph, vinfo);
|
||||
if (ret)
|
||||
|
|
@ -437,7 +430,7 @@ static int scmi_voltage_protocol_init(const struct scmi_protocol_handle *ph)
|
|||
dev_warn(ph->dev, "No Voltage domains found.\n");
|
||||
}
|
||||
|
||||
return ph->set_priv(ph, vinfo, version);
|
||||
return ph->set_priv(ph, vinfo);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_voltage = {
|
||||
|
|
|
|||
|
|
@ -181,9 +181,9 @@ static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
|
|||
return (ver->impl_id == TEE_IMPL_ID_OPTEE);
|
||||
}
|
||||
|
||||
static int tee_bnxt_fw_probe(struct device *dev)
|
||||
static int tee_bnxt_fw_probe(struct tee_client_device *bnxt_device)
|
||||
{
|
||||
struct tee_client_device *bnxt_device = to_tee_client_device(dev);
|
||||
struct device *dev = &bnxt_device->dev;
|
||||
int ret, err = -ENODEV;
|
||||
struct tee_ioctl_open_session_arg sess_arg;
|
||||
struct tee_shm *fw_shm_pool;
|
||||
|
|
@ -231,17 +231,15 @@ out_ctx:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int tee_bnxt_fw_remove(struct device *dev)
|
||||
static void tee_bnxt_fw_remove(struct tee_client_device *bnxt_device)
|
||||
{
|
||||
tee_shm_free(pvt_data.fw_shm_pool);
|
||||
tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
|
||||
tee_client_close_context(pvt_data.ctx);
|
||||
pvt_data.ctx = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tee_bnxt_fw_shutdown(struct device *dev)
|
||||
static void tee_bnxt_fw_shutdown(struct tee_client_device *bnxt_device)
|
||||
{
|
||||
tee_shm_free(pvt_data.fw_shm_pool);
|
||||
tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
|
||||
|
|
@ -258,28 +256,16 @@ static const struct tee_client_device_id tee_bnxt_fw_id_table[] = {
|
|||
MODULE_DEVICE_TABLE(tee, tee_bnxt_fw_id_table);
|
||||
|
||||
static struct tee_client_driver tee_bnxt_fw_driver = {
|
||||
.probe = tee_bnxt_fw_probe,
|
||||
.remove = tee_bnxt_fw_remove,
|
||||
.shutdown = tee_bnxt_fw_shutdown,
|
||||
.id_table = tee_bnxt_fw_id_table,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.bus = &tee_bus_type,
|
||||
.probe = tee_bnxt_fw_probe,
|
||||
.remove = tee_bnxt_fw_remove,
|
||||
.shutdown = tee_bnxt_fw_shutdown,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init tee_bnxt_fw_mod_init(void)
|
||||
{
|
||||
return driver_register(&tee_bnxt_fw_driver.driver);
|
||||
}
|
||||
|
||||
static void __exit tee_bnxt_fw_mod_exit(void)
|
||||
{
|
||||
driver_unregister(&tee_bnxt_fw_driver.driver);
|
||||
}
|
||||
|
||||
module_init(tee_bnxt_fw_mod_init);
|
||||
module_exit(tee_bnxt_fw_mod_exit);
|
||||
module_tee_client_driver(tee_bnxt_fw_driver);
|
||||
|
||||
MODULE_AUTHOR("Vikas Gupta <vikas.gupta@broadcom.com>");
|
||||
MODULE_DESCRIPTION("Broadcom bnxt firmware manager");
|
||||
|
|
|
|||
|
|
@ -520,8 +520,9 @@ static void tee_stmm_restore_efivars_generic_ops(void)
|
|||
efivars_generic_ops_register();
|
||||
}
|
||||
|
||||
static int tee_stmm_efi_probe(struct device *dev)
|
||||
static int tee_stmm_efi_probe(struct tee_client_device *tee_dev)
|
||||
{
|
||||
struct device *dev = &tee_dev->dev;
|
||||
struct tee_ioctl_open_session_arg sess_arg;
|
||||
efi_status_t ret;
|
||||
int rc;
|
||||
|
|
@ -571,37 +572,23 @@ static int tee_stmm_efi_probe(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tee_stmm_efi_remove(struct device *dev)
|
||||
static void tee_stmm_efi_remove(struct tee_client_device *dev)
|
||||
{
|
||||
tee_stmm_restore_efivars_generic_ops();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(tee, tee_stmm_efi_id_table);
|
||||
|
||||
static struct tee_client_driver tee_stmm_efi_driver = {
|
||||
.id_table = tee_stmm_efi_id_table,
|
||||
.probe = tee_stmm_efi_probe,
|
||||
.remove = tee_stmm_efi_remove,
|
||||
.driver = {
|
||||
.name = "tee-stmm-efi",
|
||||
.bus = &tee_bus_type,
|
||||
.probe = tee_stmm_efi_probe,
|
||||
.remove = tee_stmm_efi_remove,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init tee_stmm_efi_mod_init(void)
|
||||
{
|
||||
return driver_register(&tee_stmm_efi_driver.driver);
|
||||
}
|
||||
|
||||
static void __exit tee_stmm_efi_mod_exit(void)
|
||||
{
|
||||
driver_unregister(&tee_stmm_efi_driver.driver);
|
||||
}
|
||||
|
||||
module_init(tee_stmm_efi_mod_init);
|
||||
module_exit(tee_stmm_efi_mod_exit);
|
||||
module_tee_client_driver(tee_stmm_efi_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Ilias Apalodimas <ilias.apalodimas@linaro.org>");
|
||||
|
|
|
|||
|
|
@ -3,12 +3,16 @@
|
|||
* Copyright 2024 NXP
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device/devres.h>
|
||||
#include <linux/firmware/imx/sm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/scmi_imx_protocol.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
static const struct scmi_imx_misc_proto_ops *imx_misc_ctrl_ops;
|
||||
static struct scmi_protocol_handle *ph;
|
||||
|
|
@ -44,10 +48,38 @@ static int scmi_imx_misc_ctrl_notifier(struct notifier_block *nb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int syslog_show(struct seq_file *file, void *priv)
|
||||
{
|
||||
/* 4KB is large enough for syslog */
|
||||
void *syslog __free(kfree) = kmalloc(SZ_4K, GFP_KERNEL);
|
||||
/* syslog API use num words, not num bytes */
|
||||
u16 size = SZ_4K / 4;
|
||||
int ret;
|
||||
|
||||
if (!ph)
|
||||
return -ENODEV;
|
||||
|
||||
ret = imx_misc_ctrl_ops->misc_syslog(ph, &size, syslog);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
seq_hex_dump(file, " ", DUMP_PREFIX_NONE, 16, sizeof(u32), syslog, size * 4, false);
|
||||
seq_putc(file, '\n');
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(syslog);
|
||||
|
||||
static void scmi_imx_misc_put(void *p)
|
||||
{
|
||||
debugfs_remove((struct dentry *)p);
|
||||
}
|
||||
|
||||
static int scmi_imx_misc_ctrl_probe(struct scmi_device *sdev)
|
||||
{
|
||||
const struct scmi_handle *handle = sdev->handle;
|
||||
struct device_node *np = sdev->dev.of_node;
|
||||
struct dentry *scmi_imx_dentry;
|
||||
u32 src_id, flags;
|
||||
int ret, i, num;
|
||||
|
||||
|
|
@ -98,7 +130,10 @@ static int scmi_imx_misc_ctrl_probe(struct scmi_device *sdev)
|
|||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
scmi_imx_dentry = debugfs_create_dir("scmi_imx", NULL);
|
||||
debugfs_create_file("syslog", 0444, scmi_imx_dentry, &sdev->dev, &syslog_fops);
|
||||
|
||||
return devm_add_action_or_reset(&sdev->dev, scmi_imx_misc_put, scmi_imx_dentry);
|
||||
}
|
||||
|
||||
static const struct scmi_device_id scmi_id_table[] = {
|
||||
|
|
|
|||
|
|
@ -27,21 +27,29 @@
|
|||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/remoteproc.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
#include "qcom_scm.h"
|
||||
#include "qcom_tzmem.h"
|
||||
|
||||
static u32 download_mode;
|
||||
|
||||
#define GIC_SPI_BASE 32
|
||||
#define GIC_MAX_SPI 1019 // SPIs in GICv3 spec range from 32..1019
|
||||
#define GIC_ESPI_BASE 4096
|
||||
#define GIC_MAX_ESPI 5119 // ESPIs in GICv3 spec range from 4096..5119
|
||||
|
||||
struct qcom_scm {
|
||||
struct device *dev;
|
||||
struct clk *core_clk;
|
||||
struct clk *iface_clk;
|
||||
struct clk *bus_clk;
|
||||
struct icc_path *path;
|
||||
struct completion waitq_comp;
|
||||
struct completion *waitq_comps;
|
||||
struct reset_controller_dev reset;
|
||||
|
||||
/* control access to the interconnect path */
|
||||
|
|
@ -51,6 +59,7 @@ struct qcom_scm {
|
|||
u64 dload_mode_addr;
|
||||
|
||||
struct qcom_tzmem_pool *mempool;
|
||||
unsigned int wq_cnt;
|
||||
};
|
||||
|
||||
struct qcom_scm_current_perm_info {
|
||||
|
|
@ -111,6 +120,8 @@ enum qcom_scm_qseecom_tz_cmd_info {
|
|||
QSEECOM_TZ_CMD_INFO_VERSION = 3,
|
||||
};
|
||||
|
||||
#define RSCTABLE_BUFFER_NOT_SUFFICIENT 20
|
||||
|
||||
#define QSEECOM_MAX_APP_NAME_SIZE 64
|
||||
#define SHMBRIDGE_RESULT_NOTSUPP 4
|
||||
|
||||
|
|
@ -130,6 +141,8 @@ static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = {
|
|||
#define QCOM_DLOAD_MINIDUMP 2
|
||||
#define QCOM_DLOAD_BOTHDUMP 3
|
||||
|
||||
#define QCOM_SCM_DEFAULT_WAITQ_COUNT 1
|
||||
|
||||
static const char * const qcom_scm_convention_names[] = {
|
||||
[SMC_CONVENTION_UNKNOWN] = "unknown",
|
||||
[SMC_CONVENTION_ARM_32] = "smc arm 32",
|
||||
|
|
@ -558,16 +571,105 @@ static void qcom_scm_set_download_mode(u32 dload_mode)
|
|||
dev_err(__scm->dev, "failed to set download mode: %d\n", ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_qcom_scm_pas_context_alloc() - Allocate peripheral authentication service
|
||||
* context for a given peripheral
|
||||
*
|
||||
* PAS context is device-resource managed, so the caller does not need
|
||||
* to worry about freeing the context memory.
|
||||
*
|
||||
* @dev: PAS firmware device
|
||||
* @pas_id: peripheral authentication service id
|
||||
* @mem_phys: Subsystem reserve memory start address
|
||||
* @mem_size: Subsystem reserve memory size
|
||||
*
|
||||
* Returns: The new PAS context, or ERR_PTR() on failure.
|
||||
*/
|
||||
struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev,
|
||||
u32 pas_id,
|
||||
phys_addr_t mem_phys,
|
||||
size_t mem_size)
|
||||
{
|
||||
struct qcom_scm_pas_context *ctx;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ctx->dev = dev;
|
||||
ctx->pas_id = pas_id;
|
||||
ctx->mem_phys = mem_phys;
|
||||
ctx->mem_size = mem_size;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc);
|
||||
|
||||
static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys,
|
||||
struct qcom_scm_res *res)
|
||||
{
|
||||
struct qcom_scm_desc desc = {
|
||||
.svc = QCOM_SCM_SVC_PIL,
|
||||
.cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE,
|
||||
.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW),
|
||||
.args[0] = pas_id,
|
||||
.owner = ARM_SMCCC_OWNER_SIP,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = qcom_scm_clk_enable();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qcom_scm_bw_enable();
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
desc.args[1] = mdata_phys;
|
||||
|
||||
ret = qcom_scm_call(__scm->dev, &desc, res);
|
||||
qcom_scm_bw_disable();
|
||||
|
||||
disable_clk:
|
||||
qcom_scm_clk_disable();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx,
|
||||
const void *metadata, size_t size)
|
||||
{
|
||||
struct qcom_scm_res res;
|
||||
phys_addr_t mdata_phys;
|
||||
void *mdata_buf;
|
||||
int ret;
|
||||
|
||||
mdata_buf = qcom_tzmem_alloc(__scm->mempool, size, GFP_KERNEL);
|
||||
if (!mdata_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(mdata_buf, metadata, size);
|
||||
mdata_phys = qcom_tzmem_to_phys(mdata_buf);
|
||||
|
||||
ret = __qcom_scm_pas_init_image(ctx->pas_id, mdata_phys, &res);
|
||||
if (ret < 0)
|
||||
qcom_tzmem_free(mdata_buf);
|
||||
else
|
||||
ctx->ptr = mdata_buf;
|
||||
|
||||
return ret ? : res.result[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_scm_pas_init_image() - Initialize peripheral authentication service
|
||||
* state machine for a given peripheral, using the
|
||||
* metadata
|
||||
* @peripheral: peripheral id
|
||||
* @pas_id: peripheral authentication service id
|
||||
* @metadata: pointer to memory containing ELF header, program header table
|
||||
* and optional blob of data used for authenticating the metadata
|
||||
* and the rest of the firmware
|
||||
* @size: size of the metadata
|
||||
* @ctx: optional metadata context
|
||||
* @ctx: optional pas context
|
||||
*
|
||||
* Return: 0 on success.
|
||||
*
|
||||
|
|
@ -575,20 +677,16 @@ static void qcom_scm_set_download_mode(u32 dload_mode)
|
|||
* track the metadata allocation, this needs to be released by invoking
|
||||
* qcom_scm_pas_metadata_release() by the caller.
|
||||
*/
|
||||
int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
|
||||
struct qcom_scm_pas_metadata *ctx)
|
||||
int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
|
||||
struct qcom_scm_pas_context *ctx)
|
||||
{
|
||||
struct qcom_scm_res res;
|
||||
dma_addr_t mdata_phys;
|
||||
void *mdata_buf;
|
||||
int ret;
|
||||
struct qcom_scm_desc desc = {
|
||||
.svc = QCOM_SCM_SVC_PIL,
|
||||
.cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE,
|
||||
.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW),
|
||||
.args[0] = peripheral,
|
||||
.owner = ARM_SMCCC_OWNER_SIP,
|
||||
};
|
||||
struct qcom_scm_res res;
|
||||
|
||||
if (ctx && ctx->use_tzmem)
|
||||
return qcom_scm_pas_prep_and_init_image(ctx, metadata, size);
|
||||
|
||||
/*
|
||||
* During the scm call memory protection will be enabled for the meta
|
||||
|
|
@ -609,23 +707,7 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
|
|||
|
||||
memcpy(mdata_buf, metadata, size);
|
||||
|
||||
ret = qcom_scm_clk_enable();
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = qcom_scm_bw_enable();
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
desc.args[1] = mdata_phys;
|
||||
|
||||
ret = qcom_scm_call(__scm->dev, &desc, &res);
|
||||
qcom_scm_bw_disable();
|
||||
|
||||
disable_clk:
|
||||
qcom_scm_clk_disable();
|
||||
|
||||
out:
|
||||
ret = __qcom_scm_pas_init_image(pas_id, mdata_phys, &res);
|
||||
if (ret < 0 || !ctx) {
|
||||
dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
|
||||
} else if (ctx) {
|
||||
|
|
@ -640,38 +722,39 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image);
|
|||
|
||||
/**
|
||||
* qcom_scm_pas_metadata_release() - release metadata context
|
||||
* @ctx: metadata context
|
||||
* @ctx: pas context
|
||||
*/
|
||||
void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx)
|
||||
void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx)
|
||||
{
|
||||
if (!ctx->ptr)
|
||||
return;
|
||||
|
||||
dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys);
|
||||
if (ctx->use_tzmem)
|
||||
qcom_tzmem_free(ctx->ptr);
|
||||
else
|
||||
dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys);
|
||||
|
||||
ctx->ptr = NULL;
|
||||
ctx->phys = 0;
|
||||
ctx->size = 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release);
|
||||
|
||||
/**
|
||||
* qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral
|
||||
* for firmware loading
|
||||
* @peripheral: peripheral id
|
||||
* @pas_id: peripheral authentication service id
|
||||
* @addr: start address of memory area to prepare
|
||||
* @size: size of the memory area to prepare
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
|
||||
int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
|
||||
{
|
||||
int ret;
|
||||
struct qcom_scm_desc desc = {
|
||||
.svc = QCOM_SCM_SVC_PIL,
|
||||
.cmd = QCOM_SCM_PIL_PAS_MEM_SETUP,
|
||||
.arginfo = QCOM_SCM_ARGS(3),
|
||||
.args[0] = peripheral,
|
||||
.args[0] = pas_id,
|
||||
.args[1] = addr,
|
||||
.args[2] = size,
|
||||
.owner = ARM_SMCCC_OWNER_SIP,
|
||||
|
|
@ -696,21 +779,189 @@ disable_clk:
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup);
|
||||
|
||||
static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm,
|
||||
size_t input_rt_size,
|
||||
size_t *output_rt_size)
|
||||
{
|
||||
struct qcom_scm_desc desc = {
|
||||
.svc = QCOM_SCM_SVC_PIL,
|
||||
.cmd = QCOM_SCM_PIL_PAS_GET_RSCTABLE,
|
||||
.arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL, QCOM_SCM_RO, QCOM_SCM_VAL,
|
||||
QCOM_SCM_RW, QCOM_SCM_VAL),
|
||||
.args[0] = pas_id,
|
||||
.owner = ARM_SMCCC_OWNER_SIP,
|
||||
};
|
||||
struct qcom_scm_res res;
|
||||
void *output_rt_tzm;
|
||||
int ret;
|
||||
|
||||
output_rt_tzm = qcom_tzmem_alloc(__scm->mempool, *output_rt_size, GFP_KERNEL);
|
||||
if (!output_rt_tzm)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
desc.args[1] = qcom_tzmem_to_phys(input_rt_tzm);
|
||||
desc.args[2] = input_rt_size;
|
||||
desc.args[3] = qcom_tzmem_to_phys(output_rt_tzm);
|
||||
desc.args[4] = *output_rt_size;
|
||||
|
||||
/*
|
||||
* Whether SMC fail or pass, res.result[2] will hold actual resource table
|
||||
* size.
|
||||
*
|
||||
* If passed 'output_rt_size' buffer size is not sufficient to hold the
|
||||
* resource table TrustZone sends, response code in res.result[1] as
|
||||
* RSCTABLE_BUFFER_NOT_SUFFICIENT so that caller can retry this SMC call
|
||||
* with output_rt_tzm buffer with res.result[2] size however, It should not
|
||||
* be of unresonable size.
|
||||
*/
|
||||
ret = qcom_scm_call(__scm->dev, &desc, &res);
|
||||
if (!ret && res.result[2] > SZ_1G) {
|
||||
ret = -E2BIG;
|
||||
goto free_output_rt;
|
||||
}
|
||||
|
||||
*output_rt_size = res.result[2];
|
||||
if (ret && res.result[1] == RSCTABLE_BUFFER_NOT_SUFFICIENT)
|
||||
ret = -EOVERFLOW;
|
||||
|
||||
free_output_rt:
|
||||
if (ret)
|
||||
qcom_tzmem_free(output_rt_tzm);
|
||||
|
||||
return ret ? ERR_PTR(ret) : output_rt_tzm;
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_scm_pas_get_rsc_table() - Retrieve the resource table in passed output buffer
|
||||
* for a given peripheral.
|
||||
*
|
||||
* Qualcomm remote processor may rely on both static and dynamic resources for
|
||||
* its functionality. Static resources typically refer to memory-mapped addresses
|
||||
* required by the subsystem and are often embedded within the firmware binary
|
||||
* and dynamic resources, such as shared memory in DDR etc., are determined at
|
||||
* runtime during the boot process.
|
||||
*
|
||||
* On Qualcomm Technologies devices, it's possible that static resources are not
|
||||
* embedded in the firmware binary and instead are provided by TrustZone However,
|
||||
* dynamic resources are always expected to come from TrustZone. This indicates
|
||||
* that for Qualcomm devices, all resources (static and dynamic) will be provided
|
||||
* by TrustZone via the SMC call.
|
||||
*
|
||||
* If the remote processor firmware binary does contain static resources, they
|
||||
* should be passed in input_rt. These will be forwarded to TrustZone for
|
||||
* authentication. TrustZone will then append the dynamic resources and return
|
||||
* the complete resource table in output_rt_tzm.
|
||||
*
|
||||
* If the remote processor firmware binary does not include a resource table,
|
||||
* the caller of this function should set input_rt as NULL and input_rt_size
|
||||
* as zero respectively.
|
||||
*
|
||||
* More about documentation on resource table data structures can be found in
|
||||
* include/linux/remoteproc.h
|
||||
*
|
||||
* @ctx: PAS context
|
||||
* @pas_id: peripheral authentication service id
|
||||
* @input_rt: resource table buffer which is present in firmware binary
|
||||
* @input_rt_size: size of the resource table present in firmware binary
|
||||
* @output_rt_size: TrustZone expects caller should pass worst case size for
|
||||
* the output_rt_tzm.
|
||||
*
|
||||
* Return:
|
||||
* On success, returns a pointer to the allocated buffer containing the final
|
||||
* resource table and output_rt_size will have actual resource table size from
|
||||
* TrustZone. The caller is responsible for freeing the buffer. On failure,
|
||||
* returns ERR_PTR(-errno).
|
||||
*/
|
||||
struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx,
|
||||
void *input_rt,
|
||||
size_t input_rt_size,
|
||||
size_t *output_rt_size)
|
||||
{
|
||||
struct resource_table empty_rsc = {};
|
||||
size_t size = SZ_16K;
|
||||
void *output_rt_tzm;
|
||||
void *input_rt_tzm;
|
||||
void *tbl_ptr;
|
||||
int ret;
|
||||
|
||||
ret = qcom_scm_clk_enable();
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
ret = qcom_scm_bw_enable();
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
/*
|
||||
* TrustZone can not accept buffer as NULL value as argument hence,
|
||||
* we need to pass a input buffer indicating that subsystem firmware
|
||||
* does not have resource table by filling resource table structure.
|
||||
*/
|
||||
if (!input_rt) {
|
||||
input_rt = &empty_rsc;
|
||||
input_rt_size = sizeof(empty_rsc);
|
||||
}
|
||||
|
||||
input_rt_tzm = qcom_tzmem_alloc(__scm->mempool, input_rt_size, GFP_KERNEL);
|
||||
if (!input_rt_tzm) {
|
||||
ret = -ENOMEM;
|
||||
goto disable_scm_bw;
|
||||
}
|
||||
|
||||
memcpy(input_rt_tzm, input_rt, input_rt_size);
|
||||
|
||||
output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id, input_rt_tzm,
|
||||
input_rt_size, &size);
|
||||
if (PTR_ERR(output_rt_tzm) == -EOVERFLOW)
|
||||
/* Try again with the size requested by the TZ */
|
||||
output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id,
|
||||
input_rt_tzm,
|
||||
input_rt_size,
|
||||
&size);
|
||||
if (IS_ERR(output_rt_tzm)) {
|
||||
ret = PTR_ERR(output_rt_tzm);
|
||||
goto free_input_rt;
|
||||
}
|
||||
|
||||
tbl_ptr = kzalloc(size, GFP_KERNEL);
|
||||
if (!tbl_ptr) {
|
||||
qcom_tzmem_free(output_rt_tzm);
|
||||
ret = -ENOMEM;
|
||||
goto free_input_rt;
|
||||
}
|
||||
|
||||
memcpy(tbl_ptr, output_rt_tzm, size);
|
||||
*output_rt_size = size;
|
||||
qcom_tzmem_free(output_rt_tzm);
|
||||
|
||||
free_input_rt:
|
||||
qcom_tzmem_free(input_rt_tzm);
|
||||
|
||||
disable_scm_bw:
|
||||
qcom_scm_bw_disable();
|
||||
|
||||
disable_clk:
|
||||
qcom_scm_clk_disable();
|
||||
|
||||
return ret ? ERR_PTR(ret) : tbl_ptr;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_scm_pas_get_rsc_table);
|
||||
|
||||
/**
|
||||
* qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware
|
||||
* and reset the remote processor
|
||||
* @peripheral: peripheral id
|
||||
* @pas_id: peripheral authentication service id
|
||||
*
|
||||
* Return 0 on success.
|
||||
*/
|
||||
int qcom_scm_pas_auth_and_reset(u32 peripheral)
|
||||
int qcom_scm_pas_auth_and_reset(u32 pas_id)
|
||||
{
|
||||
int ret;
|
||||
struct qcom_scm_desc desc = {
|
||||
.svc = QCOM_SCM_SVC_PIL,
|
||||
.cmd = QCOM_SCM_PIL_PAS_AUTH_AND_RESET,
|
||||
.arginfo = QCOM_SCM_ARGS(1),
|
||||
.args[0] = peripheral,
|
||||
.args[0] = pas_id,
|
||||
.owner = ARM_SMCCC_OWNER_SIP,
|
||||
};
|
||||
struct qcom_scm_res res;
|
||||
|
|
@ -733,20 +984,67 @@ disable_clk:
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset);
|
||||
|
||||
/**
|
||||
* qcom_scm_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the
|
||||
* remote processor
|
||||
*
|
||||
* @ctx: Context saved during call to qcom_scm_pas_context_init()
|
||||
*
|
||||
* This function performs the necessary steps to prepare a PAS subsystem,
|
||||
* authenticate it using the provided metadata, and initiate a reset sequence.
|
||||
*
|
||||
* It should be used when Linux is in control setting up the IOMMU hardware
|
||||
* for remote subsystem during secure firmware loading processes. The preparation
|
||||
* step sets up a shmbridge over the firmware memory before TrustZone accesses the
|
||||
* firmware memory region for authentication. The authentication step verifies
|
||||
* the integrity and authenticity of the firmware or configuration using secure
|
||||
* metadata. Finally, the reset step ensures the subsystem starts in a clean and
|
||||
* sane state.
|
||||
*
|
||||
* Return: 0 on success, negative errno on failure.
|
||||
*/
|
||||
int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx)
|
||||
{
|
||||
u64 handle;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* When Linux running @ EL1, Gunyah hypervisor running @ EL2 traps the
|
||||
* auth_and_reset call and create an shmbridge on the remote subsystem
|
||||
* memory region and then invokes a call to TrustZone to authenticate.
|
||||
*/
|
||||
if (!ctx->use_tzmem)
|
||||
return qcom_scm_pas_auth_and_reset(ctx->pas_id);
|
||||
|
||||
/*
|
||||
* When Linux runs @ EL2 Linux must create the shmbridge itself and then
|
||||
* subsequently call TrustZone for authenticate and reset.
|
||||
*/
|
||||
ret = qcom_tzmem_shm_bridge_create(ctx->mem_phys, ctx->mem_size, &handle);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qcom_scm_pas_auth_and_reset(ctx->pas_id);
|
||||
qcom_tzmem_shm_bridge_delete(handle);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_scm_pas_prepare_and_auth_reset);
|
||||
|
||||
/**
|
||||
* qcom_scm_pas_shutdown() - Shut down the remote processor
|
||||
* @peripheral: peripheral id
|
||||
* @pas_id: peripheral authentication service id
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int qcom_scm_pas_shutdown(u32 peripheral)
|
||||
int qcom_scm_pas_shutdown(u32 pas_id)
|
||||
{
|
||||
int ret;
|
||||
struct qcom_scm_desc desc = {
|
||||
.svc = QCOM_SCM_SVC_PIL,
|
||||
.cmd = QCOM_SCM_PIL_PAS_SHUTDOWN,
|
||||
.arginfo = QCOM_SCM_ARGS(1),
|
||||
.args[0] = peripheral,
|
||||
.args[0] = pas_id,
|
||||
.owner = ARM_SMCCC_OWNER_SIP,
|
||||
};
|
||||
struct qcom_scm_res res;
|
||||
|
|
@ -772,18 +1070,18 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown);
|
|||
/**
|
||||
* qcom_scm_pas_supported() - Check if the peripheral authentication service is
|
||||
* available for the given peripherial
|
||||
* @peripheral: peripheral id
|
||||
* @pas_id: peripheral authentication service id
|
||||
*
|
||||
* Returns true if PAS is supported for this peripheral, otherwise false.
|
||||
*/
|
||||
bool qcom_scm_pas_supported(u32 peripheral)
|
||||
bool qcom_scm_pas_supported(u32 pas_id)
|
||||
{
|
||||
int ret;
|
||||
struct qcom_scm_desc desc = {
|
||||
.svc = QCOM_SCM_SVC_PIL,
|
||||
.cmd = QCOM_SCM_PIL_PAS_IS_SUPPORTED,
|
||||
.arginfo = QCOM_SCM_ARGS(1),
|
||||
.args[0] = peripheral,
|
||||
.args[0] = pas_id,
|
||||
.owner = ARM_SMCCC_OWNER_SIP,
|
||||
};
|
||||
struct qcom_scm_res res;
|
||||
|
|
@ -2007,6 +2305,7 @@ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = {
|
|||
{ .compatible = "lenovo,yoga-slim7x" },
|
||||
{ .compatible = "microsoft,arcata", },
|
||||
{ .compatible = "microsoft,blackrock" },
|
||||
{ .compatible = "microsoft,denali", },
|
||||
{ .compatible = "microsoft,romulus13", },
|
||||
{ .compatible = "microsoft,romulus15", },
|
||||
{ .compatible = "qcom,hamoa-iot-evk" },
|
||||
|
|
@ -2208,42 +2507,107 @@ bool qcom_scm_is_available(void)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_scm_is_available);
|
||||
|
||||
static int qcom_scm_assert_valid_wq_ctx(u32 wq_ctx)
|
||||
static int qcom_scm_fill_irq_fwspec_params(struct irq_fwspec *fwspec, u32 hwirq)
|
||||
{
|
||||
/* FW currently only supports a single wq_ctx (zero).
|
||||
* TODO: Update this logic to include dynamic allocation and lookup of
|
||||
* completion structs when FW supports more wq_ctx values.
|
||||
*/
|
||||
if (wq_ctx != 0) {
|
||||
dev_err(__scm->dev, "Firmware unexpectedly passed non-zero wq_ctx\n");
|
||||
return -EINVAL;
|
||||
if (hwirq >= GIC_SPI_BASE && hwirq <= GIC_MAX_SPI) {
|
||||
fwspec->param[0] = GIC_SPI;
|
||||
fwspec->param[1] = hwirq - GIC_SPI_BASE;
|
||||
} else if (hwirq >= GIC_ESPI_BASE && hwirq <= GIC_MAX_ESPI) {
|
||||
fwspec->param[0] = GIC_ESPI;
|
||||
fwspec->param[1] = hwirq - GIC_ESPI_BASE;
|
||||
} else {
|
||||
WARN(1, "Unexpected hwirq: %d\n", hwirq);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
fwspec->param[2] = IRQ_TYPE_EDGE_RISING;
|
||||
fwspec->param_count = 3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qcom_scm_wait_for_wq_completion(u32 wq_ctx)
|
||||
static int qcom_scm_query_waitq_count(struct qcom_scm *scm)
|
||||
{
|
||||
struct qcom_scm_desc desc = {
|
||||
.svc = QCOM_SCM_SVC_WAITQ,
|
||||
.cmd = QCOM_SCM_WAITQ_GET_INFO,
|
||||
.owner = ARM_SMCCC_OWNER_SIP
|
||||
};
|
||||
struct qcom_scm_res res;
|
||||
int ret;
|
||||
|
||||
ret = qcom_scm_assert_valid_wq_ctx(wq_ctx);
|
||||
ret = qcom_scm_call_atomic(scm->dev, &desc, &res);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
wait_for_completion(&__scm->waitq_comp);
|
||||
return res.result[0] & GENMASK(7, 0);
|
||||
}
|
||||
|
||||
static int qcom_scm_get_waitq_irq(struct qcom_scm *scm)
|
||||
{
|
||||
struct qcom_scm_desc desc = {
|
||||
.svc = QCOM_SCM_SVC_WAITQ,
|
||||
.cmd = QCOM_SCM_WAITQ_GET_INFO,
|
||||
.owner = ARM_SMCCC_OWNER_SIP
|
||||
};
|
||||
struct device_node *parent_irq_node;
|
||||
struct irq_fwspec fwspec;
|
||||
struct qcom_scm_res res;
|
||||
u32 hwirq;
|
||||
int ret;
|
||||
|
||||
ret = qcom_scm_call_atomic(scm->dev, &desc, &res);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hwirq = res.result[1] & GENMASK(15, 0);
|
||||
ret = qcom_scm_fill_irq_fwspec_params(&fwspec, hwirq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
parent_irq_node = of_irq_find_parent(scm->dev->of_node);
|
||||
if (!parent_irq_node)
|
||||
return -ENODEV;
|
||||
|
||||
fwspec.fwnode = of_fwnode_handle(parent_irq_node);
|
||||
|
||||
return irq_create_fwspec_mapping(&fwspec);
|
||||
}
|
||||
|
||||
static struct completion *qcom_scm_get_completion(u32 wq_ctx)
|
||||
{
|
||||
struct completion *wq;
|
||||
|
||||
if (WARN_ON_ONCE(wq_ctx >= __scm->wq_cnt))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
wq = &__scm->waitq_comps[wq_ctx];
|
||||
|
||||
return wq;
|
||||
}
|
||||
|
||||
int qcom_scm_wait_for_wq_completion(u32 wq_ctx)
|
||||
{
|
||||
struct completion *wq;
|
||||
|
||||
wq = qcom_scm_get_completion(wq_ctx);
|
||||
if (IS_ERR(wq))
|
||||
return PTR_ERR(wq);
|
||||
|
||||
wait_for_completion_state(wq, TASK_IDLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_scm_waitq_wakeup(unsigned int wq_ctx)
|
||||
{
|
||||
int ret;
|
||||
struct completion *wq;
|
||||
|
||||
ret = qcom_scm_assert_valid_wq_ctx(wq_ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
wq = qcom_scm_get_completion(wq_ctx);
|
||||
if (IS_ERR(wq))
|
||||
return PTR_ERR(wq);
|
||||
|
||||
complete(&__scm->waitq_comp);
|
||||
complete(wq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2319,6 +2683,7 @@ static int qcom_scm_probe(struct platform_device *pdev)
|
|||
struct qcom_tzmem_pool_config pool_config;
|
||||
struct qcom_scm *scm;
|
||||
int irq, ret;
|
||||
int i;
|
||||
|
||||
scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL);
|
||||
if (!scm)
|
||||
|
|
@ -2329,7 +2694,6 @@ static int qcom_scm_probe(struct platform_device *pdev)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
init_completion(&scm->waitq_comp);
|
||||
mutex_init(&scm->scm_bw_lock);
|
||||
|
||||
scm->path = devm_of_icc_get(&pdev->dev, NULL);
|
||||
|
|
@ -2381,7 +2745,20 @@ static int qcom_scm_probe(struct platform_device *pdev)
|
|||
return dev_err_probe(scm->dev, PTR_ERR(scm->mempool),
|
||||
"Failed to create the SCM memory pool\n");
|
||||
|
||||
irq = platform_get_irq_optional(pdev, 0);
|
||||
ret = qcom_scm_query_waitq_count(scm);
|
||||
scm->wq_cnt = ret < 0 ? QCOM_SCM_DEFAULT_WAITQ_COUNT : ret;
|
||||
scm->waitq_comps = devm_kcalloc(&pdev->dev, scm->wq_cnt, sizeof(*scm->waitq_comps),
|
||||
GFP_KERNEL);
|
||||
if (!scm->waitq_comps)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < scm->wq_cnt; i++)
|
||||
init_completion(&scm->waitq_comps[i]);
|
||||
|
||||
irq = qcom_scm_get_waitq_irq(scm);
|
||||
if (irq < 0)
|
||||
irq = platform_get_irq_optional(pdev, 0);
|
||||
|
||||
if (irq < 0) {
|
||||
if (irq != -ENXIO)
|
||||
return irq;
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev);
|
|||
#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06
|
||||
#define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07
|
||||
#define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a
|
||||
#define QCOM_SCM_PIL_PAS_GET_RSCTABLE 0x21
|
||||
|
||||
#define QCOM_SCM_SVC_IO 0x05
|
||||
#define QCOM_SCM_IO_READ 0x01
|
||||
|
|
@ -152,6 +153,7 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev);
|
|||
#define QCOM_SCM_SVC_WAITQ 0x24
|
||||
#define QCOM_SCM_WAITQ_RESUME 0x02
|
||||
#define QCOM_SCM_WAITQ_GET_WQ_CTX 0x03
|
||||
#define QCOM_SCM_WAITQ_GET_INFO 0x04
|
||||
|
||||
#define QCOM_SCM_SVC_GPU 0x28
|
||||
#define QCOM_SCM_SVC_GPU_INIT_REGS 0x01
|
||||
|
|
|
|||
|
|
@ -580,13 +580,13 @@ struct ti_sci_msg_resp_get_clock_freq {
|
|||
} __packed;
|
||||
|
||||
/**
|
||||
* struct tisci_msg_req_prepare_sleep - Request for TISCI_MSG_PREPARE_SLEEP.
|
||||
* struct ti_sci_msg_req_prepare_sleep - Request for TISCI_MSG_PREPARE_SLEEP.
|
||||
*
|
||||
* @hdr TISCI header to provide ACK/NAK flags to the host.
|
||||
* @mode Low power mode to enter.
|
||||
* @ctx_lo Low 32-bits of physical pointer to address to use for context save.
|
||||
* @ctx_hi High 32-bits of physical pointer to address to use for context save.
|
||||
* @debug_flags Flags that can be set to halt the sequence during suspend or
|
||||
* @hdr: TISCI header to provide ACK/NAK flags to the host.
|
||||
* @mode: Low power mode to enter.
|
||||
* @ctx_lo: Low 32-bits of physical pointer to address to use for context save.
|
||||
* @ctx_hi: High 32-bits of physical pointer to address to use for context save.
|
||||
* @debug_flags: Flags that can be set to halt the sequence during suspend or
|
||||
* resume to allow JTAG connection and debug.
|
||||
*
|
||||
* This message is used as the first step of entering a low power mode. It
|
||||
|
|
@ -610,7 +610,7 @@ struct ti_sci_msg_req_prepare_sleep {
|
|||
} __packed;
|
||||
|
||||
/**
|
||||
* struct tisci_msg_set_io_isolation_req - Request for TI_SCI_MSG_SET_IO_ISOLATION.
|
||||
* struct ti_sci_msg_req_set_io_isolation - Request for TI_SCI_MSG_SET_IO_ISOLATION.
|
||||
*
|
||||
* @hdr: Generic header
|
||||
* @state: The deseared state of the IO isolation.
|
||||
|
|
@ -676,7 +676,7 @@ struct ti_sci_msg_req_lpm_set_device_constraint {
|
|||
* TISCI_MSG_LPM_SET_LATENCY_CONSTRAINT.
|
||||
*
|
||||
* @hdr: TISCI header to provide ACK/NAK flags to the host.
|
||||
* @wkup_latency: The maximum acceptable latency to wake up from low power mode
|
||||
* @latency: The maximum acceptable latency to wake up from low power mode
|
||||
* in milliseconds. The deeper the state, the higher the latency.
|
||||
* @state: The desired state of wakeup latency constraint: set or clear.
|
||||
* @rsvd: Reserved for future use.
|
||||
|
|
@ -855,7 +855,7 @@ struct ti_sci_msg_rm_ring_cfg_req {
|
|||
* UDMAP transmit channels mapped to source threads will have their
|
||||
* TCHAN_THRD_ID register programmed with the destination thread if the pairing
|
||||
* is successful.
|
||||
|
||||
*
|
||||
* @dst_thread: PSI-L destination thread ID within the PSI-L System thread map.
|
||||
* PSI-L destination threads start at index 0x8000. The request is NACK'd if
|
||||
* the destination thread is not greater than or equal to 0x8000.
|
||||
|
|
@ -1000,7 +1000,8 @@ struct rm_ti_sci_msg_udmap_rx_flow_opt_cfg {
|
|||
} __packed;
|
||||
|
||||
/**
|
||||
* Configures a Navigator Subsystem UDMAP transmit channel
|
||||
* struct ti_sci_msg_rm_udmap_tx_ch_cfg_req - Configures a
|
||||
* Navigator Subsystem UDMAP transmit channel
|
||||
*
|
||||
* Configures the non-real-time registers of a Navigator Subsystem UDMAP
|
||||
* transmit channel. The channel index must be assigned to the host defined
|
||||
|
|
@ -1128,7 +1129,8 @@ struct ti_sci_msg_rm_udmap_tx_ch_cfg_req {
|
|||
} __packed;
|
||||
|
||||
/**
|
||||
* Configures a Navigator Subsystem UDMAP receive channel
|
||||
* struct ti_sci_msg_rm_udmap_rx_ch_cfg_req - Configures a
|
||||
* Navigator Subsystem UDMAP receive channel
|
||||
*
|
||||
* Configures the non-real-time registers of a Navigator Subsystem UDMAP
|
||||
* receive channel. The channel index must be assigned to the host defined
|
||||
|
|
@ -1247,7 +1249,8 @@ struct ti_sci_msg_rm_udmap_rx_ch_cfg_req {
|
|||
} __packed;
|
||||
|
||||
/**
|
||||
* Configures a Navigator Subsystem UDMAP receive flow
|
||||
* struct ti_sci_msg_rm_udmap_flow_cfg_req - Configures a
|
||||
* Navigator Subsystem UDMAP receive flow
|
||||
*
|
||||
* Configures a Navigator Subsystem UDMAP receive flow's registers.
|
||||
* Configuration does not include the flow registers which handle size-based
|
||||
|
|
@ -1258,7 +1261,7 @@ struct ti_sci_msg_rm_udmap_rx_ch_cfg_req {
|
|||
*
|
||||
* @hdr: Standard TISCI header
|
||||
*
|
||||
* @valid_params
|
||||
* @valid_params:
|
||||
* Bitfield defining validity of rx flow configuration parameters. The
|
||||
* rx flow configuration fields are not valid, and will not be used for flow
|
||||
* configuration, if their corresponding valid bit is zero. Valid bit usage:
|
||||
|
|
|
|||
|
|
@ -88,7 +88,9 @@ static int omap_hwspinlock_probe(struct platform_device *pdev)
|
|||
* make sure the module is enabled and clocked before reading
|
||||
* the module SYSSTATUS register
|
||||
*/
|
||||
devm_pm_runtime_enable(&pdev->dev);
|
||||
ret = devm_pm_runtime_enable(&pdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -125,45 +125,32 @@ static const struct irq_domain_ops extirq_domain_ops = {
|
|||
static int
|
||||
ls_extirq_parse_map(struct ls_extirq_data *priv, struct device_node *node)
|
||||
{
|
||||
const __be32 *map;
|
||||
u32 mapsize;
|
||||
struct of_imap_parser imap_parser;
|
||||
struct of_imap_item imap_item;
|
||||
int ret;
|
||||
|
||||
map = of_get_property(node, "interrupt-map", &mapsize);
|
||||
if (!map)
|
||||
return -ENOENT;
|
||||
if (mapsize % sizeof(*map))
|
||||
return -EINVAL;
|
||||
mapsize /= sizeof(*map);
|
||||
ret = of_imap_parser_init(&imap_parser, node, &imap_item);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
while (mapsize) {
|
||||
for_each_of_imap_item(&imap_parser, &imap_item) {
|
||||
struct device_node *ipar;
|
||||
u32 hwirq, intsize, j;
|
||||
u32 hwirq;
|
||||
int i;
|
||||
|
||||
if (mapsize < 3)
|
||||
return -EINVAL;
|
||||
hwirq = be32_to_cpup(map);
|
||||
if (hwirq >= MAXIRQ)
|
||||
hwirq = imap_item.child_imap[0];
|
||||
if (hwirq >= MAXIRQ) {
|
||||
of_node_put(imap_item.parent_args.np);
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->nirq = max(priv->nirq, hwirq + 1);
|
||||
|
||||
ipar = of_find_node_by_phandle(be32_to_cpup(map + 2));
|
||||
map += 3;
|
||||
mapsize -= 3;
|
||||
if (!ipar)
|
||||
return -EINVAL;
|
||||
priv->map[hwirq].fwnode = &ipar->fwnode;
|
||||
ret = of_property_read_u32(ipar, "#interrupt-cells", &intsize);
|
||||
if (ret)
|
||||
return ret;
|
||||
ipar = of_node_get(imap_item.parent_args.np);
|
||||
priv->map[hwirq].fwnode = of_fwnode_handle(ipar);
|
||||
|
||||
if (intsize > mapsize)
|
||||
return -EINVAL;
|
||||
|
||||
priv->map[hwirq].param_count = intsize;
|
||||
for (j = 0; j < intsize; ++j)
|
||||
priv->map[hwirq].param[j] = be32_to_cpup(map++);
|
||||
mapsize -= intsize;
|
||||
priv->map[hwirq].param_count = imap_item.parent_args.args_count;
|
||||
for (i = 0; i < priv->map[hwirq].param_count; i++)
|
||||
priv->map[hwirq].param[i] = imap_item.parent_args.args[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,47 +142,36 @@ static const struct irq_domain_ops rza1_irqc_domain_ops = {
|
|||
static int rza1_irqc_parse_map(struct rza1_irqc_priv *priv,
|
||||
struct device_node *gic_node)
|
||||
{
|
||||
struct of_imap_parser imap_parser;
|
||||
struct device *dev = priv->dev;
|
||||
unsigned int imaplen, i, j;
|
||||
struct of_imap_item imap_item;
|
||||
struct device_node *ipar;
|
||||
const __be32 *imap;
|
||||
u32 intsize;
|
||||
unsigned int j;
|
||||
u32 i = 0;
|
||||
int ret;
|
||||
|
||||
imap = of_get_property(dev->of_node, "interrupt-map", &imaplen);
|
||||
if (!imap)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < IRQC_NUM_IRQ; i++) {
|
||||
if (imaplen < 3)
|
||||
return -EINVAL;
|
||||
ret = of_imap_parser_init(&imap_parser, dev->of_node, &imap_item);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for_each_of_imap_item(&imap_parser, &imap_item) {
|
||||
/* Check interrupt number, ignore sense */
|
||||
if (be32_to_cpup(imap) != i)
|
||||
if (imap_item.child_imap[0] != i) {
|
||||
of_node_put(imap_item.parent_args.np);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ipar = of_find_node_by_phandle(be32_to_cpup(imap + 2));
|
||||
ipar = imap_item.parent_args.np;
|
||||
if (ipar != gic_node) {
|
||||
of_node_put(ipar);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
imap += 3;
|
||||
imaplen -= 3;
|
||||
priv->map[i].args_count = imap_item.parent_args.args_count;
|
||||
for (j = 0; j < priv->map[i].args_count; j++)
|
||||
priv->map[i].args[j] = imap_item.parent_args.args[j];
|
||||
|
||||
ret = of_property_read_u32(ipar, "#interrupt-cells", &intsize);
|
||||
of_node_put(ipar);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (imaplen < intsize)
|
||||
return -EINVAL;
|
||||
|
||||
priv->map[i].args_count = intsize;
|
||||
for (j = 0; j < intsize; j++)
|
||||
priv->map[i].args[j] = be32_to_cpup(imap++);
|
||||
|
||||
imaplen -= intsize;
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/mailbox_controller.h>
|
||||
#include <linux/mailbox/mtk-cmdq-mailbox.h>
|
||||
#include <linux/of.h>
|
||||
|
|
@ -43,6 +44,13 @@
|
|||
#define GCE_CTRL_BY_SW GENMASK(2, 0)
|
||||
#define GCE_DDR_EN GENMASK(18, 16)
|
||||
|
||||
#define GCE_VM_ID_MAP(n) (0x5018 + (n) / 10 * 4)
|
||||
#define GCE_VM_ID_MAP_THR_FLD_SHIFT(n) ((n) % 10 * 3)
|
||||
#define GCE_VM_ID_MAP_HOST_VM GENMASK(2, 0)
|
||||
#define GCE_VM_CPR_GSIZE 0x50c4
|
||||
#define GCE_VM_CPR_GSIZE_FLD_SHIFT(vm_id) ((vm_id) * 4)
|
||||
#define GCE_VM_CPR_GSIZE_MAX GENMASK(3, 0)
|
||||
|
||||
#define CMDQ_THR_ACTIVE_SLOT_CYCLES 0x3200
|
||||
#define CMDQ_THR_ENABLED 0x1
|
||||
#define CMDQ_THR_DISABLED 0x0
|
||||
|
|
@ -87,23 +95,34 @@ struct cmdq {
|
|||
struct gce_plat {
|
||||
u32 thread_nr;
|
||||
u8 shift;
|
||||
dma_addr_t mminfra_offset;
|
||||
bool control_by_sw;
|
||||
bool sw_ddr_en;
|
||||
bool gce_vm;
|
||||
u32 gce_num;
|
||||
};
|
||||
|
||||
static inline u32 cmdq_convert_gce_addr(dma_addr_t addr, const struct gce_plat *pdata)
|
||||
{
|
||||
/* Convert DMA addr (PA or IOVA) to GCE readable addr */
|
||||
return addr >> pdata->shift;
|
||||
return (addr + pdata->mminfra_offset) >> pdata->shift;
|
||||
}
|
||||
|
||||
static inline dma_addr_t cmdq_revert_gce_addr(u32 addr, const struct gce_plat *pdata)
|
||||
{
|
||||
/* Revert GCE readable addr to DMA addr (PA or IOVA) */
|
||||
return (dma_addr_t)addr << pdata->shift;
|
||||
return ((dma_addr_t)addr << pdata->shift) - pdata->mminfra_offset;
|
||||
}
|
||||
|
||||
void cmdq_get_mbox_priv(struct mbox_chan *chan, struct cmdq_mbox_priv *priv)
|
||||
{
|
||||
struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox);
|
||||
|
||||
priv->shift_pa = cmdq->pdata->shift;
|
||||
priv->mminfra_offset = cmdq->pdata->mminfra_offset;
|
||||
}
|
||||
EXPORT_SYMBOL(cmdq_get_mbox_priv);
|
||||
|
||||
u8 cmdq_get_shift_pa(struct mbox_chan *chan)
|
||||
{
|
||||
struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox);
|
||||
|
|
@ -112,6 +131,45 @@ u8 cmdq_get_shift_pa(struct mbox_chan *chan)
|
|||
}
|
||||
EXPORT_SYMBOL(cmdq_get_shift_pa);
|
||||
|
||||
static void cmdq_vm_init(struct cmdq *cmdq)
|
||||
{
|
||||
int i;
|
||||
u32 vm_cpr_gsize = 0, vm_id_map = 0;
|
||||
u32 *vm_map = NULL;
|
||||
|
||||
if (!cmdq->pdata->gce_vm)
|
||||
return;
|
||||
|
||||
vm_map = kcalloc(cmdq->pdata->thread_nr, sizeof(*vm_map), GFP_KERNEL);
|
||||
if (!vm_map)
|
||||
return;
|
||||
|
||||
/* only configure the max CPR SRAM size to host vm (vm_id = 0) currently */
|
||||
vm_cpr_gsize = GCE_VM_CPR_GSIZE_MAX << GCE_VM_CPR_GSIZE_FLD_SHIFT(0);
|
||||
|
||||
/* set all thread mapping to host vm currently */
|
||||
for (i = 0; i < cmdq->pdata->thread_nr; i++)
|
||||
vm_map[i] = GCE_VM_ID_MAP_HOST_VM << GCE_VM_ID_MAP_THR_FLD_SHIFT(i);
|
||||
|
||||
/* set the amount of CPR SRAM to allocate to each VM */
|
||||
writel(vm_cpr_gsize, cmdq->base + GCE_VM_CPR_GSIZE);
|
||||
|
||||
/* config CPR_GSIZE before setting VM_ID_MAP to avoid data leakage */
|
||||
for (i = 0; i < cmdq->pdata->thread_nr; i++) {
|
||||
vm_id_map |= vm_map[i];
|
||||
/* config every 10 threads, e.g., thread id=0~9, 10~19, ..., into one register */
|
||||
if ((i + 1) % 10 == 0) {
|
||||
writel(vm_id_map, cmdq->base + GCE_VM_ID_MAP(i));
|
||||
vm_id_map = 0;
|
||||
}
|
||||
}
|
||||
/* config remaining threads settings */
|
||||
if (cmdq->pdata->thread_nr % 10 != 0)
|
||||
writel(vm_id_map, cmdq->base + GCE_VM_ID_MAP(cmdq->pdata->thread_nr - 1));
|
||||
|
||||
kfree(vm_map);
|
||||
}
|
||||
|
||||
static void cmdq_gctl_value_toggle(struct cmdq *cmdq, bool ddr_enable)
|
||||
{
|
||||
u32 val = cmdq->pdata->control_by_sw ? GCE_CTRL_BY_SW : 0;
|
||||
|
|
@ -156,6 +214,7 @@ static void cmdq_init(struct cmdq *cmdq)
|
|||
|
||||
WARN_ON(clk_bulk_enable(cmdq->pdata->gce_num, cmdq->clocks));
|
||||
|
||||
cmdq_vm_init(cmdq);
|
||||
cmdq_gctl_value_toggle(cmdq, true);
|
||||
|
||||
writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES);
|
||||
|
|
@ -782,6 +841,16 @@ static const struct gce_plat gce_plat_mt8195 = {
|
|||
.gce_num = 2
|
||||
};
|
||||
|
||||
static const struct gce_plat gce_plat_mt8196 = {
|
||||
.thread_nr = 32,
|
||||
.shift = 3,
|
||||
.mminfra_offset = SZ_2G,
|
||||
.control_by_sw = true,
|
||||
.sw_ddr_en = true,
|
||||
.gce_vm = true,
|
||||
.gce_num = 2
|
||||
};
|
||||
|
||||
static const struct of_device_id cmdq_of_ids[] = {
|
||||
{.compatible = "mediatek,mt6779-gce", .data = (void *)&gce_plat_mt6779},
|
||||
{.compatible = "mediatek,mt8173-gce", .data = (void *)&gce_plat_mt8173},
|
||||
|
|
@ -790,6 +859,7 @@ static const struct of_device_id cmdq_of_ids[] = {
|
|||
{.compatible = "mediatek,mt8188-gce", .data = (void *)&gce_plat_mt8188},
|
||||
{.compatible = "mediatek,mt8192-gce", .data = (void *)&gce_plat_mt8192},
|
||||
{.compatible = "mediatek,mt8195-gce", .data = (void *)&gce_plat_mt8195},
|
||||
{.compatible = "mediatek,mt8196-gce", .data = (void *)&gce_plat_mt8196},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cmdq_of_ids);
|
||||
|
|
|
|||
|
|
@ -595,25 +595,28 @@ static int mtk_smi_device_link_common(struct device *dev, struct device **com_de
|
|||
|
||||
smi_com_pdev = of_find_device_by_node(smi_com_node);
|
||||
of_node_put(smi_com_node);
|
||||
if (smi_com_pdev) {
|
||||
/* smi common is the supplier, Make sure it is ready before */
|
||||
if (!platform_get_drvdata(smi_com_pdev)) {
|
||||
put_device(&smi_com_pdev->dev);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
smi_com_dev = &smi_com_pdev->dev;
|
||||
link = device_link_add(dev, smi_com_dev,
|
||||
DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
|
||||
if (!link) {
|
||||
dev_err(dev, "Unable to link smi-common dev\n");
|
||||
put_device(&smi_com_pdev->dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
*com_dev = smi_com_dev;
|
||||
} else {
|
||||
if (!smi_com_pdev) {
|
||||
dev_err(dev, "Failed to get the smi_common device\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* smi common is the supplier, Make sure it is ready before */
|
||||
if (!platform_get_drvdata(smi_com_pdev)) {
|
||||
put_device(&smi_com_pdev->dev);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
smi_com_dev = &smi_com_pdev->dev;
|
||||
link = device_link_add(dev, smi_com_dev,
|
||||
DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
|
||||
if (!link) {
|
||||
dev_err(dev, "Unable to link smi-common dev\n");
|
||||
put_device(&smi_com_pdev->dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
*com_dev = smi_com_dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -674,6 +677,7 @@ static int mtk_smi_larb_probe(struct platform_device *pdev)
|
|||
err_pm_disable:
|
||||
pm_runtime_disable(dev);
|
||||
device_link_remove(dev, larb->smi_common_dev);
|
||||
put_device(larb->smi_common_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -684,6 +688,7 @@ static void mtk_smi_larb_remove(struct platform_device *pdev)
|
|||
device_link_remove(&pdev->dev, larb->smi_common_dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
component_del(&pdev->dev, &mtk_smi_larb_component_ops);
|
||||
put_device(larb->smi_common_dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused mtk_smi_larb_resume(struct device *dev)
|
||||
|
|
@ -917,6 +922,7 @@ static void mtk_smi_common_remove(struct platform_device *pdev)
|
|||
if (common->plat->type == MTK_SMI_GEN2_SUB_COMM)
|
||||
device_link_remove(&pdev->dev, common->smi_common_dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
put_device(common->smi_common_dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused mtk_smi_common_resume(struct device *dev)
|
||||
|
|
|
|||
|
|
@ -157,6 +157,76 @@ const __be32 *of_irq_parse_imap_parent(const __be32 *imap, int len, struct of_ph
|
|||
return imap;
|
||||
}
|
||||
|
||||
int of_imap_parser_init(struct of_imap_parser *parser, struct device_node *node,
|
||||
struct of_imap_item *item)
|
||||
{
|
||||
int imaplen;
|
||||
u32 tmp;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* parent_offset is the offset where the parent part is starting.
|
||||
* In other words, the offset where the parent interrupt controller
|
||||
* phandle is present.
|
||||
*
|
||||
* Compute this offset (child #interrupt-cells + child #address-cells)
|
||||
*/
|
||||
parser->parent_offset = of_bus_n_addr_cells(node);
|
||||
|
||||
ret = of_property_read_u32(node, "#interrupt-cells", &tmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
parser->parent_offset += tmp;
|
||||
|
||||
if (WARN(parser->parent_offset > ARRAY_SIZE(item->child_imap),
|
||||
"child part size = %u, cannot fit in array of %zu items",
|
||||
parser->parent_offset, ARRAY_SIZE(item->child_imap)))
|
||||
return -EINVAL;
|
||||
|
||||
parser->imap = of_get_property(node, "interrupt-map", &imaplen);
|
||||
if (!parser->imap)
|
||||
return -ENOENT;
|
||||
|
||||
imaplen /= sizeof(*parser->imap);
|
||||
parser->imap_end = parser->imap + imaplen;
|
||||
|
||||
memset(item, 0, sizeof(*item));
|
||||
item->child_imap_count = parser->parent_offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_imap_parser_init);
|
||||
|
||||
struct of_imap_item *of_imap_parser_one(struct of_imap_parser *parser,
|
||||
struct of_imap_item *item)
|
||||
{
|
||||
const __be32 *imap_parent, *imap_next;
|
||||
int i;
|
||||
|
||||
/* Release previously get parent node */
|
||||
of_node_put(item->parent_args.np);
|
||||
|
||||
if (parser->imap + parser->parent_offset + 1 >= parser->imap_end)
|
||||
return NULL;
|
||||
|
||||
imap_parent = parser->imap + parser->parent_offset;
|
||||
|
||||
imap_next = of_irq_parse_imap_parent(imap_parent,
|
||||
parser->imap_end - imap_parent,
|
||||
&item->parent_args);
|
||||
if (!imap_next)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < parser->parent_offset; i++)
|
||||
item->child_imap[i] = be32_to_cpu(*(parser->imap + i));
|
||||
|
||||
parser->imap = imap_next;
|
||||
|
||||
return item;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_imap_parser_one);
|
||||
|
||||
/**
|
||||
* of_irq_parse_raw - Low level interrupt tree parsing
|
||||
* @addr: address specifier (start of "reg" property of the device) in be32 format
|
||||
|
|
|
|||
|
|
@ -50,6 +50,15 @@
|
|||
interrupt-map = <0x5000 1 2 &test_intc0 15>;
|
||||
};
|
||||
|
||||
intmap2 {
|
||||
#interrupt-cells = <2>;
|
||||
#address-cells = <0>;
|
||||
interrupt-map = <1 11 &test_intc0 100>,
|
||||
<2 22 &test_intc1 200 201 202>,
|
||||
<3 33 &test_intc2 300 301>,
|
||||
<4 44 &test_intc2 400 401>;
|
||||
};
|
||||
|
||||
test_intc_intmap0: intc-intmap0 {
|
||||
#interrupt-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
|
|
|
|||
|
|
@ -1654,6 +1654,121 @@ static void __init of_unittest_parse_interrupts_extended(void)
|
|||
of_node_put(np);
|
||||
}
|
||||
|
||||
struct of_unittest_expected_imap_item {
|
||||
u32 child_imap_count;
|
||||
u32 child_imap[2];
|
||||
const char *parent_path;
|
||||
int parent_args_count;
|
||||
u32 parent_args[3];
|
||||
};
|
||||
|
||||
static const struct of_unittest_expected_imap_item of_unittest_expected_imap_items[] = {
|
||||
{
|
||||
.child_imap_count = 2,
|
||||
.child_imap = {1, 11},
|
||||
.parent_path = "/testcase-data/interrupts/intc0",
|
||||
.parent_args_count = 1,
|
||||
.parent_args = {100},
|
||||
}, {
|
||||
.child_imap_count = 2,
|
||||
.child_imap = {2, 22},
|
||||
.parent_path = "/testcase-data/interrupts/intc1",
|
||||
.parent_args_count = 3,
|
||||
.parent_args = {200, 201, 202},
|
||||
}, {
|
||||
.child_imap_count = 2,
|
||||
.child_imap = {3, 33},
|
||||
.parent_path = "/testcase-data/interrupts/intc2",
|
||||
.parent_args_count = 2,
|
||||
.parent_args = {300, 301},
|
||||
}, {
|
||||
.child_imap_count = 2,
|
||||
.child_imap = {4, 44},
|
||||
.parent_path = "/testcase-data/interrupts/intc2",
|
||||
.parent_args_count = 2,
|
||||
.parent_args = {400, 401},
|
||||
}
|
||||
};
|
||||
|
||||
static void __init of_unittest_parse_interrupt_map(void)
|
||||
{
|
||||
const struct of_unittest_expected_imap_item *expected_item;
|
||||
struct device_node *imap_np, *expected_parent_np;
|
||||
struct of_imap_parser imap_parser;
|
||||
struct of_imap_item imap_item;
|
||||
int count, ret, i;
|
||||
|
||||
if (of_irq_workarounds & (OF_IMAP_NO_PHANDLE | OF_IMAP_OLDWORLD_MAC))
|
||||
return;
|
||||
|
||||
imap_np = of_find_node_by_path("/testcase-data/interrupts/intmap2");
|
||||
if (!imap_np) {
|
||||
pr_err("missing testcase data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = of_imap_parser_init(&imap_parser, imap_np, &imap_item);
|
||||
if (unittest(!ret, "of_imap_parser_init(%pOF) returned error %d\n",
|
||||
imap_np, ret))
|
||||
goto end;
|
||||
|
||||
expected_item = of_unittest_expected_imap_items;
|
||||
count = 0;
|
||||
|
||||
for_each_of_imap_item(&imap_parser, &imap_item) {
|
||||
if (unittest(count < ARRAY_SIZE(of_unittest_expected_imap_items),
|
||||
"imap item number %d not expected. Max number %zu\n",
|
||||
count, ARRAY_SIZE(of_unittest_expected_imap_items) - 1)) {
|
||||
of_node_put(imap_item.parent_args.np);
|
||||
goto end;
|
||||
}
|
||||
|
||||
expected_parent_np = of_find_node_by_path(expected_item->parent_path);
|
||||
if (unittest(expected_parent_np,
|
||||
"missing dependent testcase data (%s)\n",
|
||||
expected_item->parent_path)) {
|
||||
of_node_put(imap_item.parent_args.np);
|
||||
goto end;
|
||||
}
|
||||
|
||||
unittest(imap_item.child_imap_count == expected_item->child_imap_count,
|
||||
"imap[%d] child_imap_count = %u, expected %u\n",
|
||||
count, imap_item.child_imap_count,
|
||||
expected_item->child_imap_count);
|
||||
|
||||
for (i = 0; i < expected_item->child_imap_count; i++)
|
||||
unittest(imap_item.child_imap[i] == expected_item->child_imap[i],
|
||||
"imap[%d] child_imap[%d] = %u, expected %u\n",
|
||||
count, i, imap_item.child_imap[i],
|
||||
expected_item->child_imap[i]);
|
||||
|
||||
unittest(imap_item.parent_args.np == expected_parent_np,
|
||||
"imap[%d] parent np = %pOF, expected %pOF\n",
|
||||
count, imap_item.parent_args.np, expected_parent_np);
|
||||
|
||||
unittest(imap_item.parent_args.args_count == expected_item->parent_args_count,
|
||||
"imap[%d] parent param_count = %d, expected %d\n",
|
||||
count, imap_item.parent_args.args_count,
|
||||
expected_item->parent_args_count);
|
||||
|
||||
for (i = 0; i < expected_item->parent_args_count; i++)
|
||||
unittest(imap_item.parent_args.args[i] == expected_item->parent_args[i],
|
||||
"imap[%d] parent param[%d] = %u, expected %u\n",
|
||||
count, i, imap_item.parent_args.args[i],
|
||||
expected_item->parent_args[i]);
|
||||
|
||||
of_node_put(expected_parent_np);
|
||||
count++;
|
||||
expected_item++;
|
||||
}
|
||||
|
||||
unittest(count == ARRAY_SIZE(of_unittest_expected_imap_items),
|
||||
"Missing items. %d parsed, expected %zu\n",
|
||||
count, ARRAY_SIZE(of_unittest_expected_imap_items));
|
||||
end:
|
||||
of_node_put(imap_np);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_OF_DYNAMIC)
|
||||
static void __init of_unittest_irq_refcount(void)
|
||||
{
|
||||
|
|
@ -4393,6 +4508,7 @@ static int __init of_unittest(void)
|
|||
of_unittest_changeset_prop();
|
||||
of_unittest_parse_interrupts();
|
||||
of_unittest_parse_interrupts_extended();
|
||||
of_unittest_parse_interrupt_map();
|
||||
of_unittest_irq_refcount();
|
||||
of_unittest_dma_get_max_cpu_address();
|
||||
of_unittest_parse_dma_ranges();
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
|
@ -117,8 +118,8 @@ struct qcom_pas {
|
|||
struct qcom_rproc_ssr ssr_subdev;
|
||||
struct qcom_sysmon *sysmon;
|
||||
|
||||
struct qcom_scm_pas_metadata pas_metadata;
|
||||
struct qcom_scm_pas_metadata dtb_pas_metadata;
|
||||
struct qcom_scm_pas_context *pas_ctx;
|
||||
struct qcom_scm_pas_context *dtb_pas_ctx;
|
||||
};
|
||||
|
||||
static void qcom_pas_segment_dump(struct rproc *rproc,
|
||||
|
|
@ -211,9 +212,9 @@ static int qcom_pas_unprepare(struct rproc *rproc)
|
|||
* auth_and_reset() was successful, but in other cases clean it up
|
||||
* here.
|
||||
*/
|
||||
qcom_scm_pas_metadata_release(&pas->pas_metadata);
|
||||
qcom_scm_pas_metadata_release(pas->pas_ctx);
|
||||
if (pas->dtb_pas_id)
|
||||
qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata);
|
||||
qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -239,15 +240,9 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = qcom_mdt_pas_init(pas->dev, pas->dtb_firmware, pas->dtb_firmware_name,
|
||||
pas->dtb_pas_id, pas->dtb_mem_phys,
|
||||
&pas->dtb_pas_metadata);
|
||||
if (ret)
|
||||
goto release_dtb_firmware;
|
||||
|
||||
ret = qcom_mdt_load_no_init(pas->dev, pas->dtb_firmware, pas->dtb_firmware_name,
|
||||
pas->dtb_mem_region, pas->dtb_mem_phys,
|
||||
pas->dtb_mem_size, &pas->dtb_mem_reloc);
|
||||
ret = qcom_mdt_pas_load(pas->dtb_pas_ctx, pas->dtb_firmware,
|
||||
pas->dtb_firmware_name, pas->dtb_mem_region,
|
||||
&pas->dtb_mem_reloc);
|
||||
if (ret)
|
||||
goto release_dtb_metadata;
|
||||
}
|
||||
|
|
@ -255,14 +250,28 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw)
|
|||
return 0;
|
||||
|
||||
release_dtb_metadata:
|
||||
qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata);
|
||||
|
||||
release_dtb_firmware:
|
||||
qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
|
||||
release_firmware(pas->dtb_firmware);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qcom_pas_unmap_carveout(struct rproc *rproc, phys_addr_t mem_phys, size_t size)
|
||||
{
|
||||
if (rproc->has_iommu)
|
||||
iommu_unmap(rproc->domain, mem_phys, size);
|
||||
}
|
||||
|
||||
static int qcom_pas_map_carveout(struct rproc *rproc, phys_addr_t mem_phys, size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (rproc->has_iommu)
|
||||
ret = iommu_map(rproc->domain, mem_phys, mem_phys, size,
|
||||
IOMMU_READ | IOMMU_WRITE, GFP_KERNEL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_pas_start(struct rproc *rproc)
|
||||
{
|
||||
struct qcom_pas *pas = rproc->priv;
|
||||
|
|
@ -297,54 +306,62 @@ static int qcom_pas_start(struct rproc *rproc)
|
|||
}
|
||||
|
||||
if (pas->dtb_pas_id) {
|
||||
ret = qcom_scm_pas_auth_and_reset(pas->dtb_pas_id);
|
||||
ret = qcom_pas_map_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size);
|
||||
if (ret)
|
||||
goto disable_px_supply;
|
||||
|
||||
ret = qcom_scm_pas_prepare_and_auth_reset(pas->dtb_pas_ctx);
|
||||
if (ret) {
|
||||
dev_err(pas->dev,
|
||||
"failed to authenticate dtb image and release reset\n");
|
||||
goto disable_px_supply;
|
||||
goto unmap_dtb_carveout;
|
||||
}
|
||||
}
|
||||
|
||||
ret = qcom_mdt_pas_init(pas->dev, pas->firmware, rproc->firmware, pas->pas_id,
|
||||
pas->mem_phys, &pas->pas_metadata);
|
||||
if (ret)
|
||||
goto disable_px_supply;
|
||||
|
||||
ret = qcom_mdt_load_no_init(pas->dev, pas->firmware, rproc->firmware,
|
||||
pas->mem_region, pas->mem_phys, pas->mem_size,
|
||||
&pas->mem_reloc);
|
||||
ret = qcom_mdt_pas_load(pas->pas_ctx, pas->firmware, rproc->firmware,
|
||||
pas->mem_region, &pas->mem_reloc);
|
||||
if (ret)
|
||||
goto release_pas_metadata;
|
||||
|
||||
qcom_pil_info_store(pas->info_name, pas->mem_phys, pas->mem_size);
|
||||
|
||||
ret = qcom_scm_pas_auth_and_reset(pas->pas_id);
|
||||
ret = qcom_pas_map_carveout(rproc, pas->mem_phys, pas->mem_size);
|
||||
if (ret)
|
||||
goto release_pas_metadata;
|
||||
|
||||
ret = qcom_scm_pas_prepare_and_auth_reset(pas->pas_ctx);
|
||||
if (ret) {
|
||||
dev_err(pas->dev,
|
||||
"failed to authenticate image and release reset\n");
|
||||
goto release_pas_metadata;
|
||||
goto unmap_carveout;
|
||||
}
|
||||
|
||||
ret = qcom_q6v5_wait_for_start(&pas->q6v5, msecs_to_jiffies(5000));
|
||||
if (ret == -ETIMEDOUT) {
|
||||
dev_err(pas->dev, "start timed out\n");
|
||||
qcom_scm_pas_shutdown(pas->pas_id);
|
||||
goto release_pas_metadata;
|
||||
goto unmap_carveout;
|
||||
}
|
||||
|
||||
qcom_scm_pas_metadata_release(&pas->pas_metadata);
|
||||
qcom_scm_pas_metadata_release(pas->pas_ctx);
|
||||
if (pas->dtb_pas_id)
|
||||
qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata);
|
||||
qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
|
||||
|
||||
/* firmware is used to pass reference from qcom_pas_start(), drop it now */
|
||||
pas->firmware = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
unmap_carveout:
|
||||
qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size);
|
||||
release_pas_metadata:
|
||||
qcom_scm_pas_metadata_release(&pas->pas_metadata);
|
||||
qcom_scm_pas_metadata_release(pas->pas_ctx);
|
||||
if (pas->dtb_pas_id)
|
||||
qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata);
|
||||
qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
|
||||
|
||||
unmap_dtb_carveout:
|
||||
if (pas->dtb_pas_id)
|
||||
qcom_pas_unmap_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size);
|
||||
disable_px_supply:
|
||||
if (pas->px_supply)
|
||||
regulator_disable(pas->px_supply);
|
||||
|
|
@ -400,8 +417,12 @@ static int qcom_pas_stop(struct rproc *rproc)
|
|||
ret = qcom_scm_pas_shutdown(pas->dtb_pas_id);
|
||||
if (ret)
|
||||
dev_err(pas->dev, "failed to shutdown dtb: %d\n", ret);
|
||||
|
||||
qcom_pas_unmap_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size);
|
||||
}
|
||||
|
||||
qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size);
|
||||
|
||||
handover = qcom_q6v5_unprepare(&pas->q6v5);
|
||||
if (handover)
|
||||
qcom_pas_handover(&pas->q6v5);
|
||||
|
|
@ -427,6 +448,61 @@ static void *qcom_pas_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is
|
|||
return pas->mem_region + offset;
|
||||
}
|
||||
|
||||
static int qcom_pas_parse_firmware(struct rproc *rproc, const struct firmware *fw)
|
||||
{
|
||||
struct qcom_pas *pas = rproc->priv;
|
||||
struct resource_table *table = NULL;
|
||||
size_t output_rt_size;
|
||||
void *output_rt;
|
||||
size_t table_sz;
|
||||
int ret;
|
||||
|
||||
ret = qcom_register_dump_segments(rproc, fw);
|
||||
if (ret) {
|
||||
dev_err(pas->dev, "Error in registering dump segments\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!rproc->has_iommu)
|
||||
return 0;
|
||||
|
||||
ret = rproc_elf_load_rsc_table(rproc, fw);
|
||||
if (ret)
|
||||
dev_dbg(&rproc->dev, "Failed to load resource table from firmware\n");
|
||||
|
||||
table = rproc->table_ptr;
|
||||
table_sz = rproc->table_sz;
|
||||
|
||||
/*
|
||||
* The resources consumed by Qualcomm remote processors fall into two categories:
|
||||
* static (such as the memory carveouts for the rproc firmware) and dynamic (like
|
||||
* shared memory pools). Both are managed by a Qualcomm hypervisor (such as QHEE
|
||||
* or Gunyah), if one is present. Otherwise, a resource table must be retrieved
|
||||
* via an SCM call. That table will list all dynamic resources (if any) and possibly
|
||||
* the static ones. The static resources may also come from a resource table embedded
|
||||
* in the rproc firmware instead.
|
||||
*
|
||||
* Here, we call rproc_elf_load_rsc_table() to check firmware binary has resources
|
||||
* or not and if it is not having then we pass NULL and zero as input resource
|
||||
* table pointer and size respectively to the argument of qcom_scm_pas_get_rsc_table()
|
||||
* and this is even true for Qualcomm remote processor who does follow remoteproc
|
||||
* framework.
|
||||
*/
|
||||
output_rt = qcom_scm_pas_get_rsc_table(pas->pas_ctx, table, table_sz, &output_rt_size);
|
||||
ret = IS_ERR(output_rt) ? PTR_ERR(output_rt) : 0;
|
||||
if (ret) {
|
||||
dev_err(pas->dev, "Error in getting resource table: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
kfree(rproc->cached_table);
|
||||
rproc->cached_table = output_rt;
|
||||
rproc->table_ptr = rproc->cached_table;
|
||||
rproc->table_sz = output_rt_size;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned long qcom_pas_panic(struct rproc *rproc)
|
||||
{
|
||||
struct qcom_pas *pas = rproc->priv;
|
||||
|
|
@ -439,7 +515,7 @@ static const struct rproc_ops qcom_pas_ops = {
|
|||
.start = qcom_pas_start,
|
||||
.stop = qcom_pas_stop,
|
||||
.da_to_va = qcom_pas_da_to_va,
|
||||
.parse_fw = qcom_register_dump_segments,
|
||||
.parse_fw = qcom_pas_parse_firmware,
|
||||
.load = qcom_pas_load,
|
||||
.panic = qcom_pas_panic,
|
||||
};
|
||||
|
|
@ -449,7 +525,7 @@ static const struct rproc_ops qcom_pas_minidump_ops = {
|
|||
.start = qcom_pas_start,
|
||||
.stop = qcom_pas_stop,
|
||||
.da_to_va = qcom_pas_da_to_va,
|
||||
.parse_fw = qcom_register_dump_segments,
|
||||
.parse_fw = qcom_pas_parse_firmware,
|
||||
.load = qcom_pas_load,
|
||||
.panic = qcom_pas_panic,
|
||||
.coredump = qcom_pas_minidump,
|
||||
|
|
@ -697,6 +773,7 @@ static int qcom_pas_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rproc->has_iommu = of_property_present(pdev->dev.of_node, "iommus");
|
||||
rproc->auto_boot = desc->auto_boot;
|
||||
rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
|
||||
|
||||
|
|
@ -760,6 +837,24 @@ static int qcom_pas_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
qcom_add_ssr_subdev(rproc, &pas->ssr_subdev, desc->ssr_name);
|
||||
|
||||
pas->pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->pas_id,
|
||||
pas->mem_phys, pas->mem_size);
|
||||
if (IS_ERR(pas->pas_ctx)) {
|
||||
ret = PTR_ERR(pas->pas_ctx);
|
||||
goto remove_ssr_sysmon;
|
||||
}
|
||||
|
||||
pas->dtb_pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->dtb_pas_id,
|
||||
pas->dtb_mem_phys,
|
||||
pas->dtb_mem_size);
|
||||
if (IS_ERR(pas->dtb_pas_ctx)) {
|
||||
ret = PTR_ERR(pas->dtb_pas_ctx);
|
||||
goto remove_ssr_sysmon;
|
||||
}
|
||||
|
||||
pas->pas_ctx->use_tzmem = rproc->has_iommu;
|
||||
pas->dtb_pas_ctx->use_tzmem = rproc->has_iommu;
|
||||
ret = rproc_add(rproc);
|
||||
if (ret)
|
||||
goto remove_ssr_sysmon;
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ config RESET_K210
|
|||
config RESET_K230
|
||||
tristate "Reset controller driver for Canaan Kendryte K230 SoC"
|
||||
depends on ARCH_CANAAN || COMPILE_TEST
|
||||
depends on OF
|
||||
default ARCH_CANAAN
|
||||
help
|
||||
Support for the Canaan Kendryte K230 RISC-V SoC reset controller.
|
||||
Say Y if you want to control reset signals provided by this
|
||||
|
|
@ -299,15 +299,6 @@ config RESET_SOCFPGA
|
|||
This enables the reset driver for the SoCFPGA ARMv7 platforms. This
|
||||
driver gets initialized early during platform init calls.
|
||||
|
||||
config RESET_SPACEMIT
|
||||
tristate "SpacemiT reset driver"
|
||||
depends on ARCH_SPACEMIT || COMPILE_TEST
|
||||
select AUXILIARY_BUS
|
||||
default ARCH_SPACEMIT
|
||||
help
|
||||
This enables the reset controller driver for SpacemiT SoCs,
|
||||
including the K1.
|
||||
|
||||
config RESET_SUNPLUS
|
||||
bool "Sunplus SoCs Reset Driver" if COMPILE_TEST
|
||||
default ARCH_SUNPLUS
|
||||
|
|
@ -406,9 +397,10 @@ config RESET_ZYNQMP
|
|||
This enables the reset controller driver for Xilinx ZynqMP SoCs.
|
||||
|
||||
source "drivers/reset/amlogic/Kconfig"
|
||||
source "drivers/reset/hisilicon/Kconfig"
|
||||
source "drivers/reset/spacemit/Kconfig"
|
||||
source "drivers/reset/starfive/Kconfig"
|
||||
source "drivers/reset/sti/Kconfig"
|
||||
source "drivers/reset/hisilicon/Kconfig"
|
||||
source "drivers/reset/tegra/Kconfig"
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
obj-y += core.o
|
||||
obj-y += amlogic/
|
||||
obj-y += hisilicon/
|
||||
obj-y += spacemit/
|
||||
obj-y += starfive/
|
||||
obj-y += sti/
|
||||
obj-y += tegra/
|
||||
|
|
@ -38,7 +39,6 @@ obj-$(CONFIG_RESET_RZV2H_USB2PHY) += reset-rzv2h-usb2phy.o
|
|||
obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
|
||||
obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
|
||||
obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
|
||||
obj-$(CONFIG_RESET_SPACEMIT) += reset-spacemit.o
|
||||
obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o
|
||||
obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
|
||||
obj-$(CONFIG_RESET_TH1520) += reset-th1520.o
|
||||
|
|
|
|||
|
|
@ -868,11 +868,11 @@ static int reset_add_gpio_aux_device(struct device *parent,
|
|||
*/
|
||||
static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
|
||||
{
|
||||
struct property_entry properties[2] = { };
|
||||
struct property_entry properties[3] = { };
|
||||
unsigned int offset, of_flags, lflags;
|
||||
struct reset_gpio_lookup *rgpio_dev;
|
||||
struct device *parent;
|
||||
int id, ret;
|
||||
int id, ret, prop = 0;
|
||||
|
||||
/*
|
||||
* Currently only #gpio-cells=2 is supported with the meaning of:
|
||||
|
|
@ -923,7 +923,8 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
|
|||
|
||||
lflags = GPIO_PERSISTENT | (of_flags & GPIO_ACTIVE_LOW);
|
||||
parent = gpio_device_to_device(gdev);
|
||||
properties[0] = PROPERTY_ENTRY_GPIO("reset-gpios", parent->fwnode, offset, lflags);
|
||||
properties[prop++] = PROPERTY_ENTRY_STRING("compatible", "reset-gpio");
|
||||
properties[prop++] = PROPERTY_ENTRY_GPIO("reset-gpios", parent->fwnode, offset, lflags);
|
||||
|
||||
id = ida_alloc(&reset_gpio_ida, GFP_KERNEL);
|
||||
if (id < 0)
|
||||
|
|
|
|||
|
|
@ -22,9 +22,7 @@ static int reset_gpio_assert(struct reset_controller_dev *rc, unsigned long id)
|
|||
{
|
||||
struct reset_gpio_priv *priv = rc_to_reset_gpio(rc);
|
||||
|
||||
gpiod_set_value_cansleep(priv->reset, 1);
|
||||
|
||||
return 0;
|
||||
return gpiod_set_value_cansleep(priv->reset, 1);
|
||||
}
|
||||
|
||||
static int reset_gpio_deassert(struct reset_controller_dev *rc,
|
||||
|
|
@ -32,9 +30,7 @@ static int reset_gpio_deassert(struct reset_controller_dev *rc,
|
|||
{
|
||||
struct reset_gpio_priv *priv = rc_to_reset_gpio(rc);
|
||||
|
||||
gpiod_set_value_cansleep(priv->reset, 0);
|
||||
|
||||
return 0;
|
||||
return gpiod_set_value_cansleep(priv->reset, 0);
|
||||
}
|
||||
|
||||
static int reset_gpio_status(struct reset_controller_dev *rc, unsigned long id)
|
||||
|
|
@ -111,6 +107,7 @@ static struct auxiliary_driver reset_gpio_driver = {
|
|||
.id_table = reset_gpio_ids,
|
||||
.driver = {
|
||||
.name = "reset-gpio",
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
module_auxiliary_driver(reset_gpio_driver);
|
||||
|
|
|
|||
|
|
@ -3,22 +3,23 @@
|
|||
* Copyright 2024 NXP
|
||||
*/
|
||||
|
||||
#include <dt-bindings/reset/fsl,imx8ulp-sim-lpav.h>
|
||||
#include <dt-bindings/reset/imx8mp-reset-audiomix.h>
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
#define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200
|
||||
#define IMX8MP_AUDIOMIX_EARC_RESET_MASK BIT(0)
|
||||
#define IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK BIT(1)
|
||||
|
||||
#define IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET 0x108
|
||||
#define IMX8MP_AUDIOMIX_DSP_RUNSTALL_MASK BIT(5)
|
||||
|
||||
#define IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET 0x8
|
||||
|
||||
struct imx8mp_reset_map {
|
||||
unsigned int offset;
|
||||
|
|
@ -26,28 +27,76 @@ struct imx8mp_reset_map {
|
|||
bool active_low;
|
||||
};
|
||||
|
||||
static const struct imx8mp_reset_map reset_map[] = {
|
||||
struct imx8mp_reset_info {
|
||||
const struct imx8mp_reset_map *map;
|
||||
int num_lines;
|
||||
};
|
||||
|
||||
static const struct imx8mp_reset_map imx8mp_reset_map[] = {
|
||||
[IMX8MP_AUDIOMIX_EARC_RESET] = {
|
||||
.offset = IMX8MP_AUDIOMIX_EARC_RESET_OFFSET,
|
||||
.mask = IMX8MP_AUDIOMIX_EARC_RESET_MASK,
|
||||
.mask = BIT(0),
|
||||
.active_low = true,
|
||||
},
|
||||
[IMX8MP_AUDIOMIX_EARC_PHY_RESET] = {
|
||||
.offset = IMX8MP_AUDIOMIX_EARC_RESET_OFFSET,
|
||||
.mask = IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK,
|
||||
.mask = BIT(1),
|
||||
.active_low = true,
|
||||
},
|
||||
[IMX8MP_AUDIOMIX_DSP_RUNSTALL] = {
|
||||
.offset = IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET,
|
||||
.mask = IMX8MP_AUDIOMIX_DSP_RUNSTALL_MASK,
|
||||
.mask = BIT(5),
|
||||
.active_low = false,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct imx8mp_reset_info imx8mp_reset_info = {
|
||||
.map = imx8mp_reset_map,
|
||||
.num_lines = ARRAY_SIZE(imx8mp_reset_map),
|
||||
};
|
||||
|
||||
static const struct imx8mp_reset_map imx8ulp_reset_map[] = {
|
||||
[IMX8ULP_SIM_LPAV_HIFI4_DSP_DBG_RST] = {
|
||||
.offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
|
||||
.mask = BIT(25),
|
||||
.active_low = false,
|
||||
},
|
||||
[IMX8ULP_SIM_LPAV_HIFI4_DSP_RST] = {
|
||||
.offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
|
||||
.mask = BIT(16),
|
||||
.active_low = false,
|
||||
},
|
||||
[IMX8ULP_SIM_LPAV_HIFI4_DSP_STALL] = {
|
||||
.offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
|
||||
.mask = BIT(13),
|
||||
.active_low = false,
|
||||
},
|
||||
[IMX8ULP_SIM_LPAV_DSI_RST_BYTE_N] = {
|
||||
.offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
|
||||
.mask = BIT(5),
|
||||
.active_low = true,
|
||||
},
|
||||
[IMX8ULP_SIM_LPAV_DSI_RST_ESC_N] = {
|
||||
.offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
|
||||
.mask = BIT(4),
|
||||
.active_low = true,
|
||||
},
|
||||
[IMX8ULP_SIM_LPAV_DSI_RST_DPI_N] = {
|
||||
.offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
|
||||
.mask = BIT(3),
|
||||
.active_low = true,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct imx8mp_reset_info imx8ulp_reset_info = {
|
||||
.map = imx8ulp_reset_map,
|
||||
.num_lines = ARRAY_SIZE(imx8ulp_reset_map),
|
||||
};
|
||||
|
||||
struct imx8mp_audiomix_reset {
|
||||
struct reset_controller_dev rcdev;
|
||||
spinlock_t lock; /* protect register read-modify-write cycle */
|
||||
void __iomem *base;
|
||||
struct regmap *regmap;
|
||||
const struct imx8mp_reset_map *map;
|
||||
};
|
||||
|
||||
static struct imx8mp_audiomix_reset *to_imx8mp_audiomix_reset(struct reset_controller_dev *rcdev)
|
||||
|
|
@ -59,26 +108,15 @@ static int imx8mp_audiomix_update(struct reset_controller_dev *rcdev,
|
|||
unsigned long id, bool assert)
|
||||
{
|
||||
struct imx8mp_audiomix_reset *priv = to_imx8mp_audiomix_reset(rcdev);
|
||||
void __iomem *reg_addr = priv->base;
|
||||
unsigned int mask, offset, active_low;
|
||||
unsigned long reg, flags;
|
||||
const struct imx8mp_reset_map *reset_map = priv->map;
|
||||
unsigned int mask, offset, active_low, val;
|
||||
|
||||
mask = reset_map[id].mask;
|
||||
offset = reset_map[id].offset;
|
||||
active_low = reset_map[id].active_low;
|
||||
val = (active_low ^ assert) ? mask : ~mask;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
reg = readl(reg_addr + offset);
|
||||
if (active_low ^ assert)
|
||||
reg |= mask;
|
||||
else
|
||||
reg &= ~mask;
|
||||
writel(reg, reg_addr + offset);
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
return regmap_update_bits(priv->regmap, offset, mask, val);
|
||||
}
|
||||
|
||||
static int imx8mp_audiomix_reset_assert(struct reset_controller_dev *rcdev,
|
||||
|
|
@ -98,52 +136,96 @@ static const struct reset_control_ops imx8mp_audiomix_reset_ops = {
|
|||
.deassert = imx8mp_audiomix_reset_deassert,
|
||||
};
|
||||
|
||||
static const struct regmap_config regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
};
|
||||
|
||||
/* assumption: registered only if not using parent regmap */
|
||||
static void imx8mp_audiomix_reset_iounmap(void *data)
|
||||
{
|
||||
void __iomem *base = (void __iomem *)data;
|
||||
|
||||
iounmap(base);
|
||||
}
|
||||
|
||||
static int imx8mp_audiomix_reset_get_regmap(struct imx8mp_audiomix_reset *priv)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
int ret;
|
||||
|
||||
dev = priv->rcdev.dev;
|
||||
|
||||
/* try to use the parent's regmap */
|
||||
priv->regmap = dev_get_regmap(dev->parent, NULL);
|
||||
if (priv->regmap)
|
||||
return 0;
|
||||
|
||||
/* ... if that's not possible then initialize the regmap right now */
|
||||
base = of_iomap(dev->parent->of_node, 0);
|
||||
if (!base)
|
||||
return dev_err_probe(dev, -ENOMEM, "failed to iomap address space\n");
|
||||
|
||||
ret = devm_add_action_or_reset(dev,
|
||||
imx8mp_audiomix_reset_iounmap,
|
||||
(void __force *)base);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to register action\n");
|
||||
|
||||
priv->regmap = devm_regmap_init_mmio(dev, base, ®map_config);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->regmap),
|
||||
"failed to initialize regmap\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
|
||||
const struct auxiliary_device_id *id)
|
||||
{
|
||||
const struct imx8mp_reset_info *rinfo;
|
||||
struct imx8mp_audiomix_reset *priv;
|
||||
struct device *dev = &adev->dev;
|
||||
int ret;
|
||||
|
||||
rinfo = (void *)id->driver_data;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
priv->rcdev.owner = THIS_MODULE;
|
||||
priv->rcdev.nr_resets = ARRAY_SIZE(reset_map);
|
||||
priv->map = rinfo->map;
|
||||
priv->rcdev.nr_resets = rinfo->num_lines;
|
||||
priv->rcdev.ops = &imx8mp_audiomix_reset_ops;
|
||||
priv->rcdev.of_node = dev->parent->of_node;
|
||||
priv->rcdev.dev = dev;
|
||||
priv->rcdev.of_reset_n_cells = 1;
|
||||
priv->base = of_iomap(dev->parent->of_node, 0);
|
||||
if (!priv->base)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, priv);
|
||||
|
||||
ret = imx8mp_audiomix_reset_get_regmap(priv);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to get regmap\n");
|
||||
|
||||
ret = devm_reset_controller_register(dev, &priv->rcdev);
|
||||
if (ret)
|
||||
goto out_unmap;
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to register reset controller\n");
|
||||
|
||||
return 0;
|
||||
|
||||
out_unmap:
|
||||
iounmap(priv->base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void imx8mp_audiomix_reset_remove(struct auxiliary_device *adev)
|
||||
{
|
||||
struct imx8mp_audiomix_reset *priv = dev_get_drvdata(&adev->dev);
|
||||
|
||||
iounmap(priv->base);
|
||||
}
|
||||
|
||||
static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = {
|
||||
{
|
||||
.name = "clk_imx8mp_audiomix.reset",
|
||||
.driver_data = (kernel_ulong_t)&imx8mp_reset_info,
|
||||
},
|
||||
{
|
||||
.name = "clk_imx8ulp_sim_lpav.reset",
|
||||
.driver_data = (kernel_ulong_t)&imx8ulp_reset_info,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
|
@ -151,7 +233,6 @@ MODULE_DEVICE_TABLE(auxiliary, imx8mp_audiomix_reset_ids);
|
|||
|
||||
static struct auxiliary_driver imx8mp_audiomix_reset_driver = {
|
||||
.probe = imx8mp_audiomix_reset_probe,
|
||||
.remove = imx8mp_audiomix_reset_remove,
|
||||
.id_table = imx8mp_audiomix_reset_ids,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ struct rzg2l_usbphy_ctrl_priv {
|
|||
struct reset_control *rstc;
|
||||
void __iomem *base;
|
||||
struct platform_device *vdev;
|
||||
struct regmap_field *pwrrdy;
|
||||
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
|
@ -92,6 +93,19 @@ static int rzg2l_usbphy_ctrl_status(struct reset_controller_dev *rcdev,
|
|||
return !!(readl(priv->base + RESET) & port_mask);
|
||||
}
|
||||
|
||||
/* put pll and phy into reset state */
|
||||
static void rzg2l_usbphy_ctrl_init(struct rzg2l_usbphy_ctrl_priv *priv)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
val = readl(priv->base + RESET);
|
||||
val |= RESET_SEL_PLLRESET | RESET_PLLRESET | PHY_RESET_PORT2 | PHY_RESET_PORT1;
|
||||
writel(val, priv->base + RESET);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
#define RZG2L_USBPHY_CTRL_PWRRDY 1
|
||||
|
||||
static const struct of_device_id rzg2l_usbphy_ctrl_match_table[] = {
|
||||
|
|
@ -117,13 +131,13 @@ static const struct regmap_config rzg2l_usb_regconf = {
|
|||
.max_register = 1,
|
||||
};
|
||||
|
||||
static void rzg2l_usbphy_ctrl_set_pwrrdy(struct regmap_field *pwrrdy,
|
||||
bool power_on)
|
||||
static int rzg2l_usbphy_ctrl_set_pwrrdy(struct regmap_field *pwrrdy,
|
||||
bool power_on)
|
||||
{
|
||||
u32 val = power_on ? 0 : 1;
|
||||
|
||||
/* The initialization path guarantees that the mask is 1 bit long. */
|
||||
regmap_field_update_bits(pwrrdy, 1, val);
|
||||
return regmap_field_update_bits(pwrrdy, 1, val);
|
||||
}
|
||||
|
||||
static void rzg2l_usbphy_ctrl_pwrrdy_off(void *data)
|
||||
|
|
@ -131,13 +145,14 @@ static void rzg2l_usbphy_ctrl_pwrrdy_off(void *data)
|
|||
rzg2l_usbphy_ctrl_set_pwrrdy(data, false);
|
||||
}
|
||||
|
||||
static int rzg2l_usbphy_ctrl_pwrrdy_init(struct device *dev)
|
||||
static int rzg2l_usbphy_ctrl_pwrrdy_init(struct device *dev,
|
||||
struct rzg2l_usbphy_ctrl_priv *priv)
|
||||
{
|
||||
struct regmap_field *pwrrdy;
|
||||
struct reg_field field;
|
||||
struct regmap *regmap;
|
||||
const int *data;
|
||||
u32 args[2];
|
||||
int ret;
|
||||
|
||||
data = device_get_match_data(dev);
|
||||
if ((uintptr_t)data != RZG2L_USBPHY_CTRL_PWRRDY)
|
||||
|
|
@ -157,13 +172,15 @@ static int rzg2l_usbphy_ctrl_pwrrdy_init(struct device *dev)
|
|||
field.lsb = __ffs(args[1]);
|
||||
field.msb = __fls(args[1]);
|
||||
|
||||
pwrrdy = devm_regmap_field_alloc(dev, regmap, field);
|
||||
if (IS_ERR(pwrrdy))
|
||||
return PTR_ERR(pwrrdy);
|
||||
priv->pwrrdy = devm_regmap_field_alloc(dev, regmap, field);
|
||||
if (IS_ERR(priv->pwrrdy))
|
||||
return PTR_ERR(priv->pwrrdy);
|
||||
|
||||
rzg2l_usbphy_ctrl_set_pwrrdy(pwrrdy, true);
|
||||
ret = rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_add_action_or_reset(dev, rzg2l_usbphy_ctrl_pwrrdy_off, pwrrdy);
|
||||
return devm_add_action_or_reset(dev, rzg2l_usbphy_ctrl_pwrrdy_off, priv->pwrrdy);
|
||||
}
|
||||
|
||||
static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev)
|
||||
|
|
@ -172,9 +189,7 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev)
|
|||
struct rzg2l_usbphy_ctrl_priv *priv;
|
||||
struct platform_device *vdev;
|
||||
struct regmap *regmap;
|
||||
unsigned long flags;
|
||||
int error;
|
||||
u32 val;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
|
|
@ -188,7 +203,7 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
error = rzg2l_usbphy_ctrl_pwrrdy_init(dev);
|
||||
error = rzg2l_usbphy_ctrl_pwrrdy_init(dev, priv);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
|
@ -211,12 +226,7 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev)
|
|||
goto err_pm_disable_reset_deassert;
|
||||
}
|
||||
|
||||
/* put pll and phy into reset state */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
val = readl(priv->base + RESET);
|
||||
val |= RESET_SEL_PLLRESET | RESET_PLLRESET | PHY_RESET_PORT2 | PHY_RESET_PORT1;
|
||||
writel(val, priv->base + RESET);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
rzg2l_usbphy_ctrl_init(priv);
|
||||
|
||||
priv->rcdev.ops = &rzg2l_usbphy_ctrl_reset_ops;
|
||||
priv->rcdev.of_reset_n_cells = 1;
|
||||
|
|
@ -263,10 +273,72 @@ static void rzg2l_usbphy_ctrl_remove(struct platform_device *pdev)
|
|||
reset_control_assert(priv->rstc);
|
||||
}
|
||||
|
||||
static int rzg2l_usbphy_ctrl_suspend(struct device *dev)
|
||||
{
|
||||
struct rzg2l_usbphy_ctrl_priv *priv = dev_get_drvdata(dev);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
val = readl(priv->base + RESET);
|
||||
if (!(val & PHY_RESET_PORT2) || !(val & PHY_RESET_PORT1))
|
||||
WARN(1, "Suspend with resets de-asserted\n");
|
||||
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
ret = reset_control_assert(priv->rstc);
|
||||
if (ret)
|
||||
goto rpm_resume;
|
||||
|
||||
ret = rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, false);
|
||||
if (ret)
|
||||
goto reset_deassert;
|
||||
|
||||
return 0;
|
||||
|
||||
reset_deassert:
|
||||
reset_control_deassert(priv->rstc);
|
||||
rpm_resume:
|
||||
pm_runtime_resume_and_get(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rzg2l_usbphy_ctrl_resume(struct device *dev)
|
||||
{
|
||||
struct rzg2l_usbphy_ctrl_priv *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = reset_control_deassert(priv->rstc);
|
||||
if (ret)
|
||||
goto pwrrdy_off;
|
||||
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret)
|
||||
goto reset_assert;
|
||||
|
||||
rzg2l_usbphy_ctrl_init(priv);
|
||||
|
||||
return 0;
|
||||
|
||||
reset_assert:
|
||||
reset_control_assert(priv->rstc);
|
||||
pwrrdy_off:
|
||||
rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(rzg2l_usbphy_ctrl_pm_ops,
|
||||
rzg2l_usbphy_ctrl_suspend,
|
||||
rzg2l_usbphy_ctrl_resume);
|
||||
|
||||
static struct platform_driver rzg2l_usbphy_ctrl_driver = {
|
||||
.driver = {
|
||||
.name = "rzg2l_usbphy_ctrl",
|
||||
.of_match_table = rzg2l_usbphy_ctrl_match_table,
|
||||
.pm = pm_ptr(&rzg2l_usbphy_ctrl_pm_ops),
|
||||
},
|
||||
.probe = rzg2l_usbphy_ctrl_probe,
|
||||
.remove = rzg2l_usbphy_ctrl_remove,
|
||||
|
|
|
|||
36
drivers/reset/spacemit/Kconfig
Normal file
36
drivers/reset/spacemit/Kconfig
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
menu "Reset support for SpacemiT platforms"
|
||||
depends on ARCH_SPACEMIT || COMPILE_TEST
|
||||
|
||||
config RESET_SPACEMIT_COMMON
|
||||
tristate
|
||||
select AUXILIARY_BUS
|
||||
help
|
||||
Common reset controller infrastructure for SpacemiT SoCs.
|
||||
This provides shared code and helper functions used by
|
||||
reset drivers for various SpacemiT SoC families.
|
||||
|
||||
config RESET_SPACEMIT_K1
|
||||
tristate "Support for SpacemiT K1 SoC"
|
||||
depends on SPACEMIT_K1_CCU
|
||||
select RESET_SPACEMIT_COMMON
|
||||
default SPACEMIT_K1_CCU
|
||||
help
|
||||
Support for reset controller in SpacemiT K1 SoC.
|
||||
This driver works with the SpacemiT K1 clock controller
|
||||
unit (CCU) driver to provide reset control functionality
|
||||
for various peripherals and subsystems in the SoC.
|
||||
|
||||
config RESET_SPACEMIT_K3
|
||||
tristate "Support for SpacemiT K3 SoC"
|
||||
depends on SPACEMIT_K3_CCU
|
||||
select RESET_SPACEMIT_COMMON
|
||||
default SPACEMIT_K3_CCU
|
||||
help
|
||||
Support for reset controller in SpacemiT K3 SoC.
|
||||
This driver works with the SpacemiT K3 clock controller
|
||||
unit (CCU) driver to provide reset control functionality
|
||||
for various peripherals and subsystems in the SoC.
|
||||
|
||||
endmenu
|
||||
5
drivers/reset/spacemit/Makefile
Normal file
5
drivers/reset/spacemit/Makefile
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_RESET_SPACEMIT_COMMON) += reset-spacemit-common.o
|
||||
|
||||
obj-$(CONFIG_RESET_SPACEMIT_K1) += reset-spacemit-k1.o
|
||||
obj-$(CONFIG_RESET_SPACEMIT_K3) += reset-spacemit-k3.o
|
||||
77
drivers/reset/spacemit/reset-spacemit-common.c
Normal file
77
drivers/reset/spacemit/reset-spacemit-common.c
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
/* SpacemiT reset controller driver - common implementation */
|
||||
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <soc/spacemit/ccu.h>
|
||||
|
||||
#include "reset-spacemit-common.h"
|
||||
|
||||
static int spacemit_reset_update(struct reset_controller_dev *rcdev,
|
||||
unsigned long id, bool assert)
|
||||
{
|
||||
struct ccu_reset_controller *controller;
|
||||
const struct ccu_reset_data *data;
|
||||
u32 mask;
|
||||
u32 val;
|
||||
|
||||
controller = container_of(rcdev, struct ccu_reset_controller, rcdev);
|
||||
data = &controller->data->reset_data[id];
|
||||
mask = data->assert_mask | data->deassert_mask;
|
||||
val = assert ? data->assert_mask : data->deassert_mask;
|
||||
|
||||
return regmap_update_bits(controller->regmap, data->offset, mask, val);
|
||||
}
|
||||
|
||||
static int spacemit_reset_assert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
return spacemit_reset_update(rcdev, id, true);
|
||||
}
|
||||
|
||||
static int spacemit_reset_deassert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
return spacemit_reset_update(rcdev, id, false);
|
||||
}
|
||||
|
||||
static const struct reset_control_ops spacemit_reset_control_ops = {
|
||||
.assert = spacemit_reset_assert,
|
||||
.deassert = spacemit_reset_deassert,
|
||||
};
|
||||
|
||||
static int spacemit_reset_controller_register(struct device *dev,
|
||||
struct ccu_reset_controller *controller)
|
||||
{
|
||||
struct reset_controller_dev *rcdev = &controller->rcdev;
|
||||
|
||||
rcdev->ops = &spacemit_reset_control_ops;
|
||||
rcdev->owner = dev->driver->owner;
|
||||
rcdev->of_node = dev->of_node;
|
||||
rcdev->nr_resets = controller->data->count;
|
||||
|
||||
return devm_reset_controller_register(dev, &controller->rcdev);
|
||||
}
|
||||
|
||||
int spacemit_reset_probe(struct auxiliary_device *adev,
|
||||
const struct auxiliary_device_id *id)
|
||||
{
|
||||
struct spacemit_ccu_adev *rdev = to_spacemit_ccu_adev(adev);
|
||||
struct ccu_reset_controller *controller;
|
||||
struct device *dev = &adev->dev;
|
||||
|
||||
controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL);
|
||||
if (!controller)
|
||||
return -ENOMEM;
|
||||
controller->data = (const struct ccu_reset_controller_data *)id->driver_data;
|
||||
controller->regmap = rdev->regmap;
|
||||
|
||||
return spacemit_reset_controller_register(dev, controller);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(spacemit_reset_probe, "RESET_SPACEMIT");
|
||||
|
||||
MODULE_DESCRIPTION("SpacemiT reset controller driver - common code");
|
||||
MODULE_LICENSE("GPL");
|
||||
42
drivers/reset/spacemit/reset-spacemit-common.h
Normal file
42
drivers/reset/spacemit/reset-spacemit-common.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* SpacemiT reset controller driver - common definitions
|
||||
*/
|
||||
|
||||
#ifndef _RESET_SPACEMIT_COMMON_H_
|
||||
#define _RESET_SPACEMIT_COMMON_H_
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct ccu_reset_data {
|
||||
u32 offset;
|
||||
u32 assert_mask;
|
||||
u32 deassert_mask;
|
||||
};
|
||||
|
||||
struct ccu_reset_controller_data {
|
||||
const struct ccu_reset_data *reset_data; /* array */
|
||||
size_t count;
|
||||
};
|
||||
|
||||
struct ccu_reset_controller {
|
||||
struct reset_controller_dev rcdev;
|
||||
const struct ccu_reset_controller_data *data;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
#define RESET_DATA(_offset, _assert_mask, _deassert_mask) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.assert_mask = (_assert_mask), \
|
||||
.deassert_mask = (_deassert_mask), \
|
||||
}
|
||||
|
||||
/* Common probe function */
|
||||
int spacemit_reset_probe(struct auxiliary_device *adev,
|
||||
const struct auxiliary_device_id *id);
|
||||
|
||||
#endif /* _RESET_SPACEMIT_COMMON_H_ */
|
||||
|
|
@ -1,41 +1,13 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
/* SpacemiT reset controller driver */
|
||||
/* SpacemiT K1 reset controller driver */
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <soc/spacemit/k1-syscon.h>
|
||||
#include <dt-bindings/clock/spacemit,k1-syscon.h>
|
||||
#include <soc/spacemit/k1-syscon.h>
|
||||
|
||||
struct ccu_reset_data {
|
||||
u32 offset;
|
||||
u32 assert_mask;
|
||||
u32 deassert_mask;
|
||||
};
|
||||
|
||||
struct ccu_reset_controller_data {
|
||||
const struct ccu_reset_data *reset_data; /* array */
|
||||
size_t count;
|
||||
};
|
||||
|
||||
struct ccu_reset_controller {
|
||||
struct reset_controller_dev rcdev;
|
||||
const struct ccu_reset_controller_data *data;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
#define RESET_DATA(_offset, _assert_mask, _deassert_mask) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.assert_mask = (_assert_mask), \
|
||||
.deassert_mask = (_deassert_mask), \
|
||||
}
|
||||
#include "reset-spacemit-common.h"
|
||||
|
||||
static const struct ccu_reset_data k1_mpmu_resets[] = {
|
||||
[RESET_WDT] = RESET_DATA(MPMU_WDTPCR, BIT(2), 0),
|
||||
|
|
@ -214,91 +186,30 @@ static const struct ccu_reset_controller_data k1_apbc2_reset_data = {
|
|||
.count = ARRAY_SIZE(k1_apbc2_resets),
|
||||
};
|
||||
|
||||
static int spacemit_reset_update(struct reset_controller_dev *rcdev,
|
||||
unsigned long id, bool assert)
|
||||
{
|
||||
struct ccu_reset_controller *controller;
|
||||
const struct ccu_reset_data *data;
|
||||
u32 mask;
|
||||
u32 val;
|
||||
|
||||
controller = container_of(rcdev, struct ccu_reset_controller, rcdev);
|
||||
data = &controller->data->reset_data[id];
|
||||
mask = data->assert_mask | data->deassert_mask;
|
||||
val = assert ? data->assert_mask : data->deassert_mask;
|
||||
|
||||
return regmap_update_bits(controller->regmap, data->offset, mask, val);
|
||||
}
|
||||
|
||||
static int spacemit_reset_assert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
return spacemit_reset_update(rcdev, id, true);
|
||||
}
|
||||
|
||||
static int spacemit_reset_deassert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
return spacemit_reset_update(rcdev, id, false);
|
||||
}
|
||||
|
||||
static const struct reset_control_ops spacemit_reset_control_ops = {
|
||||
.assert = spacemit_reset_assert,
|
||||
.deassert = spacemit_reset_deassert,
|
||||
};
|
||||
|
||||
static int spacemit_reset_controller_register(struct device *dev,
|
||||
struct ccu_reset_controller *controller)
|
||||
{
|
||||
struct reset_controller_dev *rcdev = &controller->rcdev;
|
||||
|
||||
rcdev->ops = &spacemit_reset_control_ops;
|
||||
rcdev->owner = THIS_MODULE;
|
||||
rcdev->of_node = dev->of_node;
|
||||
rcdev->nr_resets = controller->data->count;
|
||||
|
||||
return devm_reset_controller_register(dev, &controller->rcdev);
|
||||
}
|
||||
|
||||
static int spacemit_reset_probe(struct auxiliary_device *adev,
|
||||
const struct auxiliary_device_id *id)
|
||||
{
|
||||
struct spacemit_ccu_adev *rdev = to_spacemit_ccu_adev(adev);
|
||||
struct ccu_reset_controller *controller;
|
||||
struct device *dev = &adev->dev;
|
||||
|
||||
controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL);
|
||||
if (!controller)
|
||||
return -ENOMEM;
|
||||
controller->data = (const struct ccu_reset_controller_data *)id->driver_data;
|
||||
controller->regmap = rdev->regmap;
|
||||
|
||||
return spacemit_reset_controller_register(dev, controller);
|
||||
}
|
||||
|
||||
#define K1_AUX_DEV_ID(_unit) \
|
||||
{ \
|
||||
.name = "spacemit_ccu_k1." #_unit "-reset", \
|
||||
.name = "spacemit_ccu.k1-" #_unit "-reset", \
|
||||
.driver_data = (kernel_ulong_t)&k1_ ## _unit ## _reset_data, \
|
||||
}
|
||||
|
||||
static const struct auxiliary_device_id spacemit_reset_ids[] = {
|
||||
static const struct auxiliary_device_id spacemit_k1_reset_ids[] = {
|
||||
K1_AUX_DEV_ID(mpmu),
|
||||
K1_AUX_DEV_ID(apbc),
|
||||
K1_AUX_DEV_ID(apmu),
|
||||
K1_AUX_DEV_ID(rcpu),
|
||||
K1_AUX_DEV_ID(rcpu2),
|
||||
K1_AUX_DEV_ID(apbc2),
|
||||
{ },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(auxiliary, spacemit_reset_ids);
|
||||
MODULE_DEVICE_TABLE(auxiliary, spacemit_k1_reset_ids);
|
||||
|
||||
static struct auxiliary_driver spacemit_k1_reset_driver = {
|
||||
.probe = spacemit_reset_probe,
|
||||
.id_table = spacemit_reset_ids,
|
||||
.id_table = spacemit_k1_reset_ids,
|
||||
};
|
||||
module_auxiliary_driver(spacemit_k1_reset_driver);
|
||||
|
||||
MODULE_IMPORT_NS("RESET_SPACEMIT");
|
||||
MODULE_AUTHOR("Alex Elder <elder@kernel.org>");
|
||||
MODULE_DESCRIPTION("SpacemiT reset controller driver");
|
||||
MODULE_DESCRIPTION("SpacemiT K1 reset controller driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
233
drivers/reset/spacemit/reset-spacemit-k3.c
Normal file
233
drivers/reset/spacemit/reset-spacemit-k3.c
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
/* SpacemiT K3 reset controller driver */
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <dt-bindings/reset/spacemit,k3-resets.h>
|
||||
#include <soc/spacemit/k3-syscon.h>
|
||||
|
||||
#include "reset-spacemit-common.h"
|
||||
|
||||
static const struct ccu_reset_data k3_mpmu_resets[] = {
|
||||
[RESET_MPMU_WDT] = RESET_DATA(MPMU_WDTPCR, BIT(2), 0),
|
||||
[RESET_MPMU_RIPC] = RESET_DATA(MPMU_RIPCCR, BIT(2), 0),
|
||||
};
|
||||
|
||||
static const struct ccu_reset_controller_data k3_mpmu_reset_data = {
|
||||
.reset_data = k3_mpmu_resets,
|
||||
.count = ARRAY_SIZE(k3_mpmu_resets),
|
||||
};
|
||||
|
||||
static const struct ccu_reset_data k3_apbc_resets[] = {
|
||||
[RESET_APBC_UART0] = RESET_DATA(APBC_UART0_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_UART2] = RESET_DATA(APBC_UART2_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_UART3] = RESET_DATA(APBC_UART3_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_UART4] = RESET_DATA(APBC_UART4_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_UART5] = RESET_DATA(APBC_UART5_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_UART6] = RESET_DATA(APBC_UART6_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_UART7] = RESET_DATA(APBC_UART7_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_UART8] = RESET_DATA(APBC_UART8_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_UART9] = RESET_DATA(APBC_UART9_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_UART10] = RESET_DATA(APBC_UART10_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_GPIO] = RESET_DATA(APBC_GPIO_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM0] = RESET_DATA(APBC_PWM0_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM1] = RESET_DATA(APBC_PWM1_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM2] = RESET_DATA(APBC_PWM2_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM3] = RESET_DATA(APBC_PWM3_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM4] = RESET_DATA(APBC_PWM4_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM5] = RESET_DATA(APBC_PWM5_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM6] = RESET_DATA(APBC_PWM6_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM7] = RESET_DATA(APBC_PWM7_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM8] = RESET_DATA(APBC_PWM8_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM9] = RESET_DATA(APBC_PWM9_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM10] = RESET_DATA(APBC_PWM10_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM11] = RESET_DATA(APBC_PWM11_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM12] = RESET_DATA(APBC_PWM12_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM13] = RESET_DATA(APBC_PWM13_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM14] = RESET_DATA(APBC_PWM14_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM15] = RESET_DATA(APBC_PWM15_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM16] = RESET_DATA(APBC_PWM16_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM17] = RESET_DATA(APBC_PWM17_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM18] = RESET_DATA(APBC_PWM18_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_PWM19] = RESET_DATA(APBC_PWM19_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_SPI0] = RESET_DATA(APBC_SSP0_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_SPI1] = RESET_DATA(APBC_SSP1_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_SPI3] = RESET_DATA(APBC_SSP3_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_RTC] = RESET_DATA(APBC_RTC_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_TWSI0] = RESET_DATA(APBC_TWSI0_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_TWSI1] = RESET_DATA(APBC_TWSI1_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_TWSI2] = RESET_DATA(APBC_TWSI2_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_TWSI4] = RESET_DATA(APBC_TWSI4_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_TWSI5] = RESET_DATA(APBC_TWSI5_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_TWSI6] = RESET_DATA(APBC_TWSI6_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_TWSI8] = RESET_DATA(APBC_TWSI8_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_TIMERS0] = RESET_DATA(APBC_TIMERS0_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_TIMERS1] = RESET_DATA(APBC_TIMERS1_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_TIMERS2] = RESET_DATA(APBC_TIMERS2_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_TIMERS3] = RESET_DATA(APBC_TIMERS3_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_TIMERS4] = RESET_DATA(APBC_TIMERS4_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_TIMERS5] = RESET_DATA(APBC_TIMERS5_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_TIMERS6] = RESET_DATA(APBC_TIMERS6_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_TIMERS7] = RESET_DATA(APBC_TIMERS7_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_AIB] = RESET_DATA(APBC_AIB_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_ONEWIRE] = RESET_DATA(APBC_ONEWIRE_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_I2S0] = RESET_DATA(APBC_SSPA0_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_I2S1] = RESET_DATA(APBC_SSPA1_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_I2S2] = RESET_DATA(APBC_SSPA2_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_I2S3] = RESET_DATA(APBC_SSPA3_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_I2S4] = RESET_DATA(APBC_SSPA4_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_I2S5] = RESET_DATA(APBC_SSPA5_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_DRO] = RESET_DATA(APBC_DRO_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_IR0] = RESET_DATA(APBC_IR0_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_IR1] = RESET_DATA(APBC_IR1_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_TSEN] = RESET_DATA(APBC_TSEN_CLK_RST, BIT(2), 0),
|
||||
[RESET_IPC_AP2AUD] = RESET_DATA(APBC_IPC_AP2AUD_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_CAN0] = RESET_DATA(APBC_CAN0_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_CAN1] = RESET_DATA(APBC_CAN1_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_CAN2] = RESET_DATA(APBC_CAN2_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_CAN3] = RESET_DATA(APBC_CAN3_CLK_RST, BIT(2), 0),
|
||||
[RESET_APBC_CAN4] = RESET_DATA(APBC_CAN4_CLK_RST, BIT(2), 0),
|
||||
};
|
||||
|
||||
static const struct ccu_reset_controller_data k3_apbc_reset_data = {
|
||||
.reset_data = k3_apbc_resets,
|
||||
.count = ARRAY_SIZE(k3_apbc_resets),
|
||||
};
|
||||
|
||||
static const struct ccu_reset_data k3_apmu_resets[] = {
|
||||
[RESET_APMU_CSI] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(1)),
|
||||
[RESET_APMU_CCIC2PHY] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(2)),
|
||||
[RESET_APMU_CCIC3PHY] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(29)),
|
||||
[RESET_APMU_ISP_CIBUS] = RESET_DATA(APMU_ISP_CLK_RES_CTRL, 0, BIT(16)),
|
||||
[RESET_APMU_DSI_ESC] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(3)),
|
||||
[RESET_APMU_LCD] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(4)),
|
||||
[RESET_APMU_V2D] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(27)),
|
||||
[RESET_APMU_LCD_MCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL2, 0, BIT(9)),
|
||||
[RESET_APMU_LCD_DSCCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL2, 0, BIT(15)),
|
||||
[RESET_APMU_SC2_HCLK] = RESET_DATA(APMU_CCIC_CLK_RES_CTRL, 0, BIT(0)),
|
||||
[RESET_APMU_CCIC_4X] = RESET_DATA(APMU_CCIC_CLK_RES_CTRL, 0, BIT(1)),
|
||||
[RESET_APMU_CCIC1_PHY] = RESET_DATA(APMU_CCIC_CLK_RES_CTRL, 0, BIT(2)),
|
||||
[RESET_APMU_SDH_AXI] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(0)),
|
||||
[RESET_APMU_SDH0] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(1)),
|
||||
[RESET_APMU_SDH1] = RESET_DATA(APMU_SDH1_CLK_RES_CTRL, 0, BIT(1)),
|
||||
[RESET_APMU_SDH2] = RESET_DATA(APMU_SDH2_CLK_RES_CTRL, 0, BIT(1)),
|
||||
[RESET_APMU_USB2] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0,
|
||||
BIT(1)|BIT(2)|BIT(3)),
|
||||
[RESET_APMU_USB3_PORTA] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0,
|
||||
BIT(5)|BIT(6)|BIT(7)),
|
||||
[RESET_APMU_USB3_PORTB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0,
|
||||
BIT(9)|BIT(10)|BIT(11)),
|
||||
[RESET_APMU_USB3_PORTC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0,
|
||||
BIT(13)|BIT(14)|BIT(15)),
|
||||
[RESET_APMU_USB3_PORTD] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0,
|
||||
BIT(17)|BIT(18)|BIT(19)),
|
||||
[RESET_APMU_QSPI] = RESET_DATA(APMU_QSPI_CLK_RES_CTRL, 0, BIT(1)),
|
||||
[RESET_APMU_QSPI_BUS] = RESET_DATA(APMU_QSPI_CLK_RES_CTRL, 0, BIT(0)),
|
||||
[RESET_APMU_DMA] = RESET_DATA(APMU_DMA_CLK_RES_CTRL, 0, BIT(0)),
|
||||
[RESET_APMU_AES_WTM] = RESET_DATA(APMU_AES_CLK_RES_CTRL, 0, BIT(4)),
|
||||
[RESET_APMU_MCB_DCLK] = RESET_DATA(APMU_MCB_CLK_RES_CTRL, 0, BIT(0)),
|
||||
[RESET_APMU_MCB_ACLK] = RESET_DATA(APMU_MCB_CLK_RES_CTRL, 0, BIT(1)),
|
||||
[RESET_APMU_VPU] = RESET_DATA(APMU_VPU_CLK_RES_CTRL, 0, BIT(0)),
|
||||
[RESET_APMU_DTC] = RESET_DATA(APMU_DTC_CLK_RES_CTRL, 0, BIT(0)),
|
||||
[RESET_APMU_GPU] = RESET_DATA(APMU_GPU_CLK_RES_CTRL, 0, BIT(1)),
|
||||
[RESET_APMU_MC] = RESET_DATA(APMU_PMUA_MC_CTRL, 0, BIT(0)),
|
||||
[RESET_APMU_CPU0_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(0), 0),
|
||||
[RESET_APMU_CPU0_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(1), 0),
|
||||
[RESET_APMU_CPU1_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(3), 0),
|
||||
[RESET_APMU_CPU1_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(4), 0),
|
||||
[RESET_APMU_CPU2_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(6), 0),
|
||||
[RESET_APMU_CPU2_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(7), 0),
|
||||
[RESET_APMU_CPU3_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(9), 0),
|
||||
[RESET_APMU_CPU3_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(10), 0),
|
||||
[RESET_APMU_C0_MPSUB_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(12), 0),
|
||||
[RESET_APMU_CPU4_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(16), 0),
|
||||
[RESET_APMU_CPU4_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(17), 0),
|
||||
[RESET_APMU_CPU5_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(19), 0),
|
||||
[RESET_APMU_CPU5_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(20), 0),
|
||||
[RESET_APMU_CPU6_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(22), 0),
|
||||
[RESET_APMU_CPU6_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(23), 0),
|
||||
[RESET_APMU_CPU7_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(25), 0),
|
||||
[RESET_APMU_CPU7_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(26), 0),
|
||||
[RESET_APMU_C1_MPSUB_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(28), 0),
|
||||
[RESET_APMU_MPSUB_DBG] = RESET_DATA(APMU_PMU_CC2_AP, BIT(29), 0),
|
||||
[RESET_APMU_UCIE] = RESET_DATA(APMU_UCIE_CTRL,
|
||||
BIT(1) | BIT(2) | BIT(3), 0),
|
||||
[RESET_APMU_RCPU] = RESET_DATA(APMU_RCPU_CLK_RES_CTRL, 0,
|
||||
BIT(3) | BIT(2) | BIT(0)),
|
||||
[RESET_APMU_DSI4LN2_ESCCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL3, 0, BIT(3)),
|
||||
[RESET_APMU_DSI4LN2_LCD_SW] = RESET_DATA(APMU_LCD_CLK_RES_CTRL3, 0, BIT(4)),
|
||||
[RESET_APMU_DSI4LN2_LCD_MCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL4, 0, BIT(9)),
|
||||
[RESET_APMU_DSI4LN2_LCD_DSCCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL4, 0, BIT(15)),
|
||||
[RESET_APMU_DSI4LN2_DPU_ACLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL5, 0, BIT(0)),
|
||||
[RESET_APMU_DPU_ACLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL5, 0, BIT(15)),
|
||||
[RESET_APMU_UFS_ACLK] = RESET_DATA(APMU_UFS_CLK_RES_CTRL, 0, BIT(0)),
|
||||
[RESET_APMU_EDP0] = RESET_DATA(APMU_LCD_EDP_CTRL, 0, BIT(0)),
|
||||
[RESET_APMU_EDP1] = RESET_DATA(APMU_LCD_EDP_CTRL, 0, BIT(16)),
|
||||
[RESET_APMU_PCIE_PORTA] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_A, 0,
|
||||
BIT(5) | BIT(4) | BIT(3)),
|
||||
[RESET_APMU_PCIE_PORTB] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_B, 0,
|
||||
BIT(5) | BIT(4) | BIT(3)),
|
||||
[RESET_APMU_PCIE_PORTC] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_C, 0,
|
||||
BIT(5) | BIT(4) | BIT(3)),
|
||||
[RESET_APMU_PCIE_PORTD] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_D, 0,
|
||||
BIT(5) | BIT(4) | BIT(3)),
|
||||
[RESET_APMU_PCIE_PORTE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_E, 0,
|
||||
BIT(5) | BIT(4) | BIT(3)),
|
||||
[RESET_APMU_EMAC0] = RESET_DATA(APMU_EMAC0_CLK_RES_CTRL, 0, BIT(1)),
|
||||
[RESET_APMU_EMAC1] = RESET_DATA(APMU_EMAC1_CLK_RES_CTRL, 0, BIT(1)),
|
||||
[RESET_APMU_EMAC2] = RESET_DATA(APMU_EMAC2_CLK_RES_CTRL, 0, BIT(1)),
|
||||
[RESET_APMU_ESPI_MCLK] = RESET_DATA(APMU_ESPI_CLK_RES_CTRL, 0, BIT(0)),
|
||||
[RESET_APMU_ESPI_SCLK] = RESET_DATA(APMU_ESPI_CLK_RES_CTRL, 0, BIT(2)),
|
||||
};
|
||||
|
||||
static const struct ccu_reset_controller_data k3_apmu_reset_data = {
|
||||
.reset_data = k3_apmu_resets,
|
||||
.count = ARRAY_SIZE(k3_apmu_resets),
|
||||
};
|
||||
|
||||
static const struct ccu_reset_data k3_dciu_resets[] = {
|
||||
[RESET_DCIU_HDMA] = RESET_DATA(DCIU_DMASYS_RSTN, 0, BIT(0)),
|
||||
[RESET_DCIU_DMA350] = RESET_DATA(DCIU_DMASYS_SDMA_RSTN, 0, BIT(0)),
|
||||
[RESET_DCIU_DMA350_0] = RESET_DATA(DCIU_DMASYS_S0_RSTN, 0, BIT(0)),
|
||||
[RESET_DCIU_DMA350_1] = RESET_DATA(DCIU_DMASYS_S1_RSTN, 0, BIT(0)),
|
||||
[RESET_DCIU_AXIDMA0] = RESET_DATA(DCIU_DMASYS_A0_RSTN, 0, BIT(0)),
|
||||
[RESET_DCIU_AXIDMA1] = RESET_DATA(DCIU_DMASYS_A1_RSTN, 0, BIT(0)),
|
||||
[RESET_DCIU_AXIDMA2] = RESET_DATA(DCIU_DMASYS_A2_RSTN, 0, BIT(0)),
|
||||
[RESET_DCIU_AXIDMA3] = RESET_DATA(DCIU_DMASYS_A3_RSTN, 0, BIT(0)),
|
||||
[RESET_DCIU_AXIDMA4] = RESET_DATA(DCIU_DMASYS_A4_RSTN, 0, BIT(0)),
|
||||
[RESET_DCIU_AXIDMA5] = RESET_DATA(DCIU_DMASYS_A5_RSTN, 0, BIT(0)),
|
||||
[RESET_DCIU_AXIDMA6] = RESET_DATA(DCIU_DMASYS_A6_RSTN, 0, BIT(0)),
|
||||
[RESET_DCIU_AXIDMA7] = RESET_DATA(DCIU_DMASYS_A7_RSTN, 0, BIT(0)),
|
||||
};
|
||||
|
||||
static const struct ccu_reset_controller_data k3_dciu_reset_data = {
|
||||
.reset_data = k3_dciu_resets,
|
||||
.count = ARRAY_SIZE(k3_dciu_resets),
|
||||
};
|
||||
|
||||
#define K3_AUX_DEV_ID(_unit) \
|
||||
{ \
|
||||
.name = "spacemit_ccu.k3-" #_unit "-reset", \
|
||||
.driver_data = (kernel_ulong_t)&k3_ ## _unit ## _reset_data, \
|
||||
}
|
||||
|
||||
static const struct auxiliary_device_id spacemit_k3_reset_ids[] = {
|
||||
K3_AUX_DEV_ID(mpmu),
|
||||
K3_AUX_DEV_ID(apbc),
|
||||
K3_AUX_DEV_ID(apmu),
|
||||
K3_AUX_DEV_ID(dciu),
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(auxiliary, spacemit_k3_reset_ids);
|
||||
|
||||
static struct auxiliary_driver spacemit_k3_reset_driver = {
|
||||
.probe = spacemit_reset_probe,
|
||||
.id_table = spacemit_k3_reset_ids,
|
||||
};
|
||||
module_auxiliary_driver(spacemit_k3_reset_driver);
|
||||
|
||||
MODULE_IMPORT_NS("RESET_SPACEMIT");
|
||||
MODULE_AUTHOR("Guodong Xu <guodong@riscstar.com>");
|
||||
MODULE_DESCRIPTION("SpacemiT K3 reset controller driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -547,9 +547,9 @@ static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int optee_rtc_probe(struct device *dev)
|
||||
static int optee_rtc_probe(struct tee_client_device *rtc_device)
|
||||
{
|
||||
struct tee_client_device *rtc_device = to_tee_client_device(dev);
|
||||
struct device *dev = &rtc_device->dev;
|
||||
struct tee_ioctl_open_session_arg sess2_arg = {0};
|
||||
struct tee_ioctl_open_session_arg sess_arg = {0};
|
||||
struct optee_rtc *priv;
|
||||
|
|
@ -682,8 +682,9 @@ out_ctx:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int optee_rtc_remove(struct device *dev)
|
||||
static void optee_rtc_remove(struct tee_client_device *rtc_device)
|
||||
{
|
||||
struct device *dev = &rtc_device->dev;
|
||||
struct optee_rtc *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (priv->features & TA_RTC_FEATURE_ALARM) {
|
||||
|
|
@ -696,8 +697,6 @@ static int optee_rtc_remove(struct device *dev)
|
|||
tee_shm_free(priv->shm);
|
||||
tee_client_close_session(priv->ctx, priv->session_id);
|
||||
tee_client_close_context(priv->ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int optee_rtc_suspend(struct device *dev)
|
||||
|
|
@ -724,27 +723,15 @@ MODULE_DEVICE_TABLE(tee, optee_rtc_id_table);
|
|||
|
||||
static struct tee_client_driver optee_rtc_driver = {
|
||||
.id_table = optee_rtc_id_table,
|
||||
.probe = optee_rtc_probe,
|
||||
.remove = optee_rtc_remove,
|
||||
.driver = {
|
||||
.name = "optee_rtc",
|
||||
.bus = &tee_bus_type,
|
||||
.probe = optee_rtc_probe,
|
||||
.remove = optee_rtc_remove,
|
||||
.pm = pm_sleep_ptr(&optee_rtc_pm_ops),
|
||||
},
|
||||
};
|
||||
|
||||
static int __init optee_rtc_mod_init(void)
|
||||
{
|
||||
return driver_register(&optee_rtc_driver.driver);
|
||||
}
|
||||
|
||||
static void __exit optee_rtc_mod_exit(void)
|
||||
{
|
||||
driver_unregister(&optee_rtc_driver.driver);
|
||||
}
|
||||
|
||||
module_init(optee_rtc_mod_init);
|
||||
module_exit(optee_rtc_mod_exit);
|
||||
module_tee_client_driver(optee_rtc_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Clément Léger <clement.leger@bootlin.com>");
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ static const struct meson_gx_package_id {
|
|||
{ "S905D3", 0x2b, 0x30, 0x3f },
|
||||
{ "A113L", 0x2c, 0x0, 0xf8 },
|
||||
{ "S805X2", 0x37, 0x2, 0xf },
|
||||
{ "S905Y4", 0x37, 0x3, 0xf },
|
||||
{ "C308L", 0x3d, 0x1, 0xf },
|
||||
{ "A311D2", 0x36, 0x1, 0xf },
|
||||
{ "A113X2", 0x3c, 0x1, 0xf },
|
||||
|
|
|
|||
|
|
@ -851,6 +851,22 @@ int apple_rtkit_shutdown(struct apple_rtkit *rtk)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(apple_rtkit_shutdown);
|
||||
|
||||
int apple_rtkit_poweroff(struct apple_rtkit *rtk)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = apple_rtkit_set_ap_power_state(rtk, APPLE_RTKIT_PWR_STATE_OFF);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = apple_rtkit_set_iop_power_state(rtk, APPLE_RTKIT_PWR_STATE_OFF);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return apple_rtkit_reinit(rtk);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(apple_rtkit_poweroff);
|
||||
|
||||
int apple_rtkit_idle(struct apple_rtkit *rtk)
|
||||
{
|
||||
int ret;
|
||||
|
|
|
|||
|
|
@ -371,7 +371,7 @@ int __init dove_init_pmu_legacy(const struct dove_pmu_initdata *initdata)
|
|||
*/
|
||||
int __init dove_init_pmu(void)
|
||||
{
|
||||
struct device_node *np_pmu, *domains_node, *np;
|
||||
struct device_node *np_pmu, *domains_node;
|
||||
struct pmu_data *pmu;
|
||||
int ret, parent_irq;
|
||||
|
||||
|
|
@ -404,21 +404,18 @@ int __init dove_init_pmu(void)
|
|||
|
||||
pmu_reset_init(pmu);
|
||||
|
||||
for_each_available_child_of_node(domains_node, np) {
|
||||
for_each_available_child_of_node_scoped(domains_node, np) {
|
||||
struct of_phandle_args args;
|
||||
struct pmu_domain *domain;
|
||||
|
||||
domain = kzalloc(sizeof(*domain), GFP_KERNEL);
|
||||
if (!domain) {
|
||||
of_node_put(np);
|
||||
if (!domain)
|
||||
break;
|
||||
}
|
||||
|
||||
domain->pmu = pmu;
|
||||
domain->base.name = kasprintf(GFP_KERNEL, "%pOFn", np);
|
||||
if (!domain->base.name) {
|
||||
kfree(domain);
|
||||
of_node_put(np);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,4 +11,4 @@ obj-$(CONFIG_UCC_SLOW) += ucc_slow.o
|
|||
obj-$(CONFIG_UCC_FAST) += ucc_fast.o
|
||||
obj-$(CONFIG_QE_TDM) += qe_tdm.o
|
||||
obj-$(CONFIG_QE_USB) += usb.o
|
||||
obj-$(CONFIG_QE_GPIO) += gpio.o
|
||||
obj-$(CONFIG_QE_GPIO) += gpio.o qe_ports_ic.o
|
||||
|
|
|
|||
141
drivers/soc/fsl/qe/qe_ports_ic.c
Normal file
141
drivers/soc/fsl/qe/qe_ports_ic.c
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* QUICC ENGINE I/O Ports Interrupt Controller
|
||||
*
|
||||
* Copyright (c) 2025 Christophe Leroy CS GROUP France (christophe.leroy@csgroup.eu)
|
||||
*/
|
||||
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/* QE IC registers offset */
|
||||
#define CEPIER 0x0c
|
||||
#define CEPIMR 0x10
|
||||
#define CEPICR 0x14
|
||||
|
||||
struct qepic_data {
|
||||
void __iomem *reg;
|
||||
struct irq_domain *host;
|
||||
};
|
||||
|
||||
static void qepic_mask(struct irq_data *d)
|
||||
{
|
||||
struct qepic_data *data = irq_data_get_irq_chip_data(d);
|
||||
|
||||
clrbits32(data->reg + CEPIMR, 1 << (31 - irqd_to_hwirq(d)));
|
||||
}
|
||||
|
||||
static void qepic_unmask(struct irq_data *d)
|
||||
{
|
||||
struct qepic_data *data = irq_data_get_irq_chip_data(d);
|
||||
|
||||
setbits32(data->reg + CEPIMR, 1 << (31 - irqd_to_hwirq(d)));
|
||||
}
|
||||
|
||||
static void qepic_end(struct irq_data *d)
|
||||
{
|
||||
struct qepic_data *data = irq_data_get_irq_chip_data(d);
|
||||
|
||||
out_be32(data->reg + CEPIER, 1 << (31 - irqd_to_hwirq(d)));
|
||||
}
|
||||
|
||||
static int qepic_set_type(struct irq_data *d, unsigned int flow_type)
|
||||
{
|
||||
struct qepic_data *data = irq_data_get_irq_chip_data(d);
|
||||
unsigned int vec = (unsigned int)irqd_to_hwirq(d);
|
||||
|
||||
switch (flow_type & IRQ_TYPE_SENSE_MASK) {
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
setbits32(data->reg + CEPICR, 1 << (31 - vec));
|
||||
return 0;
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
case IRQ_TYPE_NONE:
|
||||
clrbits32(data->reg + CEPICR, 1 << (31 - vec));
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct irq_chip qepic = {
|
||||
.name = "QEPIC",
|
||||
.irq_mask = qepic_mask,
|
||||
.irq_unmask = qepic_unmask,
|
||||
.irq_eoi = qepic_end,
|
||||
.irq_set_type = qepic_set_type,
|
||||
};
|
||||
|
||||
static int qepic_get_irq(struct irq_desc *desc)
|
||||
{
|
||||
struct qepic_data *data = irq_desc_get_handler_data(desc);
|
||||
u32 event = in_be32(data->reg + CEPIER);
|
||||
|
||||
if (!event)
|
||||
return -1;
|
||||
|
||||
return irq_find_mapping(data->host, 32 - ffs(event));
|
||||
}
|
||||
|
||||
static void qepic_cascade(struct irq_desc *desc)
|
||||
{
|
||||
generic_handle_irq(qepic_get_irq(desc));
|
||||
}
|
||||
|
||||
static int qepic_host_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw)
|
||||
{
|
||||
irq_set_chip_data(virq, h->host_data);
|
||||
irq_set_chip_and_handler(virq, &qepic, handle_fasteoi_irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops qepic_host_ops = {
|
||||
.map = qepic_host_map,
|
||||
};
|
||||
|
||||
static int qepic_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct qepic_data *data;
|
||||
int irq;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->reg = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(data->reg))
|
||||
return PTR_ERR(data->reg);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
data->host = irq_domain_add_linear(dev->of_node, 32, &qepic_host_ops, data);
|
||||
if (!data->host)
|
||||
return -ENODEV;
|
||||
|
||||
irq_set_chained_handler_and_data(irq, qepic_cascade, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qepic_match[] = {
|
||||
{
|
||||
.compatible = "fsl,mpc8323-qe-ports-ic",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver qepic_driver = {
|
||||
.driver = {
|
||||
.name = "qe_ports_ic",
|
||||
.of_match_table = qepic_match,
|
||||
},
|
||||
.probe = qepic_probe,
|
||||
};
|
||||
|
||||
static int __init qepic_init(void)
|
||||
{
|
||||
return platform_driver_register(&qepic_driver);
|
||||
}
|
||||
arch_initcall(qepic_init);
|
||||
|
|
@ -1284,31 +1284,26 @@ static unsigned int qmc_nb_chans(struct qmc *qmc)
|
|||
|
||||
static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
|
||||
{
|
||||
struct device_node *chan_np;
|
||||
struct qmc_chan *chan;
|
||||
const char *mode;
|
||||
u32 chan_id;
|
||||
u64 ts_mask;
|
||||
int ret;
|
||||
|
||||
for_each_available_child_of_node(np, chan_np) {
|
||||
for_each_available_child_of_node_scoped(np, chan_np) {
|
||||
ret = of_property_read_u32(chan_np, "reg", &chan_id);
|
||||
if (ret) {
|
||||
dev_err(qmc->dev, "%pOF: failed to read reg\n", chan_np);
|
||||
of_node_put(chan_np);
|
||||
return ret;
|
||||
}
|
||||
if (chan_id > 63) {
|
||||
dev_err(qmc->dev, "%pOF: Invalid chan_id\n", chan_np);
|
||||
of_node_put(chan_np);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chan = devm_kzalloc(qmc->dev, sizeof(*chan), GFP_KERNEL);
|
||||
if (!chan) {
|
||||
of_node_put(chan_np);
|
||||
if (!chan)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
chan->id = chan_id;
|
||||
spin_lock_init(&chan->ts_lock);
|
||||
|
|
@ -1319,7 +1314,6 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
|
|||
if (ret) {
|
||||
dev_err(qmc->dev, "%pOF: failed to read fsl,tx-ts-mask\n",
|
||||
chan_np);
|
||||
of_node_put(chan_np);
|
||||
return ret;
|
||||
}
|
||||
chan->tx_ts_mask_avail = ts_mask;
|
||||
|
|
@ -1329,7 +1323,6 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
|
|||
if (ret) {
|
||||
dev_err(qmc->dev, "%pOF: failed to read fsl,rx-ts-mask\n",
|
||||
chan_np);
|
||||
of_node_put(chan_np);
|
||||
return ret;
|
||||
}
|
||||
chan->rx_ts_mask_avail = ts_mask;
|
||||
|
|
@ -1340,7 +1333,6 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
|
|||
if (ret && ret != -EINVAL) {
|
||||
dev_err(qmc->dev, "%pOF: failed to read fsl,operational-mode\n",
|
||||
chan_np);
|
||||
of_node_put(chan_np);
|
||||
return ret;
|
||||
}
|
||||
if (!strcmp(mode, "transparent")) {
|
||||
|
|
@ -1350,7 +1342,6 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
|
|||
} else {
|
||||
dev_err(qmc->dev, "%pOF: Invalid fsl,operational-mode (%s)\n",
|
||||
chan_np, mode);
|
||||
of_node_put(chan_np);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -148,7 +148,11 @@ static int imx8m_soc_prepare(struct platform_device *pdev, const char *ocotp_com
|
|||
goto err_clk;
|
||||
}
|
||||
|
||||
return clk_prepare_enable(drvdata->clk);
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk:
|
||||
iounmap(drvdata->ocotp_base);
|
||||
|
|
|
|||
|
|
@ -12,12 +12,13 @@
|
|||
#include <linux/sys_soc.h>
|
||||
|
||||
#define IMX_SIP_GET_SOC_INFO 0xc2000006
|
||||
#define SOC_ID(x) (((x) & 0xFFFF) >> 8)
|
||||
#define SOC_ID(x) (((x) & 0xFF) ? ((x) & 0xFFFF) >> 4 : ((x) & 0xFFFF) >> 8)
|
||||
#define SOC_REV_MAJOR(x) ((((x) >> 28) & 0xF) - 0x9)
|
||||
#define SOC_REV_MINOR(x) (((x) >> 24) & 0xF)
|
||||
|
||||
static int imx9_soc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct soc_device_attribute *attr;
|
||||
struct arm_smccc_res res;
|
||||
struct soc_device *sdev;
|
||||
|
|
@ -25,17 +26,15 @@ static int imx9_soc_probe(struct platform_device *pdev)
|
|||
u64 uid127_64, uid63_0;
|
||||
int err;
|
||||
|
||||
attr = kzalloc(sizeof(*attr), GFP_KERNEL);
|
||||
attr = devm_kzalloc(dev, sizeof(*attr), GFP_KERNEL);
|
||||
if (!attr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = of_property_read_string(of_root, "model", &attr->machine);
|
||||
if (err) {
|
||||
pr_err("%s: missing model property: %d\n", __func__, err);
|
||||
goto attr;
|
||||
}
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "%s: missing model property\n", __func__);
|
||||
|
||||
attr->family = kasprintf(GFP_KERNEL, "Freescale i.MX");
|
||||
attr->family = devm_kasprintf(dev, GFP_KERNEL, "Freescale i.MX");
|
||||
|
||||
/*
|
||||
* Retrieve the soc id, rev & uid info:
|
||||
|
|
@ -45,46 +44,33 @@ static int imx9_soc_probe(struct platform_device *pdev)
|
|||
* res.a3: uid[63:0];
|
||||
*/
|
||||
arm_smccc_smc(IMX_SIP_GET_SOC_INFO, 0, 0, 0, 0, 0, 0, 0, &res);
|
||||
if (res.a0 != SMCCC_RET_SUCCESS) {
|
||||
pr_err("%s: SMC failed: 0x%lx\n", __func__, res.a0);
|
||||
err = -EINVAL;
|
||||
goto family;
|
||||
}
|
||||
if (res.a0 != SMCCC_RET_SUCCESS)
|
||||
return dev_err_probe(dev, -EINVAL, "%s: SMC failed: 0x%lx\n", __func__, res.a0);
|
||||
|
||||
soc_id = SOC_ID(res.a1);
|
||||
rev_major = SOC_REV_MAJOR(res.a1);
|
||||
rev_minor = SOC_REV_MINOR(res.a1);
|
||||
|
||||
attr->soc_id = kasprintf(GFP_KERNEL, "i.MX%2x", soc_id);
|
||||
attr->revision = kasprintf(GFP_KERNEL, "%d.%d", rev_major, rev_minor);
|
||||
attr->soc_id = devm_kasprintf(dev, GFP_KERNEL, "i.MX%2x", soc_id);
|
||||
attr->revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d", rev_major, rev_minor);
|
||||
|
||||
uid127_64 = res.a2;
|
||||
uid63_0 = res.a3;
|
||||
attr->serial_number = kasprintf(GFP_KERNEL, "%016llx%016llx", uid127_64, uid63_0);
|
||||
attr->serial_number = devm_kasprintf(dev, GFP_KERNEL, "%016llx%016llx", uid127_64, uid63_0);
|
||||
|
||||
sdev = soc_device_register(attr);
|
||||
if (IS_ERR(sdev)) {
|
||||
err = PTR_ERR(sdev);
|
||||
pr_err("%s failed to register SoC as a device: %d\n", __func__, err);
|
||||
goto serial_number;
|
||||
}
|
||||
if (IS_ERR(sdev))
|
||||
return dev_err_probe(dev, PTR_ERR(sdev),
|
||||
"%s failed to register SoC as a device\n", __func__);
|
||||
|
||||
return 0;
|
||||
|
||||
serial_number:
|
||||
kfree(attr->serial_number);
|
||||
kfree(attr->revision);
|
||||
kfree(attr->soc_id);
|
||||
family:
|
||||
kfree(attr->family);
|
||||
attr:
|
||||
kfree(attr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static __maybe_unused const struct of_device_id imx9_soc_match[] = {
|
||||
{ .compatible = "fsl,imx93", },
|
||||
{ .compatible = "fsl,imx94", },
|
||||
{ .compatible = "fsl,imx95", },
|
||||
{ .compatible = "fsl,imx952", },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/mailbox_controller.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/soc/mediatek/mtk-cmdq.h>
|
||||
|
||||
#define CMDQ_WRITE_ENABLE_MASK BIT(0)
|
||||
|
|
@ -60,20 +61,41 @@ int cmdq_dev_get_client_reg(struct device *dev,
|
|||
struct cmdq_client_reg *client_reg, int idx)
|
||||
{
|
||||
struct of_phandle_args spec;
|
||||
struct resource res;
|
||||
int err;
|
||||
|
||||
if (!client_reg)
|
||||
return -ENOENT;
|
||||
|
||||
err = of_address_to_resource(dev->of_node, 0, &res);
|
||||
if (err) {
|
||||
dev_err(dev, "Missing reg in %s node\n", dev->of_node->full_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
client_reg->pa_base = res.start;
|
||||
|
||||
err = of_parse_phandle_with_fixed_args(dev->of_node,
|
||||
"mediatek,gce-client-reg",
|
||||
3, idx, &spec);
|
||||
if (err < 0) {
|
||||
dev_warn(dev,
|
||||
dev_dbg(dev,
|
||||
"error %d can't parse gce-client-reg property (%d)",
|
||||
err, idx);
|
||||
|
||||
return err;
|
||||
/* make subsys invalid */
|
||||
client_reg->subsys = CMDQ_SUBSYS_INVALID;
|
||||
|
||||
/*
|
||||
* All GCEs support writing register PA with mask without subsys,
|
||||
* but this requires extra GCE instructions to convert the PA into
|
||||
* a format that GCE can handle, which is less performance than
|
||||
* directly using subsys. Therefore, when subsys is available,
|
||||
* we prefer to use subsys for writing register PA.
|
||||
*/
|
||||
client_reg->pkt_write = cmdq_pkt_write_pa;
|
||||
client_reg->pkt_write_mask = cmdq_pkt_write_mask_pa;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
client_reg->subsys = (u8)spec.args[0];
|
||||
|
|
@ -81,6 +103,9 @@ int cmdq_dev_get_client_reg(struct device *dev,
|
|||
client_reg->size = (u16)spec.args[2];
|
||||
of_node_put(spec.np);
|
||||
|
||||
client_reg->pkt_write = cmdq_pkt_write_subsys;
|
||||
client_reg->pkt_write_mask = cmdq_pkt_write_mask_subsys;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(cmdq_dev_get_client_reg);
|
||||
|
|
@ -140,6 +165,7 @@ int cmdq_pkt_create(struct cmdq_client *client, struct cmdq_pkt *pkt, size_t siz
|
|||
}
|
||||
|
||||
pkt->pa_base = dma_addr;
|
||||
cmdq_get_mbox_priv(client->chan, &pkt->priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -201,6 +227,26 @@ int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value)
|
|||
}
|
||||
EXPORT_SYMBOL(cmdq_pkt_write);
|
||||
|
||||
int cmdq_pkt_write_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, u32 pa_base,
|
||||
u16 offset, u32 value)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = cmdq_pkt_assign(pkt, CMDQ_THR_SPR_IDX0, CMDQ_ADDR_HIGH(pa_base));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return cmdq_pkt_write_s_value(pkt, CMDQ_THR_SPR_IDX0, CMDQ_ADDR_LOW(offset), value);
|
||||
}
|
||||
EXPORT_SYMBOL(cmdq_pkt_write_pa);
|
||||
|
||||
int cmdq_pkt_write_subsys(struct cmdq_pkt *pkt, u8 subsys, u32 pa_base /*unused*/,
|
||||
u16 offset, u32 value)
|
||||
{
|
||||
return cmdq_pkt_write(pkt, subsys, offset, value);
|
||||
}
|
||||
EXPORT_SYMBOL(cmdq_pkt_write_subsys);
|
||||
|
||||
int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys,
|
||||
u16 offset, u32 value, u32 mask)
|
||||
{
|
||||
|
|
@ -218,6 +264,27 @@ int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys,
|
|||
}
|
||||
EXPORT_SYMBOL(cmdq_pkt_write_mask);
|
||||
|
||||
int cmdq_pkt_write_mask_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, u32 pa_base,
|
||||
u16 offset, u32 value, u32 mask)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = cmdq_pkt_assign(pkt, CMDQ_THR_SPR_IDX0, CMDQ_ADDR_HIGH(pa_base));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return cmdq_pkt_write_s_mask_value(pkt, CMDQ_THR_SPR_IDX0,
|
||||
CMDQ_ADDR_LOW(offset), value, mask);
|
||||
}
|
||||
EXPORT_SYMBOL(cmdq_pkt_write_mask_pa);
|
||||
|
||||
int cmdq_pkt_write_mask_subsys(struct cmdq_pkt *pkt, u8 subsys, u32 pa_base /*unused*/,
|
||||
u16 offset, u32 value, u32 mask)
|
||||
{
|
||||
return cmdq_pkt_write_mask(pkt, subsys, offset, value, mask);
|
||||
}
|
||||
EXPORT_SYMBOL(cmdq_pkt_write_mask_subsys);
|
||||
|
||||
int cmdq_pkt_read_s(struct cmdq_pkt *pkt, u16 high_addr_reg_idx, u16 addr_low,
|
||||
u16 reg_idx)
|
||||
{
|
||||
|
|
@ -305,6 +372,7 @@ int cmdq_pkt_mem_move(struct cmdq_pkt *pkt, dma_addr_t src_addr, dma_addr_t dst_
|
|||
int ret;
|
||||
|
||||
/* read the value of src_addr into high_addr_reg_idx */
|
||||
src_addr += pkt->priv.mminfra_offset;
|
||||
ret = cmdq_pkt_assign(pkt, high_addr_reg_idx, CMDQ_ADDR_HIGH(src_addr));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
@ -313,6 +381,7 @@ int cmdq_pkt_mem_move(struct cmdq_pkt *pkt, dma_addr_t src_addr, dma_addr_t dst_
|
|||
return ret;
|
||||
|
||||
/* write the value of value_reg_idx into dst_addr */
|
||||
dst_addr += pkt->priv.mminfra_offset;
|
||||
ret = cmdq_pkt_assign(pkt, high_addr_reg_idx, CMDQ_ADDR_HIGH(dst_addr));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
@ -438,7 +507,7 @@ int cmdq_pkt_poll_addr(struct cmdq_pkt *pkt, dma_addr_t addr, u32 value, u32 mas
|
|||
inst.op = CMDQ_CODE_MASK;
|
||||
inst.dst_t = CMDQ_REG_TYPE;
|
||||
inst.sop = CMDQ_POLL_ADDR_GPR;
|
||||
inst.value = addr;
|
||||
inst.value = addr + pkt->priv.mminfra_offset;
|
||||
ret = cmdq_pkt_append_command(pkt, inst);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
@ -498,7 +567,7 @@ int cmdq_pkt_jump_abs(struct cmdq_pkt *pkt, dma_addr_t addr, u8 shift_pa)
|
|||
struct cmdq_instruction inst = {
|
||||
.op = CMDQ_CODE_JUMP,
|
||||
.offset = CMDQ_JUMP_ABSOLUTE,
|
||||
.value = addr >> shift_pa
|
||||
.value = (addr + pkt->priv.mminfra_offset) >> pkt->priv.shift_pa
|
||||
};
|
||||
return cmdq_pkt_append_command(pkt, inst);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
|
@ -15,11 +16,17 @@
|
|||
#include <linux/soc/mediatek/dvfsrc.h>
|
||||
#include <linux/soc/mediatek/mtk_sip_svc.h>
|
||||
|
||||
/* DVFSRC_BASIC_CONTROL */
|
||||
#define DVFSRC_V4_BASIC_CTRL_OPP_COUNT GENMASK(26, 20)
|
||||
|
||||
/* DVFSRC_LEVEL */
|
||||
#define DVFSRC_V1_LEVEL_TARGET_LEVEL GENMASK(15, 0)
|
||||
#define DVFSRC_TGT_LEVEL_IDLE 0x00
|
||||
#define DVFSRC_V1_LEVEL_CURRENT_LEVEL GENMASK(31, 16)
|
||||
|
||||
#define DVFSRC_V4_LEVEL_TARGET_LEVEL GENMASK(15, 8)
|
||||
#define DVFSRC_V4_LEVEL_TARGET_PRESENT BIT(16)
|
||||
|
||||
/* DVFSRC_SW_REQ, DVFSRC_SW_REQ2 */
|
||||
#define DVFSRC_V1_SW_REQ2_DRAM_LEVEL GENMASK(1, 0)
|
||||
#define DVFSRC_V1_SW_REQ2_VCORE_LEVEL GENMASK(3, 2)
|
||||
|
|
@ -27,24 +34,40 @@
|
|||
#define DVFSRC_V2_SW_REQ_DRAM_LEVEL GENMASK(3, 0)
|
||||
#define DVFSRC_V2_SW_REQ_VCORE_LEVEL GENMASK(6, 4)
|
||||
|
||||
#define DVFSRC_V4_SW_REQ_EMI_LEVEL GENMASK(3, 0)
|
||||
#define DVFSRC_V4_SW_REQ_DRAM_LEVEL GENMASK(15, 12)
|
||||
|
||||
/* DVFSRC_VCORE */
|
||||
#define DVFSRC_V2_VCORE_REQ_VSCP_LEVEL GENMASK(14, 12)
|
||||
|
||||
/* DVFSRC_TARGET_GEAR */
|
||||
#define DVFSRC_V4_GEAR_TARGET_DRAM GENMASK(7, 0)
|
||||
#define DVFSRC_V4_GEAR_TARGET_VCORE GENMASK(15, 8)
|
||||
|
||||
/* DVFSRC_GEAR_INFO */
|
||||
#define DVFSRC_V4_GEAR_INFO_REG_WIDTH 0x4
|
||||
#define DVFSRC_V4_GEAR_INFO_REG_LEVELS 64
|
||||
#define DVFSRC_V4_GEAR_INFO_VCORE GENMASK(3, 0)
|
||||
#define DVFSRC_V4_GEAR_INFO_EMI GENMASK(7, 4)
|
||||
#define DVFSRC_V4_GEAR_INFO_DRAM GENMASK(15, 12)
|
||||
|
||||
#define DVFSRC_POLL_TIMEOUT_US 1000
|
||||
#define STARTUP_TIME_US 1
|
||||
|
||||
#define MTK_SIP_DVFSRC_INIT 0x0
|
||||
#define MTK_SIP_DVFSRC_START 0x1
|
||||
|
||||
struct dvfsrc_bw_constraints {
|
||||
u16 max_dram_nom_bw;
|
||||
u16 max_dram_peak_bw;
|
||||
u16 max_dram_hrt_bw;
|
||||
enum mtk_dvfsrc_bw_type {
|
||||
DVFSRC_BW_AVG,
|
||||
DVFSRC_BW_PEAK,
|
||||
DVFSRC_BW_HRT,
|
||||
DVFSRC_BW_MAX,
|
||||
};
|
||||
|
||||
struct dvfsrc_opp {
|
||||
u32 vcore_opp;
|
||||
u32 dram_opp;
|
||||
u32 emi_opp;
|
||||
};
|
||||
|
||||
struct dvfsrc_opp_desc {
|
||||
|
|
@ -55,6 +78,7 @@ struct dvfsrc_opp_desc {
|
|||
struct dvfsrc_soc_data;
|
||||
struct mtk_dvfsrc {
|
||||
struct device *dev;
|
||||
struct clk *clk;
|
||||
struct platform_device *icc;
|
||||
struct platform_device *regulator;
|
||||
const struct dvfsrc_soc_data *dvd;
|
||||
|
|
@ -65,11 +89,16 @@ struct mtk_dvfsrc {
|
|||
|
||||
struct dvfsrc_soc_data {
|
||||
const int *regs;
|
||||
const u8 *bw_units;
|
||||
const bool has_emi_ddr;
|
||||
const struct dvfsrc_opp_desc *opps_desc;
|
||||
u32 (*calc_dram_bw)(struct mtk_dvfsrc *dvfsrc, enum mtk_dvfsrc_bw_type type, u64 bw);
|
||||
u32 (*get_target_level)(struct mtk_dvfsrc *dvfsrc);
|
||||
u32 (*get_current_level)(struct mtk_dvfsrc *dvfsrc);
|
||||
u32 (*get_vcore_level)(struct mtk_dvfsrc *dvfsrc);
|
||||
u32 (*get_vscp_level)(struct mtk_dvfsrc *dvfsrc);
|
||||
u32 (*get_opp_count)(struct mtk_dvfsrc *dvfsrc);
|
||||
int (*get_hw_opps)(struct mtk_dvfsrc *dvfsrc);
|
||||
void (*set_dram_bw)(struct mtk_dvfsrc *dvfsrc, u64 bw);
|
||||
void (*set_dram_peak_bw)(struct mtk_dvfsrc *dvfsrc, u64 bw);
|
||||
void (*set_dram_hrt_bw)(struct mtk_dvfsrc *dvfsrc, u64 bw);
|
||||
|
|
@ -78,7 +107,22 @@ struct dvfsrc_soc_data {
|
|||
void (*set_vscp_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
|
||||
int (*wait_for_opp_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
|
||||
int (*wait_for_vcore_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
|
||||
const struct dvfsrc_bw_constraints *bw_constraints;
|
||||
|
||||
/**
|
||||
* @bw_max_constraints - array of maximum bandwidth for this hardware
|
||||
*
|
||||
* indexed by &enum mtk_dvfsrc_bw_type, storing the maximum permissible
|
||||
* hardware value for each bandwidth type.
|
||||
*/
|
||||
const u32 *const bw_max_constraints;
|
||||
|
||||
/**
|
||||
* @bw_min_constraints - array of minimum bandwidth for this hardware
|
||||
*
|
||||
* indexed by &enum mtk_dvfsrc_bw_type, storing the minimum permissible
|
||||
* hardware value for each bandwidth type.
|
||||
*/
|
||||
const u32 *const bw_min_constraints;
|
||||
};
|
||||
|
||||
static u32 dvfsrc_readl(struct mtk_dvfsrc *dvfs, u32 offset)
|
||||
|
|
@ -92,6 +136,7 @@ static void dvfsrc_writel(struct mtk_dvfsrc *dvfs, u32 offset, u32 val)
|
|||
}
|
||||
|
||||
enum dvfsrc_regs {
|
||||
DVFSRC_BASIC_CONTROL,
|
||||
DVFSRC_SW_REQ,
|
||||
DVFSRC_SW_REQ2,
|
||||
DVFSRC_LEVEL,
|
||||
|
|
@ -99,7 +144,11 @@ enum dvfsrc_regs {
|
|||
DVFSRC_SW_BW,
|
||||
DVFSRC_SW_PEAK_BW,
|
||||
DVFSRC_SW_HRT_BW,
|
||||
DVFSRC_SW_EMI_BW,
|
||||
DVFSRC_VCORE,
|
||||
DVFSRC_TARGET_GEAR,
|
||||
DVFSRC_GEAR_INFO_L,
|
||||
DVFSRC_GEAR_INFO_H,
|
||||
DVFSRC_REGS_MAX,
|
||||
};
|
||||
|
||||
|
|
@ -120,6 +169,22 @@ static const int dvfsrc_mt8195_regs[] = {
|
|||
[DVFSRC_TARGET_LEVEL] = 0xd48,
|
||||
};
|
||||
|
||||
static const int dvfsrc_mt8196_regs[] = {
|
||||
[DVFSRC_BASIC_CONTROL] = 0x0,
|
||||
[DVFSRC_SW_REQ] = 0x18,
|
||||
[DVFSRC_VCORE] = 0x80,
|
||||
[DVFSRC_GEAR_INFO_L] = 0xfc,
|
||||
[DVFSRC_SW_BW] = 0x1e8,
|
||||
[DVFSRC_SW_PEAK_BW] = 0x1f4,
|
||||
[DVFSRC_SW_HRT_BW] = 0x20c,
|
||||
[DVFSRC_LEVEL] = 0x5f0,
|
||||
[DVFSRC_TARGET_LEVEL] = 0x5f0,
|
||||
[DVFSRC_SW_REQ2] = 0x604,
|
||||
[DVFSRC_SW_EMI_BW] = 0x60c,
|
||||
[DVFSRC_TARGET_GEAR] = 0x6ac,
|
||||
[DVFSRC_GEAR_INFO_H] = 0x6b0,
|
||||
};
|
||||
|
||||
static const struct dvfsrc_opp *dvfsrc_get_current_opp(struct mtk_dvfsrc *dvfsrc)
|
||||
{
|
||||
u32 level = dvfsrc->dvd->get_current_level(dvfsrc);
|
||||
|
|
@ -127,6 +192,20 @@ static const struct dvfsrc_opp *dvfsrc_get_current_opp(struct mtk_dvfsrc *dvfsrc
|
|||
return &dvfsrc->curr_opps->opps[level];
|
||||
}
|
||||
|
||||
static u32 dvfsrc_get_current_target_vcore_gear(struct mtk_dvfsrc *dvfsrc)
|
||||
{
|
||||
u32 val = dvfsrc_readl(dvfsrc, DVFSRC_TARGET_GEAR);
|
||||
|
||||
return FIELD_GET(DVFSRC_V4_GEAR_TARGET_VCORE, val);
|
||||
}
|
||||
|
||||
static u32 dvfsrc_get_current_target_dram_gear(struct mtk_dvfsrc *dvfsrc)
|
||||
{
|
||||
u32 val = dvfsrc_readl(dvfsrc, DVFSRC_TARGET_GEAR);
|
||||
|
||||
return FIELD_GET(DVFSRC_V4_GEAR_TARGET_DRAM, val);
|
||||
}
|
||||
|
||||
static bool dvfsrc_is_idle(struct mtk_dvfsrc *dvfsrc)
|
||||
{
|
||||
if (!dvfsrc->dvd->get_target_level)
|
||||
|
|
@ -183,6 +262,24 @@ static int dvfsrc_wait_for_opp_level_v2(struct mtk_dvfsrc *dvfsrc, u32 level)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dvfsrc_wait_for_vcore_level_v4(struct mtk_dvfsrc *dvfsrc, u32 level)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
return readx_poll_timeout_atomic(dvfsrc_get_current_target_vcore_gear,
|
||||
dvfsrc, val, val >= level,
|
||||
STARTUP_TIME_US, DVFSRC_POLL_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static int dvfsrc_wait_for_opp_level_v4(struct mtk_dvfsrc *dvfsrc, u32 level)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
return readx_poll_timeout_atomic(dvfsrc_get_current_target_dram_gear,
|
||||
dvfsrc, val, val >= level,
|
||||
STARTUP_TIME_US, DVFSRC_POLL_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static u32 dvfsrc_get_target_level_v1(struct mtk_dvfsrc *dvfsrc)
|
||||
{
|
||||
u32 val = dvfsrc_readl(dvfsrc, DVFSRC_LEVEL);
|
||||
|
|
@ -216,6 +313,27 @@ static u32 dvfsrc_get_current_level_v2(struct mtk_dvfsrc *dvfsrc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u32 dvfsrc_get_target_level_v4(struct mtk_dvfsrc *dvfsrc)
|
||||
{
|
||||
u32 val = dvfsrc_readl(dvfsrc, DVFSRC_TARGET_LEVEL);
|
||||
|
||||
if (val & DVFSRC_V4_LEVEL_TARGET_PRESENT)
|
||||
return FIELD_GET(DVFSRC_V4_LEVEL_TARGET_LEVEL, val) + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 dvfsrc_get_current_level_v4(struct mtk_dvfsrc *dvfsrc)
|
||||
{
|
||||
u32 level = dvfsrc_readl(dvfsrc, DVFSRC_LEVEL) + 1;
|
||||
|
||||
/* Valid levels */
|
||||
if (level < dvfsrc->curr_opps->num_opp)
|
||||
return dvfsrc->curr_opps->num_opp - level;
|
||||
|
||||
/* Zero for level 0 or invalid level */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 dvfsrc_get_vcore_level_v1(struct mtk_dvfsrc *dvfsrc)
|
||||
{
|
||||
u32 val = dvfsrc_readl(dvfsrc, DVFSRC_SW_REQ2);
|
||||
|
|
@ -267,39 +385,69 @@ static void dvfsrc_set_vscp_level_v2(struct mtk_dvfsrc *dvfsrc, u32 level)
|
|||
dvfsrc_writel(dvfsrc, DVFSRC_VCORE, val);
|
||||
}
|
||||
|
||||
static void __dvfsrc_set_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, u32 reg,
|
||||
u16 max_bw, u16 min_bw, u64 bw)
|
||||
static u32 dvfsrc_get_opp_count_v4(struct mtk_dvfsrc *dvfsrc)
|
||||
{
|
||||
u32 new_bw = (u32)div_u64(bw, 100 * 1000);
|
||||
u32 val = dvfsrc_readl(dvfsrc, DVFSRC_BASIC_CONTROL);
|
||||
|
||||
/* If bw constraints (in mbps) are defined make sure to respect them */
|
||||
if (max_bw)
|
||||
new_bw = min(new_bw, max_bw);
|
||||
if (min_bw && new_bw > 0)
|
||||
new_bw = max(new_bw, min_bw);
|
||||
return FIELD_GET(DVFSRC_V4_BASIC_CTRL_OPP_COUNT, val) + 1;
|
||||
}
|
||||
|
||||
dvfsrc_writel(dvfsrc, reg, new_bw);
|
||||
static u32
|
||||
dvfsrc_calc_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, enum mtk_dvfsrc_bw_type type, u64 bw)
|
||||
{
|
||||
return clamp_val(div_u64(bw, 100 * 1000), dvfsrc->dvd->bw_min_constraints[type],
|
||||
dvfsrc->dvd->bw_max_constraints[type]);
|
||||
}
|
||||
|
||||
/**
|
||||
* dvfsrc_calc_dram_bw_v4 - convert kbps to hardware register bandwidth value
|
||||
* @dvfsrc: pointer to the &struct mtk_dvfsrc of this driver instance
|
||||
* @type: one of %DVFSRC_BW_AVG, %DVFSRC_BW_PEAK, or %DVFSRC_BW_HRT
|
||||
* @bw: the bandwidth in kilobits per second
|
||||
*
|
||||
* Returns the hardware register value appropriate for expressing @bw, clamped
|
||||
* to hardware limits.
|
||||
*/
|
||||
static u32
|
||||
dvfsrc_calc_dram_bw_v4(struct mtk_dvfsrc *dvfsrc, enum mtk_dvfsrc_bw_type type, u64 bw)
|
||||
{
|
||||
u8 bw_unit = dvfsrc->dvd->bw_units[type];
|
||||
u64 bw_mbps;
|
||||
u32 bw_hw;
|
||||
|
||||
if (type < DVFSRC_BW_AVG || type >= DVFSRC_BW_MAX)
|
||||
return 0;
|
||||
|
||||
bw_mbps = div_u64(bw, 1000);
|
||||
bw_hw = div_u64((bw_mbps + bw_unit - 1), bw_unit);
|
||||
return clamp_val(bw_hw, dvfsrc->dvd->bw_min_constraints[type],
|
||||
dvfsrc->dvd->bw_max_constraints[type]);
|
||||
}
|
||||
|
||||
static void __dvfsrc_set_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, u32 reg,
|
||||
enum mtk_dvfsrc_bw_type type, u64 bw)
|
||||
{
|
||||
u32 bw_hw = dvfsrc->dvd->calc_dram_bw(dvfsrc, type, bw);
|
||||
|
||||
dvfsrc_writel(dvfsrc, reg, bw_hw);
|
||||
|
||||
if (type == DVFSRC_BW_AVG && dvfsrc->dvd->has_emi_ddr)
|
||||
dvfsrc_writel(dvfsrc, DVFSRC_SW_EMI_BW, bw_hw);
|
||||
}
|
||||
|
||||
static void dvfsrc_set_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, u64 bw)
|
||||
{
|
||||
u64 max_bw = dvfsrc->dvd->bw_constraints->max_dram_nom_bw;
|
||||
|
||||
__dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_BW, max_bw, 0, bw);
|
||||
__dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_BW, DVFSRC_BW_AVG, bw);
|
||||
};
|
||||
|
||||
static void dvfsrc_set_dram_peak_bw_v1(struct mtk_dvfsrc *dvfsrc, u64 bw)
|
||||
{
|
||||
u64 max_bw = dvfsrc->dvd->bw_constraints->max_dram_peak_bw;
|
||||
|
||||
__dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_PEAK_BW, max_bw, 0, bw);
|
||||
__dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_PEAK_BW, DVFSRC_BW_PEAK, bw);
|
||||
}
|
||||
|
||||
static void dvfsrc_set_dram_hrt_bw_v1(struct mtk_dvfsrc *dvfsrc, u64 bw)
|
||||
{
|
||||
u64 max_bw = dvfsrc->dvd->bw_constraints->max_dram_hrt_bw;
|
||||
|
||||
__dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_HRT_BW, max_bw, 0, bw);
|
||||
__dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_HRT_BW, DVFSRC_BW_HRT, bw);
|
||||
}
|
||||
|
||||
static void dvfsrc_set_opp_level_v1(struct mtk_dvfsrc *dvfsrc, u32 level)
|
||||
|
|
@ -315,6 +463,100 @@ static void dvfsrc_set_opp_level_v1(struct mtk_dvfsrc *dvfsrc, u32 level)
|
|||
dvfsrc_writel(dvfsrc, DVFSRC_SW_REQ, val);
|
||||
}
|
||||
|
||||
static u32 dvfsrc_get_opp_gear(struct mtk_dvfsrc *dvfsrc, u8 level)
|
||||
{
|
||||
u32 reg_ofst, val;
|
||||
u8 idx;
|
||||
|
||||
/* Calculate register offset and index for requested gear */
|
||||
if (level < DVFSRC_V4_GEAR_INFO_REG_LEVELS) {
|
||||
reg_ofst = dvfsrc->dvd->regs[DVFSRC_GEAR_INFO_L];
|
||||
idx = level;
|
||||
} else {
|
||||
reg_ofst = dvfsrc->dvd->regs[DVFSRC_GEAR_INFO_H];
|
||||
idx = level - DVFSRC_V4_GEAR_INFO_REG_LEVELS;
|
||||
}
|
||||
reg_ofst += DVFSRC_V4_GEAR_INFO_REG_WIDTH * (level / 2);
|
||||
|
||||
/* Read the corresponding gear register */
|
||||
val = readl(dvfsrc->regs + reg_ofst);
|
||||
|
||||
/* Each register contains two sets of data, 16 bits per gear */
|
||||
val >>= 16 * (idx % 2);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int dvfsrc_get_hw_opps_v4(struct mtk_dvfsrc *dvfsrc)
|
||||
{
|
||||
struct dvfsrc_opp *dvfsrc_opps;
|
||||
struct dvfsrc_opp_desc *desc;
|
||||
u32 num_opps, gear_info;
|
||||
u8 num_vcore, num_dram;
|
||||
u8 num_emi;
|
||||
int i;
|
||||
|
||||
num_opps = dvfsrc_get_opp_count_v4(dvfsrc);
|
||||
if (num_opps == 0) {
|
||||
dev_err(dvfsrc->dev, "No OPPs programmed in DVFSRC MCU.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The first 16 bits set in the gear info table says how many OPPs
|
||||
* and how many vcore, dram and emi table entries are available.
|
||||
*/
|
||||
gear_info = dvfsrc_readl(dvfsrc, DVFSRC_GEAR_INFO_L);
|
||||
if (gear_info == 0) {
|
||||
dev_err(dvfsrc->dev, "No gear info in DVFSRC MCU.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
num_vcore = FIELD_GET(DVFSRC_V4_GEAR_INFO_VCORE, gear_info) + 1;
|
||||
num_dram = FIELD_GET(DVFSRC_V4_GEAR_INFO_DRAM, gear_info) + 1;
|
||||
num_emi = FIELD_GET(DVFSRC_V4_GEAR_INFO_EMI, gear_info) + 1;
|
||||
dev_info(dvfsrc->dev,
|
||||
"Discovered %u gears and %u vcore, %u dram, %u emi table entries.\n",
|
||||
num_opps, num_vcore, num_dram, num_emi);
|
||||
|
||||
/* Allocate everything now as anything else after that cannot fail */
|
||||
desc = devm_kzalloc(dvfsrc->dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
|
||||
dvfsrc_opps = devm_kcalloc(dvfsrc->dev, num_opps + 1,
|
||||
sizeof(*dvfsrc_opps), GFP_KERNEL);
|
||||
if (!dvfsrc_opps)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Read the OPP table gear indices */
|
||||
for (i = 0; i <= num_opps; i++) {
|
||||
gear_info = dvfsrc_get_opp_gear(dvfsrc, num_opps - i);
|
||||
dvfsrc_opps[i].vcore_opp = FIELD_GET(DVFSRC_V4_GEAR_INFO_VCORE, gear_info);
|
||||
dvfsrc_opps[i].dram_opp = FIELD_GET(DVFSRC_V4_GEAR_INFO_DRAM, gear_info);
|
||||
dvfsrc_opps[i].emi_opp = FIELD_GET(DVFSRC_V4_GEAR_INFO_EMI, gear_info);
|
||||
};
|
||||
desc->num_opp = num_opps + 1;
|
||||
desc->opps = dvfsrc_opps;
|
||||
|
||||
/* Assign to main structure now that everything is done! */
|
||||
dvfsrc->curr_opps = desc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dvfsrc_set_dram_level_v4(struct mtk_dvfsrc *dvfsrc, u32 level)
|
||||
{
|
||||
u32 val = dvfsrc_readl(dvfsrc, DVFSRC_SW_REQ);
|
||||
|
||||
val &= ~DVFSRC_V4_SW_REQ_DRAM_LEVEL;
|
||||
val |= FIELD_PREP(DVFSRC_V4_SW_REQ_DRAM_LEVEL, level);
|
||||
|
||||
dev_dbg(dvfsrc->dev, "%s level=%u\n", __func__, level);
|
||||
|
||||
dvfsrc_writel(dvfsrc, DVFSRC_SW_REQ, val);
|
||||
}
|
||||
|
||||
int mtk_dvfsrc_send_request(const struct device *dev, u32 cmd, u64 data)
|
||||
{
|
||||
struct mtk_dvfsrc *dvfsrc = dev_get_drvdata(dev);
|
||||
|
|
@ -422,6 +664,11 @@ static int mtk_dvfsrc_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(dvfsrc->regs))
|
||||
return PTR_ERR(dvfsrc->regs);
|
||||
|
||||
dvfsrc->clk = devm_clk_get_enabled(&pdev->dev, NULL);
|
||||
if (IS_ERR(dvfsrc->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(dvfsrc->clk),
|
||||
"Couldn't get and enable DVFSRC clock\n");
|
||||
|
||||
arm_smccc_smc(MTK_SIP_DVFSRC_VCOREFS_CONTROL, MTK_SIP_DVFSRC_INIT,
|
||||
0, 0, 0, 0, 0, 0, &ares);
|
||||
if (ares.a0)
|
||||
|
|
@ -430,7 +677,14 @@ static int mtk_dvfsrc_probe(struct platform_device *pdev)
|
|||
dvfsrc->dram_type = ares.a1;
|
||||
dev_dbg(&pdev->dev, "DRAM Type: %d\n", dvfsrc->dram_type);
|
||||
|
||||
dvfsrc->curr_opps = &dvfsrc->dvd->opps_desc[dvfsrc->dram_type];
|
||||
/* Newer versions of the DVFSRC MCU have pre-programmed gear tables */
|
||||
if (dvfsrc->dvd->get_hw_opps) {
|
||||
ret = dvfsrc->dvd->get_hw_opps(dvfsrc);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
dvfsrc->curr_opps = &dvfsrc->dvd->opps_desc[dvfsrc->dram_type];
|
||||
}
|
||||
platform_set_drvdata(pdev, dvfsrc);
|
||||
|
||||
ret = devm_of_platform_populate(&pdev->dev);
|
||||
|
|
@ -440,17 +694,28 @@ static int mtk_dvfsrc_probe(struct platform_device *pdev)
|
|||
/* Everything is set up - make it run! */
|
||||
arm_smccc_smc(MTK_SIP_DVFSRC_VCOREFS_CONTROL, MTK_SIP_DVFSRC_START,
|
||||
0, 0, 0, 0, 0, 0, &ares);
|
||||
if (ares.a0)
|
||||
if (ares.a0 & BIT(0))
|
||||
return dev_err_probe(&pdev->dev, -EINVAL, "Cannot start DVFSRC: %lu\n", ares.a0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dvfsrc_bw_constraints dvfsrc_bw_constr_v1 = { 0, 0, 0 };
|
||||
static const struct dvfsrc_bw_constraints dvfsrc_bw_constr_v2 = {
|
||||
.max_dram_nom_bw = 255,
|
||||
.max_dram_peak_bw = 255,
|
||||
.max_dram_hrt_bw = 1023,
|
||||
static const u32 dvfsrc_bw_min_constr_none[DVFSRC_BW_MAX] = {
|
||||
[DVFSRC_BW_AVG] = 0,
|
||||
[DVFSRC_BW_PEAK] = 0,
|
||||
[DVFSRC_BW_HRT] = 0,
|
||||
};
|
||||
|
||||
static const u32 dvfsrc_bw_max_constr_v1[DVFSRC_BW_MAX] = {
|
||||
[DVFSRC_BW_AVG] = U32_MAX,
|
||||
[DVFSRC_BW_PEAK] = U32_MAX,
|
||||
[DVFSRC_BW_HRT] = U32_MAX,
|
||||
};
|
||||
|
||||
static const u32 dvfsrc_bw_max_constr_v2[DVFSRC_BW_MAX] = {
|
||||
[DVFSRC_BW_AVG] = 65535,
|
||||
[DVFSRC_BW_PEAK] = 65535,
|
||||
[DVFSRC_BW_HRT] = 1023,
|
||||
};
|
||||
|
||||
static const struct dvfsrc_opp dvfsrc_opp_mt6893_lp4[] = {
|
||||
|
|
@ -483,7 +748,8 @@ static const struct dvfsrc_soc_data mt6893_data = {
|
|||
.set_vscp_level = dvfsrc_set_vscp_level_v2,
|
||||
.wait_for_opp_level = dvfsrc_wait_for_opp_level_v2,
|
||||
.wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v1,
|
||||
.bw_constraints = &dvfsrc_bw_constr_v2,
|
||||
.bw_max_constraints = dvfsrc_bw_max_constr_v2,
|
||||
.bw_min_constraints = dvfsrc_bw_min_constr_none,
|
||||
};
|
||||
|
||||
static const struct dvfsrc_opp dvfsrc_opp_mt8183_lp4[] = {
|
||||
|
|
@ -512,6 +778,7 @@ static const struct dvfsrc_opp_desc dvfsrc_opp_mt8183_desc[] = {
|
|||
static const struct dvfsrc_soc_data mt8183_data = {
|
||||
.opps_desc = dvfsrc_opp_mt8183_desc,
|
||||
.regs = dvfsrc_mt8183_regs,
|
||||
.calc_dram_bw = dvfsrc_calc_dram_bw_v1,
|
||||
.get_target_level = dvfsrc_get_target_level_v1,
|
||||
.get_current_level = dvfsrc_get_current_level_v1,
|
||||
.get_vcore_level = dvfsrc_get_vcore_level_v1,
|
||||
|
|
@ -520,7 +787,8 @@ static const struct dvfsrc_soc_data mt8183_data = {
|
|||
.set_vcore_level = dvfsrc_set_vcore_level_v1,
|
||||
.wait_for_opp_level = dvfsrc_wait_for_opp_level_v1,
|
||||
.wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v1,
|
||||
.bw_constraints = &dvfsrc_bw_constr_v1,
|
||||
.bw_max_constraints = dvfsrc_bw_max_constr_v1,
|
||||
.bw_min_constraints = dvfsrc_bw_min_constr_none,
|
||||
};
|
||||
|
||||
static const struct dvfsrc_opp dvfsrc_opp_mt8195_lp4[] = {
|
||||
|
|
@ -542,6 +810,7 @@ static const struct dvfsrc_opp_desc dvfsrc_opp_mt8195_desc[] = {
|
|||
static const struct dvfsrc_soc_data mt8195_data = {
|
||||
.opps_desc = dvfsrc_opp_mt8195_desc,
|
||||
.regs = dvfsrc_mt8195_regs,
|
||||
.calc_dram_bw = dvfsrc_calc_dram_bw_v1,
|
||||
.get_target_level = dvfsrc_get_target_level_v2,
|
||||
.get_current_level = dvfsrc_get_current_level_v2,
|
||||
.get_vcore_level = dvfsrc_get_vcore_level_v2,
|
||||
|
|
@ -553,13 +822,44 @@ static const struct dvfsrc_soc_data mt8195_data = {
|
|||
.set_vscp_level = dvfsrc_set_vscp_level_v2,
|
||||
.wait_for_opp_level = dvfsrc_wait_for_opp_level_v2,
|
||||
.wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v1,
|
||||
.bw_constraints = &dvfsrc_bw_constr_v2,
|
||||
.bw_max_constraints = dvfsrc_bw_max_constr_v2,
|
||||
.bw_min_constraints = dvfsrc_bw_min_constr_none,
|
||||
};
|
||||
|
||||
static const u8 mt8196_bw_units[] = {
|
||||
[DVFSRC_BW_AVG] = 64,
|
||||
[DVFSRC_BW_PEAK] = 64,
|
||||
[DVFSRC_BW_HRT] = 30,
|
||||
};
|
||||
|
||||
static const struct dvfsrc_soc_data mt8196_data = {
|
||||
.regs = dvfsrc_mt8196_regs,
|
||||
.bw_units = mt8196_bw_units,
|
||||
.has_emi_ddr = true,
|
||||
.get_target_level = dvfsrc_get_target_level_v4,
|
||||
.get_current_level = dvfsrc_get_current_level_v4,
|
||||
.get_vcore_level = dvfsrc_get_vcore_level_v2,
|
||||
.get_vscp_level = dvfsrc_get_vscp_level_v2,
|
||||
.get_opp_count = dvfsrc_get_opp_count_v4,
|
||||
.get_hw_opps = dvfsrc_get_hw_opps_v4,
|
||||
.calc_dram_bw = dvfsrc_calc_dram_bw_v4,
|
||||
.set_dram_bw = dvfsrc_set_dram_bw_v1,
|
||||
.set_dram_peak_bw = dvfsrc_set_dram_peak_bw_v1,
|
||||
.set_dram_hrt_bw = dvfsrc_set_dram_hrt_bw_v1,
|
||||
.set_opp_level = dvfsrc_set_dram_level_v4,
|
||||
.set_vcore_level = dvfsrc_set_vcore_level_v2,
|
||||
.set_vscp_level = dvfsrc_set_vscp_level_v2,
|
||||
.wait_for_opp_level = dvfsrc_wait_for_opp_level_v4,
|
||||
.wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v4,
|
||||
.bw_max_constraints = dvfsrc_bw_max_constr_v2,
|
||||
.bw_min_constraints = dvfsrc_bw_min_constr_none,
|
||||
};
|
||||
|
||||
static const struct of_device_id mtk_dvfsrc_of_match[] = {
|
||||
{ .compatible = "mediatek,mt6893-dvfsrc", .data = &mt6893_data },
|
||||
{ .compatible = "mediatek,mt8183-dvfsrc", .data = &mt8183_data },
|
||||
{ .compatible = "mediatek,mt8195-dvfsrc", .data = &mt8195_data },
|
||||
{ .compatible = "mediatek,mt8196-dvfsrc", .data = &mt8196_data },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ static struct socinfo_data socinfo_data_table[] = {
|
|||
MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EZA", "Kompanio 1380", 0x81950400, CELL_NOT_USED),
|
||||
MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EHZA", "Kompanio 1380", 0x81950404, CELL_NOT_USED),
|
||||
MTK_SOCINFO_ENTRY("MT8370", "MT8370AV/AZA", "Genio 510", 0x83700000, 0x00000081),
|
||||
MTK_SOCINFO_ENTRY("MT8371", "MT8371AV/AZA", "Genio 520", 0x83710000, 0x00000081),
|
||||
MTK_SOCINFO_ENTRY("MT8390", "MT8390AV/AZA", "Genio 700", 0x83900000, 0x00000080),
|
||||
MTK_SOCINFO_ENTRY("MT8391", "MT8391AV/AZA", "Genio 720", 0x83910000, 0x00000080),
|
||||
MTK_SOCINFO_ENTRY("MT8395", "MT8395AV/ZA", "Genio 1200", 0x83950100, CELL_NOT_USED),
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/bits.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
|
@ -789,7 +790,7 @@ static ssize_t svs_enable_debug_write(struct file *filp,
|
|||
struct svs_bank *svsb = file_inode(filp)->i_private;
|
||||
struct svs_platform *svsp = dev_get_drvdata(svsb->dev);
|
||||
int enabled, ret;
|
||||
char *buf = NULL;
|
||||
char *buf __free(kfree) = NULL;
|
||||
|
||||
if (count >= PAGE_SIZE)
|
||||
return -EINVAL;
|
||||
|
|
@ -807,8 +808,6 @@ static ssize_t svs_enable_debug_write(struct file *filp,
|
|||
svsb->mode_support = SVSB_MODE_ALL_DISABLE;
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -349,15 +349,16 @@ static int cmd_db_dev_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
cmd_db_header = memremap(rmem->base, rmem->size, MEMREMAP_WC);
|
||||
if (!cmd_db_header) {
|
||||
ret = -ENOMEM;
|
||||
cmd_db_header = devm_memremap(&pdev->dev, rmem->base, rmem->size, MEMREMAP_WC);
|
||||
if (IS_ERR(cmd_db_header)) {
|
||||
ret = PTR_ERR(cmd_db_header);
|
||||
cmd_db_header = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!cmd_db_magic_matches(cmd_db_header)) {
|
||||
dev_err(&pdev->dev, "Invalid Command DB Magic\n");
|
||||
cmd_db_header = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -182,6 +182,197 @@ enum llcc_reg_offset {
|
|||
LLCC_TRP_WRS_CACHEABLE_EN,
|
||||
};
|
||||
|
||||
static const struct llcc_slice_config glymur_data[] = {
|
||||
{
|
||||
.usecase_id = LLCC_CPUSS,
|
||||
.slice_id = 1,
|
||||
.max_cap = 7680,
|
||||
.priority = 1,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.vict_prio = true,
|
||||
.activate_on_init = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_VIDSC0,
|
||||
.slice_id = 2,
|
||||
.max_cap = 512,
|
||||
.priority = 3,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.vict_prio = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_AUDIO,
|
||||
.slice_id = 6,
|
||||
.max_cap = 1024,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.vict_prio = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_VIDSC1,
|
||||
.slice_id = 4,
|
||||
.max_cap = 512,
|
||||
.priority = 3,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.vict_prio = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_CMPT,
|
||||
.slice_id = 10,
|
||||
.max_cap = 7680,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.vict_prio = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_GPUHTW,
|
||||
.slice_id = 11,
|
||||
.max_cap = 512,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.vict_prio = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_GPU,
|
||||
.slice_id = 9,
|
||||
.max_cap = 7680,
|
||||
.priority = 1,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.write_scid_en = true,
|
||||
.write_scid_cacheable_en = true,
|
||||
.stale_en = true,
|
||||
.vict_prio = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_MMUHWT,
|
||||
.slice_id = 18,
|
||||
.max_cap = 768,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.vict_prio = true,
|
||||
.activate_on_init = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_AUDHW,
|
||||
.slice_id = 22,
|
||||
.max_cap = 1024,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.vict_prio = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_CVP,
|
||||
.slice_id = 8,
|
||||
.max_cap = 64,
|
||||
.priority = 3,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.vict_prio = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_WRCACHE,
|
||||
.slice_id = 31,
|
||||
.max_cap = 1536,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.vict_prio = true,
|
||||
.activate_on_init = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_CMPTHCP,
|
||||
.slice_id = 17,
|
||||
.max_cap = 256,
|
||||
.priority = 3,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.vict_prio = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_LCPDARE,
|
||||
.slice_id = 30,
|
||||
.max_cap = 768,
|
||||
.priority = 3,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.alloc_oneway_en = true,
|
||||
.vict_prio = true,
|
||||
.activate_on_init = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_AENPU,
|
||||
.slice_id = 3,
|
||||
.max_cap = 3072,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.cache_mode = 2,
|
||||
.vict_prio = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_ISLAND1,
|
||||
.slice_id = 12,
|
||||
.max_cap = 5632,
|
||||
.priority = 7,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0x0,
|
||||
.res_ways = 0x7FF,
|
||||
.vict_prio = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_VIDVSP,
|
||||
.slice_id = 28,
|
||||
.max_cap = 256,
|
||||
.priority = 3,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.vict_prio = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_OOBM_NS,
|
||||
.slice_id = 5,
|
||||
.max_cap = 512,
|
||||
.priority = 1,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.vict_prio = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_CPUSS_OPP,
|
||||
.slice_id = 32,
|
||||
.max_cap = 0,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0x0,
|
||||
.res_ways = 0x0,
|
||||
.vict_prio = true,
|
||||
.activate_on_init = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_PCIE_TCU,
|
||||
.slice_id = 19,
|
||||
.max_cap = 256,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.vict_prio = true,
|
||||
.activate_on_init = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_VIDSC_VSP1,
|
||||
.slice_id = 29,
|
||||
.max_cap = 256,
|
||||
.priority = 3,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xFFF,
|
||||
.res_ways = 0x0,
|
||||
.vict_prio = true,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct llcc_slice_config ipq5424_data[] = {
|
||||
{
|
||||
.usecase_id = LLCC_CPUSS,
|
||||
|
|
@ -3872,6 +4063,16 @@ static const struct qcom_llcc_config kaanapali_cfg[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config glymur_cfg[] = {
|
||||
{
|
||||
.sct_data = glymur_data,
|
||||
.size = ARRAY_SIZE(glymur_data),
|
||||
.reg_offset = llcc_v6_reg_offset,
|
||||
.edac_reg_offset = &llcc_v2_1_edac_reg_offset,
|
||||
.no_edac = true,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config qcs615_cfg[] = {
|
||||
{
|
||||
.sct_data = qcs615_data,
|
||||
|
|
@ -4103,6 +4304,11 @@ static const struct qcom_sct_config kaanapali_cfgs = {
|
|||
.num_config = ARRAY_SIZE(kaanapali_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config glymur_cfgs = {
|
||||
.llcc_config = glymur_cfg,
|
||||
.num_config = ARRAY_SIZE(glymur_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config qcs615_cfgs = {
|
||||
.llcc_config = qcs615_cfg,
|
||||
.num_config = ARRAY_SIZE(qcs615_cfg),
|
||||
|
|
@ -4941,6 +5147,7 @@ err:
|
|||
}
|
||||
|
||||
static const struct of_device_id qcom_llcc_of_match[] = {
|
||||
{ .compatible = "qcom,glymur-llcc", .data = &glymur_cfgs },
|
||||
{ .compatible = "qcom,ipq5424-llcc", .data = &ipq5424_cfgs},
|
||||
{ .compatible = "qcom,kaanapali-llcc", .data = &kaanapali_cfgs},
|
||||
{ .compatible = "qcom,qcs615-llcc", .data = &qcs615_cfgs},
|
||||
|
|
|
|||
|
|
@ -227,20 +227,9 @@ void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata);
|
||||
|
||||
/**
|
||||
* qcom_mdt_pas_init() - initialize PAS region for firmware loading
|
||||
* @dev: device handle to associate resources with
|
||||
* @fw: firmware object for the mdt file
|
||||
* @fw_name: name of the firmware, for construction of segment file names
|
||||
* @pas_id: PAS identifier
|
||||
* @mem_phys: physical address of allocated memory region
|
||||
* @ctx: PAS metadata context, to be released by caller
|
||||
*
|
||||
* Returns 0 on success, negative errno otherwise.
|
||||
*/
|
||||
int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
|
||||
const char *fw_name, int pas_id, phys_addr_t mem_phys,
|
||||
struct qcom_scm_pas_metadata *ctx)
|
||||
static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
|
||||
const char *fw_name, int pas_id, phys_addr_t mem_phys,
|
||||
struct qcom_scm_pas_context *ctx)
|
||||
{
|
||||
const struct elf32_phdr *phdrs;
|
||||
const struct elf32_phdr *phdr;
|
||||
|
|
@ -302,7 +291,6 @@ int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
|
|||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_mdt_pas_init);
|
||||
|
||||
static bool qcom_mdt_bins_are_split(const struct firmware *fw)
|
||||
{
|
||||
|
|
@ -469,7 +457,7 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw,
|
|||
{
|
||||
int ret;
|
||||
|
||||
ret = qcom_mdt_pas_init(dev, fw, fw_name, pas_id, mem_phys, NULL);
|
||||
ret = __qcom_mdt_pas_init(dev, fw, fw_name, pas_id, mem_phys, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -478,5 +466,36 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_mdt_load);
|
||||
|
||||
/**
|
||||
* qcom_mdt_pas_load - Loads and authenticates the metadata of the firmware
|
||||
* (typically contained in the .mdt file), followed by loading the actual
|
||||
* firmware segments (e.g., .bXX files). Authentication of the segments done
|
||||
* by a separate call.
|
||||
*
|
||||
* The PAS context must be initialized using qcom_scm_pas_context_init()
|
||||
* prior to invoking this function.
|
||||
*
|
||||
* @ctx: Pointer to the PAS (Peripheral Authentication Service) context
|
||||
* @fw: Firmware object representing the .mdt file
|
||||
* @firmware: Name of the firmware used to construct segment file names
|
||||
* @mem_region: Memory region allocated for loading the firmware
|
||||
* @reloc_base: Physical address adjusted after relocation
|
||||
*
|
||||
* Return: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *fw,
|
||||
const char *firmware, void *mem_region, phys_addr_t *reloc_base)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __qcom_mdt_pas_init(ctx->dev, fw, firmware, ctx->pas_id, ctx->mem_phys, ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return qcom_mdt_load_no_init(ctx->dev, fw, firmware, mem_region, ctx->mem_phys,
|
||||
ctx->mem_size, reloc_base);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_mdt_pas_load);
|
||||
|
||||
MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
|||
|
|
@ -14,10 +14,12 @@
|
|||
#include <linux/soc/qcom/pdr.h>
|
||||
#include <drm/bridge/aux-bridge.h>
|
||||
|
||||
#include <linux/usb/pd.h>
|
||||
#include <linux/usb/typec_altmode.h>
|
||||
#include <linux/usb/typec_dp.h>
|
||||
#include <linux/usb/typec_mux.h>
|
||||
#include <linux/usb/typec_retimer.h>
|
||||
#include <linux/usb/typec_tbt.h>
|
||||
|
||||
#include <linux/soc/qcom/pmic_glink.h>
|
||||
|
||||
|
|
@ -37,11 +39,38 @@ struct usbc_write_req {
|
|||
__le32 reserved;
|
||||
};
|
||||
|
||||
#define NOTIFY_PAYLOAD_SIZE 16
|
||||
struct usbc_sc8280x_dp_data {
|
||||
u8 pin_assignment : 6;
|
||||
u8 hpd_state : 1;
|
||||
u8 hpd_irq : 1;
|
||||
u8 res[7];
|
||||
};
|
||||
|
||||
/* Used for both TBT and USB4 notifications */
|
||||
struct usbc_sc8280x_tbt_data {
|
||||
u8 usb_speed : 3;
|
||||
u8 cable_type : 3;
|
||||
/* This field is NOP on USB4, all cables support rounded rates by spec */
|
||||
u8 rounded_cable : 1;
|
||||
u8 power_limited : 1;
|
||||
u8 res[11];
|
||||
};
|
||||
|
||||
struct usbc_notify {
|
||||
struct pmic_glink_hdr hdr;
|
||||
char payload[NOTIFY_PAYLOAD_SIZE];
|
||||
u32 reserved;
|
||||
u8 port_idx;
|
||||
u8 orientation;
|
||||
u8 mux_ctrl;
|
||||
#define MUX_CTRL_STATE_NO_CONN 0
|
||||
#define MUX_CTRL_STATE_TUNNELING 4
|
||||
|
||||
u8 res;
|
||||
__le16 vid;
|
||||
__le16 svid;
|
||||
union usbc_sc8280x_extended_data {
|
||||
struct usbc_sc8280x_dp_data dp;
|
||||
struct usbc_sc8280x_tbt_data tbt;
|
||||
} extended_data;
|
||||
};
|
||||
|
||||
struct usbc_sc8180x_notify {
|
||||
|
|
@ -74,6 +103,7 @@ struct pmic_glink_altmode_port {
|
|||
struct typec_retimer *typec_retimer;
|
||||
struct typec_retimer_state retimer_state;
|
||||
struct typec_altmode dp_alt;
|
||||
struct typec_altmode tbt_alt;
|
||||
|
||||
struct work_struct work;
|
||||
|
||||
|
|
@ -81,10 +111,12 @@ struct pmic_glink_altmode_port {
|
|||
|
||||
enum typec_orientation orientation;
|
||||
u16 svid;
|
||||
struct usbc_sc8280x_tbt_data tbt_data;
|
||||
u8 dp_data;
|
||||
u8 mode;
|
||||
u8 hpd_state;
|
||||
u8 hpd_irq;
|
||||
u8 mux_ctrl;
|
||||
};
|
||||
|
||||
#define work_to_altmode(w) container_of((w), struct pmic_glink_altmode, enable_work)
|
||||
|
|
@ -170,6 +202,102 @@ static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode,
|
|||
dev_err(altmode->dev, "failed to setup retimer to DP: %d\n", ret);
|
||||
}
|
||||
|
||||
static void pmic_glink_altmode_enable_tbt(struct pmic_glink_altmode *altmode,
|
||||
struct pmic_glink_altmode_port *port)
|
||||
{
|
||||
struct usbc_sc8280x_tbt_data *tbt = &port->tbt_data;
|
||||
struct typec_thunderbolt_data tbt_data = {};
|
||||
u32 cable_speed;
|
||||
int ret;
|
||||
|
||||
/* Device Discover Mode VDO */
|
||||
tbt_data.device_mode = TBT_MODE;
|
||||
tbt_data.device_mode |= TBT_SET_ADAPTER(TBT_ADAPTER_TBT3);
|
||||
|
||||
/* Cable Discover Mode VDO */
|
||||
tbt_data.cable_mode = TBT_MODE;
|
||||
|
||||
if (tbt->usb_speed == 0) {
|
||||
cable_speed = TBT_CABLE_USB3_PASSIVE;
|
||||
} else if (tbt->usb_speed == 1) {
|
||||
cable_speed = TBT_CABLE_10_AND_20GBPS;
|
||||
} else {
|
||||
dev_err(altmode->dev,
|
||||
"Got illegal TBT3 cable speed value (%u), falling back to passive\n",
|
||||
tbt->usb_speed);
|
||||
cable_speed = TBT_CABLE_USB3_PASSIVE;
|
||||
}
|
||||
tbt_data.cable_mode |= TBT_SET_CABLE_SPEED(cable_speed);
|
||||
|
||||
if (tbt->cable_type) {
|
||||
tbt_data.cable_mode |= TBT_CABLE_ACTIVE_PASSIVE;
|
||||
tbt_data.cable_mode |= TBT_SET_CABLE_ROUNDED(tbt->rounded_cable);
|
||||
}
|
||||
|
||||
/* Enter Mode VDO */
|
||||
tbt_data.enter_vdo |= TBT_MODE;
|
||||
tbt_data.enter_vdo |= TBT_ENTER_MODE_CABLE_SPEED(cable_speed);
|
||||
|
||||
if (tbt->cable_type) {
|
||||
tbt_data.enter_vdo |= TBT_CABLE_ACTIVE_PASSIVE;
|
||||
tbt_data.enter_vdo |= TBT_SET_CABLE_ROUNDED(tbt->rounded_cable);
|
||||
}
|
||||
|
||||
port->state.alt = &port->tbt_alt;
|
||||
port->state.data = &tbt_data;
|
||||
port->state.mode = TYPEC_MODAL_STATE(port->mode);
|
||||
|
||||
ret = typec_mux_set(port->typec_mux, &port->state);
|
||||
if (ret)
|
||||
dev_err(altmode->dev, "failed to switch mux to USB: %d\n", ret);
|
||||
|
||||
port->retimer_state.alt = &port->tbt_alt;
|
||||
port->retimer_state.data = &tbt_data;
|
||||
port->retimer_state.mode = TYPEC_MODAL_STATE(port->mode);
|
||||
|
||||
ret = typec_retimer_set(port->typec_retimer, &port->retimer_state);
|
||||
if (ret)
|
||||
dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret);
|
||||
}
|
||||
|
||||
static void pmic_glink_altmode_enable_usb4(struct pmic_glink_altmode *altmode,
|
||||
struct pmic_glink_altmode_port *port)
|
||||
{
|
||||
struct usbc_sc8280x_tbt_data *tbt = &port->tbt_data;
|
||||
struct enter_usb_data data = {};
|
||||
int ret;
|
||||
|
||||
data.eudo = FIELD_PREP(EUDO_USB_MODE_MASK, EUDO_USB_MODE_USB4);
|
||||
|
||||
if (tbt->usb_speed == 0) {
|
||||
data.eudo |= FIELD_PREP(EUDO_CABLE_SPEED_MASK, EUDO_CABLE_SPEED_USB4_GEN2);
|
||||
} else if (tbt->usb_speed == 1) {
|
||||
data.eudo |= FIELD_PREP(EUDO_CABLE_SPEED_MASK, EUDO_CABLE_SPEED_USB4_GEN3);
|
||||
} else {
|
||||
pr_err("Got illegal USB4 cable speed value (%u), falling back to G2\n",
|
||||
tbt->usb_speed);
|
||||
data.eudo |= FIELD_PREP(EUDO_CABLE_SPEED_MASK, EUDO_CABLE_SPEED_USB4_GEN2);
|
||||
}
|
||||
|
||||
data.eudo |= FIELD_PREP(EUDO_CABLE_TYPE_MASK, tbt->cable_type);
|
||||
|
||||
port->state.alt = NULL;
|
||||
port->state.data = &data;
|
||||
port->state.mode = TYPEC_MODE_USB4;
|
||||
|
||||
ret = typec_mux_set(port->typec_mux, &port->state);
|
||||
if (ret)
|
||||
dev_err(altmode->dev, "failed to switch mux to USB: %d\n", ret);
|
||||
|
||||
port->retimer_state.alt = NULL;
|
||||
port->retimer_state.data = &data;
|
||||
port->retimer_state.mode = TYPEC_MODE_USB4;
|
||||
|
||||
ret = typec_retimer_set(port->typec_retimer, &port->retimer_state);
|
||||
if (ret)
|
||||
dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret);
|
||||
}
|
||||
|
||||
static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode,
|
||||
struct pmic_glink_altmode_port *port)
|
||||
{
|
||||
|
|
@ -222,15 +350,15 @@ static void pmic_glink_altmode_worker(struct work_struct *work)
|
|||
|
||||
typec_switch_set(alt_port->typec_switch, alt_port->orientation);
|
||||
|
||||
if (alt_port->svid == USB_TYPEC_DP_SID) {
|
||||
if (alt_port->mode == 0xff) {
|
||||
pmic_glink_altmode_safe(altmode, alt_port);
|
||||
} else {
|
||||
pmic_glink_altmode_enable_dp(altmode, alt_port,
|
||||
alt_port->mode,
|
||||
alt_port->hpd_state,
|
||||
alt_port->hpd_irq);
|
||||
}
|
||||
if (alt_port->mux_ctrl == MUX_CTRL_STATE_NO_CONN) {
|
||||
pmic_glink_altmode_safe(altmode, alt_port);
|
||||
} else if (alt_port->svid == USB_TYPEC_TBT_SID) {
|
||||
pmic_glink_altmode_enable_tbt(altmode, alt_port);
|
||||
} else if (alt_port->svid == USB_TYPEC_DP_SID) {
|
||||
pmic_glink_altmode_enable_dp(altmode, alt_port,
|
||||
alt_port->mode,
|
||||
alt_port->hpd_state,
|
||||
alt_port->hpd_irq);
|
||||
|
||||
if (alt_port->hpd_state)
|
||||
conn_status = connector_status_connected;
|
||||
|
|
@ -238,6 +366,8 @@ static void pmic_glink_altmode_worker(struct work_struct *work)
|
|||
conn_status = connector_status_disconnected;
|
||||
|
||||
drm_aux_hpd_bridge_notify(&alt_port->bridge->dev, conn_status);
|
||||
} else if (alt_port->mux_ctrl == MUX_CTRL_STATE_TUNNELING) {
|
||||
pmic_glink_altmode_enable_usb4(altmode, alt_port);
|
||||
} else {
|
||||
pmic_glink_altmode_enable_usb(altmode, alt_port);
|
||||
}
|
||||
|
|
@ -314,11 +444,10 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod
|
|||
u16 svid, const void *data, size_t len)
|
||||
{
|
||||
struct pmic_glink_altmode_port *alt_port;
|
||||
const struct usbc_sc8280x_tbt_data *tbt;
|
||||
const struct usbc_sc8280x_dp_data *dp;
|
||||
const struct usbc_notify *notify;
|
||||
u8 orientation;
|
||||
u8 hpd_state;
|
||||
u8 hpd_irq;
|
||||
u8 mode;
|
||||
u8 port;
|
||||
|
||||
if (len != sizeof(*notify)) {
|
||||
|
|
@ -329,11 +458,8 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod
|
|||
|
||||
notify = data;
|
||||
|
||||
port = notify->payload[0];
|
||||
orientation = notify->payload[1];
|
||||
mode = FIELD_GET(SC8280XP_DPAM_MASK, notify->payload[8]) - DPAM_HPD_A;
|
||||
hpd_state = FIELD_GET(SC8280XP_HPD_STATE_MASK, notify->payload[8]);
|
||||
hpd_irq = FIELD_GET(SC8280XP_HPD_IRQ_MASK, notify->payload[8]);
|
||||
port = notify->port_idx;
|
||||
orientation = notify->orientation;
|
||||
|
||||
if (port >= ARRAY_SIZE(altmode->ports) || !altmode->ports[port].altmode) {
|
||||
dev_dbg(altmode->dev, "notification on undefined port %d\n", port);
|
||||
|
|
@ -343,9 +469,21 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod
|
|||
alt_port = &altmode->ports[port];
|
||||
alt_port->orientation = pmic_glink_altmode_orientation(orientation);
|
||||
alt_port->svid = svid;
|
||||
alt_port->mode = mode;
|
||||
alt_port->hpd_state = hpd_state;
|
||||
alt_port->hpd_irq = hpd_irq;
|
||||
alt_port->mux_ctrl = notify->mux_ctrl;
|
||||
|
||||
if (svid == USB_TYPEC_DP_SID) {
|
||||
dp = ¬ify->extended_data.dp;
|
||||
|
||||
alt_port->mode = dp->pin_assignment - DPAM_HPD_A;
|
||||
alt_port->hpd_state = dp->hpd_state;
|
||||
alt_port->hpd_irq = dp->hpd_irq;
|
||||
} else if (alt_port->mux_ctrl == MUX_CTRL_STATE_TUNNELING) {
|
||||
/* Valid for both USB4 and TBT3 */
|
||||
tbt = ¬ify->extended_data.tbt;
|
||||
|
||||
alt_port->tbt_data = *tbt;
|
||||
}
|
||||
|
||||
schedule_work(&alt_port->work);
|
||||
}
|
||||
|
||||
|
|
@ -471,6 +609,10 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev,
|
|||
alt_port->dp_alt.mode = USB_TYPEC_DP_MODE;
|
||||
alt_port->dp_alt.active = 1;
|
||||
|
||||
alt_port->tbt_alt.svid = USB_TYPEC_TBT_SID;
|
||||
alt_port->tbt_alt.mode = TYPEC_TBT_MODE;
|
||||
alt_port->tbt_alt.active = 1;
|
||||
|
||||
alt_port->typec_mux = fwnode_typec_mux_get(fwnode);
|
||||
if (IS_ERR(alt_port->typec_mux)) {
|
||||
fwnode_handle_put(fwnode);
|
||||
|
|
|
|||
|
|
@ -23,18 +23,60 @@
|
|||
*p_length |= ((u8)*p_src) << 8; \
|
||||
} while (0)
|
||||
|
||||
#define QMI_ENCDEC_ENCODE_N_BYTES(p_dst, p_src, size) \
|
||||
#define QMI_ENCDEC_ENCODE_U8(p_dst, p_src) \
|
||||
do { \
|
||||
memcpy(p_dst, p_src, size); \
|
||||
p_dst = (u8 *)p_dst + size; \
|
||||
p_src = (u8 *)p_src + size; \
|
||||
memcpy(p_dst, p_src, sizeof(u8)); \
|
||||
p_dst = (u8 *)p_dst + sizeof(u8); \
|
||||
p_src = (u8 *)p_src + sizeof(u8); \
|
||||
} while (0)
|
||||
|
||||
#define QMI_ENCDEC_DECODE_N_BYTES(p_dst, p_src, size) \
|
||||
#define QMI_ENCDEC_ENCODE_U16(p_dst, p_src) \
|
||||
do { \
|
||||
memcpy(p_dst, p_src, size); \
|
||||
p_dst = (u8 *)p_dst + size; \
|
||||
p_src = (u8 *)p_src + size; \
|
||||
*(__le16 *)p_dst = __cpu_to_le16(*(u16 *)p_src); \
|
||||
p_dst = (u8 *)p_dst + sizeof(u16); \
|
||||
p_src = (u8 *)p_src + sizeof(u16); \
|
||||
} while (0)
|
||||
|
||||
#define QMI_ENCDEC_ENCODE_U32(p_dst, p_src) \
|
||||
do { \
|
||||
*(__le32 *)p_dst = __cpu_to_le32(*(u32 *)p_src); \
|
||||
p_dst = (u8 *)p_dst + sizeof(u32); \
|
||||
p_src = (u8 *)p_src + sizeof(u32); \
|
||||
} while (0)
|
||||
|
||||
#define QMI_ENCDEC_ENCODE_U64(p_dst, p_src) \
|
||||
do { \
|
||||
*(__le64 *)p_dst = __cpu_to_le64(*(u64 *)p_src); \
|
||||
p_dst = (u8 *)p_dst + sizeof(u64); \
|
||||
p_src = (u8 *)p_src + sizeof(u64); \
|
||||
} while (0)
|
||||
|
||||
#define QMI_ENCDEC_DECODE_U8(p_dst, p_src) \
|
||||
do { \
|
||||
memcpy(p_dst, p_src, sizeof(u8)); \
|
||||
p_dst = (u8 *)p_dst + sizeof(u8); \
|
||||
p_src = (u8 *)p_src + sizeof(u8); \
|
||||
} while (0)
|
||||
|
||||
#define QMI_ENCDEC_DECODE_U16(p_dst, p_src) \
|
||||
do { \
|
||||
*(u16 *)p_dst = __le16_to_cpu(*(__le16 *)p_src); \
|
||||
p_dst = (u8 *)p_dst + sizeof(u16); \
|
||||
p_src = (u8 *)p_src + sizeof(u16); \
|
||||
} while (0)
|
||||
|
||||
#define QMI_ENCDEC_DECODE_U32(p_dst, p_src) \
|
||||
do { \
|
||||
*(u32 *)p_dst = __le32_to_cpu(*(__le32 *)p_src); \
|
||||
p_dst = (u8 *)p_dst + sizeof(u32); \
|
||||
p_src = (u8 *)p_src + sizeof(u32); \
|
||||
} while (0)
|
||||
|
||||
#define QMI_ENCDEC_DECODE_U64(p_dst, p_src) \
|
||||
do { \
|
||||
*(u64 *)p_dst = __le64_to_cpu(*(__le64 *)p_src); \
|
||||
p_dst = (u8 *)p_dst + sizeof(u64); \
|
||||
p_src = (u8 *)p_src + sizeof(u64); \
|
||||
} while (0)
|
||||
|
||||
#define UPDATE_ENCODE_VARIABLES(temp_si, buf_dst, \
|
||||
|
|
@ -161,7 +203,8 @@ static int qmi_calc_min_msg_len(const struct qmi_elem_info *ei_array,
|
|||
* of primary data type which include u8 - u64 or similar. This
|
||||
* function returns the number of bytes of encoded information.
|
||||
*
|
||||
* Return: The number of bytes of encoded information.
|
||||
* Return: The number of bytes of encoded information on success or negative
|
||||
* errno on error.
|
||||
*/
|
||||
static int qmi_encode_basic_elem(void *buf_dst, const void *buf_src,
|
||||
u32 elem_len, u32 elem_size)
|
||||
|
|
@ -169,7 +212,24 @@ static int qmi_encode_basic_elem(void *buf_dst, const void *buf_src,
|
|||
u32 i, rc = 0;
|
||||
|
||||
for (i = 0; i < elem_len; i++) {
|
||||
QMI_ENCDEC_ENCODE_N_BYTES(buf_dst, buf_src, elem_size);
|
||||
switch (elem_size) {
|
||||
case sizeof(u8):
|
||||
QMI_ENCDEC_ENCODE_U8(buf_dst, buf_src);
|
||||
break;
|
||||
case sizeof(u16):
|
||||
QMI_ENCDEC_ENCODE_U16(buf_dst, buf_src);
|
||||
break;
|
||||
case sizeof(u32):
|
||||
QMI_ENCDEC_ENCODE_U32(buf_dst, buf_src);
|
||||
break;
|
||||
case sizeof(u64):
|
||||
QMI_ENCDEC_ENCODE_U64(buf_dst, buf_src);
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: Unrecognized element size\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc += elem_size;
|
||||
}
|
||||
|
||||
|
|
@ -267,11 +327,15 @@ static int qmi_encode_string_elem(const struct qmi_elem_info *ei_array,
|
|||
}
|
||||
rc = qmi_encode_basic_elem(buf_dst, &string_len,
|
||||
1, string_len_sz);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
encoded_bytes += rc;
|
||||
}
|
||||
|
||||
rc = qmi_encode_basic_elem(buf_dst + encoded_bytes, buf_src,
|
||||
string_len, temp_ei->elem_size);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
encoded_bytes += rc;
|
||||
|
||||
return encoded_bytes;
|
||||
|
|
@ -333,6 +397,8 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf,
|
|||
case QMI_OPT_FLAG:
|
||||
rc = qmi_encode_basic_elem(&opt_flag_value, buf_src,
|
||||
1, sizeof(u8));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (opt_flag_value)
|
||||
temp_ei = temp_ei + 1;
|
||||
else
|
||||
|
|
@ -340,6 +406,7 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf,
|
|||
break;
|
||||
|
||||
case QMI_DATA_LEN:
|
||||
memcpy(&data_len_value, buf_src, sizeof(u32));
|
||||
data_len_sz = temp_ei->elem_size == sizeof(u8) ?
|
||||
sizeof(u8) : sizeof(u16);
|
||||
/* Check to avoid out of range buffer access */
|
||||
|
|
@ -350,15 +417,17 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf,
|
|||
return -ETOOSMALL;
|
||||
}
|
||||
if (data_len_sz == sizeof(u8)) {
|
||||
val8 = *(u8 *)buf_src;
|
||||
data_len_value = (u32)val8;
|
||||
val8 = data_len_value;
|
||||
rc = qmi_encode_basic_elem(buf_dst, &val8,
|
||||
1, data_len_sz);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
} else {
|
||||
val16 = *(u16 *)buf_src;
|
||||
data_len_value = (u32)le16_to_cpu(val16);
|
||||
val16 = data_len_value;
|
||||
rc = qmi_encode_basic_elem(buf_dst, &val16,
|
||||
1, data_len_sz);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
|
||||
encoded_bytes, tlv_len,
|
||||
|
|
@ -386,6 +455,8 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf,
|
|||
rc = qmi_encode_basic_elem(buf_dst, buf_src,
|
||||
data_len_value,
|
||||
temp_ei->elem_size);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
|
||||
encoded_bytes, tlv_len,
|
||||
encode_tlv, rc);
|
||||
|
|
@ -444,7 +515,8 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf,
|
|||
* of primary data type which include u8 - u64 or similar. This
|
||||
* function returns the number of bytes of decoded information.
|
||||
*
|
||||
* Return: The total size of the decoded data elements, in bytes.
|
||||
* Return: The total size of the decoded data elements, in bytes, on success or
|
||||
* negative errno on error.
|
||||
*/
|
||||
static int qmi_decode_basic_elem(void *buf_dst, const void *buf_src,
|
||||
u32 elem_len, u32 elem_size)
|
||||
|
|
@ -452,7 +524,24 @@ static int qmi_decode_basic_elem(void *buf_dst, const void *buf_src,
|
|||
u32 i, rc = 0;
|
||||
|
||||
for (i = 0; i < elem_len; i++) {
|
||||
QMI_ENCDEC_DECODE_N_BYTES(buf_dst, buf_src, elem_size);
|
||||
switch (elem_size) {
|
||||
case sizeof(u8):
|
||||
QMI_ENCDEC_DECODE_U8(buf_dst, buf_src);
|
||||
break;
|
||||
case sizeof(u16):
|
||||
QMI_ENCDEC_DECODE_U16(buf_dst, buf_src);
|
||||
break;
|
||||
case sizeof(u32):
|
||||
QMI_ENCDEC_DECODE_U32(buf_dst, buf_src);
|
||||
break;
|
||||
case sizeof(u64):
|
||||
QMI_ENCDEC_DECODE_U64(buf_dst, buf_src);
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: Unrecognized element size\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc += elem_size;
|
||||
}
|
||||
|
||||
|
|
@ -544,10 +633,14 @@ static int qmi_decode_string_elem(const struct qmi_elem_info *ei_array,
|
|||
if (string_len_sz == sizeof(u8)) {
|
||||
rc = qmi_decode_basic_elem(&val8, buf_src,
|
||||
1, string_len_sz);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
string_len = (u32)val8;
|
||||
} else {
|
||||
rc = qmi_decode_basic_elem(&val16, buf_src,
|
||||
1, string_len_sz);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
string_len = (u32)val16;
|
||||
}
|
||||
decoded_bytes += rc;
|
||||
|
|
@ -565,6 +658,8 @@ static int qmi_decode_string_elem(const struct qmi_elem_info *ei_array,
|
|||
|
||||
rc = qmi_decode_basic_elem(buf_dst, buf_src + decoded_bytes,
|
||||
string_len, temp_ei->elem_size);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
*((char *)buf_dst + string_len) = '\0';
|
||||
decoded_bytes += rc;
|
||||
|
||||
|
|
@ -625,7 +720,6 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct,
|
|||
int rc;
|
||||
u8 val8;
|
||||
u16 val16;
|
||||
u32 val32;
|
||||
|
||||
while (decoded_bytes < in_buf_len) {
|
||||
if (dec_level >= 2 && temp_ei->data_type == QMI_EOTI)
|
||||
|
|
@ -667,14 +761,17 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct,
|
|||
if (data_len_sz == sizeof(u8)) {
|
||||
rc = qmi_decode_basic_elem(&val8, buf_src,
|
||||
1, data_len_sz);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
data_len_value = (u32)val8;
|
||||
} else {
|
||||
rc = qmi_decode_basic_elem(&val16, buf_src,
|
||||
1, data_len_sz);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
data_len_value = (u32)val16;
|
||||
}
|
||||
val32 = cpu_to_le32(data_len_value);
|
||||
memcpy(buf_dst, &val32, sizeof(u32));
|
||||
memcpy(buf_dst, &data_len_value, sizeof(u32));
|
||||
temp_ei = temp_ei + 1;
|
||||
buf_dst = out_c_struct + temp_ei->offset;
|
||||
tlv_len -= data_len_sz;
|
||||
|
|
@ -701,6 +798,8 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct,
|
|||
rc = qmi_decode_basic_elem(buf_dst, buf_src,
|
||||
data_len_value,
|
||||
temp_ei->elem_size);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -1219,7 +1219,9 @@ static int qcom_smem_probe(struct platform_device *pdev)
|
|||
smem->item_count = qcom_smem_get_item_count(smem);
|
||||
break;
|
||||
case SMEM_GLOBAL_HEAP_VERSION:
|
||||
qcom_smem_map_global(smem, size);
|
||||
ret = qcom_smem_map_global(smem, size);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
smem->item_count = SMEM_ITEM_COUNT;
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ config ARCH_RZN1
|
|||
select PM
|
||||
select PM_GENERIC_DOMAINS
|
||||
select ARM_AMBA
|
||||
select RZN1_IRQMUX if GPIO_DWAPB
|
||||
|
||||
if ARM && ARCH_RENESAS
|
||||
|
||||
|
|
@ -430,6 +431,7 @@ config ARCH_R9A09G077
|
|||
config ARCH_R9A09G087
|
||||
bool "ARM64 Platform support for R9A09G087 (RZ/N2H)"
|
||||
default y if ARCH_RENESAS
|
||||
select RENESAS_RZT2H_ICU
|
||||
help
|
||||
This enables support for the Renesas RZ/N2H SoC variants.
|
||||
|
||||
|
|
@ -461,6 +463,9 @@ config PWC_RZV2M
|
|||
config RST_RCAR
|
||||
bool "Reset Controller support for R-Car" if COMPILE_TEST
|
||||
|
||||
config RZN1_IRQMUX
|
||||
bool "Renesas RZ/N1 GPIO IRQ multiplexer support" if COMPILE_TEST
|
||||
|
||||
config SYSC_RZ
|
||||
bool "System controller for RZ SoCs" if COMPILE_TEST
|
||||
select MFD_SYSCON
|
||||
|
|
|
|||
|
|
@ -14,4 +14,5 @@ obj-$(CONFIG_SYS_R9A09G057) += r9a09g057-sys.o
|
|||
# Family
|
||||
obj-$(CONFIG_PWC_RZV2M) += pwc-rzv2m.o
|
||||
obj-$(CONFIG_RST_RCAR) += rcar-rst.o
|
||||
obj-$(CONFIG_RZN1_IRQMUX) += rzn1_irqmux.o
|
||||
obj-$(CONFIG_SYSC_RZ) += rz-sysc.o
|
||||
|
|
|
|||
127
drivers/soc/renesas/rzn1_irqmux.c
Normal file
127
drivers/soc/renesas/rzn1_irqmux.c
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* RZ/N1 GPIO Interrupt Multiplexer
|
||||
*
|
||||
* Copyright 2025 Schneider Electric
|
||||
* Author: Herve Codina <herve.codina@bootlin.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
/*
|
||||
* Up to 8 output lines are connected to GIC SPI interrupt controller
|
||||
* starting at IRQ 103.
|
||||
*/
|
||||
#define RZN1_IRQMUX_GIC_SPI_BASE 103
|
||||
#define RZN1_IRQMUX_NUM_OUTPUTS 8
|
||||
|
||||
static int rzn1_irqmux_parent_args_to_line_index(struct device *dev,
|
||||
const struct of_phandle_args *parent_args)
|
||||
{
|
||||
/*
|
||||
* The parent interrupt should be one of the GIC controller.
|
||||
* Three arguments must be provided.
|
||||
* - args[0]: GIC_SPI
|
||||
* - args[1]: The GIC interrupt number
|
||||
* - args[2]: The interrupt flags
|
||||
*
|
||||
* We retrieve the line index based on the GIC interrupt number
|
||||
* provided.
|
||||
*/
|
||||
|
||||
if (parent_args->args_count != 3 || parent_args->args[0] != GIC_SPI) {
|
||||
dev_err(dev, "Invalid interrupt-map item\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (parent_args->args[1] < RZN1_IRQMUX_GIC_SPI_BASE ||
|
||||
parent_args->args[1] >= RZN1_IRQMUX_GIC_SPI_BASE + RZN1_IRQMUX_NUM_OUTPUTS) {
|
||||
dev_err(dev, "Invalid GIC interrupt %u\n", parent_args->args[1]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return parent_args->args[1] - RZN1_IRQMUX_GIC_SPI_BASE;
|
||||
}
|
||||
|
||||
static int rzn1_irqmux_probe(struct platform_device *pdev)
|
||||
{
|
||||
DECLARE_BITMAP(index_done, RZN1_IRQMUX_NUM_OUTPUTS) = {};
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct of_imap_parser imap_parser;
|
||||
struct of_imap_item imap_item;
|
||||
u32 __iomem *regs;
|
||||
int index;
|
||||
int ret;
|
||||
u32 tmp;
|
||||
|
||||
regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
/* We support only #interrupt-cells = <1> and #address-cells = <0> */
|
||||
ret = of_property_read_u32(np, "#interrupt-cells", &tmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (tmp != 1)
|
||||
return -EINVAL;
|
||||
|
||||
ret = of_property_read_u32(np, "#address-cells", &tmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (tmp != 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = of_imap_parser_init(&imap_parser, np, &imap_item);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for_each_of_imap_item(&imap_parser, &imap_item) {
|
||||
index = rzn1_irqmux_parent_args_to_line_index(dev, &imap_item.parent_args);
|
||||
if (index < 0) {
|
||||
of_node_put(imap_item.parent_args.np);
|
||||
return index;
|
||||
}
|
||||
|
||||
if (test_and_set_bit(index, index_done)) {
|
||||
of_node_put(imap_item.parent_args.np);
|
||||
dev_err(dev, "Mux output line %d already defined in interrupt-map\n",
|
||||
index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The child #address-cells is 0 (already checked). The first
|
||||
* value in imap item is the src hwirq.
|
||||
*/
|
||||
writel(imap_item.child_imap[0], regs + index);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id rzn1_irqmux_of_match[] = {
|
||||
{ .compatible = "renesas,rzn1-gpioirqmux", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rzn1_irqmux_of_match);
|
||||
|
||||
static struct platform_driver rzn1_irqmux_driver = {
|
||||
.probe = rzn1_irqmux_probe,
|
||||
.driver = {
|
||||
.name = "rzn1_irqmux",
|
||||
.of_match_table = rzn1_irqmux_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(rzn1_irqmux_driver);
|
||||
|
||||
MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
|
||||
MODULE_DESCRIPTION("Renesas RZ/N1 GPIO IRQ Multiplexer Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -146,7 +146,7 @@ static const struct rockchip_grf_info rk3576_sysgrf __initconst = {
|
|||
.num_values = ARRAY_SIZE(rk3576_defaults_sys_grf),
|
||||
};
|
||||
|
||||
#define RK3576_IOCGRF_MISC_CON 0x04F0
|
||||
#define RK3576_IOCGRF_MISC_CON 0x40F0
|
||||
|
||||
static const struct rockchip_grf_value rk3576_defaults_ioc_grf[] __initconst = {
|
||||
{ "jtag switching", RK3576_IOCGRF_MISC_CON, FIELD_PREP_WM16_CONST(BIT(1), 0) },
|
||||
|
|
@ -217,34 +217,33 @@ static int __init rockchip_grf_init(void)
|
|||
struct regmap *grf;
|
||||
int ret, i;
|
||||
|
||||
np = of_find_matching_node_and_match(NULL, rockchip_grf_dt_match,
|
||||
&match);
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
if (!match || !match->data) {
|
||||
pr_err("%s: missing grf data\n", __func__);
|
||||
of_node_put(np);
|
||||
return -EINVAL;
|
||||
}
|
||||
for_each_matching_node_and_match(np, rockchip_grf_dt_match, &match) {
|
||||
if (!of_device_is_available(np))
|
||||
continue;
|
||||
if (!match || !match->data) {
|
||||
pr_err("%s: missing grf data\n", __func__);
|
||||
of_node_put(np);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
grf_info = match->data;
|
||||
grf_info = match->data;
|
||||
|
||||
grf = syscon_node_to_regmap(np);
|
||||
of_node_put(np);
|
||||
if (IS_ERR(grf)) {
|
||||
pr_err("%s: could not get grf syscon\n", __func__);
|
||||
return PTR_ERR(grf);
|
||||
}
|
||||
grf = syscon_node_to_regmap(np);
|
||||
if (IS_ERR(grf)) {
|
||||
pr_err("%s: could not get grf syscon\n", __func__);
|
||||
return PTR_ERR(grf);
|
||||
}
|
||||
|
||||
for (i = 0; i < grf_info->num_values; i++) {
|
||||
const struct rockchip_grf_value *val = &grf_info->values[i];
|
||||
for (i = 0; i < grf_info->num_values; i++) {
|
||||
const struct rockchip_grf_value *val = &grf_info->values[i];
|
||||
|
||||
pr_debug("%s: adjusting %s in %#6x to %#10x\n", __func__,
|
||||
val->desc, val->reg, val->val);
|
||||
ret = regmap_write(grf, val->reg, val->val);
|
||||
if (ret < 0)
|
||||
pr_err("%s: write to %#6x failed with %d\n",
|
||||
__func__, val->reg, ret);
|
||||
pr_debug("%s: adjusting %s in %#6x to %#10x\n", __func__,
|
||||
val->desc, val->reg, val->val);
|
||||
ret = regmap_write(grf, val->reg, val->val);
|
||||
if (ret < 0)
|
||||
pr_err("%s: write to %#6x failed with %d\n",
|
||||
__func__, val->reg, ret);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@
|
|||
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/device/devres.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
|
@ -27,9 +29,11 @@
|
|||
#include "exynos-asv.h"
|
||||
|
||||
struct exynos_chipid_variant {
|
||||
unsigned int rev_reg; /* revision register offset */
|
||||
unsigned int main_rev_reg; /* main revision register offset */
|
||||
unsigned int sub_rev_reg; /* sub revision register offset */
|
||||
unsigned int main_rev_shift; /* main revision offset in rev_reg */
|
||||
unsigned int sub_rev_shift; /* sub revision offset in rev_reg */
|
||||
bool efuse;
|
||||
};
|
||||
|
||||
struct exynos_chipid_info {
|
||||
|
|
@ -68,9 +72,11 @@ static const struct exynos_soc_id {
|
|||
{ "EXYNOS990", 0xE9830000 },
|
||||
{ "EXYNOSAUTOV9", 0xAAA80000 },
|
||||
{ "EXYNOSAUTOV920", 0x0A920000 },
|
||||
/* Compatible with: google,gs101-otp */
|
||||
{ "GS101", 0x9845000 },
|
||||
};
|
||||
|
||||
static const char *product_id_to_soc_id(unsigned int product_id)
|
||||
static const char *exynos_product_id_to_name(unsigned int product_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
|
@ -80,8 +86,8 @@ static const char *product_id_to_soc_id(unsigned int product_id)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int exynos_chipid_get_chipid_info(struct regmap *regmap,
|
||||
const struct exynos_chipid_variant *data,
|
||||
static int exynos_chipid_get_chipid_info(struct device *dev,
|
||||
struct regmap *regmap, const struct exynos_chipid_variant *data,
|
||||
struct exynos_chipid_info *soc_info)
|
||||
{
|
||||
int ret;
|
||||
|
|
@ -89,21 +95,61 @@ static int exynos_chipid_get_chipid_info(struct regmap *regmap,
|
|||
|
||||
ret = regmap_read(regmap, EXYNOS_CHIPID_REG_PRO_ID, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return dev_err_probe(dev, ret, "failed to read Product ID\n");
|
||||
soc_info->product_id = val & EXYNOS_MASK;
|
||||
|
||||
if (data->rev_reg != EXYNOS_CHIPID_REG_PRO_ID) {
|
||||
ret = regmap_read(regmap, data->rev_reg, &val);
|
||||
if (data->sub_rev_reg == EXYNOS_CHIPID_REG_PRO_ID) {
|
||||
/* exynos4210 case */
|
||||
main_rev = (val >> data->main_rev_shift) & EXYNOS_REV_PART_MASK;
|
||||
sub_rev = (val >> data->sub_rev_shift) & EXYNOS_REV_PART_MASK;
|
||||
} else {
|
||||
unsigned int val2;
|
||||
|
||||
ret = regmap_read(regmap, data->sub_rev_reg, &val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to read revision\n");
|
||||
|
||||
if (data->main_rev_reg == EXYNOS_CHIPID_REG_PRO_ID)
|
||||
/* gs101 case */
|
||||
main_rev = (val >> data->main_rev_shift) & EXYNOS_REV_PART_MASK;
|
||||
else
|
||||
/* exynos850 case */
|
||||
main_rev = (val2 >> data->main_rev_shift) & EXYNOS_REV_PART_MASK;
|
||||
|
||||
sub_rev = (val2 >> data->sub_rev_shift) & EXYNOS_REV_PART_MASK;
|
||||
}
|
||||
main_rev = (val >> data->main_rev_shift) & EXYNOS_REV_PART_MASK;
|
||||
sub_rev = (val >> data->sub_rev_shift) & EXYNOS_REV_PART_MASK;
|
||||
|
||||
soc_info->revision = (main_rev << EXYNOS_REV_PART_SHIFT) | sub_rev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct regmap *exynos_chipid_get_efuse_regmap(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
|
||||
base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(base))
|
||||
return ERR_CAST(base);
|
||||
|
||||
const struct regmap_config reg_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.use_relaxed_mmio = true,
|
||||
.max_register = (resource_size(res) - reg_config.reg_stride),
|
||||
};
|
||||
|
||||
return devm_regmap_init_mmio_clk(&pdev->dev, "pclk", base, ®_config);
|
||||
}
|
||||
|
||||
static void exynos_chipid_unregister_soc(void *data)
|
||||
{
|
||||
soc_device_unregister(data);
|
||||
}
|
||||
|
||||
static int exynos_chipid_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct exynos_chipid_variant *drv_data;
|
||||
|
|
@ -117,13 +163,19 @@ static int exynos_chipid_probe(struct platform_device *pdev)
|
|||
|
||||
drv_data = of_device_get_match_data(dev);
|
||||
if (!drv_data)
|
||||
return -EINVAL;
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"failed to get match data\n");
|
||||
|
||||
if (drv_data->efuse)
|
||||
regmap = exynos_chipid_get_efuse_regmap(pdev);
|
||||
else
|
||||
regmap = device_node_to_regmap(dev->of_node);
|
||||
|
||||
regmap = device_node_to_regmap(dev->of_node);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
return dev_err_probe(dev, PTR_ERR(regmap),
|
||||
"failed to get regmap\n");
|
||||
|
||||
ret = exynos_chipid_get_chipid_info(regmap, drv_data, &soc_info);
|
||||
ret = exynos_chipid_get_chipid_info(dev, regmap, drv_data, &soc_info);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -141,55 +193,55 @@ static int exynos_chipid_probe(struct platform_device *pdev)
|
|||
soc_info.revision);
|
||||
if (!soc_dev_attr->revision)
|
||||
return -ENOMEM;
|
||||
soc_dev_attr->soc_id = product_id_to_soc_id(soc_info.product_id);
|
||||
if (!soc_dev_attr->soc_id) {
|
||||
pr_err("Unknown SoC\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
soc_dev_attr->soc_id = exynos_product_id_to_name(soc_info.product_id);
|
||||
if (!soc_dev_attr->soc_id)
|
||||
return dev_err_probe(dev, -ENODEV, "Unknown SoC\n");
|
||||
|
||||
/* please note that the actual registration will be deferred */
|
||||
soc_dev = soc_device_register(soc_dev_attr);
|
||||
if (IS_ERR(soc_dev))
|
||||
return PTR_ERR(soc_dev);
|
||||
return dev_err_probe(dev, PTR_ERR(soc_dev),
|
||||
"failed to register to the soc interface\n");
|
||||
|
||||
ret = devm_add_action_or_reset(dev, exynos_chipid_unregister_soc,
|
||||
soc_dev);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to add devm action\n");
|
||||
|
||||
ret = exynos_asv_init(dev, regmap);
|
||||
if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, soc_dev);
|
||||
|
||||
dev_info(dev, "Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n",
|
||||
soc_dev_attr->soc_id, soc_info.product_id, soc_info.revision);
|
||||
dev_dbg(dev, "Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n",
|
||||
soc_dev_attr->soc_id, soc_info.product_id, soc_info.revision);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
soc_device_unregister(soc_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void exynos_chipid_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct soc_device *soc_dev = platform_get_drvdata(pdev);
|
||||
|
||||
soc_device_unregister(soc_dev);
|
||||
}
|
||||
|
||||
static const struct exynos_chipid_variant exynos4210_chipid_drv_data = {
|
||||
.rev_reg = 0x0,
|
||||
.main_rev_shift = 4,
|
||||
.sub_rev_shift = 0,
|
||||
};
|
||||
|
||||
static const struct exynos_chipid_variant exynos850_chipid_drv_data = {
|
||||
.rev_reg = 0x10,
|
||||
.main_rev_reg = 0x10,
|
||||
.sub_rev_reg = 0x10,
|
||||
.main_rev_shift = 20,
|
||||
.sub_rev_shift = 16,
|
||||
};
|
||||
|
||||
static const struct exynos_chipid_variant gs101_chipid_drv_data = {
|
||||
.sub_rev_reg = 0x10,
|
||||
.sub_rev_shift = 16,
|
||||
.efuse = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id exynos_chipid_of_device_ids[] = {
|
||||
{
|
||||
.compatible = "google,gs101-otp",
|
||||
.data = &gs101_chipid_drv_data,
|
||||
}, {
|
||||
.compatible = "samsung,exynos4210-chipid",
|
||||
.data = &exynos4210_chipid_drv_data,
|
||||
}, {
|
||||
|
|
@ -206,7 +258,6 @@ static struct platform_driver exynos_chipid_driver = {
|
|||
.of_match_table = exynos_chipid_of_device_ids,
|
||||
},
|
||||
.probe = exynos_chipid_probe,
|
||||
.remove = exynos_chipid_remove,
|
||||
};
|
||||
module_platform_driver(exynos_chipid_driver);
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/iopoll.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irq_work.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_clk.h>
|
||||
|
|
@ -201,18 +202,20 @@
|
|||
#define TEGRA_SMC_PMC_WRITE 0xbb
|
||||
|
||||
struct pmc_clk {
|
||||
struct clk_hw hw;
|
||||
unsigned long offs;
|
||||
u32 mux_shift;
|
||||
u32 force_en_shift;
|
||||
struct clk_hw hw;
|
||||
struct tegra_pmc *pmc;
|
||||
unsigned long offs;
|
||||
u32 mux_shift;
|
||||
u32 force_en_shift;
|
||||
};
|
||||
|
||||
#define to_pmc_clk(_hw) container_of(_hw, struct pmc_clk, hw)
|
||||
|
||||
struct pmc_clk_gate {
|
||||
struct clk_hw hw;
|
||||
unsigned long offs;
|
||||
u32 shift;
|
||||
struct clk_hw hw;
|
||||
struct tegra_pmc *pmc;
|
||||
unsigned long offs;
|
||||
u32 shift;
|
||||
};
|
||||
|
||||
#define to_pmc_clk_gate(_hw) container_of(_hw, struct pmc_clk_gate, hw)
|
||||
|
|
@ -265,6 +268,17 @@ static const struct pmc_clk_init_data tegra_pmc_clks_data[] = {
|
|||
},
|
||||
};
|
||||
|
||||
struct tegra_pmc_core_pd {
|
||||
struct generic_pm_domain genpd;
|
||||
struct tegra_pmc *pmc;
|
||||
};
|
||||
|
||||
static inline struct tegra_pmc_core_pd *
|
||||
to_core_pd(struct generic_pm_domain *genpd)
|
||||
{
|
||||
return container_of(genpd, struct tegra_pmc_core_pd, genpd);
|
||||
}
|
||||
|
||||
struct tegra_powergate {
|
||||
struct generic_pm_domain genpd;
|
||||
struct tegra_pmc *pmc;
|
||||
|
|
@ -467,7 +481,13 @@ struct tegra_pmc {
|
|||
unsigned long *wake_type_dual_edge_map;
|
||||
unsigned long *wake_sw_status_map;
|
||||
unsigned long *wake_cntrl_level_map;
|
||||
|
||||
struct notifier_block reboot_notifier;
|
||||
struct syscore syscore;
|
||||
|
||||
/* Pending wake IRQ processing */
|
||||
struct irq_work wake_work;
|
||||
u32 *wake_status;
|
||||
};
|
||||
|
||||
static struct tegra_pmc *pmc = &(struct tegra_pmc) {
|
||||
|
|
@ -541,12 +561,7 @@ static void tegra_pmc_scratch_writel(struct tegra_pmc *pmc, u32 value,
|
|||
writel(value, pmc->scratch + offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO Figure out a way to call this with the struct tegra_pmc * passed in.
|
||||
* This currently doesn't work because readx_poll_timeout() can only operate
|
||||
* on functions that take a single argument.
|
||||
*/
|
||||
static inline bool tegra_powergate_state(int id)
|
||||
static inline bool tegra_powergate_state(struct tegra_pmc *pmc, int id)
|
||||
{
|
||||
if (id == TEGRA_POWERGATE_3D && pmc->soc->has_gpu_clamps)
|
||||
return (tegra_pmc_readl(pmc, GPU_RG_CNTRL) & 0x1) == 0;
|
||||
|
|
@ -598,8 +613,9 @@ static int tegra20_powergate_set(struct tegra_pmc *pmc, unsigned int id,
|
|||
tegra_pmc_writel(pmc, PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
|
||||
|
||||
/* wait for PMC to execute the command */
|
||||
ret = readx_poll_timeout(tegra_powergate_state, id, status,
|
||||
status == new_state, 1, 10);
|
||||
ret = read_poll_timeout(tegra_powergate_state, status,
|
||||
status == new_state, 1, 10, false,
|
||||
pmc, id);
|
||||
} while (ret == -ETIMEDOUT && retries--);
|
||||
|
||||
return ret;
|
||||
|
|
@ -631,8 +647,9 @@ static int tegra114_powergate_set(struct tegra_pmc *pmc, unsigned int id,
|
|||
return err;
|
||||
|
||||
/* wait for PMC to execute the command */
|
||||
err = readx_poll_timeout(tegra_powergate_state, id, status,
|
||||
status == new_state, 10, 100000);
|
||||
err = read_poll_timeout(tegra_powergate_state, status,
|
||||
status == new_state, 10, 100000, false,
|
||||
pmc, id);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
@ -655,7 +672,7 @@ static int tegra_powergate_set(struct tegra_pmc *pmc, unsigned int id,
|
|||
|
||||
mutex_lock(&pmc->powergates_lock);
|
||||
|
||||
if (tegra_powergate_state(id) == new_state) {
|
||||
if (tegra_powergate_state(pmc, id) == new_state) {
|
||||
mutex_unlock(&pmc->powergates_lock);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -940,29 +957,121 @@ static int tegra_genpd_power_off(struct generic_pm_domain *domain)
|
|||
return err;
|
||||
}
|
||||
|
||||
static void tegra_pmc_put_device(void *data)
|
||||
{
|
||||
struct tegra_pmc *pmc = data;
|
||||
|
||||
put_device(pmc->dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id tegra_pmc_match[];
|
||||
|
||||
static struct tegra_pmc *tegra_pmc_get(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct device_node *np;
|
||||
struct tegra_pmc *pmc;
|
||||
|
||||
np = of_parse_phandle(dev->of_node, "nvidia,pmc", 0);
|
||||
if (!np) {
|
||||
struct device_node *parent = of_node_get(dev->of_node);
|
||||
|
||||
while ((parent = of_get_next_parent(parent)) != NULL) {
|
||||
np = of_find_matching_node(parent, tegra_pmc_match);
|
||||
if (np)
|
||||
break;
|
||||
}
|
||||
|
||||
of_node_put(parent);
|
||||
|
||||
if (!np)
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
pdev = of_find_device_by_node(np);
|
||||
of_node_put(np);
|
||||
|
||||
if (!pdev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
pmc = platform_get_drvdata(pdev);
|
||||
if (!pmc) {
|
||||
put_device(&pdev->dev);
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
return pmc;
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_powergate_power_on() - power on partition
|
||||
* tegra_pmc_get() - find the PMC for a given device
|
||||
* @dev: device for which to find the PMC
|
||||
*
|
||||
* Returns a pointer to the PMC on success or an ERR_PTR()-encoded error code
|
||||
* otherwise.
|
||||
*/
|
||||
struct tegra_pmc *devm_tegra_pmc_get(struct device *dev)
|
||||
{
|
||||
struct tegra_pmc *pmc;
|
||||
int err;
|
||||
|
||||
pmc = tegra_pmc_get(dev);
|
||||
if (IS_ERR(pmc))
|
||||
return pmc;
|
||||
|
||||
err = devm_add_action_or_reset(dev, tegra_pmc_put_device, pmc);
|
||||
if (err < 0)
|
||||
return ERR_PTR(err);
|
||||
|
||||
return pmc;
|
||||
}
|
||||
EXPORT_SYMBOL(devm_tegra_pmc_get);
|
||||
|
||||
/**
|
||||
* tegra_pmc_powergate_power_on() - power on partition
|
||||
* @pmc: power management controller
|
||||
* @id: partition ID
|
||||
*/
|
||||
int tegra_powergate_power_on(unsigned int id)
|
||||
int tegra_pmc_powergate_power_on(struct tegra_pmc *pmc, unsigned int id)
|
||||
{
|
||||
if (!tegra_powergate_is_available(pmc, id))
|
||||
return -EINVAL;
|
||||
|
||||
return tegra_powergate_set(pmc, id, true);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_pmc_powergate_power_on);
|
||||
|
||||
/**
|
||||
* tegra_powergate_power_on() - power on partition
|
||||
* @id: partition ID
|
||||
*/
|
||||
int tegra_powergate_power_on(unsigned int id)
|
||||
{
|
||||
return tegra_pmc_powergate_power_on(pmc, id);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_powergate_power_on);
|
||||
|
||||
/**
|
||||
* tegra_pmc_powergate_power_off() - power off partition
|
||||
* @pmc: power management controller
|
||||
* @id: partition ID
|
||||
*/
|
||||
int tegra_pmc_powergate_power_off(struct tegra_pmc *pmc, unsigned int id)
|
||||
{
|
||||
if (!tegra_powergate_is_available(pmc, id))
|
||||
return -EINVAL;
|
||||
|
||||
return tegra_powergate_set(pmc, id, false);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_pmc_powergate_power_off);
|
||||
|
||||
/**
|
||||
* tegra_powergate_power_off() - power off partition
|
||||
* @id: partition ID
|
||||
*/
|
||||
int tegra_powergate_power_off(unsigned int id)
|
||||
{
|
||||
if (!tegra_powergate_is_available(pmc, id))
|
||||
return -EINVAL;
|
||||
|
||||
return tegra_powergate_set(pmc, id, false);
|
||||
return tegra_pmc_powergate_power_off(pmc, id);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_powergate_power_off);
|
||||
|
||||
|
|
@ -976,32 +1085,45 @@ static int tegra_powergate_is_powered(struct tegra_pmc *pmc, unsigned int id)
|
|||
if (!tegra_powergate_is_valid(pmc, id))
|
||||
return -EINVAL;
|
||||
|
||||
return tegra_powergate_state(id);
|
||||
return tegra_powergate_state(pmc, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_pmc_powergate_remove_clamping() - remove power clamps for partition
|
||||
* @pmc: power management controller
|
||||
* @id: partition ID
|
||||
*/
|
||||
int tegra_pmc_powergate_remove_clamping(struct tegra_pmc *pmc, unsigned int id)
|
||||
{
|
||||
if (!tegra_powergate_is_available(pmc, id))
|
||||
return -EINVAL;
|
||||
|
||||
return __tegra_powergate_remove_clamping(pmc, id);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_pmc_powergate_remove_clamping);
|
||||
|
||||
/**
|
||||
* tegra_powergate_remove_clamping() - remove power clamps for partition
|
||||
* @id: partition ID
|
||||
*/
|
||||
int tegra_powergate_remove_clamping(unsigned int id)
|
||||
{
|
||||
if (!tegra_powergate_is_available(pmc, id))
|
||||
return -EINVAL;
|
||||
|
||||
return __tegra_powergate_remove_clamping(pmc, id);
|
||||
return tegra_pmc_powergate_remove_clamping(pmc, id);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_powergate_remove_clamping);
|
||||
|
||||
/**
|
||||
* tegra_powergate_sequence_power_up() - power up partition
|
||||
* tegra_pmc_powergate_sequence_power_up() - power up partition
|
||||
* @pmc: power management controller
|
||||
* @id: partition ID
|
||||
* @clk: clock for partition
|
||||
* @rst: reset for partition
|
||||
*
|
||||
* Must be called with clk disabled, and returns with clk enabled.
|
||||
*/
|
||||
int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
|
||||
struct reset_control *rst)
|
||||
int tegra_pmc_powergate_sequence_power_up(struct tegra_pmc *pmc,
|
||||
unsigned int id, struct clk *clk,
|
||||
struct reset_control *rst)
|
||||
{
|
||||
struct tegra_powergate *pg;
|
||||
int err;
|
||||
|
|
@ -1035,6 +1157,21 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
|
|||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_pmc_powergate_sequence_power_up);
|
||||
|
||||
/**
|
||||
* tegra_powergate_sequence_power_up() - power up partition
|
||||
* @id: partition ID
|
||||
* @clk: clock for partition
|
||||
* @rst: reset for partition
|
||||
*
|
||||
* Must be called with clk disabled, and returns with clk enabled.
|
||||
*/
|
||||
int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
|
||||
struct reset_control *rst)
|
||||
{
|
||||
return tegra_pmc_powergate_sequence_power_up(pmc, id, clk, rst);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_powergate_sequence_power_up);
|
||||
|
||||
/**
|
||||
|
|
@ -1099,7 +1236,8 @@ int tegra_pmc_cpu_remove_clamping(unsigned int cpuid)
|
|||
return tegra_powergate_remove_clamping(id);
|
||||
}
|
||||
|
||||
static void tegra_pmc_program_reboot_reason(const char *cmd)
|
||||
static void tegra_pmc_program_reboot_reason(struct tegra_pmc *pmc,
|
||||
const char *cmd)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
|
|
@ -1123,17 +1261,15 @@ static void tegra_pmc_program_reboot_reason(const char *cmd)
|
|||
static int tegra_pmc_reboot_notify(struct notifier_block *this,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct tegra_pmc *pmc = container_of(this, struct tegra_pmc,
|
||||
reboot_notifier);
|
||||
if (action == SYS_RESTART)
|
||||
tegra_pmc_program_reboot_reason(data);
|
||||
tegra_pmc_program_reboot_reason(pmc, data);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block tegra_pmc_reboot_notifier = {
|
||||
.notifier_call = tegra_pmc_reboot_notify,
|
||||
};
|
||||
|
||||
static void tegra_pmc_restart(void)
|
||||
static void tegra_pmc_restart(struct tegra_pmc *pmc)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
|
|
@ -1145,13 +1281,17 @@ static void tegra_pmc_restart(void)
|
|||
|
||||
static int tegra_pmc_restart_handler(struct sys_off_data *data)
|
||||
{
|
||||
tegra_pmc_restart();
|
||||
struct tegra_pmc *pmc = data->cb_data;
|
||||
|
||||
tegra_pmc_restart(pmc);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int tegra_pmc_power_off_handler(struct sys_off_data *data)
|
||||
{
|
||||
struct tegra_pmc *pmc = data->cb_data;
|
||||
|
||||
/*
|
||||
* Reboot Nexus 7 into special bootloader mode if USB cable is
|
||||
* connected in order to display battery status and power off.
|
||||
|
|
@ -1161,7 +1301,7 @@ static int tegra_pmc_power_off_handler(struct sys_off_data *data)
|
|||
const u32 go_to_charger_mode = 0xa5a55a5a;
|
||||
|
||||
tegra_pmc_writel(pmc, go_to_charger_mode, PMC_SCRATCH37);
|
||||
tegra_pmc_restart();
|
||||
tegra_pmc_restart(pmc);
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
|
|
@ -1169,6 +1309,7 @@ static int tegra_pmc_power_off_handler(struct sys_off_data *data)
|
|||
|
||||
static int powergate_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct tegra_pmc *pmc = data;
|
||||
unsigned int i;
|
||||
int status;
|
||||
|
||||
|
|
@ -1377,6 +1518,8 @@ static int
|
|||
tegra_pmc_core_pd_set_performance_state(struct generic_pm_domain *genpd,
|
||||
unsigned int level)
|
||||
{
|
||||
struct tegra_pmc_core_pd *pd = to_core_pd(genpd);
|
||||
struct tegra_pmc *pmc = pd->pmc;
|
||||
struct dev_pm_opp *opp;
|
||||
int err;
|
||||
|
||||
|
|
@ -1404,30 +1547,31 @@ tegra_pmc_core_pd_set_performance_state(struct generic_pm_domain *genpd,
|
|||
|
||||
static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np)
|
||||
{
|
||||
struct generic_pm_domain *genpd;
|
||||
const char *rname[] = { "core", NULL};
|
||||
struct tegra_pmc_core_pd *pd;
|
||||
int err;
|
||||
|
||||
genpd = devm_kzalloc(pmc->dev, sizeof(*genpd), GFP_KERNEL);
|
||||
if (!genpd)
|
||||
pd = devm_kzalloc(pmc->dev, sizeof(*pd), GFP_KERNEL);
|
||||
if (!pd)
|
||||
return -ENOMEM;
|
||||
|
||||
genpd->name = "core";
|
||||
genpd->flags = GENPD_FLAG_NO_SYNC_STATE;
|
||||
genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state;
|
||||
pd->genpd.name = "core";
|
||||
pd->genpd.flags = GENPD_FLAG_NO_SYNC_STATE;
|
||||
pd->genpd.set_performance_state = tegra_pmc_core_pd_set_performance_state;
|
||||
pd->pmc = pmc;
|
||||
|
||||
err = devm_pm_opp_set_regulators(pmc->dev, rname);
|
||||
if (err)
|
||||
return dev_err_probe(pmc->dev, err,
|
||||
"failed to set core OPP regulator\n");
|
||||
|
||||
err = pm_genpd_init(genpd, NULL, false);
|
||||
err = pm_genpd_init(&pd->genpd, NULL, false);
|
||||
if (err) {
|
||||
dev_err(pmc->dev, "failed to init core genpd: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = of_genpd_add_provider_simple(np, genpd);
|
||||
err = of_genpd_add_provider_simple(np, &pd->genpd);
|
||||
if (err) {
|
||||
dev_err(pmc->dev, "failed to add core genpd: %d\n", err);
|
||||
goto remove_genpd;
|
||||
|
|
@ -1436,7 +1580,7 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np)
|
|||
return 0;
|
||||
|
||||
remove_genpd:
|
||||
pm_genpd_remove(genpd);
|
||||
pm_genpd_remove(&pd->genpd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -1499,7 +1643,7 @@ static void tegra_powergate_remove(struct generic_pm_domain *genpd)
|
|||
|
||||
kfree(pg->clks);
|
||||
|
||||
set_bit(pg->id, pmc->powergates_available);
|
||||
set_bit(pg->id, pg->pmc->powergates_available);
|
||||
|
||||
kfree(pg);
|
||||
}
|
||||
|
|
@ -1603,11 +1747,12 @@ static void tegra_io_pad_unprepare(struct tegra_pmc *pmc)
|
|||
|
||||
/**
|
||||
* tegra_io_pad_power_enable() - enable power to I/O pad
|
||||
* @pmc: power management controller
|
||||
* @id: Tegra I/O pad ID for which to enable power
|
||||
*
|
||||
* Returns: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int tegra_io_pad_power_enable(enum tegra_io_pad id)
|
||||
int tegra_pmc_io_pad_power_enable(struct tegra_pmc *pmc, enum tegra_io_pad id)
|
||||
{
|
||||
const struct tegra_io_pad_soc *pad;
|
||||
unsigned long request, status;
|
||||
|
|
@ -1642,15 +1787,28 @@ unlock:
|
|||
mutex_unlock(&pmc->powergates_lock);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_pmc_io_pad_power_enable);
|
||||
|
||||
/**
|
||||
* tegra_io_pad_power_enable() - enable power to I/O pad
|
||||
* @id: Tegra I/O pad ID for which to enable power
|
||||
*
|
||||
* Returns: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int tegra_io_pad_power_enable(enum tegra_io_pad id)
|
||||
{
|
||||
return tegra_pmc_io_pad_power_enable(pmc, id);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_io_pad_power_enable);
|
||||
|
||||
/**
|
||||
* tegra_io_pad_power_disable() - disable power to I/O pad
|
||||
* tegra_pmc_io_pad_power_disable() - disable power to I/O pad
|
||||
* @pmc: power management controller
|
||||
* @id: Tegra I/O pad ID for which to disable power
|
||||
*
|
||||
* Returns: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int tegra_io_pad_power_disable(enum tegra_io_pad id)
|
||||
int tegra_pmc_io_pad_power_disable(struct tegra_pmc *pmc, enum tegra_io_pad id)
|
||||
{
|
||||
const struct tegra_io_pad_soc *pad;
|
||||
unsigned long request, status;
|
||||
|
|
@ -1685,6 +1843,18 @@ unlock:
|
|||
mutex_unlock(&pmc->powergates_lock);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_pmc_io_pad_power_disable);
|
||||
|
||||
/**
|
||||
* tegra_io_pad_power_disable() - disable power to I/O pad
|
||||
* @id: Tegra I/O pad ID for which to disable power
|
||||
*
|
||||
* Returns: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int tegra_io_pad_power_disable(enum tegra_io_pad id)
|
||||
{
|
||||
return tegra_pmc_io_pad_power_disable(pmc, id);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_io_pad_power_disable);
|
||||
|
||||
static int tegra_io_pad_is_powered(struct tegra_pmc *pmc, enum tegra_io_pad id)
|
||||
|
|
@ -1905,6 +2075,50 @@ static int tegra_pmc_parse_dt(struct tegra_pmc *pmc, struct device_node *np)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* translate sc7 wake sources back into IRQs to catch edge triggered wakeups */
|
||||
static void tegra186_pmc_wake_handler(struct irq_work *work)
|
||||
{
|
||||
struct tegra_pmc *pmc = container_of(work, struct tegra_pmc, wake_work);
|
||||
unsigned int i, wake;
|
||||
|
||||
for (i = 0; i < pmc->soc->max_wake_vectors; i++) {
|
||||
unsigned long status = pmc->wake_status[i];
|
||||
|
||||
for_each_set_bit(wake, &status, 32) {
|
||||
irq_hw_number_t hwirq = wake + (i * 32);
|
||||
struct irq_desc *desc;
|
||||
unsigned int irq;
|
||||
|
||||
irq = irq_find_mapping(pmc->domain, hwirq);
|
||||
if (!irq) {
|
||||
dev_warn(pmc->dev,
|
||||
"No IRQ found for WAKE#%lu!\n",
|
||||
hwirq);
|
||||
continue;
|
||||
}
|
||||
|
||||
dev_dbg(pmc->dev,
|
||||
"Resume caused by WAKE#%lu mapped to IRQ#%u\n",
|
||||
hwirq, irq);
|
||||
|
||||
desc = irq_to_desc(irq);
|
||||
if (!desc) {
|
||||
dev_warn(pmc->dev,
|
||||
"No descriptor found for IRQ#%u\n",
|
||||
irq);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!desc->action || !desc->action->name)
|
||||
continue;
|
||||
|
||||
generic_handle_irq(irq);
|
||||
}
|
||||
|
||||
pmc->wake_status[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int tegra_pmc_init(struct tegra_pmc *pmc)
|
||||
{
|
||||
if (pmc->soc->max_wake_events > 0) {
|
||||
|
|
@ -1923,6 +2137,18 @@ static int tegra_pmc_init(struct tegra_pmc *pmc)
|
|||
pmc->wake_cntrl_level_map = bitmap_zalloc(pmc->soc->max_wake_events, GFP_KERNEL);
|
||||
if (!pmc->wake_cntrl_level_map)
|
||||
return -ENOMEM;
|
||||
|
||||
pmc->wake_status = kcalloc(pmc->soc->max_wake_vectors, sizeof(u32), GFP_KERNEL);
|
||||
if (!pmc->wake_status)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Initialize IRQ work for processing wake IRQs. Must use
|
||||
* HARD_IRQ variant to run in hard IRQ context on PREEMPT_RT
|
||||
* because we call generic_handle_irq() which requires hard
|
||||
* IRQ context.
|
||||
*/
|
||||
pmc->wake_work = IRQ_WORK_INIT_HARD(tegra186_pmc_wake_handler);
|
||||
}
|
||||
|
||||
if (pmc->soc->init)
|
||||
|
|
@ -2104,9 +2330,9 @@ static int tegra_io_pad_pinconf_set(struct pinctrl_dev *pctl_dev,
|
|||
switch (param) {
|
||||
case PIN_CONFIG_MODE_LOW_POWER:
|
||||
if (arg)
|
||||
err = tegra_io_pad_power_disable(pad->id);
|
||||
err = tegra_pmc_io_pad_power_disable(pmc, pad->id);
|
||||
else
|
||||
err = tegra_io_pad_power_enable(pad->id);
|
||||
err = tegra_pmc_io_pad_power_enable(pmc, pad->id);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
|
|
@ -2163,6 +2389,7 @@ static int tegra_pmc_pinctrl_init(struct tegra_pmc *pmc)
|
|||
static ssize_t reset_reason_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tegra_pmc *pmc = dev_get_drvdata(dev);
|
||||
u32 value;
|
||||
|
||||
value = tegra_pmc_readl(pmc, pmc->soc->regs->rst_status);
|
||||
|
|
@ -2180,6 +2407,7 @@ static DEVICE_ATTR_RO(reset_reason);
|
|||
static ssize_t reset_level_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tegra_pmc *pmc = dev_get_drvdata(dev);
|
||||
u32 value;
|
||||
|
||||
value = tegra_pmc_readl(pmc, pmc->soc->regs->rst_status);
|
||||
|
|
@ -2543,7 +2771,7 @@ static int tegra_pmc_clk_notify_cb(struct notifier_block *nb,
|
|||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static void pmc_clk_fence_udelay(u32 offset)
|
||||
static void pmc_clk_fence_udelay(struct tegra_pmc *pmc, u32 offset)
|
||||
{
|
||||
tegra_pmc_readl(pmc, offset);
|
||||
/* pmc clk propagation delay 2 us */
|
||||
|
|
@ -2555,7 +2783,7 @@ static u8 pmc_clk_mux_get_parent(struct clk_hw *hw)
|
|||
struct pmc_clk *clk = to_pmc_clk(hw);
|
||||
u32 val;
|
||||
|
||||
val = tegra_pmc_readl(pmc, clk->offs) >> clk->mux_shift;
|
||||
val = tegra_pmc_readl(clk->pmc, clk->offs) >> clk->mux_shift;
|
||||
val &= PMC_CLK_OUT_MUX_MASK;
|
||||
|
||||
return val;
|
||||
|
|
@ -2566,11 +2794,11 @@ static int pmc_clk_mux_set_parent(struct clk_hw *hw, u8 index)
|
|||
struct pmc_clk *clk = to_pmc_clk(hw);
|
||||
u32 val;
|
||||
|
||||
val = tegra_pmc_readl(pmc, clk->offs);
|
||||
val = tegra_pmc_readl(clk->pmc, clk->offs);
|
||||
val &= ~(PMC_CLK_OUT_MUX_MASK << clk->mux_shift);
|
||||
val |= index << clk->mux_shift;
|
||||
tegra_pmc_writel(pmc, val, clk->offs);
|
||||
pmc_clk_fence_udelay(clk->offs);
|
||||
tegra_pmc_writel(clk->pmc, val, clk->offs);
|
||||
pmc_clk_fence_udelay(clk->pmc, clk->offs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2580,26 +2808,27 @@ static int pmc_clk_is_enabled(struct clk_hw *hw)
|
|||
struct pmc_clk *clk = to_pmc_clk(hw);
|
||||
u32 val;
|
||||
|
||||
val = tegra_pmc_readl(pmc, clk->offs) & BIT(clk->force_en_shift);
|
||||
val = tegra_pmc_readl(clk->pmc, clk->offs) & BIT(clk->force_en_shift);
|
||||
|
||||
return val ? 1 : 0;
|
||||
}
|
||||
|
||||
static void pmc_clk_set_state(unsigned long offs, u32 shift, int state)
|
||||
static void pmc_clk_set_state(struct tegra_pmc *pmc, unsigned long offs,
|
||||
u32 shift, int state)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = tegra_pmc_readl(pmc, offs);
|
||||
val = state ? (val | BIT(shift)) : (val & ~BIT(shift));
|
||||
tegra_pmc_writel(pmc, val, offs);
|
||||
pmc_clk_fence_udelay(offs);
|
||||
pmc_clk_fence_udelay(pmc, offs);
|
||||
}
|
||||
|
||||
static int pmc_clk_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct pmc_clk *clk = to_pmc_clk(hw);
|
||||
|
||||
pmc_clk_set_state(clk->offs, clk->force_en_shift, 1);
|
||||
pmc_clk_set_state(clk->pmc, clk->offs, clk->force_en_shift, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2608,7 +2837,7 @@ static void pmc_clk_disable(struct clk_hw *hw)
|
|||
{
|
||||
struct pmc_clk *clk = to_pmc_clk(hw);
|
||||
|
||||
pmc_clk_set_state(clk->offs, clk->force_en_shift, 0);
|
||||
pmc_clk_set_state(clk->pmc, clk->offs, clk->force_en_shift, 0);
|
||||
}
|
||||
|
||||
static const struct clk_ops pmc_clk_ops = {
|
||||
|
|
@ -2640,6 +2869,7 @@ tegra_pmc_clk_out_register(struct tegra_pmc *pmc,
|
|||
CLK_SET_PARENT_GATE;
|
||||
|
||||
pmc_clk->hw.init = &init;
|
||||
pmc_clk->pmc = pmc;
|
||||
pmc_clk->offs = offset;
|
||||
pmc_clk->mux_shift = data->mux_shift;
|
||||
pmc_clk->force_en_shift = data->force_en_shift;
|
||||
|
|
@ -2650,15 +2880,16 @@ tegra_pmc_clk_out_register(struct tegra_pmc *pmc,
|
|||
static int pmc_clk_gate_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct pmc_clk_gate *gate = to_pmc_clk_gate(hw);
|
||||
u32 value = tegra_pmc_readl(gate->pmc, gate->offs);
|
||||
|
||||
return tegra_pmc_readl(pmc, gate->offs) & BIT(gate->shift) ? 1 : 0;
|
||||
return value & BIT(gate->shift) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int pmc_clk_gate_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct pmc_clk_gate *gate = to_pmc_clk_gate(hw);
|
||||
|
||||
pmc_clk_set_state(gate->offs, gate->shift, 1);
|
||||
pmc_clk_set_state(gate->pmc, gate->offs, gate->shift, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2667,7 +2898,7 @@ static void pmc_clk_gate_disable(struct clk_hw *hw)
|
|||
{
|
||||
struct pmc_clk_gate *gate = to_pmc_clk_gate(hw);
|
||||
|
||||
pmc_clk_set_state(gate->offs, gate->shift, 0);
|
||||
pmc_clk_set_state(gate->pmc, gate->offs, gate->shift, 0);
|
||||
}
|
||||
|
||||
static const struct clk_ops pmc_clk_gate_ops = {
|
||||
|
|
@ -2695,6 +2926,7 @@ tegra_pmc_clk_gate_register(struct tegra_pmc *pmc, const char *name,
|
|||
init.flags = 0;
|
||||
|
||||
gate->hw.init = &init;
|
||||
gate->pmc = pmc;
|
||||
gate->offs = offset;
|
||||
gate->shift = shift;
|
||||
|
||||
|
|
@ -2858,6 +3090,8 @@ static int tegra_pmc_regmap_init(struct tegra_pmc *pmc)
|
|||
|
||||
static void tegra_pmc_reset_suspend_mode(void *data)
|
||||
{
|
||||
struct tegra_pmc *pmc = data;
|
||||
|
||||
pmc->suspend_mode = TEGRA_SUSPEND_NOT_READY;
|
||||
}
|
||||
|
||||
|
|
@ -2880,7 +3114,7 @@ static int tegra_pmc_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
|
||||
err = devm_add_action_or_reset(&pdev->dev, tegra_pmc_reset_suspend_mode,
|
||||
NULL);
|
||||
pmc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
@ -2931,8 +3165,10 @@ static int tegra_pmc_probe(struct platform_device *pdev)
|
|||
* CPU without resetting everything else.
|
||||
*/
|
||||
if (pmc->scratch) {
|
||||
pmc->reboot_notifier.notifier_call = tegra_pmc_reboot_notify;
|
||||
|
||||
err = devm_register_reboot_notifier(&pdev->dev,
|
||||
&tegra_pmc_reboot_notifier);
|
||||
&pmc->reboot_notifier);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"unable to register reboot notifier, %d\n",
|
||||
|
|
@ -2944,7 +3180,8 @@ static int tegra_pmc_probe(struct platform_device *pdev)
|
|||
err = devm_register_sys_off_handler(&pdev->dev,
|
||||
SYS_OFF_MODE_RESTART,
|
||||
SYS_OFF_PRIO_LOW,
|
||||
tegra_pmc_restart_handler, NULL);
|
||||
tegra_pmc_restart_handler,
|
||||
pmc);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to register sys-off handler: %d\n",
|
||||
err);
|
||||
|
|
@ -2958,7 +3195,8 @@ static int tegra_pmc_probe(struct platform_device *pdev)
|
|||
err = devm_register_sys_off_handler(&pdev->dev,
|
||||
SYS_OFF_MODE_POWER_OFF,
|
||||
SYS_OFF_PRIO_FIRMWARE,
|
||||
tegra_pmc_power_off_handler, NULL);
|
||||
tegra_pmc_power_off_handler,
|
||||
pmc);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to register sys-off handler: %d\n",
|
||||
err);
|
||||
|
|
@ -3024,7 +3262,7 @@ static int tegra_pmc_probe(struct platform_device *pdev)
|
|||
if (pmc->soc->set_wake_filters)
|
||||
pmc->soc->set_wake_filters(pmc);
|
||||
|
||||
debugfs_create_file("powergate", 0444, NULL, NULL, &powergate_fops);
|
||||
debugfs_create_file("powergate", 0444, NULL, pmc, &powergate_fops);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
@ -3129,47 +3367,33 @@ static void wke_clear_wake_status(struct tegra_pmc *pmc)
|
|||
}
|
||||
}
|
||||
|
||||
/* translate sc7 wake sources back into IRQs to catch edge triggered wakeups */
|
||||
static void tegra186_pmc_process_wake_events(struct tegra_pmc *pmc, unsigned int index,
|
||||
unsigned long status)
|
||||
{
|
||||
unsigned int wake;
|
||||
|
||||
dev_dbg(pmc->dev, "Wake[%d:%d] status=%#lx\n", (index * 32) + 31, index * 32, status);
|
||||
|
||||
for_each_set_bit(wake, &status, 32) {
|
||||
irq_hw_number_t hwirq = wake + 32 * index;
|
||||
struct irq_desc *desc;
|
||||
unsigned int irq;
|
||||
|
||||
irq = irq_find_mapping(pmc->domain, hwirq);
|
||||
|
||||
desc = irq_to_desc(irq);
|
||||
if (!desc || !desc->action || !desc->action->name) {
|
||||
dev_dbg(pmc->dev, "Resume caused by WAKE%ld, IRQ %d\n", hwirq, irq);
|
||||
continue;
|
||||
}
|
||||
|
||||
dev_dbg(pmc->dev, "Resume caused by WAKE%ld, %s\n", hwirq, desc->action->name);
|
||||
generic_handle_irq(irq);
|
||||
}
|
||||
}
|
||||
|
||||
static void tegra186_pmc_wake_syscore_resume(void *data)
|
||||
{
|
||||
u32 status, mask;
|
||||
struct tegra_pmc *pmc = data;
|
||||
unsigned int i;
|
||||
u32 mask;
|
||||
|
||||
for (i = 0; i < pmc->soc->max_wake_vectors; i++) {
|
||||
mask = readl(pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(i));
|
||||
status = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(i)) & mask;
|
||||
|
||||
tegra186_pmc_process_wake_events(pmc, i, status);
|
||||
pmc->wake_status[i] = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(i)) & mask;
|
||||
}
|
||||
|
||||
/* Schedule IRQ work to process wake IRQs (if any) */
|
||||
irq_work_queue(&pmc->wake_work);
|
||||
}
|
||||
|
||||
static int tegra186_pmc_wake_syscore_suspend(void *data)
|
||||
{
|
||||
struct tegra_pmc *pmc = data;
|
||||
unsigned int i;
|
||||
|
||||
/* Check if there are unhandled wake IRQs */
|
||||
for (i = 0; i < pmc->soc->max_wake_vectors; i++)
|
||||
if (pmc->wake_status[i])
|
||||
dev_warn(pmc->dev,
|
||||
"Unhandled wake IRQs pending vector[%u]: 0x%x\n",
|
||||
i, pmc->wake_status[i]);
|
||||
|
||||
wke_read_sw_wake_status(pmc);
|
||||
|
||||
/* flip the wakeup trigger for dual-edge triggered pads
|
||||
|
|
@ -3843,6 +4067,7 @@ static const struct tegra_pmc_regs tegra186_pmc_regs = {
|
|||
static void tegra186_pmc_init(struct tegra_pmc *pmc)
|
||||
{
|
||||
pmc->syscore.ops = &tegra186_pmc_wake_syscore_ops;
|
||||
pmc->syscore.data = pmc;
|
||||
register_syscore(&pmc->syscore);
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue