ARM: SoC drivers for 5.15

These are updates for drivers that are tied to a particular SoC,
 including the correspondig device tree bindings:
 
  - A couple of reset controller changes for unisoc, uniphier, renesas
    and zte platforms
 
  - memory controller driver fixes for omap and tegra
 
  - Rockchip io domain driver updates
 
  - Lots of updates for qualcomm platforms, mostly touching their
    firmware and power management drivers
 
  - Tegra FUSE and firmware driver updateѕ
 
  - Support for virtio transports in the SCMI firmware framework
 
  - cleanup of ixp4xx drivers, towards enabling multiplatform
    support and bringing it up to date with modern platforms
 
  - Minor updates for keystone, mediatek, omap, renesas.
 
 Signed-off-by: Arnd Bergmann <arnd@arndb.de>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iD8DBQBhLz215t5GS2LDRf4RAjlHAJ473D0PymaTzv68EuPHThG+DEPifQCdGjLq
 QGBB6JidIP8rtEdC+LWBB8I=
 =M5+N
 -----END PGP SIGNATURE-----

Merge tag 'drivers-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc

Pull ARM SoC driver updates from Arnd Bergmann:
 "These are updates for drivers that are tied to a particular SoC,
  including the correspondig device tree bindings:

   - A couple of reset controller changes for unisoc, uniphier, renesas
     and zte platforms

   - memory controller driver fixes for omap and tegra

   - Rockchip io domain driver updates

   - Lots of updates for qualcomm platforms, mostly touching their
     firmware and power management drivers

   - Tegra FUSE and firmware driver updateѕ

   - Support for virtio transports in the SCMI firmware framework

   - cleanup of ixp4xx drivers, towards enabling multiplatform support
     and bringing it up to date with modern platforms

   - Minor updates for keystone, mediatek, omap, renesas"

* tag 'drivers-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (96 commits)
  reset: simple: remove ZTE details in Kconfig help
  soc: rockchip: io-domain: Remove unneeded semicolon
  soc: rockchip: io-domain: add rk3568 support
  dt-bindings: power: add rk3568-pmu-io-domain support
  bus: ixp4xx: return on error in ixp4xx_exp_probe()
  soc: renesas: Prefer memcpy() over strcpy()
  firmware: tegra: Stop using seq_get_buf()
  soc/tegra: fuse: Enable fuse clock on suspend for Tegra124
  soc/tegra: fuse: Add runtime PM support
  soc/tegra: fuse: Clear fuse->clk on driver probe failure
  soc/tegra: pmc: Prevent racing with cpuilde driver
  soc/tegra: bpmp: Remove unused including <linux/version.h>
  dt-bindings: soc: ti: pruss: Add dma-coherent property
  soc: ti: Remove pm_runtime_irq_safe() usage for smartreflex
  soc: ti: pruss: Enable support for ICSSG subsystems on K3 AM64x SoCs
  dt-bindings: soc: ti: pruss: Update bindings for K3 AM64x SoCs
  firmware: arm_scmi: Use WARN_ON() to check configured transports
  firmware: arm_scmi: Fix boolconv.cocci warnings
  soc: mediatek: mmsys: Fix missing UFOE component in mt8173 table routing
  soc: mediatek: mmsys: add MT8365 support
  ...
This commit is contained in:
Linus Torvalds 2021-09-01 15:25:28 -07:00
commit 866147b8fa
93 changed files with 4222 additions and 1047 deletions

View File

@ -0,0 +1,61 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/ata/intel,ixp4xx-compact-flash.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Intel IXP4xx CompactFlash Card Controller
maintainers:
- Linus Walleij <linus.walleij@linaro.org>
description: |
The IXP4xx network processors have a CompactFlash interface that presents
a CompactFlash card to the system as a true IDE (parallel ATA) device. The
device is always connected to the expansion bus of the IXP4xx SoCs using one
or two chip select areas and address translating logic on the board. The
node must be placed inside a chip select node on the IXP4xx expansion bus.
properties:
compatible:
const: intel,ixp4xx-compact-flash
reg:
items:
- description: Command interface registers
- description: Control interface registers
interrupts:
maxItems: 1
required:
- compatible
- reg
- interrupts
allOf:
- $ref: pata-common.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
bus@c4000000 {
compatible = "intel,ixp43x-expansion-bus-controller", "syscon";
reg = <0xc4000000 0x1000>;
native-endian;
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0x0 0x50000000 0x01000000>, <1 0x0 0x51000000 0x01000000>;
dma-ranges = <0 0x0 0x50000000 0x01000000>, <1 0x0 0x51000000 0x01000000>;
ide@1,0 {
compatible = "intel,ixp4xx-compact-flash";
reg = <1 0x00000000 0x1000>, <1 0x00040000 0x1000>;
interrupt-parent = <&gpio0>;
interrupts = <12 IRQ_TYPE_EDGE_RISING>;
};
};
...

View File

@ -0,0 +1,168 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/bus/intel,ixp4xx-expansion-bus-controller.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Intel IXP4xx Expansion Bus Controller
description: |
The IXP4xx expansion bus controller handles access to devices on the
memory-mapped expansion bus on the Intel IXP4xx family of system on chips,
including IXP42x, IXP43x, IXP45x and IXP46x.
maintainers:
- Linus Walleij <linus.walleij@linaro.org>
properties:
$nodename:
pattern: '^bus@[0-9a-f]+$'
compatible:
items:
- enum:
- intel,ixp42x-expansion-bus-controller
- intel,ixp43x-expansion-bus-controller
- intel,ixp45x-expansion-bus-controller
- intel,ixp46x-expansion-bus-controller
- const: syscon
reg:
description: Control registers for the expansion bus, these are not
inside the memory range handled by the expansion bus.
maxItems: 1
native-endian:
$ref: /schemas/types.yaml#/definitions/flag
description: The IXP4xx has a peculiar MMIO access scheme, as it changes
the access pattern for words (swizzling) on the bus depending on whether
the SoC is running in big-endian or little-endian mode. Thus the
registers must always be accessed using native endianness.
"#address-cells":
description: |
The first cell is the chip select number.
The second cell is the address offset within the bank.
const: 2
"#size-cells":
const: 1
ranges: true
dma-ranges: true
patternProperties:
"^.*@[0-7],[0-9a-f]+$":
description: Devices attached to chip selects are represented as
subnodes.
type: object
properties:
intel,ixp4xx-eb-t1:
description: Address timing, extend address phase with n cycles.
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 3
intel,ixp4xx-eb-t2:
description: Setup chip select timing, extend setup phase with n cycles.
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 3
intel,ixp4xx-eb-t3:
description: Strobe timing, extend strobe phase with n cycles.
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 15
intel,ixp4xx-eb-t4:
description: Hold timing, extend hold phase with n cycles.
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 3
intel,ixp4xx-eb-t5:
description: Recovery timing, extend recovery phase with n cycles.
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 15
intel,ixp4xx-eb-cycle-type:
description: The type of cycles to use on the expansion bus for this
chip select. 0 = Intel cycles, 1 = Motorola cycles, 2 = HPI cycles.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2]
intel,ixp4xx-eb-byte-access-on-halfword:
description: Allow byte read access on half word devices.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1]
intel,ixp4xx-eb-hpi-hrdy-pol-high:
description: Set HPI HRDY polarity to active high when using HPI.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1]
intel,ixp4xx-eb-mux-address-and-data:
description: Multiplex address and data on the data bus.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1]
intel,ixp4xx-eb-ahb-split-transfers:
description: Enable AHB split transfers.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1]
intel,ixp4xx-eb-write-enable:
description: Enable write cycles.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1]
intel,ixp4xx-eb-byte-access:
description: Expansion bus uses only 8 bits. The default is to use
16 bits.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1]
required:
- compatible
- reg
- native-endian
- "#address-cells"
- "#size-cells"
- ranges
- dma-ranges
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
bus@50000000 {
compatible = "intel,ixp42x-expansion-bus-controller", "syscon";
reg = <0xc4000000 0x28>;
native-endian;
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0x0 0x50000000 0x01000000>,
<1 0x0 0x51000000 0x01000000>;
dma-ranges = <0 0x0 0x50000000 0x01000000>,
<1 0x0 0x51000000 0x01000000>;
flash@0,0 {
compatible = "intel,ixp4xx-flash", "cfi-flash";
bank-width = <2>;
reg = <0 0x00000000 0x1000000>;
intel,ixp4xx-eb-t3 = <3>;
intel,ixp4xx-eb-cycle-type = <0>;
intel,ixp4xx-eb-byte-access-on-halfword = <1>;
intel,ixp4xx-eb-write-enable = <1>;
intel,ixp4xx-eb-byte-access = <0>;
};
serial@1,0 {
compatible = "exar,xr16l2551", "ns8250";
reg = <1 0x00000000 0x10>;
interrupt-parent = <&gpio0>;
interrupts = <4 IRQ_TYPE_LEVEL_LOW>;
clock-frequency = <1843200>;
intel,ixp4xx-eb-t3 = <3>;
intel,ixp4xx-eb-cycle-type = <1>;
intel,ixp4xx-eb-write-enable = <1>;
intel,ixp4xx-eb-byte-access = <1>;
};
};

View File

@ -9,6 +9,7 @@ Required properties:
"fsl,imx53-sdma"
"fsl,imx6q-sdma"
"fsl,imx7d-sdma"
"fsl,imx6ul-sdma"
"fsl,imx8mq-sdma"
"fsl,imx8mm-sdma"
"fsl,imx8mn-sdma"

View File

@ -34,6 +34,10 @@ properties:
- description: SCMI compliant firmware with ARM SMC/HVC transport
items:
- const: arm,scmi-smc
- description: SCMI compliant firmware with SCMI Virtio transport.
The virtio transport only supports a single device.
items:
- const: arm,scmi-virtio
interrupts:
description:
@ -172,6 +176,7 @@ patternProperties:
Each sub-node represents a protocol supported. If the platform
supports a dedicated communication channel for a particular protocol,
then the corresponding transport properties must be present.
The virtio transport does not support a dedicated communication channel.
properties:
reg:
@ -195,7 +200,6 @@ patternProperties:
required:
- compatible
- shmem
if:
properties:
@ -209,6 +213,7 @@ then:
required:
- mboxes
- shmem
else:
if:
@ -219,6 +224,7 @@ else:
then:
required:
- arm,smc-id
- shmem
examples:
- |

View File

@ -30,6 +30,7 @@ properties:
- qcom,sc8180x-rpmhpd
- qcom,sdm845-rpmhpd
- qcom,sdx55-rpmhpd
- qcom,sm6115-rpmpd
- qcom,sm8150-rpmhpd
- qcom,sm8250-rpmhpd
- qcom,sm8350-rpmhpd

View File

@ -1,135 +0,0 @@
Rockchip SRAM for IO Voltage Domains:
-------------------------------------
IO domain voltages on some Rockchip SoCs are variable but need to be
kept in sync between the regulators and the SoC using a special
register.
A specific example using rk3288:
- If the regulator hooked up to a pin like SDMMC0_VDD is 3.3V then
bit 7 of GRF_IO_VSEL needs to be 0. If the regulator hooked up to
that same pin is 1.8V then bit 7 of GRF_IO_VSEL needs to be 1.
Said another way, this driver simply handles keeping bits in the SoC's
general register file (GRF) in sync with the actual value of a voltage
hooked up to the pins.
Note that this driver specifically doesn't include:
- any logic for deciding what voltage we should set regulators to
- any logic for deciding whether regulators (or internal SoC blocks)
should have power or not have power
If there were some other software that had the smarts of making
decisions about regulators, it would work in conjunction with this
driver. When that other software adjusted a regulator's voltage then
this driver would handle telling the SoC about it. A good example is
vqmmc for SD. In that case the dw_mmc driver simply is told about a
regulator. It changes the regulator between 3.3V and 1.8V at the
right time. This driver notices the change and makes sure that the
SoC is on the same page.
Required properties:
- compatible: should be one of:
- "rockchip,px30-io-voltage-domain" for px30
- "rockchip,px30-pmu-io-voltage-domain" for px30 pmu-domains
- "rockchip,rk3188-io-voltage-domain" for rk3188
- "rockchip,rk3228-io-voltage-domain" for rk3228
- "rockchip,rk3288-io-voltage-domain" for rk3288
- "rockchip,rk3328-io-voltage-domain" for rk3328
- "rockchip,rk3368-io-voltage-domain" for rk3368
- "rockchip,rk3368-pmu-io-voltage-domain" for rk3368 pmu-domains
- "rockchip,rk3399-io-voltage-domain" for rk3399
- "rockchip,rk3399-pmu-io-voltage-domain" for rk3399 pmu-domains
- "rockchip,rv1108-io-voltage-domain" for rv1108
- "rockchip,rv1108-pmu-io-voltage-domain" for rv1108 pmu-domains
Deprecated properties:
- rockchip,grf: phandle to the syscon managing the "general register files"
Systems should move the io-domains to a sub-node of the grf simple-mfd.
You specify supplies using the standard regulator bindings by including
a phandle the relevant regulator. All specified supplies must be able
to report their voltage. The IO Voltage Domain for any non-specified
supplies will be not be touched.
Possible supplies for PX30:
- vccio6-supply: The supply connected to VCCIO6.
- vccio1-supply: The supply connected to VCCIO1.
- vccio2-supply: The supply connected to VCCIO2.
- vccio3-supply: The supply connected to VCCIO3.
- vccio4-supply: The supply connected to VCCIO4.
- vccio5-supply: The supply connected to VCCIO5.
- vccio-oscgpi-supply: The supply connected to VCCIO_OSCGPI.
Possible supplies for PX30 pmu-domains:
- pmuio1-supply: The supply connected to PMUIO1.
- pmuio2-supply: The supply connected to PMUIO2.
Possible supplies for rk3188:
- ap0-supply: The supply connected to AP0_VCC.
- ap1-supply: The supply connected to AP1_VCC.
- cif-supply: The supply connected to CIF_VCC.
- flash-supply: The supply connected to FLASH_VCC.
- lcdc0-supply: The supply connected to LCD0_VCC.
- lcdc1-supply: The supply connected to LCD1_VCC.
- vccio0-supply: The supply connected to VCCIO0.
- vccio1-supply: The supply connected to VCCIO1.
Sometimes also labeled VCCIO1 and VCCIO2.
Possible supplies for rk3228:
- vccio1-supply: The supply connected to VCCIO1.
- vccio2-supply: The supply connected to VCCIO2.
- vccio3-supply: The supply connected to VCCIO3.
- vccio4-supply: The supply connected to VCCIO4.
Possible supplies for rk3288:
- audio-supply: The supply connected to APIO4_VDD.
- bb-supply: The supply connected to APIO5_VDD.
- dvp-supply: The supply connected to DVPIO_VDD.
- flash0-supply: The supply connected to FLASH0_VDD. Typically for eMMC
- flash1-supply: The supply connected to FLASH1_VDD. Also known as SDIO1.
- gpio30-supply: The supply connected to APIO1_VDD.
- gpio1830 The supply connected to APIO2_VDD.
- lcdc-supply: The supply connected to LCDC_VDD.
- sdcard-supply: The supply connected to SDMMC0_VDD.
- wifi-supply: The supply connected to APIO3_VDD. Also known as SDIO0.
Possible supplies for rk3368:
- audio-supply: The supply connected to APIO3_VDD.
- dvp-supply: The supply connected to DVPIO_VDD.
- flash0-supply: The supply connected to FLASH0_VDD. Typically for eMMC
- gpio30-supply: The supply connected to APIO1_VDD.
- gpio1830 The supply connected to APIO4_VDD.
- sdcard-supply: The supply connected to SDMMC0_VDD.
- wifi-supply: The supply connected to APIO2_VDD. Also known as SDIO0.
Possible supplies for rk3368 pmu-domains:
- pmu-supply: The supply connected to PMUIO_VDD.
- vop-supply: The supply connected to LCDC_VDD.
Possible supplies for rk3399:
- bt656-supply: The supply connected to APIO2_VDD.
- audio-supply: The supply connected to APIO5_VDD.
- sdmmc-supply: The supply connected to SDMMC0_VDD.
- gpio1830 The supply connected to APIO4_VDD.
Possible supplies for rk3399 pmu-domains:
- pmu1830-supply:The supply connected to PMUIO2_VDD.
Example:
io-domains {
compatible = "rockchip,rk3288-io-voltage-domain";
rockchip,grf = <&grf>;
audio-supply = <&vcc18_codec>;
bb-supply = <&vcc33_io>;
dvp-supply = <&vcc_18>;
flash0-supply = <&vcc18_flashio>;
gpio1830-supply = <&vcc33_io>;
gpio30-supply = <&vcc33_pmuio>;
lcdc-supply = <&vcc33_lcd>;
sdcard-supply = <&vccio_sd>;
wifi-supply = <&vcc18_wl>;
};

View File

@ -0,0 +1,360 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/rockchip-io-domain.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Rockchip SRAM for IO Voltage Domains
maintainers:
- Heiko Stuebner <heiko@sntech.de>
description: |
IO domain voltages on some Rockchip SoCs are variable but need to be
kept in sync between the regulators and the SoC using a special
register.
A specific example using rk3288
If the regulator hooked up to a pin like SDMMC0_VDD is 3.3V then
bit 7 of GRF_IO_VSEL needs to be 0. If the regulator hooked up to
that same pin is 1.8V then bit 7 of GRF_IO_VSEL needs to be 1.
Said another way, this driver simply handles keeping bits in the SoCs
General Register File (GRF) in sync with the actual value of a voltage
hooked up to the pins.
Note that this driver specifically does not include
any logic for deciding what voltage we should set regulators to
any logic for deciding whether regulators (or internal SoC blocks)
should have power or not have power
If there were some other software that had the smarts of making
decisions about regulators, it would work in conjunction with this
driver. When that other software adjusted a regulators voltage then
this driver would handle telling the SoC about it. A good example is
vqmmc for SD. In that case the dw_mmc driver simply is told about a
regulator. It changes the regulator between 3.3V and 1.8V at the
right time. This driver notices the change and makes sure that the
SoC is on the same page.
You specify supplies using the standard regulator bindings by including
a phandle the relevant regulator. All specified supplies must be able
to report their voltage. The IO Voltage Domain for any non-specified
supplies will be not be touched.
properties:
compatible:
enum:
- rockchip,px30-io-voltage-domain
- rockchip,px30-pmu-io-voltage-domain
- rockchip,rk3188-io-voltage-domain
- rockchip,rk3228-io-voltage-domain
- rockchip,rk3288-io-voltage-domain
- rockchip,rk3328-io-voltage-domain
- rockchip,rk3368-io-voltage-domain
- rockchip,rk3368-pmu-io-voltage-domain
- rockchip,rk3399-io-voltage-domain
- rockchip,rk3399-pmu-io-voltage-domain
- rockchip,rk3568-pmu-io-voltage-domain
- rockchip,rv1108-io-voltage-domain
- rockchip,rv1108-pmu-io-voltage-domain
required:
- compatible
unevaluatedProperties: false
allOf:
- $ref: "#/$defs/px30"
- $ref: "#/$defs/px30-pmu"
- $ref: "#/$defs/rk3188"
- $ref: "#/$defs/rk3228"
- $ref: "#/$defs/rk3288"
- $ref: "#/$defs/rk3328"
- $ref: "#/$defs/rk3368"
- $ref: "#/$defs/rk3368-pmu"
- $ref: "#/$defs/rk3399"
- $ref: "#/$defs/rk3399-pmu"
- $ref: "#/$defs/rk3568-pmu"
- $ref: "#/$defs/rv1108"
- $ref: "#/$defs/rv1108-pmu"
$defs:
px30:
if:
properties:
compatible:
contains:
const: rockchip,px30-io-voltage-domain
then:
properties:
vccio1-supply:
description: The supply connected to VCCIO1.
vccio2-supply:
description: The supply connected to VCCIO2.
vccio3-supply:
description: The supply connected to VCCIO3.
vccio4-supply:
description: The supply connected to VCCIO4.
vccio5-supply:
description: The supply connected to VCCIO5.
vccio6-supply:
description: The supply connected to VCCIO6.
vccio-oscgpi-supply:
description: The supply connected to VCCIO_OSCGPI.
px30-pmu:
if:
properties:
compatible:
contains:
const: rockchip,px30-pmu-io-voltage-domain
then:
properties:
pmuio1-supply:
description: The supply connected to PMUIO1.
pmuio2-supply:
description: The supply connected to PMUIO2.
rk3188:
if:
properties:
compatible:
contains:
const: rockchip,rk3188-io-voltage-domain
then:
properties:
ap0-supply:
description: The supply connected to AP0_VCC.
ap1-supply:
description: The supply connected to AP1_VCC.
cif-supply:
description: The supply connected to CIF_VCC.
flash-supply:
description: The supply connected to FLASH_VCC.
lcdc0-supply:
description: The supply connected to LCD0_VCC.
lcdc1-supply:
description: The supply connected to LCD1_VCC.
vccio0-supply:
description: The supply connected to VCCIO0.
vccio1-supply:
description: The supply connected to VCCIO1. Also labeled as VCCIO2.
rk3228:
if:
properties:
compatible:
contains:
const: rockchip,rk3228-io-voltage-domain
then:
properties:
vccio1-supply:
description: The supply connected to VCCIO1.
vccio2-supply:
description: The supply connected to VCCIO2.
vccio3-supply:
description: The supply connected to VCCIO3.
vccio4-supply:
description: The supply connected to VCCIO4.
rk3288:
if:
properties:
compatible:
contains:
const: rockchip,rk3288-io-voltage-domain
then:
properties:
audio-supply:
description: The supply connected to APIO4_VDD.
bb-supply:
description: The supply connected to APIO5_VDD.
dvp-supply:
description: The supply connected to DVPIO_VDD.
flash0-supply:
description: The supply connected to FLASH0_VDD. Typically for eMMC.
flash1-supply:
description: The supply connected to FLASH1_VDD. Also known as SDIO1.
gpio30-supply:
description: The supply connected to APIO1_VDD.
gpio1830-supply:
description: The supply connected to APIO2_VDD.
lcdc-supply:
description: The supply connected to LCDC_VDD.
sdcard-supply:
description: The supply connected to SDMMC0_VDD.
wifi-supply:
description: The supply connected to APIO3_VDD. Also known as SDIO0.
rk3328:
if:
properties:
compatible:
contains:
const: rockchip,rk3328-io-voltage-domain
then:
properties:
vccio1-supply:
description: The supply connected to VCCIO1.
vccio2-supply:
description: The supply connected to VCCIO2.
vccio3-supply:
description: The supply connected to VCCIO3.
vccio4-supply:
description: The supply connected to VCCIO4.
vccio5-supply:
description: The supply connected to VCCIO5.
vccio6-supply:
description: The supply connected to VCCIO6.
pmuio-supply:
description: The supply connected to VCCIO_PMU.
rk3368:
if:
properties:
compatible:
contains:
const: rockchip,rk3368-io-voltage-domain
then:
properties:
audio-supply:
description: The supply connected to APIO3_VDD.
dvp-supply:
description: The supply connected to DVPIO_VDD.
flash0-supply:
description: The supply connected to FLASH0_VDD. Typically for eMMC.
gpio30-supply:
description: The supply connected to APIO1_VDD.
gpio1830-supply:
description: The supply connected to APIO4_VDD.
sdcard-supply:
description: The supply connected to SDMMC0_VDD.
wifi-supply:
description: The supply connected to APIO2_VDD. Also known as SDIO0.
rk3368-pmu:
if:
properties:
compatible:
contains:
const: rockchip,rk3368-pmu-io-voltage-domain
then:
properties:
pmu-supply:
description: The supply connected to PMUIO_VDD.
vop-supply:
description: The supply connected to LCDC_VDD.
rk3399:
if:
properties:
compatible:
contains:
const: rockchip,rk3399-io-voltage-domain
then:
properties:
audio-supply:
description: The supply connected to APIO5_VDD.
bt656-supply:
description: The supply connected to APIO2_VDD.
gpio1830-supply:
description: The supply connected to APIO4_VDD.
sdmmc-supply:
description: The supply connected to SDMMC0_VDD.
rk3399-pmu:
if:
properties:
compatible:
contains:
const: rockchip,rk3399-pmu-io-voltage-domain
then:
properties:
pmu1830-supply:
description: The supply connected to PMUIO2_VDD.
rk3568-pmu:
if:
properties:
compatible:
contains:
const: rockchip,rk3568-pmu-io-voltage-domain
then:
properties:
pmuio1-supply:
description: The supply connected to PMUIO1.
pmuio2-supply:
description: The supply connected to PMUIO2.
vccio1-supply:
description: The supply connected to VCCIO1.
vccio2-supply:
description: The supply connected to VCCIO2.
vccio3-supply:
description: The supply connected to VCCIO3.
vccio4-supply:
description: The supply connected to VCCIO4.
vccio5-supply:
description: The supply connected to VCCIO5.
vccio6-supply:
description: The supply connected to VCCIO6.
vccio7-supply:
description: The supply connected to VCCIO7.
rv1108:
if:
properties:
compatible:
contains:
const: rockchip,rv1108-io-voltage-domain
then:
properties:
vccio1-supply:
description: The supply connected to APIO1_VDD.
vccio2-supply:
description: The supply connected to APIO2_VDD.
vccio3-supply:
description: The supply connected to APIO3_VDD.
vccio5-supply:
description: The supply connected to APIO5_VDD.
vccio6-supply:
description: The supply connected to APIO6_VDD.
rv1108-pmu:
if:
properties:
compatible:
contains:
const: rockchip,rv1108-pmu-io-voltage-domain
then:
properties:
pmu-supply:
description: The supply connected to PMUIO_VDD.
examples:
- |
io-domains {
compatible = "rockchip,rk3288-io-voltage-domain";
audio-supply = <&vcc18_codec>;
bb-supply = <&vcc33_io>;
dvp-supply = <&vcc_18>;
flash0-supply = <&vcc18_flashio>;
gpio1830-supply = <&vcc33_io>;
gpio30-supply = <&vcc33_pmuio>;
lcdc-supply = <&vcc33_lcd>;
sdcard-supply = <&vccio_sd>;
wifi-supply = <&vcc18_wl>;
};

View File

@ -21,6 +21,11 @@ properties:
- const: "qcom,sc7180-aoss-cc"
- const: "qcom,sdm845-aoss-cc"
- description: on SC7280 SoCs the following compatibles must be specified
items:
- const: "qcom,sc7280-aoss-cc"
- const: "qcom,sdm845-aoss-cc"
- description: on SDM845 SoCs the following compatibles must be specified
items:
- const: "qcom,sdm845-aoss-cc"

View File

@ -21,6 +21,10 @@ properties:
- const: "qcom,sc7180-pdc-global"
- const: "qcom,sdm845-pdc-global"
- description: on SC7280 SoCs the following compatibles must be specified
items:
- const: "qcom,sc7280-pdc-global"
- description: on SDM845 SoCs the following compatibles must be specified
items:
- const: "qcom,sdm845-pdc-global"

View File

@ -0,0 +1,65 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/reset/renesas,rzg2l-usbphy-ctrl.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas RZ/G2L USBPHY Control
maintainers:
- Biju Das <biju.das.jz@bp.renesas.com>
description:
The RZ/G2L USBPHY Control mainly controls reset and power down of the
USB/PHY.
properties:
compatible:
items:
- enum:
- renesas,r9a07g044-usbphy-ctrl # RZ/G2{L,LC}
- const: renesas,rzg2l-usbphy-ctrl
reg:
maxItems: 1
clocks:
maxItems: 1
resets:
maxItems: 1
power-domains:
maxItems: 1
'#reset-cells':
const: 1
description: |
The phandle's argument in the reset specifier is the PHY reset associated
with the USB port.
0 = Port 1 Phy reset
1 = Port 2 Phy reset
required:
- compatible
- reg
- clocks
- resets
- power-domains
- '#reset-cells'
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/r9a07g044-cpg.h>
phyrst: usbphy-ctrl@11c40000 {
compatible = "renesas,r9a07g044-usbphy-ctrl",
"renesas,rzg2l-usbphy-ctrl";
reg = <0x11c40000 0x10000>;
clocks = <&cpg CPG_MOD R9A07G044_USB_PCLK>;
resets = <&cpg R9A07G044_USB_PRESETN>;
power-domains = <&cpg>;
#reset-cells = <1>;
};

View File

@ -0,0 +1,88 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/reset/socionext,uniphier-glue-reset.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Socionext UniPhier peripheral core reset in glue layer
description: |
Some peripheral core reset belongs to its own glue layer. Before using
this core reset, it is necessary to control the clocks and resets to
enable this layer. These clocks and resets should be described in each
property.
maintainers:
- Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
properties:
compatible:
enum:
- socionext,uniphier-pro4-usb3-reset
- socionext,uniphier-pro5-usb3-reset
- socionext,uniphier-pxs2-usb3-reset
- socionext,uniphier-ld20-usb3-reset
- socionext,uniphier-pxs3-usb3-reset
- socionext,uniphier-pro4-ahci-reset
- socionext,uniphier-pxs2-ahci-reset
- socionext,uniphier-pxs3-ahci-reset
reg:
maxItems: 1
"#reset-cells":
const: 1
clocks:
minItems: 1
maxItems: 2
clock-names:
oneOf:
- items: # for Pro4, Pro5
- const: gio
- const: link
- items: # for others
- const: link
resets:
minItems: 1
maxItems: 2
reset-names:
oneOf:
- items: # for Pro4, Pro5
- const: gio
- const: link
- items: # for others
- const: link
additionalProperties: false
required:
- compatible
- reg
- "#reset-cells"
- clocks
- clock-names
- resets
- reset-names
examples:
- |
usb-glue@65b00000 {
compatible = "simple-mfd";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0x65b00000 0x400>;
usb_rst: reset@0 {
compatible = "socionext,uniphier-ld20-usb3-reset";
reg = <0x0 0x4>;
#reset-cells = <1>;
clock-names = "link";
clocks = <&sys_clk 14>;
reset-names = "link";
resets = <&sys_rst 14>;
};
};

View File

@ -1,61 +0,0 @@
UniPhier glue reset controller
Peripheral core reset in glue layer
-----------------------------------
Some peripheral core reset belongs to its own glue layer. Before using
this core reset, it is necessary to control the clocks and resets to enable
this layer. These clocks and resets should be described in each property.
Required properties:
- compatible: Should be
"socionext,uniphier-pro4-usb3-reset" - for Pro4 SoC USB3
"socionext,uniphier-pro5-usb3-reset" - for Pro5 SoC USB3
"socionext,uniphier-pxs2-usb3-reset" - for PXs2 SoC USB3
"socionext,uniphier-ld20-usb3-reset" - for LD20 SoC USB3
"socionext,uniphier-pxs3-usb3-reset" - for PXs3 SoC USB3
"socionext,uniphier-pro4-ahci-reset" - for Pro4 SoC AHCI
"socionext,uniphier-pxs2-ahci-reset" - for PXs2 SoC AHCI
"socionext,uniphier-pxs3-ahci-reset" - for PXs3 SoC AHCI
- #reset-cells: Should be 1.
- reg: Specifies offset and length of the register set for the device.
- clocks: A list of phandles to the clock gate for the glue layer.
According to the clock-names, appropriate clocks are required.
- clock-names: Should contain
"gio", "link" - for Pro4 and Pro5 SoCs
"link" - for others
- resets: A list of phandles to the reset control for the glue layer.
According to the reset-names, appropriate resets are required.
- reset-names: Should contain
"gio", "link" - for Pro4 and Pro5 SoCs
"link" - for others
Example:
usb-glue@65b00000 {
compatible = "socionext,uniphier-ld20-dwc3-glue",
"simple-mfd";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0x65b00000 0x400>;
usb_rst: reset@0 {
compatible = "socionext,uniphier-ld20-usb3-reset";
reg = <0x0 0x4>;
#reset-cells = <1>;
clock-names = "link";
clocks = <&sys_clk 14>;
reset-names = "link";
resets = <&sys_rst 14>;
};
regulator {
...
};
phy {
...
};
...
};

View File

