diff options
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, |