diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2025-07-29 11:13:27 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2025-07-29 11:13:27 -0700 |
commit | 0f46f50845ce75bfaba62df0421084d23bb6a72f (patch) | |
tree | 03e1408849f024c46d4b2294cc2d22728cc1d42b | |
parent | 115e74a29b530d121891238e9551c4bcdf7b04b5 (diff) | |
parent | 4507d205f10216d3a6b873f0a2e9c634e23f35ac (diff) |
Merge tag 'soc-drivers-6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
Pull SoC driver updates from Arnd Bergmann:
"Changes are all over the place, but very little sticks out as
noteworthy.
There is a new misc driver for the Raspberry Pi 5's RP1 multifunction
I/O chip, along with hooking it up to the pinctrl and clk frameworks.
The reset controller and memory subsystems have mainly small updates,
but there are two new reset drivers for the K230 and VC1800B SoCs, and
new memory driver support for Tegra264.
The ARM SMCCC and SCMI firmware drivers gain a few more features that
should help them be supported across more environments. Similarly, the
SoC specific firmware on Tegra and Qualcomm get minor enhancements and
chip support.
In the drivers/soc/ directory, the ASPEED LPC snoop driver gets an
overhaul for code robustness, the Tegra and Qualcomm and NXP drivers
grow to support more chips, while the Hisilicon, Mediatek and Renesas
drivers see mostly janitorial fixes"
* tag 'soc-drivers-6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (100 commits)
bus: del unnecessary init var
soc: fsl: qe: convert set_multiple() to returning an integer
pinctrl: rp1: use new GPIO line value setter callbacks
soc: hisilicon: kunpeng_hccs: Fix incorrect log information
dt-bindings: soc: qcom: qcom,pmic-glink: document Milos compatible
dt-bindings: soc: qcom,aoss-qmp: document the Milos Always-On Subsystem side channel
dt-bindings: firmware: qcom,scm: document Milos SCM Firmware Interface
soc: qcom: socinfo: Add support to retrieve APPSBL build details
soc: qcom: pmic_glink: fix OF node leak
soc: qcom: spmi-pmic: add more PMIC SUBTYPE IDs
soc: qcom: socinfo: Add PM7550 & PMIV0108 PMICs
soc: qcom: socinfo: Add SoC IDs for SM7635 family
dt-bindings: arm: qcom,ids: Add SoC IDs for SM7635 family
firmware: qcom: scm: request the waitqueue irq *after* initializing SCM
firmware: qcom: scm: initialize tzmem before marking SCM as available
firmware: qcom: scm: take struct device as argument in SHM bridge enable
firmware: qcom: scm: remove unused arguments from SHM bridge routines
soc: qcom: rpmh-rsc: Add RSC version 4 support
memory: tegra: Add Tegra264 MC and EMC support
firmware: tegra: bpmp: Fix build failure for tegra264-only config
...
94 files changed, 6791 insertions, 1069 deletions
diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml index 2e9ab9583005..5bd517befb68 100644 --- a/Documentation/devicetree/bindings/arm/cpus.yaml +++ b/Documentation/devicetree/bindings/arm/cpus.yaml @@ -200,6 +200,7 @@ properties: - qcom,kryo385 - qcom,kryo465 - qcom,kryo468 + - qcom,kryo470 - qcom,kryo485 - qcom,kryo560 - qcom,kryo570 diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml index 8cdaac8011ba..b913192219e4 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml @@ -32,6 +32,7 @@ properties: - qcom,scm-ipq8074 - qcom,scm-ipq9574 - qcom,scm-mdm9607 + - qcom,scm-milos - qcom,scm-msm8226 - qcom,scm-msm8660 - qcom,scm-msm8916 @@ -198,6 +199,7 @@ allOf: compatible: contains: enum: + - qcom,scm-milos - qcom,scm-sm8450 - qcom,scm-sm8550 - qcom,scm-sm8650 diff --git a/Documentation/devicetree/bindings/memory-controllers/arm,pl172.txt b/Documentation/devicetree/bindings/memory-controllers/arm,pl172.txt deleted file mode 100644 index 22b77ee02f58..000000000000 --- a/Documentation/devicetree/bindings/memory-controllers/arm,pl172.txt +++ /dev/null @@ -1,127 +0,0 @@ -* Device tree bindings for ARM PL172/PL175/PL176 MultiPort Memory Controller - -Required properties: - -- compatible: Must be "arm,primecell" and exactly one from - "arm,pl172", "arm,pl175" or "arm,pl176". - -- reg: Must contains offset/length value for controller. - -- #address-cells: Must be 2. The partition number has to be encoded in the - first address cell and it may accept values 0..N-1 - (N - total number of partitions). The second cell is the - offset into the partition. - -- #size-cells: Must be set to 1. - -- ranges: Must contain one or more chip select memory regions. - -- clocks: Must contain references to controller clocks. - -- clock-names: Must contain "mpmcclk" and "apb_pclk". - -- clock-ranges: Empty property indicating that child nodes can inherit - named clocks. Required only if clock tree data present - in device tree. - See clock-bindings.txt - -Child chip-select (cs) nodes contain the memory devices nodes connected to -such as NOR (e.g. cfi-flash) and NAND. - -Required child cs node properties: - -- #address-cells: Must be 2. - -- #size-cells: Must be 1. - -- ranges: Empty property indicating that child nodes can inherit - memory layout. - -- clock-ranges: Empty property indicating that child nodes can inherit - named clocks. Required only if clock tree data present - in device tree. - -- mpmc,cs: Chip select number. Indicates to the pl0172 driver - which chipselect is used for accessing the memory. - -- mpmc,memory-width: Width of the chip select memory. Must be equal to - either 8, 16 or 32. - -Optional child cs node config properties: - -- mpmc,async-page-mode: Enable asynchronous page mode. - -- mpmc,cs-active-high: Set chip select polarity to active high. - -- mpmc,byte-lane-low: Set byte lane state to low. - -- mpmc,extended-wait: Enable extended wait. - -- mpmc,buffer-enable: Enable write buffer, option is not supported by - PL175 and PL176 controllers. - -- mpmc,write-protect: Enable write protect. - -Optional child cs node timing properties: - -- mpmc,write-enable-delay: Delay from chip select assertion to write - enable (WE signal) in nano seconds. - -- mpmc,output-enable-delay: Delay from chip select assertion to output - enable (OE signal) in nano seconds. - -- mpmc,write-access-delay: Delay from chip select assertion to write - access in nano seconds. - -- mpmc,read-access-delay: Delay from chip select assertion to read - access in nano seconds. - -- mpmc,page-mode-read-delay: Delay for asynchronous page mode sequential - accesses in nano seconds. - -- mpmc,turn-round-delay: Delay between access to memory banks in nano - seconds. - -If any of the above timing parameters are absent, current parameter value will -be taken from the corresponding HW reg. - -Example for pl172 with nor flash on chip select 0 shown below. - -emc: memory-controller@40005000 { - compatible = "arm,pl172", "arm,primecell"; - reg = <0x40005000 0x1000>; - clocks = <&ccu1 CLK_CPU_EMCDIV>, <&ccu1 CLK_CPU_EMC>; - clock-names = "mpmcclk", "apb_pclk"; - #address-cells = <2>; - #size-cells = <1>; - ranges = <0 0 0x1c000000 0x1000000 - 1 0 0x1d000000 0x1000000 - 2 0 0x1e000000 0x1000000 - 3 0 0x1f000000 0x1000000>; - - cs0 { - #address-cells = <2>; - #size-cells = <1>; - ranges; - - mpmc,cs = <0>; - mpmc,memory-width = <16>; - mpmc,byte-lane-low; - mpmc,write-enable-delay = <0>; - mpmc,output-enable-delay = <0>; - mpmc,read-enable-delay = <70>; - mpmc,page-mode-read-delay = <70>; - - flash@0,0 { - compatible = "sst,sst39vf320", "cfi-flash"; - reg = <0 0 0x400000>; - bank-width = <2>; - #address-cells = <1>; - #size-cells = <1>; - partition@0 { - label = "data"; - reg = <0 0x400000>; - }; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/memory-controllers/arm,pl172.yaml b/Documentation/devicetree/bindings/memory-controllers/arm,pl172.yaml new file mode 100644 index 000000000000..c1b702669bd9 --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/arm,pl172.yaml @@ -0,0 +1,222 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/memory-controllers/arm,pl172.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ARM PL172/PL175/PL176 MultiPort Memory Controller + +maintainers: + - Frank Li <Frank.Li@nxp.com> + +# We need a select here so we don't match all nodes with 'arm,primecell' +select: + properties: + compatible: + contains: + enum: + - arm,pl172 + - arm,pl175 + - arm,pl176 + required: + - compatible + +properties: + compatible: + items: + - enum: + - arm,pl172 + - arm,pl175 + - arm,pl176 + - const: arm,primecell + + reg: + maxItems: 1 + + '#address-cells': + const: 2 + + '#size-cells': + const: 1 + + ranges: true + + clocks: + maxItems: 2 + + clock-names: + items: + - const: mpmcclk + - const: apb_pclk + + clock-ranges: true + + resets: + maxItems: 1 + +patternProperties: + "^cs[0-9]$": + type: object + additionalProperties: false + patternProperties: + "^flash@[0-9],[0-9a-f]+$": + type: object + $ref: /schemas/mtd/mtd-physmap.yaml# + unevaluatedProperties: false + + "^(gpio|sram)@[0-9],[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + '#address-cells': + const: 2 + + '#size-cells': + const: 1 + + ranges: true + + clocks: + maxItems: 2 + + clock-ranges: true + + mpmc,cs: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Chip select number. Indicates to the pl0172 driver + which chipselect is used for accessing the memory. + + mpmc,memory-width: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [8, 16, 32] + description: + Width of the chip select memory. Must be equal to either 8, 16 or 32. + + mpmc,async-page-mode: + $ref: /schemas/types.yaml#/definitions/flag + description: + Enable asynchronous page mode. + + mpmc,cs-active-high: + $ref: /schemas/types.yaml#/definitions/flag + description: + Set chip select polarity to active high. + + mpmc,byte-lane-low: + $ref: /schemas/types.yaml#/definitions/flag + description: + Set byte lane state to low. + + mpmc,extended-wait: + $ref: /schemas/types.yaml#/definitions/flag + description: + Enable extended wait. + + mpmc,buffer-enable: + $ref: /schemas/types.yaml#/definitions/flag + description: + Enable write buffer, option is not supported by + PL175 and PL176 controllers. + + mpmc,write-protect: + $ref: /schemas/types.yaml#/definitions/flag + description: + Enable write protect. + + mpmc,read-enable-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Delay from chip select assertion to read + enable (RE signal) in nano seconds. + + mpmc,write-enable-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Delay from chip select assertion to write + enable (WE signal) in nano seconds. + + mpmc,output-enable-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Delay from chip select assertion to output + enable (OE signal) in nano seconds. + + mpmc,write-access-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Delay from chip select assertion to write + access in nano seconds. + + mpmc,read-access-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Delay from chip select assertion to read + access in nano seconds. + + mpmc,page-mode-read-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Delay for asynchronous page mode sequential + accesses in nano seconds. + + mpmc,turn-round-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Delay between access to memory banks in nano + seconds. + +required: + - compatible + - reg + - '#address-cells' + - '#size-cells' + - ranges + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/lpc18xx-ccu.h> + + memory-controller@40005000 { + compatible = "arm,pl172", "arm,primecell"; + reg = <0x40005000 0x1000>; + clocks = <&ccu1 CLK_CPU_EMCDIV>, <&ccu1 CLK_CPU_EMC>; + clock-names = "mpmcclk", "apb_pclk"; + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0x1c000000 0x1000000 + 1 0 0x1d000000 0x1000000 + 2 0 0x1e000000 0x1000000 + 3 0 0x1f000000 0x1000000>; + + cs0 { + #address-cells = <2>; + #size-cells = <1>; + ranges; + + mpmc,cs = <0>; + mpmc,memory-width = <16>; + mpmc,byte-lane-low; + mpmc,write-enable-delay = <0>; + mpmc,output-enable-delay = <0>; + mpmc,read-enable-delay = <70>; + mpmc,page-mode-read-delay = <70>; + + flash@0,0 { + compatible = "sst,sst39vf320", "cfi-flash"; + reg = <0 0 0x400000>; + bank-width = <2>; + #address-cells = <1>; + #size-cells = <1>; + partition@0 { + label = "data"; + reg = <0 0x400000>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/memory-controllers/brcm,brcmstb-memc-ddr.yaml b/Documentation/devicetree/bindings/memory-controllers/brcm,brcmstb-memc-ddr.yaml index 4b072c879b02..b935894bd4fc 100644 --- a/Documentation/devicetree/bindings/memory-controllers/brcm,brcmstb-memc-ddr.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/brcm,brcmstb-memc-ddr.yaml @@ -11,25 +11,37 @@ maintainers: properties: compatible: - items: - - enum: - - brcm,brcmstb-memc-ddr-rev-b.1.x - - brcm,brcmstb-memc-ddr-rev-b.2.0 - - brcm,brcmstb-memc-ddr-rev-b.2.1 - - brcm,brcmstb-memc-ddr-rev-b.2.2 - - brcm,brcmstb-memc-ddr-rev-b.2.3 - - brcm,brcmstb-memc-ddr-rev-b.2.5 - - brcm,brcmstb-memc-ddr-rev-b.2.6 - - brcm,brcmstb-memc-ddr-rev-b.2.7 - - brcm,brcmstb-memc-ddr-rev-b.2.8 - - brcm,brcmstb-memc-ddr-rev-b.3.0 - - brcm,brcmstb-memc-ddr-rev-b.3.1 - - brcm,brcmstb-memc-ddr-rev-c.1.0 - - brcm,brcmstb-memc-ddr-rev-c.1.1 - - brcm,brcmstb-memc-ddr-rev-c.1.2 - - brcm,brcmstb-memc-ddr-rev-c.1.3 - - brcm,brcmstb-memc-ddr-rev-c.1.4 - - const: brcm,brcmstb-memc-ddr + oneOf: + - description: Revision > 2.1 controllers + items: + - enum: + - brcm,brcmstb-memc-ddr-rev-b.2.2 + - brcm,brcmstb-memc-ddr-rev-b.2.3 + - brcm,brcmstb-memc-ddr-rev-b.2.5 + - brcm,brcmstb-memc-ddr-rev-b.2.6 + - brcm,brcmstb-memc-ddr-rev-b.2.7 + - brcm,brcmstb-memc-ddr-rev-b.2.8 + - brcm,brcmstb-memc-ddr-rev-b.3.0 + - brcm,brcmstb-memc-ddr-rev-b.3.1 + - brcm,brcmstb-memc-ddr-rev-c.1.0 + - brcm,brcmstb-memc-ddr-rev-c.1.1 + - brcm,brcmstb-memc-ddr-rev-c.1.2 + - brcm,brcmstb-memc-ddr-rev-c.1.3 + - brcm,brcmstb-memc-ddr-rev-c.1.4 + - const: brcm,brcmstb-memc-ddr-rev-b.2.1 + - const: brcm,brcmstb-memc-ddr + - description: Revision 2.1 controllers + items: + - const: brcm,brcmstb-memc-ddr-rev-b.2.1 + - const: brcm,brcmstb-memc-ddr + - description: Revision 2.0 controllers + items: + - const: brcm,brcmstb-memc-ddr-rev-b.2.0 + - const: brcm,brcmstb-memc-ddr + - description: Revision 1.x controllers + items: + - const: brcm,brcmstb-memc-ddr-rev-b.1.x + - const: brcm,brcmstb-memc-ddr reg: maxItems: 1 @@ -46,7 +58,9 @@ additionalProperties: false examples: - | memory-controller@9902000 { - compatible = "brcm,brcmstb-memc-ddr-rev-c.1.1", "brcm,brcmstb-memc-ddr"; + compatible = "brcm,brcmstb-memc-ddr-rev-c.1.1", + "brcm,brcmstb-memc-ddr-rev-b.2.1", + "brcm,brcmstb-memc-ddr"; reg = <0x9902000 0x600>; clock-frequency = <2133000000>; }; diff --git a/Documentation/devicetree/bindings/memory-controllers/renesas,rzg3e-xspi.yaml b/Documentation/devicetree/bindings/memory-controllers/renesas,rzg3e-xspi.yaml index 2bfe63ec62dc..7a84f5bb7284 100644 --- a/Documentation/devicetree/bindings/memory-controllers/renesas,rzg3e-xspi.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/renesas,rzg3e-xspi.yaml @@ -23,7 +23,14 @@ allOf: properties: compatible: - const: renesas,r9a09g047-xspi # RZ/G3E + oneOf: + - const: renesas,r9a09g047-xspi # RZ/G3E + + - items: + - enum: + - renesas,r9a09g056-xspi # RZ/V2N + - renesas,r9a09g057-xspi # RZ/V2H(P) + - const: renesas,r9a09g047-xspi reg: items: diff --git a/Documentation/devicetree/bindings/reset/canaan,k230-rst.yaml b/Documentation/devicetree/bindings/reset/canaan,k230-rst.yaml new file mode 100644 index 000000000000..d352d0e12d81 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/canaan,k230-rst.yaml @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/canaan,k230-rst.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Canaan Kendryte K230 Reset Controller + +maintainers: + - Junhui Liu <junhui.liu@pigmoral.tech> + +description: + The Canaan Kendryte K230 reset controller is part of the SoC's system + controller and controls the reset registers for CPUs and various peripherals. + +properties: + compatible: + const: canaan,k230-rst + + reg: + maxItems: 1 + + '#reset-cells': + const: 1 + +required: + - compatible + - reg + - '#reset-cells' + +additionalProperties: false + +examples: + - | + reset-controller@91101000 { + compatible = "canaan,k230-rst"; + reg = <0x91101000 0x1000>; + #reset-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/reset/nxp,lpc1850-rgu.txt b/Documentation/devicetree/bindings/reset/nxp,lpc1850-rgu.txt deleted file mode 100644 index 05d5be48dae4..000000000000 --- a/Documentation/devicetree/bindings/reset/nxp,lpc1850-rgu.txt +++ /dev/null @@ -1,83 +0,0 @@ -NXP LPC1850 Reset Generation Unit (RGU) -======================================== - -Please also refer to reset.txt in this directory for common reset -controller binding usage. - -Required properties: -- compatible: Should be "nxp,lpc1850-rgu" -- reg: register base and length -- clocks: phandle and clock specifier to RGU clocks -- clock-names: should contain "delay" and "reg" -- #reset-cells: should be 1 - -See table below for valid peripheral reset numbers. Numbers not -in the table below are either reserved or not applicable for -normal operation. - -Reset Peripheral - 9 System control unit (SCU) - 12 ARM Cortex-M0 subsystem core (LPC43xx only) - 13 CPU core - 16 LCD controller - 17 USB0 - 18 USB1 - 19 DMA - 20 SDIO - 21 External memory controller (EMC) - 22 Ethernet - 25 Flash bank A - 27 EEPROM - 28 GPIO - 29 Flash bank B - 32 Timer0 - 33 Timer1 - 34 Timer2 - 35 Timer3 - 36 Repetitive Interrupt timer (RIT) - 37 State Configurable Timer (SCT) - 38 Motor control PWM (MCPWM) - 39 QEI - 40 ADC0 - 41 ADC1 - 42 DAC - 44 USART0 - 45 UART1 - 46 USART2 - 47 USART3 - 48 I2C0 - 49 I2C1 - 50 SSP0 - 51 SSP1 - 52 I2S0 and I2S1 - 53 Serial Flash Interface (SPIFI) - 54 C_CAN1 - 55 C_CAN0 - 56 ARM Cortex-M0 application core (LPC4370 only) - 57 SGPIO (LPC43xx only) - 58 SPI (LPC43xx only) - 60 ADCHS (12-bit ADC) (LPC4370 only) - -Refer to NXP LPC18xx or LPC43xx user manual for more details about -the reset signals and the connected block/peripheral. - -Reset provider example: -rgu: reset-controller@40053000 { - compatible = "nxp,lpc1850-rgu"; - reg = <0x40053000 0x1000>; - clocks = <&cgu BASE_SAFE_CLK>, <&ccu1 CLK_CPU_BUS>; - clock-names = "delay", "reg"; - #reset-cells = <1>; -}; - -Reset consumer example: -mac: ethernet@40010000 { - compatible = "nxp,lpc1850-dwmac", "snps,dwmac-3.611", "snps,dwmac"; - reg = <0x40010000 0x2000>; - interrupts = <5>; - interrupt-names = "macirq"; - clocks = <&ccu1 CLK_CPU_ETHERNET>; - clock-names = "stmmaceth"; - resets = <&rgu 22>; - reset-names = "stmmaceth"; -}; diff --git a/Documentation/devicetree/bindings/reset/nxp,lpc1850-rgu.yaml b/Documentation/devicetree/bindings/reset/nxp,lpc1850-rgu.yaml new file mode 100644 index 000000000000..9c3c13c543c7 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/nxp,lpc1850-rgu.yaml @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/nxp,lpc1850-rgu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC1850 Reset Generation Unit (RGU) + +maintainers: + - Frank Li <Frank.Li@nxp.com> + +properties: + compatible: + const: nxp,lpc1850-rgu + + reg: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: delay + - const: reg + + '#reset-cells': + const: 1 + description: | + See table below for valid peripheral reset numbers. Numbers not + in the table below are either reserved or not applicable for + normal operation. + + Reset Peripheral + 9 System control unit (SCU) + 12 ARM Cortex-M0 subsystem core (LPC43xx only) + 13 CPU core + 16 LCD controller + 17 USB0 + 18 USB1 + 19 DMA + 20 SDIO + 21 External memory controller (EMC) + 22 Ethernet + 25 Flash bank A + 27 EEPROM + 28 GPIO + 29 Flash bank B + 32 Timer0 + 33 Timer1 + 34 Timer2 + 35 Timer3 + 36 Repetitive Interrupt timer (RIT) + 37 State Configurable Timer (SCT) + 38 Motor control PWM (MCPWM) + 39 QEI + 40 ADC0 + 41 ADC1 + 42 DAC + 44 USART0 + 45 UART1 + 46 USART2 + 47 USART3 + 48 I2C0 + 49 I2C1 + 50 SSP0 + 51 SSP1 + 52 I2S0 and I2S1 + 53 Serial Flash Interface (SPIFI) + 54 C_CAN1 + 55 C_CAN0 + 56 ARM Cortex-M0 application core (LPC4370 only) + 57 SGPIO (LPC43xx only) + 58 SPI (LPC43xx only) + 60 ADCHS (12-bit ADC) (LPC4370 only) + + Refer to NXP LPC18xx or LPC43xx user manual for more details about + the reset signals and the connected block/peripheral. + +required: + - compatible + - reg + - clocks + - clock-names + - '#reset-cells' + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/lpc18xx-ccu.h> + #include <dt-bindings/clock/lpc18xx-cgu.h> + + reset-controller@40053000 { + compatible = "nxp,lpc1850-rgu"; + reg = <0x40053000 0x1000>; + clocks = <&cgu BASE_SAFE_CLK>, <&ccu1 CLK_CPU_BUS>; + clock-names = "delay", "reg"; + #reset-cells = <1>; + }; + diff --git a/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml b/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml index c79f61c2373b..c1b800a10b53 100644 --- a/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml +++ b/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml @@ -15,7 +15,12 @@ description: properties: compatible: - const: renesas,r9a09g057-usb2phy-reset # RZ/V2H(P) + oneOf: + - items: + - const: renesas,r9a09g056-usb2phy-reset # RZ/V2N + - const: renesas,r9a09g057-usb2phy-reset + + - const: renesas,r9a09g057-usb2phy-reset # RZ/V2H(P) reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/reset/snps,dw-reset.txt b/Documentation/devicetree/bindings/reset/snps,dw-reset.txt deleted file mode 100644 index 0c241d4aae76..000000000000 --- a/Documentation/devicetree/bindings/reset/snps,dw-reset.txt +++ /dev/null @@ -1,30 +0,0 @@ -Synopsys DesignWare Reset controller -======================================= - -Please also refer to reset.txt in this directory for common reset -controller binding usage. - -Required properties: - -- compatible: should be one of the following. - "snps,dw-high-reset" - for active high configuration - "snps,dw-low-reset" - for active low configuration - -- reg: physical base address of the controller and length of memory mapped - region. - -- #reset-cells: must be 1. - -example: - - dw_rst_1: reset-controller@0000 { - compatible = "snps,dw-high-reset"; - reg = <0x0000 0x4>; - #reset-cells = <1>; - }; - - dw_rst_2: reset-controller@1000 { - compatible = "snps,dw-low-reset"; - reg = <0x1000 0x8>; - #reset-cells = <1>; - }; diff --git a/Documentation/devicetree/bindings/reset/snps,dw-reset.yaml b/Documentation/devicetree/bindings/reset/snps,dw-reset.yaml new file mode 100644 index 000000000000..1dde7b6e8623 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/snps,dw-reset.yaml @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/snps,dw-reset.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Synopsys DesignWare Reset controller + +maintainers: + - Philipp Zabel <p.zabel@pengutronix.de> + +properties: + compatible: + enum: + - snps,dw-high-reset + - snps,dw-low-reset + + reg: + maxItems: 1 + + '#reset-cells': + const: 1 + + reset-controller: true + +required: + - compatible + - reg + - '#reset-cells' + +additionalProperties: false + +examples: + - | + reset-controller@0 { + compatible = "snps,dw-high-reset"; + reg = <0x0000 0x4>; + #reset-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/reset/sophgo,sg2042-reset.yaml b/Documentation/devicetree/bindings/reset/sophgo,sg2042-reset.yaml index 1d1b84575960..08d28313b870 100644 --- a/Documentation/devicetree/bindings/reset/sophgo,sg2042-reset.yaml +++ b/Documentation/devicetree/bindings/reset/sophgo,sg2042-reset.yaml @@ -16,7 +16,9 @@ properties: - enum: - sophgo,sg2044-reset - const: sophgo,sg2042-reset - - const: sophgo,sg2042-reset + - enum: + - sophgo,cv1800b-reset + - sophgo,sg2042-reset reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml index 41fbbe059d80..851a1260f8dc 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml @@ -25,6 +25,7 @@ properties: compatible: items: - enum: + - qcom,milos-aoss-qmp - qcom,qcs615-aoss-qmp - qcom,qcs8300-aoss-qmp - qcom,qdu1000-aoss-qmp @@ -38,6 +39,7 @@ properties: - qcom,sdx75-aoss-qmp - qcom,sdm845-aoss-qmp - qcom,sm6350-aoss-qmp + - qcom,sm7150-aoss-qmp - qcom,sm8150-aoss-qmp - qcom,sm8250-aoss-qmp - qcom,sm8350-aoss-qmp diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml index ce7e20dd22c9..fdc6fc17ed71 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml @@ -18,6 +18,7 @@ properties: compatible: items: - enum: + - qcom,sm7150-dcc - qcom,sm8150-dcc - qcom,sc7280-dcc - qcom,sc7180-dcc diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml index 4c9e78f29523..48114bb0c927 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml @@ -37,6 +37,7 @@ properties: - const: qcom,pmic-glink - items: - enum: + - qcom,milos-pmic-glink - qcom,sm8650-pmic-glink - qcom,sm8750-pmic-glink - qcom,x1e80100-pmic-glink diff --git a/Documentation/devicetree/bindings/sram/qcom,imem.yaml b/Documentation/devicetree/bindings/sram/qcom,imem.yaml index 2711f90d9664..72d35e30c439 100644 --- a/Documentation/devicetree/bindings/sram/qcom,imem.yaml +++ b/Documentation/devicetree/bindings/sram/qcom,imem.yaml @@ -22,17 +22,32 @@ properties: - qcom,msm8974-imem - qcom,msm8976-imem - qcom,qcs404-imem + - qcom,qcs615-imem - qcom,qcs8300-imem - qcom,qdu1000-imem - qcom,sa8775p-imem + - qcom,sar2130p-imem - qcom,sc7180-imem - qcom,sc7280-imem + - qcom,sc8280xp-imem - qcom,sdm630-imem - qcom,sdm845-imem - qcom,sdx55-imem - qcom,sdx65-imem + - qcom,sdx75-imem + - qcom,sm6115-imem + - qcom,sm6125-imem + - qcom,sm6350-imem - qcom,sm6375-imem + - qcom,sm7150-imem + - qcom,sm8150-imem + - qcom,sm8250-imem + - qcom,sm8350-imem - qcom,sm8450-imem + - qcom,sm8550-imem + - qcom,sm8650-imem + - qcom,sm8750-imem + - qcom,x1e80100-imem - const: syscon - const: simple-mfd diff --git a/MAINTAINERS b/MAINTAINERS index 8b48d5bcce5f..254ddd29b95a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2620,6 +2620,8 @@ L: imx@lists.linux.dev L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux.git +F: Documentation/devicetree/bindings/firmware/fsl* +F: Documentation/devicetree/bindings/firmware/nxp* F: arch/arm/boot/dts/nxp/imx/ F: arch/arm/boot/dts/nxp/mxs/ F: arch/arm64/boot/dts/freescale/ @@ -3035,8 +3037,10 @@ R: Ghennadi Procopciuc <ghennadi.procopciuc@oss.nxp.com> R: NXP S32 Linux Team <s32@nxp.com> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained +F: Documentation/devicetree/bindings/rtc/nxp,s32g-rtc.yaml F: arch/arm64/boot/dts/freescale/s32g*.dts* F: drivers/pinctrl/nxp/ +F: drivers/rtc/rtc-s32g.c ARM/NXP S32G/S32R DWMAC ETHERNET DRIVER M: Jan Petrous <jan.petrous@oss.nxp.com> @@ -20819,6 +20823,14 @@ S: Maintained F: Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml F: drivers/media/platform/raspberrypi/rp1-cfe/ +RASPBERRY PI RP1 PCI DRIVER +M: Andrea della Porta <andrea.porta@suse.com> +S: Maintained +F: arch/arm64/boot/dts/broadcom/rp1*.dts* +F: drivers/clk/clk-rp1.c +F: drivers/misc/rp1/ +F: drivers/pinctrl/pinctrl-rp1.c + RC-CORE / LIRC FRAMEWORK M: Sean Young <sean@mess.org> L: linux-media@vger.kernel.org diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index ff669a8ccad9..fe7600283e70 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -87,6 +87,12 @@ config HISILICON_LPC Driver to enable I/O access to devices attached to the Low Pin Count bus on the HiSilicon Hip06/7 SoC. +config IMX_AIPSTZ + tristate "Support for IMX Secure AHB to IP Slave bus (AIPSTZ) bridge" + depends on ARCH_MXC + help + Enable support for IMX AIPSTZ bridge. + config IMX_WEIM bool "Freescale EIM DRIVER" depends on ARCH_MXC || COMPILE_TEST diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index cddd4984d6af..8e693fe8a03a 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -15,6 +15,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_AIPSTZ) += imx-aipstz.o obj-$(CONFIG_IMX_WEIM) += imx-weim.o obj-$(CONFIG_INTEL_IXP4XX_EB) += intel-ixp4xx-eb.o obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o diff --git a/drivers/bus/imx-aipstz.c b/drivers/bus/imx-aipstz.c new file mode 100644 index 000000000000..5fdf377f5d06 --- /dev/null +++ b/drivers/bus/imx-aipstz.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2025 NXP + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> + +#define IMX_AIPSTZ_MPR0 0x0 + +struct imx_aipstz_config { + u32 mpr0; +}; + +struct imx_aipstz_data { + void __iomem *base; + const struct imx_aipstz_config *default_cfg; +}; + +static void imx_aipstz_apply_default(struct imx_aipstz_data *data) +{ + writel(data->default_cfg->mpr0, data->base + IMX_AIPSTZ_MPR0); +} + +static const struct of_device_id imx_aipstz_match_table[] = { + { .compatible = "simple-bus", }, + { } +}; + +static int imx_aipstz_probe(struct platform_device *pdev) +{ + struct imx_aipstz_data *data; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return dev_err_probe(&pdev->dev, -ENOMEM, + "failed to allocate data memory\n"); + + data->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(data->base)) + return dev_err_probe(&pdev->dev, -ENOMEM, + "failed to get/ioremap AC memory\n"); + + data->default_cfg = of_device_get_match_data(&pdev->dev); + + imx_aipstz_apply_default(data); + + dev_set_drvdata(&pdev->dev, data); + + pm_runtime_set_active(&pdev->dev); + devm_pm_runtime_enable(&pdev->dev); + + return of_platform_populate(pdev->dev.of_node, imx_aipstz_match_table, + NULL, &pdev->dev); +} + +static void imx_aipstz_remove(struct platform_device *pdev) +{ + of_platform_depopulate(&pdev->dev); +} + +static int imx_aipstz_runtime_resume(struct device *dev) +{ + struct imx_aipstz_data *data = dev_get_drvdata(dev); + + /* restore potentially lost configuration during domain power-off */ + imx_aipstz_apply_default(data); + + return 0; +} + +static const struct dev_pm_ops imx_aipstz_pm_ops = { + RUNTIME_PM_OPS(NULL, imx_aipstz_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) +}; + +/* + * following configuration is equivalent to: + * masters 0-7 => trusted for R/W + use AHB's HPROT[1] to det. privilege + */ +static const struct imx_aipstz_config imx8mp_aipstz_default_cfg = { + .mpr0 = 0x77777777, +}; + +static const struct of_device_id imx_aipstz_of_ids[] = { + { .compatible = "fsl,imx8mp-aipstz", .data = &imx8mp_aipstz_default_cfg }, + { } +}; +MODULE_DEVICE_TABLE(of, imx_aipstz_of_ids); + +static struct platform_driver imx_aipstz_of_driver = { + .probe = imx_aipstz_probe, + .remove = imx_aipstz_remove, + .driver = { + .name = "imx-aipstz", + .of_match_table = imx_aipstz_of_ids, + .pm = pm_ptr(&imx_aipstz_pm_ops), + }, +}; +module_platform_driver(imx_aipstz_of_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IMX secure AHB to IP Slave bus (AIPSTZ) bridge driver"); +MODULE_AUTHOR("Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>"); diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 9f624e5da991..5566ad11399e 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -2170,9 +2170,8 @@ static int sysc_reset(struct sysc *ddata) static int sysc_init_module(struct sysc *ddata) { bool rstctrl_deasserted = false; - int error = 0; + int error = sysc_clockdomain_init(ddata); - error = sysc_clockdomain_init(ddata); if (error) return error; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 19c1ed280fd7..b5a77669ed23 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -88,6 +88,15 @@ config COMMON_CLK_RK808 These multi-function devices have two fixed-rate oscillators, clocked at 32KHz each. Clkout1 is always on, Clkout2 can off by control register. +config COMMON_CLK_RP1 + tristate "Raspberry Pi RP1-based clock support" + depends on MISC_RP1 || COMPILE_TEST + default MISC_RP1 + help + Enable common clock framework support for Raspberry Pi RP1. + This multi-function device has 3 main PLLs and several clock + generators to drive the internal sub-peripherals. + config COMMON_CLK_HI655X tristate "Clock driver for Hi655x" if EXPERT depends on (MFD_HI655X_PMIC || COMPILE_TEST) diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 42867cd37c33..3d04f3463452 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_CLK_LS1028A_PLLDIG) += clk-plldig.o obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm.o obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o +obj-$(CONFIG_COMMON_CLK_RP1) += clk-rp1.o obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o diff --git a/drivers/clk/clk-rp1.c b/drivers/clk/clk-rp1.c new file mode 100644 index 000000000000..afff90d48734 --- /dev/null +++ b/drivers/clk/clk-rp1.c @@ -0,0 +1,1494 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Raspberry Pi Ltd. + * + * Clock driver for RP1 PCIe multifunction chip. + */ + +#include <linux/bitfield.h> +#include <linux/clk-provider.h> +#include <linux/regmap.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/units.h> + +#include <dt-bindings/clock/raspberrypi,rp1-clocks.h> + +#define PLL_SYS_OFFSET 0x08000 +#define PLL_SYS_CS (PLL_SYS_OFFSET + 0x00) +#define PLL_SYS_PWR (PLL_SYS_OFFSET + 0x04) +#define PLL_SYS_FBDIV_INT (PLL_SYS_OFFSET + 0x08) +#define PLL_SYS_FBDIV_FRAC (PLL_SYS_OFFSET + 0x0c) +#define PLL_SYS_PRIM (PLL_SYS_OFFSET + 0x10) +#define PLL_SYS_SEC (PLL_SYS_OFFSET + 0x14) + +#define PLL_AUDIO_OFFSET 0x0c000 +#define PLL_AUDIO_CS (PLL_AUDIO_OFFSET + 0x00) +#define PLL_AUDIO_PWR (PLL_AUDIO_OFFSET + 0x04) +#define PLL_AUDIO_FBDIV_INT (PLL_AUDIO_OFFSET + 0x08) +#define PLL_AUDIO_FBDIV_FRAC (PLL_AUDIO_OFFSET + 0x0c) +#define PLL_AUDIO_PRIM (PLL_AUDIO_OFFSET + 0x10) +#define PLL_AUDIO_SEC (PLL_AUDIO_OFFSET + 0x14) +#define PLL_AUDIO_TERN (PLL_AUDIO_OFFSET + 0x18) + +#define PLL_VIDEO_OFFSET 0x10000 +#define PLL_VIDEO_CS (PLL_VIDEO_OFFSET + 0x00) +#define PLL_VIDEO_PWR (PLL_VIDEO_OFFSET + 0x04) +#define PLL_VIDEO_FBDIV_INT (PLL_VIDEO_OFFSET + 0x08) +#define PLL_VIDEO_FBDIV_FRAC (PLL_VIDEO_OFFSET + 0x0c) +#define PLL_VIDEO_PRIM (PLL_VIDEO_OFFSET + 0x10) +#define PLL_VIDEO_SEC (PLL_VIDEO_OFFSET + 0x14) + +#define GPCLK_OE_CTRL 0x00000 + +#define CLK_SYS_OFFSET 0x00014 +#define CLK_SYS_CTRL (CLK_SYS_OFFSET + 0x00) +#define CLK_SYS_DIV_INT (CLK_SYS_OFFSET + 0x04) +#define CLK_SYS_SEL (CLK_SYS_OFFSET + 0x0c) + +#define CLK_SLOW_OFFSET 0x00024 +#define CLK_SLOW_SYS_CTRL (CLK_SLOW_OFFSET + 0x00) +#define CLK_SLOW_SYS_DIV_INT (CLK_SLOW_OFFSET + 0x04) +#define CLK_SLOW_SYS_SEL (CLK_SLOW_OFFSET + 0x0c) + +#define CLK_DMA_OFFSET 0x00044 +#define CLK_DMA_CTRL (CLK_DMA_OFFSET + 0x00) +#define CLK_DMA_DIV_INT (CLK_DMA_OFFSET + 0x04) +#define CLK_DMA_SEL (CLK_DMA_OFFSET + 0x0c) + +#define CLK_UART_OFFSET 0x00054 +#define CLK_UART_CTRL (CLK_UART_OFFSET + 0x00) +#define CLK_UART_DIV_INT (CLK_UART_OFFSET + 0x04) +#define CLK_UART_SEL (CLK_UART_OFFSET + 0x0c) + +#define CLK_ETH_OFFSET 0x00064 +#define CLK_ETH_CTRL (CLK_ETH_OFFSET + 0x00) +#define CLK_ETH_DIV_INT (CLK_ETH_OFFSET + 0x04) +#define CLK_ETH_SEL (CLK_ETH_OFFSET + 0x0c) + +#define CLK_PWM0_OFFSET 0x00074 +#define CLK_PWM0_CTRL (CLK_PWM0_OFFSET + 0x00) +#define CLK_PWM0_DIV_INT (CLK_PWM0_OFFSET + 0x04) +#define CLK_PWM0_DIV_FRAC (CLK_PWM0_OFFSET + 0x08) +#define CLK_PWM0_SEL (CLK_PWM0_OFFSET + 0x0c) + +#define CLK_PWM1_OFFSET 0x00084 +#define CLK_PWM1_CTRL (CLK_PWM1_OFFSET + 0x00) +#define CLK_PWM1_DIV_INT (CLK_PWM1_OFFSET + 0x04) +#define CLK_PWM1_DIV_FRAC (CLK_PWM1_OFFSET + 0x08) +#define CLK_PWM1_SEL (CLK_PWM1_OFFSET + 0x0c) + +#define CLK_AUDIO_IN_OFFSET 0x00094 +#define CLK_AUDIO_IN_CTRL (CLK_AUDIO_IN_OFFSET + 0x00) +#define CLK_AUDIO_IN_DIV_INT (CLK_AUDIO_IN_OFFSET + 0x04) +#define CLK_AUDIO_IN_SEL (CLK_AUDIO_IN_OFFSET + 0x0c) + +#define CLK_AUDIO_OUT_OFFSET 0x000a4 +#define CLK_AUDIO_OUT_CTRL (CLK_AUDIO_OUT_OFFSET + 0x00) +#define CLK_AUDIO_OUT_DIV_INT (CLK_AUDIO_OUT_OFFSET + 0x04) +#define CLK_AUDIO_OUT_SEL (CLK_AUDIO_OUT_OFFSET + 0x0c) + +#define CLK_I2S_OFFSET 0x000b4 +#define CLK_I2S_CTRL (CLK_I2S_OFFSET + 0x00) +#define CLK_I2S_DIV_INT (CLK_I2S_OFFSET + 0x04) +#define CLK_I2S_SEL (CLK_I2S_OFFSET + 0x0c) + +#define CLK_MIPI0_CFG_OFFSET 0x000c4 +#define CLK_MIPI0_CFG_CTRL (CLK_MIPI0_CFG_OFFSET + 0x00) +#define CLK_MIPI0_CFG_DIV_INT (CLK_MIPI0_CFG_OFFSET + 0x04) +#define CLK_MIPI0_CFG_SEL (CLK_MIPI0_CFG_OFFSET + 0x0c) + +#define CLK_MIPI1_CFG_OFFSET 0x000d4 +#define CLK_MIPI1_CFG_CTRL (CLK_MIPI1_CFG_OFFSET + 0x00) +#define CLK_MIPI1_CFG_DIV_INT (CLK_MIPI1_CFG_OFFSET + 0x04) +#define CLK_MIPI1_CFG_SEL (CLK_MIPI1_CFG_OFFSET + 0x0c) + +#define CLK_PCIE_AUX_OFFSET 0x000e4 +#define CLK_PCIE_AUX_CTRL (CLK_PCIE_AUX_OFFSET + 0x00) +#define CLK_PCIE_AUX_DIV_INT (CLK_PCIE_AUX_OFFSET + 0x04) +#define CLK_PCIE_AUX_SEL (CLK_PCIE_AUX_OFFSET + 0x0c) + +#define CLK_USBH0_MICROFRAME_OFFSET 0x000f4 +#define CLK_USBH0_MICROFRAME_CTRL (CLK_USBH0_MICROFRAME_OFFSET + 0x00) +#define CLK_USBH0_MICROFRAME_DIV_INT (CLK_USBH0_MICROFRAME_OFFSET + 0x04) +#define CLK_USBH0_MICROFRAME_SEL (CLK_USBH0_MICROFRAME_OFFSET + 0x0c) + +#define CLK_USBH1_MICROFRAME_OFFSET 0x00104 +#define CLK_USBH1_MICROFRAME_CTRL (CLK_USBH1_MICROFRAME_OFFSET + 0x00) +#define CLK_USBH1_MICROFRAME_DIV_INT (CLK_USBH1_MICROFRAME_OFFSET + 0x04) +#define CLK_USBH1_MICROFRAME_SEL (CLK_USBH1_MICROFRAME_OFFSET + 0x0c) + +#define CLK_USBH0_SUSPEND_OFFSET 0x00114 +#define CLK_USBH0_SUSPEND_CTRL (CLK_USBH0_SUSPEND_OFFSET + 0x00) +#define CLK_USBH0_SUSPEND_DIV_INT (CLK_USBH0_SUSPEND_OFFSET + 0x04) +#define CLK_USBH0_SUSPEND_SEL (CLK_USBH0_SUSPEND_OFFSET + 0x0c) + +#define CLK_USBH1_SUSPEND_OFFSET 0x00124 +#define CLK_USBH1_SUSPEND_CTRL (CLK_USBH1_SUSPEND_OFFSET + 0x00) +#define CLK_USBH1_SUSPEND_DIV_INT (CLK_USBH1_SUSPEND_OFFSET + 0x04) +#define CLK_USBH1_SUSPEND_SEL (CLK_USBH1_SUSPEND_OFFSET + 0x0c) + +#define CLK_ETH_TSU_OFFSET 0x00134 +#define CLK_ETH_TSU_CTRL (CLK_ETH_TSU_OFFSET + 0x00) +#define CLK_ETH_TSU_DIV_INT (CLK_ETH_TSU_OFFSET + 0x04) +#define CLK_ETH_TSU_SEL (CLK_ETH_TSU_OFFSET + 0x0c) + +#define CLK_ADC_OFFSET 0x00144 +#define CLK_ADC_CTRL (CLK_ADC_OFFSET + 0x00) +#define CLK_ADC_DIV_INT (CLK_ADC_OFFSET + 0x04) +#define CLK_ADC_SEL (CLK_ADC_OFFSET + 0x0c) + +#define CLK_SDIO_TIMER_OFFSET 0x00154 +#define CLK_SDIO_TIMER_CTRL (CLK_SDIO_TIMER_OFFSET + 0x00) +#define CLK_SDIO_TIMER_DIV_INT (CLK_SDIO_TIMER_OFFSET + 0x04) +#define CLK_SDIO_TIMER_SEL (CLK_SDIO_TIMER_OFFSET + 0x0c) + +#define CLK_SDIO_ALT_SRC_OFFSET 0x00164 +#define CLK_SDIO_ALT_SRC_CTRL (CLK_SDIO_ALT_SRC_OFFSET + 0x00) +#define CLK_SDIO_ALT_SRC_DIV_INT (CLK_SDIO_ALT_SRC_OFFSET + 0x04) +#define CLK_SDIO_ALT_SRC_SEL (CLK_SDIO_ALT_SRC_OFFSET + 0x0c) + +#define CLK_GP0_OFFSET 0x00174 +#define CLK_GP0_CTRL (CLK_GP0_OFFSET + 0x00) +#define CLK_GP0_DIV_INT (CLK_GP0_OFFSET + 0x04) +#define CLK_GP0_DIV_FRAC (CLK_GP0_OFFSET + 0x08) +#define CLK_GP0_SEL (CLK_GP0_OFFSET + 0x0c) + +#define CLK_GP1_OFFSET 0x00184 +#define CLK_GP1_CTRL (CLK_GP1_OFFSET + 0x00) +#define CLK_GP1_DIV_INT (CLK_GP1_OFFSET + 0x04) +#define CLK_GP1_DIV_FRAC (CLK_GP1_OFFSET + 0x08) +#define CLK_GP1_SEL (CLK_GP1_OFFSET + 0x0c) + +#define CLK_GP2_OFFSET 0x00194 +#define CLK_GP2_CTRL (CLK_GP2_OFFSET + 0x00) +#define CLK_GP2_DIV_INT (CLK_GP2_OFFSET + 0x04) +#define CLK_GP2_DIV_FRAC (CLK_GP2_OFFSET + 0x08) +#define CLK_GP2_SEL (CLK_GP2_OFFSET + 0x0c) + +#define CLK_GP3_OFFSET 0x001a4 +#define CLK_GP3_CTRL (CLK_GP3_OFFSET + 0x00) +#define CLK_GP3_DIV_INT (CLK_GP3_OFFSET + 0x04) +#define CLK_GP3_DIV_FRAC (CLK_GP3_OFFSET + 0x08) +#define CLK_GP3_SEL (CLK_GP3_OFFSET + 0x0c) + +#define CLK_GP4_OFFSET 0x001b4 +#define CLK_GP4_CTRL (CLK_GP4_OFFSET + 0x00) +#define CLK_GP4_DIV_INT (CLK_GP4_OFFSET + 0x04) +#define CLK_GP4_DIV_FRAC (CLK_GP4_OFFSET + 0x08) +#define CLK_GP4_SEL (CLK_GP4_OFFSET + 0x0c) + +#define CLK_GP5_OFFSET 0x001c4 +#define CLK_GP5_CTRL (CLK_GP5_OFFSET + 0x00) +#define CLK_GP5_DIV_INT (CLK_GP5_OFFSET + 0x04) +#define CLK_GP5_DIV_FRAC (CLK_GP5_OFFSET + 0x08) +#define CLK_GP5_SEL (CLK_GP5_OFFSET + 0x0c) + +#define CLK_SYS_RESUS_CTRL 0x0020c + +#define CLK_SLOW_SYS_RESUS_CTRL 0x00214 + +#define FC0_OFFSET 0x0021c +#define FC0_REF_KHZ (FC0_OFFSET + 0x00) +#define FC0_MIN_KHZ (FC0_OFFSET + 0x04) +#define FC0_MAX_KHZ (FC0_OFFSET + 0x08) +#define FC0_DELAY (FC0_OFFSET + 0x0c) +#define FC0_INTERVAL (FC0_OFFSET + 0x10) +#define FC0_SRC (FC0_OFFSET + 0x14) +#define FC0_STATUS (FC0_OFFSET + 0x18) +#define FC0_RESULT (FC0_OFFSET + 0x1c) +#define FC_SIZE 0x20 +#define FC_COUNT 8 +#define FC_NUM(idx, off) ((idx) * 32 + (off)) + +#define AUX_SEL 1 + +#define VIDEO_CLOCKS_OFFSET 0x4000 +#define VIDEO_CLK_VEC_CTRL (VIDEO_CLOCKS_OFFSET + 0x0000) +#define VIDEO_CLK_VEC_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0004) +#define VIDEO_CLK_VEC_SEL (VIDEO_CLOCKS_OFFSET + 0x000c) +#define VIDEO_CLK_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0010) +#define VIDEO_CLK_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0014) +#define VIDEO_CLK_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x001c) +#define VIDEO_CLK_MIPI0_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0020) +#define VIDEO_CLK_MIPI0_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0024) +#define VIDEO_CLK_MIPI0_DPI_DIV_FRAC (VIDEO_CLOCKS_OFFSET + 0x0028) +#define VIDEO_CLK_MIPI0_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x002c) +#define VIDEO_CLK_MIPI1_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0030) +#define VIDEO_CLK_MIPI1_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0034) +#define VIDEO_CLK_MIPI1_DPI_DIV_FRAC (VIDEO_CLOCKS_OFFSET + 0x0038) +#define VIDEO_CLK_MIPI1_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x003c) + +#define DIV_INT_8BIT_MAX GENMASK(7, 0) /* max divide for most clocks */ +#define DIV_INT_16BIT_MAX GENMASK(15, 0) /* max divide for GPx, PWM */ +#define DIV_INT_24BIT_MAX GENMASK(23, 0) /* max divide for CLK_SYS */ + +#define FC0_STATUS_DONE BIT(4) +#define FC0_STATUS_RUNNING BIT(8) +#define FC0_RESULT_FRAC_SHIFT 5 + +#define PLL_PRIM_DIV1_MASK GENMASK(18, 16) +#define PLL_PRIM_DIV2_MASK GENMASK(14, 12) + +#define PLL_SEC_DIV_MASK GENMASK(12, 8) + +#define PLL_CS_LOCK BIT(31) +#define PLL_CS_REFDIV_MASK BIT(1) + +#define PLL_PWR_PD BIT(0) +#define PLL_PWR_DACPD BIT(1) +#define PLL_PWR_DSMPD BIT(2) +#define PLL_PWR_POSTDIVPD BIT(3) +#define PLL_PWR_4PHASEPD BIT(4) +#define PLL_PWR_VCOPD BIT(5) +#define PLL_PWR_MASK GENMASK(5, 0) + +#define PLL_SEC_RST BIT(16) +#define PLL_SEC_IMPL BIT(31) + +/* PLL phase output for both PRI and SEC */ +#define PLL_PH_EN BIT(4) +#define PLL_PH_PHASE_SHIFT 0 + +#define RP1_PLL_PHASE_0 0 +#define RP1_PLL_PHASE_90 1 +#define RP1_PLL_PHASE_180 2 +#define RP1_PLL_PHASE_270 3 + +/* Clock fields for all clocks */ +#define CLK_CTRL_ENABLE BIT(11) +#define CLK_CTRL_AUXSRC_MASK GENMASK(9, 5) +#define CLK_CTRL_SRC_SHIFT 0 +#define CLK_DIV_FRAC_BITS 16 + +#define LOCK_TIMEOUT_US 100000 +#define LOCK_POLL_DELAY_US 5 + +#define MAX_CLK_PARENTS 16 + +#define PLL_DIV_INVALID 19 +/* + * Secondary PLL channel output divider table. + * Divider values range from 8 to 19, where + * 19 means invalid. + */ +static const struct clk_div_table pll_sec_div_table[] = { + { 0x00, PLL_DIV_INVALID }, + { 0x01, PLL_DIV_INVALID }, + { 0x02, PLL_DIV_INVALID }, + { 0x03, PLL_DIV_INVALID }, + { 0x04, PLL_DIV_INVALID }, + { 0x05, PLL_DIV_INVALID }, + { 0x06, PLL_DIV_INVALID }, + { 0x07, PLL_DIV_INVALID }, + { 0x08, 8 }, + { 0x09, 9 }, + { 0x0a, 10 }, + { 0x0b, 11 }, + { 0x0c, 12 }, + { 0x0d, 13 }, + { 0x0e, 14 }, + { 0x0f, 15 }, + { 0x10, 16 }, + { 0x11, 17 }, + { 0x12, 18 }, + { 0x13, PLL_DIV_INVALID }, + { 0x14, PLL_DIV_INVALID }, + { 0x15, PLL_DIV_INVALID }, + { 0x16, PLL_DIV_INVALID }, + { 0x17, PLL_DIV_INVALID }, + { 0x18, PLL_DIV_INVALID }, + { 0x19, PLL_DIV_INVALID }, + { 0x1a, PLL_DIV_INVALID }, + { 0x1b, PLL_DIV_INVALID }, + { 0x1c, PLL_DIV_INVALID }, + { 0x1d, PLL_DIV_INVALID }, + { 0x1e, PLL_DIV_INVALID }, + { 0x1f, PLL_DIV_INVALID }, + { 0 } +}; + +struct rp1_clockman { + struct device *dev; + void __iomem *regs; + struct regmap *regmap; + spinlock_t regs_lock; /* spinlock for all clocks */ + + /* Must be last */ + struct clk_hw_onecell_data onecell; +}; + +struct rp1_pll_core_data { + u32 cs_reg; + u32 pwr_reg; + u32 fbdiv_int_reg; + u32 fbdiv_frac_reg; + u32 fc0_src; +}; + +struct rp1_pll_data { + u32 ctrl_reg; + u32 fc0_src; +}; + +struct rp1_pll_ph_data { + unsigned int phase; + unsigned int fixed_divider; + u32 ph_reg; + u32 fc0_src; +}; + +struct rp1_pll_divider_data { + u32 sec_reg; + u32 fc0_src; +}; + +struct rp1_clock_data { + int num_std_parents; + int num_aux_parents; + u32 oe_mask; + u32 clk_src_mask; + u32 ctrl_reg; + u32 div_int_reg; + u32 div_frac_reg; + u32 sel_reg; + u32 div_int_max; + unsigned long max_freq; + u32 fc0_src; +}; + +struct rp1_clk_desc { + struct clk_hw *(*clk_register)(struct rp1_clockman *clockman, + struct rp1_clk_desc *desc); + const void *data; + struct clk_hw hw; + struct rp1_clockman *clockman; + unsigned long cached_rate; + struct clk_divider div; +}; + +static inline +void clockman_write(struct rp1_clockman *clockman, u32 reg, u32 val) +{ + regmap_write(clockman->regmap, reg, val); +} + +static inline u32 clockman_read(struct rp1_clockman *clockman, u32 reg) +{ + u32 val; + + regmap_read(clockman->regmap, reg, &val); + + return val; +} + +static int rp1_pll_core_is_on(struct clk_hw *hw) +{ + struct rp1_clk_desc *pll_core = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll_core->clockman; + const struct rp1_pll_core_data *data = pll_core->data; + u32 pwr = clockman_read(clockman, data->pwr_reg); + + return (pwr & PLL_PWR_PD) || (pwr & PLL_PWR_POSTDIVPD); +} + +static int rp1_pll_core_on(struct clk_hw *hw) +{ + struct rp1_clk_desc *pll_core = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll_core->clockman; + const struct rp1_pll_core_data *data = pll_core->data; + u32 fbdiv_frac, val; + int ret; + + spin_lock(&clockman->regs_lock); + + if (!(clockman_read(clockman, data->cs_reg) & PLL_CS_LOCK)) { + /* Reset to a known state. */ + clockman_write(clockman, data->pwr_reg, PLL_PWR_MASK); + clockman_write(clockman, data->fbdiv_int_reg, 20); + clockman_write(clockman, data->fbdiv_frac_reg, 0); + clockman_write(clockman, data->cs_reg, PLL_CS_REFDIV_MASK); + } + + /* Come out of reset. */ + fbdiv_frac = clockman_read(clockman, data->fbdiv_frac_reg); + clockman_write(clockman, data->pwr_reg, fbdiv_frac ? 0 : PLL_PWR_DSMPD); + spin_unlock(&clockman->regs_lock); + + /* Wait for the PLL to lock. */ + ret = regmap_read_poll_timeout(clockman->regmap, data->cs_reg, val, + val & PLL_CS_LOCK, + LOCK_POLL_DELAY_US, LOCK_TIMEOUT_US); + if (ret) + dev_err(clockman->dev, "%s: can't lock PLL\n", + clk_hw_get_name(hw)); + + return ret; +} + +static void rp1_pll_core_off(struct clk_hw *hw) +{ + struct rp1_clk_desc *pll_core = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll_core->clockman; + const struct rp1_pll_core_data *data = pll_core->data; + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->pwr_reg, 0); + spin_unlock(&clockman->regs_lock); +} + +static inline unsigned long get_pll_core_divider(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate, + u32 *div_int, u32 *div_frac) +{ + u32 fbdiv_int, fbdiv_frac; + unsigned long calc_rate; + u64 shifted_fbdiv_int; + u64 div_fp64; /* 32.32 fixed point fraction. */ + + /* Factor of reference clock to VCO frequency. */ + div_fp64 = (u64)(rate) << 32; + div_fp64 = DIV_ROUND_CLOSEST_ULL(div_fp64, parent_rate); + + /* Round the fractional component at 24 bits. */ + div_fp64 += 1 << (32 - 24 - 1); + + fbdiv_int = div_fp64 >> 32; + fbdiv_frac = (div_fp64 >> (32 - 24)) & 0xffffff; + + shifted_fbdiv_int = (u64)fbdiv_int << 24; + calc_rate = (u64)parent_rate * (shifted_fbdiv_int + fbdiv_frac); + calc_rate += BIT(23); + calc_rate >>= 24; + + *div_int = fbdiv_int; + *div_frac = fbdiv_frac; + + return calc_rate; +} + +static int rp1_pll_core_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct rp1_clk_desc *pll_core = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll_core->clockman; + const struct rp1_pll_core_data *data = pll_core->data; + unsigned long calc_rate; + u32 fbdiv_int, fbdiv_frac; + + /* Disable dividers to start with. */ + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->fbdiv_int_reg, 0); + clockman_write(clockman, data->fbdiv_frac_reg, 0); + spin_unlock(&clockman->regs_lock); + + calc_rate = get_pll_core_divider(hw, rate, parent_rate, + &fbdiv_int, &fbdiv_frac); + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->pwr_reg, fbdiv_frac ? 0 : PLL_PWR_DSMPD); + clockman_write(clockman, data->fbdiv_int_reg, fbdiv_int); + clockman_write(clockman, data->fbdiv_frac_reg, fbdiv_frac); + spin_unlock(&clockman->regs_lock); + + /* Check that reference frequency is no greater than VCO / 16. */ + if (WARN_ON_ONCE(parent_rate > (rate / 16))) + return -ERANGE; + + pll_core->cached_rate = calc_rate; + + spin_lock(&clockman->regs_lock); + /* Don't need to divide ref unless parent_rate > (output freq / 16) */ + clockman_write(clockman, data->cs_reg, + clockman_read(clockman, data->cs_reg) | + PLL_CS_REFDIV_MASK); + spin_unlock(&clockman->regs_lock); + + return 0; +} + +static unsigned long rp1_pll_core_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rp1_clk_desc *pll_core = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll_core->clockman; + const struct rp1_pll_core_data *data = pll_core->data; + u32 fbdiv_int, fbdiv_frac; + unsigned long calc_rate; + u64 shifted_fbdiv_int; + + fbdiv_int = clockman_read(clockman, data->fbdiv_int_reg); + fbdiv_frac = clockman_read(clockman, data->fbdiv_frac_reg); + + shifted_fbdiv_int = (u64)fbdiv_int << 24; + calc_rate = (u64)parent_rate * (shifted_fbdiv_int + fbdiv_frac); + calc_rate += BIT(23); + calc_rate >>= 24; + + return calc_rate; +} + +static long rp1_pll_core_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u32 fbdiv_int, fbdiv_frac; + + return get_pll_core_divider(hw, rate, *parent_rate, + &fbdiv_int, &fbdiv_frac); +} + +static void get_pll_prim_dividers(unsigned long rate, unsigned long parent_rate, + u32 *divider1, u32 *divider2) +{ + unsigned int div1, div2; + unsigned int best_div1 = 7, best_div2 = 7; + unsigned long best_rate_diff = + abs_diff(DIV_ROUND_CLOSEST(parent_rate, best_div1 * best_div2), rate); + unsigned long rate_diff, calc_rate; + + for (div1 = 1; div1 <= 7; div1++) { + for (div2 = 1; div2 <= div1; div2++) { + calc_rate = DIV_ROUND_CLOSEST(parent_rate, div1 * div2); + rate_diff = abs_diff(calc_rate, rate); + + if (calc_rate == rate) { + best_div1 = div1; + best_div2 = div2; + goto done; + } else if (rate_diff < best_rate_diff) { + best_div1 = div1; + best_div2 = div2; + best_rate_diff = rate_diff; + } + } + } + +done: + *divider1 = best_div1; + *divider2 = best_div2; +} + +static int rp1_pll_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct rp1_clk_desc *pll = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll->clockman; + const struct rp1_pll_data *data = pll->data; + + u32 prim, prim_div1, prim_div2; + + get_pll_prim_dividers(rate, parent_rate, &prim_div1, &prim_div2); + + spin_lock(&clockman->regs_lock); + prim = clockman_read(clockman, data->ctrl_reg); + prim &= ~PLL_PRIM_DIV1_MASK; + prim |= FIELD_PREP(PLL_PRIM_DIV1_MASK, prim_div1); + prim &= ~PLL_PRIM_DIV2_MASK; + prim |= FIELD_PREP(PLL_PRIM_DIV2_MASK, prim_div2); + clockman_write(clockman, data->ctrl_reg, prim); + spin_unlock(&clockman->regs_lock); + + return 0; +} + +static unsigned long rp1_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rp1_clk_desc *pll = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll->clockman; + const struct rp1_pll_data *data = pll->data; + u32 prim, prim_div1, prim_div2; + + prim = clockman_read(clockman, data->ctrl_reg); + prim_div1 = FIELD_GET(PLL_PRIM_DIV1_MASK, prim); + prim_div2 = FIELD_GET(PLL_PRIM_DIV2_MASK, prim); + + if (!prim_div1 || !prim_div2) { + dev_err(clockman->dev, "%s: (%s) zero divider value\n", + __func__, clk_hw_get_name(hw)); + return 0; + } + + return DIV_ROUND_CLOSEST(parent_rate, prim_div1 * prim_div2); +} + +static long rp1_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u32 div1, div2; + + get_pll_prim_dividers(rate, *parent_rate, &div1, &div2); + + return DIV_ROUND_CLOSEST(*parent_rate, div1 * div2); +} + +static int rp1_pll_ph_is_on(struct clk_hw *hw) +{ + struct rp1_clk_desc *pll_ph = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll_ph->clockman; + const struct rp1_pll_ph_data *data = pll_ph->data; + + return !!(clockman_read(clockman, data->ph_reg) & PLL_PH_EN); +} + +static int rp1_pll_ph_on(struct clk_hw *hw) +{ + struct rp1_clk_desc *pll_ph = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll_ph->clockman; + const struct rp1_pll_ph_data *data = pll_ph->data; + u32 ph_reg; + + spin_lock(&clockman->regs_lock); + ph_reg = clockman_read(clockman, data->ph_reg); + ph_reg |= data->phase << PLL_PH_PHASE_SHIFT; + ph_reg |= PLL_PH_EN; + clockman_write(clockman, data->ph_reg, ph_reg); + spin_unlock(&clockman->regs_lock); + + return 0; +} + +static void rp1_pll_ph_off(struct clk_hw *hw) +{ + struct rp1_clk_desc *pll_ph = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = pll_ph->clockman; + const struct rp1_pll_ph_data *data = pll_ph->data; + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->ph_reg, + clockman_read(clockman, data->ph_reg) & ~PLL_PH_EN); + spin_unlock(&clockman->regs_lock); +} + +static unsigned long rp1_pll_ph_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rp1_clk_desc *pll_ph = container_of(hw, struct rp1_clk_desc, hw); + const struct rp1_pll_ph_data *data = pll_ph->data; + + return parent_rate / data->fixed_divider; +} + +static long rp1_pll_ph_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct rp1_clk_desc *pll_ph = container_of(hw, struct rp1_clk_desc, hw); + const struct rp1_pll_ph_data *data = pll_ph->data; + + return *parent_rate / data->fixed_divider; +} + +static int rp1_pll_divider_is_on(struct clk_hw *hw) +{ + struct rp1_clk_desc *divider = container_of(hw, struct rp1_clk_desc, div.hw); + struct rp1_clockman *clockman = divider->clockman; + const struct rp1_pll_data *data = divider->data; + + return !(clockman_read(clockman, data->ctrl_reg) & PLL_SEC_RST); +} + +static int rp1_pll_divider_on(struct clk_hw *hw) +{ + struct rp1_clk_desc *divider = container_of(hw, struct rp1_clk_desc, div.hw); + struct rp1_clockman *clockman = divider->clockman; + const struct rp1_pll_data *data = divider->data; + + spin_lock(&clockman->regs_lock); + /* Check the implementation bit is set! */ + WARN_ON(!(clockman_read(clockman, data->ctrl_reg) & PLL_SEC_IMPL)); + clockman_write(clockman, data->ctrl_reg, + clockman_read(clockman, data->ctrl_reg) & ~PLL_SEC_RST); + spin_unlock(&clockman->regs_lock); + + return 0; +} + +static void rp1_pll_divider_off(struct clk_hw *hw) +{ + struct rp1_clk_desc *divider = container_of(hw, struct rp1_clk_desc, div.hw); + struct rp1_clockman *clockman = divider->clockman; + const struct rp1_pll_data *data = divider->data; + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->ctrl_reg, + clockman_read(clockman, data->ctrl_reg) | PLL_SEC_RST); + spin_unlock(&clockman->regs_lock); +} + +static int rp1_pll_divider_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct rp1_clk_desc *divider = container_of(hw, struct rp1_clk_desc, div.hw); + struct rp1_clockman *clockman = divider->clockman; + const struct rp1_pll_data *data = divider->data; + u32 div, sec; + + div = DIV_ROUND_UP_ULL(parent_rate, rate); + div = clamp(div, 8u, 19u); + + spin_lock(&clockman->regs_lock); + sec = clockman_read(clockman, data->ctrl_reg); + sec &= ~PLL_SEC_DIV_MASK; + sec |= FIELD_PREP(PLL_SEC_DIV_MASK, div); + + /* Must keep the divider in reset to change the value. */ + sec |= PLL_SEC_RST; + clockman_write(clockman, data->ctrl_reg, sec); + + /* must sleep 10 pll vco cycles */ + ndelay(div64_ul(10ULL * div * NSEC_PER_SEC, parent_rate)); + + sec &= ~PLL_SEC_RST; + clockman_write(clockman, data->ctrl_reg, sec); + spin_unlock(&clockman->regs_lock); + + return 0; +} + +static unsigned long rp1_pll_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return clk_divider_ops.recalc_rate(hw, parent_rate); +} + +static long rp1_pll_divider_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *parent_rate) +{ + return clk_divider_ops.round_rate(hw, rate, parent_rate); +} + +static int rp1_clock_is_on(struct clk_hw *hw) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + + return !!(clockman_read(clockman, data->ctrl_reg) & CLK_CTRL_ENABLE); +} + +static unsigned long rp1_clock_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + u64 calc_rate; + u64 div; + u32 frac; + + div = clockman_read(clockman, data->div_int_reg); + frac = (data->div_frac_reg != 0) ? + clockman_read(clockman, data->div_frac_reg) : 0; + + /* If the integer portion of the divider is 0, treat it as 2^16 */ + if (!div) + div = 1 << 16; + + div = (div << CLK_DIV_FRAC_BITS) | (frac >> (32 - CLK_DIV_FRAC_BITS)); + + calc_rate = (u64)parent_rate << CLK_DIV_FRAC_BITS; + calc_rate = div64_u64(calc_rate, div); + + return calc_rate; +} + +static int rp1_clock_on(struct clk_hw *hw) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->ctrl_reg, + clockman_read(clockman, data->ctrl_reg) | CLK_CTRL_ENABLE); + /* If this is a GPCLK, turn on the output-enable */ + if (data->oe_mask) + clockman_write(clockman, GPCLK_OE_CTRL, + clockman_read(clockman, GPCLK_OE_CTRL) | data->oe_mask); + spin_unlock(&clockman->regs_lock); + + return 0; +} + +static void rp1_clock_off(struct clk_hw *hw) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->ctrl_reg, + clockman_read(clockman, data->ctrl_reg) & ~CLK_CTRL_ENABLE); + /* If this is a GPCLK, turn off the output-enable */ + if (data->oe_mask) + clockman_write(clockman, GPCLK_OE_CTRL, + clockman_read(clockman, GPCLK_OE_CTRL) & ~data->oe_mask); + spin_unlock(&clockman->regs_lock); +} + +static u32 rp1_clock_choose_div(unsigned long rate, unsigned long parent_rate, + const struct rp1_clock_data *data) +{ + u64 div; + + /* + * Due to earlier rounding, calculated parent_rate may differ from + * expected value. Don't fail on a small discrepancy near unity divide. + */ + if (!rate || rate > parent_rate + (parent_rate >> CLK_DIV_FRAC_BITS)) + return 0; + + /* + * Always express div in fixed-point format for fractional division; + * If no fractional divider is present, the fraction part will be zero. + */ + if (data->div_frac_reg) { + div = (u64)parent_rate << CLK_DIV_FRAC_BITS; + div = DIV_ROUND_CLOSEST_ULL(div, rate); + } else { + div = DIV_ROUND_CLOSEST_ULL(parent_rate, rate); + div <<= CLK_DIV_FRAC_BITS; + } + + div = clamp(div, + 1ull << CLK_DIV_FRAC_BITS, + (u64)data->div_int_max << CLK_DIV_FRAC_BITS); + + return div; +} + +static u8 rp1_clock_get_parent(struct clk_hw *hw) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + u32 sel, ctrl; + u8 parent; + + /* Sel is one-hot, so find the first bit set */ + sel = clockman_read(clockman, data->sel_reg); + parent = ffs(sel) - 1; + + /* sel == 0 implies the parent clock is not enabled yet. */ + if (!sel) { + /* Read the clock src from the CTRL register instead */ + ctrl = clockman_read(clockman, data->ctrl_reg); + parent = (ctrl & data->clk_src_mask) >> CLK_CTRL_SRC_SHIFT; + } + + if (parent >= data->num_std_parents) + parent = AUX_SEL; + + if (parent == AUX_SEL) { + /* + * Clock parent is an auxiliary source, so get the parent from + * the AUXSRC register field. + */ + ctrl = clockman_read(clockman, data->ctrl_reg); + parent = FIELD_GET(CLK_CTRL_AUXSRC_MASK, ctrl); + parent += data->num_std_parents; + } + + return parent; +} + +static int rp1_clock_set_parent(struct clk_hw *hw, u8 index) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + u32 ctrl, sel; + + spin_lock(&clockman->regs_lock); + ctrl = clockman_read(clockman, data->ctrl_reg); + + if (index >= data->num_std_parents) { + /* This is an aux source request */ + if (index >= data->num_std_parents + data->num_aux_parents) { + spin_unlock(&clockman->regs_lock); + return -EINVAL; + } + + /* Select parent from aux list */ + ctrl &= ~CLK_CTRL_AUXSRC_MASK; + ctrl |= FIELD_PREP(CLK_CTRL_AUXSRC_MASK, index - data->num_std_parents); + /* Set src to aux list */ + ctrl &= ~data->clk_src_mask; + ctrl |= (AUX_SEL << CLK_CTRL_SRC_SHIFT) & data->clk_src_mask; + } else { + ctrl &= ~data->clk_src_mask; + ctrl |= (index << CLK_CTRL_SRC_SHIFT) & data->clk_src_mask; + } + + clockman_write(clockman, data->ctrl_reg, ctrl); + spin_unlock(&clockman->regs_lock); + + sel = rp1_clock_get_parent(hw); + if (sel != index) + return -EINVAL; + + return 0; +} + +static int rp1_clock_set_rate_and_parent(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate, + u8 parent) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + u32 div = rp1_clock_choose_div(rate, parent_rate, data); + + spin_lock(&clockman->regs_lock); + + clockman_write(clockman, data->div_int_reg, div >> CLK_DIV_FRAC_BITS); + if (data->div_frac_reg) + clockman_write(clockman, data->div_frac_reg, div << (32 - CLK_DIV_FRAC_BITS)); + + spin_unlock(&clockman->regs_lock); + + if (parent != 0xff) + return rp1_clock_set_parent(hw, parent); + + return 0; +} + +static int rp1_clock_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + return rp1_clock_set_rate_and_parent(hw, rate, parent_rate, 0xff); +} + +static void rp1_clock_choose_div_and_prate(struct clk_hw *hw, + int parent_idx, + unsigned long rate, + unsigned long *prate, + unsigned long *calc_rate) +{ + struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw); + const struct rp1_clock_data *data = clock->data; + struct clk_hw *parent; + u32 div; + u64 tmp; + + parent = clk_hw_get_parent_by_index(hw, parent_idx); + + *prate = clk_hw_get_rate(parent); + div = rp1_clock_choose_div(rate, *prate, data); + + if (!div) { + *calc_rate = 0; + return; + } + + /* Recalculate to account for rounding errors */ + tmp = (u64)*prate << CLK_DIV_FRAC_BITS; + tmp = div_u64(tmp, div); + + /* + * Prevent overclocks - if all parent choices result in + * a downstream clock in excess of the maximum, then the + * call to set the clock will fail. + */ + if (tmp > data->max_freq) + *calc_rate = 0; + else + *calc_rate = tmp; +} + +static int rp1_clock_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_hw *parent, *best_parent = NULL; + unsigned long best_rate = 0; + unsigned long best_prate = 0; + unsigned long best_rate_diff = ULONG_MAX; + unsigned long prate, calc_rate; + size_t i; + + /* + * If the NO_REPARENT flag is set, try to use existing parent. + */ + if ((clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT)) { + i = rp1_clock_get_parent(hw); + parent = clk_hw_get_parent_by_index(hw, i); + if (parent) { + rp1_clock_choose_div_and_prate(hw, i, req->rate, &prate, + &calc_rate); + if (calc_rate > 0) { + req->best_parent_hw = parent; + req->best_parent_rate = prate; + req->rate = calc_rate; + return 0; + } + } + } + + /* + * Select parent clock that results in the closest rate (lower or + * higher) + */ + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + parent = clk_hw_get_parent_by_index(hw, i); + if (!parent) + continue; + + rp1_clock_choose_div_and_prate(hw, i, req->rate, &prate, + &calc_rate); + + if (abs_diff(calc_rate, req->rate) < best_rate_diff) { + best_parent = parent; + best_prate = prate; + best_rate = calc_rate; + best_rate_diff = abs_diff(calc_rate, req->rate); + + if (best_rate_diff == 0) + break; + } + } + + if (best_rate == 0) + return -EINVAL; + + req->best_parent_hw = best_parent; + req->best_parent_rate = best_prate; + req->rate = best_rate; + + return 0; +} + +static const struct clk_ops rp1_pll_core_ops = { + .is_prepared = rp1_pll_core_is_on, + .prepare = rp1_pll_core_on, + .unprepare = rp1_pll_core_off, + .set_rate = rp1_pll_core_set_rate, + .recalc_rate = rp1_pll_core_recalc_rate, + .round_rate = rp1_pll_core_round_rate, +}; + +static const struct clk_ops rp1_pll_ops = { + .set_rate = rp1_pll_set_rate, + .recalc_rate = rp1_pll_recalc_rate, + .round_rate = rp1_pll_round_rate, +}; + +static const struct clk_ops rp1_pll_ph_ops = { + .is_prepared = rp1_pll_ph_is_on, + .prepare = rp1_pll_ph_on, + .unprepare = rp1_pll_ph_off, + .recalc_rate = rp1_pll_ph_recalc_rate, + .round_rate = rp1_pll_ph_round_rate, +}; + +static const struct clk_ops rp1_pll_divider_ops = { + .is_prepared = rp1_pll_divider_is_on, + .prepare = rp1_pll_divider_on, + .unprepare = rp1_pll_divider_off, + .set_rate = rp1_pll_divider_set_rate, + .recalc_rate = rp1_pll_divider_recalc_rate, + .round_rate = rp1_pll_divider_round_rate, +}; + +static const struct clk_ops rp1_clk_ops = { + .is_prepared = rp1_clock_is_on, + .prepare = rp1_clock_on, + .unprepare = rp1_clock_off, + .recalc_rate = rp1_clock_recalc_rate, + .get_parent = rp1_clock_get_parent, + .set_parent = rp1_clock_set_parent, + .set_rate_and_parent = rp1_clock_set_rate_and_parent, + .set_rate = rp1_clock_set_rate, + .determine_rate = rp1_clock_determine_rate, +}; + +static struct clk_hw *rp1_register_pll(struct rp1_clockman *clockman, + struct rp1_clk_desc *desc) +{ + int ret; + + desc->clockman = clockman; + + ret = devm_clk_hw_register(clockman->dev, &desc->hw); + if (ret) + return ERR_PTR(ret); + + return &desc->hw; +} + +static struct clk_hw *rp1_register_pll_divider(struct rp1_clockman *clockman, + struct rp1_clk_desc *desc) +{ + const struct rp1_pll_data *divider_data = desc->data; + int ret; + + desc->div.reg = clockman->regs + divider_data->ctrl_reg; + desc->div.shift = __ffs(PLL_SEC_DIV_MASK); + desc->div.width = __ffs(~(PLL_SEC_DIV_MASK >> desc->div.shift)); + desc->div.flags = CLK_DIVIDER_ROUND_CLOSEST; + desc->div.lock = &clockman->regs_lock; + desc->div.hw.init = desc->hw.init; + desc->div.table = pll_sec_div_table; + + desc->clockman = clockman; + + ret = devm_clk_hw_register(clockman->dev, &desc->div.hw); + if (ret) + return ERR_PTR(ret); + + return &desc->div.hw; +} + +static struct clk_hw *rp1_register_clock(struct rp1_clockman *clockman, + struct rp1_clk_desc *desc) +{ + const struct rp1_clock_data *clock_data = desc->data; + int ret; + + if (WARN_ON_ONCE(MAX_CLK_PARENTS < + clock_data->num_std_parents + clock_data->num_aux_parents)) + return ERR_PTR(-EINVAL); + + /* There must be a gap for the AUX selector */ + if (WARN_ON_ONCE(clock_data->num_std_parents > AUX_SEL && + desc->hw.init->parent_data[AUX_SEL].index != -1)) + return ERR_PTR(-EINVAL); + + desc->clockman = clockman; + + ret = devm_clk_hw_register(clockman->dev, &desc->hw); + if (ret) + return ERR_PTR(ret); + + return &desc->hw; +} + +/* Assignment helper macros for different clock types. */ +#define _REGISTER(f, ...) { .clk_register = f, __VA_ARGS__ } + +#define CLK_DATA(type, ...) .data = &(struct type) { __VA_ARGS__ } + +#define REGISTER_PLL(...) _REGISTER(&rp1_register_pll, \ + __VA_ARGS__) + +#define REGISTER_PLL_DIV(...) _REGISTER(&rp1_register_pll_divider, \ + __VA_ARGS__) + +#define REGISTER_CLK(...) _REGISTER(&rp1_register_clock, \ + __VA_ARGS__) + +static struct rp1_clk_desc pll_sys_core_desc = REGISTER_PLL( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_sys_core", + (const struct clk_parent_data[]) { { .index = 0 } }, + &rp1_pll_core_ops, + CLK_IS_CRITICAL + ), + CLK_DATA(rp1_pll_core_data, + .cs_reg = PLL_SYS_CS, + .pwr_reg = PLL_SYS_PWR, + .fbdiv_int_reg = PLL_SYS_FBDIV_INT, + .fbdiv_frac_reg = PLL_SYS_FBDIV_FRAC, + ) +); + +static struct rp1_clk_desc pll_audio_core_desc = REGISTER_PLL( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_audio_core", + (const struct clk_parent_data[]) { { .index = 0 } }, + &rp1_pll_core_ops, + CLK_IS_CRITICAL + ), + CLK_DATA(rp1_pll_core_data, + .cs_reg = PLL_AUDIO_CS, + .pwr_reg = PLL_AUDIO_PWR, + .fbdiv_int_reg = PLL_AUDIO_FBDIV_INT, + .fbdiv_frac_reg = PLL_AUDIO_FBDIV_FRAC, + ) +); + +static struct rp1_clk_desc pll_video_core_desc = REGISTER_PLL( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_video_core", + (const struct clk_parent_data[]) { { .index = 0 } }, + &rp1_pll_core_ops, + CLK_IS_CRITICAL + ), + CLK_DATA(rp1_pll_core_data, + .cs_reg = PLL_VIDEO_CS, + .pwr_reg = PLL_VIDEO_PWR, + .fbdiv_int_reg = PLL_VIDEO_FBDIV_INT, + .fbdiv_frac_reg = PLL_VIDEO_FBDIV_FRAC, + ) +); + +static struct rp1_clk_desc pll_sys_desc = REGISTER_PLL( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_sys", + (const struct clk_parent_data[]) { + { .hw = &pll_sys_core_desc.hw } + }, + &rp1_pll_ops, + 0 + ), + CLK_DATA(rp1_pll_data, + .ctrl_reg = PLL_SYS_PRIM, + .fc0_src = FC_NUM(0, 2), + ) +); + +static struct rp1_clk_desc pll_sys_sec_desc = REGISTER_PLL_DIV( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_sys_sec", + (const struct clk_parent_data[]) { + { .hw = &pll_sys_core_desc.hw } + }, + &rp1_pll_divider_ops, + 0 + ), + CLK_DATA(rp1_pll_data, + .ctrl_reg = PLL_SYS_SEC, + .fc0_src = FC_NUM(2, 2), + ) +); + +static struct rp1_clk_desc clk_eth_tsu_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_eth_tsu", + (const struct clk_parent_data[]) { { .index = 0 } }, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 1, + .ctrl_reg = CLK_ETH_TSU_CTRL, + .div_int_reg = CLK_ETH_TSU_DIV_INT, + .sel_reg = CLK_ETH_TSU_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 50 * HZ_PER_MHZ, + .fc0_src = FC_NUM(5, 7), + ) +); + +static const struct clk_parent_data clk_eth_parents[] = { + { .hw = &pll_sys_sec_desc.div.hw }, + { .hw = &pll_sys_desc.hw }, +}; + +static struct rp1_clk_desc clk_eth_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_eth", + clk_eth_parents, + &rp1_clk_ops, + 0 + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 0, + .num_aux_parents = 2, + .ctrl_reg = CLK_ETH_CTRL, + .div_int_reg = CLK_ETH_DIV_INT, + .sel_reg = CLK_ETH_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 125 * HZ_PER_MHZ, + .fc0_src = FC_NUM(4, 6), + ) +); + +static const struct clk_parent_data clk_sys_parents[] = { + { .index = 0 }, + { .index = -1 }, + { .hw = &pll_sys_desc.hw }, +}; + +static struct rp1_clk_desc clk_sys_desc = REGISTER_CLK( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "clk_sys", + clk_sys_parents, + &rp1_clk_ops, + CLK_IS_CRITICAL + ), + CLK_DATA(rp1_clock_data, + .num_std_parents = 3, + .num_aux_parents = 0, + .ctrl_reg = CLK_SYS_CTRL, + .div_int_reg = CLK_SYS_DIV_INT, + .sel_reg = CLK_SYS_SEL, + .div_int_max = DIV_INT_24BIT_MAX, + .max_freq = 200 * HZ_PER_MHZ, + .fc0_src = FC_NUM(0, 4), + .clk_src_mask = 0x3, + ) +); + +static struct rp1_clk_desc pll_sys_pri_ph_desc = REGISTER_PLL( + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "pll_sys_pri_ph", + (const struct clk_parent_data[]) { + { .hw = &pll_sys_desc.hw } + }, + &rp1_pll_ph_ops, + 0 + ), + CLK_DATA(rp1_pll_ph_data, + .ph_reg = PLL_SYS_PRIM, + .fixed_divider = 2, + .phase = RP1_PLL_PHASE_0, + .fc0_src = FC_NUM(1, 2), + ) +); + +static struct rp1_clk_desc *const clk_desc_array[] = { + [RP1_PLL_SYS_CORE] = &pll_sys_core_desc, + [RP1_PLL_AUDIO_CORE] = &pll_audio_core_desc, + [RP1_PLL_VIDEO_CORE] = &pll_video_core_desc, + [RP1_PLL_SYS] = &pll_sys_desc, + [RP1_CLK_ETH_TSU] = &clk_eth_tsu_desc, + [RP1_CLK_ETH] = &clk_eth_desc, + [RP1_CLK_SYS] = &clk_sys_desc, + [RP1_PLL_SYS_PRI_PH] = &pll_sys_pri_ph_desc, + [RP1_PLL_SYS_SEC] = &pll_sys_sec_desc, +}; + +static const struct regmap_range rp1_reg_ranges[] = { + regmap_reg_range(PLL_SYS_CS, PLL_SYS_SEC), + regmap_reg_range(PLL_AUDIO_CS, PLL_AUDIO_TERN), + regmap_reg_range(PLL_VIDEO_CS, PLL_VIDEO_SEC), + regmap_reg_range(GPCLK_OE_CTRL, GPCLK_OE_CTRL), + regmap_reg_range(CLK_SYS_CTRL, CLK_SYS_DIV_INT), + regmap_reg_range(CLK_SYS_SEL, CLK_SYS_SEL), + regmap_reg_range(CLK_SLOW_SYS_CTRL, CLK_SLOW_SYS_DIV_INT), + regmap_reg_range(CLK_SLOW_SYS_SEL, CLK_SLOW_SYS_SEL), + regmap_reg_range(CLK_DMA_CTRL, CLK_DMA_DIV_INT), + regmap_reg_range(CLK_DMA_SEL, CLK_DMA_SEL), + regmap_reg_range(CLK_UART_CTRL, CLK_UART_DIV_INT), + regmap_reg_range(CLK_UART_SEL, CLK_UART_SEL), + regmap_reg_range(CLK_ETH_CTRL, CLK_ETH_DIV_INT), + regmap_reg_range(CLK_ETH_SEL, CLK_ETH_SEL), + regmap_reg_range(CLK_PWM0_CTRL, CLK_PWM0_SEL), + regmap_reg_range(CLK_PWM1_CTRL, CLK_PWM1_SEL), + regmap_reg_range(CLK_AUDIO_IN_CTRL, CLK_AUDIO_IN_DIV_INT), + regmap_reg_range(CLK_AUDIO_IN_SEL, CLK_AUDIO_IN_SEL), + regmap_reg_range(CLK_AUDIO_OUT_CTRL, CLK_AUDIO_OUT_DIV_INT), + regmap_reg_range(CLK_AUDIO_OUT_SEL, CLK_AUDIO_OUT_SEL), + regmap_reg_range(CLK_I2S_CTRL, CLK_I2S_DIV_INT), + regmap_reg_range(CLK_I2S_SEL, CLK_I2S_SEL), + regmap_reg_range(CLK_MIPI0_CFG_CTRL, CLK_MIPI0_CFG_DIV_INT), + regmap_reg_range(CLK_MIPI0_CFG_SEL, CLK_MIPI0_CFG_SEL), + regmap_reg_range(CLK_MIPI1_CFG_CTRL, CLK_MIPI1_CFG_DIV_INT), + regmap_reg_range(CLK_MIPI1_CFG_SEL, CLK_MIPI1_CFG_SEL), + regmap_reg_range(CLK_PCIE_AUX_CTRL, CLK_PCIE_AUX_DIV_INT), + regmap_reg_range(CLK_PCIE_AUX_SEL, CLK_PCIE_AUX_SEL), + regmap_reg_range(CLK_USBH0_MICROFRAME_CTRL, CLK_USBH0_MICROFRAME_DIV_INT), + regmap_reg_range(CLK_USBH0_MICROFRAME_SEL, CLK_USBH0_MICROFRAME_SEL), + regmap_reg_range(CLK_USBH1_MICROFRAME_CTRL, CLK_USBH1_MICROFRAME_DIV_INT), + regmap_reg_range(CLK_USBH1_MICROFRAME_SEL, CLK_USBH1_MICROFRAME_SEL), + regmap_reg_range(CLK_USBH0_SUSPEND_CTRL, CLK_USBH0_SUSPEND_DIV_INT), + regmap_reg_range(CLK_USBH0_SUSPEND_SEL, CLK_USBH0_SUSPEND_SEL), + regmap_reg_range(CLK_USBH1_SUSPEND_CTRL, CLK_USBH1_SUSPEND_DIV_INT), + regmap_reg_range(CLK_USBH1_SUSPEND_SEL, CLK_USBH1_SUSPEND_SEL), + regmap_reg_range(CLK_ETH_TSU_CTRL, CLK_ETH_TSU_DIV_INT), + regmap_reg_range(CLK_ETH_TSU_SEL, CLK_ETH_TSU_SEL), + regmap_reg_range(CLK_ADC_CTRL, CLK_ADC_DIV_INT), + regmap_reg_range(CLK_ADC_SEL, CLK_ADC_SEL), + regmap_reg_range(CLK_SDIO_TIMER_CTRL, CLK_SDIO_TIMER_DIV_INT), + regmap_reg_range(CLK_SDIO_TIMER_SEL, CLK_SDIO_TIMER_SEL), + regmap_reg_range(CLK_SDIO_ALT_SRC_CTRL, CLK_SDIO_ALT_SRC_DIV_INT), + regmap_reg_range(CLK_SDIO_ALT_SRC_SEL, CLK_SDIO_ALT_SRC_SEL), + regmap_reg_range(CLK_GP0_CTRL, CLK_GP0_SEL), + regmap_reg_range(CLK_GP1_CTRL, CLK_GP1_SEL), + regmap_reg_range(CLK_GP2_CTRL, CLK_GP2_SEL), + regmap_reg_range(CLK_GP3_CTRL, CLK_GP3_SEL), + regmap_reg_range(CLK_GP4_CTRL, CLK_GP4_SEL), + regmap_reg_range(CLK_GP5_CTRL, CLK_GP5_SEL), + regmap_reg_range(CLK_SYS_RESUS_CTRL, CLK_SYS_RESUS_CTRL), + regmap_reg_range(CLK_SLOW_SYS_RESUS_CTRL, CLK_SLOW_SYS_RESUS_CTRL), + regmap_reg_range(FC0_REF_KHZ, FC0_RESULT), + regmap_reg_range(VIDEO_CLK_VEC_CTRL, VIDEO_CLK_VEC_DIV_INT), + regmap_reg_range(VIDEO_CLK_VEC_SEL, VIDEO_CLK_DPI_DIV_INT), + regmap_reg_range(VIDEO_CLK_DPI_SEL, VIDEO_CLK_MIPI1_DPI_SEL), +}; + +static const struct regmap_access_table rp1_reg_table = { + .yes_ranges = rp1_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(rp1_reg_ranges), +}; + +static const struct regmap_config rp1_clk_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = PLL_VIDEO_SEC, + .name = "rp1-clk", + .rd_table = &rp1_reg_table, + .disable_locking = true, +}; + +static int rp1_clk_probe(struct platform_device *pdev) +{ + const size_t asize = ARRAY_SIZE(clk_desc_array); + struct rp1_clk_desc *desc; + struct device *dev = &pdev->dev; + struct rp1_clockman *clockman; + struct clk_hw **hws; + unsigned int i; + + clockman = devm_kzalloc(dev, struct_size(clockman, onecell.hws, asize), + GFP_KERNEL); + if (!clockman) + return -ENOMEM; + + spin_lock_init(&clockman->regs_lock); + clockman->dev = dev; + + clockman->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(clockman->regs)) + return PTR_ERR(clockman->regs); + + clockman->regmap = devm_regmap_init_mmio(dev, clockman->regs, + &rp1_clk_regmap_cfg); + if (IS_ERR(clockman->regmap)) { + dev_err_probe(dev, PTR_ERR(clockman->regmap), + "could not init clock regmap\n"); + return PTR_ERR(clockman->regmap); + } + + clockman->onecell.num = asize; + hws = clockman->onecell.hws; + + for (i = 0; i < asize; i++) { + desc = clk_desc_array[i]; + if (desc && desc->clk_register && desc->data) + hws[i] = desc->clk_register(clockman, desc); + } + + platform_set_drvdata(pdev, clockman); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + &clockman->onecell); +} + +static const struct of_device_id rp1_clk_of_match[] = { + { .compatible = "raspberrypi,rp1-clocks" }, + {} +}; +MODULE_DEVICE_TABLE(of, rp1_clk_of_match); + +static struct platform_driver rp1_clk_driver = { + .driver = { + .name = "rp1-clk", + .of_match_table = rp1_clk_of_match, + }, + .probe = rp1_clk_probe, +}; + +module_platform_driver(rp1_clk_driver); + +MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>"); +MODULE_AUTHOR("Andrea della Porta <andrea.porta@suse.com>"); +MODULE_DESCRIPTION("RP1 clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index 1adef0389475..24e59ddf85e7 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -323,6 +323,31 @@ static struct attribute *scmi_device_attributes_attrs[] = { }; ATTRIBUTE_GROUPS(scmi_device_attributes); +static int scmi_pm_suspend(struct device *dev) +{ + const struct device_driver *drv = dev->driver; + + if (drv && drv->pm && drv->pm->suspend) + return drv->pm->suspend(dev); + + return 0; +} + +static int scmi_pm_resume(struct device *dev) +{ + const struct device_driver *drv = dev->driver; + + if (drv && drv->pm && drv->pm->resume) + return drv->pm->resume(dev); + + return 0; +} + +static const struct dev_pm_ops scmi_dev_pm_ops = { + .suspend = pm_sleep_ptr(scmi_pm_suspend), + .resume = pm_sleep_ptr(scmi_pm_resume), +}; + const struct bus_type scmi_bus_type = { .name = "scmi_protocol", .match = scmi_dev_match, @@ -330,6 +355,7 @@ const struct bus_type scmi_bus_type = { .remove = scmi_dev_remove, .uevent = scmi_device_uevent, .dev_groups = scmi_device_attributes_groups, + .pm = &scmi_dev_pm_ops, }; EXPORT_SYMBOL_GPL(scmi_bus_type); diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index dab758c5fdea..07b9e629276d 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -305,6 +305,7 @@ enum debug_counters { ERR_MSG_INVALID, ERR_MSG_NOMEM, ERR_PROTOCOL, + XFERS_INFLIGHT, SCMI_DEBUG_COUNTERS_LAST }; @@ -314,6 +315,12 @@ static inline void scmi_inc_count(atomic_t *arr, int stat) atomic_inc(&arr[stat]); } +static inline void scmi_dec_count(atomic_t *arr, int stat) +{ + if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_COUNTERS)) + atomic_dec(&arr[stat]); +} + enum scmi_bad_msg { MSG_UNEXPECTED = -1, MSG_INVALID = -2, @@ -498,4 +505,5 @@ static struct platform_driver __drv = { \ void scmi_notification_instance_data_set(const struct scmi_handle *handle, void *priv); void *scmi_notification_instance_data_get(const struct scmi_handle *handle); +int scmi_inflight_count(const struct scmi_handle *handle); #endif /* _SCMI_COMMON_H */ diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 395fe9289035..bd56a877fdfc 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -190,6 +190,7 @@ struct scmi_info { }; #define handle_to_scmi_info(h) container_of(h, struct scmi_info, handle) +#define tx_minfo_to_scmi_info(h) container_of(h, struct scmi_info, tx_minfo) #define bus_nb_to_scmi_info(nb) container_of(nb, struct scmi_info, bus_nb) #define req_nb_to_scmi_info(nb) container_of(nb, struct scmi_info, dev_req_nb) @@ -603,9 +604,14 @@ static inline void scmi_xfer_inflight_register_unlocked(struct scmi_xfer *xfer, struct scmi_xfers_info *minfo) { + /* In this context minfo will be tx_minfo due to the xfer pending */ + struct scmi_info *info = tx_minfo_to_scmi_info(minfo); + /* Set in-flight */ set_bit(xfer->hdr.seq, minfo->xfer_alloc_table); hash_add(minfo->pending_xfers, &xfer->node, xfer->hdr.seq); + scmi_inc_count(info->dbg->counters, XFERS_INFLIGHT); + xfer->pending = true; } @@ -807,9 +813,13 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer) spin_lock_irqsave(&minfo->xfer_lock, flags); if (refcount_dec_and_test(&xfer->users)) { if (xfer->pending) { + struct scmi_info *info = tx_minfo_to_scmi_info(minfo); + scmi_xfer_token_clear(minfo, xfer); hash_del(&xfer->node); xfer->pending = false; + + scmi_dec_count(info->dbg->counters, XFERS_INFLIGHT); } hlist_add_head(&xfer->node, &minfo->free_xfers); } @@ -1433,7 +1443,8 @@ static int do_xfer(const struct scmi_protocol_handle *ph, trace_scmi_xfer_begin(xfer->transfer_id, xfer->hdr.id, xfer->hdr.protocol_id, xfer->hdr.seq, - xfer->hdr.poll_completion); + xfer->hdr.poll_completion, + scmi_inflight_count(&info->handle)); /* Clear any stale status */ xfer->hdr.status = SCMI_SUCCESS; @@ -1469,7 +1480,8 @@ static int do_xfer(const struct scmi_protocol_handle *ph, info->desc->ops->mark_txdone(cinfo, ret, xfer); trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id, - xfer->hdr.protocol_id, xfer->hdr.seq, ret); + xfer->hdr.protocol_id, xfer->hdr.seq, ret, + scmi_inflight_count(&info->handle)); return ret; } @@ -2912,6 +2924,7 @@ static const char * const dbg_counter_strs[] = { "err_msg_invalid", "err_msg_nomem", "err_protocol", + "xfers_inflight", }; static ssize_t reset_all_on_write(struct file *filp, const char __user *buf, @@ -3405,6 +3418,17 @@ static struct dentry *scmi_debugfs_init(void) return d; } +int scmi_inflight_count(const struct scmi_handle *handle) +{ + if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_COUNTERS)) { + struct scmi_info *info = handle_to_scmi_info(handle); + + return atomic_read(&info->dbg->counters[XFERS_INFLIGHT]); + } else { + return 0; + } +} + static int __init scmi_driver_init(void) { scmi_quirks_initialize(); diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c index e160ecb22948..dee9f238f6fd 100644 --- a/drivers/firmware/arm_scmi/notify.c +++ b/drivers/firmware/arm_scmi/notify.c @@ -318,6 +318,9 @@ struct scmi_registered_events_desc { * customized event report * @num_sources: The number of possible sources for this event as stated at * events' registration time + * @not_supported_by_platform: A flag to indicate that not even one source was + * found to be supported by the platform for this + * event * @sources: A reference to a dynamically allocated array used to refcount the * events' enable requests for all the existing sources * @sources_mtx: A mutex to serialize the access to @sources @@ -334,6 +337,7 @@ struct scmi_registered_event { const struct scmi_event *evt; void *report; u32 num_sources; + bool not_supported_by_platform; refcount_t *sources; /* locking to serialize the access to sources */ struct mutex sources_mtx; @@ -811,10 +815,19 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id, if (!r_evt->report) return -ENOMEM; - for (id = 0; id < r_evt->num_sources; id++) - if (ee->ops->is_notify_supported && - !ee->ops->is_notify_supported(ph, r_evt->evt->id, id)) - refcount_set(&r_evt->sources[id], NOTIF_UNSUPP); + if (ee->ops->is_notify_supported) { + int supported = 0; + + for (id = 0; id < r_evt->num_sources; id++) { + if (!ee->ops->is_notify_supported(ph, r_evt->evt->id, id)) + refcount_set(&r_evt->sources[id], NOTIF_UNSUPP); + else + supported++; + } + + /* Not even one source has been found to be supported */ + r_evt->not_supported_by_platform = !supported; + } pd->registered_events[i] = r_evt; /* Ensure events are updated */ @@ -936,6 +949,11 @@ static inline int scmi_bind_event_handler(struct scmi_notify_instance *ni, * of protocol instance. */ hash_del(&hndl->hash); + + /* Bailout if event is not supported at all */ + if (r_evt->not_supported_by_platform) + return -EOPNOTSUPP; + /* * Acquire protocols only for NON pending handlers, so as NOT to trigger * protocol initialization when a notifier is registered against a still @@ -1060,6 +1078,9 @@ __scmi_event_handler_get_ops(struct scmi_notify_instance *ni, r_evt = SCMI_GET_REVT(ni, KEY_XTRACT_PROTO_ID(evt_key), KEY_XTRACT_EVT_ID(evt_key)); + if (r_evt && r_evt->not_supported_by_platform) + return ERR_PTR(-EOPNOTSUPP); + mutex_lock(&ni->pending_mtx); /* Search registered events at first ... if possible at all */ if (r_evt) { @@ -1087,7 +1108,7 @@ __scmi_event_handler_get_ops(struct scmi_notify_instance *ni, hndl->key); /* this hndl can be only a pending one */ scmi_put_handler_unlocked(ni, hndl); - hndl = NULL; + hndl = ERR_PTR(-EINVAL); } } mutex_unlock(&ni->pending_mtx); @@ -1370,8 +1391,8 @@ static int scmi_notifier_register(const struct scmi_handle *handle, evt_key = MAKE_HASH_KEY(proto_id, evt_id, src_id ? *src_id : SRC_ID_MASK); hndl = scmi_get_or_create_handler(ni, evt_key); - if (!hndl) - return -EINVAL; + if (IS_ERR(hndl)) + return PTR_ERR(hndl); blocking_notifier_chain_register(&hndl->chain, nb); @@ -1416,8 +1437,8 @@ static int scmi_notifier_unregister(const struct scmi_handle *handle, evt_key = MAKE_HASH_KEY(proto_id, evt_id, src_id ? *src_id : SRC_ID_MASK); hndl = scmi_get_handler(ni, evt_key); - if (!hndl) - return -EINVAL; + if (IS_ERR(hndl)) + return PTR_ERR(hndl); /* * Note that this chain unregistration call is safe on its own diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index c7e5a34b254b..683fd9b85c5c 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -892,7 +892,7 @@ static int scmi_dvfs_device_opps_add(const struct scmi_protocol_handle *ph, freq = dom->opp[idx].indicative_freq * dom->mult_factor; /* All OPPs above the sustained frequency are treated as turbo */ - data.turbo = freq > dom->sustained_freq_khz * 1000; + data.turbo = freq > dom->sustained_freq_khz * 1000UL; data.level = dom->opp[idx].perf; data.freq = freq; diff --git a/drivers/firmware/arm_scmi/raw_mode.c b/drivers/firmware/arm_scmi/raw_mode.c index 3d543b1d8947..73db5492ab44 100644 --- a/drivers/firmware/arm_scmi/raw_mode.c +++ b/drivers/firmware/arm_scmi/raw_mode.c @@ -475,7 +475,8 @@ static void scmi_xfer_raw_worker(struct work_struct *work) raw->desc->ops->mark_txdone(rw->cinfo, ret, xfer); trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id, - xfer->hdr.protocol_id, xfer->hdr.seq, ret); + xfer->hdr.protocol_id, xfer->hdr.seq, + ret, scmi_inflight_count(raw->handle)); /* Wait also for an async delayed response if needed */ if (!ret && xfer->async_done) { @@ -642,7 +643,8 @@ static int scmi_do_xfer_raw_start(struct scmi_raw_mode_info *raw, trace_scmi_xfer_begin(xfer->transfer_id, xfer->hdr.id, xfer->hdr.protocol_id, xfer->hdr.seq, - xfer->hdr.poll_completion); + xfer->hdr.poll_completion, + scmi_inflight_count(raw->handle)); ret = raw->desc->ops->send_message(rw->cinfo, xfer); if (ret) { diff --git a/drivers/firmware/arm_scmi/scmi_power_control.c b/drivers/firmware/arm_scmi/scmi_power_control.c index 21f467a92942..955736336061 100644 --- a/drivers/firmware/arm_scmi/scmi_power_control.c +++ b/drivers/firmware/arm_scmi/scmi_power_control.c @@ -46,6 +46,7 @@ #include <linux/math.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/pm.h> #include <linux/printk.h> #include <linux/reboot.h> #include <linux/scmi_protocol.h> @@ -324,12 +325,7 @@ static int scmi_userspace_notifier(struct notifier_block *nb, static void scmi_suspend_work_func(struct work_struct *work) { - struct scmi_syspower_conf *sc = - container_of(work, struct scmi_syspower_conf, suspend_work); - pm_suspend(PM_SUSPEND_MEM); - - sc->state = SCMI_SYSPOWER_IDLE; } static int scmi_syspower_probe(struct scmi_device *sdev) @@ -354,6 +350,7 @@ static int scmi_syspower_probe(struct scmi_device *sdev) sc->required_transition = SCMI_SYSTEM_MAX; sc->userspace_nb.notifier_call = &scmi_userspace_notifier; sc->dev = &sdev->dev; + dev_set_drvdata(&sdev->dev, sc); INIT_WORK(&sc->suspend_work, scmi_suspend_work_func); @@ -363,6 +360,18 @@ static int scmi_syspower_probe(struct scmi_device *sdev) NULL, &sc->userspace_nb); } +static int scmi_system_power_resume(struct device *dev) +{ + struct scmi_syspower_conf *sc = dev_get_drvdata(dev); + + sc->state = SCMI_SYSPOWER_IDLE; + return 0; +} + +static const struct dev_pm_ops scmi_system_power_pmops = { + SYSTEM_SLEEP_PM_OPS(NULL, scmi_system_power_resume) +}; + static const struct scmi_device_id scmi_id_table[] = { { SCMI_PROTOCOL_SYSTEM, "syspower" }, { }, @@ -370,6 +379,9 @@ static const struct scmi_device_id scmi_id_table[] = { MODULE_DEVICE_TABLE(scmi, scmi_id_table); static struct scmi_driver scmi_system_power_driver = { + .driver = { + .pm = pm_sleep_ptr(&scmi_system_power_pmops), + }, .name = "scmi-system-power", .probe = scmi_syspower_probe, .id_table = scmi_id_table, diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index f63b716be5b0..26cd0458aacd 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -1603,7 +1603,13 @@ bool qcom_scm_lmh_dcvsh_available(void) } EXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh_available); -int qcom_scm_shm_bridge_enable(void) +/* + * This is only supposed to be called once by the TZMem module. It takes the + * SCM struct device as argument and uses it to pass the call as at the time + * the SHM Bridge is enabled, the SCM is not yet fully set up and doesn't + * accept global user calls. Don't try to use the __scm pointer here. + */ +int qcom_scm_shm_bridge_enable(struct device *scm_dev) { int ret; @@ -1615,11 +1621,11 @@ int qcom_scm_shm_bridge_enable(void) struct qcom_scm_res res; - if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_MP, + if (!__qcom_scm_is_call_available(scm_dev, QCOM_SCM_SVC_MP, QCOM_SCM_MP_SHM_BRIDGE_ENABLE)) return -EOPNOTSUPP; - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(scm_dev, &desc, &res); if (ret) return ret; @@ -1631,7 +1637,7 @@ int qcom_scm_shm_bridge_enable(void) } EXPORT_SYMBOL_GPL(qcom_scm_shm_bridge_enable); -int qcom_scm_shm_bridge_create(struct device *dev, u64 pfn_and_ns_perm_flags, +int qcom_scm_shm_bridge_create(u64 pfn_and_ns_perm_flags, u64 ipfn_and_s_perm_flags, u64 size_and_flags, u64 ns_vmids, u64 *handle) { @@ -1659,7 +1665,7 @@ int qcom_scm_shm_bridge_create(struct device *dev, u64 pfn_and_ns_perm_flags, } EXPORT_SYMBOL_GPL(qcom_scm_shm_bridge_create); -int qcom_scm_shm_bridge_delete(struct device *dev, u64 handle) +int qcom_scm_shm_bridge_delete(u64 handle) { struct qcom_scm_desc desc = { .svc = QCOM_SCM_SVC_MP, @@ -2250,24 +2256,47 @@ static int qcom_scm_probe(struct platform_device *pdev) if (ret) return ret; - /* Paired with smp_load_acquire() in qcom_scm_is_available(). */ - smp_store_release(&__scm, scm); + ret = of_reserved_mem_device_init(scm->dev); + if (ret && ret != -ENODEV) + return dev_err_probe(scm->dev, ret, + "Failed to setup the reserved memory region for TZ mem\n"); + + ret = qcom_tzmem_enable(scm->dev); + if (ret) + return dev_err_probe(scm->dev, ret, + "Failed to enable the TrustZone memory allocator\n"); + + memset(&pool_config, 0, sizeof(pool_config)); + pool_config.initial_size = 0; + pool_config.policy = QCOM_TZMEM_POLICY_ON_DEMAND; + pool_config.max_size = SZ_256K; + + scm->mempool = devm_qcom_tzmem_pool_new(scm->dev, &pool_config); + if (IS_ERR(scm->mempool)) + return dev_err_probe(scm->dev, PTR_ERR(scm->mempool), + "Failed to create the SCM memory pool\n"); irq = platform_get_irq_optional(pdev, 0); if (irq < 0) { - if (irq != -ENXIO) { - ret = irq; - goto err; - } + if (irq != -ENXIO) + return irq; } else { - ret = devm_request_threaded_irq(__scm->dev, irq, NULL, qcom_scm_irq_handler, - IRQF_ONESHOT, "qcom-scm", __scm); - if (ret < 0) { - dev_err_probe(scm->dev, ret, "Failed to request qcom-scm irq\n"); - goto err; - } + ret = devm_request_threaded_irq(scm->dev, irq, NULL, qcom_scm_irq_handler, + IRQF_ONESHOT, "qcom-scm", scm); + if (ret < 0) + return dev_err_probe(scm->dev, ret, + "Failed to request qcom-scm irq\n"); } + /* + * Paired with smp_load_acquire() in qcom_scm_is_available(). + * + * This marks the SCM API as ready to accept user calls and can only + * be called after the TrustZone memory pool is initialized and the + * waitqueue interrupt requested. + */ + smp_store_release(&__scm, scm); + __get_convention(); /* @@ -2283,32 +2312,6 @@ static int qcom_scm_probe(struct platform_device *pdev) if (of_property_read_bool(pdev->dev.of_node, "qcom,sdi-enabled") || !download_mode) qcom_scm_disable_sdi(); - ret = of_reserved_mem_device_init(__scm->dev); - if (ret && ret != -ENODEV) { - dev_err_probe(__scm->dev, ret, - "Failed to setup the reserved memory region for TZ mem\n"); - goto err; - } - - ret = qcom_tzmem_enable(__scm->dev); - if (ret) { - dev_err_probe(__scm->dev, ret, - "Failed to enable the TrustZone memory allocator\n"); - goto err; - } - - memset(&pool_config, 0, sizeof(pool_config)); - pool_config.initial_size = 0; - pool_config.policy = QCOM_TZMEM_POLICY_ON_DEMAND; - pool_config.max_size = SZ_256K; - - __scm->mempool = devm_qcom_tzmem_pool_new(__scm->dev, &pool_config); - if (IS_ERR(__scm->mempool)) { - ret = dev_err_probe(__scm->dev, PTR_ERR(__scm->mempool), - "Failed to create the SCM memory pool\n"); - goto err; - } - /* * Initialize the QSEECOM interface. * @@ -2323,12 +2326,6 @@ static int qcom_scm_probe(struct platform_device *pdev) WARN(ret < 0, "failed to initialize qseecom: %d\n", ret); return 0; - -err: - /* Paired with smp_load_acquire() in qcom_scm_is_available(). */ - smp_store_release(&__scm, NULL); - - return ret; } static void qcom_scm_shutdown(struct platform_device *pdev) diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h index 3133d826f5fa..0e8dd838099e 100644 --- a/drivers/firmware/qcom/qcom_scm.h +++ b/drivers/firmware/qcom/qcom_scm.h @@ -83,6 +83,7 @@ int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, struct qcom_scm_res *res); struct qcom_tzmem_pool *qcom_scm_get_tzmem_pool(void); +int qcom_scm_shm_bridge_enable(struct device *scm_dev); #define QCOM_SCM_SVC_BOOT 0x01 #define QCOM_SCM_BOOT_SET_ADDR 0x01 diff --git a/drivers/firmware/qcom/qcom_tzmem.c b/drivers/firmware/qcom/qcom_tzmem.c index 94196ad87105..ea0a35355657 100644 --- a/drivers/firmware/qcom/qcom_tzmem.c +++ b/drivers/firmware/qcom/qcom_tzmem.c @@ -20,6 +20,7 @@ #include <linux/spinlock.h> #include <linux/types.h> +#include "qcom_scm.h" #include "qcom_tzmem.h" struct qcom_tzmem_area { @@ -94,7 +95,7 @@ static int qcom_tzmem_init(void) goto notsupp; } - ret = qcom_scm_shm_bridge_enable(); + ret = qcom_scm_shm_bridge_enable(qcom_tzmem_dev); if (ret == -EOPNOTSUPP) goto notsupp; @@ -124,9 +125,9 @@ static int qcom_tzmem_init_area(struct qcom_tzmem_area *area) if (!handle) return -ENOMEM; - ret = qcom_scm_shm_bridge_create(qcom_tzmem_dev, pfn_and_ns_perm, - ipfn_and_s_perm, size_and_flags, - QCOM_SCM_VMID_HLOS, handle); + ret = qcom_scm_shm_bridge_create(pfn_and_ns_perm, ipfn_and_s_perm, + size_and_flags, QCOM_SCM_VMID_HLOS, + handle); if (ret) return ret; @@ -142,7 +143,7 @@ static void qcom_tzmem_cleanup_area(struct qcom_tzmem_area *area) if (!qcom_tzmem_using_shm_bridge) return; - qcom_scm_shm_bridge_delete(qcom_tzmem_dev, *handle); + qcom_scm_shm_bridge_delete(*handle); kfree(handle); } diff --git a/drivers/firmware/smccc/smccc.c b/drivers/firmware/smccc/smccc.c index cd65b434dc6e..bdee057db2fd 100644 --- a/drivers/firmware/smccc/smccc.c +++ b/drivers/firmware/smccc/smccc.c @@ -72,10 +72,7 @@ bool arm_smccc_hypervisor_has_uuid(const uuid_t *hyp_uuid) struct arm_smccc_res res = {}; uuid_t uuid; - if (arm_smccc_1_1_get_conduit() != SMCCC_CONDUIT_HVC) - return false; - - arm_smccc_1_1_hvc(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, &res); + arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, &res); if (res.a0 == SMCCC_RET_NOT_SUPPORTED) return false; diff --git a/drivers/firmware/tegra/Kconfig b/drivers/firmware/tegra/Kconfig index cde1ab8bd9d1..91f2320c0d0f 100644 --- a/drivers/firmware/tegra/Kconfig +++ b/drivers/firmware/tegra/Kconfig @@ -2,7 +2,7 @@ menu "Tegra firmware driver" config TEGRA_IVC - bool "Tegra IVC protocol" + bool "Tegra IVC protocol" if COMPILE_TEST depends on ARCH_TEGRA help IVC (Inter-VM Communication) protocol is part of the IPC @@ -13,8 +13,9 @@ config TEGRA_IVC config TEGRA_BPMP bool "Tegra BPMP driver" - depends on ARCH_TEGRA && TEGRA_HSP_MBOX && TEGRA_IVC + depends on ARCH_TEGRA && TEGRA_HSP_MBOX depends on !CPU_BIG_ENDIAN + select TEGRA_IVC help BPMP (Boot and Power Management Processor) is designed to off-loading the PM functions which include clock/DVFS/thermal/power from the CPU. diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile index 620cf3fdd607..41e2e4dc31d6 100644 --- a/drivers/firmware/tegra/Makefile +++ b/drivers/firmware/tegra/Makefile @@ -4,6 +4,7 @@ tegra-bpmp-$(CONFIG_ARCH_TEGRA_210_SOC) += bpmp-tegra210.o tegra-bpmp-$(CONFIG_ARCH_TEGRA_186_SOC) += bpmp-tegra186.o tegra-bpmp-$(CONFIG_ARCH_TEGRA_194_SOC) += bpmp-tegra186.o tegra-bpmp-$(CONFIG_ARCH_TEGRA_234_SOC) += bpmp-tegra186.o +tegra-bpmp-$(CONFIG_ARCH_TEGRA_264_SOC) += bpmp-tegra186.o tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o obj-$(CONFIG_TEGRA_IVC) += ivc.o diff --git a/drivers/firmware/tegra/bpmp-private.h b/drivers/firmware/tegra/bpmp-private.h index 182bfe396516..07c3d46abb87 100644 --- a/drivers/firmware/tegra/bpmp-private.h +++ b/drivers/firmware/tegra/bpmp-private.h @@ -23,13 +23,7 @@ struct tegra_bpmp_ops { int (*resume)(struct tegra_bpmp *bpmp); }; -#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ - IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \ - IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) extern const struct tegra_bpmp_ops tegra186_bpmp_ops; -#endif -#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) extern const struct tegra_bpmp_ops tegra210_bpmp_ops; -#endif #endif diff --git a/drivers/firmware/tegra/bpmp-tegra186.c b/drivers/firmware/tegra/bpmp-tegra186.c index 6f0d0511b486..7cfc5fdfa49d 100644 --- a/drivers/firmware/tegra/bpmp-tegra186.c +++ b/drivers/firmware/tegra/bpmp-tegra186.c @@ -6,7 +6,7 @@ #include <linux/genalloc.h> #include <linux/io.h> #include <linux/mailbox_client.h> -#include <linux/of_address.h> +#include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <soc/tegra/bpmp.h> @@ -192,16 +192,11 @@ static void tegra186_bpmp_teardown_channels(struct tegra_bpmp *bpmp) static int tegra186_bpmp_dram_init(struct tegra_bpmp *bpmp) { struct tegra186_bpmp *priv = bpmp->priv; - struct device_node *np; struct resource res; size_t size; int err; - np = of_parse_phandle(bpmp->dev->of_node, "memory-region", 0); - if (!np) - return -ENODEV; - - err = of_address_to_resource(np, 0, &res); + err = of_reserved_mem_region_to_resource(bpmp->dev->of_node, 0, &res); if (err < 0) { dev_warn(bpmp->dev, "failed to parse memory region: %d\n", err); return err; diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index c3a1dc344961..e74bba7ccc44 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -836,7 +836,8 @@ static const struct dev_pm_ops tegra_bpmp_pm_ops = { #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \ - IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) + IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) || \ + IS_ENABLED(CONFIG_ARCH_TEGRA_264_SOC) static const struct tegra_bpmp_soc tegra186_soc = { .channels = { .cpu_tx = { @@ -884,7 +885,8 @@ static const struct tegra_bpmp_soc tegra210_soc = { static const struct of_device_id tegra_bpmp_match[] = { #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \ - IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) + IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) || \ + IS_ENABLED(CONFIG_ARCH_TEGRA_264_SOC) { .compatible = "nvidia,tegra186-bpmp", .data = &tegra186_soc }, #endif #if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) diff --git a/drivers/memory/brcmstb_memc.c b/drivers/memory/brcmstb_memc.c index c87b37e2c1f0..ba73470b1b13 100644 --- a/drivers/memory/brcmstb_memc.c +++ b/drivers/memory/brcmstb_memc.c @@ -184,62 +184,10 @@ static const struct of_device_id brcmstb_memc_of_match[] = { .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.1", .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] }, - { - .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.2", - .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] - }, - { - .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.3", - .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] - }, - { - .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.5", - .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] - }, - { - .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.6", - .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] - }, - { - .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.7", - .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] - }, - { - .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.8", - .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] - }, - { - .compatible = "brcm,brcmstb-memc-ddr-rev-b.3.0", - .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] - }, - { - .compatible = "brcm,brcmstb-memc-ddr-rev-b.3.1", - .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] - }, - { - .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.0", - .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] - }, - { - .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.1", - .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] - }, - { - .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.2", - .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] - }, - { - .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.3", - .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] - }, - { - .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.4", - .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] - }, - /* default to the original offset */ + /* default to the V21 offset */ { .compatible = "brcm,brcmstb-memc-ddr", - .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V1X] + .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] }, {} }; diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c index 2e1ecae9e959..2fadad0666b1 100644 --- a/drivers/memory/emif.c +++ b/drivers/memory/emif.c @@ -39,6 +39,7 @@ * are two devices attached to this EMIF, this * value is the maximum of the two temperature * levels. + * @lpmode: Chosen low power mode * @node: node in the device list * @base: base address of memory-mapped IO registers. * @dev: device pointer. diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c index c086c22511f7..733e22f695ab 100644 --- a/drivers/memory/mtk-smi.c +++ b/drivers/memory/mtk-smi.c @@ -320,6 +320,38 @@ static const u8 mtk_smi_larb_mt6893_ostd[][SMI_LARB_PORT_NR_MAX] = { [20] = {0x9, 0x9, 0x5, 0x5, 0x1, 0x1}, }; +static const u8 mtk_smi_larb_mt8186_ostd[][SMI_LARB_PORT_NR_MAX] = { + [0] = {0x2, 0x1, 0x8, 0x1,}, + [1] = {0x1, 0x3, 0x1, 0x1,}, + [2] = {0x6, 0x1, 0x4, 0x1,}, + [3] = {}, + [4] = {0xf, 0x1, 0x5, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1,}, + [5] = {}, + [6] = {}, + [7] = {0x1, 0x3, 0x1, 0x1, 0x1, 0x3, 0x2, 0xd, 0x7, 0x5, 0x3, + 0x1, 0x5,}, + [8] = {0x1, 0x2, 0x2,}, + [9] = {0x9, 0x7, 0xf, 0x8, 0x1, 0x8, 0x9, 0x3, 0x3, 0xb, 0x7, 0x4, + 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1,}, + [10] = {}, + [11] = {0x9, 0x7, 0xf, 0x8, 0x1, 0x8, 0x9, 0x3, 0x3, 0xb, 0x7, 0x4, + 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x8, 0x7, 0x7, 0x1, 0x6, 0x2, + 0xf, 0x8, 0x1, 0x1, 0x1,}, + [12] = {}, + [13] = {0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x6, 0x6, 0x6, 0x1, 0x1, 0x1,}, + [14] = {0x1, 0x1, 0x1, 0x1, 0x1, 0x1,}, + [15] = {}, + [16] = {0x28, 0x14, 0x2, 0xc, 0x18, 0x1, 0x14, 0x1, 0x4, 0x4, 0x4, + 0x2, 0x4, 0x2, 0x8, 0x4, 0x4,}, + [17] = {0x28, 0x14, 0x2, 0xc, 0x18, 0x1, 0x14, 0x1, 0x4, 0x4, 0x4, + 0x2, 0x4, 0x2, 0x8, 0x4, 0x4,}, + [18] = {}, + [19] = {0x1, 0x1, 0x1, 0x1,}, + [20] = {0x2, 0x2, 0x2, 0x2, 0x1, 0x1,}, +}; + static const u8 mtk_smi_larb_mt8188_ostd[][SMI_LARB_PORT_NR_MAX] = { [0] = {0x02, 0x18, 0x22, 0x22, 0x01, 0x02, 0x0a,}, [1] = {0x12, 0x02, 0x14, 0x14, 0x01, 0x18, 0x0a,}, @@ -491,6 +523,7 @@ static const struct mtk_smi_larb_gen mtk_smi_larb_mt8183 = { static const struct mtk_smi_larb_gen mtk_smi_larb_mt8186 = { .config_port = mtk_smi_larb_config_port_gen2_general, .flags_general = MTK_SMI_FLAG_SLEEP_CTL, + .ostd = mtk_smi_larb_mt8186_ostd, }; static const struct mtk_smi_larb_gen mtk_smi_larb_mt8188 = { diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 9c96eed00194..d9e13c1f9b13 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -1455,8 +1455,8 @@ static int gpmc_setup_irq(struct gpmc_device *gpmc) gpmc->irq_chip.irq_unmask = gpmc_irq_unmask; gpmc->irq_chip.irq_set_type = gpmc_irq_set_type; - gpmc_irq_domain = irq_domain_create_linear(of_fwnode_handle(gpmc->dev->of_node), - gpmc->nirqs, &gpmc_irq_domain_ops, gpmc); + gpmc_irq_domain = irq_domain_create_linear(dev_fwnode(gpmc->dev), gpmc->nirqs, + &gpmc_irq_domain_ops, gpmc); if (!gpmc_irq_domain) { dev_err(gpmc->dev, "IRQ domain add failed\n"); return -ENODEV; diff --git a/drivers/memory/stm32_omm.c b/drivers/memory/stm32_omm.c index 79ceb1635698..bee2ecc8c2b9 100644 --- a/drivers/memory/stm32_omm.c +++ b/drivers/memory/stm32_omm.c @@ -46,7 +46,7 @@ static int stm32_omm_set_amcr(struct device *dev, bool set) struct regmap *syscfg_regmap; struct device_node *node; struct resource res, res1; - u32 amcr_base, amcr_mask; + unsigned int syscon_args[2]; int ret, idx; unsigned int i, amcr, read_amcr; @@ -98,29 +98,20 @@ static int stm32_omm_set_amcr(struct device *dev, bool set) of_node_put(node); } - syscfg_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "st,syscfg-amcr"); + syscfg_regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node, "st,syscfg-amcr", + 2, syscon_args); if (IS_ERR(syscfg_regmap)) return dev_err_probe(dev, PTR_ERR(syscfg_regmap), "Failed to get st,syscfg-amcr property\n"); - ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 1, - &amcr_base); - if (ret) - return ret; - - ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 2, - &amcr_mask); - if (ret) - return ret; - amcr = mm_ospi2_size / SZ_64M; if (set) - regmap_update_bits(syscfg_regmap, amcr_base, amcr_mask, amcr); + regmap_update_bits(syscfg_regmap, syscon_args[0], syscon_args[1], amcr); /* read AMCR and check coherency with memory-map areas defined in DT */ - regmap_read(syscfg_regmap, amcr_base, &read_amcr); - read_amcr = read_amcr >> (ffs(amcr_mask) - 1); + regmap_read(syscfg_regmap, syscon_args[0], &read_amcr); + read_amcr = read_amcr >> (ffs(syscon_args[1]) - 1); if (amcr != read_amcr) { dev_err(dev, "AMCR value not coherent with DT memory-map areas\n"); diff --git a/drivers/memory/tegra/Makefile b/drivers/memory/tegra/Makefile index 0750847dac3c..6334601e6120 100644 --- a/drivers/memory/tegra/Makefile +++ b/drivers/memory/tegra/Makefile @@ -10,6 +10,7 @@ tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o tegra-mc-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o tegra-mc-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186.o tegra194.o tegra-mc-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra186.o tegra234.o +tegra-mc-$(CONFIG_ARCH_TEGRA_264_SOC) += tegra186.o tegra264.o obj-$(CONFIG_TEGRA_MC) += tegra-mc.o @@ -21,5 +22,6 @@ obj-$(CONFIG_TEGRA210_EMC) += tegra210-emc.o obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186-emc.o obj-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186-emc.o obj-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra186-emc.o +obj-$(CONFIG_ARCH_TEGRA_264_SOC) += tegra186-emc.o tegra210-emc-y := tegra210-emc-core.o tegra210-emc-cc-r21021.o diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index bd5b58f1fd42..6edb210287dc 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2014-2025 NVIDIA CORPORATION. All rights reserved. */ #include <linux/clk.h> @@ -49,6 +49,9 @@ static const struct of_device_id tegra_mc_of_match[] = { #ifdef CONFIG_ARCH_TEGRA_234_SOC { .compatible = "nvidia,tegra234-mc", .data = &tegra234_mc_soc }, #endif +#ifdef CONFIG_ARCH_TEGRA_264_SOC + { .compatible = "nvidia,tegra264-mc", .data = &tegra264_mc_soc }, +#endif { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, tegra_mc_of_match); diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h index c3f6655bec60..1d97cf4d3a94 100644 --- a/drivers/memory/tegra/mc.h +++ b/drivers/memory/tegra/mc.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2014-2025 NVIDIA CORPORATION. All rights reserved. */ #ifndef MEMORY_TEGRA_MC_H @@ -182,6 +182,10 @@ extern const struct tegra_mc_soc tegra194_mc_soc; extern const struct tegra_mc_soc tegra234_mc_soc; #endif +#ifdef CONFIG_ARCH_TEGRA_264_SOC +extern const struct tegra_mc_soc tegra264_mc_soc; +#endif + #if defined(CONFIG_ARCH_TEGRA_3x_SOC) || \ defined(CONFIG_ARCH_TEGRA_114_SOC) || \ defined(CONFIG_ARCH_TEGRA_124_SOC) || \ @@ -193,7 +197,8 @@ extern const struct tegra_mc_ops tegra30_mc_ops; #if defined(CONFIG_ARCH_TEGRA_186_SOC) || \ defined(CONFIG_ARCH_TEGRA_194_SOC) || \ - defined(CONFIG_ARCH_TEGRA_234_SOC) + defined(CONFIG_ARCH_TEGRA_234_SOC) || \ + defined(CONFIG_ARCH_TEGRA_264_SOC) extern const struct tegra_mc_ops tegra186_mc_ops; #endif diff --git a/drivers/memory/tegra/tegra186-emc.c b/drivers/memory/tegra/tegra186-emc.c index bc807d7fcd4e..d6cd90c7ad53 100644 --- a/drivers/memory/tegra/tegra186-emc.c +++ b/drivers/memory/tegra/tegra186-emc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2019 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2019-2025 NVIDIA CORPORATION. All rights reserved. */ #include <linux/clk.h> @@ -394,6 +394,9 @@ static const struct of_device_id tegra186_emc_of_match[] = { #if defined(CONFIG_ARCH_TEGRA_234_SOC) { .compatible = "nvidia,tegra234-emc" }, #endif +#if defined(CONFIG_ARCH_TEGRA_264_SOC) + { .compatible = "nvidia,tegra264-emc" }, +#endif { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, tegra186_emc_of_match); diff --git a/drivers/memory/tegra/tegra186.c b/drivers/memory/tegra/tegra186.c index 1b3183951bfe..aee11457bf8e 100644 --- a/drivers/memory/tegra/tegra186.c +++ b/drivers/memory/tegra/tegra186.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2017-2021 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2017-2025 NVIDIA CORPORATION. All rights reserved. */ #include <linux/io.h> @@ -26,11 +26,24 @@ static int tegra186_mc_probe(struct tegra_mc *mc) { struct platform_device *pdev = to_platform_device(mc->dev); + struct resource *res; unsigned int i; char name[8]; int err; - mc->bcast_ch_regs = devm_platform_ioremap_resource_byname(pdev, "broadcast"); + /* + * From Tegra264, the SID region is not present in MC node and BROADCAST is first. + * The common function 'tegra_mc_probe()' already maps first region entry from DT. + * Check if the SID region is present in DT then map BROADCAST. Otherwise, consider + * the first entry mapped in mc probe as the BROADCAST region. This is done to avoid + * mapping the region twice when SID is not present and keep backward compatibility. + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sid"); + if (res) + mc->bcast_ch_regs = devm_platform_ioremap_resource_byname(pdev, "broadcast"); + else + mc->bcast_ch_regs = mc->regs; + if (IS_ERR(mc->bcast_ch_regs)) { if (PTR_ERR(mc->bcast_ch_regs) == -EINVAL) { dev_warn(&pdev->dev, diff --git a/drivers/memory/tegra/tegra264-bwmgr.h b/drivers/memory/tegra/tegra264-bwmgr.h new file mode 100644 index 000000000000..93bfceaac9c8 --- /dev/null +++ b/drivers/memory/tegra/tegra264-bwmgr.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2025 NVIDIA CORPORATION. All rights reserved. */ + +#ifndef MEMORY_TEGRA_TEGRA264_BWMGR_H +#define MEMORY_TEGRA_TEGRA264_BWMGR_H + +#define TEGRA264_BWMGR_ICC_PRIMARY 1 +#define TEGRA264_BWMGR_DEBUG 2 +#define TEGRA264_BWMGR_CPU_CLUSTER0 3 +#define TEGRA264_BWMGR_CPU_CLUSTER1 4 +#define TEGRA264_BWMGR_CPU_CLUSTER2 5 +#define TEGRA264_BWMGR_CPU_CLUSTER3 6 +#define TEGRA264_BWMGR_CPU_CLUSTER4 7 +#define TEGRA264_BWMGR_CPU_CLUSTER5 8 +#define TEGRA264_BWMGR_CPU_CLUSTER6 9 +#define TEGRA264_BWMGR_CACTMON 10 +#define TEGRA264_BWMGR_DISPLAY 11 +#define TEGRA264_BWMGR_VI 12 +#define TEGRA264_BWMGR_APE 13 +#define TEGRA264_BWMGR_VIFAL 14 +#define TEGRA264_BWMGR_GPU 15 +#define TEGRA264_BWMGR_EQOS 16 +#define TEGRA264_BWMGR_PCIE_0 17 +#define TEGRA264_BWMGR_PCIE_1 18 +#define TEGRA264_BWMGR_PCIE_2 19 +#define TEGRA264_BWMGR_PCIE_3 20 +#define TEGRA264_BWMGR_PCIE_4 21 +#define TEGRA264_BWMGR_PCIE_5 22 +#define TEGRA264_BWMGR_SDMMC_1 23 +#define TEGRA264_BWMGR_SDMMC_2 24 +#define TEGRA264_BWMGR_NVDEC 25 +#define TEGRA264_BWMGR_NVENC 26 +#define TEGRA264_BWMGR_NVJPG_0 27 +#define TEGRA264_BWMGR_NVJPG_1 28 +#define TEGRA264_BWMGR_OFAA 29 +#define TEGRA264_BWMGR_XUSB_HOST 30 +#define TEGRA264_BWMGR_XUSB_DEV 31 +#define TEGRA264_BWMGR_TSEC 32 +#define TEGRA264_BWMGR_VIC 33 +#define TEGRA264_BWMGR_APEDMA 34 +#define TEGRA264_BWMGR_SE 35 +#define TEGRA264_BWMGR_ISP 36 +#define TEGRA264_BWMGR_HDA 37 +#define TEGRA264_BWMGR_VI2FAL 38 +#define TEGRA264_BWMGR_VI2 39 +#define TEGRA264_BWMGR_RCE 40 +#define TEGRA264_BWMGR_PVA 41 +#define TEGRA264_BWMGR_NVPMODEL 42 + +#endif diff --git a/drivers/memory/tegra/tegra264.c b/drivers/memory/tegra/tegra264.c new file mode 100644 index 000000000000..5203e6c11372 --- /dev/null +++ b/drivers/memory/tegra/tegra264.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025, NVIDIA CORPORATION. All rights reserved. + */ + +#include <dt-bindings/memory/nvidia,tegra264.h> + +#include <linux/interconnect.h> +#include <linux/of_device.h> +#include <linux/tegra-icc.h> + +#include <soc/tegra/bpmp.h> +#include <soc/tegra/mc.h> + +#include "mc.h" +#include "tegra264-bwmgr.h" + +/* + * MC Client entries are sorted in the increasing order of the + * override and security register offsets. + */ +static const struct tegra_mc_client tegra264_mc_clients[] = { + { + .id = TEGRA264_MEMORY_CLIENT_HDAR, + .name = "hdar", + .bpmp_id = TEGRA264_BWMGR_HDA, + .type = TEGRA_ICC_ISO_AUDIO, + }, { + .id = TEGRA264_MEMORY_CLIENT_HDAW, + .name = "hdaw", + .bpmp_id = TEGRA264_BWMGR_HDA, + .type = TEGRA_ICC_ISO_AUDIO, + }, { + .id = TEGRA264_MEMORY_CLIENT_MGBE0R, + .name = "mgbe0r", + .bpmp_id = TEGRA264_BWMGR_EQOS, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_MGBE0W, + .name = "mgbe0w", + .bpmp_id = TEGRA264_BWMGR_EQOS, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_MGBE1R, + .name = "mgbe1r", + .bpmp_id = TEGRA264_BWMGR_EQOS, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_MGBE1W, + .name = "mgbe1w", + .bpmp_id = TEGRA264_BWMGR_EQOS, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_SDMMC0R, + .name = "sdmmc0r", + .bpmp_id = TEGRA264_BWMGR_SDMMC_1, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_SDMMC0W, + .name = "sdmmc0w", + .bpmp_id = TEGRA264_BWMGR_SDMMC_1, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_VICR, + .name = "vicr", + .bpmp_id = TEGRA264_BWMGR_VIC, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_VICW, + .name = "vicw", + .bpmp_id = TEGRA264_BWMGR_VIC, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_APER, + .name = "aper", + .bpmp_id = TEGRA264_BWMGR_APE, + .type = TEGRA_ICC_ISO_AUDIO, + }, { + .id = TEGRA264_MEMORY_CLIENT_APEW, + .name = "apew", + .bpmp_id = TEGRA264_BWMGR_APE, + .type = TEGRA_ICC_ISO_AUDIO, + }, { + .id = TEGRA264_MEMORY_CLIENT_APEDMAR, + .name = "apedmar", + .bpmp_id = TEGRA264_BWMGR_APEDMA, + .type = TEGRA_ICC_ISO_AUDIO, + }, { + .id = TEGRA264_MEMORY_CLIENT_APEDMAW, + .name = "apedmaw", + .bpmp_id = TEGRA264_BWMGR_APEDMA, + .type = TEGRA_ICC_ISO_AUDIO, + }, { + .id = TEGRA264_MEMORY_CLIENT_VIFALCONR, + .name = "vifalconr", + .bpmp_id = TEGRA264_BWMGR_VIFAL, + .type = TEGRA_ICC_ISO_VIFAL, + }, { + .id = TEGRA264_MEMORY_CLIENT_VIFALCONW, + .name = "vifalconw", + .bpmp_id = TEGRA264_BWMGR_VIFAL, + .type = TEGRA_ICC_ISO_VIFAL, + }, { + .id = TEGRA264_MEMORY_CLIENT_RCER, + .name = "rcer", + .bpmp_id = TEGRA264_BWMGR_RCE, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_RCEW, + .name = "rcew", + .bpmp_id = TEGRA264_BWMGR_RCE, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_PCIE0W, + .name = "pcie0w", + .bpmp_id = TEGRA264_BWMGR_PCIE_0, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_PCIE1R, + .name = "pcie1r", + .bpmp_id = TEGRA264_BWMGR_PCIE_1, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_PCIE1W, + .name = "pcie1w", + .bpmp_id = TEGRA264_BWMGR_PCIE_1, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_PCIE2AR, + .name = "pcie2ar", + .bpmp_id = TEGRA264_BWMGR_PCIE_2, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_PCIE2AW, + .name = "pcie2aw", + .bpmp_id = TEGRA264_BWMGR_PCIE_2, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_PCIE3R, + .name = "pcie3r", + .bpmp_id = TEGRA264_BWMGR_PCIE_3, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_PCIE3W, + .name = "pcie3w", + .bpmp_id = TEGRA264_BWMGR_PCIE_3, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_PCIE4R, + .name = "pcie4r", + .bpmp_id = TEGRA264_BWMGR_PCIE_4, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_PCIE4W, + .name = "pcie4w", + .bpmp_id = TEGRA264_BWMGR_PCIE_4, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_PCIE5R, + .name = "pcie5r", + .bpmp_id = TEGRA264_BWMGR_PCIE_5, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_PCIE5W, + .name = "pcie5w", + .bpmp_id = TEGRA264_BWMGR_PCIE_5, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_GPUR02MC, + .name = "gpur02mc", + .bpmp_id = TEGRA264_BWMGR_GPU, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_GPUW02MC, + .name = "gpuw02mc", + .bpmp_id = TEGRA264_BWMGR_GPU, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_NVDECSRD2MC, + .name = "nvdecsrd2mc", + .bpmp_id = TEGRA264_BWMGR_NVDEC, + .type = TEGRA_ICC_NISO, + }, { + .id = TEGRA264_MEMORY_CLIENT_NVDECSWR2MC, + .name = "nvdecswr2mc", + .bpmp_id = TEGRA264_BWMGR_NVDEC, + .type = TEGRA_ICC_NISO, + }, +}; + +/* + * tegra264_mc_icc_set() - Pass MC client info to the BPMP-FW + * @src: ICC node for Memory Controller's (MC) Client + * @dst: ICC node for Memory Controller (MC) + * + * Passing the current request info from the MC to the BPMP-FW where + * LA and PTSA registers are accessed and the final EMC freq is set + * based on client_id, type, latency and bandwidth. + * icc_set_bw() makes set_bw calls for both MC and EMC providers in + * sequence. Both the calls are protected by 'mutex_lock(&icc_lock)'. + * So, the data passed won't be updated by concurrent set calls from + * other clients. + */ +static int tegra264_mc_icc_set(struct icc_node *src, struct icc_node *dst) +{ + struct tegra_mc *mc = icc_provider_to_tegra_mc(dst->provider); + struct mrq_bwmgr_int_request bwmgr_req = { 0 }; + struct mrq_bwmgr_int_response bwmgr_resp = { 0 }; + const struct tegra_mc_client *pclient = src->data; + struct tegra_bpmp_message msg; + int ret; + + /* + * Same Src and Dst node will happen during boot from icc_node_add(). + * This can be used to pre-initialize and set bandwidth for all clients + * before their drivers are loaded. We are skipping this case as for us, + * the pre-initialization already happened in Bootloader(MB2) and BPMP-FW. + */ + if (src->id == dst->id) + return 0; + + if (!mc->bwmgr_mrq_supported) + return 0; + + if (!mc->bpmp) { + dev_err(mc->dev, "BPMP reference NULL\n"); + return -ENOENT; + } + + if (pclient->type == TEGRA_ICC_NISO) + bwmgr_req.bwmgr_calc_set_req.niso_bw = src->avg_bw; + else + bwmgr_req.bwmgr_calc_set_req.iso_bw = src->avg_bw; + + bwmgr_req.bwmgr_calc_set_req.client_id = pclient->bpmp_id; + + bwmgr_req.cmd = CMD_BWMGR_INT_CALC_AND_SET; + bwmgr_req.bwmgr_calc_set_req.mc_floor = src->peak_bw; + bwmgr_req.bwmgr_calc_set_req.floor_unit = BWMGR_INT_UNIT_KBPS; + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_BWMGR_INT; + msg.tx.data = &bwmgr_req; + msg.tx.size = sizeof(bwmgr_req); + msg.rx.data = &bwmgr_resp; + msg.rx.size = sizeof(bwmgr_resp); + + ret = tegra_bpmp_transfer(mc->bpmp, &msg); + if (ret < 0) { + dev_err(mc->dev, "BPMP transfer failed: %d\n", ret); + goto error; + } + if (msg.rx.ret < 0) { + pr_err("failed to set bandwidth for %u: %d\n", + bwmgr_req.bwmgr_calc_set_req.client_id, msg.rx.ret); + ret = -EINVAL; + } + +error: + return ret; +} + +static int tegra264_mc_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, + u32 peak_bw, u32 *agg_avg, u32 *agg_peak) +{ + struct icc_provider *p = node->provider; + struct tegra_mc *mc = icc_provider_to_tegra_mc(p); + + if (!mc->bwmgr_mrq_supported) + return 0; + + *agg_avg += avg_bw; + *agg_peak = max(*agg_peak, peak_bw); + + return 0; +} + +static int tegra264_mc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *peak) +{ + *avg = 0; + *peak = 0; + + return 0; +} + +static const struct tegra_mc_icc_ops tegra264_mc_icc_ops = { + .xlate = tegra_mc_icc_xlate, + .aggregate = tegra264_mc_icc_aggregate, + .get_bw = tegra264_mc_icc_get_init_bw, + .set = tegra264_mc_icc_set, +}; + +const struct tegra_mc_soc tegra264_mc_soc = { + .num_clients = ARRAY_SIZE(tegra264_mc_clients), + .clients = tegra264_mc_clients, + .num_address_bits = 40, + .num_channels = 16, + .client_id_mask = 0x1ff, + .intmask = MC_INT_DECERR_ROUTE_SANITY | + MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS | + MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | + MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, + .has_addr_hi_reg = true, + .ops = &tegra186_mc_ops, + .icc_ops = &tegra264_mc_icc_ops, + .ch_intmask = 0x0000ff00, + .global_intstatus_channel_shift = 8, + /* + * Additionally, there are lite carveouts but those are not currently + * supported. + */ + .num_carveouts = 32, +}; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index b9ca56930003..b9c11f67315f 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -660,4 +660,5 @@ source "drivers/misc/pvpanic/Kconfig" source "drivers/misc/mchp_pci1xxxx/Kconfig" source "drivers/misc/keba/Kconfig" source "drivers/misc/amd-sbi/Kconfig" +source "drivers/misc/rp1/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 917b9a7183aa..e2e66f5f4fb8 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -75,3 +75,4 @@ lan966x-pci-objs += lan966x_pci.dtbo.o obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o obj-y += keba/ obj-y += amd-sbi/ +obj-$(CONFIG_MISC_RP1) += rp1/ diff --git a/drivers/misc/rp1/Kconfig b/drivers/misc/rp1/Kconfig new file mode 100644 index 000000000000..5232e70d3079 --- /dev/null +++ b/drivers/misc/rp1/Kconfig @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# RaspberryPi RP1 misc device +# + +config MISC_RP1 + tristate "RaspberryPi RP1 misc device" + depends on OF_IRQ && OF_OVERLAY && PCI_MSI && PCI_QUIRKS + select PCI_DYNAMIC_OF_NODES + help + Support the RP1 peripheral chip found on Raspberry Pi 5 board. + + This device supports several sub-devices including e.g. Ethernet + controller, USB controller, I2C, SPI and UART. + + The driver is responsible for enabling the DT node once the PCIe + endpoint has been configured, and handling interrupts. + + This driver uses an overlay to load other drivers to support for + RP1 internal sub-devices. diff --git a/drivers/misc/rp1/Makefile b/drivers/misc/rp1/Makefile new file mode 100644 index 000000000000..508b4cb05627 --- /dev/null +++ b/drivers/misc/rp1/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_MISC_RP1) += rp1-pci.o +rp1-pci-objs := rp1_pci.o rp1-pci.dtbo.o diff --git a/drivers/misc/rp1/rp1-pci.dtso b/drivers/misc/rp1/rp1-pci.dtso new file mode 100644 index 000000000000..eea826b36e02 --- /dev/null +++ b/drivers/misc/rp1/rp1-pci.dtso @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) + +/* + * The dts overlay is included from the dts directory so + * it can be possible to check it with CHECK_DTBS while + * also compile it from the driver source directory. + */ + +/dts-v1/; +/plugin/; + +/ { + fragment@0 { + target-path=""; + __overlay__ { + compatible = "pci1de4,1"; + #address-cells = <3>; + #size-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + + #include "arm64/broadcom/rp1-common.dtsi" + }; + }; +}; diff --git a/drivers/misc/rp1/rp1_pci.c b/drivers/misc/rp1/rp1_pci.c new file mode 100644 index 000000000000..803832006ec8 --- /dev/null +++ b/drivers/misc/rp1/rp1_pci.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018-2025 Raspberry Pi Ltd. + * + * All rights reserved. + */ + +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/module.h> +#include <linux/msi.h> +#include <linux/of_platform.h> +#include <linux/pci.h> +#include <linux/platform_device.h> + +#define RP1_HW_IRQ_MASK GENMASK(5, 0) + +#define REG_SET 0x800 +#define REG_CLR 0xc00 + +/* MSI-X CFG registers start at 0x8 */ +#define MSIX_CFG(x) (0x8 + (4 * (x))) + +#define MSIX_CFG_IACK_EN BIT(3) +#define MSIX_CFG_IACK BIT(2) +#define MSIX_CFG_ENABLE BIT(0) + +/* Address map */ +#define RP1_PCIE_APBS_BASE 0x108000 + +/* Interrupts */ +#define RP1_INT_END 61 + +/* Embedded dtbo symbols created by cmd_wrap_S_dtb in scripts/Makefile.lib */ +extern char __dtbo_rp1_pci_begin[]; +extern char __dtbo_rp1_pci_end[]; + +struct rp1_dev { + struct pci_dev *pdev; + struct irq_domain *domain; + struct irq_data *pcie_irqds[64]; + void __iomem *bar1; + int ovcs_id; /* overlay changeset id */ + bool level_triggered_irq[RP1_INT_END]; +}; + +static void msix_cfg_set(struct rp1_dev *rp1, unsigned int hwirq, u32 value) +{ + iowrite32(value, rp1->bar1 + RP1_PCIE_APBS_BASE + REG_SET + MSIX_CFG(hwirq)); +} + +static void msix_cfg_clr(struct rp1_dev *rp1, unsigned int hwirq, u32 value) +{ + iowrite32(value, rp1->bar1 + RP1_PCIE_APBS_BASE + REG_CLR + MSIX_CFG(hwirq)); +} + +static void rp1_mask_irq(struct irq_data *irqd) +{ + struct rp1_dev *rp1 = irqd->domain->host_data; + struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq]; + + pci_msi_mask_irq(pcie_irqd); +} + +static void rp1_unmask_irq(struct irq_data *irqd) +{ + struct rp1_dev *rp1 = irqd->domain->host_data; + struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq]; + + pci_msi_unmask_irq(pcie_irqd); +} + +static int rp1_irq_set_type(struct irq_data *irqd, unsigned int type) +{ + struct rp1_dev *rp1 = irqd->domain->host_data; + unsigned int hwirq = (unsigned int)irqd->hwirq; + + switch (type) { + case IRQ_TYPE_LEVEL_HIGH: + dev_dbg(&rp1->pdev->dev, "MSIX IACK EN for IRQ %u\n", hwirq); + msix_cfg_set(rp1, hwirq, MSIX_CFG_IACK_EN); + rp1->level_triggered_irq[hwirq] = true; + break; + case IRQ_TYPE_EDGE_RISING: + msix_cfg_clr(rp1, hwirq, MSIX_CFG_IACK_EN); + rp1->level_triggered_irq[hwirq] = false; + break; + default: + return -EINVAL; + } + + return 0; +} + +static struct irq_chip rp1_irq_chip = { + .name = "rp1_irq_chip", + .irq_mask = rp1_mask_irq, + .irq_unmask = rp1_unmask_irq, + .irq_set_type = rp1_irq_set_type, +}; + +static void rp1_chained_handle_irq(struct irq_desc *desc) +{ + unsigned int hwirq = desc->irq_data.hwirq & RP1_HW_IRQ_MASK; + struct rp1_dev *rp1 = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned int virq; + + chained_irq_enter(chip, desc); + + virq = irq_find_mapping(rp1->domain, hwirq); + generic_handle_irq(virq); + if (rp1->level_triggered_irq[hwirq]) + msix_cfg_set(rp1, hwirq, MSIX_CFG_IACK); + + chained_irq_exit(chip, desc); +} + +static int rp1_irq_xlate(struct irq_domain *d, struct device_node *node, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, unsigned int *out_type) +{ + struct rp1_dev *rp1 = d->host_data; + struct irq_data *pcie_irqd; + unsigned long hwirq; + int pcie_irq; + int ret; + + ret = irq_domain_xlate_twocell(d, node, intspec, intsize, + &hwirq, out_type); + if (ret) + return ret; + + pcie_irq = pci_irq_vector(rp1->pdev, hwirq); + pcie_irqd = irq_get_irq_data(pcie_irq); + rp1->pcie_irqds[hwirq] = pcie_irqd; + *out_hwirq = hwirq; + + return 0; +} + +static int rp1_irq_activate(struct irq_domain *d, struct irq_data *irqd, + bool reserve) +{ + struct rp1_dev *rp1 = d->host_data; + + msix_cfg_set(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE); + + return 0; +} + +static void rp1_irq_deactivate(struct irq_domain *d, struct irq_data *irqd) +{ + struct rp1_dev *rp1 = d->host_data; + + msix_cfg_clr(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE); +} + +static const struct irq_domain_ops rp1_domain_ops = { + .xlate = rp1_irq_xlate, + .activate = rp1_irq_activate, + .deactivate = rp1_irq_deactivate, +}; + +static void rp1_unregister_interrupts(struct pci_dev *pdev) +{ + struct rp1_dev *rp1 = pci_get_drvdata(pdev); + int irq, i; + + if (rp1->domain) { + for (i = 0; i < RP1_INT_END; i++) { + irq = irq_find_mapping(rp1->domain, i); + irq_dispose_mapping(irq); + } + + irq_domain_remove(rp1->domain); + } + + pci_free_irq_vectors(pdev); +} + +static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + u32 dtbo_size = __dtbo_rp1_pci_end - __dtbo_rp1_pci_begin; + void *dtbo_start = __dtbo_rp1_pci_begin; + struct device *dev = &pdev->dev; + struct device_node *rp1_node; + bool skip_ovl = true; + struct rp1_dev *rp1; + int err = 0; + int i; + + /* + * Either use rp1_nexus node if already present in DT, or + * set a flag to load it from overlay at runtime + */ + rp1_node = of_find_node_by_name(NULL, "rp1_nexus"); + if (!rp1_node) { + rp1_node = dev_of_node(dev); + skip_ovl = false; + } + + if (!rp1_node) { + dev_err(dev, "Missing of_node for device\n"); + err = -EINVAL; + goto err_put_node; + } + + rp1 = devm_kzalloc(&pdev->dev, sizeof(*rp1), GFP_KERNEL); + if (!rp1) { + err = -ENOMEM; + goto err_put_node; + } + + rp1->pdev = pdev; + + if (pci_resource_len(pdev, 1) <= 0x10000) { + dev_err(&pdev->dev, + "Not initialized - is the firmware running?\n"); + err = -EINVAL; + goto err_put_node; + } + + err = pcim_enable_device(pdev); + if (err < 0) { + err = dev_err_probe(&pdev->dev, err, + "Enabling PCI device has failed"); + goto err_put_node; + } + + rp1->bar1 = pcim_iomap(pdev, 1, 0); + if (!rp1->bar1) { + dev_err(&pdev->dev, "Cannot map PCI BAR\n"); + err = -EIO; + goto err_put_node; + } + + pci_set_master(pdev); + + err = pci_alloc_irq_vectors(pdev, RP1_INT_END, RP1_INT_END, + PCI_IRQ_MSIX); + if (err < 0) { + err = dev_err_probe(&pdev->dev, err, + "Failed to allocate MSI-X vectors\n"); + goto err_put_node; + } else if (err != RP1_INT_END) { + dev_err(&pdev->dev, "Cannot allocate enough interrupts\n"); + err = -EINVAL; + goto err_put_node; + } + + pci_set_drvdata(pdev, rp1); + rp1->domain = irq_domain_add_linear(rp1_node, RP1_INT_END, + &rp1_domain_ops, rp1); + if (!rp1->domain) { + dev_err(&pdev->dev, "Error creating IRQ domain\n"); + err = -ENOMEM; + goto err_unregister_interrupts; + } + + for (i = 0; i < RP1_INT_END; i++) { + unsigned int irq = irq_create_mapping(rp1->domain, i); + + if (!irq) { + dev_err(&pdev->dev, "Failed to create IRQ mapping\n"); + err = -EINVAL; + goto err_unregister_interrupts; + } + + irq_set_chip_and_handler(irq, &rp1_irq_chip, handle_level_irq); + irq_set_probe(irq); + irq_set_chained_handler_and_data(pci_irq_vector(pdev, i), + rp1_chained_handle_irq, rp1); + } + + if (!skip_ovl) { + err = of_overlay_fdt_apply(dtbo_start, dtbo_size, &rp1->ovcs_id, + rp1_node); + if (err) + goto err_unregister_interrupts; + } + + err = of_platform_default_populate(rp1_node, NULL, dev); + if (err) { + dev_err_probe(&pdev->dev, err, "Error populating devicetree\n"); + goto err_unload_overlay; + } + + return 0; + +err_unload_overlay: + of_overlay_remove(&rp1->ovcs_id); +err_unregister_interrupts: + rp1_unregister_interrupts(pdev); +err_put_node: + if (skip_ovl) + of_node_put(rp1_node); + + return err; +} + +static void rp1_remove(struct pci_dev *pdev) +{ + struct rp1_dev *rp1 = pci_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + of_platform_depopulate(dev); + of_overlay_remove(&rp1->ovcs_id); + rp1_unregister_interrupts(pdev); +} + +static const struct pci_device_id dev_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RPI_RP1_C0), }, + { } +}; +MODULE_DEVICE_TABLE(pci, dev_id_table); + +static struct pci_driver rp1_driver = { + .name = KBUILD_MODNAME, + .id_table = dev_id_table, + .probe = rp1_probe, + .remove = rp1_remove, +}; + +module_pci_driver(rp1_driver); + +MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>"); +MODULE_AUTHOR("Andrea della Porta <andrea.porta@suse.com>"); +MODULE_DESCRIPTION("RaspberryPi RP1 misc device"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index d7f4ee634263..cf483d82572c 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -6303,6 +6303,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_XILINX, 0x5020, of_pci_make_dev_node); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_XILINX, 0x5021, of_pci_make_dev_node); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_REDHAT, 0x0005, of_pci_make_dev_node); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_EFAR, 0x9660, of_pci_make_dev_node); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RPI_RP1_C0, of_pci_make_dev_node); /* * Devices known to require a longer delay before first config space access diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 33db9104df17..eb1b37af81fb 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -624,6 +624,17 @@ config PINCTRL_MLXBF3 each pin. This driver can also be built as a module called pinctrl-mlxbf3. +config PINCTRL_RP1 + tristate "Pinctrl driver for RP1" + depends on MISC_RP1 + default MISC_RP1 + select PINMUX + select PINCONF + select GENERIC_PINCONF + help + Enable the gpio and pinctrl/mux driver for RaspberryPi RP1 + multi function device. + source "drivers/pinctrl/actions/Kconfig" source "drivers/pinctrl/aspeed/Kconfig" source "drivers/pinctrl/bcm/Kconfig" diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index ac27e88677d1..65dac8e38798 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_PINCTRL_PIC32) += pinctrl-pic32.o obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o +obj-$(CONFIG_PINCTRL_RP1) += pinctrl-rp1.o obj-$(CONFIG_PINCTRL_SCMI) += pinctrl-scmi.o obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o diff --git a/drivers/pinctrl/pinctrl-rp1.c b/drivers/pinctrl/pinctrl-rp1.c new file mode 100644 index 000000000000..6080b57a5d87 --- /dev/null +++ b/drivers/pinctrl/pinctrl-rp1.c @@ -0,0 +1,1831 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Raspberry Pi RP1 GPIO unit + * + * Copyright (C) 2023 Raspberry Pi Ltd. + * + * This driver is inspired by: + * pinctrl-bcm2835.c, please see original file for copyright information + */ + +#include <linux/gpio/driver.h> +#include <linux/of_irq.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/regmap.h> + +#include "pinmux.h" +#include "pinconf.h" +#include "pinctrl-utils.h" + +#define MODULE_NAME "pinctrl-rp1" +#define RP1_NUM_GPIOS 54 +#define RP1_NUM_BANKS 3 + +#define RP1_INT_EDGE_FALLING BIT(0) +#define RP1_INT_EDGE_RISING BIT(1) +#define RP1_INT_LEVEL_LOW BIT(2) +#define RP1_INT_LEVEL_HIGH BIT(3) +#define RP1_INT_MASK GENMASK(3, 0) +#define RP1_INT_EDGE_BOTH (RP1_INT_EDGE_FALLING | \ + RP1_INT_EDGE_RISING) + +#define RP1_FSEL_COUNT 9 + +#define RP1_FSEL_ALT0 0x00 +#define RP1_FSEL_GPIO 0x05 +#define RP1_FSEL_NONE 0x09 +#define RP1_FSEL_NONE_HW 0x1f + +#define RP1_PAD_DRIVE_2MA 0x0 +#define RP1_PAD_DRIVE_4MA 0x1 +#define RP1_PAD_DRIVE_8MA 0x2 +#define RP1_PAD_DRIVE_12MA 0x3 + +enum { + RP1_PUD_OFF = 0, + RP1_PUD_DOWN = 1, + RP1_PUD_UP = 2, +}; + +enum { + RP1_DIR_OUTPUT = 0, + RP1_DIR_INPUT = 1, +}; + +enum { + RP1_OUTOVER_PERI = 0, + RP1_OUTOVER_INVPERI = 1, + RP1_OUTOVER_LOW = 2, + RP1_OUTOVER_HIGH = 3, +}; + +enum { + RP1_OEOVER_PERI = 0, + RP1_OEOVER_INVPERI = 1, + RP1_OEOVER_DISABLE = 2, + RP1_OEOVER_ENABLE = 3, +}; + +enum { + RP1_INOVER_PERI = 0, + RP1_INOVER_INVPERI = 1, + RP1_INOVER_LOW = 2, + RP1_INOVER_HIGH = 3, +}; + +enum { + RP1_GPIO_CTRL_IRQRESET_SET = 0, + RP1_GPIO_CTRL_INT_CLR = 1, + RP1_GPIO_CTRL_INT_SET = 2, + RP1_GPIO_CTRL_OEOVER = 3, + RP1_GPIO_CTRL_FUNCSEL = 4, + RP1_GPIO_CTRL_OUTOVER = 5, + RP1_GPIO_CTRL = 6, +}; + +enum { + RP1_INTE_SET = 0, + RP1_INTE_CLR = 1, +}; + +enum { + RP1_RIO_OUT_SET = 0, + RP1_RIO_OUT_CLR = 1, + RP1_RIO_OE = 2, + RP1_RIO_OE_SET = 3, + RP1_RIO_OE_CLR = 4, + RP1_RIO_IN = 5, +}; + +enum { + RP1_PAD_SLEWFAST = 0, + RP1_PAD_SCHMITT = 1, + RP1_PAD_PULL = 2, + RP1_PAD_DRIVE = 3, + RP1_PAD_IN_ENABLE = 4, + RP1_PAD_OUT_DISABLE = 5, +}; + +static const struct reg_field rp1_gpio_fields[] = { + [RP1_GPIO_CTRL_IRQRESET_SET] = REG_FIELD(0x2004, 28, 28), + [RP1_GPIO_CTRL_INT_CLR] = REG_FIELD(0x3004, 20, 23), + [RP1_GPIO_CTRL_INT_SET] = REG_FIELD(0x2004, 20, 23), + [RP1_GPIO_CTRL_OEOVER] = REG_FIELD(0x0004, 14, 15), + [RP1_GPIO_CTRL_FUNCSEL] = REG_FIELD(0x0004, 0, 4), + [RP1_GPIO_CTRL_OUTOVER] = REG_FIELD(0x0004, 12, 13), + [RP1_GPIO_CTRL] = REG_FIELD(0x0004, 0, 31), +}; + +static const struct reg_field rp1_inte_fields[] = { + [RP1_INTE_SET] = REG_FIELD(0x2000, 0, 0), + [RP1_INTE_CLR] = REG_FIELD(0x3000, 0, 0), +}; + +static const struct reg_field rp1_rio_fields[] = { + [RP1_RIO_OUT_SET] = REG_FIELD(0x2000, 0, 0), + [RP1_RIO_OUT_CLR] = REG_FIELD(0x3000, 0, 0), + [RP1_RIO_OE] = REG_FIELD(0x0004, 0, 0), + [RP1_RIO_OE_SET] = REG_FIELD(0x2004, 0, 0), + [RP1_RIO_OE_CLR] = REG_FIELD(0x3004, 0, 0), + [RP1_RIO_IN] = REG_FIELD(0x0008, 0, 0), +}; + +static const struct reg_field rp1_pad_fields[] = { + [RP1_PAD_SLEWFAST] = REG_FIELD(0, 0, 0), + [RP1_PAD_SCHMITT] = REG_FIELD(0, 1, 1), + [RP1_PAD_PULL] = REG_FIELD(0, 2, 3), + [RP1_PAD_DRIVE] = REG_FIELD(0, 4, 5), + [RP1_PAD_IN_ENABLE] = REG_FIELD(0, 6, 6), + [RP1_PAD_OUT_DISABLE] = REG_FIELD(0, 7, 7), +}; + +#define FUNC(f) \ + [func_##f] = #f +#define RP1_MAX_FSEL 8 +#define PIN(i, f0, f1, f2, f3, f4, f5, f6, f7, f8) \ + [i] = { \ + .funcs = { \ + func_##f0, \ + func_##f1, \ + func_##f2, \ + func_##f3, \ + func_##f4, \ + func_##f5, \ + func_##f6, \ + func_##f7, \ + func_##f8, \ + }, \ + } + +#define LEGACY_MAP(n, f0, f1, f2, f3, f4, f5) \ + [n] = { \ + func_gpio, \ + func_gpio, \ + func_##f5, \ + func_##f4, \ + func_##f0, \ + func_##f1, \ + func_##f2, \ + func_##f3, \ + } + +enum funcs { + func_alt0, + func_alt1, + func_alt2, + func_alt3, + func_alt4, + func_gpio, + func_alt6, + func_alt7, + func_alt8, + func_none, + func_aaud, + func_dpi, + func_dsi0_te_ext, + func_dsi1_te_ext, + func_gpclk0, + func_gpclk1, + func_gpclk2, + func_gpclk3, + func_gpclk4, + func_gpclk5, + func_i2c0, + func_i2c1, + func_i2c2, + func_i2c3, + func_i2c4, + func_i2c5, + func_i2c6, + func_i2s0, + func_i2s1, + func_i2s2, + func_ir, + func_mic, + func_pcie_clkreq_n, + func_pio, + func_proc_rio, + func_pwm0, + func_pwm1, + func_sd0, + func_sd1, + func_spi0, + func_spi1, + func_spi2, + func_spi3, + func_spi4, + func_spi5, + func_spi6, + func_spi7, + func_spi8, + func_uart0, + func_uart1, + func_uart2, + func_uart3, + func_uart4, + func_uart5, + func_vbus0, + func_vbus1, + func_vbus2, + func_vbus3, + func__, + func_count = func__, + func_invalid = func__, +}; + +struct rp1_pin_funcs { + u8 funcs[RP1_FSEL_COUNT]; +}; + +struct rp1_iobank_desc { + int min_gpio; + int num_gpios; + int gpio_offset; + int inte_offset; + int ints_offset; + int rio_offset; + int pads_offset; +}; + +struct rp1_pin_info { + u8 num; + u8 bank; + u8 offset; + u8 fsel; + u8 irq_type; + + struct regmap_field *gpio[ARRAY_SIZE(rp1_gpio_fields)]; + struct regmap_field *rio[ARRAY_SIZE(rp1_rio_fields)]; + struct regmap_field *inte[ARRAY_SIZE(rp1_inte_fields)]; + struct regmap_field *pad[ARRAY_SIZE(rp1_pad_fields)]; +}; + +struct rp1_pinctrl { + struct device *dev; + void __iomem *gpio_base; + void __iomem *rio_base; + void __iomem *pads_base; + int irq[RP1_NUM_BANKS]; + struct rp1_pin_info pins[RP1_NUM_GPIOS]; + + struct pinctrl_dev *pctl_dev; + struct gpio_chip gpio_chip; + struct pinctrl_gpio_range gpio_range; + + raw_spinlock_t irq_lock[RP1_NUM_BANKS]; +}; + +/* pins are just named GPIO0..GPIO53 */ +#define RP1_GPIO_PIN(a) PINCTRL_PIN(a, "gpio" #a) +static struct pinctrl_pin_desc rp1_gpio_pins[] = { + RP1_GPIO_PIN(0), + RP1_GPIO_PIN(1), + RP1_GPIO_PIN(2), + RP1_GPIO_PIN(3), + RP1_GPIO_PIN(4), + RP1_GPIO_PIN(5), + RP1_GPIO_PIN(6), + RP1_GPIO_PIN(7), + RP1_GPIO_PIN(8), + RP1_GPIO_PIN(9), + RP1_GPIO_PIN(10), + RP1_GPIO_PIN(11), + RP1_GPIO_PIN(12), + RP1_GPIO_PIN(13), + RP1_GPIO_PIN(14), + RP1_GPIO_PIN(15), + RP1_GPIO_PIN(16), + RP1_GPIO_PIN(17), + RP1_GPIO_PIN(18), + RP1_GPIO_PIN(19), + RP1_GPIO_PIN(20), + RP1_GPIO_PIN(21), + RP1_GPIO_PIN(22), + RP1_GPIO_PIN(23), + RP1_GPIO_PIN(24), + RP1_GPIO_PIN(25), + RP1_GPIO_PIN(26), + RP1_GPIO_PIN(27), + RP1_GPIO_PIN(28), + RP1_GPIO_PIN(29), + RP1_GPIO_PIN(30), + RP1_GPIO_PIN(31), + RP1_GPIO_PIN(32), + RP1_GPIO_PIN(33), + RP1_GPIO_PIN(34), + RP1_GPIO_PIN(35), + RP1_GPIO_PIN(36), + RP1_GPIO_PIN(37), + RP1_GPIO_PIN(38), + RP1_GPIO_PIN(39), + RP1_GPIO_PIN(40), + RP1_GPIO_PIN(41), + RP1_GPIO_PIN(42), + RP1_GPIO_PIN(43), + RP1_GPIO_PIN(44), + RP1_GPIO_PIN(45), + RP1_GPIO_PIN(46), + RP1_GPIO_PIN(47), + RP1_GPIO_PIN(48), + RP1_GPIO_PIN(49), + RP1_GPIO_PIN(50), + RP1_GPIO_PIN(51), + RP1_GPIO_PIN(52), + RP1_GPIO_PIN(53), +}; + +#define PIN_ARRAY(...) \ + (const unsigned int []) {__VA_ARGS__} +#define PIN_ARRAY_SIZE(...) \ + (sizeof((unsigned int[]) {__VA_ARGS__}) / sizeof(unsigned int)) +#define RP1_GROUP(name, ...) \ + PINCTRL_PINGROUP(#name, PIN_ARRAY(__VA_ARGS__), \ + PIN_ARRAY_SIZE(__VA_ARGS__)) + +static const struct pingroup rp1_gpio_groups[] = { + RP1_GROUP(uart0, 14, 15), + RP1_GROUP(uart0_ctrl, 4, 5, 6, 7, 16, 17), + RP1_GROUP(uart1, 0, 1), + RP1_GROUP(uart1_ctrl, 2, 3), + RP1_GROUP(uart2, 4, 5), + RP1_GROUP(uart2_ctrl, 6, 7), + RP1_GROUP(uart3, 8, 9), + RP1_GROUP(uart3_ctrl, 10, 11), + RP1_GROUP(uart4, 12, 13), + RP1_GROUP(uart4_ctrl, 14, 15), + RP1_GROUP(uart5_0, 30, 31), + RP1_GROUP(uart5_0_ctrl, 32, 33), + RP1_GROUP(uart5_1, 36, 37), + RP1_GROUP(uart5_1_ctrl, 38, 39), + RP1_GROUP(uart5_2, 40, 41), + RP1_GROUP(uart5_2_ctrl, 42, 43), + RP1_GROUP(uart5_3, 48, 49), + RP1_GROUP(sd0, 22, 23, 24, 25, 26, 27), + RP1_GROUP(sd1, 28, 29, 30, 31, 32, 33), + RP1_GROUP(i2s0, 18, 19, 20, 21), + RP1_GROUP(i2s0_dual, 18, 19, 20, 21, 22, 23), + RP1_GROUP(i2s0_quad, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27), + RP1_GROUP(i2s1, 18, 19, 20, 21), + RP1_GROUP(i2s1_dual, 18, 19, 20, 21, 22, 23), + RP1_GROUP(i2s1_quad, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27), + RP1_GROUP(i2s2_0, 28, 29, 30, 31), + RP1_GROUP(i2s2_0_dual, 28, 29, 30, 31, 32, 33), + RP1_GROUP(i2s2_1, 42, 43, 44, 45), + RP1_GROUP(i2s2_1_dual, 42, 43, 44, 45, 46, 47), + RP1_GROUP(i2c4_0, 28, 29), + RP1_GROUP(i2c4_1, 34, 35), + RP1_GROUP(i2c4_2, 40, 41), + RP1_GROUP(i2c4_3, 46, 47), + RP1_GROUP(i2c6_0, 38, 39), + RP1_GROUP(i2c6_1, 51, 52), + RP1_GROUP(i2c5_0, 30, 31), + RP1_GROUP(i2c5_1, 36, 37), + RP1_GROUP(i2c5_2, 44, 45), + RP1_GROUP(i2c5_3, 49, 50), + RP1_GROUP(i2c0_0, 0, 1), + RP1_GROUP(i2c0_1, 8, 9), + RP1_GROUP(i2c1_0, 2, 3), + RP1_GROUP(i2c1_1, 10, 11), + RP1_GROUP(i2c2_0, 4, 5), + RP1_GROUP(i2c2_1, 12, 13), + RP1_GROUP(i2c3_0, 6, 7), + RP1_GROUP(i2c3_1, 14, 15), + RP1_GROUP(i2c3_2, 22, 23), + RP1_GROUP(dpi_16bit, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19), + RP1_GROUP(dpi_16bit_cpadhi, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 12, 13, 14, 15, 16, 17, 20, 21, 22, 23, 24), + RP1_GROUP(dpi_16bit_pad666, 0, 1, 2, 3, 5, 6, 7, 8, 9, + 12, 13, 14, 15, 16, 17, 21, 22, 23, 24, 25), + RP1_GROUP(dpi_18bit, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21), + RP1_GROUP(dpi_18bit_cpadhi, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 12, 13, 14, 15, 16, 17, 20, 21, 22, 23, 24, + 25), + RP1_GROUP(dpi_24bit, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27), + RP1_GROUP(spi0, 9, 10, 11), + RP1_GROUP(spi0_quad, 0, 1, 9, 10, 11), + RP1_GROUP(spi1, 19, 20, 21), + RP1_GROUP(spi2, 1, 2, 3), + RP1_GROUP(spi3, 5, 6, 7), + RP1_GROUP(spi4, 9, 10, 11), + RP1_GROUP(spi5, 13, 14, 15), + RP1_GROUP(spi6_0, 28, 29, 30), + RP1_GROUP(spi6_1, 40, 41, 42), + RP1_GROUP(spi7_0, 46, 47, 48), + RP1_GROUP(spi7_1, 49, 50, 51), + RP1_GROUP(spi8_0, 37, 38, 39), + RP1_GROUP(spi8_1, 49, 50, 51), + RP1_GROUP(aaud_0, 12, 13), + RP1_GROUP(aaud_1, 38, 39), + RP1_GROUP(aaud_2, 40, 41), + RP1_GROUP(aaud_3, 49, 50), + RP1_GROUP(aaud_4, 51, 52), + RP1_GROUP(vbus0_0, 28, 29), + RP1_GROUP(vbus0_1, 34, 35), + RP1_GROUP(vbus1, 42, 43), + RP1_GROUP(vbus2, 50, 51), + RP1_GROUP(vbus3, 52, 53), + RP1_GROUP(mic_0, 25, 26, 27), + RP1_GROUP(mic_1, 34, 35, 36), + RP1_GROUP(mic_2, 37, 38, 39), + RP1_GROUP(mic_3, 46, 47, 48), + RP1_GROUP(ir, 2, 3), +}; + +#define GRP_ARRAY(...) \ + (const char * []) {__VA_ARGS__} +#define GRP_ARRAY_SIZE(...) \ + (sizeof((char *[]) {__VA_ARGS__}) / sizeof(char *)) +#define RP1_FNC(f, ...) \ + [func_##f] = PINCTRL_PINFUNCTION(#f, GRP_ARRAY(__VA_ARGS__), \ + GRP_ARRAY_SIZE(__VA_ARGS__)) +#define RP1_NULL_FNC(f) \ + [func_##f] = PINCTRL_PINFUNCTION(#f, NULL, 0) +#define RP1_ALL_LEGACY_PINS \ + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", \ + "gpio5", "gpio6", "gpio7", "gpio8", "gpio9", \ + "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", \ + "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", \ + "gpio20", "gpio21", "gpio22", "gpio32", "gpio24", \ + "gpio25", "gpio26", "gpio27" +#define RP1_ALL_PINS RP1_ALL_LEGACY_PINS, \ + "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", \ + "gpio33", "gpio34", "gpio35", "gpio36", "gpio37", \ + "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", \ + "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", \ + "gpio48", "gpio49", "gpio50", "gpio51", "gpio52", \ + "gpio53" + +static const struct pinfunction rp1_func_names[] = { + RP1_NULL_FNC(alt0), + RP1_NULL_FNC(alt1), + RP1_NULL_FNC(alt2), + RP1_NULL_FNC(alt3), + RP1_NULL_FNC(alt4), + RP1_FNC(gpio, RP1_ALL_PINS), + RP1_NULL_FNC(alt6), + RP1_NULL_FNC(alt7), + RP1_NULL_FNC(alt8), + RP1_NULL_FNC(none), + RP1_FNC(aaud, "aaud_0", "aaud_1", "aaud_2", "aaud_3", "aaud_4", + "gpio12", "gpio13", "gpio38", "gpio39", "gpio40", "gpio41", + "gpio49", "gpio50", "gpio51", "gpio52"), + RP1_FNC(dpi, "dpi_16bit", "dpi_16bit_cpadhi", + "dpi_16bit_pad666", "dpi_18bit, dpi_18bit_cpadhi", + "dpi_24bit", RP1_ALL_LEGACY_PINS), + RP1_FNC(dsi0_te_ext, "gpio16", "gpio38", "gpio46"), + RP1_FNC(dsi1_te_ext, "gpio17", "gpio39", "gpio47"), + RP1_FNC(gpclk0, "gpio4", "gpio20"), + RP1_FNC(gpclk1, "gpio5", "gpio18", "gpio21"), + RP1_FNC(gpclk2, "gpio6"), + RP1_FNC(gpclk3, "gpio32", "gpio34", "gpio46"), + RP1_FNC(gpclk4, "gpio33", "gpio43"), + RP1_FNC(gpclk5, "gpio42", "gpio44", "gpio47"), + RP1_FNC(i2c0, "i2c0_0", "i2c0_1", "gpio0", "gpio1", "gpio8", "gpio9"), + RP1_FNC(i2c1, "i2c1_0", "i2c1_1", "gpio2", "gpio3", "gpio10", "gpio11"), + RP1_FNC(i2c2, "i2c2_0", "i2c2_1", "gpio4", "gpio5", "gpio12", "gpio13"), + RP1_FNC(i2c3, "i2c3_0", "i2c3_1", "i2c3_2", "gpio6", "gpio7", "gpio14", + "gpio15", "gpio22", "gpio23"), + RP1_FNC(i2c4, "i2c4_0", "i2c4_1", "i2c4_2", "i2c4_3", "gpio28", + "gpio29", "gpio34", "gpio35", "gpio40", "gpio41", "gpio46", + "gpio47"), + RP1_FNC(i2c5, "i2c5_0", "i2c5_1", "i2c5_2", "i2c5_3", "gpio30", + "gpio31", "gpio36", "gpio37", "gpio44", "gpio45", "gpio49", + "gpio50"), + RP1_FNC(i2c6, "i2c6_0", "i2c6_1", "gpio38", "gpio39", "gpio51", + "gpio52"), + RP1_FNC(i2s0, "i2s0", "i2s0_dual", "i2s0_quad", "gpio18", "gpio19", + "gpio20", "gpio21", "gpio22", "gpio23", "gpio24", "gpio25", + "gpio26", "gpio27"), + RP1_FNC(i2s1, "i2s1", "i2s1_dual", "i2s1_quad", "gpio18", "gpio19", + "gpio20", "gpio21", "gpio22", "gpio23", "gpio24", "gpio25", + "gpio26", "gpio27"), + RP1_FNC(i2s2, "i2s2_0", "i2s2_0_dual", "i2s2_1", "i2s2_1_dual", + "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", + "gpio42", "gpio43", "gpio44", "gpio45", "gpio46", "gpio47"), + RP1_FNC(ir, "gpio2", "gpio3"), + RP1_FNC(mic, "mic_0", "mic_1", "mic_2", "mic_3", "gpio25", "gpio26", + "gpio27", "gpio34", "gpio35", "gpio36", "gpio37", "gpio38", + "gpio39", "gpio46", "gpio47", "gpio48"), + RP1_FNC(pcie_clkreq_n, "gpio36", "gpio37", "gpio48", "gpio53"), + RP1_FNC(pio, RP1_ALL_LEGACY_PINS), + RP1_FNC(proc_rio, RP1_ALL_PINS), + RP1_FNC(pwm0, "gpio12", "gpio13", "gpio14", "gpio15", "gpio18", + "gpio19"), + RP1_FNC(pwm1, "gpio34", "gpio35", "gpio40", "gpio41", "gpio44", + "gpio45", "gpio48"), + RP1_FNC(sd0, "sd0", "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", + "gpio27"), + RP1_FNC(sd1, "sd1", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", + "gpio33"), + RP1_FNC(spi0, "spi0", "spi0_quad", "gpio0", "gpio1", "gpio2", "gpio3", + "gpio7", "gpio8", "gpio9", "gpio10", "gpio11"), + RP1_FNC(spi1, "spi1", "gpio19", "gpio20", "gpio21", "gpio16", "gpio17", + "gpio18", "gpio27"), + RP1_FNC(spi2, "spi2", "gpio0", "gpio1", "gpio2", "gpio3", "gpio24"), + RP1_FNC(spi3, "spi3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio25"), + RP1_FNC(spi4, "spi4", "gpio8", "gpio9", "gpio10", "gpio11"), + RP1_FNC(spi5, "spi5", "gpio12", "gpio13", "gpio14", "gpio15", "gpio26"), + RP1_FNC(spi6, "spi6_0", "spi6_1", "gpio28", "gpio29", "gpio30", + "gpio31", "gpio32", "gpio33", "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45"), + RP1_FNC(spi7, "spi7_0", "spi7_1", "gpio45", "gpio46", "gpio47", + "gpio48", "gpio49", "gpio50", "gpio51", "gpio53"), + RP1_FNC(spi8, "spi8_0", "spi8_1", "gpio35", "gpio36", "gpio37", + "gpio38", "gpio39", "gpio49", "gpio50", "gpio51", "gpio52", + "gpio53"), + RP1_FNC(uart0, "uart0", "uart0_ctrl", "gpio4", "gpio5", "gpio6", + "gpio7", "gpio14", "gpio15", "gpio16", "gpio17"), + RP1_FNC(uart1, "uart1", "uart1_ctrl", "gpio0", "gpio1", "gpio2", + "gpio3"), + RP1_FNC(uart2, "uart2", "uart2_ctrl", "gpio4", "gpio5", "gpio6", + "gpio7"), + RP1_FNC(uart3, "uart3", "uart3_ctrl", "gpio8", "gpio9", "gpio10", + "gpio11"), + RP1_FNC(uart4, "uart4", "uart4_ctrl", "gpio12", "gpio13", "gpio14", + "gpio15"), + RP1_FNC(uart5, "uart5_0", "uart5_0_ctrl", "uart5_1", "uart5_1_ctrl", + "uart5_2", "uart5_2_ctrl", "uart5_3"), + RP1_FNC(vbus0, "vbus0_0", "vbus0_1", "gpio28", "gpio29", "gpio34", + "gpio35"), + RP1_FNC(vbus1, "vbus1", "gpio42", "gpio43"), + RP1_FNC(vbus2, "vbus2", "gpio50", "gpio51"), + RP1_FNC(vbus3, "vbus3", "gpio52", "gpio53"), + RP1_NULL_FNC(invalid), //[func_invalid] = "?" +}; + +static const struct rp1_pin_funcs rp1_gpio_pin_funcs[] = { + PIN(0, spi0, dpi, uart1, i2c0, _, gpio, proc_rio, pio, spi2), + PIN(1, spi0, dpi, uart1, i2c0, _, gpio, proc_rio, pio, spi2), + PIN(2, spi0, dpi, uart1, i2c1, ir, gpio, proc_rio, pio, spi2), + PIN(3, spi0, dpi, uart1, i2c1, ir, gpio, proc_rio, pio, spi2), + PIN(4, gpclk0, dpi, uart2, i2c2, uart0, gpio, proc_rio, pio, spi3), + PIN(5, gpclk1, dpi, uart2, i2c2, uart0, gpio, proc_rio, pio, spi3), + PIN(6, gpclk2, dpi, uart2, i2c3, uart0, gpio, proc_rio, pio, spi3), + PIN(7, spi0, dpi, uart2, i2c3, uart0, gpio, proc_rio, pio, spi3), + PIN(8, spi0, dpi, uart3, i2c0, _, gpio, proc_rio, pio, spi4), + PIN(9, spi0, dpi, uart3, i2c0, _, gpio, proc_rio, pio, spi4), + PIN(10, spi0, dpi, uart3, i2c1, _, gpio, proc_rio, pio, spi4), + PIN(11, spi0, dpi, uart3, i2c1, _, gpio, proc_rio, pio, spi4), + PIN(12, pwm0, dpi, uart4, i2c2, aaud, gpio, proc_rio, pio, spi5), + PIN(13, pwm0, dpi, uart4, i2c2, aaud, gpio, proc_rio, pio, spi5), + PIN(14, pwm0, dpi, uart4, i2c3, uart0, gpio, proc_rio, pio, spi5), + PIN(15, pwm0, dpi, uart4, i2c3, uart0, gpio, proc_rio, pio, spi5), + PIN(16, spi1, dpi, dsi0_te_ext, _, uart0, gpio, proc_rio, pio, _), + PIN(17, spi1, dpi, dsi1_te_ext, _, uart0, gpio, proc_rio, pio, _), + PIN(18, spi1, dpi, i2s0, pwm0, i2s1, gpio, proc_rio, pio, gpclk1), + PIN(19, spi1, dpi, i2s0, pwm0, i2s1, gpio, proc_rio, pio, _), + PIN(20, spi1, dpi, i2s0, gpclk0, i2s1, gpio, proc_rio, pio, _), + PIN(21, spi1, dpi, i2s0, gpclk1, i2s1, gpio, proc_rio, pio, _), + PIN(22, sd0, dpi, i2s0, i2c3, i2s1, gpio, proc_rio, pio, _), + PIN(23, sd0, dpi, i2s0, i2c3, i2s1, gpio, proc_rio, pio, _), + PIN(24, sd0, dpi, i2s0, _, i2s1, gpio, proc_rio, pio, spi2), + PIN(25, sd0, dpi, i2s0, mic, i2s1, gpio, proc_rio, pio, spi3), + PIN(26, sd0, dpi, i2s0, mic, i2s1, gpio, proc_rio, pio, spi5), + PIN(27, sd0, dpi, i2s0, mic, i2s1, gpio, proc_rio, pio, spi1), + PIN(28, sd1, i2c4, i2s2, spi6, vbus0, gpio, proc_rio, _, _), + PIN(29, sd1, i2c4, i2s2, spi6, vbus0, gpio, proc_rio, _, _), + PIN(30, sd1, i2c5, i2s2, spi6, uart5, gpio, proc_rio, _, _), + PIN(31, sd1, i2c5, i2s2, spi6, uart5, gpio, proc_rio, _, _), + PIN(32, sd1, gpclk3, i2s2, spi6, uart5, gpio, proc_rio, _, _), + PIN(33, sd1, gpclk4, i2s2, spi6, uart5, gpio, proc_rio, _, _), + PIN(34, pwm1, gpclk3, vbus0, i2c4, mic, gpio, proc_rio, _, _), + PIN(35, spi8, pwm1, vbus0, i2c4, mic, gpio, proc_rio, _, _), + PIN(36, spi8, uart5, pcie_clkreq_n, i2c5, mic, gpio, proc_rio, _, _), + PIN(37, spi8, uart5, mic, i2c5, pcie_clkreq_n, gpio, proc_rio, _, _), + PIN(38, spi8, uart5, mic, i2c6, aaud, gpio, proc_rio, dsi0_te_ext, _), + PIN(39, spi8, uart5, mic, i2c6, aaud, gpio, proc_rio, dsi1_te_ext, _), + PIN(40, pwm1, uart5, i2c4, spi6, aaud, gpio, proc_rio, _, _), + PIN(41, pwm1, uart5, i2c4, spi6, aaud, gpio, proc_rio, _, _), + PIN(42, gpclk5, uart5, vbus1, spi6, i2s2, gpio, proc_rio, _, _), + PIN(43, gpclk4, uart5, vbus1, spi6, i2s2, gpio, proc_rio, _, _), + PIN(44, gpclk5, i2c5, pwm1, spi6, i2s2, gpio, proc_rio, _, _), + PIN(45, pwm1, i2c5, spi7, spi6, i2s2, gpio, proc_rio, _, _), + PIN(46, gpclk3, i2c4, spi7, mic, i2s2, gpio, proc_rio, dsi0_te_ext, _), + PIN(47, gpclk5, i2c4, spi7, mic, i2s2, gpio, proc_rio, dsi1_te_ext, _), + PIN(48, pwm1, pcie_clkreq_n, spi7, mic, uart5, gpio, proc_rio, _, _), + PIN(49, spi8, spi7, i2c5, aaud, uart5, gpio, proc_rio, _, _), + PIN(50, spi8, spi7, i2c5, aaud, vbus2, gpio, proc_rio, _, _), + PIN(51, spi8, spi7, i2c6, aaud, vbus2, gpio, proc_rio, _, _), + PIN(52, spi8, _, i2c6, aaud, vbus3, gpio, proc_rio, _, _), + PIN(53, spi8, spi7, _, pcie_clkreq_n, vbus3, gpio, proc_rio, _, _), +}; + +static const u8 legacy_fsel_map[][8] = { + LEGACY_MAP(0, i2c0, _, dpi, spi2, uart1, _), + LEGACY_MAP(1, i2c0, _, dpi, spi2, uart1, _), + LEGACY_MAP(2, i2c1, _, dpi, spi2, uart1, _), + LEGACY_MAP(3, i2c1, _, dpi, spi2, uart1, _), + LEGACY_MAP(4, gpclk0, _, dpi, spi3, uart2, i2c2), + LEGACY_MAP(5, gpclk1, _, dpi, spi3, uart2, i2c2), + LEGACY_MAP(6, gpclk2, _, dpi, spi3, uart2, i2c3), + LEGACY_MAP(7, spi0, _, dpi, spi3, uart2, i2c3), + LEGACY_MAP(8, spi0, _, dpi, _, uart3, i2c0), + LEGACY_MAP(9, spi0, _, dpi, _, uart3, i2c0), + LEGACY_MAP(10, spi0, _, dpi, _, uart3, i2c1), + LEGACY_MAP(11, spi0, _, dpi, _, uart3, i2c1), + LEGACY_MAP(12, pwm0, _, dpi, spi5, uart4, i2c2), + LEGACY_MAP(13, pwm0, _, dpi, spi5, uart4, i2c2), + LEGACY_MAP(14, uart0, _, dpi, spi5, uart4, _), + LEGACY_MAP(15, uart0, _, dpi, spi5, uart4, _), + LEGACY_MAP(16, _, _, dpi, uart0, spi1, _), + LEGACY_MAP(17, _, _, dpi, uart0, spi1, _), + LEGACY_MAP(18, i2s0, _, dpi, _, spi1, pwm0), + LEGACY_MAP(19, i2s0, _, dpi, _, spi1, pwm0), + LEGACY_MAP(20, i2s0, _, dpi, _, spi1, gpclk0), + LEGACY_MAP(21, i2s0, _, dpi, _, spi1, gpclk1), + LEGACY_MAP(22, sd0, _, dpi, _, _, i2c3), + LEGACY_MAP(23, sd0, _, dpi, _, _, i2c3), + LEGACY_MAP(24, sd0, _, dpi, _, _, spi2), + LEGACY_MAP(25, sd0, _, dpi, _, _, spi3), + LEGACY_MAP(26, sd0, _, dpi, _, _, spi5), + LEGACY_MAP(27, sd0, _, dpi, _, _, _), +}; + +static const char * const irq_type_names[] = { + [IRQ_TYPE_NONE] = "none", + [IRQ_TYPE_EDGE_RISING] = "edge-rising", + [IRQ_TYPE_EDGE_FALLING] = "edge-falling", + [IRQ_TYPE_EDGE_BOTH] = "edge-both", + [IRQ_TYPE_LEVEL_HIGH] = "level-high", + [IRQ_TYPE_LEVEL_LOW] = "level-low", +}; + +static bool persist_gpio_outputs = true; +module_param(persist_gpio_outputs, bool, 0644); +MODULE_PARM_DESC(persist_gpio_outputs, "Enable GPIO_OUT persistence when pin is freed"); + +static const struct rp1_iobank_desc rp1_iobanks[RP1_NUM_BANKS] = { + /* gpio inte ints rio pads */ + { 0, 28, 0x0000, 0x011c, 0x0124, 0x0000, 0x0004 }, + { 28, 6, 0x4000, 0x411c, 0x4124, 0x4000, 0x4004 }, + { 34, 20, 0x8000, 0x811c, 0x8124, 0x8000, 0x8004 }, +}; + +static int rp1_pinconf_set(struct pinctrl_dev *pctldev, + unsigned int offset, unsigned long *configs, + unsigned int num_configs); + +static struct rp1_pin_info *rp1_get_pin(struct gpio_chip *chip, + unsigned int offset) +{ + struct rp1_pinctrl *pc = gpiochip_get_data(chip); + + if (pc && offset < RP1_NUM_GPIOS) + return &pc->pins[offset]; + return NULL; +} + +static struct rp1_pin_info *rp1_get_pin_pctl(struct pinctrl_dev *pctldev, + unsigned int offset) +{ + struct rp1_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + + if (pc && offset < RP1_NUM_GPIOS) + return &pc->pins[offset]; + return NULL; +} + +static void rp1_input_enable(struct rp1_pin_info *pin, int value) +{ + regmap_field_write(pin->pad[RP1_PAD_IN_ENABLE], !!value); +} + +static void rp1_output_enable(struct rp1_pin_info *pin, int value) +{ + regmap_field_write(pin->pad[RP1_PAD_OUT_DISABLE], !value); +} + +static u32 rp1_get_fsel(struct rp1_pin_info *pin) +{ + u32 oeover, fsel; + + regmap_field_read(pin->gpio[RP1_GPIO_CTRL_OEOVER], &oeover); + regmap_field_read(pin->gpio[RP1_GPIO_CTRL_FUNCSEL], &fsel); + + if (oeover != RP1_OEOVER_PERI || fsel >= RP1_FSEL_COUNT) + fsel = RP1_FSEL_NONE; + + return fsel; +} + +static void rp1_set_fsel(struct rp1_pin_info *pin, u32 fsel) +{ + if (fsel >= RP1_FSEL_COUNT) + fsel = RP1_FSEL_NONE_HW; + + rp1_input_enable(pin, 1); + rp1_output_enable(pin, 1); + + if (fsel == RP1_FSEL_NONE) { + regmap_field_write(pin->gpio[RP1_GPIO_CTRL_OEOVER], RP1_OEOVER_DISABLE); + } else { + regmap_field_write(pin->gpio[RP1_GPIO_CTRL_OUTOVER], RP1_OUTOVER_PERI); + regmap_field_write(pin->gpio[RP1_GPIO_CTRL_OEOVER], RP1_OEOVER_PERI); + } + + regmap_field_write(pin->gpio[RP1_GPIO_CTRL_FUNCSEL], fsel); +} + +static int rp1_get_dir(struct rp1_pin_info *pin) +{ + unsigned int val; + + regmap_field_read(pin->rio[RP1_RIO_OE], &val); + + return !val ? RP1_DIR_INPUT : RP1_DIR_OUTPUT; +} + +static void rp1_set_dir(struct rp1_pin_info *pin, bool is_input) +{ + int reg = is_input ? RP1_RIO_OE_CLR : RP1_RIO_OE_SET; + + regmap_field_write(pin->rio[reg], 1); +} + +static int rp1_get_value(struct rp1_pin_info *pin) +{ + unsigned int val; + + regmap_field_read(pin->rio[RP1_RIO_IN], &val); + + return !!val; +} + +static void rp1_set_value(struct rp1_pin_info *pin, int value) +{ + /* Assume the pin is already an output */ + int reg = value ? RP1_RIO_OUT_SET : RP1_RIO_OUT_CLR; + + regmap_field_write(pin->rio[reg], 1); +} + +static int rp1_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct rp1_pin_info *pin = rp1_get_pin(chip, offset); + int ret; + + if (!pin) + return -EINVAL; + + ret = rp1_get_value(pin); + + return ret; +} + +static int rp1_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct rp1_pin_info *pin = rp1_get_pin(chip, offset); + + if (pin) + rp1_set_value(pin, value); + + return 0; +} + +static int rp1_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct rp1_pin_info *pin = rp1_get_pin(chip, offset); + u32 fsel; + + if (!pin) + return -EINVAL; + + fsel = rp1_get_fsel(pin); + if (fsel != RP1_FSEL_GPIO) + return -EINVAL; + + return (rp1_get_dir(pin) == RP1_DIR_OUTPUT) ? + GPIO_LINE_DIRECTION_OUT : + GPIO_LINE_DIRECTION_IN; +} + +static int rp1_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + struct rp1_pin_info *pin = rp1_get_pin(chip, offset); + + if (!pin) + return -EINVAL; + rp1_set_dir(pin, RP1_DIR_INPUT); + rp1_set_fsel(pin, RP1_FSEL_GPIO); + + return 0; +} + +static int rp1_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct rp1_pin_info *pin = rp1_get_pin(chip, offset); + + if (!pin) + return -EINVAL; + rp1_set_value(pin, value); + rp1_set_dir(pin, RP1_DIR_OUTPUT); + rp1_set_fsel(pin, RP1_FSEL_GPIO); + + return 0; +} + +static int rp1_gpio_set_config(struct gpio_chip *chip, unsigned int offset, + unsigned long config) +{ + struct rp1_pinctrl *pc = gpiochip_get_data(chip); + unsigned long configs[] = { config }; + + return rp1_pinconf_set(pc->pctl_dev, offset, configs, + ARRAY_SIZE(configs)); +} + +static const struct gpio_chip rp1_gpio_chip = { + .label = MODULE_NAME, + .owner = THIS_MODULE, + .request = gpiochip_generic_request, + .free = gpiochip_generic_free, + .direction_input = rp1_gpio_direction_input, + .direction_output = rp1_gpio_direction_output, + .get_direction = rp1_gpio_get_direction, + .get = rp1_gpio_get, + .set_rv = rp1_gpio_set, + .base = -1, + .set_config = rp1_gpio_set_config, + .ngpio = RP1_NUM_GPIOS, + .can_sleep = false, +}; + +static void rp1_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *chip = irq_desc_get_handler_data(desc); + struct irq_chip *host_chip = irq_desc_get_chip(desc); + struct rp1_pinctrl *pc = gpiochip_get_data(chip); + const struct rp1_iobank_desc *bank; + int irq = irq_desc_get_irq(desc); + unsigned long ints; + int bit_pos; + + if (pc->irq[0] == irq) + bank = &rp1_iobanks[0]; + else if (pc->irq[1] == irq) + bank = &rp1_iobanks[1]; + else + bank = &rp1_iobanks[2]; + + chained_irq_enter(host_chip, desc); + + ints = readl(pc->gpio_base + bank->ints_offset); + for_each_set_bit(bit_pos, &ints, 32) { + struct rp1_pin_info *pin = rp1_get_pin(chip, bit_pos); + + regmap_field_write(pin->gpio[RP1_GPIO_CTRL_IRQRESET_SET], 1); + generic_handle_irq(irq_find_mapping(pc->gpio_chip.irq.domain, + bank->gpio_offset + bit_pos)); + } + + chained_irq_exit(host_chip, desc); +} + +static void rp1_gpio_irq_config(struct rp1_pin_info *pin, bool enable) +{ + int reg = enable ? RP1_INTE_SET : RP1_INTE_CLR; + + regmap_field_write(pin->inte[reg], 1); + if (!enable) + /* Clear any latched events */ + regmap_field_write(pin->gpio[RP1_GPIO_CTRL_IRQRESET_SET], 1); +} + +static void rp1_gpio_irq_enable(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + unsigned int gpio = irqd_to_hwirq(data); + struct rp1_pin_info *pin = rp1_get_pin(chip, gpio); + + rp1_gpio_irq_config(pin, true); +} + +static void rp1_gpio_irq_disable(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + unsigned int gpio = irqd_to_hwirq(data); + struct rp1_pin_info *pin = rp1_get_pin(chip, gpio); + + rp1_gpio_irq_config(pin, false); +} + +static int rp1_irq_set_type(struct rp1_pin_info *pin, unsigned int type) +{ + u32 irq_flags; + + switch (type) { + case IRQ_TYPE_NONE: + irq_flags = 0; + break; + case IRQ_TYPE_EDGE_RISING: + irq_flags = RP1_INT_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + irq_flags = RP1_INT_EDGE_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: + irq_flags = RP1_INT_EDGE_RISING | RP1_INT_EDGE_FALLING; + break; + case IRQ_TYPE_LEVEL_HIGH: + irq_flags = RP1_INT_LEVEL_HIGH; + break; + case IRQ_TYPE_LEVEL_LOW: + irq_flags = RP1_INT_LEVEL_LOW; + break; + + default: + return -EINVAL; + } + + /* Clear them all */ + regmap_field_write(pin->gpio[RP1_GPIO_CTRL_INT_CLR], RP1_INT_MASK); + + /* Set those that are needed */ + regmap_field_write(pin->gpio[RP1_GPIO_CTRL_INT_SET], irq_flags); + pin->irq_type = type; + + return 0; +} + +static int rp1_gpio_irq_set_type(struct irq_data *data, unsigned int type) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + unsigned int gpio = irqd_to_hwirq(data); + struct rp1_pin_info *pin = rp1_get_pin(chip, gpio); + struct rp1_pinctrl *pc = gpiochip_get_data(chip); + int bank = pin->bank; + unsigned long flags; + int ret; + + raw_spin_lock_irqsave(&pc->irq_lock[bank], flags); + + ret = rp1_irq_set_type(pin, type); + if (!ret) { + if (type & IRQ_TYPE_EDGE_BOTH) + irq_set_handler_locked(data, handle_edge_irq); + else + irq_set_handler_locked(data, handle_level_irq); + } + + raw_spin_unlock_irqrestore(&pc->irq_lock[bank], flags); + + return ret; +} + +static void rp1_gpio_irq_ack(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + unsigned int gpio = irqd_to_hwirq(data); + struct rp1_pin_info *pin = rp1_get_pin(chip, gpio); + + /* Clear any latched events */ + regmap_field_write(pin->gpio[RP1_GPIO_CTRL_IRQRESET_SET], 1); +} + +static int rp1_gpio_irq_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct rp1_pinctrl *pc = gpiochip_get_data(chip); + const struct rp1_iobank_desc *bank; + struct irq_data *parent_data = NULL; + int i; + + for (i = 0; i < 3; i++) { + bank = &rp1_iobanks[i]; + if (data->hwirq >= bank->min_gpio && + data->hwirq < bank->min_gpio + bank->num_gpios) { + parent_data = irq_get_irq_data(pc->irq[i]); + break; + } + } + + if (parent_data && parent_data->chip->irq_set_affinity) + return parent_data->chip->irq_set_affinity(parent_data, dest, force); + + return -EINVAL; +} + +static struct irq_chip rp1_gpio_irq_chip = { + .name = MODULE_NAME, + .irq_enable = rp1_gpio_irq_enable, + .irq_disable = rp1_gpio_irq_disable, + .irq_set_type = rp1_gpio_irq_set_type, + .irq_ack = rp1_gpio_irq_ack, + .irq_mask = rp1_gpio_irq_disable, + .irq_unmask = rp1_gpio_irq_enable, + .irq_set_affinity = rp1_gpio_irq_set_affinity, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static int rp1_pctl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(rp1_gpio_groups) + ARRAY_SIZE(rp1_gpio_pins); +} + +static const char *rp1_pctl_get_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + unsigned int ngroups = ARRAY_SIZE(rp1_gpio_groups); + + if (selector < ngroups) + return rp1_gpio_groups[selector].name; + + return rp1_gpio_pins[selector - ngroups].name; +} + +static enum funcs rp1_get_fsel_func(unsigned int pin, unsigned int fsel) +{ + if (pin < RP1_NUM_GPIOS) { + if (fsel < RP1_FSEL_COUNT) + return rp1_gpio_pin_funcs[pin].funcs[fsel]; + else if (fsel == RP1_FSEL_NONE) + return func_none; + } + return func_invalid; +} + +static int rp1_pctl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int selector, + const unsigned int **pins, + unsigned int *num_pins) +{ + unsigned int ngroups = ARRAY_SIZE(rp1_gpio_groups); + + if (selector < ngroups) { + *pins = rp1_gpio_groups[selector].pins; + *num_pins = rp1_gpio_groups[selector].npins; + } else { + *pins = &rp1_gpio_pins[selector - ngroups].number; + *num_pins = 1; + } + + return 0; +} + +static void rp1_pctl_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned int offset) +{ + struct rp1_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + struct gpio_chip *chip = &pc->gpio_chip; + struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset); + u32 fsel = rp1_get_fsel(pin); + enum funcs func = rp1_get_fsel_func(offset, fsel); + int value = rp1_get_value(pin); + int irq = irq_find_mapping(chip->irq.domain, offset); + + seq_printf(s, "function %s (%s) in %s; irq %d (%s)", + rp1_func_names[fsel].name, rp1_func_names[func].name, + value ? "hi" : "lo", + irq, irq_type_names[pin->irq_type]); +} + +static void rp1_pctl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *maps, unsigned int num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) + if (maps[i].type == PIN_MAP_TYPE_CONFIGS_PIN) + kfree(maps[i].data.configs.configs); + + kfree(maps); +} + +static int rp1_pctl_legacy_map_func(struct rp1_pinctrl *pc, + struct device_node *np, u32 pin, u32 fnum, + struct pinctrl_map *maps, + unsigned int *num_maps) +{ + struct pinctrl_map *map = &maps[*num_maps]; + enum funcs func; + + if (fnum >= ARRAY_SIZE(legacy_fsel_map[0])) { + dev_err(pc->dev, "%pOF: invalid brcm,function %d\n", np, fnum); + return -EINVAL; + } + + if (pin < ARRAY_SIZE(legacy_fsel_map)) { + func = legacy_fsel_map[pin][fnum]; + } else if (fnum < 2) { + func = func_gpio; + } else { + dev_err(pc->dev, "%pOF: invalid brcm,pins value %d\n", + np, pin); + return -EINVAL; + } + + map->type = PIN_MAP_TYPE_MUX_GROUP; + map->data.mux.group = rp1_pctl_get_group_name(pc->pctl_dev, + ARRAY_SIZE(rp1_gpio_groups) + + pin); + map->data.mux.function = rp1_func_names[func].name; + (*num_maps)++; + + return 0; +} + +static int rp1_pctl_legacy_map_pull(struct rp1_pinctrl *pc, + struct device_node *np, u32 pin, u32 pull, + struct pinctrl_map *maps, + unsigned int *num_maps) +{ + struct pinctrl_map *map = &maps[*num_maps]; + enum pin_config_param param; + unsigned long *configs; + + switch (pull) { + case RP1_PUD_OFF: + param = PIN_CONFIG_BIAS_DISABLE; + break; + case RP1_PUD_DOWN: + param = PIN_CONFIG_BIAS_PULL_DOWN; + break; + case RP1_PUD_UP: + param = PIN_CONFIG_BIAS_PULL_UP; + break; + default: + dev_err(pc->dev, "%pOF: invalid brcm,pull %d\n", np, pull); + return -EINVAL; + } + + configs = kzalloc(sizeof(*configs), GFP_KERNEL); + if (!configs) + return -ENOMEM; + + configs[0] = pinconf_to_config_packed(param, 0); + map->type = PIN_MAP_TYPE_CONFIGS_PIN; + map->data.configs.group_or_pin = rp1_gpio_pins[pin].name; + map->data.configs.configs = configs; + map->data.configs.num_configs = 1; + (*num_maps)++; + + return 0; +} + +static int rp1_pctl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, + unsigned int *num_maps) +{ + struct rp1_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + struct property *pins, *funcs, *pulls; + int num_pins, num_funcs, num_pulls, maps_per_pin; + struct pinctrl_map *maps; + unsigned long *configs = NULL; + const char *function = NULL; + unsigned int reserved_maps; + int num_configs = 0; + int i, err; + u32 pin, func, pull; + + /* Check for legacy pin declaration */ + pins = of_find_property(np, "brcm,pins", NULL); + + if (!pins) /* Assume generic bindings in this node */ + return pinconf_generic_dt_node_to_map_all(pctldev, np, map, num_maps); + + funcs = of_find_property(np, "brcm,function", NULL); + if (!funcs) + of_property_read_string(np, "function", &function); + + pulls = of_find_property(np, "brcm,pull", NULL); + if (!pulls) + pinconf_generic_parse_dt_config(np, pctldev, &configs, &num_configs); + + if (!function && !funcs && !num_configs && !pulls) { + dev_err(pc->dev, + "%pOF: no function, brcm,function, brcm,pull, etc.\n", + np); + return -EINVAL; + } + + num_pins = pins->length / 4; + num_funcs = funcs ? (funcs->length / 4) : 0; + num_pulls = pulls ? (pulls->length / 4) : 0; + + if (num_funcs > 1 && num_funcs != num_pins) { + dev_err(pc->dev, + "%pOF: brcm,function must have 1 or %d entries\n", + np, num_pins); + return -EINVAL; + } + + if (num_pulls > 1 && num_pulls != num_pins) { + dev_err(pc->dev, + "%pOF: brcm,pull must have 1 or %d entries\n", + np, num_pins); + return -EINVAL; + } + + maps_per_pin = 0; + if (function || num_funcs) + maps_per_pin++; + if (num_configs || num_pulls) + maps_per_pin++; + reserved_maps = num_pins * maps_per_pin; + maps = kcalloc(reserved_maps, sizeof(*maps), GFP_KERNEL); + if (!maps) + return -ENOMEM; + + *num_maps = 0; + + for (i = 0; i < num_pins; i++) { + err = of_property_read_u32_index(np, "brcm,pins", i, &pin); + if (err) + goto out; + if (num_funcs) { + err = of_property_read_u32_index(np, "brcm,function", + (num_funcs > 1) ? i : 0, + &func); + if (err) + goto out; + err = rp1_pctl_legacy_map_func(pc, np, pin, func, + maps, num_maps); + } else if (function) { + err = pinctrl_utils_add_map_mux(pctldev, &maps, + &reserved_maps, num_maps, + rp1_gpio_groups[pin].name, + function); + } + + if (err) + goto out; + + if (num_pulls) { + err = of_property_read_u32_index(np, "brcm,pull", + (num_pulls > 1) ? i : 0, + &pull); + if (err) + goto out; + err = rp1_pctl_legacy_map_pull(pc, np, pin, pull, + maps, num_maps); + } else if (num_configs) { + err = pinctrl_utils_add_map_configs(pctldev, &maps, + &reserved_maps, num_maps, + rp1_gpio_groups[pin].name, + configs, num_configs, + PIN_MAP_TYPE_CONFIGS_PIN); + } + + if (err) + goto out; + } + + *map = maps; + + return 0; + +out: + rp1_pctl_dt_free_map(pctldev, maps, reserved_maps); + return err; +} + +static const struct pinctrl_ops rp1_pctl_ops = { + .get_groups_count = rp1_pctl_get_groups_count, + .get_group_name = rp1_pctl_get_group_name, + .get_group_pins = rp1_pctl_get_group_pins, + .pin_dbg_show = rp1_pctl_pin_dbg_show, + .dt_node_to_map = rp1_pctl_dt_node_to_map, + .dt_free_map = rp1_pctl_dt_free_map, +}; + +static int rp1_pmx_free(struct pinctrl_dev *pctldev, unsigned int offset) +{ + struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset); + u32 fsel = rp1_get_fsel(pin); + + /* Return all pins to GPIO_IN, unless persist_gpio_outputs is set */ + if (persist_gpio_outputs && fsel == RP1_FSEL_GPIO) + return 0; + + rp1_set_dir(pin, RP1_DIR_INPUT); + rp1_set_fsel(pin, RP1_FSEL_GPIO); + + return 0; +} + +static int rp1_pmx_get_functions_count(struct pinctrl_dev *pctldev) +{ + return func_count; +} + +static const char *rp1_pmx_get_function_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + return (selector < func_count) ? rp1_func_names[selector].name : NULL; +} + +static int rp1_pmx_get_function_groups(struct pinctrl_dev *pctldev, + unsigned int selector, + const char * const **groups, + unsigned * const num_groups) +{ + *groups = rp1_func_names[selector].groups; + *num_groups = rp1_func_names[selector].ngroups; + + return 0; +} + +static int rp1_pmx_set(struct pinctrl_dev *pctldev, unsigned int func_selector, + unsigned int group_selector) +{ + struct rp1_pin_info *pin; + const unsigned int *pins; + const u8 *pin_funcs; + unsigned int num_pins; + int offset, fsel; + + rp1_pctl_get_group_pins(pctldev, group_selector, &pins, &num_pins); + + for (offset = 0; offset < num_pins; ++offset) { + pin = rp1_get_pin_pctl(pctldev, pins[offset]); + /* func_selector is an enum funcs, so needs translation */ + if (func_selector >= RP1_FSEL_COUNT) { + /* Convert to an fsel number */ + pin_funcs = rp1_gpio_pin_funcs[pin->num].funcs; + for (fsel = 0; fsel < RP1_FSEL_COUNT; fsel++) { + if (pin_funcs[fsel] == func_selector) + break; + } + } else { + fsel = (int)func_selector; + } + + if (fsel >= RP1_FSEL_COUNT && fsel != RP1_FSEL_NONE) + return -EINVAL; + + rp1_set_fsel(pin, fsel); + } + + return 0; +} + +static void rp1_pmx_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset) +{ + (void)rp1_pmx_free(pctldev, offset); +} + +static int rp1_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset, + bool input) +{ + struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset); + + rp1_set_dir(pin, input); + rp1_set_fsel(pin, RP1_FSEL_GPIO); + + return 0; +} + +static const struct pinmux_ops rp1_pmx_ops = { + .free = rp1_pmx_free, + .get_functions_count = rp1_pmx_get_functions_count, + .get_function_name = rp1_pmx_get_function_name, + .get_function_groups = rp1_pmx_get_function_groups, + .set_mux = rp1_pmx_set, + .gpio_disable_free = rp1_pmx_gpio_disable_free, + .gpio_set_direction = rp1_pmx_gpio_set_direction, +}; + +static void rp1_pull_config_set(struct rp1_pin_info *pin, unsigned int arg) +{ + regmap_field_write(pin->pad[RP1_PAD_PULL], arg & 0x3); +} + +static int rp1_pinconf_set(struct pinctrl_dev *pctldev, unsigned int offset, + unsigned long *configs, unsigned int num_configs) +{ + struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset); + u32 param, arg; + int i; + + if (!pin) + return -EINVAL; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + rp1_pull_config_set(pin, RP1_PUD_OFF); + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + rp1_pull_config_set(pin, RP1_PUD_DOWN); + break; + + case PIN_CONFIG_BIAS_PULL_UP: + rp1_pull_config_set(pin, RP1_PUD_UP); + break; + + case PIN_CONFIG_INPUT_ENABLE: + rp1_input_enable(pin, arg); + break; + + case PIN_CONFIG_OUTPUT_ENABLE: + rp1_output_enable(pin, arg); + break; + + case PIN_CONFIG_OUTPUT: + rp1_set_value(pin, arg); + rp1_set_dir(pin, RP1_DIR_OUTPUT); + rp1_set_fsel(pin, RP1_FSEL_GPIO); + break; + + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + regmap_field_write(pin->pad[RP1_PAD_SCHMITT], !!arg); + break; + + case PIN_CONFIG_SLEW_RATE: + regmap_field_write(pin->pad[RP1_PAD_SLEWFAST], !!arg); + break; + + case PIN_CONFIG_DRIVE_STRENGTH: + switch (arg) { + case 2: + arg = RP1_PAD_DRIVE_2MA; + break; + case 4: + arg = RP1_PAD_DRIVE_4MA; + break; + case 8: + arg = RP1_PAD_DRIVE_8MA; + break; + case 12: + arg = RP1_PAD_DRIVE_12MA; + break; + default: + return -ENOTSUPP; + } + regmap_field_write(pin->pad[RP1_PAD_DRIVE], arg); + break; + + default: + return -ENOTSUPP; + + } /* switch param type */ + } /* for each config */ + + return 0; +} + +static int rp1_pinconf_get(struct pinctrl_dev *pctldev, unsigned int offset, + unsigned long *config) +{ + struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset); + enum pin_config_param param = pinconf_to_config_param(*config); + u32 padctrl; + u32 arg; + + if (!pin) + return -EINVAL; + + switch (param) { + case PIN_CONFIG_INPUT_ENABLE: + regmap_field_read(pin->pad[RP1_PAD_IN_ENABLE], &padctrl); + arg = !!padctrl; + break; + case PIN_CONFIG_OUTPUT_ENABLE: + regmap_field_read(pin->pad[RP1_PAD_OUT_DISABLE], &padctrl); + arg = !padctrl; + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + regmap_field_read(pin->pad[RP1_PAD_SCHMITT], &padctrl); + arg = !!padctrl; + break; + case PIN_CONFIG_SLEW_RATE: + regmap_field_read(pin->pad[RP1_PAD_SLEWFAST], &padctrl); + arg = !!padctrl; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + regmap_field_read(pin->pad[RP1_PAD_DRIVE], &padctrl); + switch (padctrl) { + case RP1_PAD_DRIVE_2MA: + arg = 2; + break; + case RP1_PAD_DRIVE_4MA: + arg = 4; + break; + case RP1_PAD_DRIVE_8MA: + arg = 8; + break; + case RP1_PAD_DRIVE_12MA: + arg = 12; + break; + } + break; + case PIN_CONFIG_BIAS_DISABLE: + regmap_field_read(pin->pad[RP1_PAD_PULL], &padctrl); + arg = ((padctrl == RP1_PUD_OFF)); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + regmap_field_read(pin->pad[RP1_PAD_PULL], &padctrl); + arg = ((padctrl == RP1_PUD_DOWN)); + break; + + case PIN_CONFIG_BIAS_PULL_UP: + regmap_field_read(pin->pad[RP1_PAD_PULL], &padctrl); + arg = ((padctrl == RP1_PUD_UP)); + break; + default: + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int rp1_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned int selector, + unsigned long *config) +{ + const unsigned int *pins; + unsigned int npins; + int ret; + + ret = rp1_pctl_get_group_pins(pctldev, selector, &pins, &npins); + if (ret < 0) + return ret; + + if (!npins) + return -ENODEV; + + ret = rp1_pinconf_get(pctldev, pins[0], config); + + return ret; +} + +static int rp1_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned int selector, + unsigned long *configs, unsigned int num_configs) +{ + const unsigned int *pins; + unsigned int npins; + int ret, i; + + ret = rp1_pctl_get_group_pins(pctldev, selector, &pins, &npins); + if (ret < 0) + return ret; + + for (i = 0; i < npins; i++) { + ret = rp1_pinconf_set(pctldev, pins[i], configs, num_configs); + if (ret < 0) + return ret; + } + + return 0; +} + +static const struct pinconf_ops rp1_pinconf_ops = { + .is_generic = true, + .pin_config_get = rp1_pinconf_get, + .pin_config_set = rp1_pinconf_set, + .pin_config_group_get = rp1_pinconf_group_get, + .pin_config_group_set = rp1_pinconf_group_set, +}; + +static struct pinctrl_desc rp1_pinctrl_desc = { + .name = MODULE_NAME, + .pins = rp1_gpio_pins, + .npins = ARRAY_SIZE(rp1_gpio_pins), + .pctlops = &rp1_pctl_ops, + .pmxops = &rp1_pmx_ops, + .confops = &rp1_pinconf_ops, + .owner = THIS_MODULE, +}; + +static struct pinctrl_gpio_range rp1_pinctrl_gpio_range = { + .name = MODULE_NAME, + .npins = RP1_NUM_GPIOS, +}; + +static const struct of_device_id rp1_pinctrl_match[] = { + { + .compatible = "raspberrypi,rp1-gpio", + .data = &rp1_pinconf_ops, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, rp1_pinctrl_match); + +static struct rp1_pinctrl rp1_pinctrl_data = {}; + +static const struct regmap_config rp1_pinctrl_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .fast_io = true, + .name = "rp1-pinctrl", +}; + +static int rp1_gen_regfield(struct device *dev, + const struct reg_field *array, + size_t array_size, + int reg_off, + int pin_off, + bool additive_offset, + struct regmap *regmap, + struct regmap_field *out[]) +{ + struct reg_field regfield; + int k; + + for (k = 0; k < array_size; k++) { + regfield = array[k]; + regfield.reg = (additive_offset ? regfield.reg : 0) + reg_off; + if (pin_off >= 0) { + regfield.lsb = pin_off; + regfield.msb = regfield.lsb; + } + out[k] = devm_regmap_field_alloc(dev, regmap, regfield); + + if (IS_ERR(out[k])) + return PTR_ERR(out[k]); + } + + return 0; +} + +static int rp1_pinctrl_probe(struct platform_device *pdev) +{ + struct regmap *gpio_regmap, *rio_regmap, *pads_regmap; + struct rp1_pinctrl *pc = &rp1_pinctrl_data; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct gpio_irq_chip *girq; + int err, i; + + pc->dev = dev; + pc->gpio_chip = rp1_gpio_chip; + pc->gpio_chip.parent = dev; + + pc->gpio_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pc->gpio_base)) + return dev_err_probe(dev, PTR_ERR(pc->gpio_base), "could not get GPIO IO memory\n"); + + pc->rio_base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(pc->rio_base)) + return dev_err_probe(dev, PTR_ERR(pc->rio_base), "could not get RIO IO memory\n"); + + pc->pads_base = devm_platform_ioremap_resource(pdev, 2); + if (IS_ERR(pc->pads_base)) + return dev_err_probe(dev, PTR_ERR(pc->pads_base), "could not get PADS IO memory\n"); + + gpio_regmap = devm_regmap_init_mmio(dev, pc->gpio_base, + &rp1_pinctrl_regmap_cfg); + if (IS_ERR(gpio_regmap)) + return dev_err_probe(dev, PTR_ERR(gpio_regmap), "could not init GPIO regmap\n"); + + rio_regmap = devm_regmap_init_mmio(dev, pc->rio_base, + &rp1_pinctrl_regmap_cfg); + if (IS_ERR(rio_regmap)) + return dev_err_probe(dev, PTR_ERR(rio_regmap), "could not init RIO regmap\n"); + + pads_regmap = devm_regmap_init_mmio(dev, pc->pads_base, + &rp1_pinctrl_regmap_cfg); + if (IS_ERR(pads_regmap)) + return dev_err_probe(dev, PTR_ERR(pads_regmap), "could not init PADS regmap\n"); + + for (i = 0; i < RP1_NUM_BANKS; i++) { + const struct rp1_iobank_desc *bank = &rp1_iobanks[i]; + int j; + + for (j = 0; j < bank->num_gpios; j++) { + struct rp1_pin_info *pin = + &pc->pins[bank->min_gpio + j]; + int reg_off; + + pin->num = bank->min_gpio + j; + pin->bank = i; + pin->offset = j; + + reg_off = bank->gpio_offset + pin->offset * + sizeof(u32) * 2; + err = rp1_gen_regfield(dev, + rp1_gpio_fields, + ARRAY_SIZE(rp1_gpio_fields), + reg_off, + -1, + true, + gpio_regmap, + pin->gpio); + + if (err) + return dev_err_probe(dev, err, + "Unable to allocate regmap for gpio\n"); + + reg_off = bank->inte_offset; + err = rp1_gen_regfield(dev, + rp1_inte_fields, + ARRAY_SIZE(rp1_inte_fields), + reg_off, + pin->offset, + true, + gpio_regmap, + pin->inte); + + if (err) + return dev_err_probe(dev, err, + "Unable to allocate regmap for inte\n"); + + reg_off = bank->rio_offset; + err = rp1_gen_regfield(dev, + rp1_rio_fields, + ARRAY_SIZE(rp1_rio_fields), + reg_off, + pin->offset, + true, + rio_regmap, + pin->rio); + + if (err) + return dev_err_probe(dev, err, + "Unable to allocate regmap for rio\n"); + + reg_off = bank->pads_offset + pin->offset * sizeof(u32); + err = rp1_gen_regfield(dev, + rp1_pad_fields, + ARRAY_SIZE(rp1_pad_fields), + reg_off, + -1, + false, + pads_regmap, + pin->pad); + + if (err) + return dev_err_probe(dev, err, + "Unable to allocate regmap for pad\n"); + } + + raw_spin_lock_init(&pc->irq_lock[i]); + } + + pc->pctl_dev = devm_pinctrl_register(dev, &rp1_pinctrl_desc, pc); + if (IS_ERR(pc->pctl_dev)) + return dev_err_probe(dev, PTR_ERR(pc->pctl_dev), + "Could not register pin controller\n"); + + girq = &pc->gpio_chip.irq; + girq->chip = &rp1_gpio_irq_chip; + girq->parent_handler = rp1_gpio_irq_handler; + girq->num_parents = RP1_NUM_BANKS; + girq->parents = pc->irq; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_level_irq; + + /* + * Use the same handler for all groups: this is necessary + * since we use one gpiochip to cover all lines - the + * irq handler then needs to figure out which group and + * bank that was firing the IRQ and look up the per-group + * and bank data. + */ + for (i = 0; i < RP1_NUM_BANKS; i++) { + pc->irq[i] = irq_of_parse_and_map(np, i); + if (!pc->irq[i]) { + girq->num_parents = i; + break; + } + } + + platform_set_drvdata(pdev, pc); + + err = devm_gpiochip_add_data(dev, &pc->gpio_chip, pc); + if (err) + return dev_err_probe(dev, err, "could not add GPIO chip\n"); + + pc->gpio_range = rp1_pinctrl_gpio_range; + pc->gpio_range.base = pc->gpio_chip.base; + pc->gpio_range.gc = &pc->gpio_chip; + pinctrl_add_gpio_range(pc->pctl_dev, &pc->gpio_range); + + return 0; +} + +static struct platform_driver rp1_pinctrl_driver = { + .probe = rp1_pinctrl_probe, + .driver = { + .name = MODULE_NAME, + .of_match_table = rp1_pinctrl_match, + .suppress_bind_attrs = true, + }, +}; +module_platform_driver(rp1_pinctrl_driver); + +MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>"); +MODULE_AUTHOR("Andrea della Porta <andrea.porta@suse.com>"); +MODULE_DESCRIPTION("RP1 pinctrl/gpio driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index d85be5899da6..ba4bb07309a1 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -51,8 +51,8 @@ config RESET_BERLIN config RESET_BRCMSTB tristate "Broadcom STB reset controller" - depends on ARCH_BRCMSTB || COMPILE_TEST - default ARCH_BRCMSTB + depends on ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST + default ARCH_BRCMSTB || ARCH_BCM2835 help This enables the reset controller driver for Broadcom STB SoCs using a SUN_TOP_CTRL_SW_INIT style controller. @@ -60,11 +60,11 @@ config RESET_BRCMSTB config RESET_BRCMSTB_RESCAL tristate "Broadcom STB RESCAL reset controller" depends on HAS_IOMEM - depends on ARCH_BRCMSTB || COMPILE_TEST - default ARCH_BRCMSTB + depends on ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST + default ARCH_BRCMSTB || ARCH_BCM2835 help This enables the RESCAL reset controller for SATA, PCIe0, or PCIe1 on - BCM7216. + BCM7216 or the BCM2712. config RESET_EYEQ bool "Mobileye EyeQ reset controller" @@ -140,6 +140,15 @@ config RESET_K210 Say Y if you want to control reset signals provided by this controller. +config RESET_K230 + tristate "Reset controller driver for Canaan Kendryte K230 SoC" + depends on ARCH_CANAAN || COMPILE_TEST + depends on OF + help + Support for the Canaan Kendryte K230 RISC-V SoC reset controller. + Say Y if you want to control reset signals provided by this + controller. + config RESET_LANTIQ bool "Lantiq XWAY Reset Driver" if COMPILE_TEST default SOC_TYPE_XWAY @@ -287,7 +296,7 @@ config RESET_SUNXI This enables the reset driver for Allwinner SoCs. config RESET_TH1520 - tristate "T-HEAD 1520 reset controller" + tristate "T-HEAD TH1520 reset controller" depends on ARCH_THEAD || COMPILE_TEST select REGMAP_MMIO help diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 91e6348e3351..82488dac0f35 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_RESET_IMX7) += reset-imx7.o obj-$(CONFIG_RESET_IMX8MP_AUDIOMIX) += reset-imx8mp-audiomix.o obj-$(CONFIG_RESET_INTEL_GW) += reset-intel-gw.o obj-$(CONFIG_RESET_K210) += reset-k210.o +obj-$(CONFIG_RESET_K230) += reset-k230.o obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o obj-$(CONFIG_RESET_MCHP_SPARX5) += reset-microchip-sparx5.o diff --git a/drivers/reset/reset-k230.c b/drivers/reset/reset-k230.c new file mode 100644 index 000000000000..c81045bb4a14 --- /dev/null +++ b/drivers/reset/reset-k230.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2022-2024 Canaan Bright Sight Co., Ltd + * Copyright (C) 2024-2025 Junhui Liu <junhui.liu@pigmoral.tech> + * + * The reset management module in the K230 SoC provides reset time control + * registers. For RST_TYPE_CPU0, RST_TYPE_CPU1 and RST_TYPE_SW_DONE, the period + * during which reset is applied or removed while the clock is stopped can be + * set up to 15 * 0.25 = 3.75 µs. For RST_TYPE_HW_DONE, that period can be set + * up to 255 * 0.25 = 63.75 µs. For RST_TYPE_FLUSH, the reset bit is + * automatically cleared by hardware when flush completes. + * + * Although this driver does not configure the reset time registers, delays have + * been added to the assert, deassert, and reset operations to cover the maximum + * reset time. Some reset types include done bits whose toggle does not + * unambiguously signal whether hardware reset removal or clock-stop period + * expiration occurred first. Delays are therefore retained for types with done + * bits to ensure safe timing. + * + * Reference: K230 Technical Reference Manual V0.3.1 + * https://kendryte-download.canaan-creative.com/developer/k230/HDK/K230%E7%A1%AC%E4%BB%B6%E6%96%87%E6%A1%A3/K230_Technical_Reference_Manual_V0.3.1_20241118.pdf + */ + +#include <linux/cleanup.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reset-controller.h> +#include <linux/spinlock.h> + +#include <dt-bindings/reset/canaan,k230-rst.h> + +/** + * enum k230_rst_type - K230 reset types + * @RST_TYPE_CPU0: Reset type for CPU0 + * Automatically clears, has write enable and done bit, active high + * @RST_TYPE_CPU1: Reset type for CPU1 + * Manually clears, has write enable and done bit, active high + * @RST_TYPE_FLUSH: Reset type for CPU L2 cache flush + * Automatically clears, has write enable, no done bit, active high + * @RST_TYPE_HW_DONE: Reset type for hardware auto clear + * Automatically clears, no write enable, has done bit, active high + * @RST_TYPE_SW_DONE: Reset type for software manual clear + * Manually clears, no write enable and done bit, + * active high if ID is RST_SPI2AXI, otherwise active low + */ +enum k230_rst_type { + RST_TYPE_CPU0, + RST_TYPE_CPU1, + RST_TYPE_FLUSH, + RST_TYPE_HW_DONE, + RST_TYPE_SW_DONE, +}; + +struct k230_rst_map { + u32 offset; + enum k230_rst_type type; + u32 done; + u32 reset; +}; + +struct k230_rst { + struct reset_controller_dev rcdev; + void __iomem *base; + /* protect register read-modify-write */ + spinlock_t lock; +}; + +static const struct k230_rst_map k230_resets[] = { + [RST_CPU0] = { 0x4, RST_TYPE_CPU0, BIT(12), BIT(0) }, + [RST_CPU1] = { 0xc, RST_TYPE_CPU1, BIT(12), BIT(0) }, + [RST_CPU0_FLUSH] = { 0x4, RST_TYPE_FLUSH, 0, BIT(4) }, + [RST_CPU1_FLUSH] = { 0xc, RST_TYPE_FLUSH, 0, BIT(4) }, + [RST_AI] = { 0x14, RST_TYPE_HW_DONE, BIT(31), BIT(0) }, + [RST_VPU] = { 0x1c, RST_TYPE_HW_DONE, BIT(31), BIT(0) }, + [RST_HISYS] = { 0x2c, RST_TYPE_HW_DONE, BIT(4), BIT(0) }, + [RST_HISYS_AHB] = { 0x2c, RST_TYPE_HW_DONE, BIT(5), BIT(1) }, + [RST_SDIO0] = { 0x34, RST_TYPE_HW_DONE, BIT(28), BIT(0) }, + [RST_SDIO1] = { 0x34, RST_TYPE_HW_DONE, BIT(29), BIT(1) }, + [RST_SDIO_AXI] = { 0x34, RST_TYPE_HW_DONE, BIT(30), BIT(2) }, + [RST_USB0] = { 0x3c, RST_TYPE_HW_DONE, BIT(28), BIT(0) }, + [RST_USB1] = { 0x3c, RST_TYPE_HW_DONE, BIT(29), BIT(1) }, + [RST_USB0_AHB] = { 0x3c, RST_TYPE_HW_DONE, BIT(30), BIT(0) }, + [RST_USB1_AHB] = { 0x3c, RST_TYPE_HW_DONE, BIT(31), BIT(1) }, + [RST_SPI0] = { 0x44, RST_TYPE_HW_DONE, BIT(28), BIT(0) }, + [RST_SPI1] = { 0x44, RST_TYPE_HW_DONE, BIT(29), BIT(1) }, + [RST_SPI2] = { 0x44, RST_TYPE_HW_DONE, BIT(30), BIT(2) }, + [RST_SEC] = { 0x4c, RST_TYPE_HW_DONE, BIT(31), BIT(0) }, + [RST_PDMA] = { 0x54, RST_TYPE_HW_DONE, BIT(28), BIT(0) }, + [RST_SDMA] = { 0x54, RST_TYPE_HW_DONE, BIT(29), BIT(1) }, + [RST_DECOMPRESS] = { 0x5c, RST_TYPE_HW_DONE, BIT(31), BIT(0) }, + [RST_SRAM] = { 0x64, RST_TYPE_HW_DONE, BIT(28), BIT(0) }, + [RST_SHRM_AXIM] = { 0x64, RST_TYPE_HW_DONE, BIT(30), BIT(2) }, + [RST_SHRM_AXIS] = { 0x64, RST_TYPE_HW_DONE, BIT(31), BIT(3) }, + [RST_NONAI2D] = { 0x6c, RST_TYPE_HW_DONE, BIT(31), BIT(0) }, + [RST_MCTL] = { 0x74, RST_TYPE_HW_DONE, BIT(31), BIT(0) }, + [RST_ISP] = { 0x80, RST_TYPE_HW_DONE, BIT(29), BIT(6) }, + [RST_ISP_DW] = { 0x80, RST_TYPE_HW_DONE, BIT(28), BIT(5) }, + [RST_DPU] = { 0x88, RST_TYPE_HW_DONE, BIT(31), BIT(0) }, + [RST_DISP] = { 0x90, RST_TYPE_HW_DONE, BIT(31), BIT(0) }, + [RST_GPU] = { 0x98, RST_TYPE_HW_DONE, BIT(31), BIT(0) }, + [RST_AUDIO] = { 0xa4, RST_TYPE_HW_DONE, BIT(31), BIT(0) }, + [RST_TIMER0] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(0) }, + [RST_TIMER1] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(1) }, + [RST_TIMER2] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(2) }, + [RST_TIMER3] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(3) }, + [RST_TIMER4] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(4) }, + [RST_TIMER5] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(5) }, + [RST_TIMER_APB] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(6) }, + [RST_HDI] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(7) }, + [RST_WDT0] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(12) }, + [RST_WDT1] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(13) }, + [RST_WDT0_APB] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(14) }, + [RST_WDT1_APB] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(15) }, + [RST_TS_APB] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(16) }, + [RST_MAILBOX] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(17) }, + [RST_STC] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(18) }, + [RST_PMU] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(19) }, + [RST_LOSYS_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(0) }, + [RST_UART0] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(1) }, + [RST_UART1] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(2) }, + [RST_UART2] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(3) }, + [RST_UART3] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(4) }, + [RST_UART4] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(5) }, + [RST_I2C0] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(6) }, + [RST_I2C1] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(7) }, + [RST_I2C2] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(8) }, + [RST_I2C3] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(9) }, + [RST_I2C4] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(10) }, + [RST_JAMLINK0_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(11) }, + [RST_JAMLINK1_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(12) }, + [RST_JAMLINK2_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(13) }, + [RST_JAMLINK3_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(14) }, + [RST_CODEC_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(17) }, + [RST_GPIO_DB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(18) }, + [RST_GPIO_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(19) }, + [RST_ADC] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(20) }, + [RST_ADC_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(21) }, + [RST_PWM_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(22) }, + [RST_SHRM_APB] = { 0x64, RST_TYPE_SW_DONE, 0, BIT(1) }, + [RST_CSI0] = { 0x80, RST_TYPE_SW_DONE, 0, BIT(0) }, + [RST_CSI1] = { 0x80, RST_TYPE_SW_DONE, 0, BIT(1) }, + [RST_CSI2] = { 0x80, RST_TYPE_SW_DONE, 0, BIT(2) }, + [RST_CSI_DPHY] = { 0x80, RST_TYPE_SW_DONE, 0, BIT(3) }, + [RST_ISP_AHB] = { 0x80, RST_TYPE_SW_DONE, 0, BIT(4) }, + [RST_M0] = { 0x80, RST_TYPE_SW_DONE, 0, BIT(7) }, + [RST_M1] = { 0x80, RST_TYPE_SW_DONE, 0, BIT(8) }, + [RST_M2] = { 0x80, RST_TYPE_SW_DONE, 0, BIT(9) }, + [RST_SPI2AXI] = { 0xa8, RST_TYPE_SW_DONE, 0, BIT(0) } +}; + +static inline struct k230_rst *to_k230_rst(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct k230_rst, rcdev); +} + +static void k230_rst_clear_done(struct k230_rst *rstc, unsigned long id, + bool write_en) +{ + const struct k230_rst_map *rmap = &k230_resets[id]; + u32 reg; + + guard(spinlock_irqsave)(&rstc->lock); + + reg = readl(rstc->base + rmap->offset); + reg |= rmap->done; /* write 1 to clear */ + if (write_en) + reg |= rmap->done << 16; + writel(reg, rstc->base + rmap->offset); +} + +static int k230_rst_wait_and_clear_done(struct k230_rst *rstc, unsigned long id, + bool write_en) +{ + const struct k230_rst_map *rmap = &k230_resets[id]; + u32 reg; + int ret; + + ret = readl_poll_timeout(rstc->base + rmap->offset, reg, + reg & rmap->done, 10, 1000); + if (ret) { + dev_err(rstc->rcdev.dev, "Wait for reset done timeout\n"); + return ret; + } + + k230_rst_clear_done(rstc, id, write_en); + + return 0; +} + +static void k230_rst_update(struct k230_rst *rstc, unsigned long id, + bool assert, bool write_en, bool active_low) +{ + const struct k230_rst_map *rmap = &k230_resets[id]; + u32 reg; + + guard(spinlock_irqsave)(&rstc->lock); + + reg = readl(rstc->base + rmap->offset); + if (assert ^ active_low) + reg |= rmap->reset; + else + reg &= ~rmap->reset; + if (write_en) + reg |= rmap->reset << 16; + writel(reg, rstc->base + rmap->offset); +} + +static int k230_rst_assert(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct k230_rst *rstc = to_k230_rst(rcdev); + + switch (k230_resets[id].type) { + case RST_TYPE_CPU1: + k230_rst_update(rstc, id, true, true, false); + break; + case RST_TYPE_SW_DONE: + k230_rst_update(rstc, id, true, false, + id == RST_SPI2AXI ? false : true); + break; + case RST_TYPE_CPU0: + case RST_TYPE_FLUSH: + case RST_TYPE_HW_DONE: + return -EOPNOTSUPP; + } + + /* + * The time period when reset is applied but the clock is stopped for + * RST_TYPE_CPU1 and RST_TYPE_SW_DONE can be set up to 3.75us. Delay + * 10us to ensure proper reset timing. + */ + udelay(10); + + return 0; +} + +static int k230_rst_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct k230_rst *rstc = to_k230_rst(rcdev); + int ret = 0; + + switch (k230_resets[id].type) { + case RST_TYPE_CPU1: + k230_rst_update(rstc, id, false, true, false); + ret = k230_rst_wait_and_clear_done(rstc, id, true); + break; + case RST_TYPE_SW_DONE: + k230_rst_update(rstc, id, false, false, + id == RST_SPI2AXI ? false : true); + break; + case RST_TYPE_CPU0: + case RST_TYPE_FLUSH: + case RST_TYPE_HW_DONE: + return -EOPNOTSUPP; + } + + /* + * The time period when reset is removed but the clock is stopped for + * RST_TYPE_CPU1 and RST_TYPE_SW_DONE can be set up to 3.75us. Delay + * 10us to ensure proper reset timing. + */ + udelay(10); + + return ret; +} + +static int k230_rst_reset(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct k230_rst *rstc = to_k230_rst(rcdev); + const struct k230_rst_map *rmap = &k230_resets[id]; + u32 reg; + int ret = 0; + + switch (rmap->type) { + case RST_TYPE_CPU0: + k230_rst_clear_done(rstc, id, true); + k230_rst_update(rstc, id, true, true, false); + ret = k230_rst_wait_and_clear_done(rstc, id, true); + + /* + * The time period when reset is applied and removed but the + * clock is stopped for RST_TYPE_CPU0 can be set up to 7.5us. + * Delay 10us to ensure proper reset timing. + */ + udelay(10); + + break; + case RST_TYPE_FLUSH: + k230_rst_update(rstc, id, true, true, false); + + /* Wait flush request bit auto cleared by hardware */ + ret = readl_poll_timeout(rstc->base + rmap->offset, reg, + !(reg & rmap->reset), 10, 1000); + if (ret) + dev_err(rcdev->dev, "Wait for flush done timeout\n"); + + break; + case RST_TYPE_HW_DONE: + k230_rst_clear_done(rstc, id, false); + k230_rst_update(rstc, id, true, false, false); + ret = k230_rst_wait_and_clear_done(rstc, id, false); + + /* + * The time period when reset is applied and removed but the + * clock is stopped for RST_TYPE_HW_DONE can be set up to + * 127.5us. Delay 200us to ensure proper reset timing. + */ + fsleep(200); + + break; + case RST_TYPE_CPU1: + case RST_TYPE_SW_DONE: + k230_rst_assert(rcdev, id); + ret = k230_rst_deassert(rcdev, id); + break; + } + + return ret; +} + +static const struct reset_control_ops k230_rst_ops = { + .reset = k230_rst_reset, + .assert = k230_rst_assert, + .deassert = k230_rst_deassert, +}; + +static int k230_rst_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct k230_rst *rstc; + + rstc = devm_kzalloc(dev, sizeof(*rstc), GFP_KERNEL); + if (!rstc) + return -ENOMEM; + + rstc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rstc->base)) + return PTR_ERR(rstc->base); + + spin_lock_init(&rstc->lock); + + rstc->rcdev.dev = dev; + rstc->rcdev.owner = THIS_MODULE; + rstc->rcdev.ops = &k230_rst_ops; + rstc->rcdev.nr_resets = ARRAY_SIZE(k230_resets); + rstc->rcdev.of_node = dev->of_node; + + return devm_reset_controller_register(dev, &rstc->rcdev); +} + +static const struct of_device_id k230_rst_match[] = { + { .compatible = "canaan,k230-rst", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, k230_rst_match); + +static struct platform_driver k230_rst_driver = { + .probe = k230_rst_probe, + .driver = { + .name = "k230-rst", + .of_match_table = k230_rst_match, + } +}; +module_platform_driver(k230_rst_driver); + +MODULE_AUTHOR("Junhui Liu <junhui.liu@pigmoral.tech>"); +MODULE_DESCRIPTION("Canaan K230 reset driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/reset/reset-mpfs.c b/drivers/reset/reset-mpfs.c index 574e59db83a4..f6fa10e03ea8 100644 --- a/drivers/reset/reset-mpfs.c +++ b/drivers/reset/reset-mpfs.c @@ -155,62 +155,16 @@ static int mpfs_reset_probe(struct auxiliary_device *adev, return devm_reset_controller_register(dev, rcdev); } -static void mpfs_reset_unregister_adev(void *_adev) -{ - struct auxiliary_device *adev = _adev; - - auxiliary_device_delete(adev); - auxiliary_device_uninit(adev); -} - -static void mpfs_reset_adev_release(struct device *dev) -{ - struct auxiliary_device *adev = to_auxiliary_dev(dev); - - kfree(adev); -} - -static struct auxiliary_device *mpfs_reset_adev_alloc(struct device *clk_dev) -{ - struct auxiliary_device *adev; - int ret; - - adev = kzalloc(sizeof(*adev), GFP_KERNEL); - if (!adev) - return ERR_PTR(-ENOMEM); - - adev->name = "reset-mpfs"; - adev->dev.parent = clk_dev; - adev->dev.release = mpfs_reset_adev_release; - adev->id = 666u; - - ret = auxiliary_device_init(adev); - if (ret) { - kfree(adev); - return ERR_PTR(ret); - } - - return adev; -} - int mpfs_reset_controller_register(struct device *clk_dev, void __iomem *base) { struct auxiliary_device *adev; - int ret; - adev = mpfs_reset_adev_alloc(clk_dev); - if (IS_ERR(adev)) - return PTR_ERR(adev); - - ret = auxiliary_device_add(adev); - if (ret) { - auxiliary_device_uninit(adev); - return ret; - } - - adev->dev.platform_data = (__force void *)base; + adev = devm_auxiliary_device_create(clk_dev, "reset-mpfs", + (__force void *)base); + if (!adev) + return -ENODEV; - return devm_add_action_or_reset(clk_dev, mpfs_reset_unregister_adev, adev); + return 0; } EXPORT_SYMBOL_NS_GPL(mpfs_reset_controller_register, "MCHP_CLK_MPFS"); diff --git a/drivers/reset/reset-simple.c b/drivers/reset/reset-simple.c index 276067839830..79e94ecfe4f5 100644 --- a/drivers/reset/reset-simple.c +++ b/drivers/reset/reset-simple.c @@ -151,6 +151,8 @@ static const struct of_device_id reset_simple_dt_ids[] = { { .compatible = "snps,dw-high-reset" }, { .compatible = "snps,dw-low-reset", .data = &reset_simple_active_low }, + { .compatible = "sophgo,cv1800b-reset", + .data = &reset_simple_active_low }, { .compatible = "sophgo,sg2042-reset", .data = &reset_simple_active_low }, { /* sentinel */ }, diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c index fc3a2c41cc10..b03310c0830d 100644 --- a/drivers/soc/aspeed/aspeed-lpc-snoop.c +++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c @@ -12,6 +12,7 @@ #include <linux/bitops.h> #include <linux/clk.h> +#include <linux/dev_printk.h> #include <linux/interrupt.h> #include <linux/fs.h> #include <linux/kfifo.h> @@ -25,7 +26,6 @@ #define DEVICE_NAME "aspeed-lpc-snoop" -#define NUM_SNOOP_CHANNELS 2 #define SNOOP_FIFO_SIZE 2048 #define HICR5 0x80 @@ -57,7 +57,22 @@ struct aspeed_lpc_snoop_model_data { unsigned int has_hicrb_ensnp; }; +enum aspeed_lpc_snoop_index { + ASPEED_LPC_SNOOP_INDEX_0 = 0, + ASPEED_LPC_SNOOP_INDEX_1 = 1, + ASPEED_LPC_SNOOP_INDEX_MAX = ASPEED_LPC_SNOOP_INDEX_1, +}; + +struct aspeed_lpc_snoop_channel_cfg { + enum aspeed_lpc_snoop_index index; + u32 hicr5_en; + u32 snpwadr_mask; + u32 snpwadr_shift; + u32 hicrb_en; +}; + struct aspeed_lpc_snoop_channel { + const struct aspeed_lpc_snoop_channel_cfg *cfg; bool enabled; struct kfifo fifo; wait_queue_head_t wq; @@ -68,7 +83,24 @@ struct aspeed_lpc_snoop { struct regmap *regmap; int irq; struct clk *clk; - struct aspeed_lpc_snoop_channel chan[NUM_SNOOP_CHANNELS]; + struct aspeed_lpc_snoop_channel chan[ASPEED_LPC_SNOOP_INDEX_MAX + 1]; +}; + +static const struct aspeed_lpc_snoop_channel_cfg channel_cfgs[ASPEED_LPC_SNOOP_INDEX_MAX + 1] = { + { + .index = ASPEED_LPC_SNOOP_INDEX_0, + .hicr5_en = HICR5_EN_SNP0W | HICR5_ENINT_SNP0W, + .snpwadr_mask = SNPWADR_CH0_MASK, + .snpwadr_shift = SNPWADR_CH0_SHIFT, + .hicrb_en = HICRB_ENSNP0D, + }, + { + .index = ASPEED_LPC_SNOOP_INDEX_1, + .hicr5_en = HICR5_EN_SNP1W | HICR5_ENINT_SNP1W, + .snpwadr_mask = SNPWADR_CH1_MASK, + .snpwadr_shift = SNPWADR_CH1_SHIFT, + .hicrb_en = HICRB_ENSNP1D, + }, }; static struct aspeed_lpc_snoop_channel *snoop_file_to_chan(struct file *file) @@ -182,108 +214,88 @@ static int aspeed_lpc_snoop_config_irq(struct aspeed_lpc_snoop *lpc_snoop, return 0; } -static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop, - struct device *dev, - int channel, u16 lpc_port) +__attribute__((nonnull)) +static int aspeed_lpc_enable_snoop(struct device *dev, + struct aspeed_lpc_snoop *lpc_snoop, + struct aspeed_lpc_snoop_channel *channel, + const struct aspeed_lpc_snoop_channel_cfg *cfg, + u16 lpc_port) { + const struct aspeed_lpc_snoop_model_data *model_data; int rc = 0; - u32 hicr5_en, snpwadr_mask, snpwadr_shift, hicrb_en; - const struct aspeed_lpc_snoop_model_data *model_data = - of_device_get_match_data(dev); - if (WARN_ON(lpc_snoop->chan[channel].enabled)) + if (WARN_ON(channel->enabled)) return -EBUSY; - init_waitqueue_head(&lpc_snoop->chan[channel].wq); - /* Create FIFO datastructure */ - rc = kfifo_alloc(&lpc_snoop->chan[channel].fifo, - SNOOP_FIFO_SIZE, GFP_KERNEL); + init_waitqueue_head(&channel->wq); + + channel->cfg = cfg; + channel->miscdev.minor = MISC_DYNAMIC_MINOR; + channel->miscdev.fops = &snoop_fops; + channel->miscdev.parent = dev; + + channel->miscdev.name = + devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, cfg->index); + if (!channel->miscdev.name) + return -ENOMEM; + + rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL); if (rc) return rc; - lpc_snoop->chan[channel].miscdev.minor = MISC_DYNAMIC_MINOR; - lpc_snoop->chan[channel].miscdev.name = - devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel); - if (!lpc_snoop->chan[channel].miscdev.name) { - rc = -ENOMEM; - goto err_free_fifo; - } - lpc_snoop->chan[channel].miscdev.fops = &snoop_fops; - lpc_snoop->chan[channel].miscdev.parent = dev; - rc = misc_register(&lpc_snoop->chan[channel].miscdev); + rc = misc_register(&channel->miscdev); if (rc) goto err_free_fifo; /* Enable LPC snoop channel at requested port */ - switch (channel) { - case 0: - hicr5_en = HICR5_EN_SNP0W | HICR5_ENINT_SNP0W; - snpwadr_mask = SNPWADR_CH0_MASK; - snpwadr_shift = SNPWADR_CH0_SHIFT; - hicrb_en = HICRB_ENSNP0D; - break; - case 1: - hicr5_en = HICR5_EN_SNP1W | HICR5_ENINT_SNP1W; - snpwadr_mask = SNPWADR_CH1_MASK; - snpwadr_shift = SNPWADR_CH1_SHIFT; - hicrb_en = HICRB_ENSNP1D; - break; - default: - rc = -EINVAL; - goto err_misc_deregister; - } + regmap_set_bits(lpc_snoop->regmap, HICR5, cfg->hicr5_en); + regmap_update_bits(lpc_snoop->regmap, SNPWADR, cfg->snpwadr_mask, + lpc_port << cfg->snpwadr_shift); - regmap_update_bits(lpc_snoop->regmap, HICR5, hicr5_en, hicr5_en); - regmap_update_bits(lpc_snoop->regmap, SNPWADR, snpwadr_mask, - lpc_port << snpwadr_shift); - if (model_data->has_hicrb_ensnp) - regmap_update_bits(lpc_snoop->regmap, HICRB, - hicrb_en, hicrb_en); + model_data = of_device_get_match_data(dev); + if (model_data && model_data->has_hicrb_ensnp) + regmap_set_bits(lpc_snoop->regmap, HICRB, cfg->hicrb_en); - lpc_snoop->chan[channel].enabled = true; + channel->enabled = true; return 0; -err_misc_deregister: - misc_deregister(&lpc_snoop->chan[channel].miscdev); err_free_fifo: - kfifo_free(&lpc_snoop->chan[channel].fifo); + kfifo_free(&channel->fifo); return rc; } +__attribute__((nonnull)) static void aspeed_lpc_disable_snoop(struct aspeed_lpc_snoop *lpc_snoop, - int channel) + struct aspeed_lpc_snoop_channel *channel) { - if (!lpc_snoop->chan[channel].enabled) + if (!channel->enabled) return; - switch (channel) { - case 0: - regmap_update_bits(lpc_snoop->regmap, HICR5, - HICR5_EN_SNP0W | HICR5_ENINT_SNP0W, - 0); - break; - case 1: - regmap_update_bits(lpc_snoop->regmap, HICR5, - HICR5_EN_SNP1W | HICR5_ENINT_SNP1W, - 0); - break; - default: - return; - } + /* Disable interrupts along with the device */ + regmap_clear_bits(lpc_snoop->regmap, HICR5, channel->cfg->hicr5_en); - lpc_snoop->chan[channel].enabled = false; + channel->enabled = false; /* Consider improving safety wrt concurrent reader(s) */ - misc_deregister(&lpc_snoop->chan[channel].miscdev); - kfifo_free(&lpc_snoop->chan[channel].fifo); + misc_deregister(&channel->miscdev); + kfifo_free(&channel->fifo); +} + +static void aspeed_lpc_snoop_remove(struct platform_device *pdev) +{ + struct aspeed_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev); + + /* Disable both snoop channels */ + aspeed_lpc_disable_snoop(lpc_snoop, &lpc_snoop->chan[0]); + aspeed_lpc_disable_snoop(lpc_snoop, &lpc_snoop->chan[1]); } static int aspeed_lpc_snoop_probe(struct platform_device *pdev) { struct aspeed_lpc_snoop *lpc_snoop; - struct device *dev; struct device_node *np; - u32 port; + struct device *dev; + int idx; int rc; dev = &pdev->dev; @@ -301,67 +313,40 @@ static int aspeed_lpc_snoop_probe(struct platform_device *pdev) } lpc_snoop->regmap = syscon_node_to_regmap(np); - if (IS_ERR(lpc_snoop->regmap)) { - dev_err(dev, "Couldn't get regmap\n"); - return -ENODEV; - } + if (IS_ERR(lpc_snoop->regmap)) + return dev_err_probe(dev, PTR_ERR(lpc_snoop->regmap), "Couldn't get regmap\n"); dev_set_drvdata(&pdev->dev, lpc_snoop); - rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0, &port); - if (rc) { - dev_err(dev, "no snoop ports configured\n"); - return -ENODEV; - } - - lpc_snoop->clk = devm_clk_get(dev, NULL); - if (IS_ERR(lpc_snoop->clk)) { - rc = PTR_ERR(lpc_snoop->clk); - if (rc != -EPROBE_DEFER) - dev_err(dev, "couldn't get clock\n"); - return rc; - } - rc = clk_prepare_enable(lpc_snoop->clk); - if (rc) { - dev_err(dev, "couldn't enable clock\n"); - return rc; - } + lpc_snoop->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(lpc_snoop->clk)) + return dev_err_probe(dev, PTR_ERR(lpc_snoop->clk), "couldn't get clock"); rc = aspeed_lpc_snoop_config_irq(lpc_snoop, pdev); if (rc) - goto err; - - rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 0, port); - if (rc) - goto err; - - /* Configuration of 2nd snoop channel port is optional */ - if (of_property_read_u32_index(dev->of_node, "snoop-ports", - 1, &port) == 0) { - rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 1, port); - if (rc) { - aspeed_lpc_disable_snoop(lpc_snoop, 0); - goto err; - } - } + return rc; - return 0; + static_assert(ARRAY_SIZE(channel_cfgs) == ARRAY_SIZE(lpc_snoop->chan), + "Broken implementation assumption regarding cfg count"); + for (idx = ASPEED_LPC_SNOOP_INDEX_0; idx <= ASPEED_LPC_SNOOP_INDEX_MAX; idx++) { + u32 port; -err: - clk_disable_unprepare(lpc_snoop->clk); + rc = of_property_read_u32_index(dev->of_node, "snoop-ports", idx, &port); + if (rc) + break; - return rc; -} + rc = aspeed_lpc_enable_snoop(dev, lpc_snoop, &lpc_snoop->chan[idx], + &channel_cfgs[idx], port); + if (rc) + goto cleanup_channels; + } -static void aspeed_lpc_snoop_remove(struct platform_device *pdev) -{ - struct aspeed_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev); + return idx == ASPEED_LPC_SNOOP_INDEX_0 ? -ENODEV : 0; - /* Disable both snoop channels */ - aspeed_lpc_disable_snoop(lpc_snoop, 0); - aspeed_lpc_disable_snoop(lpc_snoop, 1); +cleanup_channels: + aspeed_lpc_snoop_remove(pdev); - clk_disable_unprepare(lpc_snoop->clk); + return rc; } static const struct aspeed_lpc_snoop_model_data ast2400_model_data = { diff --git a/drivers/soc/fsl/qe/gpio.c b/drivers/soc/fsl/qe/gpio.c index 3ef24ba0245b..710a3a03758b 100644 --- a/drivers/soc/fsl/qe/gpio.c +++ b/drivers/soc/fsl/qe/gpio.c @@ -57,7 +57,7 @@ static int qe_gpio_get(struct gpio_chip *gc, unsigned int gpio) return !!(ioread32be(®s->cpdata) & pin_mask); } -static void qe_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +static int qe_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) { struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); struct qe_gpio_chip *qe_gc = gpiochip_get_data(gc); @@ -75,10 +75,12 @@ static void qe_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) iowrite32be(qe_gc->cpdata, ®s->cpdata); spin_unlock_irqrestore(&qe_gc->lock, flags); + + return 0; } -static void qe_gpio_set_multiple(struct gpio_chip *gc, - unsigned long *mask, unsigned long *bits) +static int qe_gpio_set_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) { struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); struct qe_gpio_chip *qe_gc = gpiochip_get_data(gc); @@ -102,6 +104,8 @@ static void qe_gpio_set_multiple(struct gpio_chip *gc, iowrite32be(qe_gc->cpdata, ®s->cpdata); spin_unlock_irqrestore(&qe_gc->lock, flags); + + return 0; } static int qe_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) @@ -317,8 +321,8 @@ static int __init qe_add_gpiochips(void) gc->direction_input = qe_gpio_dir_in; gc->direction_output = qe_gpio_dir_out; gc->get = qe_gpio_get; - gc->set = qe_gpio_set; - gc->set_multiple = qe_gpio_set_multiple; + gc->set_rv = qe_gpio_set; + gc->set_multiple_rv = qe_gpio_set_multiple; ret = of_mm_gpiochip_add_data(np, mm_gc, qe_gc); if (ret) diff --git a/drivers/soc/fsl/qe/qe_ic.c b/drivers/soc/fsl/qe/qe_ic.c index 4068b501a3a3..943911053af6 100644 --- a/drivers/soc/fsl/qe/qe_ic.c +++ b/drivers/soc/fsl/qe/qe_ic.c @@ -407,7 +407,6 @@ static int qe_ic_init(struct platform_device *pdev) void (*high_handler)(struct irq_desc *desc); struct qe_ic *qe_ic; struct resource *res; - struct device_node *node = pdev->dev.of_node; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { @@ -441,7 +440,7 @@ static int qe_ic_init(struct platform_device *pdev) high_handler = NULL; } - qe_ic->irqhost = irq_domain_create_linear(of_fwnode_handle(node), NR_QE_IC_INTS, + qe_ic->irqhost = irq_domain_create_linear(dev_fwnode(&pdev->dev), NR_QE_IC_INTS, &qe_ic_host_ops, qe_ic); if (qe_ic->irqhost == NULL) { dev_err(dev, "failed to add irq domain\n"); diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c index 7fc353732d55..65ff45fdcac7 100644 --- a/drivers/soc/hisilicon/kunpeng_hccs.c +++ b/drivers/soc/hisilicon/kunpeng_hccs.c @@ -1295,11 +1295,11 @@ static int hccs_get_all_spec_port_idle_sta(struct hccs_dev *hdev, u8 port_type, if (ret) { dev_err(hdev->dev, "hccs%u on chip%u/die%u get idle status failed, ret = %d.\n", - k, i, j, ret); + port->port_id, chip->chip_id, die->die_id, ret); return ret; } else if (idle == 0) { dev_info(hdev->dev, "hccs%u on chip%u/die%u is busy.\n", - k, i, j); + port->port_id, chip->chip_id, die->die_id); return 0; } } diff --git a/drivers/soc/mediatek/mtk-mutex.c b/drivers/soc/mediatek/mtk-mutex.c index aaa965d4b050..38179e8cd98f 100644 --- a/drivers/soc/mediatek/mtk-mutex.c +++ b/drivers/soc/mediatek/mtk-mutex.c @@ -17,16 +17,35 @@ #define MT2701_MUTEX0_MOD0 0x2c #define MT2701_MUTEX0_SOF0 0x30 +#define MT2701_MUTEX0_MOD1 0x34 + #define MT8183_MUTEX0_MOD0 0x30 +#define MT8183_MUTEX0_MOD1 0x34 #define MT8183_MUTEX0_SOF0 0x2c #define DISP_REG_MUTEX_EN(n) (0x20 + 0x20 * (n)) #define DISP_REG_MUTEX(n) (0x24 + 0x20 * (n)) #define DISP_REG_MUTEX_RST(n) (0x28 + 0x20 * (n)) -#define DISP_REG_MUTEX_MOD(mutex_mod_reg, n) (mutex_mod_reg + 0x20 * (n)) -#define DISP_REG_MUTEX_MOD1(mutex_mod_reg, n) ((mutex_mod_reg) + 0x20 * (n) + 0x4) +/* + * Some SoCs may have multiple MUTEX_MOD registers as more than 32 mods + * are present, hence requiring multiple 32-bits registers. + * + * The mutex_table_mod fully represents that by defining the number of + * the mod sequentially, later used as a bit number, which can be more + * than 0..31. + * + * In order to retain compatibility with older SoCs, we perform R/W on + * the single 32 bits registers, but this requires us to translate the + * mutex ID bit accordingly. + */ +#define DISP_REG_MUTEX_MOD(mutex, id, n) ({ \ + const typeof(mutex) _mutex = (mutex); \ + u32 _offset = (id) < 32 ? \ + _mutex->data->mutex_mod_reg : \ + _mutex->data->mutex_mod1_reg; \ + _offset + 0x20 * (n); \ +}) #define DISP_REG_MUTEX_SOF(mutex_sof_reg, n) (mutex_sof_reg + 0x20 * (n)) -#define DISP_REG_MUTEX_MOD2(n) (0x34 + 0x20 * (n)) #define INT_MUTEX BIT(1) @@ -334,6 +353,7 @@ struct mtk_mutex_data { const u8 *mutex_table_mod; const u16 *mutex_sof; const u16 mutex_mod_reg; + const u16 mutex_mod1_reg; const u16 mutex_sof_reg; const bool no_clk; }; @@ -714,6 +734,7 @@ static const struct mtk_mutex_data mt2701_mutex_driver_data = { .mutex_mod = mt2701_mutex_mod, .mutex_sof = mt2712_mutex_sof, .mutex_mod_reg = MT2701_MUTEX0_MOD0, + .mutex_mod1_reg = MT2701_MUTEX0_MOD1, .mutex_sof_reg = MT2701_MUTEX0_SOF0, }; @@ -721,6 +742,7 @@ static const struct mtk_mutex_data mt2712_mutex_driver_data = { .mutex_mod = mt2712_mutex_mod, .mutex_sof = mt2712_mutex_sof, .mutex_mod_reg = MT2701_MUTEX0_MOD0, + .mutex_mod1_reg = MT2701_MUTEX0_MOD1, .mutex_sof_reg = MT2701_MUTEX0_SOF0, }; @@ -728,6 +750,7 @@ static const struct mtk_mutex_data mt6795_mutex_driver_data = { .mutex_mod = mt8173_mutex_mod, .mutex_sof = mt6795_mutex_sof, .mutex_mod_reg = MT2701_MUTEX0_MOD0, + .mutex_mod1_reg = MT2701_MUTEX0_MOD1, .mutex_sof_reg = MT2701_MUTEX0_SOF0, }; @@ -735,6 +758,7 @@ static const struct mtk_mutex_data mt8167_mutex_driver_data = { .mutex_mod = mt8167_mutex_mod, .mutex_sof = mt8167_mutex_sof, .mutex_mod_reg = MT2701_MUTEX0_MOD0, + .mutex_mod1_reg = MT2701_MUTEX0_MOD1, .mutex_sof_reg = MT2701_MUTEX0_SOF0, .no_clk = true, }; @@ -743,6 +767,7 @@ static const struct mtk_mutex_data mt8173_mutex_driver_data = { .mutex_mod = mt8173_mutex_mod, .mutex_sof = mt2712_mutex_sof, .mutex_mod_reg = MT2701_MUTEX0_MOD0, + .mutex_mod1_reg = MT2701_MUTEX0_MOD1, .mutex_sof_reg = MT2701_MUTEX0_SOF0, }; @@ -750,6 +775,7 @@ static const struct mtk_mutex_data mt8183_mutex_driver_data = { .mutex_mod = mt8183_mutex_mod, .mutex_sof = mt8183_mutex_sof, .mutex_mod_reg = MT8183_MUTEX0_MOD0, + .mutex_mod1_reg = MT8183_MUTEX0_MOD1, .mutex_sof_reg = MT8183_MUTEX0_SOF0, .mutex_table_mod = mt8183_mutex_table_mod, .no_clk = true, @@ -757,6 +783,7 @@ static const struct mtk_mutex_data mt8183_mutex_driver_data = { static const struct mtk_mutex_data mt8186_mdp_mutex_driver_data = { .mutex_mod_reg = MT8183_MUTEX0_MOD0, + .mutex_mod1_reg = MT8183_MUTEX0_MOD1, .mutex_sof_reg = MT8183_MUTEX0_SOF0, .mutex_table_mod = mt8186_mdp_mutex_table_mod, }; @@ -765,6 +792,7 @@ static const struct mtk_mutex_data mt8186_mutex_driver_data = { .mutex_mod = mt8186_mutex_mod, .mutex_sof = mt8186_mutex_sof, .mutex_mod_reg = MT8183_MUTEX0_MOD0, + .mutex_mod1_reg = MT8183_MUTEX0_MOD1, .mutex_sof_reg = MT8183_MUTEX0_SOF0, }; @@ -772,12 +800,14 @@ static const struct mtk_mutex_data mt8188_mutex_driver_data = { .mutex_mod = mt8188_mutex_mod, .mutex_sof = mt8188_mutex_sof, .mutex_mod_reg = MT8183_MUTEX0_MOD0, + .mutex_mod1_reg = MT8183_MUTEX0_MOD1, .mutex_sof_reg = MT8183_MUTEX0_SOF0, }; static const struct mtk_mutex_data mt8188_vpp_mutex_driver_data = { .mutex_sof = mt8188_mutex_sof, .mutex_mod_reg = MT8183_MUTEX0_MOD0, + .mutex_mod1_reg = MT8183_MUTEX0_MOD1, .mutex_sof_reg = MT8183_MUTEX0_SOF0, .mutex_table_mod = mt8188_mdp_mutex_table_mod, }; @@ -786,6 +816,7 @@ static const struct mtk_mutex_data mt8192_mutex_driver_data = { .mutex_mod = mt8192_mutex_mod, .mutex_sof = mt8183_mutex_sof, .mutex_mod_reg = MT8183_MUTEX0_MOD0, + .mutex_mod1_reg = MT8183_MUTEX0_MOD1, .mutex_sof_reg = MT8183_MUTEX0_SOF0, }; @@ -793,12 +824,14 @@ static const struct mtk_mutex_data mt8195_mutex_driver_data = { .mutex_mod = mt8195_mutex_mod, .mutex_sof = mt8195_mutex_sof, .mutex_mod_reg = MT8183_MUTEX0_MOD0, + .mutex_mod1_reg = MT8183_MUTEX0_MOD1, .mutex_sof_reg = MT8183_MUTEX0_SOF0, }; static const struct mtk_mutex_data mt8195_vpp_mutex_driver_data = { .mutex_sof = mt8195_mutex_sof, .mutex_mod_reg = MT8183_MUTEX0_MOD0, + .mutex_mod1_reg = MT8183_MUTEX0_MOD1, .mutex_sof_reg = MT8183_MUTEX0_SOF0, .mutex_table_mod = mt8195_mutex_table_mod, }; @@ -807,6 +840,7 @@ static const struct mtk_mutex_data mt8365_mutex_driver_data = { .mutex_mod = mt8365_mutex_mod, .mutex_sof = mt8183_mutex_sof, .mutex_mod_reg = MT8183_MUTEX0_MOD0, + .mutex_mod1_reg = MT8183_MUTEX0_MOD1, .mutex_sof_reg = MT8183_MUTEX0_SOF0, .no_clk = true, }; @@ -859,7 +893,7 @@ void mtk_mutex_add_comp(struct mtk_mutex *mutex, struct mtk_mutex_ctx *mtx = container_of(mutex, struct mtk_mutex_ctx, mutex[mutex->id]); unsigned int reg; - unsigned int sof_id; + unsigned int sof_id, mod_id; unsigned int offset; WARN_ON(&mtx->mutex[mutex->id] != mutex); @@ -890,18 +924,11 @@ void mtk_mutex_add_comp(struct mtk_mutex *mutex, sof_id = MUTEX_SOF_DP_INTF1; break; default: - if (mtx->data->mutex_mod[id] < 32) { - offset = DISP_REG_MUTEX_MOD(mtx->data->mutex_mod_reg, - mutex->id); - reg = readl_relaxed(mtx->regs + offset); - reg |= 1 << mtx->data->mutex_mod[id]; - writel_relaxed(reg, mtx->regs + offset); - } else { - offset = DISP_REG_MUTEX_MOD2(mutex->id); - reg = readl_relaxed(mtx->regs + offset); - reg |= 1 << (mtx->data->mutex_mod[id] - 32); - writel_relaxed(reg, mtx->regs + offset); - } + offset = DISP_REG_MUTEX_MOD(mtx, mtx->data->mutex_mod[id], mutex->id); + mod_id = mtx->data->mutex_mod[id] % 32; + reg = readl_relaxed(mtx->regs + offset); + reg |= BIT(mod_id); + writel_relaxed(reg, mtx->regs + offset); return; } @@ -917,6 +944,7 @@ void mtk_mutex_remove_comp(struct mtk_mutex *mutex, struct mtk_mutex_ctx *mtx = container_of(mutex, struct mtk_mutex_ctx, mutex[mutex->id]); unsigned int reg; + unsigned int mod_id; unsigned int offset; WARN_ON(&mtx->mutex[mutex->id] != mutex); @@ -936,18 +964,11 @@ void mtk_mutex_remove_comp(struct mtk_mutex *mutex, mutex->id)); break; default: - if (mtx->data->mutex_mod[id] < 32) { - offset = DISP_REG_MUTEX_MOD(mtx->data->mutex_mod_reg, - mutex->id); - reg = readl_relaxed(mtx->regs + offset); - reg &= ~(1 << mtx->data->mutex_mod[id]); - writel_relaxed(reg, mtx->regs + offset); - } else { - offset = DISP_REG_MUTEX_MOD2(mutex->id); - reg = readl_relaxed(mtx->regs + offset); - reg &= ~(1 << (mtx->data->mutex_mod[id] - 32)); - writel_relaxed(reg, mtx->regs + offset); - } + offset = DISP_REG_MUTEX_MOD(mtx, mtx->data->mutex_mod[id], mutex->id); + mod_id = mtx->data->mutex_mod[id] % 32; + reg = readl_relaxed(mtx->regs + offset); + reg &= ~BIT(mod_id); + writel_relaxed(reg, mtx->regs + offset); break; } } @@ -1023,7 +1044,7 @@ int mtk_mutex_write_mod(struct mtk_mutex *mutex, struct mtk_mutex_ctx *mtx = container_of(mutex, struct mtk_mutex_ctx, mutex[mutex->id]); unsigned int reg; - u32 reg_offset, id_offset = 0; + u32 offset, mod_id; WARN_ON(&mtx->mutex[mutex->id] != mutex); @@ -1033,34 +1054,16 @@ int mtk_mutex_write_mod(struct mtk_mutex *mutex, return -EINVAL; } - /* - * Some SoCs may have multiple MUTEX_MOD registers as more than 32 mods - * are present, hence requiring multiple 32-bits registers. - * - * The mutex_table_mod fully represents that by defining the number of - * the mod sequentially, later used as a bit number, which can be more - * than 0..31. - * - * In order to retain compatibility with older SoCs, we perform R/W on - * the single 32 bits registers, but this requires us to translate the - * mutex ID bit accordingly. - */ - if (mtx->data->mutex_table_mod[idx] < 32) { - reg_offset = DISP_REG_MUTEX_MOD(mtx->data->mutex_mod_reg, - mutex->id); - } else { - reg_offset = DISP_REG_MUTEX_MOD1(mtx->data->mutex_mod_reg, - mutex->id); - id_offset = 32; - } + offset = DISP_REG_MUTEX_MOD(mtx, mtx->data->mutex_table_mod[idx], mutex->id); + mod_id = mtx->data->mutex_table_mod[idx] % 32; - reg = readl_relaxed(mtx->regs + reg_offset); + reg = readl_relaxed(mtx->regs + offset); if (clear) - reg &= ~BIT(mtx->data->mutex_table_mod[idx] - id_offset); + reg &= ~BIT(mod_id); else - reg |= BIT(mtx->data->mutex_table_mod[idx] - id_offset); + reg |= BIT(mod_id); - writel_relaxed(reg, mtx->regs + reg_offset); + writel_relaxed(reg, mtx->regs + offset); return 0; } diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index b2c0fb55d4ae..0ca268bdf1f8 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -18,7 +18,38 @@ #include <linux/slab.h> #include <linux/soc/qcom/mdt_loader.h> -static bool mdt_phdr_valid(const struct elf32_phdr *phdr) +static bool mdt_header_valid(const struct firmware *fw) +{ + const struct elf32_hdr *ehdr; + size_t phend; + size_t shend; + + if (fw->size < sizeof(*ehdr)) + return false; + + ehdr = (struct elf32_hdr *)fw->data; + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) + return false; + + if (ehdr->e_phentsize != sizeof(struct elf32_phdr)) + return false; + + phend = size_add(size_mul(sizeof(struct elf32_phdr), ehdr->e_phnum), ehdr->e_phoff); + if (phend > fw->size) + return false; + + if (ehdr->e_shentsize != sizeof(struct elf32_shdr)) + return false; + + shend = size_add(size_mul(sizeof(struct elf32_shdr), ehdr->e_shnum), ehdr->e_shoff); + if (shend > fw->size) + return false; + + return true; +} + +static bool mdt_phdr_loadable(const struct elf32_phdr *phdr) { if (phdr->p_type != PT_LOAD) return false; @@ -82,13 +113,16 @@ ssize_t qcom_mdt_get_size(const struct firmware *fw) phys_addr_t max_addr = 0; int i; + if (!mdt_header_valid(fw)) + return -EINVAL; + ehdr = (struct elf32_hdr *)fw->data; - phdrs = (struct elf32_phdr *)(ehdr + 1); + phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; - if (!mdt_phdr_valid(phdr)) + if (!mdt_phdr_loadable(phdr)) continue; if (phdr->p_paddr < min_addr) @@ -134,8 +168,11 @@ void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len, ssize_t ret; void *data; + if (!mdt_header_valid(fw)) + return ERR_PTR(-EINVAL); + ehdr = (struct elf32_hdr *)fw->data; - phdrs = (struct elf32_phdr *)(ehdr + 1); + phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); if (ehdr->e_phnum < 2) return ERR_PTR(-EINVAL); @@ -214,13 +251,16 @@ int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, int ret; int i; + if (!mdt_header_valid(fw)) + return -EINVAL; + ehdr = (struct elf32_hdr *)fw->data; - phdrs = (struct elf32_phdr *)(ehdr + 1); + phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; - if (!mdt_phdr_valid(phdr)) + if (!mdt_phdr_loadable(phdr)) continue; if (phdr->p_flags & QCOM_MDT_RELOCATABLE) @@ -270,7 +310,7 @@ static bool qcom_mdt_bins_are_split(const struct firmware *fw, const char *fw_na int i; ehdr = (struct elf32_hdr *)fw->data; - phdrs = (struct elf32_phdr *)(ehdr + 1); + phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; i++) { /* @@ -310,14 +350,17 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, if (!fw || !mem_region || !mem_phys || !mem_size) return -EINVAL; + if (!mdt_header_valid(fw)) + return -EINVAL; + is_split = qcom_mdt_bins_are_split(fw, fw_name); ehdr = (struct elf32_hdr *)fw->data; - phdrs = (struct elf32_phdr *)(ehdr + 1); + phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; - if (!mdt_phdr_valid(phdr)) + if (!mdt_phdr_loadable(phdr)) continue; if (phdr->p_flags & QCOM_MDT_RELOCATABLE) @@ -344,7 +387,7 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; - if (!mdt_phdr_valid(phdr)) + if (!mdt_phdr_loadable(phdr)) continue; offset = phdr->p_paddr - mem_reloc; diff --git a/drivers/soc/qcom/pmic_glink.c b/drivers/soc/qcom/pmic_glink.c index 0a6d325b195c..c0a4be5df926 100644 --- a/drivers/soc/qcom/pmic_glink.c +++ b/drivers/soc/qcom/pmic_glink.c @@ -167,7 +167,10 @@ static int pmic_glink_rpmsg_callback(struct rpmsg_device *rpdev, void *data, return 0; } -static void pmic_glink_aux_release(struct device *dev) {} +static void pmic_glink_aux_release(struct device *dev) +{ + of_node_put(dev->of_node); +} static int pmic_glink_add_aux_device(struct pmic_glink *pg, struct auxiliary_device *aux, @@ -181,8 +184,10 @@ static int pmic_glink_add_aux_device(struct pmic_glink *pg, aux->dev.release = pmic_glink_aux_release; device_set_of_node_from_dev(&aux->dev, parent); ret = auxiliary_device_init(aux); - if (ret) + if (ret) { + of_node_put(aux->dev.of_node); return ret; + } ret = auxiliary_device_add(aux); if (ret) diff --git a/drivers/soc/qcom/qcom_stats.c b/drivers/soc/qcom/qcom_stats.c index 5de99cf59b9f..2e380faf9080 100644 --- a/drivers/soc/qcom/qcom_stats.c +++ b/drivers/soc/qcom/qcom_stats.c @@ -1,8 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2011-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2025, Qualcomm Innovation Center, Inc. All rights reserved. */ +#include <linux/bitfield.h> #include <linux/debugfs.h> #include <linux/device.h> #include <linux/io.h> @@ -11,6 +13,7 @@ #include <linux/platform_device.h> #include <linux/seq_file.h> +#include <linux/soc/qcom/qcom_aoss.h> #include <linux/soc/qcom/smem.h> #include <clocksource/arm_arch_timer.h> @@ -24,6 +27,19 @@ #define ACCUMULATED_OFFSET 0x18 #define CLIENT_VOTES_OFFSET 0x20 +#define DDR_STATS_MAGIC_KEY 0xA1157A75 +#define DDR_STATS_MAX_NUM_MODES 20 +#define DDR_STATS_MAGIC_KEY_ADDR 0x0 +#define DDR_STATS_NUM_MODES_ADDR 0x4 +#define DDR_STATS_ENTRY_START_ADDR 0x8 + +#define DDR_STATS_CP_IDX(data) FIELD_GET(GENMASK(4, 0), data) +#define DDR_STATS_LPM_NAME(data) FIELD_GET(GENMASK(7, 0), data) +#define DDR_STATS_TYPE(data) FIELD_GET(GENMASK(15, 8), data) +#define DDR_STATS_FREQ(data) FIELD_GET(GENMASK(31, 16), data) + +static struct qmp *qcom_stats_qmp; + struct subsystem_data { const char *name; u32 smem_item; @@ -48,12 +64,19 @@ static const struct subsystem_data subsystems[] = { struct stats_config { size_t stats_offset; + size_t ddr_stats_offset; size_t num_records; bool appended_stats_avail; bool dynamic_offset; bool subsystem_stats_in_smem; }; +struct ddr_stats_entry { + u32 name; + u32 count; + u64 duration; +}; + struct stats_data { bool appended_stats_avail; void __iomem *base; @@ -122,8 +145,101 @@ static int qcom_soc_sleep_stats_show(struct seq_file *s, void *unused) return 0; } +static void qcom_ddr_stats_print(struct seq_file *s, struct ddr_stats_entry *data) +{ + u32 cp_idx; + + /* + * DDR statistic have two different types of details encoded. + * (1) DDR LPM Stats + * (2) DDR Frequency Stats + * + * The name field have details like which type of DDR stat (bits 8:15) + * along with other details as explained below + * + * In case of DDR LPM stat, name field will be encoded as, + * Bits - Meaning + * 0:7 - DDR LPM name, can be of 0xd4, 0xd3, 0x11 and 0xd0. + * 8:15 - 0x0 (indicates its a LPM stat) + * 16:31 - Unused + * + * In case of DDR FREQ stats, name field will be encoded as, + * Bits - Meaning + * 0:4 - DDR Clock plan index (CP IDX) + * 5:7 - Unused + * 8:15 - 0x1 (indicates its Freq stat) + * 16:31 - Frequency value in Mhz + */ + switch (DDR_STATS_TYPE(data->name)) { + case 0: + seq_printf(s, "DDR LPM Stat Name:0x%lx\tcount:%u\tDuration (ticks):%llu\n", + DDR_STATS_LPM_NAME(data->name), data->count, data->duration); + break; + case 1: + if (!data->count || !DDR_STATS_FREQ(data->name)) + return; + + cp_idx = DDR_STATS_CP_IDX(data->name); + seq_printf(s, "DDR Freq %luMhz:\tCP IDX:%u\tcount:%u\tDuration (ticks):%llu\n", + DDR_STATS_FREQ(data->name), cp_idx, data->count, data->duration); + break; + } +} + +static int qcom_ddr_stats_show(struct seq_file *s, void *d) +{ + struct ddr_stats_entry data[DDR_STATS_MAX_NUM_MODES]; + void __iomem *reg = (void __iomem *)s->private; + u32 entry_count; + int i, ret; + + entry_count = readl_relaxed(reg + DDR_STATS_NUM_MODES_ADDR); + if (entry_count > DDR_STATS_MAX_NUM_MODES) + return -EINVAL; + + if (qcom_stats_qmp) { + /* + * Recent SoCs (SM8450 onwards) do not have duration field + * populated from boot up onwards for both DDR LPM Stats + * and DDR Frequency Stats. + * + * Send QMP message to Always on processor which will + * populate duration field into MSG RAM area. + * + * Sent every time to read latest data. + */ + ret = qmp_send(qcom_stats_qmp, "{class: ddr, action: freqsync}"); + if (ret) + return ret; + } + + reg += DDR_STATS_ENTRY_START_ADDR; + memcpy_fromio(data, reg, sizeof(struct ddr_stats_entry) * entry_count); + + for (i = 0; i < entry_count; i++) + qcom_ddr_stats_print(s, &data[i]); + + return 0; +} + DEFINE_SHOW_ATTRIBUTE(qcom_soc_sleep_stats); DEFINE_SHOW_ATTRIBUTE(qcom_subsystem_sleep_stats); +DEFINE_SHOW_ATTRIBUTE(qcom_ddr_stats); + +static void qcom_create_ddr_stat_files(struct dentry *root, void __iomem *reg, + const struct stats_config *config) +{ + u32 key; + + if (!config->ddr_stats_offset) + return; + + key = readl_relaxed(reg + config->ddr_stats_offset + DDR_STATS_MAGIC_KEY_ADDR); + if (key == DDR_STATS_MAGIC_KEY) + debugfs_create_file("ddr_stats", 0400, root, + (__force void *)reg + config->ddr_stats_offset, + &qcom_ddr_stats_fops); +} static void qcom_create_soc_sleep_stat_files(struct dentry *root, void __iomem *reg, struct stats_data *d, @@ -207,11 +323,27 @@ static int qcom_stats_probe(struct platform_device *pdev) for (i = 0; i < config->num_records; i++) d[i].appended_stats_avail = config->appended_stats_avail; + /* + * QMP is used for DDR stats syncing to MSG RAM for recent SoCs (SM8450 onwards). + * The prior SoCs do not need QMP handle as the required stats are already present + * in MSG RAM, provided the DDR_STATS_MAGIC_KEY matches. + */ + qcom_stats_qmp = qmp_get(&pdev->dev); + if (IS_ERR(qcom_stats_qmp)) { + /* We ignore error if QMP is not defined/needed */ + if (!of_property_present(pdev->dev.of_node, "qcom,qmp")) + qcom_stats_qmp = NULL; + else if (PTR_ERR(qcom_stats_qmp) == -EPROBE_DEFER) + return -EPROBE_DEFER; + else + return PTR_ERR(qcom_stats_qmp); + } root = debugfs_create_dir("qcom_stats", NULL); qcom_create_subsystem_stat_files(root, config); qcom_create_soc_sleep_stat_files(root, reg, d, config); + qcom_create_ddr_stat_files(root, reg, config); platform_set_drvdata(pdev, root); @@ -254,6 +386,7 @@ static const struct stats_config rpmh_data_sdm845 = { static const struct stats_config rpmh_data = { .stats_offset = 0x48, + .ddr_stats_offset = 0xb8, .num_records = 3, .appended_stats_avail = false, .dynamic_offset = false, diff --git a/drivers/soc/qcom/qmi_encdec.c b/drivers/soc/qcom/qmi_encdec.c index bb09eff85cff..7660a960fb45 100644 --- a/drivers/soc/qcom/qmi_encdec.c +++ b/drivers/soc/qcom/qmi_encdec.c @@ -304,6 +304,8 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf, const void *buf_src; int encode_tlv = 0; int rc; + u8 val8; + u16 val16; if (!ei_array) return 0; @@ -338,7 +340,6 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf, break; case QMI_DATA_LEN: - memcpy(&data_len_value, buf_src, temp_ei->elem_size); data_len_sz = temp_ei->elem_size == sizeof(u8) ? sizeof(u8) : sizeof(u16); /* Check to avoid out of range buffer access */ @@ -348,8 +349,17 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf, __func__); return -ETOOSMALL; } - rc = qmi_encode_basic_elem(buf_dst, &data_len_value, - 1, data_len_sz); + if (data_len_sz == sizeof(u8)) { + val8 = *(u8 *)buf_src; + data_len_value = (u32)val8; + rc = qmi_encode_basic_elem(buf_dst, &val8, + 1, data_len_sz); + } else { + val16 = *(u16 *)buf_src; + data_len_value = (u32)le16_to_cpu(val16); + rc = qmi_encode_basic_elem(buf_dst, &val16, + 1, data_len_sz); + } UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst, encoded_bytes, tlv_len, encode_tlv, rc); @@ -523,14 +533,23 @@ static int qmi_decode_string_elem(const struct qmi_elem_info *ei_array, u32 string_len = 0; u32 string_len_sz = 0; const struct qmi_elem_info *temp_ei = ei_array; + u8 val8; + u16 val16; if (dec_level == 1) { string_len = tlv_len; } else { string_len_sz = temp_ei->elem_len <= U8_MAX ? sizeof(u8) : sizeof(u16); - rc = qmi_decode_basic_elem(&string_len, buf_src, - 1, string_len_sz); + if (string_len_sz == sizeof(u8)) { + rc = qmi_decode_basic_elem(&val8, buf_src, + 1, string_len_sz); + string_len = (u32)val8; + } else { + rc = qmi_decode_basic_elem(&val16, buf_src, + 1, string_len_sz); + string_len = (u32)val16; + } decoded_bytes += rc; } @@ -604,6 +623,9 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct, u32 decoded_bytes = 0; const void *buf_src = in_buf; int rc; + u8 val8; + u16 val16; + u32 val32; while (decoded_bytes < in_buf_len) { if (dec_level >= 2 && temp_ei->data_type == QMI_EOTI) @@ -642,9 +664,17 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct, if (temp_ei->data_type == QMI_DATA_LEN) { data_len_sz = temp_ei->elem_size == sizeof(u8) ? sizeof(u8) : sizeof(u16); - rc = qmi_decode_basic_elem(&data_len_value, buf_src, - 1, data_len_sz); - memcpy(buf_dst, &data_len_value, sizeof(u32)); + if (data_len_sz == sizeof(u8)) { + rc = qmi_decode_basic_elem(&val8, buf_src, + 1, data_len_sz); + data_len_value = (u32)val8; + } else { + rc = qmi_decode_basic_elem(&val16, buf_src, + 1, data_len_sz); + data_len_value = (u32)val16; + } + val32 = cpu_to_le32(data_len_value); + memcpy(buf_dst, &val32, sizeof(u32)); temp_ei = temp_ei + 1; buf_dst = out_c_struct + temp_ei->offset; tlv_len -= data_len_sz; @@ -746,9 +776,9 @@ void *qmi_encode_message(int type, unsigned int msg_id, size_t *len, hdr = msg; hdr->type = type; - hdr->txn_id = txn_id; - hdr->msg_id = msg_id; - hdr->msg_len = msglen; + hdr->txn_id = cpu_to_le16(txn_id); + hdr->msg_id = cpu_to_le16(msg_id); + hdr->msg_len = cpu_to_le16(msglen); *len = sizeof(*hdr) + msglen; diff --git a/drivers/soc/qcom/qmi_interface.c b/drivers/soc/qcom/qmi_interface.c index bc6d6379d8b1..6500f863aae5 100644 --- a/drivers/soc/qcom/qmi_interface.c +++ b/drivers/soc/qcom/qmi_interface.c @@ -400,7 +400,7 @@ static void qmi_invoke_handler(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, for (handler = qmi->handlers; handler->fn; handler++) { if (handler->type == hdr->type && - handler->msg_id == hdr->msg_id) + handler->msg_id == le16_to_cpu(hdr->msg_id)) break; } @@ -488,7 +488,7 @@ static void qmi_handle_message(struct qmi_handle *qmi, /* If this is a response, find the matching transaction handle */ if (hdr->type == QMI_RESPONSE) { mutex_lock(&qmi->txn_lock); - txn = idr_find(&qmi->txns, hdr->txn_id); + txn = idr_find(&qmi->txns, le16_to_cpu(hdr->txn_id)); /* Ignore unexpected responses */ if (!txn) { @@ -514,7 +514,7 @@ static void qmi_handle_message(struct qmi_handle *qmi, } else { /* Create a txn based on the txn_id of the incoming message */ memset(&tmp_txn, 0, sizeof(tmp_txn)); - tmp_txn.id = hdr->txn_id; + tmp_txn.id = le16_to_cpu(hdr->txn_id); qmi_invoke_handler(qmi, sq, &tmp_txn, buf, len); } diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index cb82e887b51d..fdab2b1067db 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -1072,7 +1072,7 @@ static int rpmh_rsc_probe(struct platform_device *pdev) drv->ver.minor = rsc_id & (MINOR_VER_MASK << MINOR_VER_SHIFT); drv->ver.minor >>= MINOR_VER_SHIFT; - if (drv->ver.major == 3) + if (drv->ver.major >= 3) drv->regs = rpmh_rsc_reg_offset_ver_3_0; else drv->regs = rpmh_rsc_reg_offset_ver_2_7; diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 8c4147737c35..963772f45489 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -38,6 +38,7 @@ #define SMEM_IMAGE_TABLE_BOOT_INDEX 0 #define SMEM_IMAGE_TABLE_TZ_INDEX 1 #define SMEM_IMAGE_TABLE_RPM_INDEX 3 +#define SMEM_IMAGE_TABLE_APPSBL_INDEX 9 #define SMEM_IMAGE_TABLE_APPS_INDEX 10 #define SMEM_IMAGE_TABLE_MPSS_INDEX 11 #define SMEM_IMAGE_TABLE_ADSP_INDEX 12 @@ -48,6 +49,7 @@ #define SMEM_IMAGE_TABLE_CDSP1_INDEX 19 #define SMEM_IMAGE_TABLE_GPDSP_INDEX 20 #define SMEM_IMAGE_TABLE_GPDSP1_INDEX 21 +#define SMEM_IMAGE_TABLE_TME_INDEX 28 #define SMEM_IMAGE_VERSION_TABLE 469 /* @@ -55,6 +57,7 @@ */ static const char *const socinfo_image_names[] = { [SMEM_IMAGE_TABLE_ADSP_INDEX] = "adsp", + [SMEM_IMAGE_TABLE_APPSBL_INDEX] = "appsbl", [SMEM_IMAGE_TABLE_APPS_INDEX] = "apps", [SMEM_IMAGE_TABLE_BOOT_INDEX] = "boot", [SMEM_IMAGE_TABLE_CNSS_INDEX] = "cnss", @@ -67,6 +70,7 @@ static const char *const socinfo_image_names[] = { [SMEM_IMAGE_TABLE_CDSP1_INDEX] = "cdsp1", [SMEM_IMAGE_TABLE_GPDSP_INDEX] = "gpdsp", [SMEM_IMAGE_TABLE_GPDSP1_INDEX] = "gpdsp1", + [SMEM_IMAGE_TABLE_TME_INDEX] = "tme", }; static const char *const pmic_models[] = { @@ -126,8 +130,12 @@ static const char *const pmic_models[] = { [72] = "PMR735D", [73] = "PM8550", [74] = "PMK8550", + [78] = "PMM8650AU", + [79] = "PMM8650AU_PSAIL", + [80] = "PM7550", [82] = "PMC8380", [83] = "SMB2360", + [91] = "PMIV0108", }; struct socinfo_params { @@ -446,8 +454,13 @@ static const struct soc_id soc_id[] = { { qcom_board_id(QCM8550) }, { qcom_board_id(SM8750) }, { qcom_board_id(IPQ5300) }, + { qcom_board_id(SM7635) }, + { qcom_board_id(SM6650) }, + { qcom_board_id(SM6650P) }, { qcom_board_id(IPQ5321) }, { qcom_board_id(IPQ5424) }, + { qcom_board_id(QCM6690) }, + { qcom_board_id(QCS6690) }, { qcom_board_id(IPQ5404) }, { qcom_board_id(QCS9100) }, { qcom_board_id(QCS8300) }, diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index fbc3b69d21a7..719b7f4f376f 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -69,139 +69,139 @@ config ARCH_EMEV2 select HAVE_ARM_SCU if SMP select SYS_SUPPORTS_EM_STI -config ARCH_R8A7794 - bool "ARM32 Platform support for R-Car E2" +config ARCH_R7S72100 + bool "ARM32 Platform support for R7S72100 (RZ/A1H)" default ARCH_RENESAS - select ARCH_RCAR_GEN2 - select ARM_ERRATA_814220 - select SYSC_R8A7794 + select ARM_ERRATA_754322 + select PM + select PM_GENERIC_DOMAINS + select RENESAS_OSTM + select RENESAS_RZA1_IRQC + select SYS_SUPPORTS_SH_MTU2 -config ARCH_R8A7779 - bool "ARM32 Platform support for R-Car H1" +config ARCH_R7S9210 + bool "ARM32 Platform support for R7S9210 (RZ/A2)" default ARCH_RENESAS - select ARCH_RCAR_GEN1 - select ARM_ERRATA_754322 - select ARM_GLOBAL_TIMER - select HAVE_ARM_SCU if SMP - select HAVE_ARM_TWD if SMP - select SYSC_R8A7779 + select PM + select PM_GENERIC_DOMAINS + select RENESAS_OSTM + select RENESAS_RZA1_IRQC -config ARCH_R8A7790 - bool "ARM32 Platform support for R-Car H2" +config ARCH_R8A73A4 + bool "ARM32 Platform support for R8A73A4 (R-Mobile APE6)" default ARCH_RENESAS - select ARCH_RCAR_GEN2 + select ARCH_RMOBILE select ARM_ERRATA_798181 if SMP select ARM_ERRATA_814220 - select I2C - select SYSC_R8A7790 + select HAVE_ARM_ARCH_TIMER + select RENESAS_IRQC -config ARCH_R8A7778 - bool "ARM32 Platform support for R-Car M1A" +config ARCH_R8A7740 + bool "ARM32 Platform support for R8A7740 (R-Mobile A1)" default ARCH_RENESAS - select ARCH_RCAR_GEN1 + select ARCH_RMOBILE select ARM_ERRATA_754322 + select RENESAS_INTC_IRQPIN -config ARCH_R8A7793 - bool "ARM32 Platform support for R-Car M2-N" +config ARCH_R8A7742 + bool "ARM32 Platform support for R8A7742 (RZ/G1H)" default ARCH_RENESAS select ARCH_RCAR_GEN2 select ARM_ERRATA_798181 if SMP - select I2C - select SYSC_R8A7791 + select ARM_ERRATA_814220 + select SYSC_R8A7742 -config ARCH_R8A7791 - bool "ARM32 Platform support for R-Car M2-W" +config ARCH_R8A7743 + bool "ARM32 Platform support for R8A7743 (RZ/G1M)" default ARCH_RENESAS select ARCH_RCAR_GEN2 select ARM_ERRATA_798181 if SMP - select I2C - select SYSC_R8A7791 + select SYSC_R8A7743 -config ARCH_R8A7792 - bool "ARM32 Platform support for R-Car V2H" +config ARCH_R8A7744 + bool "ARM32 Platform support for R8A7744 (RZ/G1N)" default ARCH_RENESAS select ARCH_RCAR_GEN2 select ARM_ERRATA_798181 if SMP - select SYSC_R8A7792 + select SYSC_R8A7743 -config ARCH_R8A7740 - bool "ARM32 Platform support for R-Mobile A1" +config ARCH_R8A7745 + bool "ARM32 Platform support for R8A7745 (RZ/G1E)" default ARCH_RENESAS - select ARCH_RMOBILE - select ARM_ERRATA_754322 - select RENESAS_INTC_IRQPIN + select ARCH_RCAR_GEN2 + select ARM_ERRATA_814220 + select SYSC_R8A7745 -config ARCH_R8A73A4 - bool "ARM32 Platform support for R-Mobile APE6" +config ARCH_R8A77470 + bool "ARM32 Platform support for R8A77470 (RZ/G1C)" default ARCH_RENESAS - select ARCH_RMOBILE - select ARM_ERRATA_798181 if SMP + select ARCH_RCAR_GEN2 select ARM_ERRATA_814220 - select HAVE_ARM_ARCH_TIMER - select RENESAS_IRQC + select SYSC_R8A77470 -config ARCH_R7S72100 - bool "ARM32 Platform support for RZ/A1H" +config ARCH_R8A7778 + bool "ARM32 Platform support for R8A7778 (R-Car M1A)" default ARCH_RENESAS + select ARCH_RCAR_GEN1 select ARM_ERRATA_754322 - select PM - select PM_GENERIC_DOMAINS - select RENESAS_OSTM - select RENESAS_RZA1_IRQC - select SYS_SUPPORTS_SH_MTU2 -config ARCH_R7S9210 - bool "ARM32 Platform support for RZ/A2" +config ARCH_R8A7779 + bool "ARM32 Platform support for R8A7779 (R-Car H1)" default ARCH_RENESAS - select PM - select PM_GENERIC_DOMAINS - select RENESAS_OSTM - select RENESAS_RZA1_IRQC + select ARCH_RCAR_GEN1 + select ARM_ERRATA_754322 + select ARM_GLOBAL_TIMER + select HAVE_ARM_SCU if SMP + select HAVE_ARM_TWD if SMP + select SYSC_R8A7779 -config ARCH_R8A77470 - bool "ARM32 Platform support for RZ/G1C" +config ARCH_R8A7790 + bool "ARM32 Platform support for R8A7790 (R-Car H2)" default ARCH_RENESAS select ARCH_RCAR_GEN2 + select ARM_ERRATA_798181 if SMP select ARM_ERRATA_814220 - select SYSC_R8A77470 + select I2C + select SYSC_R8A7790 -config ARCH_R8A7745 - bool "ARM32 Platform support for RZ/G1E" +config ARCH_R8A7791 + bool "ARM32 Platform support for R8A7791 (R-Car M2-W)" default ARCH_RENESAS select ARCH_RCAR_GEN2 - select ARM_ERRATA_814220 - select SYSC_R8A7745 + select ARM_ERRATA_798181 if SMP + select I2C + select SYSC_R8A7791 -config ARCH_R8A7742 - bool "ARM32 Platform support for RZ/G1H" +config ARCH_R8A7792 + bool "ARM32 Platform support for R8A7792 (R-Car V2H)" default ARCH_RENESAS select ARCH_RCAR_GEN2 select ARM_ERRATA_798181 if SMP - select ARM_ERRATA_814220 - select SYSC_R8A7742 + select SYSC_R8A7792 -config ARCH_R8A7743 - bool "ARM32 Platform support for RZ/G1M" +config ARCH_R8A7793 + bool "ARM32 Platform support for R8A7793 (R-Car M2-N)" default ARCH_RENESAS select ARCH_RCAR_GEN2 select ARM_ERRATA_798181 if SMP - select SYSC_R8A7743 + select I2C + select SYSC_R8A7791 -config ARCH_R8A7744 - bool "ARM32 Platform support for RZ/G1N" +config ARCH_R8A7794 + bool "ARM32 Platform support for R8A7794 (R-Car E2)" default ARCH_RENESAS select ARCH_RCAR_GEN2 - select ARM_ERRATA_798181 if SMP - select SYSC_R8A7743 + select ARM_ERRATA_814220 + select SYSC_R8A7794 config ARCH_R9A06G032 - bool "ARM32 Platform support for RZ/N1D" + bool "ARM32 Platform support for R9A06G032 (RZ/N1D)" default ARCH_RENESAS select ARCH_RZN1 select ARM_ERRATA_814220 config ARCH_SH73A0 - bool "ARM32 Platform support for SH-Mobile AG5" + bool "ARM32 Platform support for SH73A0 (SH-Mobile AG5)" default ARCH_RENESAS select ARCH_RMOBILE select ARM_ERRATA_754322 @@ -214,26 +214,40 @@ endif # ARM if ARM64 -config ARCH_R8A77995 - bool "ARM64 Platform support for R-Car D3" +config ARCH_R8A774A1 + bool "ARM64 Platform support for R8A774A1 (RZ/G2M)" default y if ARCH_RENESAS select ARCH_RCAR_GEN3 - select SYSC_R8A77995 + select SYSC_R8A774A1 help - This enables support for the Renesas R-Car D3 SoC. - This includes different gradings like R-Car D3e. + This enables support for the Renesas RZ/G2M SoC. -config ARCH_R8A77990 - bool "ARM64 Platform support for R-Car E3" +config ARCH_R8A774B1 + bool "ARM64 Platform support for R8A774B1 (RZ/G2N)" default y if ARCH_RENESAS select ARCH_RCAR_GEN3 - select SYSC_R8A77990 + select SYSC_R8A774B1 help - This enables support for the Renesas R-Car E3 SoC. - This includes different gradings like R-Car E3e. + This enables support for the Renesas RZ/G2N SoC. + +config ARCH_R8A774C0 + bool "ARM64 Platform support for R8A774C0 (RZ/G2E)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN3 + select SYSC_R8A774C0 + help + This enables support for the Renesas RZ/G2E SoC. + +config ARCH_R8A774E1 + bool "ARM64 Platform support for R8A774E1 (RZ/G2H)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN3 + select SYSC_R8A774E1 + help + This enables support for the Renesas RZ/G2H SoC. config ARCH_R8A77951 - bool "ARM64 Platform support for R-Car H3 ES2.0+" + bool "ARM64 Platform support for R8A77951 (R-Car H3 ES2.0+)" default y if ARCH_RENESAS select ARCH_RCAR_GEN3 select SYSC_R8A7795 @@ -242,17 +256,8 @@ config ARCH_R8A77951 later). This includes different gradings like R-Car H3e, H3e-2G, and H3Ne. -config ARCH_R8A77965 - bool "ARM64 Platform support for R-Car M3-N" - default y if ARCH_RENESAS - select ARCH_RCAR_GEN3 - select SYSC_R8A77965 - help - This enables support for the Renesas R-Car M3-N SoC. - This includes different gradings like R-Car M3Ne and M3Ne-2G. - config ARCH_R8A77960 - bool "ARM64 Platform support for R-Car M3-W" + bool "ARM64 Platform support for R8A77960 (R-Car M3-W)" default y if ARCH_RENESAS select ARCH_RCAR_GEN3 select SYSC_R8A77960 @@ -260,7 +265,7 @@ config ARCH_R8A77960 This enables support for the Renesas R-Car M3-W SoC. config ARCH_R8A77961 - bool "ARM64 Platform support for R-Car M3-W+" + bool "ARM64 Platform support for R8A77961 (R-Car M3-W+)" default y if ARCH_RENESAS select ARCH_RCAR_GEN3 select SYSC_R8A77961 @@ -268,40 +273,67 @@ config ARCH_R8A77961 This enables support for the Renesas R-Car M3-W+ SoC. This includes different gradings like R-Car M3e and M3e-2G. -config ARCH_R8A779F0 - bool "ARM64 Platform support for R-Car S4-8" +config ARCH_R8A77965 + bool "ARM64 Platform support for R8A77965 (R-Car M3-N)" default y if ARCH_RENESAS - select ARCH_RCAR_GEN4 - select SYSC_R8A779F0 + select ARCH_RCAR_GEN3 + select SYSC_R8A77965 help - This enables support for the Renesas R-Car S4-8 SoC. + This enables support for the Renesas R-Car M3-N SoC. + This includes different gradings like R-Car M3Ne and M3Ne-2G. + +config ARCH_R8A77970 + bool "ARM64 Platform support for R8A77970 (R-Car V3M)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN3 + select SYSC_R8A77970 + help + This enables support for the Renesas R-Car V3M SoC. config ARCH_R8A77980 - bool "ARM64 Platform support for R-Car V3H" + bool "ARM64 Platform support for R8A77980 (R-Car V3H)" default y if ARCH_RENESAS select ARCH_RCAR_GEN3 select SYSC_R8A77980 help This enables support for the Renesas R-Car V3H SoC. -config ARCH_R8A77970 - bool "ARM64 Platform support for R-Car V3M" +config ARCH_R8A77990 + bool "ARM64 Platform support for R8A77990 (R-Car E3)" default y if ARCH_RENESAS select ARCH_RCAR_GEN3 - select SYSC_R8A77970 + select SYSC_R8A77990 help - This enables support for the Renesas R-Car V3M SoC. + This enables support for the Renesas R-Car E3 SoC. + This includes different gradings like R-Car E3e. + +config ARCH_R8A77995 + bool "ARM64 Platform support for R8A77995 (R-Car D3)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN3 + select SYSC_R8A77995 + help + This enables support for the Renesas R-Car D3 SoC. + This includes different gradings like R-Car D3e. config ARCH_R8A779A0 - bool "ARM64 Platform support for R-Car V3U" + bool "ARM64 Platform support for R8A779A0 (R-Car V3U)" default y if ARCH_RENESAS select ARCH_RCAR_GEN4 select SYSC_R8A779A0 help This enables support for the Renesas R-Car V3U SoC. +config ARCH_R8A779F0 + bool "ARM64 Platform support for R8A779F0 (R-Car S4-8)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN4 + select SYSC_R8A779F0 + help + This enables support for the Renesas R-Car S4-8 SoC. + config ARCH_R8A779G0 - bool "ARM64 Platform support for R-Car V4H" + bool "ARM64 Platform support for R8A779G0 (R-Car V4H)" default y if ARCH_RENESAS select ARCH_RCAR_GEN4 select SYSC_R8A779G0 @@ -309,68 +341,36 @@ config ARCH_R8A779G0 This enables support for the Renesas R-Car V4H SoC. config ARCH_R8A779H0 - bool "ARM64 Platform support for R-Car V4M" + bool "ARM64 Platform support for R8A779H0 (R-Car V4M)" default y if ARCH_RENESAS select ARCH_RCAR_GEN4 select SYSC_R8A779H0 help This enables support for the Renesas R-Car V4M SoC. -config ARCH_R8A774C0 - bool "ARM64 Platform support for RZ/G2E" - default y if ARCH_RENESAS - select ARCH_RCAR_GEN3 - select SYSC_R8A774C0 - help - This enables support for the Renesas RZ/G2E SoC. - -config ARCH_R8A774E1 - bool "ARM64 Platform support for RZ/G2H" - default y if ARCH_RENESAS - select ARCH_RCAR_GEN3 - select SYSC_R8A774E1 - help - This enables support for the Renesas RZ/G2H SoC. - -config ARCH_R8A774A1 - bool "ARM64 Platform support for RZ/G2M" - default y if ARCH_RENESAS - select ARCH_RCAR_GEN3 - select SYSC_R8A774A1 - help - This enables support for the Renesas RZ/G2M SoC. - -config ARCH_R8A774B1 - bool "ARM64 Platform support for RZ/G2N" - default y if ARCH_RENESAS - select ARCH_RCAR_GEN3 - select SYSC_R8A774B1 - help - This enables support for the Renesas RZ/G2N SoC. - config ARCH_R9A07G043 - bool "ARM64 Platform support for RZ/G2UL" + bool "ARM64 Platform support for R9A07G043U (RZ/G2UL)" default y if ARCH_RENESAS select ARCH_RZG2L help This enables support for the Renesas RZ/G2UL SoC variants. config ARCH_R9A07G044 - bool "ARM64 Platform support for RZ/G2L" + bool "ARM64 Platform support for R9A07G044 (RZ/G2L)" default y if ARCH_RENESAS select ARCH_RZG2L help This enables support for the Renesas RZ/G2L SoC variants. config ARCH_R9A07G054 - bool "ARM64 Platform support for RZ/V2L" + bool "ARM64 Platform support for R9A07G054 (RZ/V2L)" default y if ARCH_RENESAS select ARCH_RZG2L help This enables support for the Renesas RZ/V2L SoC variants. config ARCH_R9A08G045 - bool "ARM64 Platform support for RZ/G3S" + bool "ARM64 Platform support for R9A08G045 (RZ/G3S)" default y if ARCH_RENESAS select ARCH_RZG2L select SYSC_R9A08G045 @@ -378,7 +378,7 @@ config ARCH_R9A08G045 This enables support for the Renesas RZ/G3S SoC variants. config ARCH_R9A09G011 - bool "ARM64 Platform support for RZ/V2M" + bool "ARM64 Platform support for R9A09G011 (RZ/V2M)" default y if ARCH_RENESAS select PM select PM_GENERIC_DOMAINS @@ -387,33 +387,45 @@ config ARCH_R9A09G011 This enables support for the Renesas RZ/V2M SoC. config ARCH_R9A09G047 - bool "ARM64 Platform support for RZ/G3E" + bool "ARM64 Platform support for R9A09G047 (RZ/G3E)" default y if ARCH_RENESAS select SYS_R9A09G047 help This enables support for the Renesas RZ/G3E SoC variants. config ARCH_R9A09G056 - bool "ARM64 Platform support for RZ/V2N" + bool "ARM64 Platform support for R9A09G056 (RZ/V2N)" default y if ARCH_RENESAS select SYS_R9A09G056 help This enables support for the Renesas RZ/V2N SoC variants. config ARCH_R9A09G057 - bool "ARM64 Platform support for RZ/V2H(P)" + bool "ARM64 Platform support for R9A09G057 (RZ/V2H(P))" default y if ARCH_RENESAS select RENESAS_RZV2H_ICU select SYS_R9A09G057 help This enables support for the Renesas RZ/V2H(P) SoC variants. +config ARCH_R9A09G077 + bool "ARM64 Platform support for R9A09G077 (RZ/T2H)" + default y if ARCH_RENESAS + help + This enables support for the Renesas RZ/T2H SoC variants. + +config ARCH_R9A09G087 + bool "ARM64 Platform support for R9A09G087 (RZ/N2H)" + default y if ARCH_RENESAS + help + This enables support for the Renesas RZ/N2H SoC variants. + endif # ARM64 if RISCV config ARCH_R9A07G043 - bool "RISC-V Platform support for RZ/Five" + bool "RISC-V Platform support for R9A07G043F (RZ/Five)" depends on NONPORTABLE depends on !DMA_DIRECT_REMAP depends on RISCV_ALTERNATIVE @@ -439,19 +451,19 @@ config SYSC_RZ bool "System controller for RZ SoCs" if COMPILE_TEST config SYSC_R9A08G045 - bool "Renesas RZ/G3S System controller support" if COMPILE_TEST + bool "Renesas System controller support for R9A08G045 (RZ/G3S)" if COMPILE_TEST select SYSC_RZ config SYS_R9A09G047 - bool "Renesas RZ/G3E System controller support" if COMPILE_TEST + bool "Renesas System controller support for R9A09G047 (RZ/G3E)" if COMPILE_TEST select SYSC_RZ config SYS_R9A09G056 - bool "Renesas RZ/V2N System controller support" if COMPILE_TEST + bool "Renesas System controller support for R9A09G056 (RZ/V2N)" if COMPILE_TEST select SYSC_RZ config SYS_R9A09G057 - bool "Renesas RZ/V2H System controller support" if COMPILE_TEST + bool "Renesas System controller support for R9A09G057 (RZ/V2H)" if COMPILE_TEST select SYSC_RZ endif # SOC_RENESAS diff --git a/drivers/soc/renesas/pwc-rzv2m.c b/drivers/soc/renesas/pwc-rzv2m.c index 452cee8d68be..4dbcb3d4a90c 100644 --- a/drivers/soc/renesas/pwc-rzv2m.c +++ b/drivers/soc/renesas/pwc-rzv2m.c @@ -24,8 +24,8 @@ struct rzv2m_pwc_priv { DECLARE_BITMAP(ch_en_bits, 2); }; -static void rzv2m_pwc_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int rzv2m_pwc_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct rzv2m_pwc_priv *priv = gpiochip_get_data(chip); u32 reg; @@ -38,6 +38,8 @@ static void rzv2m_pwc_gpio_set(struct gpio_chip *chip, unsigned int offset, writel(reg, priv->base + PWC_GPIO); assign_bit(offset, priv->ch_en_bits, value); + + return 0; } static int rzv2m_pwc_gpio_get(struct gpio_chip *chip, unsigned int offset) @@ -62,7 +64,7 @@ static const struct gpio_chip rzv2m_pwc_gc = { .label = "gpio_rzv2m_pwc", .owner = THIS_MODULE, .get = rzv2m_pwc_gpio_get, - .set = rzv2m_pwc_gpio_set, + .set_rv = rzv2m_pwc_gpio_set, .direction_output = rzv2m_pwc_gpio_direction_output, .can_sleep = false, .ngpio = 2, diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig index 33512558af9f..bc532cbe32e7 100644 --- a/drivers/soc/tegra/Kconfig +++ b/drivers/soc/tegra/Kconfig @@ -138,6 +138,14 @@ config ARCH_TEGRA_241_SOC help Enable support for the NVIDIA Tegra241 SoC. +config ARCH_TEGRA_264_SOC + bool "NVIDIA Tegra264 SoC" + depends on !CPU_BIG_ENDIAN + select MAILBOX + select SOC_TEGRA_PMC + help + Enable support for the NVIDIA Tegra264 SoC. + endif endif diff --git a/drivers/soc/tegra/cbb/tegra194-cbb.c b/drivers/soc/tegra/cbb/tegra194-cbb.c index 846b17ffc2f9..c1bdea8c853f 100644 --- a/drivers/soc/tegra/cbb/tegra194-cbb.c +++ b/drivers/soc/tegra/cbb/tegra194-cbb.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved + * Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved * * The driver handles Error's from Control Backbone(CBB) generated due to * illegal accesses. When an error is reported from a NOC within CBB, @@ -138,7 +138,7 @@ struct tegra194_cbb_userbits { struct tegra194_cbb_noc_data { const char *name; bool erd_mask_inband_err; - const char * const *master_id; + const char * const *initiator_id; unsigned int max_aperture; const struct tegra194_cbb_aperture *noc_aperture; const char * const *routeid_initflow; @@ -216,7 +216,7 @@ static const char * const tegra194_axi2apb_error[] = { "CH2RFIFOF - Ch2 Request FIFO Full interrupt" }; -static const char * const tegra194_master_id[] = { +static const char * const tegra194_initiator_id[] = { [0x0] = "CCPLEX", [0x1] = "CCPLEX_DPMU", [0x2] = "BPMP", @@ -238,7 +238,7 @@ static const struct tegra_cbb_error tegra194_cbb_errors[] = { { .code = "SLV", .source = "Target", - .desc = "Target error detected by CBB slave" + .desc = "Target error detected by CBB target" }, { .code = "DEC", .source = "Initiator NIU", @@ -1774,8 +1774,8 @@ static void print_errlog5(struct seq_file *file, struct tegra194_cbb *cbb) tegra_cbb_print_err(file, "\t AXI ID\t\t: %#x\n", userbits.axi_id); } - tegra_cbb_print_err(file, "\t Master ID\t\t: %s\n", - cbb->noc->master_id[userbits.mstr_id]); + tegra_cbb_print_err(file, "\t Initiator ID\t\t: %s\n", + cbb->noc->initiator_id[userbits.mstr_id]); tegra_cbb_print_err(file, "\t Security Group(GRPSEC): %#x\n", userbits.grpsec); tegra_cbb_print_cache(file, userbits.axcache); tegra_cbb_print_prot(file, userbits.axprot); @@ -1837,14 +1837,14 @@ print_errlog1_2(struct seq_file *file, struct tegra194_cbb *cbb, /* * Print transcation type, error code and description from ErrLog0 for all - * errors. For NOC slave errors, all relevant error info is printed using + * errors. For NOC target errors, all relevant error info is printed using * ErrLog0 only. But additional information is printed for errors from - * APB slaves because for them: - * - All errors are logged as SLV(slave) errors due to APB having only single + * APB targets because for them: + * - All errors are logged as SLV(target) errors due to APB having only single * bit pslverr to report all errors. * - Exact cause is printed by reading DMAAPB_X_RAW_INTERRUPT_STATUS register. * - The driver prints information showing AXI2APB bridge and exact error - * only if there is error in any AXI2APB slave. + * only if there is error in any AXI2APB target. * - There is still no way to disambiguate a DEC error from SLV error type. */ static bool print_errlog0(struct seq_file *file, struct tegra194_cbb *cbb) @@ -1884,8 +1884,8 @@ static bool print_errlog0(struct seq_file *file, struct tegra194_cbb *cbb) /* For all SLV errors, read DMAAPB_X_RAW_INTERRUPT_STATUS * register to get error status for all AXI2APB bridges. * Print bridge details if a bit is set in a bridge's - * status register due to error in a APB slave connected - * to that bridge. For other NOC slaves, none of the status + * status register due to error in a APB target connected + * to that bridge. For other NOC targets, none of the status * register will be set. */ @@ -2118,7 +2118,7 @@ static const struct tegra_cbb_ops tegra194_cbb_ops = { static struct tegra194_cbb_noc_data tegra194_cbb_central_noc_data = { .name = "cbb-noc", .erd_mask_inband_err = true, - .master_id = tegra194_master_id, + .initiator_id = tegra194_initiator_id, .noc_aperture = tegra194_cbbcentralnoc_apert_lookup, .max_aperture = ARRAY_SIZE(tegra194_cbbcentralnoc_apert_lookup), .routeid_initflow = tegra194_cbbcentralnoc_routeid_initflow, @@ -2130,7 +2130,7 @@ static struct tegra194_cbb_noc_data tegra194_cbb_central_noc_data = { static struct tegra194_cbb_noc_data tegra194_aon_noc_data = { .name = "aon-noc", .erd_mask_inband_err = false, - .master_id = tegra194_master_id, + .initiator_id = tegra194_initiator_id, .noc_aperture = tegra194_aonnoc_aperture_lookup, .max_aperture = ARRAY_SIZE(tegra194_aonnoc_aperture_lookup), .routeid_initflow = tegra194_aonnoc_routeid_initflow, @@ -2142,7 +2142,7 @@ static struct tegra194_cbb_noc_data tegra194_aon_noc_data = { static struct tegra194_cbb_noc_data tegra194_bpmp_noc_data = { .name = "bpmp-noc", .erd_mask_inband_err = false, - .master_id = tegra194_master_id, + .initiator_id = tegra194_initiator_id, .noc_aperture = tegra194_bpmpnoc_apert_lookup, .max_aperture = ARRAY_SIZE(tegra194_bpmpnoc_apert_lookup), .routeid_initflow = tegra194_bpmpnoc_routeid_initflow, @@ -2154,7 +2154,7 @@ static struct tegra194_cbb_noc_data tegra194_bpmp_noc_data = { static struct tegra194_cbb_noc_data tegra194_rce_noc_data = { .name = "rce-noc", .erd_mask_inband_err = false, - .master_id = tegra194_master_id, + .initiator_id = tegra194_initiator_id, .noc_aperture = tegra194_scenoc_apert_lookup, .max_aperture = ARRAY_SIZE(tegra194_scenoc_apert_lookup), .routeid_initflow = tegra194_scenoc_routeid_initflow, @@ -2166,7 +2166,7 @@ static struct tegra194_cbb_noc_data tegra194_rce_noc_data = { static struct tegra194_cbb_noc_data tegra194_sce_noc_data = { .name = "sce-noc", .erd_mask_inband_err = false, - .master_id = tegra194_master_id, + .initiator_id = tegra194_initiator_id, .noc_aperture = tegra194_scenoc_apert_lookup, .max_aperture = ARRAY_SIZE(tegra194_scenoc_apert_lookup), .routeid_initflow = tegra194_scenoc_routeid_initflow, diff --git a/drivers/soc/tegra/cbb/tegra234-cbb.c b/drivers/soc/tegra/cbb/tegra234-cbb.c index c74629af9bb5..a9adbcecd47c 100644 --- a/drivers/soc/tegra/cbb/tegra234-cbb.c +++ b/drivers/soc/tegra/cbb/tegra234-cbb.c @@ -1,13 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved + * Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved * * The driver handles Error's from Control Backbone(CBB) version 2.0. * generated due to illegal accesses. The driver prints debug information * about failed transaction on receiving interrupt from Error Notifier. * Error types supported by CBB2.0 are: * UNSUPPORTED_ERR, PWRDOWN_ERR, TIMEOUT_ERR, FIREWALL_ERR, DECODE_ERR, - * SLAVE_ERR + * TARGET_ERR */ #include <linux/acpi.h> @@ -30,18 +30,22 @@ #define FABRIC_EN_CFG_ADDR_LOW_0 0x80 #define FABRIC_EN_CFG_ADDR_HI_0 0x84 -#define FABRIC_MN_MASTER_ERR_EN_0 0x200 -#define FABRIC_MN_MASTER_ERR_FORCE_0 0x204 -#define FABRIC_MN_MASTER_ERR_STATUS_0 0x208 -#define FABRIC_MN_MASTER_ERR_OVERFLOW_STATUS_0 0x20c +#define FABRIC_EN_CFG_TARGET_NODE_ADDR_INDEX_0_0 0x100 +#define FABRIC_EN_CFG_TARGET_NODE_ADDR_LOW_0 0x140 +#define FABRIC_EN_CFG_TARGET_NODE_ADDR_HI_0 0x144 -#define FABRIC_MN_MASTER_LOG_ERR_STATUS_0 0x300 -#define FABRIC_MN_MASTER_LOG_ADDR_LOW_0 0x304 -#define FABRIC_MN_MASTER_LOG_ADDR_HIGH_0 0x308 -#define FABRIC_MN_MASTER_LOG_ATTRIBUTES0_0 0x30c -#define FABRIC_MN_MASTER_LOG_ATTRIBUTES1_0 0x310 -#define FABRIC_MN_MASTER_LOG_ATTRIBUTES2_0 0x314 -#define FABRIC_MN_MASTER_LOG_USER_BITS0_0 0x318 +#define FABRIC_MN_INITIATOR_ERR_EN_0 0x200 +#define FABRIC_MN_INITIATOR_ERR_FORCE_0 0x204 +#define FABRIC_MN_INITIATOR_ERR_STATUS_0 0x208 +#define FABRIC_MN_INITIATOR_ERR_OVERFLOW_STATUS_0 0x20c + +#define FABRIC_MN_INITIATOR_LOG_ERR_STATUS_0 0x300 +#define FABRIC_MN_INITIATOR_LOG_ADDR_LOW_0 0x304 +#define FABRIC_MN_INITIATOR_LOG_ADDR_HIGH_0 0x308 +#define FABRIC_MN_INITIATOR_LOG_ATTRIBUTES0_0 0x30c +#define FABRIC_MN_INITIATOR_LOG_ATTRIBUTES1_0 0x310 +#define FABRIC_MN_INITIATOR_LOG_ATTRIBUTES2_0 0x314 +#define FABRIC_MN_INITIATOR_LOG_USER_BITS0_0 0x318 #define AXI_SLV_TIMEOUT_STATUS_0_0 0x8 #define APB_BLOCK_TMO_STATUS_0 0xc00 @@ -53,7 +57,7 @@ #define FAB_EM_EL_FALCONSEC GENMASK(1, 0) #define FAB_EM_EL_FABID GENMASK(20, 16) -#define FAB_EM_EL_SLAVEID GENMASK(7, 0) +#define FAB_EM_EL_TARGETID GENMASK(7, 0) #define FAB_EM_EL_ACCESSID GENMASK(7, 0) @@ -74,34 +78,79 @@ #define WEN 0x20000 enum tegra234_cbb_fabric_ids { - CBB_FAB_ID, - SCE_FAB_ID, - RCE_FAB_ID, - DCE_FAB_ID, - AON_FAB_ID, - PSC_FAB_ID, - BPMP_FAB_ID, - FSI_FAB_ID, - MAX_FAB_ID, + T234_CBB_FABRIC_ID, + T234_SCE_FABRIC_ID, + T234_RCE_FABRIC_ID, + T234_DCE_FABRIC_ID, + T234_AON_FABRIC_ID, + T234_PSC_FABRIC_ID, + T234_BPMP_FABRIC_ID, + T234_FSI_FABRIC_ID, + T234_MAX_FABRIC_ID, +}; + +enum tegra264_cbb_fabric_ids { + T264_SYSTEM_CBB_FABRIC_ID, + T264_TOP_0_CBB_FABRIC_ID, + T264_VISION_CBB_FABRIC_ID, + T264_DISP_USB_CBB_FABRIC_ID, + T264_UPHY0_CBB_FABRIC_ID, + T264_RSVD0_FABRIC_ID, + T264_RSVD1_FABRIC_ID, + T264_RSVD2_FABRIC_ID, + T264_RSVD3_FABRIC_ID, + T264_RSVD4_FABRIC_ID, + T264_RSVD5_FABRIC_ID, + T264_AON_FABRIC_ID, + T264_PSC_FABRIC_ID, + T264_OESP_FABRIC_ID, + T264_APE_FABRIC_ID, + T264_BPMP_FABRIC_ID, + T264_RCE_0_FABRIC_ID, + T264_RCE_1_FABRIC_ID, + T264_RSVD6_FABRIC_ID, + T264_DCE_FABRIC_ID, + T264_FSI_FABRIC_ID, + T264_ISC_FABRIC_ID, + T264_SB_FABRIC_ID, + T264_ISC_CPU_FABRIC_ID, + T264_RSVD7_FABRIC_ID, +}; + +enum t254_cbb_fabric_ids { + T254_DCE_FABRIC_ID = 19, + T254_DISP_CLUSTER_FABRIC_ID = 25, + T254_C2C_FABRIC_ID = 26, + T254_GPU_FABRIC_ID = 27, + T254_DISP_CLUSTER_1_FABRIC_ID = 28, + T254_MAX_FABRIC_ID, }; -struct tegra234_slave_lookup { +struct tegra234_target_lookup { const char *name; unsigned int offset; }; -struct tegra234_cbb_fabric { +struct tegra234_fabric_lookup { const char *name; + bool is_lookup; + const struct tegra234_target_lookup *target_map; + const int max_targets; +}; + +struct tegra234_cbb_fabric { + int fab_id; phys_addr_t off_mask_erd; phys_addr_t firewall_base; unsigned int firewall_ctl; unsigned int firewall_wr_ctl; - const char * const *master_id; + const char * const *initiator_id; unsigned int notifier_offset; const struct tegra_cbb_error *errors; const int max_errors; - const struct tegra234_slave_lookup *slave_map; - const int max_slaves; + const struct tegra234_fabric_lookup *fab_list; + const u32 err_intr_enbl; + const u32 err_status_clr; }; struct tegra234_cbb { @@ -177,7 +226,7 @@ static void tegra234_cbb_fault_enable(struct tegra_cbb *cbb) void __iomem *addr; addr = priv->regs + priv->fabric->notifier_offset; - writel(0x1ff, addr + FABRIC_EN_CFG_INTERRUPT_ENABLE_0_0); + writel(priv->fabric->err_intr_enbl, addr + FABRIC_EN_CFG_INTERRUPT_ENABLE_0_0); dsb(sy); } @@ -185,7 +234,9 @@ static void tegra234_cbb_error_clear(struct tegra_cbb *cbb) { struct tegra234_cbb *priv = to_tegra234_cbb(cbb); - writel(0x3f, priv->mon + FABRIC_MN_MASTER_ERR_STATUS_0); + writel(0, priv->mon + FABRIC_MN_INITIATOR_ERR_FORCE_0); + + writel(priv->fabric->err_status_clr, priv->mon + FABRIC_MN_INITIATOR_ERR_STATUS_0); dsb(sy); } @@ -216,13 +267,13 @@ static u32 tegra234_cbb_get_tmo_slv(void __iomem *addr) return timeout; } -static void tegra234_cbb_tmo_slv(struct seq_file *file, const char *slave, void __iomem *addr, +static void tegra234_cbb_tmo_slv(struct seq_file *file, const char *target, void __iomem *addr, u32 status) { - tegra_cbb_print_err(file, "\t %s : %#x\n", slave, status); + tegra_cbb_print_err(file, "\t %s : %#x\n", target, status); } -static void tegra234_cbb_lookup_apbslv(struct seq_file *file, const char *slave, +static void tegra234_cbb_lookup_apbslv(struct seq_file *file, const char *target, void __iomem *base) { unsigned int block = 0; @@ -232,7 +283,7 @@ static void tegra234_cbb_lookup_apbslv(struct seq_file *file, const char *slave, status = tegra234_cbb_get_tmo_slv(base); if (status) - tegra_cbb_print_err(file, "\t %s_BLOCK_TMO_STATUS : %#x\n", slave, status); + tegra_cbb_print_err(file, "\t %s_BLOCK_TMO_STATUS : %#x\n", target, status); while (status) { if (status & BIT(0)) { @@ -247,7 +298,7 @@ static void tegra234_cbb_lookup_apbslv(struct seq_file *file, const char *slave, if (clients != 0xffffffff) clients &= BIT(client); - sprintf(name, "%s_BLOCK%d_TMO", slave, block); + sprintf(name, "%s_BLOCK%d_TMO", target, block); tegra234_cbb_tmo_slv(file, name, addr, clients); } @@ -262,16 +313,21 @@ static void tegra234_cbb_lookup_apbslv(struct seq_file *file, const char *slave, } } -static void tegra234_lookup_slave_timeout(struct seq_file *file, struct tegra234_cbb *cbb, - u8 slave_id, u8 fab_id) +static void tegra234_sw_lookup_target_timeout(struct seq_file *file, struct tegra234_cbb *cbb, + u8 target_id, u8 fab_id) { - const struct tegra234_slave_lookup *map = cbb->fabric->slave_map; + const struct tegra234_target_lookup *map = cbb->fabric->fab_list[fab_id].target_map; void __iomem *addr; + if (target_id >= cbb->fabric->fab_list[fab_id].max_targets) { + tegra_cbb_print_err(file, "\t Invalid target_id:%d\n", target_id); + return; + } + /* - * 1) Get slave node name and address mapping using slave_id. - * 2) Check if the timed out slave node is APB or AXI. - * 3) If AXI, then print timeout register and reset axi slave + * 1) Get target node name and address mapping using target_id. + * 2) Check if the timed out target node is APB or AXI. + * 3) If AXI, then print timeout register and reset axi target * using <FABRIC>_SN_<>_SLV_TIMEOUT_STATUS_0_0 register. * 4) If APB, then perform an additional lookup to find the client * which timed out. @@ -285,12 +341,12 @@ static void tegra234_lookup_slave_timeout(struct seq_file *file, struct tegra234 * e) Goto step-a till all bits are set. */ - addr = cbb->regs + map[slave_id].offset; + addr = cbb->regs + map[target_id].offset; - if (strstr(map[slave_id].name, "AXI2APB")) { + if (strstr(map[target_id].name, "AXI2APB")) { addr += APB_BLOCK_TMO_STATUS_0; - tegra234_cbb_lookup_apbslv(file, map[slave_id].name, addr); + tegra234_cbb_lookup_apbslv(file, map[target_id].name, addr); } else { char name[64]; u32 status; @@ -299,12 +355,29 @@ static void tegra234_lookup_slave_timeout(struct seq_file *file, struct tegra234 status = tegra234_cbb_get_tmo_slv(addr); if (status) { - sprintf(name, "%s_SLV_TIMEOUT_STATUS", map[slave_id].name); + sprintf(name, "%s_SLV_TIMEOUT_STATUS", map[target_id].name); tegra234_cbb_tmo_slv(file, name, addr, status); } } } +static void tegra234_hw_lookup_target_timeout(struct seq_file *file, struct tegra234_cbb *cbb, + u8 target_id, u8 fab_id) +{ + unsigned int notifier = cbb->fabric->notifier_offset; + u32 hi, lo; + u64 addr; + + writel(target_id, cbb->regs + notifier + FABRIC_EN_CFG_TARGET_NODE_ADDR_INDEX_0_0); + + hi = readl(cbb->regs + notifier + FABRIC_EN_CFG_TARGET_NODE_ADDR_HI_0); + lo = readl(cbb->regs + notifier + FABRIC_EN_CFG_TARGET_NODE_ADDR_LOW_0); + + addr = (u64)hi << 32 | lo; + + tegra_cbb_print_err(file, "\t Target Node Addr : %#llx\n", addr); +} + static void tegra234_cbb_print_error(struct seq_file *file, struct tegra234_cbb *cbb, u32 status, u32 overflow) { @@ -349,8 +422,7 @@ static void tegra234_cbb_print_error(struct seq_file *file, struct tegra234_cbb static void print_errlog_err(struct seq_file *file, struct tegra234_cbb *cbb) { u8 cache_type, prot_type, burst_length, mstr_id, grpsec, vqc, falconsec, beat_size; - u8 access_type, access_id, requester_socket_id, local_socket_id, slave_id, fab_id; - char fabric_name[20]; + u8 access_type, access_id, requester_socket_id, local_socket_id, target_id, fab_id; bool is_numa = false; u8 burst_type; @@ -364,7 +436,7 @@ static void print_errlog_err(struct seq_file *file, struct tegra234_cbb *cbb) /* * For SOC with multiple NUMA nodes, print cross socket access - * errors only if initiator/master_id is CCPLEX, CPMU or GPU. + * errors only if initiator_id is CCPLEX, CPMU or GPU. */ if (is_numa) { local_socket_id = numa_node_id(); @@ -377,7 +449,7 @@ static void print_errlog_err(struct seq_file *file, struct tegra234_cbb *cbb) } fab_id = FIELD_GET(FAB_EM_EL_FABID, cbb->mn_attr2); - slave_id = FIELD_GET(FAB_EM_EL_SLAVEID, cbb->mn_attr2); + target_id = FIELD_GET(FAB_EM_EL_TARGETID, cbb->mn_attr2); access_id = FIELD_GET(FAB_EM_EL_ACCESSID, cbb->mn_attr1); @@ -395,21 +467,18 @@ static void print_errlog_err(struct seq_file *file, struct tegra234_cbb *cbb) else tegra_cbb_print_err(file, "\t Wrong type index:%u\n", cbb->type); - tegra_cbb_print_err(file, "\t MASTER_ID\t\t: %s\n", cbb->fabric->master_id[mstr_id]); + tegra_cbb_print_err(file, "\t Initiator_Id\t\t: %#x\n", mstr_id); + if (cbb->fabric->initiator_id) + tegra_cbb_print_err(file, "\t Initiator\t\t: %s\n", + cbb->fabric->initiator_id[mstr_id]); + tegra_cbb_print_err(file, "\t Address\t\t: %#llx\n", cbb->access); tegra_cbb_print_cache(file, cache_type); tegra_cbb_print_prot(file, prot_type); tegra_cbb_print_err(file, "\t Access_Type\t\t: %s", (access_type) ? "Write\n" : "Read\n"); - tegra_cbb_print_err(file, "\t Access_ID\t\t: %#x", access_id); - - if (fab_id == PSC_FAB_ID) - strcpy(fabric_name, "psc-fabric"); - else if (fab_id == FSI_FAB_ID) - strcpy(fabric_name, "fsi-fabric"); - else - strcpy(fabric_name, cbb->fabric->name); + tegra_cbb_print_err(file, "\t Access_ID\t\t: %#x\n", access_id); if (is_numa) { tegra_cbb_print_err(file, "\t Requester_Socket_Id\t: %#x\n", @@ -420,8 +489,21 @@ static void print_errlog_err(struct seq_file *file, struct tegra234_cbb *cbb) num_possible_nodes()); } - tegra_cbb_print_err(file, "\t Fabric\t\t: %s\n", fabric_name); - tegra_cbb_print_err(file, "\t Slave_Id\t\t: %#x\n", slave_id); + tegra_cbb_print_err(file, "\t Fabric\t\t: %s (id:%#x)\n", + cbb->fabric->fab_list[fab_id].name, fab_id); + + if (of_machine_is_compatible("nvidia,tegra264") && fab_id == T264_UPHY0_CBB_FABRIC_ID) { + /* + * In T264, AON Fabric ID value is incorrectly same as UPHY0 fabric ID. + * For 'ID = 0x4', we must check for the address which caused the error + * to find the correct fabric which returned error. + */ + tegra_cbb_print_err(file, "\t or Fabric\t\t: %s\n", + cbb->fabric->fab_list[T264_AON_FABRIC_ID].name); + tegra_cbb_print_err(file, "\t Please use Address to determine correct fabric.\n"); + } + + tegra_cbb_print_err(file, "\t Target_Id\t\t: %#x\n", target_id); tegra_cbb_print_err(file, "\t Burst_length\t\t: %#x\n", burst_length); tegra_cbb_print_err(file, "\t Burst_type\t\t: %#x\n", burst_type); tegra_cbb_print_err(file, "\t Beat_size\t\t: %#x\n", beat_size); @@ -429,27 +511,30 @@ static void print_errlog_err(struct seq_file *file, struct tegra234_cbb *cbb) tegra_cbb_print_err(file, "\t GRPSEC\t\t: %#x\n", grpsec); tegra_cbb_print_err(file, "\t FALCONSEC\t\t: %#x\n", falconsec); - if ((fab_id == PSC_FAB_ID) || (fab_id == FSI_FAB_ID)) + if (!cbb->fabric->fab_list[fab_id].is_lookup) return; - if (slave_id >= cbb->fabric->max_slaves) { - tegra_cbb_print_err(file, "\t Invalid slave_id:%d\n", slave_id); - return; - } - + /* + * If is_lookup field is set in fabric_lookup table of soc data, it + * means that address lookup of target is supported for Timeout errors. + * If is_lookup is set and the target_map is not populated making + * max_targets as zero, then it means HW lookup is to be performed. + */ if (!strcmp(cbb->fabric->errors[cbb->type].code, "TIMEOUT_ERR")) { - tegra234_lookup_slave_timeout(file, cbb, slave_id, fab_id); - return; + if (cbb->fabric->fab_list[fab_id].max_targets) + tegra234_sw_lookup_target_timeout(file, cbb, target_id, fab_id); + else + tegra234_hw_lookup_target_timeout(file, cbb, target_id, fab_id); } - tegra_cbb_print_err(file, "\t Slave\t\t\t: %s\n", cbb->fabric->slave_map[slave_id].name); + return; } static int print_errmonX_info(struct seq_file *file, struct tegra234_cbb *cbb) { u32 overflow, status, error; - status = readl(cbb->mon + FABRIC_MN_MASTER_ERR_STATUS_0); + status = readl(cbb->mon + FABRIC_MN_INITIATOR_ERR_STATUS_0); if (!status) { pr_err("Error Notifier received a spurious notification\n"); return -ENODATA; @@ -460,11 +545,11 @@ static int print_errmonX_info(struct seq_file *file, struct tegra234_cbb *cbb) return -EINVAL; } - overflow = readl(cbb->mon + FABRIC_MN_MASTER_ERR_OVERFLOW_STATUS_0); + overflow = readl(cbb->mon + FABRIC_MN_INITIATOR_ERR_OVERFLOW_STATUS_0); tegra234_cbb_print_error(file, cbb, status, overflow); - error = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ERR_STATUS_0); + error = readl(cbb->mon + FABRIC_MN_INITIATOR_LOG_ERR_STATUS_0); if (!error) { pr_info("Error Monitor doesn't have Error Logger\n"); return -EINVAL; @@ -476,15 +561,15 @@ static int print_errmonX_info(struct seq_file *file, struct tegra234_cbb *cbb) if (error & BIT(0)) { u32 hi, lo; - hi = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ADDR_HIGH_0); - lo = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ADDR_LOW_0); + hi = readl(cbb->mon + FABRIC_MN_INITIATOR_LOG_ADDR_HIGH_0); + lo = readl(cbb->mon + FABRIC_MN_INITIATOR_LOG_ADDR_LOW_0); cbb->access = (u64)hi << 32 | lo; - cbb->mn_attr0 = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ATTRIBUTES0_0); - cbb->mn_attr1 = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ATTRIBUTES1_0); - cbb->mn_attr2 = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ATTRIBUTES2_0); - cbb->mn_user_bits = readl(cbb->mon + FABRIC_MN_MASTER_LOG_USER_BITS0_0); + cbb->mn_attr0 = readl(cbb->mon + FABRIC_MN_INITIATOR_LOG_ATTRIBUTES0_0); + cbb->mn_attr1 = readl(cbb->mon + FABRIC_MN_INITIATOR_LOG_ATTRIBUTES1_0); + cbb->mn_attr2 = readl(cbb->mon + FABRIC_MN_INITIATOR_LOG_ATTRIBUTES2_0); + cbb->mn_user_bits = readl(cbb->mon + FABRIC_MN_INITIATOR_LOG_USER_BITS0_0); print_errlog_err(file, cbb); } @@ -503,7 +588,7 @@ static int print_err_notifier(struct seq_file *file, struct tegra234_cbb *cbb, u pr_crit("**************************************\n"); pr_crit("CPU:%d, Error:%s, Errmon:%d\n", smp_processor_id(), - cbb->fabric->name, status); + cbb->fabric->fab_list[cbb->fabric->fab_id].name, status); while (status) { if (status & BIT(0)) { @@ -526,13 +611,13 @@ static int print_err_notifier(struct seq_file *file, struct tegra234_cbb *cbb, u tegra234_cbb_error_clear(&cbb->base); if (err) return err; + tegra_cbb_print_err(file, "\t**************************************\n"); } status >>= 1; index++; } - tegra_cbb_print_err(file, "\t**************************************\n"); return 0; } @@ -581,7 +666,8 @@ static irqreturn_t tegra234_cbb_isr(int irq, void *data) if (status && (irq == priv->sec_irq)) { tegra_cbb_print_err(NULL, "CPU:%d, Error: %s@0x%llx, irq=%d\n", - smp_processor_id(), priv->fabric->name, + smp_processor_id(), + priv->fabric->fab_list[priv->fabric->fab_id].name, priv->res->start, irq); err = print_err_notifier(NULL, priv, status); @@ -589,7 +675,7 @@ static irqreturn_t tegra234_cbb_isr(int irq, void *data) goto unlock; /* - * If illegal request is from CCPLEX(id:0x1) master then call WARN() + * If illegal request is from CCPLEX(id:0x1) initiator then call WARN() */ if (priv->fabric->off_mask_erd) { mstr_id = FIELD_GET(USRBITS_MSTR_ID, priv->mn_user_bits); @@ -641,7 +727,7 @@ static const struct tegra_cbb_ops tegra234_cbb_ops = { #endif }; -static const char * const tegra234_master_id[] = { +static const char * const tegra234_initiator_id[] = { [0x00] = "TZ", [0x01] = "CCPLEX", [0x02] = "CCPMU", @@ -672,8 +758,8 @@ static const char * const tegra234_master_id[] = { static const struct tegra_cbb_error tegra234_cbb_errors[] = { { - .code = "SLAVE_ERR", - .desc = "Slave being accessed responded with an error" + .code = "TARGET_ERR", + .desc = "Target being accessed responded with an error" }, { .code = "DECODE_ERR", .desc = "Attempt to access an address hole" @@ -682,37 +768,24 @@ static const struct tegra_cbb_error tegra234_cbb_errors[] = { .desc = "Attempt to access a region which is firewall protected" }, { .code = "TIMEOUT_ERR", - .desc = "No response returned by slave" + .desc = "No response returned by target" }, { .code = "PWRDOWN_ERR", .desc = "Attempt to access a portion of fabric that is powered down" }, { .code = "UNSUPPORTED_ERR", - .desc = "Attempt to access a slave through an unsupported access" + .desc = "Attempt to access a target through an unsupported access" } }; -static const struct tegra234_slave_lookup tegra234_aon_slave_map[] = { +static const struct tegra234_target_lookup tegra234_aon_target_map[] = { { "AXI2APB", 0x00000 }, { "AST", 0x14000 }, { "CBB", 0x15000 }, { "CPU", 0x16000 }, }; -static const struct tegra234_cbb_fabric tegra234_aon_fabric = { - .name = "aon-fabric", - .master_id = tegra234_master_id, - .slave_map = tegra234_aon_slave_map, - .max_slaves = ARRAY_SIZE(tegra234_aon_slave_map), - .errors = tegra234_cbb_errors, - .max_errors = ARRAY_SIZE(tegra234_cbb_errors), - .notifier_offset = 0x17000, - .firewall_base = 0x30000, - .firewall_ctl = 0x8d0, - .firewall_wr_ctl = 0x8c8, -}; - -static const struct tegra234_slave_lookup tegra234_bpmp_slave_map[] = { +static const struct tegra234_target_lookup tegra234_bpmp_target_map[] = { { "AXI2APB", 0x00000 }, { "AST0", 0x15000 }, { "AST1", 0x16000 }, @@ -720,20 +793,16 @@ static const struct tegra234_slave_lookup tegra234_bpmp_slave_map[] = { { "CPU", 0x18000 }, }; -static const struct tegra234_cbb_fabric tegra234_bpmp_fabric = { - .name = "bpmp-fabric", - .master_id = tegra234_master_id, - .slave_map = tegra234_bpmp_slave_map, - .max_slaves = ARRAY_SIZE(tegra234_bpmp_slave_map), - .errors = tegra234_cbb_errors, - .max_errors = ARRAY_SIZE(tegra234_cbb_errors), - .notifier_offset = 0x19000, - .firewall_base = 0x30000, - .firewall_ctl = 0x8f0, - .firewall_wr_ctl = 0x8e8, +static const struct tegra234_target_lookup tegra234_common_target_map[] = { + { "AXI2APB", 0x00000 }, + { "AST0", 0x15000 }, + { "AST1", 0x16000 }, + { "CBB", 0x17000 }, + { "RSVD", 0x00000 }, + { "CPU", 0x18000 }, }; -static const struct tegra234_slave_lookup tegra234_cbb_slave_map[] = { +static const struct tegra234_target_lookup tegra234_cbb_target_map[] = { { "AON", 0x40000 }, { "BPMP", 0x41000 }, { "CBB", 0x42000 }, @@ -797,13 +866,65 @@ static const struct tegra234_slave_lookup tegra234_cbb_slave_map[] = { { "AXI2APB_3", 0x91000 }, }; +static const struct tegra234_fabric_lookup tegra234_cbb_fab_list[] = { + [T234_CBB_FABRIC_ID] = { "cbb-fabric", true, + tegra234_cbb_target_map, + ARRAY_SIZE(tegra234_cbb_target_map) }, + [T234_SCE_FABRIC_ID] = { "sce-fabric", true, + tegra234_common_target_map, + ARRAY_SIZE(tegra234_common_target_map) }, + [T234_RCE_FABRIC_ID] = { "rce-fabric", true, + tegra234_common_target_map, + ARRAY_SIZE(tegra234_common_target_map) }, + [T234_DCE_FABRIC_ID] = { "dce-fabric", true, + tegra234_common_target_map, + ARRAY_SIZE(tegra234_common_target_map) }, + [T234_AON_FABRIC_ID] = { "aon-fabric", true, + tegra234_aon_target_map, + ARRAY_SIZE(tegra234_bpmp_target_map) }, + [T234_PSC_FABRIC_ID] = { "psc-fabric" }, + [T234_BPMP_FABRIC_ID] = { "bpmp-fabric", true, + tegra234_bpmp_target_map, + ARRAY_SIZE(tegra234_bpmp_target_map) }, + [T234_FSI_FABRIC_ID] = { "fsi-fabric" }, +}; + +static const struct tegra234_cbb_fabric tegra234_aon_fabric = { + .fab_id = T234_AON_FABRIC_ID, + .fab_list = tegra234_cbb_fab_list, + .initiator_id = tegra234_initiator_id, + .errors = tegra234_cbb_errors, + .max_errors = ARRAY_SIZE(tegra234_cbb_errors), + .err_intr_enbl = 0x7, + .err_status_clr = 0x3f, + .notifier_offset = 0x17000, + .firewall_base = 0x30000, + .firewall_ctl = 0x8d0, + .firewall_wr_ctl = 0x8c8, +}; + +static const struct tegra234_cbb_fabric tegra234_bpmp_fabric = { + .fab_id = T234_BPMP_FABRIC_ID, + .fab_list = tegra234_cbb_fab_list, + .initiator_id = tegra234_initiator_id, + .errors = tegra234_cbb_errors, + .max_errors = ARRAY_SIZE(tegra234_cbb_errors), + .err_intr_enbl = 0xf, + .err_status_clr = 0x3f, + .notifier_offset = 0x19000, + .firewall_base = 0x30000, + .firewall_ctl = 0x8f0, + .firewall_wr_ctl = 0x8e8, +}; + static const struct tegra234_cbb_fabric tegra234_cbb_fabric = { - .name = "cbb-fabric", - .master_id = tegra234_master_id, - .slave_map = tegra234_cbb_slave_map, - .max_slaves = ARRAY_SIZE(tegra234_cbb_slave_map), + .fab_id = T234_CBB_FABRIC_ID, + .fab_list = tegra234_cbb_fab_list, + .initiator_id = tegra234_initiator_id, .errors = tegra234_cbb_errors, .max_errors = ARRAY_SIZE(tegra234_cbb_errors), + .err_intr_enbl = 0x7f, + .err_status_clr = 0x3f, .notifier_offset = 0x60000, .off_mask_erd = 0x3a004, .firewall_base = 0x10000, @@ -811,22 +932,14 @@ static const struct tegra234_cbb_fabric tegra234_cbb_fabric = { .firewall_wr_ctl = 0x23e8, }; -static const struct tegra234_slave_lookup tegra234_common_slave_map[] = { - { "AXI2APB", 0x00000 }, - { "AST0", 0x15000 }, - { "AST1", 0x16000 }, - { "CBB", 0x17000 }, - { "RSVD", 0x00000 }, - { "CPU", 0x18000 }, -}; - static const struct tegra234_cbb_fabric tegra234_dce_fabric = { - .name = "dce-fabric", - .master_id = tegra234_master_id, - .slave_map = tegra234_common_slave_map, - .max_slaves = ARRAY_SIZE(tegra234_common_slave_map), + .fab_id = T234_DCE_FABRIC_ID, + .fab_list = tegra234_cbb_fab_list, + .initiator_id = tegra234_initiator_id, .errors = tegra234_cbb_errors, .max_errors = ARRAY_SIZE(tegra234_cbb_errors), + .err_intr_enbl = 0xf, + .err_status_clr = 0x3f, .notifier_offset = 0x19000, .firewall_base = 0x30000, .firewall_ctl = 0x290, @@ -834,12 +947,13 @@ static const struct tegra234_cbb_fabric tegra234_dce_fabric = { }; static const struct tegra234_cbb_fabric tegra234_rce_fabric = { - .name = "rce-fabric", - .master_id = tegra234_master_id, - .slave_map = tegra234_common_slave_map, - .max_slaves = ARRAY_SIZE(tegra234_common_slave_map), + .fab_id = T234_RCE_FABRIC_ID, + .fab_list = tegra234_cbb_fab_list, + .initiator_id = tegra234_initiator_id, .errors = tegra234_cbb_errors, .max_errors = ARRAY_SIZE(tegra234_cbb_errors), + .err_intr_enbl = 0xf, + .err_status_clr = 0x3f, .notifier_offset = 0x19000, .firewall_base = 0x30000, .firewall_ctl = 0x290, @@ -847,19 +961,20 @@ static const struct tegra234_cbb_fabric tegra234_rce_fabric = { }; static const struct tegra234_cbb_fabric tegra234_sce_fabric = { - .name = "sce-fabric", - .master_id = tegra234_master_id, - .slave_map = tegra234_common_slave_map, - .max_slaves = ARRAY_SIZE(tegra234_common_slave_map), + .fab_id = T234_SCE_FABRIC_ID, + .fab_list = tegra234_cbb_fab_list, + .initiator_id = tegra234_initiator_id, .errors = tegra234_cbb_errors, .max_errors = ARRAY_SIZE(tegra234_cbb_errors), + .err_intr_enbl = 0xf, + .err_status_clr = 0x3f, .notifier_offset = 0x19000, .firewall_base = 0x30000, .firewall_ctl = 0x290, .firewall_wr_ctl = 0x288, }; -static const char * const tegra241_master_id[] = { +static const char * const tegra241_initiator_id[] = { [0x0] = "TZ", [0x1] = "CCPLEX", [0x2] = "CCPMU", @@ -877,22 +992,22 @@ static const char * const tegra241_master_id[] = { }; /* - * Possible causes for Slave and Timeout errors. - * SLAVE_ERR: - * Slave being accessed responded with an error. Slave could return + * Possible causes for Target and Timeout errors. + * TARGET_ERR: + * Target being accessed responded with an error. Target could return * an error for various cases : * Unsupported access, clamp setting when power gated, register - * level firewall(SCR), address hole within the slave, etc + * level firewall(SCR), address hole within the target, etc * * TIMEOUT_ERR: - * No response returned by slave. Can be due to slave being clock - * gated, under reset, powered down or slave inability to respond - * for an internal slave issue + * No response returned by target. Can be due to target being clock + * gated, under reset, powered down or target inability to respond + * for an internal target issue */ static const struct tegra_cbb_error tegra241_cbb_errors[] = { { - .code = "SLAVE_ERR", - .desc = "Slave being accessed responded with an error." + .code = "TARGET_ERR", + .desc = "Target being accessed responded with an error." }, { .code = "DECODE_ERR", .desc = "Attempt to access an address hole or Reserved region of memory." @@ -901,16 +1016,16 @@ static const struct tegra_cbb_error tegra241_cbb_errors[] = { .desc = "Attempt to access a region which is firewalled." }, { .code = "TIMEOUT_ERR", - .desc = "No response returned by slave." + .desc = "No response returned by target." }, { .code = "PWRDOWN_ERR", .desc = "Attempt to access a portion of the fabric that is powered down." }, { .code = "UNSUPPORTED_ERR", - .desc = "Attempt to access a slave through an unsupported access." + .desc = "Attempt to access a target through an unsupported access." }, { .code = "POISON_ERR", - .desc = "Slave responds with poison error to indicate error in data." + .desc = "Target responds with poison error to indicate error in data." }, { .code = "RSVD" }, { @@ -968,7 +1083,18 @@ static const struct tegra_cbb_error tegra241_cbb_errors[] = { }, }; -static const struct tegra234_slave_lookup tegra241_cbb_slave_map[] = { +static const struct tegra234_target_lookup tegra241_bpmp_target_map[] = { + { "RSVD", 0x00000 }, + { "RSVD", 0x00000 }, + { "RSVD", 0x00000 }, + { "CBB", 0x15000 }, + { "CPU", 0x16000 }, + { "AXI2APB", 0x00000 }, + { "DBB0", 0x17000 }, + { "DBB1", 0x18000 }, +}; + +static const struct tegra234_target_lookup tegra241_cbb_target_map[] = { { "RSVD", 0x00000 }, { "PCIE_C8", 0x51000 }, { "PCIE_C9", 0x52000 }, @@ -1030,13 +1156,20 @@ static const struct tegra234_slave_lookup tegra241_cbb_slave_map[] = { { "AXI2APB_32", 0x8F000 }, }; +static const struct tegra234_fabric_lookup tegra241_cbb_fab_list[] = { + [T234_CBB_FABRIC_ID] = { "cbb-fabric", true, + tegra241_cbb_target_map, ARRAY_SIZE(tegra241_cbb_target_map) }, + [T234_BPMP_FABRIC_ID] = { "bpmp-fabric", true, + tegra241_bpmp_target_map, ARRAY_SIZE(tegra241_cbb_target_map) }, +}; static const struct tegra234_cbb_fabric tegra241_cbb_fabric = { - .name = "cbb-fabric", - .master_id = tegra241_master_id, - .slave_map = tegra241_cbb_slave_map, - .max_slaves = ARRAY_SIZE(tegra241_cbb_slave_map), + .fab_id = T234_CBB_FABRIC_ID, + .fab_list = tegra241_cbb_fab_list, + .initiator_id = tegra241_initiator_id, .errors = tegra241_cbb_errors, .max_errors = ARRAY_SIZE(tegra241_cbb_errors), + .err_intr_enbl = 0x7, + .err_status_clr = 0x1ff007f, .notifier_offset = 0x60000, .off_mask_erd = 0x40004, .firewall_base = 0x20000, @@ -1044,30 +1177,302 @@ static const struct tegra234_cbb_fabric tegra241_cbb_fabric = { .firewall_wr_ctl = 0x2368, }; -static const struct tegra234_slave_lookup tegra241_bpmp_slave_map[] = { - { "RSVD", 0x00000 }, - { "RSVD", 0x00000 }, - { "RSVD", 0x00000 }, - { "CBB", 0x15000 }, - { "CPU", 0x16000 }, - { "AXI2APB", 0x00000 }, - { "DBB0", 0x17000 }, - { "DBB1", 0x18000 }, -}; - static const struct tegra234_cbb_fabric tegra241_bpmp_fabric = { - .name = "bpmp-fabric", - .master_id = tegra241_master_id, - .slave_map = tegra241_bpmp_slave_map, - .max_slaves = ARRAY_SIZE(tegra241_bpmp_slave_map), + .fab_id = T234_BPMP_FABRIC_ID, + .fab_list = tegra241_cbb_fab_list, + .initiator_id = tegra241_initiator_id, .errors = tegra241_cbb_errors, .max_errors = ARRAY_SIZE(tegra241_cbb_errors), + .err_intr_enbl = 0xf, + .err_status_clr = 0x1ff007f, .notifier_offset = 0x19000, .firewall_base = 0x30000, .firewall_ctl = 0x8f0, .firewall_wr_ctl = 0x8e8, }; +static const char * const tegra264_initiator_id[] = { + [0x0] = "TZ", + [0x1] = "CCPLEX", + [0x2] = "ISC", + [0x3] = "BPMP_FW", + [0x4] = "AON", + [0x5] = "MSS_SEQ", + [0x6] = "GPCDMA_P", + [0x7] = "TSECA_NONSECURE", + [0x8] = "TSECA_LIGHTSECURE", + [0x9] = "TSECA_HEAVYSECURE", + [0xa] = "CORESIGHT", + [0xb] = "APE_0", + [0xc] = "APE_1", + [0xd] = "PEATRANS", + [0xe] = "JTAGM_DFT", + [0xf] = "RCE", + [0x10] = "DCE", + [0x11] = "PSC_FW_USER", + [0x12] = "PSC_FW_SUPERVISOR", + [0x13] = "PSC_FW_MACHINE", + [0x14] = "PSC_BOOT", + [0x15] = "BPMP_BOOT", + [0x16] = "GPU_0", + [0x17] = "GPU_1", + [0x18] = "GPU_2", + [0x19] = "GPU_3", + [0x1a] = "GPU_4", + [0x1b] = "PSC_EXT_BOOT", + [0x1c] = "PSC_EXT_RUNTIME", + [0x1d] = "OESP_EXT", + [0x1e] = "SB_EXT", + [0x1f] = "FSI_SAFETY_0", + [0x20] = "FSI_SAFETY_1", + [0x21] = "FSI_SAFETY_2", + [0x22] = "FSI_SAFETY_3", + [0x23] = "FSI_CHSM", + [0x24] = "RCE_1", + [0x25] = "BPMP_OEM_FW", + [0x26 ... 0x3d] = "RSVD", + [0x3e] = "CBB_SMN", + [0x3f] = "CBB_RSVD" +}; + +static const struct tegra234_target_lookup tegra264_top0_cbb_target_map[] = { + { "RSVD", 0x000000 }, + { "CBB_CENTRAL", 0xC020000 }, + { "AXI2APB_1", 0x80000 }, + { "AXI2APB_10", 0x81000 }, + { "AXI2APB_11", 0x82000 }, + { "RSVD", 0x00000 }, + { "RSVD", 0x00000 }, + { "AXI2APB_14", 0x83000 }, + { "AXI2APB_15", 0x84000 }, + { "AXI2APB_16", 0x85000 }, + { "AXI2APB_17", 0x86000 }, + { "AXI2APB_2", 0x87000 }, + { "AXI2APB_3", 0x88000 }, + { "RSVD", 0x00000 }, + { "AXI2APB_5", 0x8A000 }, + { "AXI2APB_6", 0x8B000 }, + { "AXI2APB_7", 0x8C000 }, + { "AXI2APB_8", 0x8D000 }, + { "AXI2APB_9", 0x8E000 }, + { "FSI_SLAVE", 0x64000 }, + { "DISP_USB_CBB_T", 0x65000 }, + { "SYSTEM_CBB_T", 0x66000 }, + { "UPHY0_CBB_T", 0x67000 }, + { "VISION_CBB_T", 0x68000 }, + { "CCPLEX_SLAVE", 0x69000 }, + { "PCIE_C0", 0x6A000 }, + { "SMN_UCF_RX_0", 0x6B000 }, + { "SMN_UCF_RX_1", 0x6C000 }, + { "AXI2APB_4", 0x89000 }, +}; + +static const struct tegra234_target_lookup tegra264_sys_cbb_target_map[] = { + { "RSVD", 0x00000 }, + { "AXI2APB_1", 0xE1000 }, + { "RSVD", 0x00000 }, + { "AON_SLAVE", 0x79000 }, + { "APE_SLAVE", 0x73000 }, + { "BPMP_SLAVE", 0x74000 }, + { "OESP_SLAVE", 0x75000 }, + { "PSC_SLAVE", 0x76000 }, + { "SB_SLAVE", 0x7A000 }, + { "SMN_SYSTEM_RX", 0x7B000 }, + { "STM", 0x77000 }, + { "RSVD", 0x00000 }, + { "AXI2APB_3", 0xE3000 }, + { "TOP_CBB_T", 0x7C000 }, + { "AXI2APB_2", 0xE4000 }, + { "AXI2APB_4", 0xE5000 }, + { "AXI2APB_5", 0xE6000 }, +}; + +static const struct tegra234_target_lookup tegra264_uphy0_cbb_target_map[] = { + [0 ... 20] = { "RSVD", 0x00000 }, + { "AXI2APB_1", 0x71000 }, + { "RSVD", 0x00000 }, + { "AXI2APB_3", 0x75000 }, + { "SMN_UPHY0_RX", 0x53000 }, + { "RSVD", 0x00000 }, + { "RSVD", 0x00000 }, + { "RSVD", 0x00000 }, + { "RSVD", 0x00000 }, + { "PCIE_C4", 0x4B000 }, + { "AXI2APB_2", 0x74000 }, + { "AXI2APB_4", 0x76000 }, + { "AXI2APB_5", 0x77000 }, + { "RSVD", 0x00000 }, + { "AXI2APB_7", 0x79000 }, + { "PCIE_C2", 0x56000 }, + { "RSVD", 0x00000 }, + { "RSVD", 0x00000 }, + { "PCIE_C1", 0x55000 }, + { "RSVD", 0x00000 }, + { "AXI2APB_10", 0x72000 }, + { "AXI2APB_11", 0x7C000 }, + { "AXI2APB_8", 0x7A000 }, + { "AXI2APB_9", 0x7B000 }, + { "RSVD", 0x00000 }, + { "RSVD", 0x00000 }, + { "PCIE_C5", 0x4E000 }, + { "PCIE_C3", 0x58000 }, + { "RSVD", 0x00000 }, + { "ISC_SLAVE", 0x54000 }, + { "TOP_CBB_T", 0x57000 }, + { "AXI2APB_12", 0x7D000 }, + { "AXI2APB_13", 0x70000 }, + { "AXI2APB_6", 0x7E000 }, +}; + +static const struct tegra234_target_lookup tegra264_vision_cbb_target_map[] = { + [0 ... 5] = { "RSVD", 0x0 }, + { "HOST1X", 0x45000 }, + { "RSVD", 0x00000 }, + { "RSVD", 0x00000 }, + { "AXI2APB_2", 0x71000 }, + { "RSVD", 0x00000 }, + { "RSVD", 0x00000 }, + { "SMN_VISION_RX", 0x47000 }, + [13 ... 19] = { "RSVD", 0x0 }, + { "RCE_0_SLAVE", 0x4B000 }, + { "RCE_1_SLAVE", 0x4C000 }, + { "AXI2APB_1", 0x72000 }, + { "AXI2APB_3", 0x73000 }, + { "TOP_CBB_T", 0x4D000 }, + +}; + +static const struct tegra234_fabric_lookup tegra264_cbb_fab_list[] = { + [T264_SYSTEM_CBB_FABRIC_ID] = { "system-cbb-fabric", true, + tegra264_sys_cbb_target_map, + ARRAY_SIZE(tegra264_sys_cbb_target_map) }, + [T264_TOP_0_CBB_FABRIC_ID] = { "top0-cbb-fabric", true, + tegra264_top0_cbb_target_map, + ARRAY_SIZE(tegra264_top0_cbb_target_map) }, + [T264_VISION_CBB_FABRIC_ID] = { "vision-cbb-fabric", true, + tegra264_vision_cbb_target_map, + ARRAY_SIZE(tegra264_vision_cbb_target_map) }, + [T264_DISP_USB_CBB_FABRIC_ID] = { "disp-usb-cbb-fabric" }, + [T264_UPHY0_CBB_FABRIC_ID] = { "uphy0-cbb-fabric", true, + tegra264_uphy0_cbb_target_map, + ARRAY_SIZE(tegra264_uphy0_cbb_target_map) }, + [T264_AON_FABRIC_ID] = { "aon-fabric" }, + [T264_PSC_FABRIC_ID] = { "psc-fabric" }, + [T264_OESP_FABRIC_ID] = { "oesp-fabric" }, + [T264_APE_FABRIC_ID] = { "ape-fabirc" }, + [T264_BPMP_FABRIC_ID] = { "bpmp-fabric" }, + [T264_RCE_0_FABRIC_ID] = { "rce0-fabric" }, + [T264_RCE_1_FABRIC_ID] = { "rce1-fabric" }, + [T264_DCE_FABRIC_ID] = { "dce-fabric" }, + [T264_FSI_FABRIC_ID] = { "fsi-fabric" }, + [T264_ISC_FABRIC_ID] = { "isc-fabric" }, + [T264_SB_FABRIC_ID] = { "sb-fabric" }, + [T264_ISC_CPU_FABRIC_ID] = { "isc-cpu-fabric" }, +}; + +static const struct tegra234_cbb_fabric tegra264_top0_cbb_fabric = { + .fab_id = T264_TOP_0_CBB_FABRIC_ID, + .fab_list = tegra264_cbb_fab_list, + .initiator_id = tegra264_initiator_id, + .errors = tegra241_cbb_errors, + .max_errors = ARRAY_SIZE(tegra241_cbb_errors), + .err_intr_enbl = 0x7, + .err_status_clr = 0x1ff007f, + .notifier_offset = 0x90000, + .off_mask_erd = 0x4a004, + .firewall_base = 0x3c0000, + .firewall_ctl = 0x5b0, + .firewall_wr_ctl = 0x5a8, +}; + +static const struct tegra234_cbb_fabric tegra264_sys_cbb_fabric = { + .fab_id = T264_SYSTEM_CBB_FABRIC_ID, + .fab_list = tegra264_cbb_fab_list, + .initiator_id = tegra264_initiator_id, + .errors = tegra241_cbb_errors, + .max_errors = ARRAY_SIZE(tegra241_cbb_errors), + .err_intr_enbl = 0xf, + .err_status_clr = 0x1ff007f, + .notifier_offset = 0x40000, + .firewall_base = 0x29c000, + .firewall_ctl = 0x170, + .firewall_wr_ctl = 0x168, +}; + +static const struct tegra234_cbb_fabric tegra264_uphy0_cbb_fabric = { + .fab_id = T264_UPHY0_CBB_FABRIC_ID, + .fab_list = tegra264_cbb_fab_list, + .initiator_id = tegra264_initiator_id, + .errors = tegra241_cbb_errors, + .max_errors = ARRAY_SIZE(tegra241_cbb_errors), + .err_intr_enbl = 0x1, + .err_status_clr = 0x1ff007f, + .notifier_offset = 0x80000, + .firewall_base = 0x360000, + .firewall_ctl = 0x590, + .firewall_wr_ctl = 0x588, +}; + +static const struct tegra234_cbb_fabric tegra264_vision_cbb_fabric = { + .fab_id = T264_VISION_CBB_FABRIC_ID, + .fab_list = tegra264_cbb_fab_list, + .initiator_id = tegra264_initiator_id, + .errors = tegra241_cbb_errors, + .max_errors = ARRAY_SIZE(tegra241_cbb_errors), + .err_intr_enbl = 0x1, + .err_status_clr = 0x1ff007f, + .notifier_offset = 0x80000, + .firewall_base = 0x290000, + .firewall_ctl = 0x5d0, + .firewall_wr_ctl = 0x5c8, +}; + +static const struct tegra234_fabric_lookup t254_cbb_fab_list[] = { + [T254_C2C_FABRIC_ID] = { "c2c-fabric", true }, + [T254_DISP_CLUSTER_FABRIC_ID] = { "display-cluster-fabric", true }, + [T254_GPU_FABRIC_ID] = { "gpu-fabric", true }, +}; + +static const struct tegra234_cbb_fabric t254_c2c_fabric = { + .fab_id = T254_C2C_FABRIC_ID, + .fab_list = t254_cbb_fab_list, + .errors = tegra241_cbb_errors, + .max_errors = ARRAY_SIZE(tegra241_cbb_errors), + .err_intr_enbl = 0xf, + .err_status_clr = 0x1ff007f, + .notifier_offset = 0x50000, + .off_mask_erd = 0x14004, + .firewall_base = 0x40000, + .firewall_ctl = 0x9b0, + .firewall_wr_ctl = 0x9a8, +}; + +static const struct tegra234_cbb_fabric t254_disp_fabric = { + .fab_id = T254_DISP_CLUSTER_FABRIC_ID, + .fab_list = t254_cbb_fab_list, + .errors = tegra241_cbb_errors, + .max_errors = ARRAY_SIZE(tegra241_cbb_errors), + .err_intr_enbl = 0x1, + .err_status_clr = 0x1ff007f, + .notifier_offset = 0x50000, + .firewall_base = 0x30000, + .firewall_ctl = 0x810, + .firewall_wr_ctl = 0x808, +}; + +static const struct tegra234_cbb_fabric t254_gpu_fabric = { + .fab_id = T254_GPU_FABRIC_ID, + .fab_list = t254_cbb_fab_list, + .errors = tegra241_cbb_errors, + .max_errors = ARRAY_SIZE(tegra241_cbb_errors), + .err_intr_enbl = 0x1f, + .err_status_clr = 0x1ff007f, + .notifier_offset = 0x50000, + .firewall_base = 0x30000, + .firewall_ctl = 0x930, + .firewall_wr_ctl = 0x928, +}; + static const struct of_device_id tegra234_cbb_dt_ids[] = { { .compatible = "nvidia,tegra234-cbb-fabric", .data = &tegra234_cbb_fabric }, { .compatible = "nvidia,tegra234-aon-fabric", .data = &tegra234_aon_fabric }, @@ -1075,6 +1480,10 @@ static const struct of_device_id tegra234_cbb_dt_ids[] = { { .compatible = "nvidia,tegra234-dce-fabric", .data = &tegra234_dce_fabric }, { .compatible = "nvidia,tegra234-rce-fabric", .data = &tegra234_rce_fabric }, { .compatible = "nvidia,tegra234-sce-fabric", .data = &tegra234_sce_fabric }, + { .compatible = "nvidia,tegra264-sys-cbb-fabric", .data = &tegra264_sys_cbb_fabric }, + { .compatible = "nvidia,tegra264-top0-cbb-fabric", .data = &tegra264_top0_cbb_fabric }, + { .compatible = "nvidia,tegra264-uphy0-cbb-fabric", .data = &tegra264_uphy0_cbb_fabric }, + { .compatible = "nvidia,tegra264-vision-cbb-fabric", .data = &tegra264_vision_cbb_fabric }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, tegra234_cbb_dt_ids); @@ -1088,6 +1497,9 @@ struct tegra234_cbb_acpi_uid { static const struct tegra234_cbb_acpi_uid tegra234_cbb_acpi_uids[] = { { "NVDA1070", "1", &tegra241_cbb_fabric }, { "NVDA1070", "2", &tegra241_bpmp_fabric }, + { "NVDA1070", "3", &t254_c2c_fabric }, + { "NVDA1070", "4", &t254_disp_fabric }, + { "NVDA1070", "5", &t254_gpu_fabric }, { }, }; @@ -1176,7 +1588,7 @@ static int __maybe_unused tegra234_cbb_resume_noirq(struct device *dev) tegra234_cbb_error_enable(&cbb->base); - dev_dbg(dev, "%s resumed\n", cbb->fabric->name); + dev_dbg(dev, "%s resumed\n", cbb->fabric->fab_list[cbb->fabric->fab_id].name); return 0; } diff --git a/drivers/soc/tegra/fuse/tegra-apbmisc.c b/drivers/soc/tegra/fuse/tegra-apbmisc.c index e2ca5d55fd31..0ce94fdc536f 100644 --- a/drivers/soc/tegra/fuse/tegra-apbmisc.c +++ b/drivers/soc/tegra/fuse/tegra-apbmisc.c @@ -128,6 +128,7 @@ static const struct of_device_id apbmisc_match[] __initconst = { { .compatible = "nvidia,tegra186-misc", }, { .compatible = "nvidia,tegra194-misc", }, { .compatible = "nvidia,tegra234-misc", }, + { .compatible = "nvidia,tegra264-misc", }, {}, }; diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index e0d67bfe955c..4d6a46bc8c21 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -2500,8 +2500,7 @@ static int tegra_pmc_irq_init(struct tegra_pmc *pmc) pmc->irq.irq_set_type = pmc->soc->irq_set_type; pmc->irq.irq_set_wake = pmc->soc->irq_set_wake; - pmc->domain = irq_domain_create_hierarchy(parent, 0, 96, - of_fwnode_handle(pmc->dev->of_node), + pmc->domain = irq_domain_create_hierarchy(parent, 0, 96, dev_fwnode(pmc->dev), &tegra_pmc_irq_domain_ops, pmc); if (!pmc->domain) { dev_err(pmc->dev, "failed to allocate domain\n"); @@ -4248,7 +4247,128 @@ static const struct tegra_pmc_soc tegra234_pmc_soc = { .has_single_mmio_aperture = false, }; +static const struct tegra_pmc_regs tegra264_pmc_regs = { + .scratch0 = 0x684, + .rst_status = 0x4, + .rst_source_shift = 0x2, + .rst_source_mask = 0x1fc, + .rst_level_shift = 0x0, + .rst_level_mask = 0x3, +}; + +static const char * const tegra264_reset_sources[] = { + "SYS_RESET_N", /* 0x0 */ + "CSDC_RTC_XTAL", + "VREFRO_POWER_BAD", + "SCPM_SOC_XTAL", + "SCPM_RTC_XTAL", + "FMON_32K", + "FMON_OSC", + "POD_RTC", + "POD_IO", /* 0x8 */ + "POD_PLUS_IO_SPLL", + "POD_PLUS_SOC", + "VMON_PLUS_UV", + "VMON_PLUS_OV", + "FUSECRC_FAULT", + "OSC_FAULT", + "BPMP_BOOT_FAULT", + "SCPM_BPMP_CORE_CLK", /* 0x10 */ + "SCPM_PSC_SE_CLK", + "VMON_SOC_MIN", + "VMON_SOC_MAX", + "VMON_MSS_MIN", + "VMON_MSS_MAX", + "POD_PLUS_IO_VMON", + "NVJTAG_SEL_MONITOR", + "NV_THERM_FAULT", /* 0x18 */ + "FSI_THERM_FAULT", + "PSC_SW", + "SCPM_OESP_SE_CLK", + "SCPM_SB_SE_CLK", + "POD_CPU", + "POD_GPU", + "DCLS_GPU", + "POD_MSS", /* 0x20 */ + "FMON_FSI", + "POD_FSI", + "VMON_FSI_MIN", + "VMON_FSI_MAX", + "VMON_CPU0_MIN", + "VMON_CPU0_MAX", + "BPMP_FMON", + "AO_WDT_POR", /* 0x28 */ + "BPMP_WDT_POR", + "AO_TKE_WDT_POR", + "RCE0_WDT_POR", + "RCE1_WDT_POR", + "DCE_WDT_POR", + "FSI_R5_WDT_POR", + "FSI_R52_0_WDT_POR", + "FSI_R52_1_WDT_POR", /* 0x30 */ + "FSI_R52_2_WDT_POR", + "FSI_R52_3_WDT_POR", + "TOP_0_WDT_POR", + "TOP_1_WDT_POR", + "TOP_2_WDT_POR", + "APE_C0_WDT_POR", + "APE_C1_WDT_POR", + "GPU_TKE_WDT_POR", /* 0x38 */ + "PSC_WDT_POR", + "OESP_WDT_POR", + "SB_WDT_POR", + "SW_MAIN", + "L0L1_RST_OUT_N", + "FSI_HSM", + "CSITE_SW", + "AO_WDT_DBG", /* 0x40 */ + "BPMP_WDT_DBG", + "AO_TKE_WDT_DBG", + "RCE0_WDT_DBG", + "RCE1_WDT_DBG", + "DCE_WDT_DBG", + "FSI_R5_WDT_DBG", + "FSI_R52_0_WDT_DBG", + "FSI_R52_1_WDT_DBG", /* 0x48 */ + "FSI_R52_2_WDT_DBG", + "FSI_R52_3_WDT_DBG", + "TOP_0_WDT_DBG", + "TOP_1_WDT_DBG", + "TOP_2_WDT_DBG", + "APE_C0_WDT_DBG", + "APE_C1_WDT_DBG", + "PSC_WDT_DBG", /* 0x50 */ + "OESP_WDT_DBG", + "SB_WDT_DBG", + "TSC_0_WDT_DBG", + "TSC_1_WDT_DBG", + "L2_RST_OUT_N", + "SC7" +}; + +static const struct tegra_wake_event tegra264_wake_events[] = { +}; + +static const struct tegra_pmc_soc tegra264_pmc_soc = { + .has_impl_33v_pwr = true, + .regs = &tegra264_pmc_regs, + .init = tegra186_pmc_init, + .setup_irq_polarity = tegra186_pmc_setup_irq_polarity, + .set_wake_filters = tegra186_pmc_set_wake_filters, + .irq_set_wake = tegra186_pmc_irq_set_wake, + .irq_set_type = tegra186_pmc_irq_set_type, + .reset_sources = tegra264_reset_sources, + .num_reset_sources = ARRAY_SIZE(tegra264_reset_sources), + .reset_levels = tegra186_reset_levels, + .num_reset_levels = ARRAY_SIZE(tegra186_reset_levels), + .wake_events = tegra264_wake_events, + .num_wake_events = ARRAY_SIZE(tegra264_wake_events), + .max_wake_events = 128, + .max_wake_vectors = 4, +}; + static const struct of_device_id tegra_pmc_match[] = { + { .compatible = "nvidia,tegra264-pmc", .data = &tegra264_pmc_soc }, { .compatible = "nvidia,tegra234-pmc", .data = &tegra234_pmc_soc }, { .compatible = "nvidia,tegra194-pmc", .data = &tegra194_pmc_soc }, { .compatible = "nvidia,tegra186-pmc", .data = &tegra186_pmc_soc }, diff --git a/include/dt-bindings/arm/qcom,ids.h b/include/dt-bindings/arm/qcom,ids.h index 897b8135dc12..cb8ce53146f0 100644 --- a/include/dt-bindings/arm/qcom,ids.h +++ b/include/dt-bindings/arm/qcom,ids.h @@ -279,8 +279,13 @@ #define QCOM_ID_QCM8550 604 #define QCOM_ID_SM8750 618 #define QCOM_ID_IPQ5300 624 +#define QCOM_ID_SM7635 636 +#define QCOM_ID_SM6650 640 +#define QCOM_ID_SM6650P 641 #define QCOM_ID_IPQ5321 650 #define QCOM_ID_IPQ5424 651 +#define QCOM_ID_QCM6690 657 +#define QCOM_ID_QCS6690 658 #define QCOM_ID_IPQ5404 671 #define QCOM_ID_QCS9100 667 #define QCOM_ID_QCS8300 674 diff --git a/include/dt-bindings/reset/canaan,k230-rst.h b/include/dt-bindings/reset/canaan,k230-rst.h new file mode 100644 index 000000000000..e4f6612607fe --- /dev/null +++ b/include/dt-bindings/reset/canaan,k230-rst.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (C) 2023-2024 Canaan Bright Sight Co., Ltd + * Copyright (C) 2024-2025 Junhui Liu <junhui.liu@pigmoral.tech> + */ +#ifndef _DT_BINDINGS_CANAAN_K230_RST_H_ +#define _DT_BINDINGS_CANAAN_K230_RST_H_ + +#define RST_CPU0 0 +#define RST_CPU1 1 +#define RST_CPU0_FLUSH 2 +#define RST_CPU1_FLUSH 3 +#define RST_AI 4 +#define RST_VPU 5 +#define RST_HISYS 6 +#define RST_HISYS_AHB 7 +#define RST_SDIO0 8 +#define RST_SDIO1 9 +#define RST_SDIO_AXI 10 +#define RST_USB0 11 +#define RST_USB1 12 +#define RST_USB0_AHB 13 +#define RST_USB1_AHB 14 +#define RST_SPI0 15 +#define RST_SPI1 16 +#define RST_SPI2 17 +#define RST_SEC 18 +#define RST_PDMA 19 +#define RST_SDMA 20 +#define RST_DECOMPRESS 21 +#define RST_SRAM 22 +#define RST_SHRM_AXIM 23 +#define RST_SHRM_AXIS 24 +#define RST_NONAI2D 25 +#define RST_MCTL 26 +#define RST_ISP 27 +#define RST_ISP_DW 28 +#define RST_DPU 29 +#define RST_DISP 30 +#define RST_GPU 31 +#define RST_AUDIO 32 +#define RST_TIMER0 33 +#define RST_TIMER1 34 +#define RST_TIMER2 35 +#define RST_TIMER3 36 +#define RST_TIMER4 37 +#define RST_TIMER5 38 +#define RST_TIMER_APB 39 +#define RST_HDI 40 +#define RST_WDT0 41 +#define RST_WDT1 42 +#define RST_WDT0_APB 43 +#define RST_WDT1_APB 44 +#define RST_TS_APB 45 +#define RST_MAILBOX 46 +#define RST_STC 47 +#define RST_PMU 48 +#define RST_LOSYS_APB 49 +#define RST_UART0 50 +#define RST_UART1 51 +#define RST_UART2 52 +#define RST_UART3 53 +#define RST_UART4 54 +#define RST_I2C0 55 +#define RST_I2C1 56 +#define RST_I2C2 57 +#define RST_I2C3 58 +#define RST_I2C4 59 +#define RST_JAMLINK0_APB 60 +#define RST_JAMLINK1_APB 61 +#define RST_JAMLINK2_APB 62 +#define RST_JAMLINK3_APB 63 +#define RST_CODEC_APB 64 +#define RST_GPIO_DB 65 +#define RST_GPIO_APB 66 +#define RST_ADC 67 +#define RST_ADC_APB 68 +#define RST_PWM_APB 69 +#define RST_SHRM_APB 70 +#define RST_CSI0 71 +#define RST_CSI1 72 +#define RST_CSI2 73 +#define RST_CSI_DPHY 74 +#define RST_ISP_AHB 75 +#define RST_M0 76 +#define RST_M1 77 +#define RST_M2 78 +#define RST_SPI2AXI 79 + +#endif diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h index 983e1591bbba..0f667bf1d4d9 100644 --- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -148,11 +148,10 @@ bool qcom_scm_lmh_dcvsh_available(void); int qcom_scm_gpu_init_regs(u32 gpu_req); -int qcom_scm_shm_bridge_enable(void); -int qcom_scm_shm_bridge_create(struct device *dev, u64 pfn_and_ns_perm_flags, +int qcom_scm_shm_bridge_create(u64 pfn_and_ns_perm_flags, u64 ipfn_and_s_perm_flags, u64 size_and_flags, u64 ns_vmids, u64 *handle); -int qcom_scm_shm_bridge_delete(struct device *dev, u64 handle); +int qcom_scm_shm_bridge_delete(u64 handle); #ifdef CONFIG_QCOM_QSEECOM diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index e2d71b6fdd84..92ffc4373f6d 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2624,6 +2624,9 @@ #define PCI_VENDOR_ID_TEKRAM 0x1de1 #define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29 +#define PCI_VENDOR_ID_RPI 0x1de4 +#define PCI_DEVICE_ID_RPI_RP1_C0 0x0001 + #define PCI_VENDOR_ID_ALIBABA 0x1ded #define PCI_VENDOR_ID_CXL 0x1e98 diff --git a/include/linux/soc/qcom/qmi.h b/include/linux/soc/qcom/qmi.h index 469e02d2aa0d..291cdc7ef49c 100644 --- a/include/linux/soc/qcom/qmi.h +++ b/include/linux/soc/qcom/qmi.h @@ -24,9 +24,9 @@ struct socket; */ struct qmi_header { u8 type; - u16 txn_id; - u16 msg_id; - u16 msg_len; + __le16 txn_id; + __le16 msg_id; + __le16 msg_len; } __packed; #define QMI_REQUEST 0 diff --git a/include/soc/qcom/qcom-spmi-pmic.h b/include/soc/qcom/qcom-spmi-pmic.h index df3d3a0af98a..2cf9e2d8cd55 100644 --- a/include/soc/qcom/qcom-spmi-pmic.h +++ b/include/soc/qcom/qcom-spmi-pmic.h @@ -50,6 +50,8 @@ #define PMR735B_SUBTYPE 0x34 #define PM6350_SUBTYPE 0x36 #define PM4125_SUBTYPE 0x37 +#define PMM8650AU_SUBTYPE 0x4e +#define PMM8650AU_PSAIL_SUBTYPE 0x4f #define PMI8998_FAB_ID_SMIC 0x11 #define PMI8998_FAB_ID_GF 0x30 diff --git a/include/trace/events/scmi.h b/include/trace/events/scmi.h index 127300481123..703b7bb68e44 100644 --- a/include/trace/events/scmi.h +++ b/include/trace/events/scmi.h @@ -36,8 +36,8 @@ TRACE_EVENT(scmi_fc_call, TRACE_EVENT(scmi_xfer_begin, TP_PROTO(int transfer_id, u8 msg_id, u8 protocol_id, u16 seq, - bool poll), - TP_ARGS(transfer_id, msg_id, protocol_id, seq, poll), + bool poll, int inflight), + TP_ARGS(transfer_id, msg_id, protocol_id, seq, poll, inflight), TP_STRUCT__entry( __field(int, transfer_id) @@ -45,6 +45,7 @@ TRACE_EVENT(scmi_xfer_begin, __field(u8, protocol_id) __field(u16, seq) __field(bool, poll) + __field(int, inflight) ), TP_fast_assign( @@ -53,11 +54,12 @@ TRACE_EVENT(scmi_xfer_begin, __entry->protocol_id = protocol_id; __entry->seq = seq; __entry->poll = poll; + __entry->inflight = inflight; ), - TP_printk("pt=%02X msg_id=%02X seq=%04X transfer_id=%X poll=%u", - __entry->protocol_id, __entry->msg_id, __entry->seq, - __entry->transfer_id, __entry->poll) + TP_printk("pt=%02X msg_id=%02X seq=%04X transfer_id=%X poll=%u inflight=%d", + __entry->protocol_id, __entry->msg_id, __entry->seq, + __entry->transfer_id, __entry->poll, __entry->inflight) ); TRACE_EVENT(scmi_xfer_response_wait, @@ -90,8 +92,8 @@ TRACE_EVENT(scmi_xfer_response_wait, TRACE_EVENT(scmi_xfer_end, TP_PROTO(int transfer_id, u8 msg_id, u8 protocol_id, u16 seq, - int status), - TP_ARGS(transfer_id, msg_id, protocol_id, seq, status), + int status, int inflight), + TP_ARGS(transfer_id, msg_id, protocol_id, seq, status, inflight), TP_STRUCT__entry( __field(int, transfer_id) @@ -99,6 +101,7 @@ TRACE_EVENT(scmi_xfer_end, __field(u8, protocol_id) __field(u16, seq) __field(int, status) + __field(int, inflight) ), TP_fast_assign( @@ -107,11 +110,12 @@ TRACE_EVENT(scmi_xfer_end, __entry->protocol_id = protocol_id; __entry->seq = seq; __entry->status = status; + __entry->inflight = inflight; ), - TP_printk("pt=%02X msg_id=%02X seq=%04X transfer_id=%X s=%d", - __entry->protocol_id, __entry->msg_id, __entry->seq, - __entry->transfer_id, __entry->status) + TP_printk("pt=%02X msg_id=%02X seq=%04X transfer_id=%X s=%d inflight=%d", + __entry->protocol_id, __entry->msg_id, __entry->seq, + __entry->transfer_id, __entry->status, __entry->inflight) ); TRACE_EVENT(scmi_rx_done, |