@ -1,87 +0,0 @@
Qualcomm Always-On Subsystem side channel binding
This binding describes the hardware component responsible for side channel
requests to the always-on subsystem (AOSS), used for certain power management
requests that is not handled by the standard RPMh interface. Each client in the
SoC has it's own block of message RAM and IRQ for communication with the AOSS.
The protocol used to communicate in the message RAM is known as Qualcomm
Messaging Protocol (QMP)
The AOSS side channel exposes control over a set of resources, used to control
a set of debug related clocks and to affect the low power state of resources
related to the secondary subsystems. These resources are exposed as a set of
power-domains.
- compatible:
Usage: required
Value type: <string>
Definition: must be one of:
"qcom,sc7180-aoss-qmp"
"qcom,sc7280-aoss-qmp"
"qcom,sdm845-aoss-qmp"
"qcom,sm8150-aoss-qmp"
"qcom,sm8250-aoss-qmp"
"qcom,sm8350-aoss-qmp"
- reg:
Usage: required
Value type: <prop-encoded-array>
Definition: the base address and size of the message RAM for this
client's communication with the AOSS
- interrupts:
Usage: required
Value type: <prop-encoded-array>
Definition: should specify the AOSS message IRQ for this client
- mboxes:
Usage: required
Value type: <prop-encoded-array>
Definition: reference to the mailbox representing the outgoing doorbell
in APCS for this client, as described in mailbox/mailbox.txt
- #clock-cells:
Usage: optional
Value type: <u32>
Definition: must be 0
The single clock represents the QDSS clock.
- #power-domain-cells:
Usage: optional
Value type: <u32>
Definition: must be 1
The provided power-domains are:
CDSP state (0), LPASS state (1), modem state (2), SLPI
state (3), SPSS state (4) and Venus state (5).
= SUBNODES
The AOSS side channel also provides the controls for three cooling devices,
these are expressed as subnodes of the QMP node. The name of the node is used
to identify the resource and must therefor be "cx", "mx" or "ebi".
- #cooling-cells:
Usage: optional
Value type: <u32>
Definition: must be 2
= EXAMPLE
The following example represents the AOSS side-channel message RAM and the
mechanism exposing the power-domains, as found in SDM845.
aoss_qmp: qmp@c300000 {
compatible = "qcom,sdm845-aoss-qmp";
reg = <0x0c300000 0x100000>;
interrupts = <GIC_SPI 389 IRQ_TYPE_EDGE_RISING>;
mboxes = <&apss_shared 0>;
#power-domain-cells = <1>;
cx_cdev: cx {
#cooling-cells = <2>;
};
mx_cdev: mx {
#cooling-cells = <2>;
};
};

View File

@ -0,0 +1,114 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/soc/qcom/qcom,aoss-qmp.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Always-On Subsystem side channel binding
maintainers:
- Bjorn Andersson <bjorn.andersson@linaro.org>
description:
This binding describes the hardware component responsible for side channel
requests to the always-on subsystem (AOSS), used for certain power management
requests that is not handled by the standard RPMh interface. Each client in the
SoC has it's own block of message RAM and IRQ for communication with the AOSS.
The protocol used to communicate in the message RAM is known as Qualcomm
Messaging Protocol (QMP)
The AOSS side channel exposes control over a set of resources, used to control
a set of debug related clocks and to affect the low power state of resources
related to the secondary subsystems. These resources are exposed as a set of
power-domains.
properties:
compatible:
items:
- enum:
- qcom,sc7180-aoss-qmp
- qcom,sc7280-aoss-qmp
- qcom,sc8180x-aoss-qmp
- qcom,sdm845-aoss-qmp
- qcom,sm8150-aoss-qmp
- qcom,sm8250-aoss-qmp
- qcom,sm8350-aoss-qmp
- const: qcom,aoss-qmp
reg:
maxItems: 1
description:
The base address and size of the message RAM for this client's
communication with the AOSS
interrupts:
maxItems: 1
description:
Should specify the AOSS message IRQ for this client
mboxes:
maxItems: 1
description:
Reference to the mailbox representing the outgoing doorbell in APCS for
this client, as described in mailbox/mailbox.txt
"#clock-cells":
const: 0
description:
The single clock represents the QDSS clock.
"#power-domain-cells":
const: 1
description: |
The provided power-domains are:
CDSP state (0), LPASS state (1), modem state (2), SLPI
state (3), SPSS state (4) and Venus state (5).
required:
- compatible
- reg
- interrupts
- mboxes
- "#clock-cells"
additionalProperties: false
patternProperties:
"^(cx|mx|ebi)$":
type: object
description:
The AOSS side channel also provides the controls for three cooling devices,
these are expressed as subnodes of the QMP node. The name of the node is
used to identify the resource and must therefor be "cx", "mx" or "ebi".
properties:
"#cooling-cells":
const: 2
required:
- "#cooling-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
aoss_qmp: qmp@c300000 {
compatible = "qcom,sdm845-aoss-qmp", "qcom,aoss-qmp";
reg = <0x0c300000 0x100000>;
interrupts = <GIC_SPI 389 IRQ_TYPE_EDGE_RISING>;
mboxes = <&apss_shared 0>;
#clock-cells = <0>;
#power-domain-cells = <1>;
cx_cdev: cx {
#cooling-cells = <2>;
};
mx_cdev: mx {
#cooling-cells = <2>;
};
};
...

View File

@ -51,6 +51,9 @@ properties:
interconnect-names:
const: qup-core
iommus:
maxItems: 1
required:
- compatible
- reg

View File

@ -39,6 +39,7 @@ properties:
- qcom,rpm-msm8996
- qcom,rpm-msm8998
- qcom,rpm-sdm660
- qcom,rpm-sm6115
- qcom,rpm-sm6125
- qcom,rpm-qcs404

View File

@ -15,7 +15,6 @@ properties:
- items:
- enum:
- rockchip,rk3288-sgrf
- rockchip,rv1108-pmugrf
- rockchip,rv1108-usbgrf
- const: syscon
- items:
@ -41,6 +40,7 @@ properties:
- rockchip,rk3568-grf
- rockchip,rk3568-pmugrf
- rockchip,rv1108-grf
- rockchip,rv1108-pmugrf
- const: syscon
- const: simple-mfd
@ -198,21 +198,28 @@ allOf:
compatible:
contains:
enum:
- rockchip,px30-pmugrf
- rockchip,px30-grf
- rockchip,px30-pmugrf
- rockchip,rk3188-grf
- rockchip,rk3228-grf
- rockchip,rk3288-grf
- rockchip,rk3328-grf
- rockchip,rk3368-pmugrf
- rockchip,rk3368-grf
- rockchip,rk3399-pmugrf
- rockchip,rk3368-pmugrf
- rockchip,rk3399-grf
- rockchip,rk3399-pmugrf
- rockchip,rk3568-pmugrf
- rockchip,rv1108-grf
- rockchip,rv1108-pmugrf
then:
properties:
io-domains:
description:
Documentation/devicetree/bindings/power/rockchip-io-domain.txt
type: object
$ref: "/schemas/power/rockchip-io-domain.yaml#"
unevaluatedProperties: false
examples:
- |

View File

@ -68,6 +68,7 @@ properties:
- ti,k2g-pruss # for 66AK2G SoC family
- ti,am654-icssg # for K3 AM65x SoC family
- ti,j721e-icssg # for K3 J721E SoC family
- ti,am642-icssg # for K3 AM64x SoC family
reg:
maxItems: 1
@ -84,6 +85,8 @@ properties:
dma-ranges:
maxItems: 1
dma-coherent: true
power-domains:
description: |
This property is as per sci-pm-domain.txt.
@ -231,8 +234,8 @@ patternProperties:
description: |
Industrial Ethernet Peripheral to manage/generate Industrial Ethernet
functions such as time stamping. Each PRUSS has either 1 IEP (on AM335x,
AM437x, AM57xx & 66AK2G SoCs) or 2 IEPs (on K3 AM65x & J721E SoCs ). IEP
is used for creating PTP clocks and generating PPS signals.
AM437x, AM57xx & 66AK2G SoCs) or 2 IEPs (on K3 AM65x, J721E & AM64x SoCs).
IEP is used for creating PTP clocks and generating PPS signals.
type: object
@ -323,17 +326,29 @@ additionalProperties: false
# - interrupt-controller
# - pru
if:
properties:
compatible:
contains:
enum:
- ti,k2g-pruss
- ti,am654-icssg
- ti,j721e-icssg
then:
required:
- power-domains
allOf:
- if:
properties:
compatible:
contains:
enum:
- ti,k2g-pruss
- ti,am654-icssg
- ti,j721e-icssg
- ti,am642-icssg
then:
required:
- power-domains
- if:
properties:
compatible:
contains:
enum:
- ti,k2g-pruss
then:
required:
- dma-coherent
examples:
- |

View File

@ -1501,7 +1501,7 @@ M: Miquel Raynal <miquel.raynal@bootlin.com@bootlin.com>
M: Naga Sureshkumar Relli <nagasure@xilinx.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: Documentation/devicetree/bindings/mtd/arm,pl353-smc.yaml
F: Documentation/devicetree/bindings/memory-controllers/arm,pl353-smc.yaml
F: drivers/memory/pl353-smc.c
ARM PRIMECELL CLCD PL110 DRIVER
@ -2023,10 +2023,12 @@ M: Krzysztof Halasa <khalasa@piap.pl>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: Documentation/devicetree/bindings/arm/intel-ixp4xx.yaml
F: Documentation/devicetree/bindings/bus/intel,ixp4xx-expansion-bus-controller.yaml
F: Documentation/devicetree/bindings/gpio/intel,ixp4xx-gpio.txt
F: Documentation/devicetree/bindings/interrupt-controller/intel,ixp4xx-interrupt.yaml
F: Documentation/devicetree/bindings/timer/intel,ixp4xx-timer.yaml
F: arch/arm/mach-ixp4xx/
F: drivers/bus/intel-ixp4xx-eb.c
F: drivers/clocksource/timer-ixp4xx.c
F: drivers/crypto/ixp4xx_crypto.c
F: drivers/gpio/gpio-ixp4xx.c
@ -18077,6 +18079,7 @@ F: drivers/regulator/scmi-regulator.c
F: drivers/reset/reset-scmi.c
F: include/linux/sc[mp]i_protocol.h
F: include/trace/events/scmi.h
F: include/uapi/linux/virtio_scmi.h
SYSTEM RESET/SHUTDOWN DRIVERS
M: Sebastian Reichel <sre@kernel.org>

View File

@ -177,7 +177,7 @@
clocks = <&clks IMX6Q_CLK_ECSPI5>,
<&clks IMX6Q_CLK_ECSPI5>;
clock-names = "ipg", "per";
dmas = <&sdma 11 8 1>, <&sdma 12 8 2>;
dmas = <&sdma 11 7 1>, <&sdma 12 7 2>;
dma-names = "rx", "tx";
status = "disabled";
};

View File

@ -334,7 +334,7 @@
clocks = <&clks IMX6QDL_CLK_ECSPI1>,
<&clks IMX6QDL_CLK_ECSPI1>;
clock-names = "ipg", "per";
dmas = <&sdma 3 8 1>, <&sdma 4 8 2>;
dmas = <&sdma 3 7 1>, <&sdma 4 7 2>;
dma-names = "rx", "tx";
status = "disabled";
};
@ -348,7 +348,7 @@
clocks = <&clks IMX6QDL_CLK_ECSPI2>,
<&clks IMX6QDL_CLK_ECSPI2>;
clock-names = "ipg", "per";
dmas = <&sdma 5 8 1>, <&sdma 6 8 2>;
dmas = <&sdma 5 7 1>, <&sdma 6 7 2>;
dma-names = "rx", "tx";
status = "disabled";
};
@ -362,7 +362,7 @@
clocks = <&clks IMX6QDL_CLK_ECSPI3>,
<&clks IMX6QDL_CLK_ECSPI3>;
clock-names = "ipg", "per";
dmas = <&sdma 7 8 1>, <&sdma 8 8 2>;
dmas = <&sdma 7 7 1>, <&sdma 8 7 2>;
dma-names = "rx", "tx";
status = "disabled";
};
@ -376,7 +376,7 @@
clocks = <&clks IMX6QDL_CLK_ECSPI4>,
<&clks IMX6QDL_CLK_ECSPI4>;
clock-names = "ipg", "per";
dmas = <&sdma 9 8 1>, <&sdma 10 8 2>;
dmas = <&sdma 9 7 1>, <&sdma 10 7 2>;
dma-names = "rx", "tx";
status = "disabled";
};

View File

@ -26,7 +26,6 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/omap-gpmc.h>
#include <trace/events/power.h>
@ -81,8 +80,6 @@ static void omap3_core_save_context(void)
/* Save the Interrupt controller context */
omap_intc_save_context();
/* Save the GPMC context */
omap3_gpmc_save_context();
/* Save the system control module context, padconf already save above*/
omap3_control_save_context();
}
@ -91,8 +88,6 @@ static void omap3_core_restore_context(void)
{
/* Restore the control module context, padconf restored by h/w */
omap3_control_restore_context();
/* Restore the GPMC context */
omap3_gpmc_restore_context();
/* Restore the interrupt controller context */
omap_intc_restore_context();
}

View File

@ -403,7 +403,7 @@ static const struct platform_suspend_ops tegra_suspend_ops = {
.enter = tegra_suspend_enter,
};
void __init tegra_init_suspend(void)
void tegra_pm_init_suspend(void)
{
enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode();

View File

@ -25,10 +25,4 @@ void tegra30_sleep_core_init(void);
extern void (*tegra_tear_down_cpu)(void);
#ifdef CONFIG_PM_SLEEP
void tegra_init_suspend(void);
#else
static inline void tegra_init_suspend(void) {}
#endif
#endif /* _MACH_TEGRA_PM_H_ */

View File

@ -84,8 +84,6 @@ static void __init tegra_dt_init(void)
static void __init tegra_dt_init_late(void)
{
tegra_init_suspend();
if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) &&
of_machine_is_compatible("compal,paz00"))
tegra_paz00_wifikill_init();

View File

@ -13,45 +13,134 @@
*/
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/libata.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/platform_data/pata_ixp4xx_cf.h>
#include <linux/regmap.h>
#include <scsi/scsi_host.h>
#define DRV_NAME "pata_ixp4xx_cf"
#define DRV_VERSION "0.2"
#define DRV_VERSION "1.0"
static int ixp4xx_set_mode(struct ata_link *link, struct ata_device **error)
struct ixp4xx_pata {
struct ata_host *host;
struct regmap *rmap;
u32 cmd_csreg;
void __iomem *cmd;
void __iomem *ctl;
};
#define IXP4XX_EXP_TIMING_STRIDE 0x04
/* The timings for the chipselect is in bits 29..16 */
#define IXP4XX_EXP_T1_T5_MASK GENMASK(29, 16)
#define IXP4XX_EXP_PIO_0_8 0x0a470000
#define IXP4XX_EXP_PIO_1_8 0x06430000
#define IXP4XX_EXP_PIO_2_8 0x02410000
#define IXP4XX_EXP_PIO_3_8 0x00820000
#define IXP4XX_EXP_PIO_4_8 0x00400000
#define IXP4XX_EXP_PIO_0_16 0x29640000
#define IXP4XX_EXP_PIO_1_16 0x05030000
#define IXP4XX_EXP_PIO_2_16 0x00b20000
#define IXP4XX_EXP_PIO_3_16 0x00820000
#define IXP4XX_EXP_PIO_4_16 0x00400000
#define IXP4XX_EXP_BW_MASK (BIT(6)|BIT(0))
#define IXP4XX_EXP_BYTE_RD16 BIT(6) /* Byte reads on half-word devices */
#define IXP4XX_EXP_BYTE_EN BIT(0) /* Use 8bit data bus if set */
static void ixp4xx_set_8bit_timing(struct ixp4xx_pata *ixpp, u8 pio_mode)
{
struct ata_device *dev;
ata_for_each_dev(dev, link, ENABLED) {
ata_dev_info(dev, "configured for PIO0\n");
dev->pio_mode = XFER_PIO_0;
dev->xfer_mode = XFER_PIO_0;
dev->xfer_shift = ATA_SHIFT_PIO;
dev->flags |= ATA_DFLAG_PIO;
switch (pio_mode) {
case XFER_PIO_0:
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_0_8);
break;
case XFER_PIO_1:
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_1_8);
break;
case XFER_PIO_2:
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_2_8);
break;
case XFER_PIO_3:
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_3_8);
break;
case XFER_PIO_4:
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_4_8);
break;
default:
break;
}
return 0;
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
IXP4XX_EXP_BW_MASK, IXP4XX_EXP_BYTE_RD16|IXP4XX_EXP_BYTE_EN);
}
static void ixp4xx_set_16bit_timing(struct ixp4xx_pata *ixpp, u8 pio_mode)
{
switch (pio_mode){
case XFER_PIO_0:
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_0_16);
break;
case XFER_PIO_1:
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_1_16);
break;
case XFER_PIO_2:
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_2_16);
break;
case XFER_PIO_3:
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_3_16);
break;
case XFER_PIO_4:
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_4_16);
break;
default:
break;
}
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
IXP4XX_EXP_BW_MASK, IXP4XX_EXP_BYTE_RD16);
}
/* This sets up the timing on the chipselect CMD accordingly */
static void ixp4xx_set_piomode(struct ata_port *ap, struct ata_device *adev)
{
struct ixp4xx_pata *ixpp = ap->host->private_data;
ata_dev_printk(adev, KERN_INFO, "configured for PIO%d 8bit\n",
adev->pio_mode - XFER_PIO_0);
ixp4xx_set_8bit_timing(ixpp, adev->pio_mode);
}
static unsigned int ixp4xx_mmio_data_xfer(struct ata_queued_cmd *qc,
unsigned char *buf, unsigned int buflen, int rw)
unsigned char *buf, unsigned int buflen, int rw)
{
unsigned int i;
unsigned int words = buflen >> 1;
u16 *buf16 = (u16 *) buf;
struct ata_device *adev = qc->dev;
struct ata_port *ap = qc->dev->link->ap;
void __iomem *mmio = ap->ioaddr.data_addr;
struct ixp4xx_pata_data *data = dev_get_platdata(ap->host->dev);
struct ixp4xx_pata *ixpp = ap->host->private_data;
unsigned long flags;
ata_dev_printk(adev, KERN_DEBUG, "%s %d bytes\n", (rw == READ) ? "READ" : "WRITE",
buflen);
spin_lock_irqsave(ap->lock, flags);
/* set the expansion bus in 16bit mode and restore
* 8 bit mode after the transaction.
*/
*data->cs0_cfg &= ~(0x01);
udelay(100);
ixp4xx_set_16bit_timing(ixpp, adev->pio_mode);
udelay(5);
/* Transfer multiple of 2 bytes */
if (rw == READ)
@ -76,8 +165,10 @@ static unsigned int ixp4xx_mmio_data_xfer(struct ata_queued_cmd *qc,
words++;
}
udelay(100);
*data->cs0_cfg |= 0x01;
ixp4xx_set_8bit_timing(ixpp, adev->pio_mode);
udelay(5);
spin_unlock_irqrestore(ap->lock, flags);
return words << 1;
}
@ -90,79 +181,98 @@ static struct ata_port_operations ixp4xx_port_ops = {
.inherits = &ata_sff_port_ops,
.sff_data_xfer = ixp4xx_mmio_data_xfer,
.cable_detect = ata_cable_40wire,
.set_mode = ixp4xx_set_mode,
.set_piomode = ixp4xx_set_piomode,
};
static struct ata_port_info ixp4xx_port_info = {
.flags = ATA_FLAG_NO_ATAPI,
.pio_mask = ATA_PIO4,
.port_ops = &ixp4xx_port_ops,
};
static void ixp4xx_setup_port(struct ata_port *ap,
struct ixp4xx_pata_data *data,
unsigned long raw_cs0, unsigned long raw_cs1)
struct ixp4xx_pata *ixpp,
unsigned long raw_cmd, unsigned long raw_ctl)
{
struct ata_ioports *ioaddr = &ap->ioaddr;
unsigned long raw_cmd = raw_cs0;
unsigned long raw_ctl = raw_cs1 + 0x06;
ioaddr->cmd_addr = data->cs0;
ioaddr->altstatus_addr = data->cs1 + 0x06;
ioaddr->ctl_addr = data->cs1 + 0x06;
raw_ctl += 0x06;
ioaddr->cmd_addr = ixpp->cmd;
ioaddr->altstatus_addr = ixpp->ctl + 0x06;
ioaddr->ctl_addr = ixpp->ctl + 0x06;
ata_sff_std_ports(ioaddr);
#ifndef __ARMEB__
if (!IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) {
/* adjust the addresses to handle the address swizzling of the
* ixp4xx in little endian mode.
*/
/* adjust the addresses to handle the address swizzling of the
* ixp4xx in little endian mode.
*/
*(unsigned long *)&ioaddr->data_addr ^= 0x02;
*(unsigned long *)&ioaddr->cmd_addr ^= 0x03;
*(unsigned long *)&ioaddr->altstatus_addr ^= 0x03;
*(unsigned long *)&ioaddr->ctl_addr ^= 0x03;
*(unsigned long *)&ioaddr->error_addr ^= 0x03;
*(unsigned long *)&ioaddr->feature_addr ^= 0x03;
*(unsigned long *)&ioaddr->nsect_addr ^= 0x03;
*(unsigned long *)&ioaddr->lbal_addr ^= 0x03;
*(unsigned long *)&ioaddr->lbam_addr ^= 0x03;
*(unsigned long *)&ioaddr->lbah_addr ^= 0x03;
*(unsigned long *)&ioaddr->device_addr ^= 0x03;
*(unsigned long *)&ioaddr->status_addr ^= 0x03;
*(unsigned long *)&ioaddr->command_addr ^= 0x03;
*(unsigned long *)&ioaddr->data_addr ^= 0x02;
*(unsigned long *)&ioaddr->cmd_addr ^= 0x03;
*(unsigned long *)&ioaddr->altstatus_addr ^= 0x03;
*(unsigned long *)&ioaddr->ctl_addr ^= 0x03;
*(unsigned long *)&ioaddr->error_addr ^= 0x03;
*(unsigned long *)&ioaddr->feature_addr ^= 0x03;
*(unsigned long *)&ioaddr->nsect_addr ^= 0x03;
*(unsigned long *)&ioaddr->lbal_addr ^= 0x03;
*(unsigned long *)&ioaddr->lbam_addr ^= 0x03;
*(unsigned long *)&ioaddr->lbah_addr ^= 0x03;
*(unsigned long *)&ioaddr->device_addr ^= 0x03;
*(unsigned long *)&ioaddr->status_addr ^= 0x03;
*(unsigned long *)&ioaddr->command_addr ^= 0x03;
raw_cmd ^= 0x03;
raw_ctl ^= 0x03;
#endif
raw_cmd ^= 0x03;
raw_ctl ^= 0x03;
}
ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", raw_cmd, raw_ctl);
}
static int ixp4xx_pata_probe(struct platform_device *pdev)
{
struct resource *cs0, *cs1;
struct ata_host *host;
struct ata_port *ap;
struct ixp4xx_pata_data *data = dev_get_platdata(&pdev->dev);
struct resource *cmd, *ctl;
struct ata_port_info pi = ixp4xx_port_info;
const struct ata_port_info *ppi[] = { &pi, NULL };
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct ixp4xx_pata *ixpp;
u32 csindex;
int ret;
int irq;
cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
cs1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
cmd = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ctl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!cs0 || !cs1)
if (!cmd || !ctl)
return -EINVAL;
/* allocate host */
host = ata_host_alloc(&pdev->dev, 1);
if (!host)
ixpp = devm_kzalloc(dev, sizeof(*ixpp), GFP_KERNEL);
if (!ixpp)
return -ENOMEM;
/* acquire resources and fill host */
ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
ixpp->rmap = syscon_node_to_regmap(np->parent);
if (IS_ERR(ixpp->rmap))
return dev_err_probe(dev, PTR_ERR(ixpp->rmap), "no regmap\n");
/* Inspect our address to figure out what chipselect the CMD is on */
ret = of_property_read_u32_index(np, "reg", 0, &csindex);
if (ret)
return dev_err_probe(dev, ret, "can't inspect CMD address\n");
dev_info(dev, "using CS%d for PIO timing configuration\n", csindex);
ixpp->cmd_csreg = csindex * IXP4XX_EXP_TIMING_STRIDE;
ixpp->host = ata_host_alloc_pinfo(dev, ppi, 1);
if (!ixpp->host)
return -ENOMEM;
ixpp->host->private_data = ixpp;
ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
if (ret)
return ret;
data->cs0 = devm_ioremap(&pdev->dev, cs0->start, 0x1000);
data->cs1 = devm_ioremap(&pdev->dev, cs1->start, 0x1000);
if (!data->cs0 || !data->cs1)
ixpp->cmd = devm_ioremap_resource(dev, cmd);
ixpp->ctl = devm_ioremap_resource(dev, ctl);
if (IS_ERR(ixpp->cmd) || IS_ERR(ixpp->ctl))
return -ENOMEM;
irq = platform_get_irq(pdev, 0);
@ -173,27 +283,23 @@ static int ixp4xx_pata_probe(struct platform_device *pdev)
else
return -EINVAL;
/* Setup expansion bus chip selects */
*data->cs0_cfg = data->cs0_bits;
*data->cs1_cfg = data->cs1_bits;
/* Just one port to set up */
ixp4xx_setup_port(ixpp->host->ports[0], ixpp, cmd->start, ctl->start);
ap = host->ports[0];
ata_print_version_once(dev, DRV_VERSION);
ap->ops = &ixp4xx_port_ops;
ap->pio_mask = ATA_PIO4;
ap->flags |= ATA_FLAG_NO_ATAPI;
ixp4xx_setup_port(ap, data, cs0->start, cs1->start);
ata_print_version_once(&pdev->dev, DRV_VERSION);
/* activate host */
return ata_host_activate(host, irq, ata_sff_interrupt, 0, &ixp4xx_sht);
return ata_host_activate(ixpp->host, irq, ata_sff_interrupt, 0, &ixp4xx_sht);
}
static const struct of_device_id ixp4xx_pata_of_match[] = {
{ .compatible = "intel,ixp4xx-compact-flash", },
{ },
};
static struct platform_driver ixp4xx_pata_platform_driver = {
.driver = {
.name = DRV_NAME,
.of_match_table = ixp4xx_pata_of_match,
},
.probe = ixp4xx_pata_probe,
.remove = ata_platform_remove_one,

View File

@ -95,6 +95,17 @@ config IMX_WEIM
The WEIM(Wireless External Interface Module) works like a bus.
You can attach many different devices on it, such as NOR, onenand.
config INTEL_IXP4XX_EB
bool "Intel IXP4xx expansion bus interface driver"
depends on HAS_IOMEM
depends on ARCH_IXP4XX || COMPILE_TEST
default ARCH_IXP4XX
select MFD_SYSCON
help
Driver for the Intel IXP4xx expansion bus interface. The driver is
needed to set up various chip select configuration parameters before
devices on the expansion bus can be discovered.
config MIPS_CDMM
bool "MIPS Common Device Memory Map (CDMM) Driver"
depends on CPU_MIPSR2 || CPU_MIPSR5

View File

@ -16,6 +16,7 @@ obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
obj-$(CONFIG_BT1_APB) += bt1-apb.o
obj-$(CONFIG_BT1_AXI) += bt1-axi.o
obj-$(CONFIG_IMX_WEIM) += imx-weim.o
obj-$(CONFIG_INTEL_IXP4XX_EB) += intel-ixp4xx-eb.o
obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o
obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o

View File

@ -0,0 +1,429 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel IXP4xx Expansion Bus Controller
* Copyright (C) 2021 Linaro Ltd.
*
* Author: Linus Walleij <linus.walleij@linaro.org>
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/log2.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#define IXP4XX_EXP_NUM_CS 8
#define IXP4XX_EXP_TIMING_CS0 0x00
#define IXP4XX_EXP_TIMING_CS1 0x04
#define IXP4XX_EXP_TIMING_CS2 0x08
#define IXP4XX_EXP_TIMING_CS3 0x0c
#define IXP4XX_EXP_TIMING_CS4 0x10
#define IXP4XX_EXP_TIMING_CS5 0x14
#define IXP4XX_EXP_TIMING_CS6 0x18
#define IXP4XX_EXP_TIMING_CS7 0x1c
/* Bits inside each CS timing register */
#define IXP4XX_EXP_TIMING_STRIDE 0x04
#define IXP4XX_EXP_CS_EN BIT(31)
#define IXP456_EXP_PAR_EN BIT(30) /* Only on IXP45x and IXP46x */
#define IXP4XX_EXP_T1_MASK GENMASK(28, 27)
#define IXP4XX_EXP_T1_SHIFT 28
#define IXP4XX_EXP_T2_MASK GENMASK(27, 26)
#define IXP4XX_EXP_T2_SHIFT 26
#define IXP4XX_EXP_T3_MASK GENMASK(25, 22)
#define IXP4XX_EXP_T3_SHIFT 22
#define IXP4XX_EXP_T4_MASK GENMASK(21, 20)
#define IXP4XX_EXP_T4_SHIFT 20
#define IXP4XX_EXP_T5_MASK GENMASK(19, 16)
#define IXP4XX_EXP_T5_SHIFT 16
#define IXP4XX_EXP_CYC_TYPE_MASK GENMASK(15, 14)
#define IXP4XX_EXP_CYC_TYPE_SHIFT 14
#define IXP4XX_EXP_SIZE_MASK GENMASK(13, 10)
#define IXP4XX_EXP_SIZE_SHIFT 10
#define IXP4XX_EXP_CNFG_0 BIT(9) /* Always zero */
#define IXP43X_EXP_SYNC_INTEL BIT(8) /* Only on IXP43x */
#define IXP43X_EXP_EXP_CHIP BIT(7) /* Only on IXP43x */
#define IXP4XX_EXP_BYTE_RD16 BIT(6)
#define IXP4XX_EXP_HRDY_POL BIT(5) /* Only on IXP42x */
#define IXP4XX_EXP_MUX_EN BIT(4)
#define IXP4XX_EXP_SPLT_EN BIT(3)
#define IXP4XX_EXP_WORD BIT(2) /* Always zero */
#define IXP4XX_EXP_WR_EN BIT(1)
#define IXP4XX_EXP_BYTE_EN BIT(0)
#define IXP42X_RESERVED (BIT(30)|IXP4XX_EXP_CNFG_0|BIT(8)|BIT(7)|IXP4XX_EXP_WORD)
#define IXP43X_RESERVED (BIT(30)|IXP4XX_EXP_CNFG_0|BIT(5)|IXP4XX_EXP_WORD)
#define IXP4XX_EXP_CNFG0 0x20
#define IXP4XX_EXP_CNFG0_MEM_MAP BIT(31)
#define IXP4XX_EXP_CNFG1 0x24
#define IXP4XX_EXP_BOOT_BASE 0x00000000
#define IXP4XX_EXP_NORMAL_BASE 0x50000000
#define IXP4XX_EXP_STRIDE 0x01000000
/* Fuses on the IXP43x */
#define IXP43X_EXP_UNIT_FUSE_RESET 0x28
#define IXP43x_EXP_FUSE_SPEED_MASK GENMASK(23, 22)
/* Number of device tree values in "reg" */
#define IXP4XX_OF_REG_SIZE 3
struct ixp4xx_eb {
struct device *dev;
struct regmap *rmap;
u32 bus_base;
bool is_42x;
bool is_43x;
};
struct ixp4xx_exp_tim_prop {
const char *prop;
u32 max;
u32 mask;
u16 shift;
};
static const struct ixp4xx_exp_tim_prop ixp4xx_exp_tim_props[] = {
{
.prop = "intel,ixp4xx-eb-t1",
.max = 3,
.mask = IXP4XX_EXP_T1_MASK,
.shift = IXP4XX_EXP_T1_SHIFT,
},
{
.prop = "intel,ixp4xx-eb-t2",
.max = 3,
.mask = IXP4XX_EXP_T2_MASK,
.shift = IXP4XX_EXP_T2_SHIFT,
},
{
.prop = "intel,ixp4xx-eb-t3",
.max = 15,
.mask = IXP4XX_EXP_T3_MASK,
.shift = IXP4XX_EXP_T3_SHIFT,
},
{
.prop = "intel,ixp4xx-eb-t4",
.max = 3,
.mask = IXP4XX_EXP_T4_MASK,
.shift = IXP4XX_EXP_T4_SHIFT,
},
{
.prop = "intel,ixp4xx-eb-t5",
.max = 15,
.mask = IXP4XX_EXP_T5_MASK,
.shift = IXP4XX_EXP_T5_SHIFT,
},
{
.prop = "intel,ixp4xx-eb-byte-access-on-halfword",
.max = 1,
.mask = IXP4XX_EXP_BYTE_RD16,
},
{
.prop = "intel,ixp4xx-eb-hpi-hrdy-pol-high",
.max = 1,
.mask = IXP4XX_EXP_HRDY_POL,
},
{
.prop = "intel,ixp4xx-eb-mux-address-and-data",
.max = 1,
.mask = IXP4XX_EXP_MUX_EN,
},
{
.prop = "intel,ixp4xx-eb-ahb-split-transfers",
.max = 1,
.mask = IXP4XX_EXP_SPLT_EN,
},
{
.prop = "intel,ixp4xx-eb-write-enable",
.max = 1,
.mask = IXP4XX_EXP_WR_EN,
},
{
.prop = "intel,ixp4xx-eb-byte-access",
.max = 1,
.mask = IXP4XX_EXP_BYTE_EN,
},
};
static void ixp4xx_exp_setup_chipselect(struct ixp4xx_eb *eb,
struct device_node *np,
u32 cs_index,
u32 cs_size)
{
u32 cs_cfg;
u32 val;
u32 cur_cssize;
u32 cs_order;
int ret;
int i;
if (eb->is_42x && (cs_index > 7)) {
dev_err(eb->dev,
"invalid chipselect %u, we only support 0-7\n",
cs_index);
return;
}
if (eb->is_43x && (cs_index > 3)) {
dev_err(eb->dev,
"invalid chipselect %u, we only support 0-3\n",
cs_index);
return;
}
/* Several chip selects can be joined into one device */
if (cs_size > IXP4XX_EXP_STRIDE)
cur_cssize = IXP4XX_EXP_STRIDE;
else
cur_cssize = cs_size;
/*
* The following will read/modify/write the configuration for one
* chipselect, attempting to leave the boot defaults in place unless
* something is explicitly defined.
*/
regmap_read(eb->rmap, IXP4XX_EXP_TIMING_CS0 +
IXP4XX_EXP_TIMING_STRIDE * cs_index, &cs_cfg);
dev_info(eb->dev, "CS%d at %#08x, size %#08x, config before: %#08x\n",
cs_index, eb->bus_base + IXP4XX_EXP_STRIDE * cs_index,
cur_cssize, cs_cfg);
/* Size set-up first align to 2^9 .. 2^24 */
cur_cssize = roundup_pow_of_two(cur_cssize);
if (cur_cssize < 512)
cur_cssize = 512;
cs_order = ilog2(cur_cssize);
if (cs_order < 9 || cs_order > 24) {
dev_err(eb->dev, "illegal size order %d\n", cs_order);
return;
}
dev_dbg(eb->dev, "CS%d size order: %d\n", cs_index, cs_order);
cs_cfg &= ~(IXP4XX_EXP_SIZE_MASK);
cs_cfg |= ((cs_order - 9) << IXP4XX_EXP_SIZE_SHIFT);
for (i = 0; i < ARRAY_SIZE(ixp4xx_exp_tim_props); i++) {
const struct ixp4xx_exp_tim_prop *ip = &ixp4xx_exp_tim_props[i];
/* All are regular u32 values */
ret = of_property_read_u32(np, ip->prop, &val);
if (ret)
continue;
/* Handle bools (single bits) first */
if (ip->max == 1) {
if (val)
cs_cfg |= ip->mask;
else
cs_cfg &= ~ip->mask;
dev_info(eb->dev, "CS%d %s %s\n", cs_index,
val ? "enabled" : "disabled",
ip->prop);
continue;
}
if (val > ip->max) {
dev_err(eb->dev,
"CS%d too high value for %s: %u, capped at %u\n",
cs_index, ip->prop, val, ip->max);
val = ip->max;
}
/* This assumes max value fills all the assigned bits (and it does) */
cs_cfg &= ~ip->mask;
cs_cfg |= (val << ip->shift);
dev_info(eb->dev, "CS%d set %s to %u\n", cs_index, ip->prop, val);
}
ret = of_property_read_u32(np, "intel,ixp4xx-eb-cycle-type", &val);
if (!ret) {
if (val > 3) {
dev_err(eb->dev, "illegal cycle type %d\n", val);
return;
}
dev_info(eb->dev, "CS%d set cycle type %d\n", cs_index, val);
cs_cfg &= ~IXP4XX_EXP_CYC_TYPE_MASK;
cs_cfg |= val << IXP4XX_EXP_CYC_TYPE_SHIFT;
}
if (eb->is_42x)
cs_cfg &= ~IXP42X_RESERVED;
if (eb->is_43x) {
cs_cfg &= ~IXP43X_RESERVED;
/*
* This bit for Intel strata flash is currently unused, but let's
* report it if we find one.
*/
if (cs_cfg & IXP43X_EXP_SYNC_INTEL)
dev_info(eb->dev, "claims to be Intel strata flash\n");
}
cs_cfg |= IXP4XX_EXP_CS_EN;
regmap_write(eb->rmap,
IXP4XX_EXP_TIMING_CS0 + IXP4XX_EXP_TIMING_STRIDE * cs_index,
cs_cfg);
dev_info(eb->dev, "CS%d wrote %#08x into CS config\n", cs_index, cs_cfg);
/*
* If several chip selects are joined together into one big
* device area, we call ourselves recursively for each successive
* chip select. For a 32MB flash chip this results in two calls
* for example.
*/
if (cs_size > IXP4XX_EXP_STRIDE)
ixp4xx_exp_setup_chipselect(eb, np,
cs_index + 1,
cs_size - IXP4XX_EXP_STRIDE);
}
static void ixp4xx_exp_setup_child(struct ixp4xx_eb *eb,
struct device_node *np)
{
u32 cs_sizes[IXP4XX_EXP_NUM_CS];
int num_regs;
u32 csindex;
u32 cssize;
int ret;
int i;
num_regs = of_property_count_elems_of_size(np, "reg", IXP4XX_OF_REG_SIZE);
if (num_regs <= 0)
return;
dev_dbg(eb->dev, "child %s has %d register sets\n",
of_node_full_name(np), num_regs);
for (csindex = 0; csindex < IXP4XX_EXP_NUM_CS; csindex++)
cs_sizes[csindex] = 0;
for (i = 0; i < num_regs; i++) {
u32 rbase, rsize;
ret = of_property_read_u32_index(np, "reg",
i * IXP4XX_OF_REG_SIZE, &csindex);
if (ret)
break;
ret = of_property_read_u32_index(np, "reg",
i * IXP4XX_OF_REG_SIZE + 1, &rbase);
if (ret)
break;
ret = of_property_read_u32_index(np, "reg",
i * IXP4XX_OF_REG_SIZE + 2, &rsize);
if (ret)
break;
if (csindex >= IXP4XX_EXP_NUM_CS) {
dev_err(eb->dev, "illegal CS %d\n", csindex);
continue;
}
/*
* The memory window always starts from CS base so we need to add
* the start and size to get to the size from the start of the CS
* base. For example if CS0 is at 0x50000000 and the reg is
* <0 0xe40000 0x40000> the size is e80000.
*
* Roof this if we have several regs setting the same CS.
*/
cssize = rbase + rsize;
dev_dbg(eb->dev, "CS%d size %#08x\n", csindex, cssize);
if (cs_sizes[csindex] < cssize)
cs_sizes[csindex] = cssize;
}
for (csindex = 0; csindex < IXP4XX_EXP_NUM_CS; csindex++) {
cssize = cs_sizes[csindex];
if (!cssize)
continue;
/* Just this one, so set it up and return */
ixp4xx_exp_setup_chipselect(eb, np, csindex, cssize);
}
}
static int ixp4xx_exp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct ixp4xx_eb *eb;
struct device_node *child;
bool have_children = false;
u32 val;
int ret;
eb = devm_kzalloc(dev, sizeof(*eb), GFP_KERNEL);
if (!eb)
return -ENOMEM;
eb->dev = dev;
eb->is_42x = of_device_is_compatible(np, "intel,ixp42x-expansion-bus-controller");
eb->is_43x = of_device_is_compatible(np, "intel,ixp43x-expansion-bus-controller");
eb->rmap = syscon_node_to_regmap(np);
if (IS_ERR(eb->rmap))
return dev_err_probe(dev, PTR_ERR(eb->rmap), "no regmap\n");
/* We check that the regmap work only on first read */
ret = regmap_read(eb->rmap, IXP4XX_EXP_CNFG0, &val);
if (ret)
return dev_err_probe(dev, ret, "cannot read regmap\n");
if (val & IXP4XX_EXP_CNFG0_MEM_MAP)
eb->bus_base = IXP4XX_EXP_BOOT_BASE;
else
eb->bus_base = IXP4XX_EXP_NORMAL_BASE;
dev_info(dev, "expansion bus at %08x\n", eb->bus_base);
if (eb->is_43x) {
/* Check some fuses */
regmap_read(eb->rmap, IXP43X_EXP_UNIT_FUSE_RESET, &val);
switch (FIELD_GET(IXP43x_EXP_FUSE_SPEED_MASK, val)) {
case 0:
dev_info(dev, "IXP43x at 533 MHz\n");
break;
case 1:
dev_info(dev, "IXP43x at 400 MHz\n");
break;
case 2:
dev_info(dev, "IXP43x at 667 MHz\n");
break;
default:
dev_info(dev, "IXP43x unknown speed\n");
break;
}
}
/* Walk over the child nodes and see what chipselects we use */
for_each_available_child_of_node(np, child) {
ixp4xx_exp_setup_child(eb, child);
/* We have at least one child */
have_children = true;
}
if (have_children)
return of_platform_default_populate(np, NULL, dev);
return 0;
}
static const struct of_device_id ixp4xx_exp_of_match[] = {
{ .compatible = "intel,ixp42x-expansion-bus-controller", },
{ .compatible = "intel,ixp43x-expansion-bus-controller", },
{ .compatible = "intel,ixp45x-expansion-bus-controller", },
{ .compatible = "intel,ixp46x-expansion-bus-controller", },
{ }
};
static struct platform_driver ixp4xx_exp_driver = {
.probe = ixp4xx_exp_probe,
.driver = {
.name = "intel-extbus",
.of_match_table = ixp4xx_exp_of_match,
},
};
module_platform_driver(ixp4xx_exp_driver);
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
MODULE_DESCRIPTION("Intel IXP4xx external bus driver");
MODULE_LICENSE("GPL");

View File

@ -855,7 +855,7 @@ static int sysc_check_registers(struct sysc *ddata)
}
/**
* syc_ioremap - ioremap register space for the interconnect target module
* sysc_ioremap - ioremap register space for the interconnect target module
* @ddata: device driver data
*
* Note that the interconnect target module registers can be anywhere
@ -1446,10 +1446,6 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_OPT_CLKS_IN_RESET),
SYSC_QUIRK("sham", 0, 0x100, 0x110, 0x114, 0x40000c03, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("smartreflex", 0, -ENODEV, 0x24, -ENODEV, 0x00000000, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("smartreflex", 0, -ENODEV, 0x38, -ENODEV, 0x00000000, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000046, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff,
@ -1501,6 +1497,8 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_MODULE_QUIRK_SGX),
SYSC_QUIRK("lcdc", 0, 0, 0x54, -ENODEV, 0x4f201000, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
SYSC_QUIRK("mcasp", 0, 0, 0x4, -ENODEV, 0x44306302, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE),
SYSC_QUIRK("rtc", 0, 0x74, 0x78, -ENODEV, 0x4eb01908, 0xffff00f0,
SYSC_MODULE_QUIRK_RTC_UNLOCK),
SYSC_QUIRK("tptc", 0, 0, 0x10, -ENODEV, 0x40006c00, 0xffffefff,
@ -1557,7 +1555,6 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK("hsi", 0, 0, 0x10, 0x14, 0x50043101, 0xffffffff, 0),
SYSC_QUIRK("iss", 0, 0, 0x10, -ENODEV, 0x40000101, 0xffffffff, 0),
SYSC_QUIRK("keypad", 0x4a31c000, 0, 0x10, 0x14, 0x00000020, 0xffffffff, 0),
SYSC_QUIRK("mcasp", 0, 0, 0x4, -ENODEV, 0x44306302, 0xffffffff, 0),
SYSC_QUIRK("mcasp", 0, 0, 0x4, -ENODEV, 0x44307b02, 0xffffffff, 0),
SYSC_QUIRK("mcbsp", 0, -ENODEV, 0x8c, -ENODEV, 0, 0, 0),
SYSC_QUIRK("mcspi", 0, 0, 0x10, -ENODEV, 0x40300a0b, 0xffff00ff, 0),
@ -1585,6 +1582,8 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK("sdma", 0, 0, 0x2c, 0x28, 0x00010900, 0xffffffff, 0),
SYSC_QUIRK("slimbus", 0, 0, 0x10, -ENODEV, 0x40000902, 0xffffffff, 0),
SYSC_QUIRK("slimbus", 0, 0, 0x10, -ENODEV, 0x40002903, 0xffffffff, 0),
SYSC_QUIRK("smartreflex", 0, -ENODEV, 0x24, -ENODEV, 0x00000000, 0xffffffff, 0),
SYSC_QUIRK("smartreflex", 0, -ENODEV, 0x38, -ENODEV, 0x00000000, 0xffffffff, 0),
SYSC_QUIRK("spinlock", 0, 0, 0x10, -ENODEV, 0x50020000, 0xffffffff, 0),
SYSC_QUIRK("rng", 0, 0x1fe0, 0x1fe4, -ENODEV, 0x00000020, 0xffffffff, 0),
SYSC_QUIRK("timer", 0, 0, 0x10, 0x14, 0x00000013, 0xffffffff, 0),
@ -3115,9 +3114,8 @@ static int sysc_probe(struct platform_device *pdev)
goto unprepare;
pm_runtime_enable(ddata->dev);
error = pm_runtime_get_sync(ddata->dev);
error = pm_runtime_resume_and_get(ddata->dev);
if (error < 0) {
pm_runtime_put_noidle(ddata->dev);
pm_runtime_disable(ddata->dev);
goto unprepare;
}
@ -3175,9 +3173,8 @@ static int sysc_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&ddata->idle_work);
error = pm_runtime_get_sync(ddata->dev);
error = pm_runtime_resume_and_get(ddata->dev);
if (error < 0) {
pm_runtime_put_noidle(ddata->dev);
pm_runtime_disable(ddata->dev);
goto unprepare;
}

View File

@ -18,6 +18,7 @@
#include <linux/delay.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
/* Goes away with OF conversion */
#include <linux/platform_data/timer-ixp4xx.h>
@ -29,9 +30,6 @@
#define IXP4XX_OSRT1_OFFSET 0x08 /* Timer 1 Reload */
#define IXP4XX_OST2_OFFSET 0x0C /* Timer 2 Timestamp */
#define IXP4XX_OSRT2_OFFSET 0x10 /* Timer 2 Reload */
#define IXP4XX_OSWT_OFFSET 0x14 /* Watchdog Timer */
#define IXP4XX_OSWE_OFFSET 0x18 /* Watchdog Enable */
#define IXP4XX_OSWK_OFFSET 0x1C /* Watchdog Key */
#define IXP4XX_OSST_OFFSET 0x20 /* Timer Status */
/*
@ -45,17 +43,10 @@
#define IXP4XX_OSST_TIMER_1_PEND 0x00000001
#define IXP4XX_OSST_TIMER_2_PEND 0x00000002
#define IXP4XX_OSST_TIMER_TS_PEND 0x00000004
#define IXP4XX_OSST_TIMER_WDOG_PEND 0x00000008
#define IXP4XX_OSST_TIMER_WARM_RESET 0x00000010
#define IXP4XX_WDT_KEY 0x0000482E
#define IXP4XX_WDT_RESET_ENABLE 0x00000001
#define IXP4XX_WDT_IRQ_ENABLE 0x00000002
#define IXP4XX_WDT_COUNT_ENABLE 0x00000004
/* Remaining registers are for the watchdog and defined in the watchdog driver */
struct ixp4xx_timer {
void __iomem *base;
unsigned int tick_rate;
u32 latch;
struct clock_event_device clkevt;
#ifdef CONFIG_ARM
@ -181,7 +172,6 @@ static __init int ixp4xx_timer_register(void __iomem *base,
if (!tmr)
return -ENOMEM;
tmr->base = base;
tmr->tick_rate = timer_freq;
/*
* The timer register doesn't allow to specify the two least
@ -239,6 +229,40 @@ static __init int ixp4xx_timer_register(void __iomem *base,
return 0;
}
static struct platform_device ixp4xx_watchdog_device = {
.name = "ixp4xx-watchdog",
.id = -1,
};
/*
* This probe gets called after the timer is already up and running. The main
* function on this platform is to spawn the watchdog device as a child.
*/
static int ixp4xx_timer_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
/* Pass the base address as platform data and nothing else */
ixp4xx_watchdog_device.dev.platform_data = local_ixp4xx_timer->base;
ixp4xx_watchdog_device.dev.parent = dev;
return platform_device_register(&ixp4xx_watchdog_device);
}
static const struct of_device_id ixp4xx_timer_dt_id[] = {
{ .compatible = "intel,ixp4xx-timer", },
{ /* sentinel */ },
};
static struct platform_driver ixp4xx_timer_driver = {
.probe = ixp4xx_timer_probe,
.driver = {
.name = "ixp4xx-timer",
.of_match_table = ixp4xx_timer_dt_id,
.suppress_bind_attrs = true,
},
};
builtin_platform_driver(ixp4xx_timer_driver);
/**
* ixp4xx_timer_setup() - Timer setup function to be called from boardfiles
* @timerbase: physical base of timer block

View File

@ -198,12 +198,12 @@ struct sdma_script_start_addrs {
s32 per_2_firi_addr;
s32 mcu_2_firi_addr;
s32 uart_2_per_addr;
s32 uart_2_mcu_addr;
s32 uart_2_mcu_ram_addr;
s32 per_2_app_addr;
s32 mcu_2_app_addr;
s32 per_2_per_addr;
s32 uartsh_2_per_addr;
s32 uartsh_2_mcu_addr;
s32 uartsh_2_mcu_ram_addr;
s32 per_2_shp_addr;
s32 mcu_2_shp_addr;
s32 ata_2_mcu_addr;
@ -230,6 +230,10 @@ struct sdma_script_start_addrs {
s32 zcanfd_2_mcu_addr;
s32 zqspi_2_mcu_addr;
s32 mcu_2_ecspi_addr;
s32 mcu_2_sai_addr;
s32 sai_2_mcu_addr;
s32 uart_2_mcu_addr;
s32 uartsh_2_mcu_addr;
/* End of v3 array */
s32 mcu_2_zqspi_addr;
/* End of v4 array */
@ -433,9 +437,10 @@ struct sdma_channel {
unsigned long watermark_level;
u32 shp_addr, per_addr;
enum dma_status status;
bool context_loaded;
struct imx_dma_data data;
struct work_struct terminate_worker;
struct list_head terminated;
bool is_ram_script;
};
#define IMX_DMA_SG_LOOP BIT(0)
@ -476,6 +481,13 @@ struct sdma_driver_data {
int num_events;
struct sdma_script_start_addrs *script_addrs;
bool check_ratio;
/*
* ecspi ERR009165 fixed should be done in sdma script
* and it has been fixed in soc from i.mx6ul.
* please get more information from the below link:
* https://www.nxp.com/docs/en/errata/IMX6DQCE.pdf
*/
bool ecspi_fixed;
};
struct sdma_engine {
@ -499,6 +511,7 @@ struct sdma_engine {
struct sdma_buffer_descriptor *bd0;
/* clock ratio for AHB:SDMA core. 1:1 is 1, 2:1 is 0*/
bool clk_ratio;
bool fw_loaded;
};
static int sdma_config_write(struct dma_chan *chan,
@ -595,6 +608,13 @@ static struct sdma_driver_data sdma_imx6q = {
.script_addrs = &sdma_script_imx6q,
};
static struct sdma_driver_data sdma_imx6ul = {
.chnenbl0 = SDMA_CHNENBL0_IMX35,
.num_events = 48,
.script_addrs = &sdma_script_imx6q,
.ecspi_fixed = true,
};
static struct sdma_script_start_addrs sdma_script_imx7d = {
.ap_2_ap_addr = 644,
.uart_2_mcu_addr = 819,
@ -628,6 +648,7 @@ static const struct of_device_id sdma_dt_ids[] = {
{ .compatible = "fsl,imx31-sdma", .data = &sdma_imx31, },
{ .compatible = "fsl,imx25-sdma", .data = &sdma_imx25, },
{ .compatible = "fsl,imx7d-sdma", .data = &sdma_imx7d, },
{ .compatible = "fsl,imx6ul-sdma", .data = &sdma_imx6ul, },
{ .compatible = "fsl,imx8mq-sdma", .data = &sdma_imx8mq, },
{ /* sentinel */ }
};
@ -919,6 +940,7 @@ static void sdma_get_pc(struct sdma_channel *sdmac,
sdmac->pc_to_device = 0;
sdmac->device_to_device = 0;
sdmac->pc_to_pc = 0;
sdmac->is_ram_script = false;
switch (peripheral_type) {
case IMX_DMATYPE_MEMORY:
@ -945,6 +967,17 @@ static void sdma_get_pc(struct sdma_channel *sdmac,
emi_2_per = sdma->script_addrs->mcu_2_ata_addr;
break;
case IMX_DMATYPE_CSPI:
per_2_emi = sdma->script_addrs->app_2_mcu_addr;
/* Use rom script mcu_2_app if ERR009165 fixed */
if (sdmac->sdma->drvdata->ecspi_fixed) {
emi_2_per = sdma->script_addrs->mcu_2_app_addr;
} else {
emi_2_per = sdma->script_addrs->mcu_2_ecspi_addr;
sdmac->is_ram_script = true;
}
break;
case IMX_DMATYPE_EXT:
case IMX_DMATYPE_SSI:
case IMX_DMATYPE_SAI:
@ -954,6 +987,7 @@ static void sdma_get_pc(struct sdma_channel *sdmac,
case IMX_DMATYPE_SSI_DUAL:
per_2_emi = sdma->script_addrs->ssish_2_mcu_addr;
emi_2_per = sdma->script_addrs->mcu_2_ssish_addr;
sdmac->is_ram_script = true;
break;
case IMX_DMATYPE_SSI_SP:
case IMX_DMATYPE_MMC:
@ -968,6 +1002,7 @@ static void sdma_get_pc(struct sdma_channel *sdmac,
per_2_emi = sdma->script_addrs->asrc_2_mcu_addr;
emi_2_per = sdma->script_addrs->asrc_2_mcu_addr;
per_2_per = sdma->script_addrs->per_2_per_addr;
sdmac->is_ram_script = true;
break;
case IMX_DMATYPE_ASRC_SP:
per_2_emi = sdma->script_addrs->shp_2_mcu_addr;
@ -1008,9 +1043,6 @@ static int sdma_load_context(struct sdma_channel *sdmac)
int ret;
unsigned long flags;
if (sdmac->context_loaded)
return 0;
if (sdmac->direction == DMA_DEV_TO_MEM)
load_address = sdmac->pc_from_device;
else if (sdmac->direction == DMA_DEV_TO_DEV)
@ -1053,8 +1085,6 @@ static int sdma_load_context(struct sdma_channel *sdmac)
spin_unlock_irqrestore(&sdma->channel_0_lock, flags);
sdmac->context_loaded = true;
return ret;
}
@ -1078,9 +1108,6 @@ static void sdma_channel_terminate_work(struct work_struct *work)
{
struct sdma_channel *sdmac = container_of(work, struct sdma_channel,
terminate_worker);
unsigned long flags;
LIST_HEAD(head);
/*
* According to NXP R&D team a delay of one BD SDMA cost time
* (maximum is 1ms) should be added after disable of the channel
@ -1089,11 +1116,7 @@ static void sdma_channel_terminate_work(struct work_struct *work)
*/
usleep_range(1000, 2000);
spin_lock_irqsave(&sdmac->vc.lock, flags);
vchan_get_all_descriptors(&sdmac->vc, &head);
spin_unlock_irqrestore(&sdmac->vc.lock, flags);
vchan_dma_desc_free_list(&sdmac->vc, &head);
sdmac->context_loaded = false;
vchan_dma_desc_free_list(&sdmac->vc, &sdmac->terminated);
}
static int sdma_terminate_all(struct dma_chan *chan)
@ -1107,6 +1130,13 @@ static int sdma_terminate_all(struct dma_chan *chan)
if (sdmac->desc) {
vchan_terminate_vdesc(&sdmac->desc->vd);
/*
* move out current descriptor into terminated list so that
* it could be free in sdma_channel_terminate_work alone
* later without potential involving next descriptor raised
* up before the last descriptor terminated.
*/
vchan_get_all_descriptors(&sdmac->vc, &sdmac->terminated);
sdmac->desc = NULL;
schedule_work(&sdmac->terminate_worker);
}
@ -1168,7 +1198,6 @@ static void sdma_set_watermarklevel_for_p2p(struct sdma_channel *sdmac)
static int sdma_config_channel(struct dma_chan *chan)
{
struct sdma_channel *sdmac = to_sdma_chan(chan);
int ret;
sdma_disable_channel(chan);
@ -1208,9 +1237,7 @@ static int sdma_config_channel(struct dma_chan *chan)
sdmac->watermark_level = 0; /* FIXME: M3_BASE_ADDRESS */
}
ret = sdma_load_context(sdmac);
return ret;
return 0;
}
static int sdma_set_channel_priority(struct sdma_channel *sdmac,
@ -1361,7 +1388,6 @@ static void sdma_free_chan_resources(struct dma_chan *chan)
sdmac->event_id0 = 0;
sdmac->event_id1 = 0;
sdmac->context_loaded = false;
sdma_set_channel_priority(sdmac, 0);
@ -1374,6 +1400,11 @@ static struct sdma_desc *sdma_transfer_init(struct sdma_channel *sdmac,
{
struct sdma_desc *desc;
if (!sdmac->sdma->fw_loaded && sdmac->is_ram_script) {
dev_warn_once(sdmac->sdma->dev, "sdma firmware not ready!\n");
goto err_out;
}
desc = kzalloc((sizeof(*desc)), GFP_NOWAIT);
if (!desc)
goto err_out;
@ -1722,8 +1753,8 @@ static void sdma_issue_pending(struct dma_chan *chan)
#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 34
#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2 38
#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3 41
#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V4 42
#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3 45
#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V4 46
static void sdma_add_scripts(struct sdma_engine *sdma,
const struct sdma_script_start_addrs *addr)
@ -1747,6 +1778,19 @@ static void sdma_add_scripts(struct sdma_engine *sdma,
for (i = 0; i < sdma->script_number; i++)
if (addr_arr[i] > 0)
saddr_arr[i] = addr_arr[i];
/*
* get uart_2_mcu_addr/uartsh_2_mcu_addr rom script specially because
* they are now replaced by uart_2_mcu_ram_addr/uartsh_2_mcu_ram_addr
* to be compatible with legacy freescale/nxp sdma firmware, and they
* are located in the bottom part of sdma_script_start_addrs which are
* beyond the SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1.
*/
if (addr->uart_2_mcu_addr)
sdma->script_addrs->uart_2_mcu_addr = addr->uart_2_mcu_addr;
if (addr->uartsh_2_mcu_addr)
sdma->script_addrs->uartsh_2_mcu_addr = addr->uartsh_2_mcu_addr;
}
static void sdma_load_firmware(const struct firmware *fw, void *context)
@ -1803,6 +1847,8 @@ static void sdma_load_firmware(const struct firmware *fw, void *context)
sdma_add_scripts(sdma, addr);
sdma->fw_loaded = true;
dev_info(sdma->dev, "loaded firmware %d.%d\n",
header->version_major,
header->version_minor);
@ -2086,6 +2132,7 @@ static int sdma_probe(struct platform_device *pdev)
sdmac->channel = i;
sdmac->vc.desc_free = sdma_desc_free;
INIT_LIST_HEAD(&sdmac->terminated);
INIT_WORK(&sdmac->terminate_worker,
sdma_channel_terminate_work);
/*

View File

@ -6,39 +6,7 @@
menu "Firmware Drivers"
config ARM_SCMI_PROTOCOL
tristate "ARM System Control and Management Interface (SCMI) Message Protocol"
depends on ARM || ARM64 || COMPILE_TEST
depends on MAILBOX || HAVE_ARM_SMCCC_DISCOVERY
help
ARM System Control and Management Interface (SCMI) protocol is a
set of operating system-independent software interfaces that are
used in system management. SCMI is extensible and currently provides
interfaces for: Discovery and self-description of the interfaces
it supports, Power domain management which is the ability to place
a given device or domain into the various power-saving states that
it supports, Performance management which is the ability to control
the performance of a domain that is composed of compute engines
such as application processors and other accelerators, Clock
management which is the ability to set and inquire rates on platform
managed clocks and Sensor management which is the ability to read
sensor data, and be notified of sensor value.
This protocol library provides interface for all the client drivers
making use of the features offered by the SCMI.
config ARM_SCMI_POWER_DOMAIN
tristate "SCMI power domain driver"
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
default y
select PM_GENERIC_DOMAINS if PM
help
This enables support for the SCMI power domains which can be
enabled or disabled via the SCP firmware
This driver can also be built as a module. If so, the module
will be called scmi_pm_domain. Note this may needed early in boot
before rootfs may be available.
source "drivers/firmware/arm_scmi/Kconfig"
config ARM_SCPI_PROTOCOL
tristate "ARM System Control and Power Interface (SCPI) Message Protocol"
@ -235,7 +203,7 @@ config INTEL_STRATIX10_RSU
Say Y here if you want Intel RSU support.
config QCOM_SCM
bool
tristate "Qcom SCM driver"
depends on ARM || ARM64
depends on HAVE_ARM_SMCCC
select RESET_CONTROLLER

View File

@ -17,7 +17,8 @@ obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o
obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o
obj-$(CONFIG_QCOM_SCM) += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o
obj-$(CONFIG_QCOM_SCM) += qcom-scm.o
qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o
obj-$(CONFIG_SYSFB) += sysfb.o
obj-$(CONFIG_SYSFB_SIMPLEFB) += sysfb_simplefb.o
obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o

View File

@ -0,0 +1,95 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "ARM System Control and Management Interface Protocol"
config ARM_SCMI_PROTOCOL
tristate "ARM System Control and Management Interface (SCMI) Message Protocol"
depends on ARM || ARM64 || COMPILE_TEST
help
ARM System Control and Management Interface (SCMI) protocol is a
set of operating system-independent software interfaces that are
used in system management. SCMI is extensible and currently provides
interfaces for: Discovery and self-description of the interfaces
it supports, Power domain management which is the ability to place
a given device or domain into the various power-saving states that
it supports, Performance management which is the ability to control
the performance of a domain that is composed of compute engines
such as application processors and other accelerators, Clock
management which is the ability to set and inquire rates on platform
managed clocks and Sensor management which is the ability to read
sensor data, and be notified of sensor value.
This protocol library provides interface for all the client drivers
making use of the features offered by the SCMI.
if ARM_SCMI_PROTOCOL
config ARM_SCMI_HAVE_TRANSPORT
bool
help
This declares whether at least one SCMI transport has been configured.
Used to trigger a build bug when trying to build SCMI without any
configured transport.
config ARM_SCMI_HAVE_SHMEM
bool
help
This declares whether a shared memory based transport for SCMI is
available.
config ARM_SCMI_HAVE_MSG
bool
help
This declares whether a message passing based transport for SCMI is
available.
config ARM_SCMI_TRANSPORT_MAILBOX
bool "SCMI transport based on Mailbox"
depends on MAILBOX
select ARM_SCMI_HAVE_TRANSPORT
select ARM_SCMI_HAVE_SHMEM
default y
help
Enable mailbox based transport for SCMI.
If you want the ARM SCMI PROTOCOL stack to include support for a
transport based on mailboxes, answer Y.
config ARM_SCMI_TRANSPORT_SMC
bool "SCMI transport based on SMC"
depends on HAVE_ARM_SMCCC_DISCOVERY
select ARM_SCMI_HAVE_TRANSPORT
select ARM_SCMI_HAVE_SHMEM
default y
help
Enable SMC based transport for SCMI.
If you want the ARM SCMI PROTOCOL stack to include support for a
transport based on SMC, answer Y.
config ARM_SCMI_TRANSPORT_VIRTIO
bool "SCMI transport based on VirtIO"
depends on VIRTIO
select ARM_SCMI_HAVE_TRANSPORT
select ARM_SCMI_HAVE_MSG
help
This enables the virtio based transport for SCMI.
If you want the ARM SCMI PROTOCOL stack to include support for a
transport based on VirtIO, answer Y.
endif #ARM_SCMI_PROTOCOL
config ARM_SCMI_POWER_DOMAIN
tristate "SCMI power domain driver"
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
default y
select PM_GENERIC_DOMAINS if PM
help
This enables support for the SCMI power domains which can be
enabled or disabled via the SCP firmware
This driver can also be built as a module. If so, the module
will be called scmi_pm_domain. Note this may needed early in boot
before rootfs may be available.
endmenu

View File

@ -1,9 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
scmi-bus-y = bus.o
scmi-driver-y = driver.o notify.o
scmi-transport-y = shmem.o
scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
scmi-transport-$(CONFIG_ARM_SCMI_HAVE_SHMEM) = shmem.o
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_MAILBOX) += mailbox.o
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_SMC) += smc.o
scmi-transport-$(CONFIG_ARM_SCMI_HAVE_MSG) += msg.o
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_VIRTIO) += virtio.o
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o
scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
$(scmi-transport-y)

View File

@ -14,8 +14,12 @@
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/hashtable.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/refcount.h>
#include <linux/scmi_protocol.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <asm/unaligned.h>
@ -65,11 +69,22 @@ struct scmi_msg_resp_prot_version {
#define MSG_XTRACT_TOKEN(hdr) FIELD_GET(MSG_TOKEN_ID_MASK, (hdr))
#define MSG_TOKEN_MAX (MSG_XTRACT_TOKEN(MSG_TOKEN_ID_MASK) + 1)
/*
* Size of @pending_xfers hashtable included in @scmi_xfers_info; ideally, in
* order to minimize space and collisions, this should equal max_msg, i.e. the
* maximum number of in-flight messages on a specific platform, but such value
* is only available at runtime while kernel hashtables are statically sized:
* pick instead as a fixed static size the maximum number of entries that can
* fit the whole table into one 4k page.
*/
#define SCMI_PENDING_XFERS_HT_ORDER_SZ 9
/**
* struct scmi_msg_hdr - Message(Tx/Rx) header
*
* @id: The identifier of the message being sent
* @protocol_id: The identifier of the protocol used to send @id message
* @type: The SCMI type for this message
* @seq: The token to identify the message. When a message returns, the
* platform returns the whole message header unmodified including the
* token
@ -80,6 +95,7 @@ struct scmi_msg_resp_prot_version {
struct scmi_msg_hdr {
u8 id;
u8 protocol_id;
u8 type;
u16 seq;
u32 status;
bool poll_completion;
@ -89,13 +105,14 @@ struct scmi_msg_hdr {
* pack_scmi_header() - packs and returns 32-bit header
*
* @hdr: pointer to header containing all the information on message id,
* protocol id and sequence id.
* protocol id, sequence id and type.
*
* Return: 32-bit packed message header to be sent to the platform.
*/
static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr)
{
return FIELD_PREP(MSG_ID_MASK, hdr->id) |
FIELD_PREP(MSG_TYPE_MASK, hdr->type) |
FIELD_PREP(MSG_TOKEN_ID_MASK, hdr->seq) |
FIELD_PREP(MSG_PROTOCOL_ID_MASK, hdr->protocol_id);
}
@ -110,6 +127,7 @@ static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr)
{
hdr->id = MSG_XTRACT_ID(msg_hdr);
hdr->protocol_id = MSG_XTRACT_PROT_ID(msg_hdr);
hdr->type = MSG_XTRACT_TYPE(msg_hdr);
}
/**
@ -134,6 +152,27 @@ struct scmi_msg {
* buffer for the rx path as we use for the tx path.
* @done: command message transmit completion event
* @async_done: pointer to delayed response message received event completion
* @pending: True for xfers added to @pending_xfers hashtable
* @node: An hlist_node reference used to store this xfer, alternatively, on
* the free list @free_xfers or in the @pending_xfers hashtable
* @users: A refcount to track the active users for this xfer.
* This is meant to protect against the possibility that, when a command
* transaction times out concurrently with the reception of a valid
* response message, the xfer could be finally put on the TX path, and
* so vanish, while on the RX path scmi_rx_callback() is still
* processing it: in such a case this refcounting will ensure that, even
* though the timed-out transaction will anyway cause the command
* request to be reported as failed by time-out, the underlying xfer
* cannot be discarded and possibly reused until the last one user on
* the RX path has released it.
* @busy: An atomic flag to ensure exclusive write access to this xfer
* @state: The current state of this transfer, with states transitions deemed
* valid being:
* - SCMI_XFER_SENT_OK -> SCMI_XFER_RESP_OK [ -> SCMI_XFER_DRESP_OK ]
* - SCMI_XFER_SENT_OK -> SCMI_XFER_DRESP_OK
* (Missing synchronous response is assumed OK and ignored)
* @lock: A spinlock to protect state and busy fields.
* @priv: A pointer for transport private usage.
*/
struct scmi_xfer {
int transfer_id;
@ -142,8 +181,36 @@ struct scmi_xfer {
struct scmi_msg rx;
struct completion done;
struct completion *async_done;
bool pending;
struct hlist_node node;
refcount_t users;
#define SCMI_XFER_FREE 0
#define SCMI_XFER_BUSY 1
atomic_t busy;
#define SCMI_XFER_SENT_OK 0
#define SCMI_XFER_RESP_OK 1
#define SCMI_XFER_DRESP_OK 2
int state;
/* A lock to protect state and busy fields */
spinlock_t lock;
void *priv;
};
/*
* An helper macro to lookup an xfer from the @pending_xfers hashtable
* using the message sequence number token as a key.
*/
#define XFER_FIND(__ht, __k) \
({ \
typeof(__k) k_ = __k; \
struct scmi_xfer *xfer_ = NULL; \
\
hash_for_each_possible((__ht), xfer_, node, k_) \
if (xfer_->hdr.seq == k_) \
break; \
xfer_; \
})
struct scmi_xfer_ops;
/**
@ -283,9 +350,13 @@ struct scmi_chan_info {
/**
* struct scmi_transport_ops - Structure representing a SCMI transport ops
*
* @link_supplier: Optional callback to add link to a supplier device
* @chan_available: Callback to check if channel is available or not
* @chan_setup: Callback to allocate and setup a channel
* @chan_free: Callback to free a channel
* @get_max_msg: Optional callback to provide max_msg dynamically
* Returns the maximum number of messages for the channel type
* (tx or rx) that can be pending simultaneously in the system
* @send_message: Callback to send a message
* @mark_txdone: Callback to mark tx as done
* @fetch_response: Callback to fetch response
@ -294,10 +365,12 @@ struct scmi_chan_info {
* @poll_done: Callback to poll transfer status
*/
struct scmi_transport_ops {
int (*link_supplier)(struct device *dev);
bool (*chan_available)(struct device *dev, int idx);
int (*chan_setup)(struct scmi_chan_info *cinfo, struct device *dev,
bool tx);
int (*chan_free)(int id, void *p, void *data);
unsigned int (*get_max_msg)(struct scmi_chan_info *base_cinfo);
int (*send_message)(struct scmi_chan_info *cinfo,
struct scmi_xfer *xfer);
void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret);
@ -317,25 +390,39 @@ struct scmi_device *scmi_child_dev_find(struct device *parent,
/**
* struct scmi_desc - Description of SoC integration
*
* @transport_init: An optional function that a transport can provide to
* initialize some transport-specific setup during SCMI core
* initialization, so ahead of SCMI core probing.
* @transport_exit: An optional function that a transport can provide to
* de-initialize some transport-specific setup during SCMI core
* de-initialization, so after SCMI core removal.
* @ops: Pointer to the transport specific ops structure
* @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds)
* @max_msg: Maximum number of messages that can be pending
* simultaneously in the system
* @max_msg: Maximum number of messages for a channel type (tx or rx) that can
* be pending simultaneously in the system. May be overridden by the
* get_max_msg op.
* @max_msg_size: Maximum size of data per message that can be handled.
*/
struct scmi_desc {
int (*transport_init)(void);
void (*transport_exit)(void);
const struct scmi_transport_ops *ops;
int max_rx_timeout_ms;
int max_msg;
int max_msg_size;
};
#ifdef CONFIG_ARM_SCMI_TRANSPORT_MAILBOX
extern const struct scmi_desc scmi_mailbox_desc;
#ifdef CONFIG_HAVE_ARM_SMCCC_DISCOVERY
#endif
#ifdef CONFIG_ARM_SCMI_TRANSPORT_SMC
extern const struct scmi_desc scmi_smc_desc;
#endif
#ifdef CONFIG_ARM_SCMI_TRANSPORT_VIRTIO
extern const struct scmi_desc scmi_virtio_desc;
#endif
void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr);
void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, void *priv);
void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id);
/* shmem related declarations */
@ -352,8 +439,22 @@ void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem);
bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
struct scmi_xfer *xfer);
/* declarations for message passing transports */
struct scmi_msg_payld;
/* Maximum overhead of message w.r.t. struct scmi_desc.max_msg_size */
#define SCMI_MSG_MAX_PROT_OVERHEAD (2 * sizeof(__le32))
size_t msg_response_size(struct scmi_xfer *xfer);
size_t msg_command_size(struct scmi_xfer *xfer);
void msg_tx_prepare(struct scmi_msg_payld *msg, struct scmi_xfer *xfer);
u32 msg_read_header(struct scmi_msg_payld *msg);
void msg_fetch_response(struct scmi_msg_payld *msg, size_t len,
struct scmi_xfer *xfer);
void msg_fetch_notification(struct scmi_msg_payld *msg, size_t len,
size_t max_len, struct scmi_xfer *xfer);
void scmi_notification_instance_data_set(const struct scmi_handle *handle,
void *priv);
void *scmi_notification_instance_data_get(const struct scmi_handle *handle);
#endif /* _SCMI_COMMON_H */

View File

@ -21,6 +21,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/ktime.h>
#include <linux/hashtable.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of_address.h>
@ -67,16 +68,23 @@ struct scmi_requested_dev {
/**
* struct scmi_xfers_info - Structure to manage transfer information
*
* @xfer_block: Preallocated Message array
* @xfer_alloc_table: Bitmap table for allocated messages.
* Index of this bitmap table is also used for message
* sequence identifier.
* @xfer_lock: Protection for message allocation
* @max_msg: Maximum number of messages that can be pending
* @free_xfers: A free list for available to use xfers. It is initialized with
* a number of xfers equal to the maximum allowed in-flight
* messages.
* @pending_xfers: An hashtable, indexed by msg_hdr.seq, used to keep all the
* currently in-flight messages.
*/
struct scmi_xfers_info {
struct scmi_xfer *xfer_block;
unsigned long *xfer_alloc_table;
spinlock_t xfer_lock;
int max_msg;
struct hlist_head free_xfers;
DECLARE_HASHTABLE(pending_xfers, SCMI_PENDING_XFERS_HT_ORDER_SZ);
};
/**
@ -172,19 +180,6 @@ static inline int scmi_to_linux_errno(int errno)
return -EIO;
}
/**
* scmi_dump_header_dbg() - Helper to dump a message header.
*
* @dev: Device pointer corresponding to the SCMI entity
* @hdr: pointer to header.
*/
static inline void scmi_dump_header_dbg(struct device *dev,
struct scmi_msg_hdr *hdr)
{
dev_dbg(dev, "Message ID: %x Sequence ID: %x Protocol: %x\n",
hdr->id, hdr->seq, hdr->protocol_id);
}
void scmi_notification_instance_data_set(const struct scmi_handle *handle,
void *priv)
{
@ -204,46 +199,190 @@ void *scmi_notification_instance_data_get(const struct scmi_handle *handle)
return info->notify_priv;
}
/**
* scmi_xfer_token_set - Reserve and set new token for the xfer at hand
*
* @minfo: Pointer to Tx/Rx Message management info based on channel type
* @xfer: The xfer to act upon
*
* Pick the next unused monotonically increasing token and set it into
* xfer->hdr.seq: picking a monotonically increasing value avoids immediate
* reuse of freshly completed or timed-out xfers, thus mitigating the risk
* of incorrect association of a late and expired xfer with a live in-flight
* transaction, both happening to re-use the same token identifier.
*
* Since platform is NOT required to answer our request in-order we should
* account for a few rare but possible scenarios:
*
* - exactly 'next_token' may be NOT available so pick xfer_id >= next_token
* using find_next_zero_bit() starting from candidate next_token bit
*
* - all tokens ahead upto (MSG_TOKEN_ID_MASK - 1) are used in-flight but we
* are plenty of free tokens at start, so try a second pass using
* find_next_zero_bit() and starting from 0.
*
* X = used in-flight
*
* Normal
* ------
*
* |- xfer_id picked
* -----------+----------------------------------------------------------
* | | |X|X|X| | | | | | ... ... ... ... ... ... ... ... ... ... ...|X|X|
* ----------------------------------------------------------------------
* ^
* |- next_token
*
* Out-of-order pending at start
* -----------------------------
*
* |- xfer_id picked, last_token fixed
* -----+----------------------------------------------------------------
* |X|X| | | | |X|X| ... ... ... ... ... ... ... ... ... ... ... ...|X| |
* ----------------------------------------------------------------------
* ^
* |- next_token
*
*
* Out-of-order pending at end
* ---------------------------
*
* |- xfer_id picked, last_token fixed
* -----+----------------------------------------------------------------
* |X|X| | | | |X|X| ... ... ... ... ... ... ... ... ... ... |X|X|X||X|X|
* ----------------------------------------------------------------------
* ^
* |- next_token
*
* Context: Assumes to be called with @xfer_lock already acquired.
*
* Return: 0 on Success or error
*/
static int scmi_xfer_token_set(struct scmi_xfers_info *minfo,
struct scmi_xfer *xfer)
{
unsigned long xfer_id, next_token;
/*
* Pick a candidate monotonic token in range [0, MSG_TOKEN_MAX - 1]
* using the pre-allocated transfer_id as a base.
* Note that the global transfer_id is shared across all message types
* so there could be holes in the allocated set of monotonic sequence
* numbers, but that is going to limit the effectiveness of the
* mitigation only in very rare limit conditions.
*/
next_token = (xfer->transfer_id & (MSG_TOKEN_MAX - 1));
/* Pick the next available xfer_id >= next_token */
xfer_id = find_next_zero_bit(minfo->xfer_alloc_table,
MSG_TOKEN_MAX, next_token);
if (xfer_id == MSG_TOKEN_MAX) {
/*
* After heavily out-of-order responses, there are no free
* tokens ahead, but only at start of xfer_alloc_table so
* try again from the beginning.
*/
xfer_id = find_next_zero_bit(minfo->xfer_alloc_table,
MSG_TOKEN_MAX, 0);
/*
* Something is wrong if we got here since there can be a
* maximum number of (MSG_TOKEN_MAX - 1) in-flight messages
* but we have not found any free token [0, MSG_TOKEN_MAX - 1].
*/
if (WARN_ON_ONCE(xfer_id == MSG_TOKEN_MAX))
return -ENOMEM;
}
/* Update +/- last_token accordingly if we skipped some hole */
if (xfer_id != next_token)
atomic_add((int)(xfer_id - next_token), &transfer_last_id);
/* Set in-flight */
set_bit(xfer_id, minfo->xfer_alloc_table);
xfer->hdr.seq = (u16)xfer_id;
return 0;
}
/**
* scmi_xfer_token_clear - Release the token
*
* @minfo: Pointer to Tx/Rx Message management info based on channel type
* @xfer: The xfer to act upon
*/
static inline void scmi_xfer_token_clear(struct scmi_xfers_info *minfo,
struct scmi_xfer *xfer)
{
clear_bit(xfer->hdr.seq, minfo->xfer_alloc_table);
}
/**
* scmi_xfer_get() - Allocate one message
*
* @handle: Pointer to SCMI entity handle
* @minfo: Pointer to Tx/Rx Message management info based on channel type
* @set_pending: If true a monotonic token is picked and the xfer is added to
* the pending hash table.
*
* Helper function which is used by various message functions that are
* exposed to clients of this driver for allocating a message traffic event.
*
* This function can sleep depending on pending requests already in the system
* for the SCMI entity. Further, this also holds a spinlock to maintain
* integrity of internal data structures.
* Picks an xfer from the free list @free_xfers (if any available) and, if
* required, sets a monotonically increasing token and stores the inflight xfer
* into the @pending_xfers hashtable for later retrieval.
*
* The successfully initialized xfer is refcounted.
*
* Context: Holds @xfer_lock while manipulating @xfer_alloc_table and
* @free_xfers.
*
* Return: 0 if all went fine, else corresponding error.
*/
static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle,
struct scmi_xfers_info *minfo)
struct scmi_xfers_info *minfo,
bool set_pending)
{
u16 xfer_id;
int ret;
unsigned long flags;
struct scmi_xfer *xfer;
unsigned long flags, bit_pos;
struct scmi_info *info = handle_to_scmi_info(handle);
/* Keep the locked section as small as possible */
spin_lock_irqsave(&minfo->xfer_lock, flags);
bit_pos = find_first_zero_bit(minfo->xfer_alloc_table,
info->desc->max_msg);
if (bit_pos == info->desc->max_msg) {
if (hlist_empty(&minfo->free_xfers)) {
spin_unlock_irqrestore(&minfo->xfer_lock, flags);
return ERR_PTR(-ENOMEM);
}
set_bit(bit_pos, minfo->xfer_alloc_table);
spin_unlock_irqrestore(&minfo->xfer_lock, flags);
xfer_id = bit_pos;
/* grab an xfer from the free_list */
xfer = hlist_entry(minfo->free_xfers.first, struct scmi_xfer, node);
hlist_del_init(&xfer->node);
xfer = &minfo->xfer_block[xfer_id];
xfer->hdr.seq = xfer_id;
/*
* Allocate transfer_id early so that can be used also as base for
* monotonic sequence number generation if needed.
*/
xfer->transfer_id = atomic_inc_return(&transfer_last_id);
if (set_pending) {
/* Pick and set monotonic token */
ret = scmi_xfer_token_set(minfo, xfer);
if (!ret) {
hash_add(minfo->pending_xfers, &xfer->node,
xfer->hdr.seq);
xfer->pending = true;
} else {
dev_err(handle->dev,
"Failed to get monotonic token %d\n", ret);
hlist_add_head(&xfer->node, &minfo->free_xfers);
xfer = ERR_PTR(ret);
}
}
if (!IS_ERR(xfer)) {
refcount_set(&xfer->users, 1);
atomic_set(&xfer->busy, SCMI_XFER_FREE);
}
spin_unlock_irqrestore(&minfo->xfer_lock, flags);
return xfer;
}
@ -253,6 +392,9 @@ static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle,
* @minfo: Pointer to Tx/Rx Message management info based on channel type
* @xfer: message that was reserved by scmi_xfer_get
*
* After refcount check, possibly release an xfer, clearing the token slot,
* removing xfer from @pending_xfers and putting it back into free_xfers.
*
* This holds a spinlock to maintain integrity of internal data structures.
*/
static void
@ -260,17 +402,215 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer)
{
unsigned long flags;
/*
* Keep the locked section as small as possible
* NOTE: we might escape with smp_mb and no lock here..
* but just be conservative and symmetric.
*/
spin_lock_irqsave(&minfo->xfer_lock, flags);
clear_bit(xfer->hdr.seq, minfo->xfer_alloc_table);
if (refcount_dec_and_test(&xfer->users)) {
if (xfer->pending) {
scmi_xfer_token_clear(minfo, xfer);
hash_del(&xfer->node);
xfer->pending = false;
}
hlist_add_head(&xfer->node, &minfo->free_xfers);
}
spin_unlock_irqrestore(&minfo->xfer_lock, flags);
}
static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr)
/**
* scmi_xfer_lookup_unlocked - Helper to lookup an xfer_id
*
* @minfo: Pointer to Tx/Rx Message management info based on channel type
* @xfer_id: Token ID to lookup in @pending_xfers
*
* Refcounting is untouched.
*
* Context: Assumes to be called with @xfer_lock already acquired.
*
* Return: A valid xfer on Success or error otherwise
*/
static struct scmi_xfer *
scmi_xfer_lookup_unlocked(struct scmi_xfers_info *minfo, u16 xfer_id)
{
struct scmi_xfer *xfer = NULL;
if (test_bit(xfer_id, minfo->xfer_alloc_table))
xfer = XFER_FIND(minfo->pending_xfers, xfer_id);
return xfer ?: ERR_PTR(-EINVAL);
}
/**
* scmi_msg_response_validate - Validate message type against state of related
* xfer
*
* @cinfo: A reference to the channel descriptor.
* @msg_type: Message type to check
* @xfer: A reference to the xfer to validate against @msg_type
*
* This function checks if @msg_type is congruent with the current state of
* a pending @xfer; if an asynchronous delayed response is received before the
* related synchronous response (Out-of-Order Delayed Response) the missing
* synchronous response is assumed to be OK and completed, carrying on with the
* Delayed Response: this is done to address the case in which the underlying
* SCMI transport can deliver such out-of-order responses.
*
* Context: Assumes to be called with xfer->lock already acquired.
*
* Return: 0 on Success, error otherwise
*/
static inline int scmi_msg_response_validate(struct scmi_chan_info *cinfo,
u8 msg_type,
struct scmi_xfer *xfer)
{
/*
* Even if a response was indeed expected on this slot at this point,
* a buggy platform could wrongly reply feeding us an unexpected
* delayed response we're not prepared to handle: bail-out safely
* blaming firmware.
*/
if (msg_type == MSG_TYPE_DELAYED_RESP && !xfer->async_done) {
dev_err(cinfo->dev,
"Delayed Response for %d not expected! Buggy F/W ?\n",
xfer->hdr.seq);
return -EINVAL;
}
switch (xfer->state) {
case SCMI_XFER_SENT_OK:
if (msg_type == MSG_TYPE_DELAYED_RESP) {
/*
* Delayed Response expected but delivered earlier.
* Assume message RESPONSE was OK and skip state.
*/
xfer->hdr.status = SCMI_SUCCESS;
xfer->state = SCMI_XFER_RESP_OK;
complete(&xfer->done);
dev_warn(cinfo->dev,
"Received valid OoO Delayed Response for %d\n",
xfer->hdr.seq);
}
break;
case SCMI_XFER_RESP_OK:
if (msg_type != MSG_TYPE_DELAYED_RESP)
return -EINVAL;
break;
case SCMI_XFER_DRESP_OK:
/* No further message expected once in SCMI_XFER_DRESP_OK */
return -EINVAL;
}
return 0;
}
/**
* scmi_xfer_state_update - Update xfer state
*
* @xfer: A reference to the xfer to update
* @msg_type: Type of message being processed.
*
* Note that this message is assumed to have been already successfully validated
* by @scmi_msg_response_validate(), so here we just update the state.
*
* Context: Assumes to be called on an xfer exclusively acquired using the
* busy flag.
*/
static inline void scmi_xfer_state_update(struct scmi_xfer *xfer, u8 msg_type)
{
xfer->hdr.type = msg_type;
/* Unknown command types were already discarded earlier */
if (xfer->hdr.type == MSG_TYPE_COMMAND)
xfer->state = SCMI_XFER_RESP_OK;
else
xfer->state = SCMI_XFER_DRESP_OK;
}
static bool scmi_xfer_acquired(struct scmi_xfer *xfer)
{
int ret;
ret = atomic_cmpxchg(&xfer->busy, SCMI_XFER_FREE, SCMI_XFER_BUSY);
return ret == SCMI_XFER_FREE;
}
/**
* scmi_xfer_command_acquire - Helper to lookup and acquire a command xfer
*
* @cinfo: A reference to the channel descriptor.
* @msg_hdr: A message header to use as lookup key
*
* When a valid xfer is found for the sequence number embedded in the provided
* msg_hdr, reference counting is properly updated and exclusive access to this
* xfer is granted till released with @scmi_xfer_command_release.
*
* Return: A valid @xfer on Success or error otherwise.
*/
static inline struct scmi_xfer *
scmi_xfer_command_acquire(struct scmi_chan_info *cinfo, u32 msg_hdr)
{
int ret;
unsigned long flags;
struct scmi_xfer *xfer;
struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
struct scmi_xfers_info *minfo = &info->tx_minfo;
u8 msg_type = MSG_XTRACT_TYPE(msg_hdr);
u16 xfer_id = MSG_XTRACT_TOKEN(msg_hdr);
/* Are we even expecting this? */
spin_lock_irqsave(&minfo->xfer_lock, flags);
xfer = scmi_xfer_lookup_unlocked(minfo, xfer_id);
if (IS_ERR(xfer)) {
dev_err(cinfo->dev,
"Message for %d type %d is not expected!\n",
xfer_id, msg_type);
spin_unlock_irqrestore(&minfo->xfer_lock, flags);
return xfer;
}
refcount_inc(&xfer->users);
spin_unlock_irqrestore(&minfo->xfer_lock, flags);
spin_lock_irqsave(&xfer->lock, flags);
ret = scmi_msg_response_validate(cinfo, msg_type, xfer);
/*
* If a pending xfer was found which was also in a congruent state with
* the received message, acquire exclusive access to it setting the busy
* flag.
* Spins only on the rare limit condition of concurrent reception of
* RESP and DRESP for the same xfer.
*/
if (!ret) {
spin_until_cond(scmi_xfer_acquired(xfer));
scmi_xfer_state_update(xfer, msg_type);
}
spin_unlock_irqrestore(&xfer->lock, flags);
if (ret) {
dev_err(cinfo->dev,
"Invalid message type:%d for %d - HDR:0x%X state:%d\n",
msg_type, xfer_id, msg_hdr, xfer->state);
/* On error the refcount incremented above has to be dropped */
__scmi_xfer_put(minfo, xfer);
xfer = ERR_PTR(-EINVAL);
}
return xfer;
}
static inline void scmi_xfer_command_release(struct scmi_info *info,
struct scmi_xfer *xfer)
{
atomic_set(&xfer->busy, SCMI_XFER_FREE);
__scmi_xfer_put(&info->tx_minfo, xfer);
}
static inline void scmi_clear_channel(struct scmi_info *info,
struct scmi_chan_info *cinfo)
{
if (info->desc->ops->clear_channel)
info->desc->ops->clear_channel(cinfo);
}
static void scmi_handle_notification(struct scmi_chan_info *cinfo,
u32 msg_hdr, void *priv)
{
struct scmi_xfer *xfer;
struct device *dev = cinfo->dev;
@ -279,16 +619,17 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr)
ktime_t ts;
ts = ktime_get_boottime();
xfer = scmi_xfer_get(cinfo->handle, minfo);
xfer = scmi_xfer_get(cinfo->handle, minfo, false);
if (IS_ERR(xfer)) {
dev_err(dev, "failed to get free message slot (%ld)\n",
PTR_ERR(xfer));
info->desc->ops->clear_channel(cinfo);
scmi_clear_channel(info, cinfo);
return;
}
unpack_scmi_header(msg_hdr, &xfer->hdr);
scmi_dump_header_dbg(dev, &xfer->hdr);
if (priv)
xfer->priv = priv;
info->desc->ops->fetch_notification(cinfo, info->desc->max_msg_size,
xfer);
scmi_notify(cinfo->handle, xfer->hdr.protocol_id,
@ -300,59 +641,41 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr)
__scmi_xfer_put(minfo, xfer);
info->desc->ops->clear_channel(cinfo);
scmi_clear_channel(info, cinfo);
}
static void scmi_handle_response(struct scmi_chan_info *cinfo,
u16 xfer_id, u8 msg_type)
u32 msg_hdr, void *priv)
{
struct scmi_xfer *xfer;
struct device *dev = cinfo->dev;
struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
struct scmi_xfers_info *minfo = &info->tx_minfo;
/* Are we even expecting this? */
if (!test_bit(xfer_id, minfo->xfer_alloc_table)) {
dev_err(dev, "message for %d is not expected!\n", xfer_id);
info->desc->ops->clear_channel(cinfo);
return;
}
xfer = &minfo->xfer_block[xfer_id];
/*
* Even if a response was indeed expected on this slot at this point,
* a buggy platform could wrongly reply feeding us an unexpected
* delayed response we're not prepared to handle: bail-out safely
* blaming firmware.
*/
if (unlikely(msg_type == MSG_TYPE_DELAYED_RESP && !xfer->async_done)) {
dev_err(dev,
"Delayed Response for %d not expected! Buggy F/W ?\n",
xfer_id);
info->desc->ops->clear_channel(cinfo);
/* It was unexpected, so nobody will clear the xfer if not us */
__scmi_xfer_put(minfo, xfer);
xfer = scmi_xfer_command_acquire(cinfo, msg_hdr);
if (IS_ERR(xfer)) {
scmi_clear_channel(info, cinfo);
return;
}
/* rx.len could be shrunk in the sync do_xfer, so reset to maxsz */
if (msg_type == MSG_TYPE_DELAYED_RESP)
if (xfer->hdr.type == MSG_TYPE_DELAYED_RESP)
xfer->rx.len = info->desc->max_msg_size;
scmi_dump_header_dbg(dev, &xfer->hdr);
if (priv)
xfer->priv = priv;
info->desc->ops->fetch_response(cinfo, xfer);
trace_scmi_rx_done(xfer->transfer_id, xfer->hdr.id,
xfer->hdr.protocol_id, xfer->hdr.seq,
msg_type);
xfer->hdr.type);
if (msg_type == MSG_TYPE_DELAYED_RESP) {
info->desc->ops->clear_channel(cinfo);
if (xfer->hdr.type == MSG_TYPE_DELAYED_RESP) {
scmi_clear_channel(info, cinfo);
complete(xfer->async_done);
} else {
complete(&xfer->done);
}
scmi_xfer_command_release(info, xfer);
}
/**
@ -360,6 +683,7 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo,
*
* @cinfo: SCMI channel info
* @msg_hdr: Message header
* @priv: Transport specific private data.
*
* Processes one received message to appropriate transfer information and
* signals completion of the transfer.
@ -367,18 +691,17 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo,
* NOTE: This function will be invoked in IRQ context, hence should be
* as optimal as possible.
*/
void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr)
void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, void *priv)
{
u16 xfer_id = MSG_XTRACT_TOKEN(msg_hdr);
u8 msg_type = MSG_XTRACT_TYPE(msg_hdr);
switch (msg_type) {
case MSG_TYPE_NOTIFICATION:
scmi_handle_notification(cinfo, msg_hdr);
scmi_handle_notification(cinfo, msg_hdr, priv);
break;
case MSG_TYPE_COMMAND:
case MSG_TYPE_DELAYED_RESP:
scmi_handle_response(cinfo, xfer_id, msg_type);
scmi_handle_response(cinfo, msg_hdr, priv);
break;
default:
WARN_ONCE(1, "received unknown msg_type:%d\n", msg_type);
@ -390,7 +713,7 @@ void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr)
* xfer_put() - Release a transmit message
*
* @ph: Pointer to SCMI protocol handle
* @xfer: message that was reserved by scmi_xfer_get
* @xfer: message that was reserved by xfer_get_init
*/
static void xfer_put(const struct scmi_protocol_handle *ph,
struct scmi_xfer *xfer)
@ -408,7 +731,12 @@ static bool scmi_xfer_done_no_timeout(struct scmi_chan_info *cinfo,
{
struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
/*
* Poll also on xfer->done so that polling can be forcibly terminated
* in case of out-of-order receptions of delayed responses
*/
return info->desc->ops->poll_done(cinfo, xfer) ||
try_wait_for_completion(&xfer->done) ||
ktime_after(ktime_get(), stop);
}
@ -432,6 +760,12 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
struct device *dev = info->dev;
struct scmi_chan_info *cinfo;
if (xfer->hdr.poll_completion && !info->desc->ops->poll_done) {
dev_warn_once(dev,
"Polling mode is not supported by transport.\n");
return -EINVAL;
}
/*
* Initialise protocol id now from protocol handle to avoid it being
* overridden by mistake (or malice) by the protocol code mangling with
@ -448,6 +782,16 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
xfer->hdr.protocol_id, xfer->hdr.seq,
xfer->hdr.poll_completion);
xfer->state = SCMI_XFER_SENT_OK;
/*
* Even though spinlocking is not needed here since no race is possible
* on xfer->state due to the monotonically increasing tokens allocation,
* we must anyway ensure xfer->state initialization is not re-ordered
* after the .send_message() to be sure that on the RX path an early
* ISR calling scmi_rx_callback() cannot see an old stale xfer->state.
*/
smp_mb();
ret = info->desc->ops->send_message(cinfo, xfer);
if (ret < 0) {
dev_dbg(dev, "Failed to send message %d\n", ret);
@ -458,11 +802,22 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
ktime_t stop = ktime_add_ns(ktime_get(), SCMI_MAX_POLL_TO_NS);
spin_until_cond(scmi_xfer_done_no_timeout(cinfo, xfer, stop));
if (ktime_before(ktime_get(), stop)) {
unsigned long flags;
if (ktime_before(ktime_get(), stop))
info->desc->ops->fetch_response(cinfo, xfer);
else
/*
* Do not fetch_response if an out-of-order delayed
* response is being processed.
*/
spin_lock_irqsave(&xfer->lock, flags);
if (xfer->state == SCMI_XFER_SENT_OK) {
info->desc->ops->fetch_response(cinfo, xfer);
xfer->state = SCMI_XFER_RESP_OK;
}
spin_unlock_irqrestore(&xfer->lock, flags);
} else {
ret = -ETIMEDOUT;
}
} else {
/* And we wait for the response. */
timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms);
@ -557,7 +912,7 @@ static int xfer_get_init(const struct scmi_protocol_handle *ph,
tx_size > info->desc->max_msg_size)
return -ERANGE;
xfer = scmi_xfer_get(pi->handle, minfo);
xfer = scmi_xfer_get(pi->handle, minfo, true);
if (IS_ERR(xfer)) {
ret = PTR_ERR(xfer);
dev_err(dev, "failed to get free message slot(%d)\n", ret);
@ -566,6 +921,7 @@ static int xfer_get_init(const struct scmi_protocol_handle *ph,
xfer->tx.len = tx_size;
xfer->rx.len = rx_size ? : info->desc->max_msg_size;
xfer->hdr.type = MSG_TYPE_COMMAND;
xfer->hdr.id = msg_id;
xfer->hdr.poll_completion = false;
@ -1026,25 +1382,32 @@ static int __scmi_xfer_info_init(struct scmi_info *sinfo,
const struct scmi_desc *desc = sinfo->desc;
/* Pre-allocated messages, no more than what hdr.seq can support */
if (WARN_ON(!desc->max_msg || desc->max_msg > MSG_TOKEN_MAX)) {
if (WARN_ON(!info->max_msg || info->max_msg > MSG_TOKEN_MAX)) {
dev_err(dev,
"Invalid maximum messages %d, not in range [1 - %lu]\n",
desc->max_msg, MSG_TOKEN_MAX);
info->max_msg, MSG_TOKEN_MAX);
return -EINVAL;
}
info->xfer_block = devm_kcalloc(dev, desc->max_msg,
sizeof(*info->xfer_block), GFP_KERNEL);
if (!info->xfer_block)
return -ENOMEM;
hash_init(info->pending_xfers);
info->xfer_alloc_table = devm_kcalloc(dev, BITS_TO_LONGS(desc->max_msg),
/* Allocate a bitmask sized to hold MSG_TOKEN_MAX tokens */
info->xfer_alloc_table = devm_kcalloc(dev, BITS_TO_LONGS(MSG_TOKEN_MAX),
sizeof(long), GFP_KERNEL);
if (!info->xfer_alloc_table)
return -ENOMEM;
/* Pre-initialize the buffer pointer to pre-allocated buffers */
for (i = 0, xfer = info->xfer_block; i < desc->max_msg; i++, xfer++) {
/*
* Preallocate a number of xfers equal to max inflight messages,
* pre-initialize the buffer pointer to pre-allocated buffers and
* attach all of them to the free list
*/
INIT_HLIST_HEAD(&info->free_xfers);
for (i = 0; i < info->max_msg; i++) {
xfer = devm_kzalloc(dev, sizeof(*xfer), GFP_KERNEL);
if (!xfer)
return -ENOMEM;
xfer->rx.buf = devm_kcalloc(dev, sizeof(u8), desc->max_msg_size,
GFP_KERNEL);
if (!xfer->rx.buf)
@ -1052,6 +1415,10 @@ static int __scmi_xfer_info_init(struct scmi_info *sinfo,
xfer->tx.buf = xfer->rx.buf;
init_completion(&xfer->done);
spin_lock_init(&xfer->lock);
/* Add initialized xfer to the free list */
hlist_add_head(&xfer->node, &info->free_xfers);
}
spin_lock_init(&info->xfer_lock);
@ -1059,10 +1426,40 @@ static int __scmi_xfer_info_init(struct scmi_info *sinfo,
return 0;
}
static int scmi_channels_max_msg_configure(struct scmi_info *sinfo)
{
const struct scmi_desc *desc = sinfo->desc;
if (!desc->ops->get_max_msg) {
sinfo->tx_minfo.max_msg = desc->max_msg;
sinfo->rx_minfo.max_msg = desc->max_msg;
} else {
struct scmi_chan_info *base_cinfo;
base_cinfo = idr_find(&sinfo->tx_idr, SCMI_PROTOCOL_BASE);
if (!base_cinfo)
return -EINVAL;
sinfo->tx_minfo.max_msg = desc->ops->get_max_msg(base_cinfo);
/* RX channel is optional so can be skipped */
base_cinfo = idr_find(&sinfo->rx_idr, SCMI_PROTOCOL_BASE);
if (base_cinfo)
sinfo->rx_minfo.max_msg =
desc->ops->get_max_msg(base_cinfo);
}
return 0;
}
static int scmi_xfer_info_init(struct scmi_info *sinfo)
{
int ret = __scmi_xfer_info_init(sinfo, &sinfo->tx_minfo);
int ret;
ret = scmi_channels_max_msg_configure(sinfo);
if (ret)
return ret;
ret = __scmi_xfer_info_init(sinfo, &sinfo->tx_minfo);
if (!ret && idr_find(&sinfo->rx_idr, SCMI_PROTOCOL_BASE))
ret = __scmi_xfer_info_init(sinfo, &sinfo->rx_minfo);
@ -1390,6 +1787,21 @@ void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table)
mutex_unlock(&scmi_requested_devices_mtx);
}
static int scmi_cleanup_txrx_channels(struct scmi_info *info)
{
int ret;
struct idr *idr = &info->tx_idr;
ret = idr_for_each(idr, info->desc->ops->chan_free, idr);
idr_destroy(&info->tx_idr);
idr = &info->rx_idr;
ret = idr_for_each(idr, info->desc->ops->chan_free, idr);
idr_destroy(&info->rx_idr);
return ret;
}
static int scmi_probe(struct platform_device *pdev)
{
int ret;
@ -1424,13 +1836,19 @@ static int scmi_probe(struct platform_device *pdev)
handle->devm_protocol_get = scmi_devm_protocol_get;
handle->devm_protocol_put = scmi_devm_protocol_put;
if (desc->ops->link_supplier) {
ret = desc->ops->link_supplier(dev);
if (ret)
return ret;
}
ret = scmi_txrx_setup(info, dev, SCMI_PROTOCOL_BASE);
if (ret)
return ret;
ret = scmi_xfer_info_init(info);
if (ret)
return ret;
goto clear_txrx_setup;
if (scmi_notification_init(handle))
dev_err(dev, "SCMI Notifications NOT available.\n");
@ -1443,7 +1861,7 @@ static int scmi_probe(struct platform_device *pdev)
ret = scmi_protocol_acquire(handle, SCMI_PROTOCOL_BASE);
if (ret) {
dev_err(dev, "unable to communicate with SCMI\n");
return ret;
goto notification_exit;
}
mutex_lock(&scmi_list_mutex);
@ -1482,6 +1900,12 @@ static int scmi_probe(struct platform_device *pdev)
}
return 0;
notification_exit:
scmi_notification_exit(&info->handle);
clear_txrx_setup:
scmi_cleanup_txrx_channels(info);
return ret;
}
void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id)
@ -1493,7 +1917,6 @@ static int scmi_remove(struct platform_device *pdev)
{
int ret = 0, id;
struct scmi_info *info = platform_get_drvdata(pdev);
struct idr *idr = &info->tx_idr;
struct device_node *child;
mutex_lock(&scmi_list_mutex);
@ -1517,14 +1940,7 @@ static int scmi_remove(struct platform_device *pdev)
idr_destroy(&info->active_protocols);
/* Safe to free channels since no more users */
ret = idr_for_each(idr, info->desc->ops->chan_free, idr);
idr_destroy(&info->tx_idr);
idr = &info->rx_idr;
ret = idr_for_each(idr, info->desc->ops->chan_free, idr);
idr_destroy(&info->rx_idr);
return ret;
return scmi_cleanup_txrx_channels(info);
}
static ssize_t protocol_version_show(struct device *dev,
@ -1575,11 +1991,14 @@ ATTRIBUTE_GROUPS(versions);
/* Each compatible listed below must have descriptor associated with it */
static const struct of_device_id scmi_of_match[] = {
#ifdef CONFIG_MAILBOX
#ifdef CONFIG_ARM_SCMI_TRANSPORT_MAILBOX
{ .compatible = "arm,scmi", .data = &scmi_mailbox_desc },
#endif
#ifdef CONFIG_HAVE_ARM_SMCCC_DISCOVERY
#ifdef CONFIG_ARM_SCMI_TRANSPORT_SMC
{ .compatible = "arm,scmi-smc", .data = &scmi_smc_desc},
#endif
#ifdef CONFIG_ARM_SCMI_TRANSPORT_VIRTIO
{ .compatible = "arm,scmi-virtio", .data = &scmi_virtio_desc},
#endif
{ /* Sentinel */ },
};
@ -1596,10 +2015,69 @@ static struct platform_driver scmi_driver = {
.remove = scmi_remove,
};
/**
* __scmi_transports_setup - Common helper to call transport-specific
* .init/.exit code if provided.
*
* @init: A flag to distinguish between init and exit.
*
* Note that, if provided, we invoke .init/.exit functions for all the
* transports currently compiled in.
*
* Return: 0 on Success.
*/
static inline int __scmi_transports_setup(bool init)
{
int ret = 0;
const struct of_device_id *trans;
for (trans = scmi_of_match; trans->data; trans++) {
const struct scmi_desc *tdesc = trans->data;
if ((init && !tdesc->transport_init) ||
(!init && !tdesc->transport_exit))
continue;
if (init)
ret = tdesc->transport_init();
else
tdesc->transport_exit();
if (ret) {
pr_err("SCMI transport %s FAILED initialization!\n",
trans->compatible);
break;
}
}
return ret;
}
static int __init scmi_transports_init(void)
{
return __scmi_transports_setup(true);
}
static void __exit scmi_transports_exit(void)
{
__scmi_transports_setup(false);
}
static int __init scmi_driver_init(void)
{
int ret;
/* Bail out if no SCMI transport was configured */
if (WARN_ON(!IS_ENABLED(CONFIG_ARM_SCMI_HAVE_TRANSPORT)))
return -EINVAL;
scmi_bus_init();
/* Initialize any compiled-in transport which provided an init/exit */
ret = scmi_transports_init();
if (ret)
return ret;
scmi_base_register();
scmi_clock_register();
@ -1628,6 +2106,8 @@ static void __exit scmi_driver_exit(void)
scmi_bus_exit();
scmi_transports_exit();
platform_driver_unregister(&scmi_driver);
}
module_exit(scmi_driver_exit);

View File

@ -43,7 +43,7 @@ static void rx_callback(struct mbox_client *cl, void *m)
{
struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl);
scmi_rx_callback(smbox->cinfo, shmem_read_header(smbox->shmem));
scmi_rx_callback(smbox->cinfo, shmem_read_header(smbox->shmem), NULL);
}
static bool mailbox_chan_available(struct device *dev, int idx)

View File

@ -0,0 +1,111 @@
// SPDX-License-Identifier: GPL-2.0
/*
* For transports using message passing.
*
* Derived from shm.c.
*
* Copyright (C) 2019-2021 ARM Ltd.
* Copyright (C) 2020-2021 OpenSynergy GmbH
*/
#include <linux/types.h>
#include "common.h"
/*
* struct scmi_msg_payld - Transport SDU layout
*
* The SCMI specification requires all parameters, message headers, return
* arguments or any protocol data to be expressed in little endian format only.
*/
struct scmi_msg_payld {
__le32 msg_header;
__le32 msg_payload[];
};
/**
* msg_command_size() - Actual size of transport SDU for command.
*
* @xfer: message which core has prepared for sending
*
* Return: transport SDU size.
*/
size_t msg_command_size(struct scmi_xfer *xfer)
{
return sizeof(struct scmi_msg_payld) + xfer->tx.len;
}
/**
* msg_response_size() - Maximum size of transport SDU for response.
*
* @xfer: message which core has prepared for sending
*
* Return: transport SDU size.
*/
size_t msg_response_size(struct scmi_xfer *xfer)
{
return sizeof(struct scmi_msg_payld) + sizeof(__le32) + xfer->rx.len;
}
/**
* msg_tx_prepare() - Set up transport SDU for command.
*
* @msg: transport SDU for command
* @xfer: message which is being sent
*/
void msg_tx_prepare(struct scmi_msg_payld *msg, struct scmi_xfer *xfer)
{
msg->msg_header = cpu_to_le32(pack_scmi_header(&xfer->hdr));
if (xfer->tx.buf)
memcpy(msg->msg_payload, xfer->tx.buf, xfer->tx.len);
}
/**
* msg_read_header() - Read SCMI header from transport SDU.
*
* @msg: transport SDU
*
* Return: SCMI header
*/
u32 msg_read_header(struct scmi_msg_payld *msg)
{
return le32_to_cpu(msg->msg_header);
}
/**
* msg_fetch_response() - Fetch response SCMI payload from transport SDU.
*
* @msg: transport SDU with response
* @len: transport SDU size
* @xfer: message being responded to
*/
void msg_fetch_response(struct scmi_msg_payld *msg, size_t len,
struct scmi_xfer *xfer)
{
size_t prefix_len = sizeof(*msg) + sizeof(msg->msg_payload[0]);
xfer->hdr.status = le32_to_cpu(msg->msg_payload[0]);
xfer->rx.len = min_t(size_t, xfer->rx.len,
len >= prefix_len ? len - prefix_len : 0);
/* Take a copy to the rx buffer.. */
memcpy(xfer->rx.buf, &msg->msg_payload[1], xfer->rx.len);
}
/**
* msg_fetch_notification() - Fetch notification payload from transport SDU.
*
* @msg: transport SDU with notification
* @len: transport SDU size
* @max_len: maximum SCMI payload size to fetch
* @xfer: notification message
*/
void msg_fetch_notification(struct scmi_msg_payld *msg, size_t len,
size_t max_len, struct scmi_xfer *xfer)
{
xfer->rx.len = min_t(size_t, max_len,
len >= sizeof(*msg) ? len - sizeof(*msg) : 0);
/* Take a copy to the rx buffer.. */
memcpy(xfer->rx.buf, msg->msg_payload, xfer->rx.len);
}

View File

@ -154,7 +154,8 @@ static int smc_send_message(struct scmi_chan_info *cinfo,
if (scmi_info->irq)
wait_for_completion(&scmi_info->tx_complete);
scmi_rx_callback(scmi_info->cinfo, shmem_read_header(scmi_info->shmem));
scmi_rx_callback(scmi_info->cinfo,
shmem_read_header(scmi_info->shmem), NULL);
mutex_unlock(&scmi_info->shmem_lock);

View File

@ -0,0 +1,491 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Virtio Transport driver for Arm System Control and Management Interface
* (SCMI).
*
* Copyright (C) 2020-2021 OpenSynergy.
* Copyright (C) 2021 ARM Ltd.
*/
/**
* DOC: Theory of Operation
*
* The scmi-virtio transport implements a driver for the virtio SCMI device.
*
* There is one Tx channel (virtio cmdq, A2P channel) and at most one Rx
* channel (virtio eventq, P2A channel). Each channel is implemented through a
* virtqueue. Access to each virtqueue is protected by spinlocks.
*/
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/virtio.h>
#include <linux/virtio_config.h>
#include <uapi/linux/virtio_ids.h>
#include <uapi/linux/virtio_scmi.h>
#include "common.h"
#define VIRTIO_SCMI_MAX_MSG_SIZE 128 /* Value may be increased. */
#define VIRTIO_SCMI_MAX_PDU_SIZE \
(VIRTIO_SCMI_MAX_MSG_SIZE + SCMI_MSG_MAX_PROT_OVERHEAD)
#define DESCRIPTORS_PER_TX_MSG 2
/**
* struct scmi_vio_channel - Transport channel information
*
* @vqueue: Associated virtqueue
* @cinfo: SCMI Tx or Rx channel
* @free_list: List of unused scmi_vio_msg, maintained for Tx channels only
* @is_rx: Whether channel is an Rx channel
* @ready: Whether transport user is ready to hear about channel
* @max_msg: Maximum number of pending messages for this channel.
* @lock: Protects access to all members except ready.
* @ready_lock: Protects access to ready. If required, it must be taken before
* lock.
*/
struct scmi_vio_channel {
struct virtqueue *vqueue;
struct scmi_chan_info *cinfo;
struct list_head free_list;
bool is_rx;
bool ready;
unsigned int max_msg;
/* lock to protect access to all members except ready. */
spinlock_t lock;
/* lock to rotects access to ready flag. */
spinlock_t ready_lock;
};
/**
* struct scmi_vio_msg - Transport PDU information
*
* @request: SDU used for commands
* @input: SDU used for (delayed) responses and notifications
* @list: List which scmi_vio_msg may be part of
* @rx_len: Input SDU size in bytes, once input has been received
*/
struct scmi_vio_msg {
struct scmi_msg_payld *request;
struct scmi_msg_payld *input;
struct list_head list;
unsigned int rx_len;
};
/* Only one SCMI VirtIO device can possibly exist */
static struct virtio_device *scmi_vdev;
static bool scmi_vio_have_vq_rx(struct virtio_device *vdev)
{
return virtio_has_feature(vdev, VIRTIO_SCMI_F_P2A_CHANNELS);
}
static int scmi_vio_feed_vq_rx(struct scmi_vio_channel *vioch,
struct scmi_vio_msg *msg)
{
struct scatterlist sg_in;
int rc;
unsigned long flags;
sg_init_one(&sg_in, msg->input, VIRTIO_SCMI_MAX_PDU_SIZE);
spin_lock_irqsave(&vioch->lock, flags);
rc = virtqueue_add_inbuf(vioch->vqueue, &sg_in, 1, msg, GFP_ATOMIC);
if (rc)
dev_err_once(vioch->cinfo->dev,
"failed to add to virtqueue (%d)\n", rc);
else
virtqueue_kick(vioch->vqueue);
spin_unlock_irqrestore(&vioch->lock, flags);
return rc;
}
static void scmi_finalize_message(struct scmi_vio_channel *vioch,
struct scmi_vio_msg *msg)
{
if (vioch->is_rx) {
scmi_vio_feed_vq_rx(vioch, msg);
} else {
unsigned long flags;
spin_lock_irqsave(&vioch->lock, flags);
list_add(&msg->list, &vioch->free_list);
spin_unlock_irqrestore(&vioch->lock, flags);
}
}
static void scmi_vio_complete_cb(struct virtqueue *vqueue)
{
unsigned long ready_flags;
unsigned long flags;
unsigned int length;
struct scmi_vio_channel *vioch;
struct scmi_vio_msg *msg;
bool cb_enabled = true;
if (WARN_ON_ONCE(!vqueue->vdev->priv))
return;
vioch = &((struct scmi_vio_channel *)vqueue->vdev->priv)[vqueue->index];
for (;;) {
spin_lock_irqsave(&vioch->ready_lock, ready_flags);
if (!vioch->ready) {
if (!cb_enabled)
(void)virtqueue_enable_cb(vqueue);
goto unlock_ready_out;
}
spin_lock_irqsave(&vioch->lock, flags);
if (cb_enabled) {
virtqueue_disable_cb(vqueue);
cb_enabled = false;
}
msg = virtqueue_get_buf(vqueue, &length);
if (!msg) {
if (virtqueue_enable_cb(vqueue))
goto unlock_out;
cb_enabled = true;
}
spin_unlock_irqrestore(&vioch->lock, flags);
if (msg) {
msg->rx_len = length;
scmi_rx_callback(vioch->cinfo,
msg_read_header(msg->input), msg);
scmi_finalize_message(vioch, msg);
}
spin_unlock_irqrestore(&vioch->ready_lock, ready_flags);
}
unlock_out:
spin_unlock_irqrestore(&vioch->lock, flags);
unlock_ready_out:
spin_unlock_irqrestore(&vioch->ready_lock, ready_flags);
}
static const char *const scmi_vio_vqueue_names[] = { "tx", "rx" };
static vq_callback_t *scmi_vio_complete_callbacks[] = {
scmi_vio_complete_cb,
scmi_vio_complete_cb
};
static unsigned int virtio_get_max_msg(struct scmi_chan_info *base_cinfo)
{
struct scmi_vio_channel *vioch = base_cinfo->transport_info;
return vioch->max_msg;
}
static int virtio_link_supplier(struct device *dev)
{
if (!scmi_vdev) {
dev_notice_once(dev,
"Deferring probe after not finding a bound scmi-virtio device\n");
return -EPROBE_DEFER;
}
if (!device_link_add(dev, &scmi_vdev->dev,
DL_FLAG_AUTOREMOVE_CONSUMER)) {
dev_err(dev, "Adding link to supplier virtio device failed\n");
return -ECANCELED;
}
return 0;
}
static bool virtio_chan_available(struct device *dev, int idx)
{
struct scmi_vio_channel *channels, *vioch = NULL;
if (WARN_ON_ONCE(!scmi_vdev))
return false;
channels = (struct scmi_vio_channel *)scmi_vdev->priv;
switch (idx) {
case VIRTIO_SCMI_VQ_TX:
vioch = &channels[VIRTIO_SCMI_VQ_TX];
break;
case VIRTIO_SCMI_VQ_RX:
if (scmi_vio_have_vq_rx(scmi_vdev))
vioch = &channels[VIRTIO_SCMI_VQ_RX];
break;
default:
return false;
}
return vioch && !vioch->cinfo;
}
static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
bool tx)
{
unsigned long flags;
struct scmi_vio_channel *vioch;
int index = tx ? VIRTIO_SCMI_VQ_TX : VIRTIO_SCMI_VQ_RX;
int i;
if (!scmi_vdev)
return -EPROBE_DEFER;
vioch = &((struct scmi_vio_channel *)scmi_vdev->priv)[index];
for (i = 0; i < vioch->max_msg; i++) {
struct scmi_vio_msg *msg;
msg = devm_kzalloc(cinfo->dev, sizeof(*msg), GFP_KERNEL);
if (!msg)
return -ENOMEM;
if (tx) {
msg->request = devm_kzalloc(cinfo->dev,
VIRTIO_SCMI_MAX_PDU_SIZE,
GFP_KERNEL);
if (!msg->request)
return -ENOMEM;
}
msg->input = devm_kzalloc(cinfo->dev, VIRTIO_SCMI_MAX_PDU_SIZE,
GFP_KERNEL);
if (!msg->input)
return -ENOMEM;
if (tx) {
spin_lock_irqsave(&vioch->lock, flags);
list_add_tail(&msg->list, &vioch->free_list);
spin_unlock_irqrestore(&vioch->lock, flags);
} else {
scmi_vio_feed_vq_rx(vioch, msg);
}
}
spin_lock_irqsave(&vioch->lock, flags);
cinfo->transport_info = vioch;
/* Indirectly setting channel not available any more */
vioch->cinfo = cinfo;
spin_unlock_irqrestore(&vioch->lock, flags);
spin_lock_irqsave(&vioch->ready_lock, flags);
vioch->ready = true;
spin_unlock_irqrestore(&vioch->ready_lock, flags);
return 0;
}
static int virtio_chan_free(int id, void *p, void *data)
{
unsigned long flags;
struct scmi_chan_info *cinfo = p;
struct scmi_vio_channel *vioch = cinfo->transport_info;
spin_lock_irqsave(&vioch->ready_lock, flags);
vioch->ready = false;
spin_unlock_irqrestore(&vioch->ready_lock, flags);
scmi_free_channel(cinfo, data, id);
spin_lock_irqsave(&vioch->lock, flags);
vioch->cinfo = NULL;
spin_unlock_irqrestore(&vioch->lock, flags);
return 0;
}
static int virtio_send_message(struct scmi_chan_info *cinfo,
struct scmi_xfer *xfer)
{
struct scmi_vio_channel *vioch = cinfo->transport_info;
struct scatterlist sg_out;
struct scatterlist sg_in;
struct scatterlist *sgs[DESCRIPTORS_PER_TX_MSG] = { &sg_out, &sg_in };
unsigned long flags;
int rc;
struct scmi_vio_msg *msg;
spin_lock_irqsave(&vioch->lock, flags);
if (list_empty(&vioch->free_list)) {
spin_unlock_irqrestore(&vioch->lock, flags);
return -EBUSY;
}
msg = list_first_entry(&vioch->free_list, typeof(*msg), list);
list_del(&msg->list);
msg_tx_prepare(msg->request, xfer);
sg_init_one(&sg_out, msg->request, msg_command_size(xfer));
sg_init_one(&sg_in, msg->input, msg_response_size(xfer));
rc = virtqueue_add_sgs(vioch->vqueue, sgs, 1, 1, msg, GFP_ATOMIC);
if (rc) {
list_add(&msg->list, &vioch->free_list);
dev_err_once(vioch->cinfo->dev,
"%s() failed to add to virtqueue (%d)\n", __func__,
rc);
} else {
virtqueue_kick(vioch->vqueue);
}
spin_unlock_irqrestore(&vioch->lock, flags);
return rc;
}
static void virtio_fetch_response(struct scmi_chan_info *cinfo,
struct scmi_xfer *xfer)
{
struct scmi_vio_msg *msg = xfer->priv;
if (msg) {
msg_fetch_response(msg->input, msg->rx_len, xfer);
xfer->priv = NULL;
}
}
static void virtio_fetch_notification(struct scmi_chan_info *cinfo,
size_t max_len, struct scmi_xfer *xfer)
{
struct scmi_vio_msg *msg = xfer->priv;
if (msg) {
msg_fetch_notification(msg->input, msg->rx_len, max_len, xfer);
xfer->priv = NULL;
}
}
static const struct scmi_transport_ops scmi_virtio_ops = {
.link_supplier = virtio_link_supplier,
.chan_available = virtio_chan_available,
.chan_setup = virtio_chan_setup,
.chan_free = virtio_chan_free,
.get_max_msg = virtio_get_max_msg,
.send_message = virtio_send_message,
.fetch_response = virtio_fetch_response,
.fetch_notification = virtio_fetch_notification,
};
static int scmi_vio_probe(struct virtio_device *vdev)
{
struct device *dev = &vdev->dev;
struct scmi_vio_channel *channels;
bool have_vq_rx;
int vq_cnt;
int i;
int ret;
struct virtqueue *vqs[VIRTIO_SCMI_VQ_MAX_CNT];
/* Only one SCMI VirtiO device allowed */
if (scmi_vdev)
return -EINVAL;
have_vq_rx = scmi_vio_have_vq_rx(vdev);
vq_cnt = have_vq_rx ? VIRTIO_SCMI_VQ_MAX_CNT : 1;
channels = devm_kcalloc(dev, vq_cnt, sizeof(*channels), GFP_KERNEL);
if (!channels)
return -ENOMEM;
if (have_vq_rx)
channels[VIRTIO_SCMI_VQ_RX].is_rx = true;
ret = virtio_find_vqs(vdev, vq_cnt, vqs, scmi_vio_complete_callbacks,
scmi_vio_vqueue_names, NULL);
if (ret) {
dev_err(dev, "Failed to get %d virtqueue(s)\n", vq_cnt);
return ret;
}
for (i = 0; i < vq_cnt; i++) {
unsigned int sz;
spin_lock_init(&channels[i].lock);
spin_lock_init(&channels[i].ready_lock);
INIT_LIST_HEAD(&channels[i].free_list);
channels[i].vqueue = vqs[i];
sz = virtqueue_get_vring_size(channels[i].vqueue);
/* Tx messages need multiple descriptors. */
if (!channels[i].is_rx)
sz /= DESCRIPTORS_PER_TX_MSG;
if (sz > MSG_TOKEN_MAX) {
dev_info_once(dev,
"%s virtqueue could hold %d messages. Only %ld allowed to be pending.\n",
channels[i].is_rx ? "rx" : "tx",
sz, MSG_TOKEN_MAX);
sz = MSG_TOKEN_MAX;
}
channels[i].max_msg = sz;
}
vdev->priv = channels;
scmi_vdev = vdev;
return 0;
}
static void scmi_vio_remove(struct virtio_device *vdev)
{
vdev->config->reset(vdev);
vdev->config->del_vqs(vdev);
scmi_vdev = NULL;
}
static int scmi_vio_validate(struct virtio_device *vdev)
{
if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
dev_err(&vdev->dev,
"device does not comply with spec version 1.x\n");
return -EINVAL;
}
return 0;
}
static unsigned int features[] = {
VIRTIO_SCMI_F_P2A_CHANNELS,
};
static const struct virtio_device_id id_table[] = {
{ VIRTIO_ID_SCMI, VIRTIO_DEV_ANY_ID },
{ 0 }
};
static struct virtio_driver virtio_scmi_driver = {
.driver.name = "scmi-virtio",
.driver.owner = THIS_MODULE,
.feature_table = features,
.feature_table_size = ARRAY_SIZE(features),
.id_table = id_table,
.probe = scmi_vio_probe,
.remove = scmi_vio_remove,
.validate = scmi_vio_validate,
};
static int __init virtio_scmi_init(void)
{
return register_virtio_driver(&virtio_scmi_driver);
}
static void __exit virtio_scmi_exit(void)
{
unregister_virtio_driver(&virtio_scmi_driver);
}
const struct scmi_desc scmi_virtio_desc = {
.transport_init = virtio_scmi_init,
.transport_exit = virtio_scmi_exit,
.ops = &scmi_virtio_ops,
.max_rx_timeout_ms = 60000, /* for non-realtime virtio devices */
.max_msg = 0, /* overridden by virtio_get_max_msg() */
.max_msg_size = VIRTIO_SCMI_MAX_MSG_SIZE,
};

View File

@ -71,7 +71,7 @@ static struct qcom_scm_wb_entry qcom_scm_wb[] = {
{ .flag = QCOM_SCM_FLAG_WARMBOOT_CPU3 },
};
static const char *qcom_scm_convention_names[] = {
static const char * const qcom_scm_convention_names[] = {
[SMC_CONVENTION_UNKNOWN] = "unknown",
[SMC_CONVENTION_ARM_32] = "smc arm 32",
[SMC_CONVENTION_ARM_64] = "smc arm 64",
@ -331,7 +331,7 @@ int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus)
.owner = ARM_SMCCC_OWNER_SIP,
};
if (!cpus || (cpus && cpumask_empty(cpus)))
if (!cpus || cpumask_empty(cpus))
return -EINVAL;
for_each_cpu(cpu, cpus) {
@ -1299,6 +1299,7 @@ static const struct of_device_id qcom_scm_dt_match[] = {
{ .compatible = "qcom,scm" },
{}
};
MODULE_DEVICE_TABLE(of, qcom_scm_dt_match);
static struct platform_driver qcom_scm_driver = {
.driver = {
@ -1315,3 +1316,6 @@ static int __init qcom_scm_init(void)
return platform_driver_register(&qcom_scm_driver);
}
subsys_initcall(qcom_scm_init);
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. SCM driver");
MODULE_LICENSE("GPL v2");

View File

@ -296,25 +296,61 @@ static int bpmp_debug_show(struct seq_file *m, void *p)
struct file *file = m->private;
struct inode *inode = file_inode(file);
struct tegra_bpmp *bpmp = inode->i_private;
char *databuf = NULL;
char fnamebuf[256];
const char *filename;
uint32_t nbytes = 0;
size_t len;
int err;
len = seq_get_buf(m, &databuf);
if (!databuf)
return -ENOMEM;
struct mrq_debug_request req = {
.cmd = cpu_to_le32(CMD_DEBUG_READ),
};
struct mrq_debug_response resp;
struct tegra_bpmp_message msg = {
.mrq = MRQ_DEBUG,
.tx = {
.data = &req,
.size = sizeof(req),
},
.rx = {
.data = &resp,
.size = sizeof(resp),
},
};
uint32_t fd = 0, len = 0;
int remaining, err;
filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf));
if (!filename)
return -ENOENT;
err = mrq_debug_read(bpmp, filename, databuf, len, &nbytes);
if (!err)
seq_commit(m, nbytes);
mutex_lock(&bpmp_debug_lock);
err = mrq_debug_open(bpmp, filename, &fd, &len, 0);
if (err)
goto out;
req.frd.fd = fd;
remaining = len;
while (remaining > 0) {
err = tegra_bpmp_transfer(bpmp, &msg);
if (err < 0) {
goto close;
} else if (msg.rx.ret < 0) {
err = -EINVAL;
goto close;
}
if (resp.frd.readlen > remaining) {
pr_err("%s: read data length invalid\n", __func__);
err = -EINVAL;
goto close;
}
seq_write(m, resp.frd.data, resp.frd.readlen);
remaining -= resp.frd.readlen;
}
close:
err = mrq_debug_close(bpmp, fd);
out:
mutex_unlock(&bpmp_debug_lock);
return err;
}

View File

@ -253,6 +253,7 @@ config SPAPR_TCE_IOMMU
config ARM_SMMU
tristate "ARM Ltd. System MMU (SMMU) Support"
depends on ARM64 || ARM || (COMPILE_TEST && !GENERIC_ATOMIC64)
depends on QCOM_SCM || !QCOM_SCM #if QCOM_SCM=m this can't be =y
select IOMMU_API
select IOMMU_IO_PGTABLE_LPAE
select ARM_DMA_USE_IOMMU if ARM
@ -382,6 +383,7 @@ config QCOM_IOMMU
# Note: iommu drivers cannot (yet?) be built as modules
bool "Qualcomm IOMMU Support"
depends on ARCH_QCOM || (COMPILE_TEST && !GENERIC_ATOMIC64)
depends on QCOM_SCM=y
select IOMMU_API
select IOMMU_IO_PGTABLE_LPAE
select ARM_DMA_USE_IOMMU

View File

@ -9,6 +9,7 @@
* Copyright (C) 2009 Texas Instruments
* Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
*/
#include <linux/cpu_pm.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/init.h>
@ -232,7 +233,10 @@ struct gpmc_device {
int irq;
struct irq_chip irq_chip;
struct gpio_chip gpio_chip;
struct notifier_block nb;
struct omap3_gpmc_regs context;
int nirqs;
unsigned int is_suspended:1;
};
static struct irq_domain *gpmc_irq_domain;
@ -2384,6 +2388,106 @@ static int gpmc_gpio_init(struct gpmc_device *gpmc)
return 0;
}
static void omap3_gpmc_save_context(struct gpmc_device *gpmc)
{
struct omap3_gpmc_regs *gpmc_context;
int i;
if (!gpmc || !gpmc_base)
return;
gpmc_context = &gpmc->context;
gpmc_context->sysconfig = gpmc_read_reg(GPMC_SYSCONFIG);
gpmc_context->irqenable = gpmc_read_reg(GPMC_IRQENABLE);
gpmc_context->timeout_ctrl = gpmc_read_reg(GPMC_TIMEOUT_CONTROL);
gpmc_context->config = gpmc_read_reg(GPMC_CONFIG);
gpmc_context->prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
gpmc_context->prefetch_config2 = gpmc_read_reg(GPMC_PREFETCH_CONFIG2);
gpmc_context->prefetch_control = gpmc_read_reg(GPMC_PREFETCH_CONTROL);
for (i = 0; i < gpmc_cs_num; i++) {
gpmc_context->cs_context[i].is_valid = gpmc_cs_mem_enabled(i);
if (gpmc_context->cs_context[i].is_valid) {
gpmc_context->cs_context[i].config1 =
gpmc_cs_read_reg(i, GPMC_CS_CONFIG1);
gpmc_context->cs_context[i].config2 =
gpmc_cs_read_reg(i, GPMC_CS_CONFIG2);
gpmc_context->cs_context[i].config3 =
gpmc_cs_read_reg(i, GPMC_CS_CONFIG3);
gpmc_context->cs_context[i].config4 =
gpmc_cs_read_reg(i, GPMC_CS_CONFIG4);
gpmc_context->cs_context[i].config5 =
gpmc_cs_read_reg(i, GPMC_CS_CONFIG5);
gpmc_context->cs_context[i].config6 =
gpmc_cs_read_reg(i, GPMC_CS_CONFIG6);
gpmc_context->cs_context[i].config7 =
gpmc_cs_read_reg(i, GPMC_CS_CONFIG7);
}
}
}
static void omap3_gpmc_restore_context(struct gpmc_device *gpmc)
{
struct omap3_gpmc_regs *gpmc_context;
int i;
if (!gpmc || !gpmc_base)
return;
gpmc_context = &gpmc->context;
gpmc_write_reg(GPMC_SYSCONFIG, gpmc_context->sysconfig);
gpmc_write_reg(GPMC_IRQENABLE, gpmc_context->irqenable);
gpmc_write_reg(GPMC_TIMEOUT_CONTROL, gpmc_context->timeout_ctrl);
gpmc_write_reg(GPMC_CONFIG, gpmc_context->config);
gpmc_write_reg(GPMC_PREFETCH_CONFIG1, gpmc_context->prefetch_config1);
gpmc_write_reg(GPMC_PREFETCH_CONFIG2, gpmc_context->prefetch_config2);
gpmc_write_reg(GPMC_PREFETCH_CONTROL, gpmc_context->prefetch_control);
for (i = 0; i < gpmc_cs_num; i++) {
if (gpmc_context->cs_context[i].is_valid) {
gpmc_cs_write_reg(i, GPMC_CS_CONFIG1,
gpmc_context->cs_context[i].config1);
gpmc_cs_write_reg(i, GPMC_CS_CONFIG2,
gpmc_context->cs_context[i].config2);
gpmc_cs_write_reg(i, GPMC_CS_CONFIG3,
gpmc_context->cs_context[i].config3);
gpmc_cs_write_reg(i, GPMC_CS_CONFIG4,
gpmc_context->cs_context[i].config4);
gpmc_cs_write_reg(i, GPMC_CS_CONFIG5,
gpmc_context->cs_context[i].config5);
gpmc_cs_write_reg(i, GPMC_CS_CONFIG6,
gpmc_context->cs_context[i].config6);
gpmc_cs_write_reg(i, GPMC_CS_CONFIG7,
gpmc_context->cs_context[i].config7);
} else {
gpmc_cs_write_reg(i, GPMC_CS_CONFIG7, 0);
}
}
}
static int omap_gpmc_context_notifier(struct notifier_block *nb,
unsigned long cmd, void *v)
{
struct gpmc_device *gpmc;
gpmc = container_of(nb, struct gpmc_device, nb);
if (gpmc->is_suspended || pm_runtime_suspended(gpmc->dev))
return NOTIFY_OK;
switch (cmd) {
case CPU_CLUSTER_PM_ENTER:
omap3_gpmc_save_context(gpmc);
break;
case CPU_CLUSTER_PM_ENTER_FAILED: /* No need to restore context */
break;
case CPU_CLUSTER_PM_EXIT:
omap3_gpmc_restore_context(gpmc);
break;
}
return NOTIFY_OK;
}
static int gpmc_probe(struct platform_device *pdev)
{
int rc;
@ -2472,6 +2576,9 @@ static int gpmc_probe(struct platform_device *pdev)
gpmc_probe_dt_children(pdev);
gpmc->nb.notifier_call = omap_gpmc_context_notifier;
cpu_pm_register_notifier(&gpmc->nb);
return 0;
gpio_init_failed:
@ -2486,6 +2593,7 @@ static int gpmc_remove(struct platform_device *pdev)
{
struct gpmc_device *gpmc = platform_get_drvdata(pdev);
cpu_pm_unregister_notifier(&gpmc->nb);
gpmc_free_irq(gpmc);
gpmc_mem_exit();
pm_runtime_put_sync(&pdev->dev);
@ -2497,15 +2605,23 @@ static int gpmc_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int gpmc_suspend(struct device *dev)
{
omap3_gpmc_save_context();
struct gpmc_device *gpmc = dev_get_drvdata(dev);
omap3_gpmc_save_context(gpmc);
pm_runtime_put_sync(dev);
gpmc->is_suspended = 1;
return 0;
}
static int gpmc_resume(struct device *dev)
{
struct gpmc_device *gpmc = dev_get_drvdata(dev);
pm_runtime_get_sync(dev);
omap3_gpmc_restore_context();
omap3_gpmc_restore_context(gpmc);
gpmc->is_suspended = 0;
return 0;
}
#endif
@ -2527,74 +2643,3 @@ static __init int gpmc_init(void)
return platform_driver_register(&gpmc_driver);
}
postcore_initcall(gpmc_init);
static struct omap3_gpmc_regs gpmc_context;
void omap3_gpmc_save_context(void)
{
int i;
if (!gpmc_base)
return;
gpmc_context.sysconfig = gpmc_read_reg(GPMC_SYSCONFIG);
gpmc_context.irqenable = gpmc_read_reg(GPMC_IRQENABLE);
gpmc_context.timeout_ctrl = gpmc_read_reg(GPMC_TIMEOUT_CONTROL);
gpmc_context.config = gpmc_read_reg(GPMC_CONFIG);
gpmc_context.prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
gpmc_context.prefetch_config2 = gpmc_read_reg(GPMC_PREFETCH_CONFIG2);
gpmc_context.prefetch_control = gpmc_read_reg(GPMC_PREFETCH_CONTROL);
for (i = 0; i < gpmc_cs_num; i++) {
gpmc_context.cs_context[i].is_valid = gpmc_cs_mem_enabled(i);
if (gpmc_context.cs_context[i].is_valid) {
gpmc_context.cs_context[i].config1 =
gpmc_cs_read_reg(i, GPMC_CS_CONFIG1);
gpmc_context.cs_context[i].config2 =
gpmc_cs_read_reg(i, GPMC_CS_CONFIG2);
gpmc_context.cs_context[i].config3 =
gpmc_cs_read_reg(i, GPMC_CS_CONFIG3);
gpmc_context.cs_context[i].config4 =
gpmc_cs_read_reg(i, GPMC_CS_CONFIG4);
gpmc_context.cs_context[i].config5 =
gpmc_cs_read_reg(i, GPMC_CS_CONFIG5);
gpmc_context.cs_context[i].config6 =
gpmc_cs_read_reg(i, GPMC_CS_CONFIG6);
gpmc_context.cs_context[i].config7 =
gpmc_cs_read_reg(i, GPMC_CS_CONFIG7);
}
}
}
void omap3_gpmc_restore_context(void)
{
int i;
if (!gpmc_base)
return;
gpmc_write_reg(GPMC_SYSCONFIG, gpmc_context.sysconfig);
gpmc_write_reg(GPMC_IRQENABLE, gpmc_context.irqenable);
gpmc_write_reg(GPMC_TIMEOUT_CONTROL, gpmc_context.timeout_ctrl);
gpmc_write_reg(GPMC_CONFIG, gpmc_context.config);
gpmc_write_reg(GPMC_PREFETCH_CONFIG1, gpmc_context.prefetch_config1);
gpmc_write_reg(GPMC_PREFETCH_CONFIG2, gpmc_context.prefetch_config2);
gpmc_write_reg(GPMC_PREFETCH_CONTROL, gpmc_context.prefetch_control);
for (i = 0; i < gpmc_cs_num; i++) {
if (gpmc_context.cs_context[i].is_valid) {
gpmc_cs_write_reg(i, GPMC_CS_CONFIG1,
gpmc_context.cs_context[i].config1);
gpmc_cs_write_reg(i, GPMC_CS_CONFIG2,
gpmc_context.cs_context[i].config2);
gpmc_cs_write_reg(i, GPMC_CS_CONFIG3,
gpmc_context.cs_context[i].config3);
gpmc_cs_write_reg(i, GPMC_CS_CONFIG4,
gpmc_context.cs_context[i].config4);
gpmc_cs_write_reg(i, GPMC_CS_CONFIG5,
gpmc_context.cs_context[i].config5);
gpmc_cs_write_reg(i, GPMC_CS_CONFIG6,
gpmc_context.cs_context[i].config6);
gpmc_cs_write_reg(i, GPMC_CS_CONFIG7,
gpmc_context.cs_context[i].config7);
}
}
}

View File

@ -71,6 +71,7 @@ static int tegra186_mc_resume(struct tegra_mc *mc)
return 0;
}
#if IS_ENABLED(CONFIG_IOMMU_API)
static void tegra186_mc_client_sid_override(struct tegra_mc *mc,
const struct tegra_mc_client *client,
unsigned int sid)
@ -108,6 +109,7 @@ static void tegra186_mc_client_sid_override(struct tegra_mc *mc,
writel(sid, mc->regs + client->regs.sid.override);
}
}
#endif
static int tegra186_mc_probe_device(struct tegra_mc *mc, struct device *dev)
{

View File

@ -44,6 +44,7 @@ config ATH10K_SNOC
tristate "Qualcomm ath10k SNOC support"
depends on ATH10K
depends on ARCH_QCOM || COMPILE_TEST
depends on QCOM_SCM || !QCOM_SCM #if QCOM_SCM=m this can't be =y
select QCOM_QMI_HELPERS
help
This module adds support for integrated WCN3990 chip connected

View File

@ -181,6 +181,13 @@ config RESET_RASPBERRYPI
interfacing with RPi4's co-processor and model these firmware
initialization routines as reset lines.
config RESET_RZG2L_USBPHY_CTRL
tristate "Renesas RZ/G2L USBPHY control driver"
depends on ARCH_R9A07G044 || COMPILE_TEST
help
Support for USBPHY Control found on RZ/G2L family. It mainly
controls reset and power down of the USB/PHY.
config RESET_SCMI
tristate "Reset driver controlled via ARM SCMI interface"
depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
@ -207,7 +214,6 @@ config RESET_SIMPLE
- Realtek SoCs
- RCC reset controller in STM32 MCUs
- Allwinner SoCs
- ZTE's zx2967 family
- SiFive FU740 SoCs
config RESET_SOCFPGA

View File

@ -25,6 +25,7 @@ obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o
obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o
obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o
obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o

View File

@ -11,18 +11,26 @@
#include <dt-bindings/reset/qcom,sdm845-pdc.h>
#define RPMH_PDC_SYNC_RESET 0x100
#define RPMH_SDM845_PDC_SYNC_RESET 0x100
#define RPMH_SC7280_PDC_SYNC_RESET 0x1000
struct qcom_pdc_reset_map {
u8 bit;
};
struct qcom_pdc_reset_desc {
const struct qcom_pdc_reset_map *resets;
size_t num_resets;
unsigned int offset;
};
struct qcom_pdc_reset_data {
struct reset_controller_dev rcdev;
struct regmap *regmap;
const struct qcom_pdc_reset_desc *desc;
};
static const struct regmap_config sdm845_pdc_regmap_config = {
static const struct regmap_config pdc_regmap_config = {
.name = "pdc-reset",
.reg_bits = 32,
.reg_stride = 4,
@ -44,6 +52,33 @@ static const struct qcom_pdc_reset_map sdm845_pdc_resets[] = {
[PDC_MODEM_SYNC_RESET] = {9},
};
static const struct qcom_pdc_reset_desc sdm845_pdc_reset_desc = {
.resets = sdm845_pdc_resets,
.num_resets = ARRAY_SIZE(sdm845_pdc_resets),
.offset = RPMH_SDM845_PDC_SYNC_RESET,
};
static const struct qcom_pdc_reset_map sc7280_pdc_resets[] = {
[PDC_APPS_SYNC_RESET] = {0},
[PDC_SP_SYNC_RESET] = {1},
[PDC_AUDIO_SYNC_RESET] = {2},
[PDC_SENSORS_SYNC_RESET] = {3},
[PDC_AOP_SYNC_RESET] = {4},
[PDC_DEBUG_SYNC_RESET] = {5},
[PDC_GPU_SYNC_RESET] = {6},
[PDC_DISPLAY_SYNC_RESET] = {7},
[PDC_COMPUTE_SYNC_RESET] = {8},
[PDC_MODEM_SYNC_RESET] = {9},
[PDC_WLAN_RF_SYNC_RESET] = {10},
[PDC_WPSS_SYNC_RESET] = {11},
};
static const struct qcom_pdc_reset_desc sc7280_pdc_reset_desc = {
.resets = sc7280_pdc_resets,
.num_resets = ARRAY_SIZE(sc7280_pdc_resets),
.offset = RPMH_SC7280_PDC_SYNC_RESET,
};
static inline struct qcom_pdc_reset_data *to_qcom_pdc_reset_data(
struct reset_controller_dev *rcdev)
{
@ -54,19 +89,18 @@ static int qcom_pdc_control_assert(struct reset_controller_dev *rcdev,
unsigned long idx)
{
struct qcom_pdc_reset_data *data = to_qcom_pdc_reset_data(rcdev);
u32 mask = BIT(data->desc->resets[idx].bit);
return regmap_update_bits(data->regmap, RPMH_PDC_SYNC_RESET,
BIT(sdm845_pdc_resets[idx].bit),
BIT(sdm845_pdc_resets[idx].bit));
return regmap_update_bits(data->regmap, data->desc->offset, mask, mask);
}
static int qcom_pdc_control_deassert(struct reset_controller_dev *rcdev,
unsigned long idx)
{
struct qcom_pdc_reset_data *data = to_qcom_pdc_reset_data(rcdev);
u32 mask = BIT(data->desc->resets[idx].bit);
return regmap_update_bits(data->regmap, RPMH_PDC_SYNC_RESET,
BIT(sdm845_pdc_resets[idx].bit), 0);
return regmap_update_bits(data->regmap, data->desc->offset, mask, 0);
}
static const struct reset_control_ops qcom_pdc_reset_ops = {
@ -76,22 +110,27 @@ static const struct reset_control_ops qcom_pdc_reset_ops = {
static int qcom_pdc_reset_probe(struct platform_device *pdev)
{
const struct qcom_pdc_reset_desc *desc;
struct qcom_pdc_reset_data *data;
struct device *dev = &pdev->dev;
void __iomem *base;
struct resource *res;
desc = device_get_match_data(&pdev->dev);
if (!desc)
return -EINVAL;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->desc = desc;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
data->regmap = devm_regmap_init_mmio(dev, base,
&sdm845_pdc_regmap_config);
data->regmap = devm_regmap_init_mmio(dev, base, &pdc_regmap_config);
if (IS_ERR(data->regmap)) {
dev_err(dev, "Unable to initialize regmap\n");
return PTR_ERR(data->regmap);
@ -99,14 +138,15 @@ static int qcom_pdc_reset_probe(struct platform_device *pdev)
data->rcdev.owner = THIS_MODULE;
data->rcdev.ops = &qcom_pdc_reset_ops;
data->rcdev.nr_resets = ARRAY_SIZE(sdm845_pdc_resets);
data->rcdev.nr_resets = desc->num_resets;
data->rcdev.of_node = dev->of_node;
return devm_reset_controller_register(dev, &data->rcdev);
}
static const struct of_device_id qcom_pdc_reset_of_match[] = {
{ .compatible = "qcom,sdm845-pdc-global" },
{ .compatible = "qcom,sc7280-pdc-global", .data = &sc7280_pdc_reset_desc },
{ .compatible = "qcom,sdm845-pdc-global", .data = &sdm845_pdc_reset_desc },
{}
};
MODULE_DEVICE_TABLE(of, qcom_pdc_reset_of_match);

View File

@ -0,0 +1,175 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ/G2L USBPHY control driver
*
* Copyright (C) 2021 Renesas Electronics Corporation
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/reset-controller.h>
#define RESET 0x000
#define RESET_SEL_PLLRESET BIT(12)
#define RESET_PLLRESET BIT(8)
#define RESET_SEL_P2RESET BIT(5)
#define RESET_SEL_P1RESET BIT(4)
#define RESET_PHYRST_2 BIT(1)
#define RESET_PHYRST_1 BIT(0)
#define PHY_RESET_PORT2 (RESET_SEL_P2RESET | RESET_PHYRST_2)
#define PHY_RESET_PORT1 (RESET_SEL_P1RESET | RESET_PHYRST_1)
#define NUM_PORTS 2
struct rzg2l_usbphy_ctrl_priv {
struct reset_controller_dev rcdev;
struct reset_control *rstc;
void __iomem *base;
spinlock_t lock;
};
#define rcdev_to_priv(x) container_of(x, struct rzg2l_usbphy_ctrl_priv, rcdev)
static int rzg2l_usbphy_ctrl_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct rzg2l_usbphy_ctrl_priv *priv = rcdev_to_priv(rcdev);
u32 port_mask = PHY_RESET_PORT1 | PHY_RESET_PORT2;
void __iomem *base = priv->base;
unsigned long flags;
u32 val;
spin_lock_irqsave(&priv->lock, flags);
val = readl(base + RESET);
val |= id ? PHY_RESET_PORT2 : PHY_RESET_PORT1;
if (port_mask == (val & port_mask))
val |= RESET_PLLRESET;
writel(val, base + RESET);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int rzg2l_usbphy_ctrl_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct rzg2l_usbphy_ctrl_priv *priv = rcdev_to_priv(rcdev);
void __iomem *base = priv->base;
unsigned long flags;
u32 val;
spin_lock_irqsave(&priv->lock, flags);
val = readl(base + RESET);
val |= RESET_SEL_PLLRESET;
val &= ~(RESET_PLLRESET | (id ? PHY_RESET_PORT2 : PHY_RESET_PORT1));
writel(val, base + RESET);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int rzg2l_usbphy_ctrl_status(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct rzg2l_usbphy_ctrl_priv *priv = rcdev_to_priv(rcdev);
u32 port_mask;
port_mask = id ? PHY_RESET_PORT2 : PHY_RESET_PORT1;
return !!(readl(priv->base + RESET) & port_mask);
}
static const struct of_device_id rzg2l_usbphy_ctrl_match_table[] = {
{ .compatible = "renesas,rzg2l-usbphy-ctrl" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, rzg2l_usbphy_ctrl_match_table);
static const struct reset_control_ops rzg2l_usbphy_ctrl_reset_ops = {
.assert = rzg2l_usbphy_ctrl_assert,
.deassert = rzg2l_usbphy_ctrl_deassert,
.status = rzg2l_usbphy_ctrl_status,
};
static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rzg2l_usbphy_ctrl_priv *priv;
unsigned long flags;
int error;
u32 val;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
if (IS_ERR(priv->rstc))
return dev_err_probe(dev, PTR_ERR(priv->rstc),
"failed to get reset\n");
reset_control_deassert(priv->rstc);
priv->rcdev.ops = &rzg2l_usbphy_ctrl_reset_ops;
priv->rcdev.of_reset_n_cells = 1;
priv->rcdev.nr_resets = NUM_PORTS;
priv->rcdev.of_node = dev->of_node;
priv->rcdev.dev = dev;
error = devm_reset_controller_register(dev, &priv->rcdev);
if (error)
return error;
spin_lock_init(&priv->lock);
dev_set_drvdata(dev, priv);
pm_runtime_enable(&pdev->dev);
pm_runtime_resume_and_get(&pdev->dev);
/* 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);
return 0;
}
static int rzg2l_usbphy_ctrl_remove(struct platform_device *pdev)
{
struct rzg2l_usbphy_ctrl_priv *priv = dev_get_drvdata(&pdev->dev);
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
reset_control_assert(priv->rstc);
return 0;
}
static struct platform_driver rzg2l_usbphy_ctrl_driver = {
.driver = {
.name = "rzg2l_usbphy_ctrl",
.of_match_table = rzg2l_usbphy_ctrl_match_table,
},
.probe = rzg2l_usbphy_ctrl_probe,
.remove = rzg2l_usbphy_ctrl_remove,
};
module_platform_driver(rzg2l_usbphy_ctrl_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Renesas RZ/G2L USBPHY Control");
MODULE_AUTHOR("biju.das.jz@bp.renesas.com>");

View File

@ -71,6 +71,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8173[] = {
.ctl_offs = SPM_MFG_ASYNC_PWR_CON,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = 0,
.caps = MTK_SCPD_DOMAIN_SUPPLY,
},
[MT8173_POWER_DOMAIN_MFG_2D] = {
.name = "mfg_2d",

View File

@ -28,25 +28,32 @@
static const struct mtk_mmsys_routes mmsys_mt8183_routing_table[] = {
{
DDP_COMPONENT_OVL0, DDP_COMPONENT_OVL_2L0,
MT8183_DISP_OVL0_MOUT_EN, MT8183_OVL0_MOUT_EN_OVL0_2L
MT8183_DISP_OVL0_MOUT_EN, MT8183_OVL0_MOUT_EN_OVL0_2L,
MT8183_OVL0_MOUT_EN_OVL0_2L
}, {
DDP_COMPONENT_OVL_2L0, DDP_COMPONENT_RDMA0,
MT8183_DISP_OVL0_2L_MOUT_EN, MT8183_OVL0_2L_MOUT_EN_DISP_PATH0
MT8183_DISP_OVL0_2L_MOUT_EN, MT8183_OVL0_2L_MOUT_EN_DISP_PATH0,
MT8183_OVL0_2L_MOUT_EN_DISP_PATH0
}, {
DDP_COMPONENT_OVL_2L1, DDP_COMPONENT_RDMA1,
MT8183_DISP_OVL1_2L_MOUT_EN, MT8183_OVL1_2L_MOUT_EN_RDMA1
MT8183_DISP_OVL1_2L_MOUT_EN, MT8183_OVL1_2L_MOUT_EN_RDMA1,
MT8183_OVL1_2L_MOUT_EN_RDMA1
}, {
DDP_COMPONENT_DITHER, DDP_COMPONENT_DSI0,
MT8183_DISP_DITHER0_MOUT_EN, MT8183_DITHER0_MOUT_IN_DSI0
MT8183_DISP_DITHER0_MOUT_EN, MT8183_DITHER0_MOUT_IN_DSI0,
MT8183_DITHER0_MOUT_IN_DSI0
}, {
DDP_COMPONENT_OVL_2L0, DDP_COMPONENT_RDMA0,
MT8183_DISP_PATH0_SEL_IN, MT8183_DISP_PATH0_SEL_IN_OVL0_2L
MT8183_DISP_PATH0_SEL_IN, MT8183_DISP_PATH0_SEL_IN_OVL0_2L,
MT8183_DISP_PATH0_SEL_IN_OVL0_2L
}, {
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0,
MT8183_DISP_DPI0_SEL_IN, MT8183_DPI0_SEL_IN_RDMA1
MT8183_DISP_DPI0_SEL_IN, MT8183_DPI0_SEL_IN_RDMA1,
MT8183_DPI0_SEL_IN_RDMA1
}, {
DDP_COMPONENT_RDMA0, DDP_COMPONENT_COLOR0,
MT8183_DISP_RDMA0_SOUT_SEL_IN, MT8183_RDMA0_SOUT_COLOR0
MT8183_DISP_RDMA0_SOUT_SEL_IN, MT8183_RDMA0_SOUT_COLOR0,
MT8183_RDMA0_SOUT_COLOR0
}
};

View File

@ -0,0 +1,60 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __SOC_MEDIATEK_MT8365_MMSYS_H
#define __SOC_MEDIATEK_MT8365_MMSYS_H
#define MT8365_DISP_REG_CONFIG_DISP_OVL0_MOUT_EN 0xf3c
#define MT8365_DISP_REG_CONFIG_DISP_RDMA0_SOUT_SEL 0xf4c
#define MT8365_DISP_REG_CONFIG_DISP_DITHER0_MOUT_EN 0xf50
#define MT8365_DISP_REG_CONFIG_DISP_RDMA0_SEL_IN 0xf54
#define MT8365_DISP_REG_CONFIG_DISP_RDMA0_RSZ0_SEL_IN 0xf60
#define MT8365_DISP_REG_CONFIG_DISP_COLOR0_SEL_IN 0xf64
#define MT8365_DISP_REG_CONFIG_DISP_DSI0_SEL_IN 0xf68
#define MT8365_RDMA0_SOUT_COLOR0 0x1
#define MT8365_DITHER_MOUT_EN_DSI0 0x1
#define MT8365_DSI0_SEL_IN_DITHER 0x1
#define MT8365_RDMA0_SEL_IN_OVL0 0x0
#define MT8365_RDMA0_RSZ0_SEL_IN_RDMA0 0x0
#define MT8365_DISP_COLOR_SEL_IN_COLOR0 0x0
#define MT8365_OVL0_MOUT_PATH0_SEL BIT(0)
static const struct mtk_mmsys_routes mt8365_mmsys_routing_table[] = {
{
DDP_COMPONENT_OVL0, DDP_COMPONENT_RDMA0,
MT8365_DISP_REG_CONFIG_DISP_OVL0_MOUT_EN,
MT8365_OVL0_MOUT_PATH0_SEL, MT8365_OVL0_MOUT_PATH0_SEL
},
{
DDP_COMPONENT_OVL0, DDP_COMPONENT_RDMA0,
MT8365_DISP_REG_CONFIG_DISP_RDMA0_SEL_IN,
MT8365_RDMA0_SEL_IN_OVL0, MT8365_RDMA0_SEL_IN_OVL0
},
{
DDP_COMPONENT_RDMA0, DDP_COMPONENT_COLOR0,
MT8365_DISP_REG_CONFIG_DISP_RDMA0_SOUT_SEL,
MT8365_RDMA0_SOUT_COLOR0, MT8365_RDMA0_SOUT_COLOR0
},
{
DDP_COMPONENT_COLOR0, DDP_COMPONENT_CCORR,
MT8365_DISP_REG_CONFIG_DISP_COLOR0_SEL_IN,
MT8365_DISP_COLOR_SEL_IN_COLOR0,MT8365_DISP_COLOR_SEL_IN_COLOR0
},
{
DDP_COMPONENT_DITHER, DDP_COMPONENT_DSI0,
MT8365_DISP_REG_CONFIG_DISP_DITHER0_MOUT_EN,
MT8365_DITHER_MOUT_EN_DSI0, MT8365_DITHER_MOUT_EN_DSI0
},
{
DDP_COMPONENT_DITHER, DDP_COMPONENT_DSI0,
MT8365_DISP_REG_CONFIG_DISP_DSI0_SEL_IN,
MT8365_DSI0_SEL_IN_DITHER, MT8365_DSI0_SEL_IN_DITHER
},
{
DDP_COMPONENT_RDMA0, DDP_COMPONENT_COLOR0,
MT8365_DISP_REG_CONFIG_DISP_RDMA0_RSZ0_SEL_IN,
MT8365_RDMA0_RSZ0_SEL_IN_RDMA0, MT8365_RDMA0_RSZ0_SEL_IN_RDMA0
},
};
#endif /* __SOC_MEDIATEK_MT8365_MMSYS_H */

View File

@ -13,6 +13,7 @@
#include "mtk-mmsys.h"
#include "mt8167-mmsys.h"
#include "mt8183-mmsys.h"
#include "mt8365-mmsys.h"
static const struct mtk_mmsys_driver_data mt2701_mmsys_driver_data = {
.clk_driver = "clk-mt2701-mm",
@ -52,6 +53,12 @@ static const struct mtk_mmsys_driver_data mt8183_mmsys_driver_data = {
.num_routes = ARRAY_SIZE(mmsys_mt8183_routing_table),
};
static const struct mtk_mmsys_driver_data mt8365_mmsys_driver_data = {
.clk_driver = "clk-mt8365-mm",
.routes = mt8365_mmsys_routing_table,
.num_routes = ARRAY_SIZE(mt8365_mmsys_routing_table),
};
struct mtk_mmsys {
void __iomem *regs;
const struct mtk_mmsys_driver_data *data;
@ -68,7 +75,9 @@ void mtk_mmsys_ddp_connect(struct device *dev,
for (i = 0; i < mmsys->data->num_routes; i++)
if (cur == routes[i].from_comp && next == routes[i].to_comp) {
reg = readl_relaxed(mmsys->regs + routes[i].addr) | routes[i].val;
reg = readl_relaxed(mmsys->regs + routes[i].addr);
reg &= ~routes[i].mask;
reg |= routes[i].val;
writel_relaxed(reg, mmsys->regs + routes[i].addr);
}
}
@ -85,7 +94,8 @@ void mtk_mmsys_ddp_disconnect(struct device *dev,
for (i = 0; i < mmsys->data->num_routes; i++)
if (cur == routes[i].from_comp && next == routes[i].to_comp) {
reg = readl_relaxed(mmsys->regs + routes[i].addr) & ~routes[i].val;
reg = readl_relaxed(mmsys->regs + routes[i].addr);
reg &= ~routes[i].mask;
writel_relaxed(reg, mmsys->regs + routes[i].addr);
}
}
@ -157,6 +167,10 @@ static const struct of_device_id of_match_mtk_mmsys[] = {
.compatible = "mediatek,mt8183-mmsys",
.data = &mt8183_mmsys_driver_data,
},
{
.compatible = "mediatek,mt8365-mmsys",
.data = &mt8365_mmsys_driver_data,
},
{ }
};

View File

@ -35,41 +35,54 @@
#define RDMA0_SOUT_DSI1 0x1
#define RDMA0_SOUT_DSI2 0x4
#define RDMA0_SOUT_DSI3 0x5
#define RDMA0_SOUT_MASK 0x7
#define RDMA1_SOUT_DPI0 0x2
#define RDMA1_SOUT_DPI1 0x3
#define RDMA1_SOUT_DSI1 0x1
#define RDMA1_SOUT_DSI2 0x4
#define RDMA1_SOUT_DSI3 0x5
#define RDMA1_SOUT_MASK 0x7
#define RDMA2_SOUT_DPI0 0x2
#define RDMA2_SOUT_DPI1 0x3
#define RDMA2_SOUT_DSI1 0x1
#define RDMA2_SOUT_DSI2 0x4
#define RDMA2_SOUT_DSI3 0x5
#define RDMA2_SOUT_MASK 0x7
#define DPI0_SEL_IN_RDMA1 0x1
#define DPI0_SEL_IN_RDMA2 0x3
#define DPI0_SEL_IN_MASK 0x3
#define DPI1_SEL_IN_RDMA1 (0x1 << 8)
#define DPI1_SEL_IN_RDMA2 (0x3 << 8)
#define DPI1_SEL_IN_MASK (0x3 << 8)
#define DSI0_SEL_IN_RDMA1 0x1
#define DSI0_SEL_IN_RDMA2 0x4
#define DSI0_SEL_IN_MASK 0x7
#define DSI1_SEL_IN_RDMA1 0x1
#define DSI1_SEL_IN_RDMA2 0x4
#define DSI1_SEL_IN_MASK 0x7
#define DSI2_SEL_IN_RDMA1 (0x1 << 16)
#define DSI2_SEL_IN_RDMA2 (0x4 << 16)
#define DSI2_SEL_IN_MASK (0x7 << 16)
#define DSI3_SEL_IN_RDMA1 (0x1 << 16)
#define DSI3_SEL_IN_RDMA2 (0x4 << 16)
#define DSI3_SEL_IN_MASK (0x7 << 16)
#define COLOR1_SEL_IN_OVL1 0x1
#define OVL_MOUT_EN_RDMA 0x1
#define BLS_TO_DSI_RDMA1_TO_DPI1 0x8
#define BLS_TO_DPI_RDMA1_TO_DSI 0x2
#define BLS_RDMA1_DSI_DPI_MASK 0xf
#define DSI_SEL_IN_BLS 0x0
#define DPI_SEL_IN_BLS 0x0
#define DPI_SEL_IN_MASK 0x1
#define DSI_SEL_IN_RDMA 0x1
#define DSI_SEL_IN_MASK 0x1
struct mtk_mmsys_routes {
u32 from_comp;
u32 to_comp;
u32 addr;
u32 mask;
u32 val;
};
@ -91,124 +104,168 @@ struct mtk_mmsys_driver_data {
static const struct mtk_mmsys_routes mmsys_default_routing_table[] = {
{
DDP_COMPONENT_BLS, DDP_COMPONENT_DSI0,
DISP_REG_CONFIG_OUT_SEL, BLS_TO_DSI_RDMA1_TO_DPI1
DISP_REG_CONFIG_OUT_SEL, BLS_RDMA1_DSI_DPI_MASK,
BLS_TO_DSI_RDMA1_TO_DPI1
}, {
DDP_COMPONENT_BLS, DDP_COMPONENT_DSI0,
DISP_REG_CONFIG_DSI_SEL, DSI_SEL_IN_BLS
DISP_REG_CONFIG_DSI_SEL, DSI_SEL_IN_MASK,
DSI_SEL_IN_BLS
}, {
DDP_COMPONENT_BLS, DDP_COMPONENT_DPI0,
DISP_REG_CONFIG_OUT_SEL, BLS_TO_DPI_RDMA1_TO_DSI
DISP_REG_CONFIG_OUT_SEL, BLS_RDMA1_DSI_DPI_MASK,
BLS_TO_DPI_RDMA1_TO_DSI
}, {
DDP_COMPONENT_BLS, DDP_COMPONENT_DPI0,
DISP_REG_CONFIG_DSI_SEL, DSI_SEL_IN_RDMA
DISP_REG_CONFIG_DSI_SEL, DSI_SEL_IN_MASK,
DSI_SEL_IN_RDMA
}, {
DDP_COMPONENT_BLS, DDP_COMPONENT_DPI0,
DISP_REG_CONFIG_DPI_SEL, DPI_SEL_IN_BLS
DISP_REG_CONFIG_DPI_SEL, DPI_SEL_IN_MASK,
DPI_SEL_IN_BLS
}, {
DDP_COMPONENT_GAMMA, DDP_COMPONENT_RDMA1,
DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN, GAMMA_MOUT_EN_RDMA1
DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN, GAMMA_MOUT_EN_RDMA1,
GAMMA_MOUT_EN_RDMA1
}, {
DDP_COMPONENT_OD0, DDP_COMPONENT_RDMA0,
DISP_REG_CONFIG_DISP_OD_MOUT_EN, OD_MOUT_EN_RDMA0
DISP_REG_CONFIG_DISP_OD_MOUT_EN, OD_MOUT_EN_RDMA0,
OD_MOUT_EN_RDMA0
}, {
DDP_COMPONENT_OD1, DDP_COMPONENT_RDMA1,
DISP_REG_CONFIG_DISP_OD_MOUT_EN, OD1_MOUT_EN_RDMA1
DISP_REG_CONFIG_DISP_OD_MOUT_EN, OD1_MOUT_EN_RDMA1,
OD1_MOUT_EN_RDMA1
}, {
DDP_COMPONENT_OVL0, DDP_COMPONENT_COLOR0,
DISP_REG_CONFIG_DISP_OVL0_MOUT_EN, OVL0_MOUT_EN_COLOR0
DISP_REG_CONFIG_DISP_OVL0_MOUT_EN, OVL0_MOUT_EN_COLOR0,
OVL0_MOUT_EN_COLOR0
}, {
DDP_COMPONENT_OVL0, DDP_COMPONENT_COLOR0,
DISP_REG_CONFIG_DISP_COLOR0_SEL_IN, COLOR0_SEL_IN_OVL0
DISP_REG_CONFIG_DISP_COLOR0_SEL_IN, COLOR0_SEL_IN_OVL0,
COLOR0_SEL_IN_OVL0
}, {
DDP_COMPONENT_OVL0, DDP_COMPONENT_RDMA0,
DISP_REG_CONFIG_DISP_OVL_MOUT_EN, OVL_MOUT_EN_RDMA
DISP_REG_CONFIG_DISP_OVL_MOUT_EN, OVL_MOUT_EN_RDMA,
OVL_MOUT_EN_RDMA
}, {
DDP_COMPONENT_OVL1, DDP_COMPONENT_COLOR1,
DISP_REG_CONFIG_DISP_OVL1_MOUT_EN, OVL1_MOUT_EN_COLOR1
DISP_REG_CONFIG_DISP_OVL1_MOUT_EN, OVL1_MOUT_EN_COLOR1,
OVL1_MOUT_EN_COLOR1
}, {
DDP_COMPONENT_OVL1, DDP_COMPONENT_COLOR1,
DISP_REG_CONFIG_DISP_COLOR1_SEL_IN, COLOR1_SEL_IN_OVL1
DISP_REG_CONFIG_DISP_COLOR1_SEL_IN, COLOR1_SEL_IN_OVL1,
COLOR1_SEL_IN_OVL1
}, {
DDP_COMPONENT_RDMA0, DDP_COMPONENT_DPI0,
DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_DPI0
DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK,
RDMA0_SOUT_DPI0
}, {
DDP_COMPONENT_RDMA0, DDP_COMPONENT_DPI1,
DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_DPI1
DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK,
RDMA0_SOUT_DPI1
}, {
DDP_COMPONENT_RDMA0, DDP_COMPONENT_DSI1,
DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_DSI1
DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK,
RDMA0_SOUT_DSI1
}, {
DDP_COMPONENT_RDMA0, DDP_COMPONENT_DSI2,
DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_DSI2
DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK,
RDMA0_SOUT_DSI2
}, {
DDP_COMPONENT_RDMA0, DDP_COMPONENT_DSI3,
DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_DSI3
DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK,
RDMA0_SOUT_DSI3
}, {
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0,
DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_DPI0
DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK,
RDMA1_SOUT_DPI0
}, {
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0,
DISP_REG_CONFIG_DPI_SEL_IN, DPI0_SEL_IN_RDMA1
DISP_REG_CONFIG_DPI_SEL_IN, DPI0_SEL_IN_MASK,
DPI0_SEL_IN_RDMA1
}, {
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI1,
DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_DPI1
DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK,
RDMA1_SOUT_DPI1
}, {
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI1,
DISP_REG_CONFIG_DPI_SEL_IN, DPI1_SEL_IN_RDMA1
DISP_REG_CONFIG_DPI_SEL_IN, DPI1_SEL_IN_MASK,
DPI1_SEL_IN_RDMA1
}, {
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI0,
DISP_REG_CONFIG_DSIE_SEL_IN, DSI0_SEL_IN_RDMA1
DISP_REG_CONFIG_DSIE_SEL_IN, DSI0_SEL_IN_MASK,
DSI0_SEL_IN_RDMA1
}, {
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI1,
DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_DSI1
DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK,
RDMA1_SOUT_DSI1
}, {
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI1,
DISP_REG_CONFIG_DSIO_SEL_IN, DSI1_SEL_IN_RDMA1
DISP_REG_CONFIG_DSIO_SEL_IN, DSI1_SEL_IN_MASK,
DSI1_SEL_IN_RDMA1
}, {
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI2,
DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_DSI2
DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK,
RDMA1_SOUT_DSI2
}, {
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI2,
DISP_REG_CONFIG_DSIE_SEL_IN, DSI2_SEL_IN_RDMA1
DISP_REG_CONFIG_DSIE_SEL_IN, DSI2_SEL_IN_MASK,
DSI2_SEL_IN_RDMA1
}, {
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI3,
DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_DSI3
DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK,
RDMA1_SOUT_DSI3
}, {
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI3,
DISP_REG_CONFIG_DSIO_SEL_IN, DSI3_SEL_IN_RDMA1
DISP_REG_CONFIG_DSIO_SEL_IN, DSI3_SEL_IN_MASK,
DSI3_SEL_IN_RDMA1
}, {
DDP_COMPONENT_RDMA2, DDP_COMPONENT_DPI0,
DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_DPI0
DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK,
RDMA2_SOUT_DPI0
}, {
DDP_COMPONENT_RDMA2, DDP_COMPONENT_DPI0,
DISP_REG_CONFIG_DPI_SEL_IN, DPI0_SEL_IN_RDMA2
DISP_REG_CONFIG_DPI_SEL_IN, DPI0_SEL_IN_MASK,
DPI0_SEL_IN_RDMA2
}, {
DDP_COMPONENT_RDMA2, DDP_COMPONENT_DPI1,
DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_DPI1
DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK,
RDMA2_SOUT_DPI1
}, {
DDP_COMPONENT_RDMA2, DDP_COMPONENT_DPI1,
DISP_REG_CONFIG_DPI_SEL_IN, DPI1_SEL_IN_RDMA2
DISP_REG_CONFIG_DPI_SEL_IN, DPI1_SEL_IN_MASK,
DPI1_SEL_IN_RDMA2
}, {
DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI0,
DISP_REG_CONFIG_DSIE_SEL_IN, DSI0_SEL_IN_RDMA2
DISP_REG_CONFIG_DSIE_SEL_IN, DSI0_SEL_IN_MASK,
DSI0_SEL_IN_RDMA2
}, {
DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI1,
DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_DSI1
DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK,
RDMA2_SOUT_DSI1
}, {
DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI1,
DISP_REG_CONFIG_DSIO_SEL_IN, DSI1_SEL_IN_RDMA2
DISP_REG_CONFIG_DSIO_SEL_IN, DSI1_SEL_IN_MASK,
DSI1_SEL_IN_RDMA2
}, {
DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI2,
DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_DSI2
DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK,
RDMA2_SOUT_DSI2
}, {
DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI2,
DISP_REG_CONFIG_DSIE_SEL_IN, DSI2_SEL_IN_RDMA2
DISP_REG_CONFIG_DSIE_SEL_IN, DSI2_SEL_IN_MASK,
DSI2_SEL_IN_RDMA2
}, {
DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI3,
DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_DSI3
DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK,
RDMA2_SOUT_DSI3
}, {
DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI3,
DISP_REG_CONFIG_DSIO_SEL_IN, DSI3_SEL_IN_RDMA2
DISP_REG_CONFIG_DSIO_SEL_IN, DSI3_SEL_IN_MASK,
DSI3_SEL_IN_RDMA2
}, {
DDP_COMPONENT_UFOE, DDP_COMPONENT_DSI0,
DISP_REG_CONFIG_DISP_UFOE_MOUT_EN, UFOE_MOUT_EN_DSI0,
UFOE_MOUT_EN_DSI0
}
};

View File

@ -60,7 +60,7 @@
#define BUS_PROT_UPDATE_TOPAXI(_mask) \
BUS_PROT_UPDATE(_mask, \
INFRA_TOPAXI_PROTECTEN, \
INFRA_TOPAXI_PROTECTEN_CLR, \
INFRA_TOPAXI_PROTECTEN, \
INFRA_TOPAXI_PROTECTSTA1)
struct scpsys_bus_prot_data {

View File

@ -801,38 +801,6 @@ unlock:
return ret;
}
static int cpr_read_efuse(struct device *dev, const char *cname, u32 *data)
{
struct nvmem_cell *cell;
ssize_t len;
char *ret;
int i;
*data = 0;
cell = nvmem_cell_get(dev, cname);
if (IS_ERR(cell)) {
if (PTR_ERR(cell) != -EPROBE_DEFER)
dev_err(dev, "undefined cell %s\n", cname);
return PTR_ERR(cell);
}
ret = nvmem_cell_read(cell, &len);
nvmem_cell_put(cell);
if (IS_ERR(ret)) {
dev_err(dev, "can't read cell %s\n", cname);
return PTR_ERR(ret);
}
for (i = 0; i < len; i++)
*data |= ret[i] << (8 * i);
kfree(ret);
dev_dbg(dev, "efuse read(%s) = %x, bytes %zd\n", cname, *data, len);
return 0;
}
static int
cpr_populate_ring_osc_idx(struct cpr_drv *drv)
{
@ -843,8 +811,7 @@ cpr_populate_ring_osc_idx(struct cpr_drv *drv)
int ret;
for (; fuse < end; fuse++, fuses++) {
ret = cpr_read_efuse(drv->dev, fuses->ring_osc,
&data);
ret = nvmem_cell_read_variable_le_u32(drv->dev, fuses->ring_osc, &data);
if (ret)
return ret;
fuse->ring_osc_idx = data;
@ -863,7 +830,7 @@ static int cpr_read_fuse_uV(const struct cpr_desc *desc,
u32 bits = 0;
int ret;
ret = cpr_read_efuse(drv->dev, init_v_efuse, &bits);
ret = nvmem_cell_read_variable_le_u32(drv->dev, init_v_efuse, &bits);
if (ret)
return ret;
@ -932,7 +899,7 @@ static int cpr_fuse_corner_init(struct cpr_drv *drv)
}
/* Populate target quotient by scaling */
ret = cpr_read_efuse(drv->dev, fuses->quotient, &fuse->quot);
ret = nvmem_cell_read_variable_le_u32(drv->dev, fuses->quotient, &fuse->quot);
if (ret)
return ret;
@ -1001,7 +968,7 @@ static int cpr_calculate_scaling(const char *quot_offset,
prev_fuse = fuse - 1;
if (quot_offset) {
ret = cpr_read_efuse(drv->dev, quot_offset, &quot_diff);
ret = nvmem_cell_read_variable_le_u32(drv->dev, quot_offset, &quot_diff);
if (ret)
return ret;
@ -1701,7 +1668,7 @@ static int cpr_probe(struct platform_device *pdev)
* initialized after attaching to the power domain,
* since it depends on the CPU's OPP table.
*/
ret = cpr_read_efuse(dev, "cpr_fuse_revision", &cpr_rev);
ret = nvmem_cell_read_variable_le_u32(dev, "cpr_fuse_revision", &cpr_rev);
if (ret)
return ret;

View File

@ -166,6 +166,8 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
metadata = qcom_mdt_read_metadata(fw, &metadata_len);
if (IS_ERR(metadata)) {
ret = PTR_ERR(metadata);
dev_err(dev, "error %d reading firmware %s metadata\n",
ret, fw_name);
goto out;
}
@ -173,7 +175,9 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
kfree(metadata);
if (ret) {
dev_err(dev, "invalid firmware metadata\n");
/* Invalid firmware metadata */
dev_err(dev, "error %d initializing firmware %s\n",
ret, fw_name);
goto out;
}
}
@ -199,7 +203,9 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
ret = qcom_scm_pas_mem_setup(pas_id, mem_phys,
max_addr - min_addr);
if (ret) {
dev_err(dev, "unable to setup relocation\n");
/* Unable to set up relocation */
dev_err(dev, "error %d setting up firmware %s\n",
ret, fw_name);
goto out;
}
}
@ -243,9 +249,8 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
if (phdr->p_filesz && phdr->p_offset < fw->size) {
/* Firmware is large enough to be non-split */
if (phdr->p_offset + phdr->p_filesz > fw->size) {
dev_err(dev,
"failed to load segment %d from truncated file %s\n",
i, firmware);
dev_err(dev, "file %s segment %d would be truncated\n",
fw_name, i);
ret = -EINVAL;
break;
}
@ -257,7 +262,8 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
ret = request_firmware_into_buf(&seg_fw, fw_name, dev,
ptr, phdr->p_filesz);
if (ret) {
dev_err(dev, "failed to load %s\n", fw_name);
dev_err(dev, "error %d loading %s\n",
ret, fw_name);
break;
}

View File

@ -104,7 +104,6 @@ static const char * const icc_path_names[] = {"qup-core", "qup-config",
#define GENI_OUTPUT_CTRL 0x24
#define GENI_CGC_CTRL 0x28
#define GENI_CLK_CTRL_RO 0x60
#define GENI_IF_DISABLE_RO 0x64
#define GENI_FW_S_REVISION_RO 0x6c
#define SE_GENI_BYTE_GRAN 0x254
#define SE_GENI_TX_PACKING_CFG0 0x260
@ -322,6 +321,30 @@ static void geni_se_select_dma_mode(struct geni_se *se)
writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN);
}
static void geni_se_select_gpi_mode(struct geni_se *se)
{
u32 val;
geni_se_irq_clear(se);
writel(0, se->base + SE_IRQ_EN);
val = readl(se->base + SE_GENI_S_IRQ_EN);
val &= ~S_CMD_DONE_EN;
writel(val, se->base + SE_GENI_S_IRQ_EN);
val = readl(se->base + SE_GENI_M_IRQ_EN);
val &= ~(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN |
M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
writel(val, se->base + SE_GENI_M_IRQ_EN);
writel(GENI_DMA_MODE_EN, se->base + SE_GENI_DMA_MODE_EN);
val = readl(se->base + SE_GSI_EVENT_EN);
val |= (DMA_RX_EVENT_EN | DMA_TX_EVENT_EN | GENI_M_EVENT_EN | GENI_S_EVENT_EN);
writel(val, se->base + SE_GSI_EVENT_EN);
}
/**
* geni_se_select_mode() - Select the serial engine transfer mode
* @se: Pointer to the concerned serial engine.
@ -329,7 +352,7 @@ static void geni_se_select_dma_mode(struct geni_se *se)
*/
void geni_se_select_mode(struct geni_se *se, enum geni_se_xfer_mode mode)
{
WARN_ON(mode != GENI_SE_FIFO && mode != GENI_SE_DMA);
WARN_ON(mode != GENI_SE_FIFO && mode != GENI_SE_DMA && mode != GENI_GPI_DMA);
switch (mode) {
case GENI_SE_FIFO:
@ -338,6 +361,9 @@ void geni_se_select_mode(struct geni_se *se, enum geni_se_xfer_mode mode)
case GENI_SE_DMA:
geni_se_select_dma_mode(se);
break;
case GENI_GPI_DMA:
geni_se_select_gpi_mode(se);
break;
case GENI_SE_INVALID:
default:
break;

View File

@ -476,12 +476,12 @@ static int qmp_cooling_device_add(struct qmp *qmp,
static int qmp_cooling_devices_register(struct qmp *qmp)
{
struct device_node *np, *child;
int count = QMP_NUM_COOLING_RESOURCES;
int count = 0;
int ret;
np = qmp->dev->of_node;
qmp->cooling_devs = devm_kcalloc(qmp->dev, count,
qmp->cooling_devs = devm_kcalloc(qmp->dev, QMP_NUM_COOLING_RESOURCES,
sizeof(*qmp->cooling_devs),
GFP_KERNEL);
@ -497,12 +497,16 @@ static int qmp_cooling_devices_register(struct qmp *qmp)
goto unroll;
}
if (!count)
devm_kfree(qmp->dev, qmp->cooling_devs);
return 0;
unroll:
while (--count >= 0)
thermal_cooling_device_unregister
(qmp->cooling_devs[count].cdev);
devm_kfree(qmp->dev, qmp->cooling_devs);
return ret;
}
@ -602,6 +606,7 @@ static const struct of_device_id qmp_dt_match[] = {
{ .compatible = "qcom,sm8150-aoss-qmp", },
{ .compatible = "qcom,sm8250-aoss-qmp", },
{ .compatible = "qcom,sm8350-aoss-qmp", },
{ .compatible = "qcom,aoss-qmp", },
{}
};
MODULE_DEVICE_TABLE(of, qmp_dt_match);

View File

@ -403,12 +403,11 @@ static int rpmhpd_power_on(struct generic_pm_domain *domain)
static int rpmhpd_power_off(struct generic_pm_domain *domain)
{
struct rpmhpd *pd = domain_to_rpmhpd(domain);
int ret = 0;
int ret;
mutex_lock(&rpmhpd_lock);
ret = rpmhpd_aggregate_corner(pd, pd->level[0]);
ret = rpmhpd_aggregate_corner(pd, 0);
if (!ret)
pd->enabled = false;

View File

@ -346,6 +346,33 @@ static const struct rpmpd_desc sdm660_desc = {
.max_state = RPM_SMD_LEVEL_TURBO,
};
/* sm4250/6115 RPM Power domains */
DEFINE_RPMPD_PAIR(sm6115, vddcx, vddcx_ao, RWCX, LEVEL, 0);
DEFINE_RPMPD_VFL(sm6115, vddcx_vfl, RWCX, 0);
DEFINE_RPMPD_PAIR(sm6115, vddmx, vddmx_ao, RWMX, LEVEL, 0);
DEFINE_RPMPD_VFL(sm6115, vddmx_vfl, RWMX, 0);
DEFINE_RPMPD_LEVEL(sm6115, vdd_lpi_cx, RWLC, 0);
DEFINE_RPMPD_LEVEL(sm6115, vdd_lpi_mx, RWLM, 0);
static struct rpmpd *sm6115_rpmpds[] = {
[SM6115_VDDCX] = &sm6115_vddcx,
[SM6115_VDDCX_AO] = &sm6115_vddcx_ao,
[SM6115_VDDCX_VFL] = &sm6115_vddcx_vfl,
[SM6115_VDDMX] = &sm6115_vddmx,
[SM6115_VDDMX_AO] = &sm6115_vddmx_ao,
[SM6115_VDDMX_VFL] = &sm6115_vddmx_vfl,
[SM6115_VDD_LPI_CX] = &sm6115_vdd_lpi_cx,
[SM6115_VDD_LPI_MX] = &sm6115_vdd_lpi_mx,
};
static const struct rpmpd_desc sm6115_desc = {
.rpmpds = sm6115_rpmpds,
.num_pds = ARRAY_SIZE(sm6115_rpmpds),
.max_state = RPM_SMD_LEVEL_TURBO_NO_CPR,
};
static const struct of_device_id rpmpd_match_table[] = {
{ .compatible = "qcom,mdm9607-rpmpd", .data = &mdm9607_desc },
{ .compatible = "qcom,msm8916-rpmpd", .data = &msm8916_desc },
@ -356,6 +383,7 @@ static const struct of_device_id rpmpd_match_table[] = {
{ .compatible = "qcom,msm8998-rpmpd", .data = &msm8998_desc },
{ .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc },
{ .compatible = "qcom,sdm660-rpmpd", .data = &sdm660_desc },
{ .compatible = "qcom,sm6115-rpmpd", .data = &sm6115_desc },
{ }
};
MODULE_DEVICE_TABLE(of, rpmpd_match_table);

View File

@ -242,6 +242,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = {
{ .compatible = "qcom,rpm-msm8996" },
{ .compatible = "qcom,rpm-msm8998" },
{ .compatible = "qcom,rpm-sdm660" },
{ .compatible = "qcom,rpm-sm6115" },
{ .compatible = "qcom,rpm-sm6125" },
{ .compatible = "qcom,rpm-qcs404" },
{}

View File

@ -109,7 +109,7 @@ struct smsm_entry {
DECLARE_BITMAP(irq_enabled, 32);
DECLARE_BITMAP(irq_rising, 32);
DECLARE_BITMAP(irq_falling, 32);
u32 last_value;
unsigned long last_value;
u32 *remote_state;
u32 *subscription;
@ -204,8 +204,7 @@ static irqreturn_t smsm_intr(int irq, void *data)
u32 val;
val = readl(entry->remote_state);
changed = val ^ entry->last_value;
entry->last_value = val;
changed = val ^ xchg(&entry->last_value, val);
for_each_set_bit(i, entry->irq_enabled, 32) {
if (!(changed & BIT(i)))
@ -264,6 +263,12 @@ static void smsm_unmask_irq(struct irq_data *irqd)
struct qcom_smsm *smsm = entry->smsm;
u32 val;
/* Make sure our last cached state is up-to-date */
if (readl(entry->remote_state) & BIT(irq))
set_bit(irq, &entry->last_value);
else
clear_bit(irq, &entry->last_value);
set_bit(irq, entry->irq_enabled);
if (entry->subscription) {
@ -299,11 +304,28 @@ static int smsm_set_irq_type(struct irq_data *irqd, unsigned int type)
return 0;
}
static int smsm_get_irqchip_state(struct irq_data *irqd,
enum irqchip_irq_state which, bool *state)
{
struct smsm_entry *entry = irq_data_get_irq_chip_data(irqd);
irq_hw_number_t irq = irqd_to_hwirq(irqd);
u32 val;
if (which != IRQCHIP_STATE_LINE_LEVEL)
return -EINVAL;
val = readl(entry->remote_state);
*state = !!(val & BIT(irq));
return 0;
}
static struct irq_chip smsm_irq_chip = {
.name = "smsm",
.irq_mask = smsm_mask_irq,
.irq_unmask = smsm_unmask_irq,
.irq_set_type = smsm_set_irq_type,
.irq_get_irqchip_state = smsm_get_irqchip_state,
};
/**

View File

@ -417,8 +417,8 @@ QCOM_OPEN(chip_id, qcom_show_chip_id);
static int show_image_##type(struct seq_file *seq, void *p) \
{ \
struct smem_image_version *image_version = seq->private; \
seq_puts(seq, image_version->type); \
seq_putc(seq, '\n'); \
if (image_version->type[0] != '\0') \
seq_printf(seq, "%s\n", image_version->type); \
return 0; \
} \
static int open_image_##type(struct inode *inode, struct file *file) \

View File

@ -208,6 +208,7 @@ config ARCH_R8A77951
help
This enables support for the Renesas R-Car H3 SoC (revisions 2.0 and
later).
This includes different gradings like R-Car H3e-2G.
config ARCH_R8A77965
bool "ARM64 Platform support for R-Car M3-N"
@ -229,6 +230,7 @@ config ARCH_R8A77961
select SYSC_R8A77961
help
This enables support for the Renesas R-Car M3-W+ SoC.
This includes different gradings like R-Car M3e-2G.
config ARCH_R8A77980
bool "ARM64 Platform support for R-Car V3H"

View File

@ -404,19 +404,21 @@ static int __init r8a779a0_sysc_pd_init(void)
for (i = 0; i < info->num_areas; i++) {
const struct r8a779a0_sysc_area *area = &info->areas[i];
struct r8a779a0_sysc_pd *pd;
size_t n;
if (!area->name) {
/* Skip NULLified area */
continue;
}
pd = kzalloc(sizeof(*pd) + strlen(area->name) + 1, GFP_KERNEL);
n = strlen(area->name) + 1;
pd = kzalloc(sizeof(*pd) + n, GFP_KERNEL);
if (!pd) {
error = -ENOMEM;
goto out_put;
}
strcpy(pd->name, area->name);
memcpy(pd->name, area->name, n);
pd->genpd.name = pd->name;
pd->pdr = area->pdr;
pd->flags = area->flags;

View File

@ -396,19 +396,21 @@ static int __init rcar_sysc_pd_init(void)
for (i = 0; i < info->num_areas; i++) {
const struct rcar_sysc_area *area = &info->areas[i];
struct rcar_sysc_pd *pd;
size_t n;
if (!area->name) {
/* Skip NULLified area */
continue;
}
pd = kzalloc(sizeof(*pd) + strlen(area->name) + 1, GFP_KERNEL);
n = strlen(area->name) + 1;
pd = kzalloc(sizeof(*pd) + n, GFP_KERNEL);
if (!pd) {
error = -ENOMEM;
goto out_put;
}
strcpy(pd->name, area->name);
memcpy(pd->name, area->name, n);
pd->genpd.name = pd->name;
pd->ch.chan_offs = area->chan_offs;
pd->ch.chan_bit = area->chan_bit;

View File

@ -284,11 +284,15 @@ static const struct of_device_id renesas_socs[] __initconst = {
#if defined(CONFIG_ARCH_R8A77950) || defined(CONFIG_ARCH_R8A77951)
{ .compatible = "renesas,r8a7795", .data = &soc_rcar_h3 },
#endif
#ifdef CONFIG_ARCH_R8A77951
{ .compatible = "renesas,r8a779m1", .data = &soc_rcar_h3 },
#endif
#ifdef CONFIG_ARCH_R8A77960
{ .compatible = "renesas,r8a7796", .data = &soc_rcar_m3_w },
#endif
#ifdef CONFIG_ARCH_R8A77961
{ .compatible = "renesas,r8a77961", .data = &soc_rcar_m3_w },
{ .compatible = "renesas,r8a779m3", .data = &soc_rcar_m3_w },
#endif
#ifdef CONFIG_ARCH_R8A77965
{ .compatible = "renesas,r8a77965", .data = &soc_rcar_m3_n },

View File

@ -6,8 +6,8 @@ if ARCH_ROCKCHIP || COMPILE_TEST
#
config ROCKCHIP_GRF
bool
default y
bool "Rockchip General Register Files support" if COMPILE_TEST
default y if ARCH_ROCKCHIP
help
The General Register Files are a central component providing
special additional settings registers for a lot of soc-components.

View File

@ -51,13 +51,11 @@
#define RK3399_PMUGRF_CON0_VSEL BIT(8)
#define RK3399_PMUGRF_VSEL_SUPPLY_NUM 9
struct rockchip_iodomain;
#define RK3568_PMU_GRF_IO_VSEL0 (0x0140)
#define RK3568_PMU_GRF_IO_VSEL1 (0x0144)
#define RK3568_PMU_GRF_IO_VSEL2 (0x0148)
struct rockchip_iodomain_soc_data {
int grf_offset;
const char *supply_names[MAX_SUPPLIES];
void (*init)(struct rockchip_iodomain *iod);
};
struct rockchip_iodomain;
struct rockchip_iodomain_supply {
struct rockchip_iodomain *iod;
@ -66,13 +64,62 @@ struct rockchip_iodomain_supply {
int idx;
};
struct rockchip_iodomain_soc_data {
int grf_offset;
const char *supply_names[MAX_SUPPLIES];
void (*init)(struct rockchip_iodomain *iod);
int (*write)(struct rockchip_iodomain_supply *supply, int uV);
};
struct rockchip_iodomain {
struct device *dev;
struct regmap *grf;
const struct rockchip_iodomain_soc_data *soc_data;
struct rockchip_iodomain_supply supplies[MAX_SUPPLIES];
int (*write)(struct rockchip_iodomain_supply *supply, int uV);
};
static int rk3568_iodomain_write(struct rockchip_iodomain_supply *supply, int uV)
{
struct rockchip_iodomain *iod = supply->iod;
u32 is_3v3 = uV > MAX_VOLTAGE_1_8;
u32 val0, val1;
int b;
switch (supply->idx) {
case 0: /* pmuio1 */
break;
case 1: /* pmuio2 */
b = supply->idx;
val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b));
b = supply->idx + 4;
val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0);
regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL2, val0);
regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL2, val1);
break;
case 3: /* vccio2 */
break;
case 2: /* vccio1 */
case 4: /* vccio3 */
case 5: /* vccio4 */
case 6: /* vccio5 */
case 7: /* vccio6 */
case 8: /* vccio7 */
b = supply->idx - 1;
val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b));
val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0);
regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL0, val0);
regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL1, val1);
break;
default:
return -EINVAL;
}
return 0;
}
static int rockchip_iodomain_write(struct rockchip_iodomain_supply *supply,
int uV)
{
@ -136,7 +183,7 @@ static int rockchip_iodomain_notify(struct notifier_block *nb,
return NOTIFY_BAD;
}
ret = rockchip_iodomain_write(supply, uV);
ret = supply->iod->write(supply, uV);
if (ret && event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE)
return NOTIFY_BAD;
@ -398,6 +445,22 @@ static const struct rockchip_iodomain_soc_data soc_data_rk3399_pmu = {
.init = rk3399_pmu_iodomain_init,
};
static const struct rockchip_iodomain_soc_data soc_data_rk3568_pmu = {
.grf_offset = 0x140,
.supply_names = {
"pmuio1",
"pmuio2",
"vccio1",
"vccio2",
"vccio3",
"vccio4",
"vccio5",
"vccio6",
"vccio7",
},
.write = rk3568_iodomain_write,
};
static const struct rockchip_iodomain_soc_data soc_data_rv1108 = {
.grf_offset = 0x404,
.supply_names = {
@ -469,6 +532,10 @@ static const struct of_device_id rockchip_iodomain_match[] = {
.compatible = "rockchip,rk3399-pmu-io-voltage-domain",
.data = &soc_data_rk3399_pmu
},
{
.compatible = "rockchip,rk3568-pmu-io-voltage-domain",
.data = &soc_data_rk3568_pmu
},
{
.compatible = "rockchip,rv1108-io-voltage-domain",
.data = &soc_data_rv1108
@ -502,6 +569,11 @@ static int rockchip_iodomain_probe(struct platform_device *pdev)
match = of_match_node(rockchip_iodomain_match, np);
iod->soc_data = match->data;
if (iod->soc_data->write)
iod->write = iod->soc_data->write;
else
iod->write = rockchip_iodomain_write;
parent = pdev->dev.parent;
if (parent && parent->of_node) {
iod->grf = syscon_node_to_regmap(parent->of_node);
@ -562,7 +634,7 @@ static int rockchip_iodomain_probe(struct platform_device *pdev)
supply->reg = reg;
supply->nb.notifier_call = rockchip_iodomain_notify;
ret = rockchip_iodomain_write(supply, uV);
ret = iod->write(supply, uV);
if (ret) {
supply->reg = NULL;
goto unreg_notify;

View File

@ -13,6 +13,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
@ -210,6 +211,8 @@ static int tegra_fuse_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, fuse);
fuse->dev = &pdev->dev;
pm_runtime_enable(&pdev->dev);
if (fuse->soc->probe) {
err = fuse->soc->probe(fuse);
if (err < 0)
@ -246,14 +249,71 @@ static int tegra_fuse_probe(struct platform_device *pdev)
return 0;
restore:
fuse->clk = NULL;
fuse->base = base;
pm_runtime_disable(&pdev->dev);
return err;
}
static int __maybe_unused tegra_fuse_runtime_resume(struct device *dev)
{
int err;
err = clk_prepare_enable(fuse->clk);
if (err < 0) {
dev_err(dev, "failed to enable FUSE clock: %d\n", err);
return err;
}
return 0;
}
static int __maybe_unused tegra_fuse_runtime_suspend(struct device *dev)
{
clk_disable_unprepare(fuse->clk);
return 0;
}
static int __maybe_unused tegra_fuse_suspend(struct device *dev)
{
int ret;
/*
* Critical for RAM re-repair operation, which must occur on resume
* from LP1 system suspend and as part of CCPLEX cluster switching.
*/
if (fuse->soc->clk_suspend_on)
ret = pm_runtime_resume_and_get(dev);
else
ret = pm_runtime_force_suspend(dev);
return ret;
}
static int __maybe_unused tegra_fuse_resume(struct device *dev)
{
int ret = 0;
if (fuse->soc->clk_suspend_on)
pm_runtime_put(dev);
else
ret = pm_runtime_force_resume(dev);
return ret;
}
static const struct dev_pm_ops tegra_fuse_pm = {
SET_RUNTIME_PM_OPS(tegra_fuse_runtime_suspend, tegra_fuse_runtime_resume,
NULL)
SET_SYSTEM_SLEEP_PM_OPS(tegra_fuse_suspend, tegra_fuse_resume)
};
static struct platform_driver tegra_fuse_driver = {
.driver = {
.name = "tegra-fuse",
.of_match_table = tegra_fuse_match,
.pm = &tegra_fuse_pm,
.suppress_bind_attrs = true,
},
.probe = tegra_fuse_probe,

View File

@ -16,6 +16,7 @@
#include <linux/kobject.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/random.h>
#include <soc/tegra/fuse.h>
@ -46,6 +47,10 @@ static u32 tegra20_fuse_read(struct tegra_fuse *fuse, unsigned int offset)
u32 value = 0;
int err;
err = pm_runtime_resume_and_get(fuse->dev);
if (err)
return err;
mutex_lock(&fuse->apbdma.lock);
fuse->apbdma.config.src_addr = fuse->phys + FUSE_BEGIN + offset;
@ -66,8 +71,6 @@ static u32 tegra20_fuse_read(struct tegra_fuse *fuse, unsigned int offset)
reinit_completion(&fuse->apbdma.wait);
clk_prepare_enable(fuse->clk);
dmaengine_submit(dma_desc);
dma_async_issue_pending(fuse->apbdma.chan);
time_left = wait_for_completion_timeout(&fuse->apbdma.wait,
@ -78,10 +81,9 @@ static u32 tegra20_fuse_read(struct tegra_fuse *fuse, unsigned int offset)
else
value = *fuse->apbdma.virt;
clk_disable_unprepare(fuse->clk);
out:
mutex_unlock(&fuse->apbdma.lock);
pm_runtime_put(fuse->dev);
return value;
}
@ -165,4 +167,5 @@ const struct tegra_fuse_soc tegra20_fuse_soc = {
.probe = tegra20_fuse_probe,
.info = &tegra20_fuse_info,
.soc_attr_group = &tegra_soc_attr_group,
.clk_suspend_on = false,
};

View File

@ -12,6 +12,7 @@
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/random.h>
#include <soc/tegra/fuse.h>
@ -52,15 +53,13 @@ static u32 tegra30_fuse_read(struct tegra_fuse *fuse, unsigned int offset)
u32 value;
int err;
err = clk_prepare_enable(fuse->clk);
if (err < 0) {
dev_err(fuse->dev, "failed to enable FUSE clock: %d\n", err);
err = pm_runtime_resume_and_get(fuse->dev);
if (err)
return 0;
}
value = readl_relaxed(fuse->base + FUSE_BEGIN + offset);
clk_disable_unprepare(fuse->clk);
pm_runtime_put(fuse->dev);
return value;
}
@ -113,6 +112,7 @@ const struct tegra_fuse_soc tegra30_fuse_soc = {
.speedo_init = tegra30_init_speedo_data,
.info = &tegra30_fuse_info,
.soc_attr_group = &tegra_soc_attr_group,
.clk_suspend_on = false,
};
#endif
@ -128,6 +128,7 @@ const struct tegra_fuse_soc tegra114_fuse_soc = {
.speedo_init = tegra114_init_speedo_data,
.info = &tegra114_fuse_info,
.soc_attr_group = &tegra_soc_attr_group,
.clk_suspend_on = false,
};
#endif
@ -209,6 +210,7 @@ const struct tegra_fuse_soc tegra124_fuse_soc = {
.lookups = tegra124_fuse_lookups,
.num_lookups = ARRAY_SIZE(tegra124_fuse_lookups),
.soc_attr_group = &tegra_soc_attr_group,
.clk_suspend_on = true,
};
#endif
@ -295,6 +297,7 @@ const struct tegra_fuse_soc tegra210_fuse_soc = {
.lookups = tegra210_fuse_lookups,
.num_lookups = ARRAY_SIZE(tegra210_fuse_lookups),
.soc_attr_group = &tegra_soc_attr_group,
.clk_suspend_on = false,
};
#endif
@ -325,6 +328,7 @@ const struct tegra_fuse_soc tegra186_fuse_soc = {
.lookups = tegra186_fuse_lookups,
.num_lookups = ARRAY_SIZE(tegra186_fuse_lookups),
.soc_attr_group = &tegra_soc_attr_group,
.clk_suspend_on = false,
};
#endif
@ -355,6 +359,7 @@ const struct tegra_fuse_soc tegra194_fuse_soc = {
.lookups = tegra194_fuse_lookups,
.num_lookups = ARRAY_SIZE(tegra194_fuse_lookups),
.soc_attr_group = &tegra194_soc_attr_group,
.clk_suspend_on = false,
};
#endif
@ -385,5 +390,6 @@ const struct tegra_fuse_soc tegra234_fuse_soc = {
.lookups = tegra234_fuse_lookups,
.num_lookups = ARRAY_SIZE(tegra234_fuse_lookups),
.soc_attr_group = &tegra194_soc_attr_group,
.clk_suspend_on = false,
};
#endif

View File

@ -34,6 +34,8 @@ struct tegra_fuse_soc {
unsigned int num_lookups;
const struct attribute_group *soc_attr_group;
bool clk_suspend_on;
};
struct tegra_fuse {

View File

@ -436,7 +436,7 @@ struct tegra_pmc {
static struct tegra_pmc *pmc = &(struct tegra_pmc) {
.base = NULL,
.suspend_mode = TEGRA_SUSPEND_NONE,
.suspend_mode = TEGRA_SUSPEND_NOT_READY,
};
static inline struct tegra_powergate *
@ -1812,6 +1812,7 @@ static int tegra_pmc_parse_dt(struct tegra_pmc *pmc, struct device_node *np)
u32 value, values[2];
if (of_property_read_u32(np, "nvidia,suspend-mode", &value)) {
pmc->suspend_mode = TEGRA_SUSPEND_NONE;
} else {
switch (value) {
case 0:
@ -2785,6 +2786,11 @@ static int tegra_pmc_regmap_init(struct tegra_pmc *pmc)
return 0;
}
static void tegra_pmc_reset_suspend_mode(void *data)
{
pmc->suspend_mode = TEGRA_SUSPEND_NOT_READY;
}
static int tegra_pmc_probe(struct platform_device *pdev)
{
void __iomem *base;
@ -2803,6 +2809,11 @@ static int tegra_pmc_probe(struct platform_device *pdev)
if (err < 0)
return err;
err = devm_add_action_or_reset(&pdev->dev, tegra_pmc_reset_suspend_mode,
NULL);
if (err)
return err;
/* take over the memory region from the early initialization */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
@ -2909,6 +2920,7 @@ static int tegra_pmc_probe(struct platform_device *pdev)
tegra_pmc_clock_register(pmc, pdev->dev.of_node);
platform_set_drvdata(pdev, pmc);
tegra_pm_init_suspend();
return 0;

View File

@ -7,7 +7,6 @@
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <soc/tegra/bpmp.h>
#include <soc/tegra/bpmp-abi.h>

View File

@ -338,6 +338,7 @@ static const struct of_device_id pruss_of_match[] = {
{ .compatible = "ti,k2g-pruss" },
{ .compatible = "ti,am654-icssg", .data = &am65x_j721e_pruss_data, },
{ .compatible = "ti,j721e-icssg", .data = &am65x_j721e_pruss_data, },
{ .compatible = "ti,am642-icssg", .data = &am65x_j721e_pruss_data, },
{},
};
MODULE_DEVICE_TABLE(of, pruss_of_match);

View File

@ -126,23 +126,13 @@ static irqreturn_t sr_interrupt(int irq, void *data)
static void sr_set_clk_length(struct omap_sr *sr)
{
struct clk *fck;
u32 fclk_speed;
/* Try interconnect target module fck first if it already exists */
fck = clk_get(sr->pdev->dev.parent, "fck");
if (IS_ERR(fck)) {
fck = clk_get(&sr->pdev->dev, "fck");
if (IS_ERR(fck)) {
dev_err(&sr->pdev->dev,
"%s: unable to get fck for device %s\n",
__func__, dev_name(&sr->pdev->dev));
return;
}
}
if (IS_ERR(sr->fck))
return;
fclk_speed = clk_get_rate(fck);
clk_put(fck);
fclk_speed = clk_get_rate(sr->fck);
switch (fclk_speed) {
case 12000000:
@ -587,21 +577,25 @@ int sr_enable(struct omap_sr *sr, unsigned long volt)
/* errminlimit is opp dependent and hence linked to voltage */
sr->err_minlimit = nvalue_row->errminlimit;
pm_runtime_get_sync(&sr->pdev->dev);
clk_enable(sr->fck);
/* Check if SR is already enabled. If yes do nothing */
if (sr_read_reg(sr, SRCONFIG) & SRCONFIG_SRENABLE)
return 0;
goto out_enabled;
/* Configure SR */
ret = sr_class->configure(sr);
if (ret)
return ret;
goto out_enabled;
sr_write_reg(sr, NVALUERECIPROCAL, nvalue_row->nvalue);
/* SRCONFIG - enable SR */
sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, SRCONFIG_SRENABLE);
out_enabled:
sr->enabled = 1;
return 0;
}
@ -621,7 +615,7 @@ void sr_disable(struct omap_sr *sr)
}
/* Check if SR clocks are already disabled. If yes do nothing */
if (pm_runtime_suspended(&sr->pdev->dev))
if (!sr->enabled)
return;
/*
@ -642,7 +636,8 @@ void sr_disable(struct omap_sr *sr)
}
}
pm_runtime_put_sync_suspend(&sr->pdev->dev);
clk_disable(sr->fck);
sr->enabled = 0;
}
/**
@ -851,8 +846,12 @@ static int omap_sr_probe(struct platform_device *pdev)
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
sr_info->fck = devm_clk_get(pdev->dev.parent, "fck");
if (IS_ERR(sr_info->fck))
return PTR_ERR(sr_info->fck);
clk_prepare(sr_info->fck);
pm_runtime_enable(&pdev->dev);
pm_runtime_irq_safe(&pdev->dev);
snprintf(sr_info->name, SMARTREFLEX_NAME_LEN, "%s", pdata->name);
@ -878,12 +877,6 @@ static int omap_sr_probe(struct platform_device *pdev)
list_add(&sr_info->node, &sr_list);
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0) {
pm_runtime_put_noidle(&pdev->dev);
goto err_list_del;
}
/*
* Call into late init to do initializations that require
* both sr driver and sr class driver to be initiallized.
@ -933,16 +926,13 @@ static int omap_sr_probe(struct platform_device *pdev)
}
pm_runtime_put_sync(&pdev->dev);
return ret;
err_debugfs:
debugfs_remove_recursive(sr_info->dbg_dir);
err_list_del:
list_del(&sr_info->node);
pm_runtime_put_sync(&pdev->dev);
clk_unprepare(sr_info->fck);
return ret;
}
@ -950,6 +940,7 @@ err_list_del:
static int omap_sr_remove(struct platform_device *pdev)
{
struct omap_sr_data *pdata = pdev->dev.platform_data;
struct device *dev = &pdev->dev;
struct omap_sr *sr_info;
if (!pdata) {
@ -968,7 +959,8 @@ static int omap_sr_remove(struct platform_device *pdev)
sr_stop_vddautocomp(sr_info);
debugfs_remove_recursive(sr_info->dbg_dir);
pm_runtime_disable(&pdev->dev);
pm_runtime_disable(dev);
clk_unprepare(sr_info->fck);
list_del(&sr_info->node);
return 0;
}

View File

@ -77,6 +77,11 @@ struct spi_imx_devtype_data {
bool has_slavemode;
unsigned int fifo_size;
bool dynamic_burst;
/*
* ERR009165 fixed or not:
* https://www.nxp.com/docs/en/errata/IMX6DQCE.pdf
*/
bool tx_glitch_fixed;
enum spi_imx_devtype devtype;
};
@ -622,8 +627,14 @@ static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx,
ctrl |= mx51_ecspi_clkdiv(spi_imx, spi_imx->spi_bus_clk, &clk);
spi_imx->spi_bus_clk = clk;
if (spi_imx->usedma)
/*
* ERR009165: work in XHC mode instead of SMC as PIO on the chips
* before i.mx6ul.
*/
if (spi_imx->usedma && spi_imx->devtype_data->tx_glitch_fixed)
ctrl |= MX51_ECSPI_CTRL_SMC;
else
ctrl &= ~MX51_ECSPI_CTRL_SMC;
writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
@ -632,12 +643,16 @@ static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx,
static void mx51_setup_wml(struct spi_imx_data *spi_imx)
{
u32 tx_wml = 0;
if (spi_imx->devtype_data->tx_glitch_fixed)
tx_wml = spi_imx->wml;
/*
* Configure the DMA register: setup the watermark
* and enable DMA request.
*/
writel(MX51_ECSPI_DMA_RX_WML(spi_imx->wml - 1) |
MX51_ECSPI_DMA_TX_WML(spi_imx->wml) |
MX51_ECSPI_DMA_TX_WML(tx_wml) |
MX51_ECSPI_DMA_RXT_WML(spi_imx->wml) |
MX51_ECSPI_DMA_TEDEN | MX51_ECSPI_DMA_RXDEN |
MX51_ECSPI_DMA_RXTDEN, spi_imx->base + MX51_ECSPI_DMA);
@ -1028,6 +1043,23 @@ static struct spi_imx_devtype_data imx53_ecspi_devtype_data = {
.devtype = IMX53_ECSPI,
};
static struct spi_imx_devtype_data imx6ul_ecspi_devtype_data = {
.intctrl = mx51_ecspi_intctrl,
.prepare_message = mx51_ecspi_prepare_message,
.prepare_transfer = mx51_ecspi_prepare_transfer,
.trigger = mx51_ecspi_trigger,
.rx_available = mx51_ecspi_rx_available,
.reset = mx51_ecspi_reset,
.setup_wml = mx51_setup_wml,
.fifo_size = 64,
.has_dmamode = true,
.dynamic_burst = true,
.has_slavemode = true,
.tx_glitch_fixed = true,
.disable = mx51_ecspi_disable,
.devtype = IMX51_ECSPI,
};
static const struct of_device_id spi_imx_dt_ids[] = {
{ .compatible = "fsl,imx1-cspi", .data = &imx1_cspi_devtype_data, },
{ .compatible = "fsl,imx21-cspi", .data = &imx21_cspi_devtype_data, },
@ -1036,6 +1068,7 @@ static const struct of_device_id spi_imx_dt_ids[] = {
{ .compatible = "fsl,imx35-cspi", .data = &imx35_cspi_devtype_data, },
{ .compatible = "fsl,imx51-ecspi", .data = &imx51_ecspi_devtype_data, },
{ .compatible = "fsl,imx53-ecspi", .data = &imx53_ecspi_devtype_data, },
{ .compatible = "fsl,imx6ul-ecspi", .data = &imx6ul_ecspi_devtype_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, spi_imx_dt_ids);
@ -1249,10 +1282,6 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
{
int ret;
/* use pio mode for i.mx6dl chip TKT238285 */
if (of_machine_is_compatible("fsl,imx6dl"))
return 0;
spi_imx->wml = spi_imx->devtype_data->fifo_size / 2;
/* Prepare for TX DMA: */

View File

@ -487,6 +487,7 @@ config FTWDT010_WATCHDOG
config IXP4XX_WATCHDOG
tristate "IXP4xx Watchdog"
depends on ARCH_IXP4XX
select WATCHDOG_CORE
help
Say Y here if to include support for the watchdog timer
in the Intel IXP4xx network processors. This driver can

View File

@ -1,220 +1,173 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* drivers/char/watchdog/ixp4xx_wdt.c
*
* Watchdog driver for Intel IXP4xx network processors
*
* Author: Deepak Saxena <dsaxena@plexity.net>
* Author: Linus Walleij <linus.walleij@linaro.org>
*
* Copyright 2004 (c) MontaVista, Software, Inc.
* Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/of.h>
#include <linux/watchdog.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/uaccess.h>
#include <mach/hardware.h>
#include <linux/bits.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/soc/ixp4xx/cpu.h>
static bool nowayout = WATCHDOG_NOWAYOUT;
static int heartbeat = 60; /* (secs) Default is 1 minute */
static unsigned long wdt_status;
static unsigned long boot_status;
static DEFINE_SPINLOCK(wdt_lock);
#define WDT_TICK_RATE (IXP4XX_PERIPHERAL_BUS_CLOCK * 1000000UL)
#define WDT_IN_USE 0
#define WDT_OK_TO_CLOSE 1
static void wdt_enable(void)
{
spin_lock(&wdt_lock);
*IXP4XX_OSWK = IXP4XX_WDT_KEY;
*IXP4XX_OSWE = 0;
*IXP4XX_OSWT = WDT_TICK_RATE * heartbeat;
*IXP4XX_OSWE = IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE;
*IXP4XX_OSWK = 0;
spin_unlock(&wdt_lock);
}
static void wdt_disable(void)
{
spin_lock(&wdt_lock);
*IXP4XX_OSWK = IXP4XX_WDT_KEY;
*IXP4XX_OSWE = 0;
*IXP4XX_OSWK = 0;
spin_unlock(&wdt_lock);
}
static int ixp4xx_wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(WDT_IN_USE, &wdt_status))
return -EBUSY;
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
wdt_enable();
return stream_open(inode, file);
}
static ssize_t
ixp4xx_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
{
if (len) {
if (!nowayout) {
size_t i;
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
set_bit(WDT_OK_TO_CLOSE, &wdt_status);
}
}
wdt_enable();
}
return len;
}
static const struct watchdog_info ident = {
.options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
.identity = "IXP4xx Watchdog",
struct ixp4xx_wdt {
struct watchdog_device wdd;
void __iomem *base;
unsigned long rate;
};
/* Fallback if we do not have a clock for this */
#define IXP4XX_TIMER_FREQ 66666000
static long ixp4xx_wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
/* Registers after the timer registers */
#define IXP4XX_OSWT_OFFSET 0x14 /* Watchdog Timer */
#define IXP4XX_OSWE_OFFSET 0x18 /* Watchdog Enable */
#define IXP4XX_OSWK_OFFSET 0x1C /* Watchdog Key */
#define IXP4XX_OSST_OFFSET 0x20 /* Timer Status */
#define IXP4XX_OSST_TIMER_WDOG_PEND 0x00000008
#define IXP4XX_OSST_TIMER_WARM_RESET 0x00000010
#define IXP4XX_WDT_KEY 0x0000482E
#define IXP4XX_WDT_RESET_ENABLE 0x00000001
#define IXP4XX_WDT_IRQ_ENABLE 0x00000002
#define IXP4XX_WDT_COUNT_ENABLE 0x00000004
static inline
struct ixp4xx_wdt *to_ixp4xx_wdt(struct watchdog_device *wdd)
{
int ret = -ENOTTY;
int time;
switch (cmd) {
case WDIOC_GETSUPPORT:
ret = copy_to_user((struct watchdog_info *)arg, &ident,
sizeof(ident)) ? -EFAULT : 0;
break;
case WDIOC_GETSTATUS:
ret = put_user(0, (int *)arg);
break;
case WDIOC_GETBOOTSTATUS:
ret = put_user(boot_status, (int *)arg);
break;
case WDIOC_KEEPALIVE:
wdt_enable();
ret = 0;
break;
case WDIOC_SETTIMEOUT:
ret = get_user(time, (int *)arg);
if (ret)
break;
if (time <= 0 || time > 60) {
ret = -EINVAL;
break;
}
heartbeat = time;
wdt_enable();
fallthrough;
case WDIOC_GETTIMEOUT:
ret = put_user(heartbeat, (int *)arg);
break;
}
return ret;
return container_of(wdd, struct ixp4xx_wdt, wdd);
}
static int ixp4xx_wdt_release(struct inode *inode, struct file *file)
static int ixp4xx_wdt_start(struct watchdog_device *wdd)
{
if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
wdt_disable();
else
pr_crit("Device closed unexpectedly - timer will not stop\n");
clear_bit(WDT_IN_USE, &wdt_status);
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
struct ixp4xx_wdt *iwdt = to_ixp4xx_wdt(wdd);
__raw_writel(IXP4XX_WDT_KEY, iwdt->base + IXP4XX_OSWK_OFFSET);
__raw_writel(0, iwdt->base + IXP4XX_OSWE_OFFSET);
__raw_writel(wdd->timeout * iwdt->rate,
iwdt->base + IXP4XX_OSWT_OFFSET);
__raw_writel(IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE,
iwdt->base + IXP4XX_OSWE_OFFSET);
__raw_writel(0, iwdt->base + IXP4XX_OSWK_OFFSET);
return 0;
}
static const struct file_operations ixp4xx_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = ixp4xx_wdt_write,
.unlocked_ioctl = ixp4xx_wdt_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.open = ixp4xx_wdt_open,
.release = ixp4xx_wdt_release,
};
static struct miscdevice ixp4xx_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &ixp4xx_wdt_fops,
};
static int __init ixp4xx_wdt_init(void)
static int ixp4xx_wdt_stop(struct watchdog_device *wdd)
{
struct ixp4xx_wdt *iwdt = to_ixp4xx_wdt(wdd);
__raw_writel(IXP4XX_WDT_KEY, iwdt->base + IXP4XX_OSWK_OFFSET);
__raw_writel(0, iwdt->base + IXP4XX_OSWE_OFFSET);
__raw_writel(0, iwdt->base + IXP4XX_OSWK_OFFSET);
return 0;
}
static int ixp4xx_wdt_set_timeout(struct watchdog_device *wdd,
unsigned int timeout)
{
wdd->timeout = timeout;
if (watchdog_active(wdd))
ixp4xx_wdt_start(wdd);
return 0;
}
static const struct watchdog_ops ixp4xx_wdt_ops = {
.start = ixp4xx_wdt_start,
.stop = ixp4xx_wdt_stop,
.set_timeout = ixp4xx_wdt_set_timeout,
.owner = THIS_MODULE,
};
static const struct watchdog_info ixp4xx_wdt_info = {
.options = WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
| WDIOF_SETTIMEOUT,
.identity = KBUILD_MODNAME,
};
/* Devres-handled clock disablement */
static void ixp4xx_clock_action(void *d)
{
clk_disable_unprepare(d);
}
static int ixp4xx_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ixp4xx_wdt *iwdt;
struct clk *clk;
int ret;
/*
* FIXME: we bail out on device tree boot but this really needs
* to be fixed in a nicer way: this registers the MDIO bus before
* even matching the driver infrastructure, we should only probe
* detected hardware.
*/
if (of_have_populated_dt())
return -ENODEV;
if (!(read_cpuid_id() & 0xf) && !cpu_is_ixp46x()) {
pr_err("Rev. A0 IXP42x CPU detected - watchdog disabled\n");
dev_err(dev, "Rev. A0 IXP42x CPU detected - watchdog disabled\n");
return -ENODEV;
}
boot_status = (*IXP4XX_OSST & IXP4XX_OSST_TIMER_WARM_RESET) ?
WDIOF_CARDRESET : 0;
ret = misc_register(&ixp4xx_wdt_miscdev);
if (ret == 0)
pr_info("timer heartbeat %d sec\n", heartbeat);
return ret;
iwdt = devm_kzalloc(dev, sizeof(*iwdt), GFP_KERNEL);
if (!iwdt)
return -ENOMEM;
iwdt->base = dev->platform_data;
/*
* Retrieve rate from a fixed clock from the device tree if
* the parent has that, else use the default clock rate.
*/
clk = devm_clk_get(dev->parent, NULL);
if (!IS_ERR(clk)) {
ret = clk_prepare_enable(clk);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, ixp4xx_clock_action, clk);
if (ret)
return ret;
iwdt->rate = clk_get_rate(clk);
}
if (!iwdt->rate)
iwdt->rate = IXP4XX_TIMER_FREQ;
iwdt->wdd.info = &ixp4xx_wdt_info;
iwdt->wdd.ops = &ixp4xx_wdt_ops;
iwdt->wdd.min_timeout = 1;
iwdt->wdd.max_timeout = U32_MAX / iwdt->rate;
iwdt->wdd.parent = dev;
/* Default to 60 seconds */
iwdt->wdd.timeout = 60U;
watchdog_init_timeout(&iwdt->wdd, 0, dev);
if (__raw_readl(iwdt->base + IXP4XX_OSST_OFFSET) &
IXP4XX_OSST_TIMER_WARM_RESET)
iwdt->wdd.bootstatus = WDIOF_CARDRESET;
ret = devm_watchdog_register_device(dev, &iwdt->wdd);
if (ret)
return ret;
dev_info(dev, "IXP4xx watchdog available\n");
return 0;
}
static void __exit ixp4xx_wdt_exit(void)
{
misc_deregister(&ixp4xx_wdt_miscdev);
}
module_init(ixp4xx_wdt_init);
module_exit(ixp4xx_wdt_exit);
static struct platform_driver ixp4xx_wdt_driver = {
.probe = ixp4xx_wdt_probe,
.driver = {
.name = "ixp4xx-watchdog",
},
};
module_platform_driver(ixp4xx_wdt_driver);
MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");
MODULE_DESCRIPTION("IXP4xx Network Processor Watchdog");
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)");
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
MODULE_LICENSE("GPL");

View File

@ -192,6 +192,16 @@
#define SDM660_SSCMX 8
#define SDM660_SSCMX_VFL 9
/* SM6115 Power Domains */
#define SM6115_VDDCX 0
#define SM6115_VDDCX_AO 1
#define SM6115_VDDCX_VFL 2
#define SM6115_VDDMX 3
#define SM6115_VDDMX_AO 4
#define SM6115_VDDMX_VFL 5
#define SM6115_VDD_LPI_CX 6
#define SM6115_VDD_LPI_MX 7
/* RPM SMD Power Domain performance levels */
#define RPM_SMD_LEVEL_RETENTION 16
#define RPM_SMD_LEVEL_RETENTION_PLUS 32

View File

@ -16,5 +16,7 @@
#define PDC_DISPLAY_SYNC_RESET 7
#define PDC_COMPUTE_SYNC_RESET 8
#define PDC_MODEM_SYNC_RESET 9
#define PDC_WLAN_RF_SYNC_RESET 10
#define PDC_WPSS_SYNC_RESET 11
#endif

View File

@ -81,9 +81,6 @@ extern int gpmc_configure(int cmd, int wval);
extern void gpmc_read_settings_dt(struct device_node *np,
struct gpmc_settings *p);
extern void omap3_gpmc_save_context(void);
extern void omap3_gpmc_restore_context(void);
struct gpmc_timings;
struct omap_nand_platform_data;
struct omap_onenand_platform_data;

View File

@ -14,8 +14,8 @@ struct ixp4xx_pata_data {
volatile u32 *cs1_cfg;
unsigned long cs0_bits;
unsigned long cs1_bits;
void __iomem *cs0;
void __iomem *cs1;
void __iomem *cmd;
void __iomem *ctl;
};
#endif

View File

@ -155,6 +155,7 @@ struct omap_sr {
struct voltagedomain *voltdm;
struct dentry *dbg_dir;
unsigned int irq;
struct clk *fck;
int srid;
int ip_type;
int nvalue_count;
@ -169,6 +170,7 @@ struct omap_sr {
u32 senp_mod;
u32 senn_mod;
void __iomem *base;
unsigned long enabled:1;
};
/**

View File

@ -8,11 +8,24 @@
#include <linux/interconnect.h>
/* Transfer mode supported by GENI Serial Engines */
/**
* enum geni_se_xfer_mode: Transfer modes supported by Serial Engines
*
* @GENI_SE_INVALID: Invalid mode
* @GENI_SE_FIFO: FIFO mode. Data is transferred with SE FIFO
* by programmed IO method
* @GENI_SE_DMA: Serial Engine DMA mode. Data is transferred
* with SE by DMAengine internal to SE
* @GENI_GPI_DMA: GPI DMA mode. Data is transferred using a DMAengine
* configured by a firmware residing on a GSI engine. This DMA name is
* interchangeably used as GSI or GPI which seem to imply the same DMAengine
*/
enum geni_se_xfer_mode {
GENI_SE_INVALID,
GENI_SE_FIFO,
GENI_SE_DMA,
GENI_GPI_DMA,
};
/* Protocols supported by GENI Serial Engines */
@ -63,6 +76,7 @@ struct geni_se {
#define SE_GENI_STATUS 0x40
#define GENI_SER_M_CLK_CFG 0x48
#define GENI_SER_S_CLK_CFG 0x4c
#define GENI_IF_DISABLE_RO 0x64
#define GENI_FW_REVISION_RO 0x68
#define SE_GENI_CLK_SEL 0x7c
#define SE_GENI_DMA_MODE_EN 0x258
@ -105,6 +119,9 @@ struct geni_se {
#define CLK_DIV_MSK GENMASK(15, 4)
#define CLK_DIV_SHFT 4
/* GENI_IF_DISABLE_RO fields */
#define FIFO_IF_DISABLE (BIT(0))
/* GENI_FW_REVISION_RO fields */
#define FW_REV_PROTOCOL_MSK GENMASK(15, 8)
#define FW_REV_PROTOCOL_SHFT 8

View File

@ -14,6 +14,7 @@ enum tegra_suspend_mode {
TEGRA_SUSPEND_LP1, /* CPU voltage off, DRAM self-refresh */
TEGRA_SUSPEND_LP0, /* CPU + core voltage off, DRAM self-refresh */
TEGRA_MAX_SUSPEND_MODE,
TEGRA_SUSPEND_NOT_READY,
};
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_ARM)
@ -28,6 +29,7 @@ void tegra_pm_clear_cpu_in_lp2(void);
void tegra_pm_set_cpu_in_lp2(void);
int tegra_pm_enter_lp2(void);
int tegra_pm_park_secondary_cpu(unsigned long cpu);
void tegra_pm_init_suspend(void);
#else
static inline enum tegra_suspend_mode
tegra_pm_validate_suspend_mode(enum tegra_suspend_mode mode)
@ -61,6 +63,10 @@ static inline int tegra_pm_park_secondary_cpu(unsigned long cpu)
{
return -ENOTSUPP;
}
static inline void tegra_pm_init_suspend(void)
{
}
#endif /* CONFIG_PM_SLEEP */
#endif /* __SOC_TEGRA_PM_H__ */

View File

@ -55,6 +55,7 @@
#define VIRTIO_ID_FS 26 /* virtio filesystem */
#define VIRTIO_ID_PMEM 27 /* virtio pmem */
#define VIRTIO_ID_MAC80211_HWSIM 29 /* virtio mac80211-hwsim */
#define VIRTIO_ID_SCMI 32 /* virtio SCMI */
#define VIRTIO_ID_I2C_ADAPTER 34 /* virtio i2c adapter */
#define VIRTIO_ID_BT 40 /* virtio bluetooth */

View File

@ -0,0 +1,24 @@
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
/*
* Copyright (C) 2020-2021 OpenSynergy GmbH
* Copyright (C) 2021 ARM Ltd.
*/
#ifndef _UAPI_LINUX_VIRTIO_SCMI_H
#define _UAPI_LINUX_VIRTIO_SCMI_H
#include <linux/virtio_types.h>
/* Device implements some SCMI notifications, or delayed responses. */
#define VIRTIO_SCMI_F_P2A_CHANNELS 0
/* Device implements any SCMI statistics shared memory region */
#define VIRTIO_SCMI_F_SHARED_MEMORY 1
/* Virtqueues */
#define VIRTIO_SCMI_VQ_TX 0 /* cmdq */
#define VIRTIO_SCMI_VQ_RX 1 /* eventq */
#define VIRTIO_SCMI_VQ_MAX_CNT 2
#endif /* _UAPI_LINUX_VIRTIO_SCMI_H */