diff --git a/.gitignore b/.gitignore index a61e4778d011cf..a342f9278c07e1 100644 --- a/.gitignore +++ b/.gitignore @@ -178,3 +178,7 @@ sphinx_*/ # Rust analyzer configuration /rust-project.json + +# Yocto symlinks +oe-logs +oe-workdir diff --git a/Documentation/devicetree/bindings/dma/adi-dma.txt b/Documentation/devicetree/bindings/dma/adi-dma.txt new file mode 100644 index 00000000000000..49c3f49329a1f6 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/adi-dma.txt @@ -0,0 +1,113 @@ +* Analog Devices Direct Memory Access (DMA) Controller for ADRV906X-series processors + +This document will only describe differences to the generic DMA Controller and +DMA request bindings as described in dma/dma.txt + +* DMA controller + +Required properties: +- compatible : Should be "adi,mdma-controller" or "adi,dma-controller". +- reg : Should contain DMA registers location and length +- #dma-cells : Has to be 1. Does not support anything else. +- interrupts : subnode, containing: + adi,id: + adi,src-offset: Offset from base address + adi,skip-interrupts: Set to 0 usually. Setting to 1 will skip handling interrupts, + so the SHARC cores may handle them instead. + interrupts: list of interrupts, from processor's GIC interrupt list + interrupt-names: list of interrupt names +Optional properties: +- dde-descriptor-mode : this is optionally specified for a dma channel. A dma channel with this + property runs in descriptor list mode. otherwise the dma channel + runs in register based mode. +- dde-sync-bit-disable: disable the DDE sync bit for a dma channel. A sync bit is used to synchronize + work unit transitions. + +Example (DMA): + + sport0_dma_cluster: dma@0x31022000 { + compatible = "adi,dma-controller"; + reg = <0x31022000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + sport0a: channel@0 { + adi,id = <0>; + adi,src-offset = <0>; + adi,skip-interrupts = <0>; + interrupts = , + ; + interrupt-names = "complete", "error"; + }; + + sport0b: channel@1 { + adi,id = <1>; + adi,src-offset = <0x80>; + adi,skip-interrupts = <0>; + interrupts = , + ; + interrupt-names = "complete", "error"; + }; + }; + +Example (MDMA): + + mdma: dma@0x3109a000 { + compatible = "adi,mdma-controller"; + reg = <0x3109a000 0x1000>; + status = "okay"; + + sdma2: channel@40 { + adi,id = <40>; + // The destination interrupts are used for primary complete detection + interrupts = , + , + , + ; + interrupt-names = "complete", "error", "complete2", "error2"; + adi,src-offset = <0>; + adi,dest-offset = <0x80>; + }; + }; + +* DMA client + +Clients have to specify the DMA requests with phandles in a list. + +Required properties: +- dmas: List of one or more DMA request specifiers. One DMA request specifier + consists of a phandle to the DMA controller followed by the integer + specifying the request line. +- dma-names: List of string identifiers for the DMA requests. For the correct + names, have a look at the specific client driver. + +Example: + + i2s0: i2s0@0 { + ... + dmas = <&sport0_dma_cluster 0>, <&sport0_dma_cluster 1>; + dma-names = "tx", "rx"; + ... + }; + +From a driver level, additional configuration of things like bus width can set via: + + dma_config.direction = DMA_DEV_TO_MEM; + dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_config.src_maxburst = 1; + dma_config.dst_maxburst = 1; + + dmaengine_slave_config(crc->dma_ch, &dma_config); + +Or cyclic mode can be enabled via something like: + (a cyclic descriptor will reuse itself, triggering callbacks as expected, * and will not free itself when it finishes) + + desc = dmaengine_prep_dma_cyclic(uart->rx_dma_channel, uart->rx_dma_phy, + UART_XMIT_SIZE, DMA_RX_XCOUNT, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + desc->callback = adi_uart4_serial_dma_rx; + desc->callback_param = uart; + +MDMA controllers can be found automatically through the kernel's standard DMA memcpy API: + + struct dma_chan *chan = dma_find_channel(DMA_MEMCPY); diff --git a/Documentation/devicetree/bindings/gpio/gpio-adi-adrv906x.txt b/Documentation/devicetree/bindings/gpio/gpio-adi-adrv906x.txt new file mode 100644 index 00000000000000..8de41447a118c4 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-adi-adrv906x.txt @@ -0,0 +1,21 @@ +Analog Device Inc. ADRV906x SoC GPIO Controller + +Required properties: +- compatible: Compatible property value should be "adi,adrv906x-gpio". +- reg: Physical base address of the controller and length of memory mapped + region. +- #gpio-cells: Should be two. For consumer use see gpio.txt. +- ngpios: Number of GPIO lines (0..n-1) +- gpio-controller: Specifies that the node is a gpio controller. +- pintmux: Specifies the base address for the pin interrupt mux associated with the GPIO controller + +Example: + + gpio1: gpio@2021B000 { + compatible = "adi,adrv906x-gpio"; + reg = <0x2021B000 0x1000>; + pintmux = <0x20102200>; + gpio-controller; + #gpio-cells = <2>; + ngpios = ; + }; diff --git a/Documentation/devicetree/bindings/i2c/adi,twi.yaml b/Documentation/devicetree/bindings/i2c/adi,twi.yaml new file mode 100644 index 00000000000000..f13e492e6dda70 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/adi,twi.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/adi,twi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices Inter IC (I2C) controller + +maintainers: + - Slawomir Kulig + +properties: + compatible: + enum: + - adi,twi + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-khz: + description: Desired I2C bus clock frequency in kHz. If not specified, + frequency defined by CONFIG_I2C_ADI_TWI_CLK_KHZ will be + used (default 50 kHz). + If timing parameters match, the bus clock frequency can + be from 21 to 400 kHz. + default: 50 + minimum: 21 + maximum: 400 + +required: + - compatible + - "#address-cells" + - "#size-cells" + - reg + - interrupts + - clock-khz + - clocks + +additionalProperties: false + +examples: + - | + #include + #include + #define I2C_0_BASE 0x20760000 + #define I2C_IRQ_S2F_PIPED_0 149 + + i2c0: twi@I2C_0_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + status = "disabled"; + }; diff --git a/Documentation/devicetree/bindings/iio/dac/adi,pwm-dac.yaml b/Documentation/devicetree/bindings/iio/dac/adi,pwm-dac.yaml new file mode 100644 index 00000000000000..d4c0a76900b45a --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/adi,pwm-dac.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/dac/adi,pwm-dac.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices PWM DAC + +maintainers: + - Jie Zhang + +properties: + compatible: + const: adi,pwm-dac + + reg: + maxItems: 1 + + clocks: + items: + - description: Clock phandle for input clock + + adi,iovdd-microvolt: + description: | + The IOVDD voltagge [mV] + minimum: 0 + maximum: 5000000 + + adi,gpio-max-frequency: + description: | + The maximum frequency of GPIO [Hz] + minimum: 0 + maximum: 0xffffffff + +additionalProperties: false + +required: + - compatible + - reg + - clocks + - adi,iovdd-microvolt + - adi,gpio-max-frequency + +examples: + - | + #define PWM_BASE 0x20727000 + dac@PWM_BASE_UADDR { + compatible = "adi,pwm-dac"; + reg = ; + clocks = <&hsdigclk>; + adi,iovdd-microvolt = <3300000>; + adi,gpio-max-frequency = <122880000>; + }; +... diff --git a/Documentation/devicetree/bindings/misc/adi,tru.yaml b/Documentation/devicetree/bindings/misc/adi,tru.yaml new file mode 100644 index 00000000000000..467196f5c613ca --- /dev/null +++ b/Documentation/devicetree/bindings/misc/adi,tru.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/misc/adi,tru.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices Trigger Routing Unit (TRU) + +maintainers: + - Jie Zhang + +properties: + compatible: + const: adi,tru + + reg: + maxItems: 1 + + adi,tru-last-source-id: + description: | + The last TRU trigger source ID + minimum: 0 + maximum: 0xff + + adi,tru-last-target-id: + description: | + The last TRU trigger target ID + minimum: 0 + maximum: 0xffffffff + + adi,tru-connections-preset: + description: | + Preset connections between TRU sources and targets + + adi,tru-connections-preset-locked: + description: | + The preset connections between TRU sources and targets are locked. + +additionalProperties: false + +required: + - compatible + - reg + - adi,tru-last-source-id + - adi,tru-last-target-id + +examples: + - | + #define TRU_BASE 0x20052000 + + tru0: tru@TRU_BASE_UADDR { + compatible = "adi,tru"; + reg = ; + /* TODO replace 100 with actual last trigger source ID */ + adi,tru-last-source-id = <100>; + adi,tru-last-target-id = <79>; + /* each connection is */ + /* + adi,tru-connections-preset = <1 2>, + <25 32>, + <3 4>; + adi,tru-connections-preset-locked; + */ + }; +... diff --git a/Documentation/devicetree/bindings/mmc/sdhci-adrv.txt b/Documentation/devicetree/bindings/mmc/sdhci-adrv.txt new file mode 100644 index 00000000000000..64a9103d0624f3 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/sdhci-adrv.txt @@ -0,0 +1,72 @@ +* ADRV SDHCI controller + +This file documents the differences between the core properties in +Documentation/devicetree/bindings/mmc/mmc.txt and the properties used by the +sdhci-of-adi driver. + +Required properties: +- compatible: Must be "adi,dwcmshc-sdhci" for ADRV SDHCI controller + Must be "adi,sdhci-phy" for ADRV SDHCI PHY controller +- clocks: Phandlers to the clock. +- clock-names: Must be "core" +- cap-mmc-hw-reset: Mandatory in eMMC +- enable-phy-config: Enable PHY configuration + +Optional properties: +- adi,retune-period: Set periodic clock retune period +- adi,dcode-legacy: Set legacy (PHY Tx delay line) data code +- adi,dcode-hs200: Set HS200 (PHY Tx delay line) data code +- adi,dcode-hs400: Set HS400 (PHY Tx delay line) data code + +Example: + +mmcclk: mmcclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <245000000>; +}; + +mmc0_phy: phy@24724300 { + compatible = "adi,sdhci-phy"; + reg = <0x24724300 0x100>; + #phy-cells = <0>; + adi,dcode-legacy = <0x78>; + adi,dcode-hs200 = <0x00>; + adi,dcode-hs400 = <0x08>; + status = "disabled"; +}; + +mmc0_regulator: fixed-regulator_1v8 { + compatible = "regulator-fixed"; + regulator-name = "1V8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + status = "disabled"; +}; + +mmc0: mmc@24724000 { + compatible = "adi,dwcmshc-sdhci"; + reg = <0x247243000 0x300>; + interrupts = ; + clocks = <&mmcclk>; + clock-names = "core"; + phys = <&mmc0_phy>; + phy-names = "phy_adi_sdhci"; + max-frequency = <196608000>; + bus-width = <8>; + vqmmc-supply = <&mmc0_regulator>; + status = "disabled"; + enable-phy-config; + disable-wp; + non-removable; + no-sdio; + no-sd; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + adi,retune-period = <3600>; + cap-mmc-hw-reset; + #address-cells = <1>; + #size-cells = <0>; +}; diff --git a/Documentation/devicetree/bindings/net/adi,adrv906x-1g.yaml b/Documentation/devicetree/bindings/net/adi,adrv906x-1g.yaml new file mode 100644 index 00000000000000..a8c6ba0c563ad7 --- /dev/null +++ b/Documentation/devicetree/bindings/net/adi,adrv906x-1g.yaml @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/adi,adrv906x-1g.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADRV906X 1G Ethernet driver + +maintainers: + - Kim Holdt + - Daniel Mateu + +description: + Bindings for Analog Devices ADRV906X 1G Ethernet Interface Devices + +properties: + compatible: + const: adi,adrv906x-dwmac + + reg: + maxItems: 1 + description: The register base address and range + base-clk-speed: + maxItems: 1 + description: The default clock speed at initialization in Mhz + The speed can be 50MHz, 125MHz or 250MHz + mac-address: + description: Optionally filled in by u-boot if this property is + not defined and U-Boot is able to get the address + from OTP or NVMEM. + If the property is defined with any value including 0 + then it is not updated by U-Boot. + if-name: + description: Optional field that can be used for changing interface + name registered. Must fulfill netdev naming standards. +additionalProperties: false + +examples: + - | + #include + #include + + #define EMAC_1G_CLK_CTRL 0x20190000 + #define EMAC_1G_DIV_CTRL 0x201c0050 + #define EMAC_1G_BASE 0x20720000 + #define EMAC_1G_SBD_INTR_PIPED 298 + #define ADI_ADRV906X_PIN_88 (88U) + + // Common base: + emac0: ethernet@EMAC_1G_BASE_UADDR { + status = "okay"; + compatible = "adi,adrv906x-dwmac", "snps,dwmac-5.10a"; + reg = ; + interrupts = ; + interrupt-names = "macirq"; + clocks = <&emac0clk>; + clock-names = "stmmaceth"; + phy-mode = "rgmii"; + snps,reset-gpio = <&gpio0 ADI_ADRV906X_PIN_88 GPIO_ACTIVE_LOW>; + snps,reset-delays-us = <1000 1000 1000>; + snps,tso; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_emac0_rgmii>; + mac-address = [ 00 00 00 00 00 00 ]; /* will not be filled in + by u-boot */ + #address-cells = <1>; + #size-cells = <1>; + clock_divider { + reg = ; + ctrl_reg = ; + }; + }; + + /* Customization for RGMII auto-negotiation: + &emac0 { + phy-handle = <&phy0>; + mdio0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dwmac-mdio"; + phy0: ethernet-phy@0 { + reg = <15>; + }; + }; + }; + */ + + /* Customization for RMII fixed-link speed: + &emac0 { + phy-mode = "rmii"; + pinctrl-0 = <&pinctrl_emac0_rmii>; + max-speed = <100>; + + fixed-link { + speed = <100>; + full-duplex; + }; + }; + */ diff --git a/Documentation/devicetree/bindings/net/adi,adrv906x-net.yaml b/Documentation/devicetree/bindings/net/adi,adrv906x-net.yaml new file mode 100644 index 00000000000000..2a3b7344b631f5 --- /dev/null +++ b/Documentation/devicetree/bindings/net/adi,adrv906x-net.yaml @@ -0,0 +1,408 @@ +# SPDX-License-Identifier: GPL-2.0+ +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/adi,adrv906x-net.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ADRV906X Gigabit Ethernet driver + +maintainers: + - Kim Holdt + - Sheng Wang + +description: Bindings for ADRV906X 10G/25G Network Interface Controller Device + +properties: + compatible: + const: adi,adrv906x-net + reg: + maxItems: 1 + description: EMAC_CMN_BASE + recovered_clk_10g: + description: Clock divider value for the recovered Ethernet clock that + is used as a reference to support SyncE in 10 GbE operation + const: 22 + recovered_clk_25g: + description: Clock divider value for the recovered Ethernet clock that + is used as a reference to support SyncE in 25 GbE operation + const: 55 + ethernet-ports: + type: object + properties: + '#address-cells': + const: 1 + '#size-cells': + const: 1 + clock-names: + items: + - const: hsdig_clk + clocks: + items: + - description: Clock phandle for HSDIG clock + macsec: + description: Physical base address for MACsec instance + interrupts: + description: macsecX interrupt. Do only populate macsec1 when the switch + is enabled! + interrupt-names: + items: + - const: ts_event + ndma-handle: + maxItems: 1 + description: phandle on NIC-DMA associated with the port + phy-handle: + maxItems: 1 + description: phandle on PHY connected to the port + mac-address: + description: Optionally filled in by u-boot if property field is + not defined and U-Boot is able to get the address + from OTP or NVMEM. + If the property is defined with any value including 0 + then it is not updated by U-Boot. + adi,pcb-delay-tx-ns: + description: PCB delay for tx channel, which is part of static PHY + delay. This is the integer portion in units of ns. + default: 0 + minimum: 0 + maximum: 0xFFFF + + adi,pcb-delay-tx-frac-ns: + description: PCB delay for tx channel, which is part of static PHY + delay. This is the fractional portion in units of + 2^(-16)ns. For example, 1.125ns is represented by 1ns + and the frac-ns = 0x2000 + default: 0 + minimum: 0 + maximum: 0xFFFF + + adi,pcb-delay-rx-ns: + description: PCB delay for rx channel, which is part of static PHY + delay. This is the integer portion in units of ns. + default: 0 + minimum: 0 + maximum: 0xFFFF + + adi,pcb-delay-rx-frac-ns: + description: PCB delay for rx channel, which is part of static PHY + delay. This is the fractional portion in units of + 2^(-16)ns. For example, 1.125ns is represented by 1ns + and the frac-ns = 0x2000. + default: 0 + minimum: 0 + maximum: 0xFFFF + + patternProperties: + "^port@[0-9]+$": + type: object + description: ADI NIC external ports + properties: + id: + $ref: /schemas/types.yaml#definitions/uint32 + description: port id for the ethernet device + reg: + maxItems: 4 + description: The address and size of the register must be + in the below list order! + - The physical base address and size of the XMAC registers + - The physical base address and size of the EMAC TX control + registers + - The physical base address and size of the EMAC RX control + registers + - The physical base address and size of the TSU control + registers + if-name: + description: Optional field that can be used for changing interface + name registered. Must fulfill netdev naming standards. + additionalProperties: false + additionalProperties: false + + ndmaX: + type: object + description: NDMA X supporting DDE DMA + properties: + id: + $ref: /schemas/types.yaml#definitions/uint32 + description: NDMA device id number + reg: + maxItems: 5 + description: + The physical base address(s) and size of the NDMA X's registers. + The 1st region is the NDMA TX register base and size. + The 2nd region is the DMA TX data register base and size. + The 3rd region is the DMA TX status register base and size. + The 4th region is the NDMA RX register base and size. + The 5th region is the DMA RX data and status register base and size. + reset-ctrl: + description: + control ndmaX tx & rx reset + interrupts: + items: + - description: dmaX tx data done interrupt + - description: dmaX tx data error interrupt + - description: dmaX tx status done interrupt + - description: dmaX tx status error interrupt + - description: dmaX rx done interrupt + - description: dmaX rx error interrupt + interrupt-names: + items: + - const: tx_data_dma_done + - const: tx_data_dma_error + - const: tx_status_dma_done + - const: tx_status_dma_error + - const: rx_dma_done + - const: rx_dma_error + interrupt-ctrl: + description: control register address for NDMA interrupt enable + additionalProperties: false + + oran_if: + type: object + properties: + reg: + description: Enable O-RAN IF to forward traffic to NDMA. + reg-names: + items: + - const: OIF_0_RX_CTRL + - const: OIF_0_TX_CTRL + - const: OIF_1_RX_CTRL + - const: OIF_1_TX_CTRL + additionalProperties: false + + mdio_if: + type: object + properties: + reg: + description: Enable MDIO bus interface binding for PHY control. + regnames: + items: + - const: EMAC_PCS_0_BASE + - const: EMAC_PCS_1_BASE + patternProperties: + "^_phy@[0-9a-f]+$": + type: object + properties: + reg: + minimum: 0 + maximum: 3 + description: The ID number for the PHY, globally unique. + speed: + enum: [10000, 25000] + default: 25000 + description: Default PHY speed in Mbits/sec. + compatible: + const: ethernet-phy-ieee802.3-c45 + description: PHY implements IEEE802.3 clause 45. + additionalProperties: false + additionalProperties: false + + eth_recov_clk: + type: object + properties: + reg: + items: + description: Enable Ethernet recovered clock output.reg name + + reg-names: + items: + - const: EMAC_RECOVERED_CLK_CTRL + additionalProperties: false + + eth_switch: + type: object + description: Switch used for daisy-chain and 8t8r operational modes. + properties: + '#address-cells': + const: 1 + '#size-cells': + const: 1 + reg: + maxItems: 2 + description: Physical base address for switch and switch matching + engine. + interrupts: + items: + description: switchportX error interrupt + interrupt-names: + items: + - const: switch_error_X + trap-ptp-forwardable: + description: Enable optional trapping of PTP forwardable packets. + type: boolean + + patternProperties: + "^switch-port@[0-2]+$": + type: object + description: A child node for each physical port on the built-in switch. + Must be in numerical order! + properties: + id: + $ref: /schemas/types.yaml#definitions/uint32 + description: port id for the ethernet switch, 0-2 + reg: + maxItems: 1 + description: Physical base address for the specific switch port. + additionalProperties: false + additionalProperties: false + +required: + - compatible + - reg + - ndma + - dmas + - dma-names + - ethernet-ports + - mdio-handle + - phy-hanlde + - oran_if + +unevaluatedProperties: false + +examples: + - | + #include + #include + + #define EMAC_CMN_BASE 0x2B300000 + #define EMAC_MAC_0_BASE 0x2B330000 + #define EMAC_MAC_0_RX 0x2B331300 + #define EMAC_MAC_0_TX 0x2B330300 + #define EMAC_MAC_1_BASE 0x2B340000 + #define EMAC_MAC_1_RX 0x2B341300 + #define EMAC_MAC_1_TX 0x2B340300 + #define EMAC_PCS_0_BASE 0x2B310000 + #define EMAC_PCS_0_TSU 0x2B310400 + #define EMAC_PCS_1_BASE 0x2B320000 + #define EMAC_PCS_1_TSU 0x2B320400 + #define EMAC_SW_BASE 0x2B350100 + #define EMAC_SW_MAE_BASE 0x2B350300 + #define EMAC_SW_PORT_0_BASE 0x2B350600 + #define EMAC_SW_PORT_1_BASE 0x2B350D00 + #define EMAC_SW_PORT_2_BASE 0x2B351400 + #define OIF_0_RX_CTRL 0x2b103040 + #define OIF_0_TX_CTRL 0x2b10903c + #define OIF_1_RX_CTRL 0x2b104340 + #define OIF_1_TX_CTRL 0x2b10b83c + #define EMAC_RECOVERED_CLK_CTRL 0x20102d00 + #define NDMA_0_RX_BASE 0x20214000 + #define NDMA_0_RX_DMA 0x20260000 + #define NDMA_0_TX_BASE 0x20216000 + #define NDMA_0_TX_DMA 0x20261000 + #define NDMA_0_TX_STATUS_DMA 0x20264000 + + #define NDMA_DMA_ERR_INTR_GATED_0 210 + #define NDMA_DMA_ERR_INTR_GATED_1 211 + #define NDMA_DMA_DONE_INTR_GATED_0 214 + #define NDMA_DMA_DONE_INTR_GATED_1 215 + #define NDMA_STATUS_DMA_ERR_INTR_GATED_0 218 + #define NDMA_STATUS_DMA_ERR_INTR_GATED_1 219 + #define DEBUG_DDE_ERR_INTR_0 220 + #define DEBUG_DDE_ERR_INTR_1 221 + #define NDMA_STATUS_DMA_DONE_INTR_GATED_0 222 + #define NDMA_STATUS_DMA_DONE_INTR_GATED_1 223 + #define ETH_IRQ_PCS_RX_ERROR_0 787 + #define ETH_IRQ_PCS_RX_ERROR_1 788 + + adrv906x_net0: adrv906x_net@EMAC_CMN_BASE_UADDR { + compatible = "adi,adrv906x-net"; + reg = ; + #address-cells = <1>; + #size-cells = <1>; + + ethernet-ports { + port@0 { + id = <0>; + reg = , , + , ; + phy-handle = <&adrv906x_phy0>; + mac-address = [ 00 00 00 00 00 00 ]; /* will not be filled in + by u-boot */ + ndma-handle = <&ndma0>; + clocks = <&hsdigclk>; + clock-names = "hsdig_clk"; + adi,pcb-delay-tx-ns = <0>; + adi,pcb-delay-tx-frac-ns = <0>; + adi,pcb-delay-rx-ns = <0>; + adi,pcb-delay-rx-frac-ns = <0>; + }; + port@1 { + id = <1>; + reg = , , + , ; + phy-handle = <&adrv906x_phy1>; + mac-address = [ 00 00 00 00 00 00 ]; /* will not be filled in + by u-boot */ + ndma-handle = <&ndma1>; + clocks = <&hsdigclk>; + clock-names = "hsdig_clk"; + adi,pcb-delay-tx-ns = <0>; + adi,pcb-delay-tx-frac-ns = <0>; + adi,pcb-delay-rx-ns = <0>; + adi,pcb-delay-rx-frac-ns = <0>; + }; + }; + + oran_if { + reg = , , + , ; + }; + + mdio_if { + reg = , ; + #address-cells = <1>; + #size-cells = <0>; + adrv906x_phy0: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <0>; + speed = <10000>; + }; + adrv906x_phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <1>; + speed = <10000>; + }; + }; + + eth_recov_clk { + reg = ; + }; + + eth_switch { + reg = ; + interrupt-names = "switch_error_0", "switch_error_1"; + interrupts = , + ; + switch_port0:switch-port@0 { + id = <0>; + reg = ; + }; + switch_port1:switch-port@1 { + id = <1>; + reg = ; + }; + switch_port2:switch-port@2 { + id = <2>; + reg = ; + }; + }; + }; + + ndma0: ndma0@NDMA_0_TX_BASE_UADDR { + id = <0>; + reg = , + , + , + , + ; + reset-ctrl = <&ndma_rst>; + interrupts = , + , + , + , + , + ; + interrupt-names = "tx_data_dma_done", "tx_data_dma_error", + "tx_status_dma_done", "tx_status_dma_error", + "rx_dma_done", "rx_dma_error"; + interrupt-ctrl = <&ndma0_interrupt_ctrl>; + }; +... diff --git a/Documentation/devicetree/bindings/pinctrl/adi,adrv906x-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/adi,adrv906x-pinctrl.txt new file mode 100644 index 00000000000000..5506643d452ea7 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/adi,adrv906x-pinctrl.txt @@ -0,0 +1,115 @@ +* Analog Devices Inc. (ADI) - Pinmux Controller for the ADRV906X SoC + +The ADRV906X SoC integrates many internal function blocks (UART, SPI, I2C, GPIO, among others) +which can be optionally enabled by the end user. To conserve the number of pins, and +reduce overall package size, a Pinmux Controller is utilzed. This Pinmux Controller +allows for several of these function blocks to share a single Input/Output pin. +The number of configurable pins, number of sources per pin, their mappings is +described in the referenced header file (pinctrl-adi-adrv906x-io-pad.h). This +readme describes how to setup the ADRV906X SoC's configurable I/O mapping. See the +SoC documentation for more information. + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices, including the meaning of the +phrase "pin configuration node". + +ADI ADRV906X pin configuration node is a node of a group of pins which can be +used for a specific device or function. This node represents the pin number, +source mux, and config of the pins in that group. The 'source mux' selects the +function that connects to specified pin. This pin can work on and the 'config' +configures various pad settings such as pull-up, open drain, drive strength, etc. + +Required properties for pinmux controller: +- compatible: "adi,adrv906x-pinctrl" + +Pin Configuration: +- adi,pins: each pinmux entry consists of 3 32-bit integers representing: + + 1. Pin Number + 2. Source Mux Value + 3. Configuration Word + +A header file is provided, include/dt-bindings/pinctrl/pinctrl-adi-adrv906x-io-pad.h +This header provides SoC specifiec pin controller info such as + 1. Total configurable I/O + 2. Maximum pinmux sources per pin + 3. Maximum pinmux sources available + +Also provided pre configured descriptive Pin IDs (which contain Pin Number and Source Mux) +for all possible I/O configurations. Below illustrate these Pin IDs. See the above referenced +header for up-to-date PIN ID values. + +PIN-ID table example from pinctrl-adi-adrv906x-io-pad.h: + + PIN-ID PIN_NUMBER MUX_SRC +---------------------------------------------------------------------------------------- + #define UART4_CTSIN_PIN ADI_ADRV906X_PIN_51 ADI_PINMUX_SRC_SEL_1 + #define UART4_RTSOUT_PIN ADI_ADRV906X_PIN_50 ADI_PINMUX_SRC_SEL_4 + #define UART4_RXSIN_PIN ADI_ADRV906X_PIN_30 ADI_PINMUX_SRC_SEL_3 + #define UART4_TXSOUT_PIN ADI_ADRV906X_PIN_31 ADI_PINMUX_SRC_SEL_1 + + +Configuration Word 32-BIT Description: +BIT(s) Name Description +---------------------------------------------------------------------------------------- +31:7 unused N/A +6 PULLUP_ENABLE Setting this bit to '1' enables pullup of selected pin, '0' + for pulldown. +5 PULL_ENABLEMENT Setting this bit to '1' enables the pull (up/down) capability + of the selected pin. +4 SCHMITT_TRIGGER_ENABLE Setting this bit to a '1' enables the schmitt trigger input + capability +3:0 DRIVE_STRENGTH This 4-bit field specifies the drive strength (0-15) of the + selected output signal. See the SoC documentation for + more detail. + +The referenced helper file also contains configuration word helper macros that can be utilized +within the device tree descriptions. See these helper macros in pinctrl-adi-adrv906x-io-pad.h, also +see usage examples below. + +NOTE: +Some requirements for using adi,adrv906x-pinctrl binding: + +1. We have pin function node defined under the controller node to represent + what pinmux functions this SoC supports. +2. The driver can use the function node's name and pin configuration node's + name describe the pin function and group hierarchy. + For example, Linux adrv906x-pinctrl driver takes the function node's name + as the function name and pin configuration node's name as group name to + create the map table. +3. Each pin configuration node should have a phandle, devices can set pins + configurations by referring to the phandle of that pin configuration node. +4. The gpio controller must be describe in the pinctrl simple-bus. + +Examples: +&spi0 { + storage,flash-handle = <&flash>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi0>; + + flash: mt25q@0 { + compatible = "n25q128a11", "jedec,spi-nor"; + spi-max-frequency = <100000000>; + reg = <0>; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + + #include "adrv906x-nor-flash-part.dtsi" + }; +}; + +&pinctrl0 { + pinctrl_spi0: spi0-grp { + adi,pins = < + QSPI_FLASH_CLK_PIN (ADI_CONFIG_NO_PULL) + QSPI_FLASH_SELB_PIN (ADI_CONFIG_ENABLE_PULLUP | ADI_CONFIG_DRIVE_STRENGTH_4) + QSPI_FLASH_D0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + QSPI_FLASH_D1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + QSPI_FLASH_D2_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + QSPI_FLASH_D3_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + QSPI_FLASH_RESETN_PIN (ADI_CONFIG_ENABLE_PULLUP | ADI_CONFIG_DRIVE_STRENGTH_4) + QSPI_FLOW_READY_PIN (ADI_CONFIG_NO_PULL) + >; + }; + .... +}; diff --git a/Documentation/devicetree/bindings/ptp/ptp-adrv906x-soc.yaml b/Documentation/devicetree/bindings/ptp/ptp-adrv906x-soc.yaml new file mode 100644 index 00000000000000..f264b5b70b76d9 --- /dev/null +++ b/Documentation/devicetree/bindings/ptp/ptp-adrv906x-soc.yaml @@ -0,0 +1,146 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ptp/ptp-adrv906x-soc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ADRV906X SoC PTP Clock Device Tree +maintainers: + - Kim Holdt + +properties: + compatible: + const: adi,adrv906x-ptp + + reg: + maxItems: 1 + + clock-names: + items: + - const: lc_clk + - const: gc_clk + clocks: + items: + - description: Clock phandle for ToD counter local clock, refer + to common clock bindings. + - description: Clock phandle for Golden Counter clock. + + interrupts: + items: + description: ToD PPS interrupt + interrupt-names: + items: + - const: pps + + adi,max-adj: + description: + The maximum possible frequency adjustment, in parts per billion + minimum: 0 + maximum: 1000000000 + + adrv906x-tod: + type: object + properties: + adi,default-tod-counter: + description: Default selected ToD counter for the local ToD + and CDC output. + + adi,ppsx-pulse-width-ns: + description: Value of PPSX pulse width in nanoseconds. Default + is 10000000 (10ms). + minimum: 1 + maximum: 99999999 + + adi,external-pps: + type: boolean + description: + This property is only for debugging or special use cases. + If present, the PPS (not PPSX) output signal and trigger for the + /dev/ppsX device(s) will be sourced from the input PPS signal. + + adi,cdc-delay-value: + description: + this property is only for debugging or special use cases. + Instead, for normal operation, we need to configure this on the basis + of the Ethernet interface speed (which dictates the frequency for the + Ethernet Subsystem) and the 'devclk' frequency. Default is '0'. + minimum: 0 + maximum: 31 + additionalProperties: false + patternProperties: + "tod-counter@[0-2]+$": + type: object + description: + ADRV906x ToD counter(s). Each ToD node declares a counter that can be + synchronized to an external source, and will be represented by a + /dev/ptpX and /dev/ppsX. + properties: + reg: + description: The index of the ToD counter. + minimum: 0 + maximum: 2 + + adi,pps-mode: + type: boolean + description: + The read and write trigger mode of the ToD counter. + If present, the ToD counter runs in the 1PPS trigger + mode, otherwise, the counter runs in the GC trigger mode. + + adi,trigger-delay-tick: + description: + The trigger tick count for the GC trigger mode based on the + clock-frequency. + minimum: 0 + maximum: 0xFFFFFFFFFFFF + additionalProperties: false + + clock-pll: + type: object + properties: + adi,i2c-clk: + description: The reference to the i2c connected clock node when + use the i2c connecting to the clock chip directly. + additionalProperties: false + +required: + - compatible + - reg + - interrupts + - interrupt-names + - clock-names + - clocks + - adrv906x-tod + - clock-pll + +additionalProperties: false + +examples: + - | + #include + #include + #define EMAC_TOD_BASE 0x2B380000 + #define TOD_IRQ 201 + + ptpclk: ptpclk { + compatible = "adi,adrv906x-ptp"; + reg = ; + interrupts = ; + interrupt-names = "pps"; + clocks = <&sysclk>, <&sysclk>; + clock-names = "lc_clk", "gc_clk"; + adi,max-adj = <50>; + + adrv906x-tod { + adi,default-tod-counter = <0>; + adi,cdc-delay-value = <0 0 0 0>; + tod-counter@0 { + reg = <0>; + adi,pps-mode; + adi,trigger-delay-tick = <491520>; + }; + }; + clock-pll { + adi,i2c-clk = <&ad9545>; + }; + }; diff --git a/Documentation/devicetree/bindings/ptp/ptp-adrv906x-tod.yaml b/Documentation/devicetree/bindings/ptp/ptp-adrv906x-tod.yaml new file mode 100644 index 00000000000000..6112a05af75f4a --- /dev/null +++ b/Documentation/devicetree/bindings/ptp/ptp-adrv906x-tod.yaml @@ -0,0 +1,180 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ptp/ptp-adrv906x-tod.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ADRV906x PTP Clock ToD Device Tree + +maintainers: + - Kim Holdt + +properties: + compatible: + const: adi,adrv906x-tod + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + reg: + maxItems: 4 + description: Base address and size of the register set for the device. + The second pair is optional and is used for two-chip instances. + + clock-names: + items: + - const: lc_clk + - const: gc_clk + + clocks: + items: + - description: Clock phandle for ToD counter local clock, + refer to common clock bindings. + - description: Clock phandle for Golden Counter clock. + + interrupts: + items: + - description: ToD PPS interrupt + + interrupt-names: + items: + - const: pps + + adi,ppsx-pulse-width-ns: + description: Value of PPSX pulse width in nanoseconds. Default is 10000000 + (10ms). + minimum: 1 + maximum: 99999999 + + adi,external-pps: + type: boolean + description: + This property is only for debugging or special use cases. + If present, the PPS (not PPSX) output signal and trigger for the /dev/ppsX + device(s) will be sourced from the input PPS signal. + + adi,pps-in-pulse-width-ms: + description: + The expected pulse width of the input PPS signal in milliseconds. + minimum: 0 + maximum: 1000 + + adrv906x-tod: + type: object + properties: + adi,default-tod-counter: + description: Default selected ToD counter for the local ToD and CDC + output. + + adi,cdc-delay-value: + description: + this property is only for debugging or special use cases. + Instead, for normal operation, we need to configure this on the basis + of the Ethernet interface speed (which dictates the frequency for + the Ethernet Subsystem) and the 'devclk' frequency. + Default is 0. + minimum: 0 + maximum: 31 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + patternProperties: + "tod-counter@[0-2]+$": + type: object + description: ADRV906x ToD counter(s). Each ToD node declares a + counter that can be synchronized to an external source, and will + be represented by a /dev/ptpX and /dev/ppsX. + properties: + reg: + description: The index of the ToD counter. + minimum: 0 + maximum: 2 + + adi,pps-mode: + type: boolean + description: + The read and write trigger mode of the ToD counter. + If present, the ToD counter runs in the 1PPS trigger mode, + otherwise, the counter runs in the GC trigger mode. + + adi,trigger-delay-tick: + description: + The trigger delay for the GC trigger mode in clock cycles. + Default is 491520. + minimum: 0 + maximum: 0xFFFFFFFF + additionalProperties: false + additionalProperties: false + +required: + - compatible + - reg + - interrupts + - interrupt-names + - clock-names + - clocks + - adrv906x-tod + +additionalProperties: false + +examples: + - | + #include + #include + #define EMAC_TOD_BASE 0x2B380000 + #define SEC_EMAC_TOD_BASE 0x2F380000 + #define TOD_IRQ 201 + + ptpclk: ptpclk { + compatible = "adi,adi-tod"; + #address-cells = <1>; + #size-cells = <1>; + reg = ; + interrupts = ; + interrupt-names = "pps"; + clocks = <&sysclk>, <&sysclk>; + clock-names = "lc_clk", "gc_clk"; + + adrv906x-tod { + adi,default-tod-counter = <0>; + #address-cells = <1>; + #size-cells = <0>; + tod-counter@0 { + reg = <0>; + adi,trigger-delay-tick = <491520>; + }; + }; + }; + + - | + ptpclk2: ptpclk { + compatible = "adi,adi-tod"; + #address-cells = <1>; + #size-cells = <1>; + reg = ; + interrupts = ; + interrupt-names = "pps"; + clocks = <&sysclk>, <&sysclk>; + clock-names = "lc_clk", "gc_clk"; + adi,ppsx-pulse-width-ns = <10000000>; + adi,external-pps; + + adrv906x-tod { + adi,default-tod-counter = <0>; + adi,cdc-delay-value = <0 0 0 0>; + #address-cells = <1>; + #size-cells = <0>; + tod-counter@0 { + reg = <0>; + adi,pps-mode; + adi,trigger-delay-tick = <491520>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/spi/adi,spi3.yaml b/Documentation/devicetree/bindings/spi/adi,spi3.yaml new file mode 100644 index 00000000000000..9197e587bd9fef --- /dev/null +++ b/Documentation/devicetree/bindings/spi/adi,spi3.yaml @@ -0,0 +1,118 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/adi,spi3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices SPI3 controller driver + +description: | + The ADI SPI3 controller is used to communicate with external devices using + the Serial Peripheral Interface. It supports full-duplex, half-duplex and + simplex synchronous serial communication with external devices. It supports + 8, 16, 32-bit data size. + +maintainers: + - Greg Malysa + - Slawomir Kulig + +allOf: + - $ref: "spi-controller.yaml#" + +properties: + compatible: + enum: + - adi,spi3 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: spi + + interrupts: + maxItems: 1 + + resets: + maxItems: 1 + + dmas: + description: | + DMA specifiers for tx and rx dma. + items: + - description: rx DMA channel + - description: tx DMA channel + + dma-names: + items: + - const: rx + - const: tx + +patternProperties: + "^[a-zA-Z][a-zA-Z0-9,+\\-._]{0,63}@[0-9a-f]+$": + type: object + # SPI slave nodes must be children of the SPI master node and can + # contain the following properties. + properties: + adi,enable-dma: + description: | + Use DMA for data transfers to/from SPI controller. + adi,open-drain-mode: + description: | + Enable ODM - Open Drain Mode for all output pins. + adi,psse: + description: | + Enable PSSE - Controls signalling of MODF error. + additionalProperties: false + +required: + - compatible + - reg + - clocks + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include + #include + + spi@f9068000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = <0xf9068000 0xff>; + interrupts = ; + clocks = <&adi_spi_v3_clk>; + clock-names = "spi"; + dmas = <&pdma 0>, <&pdma 1>; + dma-names = "tx", "rx"; + pinctrl-names = "default"; + pinctrl-0 = <&spi3_default>; + cs-gpios = <0>, <&gpa 13 GPIO_ACTIVE_LOW>; + status = "disabled"; + + spidev@0 { + compatible = "rohm,dh2228fv"; + reg = <0>; + spi-max-frequency = <4000000>; + spi-cpol; + spi-cpha; + adi,enable-dma; + adi,open-drain-mode; + adi,psse; + }; + + spidev@1 { + compatible = "rohm,dh2228fv"; + reg = <1>; + spi-max-frequency = <4000000>; + }; + }; + +... diff --git a/Documentation/i2c/busses/i2c-adi-twi.rst b/Documentation/i2c/busses/i2c-adi-twi.rst new file mode 100644 index 00000000000000..65e1b6dbb6b430 --- /dev/null +++ b/Documentation/i2c/busses/i2c-adi-twi.rst @@ -0,0 +1,71 @@ + Analog Devices TWI adapter driver + +Copyright (C) 2014 - 2018 Analog Devices Inc. + +This document describes the driver for the on-chip TWI (I2C) controllers (Analog Devices IP blocks). + +1) Kernel Configuration +The kernel configuration option is CONFIG_I2C_ADI_TWI: + Device Drivers ---> I2C support ---> I2C Hardware Bus support ---> ADI TWI I2C support + +CONFIG_I2C_ADI_TWI_CLK_KHZ: is to set serial clock frequencies (SCL), which can vary from 21kHz to 400kHz. + +In situations where a user wants simple means to send and receive I2C messages, the i2c-dev driver can be used. i2c-dev +provides a userspace accessible means to communicate with the I2C interface. To enable the i2c-dev driver (the kernel +configuration option is CONFIG_I2C_CHARDEV): + Device Drivers ---> I2C support ---> I2C device interface + +2) Driver parameters list + This driver does not take in any parameters. + +3) Command line options + This driver does not take in any command-line options. + +4) Driver information and notes + +4.1) I2C introduction. +I2C is a bidirectional low-speed serial bus that provides a simple, efficient method of data exchange, minimizing the +interconnection between devices. Multiple slave devices may be accessed over the same bus, using a unique 7-bit +address for each slave. Communication on the bus is half-duplex, and slaves do not transmit any data unless a master +has addressed it first. + +From the Linux OS point of view the I2C driver has two parts: +o Bus driver - low-level interface that is used to communicate with the I2C bus +o Chip driver - the interface between other device drivers and the I2C bus driver +The I2C bus driver is a low-level interface that is used to interface with the I2C bus. This driver is invoked by the +I2C chip driver, and it is not exposed to the user space. The Linux kernel contains a core I2C module that is used by +the chip driver to access the bus driver to transfer data over the I2C bus. + +This document focuses on the bus driver provided by Analog Devices, product developer needs to provide an implementation +of the chip driver to connect a specific I2C slave device to applications running under Linux. + +4.2) I2C bus driver overview. +The I2C bus driver is invoked only by the chip driver and is not exposed to the user space. The Linux kernel contains +a core I2C module that is used by the chip driver to access the I2C bus driver to transfer data over the I2C bus. The +chip driver uses a standard kernel space API that is provided in the Linux kernel to access the core I2C module. The +standard I2C kernel functions are documented in the files available under Documentation/i2c in the kernel source tree. + +4.3) Driver Features +The I2C driver supports the following features: +o Compatibility with the I2C bus standard +o Bit rates from 21Kbps to 400 Kbps +o Power management features by suspending and resuming I2C. +o 7-bit addressing (note: 10-bit address mode is not supported due to some patent issue) +o I2C master mode of operation (note: driver does not support the slave mode) +o Interrupt-driven data transfer + +4.4) I2C bus driver software operation +The I2C bus driver is described by a structure called i2c_adapter. The most important field in this structure is +struct i2c_algorithm +*algo . This field is a pointer to the i2c_algorithm structure that describes how data is transferred over the I2C +bus.* +The algorithm structure contains a pointer to a function that is called whenever the I2C chip driver wants to +communicate with an I2C device. During startup, the I2C bus adapter is registered with the I2C core when the driver is +loaded. Certain architectures have more than one I2C module. If so, the driver registers separate i2c_adapter +structures for each I2C module with the I2C core. These adapters are unregistered (removed) when the driver is +unloaded. During normal communication, it times out and returns an error when the transfer has some error condition, +such as NACK is detected. When an error condition occurs, the I2C driver should stop current transfer. + +4.5) Device-tree support. +Please see the following document: + Documentation/devicetree/bindings/i2c/adi,twi.yaml diff --git a/Documentation/misc-devices/adi-tru.rst b/Documentation/misc-devices/adi-tru.rst new file mode 100644 index 00000000000000..cb924083b83304 --- /dev/null +++ b/Documentation/misc-devices/adi-tru.rst @@ -0,0 +1,82 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================== +ADI Trigger Routing Unit (TRU) +============================== + +The adi-tru driver exports a simple sysfs interface. This allows you to +control TRU and view its status. + +Files +===== + +Each TRU device will have a set of enable, reset, status, mtr and ssr/ssrN +files. + +The enable file is used to enable/disable the device. + +The reset file is used to do a soft reset of the device. + +The status file is used to view the error status of the device. + +The mtr file is used to manually generate triggers by writing source IDs. + +The ssr/ssrN files are used to connect source trigger IDs to target trigger IDs. + +Example +======= + +Locate the device in your sysfs tree. This is probably easiest by going into +the platform devices directory and locating the device by its base address:: + + # ls /sys/bus/platform/devices/ + ... + 3fe0b000.tru + 3fe0c000.tru + ... + +Let's take a look at one of the TRU devices (unrelated sysfs entries have been +trimmed):: + + # ls /sys/bus/platform/devices/3fe0b000.tru + enable mtr reset ssr status + # ls /sys/bus/platform/devices/3fe0b000.tru/ssr + ssr0 ssr108 ssr118 ssr2 ssr3 ssr4 ssr5 ssr6 ssr7 ssr8 ssr9 + ssr1 ssr109 ssr119 ssr20 ssr30 ssr40 ssr50 ssr60 ssr70 ssr80 ssr90 + ... + +You can use simple reads/writes to access these files:: + + # cd /sys/bus/platform/devices/3fe0b000.tru + + # cat enable + 1 + # echo 0 > enable + # cat enable + 0 + # echo 1 > enable + # cat enable + 1 + + # cat status + 0: no address error, no lock write error + + # cat ssr/ssr0 + 0 + # echo 23 > ssr/ssr0 + # cat ssr/ssr0 + 23 + + # cat ssr/ssr1 + 0 + # echo "45,locked" > ssr/ssr1 + # cat ssr/ssr1 + 45(locked) + # echo 0 > ssr/ssr1 + -bash: echo: write error: Invalid argument + + # echo 0x1245 > mtr + # cat mtr + 0x00001245 + + # echo 1 > reset diff --git a/Documentation/spi/spi-adi-v3.rts b/Documentation/spi/spi-adi-v3.rts new file mode 100644 index 00000000000000..8393098de8b996 --- /dev/null +++ b/Documentation/spi/spi-adi-v3.rts @@ -0,0 +1,65 @@ + Analog Devices SPI3 controller driver + +Copyright (C) 2014 - 2018 Analog Devices Inc. + +This document describes the driver for the on-chip SPI3 controllers (Analog Devices IP blocks). + +This driver only supports SPI master controller mode. + +1) Kernel Configuration + +The kernel configuration option is SPI_ADI_V3: + Device Drivers ---> SPI support ---> SPI controller v3 for ADI + +In situations where a user wants simple means to send and receive SPI messages, the spidev driver can be +used. Spidev provides a userspace accessible means to communicate with the SPI interface. If you want to +use user space API, enable the spidev driver (the kernel configuration option is SPI_SPIDEV): + Device Drivers ---> SPI support ---> User mode SPI device driver support + +2) Driver parameters list + This driver does not take in any parameters. + +3) Command line options + This driver does not take in any command line options. + +4) Driver information and notes + +4.1) Features +The driver has the following features: + +- Support for ADI SPIv3/SPIv4 IP modules +- SPI PIO and SPI DMA data transfers. +- Internal and external (GPIO) chip selects. +- Per-slave device (chip) configuration. +- System-wide PM suspend, resume support. + +4.2) Implementation +The controller driver incorporates the standard queueing mechanism which the SPI core framework provides - +where &struct spi_message FIFO is serviced by a kernel thread. The kernel thread - spi_pump_messages() - drives +message FIFO and is responsible for queuing SPI transactions as well as setting up and launching the DMA or PIO +message transfers - this is done via a set of callbacks provided and registered by the controller driver in the +probe(). + +4.3) Platform information +The SPI core framework defines several mandatory and optional configuration information that can be passed +through the device-tree for both the SPI controller (master) and the associated SPI device (slave). Additionally ADI +SPI3 master controller driver provides an extension for slave device configuration via the structure "adi_spi_device". +The adi_spi master controller driver will use this configuration whenever the driver communicates with the +slave device. The below fields are optional (disabled by default). + +struct adi_spi_device { + bool dma; + u32 control; +}; + +o dma: Field informs the driver to leverage DMA mode when transferring data to/from the SPI controller. +o control: Field is used to enable ODM (Open Drain Mode for all output pins) and PSSE (Controls signalling of +MODF error) - see the "3.1.2 SPI_CONTROL" section of "SPI3_specification.pdf". + +4.4) DMA and PIO I/O Support +The adi_spi driver supports both DMA and PIO message transfers. PIO is the default mode of operation. Set the "dma" +flag in the "adi_spi_device" structure to enable DMA mode. + +4.5) Device-tree support. +Please see the following document: + Documentation/devicetree/bindings/spi/adi,spi3.yaml diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index 6c6d11536b42ec..78de41a6798f29 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -292,6 +292,11 @@ config ARCH_ROCKCHIP This enables support for the ARMv8 based Rockchip chipsets, like the RK3368. +config ARCH_ADRV906X + bool "ADI ADRV906X SoC" + help + This enables support for Analog Devices Incorporated's ADRV906X SoC family. + config ARCH_SEATTLE bool "AMD Seattle SoC Family" help diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile index 21cd3a87f38530..9b3996a8e01d8e 100644 --- a/arch/arm64/boot/dts/Makefile +++ b/arch/arm64/boot/dts/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 subdir-y += actions +subdir-y += adi subdir-y += airoha subdir-y += allwinner subdir-y += altera diff --git a/arch/arm64/boot/dts/adi/Makefile b/arch/arm64/boot/dts/adi/Makefile new file mode 100644 index 00000000000000..f7bb6e482dd2bd --- /dev/null +++ b/arch/arm64/boot/dts/adi/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_ARCH_ADRV906X) += adrv906x-denali-4.dtb +dtb-$(CONFIG_ARCH_ADRV906X) += adrv906x-denali-8.dtb +dtb-$(CONFIG_ARCH_ADRV906X) += adrv906x-titan-4.dtb +dtb-$(CONFIG_ARCH_ADRV906X) += adrv906x-titan-8.dtb +dtb-$(CONFIG_ARCH_ADRV906X) += adrv906x-secondary.dtb diff --git a/arch/arm64/boot/dts/adi/adrv906x-denali-4.dts b/arch/arm64/boot/dts/adi/adrv906x-denali-4.dts new file mode 100644 index 00000000000000..3e61d20e09b7ff --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-denali-4.dts @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Analog Devices Incorporated, All Rights Reserved + */ + +#include "adrv906x.dtsi" +#include "adrv906x-sysc.dtsi" +#include "adrv906x-protium.dtsi" +#include "adrv906x-eth-4t4r-dc.dtsi" + +/ { + model = "ADI ADRV906X Denali 4T4R Evaluation Kit"; + compatible = "adi,adrv906x-denali-4", "adi,adrv906x"; + + chosen { + bootargs = "console=ttyAMA0,115200n8 earlycon=pl011,0x20060000 rootwait uio_pdrv_genirq.of_id=generic-uio panic=-1 reboot=w"; + stdout-path = &uart0; + }; + + /* These UIO devices are for debugging purposes - remove for production */ + uio-ethernet-debug@EMAC_CMN_BASE_UADDR { + compatible = "generic-uio"; + reg = ; + }; + + uio-ethernet-debug-tod@EMAC_TOD_BASE { + compatible = "generic-uio"; + reg = ; + }; + + uio-ndma@NDMA_0_RX_BASE_UADDR { + compatible = "generic-uio"; + reg = ; + }; + + leds: gpio-leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_leds>; + ds1 { + gpios = <&gpio0 ADI_ADRV906X_PIN_74 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + ds3 { + gpios = <&gpio0 ADI_ADRV906X_PIN_75 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + ethernet-phy0-ds5 { + gpios = <&gpio0 ADI_ADRV906X_PIN_76 GPIO_ACTIVE_HIGH>; + default-state = "off"; + linux,default-trigger = "2b300000.adrv906x_net:00:link"; + }; + ethernet-phy1-ds7 { + gpios = <&gpio0 ADI_ADRV906X_PIN_77 GPIO_ACTIVE_HIGH>; + default-state = "off"; + linux,default-trigger = "2b300000.adrv906x_net:01:link"; + }; + }; +}; + +&uart0 { + status = "okay"; +}; + +/* Remove CTSIN and RTSOUT. CTSIN conflicts with GPIO 51 below */ +&pinctrl_uart4 { + adi,pins = < + UART4_RXSIN_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + UART4_TXSOUT_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; +}; + +&uart4 { + status = "okay"; +}; + +&mmc0 { + status = "okay"; +}; + +&mmc0_regulator { + status = "okay"; +}; + +&mmc0_phy { + status = "okay"; +}; + +/* BRINGUP TODO: Remove when 50MHz SD is supported */ +&pinctrl_mmc1_sd { + adi,pins = < + SD_CLK_SEL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + SD_CMD_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_4) + SD_DATA0_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_1) + SD_DATA1_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_1) + SD_DATA2_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_1) + SD_DATA3_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_1) + SD_CARDDETECT_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + +&mmc1 { + status = "okay"; + /* BRINGUP TODO: Set this back to 50MHz */ + max-frequency = <25000000>; +}; + +&mmc1_regulator { + status = "okay"; +}; + +&qspi0 { + status = "okay"; + storage,flash-handle = <&flash>; + + flash: mt25q@0 { + compatible = "mt25qu02g", "jedec,spi-nor"; + spi-max-frequency = <50000000>; + reg = <0>; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + adi,enable-dma; + + #include "adrv906x-nor-flash-part.dtsi" + }; +}; + +&qspi0_dma { + status = "okay"; +}; + +&emac0 { + status = "okay"; + phy-handle = <&phy0>; + mdio0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dwmac-mdio"; + phy0: ethernet-phy@0 { + reg = <15>; + }; + }; +}; + +&spi0 { + status = "okay"; + + spidev_clkic@0 { + compatible = "microchip,zl30732"; + spi-max-frequency = <12500000>; + reg = <0>; + }; + + spidev_rffe@1 { + compatible = "adi,adrv906x-rffe-header"; + spi-max-frequency = <10000000>; + reg = <1>; + }; +}; + +&i2c1 { + status = "okay"; + + /* MAX6581 temp sensor. Bus addr can vary from board + * to board, so four different entries are needed. + */ + temp-sensor@4c { + compatible = "maxim,max6581"; + reg = <0x4c>; + smbus-timeout-disable; + extended-range-enable; + resistance-cancellation; + alert-mask = <0x7f>; /* Ignore all alerts */ + over-temperature-mask = <0x7f>; /* Ignore all alerts */ + }; + temp-sensor@4d { + compatible = "maxim,max6581"; + reg = <0x4d>; + smbus-timeout-disable; + extended-range-enable; + resistance-cancellation; + alert-mask = <0x7f>; /* Ignore all alerts */ + over-temperature-mask = <0x7f>; /* Ignore all alerts */ + }; + temp-sensor@4e { + compatible = "maxim,max6581"; + reg = <0x4e>; + smbus-timeout-disable; + extended-range-enable; + resistance-cancellation; + alert-mask = <0x7f>; /* Ignore all alerts */ + over-temperature-mask = <0x7f>; /* Ignore all alerts */ + }; + temp-sensor@4f { + compatible = "maxim,max6581"; + reg = <0x4f>; + smbus-timeout-disable; + extended-range-enable; + resistance-cancellation; + alert-mask = <0x7f>; /* Ignore all alerts */ + over-temperature-mask = <0x7f>; /* Ignore all alerts */ + }; + + board-eeprom@57 { + compatible = "atmel,24c32"; + reg = <0x57>; + }; + + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + }; +}; + +&dac0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm13>; +}; + +&pinctrl_leds { + adi,pins = < + A55_GPIO_NS_74_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_75_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_76_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_77_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; +}; + +/* + * GPIO Pin mapping: + * + * Pin 0-6: SD + * Pins 7-17: RFFE + * Pins 20-21: I2C + * Pins 22-23: Stream processor fault handling + * Pins 24-27: UART0 + * Pins 28-29: UART1 + * Pins 30-31: UART4 + * Pin 32: GPINT0 + * Pin 33: Debug satellite board clk + * Pins 34-38: JTAG M4 + * Pin 42: SPI_MASTER0_SELB_1_PIN or TRACE_D0_PIN + * Pin 43: TRACE_D1 + * Pin 44: TRACE_D2 + * Pin 45: ONE_PPS_CLK_OUTPUT_SE + * Pin 46: TRACE_D3 + * Pin 47: PWM_13 + * Pin 51: Board pushbutton + * Pin 54: GNSS + * Pin 55: RTC + * Pins 56-59: QSFP Interface + * Pins 60-64: JTAG A5 + * Pin 65: QSFP Interface + * Pins 66-73: Debug Out Signals + * Pins 74-77: Gpio LEDs + * Pins 78-84: QSPI + * Pins 85-100: EMAC + * Pin 101: GPINT_INTERRUPT_INPUT_SECONDARY_TO_PRIMARY + * Pin 102: A55_GPIO_S_102_PIN (reboot/shutdown signal to ADM12366) + */ +&pinctrl_hog { + adi,pins = < + /* Pins 7-17: RFFE signals */ + RFFE_0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_2_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_3_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_4_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_5_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_6_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_7_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_8_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_9_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_10_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + + /* Pins 22-23: Stream processor fault handling */ + A55_GPIO_NS_22_PIN (ADI_CONFIG_ENABLE_PULLDOWN | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_23_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + + /* Pin 51: Board pushbutton */ + A55_GPIO_NS_51_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + + /* Pins 56-59,65: QSFP interface */ + A55_GPIO_NS_56_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_57_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + A55_GPIO_NS_58_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_59_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + A55_GPIO_NS_65_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + + /* Pins 66-73: Debug out signals */ + GPIO_DEBUG_0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_2_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_3_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_4_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_5_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_6_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_7_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + + /* Pins 42-44,46,51: CPU trace + * Enable if needed. + * Conflicts with board pushbutton above (pin 51). + * Conflicts with SPI_MASTER0_SELB1 above (pin 42). + * + * TRACE_D0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + * TRACE_D1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + * TRACE_D2_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + * TRACE_D3_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + * TRACE_CLK_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + */ + >; +}; + +&adrv906x_net0 { + ethernet-ports { + port@0 { + adi,pcb-delay-tx-ns = <1>; + adi,pcb-delay-tx-frac-ns = <0>; + adi,pcb-delay-rx-ns = <1>; + adi,pcb-delay-rx-frac-ns = <0>; + }; + port@1 { + adi,pcb-delay-tx-ns = <1>; + adi,pcb-delay-tx-frac-ns = <0>; + adi,pcb-delay-rx-ns = <1>; + adi,pcb-delay-rx-frac-ns = <0>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/adi/adrv906x-denali-8.dts b/arch/arm64/boot/dts/adi/adrv906x-denali-8.dts new file mode 100644 index 00000000000000..0eaef4dc4d0b5c --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-denali-8.dts @@ -0,0 +1,478 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Analog Devices Incorporated, All Rights Reserved + */ + +#include "adrv906x-dual-tile.dtsi" +#include "adrv906x-sysc.dtsi" +#include "adrv906x-protium.dtsi" +#include "adrv906x-eth-4t4r-dc.dtsi" +#include "adrv906x-eth-8t8r-dc.dtsi" + +/ { + model = "ADI ADRV906X Denali 8T8R Evaluation Kit"; + compatible = "adi,adrv906x-denali-8", "adi,adrv906x"; + + chosen { + bootargs = "console=ttyAMA0,115200n8 earlycon=pl011,0x20060000 rootwait uio_pdrv_genirq.of_id=generic-uio panic=-1 reboot=w"; + stdout-path = &uart0; + }; + + leds: gpio-leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_leds>; + ds1 { + gpios = <&gpio0 ADI_ADRV906X_PIN_74 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + ds3 { + gpios = <&gpio0 ADI_ADRV906X_PIN_75 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + ethernet-phy0-ds5 { + gpios = <&gpio0 ADI_ADRV906X_PIN_76 GPIO_ACTIVE_HIGH>; + default-state = "off"; + linux,default-trigger = "2b300000.adrv906x_net:00:link"; + }; + ethernet-phy1-ds7 { + gpios = <&gpio0 ADI_ADRV906X_PIN_77 GPIO_ACTIVE_HIGH>; + default-state = "off"; + linux,default-trigger = "2b300000.adrv906x_net:01:link"; + }; + }; + + sec_leds: gpio-secondary-leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_secondary_leds>; + ds2 { + gpios = <&sec_gpio0 ADI_ADRV906X_PIN_74 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + ds4 { + gpios = <&sec_gpio0 ADI_ADRV906X_PIN_75 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + ethernet-phy2-ds6 { + gpios = <&sec_gpio0 ADI_ADRV906X_PIN_76 GPIO_ACTIVE_HIGH>; + default-state = "off"; + linux,default-trigger = "2f300000.adrv906x_net:02:link"; + }; + ethernet-phy3-ds8 { + gpios = <&sec_gpio0 ADI_ADRV906X_PIN_77 GPIO_ACTIVE_HIGH>; + default-state = "off"; + linux,default-trigger = "2f300000.adrv906x_net:03:link"; + }; + }; + + /* These UIO devices are for debugging purposes - remove for production */ + uio-ethernet-debug@EMAC_CMN_BASE_UADDR { + compatible = "generic-uio"; + reg = ; + }; + + uio-ethernet-debug-sec@SEC_EMAC_CMN_BASE_UADDR { + compatible = "generic-uio"; + reg = ; + }; + + uio-ethernet-debug-tod@EMAC_TOD_BASE { + compatible = "generic-uio"; + reg = ; + }; + + uio-ethernet-debug-tod-sec@SEC_EMAC_TOD_BASE { + compatible = "generic-uio"; + reg = ; + }; + + uio-ndma@NDMA_0_RX_BASE_UADDR { + compatible = "generic-uio"; + reg = ; + }; + + uio-ndma-sec@SEC_NDMA_0_RX_BASE_UADDR { + compatible = "generic-uio"; + reg = ; + }; +}; + +/* Remove CTSIN and RTSOUT. CTSIN conflicts with GPIO 51 below */ +&pinctrl_uart4 { + adi,pins = < + UART4_RXSIN_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + UART4_TXSOUT_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; +}; + +&uart0 { + status = "okay"; +}; + +&uart4 { + status = "okay"; +}; + +&mmc0 { + status = "okay"; +}; + +&mmc0_regulator { + status = "okay"; +}; + +&mmc0_phy { + status = "okay"; +}; + +/* BRINGUP TODO: Remove when 50MHz SD is supported */ +&pinctrl_mmc1_sd { + adi,pins = < + SD_CLK_SEL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + SD_CMD_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_4) + SD_DATA0_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_1) + SD_DATA1_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_1) + SD_DATA2_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_1) + SD_DATA3_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_1) + SD_CARDDETECT_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_4) + >; +}; + +&mmc1 { + status = "okay"; + /* BRINGUP TODO: Set this back to 50MHz */ + max-frequency = <25000000>; +}; + +&mmc1_regulator { + status = "okay"; +}; + +&qspi0 { + status = "okay"; + storage,flash-handle = <&flash>; + + flash: mt25q@0 { + compatible = "mt25qu02g", "jedec,spi-nor"; + spi-max-frequency = <50000000>; + reg = <0>; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + adi,enable-dma; + + #include "adrv906x-nor-flash-part.dtsi" + }; +}; + +&qspi0_dma { + status = "okay"; +}; + +&emac0 { + status = "okay"; + phy-handle = <&phy0>; + mdio0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dwmac-mdio"; + phy0: ethernet-phy@0 { + reg = <15>; + }; + }; +}; + +&spi0 { + status = "okay"; + + spidev_clkic@0 { + compatible = "microchip,zl30732"; + spi-max-frequency = <12500000>; + reg = <0>; + }; + + spidev_rffe@1 { + compatible = "adi,adrv906x-rffe-header"; + spi-max-frequency = <10000000>; + reg = <1>; + }; +}; + +&i2c1 { + status = "okay"; + + /* MAX6581 temp sensor. Bus addr can vary from board + * to board, so four different entries are needed. + */ + temp-sensor@4c { + status = "okay"; + compatible = "maxim,max6581"; + reg = <0x4c>; + smbus-timeout-disable; + extended-range-enable; + resistance-cancellation; + alert-mask = <0x7f>; /* Ignore all alerts */ + over-temperature-mask = <0x7f>; /* Ignore all alerts */ + }; + temp-sensor@4d { + status = "okay"; + compatible = "maxim,max6581"; + reg = <0x4d>; + smbus-timeout-disable; + extended-range-enable; + resistance-cancellation; + alert-mask = <0x7f>; /* Ignore all alerts */ + over-temperature-mask = <0x7f>; /* Ignore all alerts */ + }; + temp-sensor@4e { + status = "okay"; + compatible = "maxim,max6581"; + reg = <0x4e>; + smbus-timeout-disable; + extended-range-enable; + resistance-cancellation; + alert-mask = <0x7f>; /* Ignore all alerts */ + over-temperature-mask = <0x7f>; /* Ignore all alerts */ + }; + temp-sensor@4f { + status = "okay"; + compatible = "maxim,max6581"; + reg = <0x4f>; + smbus-timeout-disable; + extended-range-enable; + resistance-cancellation; + alert-mask = <0x7f>; /* Ignore all alerts */ + over-temperature-mask = <0x7f>; /* Ignore all alerts */ + }; + + board-eeprom@57 { + status = "okay"; + compatible = "atmel,24c32"; + reg = <0x57>; + }; + + rtc@68 { + status = "okay"; + compatible = "dallas,ds1339"; + reg = <0x68>; + }; +}; + +&sec_i2c1 { + status = "disabled"; +}; + +&sec_i2c5 { + status = "disabled"; +}; + +&dac0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm13>; +}; + +&sec_dac0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_secondary_pwm13>; +}; + +&pinctrl_secondary_eth_recov_clk { + adi,pins = < + ETHERNET_RECOVERED_CLK_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; +}; + +&pinctrl_leds { + adi,pins = < + A55_GPIO_NS_74_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_75_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_76_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_77_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; +}; + +&pinctrl_secondary_leds { + adi,pins = < + A55_GPIO_NS_74_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_75_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_76_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_77_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; +}; + +/* + * GPIO Pin mapping: + * + * Pin 0-6: SD + * Pins 7-17: RFFE + * Pins 20-21: I2C + * Pins 22-23: Stream processor fault handling + * Pins 24-27: UART0 + * Pins 28-29: UART1 + * Pins 30-31: UART4 + * Pin 32: GPINT0 + * Pin 33: Debug satellite board clk + * Pins 34-38: JTAG M4 + * Pin 42: SPI_MASTER0_SELB_1_PIN or TRACE_D0_PIN + * Pin 43: TRACE_D1 + * Pin 44: TRACE_D2 + * Pin 45: ONE_PPS_CLK_OUTPUT_SE + * Pin 46: TRACE_D3 + * Pin 47: PWM_13 + * Pin 51: Board pushbutton + * Pin 54: GNSS + * Pin 55: RTC + * Pins 56-59: QSFP Interface + * Pins 60-64: JTAG A5 + * Pin 65: QSFP Interface + * Pins 66-73: Debug Out Signals + * Pins 74-77: Gpio LEDs + * Pins 78-84: QSPI + * Pins 85-100: EMAC + * Pin 101: GPINT_INTERRUPT_INPUT_SECONDARY_TO_PRIMARY + * Pin 102: A55_GPIO_S_102_PIN (reboot/shutdown signal to ADM12366) + */ + +&pinctrl_hog { + adi,pins = < + /* Pins 7-17: RFFE signals */ + RFFE_0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_2_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_3_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_4_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_5_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_6_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_7_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_8_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_9_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_10_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + + /* Pins 22-23: Stream processor fault handling */ + A55_GPIO_NS_22_PIN (ADI_CONFIG_ENABLE_PULLDOWN | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_23_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + + /* Pin 51: Board pushbutton */ + A55_GPIO_NS_51_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + + /* Pins 56-59,65: QSFP interface */ + A55_GPIO_NS_56_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_57_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + A55_GPIO_NS_58_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_59_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + A55_GPIO_NS_65_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + + /* Pins 66-73: Debug out signals */ + GPIO_DEBUG_0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_2_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_3_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_4_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_5_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_6_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_7_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + + /* Pins 42-44,46,51: CPU trace + * Enable if needed. + * Conflicts with board pushbutton above (pin 51). + * Conflicts with SPI_MASTER0_SELB1 above (pin 42). + * + * TRACE_D0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + * TRACE_D1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + * TRACE_D2_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + * TRACE_D3_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + * TRACE_CLK_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + */ + >; +}; + + +/* + * Secondary GPIO Pin mapping: + * + * Pins 7-13: RFFE + * Pins 22-23: Stream processor fault handling + * Pins 24-27: UART0 + * Pins 28-29: UART1 + * Pins 30-31: UART4 + * Pin 32: GPINT_OUTPUT_0_PIN (connected to primary 101) + * Pin 33: Debug satellite board clk + * Pins 34-38: JTAG M4 + * Pin 42: TRACE_D0 + * Pin 43: TRACE_D1 + * Pin 44: TRACE_D2 + * Pin 45: ONE_PPS_CLK_OUTPUT_SE + * Pin 46: TRACE_D3 + * Pin 47: PWM_13 + * Pin 51: Board pushbutton + * Pins 60-64: JTAG A5 + * Pin 65: QSFP Interface + * Pins 66-73: Debug Out Signals + * Pins 74-77: Gpio LEDs + * Pins 85-100: EMAC + * Pin 101: GPINT_INTERRUPT_INPUT_SECONDARY_TO_PRIMARY + */ +&pinctrl_secondary_hog { + adi,pins = < + /* Pins 7-13: RFFE signals */ + RFFE_0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_2_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_3_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_4_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_5_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_6_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + + /* Pins 22-23: Stream processor fault handling */ + A55_GPIO_NS_22_PIN (ADI_CONFIG_ENABLE_PULLDOWN | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_23_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + + /* Pins 66-73: Debug out signals */ + GPIO_DEBUG_0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_2_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_3_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_4_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_5_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_6_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + GPIO_DEBUG_7_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; +}; + +&adrv906x_net0 { + ethernet-ports { + port@0 { + adi,pcb-delay-tx-ns = <1>; + adi,pcb-delay-tx-frac-ns = <0>; + adi,pcb-delay-rx-ns = <1>; + adi,pcb-delay-rx-frac-ns = <0>; + }; + port@1 { + adi,pcb-delay-tx-ns = <1>; + adi,pcb-delay-tx-frac-ns = <0>; + adi,pcb-delay-rx-ns = <1>; + adi,pcb-delay-rx-frac-ns = <0>; + }; + }; +}; + +&adrv906x_net1 { + ethernet-ports { + port@0 { + adi,pcb-delay-tx-ns = <1>; + adi,pcb-delay-tx-frac-ns = <0>; + adi,pcb-delay-rx-ns = <1>; + adi,pcb-delay-rx-frac-ns = <0>; + }; + port@1 { + adi,pcb-delay-tx-ns = <1>; + adi,pcb-delay-tx-frac-ns = <0>; + adi,pcb-delay-rx-ns = <1>; + adi,pcb-delay-rx-frac-ns = <0>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/adi/adrv906x-disabled.dtsi b/arch/arm64/boot/dts/adi/adrv906x-disabled.dtsi new file mode 100644 index 00000000000000..f9dd7d464a4160 --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-disabled.dtsi @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Analog Devices Incorporated, All Rights Reserved + */ + +/dts-v1/; + +/* Sandbox for all adrv906x devices that are not ready to be compiled in or enabled. + * + * TODO: Move these into adrv906x.dtsi as they become ready, and eventually remove + * this file. + */ + +/ { + pmu { + compatible = "arm,armv8-pmuv3"; + interrupts = ; + interrupt-parent = <&gic>; + }; + + tru0: tru@TRU_BASE_UADDR { + compatible = "adi,tru"; + reg = ; + /* TODO replace 100 with actual last trigger source ID */ + adi,tru-last-source-id = <100>; + adi,tru-last-target-id = <79>; + /* each connection is */ + /* + adi,tru-connections-preset = <1 2>, + <25 32>, + <3 4>; + adi,tru-connections-preset-locked; + */ + }; + + /* TODO: enable blocks below and test when avail*/ + /* memory to memory DMA */ + mdma: dma@MDMA_0_CH00_BASE_UADDR { + compatible = "adi,mdma-controller"; + reg = ; + status = "okay"; + + mdma0: channel@0{ + adi,id = <0>; + // The destination interrupts are used for primary complete detection + interrupts = , + , + , + ; + interrupt-names = "complete", "error", "complete2", "error2"; + adi,src-offset = <0>; + adi,dest-offset = <0x80>; + }; + mdma1: channel@1{ + adi,id = <1>; + // The destination interrupts are used for primary complete detection + interrupts = , + , + , + ; + interrupt-names = "complete", "error", "complete2", "error2"; + adi,src-offset = <0>; + adi,dest-offset = <0x80>; + }; + mdma2: channel@2{ + adi,id = <2>; + // The destination interrupts are used for primary complete detection + interrupts = , + , + , + ; + interrupt-names = "complete", "error", "complete2", "error2"; + adi,src-offset = <0>; + adi,dest-offset = <0x80>; + }; + mdma3: channel@3{ + adi,id = <3>; + // The destination interrupts are used for primary complete detection + interrupts = , + , + , + ; + interrupt-names = "complete", "error", "complete2", "error2"; + adi,src-offset = <0>; + adi,dest-offset = <0x80>; + }; + mdma4: channel@4{ + adi,id = <4>; + // The destination interrupts are used for primary complete detection + interrupts = , + , + , + ; + interrupt-names = "complete", "error", "complete2", "error2"; + adi,src-offset = <0>; + adi,dest-offset = <0x80>; + }; + mdma5: channel@5{ + adi,id = <5>; + // The destination interrupts are used for primary complete detection + interrupts = , + , + , + ; + interrupt-names = "complete", "error", "complete2", "error2"; + adi,src-offset = <0>; + adi,dest-offset = <0x80>; + }; + }; + + /* spi master */ + spi_master: dma@SPI_MASTER_0_BASE_UADDR { + compatible = "adi,dma-controller"; + reg = ; + status = "okay"; + #dma-cells = <1>; + + spi0_tx: channel@30 { + adi,id = <30>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0>; + }; + + spi0_rx: channel@31 { + adi,id = <31>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x80>; + }; + + spi1_tx: channel@32 { + adi,id = <32>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x100>; + }; + + spi1_rx: channel@33 { + adi,id = <33>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x180>; + }; + + spi2_tx: channel@34 { + adi,id = <34>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x200>; + }; + + spi2_rx: channel@35 { + adi,id = <35>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x280>; + }; + + spi3_tx: channel@36 { + adi,id = <36>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x300>; + }; + + spi3_rx: channel@37 { + adi,id = <37>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x380>; + }; + + spi4_tx: channel@38 { + adi,id = <38>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x300>; + }; + + spi4_rx: channel@39 { + adi,id = <39>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x380>; + }; + spi5_tx: channel@40 { + adi,id = <40>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x300>; + }; + + spi5_rx: channel@41 { + adi,id = <41>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x380>; + }; + + }; + + /* PMIC */ + PMIC_RX: dma@PIMC_DDE_BASE_UADDR { + compatible = "adi,dma-controller"; + reg = ; + status = "okay"; + #dma-cells = <1>; + + pmic_rx: channel@40 { + adi,id = <40>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0>; + }; + }; + + /* anttena calibration */ + attenna_cal: dma@ANTENNA_CAL_DDE_BASE_UADDR { + compatible = "adi,dma-controller"; + reg = ; + status = "okay"; + #dma-cells = <1>; + + att_cal: channel@50 { + adi,id = <50>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0>; + }; + }; + + /* debug inject */ + debug_module: dma@DEBUG_DDE_INJ_BASE_UADDR { + compatible = "adi,dma-controller"; + reg = ; + status = "okay"; + #dma-cells = <1>; + + debug_inject: channel@60 { + adi,id = <60>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0>; + }; + debug_capture_0: channel@61 { + adi,id = <61>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0>; + }; + debug_capture_1: channel@62 { + adi,id = <62>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0>; + }; + + }; +}; diff --git a/arch/arm64/boot/dts/adi/adrv906x-dual-tile.dtsi b/arch/arm64/boot/dts/adi/adrv906x-dual-tile.dtsi new file mode 100644 index 00000000000000..5c508647b7888f --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-dual-tile.dtsi @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include "adrv906x.dtsi" + +/ { + aliases { + /* Virtual UART: A55 to M4 on secondary */ + serial7 = &sec_v_uart1_1; + + /* I2C buses */ + i2c8 = &sec_i2c0; + i2c9 = &sec_i2c1; + i2c10 = &sec_i2c2; + i2c11 = &sec_i2c3; + i2c12 = &sec_i2c4; + i2c13 = &sec_i2c5; + i2c14 = &sec_i2c6; + i2c15 = &sec_i2c7; + }; + + sec_v_uart1_1: v_uart@SEC_VIRTUAL_PL011_1_1_BASE_UADDR { + compatible = "arm,pl011", "arm,primecell"; + reg = ; + interrupts = ; + clocks = <&sysclk>, <&sysclk>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + pinctrl_secondary: pinctrl@SEC_PINCTRL_BASE_UADDR { + reg = ; + compatible = "adi,adrv906x-pinctrl"; + }; + + sec_ndma_reset_ctrl: reset@SEC_NDMA_RST_CTRL_UADDR{ + reg = ; + }; + sec_ndma0_interrupt_ctrl: ndma0_intr_ctrl@SEC_NDMA_0_INTR_CTRL_UADDR { + reg = ; + }; + sec_ndma1_interrupt_ctrl: ndma1_intr_ctrl@SEC_NDMA_1_INTR_CTRL_UADDR { + reg = ; + }; + + sec_ndma0: ndma0@SEC_NDMA_0_TX_BASE_UADDR { + id = <0>; + reg = , + , + , + , + ; + reset-ctrl = <&sec_ndma_reset_ctrl>; + interrupts = , + , + , + , + , + ; + interrupt-names = "tx_data_dma_done", "tx_data_dma_error", + "tx_status_dma_done", "tx_status_dma_error", + "rx_dma_done", "rx_dma_error"; + interrupt-ctrl = <&sec_ndma0_interrupt_ctrl>; + }; + + sec_ndma1: ndma1@SEC_NDMA_1_TX_BASE_UADDR { + id = <1>; + reg = , + , + , + , + ; + reset-ctrl = <&sec_ndma_reset_ctrl>; + interrupts = , + , + , + , + , + ; + interrupt-names = "tx_data_dma_done", "tx_data_dma_error", + "tx_status_dma_done", "tx_status_dma_error", + "rx_dma_done", "rx_dma_error"; + interrupt-ctrl = <&sec_ndma1_interrupt_ctrl>; + }; + + sec_i2c0: twi@SEC_I2C_0_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_secondary_i2c0>; + status = "disabled"; + }; + + sec_i2c1: twi@SEC_I2C_1_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_secondary_i2c1>; + status = "disabled"; + }; + + sec_i2c2: twi@SEC_I2C_2_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_secondary_i2c2>; + status = "disabled"; + }; + + sec_i2c3: twi@SEC_I2C_3_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_secondary_i2c3>; + status = "disabled"; + }; + + sec_i2c4: twi@SEC_I2C_4_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_secondary_i2c4>; + status = "disabled"; + }; + + sec_i2c5: twi@SEC_I2C_5_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_secondary_i2c5>; + status = "disabled"; + }; + + sec_i2c6: twi@SEC_I2C_6_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_secondary_i2c6>; + status = "disabled"; + }; + + sec_i2c7: twi@SEC_I2C_7_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_secondary_i2c7>; + status = "disabled"; + }; + + sec_dac0: dac@SEC_PWM_BASE_UADDR { + compatible = "adi,pwm-dac"; + reg = ; + clocks = <&hsdigclk>; + adi,iovdd-microvolt = <1800000>; + adi,gpio-max-frequency = <122880000>; + status = "disabled"; + }; + + sec_gpio0: gpio@SEC_GPIO_NS_BASE_UADDR { + compatible = "adi,adrv906x-gpio"; + reg = ; + pintmux = ; + gpio-controller; + #gpio-cells = <2>; + ngpios = ; + #interrupt-cells = <2>; + interrupt-controller; + }; +}; + +&ptpclk { + reg = ; + /* This enables both 1PPS outputs for debug purposes only. */ + pinctrl-0 = <&pinctrl_one_pps &pinctrl_secondary_one_pps>; +}; + +#include "adrv906x-pinctrl-secondary.dtsi" diff --git a/arch/arm64/boot/dts/adi/adrv906x-eth-4t4r-dc.dtsi b/arch/arm64/boot/dts/adi/adrv906x-eth-4t4r-dc.dtsi new file mode 100644 index 00000000000000..562444ab5ec92d --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-eth-4t4r-dc.dtsi @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include "adrv906x-eth-4t4r.dtsi" + +&adrv906x_net0 { + ethernet-ports { + port@0 { + ndma-handle = <&ndma1>; + }; + }; + + eth_switch { + reg = ; + interrupt-names = "switch_error_0", "switch_error_1"; + interrupts = , ; + switch_port0:switch-port@0 { + id = <0>; + reg = ; + }; + switch_port1:switch-port@1 { + id = <1>; + reg = ; + }; + switch_port2:switch-port@2 { + id = <2>; + reg = ; + }; + }; +}; diff --git a/arch/arm64/boot/dts/adi/adrv906x-eth-4t4r.dtsi b/arch/arm64/boot/dts/adi/adrv906x-eth-4t4r.dtsi new file mode 100644 index 00000000000000..be183fb56c14c9 --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-eth-4t4r.dtsi @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include "adrv906x_def.h" + +/ { + adrv906x_net0: adrv906x_net@EMAC_CMN_BASE_UADDR { + compatible = "adi,adrv906x-net"; + reg = ; + #address-cells = <1>; + #size-cells = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_eth_recov_clk>; + + ethernet-ports { + #address-cells = <1>; + #size-cells = <1>; + port@0 { + id = <0>; + reg = , , , + ; + phy-handle = <&adrv906x_phy0>; + ndma-handle = <&ndma0>; + clocks = <&hsdigclk>; + clock-names = "hsdig_clk"; + adi,pcb-delay-tx-ns = <0>; + adi,pcb-delay-tx-frac-ns = <0>; + adi,pcb-delay-rx-ns = <0>; + adi,pcb-delay-rx-frac-ns = <0>; + }; + port@1 { + id = <1>; + reg = , , , + ; + phy-handle = <&adrv906x_phy1>; + ndma-handle = <&ndma1>; + clocks = <&hsdigclk>; + clock-names = "hsdig_clk"; + adi,pcb-delay-tx-ns = <0>; + adi,pcb-delay-tx-frac-ns = <0>; + adi,pcb-delay-rx-ns = <0>; + adi,pcb-delay-rx-frac-ns = <0>; + }; + }; + + oran_if { + reg = , , , ; + }; + + mdio_if { + reg = , ; + #address-cells = <1>; + #size-cells = <0>; + adrv906x_phy0: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <0>; + speed = <25000>; + }; + adrv906x_phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <1>; + speed = <25000>; + }; + }; + + eth_recov_clk { + reg = ; + }; + }; +}; diff --git a/arch/arm64/boot/dts/adi/adrv906x-eth-8t8r-dc.dtsi b/arch/arm64/boot/dts/adi/adrv906x-eth-8t8r-dc.dtsi new file mode 100644 index 00000000000000..da4c5a9f7737f8 --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-eth-8t8r-dc.dtsi @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include "adrv906x-eth-8t8r.dtsi" +#include "adrv906x-eth-4t4r-dc.dtsi" + +&adrv906x_net1 { + ethernet-ports { + port@0 { + ndma-handle = <&sec_ndma1>; + }; + }; + + eth_switch { + reg = ; + interrupt-names = "switch_error_0", "switch_error_1"; + interrupts = , ; + switch_port3:switch-port@3 { + id = <0>; + reg = ; + }; + switch_port4:switch-port@4 { + id = <1>; + reg = ; + }; + switch_port5:switch-port@5 { + id = <2>; + reg = ; + }; + }; +}; diff --git a/arch/arm64/boot/dts/adi/adrv906x-eth-8t8r.dtsi b/arch/arm64/boot/dts/adi/adrv906x-eth-8t8r.dtsi new file mode 100644 index 00000000000000..7bafff5c699f10 --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-eth-8t8r.dtsi @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include "adrv906x_def.h" + +/ { + adrv906x_net1: adrv906x_net@SEC_EMAC_CMN_BASE_UADDR { + compatible = "adi,adrv906x-net"; + reg = ; + #address-cells = <1>; + #size-cells = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_secondary_eth_recov_clk>; + + ethernet-ports { + #address-cells = <1>; + #size-cells = <1>; + port@0 { + id = <0>; + reg = , , + , ; + phy-handle = <&sec_adrv906x_phy0>; + ndma-handle = <&sec_ndma0>; + clocks = <&hsdigclk>; + clock-names = "hsdig_clk"; + adi,pcb-delay-tx-ns = <0>; + adi,pcb-delay-tx-frac-ns = <0>; + adi,pcb-delay-rx-ns = <0>; + adi,pcb-delay-rx-frac-ns = <0>; + }; + port@1 { + id = <1>; + reg = , , + , ; + phy-handle = <&sec_adrv906x_phy1>; + ndma-handle = <&sec_ndma1>; + clocks = <&hsdigclk>; + clock-names = "hsdig_clk"; + adi,pcb-delay-tx-ns = <0>; + adi,pcb-delay-tx-frac-ns = <0>; + adi,pcb-delay-rx-ns = <0>; + adi,pcb-delay-rx-frac-ns = <0>; + }; + }; + + oran_if { + reg = , , + , ; + }; + + mdio_if { + reg = , ; + #address-cells = <1>; + #size-cells = <0>; + sec_adrv906x_phy0: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <2>; + speed = <25000>; + }; + sec_adrv906x_phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <3>; + speed = <25000>; + }; + }; + + eth_recov_clk { + reg = ; + }; + }; +}; diff --git a/arch/arm64/boot/dts/adi/adrv906x-nor-flash-part.dtsi b/arch/arm64/boot/dts/adi/adrv906x-nor-flash-part.dtsi new file mode 100644 index 00000000000000..3ff2e8d5e0a670 --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-nor-flash-part.dtsi @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0{ + label = "boot_a"; + reg = <0x00000000 0x00040000>; + }; + partition@40000{ + label = "boot_b"; + reg = <0x00040000 0x00040000>; + }; + partition@80000{ + label = "bootctrl"; + reg = <0x00080000 0x00010000>; + }; + partition@90000{ + label = "bootcfg"; + reg = <0x00090000 0x00010000>; + }; + partition@A0000{ + label = "fip_a"; + reg = <0x000A0000 0x00200000>; + }; + partition@2A0000{ + label = "fip_b"; + reg = <0x002A0000 0x00200000>; + }; + partition@4A0000{ + label = "kernel_a"; + reg = <0x004A0000 0x03000000>; + }; + partition@34A0000{ + label = "kernel_b"; + reg = <0x034A0000 0x03000000>; + }; + partition@all{ + label = "nor-flash-overlay"; + reg = <0x0 0x08000000>; + }; +}; diff --git a/arch/arm64/boot/dts/adi/adrv906x-pinctrl-secondary.dtsi b/arch/arm64/boot/dts/adi/adrv906x-pinctrl-secondary.dtsi new file mode 100644 index 00000000000000..4c253365fc5497 --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-pinctrl-secondary.dtsi @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include "adrv906x_def.h" + +&pinctrl_secondary { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_secondary_hog>; + + pinctrl_secondary_eth_recov_clk: secondary-eth-recov-clk-grp { + adi,pins = < + ETHERNET_RECOVERED_CLK_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_leds: secondary-leds-grp { + }; + + pinctrl_secondary_hog: secondary-hog-grp { + }; + + pinctrl_secondary_i2c0: secondary-i2c0-grp { + adi,pins = < + I2C0_SCL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* DedicatedIO */ + I2C0_SDA_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* DedicatedIO */ + >; + }; + + pinctrl_secondary_i2c1: secondary-i2c1-grp { + adi,pins = < + I2C1_SCL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + I2C1_SDA_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_i2c2: secondary-i2c2-grp { + adi,pins = < + I2C2_SCL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + I2C2_SDA_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_i2c3: secondary-i2c3-grp { + adi,pins = < + I2C3_SCL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + I2C3_SDA_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_i2c4: secondary-i2c4-grp { + adi,pins = < + I2C4_SCL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + I2C4_SDA_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_i2c5: secondary-i2c5-grp { + adi,pins = < + I2C5_SCL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + I2C5_SDA_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_i2c6: secondary-i2c6-grp { + adi,pins = < + I2C6_SCL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + I2C6_SDA_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_i2c7: secondary-i2c7-grp { + adi,pins = < + I2C7_SCL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + I2C7_SDA_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_pwm0: secondary-pwm0-grp { + adi,pins = < + PWM_0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_pwm1: secondary-pwm1-grp { + adi,pins = < + PWM_1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_pwm2: secondary-pwm2-grp { + adi,pins = < + PWM_2_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_pwm3: secondary-pwm3-grp { + adi,pins = < + PWM_3_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_pwm4: secondary-pwm4-grp { + adi,pins = < + PWM_4_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_pwm5: secondary-pwm5-grp { + adi,pins = < + PWM_5_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_pwm6: secondary-pwm6-grp { + adi,pins = < + PWM_6_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_pwm7: secondary-pwm7-grp { + adi,pins = < + PWM_7_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_pwm8: secondary-pwm8-grp { + adi,pins = < + PWM_8_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_pwm9: secondary-pwm9-grp { + adi,pins = < + PWM_9_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_pwm10: secondary-pwm10-grp { + adi,pins = < + PWM_10_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_pwm11: secondary-pwm11-grp { + adi,pins = < + PWM_11_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_pwm12: secondary-pwm12-grp { + adi,pins = < + PWM_12_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_pwm13: secondary-pwm13-grp { + adi,pins = < + PWM_13_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_pwm14: secondary-pwm14-grp { + adi,pins = < + PWM_14_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_pwm15: secondary-pwm15-grp { + adi,pins = < + PWM_15_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_secondary_one_pps: secondary-one-pps-grp { + adi,pins = < + ONE_PPS_CLK_OUTPUT_SE_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; +}; diff --git a/arch/arm64/boot/dts/adi/adrv906x-pinctrl.dtsi b/arch/arm64/boot/dts/adi/adrv906x-pinctrl.dtsi new file mode 100644 index 00000000000000..8570ebc5b95a16 --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-pinctrl.dtsi @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include "adrv906x_def.h" + +&pinctrl_primary { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + pinctrl_eth_recov_clk: eth-recov-clk-grp { + adi,pins = < + ETHERNET_RECOVERED_CLK_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_leds: leds-grp { + }; + + pinctrl_hog: hog-grp { + }; + + pinctrl_qspi: qspi-grp { + adi,pins = < + QSPI_FLASH_CLK_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + QSPI_FLASH_SELB_PIN (ADI_CONFIG_ENABLE_PULLUP | ADI_CONFIG_DRIVE_STRENGTH_4) + QSPI_FLASH_D0_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_4) + QSPI_FLASH_D1_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_4) + QSPI_FLASH_D2_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_4) + QSPI_FLASH_D3_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_84_PIN (ADI_CONFIG_ENABLE_PULLUP | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_mmc1_sd: mmc1-grp { + adi,pins = < + SD_CLK_SEL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + SD_CMD_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_4) + SD_DATA0_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_4) + SD_DATA1_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_4) + SD_DATA2_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_4) + SD_DATA3_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_4) + SD_CARDDETECT_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_emac0_rgmii: emac0-rgmii-grp { + adi,pins = < + EMAC_CLK_RX_PIN (ADI_CONFIG_NO_PULL) + EMAC_CLK_TX_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + EMAC_GMII_MDC_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + EMAC_GMII_MDIO_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + EMAC_PHY_INTR_PIN (ADI_CONFIG_NO_PULL) + EMAC_PHY_RXD_0_PIN (ADI_CONFIG_NO_PULL) + EMAC_PHY_RXD_1_PIN (ADI_CONFIG_NO_PULL) + EMAC_PHY_RXD_2_PIN (ADI_CONFIG_NO_PULL) + EMAC_PHY_RXD_3_PIN (ADI_CONFIG_NO_PULL) + EMAC_PHY_RX_DV_PIN (ADI_CONFIG_NO_PULL) + EMAC_PHY_TXD_0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + EMAC_PHY_TXD_1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + EMAC_PHY_TXD_2_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + EMAC_PHY_TXD_3_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + EMAC_PHY_TXEN_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_88_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_emac0_rmii: emac0-rmii-grp { + adi,pins = < + EMAC_CLK_TX_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + EMAC_GMII_MDC_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + EMAC_GMII_MDIO_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + EMAC_PHY_INTR_PIN (ADI_CONFIG_NO_PULL) + EMAC_PHY_RXD_0_PIN (ADI_CONFIG_NO_PULL) + EMAC_PHY_RXD_1_PIN (ADI_CONFIG_NO_PULL) + EMAC_PHY_RX_DV_PIN (ADI_CONFIG_NO_PULL) + EMAC_PHY_TXD_0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + EMAC_PHY_TXD_1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + EMAC_PHY_TXEN_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_88_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_i2c0: i2c0-grp { + adi,pins = < + I2C0_SCL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* DedicatedIO */ + I2C0_SDA_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* DedicatedIO */ + >; + }; + + pinctrl_i2c1: i2c1-grp { + adi,pins = < + I2C1_SCL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + I2C1_SDA_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_i2c2: i2c2-grp { + adi,pins = < + I2C2_SCL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + I2C2_SDA_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_i2c3: i2c3-grp { + adi,pins = < + I2C3_SCL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + I2C3_SDA_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_i2c4: i2c4-grp { + adi,pins = < + I2C4_SCL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + I2C4_SDA_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_i2c5: i2c5-grp { + adi,pins = < + I2C5_SCL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + I2C5_SDA_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_i2c6: i2c6-grp { + adi,pins = < + I2C6_SCL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + I2C6_SDA_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_i2c7: i2c7-grp { + adi,pins = < + I2C7_SCL_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + I2C7_SDA_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_qsfp: qsfp-grp { + adi,pins = < + QSFP_INTERRUPT_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + QSFP_MODPRS_0_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + QSFP_MODPRS_1_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + QSFP_MODSEL_0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + QSFP_MODSEL_1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + QSFP_RESET_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_spi0: spi0-grp { + adi,pins = < + SPI_MASTER0_MISO_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) /* DedicatedIO */ + SPI_MASTER0_MOSI_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* DedicatedIO */ + SPI_MASTER0_CLK_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* DedicatedIO */ + SPI_MASTER0_SELB_0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* DedicatedIO */ + SPI_MASTER0_SELB_1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_spi1: spi1-grp { + adi,pins = < + SPI_MASTER1_CLK_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + SPI_MASTER1_MISO_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + SPI_MASTER1_MOSI_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + SPI_MASTER1_SELB_0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + SPI_MASTER1_SELB_1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + SPI_MASTER1_SELB_2_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + SPI_MASTER1_SELB_3_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_spi2: spi2-grp { + adi,pins = < + SPI_MASTER2_CLK_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + SPI_MASTER2_MISO_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + SPI_MASTER2_MOSI_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + SPI_MASTER2_SELB_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_spi3: spi3-grp { + adi,pins = < + SPI_MASTER3_CLK_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + SPI_MASTER3_MISO_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + SPI_MASTER3_MOSI_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + SPI_MASTER3_SELB_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_spi4: spi4-grp { + adi,pins = < + SPI_MASTER4_CLK_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + SPI_MASTER4_MISO_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + SPI_MASTER4_MOSI_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + SPI_MASTER4_SELB_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_spi5: spi5-grp { + adi,pins = < + SPI_MASTER5_CLK_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + SPI_MASTER5_MISO_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + SPI_MASTER5_MOSI_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + SPI_MASTER5_SELB_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_uart0: uart0-grp { + adi,pins = < + UART0_CTSIN_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + UART0_RTSOUT_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + UART0_RXSIN_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + UART0_TXSOUT_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_uart1: uart1-grp { + adi,pins = < + UART1_RXSIN_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + UART1_TXSOUT_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_uart3: uart3-grp { + adi,pins = < + UART3_CTSIN_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + UART3_RTSOUT_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + UART3_RXSIN_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + UART3_TXSOUT_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_uart4: uart4-grp { + adi,pins = < + UART4_CTSIN_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + UART4_RTSOUT_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + UART4_RXSIN_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + UART4_TXSOUT_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_pwm0: pwm0-grp { + adi,pins = < + PWM_0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_pwm1: pwm1-grp { + adi,pins = < + PWM_1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_pwm2: pwm2-grp { + adi,pins = < + PWM_2_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_pwm3: pwm3-grp { + adi,pins = < + PWM_3_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_pwm4: pwm4-grp { + adi,pins = < + PWM_4_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_pwm5: pwm5-grp { + adi,pins = < + PWM_5_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_pwm6: pwm6-grp { + adi,pins = < + PWM_6_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_pwm7: pwm7-grp { + adi,pins = < + PWM_7_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_pwm8: pwm8-grp { + adi,pins = < + PWM_8_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_pwm9: pwm9-grp { + adi,pins = < + PWM_9_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_pwm10: pwm10-grp { + adi,pins = < + PWM_10_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_pwm11: pwm11-grp { + adi,pins = < + PWM_11_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_pwm12: pwm12-grp { + adi,pins = < + PWM_12_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_pwm13: pwm13-grp { + adi,pins = < + PWM_13_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_pwm14: pwm14-grp { + adi,pins = < + PWM_14_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_pwm15: pwm15-grp { + adi,pins = < + PWM_15_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; + + pinctrl_one_pps: one-pps-grp { + adi,pins = < + ONE_PPS_CLK_OUTPUT_SE_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + >; + }; +}; diff --git a/arch/arm64/boot/dts/adi/adrv906x-protium.dtsi b/arch/arm64/boot/dts/adi/adrv906x-protium.dtsi new file mode 100644 index 00000000000000..b0afb61523ced5 --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-protium.dtsi @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Analog Devices Incorporated, All Rights Reserved + */ + +/ { +}; + +&emac0 { + /* TODO: Consider removing this if/when Protium support is removed */ + /*fixed-link { + speed = <1000>; + full-duplex; + };*/ +}; diff --git a/arch/arm64/boot/dts/adi/adrv906x-secondary.dts b/arch/arm64/boot/dts/adi/adrv906x-secondary.dts new file mode 100644 index 00000000000000..f95771430fc6fb --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-secondary.dts @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Analog Devices Incorporated, All Rights Reserved + */ + +/dts-v1/; + +#include +#include + +#include "adrv906x_def.h" +#include "adrv906x_irq_def.h" +#include "adrv906x-uio-sec.dtsi" + +/ { + interrupt-parent = <&gic>; + #address-cells = <1>; + #size-cells = <1>; + + model = "ADI ADRV906X Secondary SoC"; + compatible = "adi,adrv906x"; + + aliases { + serial0 = &uart0; + serial4 = &uart4; + }; + + chosen { + bootargs = "console=ttyAMA0,4000000n8 earlycon=pl011,0x20065000 uio_pdrv_genirq.of_id=generic-uio"; + stdout-path = &uart0; + + boot { + dual-tile = <1>; + secondary-tile = <1>; + plat = ""; + + /* Populated by U-Boot. Intentionally left blank here. */ + lifecycle-state { + description = ""; + deployed = <0>; + }; + }; + }; + + /* Populated by U-Boot. Intentionally left blank here. */ + memory { + device_type = "memory"; + reg = <0x00000000 0x00000000>; + }; + + sram_memory { + device_type = "memory"; + reg = <0x04100000 0x00400000>; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + cpu_off = <0x84000002>; + cpu_on = <0xc4000003>; + }; + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu-map { + cluster0 { + core0 { + cpu = <&CPU0>; + }; + core1 { + cpu = <&CPU1>; + }; + core2 { + cpu = <&CPU2>; + }; + core3 { + cpu = <&CPU3>; + }; + }; + }; + + CPU0:cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0 0x0>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; + + CPU1:cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0 0x100>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; + + CPU2:cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0 0x200>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; + + CPU3:cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0 0x300>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; + + L2_0: l2-cache0 { + compatible = "cache"; + }; + }; + + gic: interrupt-controller@SEC_GIC_BASE_UADDR { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + interrupt-controller; + reg = , /* GICD */ + <(SEC_GIC_BASE + 0x00040000) 0x80000>; /* GICR */ + /*interrupts = <1 9 4>;*/ + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , /* Physical Secure */ + , /* Physical Non-Secure */ + , /* Virtual */ + ; /* Hypervisor */ + }; + + /* Populated by U-Boot. Intentionally left blank here. */ + sysclk: sysclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + + uart0: uart@VIRTUAL_PL011_0_1_BASE_UADDR { + compatible = "arm,pl011", "arm,primecell"; + reg = ; + interrupts = ; + clocks = <&sysclk>, <&sysclk>; + clock-names = "uartclk", "apb_pclk"; + }; + + uart4: uart@SEC_PL011_3_BASE_UADDR { + compatible = "arm,pl011", "arm,primecell"; + reg = ; + interrupts = ; + clocks = <&sysclk>, <&sysclk>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + /* Reserved memory regions */ + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + sram0_res: sram-reserved@0 { + compatible = "adi,sram-access"; + reg = <0x04100000 0x00400000>; /* 4 MB */ + }; + + /* Base address populated by U-boot. Intentionally left blank here */ + ddr0_res: ddr-reserved@0 { + compatible = "adi,sram-access"; + reg = <0x00000000 0x01000000>; /* 16 MB */ + }; + }; + + /* Link reserved regions to User Space */ + sram0_mmap: sram-mmap@0 { + compatible = "adi,sram-mmap"; + memory-region = <&sram0_res>; + }; + + ddr0_mmap: ddr-mmap@0 { + compatible = "adi,sram-mmap"; + memory-region = <&ddr0_res>; + }; +}; + diff --git a/arch/arm64/boot/dts/adi/adrv906x-sysc.dtsi b/arch/arm64/boot/dts/adi/adrv906x-sysc.dtsi new file mode 100644 index 00000000000000..9b91ed928cbac6 --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-sysc.dtsi @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Analog Devices Incorporated, All Rights Reserved + */ + +/ { + /* eMMC, SystemC only. Using 0 as node address to differentiate it from SDHCI driver */ + mmc0@0 { + compatible = "arm,pl180", "arm,primecell"; + arm,primecell-periphid = <0xF0041180>; + reg = ; + interrupt-parent = <&gic>; + interrupts = , ; + max-frequency = <12000000>; + vmmc-supply = <&sysc_mmc_regulator>; + clocks = <&sysclk>, <&sysclk>; + clock-names = "mclk", "apb_pclk"; + status = "disabled"; + }; + + /* SD, SystemC only. Using 1 as node address to differentiate it from SDHCI driver */ + mmc1@1 { + compatible = "arm,pl180", "arm,primecell"; + arm,primecell-periphid = <0xF0041180>; + reg = ; + interrupt-parent = <&gic>; + interrupts = , ; + max-frequency = <12000000>; + vmmc-supply = <&sysc_mmc_regulator>; + clocks = <&sysclk>, <&sysclk>; + clock-names = "mclk", "apb_pclk"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_mmc1_sd>; + status = "disabled"; + }; + + /* SystemC only */ + ethernet@2 { + compatible = "smsc,lan91c111"; + reg = ; + interrupts = ; + status = "disabled"; + }; + + /* SystemC only */ + sysc_mmc_regulator: sysc-fixed-regulator-0 { + compatible = "regulator-fixed"; + regulator-name = "3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + status = "disabled"; + }; +}; diff --git a/arch/arm64/boot/dts/adi/adrv906x-titan-4.dts b/arch/arm64/boot/dts/adi/adrv906x-titan-4.dts new file mode 100644 index 00000000000000..b874fa34f4054e --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-titan-4.dts @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +/* For now, Titan-4 is identical to Denali-4 except for the changes below */ +#include "adrv906x-denali-4.dts" + +/ { + model = "ADI ADRV906X Titan 4T4R Evaluation Kit"; + compatible = "adi,adrv906x-titan-4", "adi,adrv906x"; + + sfp_port0: sfp_port0 { + compatible = "sff,sfp"; + i2c-bus = <&i2c1>; + los-gpio = <&gpio0 ADI_ADRV906X_PIN_72 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio0 ADI_ADRV906X_PIN_73 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&gpio0 ADI_ADRV906X_PIN_70 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&gpio0 ADI_ADRV906X_PIN_71 GPIO_ACTIVE_HIGH>; + }; + + sfp_port1: sfp_port1 { + compatible = "sff,sfp"; + i2c-bus = <&i2c7>; + los-gpio = <&gpio0 ADI_ADRV906X_PIN_58 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio0 ADI_ADRV906X_PIN_59 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&gpio0 ADI_ADRV906X_PIN_56 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&gpio0 ADI_ADRV906X_PIN_57 GPIO_ACTIVE_HIGH>; + }; +}; + +&mmc1 { + /* BRINGUP TODO: Set this back to 50MHz */ + max-frequency = <12500000>; +}; + +&i2c7 { + status = "okay"; +}; + +&dac0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm12>; +}; + +/* + * GPIO Pin mapping: + * + * Pin 0-6: SD + * Pins 7-19: RFFE + * Pins 20-21: I2C + * Pins 22-23: Stream processor fault handling + * Pins 24-27: UART0 + * Pins 28-29: Not Connected + * Pins 30-31: UART4 + * Pin 32: GPINT0 + * Pins 33-37: RFFE + * Pins 38-39: JTAG M4 + * Pins 40-41: RFFE + * Pin 42: SPI_MASTER0_SELB_1 + * Pin 43: RFFE + * Pin 44: TRACE_D2 + * Pin 45: ONE_PPS_CLK_OUTPUT_SE + * Pin 46: PWM_12 + * Pin 47: RFFE + * Pin 51: Board pushbutton + * Pins 52-53: Not Connected + * Pins 54-55: RFFE + * Pins 56-59: QSFP Interface + * Pins 60-64: JTAG A5 + * Pins 65-69: RFFE + * Pins 70-73: QSFP Interface + * Pins 74-77: Gpio LEDs + * Pins 78-84: QSPI + * Pins 85-100: EMAC + * Pin 101: GPINT_INTERRUPT_INPUT_SECONDARY_TO_PRIMARY + * Pin 102: A55_GPIO_S_102_PIN (reboot/shutdown signal to ADM12366) + */ +&pinctrl_hog { + adi,pins = < + /* RFFE signals */ + RFFE_0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_2_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_3_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_4_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_5_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_6_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_7_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_8_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_9_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_10_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_11_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_12_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_19_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_20_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_21_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_22_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_30_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_31_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_32_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_33_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_34_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_37_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_38_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_39_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_40_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_44_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_45_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_46_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + + /* Pin 51: Board pushbutton */ + A55_GPIO_NS_51_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + + /* Pins 56-59: SFP1 interface */ + A55_GPIO_NS_56_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_57_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + A55_GPIO_NS_58_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + A55_GPIO_NS_59_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + + /* Pins 70-73: SFP0 interface */ + A55_GPIO_NS_70_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_71_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + A55_GPIO_NS_72_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + A55_GPIO_NS_73_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + >; +}; + +&adrv906x_phy0 { + sfp = <&sfp_port0>; +}; + +&adrv906x_phy1 { + sfp = <&sfp_port1>; +}; + +&adrv906x_net0 { + ethernet-ports { + port@0 { + adi,pcb-delay-tx-ns = <1>; + adi,pcb-delay-tx-frac-ns = <0>; + adi,pcb-delay-rx-ns = <1>; + adi,pcb-delay-rx-frac-ns = <0>; + }; + port@1 { + adi,pcb-delay-tx-ns = <1>; + adi,pcb-delay-tx-frac-ns = <0>; + adi,pcb-delay-rx-ns = <1>; + adi,pcb-delay-rx-frac-ns = <0>; + }; + }; +}; \ No newline at end of file diff --git a/arch/arm64/boot/dts/adi/adrv906x-titan-8.dts b/arch/arm64/boot/dts/adi/adrv906x-titan-8.dts new file mode 100644 index 00000000000000..e32f0ac87cb39b --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-titan-8.dts @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +/* For now, Titan-8 is identical to Denali-8 except for the changes below */ +#include "adrv906x-denali-8.dts" + +/ { + model = "ADI ADRV906X Titan 8T8R Evaluation Kit"; + compatible = "adi,adrv906x-titan-8", "adi,adrv906x"; +}; + +&dac0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm12>; +}; + +&sec_dac0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_secondary_pwm8>; +}; + +/* + * GPIO Pin mapping: + * + * Pin 0-6: SD + * Pins 7-19: RFFE + * Pins 20-21: I2C + * Pins 22-23: Stream processor fault handling + * Pins 24-27: UART0 + * Pins 28-29: Not Connected + * Pins 30-31: UART4 + * Pin 32: GPINT0 + * Pins 33-37: RFFE + * Pins 38-39: JTAG M4 + * Pins 40-41: RFFE + * Pin 42: SPI_MASTER0_SELB_1 + * Pin 43: RFFE + * Pin 44: TRACE_D2 + * Pin 45: ONE_PPS_CLK_OUTPUT_SE + * Pin 46: PWM_12 + * Pin 47: RFFE + * Pin 51: Board pushbutton + * Pins 52-53: Not Connected + * Pins 54-55: RFFE + * Pins 56-59: QSFP Interface + * Pins 60-64: JTAG A5 + * Pins 65-69: RFFE + * Pins 70-73: QSFP Interface + * Pins 74-77: Gpio LEDs + * Pins 78-84: QSPI + * Pins 85-100: EMAC + * Pin 101: GPINT_INTERRUPT_INPUT_SECONDARY_TO_PRIMARY + * Pin 102: A55_GPIO_S_102_PIN (reboot/shutdown signal to ADM12366) + */ +&pinctrl_hog { + adi,pins = < + /* RFFE signals */ + RFFE_0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_2_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_3_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_4_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_5_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_6_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_7_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_8_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_9_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_10_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_11_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_12_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + + RFFE_19_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_20_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_21_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_22_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + + RFFE_30_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_31_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_32_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_33_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_34_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_37_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_38_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_39_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_40_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_44_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_45_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + RFFE_46_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) + + /* Pins 22-23: Stream processor fault handling */ + A55_GPIO_NS_22_PIN (ADI_CONFIG_ENABLE_PULLDOWN | ADI_CONFIG_DRIVE_STRENGTH_4) + A55_GPIO_NS_23_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + + /* Pin 51: Board pushbutton */ + A55_GPIO_NS_51_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) + + /* QSFP */ + A55_GPIO_NS_56_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* QSFP_RESET_L */ + A55_GPIO_NS_57_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) /* QSFP_INT_L */ + A55_GPIO_NS_58_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* QSFP_MODSEL_L */ + A55_GPIO_NS_59_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) /* QSFP_MODPRS_L */ + A55_GPIO_NS_70_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* QSFP LP Mode */ + >; +}; + +/* + * GPIO Pin mapping: + * + * Pin 0-3: SPI3 + * Pin 6: Not Connected + * Pins 7-8: RFFE + * Pins 9-11: Not Connected + * Pin 17: Not Connected + * Pin 18-19: RFFE + * Pins 20-21: Not Connected + * Pins 22-23: Stream processor fault handling + * Pins 24-27: UART0 + * Pins 28-31: AISG + * Pin 32: GPINT0 + * Pin 37: RFFE + * Pin 38: PWM_8 + * Pins 39-41: RFFE + * Pin 42: AISG + * Pin 43: RFFE + * Pin 44: Not Connected + * Pins 45-49: SPI1 + * Pin 50: Not Connected + * Pins 51-53: Not Connected + * Pins 54-55: RFFE + * Pins 56-59: Not Connected + * Pins 60-64: JTAG A5 + * Pins 65-69: RFFE + * Pins 74-77: Gpio LEDs + * Pins 78-102: Not Connected + */ +&pinctrl_secondary_hog { + adi,pins = < + + /* RFFE pins on secondary tile */ + RFFE_17_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* PREAMP_0_TO_3_EN */ + RFFE_18_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* PREAMP_4_TO_7_EN */ + RFFE_0_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (PA4_EN) */ + RFFE_1_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (LNA4_EN) */ + RFFE_5_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (T_R_Switch_Control_4_7) */ + RFFE_6_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (SS_TxToORxMap_DPD_LOL_D0) */ + RFFE_7_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (SS_TxToORxMap_DPD_LOL_D1) */ + RFFE_8_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (SS_TxToORxMap_DPD_LOL_D2) */ + RFFE_9_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (SS_DPD_VSWR_SW_CTRL) */ + RFFE_11_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (PA5_EN) */ + RFFE_12_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (PA6_EN) */ + RFFE_30_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (PA7_EN) */ + RFFE_31_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (LNA5_EN) */ + RFFE_32_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (LNA6_EN) */ + RFFE_33_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (LNA7_EN) */ + RFFE_34_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (PA4_ET_D0) */ + RFFE_36_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (PA7_ET_D0) */ + RFFE_37_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (PA5_ET_D0) */ + RFFE_38_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (PA5_ET_D1) */ + RFFE_39_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (PA6_ET_D0) */ + RFFE_44_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (PA5_ET_TSYNC) */ + RFFE_45_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (PA7_ET_D1) */ + RFFE_46_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (PA6_ET_D1) */ + RFFE_19_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (PA4_ET_D1) */ + RFFE_20_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (PA4_ET_TSYNC) */ + RFFE_21_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (PA7_ET_TSYNC) */ + RFFE_22_PIN (ADI_CONFIG_NO_PULL | ADI_CONFIG_DRIVE_STRENGTH_4) /* RFFE FUNCTIONALITY (PA6_ET_TSYNC) */ + + /* Pins 22-23: Stream processor fault handling */ + A55_GPIO_NS_22_PIN (ADI_CONFIG_ENABLE_PULLDOWN | ADI_CONFIG_DRIVE_STRENGTH_4) /* INTERRUPT_PS_TO_SS */ + A55_GPIO_NS_23_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) /* CONFIRMATION_SS_TO_PS */ + + A55_GPIO_NS_42_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) /* AISG1_DIR */ + A55_GPIO_NS_50_PIN (ADI_CONFIG_ENABLE_SCHMITT_TRIGGER) /* AISG0_DIR */ + >; +}; diff --git a/arch/arm64/boot/dts/adi/adrv906x-uio-sec.dtsi b/arch/arm64/boot/dts/adi/adrv906x-uio-sec.dtsi new file mode 100644 index 00000000000000..4921f545c50bc5 --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-uio-sec.dtsi @@ -0,0 +1,869 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 - 2024, Analog Devices Incorporated, All Rights Reserved + */ + +/dts-v1/; + +#include "adrv906x_irq_def.h" + +/ { + /* RegMap UIO Device: M4_memory_sec */ + uio-adrv906x-regmap-M4_memory_sec@c000000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0xc000000 0x60000 /* INST_ALT_ARM_MEMORY + MEMR_IDX_ARM_MEM_CODE_MEMORY_A_0*/ + 0xd000000 0x60000 /* INST_ALT_ARM_MEMORY + MEMR_IDX_ARM_MEM_CODE_MEMORY_B_0*/ + 0x34000000 0x60000 /* INST_ALT_ARM_MEMORY + MEMR_IDX_ARM_MEM_SYS_MEMORY_A_0*/ + 0x35000000 0x60000 /* INST_ALT_ARM_MEMORY + MEMR_IDX_ARM_MEM_SYS_MEMORY_B_0*/ + >; + }; + + /* RegMap UIO Device: antcal_sec */ + uio-adrv906x-regmap-antcal_sec@2f000000 { + compatible = "generic-uio"; + reg = < + 0x2f000000 0x2f4 /* INST_SEC_ORAN_TOP_MMR_ANTENNA_CAL_ADAPT*/ + 0x2f000400 0x2000 /* INST_SEC_ORAN_TOP_MMR_ANTENNA_CAL_ADAPT_ANT_CAL_ADPT_DL_INJECTION_BUF_MEMORY*/ + 0x2f002400 0xc000 /* INST_SEC_ORAN_TOP_MMR_ANTENNA_CAL_ADAPT_ANT_CAL_ADPT_DL_CAPTURE_BUF_MEMORY*/ + 0x2f00e400 0x2000 /* INST_SEC_ORAN_TOP_MMR_ANTENNA_CAL_ADAPT_ANT_CAL_ADPT_UL_INJECTION_BUF_MEMORY*/ + >; + }; + + /* RegMap UIO Device: antcal_sec2 */ + uio-adrv906x-regmap-antcal_sec2@2f010400 { + compatible = "generic-uio"; + reg = < + 0x2f010400 0xc000 /* INST_SEC_ORAN_TOP_MMR_ANTENNA_CAL_ADAPT_ANT_CAL_ADPT_UL_CAPTURE_BUF_MEMORY*/ + >; + }; + + /* RegMap UIO Device: capture_buffer_sec */ + uio-adrv906x-regmap-capture_buffer_sec@24400000 { + compatible = "generic-uio"; + reg = < + 0x24400000 0x150 /* INST_SEC_PROC_DFE_PERIP_CAPBUF*/ + >; + }; + + /* RegMap UIO Device: core_sec */ + uio-adrv906x-regmap-core_sec@1c290000 { + compatible = "generic-uio"; + reg = < + 0x1c290000 0x1bbf /* INST_SEC_DIGITAL_CORE_SPI_ONLY_REGS*/ + 0x1c210000 0x74 /* INST_SEC_DIGITAL_CORE_TELEMETRY*/ + 0x1c300000 0xcc /* INST_SEC_DIGITAL_CORE_EAST_RFPLL*/ + 0x1c400000 0xcc /* INST_SEC_DIGITAL_CORE_WEST_RFPLL*/ + >; + }; + + /* RegMap UIO Device: core_sec2 */ + uio-adrv906x-regmap-core_sec2@1c009000 { + compatible = "generic-uio"; + reg = < + 0x1c009000 0x4 /* INST_SEC_DIGITAL_CORE_CORE_0_SPI_PM_KEY*/ + 0x1c109000 0x4 /* INST_SEC_DIGITAL_CORE_CORE_1_SPI_PM_KEY*/ + >; + }; + + /* RegMap UIO Device: datapath_debug_sec */ + uio-adrv906x-regmap-datapath_debug_sec@2f02c400 { + compatible = "generic-uio"; + reg = < + 0x2f02c400 0x134 /* INST_SEC_ORAN_TOP_MMR_DATAPATH_DBG_CAPTURE0*/ + 0x2f02c800 0x134 /* INST_SEC_ORAN_TOP_MMR_DATAPATH_DBG_CAPTURE1*/ + 0x2f02cc00 0x3c /* INST_SEC_ORAN_TOP_MMR_DATAPATH_DBG_INJECT*/ + 0x2f02c000 0x18 /* INST_SEC_ORAN_TOP_MMR_DATAPATH_DBG_SHARED*/ + >; + }; + + /* RegMap UIO Device: dfe_perip_sec */ + uio-adrv906x-regmap-dfe_perip_sec@24100000 { + compatible = "generic-uio"; + reg = < + 0x24100000 0xc009c /* INST_SEC_PROC_DFE_PERIP_A55_SYS_CFG*/ + 0x24700000 0x8000 /* INST_SEC_PROC_DFE_PERIP_A55_TIMER0*/ + 0x24020000 0x4000 /* INST_SEC_PROC_DFE_PERIP_MDMA0_CH00*/ + 0x24270000 0x4000 /* INST_SEC_PROC_DFE_PERIP_DEBUG_DDE_INJ*/ + >; + }; + + /* RegMap UIO Device: dfe_perip_sec2 */ + uio-adrv906x-regmap-dfe_perip_sec2@24050000 { + compatible = "generic-uio"; + reg = < + 0x24050000 0x50 /* INST_SEC_PROC_DFE_PERIP_CAPBUFDDE*/ + 0x24271000 0x50 /* INST_SEC_PROC_DFE_PERIP_DEBUG_DDE_CAP0*/ + 0x24272000 0x50 /* INST_SEC_PROC_DFE_PERIP_DEBUG_DDE_CAP1*/ + 0x242a0000 0x50 /* INST_SEC_PROC_DFE_PERIP_PIMC_DDE*/ + >; + }; + + /* RegMap UIO Device: dfe_perip_sec3 */ + uio-adrv906x-regmap-dfe_perip_sec3@24280000 { + compatible = "generic-uio"; + reg = < + 0x24280000 0x50 /* INST_SEC_PROC_DFE_PERIP_ANTENNA_CAL_DDE*/ + 0x24052000 0x7f8 /* INST_SEC_PROC_DFE_PERIP_TRU*/ + 0x24218000 0x8dc /* INST_SEC_PROC_DFE_PERIP_GPIO_PINMUX_PAD*/ + >; + }; + + /* RegMap UIO Device: feature_filter_sec */ + uio-adrv906x-regmap-feature_filter_sec@24728000 { + compatible = "generic-uio"; + reg = < + 0x24728000 0x90 /* INST_SEC_PROC_DFE_PERIP_FEATURE_FILTER*/ + >; + }; + + /* RegMap UIO Device: kfa_sec */ + uio-adrv906x-regmap-kfa_sec@1c609000 { + compatible = "generic-uio"; + reg = < + 0x1c609000 0x288 /* INST_SEC_DIGITAL_CORE_KFA_TOP*/ + >; + }; + + /* RegMap UIO Device: sec-interrupt_aggregator_core */ + uio-adrv906x-regmap-sec-interrupt_aggregator_core@24200000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x24200000 0x74 /* INST_SEC_PROC_DFE_PERIP_INTR_AGGREGATOR*/ + >; + }; + + /* RegMap UIO Device: mailbox_a55_spi0_dst_sec */ + uio-adrv906x-regmap-mailbox_a55_spi0_dst_sec@1c690000 { + compatible = "generic-uio"; + reg = < + 0x1c690000 0x1c /* INST_SEC_DIGITAL_CORE_A55_SPI1_CMD_MAILBOX*/ + 0x1c691000 0x1c /* INST_SEC_DIGITAL_CORE_A55_SPI0_CMD_MAILBOX*/ + >; + }; + + /* RegMap UIO Device: mailbox_a55_spi0_src_sec */ + uio-adrv906x-regmap-mailbox_a55_spi0_src_sec@1c690000 { + compatible = "generic-uio"; + reg = < + 0x1c690000 0x1c /* INST_SEC_DIGITAL_CORE_A55_SPI1_CMD_MAILBOX*/ + 0x1c691000 0x1c /* INST_SEC_DIGITAL_CORE_A55_SPI0_CMD_MAILBOX*/ + >; + }; + + /* RegMap UIO Device: mailbox_a55tocore_dst_sec */ + uio-adrv906x-regmap-mailbox_a55tocore_dst_sec@24711000 { + compatible = "generic-uio"; + reg = < + 0x24711000 0x1c /* INST_SEC_PROC_DFE_PERIP_A55TOCORE0_MAILBOX_DST*/ + 0x24713000 0x1c /* INST_SEC_PROC_DFE_PERIP_A55TOCORE1_MAILBOX_DST*/ + >; + }; + + /* RegMap UIO Device: mailbox_a55tocore_src_sec */ + uio-adrv906x-regmap-mailbox_a55tocore_src_sec@24710000 { + compatible = "generic-uio"; + reg = < + 0x24710000 0x1c /* INST_SEC_PROC_DFE_PERIP_A55TOCORE0_MAILBOX_SRC*/ + 0x24712000 0x1c /* INST_SEC_PROC_DFE_PERIP_A55TOCORE1_MAILBOX_SRC*/ + >; + }; + + /* RegMap UIO Device: mailbox_coretoa55_dst_sec */ + uio-adrv906x-regmap-mailbox_coretoa55_dst_sec@24715000 { + compatible = "generic-uio"; + reg = < + 0x24715000 0x1c /* INST_SEC_PROC_DFE_PERIP_CORE0TOA55_MAILBOX_DST*/ + 0x24717000 0x1c /* INST_SEC_PROC_DFE_PERIP_CORE1TOA55_MAILBOX_DST*/ + >; + }; + + /* RegMap UIO Device: mailbox_coretoa55_src_sec */ + uio-adrv906x-regmap-mailbox_coretoa55_src_sec@24714000 { + compatible = "generic-uio"; + reg = < + 0x24714000 0x1c /* INST_SEC_PROC_DFE_PERIP_CORE0TOA55_MAILBOX_SRC*/ + 0x24716000 0x1c /* INST_SEC_PROC_DFE_PERIP_CORE1TOA55_MAILBOX_SRC*/ + >; + }; + + /* RegMap UIO Device: npd_sec */ + uio-adrv906x-regmap-npd_sec@1d9c0000 { + compatible = "generic-uio"; + reg = < + 0x1d9c0000 0x200000 /* INST_SEC_SLICE_TX0_TX_DFE_TX_NPD*/ + 0x1dbc0000 0x200000 /* INST_SEC_SLICE_TX1_TX_DFE_TX_NPD*/ + 0x1ddc0000 0x200000 /* INST_SEC_SLICE_TX2_TX_DFE_TX_NPD*/ + 0x1dfc0000 0x200000 /* INST_SEC_SLICE_TX3_TX_DFE_TX_NPD*/ + >; + }; + + /* RegMap UIO Device: oran_cduc_sec */ + uio-adrv906x-regmap-oran_cduc_sec@2f030000 { + compatible = "generic-uio"; + reg = < + 0x2f030000 0x1f4 /* INST_SEC_ORAN_TOP_MMR_ORAN_CDUC*/ + >; + }; + + /* RegMap UIO Device: oran_if_sec */ + uio-adrv906x-regmap-oran_if_sec@2f100000 { + compatible = "generic-uio"; + reg = < + 0x2f100000 0x200000 /* INST_SEC_ORAN_TOP_MMR_ORAN_IF_RUE_COMMON*/ + >; + }; + + /* RegMap UIO Device: rcu_sec */ + uio-adrv906x-regmap-rcu_sec@2f02a000 { + compatible = "generic-uio"; + reg = < + 0x2f02a000 0x1cd0 /* INST_SEC_ORAN_TOP_MMR_RADIO_CONTROL*/ + >; + }; + + /* RegMap UIO Device: serdes_sec */ + uio-adrv906x-regmap-serdes_sec@2f390000 { + compatible = "generic-uio"; + reg = < + 0x2f390000 0x100 /* INST_SEC_EMAC_TOP_SERDES_PHY_RXDES_CH0*/ + 0x2f390800 0x100 /* INST_SEC_EMAC_TOP_SERDES_PHY_RXDES_CH1*/ + 0x2f392000 0x40 /* INST_SEC_EMAC_TOP_SERDES_PHY_TXSER_CH0*/ + 0x2f392800 0x40 /* INST_SEC_EMAC_TOP_SERDES_PHY_TXSER_CH1*/ + >; + }; + + /* RegMap UIO Device: serdes_sec2 */ + uio-adrv906x-regmap-serdes_sec2@2f398000 { + compatible = "generic-uio"; + reg = < + 0x2f398000 0x100 /* INST_SEC_EMAC_TOP_SERDES_PHY_SERDES_4_PACK*/ + 0x2f3a0000 0xc9 /* INST_SEC_EMAC_TOP_ETH_PLL*/ + >; + }; + + /* RegMap UIO Device: slice_orx_sec */ + uio-adrv906x-regmap-slice_orx_sec@1c700000 { + compatible = "generic-uio"; + reg = < + 0x1c700000 0x200000 /* INST_SEC_SLICE_ORX*/ + >; + }; + + /* RegMap UIO Device: slice_rx_sec */ + uio-adrv906x-regmap-slice_rx_sec@1d000000 { + compatible = "generic-uio"; + reg = < + 0x1d000000 0x200000 /* INST_SEC_SLICE_RX0*/ + 0x1d200000 0x200000 /* INST_SEC_SLICE_RX1*/ + 0x1d400000 0x200000 /* INST_SEC_SLICE_RX2*/ + 0x1d600000 0x200000 /* INST_SEC_SLICE_RX3*/ + >; + }; + + /* RegMap UIO Device: slice_tx_sec */ + uio-adrv906x-regmap-slice_tx_sec@1d800000 { + compatible = "generic-uio"; + reg = < + 0x1d800000 0x200000 /* INST_SEC_SLICE_TX0*/ + 0x1da00000 0x200000 /* INST_SEC_SLICE_TX1*/ + 0x1dc00000 0x200000 /* INST_SEC_SLICE_TX2*/ + 0x1de00000 0x200000 /* INST_SEC_SLICE_TX3*/ + >; + }; + + /* RegMap UIO Device: stream_memory_sec */ + uio-adrv906x-regmap-stream_memory_sec@1c280000 { + compatible = "generic-uio"; + reg = < + 0x1c280000 0x8000 /* INST_SEC_DIGITAL_CORE_CORE_STREAM_PROC_MEMORY*/ + 0x1c600000 0x2000 /* INST_SEC_DIGITAL_CORE_KFA_STREAM_PROC_MEMORY*/ + 0x1c720000 0x2000 /* INST_SEC_SLICE_ORX_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x1d020000 0x1000 /* INST_SEC_SLICE_RX0_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + >; + }; + + /* RegMap UIO Device: stream_memory_sec2 */ + uio-adrv906x-regmap-stream_memory_sec2@1d220000 { + compatible = "generic-uio"; + reg = < + 0x1d220000 0x1000 /* INST_SEC_SLICE_RX1_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x1d420000 0x1000 /* INST_SEC_SLICE_RX2_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x1d620000 0x1000 /* INST_SEC_SLICE_RX3_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x1d820000 0x2000 /* INST_SEC_SLICE_TX0_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + >; + }; + + /* RegMap UIO Device: stream_memory_sec3 */ + uio-adrv906x-regmap-stream_memory_sec3@1da20000 { + compatible = "generic-uio"; + reg = < + 0x1da20000 0x2000 /* INST_SEC_SLICE_TX1_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x1dc20000 0x2000 /* INST_SEC_SLICE_TX2_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x1de20000 0x2000 /* INST_SEC_SLICE_TX3_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x1d9c0000 0x1000 /* INST_SEC_SLICE_TX0_TX_DFE_TX_NPD_NPD_MEM_STREAM_MEMORY*/ + >; + }; + + /* RegMap UIO Device: stream_memory_sec4 */ + uio-adrv906x-regmap-stream_memory_sec4@1dbc0000 { + compatible = "generic-uio"; + reg = < + 0x1dbc0000 0x1000 /* INST_SEC_SLICE_TX1_TX_DFE_TX_NPD_NPD_MEM_STREAM_MEMORY*/ + 0x1ddc0000 0x1000 /* INST_SEC_SLICE_TX2_TX_DFE_TX_NPD_NPD_MEM_STREAM_MEMORY*/ + 0x1dfc0000 0x1000 /* INST_SEC_SLICE_TX3_TX_DFE_TX_NPD_NPD_MEM_STREAM_MEMORY*/ + >; + }; + + /* RegMap UIO Device: stream_proc_sec */ + uio-adrv906x-regmap-stream_proc_sec@1c288000 { + compatible = "generic-uio"; + reg = < + 0x1c288000 0x200 /* INST_SEC_DIGITAL_CORE_MAIN_STREAM_PROC*/ + 0x1c608000 0x1c8 /* INST_SEC_DIGITAL_CORE_KFA_STREAM_PROC_REGS*/ + 0x1c722000 0x1c8 /* INST_SEC_SLICE_ORX_SLICE_AHB_STREAM_PROC*/ + 0x1d022000 0x1c8 /* INST_SEC_SLICE_RX0_SLICE_AHB_STREAM_PROC*/ + >; + }; + + /* RegMap UIO Device: stream_proc_sec2 */ + uio-adrv906x-regmap-stream_proc_sec2@1d222000 { + compatible = "generic-uio"; + reg = < + 0x1d222000 0x1c8 /* INST_SEC_SLICE_RX1_SLICE_AHB_STREAM_PROC*/ + 0x1d422000 0x1c8 /* INST_SEC_SLICE_RX2_SLICE_AHB_STREAM_PROC*/ + 0x1d622000 0x1c8 /* INST_SEC_SLICE_RX3_SLICE_AHB_STREAM_PROC*/ + 0x1d822000 0x1c8 /* INST_SEC_SLICE_TX0_SLICE_AHB_STREAM_PROC*/ + >; + }; + + /* RegMap UIO Device: stream_proc_sec3 */ + uio-adrv906x-regmap-stream_proc_sec3@1da22000 { + compatible = "generic-uio"; + reg = < + 0x1da22000 0x1c8 /* INST_SEC_SLICE_TX1_SLICE_AHB_STREAM_PROC*/ + 0x1dc22000 0x1c8 /* INST_SEC_SLICE_TX2_SLICE_AHB_STREAM_PROC*/ + 0x1de22000 0x1c8 /* INST_SEC_SLICE_TX3_SLICE_AHB_STREAM_PROC*/ + 0x1d9f4800 0x1c8 /* INST_SEC_SLICE_TX0_TX_DFE_TX_NPD_NPD_SP*/ + >; + }; + + /* RegMap UIO Device: stream_proc_sec4 */ + uio-adrv906x-regmap-stream_proc_sec4@1dbf4800 { + compatible = "generic-uio"; + reg = < + 0x1dbf4800 0x1c8 /* INST_SEC_SLICE_TX1_TX_DFE_TX_NPD_NPD_SP*/ + 0x1ddf4800 0x1c8 /* INST_SEC_SLICE_TX2_TX_DFE_TX_NPD_NPD_SP*/ + 0x1dff4800 0x1c8 /* INST_SEC_SLICE_TX3_TX_DFE_TX_NPD_NPD_SP*/ + >; + }; + + /* RegMap UIO Device: xcorr_sec */ + uio-adrv906x-regmap-xcorr_sec@25400000 { + compatible = "generic-uio"; + reg = < + 0x25400000 0x201050 /* INST_SEC_PROC_DFE_PERIP_XCORR*/ + >; + }; + + /* Interrupt UIO Devices */ + uio-adrv906x-interrupt-12 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-13 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-14 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-15 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-16 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-17 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-18 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-19 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-20 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-21 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-22 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-23 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-24 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-25 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-26 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-27 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-42 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-43 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-46 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-55 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-49 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-40 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-41 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-48 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-56 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-57 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-811 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-812 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-815 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-816 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-819 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-820 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-823 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-824 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-28 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-29 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-30 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-31 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-32 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-33 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-34 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-35 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-220 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-232 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-221 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-233 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-285 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-284 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-280 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-281 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-771 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-772 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-773 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-774 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-287 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-286 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-536 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-537 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-304 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-305 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-306 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-307 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-308 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-309 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-310 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-311 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-312 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-313 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-314 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-315 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-316 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-317 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-318 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-319 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-96 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-104 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-112 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-120 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-160 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-168 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-176 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-184 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-224 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-240 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-241 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-242 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-243 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-244 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-245 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-246 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-247 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-810 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-835 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-836 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-837 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-336 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-759 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-760 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-761 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-762 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-763 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-764 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-765 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-766 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-800 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-801 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-802 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-803 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-804 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-805 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-806 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-807 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-263 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-264 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-272 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-273 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-700 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-701 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-709 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-710 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-rue-fm { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-oif-fm { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-gpint-fm { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-dying-gasp-fm { + compatible = "generic-uio"; + interrupts = ; + }; + +}; diff --git a/arch/arm64/boot/dts/adi/adrv906x-uio.dtsi b/arch/arm64/boot/dts/adi/adrv906x-uio.dtsi new file mode 100644 index 00000000000000..e7138025522861 --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x-uio.dtsi @@ -0,0 +1,1771 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 - 2024, Analog Devices Incorporated, All Rights Reserved + */ + +/dts-v1/; + +#include "adrv906x_irq_def.h" + +/ { + /* RegMap UIO Device: M4_memory */ + uio-adrv906x-regmap-M4_memory@8000000 { + compatible = "generic-uio"; + reg = < + 0x8000000 0x60000 /* INST_ARM_MEMORY + MEMR_IDX_ARM_MEM_CODE_MEMORY_A_0*/ + 0x9000000 0x60000 /* INST_ARM_MEMORY + MEMR_IDX_ARM_MEM_CODE_MEMORY_B_0*/ + 0x30000000 0x60000 /* INST_ARM_MEMORY + MEMR_IDX_ARM_MEM_SYS_MEMORY_A_0*/ + 0x31000000 0x60000 /* INST_ARM_MEMORY + MEMR_IDX_ARM_MEM_SYS_MEMORY_B_0*/ + >; + }; + + /* RegMap UIO Device: sec-M4_memory */ + uio-adrv906x-regmap-sec-M4_memory@c000000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0xc000000 0x60000 /* INST_ALT_ARM_MEMORY + MEMR_IDX_ARM_MEM_CODE_MEMORY_A_0*/ + 0xd000000 0x60000 /* INST_ALT_ARM_MEMORY + MEMR_IDX_ARM_MEM_CODE_MEMORY_B_0*/ + 0x34000000 0x60000 /* INST_ALT_ARM_MEMORY + MEMR_IDX_ARM_MEM_SYS_MEMORY_A_0*/ + 0x35000000 0x60000 /* INST_ALT_ARM_MEMORY + MEMR_IDX_ARM_MEM_SYS_MEMORY_B_0*/ + >; + }; + + /* RegMap UIO Device: antcal */ + uio-adrv906x-regmap-antcal@2b000000 { + compatible = "generic-uio"; + reg = < + 0x2b000000 0x2f4 /* INST_ORAN_TOP_MMR_ANTENNA_CAL_ADAPT*/ + 0x2b000400 0x2000 /* INST_ORAN_TOP_MMR_ANTENNA_CAL_ADAPT_ANT_CAL_ADPT_DL_INJECTION_BUF_MEMORY*/ + 0x2b002400 0xc000 /* INST_ORAN_TOP_MMR_ANTENNA_CAL_ADAPT_ANT_CAL_ADPT_DL_CAPTURE_BUF_MEMORY*/ + 0x2b00e400 0x2000 /* INST_ORAN_TOP_MMR_ANTENNA_CAL_ADAPT_ANT_CAL_ADPT_UL_INJECTION_BUF_MEMORY*/ + >; + }; + + /* RegMap UIO Device: sec-antcal */ + uio-adrv906x-regmap-sec-antcal@2f000000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x2f000000 0x2f4 /* INST_SEC_ORAN_TOP_MMR_ANTENNA_CAL_ADAPT*/ + 0x2f000400 0x2000 /* INST_SEC_ORAN_TOP_MMR_ANTENNA_CAL_ADAPT_ANT_CAL_ADPT_DL_INJECTION_BUF_MEMORY*/ + 0x2f002400 0xc000 /* INST_SEC_ORAN_TOP_MMR_ANTENNA_CAL_ADAPT_ANT_CAL_ADPT_DL_CAPTURE_BUF_MEMORY*/ + 0x2f00e400 0x2000 /* INST_SEC_ORAN_TOP_MMR_ANTENNA_CAL_ADAPT_ANT_CAL_ADPT_UL_INJECTION_BUF_MEMORY*/ + >; + }; + + /* RegMap UIO Device: antcal2 */ + uio-adrv906x-regmap-antcal2@2b010400 { + compatible = "generic-uio"; + reg = < + 0x2b010400 0xc000 /* INST_ORAN_TOP_MMR_ANTENNA_CAL_ADAPT_ANT_CAL_ADPT_UL_CAPTURE_BUF_MEMORY*/ + >; + }; + + /* RegMap UIO Device: sec-antcal2 */ + uio-adrv906x-regmap-sec-antcal2@2f010400 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x2f010400 0xc000 /* INST_SEC_ORAN_TOP_MMR_ANTENNA_CAL_ADAPT_ANT_CAL_ADPT_UL_CAPTURE_BUF_MEMORY*/ + >; + }; + + /* RegMap UIO Device: capture_buffer */ + uio-adrv906x-regmap-capture_buffer@20400000 { + compatible = "generic-uio"; + reg = < + 0x20400000 0x150 /* INST_PROC_DFE_PERIP_CAPBUF*/ + >; + }; + + /* RegMap UIO Device: sec-capture_buffer */ + uio-adrv906x-regmap-sec-capture_buffer@24400000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x24400000 0x150 /* INST_SEC_PROC_DFE_PERIP_CAPBUF*/ + >; + }; + + /* RegMap UIO Device: core */ + uio-adrv906x-regmap-core@18290000 { + compatible = "generic-uio"; + reg = < + 0x18290000 0x1bbf /* INST_DIGITAL_CORE_SPI_ONLY_REGS*/ + 0x18210000 0x74 /* INST_DIGITAL_CORE_TELEMETRY*/ + 0x18300000 0xcc /* INST_DIGITAL_CORE_EAST_RFPLL*/ + 0x18400000 0xcc /* INST_DIGITAL_CORE_WEST_RFPLL*/ + >; + }; + + /* RegMap UIO Device: sec-core */ + uio-adrv906x-regmap-sec-core@1c290000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1c290000 0x1bbf /* INST_SEC_DIGITAL_CORE_SPI_ONLY_REGS*/ + 0x1c210000 0x74 /* INST_SEC_DIGITAL_CORE_TELEMETRY*/ + 0x1c300000 0xcc /* INST_SEC_DIGITAL_CORE_EAST_RFPLL*/ + 0x1c400000 0xcc /* INST_SEC_DIGITAL_CORE_WEST_RFPLL*/ + >; + }; + + /* RegMap UIO Device: core2 */ + uio-adrv906x-regmap-core2@18009000 { + compatible = "generic-uio"; + reg = < + 0x18009000 0x4 /* INST_DIGITAL_CORE_CORE_0_SPI_PM_KEY*/ + 0x18109000 0x4 /* INST_DIGITAL_CORE_CORE_1_SPI_PM_KEY*/ + 0x18610000 0x40000 /* INST_DIGITAL_CORE_MPU_NCO_SUBSYS_REGS0_MPU_REGS*/ + >; + }; + + /* RegMap UIO Device: sec-core2 */ + uio-adrv906x-regmap-sec-core2@1c009000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1c009000 0x4 /* INST_SEC_DIGITAL_CORE_CORE_0_SPI_PM_KEY*/ + 0x1c109000 0x4 /* INST_SEC_DIGITAL_CORE_CORE_1_SPI_PM_KEY*/ + 0x1c610000 0x40000 /* INST_SEC_DIGITAL_CORE_MPU_NCO_SUBSYS_REGS0_MPU_REGS*/ + >; + }; + + /* RegMap UIO Device: datapath_debug */ + uio-adrv906x-regmap-datapath_debug@2b02c400 { + compatible = "generic-uio"; + reg = < + 0x2b02c400 0x134 /* INST_ORAN_TOP_MMR_DATAPATH_DBG_CAPTURE0*/ + 0x2b02c800 0x134 /* INST_ORAN_TOP_MMR_DATAPATH_DBG_CAPTURE1*/ + 0x2b02cc00 0x3c /* INST_ORAN_TOP_MMR_DATAPATH_DBG_INJECT*/ + 0x2b02c000 0x18 /* INST_ORAN_TOP_MMR_DATAPATH_DBG_SHARED*/ + >; + }; + + /* RegMap UIO Device: sec-datapath_debug */ + uio-adrv906x-regmap-sec-datapath_debug@2f02c400 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x2f02c400 0x134 /* INST_SEC_ORAN_TOP_MMR_DATAPATH_DBG_CAPTURE0*/ + 0x2f02c800 0x134 /* INST_SEC_ORAN_TOP_MMR_DATAPATH_DBG_CAPTURE1*/ + 0x2f02cc00 0x3c /* INST_SEC_ORAN_TOP_MMR_DATAPATH_DBG_INJECT*/ + 0x2f02c000 0x18 /* INST_SEC_ORAN_TOP_MMR_DATAPATH_DBG_SHARED*/ + >; + }; + + /* RegMap UIO Device: dfe_perip */ + uio-adrv906x-regmap-dfe_perip@20100000 { + compatible = "generic-uio"; + reg = < + 0x20100000 0xc009c /* INST_PROC_DFE_PERIP_A55_SYS_CFG*/ + 0x20700000 0x8000 /* INST_PROC_DFE_PERIP_A55_TIMER0*/ + 0x20020000 0x4000 /* INST_PROC_DFE_PERIP_MDMA0_CH00*/ + 0x20270000 0x4000 /* INST_PROC_DFE_PERIP_DEBUG_DDE_INJ*/ + >; + }; + + /* RegMap UIO Device: sec-dfe_perip */ + uio-adrv906x-regmap-sec-dfe_perip@24100000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x24100000 0xc009c /* INST_SEC_PROC_DFE_PERIP_A55_SYS_CFG*/ + 0x24700000 0x8000 /* INST_SEC_PROC_DFE_PERIP_A55_TIMER0*/ + 0x24020000 0x4000 /* INST_SEC_PROC_DFE_PERIP_MDMA0_CH00*/ + 0x24270000 0x4000 /* INST_SEC_PROC_DFE_PERIP_DEBUG_DDE_INJ*/ + >; + }; + + /* RegMap UIO Device: dfe_perip2 */ + uio-adrv906x-regmap-dfe_perip2@20050000 { + compatible = "generic-uio"; + reg = < + 0x20050000 0x50 /* INST_PROC_DFE_PERIP_CAPBUFDDE*/ + 0x20271000 0x50 /* INST_PROC_DFE_PERIP_DEBUG_DDE_CAP0*/ + 0x20272000 0x50 /* INST_PROC_DFE_PERIP_DEBUG_DDE_CAP1*/ + 0x202a0000 0x50 /* INST_PROC_DFE_PERIP_PIMC_DDE*/ + >; + }; + + /* RegMap UIO Device: sec-dfe_perip2 */ + uio-adrv906x-regmap-sec-dfe_perip2@24050000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x24050000 0x50 /* INST_SEC_PROC_DFE_PERIP_CAPBUFDDE*/ + 0x24271000 0x50 /* INST_SEC_PROC_DFE_PERIP_DEBUG_DDE_CAP0*/ + 0x24272000 0x50 /* INST_SEC_PROC_DFE_PERIP_DEBUG_DDE_CAP1*/ + 0x242a0000 0x50 /* INST_SEC_PROC_DFE_PERIP_PIMC_DDE*/ + >; + }; + + /* RegMap UIO Device: dfe_perip3 */ + uio-adrv906x-regmap-dfe_perip3@20280000 { + compatible = "generic-uio"; + reg = < + 0x20280000 0x50 /* INST_PROC_DFE_PERIP_ANTENNA_CAL_DDE*/ + 0x20052000 0x7f8 /* INST_PROC_DFE_PERIP_TRU*/ + 0x20218000 0x8dc /* INST_PROC_DFE_PERIP_GPIO_PINMUX_PAD*/ + >; + }; + + /* RegMap UIO Device: sec-dfe_perip3 */ + uio-adrv906x-regmap-sec-dfe_perip3@24280000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x24280000 0x50 /* INST_SEC_PROC_DFE_PERIP_ANTENNA_CAL_DDE*/ + 0x24052000 0x7f8 /* INST_SEC_PROC_DFE_PERIP_TRU*/ + 0x24218000 0x8dc /* INST_SEC_PROC_DFE_PERIP_GPIO_PINMUX_PAD*/ + >; + }; + + /* RegMap UIO Device: feature_filter */ + uio-adrv906x-regmap-feature_filter@20728000 { + compatible = "generic-uio"; + reg = < + 0x20728000 0x90 /* INST_PROC_DFE_PERIP_FEATURE_FILTER*/ + >; + }; + + /* RegMap UIO Device: sec-feature_filter */ + uio-adrv906x-regmap-sec-feature_filter@24728000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x24728000 0x90 /* INST_SEC_PROC_DFE_PERIP_FEATURE_FILTER*/ + >; + }; + + /* RegMap UIO Device: interrupt_aggregator_core */ + uio-adrv906x-regmap-interrupt_aggregator_core@20200000 { + compatible = "generic-uio"; + reg = < + 0x20200000 0x74 /* INST_PROC_DFE_PERIP_INTR_AGGREGATOR*/ + >; + }; + + /* RegMap UIO Device: sec-interrupt_aggregator_core */ + uio-adrv906x-regmap-sec-interrupt_aggregator_core@24200000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x24200000 0x74 /* INST_SEC_PROC_DFE_PERIP_INTR_AGGREGATOR*/ + >; + }; + + /* RegMap UIO Device: kfa */ + uio-adrv906x-regmap-kfa@18609000 { + compatible = "generic-uio"; + reg = < + 0x18609000 0x288 /* INST_DIGITAL_CORE_KFA_TOP*/ + >; + }; + + /* RegMap UIO Device: sec-kfa */ + uio-adrv906x-regmap-sec-kfa@1c609000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1c609000 0x288 /* INST_SEC_DIGITAL_CORE_KFA_TOP*/ + >; + }; + + /* RegMap UIO Device: mailbox_a55_spi0_dst */ + uio-adrv906x-regmap-mailbox_a55_spi0_dst@18690000 { + compatible = "generic-uio"; + reg = < + 0x18690000 0x1c /* INST_DIGITAL_CORE_A55_SPI1_CMD_MAILBOX*/ + 0x18691000 0x1c /* INST_DIGITAL_CORE_A55_SPI0_CMD_MAILBOX*/ + >; + }; + + /* RegMap UIO Device: sec-mailbox_a55_spi0_dst */ + uio-adrv906x-regmap-sec-mailbox_a55_spi0_dst@1c690000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1c690000 0x1c /* INST_SEC_DIGITAL_CORE_A55_SPI1_CMD_MAILBOX*/ + 0x1c691000 0x1c /* INST_SEC_DIGITAL_CORE_A55_SPI0_CMD_MAILBOX*/ + >; + }; + + /* RegMap UIO Device: mailbox_a55_spi0_src */ + uio-adrv906x-regmap-mailbox_a55_spi0_src@18690000 { + compatible = "generic-uio"; + reg = < + 0x18690000 0x1c /* INST_DIGITAL_CORE_A55_SPI1_CMD_MAILBOX*/ + 0x18691000 0x1c /* INST_DIGITAL_CORE_A55_SPI0_CMD_MAILBOX*/ + >; + }; + + /* RegMap UIO Device: sec-mailbox_a55_spi0_src */ + uio-adrv906x-regmap-sec-mailbox_a55_spi0_src@1c690000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1c690000 0x1c /* INST_SEC_DIGITAL_CORE_A55_SPI1_CMD_MAILBOX*/ + 0x1c691000 0x1c /* INST_SEC_DIGITAL_CORE_A55_SPI0_CMD_MAILBOX*/ + >; + }; + + /* RegMap UIO Device: mailbox_a55tocore_dst */ + uio-adrv906x-regmap-mailbox_a55tocore_dst@20711000 { + compatible = "generic-uio"; + reg = < + 0x20711000 0x1c /* INST_PROC_DFE_PERIP_A55TOCORE0_MAILBOX_DST*/ + 0x20713000 0x1c /* INST_PROC_DFE_PERIP_A55TOCORE1_MAILBOX_DST*/ + >; + }; + + /* RegMap UIO Device: sec-mailbox_a55tocore_dst */ + uio-adrv906x-regmap-sec-mailbox_a55tocore_dst@24711000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x24711000 0x1c /* INST_SEC_PROC_DFE_PERIP_A55TOCORE0_MAILBOX_DST*/ + 0x24713000 0x1c /* INST_SEC_PROC_DFE_PERIP_A55TOCORE1_MAILBOX_DST*/ + >; + }; + + /* RegMap UIO Device: mailbox_a55tocore_src */ + uio-adrv906x-regmap-mailbox_a55tocore_src@20710000 { + compatible = "generic-uio"; + reg = < + 0x20710000 0x1c /* INST_PROC_DFE_PERIP_A55TOCORE0_MAILBOX_SRC*/ + 0x20712000 0x1c /* INST_PROC_DFE_PERIP_A55TOCORE1_MAILBOX_SRC*/ + >; + }; + + /* RegMap UIO Device: sec-mailbox_a55tocore_src */ + uio-adrv906x-regmap-sec-mailbox_a55tocore_src@24710000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x24710000 0x1c /* INST_SEC_PROC_DFE_PERIP_A55TOCORE0_MAILBOX_SRC*/ + 0x24712000 0x1c /* INST_SEC_PROC_DFE_PERIP_A55TOCORE1_MAILBOX_SRC*/ + >; + }; + + /* RegMap UIO Device: mailbox_coretoa55_dst */ + uio-adrv906x-regmap-mailbox_coretoa55_dst@20715000 { + compatible = "generic-uio"; + reg = < + 0x20715000 0x1c /* INST_PROC_DFE_PERIP_CORE0TOA55_MAILBOX_DST*/ + 0x20717000 0x1c /* INST_PROC_DFE_PERIP_CORE1TOA55_MAILBOX_DST*/ + >; + }; + + /* RegMap UIO Device: sec-mailbox_coretoa55_dst */ + uio-adrv906x-regmap-sec-mailbox_coretoa55_dst@24715000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x24715000 0x1c /* INST_SEC_PROC_DFE_PERIP_CORE0TOA55_MAILBOX_DST*/ + 0x24717000 0x1c /* INST_SEC_PROC_DFE_PERIP_CORE1TOA55_MAILBOX_DST*/ + >; + }; + + /* RegMap UIO Device: mailbox_coretoa55_src */ + uio-adrv906x-regmap-mailbox_coretoa55_src@20714000 { + compatible = "generic-uio"; + reg = < + 0x20714000 0x1c /* INST_PROC_DFE_PERIP_CORE0TOA55_MAILBOX_SRC*/ + 0x20716000 0x1c /* INST_PROC_DFE_PERIP_CORE1TOA55_MAILBOX_SRC*/ + >; + }; + + /* RegMap UIO Device: sec-mailbox_coretoa55_src */ + uio-adrv906x-regmap-sec-mailbox_coretoa55_src@24714000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x24714000 0x1c /* INST_SEC_PROC_DFE_PERIP_CORE0TOA55_MAILBOX_SRC*/ + 0x24716000 0x1c /* INST_SEC_PROC_DFE_PERIP_CORE1TOA55_MAILBOX_SRC*/ + >; + }; + + /* RegMap UIO Device: npd */ + uio-adrv906x-regmap-npd@199c0000 { + compatible = "generic-uio"; + reg = < + 0x199c0000 0x200000 /* INST_SLICE_TX0_TX_DFE_TX_NPD*/ + 0x19bc0000 0x200000 /* INST_SLICE_TX1_TX_DFE_TX_NPD*/ + 0x19dc0000 0x200000 /* INST_SLICE_TX2_TX_DFE_TX_NPD*/ + 0x19fc0000 0x200000 /* INST_SLICE_TX3_TX_DFE_TX_NPD*/ + >; + }; + + /* RegMap UIO Device: sec-npd */ + uio-adrv906x-regmap-sec-npd@1d9c0000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1d9c0000 0x200000 /* INST_SEC_SLICE_TX0_TX_DFE_TX_NPD*/ + 0x1dbc0000 0x200000 /* INST_SEC_SLICE_TX1_TX_DFE_TX_NPD*/ + 0x1ddc0000 0x200000 /* INST_SEC_SLICE_TX2_TX_DFE_TX_NPD*/ + 0x1dfc0000 0x200000 /* INST_SEC_SLICE_TX3_TX_DFE_TX_NPD*/ + >; + }; + + /* RegMap UIO Device: oran_cduc */ + uio-adrv906x-regmap-oran_cduc@2b030000 { + compatible = "generic-uio"; + reg = < + 0x2b030000 0x1f4 /* INST_ORAN_TOP_MMR_ORAN_CDUC*/ + >; + }; + + /* RegMap UIO Device: sec-oran_cduc */ + uio-adrv906x-regmap-sec-oran_cduc@2f030000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x2f030000 0x1f4 /* INST_SEC_ORAN_TOP_MMR_ORAN_CDUC*/ + >; + }; + + /* RegMap UIO Device: oran_if */ + uio-adrv906x-regmap-oran_if@2b100000 { + compatible = "generic-uio"; + reg = < + 0x2b100000 0x200000 /* INST_ORAN_TOP_MMR_ORAN_IF_RUE_COMMON*/ + >; + }; + + /* RegMap UIO Device: sec-oran_if */ + uio-adrv906x-regmap-sec-oran_if@2f100000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x2f100000 0x200000 /* INST_SEC_ORAN_TOP_MMR_ORAN_IF_RUE_COMMON*/ + >; + }; + + /* RegMap UIO Device: rcu */ + uio-adrv906x-regmap-rcu@2b02a000 { + compatible = "generic-uio"; + reg = < + 0x2b02a000 0x1cd0 /* INST_ORAN_TOP_MMR_RADIO_CONTROL*/ + >; + }; + + /* RegMap UIO Device: sec-rcu */ + uio-adrv906x-regmap-sec-rcu@2f02a000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x2f02a000 0x1cd0 /* INST_SEC_ORAN_TOP_MMR_RADIO_CONTROL*/ + >; + }; + + /* RegMap UIO Device: serdes */ + uio-adrv906x-regmap-serdes@2b390000 { + compatible = "generic-uio"; + reg = < + 0x2b390000 0x100 /* INST_EMAC_TOP_SERDES_PHY_RXDES_CH0*/ + 0x2b390800 0x100 /* INST_EMAC_TOP_SERDES_PHY_RXDES_CH1*/ + 0x2b392000 0x40 /* INST_EMAC_TOP_SERDES_PHY_TXSER_CH0*/ + 0x2b392800 0x40 /* INST_EMAC_TOP_SERDES_PHY_TXSER_CH1*/ + >; + }; + + /* RegMap UIO Device: sec-serdes */ + uio-adrv906x-regmap-sec-serdes@2f390000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x2f390000 0x100 /* INST_SEC_EMAC_TOP_SERDES_PHY_RXDES_CH0*/ + 0x2f390800 0x100 /* INST_SEC_EMAC_TOP_SERDES_PHY_RXDES_CH1*/ + 0x2f392000 0x40 /* INST_SEC_EMAC_TOP_SERDES_PHY_TXSER_CH0*/ + 0x2f392800 0x40 /* INST_SEC_EMAC_TOP_SERDES_PHY_TXSER_CH1*/ + >; + }; + + /* RegMap UIO Device: serdes2 */ + uio-adrv906x-regmap-serdes2@2b398000 { + compatible = "generic-uio"; + reg = < + 0x2b398000 0x100 /* INST_EMAC_TOP_SERDES_PHY_SERDES_4_PACK*/ + 0x2b3a0000 0xc9 /* INST_EMAC_TOP_ETH_PLL*/ + >; + }; + + /* RegMap UIO Device: sec-serdes2 */ + uio-adrv906x-regmap-sec-serdes2@2f398000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x2f398000 0x100 /* INST_SEC_EMAC_TOP_SERDES_PHY_SERDES_4_PACK*/ + 0x2f3a0000 0xc9 /* INST_SEC_EMAC_TOP_ETH_PLL*/ + >; + }; + + /* RegMap UIO Device: slice_orx */ + uio-adrv906x-regmap-slice_orx@18700000 { + compatible = "generic-uio"; + reg = < + 0x18700000 0x200000 /* INST_SLICE_ORX*/ + >; + }; + + /* RegMap UIO Device: sec-slice_orx */ + uio-adrv906x-regmap-sec-slice_orx@1c700000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1c700000 0x200000 /* INST_SEC_SLICE_ORX*/ + >; + }; + + /* RegMap UIO Device: slice_rx */ + uio-adrv906x-regmap-slice_rx@19000000 { + compatible = "generic-uio"; + reg = < + 0x19000000 0x200000 /* INST_SLICE_RX0*/ + 0x19200000 0x200000 /* INST_SLICE_RX1*/ + 0x19400000 0x200000 /* INST_SLICE_RX2*/ + 0x19600000 0x200000 /* INST_SLICE_RX3*/ + >; + }; + + /* RegMap UIO Device: sec-slice_rx */ + uio-adrv906x-regmap-sec-slice_rx@1d000000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1d000000 0x200000 /* INST_SEC_SLICE_RX0*/ + 0x1d200000 0x200000 /* INST_SEC_SLICE_RX1*/ + 0x1d400000 0x200000 /* INST_SEC_SLICE_RX2*/ + 0x1d600000 0x200000 /* INST_SEC_SLICE_RX3*/ + >; + }; + + /* RegMap UIO Device: slice_tx */ + uio-adrv906x-regmap-slice_tx@19800000 { + compatible = "generic-uio"; + reg = < + 0x19800000 0x200000 /* INST_SLICE_TX0*/ + 0x19a00000 0x200000 /* INST_SLICE_TX1*/ + 0x19c00000 0x200000 /* INST_SLICE_TX2*/ + 0x19e00000 0x200000 /* INST_SLICE_TX3*/ + >; + }; + + /* RegMap UIO Device: sec-slice_tx */ + uio-adrv906x-regmap-sec-slice_tx@1d800000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1d800000 0x200000 /* INST_SEC_SLICE_TX0*/ + 0x1da00000 0x200000 /* INST_SEC_SLICE_TX1*/ + 0x1dc00000 0x200000 /* INST_SEC_SLICE_TX2*/ + 0x1de00000 0x200000 /* INST_SEC_SLICE_TX3*/ + >; + }; + + /* RegMap UIO Device: stream_memory */ + uio-adrv906x-regmap-stream_memory@18280000 { + compatible = "generic-uio"; + reg = < + 0x18280000 0x8000 /* INST_DIGITAL_CORE_CORE_STREAM_PROC_MEMORY*/ + 0x18600000 0x2000 /* INST_DIGITAL_CORE_KFA_STREAM_PROC_MEMORY*/ + 0x18720000 0x2000 /* INST_SLICE_ORX_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x19020000 0x1000 /* INST_SLICE_RX0_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + >; + }; + + /* RegMap UIO Device: sec-stream_memory */ + uio-adrv906x-regmap-sec-stream_memory@1c280000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1c280000 0x8000 /* INST_SEC_DIGITAL_CORE_CORE_STREAM_PROC_MEMORY*/ + 0x1c600000 0x2000 /* INST_SEC_DIGITAL_CORE_KFA_STREAM_PROC_MEMORY*/ + 0x1c720000 0x2000 /* INST_SEC_SLICE_ORX_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x1d020000 0x1000 /* INST_SEC_SLICE_RX0_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + >; + }; + + /* RegMap UIO Device: stream_memory2 */ + uio-adrv906x-regmap-stream_memory2@19220000 { + compatible = "generic-uio"; + reg = < + 0x19220000 0x1000 /* INST_SLICE_RX1_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x19420000 0x1000 /* INST_SLICE_RX2_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x19620000 0x1000 /* INST_SLICE_RX3_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x19820000 0x2000 /* INST_SLICE_TX0_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + >; + }; + + /* RegMap UIO Device: sec-stream_memory2 */ + uio-adrv906x-regmap-sec-stream_memory2@1d220000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1d220000 0x1000 /* INST_SEC_SLICE_RX1_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x1d420000 0x1000 /* INST_SEC_SLICE_RX2_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x1d620000 0x1000 /* INST_SEC_SLICE_RX3_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x1d820000 0x2000 /* INST_SEC_SLICE_TX0_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + >; + }; + + /* RegMap UIO Device: stream_memory3 */ + uio-adrv906x-regmap-stream_memory3@19a20000 { + compatible = "generic-uio"; + reg = < + 0x19a20000 0x2000 /* INST_SLICE_TX1_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x19c20000 0x2000 /* INST_SLICE_TX2_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x19e20000 0x2000 /* INST_SLICE_TX3_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x199c0000 0x1000 /* INST_SLICE_TX0_TX_DFE_TX_NPD_NPD_MEM_STREAM_MEMORY*/ + >; + }; + + /* RegMap UIO Device: sec-stream_memory3 */ + uio-adrv906x-regmap-sec-stream_memory3@1da20000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1da20000 0x2000 /* INST_SEC_SLICE_TX1_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x1dc20000 0x2000 /* INST_SEC_SLICE_TX2_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x1de20000 0x2000 /* INST_SEC_SLICE_TX3_SLICE_AHB_AHB_STREAM_PROC_MEMORY*/ + 0x1d9c0000 0x1000 /* INST_SEC_SLICE_TX0_TX_DFE_TX_NPD_NPD_MEM_STREAM_MEMORY*/ + >; + }; + + /* RegMap UIO Device: stream_memory4 */ + uio-adrv906x-regmap-stream_memory4@19bc0000 { + compatible = "generic-uio"; + reg = < + 0x19bc0000 0x1000 /* INST_SLICE_TX1_TX_DFE_TX_NPD_NPD_MEM_STREAM_MEMORY*/ + 0x19dc0000 0x1000 /* INST_SLICE_TX2_TX_DFE_TX_NPD_NPD_MEM_STREAM_MEMORY*/ + 0x19fc0000 0x1000 /* INST_SLICE_TX3_TX_DFE_TX_NPD_NPD_MEM_STREAM_MEMORY*/ + >; + }; + + /* RegMap UIO Device: sec-stream_memory4 */ + uio-adrv906x-regmap-sec-stream_memory4@1dbc0000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1dbc0000 0x1000 /* INST_SEC_SLICE_TX1_TX_DFE_TX_NPD_NPD_MEM_STREAM_MEMORY*/ + 0x1ddc0000 0x1000 /* INST_SEC_SLICE_TX2_TX_DFE_TX_NPD_NPD_MEM_STREAM_MEMORY*/ + 0x1dfc0000 0x1000 /* INST_SEC_SLICE_TX3_TX_DFE_TX_NPD_NPD_MEM_STREAM_MEMORY*/ + >; + }; + + /* RegMap UIO Device: stream_proc */ + uio-adrv906x-regmap-stream_proc@18288000 { + compatible = "generic-uio"; + reg = < + 0x18288000 0x200 /* INST_DIGITAL_CORE_MAIN_STREAM_PROC*/ + 0x18608000 0x1c8 /* INST_DIGITAL_CORE_KFA_STREAM_PROC_REGS*/ + 0x18722000 0x1c8 /* INST_SLICE_ORX_SLICE_AHB_STREAM_PROC*/ + 0x19022000 0x1c8 /* INST_SLICE_RX0_SLICE_AHB_STREAM_PROC*/ + >; + }; + + /* RegMap UIO Device: sec-stream_proc */ + uio-adrv906x-regmap-sec-stream_proc@1c288000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1c288000 0x200 /* INST_SEC_DIGITAL_CORE_MAIN_STREAM_PROC*/ + 0x1c608000 0x1c8 /* INST_SEC_DIGITAL_CORE_KFA_STREAM_PROC_REGS*/ + 0x1c722000 0x1c8 /* INST_SEC_SLICE_ORX_SLICE_AHB_STREAM_PROC*/ + 0x1d022000 0x1c8 /* INST_SEC_SLICE_RX0_SLICE_AHB_STREAM_PROC*/ + >; + }; + + /* RegMap UIO Device: stream_proc2 */ + uio-adrv906x-regmap-stream_proc2@19222000 { + compatible = "generic-uio"; + reg = < + 0x19222000 0x1c8 /* INST_SLICE_RX1_SLICE_AHB_STREAM_PROC*/ + 0x19422000 0x1c8 /* INST_SLICE_RX2_SLICE_AHB_STREAM_PROC*/ + 0x19622000 0x1c8 /* INST_SLICE_RX3_SLICE_AHB_STREAM_PROC*/ + 0x19822000 0x1c8 /* INST_SLICE_TX0_SLICE_AHB_STREAM_PROC*/ + >; + }; + + /* RegMap UIO Device: stream_proc3 */ + uio-adrv906x-regmap-stream_proc3@19a22000 { + compatible = "generic-uio"; + reg = < + 0x19a22000 0x1c8 /* INST_SLICE_TX1_SLICE_AHB_STREAM_PROC*/ + 0x19c22000 0x1c8 /* INST_SLICE_TX2_SLICE_AHB_STREAM_PROC*/ + 0x19e22000 0x1c8 /* INST_SLICE_TX3_SLICE_AHB_STREAM_PROC*/ + 0x199f4800 0x1c8 /* INST_SLICE_TX0_TX_DFE_TX_NPD_NPD_SP*/ + >; + }; + + /* RegMap UIO Device: sec-stream_proc2 */ + uio-adrv906x-regmap-sec-stream_proc2@1d222000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1d222000 0x1c8 /* INST_SEC_SLICE_RX1_SLICE_AHB_STREAM_PROC*/ + 0x1d422000 0x1c8 /* INST_SEC_SLICE_RX2_SLICE_AHB_STREAM_PROC*/ + 0x1d622000 0x1c8 /* INST_SEC_SLICE_RX3_SLICE_AHB_STREAM_PROC*/ + 0x1d822000 0x1c8 /* INST_SEC_SLICE_TX0_SLICE_AHB_STREAM_PROC*/ + >; + }; + + /* RegMap UIO Device: sec-stream_proc3 */ + uio-adrv906x-regmap-sec-stream_proc3@1da22000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1da22000 0x1c8 /* INST_SEC_SLICE_TX1_SLICE_AHB_STREAM_PROC*/ + 0x1dc22000 0x1c8 /* INST_SEC_SLICE_TX2_SLICE_AHB_STREAM_PROC*/ + 0x1de22000 0x1c8 /* INST_SEC_SLICE_TX3_SLICE_AHB_STREAM_PROC*/ + 0x1d9f4800 0x1c8 /* INST_SEC_SLICE_TX0_TX_DFE_TX_NPD_NPD_SP*/ + >; + }; + + /* RegMap UIO Device: stream_proc4 */ + uio-adrv906x-regmap-stream_proc4@19bf4800 { + compatible = "generic-uio"; + reg = < + 0x19bf4800 0x1c8 /* INST_SLICE_TX1_TX_DFE_TX_NPD_NPD_SP*/ + 0x19df4800 0x1c8 /* INST_SLICE_TX2_TX_DFE_TX_NPD_NPD_SP*/ + 0x19ff4800 0x1c8 /* INST_SLICE_TX3_TX_DFE_TX_NPD_NPD_SP*/ + >; + }; + + /* RegMap UIO Device: sec-stream_proc4 */ + uio-adrv906x-regmap-sec-stream_proc4@1dbf4800 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x1dbf4800 0x1c8 /* INST_SEC_SLICE_TX1_TX_DFE_TX_NPD_NPD_SP*/ + 0x1ddf4800 0x1c8 /* INST_SEC_SLICE_TX2_TX_DFE_TX_NPD_NPD_SP*/ + 0x1dff4800 0x1c8 /* INST_SEC_SLICE_TX3_TX_DFE_TX_NPD_NPD_SP*/ + >; + }; + + /* RegMap UIO Device: xcorr */ + uio-adrv906x-regmap-xcorr@21400000 { + compatible = "generic-uio"; + reg = < + 0x21400000 0x201050 /* INST_PROC_DFE_PERIP_XCORR*/ + >; + }; + + /* RegMap UIO Device: sec-xcorr */ + uio-adrv906x-regmap-sec-xcorr@25400000 { + compatible = "generic-uio"; + status = "disabled"; + reg = < + 0x25400000 0x201050 /* INST_SEC_PROC_DFE_PERIP_XCORR*/ + >; + }; + + /* RegMap UIO Device: telem_buff */ + uio-adrv906x-regmap-telem_buff@18240000 { + compatible = "generic-uio"; + reg = < + 0x18240000 0x18000 /* Telem Buff Region*/ + >; + }; + + /* RegMap UIO Device: sec-telem_buff */ + uio-adrv906x-regmap-sec-telem_buff@1c240000 { + compatible = "generic-uio"; + reg = < + 0x1c240000 0x18000 /* Secondary Telem Buff Region*/ + >; + }; + + /* Interrupt UIO Devices */ + uio-adrv906x-interrupt-12 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-13 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-14 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-15 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-16 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-17 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-18 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-19 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-20 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-21 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-22 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-23 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-24 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-25 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-26 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-27 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-424 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-425 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-426 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-427 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-428 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-429 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-430 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-431 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-432 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-433 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-434 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-435 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-436 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-437 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-438 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-439 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-interrupt-42 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-43 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-442 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-443 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-interrupt-55 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-49 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-40 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-41 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-440 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-441 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-interrupt-48 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-56 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-57 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-811 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-812 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-815 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-816 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-819 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-820 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-823 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-824 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-729 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-730 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-733 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-734 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-737 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-738 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-741 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-742 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-interrupt-28 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-29 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-30 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-31 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-32 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-33 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-34 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-35 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-220 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-232 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-221 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-233 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-285 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-284 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-280 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-281 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-771 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-772 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-773 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-774 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-527 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-528 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-540 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-541 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-577 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-578 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-575 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-576 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-714 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-715 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-716 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-717 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-interrupt-287 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-286 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-579 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-580 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-interrupt-536 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-537 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-658 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-659 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-interrupt-304 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-305 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-306 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-307 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-308 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-309 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-310 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-311 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-312 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-313 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-314 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-315 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-316 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-317 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-318 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-319 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-594 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-595 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-596 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-597 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-598 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-599 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-600 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-601 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-602 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-603 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-604 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-605 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-606 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-607 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-interrupt-96 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-104 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-112 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-120 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-160 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-168 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-176 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-184 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-224 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-240 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-241 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-242 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-243 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-244 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-245 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-246 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-247 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-542 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-543 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-544 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-545 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-546 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-547 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-548 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-549 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-interrupt-810 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-835 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-836 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-837 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-728 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-745 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-746 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-747 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-748 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-interrupt-336 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-759 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-760 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-761 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-762 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-763 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-764 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-765 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-766 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-800 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-801 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-802 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-803 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-804 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-805 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-806 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-807 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-263 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-264 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-272 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-273 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-700 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-701 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-709 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-interrupt-710 { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-610 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-681 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-682 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-683 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-684 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-685 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-686 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-687 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-688 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-720 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-721 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-722 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-723 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-724 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-725 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-726 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-727 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-561 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-562 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-570 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-571 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-670 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-671 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-679 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-680 { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-interrupt-rue-fm { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-rue-fm { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-interrupt-oif-fm { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-oif-fm { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-interrupt-gpint-fm { + compatible = "generic-uio"; + interrupts = ; + }; + uio-adrv906x-c2c-interrupt-gpint-fm { + compatible = "generic-uio"; + status = "disabled"; + interrupts = ; + }; + uio-adrv906x-interrupt-dying-gasp-fm { + compatible = "generic-uio"; + interrupts = ; + }; + +}; diff --git a/arch/arm64/boot/dts/adi/adrv906x.dtsi b/arch/arm64/boot/dts/adi/adrv906x.dtsi new file mode 100755 index 00000000000000..162c0737ad0e38 --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x.dtsi @@ -0,0 +1,851 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Analog Devices Incorporated, All Rights Reserved + */ + +/dts-v1/; + +#include /* GPIO_ACTIVE_LOW */ +#include +#include +#include + +#include "adrv906x_def.h" +#include "adrv906x_irq_def.h" +#include "adrv906x-uio.dtsi" + +/ { + model = "ADI ADRV906X SoC"; + compatible = "adi,adrv906x"; + + interrupt-parent = <&gic>; + #address-cells = <1>; + #size-cells = <1>; + + chosen { + /* Boot-related information. + * Populated by U-Boot. Intentionally left blank here. + */ + boot { + device = ""; + slot = ""; + te-slot = ""; + plat = ""; + lifecycle-state { + description = ""; + deployed = <0>; + }; + }; + }; + + aliases { + /* UART consoles */ + serial0 = &uart0; + serial1 = &uart1; + serial3 = &uart3; + serial4 = &uart4; + + /* Virtual UART: A55-to-M4 */ + serial5 = &v_uart1_1; + + /* Virtual UART: A55-to-A55 + * (applicable for dual-tile only) + */ + serial6 = &v_uart0_0; + + /* I2C buses */ + i2c0 = &i2c0; + i2c1 = &i2c1; + i2c2 = &i2c2; + i2c3 = &i2c3; + i2c4 = &i2c4; + i2c5 = &i2c5; + i2c6 = &i2c6; + i2c7 = &i2c7; + + /* QSPI bus */ + spi99 = &qspi0; + + /* SPI buses */ + spi0 = &spi0; + spi1 = &spi1; + spi2 = &spi2; + spi3 = &spi3; + spi4 = &spi4; + spi5 = &spi5; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + cpu_off = <0x84000002>; + cpu_on = <0xc4000003>; + }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + + sdei { + compatible = "arm,sdei-1.0"; + method = "smc"; + }; + }; + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu-map { + cluster0 { + core0 { + cpu = <&CPU0>; + }; + core1 { + cpu = <&CPU1>; + }; + core2 { + cpu = <&CPU2>; + }; + core3 { + cpu = <&CPU3>; + }; + }; + }; + + CPU0:cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0 0x0>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; + + CPU1:cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0 0x100>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; + + CPU2:cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0 0x200>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; + + CPU3:cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0 0x300>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; + + L2_0: l2-cache0 { + compatible = "cache"; + }; + }; + + /* Populated by U-Boot. Intentionally left blank here. */ + memory { + device_type = "memory"; + reg = <0x00000000 0x00000000>; + }; + + /* Definition of primary and secondary regions + * Secondary regions are only intended for a special use-case (dual-tile + * with no Linux on secondary, but DDR installed in secondary) + */ + + /* L4 regions + * U-boot will remove the secondary region for the regular use-case + */ + sram_memory { + device_type = "memory"; + reg = <0x00100000 0x00400000>, /* Primary L4 */ + <0x04100000 0x00400000>; /* Secondary L4 */ + }; + + /* XCORR memory */ + xcorr_memory { + device_type = "memory"; + reg = <0x38800000 0x00201000>, /* Primary */ + <0x3c800000 0x00201000>; /* Secondary */ + }; + + /* Reserved memory regions + * 1) U-boot will enable secondary regions only for the special use-case. + * They are intentionally left disabled by default. + * 2) DDR base address (primary and secondary) is computed and + * populated by U-boot. This value is intentionally left blank + * 3) The size of each region is explicitly defined, except for the + * secondary DDR region. U-boot will populate the whole secondary DDR + * region + */ + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + /* Primary regions */ + sram0_res: sram-reserved@0 { + compatible = "adi,sram-access"; + reg = <0x00100000 0x00010000>; /* 64 KB */ + no-map; + }; + + sram1_res: sram-reserved@1 { + compatible = "adi,sram-access"; + reg = <0x00110000 0x003F0000>; /* 4 MB - 64 KB */ + no-map; + }; + + ddr0_res: ddr-reserved@0 { + compatible = "adi,sram-access"; + reg = <0x00000000 0x11000000>; /* 272 MB */ + no-map; + status = "disabled"; + }; + + xcorr0_res: xcorr-reserved@0 { + compatible = "adi,sram-access"; + reg = <0x38800000 0x00201000>; /* 2 MB + 4 KB */ + no-map; + }; + + /* Secondary regions */ + + sram2_res: sram-reserved@2 { + compatible = "adi,sram-access"; + reg = <0x04100000 0x00400000>; /* 4 MB */ + no-map; + status = "disabled"; + }; + + ddr1_res: ddr-reserved@1 { + compatible = "adi,sram-access"; + reg = <0x00000000 0x00000000>; /* Entire DDR */ + no-map; + status = "disabled"; + }; + + xcorr1_res: xcorr-reserved@1 { + compatible = "adi,sram-access"; + reg = <0x3c800000 0x00201000>; /* 2 MB + 4 KB */ + no-map; + status = "disabled"; + }; + }; + + /* Link reserved regions to User Space + * U-boot will enable secondary regions only for the special use-case. + * They are intentionally left disabled by default. + */ + sram0_mmap: sram-mmap@0 { + /* Region for device profile */ + compatible = "adi,sram-mmap"; + memory-region = <&sram0_res>; + status = "okay"; + }; + + sram1_mmap: sram-mmap@1 { + compatible = "adi,sram-mmap"; + memory-region = <&sram1_res>; + status = "okay"; + }; + + ddr0_mmap: ddr-mmap@0 { + compatible = "adi,sram-mmap"; + memory-region = <&ddr0_res>; + status = "okay"; + }; + + xcorr0_mmap: xcorr-mmap@0 { + compatible = "adi,sram-mmap"; + memory-region = <&xcorr0_res>; + status = "okay"; + }; + + sram2_mmap: sram-mmap@2 { + compatible = "adi,sram-mmap"; + memory-region = <&sram2_res>; + status = "disabled"; + }; + + ddr1_mmap: ddr-mmap@1 { + compatible = "adi,sram-mmap"; + memory-region = <&ddr1_res>; + status = "disabled"; + }; + + xcorr1_mmap: xcorr-mmap@1 { + compatible = "adi,sram-mmap"; + memory-region = <&xcorr1_res>; + status = "disabled"; + }; + + gic: interrupt-controller@GIC_BASE_UADDR { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + interrupt-controller; + reg = , /* GICD */ + <(GIC_BASE + 0x00040000) 0x80000>; /* GICR */ + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , /* Physical Secure */ + , /* Physical Non-Secure */ + , /* Virtual */ + ; /* Hypervisor */ + }; + + /* High Speed digital clock + * Populated by U-Boot. Intentionally left blank here. + */ + hsdigclk: hsdigclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + + /* System clock + * Populated by U-Boot. Intentionally left blank here. + */ + sysclk: sysclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + + /* eMMC/SD card clock + * Populated by U-Boot. Intentionally left blank here. + */ + mmcclk: mmcclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + + emac0clk: emac0clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <250000000>; + }; + + watchdog { + compatible = "arm,smc-wdt"; + timeout-sec = <60>; + }; + + uart0: uart@PL011_0_BASE_UADDR { + compatible = "arm,pl011", "arm,primecell"; + reg = ; + interrupts = ; + clocks = <&sysclk>, <&sysclk>; + clock-names = "uartclk", "apb_pclk"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart0>; + status = "disabled"; + }; + + uart1: uart@PL011_1_BASE_UADDR { + compatible = "arm,pl011", "arm,primecell"; + reg = ; + interrupts = ; + clocks = <&sysclk>, <&sysclk>; + clock-names = "uartclk", "apb_pclk"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; + status = "disabled"; + }; + + /* uart2 is intentionally not populated here. It is assigned to the + * Cortex M4 processor. + */ + + uart3: uart@PL011_2_BASE_UADDR { + compatible = "arm,pl011", "arm,primecell"; + reg = ; + interrupts = ; + clocks = <&sysclk>, <&sysclk>; + clock-names = "uartclk", "apb_pclk"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart3>; + status = "disabled"; + }; + + uart4: uart@PL011_3_BASE_UADDR { + compatible = "arm,pl011", "arm,primecell"; + reg = ; + interrupts = ; + clocks = <&sysclk>, <&sysclk>; + clock-names = "uartclk", "apb_pclk"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4>; + status = "disabled"; + }; + + v_uart0_0: v_uart@VIRTUAL_PL011_0_0_BASE_UADDR { + compatible = "arm,pl011", "arm,primecell"; + reg = ; + interrupts = ; + clocks = <&sysclk>, <&sysclk>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + v_uart1_1: v_uart@VIRTUAL_PL011_1_1_BASE_UADDR { + compatible = "arm,pl011", "arm,primecell"; + reg = ; + interrupts = ; + clocks = <&sysclk>, <&sysclk>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + pinctrl_primary: pinctrl@PINCTRL_BASE_UADDR { + reg = ; + compatible = "adi,adrv906x-pinctrl"; + }; + + gpio0: gpio@GPIO_NS_BASE_UADDR { + compatible = "adi,adrv906x-gpio"; + reg = ; + pintmux = ; + gpio-controller; + #gpio-cells = <2>; + ngpios = ; + #interrupt-cells = <2>; + interrupt-controller; + }; + + /* QSPI */ + qspi0: spi@QSPI_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = ; + interrupts = ; + dmas = <&qspi0_dma 10>, <&qspi0_dma 11>; + dma-names = "tx", "rx"; + clocks = <&sysclk>; + clock-names = "spi"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_qspi>; + status = "disabled"; + }; + + /* QSPI: DMA */ + qspi0_dma: dma@QSPI_0_TX_DDE_BASE_UADDR { + compatible = "adi,dma-controller"; + reg = ; + status = "disabled"; + #dma-cells = <1>; + + qspi_tx: channel@10 { + adi,id = <10>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0>; + periph-intf-width = <1>; + adi,skip-interrupts = <0>; + }; + + qspi_rx: channel@11 { + adi,id = <11>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x1000>; + periph-intf-width = <1>; + adi,skip-interrupts = <0>; + }; + }; + + /* SDHCI: EMMC regulator */ + mmc0_regulator: fixed-regulator_1v8 { + compatible = "regulator-fixed"; + regulator-name = "1V8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + status = "disabled"; + }; + + /* SDHCI: SD regulator */ + mmc1_regulator: fixed-regulator_3v3 { + compatible = "regulator-fixed"; + regulator-name = "3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + status = "disabled"; + }; + + /* SDHCI: eMMC PHY */ + mmc0_phy: phy@EMMC_0_PHY_BASE_UADDR { + compatible = "adi,sdhci-phy"; + reg = ; + #phy-cells = <0>; + adi,dcode-legacy = <0x78>; + adi,dcode-hs200 = <0x00>; + adi,dcode-hs400 = <0x08>; + adi,driver-strength-ohm = <40>; + status = "disabled"; + }; + + /* SDHCI: eMMC interface */ + mmc0: mmc@EMMC_0_BASE_UADDR { + compatible = "adi,dwcmshc-sdhci"; + reg = ; + interrupts = ; /* Status */ + clocks = <&mmcclk>; + clock-names = "core"; + phys = <&mmc0_phy>; + phy-names = "phy_adi_sdhci"; + max-frequency = <196608000>; + bus-width = <8>; + vqmmc-supply = <&mmc0_regulator>; + status = "disabled"; + enable-phy-config; + disable-wp; + non-removable; + no-sdio; + no-sd; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + adi,retune-period = <3600>; + cap-mmc-hw-reset; + #address-cells = <1>; + #size-cells = <0>; + }; + + /* SDHCI: SD interface */ + mmc1: mmc@SD_0_BASE_UADDR { + compatible = "adi,dwcmshc-sdhci"; + reg = ; + interrupts = , /* Status */ + ; /* Wakeup */ + clocks = <&mmcclk>; + clock-names = "core"; + max-frequency = <50000000>; + bus-width = <4>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_mmc1_sd>; + vmmc-supply = <&mmc1_regulator>; + status = "disabled"; + disable-wp; + no-sdio; + no-mmc; + #address-cells = <1>; + #size-cells = <0>; + }; + + emac0: ethernet@EMAC_1G_BASE_UADDR { + compatible = "adi,adrv906x-dwmac", "snps,dwmac-5.10a"; + reg = ; + interrupts = ; + interrupt-names = "macirq"; + clocks = <&emac0clk>; + clock-names = "stmmaceth"; + phy-mode = "rgmii"; + snps,reset-gpio = <&gpio0 ADI_ADRV906X_PIN_88 GPIO_ACTIVE_LOW>; + snps,reset-delays-us = <1000 1000 1000>; + snps,tso; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_emac0_rgmii>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <1>; + emac0_clk_div: clock_divider { + reg = ; + ctrl_reg = ; + }; + }; + + i2c0: twi@I2C_0_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c0>; + status = "disabled"; + }; + + i2c1: twi@I2C_1_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; + status = "disabled"; + }; + + i2c2: twi@I2C_2_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; + status = "disabled"; + }; + + i2c3: twi@I2C_3_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3>; + status = "disabled"; + }; + + i2c4: twi@I2C_4_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c4>; + status = "disabled"; + }; + + i2c5: twi@I2C_5_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c5>; + status = "disabled"; + }; + + i2c6: twi@I2C_6_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c6>; + status = "disabled"; + }; + + i2c7: twi@I2C_7_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = ; + interrupts = ; + clock-khz = <100>; + clocks = <&sysclk>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c7>; + status = "disabled"; + }; + + spi0: spi@SPI_0_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = ; + interrupts = ; + clocks = <&sysclk>; + clock-names = "spi"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi0>; + status = "disabled"; + }; + + spi1: spi@SPI_1_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = ; + interrupts = ; + clocks = <&sysclk>; + clock-names = "spi"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi1>; + status = "disabled"; + }; + + spi2: spi@SPI_2_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = ; + interrupts = ; + clocks = <&sysclk>; + clock-names = "spi"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi2>; + status = "disabled"; + }; + + spi3: spi@SPI_3_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = ; + interrupts = ; + clocks = <&sysclk>; + clock-names = "spi"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi3>; + status = "disabled"; + }; + + spi4: spi@SPI_4_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = ; + interrupts = ; + clocks = <&sysclk>; + clock-names = "spi"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi4>; + status = "disabled"; + }; + + spi5: spi@SPI_5_BASE_UADDR { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = ; + interrupts = ; + clocks = <&sysclk>; + clock-names = "spi"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi5>; + status = "disabled"; + }; + + dac0: dac@PWM_BASE_UADDR { + compatible = "adi,pwm-dac"; + reg = ; + clocks = <&hsdigclk>; + adi,iovdd-microvolt = <1800000>; + adi,gpio-max-frequency = <122880000>; + status = "disabled"; + }; + + ndma_reset_ctrl: reset@NDMA_RST_CTRL_UADDR{ + reg = ; + }; + ndma0_interrupt_ctrl: ndma0_intr_ctrl@NDMA_0_INTR_CTRL_UADDR { + reg = ; + }; + ndma1_interrupt_ctrl: ndma1_intr_ctrl@NDMA_1_INTR_CTRL_UADDR { + reg = ; + }; + + ndma0: ndma0@NDMA_0_TX_BASE_UADDR { + id = <0>; + reg = , + , + , + , + ; + reset-ctrl = <&ndma_reset_ctrl>; + interrupts = , + , + , + , + , + ; + interrupt-names = "tx_data_dma_done", "tx_data_dma_error", + "tx_status_dma_done", "tx_status_dma_error", + "rx_dma_done", "rx_dma_error"; + interrupt-ctrl = <&ndma0_interrupt_ctrl>; + }; + + + ndma1: ndma1@NDMA_1_TX_BASE_UADDR { + id = <1>; + reg = , + , + , + , + ; + reset-ctrl = <&ndma_reset_ctrl>; + interrupts = , + , + , + , + , + ; + interrupt-names = "tx_data_dma_done", "tx_data_dma_error", + "tx_status_dma_done", "tx_status_dma_error", + "rx_dma_done", "rx_dma_error"; + interrupt-ctrl = <&ndma1_interrupt_ctrl>; + }; + + ptpclk: ptpclk { + compatible = "adi,adrv906x-tod"; + #address-cells = <1>; + #size-cells = <1>; + reg = ; + interrupts = ; + interrupt-names = "pps"; + clocks = <&hsdigclk>, <&hsdigclk>; + clock-names = "lc_clk", "gc_clk"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_one_pps>; + adi,ppsx-pulse-width-ns = <10000000>; + adi,pps-in-pulse-width-ms = <500>; + + adrv906x-tod { + adi,default-tod-counter = <0>; + #address-cells = <1>; + #size-cells = <0>; + tod-counter@0 { + reg = <0>; + adi,trigger-delay-tick = <491520>; + }; + tod-counter@1 { + reg = <1>; + adi,trigger-delay-tick = <491520>; + }; + tod-counter@2 { + reg = <2>; + adi,pps-mode; + }; + }; + }; +}; + +#include "adrv906x-pinctrl.dtsi" diff --git a/arch/arm64/boot/dts/adi/adrv906x_def.h b/arch/arm64/boot/dts/adi/adrv906x_def.h new file mode 100644 index 00000000000000..9136c36970265f --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x_def.h @@ -0,0 +1,294 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2025, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __ADI_ADRV906X_DEF_H__ +#define __ADI_ADRV906X_DEF_H__ + +#define ANTENNA_CAL_DDE_BASE 0x20280000 +#define DEBUG_DDE_INJ_BASE 0x20270000 +#define EMAC_1G_BASE 0x20720000 +#define EMAC_CMN_BASE 0x2B300000 +#define EMAC_MACSEC_0_BASE 0x2B360000 +#define EMAC_MACSEC_1_BASE 0x2B370000 +#define EMAC_MAC_0_BASE 0x2B330000 +#define EMAC_MAC_0_RX 0x2B331300 +#define EMAC_MAC_0_TX 0x2B330300 +#define EMAC_MAC_1_BASE 0x2B340000 +#define EMAC_MAC_1_RX 0x2B341300 +#define EMAC_MAC_1_TX 0x2B340300 +#define EMAC_PCS_0_BASE 0x2B310000 +#define EMAC_PCS_0_TSU 0x2B310400 +#define EMAC_PCS_1_BASE 0x2B320000 +#define EMAC_PCS_1_TSU 0x2B320400 +#define EMAC_SW_BASE 0x2B350100 +#define EMAC_SW_MAE_BASE 0x2B350300 +#define EMAC_SW_PORT_0_BASE 0x2B350600 +#define EMAC_SW_PORT_1_BASE 0x2B350D00 +#define EMAC_SW_PORT_2_BASE 0x2B351400 +#define EMAC_TOD_BASE 0x2B380000 +#define EMMC_0_BASE 0x20724000 +#define EMMC_0_PHY_BASE 0x20724300 +#define GIC_BASE 0x21000000 +#define GPIO_NS_BASE 0x2021B000 +#define I2C_0_BASE 0x20760000 +#define I2C_1_BASE 0x20760100 +#define I2C_2_BASE 0x20760200 +#define I2C_3_BASE 0x20760300 +#define I2C_4_BASE 0x20760400 +#define I2C_5_BASE 0x20760500 +#define I2C_6_BASE 0x20760600 +#define I2C_7_BASE 0x20760700 +#define MDMA_0_CH00_BASE 0x20020000 +#define NDMA_0_RX_BASE 0x20214000 +#define NDMA_0_RX_DMA 0x20260000 +#define NDMA_0_TX_BASE 0x20216000 +#define NDMA_0_TX_DMA 0x20261000 +#define NDMA_0_TX_STATUS_DMA 0x20264000 +#define NDMA_1_RX_BASE 0x20215000 +#define NDMA_1_RX_DMA 0x20262000 +#define NDMA_1_TX_BASE 0x20217000 +#define NDMA_1_TX_DMA 0x20263000 +#define NDMA_1_TX_STATUS_DMA 0x20265000 +#define PIMC_DDE_BASE 0x202A0000 +#define PINCTRL_BASE 0x20218000 +#define PINTMUX_BASE 0x20102200 +#define PL011_0_BASE 0x20060000 +#define PL011_1_BASE 0x20061000 +#define PL011_2_BASE 0x20062000 +#define PL011_3_BASE 0x20063000 +#define PWM_BASE 0x20727000 +#define QSPI_0_RX_DDE_BASE 0x20731000 +#define QSPI_0_TX_DDE_BASE 0x20730000 +#define QSPI_BASE 0x20732000 +#define SD_0_BASE 0x20725000 +#define SEC_EMAC_CMN_BASE 0x2F300000 +#define SEC_EMAC_MACSEC_0_BASE 0x2F360000 +#define SEC_EMAC_MACSEC_1_BASE 0x2F370000 +#define SEC_EMAC_MAC_0_BASE 0x2F330000 +#define SEC_EMAC_MAC_0_RX 0x2F331300 +#define SEC_EMAC_MAC_0_TX 0x2F330300 +#define SEC_EMAC_MAC_1_BASE 0x2F340000 +#define SEC_EMAC_MAC_1_RX 0x2F341300 +#define SEC_EMAC_MAC_1_TX 0x2F340300 +#define SEC_EMAC_PCS_0_BASE 0x2F310000 +#define SEC_EMAC_PCS_0_TSU 0x2F310400 +#define SEC_EMAC_PCS_1_BASE 0x2F320000 +#define SEC_EMAC_PCS_1_TSU 0x2F320400 +#define SEC_EMAC_SW_BASE 0x2F350100 +#define SEC_EMAC_SW_MAE_BASE 0x2F350300 +#define SEC_EMAC_SW_PORT_0_BASE 0x2F350600 +#define SEC_EMAC_SW_PORT_1_BASE 0x2F350D00 +#define SEC_EMAC_SW_PORT_2_BASE 0x2F351400 +#define SEC_EMAC_TOD_BASE 0x2F380000 +#define SEC_GIC_BASE 0x25000000 +#define SEC_GPIO_NS_BASE 0x2421B000 +#define SEC_I2C_0_BASE 0x24760000 +#define SEC_I2C_1_BASE 0x24760100 +#define SEC_I2C_2_BASE 0x24760200 +#define SEC_I2C_3_BASE 0x24760300 +#define SEC_I2C_4_BASE 0x24760400 +#define SEC_I2C_5_BASE 0x24760500 +#define SEC_I2C_6_BASE 0x24760600 +#define SEC_I2C_7_BASE 0x24760700 +#define SEC_NDMA_0_RX_BASE 0x24214000 +#define SEC_NDMA_0_RX_DMA 0x24260000 +#define SEC_NDMA_0_TX_BASE 0x24216000 +#define SEC_NDMA_0_TX_DMA 0x24261000 +#define SEC_NDMA_0_TX_STATUS_DMA 0x24264000 +#define SEC_NDMA_1_RX_BASE 0x24215000 +#define SEC_NDMA_1_RX_DMA 0x24262000 +#define SEC_NDMA_1_TX_BASE 0x24217000 +#define SEC_NDMA_1_TX_DMA 0x24263000 +#define SEC_NDMA_1_TX_STATUS_DMA 0x24265000 +#define SEC_PINCTRL_BASE 0x24218000 +#define SEC_PINTMUX_BASE 0x24102200 +#define SEC_PL011_3_BASE 0x24063000 +#define SEC_PWM_BASE 0x24727000 +#define SEC_SERDES_0_RX_BASE 0x2F390000 +#define SEC_SERDES_0_TX_BASE 0x2F392000 +#define SEC_SERDES_1_RX_BASE 0x2F390800 +#define SEC_SERDES_1_TX_BASE 0x2F392800 +#define SEC_SERDES_4_PACK_BASE 0x2F398000 +#define SEC_VIRTUAL_PL011_1_1_BASE 0x24067000 +#define SERDES_0_RX_BASE 0x2B390000 +#define SERDES_0_TX_BASE 0x2B392000 +#define SERDES_1_RX_BASE 0x2B390800 +#define SERDES_1_TX_BASE 0x2B392800 +#define SERDES_4_PACK_BASE 0x2B398000 +#define SPI_0_BASE 0x20733000 +#define SPI_1_BASE 0x20734000 +#define SPI_2_BASE 0x20735000 +#define SPI_3_BASE 0x20736000 +#define SPI_4_BASE 0x20737000 +#define SPI_5_BASE 0x20738000 +#define TRU_BASE 0x20052000 +#define VIRTUAL_PL011_0_0_BASE 0x20064000 +#define VIRTUAL_PL011_0_1_BASE 0x20065000 +#define VIRTUAL_PL011_1_1_BASE 0x20067000 + +#define ANTENNA_CAL_DDE_BASE_UADDR 20280000 +#define DEBUG_DDE_INJ_BASE_UADDR 20270000 +#define EMAC_1G_BASE_UADDR 20720000 +#define EMAC_CMN_BASE_UADDR 2B300000 +#define EMAC_MACSEC_0_BASE_UADDR 2B360000 +#define EMAC_MACSEC_1_BASE_UADDR 2B370000 +#define EMAC_MAC_0_BASE_UADDR 2B330000 +#define EMAC_MAC_0_RX_UADDR 2B331300 +#define EMAC_MAC_0_TX_UADDR 2B330300 +#define EMAC_MAC_1_BASE_UADDR 2B340000 +#define EMAC_MAC_1_RX_UADDR 2B341300 +#define EMAC_MAC_1_TX_UADDR 2B340300 +#define EMAC_PCS_0_BASE_UADDR 2B310000 +#define EMAC_PCS_0_TSU_UADDR 2B310400 +#define EMAC_PCS_1_BASE_UADDR 2B320000 +#define EMAC_PCS_1_TSU_UADDR 2B320400 +#define EMAC_SW_BASE_UADDR 2B350100 +#define EMAC_SW_MAE_BASE_UADDR 2B350300 +#define EMAC_SW_PORT_0_BASE_UADDR 2B350600 +#define EMAC_SW_PORT_1_BASE_UADDR 2B350D00 +#define EMAC_SW_PORT_2_BASE_UADDR 2B351400 +#define EMAC_TOD_BASE_UADDR 2B380000 +#define EMMC_0_BASE_UADDR 20724000 +#define EMMC_0_PHY_BASE_UADDR 20724300 +#define GIC_BASE_UADDR 21000000 +#define GPIO_NS_BASE_UADDR 2021B000 +#define I2C_0_BASE_UADDR 20760000 +#define I2C_1_BASE_UADDR 20760100 +#define I2C_2_BASE_UADDR 20760200 +#define I2C_3_BASE_UADDR 20760300 +#define I2C_4_BASE_UADDR 20760400 +#define I2C_5_BASE_UADDR 20760500 +#define I2C_6_BASE_UADDR 20760600 +#define I2C_7_BASE_UADDR 20760700 +#define MDMA_0_CH00_BASE_UADDR 20020000 +#define NDMA_0_RX_BASE_UADDR 20214000 +#define NDMA_0_RX_DMA_UADDR 20260000 +#define NDMA_0_TX_BASE_UADDR 20216000 +#define NDMA_0_TX_DMA_UADDR 20261000 +#define NDMA_0_TX_STATUS_DMA_UADDR 20264000 +#define NDMA_1_RX_BASE_UADDR 20215000 +#define NDMA_1_RX_DMA_UADDR 20262000 +#define NDMA_1_TX_BASE_UADDR 20217000 +#define NDMA_1_TX_DMA_UADDR 20263000 +#define NDMA_1_TX_STATUS_DMA_UADDR 20265000 +#define PIMC_DDE_BASE_UADDR 202A0000 +#define PINCTRL_BASE_UADDR 20218000 +#define PINTMUX_BASE_UADDR 20102200 +#define PL011_0_BASE_UADDR 20060000 +#define PL011_1_BASE_UADDR 20061000 +#define PL011_2_BASE_UADDR 20062000 +#define PL011_3_BASE_UADDR 20063000 +#define PWM_BASE_UADDR 20727000 +#define QSPI_0_RX_DDE_BASE_UADDR 20731000 +#define QSPI_0_TX_DDE_BASE_UADDR 20730000 +#define QSPI_BASE_UADDR 20732000 +#define SD_0_BASE_UADDR 20725000 +#define SEC_EMAC_CMN_BASE_UADDR 2F300000 +#define SEC_EMAC_MACSEC_0_BASE_UADDR 2F360000 +#define SEC_EMAC_MACSEC_1_BASE_UADDR 2F370000 +#define SEC_EMAC_MAC_0_BASE_UADDR 2F330000 +#define SEC_EMAC_MAC_0_RX_UADDR 2F331300 +#define SEC_EMAC_MAC_0_TX_UADDR 2F330300 +#define SEC_EMAC_MAC_1_BASE_UADDR 2F340000 +#define SEC_EMAC_MAC_1_RX_UADDR 2F341300 +#define SEC_EMAC_MAC_1_TX_UADDR 2F340300 +#define SEC_EMAC_PCS_0_BASE_UADDR 2F310000 +#define SEC_EMAC_PCS_0_TSU_UADDR 2F310400 +#define SEC_EMAC_PCS_1_BASE_UADDR 2F320000 +#define SEC_EMAC_PCS_1_TSU_UADDR 2F320400 +#define SEC_EMAC_SW_BASE_UADDR 2F350100 +#define SEC_EMAC_SW_MAE_BASE_UADDR 2F350300 +#define SEC_EMAC_SW_PORT_0_BASE_UADDR 2F350600 +#define SEC_EMAC_SW_PORT_1_BASE_UADDR 2F350D00 +#define SEC_EMAC_SW_PORT_2_BASE_UADDR 2F351400 +#define SEC_EMAC_TOD_BASE_UADDR 2F380000 +#define SEC_GIC_BASE_UADDR 25000000 +#define SEC_GPIO_NS_BASE_UADDR 2421B000 +#define SEC_I2C_0_BASE_UADDR 24760000 +#define SEC_I2C_1_BASE_UADDR 24760100 +#define SEC_I2C_2_BASE_UADDR 24760200 +#define SEC_I2C_3_BASE_UADDR 24760300 +#define SEC_I2C_4_BASE_UADDR 24760400 +#define SEC_I2C_5_BASE_UADDR 24760500 +#define SEC_I2C_6_BASE_UADDR 24760600 +#define SEC_I2C_7_BASE_UADDR 24760700 +#define SEC_NDMA_0_RX_BASE_UADDR 24214000 +#define SEC_NDMA_0_RX_DMA_UADDR 24260000 +#define SEC_NDMA_0_TX_BASE_UADDR 24216000 +#define SEC_NDMA_0_TX_DMA_UADDR 24261000 +#define SEC_NDMA_0_TX_STATUS_DMA_UADDR 24264000 +#define SEC_NDMA_1_RX_BASE_UADDR 24215000 +#define SEC_NDMA_1_RX_DMA_UADDR 24262000 +#define SEC_NDMA_1_TX_BASE_UADDR 24217000 +#define SEC_NDMA_1_TX_DMA_UADDR 24263000 +#define SEC_NDMA_1_TX_STATUS_DMA_UADDR 24265000 +#define SEC_PINCTRL_BASE_UADDR 24218000 +#define SEC_PINTMUX_BASE_UADDR 24102200 +#define SEC_PL011_3_BASE_UADDR 24063000 +#define SEC_PWM_BASE_UADDR 24727000 +#define SEC_SERDES_0_RX_BASE_UADDR 2F390000 +#define SEC_SERDES_0_TX_BASE_UADDR 2F392000 +#define SEC_SERDES_1_RX_BASE_UADDR 2F390800 +#define SEC_SERDES_1_TX_BASE_UADDR 2F392800 +#define SEC_SERDES_4_PACK_BASE_UADDR 2F398000 +#define SEC_VIRTUAL_PL011_1_1_BASE_UADDR 24067000 +#define SERDES_0_RX_BASE_UADDR 2B390000 +#define SERDES_0_TX_BASE_UADDR 2B392000 +#define SERDES_1_RX_BASE_UADDR 2B390800 +#define SERDES_1_TX_BASE_UADDR 2B392800 +#define SERDES_4_PACK_BASE_UADDR 2B398000 +#define SPI_0_BASE_UADDR 20733000 +#define SPI_1_BASE_UADDR 20734000 +#define SPI_2_BASE_UADDR 20735000 +#define SPI_3_BASE_UADDR 20736000 +#define SPI_4_BASE_UADDR 20737000 +#define SPI_5_BASE_UADDR 20738000 +#define TRU_BASE_UADDR 20052000 +#define VIRTUAL_PL011_0_0_BASE_UADDR 20064000 +#define VIRTUAL_PL011_0_1_BASE_UADDR 20065000 +#define VIRTUAL_PL011_1_1_BASE_UADDR 20067000 + +#define EMAC_1G_DIV_CTRL 0x201c0050 +#define EMAC_1G_CLK_CTRL 0x20190000 +#define NDMA_RST_CTRL 0x201c0000 +#define NDMA_0_INTR_CTRL 0x201c0060 +#define NDMA_1_INTR_CTRL 0x201c0064 +#define OIF_0_RX_CTRL 0x2b103040 +#define OIF_0_TX_CTRL 0x2b10903c +#define OIF_1_RX_CTRL 0x2b104340 +#define OIF_1_TX_CTRL 0x2b10b83c +#define EMAC_RECOVERED_CLK_CTRL 0x20102d00 +#define SEC_EMAC_1G_CTRL 0x241c0050 +#define SEC_NDMA_RST_CTRL 0x241c0000 +#define SEC_NDMA_0_INTR_CTRL 0x241c0060 +#define SEC_NDMA_1_INTR_CTRL 0x241c0064 +#define SEC_OIF_0_RX_CTRL 0x2f103040 +#define SEC_OIF_0_TX_CTRL 0x2f10903c +#define SEC_OIF_1_RX_CTRL 0x2f104340 +#define SEC_OIF_1_TX_CTRL 0x2f10b83c +#define SEC_EMAC_RECOVERED_CLK_CTRL 0x24102d00 + +#define EMAC_1G_DIV_CTRL_UADDR 201C0050 +#define EMAC_1G_CLK_CTRL_UADDR 20190000 +#define NDMA_RST_CTRL_UADDR 201C0000 +#define NDMA_0_INTR_CTRL_UADDR 201C0060 +#define NDMA_1_INTR_CTRL_UADDR 201C0064 +#define OIF_0_RX_CTRL_UADDR 2B103040 +#define OIF_0_TX_CTRL_UADDR 2B10903C +#define OIF_1_RX_CTRL_UADDR 2B104340 +#define OIF_1_TX_CTRL_UADDR 2B10B83C +#define EMAC_RECOVERED_CLK_CTRL_UADDR 20102D00 +#define SEC_EMAC_1G_CTRL_UADDR 241C0050 +#define SEC_NDMA_RST_CTRL_UADDR 241C0000 +#define SEC_NDMA_0_INTR_CTRL_UADDR 241C0060 +#define SEC_NDMA_1_INTR_CTRL_UADDR 241C0064 +#define SEC_OIF_0_RX_CTRL_UADDR 2F103040 +#define SEC_OIF_0_TX_CTRL_UADDR 2F10903C +#define SEC_OIF_1_RX_CTRL_UADDR 2F104340 +#define SEC_OIF_1_TX_CTRL_UADDR 2F10B83C +#define SEC_EMAC_RECOVERED_CLK_CTRL_UADDR 24102D00 + + +#endif /* __ADI_ADRV906X_DEF_H__ */ diff --git a/arch/arm64/boot/dts/adi/adrv906x_irq_def.h b/arch/arm64/boot/dts/adi/adrv906x_irq_def.h new file mode 100644 index 00000000000000..d6127bd9179aac --- /dev/null +++ b/arch/arm64/boot/dts/adi/adrv906x_irq_def.h @@ -0,0 +1,971 @@ +/* + * Copyright (c) 2025, Analog Devices Incorporated, All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __ADI_ADRV906X_IRQ_DEF_H__ +#define __ADI_ADRV906X_IRQ_DEF_H__ + +#define L4_SCBINIT_INTR_0 0 +#define L4_ECC_WRN_INTR_0 1 +#define L4_ECC_ERR_INTR_0 2 +#define L4_SCBINIT_INTR_1 3 +#define L4_ECC_WRN_INTR_1 4 +#define L4_ECC_ERR_INTR_1 5 +#define L4_SCBINIT_INTR_2 6 +#define L4_ECC_WRN_INTR_2 7 +#define L4_ECC_ERR_INTR_2 8 +#define L4CTL_0_SWU_INTR 9 +#define L4CTL_1_SWU_INTR 10 +#define L4CTL_2_SWU_INTR 11 +#define MDMA_CH0_DONE_INTR_0 12 +#define MDMA_CH0_ERR_INTR_0 13 +#define MDMA_CH1_DONE_INTR_0 14 +#define MDMA_CH1_ERR_INTR_0 15 +#define MDMA_CH0_DONE_INTR_1 16 +#define MDMA_CH0_ERR_INTR_1 17 +#define MDMA_CH1_DONE_INTR_1 18 +#define MDMA_CH1_ERR_INTR_1 19 +#define MDMA_CH0_DONE_INTR_2 20 +#define MDMA_CH0_ERR_INTR_2 21 +#define MDMA_CH1_DONE_INTR_2 22 +#define MDMA_CH1_ERR_INTR_2 23 +#define MDMA_CH0_DONE_INTR_3 24 +#define MDMA_CH0_ERR_INTR_3 25 +#define MDMA_CH1_DONE_INTR_3 26 +#define MDMA_CH1_ERR_INTR_3 27 +#define GPT_A55_IRQ_PIPED_0 28 +#define GPT_A55_IRQ_PIPED_1 29 +#define GPT_A55_IRQ_PIPED_2 30 +#define GPT_A55_IRQ_PIPED_3 31 +#define GPT_A55_IRQ_PIPED_4 32 +#define GPT_A55_IRQ_PIPED_5 33 +#define GPT_A55_IRQ_PIPED_6 34 +#define GPT_A55_IRQ_PIPED_7 35 +#define WATCHDOG_A55_TIMEOUT_PIPED_0 36 +#define WATCHDOG_A55_TIMEOUT_PIPED_1 37 +#define WATCHDOG_A55_TIMEOUT_PIPED_2 38 +#define WATCHDOG_A55_TIMEOUT_PIPED_3 39 +#define A55_PERI_MAILBOX_INTERRUPT_PIPED_2 40 +#define A55_PERI_MAILBOX_INTERRUPT_PIPED_3 41 +#define CBDDE_DONE_INTR_0 42 +#define CBDDE_ERR_INTR_0 43 +#define RUE_IRQ 44 +#define OIF_IRQ 45 +#define CB_DONE_INTR_0 46 +#define QSFP_INTERRUPT 47 +#define XCORR_DONE_INT_PIPED 48 +#define MB_SPI0_TO_A55 49 +#define ALT_A55_AHB_ERROR_INDICATION_PIPED 50 +#define NCNTHPIRQ_0 10 +#define NCNTPNSIRQ_0 14 +#define NCNTPSIRQ_0 13 +#define NCNTVIRQ_0 11 +#define NCNTHVIRQ_0 12 +#define NCNTHPIRQ_1 10 +#define NCNTPNSIRQ_1 14 +#define NCNTPSIRQ_1 13 +#define NCNTVIRQ_1 11 +#define NCNTHVIRQ_1 12 +#define NCNTHPIRQ_2 10 +#define NCNTPNSIRQ_2 14 +#define NCNTPSIRQ_2 13 +#define NCNTVIRQ_2 11 +#define NCNTHVIRQ_2 12 +#define NCNTHPIRQ_3 10 +#define NCNTPNSIRQ_3 14 +#define NCNTPSIRQ_3 13 +#define NCNTVIRQ_3 11 +#define NCNTHVIRQ_3 12 +#define NVCPUMNTIRQ_0 9 +#define NVCPUMNTIRQ_1 9 +#define NVCPUMNTIRQ_2 9 +#define NVCPUMNTIRQ_3 9 +#define NPMUIRQ_0 7 +#define NPMUIRQ_1 7 +#define NPMUIRQ_2 7 +#define NPMUIRQ_3 7 +#define NCLUSTERPMUIRQ 51 +#define GIC_PMU_INT 52 +#define POSTED_HRESP_LVL_A55_PIPED 53 +#define FF_FEATURE_DONE_PIPED 54 +#define FF_PROGRAM_DONE_PIPED 55 +#define XCORR_ECC_ERROR_IRQ_PIPED 56 +#define XCORR_ECC_ERROR_WARNING_PIPED 57 +#define XCORR_DATA_REQUEST_INT_PIPED 58 +#define TEMP_SENSOR_INT0 59 +#define TEMP_SENSOR_INT1 60 +#define RFFE_0_INT_SYNC 61 +#define RFFE_1_INT_SYNC 62 +#define DYINGGASPDETECTION_POWERCONTROL 63 +#define GPIO_TO_GIC_SYNC_0 64 +#define GPIO_TO_GIC_SYNC_1 65 +#define GPIO_TO_GIC_SYNC_2 66 +#define GPIO_TO_GIC_SYNC_3 67 +#define GPIO_TO_GIC_SYNC_4 68 +#define GPIO_TO_GIC_SYNC_5 69 +#define GPIO_TO_GIC_SYNC_6 70 +#define GPIO_TO_GIC_SYNC_7 71 +#define GPIO_TO_GIC_SYNC_8 72 +#define GPIO_TO_GIC_SYNC_9 73 +#define GPIO_TO_GIC_SYNC_10 74 +#define GPIO_TO_GIC_SYNC_11 75 +#define GPIO_TO_GIC_SYNC_12 76 +#define GPIO_TO_GIC_SYNC_13 77 +#define GPIO_TO_GIC_SYNC_14 78 +#define GPIO_TO_GIC_SYNC_15 79 +#define GPIO_TO_GIC_SYNC_16 80 +#define GPIO_TO_GIC_SYNC_17 81 +#define GPIO_TO_GIC_SYNC_18 82 +#define GPIO_TO_GIC_SYNC_19 83 +#define GPIO_TO_GIC_SYNC_20 84 +#define GPIO_TO_GIC_SYNC_21 85 +#define GPIO_TO_GIC_SYNC_22 86 +#define GPIO_TO_GIC_SYNC_23 87 +#define PIMC0_EXT_IRQ_0 88 +#define PIMC0_EXT_IRQ_1 89 +#define NCOMMIRQ_0 6 +#define NCOMMIRQ_1 6 +#define NCOMMIRQ_2 6 +#define NCOMMIRQ_3 6 +#define CTIIRQ_0 8 +#define CTIIRQ_1 8 +#define CTIIRQ_2 8 +#define CTIIRQ_3 8 +#define PIMC1_EXT_IRQ_0 90 +#define PIMC1_EXT_IRQ_1 91 +#define A55_TRU_INTR0 92 +#define A55_TRU_INTR1 93 +#define A55_TRU_INTR2 94 +#define A55_TRU_INTR3 95 +#define O_PDS_TX0_ARM_IRQ_0 96 +#define O_PDS_TX0_ARM_IRQ_1 97 +#define O_PDS_TX0_ARM_IRQ_2 98 +#define O_PDS_TX0_ARM_IRQ_3 99 +#define O_PDS_TX0_ARM_IRQ_4 100 +#define O_PDS_TX0_ARM_IRQ_5 101 +#define O_PDS_TX0_ARM_IRQ_6 102 +#define O_PDS_TX0_ARM_IRQ_7 103 +#define O_PDS_TX1_ARM_IRQ_0 104 +#define O_PDS_TX1_ARM_IRQ_1 105 +#define O_PDS_TX1_ARM_IRQ_2 106 +#define O_PDS_TX1_ARM_IRQ_3 107 +#define O_PDS_TX1_ARM_IRQ_4 108 +#define O_PDS_TX1_ARM_IRQ_5 109 +#define O_PDS_TX1_ARM_IRQ_6 110 +#define O_PDS_TX1_ARM_IRQ_7 111 +#define O_PDS_TX2_ARM_IRQ_0 112 +#define O_PDS_TX2_ARM_IRQ_1 113 +#define O_PDS_TX2_ARM_IRQ_2 114 +#define O_PDS_TX2_ARM_IRQ_3 115 +#define O_PDS_TX2_ARM_IRQ_4 116 +#define O_PDS_TX2_ARM_IRQ_5 117 +#define O_PDS_TX2_ARM_IRQ_6 118 +#define O_PDS_TX2_ARM_IRQ_7 119 +#define O_PDS_TX3_ARM_IRQ_0 120 +#define O_PDS_TX3_ARM_IRQ_1 121 +#define O_PDS_TX3_ARM_IRQ_2 122 +#define O_PDS_TX3_ARM_IRQ_3 123 +#define O_PDS_TX3_ARM_IRQ_4 124 +#define O_PDS_TX3_ARM_IRQ_5 125 +#define O_PDS_TX3_ARM_IRQ_6 126 +#define O_PDS_TX3_ARM_IRQ_7 127 +#define RFFE_2_INT_SYNC 128 +#define RFFE_3_INT_SYNC 129 +#define W_SD_INTR_PIPED 130 +#define W_SD_WAKEUP_INTR_PIPED 131 +#define PIMC2_EXT_IRQ_0 132 +#define PIMC2_EXT_IRQ_1 133 +#define PIMC3_EXT_IRQ_0 134 +#define PIMC3_EXT_IRQ_1 135 +#define ANT_CAL_INTRPT 136 +#define DDR_EVENT_COUNT_OVERFLOW_OR_HW_EN_EXPIRY_INTR 137 +#define O_DFI_INTERNAL_ERR_INTR 138 +#define O_DFI_PHYUPD_ERR_INTR 139 +#define O_DFI_ALERT_ERR_INTR 140 +#define O_ECC_AP_ERR_INTR 141 +#define O_ECC_AP_ERR_INTR_FAULT 142 +#define ECC_CORRECTED_ERR_INTR 143 +#define ECC_CORRECTED_ERR_INTR_FAULT 144 +#define O_ECC_UNCORRECTED_ERR_INTR 145 +#define O_ECC_UNCORRECTED_ERR_INTR_FAULT 146 +#define SBR_DONE_INTR 147 +#define O_DWC_DDRPHY_INT_N 148 +#define I2C_IRQ_S2F_PIPED_0 149 +#define I2C_IRQ_S2F_PIPED_1 150 +#define I2C_IRQ_S2F_PIPED_2 151 +#define I2C_IRQ_S2F_PIPED_3 152 +#define I2C_IRQ_S2F_PIPED_4 153 +#define I2C_IRQ_S2F_PIPED_5 154 +#define I2C_IRQ_S2F_PIPED_6 155 +#define I2C_IRQ_S2F_PIPED_7 156 +#define MACSEC_IRQ_0 157 +#define MACSEC_IRQ_1 158 +#define RADIO_CONTROL_INTRPT 159 +#define O_PDS_RX0_ARM_IRQ_0 160 +#define O_PDS_RX0_ARM_IRQ_1 161 +#define O_PDS_RX0_ARM_IRQ_2 162 +#define O_PDS_RX0_ARM_IRQ_3 163 +#define O_PDS_RX0_ARM_IRQ_4 164 +#define O_PDS_RX0_ARM_IRQ_5 165 +#define O_PDS_RX0_ARM_IRQ_6 166 +#define O_PDS_RX0_ARM_IRQ_7 167 +#define O_PDS_RX1_ARM_IRQ_0 168 +#define O_PDS_RX1_ARM_IRQ_1 169 +#define O_PDS_RX1_ARM_IRQ_2 170 +#define O_PDS_RX1_ARM_IRQ_3 171 +#define O_PDS_RX1_ARM_IRQ_4 172 +#define O_PDS_RX1_ARM_IRQ_5 173 +#define O_PDS_RX1_ARM_IRQ_6 174 +#define O_PDS_RX1_ARM_IRQ_7 175 +#define O_PDS_RX2_ARM_IRQ_0 176 +#define O_PDS_RX2_ARM_IRQ_1 177 +#define O_PDS_RX2_ARM_IRQ_2 178 +#define O_PDS_RX2_ARM_IRQ_3 179 +#define O_PDS_RX2_ARM_IRQ_4 180 +#define O_PDS_RX2_ARM_IRQ_5 181 +#define O_PDS_RX2_ARM_IRQ_6 182 +#define O_PDS_RX2_ARM_IRQ_7 183 +#define O_PDS_RX3_ARM_IRQ_0 184 +#define O_PDS_RX3_ARM_IRQ_1 185 +#define O_PDS_RX3_ARM_IRQ_2 186 +#define O_PDS_RX3_ARM_IRQ_3 187 +#define O_PDS_RX3_ARM_IRQ_4 188 +#define O_PDS_RX3_ARM_IRQ_5 189 +#define O_PDS_RX3_ARM_IRQ_6 190 +#define O_PDS_RX3_ARM_IRQ_7 191 +#define NFAULTIRQ_0 192 +#define NFAULTIRQ_1 193 +#define NFAULTIRQ_2 194 +#define NFAULTIRQ_3 195 +#define NFAULTIRQ_4 196 +#define C2C_SWU_INTR 197 +#define ETH_IRQ_TX_TIMESTAMP_0 198 +#define ETH_IRQ_TX_TIMESTAMP_1 199 +#define MMI_SWU_INTR 200 +#define TOD_IRQ 201 +#define A55_PERI_SWU_INTR_PIPED 203 +#define NERRIRQ_0 204 +#define NERRIRQ_1 205 +#define NERRIRQ_2 206 +#define NERRIRQ_3 207 +#define NERRIRQ_4 208 +#define TELE_TS_OVERFLOW_INTERRUPT 209 +#define NDMA_DMA_ERR_INTR_GATED_0 210 +#define NDMA_DMA_ERR_INTR_GATED_1 211 +#define NDMA_DMA_ERR_INTR_GATED_2 212 +#define NDMA_DMA_ERR_INTR_GATED_3 213 +#define NDMA_DMA_DONE_INTR_GATED_0 214 +#define NDMA_DMA_DONE_INTR_GATED_1 215 +#define NDMA_DMA_DONE_INTR_GATED_2 216 +#define NDMA_DMA_DONE_INTR_GATED_3 217 +#define NDMA_STATUS_DMA_ERR_INTR_GATED_0 218 +#define NDMA_STATUS_DMA_ERR_INTR_GATED_1 219 +#define DEBUG_DDE_ERR_INTR_0 220 +#define DEBUG_DDE_ERR_INTR_1 221 +#define NDMA_STATUS_DMA_DONE_INTR_GATED_0 222 +#define NDMA_STATUS_DMA_DONE_INTR_GATED_1 223 +#define O_PDS_ORX0_ARM_IRQ_0 224 +#define O_PDS_ORX0_ARM_IRQ_1 225 +#define O_PDS_ORX0_ARM_IRQ_2 226 +#define O_PDS_ORX0_ARM_IRQ_3 227 +#define O_PDS_ORX0_ARM_IRQ_4 228 +#define O_PDS_ORX0_ARM_IRQ_5 229 +#define O_PDS_ORX0_ARM_IRQ_6 230 +#define O_PDS_ORX0_ARM_IRQ_7 231 +#define DEBUG_DDE_DONE_INTR_0 232 +#define DEBUG_DDE_DONE_INTR_1 233 +#define IRQ_SPI_QUAD_RX_DDEERR_PIPED 234 +#define IRQ_SPI_QUAD_TX_DDEERR_PIPED 235 +#define IRQ_SPI_QUAD_TX_PIPED 236 +#define IRQ_SPI_QUAD_RX_PIPED 237 +#define IRQ_SPI_QUAD_ERR_PIPED 238 +#define IRQ_SPI_QUAD_STAT_PIPED 239 +#define I_STREAM_PROC_INTERRUPT_ARM_0 240 +#define I_STREAM_PROC_INTERRUPT_ARM_1 241 +#define I_STREAM_PROC_INTERRUPT_ARM_2 242 +#define I_STREAM_PROC_INTERRUPT_ARM_3 243 +#define I_STREAM_PROC_INTERRUPT_ARM_4 244 +#define I_STREAM_PROC_INTERRUPT_ARM_5 245 +#define I_STREAM_PROC_INTERRUPT_ARM_6 246 +#define I_STREAM_PROC_INTERRUPT_ARM_7 247 +#define I_PDS_TX0_INTR_IRQ_1 248 +#define I_PDS_TX1_INTR_IRQ_1 249 +#define I_PDS_TX2_INTR_IRQ_1 250 +#define I_PDS_TX3_INTR_IRQ_1 251 +#define NDMA_RX_STATUS_INTR_GATED_0 252 +#define NDMA_RX_STATUS_INTR_GATED_1 253 +#define NDMA_RX_ERR_INTR_GATED_0 254 +#define NDMA_RX_ERR_INTR_GATED_1 255 +#define TX0_DFE_IRQ_0 256 +#define TX0_DFE_IRQ_1 257 +#define TX0_DFE_IRQ_2 258 +#define TX0_DFE_IRQ_3 259 +#define TX0_DFE_IRQ_4 260 +#define TX0_DFE_IRQ_5 261 +#define TX0_DFE_IRQ_6 262 +#define TX0_DFE_IRQ_7 263 +#define TX0_DFE_IRQ_8 264 +#define TX1_DFE_IRQ_0 265 +#define TX1_DFE_IRQ_1 266 +#define TX1_DFE_IRQ_2 267 +#define TX1_DFE_IRQ_3 268 +#define TX1_DFE_IRQ_4 269 +#define TX1_DFE_IRQ_5 270 +#define TX1_DFE_IRQ_6 271 +#define TX1_DFE_IRQ_7 272 +#define TX1_DFE_IRQ_8 273 +#define EAST_RFPLL_PLL_LOCKED_SYNC 274 +#define WEST_RFPLL_PLL_LOCKED_SYNC 275 +#define CLKPLL_PLL_LOCKED_SYNC 276 +#define DDR_CL_SWU_INTR 278 +#define DDR_DL_SWU_INTR 279 +#define INJECT_DBG_ERROR 280 +#define INJECT_DBG_STAT 281 +#define I_M4_ARM_AHB_ERROR_INDICATION_0 282 +#define I_M4_ARM_AHB_ERROR_INDICATION_1 283 +#define DEBUG_DDE_DONE_INTR_2 284 +#define DEBUG_DDE_ERR_INTR_2 285 +#define ANTENNA_CAL_DDE_ERR_INTR_0 286 +#define ANTENNA_CAL_DDE_DONE_INTR_0 287 +#define MDMA_CH0_DONE_INTR_4 288 +#define MDMA_CH0_ERR_INTR_4 289 +#define MDMA_CH1_DONE_INTR_4 290 +#define MDMA_CH1_ERR_INTR_4 291 +#define MDMA_CH0_DONE_INTR_5 292 +#define MDMA_CH0_ERR_INTR_5 293 +#define MDMA_CH1_DONE_INTR_5 294 +#define MDMA_CH1_ERR_INTR_5 295 +#define C2C_NON_CRIT_INTR 296 +#define C2C_CRIT_INTR 297 +#define EMAC_1G_SBD_INTR_PIPED 298 +#define EMAC_1G_SBD_PERCH_TX_INTR_PIPED 299 +#define EMAC_1G_SBD_PERCH_RX_INTR_PIPED 300 +#define CLOCK_STATUS_IN_0 301 +#define CLOCK_STATUS_IN_1 302 +#define SPI_REG_MAIN_STREAMPROC_ERROR_STATUS 303 +#define RS_TO_A55_GIC_TRU_0 304 +#define RS_TO_A55_GIC_TRU_1 305 +#define RS_TO_A55_GIC_TRU_2 306 +#define RS_TO_A55_GIC_TRU_3 307 +#define RS_TO_A55_GIC_TRU_4 308 +#define RS_TO_A55_GIC_TRU_5 309 +#define RS_TO_A55_GIC_TRU_6 310 +#define RS_TO_A55_GIC_TRU_7 311 +#define RS_TO_A55_GIC_TRU_8 312 +#define RS_TO_A55_GIC_TRU_9 313 +#define RS_TO_A55_GIC_TRU_10 314 +#define RS_TO_A55_GIC_TRU_11 315 +#define RS_TO_A55_GIC_TRU_12 316 +#define RS_TO_A55_GIC_TRU_13 317 +#define RS_TO_A55_GIC_TRU_14 318 +#define RS_TO_A55_GIC_TRU_15 319 +#define RS_TO_A55_GIC_TRU_16 320 +#define RS_TO_A55_GIC_TRU_17 321 +#define RS_TO_A55_GIC_TRU_18 322 +#define RS_TO_A55_GIC_TRU_19 323 +#define RS_TO_A55_GIC_TRU_20 324 +#define RS_TO_A55_GIC_TRU_21 325 +#define RS_TO_A55_GIC_TRU_22 326 +#define RS_TO_A55_GIC_TRU_23 327 +#define RS_TO_A55_GIC_TRU_24 328 +#define RS_TO_A55_GIC_TRU_25 329 +#define RS_TO_A55_GIC_TRU_26 330 +#define RS_TO_A55_GIC_TRU_27 331 +#define RS_TO_A55_GIC_TRU_28 332 +#define RS_TO_A55_GIC_TRU_29 333 +#define RS_TO_A55_GIC_TRU_30 334 +#define RS_TO_A55_GIC_TRU_31 335 +#define O_PDS_ORX0_INTR_IRQ_3 336 +#define IRQ_SPI_1_RX_DDEERR_PIPED 337 +#define IRQ_SPI_1_TX_DDEERR_PIPED 338 +#define IRQ_SPI_1_TX_PIPED 339 +#define IRQ_SPI_1_RX_PIPED 340 +#define IRQ_SPI_1_ERR_PIPED 341 +#define IRQ_SPI_1_STAT_PIPED 342 +#define IRQ_SPI_2_RX_DDEERR_PIPED 343 +#define IRQ_SPI_2_TX_DDEERR_PIPED 344 +#define IRQ_SPI_2_TX_PIPED 345 +#define IRQ_SPI_2_RX_PIPED 346 +#define IRQ_SPI_2_ERR_PIPED 347 +#define IRQ_SPI_2_STAT_PIPED 348 +#define IRQ_SPI_3_RX_DDEERR_PIPED 349 +#define IRQ_SPI_3_TX_DDEERR_PIPED 350 +#define IRQ_SPI_3_TX_PIPED 351 +#define IRQ_SPI_3_RX_PIPED 352 +#define IRQ_SPI_3_ERR_PIPED 353 +#define IRQ_SPI_3_STAT_PIPED 354 +#define IRQ_SPI_4_RX_DDEERR_PIPED 355 +#define IRQ_SPI_4_TX_DDEERR_PIPED 356 +#define IRQ_SPI_4_TX_PIPED 357 +#define IRQ_SPI_4_RX_PIPED 358 +#define IRQ_SPI_4_ERR_PIPED 359 +#define IRQ_SPI_4_STAT_PIPED 360 +#define IRQ_SPI_5_RX_DDEERR_PIPED 361 +#define IRQ_SPI_5_TX_DDEERR_PIPED 362 +#define IRQ_SPI_5_TX_PIPED 363 +#define IRQ_SPI_5_RX_PIPED 364 +#define IRQ_SPI_5_ERR_PIPED 365 +#define IRQ_SPI_5_STAT_PIPED 366 +#define IRQ_SPI_6_RX_DDEERR_PIPED 367 +#define IRQ_SPI_6_TX_DDEERR_PIPED 368 +#define IRQ_SPI_6_TX_PIPED 369 +#define IRQ_SPI_6_RX_PIPED 370 +#define IRQ_SPI_6_ERR_PIPED 371 +#define IRQ_SPI_6_STAT_PIPED 372 +#define W_INTR_PIPED 373 +#define W_WAKEUP_INTR_PIPED 374 +#define GNSS_INTERRUPT 375 +#define NDMA_TX_STATUS_INTR_GATED_0 376 +#define NDMA_TX_STATUS_INTR_GATED_1 377 +#define NDMA_TX_ERR_INTR_GATED_0 378 +#define NDMA_TX_ERR_INTR_GATED_1 379 +#define GP_INTERRUPT_SYNC_0 380 +#define GP_INTERRUPT_SYNC_1 381 +#define SPU_IRQ0 382 +#define SPU_IRQ1 383 +#define SPU_IRQ2 384 +#define SPU_IRQ3 385 +#define SPU_IRQ4 386 +#define TE_HREQ_ACK_IRQ_PIPED 387 +#define TE_ERESP_RDY_IRQ_PIPED 388 +#define TE_FAULT_GP_INTR_PIPED 389 +#define TE_H_HINF_IRQ_PIPED 390 +#define O_DL_ANT_CAL_CAP_DONE_INTRPT 391 +#define O_DL_ANT_CAL_DDE_DONE_INTRPT 392 +#define O_UL_ANT_CAL_CAP_DONE_INTRPT 393 +#define O_UL_ANT_CAL_DDE_DONE_INTRPT 394 +#define O_DL_ANT_CAL_DDE_ANT_DONE_INTRPT_0 395 +#define O_DL_ANT_CAL_DDE_ANT_DONE_INTRPT_1 396 +#define O_DL_ANT_CAL_DDE_ANT_DONE_INTRPT_2 397 +#define O_DL_ANT_CAL_DDE_ANT_DONE_INTRPT_3 398 +#define O_DL_ANT_CAL_DDE_ANT_DONE_INTRPT_4 399 +#define O_DL_ANT_CAL_DDE_ANT_DONE_INTRPT_5 400 +#define O_DL_ANT_CAL_DDE_ANT_DONE_INTRPT_6 401 +#define O_DL_ANT_CAL_DDE_ANT_DONE_INTRPT_7 402 +#define O_UL_ANT_CAL_DDE_ANT_DONE_INTRPT_0 403 +#define O_UL_ANT_CAL_DDE_ANT_DONE_INTRPT_1 404 +#define O_UL_ANT_CAL_DDE_ANT_DONE_INTRPT_2 405 +#define O_UL_ANT_CAL_DDE_ANT_DONE_INTRPT_3 406 +#define GPIO_TO_GIC_SYNC_24 407 +#define GPIO_TO_GIC_SYNC_25 408 +#define GPIO_TO_GIC_SYNC_26 409 +#define GPIO_TO_GIC_SYNC_27 410 +#define GPIO_TO_GIC_SYNC_28 411 +#define GPIO_TO_GIC_SYNC_29 412 +#define GPIO_TO_GIC_SYNC_30 413 +#define GPIO_TO_GIC_SYNC_31 414 +#define C2C_OUT_HW_INTERRUPT_16 415 /* L4_SCBINIT_INTR_0 */ +#define C2C_OUT_HW_INTERRUPT_17 416 /* L4_ECC_WRN_INTR_0 */ +#define C2C_OUT_HW_INTERRUPT_18 417 /* L4_ECC_ERR_INTR_0 */ +#define C2C_OUT_HW_INTERRUPT_19 418 /* L4_SCBINIT_INTR_1 */ +#define C2C_OUT_HW_INTERRUPT_20 419 /* L4_ECC_WRN_INTR_1 */ +#define C2C_OUT_HW_INTERRUPT_21 420 /* L4_ECC_ERR_INTR_1 */ +#define C2C_OUT_HW_INTERRUPT_22 421 /* L4_SCBINIT_INTR_2 */ +#define C2C_OUT_HW_INTERRUPT_23 422 /* L4_ECC_WRN_INTR_2 */ +#define C2C_OUT_HW_INTERRUPT_24 423 /* L4_ECC_ERR_INTR_2 */ +#define C2C_OUT_HW_INTERRUPT_25 424 /* MDMA_CH0_DONE_INTR_0 */ +#define C2C_OUT_HW_INTERRUPT_26 425 /* MDMA_CH0_ERR_INTR_0 */ +#define C2C_OUT_HW_INTERRUPT_27 426 /* MDMA_CH1_DONE_INTR_0 */ +#define C2C_OUT_HW_INTERRUPT_28 427 /* MDMA_CH1_ERR_INTR_0 */ +#define C2C_OUT_HW_INTERRUPT_29 428 /* MDMA_CH0_DONE_INTR_1 */ +#define C2C_OUT_HW_INTERRUPT_30 429 /* MDMA_CH0_ERR_INTR_1 */ +#define C2C_OUT_HW_INTERRUPT_31 430 /* MDMA_CH1_DONE_INTR_1 */ +#define C2C_OUT_HW_INTERRUPT_32 431 /* MDMA_CH1_ERR_INTR_1 */ +#define C2C_OUT_HW_INTERRUPT_33 432 /* MDMA_CH0_DONE_INTR_2 */ +#define C2C_OUT_HW_INTERRUPT_34 433 /* MDMA_CH0_ERR_INTR_2 */ +#define C2C_OUT_HW_INTERRUPT_35 434 /* MDMA_CH1_DONE_INTR_2 */ +#define C2C_OUT_HW_INTERRUPT_36 435 /* MDMA_CH1_ERR_INTR_2 */ +#define C2C_OUT_HW_INTERRUPT_37 436 /* MDMA_CH0_DONE_INTR_3 */ +#define C2C_OUT_HW_INTERRUPT_38 437 /* MDMA_CH0_ERR_INTR_3 */ +#define C2C_OUT_HW_INTERRUPT_39 438 /* MDMA_CH1_DONE_INTR_3 */ +#define C2C_OUT_HW_INTERRUPT_40 439 /* MDMA_CH1_ERR_INTR_3 */ +#define C2C_OUT_HW_INTERRUPT_41 440 /* A55_PERI_MAILBOX_INTERRUPT_PIPED_2 */ +#define C2C_OUT_HW_INTERRUPT_42 441 /* A55_PERI_MAILBOX_INTERRUPT_PIPED_3 */ +#define C2C_OUT_HW_INTERRUPT_43 442 /* CBDDE_DONE_INTR_0 */ +#define C2C_OUT_HW_INTERRUPT_44 443 /* CBDDE_ERR_INTR_0 */ +#define C2C_OUT_HW_INTERRUPT_45 444 /* RUE_IRQ */ +#define C2C_OUT_HW_INTERRUPT_46 445 /* OIF_IRQ */ +#define C2C_OUT_HW_INTERRUPT_47 446 /* CB_DONE_INTR_0 */ +#define C2C_OUT_HW_INTERRUPT_48 447 /* QSFP_INTERRUPT */ +#define C2C_OUT_HW_INTERRUPT_49 448 /* ALT_A55_AHB_ERROR_INDICATION_PIPED */ +#define C2C_OUT_HW_INTERRUPT_50 449 /* POSTED_HRESP_LVL_A55_PIPED */ +#define C2C_OUT_HW_INTERRUPT_51 450 /* TEMP_SENSOR_INT0 */ +#define C2C_OUT_HW_INTERRUPT_52 451 /* TEMP_SENSOR_INT1 */ +#define C2C_OUT_HW_INTERRUPT_53 452 /* RFFE_0_INT_SYNC */ +#define C2C_OUT_HW_INTERRUPT_54 453 /* RFFE_1_INT_SYNC */ +#define C2C_OUT_HW_INTERRUPT_55 454 /* GPIO_TO_GIC_SYNC_0 */ +#define C2C_OUT_HW_INTERRUPT_56 455 /* GPIO_TO_GIC_SYNC_1 */ +#define C2C_OUT_HW_INTERRUPT_57 456 /* GPIO_TO_GIC_SYNC_2 */ +#define C2C_OUT_HW_INTERRUPT_58 457 /* GPIO_TO_GIC_SYNC_3 */ +#define C2C_OUT_HW_INTERRUPT_59 458 /* GPIO_TO_GIC_SYNC_4 */ +#define C2C_OUT_HW_INTERRUPT_60 459 /* GPIO_TO_GIC_SYNC_5 */ +#define C2C_OUT_HW_INTERRUPT_61 460 /* GPIO_TO_GIC_SYNC_6 */ +#define C2C_OUT_HW_INTERRUPT_62 461 /* GPIO_TO_GIC_SYNC_7 */ +#define C2C_OUT_HW_INTERRUPT_63 462 /* GPIO_TO_GIC_SYNC_8 */ +#define C2C_OUT_HW_INTERRUPT_64 463 /* GPIO_TO_GIC_SYNC_9 */ +#define C2C_OUT_HW_INTERRUPT_65 464 /* GPIO_TO_GIC_SYNC_10 */ +#define C2C_OUT_HW_INTERRUPT_66 465 /* GPIO_TO_GIC_SYNC_11 */ +#define C2C_OUT_HW_INTERRUPT_67 466 /* GPIO_TO_GIC_SYNC_12 */ +#define C2C_OUT_HW_INTERRUPT_68 467 /* GPIO_TO_GIC_SYNC_13 */ +#define C2C_OUT_HW_INTERRUPT_69 468 /* GPIO_TO_GIC_SYNC_14 */ +#define C2C_OUT_HW_INTERRUPT_70 469 /* GPIO_TO_GIC_SYNC_15 */ +#define C2C_OUT_HW_INTERRUPT_71 470 /* GPIO_TO_GIC_SYNC_16 */ +#define C2C_OUT_HW_INTERRUPT_72 471 /* GPIO_TO_GIC_SYNC_17 */ +#define C2C_OUT_HW_INTERRUPT_73 472 /* GPIO_TO_GIC_SYNC_18 */ +#define C2C_OUT_HW_INTERRUPT_74 473 /* GPIO_TO_GIC_SYNC_19 */ +#define C2C_OUT_HW_INTERRUPT_75 474 /* GPIO_TO_GIC_SYNC_20 */ +#define C2C_OUT_HW_INTERRUPT_76 475 /* GPIO_TO_GIC_SYNC_21 */ +#define C2C_OUT_HW_INTERRUPT_77 476 /* GPIO_TO_GIC_SYNC_22 */ +#define C2C_OUT_HW_INTERRUPT_78 477 /* GPIO_TO_GIC_SYNC_23 */ +#define C2C_OUT_HW_INTERRUPT_79 478 /* PIMC0_EXT_IRQ_0 */ +#define C2C_OUT_HW_INTERRUPT_80 479 /* PIMC0_EXT_IRQ_1 */ +#define C2C_OUT_HW_INTERRUPT_81 480 /* PIMC1_EXT_IRQ_0 */ +#define C2C_OUT_HW_INTERRUPT_82 481 /* PIMC1_EXT_IRQ_1 */ +#define C2C_OUT_HW_INTERRUPT_83 482 /* A55_TRU_INTR0 */ +#define C2C_OUT_HW_INTERRUPT_84 483 /* A55_TRU_INTR1 */ +#define C2C_OUT_HW_INTERRUPT_85 484 /* A55_TRU_INTR2 */ +#define C2C_OUT_HW_INTERRUPT_86 485 /* A55_TRU_INTR3 */ +#define C2C_OUT_HW_INTERRUPT_87 486 /* RFFE_2_INT_SYNC */ +#define C2C_OUT_HW_INTERRUPT_88 487 /* RFFE_3_INT_SYNC */ +#define C2C_OUT_HW_INTERRUPT_89 488 /* PIMC2_EXT_IRQ_0 */ +#define C2C_OUT_HW_INTERRUPT_90 489 /* PIMC2_EXT_IRQ_1 */ +#define C2C_OUT_HW_INTERRUPT_91 490 /* PIMC3_EXT_IRQ_0 */ +#define C2C_OUT_HW_INTERRUPT_92 491 /* PIMC3_EXT_IRQ_1 */ +#define C2C_OUT_HW_INTERRUPT_93 492 /* ANT_CAL_INTRPT */ +#define C2C_OUT_HW_INTERRUPT_94 493 /* I2C_IRQ_S2F_PIPED_0 */ +#define C2C_OUT_HW_INTERRUPT_95 494 /* I2C_IRQ_S2F_PIPED_1 */ +#define C2C_OUT_HW_INTERRUPT_96 495 /* I2C_IRQ_S2F_PIPED_2 */ +#define C2C_OUT_HW_INTERRUPT_97 496 /* I2C_IRQ_S2F_PIPED_3 */ +#define C2C_OUT_HW_INTERRUPT_98 497 /* I2C_IRQ_S2F_PIPED_4 */ +#define C2C_OUT_HW_INTERRUPT_99 498 /* I2C_IRQ_S2F_PIPED_5 */ +#define C2C_OUT_HW_INTERRUPT_100 499 /* I2C_IRQ_S2F_PIPED_6 */ +#define C2C_OUT_HW_INTERRUPT_101 500 /* I2C_IRQ_S2F_PIPED_7 */ +#define PL011_UART_INTR_0 501 +#define PL011_UART_INTR_1 502 +#define PL011_UART_INTR_2 503 +#define PL011_UART_INTR_3 504 +#define SERDES_INTERRUPT_SYNC 505 +#define ETHPLL_LOCKED_SYNC 506 +#define ARM0_MEMORY_ECC_ERROR 507 +#define ARM1_MEMORY_ECC_ERROR 508 +#define ML_DPD_DONE_INTR_PIPED 509 +#define ML_DPD_ERR_INTR_PIPED 510 +#define C2C_OUT_HW_INTERRUPT_102 511 /* MACSEC_IRQ_0 */ +#define C2C_OUT_HW_INTERRUPT_103 512 /* MACSEC_IRQ_1 */ +#define C2C_OUT_HW_INTERRUPT_104 513 /* RADIO_CONTROL_INTRPT */ +#define C2C_OUT_HW_INTERRUPT_105 514 /* ETH_IRQ_TX_TIMESTAMP_0 */ +#define C2C_OUT_HW_INTERRUPT_106 515 /* ETH_IRQ_TX_TIMESTAMP_1 */ +#define C2C_OUT_HW_INTERRUPT_107 516 /* TELE_TS_OVERFLOW_INTERRUPT */ +#define C2C_OUT_HW_INTERRUPT_108 517 /* MS_DDE_ERR_INTR_GATED_0 */ +#define C2C_OUT_HW_INTERRUPT_109 518 /* MS_DDE_ERR_INTR_GATED_1 */ +#define C2C_OUT_HW_INTERRUPT_110 519 /* MS_DDE_ERR_INTR_GATED_2 */ +#define C2C_OUT_HW_INTERRUPT_111 520 /* MS_DDE_ERR_INTR_GATED_3 */ +#define C2C_OUT_HW_INTERRUPT_112 521 /* MS_DDE_DONE_INTR_GATED_0 */ +#define C2C_OUT_HW_INTERRUPT_113 522 /* MS_DDE_DONE_INTR_GATED_1 */ +#define C2C_OUT_HW_INTERRUPT_114 523 /* MS_DDE_DONE_INTR_GATED_2 */ +#define C2C_OUT_HW_INTERRUPT_115 524 /* MS_DDE_DONE_INTR_GATED_3 */ +#define C2C_OUT_HW_INTERRUPT_116 525 /* MS_STAT_DDE_ERR_INTR_GATED_0 */ +#define C2C_OUT_HW_INTERRUPT_117 526 /* MS_STAT_DDE_ERR_INTR_GATED_1 */ +#define C2C_OUT_HW_INTERRUPT_118 527 /* DEBUG_DDE_ERR_INTR_0 */ +#define C2C_OUT_HW_INTERRUPT_119 528 /* DEBUG_DDE_ERR_INTR_1 */ +#define C2C_OUT_HW_INTERRUPT_120 529 /* MS_STAT_DDE_DONE_INTR_GATED_0 */ +#define C2C_OUT_HW_INTERRUPT_121 530 /* MS_STAT_DDE_DONE_INTR_GATED_1 */ +#define TZCINT_DDR 531 +#define TZCINT_L4_0 532 +#define TZCINT_L4_1 533 +#define TZCINT_L4_2 534 +#define TX_ORX_MAP_CHANGE 535 +#define PIMC_DDE_DONE_INTR_0 536 +#define PIMC_DDE_ERR_INTR_0 537 +#define GPINT_INTERRUPT_SECONDARY_TO_PRIMARY 538 +#define RTC_INT 539 +#define C2C_OUT_HW_INTERRUPT_122 540 /* DEBUG_DDE_DONE_INTR_0 */ +#define C2C_OUT_HW_INTERRUPT_123 541 /* DEBUG_DDE_DONE_INTR_1 */ +#define C2C_OUT_HW_INTERRUPT_124 542 /* I_STREAM_PROC_INTERRUPT_ARM_0 */ +#define C2C_OUT_HW_INTERRUPT_125 543 /* I_STREAM_PROC_INTERRUPT_ARM_1 */ +#define C2C_OUT_HW_INTERRUPT_126 544 /* I_STREAM_PROC_INTERRUPT_ARM_2 */ +#define C2C_OUT_HW_INTERRUPT_127 545 /* I_STREAM_PROC_INTERRUPT_ARM_3 */ +#define C2C_OUT_HW_INTERRUPT_128 546 /* I_STREAM_PROC_INTERRUPT_ARM_4 */ +#define C2C_OUT_HW_INTERRUPT_129 547 /* I_STREAM_PROC_INTERRUPT_ARM_5 */ +#define C2C_OUT_HW_INTERRUPT_130 548 /* I_STREAM_PROC_INTERRUPT_ARM_6 */ +#define C2C_OUT_HW_INTERRUPT_131 549 /* I_STREAM_PROC_INTERRUPT_ARM_7 */ +#define C2C_OUT_HW_INTERRUPT_132 550 /* MSP_RX_STATUS_INTR_GATED_0 */ +#define C2C_OUT_HW_INTERRUPT_133 551 /* MSP_RX_STATUS_INTR_GATED_1 */ +#define C2C_OUT_HW_INTERRUPT_134 552 /* MSP_RX_ERR_INTR_GATED_0 */ +#define C2C_OUT_HW_INTERRUPT_135 553 /* MSP_RX_ERR_INTR_GATED_1 */ +#define C2C_OUT_HW_INTERRUPT_136 554 /* TX0_DFE_IRQ_0 */ +#define C2C_OUT_HW_INTERRUPT_137 555 /* TX0_DFE_IRQ_1 */ +#define C2C_OUT_HW_INTERRUPT_138 556 /* TX0_DFE_IRQ_2 */ +#define C2C_OUT_HW_INTERRUPT_139 557 /* TX0_DFE_IRQ_3 */ +#define C2C_OUT_HW_INTERRUPT_140 558 /* TX0_DFE_IRQ_4 */ +#define C2C_OUT_HW_INTERRUPT_141 559 /* TX0_DFE_IRQ_5 */ +#define C2C_OUT_HW_INTERRUPT_142 560 /* TX0_DFE_IRQ_6 */ +#define C2C_OUT_HW_INTERRUPT_143 561 /* TX0_DFE_IRQ_7 */ +#define C2C_OUT_HW_INTERRUPT_144 562 /* TX0_DFE_IRQ_8 */ +#define C2C_OUT_HW_INTERRUPT_145 563 /* TX1_DFE_IRQ_0 */ +#define C2C_OUT_HW_INTERRUPT_146 564 /* TX1_DFE_IRQ_1 */ +#define C2C_OUT_HW_INTERRUPT_147 565 /* TX1_DFE_IRQ_2 */ +#define C2C_OUT_HW_INTERRUPT_148 566 /* TX1_DFE_IRQ_3 */ +#define C2C_OUT_HW_INTERRUPT_149 567 /* TX1_DFE_IRQ_4 */ +#define C2C_OUT_HW_INTERRUPT_150 568 /* TX1_DFE_IRQ_5 */ +#define C2C_OUT_HW_INTERRUPT_151 569 /* TX1_DFE_IRQ_6 */ +#define C2C_OUT_HW_INTERRUPT_152 570 /* TX1_DFE_IRQ_7 */ +#define C2C_OUT_HW_INTERRUPT_153 571 /* TX1_DFE_IRQ_8 */ +#define C2C_OUT_HW_INTERRUPT_154 572 /* EAST_RFPLL_PLL_LOCKED_SYNC */ +#define C2C_OUT_HW_INTERRUPT_155 573 /* WEST_RFPLL_PLL_LOCKED_SYNC */ +#define C2C_OUT_HW_INTERRUPT_156 574 /* CLKPLL_PLL_LOCKED_SYNC */ +#define C2C_OUT_HW_INTERRUPT_157 575 /* INJECT_DBG_ERROR */ +#define C2C_OUT_HW_INTERRUPT_158 576 /* INJECT_DBG_STAT */ +#define C2C_OUT_HW_INTERRUPT_159 577 /* DEBUG_DDE_DONE_INTR_2 */ +#define C2C_OUT_HW_INTERRUPT_160 578 /* DEBUG_DDE_ERR_INTR_2 */ +#define C2C_OUT_HW_INTERRUPT_161 579 /* ANTENNA_CAL_DDE_ERR_INTR_0 */ +#define C2C_OUT_HW_INTERRUPT_162 580 /* ANTENNA_CAL_DDE_DONE_INTR_0 */ +#define C2C_OUT_HW_INTERRUPT_163 581 /* MDMA_CH0_DONE_INTR_4 */ +#define C2C_OUT_HW_INTERRUPT_164 582 /* MDMA_CH0_ERR_INTR_4 */ +#define C2C_OUT_HW_INTERRUPT_165 583 /* MDMA_CH1_DONE_INTR_4 */ +#define C2C_OUT_HW_INTERRUPT_166 584 /* MDMA_CH1_ERR_INTR_4 */ +#define C2C_OUT_HW_INTERRUPT_167 585 /* MDMA_CH0_DONE_INTR_5 */ +#define C2C_OUT_HW_INTERRUPT_168 586 /* MDMA_CH0_ERR_INTR_5 */ +#define C2C_OUT_HW_INTERRUPT_169 587 /* MDMA_CH1_DONE_INTR_5 */ +#define C2C_OUT_HW_INTERRUPT_170 588 /* MDMA_CH1_ERR_INTR_5 */ +#define C2C_OUT_HW_INTERRUPT_171 589 /* C2C_NON_CRIT_INTR */ +#define C2C_OUT_HW_INTERRUPT_172 590 /* EMAC_1G_SBD_INTR_PIPED */ +#define C2C_OUT_HW_INTERRUPT_173 591 /* EMAC_1G_SBD_PERCH_TX_INTR_PIPED */ +#define C2C_OUT_HW_INTERRUPT_174 592 /* EMAC_1G_SBD_PERCH_RX_INTR_PIPED */ +#define C2C_OUT_HW_INTERRUPT_175 593 /* SPI_REG_MAIN_STREAMPROC_ERROR_STATUS */ +#define C2C_OUT_HW_INTERRUPT_176 594 /* RS_TO_A55_GIC_TRU_0 */ +#define C2C_OUT_HW_INTERRUPT_177 595 /* RS_TO_A55_GIC_TRU_1 */ +#define C2C_OUT_HW_INTERRUPT_178 596 /* RS_TO_A55_GIC_TRU_2 */ +#define C2C_OUT_HW_INTERRUPT_179 597 /* RS_TO_A55_GIC_TRU_3 */ +#define C2C_OUT_HW_INTERRUPT_180 598 /* RS_TO_A55_GIC_TRU_4 */ +#define C2C_OUT_HW_INTERRUPT_181 599 /* RS_TO_A55_GIC_TRU_5 */ +#define C2C_OUT_HW_INTERRUPT_182 600 /* RS_TO_A55_GIC_TRU_6 */ +#define C2C_OUT_HW_INTERRUPT_183 601 /* RS_TO_A55_GIC_TRU_7 */ +#define C2C_OUT_HW_INTERRUPT_184 602 /* RS_TO_A55_GIC_TRU_8 */ +#define C2C_OUT_HW_INTERRUPT_185 603 /* RS_TO_A55_GIC_TRU_9 */ +#define C2C_OUT_HW_INTERRUPT_186 604 /* RS_TO_A55_GIC_TRU_10 */ +#define C2C_OUT_HW_INTERRUPT_187 605 /* RS_TO_A55_GIC_TRU_11 */ +#define C2C_OUT_HW_INTERRUPT_188 606 /* RS_TO_A55_GIC_TRU_12 */ +#define C2C_OUT_HW_INTERRUPT_189 607 /* RS_TO_A55_GIC_TRU_13 */ +#define C2C_OUT_HW_INTERRUPT_190 608 /* RS_TO_A55_GIC_TRU_14 */ +#define C2C_OUT_HW_INTERRUPT_191 609 /* RS_TO_A55_GIC_TRU_15 */ +#define C2C_OUT_HW_INTERRUPT_192 610 /* O_PDS_ORX0_INTR_IRQ_3 */ +#define C2C_OUT_HW_INTERRUPT_193 611 /* MSP_TX_STATUS_INTR_GATED_0 */ +#define C2C_OUT_HW_INTERRUPT_194 612 /* MSP_TX_STATUS_INTR_GATED_1 */ +#define C2C_OUT_HW_INTERRUPT_195 613 /* MSP_TX_ERR_INTR_GATED_0 */ +#define C2C_OUT_HW_INTERRUPT_196 614 /* MSP_TX_ERR_INTR_GATED_1 */ +#define C2C_OUT_HW_INTERRUPT_197 615 /* GP_INTERRUPT_SYNC_1 */ +#define C2C_OUT_HW_INTERRUPT_198 616 /* SPU_IRQ0 */ +#define C2C_OUT_HW_INTERRUPT_199 617 /* SPU_IRQ1 */ +#define C2C_OUT_HW_INTERRUPT_200 618 /* SPU_IRQ2 */ +#define C2C_OUT_HW_INTERRUPT_201 619 /* SPU_IRQ3 */ +#define C2C_OUT_HW_INTERRUPT_202 620 /* SPU_IRQ4 */ +#define C2C_OUT_HW_INTERRUPT_203 621 /* TE_HREQ_ACK_IRQ_PIPED */ +#define C2C_OUT_HW_INTERRUPT_204 622 /* TE_ERESP_RDY_IRQ_PIPED */ +#define C2C_OUT_HW_INTERRUPT_205 623 /* TE_FAULT_GP_INTR_PIPED */ +#define C2C_OUT_HW_INTERRUPT_206 624 /* TE_H_HINF_IRQ_PIPED */ +#define C2C_OUT_HW_INTERRUPT_207 625 /* O_DL_ANT_CAL_CAP_DONE_INTRPT */ +#define C2C_OUT_HW_INTERRUPT_208 626 /* O_DL_ANT_CAL_DDE_DONE_INTRPT */ +#define C2C_OUT_HW_INTERRUPT_209 627 /* O_UL_ANT_CAL_CAP_DONE_INTRPT */ +#define C2C_OUT_HW_INTERRUPT_210 628 /* O_UL_ANT_CAL_DDE_DONE_INTRPT */ +#define C2C_OUT_HW_INTERRUPT_211 629 /* O_DL_ANT_CAL_DDE_ANT_DONE_INTRPT_0 */ +#define C2C_OUT_HW_INTERRUPT_212 630 /* O_DL_ANT_CAL_DDE_ANT_DONE_INTRPT_1 */ +#define C2C_OUT_HW_INTERRUPT_213 631 /* O_DL_ANT_CAL_DDE_ANT_DONE_INTRPT_2 */ +#define C2C_OUT_HW_INTERRUPT_214 632 /* O_DL_ANT_CAL_DDE_ANT_DONE_INTRPT_3 */ +#define C2C_OUT_HW_INTERRUPT_215 633 /* O_DL_ANT_CAL_DDE_ANT_DONE_INTRPT_4 */ +#define C2C_OUT_HW_INTERRUPT_216 634 /* O_DL_ANT_CAL_DDE_ANT_DONE_INTRPT_5 */ +#define C2C_OUT_HW_INTERRUPT_217 635 /* O_DL_ANT_CAL_DDE_ANT_DONE_INTRPT_6 */ +#define C2C_OUT_HW_INTERRUPT_218 636 /* O_DL_ANT_CAL_DDE_ANT_DONE_INTRPT_7 */ +#define C2C_OUT_HW_INTERRUPT_219 637 /* O_UL_ANT_CAL_DDE_ANT_DONE_INTRPT_0 */ +#define C2C_OUT_HW_INTERRUPT_220 638 /* O_UL_ANT_CAL_DDE_ANT_DONE_INTRPT_1 */ +#define C2C_OUT_HW_INTERRUPT_221 639 /* O_UL_ANT_CAL_DDE_ANT_DONE_INTRPT_2 */ +#define C2C_OUT_HW_INTERRUPT_222 640 /* O_UL_ANT_CAL_DDE_ANT_DONE_INTRPT_3 */ +#define C2C_OUT_HW_INTERRUPT_223 641 /* GPIO_TO_GIC_SYNC_24 */ +#define C2C_OUT_HW_INTERRUPT_224 642 /* GPIO_TO_GIC_SYNC_25 */ +#define C2C_OUT_HW_INTERRUPT_225 643 /* GPIO_TO_GIC_SYNC_26 */ +#define C2C_OUT_HW_INTERRUPT_226 644 /* GPIO_TO_GIC_SYNC_27 */ +#define C2C_OUT_HW_INTERRUPT_227 645 /* GPIO_TO_GIC_SYNC_28 */ +#define C2C_OUT_HW_INTERRUPT_228 646 /* GPIO_TO_GIC_SYNC_29 */ +#define C2C_OUT_HW_INTERRUPT_229 647 /* GPIO_TO_GIC_SYNC_30 */ +#define C2C_OUT_HW_INTERRUPT_230 648 /* GPIO_TO_GIC_SYNC_31 */ +#define C2C_OUT_HW_INTERRUPT_231 649 /* ETHPLL_LOCKED_SYNC */ +#define C2C_OUT_HW_INTERRUPT_232 650 /* ARM0_MEMORY_ECC_ERROR */ +#define C2C_OUT_HW_INTERRUPT_233 651 /* ARM1_MEMORY_ECC_ERROR */ +#define C2C_OUT_HW_INTERRUPT_234 652 /* ML_DPD_DONE_INTR_PIPED */ +#define C2C_OUT_HW_INTERRUPT_235 653 /* ML_DPD_ERR_INTR_PIPED */ +#define C2C_OUT_HW_INTERRUPT_236 654 /* TZCINT_DDR */ +#define C2C_OUT_HW_INTERRUPT_237 655 /* TZCINT_L4_0 */ +#define C2C_OUT_HW_INTERRUPT_238 656 /* TZCINT_L4_1 */ +#define C2C_OUT_HW_INTERRUPT_239 657 /* TZCINT_L4_2 */ +#define C2C_OUT_HW_INTERRUPT_240 658 /* PIMC_DDE_DONE_INTR_0 */ +#define C2C_OUT_HW_INTERRUPT_241 659 /* PIMC_DDE_ERR_INTR_0 */ +#define C2C_OUT_HW_INTERRUPT_242 660 /* V_UART_INTR_0_1 */ +#define C2C_OUT_HW_INTERRUPT_243 661 /* V_UART_INTR_1_1 */ +#define C2C_OUT_HW_INTERRUPT_244 662 /* I_ALT_M4_AHB_ERROR_INDICATION */ +#define C2C_OUT_HW_INTERRUPT_245 663 /* TX2_DFE_IRQ_0 */ +#define C2C_OUT_HW_INTERRUPT_246 664 /* TX2_DFE_IRQ_1 */ +#define C2C_OUT_HW_INTERRUPT_247 665 /* TX2_DFE_IRQ_2 */ +#define C2C_OUT_HW_INTERRUPT_248 666 /* TX2_DFE_IRQ_3 */ +#define C2C_OUT_HW_INTERRUPT_249 667 /* TX2_DFE_IRQ_4 */ +#define C2C_OUT_HW_INTERRUPT_250 668 /* TX2_DFE_IRQ_5 */ +#define C2C_OUT_HW_INTERRUPT_251 669 /* TX2_DFE_IRQ_6 */ +#define C2C_OUT_HW_INTERRUPT_252 670 /* TX2_DFE_IRQ_7 */ +#define C2C_OUT_HW_INTERRUPT_253 671 /* TX2_DFE_IRQ_8 */ +#define C2C_OUT_HW_INTERRUPT_254 672 /* TX3_DFE_IRQ_0 */ +#define C2C_OUT_HW_INTERRUPT_255 673 /* TX3_DFE_IRQ_1 */ +#define C2C_OUT_HW_INTERRUPT_256 674 /* TX3_DFE_IRQ_2 */ +#define C2C_OUT_HW_INTERRUPT_257 675 /* TX3_DFE_IRQ_3 */ +#define C2C_OUT_HW_INTERRUPT_258 676 /* TX3_DFE_IRQ_4 */ +#define C2C_OUT_HW_INTERRUPT_259 677 /* TX3_DFE_IRQ_5 */ +#define C2C_OUT_HW_INTERRUPT_260 678 /* TX3_DFE_IRQ_6 */ +#define C2C_OUT_HW_INTERRUPT_261 679 /* TX3_DFE_IRQ_7 */ +#define C2C_OUT_HW_INTERRUPT_262 680 /* TX3_DFE_IRQ_8 */ +#define C2C_OUT_HW_INTERRUPT_263 681 /* O_CDDC_RSSI_READY_TXRX_0_IRQ_0,O_CDDC_RSSI_READY_TXRX_1_IRQ_0,O_CDDC_RSSI_READY_TXRX_2_IRQ_0,O_CDDC_RSSI_READY_TXRX_3_IRQ_0 */ +#define C2C_OUT_HW_INTERRUPT_264 682 /* O_CDDC_RSSI_READY_TXRX_0_IRQ_1,O_CDDC_RSSI_READY_TXRX_1_IRQ_1,O_CDDC_RSSI_READY_TXRX_2_IRQ_1,O_CDDC_RSSI_READY_TXRX_3_IRQ_1 */ +#define C2C_OUT_HW_INTERRUPT_265 683 /* O_CDDC_RSSI_READY_TXRX_0_IRQ_2,O_CDDC_RSSI_READY_TXRX_1_IRQ_2,O_CDDC_RSSI_READY_TXRX_2_IRQ_2,O_CDDC_RSSI_READY_TXRX_3_IRQ_2 */ +#define C2C_OUT_HW_INTERRUPT_266 684 /* O_CDDC_RSSI_READY_TXRX_0_IRQ_3,O_CDDC_RSSI_READY_TXRX_1_IRQ_3,O_CDDC_RSSI_READY_TXRX_2_IRQ_3,O_CDDC_RSSI_READY_TXRX_3_IRQ_3 */ +#define C2C_OUT_HW_INTERRUPT_267 685 /* O_CDDC_RSSI_READY_TXRX_0_IRQ_4,O_CDDC_RSSI_READY_TXRX_1_IRQ_4,O_CDDC_RSSI_READY_TXRX_2_IRQ_4,O_CDDC_RSSI_READY_TXRX_3_IRQ_4 */ +#define C2C_OUT_HW_INTERRUPT_268 686 /* O_CDDC_RSSI_READY_TXRX_0_IRQ_5,O_CDDC_RSSI_READY_TXRX_1_IRQ_5,O_CDDC_RSSI_READY_TXRX_2_IRQ_5,O_CDDC_RSSI_READY_TXRX_3_IRQ_5 */ +#define C2C_OUT_HW_INTERRUPT_269 687 /* O_CDDC_RSSI_READY_TXRX_0_IRQ_6,O_CDDC_RSSI_READY_TXRX_1_IRQ_6,O_CDDC_RSSI_READY_TXRX_2_IRQ_6,O_CDDC_RSSI_READY_TXRX_3_IRQ_6 */ +#define C2C_OUT_HW_INTERRUPT_270 688 /* O_CDDC_RSSI_READY_TXRX_0_IRQ_7,O_CDDC_RSSI_READY_TXRX_1_IRQ_7,O_CDDC_RSSI_READY_TXRX_2_IRQ_7,O_CDDC_RSSI_READY_TXRX_3_IRQ_7 */ +#define C2C_OUT_HW_INTERRUPT_271 689 /* I_APD_HIGH_TXRX_0,I_APD_HIGH_TXRX_1,I_APD_HIGH_TXRX_2,I_APD_HIGH_TXRX_3 */ +#define V_UART_INTR_0_0 690 +#define V_UART_INTR_0_1 691 +#define V_UART_INTR_1_1 692 +#define TX2_DFE_IRQ_0 693 +#define TX2_DFE_IRQ_1 694 +#define TX2_DFE_IRQ_2 695 +#define TX2_DFE_IRQ_3 696 +#define TX2_DFE_IRQ_4 697 +#define TX2_DFE_IRQ_5 698 +#define TX2_DFE_IRQ_6 699 +#define TX2_DFE_IRQ_7 700 +#define TX2_DFE_IRQ_8 701 +#define TX3_DFE_IRQ_0 702 +#define TX3_DFE_IRQ_1 703 +#define TX3_DFE_IRQ_2 704 +#define TX3_DFE_IRQ_3 705 +#define TX3_DFE_IRQ_4 706 +#define TX3_DFE_IRQ_5 707 +#define TX3_DFE_IRQ_6 708 +#define TX3_DFE_IRQ_7 709 +#define TX3_DFE_IRQ_8 710 +#define C2C_OUT_HW_INTERRUPT_272 711 /* I_APD_LOW_TXRX_0,I_APD_LOW_TXRX_1,I_APD_LOW_TXRX_2,I_APD_LOW_TXRX_3 */ +#define C2C_OUT_HW_INTERRUPT_273 712 /* I_HB2_HIGH_TXRX_0,I_HB2_HIGH_TXRX_1,I_HB2_HIGH_TXRX_2,I_HB2_HIGH_TXRX_3 */ +#define C2C_OUT_HW_INTERRUPT_274 713 /* I_HB2_LOW_TXRX_0,I_HB2_LOW_TXRX_1,I_HB2_LOW_TXRX_2,I_HB2_LOW_TXRX_3 */ +#define C2C_OUT_HW_INTERRUPT_275 714 /* CAPTURE_DBG_ERROR_0 */ +#define C2C_OUT_HW_INTERRUPT_276 715 /* CAPTURE_DBG_STAT_0 */ +#define C2C_OUT_HW_INTERRUPT_277 716 /* CAPTURE_DBG_ERROR_1 */ +#define C2C_OUT_HW_INTERRUPT_278 717 /* CAPTURE_DBG_STAT_1 */ +#define C2C_OUT_HW_INTERRUPT_279 718 /* ETH_IRQ_TX_TIMESTAMP_FIFO_FULL_0 */ +#define C2C_OUT_HW_INTERRUPT_280 719 /* ETH_IRQ_TX_TIMESTAMP_FIFO_FULL_1 */ +#define C2C_OUT_HW_INTERRUPT_281 720 /* O_CDUC_TSSI_READY_TXRX_0_IRQ_0,O_CDUC_TSSI_READY_TXRX_1_IRQ_0,O_CDUC_TSSI_READY_TXRX_2_IRQ_0,O_CDUC_TSSI_READY_TXRX_3_IRQ_0 */ +#define C2C_OUT_HW_INTERRUPT_282 721 /* O_CDUC_TSSI_READY_TXRX_0_IRQ_1,O_CDUC_TSSI_READY_TXRX_1_IRQ_1,O_CDUC_TSSI_READY_TXRX_2_IRQ_1,O_CDUC_TSSI_READY_TXRX_3_IRQ_1 */ +#define C2C_OUT_HW_INTERRUPT_283 722 /* O_CDUC_TSSI_READY_TXRX_0_IRQ_2,O_CDUC_TSSI_READY_TXRX_1_IRQ_2,O_CDUC_TSSI_READY_TXRX_2_IRQ_2,O_CDUC_TSSI_READY_TXRX_3_IRQ_2 */ +#define C2C_OUT_HW_INTERRUPT_284 723 /* O_CDUC_TSSI_READY_TXRX_0_IRQ_3,O_CDUC_TSSI_READY_TXRX_1_IRQ_3,O_CDUC_TSSI_READY_TXRX_2_IRQ_3,O_CDUC_TSSI_READY_TXRX_3_IRQ_3 */ +#define C2C_OUT_HW_INTERRUPT_285 724 /* O_CDUC_TSSI_READY_TXRX_0_IRQ_4,O_CDUC_TSSI_READY_TXRX_1_IRQ_4,O_CDUC_TSSI_READY_TXRX_2_IRQ_4,O_CDUC_TSSI_READY_TXRX_3_IRQ_4 */ +#define C2C_OUT_HW_INTERRUPT_286 725 /* O_CDUC_TSSI_READY_TXRX_0_IRQ_5,O_CDUC_TSSI_READY_TXRX_1_IRQ_5,O_CDUC_TSSI_READY_TXRX_2_IRQ_5,O_CDUC_TSSI_READY_TXRX_3_IRQ_5 */ +#define C2C_OUT_HW_INTERRUPT_287 726 /* O_CDUC_TSSI_READY_TXRX_0_IRQ_6,O_CDUC_TSSI_READY_TXRX_1_IRQ_6,O_CDUC_TSSI_READY_TXRX_2_IRQ_6,O_CDUC_TSSI_READY_TXRX_3_IRQ_6 */ +#define C2C_OUT_HW_INTERRUPT_288 727 /* O_CDUC_TSSI_READY_TXRX_0_IRQ_7,O_CDUC_TSSI_READY_TXRX_1_IRQ_7,O_CDUC_TSSI_READY_TXRX_2_IRQ_7,O_CDUC_TSSI_READY_TXRX_3_IRQ_7 */ +#define C2C_OUT_HW_INTERRUPT_289 728 /* RC_DYN_GLBL_CNTRL_INTRPT */ +#define C2C_OUT_HW_INTERRUPT_290 729 /* TX0_DFE_IRQ_9 */ +#define C2C_OUT_HW_INTERRUPT_291 730 /* TX0_DFE_IRQ_10 */ +#define C2C_OUT_HW_INTERRUPT_292 731 /* TX0_DFE_IRQ_11 */ +#define C2C_OUT_HW_INTERRUPT_293 732 /* TX0_DFE_IRQ_12 */ +#define C2C_OUT_HW_INTERRUPT_294 733 /* TX1_DFE_IRQ_9 */ +#define C2C_OUT_HW_INTERRUPT_295 734 /* TX1_DFE_IRQ_10 */ +#define C2C_OUT_HW_INTERRUPT_296 735 /* TX1_DFE_IRQ_11 */ +#define C2C_OUT_HW_INTERRUPT_297 736 /* TX1_DFE_IRQ_12 */ +#define C2C_OUT_HW_INTERRUPT_298 737 /* TX2_DFE_IRQ_9 */ +#define C2C_OUT_HW_INTERRUPT_299 738 /* TX2_DFE_IRQ_10 */ +#define C2C_OUT_HW_INTERRUPT_300 739 /* TX2_DFE_IRQ_11 */ +#define C2C_OUT_HW_INTERRUPT_301 740 /* TX2_DFE_IRQ_12 */ +#define C2C_OUT_HW_INTERRUPT_302 741 /* TX3_DFE_IRQ_9 */ +#define C2C_OUT_HW_INTERRUPT_303 742 /* TX3_DFE_IRQ_10 */ +#define C2C_OUT_HW_INTERRUPT_304 743 /* TX3_DFE_IRQ_11 */ +#define C2C_OUT_HW_INTERRUPT_305 744 /* TX3_DFE_IRQ_12 */ +#define C2C_OUT_HW_INTERRUPT_306 745 /* RC_DYN_CNTRL0_INTRPT */ +#define C2C_OUT_HW_INTERRUPT_307 746 /* RC_DYN_CNTRL1_INTRPT */ +#define C2C_OUT_HW_INTERRUPT_308 747 /* RC_DYN_CNTRL2_INTRPT */ +#define C2C_OUT_HW_INTERRUPT_309 748 /* RC_DYN_CNTRL1_INTRPT */ +#define C2C_OUT_HW_INTERRUPT_310 749 /* Not connected */ +#define C2C_OUT_HW_INTERRUPT_311 750 /* Not connected */ +#define C2C_OUT_HW_INTERRUPT_312 751 /* Not connected */ +#define C2C_OUT_HW_INTERRUPT_313 752 /* Not connected */ +#define C2C_OUT_HW_INTERRUPT_314 753 /* Not connected */ +#define C2C_OUT_HW_INTERRUPT_315 754 /* Not connected */ +#define C2C_OUT_HW_INTERRUPT_316 755 /* Not connected */ +#define C2C_OUT_HW_INTERRUPT_317 756 /* Not connected */ +#define C2C_OUT_HW_INTERRUPT_318 757 /* Not connected */ +#define C2C_OUT_HW_INTERRUPT_319 758 /* Not connected */ +#define O_CDDC_RSSI_READY_TXRX_0_IRQ_0 759 +#define O_CDDC_RSSI_READY_TXRX_1_IRQ_0 759 +#define O_CDDC_RSSI_READY_TXRX_2_IRQ_0 759 +#define O_CDDC_RSSI_READY_TXRX_3_IRQ_0 759 +#define O_CDDC_RSSI_READY_TXRX_0_IRQ_1 760 +#define O_CDDC_RSSI_READY_TXRX_1_IRQ_1 760 +#define O_CDDC_RSSI_READY_TXRX_2_IRQ_1 760 +#define O_CDDC_RSSI_READY_TXRX_3_IRQ_1 760 +#define O_CDDC_RSSI_READY_TXRX_0_IRQ_2 761 +#define O_CDDC_RSSI_READY_TXRX_1_IRQ_2 761 +#define O_CDDC_RSSI_READY_TXRX_2_IRQ_2 761 +#define O_CDDC_RSSI_READY_TXRX_3_IRQ_2 761 +#define O_CDDC_RSSI_READY_TXRX_0_IRQ_3 762 +#define O_CDDC_RSSI_READY_TXRX_1_IRQ_3 762 +#define O_CDDC_RSSI_READY_TXRX_2_IRQ_3 762 +#define O_CDDC_RSSI_READY_TXRX_3_IRQ_3 762 +#define O_CDDC_RSSI_READY_TXRX_0_IRQ_4 763 +#define O_CDDC_RSSI_READY_TXRX_1_IRQ_4 763 +#define O_CDDC_RSSI_READY_TXRX_2_IRQ_4 763 +#define O_CDDC_RSSI_READY_TXRX_3_IRQ_4 763 +#define O_CDDC_RSSI_READY_TXRX_0_IRQ_5 764 +#define O_CDDC_RSSI_READY_TXRX_1_IRQ_5 764 +#define O_CDDC_RSSI_READY_TXRX_2_IRQ_5 764 +#define O_CDDC_RSSI_READY_TXRX_3_IRQ_5 764 +#define O_CDDC_RSSI_READY_TXRX_0_IRQ_6 765 +#define O_CDDC_RSSI_READY_TXRX_1_IRQ_6 765 +#define O_CDDC_RSSI_READY_TXRX_2_IRQ_6 765 +#define O_CDDC_RSSI_READY_TXRX_3_IRQ_6 765 +#define O_CDDC_RSSI_READY_TXRX_0_IRQ_7 766 +#define O_CDDC_RSSI_READY_TXRX_1_IRQ_7 766 +#define O_CDDC_RSSI_READY_TXRX_2_IRQ_7 766 +#define O_CDDC_RSSI_READY_TXRX_3_IRQ_7 766 +#define I_APD_HIGH_TXRX_0 767 +#define I_APD_HIGH_TXRX_1 767 +#define I_APD_HIGH_TXRX_2 767 +#define I_APD_HIGH_TXRX_3 767 +#define I_APD_LOW_TXRX_0 768 +#define I_APD_LOW_TXRX_1 768 +#define I_APD_LOW_TXRX_2 768 +#define I_APD_LOW_TXRX_3 768 +#define I_HB2_HIGH_TXRX_0 769 +#define I_HB2_HIGH_TXRX_1 769 +#define I_HB2_HIGH_TXRX_2 769 +#define I_HB2_HIGH_TXRX_3 769 +#define I_HB2_LOW_TXRX_0 770 +#define I_HB2_LOW_TXRX_1 770 +#define I_HB2_LOW_TXRX_2 770 +#define I_HB2_LOW_TXRX_3 770 +#define CAPTURE_DBG_ERROR_0 771 +#define CAPTURE_DBG_STAT_0 772 +#define CAPTURE_DBG_ERROR_1 773 +#define CAPTURE_DBG_STAT_1 774 +#define A55_PERI_MDMA_CH0_DONE_INTR_PIPED_0 775 +#define A55_PERI_MDMA_CH0_DONE_INTR_PIPED_1 776 +#define A55_PERI_MDMA_CH0_ERR_INTR_PIPED_0 777 +#define A55_PERI_MDMA_CH0_ERR_INTR_PIPED_1 778 +#define A55_PERI_MDMA_CH1_DONE_INTR_PIPED_0 779 +#define A55_PERI_MDMA_CH1_DONE_INTR_PIPED_1 780 +#define A55_PERI_MDMA_CH1_ERR_INTR_PIPED_0 781 +#define A55_PERI_MDMA_CH1_ERR_INTR_PIPED_1 782 +#define ETH_IRQ_MAC_RX_ERROR_0 783 +#define ETH_IRQ_MAC_RX_ERROR_1 784 +#define ETH_IRQ_MAC_TX_ERROR_0 785 +#define ETH_IRQ_MAC_TX_ERROR_1 786 +#define ETH_IRQ_PCS_RX_ERROR_0 787 +#define ETH_IRQ_PCS_RX_ERROR_1 788 +#define ETH_IRQ_TX_TIMESTAMP_FIFO_FULL_0 789 +#define ETH_IRQ_TX_TIMESTAMP_FIFO_FULL_1 790 +#define O_PDS_TX0_NPD_ARM_IRQ_0 791 +#define O_PDS_TX1_NPD_ARM_IRQ_0 791 +#define O_PDS_TX2_NPD_ARM_IRQ_0 791 +#define O_PDS_TX3_NPD_ARM_IRQ_0 791 +#define O_PDS_TX0_NPD_ARM_IRQ_1 792 +#define O_PDS_TX1_NPD_ARM_IRQ_1 792 +#define O_PDS_TX2_NPD_ARM_IRQ_1 792 +#define O_PDS_TX3_NPD_ARM_IRQ_1 792 +#define O_PDS_TX0_NPD_ARM_IRQ_2 793 +#define O_PDS_TX1_NPD_ARM_IRQ_2 793 +#define O_PDS_TX2_NPD_ARM_IRQ_2 793 +#define O_PDS_TX3_NPD_ARM_IRQ_2 793 +#define O_PDS_TX0_NPD_ARM_IRQ_3 794 +#define O_PDS_TX1_NPD_ARM_IRQ_3 794 +#define O_PDS_TX2_NPD_ARM_IRQ_3 794 +#define O_PDS_TX3_NPD_ARM_IRQ_3 794 +#define O_PDS_TX0_NPD_ARM_IRQ_4 795 +#define O_PDS_TX1_NPD_ARM_IRQ_4 795 +#define O_PDS_TX2_NPD_ARM_IRQ_4 795 +#define O_PDS_TX3_NPD_ARM_IRQ_4 795 +#define O_PDS_TX0_NPD_ARM_IRQ_5 796 +#define O_PDS_TX1_NPD_ARM_IRQ_5 796 +#define O_PDS_TX2_NPD_ARM_IRQ_5 796 +#define O_PDS_TX3_NPD_ARM_IRQ_5 796 +#define O_PDS_TX0_NPD_ARM_IRQ_6 797 +#define O_PDS_TX1_NPD_ARM_IRQ_6 797 +#define O_PDS_TX2_NPD_ARM_IRQ_6 797 +#define O_PDS_TX3_NPD_ARM_IRQ_6 797 +#define O_PDS_TX0_NPD_ARM_IRQ_7 798 +#define O_PDS_TX1_NPD_ARM_IRQ_7 798 +#define O_PDS_TX2_NPD_ARM_IRQ_7 798 +#define O_PDS_TX3_NPD_ARM_IRQ_7 798 +#define O_PDS_TX0_NPD_ARM_IRQ_8 799 +#define O_PDS_TX1_NPD_ARM_IRQ_8 799 +#define O_PDS_TX2_NPD_ARM_IRQ_8 799 +#define O_PDS_TX3_NPD_ARM_IRQ_8 799 +#define O_CDUC_TSSI_READY_TXRX_0_IRQ_0 800 +#define O_CDUC_TSSI_READY_TXRX_1_IRQ_0 800 +#define O_CDUC_TSSI_READY_TXRX_2_IRQ_0 800 +#define O_CDUC_TSSI_READY_TXRX_3_IRQ_0 800 +#define O_CDUC_TSSI_READY_TXRX_0_IRQ_1 801 +#define O_CDUC_TSSI_READY_TXRX_1_IRQ_1 801 +#define O_CDUC_TSSI_READY_TXRX_2_IRQ_1 801 +#define O_CDUC_TSSI_READY_TXRX_3_IRQ_1 801 +#define O_CDUC_TSSI_READY_TXRX_0_IRQ_2 802 +#define O_CDUC_TSSI_READY_TXRX_1_IRQ_2 802 +#define O_CDUC_TSSI_READY_TXRX_2_IRQ_2 802 +#define O_CDUC_TSSI_READY_TXRX_3_IRQ_2 802 +#define O_CDUC_TSSI_READY_TXRX_0_IRQ_3 803 +#define O_CDUC_TSSI_READY_TXRX_1_IRQ_3 803 +#define O_CDUC_TSSI_READY_TXRX_2_IRQ_3 803 +#define O_CDUC_TSSI_READY_TXRX_3_IRQ_3 803 +#define O_CDUC_TSSI_READY_TXRX_0_IRQ_4 804 +#define O_CDUC_TSSI_READY_TXRX_1_IRQ_4 804 +#define O_CDUC_TSSI_READY_TXRX_2_IRQ_4 804 +#define O_CDUC_TSSI_READY_TXRX_3_IRQ_4 804 +#define O_CDUC_TSSI_READY_TXRX_0_IRQ_5 805 +#define O_CDUC_TSSI_READY_TXRX_1_IRQ_5 805 +#define O_CDUC_TSSI_READY_TXRX_2_IRQ_5 805 +#define O_CDUC_TSSI_READY_TXRX_3_IRQ_5 805 +#define O_CDUC_TSSI_READY_TXRX_0_IRQ_6 806 +#define O_CDUC_TSSI_READY_TXRX_1_IRQ_6 806 +#define O_CDUC_TSSI_READY_TXRX_2_IRQ_6 806 +#define O_CDUC_TSSI_READY_TXRX_3_IRQ_6 806 +#define O_CDUC_TSSI_READY_TXRX_0_IRQ_7 807 +#define O_CDUC_TSSI_READY_TXRX_1_IRQ_7 807 +#define O_CDUC_TSSI_READY_TXRX_2_IRQ_7 807 +#define O_CDUC_TSSI_READY_TXRX_3_IRQ_7 807 +#define I_M4_ARM_WATCHDOG_TIMEOUT_0 808 +#define I_M4_ARM_WATCHDOG_TIMEOUT_1 809 +#define RC_DYN_GLBL_CNTRL_INTRPT 810 +#define TX0_DFE_IRQ_9 811 +#define TX0_DFE_IRQ_10 812 +#define TX0_DFE_IRQ_11 813 +#define TX0_DFE_IRQ_12 814 +#define TX1_DFE_IRQ_9 815 +#define TX1_DFE_IRQ_10 816 +#define TX1_DFE_IRQ_11 817 +#define TX1_DFE_IRQ_12 818 +#define TX2_DFE_IRQ_9 819 +#define TX2_DFE_IRQ_10 820 +#define TX2_DFE_IRQ_11 821 +#define TX2_DFE_IRQ_12 822 +#define TX3_DFE_IRQ_9 823 +#define TX3_DFE_IRQ_10 824 +#define TX3_DFE_IRQ_11 825 +#define TX3_DFE_IRQ_12 826 +#define I_PDS_TX0_INTR_IRQ_8 827 +#define I_PDS_TX1_INTR_IRQ_8 828 +#define I_PDS_TX2_INTR_IRQ_8 829 +#define I_PDS_TX3_INTR_IRQ_8 830 +#define I_PDS_TX0_INTR_IRQ_9 831 +#define I_PDS_TX1_INTR_IRQ_9 832 +#define I_PDS_TX2_INTR_IRQ_9 833 +#define I_PDS_TX3_INTR_IRQ_9 834 +#define RC_DYN_CNTRL0_INTRPT 835 +#define RC_DYN_CNTRL1_INTRPT 836 +#define RC_DYN_CNTRL2_INTRPT 837 +#define I_SBET_ARM_INTERRUPT 838 + +#endif /* __ADI_ADRV906X_IRQ_DEF_H__ */ diff --git a/arch/arm64/configs/adrv906x-eval_defconfig b/arch/arm64/configs/adrv906x-eval_defconfig new file mode 100644 index 00000000000000..b137286cc4d4cc --- /dev/null +++ b/arch/arm64/configs/adrv906x-eval_defconfig @@ -0,0 +1,295 @@ +CONFIG_LOCALVERSION="-yocto-standard" +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_NUMA_BALANCING=y +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_HUGETLB=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_NAMESPACES=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_ARCH_ADRV906X=y +CONFIG_ARM64_VA_BITS_48=y +CONFIG_SCHED_MC=y +CONFIG_SCHED_SMT=y +CONFIG_NUMA=y +CONFIG_ARM64_SW_TTBR0_PAN=y +CONFIG_RANDOMIZE_BASE=y +# CONFIG_EFI is not set +# CONFIG_SUSPEND is not set +CONFIG_PM=y +CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y +CONFIG_CPU_IDLE=y +CONFIG_ARM_CPUIDLE=y +CONFIG_ARM_PSCI_CPUIDLE=y +CONFIG_ARM_SDE_INTERFACE=y +# CONFIG_ARM_SMCCC_SOC_ID is not set +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_JUMP_LABEL=y +CONFIG_ARCH_MMAP_RND_BITS=33 +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_BLK_DEV_BSGLIB=y +CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_COREDUMP=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_PAGE_REPORTING=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_MEMORY_FAILURE=y +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_CMA=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_INET_DIAG is not set +CONFIG_NETFILTER=y +CONFIG_NF_TABLES=y +CONFIG_NF_TABLES_INET=y +CONFIG_VLAN_8021Q=y +CONFIG_NET_SCHED=y +CONFIG_NET_CLS_ACT=y +# CONFIG_WIRELESS is not set +CONFIG_FAILOVER=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_FW_LOADER_USER_HELPER=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +CONFIG_BRCMSTB_GISB_ARB=y +CONFIG_SIMPLE_PM_BUS=y +CONFIG_VEXPRESS_CONFIG=y +CONFIG_MTD=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_DATAFLASH=y +CONFIG_MTD_SST25L=y +CONFIG_MTD_SPI_NOR=y +# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set +CONFIG_OF_OVERLAY=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_ADI_TRU=y +CONFIG_SRAM=y +CONFIG_EEPROM_AT24=y +CONFIG_ADI_SRAM_MMAP=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_NETDEVICES=y +# CONFIG_MACSEC is not set +CONFIG_NET_ADRV906X=y +# CONFIG_NET_VENDOR_ALACRITECH is not set +# CONFIG_NET_VENDOR_AMAZON is not set +# CONFIG_NET_VENDOR_AMD is not set +# CONFIG_NET_VENDOR_AQUANTIA is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_VENDOR_AURORA is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CADENCE is not set +# CONFIG_NET_VENDOR_CAVIUM is not set +# CONFIG_NET_VENDOR_CORTINA is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_GOOGLE is not set +# CONFIG_NET_VENDOR_HISILICON is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_MICROSEMI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETRONOME is not set +# CONFIG_NET_VENDOR_NI is not set +# CONFIG_NET_VENDOR_PENSANDO is not set +# CONFIG_NET_VENDOR_QUALCOMM is not set +# CONFIG_NET_VENDOR_RENESAS is not set +# CONFIG_NET_VENDOR_ROCKER is not set +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SOLARFLARE is not set +CONFIG_SMC91X=y +CONFIG_SMSC911X=y +# CONFIG_NET_VENDOR_SOCIONEXT is not set +CONFIG_STMMAC_ETH=y +CONFIG_DWMAC_ADRV906X=y +# CONFIG_NET_VENDOR_SYNOPSYS is not set +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +# CONFIG_NET_VENDOR_XILINX is not set +CONFIG_SFP=y +CONFIG_MDIO_BITBANG=y +CONFIG_MDIO_BUS_MUX_MULTIPLEXER=y +CONFIG_MDIO_BUS_MUX_MMIOREG=y +CONFIG_PPP=y +CONFIG_PPP_ASYNC=y +# CONFIG_WLAN is not set +# CONFIG_INPUT is not set +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_LDISC_AUTOLOAD is not set +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_DEV_BUS=y +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_HISI_V2 is not set +# CONFIG_DEVMEM is not set +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y +CONFIG_I2C_ADI_TWI=y +CONFIG_SPI=y +CONFIG_SPI_ADI_V3=y +CONFIG_SPI_SPIDEV=y +CONFIG_PTP_1588_CLOCK_ADRV906X=y +CONFIG_PINCTRL=y +CONFIG_PINCTRL_SINGLE=y +CONFIG_PINCTRL_ADI=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_SENSORS_MAX6697=y +CONFIG_PMBUS=y +CONFIG_SENSORS_ADM1266=y +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y +CONFIG_THERMAL_EMULATION=y +CONFIG_WATCHDOG=y +CONFIG_ARM_SMC_WATCHDOG=y +# CONFIG_MFD_VEXPRESS_SYSREG is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_GPIO=y +CONFIG_REGULATOR_PWM=y +# CONFIG_USB_SUPPORT is not set +CONFIG_MMC=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_ADI_SYSTEMC=y +CONFIG_MMC_ARMMMCI=y +# CONFIG_MMC_STM32_SDMMC is not set +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_OF_ADI=y +CONFIG_MMC_CQHCI=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_PWM=y +CONFIG_LEDS_SYSCON=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_EDAC=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_DS1307=y +CONFIG_DMADEVICES=y +CONFIG_ADI_DMA=y +CONFIG_SYNC_FILE=y +CONFIG_UIO=y +CONFIG_UIO_PDRV_GENIRQ=y +# CONFIG_VIRTIO_MENU is not set +# CONFIG_VHOST_MENU is not set +CONFIG_HWSPINLOCK=y +CONFIG_ARM_SMMU=y +CONFIG_ARM_SMMU_V3=y +CONFIG_PM_DEVFREQ=y +CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y +CONFIG_IIO=y +CONFIG_IIO_BUFFER=y +CONFIG_IIO_TRIGGER=y +CONFIG_ADI_PWM_DAC=y +CONFIG_PWM=y +CONFIG_PHY_ADI_SDHCI=y +CONFIG_TEE=y +CONFIG_OPTEE=y +CONFIG_MUX_MMIO=y +CONFIG_EXT2_FS=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +CONFIG_QUOTA=y +CONFIG_AUTOFS4_FS=y +CONFIG_OVERLAY_FS=y +CONFIG_VFAT_FS=y +# CONFIG_PROC_PAGE_MONITOR is not set +CONFIG_TMPFS=y +CONFIG_TMPFS_XATTR=y +CONFIG_HUGETLBFS=y +CONFIG_CONFIGFS_FS=y +CONFIG_JFFS2_FS=y +CONFIG_PSTORE=y +CONFIG_NFS_FS=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_SECURITY=y +CONFIG_SECURITYFS=y +CONFIG_HARDENED_USERCOPY=y +# CONFIG_HARDENED_USERCOPY_FALLBACK is not set +CONFIG_CRYPTO_RSA=y +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_USER_API_HASH=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y +CONFIG_CRYPTO_USER_API_RNG=y +CONFIG_ASYMMETRIC_KEY_TYPE=y +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y +CONFIG_X509_CERTIFICATE_PARSER=y +CONFIG_PKCS7_MESSAGE_PARSER=y +CONFIG_SYSTEM_TRUSTED_KEYRING=y +CONFIG_PACKING=y +CONFIG_INDIRECT_PIO=y +CONFIG_CRC_T10DIF=y +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y +CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=32 +CONFIG_IRQ_POLL=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_WX=y +# CONFIG_SCHED_DEBUG is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_FTRACE is not set +CONFIG_MEMTEST=y diff --git a/drivers/Makefile b/drivers/Makefile index 45d1c3e630f754..016d4a2f3af5c4 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -114,6 +114,7 @@ obj-$(CONFIG_GAMEPORT) += input/gameport/ obj-$(CONFIG_INPUT) += input/ obj-$(CONFIG_RTC_LIB) += rtc/ obj-y += i2c/ i3c/ media/ +obj-$(CONFIG_TEE) += tee/ obj-$(CONFIG_PPS) += pps/ obj-y += ptp/ obj-$(CONFIG_W1) += w1/ @@ -181,7 +182,6 @@ obj-y += android/ obj-$(CONFIG_NVMEM) += nvmem/ obj-$(CONFIG_FPGA) += fpga/ obj-$(CONFIG_FSI) += fsi/ -obj-$(CONFIG_TEE) += tee/ obj-$(CONFIG_MULTIPLEXER) += mux/ obj-$(CONFIG_SIOX) += siox/ obj-$(CONFIG_GNSS) += gnss/ diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index d9ec1e69e42831..a9c8a648a3532d 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -733,6 +733,13 @@ config XILINX_ZYNQMP_DPDMA driver provides the dmaengine required by the DisplayPort subsystem display driver. +config ADI_DMA + tristate "adi dma support" + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + support ADI's distributed DMA engine + # driver files source "drivers/dma/amd/Kconfig" diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index ad6a03c052ec4a..d93a4cb6c21dc3 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_XGENE_DMA) += xgene-dma.o obj-$(CONFIG_ST_FDMA) += st_fdma.o obj-$(CONFIG_FSL_DPAA2_QDMA) += fsl-dpaa2-qdma/ obj-$(CONFIG_INTEL_LDMA) += lgm/ +obj-$(CONFIG_ADI_DMA) += adi-dma.o obj-y += amd/ obj-y += mediatek/ diff --git a/drivers/dma/adi-dma.c b/drivers/dma/adi-dma.c new file mode 100644 index 00000000000000..ddb97826e67859 --- /dev/null +++ b/drivers/dma/adi-dma.c @@ -0,0 +1,1447 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmaengine.h" +#include "adi-dma.h" + +#define ADI_MEMSET_SIZE (4 * sizeof(uint64_t)) + +struct adi_dma_hw { + int has_mdma; +}; + +struct adi_dma_filter_data { + u32 id; +}; + +/* + * register fields used to program dde engine + */ +struct adi_dde_descriptor { + uint32_t next; + uint32_t start; + uint32_t cfg; + uint32_t xcnt; + uint32_t xmod; + uint32_t ycnt; + uint32_t ymod; + struct list_head dde_desc_unit; + dma_addr_t dma_handle; +}; + +struct adi_dma_descriptor { + //dde descriptor used for register-based or descriptor based + //mode + struct adi_dde_descriptor dde_descriptor_src; + //destination descriptor config to support memory copy/set operations + struct adi_dde_descriptor dde_descriptor_dest; + + // additional bookkeeping + struct dma_async_tx_descriptor tx; + struct dmaengine_result result; + struct list_head node; + struct list_head cb_node; + + enum dma_transfer_direction direction; + + /* a cyclic descriptor will reuse itself, triggering callbacks as expected, + * and will not free itself when it finishes + */ + int cyclic; + + /* physical address of source location, in case of peripheral<->mem, the + * mem address is ALWAYS here and dest is unused. + */ + dma_addr_t src; + + /* physical address of destination, only used for MDMA */ + dma_addr_t dest; + + /* virtual address of memset buffer, used only with memset */ + uint64_t *memset; + bool scattergather_mode; + struct list_head dde_desc_list; +}; + +struct adi_dma_channel { + int id; + struct adi_dma *dma; + void __iomem *iosrc; + void __iomem *iodest; + int running; + int use_interrupts; + int src_irq; + int src_err_irq; + int dest_irq; + int dest_err_irq; + /* descriptor in flight */ + struct adi_dma_descriptor *current_desc; + /* descriptors to process */ + struct list_head pending; + /* descriptors to call callbacks on */ + struct list_head cb_pending; + struct dma_chan chan; + struct dma_slave_config config; + spinlock_t lock; + bool desc_mode; + bool ch_sync_disable; + int periph_intf_width; +}; + +struct adi_dma { + struct device *dev; + struct dma_device dma_device; + void __iomem *ioaddr; + const struct adi_dma_hw *hw_cfg; + spinlock_t lock; + struct dma_pool *dde_desc_pool; +}; + +static struct adi_dma_hw adi_peripheral_dma_data = { + .has_mdma = 0, +}; + +static struct adi_dma_hw adi_mdma_data = { + .has_mdma = 1, +}; + +static const struct of_device_id dma_dt_ids[] = { + { .compatible = "adi,dma-controller", .data = &adi_peripheral_dma_data }, + { .compatible = "adi,mdma-controller", .data = &adi_mdma_data }, + { } +}; +MODULE_DEVICE_TABLE(of, dma_dt_ids); + + +static void __adi_dma_enable_irqs(struct adi_dma_channel *); +static void __adi_dma_disable_irqs(struct adi_dma_channel *); +static void __adi_dma_clear_and_reset(struct adi_dma_channel *channel); + +static irqreturn_t adi_dma_handler(int irq, void *id); +static irqreturn_t adi_dma_error_handler(int irq, void *id); +static irqreturn_t adi_dma_thread_handler(int irq, void *id); +static void __adi_dma_process_descriptor(struct adi_dma_descriptor *desc); +static void __adi_dma_process_descriptor_list(struct adi_dma_descriptor *desc); +static void __adi_dma_free_slave_sg_desc_list(struct adi_dma_descriptor *desc); +static struct adi_dde_descriptor *adi_dma_alloc_dde_descriptor(struct adi_dma *dma); + +static int adi_dma_init_channel_interrupts(struct adi_dma *dma, struct device_node *node, + struct adi_dma_channel *channel) +{ + int irq; + int ret; + + irq = of_irq_get_byname(node, "complete"); + if (irq <= 0) { + dev_err(dma->dev, "Missing complete IRQ for channel %s\n", node->full_name); + return irq ? irq : -ENOENT; + } + + channel->src_irq = irq; + + ret = devm_request_threaded_irq(dma->dev, irq, adi_dma_handler, + adi_dma_thread_handler, 0, "dma controller irq", channel); + if (ret) { + dev_err(dma->dev, "Failed to request IRQ %d\n", ret); + return ret; + } + + irq = of_irq_get_byname(node, "error"); + if (irq <= 0) { + dev_err(dma->dev, "Missing error IRQ for channel %s\n", node->full_name); + return irq ? irq : -ENOENT; + } + + channel->src_err_irq = irq; + + ret = devm_request_threaded_irq(dma->dev, irq, adi_dma_error_handler, + adi_dma_thread_handler, 0, "dma controller error irq", channel); + if (ret) { + dev_err(dma->dev, "Failed to request IRQ %d\n", ret); + return ret; + } + + if (dma->hw_cfg->has_mdma) { + irq = of_irq_get_byname(node, "complete2"); + if (irq <= 0) { + dev_err(dma->dev, "Missing complete2 IRQ for channel %s\n", + node->full_name); + return irq ? irq : -ENOENT; + } + + channel->dest_irq = irq; + + ret = devm_request_threaded_irq(dma->dev, irq, adi_dma_handler, + adi_dma_thread_handler, 0, "dma controller irq", channel); + if (ret) { + dev_err(dma->dev, "Failed to request IRQ %d\n", ret); + return ret; + } + + irq = of_irq_get_byname(node, "error2"); + if (irq <= 0) { + dev_err(dma->dev, "Missing error2 IRQ for channel %s\n", node->full_name); + return irq ? irq : -ENOENT; + } + + channel->dest_err_irq = irq; + + ret = devm_request_threaded_irq(dma->dev, irq, adi_dma_error_handler, + adi_dma_thread_handler, 0, "dma controller error irq", channel); + if (ret) { + dev_err(dma->dev, "Failed to request IRQ %d\n", ret); + return ret; + } + } + + return 0; +} + +static int adi_dma_init_channel(struct adi_dma *dma, struct device_node *node, unsigned int start, unsigned int len) +{ + struct adi_dma_channel *channel; + int ret; + u32 offset; + u32 skip_int = 0; + + channel = devm_kzalloc(dma->dev, sizeof(*channel), GFP_KERNEL); + if (!channel) + return -ENOMEM; + + if (of_property_read_u32(node, "adi,id", &channel->id)) { + dev_err(dma->dev, "Missing adi,id for channel %s\n", node->full_name); + return -ENOENT; + } + + if (of_property_read_u32(node, "adi,src-offset", &offset)) { + dev_err(dma->dev, "Missing adi,src-offset for channel %s\n", + node->full_name); + return -ENOENT; + } + + channel->iosrc = devm_ioremap(dma->dev, start + offset, len); + + channel->dma = dma; + channel->current_desc = NULL; + spin_lock_init(&channel->lock); + INIT_LIST_HEAD(&channel->pending); + INIT_LIST_HEAD(&channel->cb_pending); + + channel->config = (struct dma_slave_config) { + .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .src_maxburst = 1, + .dst_maxburst = 1, + }; + + if (dma->hw_cfg->has_mdma) { + if (of_property_read_u32(node, "adi,dest-offset", &offset)) { + dev_err(dma->dev, "Missing adi,dest-offset for channel %s\n", + node->full_name); + return -ENOENT; + } + channel->iodest = dma->ioaddr + offset; + } + + of_property_read_u32(node, "adi,skip-interrupts", &skip_int); + channel->use_interrupts = !skip_int; + + if (channel->use_interrupts) { + ret = adi_dma_init_channel_interrupts(dma, node, channel); + if (ret) + return ret; + } + + channel->desc_mode = of_property_read_bool(node, "adi,dde-descriptor-mode"); + dev_info(dma->dev, "descriptor list mode is %d", channel->desc_mode); + /* + * create dde descriptors dma pool for coherent access + */ + if (!dma->dde_desc_pool) { + dma->dde_desc_pool = dma_pool_create("dde desc", dma->dev, + sizeof(struct adi_dde_descriptor), 4, 0); + if (!dma->dde_desc_pool) + return -ENOMEM; + } + channel->ch_sync_disable = of_property_read_bool(node, "adi,dde-sync-bit-disable"); + dev_info(dma->dev, "dde-sync-bit-disable is %d", channel->ch_sync_disable); + + if (of_property_read_u32(node, "periph-intf-width", &channel->periph_intf_width)) { + dev_info(dma->dev, "channel %s: peripheral interface data width is not fixed.\n", + node->full_name); + channel->periph_intf_width = -1; + } + + // start with interrupts disabled, enable them when transactions appear + channel->running = 1; + __adi_dma_disable_irqs(channel); + __adi_dma_clear_and_reset(channel); + + dma_cookie_init(&channel->chan); + channel->chan.device = &dma->dma_device; + list_add_tail(&channel->chan.device_node, &dma->dma_device.channels); + return 0; +} + +static struct adi_dma_descriptor *adi_dma_to_adi_desc(struct dma_async_tx_descriptor *tx) +{ + return container_of(tx, struct adi_dma_descriptor, tx); +} + +static struct adi_dma_channel *adi_dma_to_adi_channel(struct dma_chan *chan) +{ + return container_of(chan, struct adi_dma_channel, chan); +} + +static struct adi_dma_descriptor *adi_dma_alloc_descriptor(struct adi_dma *dma) +{ + struct adi_dma_descriptor *ret = NULL; + + ret = devm_kzalloc(dma->dev, sizeof(*ret), GFP_NOWAIT); + dev_dbg(dma->dev, "%s: new desc allocated %px\n", __func__, ret); + + return ret; +} + +static int adi_dma_desc_free(struct dma_async_tx_descriptor *tx) +{ + struct adi_dma_channel *adi_chan = adi_dma_to_adi_channel(tx->chan); + struct adi_dma_descriptor *desc = adi_dma_to_adi_desc(tx); + struct adi_dma *dma = adi_chan->dma; + + dev_dbg(dma->dev, "%s: free desc %px\n", __func__, desc); + + if (desc->memset) + dmam_free_coherent(dma->dev, ADI_MEMSET_SIZE, desc->memset, desc->src); + + devm_kfree(dma->dev, desc); + return 0; +} + +static struct adi_dde_descriptor *adi_dma_alloc_dde_descriptor(struct adi_dma *dma) +{ + struct adi_dde_descriptor *desc; + dma_addr_t dma_handle; + + desc = dma_pool_zalloc(dma->dde_desc_pool, GFP_KERNEL, &dma_handle); + desc->dma_handle = dma_handle; + return desc; +} + +/** + * Only used by MDMA for determining access sizes, don't use with dma that is + * attached directly to a peripheral + */ +static void adi_dma_get_txn_align(dma_addr_t src, dma_addr_t dst, size_t size, + u32 *conf, u32 *shift) +{ + if (dst % 32 == 0 && src % 32 == 0 && size % 32 == 0) { + *conf = WDSIZE_256; + *shift = 5; + } else if (dst % 16 == 0 && src % 16 == 0 && size % 16 == 0) { + *conf = WDSIZE_128; + *shift = 4; + } else if (dst % 8 == 0 && src % 8 == 0 && size % 8 == 0) { + *conf = WDSIZE_64; + *shift = 3; + } else if (dst % 4 == 0 && src % 4 == 0 && size % 4 == 0) { + *conf = WDSIZE_32; + *shift = 2; + } else if (dst % 2 == 0 && src % 2 == 0 && size % 2 == 0) { + *conf = WDSIZE_16; + *shift = 1; + } else { + *conf = WDSIZE_8; + *shift = 0; + } +} + +/** + * Only used with peripheral attached DMA and considers the burst characteristics + * of the peripheral in addition to the memory and transaction size to make sure + * that reads/writes work properly + */ +static void adi_dma_get_periph_align(struct adi_dma_channel *adi_chan, + enum dma_transfer_direction direction, dma_addr_t mem, size_t len, + u32 *conf, u32 *shift) +{ + struct dma_slave_config *cfg = &adi_chan->config; + u32 burst = 0; + int fixed_periph_width = adi_chan->periph_intf_width; + int psize; + + if (fixed_periph_width != -1) { + switch (fixed_periph_width) { + case 1: + psize = PSIZE_8; + break; + case 2: + psize = PSIZE_16; + break; + case 4: + psize = PSIZE_32; + break; + default: + case 8: + psize = PSIZE_64; + break; + } + } + if (direction == DMA_DEV_TO_MEM) + burst = cfg->src_maxburst * cfg->src_addr_width; + else + burst = cfg->dst_maxburst * cfg->src_addr_width; + + if (mem % 8 == 0 && len % 8 == 0 && burst >= 8) { + *conf = WDSIZE_64 | ((fixed_periph_width != -1) ? psize : PSIZE_64); + *shift = 3; + } else if (mem % 4 == 0 && len % 4 == 0 && burst >= 4) { + *conf = WDSIZE_32 | ((fixed_periph_width != -1) ? psize : PSIZE_32); + *shift = 2; + } else if (mem % 2 == 0 && len % 2 == 0 && burst >= 2) { + *conf = WDSIZE_16 | ((fixed_periph_width != -1) ? psize : PSIZE_16); + *shift = 1; + } else { + *conf = WDSIZE_8 | ((fixed_periph_width != -1) ? psize : PSIZE_8); + *shift = 0; + } +} + +static dma_cookie_t adi_dma_submit(struct dma_async_tx_descriptor *tx) +{ + struct adi_dma_descriptor *adi_desc = adi_dma_to_adi_desc(tx); + struct adi_dma_channel *adi_chan = adi_dma_to_adi_channel(tx->chan); + unsigned long flags; + dma_cookie_t cookie; + + dev_dbg(adi_chan->dma->dev, "%s: submit desc %px\n", __func__, adi_desc); + + spin_lock_irqsave(&adi_chan->lock, flags); + cookie = dma_cookie_assign(tx); + list_add_tail(&adi_desc->node, &adi_chan->pending); + spin_unlock_irqrestore(&adi_chan->lock, flags); + + dev_dbg(adi_chan->dma->dev, "%s: produced cookie %d\n", __func__, cookie); + return cookie; +} + +static void __adi_set_descriptor_list_config(void __iomem *ch_base, + struct adi_dde_descriptor *dde_desc) +{ + set_dma_start_addr(ch_base, dde_desc->start); + set_dma_x_count(ch_base, dde_desc->xcnt); + set_dma_x_modify(ch_base, dde_desc->xmod); + + /* + * set current desc config to descriptor list + * mode if next desc is available + */ + if (dde_desc->next) + set_dma_next_desc_addr(ch_base, dde_desc->dma_handle); +} + +/** + * process DDE descriptors in descriptor list mode + */ +static void __adi_dma_process_descriptor_list(struct adi_dma_descriptor *desc) +{ + struct adi_dma_channel *channel = adi_dma_to_adi_channel(desc->tx.chan); + struct adi_dma *dma = channel->dma; + struct adi_dde_descriptor *dde_desc_1, *dde_desc_2; + + dev_dbg(dma->dev, "%s: process desc at %px\n", __func__, desc); + + if (get_dma_curr_irqstat(channel->iosrc) & DMA_RUN) + dev_err(dma->dev, "processing a new descriptor while running\n"); + + dde_desc_1 = list_first_entry(&desc->dde_desc_list, + struct adi_dde_descriptor, dde_desc_unit); + __adi_set_descriptor_list_config(channel->iosrc, dde_desc_1); + + if (desc->direction == DMA_MEM_TO_MEM) { + dde_desc_1 = &desc->dde_descriptor_src; + dde_desc_2 = &desc->dde_descriptor_dest; + __adi_set_descriptor_list_config(channel->iosrc, dde_desc_1); + __adi_set_descriptor_list_config(channel->iodest, dde_desc_2); + } + channel->current_desc = desc; + + clear_dma_irqstat(channel->iosrc); + if (desc->direction == DMA_MEM_TO_MEM) + clear_dma_irqstat(channel->iodest); + + set_dma_config(channel->iosrc, dde_desc_1->cfg); + __adi_dma_enable_irqs(channel); +} + +/** + * Load descriptor information into hardware registers + * must be holding channel lock here + */ +static void __adi_dma_process_descriptor(struct adi_dma_descriptor *desc) +{ + struct adi_dma_channel *channel = adi_dma_to_adi_channel(desc->tx.chan); + struct adi_dma *dma = channel->dma; + struct adi_dde_descriptor *dde_desc; + + dev_dbg(dma->dev, "%s: process desc at %px", __func__, desc); + + if (get_dma_curr_irqstat(channel->iosrc) & DMA_RUN) + dev_err(dma->dev, "processing a new descriptor while running\n"); + + if (desc->scattergather_mode) { + if (list_empty(&desc->dde_desc_list)) { + dev_err(dma->dev, "%s , channel %d, dma desc contains empty dde descriptor list", __func__, channel->id); + return; + } + dde_desc = list_first_entry(&desc->dde_desc_list, struct adi_dde_descriptor, dde_desc_unit); + list_del(&dde_desc->dde_desc_unit); + } else { + dde_desc = &desc->dde_descriptor_src; + } + + channel->current_desc = desc; + desc->src = dde_desc->start; + + dev_dbg(dma->dev, "dma config: src = 0x%llx, dst = 0x%llx\n", desc->src, desc->dest); + dev_dbg(dma->dev, " xcount = %d, xmod = %d, cfg = 0x%x\n", dde_desc->xcnt, dde_desc->xmod, dde_desc->cfg); + + if (dde_desc->cfg & DMA2D) { + dev_dbg(dma->dev, " ycount = %d, ymod = %d\n", dde_desc->ycnt, dde_desc->ymod); + set_dma_y_count(channel->iosrc, dde_desc->ycnt); + set_dma_y_modify(channel->iosrc, dde_desc->ymod); + } + set_dma_start_addr(channel->iosrc, desc->src); + set_dma_x_count(channel->iosrc, dde_desc->xcnt); + + /* In memset mode, we use xmod = 0 to copy from the same address repeatedly, + * and xmod only applies to the destination location + */ + if (desc->memset) + set_dma_x_modify(channel->iosrc, 0); + else + set_dma_x_modify(channel->iosrc, dde_desc->xmod); + + clear_dma_irqstat(channel->iosrc); + set_dma_config(channel->iosrc, dde_desc->cfg); + + if (desc->direction == DMA_MEM_TO_MEM) { + u32 extra_config = WNR; + + if (dde_desc->cfg & DMA2D) { + set_dma_y_count(channel->iodest, dde_desc->ycnt); + set_dma_y_modify(channel->iodest, dde_desc->ymod); + extra_config |= DI_EN_Y; + } else { + extra_config |= DI_EN_X; + } + + dev_dbg(dma->dev, " extracfg = 0x%x\n", dde_desc->cfg | extra_config); + + set_dma_start_addr(channel->iodest, desc->dest); + set_dma_x_count(channel->iodest, dde_desc->xcnt); + set_dma_x_modify(channel->iodest, dde_desc->xmod); + clear_dma_irqstat(channel->iodest); + set_dma_config(channel->iodest, dde_desc->cfg | extra_config); + } + + if (desc->scattergather_mode) + dma_pool_free(dma->dde_desc_pool, dde_desc, dde_desc->dma_handle); + + // For first descriptor enable IRQs again + __adi_dma_enable_irqs(channel); +} + +/** + * must be holding channel lock here + */ +static void __adi_dma_issue_pending(struct adi_dma_channel *adi_chan) +{ + struct adi_dma_descriptor *desc; + + if (!adi_chan->current_desc) { + if (!list_empty(&adi_chan->pending)) { + desc = list_first_entry(&adi_chan->pending, struct adi_dma_descriptor, + node); + list_del(&desc->node); + if (adi_chan->desc_mode == true) + __adi_dma_process_descriptor_list(desc); + else + __adi_dma_process_descriptor(desc); + } else { + // Final descriptor ended, disable things + __adi_dma_disable_irqs(adi_chan); + } + } +} + +static void adi_dma_issue_pending(struct dma_chan *chan) +{ + struct adi_dma_channel *adi_chan = adi_dma_to_adi_channel(chan); + unsigned long flags; + + dev_dbg(adi_chan->dma->dev, "%s: run\n", __func__); + + spin_lock_irqsave(&adi_chan->lock, flags); + __adi_dma_issue_pending(adi_chan); + spin_unlock_irqrestore(&adi_chan->lock, flags); +} + +static enum dma_status adi_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct adi_dma_channel *adi_chan = adi_dma_to_adi_channel(chan); + struct adi_dma_descriptor *desc = adi_chan->current_desc; + u32 done, bytes, start; + enum dma_status ret; + struct adi_dde_descriptor *dde_desc = &desc->dde_descriptor_src; + struct list_head *curr; + struct list_head *tmp; + bool desc_done = true; + + ret = dma_cookie_status(chan, cookie, txstate); + if (ret == DMA_COMPLETE) + return ret; + + if (!desc) + return DMA_COMPLETE; + + if (desc->result.result != DMA_TRANS_NOERROR) + return DMA_ERROR; + + spin_lock(&adi_chan->lock); + txstate->residue = 0; + + //check sg based transfers + if (desc->scattergather_mode) { + if (list_empty(&desc->dde_desc_list)) { + spin_unlock(&adi_chan->lock); + return DMA_COMPLETE; + } + start = get_dma_start_addr(adi_chan->iosrc); + list_for_each_safe(curr, tmp, &desc->dde_desc_list) { + dde_desc = list_entry(curr, struct adi_dde_descriptor, dde_desc_unit); + if (adi_chan->desc_mode) { + if (dde_desc->start != start) + desc_done = false; + if (!desc_done) + txstate->residue += (dde_desc->xcnt * dde_desc->xmod); + } else { + txstate->residue += (dde_desc->xcnt * dde_desc->xmod); + } + } + } else { + // @todo this assumes ymod is one element, which is currently true in the only 2D case we support + done = get_dma_curr_addr(adi_chan->iosrc) - desc->src; + bytes = (dde_desc->xcnt * dde_desc->xmod); + if (dde_desc->cfg & DMA2D) + bytes = bytes * dde_desc->ycnt; + txstate->residue = bytes - done; + } + spin_unlock(&adi_chan->lock); + return DMA_IN_PROGRESS; +} + +static void __adi_dma_enable_irqs(struct adi_dma_channel *adi_chan) +{ + if (adi_chan->running) + return; + + adi_chan->running = 1; + + if (adi_chan->use_interrupts) { + enable_irq(adi_chan->src_irq); + enable_irq(adi_chan->src_err_irq); + + if (adi_chan->iodest) { + enable_irq(adi_chan->dest_irq); + enable_irq(adi_chan->dest_err_irq); + } + } +} + +static void __adi_dma_disable_irqs(struct adi_dma_channel *adi_chan) +{ + if (!adi_chan->running) + return; + + adi_chan->running = 0; + + if (adi_chan->use_interrupts) { + disable_irq_nosync(adi_chan->src_irq); + disable_irq_nosync(adi_chan->src_err_irq); + + if (adi_chan->iodest) { + disable_irq_nosync(adi_chan->dest_irq); + disable_irq_nosync(adi_chan->dest_err_irq); + } + } +} + +static void __adi_dma_enable(struct adi_dma_channel *adi_chan) +{ + u32 cfg; + + cfg = get_dma_config(adi_chan->iosrc); + set_dma_config(adi_chan->iosrc, cfg | DMAEN); + + if (adi_chan->iodest) { + cfg = get_dma_config(adi_chan->iodest); + set_dma_config(adi_chan->iodest, cfg | DMAEN); + } + + __adi_dma_enable_irqs(adi_chan); +} + +static void __adi_dma_disable(struct adi_dma_channel *adi_chan) +{ + u32 cfg; + + cfg = get_dma_config(adi_chan->iosrc); + set_dma_config(adi_chan->iosrc, cfg & ~DMAEN); + + if (adi_chan->iodest) { + cfg = get_dma_config(adi_chan->iodest); + set_dma_config(adi_chan->iodest, cfg & ~DMAEN); + } + + __adi_dma_disable_irqs(adi_chan); +} + +static int adi_dma_pause(struct dma_chan *chan) +{ + struct adi_dma_channel *adi_chan = adi_dma_to_adi_channel(chan); + unsigned long flags; + + spin_lock_irqsave(&adi_chan->lock, flags); + if (adi_chan->current_desc) + __adi_dma_disable(adi_chan); + spin_unlock_irqrestore(&adi_chan->lock, flags); + + return 0; +} + +static int adi_dma_resume(struct dma_chan *chan) +{ + struct adi_dma_channel *adi_chan = adi_dma_to_adi_channel(chan); + unsigned long flags; + + spin_lock_irqsave(&adi_chan->lock, flags); + if (adi_chan->current_desc) + __adi_dma_enable(adi_chan); + spin_unlock_irqrestore(&adi_chan->lock, flags); + + return 0; +} + +static int adi_dma_terminate_all(struct dma_chan *chan) +{ + struct adi_dma_channel *adi_chan = adi_dma_to_adi_channel(chan); + struct adi_dma_descriptor *desc; + unsigned long flags; + struct list_head *curr; + struct list_head *tmp; + + spin_lock_irqsave(&adi_chan->lock, flags); + + // Disable regardless of status to clear config that may have been modified + // externally (for example in bootrom during resume) + __adi_dma_clear_and_reset(adi_chan); + __adi_dma_disable_irqs(adi_chan); + + if (adi_chan->current_desc) { + desc = adi_chan->current_desc; + desc->tx.desc_free(&desc->tx); + adi_chan->current_desc = NULL; + } + + list_for_each_safe(curr, tmp, &adi_chan->pending) { + desc = list_entry(curr, struct adi_dma_descriptor, node); + list_del(curr); + desc->tx.desc_free(&desc->tx); + } + + list_for_each_safe(curr, tmp, &adi_chan->cb_pending) { + desc = list_entry(curr, struct adi_dma_descriptor, cb_node); + list_del(curr); + desc->tx.desc_free(&desc->tx); + } + + spin_unlock_irqrestore(&adi_chan->lock, flags); + + return 0; +} + +static void adi_dma_synchronize(struct dma_chan *chan) +{ + // terminate all doesn't sleep and also has nothing asynchronous to wait on +} + +static int adi_dma_slave_config(struct dma_chan *chan, + struct dma_slave_config *config) +{ + struct adi_dma_channel *adi_chan = adi_dma_to_adi_channel(chan); + + adi_chan->config = *config; + return 0; +} + +static void __adi_dma_clear_only(struct adi_dma_channel *channel) +{ + clear_dma_irqstat(channel->iosrc); + + if (channel->iodest) + clear_dma_irqstat(channel->iodest); +} + +static void __adi_dma_clear_and_reset(struct adi_dma_channel *channel) +{ + set_dma_config(channel->iosrc, 0); + clear_dma_irqstat(channel->iosrc); + + if (channel->iodest) { + set_dma_config(channel->iodest, 0); + clear_dma_irqstat(channel->iodest); + } +} + +static irqreturn_t __adi_dma_handler(struct adi_dma_channel *channel, + enum dmaengine_tx_result result) +{ + struct adi_dma_descriptor *desc; + u32 stat = 0; + u32 stat2 = 0; + irqreturn_t ret = IRQ_WAKE_THREAD; + + spin_lock(&channel->lock); + + stat = get_dma_curr_irqstat(channel->iosrc); + + dev_dbg(channel->dma->dev, "%s: got irqstat = 0x%x\n", __func__, stat); + + if (channel->iodest) { + stat2 = get_dma_curr_irqstat(channel->iodest); + dev_dbg(channel->dma->dev, "%s: got dest irqstat = 0x%x\n", __func__, stat); + } + + // If we're not running, clear interrupt status + if (!channel->running) { + __adi_dma_clear_and_reset(channel); + dev_err(channel->dma->dev, + "channel %d: received interrupt while not runnnig\n", channel->id); + ret = IRQ_HANDLED; + goto done; + } + + // DMA transaction still running, some peripherals will do this + // before the transaction is finished because they signal the DMA channel + // for more data on the same interrupt line + if (!(stat & DMA_DONE) && !(stat2 & DMA_DONE)) { + dev_err(channel->dma->dev, "channel %d: dma with not-done status 0x%x", channel->id, stat); + ret = IRQ_HANDLED; + goto done; + } + + if (!channel->current_desc) { + dev_err(channel->dma->dev, "channel %d: interrupt with no active desc\n", + channel->id); + ret = IRQ_HANDLED; + goto done; + } + + desc = channel->current_desc; + dev_dbg(channel->dma->dev, "%s: current descriptor %px\n", __func__, desc); + + if (desc->cyclic || channel->desc_mode == true) + __adi_dma_clear_only(channel); + else + __adi_dma_clear_and_reset(channel); + + /* register based mode, process sg list */ + if ((desc->scattergather_mode) && (channel->desc_mode == false)) { + if (!list_empty(&desc->dde_desc_list)) { + __adi_dma_process_descriptor(desc); + ret = IRQ_HANDLED; + goto done; + } + } + + desc->result.result = result; + desc->result.residue = 0; + list_add_tail(&desc->cb_node, &channel->cb_pending); + + if (!desc->cyclic) { + channel->current_desc = NULL; + __adi_dma_issue_pending(channel); + } + +done: + spin_unlock(&channel->lock); + return ret; +} + +static irqreturn_t adi_dma_handler(int irq, void *id) +{ + struct adi_dma_channel *channel = id; + + return __adi_dma_handler(channel, DMA_TRANS_NOERROR); +} + +static irqreturn_t adi_dma_error_handler(int irq, void *id) +{ + struct adi_dma_channel *channel = id; + enum dmaengine_tx_result result; + u32 stat; + + // This is only meaningful for memcpy, as peripherals should be interpreted + // based on dev-to-mem or mem-to-dev direction + if (irq == channel->src_err_irq) + result = DMA_TRANS_READ_FAILED; + else + result = DMA_TRANS_WRITE_FAILED; + + spin_lock(&channel->lock); + + // stop on this descriptor, user needs to read out the status and see what is + // wrong, then terminate, and then queue new descriptors + if (channel->current_desc) { + stat = get_dma_curr_irqstat(channel->iosrc); + dev_err(channel->dma->dev, "DMA error on channel %d, stat = 0x%x\n", + channel->id, stat); + channel->current_desc->result.result = result; + __adi_dma_disable_irqs(channel); + } + + __adi_dma_clear_and_reset(channel); + + spin_unlock(&channel->lock); + return IRQ_HANDLED; +} + +static irqreturn_t adi_dma_thread_handler(int irq, void *id) +{ + struct adi_dma_channel *channel = id; + struct adi_dma_descriptor *desc; + struct dmaengine_desc_callback cb; + unsigned long flags; + u32 stat; + + spin_lock_irqsave(&channel->lock, flags); + + while (!list_empty(&channel->cb_pending)) { + desc = list_first_entry(&channel->cb_pending, struct adi_dma_descriptor, + cb_node); + list_del(&desc->cb_node); + + if (channel->desc_mode == true) { + stat = get_dma_curr_irqstat(channel->iosrc); + /* + * we might miss interrupts when processing + * descriptor list. + * + * poll DMA engine to ensure we get all + * data before terminating. + * + * if DMA is idle -> all descriptors are processed + * if DMA is not idle- > wait till DMA's start address is + * the same as its current address: + * either all descriptors are processed or + * there are no more data left to be xferred + */ + if (!DMA_IDLE(stat)) { + /* + * dma current address is advancing. wait here + */ + while (get_dma_start_addr(channel->iosrc) != get_dma_curr_addr(channel->iosrc)) + udelay(1); + } + /* release descriptor list and call it done */ + __adi_dma_free_slave_sg_desc_list(desc); + __adi_dma_clear_and_reset(channel); + } + + if (!desc->cyclic) + dma_cookie_complete(&desc->tx); + dmaengine_desc_get_callback(&desc->tx, &cb); + + spin_unlock_irqrestore(&channel->lock, flags); + dmaengine_desc_callback_invoke(&cb, &desc->result); + + if (!desc->cyclic) + desc->tx.desc_free(&desc->tx); + + spin_lock_irqsave(&channel->lock, flags); + } + + spin_unlock_irqrestore(&channel->lock, flags); + return IRQ_HANDLED; +} + +/** + * This never generates 2D memcpy but can handle up to 4 GB anyway + */ +static void adi_dma_memcpy_config(struct adi_dma_descriptor *desc, + struct adi_dma_channel *adi_chan, + dma_addr_t dst, + dma_addr_t src, size_t size) +{ + u32 conf, shift; + s16 mod; + struct adi_dde_descriptor *dde_desc_src = &desc->dde_descriptor_src; + struct adi_dde_descriptor *dde_desc_dst = &desc->dde_descriptor_dest; + + + adi_dma_get_txn_align(src, dst, size, &conf, &shift); + + // Run memcpy backwards if the two regions might overlap + mod = 1 << shift; + if (src < dst) { + mod *= -1; + dst += size + mod; + src += size + mod; + } + size >>= shift; + + dde_desc_src->xcnt = size; + dde_desc_src->xmod = mod; + dde_desc_src->cfg = conf | DMAEN; + desc->src = src; + desc->dest = dst; + if (adi_chan->desc_mode == true) { + //set up source + // descriptor list mode, 2D mode, restart on synchronize, enable dma channel, + dde_desc_src->cfg = conf; + dde_desc_src->start = src; + + //setup destination + dde_desc_dst->cfg = conf | DMAEN | WNR | DI_EN_X; + dde_desc_dst->start = dst; + dde_desc_dst->xcnt = size; + dde_desc_dst->xmod = mod; + } +} + +static struct dma_async_tx_descriptor *adi_dma_prep_memcpy(struct dma_chan *chan, + dma_addr_t dst, dma_addr_t src, size_t len, unsigned long flags) +{ + struct adi_dma_channel *adi_chan = adi_dma_to_adi_channel(chan); + struct adi_dma *dma = adi_chan->dma; + struct adi_dma_descriptor *desc; + + if (!dma->hw_cfg->has_mdma) + return NULL; + + desc = adi_dma_alloc_descriptor(dma); + if (!desc) + return NULL; + + dev_dbg(dma->dev, "%s: using desc at %px\n", __func__, desc); + + adi_dma_memcpy_config(desc, adi_chan, dst, src, len); + desc->direction = DMA_MEM_TO_MEM; + + dma_async_tx_descriptor_init(&desc->tx, chan); + desc->tx.flags = flags; + desc->tx.tx_submit = adi_dma_submit; + desc->tx.desc_free = adi_dma_desc_free; + + return &desc->tx; +} + +static struct dma_async_tx_descriptor *adi_dma_prep_memset(struct dma_chan *chan, + dma_addr_t dest, int value, size_t len, unsigned long flags) +{ + struct adi_dma_channel *adi_chan = adi_dma_to_adi_channel(chan); + struct adi_dma *dma = adi_chan->dma; + struct adi_dma_descriptor *desc; + u8 byte = (u8)value; + u64 bigword = byte * 0x01010101010101ull; + u32 conf, shift; + s16 mod; + struct adi_dde_descriptor *dde_desc_src, *dde_desc_dst; + + if (!dma->hw_cfg->has_mdma) + return NULL; + + desc = adi_dma_alloc_descriptor(dma); + if (!desc) + return NULL; + dev_dbg(dma->dev, "%s: using desc at %px\n", __func__, desc); + + dde_desc_src = &desc->dde_descriptor_src; + dde_desc_dst = &desc->dde_descriptor_dest; + + desc->memset = dmam_alloc_coherent(dma->dev, ADI_MEMSET_SIZE, &desc->src, + GFP_NOWAIT); + if (!desc->memset) { + dev_err(dma->dev, "%s, dmam_alloc_coherent failed\n", __func__); + devm_kfree(dma->dev, desc); + return NULL; + } + + adi_dma_get_txn_align(desc->src, dest, len, &conf, &shift); + + desc->memset[0] = bigword; + desc->memset[1] = bigword; + desc->memset[2] = bigword; + desc->memset[3] = bigword; + + mod = 1 << shift; + len >>= shift; + + dde_desc_src->xcnt = len; + dde_desc_src->xmod = mod; + if (adi_chan->desc_mode == true) { + //set up source + // descriptor list mode, 2D mode, restart on synchronize, enable dma channel, + dde_desc_src->cfg = conf | DMAEN; + dde_desc_src->start = lower_32_bits((dma_addr_t)desc->memset); + //use the same address to set memory + dde_desc_src->xmod = 0; + //setup destination + dde_desc_dst->cfg = conf | DMAEN | WNR | DI_EN_X; + dde_desc_dst->start = dest; + dde_desc_dst->xcnt = len; + dde_desc_dst->xmod = mod; + } else { + dde_desc_src->cfg = conf | DMAEN; + } + // desc->src set above when memset buf is allocated + desc->dest = dest; + desc->direction = DMA_MEM_TO_MEM; + + dma_async_tx_descriptor_init(&desc->tx, chan); + desc->tx.flags = flags; + desc->tx.tx_submit = adi_dma_submit; + desc->tx.desc_free = adi_dma_desc_free; + + return &desc->tx; +} + +static void __adi_dma_free_slave_sg_desc_list(struct adi_dma_descriptor *desc) +{ + struct dma_chan *chan = desc->tx.chan; + struct adi_dma_channel *adi_chan = adi_dma_to_adi_channel(chan); + struct adi_dma *dma = adi_chan->dma; + struct adi_dde_descriptor *dde_desc; + struct list_head *curr; + struct list_head *tmp; + + list_for_each_safe(curr, tmp, &desc->dde_desc_list) { + dde_desc = list_entry(curr, struct adi_dde_descriptor, dde_desc_unit); + list_del(curr); + dma_pool_free(dma->dde_desc_pool, dde_desc, dde_desc->dma_handle); + } +} + +static void __adi_dma_prep_slave_sg_desc_list(struct adi_dma_descriptor *desc, struct scatterlist *sgl) +{ + struct dma_chan *chan = desc->tx.chan; + struct adi_dma_channel *adi_chan = adi_dma_to_adi_channel(chan); + struct adi_dma *dma = adi_chan->dma; + size_t txlen; + dma_addr_t src; + u32 conf, shift, sync; + struct adi_dde_descriptor *dde_desc, *dde_desc_next; + + INIT_LIST_HEAD(&desc->dde_desc_list); + + dde_desc = adi_dma_alloc_dde_descriptor(dma); + + sync = adi_chan->ch_sync_disable ? 0 : DMASYNC; + + while (sgl != NULL) { + txlen = sg_dma_len(sgl); + src = sg_dma_address(sgl); + + adi_dma_get_periph_align(adi_chan, desc->direction, src, txlen, &conf, &shift); + + if (desc->direction == DMA_DEV_TO_MEM) + conf |= WNR; + + conf |= DI_EN_X; + + dde_desc->xcnt = txlen >> shift; + dde_desc->xmod = 1 << shift; + dde_desc->start = src; + dde_desc->cfg = conf | sync | DMAEN; + + sgl = sg_next(sgl); + if (sgl) { + dde_desc_next = adi_dma_alloc_dde_descriptor(dma); + if (adi_chan->desc_mode == true) { + dde_desc->cfg |= DMAFLOW_LIST | NDSIZE_4; + dde_desc->next = dde_desc_next->dma_handle; + } + } + list_add_tail(&dde_desc->dde_desc_unit, &desc->dde_desc_list); + dde_desc = dde_desc_next; + } +} + +static struct dma_async_tx_descriptor *adi_dma_prep_slave_sg(struct dma_chan *chan, + struct scatterlist *sgl, unsigned int sg_len, + enum dma_transfer_direction direction, unsigned long flags, void *context) +{ + struct adi_dma_channel *adi_chan = adi_dma_to_adi_channel(chan); + struct adi_dma *dma = adi_chan->dma; + struct adi_dma_descriptor *desc = NULL; + + desc = adi_dma_alloc_descriptor(dma); + if (!desc) + return NULL; + + dev_dbg(dma->dev, "%s: using desc at %px\n", __func__, desc); + + dma_async_tx_descriptor_init(&desc->tx, chan); + desc->tx.flags = flags; + desc->tx.tx_submit = adi_dma_submit; + desc->tx.desc_free = adi_dma_desc_free; + desc->scattergather_mode = true; + desc->direction = direction; + /* + * parse sgl and add reqs to the dde dma decriptor list + */ + __adi_dma_prep_slave_sg_desc_list(desc, sgl); + + return &desc->tx; +} + +static struct dma_async_tx_descriptor *adi_dma_prep_cyclic(struct dma_chan *chan, + dma_addr_t buf, size_t len, size_t period_len, + enum dma_transfer_direction direction, unsigned long flags) +{ + struct adi_dma_channel *adi_chan = adi_dma_to_adi_channel(chan); + struct adi_dma *dma = adi_chan->dma; + struct adi_dma_descriptor *desc; + u32 conf, shift, sync; + struct adi_dde_descriptor *dde_desc; + + desc = adi_dma_alloc_descriptor(dma); + if (!desc) + return NULL; + + dev_dbg(dma->dev, "%s: using desc at %px\n", __func__, desc); + + dde_desc = &desc->dde_descriptor_src; + + adi_dma_get_periph_align(adi_chan, direction, buf, period_len, &conf, &shift); + + dde_desc->xcnt = period_len >> shift; + dde_desc->xmod = 1 << shift; + dde_desc->ycnt = len / period_len; + dde_desc->ymod = dde_desc->xmod; + + // Interpret prep interrupt to mean interrupt between each period, + // without it only interrupt after all periods for bookkeeping + if (flags & DMA_PREP_INTERRUPT) + conf |= DI_EN_X; + else + conf |= DI_EN_Y; + + if (direction == DMA_DEV_TO_MEM) + conf |= WNR; + + sync = adi_chan->ch_sync_disable ? 0 : DMASYNC; + + if (adi_chan->desc_mode == true) { + // descriptor list mode, 2D mode, restart on synchronize, enable dma channel, + dde_desc->cfg = conf | DMAFLOW_LIST | NDSIZE_6 | DMA2D | sync | DMAEN; + dde_desc->start = buf; + //set the next descriptor to point to itself. + dde_desc->next = (uintptr_t)dde_desc; + } else { + // autoflow mode, 2D mode, restart on synchronize, enable dma channel, + dde_desc->cfg = conf | DMAFLOW_AUTO | DMA2D | sync | DMAEN; + desc->src = buf; + } + desc->direction = direction; + desc->cyclic = 1; + + dma_async_tx_descriptor_init(&desc->tx, chan); + desc->tx.flags = flags; + desc->tx.tx_submit = adi_dma_submit; + desc->tx.desc_free = adi_dma_desc_free; + + return &desc->tx; +} + +static bool adi_dma_filter(struct dma_chan *chan, void *data) +{ + struct adi_dma_channel *adi_chan = adi_dma_to_adi_channel(chan); + struct adi_dma_filter_data *adi_data = data; + + if (adi_chan->id == adi_data->id) + return true; + + return false; +} + +static struct dma_chan *adi_dma_translate(struct of_phandle_args *args, + struct of_dma *ofdma) +{ + dma_cap_mask_t mask; + struct adi_dma_filter_data data; + + if (args->args_count != 1) + return NULL; + + data.id = (u32)args->args[0]; + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + return __dma_request_channel(&mask, adi_dma_filter, &data, ofdma->of_node); +} + +static int adi_dma_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child; + const struct of_device_id *of_id; + struct adi_dma *dma; + struct resource *res; + void __iomem *base = NULL; + int ret; + u32 buswidths; + + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; + + spin_lock_init(&dma->lock); + dma->dev = dev; + + of_id = of_match_device(dma_dt_ids, dev); + if (!of_id) { + dev_err(dev, "No matching device data found...?\n"); + return -ENOENT; + } + + dma->hw_cfg = of_id->data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + dma->ioaddr = base; + + INIT_LIST_HEAD(&dma->dma_device.channels); + + dma->dma_device.device_issue_pending = adi_dma_issue_pending; + dma->dma_device.device_tx_status = adi_dma_tx_status; + dma->dma_device.device_pause = adi_dma_pause; + dma->dma_device.device_resume = adi_dma_resume; + dma->dma_device.device_terminate_all = adi_dma_terminate_all; + dma->dma_device.device_synchronize = adi_dma_synchronize; + + buswidths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_8_BYTES); + + if (dma->hw_cfg->has_mdma) { + dev_info(dev, "Creating new MDMA controller instance\n"); + dma_cap_set(DMA_MEMCPY, dma->dma_device.cap_mask); + dma_cap_set(DMA_MEMSET, dma->dma_device.cap_mask); + + buswidths |= BIT(DMA_SLAVE_BUSWIDTH_16_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_32_BYTES); + + dma->dma_device.directions = BIT(DMA_MEM_TO_MEM); + dma->dma_device.src_addr_widths = buswidths; + dma->dma_device.dst_addr_widths = buswidths; + dma->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + dma->dma_device.copy_align = 0; + dma->dma_device.fill_align = 0; + + dma->dma_device.device_prep_dma_memcpy = adi_dma_prep_memcpy; + dma->dma_device.device_prep_dma_memset = adi_dma_prep_memset; + } else { + dev_info(dev, "Creating new peripheral DMA controller instance\n"); + dma_cap_set(DMA_SLAVE, dma->dma_device.cap_mask); + dma_cap_set(DMA_CYCLIC, dma->dma_device.cap_mask); + dma_cap_set(DMA_PRIVATE, dma->dma_device.cap_mask); + + dma->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + dma->dma_device.src_addr_widths = buswidths; + dma->dma_device.dst_addr_widths = buswidths; + dma->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + + dma->dma_device.device_config = adi_dma_slave_config; + dma->dma_device.device_prep_slave_sg = adi_dma_prep_slave_sg; + dma->dma_device.device_prep_dma_cyclic = adi_dma_prep_cyclic; + } + + child = NULL; + while ((child = of_get_next_child(np, child))) { + ret = adi_dma_init_channel(dma, child, res->start, res->end - res->start + 1); + if (ret) { + of_node_put(child); + return ret; + } + } + + platform_set_drvdata(pdev, dma); + + dma->dma_device.dev = dev; + ret = dmaenginem_async_device_register(&dma->dma_device); + if (ret) { + dev_err(dev, "Unable to register async transaction DMA engine\n"); + return ret; + } + + ret = of_dma_controller_register(np, adi_dma_translate, dma); + if (ret) { + dev_err(&pdev->dev, "failed to register controller\n"); + return ret; + } + + return 0; +} + +static void adi_dma_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct adi_dma *dma; + + /* everything else allocated with devm, we don't have to free anything */ + dma = platform_get_drvdata(pdev); + if (!dma->dde_desc_pool) + dma_pool_destroy(dma->dde_desc_pool); + + of_dma_controller_free(np); +} + +static struct platform_driver dma_driver = { + .driver = { + .name = "adi-dma", + .of_match_table = dma_dt_ids, + }, + .probe = adi_dma_probe, + .remove = adi_dma_remove, +}; +module_platform_driver(dma_driver); + +MODULE_AUTHOR("Greg Malysa "); +MODULE_DESCRIPTION("SC5xx DMA Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma/adi-dma.h b/drivers/dma/adi-dma.h new file mode 100644 index 00000000000000..40207c781a4dbf --- /dev/null +++ b/drivers/dma/adi-dma.h @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __ASM_DMA_H__ +#define __ASM_DMA_H__ + +#include +#include +#include +#include +#include + +#define ADI_DMA_NEXT_DESC 0x00 +#define ADI_DMA_ADDRSTART 0x04 + +#define ADI_DMA_CFG 0x08 +#define DMA2D BIT(26) /* DMA Mode (2D/1D*) */ +#define DMA_INT_MSK GENMASK(21, 20) /* Generate Interrupt Bits Mask */ +#define DI_EN_X 0x00100000 /* Data Interrupt Enable in X count */ +#define DI_EN_Y 0x00200000 /* Data Interrupt Enable in Y count */ +#define DI_EN_P 0x00300000 /* Data Interrupt Enable in Peripheral */ +#define DI_EN DI_EN_X /* Data Interrupt Enable */ +#define NDSIZE GENMASK(18, 16) /* Next Descriptor */ +#define NDSIZE_0 0x00000000 /* Next Descriptor Size = 1 */ +#define NDSIZE_1 0x00010000 /* Next Descriptor Size = 2 */ +#define NDSIZE_2 0x00020000 /* Next Descriptor Size = 3 */ +#define NDSIZE_3 0x00030000 /* Next Descriptor Size = 4 */ +#define NDSIZE_4 0x00040000 /* Next Descriptor Size = 5 */ +#define NDSIZE_5 0x00050000 /* Next Descriptor Size = 6 */ +#define NDSIZE_6 0x00060000 /* Next Descriptor Size = 7 */ +#define NDSIZE_OFFSET 16 /* Next Descriptor Size Offset */ +#define DMAFLOW GENMASK(14, 12) /* Flow Control */ +#define DMAFLOW_STOP 0x00000000 /* Stop Mode */ +#define DMAFLOW_AUTO 0x00001000 /* Autobuffer Mode */ +#define DMAFLOW_LIST 0x00004000 /* Descriptor List Mode */ +#define DMAFLOW_LARGE DMAFLOW_LIST +#define DMAFLOW_ARRAY 0x00005000 /* Descriptor Array Mode */ +#define DMAFLOW_LIST_DEMAND 0x00006000 /* Descriptor Demand List Mode */ +#define DMAFLOW_ARRAY_DEMAND 0x00007000 /* Descriptor Demand Array Mode */ +#define WDSIZEE_MSK GENMASK(10, 8) /* Memory Transfer Word Size Mask */ +#define WDSIZE_8 0x00000000 /* Memory Transfer Word Size = 8 bits */ +#define WDSIZE_16 0x00000100 /* Memory Transfer Word Size = 16 bits */ +#define WDSIZE_32 0x00000200 /* Memory Transfer Word Size = 32 bits */ +#define WDSIZE_64 0x00000300 /* Memory Transfer Word Size = 64 bits */ +#define WDSIZE_128 0x00000400 /* Memory Transfer Word Size = 128 bits */ +#define WDSIZE_256 0x00000500 /* Memory Transfer Word Size = 256 bits */ +#define PSIZE_MSK GENMASK(6, 4) /* Peripheral Transfer Word Size Mask */ +#define PSIZE_8 0x00000000 /* Peripheral Transfer Word Size = 8 bits */ +#define PSIZE_16 0x00000010 /* Peripheral Transfer Word Size = 16 bits */ +#define PSIZE_32 0x00000020 /* Peripheral Transfer Word Size = 32 bits */ +#define PSIZE_64 0x00000030 /* Peripheral Transfer Word Size = 64 bits */ +#define DMASYNC BIT(2) /* DMA Buffer Clear SYNC */ +#define WNR BIT(1) /* Channel Direction (W/R*) */ +#define DMAEN BIT(0) /* DMA Channel Enable */ + +#define ADI_DMA_XCNT 0x0c +#define ADI_DMA_XMOD 0x10 +#define ADI_DMA_YCNT 0x14 +#define ADI_DMA_YMOD 0x18 +#define ADI_DMA_DSCPTR_CUR 0x24 +#define ADI_DMA_DSCPTR_PRV 0x28 +#define ADI_DMA_ADDR_CUR 0x2c + +#define ADI_DMA_STAT 0x30 +#define DMA_RUN_MASK GENMASK(10, 8) /* DMA Running Bits Mask */ +#define DMA_RUN_DFETCH 0x00000100 /* DMA Running Fetch */ +#define DMA_RUN 0x00000200 /* DMA Running Trans */ +#define DMA_RUN_WAIT_TRIG 0x00000300 /* DMA Running WAIT TRIG */ +#define DMA_RUN_WAIT_ACK 0x00000400 /* DMA Running WAIT ACK */ +#define DMA_PIRQ BIT(2) /* DMA Peripheral Error Interrupt Status */ +#define DMA_ERR BIT(1) /* DMA Error Interrupt Status */ +#define DMA_DONE BIT(0) /* DMA Completion Interrupt Status */ + +#define ADI_DMA_XCNT_CUR 0x34 +#define ADI_DMA_YCNT_CUR 0x38 +#define ADI_DMA_BWLCNT 0x40 +#define ADI_DMA_BWLCNT_CUR 0x44 +#define ADI_DMA_BWMCNT 0x48 +#define ADI_DMA_BWMCNT_CUR 0x4c + +#define DMA_DESC_FETCH 0x100 /* DMA is fetching descriptors */ +#define DMA_DATA_XFER 0x200 /* DMA is in data transfer state */ +#define DMA_IDLE_MASK 0x700 +#define DMA_IDLE(x) ((x & DMA_IDLE_MASK) == 0) +#define DMA_FETCHING_DESC(x) (x & DMA_DESC_FETCH) +#define DMA_XFER_DATA(x) (x & DMA_DATA_XFER) + +#define START_DESC_LIST_XFR (NDSIZE_4 | DMAFLOW_LIST) + +/** DMA API's **/ +static inline void set_dma_start_addr(void __iomem *ioaddr, dma_addr_t addr) +{ + iowrite32(lower_32_bits(addr), ioaddr + ADI_DMA_ADDRSTART); +} +static inline void set_dma_next_desc_addr(void __iomem *ioaddr, dma_addr_t addr) +{ + iowrite32(lower_32_bits(addr), ioaddr + ADI_DMA_NEXT_DESC); +} +static inline void set_dma_curr_desc_addr(void __iomem *ioaddr, dma_addr_t addr) +{ + iowrite32(lower_32_bits(addr), ioaddr + ADI_DMA_DSCPTR_CUR); +} +static inline void set_dma_x_count(void __iomem *ioaddr, unsigned long x_count) +{ + iowrite32(x_count, ioaddr + ADI_DMA_XCNT); +} +static inline void set_dma_y_count(void __iomem *ioaddr, unsigned long y_count) +{ + iowrite32(y_count, ioaddr + ADI_DMA_YCNT); +} +static inline void set_dma_x_modify(void __iomem *ioaddr, long x_modify) +{ + iowrite32(x_modify, ioaddr + ADI_DMA_XMOD); +} +static inline void set_dma_y_modify(void __iomem *ioaddr, long y_modify) +{ + iowrite32(y_modify, ioaddr + ADI_DMA_YMOD); +} +static inline void set_dma_config(void __iomem *ioaddr, unsigned long config) +{ + iowrite32(config, ioaddr + ADI_DMA_CFG); +} +static inline void set_dma_curr_addr(void __iomem *ioaddr, dma_addr_t addr) +{ + iowrite32(lower_32_bits(addr), ioaddr + ADI_DMA_ADDR_CUR); +} + +static inline unsigned long get_dma_curr_irqstat(void __iomem *ioaddr) +{ + return ioread32(ioaddr + ADI_DMA_STAT); +} +static inline unsigned long get_dma_curr_xcount(void __iomem *ioaddr) +{ + return ioread32(ioaddr + ADI_DMA_XCNT_CUR); +} +static inline unsigned long get_dma_curr_ycount(void __iomem *ioaddr) +{ + return ioread32(ioaddr + ADI_DMA_YCNT_CUR); +} +static inline dma_addr_t get_dma_next_desc_ptr(void __iomem *ioaddr) +{ + return ioread32(ioaddr + ADI_DMA_NEXT_DESC); +} +static inline dma_addr_t get_dma_curr_desc_ptr(void __iomem *ioaddr) +{ + return ioread32(ioaddr + ADI_DMA_DSCPTR_CUR); +} +static inline unsigned long get_dma_config(void __iomem *ioaddr) +{ + return ioread32(ioaddr + ADI_DMA_CFG); +} +static inline unsigned long get_dma_curr_addr(void __iomem *ioaddr) +{ + return ioread32(ioaddr + ADI_DMA_ADDR_CUR); +} +static inline unsigned long get_dma_start_addr(void __iomem *ioaddr) +{ + return ioread32(ioaddr + ADI_DMA_ADDRSTART); +} +static inline void clear_dma_irqstat(void __iomem *ioaddr) +{ + iowrite32(DMA_DONE | DMA_ERR | DMA_PIRQ, ioaddr + ADI_DMA_STAT); +} +#endif diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d93cd4f722b401..0341face5189c8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -35,6 +35,8 @@ config GPIO_ACPI def_bool y depends on ACPI +source "drivers/gpio/adi/Kconfig" + config GPIOLIB_IRQCHIP select IRQ_DOMAIN bool diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 1429e8c0229b92..bd7a13d0243ff0 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_GPIO_104_IDI_48) += gpio-104-idi-48.o obj-$(CONFIG_GPIO_104_IDIO_16) += gpio-104-idio-16.o obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o +obj-$(CONFIG_GPIO_ADI_ADRV906X) += adi/ obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o obj-$(CONFIG_GPIO_ADP5585) += gpio-adp5585.o diff --git a/drivers/gpio/adi/Kconfig b/drivers/gpio/adi/Kconfig new file mode 100644 index 00000000000000..f727e2582e0f85 --- /dev/null +++ b/drivers/gpio/adi/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config GPIO_ADI_ADRV906X + bool "Analog Devices Inc, ADRV906x SoC GPIO support" + depends on PINCTRL_ADI_ADRV906X + default PINCTRL_ADI_ADRV906X + select GPIOLIB_IRQCHIP + help + Say yes here to add support for ADI's ADRV906x SoC GPIO controller. diff --git a/drivers/gpio/adi/Makefile b/drivers/gpio/adi/Makefile new file mode 100644 index 00000000000000..20c55b68be4f06 --- /dev/null +++ b/drivers/gpio/adi/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +obj-$(CONFIG_GPIO_ADI_ADRV906X) += gpio-adi.o +gpio-adi-y := gpio-adi-adrv906x.o gpio-adi-smc.o \ No newline at end of file diff --git a/drivers/gpio/adi/gpio-adi-adrv906x.c b/drivers/gpio/adi/gpio-adi-adrv906x.c new file mode 100644 index 00000000000000..92f1cd3070530c --- /dev/null +++ b/drivers/gpio/adi/gpio-adi-adrv906x.c @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "gpio-adi-smc.h" + +/* The adrv906x SoC utilizes GPIO controllers which support up to 32 GPIO's per instance */ +#define ADI_ADRV906X_MAX_GPIO_PER_INST (32U) + +#define ADI_ADRV906X_GPIO_NUM_INSTS (4U) + +#define ADI_ADRV906X_MAX_GPIOS (ADI_ADRV906X_MAX_GPIO_PER_INST * ADI_ADRV906X_GPIO_NUM_INSTS) + +#define ADI_ADRV906X_GET_GPIO_INST_NUM(pin) (pin / ADI_ADRV906X_MAX_GPIO_PER_INST) +#define ADI_ADRV906X_GET_GPIO_INST_BIT_MASK(pin) (0x1 << (pin % ADI_ADRV906X_MAX_GPIO_PER_INST)) + +#define ADI_ADRV906X_GPIO_INST_0_OFFSET (0x800U) +#define ADI_ADRV906X_GPIO_INST_1_OFFSET (0x900U) +#define ADI_ADRV906X_GPIO_INST_2_OFFSET (0xA00U) +#define ADI_ADRV906X_GPIO_INST_3_OFFSET (0xB00U) + +#define ADI_ADRV906X_GPIO_WRITE_OFFSET (0x000U) +#define ADI_ADRV906X_GPIO_CLEAR_OFFSET (0x004U) +#define ADI_ADRV906X_GPIO_SET_OFFSET (0x008U) +#define ADI_ADRV906X_GPIO_TOGGLE_OFFSET (0x00AU) +#define ADI_ADRV906X_GPIO_READ_OFFSET (0x010U) + +#define ADI_ADRV906X_GPIO_DIR_CONTROL_BASE (0xB14U) + +#define GPIO_LINE_DIRECTION_IN 1 +#define GPIO_LINE_DIRECTION_OUT 0 + +#define GPIO_DIR_CONTROL_OE_BIT_MASK (0x1U) +#define GPIO_DIR_CONTROL_IE_BIT_MASK (0x2U) + +#define GPIO_PIN_STATE_LOW 0 +#define GPIO_PIN_STATE_HIGH 1 + +static uint32_t adrv906x_gpio_inst_base_addr[ADI_ADRV906X_GPIO_NUM_INSTS] = { + ADI_ADRV906X_GPIO_INST_0_OFFSET, + ADI_ADRV906X_GPIO_INST_1_OFFSET, + ADI_ADRV906X_GPIO_INST_2_OFFSET, + ADI_ADRV906X_GPIO_INST_3_OFFSET +}; + +/** + * struct adi_adrv906x_platform_data - adi adrv906x soc gpio platform data structure + * @label: string to store in gpio->label + * @ngpio: max number of gpio pins + */ +struct adi_adrv906x_platform_data { + const char *label; +}; + +/** + * struct adi_adrv906x_gpio - gpio device private data structure + * @chip: instance of the gpio_chip + * @regmap: base address of the GPIO device + */ +struct adi_adrv906x_gpio { + struct gpio_chip chip; + void __iomem *base_addr; + const struct adi_adrv906x_platform_data *p_data; + uint32_t pintmux_addr; + + // For interrupt-controller + struct irq_chip irq; + struct fwnode_handle *fwnode; +}; + +/** + * adi_adrv906x_gpio_get_direction - Read the direction of the specified GPIO pin + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * + * This function returns the direction of the specified GPIO. + * + * Return: GPIO_LINE_DIRECTION_OUT or GPIO_LINE_DIRECTION_IN + */ +static int adi_adrv906x_gpio_get_direction(struct gpio_chip *chip, unsigned int pin) +{ + uint32_t reg; + struct adi_adrv906x_gpio *gpio = gpiochip_get_data(chip); + + reg = ioread32(gpio->base_addr + ADI_ADRV906X_GPIO_DIR_CONTROL_BASE + (pin * sizeof(uint32_t))); + + if (reg & GPIO_DIR_CONTROL_OE_BIT_MASK) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + +/** + * adi_adrv906x_gpio_direction_input - Set the direction of the specified GPIO pin as input + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * + * This sets the direction of the gpio pin as input. + * + * Return: 0 always + */ +static int adi_adrv906x_gpio_direction_input(struct gpio_chip *chip, unsigned int pin) +{ + struct adi_adrv906x_gpio *gpio = gpiochip_get_data(chip); + + iowrite32(GPIO_DIR_CONTROL_IE_BIT_MASK, gpio->base_addr + ADI_ADRV906X_GPIO_DIR_CONTROL_BASE + (pin * sizeof(uint32_t))); + + return 0; +} + +/** + * adi_adrv906x_gpio_get() - Get the state of the specified pin of GPIO device + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * + * This function reads the state of the specified pin of the GPIO device. + * + * Return: 0 if the pin is low, 1 if pin is high. + */ +static int adi_adrv906x_gpio_get(struct gpio_chip *chip, unsigned int pin) +{ + uint32_t reg; + struct adi_adrv906x_gpio *gpio = gpiochip_get_data(chip); + uint32_t gpio_inst_number = ADI_ADRV906X_GET_GPIO_INST_NUM(pin); + uint32_t gpio_inst_bit_mask = ADI_ADRV906X_GET_GPIO_INST_BIT_MASK(pin); + + /* if pin is input, read value from the read register, else write register */ + int ret = adi_adrv906x_gpio_get_direction(chip, pin); + + if (ret == GPIO_LINE_DIRECTION_IN) + reg = ioread32(gpio->base_addr + adrv906x_gpio_inst_base_addr[gpio_inst_number] + ADI_ADRV906X_GPIO_READ_OFFSET); + else + reg = ioread32(gpio->base_addr + adrv906x_gpio_inst_base_addr[gpio_inst_number] + ADI_ADRV906X_GPIO_WRITE_OFFSET); + + if (gpio_inst_bit_mask & reg) + return GPIO_PIN_STATE_HIGH; + + return GPIO_PIN_STATE_LOW; +} + +/** + * adi_adrv906x_gpio_set - Modify the state of the pin with specified value + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * @state: value used to modify the state of the specified pin + * + * This function calculates the register offset based on the given pin number and sets the state of a + * gpio pin to the specified value. The state is either 0 or non-zero. + */ +static void adi_adrv906x_gpio_set(struct gpio_chip *chip, unsigned int pin, int state) +{ + struct adi_adrv906x_gpio *gpio = gpiochip_get_data(chip); + uint32_t gpio_inst_number = ADI_ADRV906X_GET_GPIO_INST_NUM(pin); + uint32_t gpio_inst_bit_mask = ADI_ADRV906X_GET_GPIO_INST_BIT_MASK(pin); + + if (state) + iowrite32(gpio_inst_bit_mask, gpio->base_addr + adrv906x_gpio_inst_base_addr[gpio_inst_number] + ADI_ADRV906X_GPIO_SET_OFFSET); + else + iowrite32(gpio_inst_bit_mask, gpio->base_addr + adrv906x_gpio_inst_base_addr[gpio_inst_number] + ADI_ADRV906X_GPIO_CLEAR_OFFSET); + + return; +} + +/** + * adi_adrv906x_gpio_direction_output - Set the direction of the specified GPIO pin as output + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * @state: value to be written to specified pin + * + * This function sets the direction of specified GPIO pin as output, configures + * the Output Enable register for the pin and uses adi_adrv906x_gpio_set to set + * the state of the pin to the value specified. + * + * Return: 0 always + */ +static int adi_adrv906x_gpio_direction_output(struct gpio_chip *chip, + unsigned int pin, int state) +{ + struct adi_adrv906x_gpio *gpio = gpiochip_get_data(chip); + + /* + * Write the specified value to the output pin + */ + adi_adrv906x_gpio_set(chip, pin, state); + + /* + * Setup pin as output + */ + iowrite32(GPIO_DIR_CONTROL_OE_BIT_MASK, gpio->base_addr + ADI_ADRV906X_GPIO_DIR_CONTROL_BASE + (pin * sizeof(uint32_t))); + + return 0; +} + +/** + * adi_adrv906x_gpio_irq_convert_to_supported_type + * - GIC supports LEVEL_HIGH and EDGE_RISING interrupts. + * - Our trasmutter can manage both edge and level interrupts, and allows inverting the polarity of both types. + * - We here adapt the requested type to match those supported by GIC. + * - Note that IRQ_TYPE_NONE is used to skip the configuration of the hardware in some drivers, we transform it to level high. + * @requested_type: IRQ type requested bo be configured + * @supported_type: IRQ type to be used + * @polarity: polarity for the supported_type IRQ + * Returns 0 on success + */ +static int adi_adrv906x_gpio_irq_convert_to_supported_type(unsigned int requested_type, unsigned int *supported_type, bool *polarity) +{ + bool pol; + + if (!supported_type) return -EINVAL; + + switch (requested_type) { + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_LEVEL_HIGH: + *supported_type = requested_type; + pol = true; + break; + case IRQ_TYPE_EDGE_FALLING: + *supported_type = IRQ_TYPE_EDGE_RISING; + pol = false; + break; + case IRQ_TYPE_LEVEL_LOW: + *supported_type = IRQ_TYPE_LEVEL_HIGH; + pol = false; + break; + case IRQ_TYPE_EDGE_BOTH: + printk(KERN_INFO "adi-adrv906x-gpio: requested IRQ_TYPE_EDGE_BOTH. Forced to IRQ_TYPE_EDGE_RISING\n"); + *supported_type = IRQ_TYPE_EDGE_RISING; + pol = true; + break; + case IRQ_TYPE_NONE: + printk(KERN_INFO "adi-adrv906x-gpio: requested IRQ_TYPE_NONE. Forced to IRQ_TYPE_LEVEL_HIGH\n"); + *supported_type = IRQ_TYPE_LEVEL_HIGH; + pol = true; + break; + default: + printk(KERN_ERR "adi-adrv906x-gpio: Unknown requested irq type: %i\n", requested_type); + return -EINVAL; + } + + if (polarity) *polarity = pol; + + return 0; +} + +/** + * adi_adrv906x_gpio_child_to_parent_hwirq - Look up the parent hardware irq from a child hardware irq + * @gc: gpio_chip pointer + * @child: GPIO index 0..ngpio-1 + * @child_type: IRQ type (such as IRQ_TYPE_*) + * @parent: returned parent IRQ number + * @parent_type: returned parent IRQ type (such as IRQ_TYPE_*) + */ +static int adi_adrv906x_gpio_child_to_parent_hwirq(struct gpio_chip *gc, + unsigned int child, + unsigned int child_type, + unsigned int *parent, + unsigned int *parent_type) +{ + struct adi_adrv906x_gpio *gpio = gpiochip_get_data(gc); + unsigned int type; + bool polarity; + unsigned int irq_num; + + if (adi_adrv906x_gpio_irq_convert_to_supported_type(child_type, &type, &polarity) != 0) { + printk(KERN_ERR "adi-adrv906x-gpio: Cannot find supported irq type for requested type %u)\n", child_type); + return -EINVAL; + } + + if (adi_adrv906x_pintmux_map(child, polarity, &irq_num, gpio->pintmux_addr)) { + *parent = irq_num; + *parent_type = type; + return 0; + } + printk(KERN_ERR "adi-adrv906x-gpio: pintmux_map failed (child irq number: %u)\n", child); + + return -EINVAL; +} + +/** + * adi_adrv906x_gpio_populate_parent_alloc_arg - Allocates and populates the specific struct for the parent's IRQ domain + * @chip: gpio_chip pointer + * @parent_hwirq: parent IRQ number + * @parent_type: IRQ type (such as IRQ_TYPE_*) + */ +static int adi_adrv906x_gpio_populate_parent_alloc_arg(struct gpio_chip *chip, + union gpio_irq_fwspec *gfwspec, + unsigned int parent_hwirq, + unsigned int parent_type) +{ + struct irq_fwspec *fwspec = &gfwspec->fwspec; + + fwspec->fwnode = chip->irq.parent_domain->fwnode; + fwspec->param_count = 3; + fwspec->param[0] = GIC_SPI; + fwspec->param[1] = parent_hwirq; + fwspec->param[2] = parent_type; + + return 0; +} + +/** + * adi_adrv906x_gpio_irq_mask - Masks the IRQ + * @d: irq_data pointer + */ +static void adi_adrv906x_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + + irq_chip_mask_parent(d); + gpiochip_disable_irq(gc, irqd_to_hwirq(d)); +} + +/** + * adi_adrv906x_gpio_irq_unmask - Unmasks the IRQ + * @d: irq_data pointer + */ +static void adi_adrv906x_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + + gpiochip_enable_irq(gc, irqd_to_hwirq(d)); + irq_chip_unmask_parent(d); +} + +/** + * adi_adrv906x_gpio_irq_set_type - Sets the parent's IRQ type + * @d: irq_data pointer + * @type: IRQ type (such as IRQ_TYPE_*) + */ +static int adi_adrv906x_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + unsigned int supported_type; + + if (adi_adrv906x_gpio_irq_convert_to_supported_type(type, &supported_type, NULL) != 0) { + printk(KERN_ERR "adi-adrv906x-gpio: Cannot find supported irq type for requested type %u)\n", type); + return -EINVAL; + } + + return irq_chip_set_type_parent(d, supported_type); +} + +/** + * adi_adrv906x_gpio_irq_shutdown - Shutdowns the IRQ, unmapping the gpio from the tf-a pintmux service + * @d: irq_data pointer + */ +static void adi_adrv906x_gpio_irq_shutdown(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adi_adrv906x_gpio *gpio = gpiochip_get_data(gc); + unsigned int gpionum = d->hwirq; + + gpiochip_unlock_as_irq(gc, gpionum); + if (!adi_adrv906x_pintmux_unmap(gpionum, gpio->pintmux_addr)) + printk(KERN_ERR "adi-adrv906x-gpio: pintmux_unmap failed (child irq number: %u)\n", gpionum); +} + +static const struct adi_adrv906x_platform_data adi_adrv906x_gpio_def = { + .label = "adi_adrv906x_gpio", +}; + +static const struct of_device_id adi_adrv906x_gpio_ids[] = { + { .compatible = "adi,adrv906x-gpio", .data = &adi_adrv906x_gpio_def }, + {}, +}; + +static int adi_adrv906x_gpio_probe(struct platform_device *pdev) +{ + int ret; + struct adi_adrv906x_gpio *gpio; + struct gpio_chip *chip; + const struct of_device_id *match; + uint32_t ngpio = 0U; + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + match = of_match_node(adi_adrv906x_gpio_ids, pdev->dev.of_node); + if (!match) { + dev_err(&pdev->dev, "of_match_node() failed\n"); + return -EINVAL; + } + + if (of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpio)) + return -EINVAL; + + if (ngpio > ADI_ADRV906X_MAX_GPIOS) { + dev_err(&pdev->dev, "ngpios exceeded maximum, failed\n"); + return -EINVAL; + } + + gpio->p_data = match->data; + platform_set_drvdata(pdev, gpio); + if (of_property_read_u32(pdev->dev.of_node, "pintmux", &gpio->pintmux_addr)) + return -EINVAL; + gpio->base_addr = devm_platform_ioremap_resource(pdev, 0); + + if (IS_ERR(gpio->base_addr)) + return PTR_ERR(gpio->base_addr); + + chip = &gpio->chip; + chip->label = gpio->p_data->label; + chip->parent = &pdev->dev; + chip->owner = THIS_MODULE; + chip->get_direction = adi_adrv906x_gpio_get_direction; + chip->direction_input = adi_adrv906x_gpio_direction_input; + chip->direction_output = adi_adrv906x_gpio_direction_output; + chip->get = adi_adrv906x_gpio_get; + chip->set = adi_adrv906x_gpio_set; + chip->base = -1; + chip->ngpio = (uint16_t)ngpio; + chip->can_sleep = 0; + + if (of_property_read_bool(pdev->dev.of_node, "interrupt-controller")) { + struct gpio_irq_chip *girq; + struct device_node *node = pdev->dev.of_node; + struct device_node *irq_parent; + struct irq_domain *parent; + + irq_parent = of_irq_find_parent(node); + if (!irq_parent) { + dev_err(&pdev->dev, "no IRQ parent node\n"); + return -ENODEV; + } + parent = irq_find_host(irq_parent); + of_node_put(irq_parent); + if (!parent) { + dev_err(&pdev->dev, "no IRQ parent domain\n"); + return -ENODEV; + } + + gpio->irq.name = "adi-adrv906x-gpio"; + gpio->irq.irq_mask = adi_adrv906x_gpio_irq_mask; + gpio->irq.irq_unmask = adi_adrv906x_gpio_irq_unmask; + gpio->irq.irq_set_type = adi_adrv906x_gpio_irq_set_type; + gpio->irq.irq_shutdown = adi_adrv906x_gpio_irq_shutdown; + gpio->irq.irq_eoi = irq_chip_eoi_parent; + + girq = &gpio->chip.irq; + girq->chip = &gpio->irq; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_bad_irq; + girq->fwnode = of_node_to_fwnode(node); + girq->parent_domain = parent; + girq->child_to_parent_hwirq = adi_adrv906x_gpio_child_to_parent_hwirq; + girq->populate_parent_alloc_arg = adi_adrv906x_gpio_populate_parent_alloc_arg; + } + + /* report a bug if gpio chip registration fails */ + ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); + if (ret) { + dev_err(&pdev->dev, "Failed to add gpio chip\n"); + return -EINVAL; + } + + printk(KERN_INFO "adi_adrv906x_gpio_probe :: SUCCESS \n"); + + return 0; +} + +MODULE_DEVICE_TABLE(of, adi_adrv906x_gpio_ids); + +static struct platform_driver adi_adrv906x_gpio_driver = { + .driver = { + .name = "adi-adrv906x-gpio", + .of_match_table = of_match_ptr(adi_adrv906x_gpio_ids) + }, + .probe = adi_adrv906x_gpio_probe, +}; + +module_platform_driver(adi_adrv906x_gpio_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ADI ADRV906X GPIO driver"); +MODULE_AUTHOR("Analog Devices Inc."); diff --git a/drivers/gpio/adi/gpio-adi-smc.c b/drivers/gpio/adi/gpio-adi-smc.c new file mode 100644 index 00000000000000..eb01bde917bab0 --- /dev/null +++ b/drivers/gpio/adi/gpio-adi-smc.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include "gpio-adi-smc.h" + +/* PINTMUX Function ID*/ +#define ADI_PINTMUX_SIP_SERVICE_FUNCTION_ID (0xC2000002) + +/* ADI Pintmux SIP Service Functions*/ +#define ADI_PINTMUX_MAP (1U) +#define ADI_PINTMUX_UNMAP (2U) + +/* SMC Handler return Status Values (res.a0 return value) */ +#define ADI_PINTMUX_SMC_RETURN_SUCCESS (0U) +#define ADI_PINTMUX_SMC_RETURN_UNSUPPORTED_REQUEST (0xFFFFFFFFFFFFFFFFU) + +/* SMC Pintmux Handler return values (res.a1 return value) */ +#define ADI_TFA_PINTMUX_ERR_LOOKUP_FAIL (0xFFFFFFFFFFFFFFFFU) +#define ADI_TFA_PINTMUX_ERR_MAP_FAIL (0xFFFFFFFFFFFFFFFEU) +#define ADI_TFA_PINTMUX_ERR_NOT_MAPPED (0xFFFFFFFFFFFFFFFDU) +#define ADI_TFA_PINTMUX_ERR_SECURITY (0xFFFFFFFFFFFFFFFCU) + +static bool adi_adrv906x_pintmux_smc(unsigned int fid, unsigned int pin_id, bool polarity, unsigned int *irq_num, uintptr_t base_addr) +{ + struct arm_smccc_res res = { 0 }; + + /* + * Setup smc call to perform the pintmux operation + * + * arm_smccc_smc expected params: + * param1: SMC SIP SERVICE ID + * param2: ADI Pintmux function id (MAP, UNMAP) + * param3: Pin Number requested + * param4: Polarity + * param5: Base Address of Pinctrl + * param6-8: Currently UNUSED/UNDEFINED + * param9: response output of the SMC call + * a0= SMC return value + * a1= irq number (or <0 if error) + * + */ + arm_smccc_smc(ADI_PINTMUX_SIP_SERVICE_FUNCTION_ID, + fid, + pin_id, + polarity, + base_addr, + 0, 0, 0, + &res); + + /* Check SMC error */ + if (res.a0 != ADI_PINTMUX_SMC_RETURN_SUCCESS) { + printk(KERN_ERR "ADI SMC_ERR 0x%016lx\n", res.a0); + return false; + } + + /* Check PINTMUX function error */ + switch (res.a1) { + case ADI_TFA_PINTMUX_ERR_LOOKUP_FAIL: + case ADI_TFA_PINTMUX_ERR_MAP_FAIL: + case ADI_TFA_PINTMUX_ERR_NOT_MAPPED: + case ADI_TFA_PINTMUX_ERR_SECURITY: + printk(KERN_ERR "ADI PINTMUX Service ERR 0x%016lx\n", res.a1); + return false; + default: + break; + } + + /* Get IRQ number */ + if (irq_num) + *irq_num = (unsigned int)res.a1; + + return true; +} + +bool adi_adrv906x_pintmux_map(unsigned int gpio, bool polarity, unsigned int *irq, uintptr_t base_addr) +{ + if (!irq) { + printk(KERN_ERR "%s :: Invalid arguments\n", __FUNCTION__); + return false; + } + return adi_adrv906x_pintmux_smc(ADI_PINTMUX_MAP, gpio, polarity, irq, base_addr); +} + +bool adi_adrv906x_pintmux_unmap(unsigned int gpio, uintptr_t base_addr) +{ + return adi_adrv906x_pintmux_smc(ADI_PINTMUX_UNMAP, gpio, true, NULL, base_addr); +} diff --git a/drivers/gpio/adi/gpio-adi-smc.h b/drivers/gpio/adi/gpio-adi-smc.h new file mode 100644 index 00000000000000..1bddca48bb241d --- /dev/null +++ b/drivers/gpio/adi/gpio-adi-smc.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2023, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __DRIVERS_GPIO_ADI_SMC_H +#define __DRIVERS_GPIO_ADI_SMC_H + +#include + +bool adi_adrv906x_pintmux_map(unsigned int gpio, bool polarity, unsigned int *irq, uintptr_t base_addr); +bool adi_adrv906x_pintmux_unmap(unsigned int gpio, uintptr_t base_addr); + +#endif /* __DRIVERS_GPIO_ADI_SMC_H */ diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 2254abda5c46c9..c50c38ba68f9f6 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -519,6 +519,24 @@ config I2C_BRCMSTB If you do not need I2C interface, say N. +config I2C_ADI_TWI + tristate "ADI TWI I2C support" + depends on BLACKFIN || ARCH_SC59X_64 || ARCH_SC59X || ARCH_SC58X || ARCH_SC57X || ARCH_ADRV906X + depends on !BF561 && !BF531 && !BF532 && !BF533 + help + This is the I2C bus driver for ADI on-chip TWI interface. + + This driver can also be built as a module. If so, the module + will be called i2c-adi-twi. + +config I2C_ADI_TWI_CLK_KHZ + int "ADI TWI I2C clock (kHz)" + depends on I2C_ADI_TWI + range 21 400 + default 50 + help + The unit of the TWI clock is kHz. + config I2C_CADENCE tristate "Cadence I2C Controller" depends on ARCH_ZYNQ || ARM64 || XTENSA || RISCV || COMPILE_TEST diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index ecc07c50f2a0fe..1a89ed96088dbf 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o +obj-$(CONFIG_I2C_ADI_TWI) += i2c-adi-twi.o obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o obj-$(CONFIG_I2C_CPM) += i2c-cpm.o diff --git a/drivers/i2c/busses/i2c-adi-twi.c b/drivers/i2c/busses/i2c-adi-twi.c new file mode 100644 index 00000000000000..297821fa888f51 --- /dev/null +++ b/drivers/i2c/busses/i2c-adi-twi.c @@ -0,0 +1,936 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ADI On-Chip Two Wire Interface Driver + * + * Copyright 2005-2024 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* There is a known issue with the SMBus protocol implementation. As + * a temporary solution, an emulated version of the protocol has been selected. + */ +#define TWI_USE_EMULATED_SMBUS + +/* SMBus mode*/ +#define TWI_I2C_MODE_STANDARD 1 +#define TWI_I2C_MODE_STANDARDSUB 2 +#define TWI_I2C_MODE_COMBINED 3 +#define TWI_I2C_MODE_REPEAT 4 + +/* + * ADI SPI registers layout + */ +#define ADI_TWI_CLKDIV 0x00 + +#define ADI_TWI_CTL 0x04 +#define TWI_ENA BIT(7) /* TWI Enable */ + +#define ADI_TWI_SLVCTL 0x08 +#define ADI_TWI_SLVSTAT 0x0c +#define ADI_TWI_SLVADDR 0x10 + +#define ADI_TWI_MSTRCTL 0x14 +#define SCLOVR BIT(15) /* Serial Clock Override */ +#define SDAOVR BIT(14) /* Serial Data Override */ +#define RSTART BIT(5) /* Repeat Start or Stop* At End Of Transfer */ +#define STOP BIT(4) /* Issue Stop Condition */ +#define FAST BIT(3) /* Use Fast Mode Timing Specs */ +#define MDIR BIT(2) /* Master Transmit Direction (RX/TX*) */ +#define MEN BIT(0) /* Master Mode Enable */ + +#define ADI_TWI_MSTRSTAT 0x18 +#define BUSBUSY BIT(8) /* Bus Busy Indicator */ +#define SDASEN BIT(6) /* Serial Data Sense */ +#define BUFWRERR BIT(5) /* Buffer Write Error */ +#define BUFRDERR BIT(4) /* Buffer Read Error */ +#define DNAK BIT(3) /* Data Not Acknowledged */ +#define ANAK BIT(2) /* Address Not Acknowledged */ +#define LOSTARB BIT(1) /* Lost Arbitration Indicator (Xfer Aborted) */ + +#define ADI_TWI_MSTRADDR 0x1c + +#define ADI_TWI_ISTAT 0x20 +#define ADI_TWI_IMSK 0x24 +#define RCVSERV BIT(7) /* Receive FIFO Service */ +#define XMTSERV BIT(6) /* Transmit FIFO Service */ +#define MERR BIT(5) /* Master Transfer Error */ +#define MCOMP BIT(4) /* Master Transfer Complete */ + +#define ADI_TWI_FIFOCTL 0x28 + +#define ADI_TWI_FIFOSTAT 0x2c +#define XMTSTAT 0x0003 /* Transmit FIFO Status */ +#define XMT_FULL 0x0003 /* Transmit FIFO Full (2 Bytes To Write) */ +#define RCVSTAT 0x000C /* Receive FIFO Status */ + +#define ADI_TWI_TXDATA8 0x80 +#define ADI_TWI_TXDATA16 0x84 +#define ADI_TWI_RXDATA8 0x88 +#define ADI_TWI_RXDATA16 0x8c + +struct adi_twi_iface { + void __iomem *regs_base; + int irq; + spinlock_t lock; + char read_write; + u8 command; + u8 *transPtr; + int readNum; + int writeNum; + int cur_mode; + int manual_stop; + int result; + unsigned int twi_clk; + struct i2c_adapter adap; + struct completion complete; + struct i2c_msg *pmsg; + int msg_num; + int cur_msg; + u16 saved_clkdiv; + u16 saved_control; + struct clk *sclk; +}; + +static void adi_twi_handle_interrupt(struct adi_twi_iface *iface, + unsigned short twi_int_status, + bool polling) +{ + u16 writeValue; + u16 fifo_status; + unsigned short mast_stat = ioread16(iface->regs_base + ADI_TWI_MSTRSTAT); + + if (twi_int_status & XMTSERV) { + if (iface->writeNum <= 0) { + /* start receive immediately after complete sending in + * combine mode. + */ + if (iface->cur_mode == TWI_I2C_MODE_COMBINED) { + writeValue = ioread16(iface->regs_base + ADI_TWI_MSTRCTL) | MDIR; + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + } else if (iface->manual_stop) { + writeValue = ioread16(iface->regs_base + ADI_TWI_MSTRCTL) | STOP; + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && + iface->cur_msg + 1 < iface->msg_num) { + if (iface->pmsg[iface->cur_msg + 1].flags & + I2C_M_RD) { + writeValue = ioread16(iface->regs_base + ADI_TWI_MSTRCTL) | MDIR; + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + } else { + writeValue = ioread16(iface->regs_base + ADI_TWI_MSTRCTL) & ~MDIR; + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + } + } + } + /* Transmit next data */ + while (iface->writeNum > 0 && + (ioread16(iface->regs_base + ADI_TWI_FIFOSTAT) & XMTSTAT) != XMT_FULL) { + iowrite16(*(iface->transPtr++), iface->regs_base + ADI_TWI_TXDATA8); + iface->writeNum--; + } + } + if (twi_int_status & RCVSERV) { + while (iface->readNum > 0 && + (ioread16(iface->regs_base + ADI_TWI_FIFOSTAT) & RCVSTAT)) { + /* Receive next data */ + *(iface->transPtr) = ioread16(iface->regs_base + ADI_TWI_RXDATA8); + if (iface->cur_mode == TWI_I2C_MODE_COMBINED) { + /* Change combine mode into sub mode after + * read first data. + */ + iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; + /* Get read number from first byte in block + * combine mode. + */ + if (iface->readNum == 1 && iface->manual_stop) + iface->readNum = *iface->transPtr + 1; + } + iface->transPtr++; + iface->readNum--; + } + + if (iface->readNum == 0) { + if (iface->manual_stop) { + /* Temporary workaround to avoid possible bus stall - + * Flush FIFO before issuing the STOP condition + */ + ioread16(iface->regs_base + ADI_TWI_RXDATA16); + writeValue = ioread16(iface->regs_base + ADI_TWI_MSTRCTL) | STOP; + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && + iface->cur_msg + 1 < iface->msg_num) { + if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD) { + writeValue = ioread16(iface->regs_base + ADI_TWI_MSTRCTL) | MDIR; + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + } else { + writeValue = ioread16(iface->regs_base + ADI_TWI_MSTRCTL) & ~MDIR; + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + } + } + } + } + if (twi_int_status & MERR) { + iowrite16(0, iface->regs_base + ADI_TWI_IMSK); + iowrite16(0x3e, iface->regs_base + ADI_TWI_MSTRSTAT); + iowrite16(0, iface->regs_base + ADI_TWI_MSTRCTL); + iface->result = -EIO; + + if (mast_stat & LOSTARB) + dev_dbg(&iface->adap.dev, "Lost Arbitration\n"); + if (mast_stat & ANAK) + dev_dbg(&iface->adap.dev, "Address Not Acknowledged\n"); + if (mast_stat & DNAK) + dev_dbg(&iface->adap.dev, "Data Not Acknowledged\n"); + if (mast_stat & BUFRDERR) + dev_dbg(&iface->adap.dev, "Buffer Read Error\n"); + if (mast_stat & BUFWRERR) + dev_dbg(&iface->adap.dev, "Buffer Write Error\n"); + + /* Faulty slave devices, may drive SDA low after a transfer + * finishes. To release the bus this code generates up to 9 + * extra clocks until SDA is released. + */ + + if (ioread16(iface->regs_base + ADI_TWI_MSTRSTAT) & SDASEN) { + int cnt = 9; + + do { + iowrite16(SCLOVR, iface->regs_base + ADI_TWI_MSTRCTL); + udelay(6); + iowrite16(0, iface->regs_base + ADI_TWI_MSTRCTL); + udelay(6); + } while ((ioread16(iface->regs_base + ADI_TWI_MSTRSTAT) & SDASEN) && cnt--); + + iowrite16(SDAOVR | SCLOVR, iface->regs_base + ADI_TWI_MSTRCTL); + udelay(6); + iowrite16(SDAOVR, iface->regs_base + ADI_TWI_MSTRCTL); + udelay(6); + iowrite16(0, iface->regs_base + ADI_TWI_MSTRCTL); + } + + /* If it is a quick transfer, only address without data, + * not an err, return 1. + */ + if (iface->cur_mode == TWI_I2C_MODE_STANDARD && + iface->transPtr == NULL && + (twi_int_status & MCOMP) && (mast_stat & DNAK)) + iface->result = 1; + + if (!polling) + complete(&iface->complete); + return; + } + if (twi_int_status & MCOMP) { + fifo_status = ioread16(iface->regs_base + ADI_TWI_FIFOSTAT); + + if (fifo_status & (XMTSTAT | RCVSTAT) && + (ioread16(iface->regs_base + ADI_TWI_MSTRCTL) & MEN) == 0 && + (iface->cur_mode == TWI_I2C_MODE_REPEAT || + iface->cur_mode == TWI_I2C_MODE_COMBINED)) { + iface->result = -1; + iowrite16(0, iface->regs_base + ADI_TWI_IMSK); + iowrite16(0, iface->regs_base + ADI_TWI_MSTRCTL); + } else if (iface->cur_mode == TWI_I2C_MODE_COMBINED) { + if (iface->readNum == 0) { + /* set the read number to 1 and ask for manual + * stop in block combine mode + */ + iface->readNum = 1; + iface->manual_stop = 1; + writeValue = ioread16(iface->regs_base + ADI_TWI_MSTRCTL) | (0xff << 6); + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + } else { + /* set the readd number in other + * combine mode. + */ + writeValue = (ioread16(iface->regs_base + ADI_TWI_MSTRCTL) + & (~(0xff << 6))) | (iface->readNum << 6); + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + } + /* remove restart bit and enable master receive */ + writeValue = ioread16(iface->regs_base + ADI_TWI_MSTRCTL) & ~RSTART; + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && + iface->cur_msg + 1 < iface->msg_num) { + iface->cur_msg++; + iface->transPtr = iface->pmsg[iface->cur_msg].buf; + iface->writeNum = iface->readNum = + iface->pmsg[iface->cur_msg].len; + /* Set Transmit device address */ + iowrite16(iface->pmsg[iface->cur_msg].addr, iface->regs_base + ADI_TWI_MSTRADDR); + if (iface->pmsg[iface->cur_msg].flags & I2C_M_RD) { + iface->read_write = I2C_SMBUS_READ; + } else { + iface->read_write = I2C_SMBUS_WRITE; + /* Transmit first data */ + if (iface->writeNum > 0) { + iowrite16(*(iface->transPtr++), iface->regs_base + ADI_TWI_TXDATA8); + iface->writeNum--; + } + } + + if (iface->pmsg[iface->cur_msg].len < 255) { + writeValue = (ioread16(iface->regs_base + ADI_TWI_MSTRCTL) + & (~(0xff << 6))) + | (iface->pmsg[iface->cur_msg].len << 6); + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + iface->manual_stop = 0; + } else { + writeValue = (ioread16(iface->regs_base + ADI_TWI_MSTRCTL) | (0xff << 6)); + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + iface->manual_stop = 1; + } + /* remove restart bit before last message */ + if (iface->cur_msg + 1 == iface->msg_num) { + writeValue = ioread16(iface->regs_base + ADI_TWI_MSTRCTL) & ~RSTART; + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + } + } else { + iface->result = 1; + iowrite16(0, iface->regs_base + ADI_TWI_IMSK); + iowrite16(0, iface->regs_base + ADI_TWI_MSTRCTL); + } + if (!polling) + complete(&iface->complete); + } +} + +/* Interrupt handler */ +static irqreturn_t adi_twi_handle_all_interrupts(struct adi_twi_iface *iface, + bool polling) +{ + irqreturn_t handled = IRQ_NONE; + unsigned short twi_int_status; + + while (1) { + twi_int_status = ioread16(iface->regs_base + ADI_TWI_ISTAT); + if (!twi_int_status) + return handled; + /* Clear interrupt status */ + iowrite16(twi_int_status, iface->regs_base + ADI_TWI_ISTAT); + adi_twi_handle_interrupt(iface, twi_int_status, polling); + handled = IRQ_HANDLED; + } +} + +static irqreturn_t adi_twi_interrupt_entry(int irq, void *dev_id) +{ + struct adi_twi_iface *iface = dev_id; + unsigned long flags; + irqreturn_t handled; + + spin_lock_irqsave(&iface->lock, flags); + handled = adi_twi_handle_all_interrupts(iface, false); + spin_unlock_irqrestore(&iface->lock, flags); + return handled; +} + +/* + * One i2c master transfer + */ +static int adi_twi_do_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num, bool polling) +{ + struct adi_twi_iface *iface = adap->algo_data; + struct i2c_msg *pmsg; + int rc = 0; + u16 writeValue; + + if (!(ioread16(iface->regs_base + ADI_TWI_CTL) & TWI_ENA)) + return -ENXIO; + + if (ioread16(iface->regs_base + ADI_TWI_MSTRSTAT) & BUSBUSY) + return -EAGAIN; + + iface->pmsg = msgs; + iface->msg_num = num; + iface->cur_msg = 0; + + pmsg = &msgs[0]; + if (pmsg->flags & I2C_M_TEN) { + dev_err(&adap->dev, "10 bits addr not supported!\n"); + return -EINVAL; + } + + iface->cur_mode = (iface->msg_num > 1) ? TWI_I2C_MODE_REPEAT : TWI_I2C_MODE_STANDARD; + iface->manual_stop = 0; + iface->transPtr = pmsg->buf; + iface->writeNum = iface->readNum = pmsg->len; + iface->result = 0; + + if (!polling) + init_completion(&(iface->complete)); + /* Set Transmit device address */ + iowrite16(pmsg->addr, iface->regs_base + ADI_TWI_MSTRADDR); + + /* FIFO Initiation. Data in FIFO should be + * discarded before start a new operation. + */ + iowrite16(0x3, iface->regs_base + ADI_TWI_FIFOCTL); + iowrite16(0, iface->regs_base + ADI_TWI_FIFOCTL); + + if (pmsg->flags & I2C_M_RD) { + iface->read_write = I2C_SMBUS_READ; + } else { + iface->read_write = I2C_SMBUS_WRITE; + /* Transmit first data */ + if (iface->writeNum > 0) { + iowrite16(*(iface->transPtr++), iface->regs_base + ADI_TWI_TXDATA8); + iface->writeNum--; + } + } + + /* clear int stat */ + iowrite16(MCOMP | MERR | XMTSERV | RCVSERV, iface->regs_base + ADI_TWI_ISTAT); + + /* Interrupt mask . Enable XMT, RCV interrupt */ + iowrite16(MCOMP | MERR | XMTSERV | RCVSERV, iface->regs_base + ADI_TWI_IMSK); + + if (pmsg->len < 255) { + iowrite16(pmsg->len << 6, iface->regs_base + ADI_TWI_MSTRCTL); + } else { + iowrite16(0xff << 6, iface->regs_base + ADI_TWI_MSTRCTL); + iface->manual_stop = 1; + } + + /* Master enable */ + writeValue = ioread16(iface->regs_base + ADI_TWI_MSTRCTL) | MEN | + ((iface->msg_num > 1) ? RSTART : 0) | + ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) | + ((iface->twi_clk > 100) ? FAST : 0); + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + + if (polling) { + int timeout = 50000; + + for (;;) { + irqreturn_t handled = adi_twi_handle_all_interrupts( + iface, true); + if (handled == IRQ_HANDLED && iface->result) + break; + if (--timeout == 0) { + iface->result = -1; + dev_err(&adap->dev, "master polling timeout"); + break; + } + } + } else { /* interrupt driven */ + while (!iface->result) { + if (!wait_for_completion_timeout(&iface->complete, + adap->timeout)) { + iface->result = -1; + dev_err(&adap->dev, "master transfer timeout"); + } + } + } + + if (iface->result == 1) + rc = iface->cur_msg + 1; + else + rc = iface->result; + + return rc; +} + +/* + * Generic i2c master transfer entrypoint + */ +static int adi_twi_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + return adi_twi_do_master_xfer(adap, msgs, num, false); +} + +static int adi_twi_master_xfer_atomic(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + return adi_twi_do_master_xfer(adap, msgs, num, true); +} + +#ifndef TWI_USE_EMULATED_SMBUS +/* + * One I2C SMBus transfer + */ +static int adi_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data, + bool polling) +{ + struct adi_twi_iface *iface = adap->algo_data; + int rc = 0; + u16 writeValue; + + if (!(ioread16(iface->regs_base + ADI_TWI_CTL) & TWI_ENA)) + return -ENXIO; + + if (ioread16(iface->regs_base + ADI_TWI_MSTRSTAT) & BUSBUSY) + return -EAGAIN; + + iface->writeNum = 0; + iface->readNum = 0; + + /* Prepare datas & select mode */ + switch (size) { + case I2C_SMBUS_QUICK: + iface->transPtr = NULL; + iface->cur_mode = TWI_I2C_MODE_STANDARD; + break; + case I2C_SMBUS_BYTE: + if (data == NULL) { + iface->transPtr = NULL; + } else { + if (read_write == I2C_SMBUS_READ) + iface->readNum = 1; + else + iface->writeNum = 1; + iface->transPtr = &data->byte; + } + iface->cur_mode = TWI_I2C_MODE_STANDARD; + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) { + iface->readNum = 1; + iface->cur_mode = TWI_I2C_MODE_COMBINED; + } else { + iface->writeNum = 1; + iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; + } + iface->transPtr = &data->byte; + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) { + iface->readNum = 2; + iface->cur_mode = TWI_I2C_MODE_COMBINED; + } else { + iface->writeNum = 2; + iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; + } + iface->transPtr = (u8 *)&data->word; + break; + case I2C_SMBUS_PROC_CALL: + iface->writeNum = 2; + iface->readNum = 2; + iface->cur_mode = TWI_I2C_MODE_COMBINED; + iface->transPtr = (u8 *)&data->word; + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + iface->readNum = 0; + iface->cur_mode = TWI_I2C_MODE_COMBINED; + } else { + iface->writeNum = data->block[0] + 1; + iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; + } + iface->transPtr = data->block; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + iface->readNum = data->block[0]; + iface->cur_mode = TWI_I2C_MODE_COMBINED; + } else { + iface->writeNum = data->block[0]; + iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; + } + iface->transPtr = (u8 *)&data->block[1]; + break; + default: + return -1; + } + + iface->result = 0; + iface->manual_stop = 0; + iface->read_write = read_write; + iface->command = command; + if (!polling) + init_completion(&(iface->complete)); + + /* FIFO Initiation. Data in FIFO should be discarded before + * start a new operation. + */ + iowrite16(0x3, iface->regs_base + ADI_TWI_FIFOCTL); + iowrite16(0, iface->regs_base + ADI_TWI_FIFOCTL); + + /* clear int stat */ + iowrite16(MERR | MCOMP | XMTSERV | RCVSERV, iface->regs_base + ADI_TWI_ISTAT); + + /* Set Transmit device address */ + iowrite16(addr, iface->regs_base + ADI_TWI_MSTRADDR); + + switch (iface->cur_mode) { + case TWI_I2C_MODE_STANDARDSUB: + iowrite16(iface->command, iface->regs_base + ADI_TWI_TXDATA8); + + writeValue = MCOMP | MERR; + if (iface->read_write == I2C_SMBUS_READ) + writeValue |= RCVSERV; + else + writeValue |= XMTSERV; + + iowrite16(writeValue, iface->regs_base + ADI_TWI_IMSK); + + if (iface->writeNum < 255) { + iowrite16((iface->writeNum + 1) << 6, iface->regs_base + ADI_TWI_MSTRCTL); + } else { + iowrite16(0xff << 6, iface->regs_base + ADI_TWI_MSTRCTL); + iface->manual_stop = 1; + } + /* Master enable */ + writeValue = ioread16(iface->regs_base + ADI_TWI_MSTRCTL) | MEN; + if (iface->twi_clk > 100) + writeValue |= FAST; + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + break; + case TWI_I2C_MODE_COMBINED: + iowrite16(iface->command, iface->regs_base + ADI_TWI_TXDATA8); + iowrite16(MCOMP | MERR | RCVSERV | XMTSERV, iface->regs_base + ADI_TWI_IMSK); + + if (iface->writeNum > 0) + iowrite16((iface->writeNum + 1) << 6, iface->regs_base + ADI_TWI_MSTRCTL); + else + iowrite16(0x1 << 6, iface->regs_base + ADI_TWI_MSTRCTL); + /* Master enable */ + writeValue = ioread16(iface->regs_base + ADI_TWI_MSTRCTL) | MEN | RSTART; + if (iface->twi_clk > 100) + writeValue |= FAST; + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + break; + default: + iowrite16(0, iface->regs_base + ADI_TWI_MSTRCTL); + if (size != I2C_SMBUS_QUICK) { + /* Don't access xmit data register when this is a + * read operation. + */ + if (iface->read_write != I2C_SMBUS_READ) { + if (iface->writeNum > 0) { + iowrite16(*(iface->transPtr++), + iface->regs_base + ADI_TWI_TXDATA8); + if (iface->writeNum < 255) { + iowrite16(iface->writeNum << 6, + iface->regs_base + ADI_TWI_MSTRCTL); + } else { + iowrite16(0xff << 6, iface->regs_base + ADI_TWI_MSTRCTL); + iface->manual_stop = 1; + } + iface->writeNum--; + } else { + iowrite16(iface->command, iface->regs_base + ADI_TWI_TXDATA8); + iowrite16(1 << 6, iface->regs_base + ADI_TWI_MSTRCTL); + } + } else { + if (iface->readNum > 0 && iface->readNum < 255) { + iowrite16(iface->readNum << 6, + iface->regs_base + ADI_TWI_MSTRCTL); + } else if (iface->readNum >= 255) { + iowrite16(0xff << 6, iface->regs_base + ADI_TWI_MSTRCTL); + iface->manual_stop = 1; + } else { + break; + } + } + } + writeValue = MCOMP | MERR; + if (iface->read_write == I2C_SMBUS_READ) + writeValue |= RCVSERV; + else + writeValue |= XMTSERV; + iowrite16(writeValue, iface->regs_base + ADI_TWI_IMSK); + + /* Master enable */ + writeValue = ioread16(iface->regs_base + ADI_TWI_MSTRCTL) | MEN | + ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) | + ((iface->twi_clk > 100) ? FAST : 0); + iowrite16(writeValue, iface->regs_base + ADI_TWI_MSTRCTL); + break; + } + + if (polling) { + int timeout = 50000; + + for (;;) { + irqreturn_t handled = adi_twi_handle_all_interrupts( + iface, true); + if (handled == IRQ_HANDLED && iface->result) + break; + if (--timeout == 0) { + iface->result = -1; + dev_err(&adap->dev, "smbus polling timeout"); + break; + } + } + } else { /* interrupt driven */ + while (!iface->result) { + if (!wait_for_completion_timeout(&iface->complete, + adap->timeout)) { + iface->result = -1; + dev_err(&adap->dev, "smbus transfer timeout"); + } + } + } + + rc = (iface->result >= 0) ? 0 : -1; + + return rc; +} + +/* + * Generic I2C SMBus transfer entrypoint + */ +static int adi_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + return adi_twi_do_smbus_xfer(adap, addr, flags, + read_write, command, size, data, false); +} + +static int adi_twi_smbus_xfer_atomic(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + return adi_twi_do_smbus_xfer(adap, addr, flags, + read_write, command, size, data, true); +} +#endif /* TWI_USE_EMULATED_SMBUS */ + +/* + * Return what the adapter supports + */ +static u32 adi_twi_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL | + I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static const struct i2c_algorithm adi_twi_algorithm = { + .master_xfer = adi_twi_master_xfer, + .master_xfer_atomic = adi_twi_master_xfer_atomic, +#ifndef TWI_USE_EMULATED_SMBUS + .smbus_xfer = adi_twi_smbus_xfer, + .smbus_xfer_atomic = adi_twi_smbus_xfer_atomic, +#endif /* TWI_USE_EMULATED_SMBUS */ + .functionality = adi_twi_functionality, +}; + +#ifdef CONFIG_PM_SLEEP +static int i2c_adi_twi_suspend(struct device *dev) +{ + struct adi_twi_iface *iface = dev_get_drvdata(dev); + + iface->saved_clkdiv = ioread16(iface->regs_base + ADI_TWI_CLKDIV); + iface->saved_control = ioread16(iface->regs_base + ADI_TWI_CTL); + + free_irq(iface->irq, iface); + + /* Disable TWI */ + iowrite16(iface->saved_control & ~TWI_ENA, iface->regs_base + ADI_TWI_CTL); + + return 0; +} + +static int i2c_adi_twi_resume(struct device *dev) +{ + struct adi_twi_iface *iface = dev_get_drvdata(dev); + + int rc = request_irq(iface->irq, adi_twi_interrupt_entry, + 0, to_platform_device(dev)->name, iface); + + if (rc) { + dev_err(dev, "Can't get IRQ %d !\n", iface->irq); + return -ENODEV; + } + + /* Resume TWI interface clock as specified */ + iowrite16(iface->saved_clkdiv, iface->regs_base + ADI_TWI_CLKDIV); + + /* Resume TWI */ + iowrite16(iface->saved_control, iface->regs_base + ADI_TWI_CTL); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(i2c_adi_twi_pm, + i2c_adi_twi_suspend, i2c_adi_twi_resume); +#define I2C_ADI_TWI_PM_OPS (&i2c_adi_twi_pm) +#else +#define I2C_ADI_TWI_PM_OPS NULL +#endif + +#ifdef CONFIG_OF +static const struct of_device_id adi_twi_of_match[] = { + { + .compatible = "adi,twi", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, adi_twi_of_match); +#endif + +static int i2c_adi_twi_probe(struct platform_device *pdev) +{ + struct adi_twi_iface *iface; + struct i2c_adapter *p_adap; + struct resource *res; + const struct of_device_id *match; + struct device_node *node = pdev->dev.of_node; + int rc; + unsigned int clkhilow; + u16 writeValue; + + iface = devm_kzalloc(&pdev->dev, sizeof(*iface), GFP_KERNEL); + if (!iface) + return -ENOMEM; + + spin_lock_init(&(iface->lock)); + + match = of_match_device(of_match_ptr(adi_twi_of_match), &pdev->dev); + if (match) { + if (of_property_read_u32(node, "clock-khz", + &iface->twi_clk)) + iface->twi_clk = 50; + } else { + iface->twi_clk = CONFIG_I2C_ADI_TWI_CLK_KHZ; + } + + iface->sclk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(iface->sclk)) { + if (PTR_ERR(iface->sclk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Missing i2c clock\n"); + return PTR_ERR(iface->sclk); + } + + /* Find and map our resources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + return -ENOENT; + } + + iface->regs_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(iface->regs_base)) { + dev_err(&pdev->dev, "Cannot map IO\n"); + return PTR_ERR(iface->regs_base); + } + + iface->irq = platform_get_irq(pdev, 0); + if (iface->irq < 0) { + dev_err(&pdev->dev, "No IRQ specified\n"); + return -ENOENT; + } + + p_adap = &iface->adap; + p_adap->nr = pdev->id; + strscpy(p_adap->name, pdev->name, sizeof(p_adap->name)); + p_adap->algo = &adi_twi_algorithm; + p_adap->algo_data = iface; + p_adap->class = I2C_CLASS_DEPRECATED; + p_adap->dev.parent = &pdev->dev; + p_adap->dev.of_node = node; + p_adap->timeout = 5 * HZ; + p_adap->retries = 3; + + rc = devm_request_irq(&pdev->dev, iface->irq, adi_twi_interrupt_entry, + 0, pdev->name, iface); + if (rc) { + dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq); + rc = -ENODEV; + goto out_error; + } + + /* Set TWI internal clock as 10MHz */ + clk_prepare_enable(iface->sclk); + if (rc) { + dev_err(&pdev->dev, "Could not enable sclk\n"); + goto out_error; + } + + writeValue = ((clk_get_rate(iface->sclk) / 1000 / 1000 + 5) / 10) & 0x7F; + iowrite16(writeValue, iface->regs_base + ADI_TWI_CTL); + + /* + * We will not end up with a CLKDIV=0 because no one will specify + * 20kHz SCL or less in Kconfig now. (5 * 1000 / 20 = 250) + */ + clkhilow = ((10 * 1000 / iface->twi_clk) + 1) / 2; + + /* Set Twi interface clock as specified */ + writeValue = (clkhilow << 8) | clkhilow; + iowrite16(writeValue, iface->regs_base + ADI_TWI_CLKDIV); + + /* Enable TWI */ + writeValue = ioread16(iface->regs_base + ADI_TWI_CTL) | TWI_ENA; + iowrite16(writeValue, iface->regs_base + ADI_TWI_CTL); + + rc = i2c_add_numbered_adapter(p_adap); + if (rc < 0) + goto disable_clk; + + platform_set_drvdata(pdev, iface); + + dev_info(&pdev->dev, "ADI on-chip I2C TWI Controller, regs_base@%p\n", + iface->regs_base); + + return 0; + +disable_clk: + clk_disable_unprepare(iface->sclk); + +out_error: + return rc; +} + +static void i2c_adi_twi_remove(struct platform_device *pdev) +{ + struct adi_twi_iface *iface = platform_get_drvdata(pdev); + + clk_disable_unprepare(iface->sclk); + i2c_del_adapter(&(iface->adap)); +} + +static struct platform_driver i2c_adi_twi_driver = { + .probe = i2c_adi_twi_probe, + .remove = i2c_adi_twi_remove, + .driver = { + .name = "i2c-adi-twi", + .pm = I2C_ADI_TWI_PM_OPS, + .of_match_table = of_match_ptr(adi_twi_of_match), + }, +}; + +static int __init i2c_adi_twi_init(void) +{ + return platform_driver_register(&i2c_adi_twi_driver); +} + +static void __exit i2c_adi_twi_exit(void) +{ + platform_driver_unregister(&i2c_adi_twi_driver); +} + +subsys_initcall(i2c_adi_twi_init); +module_exit(i2c_adi_twi_exit); + +MODULE_AUTHOR("Bryan Wu, Sonic Zhang"); +MODULE_DESCRIPTION("ADI on-chip I2C TWI Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:i2c-adi-twi"); diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 9f5d5ebb865374..2415032624e878 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -475,6 +475,15 @@ config MCP4922 To compile this driver as a module, choose M here: the module will be called mcp4922. +config ADI_PWM_DAC + tristate "Analog Devices PWM DAC" + depends on OF + help + Say yes here to build support for using Analog Devices PWM as a DAC. + + To compile this driver as a module, choose M here: the module will be + called adi-pwm-dac. + config STM32_DAC tristate "STMicroelectronics STM32 DAC" depends on (ARCH_STM32 && OF) || COMPILE_TEST diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 56a125f56284f1..817c078196a5d9 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_MCP4725) += mcp4725.o obj-$(CONFIG_MCP4728) += mcp4728.o obj-$(CONFIG_MCP4821) += mcp4821.o obj-$(CONFIG_MCP4922) += mcp4922.o +obj-$(CONFIG_ADI_PWM_DAC) += adi-pwm-dac.o obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o obj-$(CONFIG_STM32_DAC) += stm32-dac.o obj-$(CONFIG_TI_DAC082S085) += ti-dac082s085.o diff --git a/drivers/iio/dac/adi-pwm-dac.c b/drivers/iio/dac/adi-pwm-dac.c new file mode 100644 index 00000000000000..3904e0eed1922a --- /dev/null +++ b/drivers/iio/dac/adi-pwm-dac.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define PWM_CONTROL_REG 0x40 +#define PWM_CLK_DIV_REG 0x44 +#define PWM_AVRG_READY_REG 0x48 + +#define ADI_PWM_DAC_NUM_CHANNELS 16 + +struct adi_pwm_dac { + void __iomem *base; + struct mutex lock; + unsigned long input_clk_rate; + u32 iovdd_microvolt; + u32 gpio_max_frequency; +}; + +#define ADI_PWM_DAC_CHANNEL(chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = chan, \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_ENABLE), \ + .info_mask_shared_by_all = \ + BIT(IIO_CHAN_INFO_FREQUENCY), \ +} + +static const struct iio_chan_spec adi_pwm_dac_channels[] = { + ADI_PWM_DAC_CHANNEL(0), + ADI_PWM_DAC_CHANNEL(1), + ADI_PWM_DAC_CHANNEL(2), + ADI_PWM_DAC_CHANNEL(3), + ADI_PWM_DAC_CHANNEL(4), + ADI_PWM_DAC_CHANNEL(5), + ADI_PWM_DAC_CHANNEL(6), + ADI_PWM_DAC_CHANNEL(7), + ADI_PWM_DAC_CHANNEL(8), + ADI_PWM_DAC_CHANNEL(9), + ADI_PWM_DAC_CHANNEL(10), + ADI_PWM_DAC_CHANNEL(11), + ADI_PWM_DAC_CHANNEL(12), + ADI_PWM_DAC_CHANNEL(13), + ADI_PWM_DAC_CHANNEL(14), + ADI_PWM_DAC_CHANNEL(15), +}; + +static int adi_pwm_dac_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct adi_pwm_dac *const dac = iio_priv(indio_dev); + u32 reg; + u64 tmp; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + reg = readl(dac->base + chan->channel * 4); + tmp = dac->iovdd_microvolt; + tmp = tmp * reg / 65521; + *val = (int)tmp; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_ENABLE: + reg = readl(dac->base + PWM_CONTROL_REG); + *val = (reg >> chan->channel) & 0x1; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_FREQUENCY: + reg = readl(dac->base + PWM_CLK_DIV_REG); + *val = dac->input_clk_rate / (reg + 1); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int adi_pwm_dac_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct adi_pwm_dac *const dac = iio_priv(indio_dev); + u32 reg; + u64 tmp; + unsigned long timenow; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + /* The required analog voltage should not exceed IOVDD */ + if (val > dac->iovdd_microvolt) + return -EINVAL; + + tmp = val; + tmp = tmp * 65521 / dac->iovdd_microvolt; + + /* + * Wait until the AVRG register is ready to be written or + * timeout. One second should be good for timeout value. + */ + timenow = jiffies; + do + reg = readl(dac->base + PWM_AVRG_READY_REG); + while ((1UL & (reg >> chan->channel)) == 0 + && time_before(jiffies, timenow + HZ)); + + if ((1UL & (reg >> chan->channel)) == 0) + return -EBUSY; + + reg = (u32)tmp; + writel(reg, dac->base + chan->channel * 4); + return 0; + + case IIO_CHAN_INFO_ENABLE: + if (val != 0 && val != 1) + return -EINVAL; + + mutex_lock(&dac->lock); + + reg = readl(dac->base + PWM_CONTROL_REG); + if (val == 0) + reg &= ~((u32)1 << chan->channel); + else /* val == 1 */ + reg |= (u32)1 << chan->channel; + + writel(reg, dac->base + PWM_CONTROL_REG); + + mutex_unlock(&dac->lock); + + return 0; + + case IIO_CHAN_INFO_FREQUENCY: + if (val > dac->gpio_max_frequency) + return -EINVAL; + + reg = dac->input_clk_rate / val - 1; + writel(reg, dac->base + PWM_CLK_DIV_REG); + return 0; + + default: + return -EINVAL; + } +} + +static const struct iio_info adi_pwm_dac_info = { + .read_raw = adi_pwm_dac_read_raw, + .write_raw = adi_pwm_dac_write_raw +}; + + +static int adi_pwm_dac_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct adi_pwm_dac *dac; + struct clk *input_clk; + int i, ret; + u32 val; + + if (!np) + return -ENODEV; + + input_clk = devm_clk_get(dev, NULL); + if (IS_ERR(input_clk)) { + dev_err(dev, "cannot get input clock"); + return PTR_ERR(input_clk); + } + + indio_dev = devm_iio_device_alloc(dev, sizeof(*dac)); + if (!indio_dev) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + dac = iio_priv(indio_dev); + + mutex_init(&dac->lock); + + indio_dev->info = &adi_pwm_dac_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = adi_pwm_dac_channels; + indio_dev->num_channels = ADI_PWM_DAC_NUM_CHANNELS; + indio_dev->name = dev_name(dev); + + dac->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dac->base)) + return PTR_ERR(dac->base); + + dac->input_clk_rate = clk_get_rate(input_clk); + + ret = of_property_read_u32(dev->of_node, "adi,iovdd-microvolt", + &val); + if (ret != 0) { + dev_err(dev, "Missing or bad adi,iovdd_microvolt property\n"); + return -EINVAL; + } + dac->iovdd_microvolt = val; + + ret = of_property_read_u32(dev->of_node, "adi,gpio-max-frequency", + &val); + if (ret != 0 || val > dac->input_clk_rate) { + dev_err(dev, + "Missing or bad adi,gpio_max_frequency property\n"); + return -EINVAL; + } + dac->gpio_max_frequency = val; + + /* Disable all channels */ + writel(0, dac->base + PWM_CONTROL_REG); + + for (i = 0; i < ADI_PWM_DAC_NUM_CHANNELS; i++) + writel(0, dac->base + i * 4); + + /* The reset value of PWM_CLK_DIV_REG is 0x7 */ + writel(7, dac->base + PWM_CLK_DIV_REG); + + return iio_device_register(indio_dev); +} + +static void adi_pwm_dac_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct adi_pwm_dac *dac = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + /* Disable all channels */ + writel(0, dac->base + PWM_CONTROL_REG); +} + +static const struct of_device_id adi_pwm_dac_of_match[] = { + { .compatible = "adi,pwm-dac", }, + {}, +}; +MODULE_DEVICE_TABLE(of, adi_pwm_dac_of_match); + +static struct platform_driver adi_pwm_dac_driver = { + .probe = adi_pwm_dac_probe, + .remove = adi_pwm_dac_remove, + .driver = { + .name = "adi-pwm-dac", + .of_match_table = adi_pwm_dac_of_match, + }, +}; +module_platform_driver(adi_pwm_dac_driver); + +MODULE_DESCRIPTION("Analog Devices PWM DAC driver"); +MODULE_AUTHOR("Jie Zhang "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 3fe7e2a9bd294d..84800fbd6ff8cd 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -50,6 +50,16 @@ config AD525X_DPOT_SPI To compile this driver as a module, choose M here: the module will be called ad525x_dpot-spi. +config ADI_TRU + tristate "Analog Devices Trigger Routing Unit (TRU)" + depends on OF + help + Say yes here to build support for Analog Devices Trigger Routing + Unit. + + To compile this driver as a module, choose M here. The module will + be called adi-tru. + config DUMMY_IRQ tristate "Dummy IRQ handler" help @@ -621,6 +631,7 @@ source "drivers/misc/vmw_vmci/Kconfig" source "drivers/misc/genwqe/Kconfig" source "drivers/misc/echo/Kconfig" source "drivers/misc/cxl/Kconfig" +source "drivers/misc/adi/Kconfig" source "drivers/misc/ocxl/Kconfig" source "drivers/misc/bcm-vk/Kconfig" source "drivers/misc/cardreader/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index a9f94525e1819d..b8ab98aba47ecb 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -72,3 +72,5 @@ obj-$(CONFIG_TPS6594_PFSM) += tps6594-pfsm.o obj-$(CONFIG_NSM) += nsm.o obj-$(CONFIG_MARVELL_CN10K_DPI) += mrvl_cn10k_dpi.o obj-y += keba/ +obj-$(CONFIG_ADI_TRU) += adi-tru.o +obj-$(CONFIG_ADI_SRAM_MMAP) += adi/ diff --git a/drivers/misc/adi-tru.c b/drivers/misc/adi-tru.c new file mode 100644 index 00000000000000..75faf058bce2ec --- /dev/null +++ b/drivers/misc/adi-tru.c @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices Trigger Routing Unit (TRU) driver + * + * Copyright 2022 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define TRU_SSR0 0x0 +#define TRU_MTR 0x7e0 +#define TRU_ERRADDR 0x7e8 +#define TRU_STAT 0x7ec +#define TRU_REVID 0x7f0 +#define TRU_GCTL 0x7f4 + +#define TRU_GCTL_EN 0x1 +#define TRU_GCTL_RESET 0x2 + +#define TRU_STAT_LWERR 0x1 +#define TRU_STAT_ADDRERR 0x2 + +#define TRU_SSR_LOCK 0x80000000 + + +static LIST_HEAD(tru_list); +static DEFINE_MUTEX(tru_list_lock); + + +/* Get TRU device by its alias ID */ +struct adi_tru *adi_tru_get(u32 alias_id) +{ + struct adi_tru *tru; + + mutex_lock(&tru_list_lock); + list_for_each_entry(tru, &tru_list, node) { + if (tru->alias_id == alias_id) { + mutex_unlock(&tru_list_lock); + return tru; + } + } + mutex_unlock(&tru_list_lock); + + return NULL; +} +EXPORT_SYMBOL_GPL(adi_tru_get); + +int adi_tru_enable(struct adi_tru *tru) +{ + u32 reg; + + mutex_lock(&tru->lock); + + reg = readl(tru->base + TRU_GCTL); + reg |= TRU_GCTL_EN; + writel(reg, tru->base + TRU_GCTL); + + mutex_unlock(&tru->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(adi_tru_enable); + +int adi_tru_disable(struct adi_tru *tru) +{ + u32 reg; + + mutex_lock(&tru->lock); + + reg = readl(tru->base + TRU_GCTL); + reg &= ~TRU_GCTL_EN; + writel(reg, tru->base + TRU_GCTL); + + mutex_unlock(&tru->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(adi_tru_disable); + +int adi_tru_soft_reset(struct adi_tru *tru) +{ + u32 reg; + + mutex_lock(&tru->lock); + + reg = readl(tru->base + TRU_GCTL); + reg |= TRU_GCTL_RESET; + writel(reg, tru->base + TRU_GCTL); + + mutex_unlock(&tru->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(adi_tru_soft_reset); + +int adi_tru_trigger(struct adi_tru *tru, int n, ...) +{ + va_list ap; + u32 reg, source; + int i; + + if (n < 0 || n > 4) { + dev_err(tru->dev, "Invalid number of arguments"); + return -EINVAL; + } + + va_start(ap, n); + + reg = 0; + for (i = 0; i < n; i++) { + source = va_arg(ap, u32); + reg |= source << (i * 8); + } + + writel(reg, tru->base + TRU_MTR); + + return 0; +} +EXPORT_SYMBOL_GPL(adi_tru_trigger); + +int adi_tru_connect_source_to_target(struct adi_tru *tru, + u32 source, u32 target, bool locked) +{ + u32 reg; + + if (source > tru->last_source_id) { + dev_err(tru->dev, "Invalid TRU source: %d", source); + return -EINVAL; + } + + if (target > tru->last_target_id) { + dev_err(tru->dev, "Invalid TRU target: %d", target); + return -EINVAL; + } + + mutex_lock(&tru->lock); + + reg = readl(tru->base + target * 4); + if (reg & TRU_SSR_LOCK) { + mutex_unlock(&tru->lock); + return -EINVAL; + } + + if (locked) + source |= TRU_SSR_LOCK; + + writel(source, tru->base + target * 4); + + mutex_unlock(&tru->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(adi_tru_connect_source_to_target); + +/* + * sysfs interface for TRU + * + * ssr \ + * + ssr0 (RW) + * + ssr1 (RW) + * + ssr2 (RW) + * ... + * mtr (RW) + * enable (RW) + * status (RW) + * reset (WO) + */ +static ssize_t mtr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adi_tru *tru = dev_get_drvdata(dev); + u32 mtr = readl(tru->base + TRU_MTR); + + /* TODO a better way is to output four trigger source IDs */ + return scnprintf(buf, PAGE_SIZE, "0x%08x\n", mtr); +} + +static ssize_t mtr_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adi_tru *tru = dev_get_drvdata(dev); + unsigned long value; + int err; + + /* TODO a better way is to input four trigger source IDs */ + err = kstrtoul(buf, 0, &value); + if (err) + return err; + + writel(value, tru->base + TRU_MTR); + + return count; +} + +static DEVICE_ATTR_RW(mtr); + +static ssize_t enable_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adi_tru *tru = dev_get_drvdata(dev); + u32 gctl = readl(tru->base + TRU_GCTL); + + return scnprintf(buf, PAGE_SIZE, "%d\n", (gctl & TRU_GCTL_EN) ? 1 : 0); +} + +static ssize_t enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adi_tru *tru = dev_get_drvdata(dev); + unsigned long value; + int err; + + err = kstrtoul(buf, 0, &value); + if (err) + return err; + + if (value == 0) + err = adi_tru_disable(tru); + else if (value == 1) + err = adi_tru_enable(tru); + else + return -EINVAL; + + return err ? err : count; +} + +static DEVICE_ATTR_RW(enable); + +static ssize_t reset_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adi_tru *tru = dev_get_drvdata(dev); + unsigned long value; + int err; + + err = kstrtoul(buf, 0, &value); + if (err) + return err; + + if (value == 0) + err = 0; // do nothing + else if (value == 1) + err = adi_tru_soft_reset(tru); + else + return -EINVAL; + + return err ? err : count; +} + +static DEVICE_ATTR_WO(reset); + +static ssize_t status_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adi_tru *tru = dev_get_drvdata(dev); + u32 stat; + bool addrerr, lwerr; + + stat = readl(tru->base + TRU_STAT); + addrerr = ((stat & TRU_STAT_ADDRERR) != 0); + lwerr = ((stat & TRU_STAT_LWERR) != 0); + + return scnprintf(buf, PAGE_SIZE, + "%d: %saddress error, %slock write error\n", + stat, + addrerr ? "" : "no ", lwerr ? "" : "no "); +} + +static ssize_t status_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adi_tru *tru = dev_get_drvdata(dev); + unsigned long value; + int err; + + err = kstrtoul(buf, 0, &value); + if (err) + return err; + + writel(value, tru->base + TRU_STAT); + + return count; +} + +static DEVICE_ATTR_RW(status); + +static struct attribute *tru_attrs[] = { + &dev_attr_mtr.attr, + &dev_attr_enable.attr, + &dev_attr_status.attr, + &dev_attr_reset.attr, + NULL, +}; + +static struct attribute_group tru_attribute_group = { + .attrs = tru_attrs, +}; + +static struct device_attribute *ssr_dev_attrs; +static struct attribute **ssr_attrs; +static struct attribute_group ssr_attribute_group = { + .name = "ssr", +}; + +static ssize_t ssr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adi_tru *tru = dev_get_drvdata(dev); + int ssr, source; + u32 reg; + bool locked; + + if (sscanf(attr->attr.name, "ssr%d", &ssr) != 1) + return -EINVAL; + + reg = readl(tru->base + ssr * 4); + source = reg & 0xff; + locked = ((reg & TRU_SSR_LOCK) != 0); + + return scnprintf(buf, PAGE_SIZE, "%d%s\n", source, + locked ? "(locked)" : ""); +} + +static ssize_t ssr_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adi_tru *tru = dev_get_drvdata(dev); + int value; + u32 source, target; + int err; + bool locked = false; + + if (sscanf(attr->attr.name, "ssr%d", &value) != 1) + return -EINVAL; + target = value; + + if (sscanf(buf, "%d", &value) != 1) + return -EINVAL; + if (value < 0) + return -EINVAL; + source = value; + + if (strstr(buf, "locked") != NULL) + locked = true; + + err = adi_tru_connect_source_to_target(tru, source, target, locked); + if (err) + return err; + + return count; +} + +static int adi_tru_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct adi_tru *tru; + struct device *dev; + struct resource *res; + int ret, n, i, id; + + tru = devm_kzalloc(&pdev->dev, sizeof(*tru), GFP_KERNEL); + if (!tru) + return -ENOMEM; + + mutex_init(&tru->lock); + + tru->dev = &pdev->dev; + + dev = tru->dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tru->base = devm_ioremap_resource(dev, res); + if (IS_ERR(tru->base)) + return PTR_ERR(tru->base); + + platform_set_drvdata(pdev, tru); + + id = of_alias_get_id(np, "tru"); + if (id < 0) { + dev_err(tru->dev, + "The alias of tru node should be truN"); + return id; + } + tru->alias_id = id; + + ret = of_property_read_u32(np, "adi,tru-last-source-id", + &tru->last_source_id); + if (ret) { + dev_err(tru->dev, + "Can't find adi,tru-max-source-id in tru node"); + return ret; + } + + ret = of_property_read_u32(np, "adi,tru-last-target-id", + &tru->last_target_id); + if (ret) { + dev_err(tru->dev, + "Can't find adi,tru-last-target-id in tru node"); + return ret; + } + + /* Soft reset all TRU registers */ + writel(TRU_GCTL_RESET, tru->base + TRU_GCTL); + + tru->preset_locked = + of_property_read_bool(np, "adi,tru-connections-preset-locked"); + + /* Configure TRU with the static settings in device tree */ + n = of_property_count_elems_of_size(np, "adi,tru-connections-preset", + sizeof(u32)); + if (n < 0 || n % 2 != 0) + return -EINVAL; + + for (i = 0; i < n / 2; i++) { + u32 source, target; + + ret = of_property_read_u32_index(np, + "adi,tru-connections-preset", + i * 2, &source); + if (ret) + return ret; + + ret = of_property_read_u32_index(np, + "adi,tru-connections-preset", + i * 2 + 1, &target); + if (ret) + return ret; + + ret = adi_tru_connect_source_to_target(tru, source, target, + tru->preset_locked); + if (ret) + return ret; + } + + /* Enable TRU */ + writel(TRU_GCTL_EN, tru->base + TRU_GCTL); + + /* Add to tru to tru_list */ + mutex_lock(&tru_list_lock); + list_add_tail(&tru->node, &tru_list); + mutex_unlock(&tru_list_lock); + + + /* Create sysfs interface files */ + + ret = sysfs_create_group(&dev->kobj, &tru_attribute_group); + if (ret) + return ret; + + ssr_dev_attrs = + devm_kcalloc(dev, tru->last_target_id + 1, + sizeof(struct device_attribute), GFP_KERNEL); + if (ssr_dev_attrs == NULL) + return -ENOMEM; + + ssr_attrs = + devm_kcalloc(dev, tru->last_target_id + 2, + sizeof(struct attribute *), GFP_KERNEL); + if (ssr_attrs == NULL) + return -ENOMEM; + + for (i = 0; i <= tru->last_target_id; i++) { + char *name; + + name = devm_kasprintf(dev, GFP_KERNEL, "ssr%d", i); + if (name == NULL) + return -ENOMEM; + + sysfs_attr_init(&ssr_dev_attrs[i].attr); + ssr_dev_attrs[i].attr.name = name; + ssr_dev_attrs[i].attr.mode = S_IRUGO | S_IWUSR; + ssr_dev_attrs[i].show = ssr_show; + ssr_dev_attrs[i].store = ssr_store; + + ssr_attrs[i] = &ssr_dev_attrs[i].attr; + } + + ssr_attribute_group.attrs = ssr_attrs; + + ret = sysfs_create_group(&dev->kobj, &ssr_attribute_group); + if (ret) + return ret; + + return 0; +} + +static void adi_tru_remove(struct platform_device *pdev) +{ + struct adi_tru *tru = platform_get_drvdata(pdev); + + /* Disable TRU */ + writel(0, tru->base + TRU_GCTL); + + /* Remove tru from tru_list */ + mutex_lock(&tru_list_lock); + list_del(&tru->node); + mutex_unlock(&tru_list_lock); +} + +static const struct of_device_id adi_tru_match_table[] = { + { .compatible = "adi,tru", }, + {} +}; + +MODULE_DEVICE_TABLE(of, adi_tru_match_table); + +static struct platform_driver adi_tru_driver = { + .driver = { + .name = "adi-tru", + .of_match_table = adi_tru_match_table, + }, + .probe = adi_tru_probe, + .remove = adi_tru_remove, +}; + +module_platform_driver(adi_tru_driver); + +MODULE_DESCRIPTION("Analog Devices Trigger Routing Unit (TRU) driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/adi/Kconfig b/drivers/misc/adi/Kconfig new file mode 100644 index 00000000000000..9f03859a2057f5 --- /dev/null +++ b/drivers/misc/adi/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config ADI_SRAM_MMAP + bool "Aarch64 MMAP driver for ADI onchip SRAM" + depends on SRAM + default n + help + Enable the mmap driver for ADI on chip SRAM. + diff --git a/drivers/misc/adi/Makefile b/drivers/misc/adi/Makefile new file mode 100644 index 00000000000000..b94f6bf500b12c --- /dev/null +++ b/drivers/misc/adi/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +obj-$(CONFIG_ADI_SRAM_MMAP) += sram_mmap.o diff --git a/drivers/misc/adi/sram_mmap.c b/drivers/misc/adi/sram_mmap.c new file mode 100644 index 00000000000000..177f6706cf06f9 --- /dev/null +++ b/drivers/misc/adi/sram_mmap.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SRAM mmap misc driver for ADI processor on-chip memory + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SRAM_MMAP_DRV_NAME "sram_mmap" + +struct adi_sram_mmap { + struct miscdevice miscdev; + struct device *dev; + struct page *start; + struct reserved_mem *rmem; +}; + +struct address_space_operations sram_aops = { + .dirty_folio = noop_dirty_folio, +}; + +/** + * For now ignore pgoff supplied by the user and start mapping at + * the start of SRAM + */ +static int sram_mmap(struct file *fp, struct vm_area_struct *vma) +{ + struct adi_sram_mmap *sram = container_of(fp->private_data, + struct adi_sram_mmap, miscdev); + size_t sram_size = vma->vm_end - vma->vm_start; + struct page *start_page; + int pages, i; + int ret = 0; + + if ((vma->vm_pgoff * PAGE_SIZE) + sram_size > sram->rmem->size) { + dev_err(sram->dev, "Tried to map 0x%zx@0x%zx, only 0x%zx available\n", + sram_size, vma->vm_pgoff * PAGE_SIZE, (size_t)sram->rmem->size); + return -ENOMEM; + } + + if (sram_size % PAGE_SIZE) { + dev_err(sram->dev, "Requested mapping is not a multiple of page size, requested 0x%lx bytes\n", sram_size); + return -EINVAL; + } + + fp->f_mapping->a_ops = &sram_aops; + vma->vm_page_prot = __pgprot_modify(vma->vm_page_prot, PTE_ATTRINDX_MASK, + PTE_ATTRINDX(MT_NORMAL) | PTE_PXN | PTE_UXN); + vma->vm_private_data = sram; + vma->vm_ops = NULL; + + start_page = sram->start; + pages = sram_size / PAGE_SIZE; + for (i = 0; i < pages; ++i) { + struct page *page = start_page + vma->vm_pgoff + i; + + if (!page->mapping) + page->mapping = fp->f_mapping; + + ret = vm_insert_page(vma, vma->vm_start + (i * PAGE_SIZE), page); + if (ret) { + dev_err(sram->dev, "Failed to map page to userspace\n"); + return ret; + } + } + + dev_dbg(sram->dev, "Mapped 0x%zx : 0x%zx successfully\n", + (size_t)(sram->rmem->base + vma->vm_pgoff * PAGE_SIZE), + (size_t)(sram->rmem->base + vma->vm_pgoff * PAGE_SIZE + sram_size)); + return 0; +} + +static const struct file_operations sram_fops = { + .mmap = sram_mmap, +}; + +static const struct of_device_id adi_sram_mmap_of_match[] = { + { .compatible = "adi,sram-mmap" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adi_sram_mmap_of_match); + +static int adi_sram_mmap_probe(struct platform_device *pdev) +{ + struct adi_sram_mmap *sram; + struct reserved_mem *rmem; + struct device_node *np; + struct device *dev; + struct page *page; + int ret, pages, i; + + dev = &pdev->dev; + + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_err(dev, "Unable to configure SRAM reserved memory\n"); + return ret; + } + + np = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!np) + return -EINVAL; + + rmem = of_reserved_mem_lookup(np); + if (!rmem) { + dev_err(dev, "SRAM MMAP requires adi,sram-access reserved memory, please check your device tree\n"); + return -ENOENT; + } + + page = pfn_to_page(PFN_DOWN(rmem->base)); + pages = rmem->size / PAGE_SIZE; + + // Grab reference to page so they're never freed back into the allocator + for (i = 0; i < pages; ++i) + set_page_count(page + i, 1); + + sram = devm_kzalloc(dev, sizeof(*sram), GFP_KERNEL); + if (!sram) { + dev_err(dev, "Unable to allocate sram device data\n"); + return -ENOMEM; + } + + sram->dev = dev; + sram->start = page; + sram->rmem = rmem; + dev_set_drvdata(&pdev->dev, sram); + + sram->miscdev.minor = MISC_DYNAMIC_MINOR; + sram->miscdev.name = kasprintf(GFP_KERNEL, "%s.%s", SRAM_MMAP_DRV_NAME, rmem->name); + sram->miscdev.fops = &sram_fops; + sram->miscdev.parent = dev; + + ret = misc_register(&sram->miscdev); + if (ret < 0) + dev_err(dev, "Faied to register sram mmap misc device\n"); + + return ret; +} + +static void adi_sram_mmap_remove(struct platform_device *pdev) +{ + struct adi_sram_mmap *sram = dev_get_drvdata(&pdev->dev); + + misc_deregister(&sram->miscdev); +} + +static struct platform_driver adi_sram_mmap_driver = { + .probe = adi_sram_mmap_probe, + .remove = adi_sram_mmap_remove, + .driver = { + .name = SRAM_MMAP_DRV_NAME, + .of_match_table = of_match_ptr(adi_sram_mmap_of_match), + }, +}; + +static int rmem_sram_init(struct reserved_mem *rmem, struct device *dev) +{ + return 0; +} + +static void rmem_sram_release(struct reserved_mem *rmem, struct device *dev) +{ +} + +static const struct reserved_mem_ops rmem_sram_ops = { + .device_init = rmem_sram_init, + .device_release = rmem_sram_release, +}; + +static int __init rmem_sram_setup(struct reserved_mem *rmem) +{ + if (rmem->base & (PAGE_SIZE - 1)) { + pr_err("sram region starting at 0x%px is not page aligned!\n", (void *)rmem->base); + return -EINVAL; + } + + if (rmem->size & (PAGE_SIZE - 1)) { + pr_err("sram region starting at 0x%px is not a multiple of the page size (requested 0x%llx bytes)\n", + (void *)rmem->base, rmem->size); + return -EINVAL; + } + + rmem->ops = &rmem_sram_ops; + + pr_info("Reserved memory: SRAM at %pa, size %ld KiB\n", + &rmem->base, (unsigned long)(rmem->size / SZ_1K)); + return 0; +} +RESERVEDMEM_OF_DECLARE(adi_sram, "adi,sram-access", rmem_sram_setup); + +module_platform_driver(adi_sram_mmap_driver); +MODULE_DESCRIPTION("SRAM mmap misc driver for ADI processor on-chip memory"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 6a23be214543db..31b54ddba628a5 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1433,7 +1433,14 @@ static int mmc_select_hs400es(struct mmc_card *card) if (host->ops->hs400_enhanced_strobe) host->ops->hs400_enhanced_strobe(host, &host->ios); - err = mmc_switch_status(card, true); + /* + * Workaround: According to JEDEC, it is not reliable to send CMD 13 + * just after switching speed mode (there might be CRC errors). + * Adrv906x suffers this issue in this specific point at high + * temperature conditions, so let's ignore the error in case of CRC + * error. + */ + err = mmc_switch_status(card, false); if (err) goto out_err; diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 7199cb0bd0b9e7..bd2f96ad80a21b 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -23,6 +23,12 @@ config MMC_SUNPLUS If unsure, say N +config MMC_ADI_SYSTEMC + bool "MMC host driver for ADI SystemC" + default n + help + This selects the MMC driver with workarounds for the ADI SystemC eMMC. + config MMC_ARMMMCI tristate "ARM AMBA Multimedia Card Interface support" depends on ARM_AMBA @@ -252,6 +258,16 @@ config MMC_SDHCI_OF_SPARX5 If unsure, say N. +config MMC_SDHCI_OF_ADI + tristate "SDHCI OF support for the ADI SDHCI controller" + depends on MMC_SDHCI_PLTFM + depends on OF + depends on COMMON_CLK + help + This selects the ADI SDHCI controller support + If you have a controller with this interface, say Y or M here. + If unsure, say N. + config MMC_SDHCI_OF_MA35D1 tristate "SDHCI OF support for the MA35D1 SDHCI controller" depends on ARCH_MA35 || COMPILE_TEST diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 3ccffebbe59b91..c23ea1df7c655a 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -4,7 +4,11 @@ # obj-$(CONFIG_MMC_ARMMMCI) += armmmci.o -armmmci-y := mmci.o +ifeq ($(CONFIG_MMC_ADI_SYSTEMC),y) + armmmci-y := mmci_adi_systemc.o +else + armmmci-y := mmci.o +endif armmmci-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o armmmci-$(CONFIG_MMC_STM32_SDMMC) += mmci_stm32_sdmmc.o obj-$(CONFIG_MMC_PXA) += pxamci.o @@ -89,6 +93,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o obj-$(CONFIG_MMC_SDHCI_OF_DWCMSHC) += sdhci-of-dwcmshc.o obj-$(CONFIG_MMC_SDHCI_OF_SPARX5) += sdhci-of-sparx5.o obj-$(CONFIG_MMC_SDHCI_OF_MA35D1) += sdhci-of-ma35d1.o +obj-$(CONFIG_MMC_SDHCI_OF_ADI) += sdhci-of-adi.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o obj-$(CONFIG_MMC_SDHCI_NPCM) += sdhci-npcm.o diff --git a/drivers/mmc/host/mmci_adi_systemc.c b/drivers/mmc/host/mmci_adi_systemc.c new file mode 100644 index 00000000000000..7398813048cb30 --- /dev/null +++ b/drivers/mmc/host/mmci_adi_systemc.c @@ -0,0 +1,2439 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/drivers/mmc/host/mmci.c - ARM PrimeCell MMCI PL180/1 driver + * + * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. + * Copyright (C) 2010 ST-Ericsson SA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "mmci.h" + +#define DRIVER_NAME "mmci-pl18x" + +static void mmci_variant_init(struct mmci_host *host); +static void ux500_variant_init(struct mmci_host *host); +static void ux500v2_variant_init(struct mmci_host *host); +static void adi_systemc_variant_init(struct mmci_host *host); +static void adi_systemc_init_card(struct mmc_host *host, struct mmc_card *card); + +static unsigned int fmax = 515633; + +static struct variant_data variant_arm = { + .fifosize = 16 * 4, + .fifohalfsize = 8 * 4, + .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, + .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, + .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, + .cmdreg_srsp = MCI_CPSM_RESPONSE, + .datalength_bits = 16, + .datactrl_blocksz = 11, + .pwrreg_powerup = MCI_PWR_UP, + .f_max = 100000000, + .reversed_irq_handling = true, + .mmcimask1 = true, + .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_ROD, + .init = mmci_variant_init, +}; + +static struct variant_data variant_arm_extended_fifo = { + .fifosize = 128 * 4, + .fifohalfsize = 64 * 4, + .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, + .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, + .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, + .cmdreg_srsp = MCI_CPSM_RESPONSE, + .datalength_bits = 16, + .datactrl_blocksz = 11, + .pwrreg_powerup = MCI_PWR_UP, + .f_max = 100000000, + .mmcimask1 = true, + .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_ROD, + .init = mmci_variant_init, +}; + +static struct variant_data variant_arm_extended_fifo_hwfc = { + .fifosize = 128 * 4, + .fifohalfsize = 64 * 4, + .clkreg_enable = MCI_ARM_HWFCEN, + .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, + .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, + .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, + .cmdreg_srsp = MCI_CPSM_RESPONSE, + .datalength_bits = 16, + .datactrl_blocksz = 11, + .pwrreg_powerup = MCI_PWR_UP, + .f_max = 100000000, + .mmcimask1 = true, + .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_ROD, + .init = mmci_variant_init, +}; + +static struct variant_data variant_u300 = { + .fifosize = 16 * 4, + .fifohalfsize = 8 * 4, + .clkreg_enable = MCI_ST_U300_HWFCEN, + .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, + .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, + .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, + .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, + .cmdreg_srsp = MCI_CPSM_RESPONSE, + .datalength_bits = 16, + .datactrl_blocksz = 11, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, + .st_sdio = true, + .pwrreg_powerup = MCI_PWR_ON, + .f_max = 100000000, + .signal_direction = true, + .pwrreg_clkgate = true, + .pwrreg_nopower = true, + .mmcimask1 = true, + .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_OD, + .init = mmci_variant_init, +}; + +static struct variant_data variant_nomadik = { + .fifosize = 16 * 4, + .fifohalfsize = 8 * 4, + .clkreg = MCI_CLK_ENABLE, + .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, + .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, + .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, + .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, + .cmdreg_srsp = MCI_CPSM_RESPONSE, + .datalength_bits = 24, + .datactrl_blocksz = 11, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, + .st_sdio = true, + .st_clkdiv = true, + .pwrreg_powerup = MCI_PWR_ON, + .f_max = 100000000, + .signal_direction = true, + .pwrreg_clkgate = true, + .pwrreg_nopower = true, + .mmcimask1 = true, + .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_OD, + .init = mmci_variant_init, +}; + +static struct variant_data variant_ux500 = { + .fifosize = 30 * 4, + .fifohalfsize = 8 * 4, + .clkreg = MCI_CLK_ENABLE, + .clkreg_enable = MCI_ST_UX500_HWFCEN, + .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, + .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE, + .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, + .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, + .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, + .cmdreg_srsp = MCI_CPSM_RESPONSE, + .datalength_bits = 24, + .datactrl_blocksz = 11, + .datactrl_any_blocksz = true, + .dma_power_of_2 = true, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, + .st_sdio = true, + .st_clkdiv = true, + .pwrreg_powerup = MCI_PWR_ON, + .f_max = 100000000, + .signal_direction = true, + .pwrreg_clkgate = true, + .busy_detect = true, + .busy_dpsm_flag = MCI_DPSM_ST_BUSYMODE, + .busy_detect_flag = MCI_ST_CARDBUSY, + .busy_detect_mask = MCI_ST_BUSYENDMASK, + .pwrreg_nopower = true, + .mmcimask1 = true, + .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_OD, + .init = ux500_variant_init, +}; + +static struct variant_data variant_ux500v2 = { + .fifosize = 30 * 4, + .fifohalfsize = 8 * 4, + .clkreg = MCI_CLK_ENABLE, + .clkreg_enable = MCI_ST_UX500_HWFCEN, + .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, + .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE, + .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, + .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, + .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, + .cmdreg_srsp = MCI_CPSM_RESPONSE, + .datactrl_mask_ddrmode = MCI_DPSM_ST_DDRMODE, + .datalength_bits = 24, + .datactrl_blocksz = 11, + .datactrl_any_blocksz = true, + .dma_power_of_2 = true, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, + .st_sdio = true, + .st_clkdiv = true, + .pwrreg_powerup = MCI_PWR_ON, + .f_max = 100000000, + .signal_direction = true, + .pwrreg_clkgate = true, + .busy_detect = true, + .busy_dpsm_flag = MCI_DPSM_ST_BUSYMODE, + .busy_detect_flag = MCI_ST_CARDBUSY, + .busy_detect_mask = MCI_ST_BUSYENDMASK, + .pwrreg_nopower = true, + .mmcimask1 = true, + .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_OD, + .init = ux500v2_variant_init, +}; + +static struct variant_data variant_stm32 = { + .fifosize = 32 * 4, + .fifohalfsize = 8 * 4, + .clkreg = MCI_CLK_ENABLE, + .clkreg_enable = MCI_ST_UX500_HWFCEN, + .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, + .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE, + .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, + .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, + .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, + .cmdreg_srsp = MCI_CPSM_RESPONSE, + .irq_pio_mask = MCI_IRQ_PIO_MASK, + .datalength_bits = 24, + .datactrl_blocksz = 11, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, + .st_sdio = true, + .st_clkdiv = true, + .pwrreg_powerup = MCI_PWR_ON, + .f_max = 48000000, + .pwrreg_clkgate = true, + .pwrreg_nopower = true, + .init = mmci_variant_init, +}; + +static struct variant_data variant_stm32_sdmmc = { + .fifosize = 16 * 4, + .fifohalfsize = 8 * 4, + .f_max = 208000000, + .stm32_clkdiv = true, + .cmdreg_cpsm_enable = MCI_CPSM_STM32_ENABLE, + .cmdreg_lrsp_crc = MCI_CPSM_STM32_LRSP_CRC, + .cmdreg_srsp_crc = MCI_CPSM_STM32_SRSP_CRC, + .cmdreg_srsp = MCI_CPSM_STM32_SRSP, + .cmdreg_stop = MCI_CPSM_STM32_CMDSTOP, + .data_cmd_enable = MCI_CPSM_STM32_CMDTRANS, + .irq_pio_mask = MCI_IRQ_PIO_STM32_MASK, + .datactrl_first = true, + .datacnt_useless = true, + .datalength_bits = 25, + .datactrl_blocksz = 14, + .datactrl_any_blocksz = true, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, + .stm32_idmabsize_mask = GENMASK(12, 5), + .busy_timeout = true, + .busy_detect = true, + .busy_detect_flag = MCI_STM32_BUSYD0, + .busy_detect_mask = MCI_STM32_BUSYD0ENDMASK, + .init = sdmmc_variant_init, +}; + +static struct variant_data variant_stm32_sdmmcv2 = { + .fifosize = 16 * 4, + .fifohalfsize = 8 * 4, + .f_max = 208000000, + .stm32_clkdiv = true, + .cmdreg_cpsm_enable = MCI_CPSM_STM32_ENABLE, + .cmdreg_lrsp_crc = MCI_CPSM_STM32_LRSP_CRC, + .cmdreg_srsp_crc = MCI_CPSM_STM32_SRSP_CRC, + .cmdreg_srsp = MCI_CPSM_STM32_SRSP, + .cmdreg_stop = MCI_CPSM_STM32_CMDSTOP, + .data_cmd_enable = MCI_CPSM_STM32_CMDTRANS, + .irq_pio_mask = MCI_IRQ_PIO_STM32_MASK, + .datactrl_first = true, + .datacnt_useless = true, + .datalength_bits = 25, + .datactrl_blocksz = 14, + .datactrl_any_blocksz = true, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, + .stm32_idmabsize_mask = GENMASK(16, 5), + .dma_lli = true, + .busy_timeout = true, + .busy_detect = true, + .busy_detect_flag = MCI_STM32_BUSYD0, + .busy_detect_mask = MCI_STM32_BUSYD0ENDMASK, + .init = sdmmc_variant_init, +}; + +static struct variant_data variant_qcom = { + .fifosize = 16 * 4, + .fifohalfsize = 8 * 4, + .clkreg = MCI_CLK_ENABLE, + .clkreg_enable = MCI_QCOM_CLK_FLOWENA | + MCI_QCOM_CLK_SELECT_IN_FBCLK, + .clkreg_8bit_bus_enable = MCI_QCOM_CLK_WIDEBUS_8, + .datactrl_mask_ddrmode = MCI_QCOM_CLK_SELECT_IN_DDR_MODE, + .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, + .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, + .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, + .cmdreg_srsp = MCI_CPSM_RESPONSE, + .data_cmd_enable = MCI_CPSM_QCOM_DATCMD, + .datalength_bits = 24, + .datactrl_blocksz = 11, + .datactrl_any_blocksz = true, + .pwrreg_powerup = MCI_PWR_UP, + .f_max = 208000000, + .explicit_mclk_control = true, + .qcom_fifo = true, + .qcom_dml = true, + .mmcimask1 = true, + .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_ROD, + .init = qcom_variant_init, +}; + +static struct variant_data variant_adi_systemc = { + .fifosize = 16 * 4, + .fifohalfsize = 8 * 4, + .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, + .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, + .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, + .cmdreg_srsp = MCI_CPSM_RESPONSE, + .datalength_bits = 16, + .datactrl_blocksz = 11, + .pwrreg_powerup = MCI_PWR_UP, + .f_max = 100000000, + .reversed_irq_handling = true, + .mmcimask1 = true, + .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_ROD, + .init = adi_systemc_variant_init, +}; + +/* Busy detection for the ST Micro variant */ +static int mmci_card_busy(struct mmc_host *mmc) +{ + struct mmci_host *host = mmc_priv(mmc); + unsigned long flags; + int busy = 0; + + spin_lock_irqsave(&host->lock, flags); + if (readl(host->base + MMCISTATUS) & host->variant->busy_detect_flag) + busy = 1; + spin_unlock_irqrestore(&host->lock, flags); + + return busy; +} + +static void mmci_reg_delay(struct mmci_host *host) +{ + /* + * According to the spec, at least three feedback clock cycles + * of max 52 MHz must pass between two writes to the MMCICLOCK reg. + * Three MCLK clock cycles must pass between two MMCIPOWER reg writes. + * Worst delay time during card init is at 100 kHz => 30 us. + * Worst delay time when up and running is at 25 MHz => 120 ns. + */ + if (host->cclk < 25000000) + udelay(30); + else + ndelay(120); +} + +/* + * This must be called with host->lock held + */ +void mmci_write_clkreg(struct mmci_host *host, u32 clk) +{ + if (host->clk_reg != clk) { + host->clk_reg = clk; + writel(clk, host->base + MMCICLOCK); + } +} + +/* + * This must be called with host->lock held + */ +void mmci_write_pwrreg(struct mmci_host *host, u32 pwr) +{ + if (host->pwr_reg != pwr) { + host->pwr_reg = pwr; + writel(pwr, host->base + MMCIPOWER); + } +} + +/* + * This must be called with host->lock held + */ +static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl) +{ + /* Keep busy mode in DPSM if enabled */ + datactrl |= host->datactrl_reg & host->variant->busy_dpsm_flag; + + if (host->datactrl_reg != datactrl) { + host->datactrl_reg = datactrl; + writel(datactrl, host->base + MMCIDATACTRL); + } +} + +/* + * This must be called with host->lock held + */ +static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) +{ + struct variant_data *variant = host->variant; + u32 clk = variant->clkreg; + + /* Make sure cclk reflects the current calculated clock */ + host->cclk = 0; + + if (desired) { + if (variant->explicit_mclk_control) { + host->cclk = host->mclk; + } else if (desired >= host->mclk) { + clk = MCI_CLK_BYPASS; + if (variant->st_clkdiv) + clk |= MCI_ST_UX500_NEG_EDGE; + host->cclk = host->mclk; + } else if (variant->st_clkdiv) { + /* + * DB8500 TRM says f = mclk / (clkdiv + 2) + * => clkdiv = (mclk / f) - 2 + * Round the divider up so we don't exceed the max + * frequency + */ + clk = DIV_ROUND_UP(host->mclk, desired) - 2; + if (clk >= 256) + clk = 255; + host->cclk = host->mclk / (clk + 2); + } else { + /* + * PL180 TRM says f = mclk / (2 * (clkdiv + 1)) + * => clkdiv = mclk / (2 * f) - 1 + */ + clk = host->mclk / (2 * desired) - 1; + if (clk >= 256) + clk = 255; + host->cclk = host->mclk / (2 * (clk + 1)); + } + + clk |= variant->clkreg_enable; + clk |= MCI_CLK_ENABLE; + /* This hasn't proven to be worthwhile */ + /* clk |= MCI_CLK_PWRSAVE; */ + } + + /* Set actual clock for debug */ + host->mmc->actual_clock = host->cclk; + + if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) + clk |= MCI_4BIT_BUS; + if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8) + clk |= variant->clkreg_8bit_bus_enable; + + if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 || + host->mmc->ios.timing == MMC_TIMING_MMC_DDR52) + clk |= variant->clkreg_neg_edge_enable; + + mmci_write_clkreg(host, clk); +} + +static void mmci_dma_release(struct mmci_host *host) +{ + if (host->ops && host->ops->dma_release) + host->ops->dma_release(host); + + host->use_dma = false; +} + +static void mmci_dma_setup(struct mmci_host *host) +{ + if (!host->ops || !host->ops->dma_setup) + return; + + if (host->ops->dma_setup(host)) + return; + + /* initialize pre request cookie */ + host->next_cookie = 1; + + host->use_dma = true; +} + +/* + * Validate mmc prerequisites + */ +static int mmci_validate_data(struct mmci_host *host, + struct mmc_data *data) +{ + struct variant_data *variant = host->variant; + + if (!data) + return 0; + if (!is_power_of_2(data->blksz) && !variant->datactrl_any_blocksz) { + dev_err(mmc_dev(host->mmc), + "unsupported block size (%d bytes)\n", data->blksz); + return -EINVAL; + } + + if (host->ops && host->ops->validate_data) + return host->ops->validate_data(host, data); + + return 0; +} + +static int mmci_prep_data(struct mmci_host *host, struct mmc_data *data, bool next) +{ + int err; + + if (!host->ops || !host->ops->prep_data) + return 0; + + err = host->ops->prep_data(host, data, next); + + if (next && !err) + data->host_cookie = ++host->next_cookie < 0 ? + 1 : host->next_cookie; + + return err; +} + +static void mmci_unprep_data(struct mmci_host *host, struct mmc_data *data, + int err) +{ + if (host->ops && host->ops->unprep_data) + host->ops->unprep_data(host, data, err); + + data->host_cookie = 0; +} + +static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data) +{ + WARN_ON(data->host_cookie && data->host_cookie != host->next_cookie); + + if (host->ops && host->ops->get_next_data) + host->ops->get_next_data(host, data); +} + +static int mmci_dma_start(struct mmci_host *host, unsigned int datactrl) +{ + struct mmc_data *data = host->data; + int ret; + + if (!host->use_dma) + return -EINVAL; + + ret = mmci_prep_data(host, data, false); + if (ret) + return ret; + + if (!host->ops || !host->ops->dma_start) + return -EINVAL; + + /* Okay, go for it. */ + dev_vdbg(mmc_dev(host->mmc), + "Submit MMCI DMA job, sglen %d blksz %04x blks %04x flags %08x\n", + data->sg_len, data->blksz, data->blocks, data->flags); + + ret = host->ops->dma_start(host, &datactrl); + if (ret) + return ret; + + /* Trigger the DMA transfer */ + mmci_write_datactrlreg(host, datactrl); + + /* + * Let the MMCI say when the data is ended and it's time + * to fire next DMA request. When that happens, MMCI will + * call mmci_data_end() + */ + writel(readl(host->base + MMCIMASK0) | MCI_DATAENDMASK, + host->base + MMCIMASK0); + return 0; +} + +static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data) +{ + if (!host->use_dma) + return; + + if (host->ops && host->ops->dma_finalize) + host->ops->dma_finalize(host, data); +} + +static void mmci_dma_error(struct mmci_host *host) +{ + if (!host->use_dma) + return; + + if (host->ops && host->ops->dma_error) + host->ops->dma_error(host); +} + +static void +mmci_request_end(struct mmci_host *host, struct mmc_request *mrq) +{ + writel(0, host->base + MMCICOMMAND); + + BUG_ON(host->data); + + host->mrq = NULL; + host->cmd = NULL; + + mmc_request_done(host->mmc, mrq); +} + +static void mmci_set_mask1(struct mmci_host *host, unsigned int mask) +{ + void __iomem *base = host->base; + struct variant_data *variant = host->variant; + + if (host->singleirq) { + unsigned int mask0 = readl(base + MMCIMASK0); + + mask0 &= ~variant->irq_pio_mask; + mask0 |= mask; + + writel(mask0, base + MMCIMASK0); + } + + if (variant->mmcimask1) + writel(mask, base + MMCIMASK1); + + host->mask1_reg = mask; +} + +static void mmci_stop_data(struct mmci_host *host) +{ + mmci_write_datactrlreg(host, 0); + mmci_set_mask1(host, 0); + host->data = NULL; +} + +static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) +{ + unsigned int flags = SG_MITER_ATOMIC; + + if (data->flags & MMC_DATA_READ) + flags |= SG_MITER_TO_SG; + else + flags |= SG_MITER_FROM_SG; + + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); +} + +static u32 mmci_get_dctrl_cfg(struct mmci_host *host) +{ + return MCI_DPSM_ENABLE | mmci_dctrl_blksz(host); +} + +static u32 ux500v2_get_dctrl_cfg(struct mmci_host *host) +{ + return MCI_DPSM_ENABLE | (host->data->blksz << 16); +} + +static bool ux500_busy_complete(struct mmci_host *host, struct mmc_command *cmd, + u32 status, u32 err_msk) +{ + void __iomem *base = host->base; + + /* + * Before unmasking for the busy end IRQ, confirm that the + * command was sent successfully. To keep track of having a + * command in-progress, waiting for busy signaling to end, + * store the status in host->busy_status. + * + * Note that, the card may need a couple of clock cycles before + * it starts signaling busy on DAT0, hence re-read the + * MMCISTATUS register here, to allow the busy bit to be set. + * Potentially we may even need to poll the register for a + * while, to allow it to be set, but tests indicates that it + * isn't needed. + */ + if (!host->busy_status && !(status & err_msk) && + (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) { + writel(readl(base + MMCIMASK0) | + host->variant->busy_detect_mask, + base + MMCIMASK0); + + host->busy_status = status & (MCI_CMDSENT | MCI_CMDRESPEND); + return false; + } + + /* + * If there is a command in-progress that has been successfully + * sent, then bail out if busy status is set and wait for the + * busy end IRQ. + * + * Note that, the HW triggers an IRQ on both edges while + * monitoring DAT0 for busy completion, but there is only one + * status bit in MMCISTATUS for the busy state. Therefore + * both the start and the end interrupts needs to be cleared, + * one after the other. So, clear the busy start IRQ here. + */ + if (host->busy_status && + (status & host->variant->busy_detect_flag)) { + writel(host->variant->busy_detect_mask, base + MMCICLEAR); + return false; + } + + /* + * If there is a command in-progress that has been successfully + * sent and the busy bit isn't set, it means we have received + * the busy end IRQ. Clear and mask the IRQ, then continue to + * process the command. + */ + if (host->busy_status) { + writel(host->variant->busy_detect_mask, base + MMCICLEAR); + + writel(readl(base + MMCIMASK0) & + ~host->variant->busy_detect_mask, base + MMCIMASK0); + host->busy_status = 0; + } + + return true; +} + +/* + * All the DMA operation mode stuff goes inside this ifdef. + * This assumes that you have a generic DMA device interface, + * no custom DMA interfaces are supported. + */ +#ifdef CONFIG_DMA_ENGINE +struct mmci_dmae_next { + struct dma_async_tx_descriptor *desc; + struct dma_chan *chan; +}; + +struct mmci_dmae_priv { + struct dma_chan *cur; + struct dma_chan *rx_channel; + struct dma_chan *tx_channel; + struct dma_async_tx_descriptor *desc_current; + struct mmci_dmae_next next_data; +}; + +int mmci_dmae_setup(struct mmci_host *host) +{ + const char *rxname, *txname; + struct mmci_dmae_priv *dmae; + + dmae = devm_kzalloc(mmc_dev(host->mmc), sizeof(*dmae), GFP_KERNEL); + if (!dmae) + return -ENOMEM; + + host->dma_priv = dmae; + + dmae->rx_channel = dma_request_chan(mmc_dev(host->mmc), "rx"); + if (IS_ERR(dmae->rx_channel)) { + int ret = PTR_ERR(dmae->rx_channel); + dmae->rx_channel = NULL; + return ret; + } + + dmae->tx_channel = dma_request_chan(mmc_dev(host->mmc), "tx"); + if (IS_ERR(dmae->tx_channel)) { + if (PTR_ERR(dmae->tx_channel) == -EPROBE_DEFER) + dev_warn(mmc_dev(host->mmc), + "Deferred probe for TX channel ignored\n"); + dmae->tx_channel = NULL; + } + + /* + * If only an RX channel is specified, the driver will + * attempt to use it bidirectionally, however if it is + * is specified but cannot be located, DMA will be disabled. + */ + if (dmae->rx_channel && !dmae->tx_channel) + dmae->tx_channel = dmae->rx_channel; + + if (dmae->rx_channel) + rxname = dma_chan_name(dmae->rx_channel); + else + rxname = "none"; + + if (dmae->tx_channel) + txname = dma_chan_name(dmae->tx_channel); + else + txname = "none"; + + dev_info(mmc_dev(host->mmc), "DMA channels RX %s, TX %s\n", + rxname, txname); + + /* + * Limit the maximum segment size in any SG entry according to + * the parameters of the DMA engine device. + */ + if (dmae->tx_channel) { + struct device *dev = dmae->tx_channel->device->dev; + unsigned int max_seg_size = dma_get_max_seg_size(dev); + + if (max_seg_size < host->mmc->max_seg_size) + host->mmc->max_seg_size = max_seg_size; + } + if (dmae->rx_channel) { + struct device *dev = dmae->rx_channel->device->dev; + unsigned int max_seg_size = dma_get_max_seg_size(dev); + + if (max_seg_size < host->mmc->max_seg_size) + host->mmc->max_seg_size = max_seg_size; + } + + if (!dmae->tx_channel || !dmae->rx_channel) { + mmci_dmae_release(host); + return -EINVAL; + } + + return 0; +} + +/* + * This is used in or so inline it + * so it can be discarded. + */ +void mmci_dmae_release(struct mmci_host *host) +{ + struct mmci_dmae_priv *dmae = host->dma_priv; + + if (dmae->rx_channel) + dma_release_channel(dmae->rx_channel); + if (dmae->tx_channel) + dma_release_channel(dmae->tx_channel); + dmae->rx_channel = dmae->tx_channel = NULL; +} + +static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data) +{ + struct mmci_dmae_priv *dmae = host->dma_priv; + struct dma_chan *chan; + + if (data->flags & MMC_DATA_READ) + chan = dmae->rx_channel; + else + chan = dmae->tx_channel; + + dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, + mmc_get_dma_dir(data)); +} + +void mmci_dmae_error(struct mmci_host *host) +{ + struct mmci_dmae_priv *dmae = host->dma_priv; + + if (!dma_inprogress(host)) + return; + + dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n"); + dmaengine_terminate_all(dmae->cur); + host->dma_in_progress = false; + dmae->cur = NULL; + dmae->desc_current = NULL; + host->data->host_cookie = 0; + + mmci_dma_unmap(host, host->data); +} + +void mmci_dmae_finalize(struct mmci_host *host, struct mmc_data *data) +{ + struct mmci_dmae_priv *dmae = host->dma_priv; + u32 status; + int i; + + if (!dma_inprogress(host)) + return; + + /* Wait up to 1ms for the DMA to complete */ + for (i = 0; ; i++) { + status = readl(host->base + MMCISTATUS); + if (!(status & MCI_RXDATAAVLBLMASK) || i >= 100) + break; + udelay(10); + } + + /* + * Check to see whether we still have some data left in the FIFO - + * this catches DMA controllers which are unable to monitor the + * DMALBREQ and DMALSREQ signals while allowing us to DMA to non- + * contiguous buffers. On TX, we'll get a FIFO underrun error. + */ + if (status & MCI_RXDATAAVLBLMASK) { + mmci_dma_error(host); + if (!data->error) + data->error = -EIO; + } else if (!data->host_cookie) { + mmci_dma_unmap(host, data); + } + + /* + * Use of DMA with scatter-gather is impossible. + * Give up with DMA and switch back to PIO mode. + */ + if (status & MCI_RXDATAAVLBLMASK) { + dev_err(mmc_dev(host->mmc), "buggy DMA detected. Taking evasive action.\n"); + mmci_dma_release(host); + } + + host->dma_in_progress = false; + dmae->cur = NULL; + dmae->desc_current = NULL; +} + +/* prepares DMA channel and DMA descriptor, returns non-zero on failure */ +static int _mmci_dmae_prep_data(struct mmci_host *host, struct mmc_data *data, + struct dma_chan **dma_chan, + struct dma_async_tx_descriptor **dma_desc) +{ + struct mmci_dmae_priv *dmae = host->dma_priv; + struct variant_data *variant = host->variant; + struct dma_slave_config conf = { + .src_addr = host->phybase + MMCIFIFO, + .dst_addr = host->phybase + MMCIFIFO, + .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .src_maxburst = variant->fifohalfsize >> 2, /* # of words */ + .dst_maxburst = variant->fifohalfsize >> 2, /* # of words */ + .device_fc = false, + }; + struct dma_chan *chan; + struct dma_device *device; + struct dma_async_tx_descriptor *desc; + int nr_sg; + unsigned long flags = DMA_CTRL_ACK; + + if (data->flags & MMC_DATA_READ) { + conf.direction = DMA_DEV_TO_MEM; + chan = dmae->rx_channel; + } else { + conf.direction = DMA_MEM_TO_DEV; + chan = dmae->tx_channel; + } + + /* If there's no DMA channel, fall back to PIO */ + if (!chan) + return -EINVAL; + + /* If less than or equal to the fifo size, don't bother with DMA */ + if (data->blksz * data->blocks <= variant->fifosize) + return -EINVAL; + + /* + * This is necessary to get SDIO working on the Ux500. We do not yet + * know if this is a bug in: + * - The Ux500 DMA controller (DMA40) + * - The MMCI DMA interface on the Ux500 + * some power of two blocks (such as 64 bytes) are sent regularly + * during SDIO traffic and those work fine so for these we enable DMA + * transfers. + */ + if (host->variant->dma_power_of_2 && !is_power_of_2(data->blksz)) + return -EINVAL; + + device = chan->device; + nr_sg = dma_map_sg(device->dev, data->sg, data->sg_len, + mmc_get_dma_dir(data)); + if (nr_sg == 0) + return -EINVAL; + + if (host->variant->qcom_dml) + flags |= DMA_PREP_INTERRUPT; + + dmaengine_slave_config(chan, &conf); + desc = dmaengine_prep_slave_sg(chan, data->sg, nr_sg, + conf.direction, flags); + if (!desc) + goto unmap_exit; + + *dma_chan = chan; + *dma_desc = desc; + + return 0; + + unmap_exit: + dma_unmap_sg(device->dev, data->sg, data->sg_len, + mmc_get_dma_dir(data)); + return -ENOMEM; +} + +int mmci_dmae_prep_data(struct mmci_host *host, + struct mmc_data *data, + bool next) +{ + struct mmci_dmae_priv *dmae = host->dma_priv; + struct mmci_dmae_next *nd = &dmae->next_data; + + if (!host->use_dma) + return -EINVAL; + + if (next) + return _mmci_dmae_prep_data(host, data, &nd->chan, &nd->desc); + /* Check if next job is already prepared. */ + if (dmae->cur && dmae->desc_current) + return 0; + + /* No job were prepared thus do it now. */ + return _mmci_dmae_prep_data(host, data, &dmae->cur, + &dmae->desc_current); +} + +int mmci_dmae_start(struct mmci_host *host, unsigned int *datactrl) +{ + struct mmci_dmae_priv *dmae = host->dma_priv; + int ret; + + host->dma_in_progress = true; + ret = dma_submit_error(dmaengine_submit(dmae->desc_current)); + if (ret < 0) { + host->dma_in_progress = false; + return ret; + } + dma_async_issue_pending(dmae->cur); + + *datactrl |= MCI_DPSM_DMAENABLE; + + return 0; +} + +void mmci_dmae_get_next_data(struct mmci_host *host, struct mmc_data *data) +{ + struct mmci_dmae_priv *dmae = host->dma_priv; + struct mmci_dmae_next *next = &dmae->next_data; + + if (!host->use_dma) + return; + + WARN_ON(!data->host_cookie && (next->desc || next->chan)); + + dmae->desc_current = next->desc; + dmae->cur = next->chan; + next->desc = NULL; + next->chan = NULL; +} + +void mmci_dmae_unprep_data(struct mmci_host *host, + struct mmc_data *data, int err) + +{ + struct mmci_dmae_priv *dmae = host->dma_priv; + + if (!host->use_dma) + return; + + mmci_dma_unmap(host, data); + + if (err) { + struct mmci_dmae_next *next = &dmae->next_data; + struct dma_chan *chan; + if (data->flags & MMC_DATA_READ) + chan = dmae->rx_channel; + else + chan = dmae->tx_channel; + dmaengine_terminate_all(chan); + + if (dmae->desc_current == next->desc) + dmae->desc_current = NULL; + + if (dmae->cur == next->chan) { + host->dma_in_progress = false; + dmae->cur = NULL; + } + + next->desc = NULL; + next->chan = NULL; + } +} + +static struct mmci_host_ops mmci_variant_ops = { + .prep_data = mmci_dmae_prep_data, + .unprep_data = mmci_dmae_unprep_data, + .get_datactrl_cfg = mmci_get_dctrl_cfg, + .get_next_data = mmci_dmae_get_next_data, + .dma_setup = mmci_dmae_setup, + .dma_release = mmci_dmae_release, + .dma_start = mmci_dmae_start, + .dma_finalize = mmci_dmae_finalize, + .dma_error = mmci_dmae_error, +}; +#else +static struct mmci_host_ops mmci_variant_ops = { + .get_datactrl_cfg = mmci_get_dctrl_cfg, +}; +#endif + +static void mmci_variant_init(struct mmci_host *host) +{ + host->ops = &mmci_variant_ops; +} + +static void ux500_variant_init(struct mmci_host *host) +{ + host->ops = &mmci_variant_ops; + host->ops->busy_complete = ux500_busy_complete; +} + +static void ux500v2_variant_init(struct mmci_host *host) +{ + host->ops = &mmci_variant_ops; + host->ops->busy_complete = ux500_busy_complete; + host->ops->get_datactrl_cfg = ux500v2_get_dctrl_cfg; +} + +static void adi_systemc_variant_init(struct mmci_host *host) +{ + host->ops = &mmci_variant_ops; + host->mmc_ops->init_card = adi_systemc_init_card; + host->mmc->caps2 |= MMC_CAP2_NO_SDIO; +} + +static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct mmci_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (!data) + return; + + WARN_ON(data->host_cookie); + + if (mmci_validate_data(host, data)) + return; + + mmci_prep_data(host, data, true); +} + +static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq, + int err) +{ + struct mmci_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (!data || !data->host_cookie) + return; + + mmci_unprep_data(host, data, err); +} + +static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) +{ + struct variant_data *variant = host->variant; + unsigned int datactrl, timeout, irqmask; + unsigned long long clks; + void __iomem *base; + + dev_dbg(mmc_dev(host->mmc), "blksz %04x blks %04x flags %08x\n", + data->blksz, data->blocks, data->flags); + + host->data = data; + host->size = data->blksz * data->blocks; + data->bytes_xfered = 0; + + clks = (unsigned long long)data->timeout_ns * host->cclk; + do_div(clks, NSEC_PER_SEC); + + timeout = data->timeout_clks + (unsigned int)clks; + + base = host->base; + writel(timeout, base + MMCIDATATIMER); + writel(host->size, base + MMCIDATALENGTH); + + datactrl = host->ops->get_datactrl_cfg(host); + datactrl |= host->data->flags & MMC_DATA_READ ? MCI_DPSM_DIRECTION : 0; + + if (host->mmc->card && mmc_card_sdio(host->mmc->card)) { + u32 clk; + + datactrl |= variant->datactrl_mask_sdio; + + /* + * The ST Micro variant for SDIO small write transfers + * needs to have clock H/W flow control disabled, + * otherwise the transfer will not start. The threshold + * depends on the rate of MCLK. + */ + if (variant->st_sdio && data->flags & MMC_DATA_WRITE && + (host->size < 8 || + (host->size <= 8 && host->mclk > 50000000))) + clk = host->clk_reg & ~variant->clkreg_enable; + else + clk = host->clk_reg | variant->clkreg_enable; + + mmci_write_clkreg(host, clk); + } + + if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 || + host->mmc->ios.timing == MMC_TIMING_MMC_DDR52) + datactrl |= variant->datactrl_mask_ddrmode; + + /* + * Attempt to use DMA operation mode, if this + * should fail, fall back to PIO mode + */ + if (!mmci_dma_start(host, datactrl)) + return; + + /* IRQ mode, map the SG list for CPU reading/writing */ + mmci_init_sg(host, data); + + if (data->flags & MMC_DATA_READ) { + irqmask = MCI_RXFIFOHALFFULLMASK; + + /* + * If we have less than the fifo 'half-full' threshold to + * transfer, trigger a PIO interrupt as soon as any data + * is available. + */ + if (host->size < variant->fifohalfsize) + irqmask |= MCI_RXDATAAVLBLMASK; + } else { + /* + * We don't actually need to include "FIFO empty" here + * since its implicit in "FIFO half empty". + */ + irqmask = MCI_TXFIFOHALFEMPTYMASK; + } + + mmci_write_datactrlreg(host, datactrl); + writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0); + mmci_set_mask1(host, irqmask); +} + +static void +mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) +{ + void __iomem *base = host->base; + unsigned long long clks; + + dev_dbg(mmc_dev(host->mmc), "op %02x arg %08x flags %08x\n", + cmd->opcode, cmd->arg, cmd->flags); + + if (readl(base + MMCICOMMAND) & host->variant->cmdreg_cpsm_enable) { + writel(0, base + MMCICOMMAND); + mmci_reg_delay(host); + } + + if (host->variant->cmdreg_stop && + cmd->opcode == MMC_STOP_TRANSMISSION) + c |= host->variant->cmdreg_stop; + + c |= cmd->opcode | host->variant->cmdreg_cpsm_enable; + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) + c |= host->variant->cmdreg_lrsp_crc; + else if (cmd->flags & MMC_RSP_CRC) + c |= host->variant->cmdreg_srsp_crc; + else + c |= host->variant->cmdreg_srsp; + } + + if (host->variant->busy_timeout && cmd->flags & MMC_RSP_BUSY) { + if (!cmd->busy_timeout) + cmd->busy_timeout = 10 * MSEC_PER_SEC; + + if (cmd->busy_timeout > host->mmc->max_busy_timeout) + clks = (unsigned long long)host->mmc->max_busy_timeout * host->cclk; + else + clks = (unsigned long long)cmd->busy_timeout * host->cclk; + + do_div(clks, MSEC_PER_SEC); + writel_relaxed(clks, host->base + MMCIDATATIMER); + } + + if (host->ops->pre_sig_volt_switch && cmd->opcode == SD_SWITCH_VOLTAGE) + host->ops->pre_sig_volt_switch(host); + + if (/*interrupt*/0) + c |= MCI_CPSM_INTERRUPT; + + if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) + c |= host->variant->data_cmd_enable; + + host->cmd = cmd; + + writel(cmd->arg, base + MMCIARGUMENT); + writel(c, base + MMCICOMMAND); +} + +static void mmci_stop_command(struct mmci_host *host) +{ + host->stop_abort.error = 0; + mmci_start_command(host, &host->stop_abort, 0); +} + +static void +mmci_data_irq(struct mmci_host *host, struct mmc_data *data, + unsigned int status) +{ + unsigned int status_err; + + /* Make sure we have data to handle */ + if (!data) + return; + + /* First check for errors */ + status_err = status & (host->variant->start_err | + MCI_DATACRCFAIL | MCI_DATATIMEOUT | + MCI_TXUNDERRUN | MCI_RXOVERRUN); + + if (status_err) { + u32 remain, success; + + /* Terminate the DMA transfer */ + mmci_dma_error(host); + + /* + * Calculate how far we are into the transfer. Note that + * the data counter gives the number of bytes transferred + * on the MMC bus, not on the host side. On reads, this + * can be as much as a FIFO-worth of data ahead. This + * matters for FIFO overruns only. + */ + if (!host->variant->datacnt_useless) { + remain = readl(host->base + MMCIDATACNT); + success = data->blksz * data->blocks - remain; + } else { + success = 0; + } + + dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ, status 0x%08x at 0x%08x\n", + status_err, success); + if (status_err & MCI_DATACRCFAIL) { + /* Last block was not successful */ + success -= 1; + data->error = -EILSEQ; + } else if (status_err & MCI_DATATIMEOUT) { + data->error = -ETIMEDOUT; + } else if (status_err & MCI_STARTBITERR) { + data->error = -ECOMM; + } else if (status_err & MCI_TXUNDERRUN) { + data->error = -EIO; + } else if (status_err & MCI_RXOVERRUN) { + if (success > host->variant->fifosize) + success -= host->variant->fifosize; + else + success = 0; + data->error = -EIO; + } + data->bytes_xfered = round_down(success, data->blksz); + } + + if (status & MCI_DATABLOCKEND) + dev_err(mmc_dev(host->mmc), "stray MCI_DATABLOCKEND interrupt\n"); + + if (status & MCI_DATAEND || data->error) { + mmci_dma_finalize(host, data); + + mmci_stop_data(host); + + if (!data->error) + /* The error clause is handled above, success! */ + data->bytes_xfered = data->blksz * data->blocks; + + if (!data->stop) { + if (host->variant->cmdreg_stop && data->error) + mmci_stop_command(host); + else + mmci_request_end(host, data->mrq); + } else if (host->mrq->sbc && !data->error) { + mmci_request_end(host, data->mrq); + } else { + mmci_start_command(host, data->stop, 0); + } + } +} + +static void +mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, + unsigned int status) +{ + u32 err_msk = MCI_CMDCRCFAIL | MCI_CMDTIMEOUT; + void __iomem *base = host->base; + bool sbc, busy_resp; + + if (!cmd) + return; + + sbc = (cmd == host->mrq->sbc); + busy_resp = !!(cmd->flags & MMC_RSP_BUSY); + + /* + * We need to be one of these interrupts to be considered worth + * handling. Note that we tag on any latent IRQs postponed + * due to waiting for busy status. + */ + if (host->variant->busy_timeout && busy_resp) + err_msk |= MCI_DATATIMEOUT; + + if (!((status | host->busy_status) & + (err_msk | MCI_CMDSENT | MCI_CMDRESPEND))) + return; + + /* Handle busy detection on DAT0 if the variant supports it. */ + if (busy_resp && host->variant->busy_detect) + if (!host->ops->busy_complete(host, cmd, status, err_msk)) + return; + + host->cmd = NULL; + + if (status & MCI_CMDTIMEOUT) { + cmd->error = -ETIMEDOUT; + } else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) { + cmd->error = -EILSEQ; + } else if (host->variant->busy_timeout && busy_resp && + status & MCI_DATATIMEOUT) { + cmd->error = -ETIMEDOUT; + host->irq_action = IRQ_WAKE_THREAD; + } else { + cmd->resp[0] = readl(base + MMCIRESPONSE0); + cmd->resp[1] = readl(base + MMCIRESPONSE1); + cmd->resp[2] = readl(base + MMCIRESPONSE2); + cmd->resp[3] = readl(base + MMCIRESPONSE3); + } + + if ((!sbc && !cmd->data) || cmd->error) { + if (host->data) { + /* Terminate the DMA transfer */ + mmci_dma_error(host); + + mmci_stop_data(host); + if (host->variant->cmdreg_stop && cmd->error) { + mmci_stop_command(host); + return; + } + } + + if (host->irq_action != IRQ_WAKE_THREAD) + mmci_request_end(host, host->mrq); + + } else if (sbc) { + mmci_start_command(host, host->mrq->cmd, 0); + } else if (!host->variant->datactrl_first && + !(cmd->data->flags & MMC_DATA_READ)) { + mmci_start_data(host, cmd->data); + } +} + +static int mmci_get_rx_fifocnt(struct mmci_host *host, u32 status, int remain) +{ + return remain - (readl(host->base + MMCIFIFOCNT) << 2); +} + +static int mmci_qcom_get_rx_fifocnt(struct mmci_host *host, u32 status, int r) +{ + /* + * on qcom SDCC4 only 8 words are used in each burst so only 8 addresses + * from the fifo range should be used + */ + if (status & MCI_RXFIFOHALFFULL) + return host->variant->fifohalfsize; + else if (status & MCI_RXDATAAVLBL) + return 4; + + return 0; +} + +static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain) +{ + void __iomem *base = host->base; + char *ptr = buffer; + u32 status = readl(host->base + MMCISTATUS); + int host_remain = host->size; + + do { + int count = host->get_rx_fifocnt(host, status, host_remain); + + if (count > remain) + count = remain; + + if (count <= 0) + break; + + /* + * SDIO especially may want to send something that is + * not divisible by 4 (as opposed to card sectors + * etc). Therefore make sure to always read the last bytes + * while only doing full 32-bit reads towards the FIFO. + */ + if (unlikely(count & 0x3)) { + if (count < 4) { + unsigned char buf[4]; + ioread32_rep(base + MMCIFIFO, buf, 1); + memcpy(ptr, buf, count); + } else { + ioread32_rep(base + MMCIFIFO, ptr, count >> 2); + count &= ~0x3; + } + } else { + ioread32_rep(base + MMCIFIFO, ptr, count >> 2); + } + + ptr += count; + remain -= count; + host_remain -= count; + + if (remain == 0) + break; + + status = readl(base + MMCISTATUS); + } while (status & MCI_RXDATAAVLBL); + + return ptr - buffer; +} + +static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int remain, u32 status) +{ + struct variant_data *variant = host->variant; + void __iomem *base = host->base; + char *ptr = buffer; + + do { + unsigned int count, maxcnt; + + maxcnt = status & MCI_TXFIFOEMPTY ? + variant->fifosize : variant->fifohalfsize; + count = min(remain, maxcnt); + + /* + * SDIO especially may want to send something that is + * not divisible by 4 (as opposed to card sectors + * etc), and the FIFO only accept full 32-bit writes. + * So compensate by adding +3 on the count, a single + * byte become a 32bit write, 7 bytes will be two + * 32bit writes etc. + */ + iowrite32_rep(base + MMCIFIFO, ptr, (count + 3) >> 2); + + ptr += count; + remain -= count; + + if (remain == 0) + break; + + status = readl(base + MMCISTATUS); + } while (status & MCI_TXFIFOHALFEMPTY); + + return ptr - buffer; +} + +/* + * PIO data transfer IRQ handler. + */ +static irqreturn_t mmci_pio_irq(int irq, void *dev_id) +{ + struct mmci_host *host = dev_id; + struct sg_mapping_iter *sg_miter = &host->sg_miter; + struct variant_data *variant = host->variant; + void __iomem *base = host->base; + u32 status; + + status = readl(base + MMCISTATUS); + + dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status); + + do { + unsigned int remain, len; + char *buffer; + + /* + * For write, we only need to test the half-empty flag + * here - if the FIFO is completely empty, then by + * definition it is more than half empty. + * + * For read, check for data available. + */ + if (!(status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL))) + break; + + if (!sg_miter_next(sg_miter)) + break; + + buffer = sg_miter->addr; + remain = sg_miter->length; + + len = 0; + if (status & MCI_RXACTIVE) + len = mmci_pio_read(host, buffer, remain); + if (status & MCI_TXACTIVE) + len = mmci_pio_write(host, buffer, remain, status); + + sg_miter->consumed = len; + + host->size -= len; + remain -= len; + + if (remain) + break; + + status = readl(base + MMCISTATUS); + } while (1); + + sg_miter_stop(sg_miter); + + /* + * If we have less than the fifo 'half-full' threshold to transfer, + * trigger a PIO interrupt as soon as any data is available. + */ + if (status & MCI_RXACTIVE && host->size < variant->fifohalfsize) + mmci_set_mask1(host, MCI_RXDATAAVLBLMASK); + + /* + * If we run out of data, disable the data IRQs; this + * prevents a race where the FIFO becomes empty before + * the chip itself has disabled the data path, and + * stops us racing with our data end IRQ. + */ + if (host->size == 0) { + mmci_set_mask1(host, 0); + writel(readl(base + MMCIMASK0) | MCI_DATAENDMASK, base + MMCIMASK0); + } + + return IRQ_HANDLED; +} + +/* + * Handle completion of command and data transfers. + */ +static irqreturn_t mmci_irq(int irq, void *dev_id) +{ + struct mmci_host *host = dev_id; + u32 status; + + spin_lock(&host->lock); + host->irq_action = IRQ_HANDLED; + + do { + status = readl(host->base + MMCISTATUS); + + if (host->singleirq) { + if (status & host->mask1_reg) + mmci_pio_irq(irq, dev_id); + + status &= ~host->variant->irq_pio_mask; + } + + /* + * Busy detection is managed by mmci_cmd_irq(), including to + * clear the corresponding IRQ. + */ + status &= readl(host->base + MMCIMASK0); + if (host->variant->busy_detect) + writel(status & ~host->variant->busy_detect_mask, + host->base + MMCICLEAR); + else + writel(status, host->base + MMCICLEAR); + + dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status); + + if (host->variant->reversed_irq_handling) { + mmci_data_irq(host, host->data, status); + mmci_cmd_irq(host, host->cmd, status); + } else { + mmci_cmd_irq(host, host->cmd, status); + mmci_data_irq(host, host->data, status); + } + + /* + * Busy detection has been handled by mmci_cmd_irq() above. + * Clear the status bit to prevent polling in IRQ context. + */ + if (host->variant->busy_detect_flag) + status &= ~host->variant->busy_detect_flag; + + } while (status); + + spin_unlock(&host->lock); + + return host->irq_action; +} + +/* + * mmci_irq_thread() - A threaded IRQ handler that manages a reset of the HW. + * + * A reset is needed for some variants, where a datatimeout for a R1B request + * causes the DPSM to stay busy (non-functional). + */ +static irqreturn_t mmci_irq_thread(int irq, void *dev_id) +{ + struct mmci_host *host = dev_id; + unsigned long flags; + + if (host->rst) { + reset_control_assert(host->rst); + udelay(2); + reset_control_deassert(host->rst); + } + + spin_lock_irqsave(&host->lock, flags); + writel(host->clk_reg, host->base + MMCICLOCK); + writel(host->pwr_reg, host->base + MMCIPOWER); + writel(MCI_IRQENABLE | host->variant->start_err, + host->base + MMCIMASK0); + + host->irq_action = IRQ_HANDLED; + mmci_request_end(host, host->mrq); + spin_unlock_irqrestore(&host->lock, flags); + + return host->irq_action; +} + +static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct mmci_host *host = mmc_priv(mmc); + unsigned long flags; + + WARN_ON(host->mrq != NULL); + + mrq->cmd->error = mmci_validate_data(host, mrq->data); + if (mrq->cmd->error) { + mmc_request_done(mmc, mrq); + return; + } + + spin_lock_irqsave(&host->lock, flags); + + host->mrq = mrq; + + if (mrq->data) + mmci_get_next_data(host, mrq->data); + + if (mrq->data && + (host->variant->datactrl_first || mrq->data->flags & MMC_DATA_READ)) + mmci_start_data(host, mrq->data); + + if (mrq->sbc) + mmci_start_command(host, mrq->sbc, 0); + else + mmci_start_command(host, mrq->cmd, 0); + + spin_unlock_irqrestore(&host->lock, flags); +} + +static void mmci_set_max_busy_timeout(struct mmc_host *mmc) +{ + struct mmci_host *host = mmc_priv(mmc); + u32 max_busy_timeout = 0; + + if (!host->variant->busy_detect) + return; + + if (host->variant->busy_timeout && mmc->actual_clock) + max_busy_timeout = ~0UL / (mmc->actual_clock / MSEC_PER_SEC); + + mmc->max_busy_timeout = max_busy_timeout; +} + +static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct mmci_host *host = mmc_priv(mmc); + struct variant_data *variant = host->variant; + u32 pwr = 0; + unsigned long flags; + int ret; + + switch (ios->power_mode) { + case MMC_POWER_OFF: + if (!IS_ERR(mmc->supply.vmmc)) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + + if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) { + regulator_disable(mmc->supply.vqmmc); + host->vqmmc_enabled = false; + } + + break; + case MMC_POWER_UP: + if (!IS_ERR(mmc->supply.vmmc)) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); + + /* + * The ST Micro variant doesn't have the PL180s MCI_PWR_UP + * and instead uses MCI_PWR_ON so apply whatever value is + * configured in the variant data. + */ + pwr |= variant->pwrreg_powerup; + + break; + case MMC_POWER_ON: + if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) { + ret = regulator_enable(mmc->supply.vqmmc); + if (ret < 0) + dev_err(mmc_dev(mmc), + "failed to enable vqmmc regulator\n"); + else + host->vqmmc_enabled = true; + } + + pwr |= MCI_PWR_ON; + break; + } + + if (variant->signal_direction && ios->power_mode != MMC_POWER_OFF) { + /* + * The ST Micro variant has some additional bits + * indicating signal direction for the signals in + * the SD/MMC bus and feedback-clock usage. + */ + pwr |= host->pwr_reg_add; + + if (ios->bus_width == MMC_BUS_WIDTH_4) + pwr &= ~MCI_ST_DATA74DIREN; + else if (ios->bus_width == MMC_BUS_WIDTH_1) + pwr &= (~MCI_ST_DATA74DIREN & + ~MCI_ST_DATA31DIREN & + ~MCI_ST_DATA2DIREN); + } + + if (variant->opendrain) { + if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) + pwr |= variant->opendrain; + } else { + /* + * If the variant cannot configure the pads by its own, then we + * expect the pinctrl to be able to do that for us + */ + if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) + pinctrl_select_state(host->pinctrl, host->pins_opendrain); + else + pinctrl_select_default_state(mmc_dev(mmc)); + } + + /* + * If clock = 0 and the variant requires the MMCIPOWER to be used for + * gating the clock, the MCI_PWR_ON bit is cleared. + */ + if (!ios->clock && variant->pwrreg_clkgate) + pwr &= ~MCI_PWR_ON; + + if (host->variant->explicit_mclk_control && + ios->clock != host->clock_cache) { + ret = clk_set_rate(host->clk, ios->clock); + if (ret < 0) + dev_err(mmc_dev(host->mmc), + "Error setting clock rate (%d)\n", ret); + else + host->mclk = clk_get_rate(host->clk); + } + host->clock_cache = ios->clock; + + spin_lock_irqsave(&host->lock, flags); + + if (host->ops && host->ops->set_clkreg) + host->ops->set_clkreg(host, ios->clock); + else + mmci_set_clkreg(host, ios->clock); + + mmci_set_max_busy_timeout(mmc); + + if (host->ops && host->ops->set_pwrreg) + host->ops->set_pwrreg(host, pwr); + else + mmci_write_pwrreg(host, pwr); + + mmci_reg_delay(host); + + spin_unlock_irqrestore(&host->lock, flags); +} + +static int mmci_get_cd(struct mmc_host *mmc) +{ + struct mmci_host *host = mmc_priv(mmc); + struct mmci_platform_data *plat = host->plat; + unsigned int status = mmc_gpio_get_cd(mmc); + + if (status == -ENOSYS) { + if (!plat->status) + return 1; /* Assume always present */ + + status = plat->status(mmc_dev(host->mmc)); + } + return status; +} + +static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct mmci_host *host = mmc_priv(mmc); + int ret; + + ret = mmc_regulator_set_vqmmc(mmc, ios); + + if (!ret && host->ops && host->ops->post_sig_volt_switch) + ret = host->ops->post_sig_volt_switch(host, ios); + else if (ret) + ret = 0; + + if (ret < 0) + dev_warn(mmc_dev(mmc), "Voltage switch failed\n"); + + return ret; +} + +static void adi_systemc_init_card(struct mmc_host *host, struct mmc_card *card) +{ + /* The Arm Fast Model for MMC doesn't seem to respond well to SEND_EXT_CSD, + * so we fake out a response from the chip for the sector size here. + * This should be sufficient for simulation purposes, but of course + * could never work in a production driver. + */ + /* Set number of sectors to equal a 2GB disk (512 bytes/sector) */ + card->ext_csd.sectors = 4194304; +} + +static struct mmc_host_ops mmci_ops = { + .request = mmci_request, + .pre_req = mmci_pre_request, + .post_req = mmci_post_request, + .set_ios = mmci_set_ios, + .get_ro = mmc_gpio_get_ro, + .get_cd = mmci_get_cd, + .start_signal_voltage_switch = mmci_sig_volt_switch, +}; + +static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc) +{ + struct mmci_host *host = mmc_priv(mmc); + int ret = mmc_of_parse(mmc); + + if (ret) + return ret; + + if (of_get_property(np, "st,sig-dir-dat0", NULL)) + host->pwr_reg_add |= MCI_ST_DATA0DIREN; + if (of_get_property(np, "st,sig-dir-dat2", NULL)) + host->pwr_reg_add |= MCI_ST_DATA2DIREN; + if (of_get_property(np, "st,sig-dir-dat31", NULL)) + host->pwr_reg_add |= MCI_ST_DATA31DIREN; + if (of_get_property(np, "st,sig-dir-dat74", NULL)) + host->pwr_reg_add |= MCI_ST_DATA74DIREN; + if (of_get_property(np, "st,sig-dir-cmd", NULL)) + host->pwr_reg_add |= MCI_ST_CMDDIREN; + if (of_get_property(np, "st,sig-pin-fbclk", NULL)) + host->pwr_reg_add |= MCI_ST_FBCLKEN; + if (of_get_property(np, "st,sig-dir", NULL)) + host->pwr_reg_add |= MCI_STM32_DIRPOL; + if (of_get_property(np, "st,neg-edge", NULL)) + host->clk_reg_add |= MCI_STM32_CLK_NEGEDGE; + if (of_get_property(np, "st,use-ckin", NULL)) + host->clk_reg_add |= MCI_STM32_CLK_SELCKIN; + + if (of_get_property(np, "mmc-cap-mmc-highspeed", NULL)) + mmc->caps |= MMC_CAP_MMC_HIGHSPEED; + if (of_get_property(np, "mmc-cap-sd-highspeed", NULL)) + mmc->caps |= MMC_CAP_SD_HIGHSPEED; + + return 0; +} + +static int mmci_probe(struct amba_device *dev, + const struct amba_id *id) +{ + struct mmci_platform_data *plat = dev->dev.platform_data; + struct device_node *np = dev->dev.of_node; + struct variant_data *variant = id->data; + struct mmci_host *host; + struct mmc_host *mmc; + int ret; + + /* Must have platform data or Device Tree. */ + if (!plat && !np) { + dev_err(&dev->dev, "No plat data or DT found\n"); + return -EINVAL; + } + + if (!plat) { + plat = devm_kzalloc(&dev->dev, sizeof(*plat), GFP_KERNEL); + if (!plat) + return -ENOMEM; + } + + mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev); + if (!mmc) + return -ENOMEM; + + ret = mmci_of_parse(np, mmc); + if (ret) + goto host_free; + + host = mmc_priv(mmc); + host->mmc = mmc; + host->mmc_ops = &mmci_ops; + mmc->ops = &mmci_ops; + + /* + * Some variant (STM32) doesn't have opendrain bit, nevertheless + * pins can be set accordingly using pinctrl + */ + if (!variant->opendrain) { + host->pinctrl = devm_pinctrl_get(&dev->dev); + if (IS_ERR(host->pinctrl)) { + dev_err(&dev->dev, "failed to get pinctrl"); + ret = PTR_ERR(host->pinctrl); + goto host_free; + } + + host->pins_opendrain = pinctrl_lookup_state(host->pinctrl, + MMCI_PINCTRL_STATE_OPENDRAIN); + if (IS_ERR(host->pins_opendrain)) { + dev_err(mmc_dev(mmc), "Can't select opendrain pins\n"); + ret = PTR_ERR(host->pins_opendrain); + goto host_free; + } + } + + host->hw_designer = amba_manf(dev); + host->hw_revision = amba_rev(dev); + dev_dbg(mmc_dev(mmc), "designer ID = 0x%02x\n", host->hw_designer); + dev_dbg(mmc_dev(mmc), "revision = 0x%01x\n", host->hw_revision); + + host->clk = devm_clk_get(&dev->dev, NULL); + if (IS_ERR(host->clk)) { + ret = PTR_ERR(host->clk); + goto host_free; + } + + ret = clk_prepare_enable(host->clk); + if (ret) + goto host_free; + + if (variant->qcom_fifo) + host->get_rx_fifocnt = mmci_qcom_get_rx_fifocnt; + else + host->get_rx_fifocnt = mmci_get_rx_fifocnt; + + host->plat = plat; + host->variant = variant; + host->mclk = clk_get_rate(host->clk); + /* + * According to the spec, mclk is max 100 MHz, + * so we try to adjust the clock down to this, + * (if possible). + */ + if (host->mclk > variant->f_max) { + ret = clk_set_rate(host->clk, variant->f_max); + if (ret < 0) + goto clk_disable; + host->mclk = clk_get_rate(host->clk); + dev_dbg(mmc_dev(mmc), "eventual mclk rate: %u Hz\n", + host->mclk); + } + + host->phybase = dev->res.start; + host->base = devm_ioremap_resource(&dev->dev, &dev->res); + if (IS_ERR(host->base)) { + ret = PTR_ERR(host->base); + goto clk_disable; + } + + if (variant->init) + variant->init(host); + + /* + * The ARM and ST versions of the block have slightly different + * clock divider equations which means that the minimum divider + * differs too. + * on Qualcomm like controllers get the nearest minimum clock to 100Khz + */ + if (variant->st_clkdiv) + mmc->f_min = DIV_ROUND_UP(host->mclk, 257); + else if (variant->stm32_clkdiv) + mmc->f_min = DIV_ROUND_UP(host->mclk, 2046); + else if (variant->explicit_mclk_control) + mmc->f_min = clk_round_rate(host->clk, 100000); + else + mmc->f_min = DIV_ROUND_UP(host->mclk, 512); + /* + * If no maximum operating frequency is supplied, fall back to use + * the module parameter, which has a (low) default value in case it + * is not specified. Either value must not exceed the clock rate into + * the block, of course. + */ + if (mmc->f_max) + mmc->f_max = variant->explicit_mclk_control ? + min(variant->f_max, mmc->f_max) : + min(host->mclk, mmc->f_max); + else + mmc->f_max = variant->explicit_mclk_control ? + fmax : min(host->mclk, fmax); + + + dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max); + + host->rst = devm_reset_control_get_optional_exclusive(&dev->dev, NULL); + if (IS_ERR(host->rst)) { + ret = PTR_ERR(host->rst); + goto clk_disable; + } + + /* Get regulators and the supported OCR mask */ + ret = mmc_regulator_get_supply(mmc); + if (ret) + goto clk_disable; + + if (!mmc->ocr_avail) + mmc->ocr_avail = plat->ocr_mask; + else if (plat->ocr_mask) + dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n"); + + /* We support these capabilities. */ + mmc->caps |= MMC_CAP_CMD23; + + /* + * Enable busy detection. + */ + if (variant->busy_detect) { + mmci_ops.card_busy = mmci_card_busy; + /* + * Not all variants have a flag to enable busy detection + * in the DPSM, but if they do, set it here. + */ + if (variant->busy_dpsm_flag) + mmci_write_datactrlreg(host, + host->variant->busy_dpsm_flag); + mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; + } + + /* Variants with mandatory busy timeout in HW needs R1B responses. */ + if (variant->busy_timeout) + mmc->caps |= MMC_CAP_NEED_RSP_BUSY; + + /* Prepare a CMD12 - needed to clear the DPSM on some variants. */ + host->stop_abort.opcode = MMC_STOP_TRANSMISSION; + host->stop_abort.arg = 0; + host->stop_abort.flags = MMC_RSP_R1B | MMC_CMD_AC; + + /* We support these PM capabilities. */ + mmc->pm_caps |= MMC_PM_KEEP_POWER; + + /* + * We can do SGIO + */ + mmc->max_segs = NR_SG; + + /* + * Since only a certain number of bits are valid in the data length + * register, we must ensure that we don't exceed 2^num-1 bytes in a + * single request. + */ + mmc->max_req_size = (1 << variant->datalength_bits) - 1; + + /* + * Set the maximum segment size. Since we aren't doing DMA + * (yet) we are only limited by the data length register. + */ + mmc->max_seg_size = mmc->max_req_size; + + /* + * Block size can be up to 2048 bytes, but must be a power of two. + */ + mmc->max_blk_size = 1 << variant->datactrl_blocksz; + + /* + * Limit the number of blocks transferred so that we don't overflow + * the maximum request size. + */ + mmc->max_blk_count = mmc->max_req_size >> variant->datactrl_blocksz; + + spin_lock_init(&host->lock); + + writel(0, host->base + MMCIMASK0); + + if (variant->mmcimask1) + writel(0, host->base + MMCIMASK1); + + writel(0xfff, host->base + MMCICLEAR); + + /* + * If: + * - not using DT but using a descriptor table, or + * - using a table of descriptors ALONGSIDE DT, or + * look up these descriptors named "cd" and "wp" right here, fail + * silently of these do not exist + */ + if (!np) { + ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0); + if (ret == -EPROBE_DEFER) + goto clk_disable; + + ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0); + if (ret == -EPROBE_DEFER) + goto clk_disable; + } + + ret = devm_request_threaded_irq(&dev->dev, dev->irq[0], mmci_irq, + mmci_irq_thread, IRQF_SHARED, + DRIVER_NAME " (cmd)", host); + if (ret) + goto clk_disable; + + if (!dev->irq[1]) + host->singleirq = true; + else { + ret = devm_request_irq(&dev->dev, dev->irq[1], mmci_pio_irq, + IRQF_SHARED, DRIVER_NAME " (pio)", host); + if (ret) + goto clk_disable; + } + + writel(MCI_IRQENABLE | variant->start_err, host->base + MMCIMASK0); + + amba_set_drvdata(dev, mmc); + + dev_info(&dev->dev, "%s: PL%03x manf %x rev%u at 0x%08llx irq %d,%d (pio)\n", + mmc_hostname(mmc), amba_part(dev), amba_manf(dev), + amba_rev(dev), (unsigned long long)dev->res.start, + dev->irq[0], dev->irq[1]); + + mmci_dma_setup(host); + + pm_runtime_set_autosuspend_delay(&dev->dev, 50); + pm_runtime_use_autosuspend(&dev->dev); + + mmc_add_host(mmc); + + pm_runtime_put(&dev->dev); + return 0; + + clk_disable: + clk_disable_unprepare(host->clk); + host_free: + mmc_free_host(mmc); + return ret; +} + +static void mmci_remove(struct amba_device *dev) +{ + struct mmc_host *mmc = amba_get_drvdata(dev); + + if (mmc) { + struct mmci_host *host = mmc_priv(mmc); + struct variant_data *variant = host->variant; + + /* + * Undo pm_runtime_put() in probe. We use the _sync + * version here so that we can access the primecell. + */ + pm_runtime_get_sync(&dev->dev); + + mmc_remove_host(mmc); + + writel(0, host->base + MMCIMASK0); + + if (variant->mmcimask1) + writel(0, host->base + MMCIMASK1); + + writel(0, host->base + MMCICOMMAND); + writel(0, host->base + MMCIDATACTRL); + + mmci_dma_release(host); + clk_disable_unprepare(host->clk); + mmc_free_host(mmc); + } +} + +#ifdef CONFIG_PM +static void mmci_save(struct mmci_host *host) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + + writel(0, host->base + MMCIMASK0); + if (host->variant->pwrreg_nopower) { + writel(0, host->base + MMCIDATACTRL); + writel(0, host->base + MMCIPOWER); + writel(0, host->base + MMCICLOCK); + } + mmci_reg_delay(host); + + spin_unlock_irqrestore(&host->lock, flags); +} + +static void mmci_restore(struct mmci_host *host) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + + if (host->variant->pwrreg_nopower) { + writel(host->clk_reg, host->base + MMCICLOCK); + writel(host->datactrl_reg, host->base + MMCIDATACTRL); + writel(host->pwr_reg, host->base + MMCIPOWER); + } + writel(MCI_IRQENABLE | host->variant->start_err, + host->base + MMCIMASK0); + mmci_reg_delay(host); + + spin_unlock_irqrestore(&host->lock, flags); +} + +static int mmci_runtime_suspend(struct device *dev) +{ + struct amba_device *adev = to_amba_device(dev); + struct mmc_host *mmc = amba_get_drvdata(adev); + + if (mmc) { + struct mmci_host *host = mmc_priv(mmc); + pinctrl_pm_select_sleep_state(dev); + mmci_save(host); + clk_disable_unprepare(host->clk); + } + + return 0; +} + +static int mmci_runtime_resume(struct device *dev) +{ + struct amba_device *adev = to_amba_device(dev); + struct mmc_host *mmc = amba_get_drvdata(adev); + + if (mmc) { + struct mmci_host *host = mmc_priv(mmc); + clk_prepare_enable(host->clk); + mmci_restore(host); + pinctrl_select_default_state(dev); + } + + return 0; +} +#endif + +static const struct dev_pm_ops mmci_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(mmci_runtime_suspend, mmci_runtime_resume, NULL) +}; + +static const struct amba_id mmci_ids[] = { + { + .id = 0x00041180, + .mask = 0xff0fffff, + .data = &variant_arm, + }, + { + .id = 0x01041180, + .mask = 0xff0fffff, + .data = &variant_arm_extended_fifo, + }, + { + .id = 0x02041180, + .mask = 0xff0fffff, + .data = &variant_arm_extended_fifo_hwfc, + }, + { + .id = 0x00041181, + .mask = 0x000fffff, + .data = &variant_arm, + }, + /* ST Micro variants */ + { + .id = 0x00180180, + .mask = 0x00ffffff, + .data = &variant_u300, + }, + { + .id = 0x10180180, + .mask = 0xf0ffffff, + .data = &variant_nomadik, + }, + { + .id = 0x00280180, + .mask = 0x00ffffff, + .data = &variant_nomadik, + }, + { + .id = 0x00480180, + .mask = 0xf0ffffff, + .data = &variant_ux500, + }, + { + .id = 0x10480180, + .mask = 0xf0ffffff, + .data = &variant_ux500v2, + }, + { + .id = 0x00880180, + .mask = 0x00ffffff, + .data = &variant_stm32, + }, + { + .id = 0x10153180, + .mask = 0xf0ffffff, + .data = &variant_stm32_sdmmc, + }, + { + .id = 0x00253180, + .mask = 0xf0ffffff, + .data = &variant_stm32_sdmmcv2, + }, + /* Qualcomm variants */ + { + .id = 0x00051180, + .mask = 0x000fffff, + .data = &variant_qcom, + }, + /* ADI variant for SystemC models */ + { + .id = 0xF0041180, + .mask = 0xff0fffff, + .data = &variant_adi_systemc, + }, + { 0, 0 }, +}; + +MODULE_DEVICE_TABLE(amba, mmci_ids); + +static struct amba_driver mmci_driver = { + .drv = { + .name = DRIVER_NAME, + .pm = &mmci_dev_pm_ops, + }, + .probe = mmci_probe, + .remove = mmci_remove, + .id_table = mmci_ids, +}; + +module_amba_driver(mmci_driver); + +module_param(fmax, uint, 0444); + +MODULE_DESCRIPTION("ARM PrimeCell PL180/181 Multimedia Card Interface driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/sdhci-of-adi.c b/drivers/mmc/host/sdhci-of-adi.c new file mode 100644 index 00000000000000..24b38c14d5dde9 --- /dev/null +++ b/drivers/mmc/host/sdhci-of-adi.c @@ -0,0 +1,761 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ADI Driver for Synopsys DesignWare Cores Mobile Storage Host Controller + * It is based on sdhci-of-dwcmshc.c + * + * Copyright (c) 2023, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sdhci-pltfm.h" +#include "sdhci.h" + +/* Vendor register offsets */ +#define SDHCI_VENDOR1_MSHC_CTRL_R_OFF (0x508U) +#define SDHCI_VENDOR1_EMMC_CTRL_R_OFF (0x52CU) +#define SDHCI_VENDOR1_AT_CTRL_R_OFF (0x540U) + +/* Vendor register field bit masks */ +#define SDHCI_VENDOR1_NEGEDGE_DATAOUT_EN BIT(1) +#define SDHCI_VENDOR1_ENH_STROBE_EN_BM BIT(8) +#define SDHCI_VENDOR1_CARD_IS_EMMC BIT(0) +#define SDHCI_VENDOR1_AT_EN BIT(0) +#define SDHCI_VENDOR1_SWIN_TH_EN BIT(2) +#define SDHCI_VENDOR1_POST_CHANGE_DLY GENMASK(20, 19) +#define SDHCI_VENDOR1_TUNE_CLK_STOP_EN BIT(16) + +/* Vendor register field values */ +#define POST_CHANGE_DLY_LESS_4_CYCLES (0x03U) +#define TUNE_CLK_STOP_EN (1U) + +/* Vendor register Bit positions */ +#define POST_CHANGE_DLY_OFF (19U) +#define TUNE_CLK_STOP_EN_OFF (16U) + +#define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16) + +/* DWCMSHC specific Mode Select value */ +#define DWCMSHC_CTRL_HS400 0x7 + +#define BOUNDARY_OK(addr, len) \ + ((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1))) + +/* SDHCI PHY configure operations */ +#define SDHCI_PHY_OPS_CFG_DLL_NO_CLK (1U) +#define SDHCI_PHY_OPS_ENABLE_DLL_AFTER_CLK (2U) +#define SDHCI_PHY_OPS_SET_DELAY (3U) + +/* To this day, ADI design integrates the 1.8V IP versions (Host Controller + * and PHY for eMMC and Host Controller for SD). All eMMC speed modes are + * supported and all modes can operate at 1.8V. On the other side, only low speed + * modes are supported in SD card and those modes can only operate at 3.3V. That + * is solved by adding an externel level shifter. + * Driver does not manage well this SD scenario. + * Note: This macro is just to identify the action taken on this issue + */ +#define SDHCI_ADI_IP_1_8V + +/* PHY Delay Lines may cause a potential glitch on the RX clock (because PHY DL2 + * input (rx clock) is connected to PHY DL1 output (tx clock)). Delay lines + * configuration comes from Synopsys, and.it is expected not to change for future + * products. + * Note: This macro is just to identify the action taken on this issue + */ +#define SDHCI_ADI_RX_CLOCK_GLITCH + +/* MMC HS400 in Adrv906x requires data to be sent out on negedge of cclk_tx. + * This is a soc-specific requirement (coming from Adrv906x GLS simulations). + */ +#define SDHCI_ADI_HS400_TX_CLK_NEGEDGE + +#define SDHCI_IDLE_TIMEOUT (20) /* 20 ms */ + +/* There are up to 128 delay cells (each one around 50 ps) */ +#define MAX_DELAY_LINE_TAPS (128) + +struct dwcmshc_priv { + struct clk *bus_clk; + struct phy *phy; + bool is_emmc; + bool phy_init_done; +}; + +/* + * If DMA addr spans 128MB boundary, we split the DMA transfer into two + * so that each DMA transfer doesn't exceed the boundary. + */ +static void dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc, + dma_addr_t addr, int len, unsigned int cmd) +{ + int tmplen, offset; + + if (likely(!len || BOUNDARY_OK(addr, len))) { + sdhci_adma_write_desc(host, desc, addr, len, cmd); + return; + } + + offset = addr & (SZ_128M - 1); + tmplen = SZ_128M - offset; + sdhci_adma_write_desc(host, desc, addr, tmplen, cmd); + + addr += tmplen; + len -= tmplen; + sdhci_adma_write_desc(host, desc, addr, len, cmd); +} + +static void dwcmshc_check_auto_cmd23(struct mmc_host *mmc, + struct mmc_request *mrq) +{ + struct sdhci_host *host = mmc_priv(mmc); + + /* + * No matter V4 is enabled or not, ARGUMENT2 register is 32-bit + * block count register which doesn't support stuff bits of + * CMD23 argument on dwcmsch host controller. + */ + if (mrq->sbc && (mrq->sbc->arg & SDHCI_DWCMSHC_ARG2_STUFF)) + host->flags &= ~SDHCI_AUTO_CMD23; + else + host->flags |= SDHCI_AUTO_CMD23; +} + +static void dwcmshc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + dwcmshc_check_auto_cmd23(mmc, mrq); + + sdhci_request(mmc, mrq); +} + +static void adi_sdhci_set_clk_tx_negedge(struct sdhci_host *host, bool enable) +{ + u16 mshc_ctrl; + + mshc_ctrl = sdhci_readw(host, SDHCI_VENDOR1_MSHC_CTRL_R_OFF); + if (enable) + mshc_ctrl |= SDHCI_VENDOR1_NEGEDGE_DATAOUT_EN; + else + mshc_ctrl &= ~SDHCI_VENDOR1_NEGEDGE_DATAOUT_EN; + sdhci_writew(host, mshc_ctrl, SDHCI_VENDOR1_MSHC_CTRL_R_OFF); +} + +static void dwcmshc_set_uhs_signaling(struct sdhci_host *host, + unsigned int timing) +{ + u16 ctrl_2; + + /* Workaround: wrong SDHCI_CTRL_HS400 definition in general framework */ + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); + /* Select Bus Speed Mode for host */ + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; + if ((timing == MMC_TIMING_MMC_HS200) || + (timing == MMC_TIMING_UHS_SDR104)) + ctrl_2 |= SDHCI_CTRL_UHS_SDR104; + else if (timing == MMC_TIMING_UHS_SDR12) + ctrl_2 |= SDHCI_CTRL_UHS_SDR12; + else if ((timing == MMC_TIMING_UHS_SDR25) || + (timing == MMC_TIMING_MMC_HS)) + ctrl_2 |= SDHCI_CTRL_UHS_SDR25; + else if (timing == MMC_TIMING_UHS_SDR50) + ctrl_2 |= SDHCI_CTRL_UHS_SDR50; + else if ((timing == MMC_TIMING_UHS_DDR50) || + (timing == MMC_TIMING_MMC_DDR52)) + ctrl_2 |= SDHCI_CTRL_UHS_DDR50; + else if (timing == MMC_TIMING_MMC_HS400) + ctrl_2 |= DWCMSHC_CTRL_HS400; + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); + +#ifdef SDHCI_ADI_HS400_TX_CLK_NEGEDGE + if (timing == MMC_TIMING_MMC_HS400) + /* Required to meet timing (from GLS simulations) */ + adi_sdhci_set_clk_tx_negedge(host, true); + else +#endif + adi_sdhci_set_clk_tx_negedge(host, false); +} + +#ifdef SDHCI_ADI_RX_CLOCK_GLITCH +static void adi_sdhci_fix_rx_clock_glitch(struct sdhci_host *host, u8 hs_timing) +{ + u32 reg; + + reg = sdhci_readl(host, SDHCI_VENDOR1_AT_CTRL_R_OFF); + reg &= ~(SDHCI_VENDOR1_POST_CHANGE_DLY | SDHCI_VENDOR1_TUNE_CLK_STOP_EN); + /* This configuration helps to fix this issue (verified in RTL and GLS simulations) */ + if ((hs_timing == MMC_TIMING_MMC_HS400) || + (hs_timing == MMC_TIMING_MMC_HS200)) + reg |= (POST_CHANGE_DLY_LESS_4_CYCLES << POST_CHANGE_DLY_OFF); + else + reg |= (POST_CHANGE_DLY_LESS_4_CYCLES << POST_CHANGE_DLY_OFF) | + (TUNE_CLK_STOP_EN << TUNE_CLK_STOP_EN_OFF); + sdhci_writel(host, reg, SDHCI_VENDOR1_AT_CTRL_R_OFF); +} +#endif + +/* + * PHY configuration is done through: + * int phy_configure(struct phy *phy, union phy_configure_opts *opts); + * where: + * union phy_configure_opts { + * struct phy_configure_opts_mipi_dphy mipi_dphy; + * struct phy_configure_opts_dp dp; + * }; + * + * None of the above structs meet MMC requirements, so a new one should be + * added: + * struct phy_configure_opts_sdhci sdhci; + * + * By now let's reuse 'dp' one for MMC purposes + */ +static int adi_sdhci_set_delay(struct sdhci_host *host, struct phy *phy, u8 hs_timing) +{ + union phy_configure_opts opts; + + if (!phy) + return 0; + +#ifdef SDHCI_ADI_RX_CLOCK_GLITCH + /* eMMC PHY delay lines may cause a glitch on RX clock */ + adi_sdhci_fix_rx_clock_glitch(host, hs_timing); +#endif + + /* Reusing dp struct: link_rate as MMC operation event and lanes as speed mode */ + opts.dp.link_rate = SDHCI_PHY_OPS_SET_DELAY; + opts.dp.lanes = hs_timing; + + return phy_configure(phy, &opts); +} + +static int adi_sdhci_set_dll(struct phy *phy, u8 hs_timing, bool enable) +{ + union phy_configure_opts opts; + + if (!phy) + return 0; + + /* Reusing dp struct: link_rate as MMC operation event */ + opts.dp.link_rate = enable ? SDHCI_PHY_OPS_ENABLE_DLL_AFTER_CLK : + SDHCI_PHY_OPS_CFG_DLL_NO_CLK; + + return phy_configure(phy, &opts); +} + +static u16 adi_sdhci_calc_clk(struct sdhci_host *host, unsigned int clock, + unsigned int *actual_clock) +{ + int div = 0; + int real_div = div; + u16 clk = 0; + + /* Workaround: ADRV906X does not use SDHCI_DIV as specified + * in the SDHCI spec. Instead of 1/(2N), it is 1/(N+1). + */ + if (host->max_clk <= clock) { + div = 0; + } else { + div = (host->max_clk / clock) - 1U; + if ((host->max_clk % clock) != 0) + div++; + } + real_div = div + 1; + + if (real_div) + *actual_clock = host->max_clk / real_div; + clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; + clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) + << SDHCI_DIVIDER_HI_SHIFT; + + return clk; +} + +static void adi_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) +{ + struct sdhci_pltfm_host *pltfm_host; + struct dwcmshc_priv *priv; + int ret; + u16 clk; + u8 hs_timing; + bool dll_en; + + host->mmc->actual_clock = 0; + + /* Stop clock */ + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + + if (clock == 0) + return; + + pltfm_host = sdhci_priv(host); + priv = sdhci_pltfm_priv(pltfm_host); + + hs_timing = host->mmc->ios.timing; + /* + * General sdhci framework calls 'set_clock' twice after configuring + * HS400. One before updating clock variable to 200 MHz (so still using + * 52 Mhz clock as in HS mode) and one after it. + * DLL fails to lock at clock freq lower than 100Mhz, so let's avoid DLL + * configuration before clock var is high enough. + */ + dll_en = (hs_timing == MMC_TIMING_MMC_HS400) && + (clock > MMC_HIGH_52_MAX_DTR); + + /* Configure Delay Lines (PHY instance 1) */ + ret = adi_sdhci_set_delay(host, priv->phy, hs_timing); + if (ret) { + pr_err("Error setting delay lines %d\n", ret); + return; + } + + /* Disable and configure DLL (PHY instance 2) */ + ret = adi_sdhci_set_dll(priv->phy, hs_timing, false); + if (ret) { + pr_err("Error setting DLL %d\n", ret); + return; + } + + /* Configure and enable clock */ + clk = adi_sdhci_calc_clk(host, clock, &host->mmc->actual_clock); + sdhci_enable_clk(host, clk); + + /* Enable DLL and wait for it to lock */ + if (dll_en) { + ret = adi_sdhci_set_dll(priv->phy, hs_timing, true); + if (ret) { + pr_err("Error enabling DLL %d\n", ret); + return; + } + } +} + +static void adi_sdhci_reset(struct sdhci_host *host, u8 mask) +{ + int16_t reg; + bool reset_all; + + reset_all = (mask & SDHCI_RESET_ALL) == SDHCI_RESET_ALL; + + if (reset_all) + /* Save CARD_IS_EMMC bit state */ + reg = sdhci_readw(host, SDHCI_VENDOR1_EMMC_CTRL_R_OFF) & SDHCI_VENDOR1_CARD_IS_EMMC; + + sdhci_reset(host, mask); + + if (reset_all) { + /* Restore CARD_IS_EMMC bit */ + reg = reg | (sdhci_readw(host, SDHCI_VENDOR1_EMMC_CTRL_R_OFF) & ~SDHCI_VENDOR1_CARD_IS_EMMC); + sdhci_writew(host, reg, SDHCI_VENDOR1_EMMC_CTRL_R_OFF); + } +} + +/* Hook used for a totally different purpose (no reset). + * mmc_rescan function is doing the card initialization (CMD0, CMD1, ...) + * asynchronously to this ADI driver probe. We need to ensure that card + * initialization does not happen before probe is complete (which includes PHY + * init configuration as the last step) + * Note: this workaround does only apply to eMMC, since SD path does not include + * a PHY instance to configure). + */ +static void adi_sdhci_hw_reset(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host; + struct dwcmshc_priv *priv; + int max_cnt = 200; + + pltfm_host = sdhci_priv(host); + priv = sdhci_pltfm_priv(pltfm_host); + + if (priv->is_emmc) { + if (!priv) { + pr_err("host priv is null"); + return; + } + + while (!priv->phy_init_done) { + if (--max_cnt <= 0) { + pr_err("PHY init not completed on time"); + return; + } + + mdelay(10); + } + } +} + +/* This hook is used to inject a workaround for an issue that has nothing to do + * with tuning, but it is the only place to do it. + * This code is a copy-paste from sdhci_execute_tuning (sdhci.c file), except + * for the workaround. + */ +static int adi_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) +{ + int err = 0; + unsigned int tuning_count = 0; + bool hs400_tuning; + + hs400_tuning = host->flags & SDHCI_HS400_TUNING; + + if (host->tuning_mode == SDHCI_TUNING_MODE_1) + tuning_count = host->tuning_count; + + /* + * The Host Controller needs tuning in case of SDR104 and DDR50 + * mode, and for SDR50 mode when Use Tuning for SDR50 is set in + * the Capabilities register. + * If the Host Controller supports the HS200 mode then the + * tuning function has to be executed. + */ + switch (host->timing) { + /* HS400 tuning is done in HS200 mode */ + case MMC_TIMING_MMC_HS400: + err = -EINVAL; + goto out; + + case MMC_TIMING_MMC_HS200: + /* + * Periodic re-tuning for HS400 is not expected to be needed, so + * disable it here. + */ + if (hs400_tuning) + tuning_count = 0; + break; + + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_UHS_DDR50: + break; + + case MMC_TIMING_UHS_SDR50: + if (host->flags & SDHCI_SDR50_NEEDS_TUNING) + break; + fallthrough; + + default: + goto out; + } + host->mmc->retune_period = tuning_count; + + if (host->tuning_delay < 0) + host->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK; + + sdhci_start_tuning(host); + + host->tuning_err = __sdhci_execute_tuning(host, opcode); + + sdhci_end_tuning(host); + + /* When configuring HS400, the next step in the sequence after tuning + * (in HS200) is to downgrade to HS mode. This operation (CMD6) fails + * sporadically. + * Issue root cause:Unclear + * Fix options (empirically found): + * - repeat CMD6 (empirically it only fails the first attempt). This is + * general Linux driver code (not ADI code) + * - reduce clock freq to 52MHz (this can be done in this hook) + */ + + /* Reduce frequency to HS frequency */ + if (host->flags & SDHCI_HS400_TUNING) { + struct mmc_ios *ios = &host->mmc->ios; + host->mmc->ios.clock = 52000000; + sdhci_set_ios(host->mmc, ios); + } + + return 0; +out: + return -1; +} + +static const struct sdhci_ops sdhci_dwcmshc_ops = { + .set_clock = adi_sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = dwcmshc_set_uhs_signaling, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .reset = adi_sdhci_reset, + .adma_write_desc = dwcmshc_adma_write_desc, + .hw_reset = adi_sdhci_hw_reset, + .platform_execute_tuning = adi_sdhci_execute_tuning, +}; + +static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = { + .ops = &sdhci_dwcmshc_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, +}; + +static void adi_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc, + struct mmc_ios *ios) +{ + struct sdhci_host *host = mmc_priv(mmc); + u16 emmc_ctrl; + + emmc_ctrl = sdhci_readw(host, SDHCI_VENDOR1_EMMC_CTRL_R_OFF); + if (ios->enhanced_strobe) + emmc_ctrl |= SDHCI_VENDOR1_ENH_STROBE_EN_BM; + else + emmc_ctrl &= ~SDHCI_VENDOR1_ENH_STROBE_EN_BM; + sdhci_writew(host, emmc_ctrl, SDHCI_VENDOR1_EMMC_CTRL_R_OFF); +} + +static int adi_sdhci_deinit(struct sdhci_host *host) +{ + uint16_t u16_reg_data; + unsigned int timeout = SDHCI_IDLE_TIMEOUT; + + /* Wait for the host controller to become idle before stopping card clock.*/ + while (sdhci_readl(host, SDHCI_PRESENT_STATE) & + (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) { + if (--timeout == 0) { + pr_err("Host controller is not idle\n"); + return -1; + } + udelay(1000); + } + + /* Stop card clock, and turn off internal clock and PLL */ + u16_reg_data = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + u16_reg_data &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN | SDHCI_CLOCK_PLL_EN); + sdhci_writew(host, u16_reg_data, SDHCI_CLOCK_CONTROL); + + return 0; +} + +static int dwcmshc_probe(struct platform_device *pdev) +{ + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_host *host; + struct dwcmshc_priv *priv; + int err; + u32 extra; + u32 u32_reg_data; + u32 retune_period; + bool is_emmc; + + host = sdhci_pltfm_init(pdev, &sdhci_dwcmshc_pdata, + sizeof(struct dwcmshc_priv)); + if (IS_ERR(host)) + return PTR_ERR(host); + + pltfm_host = sdhci_priv(host); + priv = sdhci_pltfm_priv(pltfm_host); + + is_emmc = device_property_read_bool(&pdev->dev, "non-removable"); + priv->phy_init_done = 0; + priv->is_emmc = is_emmc ? 1 : 0; + + /* Deinit to ensure a proper initialization */ + err = adi_sdhci_deinit(host); + if (err) + goto free_pltfm; + + if (is_emmc) { + int16_t emmc_ctrl; + + /* Set CARD_IS_EMMC bit */ + emmc_ctrl = sdhci_readw(host, SDHCI_VENDOR1_EMMC_CTRL_R_OFF); + if (!(emmc_ctrl & SDHCI_VENDOR1_CARD_IS_EMMC)) { + emmc_ctrl |= SDHCI_VENDOR1_CARD_IS_EMMC; + sdhci_writew(host, emmc_ctrl, SDHCI_VENDOR1_EMMC_CTRL_R_OFF); + } + +#ifdef SDHCI_ADI_RX_CLOCK_GLITCH + adi_sdhci_fix_rx_clock_glitch(host, MMC_TIMING_LEGACY); +#endif + } +#ifdef SDHCI_ADI_IP_1_8V + /* + * Workaround for SD interface + */ + else { + /* SD interface design is a bit special. IP side always works at + * 1.8V and the card side always works at 3.3V. + * A level shifter is added between the ASIC and the card. + * + * SD cards must work at 3.3V during initialization (ignoring + * newer LVS cards), and then depending on the speed mode used, + * it would require a switch to 1.8V. ADI design does not support + * that cases (only default and HS modes, both at 3.3V) + * + * Driver follows this flow, by configuring the initial signal + * voltage (HOST_CTRL2_R.SINGNALING_EN) to 3.3V ... and that + * behaviour can not be modified from device tree unless both: + * - a 1.8V vqmmc regulator is added (which does not apply to SD) + * - and, at least, one high speed mode is supported (which is + * not the case). + * Let's indicate (force) that the only voltage supported (from + * Host Controller point of view) is 1.8V. + */ + host->flags &= ~SDHCI_SIGNALING_330; + host->flags |= SDHCI_SIGNALING_180; + } +#endif + + /* + * extra adma table cnt for cross 128M boundary handling. + */ + extra = DIV_ROUND_UP_ULL(dma_get_required_mask(&pdev->dev), SZ_128M); + if (extra > SDHCI_MAX_SEGS) + extra = SDHCI_MAX_SEGS; + host->adma_table_cnt += extra; + + pltfm_host->clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(pltfm_host->clk)) { + err = PTR_ERR(pltfm_host->clk); + dev_err(&pdev->dev, "failed to get core clk: %d\n", err); + goto free_pltfm; + } + err = clk_prepare_enable(pltfm_host->clk); + if (err) + goto free_pltfm; + + priv->bus_clk = devm_clk_get(&pdev->dev, "bus"); + if (!IS_ERR(priv->bus_clk)) + clk_prepare_enable(priv->bus_clk); + + err = mmc_of_parse(host->mmc); + if (err) + goto err_clk; + + sdhci_get_of_property(pdev); + + host->mmc_host_ops.request = dwcmshc_request; + host->mmc_host_ops.hs400_enhanced_strobe = adi_sdhci_hs400_enhanced_strobe; + + err = sdhci_add_host(host); + if (err) + goto err_clk; + + if (is_emmc) { + /* Tuning procedure configuration: */ + + /* Initial tuning: sweep through all the taps (128) */ + host->tuning_loop_count = MAX_DELAY_LINE_TAPS; + u32_reg_data = sdhci_readl(host, SDHCI_VENDOR1_AT_CTRL_R_OFF); + u32_reg_data &= ~SDHCI_VENDOR1_SWIN_TH_EN; + + /* Configure re-tuning mode */ + if (device_property_read_u32(&pdev->dev, "adi,retune-period", &retune_period) >= 0) { + /* Re-tuning mode 1 (periodic) */ + host->tuning_mode = SDHCI_TUNING_MODE_1; + host->tuning_count = retune_period; /* Unit: seconds */ + + u32_reg_data &= ~SDHCI_VENDOR1_AT_EN; + } else { + /* Re-tuning mode 3 (Auto-tuning) */ + host->tuning_mode = SDHCI_TUNING_MODE_3; + } + + sdhci_writel(host, u32_reg_data, SDHCI_VENDOR1_AT_CTRL_R_OFF); + } + + if (device_property_read_bool(&pdev->dev, "enable-phy-config")) { + priv->phy = devm_phy_get(&pdev->dev, "phy_adi_sdhci"); + if (IS_ERR(priv->phy)) { + err = dev_err_probe(&pdev->dev, PTR_ERR(priv->phy), + "No phy for sdhci-of-dwcmshc.\n"); + goto remove_host; + } + err = phy_init(priv->phy); + if (err < 0) { + dev_err(&pdev->dev, "phy_init err: %d\n", err); + goto remove_host; + } + priv->phy_init_done = 1; + } else { + priv->phy = NULL; + } + + return 0; + +remove_host: + sdhci_remove_host(host, 0); +err_clk: + clk_disable_unprepare(pltfm_host->clk); + clk_disable_unprepare(priv->bus_clk); +free_pltfm: + sdhci_pltfm_free(pdev); + return err; +} + +static void dwcmshc_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + + sdhci_remove_host(host, 0); + + clk_disable_unprepare(pltfm_host->clk); + clk_disable_unprepare(priv->bus_clk); + + sdhci_pltfm_free(pdev); +} + +#ifdef CONFIG_PM_SLEEP +static int dwcmshc_suspend(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + int ret; + + ret = sdhci_suspend_host(host); + if (ret) + return ret; + + clk_disable_unprepare(pltfm_host->clk); + if (!IS_ERR(priv->bus_clk)) + clk_disable_unprepare(priv->bus_clk); + + return ret; +} + +static int dwcmshc_resume(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + int ret; + + ret = clk_prepare_enable(pltfm_host->clk); + if (ret) + return ret; + + if (!IS_ERR(priv->bus_clk)) { + ret = clk_prepare_enable(priv->bus_clk); + if (ret) + return ret; + } + + return sdhci_resume_host(host); +} +#endif + +static SIMPLE_DEV_PM_OPS(dwcmshc_pmops, dwcmshc_suspend, dwcmshc_resume); + +static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { + { .compatible = "adi,dwcmshc-sdhci" }, + {} +}; +MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); + +static struct platform_driver sdhci_dwcmshc_driver = { + .driver = { + .name = "sdhci-dwcmshc", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = sdhci_dwcmshc_dt_ids, + .pm = &dwcmshc_pmops, + }, + .probe = dwcmshc_probe, + .remove = dwcmshc_remove, +}; +module_platform_driver(sdhci_dwcmshc_driver); + +MODULE_DESCRIPTION("ADI SDHCI platform driver for Synopsys DWC MSHC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index 9a542e3c9b05d8..4e64b23a915fa9 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -18,6 +18,7 @@ config MDIO source "drivers/net/ethernet/3com/Kconfig" source "drivers/net/ethernet/actions/Kconfig" source "drivers/net/ethernet/adaptec/Kconfig" +source "drivers/net/ethernet/adi/Kconfig" source "drivers/net/ethernet/aeroflex/Kconfig" source "drivers/net/ethernet/agere/Kconfig" source "drivers/net/ethernet/alacritech/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 99fa180dedb805..cc75deb9048f30 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_NET_VENDOR_3COM) += 3com/ obj-$(CONFIG_NET_VENDOR_8390) += 8390/ obj-$(CONFIG_NET_VENDOR_ACTIONS) += actions/ obj-$(CONFIG_NET_VENDOR_ADAPTEC) += adaptec/ +obj-$(CONFIG_NET_ADRV906X) += adi/ obj-$(CONFIG_GRETH) += aeroflex/ obj-$(CONFIG_NET_VENDOR_ADI) += adi/ obj-$(CONFIG_NET_VENDOR_AGERE) += agere/ diff --git a/drivers/net/ethernet/adi/Kconfig b/drivers/net/ethernet/adi/Kconfig index 760a9a60bc15c1..c9ccb3ee08cdcd 100644 --- a/drivers/net/ethernet/adi/Kconfig +++ b/drivers/net/ethernet/adi/Kconfig @@ -26,4 +26,16 @@ config ADIN1110 Say yes here to build support for Analog Devices ADIN1110 Low Power 10BASE-T1L Ethernet MAC-PHY. +# SPDX-License-Identifier: GPL-2.0-only +# +config NET_ADRV906X + tristate "ADRV906X Ethernet MAC support" + depends on HAS_DMA + select LED_TRIGGER_PHY + select PHYLIB + help + This driver supports the ADRV906X Ethernet MAC. + To compile this driver as a module, choose M here. The module + will be called adrv906x_eth. + endif # NET_VENDOR_ADI diff --git a/drivers/net/ethernet/adi/Makefile b/drivers/net/ethernet/adi/Makefile index d0383d94303c80..a9d130988a03ad 100644 --- a/drivers/net/ethernet/adi/Makefile +++ b/drivers/net/ethernet/adi/Makefile @@ -4,3 +4,9 @@ # obj-$(CONFIG_ADIN1110) += adin1110.o + +obj-$(CONFIG_NET_ADRV906X) += adrv906x-eth.o +adrv906x-eth-y := adrv906x-net.o adrv906x-mac.o adrv906x-switch.o adrv906x-ndma.o \ + adrv906x-ethtool.o adrv906x-mdio.o adrv906x-tsu.o adrv906x-cmn.o \ + adrv906x-phy.o adrv906x-phy-serdes.o +adrv906x-eth-$(CONFIG_MACSEC) += adrv906x-macsec-ext.o macsec/cco_macsec.o diff --git a/drivers/net/ethernet/adi/adrv906x-cmn.c b/drivers/net/ethernet/adi/adrv906x-cmn.c new file mode 100644 index 00000000000000..4c7490bb91367f --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-cmn.c @@ -0,0 +1,448 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024-2025, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adrv906x-net.h" +#include "adrv906x-cmn.h" + +#define EMAC_CMN_DIGITAL_CTRL0 0x0010 +#define EMAC_CMN_RX_LINK0_EN BIT(0) +#define EMAC_CMN_RX_LINK1_EN BIT(1) +#define EMAC_CMN_TX_LINK0_EN BIT(4) +#define EMAC_CMN_TX_LINK1_EN BIT(5) +#define EMAC_CMN_SW_LINK0_BYPASS_EN BIT(8) +#define EMAC_CMN_SW_LINK1_BYPASS_EN BIT(9) +#define EMAC_CMN_SW_PORT0_EN BIT(12) +#define EMAC_CMN_SW_PORT1_EN BIT(13) +#define EMAC_CMN_SW_PORT2_EN BIT(14) +#define EMAC_CMN_MACSEC_BYPASS_EN BIT(16) +#define EMAC_CMN_CDR_DIV_PORT0_EN BIT(20) +#define EMAC_CMN_CDR_DIV_PORT1_EN BIT(21) +#define EMAC_CMN_CDR_SEL BIT(24) +#define EMAC_CMN_DIGITAL_CTRL1 0x0014 +#define EMAC_CMN_RECOVERED_CLK_DIV_0 GENMASK(12, 0) +#define EMAC_CMN_RECOVERED_CLK_DIV_1 GENMASK(28, 16) +#define EMAC_CMN_DIGITAL_CTRL2 0x0018 +#define EMAC_CMN_LOOPBACK_BYPASS_MAC_0 BIT(28) +#define EMAC_CMN_LOOPBACK_BYPASS_MAC_1 BIT(29) +#define EMAC_CMN_LOOPBACK_BYPASS_PCS_0 BIT(24) +#define EMAC_CMN_LOOPBACK_BYPASS_PCS_1 BIT(25) +#define EMAC_CMN_LOOPBACK_BYPASS_DESER_0 BIT(20) +#define EMAC_CMN_LOOPBACK_BYPASS_DESER_1 BIT(21) +#define EMAC_CMN_TX_BIT_REPEAT_RATIO BIT(0) +#define EMAC_CMN_DIGITAL_CTRL3 0x001c +#define EMAC_CMN_SW_PORT2_DSA_INSERT_EN BIT(20) +#define EMAC_CMN_DIGITAL_CTRL4 0x0020 +#define EMAC_CMN_PCS_STATUS_NE_CNT_0 GENMASK(7, 0) +#define EMAC_CMN_PCS_STATUS_NE_CNT_1 GENMASK(15, 8) +#define EMAC_CMN_CLEAR_PCS_STATUS_NE_CNT BIT(16) +#define EMAC_CMN_RST_REG 0x0030 +#define EMAC_CMN_PHY_CTRL 0x0040 +#define EMAC_CMN_RXDES_DIG_RESET_N_0 BIT(0) +#define EMAC_CMN_RXDES_DIG_RESET_N_1 BIT(1) +#define EMAC_CMN_RXDES_FORCE_LANE_PD_0 BIT(4) +#define EMAC_CMN_RXDES_FORCE_LANE_PD_1 BIT(5) +#define EMAC_CMN_TXSER_DIG_RESET_N_0 BIT(8) +#define EMAC_CMN_TXSER_DIG_RESET_N_1 BIT(9) +#define EMAC_CMN_TXSER_FORCE_LANE_PD_0 BIT(12) +#define EMAC_CMN_TXSER_FORCE_LANE_PD_1 BIT(13) +#define EMAC_CMN_SERDES_REG_RESET_N BIT(16) +#define EMAC_CMN_TXSER_SYNC_TRIGGER_0 BIT(20) +#define EMAC_CMN_TXSER_SYNC_TRIGGER_1 BIT(21) +#define EMAC_CMN_TXSER_SYNC_OVERRIDE_EN_0 BIT(24) +#define EMAC_CMN_TXSER_SYNC_OVERRIDE_EN_1 BIT(25) +#define EMAC_CMN_TXSER_SYNC_OVERRIDE_VAL_0 BIT(28) +#define EMAC_CMN_TXSER_SYNC_OVERRIDE_VAL_1 BIT(29) +#define EMAC_CMN_PLL_CTRL 0x0050 +#define EMAC_CMN_PLL_MEM_MAP_RESET_N BIT(0) +#define EMAC_CMN_GPIO_SELECT 0x0060 +#define EMAC_CMN_EMAC_SPARE 0x3000 + +void adrv906x_eth_cmn_pll_reset(struct net_device *ndev) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + void __iomem *regs = eth_if->emac_cmn_regs; + unsigned int val; + + mutex_lock(ð_if->mtx); + + val = ioread32(regs + EMAC_CMN_PLL_CTRL); + val &= ~EMAC_CMN_PLL_MEM_MAP_RESET_N; + iowrite32(val, regs + EMAC_CMN_PLL_CTRL); + usleep_range(50, 60); + val |= EMAC_CMN_PLL_MEM_MAP_RESET_N; + iowrite32(val, regs + EMAC_CMN_PLL_CTRL); + + mutex_unlock(ð_if->mtx); +} + +void adrv906x_eth_cmn_ser_tx_sync_trigger(struct net_device *ndev) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + void __iomem *regs = eth_if->emac_cmn_regs; + unsigned int val, bit_mask; + + mutex_lock(ð_if->mtx); + + bit_mask = (adrv906x_dev->port == 0) ? + EMAC_CMN_TXSER_SYNC_TRIGGER_0 : EMAC_CMN_TXSER_SYNC_TRIGGER_1; + val = ioread32(regs + EMAC_CMN_PHY_CTRL); + val |= bit_mask; + iowrite32(val, regs + EMAC_CMN_PHY_CTRL); + usleep_range(1, 10); + val &= ~bit_mask; + iowrite32(val, regs + EMAC_CMN_PHY_CTRL); + + mutex_unlock(ð_if->mtx); +} + +void adrv906x_eth_cmn_ser_pwr_down(struct net_device *ndev) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + void __iomem *regs = eth_if->emac_cmn_regs; + unsigned int val, bit_mask; + + mutex_lock(ð_if->mtx); + + bit_mask = (adrv906x_dev->port == 0) ? + EMAC_CMN_TXSER_FORCE_LANE_PD_0 : EMAC_CMN_TXSER_FORCE_LANE_PD_1; + val = ioread32(regs + EMAC_CMN_PHY_CTRL); + val |= bit_mask; + iowrite32(val, regs + EMAC_CMN_PHY_CTRL); + usleep_range(10, 20); + + mutex_unlock(ð_if->mtx); +} + +void adrv906x_eth_cmn_ser_pwr_up_and_reset(struct net_device *ndev) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + void __iomem *regs = eth_if->emac_cmn_regs; + unsigned int val, bit_mask; + + mutex_lock(ð_if->mtx); + + bit_mask = (adrv906x_dev->port == 0) ? + EMAC_CMN_TXSER_FORCE_LANE_PD_0 : EMAC_CMN_TXSER_FORCE_LANE_PD_1; + val = ioread32(regs + EMAC_CMN_PHY_CTRL); + val &= ~bit_mask; + iowrite32(val, regs + EMAC_CMN_PHY_CTRL); + usleep_range(1, 10); + + bit_mask = (adrv906x_dev->port == 0) ? + EMAC_CMN_TXSER_DIG_RESET_N_0 : EMAC_CMN_TXSER_DIG_RESET_N_1; + val &= ~bit_mask; + iowrite32(val, regs + EMAC_CMN_PHY_CTRL); + usleep_range(1, 10); + val |= bit_mask; + iowrite32(val, regs + EMAC_CMN_PHY_CTRL); + usleep_range(1, 10); + + mutex_unlock(ð_if->mtx); +} + +void adrv906x_eth_cmn_deser_pwr_down(struct net_device *ndev) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + void __iomem *regs = eth_if->emac_cmn_regs; + unsigned int val, bit_mask; + + mutex_lock(ð_if->mtx); + + bit_mask = (adrv906x_dev->port == 0) ? + EMAC_CMN_RXDES_FORCE_LANE_PD_0 : EMAC_CMN_RXDES_FORCE_LANE_PD_1; + val = ioread32(regs + EMAC_CMN_PHY_CTRL); + val |= bit_mask; + iowrite32(val, regs + EMAC_CMN_PHY_CTRL); + usleep_range(10, 20); + + mutex_unlock(ð_if->mtx); +} + +void adrv906x_eth_cmn_deser_pwr_up_and_reset(struct net_device *ndev) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + void __iomem *regs = eth_if->emac_cmn_regs; + unsigned int val, bit_mask; + + mutex_lock(ð_if->mtx); + + bit_mask = (adrv906x_dev->port == 0) ? + EMAC_CMN_RXDES_FORCE_LANE_PD_0 : EMAC_CMN_RXDES_FORCE_LANE_PD_1; + val = ioread32(regs + EMAC_CMN_PHY_CTRL); + val &= ~bit_mask; + iowrite32(val, regs + EMAC_CMN_PHY_CTRL); + usleep_range(1, 10); + + bit_mask = (adrv906x_dev->port == 0) ? + EMAC_CMN_RXDES_DIG_RESET_N_0 : EMAC_CMN_RXDES_DIG_RESET_N_1; + val &= ~bit_mask; + iowrite32(val, regs + EMAC_CMN_PHY_CTRL); + usleep_range(1, 10); + val |= bit_mask; + iowrite32(val, regs + EMAC_CMN_PHY_CTRL); + usleep_range(1, 10); + + mutex_unlock(ð_if->mtx); +} + +int adrv906x_eth_cmn_rst_reg(void __iomem *regs) +{ + unsigned int val; + + val = REGMAP_RESET_SWITCH + | REGMAP_RESET_PCS_MAC0 + | REGMAP_RESET_PCS_MAC1 + | REGMAP_RESET_MACSEC0 + | REGMAP_RESET_MACSEC1; + + iowrite32(val, regs + EMAC_CMN_RST_REG); + iowrite32(0, regs + EMAC_CMN_RST_REG); + + return 0; +} + +void adrv906x_eth_cmn_recovered_clk_config(struct adrv906x_eth_dev *adrv906x_dev) +{ + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + void __iomem *regs = eth_if->emac_cmn_regs; + struct net_device *ndev = adrv906x_dev->ndev; + struct phy_device *phydev = ndev->phydev; + u32 val; + + mutex_lock(ð_if->mtx); + val = (phydev->speed == SPEED_25000) ? eth_if->recovered_clk_div_25g - 1 : + eth_if->recovered_clk_div_10g - 1; + val = FIELD_PREP(EMAC_CMN_RECOVERED_CLK_DIV_0, val); + val |= FIELD_PREP(EMAC_CMN_RECOVERED_CLK_DIV_1, val); + iowrite32(val, regs + EMAC_CMN_DIGITAL_CTRL1); + mutex_unlock(ð_if->mtx); +} + +void adrv906x_eth_cmn_mode_cfg(struct net_device *ndev) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + void __iomem *regs = adrv906x_dev->parent->emac_cmn_regs; + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + struct phy_device *phydev = ndev->phydev; + u32 val; + + mutex_lock(ð_if->mtx); + val = ioread32(regs + EMAC_CMN_DIGITAL_CTRL2); + + if (phydev->speed == SPEED_10000) + val |= EMAC_CMN_TX_BIT_REPEAT_RATIO; + else + val &= ~EMAC_CMN_TX_BIT_REPEAT_RATIO; + + iowrite32(val, regs + EMAC_CMN_DIGITAL_CTRL2); + mutex_unlock(ð_if->mtx); +} + +void adrv906x_eth_cmn_init(void __iomem *regs, bool switch_enabled, bool macsec_enabled) +{ + unsigned int val1, val2; + + val1 = ioread32(regs + EMAC_CMN_DIGITAL_CTRL0); + val2 = ioread32(regs + EMAC_CMN_DIGITAL_CTRL3); + + val1 |= EMAC_CMN_RX_LINK0_EN + | EMAC_CMN_RX_LINK1_EN + | EMAC_CMN_TX_LINK0_EN + | EMAC_CMN_TX_LINK1_EN; +#if IS_ENABLED(CONFIG_MACSEC) + if (macsec_enabled) + val1 &= ~EMAC_CMN_MACSEC_BYPASS_EN; +#endif + + if (switch_enabled) { + val1 |= EMAC_CMN_SW_PORT0_EN | + EMAC_CMN_SW_PORT1_EN | + EMAC_CMN_SW_PORT2_EN; + val1 &= ~(EMAC_CMN_SW_LINK0_BYPASS_EN | + EMAC_CMN_SW_LINK1_BYPASS_EN); + val2 |= EMAC_CMN_SW_PORT2_DSA_INSERT_EN; + } else { + val1 |= EMAC_CMN_SW_LINK0_BYPASS_EN | + EMAC_CMN_SW_LINK1_BYPASS_EN; + val1 &= ~(EMAC_CMN_SW_PORT0_EN | + EMAC_CMN_SW_PORT1_EN | + EMAC_CMN_SW_PORT2_EN); + val2 &= ~EMAC_CMN_SW_PORT2_DSA_INSERT_EN; + } + + iowrite32(val1, regs + EMAC_CMN_DIGITAL_CTRL0); + iowrite32(val2, regs + EMAC_CMN_DIGITAL_CTRL3); +} + +void adrv906x_cmn_pcs_link_drop_cnt_clear(struct adrv906x_eth_if *adrv906x_eth) +{ + void __iomem *regs; + u32 val; + + regs = adrv906x_eth->emac_cmn_regs; + + mutex_lock(&adrv906x_eth->mtx); + val = ioread32(regs + EMAC_CMN_DIGITAL_CTRL4); + val |= EMAC_CMN_CLEAR_PCS_STATUS_NE_CNT; + iowrite32(val, regs + EMAC_CMN_DIGITAL_CTRL4); + val &= ~EMAC_CMN_CLEAR_PCS_STATUS_NE_CNT; + iowrite32(val, regs + EMAC_CMN_DIGITAL_CTRL4); + mutex_unlock(&adrv906x_eth->mtx); +} + +ssize_t adrv906x_cmn_pcs_link_drop_cnt_get(struct adrv906x_eth_if *adrv906x_eth, char *buf) +{ + void __iomem *regs; + u8 cnt0, cnt1; + unsigned long offset; + u32 val; + + regs = adrv906x_eth->emac_cmn_regs; + + val = ioread32(regs + EMAC_CMN_DIGITAL_CTRL4); + cnt0 = FIELD_GET(EMAC_CMN_PCS_STATUS_NE_CNT_0, val); + cnt1 = FIELD_GET(EMAC_CMN_PCS_STATUS_NE_CNT_1, val); + + offset = sprintf(buf, "port 0 link failures: %d\n", cnt0); + offset += sprintf(buf + offset, "port 1 link failures: %d\n", cnt1); + + return offset; +} + +ssize_t adrv906x_cmn_recovered_clock_output_get(struct device *dev, char *buf) +{ + struct adrv906x_eth_dev *adrv906x_dev; + int enabled, selected, result; + void __iomem *regs; + unsigned int val; + + adrv906x_dev = dev_get_drvdata(dev); + regs = adrv906x_dev->parent->emac_cmn_regs; + val = ioread32(regs + EMAC_CMN_DIGITAL_CTRL0); + + enabled = (adrv906x_dev->port == 0) ? + FIELD_GET(EMAC_CMN_CDR_DIV_PORT0_EN, val) : + FIELD_GET(EMAC_CMN_CDR_DIV_PORT1_EN, val); + selected = (FIELD_GET(EMAC_CMN_CDR_SEL, val) == adrv906x_dev->port) ? 1 : 0; + result = sprintf(buf, "Selected as recovered_clk source: %s\n", + selected && enabled ? "Yes" : "No"); + + return result; +} + +ssize_t adrv906x_cmn_recovered_clock_output_set(struct device *dev, const char *buf, size_t cnt) +{ + struct adrv906x_eth_dev *adrv906x_dev; + void __iomem *regs; + int enable, err; + u32 val; + + adrv906x_dev = dev_get_drvdata(dev); + regs = adrv906x_dev->parent->emac_cmn_regs; + + err = kstrtoint(buf, 10, &enable); + if (err) { + dev_err(dev, "recovered_clock_output: invalid input"); + return err; + } + if (enable < 0 || enable > 1) { + dev_err(dev, "recovered_clock_output: input out of range"); + return -EINVAL; + } + + mutex_lock(&adrv906x_dev->parent->mtx); + val = ioread32(regs + EMAC_CMN_DIGITAL_CTRL0); + if (enable) { + if (adrv906x_dev->port == 0) { + val |= EMAC_CMN_CDR_DIV_PORT0_EN; + val &= ~EMAC_CMN_CDR_SEL; + } else { + val |= EMAC_CMN_CDR_DIV_PORT1_EN; + val |= EMAC_CMN_CDR_SEL; + } + } else { + val &= (adrv906x_dev->port == 0) ? ~EMAC_CMN_CDR_DIV_PORT0_EN : + ~EMAC_CMN_CDR_DIV_PORT1_EN; + } + + iowrite32(val, regs + EMAC_CMN_DIGITAL_CTRL0); + mutex_unlock(&adrv906x_dev->parent->mtx); + + return cnt; +} + +void adrv906x_cmn_set_phy_loopback(struct adrv906x_eth_dev *adrv906x_dev, bool enable) +{ + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + void __iomem *regs; + u32 ctrl0, ctrl2, loopback_bit, enable_bits; + + if (adrv906x_dev->port == 0) { + loopback_bit = EMAC_CMN_LOOPBACK_BYPASS_DESER_0; + enable_bits = EMAC_CMN_RX_LINK0_EN | EMAC_CMN_TX_LINK0_EN; + } else { + loopback_bit = EMAC_CMN_LOOPBACK_BYPASS_DESER_1; + enable_bits = EMAC_CMN_RX_LINK1_EN | EMAC_CMN_TX_LINK1_EN; + } + + regs = adrv906x_dev->parent->emac_cmn_regs; + + mutex_lock(ð_if->mtx); + ctrl0 = ioread32(regs + EMAC_CMN_DIGITAL_CTRL0); + ctrl2 = ioread32(regs + EMAC_CMN_DIGITAL_CTRL2); + + ctrl0 &= ~enable_bits; + iowrite32(ctrl0, regs + EMAC_CMN_DIGITAL_CTRL0); + + if (enable) + ctrl2 |= loopback_bit; + else + ctrl2 &= ~loopback_bit; + + iowrite32(ctrl2, regs + EMAC_CMN_DIGITAL_CTRL2); + + ctrl0 |= enable_bits; + iowrite32(ctrl0, regs + EMAC_CMN_DIGITAL_CTRL0); + mutex_unlock(ð_if->mtx); +} + +void adrv906x_cmn_set_mac_loopback(struct adrv906x_eth_dev *adrv906x_dev, bool enable) +{ + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + void __iomem *regs; + u32 val, loopback_bit; + + regs = adrv906x_dev->parent->emac_cmn_regs; + loopback_bit = adrv906x_dev->port == 0 ? + EMAC_CMN_LOOPBACK_BYPASS_MAC_0 : EMAC_CMN_LOOPBACK_BYPASS_MAC_1; + + mutex_lock(ð_if->mtx); + if (enable) { + val = ioread32(regs + EMAC_CMN_DIGITAL_CTRL2); + val |= loopback_bit; + iowrite32(val, regs + EMAC_CMN_DIGITAL_CTRL2); + } else { + val = ioread32(regs + EMAC_CMN_DIGITAL_CTRL2); + val &= ~loopback_bit; + iowrite32(val, regs + EMAC_CMN_DIGITAL_CTRL2); + } + mutex_unlock(ð_if->mtx); +} diff --git a/drivers/net/ethernet/adi/adrv906x-cmn.h b/drivers/net/ethernet/adi/adrv906x-cmn.h new file mode 100644 index 00000000000000..cdf750bd3d5cbc --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-cmn.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024-2025, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __ADRV906X_CMN_H__ +#define __ADRV906X_CMN_H__ + +#include +#include +#include "adrv906x-net.h" + +void adrv906x_eth_cmn_pll_reset(struct net_device *ndev); +void adrv906x_eth_cmn_ser_tx_sync_trigger(struct net_device *ndev); +void adrv906x_eth_cmn_ser_pwr_down(struct net_device *ndev); +void adrv906x_eth_cmn_ser_pwr_up_and_reset(struct net_device *ndev); +void adrv906x_eth_cmn_deser_pwr_down(struct net_device *ndev); +void adrv906x_eth_cmn_deser_pwr_up_and_reset(struct net_device *ndev); +int adrv906x_eth_cmn_rst_reg(void __iomem *regs); +void adrv906x_eth_cmn_recovered_clk_config(struct adrv906x_eth_dev *adrv906x_dev); +void adrv906x_eth_cmn_mode_cfg(struct net_device *ndev); +void adrv906x_eth_cmn_init(void __iomem *regs, bool switch_enabled, bool macsec_enabled); +void adrv906x_cmn_pcs_link_drop_cnt_clear(struct adrv906x_eth_if *adrv906x_eth); +ssize_t adrv906x_cmn_pcs_link_drop_cnt_get(struct adrv906x_eth_if *adrv906x_eth, char *buf); +ssize_t adrv906x_cmn_recovered_clock_output_set(struct device *dev, const char *buf, size_t cnt); +ssize_t adrv906x_cmn_recovered_clock_output_get(struct device *dev, char *buf); +void adrv906x_cmn_set_mac_loopback(struct adrv906x_eth_dev *adrv906x_dev, bool enable); +void adrv906x_cmn_set_phy_loopback(struct adrv906x_eth_dev *adrv906x_dev, bool enable); + +#endif /* __ADRV906X_CMN_H__ */ diff --git a/drivers/net/ethernet/adi/adrv906x-ethtool.c b/drivers/net/ethernet/adi/adrv906x-ethtool.c new file mode 100644 index 00000000000000..ba02a4c76482d6 --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-ethtool.c @@ -0,0 +1,982 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adrv906x-net.h" +#include "adrv906x-cmn.h" +#include "adrv906x-mac.h" +#include "adrv906x-phy.h" +#include "adrv906x-ndma.h" +#include "adrv906x-ethtool.h" + +#define NDMA_LOOPBACK_TEST_PATTERN 0x12 +#define NDMA_LOOPBACK_TEST_SIZE 1024 + +/* TODO: Ugly global variable, need to be changed */ +#if IS_BUILTIN(CONFIG_PTP_1588_CLOCK_ADRV906X) +/* The adi ptp module will set this variable */ +extern int adrv906x_phc_index; +#endif +extern const struct ethtool_ops adrv906x_ethtool_ops; + +static const char adrv906x_gstrings_stats_names[][ETH_GSTRING_LEN] = { + "mac_rx_drop_events", + "mac_rx_octets", + "mac_rx_pkts", + "mac_rx_broadcast_pkts", + "mac_rx_multicast_pkts", + "mac_rx_unicast_pkts", + "mac_rx_undersize_pkts", + "mac_rx_oversize_pkts", + "mac_rx_pkts_64_octets", + "mac_rx_pkts_65to127_octets", + "mac_rx_pkts_128to255_octets", + "mac_rx_pkts_256to511_octets", + "mac_rx_pkts_512to1023_octets", + "mac_rx_pkts_1024to1518_octets", + "mac_rx_pkts_1519tox_octets", + "mac_rx_overflow", + "mac_rx_crc_error", + "mac_rx_mc_drop", + "mac_rx_fragments", + "mac_rx_jabbers", + "mac_rx_mac_framing_error", + "mac_rx_rs_framing_error", + "mac_tx_drop_events", + "mac_tx_octets", + "mac_tx_pkts", + "mac_tx_broadcast_pkts", + "mac_tx_multicast_pkts", + "mac_tx_unicast_pkts", + "mac_tx_undersize_pkts", + "mac_tx_oversize_pkts", + "mac_tx_pkts_64_octets", + "mac_tx_pkts_65to127_octets", + "mac_tx_pkts_128to255_octets", + "mac_tx_pkts_256to511_octets", + "mac_tx_pkts_512to1023_octets", + "mac_tx_pkts_1024to1518_octets", + "mac_tx_pkts_1519tox_octets", + "mac_tx_underflow", + "mac_tx_padded", + "ndma_rx_frame_error", + "ndma_rx_frame_size_error", + "ndma_rx_frame_dropped_error", + "ndma_rx_frame_dropped_s_plane", + "ndma_rx_frame_dropped_m_plane", + "ndma_rx_seqnumb_mismatch_error", + "ndma_rx_wu_header_error", + "ndma_rx_unknown_error", + "ndma_rx_pending_work_unit", + "ndma_rx_done_work_unit", + "ndma_rx_dma_error", + "ndma_tx_frame_size_error", + "ndma_tx_wu_data_header_error", + "ndma_tx_wu_status_header_error", + "ndma_tx_tstamp_timeout_error", + "ndma_tx_seqnumb_mismatch_error", + "ndma_tx_unknown_error", + "ndma_tx_pending_work_unit", + "ndma_tx_done_work_unit", + "ndma_tx_data_dma_error", + "ndma_tx_status_dma_error", + "switch_port0_pkt_fltr_rx", + "switch_port0_bytes_fltr_rx", + "switch_port0_pkt_buf_ovfl", + "switch_port0_bytes_buf_ovfl", + "switch_port0_pkt_err", + "switch_port0_bytes_err", + "switch_port0_drop_pkt_tx", + "switch_port0_0_pkt_voq_nqn", + "switch_port0_1_pkt_voq_nqn", + "switch_port0_0_bytes_voq_nqn", + "switch_port0_1_bytes_voq_nqn", + "switch_port0_0_pkt_voq_dqn", + "switch_port0_1_pkt_voq_dqn", + "switch_port0_0_bytes_voq_dqn", + "switch_port0_1_bytes_voq_dqn", + "switch_port0_2_bytes_voq_dqn", + "switch_port0_1_pkt_voq_dropn", + "switch_port0_0_bytes_voq_dropn", + "switch_port0_1_bytes_voq_dropn", + "switch_port0_ucast_pkt_rx", + "switch_port0_ucast_bytes_rx", + "switch_port0_ucast_pkt_tx", + "switch_port0_ucast_bytes_tx", + "switch_port0_mcast_pkt_rx", + "switch_port0_mcast_bytes_rx", + "switch_port0_mcast_pkt_tx", + "switch_port0_mcast_bytes_tx", + "switch_port0_bcast_pkt_rx", + "switch_port0_bcast_bytes_rx", + "switch_port0_bcast_pkt_tx", + "switch_port0_bcast_bytes_tx", + "switch_port0_crd_buffer_drop", + "switch_port1_pkt_fltr_rx", + "switch_port1_bytes_fltr_rx", + "switch_port1_pkt_buf_ovfl", + "switch_port1_bytes_buf_ovfl", + "switch_port1_pkt_err", + "switch_port1_bytes_err", + "switch_port1_drop_pkt_tx", + "switch_port1_0_pkt_voq_nqn", + "switch_port1_1_pkt_voq_nqn", + "switch_port1_0_bytes_voq_nqn", + "switch_port1_1_bytes_voq_nqn", + "switch_port1_0_pkt_voq_dqn", + "switch_port1_1_pkt_voq_dqn", + "switch_port1_0_bytes_voq_dqn", + "switch_port1_1_bytes_voq_dqn", + "switch_port1_0_pkt_voq_dropn", + "switch_port1_1_pkt_voq_dropn", + "switch_port1_0_bytes_voq_dropn", + "switch_port1_1_bytes_voq_dropn", + "switch_port1_ucast_pkt_rx", + "switch_port1_ucast_bytes_rx", + "switch_port1_ucast_pkt_tx", + "switch_port1_ucast_bytes_tx", + "switch_port1_mcast_pkt_rx", + "switch_port1_mcast_bytes_rx", + "switch_port1_mcast_pkt_tx", + "switch_port1_mcast_bytes_tx", + "switch_port1_bcast_pkt_rx", + "switch_port1_bcast_bytes_rx", + "switch_port1_bcast_pkt_tx", + "switch_port1_bcast_bytes_tx", + "switch_port1_crd_buffer_drop", + "switch_port2_pkt_fltr_rx", + "switch_port2_bytes_fltr_rx", + "switch_port2_pkt_buf_ovfl", + "switch_port2_bytes_buf_ovfl", + "switch_port2_pkt_err", + "switch_port2_bytes_err", + "switch_port2_drop_pkt_tx", + "switch_port2_0_pkt_voq_nqn", + "switch_port2_1_pkt_voq_nqn", + "switch_port2_0_bytes_voq_nqn", + "switch_port2_1_bytes_voq_nqn", + "switch_port2_0_pkt_voq_dqn", + "switch_port2_1_pkt_voq_dqn", + "switch_port2_0_bytes_voq_dqn", + "switch_port2_1_bytes_voq_dqn", + "switch_port2_0_pkt_voq_dropn", + "switch_port2_1_pkt_voq_dropn", + "switch_port2_0_bytes_voq_dropn", + "switch_port2_1_bytes_voq_dropn", + "switch_port2_ucast_pkt_rx", + "switch_port2_ucast_bytes_rx", + "switch_port2_ucast_pkt_tx", + "switch_port2_ucast_bytes_tx", + "switch_port2_mcast_pkt_rx", + "switch_port2_mcast_bytes_rx", + "switch_port2_mcast_pkt_tx", + "switch_port2_mcast_bytes_tx", + "switch_port2_bcast_pkt_rx", + "switch_port2_bcast_bytes_rx", + "switch_port2_bcast_pkt_tx", + "switch_port2_bcast_bytes_tx", + "switch_port2_crd_buffer_drop", +}; + +static const char adrv906x_gstrings_selftest_names[][ETH_GSTRING_LEN] = { + "Near-end loopback: ", + "NDMA loopback: ", + "Far-end loopback on/off: ", +}; + +#define ADRV906X_NUM_STATS ARRAY_SIZE(adrv906x_gstrings_stats_names) +#define ADRV906X_NUM_SELFTEST ARRAY_SIZE(adrv906x_gstrings_selftest_names) + +struct adrv906x_test { + char name[ETH_GSTRING_LEN]; + int (*fn)(struct net_device *ndev); + unsigned char etest_flag; +}; + +struct payload_hdr { + __be32 version; + __be64 magic; + u8 id; +} __packed; + +struct adrv906x_packet_attrs { + const unsigned char *src; + const unsigned char *dst; + u32 ip_src; + u32 ip_dst; + int sport; + int dport; + u32 exp_hash; + int dont_wait; + int timeout; + int size; + int max_size; + u8 id; + u16 queue_mapping; + u64 timestamp; +}; + +struct adrv906x_test_priv { + union { + struct adrv906x_packet_attrs *packet; + struct net_device *ndev; + }; + struct packet_type pt; + struct completion completion; + int ok; +}; + +#define ADRV906X_TEST_PKT_SIZE (sizeof(struct ethhdr) + sizeof(struct iphdr) + \ + sizeof(struct udphdr) + sizeof(struct payload_hdr)) +#define ADRV906X_TEST_PKT_MAGIC 0xdeadcafecafedeadULL +#define ADRV906X_LB_TIMEOUT msecs_to_jiffies(200) + +static u8 adrv906x_packet_next_id; + +static int adrv906x_ethtool_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *cmd) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); + u8 autoneg = cmd->base.autoneg; + u8 duplex = cmd->base.duplex; + u32 speed = cmd->base.speed; + struct phy_device *phydev = ndev->phydev; + + if (!phydev) + return -ENODEV; + + if (cmd->base.phy_address != phydev->mdio.addr) + return -EINVAL; + + linkmode_copy(advertising, cmd->link_modes.advertising); + + /* We make sure that we don't pass unsupported values in to the PHY */ + linkmode_and(advertising, advertising, phydev->supported); + + /* Verify the settings we care about. */ + if (autoneg == AUTONEG_ENABLE) + return -EINVAL; + + if ((speed != SPEED_10000 && speed != SPEED_25000) || duplex != DUPLEX_FULL) + return -EINVAL; + + mutex_lock(&phydev->lock); + phydev->autoneg = autoneg; + phydev->speed = speed; + phydev->duplex = duplex; + + linkmode_copy(phydev->advertising, advertising); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + phydev->advertising, autoneg == AUTONEG_ENABLE); + + if (phy_is_started(phydev)) { + phydev->state = PHY_UP; + phy_start_machine(phydev); + } + mutex_unlock(&phydev->lock); + + return 0; +} + +static int adrv906x_ethtool_get_ts_info(struct net_device *ndev, struct kernel_ethtool_ts_info *info) +{ + info->so_timestamping = + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + info->tx_types = + (1 << HWTSTAMP_TX_OFF) | + (1 << HWTSTAMP_TX_ON); + info->rx_filters = + (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) | + (1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) | + (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_L2_SYNC) | + (1 << HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) | + (1 << HWTSTAMP_FILTER_PTP_V2_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_SYNC) | + (1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) | + (1 << HWTSTAMP_FILTER_ALL); + +#if IS_BUILTIN(CONFIG_PTP_1588_CLOCK_ADRV906X) + info->phc_index = adrv906x_phc_index; +#else + info->phc_index = -1; +#endif + return 0; +} + +static int adrv906x_ethtool_get_sset_count(struct net_device *ndev, int sset) +{ + if (sset == ETH_SS_STATS) + return ADRV906X_NUM_STATS; + + if (sset == ETH_SS_TEST) + return ADRV906X_NUM_SELFTEST; + + return -EOPNOTSUPP; +} + +static void adrv906x_ethtool_get_strings(struct net_device *ndev, u32 sset, u8 *buf) +{ + if (sset == ETH_SS_STATS) + memcpy(buf, &adrv906x_gstrings_stats_names, + sizeof(adrv906x_gstrings_stats_names)); + + if (sset == ETH_SS_TEST) + memcpy(buf, &adrv906x_gstrings_selftest_names, + sizeof(adrv906x_gstrings_selftest_names)); +} + +static void adrv906x_ethtool_get_stats(struct net_device *ndev, struct ethtool_stats *stats, + u64 *data) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + struct adrv906x_eth_switch *es = ð_if->ethswitch; + union adrv906x_ndma_chan_stats *ndma_rx_stats = &adrv906x_dev->ndma_dev->rx_chan.stats; + union adrv906x_ndma_chan_stats *ndma_tx_stats = &adrv906x_dev->ndma_dev->tx_chan.stats; + struct adrv906x_mac_rx_stats *mac_rx_stats = &adrv906x_dev->mac.hw_stats_rx; + struct adrv906x_mac_tx_stats *mac_tx_stats = &adrv906x_dev->mac.hw_stats_tx; + int i; + + adrv906x_ndma_update_frame_drop_stats(adrv906x_dev->ndma_dev); + + data[0] = mac_rx_stats->general_stats.drop_events; + data[1] = mac_rx_stats->general_stats.octets; + data[2] = mac_rx_stats->general_stats.pkts; + data[3] = mac_rx_stats->general_stats.broadcast_pkts; + data[4] = mac_rx_stats->general_stats.multicast_pkts; + data[5] = mac_rx_stats->general_stats.unicast_pkts; + data[6] = mac_rx_stats->general_stats.undersize_pkts; + data[7] = mac_rx_stats->general_stats.oversize_pkts; + data[8] = mac_rx_stats->general_stats.pkts_64_octets; + data[9] = mac_rx_stats->general_stats.pkts_65to127_octets; + data[10] = mac_rx_stats->general_stats.pkts_128to255_octets; + data[11] = mac_rx_stats->general_stats.pkts_256to511_octets; + data[12] = mac_rx_stats->general_stats.pkts_512to1023_octets; + data[13] = mac_rx_stats->general_stats.pkts_1024to1518_octets; + data[14] = mac_rx_stats->general_stats.pkts_1519tox_octets; + data[15] = mac_rx_stats->overflow; + data[16] = mac_rx_stats->crc_errors; + data[17] = mac_rx_stats->mc_drop; + data[18] = mac_rx_stats->fragments; + data[19] = mac_rx_stats->jabbers; + data[20] = mac_rx_stats->mac_framing_error; + data[21] = mac_rx_stats->rs_framing_error; + data[22] = mac_tx_stats->general_stats.drop_events; + data[23] = mac_tx_stats->general_stats.octets; + data[24] = mac_tx_stats->general_stats.pkts; + data[25] = mac_tx_stats->general_stats.broadcast_pkts; + data[26] = mac_tx_stats->general_stats.multicast_pkts; + data[27] = mac_tx_stats->general_stats.unicast_pkts; + data[28] = mac_tx_stats->general_stats.undersize_pkts; + data[29] = mac_tx_stats->general_stats.oversize_pkts; + data[30] = mac_tx_stats->general_stats.pkts_64_octets; + data[31] = mac_tx_stats->general_stats.pkts_65to127_octets; + data[32] = mac_tx_stats->general_stats.pkts_128to255_octets; + data[33] = mac_tx_stats->general_stats.pkts_256to511_octets; + data[34] = mac_tx_stats->general_stats.pkts_512to1023_octets; + data[35] = mac_tx_stats->general_stats.pkts_1024to1518_octets; + data[36] = mac_tx_stats->general_stats.pkts_1519tox_octets; + data[37] = mac_tx_stats->underflow; + data[38] = mac_tx_stats->padded; + data[39] = ndma_rx_stats->rx.frame_errors; + data[40] = ndma_rx_stats->rx.frame_size_errors; + data[41] = ndma_rx_stats->rx.frame_dropped_errors; + data[42] = ndma_rx_stats->rx.frame_dropped_splane_errors; + data[43] = ndma_rx_stats->rx.frame_dropped_mplane_errors; + data[44] = ndma_rx_stats->rx.seqnumb_mismatch_errors; + data[45] = ndma_rx_stats->rx.wu_header_errors; + data[46] = ndma_rx_stats->rx.unknown_errors; + data[47] = ndma_rx_stats->rx.pending_work_units; + data[48] = ndma_rx_stats->rx.done_work_units; + data[49] = ndma_rx_stats->rx.dma_errors; + data[50] = ndma_tx_stats->tx.frame_size_errors; + data[51] = ndma_tx_stats->tx.wu_data_header_errors; + data[52] = ndma_tx_stats->tx.wu_status_header_errors; + data[53] = ndma_tx_stats->tx.tstamp_timeout_errors; + data[54] = ndma_tx_stats->tx.seqnumb_mismatch_errors; + data[55] = ndma_tx_stats->tx.unknown_errors; + data[56] = ndma_tx_stats->tx.pending_work_units; + data[57] = ndma_tx_stats->tx.done_work_units; + data[58] = ndma_tx_stats->tx.data_dma_errors; + data[59] = ndma_tx_stats->tx.status_dma_errors; + + for (i = 0; i < SWITCH_MAX_PORT_NUM; i++) { + data[i * SWITCH_PORT_STATS_NUM + 60] = es->port_stats[i].pkt_fltr_rx; + data[i * SWITCH_PORT_STATS_NUM + 61] = es->port_stats[i].bytes_fltr_rx; + data[i * SWITCH_PORT_STATS_NUM + 62] = es->port_stats[i].pkt_buf_ovfl; + data[i * SWITCH_PORT_STATS_NUM + 63] = es->port_stats[i].bytes_buf_ovfl; + data[i * SWITCH_PORT_STATS_NUM + 64] = es->port_stats[i].pkt_err; + data[i * SWITCH_PORT_STATS_NUM + 65] = es->port_stats[i].bytes_err; + data[i * SWITCH_PORT_STATS_NUM + 66] = es->port_stats[i].drop_pkt_tx; + data[i * SWITCH_PORT_STATS_NUM + 67] = es->port_stats[i].ipv0_pkt_voq_nqn; + data[i * SWITCH_PORT_STATS_NUM + 68] = es->port_stats[i].ipv1_pkt_voq_nqn; + data[i * SWITCH_PORT_STATS_NUM + 69] = es->port_stats[i].ipv0_bytes_voq_nqn; + data[i * SWITCH_PORT_STATS_NUM + 70] = es->port_stats[i].ipv1_bytes_voq_nqn; + data[i * SWITCH_PORT_STATS_NUM + 71] = es->port_stats[i].ipv0_pkt_voq_dqn; + data[i * SWITCH_PORT_STATS_NUM + 72] = es->port_stats[i].ipv1_pkt_voq_dqn; + data[i * SWITCH_PORT_STATS_NUM + 73] = es->port_stats[i].ipv0_bytes_voq_dqn; + data[i * SWITCH_PORT_STATS_NUM + 74] = es->port_stats[i].ipv1_bytes_voq_dqn; + data[i * SWITCH_PORT_STATS_NUM + 75] = es->port_stats[i].ipv0_pkt_voq_dropn; + data[i * SWITCH_PORT_STATS_NUM + 76] = es->port_stats[i].ipv1_pkt_voq_dropn; + data[i * SWITCH_PORT_STATS_NUM + 77] = es->port_stats[i].ipv0_bytes_voq_dropn; + data[i * SWITCH_PORT_STATS_NUM + 78] = es->port_stats[i].ipv1_bytes_voq_dropn; + data[i * SWITCH_PORT_STATS_NUM + 79] = es->port_stats[i].ucast_pkt_rx; + data[i * SWITCH_PORT_STATS_NUM + 80] = es->port_stats[i].ucast_bytes_rx; + data[i * SWITCH_PORT_STATS_NUM + 81] = es->port_stats[i].ucast_pkt_tx; + data[i * SWITCH_PORT_STATS_NUM + 82] = es->port_stats[i].ucast_bytes_tx; + data[i * SWITCH_PORT_STATS_NUM + 83] = es->port_stats[i].mcast_pkt_rx; + data[i * SWITCH_PORT_STATS_NUM + 84] = es->port_stats[i].mcast_bytes_rx; + data[i * SWITCH_PORT_STATS_NUM + 85] = es->port_stats[i].mcast_pkt_tx; + data[i * SWITCH_PORT_STATS_NUM + 86] = es->port_stats[i].mcast_bytes_tx; + data[i * SWITCH_PORT_STATS_NUM + 87] = es->port_stats[i].bcast_pkt_rx; + data[i * SWITCH_PORT_STATS_NUM + 88] = es->port_stats[i].bcast_bytes_rx; + data[i * SWITCH_PORT_STATS_NUM + 89] = es->port_stats[i].bcast_pkt_tx; + data[i * SWITCH_PORT_STATS_NUM + 90] = es->port_stats[i].bcast_bytes_tx; + data[i * SWITCH_PORT_STATS_NUM + 91] = es->port_stats[i].crd_buffer_drop; + } +} + +static const struct ethtool_rmon_hist_range adrv906x_ethtool_rmon_ranges[] = { + { 64, 64 }, + { 65, 127 }, + { 128, 255 }, + { 256, 511 }, + { 512, 1023 }, + { 1024, 1518 }, + { 1519, NDMA_MAX_FRAME_SIZE_VALUE }, + {}, +}; + +static void adrv906x_ethtool_get_rmon_stats(struct net_device *ndev, + struct ethtool_rmon_stats *stats, + const struct ethtool_rmon_hist_range **ranges) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_mac_rx_stats *mac_rx_stats = &adrv906x_dev->mac.hw_stats_rx; + struct adrv906x_mac_tx_stats *mac_tx_stats = &adrv906x_dev->mac.hw_stats_tx; + + *ranges = adrv906x_ethtool_rmon_ranges; + + adrv906x_ndma_update_frame_drop_stats(adrv906x_dev->ndma_dev); + + stats->undersize_pkts = mac_rx_stats->general_stats.undersize_pkts; + stats->oversize_pkts = mac_rx_stats->general_stats.oversize_pkts; + stats->fragments = mac_rx_stats->fragments; + stats->jabbers = mac_rx_stats->jabbers; + stats->hist[0] = mac_rx_stats->general_stats.pkts_64_octets; + stats->hist[1] = mac_rx_stats->general_stats.pkts_65to127_octets; + stats->hist[2] = mac_rx_stats->general_stats.pkts_128to255_octets; + stats->hist[3] = mac_rx_stats->general_stats.pkts_256to511_octets; + stats->hist[4] = mac_rx_stats->general_stats.pkts_512to1023_octets; + stats->hist[5] = mac_rx_stats->general_stats.pkts_1024to1518_octets; + stats->hist[6] = mac_rx_stats->general_stats.pkts_1519tox_octets; + stats->hist_tx[0] = mac_tx_stats->general_stats.pkts_64_octets; + stats->hist_tx[1] = mac_tx_stats->general_stats.pkts_65to127_octets; + stats->hist_tx[2] = mac_tx_stats->general_stats.pkts_128to255_octets; + stats->hist_tx[3] = mac_tx_stats->general_stats.pkts_256to511_octets; + stats->hist_tx[4] = mac_tx_stats->general_stats.pkts_512to1023_octets; + stats->hist_tx[5] = mac_tx_stats->general_stats.pkts_1024to1518_octets; + stats->hist_tx[6] = mac_tx_stats->general_stats.pkts_1519tox_octets; +} + +static int adrv906x_ethtool_get_fecparam(struct net_device *ndev, + struct ethtool_fecparam *fecparam) +{ + struct phy_device *phydev = ndev->phydev; + + fecparam->fec = ETHTOOL_FEC_RS; + + mutex_lock(&phydev->lock); + if (phydev->speed == SPEED_25000 && phydev->dev_flags & ADRV906X_PHY_FLAGS_PCS_RS_FEC_EN) + fecparam->active_fec = ETHTOOL_FEC_RS; + else + fecparam->active_fec = ETHTOOL_FEC_OFF; + mutex_unlock(&phydev->lock); + + return 0; +} + +static int adrv906x_ethtool_set_fecparam(struct net_device *ndev, + struct ethtool_fecparam *fecparam) +{ + struct phy_device *phydev = ndev->phydev; + bool fec_cur_en, fec_new_en; + + if (!(fecparam->fec & ETHTOOL_FEC_OFF) && !(fecparam->fec & ETHTOOL_FEC_RS)) + return -EOPNOTSUPP; + + mutex_lock(&phydev->lock); + fec_cur_en = !!(phydev->dev_flags & ADRV906X_PHY_FLAGS_PCS_RS_FEC_EN); + fec_new_en = !!(fecparam->fec & ETHTOOL_FEC_RS); + + if (fec_cur_en != fec_new_en) { + phydev->dev_flags ^= ADRV906X_PHY_FLAGS_PCS_RS_FEC_EN; + + if (phy_is_started(phydev)) { + phydev->state = PHY_UP; + phy_start_machine(phydev); + } + } + mutex_unlock(&phydev->lock); + + return 0; +} + +static struct sk_buff *adrv906x_test_get_udp_skb(struct net_device *ndev, + struct adrv906x_packet_attrs *attr) +{ + struct sk_buff *skb = NULL; + struct udphdr *uhdr = NULL; + struct payload_hdr *phdr; + struct ethhdr *ehdr; + struct iphdr *ihdr; + int iplen, size; + + size = ADRV906X_TEST_PKT_SIZE; + + skb = netdev_alloc_skb(ndev, size); + if (!skb) + return NULL; + + prefetchw(skb->data); + + ehdr = skb_push(skb, ETH_HLEN); + + skb_reset_mac_header(skb); + + skb_set_network_header(skb, skb->len); + ihdr = skb_put(skb, sizeof(*ihdr)); + + skb_set_transport_header(skb, skb->len); + uhdr = skb_put(skb, sizeof(*uhdr)); + + eth_zero_addr(ehdr->h_dest); + if (attr->src) + ether_addr_copy(ehdr->h_source, attr->src); + if (attr->dst) + ether_addr_copy(ehdr->h_dest, attr->dst); + + ehdr->h_proto = htons(ETH_P_IP); + + uhdr->len = htons(sizeof(*phdr) + sizeof(*uhdr) + attr->size); + uhdr->check = 0; + + ihdr->ihl = 5; + ihdr->ttl = 32; + ihdr->version = 4; + ihdr->protocol = IPPROTO_UDP; + iplen = sizeof(*ihdr) + sizeof(*phdr) + attr->size; + iplen += sizeof(*uhdr); + + ihdr->tot_len = htons(iplen); + ihdr->frag_off = 0; + ihdr->saddr = htonl(attr->ip_src); + ihdr->daddr = htonl(attr->ip_dst); + ihdr->tos = 0; + ihdr->id = 0; + ip_send_check(ihdr); + + phdr = skb_put(skb, sizeof(*phdr)); + phdr->version = 0; + phdr->magic = cpu_to_be64(ADRV906X_TEST_PKT_MAGIC); + attr->id = adrv906x_packet_next_id; + phdr->id = adrv906x_packet_next_id++; + + skb->csum = 0; + skb->ip_summed = CHECKSUM_PARTIAL; + udp4_hwcsum(skb, ihdr->saddr, ihdr->daddr); + + skb->protocol = htons(ETH_P_IP); + skb->pkt_type = PACKET_HOST; + skb->dev = ndev; + + return skb; +} + +static int adrv906x_test_loopback_validate(struct sk_buff *skb, struct net_device *ndev, + struct packet_type *pt, struct net_device *orig_ndev) +{ + struct adrv906x_test_priv *tpriv = pt->af_packet_priv; + const unsigned char *src = tpriv->packet->src; + const unsigned char *dst = tpriv->packet->dst; + + struct payload_hdr *phdr; + struct ethhdr *ehdr; + struct udphdr *uhdr; + struct iphdr *ihdr; + + skb = skb_unshare(skb, GFP_ATOMIC); + if (!skb) + goto out; + + if (skb_linearize(skb)) + goto out; + if (skb_headlen(skb) < (ADRV906X_TEST_PKT_SIZE - ETH_HLEN)) + goto out; + + ehdr = (struct ethhdr *)skb_mac_header(skb); + + if (dst) + if (!ether_addr_equal_unaligned(ehdr->h_dest, dst)) + goto out; + + if (src) + if (!ether_addr_equal_unaligned(ehdr->h_source, src)) + goto out; + + ihdr = ip_hdr(skb); + + if (ihdr->protocol != IPPROTO_UDP) + goto out; + + uhdr = (struct udphdr *)((u8 *)ihdr + 4 * ihdr->ihl); + + phdr = (struct payload_hdr *)((u8 *)uhdr + sizeof(*uhdr)); + + if (phdr->magic != cpu_to_be64(ADRV906X_TEST_PKT_MAGIC)) + goto out; + if (tpriv->packet->exp_hash && !skb->hash) + goto out; + if (tpriv->packet->id != phdr->id) + goto out; + + tpriv->ok = true; + complete(&tpriv->completion); +out: + kfree_skb(skb); + return 0; +} + +static int adrv906x_test_near_end_loopback_run(struct net_device *ndev, + struct adrv906x_packet_attrs *attr) +{ + struct adrv906x_test_priv *tpriv; + struct sk_buff *skb = NULL; + int ret; + + netdev_printk(KERN_DEBUG, ndev, "adrv906x_test_near_end_loopback_run"); + tpriv = kzalloc(sizeof(*tpriv), GFP_KERNEL); + if (!tpriv) + return -ENOMEM; + + tpriv->ok = false; + init_completion(&tpriv->completion); + + tpriv->pt.type = htons(ETH_P_IP); + tpriv->pt.func = adrv906x_test_loopback_validate; + tpriv->pt.dev = ndev; + tpriv->pt.af_packet_priv = tpriv; + tpriv->packet = attr; + + dev_add_pack(&tpriv->pt); + skb = adrv906x_test_get_udp_skb(ndev, attr); + if (!skb) { + ret = -ENOMEM; + goto cleanup; + } + + ret = __netdev_start_xmit(ndev->netdev_ops, skb, ndev, false); + if (ret) + goto cleanup; + + if (unlikely(!attr->timeout)) + attr->timeout = ADRV906X_LB_TIMEOUT; + + wait_for_completion_timeout(&tpriv->completion, attr->timeout); + + ret = tpriv->ok ? 0 : -ETIMEDOUT; + +cleanup: + dev_remove_pack(&tpriv->pt); + kfree(tpriv); + + return ret; +} + +static int adrv906x_test_set_phy_loopback(struct net_device *ndev, bool enable) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct phy_device *phydev = ndev->phydev; + + /* Start/stop update of PHY status in PAL */ + phy_loopback(phydev, enable); + + if (enable) { + mutex_lock(&phydev->lock); + phydev->dev_flags |= ADRV906X_PHY_FLAGS_LOOPBACK_TEST; + mutex_unlock(&phydev->lock); + } else { + mutex_lock(&phydev->lock); + phydev->dev_flags &= ~ADRV906X_PHY_FLAGS_LOOPBACK_TEST; + mutex_unlock(&phydev->lock); + } + + adrv906x_cmn_set_phy_loopback(adrv906x_dev, enable); + + /* Give PHY time to establish link */ + msleep(2000); + + return 0; +} + +static int adrv906x_test_near_end_loopback_test(struct net_device *ndev) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + struct adrv906x_eth_switch *es = ð_if->ethswitch; + struct adrv906x_mac *mac = &adrv906x_dev->mac; + struct phy_device *phydev = ndev->phydev; + struct adrv906x_packet_attrs attr = { }; + int dev_state = netif_running(ndev); + int ret; + + netdev_printk(KERN_DEBUG, ndev, "enter %s", __func__); + + if (es->enabled) + adrv906x_switch_reset_soft(es); + adrv906x_mac_set_path(mac, true); + + if (dev_state) { + netdev_printk(KERN_DEBUG, ndev, "stopping device in network stack"); + netif_tx_stop_all_queues(ndev); + netif_carrier_off(ndev); + } + adrv906x_test_set_phy_loopback(ndev, true); + + phy_resume(phydev); + if (es->enabled) + adrv906x_switch_port_enable(es, adrv906x_dev->port, true); + + attr.dst = ndev->dev_addr; + ret = adrv906x_test_near_end_loopback_run(ndev, &attr); + netdev_printk(KERN_DEBUG, ndev, "test result: %d", ret); + if (ret) + goto out; + +out: + adrv906x_test_set_phy_loopback(ndev, false); + if (dev_state) { + netdev_printk(KERN_DEBUG, ndev, "restarting device in network stack"); + netif_tx_start_all_queues(ndev); + netif_carrier_on(ndev); + } + + msleep(2000); + adrv906x_mac_set_path(mac, false); + + phy_suspend(phydev); + if (es->enabled) { + adrv906x_switch_port_enable(es, adrv906x_dev->port, false); + adrv906x_switch_reset_soft(es); + } + + netdev_printk(KERN_DEBUG, ndev, "%s done", __func__); + + return ret; +} + +static int adrv906x_test_far_end_loopback_test(struct net_device *ndev) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + static bool loopback_state[2]; + u32 index; + + netdev_printk(KERN_DEBUG, ndev, "adrv906x_test_far_end_loopback_test"); + + index = adrv906x_dev->port; + if (index > 1) { + netdev_err(ndev, "port index %d is out of range", index); + return -EFAULT; + } + + if (loopback_state[index]) { + adrv906x_cmn_set_mac_loopback(adrv906x_dev, false); + loopback_state[index] = 0; + netif_start_queue(ndev); + netdev_info(ndev, "Turn off MAC loopback"); + } else { + netif_stop_queue(ndev); + adrv906x_cmn_set_mac_loopback(adrv906x_dev, true); + loopback_state[index] = 1; + netdev_info(ndev, "Turn on MAC loopback"); + } + + return 0; +} + +static void adrv906x_ndma_loopback_tx_callback(struct sk_buff *skb, unsigned int port_id, + struct timespec64 ts, void *cb_param) +{ + dev_kfree_skb(skb); +} + +static void adrv906x_ndma_loopback_rx_callback(struct sk_buff *skb, unsigned int port_id, + struct timespec64 ts, void *cb_param) +{ + struct adrv906x_test_priv *tpriv = (struct adrv906x_test_priv *)cb_param; + struct net_device *ndev = tpriv->ndev; + unsigned char *p; + int i; + + p = skb->data; + for (i = 0; i < NDMA_LOOPBACK_TEST_SIZE; i++) { + if (*p != NDMA_LOOPBACK_TEST_PATTERN) { + netdev_printk(KERN_DEBUG, ndev, "rx:0x%x tx:0x%x", *p, i); + tpriv->ok = false; + break; + } + p++; + } + dev_kfree_skb(skb); + complete(&tpriv->completion); +} + +static int adrv906x_ndma_loopback_test(struct net_device *ndev) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_ndma_dev *ndma_dev = adrv906x_dev->ndma_dev; + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + struct adrv906x_eth_dev *temp_eth_dev; + struct adrv906x_test_priv *tpriv; + int port = adrv906x_dev->port; + struct net_device *temp_ndev; + struct sk_buff *tx_data1; + bool all_down = false; + int i, ret = 0, tmo; + unsigned char *p; + + tpriv = kzalloc(sizeof(*tpriv), GFP_KERNEL); + if (!tpriv) + return -ENOMEM; + tpriv->ok = true; + init_completion(&tpriv->completion); + tpriv->ndev = ndev; + + if (eth_if->ethswitch.enabled) { + if (kref_read(&ndma_dev->refcount) > 1) { + netdev_printk(KERN_DEBUG, ndev, "switch enabled, shut down both interfaces"); + all_down = true; + for (i = 0; i < MAX_NETDEV_NUM; i++) { + temp_eth_dev = eth_if->adrv906x_dev[i]; + temp_ndev = temp_eth_dev->ndev; + ndev->netdev_ops->ndo_stop(temp_ndev); + } + } + } else { + ndev->netdev_ops->ndo_stop(ndev); + } + adrv906x_ndma_open(ndma_dev, + adrv906x_ndma_loopback_tx_callback, + adrv906x_ndma_loopback_rx_callback, + tpriv, true); + tx_data1 = netdev_alloc_skb(ndev, NDMA_LOOPBACK_TEST_SIZE); + skb_put(tx_data1, NDMA_LOOPBACK_TEST_SIZE); + p = tx_data1->data; + for (i = 0; i < NDMA_LOOPBACK_TEST_SIZE; i++) { + *p = NDMA_LOOPBACK_TEST_PATTERN; + p++; + } + ret = adrv906x_ndma_start_xmit(ndma_dev, tx_data1, port, 0, 0); + if (ret) { + netdev_printk(KERN_DEBUG, ndev, "ndma tx failed to send frame:0x%x", ret); + ret = -EIO; + dev_kfree_skb(tx_data1); + goto out; + } + tmo = wait_for_completion_timeout(&tpriv->completion, ADRV906X_LB_TIMEOUT); + if (!tmo) { + netdev_printk(KERN_DEBUG, ndev, "ndma loopback test timeout"); + ret = -ETIMEDOUT; + } + if (!tpriv->ok) { + netdev_printk(KERN_DEBUG, ndev, "ndma loopback test failed"); + ret = -EINVAL; + } +out: + adrv906x_ndma_close(ndma_dev); + if (all_down) { + for (i = 0; i < MAX_NETDEV_NUM; i++) { + temp_eth_dev = eth_if->adrv906x_dev[i]; + temp_ndev = temp_eth_dev->ndev; + ndev->netdev_ops->ndo_open(temp_ndev); + } + } else { + ndev->netdev_ops->ndo_open(ndev); + } + kfree(tpriv); + return ret; +} + +struct adrv906x_test adrv906x_ethtool_selftests[] = { + { + .name = "Near-end loopback", + .fn = adrv906x_test_near_end_loopback_test, + .etest_flag = ETH_TEST_FL_OFFLINE, + }, + { + .name = "NDMA loopback", + .fn = adrv906x_ndma_loopback_test, + .etest_flag = ETH_TEST_FL_OFFLINE, + }, + { + .name = "Far-end loopback", + .fn = adrv906x_test_far_end_loopback_test, + .etest_flag = 0, + }, +}; + +static void adrv906x_ethtool_selftest_run(struct net_device *ndev, struct ethtool_test *etest, u64 *buf) +{ + unsigned char etest_flags = etest->flags; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(adrv906x_ethtool_selftests); i++) { + ret = 1; + if (etest_flags == adrv906x_ethtool_selftests[i].etest_flag) { + ret = adrv906x_ethtool_selftests[i].fn(ndev); + if (ret) + etest->flags |= ETH_TEST_FL_FAILED; + } + buf[i] = ret; + } +} + +const struct ethtool_ops adrv906x_ethtool_ops = { + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = adrv906x_ethtool_set_link_ksettings, + .get_fecparam = adrv906x_ethtool_get_fecparam, + .set_fecparam = adrv906x_ethtool_set_fecparam, + .get_ts_info = adrv906x_ethtool_get_ts_info, + .get_sset_count = adrv906x_ethtool_get_sset_count, + .get_strings = adrv906x_ethtool_get_strings, + .get_ethtool_stats = adrv906x_ethtool_get_stats, + .get_rmon_stats = adrv906x_ethtool_get_rmon_stats, + .self_test = adrv906x_ethtool_selftest_run, +}; + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/adi/adrv906x-ethtool.h b/drivers/net/ethernet/adi/adrv906x-ethtool.h new file mode 100644 index 00000000000000..a9da5c2d87f721 --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-ethtool.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __ADRV906X_ETHTOOL_H__ +#define __ADRV906X_ETHTOOL_H__ + +#include +#include +#include +#include +#include +#include + +extern const struct ethtool_ops adrv906x_ethtool_ops; + +int adrv906x_eth_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *cmd); +int adrv906x_eth_get_ts_info(struct net_device *ndev, struct ethtool_ts_info *info); +int adrv906x_eth_get_sset_count(struct net_device *netdev, int sset); +void adrv906x_eth_get_strings(struct net_device *netdev, u32 sset, u8 *buf); +void adrv906x_eth_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data); +void adrv906x_eth_selftest_run(struct net_device *ndev, struct ethtool_test *etest, u64 *buf); + +#endif /* __ADRV906X_ETHTOOL_H__ */ diff --git a/drivers/net/ethernet/adi/adrv906x-mac.c b/drivers/net/ethernet/adi/adrv906x-mac.c new file mode 100644 index 00000000000000..6e93cc51338f7a --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-mac.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include "adrv906x-mac.h" + +void adrv906x_mac_promiscuous_mode_en(struct adrv906x_mac *mac) +{ + void __iomem *emac_rx = mac->emac_rx; + unsigned int val; + + val = ioread32(emac_rx + MAC_RX_CTRL); + val |= MAC_RX_PROMISCUOUS_MODE_EN; + iowrite32(val, emac_rx + MAC_RX_CTRL); +} + +void adrv906x_mac_promiscuous_mode_dis(struct adrv906x_mac *mac) +{ + void __iomem *emac_rx = mac->emac_rx; + unsigned int val; + + val = ioread32(emac_rx + MAC_RX_CTRL); + val &= ~MAC_RX_PROMISCUOUS_MODE_EN; + iowrite32(val, emac_rx + MAC_RX_CTRL); +} + +static void adrv906x_mac_tx_path_en(struct adrv906x_mac *mac) +{ + void __iomem *emac_tx = mac->emac_tx; + unsigned int val; + + val = ioread32(emac_tx + MAC_TX_CTRL); + val |= MAC_TX_PATH_EN; + iowrite32(val, emac_tx + MAC_TX_CTRL); +} + +static void adrv906x_mac_tx_path_dis(struct adrv906x_mac *mac) +{ + void __iomem *emac_tx = mac->emac_tx; + unsigned int val; + + val = ioread32(emac_tx + MAC_TX_CTRL); + val &= ~MAC_TX_PATH_EN; + iowrite32(val, emac_tx + MAC_TX_CTRL); +} + +void adrv906x_mac_rx_path_en(struct adrv906x_mac *mac) +{ + void __iomem *emac_rx = mac->emac_rx; + unsigned int val; + + val = ioread32(emac_rx + MAC_RX_CTRL); + val |= MAC_RX_PATH_EN; + iowrite32(val, emac_rx + MAC_RX_CTRL); +} + +void adrv906x_mac_rx_path_dis(struct adrv906x_mac *mac) +{ + void __iomem *emac_rx = mac->emac_rx; + unsigned int val; + + val = ioread32(emac_rx + MAC_RX_CTRL); + val &= ~MAC_RX_PATH_EN; + iowrite32(val, emac_rx + MAC_RX_CTRL); +} + +void adrv906x_mac_set_path(struct adrv906x_mac *mac, bool enable) +{ + if (enable) { + adrv906x_mac_rx_path_en(mac); + adrv906x_mac_tx_path_en(mac); + } else { + adrv906x_mac_tx_path_dis(mac); + adrv906x_mac_rx_path_dis(mac); + } +} + +void adrv906x_mac_set_multicast_filter(struct adrv906x_mac *mac, u64 mac_addr, int filter_id) +{ + void __iomem *emac_rx = mac->emac_rx; + unsigned int low, high, val; + + low = FIELD_GET(0x0000FFFFFFFF, mac_addr); + high = FIELD_GET(0xFFFF00000000, mac_addr); + + iowrite32(low, emac_rx + CFG_MULT_ADDR0_LOW + filter_id * 4); + iowrite32(high, emac_rx + CFG_MULT_ADDR0_HIGH + filter_id * 4); + val = ioread32(emac_rx + MAC_RX_CTRL); + val |= MAC_RX_PERMITTABLE_ADDR0_EN << filter_id; + iowrite32(val, emac_rx + MAC_RX_CTRL); +} + +static void adrv906x_mac_set_mfs(struct adrv906x_mac *mac, unsigned int mfs) +{ + unsigned int val_tx, val_rx; + + val_tx = ioread32(mac->emac_tx + MAC_TX_CTRL); + val_rx = ioread32(mac->emac_rx + MAC_RX_CTRL); + + val_tx &= ~MAC_TX_MFS; + val_tx |= FIELD_PREP(MAC_TX_MFS, mfs); + val_rx &= ~MAC_RX_MFS; + val_rx |= FIELD_PREP(MAC_RX_MFS, mfs); + + iowrite32(val_tx, mac->emac_tx + MAC_TX_CTRL); + iowrite32(val_rx, mac->emac_rx + MAC_RX_CTRL); +} + +static void adrv906x_mac_update_general_stats(void __iomem *base, + struct adrv906x_mac_general_stats *gs) +{ + unsigned int val; + + val = ioread32(base + GMAC_STAT_DROP_EVENTS); + gs->drop_events += val; + val = ioread32(base + GMAC_STAT_OCTETS); + gs->octets += val; + val = ioread32(base + GMAC_STAT_PKTS); + gs->pkts += val; + val = ioread32(base + GMAC_STAT_BROADCAST_PKTS); + gs->broadcast_pkts += val; + val = ioread32(base + GMAC_STAT_MULTICAST_PKTS); + gs->multicast_pkts += val; + val = ioread32(base + GMAC_STAT_UNICAST_PKTS); + gs->unicast_pkts += val; + val = ioread32(base + GMAC_STAT_UNDERSIZE_PKTS); + gs->undersize_pkts += val; + val = ioread32(base + GMAC_STAT_OVERSIZE_PKTS); + gs->oversize_pkts += val; + val = ioread32(base + GMAC_STAT_PKTS_64_OCTETS); + gs->pkts_64_octets += val; + val = ioread32(base + GMAC_STAT_PKTS_65TO127_OCTETS); + gs->pkts_65to127_octets += val; + val = ioread32(base + GMAC_STAT_PKTS_128TO255_OCTETS); + gs->pkts_128to255_octets += val; + val = ioread32(base + GMAC_STAT_PKTS_256TO511_OCTETS); + gs->pkts_256to511_octets += val; + val = ioread32(base + GMAC_STAT_PKTS_512TO1023_OCTETS); + gs->pkts_512to1023_octets += val; + val = ioread32(base + GMAC_STAT_PKTS_1024TO1518_OCTETS); + gs->pkts_1024to1518_octets += val; + val = ioread32(base + GMAC_STAT_PKTS_1519TOX_OCTETS); + gs->pkts_1519tox_octets += val; +} + +static void adrv906x_mac_update_hw_stats(struct work_struct *work) +{ + struct adrv906x_mac *mac = container_of(work, struct adrv906x_mac, update_stats.work); + unsigned int val; + + rtnl_lock(); + val = ioread32(mac->xmac + MAC_GENERAL_CONTROL); + val |= TX_STATS_SNAPSHOT_BIT | RX_STATS_SNAPSHOT_BIT; + iowrite32(val, mac->xmac + MAC_GENERAL_CONTROL); + val = ioread32(mac->emac_tx + MAC_TX_STAT_UNDERFLOW); + mac->hw_stats_tx.underflow += val; + val = ioread32(mac->emac_tx + MAC_TX_STAT_PADDED); + mac->hw_stats_tx.padded += val; + adrv906x_mac_update_general_stats(mac->emac_tx, + &mac->hw_stats_tx.general_stats); + + val = ioread32(mac->emac_rx + MAC_RX_STAT_OVERFLOW); + mac->hw_stats_rx.overflow += val; + val = ioread32(mac->emac_rx + MAC_RX_STAT_CRC_ERRORS); + mac->hw_stats_rx.crc_errors += val; + val = ioread32(mac->emac_rx + MAC_RX_STAT_MC_DROP); + mac->hw_stats_rx.mc_drop += val; + val = ioread32(mac->emac_rx + MAC_RX_STAT_FRAGMENTS); + mac->hw_stats_rx.fragments += val; + val = ioread32(mac->emac_rx + MAC_RX_STAT_JABBERS); + mac->hw_stats_rx.jabbers += val; + val = ioread32(mac->emac_rx + MAC_RX_STAT_MAC_FRAMING_ERROR); + mac->hw_stats_rx.mac_framing_error += val; + val = ioread32(mac->emac_rx + MAC_RX_STAT_RS_FRAMING_ERROR); + mac->hw_stats_rx.rs_framing_error += val; + adrv906x_mac_update_general_stats(mac->emac_rx, + &mac->hw_stats_rx.general_stats); + rtnl_unlock(); + + mod_delayed_work(system_long_wq, &mac->update_stats, msecs_to_jiffies(1000)); +} + +void adrv906x_mac_cleanup(struct adrv906x_mac *mac) +{ + cancel_delayed_work(&mac->update_stats); +} + +int adrv906x_mac_init(struct adrv906x_mac *mac, unsigned int size) +{ + adrv906x_mac_set_mfs(mac, size); + + mac->id = ioread32(mac->xmac + MAC_IP_ID); + mac->version = ioread32(mac->xmac + MAC_IP_VERSION); + mac->cap = ioread32(mac->xmac + MAC_IP_CAPABILITIES); + + adrv906x_mac_tx_path_dis(mac); + adrv906x_mac_rx_path_dis(mac); + + INIT_DELAYED_WORK(&mac->update_stats, adrv906x_mac_update_hw_stats); + mod_delayed_work(system_long_wq, &mac->update_stats, msecs_to_jiffies(1000)); + + return 0; +} + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/adi/adrv906x-mac.h b/drivers/net/ethernet/adi/adrv906x-mac.h new file mode 100644 index 00000000000000..ca29e66de04251 --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-mac.h @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __ADRV906X_MAC_H__ +#define __ADRV906X_MAC_H__ + +#include +#include +#include +#include + +#define MAC_IP_ID 0x00000000 +#define MAC_IP_VERSION 0x00000004 +#define MAC_IP_CAPABILITIES 0x0000000C +#define MAC_GENERAL_CONTROL 0x00000010 +#define TX_STATS_SNAPSHOT_BIT BIT(8) +#define RX_STATS_SNAPSHOT_BIT BIT(12) + +#define MAC_TX_CTRL 0x00000000 +#define MAC_TX_PATH_EN BIT(0) +#define MAC_TX_MFS GENMASK(29, 16) +#define MAC_TX_STAT_UNDERFLOW 0x00000060 +#define MAC_TX_STAT_PADDED 0x00000068 + +#define MAC_RX_CTRL 0x00000000 +#define MAC_RX_PATH_EN BIT(0) +#define MAC_RX_PROMISCUOUS_MODE_EN BIT(3) +#define MAC_RX_PERMITTABLE_ADDR0_EN BIT(4) +#define MAC_RX_PERMITTABLE_ADDR1_EN BIT(5) +#define MAC_RX_PERMITTABLE_ADDR2_EN BIT(6) +#define MAC_RX_MFS GENMASK(29, 16) +#define MAC_RX_STAT_OVERFLOW 0x00000060 +#define MAC_RX_STAT_CRC_ERRORS 0x00000068 +#define MAC_RX_STAT_MC_DROP 0x00000070 +#define MAC_RX_STAT_FRAGMENTS 0x00000078 +#define MAC_RX_STAT_JABBERS 0x00000080 +#define MAC_RX_STAT_MAC_FRAMING_ERROR 0x0000008C +#define MAC_RX_STAT_RS_FRAMING_ERROR 0x00000094 + +#define GMAC_STAT_DROP_EVENTS 0x00000100 +#define GMAC_STAT_OCTETS 0x00000108 +#define GMAC_STAT_PKTS 0x00000110 +#define GMAC_STAT_BROADCAST_PKTS 0x00000118 +#define GMAC_STAT_MULTICAST_PKTS 0x00000120 +#define GMAC_STAT_UNICAST_PKTS 0x00000128 +#define GMAC_STAT_UNDERSIZE_PKTS 0x00000130 +#define GMAC_STAT_OVERSIZE_PKTS 0x00000138 +#define GMAC_STAT_PKTS_64_OCTETS 0x00000140 +#define GMAC_STAT_PKTS_65TO127_OCTETS 0x00000148 +#define GMAC_STAT_PKTS_128TO255_OCTETS 0x00000150 +#define GMAC_STAT_PKTS_256TO511_OCTETS 0x00000158 +#define GMAC_STAT_PKTS_512TO1023_OCTETS 0x00000160 +#define GMAC_STAT_PKTS_1024TO1518_OCTETS 0x00000168 +#define GMAC_STAT_PKTS_1519TOX_OCTETS 0x00000170 + +#define CFG_MULT_ADDR0_LOW 0x00000020 +#define CFG_MULT_ADDR1_LOW 0x00000024 +#define CFG_MULT_ADDR2_LOW 0x00000028 +#define CFG_MULT_ADDR0_HIGH 0x00000030 +#define CFG_MULT_ADDR1_HIGH 0x00000034 +#define CFG_MULT_ADDR2_HIGH 0x00000038 + +struct adrv906x_mac_general_stats { + u64 drop_events; + u64 octets; + u64 pkts; + u64 broadcast_pkts; + u64 multicast_pkts; + u64 unicast_pkts; + u64 undersize_pkts; + u64 oversize_pkts; + u64 pkts_64_octets; + u64 pkts_65to127_octets; + u64 pkts_128to255_octets; + u64 pkts_256to511_octets; + u64 pkts_512to1023_octets; + u64 pkts_1024to1518_octets; + u64 pkts_1519tox_octets; +}; + +struct adrv906x_mac_tx_stats { + u64 underflow; + u64 padded; + struct adrv906x_mac_general_stats general_stats; +}; + +struct adrv906x_mac_rx_stats { + u64 overflow; + u64 crc_errors; + u64 mc_drop; + u64 fragments; + u64 jabbers; + u64 mac_framing_error; + u64 rs_framing_error; + struct adrv906x_mac_general_stats general_stats; +}; + +struct adrv906x_mac { + unsigned int id; + unsigned int version; + unsigned int cap; + void __iomem *xmac; + void __iomem *emac_tx; + void __iomem *emac_rx; + struct adrv906x_mac_tx_stats hw_stats_tx; + struct adrv906x_mac_rx_stats hw_stats_rx; + struct delayed_work update_stats; +}; + +void adrv906x_mac_promiscuous_mode_en(struct adrv906x_mac *mac); +void adrv906x_mac_promiscuous_mode_dis(struct adrv906x_mac *mac); +void adrv906x_mac_rx_path_en(struct adrv906x_mac *mac); +void adrv906x_mac_rx_path_dis(struct adrv906x_mac *mac); +void adrv906x_mac_set_multicast_filter(struct adrv906x_mac *mac, u64 mac_addr, int filter_id); +void adrv906x_mac_cleanup(struct adrv906x_mac *mac); +int adrv906x_mac_init(struct adrv906x_mac *mac, unsigned int size); +void adrv906x_mac_set_path(struct adrv906x_mac *mac, bool enable); + +#endif /* __ADRV906X_MAC_H__ */ diff --git a/drivers/net/ethernet/adi/adrv906x-macsec-ext.c b/drivers/net/ethernet/adi/adrv906x-macsec-ext.c new file mode 100644 index 00000000000000..742640cfc9da62 --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-macsec-ext.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adrv906x-macsec-ext.h" + +struct cco_macsec_priv *cco_macsec_get_priv(struct net_device *netdev) +{ + struct adrv906x_macsec_priv *macsec = adrv906x_macsec_get(netdev); + + return &macsec->priv; +} + +void *cco_macsec_get_base(struct net_device *netdev) +{ + struct adrv906x_macsec_priv *macsec = adrv906x_macsec_get(netdev); + + return macsec->base; +} + +void cco_macsec_reg_wr(struct net_device *netdev, unsigned long addr, u32 value) +{ +// void __iomem *reg_ptr = (void __iomem *)cco_macsec_get_base(netdev); +// printk("adi- write reg %s(0x%lx, 0x%08x) reg_ptr=0x%lx\n", + // __func__, addr, value, reg_ptr); +// iowrite32(value, reg_ptr + addr); +} + +u32 cco_macsec_reg_rd(struct net_device *netdev, unsigned long addr) +{ + u32 val = 0; + +// void __iomem *reg_ptr = (void __iomem *)cco_macsec_get_base(netdev); +// val = ioread32(reg_ptr + addr ); +// printk("adi- read reg %s(0x%lx), return 0x%08x (reg_ptr=0x%lx)\n", + // __func__, addr, val, reg_ptr); + + // simulate values for some of the register reads here: + switch (addr) { + case (MACSEC_CORE_BASE_ADDR + MACSEC_CORE_IP_ID_BASE_ADDR): + val = CCO_MACSEC_IP_ID; + break; + case (MACSEC_CORE_BASE_ADDR + MACSEC_CORE_IP_VERSION_BASE_ADDR): + val = (CCO_MACSEC_MAJOR_VER << MACSEC_CORE_IP_VERSION_MAJOR_SHIFT); + break; + case (MACSEC_CORE_BASE_ADDR + MACSEC_CORE_IP_CAPABILITIES_1_BASE_ADDR): + val = ((4 << MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_PEERS_SHIFT) | + (16 << MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_CS_ENTRIES_RX_SHIFT) | + (16 << MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_CS_ENTRIES_TX_SHIFT) | + (4 << MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_SECYS_SHIFT)); + break; + case (MACSEC_CORE_BASE_ADDR + MACSEC_CORE_IP_CAPABILITIES_2_BASE_ADDR): + val = ((CCO_CS_AES_GCM_128 << MACSEC_CORE_IP_CAPABILITIES_2_AVAILABLE_CIPHERSUITES_SHIFT) | + (1 << MACSEC_CORE_IP_CAPABILITIES_2_VLAN_IN_CLEAR_SHIFT) | + (8 << MACSEC_CORE_IP_CAPABILITIES_2_NO_TT_ENTRIES_RX_SHIFT) | + (8 << MACSEC_CORE_IP_CAPABILITIES_2_NO_TT_ENTRIES_TX_SHIFT)); + break; + case (MACSEC_CORE_BASE_ADDR + MACSEC_CORE_IP_CS_CAPABILITY_BASE_ADDR): + val = (16 << MACSEC_CORE_IP_CS_CAPABILITY_ICVLENGTH_SHIFT); + break; + case (SECY_CONFIG_BASE_ADDR + SECY_CONFIG_TX_CONFIG_BASE_ADDR): + val = ((8 << SECY_CONFIG_TX_CONFIG_MAXTRANSMITKEYS_SHIFT) | + (8 << SECY_CONFIG_TX_CONFIG_MAXTRANSMITCHANNELS_SHIFT)); + break; + case (SECY_CONFIG_BASE_ADDR + SECY_CONFIG_RX_CONFIG_BASE_ADDR): + val = ((8 << SECY_CONFIG_RX_CONFIG_MAXRECEIVEKEYS_SHIFT) | + (8 << SECY_CONFIG_RX_CONFIG_MAXRECEIVECHANNELS_SHIFT)); + break; + default: + break; + } + return val; +} + +void cco_macsec_commonport_status_get(struct net_device *netdev, u8 *operational, u8 *enabled) +{ + // just always return up + *operational = 1; + *enabled = 1; +} + +void cco_macsec_max_framesize_get(struct net_device *netdev, u32 *max_framesize) +{ + *max_framesize = netdev->mtu + 14 + 8; // DMAC, SMAC, EthType, 2 VLAN tags +} + +irqreturn_t adi_macsec_isr(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} + +void adrv906x_macsec_commonport_status_update(struct net_device *netdev) +{ + cco_macsec_commonport_status_update(netdev, 1, 1); +} + +int adrv906x_macsec_probe(struct platform_device *pdev, struct net_device *netdev, + struct device_node *np) +{ + struct adrv906x_macsec_priv *macsec = adrv906x_macsec_get(netdev); + struct device *dev = &pdev->dev; + u32 reg, len; + int ret; + + macsec->dev = dev; + + if (of_property_read_u32_index(np, "macsec", 0, ®)) + goto error; + if (of_property_read_u32_index(np, "macsec", 1, &len)) + goto error; + + macsec->base = devm_ioremap(dev, reg, len); + if (!macsec->base) { + dev_err(dev, "ioremap macsec membase failed!"); + goto error; + } + + macsec->irq = of_irq_get_byname(np, "ts_event"); + if (macsec->irq <= 0) { + dev_err(dev, "cannot obtain macsec ts_event irq"); + goto error; + } + + dev_info(dev, "macsec irq %d", macsec->irq); + +/* TODO turn it on after basic MACsec functinality is tested + * ret = devm_request_irq(dev, macsec_irq, adi_macsec_isr, IRQF_SHARED, "macsec", dev); + * if (val) { + * dev_err(dev, "unable to register macsec interrupt %d", macsec_irq); + * return -EFAULT; + * } + */ + + macsec->enabled = 1; + + ret = cco_macsec_init(netdev); + if (ret) + dev_err(dev, "failed to initialize macsec hw offload driver"); + + return ret; + +error: + macsec->enabled = 0; + return 0; +} + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/adi/adrv906x-macsec-ext.h b/drivers/net/ethernet/adi/adrv906x-macsec-ext.h new file mode 100644 index 00000000000000..7054c9c05bbc39 --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-macsec-ext.h @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __ADRV906X_MACSEC_EXT_H__ +#define __ADRV906X_MACSEC_EXT_H__ + +#include +#include +#include +#include +#include +#include "macsec/cco_macsec.h" + +struct adrv906x_macsec_priv { + struct device *dev; + struct cco_macsec_priv priv; + void *base; + int irq; + bool enabled; +}; + +struct adrv906x_macsec_priv *adrv906x_macsec_get(struct net_device *netdev); +void adrv906x_macsec_commonport_status_update(struct net_device *netdev); +int adrv906x_macsec_probe(struct platform_device *pdev, struct net_device *netdev, + struct device_node *np); + +#endif /* __ADRV906X_MACSEC_EXT_H__ */ diff --git a/drivers/net/ethernet/adi/adrv906x-mdio.c b/drivers/net/ethernet/adi/adrv906x-mdio.c new file mode 100644 index 00000000000000..5d18f615d97252 --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-mdio.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include "adrv906x-mdio.h" +#include "adrv906x-net.h" + +struct adrv906x_mdio_priv { + struct device *dev; + void __iomem *pcs_base[MAX_NETDEV_NUM]; +}; + +static int adrv906x_pseudo_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 val) +{ + struct adrv906x_mdio_priv *priv = bus->priv; + u32 offset; + + offset = 4 * (regnum & 0xFFFF); + + iowrite32(val, priv->pcs_base[mii_id % MAX_NETDEV_NUM] + offset); + + return 0; +} + +static int adrv906x_pseudo_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + struct adrv906x_mdio_priv *priv = bus->priv; + u32 offset; + int ret; + + offset = 4 * (regnum & 0xFFFF); + + ret = ioread32(priv->pcs_base[mii_id % MAX_NETDEV_NUM] + offset) & 0xFFFF; + + return ret; +} + +static int adrv906x_pseudo_mdio_write_c45(struct mii_bus *bus, int addr, + int devnum, int regnum, u16 val) +{ + return adrv906x_pseudo_mdio_write(bus, addr, regnum, val); +} + +static int adrv906x_pseudo_mdio_read_c45(struct mii_bus *bus, int addr, + int devnum, int regnum) +{ + return adrv906x_pseudo_mdio_read(bus, addr, regnum); +} + +int adrv906x_mdio_register(struct adrv906x_eth_if *eth_if, struct device_node *mdio_np) +{ + struct device *dev = eth_if->dev; + struct adrv906x_mdio_priv *priv; + struct mii_bus *bus; + int idx, ret; + u32 reg, len; + + bus = devm_mdiobus_alloc_size(dev, sizeof(*priv)); + if (!bus) { + dev_err(dev, "failed to allocate private driver data"); + return -ENOMEM; + } + + priv = bus->priv; + priv->dev = dev; + + for (idx = 0; idx < MAX_NETDEV_NUM; idx++) { + of_property_read_u32_index(mdio_np, "reg", 2 * idx, ®); + of_property_read_u32_index(mdio_np, "reg", 2 * idx + 1, &len); + priv->pcs_base[idx] = devm_ioremap(dev, reg, len); + + if (IS_ERR(priv->pcs_base[idx])) + return PTR_ERR(priv->pcs_base[idx]); + } + + snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); + bus->name = "adrv906x-pseudo-mdio"; + bus->read = adrv906x_pseudo_mdio_read, + bus->write = adrv906x_pseudo_mdio_write, + bus->read_c45 = adrv906x_pseudo_mdio_read_c45, + bus->write_c45 = adrv906x_pseudo_mdio_write_c45, + bus->parent = dev; + + ret = of_mdiobus_register(bus, mdio_np); + if (ret) { + dev_err(dev, "failed to register mdio bus"); + return ret; + } + + eth_if->mdio = bus; + + return 0; +} + +void adrv906x_mdio_unregister(struct adrv906x_eth_if *eth_if) +{ + if (eth_if->mdio) + mdiobus_unregister(eth_if->mdio); +} diff --git a/drivers/net/ethernet/adi/adrv906x-mdio.h b/drivers/net/ethernet/adi/adrv906x-mdio.h new file mode 100644 index 00000000000000..e53a2218cbe9c4 --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-mdio.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __ADRV906X_MDIO_H__ +#define __ADRV906X_MDIO_H__ + +#include +#include +#include +#include "adrv906x-net.h" + +int adrv906x_mdio_register(struct adrv906x_eth_if *eth_if, struct device_node *mdio_np); +void adrv906x_mdio_unregister(struct adrv906x_eth_if *eth_if); + +#endif /* __ADRV906X_MDIO_H__ */ diff --git a/drivers/net/ethernet/adi/adrv906x-ndma.c b/drivers/net/ethernet/adi/adrv906x-ndma.c new file mode 100644 index 00000000000000..d5a1c3def826cf --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-ndma.c @@ -0,0 +1,1854 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adrv906x-ndma.h" + +#define NDMA_TX_STAT_AND_CTRL 0x000 +#define NDMA_DATAPATH_EN BIT(0) +#define NDMA_TX_EVENT_EN 0x004 +#define NDMA_TX_EVENT_STAT 0x008 +#define NDMA_TX_STATUS_FIFO_FULL_EVENT BIT(4) +#define NDMA_TX_FRAME_SIZE_ERR_EVENT BIT(3) +#define NDMA_TX_STATUS_WRITE_COMPLETE_EVENT BIT(2) +#define NDMA_TX_WORKUNIT_COMPLETE_EVENT BIT(1) +#define NDMA_TX_WU_HEADER_ERR_EVENT BIT(0) +#define NDMA_TX_TIMEOUT_VALUE 0x00c +#define NDMA_TX_FRAME_SIZE 0x010 +#define NDMA_TX_MIN_FRAME_SIZE GENMASK(15, 0) +#define NDMA_TX_MAX_FRAME_SIZE GENMASK(31, 16) + +#define NDMA_TX_ERROR_EVENTS (NDMA_TX_FRAME_SIZE_ERR_EVENT | \ + NDMA_TX_WU_HEADER_ERR_EVENT) +#define NDMA_TX_STATUS_EVENTS (NDMA_TX_STATUS_FIFO_FULL_EVENT | \ + NDMA_TX_STATUS_WRITE_COMPLETE_EVENT | \ + NDMA_TX_WORKUNIT_COMPLETE_EVENT) + +#define NDMA_RX_STAT_AND_CTRL 0x000 +#define NDMA_RX_DATAPATH_EN BIT(0) +#define NDMA_LOOPBACK_EN BIT(20) +#define NDMA_RX_EVENT_EN 0x004 +#define NDMA_RX_EVENT_STAT 0x008 +#define NDMA_RX_FRAME_SIZE_ERR_EVENT BIT(4) +#define NDMA_RX_ERR_EVENT BIT(3) +#define NDMA_RX_STATUS_WR_EVENT BIT(2) +#define NDMA_RX_WORKUNIT_COMPLETE_EVENT BIT(1) +#define NDMA_RX_FRAME_DROPPED_ERR_EVENT BIT(0) +#define NDMA_RX_FRAME_DROPPED_COUNT_MPLANE 0x00c +#define NDMA_RX_FRAME_DROPPED_COUNT_SPLANE 0x010 +#define NDMA_RX_FRAME_SIZE 0x014 +#define NDMA_RX_MIN_FRAME_SIZE GENMASK(15, 0) +#define NDMA_RX_MAX_FRAME_SIZE GENMASK(31, 16) +#define NDMA_RX_SYNC_FIFO_MPLANE_THRESHOLD 0x018 +#define NDMA_RX_IPV4_FRAME_FIELD_VALUE0 0x100 +#define NDMA_RX_IPV4_FRAME_FIELD_VALUE1 0x104 +#define NDMA_RX_IPV4_FRAME_FIELD_OFFSET0 0x108 +#define NDMA_RX_IPV4_FRAME_FIELD_OFFSET1 0x10c +#define NDMA_RX_IPV4_FRAME_FIELD_MASK0 0x110 +#define NDMA_RX_IPV4_FRAME_FIELD_MASK1 0x114 +#define NDMA_RX_IPV6_FRAME_FIELD_VALUE0 0x200 +#define NDMA_RX_IPV6_FRAME_FIELD_VALUE1 0x204 +#define NDMA_RX_IPV6_FRAME_FIELD_OFFSET0 0x208 +#define NDMA_RX_IPV6_FRAME_FIELD_OFFSET1 0x20c +#define NDMA_RX_IPV6_FRAME_FIELD_MASK0 0x210 +#define NDMA_RX_IPV6_FRAME_FIELD_MASK1 0x214 +#define NDMA_RX_ETH_FRAME_FIELD_VALUE 0x300 +#define NDMA_RX_ETH_FRAME_FIELD_OFFSET 0x304 +#define NDMA_RX_ETH_FRAME_FIELD_MASK 0x308 +#define NDMA_RX_SPLANE_FILTER_PTP_MSG_VALUE 0x400 +#define NDMA_RX_SPLANE_FILTER_VLAN_TAG_VALUE 0x404 +#define NDMA_RX_SPLANE_FILTER_VLAN_TAG_OFFSET 0x408 +#define NDMA_RX_SPLANE_FILTER_VLAN_TAG_MASK 0x40c +#define NDMA_RX_SPLANE_FILTER_VLAN_FRAME_OFFSET 0x410 +#define NDMA_RX_SPLANE_FILTER_EN 0x414 +#define NDMA_RX_GEN_FILTER_REG_STRIDE 0x100 +#define NDMA_RX_GEN_FILTER0_REG_OFFSET 0x500 +#define NDMA_RX_GEN_FILTER1_REG_OFFSET 0x600 +#define NDMA_RX_GEN_FILTER2_REG_OFFSET 0x700 +#define NDMA_RX_GEN_FILTER3_REG_OFFSET 0x800 +#define NDMA_RX_GEN_FILTER4_REG_OFFSET 0x900 + +#define NDMA_RX_CYCLE0_LOWER_VALUE_REG_OFFSET 0x00 +#define NDMA_RX_CYCLE0_LOWER_MASK_REG_OFFSET 0x60 + +#define NDMA_RX_ERROR_EVENTS (NDMA_RX_FRAME_SIZE_ERR_EVENT | \ + NDMA_RX_ERR_EVENT | \ + NDMA_RX_FRAME_DROPPED_ERR_EVENT) +#define NDMA_RX_STATUS_EVENTS (NDMA_RX_STATUS_WR_EVENT | \ + NDMA_RX_WORKUNIT_COMPLETE_EVENT) + +#define NDMA_INTR_CTRL_TX 0x00 +#define NDMA_INTR_CTRL_TX_DMA_ERR_EN BIT(4) +#define NDMA_INTR_CTRL_TX_DMA_DMADONE_EN BIT(3) +#define NDMA_INTR_CTRL_TX_DMA_DONE_EN BIT(2) +#define NDMA_INTR_CTRL_TX_ERR_EN BIT(0) +#define NDMA_INTR_CTRL_STATUS 0x10 +#define NDMA_INTR_CTRL_TX_STATUS_DMA_ERR_EN BIT(4) +#define NDMA_INTR_CTRL_TX_STATUS_DMA_DMADONE_EN BIT(3) +#define NDMA_INTR_CTRL_TX_STATUS_DMA_DONE_EN BIT(2) +#define NDMA_INTR_CTRL_TX_STATUS_EN BIT(1) +#define NDMA_INTR_CTRL_RX_STATUS_EN BIT(0) +#define NDMA_INTR_CTRL_RX 0x20 +#define NDMA_INTR_CTRL_RX_DMA_ERR_EN BIT(4) +#define NDMA_INTR_CTRL_RX_DMA_DMADONE_EN BIT(3) +#define NDMA_INTR_CTRL_RX_DMA_DONE_EN BIT(2) +#define NDMA_INTR_CTRL_RX_ERR_EN BIT(0) + +#define NDMA_RESET 0x00 +#define NDMA_RX0_RST BIT(0) +#define NDMA_RX1_RST BIT(1) +#define NDMA_TX0_RST BIT(2) +#define NDMA_TX1_RST BIT(3) +#define NDMA_TX0_PTP_MODE BIT(4) +#define NDMA_TX1_PTP_MODE BIT(5) + +#define DMA_NEXT_DESC 0x00 +#define DMA_ADDRSTART 0x04 + +#define DMA_CFG 0x08 +#define DMA2D BIT(26) /* DMA Mode (2D/1D*) */ +#define DESCIDCPY BIT(25) /* Descriptor ID Copy Control */ +#define DMA_INT_MSK GENMASK(21, 20) /* Generate Interrupt Bits Mask */ +#define DI_EN_X 0x00100000 /* Data Interrupt Enable in X count */ +#define DI_EN_Y 0x00200000 /* Data Interrupt Enable in Y count */ +#define DI_EN_P 0x00300000 /* Data Interrupt Enable in Peripheral */ +#define DI_EN DI_EN_X /* Data Interrupt Enable */ +#define NDSIZE GENMASK(18, 16) /* Next Descriptor */ +#define NDSIZE_0 0x00000000 /* Next Descriptor Size 1 */ +#define NDSIZE_1 0x00010000 /* Next Descriptor Size 2 */ +#define NDSIZE_2 0x00020000 /* Next Descriptor Size 3 */ +#define NDSIZE_3 0x00030000 /* Next Descriptor Size 4 */ +#define NDSIZE_4 0x00040000 /* Next Descriptor Size 5 */ +#define NDSIZE_5 0x00050000 /* Next Descriptor Size 6 */ +#define NDSIZE_6 0x00060000 /* Next Descriptor Size 7 */ +#define NDSIZE_OFFSET 16 /* Next Descriptor Size Offset */ +#define DMAFLOW GENMASK(14, 12) /* Flow Control */ +#define DMAFLOW_STOP 0x00000000 /* Stop Mode */ +#define DMAFLOW_AUTO 0x00001000 /* Autobuffer Mode */ +#define DMAFLOW_LIST 0x00004000 /* Descriptor List Mode */ +#define DMAFLOW_LARGE DMAFLOW_LIST +#define DMAFLOW_ARRAY 0x00005000 /* Descriptor Array Mode */ +#define DMAFLOW_LIST_DEMAND 0x00006000 /* Descriptor Demand List Mode */ +#define DMAFLOW_ARRAY_DEMAND 0x00007000 /* Descriptor Demand Array Mode */ +#define WDSIZE_MSK GENMASK(10, 8) /* Memory Transfer Word Size Mask */ +#define WDSIZE_8 0x00000000 /* Memory Transfer Word Size 8 bits */ +#define WDSIZE_16 0x00000100 /* Memory Transfer Word Size 16 bits */ +#define WDSIZE_32 0x00000200 /* Memory Transfer Word Size 32 bits */ +#define WDSIZE_64 0x00000300 /* Memory Transfer Word Size 64 bits */ +#define WDSIZE_128 0x00000400 /* Memory Transfer Word Size 128 bits */ +#define WDSIZE_256 0x00000500 /* Memory Transfer Word Size 256 bits */ +#define PSIZE_MSK GENMASK(6, 4) /* Peripheral Transfer Word Size Mask */ +#define PSIZE_8 0x00000000 /* Peripheral Transfer Word Size 8 bits */ +#define PSIZE_16 0x00000010 /* Peripheral Transfer Word Size 16 bits */ +#define PSIZE_32 0x00000020 /* Peripheral Transfer Word Size 32 bits */ +#define PSIZE_64 0x00000030 /* Peripheral Transfer Word Size 64 bits */ +#define DMASYNC BIT(2) /* DMA Buffer Clear SYNC */ +#define WNR BIT(1) /* Channel Direction (W/R*) */ +#define DMAEN BIT(0) /* DMA Channel Enable */ +#define DMA_XCNT 0x0c +#define DMA_XMOD 0x10 +#define XMODE_8 0x01 +#define XMODE_16 0x02 +#define XMODE_32 0x04 +#define XMODE_64 0x08 +#define XMODE_128 0x10 +#define XMODE_256 0x20 +#define DMA_YCNT 0x14 +#define DMA_YMOD 0x18 +#define DSCPTR_CUR 0x24 +#define DSCPTR_PRV 0x28 +#define DMA_ADDR_CUR 0x2c +#define DMA_STAT 0x30 +#define DMA_RUN_MASK GENMASK(10, 8) /* DMA Run Bits Mask */ +#define DMA_RUN_IDLE 0x00000000 /* DMA Run IDLE */ +#define DMA_RUN_DFETCH 0x00000100 /* DMA Run Fetch */ +#define DMA_RUN 0x00000200 /* DMA Run Trans */ +#define DMA_RUN_WAIT_TRIG 0x00000300 /* DMA Run WAIT TRIG */ +#define DMA_RUN_WAIT_ACK 0x00000400 /* DMA Run WAIT ACK */ +#define DMA_PIRQ BIT(2) /* DMA Peripheral Error Interrupt Status */ +#define DMA_ERR BIT(1) /* DMA Error Interrupt Status */ +#define DMA_DONE BIT(0) /* DMA Completion Interrupt Status */ +#define DMA_XCNT_CUR 0x34 +#define DMA_YCNT_CUR 0x38 +#define DMA_BWLCNT 0x40 +#define DMA_BWLCNT_CUR 0x44 +#define DMA_BWMCNT 0x48 +#define DMA_BWMCNT_CUR 0x4c +#define FULL_BANDWIDTH 0x0000 +#define SUSPEND_TRANSFER 0xffff + +#define DMA_DESC_FETCH 0x100 /* DMA is fetching descriptors */ +#define DMA_DATA_XFER 0x200 /* DMA is in data transfer state */ +#define DMA_IDLE_MASK 0x700 +#define DMA_IDLE(x) (((x)& DMA_IDLE_MASK) == 0) +#define DMA_FETCHING_DESC(x) ((x)& DMA_DESC_FETCH) +#define DMA_XFER_DATA(x) ((x)& DMA_DATA_XFER) + +#define TIMEOUT_100_MS (HZ / 10) + +DEFINE_SPINLOCK(ndma_reset_lock); + +enum adrv906x_ndma_rx_filter_status { + NDMA_RX_FILTER_OFF, + NDMA_RX_FILTER_ON +}; + +enum adrv906x_ndma_error { + NDMA_NO_ERROR, + NDMA_TX_FRAME_SIZE_ERROR, + NDMA_TX_DATA_HEADER_ERROR, + NDMA_TX_STATUS_HEADER_ERROR, + NDMA_TX_TSTAMP_TIMEOUT_ERROR, + NDMA_TX_SEQNUM_MISMATCH_ERROR, + NDMA_TX_DMA_TRANS_ERROR, + NDMA_TX_UNKNOWN_ERROR, + NDMA_RX_FRAME_SIZE_ERROR, + NDMA_RX_FRAME_DROPPED_ERROR, + NDMA_RX_ERROR, + NDMA_RX_SEQNUM_MISMATCH_ERROR, + NDMA_RX_DMA_TRANS_ERROR, + NDMA_RX_UNKNOWN_ERROR, +}; + +enum adrv906x_ndma_irqs { + NDMA_TX_DATA_DMA_ERR_IRQ = BIT(0), + NDMA_TX_DATA_DMA_DMADONE_IRQ = BIT(1), + NDMA_TX_DATA_DMA_DONE_IRQ = BIT(2), + NDMA_TX_STATUS_DMA_ERR_IRQ = BIT(3), + NDMA_TX_STATUS_DMA_DMADONE_IRQ = BIT(4), + NDMA_TX_STATUS_DMA_DONE_IRQ = BIT(5), + NDMA_TX_STATUS_IRQ = BIT(6), + NDMA_TX_ERR_IRQ = BIT(7), + NDMA_RX_ERR_IRQ = BIT(8), + NDMA_RX_STATUS_IRQ = BIT(9), + NDMA_RX_DMA_ERR_IRQ = BIT(10), + NDMA_RX_DMA_DMADONE_IRQ = BIT(11), + NDMA_RX_DMA_DONE_IRQ = BIT(12), +}; + +enum adrv906x_ndma_rx_filter_id { + NDMA_RX_IPV4_FILTER = 0, + NDMA_RX_IPV6_FILTER, + NDMA_RX_ETH_FILTER, + NDMA_RX_GENERIC_FILTER_0, + NDMA_RX_GENERIC_FILTER_1, + NDMA_RX_GENERIC_FILTER_2, + NDMA_RX_GENERIC_FILTER_3, + NDMA_RX_GENERIC_FILTER_4, + NDMA_RX_FILTER_CNT, +}; + +static void get_ts_from_status(unsigned char *status, struct timespec64 *ts) +{ + ts->tv_nsec = ((uint32_t)status[4]) | ((uint32_t)status[5] << 8) | + ((uint32_t)status[6] << 16) | ((uint32_t)status[7] << 24); + + ts->tv_sec = ((uint64_t)status[8]) | ((uint64_t)status[9] << 8) | + ((uint64_t)status[10] << 16) | ((uint64_t)status[11] << 24) | + ((uint64_t)status[12] << 32) | ((uint64_t)status[13] << 40); +} + +static bool is_timestamp_all_zero(unsigned char *status) +{ + unsigned char str[12] = { 0 }; + + return !memcmp(&status[2], str, 12); +} + +static void adrv906x_ndma_enable_irqs(struct adrv906x_ndma_dev *ndma_dev, + enum adrv906x_ndma_irqs irqs) +{ + unsigned int val; + + val = 0; + if (irqs & NDMA_TX_DATA_DMA_ERR_IRQ) + val |= NDMA_INTR_CTRL_TX_DMA_ERR_EN; + if (irqs & NDMA_TX_DATA_DMA_DMADONE_IRQ) + val |= NDMA_INTR_CTRL_TX_DMA_DMADONE_EN; + if (irqs & NDMA_TX_DATA_DMA_DONE_IRQ) + val |= NDMA_INTR_CTRL_TX_DMA_DONE_EN; + if (irqs & NDMA_TX_ERR_IRQ) + val |= NDMA_INTR_CTRL_TX_ERR_EN; + if (val) { + val |= ioread32(ndma_dev->intr_ctrl + NDMA_INTR_CTRL_TX); + iowrite32(val, ndma_dev->intr_ctrl + NDMA_INTR_CTRL_TX); + } + + val = 0; + if (irqs & NDMA_TX_STATUS_DMA_ERR_IRQ) + val |= NDMA_INTR_CTRL_TX_STATUS_DMA_ERR_EN; + if (irqs & NDMA_TX_STATUS_DMA_DMADONE_IRQ) + val |= NDMA_INTR_CTRL_TX_STATUS_DMA_DMADONE_EN; + if (irqs & NDMA_TX_STATUS_DMA_DONE_IRQ) + val |= NDMA_INTR_CTRL_TX_STATUS_DMA_DONE_EN; + if (irqs & NDMA_TX_STATUS_IRQ) + val |= NDMA_INTR_CTRL_TX_STATUS_EN; + if (irqs & NDMA_RX_STATUS_IRQ) + val |= NDMA_INTR_CTRL_RX_STATUS_EN; + if (val) { + val |= ioread32(ndma_dev->intr_ctrl + NDMA_INTR_CTRL_STATUS); + iowrite32(val, ndma_dev->intr_ctrl + NDMA_INTR_CTRL_STATUS); + } + + val = 0; + if (irqs & NDMA_RX_DMA_ERR_IRQ) + val |= NDMA_INTR_CTRL_RX_DMA_ERR_EN; + if (irqs & NDMA_RX_DMA_DMADONE_IRQ) + val |= NDMA_INTR_CTRL_RX_DMA_DMADONE_EN; + if (irqs & NDMA_RX_DMA_DONE_IRQ) + val |= NDMA_INTR_CTRL_RX_DMA_DONE_EN; + if (irqs & NDMA_RX_ERR_IRQ) + val |= NDMA_INTR_CTRL_RX_ERR_EN; + + if (val) { + val |= ioread32(ndma_dev->intr_ctrl + NDMA_INTR_CTRL_RX); + iowrite32(val, ndma_dev->intr_ctrl + NDMA_INTR_CTRL_RX); + } +} + +static void adrv906x_ndma_disable_irqs(struct adrv906x_ndma_dev *ndma_dev, + enum adrv906x_ndma_irqs irqs) +{ + unsigned int val; + + val = 0; + if (irqs & NDMA_TX_DATA_DMA_ERR_IRQ) + val |= NDMA_INTR_CTRL_TX_DMA_ERR_EN; + if (irqs & NDMA_TX_DATA_DMA_DMADONE_IRQ) + val |= NDMA_INTR_CTRL_TX_DMA_DMADONE_EN; + if (irqs & NDMA_TX_DATA_DMA_DONE_IRQ) + val |= NDMA_INTR_CTRL_TX_DMA_DONE_EN; + if (irqs & NDMA_TX_ERR_IRQ) + val |= NDMA_INTR_CTRL_TX_ERR_EN; + if (val) { + val = ioread32(ndma_dev->intr_ctrl + NDMA_INTR_CTRL_TX) & ~val; + iowrite32(val, ndma_dev->intr_ctrl + NDMA_INTR_CTRL_TX); + } + + val = 0; + if (irqs & NDMA_TX_STATUS_DMA_ERR_IRQ) + val |= NDMA_INTR_CTRL_TX_STATUS_DMA_ERR_EN; + if (irqs & NDMA_TX_STATUS_DMA_DMADONE_IRQ) + val |= NDMA_INTR_CTRL_TX_STATUS_DMA_DMADONE_EN; + if (irqs & NDMA_TX_STATUS_DMA_DONE_IRQ) + val |= NDMA_INTR_CTRL_TX_STATUS_DMA_DONE_EN; + if (irqs & NDMA_TX_STATUS_IRQ) + val |= NDMA_INTR_CTRL_TX_STATUS_EN; + if (irqs & NDMA_RX_STATUS_IRQ) + val |= NDMA_INTR_CTRL_RX_STATUS_EN; + if (val) { + val = ioread32(ndma_dev->intr_ctrl + NDMA_INTR_CTRL_STATUS) & ~val; + iowrite32(val, ndma_dev->intr_ctrl + NDMA_INTR_CTRL_STATUS); + } + + val = 0; + if (irqs & NDMA_RX_DMA_ERR_IRQ) + val |= NDMA_INTR_CTRL_RX_DMA_ERR_EN; + if (irqs & NDMA_RX_DMA_DMADONE_IRQ) + val |= NDMA_INTR_CTRL_RX_DMA_DMADONE_EN; + if (irqs & NDMA_RX_DMA_DONE_IRQ) + val |= NDMA_INTR_CTRL_RX_DMA_DONE_EN; + if (irqs & NDMA_RX_ERR_IRQ) + val |= NDMA_INTR_CTRL_RX_ERR_EN; + if (val) { + val = ioread32(ndma_dev->intr_ctrl + NDMA_INTR_CTRL_RX) & ~val; + iowrite32(val, ndma_dev->intr_ctrl + NDMA_INTR_CTRL_RX); + } +} + +static void adrv906x_ndma_disable_all_irqs(struct adrv906x_ndma_dev *ndma_dev) +{ + iowrite32(0, ndma_dev->intr_ctrl + NDMA_INTR_CTRL_TX); + iowrite32(0, ndma_dev->intr_ctrl + NDMA_INTR_CTRL_STATUS); + iowrite32(0, ndma_dev->intr_ctrl + NDMA_INTR_CTRL_RX); +} + +static void adrv906x_dma_rx_reset(struct adrv906x_ndma_chan *ndma_ch) +{ + iowrite32(0, ndma_ch->rx_dma_base + DMA_CFG); + iowrite32(DMA_ERR | DMA_PIRQ | DMA_DONE, ndma_ch->rx_dma_base + DMA_STAT); +} + +static void adrv906x_dma_tx_reset(struct adrv906x_ndma_chan *ndma_ch) +{ + if (ndma_ch->tx_dma_base) { + iowrite32(0, ndma_ch->tx_dma_base + DMA_CFG); + iowrite32(DMA_ERR | DMA_PIRQ | DMA_DONE, ndma_ch->tx_dma_base + DMA_STAT); + } +} + +static void adrv906x_dma_rx_start(struct adrv906x_ndma_chan *ndma_ch) +{ + dma_addr_t desc_addr; + + desc_addr = ndma_ch->rx_ring_dma + sizeof(struct dma_desc) * ndma_ch->rx_tail; + + iowrite32(0, ndma_ch->rx_dma_base + DMA_CFG); + iowrite32(0, ndma_ch->rx_dma_base + DMA_STAT); + iowrite32(desc_addr, ndma_ch->rx_dma_base + DMA_NEXT_DESC); + iowrite32(ndma_ch->rx_ring[ndma_ch->rx_tail].cfg, ndma_ch->rx_dma_base + DMA_CFG); +} + +static void adrv906x_dma_tx_start(struct adrv906x_ndma_chan *ndma_ch) +{ + dma_addr_t desc_addr; + + desc_addr = ndma_ch->tx_ring_dma + sizeof(struct dma_desc) * ndma_ch->tx_tail; + iowrite32(0, ndma_ch->tx_dma_base + DMA_CFG); + iowrite32(desc_addr, ndma_ch->tx_dma_base + DMA_NEXT_DESC); + iowrite32(ndma_ch->tx_ring[ndma_ch->tx_tail].cfg | DMAFLOW_LIST, + ndma_ch->tx_dma_base + DMA_CFG); +} + +static irqreturn_t adrv906x_dma_rx_done_irq_handler(int irq, void *ctx) +{ + struct adrv906x_ndma_chan *ndma_ch = ctx; + struct adrv906x_ndma_dev *ndma_dev = ndma_ch->parent; + unsigned long flags; + + if (napi_schedule_prep(&ndma_ch->napi)) { + spin_lock_irqsave(&ndma_ch->lock, flags); + if (ndma_ch->chan_type == NDMA_RX_CHANNEL) + adrv906x_ndma_disable_irqs(ndma_dev, NDMA_RX_DMA_DONE_IRQ); + else + adrv906x_ndma_disable_irqs(ndma_dev, NDMA_TX_STATUS_DMA_DONE_IRQ); + spin_unlock_irqrestore(&ndma_ch->lock, flags); + __napi_schedule(&ndma_ch->napi); + } + + return IRQ_HANDLED; +} + +static irqreturn_t adrv906x_dma_error_irq_handler(int irq, void *ctx) +{ + struct adrv906x_ndma_chan *ndma_ch = ctx; + struct adrv906x_ndma_dev *ndma_dev = ndma_ch->parent; + struct device *dev = ndma_dev->dev; + + if (ndma_ch->rx_dma_error_irq == irq) + adrv906x_dma_rx_reset(ndma_ch); + else + adrv906x_dma_tx_reset(ndma_ch); + + dev_dbg(dev, "%s", __func__); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t adrv906x_dma_error_irq_handler_thread(int irq, void *ctx) +{ + struct adrv906x_ndma_chan *ndma_ch = ctx; + struct adrv906x_ndma_dev *ndma_dev = ndma_ch->parent; + struct device *dev = ndma_dev->dev; + unsigned long flags; + + /* TODO: Implement recovery procedure */ + spin_lock_irqsave(&ndma_ch->lock, flags); + if (ndma_ch->chan_type == NDMA_RX_CHANNEL) { + ndma_ch->stats.rx.dma_errors++; + } else { + if (ndma_ch->rx_dma_error_irq == irq) + ndma_ch->stats.tx.status_dma_errors++; + else + ndma_ch->stats.tx.data_dma_errors++; + } + spin_unlock_irqrestore(&ndma_ch->lock, flags); + + dev_dbg(dev, "%s", __func__); + + return IRQ_HANDLED; +} + +static void adrv906x_ndma_chan_enable(struct adrv906x_ndma_chan *ndma_ch) +{ + unsigned int val, offset; + + offset = (ndma_ch->chan_type == NDMA_RX_CHANNEL) ? + NDMA_RX_STAT_AND_CTRL : NDMA_TX_STAT_AND_CTRL; + + val = ioread32(ndma_ch->ctrl_base + offset); + val |= NDMA_DATAPATH_EN; + iowrite32(val, ndma_ch->ctrl_base + offset); +} + +static bool adrv906x_ndma_chan_enabled(struct adrv906x_ndma_chan *ndma_ch) +{ + unsigned int val, offset; + + offset = (ndma_ch->chan_type == NDMA_RX_CHANNEL) ? + NDMA_RX_STAT_AND_CTRL : NDMA_TX_STAT_AND_CTRL; + val = ioread32(ndma_ch->ctrl_base + offset); + return val & NDMA_DATAPATH_EN; +} + +static void adrv906x_ndma_chan_disable(struct adrv906x_ndma_chan *ndma_ch) +{ + unsigned int val, offset; + + offset = (ndma_ch->chan_type == NDMA_RX_CHANNEL) ? + NDMA_RX_STAT_AND_CTRL : NDMA_TX_STAT_AND_CTRL; + + val = ioread32(ndma_ch->ctrl_base + offset); + val &= ~NDMA_DATAPATH_EN; + iowrite32(val, ndma_ch->ctrl_base + offset); + + /* Reset DMAs just to ensure there will be no active DMA transfer during NDMA reset */ + adrv906x_dma_rx_reset(ndma_ch); + adrv906x_dma_tx_reset(ndma_ch); +} + +static void adrv906x_ndma_set_frame_size(struct adrv906x_ndma_dev *ndma_dev) +{ + struct adrv906x_ndma_chan *rx_chan = &ndma_dev->rx_chan; + struct adrv906x_ndma_chan *tx_chan = &ndma_dev->tx_chan; + unsigned int val; + + val = FIELD_PREP(NDMA_RX_MIN_FRAME_SIZE, NDMA_RX_MIN_FRAME_SIZE_VALUE) + | FIELD_PREP(NDMA_RX_MAX_FRAME_SIZE, NDMA_MAX_FRAME_SIZE_VALUE); + iowrite32(val, rx_chan->ctrl_base + NDMA_RX_FRAME_SIZE); + + val = FIELD_PREP(NDMA_TX_MIN_FRAME_SIZE, NDMA_TX_MIN_FRAME_SIZE_VALUE) + | FIELD_PREP(NDMA_TX_MAX_FRAME_SIZE, NDMA_MAX_FRAME_SIZE_VALUE); + iowrite32(val, tx_chan->ctrl_base + NDMA_TX_FRAME_SIZE); +} + +static void adrv906x_ndma_set_tx_timeout_value(struct adrv906x_ndma_dev *ndma_dev, u32 val) +{ + struct adrv906x_ndma_chan *tx_chan = &ndma_dev->tx_chan; + + iowrite32(val, tx_chan->ctrl_base + NDMA_TX_TIMEOUT_VALUE); +} + +void adrv906x_ndma_set_ptp_mode(struct adrv906x_ndma_dev *ndma_dev, u32 mode) +{ + struct adrv906x_ndma_reset *reset = &ndma_dev->reset; + unsigned int val; + unsigned long flags; + + spin_lock_irqsave(&ndma_reset_lock, flags); + val = ioread32(reset->reg); + if (ndma_dev->dev_num == 0) { + val &= ~NDMA_TX0_PTP_MODE; + val |= FIELD_PREP(NDMA_TX0_PTP_MODE, mode); + } else { + val &= ~NDMA_TX1_PTP_MODE; + val |= FIELD_PREP(NDMA_TX1_PTP_MODE, mode); + } + iowrite32(val, reset->reg); + spin_unlock_irqrestore(&ndma_reset_lock, flags); +} + +static void adrv906x_ndma_config_rx_ipv4_filter(struct adrv906x_ndma_chan *ndma_ch) +{ + iowrite32(0x11450800, ndma_ch->ctrl_base + NDMA_RX_IPV4_FRAME_FIELD_VALUE0); + iowrite32(0x140013F, ndma_ch->ctrl_base + NDMA_RX_IPV4_FRAME_FIELD_VALUE1); + iowrite32(0x24170E0C, ndma_ch->ctrl_base + NDMA_RX_IPV4_FRAME_FIELD_OFFSET0); + iowrite32(0x2A, ndma_ch->ctrl_base + NDMA_RX_IPV4_FRAME_FIELD_OFFSET1); + iowrite32(0x00, ndma_ch->ctrl_base + NDMA_RX_IPV4_FRAME_FIELD_MASK0); + iowrite32(0xF0000, ndma_ch->ctrl_base + NDMA_RX_IPV4_FRAME_FIELD_MASK1); +} + +static void adrv906x_ndma_config_rx_ipv6_filter(struct adrv906x_ndma_chan *ndma_ch) +{ + iowrite32(0x110686DD, ndma_ch->ctrl_base + NDMA_RX_IPV6_FRAME_FIELD_VALUE0); + iowrite32(0x140013F, ndma_ch->ctrl_base + NDMA_RX_IPV6_FRAME_FIELD_VALUE1); + iowrite32(0x38140E0C, ndma_ch->ctrl_base + NDMA_RX_IPV6_FRAME_FIELD_OFFSET0); + iowrite32(0x3E, ndma_ch->ctrl_base + NDMA_RX_IPV6_FRAME_FIELD_OFFSET1); + iowrite32(0x00, ndma_ch->ctrl_base + NDMA_RX_IPV6_FRAME_FIELD_MASK0); + iowrite32(0xF0000, ndma_ch->ctrl_base + NDMA_RX_IPV6_FRAME_FIELD_MASK1); +} + +static void adrv906x_ndma_config_rx_eth_filter(struct adrv906x_ndma_chan *ndma_ch) +{ + iowrite32(0x88F7, ndma_ch->ctrl_base + NDMA_RX_ETH_FRAME_FIELD_VALUE); + iowrite32(0xE0C, ndma_ch->ctrl_base + NDMA_RX_ETH_FRAME_FIELD_OFFSET); + iowrite32(0xF0000, ndma_ch->ctrl_base + NDMA_RX_ETH_FRAME_FIELD_MASK); +} + +static void adrv906x_ndma_config_rx_splane_filter(struct adrv906x_ndma_chan *ndma_ch) +{ + iowrite32(0xCB9810, ndma_ch->ctrl_base + NDMA_RX_SPLANE_FILTER_PTP_MSG_VALUE); + iowrite32(0x88A88100, ndma_ch->ctrl_base + NDMA_RX_SPLANE_FILTER_VLAN_TAG_VALUE); + iowrite32(0x0C, ndma_ch->ctrl_base + NDMA_RX_SPLANE_FILTER_VLAN_TAG_OFFSET); + iowrite32(0x00, ndma_ch->ctrl_base + NDMA_RX_SPLANE_FILTER_VLAN_TAG_MASK); + iowrite32(0x84, ndma_ch->ctrl_base + NDMA_RX_SPLANE_FILTER_VLAN_FRAME_OFFSET); +} + +static void adrv906x_ndma_config_rx_ecpri_filter(struct adrv906x_ndma_chan *ndma_ch) +{ + unsigned int val, mask, nbytes; + + /* eCPRI "One-Way delay measurement" message to match: + *   byte 12: 0xAE     + *   byte 13: 0xFE     + *   byte 15: 0x05     + *   byte 19: 0x00 or 0x01 + */ + for (nbytes = 0; nbytes < 96; nbytes += 4) { + if (nbytes == 12) { + val = 0x0500FEAE; + mask = 0x00FF0000; + } else if (nbytes == 16) { + val = 0; + mask = 0x01FFFFFF; + } else { + val = 0; + mask = 0xFFFFFFFF; + } + + iowrite32(val, ndma_ch->ctrl_base + NDMA_RX_GEN_FILTER0_REG_OFFSET + + NDMA_RX_CYCLE0_LOWER_VALUE_REG_OFFSET + nbytes); + iowrite32(mask, ndma_ch->ctrl_base + NDMA_RX_GEN_FILTER0_REG_OFFSET + + NDMA_RX_CYCLE0_LOWER_MASK_REG_OFFSET + nbytes); + } +} + +static void adrv906x_ndma_enable_rx_filter(struct adrv906x_ndma_dev *ndma_dev, + unsigned int filter_en_mask, + enum adrv906x_ndma_rx_filter_status status) +{ + struct adrv906x_ndma_chan *rx_chan = &ndma_dev->rx_chan; + unsigned int val; + + val = ioread32(rx_chan->ctrl_base + NDMA_RX_SPLANE_FILTER_EN); + + if (status == NDMA_RX_FILTER_ON) + val |= filter_en_mask; + else + val &= ~filter_en_mask; + + iowrite32(val, rx_chan->ctrl_base + NDMA_RX_SPLANE_FILTER_EN); +} + +static void adrv906x_ndma_config_rx_filter(struct adrv906x_ndma_dev *ndma_dev) +{ + struct adrv906x_ndma_chan *rx_chan = &ndma_dev->rx_chan; + unsigned int en_mask; + + adrv906x_ndma_config_rx_ipv4_filter(rx_chan); + adrv906x_ndma_config_rx_ipv6_filter(rx_chan); + adrv906x_ndma_config_rx_eth_filter(rx_chan); + adrv906x_ndma_config_rx_ecpri_filter(rx_chan); + adrv906x_ndma_config_rx_splane_filter(rx_chan); + + en_mask = BIT(NDMA_RX_IPV4_FILTER) + | BIT(NDMA_RX_IPV6_FILTER) + | BIT(NDMA_RX_ETH_FILTER) + | BIT(NDMA_RX_GENERIC_FILTER_0); + + adrv906x_ndma_enable_rx_filter(ndma_dev, en_mask, NDMA_RX_FILTER_ON); +} + +static int adrv906x_ndma_refill_rx(struct adrv906x_ndma_chan *ndma_ch, int budget) +{ + struct adrv906x_ndma_dev *ndma_dev = ndma_ch->parent; + struct device *dev = ndma_dev->dev; + dma_addr_t addr, offset; + struct sk_buff *skb; + int end_desc_idx, done = 0; + + /* Get the index of the end descriptor in the list */ + end_desc_idx = (ndma_ch->rx_head + NDMA_RX_RING_SIZE - 1) % NDMA_RX_RING_SIZE; + + while (ndma_ch->rx_free < NDMA_RX_RING_SIZE) { + skb = napi_alloc_skb(&ndma_ch->napi, NDMA_RX_WU_BUF_SIZE); + if (!skb) + break; + + /* Adjust the buffer alignment to 32 bytes */ + offset = (32 - ((dma_addr_t)skb->data & 0x1F)) & 0x1F; + skb_reserve(skb, offset); + /* Mark an empty buffer by setting the first byte of the WU header to 0 */ + skb->data[0] = 0; + + addr = dma_map_single(dev, skb->data, NDMA_RX_WU_BUF_SIZE, DMA_FROM_DEVICE); + + if (unlikely(dma_mapping_error(dev, addr))) { + napi_consume_skb(skb, budget); + break; + } + + if (unlikely(addr & 0x1F)) { + dma_unmap_single(dev, addr, NDMA_RX_WU_BUF_SIZE, DMA_FROM_DEVICE); + napi_consume_skb(skb, budget); + break; + } + + ndma_ch->rx_buffs[ndma_ch->rx_head] = skb; + ndma_ch->rx_ring[ndma_ch->rx_head].start = addr; + ndma_ch->rx_ring[ndma_ch->rx_head].cfg |= DMAEN | DMAFLOW_LIST; + ndma_ch->rx_head = (ndma_ch->rx_head + 1) % NDMA_RX_RING_SIZE; + ndma_ch->rx_free++; + done++; + } + + if (done) { + /* Clear previously set end of the descriptor list */ + ndma_ch->rx_ring[end_desc_idx].cfg |= DMAFLOW_LIST; + /* Set new end of the descriptor list */ + ndma_ch->rx_ring[(end_desc_idx + done) % NDMA_RX_RING_SIZE].cfg &= ~DMAFLOW_LIST; + } + + return done; +} + +static int adrv906x_ndma_init_irqs(struct device_node *node, struct adrv906x_ndma_dev *ndma_dev) +{ + struct device *dev = ndma_dev->dev; + struct adrv906x_ndma_chan *rx_chan = &ndma_dev->rx_chan; + struct adrv906x_ndma_chan *tx_chan = &ndma_dev->tx_chan; + int irq, ret; + + /* Init for TX */ + irq = of_irq_get_byname(node, "tx_data_dma_done"); + if (irq <= 0) { + dev_err(dev, "failed to get tx_data_dma_done interrupt"); + return irq ? irq : -ENOENT; + } + tx_chan->tx_dma_done_irq = irq; + + irq = of_irq_get_byname(node, "tx_data_dma_error"); + if (irq <= 0) { + dev_err(dev, "failed to get tx_data_dma_error interrupt"); + return irq ? irq : -ENOENT; + } + tx_chan->tx_dma_error_irq = irq; + + ret = devm_request_threaded_irq(dev, tx_chan->tx_dma_error_irq, + adrv906x_dma_error_irq_handler, + adrv906x_dma_error_irq_handler_thread, + IRQF_ONESHOT, dev_name(dev), tx_chan); + if (ret) { + dev_err(dev, "failed to request tx_dma_error_irq interrupt"); + return ret; + } + + irq = of_irq_get_byname(node, "tx_status_dma_done"); + if (irq <= 0) { + dev_err(dev, "failed to get tx_status_dma_done interrupt"); + return irq ? irq : -ENOENT; + } + tx_chan->rx_dma_done_irq = irq; + + irq = of_irq_get_byname(node, "tx_status_dma_error"); + if (irq <= 0) { + dev_err(dev, "failed to get tx_status_dma_error interrupt"); + return irq ? irq : -ENOENT; + } + tx_chan->rx_dma_error_irq = irq; + + ret = devm_request_irq(dev, tx_chan->rx_dma_done_irq, + adrv906x_dma_rx_done_irq_handler, 0, dev_name(dev), tx_chan); + if (ret) { + dev_err(dev, "failed to request tx_status_dma_done interrupt"); + return ret; + } + + ret = devm_request_threaded_irq(dev, tx_chan->rx_dma_error_irq, + adrv906x_dma_error_irq_handler, + adrv906x_dma_error_irq_handler_thread, + IRQF_ONESHOT, dev_name(dev), tx_chan); + if (ret) { + dev_err(dev, "failed to request tx_status_dma_error interrupt"); + return ret; + } + + /* Init for RX */ + irq = of_irq_get_byname(node, "rx_dma_done"); + if (irq <= 0) { + dev_err(dev, "failed to get rx_dma_done interrupt"); + return irq ? irq : -ENOENT; + } + rx_chan->rx_dma_done_irq = irq; + + irq = of_irq_get_byname(node, "rx_dma_error"); + if (irq <= 0) { + dev_err(dev, "failed to get rx_dma_error interrupt"); + return irq ? irq : -ENOENT; + } + rx_chan->rx_dma_error_irq = irq; + + ret = devm_request_irq(dev, rx_chan->rx_dma_done_irq, + adrv906x_dma_rx_done_irq_handler, 0, dev_name(dev), rx_chan); + if (ret) { + dev_err(dev, "failed to request rx_dma_done interrupt"); + return ret; + } + + ret = devm_request_threaded_irq(dev, rx_chan->rx_dma_error_irq, + adrv906x_dma_error_irq_handler, + adrv906x_dma_error_irq_handler_thread, + IRQF_ONESHOT, dev_name(dev), rx_chan); + if (ret) { + dev_err(dev, "failed to request rx_dma_error interrupt"); + return ret; + } + + return 0; +} + +static int adrv906x_ndma_get_reset_ctrl(struct adrv906x_ndma_dev *ndma_dev, + struct device_node *ndma_np) +{ + struct device *dev = ndma_dev->dev; + struct adrv906x_ndma_reset *reset = &ndma_dev->reset; + struct device_node *reset_np; + unsigned int ret, reg, len; + + reset_np = of_parse_phandle(ndma_np, "reset-ctrl", 0); + if (!reset_np) { + dev_err(dev, "missing reset-ctrl property"); + return -ENODEV; + } + ret = of_property_read_u32_index(reset_np, "reg", 0, ®); + if (ret) { + dev_err(dev, "missing reg property of reset-ctrl node"); + return -EINVAL; + } + ret = of_property_read_u32_index(reset_np, "reg", 1, &len); + if (ret) { + dev_err(dev, "missing reg length of reset-ctrl node"); + return -EINVAL; + } + reset->reg = devm_ioremap(dev->parent, reg, len); + if (!reset->reg) { + dev_err(dev, "ioremap ndma-rst failed!"); + return -EINVAL; + } + + if (ndma_dev->dev_num == 0) { + reset->rx_chan_reset_bit = NDMA_RX0_RST; + reset->tx_chan_reset_bit = NDMA_TX0_RST; + } else { + reset->rx_chan_reset_bit = NDMA_RX1_RST; + reset->tx_chan_reset_bit = NDMA_TX1_RST; + } + return 0; +} + +static int adrv906x_ndma_get_intr_ctrl(struct adrv906x_ndma_dev *ndma_dev, + struct device_node *ndma_np) +{ + struct device *dev = ndma_dev->dev; + unsigned int reg, len, ret; + struct device_node *intr_ctrl; + + intr_ctrl = of_parse_phandle(ndma_np, "interrupt-ctrl", 0); + if (!intr_ctrl) { + dev_err(dev, "missing interrupt-ctrl property"); + return -ENODEV; + } + ret = of_property_read_u32_index(intr_ctrl, "reg", 0, ®); + if (ret) { + dev_err(dev, "missing interrupt-ctrl node reg addr"); + return -EINVAL; + } + ret = of_property_read_u32_index(intr_ctrl, "reg", 1, &len); + if (ret) { + dev_err(dev, "missing interrupt-ctrl node reg length"); + return -EINVAL; + } + ndma_dev->intr_ctrl = devm_ioremap(dev->parent, reg, len); + return 0; +} + +void adrv906x_ndma_update_frame_drop_stats(struct adrv906x_ndma_dev *ndma_dev) +{ + struct adrv906x_ndma_chan *rx_chan = &ndma_dev->rx_chan; + union adrv906x_ndma_chan_stats *stats = &rx_chan->stats; + u32 count; + + count = ioread32(rx_chan->ctrl_base + NDMA_RX_FRAME_DROPPED_COUNT_SPLANE); + if (count < (u32)stats->rx.frame_dropped_splane_errors) + stats->rx.frame_dropped_splane_errors += 0x100000000ULL; + stats->rx.frame_dropped_splane_errors &= 0xffffffff00000000ULL; + stats->rx.frame_dropped_splane_errors |= count; + + count = ioread32(rx_chan->ctrl_base + NDMA_RX_FRAME_DROPPED_COUNT_MPLANE); + if (count < (u32)stats->rx.frame_dropped_mplane_errors) + stats->rx.frame_dropped_mplane_errors += 0x100000000ULL; + stats->rx.frame_dropped_mplane_errors &= 0xffffffff00000000ULL; + stats->rx.frame_dropped_mplane_errors |= count; + + stats->rx.frame_dropped_errors = stats->rx.frame_dropped_splane_errors + + stats->rx.frame_dropped_mplane_errors; +} + +static void adrv906x_ndma_get_frame_drop_stats(struct work_struct *work) +{ + struct adrv906x_ndma_dev *ndma_dev = + container_of(work, struct adrv906x_ndma_dev, update_stats.work); + + rtnl_lock(); + adrv906x_ndma_update_frame_drop_stats(ndma_dev); + rtnl_unlock(); + + mod_delayed_work(system_long_wq, &ndma_dev->update_stats, msecs_to_jiffies(1000 * 60)); +} + +static void adrv906x_dma_tx_prep_desc_list(struct adrv906x_ndma_chan *ndma_ch) +{ + struct adrv906x_ndma_dev *ndma_dev = ndma_ch->parent; + struct dma_desc *tx_ring = ndma_ch->tx_ring; + unsigned int desc_indx = ndma_ch->tx_tail; + unsigned int num_of_desc; + + num_of_desc = ndma_dev->loopback_en ? + 2 * ndma_ch->tx_frames_waiting : ndma_ch->tx_frames_waiting; + while (num_of_desc) { + tx_ring[desc_indx].cfg |= DMAFLOW_LIST; + desc_indx = (desc_indx + 1) % NDMA_TX_RING_SIZE; + num_of_desc--; + } + + ndma_ch->tx_frames_pending = ndma_ch->tx_frames_waiting; + ndma_ch->tx_frames_waiting = 0; + desc_indx = (desc_indx) ? desc_indx - 1 : NDMA_TX_RING_SIZE - 1; + tx_ring[desc_indx].cfg &= ~DMAFLOW_LIST; +} + +static int adrv906x_ndma_device_init(struct adrv906x_ndma_dev *ndma_dev, struct device_node *np) +{ + struct device *dev = ndma_dev->dev; + struct adrv906x_ndma_chan *rx_chan = &ndma_dev->rx_chan; + struct adrv906x_ndma_chan *tx_chan = &ndma_dev->tx_chan; + unsigned int reg, len; + int ret; + + /* Config TX */ + ret = of_property_read_u32_index(np, "reg", 0, ®); + if (ret) + return ret; + ret = of_property_read_u32_index(np, "reg", 1, &len); + if (ret) + return ret; + tx_chan->ctrl_base = devm_ioremap(dev->parent, reg, len); + ret = of_property_read_u32_index(np, "reg", 2, ®); + if (ret) + return ret; + ret = of_property_read_u32_index(np, "reg", 3, &len); + if (ret) + return ret; + tx_chan->tx_dma_base = devm_ioremap(dev->parent, reg, len); + ret = of_property_read_u32_index(np, "reg", 4, ®); + if (ret) + return ret; + ret = of_property_read_u32_index(np, "reg", 5, &len); + if (ret) + return ret; + tx_chan->rx_dma_base = devm_ioremap(dev->parent, reg, len); + tx_chan->chan_type = NDMA_TX_CHANNEL; + tx_chan->parent = ndma_dev; + + /* Config RX */ + ret = of_property_read_u32_index(np, "reg", 6, ®); + if (ret) + return ret; + ret = of_property_read_u32_index(np, "reg", 7, &len); + if (ret) + return ret; + rx_chan->ctrl_base = devm_ioremap(dev->parent, reg, len); + ret = of_property_read_u32_index(np, "reg", 8, ®); + if (ret) + return ret; + ret = of_property_read_u32_index(np, "reg", 9, &len); + if (ret) + return ret; + rx_chan->rx_dma_base = devm_ioremap(dev->parent, reg, len); + rx_chan->chan_type = NDMA_RX_CHANNEL; + rx_chan->parent = ndma_dev; + + ret = adrv906x_ndma_init_irqs(np, ndma_dev); + if (ret) + return ret; + + spin_lock_init(&ndma_dev->lock); + spin_lock_init(&tx_chan->lock); + spin_lock_init(&rx_chan->lock); + + INIT_DELAYED_WORK(&ndma_dev->update_stats, adrv906x_ndma_get_frame_drop_stats); + + return ret; +} + +static void adrv906x_ndma_enable_events(struct adrv906x_ndma_chan *ndma_ch, unsigned int events) +{ + unsigned int val, offset; + + offset = (ndma_ch->chan_type == NDMA_RX_CHANNEL) ? NDMA_RX_EVENT_EN : NDMA_TX_EVENT_EN; + + val = ioread32(ndma_ch->ctrl_base + offset); + val |= events; + iowrite32(val, ndma_ch->ctrl_base + offset); +} + +static void adrv906x_ndma_disable_all_event(struct adrv906x_ndma_chan *ndma_ch) +{ + unsigned int offset; + + offset = (ndma_ch->chan_type == NDMA_RX_CHANNEL) ? NDMA_RX_EVENT_EN : NDMA_TX_EVENT_EN; + + iowrite32(0, ndma_ch->ctrl_base + offset); +} + +static void adrv906x_ndma_add_tx_header(struct adrv906x_ndma_dev *ndma_dev, struct sk_buff *skb, + unsigned char seq_num, unsigned char port, + bool hw_tstamp_en, bool dsa_en) +{ + unsigned int frame_len; + unsigned char *hdr; + + frame_len = ndma_dev->loopback_en ? skb->len + NDMA_TX_HDR_LOOPBACK_SIZE : skb->len; + hdr = skb_push(skb, NDMA_TX_HDR_SOF_SIZE); + + hdr[0] = FIELD_PREP(NDMA_HDR_TYPE_MASK, NDMA_TX_HDR_TYPE_SOF) + | FIELD_PREP(NDMA_TX_HDR_SOF_FR_PTP, hw_tstamp_en) + | FIELD_PREP(NDMA_TX_HDR_SOF_PORT_ID, port) + | FIELD_PREP(NDMA_TX_HDR_SOF_DSA_EN, dsa_en); + hdr[1] = seq_num; + hdr[2] = frame_len & 0xff; + hdr[3] = (frame_len >> 8) & 0xff; + hdr[4] = 0; + hdr[5] = 0; + hdr[6] = 0; + hdr[7] = 0; +} + +static void adrv906x_ndma_reset(struct adrv906x_ndma_dev *ndma_dev) +{ + struct adrv906x_ndma_reset *reset = &ndma_dev->reset; + struct adrv906x_ndma_chan *rx_chan = &ndma_dev->rx_chan; + struct adrv906x_ndma_chan *tx_chan = &ndma_dev->tx_chan; + unsigned int val, reset_bits; + unsigned long flags; + + spin_lock_irqsave(&ndma_reset_lock, flags); + reset_bits = reset->rx_chan_reset_bit | reset->tx_chan_reset_bit; + val = ioread32(reset->reg); + iowrite32(val & ~reset_bits, reset->reg); + iowrite32(val | reset_bits, reset->reg); + spin_unlock_irqrestore(&ndma_reset_lock, flags); + + spin_lock_irqsave(&rx_chan->lock, flags); + rx_chan->expected_seq_num = 0; + spin_unlock_irqrestore(&rx_chan->lock, flags); + + spin_lock_irqsave(&tx_chan->lock, flags); + ndma_dev->tx_chan.expected_seq_num = 1; + ndma_dev->tx_chan.seq_num = 1; + spin_unlock_irqrestore(&tx_chan->lock, flags); +} + +static int adrv906x_ndma_alloc_rings(struct adrv906x_ndma_dev *ndma_dev) +{ + struct adrv906x_ndma_chan *rx_chan = &ndma_dev->rx_chan; + struct adrv906x_ndma_chan *tx_chan = &ndma_dev->tx_chan; + struct device *dev = ndma_dev->dev; + void *tx_status_buffs; + dma_addr_t addr; + int i; + + /* Allocate and initialize DMA descriptor ring for RX data & status */ + rx_chan->rx_ring = dmam_alloc_coherent(dev, + sizeof(struct dma_desc) * NDMA_RX_RING_SIZE, + &rx_chan->rx_ring_dma, GFP_KERNEL); + if (!rx_chan->rx_ring) + return -ENOMEM; + + for (i = 0; i < NDMA_RX_RING_SIZE; i++) { + rx_chan->rx_ring[i].cfg = (DESCIDCPY | DI_EN_X | NDSIZE_4 | DMASYNC | + WDSIZE_256 | PSIZE_64 | WNR | DMAEN); + rx_chan->rx_ring[i].cfg |= + (i == NDMA_RX_RING_SIZE - 1) ? DMAFLOW_STOP : DMAFLOW_LIST; + rx_chan->rx_ring[i].xcnt = NDMA_RX_WU_BUF_SIZE / XMODE_256; + rx_chan->rx_ring[i].xmod = XMODE_256; + rx_chan->rx_ring[i].next = rx_chan->rx_ring_dma + + sizeof(struct dma_desc) * ((i + 1) % NDMA_RX_RING_SIZE); + } + + /* Allocate and initialize DMA descriptor ring for TX data */ + tx_chan->tx_ring = dmam_alloc_coherent(dev, + sizeof(struct dma_desc) * NDMA_TX_RING_SIZE, + &tx_chan->tx_ring_dma, GFP_KERNEL); + if (!tx_chan->tx_ring) + return -ENOMEM; + + for (i = 0; i < NDMA_TX_RING_SIZE; i++) { + tx_chan->tx_ring[i].cfg = (DESCIDCPY | NDSIZE_4 | DMASYNC | PSIZE_64 | DMAEN); + tx_chan->tx_ring[i].next = tx_chan->tx_ring_dma + + sizeof(struct dma_desc) * ((i + 1) % NDMA_TX_RING_SIZE); + } + + /* Allocate and initialize DMA descriptor ring and buffers for TX status */ + tx_chan->rx_ring = dmam_alloc_coherent(dev, + sizeof(struct dma_desc) * NDMA_TX_RING_SIZE, + &tx_chan->rx_ring_dma, GFP_KERNEL); + if (!tx_chan->rx_ring) + return -ENOMEM; + + tx_status_buffs = dmam_alloc_coherent(dev, NDMA_TX_HDR_STATUS_SIZE * NDMA_TX_RING_SIZE, + &addr, GFP_KERNEL); + if (!tx_status_buffs) + return -ENOMEM; + + for (i = 0; i < NDMA_TX_RING_SIZE; i++) { + tx_chan->rx_buffs[i] = tx_status_buffs + i * NDMA_TX_HDR_STATUS_SIZE; + + tx_chan->rx_ring[i].cfg = (DESCIDCPY | DI_EN_X | NDSIZE_4 | DMASYNC | + WDSIZE_64 | PSIZE_32 | WNR | DMAEN | DMAFLOW_LIST); + tx_chan->rx_ring[i].xcnt = NDMA_TX_HDR_STATUS_SIZE / XMODE_64; + tx_chan->rx_ring[i].xmod = XMODE_64; + tx_chan->rx_ring[i].next = tx_chan->rx_ring_dma + + sizeof(struct dma_desc) * ((i + 1) % NDMA_TX_RING_SIZE); + tx_chan->rx_ring[i].start = addr + i * NDMA_TX_HDR_STATUS_SIZE; + } + + return 0; +} + +static void adrv906x_ndma_config_loopback(struct adrv906x_ndma_dev *ndma_dev, bool enable) +{ + struct adrv906x_ndma_chan *rx_chan = &ndma_dev->rx_chan; + struct adrv906x_ndma_chan *tx_chan = &ndma_dev->tx_chan; + unsigned int val; + + if (ndma_dev->loopback_en == enable) + return; + + val = ioread32(rx_chan->ctrl_base + NDMA_RX_STAT_AND_CTRL); + if (enable) { + val |= NDMA_LOOPBACK_EN; + ndma_dev->loopback_en = true; + tx_chan->tx_loopback_wu[0] = NDMA_TX_HDR_TYPE_LOOPBACK; + tx_chan->tx_loopback_addr = dma_map_single(ndma_dev->dev, tx_chan->tx_loopback_wu, + NDMA_TX_HDR_LOOPBACK_SIZE, + DMA_TO_DEVICE); + tx_chan->tx_loopback_desc.cfg = (DESCIDCPY | DI_EN_X | NDSIZE_4 | + WDSIZE_8 | PSIZE_32 | DMAEN); + tx_chan->tx_loopback_desc.xcnt = NDMA_TX_HDR_LOOPBACK_SIZE / XMODE_8; + tx_chan->tx_loopback_desc.xmod = XMODE_8; + tx_chan->tx_loopback_desc.start = tx_chan->tx_loopback_addr; + } else { + val &= ~NDMA_LOOPBACK_EN; + ndma_dev->loopback_en = false; + + dma_unmap_single(ndma_dev->dev, tx_chan->tx_loopback_addr, + NDMA_TX_HDR_LOOPBACK_SIZE, DMA_TO_DEVICE); + } + iowrite32(val, rx_chan->ctrl_base + NDMA_RX_STAT_AND_CTRL); +} + +void adrv906x_ndma_open(struct adrv906x_ndma_dev *ndma_dev, ndma_callback tx_cb_fn, + ndma_callback rx_cb_fn, void *cb_param, bool loopback_mode) +{ + struct adrv906x_ndma_chan *rx_chan = &ndma_dev->rx_chan; + struct adrv906x_ndma_chan *tx_chan = &ndma_dev->tx_chan; + unsigned long flags0, flags1; + + spin_lock_irqsave(&ndma_dev->lock, flags0); + if (!ndma_dev->enabled) { + adrv906x_ndma_disable_all_irqs(ndma_dev); + adrv906x_ndma_enable_events(rx_chan, NDMA_RX_ERROR_EVENTS); + adrv906x_ndma_enable_events(tx_chan, NDMA_TX_ERROR_EVENTS); + + memset(&rx_chan->stats, 0, sizeof(union adrv906x_ndma_chan_stats)); + memset(&tx_chan->stats, 0, sizeof(union adrv906x_ndma_chan_stats)); + + adrv906x_ndma_config_rx_filter(ndma_dev); + adrv906x_ndma_set_frame_size(ndma_dev); + adrv906x_ndma_set_tx_timeout_value(ndma_dev, NDMA_TS_TX_DELAY); + adrv906x_ndma_config_loopback(ndma_dev, loopback_mode); + + spin_lock_irqsave(&tx_chan->lock, flags1); + adrv906x_dma_rx_reset(tx_chan); + adrv906x_dma_tx_reset(tx_chan); + adrv906x_ndma_chan_enable(tx_chan); + spin_unlock_irqrestore(&tx_chan->lock, flags1); + + spin_lock_irqsave(&rx_chan->lock, flags1); + adrv906x_dma_rx_reset(rx_chan); + adrv906x_dma_tx_reset(rx_chan); + adrv906x_ndma_chan_enable(rx_chan); + spin_unlock_irqrestore(&rx_chan->lock, flags1); + + tx_chan->status_cb_fn = tx_cb_fn; + tx_chan->status_cb_param = cb_param; + tx_chan->rx_head = 0; + tx_chan->rx_tail = 0; + tx_chan->rx_free = 0; + tx_chan->tx_head = 0; + tx_chan->tx_tail = 0; + tx_chan->tx_frames_waiting = 0; + tx_chan->tx_frames_pending = 0; + adrv906x_dma_rx_start(tx_chan); + napi_enable(&tx_chan->napi); + + rx_chan->status_cb_fn = rx_cb_fn; + rx_chan->status_cb_param = cb_param; + rx_chan->rx_head = 0; + rx_chan->rx_tail = 0; + rx_chan->rx_free = 0; + adrv906x_ndma_refill_rx(rx_chan, NDMA_RX_RING_SIZE); + adrv906x_dma_rx_start(rx_chan); + napi_enable(&rx_chan->napi); + + adrv906x_ndma_enable_irqs(ndma_dev, + NDMA_RX_DMA_ERR_IRQ | + NDMA_RX_DMA_DONE_IRQ | + NDMA_TX_DATA_DMA_ERR_IRQ | + NDMA_TX_STATUS_DMA_ERR_IRQ | + NDMA_TX_STATUS_DMA_DONE_IRQ); + + ndma_dev->enabled = true; + kref_init(&ndma_dev->refcount); + } else { + kref_get(&ndma_dev->refcount); + } + + mod_delayed_work(system_long_wq, &ndma_dev->update_stats, msecs_to_jiffies(1000)); + spin_unlock_irqrestore(&ndma_dev->lock, flags0); +} + +static void adrv906x_ndma_rx_free_data_wu_list(struct list_head *data_wu_list, int budget) +{ + struct sk_buff *skb, *next; + + list_for_each_entry_safe(skb, next, data_wu_list, list) { + skb_list_del_init(skb); + napi_consume_skb(skb, budget); + } +} + +static void adrv906x_ndma_stop(struct kref *ref) +{ + struct adrv906x_ndma_dev *ndma_dev = container_of(ref, struct adrv906x_ndma_dev, refcount); + struct adrv906x_ndma_chan *rx_chan = &ndma_dev->rx_chan; + struct adrv906x_ndma_chan *tx_chan = &ndma_dev->tx_chan; + struct device *dev = ndma_dev->dev; + struct timespec64 ts = { 0, 0 }; + struct sk_buff *skb; + dma_addr_t addr; + unsigned long flags; + unsigned int size, num_frames; + unsigned char port; + + spin_lock_irqsave(&ndma_dev->lock, flags); + adrv906x_ndma_disable_all_irqs(ndma_dev); + cancel_delayed_work(&ndma_dev->update_stats); + spin_unlock_irqrestore(&ndma_dev->lock, flags); + + /* Disable ndma RX channel */ + napi_disable(&rx_chan->napi); + spin_lock_irqsave(&rx_chan->lock, flags); + adrv906x_ndma_disable_all_event(rx_chan); + adrv906x_ndma_chan_disable(rx_chan); + + adrv906x_ndma_rx_free_data_wu_list(&rx_chan->rx_data_wu_list, 0); + while (rx_chan->rx_free) { + skb = (struct sk_buff *)rx_chan->rx_buffs[rx_chan->rx_tail]; + addr = rx_chan->rx_ring[rx_chan->rx_tail].start; + + dma_unmap_single(dev, addr, NDMA_RX_WU_BUF_SIZE, DMA_FROM_DEVICE); + dev_kfree_skb(skb); + + rx_chan->rx_tail = (rx_chan->rx_tail + 1) % NDMA_RX_RING_SIZE; + rx_chan->rx_free--; + } + spin_unlock_irqrestore(&rx_chan->lock, flags); + + /* Disable ndma TX channel */ + napi_disable(&tx_chan->napi); + spin_lock_irqsave(&tx_chan->lock, flags); + adrv906x_ndma_disable_all_event(tx_chan); + adrv906x_ndma_chan_disable(tx_chan); + + num_frames = tx_chan->tx_frames_waiting + tx_chan->tx_frames_pending; + while (num_frames--) { + skb = tx_chan->tx_buffs[tx_chan->tx_tail]; + port = FIELD_GET(NDMA_TX_HDR_SOF_PORT_ID, skb->data[0]); + addr = tx_chan->tx_ring[tx_chan->tx_tail].start; + size = tx_chan->tx_ring[tx_chan->tx_tail].xcnt * + tx_chan->tx_ring[tx_chan->tx_tail].xmod; + dma_unmap_single(dev, addr, size, DMA_TO_DEVICE); + + tx_chan->tx_buffs[tx_chan->tx_tail] = NULL; + tx_chan->tx_tail = (tx_chan->tx_tail + 1) % NDMA_TX_RING_SIZE; + tx_chan->status_cb_fn(skb, port, ts, tx_chan->status_cb_param); + } + tx_chan->tx_frames_pending = 0; + tx_chan->tx_frames_waiting = 0; + spin_unlock_irqrestore(&tx_chan->lock, flags); + + adrv906x_ndma_reset(ndma_dev); + ndma_dev->enabled = false; +} + +void adrv906x_ndma_close(struct adrv906x_ndma_dev *ndma_dev) +{ + kref_put(&ndma_dev->refcount, adrv906x_ndma_stop); +} + +static int adrv906x_ndma_parse_rx_status_header(struct adrv906x_ndma_chan *ndma_ch, + unsigned char *status_hdr, + struct timespec64 *ts, unsigned int *port_id, + unsigned int *frame_size) +{ + struct adrv906x_ndma_dev *ndma_dev = ndma_ch->parent; + union adrv906x_ndma_chan_stats *stats = &ndma_ch->stats; + struct device *dev = ndma_dev->dev; + int ret = NDMA_NO_ERROR; + unsigned int error; + + get_ts_from_status(status_hdr, ts); + *port_id = FIELD_GET(NDMA_RX_HDR_STATUS_PORT_ID, status_hdr[0]); + *frame_size = (status_hdr[NDMA_RX_FRAME_LEN_MSB] << 8) | + (status_hdr[NDMA_RX_FRAME_LEN_LSB]); + + if (NDMA_RX_HDR_STATUS_FR_ERR & status_hdr[0]) { + error = ioread32(ndma_ch->ctrl_base + NDMA_RX_EVENT_STAT) & NDMA_RX_ERROR_EVENTS; + + /* Get error type from IRQ status register and update statistics. + * Note: More than one error bit in IRQ status register can be set, + * so to avoid losing them, we clear only one error bit at a time. + */ + if (NDMA_RX_FRAME_SIZE_ERR_EVENT & error) { + dev_dbg(dev, "frame size error"); + stats->rx.frame_size_errors++; + if (NDMA_RX_HDR_STATUS_FR_DROP_ERR & status_hdr[0]) + dev_dbg(dev, "partial frame dropped error"); + iowrite32(NDMA_RX_FRAME_SIZE_ERR_EVENT, + ndma_ch->ctrl_base + NDMA_RX_EVENT_STAT); + ret = NDMA_RX_FRAME_SIZE_ERR_EVENT; + } else if (NDMA_RX_ERR_EVENT & error) { + dev_dbg(dev, "mac error(s) signaled by tuser[0]"); + stats->rx.frame_errors++; + iowrite32(NDMA_RX_ERR_EVENT, ndma_ch->ctrl_base + NDMA_RX_EVENT_STAT); + ret = NDMA_RX_ERR_EVENT; + } else if (NDMA_RX_FRAME_DROPPED_ERR_EVENT & error) { + dev_dbg(dev, "frame dropped error"); + iowrite32(NDMA_RX_FRAME_DROPPED_ERR_EVENT, + ndma_ch->ctrl_base + NDMA_RX_EVENT_STAT); + ret = NDMA_RX_FRAME_DROPPED_ERR_EVENT; + } else { + dev_dbg(dev, "status wu has error flag set but no interrupt generated"); + stats->rx.unknown_errors++; + ret = NDMA_RX_UNKNOWN_ERROR; + } + } else { + /* If no error, check and update sequence number */ + if (status_hdr[1] != ndma_ch->expected_seq_num) { + dev_dbg(dev, "frame seq number mismatch, exp:0x%x recv:0x%x", + ndma_ch->expected_seq_num, status_hdr[1]); + stats->rx.seqnumb_mismatch_errors++; + ndma_ch->expected_seq_num = status_hdr[1]; + ret = NDMA_RX_SEQNUM_MISMATCH_ERROR; + } + ndma_ch->expected_seq_num++; + } + + return ret; +} + +static int adrv906x_ndma_rx_validate_data_wu_list(struct list_head *data_wu_list, + int frame_size) +{ + int n_elem_exp, n_elem = 0; + struct list_head *pos; + + n_elem_exp = DIV_ROUND_UP(frame_size, NDMA_RX_PKT_BUF_SIZE); + + list_for_each(pos, data_wu_list) { + n_elem++; + } + + return (n_elem_exp != n_elem) ? -EINVAL : 0; +} + +static struct sk_buff *adrv906x_ndma_rx_build_linear_pkt_buf(struct list_head *data_wu_list, + unsigned int frame_size) +{ + struct sk_buff *frag, *skb = NULL; + struct list_head *pos; + unsigned int length; + + if (frame_size > NDMA_MAX_FRAME_SIZE_VALUE) + goto out; + + if (adrv906x_ndma_rx_validate_data_wu_list(data_wu_list, frame_size)) + goto out; + + /* If the data work unit list contains only one entry, we avoid allocating a new buffer. + * Instead, we hold a reference to the existing buffer to prevent it from being freed + * by adrv906x_ndma_rx_free_data_wu_list(). + */ + if (list_is_singular(data_wu_list)) { + skb = list_first_entry(data_wu_list, struct sk_buff, list); + skb_put(skb, NDMA_RX_HDR_DATA_SIZE + frame_size); + skb_pull(skb, NDMA_RX_HDR_DATA_SIZE); + skb_get(skb); + } else { + skb = alloc_skb(frame_size, GFP_ATOMIC); + if (!skb) + goto out; + list_for_each(pos, data_wu_list) { + frag = list_entry(pos, struct sk_buff, list); + length = (frame_size > NDMA_RX_PKT_BUF_SIZE) ? + NDMA_RX_PKT_BUF_SIZE : frame_size; + frame_size -= length; + skb_put(frag, NDMA_RX_HDR_DATA_SIZE + length); + skb_pull(frag, NDMA_RX_HDR_DATA_SIZE); + skb_copy_from_linear_data(frag, skb_tail_pointer(skb), length); + skb_put(skb, length); + } + } +out: + return skb; +} + +static void adrv906x_ndma_process_rx_work_unit(struct adrv906x_ndma_chan *ndma_ch, + struct sk_buff *skb, int budget) +{ + union adrv906x_ndma_chan_stats *stats = &ndma_ch->stats; + struct adrv906x_ndma_dev *ndma_dev = ndma_ch->parent; + unsigned int port_id = 0, frame_size = 0, hdr_type; + struct device *dev = ndma_dev->dev; + struct timespec64 ts = { 0, 0 }; + struct sk_buff *pktbuf; + int ret; + + hdr_type = FIELD_GET(NDMA_HDR_TYPE_MASK, skb->data[0]); + + switch (hdr_type) { + case NDMA_RX_HDR_TYPE_STATUS: + if (list_empty(&ndma_ch->rx_data_wu_list)) { + dev_dbg(dev, "status received without preceding data work units"); + } else { + ret = adrv906x_ndma_parse_rx_status_header(ndma_ch, skb->data, &ts, + &port_id, &frame_size); + if (ret == NDMA_NO_ERROR || ret == NDMA_RX_SEQNUM_MISMATCH_ERROR || + unlikely(ndma_dev->loopback_en)) { + pktbuf = adrv906x_ndma_rx_build_linear_pkt_buf(&ndma_ch->rx_data_wu_list, + frame_size); + if (pktbuf) + ndma_ch->status_cb_fn(pktbuf, port_id, ts, + ndma_ch->status_cb_param); + } + } + adrv906x_ndma_rx_free_data_wu_list(&ndma_ch->rx_data_wu_list, budget); + napi_consume_skb(skb, budget); /* Free skb with status WU */ + break; + case NDMA_RX_HDR_TYPE_DATA: + if (FIELD_GET(NDMA_RX_HDR_TYPE_DATA_SOF, skb->data[0])) { + if (!list_empty(&ndma_ch->rx_data_wu_list)) { + dev_dbg(dev, "no status received for previous data work units"); + adrv906x_ndma_rx_free_data_wu_list(&ndma_ch->rx_data_wu_list, + budget); + } + list_add_tail(&skb->list, &ndma_ch->rx_data_wu_list); + } else { + if (list_empty(&ndma_ch->rx_data_wu_list)) { + dev_dbg(dev, "start of frame not detected"); + napi_consume_skb(skb, budget); + } else { + list_add_tail(&skb->list, &ndma_ch->rx_data_wu_list); + } + } + break; + default: + dev_dbg(dev, "incorrect wu header detected"); + adrv906x_ndma_rx_free_data_wu_list(&ndma_ch->rx_data_wu_list, budget); + napi_consume_skb(skb, budget); + stats->rx.wu_header_errors++; + } + + stats->rx.done_work_units++; +} + +static int adrv906x_ndma_parse_tx_status_header(struct adrv906x_ndma_chan *ndma_ch, + unsigned char *status_hdr, + struct timespec64 *ts) +{ + struct adrv906x_ndma_dev *ndma_dev = ndma_ch->parent; + union adrv906x_ndma_chan_stats *stats = &ndma_ch->stats; + struct device *dev = ndma_dev->dev; + int ret = NDMA_NO_ERROR; + unsigned int error; + + if (FIELD_GET(NDMA_HDR_TYPE_MASK, status_hdr[0]) != NDMA_TX_HDR_TYPE_STATUS) { + dev_dbg(dev, "incorrect format of wu status header: 0x%x", status_hdr[0]); + stats->tx.wu_status_header_errors++; + ret = NDMA_TX_STATUS_HEADER_ERROR; + } else { + get_ts_from_status(status_hdr, ts); + + if (status_hdr[1] != ndma_ch->expected_seq_num) { + dev_dbg(dev, "frame seq number mismatch, exp:0x%x recv:0x%x", + ndma_ch->expected_seq_num, status_hdr[1]); + stats->tx.seqnumb_mismatch_errors++; + /* If seq number mismatch, update it to new value */ + ndma_ch->expected_seq_num = status_hdr[1]; + ret = NDMA_TX_SEQNUM_MISMATCH_ERROR; + } + ndma_ch->expected_seq_num++; + if (ndma_ch->expected_seq_num == 0) + ndma_ch->expected_seq_num++; + + if (NDMA_TX_HDR_STATUS_FR_ERR & status_hdr[0]) { + if (adrv906x_ndma_chan_enabled(ndma_ch) && + is_timestamp_all_zero(status_hdr)) { + dev_dbg(dev, "hw timestamp timeout error"); + stats->tx.tstamp_timeout_errors++; + ret = NDMA_TX_TSTAMP_TIMEOUT_ERROR; + } else { + /* Get error type from IRQ status register and update statistics. + * Note: More than one error bit in IRQ status register can be set, + * so to avoid losing them, we clear only one error bit at a time. + */ + error = ioread32(ndma_ch->ctrl_base + NDMA_TX_EVENT_STAT) & + NDMA_TX_ERROR_EVENTS; + + if (NDMA_TX_FRAME_SIZE_ERR_EVENT & error) { + dev_dbg(dev, "frame size error"); + stats->tx.frame_size_errors++; + iowrite32(NDMA_TX_FRAME_SIZE_ERR_EVENT, ndma_ch->ctrl_base + + NDMA_TX_EVENT_STAT); + ret = NDMA_TX_FRAME_SIZE_ERROR; + } else if (NDMA_TX_WU_HEADER_ERR_EVENT & error) { + dev_dbg(dev, "incorrect format of wu data header"); + stats->tx.wu_data_header_errors++; + iowrite32(NDMA_TX_WU_HEADER_ERR_EVENT, ndma_ch->ctrl_base + + NDMA_TX_EVENT_STAT); + ret = NDMA_TX_DATA_HEADER_ERROR; + } else { + dev_dbg(dev, "unknown error: status register does not indicate an error"); + stats->tx.unknown_errors++; + ret = NDMA_RX_UNKNOWN_ERROR; + } + } + } + } + + stats->tx.pending_work_units = ndma_ch->tx_frames_pending; + stats->tx.done_work_units++; + + return ret; +} + +static void adrv906x_ndma_process_tx_status(struct adrv906x_ndma_chan *ndma_ch, + unsigned char *status) +{ + union adrv906x_ndma_chan_stats *stats = &ndma_ch->stats; + struct adrv906x_ndma_dev *ndma_dev = ndma_ch->parent; + struct device *dev = ndma_dev->dev; + struct dma_desc *tx_ring = ndma_ch->tx_ring; + struct timespec64 ts = { 0, 0 }; + struct sk_buff *skb; + dma_addr_t addr; + unsigned int size; + unsigned char port; + int ret; + + ret = adrv906x_ndma_parse_tx_status_header(ndma_ch, status, &ts); + /* ndma channel must be re-enabled after error occurrence */ + if (ret) + adrv906x_ndma_chan_enable(ndma_ch); + + skb = ndma_ch->tx_buffs[ndma_ch->tx_tail]; + port = FIELD_GET(NDMA_TX_HDR_SOF_PORT_ID, skb->data[0]); + addr = tx_ring[ndma_ch->tx_tail].start; + size = tx_ring[ndma_ch->tx_tail].xcnt * tx_ring[ndma_ch->tx_tail].xmod; + dma_unmap_single(dev, addr, size, DMA_TO_DEVICE); + + ndma_ch->tx_buffs[ndma_ch->tx_tail] = NULL; + ndma_ch->tx_tail = (ndma_ch->tx_tail + 1) % NDMA_TX_RING_SIZE; + ndma_ch->status_cb_fn(skb, port, ts, ndma_ch->status_cb_param); + ndma_ch->tx_frames_pending--; + + if (!ndma_ch->tx_frames_pending && ndma_ch->tx_frames_waiting) { + adrv906x_dma_tx_prep_desc_list(ndma_ch); + adrv906x_dma_tx_start(ndma_ch); + } + + stats->tx.pending_work_units = ndma_ch->tx_frames_pending; +} + +int adrv906x_ndma_start_xmit(struct adrv906x_ndma_dev *ndma_dev, struct sk_buff *skb, + unsigned char port, bool hw_tstamp_en, bool dsa_en) +{ + struct adrv906x_ndma_chan *ndma_ch = &ndma_dev->tx_chan; + union adrv906x_ndma_chan_stats *stats = &ndma_ch->stats; + struct device *dev = ndma_dev->dev; + unsigned int tx_frames_max_num; + unsigned long flags; + unsigned int size; + dma_addr_t addr; + u32 wdsize, xmod; + int ret = 0; + + spin_lock_irqsave(&ndma_ch->lock, flags); + tx_frames_max_num = ndma_dev->loopback_en ? NDMA_TX_RING_SIZE / 2 : NDMA_TX_RING_SIZE; + if (ndma_ch->tx_frames_waiting + ndma_ch->tx_frames_pending >= tx_frames_max_num) { + ret = -EBUSY; + goto out; + } + + if (skb->len < NDMA_TX_MIN_FRAME_SIZE_VALUE) + skb_put(skb, NDMA_TX_MIN_FRAME_SIZE_VALUE - skb->len); + + adrv906x_ndma_add_tx_header(ndma_dev, skb, ndma_ch->seq_num, port, hw_tstamp_en, dsa_en); + + size = ALIGN(skb->len, 8); + addr = dma_map_single(dev, skb->data, size, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev, addr))) { + ret = -EINVAL; + goto out; + } + + if (addr % 8 == 0) { + wdsize = WDSIZE_64; + xmod = XMODE_64; + } else if (addr % 4 == 0) { + wdsize = WDSIZE_32; + xmod = XMODE_32; + } else if (addr % 2 == 0) { + wdsize = WDSIZE_16; + xmod = XMODE_16; + } else { + wdsize = WDSIZE_8; + xmod = XMODE_8; + } + + ndma_ch->tx_buffs[ndma_ch->tx_head] = skb; + ndma_ch->tx_ring[ndma_ch->tx_head].start = addr; + ndma_ch->tx_ring[ndma_ch->tx_head].xmod = xmod; + ndma_ch->tx_ring[ndma_ch->tx_head].xcnt = size / xmod; + ndma_ch->tx_ring[ndma_ch->tx_head].cfg &= ~WDSIZE_MSK; + ndma_ch->tx_ring[ndma_ch->tx_head].cfg |= wdsize; + ndma_ch->tx_head = (ndma_ch->tx_head + 1) % NDMA_TX_RING_SIZE; + if (ndma_dev->loopback_en) { + ndma_ch->tx_ring[ndma_ch->tx_head] = ndma_ch->tx_loopback_desc; + ndma_ch->tx_head = (ndma_ch->tx_head + 1) % NDMA_TX_RING_SIZE; + } + ndma_ch->tx_frames_waiting++; + ndma_ch->seq_num++; + if (ndma_ch->seq_num == 0) + ndma_ch->seq_num++; + + if (!ndma_ch->tx_frames_pending) { + adrv906x_dma_tx_prep_desc_list(ndma_ch); + adrv906x_dma_tx_start(ndma_ch); + } + + stats->tx.pending_work_units = ndma_ch->tx_frames_pending; +out: + spin_unlock_irqrestore(&ndma_ch->lock, flags); + return ret; +} + +static int adrv906x_ndma_tx_status_poll(struct napi_struct *napi, int budget) +{ + struct adrv906x_ndma_chan *ndma_ch = container_of(napi, struct adrv906x_ndma_chan, napi); + struct adrv906x_ndma_dev *ndma_dev = ndma_ch->parent; + int count = 0; + unsigned char *buff; + dma_addr_t addr, addr_cur, state; + unsigned long flags; + + spin_lock_irqsave(&ndma_ch->lock, flags); + while (count < budget) { + buff = (unsigned char *)ndma_ch->rx_buffs[ndma_ch->rx_tail]; + addr = ndma_ch->rx_ring[ndma_ch->rx_tail].start; + + if (buff[0] == 0) + break; /* WU not copied */ + + /* Clear DMA IRQ status */ + iowrite32(DMA_DONE, ndma_ch->rx_dma_base + DMA_STAT); + + addr_cur = ioread32(ndma_ch->rx_dma_base + DMA_ADDR_CUR); + state = ioread32(ndma_ch->rx_dma_base + DMA_STAT); + if (addr_cur >= addr && + addr_cur < addr + NDMA_TX_HDR_STATUS_SIZE && + (DMA_RUN_MASK & state) != DMA_RUN_IDLE) + break; /* WU copy in proggress */ + + adrv906x_ndma_process_tx_status(ndma_ch, buff); + + buff[0] = 0; + ndma_ch->rx_tail = (ndma_ch->rx_tail + 1) % NDMA_TX_RING_SIZE; + count++; + } + spin_unlock_irqrestore(&ndma_ch->lock, flags); + + if (count < budget) { + /* We processed all status available. Tell NAPI it can + * stop polling then re-enable rx interrupts. + */ + napi_complete_done(napi, count); + + spin_lock_irqsave(&ndma_ch->lock, flags); + adrv906x_ndma_enable_irqs(ndma_dev, NDMA_TX_STATUS_DMA_DONE_IRQ); + spin_unlock_irqrestore(&ndma_ch->lock, flags); + } + + return count; +} + +static int adrv906x_ndma_rx_data_and_status_poll(struct napi_struct *napi, int budget) +{ + struct adrv906x_ndma_chan *ndma_ch = container_of(napi, struct adrv906x_ndma_chan, napi); + struct adrv906x_ndma_dev *ndma_dev = ndma_ch->parent; + struct device *dev = ndma_dev->dev; + union adrv906x_ndma_chan_stats *stats = &ndma_ch->stats; + int count = 0, cur_desc_idx, next_desc_idx; + dma_addr_t buf_addr, cur_addr, next_desc_addr; + struct sk_buff *skb; + unsigned int state; + unsigned long flags; + + spin_lock_irqsave(&ndma_ch->lock, flags); + + /* Clear DMA IRQ status */ + iowrite32(DMA_DONE, ndma_ch->rx_dma_base + DMA_STAT); + + while (count < budget) { + if (!ndma_ch->rx_buffs[ndma_ch->rx_tail]) + break; + + skb = (struct sk_buff *)ndma_ch->rx_buffs[ndma_ch->rx_tail]; + buf_addr = ndma_ch->rx_ring[ndma_ch->rx_tail].start; + + dma_sync_single_for_cpu(dev, buf_addr, NDMA_RX_HDR_DATA_SIZE, DMA_FROM_DEVICE); + + if (skb->data[0] == 0) + break; /* WU not copied */ + + cur_addr = ioread32(ndma_ch->rx_dma_base + DMA_ADDR_CUR); + state = ioread32(ndma_ch->rx_dma_base + DMA_STAT); + if (cur_addr >= buf_addr && + cur_addr < buf_addr + NDMA_RX_WU_BUF_SIZE && + (DMA_RUN_MASK & state) != DMA_RUN_IDLE) + break; /* WU copy in progress */ + + ndma_ch->rx_buffs[ndma_ch->rx_tail] = NULL; + dma_unmap_single(dev, buf_addr, NDMA_RX_WU_BUF_SIZE, DMA_FROM_DEVICE); + adrv906x_ndma_process_rx_work_unit(ndma_ch, skb, budget); + + ndma_ch->rx_tail = (ndma_ch->rx_tail + 1) % NDMA_RX_RING_SIZE; + ndma_ch->rx_free--; + count++; + } + + /* If there are no free buffers, the DMA will be idle. In this case, we need to + * allocate and apply a new descriptor list. Otherwise, we stop the DMA transfer, + * verify if the end of the descriptor list hasn't been reached, and if it hasn't, + * extend the current list with the new descriptor before resuming the DMA transfer. + */ + if (ndma_ch->rx_free == 0) { + adrv906x_ndma_refill_rx(ndma_ch, budget); + adrv906x_dma_rx_start(ndma_ch); + } else if (count) { + /* Suspend the next DMA transfer - note: this doesn't stop fetching new + * descriptors after finishing the current transfer, so we need to check the + * status of both the current and next descriptors. + */ + iowrite32(SUSPEND_TRANSFER, ndma_ch->rx_dma_base + DMA_BWLCNT); + + /* Get the index of the current and next descriptors */ + next_desc_addr = ioread32(ndma_ch->rx_dma_base + DMA_NEXT_DESC); + next_desc_idx = (next_desc_addr - ndma_ch->rx_ring_dma) / sizeof(struct dma_desc); + cur_desc_idx = (next_desc_idx + NDMA_RX_RING_SIZE - 1) % NDMA_RX_RING_SIZE; + + /* If neither current nor next descriptors are the end of the list, + * append new descriptors to the end. + */ + if ((ndma_ch->rx_ring[cur_desc_idx].cfg & DMAFLOW_LIST) && + (ndma_ch->rx_ring[next_desc_idx].cfg & DMAFLOW_LIST)) + adrv906x_ndma_refill_rx(ndma_ch, budget); + + /* Resume DMA transfer */ + iowrite32(FULL_BANDWIDTH, ndma_ch->rx_dma_base + DMA_BWLCNT); + } + + spin_unlock_irqrestore(&ndma_ch->lock, flags); + + stats->rx.pending_work_units = ndma_ch->rx_free; + + if (count < budget) { + /* We processed all packets available. Tell NAPI it can + * stop polling then re-enable rx interrupts. + */ + napi_complete_done(napi, count); + + spin_lock_irqsave(&ndma_ch->lock, flags); + adrv906x_ndma_enable_irqs(ndma_dev, NDMA_RX_DMA_DONE_IRQ); + spin_unlock_irqrestore(&ndma_ch->lock, flags); + } + + return count; +} + +int adrv906x_ndma_probe(struct platform_device *pdev, struct net_device *ndev, + struct device_node *ndma_np, struct adrv906x_ndma_dev *ndma_dev) +{ + struct adrv906x_ndma_chan *rx_chan = &ndma_dev->rx_chan; + struct adrv906x_ndma_chan *tx_chan = &ndma_dev->tx_chan; + struct platform_device *ndma_pdev; + struct device *dev; + int ret; + + ndma_pdev = of_platform_device_create(ndma_np, NULL, &pdev->dev); + if (!ndma_pdev) { + dev_err(dev, "failed to create ndma platform device"); + return -ENODEV; + } + dev = &ndma_pdev->dev; + ndma_dev->dev = dev; + + if (of_property_read_u32(ndma_np, "id", &ndma_dev->dev_num)) { + dev_err(dev, "failed to retrieve ndma device id from device tree"); + return -EINVAL; + } + + ret = adrv906x_ndma_device_init(ndma_dev, ndma_np); + if (ret) + return ret; + ret = adrv906x_ndma_get_reset_ctrl(ndma_dev, ndma_np); + if (ret) + return ret; + ret = adrv906x_ndma_get_intr_ctrl(ndma_dev, ndma_np); + if (ret) + return ret; + ret = adrv906x_ndma_alloc_rings(ndma_dev); + if (ret) + return ret; + + INIT_LIST_HEAD(&rx_chan->rx_data_wu_list); + + netif_napi_add_weight(ndev, &rx_chan->napi, + adrv906x_ndma_rx_data_and_status_poll, NDMA_RX_NAPI_POLL_WEIGHT); + netif_napi_add_weight(ndev, &tx_chan->napi, + adrv906x_ndma_tx_status_poll, NDMA_TX_NAPI_POLL_WEIGHT); + + return 0; +} + +void adrv906x_ndma_remove(struct adrv906x_ndma_dev *ndma_dev) +{ + adrv906x_ndma_chan_disable(&ndma_dev->tx_chan); + adrv906x_ndma_chan_disable(&ndma_dev->rx_chan); + of_platform_device_destroy(ndma_dev->dev, NULL); +} diff --git a/drivers/net/ethernet/adi/adrv906x-ndma.h b/drivers/net/ethernet/adi/adrv906x-ndma.h new file mode 100644 index 00000000000000..7fc433445be1c7 --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-ndma.h @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __ADRV906X_NDMA_H__ +#define __ADRV906X_NDMA_H__ + +#include +#include +#include +#include +#include + +#define NDMA_MAX_FRAME_SIZE_VALUE 9038 +#define NDMA_RX_MIN_FRAME_SIZE_VALUE 42 +#define NDMA_TX_MIN_FRAME_SIZE_VALUE 57 + +#define NDMA_PTP_MODE_1 0 +#define NDMA_PTP_MODE_4 1 +#define NDMA_HDR_TYPE_MASK GENMASK(1, 0) + +/* NDMA TX header */ +#define NDMA_TX_HDR_TYPE_SOF BIT(0) +#define NDMA_TX_HDR_TYPE_SUBSEQ BIT(1) +#define NDMA_TX_HDR_TYPE_LOOPBACK GENMASK(1, 0) +#define NDMA_TX_HDR_TYPE_STATUS GENMASK(1, 0) +#define NDMA_TX_HDR_SOF_FR_PTP BIT(2) +#define NDMA_TX_HDR_SOF_PORT_ID BIT(3) +#define NDMA_TX_HDR_SOF_DSA_EN BIT(4) +#define NDMA_TX_HDR_STATUS_FR_ERR BIT(2) +#define NDMA_TX_HDR_STATUS_FR_PTP BIT(3) +#define NDMA_TX_HDR_SOF_SIZE 8 +#define NDMA_TX_HDR_SUBSEQ_SIZE 8 +#define NDMA_TX_HDR_STATUS_SIZE 16 +#define NDMA_TX_HDR_LOOPBACK_SIZE 16 + +/* NDMA RX header */ +#define NDMA_RX_HDR_TYPE_DATA_SOF BIT(2) +#define NDMA_RX_HDR_TYPE_DATA BIT(0) +#define NDMA_RX_HDR_TYPE_STATUS BIT(1) +#define NDMA_RX_HDR_STATUS_FR_ERR BIT(2) +#define NDMA_RX_HDR_STATUS_PORT_ID BIT(3) +#define NDMA_RX_HDR_STATUS_FR_DROP_ERR BIT(4) +#define NDMA_RX_HDR_DATA_SIZE 2 +#define NDMA_RX_HDR_STATUS_SIZE 16 + +#define NDMA_RX_FRAME_LEN_MSB (NDMA_RX_HDR_STATUS_SIZE - 1) +#define NDMA_RX_FRAME_LEN_LSB (NDMA_RX_HDR_STATUS_SIZE - 2) +#define NDMA_RX_WU_BUF_SIZE ALIGN(1536 + NDMA_RX_HDR_DATA_SIZE + NET_IP_ALIGN, 32) +#define NDMA_RX_PKT_BUF_SIZE (NDMA_RX_WU_BUF_SIZE - NDMA_RX_HDR_DATA_SIZE) + +#define NDMA_TX_RING_SIZE 64 +#define NDMA_TX_NAPI_POLL_WEIGHT (NDMA_TX_RING_SIZE / 2) +#define NDMA_RX_RING_SIZE 128 +#define NDMA_RX_NAPI_POLL_WEIGHT (NDMA_RX_RING_SIZE / 2) + +/* default timestamp timeout delay */ +#define NDMA_TS_TX_DELAY 0x9975 + +enum adrv906x_ndma_chan_type { + NDMA_TX_CHANNEL, + NDMA_RX_CHANNEL, + MAX_NDMA_CHANNELS +}; + +typedef void (*ndma_callback)(struct sk_buff *skb, unsigned int port_id, + struct timespec64 ts, void *cb_param); + +struct dma_desc { + u32 next; + u32 start; + u32 cfg; + u32 xcnt; + u32 xmod; +}; + +struct adrv906x_ndma_reset { + void __iomem *reg; + unsigned int rx_chan_reset_bit; + unsigned int tx_chan_reset_bit; +}; + +union adrv906x_ndma_chan_stats { + struct { + u64 frame_size_errors; + u64 wu_data_header_errors; + u64 wu_status_header_errors; + u64 tstamp_timeout_errors; + u64 seqnumb_mismatch_errors; + u64 unknown_errors; + u64 pending_work_units; + u64 done_work_units; + u64 status_dma_errors; + u64 data_dma_errors; + } tx; + struct { + u64 frame_errors; + u64 frame_size_errors; + u64 frame_dropped_errors; + u64 frame_dropped_splane_errors; + u64 frame_dropped_mplane_errors; + u64 seqnumb_mismatch_errors; + u64 wu_header_errors; + u64 unknown_errors; + u64 pending_work_units; + u64 done_work_units; + u64 dma_errors; + } rx; +}; + +struct adrv906x_ndma_chan { + struct adrv906x_ndma_dev *parent; + enum adrv906x_ndma_chan_type chan_type; + void __iomem *ctrl_base; + union adrv906x_ndma_chan_stats stats; + ndma_callback status_cb_fn; + void *status_cb_param; + spinlock_t lock; /* protects struct and register access */ + unsigned char expected_seq_num; + unsigned char seq_num; + + /* TX DMA channel related fields */ + void __iomem *tx_dma_base; + void *tx_buffs[NDMA_TX_RING_SIZE]; + char tx_loopback_wu[NDMA_TX_HDR_LOOPBACK_SIZE]; + struct dma_desc tx_loopback_desc; + dma_addr_t tx_loopback_addr; + int tx_dma_done_irq; + int tx_dma_error_irq; + struct dma_desc *tx_ring; + dma_addr_t tx_ring_dma; + unsigned int tx_tail; /* Next entry in tx ring to read */ + unsigned int tx_head; /* Next entry in tx ring to give a new buffer */ + unsigned int tx_frames_waiting; + unsigned int tx_frames_pending; + struct timer_list tx_timer; + + /* RX DMA channel related fields */ + void __iomem *rx_dma_base; + struct list_head rx_data_wu_list; + void *rx_buffs[NDMA_RX_RING_SIZE]; + int rx_dma_done_irq; + int rx_dma_error_irq; + struct dma_desc *rx_ring; + dma_addr_t rx_ring_dma; + unsigned int rx_tail; /* Next entry in rx ring to read */ + unsigned int rx_head; /* Next entry in rx ring to give a new buffer */ + unsigned int rx_free; /* Number of free RX buffers */ + struct napi_struct napi; +}; + +struct adrv906x_ndma_dev { + struct device *dev; + unsigned int dev_num; + struct adrv906x_ndma_chan rx_chan; + struct adrv906x_ndma_chan tx_chan; + struct adrv906x_ndma_reset reset; + void __iomem *intr_ctrl; + struct delayed_work update_stats; + bool enabled; + struct kref refcount; + spinlock_t lock; /* protects struct and stats access */ + bool loopback_en; +}; + +int adrv906x_ndma_start_xmit(struct adrv906x_ndma_dev *ndma_dev, struct sk_buff *skb, + unsigned char port, bool hw_tstamp_en, bool dsa_en); +int adrv906x_ndma_probe(struct platform_device *pdev, struct net_device *ndev, + struct device_node *ndma_np, struct adrv906x_ndma_dev *ndma_dev); +void adrv906x_ndma_remove(struct adrv906x_ndma_dev *ndma_dev); +void adrv906x_ndma_set_ptp_mode(struct adrv906x_ndma_dev *ndma_dev, u32 ptp_mode); +void adrv906x_ndma_open(struct adrv906x_ndma_dev *ndma_dev, ndma_callback tx_cb_fn, + ndma_callback rx_cb_fn, void *rx_cb_param, bool loopback_mode); +void adrv906x_ndma_close(struct adrv906x_ndma_dev *ndma_dev); +void adrv906x_ndma_update_frame_drop_stats(struct adrv906x_ndma_dev *ndma_dev); + +#endif /* __ADRV906X_NDMA_H__ */ diff --git a/drivers/net/ethernet/adi/adrv906x-net.c b/drivers/net/ethernet/adi/adrv906x-net.c new file mode 100644 index 00000000000000..7454c60d0006a0 --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-net.c @@ -0,0 +1,1039 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024-2025, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adrv906x-ndma.h" +#include "adrv906x-macsec-ext.h" +#include "adrv906x-mac.h" +#include "adrv906x-switch.h" +#include "adrv906x-ethtool.h" +#include "adrv906x-cmn.h" +#include "adrv906x-mdio.h" +#include "adrv906x-tsu.h" +#include "adrv906x-phy.h" + +/* OIF register RX */ +#define OIF_RX_CTRL_EN BIT(0) +/* OIF register TX */ +#define OIF_CFG_TX_EN BIT(0) +#define OIF_CFG_TX_IPG GENMASK(10, 8) +#define OIF_CFG_TX_IPG_VAL 0x600 +/* default recovered clk divs */ +#define DEFAULT_RECOVERED_CLK_DIV_10G 22 +#define DEFAULT_RECOVERED_CLK_DIV_25G 55 + +static char *macaddr[MAX_NETDEV_NUM]; +module_param_array(macaddr, charp, 0, 0644); +MODULE_PARM_DESC(macaddr, "set dev0 and dev1 mac addresses via kernel module parameter"); +static u64 default_multicast_list[MAX_MULTICAST_FILTERS] = { + 0x0000011B19000000, 0x00000180C200000E, 0x00000180C2000003 +}; + +struct adrv906x_macsec_priv *adrv906x_macsec_get(struct net_device *netdev) +{ +#if IS_ENABLED(CONFIG_MACSEC) + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(netdev); + + return &adrv906x_dev->macsec; +#else + return NULL; +#endif // IS_ENABLED(CONFIG_MACSEC) +} + +static void adrv906x_eth_cdr_get_recovered_clk_divs(struct device_node *np, + struct adrv906x_eth_if *eth_if) +{ + struct device *dev = eth_if->dev; + u32 val; + + if (of_property_read_u32(np, "recovered_clk_10g", &val)) { + eth_if->recovered_clk_div_10g = DEFAULT_RECOVERED_CLK_DIV_10G; + dev_info(dev, "dt: recovered_clk_10g is missing, use default %d", + eth_if->recovered_clk_div_10g); + } else { + eth_if->recovered_clk_div_10g = val; + } + if (of_property_read_u32(np, "recovered_clk_25g", &val)) { + eth_if->recovered_clk_div_25g = DEFAULT_RECOVERED_CLK_DIV_25G; + dev_info(dev, "dt: recovered_clk_25g is missing, use default %d", + eth_if->recovered_clk_div_25g); + } else { + eth_if->recovered_clk_div_25g = val; + } +} + +static ssize_t adrv906x_pcs_link_drop_cnt_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t cnt) +{ + struct adrv906x_eth_if *adrv906x_eth; + + adrv906x_eth = dev_get_drvdata(dev); + adrv906x_cmn_pcs_link_drop_cnt_clear(adrv906x_eth); + return cnt; +} + +static ssize_t adrv906x_pcs_link_drop_cnt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adrv906x_eth_if *adrv906x_eth; + + adrv906x_eth = dev_get_drvdata(dev); + return adrv906x_cmn_pcs_link_drop_cnt_get(adrv906x_eth, buf); +} + +static DEVICE_ATTR_RW(adrv906x_pcs_link_drop_cnt); + +static struct attribute *adrv906x_eth_debug_attrs[] = { + &dev_attr_adrv906x_pcs_link_drop_cnt.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(adrv906x_eth_debug); + +static ssize_t adrv906x_eth_recovered_clock_output_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adrv906x_cmn_recovered_clock_output_get(dev, buf); +} + +static ssize_t adrv906x_eth_recovered_clock_output_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t cnt) +{ + return adrv906x_cmn_recovered_clock_output_set(dev, buf, cnt); +} + +static DEVICE_ATTR(recovered_clock_output, 0644, + adrv906x_eth_recovered_clock_output_get, + adrv906x_eth_recovered_clock_output_set); + +static void adrv906x_eth_adjust_link(struct net_device *ndev) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + struct adrv906x_eth_switch *es = ð_if->ethswitch; + struct adrv906x_mac *mac = &adrv906x_dev->mac; + struct adrv906x_tsu *tsu = &adrv906x_dev->tsu; + struct phy_device *phydev = ndev->phydev; + u32 val; + + if (adrv906x_dev->link == phydev->link) + return; + + adrv906x_dev->link = phydev->link; + + if (phydev->link) { + adrv906x_tsu_set_speed(tsu, phydev->speed); + adrv906x_eth_cmn_recovered_clk_config(adrv906x_dev); + adrv906x_mac_set_path(mac, true); + + if (eth_if->ethswitch.enabled) { + val = phydev->speed == SPEED_10000 ? AGE_TIME_5MIN_10G : AGE_TIME_5MIN_25G; + adrv906x_switch_set_mae_age_time(es, val); + adrv906x_switch_port_enable(es, adrv906x_dev->port, true); + } + } else { + if (eth_if->ethswitch.enabled) + adrv906x_switch_port_enable(es, adrv906x_dev->port, false); + } + + phy_print_status(phydev); +} + +static int adrv906x_eth_phy_connect(struct net_device *ndev, struct device_node *port_np) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct device *dev = adrv906x_dev->dev; + struct device_node *phynode; + struct phy_device *phydev; + struct device *mdiodev; + + phynode = of_parse_phandle(port_np, "phy-handle", 0); + if (!phynode) { + dev_err(dev, "dt: failed to retrieve phy phandle"); + return -ENODEV; + } + + phydev = of_phy_connect(ndev, phynode, &adrv906x_eth_adjust_link, 0, PHY_INTERFACE_MODE_NA); + if (!phydev) { + netdev_err(ndev, "could not connect to PHY"); + return -ENODEV; + } + + /* When the of_phy_connect() function attaches the phydev to the netdev based on the + * device tree node, it increases the reference count of the PHY module to prevent it + * from being removed prematurely. For ADRV906x, the PHY and Ethernet drivers are built + * into the same module and, thus, the resulting reference count of the adrv906x_eth + * module is incorrectly increased, which prevents the module from being removed later. + * put_module() decreases the module's reference count. + */ + mdiodev = &phydev->mdio.dev; + module_put(mdiodev->driver->owner); + + return 0; +} + +static void __add_tx_hw_tstamp(struct sk_buff *skb, struct timespec64 ts) +{ + struct skb_shared_hwtstamps shhwtstamps; + + if (skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) { + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); + skb_tstamp_tx(skb, &shhwtstamps); + } +} + +static void __add_rx_hw_tstamp(struct sk_buff *skb, struct timespec64 ts) +{ + struct skb_shared_hwtstamps *hwtstamps; + + hwtstamps = skb_hwtstamps(skb); + hwtstamps->hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); +} + +static void adrv906x_eth_tx_callback(struct sk_buff *skb, unsigned int port_id, + struct timespec64 ts, void *cb_param) +{ + struct net_device *ndev = (struct net_device *)cb_param; + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_eth_if *adrv906x_eth = adrv906x_dev->parent; + unsigned long flags; + + adrv906x_dev = adrv906x_eth->adrv906x_dev[port_id]; + ndev = adrv906x_dev->ndev; + + spin_lock_irqsave(&adrv906x_dev->lock, flags); + if (--adrv906x_dev->tx_frames_pending < adrv906x_eth->tx_max_frames_pending) + netif_wake_queue(ndev); + spin_unlock_irqrestore(&adrv906x_dev->lock, flags); + + adrv906x_tsu_compensate_tx_tstamp(&adrv906x_dev->tsu, &ts); + __add_tx_hw_tstamp(skb, ts); + dev_kfree_skb(skb); +} + +static int adrv906x_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_ndma_dev *ndma_dev = adrv906x_dev->ndma_dev; + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + struct adrv906x_eth_switch *es = ð_if->ethswitch; + int port = adrv906x_dev->port; + bool hw_tstamp_en, dsa_en; + unsigned long flags; + int ret; + + spin_lock_irqsave(&adrv906x_dev->lock, flags); + if (++adrv906x_dev->tx_frames_pending >= eth_if->tx_max_frames_pending) + netif_stop_queue(ndev); + spin_unlock_irqrestore(&adrv906x_dev->lock, flags); + + hw_tstamp_en = (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) ? 1 : 0; + if (hw_tstamp_en) + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + skb_tx_timestamp(skb); + + dsa_en = es->enabled ? 1 : 0; + + ret = adrv906x_ndma_start_xmit(ndma_dev, skb, port, hw_tstamp_en, dsa_en); + + return ret ? NETDEV_TX_BUSY : NETDEV_TX_OK; +} + +static void __skb_pvid_pop(struct net_device *ndev, struct sk_buff *skb) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + struct adrv906x_eth_switch *es = ð_if->ethswitch; + struct ethhdr *hdr = (struct ethhdr *)skb->data; + unsigned short ether_type = ntohs(hdr->h_proto); + struct vlan_hdr *vhdr; + unsigned short vlan_tag; + + if (!es->enabled || ether_type != ETH_P_8021Q) + return; + + vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN); + vlan_tag = ntohs(vhdr->h_vlan_TCI); + + if ((vlan_tag & VLAN_VID_MASK) == es->pvid) { + memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN); + skb_pull(skb, VLAN_HLEN); + } +} + +static void adrv906x_eth_rx_callback(struct sk_buff *skb, unsigned int port_id, + struct timespec64 ts, void *cb_param) +{ + struct net_device *ndev = (struct net_device *)cb_param; + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + + /* network interface is selected base on port_id from ndma rx status header */ + adrv906x_dev = eth_if->adrv906x_dev[port_id]; + ndev = adrv906x_dev->ndev; + + __skb_pvid_pop(ndev, skb); + __add_rx_hw_tstamp(skb, ts); + skb->protocol = eth_type_trans(skb, ndev); + netif_receive_skb(skb); +} + +static void adrv906x_eth_multicast_list(struct net_device *ndev) +{ + struct adrv906x_mac *mac; + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + + mac = &adrv906x_dev->mac; + + if (ndev->flags & IFF_PROMISC) + /* Promiscuous mode. */ + adrv906x_mac_promiscuous_mode_en(mac); + else + adrv906x_mac_promiscuous_mode_dis(mac); +} + +static int __set_mac_address(struct adrv906x_eth_dev *adrv906x_dev, struct device_node *port_np) +{ + struct device *dev = adrv906x_dev->dev; + struct net_device *ndev = adrv906x_dev->ndev; + int port = adrv906x_dev->port; + u8 tmpaddr[ETH_ALEN]; + struct sockaddr addr; + int ret; + + memset(addr.sa_data, 0, sizeof(addr.sa_data_min)); + + /* try to get mac address in following order: + * + * 1) module parameter via kernel command line + * macaddr="dev 0 mac addr","dev 1 mac addr" + */ + if (macaddr[port]) + mac_pton(macaddr[port], addr.sa_data); + /* 2) mac address in the device tree + * it is filled in by u-boot if it + * is not set + */ + if (!is_valid_ether_addr(addr.sa_data)) { + if (port_np) { + ret = of_get_mac_address(port_np, tmpaddr); + if (!ret) + ether_addr_copy(addr.sa_data, tmpaddr); + } + } + /* 3) random mac address */ + if (!is_valid_ether_addr(addr.sa_data)) { + /* Report it and use a random ethernet address instead */ + dev_warn(dev, "invalid MAC address: %pM, generate a random addr", addr.sa_data); + /* assign random address */ + eth_random_addr(addr.sa_data); + } + + return eth_mac_addr(ndev, &addr); +} + +static void __set_default_multicast_filters(struct adrv906x_eth_dev *adrv906x_dev) +{ + struct adrv906x_mac *mac = &adrv906x_dev->mac; + int i; + + for (i = 0; i < MAX_MULTICAST_FILTERS; i++) + adrv906x_mac_set_multicast_filter(mac, default_multicast_list[i], i); +} + +static int adrv906x_eth_change_mtu(struct net_device *ndev, int new_mtu) +{ + if (netif_running(ndev)) + return -EBUSY; + if (new_mtu > ndev->max_mtu) { + netdev_err(ndev, "Tried to set mtu size to a bigger than max, maximum value is %d", + ndev->max_mtu); + return -EINVAL; + } + ndev->mtu = new_mtu; + + netdev_info(ndev, "set mtu size to %d", ndev->mtu); + + return 0; +} + +static int adrv906x_get_hwtstamp_config(struct net_device *ndev, struct ifreq *ifr) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + + if (copy_to_user(ifr->ifr_data, &adrv906x_dev->tstamp_config, + sizeof(adrv906x_dev->tstamp_config))) + return -EFAULT; + return 0; +} + +static int adrv906x_set_hwtstamp_config(struct net_device *ndev, struct ifreq *ifr) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_ndma_dev *ndma_dev = adrv906x_dev->ndma_dev; + struct adrv906x_eth_if *eth_if = adrv906x_dev->parent; + struct hwtstamp_config config; + u32 ptp_mode; + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + if (config.flags) + return -EINVAL; + + switch (config.tx_type) { + case HWTSTAMP_TX_OFF: + /* TODO clear timestamp flag */ + break; + + case HWTSTAMP_TX_ON: + /* TODO set timestamp flag */ + break; + + default: + return -ERANGE; + } + + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + break; + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + case HWTSTAMP_FILTER_ALL: + /* time stamp any incoming packet */ + config.rx_filter = HWTSTAMP_FILTER_ALL; + + ptp_mode = eth_if->ethswitch.enabled ? NDMA_PTP_MODE_1 : NDMA_PTP_MODE_4; + adrv906x_ndma_set_ptp_mode(ndma_dev, ptp_mode); + + break; + default: + return -ERANGE; + } + + if (copy_to_user(ifr->ifr_data, &config, sizeof(config))) + return -EFAULT; + + memcpy(&adrv906x_dev->tstamp_config, &config, sizeof(config)); + + return 0; +} + +static int adrv906x_config_eth_recov_clk(struct device *dev, + struct device_node *np) +{ + u32 reg, len; + void *ptr; + + if (of_property_read_u32_index(np, "reg", 0, ®)) + dev_err(dev, "dt: eth_recov_clk_np failed - skipping"); + if (of_property_read_u32_index(np, "reg", 1, &len)) + dev_err(dev, "dt: eth_recov_clk_np failed - skipping"); + + ptr = ioremap(reg, len); + iowrite32(0x1, ptr); + iounmap(ptr); + + return 0; +} + +static int adrv906x_get_oran_if_reg_addr(struct adrv906x_eth_dev *adrv906x_dev, + struct device_node *np) +{ + struct device *dev = adrv906x_dev->dev; + u8 port_id = adrv906x_dev->port; + u32 reg, len; + + /* get oif_rx address */ + if (of_property_read_u32_index(np, "reg", port_id * 4 + 0, ®)) + goto err; + if (of_property_read_u32_index(np, "reg", port_id * 4 + 1, &len)) + goto err; + adrv906x_dev->oif.oif_rx = devm_ioremap(dev, reg, len); + if (!adrv906x_dev->oif.oif_rx) { + dev_err(dev, "ioremap oif rx reg failed!"); + return -ENOMEM; + } + + /* get oif_tx address */ + if (of_property_read_u32_index(np, "reg", port_id * 4 + 2, ®)) + goto err; + if (of_property_read_u32_index(np, "reg", port_id * 4 + 3, &len)) + goto err; + adrv906x_dev->oif.oif_tx = devm_ioremap(dev, reg, len); + if (!adrv906x_dev->oif.oif_tx) { + dev_err(dev, "ioremap oif tx reg failed!"); + return -ENOMEM; + } + return 0; +err: + dev_err(dev, "check the reg property of oran_oif node"); + return -1; +} + +static int adrv906x_eth_oran_if_tx_dis(struct adrv906x_oran_if *oif) +{ + u32 val; + + if (oif->oif_tx) { + val = ioread32(oif->oif_tx) & ~OIF_CFG_TX_EN; + iowrite32(val, oif->oif_tx); + } + return 0; +} + +static int adrv906x_eth_oran_if_en(struct adrv906x_oran_if *oif) +{ + u32 val; + + if (oif->oif_rx && oif->oif_tx) { + val = ioread32(oif->oif_rx); + val |= OIF_RX_CTRL_EN; + iowrite32(val, oif->oif_rx); + + val = ioread32(oif->oif_tx); + val |= (OIF_CFG_TX_EN | OIF_CFG_TX_IPG_VAL); + iowrite32(val, oif->oif_tx); + } + return 0; +} + +static int adrv906x_eth_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd) +{ + int ret; + + switch (cmd) { + case SIOCGHWTSTAMP: + ret = adrv906x_get_hwtstamp_config(ndev, ifr); + break; + + case SIOCSHWTSTAMP: + ret = adrv906x_set_hwtstamp_config(ndev, ifr); + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +static int adrv906x_eth_switch_reset_soft_pre(void *arg) +{ + struct adrv906x_eth_if *eth_if = (struct adrv906x_eth_if *)arg; + int ret; + int i; + + for (i = 0; i < MAX_NETDEV_NUM; i++) + adrv906x_mac_rx_path_dis(ð_if->adrv906x_dev[i]->mac); + + ret = adrv906x_eth_oran_if_tx_dis(ð_if->adrv906x_dev[1]->oif); + + return ret; +} + +static int adrv906x_eth_switch_reset_soft_post(void *arg) +{ + struct adrv906x_eth_if *eth_if = (struct adrv906x_eth_if *)arg; + int ret; + int i; + + ret = adrv906x_eth_oran_if_en(ð_if->adrv906x_dev[1]->oif); + for (i = 0; i < MAX_NETDEV_NUM; i++) + adrv906x_mac_rx_path_en(ð_if->adrv906x_dev[i]->mac); + + return ret; +} + +static int adrv906x_eth_open(struct net_device *ndev) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct device *dev = adrv906x_dev->dev; + struct adrv906x_ndma_dev *ndma_dev = adrv906x_dev->ndma_dev; + + dev_info(dev, "%s called", __func__); + + adrv906x_eth_oran_if_en(&adrv906x_dev->oif); + + phy_start(ndev->phydev); + + adrv906x_ndma_open(ndma_dev, adrv906x_eth_tx_callback, adrv906x_eth_rx_callback, ndev, + false); + +#if IS_ENABLED(CONFIG_MACSEC) + if (adrv906x_dev->macsec.enabled) + adrv906x_macsec_commonport_status_update(ndev); +#endif // IS_ENABLED(CONFIG_MACSEC) + + adrv906x_dev->tx_frames_pending = 0; + + netif_start_queue(ndev); + return 0; +} + +static int adrv906x_eth_stop(struct net_device *ndev) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + struct adrv906x_ndma_dev *ndma_dev = adrv906x_dev->ndma_dev; + struct device *dev = adrv906x_dev->dev; + + dev_info(dev, "%s called", __func__); + netif_stop_queue(ndev); + adrv906x_ndma_close(ndma_dev); + if (ndev->phydev) + phy_stop(ndev->phydev); + return 0; +} + +static void adrv906x_get_rtnl_stats(struct adrv906x_eth_dev *adrv906x_dev) +{ + adrv906x_dev->rtnl_stats.tx_packets = + adrv906x_dev->mac.hw_stats_tx.general_stats.unicast_pkts + + adrv906x_dev->mac.hw_stats_tx.general_stats.multicast_pkts + + adrv906x_dev->mac.hw_stats_tx.general_stats.broadcast_pkts; + adrv906x_dev->rtnl_stats.tx_errors = + adrv906x_dev->mac.hw_stats_tx.underflow + + adrv906x_dev->mac.hw_stats_tx.general_stats.undersize_pkts + + adrv906x_dev->mac.hw_stats_tx.general_stats.oversize_pkts; + adrv906x_dev->rtnl_stats.tx_fifo_errors = adrv906x_dev->mac.hw_stats_tx.underflow; + adrv906x_dev->rtnl_stats.tx_dropped = + adrv906x_dev->mac.hw_stats_tx.general_stats.drop_events; + adrv906x_dev->rtnl_stats.rx_packets = + adrv906x_dev->mac.hw_stats_rx.general_stats.unicast_pkts + + adrv906x_dev->mac.hw_stats_rx.general_stats.multicast_pkts + + adrv906x_dev->mac.hw_stats_rx.general_stats.broadcast_pkts; + adrv906x_dev->rtnl_stats.rx_errors = + adrv906x_dev->mac.hw_stats_rx.general_stats.undersize_pkts + + adrv906x_dev->mac.hw_stats_rx.general_stats.oversize_pkts + + adrv906x_dev->mac.hw_stats_rx.crc_errors + + adrv906x_dev->mac.hw_stats_rx.fragments + + adrv906x_dev->mac.hw_stats_rx.jabbers + + adrv906x_dev->mac.hw_stats_rx.mac_framing_error + + adrv906x_dev->mac.hw_stats_rx.rs_framing_error; + adrv906x_dev->rtnl_stats.rx_length_errors = + adrv906x_dev->mac.hw_stats_rx.general_stats.undersize_pkts + + adrv906x_dev->mac.hw_stats_rx.general_stats.oversize_pkts; + adrv906x_dev->rtnl_stats.rx_over_errors = adrv906x_dev->mac.hw_stats_rx.overflow; + adrv906x_dev->rtnl_stats.rx_crc_errors = adrv906x_dev->mac.hw_stats_rx.crc_errors; + adrv906x_dev->rtnl_stats.rx_frame_errors = adrv906x_dev->mac.hw_stats_rx.mac_framing_error + + adrv906x_dev->mac.hw_stats_rx.rs_framing_error; + adrv906x_dev->rtnl_stats.rx_fifo_errors = adrv906x_dev->mac.hw_stats_rx.overflow; + adrv906x_dev->rtnl_stats.rx_missed_errors = adrv906x_dev->mac.hw_stats_rx.overflow; + adrv906x_dev->rtnl_stats.rx_dropped = + adrv906x_dev->mac.hw_stats_rx.mc_drop + + adrv906x_dev->mac.hw_stats_rx.general_stats.drop_events; + adrv906x_dev->rtnl_stats.multicast = + adrv906x_dev->mac.hw_stats_rx.general_stats.multicast_pkts; +} + +static void adrv906x_eth_get_stats64(struct net_device *ndev, + struct rtnl_link_stats64 *stats) +{ + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(ndev); + + adrv906x_get_rtnl_stats(adrv906x_dev); + + memcpy(stats, &adrv906x_dev->rtnl_stats, sizeof(*stats)); +} + +static const struct net_device_ops adrv906x_eth_ops = { + .ndo_open = adrv906x_eth_open, + .ndo_stop = adrv906x_eth_stop, + .ndo_start_xmit = adrv906x_eth_start_xmit, + .ndo_set_rx_mode = adrv906x_eth_multicast_list, + .ndo_set_mac_address = eth_mac_addr, + .ndo_change_mtu = adrv906x_eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, + .ndo_get_stats64 = adrv906x_eth_get_stats64, + .ndo_eth_ioctl = adrv906x_eth_ioctl, +}; + +static const struct of_device_id adrv906x_eth_dt_ids[] = { + { .compatible = "adi,adrv906x-net", }, + {} +}; + +MODULE_DEVICE_TABLE(of, adrv906x_eth_dt_ids); + +static int adrv906x_get_mac_reg_addr(struct adrv906x_eth_dev *adrv906x_dev, + struct device_node *port_np) +{ + u32 reg, len; + struct device *dev = adrv906x_dev->dev; + + /* Get xmac device address */ + of_property_read_u32_index(port_np, "reg", 0, ®); + of_property_read_u32_index(port_np, "reg", 1, &len); + adrv906x_dev->mac.xmac = devm_ioremap(dev, reg, len); + if (!adrv906x_dev->mac.xmac) { + dev_err(dev, "ioremap xmac failed"); + return -ENOMEM; + } + + /* Get emac_tx device address */ + of_property_read_u32_index(port_np, "reg", 2, ®); + of_property_read_u32_index(port_np, "reg", 3, &len); + adrv906x_dev->mac.emac_tx = devm_ioremap(dev, reg, len); + if (!adrv906x_dev->mac.emac_tx) { + dev_err(dev, "ioremap emac tx failed"); + return -ENOMEM; + } + + /* Get emac_rx device address */ + of_property_read_u32_index(port_np, "reg", 4, ®); + of_property_read_u32_index(port_np, "reg", 5, &len); + adrv906x_dev->mac.emac_rx = devm_ioremap(dev, reg, len); + if (!adrv906x_dev->mac.emac_rx) { + dev_err(dev, "ioremap emac rx failed"); + return -ENOMEM; + } + + return 0; +} + +static struct device_node * +adrv906x_get_eth_child_node(struct device_node *ether_np, int id) +{ + struct device_node *port_np; + unsigned int port_id; + + for_each_child_of_node(ether_np, port_np) { + /* It is not a 'port' node, continue */ + if (strcmp(port_np->name, "port")) + continue; + + if (of_property_read_u32(port_np, "id", &port_id) < 0) + continue; + + if (port_id == id) + return port_np; + } + /* Not found */ + return NULL; +} + +static int adrv906x_eth_dev_reg(struct platform_device *pdev, struct device_node *port_np, + struct adrv906x_eth_dev **adrv906x_dev) +{ + struct net_device *ndev; + struct adrv906x_eth_dev *priv; + struct device *dev = &pdev->dev; + int ret; + const char *if_name; + struct net *net; + + ndev = devm_alloc_etherdev_mqs(dev, sizeof(struct adrv906x_eth_dev), 1, 1); + ndev->netdev_ops = &adrv906x_eth_ops; + ndev->ethtool_ops = &adrv906x_ethtool_ops; + net = dev_net(ndev); + + /* Try interface name from DT */ + ret = of_property_read_string(port_np, "if-name", &if_name); + if (!ret) { + if (dev_valid_name(if_name)) { + if (__dev_get_by_name(net, if_name)) { + dev_err(dev, "interface name: %s is already used, using default", if_name); + } else { + dev_info(dev, "using %s interface name from device tree", if_name); + snprintf(ndev->name, IFNAMSIZ, if_name); + } + } else { + dev_err(dev, "interface name: %s is not valid, using default", if_name); + } + } + + SET_NETDEV_DEV(ndev, &pdev->dev); + priv = netdev_priv(ndev); + priv->dev = dev; + priv->ndev = ndev; + priv->pdev = pdev; + ret = register_netdev(ndev); + + if (ret) { + dev_err(dev, "error %d initializing ethernet device ...", ret); + return ret; + } + *adrv906x_dev = priv; + + dev_info(dev, "initialized %s", ndev->name); + + return 0; +} + +static int adrv906x_eth_probe(struct platform_device *pdev) +{ + struct net_device *ndev; + struct adrv906x_eth_if *eth_if; + struct adrv906x_eth_dev *adrv906x_dev; + struct adrv906x_tsu *tsu; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *eth_ports_np, *port_np, *oran_if_np, *eth_recov_clk_np, *ndma_np, + *mdio_np; + struct adrv906x_ndma_dev *ndma_devs[MAX_NETDEV_NUM] = { NULL }; + unsigned int ndma_num; + int ret, i; + + eth_if = devm_kzalloc(dev, sizeof(struct adrv906x_eth_if), GFP_KERNEL); + if (!eth_if) + return -ENOMEM; + + eth_if->dev = dev; + dev_set_drvdata(dev, eth_if); + + eth_if->emac_cmn_regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(eth_if->emac_cmn_regs)) { + dev_err(dev, "dt: reg-property missing or broken for common regs"); + ret = PTR_ERR(eth_if->emac_cmn_regs); + goto error; + } + + adrv906x_eth_cmn_rst_reg(eth_if->emac_cmn_regs); + + mutex_init(ð_if->mtx); + + adrv906x_eth_cdr_get_recovered_clk_divs(np, eth_if); + + ret = adrv906x_phy_register(); + if (ret) + goto error; + + mdio_np = of_get_child_by_name(np, "mdio_if"); + ret = adrv906x_mdio_register(eth_if, mdio_np); + if (ret) + goto error; + + /* Get child node ethernet-ports */ + eth_ports_np = of_get_child_by_name(np, "ethernet-ports"); + if (!eth_ports_np) { + dev_err(dev, "dt: ethernet-ports property missing"); + ret = -ENODEV; + goto error; + } + + oran_if_np = of_get_child_by_name(np, "oran_if"); + if (!oran_if_np) + dev_warn(dev, "dt: oran_if node missing - skipping"); + + eth_recov_clk_np = of_get_child_by_name(np, "eth_recov_clk"); + if (eth_recov_clk_np) + adrv906x_config_eth_recov_clk(dev, eth_recov_clk_np); + else + dev_warn(dev, "dt: eth_recov_clk_np node missing - skipping"); + + for (i = 0; i < MAX_NETDEV_NUM; i++) { + /* Get port@i of node ethernet-ports */ + port_np = adrv906x_get_eth_child_node(eth_ports_np, i); + if (!port_np) + continue; + + ret = adrv906x_eth_dev_reg(pdev, port_np, &adrv906x_dev); + if (ret) + goto error; + + tsu = &adrv906x_dev->tsu; + ret = adrv906x_tsu_setup(pdev, tsu, port_np); + if (ret) + goto error; + + eth_if->adrv906x_dev[i] = adrv906x_dev; + adrv906x_dev->parent = eth_if; + adrv906x_dev->port = i; + adrv906x_dev->dev = dev; + ndev = adrv906x_dev->ndev; + ndev->max_mtu = MAX_MTU_SIZE; + ndev->min_mtu = ETH_MIN_MTU; + ndev->mtu = ETH_DATA_LEN; + /* Headroom required for ndma headers for tx packets */ + ndev->needed_headroom += NDMA_TX_HDR_SOF_SIZE; + // Set promiscuous mode by default + rtnl_lock(); + dev_change_flags(ndev, ndev->flags | IFF_PROMISC, NULL); + rtnl_unlock(); + + ret = device_create_file(&adrv906x_dev->ndev->dev, + &dev_attr_recovered_clock_output); + dev_set_drvdata(&adrv906x_dev->ndev->dev, adrv906x_dev); + if (ret) + goto error_delete_cdr_div_out_enable_sysfs; + +#if IS_ENABLED(CONFIG_MACSEC) + adrv906x_macsec_probe(pdev, ndev, port_np); +#endif // IS_ENABLED(CONFIG_MACSEC) + + /* read ndma dt */ + ndma_np = of_parse_phandle(port_np, "ndma-handle", 0); + if (!ndma_np) { + dev_err(dev, "dt: failed to retrieve ndma phandle from device tree"); + return -ENODEV; + } + + if (of_property_read_u32(ndma_np, "id", &ndma_num)) { + dev_err(dev, "dt: failed to retrieve ndma device id from device tree"); + return -ENODEV; + } + + if (!ndma_devs[ndma_num]) { + ndma_devs[ndma_num] = devm_kzalloc(dev, sizeof(struct adrv906x_ndma_dev), + GFP_KERNEL); + if (!ndma_devs[ndma_num]) + return -ENOMEM; + + ret = adrv906x_ndma_probe(pdev, ndev, ndma_np, + ndma_devs[ndma_num]); + if (ret) { + dev_err(dev, "failed to probe ndma device"); + goto error_unregister_netdev; + } + } + + adrv906x_dev->ndma_dev = ndma_devs[ndma_num]; + dev_info(dev, "%s: connected to ndma%d", ndev->name, ndma_num); + + ret = adrv906x_get_mac_reg_addr(adrv906x_dev, port_np); + if (ret) { + dev_err(dev, "failed to get mac register addresses"); + goto error_unregister_netdev; + } + + __set_mac_address(adrv906x_dev, port_np); + adrv906x_mac_init(&adrv906x_dev->mac, NDMA_MAX_FRAME_SIZE_VALUE); + + dev_info(dev, "inited mac: id:0x%x, ver:0x%x, cap:0x%x", + adrv906x_dev->mac.id, adrv906x_dev->mac.version, adrv906x_dev->mac.cap); + + __set_default_multicast_filters(adrv906x_dev); + + ret = adrv906x_eth_phy_connect(ndev, port_np); + if (ret) + goto error_unregister_netdev; + + if (oran_if_np) { + adrv906x_get_oran_if_reg_addr(adrv906x_dev, oran_if_np); + adrv906x_eth_oran_if_en(&adrv906x_dev->oif); + } + + spin_lock_init(&adrv906x_dev->lock); + } + + ret = adrv906x_switch_probe(ð_if->ethswitch, pdev, + &adrv906x_eth_switch_reset_soft_pre, + &adrv906x_eth_switch_reset_soft_post, eth_if); + if (ret) + dev_warn(dev, "failed to probe switch - falling back to non-switch mode"); + + mutex_lock(ð_if->mtx); +#if IS_ENABLED(CONFIG_MACSEC) + adrv906x_eth_cmn_init(eth_if->emac_cmn_regs, eth_if->ethswitch.enabled, + adrv906x_dev->macsec.enabled); +#else + adrv906x_eth_cmn_init(eth_if->emac_cmn_regs, eth_if->ethswitch.enabled, false); +#endif + mutex_unlock(ð_if->mtx); + + if (eth_if->ethswitch.enabled) { + ret = adrv906x_switch_init(ð_if->ethswitch); + if (ret) + goto error_unregister_netdev; + } + + eth_if->tx_max_frames_pending = + eth_if->ethswitch.enabled ? NDMA_TX_RING_SIZE / 2 : NDMA_TX_RING_SIZE; + + platform_set_drvdata(pdev, eth_if); + + ret = sysfs_create_group(&pdev->dev.kobj, *adrv906x_eth_debug_groups); + if (ret) + goto error_delete_groups; + + return 0; + +error_delete_cdr_div_out_enable_sysfs: + device_remove_file(ð_if->adrv906x_dev[i]->ndev->dev, + &dev_attr_recovered_clock_output); + dev_set_drvdata(ð_if->adrv906x_dev[i]->ndev->dev, NULL); +error_delete_groups: + sysfs_remove_groups(&pdev->dev.kobj, adrv906x_eth_debug_groups); +error_unregister_netdev: + for (i = 0; i < MAX_NETDEV_NUM; i++) + if (eth_if->adrv906x_dev[i] && eth_if->adrv906x_dev[i]->ndev) + unregister_netdev(eth_if->adrv906x_dev[i]->ndev); +error: + return ret; +} + +static void adrv906x_eth_remove(struct platform_device *pdev) +{ + struct adrv906x_eth_if *eth_if = platform_get_drvdata(pdev); + struct adrv906x_eth_switch *es = ð_if->ethswitch; + struct net_device *ndev; + int i; + + mutex_destroy(ð_if->mtx); + sysfs_remove_groups(&pdev->dev.kobj, adrv906x_eth_debug_groups); + + for (i = 0; i < MAX_NETDEV_NUM; i++) { + if (eth_if->adrv906x_dev[i]) { + ndev = eth_if->adrv906x_dev[i]->ndev; + device_remove_file(ð_if->adrv906x_dev[i]->ndev->dev, + &dev_attr_recovered_clock_output); + dev_set_drvdata(ð_if->adrv906x_dev[i]->ndev->dev, NULL); + phy_disconnect(ndev->phydev); + unregister_netdev(ndev); + adrv906x_mac_cleanup(ð_if->adrv906x_dev[i]->mac); + } + } + + for (i = 0; i < MAX_NETDEV_NUM; i++) { + adrv906x_ndma_remove(eth_if->adrv906x_dev[i]->ndma_dev); + if (es->enabled) + break; + } + + adrv906x_mdio_unregister(eth_if); + adrv906x_phy_unregister(); + + if (es->enabled) + adrv906x_switch_cleanup(es); +} + +static struct platform_driver adrv906x_eth_drv = { + .probe = adrv906x_eth_probe, + .remove = adrv906x_eth_remove, + .driver = { + .name = "adrv906x-net", + .of_match_table = of_match_ptr(adrv906x_eth_dt_ids), + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(adrv906x_eth_drv); +MODULE_DESCRIPTION("ADRV906X Gigabit Ethernet driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/adi/adrv906x-net.h b/drivers/net/ethernet/adi/adrv906x-net.h new file mode 100644 index 00000000000000..b9e08e8512823d --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-net.h @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __ADRV906X_NET_H__ +#define __ADRV906X_NET_H__ + +#include +#include +#include +#include "adrv906x-ndma.h" +#include "adrv906x-mac.h" +#include "adrv906x-switch.h" +#include "adrv906x-macsec-ext.h" +#include "adrv906x-tsu.h" + +#define REGMAP_RESET_SWITCH BIT(0) +#define REGMAP_RESET_PCS_MAC0 BIT(4) +#define REGMAP_RESET_PCS_MAC1 BIT(5) +#define REGMAP_RESET_MACSEC0 BIT(8) +#define REGMAP_RESET_MACSEC1 BIT(9) + +#define MAX_NETDEV_NUM 2 +#define MAX_MULTICAST_FILTERS 3 +#define MAX_MTU_SIZE (NDMA_MAX_FRAME_SIZE_VALUE - ETH_HLEN - VLAN_HLEN \ + - ETH_FCS_LEN) + +struct adrv906x_oran_if { + void __iomem *oif_rx; + void __iomem *oif_tx; +}; + +struct adrv906x_eth_dev { + struct net_device *ndev; + struct device *dev; + struct platform_device *pdev; + struct adrv906x_ndma_dev *ndma_dev; + struct hwtstamp_config tstamp_config; + struct adrv906x_mac mac; + struct adrv906x_oran_if oif; + struct adrv906x_tsu tsu; +#if IS_ENABLED(CONFIG_MACSEC) + struct adrv906x_macsec_priv macsec; +#endif // IS_ENABLED(CONFIG_MACSEC) + int port; + int link; + struct adrv906x_eth_if *parent; + struct rtnl_link_stats64 rtnl_stats; + int tx_frames_pending; + spinlock_t lock; /* protects struct access */ +}; + +struct adrv906x_eth_if { + struct adrv906x_eth_dev *adrv906x_dev[MAX_NETDEV_NUM]; + struct device *dev; + struct adrv906x_eth_switch ethswitch; + struct mii_bus *mdio; /* saved for cleanup */ + void __iomem *emac_cmn_regs; + int tx_max_frames_pending; + struct mutex mtx; /* protects regs access*/ + u32 recovered_clk_div_10g; + u32 recovered_clk_div_25g; +}; + +#endif /* __ADRV906X_NET_H__ */ diff --git a/drivers/net/ethernet/adi/adrv906x-phy-serdes.c b/drivers/net/ethernet/adi/adrv906x-phy-serdes.c new file mode 100644 index 00000000000000..585c35c3bf613f --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-phy-serdes.c @@ -0,0 +1,1220 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adrv906x-phy-serdes.h" +#include "adrv906x-cmn.h" + +#define ADRV906X_GENL_NAME "adrv906x" +#define ADRV906X_GENL_VERSION 1 +#define ADRV906X_GENL_MC_GRP_NAME "adrv906x_mcgrp" +#define ADRV906X_PHY_MAX_PLLS 2 +#define ADRV906X_PHY_MAX_LANES 4 +#define ADRV906X_PHY_DEV_IDX_MSK GENMASK(3, 0) +#define ADRV906X_PHY_DEV_SPEED_MSK GENMASK(31, 16) + +#define APP_HEARTBEAT_TIMEOUT_MS 1500 + +typedef void (*adrv906x_phy_fsm_action)(void *param); +typedef char * (*adrv906x_phy_fsm_state_to_str)(u32 state); +typedef char * (*adrv906x_phy_fsm_event_to_str)(u32 event); + +enum adrv906x_nl_attrs { + ATTR_UNSPEC, + ATTR_CMD_PAYLOAD, /* u32, link speed (bits 31:16), dev_id (bits 3:0) */ + __ATTR_MAX, + NUM_ATTR = __ATTR_MAX, + ATTR_MAX = __ATTR_MAX - 1, +}; + +enum adrv906x_nl_commands { + NL_CMD_PLL_CFG_REQ, + NL_CMD_PLL_CFG_DONE, + NL_CMD_PLL_RELOCK_SUCCEED, + NL_CMD_PLL_RELOCK_FAILED, + NL_CMD_SER_CFG_REQ, + NL_CMD_SER_CFG_DONE, + NL_CMD_DESER_CFG_REQ, + NL_CMD_DESER_CFG_DONE, + NL_CMD_DESER_INIT_CAL_REQ, + NL_CMD_DESER_SIGNAL_OK, + NL_CMD_DESER_LOS, + NL_CMD_SERDES_PWR_DOWN_REQ, + NL_CMD_SERDES_PWR_DOWN_RDY, + NL_CMD_SERDES_APP_HEARTBEAT, +}; + +enum adrv906x_serdes_states { + SD_ST_IDLE, + SD_ST_LNK_UP_PEND, + SD_ST_APP_ACTV, + SD_ST_PLL_UNLOCKED, + SD_ST_PLL_CFG, + SD_ST_RATE_CHANGED, + SD_ST_SER_CFG, + SD_ST_DESER_CFG, + SD_ST_CAL_STARTED, + SD_ST_LOS, + SD_ST_SIGNAL_OK, + SD_ST_PWR_DOWN, +}; + +enum adrv906x_serdes_events { + SD_EVT_UNKNOWN, + SD_EVT_APP_ACTV, + SD_EVT_APP_INACT, + SD_EVT_LNK_UP, + SD_EVT_PLL_LOCKED, + SD_EVT_PLL_UNLOCKED, + SD_EVT_SER_RDY, + SD_EVT_DESER_RDY, + SD_EVT_LNK_DOWN, + SD_EVT_PWR_DOWN_DONE, + SD_EVT_SIGNAL_OK, + SD_EVT_LOS_DETECTED, +}; + +enum adrv906x_pll_states { + PLL_ST_UNLOCKED, + PLL_ST_10G_RUN, + PLL_ST_25G_RUN, + PLL_ST_LNK0_10G_REQ, + PLL_ST_LNK1_10G_REQ, + PLL_ST_LNK0_25G_REQ, + PLL_ST_LNK1_25G_REQ, + PLL_ST_LNK0_10G_PEND, + PLL_ST_LNK1_10G_PEND, + PLL_ST_LNK01_10G_PEND, + PLL_ST_LNK0_25G_PEND, + PLL_ST_LNK1_25G_PEND, + PLL_ST_LNK01_25G_PEND, +}; + +enum adrv906x_pll_events { + PLL_EVT_UNKNOWN, + PLL_EVT_UNLOCKED, + PLL_EVT_APP_ACTV, + PLL_EVT_APP_INACT, + PLL_EVT_LNK0_10G_REQ, + PLL_EVT_LNK1_10G_REQ, + PLL_EVT_LNK0_25G_REQ, + PLL_EVT_LNK1_25G_REQ, + PLL_EVT_CFG_DONE, + PLL_EVT_LNK0_DOWN, + PLL_EVT_LNK1_DOWN, +}; + +struct adrv906x_phy_fsm { + char name[16]; + atomic_t state; + struct task_struct *task; + struct adrv906x_phy_fsm_tran *tran_tbl; + int tran_tbl_size; + struct completion comp_tran; + struct kfifo event_fifo; + spinlock_t event_fifo_lock; + adrv906x_phy_fsm_state_to_str state_to_str; + adrv906x_phy_fsm_event_to_str event_to_str; +}; + +struct adrv906x_phy_fsm_tran { + int src_state; + int event; + adrv906x_phy_fsm_action action; + int dst_state; +}; + +struct adrv906x_serdes { + struct phy_device *phydev; + adrv906x_serdes_cb tx_path_en; + adrv906x_serdes_cb rx_path_en; + struct adrv906x_phy_fsm fsm; + int dev_id; + int speed; +}; + +struct adrv906x_pll { + struct adrv906x_phy_fsm fsm; + struct mutex mtx; /* protects struct access */ + int dev_id; + bool started; +}; + +static int __pll_cfg_done_recv(struct sk_buff *skb, struct genl_info *info); +static int __pll_relock_succeed_recv(struct sk_buff *skb, struct genl_info *info); +static int __pll_relock_failed_recv(struct sk_buff *skb, struct genl_info *info); +static int __sd_ser_cfg_done_recv(struct sk_buff *skb, struct genl_info *info); +static int __sd_deser_los_detected_recv(struct sk_buff *skb, struct genl_info *info); +static int __sd_deser_cfg_done_recv(struct sk_buff *skb, struct genl_info *info); +static int __sd_deser_signal_ok_recv(struct sk_buff *skb, struct genl_info *info); +static int __sd_app_pwr_down_rdy_recv(struct sk_buff *skb, struct genl_info *info); +static int __sd_app_heartbeat_recv(struct sk_buff *skb, struct genl_info *info); +static void __sd_pwr_down(void *param); +static void __sd_cfg_pll_req(void *param); +static void __sd_lnk_down_notif(void *param); +static void __sd_pwr_down_notif_send(void *param); +static void __sd_ser_cfg_send(void *param); +static void __sd_deser_cfg_send(void *param); +static void __sd_deser_init_cal_send(void *param); +static void __pll_unlkd_notif_lnk01(void *param); +static void __pll_lkd_notif_lnk0(void *param); +static void __pll_lkd_notif_lnk1(void *param); +static void __pll_lkd_notif_lnk01(void *param); +static void __pll_check_actv_lnks(void *param); +static void __pll_cfg_10G_send(void *param); +static void __pll_cfg_25G_send(void *param); +static void __do_nothing(void *param); +static void __sd_app_watchdog_expired(struct work_struct *work); + +static DEFINE_MUTEX(genl_mutex); + +static struct adrv906x_phy_fsm_tran adrv906x_phy_fsm_serdes_trans[] = { + /* Source State Event Action Destination State */ + { SD_ST_IDLE, SD_EVT_APP_ACTV, __do_nothing, SD_ST_APP_ACTV }, + { SD_ST_IDLE, SD_EVT_LNK_UP, __do_nothing, SD_ST_LNK_UP_PEND }, + + { SD_ST_LNK_UP_PEND, SD_EVT_APP_ACTV, __sd_cfg_pll_req, SD_ST_PLL_CFG }, + { SD_ST_LNK_UP_PEND, SD_EVT_LNK_DOWN, __do_nothing, SD_ST_IDLE }, + + { SD_ST_APP_ACTV, SD_EVT_APP_INACT, __do_nothing, SD_ST_IDLE }, + { SD_ST_APP_ACTV, SD_EVT_LNK_UP, __sd_cfg_pll_req, SD_ST_PLL_CFG }, + + { SD_ST_PLL_UNLOCKED, SD_EVT_PWR_DOWN_DONE, __sd_cfg_pll_req, SD_ST_PLL_CFG }, + { SD_ST_PLL_UNLOCKED, SD_EVT_APP_INACT, __do_nothing, SD_ST_LNK_UP_PEND }, + + { SD_ST_PLL_CFG, SD_EVT_PLL_LOCKED, __sd_ser_cfg_send, SD_ST_SER_CFG }, + { SD_ST_PLL_CFG, SD_EVT_PLL_UNLOCKED, __sd_cfg_pll_req, SD_ST_PLL_CFG }, + { SD_ST_PLL_CFG, SD_EVT_LNK_DOWN, __sd_lnk_down_notif, SD_ST_APP_ACTV }, + { SD_ST_PLL_CFG, SD_EVT_APP_INACT, __do_nothing, SD_ST_LNK_UP_PEND }, + { SD_ST_PLL_CFG, SD_EVT_LNK_UP, __sd_cfg_pll_req, SD_ST_PLL_CFG }, + { SD_ST_PLL_CFG, SD_EVT_PWR_DOWN_DONE, __sd_ser_cfg_send, SD_ST_SER_CFG }, + + { SD_ST_SER_CFG, SD_EVT_SER_RDY, __sd_deser_cfg_send, SD_ST_DESER_CFG }, + { SD_ST_SER_CFG, SD_EVT_APP_INACT, __sd_pwr_down, SD_ST_LNK_UP_PEND }, + { SD_ST_SER_CFG, SD_EVT_LNK_DOWN, __sd_pwr_down_notif_send, SD_ST_PWR_DOWN }, + { SD_ST_SER_CFG, SD_EVT_PLL_LOCKED, __sd_pwr_down_notif_send, SD_ST_PLL_CFG }, + { SD_ST_SER_CFG, SD_EVT_PLL_UNLOCKED, __sd_pwr_down_notif_send, SD_ST_PLL_UNLOCKED }, + { SD_ST_SER_CFG, SD_EVT_LNK_UP, __sd_pwr_down_notif_send, SD_ST_RATE_CHANGED }, + + { SD_ST_DESER_CFG, SD_EVT_DESER_RDY, __sd_deser_init_cal_send, SD_ST_CAL_STARTED }, + { SD_ST_DESER_CFG, SD_EVT_APP_INACT, __sd_pwr_down, SD_ST_LNK_UP_PEND }, + { SD_ST_DESER_CFG, SD_EVT_LNK_DOWN, __sd_pwr_down_notif_send, SD_ST_PWR_DOWN }, + { SD_ST_DESER_CFG, SD_EVT_PLL_LOCKED, __sd_pwr_down_notif_send, SD_ST_PLL_CFG }, + { SD_ST_DESER_CFG, SD_EVT_PLL_UNLOCKED, __sd_pwr_down_notif_send, SD_ST_PLL_UNLOCKED }, + { SD_ST_DESER_CFG, SD_EVT_LNK_UP, __sd_pwr_down_notif_send, SD_ST_RATE_CHANGED }, + + { SD_ST_CAL_STARTED, SD_EVT_SIGNAL_OK, __do_nothing, SD_ST_SIGNAL_OK }, + { SD_ST_CAL_STARTED, SD_EVT_APP_INACT, __sd_pwr_down, SD_ST_LNK_UP_PEND }, + { SD_ST_CAL_STARTED, SD_EVT_LNK_DOWN, __sd_pwr_down_notif_send, SD_ST_PWR_DOWN }, + { SD_ST_CAL_STARTED, SD_EVT_PLL_LOCKED, __sd_pwr_down_notif_send, SD_ST_PLL_CFG }, + { SD_ST_CAL_STARTED, SD_EVT_PLL_UNLOCKED, __sd_pwr_down_notif_send, SD_ST_PLL_UNLOCKED }, + { SD_ST_CAL_STARTED, SD_EVT_LNK_UP, __sd_pwr_down_notif_send, SD_ST_RATE_CHANGED }, + + { SD_ST_SIGNAL_OK, SD_EVT_LOS_DETECTED, __do_nothing, SD_ST_LOS }, + { SD_ST_SIGNAL_OK, SD_EVT_APP_INACT, __sd_pwr_down, SD_ST_LNK_UP_PEND }, + { SD_ST_SIGNAL_OK, SD_EVT_LNK_DOWN, __sd_pwr_down_notif_send, SD_ST_PWR_DOWN }, + { SD_ST_SIGNAL_OK, SD_EVT_PLL_LOCKED, __sd_pwr_down_notif_send, SD_ST_PLL_CFG }, + { SD_ST_SIGNAL_OK, SD_EVT_PLL_UNLOCKED, __sd_pwr_down_notif_send, SD_ST_PLL_UNLOCKED }, + { SD_ST_SIGNAL_OK, SD_EVT_LNK_UP, __sd_pwr_down_notif_send, SD_ST_RATE_CHANGED }, + + { SD_ST_LOS, SD_EVT_SIGNAL_OK, __do_nothing, SD_ST_SIGNAL_OK }, + { SD_ST_LOS, SD_EVT_APP_INACT, __sd_pwr_down, SD_ST_LNK_UP_PEND }, + { SD_ST_LOS, SD_EVT_LNK_DOWN, __sd_pwr_down_notif_send, SD_ST_PWR_DOWN }, + { SD_ST_LOS, SD_EVT_PLL_LOCKED, __sd_pwr_down_notif_send, SD_ST_PLL_CFG }, + { SD_ST_LOS, SD_EVT_PLL_UNLOCKED, __sd_pwr_down_notif_send, SD_ST_PLL_UNLOCKED }, + { SD_ST_LOS, SD_EVT_LNK_UP, __sd_pwr_down_notif_send, SD_ST_RATE_CHANGED }, + + { SD_ST_PWR_DOWN, SD_EVT_APP_INACT, __sd_pwr_down, SD_ST_IDLE }, + { SD_ST_PWR_DOWN, SD_EVT_PWR_DOWN_DONE, __sd_lnk_down_notif, SD_ST_APP_ACTV }, + { SD_ST_PWR_DOWN, SD_EVT_LNK_UP, __sd_cfg_pll_req, SD_ST_PLL_CFG }, + + { SD_ST_RATE_CHANGED, SD_EVT_APP_INACT, __sd_pwr_down, SD_ST_IDLE }, + { SD_ST_RATE_CHANGED, SD_EVT_PWR_DOWN_DONE, __sd_cfg_pll_req, SD_ST_PLL_CFG }, +}; + +static struct adrv906x_phy_fsm_tran adrv906x_phy_fsm_pll_trans[] = { + /* Source State Event Action Destination State */ + { PLL_ST_UNLOCKED, PLL_EVT_LNK0_10G_REQ, __pll_cfg_10G_send, PLL_ST_LNK0_10G_PEND }, + { PLL_ST_UNLOCKED, PLL_EVT_LNK1_10G_REQ, __pll_cfg_10G_send, PLL_ST_LNK1_10G_PEND }, + { PLL_ST_UNLOCKED, PLL_EVT_LNK0_25G_REQ, __pll_cfg_25G_send, PLL_ST_LNK0_25G_PEND }, + { PLL_ST_UNLOCKED, PLL_EVT_LNK1_25G_REQ, __pll_cfg_25G_send, PLL_ST_LNK1_25G_PEND }, + + { PLL_ST_10G_RUN, PLL_EVT_LNK0_10G_REQ, __pll_lkd_notif_lnk0, PLL_ST_10G_RUN }, + { PLL_ST_10G_RUN, PLL_EVT_LNK1_10G_REQ, __pll_lkd_notif_lnk1, PLL_ST_10G_RUN }, + { PLL_ST_10G_RUN, PLL_EVT_LNK0_25G_REQ, __pll_check_actv_lnks, PLL_ST_LNK0_25G_REQ }, + { PLL_ST_10G_RUN, PLL_EVT_LNK1_25G_REQ, __pll_check_actv_lnks, PLL_ST_LNK1_25G_REQ }, + { PLL_ST_10G_RUN, PLL_EVT_UNLOCKED, __pll_unlkd_notif_lnk01, PLL_ST_UNLOCKED }, + { PLL_ST_10G_RUN, PLL_EVT_APP_INACT, __do_nothing, PLL_ST_UNLOCKED }, + + { PLL_ST_25G_RUN, PLL_EVT_LNK0_25G_REQ, __pll_lkd_notif_lnk0, PLL_ST_25G_RUN }, + { PLL_ST_25G_RUN, PLL_EVT_LNK1_25G_REQ, __pll_lkd_notif_lnk1, PLL_ST_25G_RUN }, + { PLL_ST_25G_RUN, PLL_EVT_LNK0_10G_REQ, __pll_check_actv_lnks, PLL_ST_LNK0_10G_REQ }, + { PLL_ST_25G_RUN, PLL_EVT_LNK1_10G_REQ, __pll_check_actv_lnks, PLL_ST_LNK1_10G_REQ }, + { PLL_ST_25G_RUN, PLL_EVT_UNLOCKED, __pll_unlkd_notif_lnk01, PLL_ST_UNLOCKED }, + { PLL_ST_25G_RUN, PLL_EVT_APP_INACT, __do_nothing, PLL_ST_UNLOCKED }, + + { PLL_ST_LNK0_10G_REQ, PLL_EVT_LNK0_DOWN, __do_nothing, PLL_ST_25G_RUN }, + { PLL_ST_LNK0_10G_REQ, PLL_EVT_LNK0_25G_REQ, __pll_lkd_notif_lnk0, PLL_ST_25G_RUN }, + { PLL_ST_LNK0_10G_REQ, PLL_EVT_LNK1_DOWN, __pll_cfg_10G_send, PLL_ST_LNK0_10G_PEND }, + { PLL_ST_LNK0_10G_REQ, PLL_EVT_LNK1_10G_REQ, __pll_cfg_10G_send, PLL_ST_LNK01_10G_PEND }, + { PLL_ST_LNK0_10G_REQ, PLL_EVT_UNLOCKED, __pll_unlkd_notif_lnk01, PLL_ST_UNLOCKED }, + { PLL_ST_LNK0_10G_REQ, PLL_EVT_CFG_DONE, __pll_lkd_notif_lnk1, PLL_ST_LNK0_10G_REQ }, + + { PLL_ST_LNK1_10G_REQ, PLL_EVT_LNK1_DOWN, __do_nothing, PLL_ST_25G_RUN }, + { PLL_ST_LNK1_10G_REQ, PLL_EVT_LNK1_25G_REQ, __pll_lkd_notif_lnk1, PLL_ST_25G_RUN }, + { PLL_ST_LNK1_10G_REQ, PLL_EVT_LNK0_DOWN, __pll_cfg_10G_send, PLL_ST_LNK1_10G_PEND }, + { PLL_ST_LNK1_10G_REQ, PLL_EVT_LNK0_10G_REQ, __pll_cfg_10G_send, PLL_ST_LNK01_10G_PEND }, + { PLL_ST_LNK1_10G_REQ, PLL_EVT_UNLOCKED, __pll_unlkd_notif_lnk01, PLL_ST_UNLOCKED }, + { PLL_ST_LNK1_10G_REQ, PLL_EVT_CFG_DONE, __pll_lkd_notif_lnk0, PLL_ST_LNK1_10G_REQ }, + + { PLL_ST_LNK0_25G_REQ, PLL_EVT_LNK0_DOWN, __do_nothing, PLL_ST_10G_RUN }, + { PLL_ST_LNK0_25G_REQ, PLL_EVT_LNK0_10G_REQ, __pll_lkd_notif_lnk0, PLL_ST_10G_RUN }, + { PLL_ST_LNK0_25G_REQ, PLL_EVT_LNK1_DOWN, __pll_cfg_25G_send, PLL_ST_LNK0_25G_PEND }, + { PLL_ST_LNK0_25G_REQ, PLL_EVT_LNK1_25G_REQ, __pll_cfg_25G_send, PLL_ST_LNK01_25G_PEND }, + { PLL_ST_LNK0_25G_REQ, PLL_EVT_UNLOCKED, __pll_unlkd_notif_lnk01, PLL_ST_UNLOCKED }, + { PLL_ST_LNK0_25G_REQ, PLL_EVT_CFG_DONE, __pll_lkd_notif_lnk1, PLL_ST_LNK0_25G_REQ }, + + { PLL_ST_LNK1_25G_REQ, PLL_EVT_LNK1_DOWN, __do_nothing, PLL_ST_10G_RUN }, + { PLL_ST_LNK1_25G_REQ, PLL_EVT_LNK1_10G_REQ, __pll_lkd_notif_lnk1, PLL_ST_10G_RUN }, + { PLL_ST_LNK1_25G_REQ, PLL_EVT_LNK0_DOWN, __pll_cfg_25G_send, PLL_ST_LNK1_25G_PEND }, + { PLL_ST_LNK1_25G_REQ, PLL_EVT_LNK0_25G_REQ, __pll_cfg_25G_send, PLL_ST_LNK01_25G_PEND }, + { PLL_ST_LNK1_25G_REQ, PLL_EVT_UNLOCKED, __pll_unlkd_notif_lnk01, PLL_ST_UNLOCKED }, + { PLL_ST_LNK1_25G_REQ, PLL_EVT_CFG_DONE, __pll_lkd_notif_lnk0, PLL_ST_LNK1_25G_REQ }, + + { PLL_ST_LNK0_10G_PEND, PLL_EVT_CFG_DONE, __pll_lkd_notif_lnk0, PLL_ST_10G_RUN }, + { PLL_ST_LNK0_10G_PEND, PLL_EVT_APP_INACT, __do_nothing, PLL_ST_UNLOCKED }, + { PLL_ST_LNK0_10G_PEND, PLL_EVT_UNLOCKED, __pll_unlkd_notif_lnk01, PLL_ST_UNLOCKED }, + { PLL_ST_LNK0_10G_PEND, PLL_EVT_LNK1_10G_REQ, __do_nothing, PLL_ST_LNK01_10G_PEND }, + { PLL_ST_LNK0_10G_PEND, PLL_EVT_LNK1_25G_REQ, __do_nothing, PLL_ST_LNK1_25G_REQ }, + + { PLL_ST_LNK1_10G_PEND, PLL_EVT_CFG_DONE, __pll_lkd_notif_lnk1, PLL_ST_10G_RUN }, + { PLL_ST_LNK1_10G_PEND, PLL_EVT_APP_INACT, __do_nothing, PLL_ST_UNLOCKED }, + { PLL_ST_LNK1_10G_PEND, PLL_EVT_UNLOCKED, __pll_unlkd_notif_lnk01, PLL_ST_UNLOCKED }, + { PLL_ST_LNK1_10G_PEND, PLL_EVT_LNK0_10G_REQ, __do_nothing, PLL_ST_LNK01_10G_PEND }, + { PLL_ST_LNK1_10G_PEND, PLL_EVT_LNK0_25G_REQ, __do_nothing, PLL_ST_LNK0_25G_REQ }, + + { PLL_ST_LNK01_10G_PEND, PLL_EVT_CFG_DONE, __pll_lkd_notif_lnk01, PLL_ST_10G_RUN }, + { PLL_ST_LNK01_10G_PEND, PLL_EVT_APP_INACT, __do_nothing, PLL_ST_UNLOCKED }, + { PLL_ST_LNK01_10G_PEND, PLL_EVT_UNLOCKED, __pll_unlkd_notif_lnk01, PLL_ST_UNLOCKED }, + { PLL_ST_LNK01_10G_PEND, PLL_EVT_LNK1_25G_REQ, __do_nothing, PLL_ST_LNK1_25G_REQ }, + { PLL_ST_LNK01_10G_PEND, PLL_EVT_LNK0_25G_REQ, __do_nothing, PLL_ST_LNK0_25G_REQ }, + + { PLL_ST_LNK0_25G_PEND, PLL_EVT_CFG_DONE, __pll_lkd_notif_lnk0, PLL_ST_25G_RUN }, + { PLL_ST_LNK0_25G_PEND, PLL_EVT_APP_INACT, __do_nothing, PLL_ST_UNLOCKED }, + { PLL_ST_LNK0_25G_PEND, PLL_EVT_UNLOCKED, __pll_unlkd_notif_lnk01, PLL_ST_UNLOCKED }, + { PLL_ST_LNK0_25G_PEND, PLL_EVT_LNK1_25G_REQ, __do_nothing, PLL_ST_LNK01_25G_PEND }, + { PLL_ST_LNK0_25G_PEND, PLL_EVT_LNK1_10G_REQ, __do_nothing, PLL_ST_LNK1_10G_REQ }, + + { PLL_ST_LNK1_25G_PEND, PLL_EVT_CFG_DONE, __pll_lkd_notif_lnk1, PLL_ST_25G_RUN }, + { PLL_ST_LNK1_25G_PEND, PLL_EVT_APP_INACT, __do_nothing, PLL_ST_UNLOCKED }, + { PLL_ST_LNK1_25G_PEND, PLL_EVT_UNLOCKED, __pll_unlkd_notif_lnk01, PLL_ST_UNLOCKED }, + { PLL_ST_LNK1_25G_PEND, PLL_EVT_LNK0_25G_REQ, __do_nothing, PLL_ST_LNK01_25G_PEND }, + { PLL_ST_LNK1_25G_PEND, PLL_EVT_LNK0_10G_REQ, __do_nothing, PLL_ST_LNK0_10G_REQ }, + + { PLL_ST_LNK01_25G_PEND, PLL_EVT_CFG_DONE, __pll_lkd_notif_lnk01, PLL_ST_25G_RUN }, + { PLL_ST_LNK01_25G_PEND, PLL_EVT_APP_INACT, __do_nothing, PLL_ST_UNLOCKED }, + { PLL_ST_LNK01_25G_PEND, PLL_EVT_UNLOCKED, __pll_unlkd_notif_lnk01, PLL_ST_UNLOCKED }, + { PLL_ST_LNK01_25G_PEND, PLL_EVT_LNK1_10G_REQ, __do_nothing, PLL_ST_LNK1_10G_REQ }, + { PLL_ST_LNK01_25G_PEND, PLL_EVT_LNK0_10G_REQ, __do_nothing, PLL_ST_LNK0_10G_REQ }, +}; + +static struct nla_policy adrv906x_phy_genl_policy[ATTR_MAX + 1] = { + [ATTR_CMD_PAYLOAD] = { .type = NLA_U32 }, +}; + +static const struct genl_small_ops adrv906x_phy_genl_ops[] = { + { + .cmd = NL_CMD_PLL_CFG_DONE, + .doit = __pll_cfg_done_recv, + }, + { + .cmd = NL_CMD_PLL_RELOCK_SUCCEED, + .doit = __pll_relock_succeed_recv, + }, + { + .cmd = NL_CMD_PLL_RELOCK_FAILED, + .doit = __pll_relock_failed_recv, + }, + { + .cmd = NL_CMD_SER_CFG_DONE, + .doit = __sd_ser_cfg_done_recv, + }, + { + .cmd = NL_CMD_DESER_CFG_DONE, + .doit = __sd_deser_cfg_done_recv, + }, + { + .cmd = NL_CMD_DESER_SIGNAL_OK, + .doit = __sd_deser_signal_ok_recv, + }, + { + .cmd = NL_CMD_DESER_LOS, + .doit = __sd_deser_los_detected_recv, + }, + { + .cmd = NL_CMD_SERDES_PWR_DOWN_RDY, + .doit = __sd_app_pwr_down_rdy_recv, + }, + { + .cmd = NL_CMD_SERDES_APP_HEARTBEAT, + .doit = __sd_app_heartbeat_recv, + }, +}; + +static const struct genl_multicast_group adrv906x_phy_genl_mcgrps[] = { + { .name = ADRV906X_GENL_MC_GRP_NAME }, +}; + +static struct genl_family adrv906x_phy_genl_fam __ro_after_init = { + .name = ADRV906X_GENL_NAME, + .hdrsize = 0, + .version = ADRV906X_GENL_VERSION, + .maxattr = ATTR_MAX, + .policy = adrv906x_phy_genl_policy, + .module = THIS_MODULE, + .small_ops = adrv906x_phy_genl_ops, + .n_small_ops = ARRAY_SIZE(adrv906x_phy_genl_ops), + .mcgrps = adrv906x_phy_genl_mcgrps, + .n_mcgrps = ARRAY_SIZE(adrv906x_phy_genl_mcgrps), +}; + +static DECLARE_DELAYED_WORK(adrv_906x_app_watchdog_task, __sd_app_watchdog_expired); + +static struct adrv906x_serdes adrv906x_serdes_devs[ADRV906X_PHY_MAX_LANES] = { + [0] = { .dev_id = -1 }, + [1] = { .dev_id = -1 }, + [2] = { .dev_id = -1 }, + [3] = { .dev_id = -1 }, +}; + +static struct adrv906x_pll adrv906x_pll_dev[ADRV906X_PHY_MAX_PLLS] = { + [0] = { .dev_id = -1, .mtx = __MUTEX_INITIALIZER(adrv906x_pll_dev[0].mtx) }, + [1] = { .dev_id = -1, .mtx = __MUTEX_INITIALIZER(adrv906x_pll_dev[1].mtx) }, +}; + +static struct adrv906x_serdes *adrv906x_serdes_instance_get(int dev_id) +{ + if (dev_id >= ADRV906X_PHY_MAX_LANES) + return NULL; + + return &adrv906x_serdes_devs[dev_id]; +} + +static struct adrv906x_pll *adrv906x_pll_instance_get(int dev_id) +{ + if (dev_id >= ADRV906X_PHY_MAX_PLLS) + return NULL; + + return &adrv906x_pll_dev[dev_id]; +} + +static char *adrv906x_serdes_state_to_str(u32 state) +{ + switch (state) { + case SD_ST_IDLE: return "IDLE"; + case SD_ST_LNK_UP_PEND: return "LNK_UP_PEND"; + case SD_ST_APP_ACTV: return "APP_ACTV"; + case SD_ST_PLL_UNLOCKED: return "PLL_UNLOCKED"; + case SD_ST_PLL_CFG: return "PLL_CFG"; + case SD_ST_RATE_CHANGED: return "RATE_CHANGED"; + case SD_ST_SER_CFG: return "SER_CFG"; + case SD_ST_DESER_CFG: return "DESER_CFG"; + case SD_ST_CAL_STARTED: return "CAL_STARTED"; + case SD_ST_LOS: return "LOS"; + case SD_ST_SIGNAL_OK: return "SIGNAL_OK"; + case SD_ST_PWR_DOWN: return "PWR_DOWN"; + default: return "UNKNOWN"; + } +} + +static char *adrv906x_serdes_event_to_str(u32 event) +{ + switch (event) { + case SD_EVT_APP_ACTV: return "APP_ACTV"; + case SD_EVT_APP_INACT: return "APP_INACT"; + case SD_EVT_LNK_UP: return "LNK_UP"; + case SD_EVT_PLL_LOCKED: return "PLL_LOCKED"; + case SD_EVT_PLL_UNLOCKED: return "PLL_UNLOCKED"; + case SD_EVT_SER_RDY: return "SER_RDY"; + case SD_EVT_DESER_RDY: return "DESER_RDY"; + case SD_EVT_LNK_DOWN: return "LNK_DOWN"; + case SD_EVT_PWR_DOWN_DONE: return "PWR_DOWN_DONE"; + case SD_EVT_SIGNAL_OK: return "SIGNAL_OK"; + case SD_EVT_LOS_DETECTED: return "LOS_DETECTED"; + default: return "UNKNOWN"; + } +} + +static char *adrv906x_pll_state_to_str(u32 state) +{ + switch (state) { + case PLL_ST_UNLOCKED: return "UNLOCKED"; + case PLL_ST_10G_RUN: return "10G_RUN"; + case PLL_ST_25G_RUN: return "25G_RUN"; + case PLL_ST_LNK0_10G_REQ: return "LNK0_10G_REQ"; + case PLL_ST_LNK1_10G_REQ: return "LNK1_10G_REQ"; + case PLL_ST_LNK0_25G_REQ: return "LNK0_25G_REQ"; + case PLL_ST_LNK1_25G_REQ: return "LNK1_25G_REQ"; + case PLL_ST_LNK0_10G_PEND: return "LNK0_10G_PEND"; + case PLL_ST_LNK01_10G_PEND: return "LNK01_10G_PEND"; + case PLL_ST_LNK1_10G_PEND: return "LNK1_10G_PEND"; + case PLL_ST_LNK0_25G_PEND: return "LNK0_25G_PEND"; + case PLL_ST_LNK1_25G_PEND: return "LNK1_25G_PEND"; + case PLL_ST_LNK01_25G_PEND: return "LNK01_25G_PEND"; + default: return "UNKNOWN"; + } +} + +static char *adrv906x_pll_event_to_str(u32 event) +{ + switch (event) { + case PLL_EVT_UNLOCKED: return "UNLOCKED"; + case PLL_EVT_APP_ACTV: return "APP_ACTV"; + case PLL_EVT_APP_INACT: return "APP_INACT"; + case PLL_EVT_LNK0_10G_REQ: return "LNK0_10G_REQ"; + case PLL_EVT_LNK1_10G_REQ: return "LNK1_10G_REQ"; + case PLL_EVT_LNK0_25G_REQ: return "LNK0_25G_REQ"; + case PLL_EVT_LNK1_25G_REQ: return "LNK1_25G_REQ"; + case PLL_EVT_CFG_DONE: return "CFG_DONE"; + case PLL_EVT_LNK0_DOWN: return "LNK0_DOWN"; + case PLL_EVT_LNK1_DOWN: return "LNK1_DOWN"; + default: return "UNKNOWN"; + } +} + +int adrv906x_serdes_genl_register_family(void) +{ + return genl_register_family(&adrv906x_phy_genl_fam); +} + +int adrv906x_serdes_genl_unregister_family(void) +{ + return genl_unregister_family(&adrv906x_phy_genl_fam); +} + +static int adrv906x_phy_send_message(u32 cmd, u32 dev_id, u32 speed) +{ + struct sk_buff *skb; + void *hdr; + u32 data; + int ret; + + data = FIELD_PREP(ADRV906X_PHY_DEV_IDX_MSK, dev_id) + | FIELD_PREP(ADRV906X_PHY_DEV_SPEED_MSK, speed); + + skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (unlikely(!skb)) + return -ENOMEM; + + hdr = genlmsg_put(skb, 0, 0, &adrv906x_phy_genl_fam, 0, cmd); + if (unlikely(!hdr)) { + nlmsg_free(skb); + return -ENOMEM; + } + + ret = nla_put_u32(skb, ATTR_CMD_PAYLOAD, data); + if (ret) { + genlmsg_cancel(skb, hdr); + nlmsg_free(skb); + return ret; + } + + genlmsg_end(skb, hdr); + + mutex_lock(&genl_mutex); + ret = genlmsg_multicast(&adrv906x_phy_genl_fam, skb, 0, 0, GFP_KERNEL); + mutex_unlock(&genl_mutex); + + return ret; +} + +static void adrv906x_phy_fsm_trigger_transition(struct adrv906x_phy_fsm *fsm, int event) +{ + int ret; + + if (!kfifo_initialized(&fsm->event_fifo)) + return; + + ret = kfifo_in_spinlocked(&fsm->event_fifo, &event, sizeof(event), &fsm->event_fifo_lock); + if (!ret) + return; + + complete(&fsm->comp_tran); +} + +static int adrv906x_phy_fsm_handle_transition(void *data) +{ + struct adrv906x_phy_fsm *fsm = data; + struct adrv906x_phy_fsm_tran *tran; + adrv906x_phy_fsm_action action; + int i, event, ret; + + while (!kthread_should_stop()) { + if (!wait_for_completion_timeout(&fsm->comp_tran, msecs_to_jiffies(50))) + continue; + + action = NULL; + + ret = kfifo_out_spinlocked(&fsm->event_fifo, &event, sizeof(event), &fsm->event_fifo_lock); + if (!ret) + continue; + + for (i = 0; i < fsm->tran_tbl_size; i++) { + tran = &fsm->tran_tbl[i]; + + if (tran->src_state == atomic_read(&fsm->state) && tran->event == event) { + pr_debug("[adrv906x] %s, event: %s, transition: %s -> %s", + fsm->name, + fsm->event_to_str(event), + fsm->state_to_str(atomic_read(&fsm->state)), + fsm->state_to_str(tran->dst_state)); + atomic_set(&fsm->state, tran->dst_state); + action = tran->action; + break; + } + } + + if (action) + action(fsm); + } + + return 0; +} + +static int adrv906x_phy_parse_message(struct genl_info *info, u32 *dev_id, u32 *speed) +{ + u32 data; + + if (!info->attrs[ATTR_CMD_PAYLOAD]) + return -EINVAL; + + data = nla_get_u32(info->attrs[ATTR_CMD_PAYLOAD]); + *dev_id = FIELD_GET(ADRV906X_PHY_DEV_IDX_MSK, data); + *speed = FIELD_GET(ADRV906X_PHY_DEV_SPEED_MSK, data); + + return 0; +} + +static int __pll_cfg_done_recv(struct sk_buff *skb, struct genl_info *info) +{ + struct adrv906x_pll *pll; + u32 dev_id, speed; + int ret; + + ret = adrv906x_phy_parse_message(info, &dev_id, &speed); + if (ret) + return ret; + + if (dev_id >= ADRV906X_PHY_MAX_PLLS) + return -EINVAL; + + pll = adrv906x_pll_instance_get(dev_id); + adrv906x_phy_fsm_trigger_transition(&pll->fsm, PLL_EVT_CFG_DONE); + + return 0; +} + +static int __pll_relock_succeed_recv(struct sk_buff *skb, struct genl_info *info) +{ + struct adrv906x_serdes *serdes0; + struct adrv906x_serdes *serdes1; + u32 dev_id, speed; + int ret; + + ret = adrv906x_phy_parse_message(info, &dev_id, &speed); + if (ret) + return ret; + + if (dev_id >= ADRV906X_PHY_MAX_PLLS) + return -EINVAL; + + if (speed != SPEED_10000 && speed != SPEED_25000) + return -EINVAL; + + serdes0 = adrv906x_serdes_instance_get(2 * dev_id); + serdes1 = adrv906x_serdes_instance_get(2 * dev_id + 1); + + adrv906x_phy_fsm_trigger_transition(&serdes0->fsm, SD_EVT_PLL_LOCKED); + adrv906x_phy_fsm_trigger_transition(&serdes1->fsm, SD_EVT_PLL_LOCKED); + + return 0; +} + +static int __pll_relock_failed_recv(struct sk_buff *skb, struct genl_info *info) +{ + struct adrv906x_pll *pll; + u32 dev_id, speed; + int ret; + + ret = adrv906x_phy_parse_message(info, &dev_id, &speed); + if (ret) + return ret; + + if (dev_id >= ADRV906X_PHY_MAX_PLLS) + return -EINVAL; + + pll = adrv906x_pll_instance_get(dev_id); + adrv906x_phy_fsm_trigger_transition(&pll->fsm, PLL_EVT_UNLOCKED); + + return 0; +} + +static int __sd_app_heartbeat_recv(struct sk_buff *skb, struct genl_info *info) +{ + struct adrv906x_serdes *serdes0 = adrv906x_serdes_instance_get(0); + struct adrv906x_serdes *serdes1 = adrv906x_serdes_instance_get(1); + struct adrv906x_serdes *serdes2 = adrv906x_serdes_instance_get(2); + struct adrv906x_serdes *serdes3 = adrv906x_serdes_instance_get(3); + struct adrv906x_pll *pll0 = adrv906x_pll_instance_get(0); + struct adrv906x_pll *pll1 = adrv906x_pll_instance_get(1); + + mod_delayed_work(system_long_wq, &adrv_906x_app_watchdog_task, + msecs_to_jiffies(APP_HEARTBEAT_TIMEOUT_MS)); + + adrv906x_phy_fsm_trigger_transition(&pll0->fsm, PLL_EVT_APP_ACTV); + adrv906x_phy_fsm_trigger_transition(&pll1->fsm, PLL_EVT_APP_ACTV); + adrv906x_phy_fsm_trigger_transition(&serdes0->fsm, SD_EVT_APP_ACTV); + adrv906x_phy_fsm_trigger_transition(&serdes1->fsm, SD_EVT_APP_ACTV); + adrv906x_phy_fsm_trigger_transition(&serdes2->fsm, SD_EVT_APP_ACTV); + adrv906x_phy_fsm_trigger_transition(&serdes3->fsm, SD_EVT_APP_ACTV); + + return 0; +} + +static void __sd_app_watchdog_expired(struct work_struct *work) +{ + struct adrv906x_serdes *serdes0 = adrv906x_serdes_instance_get(0); + struct adrv906x_serdes *serdes1 = adrv906x_serdes_instance_get(1); + struct adrv906x_serdes *serdes2 = adrv906x_serdes_instance_get(2); + struct adrv906x_serdes *serdes3 = adrv906x_serdes_instance_get(3); + struct adrv906x_pll *pll0 = adrv906x_pll_instance_get(0); + struct adrv906x_pll *pll1 = adrv906x_pll_instance_get(1); + + adrv906x_phy_fsm_trigger_transition(&pll0->fsm, PLL_EVT_APP_INACT); + adrv906x_phy_fsm_trigger_transition(&pll1->fsm, PLL_EVT_APP_INACT); + adrv906x_phy_fsm_trigger_transition(&serdes0->fsm, SD_EVT_APP_INACT); + adrv906x_phy_fsm_trigger_transition(&serdes1->fsm, SD_EVT_APP_INACT); + adrv906x_phy_fsm_trigger_transition(&serdes2->fsm, SD_EVT_APP_INACT); + adrv906x_phy_fsm_trigger_transition(&serdes3->fsm, SD_EVT_APP_INACT); +} + +static int __sd_ser_cfg_done_recv(struct sk_buff *skb, struct genl_info *info) +{ + struct adrv906x_serdes *serdes; + struct phy_device *phydev; + struct net_device *netdev; + u32 dev_id, speed; + int ret; + + ret = adrv906x_phy_parse_message(info, &dev_id, &speed); + if (ret) + return ret; + + if (dev_id >= ADRV906X_PHY_MAX_LANES) + return -EINVAL; + + if (speed != SPEED_10000 && speed != SPEED_25000) + return -EINVAL; + + serdes = adrv906x_serdes_instance_get(dev_id); + phydev = serdes->phydev; + netdev = phydev->attached_dev; + + serdes->tx_path_en(phydev, true); + adrv906x_eth_cmn_ser_tx_sync_trigger(netdev); + adrv906x_phy_fsm_trigger_transition(&serdes->fsm, SD_EVT_SER_RDY); + + return 0; +} + +static int __sd_deser_cfg_done_recv(struct sk_buff *skb, struct genl_info *info) +{ + struct adrv906x_serdes *serdes; + u32 dev_id, speed; + int ret; + + ret = adrv906x_phy_parse_message(info, &dev_id, &speed); + if (ret) + return ret; + + if (dev_id >= ADRV906X_PHY_MAX_LANES) + return -EINVAL; + + if (speed != SPEED_10000 && speed != SPEED_25000) + return -EINVAL; + + serdes = adrv906x_serdes_instance_get(dev_id); + adrv906x_phy_fsm_trigger_transition(&serdes->fsm, SD_EVT_DESER_RDY); + + return 0; +} + +static int __sd_deser_signal_ok_recv(struct sk_buff *skb, struct genl_info *info) +{ + struct adrv906x_serdes *serdes; + struct phy_device *phydev; + struct net_device *netdev; + u32 dev_id, speed; + int ret; + + ret = adrv906x_phy_parse_message(info, &dev_id, &speed); + if (ret) + return ret; + + if (dev_id >= ADRV906X_PHY_MAX_LANES) + return -EINVAL; + + if (speed != SPEED_10000 && speed != SPEED_25000) + return -EINVAL; + + serdes = adrv906x_serdes_instance_get(dev_id); + phydev = serdes->phydev; + netdev = phydev->attached_dev; + + /* Reset PCS RX/TX data path */ + serdes->rx_path_en(phydev, false); + serdes->rx_path_en(phydev, true); + serdes->tx_path_en(phydev, false); + serdes->tx_path_en(phydev, true); + + adrv906x_phy_fsm_trigger_transition(&serdes->fsm, SD_EVT_SIGNAL_OK); + + return 0; +} + +static int __sd_app_pwr_down_rdy_recv(struct sk_buff *skb, struct genl_info *info) +{ + struct adrv906x_serdes *serdes; + struct phy_device *phydev; + struct net_device *netdev; + u32 dev_id, speed; + int ret; + + ret = adrv906x_phy_parse_message(info, &dev_id, &speed); + if (ret) + return ret; + + if (dev_id >= ADRV906X_PHY_MAX_LANES) + return -EINVAL; + + if (speed != SPEED_10000 && speed != SPEED_25000) + return -EINVAL; + + serdes = adrv906x_serdes_instance_get(dev_id); + phydev = serdes->phydev; + netdev = phydev->attached_dev; + + adrv906x_phy_fsm_trigger_transition(&serdes->fsm, SD_EVT_PWR_DOWN_DONE); + + return 0; +} + +static int __sd_deser_los_detected_recv(struct sk_buff *skb, struct genl_info *info) +{ + struct adrv906x_serdes *serdes; + struct phy_device *phydev; + struct net_device *netdev; + u32 dev_id, speed; + int ret; + + ret = adrv906x_phy_parse_message(info, &dev_id, &speed); + if (ret) + return ret; + + if (dev_id >= ADRV906X_PHY_MAX_LANES) + return -EINVAL; + + if (speed != SPEED_10000 && speed != SPEED_25000) + return -EINVAL; + + serdes = adrv906x_serdes_instance_get(dev_id); + phydev = serdes->phydev; + netdev = phydev->attached_dev; + serdes->rx_path_en(phydev, false); + + adrv906x_phy_fsm_trigger_transition(&serdes->fsm, SD_EVT_LOS_DETECTED); + + return 0; +} + +static void __sd_pwr_down(void *param) +{ + struct adrv906x_phy_fsm *serdes_fsm = param; + struct adrv906x_serdes *serdes = container_of(serdes_fsm, struct adrv906x_serdes, fsm); + struct phy_device *phydev = serdes->phydev; + struct net_device *netdev = phydev->attached_dev; + + adrv906x_eth_cmn_ser_pwr_down(netdev); + adrv906x_eth_cmn_deser_pwr_down(netdev); + serdes->rx_path_en(phydev, false); + serdes->tx_path_en(phydev, false); +} + +static void __sd_cfg_pll_req(void *param) +{ + struct adrv906x_phy_fsm *serdes_fsm = param; + struct adrv906x_serdes *serdes = container_of(serdes_fsm, struct adrv906x_serdes, fsm); + struct adrv906x_pll *pll; + int event = PLL_EVT_UNKNOWN; + + if (serdes->speed == SPEED_10000 && serdes->dev_id % 2 == 0) + event = PLL_EVT_LNK0_10G_REQ; + else if (serdes->speed == SPEED_10000 && serdes->dev_id % 2 == 1) + event = PLL_EVT_LNK1_10G_REQ; + else if (serdes->speed == SPEED_25000 && serdes->dev_id % 2 == 0) + event = PLL_EVT_LNK0_25G_REQ; + else if (serdes->speed == SPEED_25000 && serdes->dev_id % 2 == 1) + event = PLL_EVT_LNK1_25G_REQ; + + pll = adrv906x_pll_instance_get(serdes->dev_id / 2); + adrv906x_phy_fsm_trigger_transition(&pll->fsm, event); +} + +static void __sd_lnk_down_notif(void *param) +{ + struct adrv906x_phy_fsm *serdes_fsm = param; + struct adrv906x_serdes *serdes = container_of(serdes_fsm, struct adrv906x_serdes, fsm); + struct adrv906x_pll *pll; + int event = PLL_EVT_UNKNOWN; + + if (serdes->dev_id % 2 == 0) + event = PLL_EVT_LNK0_DOWN; + else if (serdes->dev_id % 2 == 1) + event = PLL_EVT_LNK1_DOWN; + + __sd_pwr_down(&serdes->fsm); + pll = adrv906x_pll_instance_get(serdes->dev_id / 2); + adrv906x_phy_fsm_trigger_transition(&pll->fsm, event); +} + +static void __sd_ser_cfg_send(void *param) +{ + struct adrv906x_phy_fsm *fsm = param; + struct adrv906x_serdes *serdes = container_of(fsm, struct adrv906x_serdes, fsm); + struct phy_device *phydev = serdes->phydev; + struct net_device *netdev = phydev->attached_dev; + int ret; + + adrv906x_eth_cmn_ser_pwr_up_and_reset(netdev); + ret = adrv906x_phy_send_message(NL_CMD_SER_CFG_REQ, serdes->dev_id, serdes->speed); + if (ret) + adrv906x_phy_fsm_trigger_transition(fsm, SD_EVT_APP_INACT); +} + +static void __sd_deser_cfg_send(void *param) +{ + struct adrv906x_phy_fsm *fsm = param; + struct adrv906x_serdes *serdes = container_of(fsm, struct adrv906x_serdes, fsm); + struct phy_device *phydev = serdes->phydev; + struct net_device *netdev = phydev->attached_dev; + int ret; + + adrv906x_eth_cmn_deser_pwr_up_and_reset(netdev); + ret = adrv906x_phy_send_message(NL_CMD_DESER_CFG_REQ, serdes->dev_id, serdes->speed); + if (ret) + adrv906x_phy_fsm_trigger_transition(fsm, SD_EVT_APP_INACT); +} + +static void __sd_deser_init_cal_send(void *param) +{ + struct adrv906x_phy_fsm *fsm = param; + struct adrv906x_serdes *serdes = container_of(fsm, struct adrv906x_serdes, fsm); + int ret; + + ret = adrv906x_phy_send_message(NL_CMD_DESER_INIT_CAL_REQ, serdes->dev_id, serdes->speed); + if (ret) + adrv906x_phy_fsm_trigger_transition(fsm, SD_EVT_APP_INACT); +} + +static void __sd_pwr_down_notif_send(void *param) +{ + struct adrv906x_phy_fsm *fsm = param; + struct adrv906x_serdes *serdes = container_of(fsm, struct adrv906x_serdes, fsm); + int ret; + + ret = adrv906x_phy_send_message(NL_CMD_SERDES_PWR_DOWN_REQ, serdes->dev_id, serdes->speed); + if (ret) + adrv906x_phy_fsm_trigger_transition(fsm, SD_EVT_APP_INACT); +} + +static void __do_nothing(void *param) +{ +} + +int adrv906x_serdes_lnk_up_req(struct phy_device *phydev) +{ + struct adrv906x_serdes *serdes; + struct net_device *netdev; + int dev_id = phydev->mdio.addr; + + netdev = phydev->attached_dev; + serdes = adrv906x_serdes_instance_get(dev_id); + if (!serdes) + return -EINVAL; + + serdes->rx_path_en(phydev, false); + serdes->tx_path_en(phydev, false); + serdes->speed = phydev->speed; + + adrv906x_phy_fsm_trigger_transition(&serdes->fsm, SD_EVT_LNK_UP); + + return 0; +} + +int adrv906x_serdes_lnk_down_req(struct phy_device *phydev) +{ + struct adrv906x_serdes *serdes; + int dev_id = phydev->mdio.addr; + + serdes = adrv906x_serdes_instance_get(dev_id); + if (!serdes) + return -EINVAL; + + adrv906x_phy_fsm_trigger_transition(&serdes->fsm, SD_EVT_LNK_DOWN); + + return 0; +} + +static bool adrv906x_serdes_disabled(int dev_id) +{ + struct adrv906x_serdes *serdes = adrv906x_serdes_instance_get(dev_id); + + switch (atomic_read(&serdes->fsm.state)) { + case SD_ST_SER_CFG: + case SD_ST_DESER_CFG: + case SD_ST_CAL_STARTED: + case SD_ST_LOS: + case SD_ST_SIGNAL_OK: + return false; + default: + return true; + } +} + +static void __pll_unlkd_notif_lnk01(void *param) +{ + struct adrv906x_phy_fsm *fsm = param; + struct adrv906x_pll *pll = container_of(fsm, struct adrv906x_pll, fsm); + struct adrv906x_serdes *serdes0; + struct adrv906x_serdes *serdes1; + + serdes0 = adrv906x_serdes_instance_get(2 * pll->dev_id); + serdes1 = adrv906x_serdes_instance_get(2 * pll->dev_id + 1); + + adrv906x_phy_fsm_trigger_transition(&serdes0->fsm, SD_EVT_PLL_UNLOCKED); + adrv906x_phy_fsm_trigger_transition(&serdes1->fsm, SD_EVT_PLL_UNLOCKED); +} + +static void __pll_lkd_notif_lnk0(void *param) +{ + struct adrv906x_phy_fsm *fsm = param; + struct adrv906x_pll *pll = container_of(fsm, struct adrv906x_pll, fsm); + struct adrv906x_serdes *serdes; + + serdes = adrv906x_serdes_instance_get(2 * pll->dev_id); + + adrv906x_phy_fsm_trigger_transition(&serdes->fsm, SD_EVT_PLL_LOCKED); +} + +static void __pll_lkd_notif_lnk1(void *param) +{ + struct adrv906x_phy_fsm *fsm = param; + struct adrv906x_pll *pll = container_of(fsm, struct adrv906x_pll, fsm); + struct adrv906x_serdes *serdes; + + serdes = adrv906x_serdes_instance_get(2 * pll->dev_id + 1); + + adrv906x_phy_fsm_trigger_transition(&serdes->fsm, SD_EVT_PLL_LOCKED); +} + +static void __pll_lkd_notif_lnk01(void *param) +{ + __pll_lkd_notif_lnk0(param); + __pll_lkd_notif_lnk1(param); +} + +static void __pll_check_actv_lnks(void *param) +{ + struct adrv906x_phy_fsm *fsm = param; + struct adrv906x_pll *pll = container_of(fsm, struct adrv906x_pll, fsm); + int event = PLL_EVT_UNKNOWN; + + if ((atomic_read(&fsm->state) == PLL_ST_LNK0_10G_REQ || atomic_read(&fsm->state) == PLL_ST_LNK0_25G_REQ) + && adrv906x_serdes_disabled(2 * pll->dev_id + 1)) + event = PLL_EVT_LNK1_DOWN; + else if ((atomic_read(&fsm->state) == PLL_ST_LNK1_10G_REQ || atomic_read(&fsm->state) == PLL_ST_LNK1_25G_REQ) + && adrv906x_serdes_disabled(2 * pll->dev_id)) + event = PLL_EVT_LNK0_DOWN; + + adrv906x_phy_fsm_trigger_transition(fsm, event); +} + +static void __pll_cfg_10G_send(void *param) +{ + struct adrv906x_phy_fsm *fsm = param; + struct adrv906x_pll *pll = container_of(fsm, struct adrv906x_pll, fsm); + struct adrv906x_serdes *serdes; + struct phy_device *phydev; + struct net_device *netdev; + int ret; + + if (adrv906x_serdes_instance_get(2 * pll->dev_id)) + serdes = adrv906x_serdes_instance_get(2 * pll->dev_id); + else + serdes = adrv906x_serdes_instance_get(2 * pll->dev_id + 1); + + phydev = serdes->phydev; + netdev = phydev->attached_dev; + + adrv906x_eth_cmn_pll_reset(netdev); + adrv906x_eth_cmn_mode_cfg(netdev); + ret = adrv906x_phy_send_message(NL_CMD_PLL_CFG_REQ, pll->dev_id, SPEED_10000); + if (ret) + adrv906x_phy_fsm_trigger_transition(fsm, PLL_EVT_APP_INACT); +} + +static void __pll_cfg_25G_send(void *param) +{ + struct adrv906x_phy_fsm *fsm = param; + struct adrv906x_pll *pll = container_of(fsm, struct adrv906x_pll, fsm); + struct adrv906x_serdes *serdes; + struct phy_device *phydev; + struct net_device *netdev; + int ret; + + if (adrv906x_serdes_instance_get(2 * pll->dev_id)) + serdes = adrv906x_serdes_instance_get(2 * pll->dev_id); + else + serdes = adrv906x_serdes_instance_get(2 * pll->dev_id + 1); + + phydev = serdes->phydev; + netdev = phydev->attached_dev; + + adrv906x_eth_cmn_pll_reset(netdev); + adrv906x_eth_cmn_mode_cfg(netdev); + ret = adrv906x_phy_send_message(NL_CMD_PLL_CFG_REQ, pll->dev_id, SPEED_25000); + if (ret) + adrv906x_phy_fsm_trigger_transition(fsm, PLL_EVT_APP_INACT); +} + +static int adrv906x_pll_open(int dev_id) +{ + struct adrv906x_pll *pll = adrv906x_pll_instance_get(dev_id); + int ret; + + mutex_lock(&pll->mtx); + if (!pll->started) { + init_completion(&pll->fsm.comp_tran); + spin_lock_init(&pll->fsm.event_fifo_lock); + ret = kfifo_alloc(&pll->fsm.event_fifo, 32, GFP_KERNEL); + if (ret) { + pr_err("failed to allocate fifo"); + return ret; + } + pll->fsm.task = kthread_run(adrv906x_phy_fsm_handle_transition, + &pll->fsm, "pll%d-fsm", dev_id); + if (IS_ERR(pll->fsm.task)) { + pr_err("kthread_run() failed"); + mutex_unlock(&pll->mtx); + return PTR_ERR(pll->fsm.task); + } + snprintf(pll->fsm.name, sizeof(pll->fsm.name), "pll%d-fsm", dev_id); + atomic_set(&pll->fsm.state, PLL_ST_UNLOCKED); + pll->fsm.tran_tbl = adrv906x_phy_fsm_pll_trans; + pll->fsm.tran_tbl_size = ARRAY_SIZE(adrv906x_phy_fsm_pll_trans); + pll->fsm.state_to_str = adrv906x_pll_state_to_str; + pll->fsm.event_to_str = adrv906x_pll_event_to_str; + pll->dev_id = dev_id; + pll->started = true; + } + mutex_unlock(&pll->mtx); + + return 0; +} + +static void adrv906x_pll_close(int dev_id) +{ + struct adrv906x_pll *pll = adrv906x_pll_instance_get(dev_id); + + mutex_lock(&pll->mtx); + if (pll->started) { + kthread_stop(pll->fsm.task); + kfifo_free(&pll->fsm.event_fifo); + pll->started = false; + } + mutex_unlock(&pll->mtx); +} + +int adrv906x_serdes_open(struct phy_device *phydev, + adrv906x_serdes_cb tx_cb, adrv906x_serdes_cb rx_cb) +{ + struct adrv906x_serdes *serdes; + int dev_id = phydev->mdio.addr; + int ret; + + serdes = adrv906x_serdes_instance_get(dev_id); + if (!serdes) + return -EINVAL; + + serdes->phydev = phydev; + serdes->dev_id = dev_id; + serdes->tx_path_en = tx_cb; + serdes->rx_path_en = rx_cb; + + init_completion(&serdes->fsm.comp_tran); + spin_lock_init(&serdes->fsm.event_fifo_lock); + + ret = kfifo_alloc(&serdes->fsm.event_fifo, 32, GFP_KERNEL); + if (ret) { + pr_err("failed to allocate fifo"); + return ret; + } + + serdes->fsm.task = kthread_run(adrv906x_phy_fsm_handle_transition, + &serdes->fsm, "serdes%d-fsm", dev_id); + if (IS_ERR(serdes->fsm.task)) { + pr_err("kthread_run() failed"); + return PTR_ERR(serdes->fsm.task); + } + snprintf(serdes->fsm.name, sizeof(serdes->fsm.name), "serdes%d-fsm", dev_id); + atomic_set(&serdes->fsm.state, SD_ST_IDLE); + serdes->fsm.tran_tbl = adrv906x_phy_fsm_serdes_trans; + serdes->fsm.tran_tbl_size = ARRAY_SIZE(adrv906x_phy_fsm_serdes_trans); + serdes->fsm.state_to_str = adrv906x_serdes_state_to_str; + serdes->fsm.event_to_str = adrv906x_serdes_event_to_str; + + ret = adrv906x_pll_open(dev_id / 2); + if (ret) + return ret; + + return 0; +} + +int adrv906x_serdes_close(struct phy_device *phydev) +{ + struct adrv906x_serdes *serdes; + int dev_id = phydev->mdio.addr; + + serdes = adrv906x_serdes_instance_get(dev_id); + if (!serdes) + return -EINVAL; + + kthread_stop(serdes->fsm.task); + kfifo_free(&serdes->fsm.event_fifo); + adrv906x_pll_close(dev_id / 2); + + return 0; +} diff --git a/drivers/net/ethernet/adi/adrv906x-phy-serdes.h b/drivers/net/ethernet/adi/adrv906x-phy-serdes.h new file mode 100644 index 00000000000000..74894d98ab7f5e --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-phy-serdes.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __ADRV906X_SERDES_H__ +#define __ADRV906X_SERDES_H__ + +#include +#include +#include + +typedef void (*adrv906x_serdes_cb)(struct phy_device *phydev, bool enable); + +int adrv906x_serdes_open(struct phy_device *phydev, + adrv906x_serdes_cb tx_cb, adrv906x_serdes_cb rx_cb); +int adrv906x_serdes_close(struct phy_device *phydev); +int adrv906x_serdes_lnk_up_req(struct phy_device *phydev); +int adrv906x_serdes_lnk_down_req(struct phy_device *phydev); +int adrv906x_serdes_genl_register_family(void); +int adrv906x_serdes_genl_unregister_family(void); + +#endif /* __ADRV906X_SERDES_H__ */ diff --git a/drivers/net/ethernet/adi/adrv906x-phy.c b/drivers/net/ethernet/adi/adrv906x-phy.c new file mode 100644 index 00000000000000..78cbd390d99a4c --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-phy.c @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include "adrv906x-phy.h" +#include "adrv906x-phy-serdes.h" +#include "adrv906x-net.h" +#include "adrv906x-tsu.h" + +struct adrv906x_phy_hw_stat { + const char *string; + u8 reg; + u8 shift; + u8 bits; +}; + +/* Elastic buffer and synchronization stats */ +static const struct adrv906x_phy_hw_stat adrv906x_phy_hw_stats[] = { + { "tx_buf_stat_out_of_bounds_ind", ADRV906X_PCS_BUF_STAT_TX_REG, 15, 1 }, + { "tx_buf_stat_read_occupancy", ADRV906X_PCS_BUF_STAT_TX_REG, 8, 7 }, + { "tx_buf_stat_fine_occupancy", ADRV906X_PCS_BUF_STAT_TX_REG, 0, 8 }, + { "rx_buf_stat_out_of_bounds_ind", ADRV906X_PCS_BUF_STAT_RX_REG, 15, 1 }, + { "rx_buf_stat_read_occupancy", ADRV906X_PCS_BUF_STAT_RX_REG, 8, 7 }, + { "rx_buf_stat_fine_occupancy", ADRV906X_PCS_BUF_STAT_RX_REG, 0, 8 }, + { "rx_bit_slip_cnt", ADRV906X_PCS_DELAY_RX_REG, 8, 7 }, + { "rx_delay_byte_cnt", ADRV906X_PCS_DELAY_RX_REG, 4, 3 }, + { "rx_buf_fine_occupancy_cnt", ADRV906X_PCS_DELAY_RX_REG, 0, 4 }, + { "disp_error_cnt", ADRV906X_PCS_DISP_ERR_REG, 0, 16 }, + { "code_error_cnt", ADRV906X_PCS_CODE_ERR_REG, 0, 16 }, + { "cpc_shcv_error_cnt", ADRV906X_PCS_CPCS_SHCV_REG, 0, 16 }, +}; + +static struct kref phydrv_refcount; +static DEFINE_SPINLOCK(phydrv_lock); +static bool phydrv_is_registered; + +static bool adrv906x_phy_valid_speed(int speed) +{ + switch (speed) { + case SPEED_10000: + return true; + case SPEED_25000: + return true; + default: + return false; + } +} + +static int adrv906x_phy_get_sset_count(struct phy_device *phydev) +{ + return ARRAY_SIZE(adrv906x_phy_hw_stats); +} + +static void adrv906x_phy_get_strings(struct phy_device *phydev, u8 *data) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(adrv906x_phy_hw_stats); i++) + strscpy(data + i * ETH_GSTRING_LEN, + adrv906x_phy_hw_stats[i].string, ETH_GSTRING_LEN); +} + +static u64 adrv906x_phy_get_stat(struct phy_device *phydev, int i) +{ + const struct adrv906x_phy_hw_stat *stat = &adrv906x_phy_hw_stats[i]; + u32 val; + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat->reg); + val >>= stat->shift; + val = val & ((1 << stat->bits) - 1); + + return val; +} + +static void adrv906x_phy_get_stats(struct phy_device *phydev, + struct ethtool_stats *stats, u64 *data) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(adrv906x_phy_hw_stats); i++) + data[i] = adrv906x_phy_get_stat(phydev, i); +} + +static int adrv906x_phy_get_features(struct phy_device *phydev) +{ + u32 val; + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT2); + + if (val & ADRV906X_PCS_STAT2_10GBR) { + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, phydev->supported); + } + if (val & ADRV906X_PCS_STAT2_25GBR) { + linkmode_set_bit(ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, phydev->supported); + } + + linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported); + linkmode_copy(phydev->advertising, phydev->supported); + + return 0; +} + +static void adrv906x_phy_rx_path_enable(struct phy_device *phydev, bool enable) +{ + phy_modify_mmd_changed(phydev, MDIO_MMD_PCS, ADRV906X_PCS_GENERAL_RX_REG, + ADRV906X_PCS_GENERAL_PATH_RESET, !enable); +} + +static void adrv906x_phy_tx_path_enable(struct phy_device *phydev, bool enable) +{ + phy_modify_mmd_changed(phydev, MDIO_MMD_PCS, ADRV906X_PCS_GENERAL_TX_REG, + ADRV906X_PCS_GENERAL_PATH_RESET, !enable); +} + +static int adrv906x_phy_suspend(struct phy_device *phydev) +{ + adrv906x_phy_rx_path_enable(phydev, false); + adrv906x_phy_tx_path_enable(phydev, false); + adrv906x_serdes_lnk_down_req(phydev); + + return 0; +} + +static void adrv906x_phy_link_change_notify(struct phy_device *phydev) +{ + struct net_device *netdev = phydev->attached_dev; + struct adrv906x_eth_dev *adrv906x_dev = netdev_priv(netdev); + struct adrv906x_tsu *tsu = &adrv906x_dev->tsu; + u32 bit_slip, buf_delay_tx, buf_delay_rx; + bool rs_fec_enabled = false; + int i, val; + + if (!phydev->link || phydev->state != PHY_RUNNING) + return; + + if (adrv906x_tod_cfg_cdc_delay < 0) { + phydev_err(phydev, "tod cfg_cdc_delay not initialized yet"); + return; + } + + /* We need a dummy read to get the correct value. */ + phy_read_mmd(phydev, MDIO_MMD_PCS, ADRV906X_PCS_BRMGBT_STAT2); + val = phy_read_mmd(phydev, MDIO_MMD_PCS, ADRV906X_PCS_BRMGBT_STAT2); + if (!(val & ADRV906X_PCS_BRMGBT_STAT2_LBLKLK)) { + phydev_warn(phydev, "pcs not locked and synced to the ethernet block"); + return; + } + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, ADRV906X_PCS_RS_FEC_CTRL_REG); + if (val & ADRV906X_PCS_RS_FEC_CTRL_EN) { + val = phy_read_mmd(phydev, MDIO_MMD_PCS, + ADRV906X_PCS_RS_FEC_STAT_REG); + if (!(val & ADRV906X_PCS_RS_FEC_STAT_ALIGN)) { + phydev_warn(phydev, "rs-fec is not locked and aligned"); + return; + } + + rs_fec_enabled = true; + } + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, ADRV906X_PCS_DELAY_RX_REG); + bit_slip = FIELD_GET(ADRV906X_PCS_DELAY_RX_BIT_SLIP, val); + + buf_delay_rx = U32_MAX; + for (i = 0; i < 10; i++) { + u32 d; + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, ADRV906X_PCS_BUF_STAT_RX_REG); + d = FIELD_GET(ADRV906X_PCS_BUF_STAT_RX_FINE_DELAY, val); + if (d < buf_delay_rx) + buf_delay_rx = d; + } + + buf_delay_tx = U32_MAX; + for (i = 0; i < 10; i++) { + u32 d; + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, ADRV906X_PCS_BUF_STAT_TX_REG); + d = FIELD_GET(ADRV906X_PCS_BUF_STAT_TX_FINE_DELAY, val); + if (d < buf_delay_tx) + buf_delay_tx = d; + } + + adrv906x_tsu_calculate_phy_delay(tsu, phydev->speed, rs_fec_enabled, + bit_slip, buf_delay_tx, buf_delay_rx); + + phydev_info(phydev, "static phy delay tx: 0x%08x", tsu->phy_delay_tx); + phydev_info(phydev, "static phy delay rx: 0x%08x", tsu->phy_delay_rx); + + adrv906x_tsu_set_phy_delay(tsu); +} + +static int adrv906x_phy_resume(struct phy_device *phydev) +{ + adrv906x_phy_rx_path_enable(phydev, true); + adrv906x_phy_tx_path_enable(phydev, true); + + return 0; +} + +static int adrv906x_phy_read_status(struct phy_device *phydev) +{ + int val; + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1); + + if (phydev->dev_flags & ADRV906X_PHY_FLAGS_LOOPBACK_TEST) + phydev->link = 1; + else + phydev->link = !!(val & MDIO_STAT1_LSTATUS); + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2); + if ((val & ADRV906X_PCS_CTRL2_TYPE_SEL_MSK) == MDIO_PCS_CTRL2_10GBR) { + phydev->speed = SPEED_10000; + phydev->duplex = DUPLEX_FULL; + } else if ((val & ADRV906X_PCS_CTRL2_TYPE_SEL_MSK) == ADRV906X_PCS_CTRL2_25GBR) { + phydev->speed = SPEED_25000; + phydev->duplex = DUPLEX_FULL; + } else { + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + } + + return 0; +} + +static int adrv906x_phy_set_loopback(struct phy_device *phydev, bool enable) +{ + return 0; +} + +static int adrv906x_phy_config_pcs_baser_mode(struct phy_device *phydev) +{ + int ctrl1, ctrl2, cfg_tx, cfg_rx, gen_tx, gen_rx; + + if (!adrv906x_phy_valid_speed(phydev->speed)) { + phydev_err(phydev, + "unsupported speed: %d", phydev->speed); + return -EINVAL; + } + + ctrl2 = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2); + ctrl2 &= ~ADRV906X_PCS_CTRL2_TYPE_SEL_MSK; + + ctrl1 = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); + ctrl1 &= ~MDIO_CTRL1_SPEEDSEL; + + if (phydev->speed == SPEED_25000) { + ctrl1 |= ADRV906X_PCS_CTRL1_SPEED25G; + ctrl2 |= ADRV906X_PCS_CTRL2_25GBR; + } else { + ctrl1 |= ADRV906X_PCS_CTRL1_SPEED10G; + ctrl2 |= ADRV906X_PCS_CTRL2_10GBR; + } + + cfg_tx = ADRV906X_PCS_CFG_TX_BUF_INIT; + cfg_rx = ADRV906X_PCS_CFG_RX_BUF_INIT; + gen_tx = ADRV906X_PCS_GENERAL_SERDES_64_BITS_BUS_WIDTH | + ADRV906X_PCS_GENERAL_64_BITS_XGMII; + gen_rx = ADRV906X_PCS_GENERAL_SERDES_64_BITS_BUS_WIDTH | + ADRV906X_PCS_GENERAL_64_BITS_XGMII; + + phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ctrl1); + phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2, ctrl2); + phy_write_mmd(phydev, MDIO_MMD_PCS, ADRV906X_PCS_CFG_TX_REG, cfg_tx); + phy_write_mmd(phydev, MDIO_MMD_PCS, ADRV906X_PCS_CFG_RX_REG, cfg_rx); + phy_write_mmd(phydev, MDIO_MMD_PCS, ADRV906X_PCS_GENERAL_TX_REG, gen_tx); + phy_write_mmd(phydev, MDIO_MMD_PCS, ADRV906X_PCS_GENERAL_RX_REG, gen_rx); + + if (phydev->speed == SPEED_25000 && phydev->dev_flags & ADRV906X_PHY_FLAGS_PCS_RS_FEC_EN) + phy_write_mmd(phydev, MDIO_MMD_PCS, ADRV906X_PCS_RS_FEC_CTRL_REG, + ADRV906X_PCS_RS_FEC_CTRL_EN); + else + phy_write_mmd(phydev, MDIO_MMD_PCS, ADRV906X_PCS_RS_FEC_CTRL_REG, 0); + + return 0; +} + +static int adrv906x_phy_config_aneg(struct phy_device *phydev) +{ + int ret; + + if (phydev->duplex != DUPLEX_FULL) + return -EINVAL; + + if (phydev->autoneg != AUTONEG_DISABLE) + return -EINVAL; + + if (!adrv906x_phy_valid_speed(phydev->speed)) + return -EINVAL; + + ret = adrv906x_phy_config_pcs_baser_mode(phydev); + if (ret) + return ret; + + ret = adrv906x_serdes_lnk_up_req(phydev); + if (ret) + return ret; + + return genphy_c45_an_disable_aneg(phydev); +} + +static int adrv906x_phy_aneg_done(struct phy_device *phydev) +{ + int val; + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1); + + return !!(val & MDIO_STAT1_LSTATUS); +} + +static int adrv906x_phy_config_init(struct phy_device *phydev) +{ + phydev->autoneg = AUTONEG_DISABLE; + phydev->duplex = DUPLEX_FULL; + phydev->port = PORT_FIBRE; + + return 0; +} + +static int adrv906x_phy_module_info(struct phy_device *dev, + struct ethtool_modinfo *modinfo) +{ + int ret = -EOPNOTSUPP; + + if (dev->sfp_bus) + ret = sfp_get_module_info(dev->sfp_bus, modinfo); + + return ret; +} + +static const struct sfp_upstream_ops adrv906x_sfp_ops = { + .attach = phy_sfp_attach, + .detach = phy_sfp_detach, +}; + +static int adrv906x_phy_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct device_node *np = dev->of_node; + u32 mmd_mask = MDIO_DEVS_PCS; + int ret; + + if (!phydev->is_c45 || + (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask) + return -ENODEV; + + if (of_property_read_u32(np, "speed", &phydev->speed) < 0) { + phydev->speed = SPEED_25000; + } else { + if (phydev->speed != SPEED_10000 && phydev->speed != SPEED_25000) { + dev_warn(dev, "dt: phy unsupported speed: %d, defaulting to 25000", phydev->speed); + phydev->speed = SPEED_25000; + } + } + + phydev->dev_flags |= ADRV906X_PHY_FLAGS_PCS_RS_FEC_EN; + + /* TODO remove the following workaround when A0 silicon is retired */ + /* Disable RS-FEC for silicon A0 */ + { +#define ADRV906X_SI_REV_ID_REG 0x18290005 +#define ADRV906X_SEC_SI_REV_ID_REG 0x1c290005 + phys_addr_t si_rev_id_reg; + void __iomem *addr; + unsigned long flags; + u32 index; + u8 si_rev; + + of_property_read_u32(np, "reg", &index); + if (index < 2) + si_rev_id_reg = ADRV906X_SI_REV_ID_REG; + else + si_rev_id_reg = ADRV906X_SEC_SI_REV_ID_REG; + + spin_lock_irqsave(&phydrv_lock, flags); + addr = ioremap(si_rev_id_reg, 1); + if (!addr) + return -ENOMEM; + si_rev = ioread8(addr); + iounmap(addr); + spin_unlock_irqrestore(&phydrv_lock, flags); + + if (si_rev == 0xA0) { + phydev->dev_flags &= ~ADRV906X_PHY_FLAGS_PCS_RS_FEC_EN; + dev_info(dev, "disable RS-FEC"); + } else { + dev_info(dev, "enable RS-FEC"); + } + } + + ret = adrv906x_serdes_open(phydev, adrv906x_phy_tx_path_enable, adrv906x_phy_rx_path_enable); + if (ret) + return ret; + + return phy_sfp_probe(phydev, &adrv906x_sfp_ops); +} + +static void adrv906x_phy_remove(struct phy_device *phydev) +{ + adrv906x_serdes_close(phydev); +} + +static struct phy_driver adrv906x_phy_driver = { + PHY_ID_MATCH_EXACT(ADRV906X_PHY_ID), + .name = "adrv906x-phy", + .probe = adrv906x_phy_probe, + .remove = adrv906x_phy_remove, + .config_init = adrv906x_phy_config_init, + .soft_reset = genphy_soft_reset, + .config_aneg = adrv906x_phy_config_aneg, + .aneg_done = adrv906x_phy_aneg_done, + .read_status = adrv906x_phy_read_status, + .set_loopback = adrv906x_phy_set_loopback, + .get_sset_count = adrv906x_phy_get_sset_count, + .get_strings = adrv906x_phy_get_strings, + .get_stats = adrv906x_phy_get_stats, + .get_features = adrv906x_phy_get_features, + .resume = adrv906x_phy_resume, + .suspend = adrv906x_phy_suspend, + .link_change_notify = adrv906x_phy_link_change_notify, + .module_info = adrv906x_phy_module_info, +}; + +int adrv906x_phy_register(void) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&phydrv_lock, flags); + + if (phydrv_is_registered) { + kref_get(&phydrv_refcount); + goto out; + } else { + ret = phy_driver_register(&adrv906x_phy_driver, THIS_MODULE); + if (ret) { + pr_err("%s: driver register failed", __func__); + goto out; + } + + ret = adrv906x_serdes_genl_register_family(); + if (ret) { + phy_driver_unregister(&adrv906x_phy_driver); + pr_err("%s: generic netlink family register failed", __func__); + goto out; + } + + phydrv_is_registered = true; + kref_init(&phydrv_refcount); + } + +out: + spin_unlock_irqrestore(&phydrv_lock, flags); + return ret; +} + +static void adrv906x_phy_exit(struct kref *ref) +{ + unsigned long flags; + + spin_lock_irqsave(&phydrv_lock, flags); + + phydrv_is_registered = false; + phy_driver_unregister(&adrv906x_phy_driver); + adrv906x_serdes_genl_unregister_family(); + + spin_unlock_irqrestore(&phydrv_lock, flags); +} + +void adrv906x_phy_unregister(void) +{ + kref_put(&phydrv_refcount, adrv906x_phy_exit); +} diff --git a/drivers/net/ethernet/adi/adrv906x-phy.h b/drivers/net/ethernet/adi/adrv906x-phy.h new file mode 100644 index 00000000000000..689e927fb1f700 --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-phy.h @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __ADRV906X_PHY_H__ +#define __ADRV906X_PHY_H__ + +#include +#include +#include + +#define ADRV906X_PHY_ID 0x00000000 +#define ADRV906X_PHY_FLAGS_PCS_RS_FEC_EN BIT(0) +#define ADRV906X_PHY_FLAGS_LOOPBACK_TEST BIT(1) + +/* ADI PCS registers */ + +/* The following registers and bitfields are defined in 802.3 clause 45. + * We should replace them with those from mdio.h in future. + */ +#define ADRV906X_PCS_STATUS_3_REG 9 +#define ADRV906X_PCS_BRMGBT_STAT2 33 +#define ADRV906X_PCS_BRMGBT_STAT2_LBLKLK BIT(15) +#define ADRV906X_PCS_SEED_A0_REG 34 +#define ADRV906X_PCS_SEED_A1_REG 35 +#define ADRV906X_PCS_SEED_A2_REG 36 +#define ADRV906X_PCS_SEED_A3_REG 37 +#define ADRV906X_PCS_SEED_B0_REG 38 +#define ADRV906X_PCS_SEED_B1_REG 39 +#define ADRV906X_PCS_SEED_B2_REG 40 +#define ADRV906X_PCS_SEED_B3_REG 41 +#define ADRV906X_PCS_TEST_CTRL_REG 42 +#define ADRV906X_PCS_TEST_ERROR_CNT_REG 43 +#define ADRV906X_PCS_BER_HIGH_REG 44 +#define ADRV906X_PCS_ERROR_BLOCKS_REG 45 + +/* Configuration values of PCS specific registers */ +#define ADRV906X_PCS_CTRL2_TYPE_SEL_MSK GENMASK(3, 0) /* PCS type selection */ +#define ADRV906X_PCS_CTRL2_10GBR 0x0000 +#define ADRV906X_PCS_CTRL2_25GBR 0x0007 /* 25GBASE-R type */ +#define ADRV906X_PCS_CTRL1_SPEED10G (MDIO_CTRL1_SPEEDSELEXT | 0x00) /* 10 Gb/s */ +#define ADRV906X_PCS_CTRL1_SPEED25G (MDIO_CTRL1_SPEEDSELEXT | 0x14) /* 25 Gb/s */ + +#define ADRV906X_PCS_STAT2_25GBR 0x0080 /* 25GBASE-R */ +#define ADRV906X_PCS_STAT2_10GBR 0x0001 /* 10GBASE-R */ + +/* 802.3 clause 45 states PCS vendor registers to be numbered from 32768. + * But ADRV906x PCS vendor registers are numbered from 46. + */ +#define ADRV906X_PCS_GENERAL_TX_REG 46 +#define ADRV906X_PCS_GENERAL_RX_REG 47 +#define ADRV906X_PCS_GENERAL_SCRAMBLER_BYPASS_EN BIT(15) +#define ADRV906X_PCS_GENERAL_CPRI_EN BIT(14) +#define ADRV906X_PCS_GENERAL_SERDES_BUS_WIDTH_MSK GENMASK(13, 7) +#define ADRV906X_PCS_GENERAL_SERDES_64_BITS_BUS_WIDTH 0x2000 +#define ADRV906X_PCS_GENERAL_SERDES_32_BITS_BUS_WIDTH 0x1000 +#define ADRV906X_PCS_GENERAL_HALF_DUTY_CYCLE_EN BIT(4) +#define ADRV906X_PCS_GENERAL_XGMII_BUS_WIDTH_MSK BIT(3) +#define ADRV906X_PCS_GENERAL_64_BITS_XGMII 0x0008 +#define ADRV906X_PCS_GENERAL_32_BITS_XGMII 0x0000 +#define ADRV906X_PCS_GENERAL_PRBS23_TESTPATTERN_EN BIT(2) +#define ADRV906X_PCS_GENERAL_PRBS7_TESTPATTERN_EN BIT(1) +#define ADRV906X_PCS_GENERAL_PATH_RESET BIT(0) + +#define ADRV906X_PCS_CFG_TX_REG 48 +#define ADRV906X_PCS_CFG_TX_BIT_DELAY_MSK GENMASK(15, 9) +#define ADRV906X_PCS_CFG_TX_BUF_INIT_MSK GENMASK(8, 1) +#define ADRV906X_PCS_CFG_TX_BUF_INIT 0x000A +#define ADRV906X_PCS_CFG_TX_BUF_BYPASS_EN BIT(0) + +#define ADRV906X_PCS_CFG_RX_REG 49 +#define ADRV906X_PCS_CFG_RX_GEARBOX_BYPASS_EN BIT(12) +#define ADRV906X_PCS_CFG_RX_SERDES_LOOPBACK_EN BIT(11) +#define ADRV906X_PCS_CFG_RX_CORE_IF_LOOPBACK_EN BIT(10) +#define ADRV906X_PCS_CFG_RX_COMMA_SEARCH_DIS BIT(9) +#define ADRV906X_PCS_CFG_RX_BUF_INIT_MSK GENMASK(8, 1) +#define ADRV906X_PCS_CFG_RX_BUF_INIT 0x000A +#define ADRV906X_PCS_CFG_RX_BUF_BYPASS_EN BIT(0) + +#define ADRV906X_PCS_BUF_STAT_TX_REG 50 +#define ADRV906X_PCS_BUF_STAT_TX_FINE_DELAY GENMASK(7, 0) +#define ADRV906X_PCS_BUF_STAT_RX_REG 51 +#define ADRV906X_PCS_BUF_STAT_RX_FINE_DELAY GENMASK(7, 0) +#define ADRV906X_PCS_DELAY_RX_REG 52 +#define ADRV906X_PCS_DELAY_RX_BIT_SLIP GENMASK(14, 8) +#define ADRV906X_PCS_DISP_ERR_REG 53 +#define ADRV906X_PCS_CODE_ERR_REG 54 +#define ADRV906X_PCS_CPCS_SHCV_REG 55 + +#define ADRV906X_PCS_RS_FEC_CTRL_REG 200 +#define ADRV906X_PCS_RS_FEC_CTRL_EN BIT(2) +#define ADRV906X_PCS_RS_FEC_STAT_REG 201 +#define ADRV906X_PCS_RS_FEC_STAT_ALIGN BIT(14) + +int adrv906x_phy_register(void); +void adrv906x_phy_unregister(void); + +#endif /* __ADRV906X_PHY_H__ */ diff --git a/drivers/net/ethernet/adi/adrv906x-switch.c b/drivers/net/ethernet/adi/adrv906x-switch.c new file mode 100644 index 00000000000000..aa684406a62d84 --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-switch.c @@ -0,0 +1,532 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adrv906x-switch.h" + +static int adrv906x_switch_wait_for_mae_ready(struct adrv906x_eth_switch *es) +{ + int wait_count = SWITCH_MAE_TIMEOUT; + u32 val; + + while (wait_count > 0) { + val = ioread32(es->reg_match_action + SWITCH_MAS_OP_CTRL); + val &= ~SWITCH_MAS_OP_CTRL_OPCODE_MASK; + + if (!val) + return 0; + + wait_count--; + usleep_range(1, 2); + } + return -ETIMEDOUT; +} + +static int adrv906x_switch_vlan_match_action_sync(struct adrv906x_eth_switch *es, u32 mask, u32 vid) +{ + u32 val; + int ret; + + ret = adrv906x_switch_wait_for_mae_ready(es); + if (ret) { + dev_err(&es->pdev->dev, "vlan add timeout"); + return ret; + } + + iowrite32(mask, es->reg_match_action + SWITCH_MAS_PORT_MASK1); + iowrite32(0, es->reg_match_action + SWITCH_MAS_PORT_MASK2); + iowrite32(vid, es->reg_match_action + SWITCH_MAS_VLAN_ID); + val = FIELD_PREP(SWITCH_MAS_OP_CTRL_OPCODE_MASK, SWITCH_MAS_OP_CTRL_VLAN_ADD) | + SWITCH_MAS_OP_CTRL_TRIGGER; + iowrite32(val, es->reg_match_action + SWITCH_MAS_OP_CTRL); + + return 0; +} + +void adrv906x_switch_set_mae_age_time(struct adrv906x_eth_switch *es, u8 data) +{ + u32 val; + + val = ioread32(es->reg_match_action + SWITCH_MAS_CFG_MAE); + val &= ~CFG_MAE_AGE_TIME_MASK; + val |= FIELD_PREP(CFG_MAE_AGE_TIME_MASK, data); + iowrite32(val, es->reg_match_action + SWITCH_MAS_CFG_MAE); +} + +static void adrv906x_switch_dsa_tx_enable(struct adrv906x_eth_switch *es, bool enabled) +{ + int i, val; + + for (i = 0; i < SWITCH_MAX_PORT_NUM; i++) { + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_CFG_QINQ); + if (enabled) + val |= SWITCH_PORT_CFG_DSA_TX_EN; + else + val &= ~SWITCH_PORT_CFG_DSA_TX_EN; + iowrite32(val, es->switch_port[i].reg + SWITCH_PORT_CFG_QINQ); + } +} + +static void adrv906x_switch_dsa_rx_enable(struct adrv906x_eth_switch *es, bool enabled) +{ + int i, val; + + for (i = 0; i < SWITCH_MAX_PORT_NUM; i++) { + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_CFG_QINQ); + if (enabled) + val |= SWITCH_PORT_CFG_DSA_RX_EN; + else + val &= ~SWITCH_PORT_CFG_DSA_RX_EN; + iowrite32(val, es->switch_port[i].reg + SWITCH_PORT_CFG_QINQ); + } +} + +static struct vlan_cfg_list *adrv906x_switch_vlan_find(struct adrv906x_eth_switch *es, u16 vid) +{ + struct vlan_cfg_list *vcl; + + mutex_lock(&es->vlan_cfg_list_lock); + list_for_each_entry(vcl, &es->vlan_cfg_list, list) { + if (vcl->vlan_id == vid) { + mutex_unlock(&es->vlan_cfg_list_lock); + return vcl; + } + } + mutex_unlock(&es->vlan_cfg_list_lock); + + return NULL; +} + +static int adrv906x_switch_vlan_add(struct adrv906x_eth_switch *es, u16 port, u16 vid) +{ + struct vlan_cfg_list *vcl; + u32 mask; + int ret; + + if (port >= SWITCH_MAX_PORT_NUM) + return -EINVAL; + + if (vid >= VLAN_N_VID) + return -EINVAL; + + vcl = adrv906x_switch_vlan_find(es, vid); + if (!vcl) { + vcl = devm_kzalloc(&es->pdev->dev, sizeof(*vcl), GFP_ATOMIC); + if (!vcl) + return -ENOMEM; + + vcl->vlan_id = vid; + mutex_lock(&es->vlan_cfg_list_lock); + list_add(&vcl->list, &es->vlan_cfg_list); + mutex_unlock(&es->vlan_cfg_list_lock); + } + + mask = BIT(port); + if (!(vcl->port_mask & mask)) { + mask |= vcl->port_mask; + ret = adrv906x_switch_vlan_match_action_sync(es, mask, vid); + if (ret) + return ret; + + vcl->port_mask = mask; + } + + return 0; +} + +static int adrv906x_switch_pvid_set(struct adrv906x_eth_switch *es, u16 pvid) +{ + int portid, ret; + u32 val; + + if (pvid >= VLAN_N_VID) + return -EINVAL; + + for (portid = 0; portid < SWITCH_MAX_PORT_NUM; portid++) { + ret = adrv906x_switch_vlan_add(es, portid, pvid); + if (ret) + return ret; + val = ioread32(es->switch_port[portid].reg + SWITCH_PORT_CFG_VLAN); + val &= ~SWITCH_PORT_PVID_MASK; + val |= pvid; + iowrite32(val, es->switch_port[portid].reg + SWITCH_PORT_CFG_VLAN); + } + + es->pvid = pvid; + + return 0; +} + +static void adrv906x_switch_pcp_regen_set(struct adrv906x_eth_switch *es, u32 pcpmap) +{ + int portid; + + for (portid = 0; portid < SWITCH_MAX_PORT_NUM; portid++) + iowrite32(pcpmap, es->switch_port[portid].reg + SWITCH_PORT_PCP_REGEN); +} + +static void adrv906x_switch_ipv_mapping_set(struct adrv906x_eth_switch *es, u32 pcpmap) +{ + int portid; + + for (portid = 0; portid < SWITCH_MAX_PORT_NUM; portid++) + iowrite32(pcpmap, es->switch_port[portid].reg + SWITCH_PORT_PCP2IPV); +} + +static int adrv906x_switch_default_vlan_set(struct adrv906x_eth_switch *es) +{ + u16 default_vids[] = { 2, 3, 4, 5 }; + int i, portid, ret; + + for (portid = 0; portid < SWITCH_MAX_PORT_NUM; portid++) { + for (i = 0; i < ARRAY_SIZE(default_vids); i++) { + ret = adrv906x_switch_vlan_add(es, portid, default_vids[i]); + if (ret) + return ret; + } + } + + return 0; +} + +static int adrv906x_switch_add_fdb_entry(struct adrv906x_eth_switch *es, u64 mac_addr, int portid) +{ + u32 val, mac_addr_hi, mac_addr_lo; + int ret; + + if (portid >= SWITCH_MAX_PORT_NUM) + return -EINVAL; + + ret = adrv906x_switch_wait_for_mae_ready(es); + if (ret) { + dev_err(&es->pdev->dev, "fdb entry add timeout"); + return ret; + } + + mac_addr_hi = FIELD_GET(0xFFFFFFFF0000, mac_addr); + mac_addr_lo = FIELD_GET(0xFFFF, mac_addr); + + iowrite32(BIT(portid), es->reg_match_action + SWITCH_MAS_PORT_MASK1); + iowrite32(mac_addr_hi, es->reg_match_action + SWITCH_MAS_FDB_MAC_INSERT_1); + val = FIELD_PREP(SWITCH_INSERT_MAC_ADDR_LOW_MASK, mac_addr_lo) | + SWITCH_INSERT_VALID | SWITCH_INSERT_STATIC; + iowrite32(val, es->reg_match_action + SWITCH_MAS_FDB_MAC_INSERT_2); + val = FIELD_PREP(SWITCH_MAS_OP_CTRL_OPCODE_MASK, SWITCH_MAS_OP_CTRL_MAC_INSERT) | + SWITCH_MAS_OP_CTRL_TRIGGER; + iowrite32(val, es->reg_match_action + SWITCH_MAS_OP_CTRL); + + return 0; +} + +static int adrv906x_switch_packet_trapping_set(struct adrv906x_eth_switch *es) +{ + u32 val; + int ret; + + /* Trap PTP messages from port 0 & 1 to port 2 */ + val = SWITCH_PORT_TRAP_PTP_EN | FIELD_PREP(SWITCH_PORT_TRAP_DSTPORT_MASK, SWITCH_CPU_PORT); + iowrite32(val, es->switch_port[0].reg + SWITCH_PORT_TRAP_PTP); + iowrite32(val, es->switch_port[1].reg + SWITCH_PORT_TRAP_PTP); + + ret = adrv906x_switch_add_fdb_entry(es, EAPOL_MAC_ADDR, SWITCH_CPU_PORT); + if (ret) + return ret; + ret = adrv906x_switch_add_fdb_entry(es, ESMC_MAC_ADDR, SWITCH_CPU_PORT); + if (ret) + return ret; + if (es->trap_ptp_fwd_en) { + ret = adrv906x_switch_add_fdb_entry(es, PTP_FWD_ADDR, SWITCH_CPU_PORT); + if (ret) + return ret; + } + + return 0; +} + +void adrv906x_switch_reset_soft(struct adrv906x_eth_switch *es) +{ + int ret; + + iowrite32(SWITCH_ALL_EX_MAE, es->reg_switch + SWITCH_SOFT_RESET); + iowrite32(0, es->reg_switch + SWITCH_SOFT_RESET); + + ret = adrv906x_switch_wait_for_mae_ready(es); + if (ret) + dev_err(&es->pdev->dev, "reset of internal switch failed"); +} + +static irqreturn_t adrv906x_switch_error_isr(int irq, void *dev_id) +{ + struct adrv906x_eth_switch *es = (struct adrv906x_eth_switch *)dev_id; + int ret; + + ret = es->isr_pre_args.func(es->isr_pre_args.arg); + if (ret) + return IRQ_NONE; + + usleep_range(1000, 1100); + /* TODO: Look at re-applying non-register switch configuration */ + adrv906x_switch_reset_soft(es); + + ret = es->isr_post_args.func(es->isr_post_args.arg); + if (ret) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static void adrv906x_switch_update_hw_stats(struct work_struct *work) +{ + struct adrv906x_eth_switch *es = container_of(work, struct adrv906x_eth_switch, + update_stats.work); + unsigned int val; + int i; + + rtnl_lock(); + + for (i = 0; i < SWITCH_MAX_PORT_NUM; i++) { + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STATS_CTRL); + val |= SWITCH_PORT_SNAPSHOT_EN; + iowrite32(val, es->switch_port[i].reg + SWITCH_PORT_STATS_CTRL); + + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_PKT_FLTR_RX); + es->port_stats[i].pkt_fltr_rx += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_BYTES_FLTR_RX); + es->port_stats[i].bytes_fltr_rx += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_PKT_BUF_OVFL); + es->port_stats[i].pkt_buf_ovfl += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_BYTES_BUF_OVFL); + es->port_stats[i].bytes_buf_ovfl += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_PKT_ERR); + es->port_stats[i].pkt_err += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_BYTES_ERR); + es->port_stats[i].bytes_err += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_DROP_PKT_TX); + es->port_stats[i].drop_pkt_tx += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_PKT_VOQ_NQN_IPV0); + es->port_stats[i].ipv0_pkt_voq_nqn += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_PKT_VOQ_NQN_IPV1); + es->port_stats[i].ipv1_pkt_voq_nqn += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_BYTES_VOQ_NQN_IPV0); + es->port_stats[i].ipv0_bytes_voq_nqn += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_BYTES_VOQ_NQN_IPV1); + es->port_stats[i].ipv1_bytes_voq_nqn += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_PKT_VOQ_DQN_IPV0); + es->port_stats[i].ipv0_pkt_voq_dqn += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_PKT_VOQ_DQN_IPV1); + es->port_stats[i].ipv1_pkt_voq_dqn += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_BYTES_VOQ_DQN_IPV0); + es->port_stats[i].ipv0_bytes_voq_dqn += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_BYTES_VOQ_DQN_IPV1); + es->port_stats[i].ipv1_bytes_voq_dqn += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_PKT_VOQ_DROPN_IPV0); + es->port_stats[i].ipv0_pkt_voq_dropn += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_PKT_VOQ_DROPN_IPV1); + es->port_stats[i].ipv1_pkt_voq_dropn += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_BYTES_VOQ_DROPN_IPV0); + es->port_stats[i].ipv0_bytes_voq_dropn += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_BYTES_VOQ_DROPN_IPV1); + es->port_stats[i].ipv1_bytes_voq_dropn += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_UCAST_PKT_RX); + es->port_stats[i].ucast_pkt_rx += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_UCAST_BYTES_RX); + es->port_stats[i].ucast_bytes_rx += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_UCAST_PKT_TX); + es->port_stats[i].ucast_pkt_tx += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_UCAST_BYTES_TX); + es->port_stats[i].ucast_bytes_tx += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_MCAST_PKT_RX); + es->port_stats[i].mcast_pkt_rx += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_MCAST_BYTES_RX); + es->port_stats[i].mcast_bytes_rx += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_MCAST_PKT_TX); + es->port_stats[i].mcast_pkt_tx += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_MCAST_BYTES_TX); + es->port_stats[i].mcast_bytes_tx += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_BCAST_PKT_RX); + es->port_stats[i].bcast_pkt_rx += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_BCAST_BYTES_RX); + es->port_stats[i].bcast_bytes_rx += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_BCAST_PKT_TX); + es->port_stats[i].bcast_pkt_tx += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_BCAST_BYTES_TX); + es->port_stats[i].bcast_bytes_tx += val; + val = ioread32(es->switch_port[i].reg + SWITCH_PORT_STAT_CRD_BUFFER_DROP); + es->port_stats[i].crd_buffer_drop += val; + } + + rtnl_unlock(); + + mod_delayed_work(system_long_wq, &es->update_stats, msecs_to_jiffies(1000)); +} + +int adrv906x_switch_port_enable(struct adrv906x_eth_switch *es, int portid, bool enabled) +{ + u32 val; + + if (portid >= SWITCH_MAX_PORT_NUM) + return -EINVAL; + + val = ioread32(es->switch_port[portid].reg + SWITCH_PORT_CFG_PORT); + if (enabled) + val |= SWITCH_PORT_CFG_PORT_EN; + else + val &= ~SWITCH_PORT_CFG_PORT_EN; + iowrite32(val, es->switch_port[portid].reg + SWITCH_PORT_CFG_PORT); + + return 0; +} + +int adrv906x_switch_register_irqs(struct adrv906x_eth_switch *es, struct device_node *np) +{ + char err_irq_name[16] = { 0, }; + int ret; + int i; + + /* Ignore the highest port number which is the CPU port */ + for (i = 0; i < (SWITCH_MAX_PORT_NUM - 1); i++) { + snprintf(err_irq_name, ARRAY_SIZE(err_irq_name), "%s%d", "switch_error_", i); + ret = of_irq_get_byname(np, err_irq_name); + if (ret < 0) + dev_err(&es->pdev->dev, "failed to get switch[%d] error irq", i); + + es->err_irqs[i] = ret; + + ret = devm_request_threaded_irq(&es->pdev->dev, es->err_irqs[i], + NULL, adrv906x_switch_error_isr, + IRQF_SHARED | IRQF_ONESHOT, + dev_name(&es->pdev->dev), es); + if (ret) { + dev_err(&es->pdev->dev, "failed to request switch[%d] error irq: %d", + i, es->err_irqs[i]); + return ret; + } + } + + return ret; +} + +int adrv906x_switch_probe(struct adrv906x_eth_switch *es, struct platform_device *pdev, + int (*isr_pre_func)(void *), int (*isr_post_func)(void *), void *isr_arg) +{ + struct device *dev = &pdev->dev; + struct device_node *eth_switch_np, *switch_port_np; + u32 reg, len, portid; + int i = 0; + int ret; + + es->pdev = pdev; + eth_switch_np = of_get_child_by_name(es->pdev->dev.of_node, "eth_switch"); + if (!eth_switch_np) { + dev_info(dev, "dt: switch node missing"); + return -ENODEV; + } + + INIT_LIST_HEAD(&es->vlan_cfg_list); + mutex_init(&es->vlan_cfg_list_lock); + + /* get switch device register address */ + of_property_read_u32_index(eth_switch_np, "reg", 0, ®); + of_property_read_u32_index(eth_switch_np, "reg", 1, &len); + es->reg_switch = devm_ioremap(dev, reg, len); + if (!es->reg_switch) { + dev_err(dev, "ioremap switch device failed"); + return -ENOMEM; + } + + /* get switch match action sync register address */ + of_property_read_u32_index(eth_switch_np, "reg", 2, ®); + of_property_read_u32_index(eth_switch_np, "reg", 3, &len); + es->reg_match_action = devm_ioremap(dev, reg, len); + if (!es->reg_match_action) { + dev_err(dev, "ioremap switch mas failed"); + return -ENOMEM; + } + + es->trap_ptp_fwd_en = of_property_read_bool(eth_switch_np, "trap-ptp-forwardable"); + + /* probe the switch ports */ + for_each_child_of_node(eth_switch_np, switch_port_np) { + if (strcmp(switch_port_np->name, "switch-port")) + continue; + of_property_read_u32(switch_port_np, "id", &portid); + if (portid != i) { + dev_err(dev, "dt: port id mismatch"); + return -EINVAL; + } + /* get switch port register address */ + of_property_read_u32_index(switch_port_np, "reg", 0, ®); + of_property_read_u32_index(switch_port_np, "reg", 1, &len); + es->switch_port[i].reg = devm_ioremap(&es->pdev->dev, reg, len); + if (!es->switch_port[i].reg) { + dev_err(dev, "ioremap switch port %d failed!", portid); + return -ENOMEM; + } + i++; + } + + es->isr_pre_args.func = isr_pre_func; + es->isr_pre_args.arg = isr_arg; + es->isr_post_args.func = isr_post_func; + es->isr_post_args.arg = isr_arg; + /* TODO: Add de-allocation in case of error below */ + ret = adrv906x_switch_register_irqs(es, eth_switch_np); + if (ret) + return ret; + + es->enabled = true; + + return 0; +} + +void adrv906x_switch_cleanup(struct adrv906x_eth_switch *es) +{ + cancel_delayed_work(&es->update_stats); +} + +int adrv906x_switch_init(struct adrv906x_eth_switch *es) +{ + int portid, ret; + + adrv906x_switch_dsa_tx_enable(es, false); + adrv906x_switch_dsa_rx_enable(es, true); + adrv906x_switch_pcp_regen_set(es, SWITCH_PCP_REGEN_VAL); + adrv906x_switch_ipv_mapping_set(es, SWITCH_PCP_IPV_MAPPING); + ret = adrv906x_switch_default_vlan_set(es); + if (ret) + return ret; + adrv906x_switch_set_mae_age_time(es, AGE_TIME_5MIN_25G); + ret = adrv906x_switch_pvid_set(es, SWITCH_PVID); + if (ret) + return ret; + ret = adrv906x_switch_packet_trapping_set(es); + if (ret) + return ret; + + for (portid = 0; portid < SWITCH_MAX_PORT_NUM; portid++) { + ret = adrv906x_switch_port_enable(es, portid, portid == SWITCH_CPU_PORT); + if (ret) + return ret; + } + + INIT_DELAYED_WORK(&es->update_stats, adrv906x_switch_update_hw_stats); + mod_delayed_work(system_long_wq, &es->update_stats, msecs_to_jiffies(1000)); + return 0; +} + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/adi/adrv906x-switch.h b/drivers/net/ethernet/adi/adrv906x-switch.h new file mode 100644 index 00000000000000..b7638978e6f222 --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-switch.h @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __ADRV906X_SWITCH_H__ +#define __ADRV906X_SWITCH_H__ + +#include +#include +#include +#include +#include + +#define SWITCH_MAX_PORT_NUM 3 +#define SWITCH_CPU_PORT 2 +#define SWITCH_PORT_STATS_NUM 32 +#define SWITCH_MAE_TIMEOUT 50 /* read count */ + +#define SWITCH_PORT_CFG_PORT 0x0004 +#define SWITCH_PORT_CFG_PORT_EN BIT(0) +#define SWITCH_PORT_CFG_VLAN 0x0008 +#define SWITCH_PORT_PVID_MASK GENMASK(11, 0) +#define SWITCH_PORT_CFG_QINQ 0x000c +#define SWITCH_PORT_CFG_DSA_TX_EN BIT(17) +#define SWITCH_PORT_CFG_DSA_RX_EN BIT(16) +#define SWITCH_PORT_PCP_REGEN 0x0010 +#define SWITCH_PORT_TRAP_PTP 0x0084 +#define SWITCH_PORT_TRAP_PTP_EN BIT(24) +#define SWITCH_PORT_TRAP_DSTPORT_MASK GENMASK(23, 16) +#define SWITCH_PORT_PCP2IPV 0x008c +#define SWITCH_PORT_STATS_CTRL 0x0200 +#define SWITCH_PORT_SNAPSHOT_EN BIT(0) +#define SWITCH_PORT_STAT_PKT_FLTR_RX 0x0204 +#define SWITCH_PORT_STAT_BYTES_FLTR_RX 0x0208 +#define SWITCH_PORT_STAT_PKT_BUF_OVFL 0x020C +#define SWITCH_PORT_STAT_BYTES_BUF_OVFL 0x0210 +#define SWITCH_PORT_STAT_PKT_ERR 0x0214 +#define SWITCH_PORT_STAT_BYTES_ERR 0x0218 +#define SWITCH_PORT_STAT_DROP_PKT_TX 0x021C +#define SWITCH_PORT_STAT_PKT_VOQ_NQN_IPV0 0x0220 +#define SWITCH_PORT_STAT_PKT_VOQ_NQN_IPV1 0x0224 +#define SWITCH_PORT_STAT_BYTES_VOQ_NQN_IPV0 0x0240 +#define SWITCH_PORT_STAT_BYTES_VOQ_NQN_IPV1 0x0244 +#define SWITCH_PORT_STAT_PKT_VOQ_DQN_IPV0 0x0260 +#define SWITCH_PORT_STAT_PKT_VOQ_DQN_IPV1 0x0264 +#define SWITCH_PORT_STAT_BYTES_VOQ_DQN_IPV0 0x0280 +#define SWITCH_PORT_STAT_BYTES_VOQ_DQN_IPV1 0x0284 +#define SWITCH_PORT_STAT_PKT_VOQ_DROPN_IPV0 0x02A0 +#define SWITCH_PORT_STAT_PKT_VOQ_DROPN_IPV1 0x02A4 +#define SWITCH_PORT_STAT_BYTES_VOQ_DROPN_IPV0 0x02C0 +#define SWITCH_PORT_STAT_BYTES_VOQ_DROPN_IPV1 0x02C4 +#define SWITCH_PORT_STAT_UCAST_PKT_RX 0x02E0 +#define SWITCH_PORT_STAT_UCAST_BYTES_RX 0x02E4 +#define SWITCH_PORT_STAT_UCAST_PKT_TX 0x02E8 +#define SWITCH_PORT_STAT_UCAST_BYTES_TX 0x02EC +#define SWITCH_PORT_STAT_MCAST_PKT_RX 0x02F0 +#define SWITCH_PORT_STAT_MCAST_BYTES_RX 0x02F4 +#define SWITCH_PORT_STAT_MCAST_PKT_TX 0x02F8 +#define SWITCH_PORT_STAT_MCAST_BYTES_TX 0x02FC +#define SWITCH_PORT_STAT_BCAST_PKT_RX 0x0300 +#define SWITCH_PORT_STAT_BCAST_BYTES_RX 0x0304 +#define SWITCH_PORT_STAT_BCAST_PKT_TX 0x0308 +#define SWITCH_PORT_STAT_BCAST_BYTES_TX 0x030C +#define SWITCH_PORT_STAT_CRD_BUFFER_DROP 0x0310 +#define SWITCH_MAS_OP_CTRL 0x0000 +#define SWITCH_MAS_OP_CTRL_TRIGGER BIT(8) +#define SWITCH_MAS_OP_CTRL_OPCODE_MASK GENMASK(7, 4) +#define SWITCH_MAS_OP_CTRL_MAC_INSERT 0 +#define SWITCH_MAS_OP_CTRL_VLAN_ADD 1 +#define SWITCH_MAS_OP_CTRL_MAC_TABLE_SOFT_FLUSH 2 +#define SWITCH_MAS_OP_CTRL_MAC_TABLE_HARD_FLUSH 3 +#define SWITCH_MAS_OP_CTRL_FLUSH_MAC_TABLE_PENDING BIT(2) +#define SWITCH_MAS_PORT_MASK1 0x0004 +#define SWITCH_MAS_PORT_MASK2 0x0008 +#define SWITCH_MAS_FDB_MAC_INSERT_1 0x000c +#define SWITCH_MAS_FDB_MAC_INSERT_2 0x0010 +#define SWITCH_INSERT_MAC_ADDR_LOW_MASK GENMASK(31, 16) +#define SWITCH_INSERT_VALID BIT(0) +#define SWITCH_INSERT_STATIC BIT(1) +#define SWITCH_INSERT_OVERWRITE BIT(3) +#define SWITCH_MAS_VLAN_ID 0x0014 +#define SWITCH_MAS_CFG_MAE 0x0018 +#define CFG_MAE_AGE_TIME_MASK GENMASK(11, 4) +#define AGE_TIME_5MIN_25G 0x19 +#define AGE_TIME_5MIN_10G 0x0A + +#define SWITCH_SOFT_RESET 0x0020 +#define SWITCH_ALL_EX_MAE BIT(0) + +#define SWITCH_PCP_REGEN_VAL 0x77000000 +#define SWITCH_PCP_IPV_MAPPING 0x10000000 +#define SWITCH_PVID 1 + +#define EAPOL_MAC_ADDR 0x0180c2000003 +#define ESMC_MAC_ADDR 0x0180c2000002 +#define PTP_FWD_ADDR 0x011b19000000 + +struct switch_port { + unsigned int config_mask; + void __iomem *reg; +}; + +struct switch_pcp { + u32 pcpregen; + u32 pcp2ipv; + u8 pcp_express; +}; + +struct vlan_cfg_list { + struct list_head list; + u32 port_mask; + u16 vlan_id; + u8 pcp_val; +}; + +struct switch_isr_args { + int (*func)(void *arg); + void *arg; +}; + +struct switch_port_stats { + u64 pkt_fltr_rx; + u64 bytes_fltr_rx; + u64 pkt_buf_ovfl; + u64 bytes_buf_ovfl; + u64 pkt_err; + u64 bytes_err; + u64 drop_pkt_tx; + u64 ipv0_pkt_voq_nqn; + u64 ipv1_pkt_voq_nqn; + u64 ipv0_bytes_voq_nqn; + u64 ipv1_bytes_voq_nqn; + u64 ipv0_pkt_voq_dqn; + u64 ipv1_pkt_voq_dqn; + u64 ipv0_bytes_voq_dqn; + u64 ipv1_bytes_voq_dqn; + u64 ipv0_pkt_voq_dropn; + u64 ipv1_pkt_voq_dropn; + u64 ipv0_bytes_voq_dropn; + u64 ipv1_bytes_voq_dropn; + u64 ucast_pkt_rx; + u64 ucast_bytes_rx; + u64 ucast_pkt_tx; + u64 ucast_bytes_tx; + u64 mcast_pkt_rx; + u64 mcast_bytes_rx; + u64 mcast_pkt_tx; + u64 mcast_bytes_tx; + u64 bcast_pkt_rx; + u64 bcast_bytes_rx; + u64 bcast_pkt_tx; + u64 bcast_bytes_tx; + u64 crd_buffer_drop; +}; + +struct adrv906x_eth_switch { + struct platform_device *pdev; + bool enabled; + struct switch_port switch_port[SWITCH_MAX_PORT_NUM]; + struct list_head vlan_cfg_list; + struct mutex vlan_cfg_list_lock; /* VLan cfg list lock */ + void __iomem *reg_match_action; + void __iomem *reg_switch; + u16 pvid; + int err_irqs[2]; + struct switch_isr_args isr_pre_args; + struct switch_isr_args isr_post_args; + struct switch_port_stats port_stats[SWITCH_MAX_PORT_NUM]; + struct delayed_work update_stats; + bool trap_ptp_fwd_en; +}; + +int adrv906x_switch_port_enable(struct adrv906x_eth_switch *es, int portid, bool enabled); +int adrv906x_switch_register_irqs(struct adrv906x_eth_switch *es, + struct device_node *eth_switch_np); +int adrv906x_switch_probe(struct adrv906x_eth_switch *es, struct platform_device *pdev, + int (*isr_pre_func)(void *), int (*isr_post_func)(void *), void *isr_arg); +int adrv906x_switch_init(struct adrv906x_eth_switch *es); +void adrv906x_switch_set_mae_age_time(struct adrv906x_eth_switch *es, u8 data); +void adrv906x_switch_reset_soft(struct adrv906x_eth_switch *es); +void adrv906x_switch_cleanup(struct adrv906x_eth_switch *es); + +#endif /* __ADRV906X_SWITCH_H__ */ diff --git a/drivers/net/ethernet/adi/adrv906x-tsu.c b/drivers/net/ethernet/adi/adrv906x-tsu.c new file mode 100644 index 00000000000000..22f207036f7a51 --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-tsu.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include "adrv906x-tsu.h" + +void adrv906x_tsu_calculate_phy_delay(struct adrv906x_tsu *tsu, int speed, + bool rs_fec_enabled, u32 bit_slip, + u32 buf_delay_tx, u32 buf_delay_rx) +{ + u32 rx_pcs, rx_pcs_pipeline, rx_pcs_decode, rx_pcs_gearbox, rx_rs_fec, rx_des_delay; + u32 tx_pcs, tx_pcs_pipeline, tx_pcs_encode, tx_pcs_gearbox, tx_rs_fec, tx_ser_delay; + u32 t_div66, t_div64; + u32 tod_cdc_delay; + + t_div66 = (speed == SPEED_25000) ? T_DIV66_25G : T_DIV66_10G; + t_div64 = (speed == SPEED_25000) ? T_DIV64_25G : T_DIV64_10G; + + tx_ser_delay = (speed == SPEED_25000) ? + ADRV906X_SER_DELAY_TX_25G : ADRV906X_SER_DELAY_TX_10G; + rx_des_delay = (speed == SPEED_25000) ? + ADRV906X_DES_DELAY_RX_25G : ADRV906X_DES_DELAY_RX_10G; + + /* ToD_cdc_delay = 1.5 * 1 / hsdig_clk + (cdc_delay + 3.0) * T_div66 */ + + tod_cdc_delay = (1000000000ULL << 16) / tsu->hsdig_clk_rate; + tod_cdc_delay = tod_cdc_delay * 3 / 2; + tod_cdc_delay += (adrv906x_tod_cfg_cdc_delay + 3) * t_div66; + + /* Calculate Rx_static_phy_delay */ + + /* Rx_pcs = Rx_pcs_pipeline + Rx_pcs_decode + Rx_pcs_gearbox + Rx_rs_fec + * + * Rx_pcs_pipeline = 2 * T_div66 + 2 * T_div64 + * Rx_pcs_decode = 6 * T_div66 - bit_slip / 66 * T_div66 + * Rx_pcs_gearbox = floor(fine_buf_receive_delay) * T_div66 + * if RS-FEC is used + * Rx_rs_fec = 234 * T_div66 + */ + + rx_pcs_pipeline = 2 * t_div66 + 2 * t_div64; + rx_pcs_decode = 6 * t_div66 - bit_slip * t_div66 / 66; + rx_pcs_gearbox = buf_delay_rx * t_div66; + rx_rs_fec = rs_fec_enabled ? 234 * t_div66 : 0; + + rx_pcs = rx_pcs_pipeline + rx_pcs_decode + rx_pcs_gearbox + rx_rs_fec; + + /* Rx_static_phy_delay = Rx_pcs - ToD_cdc_delay + PCB_delay + DESER_delay */ + + if (rx_pcs > tod_cdc_delay + tsu->pcb_delay_rx + rx_des_delay) + tsu->phy_delay_rx = rx_pcs - tod_cdc_delay + tsu->pcb_delay_rx + rx_des_delay; + else + tsu->phy_delay_rx = 0; + + /* Calculate Tx_static_phy_delay */ + + /* Tx_pcs = Tx_pcs_pipeline + Tx_pcs_decode + Tx_pcs_gearbox + Tx_rs_fec + * + * Tx_pcs_pipeline = 2 * T_div66 + T_div64 + * Tx_pcs_decode = 4 * T_div66 + * Tx_pcs_gearbox = floor(fine_buf_transmit_delay) * T_div66 + * if RS-FEC is used + * Tx_rs_fec = 17 * T_div66 + */ + + tx_pcs_pipeline = 2 * t_div66 + t_div64; + tx_pcs_encode = 4 * t_div66; + tx_pcs_gearbox = buf_delay_tx * t_div66; + tx_rs_fec = rs_fec_enabled ? 17 * t_div66 : 0; + + tx_pcs = tx_pcs_pipeline + tx_pcs_encode + tx_pcs_gearbox + tx_rs_fec; + + /* Tx_static_phy_delay = Tx_pcs + ToD_cdc_delay + PCB_delay + SER_delay */ + + tsu->phy_delay_tx = tx_pcs + tod_cdc_delay + tsu->pcb_delay_tx + tx_ser_delay; +} + +void adrv906x_tsu_set_phy_delay(struct adrv906x_tsu *tsu) +{ + void __iomem *base = tsu->base; + + iowrite32(tsu->phy_delay_tx, base + ADRV906X_TSU_STATIC_PHY_DELAY_TX); + iowrite32(tsu->phy_delay_rx, base + ADRV906X_TSU_STATIC_PHY_DELAY_RX); +} + +void adrv906x_tsu_set_ptp_timestamping_mode(void __iomem *base) +{ + u32 mode, val; + + mode = ADRV906X_PTP_TIMESTAMPING_MODE_TWO_STEP; + + val = ioread32(base + ADRV906X_TSU_TIMESTAMPING_MODE); + val &= ~ADRV906X_PTP_TIMESTAMPING_MODE; + val |= (mode & ADRV906X_PTP_TIMESTAMPING_MODE); + + iowrite32(val, base + ADRV906X_TSU_TIMESTAMPING_MODE); +} + +void adrv906x_tsu_set_speed(struct adrv906x_tsu *tsu, int speed) +{ + u32 mode, val; + void __iomem *base; + + base = tsu->base; + mode = (speed == SPEED_25000) ? ADRV906X_CORE_SPEED_25G : ADRV906X_CORE_SPEED_10G; + + val = ioread32(base + ADRV906X_TSU_TIMESTAMPING_MODE); + val &= ~ADRV906X_CORE_SPEED; + val |= (mode & ADRV906X_CORE_SPEED); + + iowrite32(val, base + ADRV906X_TSU_TIMESTAMPING_MODE); +} + +int adrv906x_tsu_setup(struct platform_device *pdev, struct adrv906x_tsu *tsu, + struct device_node *eth_port_np) +{ + struct device *dev = &pdev->dev; + struct clk *hsdig_clk; + u32 val, frac_val, reg, len; + int ret; + + hsdig_clk = devm_get_clk_from_child(dev, eth_port_np, "hsdig_clk"); + if (IS_ERR(hsdig_clk)) { + dev_err(dev, "cannot get 'hsdig_clk'"); + return PTR_ERR(hsdig_clk); + } + tsu->hsdig_clk_rate = clk_get_rate(hsdig_clk); + + of_property_read_u32_index(eth_port_np, "reg", 6, ®); + of_property_read_u32_index(eth_port_np, "reg", 7, &len); + + tsu->base = devm_ioremap(dev, reg, len); + if (IS_ERR(tsu->base)) + return PTR_ERR(tsu->base); + + ret = of_property_read_u32(eth_port_np, "adi,pcb-delay-tx-ns", &val); + if (ret < 0 || val > 0xffff) + dev_warn(dev, "dt: adi,pcb-delay-tx-ns missing or invalid, using 0"); + + ret = of_property_read_u32(eth_port_np, "adi,pcb-delay-tx-frac-ns", &frac_val); + if (ret < 0 || val > 0xffff) { + dev_warn(dev, "dt: adi,pcb-delay-tx-frac-ns missing or invalid, using 0"); + frac_val = 0; + } + + tsu->pcb_delay_tx = (val << 16) | (frac_val & 0xFFFF); + dev_info(dev, "tsu pcb delay tx 0x%08x", tsu->pcb_delay_tx); + + ret = of_property_read_u32(eth_port_np, "adi,pcb-delay-rx-ns", &val); + if (ret < 0 || val > 0xffff) { + dev_warn(dev, "dt: adi,pcb-delay-rx-ns missing or invalid, using 0"); + val = 0; + } + + ret = of_property_read_u32(eth_port_np, "adi,pcb-delay-rx-frac-ns", &frac_val); + if (ret < 0 || val > 0xffff) { + dev_warn(dev, "dt: adi,pcb-delay-rx-frac-ns missing or invalid, using 0"); + frac_val = 0; + } + + tsu->pcb_delay_rx = (val << 16) | (frac_val & 0xFFFF); + dev_info(dev, "tsu pcb delay rx 0x%08x", tsu->pcb_delay_rx); + + adrv906x_tsu_set_ptp_timestamping_mode(tsu->base); + + return 0; +} + +void adrv906x_tsu_compensate_tx_tstamp(struct adrv906x_tsu *tsu, struct timespec64 *ts) +{ + timespec64_add_ns(ts, tsu->phy_delay_tx >> 16); +} diff --git a/drivers/net/ethernet/adi/adrv906x-tsu.h b/drivers/net/ethernet/adi/adrv906x-tsu.h new file mode 100644 index 00000000000000..56d70099e4b207 --- /dev/null +++ b/drivers/net/ethernet/adi/adrv906x-tsu.h @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __ADRV906X_TSU_H__ +#define __ADRV906X_TSU_H__ + +#include +#include +#include +#include +#include + +#define ADRV906X_TSU_STATIC_PHY_DELAY_RX 0x0000003C +#define ADRV906X_TSU_STATIC_PHY_DELAY_TX 0x00000040 +#define ADRV906X_TSU_TIMESTAMPING_MODE 0x00000038 +#define ADRV906X_CORE_SPEED BIT(8) +#define ADRV906X_CORE_SPEED_10G 0x00000000 +#define ADRV906X_CORE_SPEED_25G 0x00000100 +#define ADRV906X_PTP_TIMESTAMPING_MODE GENMASK(1, 0) +#define ADRV906X_PTP_TIMESTAMPING_MODE_TWO_STEP 0x00000000 /* Two-step */ +#define ADRV906X_PTP_TIMESTAMPING_MODE_ONE_STEP 0x00000001 /* One-step */ +#define ADRV906X_PTP_TIMESTAMPING_MODE_TRANSP 0x00000002 /* Transparent Clock */ + +/* T_div66 is PCS RX output clock period, its [31:16] is nanosecond part + * its [15:0] is the fractional nanosecond part + * So it can be calculated as (1,000,000,000 * 0x10000) / frequency + * where frequency is 156.25MHz for 10G, 390.625MHz for 25G + */ +#define T_DIV66_10G 419430 /* 0x66666 */ +#define T_DIV66_25G 167772 /* 0x28f5c */ + +/* T_div64 is PCS RX input clock period. T_div64 = T_div66 * 64 / 66 */ +#define T_DIV64_10G 406720 /* 0x634c0 */ +#define T_DIV64_25G 162688 /* 0x27b80 */ + +/* SerDes delays */ +#define ADRV906X_DES_DELAY_RX_10G 0x000363D7 /* 3.39 ns */ +#define ADRV906X_DES_DELAY_RX_25G 0x0002C9FA /* 2.79 ns */ +#define ADRV906X_SER_DELAY_TX_10G 0x000C051F /* 12.02 ns */ +#define ADRV906X_SER_DELAY_TX_25G 0x000975A3 /* 9.46 ns */ + +extern int adrv906x_tod_cfg_cdc_delay; + +struct adrv906x_tsu { + void __iomem *base; + unsigned long hsdig_clk_rate; + u32 pcb_delay_tx; /* Upper 16 is ns and lower 16 is fractional ns */ + u32 pcb_delay_rx; /* Upper 16 is ns and lower 16 is fractional ns */ + u32 phy_delay_tx; /* Upper 16 is ns and lower 16 is fractional ns */ + u32 phy_delay_rx; /* Upper 16 is ns and lower 16 is fractional ns */ +}; + +void adrv906x_tsu_set_speed(struct adrv906x_tsu *tsu, int speed); +int adrv906x_tsu_setup(struct platform_device *pdev, struct adrv906x_tsu *tsu, + struct device_node *eth_port_np); +void adrv906x_tsu_calculate_phy_delay(struct adrv906x_tsu *tsu, int speed, + bool rs_fec_enabled, u32 bit_slip, + u32 buf_delay_tx, u32 buf_delay_rx); +void adrv906x_tsu_set_phy_delay(struct adrv906x_tsu *tsu); +void adrv906x_tsu_set_ptp_timestamping_mode(void __iomem *base); +void adrv906x_tsu_compensate_tx_tstamp(struct adrv906x_tsu *tsu, struct timespec64 *ts); + +#endif /* __ADRV906X_TSU_H__ */ diff --git a/drivers/net/ethernet/adi/macsec/cco_ciphersuite_memmap.h b/drivers/net/ethernet/adi/macsec/cco_ciphersuite_memmap.h new file mode 100644 index 00000000000000..5a38401116ad71 --- /dev/null +++ b/drivers/net/ethernet/adi/macsec/cco_ciphersuite_memmap.h @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023-2025, Analog Devices Incorporated, All Rights Reserved + */ +/******************************************************************************/ +/* DO NOT MODIFY */ +/* THIS FILE IS AUTOGENERATED AND ALL CHANGES WILL BE LOST */ +/******************************************************************************/ +#ifndef _MACSEC_TOP_CIPHERSUITE_MEMMAP_H_ +#define _MACSEC_TOP_CIPHERSUITE_MEMMAP_H_ + +/* Cipher Suite capability, control and status */ +#define CIPHERSUITE_BASE_ADDR 0x00000700 +#define CIPHERSUITE_STRIDE 0x00000100 +/******************************************************************************/ +/* Control and status indicator for rule / configuration insert */ +#define CIPHERSUITE_CS_CTRL_BASE_ADDR 0x00000000 +/* WR_TRIGGER W1C Trigger a write operation */ +#define CIPHERSUITE_CS_CTRL_WR_TRIGGER_MASK 0x00010000 +#define CIPHERSUITE_CS_CTRL_WR_TRIGGER_SHIFT 16 + +/* AES_MODE_SEL RW Cipher Suite selection: + * The Cipher Suite Identifier (10.7.25) for the cipher suite + * 0x0: AES-GCM-128 + * 0x1: AES-GCM-256 + * 0x2: AES-GCM-XPN-128 + * 0x3: AES-GCM-XPN-256 */ +#define CIPHERSUITE_CS_CTRL_AES_MODE_SEL_MASK 0x0000c000 +#define CIPHERSUITE_CS_CTRL_AES_MODE_SEL_SHIFT 14 + +/* RECEIVE_SEL RW Rx key table selection insert + * SAK creation: + * True if the key is to be installed for reception */ +#define CIPHERSUITE_CS_CTRL_RECEIVE_SEL_MASK 0x00002000 +#define CIPHERSUITE_CS_CTRL_RECEIVE_SEL_SHIFT 13 + +/* TRANSMIT_SEL RW Tx key table selection insert + * SAK creation: + * True if the key is to be installed for transmission */ +#define CIPHERSUITE_CS_CTRL_TRANSMIT_SEL_MASK 0x00001000 +#define CIPHERSUITE_CS_CTRL_TRANSMIT_SEL_SHIFT 12 + +/* INDEX RW Index number of key to read/write */ +#define CIPHERSUITE_CS_CTRL_INDEX_MASK 0x00000fff +#define CIPHERSUITE_CS_CTRL_INDEX_SHIFT 0 + +/******************************************************************************/ +/* SAK creation: + * The SAK value + * 31 downto 0 */ +#define CIPHERSUITE_CS_SAK_0_BASE_ADDR 0x00000004 +/* VAL RW ---- */ +#define CIPHERSUITE_CS_SAK_0_VAL_MASK 0xffffffff +#define CIPHERSUITE_CS_SAK_0_VAL_SHIFT 0 + +/******************************************************************************/ +/* SAK creation: + * The SAK value + * 63 downto 32 */ +#define CIPHERSUITE_CS_SAK_1_BASE_ADDR 0x00000008 +/* VAL RW ---- */ +#define CIPHERSUITE_CS_SAK_1_VAL_MASK 0xffffffff +#define CIPHERSUITE_CS_SAK_1_VAL_SHIFT 0 + +/******************************************************************************/ +/* SAK creation: + * The SAK value + * 95 downto 64 */ +#define CIPHERSUITE_CS_SAK_2_BASE_ADDR 0x0000000c +/* VAL RW ---- */ +#define CIPHERSUITE_CS_SAK_2_VAL_MASK 0xffffffff +#define CIPHERSUITE_CS_SAK_2_VAL_SHIFT 0 + +/******************************************************************************/ +/* SAK creation: + * The SAK value + * 127 downto 96 */ +#define CIPHERSUITE_CS_SAK_3_BASE_ADDR 0x00000010 +/* VAL RW ---- */ +#define CIPHERSUITE_CS_SAK_3_VAL_MASK 0xffffffff +#define CIPHERSUITE_CS_SAK_3_VAL_SHIFT 0 + +/******************************************************************************/ +/* SAK creation: + * The SAK value + * 159 downto 128 */ +#define CIPHERSUITE_CS_SAK_4_BASE_ADDR 0x00000014 +/* VAL RW ---- */ +#define CIPHERSUITE_CS_SAK_4_VAL_MASK 0xffffffff +#define CIPHERSUITE_CS_SAK_4_VAL_SHIFT 0 + +/******************************************************************************/ +/* SAK creation: + * The SAK value + * 191 downto 160 */ +#define CIPHERSUITE_CS_SAK_5_BASE_ADDR 0x00000018 +/* VAL RW ---- */ +#define CIPHERSUITE_CS_SAK_5_VAL_MASK 0xffffffff +#define CIPHERSUITE_CS_SAK_5_VAL_SHIFT 0 + +/******************************************************************************/ +/* SAK creation: + * The SAK value + * 223 downto 192 */ +#define CIPHERSUITE_CS_SAK_6_BASE_ADDR 0x0000001c +/* VAL RW ---- */ +#define CIPHERSUITE_CS_SAK_6_VAL_MASK 0xffffffff +#define CIPHERSUITE_CS_SAK_6_VAL_SHIFT 0 + +/******************************************************************************/ +/* SAK creation: + * The SAK value + * 255 downto 224 */ +#define CIPHERSUITE_CS_SAK_7_BASE_ADDR 0x00000020 +/* VAL RW ---- */ +#define CIPHERSUITE_CS_SAK_7_VAL_MASK 0xffffffff +#define CIPHERSUITE_CS_SAK_7_VAL_SHIFT 0 + +/******************************************************************************/ +/* SAK creation insert: + * If the Current Cipher Suite uses extended packet numbering, + * Salt (McGrew [B11]), a 96-bit parameter provided to the Current + * Cipher Suite for subsequent protection and validation operations + * 32 downto 0 */ +#define CIPHERSUITE_CS_SALT_0_BASE_ADDR 0x00000024 +/* VAL RW ---- */ +#define CIPHERSUITE_CS_SALT_0_VAL_MASK 0xffffffff +#define CIPHERSUITE_CS_SALT_0_VAL_SHIFT 0 + +/******************************************************************************/ +/* SAK creation insert: + * If the Current Cipher Suite uses extended packet numbering, + * Salt (McGrew [B11]), a 96-bit parameter provided to the Current + * Cipher Suite for subsequent protection and validation operations + * 63 downto 32 */ +#define CIPHERSUITE_CS_SALT_1_BASE_ADDR 0x00000028 +/* VAL RW ---- */ +#define CIPHERSUITE_CS_SALT_1_VAL_MASK 0xffffffff +#define CIPHERSUITE_CS_SALT_1_VAL_SHIFT 0 + +/******************************************************************************/ +/* SAK creation insert: + * If the Current Cipher Suite uses extended packet numbering, + * Salt (McGrew [B11]), a 96-bit parameter provided to the Current + * Cipher Suite for subsequent protection and validation operations + * 95 downto 64 */ +#define CIPHERSUITE_CS_SALT_2_BASE_ADDR 0x0000002c +/* VAL RW ---- */ +#define CIPHERSUITE_CS_SALT_2_VAL_MASK 0xffffffff +#define CIPHERSUITE_CS_SALT_2_VAL_SHIFT 0 + +#endif /* _MACSEC_TOP_CIPHERSUITE_MEMMAP_H_ */ diff --git a/drivers/net/ethernet/adi/macsec/cco_macsec.c b/drivers/net/ethernet/adi/macsec/cco_macsec.c new file mode 100644 index 00000000000000..edfcb2feb057a7 --- /dev/null +++ b/drivers/net/ethernet/adi/macsec/cco_macsec.c @@ -0,0 +1,2997 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023-2025, Analog Devices Incorporated, All Rights Reserved + */ + +#include "cco_macsec.h" +#include + +static u32 default_txsc_pn_thr = 0xff000000; module_param(default_txsc_pn_thr, uint, 0644); +static u32 debug_max_framesize = 0; module_param(debug_max_framesize, uint, 0644); +static bool debug_xpn = false; module_param(debug_xpn, bool, 0644); +static bool debug_sw_macsec = false; module_param(debug_sw_macsec, bool, 0644); + +// Find the SecY index from the provided ctx->secy pointer and netdev_priv(ctx->netdev) secy_array. +static bool get_secy(struct macsec_context *ctx, u32 *secy_index) +{ + const struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + int i; + + for (i = 0; i < macsec_priv->capabilities.no_of_secys && i < CCO_MACSEC_SECY_MAX; ++i) + if (ctx->secy == macsec_priv->secy_array[i]) { + *secy_index = i; + return true; + } + return false; +} + +// Find the first free SecY index +static bool get_free_secy(struct macsec_context *ctx, u32 *secy_index) +{ + const struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + int i; + + for (i = 0; i < macsec_priv->capabilities.no_of_secys && i < CCO_MACSEC_SECY_MAX; ++i) + if (!macsec_priv->secy_array[i]) { + *secy_index = i; + return true; + } + return false; +} + +// Count number of SecYs +static u32 count_secy(struct net_device *netdev) +{ + const struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(netdev); + int i; + u32 cnt = 0; + + for (i = 0; i < macsec_priv->capabilities.no_of_secys && i < CCO_MACSEC_SECY_MAX; ++i) + if (macsec_priv->secy_array[i]) { + cnt++; + } + return cnt; +} + +// Find SecY index from SecY pointer +static bool find_secy(struct macsec_context *ctx, u32 *secy_index) +{ + const struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + const struct macsec_secy *secy = ctx->secy; + int i; + + for (i = 0; i < macsec_priv->capabilities.no_of_secys && i < CCO_MACSEC_SECY_MAX; ++i) + if (macsec_priv->secy_array[i] == secy) { + *secy_index = i; + return true; + } + return false; +} + +// Find the first free Rx-SC index +static bool get_free_rxsc(struct macsec_context *ctx, u32 secy_index, u32 *rxsc_index) +{ + const struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + int i; + + for (i = 0; i < macsec_priv->capabilities.maxRxChannels && i < CCO_MACSEC_RXSC_MAX; ++i) + if (!macsec_priv->rxsc_array[secy_index][i]) { + *rxsc_index = i; + return true; + } + return false; +} + +// Find Rx-SC index from Rx-SC pointer +static bool find_rxsc(const struct cco_macsec_priv *macsec_priv, const struct macsec_rx_sc *rx_sc, + u32 secy_index, u32 *rxsc_index) +{ + int i; + + for (i = 0; i < macsec_priv->capabilities.maxRxChannels && i < CCO_MACSEC_RXSC_MAX; ++i) + if (macsec_priv->rxsc_array[secy_index][i] == rx_sc) { + *rxsc_index = i; + return true; + } + return false; +} + +// write SecY + Tx-SC registers +static void write_secy_txsc(struct net_device *netdev, + const struct macsec_secy *secy, const u32 secy_index, + const u8 disable, const u8 sa_enabled, + const u8 vlan_in_clear, const u8 conf_offs) +{ + u8 validate_frames = 0, val; + u32 max_framesize; + + if (debug_max_framesize) + max_framesize = debug_max_framesize; + else + cco_macsec_max_framesize_get(netdev, &max_framesize); + + // Configure the SecY registers: + cco_macsec_reg_wr(netdev, SECY_CONFIG_BASE_ADDR + SECY_CONFIG_PORT_CONFIG_BASE_ADDR, + disable ? 0 : (vlan_in_clear << SECY_CONFIG_PORT_CONFIG_VLANINCLEAR_WR_SHIFT) | + SECY_CONFIG_PORT_CONFIG_CONTROLLEDPORTENABLED_WR_MASK); + cco_macsec_reg_wr(netdev, SECY_CONFIG_BASE_ADDR + SECY_CONFIG_TX_CONFIG_BASE_ADDR, + disable ? 0 : + (secy->tx_sc.scb ? SECY_CONFIG_TX_CONFIG_USESCB_WR_MASK : 0) | + (secy->tx_sc.end_station ? SECY_CONFIG_TX_CONFIG_USEES_WR_MASK : 0) | + (secy->tx_sc.send_sci ? SECY_CONFIG_TX_CONFIG_ALWAYSINCLUDESCI_WR_MASK : 0) | + (secy->protect_frames ? SECY_CONFIG_TX_CONFIG_PROTECTFRAMES_WR_MASK : 0)); + if (secy->validate_frames == MACSEC_VALIDATE_DISABLED) + validate_frames = 1; // disabled + else if (secy->validate_frames == MACSEC_VALIDATE_CHECK) + validate_frames = 2; // check + else if (secy->validate_frames == MACSEC_VALIDATE_STRICT) + validate_frames = 3; // strict + cco_macsec_reg_wr(netdev, SECY_CONFIG_BASE_ADDR + SECY_CONFIG_RX_CONFIG_BASE_ADDR, + disable ? 0 : + (secy->replay_protect ? SECY_CONFIG_RX_CONFIG_REPLAYPROTECT_WR_MASK : 0) | + (validate_frames << SECY_CONFIG_RX_CONFIG_VALIDATEFRAMES_WR_SHIFT)); + cco_macsec_reg_wr(netdev, SECY_CONFIG_BASE_ADDR + SECY_CONFIG_RX_REPLAYWINDOW_WR_BASE_ADDR, + disable ? 0 : secy->replay_window); + if (secy->key_len == (128/8)) { + if (!secy->xpn) + val = 0; // 0x0: AES-GCM-128 + else + val = 2; // 0x2: AES-GCM-XPN-128 + } else { // secy->key_len == (256/8)) + if (!secy->xpn) + val = 1; // 0x1: AES-GCM-256 + else + val = 3; // 0x3: AES-GCM-XPN-256 + } + cco_macsec_reg_wr(netdev, SECY_CONFIG_BASE_ADDR + SECY_CONFIG_CIPHERSUITE_CONFIG_BASE_ADDR, + disable ? 0 : + (conf_offs << SECY_CONFIG_CIPHERSUITE_CONFIG_CONFIDENTIALITYOFFSET_WR_SHIFT) | + (val << SECY_CONFIG_CIPHERSUITE_CONFIG_CURRENTCIPHERSUITE_WR_SHIFT) | + (0 << SECY_CONFIG_CIPHERSUITE_CONFIG_REQUIRECONFIDENTIALITY_WR_SHIFT)); + cco_macsec_reg_wr(netdev, SECY_CONFIG_BASE_ADDR + SECY_CONFIG_MAX_FRAME_LENGHT_WR_BASE_ADDR, + disable ? 0 : max_framesize); + cco_macsec_reg_wr(netdev, SECY_CONFIG_BASE_ADDR + SECY_CONFIG_CONFIG_CTRL_BASE_ADDR, + SECY_CONFIG_CONFIG_CTRL_CIPHERSUITE_CONFIG_EN_MASK | + SECY_CONFIG_CONFIG_CTRL_RX_CONFIG_EN_MASK | + SECY_CONFIG_CONFIG_CTRL_TX_CONFIG_EN_MASK | + SECY_CONFIG_CONFIG_CTRL_PORT_CONFIG_EN_MASK | + SECY_CONFIG_CONFIG_CTRL_WR_TRIGGER_MASK | + (secy_index << SECY_CONFIG_CONFIG_CTRL_SECY_INDEX_SHIFT)); + + // Configure the Tx-SC registers: + cco_macsec_reg_wr(netdev, TRANSMITSC_BASE_ADDR + TRANSMITSC_TX_SC_SCI_0_WR_BASE_ADDR, + disable ? 0 : ((((u8*)&secy->sci)[4] << 24) | (((u8*)&secy->sci)[5] << 16) | + (((u8*)&secy->sci)[6] << 8) | ((u8*)&secy->sci)[7])); + cco_macsec_reg_wr(netdev, TRANSMITSC_BASE_ADDR + TRANSMITSC_TX_SC_SCI_1_WR_BASE_ADDR, + disable ? 0 : ((((u8*)&secy->sci)[0] << 24) | (((u8*)&secy->sci)[1] << 16) | + (((u8*)&secy->sci)[2] << 8) | ((u8*)&secy->sci)[3])); + if ((sa_enabled & (0x10 << secy->tx_sc.encoding_sa)) && !disable) { + cco_macsec_reg_wr(netdev, TRANSMITSC_BASE_ADDR + TRANSMITSC_TX_SC_TRANSMITSC_CFG_BASE_ADDR, + ((1 << secy->tx_sc.encoding_sa) << TRANSMITSC_TX_SC_TRANSMITSC_CFG_ENABLETRANSMIT_SA_WR_SHIFT)); + } else { + cco_macsec_reg_wr(netdev, TRANSMITSC_BASE_ADDR + TRANSMITSC_TX_SC_TRANSMITSC_CFG_BASE_ADDR, 0); + } + cco_macsec_reg_wr(netdev, TRANSMITSC_BASE_ADDR + TRANSMITSC_TX_SC_CTRL_BASE_ADDR, + TRANSMITSC_TX_SC_CTRL_WR_TRIGGER_MASK | + (secy_index << TRANSMITSC_TX_SC_CTRL_SECY_INDEX_SHIFT)); +} + +static int cco_macsec_add_secy(struct macsec_context *ctx) +{ + struct macsec_secy *secy = ctx->secy; + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + u32 secy_index; + u8 vlan_in_clear, conf_offs; + u32 secy_cnt, val; + + if (debug_xpn) + secy->xpn = true; + + // In the prepare phase, check that ctx->secy->xpn, ctx->secy->key_len and ctx->secy->icv_len + // matches MACsec IP capabilities; otherwise fail. Also check that a new SecY index can be allocated. + if (secy->xpn) { + if (!(macsec_priv->capabilities.available_ciphersuites & + (CCO_CS_AES_GCM_XPN_128 | CCO_CS_AES_GCM_XPN_256))) + return -EOPNOTSUPP; + } else if (!(macsec_priv->capabilities.available_ciphersuites & + (CCO_CS_AES_GCM_128 | CCO_CS_AES_GCM_256))) + return -EOPNOTSUPP; + if ((secy->key_len == (128/8)) && + !(macsec_priv->capabilities.available_ciphersuites & + (CCO_CS_AES_GCM_128 | CCO_CS_AES_GCM_XPN_128))) + return -EOPNOTSUPP; + if ((secy->key_len == (256/8)) && + !(macsec_priv->capabilities.available_ciphersuites & + (CCO_CS_AES_GCM_256 | CCO_CS_AES_GCM_XPN_256))) + return -EOPNOTSUPP; + if (secy->icv_len != macsec_priv->capabilities.ICVLength) + return -EOPNOTSUPP; + if (!secy->netdev) + return EINVAL; + if (!get_free_secy(ctx, &secy_index)) + return -ENOSPC; + + if (ctx->prepare) + return 0; + + // netdev_info(ctx->netdev, "%s\n", __func__); + + secy_cnt = count_secy(ctx->netdev); + if (secy_cnt == 0) { + val = cco_macsec_reg_rd(ctx->netdev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_GENERAL_CTRL_BASE_ADDR); + cco_macsec_reg_wr(ctx->netdev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_GENERAL_CTRL_BASE_ADDR, + val | MACSEC_CORE_GENERAL_CTRL_MACSEC_EN_MASK); + } + memset(&macsec_priv->dev_stats[secy_index], 0, sizeof(macsec_priv->dev_stats[secy_index])); + memset(&macsec_priv->txsc_stats[secy_index], 0, sizeof(macsec_priv->txsc_stats[secy_index])); + memset(&macsec_priv->port_stats[secy_index], 0, sizeof(macsec_priv->port_stats[secy_index])); + memset(&macsec_priv->uport_stats[secy_index], 0, sizeof(macsec_priv->uport_stats[secy_index])); + memset(&macsec_priv->ext_port_stats[secy_index], 0, sizeof(macsec_priv->ext_port_stats[secy_index])); + + vlan_in_clear = macsec_priv->secy_vlan_in_clear[secy_index]; + conf_offs = macsec_priv->secy_confidentiality_offs[secy_index]; + write_secy_txsc(ctx->netdev, secy, secy_index, 0, macsec_priv->sa_enabled[secy_index][0], vlan_in_clear, conf_offs); + + macsec_priv->secy_array[secy_index] = secy; + macsec_priv->secy_ifIndex[secy_index] = secy->netdev->ifindex; + macsec_priv->txsc_ext[secy_index].createdTime = jiffies; + macsec_priv->txsc_ext[secy_index].startedTime = + macsec_priv->txsc_ext[secy_index].createdTime; + macsec_priv->txsc_ext[secy_index].stoppedTime = + macsec_priv->txsc_ext[secy_index].createdTime; + return 0; +} + +static int cco_macsec_upd_secy(struct macsec_context *ctx) +{ + struct macsec_secy *secy = ctx->secy; + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + u32 secy_index; + u8 vlan_in_clear, conf_offs; + + if (debug_xpn) + secy->xpn = true; + + // In the prepare phase, check that ctx->secy->xpn, ctx->secy->key_len and ctx->secy->icv_len + // matches MACsec IP capabilities; otherwise fail. Also check that SecY index can be found. + if (secy->xpn && + (macsec_priv->capabilities.available_ciphersuites & (CCO_CS_AES_GCM_XPN_128 | CCO_CS_AES_GCM_XPN_256)) == 0) + return -EOPNOTSUPP; + if (secy->key_len == (128/8) && + (macsec_priv->capabilities.available_ciphersuites & (CCO_CS_AES_GCM_128 | CCO_CS_AES_GCM_XPN_128)) == 0) + return -EOPNOTSUPP; + if (secy->key_len == (256/8) && + (macsec_priv->capabilities.available_ciphersuites & (CCO_CS_AES_GCM_256 | CCO_CS_AES_GCM_XPN_256)) == 0) + return -EOPNOTSUPP; + if (secy->icv_len != macsec_priv->capabilities.ICVLength) + return -EOPNOTSUPP; + if (!find_secy(ctx, &secy_index)) + return -EINVAL; + if (!secy->netdev) + return EINVAL; + + if (ctx->prepare) + return 0; + + // netdev_info(ctx->netdev, "%s\n", __func__); + + vlan_in_clear = macsec_priv->secy_vlan_in_clear[secy_index]; + conf_offs = macsec_priv->secy_confidentiality_offs[secy_index]; + write_secy_txsc(ctx->netdev, secy, secy_index, 0, macsec_priv->sa_enabled[secy_index][0], vlan_in_clear, conf_offs); + macsec_priv->secy_ifIndex[secy_index] = secy->netdev->ifindex; + + return 0; +} + +static void write_rxsc(struct net_device *netdev, + struct macsec_rx_sc *rx_sc, const u32 secy_index, + const u32 peer_index, const u8 disable) +{ + // Configure the Rx-SC registers: + cco_macsec_reg_wr(netdev, RECEIVESC_BASE_ADDR + RECEIVESC_RX_SC_SCI_0_WR_BASE_ADDR, + disable ? 0 : ((((u8*)&rx_sc->sci)[4] << 24) | (((u8*)&rx_sc->sci)[5] << 16) | + (((u8*)&rx_sc->sci)[6] << 8) | ((u8*)&rx_sc->sci)[7])); + cco_macsec_reg_wr(netdev, RECEIVESC_BASE_ADDR + RECEIVESC_RX_SC_SCI_1_WR_BASE_ADDR, + disable ? 0 : ((((u8*)&rx_sc->sci)[0] << 24) | (((u8*)&rx_sc->sci)[1] << 16) | + (((u8*)&rx_sc->sci)[2] << 8) | ((u8*)&rx_sc->sci)[3])); + cco_macsec_reg_wr(netdev, RECEIVESC_BASE_ADDR + RECEIVESC_RX_SC_CTRL_BASE_ADDR, + RECEIVESC_RX_SC_CTRL_WR_TRIGGER_MASK | + (peer_index << RECEIVESC_RX_SC_CTRL_PEER_INDEX_SHIFT) | + (secy_index << RECEIVESC_RX_SC_CTRL_SECY_INDEX_SHIFT)); +} + +static void clear_rxsa(struct macsec_context *ctx, struct cco_macsec_priv *macsec_priv, + const u32 secy_index, const u32 peer_index, const u8 assoc_num, const u32 key_index) +{ + // Configure Rx-SA registers (disable): + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_AN_BASE_ADDR, + (assoc_num << RECEIVESA_RX_SA_AN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_KEY_INDEX_WR_BASE_ADDR, + key_index << RECEIVESA_RX_SA_KEY_INDEX_WR_VAL_SHIFT); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_RECEIVESA_CFG_BASE_ADDR, 0); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_CTRL_BASE_ADDR, + (peer_index << RECEIVESA_RX_SA_CTRL_PEER_INDEX_SHIFT) | + (secy_index << RECEIVESA_RX_SA_CTRL_SECY_INDEX_SHIFT) | + RECEIVESA_RX_SA_CTRL_WR_TRIGGER_MASK); + + macsec_priv->sa_enabled[secy_index][peer_index] &= ~(1 << assoc_num); + + macsec_priv->key_refcnt[key_index]--; + if (macsec_priv->key_refcnt[key_index] == 0) + macsec_priv->key_use[key_index] = 0; + macsec_priv->rxsa_ext[secy_index][peer_index][assoc_num].createdTime = 0; + macsec_priv->rxsa_ext[secy_index][peer_index][assoc_num].startedTime = 0; + macsec_priv->rxsa_ext[secy_index][peer_index][assoc_num].stoppedTime = 0; +} + +static void clear_rxsc(struct macsec_context *ctx, struct cco_macsec_priv *macsec_priv, + struct macsec_rx_sc *rx_sc, + const u32 secy_index, const u32 peer_index) +{ + struct macsec_rx_sa *rx_sa; + u8 sa_ix; + u32 i; + int key_index; + + // clear the Rx-SC registers: + write_rxsc(ctx->netdev, rx_sc, secy_index, peer_index, 1); + + macsec_priv->rxsc_array[secy_index][peer_index] = NULL; + macsec_priv->rxsc_ext[secy_index][peer_index].createdTime = 0; + macsec_priv->rxsc_ext[secy_index][peer_index].startedTime = 0; + macsec_priv->rxsc_ext[secy_index][peer_index].stoppedTime = 0; + + // Delete any Rx-SA's also: + for (sa_ix = 0; sa_ix < MACSEC_NUM_AN; ++sa_ix) { + rx_sa = rtnl_dereference(rx_sc->sa[sa_ix]); + if (!rx_sa) + continue; + // check if rx_sa->key.id exists in the netdev_priv(ctx->netdev) key_id_table + // and whether it is enabled for Rx (check key_use). + key_index = -1; + for (i = 0; i < macsec_priv->capabilities.no_of_key_entries_rx && i < CCO_MACSEC_KEYS; ++i) { + if (!(macsec_priv->key_use[i] & CCO_MACSEC_KEY_RX)) + continue; + if (memcmp(rx_sa->key.id, macsec_priv->key_id_table[i], MACSEC_KEYID_LEN) == 0) { + // found key.id + key_index = i; + break; + } + } + if (key_index < 0) + continue; + clear_rxsa(ctx, macsec_priv, secy_index, peer_index, sa_ix, key_index); + } +} + +static void clear_txsa(struct macsec_context *ctx, struct cco_macsec_priv *macsec_priv, + const u32 secy_index, const u8 assoc_num, const u32 key_index) +{ + // Configure Tx-SA registers (disable): + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_AN_BASE_ADDR, + (assoc_num << TRANSMITSA_TX_SA_AN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_KEY_INDEX_WR_BASE_ADDR, + key_index << TRANSMITSA_TX_SA_KEY_INDEX_WR_VAL_SHIFT); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_TRANSMITSA_CFG_BASE_ADDR, 0); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_CTRL_BASE_ADDR, + (secy_index << TRANSMITSA_TX_SA_CTRL_SECY_INDEX_SHIFT) | + TRANSMITSA_TX_SA_CTRL_WR_TRIGGER_MASK); + + macsec_priv->sa_enabled[secy_index][0] &= ~(0x10 << assoc_num); + + macsec_priv->key_refcnt[key_index]--; + if (macsec_priv->key_refcnt[key_index] == 0) + macsec_priv->key_use[key_index] = 0; + macsec_priv->txsa_ext[secy_index][assoc_num].createdTime = 0; + macsec_priv->txsa_ext[secy_index][assoc_num].startedTime = 0; + macsec_priv->txsa_ext[secy_index][assoc_num].stoppedTime = 0; +} + +static int cco_macsec_del_secy(struct macsec_context *ctx) +{ + struct macsec_secy *secy = ctx->secy; + struct macsec_rx_sc *rx_sc; + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + struct macsec_tx_sa *tx_sa; + u32 secy_index, peer_index, i, secy_cnt, val; + u8 sa_ix; + int key_index; + + if (!find_secy(ctx, &secy_index)) + return -EINVAL; + + if (ctx->prepare) + return 0; + + // netdev_info(ctx->netdev, "%s\n", __func__); + + write_secy_txsc(ctx->netdev, secy, secy_index, 1, 0, 0, 0); + + // Delete any Rx-SCs (and Rx-SA's): + for (rx_sc = rcu_dereference_bh(secy->rx_sc); rx_sc; + rx_sc = rcu_dereference_bh(rx_sc->next)) { + // get the peer_index: + if (!find_rxsc(macsec_priv, rx_sc, secy_index, &peer_index)) + continue; + clear_rxsc(ctx, macsec_priv, rx_sc, secy_index, peer_index); + } + // Delete any Tx-SA's: + for (sa_ix = 0; sa_ix < MACSEC_NUM_AN; ++sa_ix) { + tx_sa = rcu_dereference_bh(secy->tx_sc.sa[sa_ix]); + if (!tx_sa) + continue; + // check if tx_sa->key.id exists in the netdev_priv(ctx->netdev) key_id_table + // and whether it is enabled for Tx (check key_use). + key_index = -1; + for (i = 0; i < macsec_priv->capabilities.no_of_key_entries_tx && i < CCO_MACSEC_KEYS; ++i) { + if (!(macsec_priv->key_use[i] & CCO_MACSEC_KEY_TX)) + continue; + if (memcmp(tx_sa->key.id, macsec_priv->key_id_table[i], MACSEC_KEYID_LEN) == 0) { + // found key.id + key_index = i; + break; + } + } + if (key_index < 0) + continue; + clear_txsa(ctx, macsec_priv, secy_index, sa_ix, key_index); + } + + macsec_priv->secy_vlan_in_clear[secy_index] = 0; + macsec_priv->secy_array[secy_index] = NULL; + macsec_priv->secy_ifIndex[secy_index] = 0; + macsec_priv->txsc_ext[secy_index].createdTime = 0; + macsec_priv->txsc_ext[secy_index].startedTime = 0; + macsec_priv->txsc_ext[secy_index].stoppedTime = 0; + secy_cnt = count_secy(ctx->netdev); + if (secy_cnt == 0) { + val = cco_macsec_reg_rd(ctx->netdev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_GENERAL_CTRL_BASE_ADDR); + cco_macsec_reg_wr(ctx->netdev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_GENERAL_CTRL_BASE_ADDR, + val & ~MACSEC_CORE_GENERAL_CTRL_MACSEC_EN_MASK); + } + return 0; +} + +static int cco_macsec_add_rxsc(struct macsec_context *ctx) +{ + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + u8 disable = ctx->rx_sc->active ? 0 : 1; + u32 secy_index, peer_index; + + // get the SecY: + if (!find_secy(ctx, &secy_index)) + return -EINVAL; + + // check that a new Rx-SC can be added: + if (!get_free_rxsc(ctx, secy_index, &peer_index)) + return -ENOSPC; + + if (ctx->prepare) + return 0; + + // netdev_info(ctx->netdev, "%s\n", __func__); + + memset(&macsec_priv->rxsc_stats[secy_index][peer_index], 0, sizeof(macsec_priv->rxsc_stats[secy_index][peer_index])); + + write_rxsc(ctx->netdev, ctx->rx_sc, secy_index, peer_index, disable); + + macsec_priv->rxsc_array[secy_index][peer_index] = ctx->rx_sc; + macsec_priv->rxsc_ext[secy_index][peer_index].createdTime = jiffies; + macsec_priv->rxsc_ext[secy_index][peer_index].startedTime = + macsec_priv->rxsc_ext[secy_index][peer_index].createdTime; + macsec_priv->rxsc_ext[secy_index][peer_index].stoppedTime = + macsec_priv->rxsc_ext[secy_index][peer_index].createdTime; + return 0; +} + +static int cco_macsec_upd_rxsc(struct macsec_context *ctx) +{ + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + struct macsec_rx_sc *rx_sc = ctx->rx_sc; + u8 disable = rx_sc->active ? 0 : 1; + u32 secy_index, peer_index; + + // get the SecY: + if (!find_secy(ctx, &secy_index)) + return -EINVAL; + + // get the peer_index: + if (!find_rxsc(macsec_priv, rx_sc, secy_index, &peer_index)) + return -EINVAL; + + if (ctx->prepare) + return 0; + + // netdev_info(ctx->netdev, "%s\n", __func__); + + // update the Rx-SC registers: + write_rxsc(ctx->netdev, rx_sc, secy_index, peer_index, disable); + if (disable) + macsec_priv->rxsc_ext[secy_index][peer_index].stoppedTime = jiffies; + else if (macsec_priv->sa_enabled[secy_index][peer_index] & 0x0f) + macsec_priv->rxsc_ext[secy_index][peer_index].startedTime = jiffies; + + return 0; +} + +static int cco_macsec_del_rxsc(struct macsec_context *ctx) +{ + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + struct macsec_rx_sc *rx_sc = ctx->rx_sc; + u32 secy_index, peer_index; + + // get the SecY: + if (!find_secy(ctx, &secy_index)) + return -EINVAL; + + // get the peer_index: + if (!find_rxsc(macsec_priv, rx_sc, secy_index, &peer_index)) + return -EINVAL; + + if (ctx->prepare) + return 0; + + // netdev_info(ctx->netdev, "%s\n", __func__); + + clear_rxsc(ctx, macsec_priv, rx_sc, secy_index, peer_index); + return 0; +} + +static int cco_macsec_add_rxsa(struct macsec_context *ctx) +{ + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + struct macsec_rx_sc *rx_sc = ctx->sa.rx_sa->sc; + u8 disable = ctx->sa.rx_sa->active ? 0 : 1, val; + u32 secy_index, peer_index, i; + int key_index = -1; + + // get the SecY: + if (!find_secy(ctx, &secy_index)) + return -EINVAL; + + // get the peer_index: + if (!find_rxsc(macsec_priv, rx_sc, secy_index, &peer_index)) + return -EINVAL; + + // check if ctx->sa.rx_sa->key.id exists in the netdev_priv(ctx->netdev) key_id_table + // and whether it is enabled for Rx (check key_use). + for (i = 0; i < macsec_priv->capabilities.no_of_key_entries_rx && i < CCO_MACSEC_KEYS; ++i) { + if (!macsec_priv->key_use[i]) { + if (key_index < 0) + key_index = i; // first free entry + continue; + } + if (memcmp(ctx->sa.rx_sa->key.id, macsec_priv->key_id_table[i], MACSEC_KEYID_LEN) == 0) { + // found key.id + key_index = i; + break; + } + } + if (key_index < 0) + // not found and no room for a new + return -ENOSPC; + + if (ctx->prepare) + return 0; + + // netdev_info(ctx->netdev, "%s\n", __func__); + + if (!(macsec_priv->key_use[key_index] & CCO_MACSEC_KEY_RX)) { + u8 key_tx = (macsec_priv->key_use[key_index] & CCO_MACSEC_KEY_TX) ? 1 : 0; + macsec_priv->key_use[key_index] |= CCO_MACSEC_KEY_RX; + memcpy(macsec_priv->key_id_table[key_index], ctx->sa.rx_sa->key.id, MACSEC_KEYID_LEN); + // Configure the Rx-SA key: + if (ctx->secy->key_len == (128/8)) { + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_0_BASE_ADDR, + (ctx->sa.key[12] << 24) | (ctx->sa.key[13] << 16) | + (ctx->sa.key[14] << 8) | ctx->sa.key[15]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_1_BASE_ADDR, + (ctx->sa.key[8] << 24) | (ctx->sa.key[9] << 16) | + (ctx->sa.key[10] << 8) | ctx->sa.key[11]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_2_BASE_ADDR, + (ctx->sa.key[4] << 24) | (ctx->sa.key[5] << 16) | + (ctx->sa.key[6] << 8) | ctx->sa.key[7]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_3_BASE_ADDR, + (ctx->sa.key[0] << 24) | (ctx->sa.key[1] << 16) | + (ctx->sa.key[2] << 8) | ctx->sa.key[3]); + if (!ctx->secy->xpn) + val = 0; // 0x0: AES-GCM-128 + else + val = 2; // 0x2: AES-GCM-XPN-128 + } else { + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_0_BASE_ADDR, + (ctx->sa.key[28] << 24) | (ctx->sa.key[29] << 16) | + (ctx->sa.key[30] << 8) | ctx->sa.key[31]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_1_BASE_ADDR, + (ctx->sa.key[24] << 24) | (ctx->sa.key[25] << 16) | + (ctx->sa.key[26] << 8) | ctx->sa.key[27]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_2_BASE_ADDR, + (ctx->sa.key[20] << 24) | (ctx->sa.key[21] << 16) | + (ctx->sa.key[22] << 8) | ctx->sa.key[23]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_3_BASE_ADDR, + (ctx->sa.key[16] << 24) | (ctx->sa.key[17] << 16) | + (ctx->sa.key[18] << 8) | ctx->sa.key[19]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_4_BASE_ADDR, + (ctx->sa.key[12] << 24) | (ctx->sa.key[13] << 16) | + (ctx->sa.key[14] << 8) | ctx->sa.key[15]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_5_BASE_ADDR, + (ctx->sa.key[8] << 24) | (ctx->sa.key[9] << 16) | + (ctx->sa.key[10] << 8) | ctx->sa.key[11]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_6_BASE_ADDR, + (ctx->sa.key[4] << 24) | (ctx->sa.key[5] << 16) | + (ctx->sa.key[6] << 8) | ctx->sa.key[7]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_7_BASE_ADDR, + (ctx->sa.key[0] << 24) | (ctx->sa.key[1] << 16) | + (ctx->sa.key[2] << 8) | ctx->sa.key[3]); + if (!ctx->secy->xpn) + val = 1; // 0x1: AES-GCM-256 + else + val = 3; // 0x3: AES-GCM-XPN-256 + } + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SALT_0_BASE_ADDR, + (ctx->sa.rx_sa->key.salt.bytes[8] << 24) | (ctx->sa.rx_sa->key.salt.bytes[9] << 16) | + (ctx->sa.rx_sa->key.salt.bytes[10] << 8) | ctx->sa.rx_sa->key.salt.bytes[11]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SALT_1_BASE_ADDR, + (ctx->sa.rx_sa->key.salt.bytes[4] << 24) | (ctx->sa.rx_sa->key.salt.bytes[5] << 16) | + (ctx->sa.rx_sa->key.salt.bytes[6] << 8) | ctx->sa.rx_sa->key.salt.bytes[7]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SALT_2_BASE_ADDR, + (ctx->sa.rx_sa->key.salt.bytes[0] << 24) | (ctx->sa.rx_sa->key.salt.bytes[1] << 16) | + (ctx->sa.rx_sa->key.salt.bytes[2] << 8) | ctx->sa.rx_sa->key.salt.bytes[3]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_CTRL_BASE_ADDR, + (key_index << CIPHERSUITE_CS_CTRL_INDEX_SHIFT) | + (key_tx << CIPHERSUITE_CS_CTRL_TRANSMIT_SEL_SHIFT) | + (val << CIPHERSUITE_CS_CTRL_AES_MODE_SEL_SHIFT) | + CIPHERSUITE_CS_CTRL_RECEIVE_SEL_MASK | + CIPHERSUITE_CS_CTRL_WR_TRIGGER_MASK); + } + macsec_priv->key_refcnt[key_index]++; + + // Configure Rx-SA registers: + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_AN_BASE_ADDR, + (ctx->sa.assoc_num << RECEIVESA_RX_SA_AN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_NEXTPN_BASE_ADDR, + (ctx->sa.rx_sa->next_pn << RECEIVESA_RX_SA_NEXTPN_VAL_SHIFT)); + if (!ctx->secy->replay_protect) + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_LOWESTPN_BASE_ADDR, + (ctx->sa.rx_sa->next_pn << RECEIVESA_RX_SA_LOWESTPN_VAL_SHIFT)); + else if (ctx->sa.rx_sa->next_pn >= ctx->secy->replay_window) + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_LOWESTPN_BASE_ADDR, + ((ctx->sa.rx_sa->next_pn - ctx->secy->replay_window) << RECEIVESA_RX_SA_LOWESTPN_VAL_SHIFT)); + else + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_LOWESTPN_BASE_ADDR, + (0 << RECEIVESA_RX_SA_LOWESTPN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_KEY_INDEX_WR_BASE_ADDR, + key_index << RECEIVESA_RX_SA_KEY_INDEX_WR_VAL_SHIFT); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_SSCI_WR_BASE_ADDR, ctx->sa.rx_sa->ssci); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_RECEIVESA_CFG_BASE_ADDR, + disable ? 0 : RECEIVESA_RX_SA_RECEIVESA_CFG_ENABLERECEIVE_WR_MASK); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_CTRL_BASE_ADDR, + (peer_index << RECEIVESA_RX_SA_CTRL_PEER_INDEX_SHIFT) | + (secy_index << RECEIVESA_RX_SA_CTRL_SECY_INDEX_SHIFT) | + RECEIVESA_RX_SA_CTRL_WR_TRIGGER_MASK); + + macsec_priv->rxsa_ext[secy_index][peer_index][ctx->sa.assoc_num].createdTime = jiffies; + macsec_priv->rxsa_ext[secy_index][peer_index][ctx->sa.assoc_num].startedTime = + macsec_priv->rxsa_ext[secy_index][peer_index][ctx->sa.assoc_num].createdTime; + macsec_priv->rxsa_ext[secy_index][peer_index][ctx->sa.assoc_num].stoppedTime = + macsec_priv->rxsa_ext[secy_index][peer_index][ctx->sa.assoc_num].createdTime; + if (disable) + macsec_priv->sa_enabled[secy_index][peer_index] &= ~(1 << ctx->sa.assoc_num); + else { + if ((macsec_priv->sa_enabled[secy_index][peer_index] & 0x0f) == 0) + // first Rx-SA to become active, so Rx-SC is now active: + macsec_priv->rxsc_ext[secy_index][peer_index].startedTime = jiffies; + macsec_priv->sa_enabled[secy_index][peer_index] |= (1 << ctx->sa.assoc_num); + } + + return 0; +} + +static int cco_macsec_upd_rxsa(struct macsec_context *ctx) +{ + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + struct macsec_rx_sc *rx_sc = ctx->sa.rx_sa->sc; + u8 disable = ctx->sa.rx_sa->active ? 0 : 1; + u8 isEnabled; + u32 secy_index, peer_index, i; + int key_index = -1; + + // get the SecY: + if (!find_secy(ctx, &secy_index)) + return -EINVAL; + + // get the peer_index: + if (!find_rxsc(macsec_priv, rx_sc, secy_index, &peer_index)) + return -EINVAL; + + // check if ctx->sa.rx_sa->key.id exists in the netdev_priv(ctx->netdev) key_id_table + // and whether it is enabled for Rx (check key_use). + for (i = 0; i < macsec_priv->capabilities.no_of_key_entries_rx && i < CCO_MACSEC_KEYS; ++i) { + if (!(macsec_priv->key_use[i] & CCO_MACSEC_KEY_RX)) + continue; + if (memcmp(ctx->sa.rx_sa->key.id, macsec_priv->key_id_table[i], MACSEC_KEYID_LEN) == 0) { + // found key.id + key_index = i; + break; + } + } + if (key_index < 0) + return -EINVAL; + + if (ctx->prepare) + return 0; + + // netdev_info(ctx->netdev, "%s\n", __func__); + + isEnabled = (macsec_priv->sa_enabled[secy_index][peer_index] >> ctx->sa.assoc_num) & 1; + + if (isEnabled) { + // currently enabled + // Configure Rx-SA registers (common part): + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_AN_BASE_ADDR, + (ctx->sa.assoc_num << RECEIVESA_RX_SA_AN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_KEY_INDEX_WR_BASE_ADDR, + key_index << RECEIVESA_RX_SA_KEY_INDEX_WR_VAL_SHIFT); + if (disable) { + // Configure Rx-SA registers (disable): + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_RECEIVESA_CFG_BASE_ADDR, 0); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_CTRL_BASE_ADDR, + (peer_index << RECEIVESA_RX_SA_CTRL_PEER_INDEX_SHIFT) | + (secy_index << RECEIVESA_RX_SA_CTRL_SECY_INDEX_SHIFT) | + RECEIVESA_RX_SA_CTRL_WR_TRIGGER_MASK); + macsec_priv->sa_enabled[secy_index][peer_index] &= ~(1 << ctx->sa.assoc_num); + macsec_priv->rxsa_ext[secy_index][peer_index][ctx->sa.assoc_num].stoppedTime = jiffies; + if ((macsec_priv->sa_enabled[secy_index][peer_index] & 0x0f) == 0) + // no Rx-SA active now, so Rx-SC is no longer active: + macsec_priv->rxsc_ext[secy_index][peer_index].stoppedTime = jiffies; + } else { + // Configure Rx-SA registers (update PN): + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_UPDTNEXTPN_BASE_ADDR, + (ctx->sa.rx_sa->next_pn << RECEIVESA_RX_SA_UPDTNEXTPN_VAL_SHIFT)); + if (ctx->sa.rx_sa->next_pn >= ctx->secy->replay_window) + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_UPDTLOWESTPN_BASE_ADDR, + ((ctx->sa.rx_sa->next_pn - ctx->secy->replay_window) << RECEIVESA_RX_SA_UPDTLOWESTPN_VAL_SHIFT)); + else + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_UPDTLOWESTPN_BASE_ADDR, + (0 << RECEIVESA_RX_SA_UPDTLOWESTPN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_CTRL_BASE_ADDR, + (peer_index << RECEIVESA_RX_SA_CTRL_PEER_INDEX_SHIFT) | + (secy_index << RECEIVESA_RX_SA_CTRL_SECY_INDEX_SHIFT) | + RECEIVESA_RX_SA_CTRL_UPDATE_PN_TRIGGER_MASK); + } + } else { + // currently disabled + if (!disable) { + // Configure Rx-SA registers (common part): + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_AN_BASE_ADDR, + (ctx->sa.assoc_num << RECEIVESA_RX_SA_AN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_KEY_INDEX_WR_BASE_ADDR, + key_index << RECEIVESA_RX_SA_KEY_INDEX_WR_VAL_SHIFT); + // Configure Rx-SA registers (enable): + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_NEXTPN_BASE_ADDR, + (ctx->sa.rx_sa->next_pn << RECEIVESA_RX_SA_NEXTPN_VAL_SHIFT)); + if (!ctx->secy->replay_protect) + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_LOWESTPN_BASE_ADDR, + (ctx->sa.rx_sa->next_pn << RECEIVESA_RX_SA_LOWESTPN_VAL_SHIFT)); + else if (ctx->sa.rx_sa->next_pn >= ctx->secy->replay_window) + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_LOWESTPN_BASE_ADDR, + ((ctx->sa.rx_sa->next_pn - ctx->secy->replay_window) << RECEIVESA_RX_SA_LOWESTPN_VAL_SHIFT)); + else + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_LOWESTPN_BASE_ADDR, + (0 << RECEIVESA_RX_SA_LOWESTPN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_SSCI_WR_BASE_ADDR, ctx->sa.rx_sa->ssci); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_RECEIVESA_CFG_BASE_ADDR, + disable ? 0 : RECEIVESA_RX_SA_RECEIVESA_CFG_ENABLERECEIVE_WR_MASK); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_CTRL_BASE_ADDR, + (peer_index << RECEIVESA_RX_SA_CTRL_PEER_INDEX_SHIFT) | + (secy_index << RECEIVESA_RX_SA_CTRL_SECY_INDEX_SHIFT) | + RECEIVESA_RX_SA_CTRL_WR_TRIGGER_MASK); + if ((macsec_priv->sa_enabled[secy_index][peer_index] & 0x0f) == 0) + // first Rx-SA to become active, so Rx-SC is now active: + macsec_priv->rxsc_ext[secy_index][peer_index].startedTime = jiffies; + macsec_priv->sa_enabled[secy_index][peer_index] |= (1 << ctx->sa.assoc_num); + macsec_priv->rxsa_ext[secy_index][peer_index][ctx->sa.assoc_num].startedTime = jiffies; + } + } + + return 0; +} + +static int cco_macsec_del_rxsa(struct macsec_context *ctx) +{ + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + struct macsec_rx_sc *rx_sc = ctx->sa.rx_sa->sc; + u32 secy_index, peer_index, i; + int key_index = -1; + u8 enable_mask; + + // get the SecY: + if (!find_secy(ctx, &secy_index)) + return -EINVAL; + + // get the peer_index: + if (!find_rxsc(macsec_priv, rx_sc, secy_index, &peer_index)) + return -EINVAL; + + // check if ctx->sa.rx_sa->key.id exists in the netdev_priv(ctx->netdev) key_id_table + // and whether it is enabled for Rx (check key_use). + for (i = 0; i < macsec_priv->capabilities.no_of_key_entries_rx && i < CCO_MACSEC_KEYS; ++i) { + if (!(macsec_priv->key_use[i] & CCO_MACSEC_KEY_RX)) + continue; + if (memcmp(ctx->sa.rx_sa->key.id, macsec_priv->key_id_table[i], MACSEC_KEYID_LEN) == 0) { + // found key.id + key_index = i; + break; + } + } + if (key_index < 0) + return -EINVAL; + + if (ctx->prepare) + return 0; + + // netdev_info(ctx->netdev, "%s\n", __func__); + + enable_mask = macsec_priv->sa_enabled[secy_index][peer_index] & 0x0f; + clear_rxsa(ctx, macsec_priv, secy_index, peer_index, ctx->sa.assoc_num, key_index); + if (enable_mask && (macsec_priv->sa_enabled[secy_index][peer_index] & 0x0f) == 0) + // no Rx-SA active now, so Rx-SC is no longer active: + macsec_priv->rxsc_ext[secy_index][peer_index].stoppedTime = jiffies; + return 0; +} + +static int cco_macsec_add_txsa(struct macsec_context *ctx) +{ + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + u8 disable = ctx->sa.tx_sa->active ? 0 : 1, val; + u8 vlan_in_clear, conf_offs; + u32 secy_index, i; + int key_index = -1; + + // get the SecY: + if (!find_secy(ctx, &secy_index)) + return -EINVAL; + + // check if ctx->sa.tx_sa->key.id exists in the netdev_priv(ctx->netdev) key_id_table + // and whether it is enabled for Tx (check key_use). + for (i = 0; i < macsec_priv->capabilities.no_of_key_entries_tx && i < CCO_MACSEC_KEYS; ++i) { + if (!macsec_priv->key_use[i]) { + if (key_index < 0) + key_index = i; // first free entry + continue; + } + if (memcmp(ctx->sa.tx_sa->key.id, macsec_priv->key_id_table[i], MACSEC_KEYID_LEN) == 0) { + // found key.id + key_index = i; + break; + } + } + if (key_index < 0) + // not found and no room for a new + return -ENOSPC; + + if (ctx->prepare) + return 0; + + // netdev_info(ctx->netdev, "%s\n", __func__); + + if (!(macsec_priv->key_use[key_index] & CCO_MACSEC_KEY_TX)) { + u8 key_rx = (macsec_priv->key_use[key_index] & CCO_MACSEC_KEY_RX) ? 1 : 0; + macsec_priv->key_use[key_index] |= CCO_MACSEC_KEY_TX; + memcpy(macsec_priv->key_id_table[key_index], ctx->sa.tx_sa->key.id, MACSEC_KEYID_LEN); + // Configure the Tx-SA key: + if (ctx->secy->key_len == (128/8)) { + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_0_BASE_ADDR, + (ctx->sa.key[12] << 24) | (ctx->sa.key[13] << 16) | + (ctx->sa.key[14] << 8) | ctx->sa.key[15]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_1_BASE_ADDR, + (ctx->sa.key[8] << 24) | (ctx->sa.key[9] << 16) | + (ctx->sa.key[10] << 8) | ctx->sa.key[11]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_2_BASE_ADDR, + (ctx->sa.key[4] << 24) | (ctx->sa.key[5] << 16) | + (ctx->sa.key[6] << 8) | ctx->sa.key[7]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_3_BASE_ADDR, + (ctx->sa.key[0] << 24) | (ctx->sa.key[1] << 16) | + (ctx->sa.key[2] << 8) | ctx->sa.key[3]); + if (!ctx->secy->xpn) + val = 0; // 0x0: AES-GCM-128 + else + val = 2; // 0x2: AES-GCM-XPN-128 + } else { + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_0_BASE_ADDR, + (ctx->sa.key[28] << 24) | (ctx->sa.key[29] << 16) | + (ctx->sa.key[30] << 8) | ctx->sa.key[31]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_1_BASE_ADDR, + (ctx->sa.key[24] << 24) | (ctx->sa.key[25] << 16) | + (ctx->sa.key[26] << 8) | ctx->sa.key[27]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_2_BASE_ADDR, + (ctx->sa.key[20] << 24) | (ctx->sa.key[21] << 16) | + (ctx->sa.key[22] << 8) | ctx->sa.key[23]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_3_BASE_ADDR, + (ctx->sa.key[16] << 24) | (ctx->sa.key[17] << 16) | + (ctx->sa.key[18] << 8) | ctx->sa.key[19]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_4_BASE_ADDR, + (ctx->sa.key[12] << 24) | (ctx->sa.key[13] << 16) | + (ctx->sa.key[14] << 8) | ctx->sa.key[15]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_5_BASE_ADDR, + (ctx->sa.key[8] << 24) | (ctx->sa.key[9] << 16) | + (ctx->sa.key[10] << 8) | ctx->sa.key[11]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_6_BASE_ADDR, + (ctx->sa.key[4] << 24) | (ctx->sa.key[5] << 16) | + (ctx->sa.key[6] << 8) | ctx->sa.key[7]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SAK_7_BASE_ADDR, + (ctx->sa.key[0] << 24) | (ctx->sa.key[1] << 16) | + (ctx->sa.key[2] << 8) | ctx->sa.key[3]); + if (!ctx->secy->xpn) + val = 1; // 0x1: AES-GCM-256 + else + val = 3; // 0x3: AES-GCM-XPN-256 + } + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SALT_0_BASE_ADDR, + (ctx->sa.tx_sa->key.salt.bytes[8] << 24) | (ctx->sa.tx_sa->key.salt.bytes[9] << 16) | + (ctx->sa.tx_sa->key.salt.bytes[10] << 8) | ctx->sa.tx_sa->key.salt.bytes[11]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SALT_1_BASE_ADDR, + (ctx->sa.tx_sa->key.salt.bytes[4] << 24) | (ctx->sa.tx_sa->key.salt.bytes[5] << 16) | + (ctx->sa.tx_sa->key.salt.bytes[6] << 8) | ctx->sa.tx_sa->key.salt.bytes[7]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_SALT_2_BASE_ADDR, + (ctx->sa.tx_sa->key.salt.bytes[0] << 24) | (ctx->sa.tx_sa->key.salt.bytes[1] << 16) | + (ctx->sa.tx_sa->key.salt.bytes[2] << 8) | ctx->sa.tx_sa->key.salt.bytes[3]); + cco_macsec_reg_wr(ctx->netdev, CIPHERSUITE_BASE_ADDR + CIPHERSUITE_CS_CTRL_BASE_ADDR, + (key_index << CIPHERSUITE_CS_CTRL_INDEX_SHIFT) | + (key_rx << CIPHERSUITE_CS_CTRL_RECEIVE_SEL_SHIFT) | + (val << CIPHERSUITE_CS_CTRL_AES_MODE_SEL_SHIFT) | + CIPHERSUITE_CS_CTRL_TRANSMIT_SEL_MASK | + CIPHERSUITE_CS_CTRL_WR_TRIGGER_MASK); + } + macsec_priv->key_refcnt[key_index]++; + + // Configure Tx-SA registers: + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_AN_BASE_ADDR, + (ctx->sa.assoc_num << TRANSMITSA_TX_SA_AN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_NEXT_PN_BASE_ADDR, + (ctx->sa.tx_sa->next_pn << TRANSMITSA_TX_SA_NEXT_PN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_KEY_INDEX_WR_BASE_ADDR, + key_index << TRANSMITSA_TX_SA_KEY_INDEX_WR_VAL_SHIFT); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_SSCI_WR_BASE_ADDR, ctx->sa.tx_sa->ssci); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_TRANSMITSA_CFG_BASE_ADDR, + (ctx->secy->tx_sc.encrypt ? TRANSMITSA_TX_SA_TRANSMITSA_CFG_CONFIDENTIALITY_WR_MASK : 0)); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_CTRL_BASE_ADDR, + (secy_index << TRANSMITSA_TX_SA_CTRL_SECY_INDEX_SHIFT) | + TRANSMITSA_TX_SA_CTRL_WR_TRIGGER_MASK); + + macsec_priv->txsa_ext[secy_index][ctx->sa.assoc_num].createdTime = jiffies; + macsec_priv->txsa_ext[secy_index][ctx->sa.assoc_num].startedTime = + macsec_priv->txsa_ext[secy_index][ctx->sa.assoc_num].createdTime; + macsec_priv->txsa_ext[secy_index][ctx->sa.assoc_num].stoppedTime = + macsec_priv->txsa_ext[secy_index][ctx->sa.assoc_num].createdTime; + if (disable) + macsec_priv->sa_enabled[secy_index][0] &= ~(0x10 << ctx->sa.assoc_num); + else { + macsec_priv->sa_enabled[secy_index][0] |= (0x10 << ctx->sa.assoc_num); + if (ctx->secy->tx_sc.encoding_sa == ctx->sa.assoc_num) { + // Tx-SA used by Tx-SC becomes active, so Tx-SC is now active: + macsec_priv->txsc_ext[secy_index].startedTime = jiffies; + vlan_in_clear = macsec_priv->secy_vlan_in_clear[secy_index]; + conf_offs = macsec_priv->secy_confidentiality_offs[secy_index]; + write_secy_txsc(ctx->netdev, ctx->secy, secy_index, 0, macsec_priv->sa_enabled[secy_index][0], vlan_in_clear, conf_offs); + } + } + + return 0; +} + +static int cco_macsec_upd_txsa(struct macsec_context *ctx) +{ + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + u8 disable = ctx->sa.tx_sa->active ? 0 : 1; + u8 isEnabled, vlan_in_clear, conf_offs; + u32 secy_index, i; + int key_index = -1; + + // get the SecY: + if (!find_secy(ctx, &secy_index)) + return -EINVAL; + + // check if ctx->sa.tx_sa->key.id exists in the netdev_priv(ctx->netdev) key_id_table + // and whether it is enabled for Tx (check key_use). + for (i = 0; i < macsec_priv->capabilities.no_of_key_entries_tx && i < CCO_MACSEC_KEYS; ++i) { + if (!(macsec_priv->key_use[i] & CCO_MACSEC_KEY_TX)) + continue; + if (memcmp(ctx->sa.tx_sa->key.id, macsec_priv->key_id_table[i], MACSEC_KEYID_LEN) == 0) { + // found key.id + key_index = i; + break; + } + } + if (key_index < 0) + return -EINVAL; + + if (ctx->prepare) + return 0; + + // netdev_info(ctx->netdev, "%s\n", __func__); + + isEnabled = (macsec_priv->sa_enabled[secy_index][0] >> (ctx->sa.assoc_num + 4)) & 1; + + if (isEnabled) { + // currently enabled + if (disable) { + // Configure Tx-SA registers (disable): + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_AN_BASE_ADDR, + (ctx->sa.assoc_num << TRANSMITSA_TX_SA_AN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_KEY_INDEX_WR_BASE_ADDR, + key_index << TRANSMITSA_TX_SA_KEY_INDEX_WR_VAL_SHIFT); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_TRANSMITSA_CFG_BASE_ADDR, 0); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_CTRL_BASE_ADDR, + (secy_index << TRANSMITSA_TX_SA_CTRL_SECY_INDEX_SHIFT) | + TRANSMITSA_TX_SA_CTRL_WR_TRIGGER_MASK); + macsec_priv->sa_enabled[secy_index][0] &= ~(0x10 << ctx->sa.assoc_num); + macsec_priv->txsa_ext[secy_index][ctx->sa.assoc_num].stoppedTime = jiffies; + if (ctx->secy->tx_sc.encoding_sa == ctx->sa.assoc_num) { + // Tx-SA used by Tx-SC becomes inactive, so Tx-SC is no longer active: + macsec_priv->txsc_ext[secy_index].stoppedTime = jiffies; + vlan_in_clear = macsec_priv->secy_vlan_in_clear[secy_index]; + conf_offs = macsec_priv->secy_confidentiality_offs[secy_index]; + write_secy_txsc(ctx->netdev, ctx->secy, secy_index, 0, 0, vlan_in_clear, conf_offs); + } + } else { + // Configure Tx-SA registers (e.g. update PN): + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_AN_BASE_ADDR, + (ctx->sa.assoc_num << TRANSMITSA_TX_SA_AN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_KEY_INDEX_WR_BASE_ADDR, + key_index << TRANSMITSA_TX_SA_KEY_INDEX_WR_VAL_SHIFT); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_NEXT_PN_BASE_ADDR, + (ctx->sa.tx_sa->next_pn << TRANSMITSA_TX_SA_NEXT_PN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_SSCI_WR_BASE_ADDR, ctx->sa.tx_sa->ssci); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_TRANSMITSA_CFG_BASE_ADDR, + disable ? 0 : (ctx->secy->tx_sc.encrypt ? TRANSMITSA_TX_SA_TRANSMITSA_CFG_CONFIDENTIALITY_WR_MASK : 0)); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_CTRL_BASE_ADDR, + (secy_index << TRANSMITSA_TX_SA_CTRL_SECY_INDEX_SHIFT) | + TRANSMITSA_TX_SA_CTRL_WR_TRIGGER_MASK); + } + } else { + // currently disabled + if (!disable) { + // Configure Tx-SA registers (enable): + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_AN_BASE_ADDR, + (ctx->sa.assoc_num << TRANSMITSA_TX_SA_AN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_KEY_INDEX_WR_BASE_ADDR, + key_index << TRANSMITSA_TX_SA_KEY_INDEX_WR_VAL_SHIFT); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_NEXT_PN_BASE_ADDR, + (ctx->sa.tx_sa->next_pn << TRANSMITSA_TX_SA_NEXT_PN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_SSCI_WR_BASE_ADDR, ctx->sa.tx_sa->ssci); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_TRANSMITSA_CFG_BASE_ADDR, + disable ? 0 : (ctx->secy->tx_sc.encrypt ? TRANSMITSA_TX_SA_TRANSMITSA_CFG_CONFIDENTIALITY_WR_MASK : 0)); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_CTRL_BASE_ADDR, + (secy_index << TRANSMITSA_TX_SA_CTRL_SECY_INDEX_SHIFT) | + TRANSMITSA_TX_SA_CTRL_WR_TRIGGER_MASK); + macsec_priv->sa_enabled[secy_index][0] |= (0x10 << ctx->sa.assoc_num); + macsec_priv->txsa_ext[secy_index][ctx->sa.assoc_num].startedTime = jiffies; + if (ctx->secy->tx_sc.encoding_sa == ctx->sa.assoc_num) { + // Tx-SA used by Tx-SC becomes active, so Tx-SC is now active: + macsec_priv->txsc_ext[secy_index].startedTime = jiffies; + vlan_in_clear = macsec_priv->secy_vlan_in_clear[secy_index]; + conf_offs = macsec_priv->secy_confidentiality_offs[secy_index]; + write_secy_txsc(ctx->netdev, ctx->secy, secy_index, 0, macsec_priv->sa_enabled[secy_index][0], vlan_in_clear, conf_offs); + } + } + } + + return 0; +} + +static int cco_macsec_del_txsa(struct macsec_context *ctx) +{ + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + u32 secy_index, i; + int key_index = -1; + u8 enable_mask, vlan_in_clear, conf_offs; + + // get the SecY: + if (!find_secy(ctx, &secy_index)) + return -EINVAL; + + // check if ctx->sa.tx_sa->key.id exists in the netdev_priv(ctx->netdev) key_id_table + // and whether it is enabled for Tx (check key_use). + for (i = 0; i < macsec_priv->capabilities.no_of_key_entries_tx && i < CCO_MACSEC_KEYS; ++i) { + if (!(macsec_priv->key_use[i] & CCO_MACSEC_KEY_TX)) + continue; + if (memcmp(ctx->sa.tx_sa->key.id, macsec_priv->key_id_table[i], MACSEC_KEYID_LEN) == 0) { + // found key.id + key_index = i; + break; + } + } + if (key_index < 0) + return -EINVAL; + + if (ctx->prepare) + return 0; + + // netdev_info(ctx->netdev, "%s\n", __func__); + + enable_mask = macsec_priv->sa_enabled[secy_index][0] & 0xf0; + clear_txsa(ctx, macsec_priv, secy_index, ctx->sa.assoc_num, key_index); + if (ctx->secy->tx_sc.encoding_sa == ctx->sa.assoc_num) { + // Tx-SA used by Tx-SC becomes inactive, so Tx-SC is no longer active: + macsec_priv->txsc_ext[secy_index].stoppedTime = jiffies; + vlan_in_clear = macsec_priv->secy_vlan_in_clear[secy_index]; + conf_offs = macsec_priv->secy_confidentiality_offs[secy_index]; + write_secy_txsc(ctx->netdev, ctx->secy, secy_index, 0, 0, vlan_in_clear, conf_offs); + } + return 0; +} + +static int cco_macsec_get_dev_stats(struct macsec_context *ctx) +{ + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + u32 secy_index; + + // get the SecY: + if (!find_secy(ctx, &secy_index)) + return -EINVAL; + + if (ctx->prepare) + return 0; + + // trigger a read of per SecY stats: + cco_macsec_reg_wr(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_STATS_CTRL_BASE_ADDR, + STATISTICS_STATS_CTRL_PORT_STATS_RD_TRIGGER_MASK | + (secy_index << STATISTICS_STATS_CTRL_SECY_INDEX_SHIFT)); + + // read counters: + macsec_priv->dev_stats[secy_index].OutPktsUntagged += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_OUTPKTSUNTAGGED_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsUntagged += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSUNTAGGED_BASE_ADDR); + macsec_priv->dev_stats[secy_index].OutPktsTooLong += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_OUTPKTSTOOLONG_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsNoTag += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSNOTAG_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsBadTag += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSBADTAG_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsNoSCI += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSNOSA_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsUnknownSCI += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSNOSAERROR_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsOverrun += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSOVERRUN_BASE_ADDR); + *ctx->stats.dev_stats = macsec_priv->dev_stats[secy_index]; + + // store other port stats: + macsec_priv->port_stats[secy_index].ifInOctets += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINOCTETS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInUcPkts += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINUCASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInMcPkts += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINMULTICASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInBcPkts += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINBROADCASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInDiscards += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINDISCARDS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInErrors += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINERRORS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutOctets += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTOCTETS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutUcPkts += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTUCASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutMcPkts += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTMULTICASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutBcPkts += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTBROADCASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutErrors += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTERRORS_BASE_ADDR); + + // store other uport stats: + macsec_priv->uport_stats[secy_index].ifInOctets += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINOCTETS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInUcPkts += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINUCASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInMcPkts += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINMULTICASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInBcPkts += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINBROADCASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInDiscards += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINDISCARDS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInErrors += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINERRORS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutOctets += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTOCTETS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutUcPkts += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTUCASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutMcPkts += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTMULTICASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutBcPkts += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTBROADCASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutErrors += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTERRORS_BASE_ADDR); + + // store other ext port stats: + macsec_priv->ext_port_stats[secy_index].compTxDisable += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_COMP_TX_DISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].compRxDisable += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_COMP_RX_DISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].txSecYDisable += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_SECYDISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].rxSecYDisable += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_SECYDISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].txReceivingDisable += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_RECEIVINGDISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].rxTransmittingDisable += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_TRANSMITTINGDISABLE_BASE_ADDR); + + return 0; +} + +static int cco_macsec_get_tx_sc_stats(struct macsec_context *ctx) +{ + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + u32 secy_index; + + // get the SecY: + if (!find_secy(ctx, &secy_index)) + return -EINVAL; + + if (ctx->prepare) + return 0; + + // trigger a read of Tx-SC stats: + cco_macsec_reg_wr(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_STATS_CTRL_BASE_ADDR, + STATISTICS_STATS_CTRL_TX_STATS_RD_TRIGGER_MASK | + (secy_index << STATISTICS_STATS_CTRL_SECY_INDEX_SHIFT)); + + // read counters: + macsec_priv->txsc_stats[secy_index].OutPktsProtected += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_OUTPKTSPROTECTED_BASE_ADDR); + macsec_priv->txsc_stats[secy_index].OutPktsEncrypted += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_OUTPKTSENCRYPTED_BASE_ADDR); + macsec_priv->txsc_stats[secy_index].OutOctetsProtected += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_OUTOCTETSPROTECTED_BASE_ADDR); + macsec_priv->txsc_stats[secy_index].OutOctetsEncrypted += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_OUTOCTETSENCRYPTED_BASE_ADDR); + *ctx->stats.tx_sc_stats = macsec_priv->txsc_stats[secy_index]; + + return 0; +} + +static int cco_macsec_get_tx_sa_stats(struct macsec_context *ctx) +{ + struct macsec_tx_sa *tx_sa; + u32 secy_index, next_pn; + + // get the SecY: + if (!find_secy(ctx, &secy_index)) + return -EINVAL; + + if (ctx->prepare) + return 0; + + // no per-SA stats: + memset(ctx->stats.tx_sa_stats, 0, sizeof(*ctx->stats.tx_sa_stats)); + + // but do update tx_sa->next_pn: + tx_sa = rcu_dereference_bh(ctx->secy->tx_sc.sa[ctx->sa.assoc_num]); + cco_macsec_reg_wr(ctx->netdev, STATUS_BASE_ADDR + STATUS_ST_CTRL_BASE_ADDR, + STATUS_ST_CTRL_RD_TRIGGER_MASK | + (ctx->sa.assoc_num << STATUS_ST_CTRL_SA_INDEX_SHIFT) | + (secy_index << STATUS_ST_CTRL_SECY_INDEX_SHIFT)); + next_pn = cco_macsec_reg_rd(ctx->netdev, STATUS_BASE_ADDR + STATUS_ST_TX_SA_NEXT_PN_BASE_ADDR); + spin_lock_bh(&tx_sa->lock); + tx_sa->next_pn = next_pn; + spin_unlock_bh(&tx_sa->lock); + + return 0; +} + +static int cco_macsec_get_rx_sc_stats(struct macsec_context *ctx) +{ + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + struct macsec_rx_sc *rx_sc = ctx->rx_sc; + u32 secy_index, peer_index; + + // get the SecY: + if (!find_secy(ctx, &secy_index)) + return -EINVAL; + + // get the peer_index: + if (!find_rxsc(macsec_priv, rx_sc, secy_index, &peer_index)) + return -EINVAL; + + if (ctx->prepare) + return 0; + + // trigger a read of Rx-SC stats: + cco_macsec_reg_wr(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_STATS_CTRL_BASE_ADDR, + STATISTICS_STATS_CTRL_RX_STATS_RD_TRIGGER_MASK | + (peer_index << STATISTICS_STATS_CTRL_PEER_INDEX_SHIFT) | + (secy_index << STATISTICS_STATS_CTRL_SECY_INDEX_SHIFT)); + // read counters: + macsec_priv->rxsc_stats[secy_index][peer_index].InOctetsValidated += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INOCTETSVALIDATED_BASE_ADDR); + macsec_priv->rxsc_stats[secy_index][peer_index].InOctetsDecrypted += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INOCTETSDECRYPTED_BASE_ADDR); + macsec_priv->rxsc_stats[secy_index][peer_index].InPktsUnchecked += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSUNCHECKED_BASE_ADDR); + macsec_priv->rxsc_stats[secy_index][peer_index].InPktsDelayed += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSDELAYED_BASE_ADDR); + macsec_priv->rxsc_stats[secy_index][peer_index].InPktsOK += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSOK_BASE_ADDR); + macsec_priv->rxsc_stats[secy_index][peer_index].InPktsInvalid += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSINVALID_BASE_ADDR); + macsec_priv->rxsc_stats[secy_index][peer_index].InPktsLate += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSLATE_BASE_ADDR); + macsec_priv->rxsc_stats[secy_index][peer_index].InPktsNotValid += cco_macsec_reg_rd(ctx->netdev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSNOTVALID_BASE_ADDR); + // macsec_priv->rxsc_stats[secy_index][peer_index].InPktsNotUsingSA = 0; // deprecated in IEEE 802.1AE-2018 + // macsec_priv->rxsc_stats[secy_index][peer_index].InPktsUnusedSA = 0; // deprecated in IEEE 802.1AE-2018 + *ctx->stats.rx_sc_stats = macsec_priv->rxsc_stats[secy_index][peer_index]; + + return 0; +} + +static int cco_macsec_get_rx_sa_stats(struct macsec_context *ctx) +{ + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + struct macsec_rx_sa *rx_sa; + struct macsec_rx_sc *rx_sc = ctx->rx_sc; + u32 secy_index, peer_index, next_pn; + + // get the SecY: + if (!find_secy(ctx, &secy_index)) + return -EINVAL; + + // get the peer_index: + if (!find_rxsc(macsec_priv, rx_sc, secy_index, &peer_index)) + return -EINVAL; + + if (ctx->prepare) + return 0; + + // no per-SA stats: + memset(ctx->stats.rx_sa_stats, 0, sizeof(*ctx->stats.rx_sa_stats)); + + // but do update rx_sa->next_pn: + rx_sa = rcu_dereference_bh(rx_sc->sa[ctx->sa.assoc_num]); + cco_macsec_reg_wr(ctx->netdev, STATUS_BASE_ADDR + STATUS_ST_CTRL_BASE_ADDR, + STATUS_ST_CTRL_RD_TRIGGER_MASK | + (ctx->sa.assoc_num << STATUS_ST_CTRL_SA_INDEX_SHIFT) | + (peer_index << STATUS_ST_CTRL_PEER_INDEX_SHIFT) | + (secy_index << STATUS_ST_CTRL_SECY_INDEX_SHIFT)); + next_pn = cco_macsec_reg_rd(ctx->netdev, STATUS_BASE_ADDR + STATUS_ST_RX_SA_NEXTPN_BASE_ADDR); + spin_lock_bh(&rx_sa->lock); + rx_sa->next_pn = next_pn; + spin_unlock_bh(&rx_sa->lock); + + return 0; +} + +static int cco_macsec_dev_open(struct macsec_context *ctx) +{ + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + struct macsec_secy *secy = ctx->secy; + struct macsec_rx_sc *rx_sc; + struct macsec_rx_sa *rx_sa; + struct macsec_tx_sa *tx_sa; + u32 secy_index, peer_index, i; + int key_index; + u8 vlan_in_clear, conf_offs, disable, sa_ix; + + if (!get_secy(ctx, &secy_index)) + return -EINVAL; + + if (ctx->prepare) + return 0; + + if (macsec_priv->secy_stopped[secy_index]) { + // 1. Update the SecY and Tx SC configuration: + vlan_in_clear = macsec_priv->secy_vlan_in_clear[secy_index]; + conf_offs = macsec_priv->secy_confidentiality_offs[secy_index]; + write_secy_txsc(ctx->netdev, secy, secy_index, 0, macsec_priv->sa_enabled[secy_index][0], vlan_in_clear, conf_offs); + + // 2. Add/update each of the Rx SCs found in the macsec_priv->rxsc_array[secy_index]. + // For each Rx SC, add/update any Rx SAs found in the ‘sa’ array for the Rx SC. + for (peer_index = 0; peer_index < CCO_MACSEC_RXSC_MAX; ++peer_index) { + rx_sc = macsec_priv->rxsc_array[secy_index][peer_index]; + if (!rx_sc) + continue; + disable = rx_sc->active ? 0 : 1; + write_rxsc(ctx->netdev, rx_sc, secy_index, peer_index, disable); + for (sa_ix = 0; sa_ix < MACSEC_NUM_AN; ++sa_ix) { + rx_sa = rtnl_dereference(rx_sc->sa[sa_ix]); + if (!rx_sa) + continue; + // find key_index: + for (i = 0, key_index = -1; i < macsec_priv->capabilities.no_of_key_entries_rx && i < CCO_MACSEC_KEYS; ++i) { + if (!(macsec_priv->key_use[i] & CCO_MACSEC_KEY_RX)) + continue; + if (memcmp(rx_sa->key.id, macsec_priv->key_id_table[i], MACSEC_KEYID_LEN) == 0) { + // found key.id + key_index = i; + break; + } + } + if (key_index < 0) { + // this should not happen + netdev_warn(ctx->netdev, "%s secy_index=%u peer_index=%u Rx-SA #%u key not found, should not happen\n", __func__, secy_index, peer_index, sa_ix); + continue; + } + disable = rx_sa->active ? 0 : 1; + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_AN_BASE_ADDR, + (sa_ix << RECEIVESA_RX_SA_AN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_NEXTPN_BASE_ADDR, + (rx_sa->next_pn << RECEIVESA_RX_SA_NEXTPN_VAL_SHIFT)); + if (!ctx->secy->replay_protect) + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_LOWESTPN_BASE_ADDR, + (rx_sa->next_pn << RECEIVESA_RX_SA_LOWESTPN_VAL_SHIFT)); + else if (rx_sa->next_pn >= secy->replay_window) + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_LOWESTPN_BASE_ADDR, + ((rx_sa->next_pn - secy->replay_window) << RECEIVESA_RX_SA_LOWESTPN_VAL_SHIFT)); + else + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_LOWESTPN_BASE_ADDR, + (0 << RECEIVESA_RX_SA_LOWESTPN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_KEY_INDEX_WR_BASE_ADDR, + key_index << RECEIVESA_RX_SA_KEY_INDEX_WR_VAL_SHIFT); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_SSCI_WR_BASE_ADDR, rx_sa->ssci); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_RECEIVESA_CFG_BASE_ADDR, + disable ? 0 : RECEIVESA_RX_SA_RECEIVESA_CFG_ENABLERECEIVE_WR_MASK); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_CTRL_BASE_ADDR, + (peer_index << RECEIVESA_RX_SA_CTRL_PEER_INDEX_SHIFT) | + (secy_index << RECEIVESA_RX_SA_CTRL_SECY_INDEX_SHIFT) | + RECEIVESA_RX_SA_CTRL_WR_TRIGGER_MASK); + } + } + + // 3. Add/update any Tx SAs found in the ctx->secy->tx_sc Tx SC structure. + for (sa_ix = 0; sa_ix < MACSEC_NUM_AN; ++sa_ix) { + tx_sa = rtnl_dereference(secy->tx_sc.sa[sa_ix]); + if (!tx_sa) + continue; + // find key_index: + for (i = 0, key_index = -1; i < macsec_priv->capabilities.no_of_key_entries_tx && i < CCO_MACSEC_KEYS; ++i) { + if (!(macsec_priv->key_use[i] & CCO_MACSEC_KEY_TX)) + continue; + if (memcmp(tx_sa->key.id, macsec_priv->key_id_table[i], MACSEC_KEYID_LEN) == 0) { + // found key.id + key_index = i; + break; + } + } + if (key_index < 0) { + // this should not happen + netdev_warn(ctx->netdev, "%s secy_index=%u peer_index=%u Tx-SA #%u key not found, should not happen\n", __func__, secy_index, peer_index, sa_ix); + continue; + } + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_AN_BASE_ADDR, + (sa_ix << TRANSMITSA_TX_SA_AN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_NEXT_PN_BASE_ADDR, + (tx_sa->next_pn << TRANSMITSA_TX_SA_NEXT_PN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_KEY_INDEX_WR_BASE_ADDR, + key_index << TRANSMITSA_TX_SA_KEY_INDEX_WR_VAL_SHIFT); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_SSCI_WR_BASE_ADDR, tx_sa->ssci); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_TRANSMITSA_CFG_BASE_ADDR, + (ctx->secy->tx_sc.encrypt ? TRANSMITSA_TX_SA_TRANSMITSA_CFG_CONFIDENTIALITY_WR_MASK : 0)); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_CTRL_BASE_ADDR, + (secy_index << TRANSMITSA_TX_SA_CTRL_SECY_INDEX_SHIFT) | + TRANSMITSA_TX_SA_CTRL_WR_TRIGGER_MASK); + } + macsec_priv->secy_stopped[secy_index] = 0; + } + return 0; +} + +static int cco_macsec_dev_stop(struct macsec_context *ctx) +{ + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(ctx->netdev); + struct macsec_secy *secy = ctx->secy; + struct macsec_rx_sc *rx_sc; + struct macsec_rx_sa *rx_sa; + struct macsec_tx_sa *tx_sa; + u32 secy_index, peer_index, i; + int key_index; + u8 sa_ix; + + if (!get_secy(ctx, &secy_index)) + return -EINVAL; + + if (ctx->prepare) + return 0; + + // netdev_info(ctx->netdev, "%s\n", __func__); + + if (macsec_priv->secy_stopped[secy_index] == 0) { + // 1. Delete the SecY and Tx SC registers: + write_secy_txsc(ctx->netdev, secy, secy_index, 1, 0, 0, 0); + + // 2. Delete each of the Rx SCs found in the macsec_priv->rxsc_array[secy_index]. + // For each Rx SC, delete any Rx SAs found in the ‘sa’ array for the Rx SC. + for (peer_index = 0; peer_index < CCO_MACSEC_RXSC_MAX; ++peer_index) { + rx_sc = macsec_priv->rxsc_array[secy_index][peer_index]; + if (!rx_sc) + continue; + write_rxsc(ctx->netdev, rx_sc, secy_index, peer_index, 1); + for (sa_ix = 0; sa_ix < MACSEC_NUM_AN; ++sa_ix) { + rx_sa = rtnl_dereference(rx_sc->sa[sa_ix]); + if (!rx_sa) + continue; + // find key_index: + for (i = 0, key_index = -1; i < macsec_priv->capabilities.no_of_key_entries_rx && i < CCO_MACSEC_KEYS; ++i) { + if (!(macsec_priv->key_use[i] & CCO_MACSEC_KEY_RX)) + continue; + if (memcmp(rx_sa->key.id, macsec_priv->key_id_table[i], MACSEC_KEYID_LEN) == 0) { + // found key.id + key_index = i; + break; + } + } + if (key_index < 0) { + // this should not happen + netdev_warn(ctx->netdev, "%s Rx-SA key not found, should not happen\n", __func__); + continue; + } + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_AN_BASE_ADDR, + (sa_ix << RECEIVESA_RX_SA_AN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_KEY_INDEX_WR_BASE_ADDR, + key_index << RECEIVESA_RX_SA_KEY_INDEX_WR_VAL_SHIFT); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_RECEIVESA_CFG_BASE_ADDR, 0); + cco_macsec_reg_wr(ctx->netdev, RECEIVESA_BASE_ADDR + RECEIVESA_RX_SA_CTRL_BASE_ADDR, + (peer_index << RECEIVESA_RX_SA_CTRL_PEER_INDEX_SHIFT) | + (secy_index << RECEIVESA_RX_SA_CTRL_SECY_INDEX_SHIFT) | + RECEIVESA_RX_SA_CTRL_WR_TRIGGER_MASK); + } + } + + // 3. Delete any Tx SAs found in the ctx->secy->tx_sc Tx SC structure. + for (sa_ix = 0; sa_ix < MACSEC_NUM_AN; ++sa_ix) { + tx_sa = rtnl_dereference(secy->tx_sc.sa[sa_ix]); + if (!tx_sa) + continue; + // find key_index: + for (i = 0, key_index = -1; i < macsec_priv->capabilities.no_of_key_entries_tx && i < CCO_MACSEC_KEYS; ++i) { + if (!(macsec_priv->key_use[i] & CCO_MACSEC_KEY_TX)) + continue; + if (memcmp(tx_sa->key.id, macsec_priv->key_id_table[i], MACSEC_KEYID_LEN) == 0) { + // found key.id + key_index = i; + break; + } + } + if (key_index < 0) { + // this should not happen + netdev_warn(ctx->netdev, "%s secy_index=%u peer_index=%u Tx-SA #%u key not found, should not happen\n", __func__, secy_index, peer_index, sa_ix); + continue; + } + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_AN_BASE_ADDR, + (sa_ix << TRANSMITSA_TX_SA_AN_VAL_SHIFT)); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_KEY_INDEX_WR_BASE_ADDR, + key_index << TRANSMITSA_TX_SA_KEY_INDEX_WR_VAL_SHIFT); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_TRANSMITSA_CFG_BASE_ADDR, 0); + cco_macsec_reg_wr(ctx->netdev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_CTRL_BASE_ADDR, + (secy_index << TRANSMITSA_TX_SA_CTRL_SECY_INDEX_SHIFT) | + TRANSMITSA_TX_SA_CTRL_WR_TRIGGER_MASK); + } + macsec_priv->secy_stopped[secy_index] = 1; + } + return 0; +} + +static const struct macsec_ops cco_macsec_ops = { + /* Device wide */ + .mdo_dev_open = cco_macsec_dev_open, + .mdo_dev_stop = cco_macsec_dev_stop, + /* SecY */ + .mdo_add_secy = cco_macsec_add_secy, + .mdo_upd_secy = cco_macsec_upd_secy, + .mdo_del_secy = cco_macsec_del_secy, + /* Security channels */ + .mdo_add_rxsc = cco_macsec_add_rxsc, + .mdo_upd_rxsc = cco_macsec_upd_rxsc, + .mdo_del_rxsc = cco_macsec_del_rxsc, + /* Security associations */ + .mdo_add_rxsa = cco_macsec_add_rxsa, + .mdo_upd_rxsa = cco_macsec_upd_rxsa, + .mdo_del_rxsa = cco_macsec_del_rxsa, + .mdo_add_txsa = cco_macsec_add_txsa, + .mdo_upd_txsa = cco_macsec_upd_txsa, + .mdo_del_txsa = cco_macsec_del_txsa, + /* Statistics */ + .mdo_get_dev_stats = cco_macsec_get_dev_stats, + .mdo_get_tx_sc_stats = cco_macsec_get_tx_sc_stats, + .mdo_get_tx_sa_stats = cco_macsec_get_tx_sa_stats, + .mdo_get_rx_sc_stats = cco_macsec_get_rx_sc_stats, + .mdo_get_rx_sa_stats = cco_macsec_get_rx_sa_stats, +}; + +static int cco_macsec_get_capabilities(struct sk_buff *skb, struct netlink_callback *cb); +static int cco_macsec_get_secy_ext(struct sk_buff *skb, struct netlink_callback *cb); +static int cco_macsec_set_secy_ext(struct sk_buff *skb, struct genl_info *info); +static int cco_macsec_get_rx_traffic_rule(struct sk_buff *skb, struct netlink_callback *cb); +static int cco_macsec_set_rx_traffic_rule(struct sk_buff *skb, struct genl_info *info); +static int cco_macsec_get_tx_traffic_rule(struct sk_buff *skb, struct netlink_callback *cb); +static int cco_macsec_set_tx_traffic_rule(struct sk_buff *skb, struct genl_info *info); +static int cco_macsec_get_port_stats(struct sk_buff *skb, struct netlink_callback *cb); +static int cco_macsec_get_uport_stats(struct sk_buff *skb, struct netlink_callback *cb); +static int cco_macsec_get_ext_port_stats(struct sk_buff *skb, struct netlink_callback *cb); +static int cco_macsec_get_txsc_ext(struct sk_buff *skb, struct netlink_callback *cb); +static int cco_macsec_get_rxsc_ext(struct sk_buff *skb, struct netlink_callback *cb); +static int cco_macsec_get_txsa_ext(struct sk_buff *skb, struct netlink_callback *cb); +static int cco_macsec_get_rxsa_ext(struct sk_buff *skb, struct netlink_callback *cb); +static int cco_macsec_clear_stats(struct sk_buff *skb, struct genl_info *info); + +static const struct nla_policy cco_macsec_genl_policy[NUM_CCO_MACSEC_ATTR] = { + [CCO_MACSEC_ATTR_CAPABILITIES] = { + .type = NLA_BINARY, + .len = sizeof(struct cco_macsec_capabilities) }, + [CCO_MACSEC_ATTR_SECY_EXT] = { + .type = NLA_BINARY, + .len = sizeof(struct cco_macsec_secy_ext) }, + [CCO_MACSEC_ATTR_IFINDEX] = { .type = NLA_U32 }, + [CCO_MACSEC_ATTR_INDEX] = { .type = NLA_U32 }, + [CCO_MACSEC_ATTR_SCI] = { .type = NLA_U64 }, + [CCO_MACSEC_ATTR_TRAFFIC_RULE] = { + .type = NLA_BINARY, + .len = sizeof(struct cco_macsec_traffic_rule) }, + [CCO_MACSEC_ATTR_PORT_STATS] = { + .type = NLA_BINARY, + .len = sizeof(struct cco_macsec_port_stats) }, + [CCO_MACSEC_ATTR_TIME_STATS] = { + .type = NLA_BINARY, + .len = sizeof(struct cco_macsec_time_stats) }, +}; + +static const struct genl_small_ops cco_macsec_genl_ops[] = { + { + .cmd = CCO_MACSEC_CMD_GET_CAPABILITIES, + .dumpit = cco_macsec_get_capabilities, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = CCO_MACSEC_CMD_GET_SECY_EXT, + .dumpit = cco_macsec_get_secy_ext, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = CCO_MACSEC_CMD_SET_SECY_EXT, + .doit = cco_macsec_set_secy_ext, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = CCO_MACSEC_CMD_GET_RX_TRAFFIC_RULE, + .dumpit = cco_macsec_get_rx_traffic_rule, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = CCO_MACSEC_CMD_SET_RX_TRAFFIC_RULE, + .doit = cco_macsec_set_rx_traffic_rule, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = CCO_MACSEC_CMD_GET_TX_TRAFFIC_RULE, + .dumpit = cco_macsec_get_tx_traffic_rule, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = CCO_MACSEC_CMD_SET_TX_TRAFFIC_RULE, + .doit = cco_macsec_set_tx_traffic_rule, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = CCO_MACSEC_CMD_GET_PORT_STATS, + .dumpit = cco_macsec_get_port_stats, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = CCO_MACSEC_CMD_GET_UPORT_STATS, + .dumpit = cco_macsec_get_uport_stats, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = CCO_MACSEC_CMD_GET_EXT_PORT_STATS, + .dumpit = cco_macsec_get_ext_port_stats, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = CCO_MACSEC_CMD_GET_TXSC_EXT, + .dumpit = cco_macsec_get_txsc_ext, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = CCO_MACSEC_CMD_GET_RXSC_EXT, + .dumpit = cco_macsec_get_rxsc_ext, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = CCO_MACSEC_CMD_GET_TXSA_EXT, + .dumpit = cco_macsec_get_txsa_ext, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = CCO_MACSEC_CMD_GET_RXSA_EXT, + .dumpit = cco_macsec_get_rxsa_ext, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = CCO_MACSEC_CMD_CLEAR_STATS, + .doit = cco_macsec_clear_stats, + .flags = GENL_ADMIN_PERM, + }, +}; + +static struct genl_family cco_macsec_fam __ro_after_init = { + .name = CCO_MACSEC_GENL_NAME, + .hdrsize = 0, + .version = CCO_MACSEC_GENL_VERSION, + .maxattr = CCO_MACSEC_ATTR_MAX, + .policy = cco_macsec_genl_policy, + .netnsok = true, + .module = THIS_MODULE, + .small_ops = cco_macsec_genl_ops, + .n_small_ops = ARRAY_SIZE(cco_macsec_genl_ops), +}; + +static int cco_macsec_get_capabilities(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + const struct cco_macsec_priv *macsec_priv; + void *hdr; + int dev_idx, d; + u8 found = 0; + + dev_idx = cb->args[0]; + + d = 0; + rtnl_lock(); + + cb->seq = 1; + + for_each_netdev(net, dev) { + if (d < dev_idx || found) + goto next; + + if (!((dev->features & NETIF_F_HW_MACSEC) && + dev->macsec_ops == &cco_macsec_ops)) + goto next; + + macsec_priv = cco_macsec_get_priv(dev); + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + &cco_macsec_fam, NLM_F_MULTI, CCO_MACSEC_CMD_GET_CAPABILITIES); + if (!hdr) + goto done; + genl_dump_check_consistent(cb, hdr); + + if (nla_put(skb, CCO_MACSEC_ATTR_CAPABILITIES, sizeof(macsec_priv->capabilities), &macsec_priv->capabilities)) { + genlmsg_cancel(skb, hdr); + goto done; + } + + genlmsg_end(skb, hdr); + found = 1; +next: + d++; + } + +done: + rtnl_unlock(); + cb->args[0] = d; + return skb->len; +} + +static int cco_macsec_get_secy_ext(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + const struct cco_macsec_priv *macsec_priv; + const struct macsec_secy *secy = NULL; + struct cco_macsec_secy_ext secy_ext = {}; + const struct genl_dumpit_info *genl_info; + struct nlattr **attrs; + u32 secy_index = 0, ifIndex; + void *hdr; + int dev_idx, d; + u8 found = 0; + + genl_info = genl_dumpit_info(cb); + if (!genl_info) + return -EINVAL; + attrs = genl_info->attrs; + if (!attrs) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_IFINDEX]) + return -EINVAL; + ifIndex = nla_get_u32(attrs[CCO_MACSEC_ATTR_IFINDEX]); + + dev_idx = cb->args[0]; + d = 0; + rtnl_lock(); + + cb->seq = 1; + + for_each_netdev(net, dev) { + if (d < dev_idx || found) + goto next; + + if (!((dev->features & NETIF_F_HW_MACSEC) && + dev->macsec_ops == &cco_macsec_ops)) + goto next; + + macsec_priv = cco_macsec_get_priv(dev); + for (secy_index = 0; secy_index < macsec_priv->capabilities.no_of_secys && secy_index < CCO_MACSEC_SECY_MAX; ++secy_index) + if (macsec_priv->secy_array[secy_index] && macsec_priv->secy_ifIndex[secy_index] == ifIndex) { + secy = macsec_priv->secy_array[secy_index]; + break; + } + if (!secy) + goto next; + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + &cco_macsec_fam, NLM_F_MULTI, CCO_MACSEC_CMD_GET_SECY_EXT); + if (!hdr) + goto done; + genl_dump_check_consistent(cb, hdr); + + secy_ext.vlan_in_clear = macsec_priv->secy_vlan_in_clear[secy_index]; + secy_ext.confidentiality_offset = macsec_priv->secy_confidentiality_offs[secy_index]; + secy_ext.secy_txsc_pn_thr = cco_macsec_reg_rd(dev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_TX_SC_PN_THRESHOLD_BASE_ADDR); + + if (nla_put(skb, CCO_MACSEC_ATTR_SECY_EXT, sizeof(secy_ext), &secy_ext)) { + genlmsg_cancel(skb, hdr); + goto done; + } + + genlmsg_end(skb, hdr); + found = 1; +next: + d++; + } + +done: + rtnl_unlock(); + cb->args[0] = d; + return skb->len; +} + +static int cco_macsec_set_secy_ext(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + struct nlattr **attrs = info->attrs; + struct cco_macsec_priv *macsec_priv; + struct cco_macsec_secy_ext secy_ext = {}; + const struct macsec_secy *secy = NULL; + u32 secy_index = 0, ifIndex; + + if (!attrs[CCO_MACSEC_ATTR_IFINDEX]) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_SECY_EXT]) + return -EINVAL; + ifIndex = nla_get_u32(attrs[CCO_MACSEC_ATTR_IFINDEX]); + nla_memcpy(&secy_ext, attrs[CCO_MACSEC_ATTR_SECY_EXT], sizeof(secy_ext)); + + rtnl_lock(); + for_each_netdev(net, dev) { + if ((dev->features & NETIF_F_HW_MACSEC) && + dev->macsec_ops == &cco_macsec_ops) { + macsec_priv = cco_macsec_get_priv(dev); + for (secy_index = 0; secy_index < macsec_priv->capabilities.no_of_secys && secy_index < CCO_MACSEC_SECY_MAX; ++secy_index) + if (macsec_priv->secy_array[secy_index] && macsec_priv->secy_ifIndex[secy_index] == ifIndex) { + secy = macsec_priv->secy_array[secy_index]; + break; + } + if (secy) { + macsec_priv->secy_vlan_in_clear[secy_index] = secy_ext.vlan_in_clear; + macsec_priv->secy_confidentiality_offs[secy_index] = secy_ext.confidentiality_offset; + // Configure the SecY registers: + write_secy_txsc(dev, secy, secy_index, 0, macsec_priv->sa_enabled[secy_index][0], secy_ext.vlan_in_clear, secy_ext.confidentiality_offset); + cco_macsec_reg_wr(dev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_TX_SC_PN_THRESHOLD_BASE_ADDR, secy_ext.secy_txsc_pn_thr); + break; + } + } + } + rtnl_unlock(); + return 0; +} + +static int cco_macsec_clear_stats(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + struct nlattr **attrs = info->attrs; + struct cco_macsec_priv *macsec_priv; + const struct macsec_secy *secy = NULL; + u32 secy_index = 0, ifIndex, peer_index; + + if (!attrs[CCO_MACSEC_ATTR_IFINDEX]) + return -EINVAL; + ifIndex = nla_get_u32(attrs[CCO_MACSEC_ATTR_IFINDEX]); + + rtnl_lock(); + for_each_netdev(net, dev) { + if ((dev->features & NETIF_F_HW_MACSEC) && + dev->macsec_ops == &cco_macsec_ops) { + macsec_priv = cco_macsec_get_priv(dev); + for (secy_index = 0; secy_index < macsec_priv->capabilities.no_of_secys && secy_index < CCO_MACSEC_SECY_MAX; ++secy_index) + if (macsec_priv->secy_array[secy_index] && macsec_priv->secy_ifIndex[secy_index] == ifIndex) { + secy = macsec_priv->secy_array[secy_index]; + break; + } + if (secy) { + memset(&macsec_priv->dev_stats[secy_index], 0, sizeof(macsec_priv->dev_stats[secy_index])); + memset(&macsec_priv->txsc_stats[secy_index], 0, sizeof(macsec_priv->txsc_stats[secy_index])); + memset(&macsec_priv->port_stats[secy_index], 0, sizeof(macsec_priv->port_stats[secy_index])); + memset(&macsec_priv->uport_stats[secy_index], 0, sizeof(macsec_priv->uport_stats[secy_index])); + memset(&macsec_priv->ext_port_stats[secy_index], 0, sizeof(macsec_priv->ext_port_stats[secy_index])); + for (peer_index = 0; peer_index < CCO_MACSEC_RXSC_MAX; ++peer_index) + memset(&macsec_priv->rxsc_stats[secy_index][peer_index], 0, sizeof(macsec_priv->rxsc_stats[secy_index][peer_index])); + break; + } + } + } + rtnl_unlock(); + return 0; +} + +static int cco_macsec_get_rx_traffic_rule(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + const struct cco_macsec_priv *macsec_priv; + struct cco_macsec_traffic_rule rule = {}; + const struct genl_dumpit_info *genl_info; + struct nlattr **attrs; + u32 index, val; + void *hdr; + int dev_idx, d; + u8 found = 0; + + genl_info = genl_dumpit_info(cb); + if (!genl_info) + return -EINVAL; + attrs = genl_info->attrs; + if (!attrs) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_INDEX]) + return -EINVAL; + index = nla_get_u32(attrs[CCO_MACSEC_ATTR_INDEX]); + + dev_idx = cb->args[0]; + d = 0; + rtnl_lock(); + + cb->seq = 1; + + for_each_netdev(net, dev) { + if (d < dev_idx || found) + goto next; + + if (!((dev->features & NETIF_F_HW_MACSEC) && + dev->macsec_ops == &cco_macsec_ops)) + goto next; + + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + &cco_macsec_fam, NLM_F_MULTI, CCO_MACSEC_CMD_GET_RX_TRAFFIC_RULE); + if (!hdr) + goto done; + genl_dump_check_consistent(cb, hdr); + + macsec_priv = cco_macsec_get_priv(dev); + if (index >= macsec_priv->capabilities.no_tt_entries_rx) { + rtnl_unlock(); + return -EINVAL; + } + + // trigger a read of Traffic Map rule (Rx): + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_CTRL_BASE_ADDR, + TRAFFIC_MAP_TT_CTRL_RX_CONFIG_EN_MASK | + TRAFFIC_MAP_TT_CTRL_RD_TRIGGER_MASK | + TRAFFIC_MAP_TT_CTRL_FIELD_SELECT_WR_MASK | + (index << TRAFFIC_MAP_TT_CTRL_INDEX_SHIFT)); + val = cco_macsec_reg_rd(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_MAC_ADDR_0_RD_BASE_ADDR); + rule.macAddr[0] = val >> 24; + rule.macAddr[1] = val >> 16; + rule.macAddr[2] = val >> 8; + rule.macAddr[3] = val; + val = cco_macsec_reg_rd(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_MAC_ADDR_1_RD_BASE_ADDR); + rule.macAddr[4] = val >> 8; + rule.macAddr[5] = val; + rule.vlanId = cco_macsec_reg_rd(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_VLAN_RD_BASE_ADDR); + rule.ethType = cco_macsec_reg_rd(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_ETYPE_RD_BASE_ADDR); + rule.fieldSelect = cco_macsec_reg_rd(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_FIELD_SELECT_RD_BASE_ADDR); + rule.other = cco_macsec_reg_rd(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_OTHER_RD_BASE_ADDR); + rule.secy_index = cco_macsec_reg_rd(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_SECY_RD_BASE_ADDR); + + if (nla_put(skb, CCO_MACSEC_ATTR_TRAFFIC_RULE, sizeof(rule), &rule)) { + genlmsg_cancel(skb, hdr); + goto done; + } + + genlmsg_end(skb, hdr); + found = 1; +next: + d++; + } + +done: + rtnl_unlock(); + cb->args[0] = d; + return skb->len; +} + +static int cco_macsec_set_rx_traffic_rule(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + struct nlattr **attrs = info->attrs; + struct cco_macsec_priv *macsec_priv; + struct cco_macsec_traffic_rule rule = {}; + u32 index, val; + + if (!attrs[CCO_MACSEC_ATTR_INDEX]) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_TRAFFIC_RULE]) + return -EINVAL; + index = nla_get_u32(attrs[CCO_MACSEC_ATTR_INDEX]); + nla_memcpy(&rule, attrs[CCO_MACSEC_ATTR_TRAFFIC_RULE], sizeof(rule)); + + rtnl_lock(); + for_each_netdev(net, dev) { + if ((dev->features & NETIF_F_HW_MACSEC) && + dev->macsec_ops == &cco_macsec_ops) { + macsec_priv = cco_macsec_get_priv(dev); + if (index >= macsec_priv->capabilities.no_tt_entries_rx) { + rtnl_unlock(); + return -EINVAL; + } + // trigger a write of Traffic Map rule (Rx): + val = ((rule.macAddr[0] << 24) | (rule.macAddr[1] << 16) | + (rule.macAddr[2] << 8) | rule.macAddr[3]); + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_MAC_ADDR_0_WR_BASE_ADDR, val); + val = (rule.macAddr[4] << 8) | rule.macAddr[5]; + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_MAC_ADDR_1_WR_BASE_ADDR, val); + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_VLAN_WR_BASE_ADDR, rule.vlanId); + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_ETYPE_WR_BASE_ADDR, rule.ethType); + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_OTHER_WR_BASE_ADDR, rule.other); + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_SECY_WR_BASE_ADDR, rule.secy_index); + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_CTRL_BASE_ADDR, + TRAFFIC_MAP_TT_CTRL_RX_CONFIG_EN_MASK | + TRAFFIC_MAP_TT_CTRL_WR_TRIGGER_MASK | + (rule.fieldSelect << TRAFFIC_MAP_TT_CTRL_FIELD_SELECT_WR_SHIFT) | + (index << TRAFFIC_MAP_TT_CTRL_INDEX_SHIFT)); + break; + } + } + rtnl_unlock(); + return 0; +} + +static int cco_macsec_get_tx_traffic_rule(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + const struct cco_macsec_priv *macsec_priv; + struct cco_macsec_traffic_rule rule = {}; + const struct genl_dumpit_info *genl_info; + struct nlattr **attrs; + u32 index, val; + void *hdr; + int dev_idx, d; + u8 found = 0; + + genl_info = genl_dumpit_info(cb); + if (!genl_info) + return -EINVAL; + attrs = genl_info->attrs; + if (!attrs) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_INDEX]) + return -EINVAL; + index = nla_get_u32(attrs[CCO_MACSEC_ATTR_INDEX]); + + dev_idx = cb->args[0]; + d = 0; + rtnl_lock(); + + cb->seq = 1; + + for_each_netdev(net, dev) { + if (d < dev_idx || found) + goto next; + + if (!((dev->features & NETIF_F_HW_MACSEC) && + dev->macsec_ops == &cco_macsec_ops)) + goto next; + + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + &cco_macsec_fam, NLM_F_MULTI, CCO_MACSEC_CMD_GET_TX_TRAFFIC_RULE); + if (!hdr) + goto done; + genl_dump_check_consistent(cb, hdr); + + macsec_priv = cco_macsec_get_priv(dev); + if (index >= macsec_priv->capabilities.no_tt_entries_tx) { + rtnl_unlock(); + return -EINVAL; + } + + // trigger a read of Traffic Map rule (Tx): + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_CTRL_BASE_ADDR, + TRAFFIC_MAP_TT_CTRL_TX_CONFIG_EN_MASK | + TRAFFIC_MAP_TT_CTRL_RD_TRIGGER_MASK | + TRAFFIC_MAP_TT_CTRL_FIELD_SELECT_WR_MASK | + (index << TRAFFIC_MAP_TT_CTRL_INDEX_SHIFT)); + val = cco_macsec_reg_rd(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_MAC_ADDR_0_RD_BASE_ADDR); + rule.macAddr[0] = val >> 24; + rule.macAddr[1] = val >> 16; + rule.macAddr[2] = val >> 8; + rule.macAddr[3] = val; + val = cco_macsec_reg_rd(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_MAC_ADDR_1_RD_BASE_ADDR); + rule.macAddr[4] = val >> 8; + rule.macAddr[5] = val; + rule.vlanId = cco_macsec_reg_rd(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_VLAN_RD_BASE_ADDR); + rule.ethType = cco_macsec_reg_rd(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_ETYPE_RD_BASE_ADDR); + rule.fieldSelect = cco_macsec_reg_rd(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_FIELD_SELECT_RD_BASE_ADDR); + rule.other = cco_macsec_reg_rd(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_OTHER_RD_BASE_ADDR); + rule.secy_index = cco_macsec_reg_rd(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_SECY_RD_BASE_ADDR); + + if (nla_put(skb, CCO_MACSEC_ATTR_TRAFFIC_RULE, sizeof(rule), &rule)) { + genlmsg_cancel(skb, hdr); + goto done; + } + + genlmsg_end(skb, hdr); + found = 1; +next: + d++; + } + +done: + rtnl_unlock(); + cb->args[0] = d; + return skb->len; +} + +static int cco_macsec_set_tx_traffic_rule(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + struct nlattr **attrs = info->attrs; + struct cco_macsec_priv *macsec_priv; + struct cco_macsec_traffic_rule rule = {}; + u32 index, val; + + if (!attrs[CCO_MACSEC_ATTR_INDEX]) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_TRAFFIC_RULE]) + return -EINVAL; + index = nla_get_u32(attrs[CCO_MACSEC_ATTR_INDEX]); + nla_memcpy(&rule, attrs[CCO_MACSEC_ATTR_TRAFFIC_RULE], sizeof(rule)); + + rtnl_lock(); + for_each_netdev(net, dev) { + if ((dev->features & NETIF_F_HW_MACSEC) && + dev->macsec_ops == &cco_macsec_ops) { + macsec_priv = cco_macsec_get_priv(dev); + if (index >= macsec_priv->capabilities.no_tt_entries_tx) { + rtnl_unlock(); + return -EINVAL; + } + // trigger a write of Traffic Map rule (Tx): + val = ((rule.macAddr[0] << 24) | (rule.macAddr[1] << 16) | + (rule.macAddr[2] << 8) | rule.macAddr[3]); + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_MAC_ADDR_0_WR_BASE_ADDR, val); + val = (rule.macAddr[4] << 8) | rule.macAddr[5]; + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_MAC_ADDR_1_WR_BASE_ADDR, val); + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_VLAN_WR_BASE_ADDR, rule.vlanId); + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_ETYPE_WR_BASE_ADDR, rule.ethType); + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_OTHER_WR_BASE_ADDR, rule.other); + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_SECY_WR_BASE_ADDR, rule.secy_index); + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_CTRL_BASE_ADDR, + TRAFFIC_MAP_TT_CTRL_TX_CONFIG_EN_MASK | + TRAFFIC_MAP_TT_CTRL_WR_TRIGGER_MASK | + (rule.fieldSelect << TRAFFIC_MAP_TT_CTRL_FIELD_SELECT_WR_SHIFT) | + (index << TRAFFIC_MAP_TT_CTRL_INDEX_SHIFT)); + break; + } + } + rtnl_unlock(); + return 0; +} + +static int cco_macsec_get_port_stats(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + struct cco_macsec_priv *macsec_priv; + struct cco_macsec_port_stats port_stats = {}; + const struct genl_dumpit_info *genl_info; + struct nlattr **attrs; + const struct macsec_secy *secy = NULL; + u32 secy_index = 0, ifIndex; + void *hdr; + int dev_idx, d; + u8 found = 0; + + genl_info = genl_dumpit_info(cb); + if (!genl_info) + return -EINVAL; + attrs = genl_info->attrs; + if (!attrs) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_IFINDEX]) + return -EINVAL; + ifIndex = nla_get_u32(attrs[CCO_MACSEC_ATTR_IFINDEX]); + + dev_idx = cb->args[0]; + d = 0; + rtnl_lock(); + + cb->seq = 1; + + for_each_netdev(net, dev) { + if (d < dev_idx || found) + goto next; + + if (!((dev->features & NETIF_F_HW_MACSEC) && + dev->macsec_ops == &cco_macsec_ops)) + goto next; + + macsec_priv = cco_macsec_get_priv(dev); + for (secy_index = 0; secy_index < macsec_priv->capabilities.no_of_secys && secy_index < CCO_MACSEC_SECY_MAX; ++secy_index) + if (macsec_priv->secy_array[secy_index] && macsec_priv->secy_ifIndex[secy_index] == ifIndex) { + secy = macsec_priv->secy_array[secy_index]; + break; + } + if (!secy) + goto next; + + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + &cco_macsec_fam, NLM_F_MULTI, CCO_MACSEC_CMD_GET_PORT_STATS); + if (!hdr) + goto done; + genl_dump_check_consistent(cb, hdr); + + // trigger a read of per SecY port stats: + cco_macsec_reg_wr(dev, STATISTICS_BASE_ADDR + STATISTICS_STATS_CTRL_BASE_ADDR, + STATISTICS_STATS_CTRL_PORT_STATS_RD_TRIGGER_MASK | + (secy_index << STATISTICS_STATS_CTRL_SECY_INDEX_SHIFT)); + + // read counters: + macsec_priv->port_stats[secy_index].ifInOctets += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINOCTETS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInUcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINUCASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInMcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINMULTICASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInBcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINBROADCASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInDiscards += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINDISCARDS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInErrors += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINERRORS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutOctets += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTOCTETS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutUcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTUCASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutMcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTMULTICASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutBcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTBROADCASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutErrors += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTERRORS_BASE_ADDR); + port_stats = macsec_priv->port_stats[secy_index]; + + // store other port stats: + macsec_priv->dev_stats[secy_index].OutPktsUntagged += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_OUTPKTSUNTAGGED_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsUntagged += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSUNTAGGED_BASE_ADDR); + macsec_priv->dev_stats[secy_index].OutPktsTooLong += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_OUTPKTSTOOLONG_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsNoTag += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSNOTAG_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsBadTag += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSBADTAG_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsNoSCI += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSNOSA_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsUnknownSCI += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSNOSAERROR_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsOverrun += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSOVERRUN_BASE_ADDR); + + // store other uport stats: + macsec_priv->uport_stats[secy_index].ifInOctets += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINOCTETS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInUcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINUCASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInMcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINMULTICASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInBcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINBROADCASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInDiscards += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINDISCARDS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInErrors += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINERRORS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutOctets += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTOCTETS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutUcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTUCASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutMcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTMULTICASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutBcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTBROADCASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutErrors += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTERRORS_BASE_ADDR); + + // store other ext port stats: + macsec_priv->ext_port_stats[secy_index].compTxDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_COMP_TX_DISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].compRxDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_COMP_RX_DISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].txSecYDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_SECYDISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].rxSecYDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_SECYDISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].txReceivingDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_RECEIVINGDISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].rxTransmittingDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_TRANSMITTINGDISABLE_BASE_ADDR); + + if (nla_put(skb, CCO_MACSEC_ATTR_PORT_STATS, sizeof(port_stats), &port_stats)) { + genlmsg_cancel(skb, hdr); + goto done; + } + + genlmsg_end(skb, hdr); + found = 1; +next: + d++; + } + +done: + rtnl_unlock(); + cb->args[0] = d; + return skb->len; +} + +static int cco_macsec_get_uport_stats(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + struct cco_macsec_priv *macsec_priv; + struct cco_macsec_port_stats port_stats = {}; + const struct genl_dumpit_info *genl_info; + struct nlattr **attrs; + const struct macsec_secy *secy = NULL; + u32 secy_index = 0, ifIndex; + void *hdr; + int dev_idx, d; + u8 found = 0; + + genl_info = genl_dumpit_info(cb); + if (!genl_info) + return -EINVAL; + attrs = genl_info->attrs; + if (!attrs) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_IFINDEX]) + return -EINVAL; + ifIndex = nla_get_u32(attrs[CCO_MACSEC_ATTR_IFINDEX]); + + dev_idx = cb->args[0]; + d = 0; + rtnl_lock(); + + cb->seq = 1; + + for_each_netdev(net, dev) { + if (d < dev_idx || found) + goto next; + + if (!((dev->features & NETIF_F_HW_MACSEC) && + dev->macsec_ops == &cco_macsec_ops)) + goto next; + + macsec_priv = cco_macsec_get_priv(dev); + for (secy_index = 0; secy_index < macsec_priv->capabilities.no_of_secys && secy_index < CCO_MACSEC_SECY_MAX; ++secy_index) + if (macsec_priv->secy_array[secy_index] && macsec_priv->secy_ifIndex[secy_index] == ifIndex) { + secy = macsec_priv->secy_array[secy_index]; + break; + } + if (!secy) + goto next; + + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + &cco_macsec_fam, NLM_F_MULTI, CCO_MACSEC_CMD_GET_UPORT_STATS); + if (!hdr) + goto done; + genl_dump_check_consistent(cb, hdr); + + // trigger a read of per SecY port stats: + cco_macsec_reg_wr(dev, STATISTICS_BASE_ADDR + STATISTICS_STATS_CTRL_BASE_ADDR, + STATISTICS_STATS_CTRL_PORT_STATS_RD_TRIGGER_MASK | + (secy_index << STATISTICS_STATS_CTRL_SECY_INDEX_SHIFT)); + + // read counters: + macsec_priv->uport_stats[secy_index].ifInOctets += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINOCTETS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInUcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINUCASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInMcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINMULTICASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInBcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINBROADCASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInDiscards += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINDISCARDS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInErrors += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINERRORS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutOctets += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTOCTETS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutUcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTUCASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutMcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTMULTICASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutBcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTBROADCASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutErrors += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTERRORS_BASE_ADDR); + port_stats = macsec_priv->uport_stats[secy_index]; + + // store other port stats: + macsec_priv->dev_stats[secy_index].OutPktsUntagged += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_OUTPKTSUNTAGGED_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsUntagged += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSUNTAGGED_BASE_ADDR); + macsec_priv->dev_stats[secy_index].OutPktsTooLong += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_OUTPKTSTOOLONG_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsNoTag += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSNOTAG_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsBadTag += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSBADTAG_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsNoSCI += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSNOSA_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsUnknownSCI += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSNOSAERROR_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsOverrun += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSOVERRUN_BASE_ADDR); + + // store other port stats: + macsec_priv->port_stats[secy_index].ifInOctets += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINOCTETS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInUcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINUCASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInMcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINMULTICASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInBcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINBROADCASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInDiscards += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINDISCARDS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInErrors += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINERRORS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutOctets += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTOCTETS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutUcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTUCASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutMcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTMULTICASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutBcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTBROADCASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutErrors += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTERRORS_BASE_ADDR); + + // store other ext port stats: + macsec_priv->ext_port_stats[secy_index].compTxDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_COMP_TX_DISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].compRxDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_COMP_RX_DISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].txSecYDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_SECYDISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].rxSecYDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_SECYDISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].txReceivingDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_RECEIVINGDISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].rxTransmittingDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_TRANSMITTINGDISABLE_BASE_ADDR); + + if (nla_put(skb, CCO_MACSEC_ATTR_PORT_STATS, sizeof(port_stats), &port_stats)) { + genlmsg_cancel(skb, hdr); + goto done; + } + + genlmsg_end(skb, hdr); + found = 1; +next: + d++; + } + +done: + rtnl_unlock(); + cb->args[0] = d; + return skb->len; +} + +static int cco_macsec_get_ext_port_stats(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + struct cco_macsec_priv *macsec_priv; + struct cco_macsec_ext_port_stats ext_port_stats = {}; + const struct genl_dumpit_info *genl_info; + struct nlattr **attrs; + const struct macsec_secy *secy = NULL; + u32 secy_index = 0, ifIndex; + void *hdr; + int dev_idx, d; + u8 found = 0; + + genl_info = genl_dumpit_info(cb); + if (!genl_info) + return -EINVAL; + attrs = genl_info->attrs; + if (!attrs) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_IFINDEX]) + return -EINVAL; + ifIndex = nla_get_u32(attrs[CCO_MACSEC_ATTR_IFINDEX]); + + dev_idx = cb->args[0]; + d = 0; + rtnl_lock(); + + cb->seq = 1; + + for_each_netdev(net, dev) { + if (d < dev_idx || found) + goto next; + + if (!((dev->features & NETIF_F_HW_MACSEC) && + dev->macsec_ops == &cco_macsec_ops)) + goto next; + + macsec_priv = cco_macsec_get_priv(dev); + for (secy_index = 0; secy_index < macsec_priv->capabilities.no_of_secys && secy_index < CCO_MACSEC_SECY_MAX; ++secy_index) + if (macsec_priv->secy_array[secy_index] && macsec_priv->secy_ifIndex[secy_index] == ifIndex) { + secy = macsec_priv->secy_array[secy_index]; + break; + } + if (!secy) + goto next; + + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + &cco_macsec_fam, NLM_F_MULTI, CCO_MACSEC_CMD_GET_EXT_PORT_STATS); + if (!hdr) + goto done; + genl_dump_check_consistent(cb, hdr); + + // trigger a read of per SecY port stats: + cco_macsec_reg_wr(dev, STATISTICS_BASE_ADDR + STATISTICS_STATS_CTRL_BASE_ADDR, + STATISTICS_STATS_CTRL_PORT_STATS_RD_TRIGGER_MASK | + (secy_index << STATISTICS_STATS_CTRL_SECY_INDEX_SHIFT)); + + // read counters: + macsec_priv->ext_port_stats[secy_index].compTxDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_COMP_TX_DISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].compRxDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_COMP_RX_DISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].txSecYDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_SECYDISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].rxSecYDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_SECYDISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].txReceivingDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_RECEIVINGDISABLE_BASE_ADDR); + macsec_priv->ext_port_stats[secy_index].rxTransmittingDisable += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_TRANSMITTINGDISABLE_BASE_ADDR); + ext_port_stats = macsec_priv->ext_port_stats[secy_index]; + + // store other port stats: + macsec_priv->dev_stats[secy_index].OutPktsUntagged += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_OUTPKTSUNTAGGED_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsUntagged += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSUNTAGGED_BASE_ADDR); + macsec_priv->dev_stats[secy_index].OutPktsTooLong += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_TX_OUTPKTSTOOLONG_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsNoTag += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSNOTAG_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsBadTag += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSBADTAG_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsNoSCI += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSNOSA_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsUnknownSCI += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSNOSAERROR_BASE_ADDR); + macsec_priv->dev_stats[secy_index].InPktsOverrun += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_RX_INPKTSOVERRUN_BASE_ADDR); + + // store other port stats: + macsec_priv->port_stats[secy_index].ifInOctets += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINOCTETS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInUcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINUCASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInMcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINMULTICASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInBcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINBROADCASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInDiscards += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINDISCARDS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifInErrors += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFINERRORS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutOctets += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTOCTETS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutUcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTUCASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutMcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTMULTICASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutBcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTBROADCASTPKTS_BASE_ADDR); + macsec_priv->port_stats[secy_index].ifOutErrors += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_CP_IFOUTERRORS_BASE_ADDR); + + // store other uport stats: + macsec_priv->uport_stats[secy_index].ifInOctets += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINOCTETS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInUcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINUCASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInMcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINMULTICASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInBcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINBROADCASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInDiscards += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINDISCARDS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifInErrors += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFINERRORS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutOctets += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTOCTETS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutUcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTUCASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutMcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTMULTICASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutBcPkts += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTBROADCASTPKTS_BASE_ADDR); + macsec_priv->uport_stats[secy_index].ifOutErrors += cco_macsec_reg_rd(dev, STATISTICS_BASE_ADDR + STATISTICS_PS_UP_IFOUTERRORS_BASE_ADDR); + + if (nla_put(skb, CCO_MACSEC_ATTR_EXT_PORT_STATS, sizeof(ext_port_stats), &ext_port_stats)) { + genlmsg_cancel(skb, hdr); + goto done; + } + + genlmsg_end(skb, hdr); + found = 1; +next: + d++; + } + +done: + rtnl_unlock(); + cb->args[0] = d; + return skb->len; +} + +static int cco_macsec_get_txsc_ext(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + const struct cco_macsec_priv *macsec_priv; + const struct macsec_secy *secy = NULL; + const struct genl_dumpit_info *genl_info; + struct nlattr **attrs; + u32 secy_index = 0, ifIndex; + void *hdr; + int dev_idx, d; + u8 found = 0; + + genl_info = genl_dumpit_info(cb); + if (!genl_info) + return -EINVAL; + attrs = genl_info->attrs; + if (!attrs) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_IFINDEX]) + return -EINVAL; + ifIndex = nla_get_u32(attrs[CCO_MACSEC_ATTR_IFINDEX]); + + dev_idx = cb->args[0]; + d = 0; + rtnl_lock(); + + cb->seq = 1; + + for_each_netdev(net, dev) { + if (d < dev_idx || found) + goto next; + + if (!((dev->features & NETIF_F_HW_MACSEC) && + dev->macsec_ops == &cco_macsec_ops)) + goto next; + + macsec_priv = cco_macsec_get_priv(dev); + for (secy_index = 0; secy_index < macsec_priv->capabilities.no_of_secys && secy_index < CCO_MACSEC_SECY_MAX; ++secy_index) + if (macsec_priv->secy_array[secy_index] && macsec_priv->secy_ifIndex[secy_index] == ifIndex) { + secy = macsec_priv->secy_array[secy_index]; + break; + } + if (!secy) + goto next; + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + &cco_macsec_fam, NLM_F_MULTI, CCO_MACSEC_CMD_GET_TXSC_EXT); + if (!hdr) + goto done; + genl_dump_check_consistent(cb, hdr); + + if (nla_put(skb, CCO_MACSEC_ATTR_TIME_STATS, sizeof(macsec_priv->txsc_ext[0]), &macsec_priv->txsc_ext[secy_index])) { + genlmsg_cancel(skb, hdr); + goto done; + } + + genlmsg_end(skb, hdr); + found = 1; +next: + d++; + } + +done: + rtnl_unlock(); + cb->args[0] = d; + return skb->len; +} + +static int cco_macsec_get_rxsc_ext(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + const struct cco_macsec_priv *macsec_priv; + const struct macsec_secy *secy = NULL; + const struct genl_dumpit_info *genl_info; + struct nlattr **attrs; + u32 secy_index = 0, ifIndex, index; + sci_t sci; + void *hdr; + int dev_idx, d; + u8 found = 0; + + genl_info = genl_dumpit_info(cb); + if (!genl_info) + return -EINVAL; + attrs = genl_info->attrs; + if (!attrs) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_IFINDEX]) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_SCI]) + return -EINVAL; + ifIndex = nla_get_u32(attrs[CCO_MACSEC_ATTR_IFINDEX]); + sci = (sci_t)nla_get_u64(attrs[CCO_MACSEC_ATTR_SCI]); + + dev_idx = cb->args[0]; + d = 0; + rtnl_lock(); + + cb->seq = 1; + + for_each_netdev(net, dev) { + if (d < dev_idx || found) + goto next; + + if (!((dev->features & NETIF_F_HW_MACSEC) && + dev->macsec_ops == &cco_macsec_ops)) + goto next; + + macsec_priv = cco_macsec_get_priv(dev); + for (secy_index = 0; secy_index < macsec_priv->capabilities.no_of_secys && secy_index < CCO_MACSEC_SECY_MAX; ++secy_index) + if (macsec_priv->secy_array[secy_index] && macsec_priv->secy_ifIndex[secy_index] == ifIndex) { + secy = macsec_priv->secy_array[secy_index]; + break; + } + if (!secy) + goto next; + + for (index = 0; index < CCO_MACSEC_RXSC_MAX; ++index) + if (macsec_priv->rxsc_array[secy_index][index] && macsec_priv->rxsc_array[secy_index][index]->sci == sci) + // found + break; + if (index >= CCO_MACSEC_RXSC_MAX) + goto next; + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + &cco_macsec_fam, NLM_F_MULTI, CCO_MACSEC_CMD_GET_RXSC_EXT); + if (!hdr) + goto done; + genl_dump_check_consistent(cb, hdr); + + if (nla_put(skb, CCO_MACSEC_ATTR_TIME_STATS, sizeof(macsec_priv->rxsc_ext[0][0]), &macsec_priv->rxsc_ext[secy_index][index])) { + genlmsg_cancel(skb, hdr); + goto done; + } + + genlmsg_end(skb, hdr); + found = 1; +next: + d++; + } + +done: + rtnl_unlock(); + cb->args[0] = d; + return skb->len; +} + +static int cco_macsec_get_txsa_ext(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + const struct cco_macsec_priv *macsec_priv; + const struct macsec_secy *secy = NULL; + const struct genl_dumpit_info *genl_info; + struct nlattr **attrs; + u32 secy_index = 0, ifIndex, sa_ix; + void *hdr; + int dev_idx, d; + u8 found = 0; + + genl_info = genl_dumpit_info(cb); + if (!genl_info) + return -EINVAL; + attrs = genl_info->attrs; + if (!attrs) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_IFINDEX]) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_INDEX]) + return -EINVAL; + ifIndex = nla_get_u32(attrs[CCO_MACSEC_ATTR_IFINDEX]); + sa_ix = nla_get_u32(attrs[CCO_MACSEC_ATTR_INDEX]); + if (sa_ix >= MACSEC_NUM_AN) + return -EINVAL; + + dev_idx = cb->args[0]; + d = 0; + rtnl_lock(); + + cb->seq = 1; + + for_each_netdev(net, dev) { + if (d < dev_idx || found) + goto next; + + if (!((dev->features & NETIF_F_HW_MACSEC) && + dev->macsec_ops == &cco_macsec_ops)) + goto next; + + macsec_priv = cco_macsec_get_priv(dev); + for (secy_index = 0; secy_index < macsec_priv->capabilities.no_of_secys && secy_index < CCO_MACSEC_SECY_MAX; ++secy_index) + if (macsec_priv->secy_array[secy_index] && macsec_priv->secy_ifIndex[secy_index] == ifIndex) { + secy = macsec_priv->secy_array[secy_index]; + break; + } + if (!secy) + goto next; + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + &cco_macsec_fam, NLM_F_MULTI, CCO_MACSEC_CMD_GET_TXSA_EXT); + if (!hdr) + goto done; + genl_dump_check_consistent(cb, hdr); + + if (nla_put(skb, CCO_MACSEC_ATTR_TIME_STATS, sizeof(macsec_priv->txsa_ext[0][0]), &macsec_priv->txsa_ext[secy_index][sa_ix])) { + genlmsg_cancel(skb, hdr); + goto done; + } + + genlmsg_end(skb, hdr); + found = 1; +next: + d++; + } + +done: + rtnl_unlock(); + cb->args[0] = d; + return skb->len; +} + +static int cco_macsec_get_rxsa_ext(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + const struct cco_macsec_priv *macsec_priv; + const struct macsec_secy *secy = NULL; + const struct genl_dumpit_info *genl_info; + struct nlattr **attrs; + u32 secy_index = 0, ifIndex, index, sa_ix; + sci_t sci; + void *hdr; + int dev_idx, d; + u8 found = 0; + + genl_info = genl_dumpit_info(cb); + if (!genl_info) + return -EINVAL; + attrs = genl_info->attrs; + if (!attrs) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_IFINDEX]) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_SCI]) + return -EINVAL; + if (!attrs[CCO_MACSEC_ATTR_INDEX]) + return -EINVAL; + ifIndex = nla_get_u32(attrs[CCO_MACSEC_ATTR_IFINDEX]); + sci = (sci_t)nla_get_u64(attrs[CCO_MACSEC_ATTR_SCI]); + sa_ix = nla_get_u32(attrs[CCO_MACSEC_ATTR_INDEX]); + if (sa_ix >= MACSEC_NUM_AN) + return -EINVAL; + + dev_idx = cb->args[0]; + d = 0; + rtnl_lock(); + + cb->seq = 1; + + for_each_netdev(net, dev) { + if (d < dev_idx || found) + goto next; + + if (!((dev->features & NETIF_F_HW_MACSEC) && + dev->macsec_ops == &cco_macsec_ops)) + goto next; + + macsec_priv = cco_macsec_get_priv(dev); + for (secy_index = 0; secy_index < macsec_priv->capabilities.no_of_secys && secy_index < CCO_MACSEC_SECY_MAX; ++secy_index) + if (macsec_priv->secy_array[secy_index] && macsec_priv->secy_ifIndex[secy_index] == ifIndex) { + secy = macsec_priv->secy_array[secy_index]; + break; + } + if (!secy) + goto next; + + for (index = 0; index < CCO_MACSEC_RXSC_MAX; ++index) + if (macsec_priv->rxsc_array[secy_index][index] && macsec_priv->rxsc_array[secy_index][index]->sci == sci) + // found + break; + if (index >= CCO_MACSEC_RXSC_MAX) + goto next; + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + &cco_macsec_fam, NLM_F_MULTI, CCO_MACSEC_CMD_GET_RXSA_EXT); + if (!hdr) + goto done; + genl_dump_check_consistent(cb, hdr); + + if (nla_put(skb, CCO_MACSEC_ATTR_TIME_STATS, sizeof(macsec_priv->rxsa_ext[0][0][0]), &macsec_priv->rxsa_ext[secy_index][index][sa_ix])) { + genlmsg_cancel(skb, hdr); + goto done; + } + + genlmsg_end(skb, hdr); + found = 1; +next: + d++; + } + +done: + rtnl_unlock(); + cb->args[0] = d; + return skb->len; +} + +static void get_macsec_capabilities(struct net_device *netdev, struct cco_macsec_capabilities* p) +{ + u32 val, msb; + + val = cco_macsec_reg_rd(netdev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_IP_AES_GCM_128_CS_IDENTIFIER_0_BASE_ADDR); + msb = cco_macsec_reg_rd(netdev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_IP_AES_GCM_128_CS_IDENTIFIER_1_BASE_ADDR); + p->aes_gcm_128_cs_id = ((u64)msb << 32) | val; + + val = cco_macsec_reg_rd(netdev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_IP_AES_GCM_256_CS_IDENTIFIER_0_BASE_ADDR); + msb = cco_macsec_reg_rd(netdev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_IP_AES_GCM_256_CS_IDENTIFIER_1_BASE_ADDR); + p->aes_gcm_256_cs_id = ((u64)msb << 32) | val; + + val = cco_macsec_reg_rd(netdev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_IP_CAPABILITIES_1_BASE_ADDR); + p->no_of_peers = (val & MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_PEERS_MASK) >> MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_PEERS_SHIFT; + p->no_of_key_entries_rx = (val & MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_CS_ENTRIES_RX_MASK) >> MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_CS_ENTRIES_RX_SHIFT; + p->no_of_key_entries_tx = (val & MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_CS_ENTRIES_TX_MASK) >> MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_CS_ENTRIES_TX_SHIFT; + p->no_of_secys = (val & MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_SECYS_MASK) >> MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_SECYS_SHIFT; + netdev_info(netdev, "%s: no_of_peers=%u no_of_key_entries_rx=%u no_of_key_entries_tx=%u no_of_secys=%u\n", + __func__, p->no_of_peers, p->no_of_key_entries_rx, p->no_of_key_entries_tx, p->no_of_secys); + + val = cco_macsec_reg_rd(netdev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_IP_CAPABILITIES_2_BASE_ADDR); + p->no_tt_entries_rx = (val & MACSEC_CORE_IP_CAPABILITIES_2_NO_TT_ENTRIES_RX_MASK) >> MACSEC_CORE_IP_CAPABILITIES_2_NO_TT_ENTRIES_RX_SHIFT; + p->no_tt_entries_tx = (val & MACSEC_CORE_IP_CAPABILITIES_2_NO_TT_ENTRIES_TX_MASK) >> MACSEC_CORE_IP_CAPABILITIES_2_NO_TT_ENTRIES_TX_SHIFT; + p->confidentiality_offs = (val & MACSEC_CORE_IP_CAPABILITIES_2_CONFIDENTIALITY_OFFSET_MASK) >> MACSEC_CORE_IP_CAPABILITIES_2_CONFIDENTIALITY_OFFSET_SHIFT; + p->available_ciphersuites = (val & MACSEC_CORE_IP_CAPABILITIES_2_AVAILABLE_CIPHERSUITES_MASK) >> MACSEC_CORE_IP_CAPABILITIES_2_AVAILABLE_CIPHERSUITES_SHIFT; + p->vlan_in_clear = (val & MACSEC_CORE_IP_CAPABILITIES_2_VLAN_IN_CLEAR_MASK) >> MACSEC_CORE_IP_CAPABILITIES_2_VLAN_IN_CLEAR_SHIFT; + netdev_info(netdev, "%s: no_tt_entries_rx=%u no_tt_entries_tx=%u confidentiality_offs=%u available_ciphersuites=%u vlan_in_clear=%u\n", + __func__, p->no_tt_entries_rx, p->no_tt_entries_tx, p->confidentiality_offs, p->available_ciphersuites, p->vlan_in_clear); + + val = cco_macsec_reg_rd(netdev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_IP_CS_CAPABILITY_BASE_ADDR); + p->ICVLength = (val & MACSEC_CORE_IP_CS_CAPABILITY_ICVLENGTH_MASK) >> MACSEC_CORE_IP_CS_CAPABILITY_ICVLENGTH_SHIFT; + p->changesDataLength = (val & MACSEC_CORE_IP_CS_CAPABILITY_CHANGESDATALENGTH_MASK) >> MACSEC_CORE_IP_CS_CAPABILITY_CHANGESDATALENGTH_SHIFT; + p->offsConfidentiality = (val & MACSEC_CORE_IP_CS_CAPABILITY_OFFSETCONFIDENTIALITY_MASK) >> MACSEC_CORE_IP_CS_CAPABILITY_OFFSETCONFIDENTIALITY_SHIFT; + p->integrityProtection = (val & MACSEC_CORE_IP_CS_CAPABILITY_INTEGRITYPROTECTION_MASK) >> MACSEC_CORE_IP_CS_CAPABILITY_INTEGRITYPROTECTION_SHIFT; + netdev_info(netdev, "%s: ICVLength=%u changesDataLength=%u offsConfidentiality=%u integrityProtection=%u\n", + __func__, p->ICVLength, p->changesDataLength, p->offsConfidentiality, p->integrityProtection); + + // trigger read from SecY#0: + cco_macsec_reg_wr(netdev, SECY_CONFIG_BASE_ADDR + SECY_CONFIG_CONFIG_CTRL_BASE_ADDR, + SECY_CONFIG_CONFIG_CTRL_TX_CONFIG_EN_MASK | + SECY_CONFIG_CONFIG_CTRL_RX_CONFIG_EN_MASK | + SECY_CONFIG_CONFIG_CTRL_RD_TRIGGER_MASK); + + val = cco_macsec_reg_rd(netdev, SECY_CONFIG_BASE_ADDR + SECY_CONFIG_TX_CONFIG_BASE_ADDR); + p->maxTxKeys = (val & SECY_CONFIG_TX_CONFIG_MAXTRANSMITKEYS_MASK) >> SECY_CONFIG_TX_CONFIG_MAXTRANSMITKEYS_SHIFT; + p->maxTxChannels = (val & SECY_CONFIG_TX_CONFIG_MAXTRANSMITCHANNELS_MASK) >> SECY_CONFIG_TX_CONFIG_MAXTRANSMITCHANNELS_SHIFT; + + val = cco_macsec_reg_rd(netdev, SECY_CONFIG_BASE_ADDR + SECY_CONFIG_RX_CONFIG_BASE_ADDR); + p->maxRxKeys = (val & SECY_CONFIG_RX_CONFIG_MAXRECEIVEKEYS_MASK) >> SECY_CONFIG_RX_CONFIG_MAXRECEIVEKEYS_SHIFT; + p->maxRxChannels = (val & SECY_CONFIG_RX_CONFIG_MAXRECEIVECHANNELS_MASK) >> SECY_CONFIG_RX_CONFIG_MAXRECEIVECHANNELS_SHIFT; + netdev_info(netdev, "%s: maxTxKeys=%u maxTxChannels=%u maxRxKeys=%u maxRxChannels=%u\n", + __func__, p->maxTxKeys, p->maxTxChannels, p->maxRxKeys, p->maxRxChannels); +} + +int cco_macsec_init(struct net_device *dev) +{ + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(dev); + u32 id, ver; + int err; + const struct macsec_secy secy = {}; + + id = cco_macsec_reg_rd(dev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_IP_ID_BASE_ADDR); + ver = cco_macsec_reg_rd(dev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_IP_VERSION_BASE_ADDR); + netdev_info(dev, "%s: Probe MACsec device: id=0x%08x version=0x%08x\n", __func__, id, ver); + // only major version must match for driver to work: + if (id != CCO_MACSEC_IP_ID || + (ver & MACSEC_CORE_IP_VERSION_MAJOR_MASK) != (CCO_MACSEC_MAJOR_VER << MACSEC_CORE_IP_VERSION_MAJOR_SHIFT)) { + netdev_warn(dev, "%s MACsec device not supported IP_ID=0x%08x version=0x%08x\n", __func__, id, ver); + return -1; + } + + err = genl_register_family(&cco_macsec_fam); + if (err) { + netdev_info(dev, "%s genl_register_family() failed, err=%i\n", __func__, err); + return err; + } + + memset(macsec_priv, 0, sizeof(*macsec_priv)); + get_macsec_capabilities(dev, &macsec_priv->capabilities); + cco_macsec_reg_wr(dev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_GENERAL_CTRL_BASE_ADDR, 0); + cco_macsec_reg_wr(dev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_TX_SC_PN_THRESHOLD_BASE_ADDR, default_txsc_pn_thr); + + // enable common port: + write_secy_txsc(dev, &secy, 0, 1, 0, 0, 0); + + // Write default Traffic Map rules to bypass EAPol messages (EthType 0x888E): + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_MAC_ADDR_0_WR_BASE_ADDR, 0); + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_MAC_ADDR_1_WR_BASE_ADDR, 0); + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_VLAN_WR_BASE_ADDR, 0); + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_ETYPE_WR_BASE_ADDR, ETH_P_PAE); // 0x888E + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_OTHER_WR_BASE_ADDR, 0); + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_SECY_WR_BASE_ADDR, TRAFFIC_MAP_TT_SECY_WR_VAL_MASK); // all ones = bypass + cco_macsec_reg_wr(dev, TRAFFIC_MAP_BASE_ADDR + TRAFFIC_MAP_TT_CTRL_BASE_ADDR, + TRAFFIC_MAP_TT_CTRL_TX_CONFIG_EN_MASK | + TRAFFIC_MAP_TT_CTRL_RX_CONFIG_EN_MASK | + TRAFFIC_MAP_TT_CTRL_WR_TRIGGER_MASK | + (4 << TRAFFIC_MAP_TT_CTRL_FIELD_SELECT_WR_SHIFT) | // 4=EtherType + (0 << TRAFFIC_MAP_TT_CTRL_INDEX_SHIFT)); + + if (debug_sw_macsec) + return 0; + + dev->features |= NETIF_F_HW_MACSEC; + dev->macsec_ops = &cco_macsec_ops; + + return 0; +} + +void cco_macsec_exit(struct net_device *dev) +{ + cco_macsec_reg_wr(dev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_GENERAL_CTRL_BASE_ADDR, 0); + genl_unregister_family(&cco_macsec_fam); +} + +void cco_macsec_commonport_status_update(struct net_device *netdev, u8 operational, u8 enabled) +{ + u32 secy_cnt = count_secy(netdev); + cco_macsec_reg_wr(netdev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_GENERAL_CTRL_BASE_ADDR, + (secy_cnt ? MACSEC_CORE_GENERAL_CTRL_MACSEC_EN_MASK : 0) | + ((operational ? 1 : 0) << MACSEC_CORE_GENERAL_CTRL_COMMONPORT_MAC_OPERATIONAL_SHIFT) | + ((enabled ? 1 : 0) << MACSEC_CORE_GENERAL_CTRL_COMMONPORT_MAC_ENABLED_SHIFT)); +} + +// MACsec interrupt callback (PN exhaustion) +irqreturn_t cco_macsec_isr(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct cco_macsec_priv *macsec_priv = cco_macsec_get_priv(dev); + struct macsec_secy *secy; + struct macsec_tx_sa *tx_sa; + u32 regval; + u8 sa_ix, vlan_in_clear, conf_offs; + u32 secy_index; + + + regval = cco_macsec_reg_rd(dev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_INTERRUPT_BASE_ADDR); + // identify which SecY(s)/Tx SA's is near PN exhaustion: + for (secy_index = 0; secy_index < macsec_priv->capabilities.no_of_secys && secy_index < CCO_MACSEC_SECY_MAX; ++secy_index) { + if (!(regval & ((1 << (secy_index + MACSEC_CORE_INTERRUPT_ALMOST_PN_EXHAUSTION_SHIFT)) | + (1 << (secy_index + MACSEC_CORE_INTERRUPT_PN_EXHAUSTION_SHIFT))))) + continue; + // secy_index has PN exhaustion, validate: + secy = macsec_priv->secy_array[secy_index]; + if (unlikely(!secy)) { + netdev_warn(dev, "PN threshold expired, but secy_index=%u no longer exists", secy_index); + continue; + } + if (macsec_priv->secy_stopped[secy_index] || + !netif_running(secy->netdev) || + !secy->tx_sc.active) { + netdev_warn(dev, "PN threshold expired on down TX SC"); + continue; + } + // get Tx SA in use: + sa_ix = secy->tx_sc.encoding_sa; + tx_sa = rtnl_dereference(secy->tx_sc.sa[sa_ix]); + if (unlikely(!tx_sa)) { + netdev_warn(dev, "PN threshold expired on invalid TX SA"); + continue; + } + netdev_info(dev, "PN threshold expired for secy_index=%u, Tx sa_ix=%u, transitioning to !oper", secy_index, sa_ix); + macsec_pn_wrapped((struct macsec_secy *)secy, tx_sa); + // Configure Tx-SA registers (disable): + cco_macsec_reg_wr(dev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_AN_BASE_ADDR, + (sa_ix << TRANSMITSA_TX_SA_AN_VAL_SHIFT)); + cco_macsec_reg_wr(dev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_KEY_INDEX_WR_BASE_ADDR, 0); + cco_macsec_reg_wr(dev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_TRANSMITSA_CFG_BASE_ADDR, 0); + cco_macsec_reg_wr(dev, TRANSMITSA_BASE_ADDR + TRANSMITSA_TX_SA_CTRL_BASE_ADDR, + (secy_index << TRANSMITSA_TX_SA_CTRL_SECY_INDEX_SHIFT) | + TRANSMITSA_TX_SA_CTRL_WR_TRIGGER_MASK); + macsec_priv->sa_enabled[secy_index][0] &= ~(0x10 << sa_ix); + macsec_priv->txsa_ext[secy_index][sa_ix].stoppedTime = jiffies; + if (secy->tx_sc.encoding_sa == sa_ix) { + // Tx-SA used by Tx-SC becomes inactive, so Tx-SC is no longer active: + macsec_priv->txsc_ext[secy_index].stoppedTime = jiffies; + vlan_in_clear = macsec_priv->secy_vlan_in_clear[secy_index]; + conf_offs = macsec_priv->secy_confidentiality_offs[secy_index]; + write_secy_txsc(dev, secy, secy_index, 0, 0, vlan_in_clear, conf_offs); + } + } + // clear all interrupts handled: + cco_macsec_reg_wr(dev, MACSEC_CORE_BASE_ADDR + MACSEC_CORE_INTERRUPT_BASE_ADDR, regval); + return IRQ_HANDLED; +} diff --git a/drivers/net/ethernet/adi/macsec/cco_macsec.h b/drivers/net/ethernet/adi/macsec/cco_macsec.h new file mode 100644 index 00000000000000..be766d1a746187 --- /dev/null +++ b/drivers/net/ethernet/adi/macsec/cco_macsec.h @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023-2025, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef _CCO_MACSEC_H_ +#define _CCO_MACSEC_H_ + +#include "cco_regdefs.h" + +#include +#include +#include +#include +#include + +#if IS_ENABLED(CONFIG_KUNIT_TEST) +#define netdev_info(dev, fmt, ...) printk(KERN_NOTICE, fmt, ##__VA_ARGS__) +#define netdev_warn(dev, fmt, ...) printk(KERN_WARNING, fmt, ##__VA_ARGS__) +#define netdev_err(dev, fmt, ...) printk(KERN_ERROR, fmt, ##__VA_ARGS__) +#endif + +// Maximum number of MACsec SecY's supported +#define CCO_MACSEC_SECY_MAX 16 + +// Maximum number of MACsec Rx channels supported (per SecY) +#define CCO_MACSEC_RXSC_MAX 32 + +// Maximum number of MACsec keys supported +#define CCO_MACSEC_KEYS 64 + +// MACsec key use (unused, Rx, Tx or both) +#define CCO_MACSEC_KEY_RX 1 +#define CCO_MACSEC_KEY_TX 2 + +struct cco_macsec_priv { + // MACsec capabilities + struct cco_macsec_capabilities capabilities; + + // MACsec key ID table + u8 key_id_table[CCO_MACSEC_KEYS][MACSEC_KEYID_LEN]; + + // MACsec key use (unavailable, Rx, Tx or both) + u8 key_use[CCO_MACSEC_KEYS]; + + // MACsec key reference count + u8 key_refcnt[CCO_MACSEC_KEYS]; + + // Array of configured SecYs + struct macsec_secy *secy_array[CCO_MACSEC_SECY_MAX]; + + // 1 if SecY has been stopped (via mdo_dev_stop) + u8 secy_stopped[CCO_MACSEC_SECY_MAX]; + + // vlan-in-clear setting for SecY + u8 secy_vlan_in_clear[CCO_MACSEC_SECY_MAX]; + + // Number of initial octets of each MSDU without confidentiality protection per SecY + u8 secy_confidentiality_offs[CCO_MACSEC_SECY_MAX]; + + // SecY ifIndex + u32 secy_ifIndex[CCO_MACSEC_SECY_MAX]; + + // Array of configured Rx-SCs per SecY + struct macsec_rx_sc *rxsc_array[CCO_MACSEC_SECY_MAX][CCO_MACSEC_RXSC_MAX]; + + // Tx/Rx-SA enabled array + u8 sa_enabled[CCO_MACSEC_SECY_MAX][CCO_MACSEC_RXSC_MAX]; + + // createdTime, startedTime, stoppedTime: + struct cco_macsec_time_stats txsc_ext[CCO_MACSEC_SECY_MAX]; + struct cco_macsec_time_stats rxsc_ext[CCO_MACSEC_SECY_MAX][CCO_MACSEC_RXSC_MAX]; + struct cco_macsec_time_stats txsa_ext[CCO_MACSEC_SECY_MAX][MACSEC_NUM_AN]; + struct cco_macsec_time_stats rxsa_ext[CCO_MACSEC_SECY_MAX][CCO_MACSEC_RXSC_MAX][MACSEC_NUM_AN]; + + // stats: + struct macsec_dev_stats dev_stats[CCO_MACSEC_SECY_MAX]; + struct macsec_tx_sc_stats txsc_stats[CCO_MACSEC_SECY_MAX]; + struct macsec_rx_sc_stats rxsc_stats[CCO_MACSEC_SECY_MAX][CCO_MACSEC_RXSC_MAX]; + struct cco_macsec_port_stats port_stats[CCO_MACSEC_SECY_MAX]; + struct cco_macsec_port_stats uport_stats[CCO_MACSEC_SECY_MAX]; + struct cco_macsec_ext_port_stats ext_port_stats[CCO_MACSEC_SECY_MAX]; +}; + +// provided by the Ethernet device driver module: +struct cco_macsec_priv* cco_macsec_get_priv(struct net_device *netdev); +void cco_macsec_reg_wr(struct net_device *netdev, unsigned long addr, u32 value); +u32 cco_macsec_reg_rd(struct net_device *netdev, unsigned long addr); +void cco_macsec_max_framesize_get(struct net_device *netdev, u32* max_framesize); + +// called from the Ethernet device driver module: +int cco_macsec_init(struct net_device *dev); +void cco_macsec_exit(struct net_device *dev); +void cco_macsec_commonport_status_update(struct net_device *netdev, u8 operational, u8 enabled); +irqreturn_t cco_macsec_isr(int irq, void *dev_id); + +#endif /* _CCO_MACSEC_H_ */ diff --git a/drivers/net/ethernet/adi/macsec/cco_macseccore_memmap.h b/drivers/net/ethernet/adi/macsec/cco_macseccore_memmap.h new file mode 100644 index 00000000000000..f83102228d7a29 --- /dev/null +++ b/drivers/net/ethernet/adi/macsec/cco_macseccore_memmap.h @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023-2025, Analog Devices Incorporated, All Rights Reserved + */ +/******************************************************************************/ +/* DO NOT MODIFY */ +/* THIS FILE IS AUTOGENERATED AND ALL CHANGES WILL BE LOST */ +/******************************************************************************/ +#ifndef _MACSEC_TOP_MACSEC_CORE_MEMMAP_H_ +#define _MACSEC_TOP_MACSEC_CORE_MEMMAP_H_ + +/* Top level register bank */ +#define MACSEC_CORE_BASE_ADDR 0x00000000 +#define MACSEC_CORE_STRIDE 0x00000100 +/******************************************************************************/ +/* Unique IP ID */ +#define MACSEC_CORE_IP_ID_BASE_ADDR 0x00000000 +/* IP_ID RO_STATIC IP ID */ +#define MACSEC_CORE_IP_ID_IP_ID_MASK 0xffffffff +#define MACSEC_CORE_IP_ID_IP_ID_SHIFT 0 + +/******************************************************************************/ +/* IP version */ +#define MACSEC_CORE_IP_VERSION_BASE_ADDR 0x00000004 +/* MAJOR RO_STATIC Incrementing version number */ +#define MACSEC_CORE_IP_VERSION_MAJOR_MASK 0xffff0000 +#define MACSEC_CORE_IP_VERSION_MAJOR_SHIFT 16 + +/* REVISION RO_STATIC Incrementing revision number */ +#define MACSEC_CORE_IP_VERSION_REVISION_MASK 0x0000ffff +#define MACSEC_CORE_IP_VERSION_REVISION_SHIFT 0 + +/******************************************************************************/ +/* Static IP configuration - Core */ +#define MACSEC_CORE_IP_CAPABILITIES_1_BASE_ADDR 0x00000008 +/* NO_OF_PEERS RO_STATIC Maximum number of peers per CA. */ +#define MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_PEERS_MASK 0xff000000 +#define MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_PEERS_SHIFT 24 + +/* NO_OF_CS_ENTRIES_RX RO_STATIC Maximum number of supported ciphersuites in ingress. */ +#define MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_CS_ENTRIES_RX_MASK 0x00ff0000 +#define MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_CS_ENTRIES_RX_SHIFT 16 + +/* NO_OF_CS_ENTRIES_TX RO_STATIC Maximum number of supported ciphersuites in egress. */ +#define MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_CS_ENTRIES_TX_MASK 0x0000ff00 +#define MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_CS_ENTRIES_TX_SHIFT 8 + +/* NO_OF_SECYS RO_STATIC Maximum number of virtual ports/SecYs instantiated. */ +#define MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_SECYS_MASK 0x000000ff +#define MACSEC_CORE_IP_CAPABILITIES_1_NO_OF_SECYS_SHIFT 0 + +/******************************************************************************/ +/* Static IP configuration - Core */ +#define MACSEC_CORE_IP_CAPABILITIES_2_BASE_ADDR 0x0000000c +/* CONFIDENTIALITY_OFFSET RO_STATIC Confidentiality offset per SecY */ +#define MACSEC_CORE_IP_CAPABILITIES_2_CONFIDENTIALITY_OFFSET_MASK 0x0f000000 +#define MACSEC_CORE_IP_CAPABILITIES_2_CONFIDENTIALITY_OFFSET_SHIFT 24 + +/* AVAILABLE_CIPHERSUITES RO_STATIC Available Cipher Suites + * [0] - AES-GCM-128 + * [1] - AES-GCM-256 + * [2] - AES-GCM-XPN-128 + * [3] - AES-GCM-XPN-256 */ +#define MACSEC_CORE_IP_CAPABILITIES_2_AVAILABLE_CIPHERSUITES_MASK 0x00f00000 +#define MACSEC_CORE_IP_CAPABILITIES_2_AVAILABLE_CIPHERSUITES_SHIFT 20 + +/* VLAN_IN_CLEAR RO_STATIC Support for Vlan in clear */ +#define MACSEC_CORE_IP_CAPABILITIES_2_VLAN_IN_CLEAR_MASK 0x000f0000 +#define MACSEC_CORE_IP_CAPABILITIES_2_VLAN_IN_CLEAR_SHIFT 16 + +/* NO_TT_ENTRIES_RX RO_STATIC Maximum number of rules for traffic mapping table in ingress. */ +#define MACSEC_CORE_IP_CAPABILITIES_2_NO_TT_ENTRIES_RX_MASK 0x0000ff00 +#define MACSEC_CORE_IP_CAPABILITIES_2_NO_TT_ENTRIES_RX_SHIFT 8 + +/* NO_TT_ENTRIES_TX RO_STATIC Maximum number of rules for traffic mapping table in egress. */ +#define MACSEC_CORE_IP_CAPABILITIES_2_NO_TT_ENTRIES_TX_MASK 0x000000ff +#define MACSEC_CORE_IP_CAPABILITIES_2_NO_TT_ENTRIES_TX_SHIFT 0 + +/******************************************************************************/ +/* AES-GCM-128 Cipher Suite: + * A globally unique 64-bit (EUI-64) identifier + * 31 downto 0 */ +#define MACSEC_CORE_IP_AES_GCM_128_CS_IDENTIFIER_0_BASE_ADDR 0x00000010 +/* VAL RO_STATIC ---- */ +#define MACSEC_CORE_IP_AES_GCM_128_CS_IDENTIFIER_0_VAL_MASK 0xffffffff +#define MACSEC_CORE_IP_AES_GCM_128_CS_IDENTIFIER_0_VAL_SHIFT 0 + +/******************************************************************************/ +/* AES-GCM-128 Cipher Suite: + * A globally unique 64-bit (EUI-64) identifier + * 63 downto 32 */ +#define MACSEC_CORE_IP_AES_GCM_128_CS_IDENTIFIER_1_BASE_ADDR 0x00000014 +/* VAL RO_STATIC ---- */ +#define MACSEC_CORE_IP_AES_GCM_128_CS_IDENTIFIER_1_VAL_MASK 0xffffffff +#define MACSEC_CORE_IP_AES_GCM_128_CS_IDENTIFIER_1_VAL_SHIFT 0 + +/******************************************************************************/ +/* AES-GCM-256 Cipher Suite: + * A globally unique 64-bit (EUI-64) identifier + * 31 downto 0 */ +#define MACSEC_CORE_IP_AES_GCM_256_CS_IDENTIFIER_0_BASE_ADDR 0x00000018 +/* VAL RO_STATIC ---- */ +#define MACSEC_CORE_IP_AES_GCM_256_CS_IDENTIFIER_0_VAL_MASK 0xffffffff +#define MACSEC_CORE_IP_AES_GCM_256_CS_IDENTIFIER_0_VAL_SHIFT 0 + +/******************************************************************************/ +/* AES-GCM-256 Cipher Suite: + * A globally unique 64-bit (EUI-64) identifier + * 63 downto 32 */ +#define MACSEC_CORE_IP_AES_GCM_256_CS_IDENTIFIER_1_BASE_ADDR 0x0000001c +/* VAL RO_STATIC ---- */ +#define MACSEC_CORE_IP_AES_GCM_256_CS_IDENTIFIER_1_VAL_MASK 0xffffffff +#define MACSEC_CORE_IP_AES_GCM_256_CS_IDENTIFIER_1_VAL_SHIFT 0 + +/******************************************************************************/ +/* Cipher Suite capabilities */ +#define MACSEC_CORE_IP_CS_CAPABILITY_BASE_ADDR 0x00000020 +/* ICVLENGTH RO_STATIC Cipher Suite capability: + * Number of octets in the ICV */ +#define MACSEC_CORE_IP_CS_CAPABILITY_ICVLENGTH_MASK 0x000001f0 +#define MACSEC_CORE_IP_CS_CAPABILITY_ICVLENGTH_SHIFT 4 + +/* CHANGESDATALENGTH RO_STATIC Cipher Suite capability: + * True if the data length is changed */ +#define MACSEC_CORE_IP_CS_CAPABILITY_CHANGESDATALENGTH_MASK 0x00000008 +#define MACSEC_CORE_IP_CS_CAPABILITY_CHANGESDATALENGTH_SHIFT 3 + +/* OFFSETCONFIDENTIALITY RO_STATIC Cipher Suite capability: + * True if a selectable offset for confidentiality can be provided */ +#define MACSEC_CORE_IP_CS_CAPABILITY_OFFSETCONFIDENTIALITY_MASK 0x00000004 +#define MACSEC_CORE_IP_CS_CAPABILITY_OFFSETCONFIDENTIALITY_SHIFT 2 + +/* CONFIDENTIALITYPROTECTION RO_STATIC Cipher Suite capability: + * True if confidentiality with integrity protection can be provided */ +#define MACSEC_CORE_IP_CS_CAPABILITY_CONFIDENTIALITYPROTECTION_MASK 0x00000002 +#define MACSEC_CORE_IP_CS_CAPABILITY_CONFIDENTIALITYPROTECTION_SHIFT 1 + +/* INTEGRITYPROTECTION RO_STATIC Cipher Suite capability: + * True if integrity protection without confidentiality can be provided */ +#define MACSEC_CORE_IP_CS_CAPABILITY_INTEGRITYPROTECTION_MASK 0x00000001 +#define MACSEC_CORE_IP_CS_CAPABILITY_INTEGRITYPROTECTION_SHIFT 0 + +/******************************************************************************/ +/* General core control */ +#define MACSEC_CORE_GENERAL_CTRL_BASE_ADDR 0x00000024 +/* COMMONPORT_MAC_OPERATIONAL RW Common Port MAC_Operational status insert */ +#define MACSEC_CORE_GENERAL_CTRL_COMMONPORT_MAC_OPERATIONAL_MASK 0x00000008 +#define MACSEC_CORE_GENERAL_CTRL_COMMONPORT_MAC_OPERATIONAL_SHIFT 3 + +/* COMMONPORT_MAC_ENABLED RW Common Port MAC_Enabled status insert */ +#define MACSEC_CORE_GENERAL_CTRL_COMMONPORT_MAC_ENABLED_MASK 0x00000004 +#define MACSEC_CORE_GENERAL_CTRL_COMMONPORT_MAC_ENABLED_SHIFT 2 + +/* MACSEC_EN RW Enable MACsec protection using Aggregated Controlled Port + * Disable MACsec: all traffic through Uncontrolled Port + * Enable MACsec: traffic according to Traffic Mapping Table for Controlled and Uncontrolled Port */ +#define MACSEC_CORE_GENERAL_CTRL_MACSEC_EN_MASK 0x00000002 +#define MACSEC_CORE_GENERAL_CTRL_MACSEC_EN_SHIFT 1 + +/* SOFT_RESET_ALL RW Software reset register for all domains, ACTIVE HIGH software reset */ +#define MACSEC_CORE_GENERAL_CTRL_SOFT_RESET_ALL_MASK 0x00000001 +#define MACSEC_CORE_GENERAL_CTRL_SOFT_RESET_ALL_SHIFT 0 + +/******************************************************************************/ +/* Transmit PN threshold for almost PN exhaustion interrupt */ +#define MACSEC_CORE_TX_SC_PN_THRESHOLD_BASE_ADDR 0x00000028 +/* VAL RW ---- */ +#define MACSEC_CORE_TX_SC_PN_THRESHOLD_VAL_MASK 0xffffffff +#define MACSEC_CORE_TX_SC_PN_THRESHOLD_VAL_SHIFT 0 + +/******************************************************************************/ +/* General core interrupt */ +#define MACSEC_CORE_INTERRUPT_BASE_ADDR 0x0000002c +/* PN_EXHAUSTION W1C pn_exhaustion interrupt */ +#define MACSEC_CORE_INTERRUPT_PN_EXHAUSTION_MASK 0xffff0000 +#define MACSEC_CORE_INTERRUPT_PN_EXHAUSTION_SHIFT 16 + +/* ALMOST_PN_EXHAUSTION W1C almost_pn_exhaustion interrupt */ +#define MACSEC_CORE_INTERRUPT_ALMOST_PN_EXHAUSTION_MASK 0x0000ffff +#define MACSEC_CORE_INTERRUPT_ALMOST_PN_EXHAUSTION_SHIFT 0 + +#endif /* _MACSEC_TOP_MACSEC_CORE_MEMMAP_H_ */ diff --git a/drivers/net/ethernet/adi/macsec/cco_receivesa_memmap.h b/drivers/net/ethernet/adi/macsec/cco_receivesa_memmap.h new file mode 100644 index 00000000000000..bc521107f63076 --- /dev/null +++ b/drivers/net/ethernet/adi/macsec/cco_receivesa_memmap.h @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023-2025, Analog Devices Incorporated, All Rights Reserved + */ +/******************************************************************************/ +/* DO NOT MODIFY */ +/* THIS FILE IS AUTOGENERATED AND ALL CHANGES WILL BE LOST */ +/******************************************************************************/ +#ifndef _MACSEC_TOP_RECEIVESA_MEMMAP_H_ +#define _MACSEC_TOP_RECEIVESA_MEMMAP_H_ + +/* receiveAssociations */ +#define RECEIVESA_BASE_ADDR 0x00000600 +#define RECEIVESA_STRIDE 0x00000100 +/******************************************************************************/ +/* Control and status indicator for rule / configuration insert */ +#define RECEIVESA_RX_SA_CTRL_BASE_ADDR 0x00000000 +/* UPDATE_PN_TRIGGER W1C Trigger a PN update operation */ +#define RECEIVESA_RX_SA_CTRL_UPDATE_PN_TRIGGER_MASK 0x00001000 +#define RECEIVESA_RX_SA_CTRL_UPDATE_PN_TRIGGER_SHIFT 12 + +/* RD_TRIGGER W1C Trigger a read operation */ +#define RECEIVESA_RX_SA_CTRL_RD_TRIGGER_MASK 0x00000800 +#define RECEIVESA_RX_SA_CTRL_RD_TRIGGER_SHIFT 11 + +/* WR_TRIGGER W1C Trigger a write operation */ +#define RECEIVESA_RX_SA_CTRL_WR_TRIGGER_MASK 0x00000400 +#define RECEIVESA_RX_SA_CTRL_WR_TRIGGER_SHIFT 10 + +/* PEER_INDEX RW Index number of secy table associated to the peer to read/write peer SA */ +#define RECEIVESA_RX_SA_CTRL_PEER_INDEX_MASK 0x000003f0 +#define RECEIVESA_RX_SA_CTRL_PEER_INDEX_SHIFT 4 + +/* SECY_INDEX RW Index number of SecY associated to the peer to read/write peer SA */ +#define RECEIVESA_RX_SA_CTRL_SECY_INDEX_MASK 0x0000000f +#define RECEIVESA_RX_SA_CTRL_SECY_INDEX_SHIFT 0 + +/******************************************************************************/ +/* Receive SA ID: + * association number for the SA */ +#define RECEIVESA_RX_SA_AN_BASE_ADDR 0x00000004 +/* VAL RW ---- */ +#define RECEIVESA_RX_SA_AN_VAL_MASK 0x00000003 +#define RECEIVESA_RX_SA_AN_VAL_SHIFT 0 + +/******************************************************************************/ +/* Receive SA creation: + * Initial value of nextPN */ +#define RECEIVESA_RX_SA_NEXTPN_BASE_ADDR 0x00000008 +/* VAL RW ---- */ +#define RECEIVESA_RX_SA_NEXTPN_VAL_MASK 0xffffffff +#define RECEIVESA_RX_SA_NEXTPN_VAL_SHIFT 0 + +/******************************************************************************/ +/* Receive SA creation: + * Lowest acceptable PN value for a received frame */ +#define RECEIVESA_RX_SA_LOWESTPN_BASE_ADDR 0x0000000c +/* VAL RW ---- */ +#define RECEIVESA_RX_SA_LOWESTPN_VAL_MASK 0xffffffff +#define RECEIVESA_RX_SA_LOWESTPN_VAL_SHIFT 0 + +/******************************************************************************/ +/* Receive SA creation: + * A reference to an SAK that is unchanged for the life of the SA */ +#define RECEIVESA_RX_SA_KEY_INDEX_WR_BASE_ADDR 0x00000010 +/* VAL RW ---- */ +#define RECEIVESA_RX_SA_KEY_INDEX_WR_VAL_MASK 0x00000fff +#define RECEIVESA_RX_SA_KEY_INDEX_WR_VAL_SHIFT 0 + +/******************************************************************************/ +/* Receive SA status: + * A reference to an SAK that is unchanged for the life of the SA */ +#define RECEIVESA_RX_SA_KEY_INDEX_RD_BASE_ADDR 0x00000014 +/* VAL RO ---- */ +#define RECEIVESA_RX_SA_KEY_INDEX_RD_VAL_MASK 0x00000fff +#define RECEIVESA_RX_SA_KEY_INDEX_RD_VAL_SHIFT 0 + +/******************************************************************************/ +/* Receive SA creation and status insert: + * If the Current Cipher Suite uses extended packet numbering, + * the KaY also supplies the SSCI for the SA */ +#define RECEIVESA_RX_SA_SSCI_WR_BASE_ADDR 0x00000018 +/* VAL RW For XPN cipher suites */ +#define RECEIVESA_RX_SA_SSCI_WR_VAL_MASK 0xffffffff +#define RECEIVESA_RX_SA_SSCI_WR_VAL_SHIFT 0 + +/******************************************************************************/ +/* Receive SA creation and status read: + * If the Current Cipher Suite uses extended packet numbering, + * the KaY also supplies the SSCI for the SA */ +#define RECEIVESA_RX_SA_SSCI_RD_BASE_ADDR 0x0000001c +/* VAL RO For XPN cipher suites */ +#define RECEIVESA_RX_SA_SSCI_RD_VAL_MASK 0xffffffff +#define RECEIVESA_RX_SA_SSCI_RD_VAL_SHIFT 0 + +/******************************************************************************/ +/* Receive SA control */ +#define RECEIVESA_RX_SA_UPDTNEXTPN_BASE_ADDR 0x00000020 +/* VAL RW ---- */ +#define RECEIVESA_RX_SA_UPDTNEXTPN_VAL_MASK 0xffffffff +#define RECEIVESA_RX_SA_UPDTNEXTPN_VAL_SHIFT 0 + +/******************************************************************************/ +/* Receive SA control */ +#define RECEIVESA_RX_SA_UPDTLOWESTPN_BASE_ADDR 0x00000024 +/* VAL RW ---- */ +#define RECEIVESA_RX_SA_UPDTLOWESTPN_VAL_MASK 0xffffffff +#define RECEIVESA_RX_SA_UPDTLOWESTPN_VAL_SHIFT 0 + +/******************************************************************************/ +/* Receive SA status and configuration */ +#define RECEIVESA_RX_SA_RECEIVESA_CFG_BASE_ADDR 0x00000028 +/* ENABLERECEIVE_RD RO Receive SA control read: + * When the SA is created, enableReceive and inUse are False + * and the SA cannot be used to receive frames. The SA shall + * be able to receive, and inUse shall be True, when enableReceive + * is set. The SA shall stop receiving, and inUse shall be False, + * when enableReceive is reset. */ +#define RECEIVESA_RX_SA_RECEIVESA_CFG_ENABLERECEIVE_RD_MASK 0x00000002 +#define RECEIVESA_RX_SA_RECEIVESA_CFG_ENABLERECEIVE_RD_SHIFT 1 + +/* ENABLERECEIVE_WR RW Receive SA control insert: + * When the SA is created, enableReceive and inUse are False + * and the SA cannot be used to receive frames. The SA shall + * be able to receive, and inUse shall be True, when enableReceive + * is set. The SA shall stop receiving, and inUse shall be False, + * when enableReceive is reset. */ +#define RECEIVESA_RX_SA_RECEIVESA_CFG_ENABLERECEIVE_WR_MASK 0x00000001 +#define RECEIVESA_RX_SA_RECEIVESA_CFG_ENABLERECEIVE_WR_SHIFT 0 + +/******************************************************************************/ +/* Receive SC ID to read + * Each SC has a unique SCI comprising a 48-bit MAC address + * concatenated with a 16-bit Port Identifier + * 31 downto 0 */ +#define RECEIVESA_RX_SA_SCI_0_RD_BASE_ADDR 0x0000002c +/* VAL RO ---- */ +#define RECEIVESA_RX_SA_SCI_0_RD_VAL_MASK 0xffffffff +#define RECEIVESA_RX_SA_SCI_0_RD_VAL_SHIFT 0 + +/******************************************************************************/ +/* Receive SC ID to read + * Each SC has a unique SCI comprising a 48-bit MAC address + * concatenated with a 16-bit Port Identifier + * 63 downto 32 */ +#define RECEIVESA_RX_SA_SCI_1_RD_BASE_ADDR 0x00000030 +/* VAL RO ---- */ +#define RECEIVESA_RX_SA_SCI_1_RD_VAL_MASK 0xffffffff +#define RECEIVESA_RX_SA_SCI_1_RD_VAL_SHIFT 0 + +#endif /* _MACSEC_TOP_RECEIVESA_MEMMAP_H_ */ diff --git a/drivers/net/ethernet/adi/macsec/cco_receivesc_memmap.h b/drivers/net/ethernet/adi/macsec/cco_receivesc_memmap.h new file mode 100644 index 00000000000000..a73ed13d48db96 --- /dev/null +++ b/drivers/net/ethernet/adi/macsec/cco_receivesc_memmap.h @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023-2025, Analog Devices Incorporated, All Rights Reserved + */ +/******************************************************************************/ +/* DO NOT MODIFY */ +/* THIS FILE IS AUTOGENERATED AND ALL CHANGES WILL BE LOST */ +/******************************************************************************/ +#ifndef _MACSEC_TOP_RECEIVESC_MEMMAP_H_ +#define _MACSEC_TOP_RECEIVESC_MEMMAP_H_ + +/* receiveChannels */ +#define RECEIVESC_BASE_ADDR 0x00000500 +#define RECEIVESC_STRIDE 0x00000100 +/******************************************************************************/ +/* Control and status indicator for rule / configuration insert */ +#define RECEIVESC_RX_SC_CTRL_BASE_ADDR 0x00000000 +/* RD_TRIGGER W1C Trigger a read operation */ +#define RECEIVESC_RX_SC_CTRL_RD_TRIGGER_MASK 0x00000800 +#define RECEIVESC_RX_SC_CTRL_RD_TRIGGER_SHIFT 11 + +/* WR_TRIGGER W1C Trigger a write operation */ +#define RECEIVESC_RX_SC_CTRL_WR_TRIGGER_MASK 0x00000400 +#define RECEIVESC_RX_SC_CTRL_WR_TRIGGER_SHIFT 10 + +/* PEER_INDEX RW Index number of secy table to read/write peer SCI */ +#define RECEIVESC_RX_SC_CTRL_PEER_INDEX_MASK 0x000003f0 +#define RECEIVESC_RX_SC_CTRL_PEER_INDEX_SHIFT 4 + +/* SECY_INDEX RW Index number of SecY to read/write peer SCI */ +#define RECEIVESC_RX_SC_CTRL_SECY_INDEX_MASK 0x0000000f +#define RECEIVESC_RX_SC_CTRL_SECY_INDEX_SHIFT 0 + +/******************************************************************************/ +/* Receive SC ID to insert + * Each SC has a unique SCI comprising a 48-bit MAC address + * concatenated with a 16-bit Port Identifier + * 31 downto 0 */ +#define RECEIVESC_RX_SC_SCI_0_WR_BASE_ADDR 0x00000004 +/* VAL RW ---- */ +#define RECEIVESC_RX_SC_SCI_0_WR_VAL_MASK 0xffffffff +#define RECEIVESC_RX_SC_SCI_0_WR_VAL_SHIFT 0 + +/******************************************************************************/ +/* Receive SC ID to insert + * Each SC has a unique SCI comprising a 48-bit MAC address + * concatenated with a 16-bit Port Identifier + * 63 downto 32 */ +#define RECEIVESC_RX_SC_SCI_1_WR_BASE_ADDR 0x00000008 +/* VAL RW ---- */ +#define RECEIVESC_RX_SC_SCI_1_WR_VAL_MASK 0xffffffff +#define RECEIVESC_RX_SC_SCI_1_WR_VAL_SHIFT 0 + +/******************************************************************************/ +/* Receive SC ID to read + * Each SC has a unique SCI comprising a 48-bit MAC address + * concatenated with a 16-bit Port Identifier + * 31 downto 0 */ +#define RECEIVESC_RX_SC_SCI_0_RD_BASE_ADDR 0x0000000c +/* VAL RO ---- */ +#define RECEIVESC_RX_SC_SCI_0_RD_VAL_MASK 0xffffffff +#define RECEIVESC_RX_SC_SCI_0_RD_VAL_SHIFT 0 + +/******************************************************************************/ +/* Receive SC ID to read + * Each SC has a unique SCI comprising a 48-bit MAC address + * concatenated with a 16-bit Port Identifier + * 63 downto 32 */ +#define RECEIVESC_RX_SC_SCI_1_RD_BASE_ADDR 0x00000010 +/* VAL RO ---- */ +#define RECEIVESC_RX_SC_SCI_1_RD_VAL_MASK 0xffffffff +#define RECEIVESC_RX_SC_SCI_1_RD_VAL_SHIFT 0 + +#endif /* _MACSEC_TOP_RECEIVESC_MEMMAP_H_ */ diff --git a/drivers/net/ethernet/adi/macsec/cco_regdefs.h b/drivers/net/ethernet/adi/macsec/cco_regdefs.h new file mode 100644 index 00000000000000..cf4b42bb2cd571 --- /dev/null +++ b/drivers/net/ethernet/adi/macsec/cco_regdefs.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023-2025, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef _REGDEFS_H_ +#define _REGDEFS_H_ + +#include "cco_ciphersuite_memmap.h" +#include "cco_macseccore_memmap.h" +#include "cco_receivesa_memmap.h" +#include "cco_receivesc_memmap.h" +#include "cco_secy_config_memmap.h" +#include "cco_statistics_memmap.h" +#include "cco_status_memmap.h" +#include "cco_traffic_map_memmap.h" +#include "cco_transmitsa_memmap.h" +#include "cco_transmitsc_memmap.h" + + /* IP_ID should be 0x4d435343 "MCSC" */ +#define CCO_MACSEC_IP_ID 0x4d435343 // "MCSC" +#define CCO_MACSEC_MAJOR_VER 13 + +#endif // _REGDEFS_H_ diff --git a/drivers/net/ethernet/adi/macsec/cco_secy_config_memmap.h b/drivers/net/ethernet/adi/macsec/cco_secy_config_memmap.h new file mode 100644 index 00000000000000..3703985f4e3d3f --- /dev/null +++ b/drivers/net/ethernet/adi/macsec/cco_secy_config_memmap.h @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023-2025, Analog Devices Incorporated, All Rights Reserved + */ +/******************************************************************************/ +/* DO NOT MODIFY */ +/* THIS FILE IS AUTOGENERATED AND ALL CHANGES WILL BE LOST */ +/******************************************************************************/ +#ifndef _MACSEC_TOP_SECY_CONFIG_MEMMAP_H_ +#define _MACSEC_TOP_SECY_CONFIG_MEMMAP_H_ + +/* Configuration registers per SecY */ +#define SECY_CONFIG_BASE_ADDR 0x00000100 +#define SECY_CONFIG_STRIDE 0x00000100 +/******************************************************************************/ +/* Control and status indicator for configuration insert */ +#define SECY_CONFIG_CONFIG_CTRL_BASE_ADDR 0x00000000 +/* CIPHERSUITE_CONFIG_EN RW Select Ciphersuite registers */ +#define SECY_CONFIG_CONFIG_CTRL_CIPHERSUITE_CONFIG_EN_MASK 0x00000200 +#define SECY_CONFIG_CONFIG_CTRL_CIPHERSUITE_CONFIG_EN_SHIFT 9 + +/* RX_CONFIG_EN RW Select Frame Verification and Validation registers */ +#define SECY_CONFIG_CONFIG_CTRL_RX_CONFIG_EN_MASK 0x00000100 +#define SECY_CONFIG_CONFIG_CTRL_RX_CONFIG_EN_SHIFT 8 + +/* TX_CONFIG_EN RW Select Frame Generation and Protection registers */ +#define SECY_CONFIG_CONFIG_CTRL_TX_CONFIG_EN_MASK 0x00000080 +#define SECY_CONFIG_CONFIG_CTRL_TX_CONFIG_EN_SHIFT 7 + +/* PORT_CONFIG_EN RW Select Provided Interface registers */ +#define SECY_CONFIG_CONFIG_CTRL_PORT_CONFIG_EN_MASK 0x00000040 +#define SECY_CONFIG_CONFIG_CTRL_PORT_CONFIG_EN_SHIFT 6 + +/* RD_TRIGGER W1C Trigger a read operation */ +#define SECY_CONFIG_CONFIG_CTRL_RD_TRIGGER_MASK 0x00000020 +#define SECY_CONFIG_CONFIG_CTRL_RD_TRIGGER_SHIFT 5 + +/* WR_TRIGGER W1C Trigger a write operation */ +#define SECY_CONFIG_CONFIG_CTRL_WR_TRIGGER_MASK 0x00000010 +#define SECY_CONFIG_CONFIG_CTRL_WR_TRIGGER_SHIFT 4 + +/* SECY_INDEX RW Index number of SecY to read/write */ +#define SECY_CONFIG_CONFIG_CTRL_SECY_INDEX_MASK 0x0000000f +#define SECY_CONFIG_CONFIG_CTRL_SECY_INDEX_SHIFT 0 + +/******************************************************************************/ +/* Provided Interface registers for controlled port: + * 1) ControlledPortEnabled + * 2) vlan-in-clear + * 3) max_frame_lenght */ +#define SECY_CONFIG_PORT_CONFIG_BASE_ADDR 0x00000004 +/* VLANINCLEAR_RD RO Status of vlan-in-clear enable for SecY */ +#define SECY_CONFIG_PORT_CONFIG_VLANINCLEAR_RD_MASK 0x00000008 +#define SECY_CONFIG_PORT_CONFIG_VLANINCLEAR_RD_SHIFT 3 + +/* VLANINCLEAR_WR RW vlan-in-clear enable for SecY */ +#define SECY_CONFIG_PORT_CONFIG_VLANINCLEAR_WR_MASK 0x00000004 +#define SECY_CONFIG_PORT_CONFIG_VLANINCLEAR_WR_SHIFT 2 + +/* CONTROLLEDPORTENABLED_RD RO Controlled Port control read: + * False: the KaY can prohibit use of the Controlled Port until the secure + * connectivity required has been configured */ +#define SECY_CONFIG_PORT_CONFIG_CONTROLLEDPORTENABLED_RD_MASK 0x00000002 +#define SECY_CONFIG_PORT_CONFIG_CONTROLLEDPORTENABLED_RD_SHIFT 1 + +/* CONTROLLEDPORTENABLED_WR RW Controlled Port control insert: + * False: the KaY can prohibit use of the Controlled Port until the secure + * connectivity required has been configured */ +#define SECY_CONFIG_PORT_CONFIG_CONTROLLEDPORTENABLED_WR_MASK 0x00000001 +#define SECY_CONFIG_PORT_CONFIG_CONTROLLEDPORTENABLED_WR_SHIFT 0 + +/******************************************************************************/ +/* Frame Generation and Protection read registers */ +#define SECY_CONFIG_TX_CONFIG_BASE_ADDR 0x00000008 +/* USESCB_RD RO Frame generation controls read: + * True or False, with a default of False */ +#define SECY_CONFIG_TX_CONFIG_USESCB_RD_MASK 0x00020000 +#define SECY_CONFIG_TX_CONFIG_USESCB_RD_SHIFT 17 + +/* USESCB_WR RW Frame generation controls insert: + * True or False, with a default of False */ +#define SECY_CONFIG_TX_CONFIG_USESCB_WR_MASK 0x00010000 +#define SECY_CONFIG_TX_CONFIG_USESCB_WR_SHIFT 16 + +/* USEES_RD RO Frame generation controls read: + * True or False, with a default of False */ +#define SECY_CONFIG_TX_CONFIG_USEES_RD_MASK 0x00008000 +#define SECY_CONFIG_TX_CONFIG_USEES_RD_SHIFT 15 + +/* USEES_WR RW Frame generation controls insert: + * True or False, with a default of False */ +#define SECY_CONFIG_TX_CONFIG_USEES_WR_MASK 0x00004000 +#define SECY_CONFIG_TX_CONFIG_USEES_WR_SHIFT 14 + +/* ALWAYSINCLUDESCI_RD RO Frame generation controls read: + * True or False, with a default of False */ +#define SECY_CONFIG_TX_CONFIG_ALWAYSINCLUDESCI_RD_MASK 0x00002000 +#define SECY_CONFIG_TX_CONFIG_ALWAYSINCLUDESCI_RD_SHIFT 13 + +/* ALWAYSINCLUDESCI_WR RW Frame generation controls insert: + * True or False, with a default of False + * In Tx, SCI explicit (included in SecTAG) when: G_NO_OF_SECYS=1 and alwaysIncludeSCI=1, or G_NO_OF_SECYS>1 */ +#define SECY_CONFIG_TX_CONFIG_ALWAYSINCLUDESCI_WR_MASK 0x00001000 +#define SECY_CONFIG_TX_CONFIG_ALWAYSINCLUDESCI_WR_SHIFT 12 + +/* PROTECTFRAMES_RD RO Frame generation controls read: + * True or False, with a default of True */ +#define SECY_CONFIG_TX_CONFIG_PROTECTFRAMES_RD_MASK 0x00000800 +#define SECY_CONFIG_TX_CONFIG_PROTECTFRAMES_RD_SHIFT 11 + +/* PROTECTFRAMES_WR RW Frame generation controls insert: + * True or False, with a default of True */ +#define SECY_CONFIG_TX_CONFIG_PROTECTFRAMES_WR_MASK 0x00000400 +#define SECY_CONFIG_TX_CONFIG_PROTECTFRAMES_WR_SHIFT 10 + +/* MAXTRANSMITKEYS RO Maximum number of keys in simultaneous use for transmission */ +#define SECY_CONFIG_TX_CONFIG_MAXTRANSMITKEYS_MASK 0x000003f0 +#define SECY_CONFIG_TX_CONFIG_MAXTRANSMITKEYS_SHIFT 4 + +/* MAXTRANSMITCHANNELS RO Maximum number of transmit channels */ +#define SECY_CONFIG_TX_CONFIG_MAXTRANSMITCHANNELS_MASK 0x0000000f +#define SECY_CONFIG_TX_CONFIG_MAXTRANSMITCHANNELS_SHIFT 0 + +/******************************************************************************/ +/* Frame Verification and Validation read registers */ +#define SECY_CONFIG_RX_CONFIG_BASE_ADDR 0x0000000c +/* REPLAYPROTECT_RD RO Frame verification controls rd + * Values: + * True or False, with a default of True */ +#define SECY_CONFIG_RX_CONFIG_REPLAYPROTECT_RD_MASK 0x00020000 +#define SECY_CONFIG_RX_CONFIG_REPLAYPROTECT_RD_SHIFT 17 + +/* REPLAYPROTECT_WR RW Frame verification controls insert + * Values: + * True or False, with a default of True */ +#define SECY_CONFIG_RX_CONFIG_REPLAYPROTECT_WR_MASK 0x00010000 +#define SECY_CONFIG_RX_CONFIG_REPLAYPROTECT_WR_SHIFT 16 + +/* VALIDATEFRAMES_RD RO Frame verification controls read + * Values: + * 0x0: Null, + * 0x1: Disabled, + * 0x2: Check, or + * 0x3: Strict, with a default of Strict */ +#define SECY_CONFIG_RX_CONFIG_VALIDATEFRAMES_RD_MASK 0x0000c000 +#define SECY_CONFIG_RX_CONFIG_VALIDATEFRAMES_RD_SHIFT 14 + +/* VALIDATEFRAMES_WR RW Frame verification controls insert + * Values: + * 0x0: Null, + * 0x1: Disabled, + * 0x2: Check, or + * 0x3: Strict, with a default of Strict */ +#define SECY_CONFIG_RX_CONFIG_VALIDATEFRAMES_WR_MASK 0x00003000 +#define SECY_CONFIG_RX_CONFIG_VALIDATEFRAMES_WR_SHIFT 12 + +/* MAXRECEIVEKEYS RO Frame verification capabilities. + * Maximum number of keys in simultaneous use for reception */ +#define SECY_CONFIG_RX_CONFIG_MAXRECEIVEKEYS_MASK 0x00000fc0 +#define SECY_CONFIG_RX_CONFIG_MAXRECEIVEKEYS_SHIFT 6 + +/* MAXRECEIVECHANNELS RO Frame verification capabilities. + * Maximum number of receive channels */ +#define SECY_CONFIG_RX_CONFIG_MAXRECEIVECHANNELS_MASK 0x0000003f +#define SECY_CONFIG_RX_CONFIG_MAXRECEIVECHANNELS_SHIFT 0 + +/******************************************************************************/ +/* Frame verification controls insert + * Values: + * Between 0 and 232-1, with a default of 0 */ +#define SECY_CONFIG_RX_REPLAYWINDOW_WR_BASE_ADDR 0x00000010 +/* VAL RW ---- */ +#define SECY_CONFIG_RX_REPLAYWINDOW_WR_VAL_MASK 0xffffffff +#define SECY_CONFIG_RX_REPLAYWINDOW_WR_VAL_SHIFT 0 + +/******************************************************************************/ +/* Frame verification controls insert + * Values: + * Between 0 and 232-1, with a default of 0 */ +#define SECY_CONFIG_RX_REPLAYWINDOW_RD_BASE_ADDR 0x00000014 +/* VAL RO ---- */ +#define SECY_CONFIG_RX_REPLAYWINDOW_RD_VAL_MASK 0xffffffff +#define SECY_CONFIG_RX_REPLAYWINDOW_RD_VAL_SHIFT 0 + +/******************************************************************************/ +/* Cipher Suite configuration for SecY */ +#define SECY_CONFIG_CIPHERSUITE_CONFIG_BASE_ADDR 0x00000018 +/* CONFIDENTIALITYOFFSET_RD RO Cipher Suite selection read: + * Number of initial octets of each MSDU without confidentiality protection */ +#define SECY_CONFIG_CIPHERSUITE_CONFIG_CONFIDENTIALITYOFFSET_RD_MASK 0x000fe000 +#define SECY_CONFIG_CIPHERSUITE_CONFIG_CONFIDENTIALITYOFFSET_RD_SHIFT 13 + +/* CONFIDENTIALITYOFFSET_WR RW Cipher Suite selection insert: + * Number of initial octets of each MSDU without confidentiality protection */ +#define SECY_CONFIG_CIPHERSUITE_CONFIG_CONFIDENTIALITYOFFSET_WR_MASK 0x00001fc0 +#define SECY_CONFIG_CIPHERSUITE_CONFIG_CONFIDENTIALITYOFFSET_WR_SHIFT 6 + +/* CURRENTCIPHERSUITE_RD RO Cipher Suite selection: + * The Cipher Suite Identifier (10.7.25) for the cipher suite + * 0x0: AES-GCM-128 + * 0x1: AES-GCM-256 + * 0x2: AES-GCM-XPN-128 + * 0x3: AES-GCM-XPN-256 */ +#define SECY_CONFIG_CIPHERSUITE_CONFIG_CURRENTCIPHERSUITE_RD_MASK 0x00000030 +#define SECY_CONFIG_CIPHERSUITE_CONFIG_CURRENTCIPHERSUITE_RD_SHIFT 4 + +/* CURRENTCIPHERSUITE_WR RW Cipher Suite selection: + * The Cipher Suite Identifier (10.7.25) for the cipher suite + * 0x0: AES-GCM-128 + * 0x1: AES-GCM-256 + * 0x2: AES-GCM-XPN-128 + * 0x3: AES-GCM-XPN-256 */ +#define SECY_CONFIG_CIPHERSUITE_CONFIG_CURRENTCIPHERSUITE_WR_MASK 0x0000000c +#define SECY_CONFIG_CIPHERSUITE_CONFIG_CURRENTCIPHERSUITE_WR_SHIFT 2 + +/* REQUIRECONFIDENTIALITY_RD RO Cipher Suite use read: + * True if the Cipher Suite can only be used to provide both + * confidentiality and integrity (and not integrity only, or + * confidentiality with an offset) */ +#define SECY_CONFIG_CIPHERSUITE_CONFIG_REQUIRECONFIDENTIALITY_RD_MASK 0x00000002 +#define SECY_CONFIG_CIPHERSUITE_CONFIG_REQUIRECONFIDENTIALITY_RD_SHIFT 1 + +/* REQUIRECONFIDENTIALITY_WR RW Cipher Suite use insert: + * True if the Cipher Suite can only be used to provide both + * confidentiality and integrity (and not integrity only, or + * confidentiality with an offset) */ +#define SECY_CONFIG_CIPHERSUITE_CONFIG_REQUIRECONFIDENTIALITY_WR_MASK 0x00000001 +#define SECY_CONFIG_CIPHERSUITE_CONFIG_REQUIRECONFIDENTIALITY_WR_SHIFT 0 + +/******************************************************************************/ +/* Max frame lenght to transmit */ +#define SECY_CONFIG_MAX_FRAME_LENGHT_WR_BASE_ADDR 0x0000001c +/* VAL RW ---- */ +#define SECY_CONFIG_MAX_FRAME_LENGHT_WR_VAL_MASK 0x0000ffff +#define SECY_CONFIG_MAX_FRAME_LENGHT_WR_VAL_SHIFT 0 + +/******************************************************************************/ +/* Max frame lenght to transmit */ +#define SECY_CONFIG_MAX_FRAME_LENGHT_RD_BASE_ADDR 0x00000020 +/* VAL RO ---- */ +#define SECY_CONFIG_MAX_FRAME_LENGHT_RD_VAL_MASK 0x0000ffff +#define SECY_CONFIG_MAX_FRAME_LENGHT_RD_VAL_SHIFT 0 + +#endif /* _MACSEC_TOP_SECY_CONFIG_MEMMAP_H_ */ diff --git a/drivers/net/ethernet/adi/macsec/cco_statistics_memmap.h b/drivers/net/ethernet/adi/macsec/cco_statistics_memmap.h new file mode 100644 index 00000000000000..7bde49c5ce2900 --- /dev/null +++ b/drivers/net/ethernet/adi/macsec/cco_statistics_memmap.h @@ -0,0 +1,429 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023-2025, Analog Devices Incorporated, All Rights Reserved + */ +/******************************************************************************/ +/* DO NOT MODIFY */ +/* THIS FILE IS AUTOGENERATED AND ALL CHANGES WILL BE LOST */ +/******************************************************************************/ +#ifndef _MACSEC_TOP_STATISTICS_MEMMAP_H_ +#define _MACSEC_TOP_STATISTICS_MEMMAP_H_ + +/* General Port and SecY Statistics */ +#define STATISTICS_BASE_ADDR 0x00000200 +#define STATISTICS_STRIDE 0x00000100 +/******************************************************************************/ +/* Control and status indicator for configuration insert */ +#define STATISTICS_STATS_CTRL_BASE_ADDR 0x00000000 +/* RX_STATS_RD_TRIGGER W1C Select Frame Verification and Validation statistics registers */ +#define STATISTICS_STATS_CTRL_RX_STATS_RD_TRIGGER_MASK 0x00001000 +#define STATISTICS_STATS_CTRL_RX_STATS_RD_TRIGGER_SHIFT 12 + +/* TX_STATS_RD_TRIGGER W1C Select Frame Generation and Protection statistics registers */ +#define STATISTICS_STATS_CTRL_TX_STATS_RD_TRIGGER_MASK 0x00000800 +#define STATISTICS_STATS_CTRL_TX_STATS_RD_TRIGGER_SHIFT 11 + +/* PORT_STATS_RD_TRIGGER W1C Select Port Statistics registers; Port Statistics to support IETF RFC 2863 interface MIB + * Counters */ +#define STATISTICS_STATS_CTRL_PORT_STATS_RD_TRIGGER_MASK 0x00000400 +#define STATISTICS_STATS_CTRL_PORT_STATS_RD_TRIGGER_SHIFT 10 + +/* PEER_INDEX RW Index number of secy table to read/write peer SCI */ +#define STATISTICS_STATS_CTRL_PEER_INDEX_MASK 0x000003f0 +#define STATISTICS_STATS_CTRL_PEER_INDEX_SHIFT 4 + +/* SECY_INDEX RW Index number of SecY to read/write */ +#define STATISTICS_STATS_CTRL_SECY_INDEX_MASK 0x0000000f +#define STATISTICS_STATS_CTRL_SECY_INDEX_SHIFT 0 + +/******************************************************************************/ +/* Controlled Port Statistics register: + * The ifInOctets count is the sum of all the octets of the MSDUs delivered to the user of the Controlled Port by + * the Secure Frame Verification process (10.6), plus the octets of the destination and source MAC addresses. */ +#define STATISTICS_PS_CP_IFINOCTETS_BASE_ADDR 0x00000004 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_IFINOCTETS_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_IFINOCTETS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Controlled Port Statistics register: The ifinucastpkts count is the number of unicast packets based on destination + * MAC address. */ +#define STATISTICS_PS_CP_IFINUCASTPKTS_BASE_ADDR 0x00000008 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_IFINUCASTPKTS_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_IFINUCASTPKTS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Controlled Port Statistics register: The ifinmulticastpkts count is the number of multicast packets based on + * destination MAC address. */ +#define STATISTICS_PS_CP_IFINMULTICASTPKTS_BASE_ADDR 0x0000000c +/* VAL RO ---- */ +#define STATISTICS_PS_CP_IFINMULTICASTPKTS_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_IFINMULTICASTPKTS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Controlled Port Statistics register: The ifinbroadcastpkts count is the number of broadcast packets based on + * destination MAC address. */ +#define STATISTICS_PS_CP_IFINBROADCASTPKTS_BASE_ADDR 0x00000010 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_IFINBROADCASTPKTS_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_IFINBROADCASTPKTS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Controlled Port Statistics register: The ifInDiscards count is the sum of all the InPktsNoTag, InPktsLate, and + * InPktsOverrun counts. */ +#define STATISTICS_PS_CP_IFINDISCARDS_BASE_ADDR 0x00000014 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_IFINDISCARDS_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_IFINDISCARDS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Controlled Port Statistics register: The ifInErrors count is the sum of all the InPktsBadTag, InPktsNoSA, and + * InPktsNotValid counts (10.6). */ +#define STATISTICS_PS_CP_IFINERRORS_BASE_ADDR 0x00000018 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_IFINERRORS_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_IFINERRORS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Controlled Port Statistics register: The ifOutOctets count is the sum of the all octets of the MSDUs delivered by the + * user of the Controlled + * Port to the Secure Frame Generation process (10.5), plus the octets of the destination and source MAC addresses. */ +#define STATISTICS_PS_CP_IFOUTOCTETS_BASE_ADDR 0x0000001c +/* VAL RO ---- */ +#define STATISTICS_PS_CP_IFOUTOCTETS_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_IFOUTOCTETS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Controlled Port Statistics register: The ifoutucastpkts count is the number of unicast packets based on destination + * MAC address. */ +#define STATISTICS_PS_CP_IFOUTUCASTPKTS_BASE_ADDR 0x00000020 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_IFOUTUCASTPKTS_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_IFOUTUCASTPKTS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Controlled Port Statistics register: The ifoutmulticastpkts count is the number of multicast packets based on + * destination MAC address. */ +#define STATISTICS_PS_CP_IFOUTMULTICASTPKTS_BASE_ADDR 0x00000024 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_IFOUTMULTICASTPKTS_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_IFOUTMULTICASTPKTS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Controlled Port Statistics register: The ifoutbroadcastpkts count is the number of broadcast packets based on + * destination MAC address. */ +#define STATISTICS_PS_CP_IFOUTBROADCASTPKTS_BASE_ADDR 0x00000028 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_IFOUTBROADCASTPKTS_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_IFOUTBROADCASTPKTS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Controlled Port Statistics register: The ifOutErrors count is equal to the number packets that could not be + * proccessed due to not Tx_SA InUse. */ +#define STATISTICS_PS_CP_IFOUTERRORS_BASE_ADDR 0x0000002c +/* VAL RO ---- */ +#define STATISTICS_PS_CP_IFOUTERRORS_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_IFOUTERRORS_VAL_SHIFT 0 + +/******************************************************************************/ +/* TX Port Statistics register: If the management control protectFrames is False, the preceding steps are omitted, + * an identical transmit request is made to the Transmit Multiplexer, and the OutPktsUntagged counter incremented. */ +#define STATISTICS_PS_CP_TX_OUTPKTSUNTAGGED_BASE_ADDR 0x00000030 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_TX_OUTPKTSUNTAGGED_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_TX_OUTPKTSUNTAGGED_VAL_SHIFT 0 + +/******************************************************************************/ +/* TX Port Statistics register: If size of frame to be transmitted is greater than the configured max_frame_length value + * then the frame is dropped + * and outpktstoolong counter increaments. */ +#define STATISTICS_PS_CP_TX_OUTPKTSTOOLONG_BASE_ADDR 0x00000034 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_TX_OUTPKTSTOOLONG_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_TX_OUTPKTSTOOLONG_VAL_SHIFT 0 + +/******************************************************************************/ +/* TX Port Statistics register: Number of octets of User Data in transmitted frames that were integrity protected but + * not encrypted */ +#define STATISTICS_PS_CP_TX_OUTOCTETSPROTECTED_BASE_ADDR 0x00000038 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_TX_OUTOCTETSPROTECTED_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_TX_OUTOCTETSPROTECTED_VAL_SHIFT 0 + +/******************************************************************************/ +/* TX Port Statistics register: Number of octets of User Data in transmitted frames that were both integrity protected + * and encrypted */ +#define STATISTICS_PS_CP_TX_OUTOCTETSENCRYPTED_BASE_ADDR 0x0000003c +/* VAL RO ---- */ +#define STATISTICS_PS_CP_TX_OUTOCTETSENCRYPTED_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_TX_OUTOCTETSENCRYPTED_VAL_SHIFT 0 + +/******************************************************************************/ +/* TX Port Statistics register: Counts when the packets for a given SA are all only integrity protected. */ +#define STATISTICS_PS_CP_TX_OUTPKTSPROTECTED_BASE_ADDR 0x00000040 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_TX_OUTPKTSPROTECTED_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_TX_OUTPKTSPROTECTED_VAL_SHIFT 0 + +/******************************************************************************/ +/* TX Port Statistics register: Counts when the packets for a given SA are all encrypted (confidentiality protected). */ +#define STATISTICS_PS_CP_TX_OUTPKTSENCRYPTED_BASE_ADDR 0x00000044 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_TX_OUTPKTSENCRYPTED_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_TX_OUTPKTSENCRYPTED_VAL_SHIFT 0 + +/******************************************************************************/ +/* RX Port Statistics register: if there is no sec tag when macsec frame is expected + * then the frame is dropped and inpktsuntagged counter increments. */ +#define STATISTICS_PS_CP_RX_INPKTSUNTAGGED_BASE_ADDR 0x00000048 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_RX_INPKTSUNTAGGED_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_RX_INPKTSUNTAGGED_VAL_SHIFT 0 + +/******************************************************************************/ +/* RX Port Statistics register: if there is no sec tag when macsec frame is expected and validate frame is under strict + * validation + * then the frame is dropped and inpktsnotag counter increments. */ +#define STATISTICS_PS_CP_RX_INPKTSNOTAG_BASE_ADDR 0x0000004c +/* VAL RO ---- */ +#define STATISTICS_PS_CP_RX_INPKTSNOTAG_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_RX_INPKTSNOTAG_VAL_SHIFT 0 + +/******************************************************************************/ +/* RX Port Statistics register: if the received frame have invalid frame with wrong sectag encoding (Table 10-1) + * then the frame is dropped and inpktsbadtag counter increments. */ +#define STATISTICS_PS_CP_RX_INPKTSBADTAG_BASE_ADDR 0x00000050 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_RX_INPKTSBADTAG_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_RX_INPKTSBADTAG_VAL_SHIFT 0 + +/******************************************************************************/ +/* RX Port Statistics register: if InPktsNoSAError is not true then InPktsNoSA counter increments. */ +#define STATISTICS_PS_CP_RX_INPKTSNOSA_BASE_ADDR 0x00000054 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_RX_INPKTSNOSA_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_RX_INPKTSNOSA_VAL_SHIFT 0 + +/******************************************************************************/ +/* RX Port Statistics register: If the SC is not found, the received SCI may be recorded to assist network management + * resolution of the problem, + * and If validateFrames is Strict or the C bit in the SecTAG is set, the InPktsNoSAError counter is incremented and the + * frame is discarded. + * OR + * If the receive SC has been identified, the frame's AN is used to locate the receive SA received frame and processing + * continues with the preliminary + * replay check and if validateFrames is Strict or the C bit is set, the frame is discarded and the InPktsNoSAError + * counter incremented; */ +#define STATISTICS_PS_CP_RX_INPKTSNOSAERROR_BASE_ADDR 0x00000058 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_RX_INPKTSNOSAERROR_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_RX_INPKTSNOSAERROR_VAL_SHIFT 0 + +/******************************************************************************/ +/* RX Port Statistics register: counts of InPktsOverrun can be maintained by counting packates that have been discarded + * due to inability to validate + * frames at the received rate, and by accumulation of the counts InOctetsValidated and InOctetsDecrypted. */ +#define STATISTICS_PS_CP_RX_INPKTSOVERRUN_BASE_ADDR 0x0000005c +/* VAL RO ---- */ +#define STATISTICS_PS_CP_RX_INPKTSOVERRUN_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_RX_INPKTSOVERRUN_VAL_SHIFT 0 + +/******************************************************************************/ +/* RX Port Statistics register: Number of octets of User Data recovered from received frames that were integrity + * protected but not encrypted */ +#define STATISTICS_PS_CP_RX_INOCTETSVALIDATED_BASE_ADDR 0x00000060 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_RX_INOCTETSVALIDATED_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_RX_INOCTETSVALIDATED_VAL_SHIFT 0 + +/******************************************************************************/ +/* RX Port Statistics register: Number of octets of User Data recovered from received frames that were both integrity + * protected and encrypted */ +#define STATISTICS_PS_CP_RX_INOCTETSDECRYPTED_BASE_ADDR 0x00000064 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_RX_INOCTETSDECRYPTED_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_RX_INOCTETSDECRYPTED_VAL_SHIFT 0 + +/******************************************************************************/ +/* RX Port Statistics register: if InPktsUnchecked, InPktsDelayed, InPktsInvalid and InPktsNotValid conditions are false + * then InPktsOK counts */ +#define STATISTICS_PS_CP_RX_INPKTSOK_BASE_ADDR 0x00000068 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_RX_INPKTSOK_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_RX_INPKTSOK_VAL_SHIFT 0 + +/******************************************************************************/ +/* RX Port Statistics register: If the frame is not valid, InPktsUnchecked counts. */ +#define STATISTICS_PS_CP_RX_INPKTSUNCHECKED_BASE_ADDR 0x0000006c +/* VAL RO ---- */ +#define STATISTICS_PS_CP_RX_INPKTSUNCHECKED_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_RX_INPKTSUNCHECKED_VAL_SHIFT 0 + +/******************************************************************************/ +/* RX Port Statistics register: If the received PN is less than the lowest acceptable PN + * (treating a 32-bit PN value of zero as 232 and a 64-bit PN value of zero as 264), InPktsDelayed counts. */ +#define STATISTICS_PS_CP_RX_INPKTSDELAYED_BASE_ADDR 0x00000070 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_RX_INPKTSDELAYED_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_RX_INPKTSDELAYED_VAL_SHIFT 0 + +/******************************************************************************/ +/* RX Port Statistics register: If replayProtect control is enabled and the PN recovered from the received frame is less + * than + * the lowest acceptable packet number (see 10.6.5) for the SA, the frame is discarded and the InPktsLate counter + * incremented. */ +#define STATISTICS_PS_CP_RX_INPKTSLATE_BASE_ADDR 0x00000074 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_RX_INPKTSLATE_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_RX_INPKTSLATE_VAL_SHIFT 0 + +/******************************************************************************/ +/* RX Port Statistics register: If the frame is not valid and validateFrames is set to Check, then InPktsInvalid counts. */ +#define STATISTICS_PS_CP_RX_INPKTSINVALID_BASE_ADDR 0x00000078 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_RX_INPKTSINVALID_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_RX_INPKTSINVALID_VAL_SHIFT 0 + +/******************************************************************************/ +/* RX Port Statistics register: If the received frame is marked as invalid, and the validateFrames control is Strict + * or the C bit in the SecTAG was set, the frame is discarded and the InPktsNotValid counter incremented. */ +#define STATISTICS_PS_CP_RX_INPKTSNOTVALID_BASE_ADDR 0x0000007c +/* VAL RO ---- */ +#define STATISTICS_PS_CP_RX_INPKTSNOTVALID_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_RX_INPKTSNOTVALID_VAL_SHIFT 0 + +/******************************************************************************/ +/* Controlled Port Statistics register: The ifInOctets count is the sum of all the octets of the MSDUs delivered to the + * user of the Controlled Port + * by the Secure Frame Verification process (10.6), plus the octets of the destination and source MAC addresses. */ +#define STATISTICS_PS_UP_IFINOCTETS_BASE_ADDR 0x00000080 +/* VAL RO ---- */ +#define STATISTICS_PS_UP_IFINOCTETS_VAL_MASK 0xffffffff +#define STATISTICS_PS_UP_IFINOCTETS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Controlled Port Statistics register: The ifinucastpkts count is the number of unicast packets based on destination + * MAC address. */ +#define STATISTICS_PS_UP_IFINUCASTPKTS_BASE_ADDR 0x00000084 +/* VAL RO ---- */ +#define STATISTICS_PS_UP_IFINUCASTPKTS_VAL_MASK 0xffffffff +#define STATISTICS_PS_UP_IFINUCASTPKTS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Controlled Port Statistics register: The ifinmulticastpkts count is the number of multicast packets based on + * destination MAC address. */ +#define STATISTICS_PS_UP_IFINMULTICASTPKTS_BASE_ADDR 0x00000088 +/* VAL RO ---- */ +#define STATISTICS_PS_UP_IFINMULTICASTPKTS_VAL_MASK 0xffffffff +#define STATISTICS_PS_UP_IFINMULTICASTPKTS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Controlled Port Statistics register: The ifinbroadcastpkts count is the number of broadcast packets based on + * destination MAC address. */ +#define STATISTICS_PS_UP_IFINBROADCASTPKTS_BASE_ADDR 0x0000008c +/* VAL RO ---- */ +#define STATISTICS_PS_UP_IFINBROADCASTPKTS_VAL_MASK 0xffffffff +#define STATISTICS_PS_UP_IFINBROADCASTPKTS_VAL_SHIFT 0 + +/******************************************************************************/ +/* The ifInDiscards counts are zero, as the operation of the Uncontrolled Port provides no occasion to discard packets. */ +#define STATISTICS_PS_UP_IFINDISCARDS_BASE_ADDR 0x00000090 +/* VAL RO ---- */ +#define STATISTICS_PS_UP_IFINDISCARDS_VAL_MASK 0xffffffff +#define STATISTICS_PS_UP_IFINDISCARDS_VAL_SHIFT 0 + +/******************************************************************************/ +/* The ifInErrors counts are zero, as the operation of the Uncontrolled Port provides no error checking. */ +#define STATISTICS_PS_UP_IFINERRORS_BASE_ADDR 0x00000094 +/* VAL RO ---- */ +#define STATISTICS_PS_UP_IFINERRORS_VAL_MASK 0xffffffff +#define STATISTICS_PS_UP_IFINERRORS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Uncontrolled Port Statistics register: The ifOutOctets count is the sum of the all octets of the MSDUs delivered by + * the user of the Controlled Port + * to the Secure Frame Generation process (10.5), plus the octets of the destination and source MAC addresses. */ +#define STATISTICS_PS_UP_IFOUTOCTETS_BASE_ADDR 0x00000098 +/* VAL RO ---- */ +#define STATISTICS_PS_UP_IFOUTOCTETS_VAL_MASK 0xffffffff +#define STATISTICS_PS_UP_IFOUTOCTETS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Uncontrolled Port Statistics register: The ifoutucastpkts count is the number of unicast packets based on destination + * MAC address. */ +#define STATISTICS_PS_UP_IFOUTUCASTPKTS_BASE_ADDR 0x0000009c +/* VAL RO ---- */ +#define STATISTICS_PS_UP_IFOUTUCASTPKTS_VAL_MASK 0xffffffff +#define STATISTICS_PS_UP_IFOUTUCASTPKTS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Uncontrolled Port Statistics register: The ifoutmulticastpkts count is the number of multicast packets based on + * destination MAC address. */ +#define STATISTICS_PS_UP_IFOUTMULTICASTPKTS_BASE_ADDR 0x000000a0 +/* VAL RO ---- */ +#define STATISTICS_PS_UP_IFOUTMULTICASTPKTS_VAL_MASK 0xffffffff +#define STATISTICS_PS_UP_IFOUTMULTICASTPKTS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Uncontrolled Port Statistics register: The ifoutbroadcastpkts count is the number of broadcast packets based on + * destination MAC address. */ +#define STATISTICS_PS_UP_IFOUTBROADCASTPKTS_BASE_ADDR 0x000000a4 +/* VAL RO ---- */ +#define STATISTICS_PS_UP_IFOUTBROADCASTPKTS_VAL_MASK 0xffffffff +#define STATISTICS_PS_UP_IFOUTBROADCASTPKTS_VAL_SHIFT 0 + +/******************************************************************************/ +/* The ifOutErrors count is zero, as no checking is applied to frames transmitted by the Uncontrolled Port. */ +#define STATISTICS_PS_UP_IFOUTERRORS_BASE_ADDR 0x000000a8 +/* VAL RO ---- */ +#define STATISTICS_PS_UP_IFOUTERRORS_VAL_MASK 0xffffffff +#define STATISTICS_PS_UP_IFOUTERRORS_VAL_SHIFT 0 + +/******************************************************************************/ +/* Additional Port Statistics register: If common port is disabled then the counter increments. */ +#define STATISTICS_PS_COMP_TX_DISABLE_BASE_ADDR 0x000000ac +/* VAL RO ---- */ +#define STATISTICS_PS_COMP_TX_DISABLE_VAL_MASK 0xffffffff +#define STATISTICS_PS_COMP_TX_DISABLE_VAL_SHIFT 0 + +/******************************************************************************/ +/* Additional Port Statistics register: If common port is disabled then the counter increments. */ +#define STATISTICS_PS_COMP_RX_DISABLE_BASE_ADDR 0x000000b0 +/* VAL RO ---- */ +#define STATISTICS_PS_COMP_RX_DISABLE_VAL_MASK 0xffffffff +#define STATISTICS_PS_COMP_RX_DISABLE_VAL_SHIFT 0 + +/******************************************************************************/ +/* Additional Port Statistics register: If common port is enabled but control port is disabled then the counter + * increments. */ +#define STATISTICS_PS_CP_TX_SECYDISABLE_BASE_ADDR 0x000000b4 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_TX_SECYDISABLE_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_TX_SECYDISABLE_VAL_SHIFT 0 + +/******************************************************************************/ +/* Additional Port Statistics register: If common port is enabled but control port is disabled then the counter + * increments. */ +#define STATISTICS_PS_CP_RX_SECYDISABLE_BASE_ADDR 0x000000b8 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_RX_SECYDISABLE_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_RX_SECYDISABLE_VAL_SHIFT 0 + +/******************************************************************************/ +/* Additional Port Statistics register : If common port is enabled but reception is disabled then the counter + * increments. */ +#define STATISTICS_PS_CP_TX_RECEIVINGDISABLE_BASE_ADDR 0x000000bc +/* VAL RO ---- */ +#define STATISTICS_PS_CP_TX_RECEIVINGDISABLE_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_TX_RECEIVINGDISABLE_VAL_SHIFT 0 + +/******************************************************************************/ +/* Additional Port Statistics register: If common port is enabled but transmission is disabled then the counter + * increments. */ +#define STATISTICS_PS_CP_RX_TRANSMITTINGDISABLE_BASE_ADDR 0x000000c0 +/* VAL RO ---- */ +#define STATISTICS_PS_CP_RX_TRANSMITTINGDISABLE_VAL_MASK 0xffffffff +#define STATISTICS_PS_CP_RX_TRANSMITTINGDISABLE_VAL_SHIFT 0 + +#endif /* _MACSEC_TOP_STATISTICS_MEMMAP_H_ */ diff --git a/drivers/net/ethernet/adi/macsec/cco_status_memmap.h b/drivers/net/ethernet/adi/macsec/cco_status_memmap.h new file mode 100644 index 00000000000000..c73c4d814f8179 --- /dev/null +++ b/drivers/net/ethernet/adi/macsec/cco_status_memmap.h @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023-2025, Analog Devices Incorporated, All Rights Reserved + */ +/******************************************************************************/ +/* DO NOT MODIFY */ +/* THIS FILE IS AUTOGENERATED AND ALL CHANGES WILL BE LOST */ +/******************************************************************************/ +#ifndef _MACSEC_TOP_STATUS_MEMMAP_H_ +#define _MACSEC_TOP_STATUS_MEMMAP_H_ + +/* Status for controlled port, Tx/Rx SC/SA */ +#define STATUS_BASE_ADDR 0x00000900 +#define STATUS_STRIDE 0x00000100 +/******************************************************************************/ +/* Control for status readback */ +#define STATUS_ST_CTRL_BASE_ADDR 0x00000000 +/* RD_TRIGGER W1C Trigger a read operation */ +#define STATUS_ST_CTRL_RD_TRIGGER_MASK 0x00001000 +#define STATUS_ST_CTRL_RD_TRIGGER_SHIFT 12 + +/* SA_INDEX RW Index number of SA */ +#define STATUS_ST_CTRL_SA_INDEX_MASK 0x00000c00 +#define STATUS_ST_CTRL_SA_INDEX_SHIFT 10 + +/* PEER_INDEX RW Index number of peer */ +#define STATUS_ST_CTRL_PEER_INDEX_MASK 0x000003f0 +#define STATUS_ST_CTRL_PEER_INDEX_SHIFT 4 + +/* SECY_INDEX RW Index number of SecY */ +#define STATUS_ST_CTRL_SECY_INDEX_MASK 0x0000000f +#define STATUS_ST_CTRL_SECY_INDEX_SHIFT 0 + +/******************************************************************************/ +/* Provided Interface registers: + * Controlled port status - SecY status of: + * 1) MAC_Enabled + * 2) MAC_Operational */ +#define STATUS_ST_SECY_STATUS_BASE_ADDR 0x00000004 +/* MAC_OPERATIONAL RO True if and only if + * 1) MAC_Enabled is True, and + * 2) MAC_Operational is True for the Common Port. */ +#define STATUS_ST_SECY_STATUS_MAC_OPERATIONAL_MASK 0x00000002 +#define STATUS_ST_SECY_STATUS_MAC_OPERATIONAL_SHIFT 1 + +/* MAC_ENABLED RO True if and only if + * 1) ControlledPortEnabled (10.7.5) is True, and + * 2) MAC_Enabled is True for the Common Port, and + * 3) transmitting (10.7.21) is True for the transmit SC, and + * 4) receiving (10.7.12) is True for at least one receive SC. */ +#define STATUS_ST_SECY_STATUS_MAC_ENABLED_MASK 0x00000001 +#define STATUS_ST_SECY_STATUS_MAC_ENABLED_SHIFT 0 + +/******************************************************************************/ +/* Transmit SC status */ +#define STATUS_ST_TX_SC_STATUS_BASE_ADDR 0x00000008 +/* INCLUDINGSCI RO Frame generation controls: + * True if and only if the SC bit is set and the SCI explicitly + * encoded in each SecTAG transmitted */ +#define STATUS_ST_TX_SC_STATUS_INCLUDINGSCI_MASK 0x00000008 +#define STATUS_ST_TX_SC_STATUS_INCLUDINGSCI_SHIFT 3 + +/* ENCODINGSA RO Transmit SC status: + * SA inUse at any time */ +#define STATUS_ST_TX_SC_STATUS_ENCODINGSA_MASK 0x00000006 +#define STATUS_ST_TX_SC_STATUS_ENCODINGSA_SHIFT 1 + +/* TRANSMITTING RO Transmit SC status: + * True if inUse (10.7.23) is True for any of the SAs for the SC, + * and False otherwise */ +#define STATUS_ST_TX_SC_STATUS_TRANSMITTING_MASK 0x00000001 +#define STATUS_ST_TX_SC_STATUS_TRANSMITTING_SHIFT 0 + +/******************************************************************************/ +/* Transmit SA controls configuration */ +#define STATUS_ST_TX_SA_STATUS_BASE_ADDR 0x0000000c +/* INUSE RO Transmit SA status: + * If inUse is True, and MAC_Operational is True for the Common Port, + * the SA can transmit frames */ +#define STATUS_ST_TX_SA_STATUS_INUSE_MASK 0x00000001 +#define STATUS_ST_TX_SA_STATUS_INUSE_SHIFT 0 + +/******************************************************************************/ +/* Transmit SA status: + * the current value of Transmit PN (10.5.2) for the SA */ +#define STATUS_ST_TX_SA_NEXT_PN_BASE_ADDR 0x00000010 +/* VAL RO ---- */ +#define STATUS_ST_TX_SA_NEXT_PN_VAL_MASK 0xffffffff +#define STATUS_ST_TX_SA_NEXT_PN_VAL_SHIFT 0 + +/******************************************************************************/ +/* Receive SC status and configuration */ +#define STATUS_ST_RX_SC_STATUS_BASE_ADDR 0x00000014 +/* RECEIVING RO Receive SC status: + * True if inUse (10.7.14) is True for any of the SAs for the SC, + * and False otherwise. + * When the SC is created, receiving is False, and startedTime + * and stoppedTime are equal to createdTime. */ +#define STATUS_ST_RX_SC_STATUS_RECEIVING_MASK 0x00000001 +#define STATUS_ST_RX_SC_STATUS_RECEIVING_SHIFT 0 + +/******************************************************************************/ +/* Receive SA status and configuration */ +#define STATUS_ST_RX_SA_STATUS_BASE_ADDR 0x00000018 +/* INUSE RO Receive SA status */ +#define STATUS_ST_RX_SA_STATUS_INUSE_MASK 0x00000001 +#define STATUS_ST_RX_SA_STATUS_INUSE_SHIFT 0 + +/******************************************************************************/ +/* Receive SA status: + * Current value of nextPN */ +#define STATUS_ST_RX_SA_NEXTPN_BASE_ADDR 0x0000001c +/* VAL RO ---- */ +#define STATUS_ST_RX_SA_NEXTPN_VAL_MASK 0xffffffff +#define STATUS_ST_RX_SA_NEXTPN_VAL_SHIFT 0 + +/******************************************************************************/ +/* Receive SA status: + * Lowest acceptable PN value for a received frame */ +#define STATUS_ST_RX_SA_LOWESTPN_BASE_ADDR 0x00000020 +/* VAL RO ---- */ +#define STATUS_ST_RX_SA_LOWESTPN_VAL_MASK 0xffffffff +#define STATUS_ST_RX_SA_LOWESTPN_VAL_SHIFT 0 + +#endif /* _MACSEC_TOP_STATUS_MEMMAP_H_ */ diff --git a/drivers/net/ethernet/adi/macsec/cco_traffic_map_memmap.h b/drivers/net/ethernet/adi/macsec/cco_traffic_map_memmap.h new file mode 100644 index 00000000000000..f4f8bdacfb9223 --- /dev/null +++ b/drivers/net/ethernet/adi/macsec/cco_traffic_map_memmap.h @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023-2025, Analog Devices Incorporated, All Rights Reserved + */ +/******************************************************************************/ +/* DO NOT MODIFY */ +/* THIS FILE IS AUTOGENERATED AND ALL CHANGES WILL BE LOST */ +/******************************************************************************/ +#ifndef _MACSEC_TOP_TRAFFIC_MAP_MEMMAP_H_ +#define _MACSEC_TOP_TRAFFIC_MAP_MEMMAP_H_ + +/* Rules for Tx/Rx traffic mapping table */ +#define TRAFFIC_MAP_BASE_ADDR 0x00000800 +#define TRAFFIC_MAP_STRIDE 0x00000100 +/******************************************************************************/ +/* Control and status indicator for rule / configuration insert */ +#define TRAFFIC_MAP_TT_CTRL_BASE_ADDR 0x00000000 +/* RD_TRIGGER W1C Trigger a read operation */ +#define TRAFFIC_MAP_TT_CTRL_RD_TRIGGER_MASK 0x00010000 +#define TRAFFIC_MAP_TT_CTRL_RD_TRIGGER_SHIFT 16 + +/* WR_TRIGGER W1C Trigger a write operation */ +#define TRAFFIC_MAP_TT_CTRL_WR_TRIGGER_MASK 0x00008000 +#define TRAFFIC_MAP_TT_CTRL_WR_TRIGGER_SHIFT 15 + +/* RX_CONFIG_EN RW Select Rx table */ +#define TRAFFIC_MAP_TT_CTRL_RX_CONFIG_EN_MASK 0x00004000 +#define TRAFFIC_MAP_TT_CTRL_RX_CONFIG_EN_SHIFT 14 + +/* TX_CONFIG_EN RW Select Tx table */ +#define TRAFFIC_MAP_TT_CTRL_TX_CONFIG_EN_MASK 0x00002000 +#define TRAFFIC_MAP_TT_CTRL_TX_CONFIG_EN_SHIFT 13 + +/* FIELD_SELECT_WR RW Each bit asserted high means the following field to be performed + * [0] - Destination MAC address + * [1] - VLAN + * [2] - Ethertype + * [3] - Other */ +#define TRAFFIC_MAP_TT_CTRL_FIELD_SELECT_WR_MASK 0x00001e00 +#define TRAFFIC_MAP_TT_CTRL_FIELD_SELECT_WR_SHIFT 9 + +/* INDEX RW Index number of rule to read/write */ +#define TRAFFIC_MAP_TT_CTRL_INDEX_MASK 0x000001ff +#define TRAFFIC_MAP_TT_CTRL_INDEX_SHIFT 0 + +/******************************************************************************/ +/* First 4-bytes of a mac address to insert */ +#define TRAFFIC_MAP_TT_MAC_ADDR_0_WR_BASE_ADDR 0x00000004 +/* VAL RW Most significant bytes of the MAC address. */ +#define TRAFFIC_MAP_TT_MAC_ADDR_0_WR_VAL_MASK 0xffffffff +#define TRAFFIC_MAP_TT_MAC_ADDR_0_WR_VAL_SHIFT 0 + +/******************************************************************************/ +/* Second part of the MAC address to insert */ +#define TRAFFIC_MAP_TT_MAC_ADDR_1_WR_BASE_ADDR 0x00000008 +/* VAL RW Least significant bytes of the MAC address. */ +#define TRAFFIC_MAP_TT_MAC_ADDR_1_WR_VAL_MASK 0x0000ffff +#define TRAFFIC_MAP_TT_MAC_ADDR_1_WR_VAL_SHIFT 0 + +/******************************************************************************/ +/* First 4-bytes of a mac address to read */ +#define TRAFFIC_MAP_TT_MAC_ADDR_0_RD_BASE_ADDR 0x0000000c +/* VAL RO Most significant bytes of the MAC address. */ +#define TRAFFIC_MAP_TT_MAC_ADDR_0_RD_VAL_MASK 0xffffffff +#define TRAFFIC_MAP_TT_MAC_ADDR_0_RD_VAL_SHIFT 0 + +/******************************************************************************/ +/* Second part of the MAC address to read */ +#define TRAFFIC_MAP_TT_MAC_ADDR_1_RD_BASE_ADDR 0x00000010 +/* VAL RO Least significant bytes of the MAC address. */ +#define TRAFFIC_MAP_TT_MAC_ADDR_1_RD_VAL_MASK 0x0000ffff +#define TRAFFIC_MAP_TT_MAC_ADDR_1_RD_VAL_SHIFT 0 + +/******************************************************************************/ +/* vlan id to insert */ +#define TRAFFIC_MAP_TT_VLAN_WR_BASE_ADDR 0x00000014 +/* VAL RW vlan id */ +#define TRAFFIC_MAP_TT_VLAN_WR_VAL_MASK 0x00000fff +#define TRAFFIC_MAP_TT_VLAN_WR_VAL_SHIFT 0 + +/******************************************************************************/ +/* vlan id to read */ +#define TRAFFIC_MAP_TT_VLAN_RD_BASE_ADDR 0x00000018 +/* VAL RO vlan id */ +#define TRAFFIC_MAP_TT_VLAN_RD_VAL_MASK 0x00000fff +#define TRAFFIC_MAP_TT_VLAN_RD_VAL_SHIFT 0 + +/******************************************************************************/ +/* ethertype to insert */ +#define TRAFFIC_MAP_TT_ETYPE_WR_BASE_ADDR 0x0000001c +/* VAL RW ethertype */ +#define TRAFFIC_MAP_TT_ETYPE_WR_VAL_MASK 0x0000ffff +#define TRAFFIC_MAP_TT_ETYPE_WR_VAL_SHIFT 0 + +/******************************************************************************/ +/* ethertype to read */ +#define TRAFFIC_MAP_TT_ETYPE_RD_BASE_ADDR 0x00000020 +/* VAL RO ethertype */ +#define TRAFFIC_MAP_TT_ETYPE_RD_VAL_MASK 0x0000ffff +#define TRAFFIC_MAP_TT_ETYPE_RD_VAL_SHIFT 0 + +/******************************************************************************/ +/* other field rule to insert */ +#define TRAFFIC_MAP_TT_OTHER_WR_BASE_ADDR 0x00000024 +/* VAL RW other */ +#define TRAFFIC_MAP_TT_OTHER_WR_VAL_MASK 0x0000ffff +#define TRAFFIC_MAP_TT_OTHER_WR_VAL_SHIFT 0 + +/******************************************************************************/ +/* other field rule to read */ +#define TRAFFIC_MAP_TT_OTHER_RD_BASE_ADDR 0x00000028 +/* VAL RO other */ +#define TRAFFIC_MAP_TT_OTHER_RD_VAL_MASK 0x0000ffff +#define TRAFFIC_MAP_TT_OTHER_RD_VAL_SHIFT 0 + +/******************************************************************************/ +/* SecY associated to the rule to insert */ +#define TRAFFIC_MAP_TT_SECY_WR_BASE_ADDR 0x0000002c +/* VAL RW Index of SecY */ +#define TRAFFIC_MAP_TT_SECY_WR_VAL_MASK 0x0000001f +#define TRAFFIC_MAP_TT_SECY_WR_VAL_SHIFT 0 + +/******************************************************************************/ +/* SecY associated to the rule to read */ +#define TRAFFIC_MAP_TT_SECY_RD_BASE_ADDR 0x00000030 +/* VAL RO Index of SecY */ +#define TRAFFIC_MAP_TT_SECY_RD_VAL_MASK 0x0000001f +#define TRAFFIC_MAP_TT_SECY_RD_VAL_SHIFT 0 + +/******************************************************************************/ +/* Each bit asserted high means the following is performed + * [0] - Destination MAC address + * [1] - VLAN + * [2] - Ethertype + * [3] - Other */ +#define TRAFFIC_MAP_TT_FIELD_SELECT_RD_BASE_ADDR 0x00000034 +/* VAL RO ---- */ +#define TRAFFIC_MAP_TT_FIELD_SELECT_RD_VAL_MASK 0x0000000f +#define TRAFFIC_MAP_TT_FIELD_SELECT_RD_VAL_SHIFT 0 + +#endif /* _MACSEC_TOP_TRAFFIC_MAP_MEMMAP_H_ */ diff --git a/drivers/net/ethernet/adi/macsec/cco_transmitsa_memmap.h b/drivers/net/ethernet/adi/macsec/cco_transmitsa_memmap.h new file mode 100644 index 00000000000000..62db9a98fbb059 --- /dev/null +++ b/drivers/net/ethernet/adi/macsec/cco_transmitsa_memmap.h @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023-2025, Analog Devices Incorporated, All Rights Reserved + */ +/******************************************************************************/ +/* DO NOT MODIFY */ +/* THIS FILE IS AUTOGENERATED AND ALL CHANGES WILL BE LOST */ +/******************************************************************************/ +#ifndef _MACSEC_TOP_TRANSMITSA_MEMMAP_H_ +#define _MACSEC_TOP_TRANSMITSA_MEMMAP_H_ + +/* transmitAssociations */ +#define TRANSMITSA_BASE_ADDR 0x00000400 +#define TRANSMITSA_STRIDE 0x00000100 +/******************************************************************************/ +/* Control and status indicator for rule / configuration insert */ +#define TRANSMITSA_TX_SA_CTRL_BASE_ADDR 0x00000000 +/* RD_TRIGGER W1C Trigger a read operation */ +#define TRANSMITSA_TX_SA_CTRL_RD_TRIGGER_MASK 0x00000020 +#define TRANSMITSA_TX_SA_CTRL_RD_TRIGGER_SHIFT 5 + +/* WR_TRIGGER W1C Trigger a write operation */ +#define TRANSMITSA_TX_SA_CTRL_WR_TRIGGER_MASK 0x00000010 +#define TRANSMITSA_TX_SA_CTRL_WR_TRIGGER_SHIFT 4 + +/* SECY_INDEX RW Index number of SecY to read/write SA */ +#define TRANSMITSA_TX_SA_CTRL_SECY_INDEX_MASK 0x0000000f +#define TRANSMITSA_TX_SA_CTRL_SECY_INDEX_SHIFT 0 + +/******************************************************************************/ +/* Transmit SA ID: + * association number for the SA */ +#define TRANSMITSA_TX_SA_AN_BASE_ADDR 0x00000004 +/* VAL RW ---- */ +#define TRANSMITSA_TX_SA_AN_VAL_MASK 0x00000003 +#define TRANSMITSA_TX_SA_AN_VAL_SHIFT 0 + +/******************************************************************************/ +/* Transmit SA creation: + * the initial value of Transmit PN (10.5.2) for the SA */ +#define TRANSMITSA_TX_SA_NEXT_PN_BASE_ADDR 0x00000008 +/* VAL RW ---- */ +#define TRANSMITSA_TX_SA_NEXT_PN_VAL_MASK 0xffffffff +#define TRANSMITSA_TX_SA_NEXT_PN_VAL_SHIFT 0 + +/******************************************************************************/ +/* Transmit SA creation: + * A reference to an SAK that is unchanged for the life of the SA */ +#define TRANSMITSA_TX_SA_KEY_INDEX_WR_BASE_ADDR 0x0000000c +/* VAL RW ---- */ +#define TRANSMITSA_TX_SA_KEY_INDEX_WR_VAL_MASK 0x00000fff +#define TRANSMITSA_TX_SA_KEY_INDEX_WR_VAL_SHIFT 0 + +/******************************************************************************/ +/* Transmit SA status: + * A reference to an SAK that is unchanged for the life of the SA */ +#define TRANSMITSA_TX_SA_KEY_INDEX_RD_BASE_ADDR 0x00000010 +/* VAL RO ---- */ +#define TRANSMITSA_TX_SA_KEY_INDEX_RD_VAL_MASK 0x00000fff +#define TRANSMITSA_TX_SA_KEY_INDEX_RD_VAL_SHIFT 0 + +/******************************************************************************/ +/* Transmit SA creation and status: + * if the Current Cipher Suite uses extended packet numbering + * the SSCI for this transmit SA */ +#define TRANSMITSA_TX_SA_SSCI_WR_BASE_ADDR 0x00000014 +/* VAL RW For XPN cipher suites */ +#define TRANSMITSA_TX_SA_SSCI_WR_VAL_MASK 0xffffffff +#define TRANSMITSA_TX_SA_SSCI_WR_VAL_SHIFT 0 + +/******************************************************************************/ +/* Transmit SA creation and status: + * if the Current Cipher Suite uses extended packet numbering + * the SSCI for this transmit SA */ +#define TRANSMITSA_TX_SA_SSCI_RD_BASE_ADDR 0x00000018 +/* VAL RO For XPN cipher suites */ +#define TRANSMITSA_TX_SA_SSCI_RD_VAL_MASK 0xffffffff +#define TRANSMITSA_TX_SA_SSCI_RD_VAL_SHIFT 0 + +/******************************************************************************/ +/* Transmit SA controls configuration */ +#define TRANSMITSA_TX_SA_TRANSMITSA_CFG_BASE_ADDR 0x0000001c +/* CONFIDENTIALITY_RD RO Transmit SA creation and status read: + * True if the SA is to provide confidentiality as well as integrity + * for transmitted frames */ +#define TRANSMITSA_TX_SA_TRANSMITSA_CFG_CONFIDENTIALITY_RD_MASK 0x00000002 +#define TRANSMITSA_TX_SA_TRANSMITSA_CFG_CONFIDENTIALITY_RD_SHIFT 1 + +/* CONFIDENTIALITY_WR RW Transmit SA creation and status insert: + * True if the SA is to provide confidentiality as well as integrity + * for transmitted frames */ +#define TRANSMITSA_TX_SA_TRANSMITSA_CFG_CONFIDENTIALITY_WR_MASK 0x00000001 +#define TRANSMITSA_TX_SA_TRANSMITSA_CFG_CONFIDENTIALITY_WR_SHIFT 0 + +/******************************************************************************/ +/* Transmit SC ID to read + * Each SC has a unique SCI comprising a 48-bit MAC address + * concatenated with a 16-bit Port Identifier + * 31 downto 0 */ +#define TRANSMITSA_TX_SA_SCI_0_RD_BASE_ADDR 0x00000020 +/* VAL RO ---- */ +#define TRANSMITSA_TX_SA_SCI_0_RD_VAL_MASK 0xffffffff +#define TRANSMITSA_TX_SA_SCI_0_RD_VAL_SHIFT 0 + +/******************************************************************************/ +/* Transmit SC ID to read + * Each SC has a unique SCI comprising a 48-bit MAC address + * concatenated with a 16-bit Port Identifier + * 63 downto 32 */ +#define TRANSMITSA_TX_SA_SCI_1_RD_BASE_ADDR 0x00000024 +/* VAL RO ---- */ +#define TRANSMITSA_TX_SA_SCI_1_RD_VAL_MASK 0xffffffff +#define TRANSMITSA_TX_SA_SCI_1_RD_VAL_SHIFT 0 + +#endif /* _MACSEC_TOP_TRANSMITSA_MEMMAP_H_ */ diff --git a/drivers/net/ethernet/adi/macsec/cco_transmitsc_memmap.h b/drivers/net/ethernet/adi/macsec/cco_transmitsc_memmap.h new file mode 100644 index 00000000000000..63442b41e567bb --- /dev/null +++ b/drivers/net/ethernet/adi/macsec/cco_transmitsc_memmap.h @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023-2025, Analog Devices Incorporated, All Rights Reserved + */ +/******************************************************************************/ +/* DO NOT MODIFY */ +/* THIS FILE IS AUTOGENERATED AND ALL CHANGES WILL BE LOST */ +/******************************************************************************/ +#ifndef _MACSEC_TOP_TRANSMITSC_MEMMAP_H_ +#define _MACSEC_TOP_TRANSMITSC_MEMMAP_H_ + +/* transmitChannels */ +#define TRANSMITSC_BASE_ADDR 0x00000300 +#define TRANSMITSC_STRIDE 0x00000100 +/******************************************************************************/ +/* Control and status indicator for rule / configuration insert */ +#define TRANSMITSC_TX_SC_CTRL_BASE_ADDR 0x00000000 +/* RD_TRIGGER W1C Trigger a read operation */ +#define TRANSMITSC_TX_SC_CTRL_RD_TRIGGER_MASK 0x00000020 +#define TRANSMITSC_TX_SC_CTRL_RD_TRIGGER_SHIFT 5 + +/* WR_TRIGGER W1C Trigger a write operation */ +#define TRANSMITSC_TX_SC_CTRL_WR_TRIGGER_MASK 0x00000010 +#define TRANSMITSC_TX_SC_CTRL_WR_TRIGGER_SHIFT 4 + +/* SECY_INDEX RW Index number of SecY to read/write SCI */ +#define TRANSMITSC_TX_SC_CTRL_SECY_INDEX_MASK 0x0000000f +#define TRANSMITSC_TX_SC_CTRL_SECY_INDEX_SHIFT 0 + +/******************************************************************************/ +/* Transmit SC ID to insert + * When the SC is created, transmitting is False and startedTime + * and stoppedTime are equal to createdTime + * 31 downto 0 */ +#define TRANSMITSC_TX_SC_SCI_0_WR_BASE_ADDR 0x00000004 +/* VAL RW ---- */ +#define TRANSMITSC_TX_SC_SCI_0_WR_VAL_MASK 0xffffffff +#define TRANSMITSC_TX_SC_SCI_0_WR_VAL_SHIFT 0 + +/******************************************************************************/ +/* Transmit SC ID to insert + * When the SC is created, transmitting is False and startedTime + * and stoppedTime are equal to createdTime + * 63 downto 32 */ +#define TRANSMITSC_TX_SC_SCI_1_WR_BASE_ADDR 0x00000008 +/* VAL RW ---- */ +#define TRANSMITSC_TX_SC_SCI_1_WR_VAL_MASK 0xffffffff +#define TRANSMITSC_TX_SC_SCI_1_WR_VAL_SHIFT 0 + +/******************************************************************************/ +/* Transmit SC ID to read + * When the SC is created, transmitting is False and startedTime + * and stoppedTime are equal to createdTime + * 31 downto 0 */ +#define TRANSMITSC_TX_SC_SCI_0_RD_BASE_ADDR 0x0000000c +/* VAL RO ---- */ +#define TRANSMITSC_TX_SC_SCI_0_RD_VAL_MASK 0xffffffff +#define TRANSMITSC_TX_SC_SCI_0_RD_VAL_SHIFT 0 + +/******************************************************************************/ +/* Transmit SC ID to read + * When the SC is created, transmitting is False and startedTime + * and stoppedTime are equal to createdTime + * 63 downto 32 */ +#define TRANSMITSC_TX_SC_SCI_1_RD_BASE_ADDR 0x00000010 +/* VAL RO ---- */ +#define TRANSMITSC_TX_SC_SCI_1_RD_VAL_MASK 0xffffffff +#define TRANSMITSC_TX_SC_SCI_1_RD_VAL_SHIFT 0 + +/******************************************************************************/ +/* Transmit SC configuration */ +#define TRANSMITSC_TX_SC_TRANSMITSC_CFG_BASE_ADDR 0x00000014 +/* ENABLETRANSMIT_SA_RD RO Transmit SC control: SA in Use at any time + * When the SA is created, enableTransmit and inUse are False, + * and the SA is not used to transmit frames. The SC parameter + * encodingSA shall be set to the value of the AN for the SA + * and inUse set True, when enableTransmit is set. The SA shall + * stop transmitting, and inUse reset, when enableTransmit is reset + * [0] - SA 0 + * [1] - SA 1 + * [2] - SA 2 + * [3] - SA 3 */ +#define TRANSMITSC_TX_SC_TRANSMITSC_CFG_ENABLETRANSMIT_SA_RD_MASK 0x000000f0 +#define TRANSMITSC_TX_SC_TRANSMITSC_CFG_ENABLETRANSMIT_SA_RD_SHIFT 4 + +/* ENABLETRANSMIT_SA_WR RW Transmit SC control: SA in Use at any time + * When the SA is created, enableTransmit and inUse are False, + * and the SA is not used to transmit frames. The SC parameter + * encodingSA shall be set to the value of the AN for the SA + * and inUse set True, when enableTransmit is set. The SA shall + * stop transmitting, and inUse reset, when enableTransmit is reset + * [0] - SA 0 + * [1] - SA 1 + * [2] - SA 2 + * [3] - SA 3 */ +#define TRANSMITSC_TX_SC_TRANSMITSC_CFG_ENABLETRANSMIT_SA_WR_MASK 0x0000000f +#define TRANSMITSC_TX_SC_TRANSMITSC_CFG_ENABLETRANSMIT_SA_WR_SHIFT 0 + +#endif /* _MACSEC_TOP_TRANSMITSC_MEMMAP_H_ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 05cc07b8f48c03..3ad84d8cf390c4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -298,6 +298,12 @@ config DWMAC_LOONGSON This selects the LOONGSON PCI bus support for the stmmac driver, Support for ethernet controller on Loongson-2K1000 SoC and LS7A1000 bridge. +config DWMAC_ADRV906X + tristate "ADRV906X 1G support" + depends on OF && STMMAC_PLATFORM + help + ADRV906X 1G ethernet driver. + config STMMAC_PCI tristate "STMMAC PCI bus support" depends on STMMAC_ETH && PCI diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index c2f0e91f6bf83d..b3eb8ae3ae814a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -41,4 +41,5 @@ dwmac-altr-socfpga-objs := dwmac-socfpga.o obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o obj-$(CONFIG_DWMAC_INTEL) += dwmac-intel.o obj-$(CONFIG_DWMAC_LOONGSON) += dwmac-loongson.o +obj-$(CONFIG_DWMAC_ADRV906X) += dwmac-adrv906x-1g.o stmmac-pci-objs:= stmmac_pci.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-adrv906x-1g.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-adrv906x-1g.c new file mode 100644 index 00000000000000..9e2394c1edf186 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-adrv906x-1g.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "stmmac.h" +#include "stmmac_platform.h" + +#define EMAC_1G_CG_ENABLE BIT(0) +#define EMAC_1G_OSC_CLK_DIV_MASK GENMASK(19, 13) +#define EMAC_1G_CLK_DIV_MASK GENMASK(12, 6) +#define EMAC_1G_PHY_INTF_SEL_I_MASK GENMASK(5, 3) +#define EMAC_1G_OSC_CLK_DIV_OFF 13 +#define EMAC_1G_CLK_DIV_OFF 6 +#define EMAC_1G_PHY_INTF_SEL_I_OFF 3 +#define EMAC_1G_PHY_INTF_SEL_I_RMII 4 +#define EMAC_1G_PHY_INTF_SEL_I_RGMII 1 + +#define ETH1G_DEVCLK_MASK GENMASK(13, 6) +#define ETH1G_DEVCLK_DIV_FUND BIT(6) +#define ETH1G_DEVCLK_DIV_KILLCLK 0 /* BIT(7) */ +#define ETH1G_DEVCLK_DIV_MCS_RESET 0 /* BIT(8) */ +#define ETH1G_DEVCLK_DIV_RATIO 0 /* Bits 9-10 */ +#define ETH1G_DEVCLK_DIV_RB BIT(11) +#define ETH1G_DEVCLK_BUFFER_ENABLE BIT(12) +#define ETH1G_DEVCLK_BUFFER_TERM_ENABLE BIT(13) +#define ETH1G_DEVCLK_DEFAULT_VAL (ETH1G_DEVCLK_DIV_FUND | \ + ETH1G_DEVCLK_DIV_KILLCLK | \ + ETH1G_DEVCLK_DIV_MCS_RESET | \ + ETH1G_DEVCLK_DIV_RATIO | \ + ETH1G_DEVCLK_DIV_RB | \ + ETH1G_DEVCLK_BUFFER_ENABLE) + +#define ETH1G_REFCLK_MASK BIT(17) +#define ETH1G_REFCLK_REFPATH_PD 0 /* BIT(17) */ +#define ETH1G_REFCLK_DEFAULT_VAL ETH1G_REFCLK_REFPATH_PD + +#define HZ_TO_MHZ(freq) (freq * 1000 * 1000) +#define CLK_2_5MHZ HZ_TO_MHZ(2.5) +#define CLK_25MHZ HZ_TO_MHZ(25) +#define CLK_50MHZ HZ_TO_MHZ(50) +#define CLK_125MHZ HZ_TO_MHZ(125) + +struct adrv906x_priv_data { + struct stmmac_priv *stm_priv; + uint32_t base_clk_speed; + void __iomem *clk_div_base; + phy_interface_t phy_interface; +}; + +static char *macaddr; +module_param(macaddr, charp, 0644); +MODULE_PARM_DESC(macaddr, "set dev mac addresse via kernel module parameter"); + +static int adrv906x_dwmac_set_clk_dividers(void *priv, unsigned int speed, bool force_reconfig) +{ + struct adrv906x_priv_data *adrv_priv = (struct adrv906x_priv_data *)priv; + ulong rate; + u32 reg; + uint32_t osc_div; + uint32_t rmii_div; + + /* Required clock freq depends on the link speed */ + switch (speed) { + case SPEED_10: rate = CLK_2_5MHZ; break; + case SPEED_100: rate = CLK_25MHZ; break; + case SPEED_1000: rate = CLK_125MHZ; break; + default: pr_err("Invalid link speed"); return -1; + } + + /* Sanity checks */ + if (((adrv_priv->base_clk_speed) % rate) != 0) { + pr_err("Unable to get MAC clock"); + return -1; + } + + if ((adrv_priv->phy_interface == PHY_INTERFACE_MODE_RMII) && + ((adrv_priv->base_clk_speed % CLK_50MHZ) != 0)) { + pr_err("Unable to get RMII PHY clock (50 MHz)"); + return -1; + } + + if ((adrv_priv->phy_interface == PHY_INTERFACE_MODE_RMII) && + (speed == SPEED_1000)) { + pr_err("RMII does not support 1000 Mbs"); + return -1; + } + + if ((adrv_priv->phy_interface != PHY_INTERFACE_MODE_RMII) && + (adrv_priv->phy_interface != PHY_INTERFACE_MODE_RGMII)) { + pr_err("MAC-PHY Interface (%d) not supported", adrv_priv->phy_interface); + return -1; + } + + /* Compute clock dividers */ + if (adrv_priv->phy_interface == PHY_INTERFACE_MODE_RMII) { + /* input_clk _|-> OSC_CLK_DIV (50 MHz) ------> PHY core clock (REF_CLK) + * (ie 250MHz) |-> RMII_CLK_DIV (2.5|25 MHz) -> Tx_clk and Rx_clk to GMAC + * (based on PHY link 10|100) + */ + osc_div = (adrv_priv->base_clk_speed / CLK_50MHZ) - 1; + rmii_div = ((adrv_priv->base_clk_speed) / (rate)) - 1; + } else if (adrv_priv->phy_interface == PHY_INTERFACE_MODE_RGMII) { + /* + * input_clk -> OSC_CLK_DIV (2.5|25|125 MHz) -> Tx_clk to PHY and Tx_clk to GMAC + * (ie 250MHz) (based on PHY link 10|100|1000) + * + * Note: Rx_clk to GMAC IP is provided by the external PHY + * Note: RMII_CLK_DIV does not apply + * Note: PHY core clock is provided by a crystal circuitry + */ + osc_div = ((adrv_priv->base_clk_speed) / rate) - 1; + rmii_div = 0; + } + + /* Do not reconfigure clock dividers in RMII if they have not changed + * + * Workaround: Adrv906x clocks divider block require to stop all the + * clocks before updating their value (even though only one of them + * needs to be updated). For RMII, one of these clocks is the PHY core + * clock (REF_CLK). Linux general driver flow does not expect that PHY + * core clock is interrupted at any time. This leads to a wrong behaviour + * (link up and down all the time). Flow: + * - MAC detects link is up (reading PHY register) + * - MAC reconfigure clocks (according to Link Speed) + * - Stopping the clock in the previous step results in a link down + * indication from the PHY chip (and a new autonegotiation process) + * - MAC sets the link down again + * - A bit later, autoneg was complete and link is up again and the + * story repeats once and again. + * + * The fix is not to reconfigure clocks if their value remains the same. + * This prevents the endless loop in the second iteration. + * + * Note: PHY core clock in RGMII is not generated by Adrv906x, so this + * issue does not apply. + * Note: During initalization, reconfiguration is forced. This enables + * the 50 MHz PHY core clock (RMII case) + */ + if (!force_reconfig && adrv_priv->phy_interface == PHY_INTERFACE_MODE_RMII) { + uint32_t curr_osc_div; + uint32_t curr_rmii_div; + + reg = ioread32(adrv_priv->clk_div_base); + curr_osc_div = (reg & EMAC_1G_OSC_CLK_DIV_MASK) >> EMAC_1G_OSC_CLK_DIV_OFF; + curr_rmii_div = (reg & EMAC_1G_CLK_DIV_MASK) >> EMAC_1G_CLK_DIV_OFF; + + if ((osc_div == curr_osc_div) && (rmii_div == curr_rmii_div)) + return 0; + } + + /* Disable clock */ + reg = ioread32(adrv_priv->clk_div_base); + reg |= EMAC_1G_CG_ENABLE; + iowrite32(reg, adrv_priv->clk_div_base); + + /* Set clock divider */ + if (adrv_priv->phy_interface == PHY_INTERFACE_MODE_RMII) { + reg &= ~EMAC_1G_PHY_INTF_SEL_I_MASK; + reg |= EMAC_1G_PHY_INTF_SEL_I_RMII << EMAC_1G_PHY_INTF_SEL_I_OFF; + } else if (adrv_priv->phy_interface == PHY_INTERFACE_MODE_RGMII) { + reg &= ~EMAC_1G_PHY_INTF_SEL_I_MASK; + reg |= EMAC_1G_PHY_INTF_SEL_I_RGMII << EMAC_1G_PHY_INTF_SEL_I_OFF; + } + reg &= ~(EMAC_1G_OSC_CLK_DIV_MASK | EMAC_1G_CLK_DIV_MASK); + reg |= (osc_div << EMAC_1G_OSC_CLK_DIV_OFF) | + (rmii_div << EMAC_1G_CLK_DIV_OFF); + iowrite32(reg, adrv_priv->clk_div_base); + + /* Re-enable clock */ + reg &= ~EMAC_1G_CG_ENABLE; + iowrite32(reg, adrv_priv->clk_div_base); + + return 0; +} + +static void adrv906x_dwmac_fix_mac_speed(void *priv, unsigned int speed, unsigned int mode) +{ + adrv906x_dwmac_set_clk_dividers(priv, speed, false); +} + +static void adrv906x_clk_buffer_enable(void __iomem *clk_ctrl_base, bool term_en) +{ + u32 val; + + val = ioread32(clk_ctrl_base); + val &= ~ETH1G_DEVCLK_MASK; + val |= ETH1G_DEVCLK_DEFAULT_VAL; + if (term_en) + val |= ETH1G_DEVCLK_BUFFER_TERM_ENABLE; + iowrite32(val, clk_ctrl_base); + + val = ioread32(clk_ctrl_base + 0x04); + val &= ~ETH1G_REFCLK_MASK; + val |= ETH1G_REFCLK_DEFAULT_VAL; + iowrite32(val, clk_ctrl_base + 0x04); +} + +static int dwmac_adrv906x_probe(struct platform_device *pdev) +{ + struct plat_stmmacenet_data *plat_dat; + struct stmmac_resources stmmac_res; + struct adrv906x_priv_data *adrv_priv; + struct device *dev = &pdev->dev; + struct device_node *clk_div_np; + void __iomem *clk_ctrl_base; + struct sockaddr sock_addr; + struct net_device *ndev; + const char *if_name; + u32 addr, len; + bool term_en; + int ret; + + adrv_priv = devm_kzalloc(dev, sizeof(*adrv_priv), GFP_KERNEL); + if (!adrv_priv) + return -ENOMEM; + + ret = stmmac_get_platform_resources(pdev, &stmmac_res); + if (ret) + return ret; + + if (pdev->dev.of_node) { + plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac); + if (IS_ERR(plat_dat)) { + dev_err(&pdev->dev, "dt configuration failed"); + return PTR_ERR(plat_dat); + } + } else { + plat_dat = dev_get_platdata(&pdev->dev); + if (!plat_dat) { + dev_err(&pdev->dev, "no platform data provided"); + return -EINVAL; + } + + /* Set default value for multicast hash bins */ + plat_dat->multicast_filter_bins = HASH_TABLE_SIZE; + + /* Set default value for unicast filter entries */ + plat_dat->unicast_filter_entries = 1; + } + + if (macaddr) { + memset(sock_addr.sa_data, 0, sizeof(sock_addr.sa_data_min)); + mac_pton(macaddr, sock_addr.sa_data); + ether_addr_copy(stmmac_res.mac, sock_addr.sa_data); + } + + clk_div_np = of_get_child_by_name(pdev->dev.of_node, "clock_divider"); + if (!clk_div_np) { + dev_err(&pdev->dev, "clock divider could not be detected"); + return -EINVAL; + } + + adrv_priv->base_clk_speed = clk_get_rate(plat_dat->stmmac_clk); + dev_info(&pdev->dev, "base clock speed %d MHz", adrv_priv->base_clk_speed / (HZ_TO_MHZ(1))); + + of_property_read_u32_index(clk_div_np, "reg", 0, &addr); + of_property_read_u32_index(clk_div_np, "reg", 1, &len); + adrv_priv->clk_div_base = devm_ioremap(&pdev->dev, addr, len); + + of_property_read_u32_index(clk_div_np, "ctrl_reg", 0, &addr); + of_property_read_u32_index(clk_div_np, "ctrl_reg", 1, &len); + clk_ctrl_base = devm_ioremap(&pdev->dev, addr, len); + + term_en = of_property_read_bool(clk_div_np, "adi,term_en"); + + adrv906x_clk_buffer_enable(clk_ctrl_base, term_en); + + plat_dat->bsp_priv = adrv_priv; + plat_dat->fix_mac_speed = adrv906x_dwmac_fix_mac_speed; + + /* Custom initialisation (if needed) */ + if (plat_dat->init) { + ret = plat_dat->init(pdev, plat_dat->bsp_priv); + if (ret) + return ret; + } + + + adrv_priv->phy_interface = plat_dat->phy_interface; + /* Configure clock distribution (depends on the phy interface type). + * Enable Link-speed-related clocks to arbitrary value + * Enable PHY core clock to 50 MHz (only for RMII) + */ + ret = adrv906x_dwmac_set_clk_dividers(adrv_priv, SPEED_100, true); + if (ret) + return ret; + + ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + if (ret) + goto err_exit; + + ndev = platform_get_drvdata(pdev); + adrv_priv->stm_priv = netdev_priv(ndev); + + /* Change interface name from DT property */ + ret = of_property_read_string(pdev->dev.of_node, "if-name", &if_name); + dev_info(dev, "TRY using %s interface name from device tree", if_name); + if (!ret) + strscpy(ndev->name, if_name, sizeof(ndev->name)); + + return 0; + +err_exit: + if (plat_dat->exit) + plat_dat->exit(pdev, plat_dat->bsp_priv); + + devm_kfree(dev, adrv_priv); + return ret; +} + +static const struct of_device_id dwmac_adrv906x_match[] = { + { .compatible = "adi,adrv906x-dwmac", }, + { .compatible = "snps,dwmac-5.10a", }, + { }, +}; +MODULE_DEVICE_TABLE(of, dwmac_adrv906x_match); + +static struct platform_driver dwmac_adrv906x_driver = { + .probe = dwmac_adrv906x_probe, + .remove = stmmac_pltfr_remove, + .driver = { + .name = "adrv906x1geth", + .pm = &stmmac_pltfr_pm_ops, + .of_match_table = of_match_ptr(dwmac_adrv906x_match), + }, +}; +module_platform_driver(dwmac_adrv906x_driver); + +MODULE_DESCRIPTION("ADRV906X 1G dwmac driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index f73abff416bedb..c03109e94a6524 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -82,6 +82,7 @@ config PHY_AIROHA_PCIE This driver create the basic PHY instance and provides initialize callback for PCIe GEN3 port. +source "drivers/phy/adi/Kconfig" source "drivers/phy/allwinner/Kconfig" source "drivers/phy/amlogic/Kconfig" source "drivers/phy/broadcom/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index ebc399560da4f6..ed9e671a51cd89 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -11,7 +11,8 @@ obj-$(CONFIG_PHY_XGENE) += phy-xgene.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o -obj-y += allwinner/ \ +obj-y += adi/ \ + allwinner/ \ amlogic/ \ broadcom/ \ cadence/ \ diff --git a/drivers/phy/adi/Kconfig b/drivers/phy/adi/Kconfig new file mode 100644 index 00000000000000..0db65e33007fe8 --- /dev/null +++ b/drivers/phy/adi/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only + +# +# PHY drivers for ADI platforms +# + +config PHY_ADI_SDHCI + tristate "ADI SDHCI PHY driver" + depends on OF + select GENERIC_PHY + help + Enable this to support the ADI SDHCI PHY. diff --git a/drivers/phy/adi/Makefile b/drivers/phy/adi/Makefile new file mode 100644 index 00000000000000..56e676c5d115cd --- /dev/null +++ b/drivers/phy/adi/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_PHY_ADI_SDHCI) += phy-adi-sdhci.o diff --git a/drivers/phy/adi/phy-adi-sdhci.c b/drivers/phy/adi/phy-adi-sdhci.c new file mode 100644 index 00000000000000..9d0ef9168a0a8b --- /dev/null +++ b/drivers/phy/adi/phy-adi-sdhci.c @@ -0,0 +1,505 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * phy-adi-sdhci.c - PHY driver for ADI sdhci PHY. + * + * Copyright (c) 2023, Analog Devices Incorporated, All Rights Reserved + * + */ + +#include +#include +#include +#include +#include +#include + +/* PHY register offsets */ +#define SDHCI_PHY_CNFG_R_OFF (0x00U) +#define SDHCI_PHY_CMDPAD_CNFG_R_OFF (0x04U) +#define SDHCI_PHY_DATPAD_CNFG_R_OFF (0x06U) +#define SDHCI_PHY_STBPAD_CNFG_R_OFF (0x0AU) +#define SDHCI_PHY_RSTNPAD_CNFG_R_OFF (0x0CU) +#define SDHCI_PHY_COMMDL_CNFG_R_OFF (0x1CU) +#define SDHCI_PHY_SDCLKDL_CNFG_R_OFF (0x1DU) +#define SDHCI_PHY_SDCLKDL_DC_R_OFF (0x1EU) +#define SDHCI_PHY_SMPLDL_CNFG_R_OFF (0x20U) +#define SDHCI_PHY_ATDL_CNFG_R_OFF (0x21U) +#define SDHCI_PHY_DLL_CTRL_R_OFF (0x24U) +#define SDHCI_PHY_DLL_CNFG1_R_OFF (0x25U) +#define SDHCI_PHY_DLL_CNFG2_R_OFF (0x26U) +#define SDHCI_PHY_DLLDL_CNFG_R_OFF (0x28U) +#define SDHCI_PHY_DLLLBT_CNFG_R_OFF (0x2CU) +#define SDHCI_PHY_DLL_STATUS_R_OFF (0x2EU) + +/* PHY register field bit masks */ +#define SDHCI_PHY_RSTN_BM BIT(0) +#define SDHCI_PHY_POWERGOOD_BM BIT(1) +#define SDHCI_PHY_DLL_EN_BM BIT(0) +#define SDHCI_PHY_LOCK_STS_BM BIT(0) +#define SDHCI_PHY_ERROR_STS_BM BIT(1) +#define SDHCI_PHY_SLVDLY_BM GENMASK(5, 4) +#define SDHCI_PHY_WAIT_CYCLE_BM GENMASK(2, 0) +#define SDHCI_PHY_JUMPSTEP_BM GENMASK(6, 0) +#define SDHCI_PHY_MST_INPSEL_BM GENMASK(2, 1) +#define SDHCI_PHY_SLV_INPSEL_BM GENMASK(6, 5) +#define SDHCI_RXSEL_BM GENMASK(2, 0) +#define SDHCI_WEAKPULL_EN_BM GENMASK(4, 3) +#define SDHCI_INPSEL_CNFG_BM GENMASK(3, 2) +#define SDHCI_DLSTEP_SEL_BM BIT(0) +#define SDHCI_CCLK_DC_BM GENMASK(6, 0) + +/* PHY register field bit positions */ +#define SDHCI_PHY_SLVDLY_POS (4U) +#define SDHCI_PHY_WAIT_CYCLE_POS (0U) +#define SDHCI_PHY_JUMPSTEP_POS (0U) +#define SDHCI_PHY_MST_INPSEL_POS (1U) +#define SDHCI_PHY_SLV_INPSEL_POS (5U) +#define SDHCI_PHY_DLL_EN_POS (0U) +#define SDHCI_PAD_SP_POS (16U) +#define SDHCI_PAD_SN_POS (20U) +#define SDHCI_WEAKPULL_EN_POS (3U) +#define SDHCI_INPSEL_CNFG_POS (2U) +#define SDHCI_UPDATE_DC_POS (4U) +#define SDHCI_DLSTEP_SEL_POS (0U) +#define SDHCI_CCLK_DC_POS (0U) + +/* PHY register field values */ +#define SDHCI_PHY_SLVDLY (0x2U) +#define SDHCI_PHY_WAIT_CYCLE (0x0U) +#define SDHCI_PHY_JUMPSTEP (0x20U) +#define SDHCI_PHY_MST_INPSEL (0x0U) +#define SDHCI_PHY_SLV_INPSEL (0x3U) +#define SDHCI_PHY_LBT_LOADVAL (0x12U) +#define SDHCI_PHY_DLL_EN (0x1U) +#define SDHCI_PHY_LOCK_STS (0x1U) +#define SDHCI_PHY_ERROR_STS (0x0U) +#define SDHCI_RXSEL_CMD_PAD (0x1U) +#define SDHCI_RXSEL_DAT_PAD (0x1U) +#define SDHCI_RXSEL_RST_N_PAD (0x1U) +#define SDHCI_RXSEL_STB_N_PAD (0x1U) +#define SDHCI_WEAKPULL_EN_CMD_PAD (0x1U) +#define SDHCI_WEAKPULL_EN_DAT_PAD (0x1U) +#define SDHCI_WEAKPULL_EN_RST_N_PAD (0x1U) +#define SDHCI_WEAKPULL_EN_STB_PAD (0x2U) +#define SDHCI_UPDATE_DC (0x1U) +#define SDHCI_DLSTEP_SEL (0x1U) +#define SDHCI_DEFAULT_CCLK_DC_LEGACY (0x78U) +#define SDHCI_DEFAULT_CCLK_DC_HS200 (0x0U) +#define SDHCI_DEFAULT_CCLK_DC_HS400 (0x8U) +#define SDHCI_DEFAULT_DRIVE_STRENGTH_OHM (50) + + +/* PHY powergood timeout value */ +#define SDHCI_PHY_TIMEOUT_100_MS (100U) + +/* PHY 0 Delay Lines input selection */ +#define SDHCI_PHY_0_DL_0_INPSEL_IDL0_IN (0) +#define SDHCI_PHY_0_DL_0_INPSEL_ITST_CLKIN (1) +#define SDHCI_PHY_0_DL_0_INPSEL_ZERO (2) +#define SDHCI_PHY_0_DL_0_INPSEL_IDL1_IN (3) + +#define SDHCI_PHY_0_DL_1_INPSEL_IDL1_IN (0) +#define SDHCI_PHY_0_DL_1_INPSEL_ITST_CLKIN (1) +#define SDHCI_PHY_0_DL_1_INPSEL_ZERO (2) +#define SDHCI_PHY_0_DL_1_INPSEL_OY_CLK (3) + +#define SDHCI_PHY_0_DL_2_INPSEL_IDL2_IN (0) +#define SDHCI_PHY_0_DL_2_INPSEL_ITST_CLKIN (1) +#define SDHCI_PHY_0_DL_2_INPSEL_ODL1_OUT (2) +#define SDHCI_PHY_0_DL_2_INPSEL_IDL1_IN (3) + +/* SDHCI PHY configure operations */ +#define SDHCI_PHY_OPS_CFG_DLL_NO_CLK (1U) +#define SDHCI_PHY_OPS_ENABLE_DLL_AFTER_CLK (2U) +#define SDHCI_PHY_OPS_SET_DELAY (3U) + +/* HS timing */ +#define MMC_TIMING_LEGACY (0) +#define MMC_TIMING_MMC_HS (1) +#define MMC_TIMING_MMC_DDR52 (8) +#define MMC_TIMING_MMC_HS200 (9) +#define MMC_TIMING_MMC_HS400 (10) + +/* Driver strength */ +#define SDHCI_PHY_DRV_STRENGTH_33_OHM (14) +#define SDHCI_PHY_DRV_STRENGTH_40_OHM (12) +#define SDHCI_PHY_DRV_STRENGTH_50_OHM (8) +#define SDHCI_PHY_DRV_STRENGTH_66_OHM (4) +#define SDHCI_PHY_DRV_STRENGTH_100_OHM (0) + +struct adi_sdhci_phy { + void __iomem *base; + u32 dcode_legacy; + u32 dcode_hs200; + u32 dcode_hs400; + u32 drv_strength; +}; + +static void adi_sdhci_phy_writel(struct adi_sdhci_phy *adi_phy, u32 val, int reg) +{ + writel(val, adi_phy->base + reg); +} + +static void adi_sdhci_phy_writew(struct adi_sdhci_phy *adi_phy, u16 val, int reg) +{ + writew(val, adi_phy->base + reg); +} + +static void adi_sdhci_phy_writeb(struct adi_sdhci_phy *adi_phy, u8 val, int reg) +{ + writeb(val, adi_phy->base + reg); +} + +static u32 adi_sdhci_phy_readl(struct adi_sdhci_phy *adi_phy, int reg) +{ + return readl(adi_phy->base + reg); +} + +static u16 adi_sdhci_phy_readw(struct adi_sdhci_phy *adi_phy, int reg) +{ + return readw(adi_phy->base + reg); +} + +static u8 adi_sdhci_phy_readb(struct adi_sdhci_phy *adi_phy, int reg) +{ + return readb(adi_phy->base + reg); +} + +static int adi_sdhci_phy_set_delay(struct phy *phy, u8 hs_timing) +{ + struct adi_sdhci_phy *adi_phy = phy_get_drvdata(phy); + u8 dl0; + u8 dl1; + u8 dl2; + u8 dl1_code; + u8 u8_val; + u8 u8_aux; + + /* PHY instance 1 Delay Lines */ + if ((hs_timing == MMC_TIMING_MMC_HS400) || (hs_timing == MMC_TIMING_MMC_HS200)) { + dl0 = SDHCI_PHY_0_DL_0_INPSEL_IDL1_IN; + dl1 = SDHCI_PHY_0_DL_1_INPSEL_IDL1_IN; + dl2 = SDHCI_PHY_0_DL_2_INPSEL_IDL1_IN; + if (hs_timing == MMC_TIMING_MMC_HS400) + dl1_code = adi_phy->dcode_hs400; + else + dl1_code = adi_phy->dcode_hs200; + } else { + dl0 = SDHCI_PHY_0_DL_0_INPSEL_ZERO; + dl1 = SDHCI_PHY_0_DL_1_INPSEL_IDL1_IN; + dl2 = SDHCI_PHY_0_DL_2_INPSEL_ODL1_OUT; + dl1_code = adi_phy->dcode_legacy; + } + + /* DelayLine 0 (auto-tuning clock) input source selection */ + u8_val = adi_sdhci_phy_readb(adi_phy, SDHCI_PHY_ATDL_CNFG_R_OFF) & ~SDHCI_INPSEL_CNFG_BM; + u8_val |= (dl0 << SDHCI_INPSEL_CNFG_POS); + adi_sdhci_phy_writeb(adi_phy, u8_val, SDHCI_PHY_ATDL_CNFG_R_OFF); + + /* DelayLine 1 (tx clock) input source selection */ + u8_val = adi_sdhci_phy_readb(adi_phy, SDHCI_PHY_SDCLKDL_CNFG_R_OFF) & ~SDHCI_INPSEL_CNFG_BM; + u8_val |= (dl1 << SDHCI_INPSEL_CNFG_POS); + adi_sdhci_phy_writeb(adi_phy, u8_val, SDHCI_PHY_SDCLKDL_CNFG_R_OFF); + + /* eMMC clk_tx DelayLine value settings + * Note: Card clock must be disabled (the framework do it before calling this function) + */ + u8_val |= (SDHCI_UPDATE_DC << SDHCI_UPDATE_DC_POS); + adi_sdhci_phy_writeb(adi_phy, u8_val, SDHCI_PHY_SDCLKDL_CNFG_R_OFF); + u8_aux = adi_sdhci_phy_readb(adi_phy, SDHCI_PHY_SDCLKDL_DC_R_OFF) & + ~SDHCI_CCLK_DC_BM; + u8_aux |= (dl1_code << SDHCI_CCLK_DC_POS); + adi_sdhci_phy_writeb(adi_phy, u8_aux, SDHCI_PHY_SDCLKDL_DC_R_OFF); + u8_val &= ~(SDHCI_UPDATE_DC << SDHCI_UPDATE_DC_POS); + adi_sdhci_phy_writeb(adi_phy, u8_val, SDHCI_PHY_SDCLKDL_CNFG_R_OFF); + + /* DelayLine 2 (rx sampling clock) input source selection */ + u8_val = adi_sdhci_phy_readb(adi_phy, SDHCI_PHY_SMPLDL_CNFG_R_OFF) & ~SDHCI_INPSEL_CNFG_BM; + u8_val |= (dl2 << SDHCI_INPSEL_CNFG_POS); + adi_sdhci_phy_writeb(adi_phy, u8_val, SDHCI_PHY_SMPLDL_CNFG_R_OFF); + + return 0; +} + +static int adi_sdhci_phy_config_dll(struct phy *phy, bool enable) +{ + struct adi_sdhci_phy *adi_phy = phy_get_drvdata(phy); + u8 u8_val; + u8 timeout; + + if (enable == false) { + /* DLL configuration (PHY instance 2) */ + + /* Disable DLL */ + u8_val = adi_sdhci_phy_readb(adi_phy, SDHCI_PHY_DLL_CTRL_R_OFF) & + ~(SDHCI_PHY_DLL_EN_BM); + adi_sdhci_phy_writeb(adi_phy, u8_val, SDHCI_PHY_DLL_CTRL_R_OFF); + + /* DLL slave's update delay input */ + u8_val = adi_sdhci_phy_readb(adi_phy, SDHCI_PHY_DLL_CNFG1_R_OFF) & + ~(SDHCI_PHY_SLVDLY_BM | SDHCI_PHY_WAIT_CYCLE_BM); + u8_val |= (SDHCI_PHY_SLVDLY << SDHCI_PHY_SLVDLY_POS) | + (SDHCI_PHY_WAIT_CYCLE << SDHCI_PHY_WAIT_CYCLE_POS); + adi_sdhci_phy_writeb(adi_phy, u8_val, SDHCI_PHY_DLL_CNFG1_R_OFF); + + /* DLL's jump step input */ + u8_val = adi_sdhci_phy_readb(adi_phy, SDHCI_PHY_DLL_CNFG2_R_OFF) & + ~(SDHCI_PHY_JUMPSTEP_BM); + u8_val |= (SDHCI_PHY_JUMPSTEP << SDHCI_PHY_JUMPSTEP_POS); + adi_sdhci_phy_writeb(adi_phy, u8_val, SDHCI_PHY_DLL_CNFG2_R_OFF); + + /* Delay Lines 0 (master) and 1 (slave) input source selection */ + u8_val = adi_sdhci_phy_readb(adi_phy, SDHCI_PHY_DLLDL_CNFG_R_OFF) & + ~(SDHCI_PHY_MST_INPSEL_BM | SDHCI_PHY_SLV_INPSEL_BM); + u8_val |= (SDHCI_PHY_MST_INPSEL << SDHCI_PHY_MST_INPSEL_POS) | + (SDHCI_PHY_SLV_INPSEL << SDHCI_PHY_SLV_INPSEL_POS); + adi_sdhci_phy_writeb(adi_phy, u8_val, SDHCI_PHY_DLLDL_CNFG_R_OFF); + + /* DLL Low Bandwidth Timer */ + adi_sdhci_phy_writew(adi_phy, SDHCI_PHY_LBT_LOADVAL, SDHCI_PHY_DLLLBT_CNFG_R_OFF); + } else { + /* Enable DLL */ + u8_val = adi_sdhci_phy_readb(adi_phy, SDHCI_PHY_DLL_CTRL_R_OFF) & + ~(SDHCI_PHY_DLL_EN_BM); + u8_val |= (SDHCI_PHY_DLL_EN << SDHCI_PHY_DLL_EN_POS); + adi_sdhci_phy_writeb(adi_phy, u8_val, SDHCI_PHY_DLL_CTRL_R_OFF); + + /* Wait for DLL lock */ + timeout = SDHCI_PHY_TIMEOUT_100_MS; + while (0U == (adi_sdhci_phy_readb(adi_phy, SDHCI_PHY_DLL_STATUS_R_OFF) & SDHCI_PHY_LOCK_STS_BM)) { + if (timeout-- > 0) { + udelay(1000U); + } else { + pr_err("%s: PHY DLL has not locked.\n", __func__); + return -ETIMEDOUT; + } + } + if (0U != (adi_sdhci_phy_readb(adi_phy, SDHCI_PHY_DLL_STATUS_R_OFF) & SDHCI_PHY_ERROR_STS_BM)) { + pr_err("%s: PHY DLL is lock to default with errors.\n", __func__); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int adi_sdhci_phy_init(struct phy *phy) +{ + struct adi_sdhci_phy *adi_phy = phy_get_drvdata(phy); + int err; + u32 u32_val; + u16 u16_val; + u8 u8_val; + u8 timeout; + + /* Assert PHY Reset */ + u32_val = adi_sdhci_phy_readl(adi_phy, SDHCI_PHY_CNFG_R_OFF); + u32_val &= ~SDHCI_PHY_RSTN_BM; + adi_sdhci_phy_writel(adi_phy, u32_val, SDHCI_PHY_CNFG_R_OFF); + + /* Disable DLL */ + u8_val = adi_sdhci_phy_readb(adi_phy, SDHCI_PHY_DLL_CTRL_R_OFF) & + ~(SDHCI_PHY_DLL_EN_BM); + adi_sdhci_phy_writeb(adi_phy, u8_val, SDHCI_PHY_DLL_CTRL_R_OFF); + + /* sdhci PHY general configuration */ + u32_val = adi_sdhci_phy_readl(adi_phy, SDHCI_PHY_CNFG_R_OFF); + u32_val |= ((adi_phy->drv_strength << SDHCI_PAD_SP_POS) | + (adi_phy->drv_strength << SDHCI_PAD_SN_POS)); + adi_sdhci_phy_writel(adi_phy, u32_val, SDHCI_PHY_CNFG_R_OFF); + + /* Command/response PAD settings */ + u16_val = adi_sdhci_phy_readw(adi_phy, SDHCI_PHY_CMDPAD_CNFG_R_OFF) & + ~(SDHCI_RXSEL_BM | SDHCI_WEAKPULL_EN_BM); + u16_val |= (SDHCI_RXSEL_CMD_PAD | + (SDHCI_WEAKPULL_EN_CMD_PAD << SDHCI_WEAKPULL_EN_POS)); + adi_sdhci_phy_writew(adi_phy, u16_val, SDHCI_PHY_CMDPAD_CNFG_R_OFF); + + /* Data PAD settings */ + u16_val = adi_sdhci_phy_readw(adi_phy, SDHCI_PHY_DATPAD_CNFG_R_OFF) & + ~(SDHCI_RXSEL_BM | SDHCI_WEAKPULL_EN_BM); + u16_val |= (SDHCI_RXSEL_DAT_PAD | + (SDHCI_WEAKPULL_EN_DAT_PAD << SDHCI_WEAKPULL_EN_POS)); + adi_sdhci_phy_writew(adi_phy, u16_val, SDHCI_PHY_DATPAD_CNFG_R_OFF); + + /* RSTN PAD settings */ + u16_val = adi_sdhci_phy_readw(adi_phy, SDHCI_PHY_RSTNPAD_CNFG_R_OFF) & + ~(SDHCI_RXSEL_BM | SDHCI_WEAKPULL_EN_BM); + u16_val |= (SDHCI_RXSEL_RST_N_PAD | + (SDHCI_WEAKPULL_EN_RST_N_PAD << SDHCI_WEAKPULL_EN_POS)); + adi_sdhci_phy_writew(adi_phy, u16_val, SDHCI_PHY_RSTNPAD_CNFG_R_OFF); + + /* Strobe PAD settings */ + u16_val = adi_sdhci_phy_readw(adi_phy, SDHCI_PHY_STBPAD_CNFG_R_OFF) & + ~(SDHCI_RXSEL_BM | SDHCI_WEAKPULL_EN_BM); + u16_val |= (SDHCI_RXSEL_STB_N_PAD | + (SDHCI_WEAKPULL_EN_STB_PAD << SDHCI_WEAKPULL_EN_POS)); + adi_sdhci_phy_writew(adi_phy, u16_val, SDHCI_PHY_STBPAD_CNFG_R_OFF); + + /* Set Delay Lines configuration for legacy mode */ + err = adi_sdhci_phy_set_delay(phy, MMC_TIMING_LEGACY); + if (err != 0) { + pr_err("%s: SDHCI PHY: Failed to set delay.\n", __func__); + return err; + } + + /* eMMC DelayLine's per step delay selection */ + u8_val = adi_sdhci_phy_readb(adi_phy, SDHCI_PHY_COMMDL_CNFG_R_OFF) & + ~SDHCI_DLSTEP_SEL_BM; + u8_val |= (SDHCI_DLSTEP_SEL << SDHCI_DLSTEP_SEL_POS); + adi_sdhci_phy_writeb(adi_phy, u8_val, SDHCI_PHY_COMMDL_CNFG_R_OFF); + + /* Wait max 100ms for the PHY Powergood to be 1. As per JEDEC Spec v5.1, + * supply power-up time for SDHCI operating at 1.8V is 25ms, but we give + * more time for the PHY to powerup. */ + timeout = SDHCI_PHY_TIMEOUT_100_MS; + while (0U == (adi_sdhci_phy_readl(adi_phy, SDHCI_PHY_CNFG_R_OFF) & SDHCI_PHY_POWERGOOD_BM)) { + if (timeout-- > 0) { + udelay(1000U); + } else { + pr_err("%s: PHY Powergood status never asserted.\n", __func__); + return -ETIMEDOUT; + } + } + + /* De-assert PHY Reset */ + u32_val = adi_sdhci_phy_readl(adi_phy, SDHCI_PHY_CNFG_R_OFF); + u32_val |= SDHCI_PHY_RSTN_BM; + adi_sdhci_phy_writel(adi_phy, u32_val, SDHCI_PHY_CNFG_R_OFF); + + return 0; +} + +/* + * Argument 'opts' can be casted to any of the available structures + * union phy_configure_opts { + * struct phy_configure_opts_mipi_dphy mipi_dphy; <- MIPI + * struct phy_configure_opts_dp dp; <- Display Port + * }; + * + * It would be nice to add a new one for MMC purposes: + * struct phy_configure_opts_sdhci sdhci; + * + * By now let's reuse 'dp' one for MMC purposes + */ +static int adi_sdhci_phy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + unsigned int event = opts->dp.link_rate; + unsigned int arg1 = opts->dp.lanes; + int err; + + switch (event) { + case SDHCI_PHY_OPS_CFG_DLL_NO_CLK: + err = adi_sdhci_phy_config_dll(phy, false); + break; + case SDHCI_PHY_OPS_ENABLE_DLL_AFTER_CLK: + err = adi_sdhci_phy_config_dll(phy, true); + break; + case SDHCI_PHY_OPS_SET_DELAY: + err = adi_sdhci_phy_set_delay(phy, arg1); + break; + default: + err = -EINVAL; + break; + } + + return err; +} +static const struct phy_ops adi_sdhci_phy_ops = { + .init = adi_sdhci_phy_init, + .configure = adi_sdhci_phy_configure, + .owner = THIS_MODULE, +}; + +static void adi_sdhci_phy_device_tree(struct platform_device *pdev, struct adi_sdhci_phy *adi_phy) +{ + struct device *dev = &pdev->dev; + struct device_node *np; + u32 drv_impedance; + + adi_phy->dcode_legacy = SDHCI_DEFAULT_CCLK_DC_LEGACY; + adi_phy->dcode_hs200 = SDHCI_DEFAULT_CCLK_DC_HS200; + adi_phy->dcode_hs400 = SDHCI_DEFAULT_CCLK_DC_HS400; + + np = pdev->dev.of_node; + if (np) { + of_property_read_u32(np, "adi,dcode-legacy", &adi_phy->dcode_legacy); + of_property_read_u32(np, "adi,dcode-hs200", &adi_phy->dcode_hs200); + of_property_read_u32(np, "adi,dcode-hs400", &adi_phy->dcode_hs400); + of_property_read_u32(np, "adi,driver-strength-ohm", &drv_impedance); + + switch (drv_impedance) { + case 33: + adi_phy->drv_strength = SDHCI_PHY_DRV_STRENGTH_33_OHM; + break; + case 40: + adi_phy->drv_strength = SDHCI_PHY_DRV_STRENGTH_40_OHM; + break; + case 50: + adi_phy->drv_strength = SDHCI_PHY_DRV_STRENGTH_50_OHM; + break; + case 66: + adi_phy->drv_strength = SDHCI_PHY_DRV_STRENGTH_66_OHM; + break; + case 100: + adi_phy->drv_strength = SDHCI_PHY_DRV_STRENGTH_100_OHM; + break; + default: + adi_phy->drv_strength = SDHCI_PHY_DRV_STRENGTH_50_OHM; + dev_err(dev, "Invalid driver impedance (%d). Using default (50 ohm)\n", drv_impedance); + } + } +} + +static int adi_sdhci_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct adi_sdhci_phy *adi_phy; + struct phy *generic_phy; + struct phy_provider *phy_provider; + + if (!dev->parent) + return -ENODEV; + + adi_phy = devm_kzalloc(dev, sizeof(*adi_phy), GFP_KERNEL); + if (!adi_phy) + return -ENOMEM; + + adi_phy->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adi_phy->base)) + return PTR_ERR(adi_phy->base); + + adi_sdhci_phy_device_tree(pdev, adi_phy); + + generic_phy = devm_phy_create(dev, dev->of_node, &adi_sdhci_phy_ops); + if (IS_ERR(generic_phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, adi_phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id adi_sdhci_phy_dt_ids[] = { + { .compatible = "adi,sdhci-phy" }, + {} +}; + +MODULE_DEVICE_TABLE(of, adi_sdhci_phy_dt_ids); + +static struct platform_driver adi_sdhci_driver = { + .probe = adi_sdhci_phy_probe, + .driver = { + .name = "adi-sdhci-phy", + .of_match_table = adi_sdhci_phy_dt_ids, + }, +}; + +module_platform_driver(adi_sdhci_driver); + +MODULE_AUTHOR("Analog Devices Inc."); +MODULE_DESCRIPTION("ADI SDHCI PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 354536de564b67..a46b2484dc638d 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -588,6 +588,7 @@ config PINCTRL_MLXBF3 pinctrl-mlxbf3. source "drivers/pinctrl/actions/Kconfig" +source "drivers/pinctrl/adi/Kconfig" source "drivers/pinctrl/aspeed/Kconfig" source "drivers/pinctrl/bcm/Kconfig" source "drivers/pinctrl/berlin/Kconfig" diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 97823f52b972a3..3f131b779bf6bf 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -87,3 +87,4 @@ obj-y += ti/ obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/ obj-$(CONFIG_PINCTRL_VISCONTI) += visconti/ obj-$(CONFIG_ARCH_VT8500) += vt8500/ +obj-$(CONFIG_PINCTRL_ADI) += adi/ diff --git a/drivers/pinctrl/adi/Kconfig b/drivers/pinctrl/adi/Kconfig new file mode 100644 index 00000000000000..d245d8ec41b825 --- /dev/null +++ b/drivers/pinctrl/adi/Kconfig @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0-only + + +config PINCTRL_ADI + bool "ADI SoC pin control support" + default false + select GENERIC_PINCTRL_GROUPS + select GENERIC_PINMUX_FUNCTIONS + select GENERIC_PINCONF + select PINCTRL_ADI_ADRV906X #if (ARCH_ADRV906X) + help + Say yes here to add support for ADI SoC pincontroller support + This the use of general ADI related pinmux controllers + Once selected, the available ADI SoC's will populate + and the specific SoC will need to be selected + +config PINCTRL_ADI_ADRV906X + depends on PINCTRL_ADI + bool "ADI ADRV906X SOC pin control support" + default true + help + Say The ADRV906X SoC provides a pin controller that allows + specified I/O the ability to be optionally enable for + use with specific ADRV906X peripherals. + Input yes here to add support for ADI ADRV906X SoC pin control diff --git a/drivers/pinctrl/adi/Makefile b/drivers/pinctrl/adi/Makefile new file mode 100644 index 00000000000000..7eeec5e4d46e58 --- /dev/null +++ b/drivers/pinctrl/adi/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +# Analog Devices Inc. pin control drivers +obj-$(CONFIG_PINCTRL_ADI) += pinctrl-adi.o +obj-$(CONFIG_PINCTRL_ADI_ADRV906X) += pinctrl-smc.o +obj-$(CONFIG_PINCTRL_ADI_ADRV906X) += pinctrl-adrv906x.o diff --git a/drivers/pinctrl/adi/pinctrl-adi.c b/drivers/pinctrl/adi/pinctrl-adi.c new file mode 100644 index 00000000000000..30c9f6a6f02562 --- /dev/null +++ b/drivers/pinctrl/adi/pinctrl-adi.c @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2022, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../core.h" +#include "../pinconf.h" +#include "../pinmux.h" +#include "pinctrl-adi.h" + +static inline const struct group_desc *adi_pinctrl_find_group_by_name( + struct pinctrl_dev *pctldev, + const char *name) +{ + const struct group_desc *grp = NULL; + int i; + + for (i = 0; i < pctldev->num_groups; i++) { + grp = pinctrl_generic_get_group(pctldev, i); + if (grp && !strcmp(grp->grp.name, name)) + break; + } + + return grp; +} + +static void adi_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, + unsigned int offset) +{ + seq_printf(s, "%s", dev_name(pctldev->dev)); +} + +static int adi_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, unsigned int *num_maps) +{ + const struct group_desc *grp; + struct pinctrl_map *new_map; + struct device_node *parent; + struct adi_pin *pin; + int map_num = 1; + int i; + + /* + * first find the group of this node and check if we need create + * config maps for pins + */ + grp = adi_pinctrl_find_group_by_name(pctldev, np->name); + if (!grp) + return -EINVAL; + + for (i = 0; i < grp->grp.npins; i++) { + pin = &((struct adi_pin *)(grp->data))[i]; + map_num++; + } + + new_map = kmalloc_array(map_num, sizeof(struct pinctrl_map), + GFP_KERNEL); + if (!new_map) + return -ENOMEM; + + *map = new_map; + *num_maps = map_num; + /* create mux map */ + parent = of_get_parent(np); + if (!parent) { + kfree(new_map); + return -EINVAL; + } + new_map[0].type = PIN_MAP_TYPE_MUX_GROUP; + new_map[0].data.mux.function = parent->name; + new_map[0].data.mux.group = np->name; + of_node_put(parent); + + /* create config map */ + new_map++; + for (i = 0; i < grp->grp.npins; i++) { + pin = &((struct adi_pin *)(grp->data))[i]; + + new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN; + new_map[i].data.configs.group_or_pin = + pin_get_name(pctldev, pin->pin); + + new_map[i].data.configs.configs = (long *)&pin->conf.mio; + new_map[i].data.configs.num_configs = 1; + } + + return 0; +} + +static void adi_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned int num_maps) +{ + kfree(map); +} + +static const struct pinctrl_ops adi_pctrl_ops = { + .get_groups_count = pinctrl_generic_get_group_count, + .get_group_name = pinctrl_generic_get_group_name, + .get_group_pins = pinctrl_generic_get_group_pins, + .pin_dbg_show = adi_pin_dbg_show, + .dt_node_to_map = adi_dt_node_to_map, + .dt_free_map = adi_dt_free_map, +}; + +static int adi_pmx_set(struct pinctrl_dev *pctldev, unsigned int selector, + unsigned int group) +{ + struct function_desc *func; + struct group_desc *grp; + unsigned int npins; + + /* + * Configure the mux mode for each pin in the group for a specific + * function. + */ + grp = pinctrl_generic_get_group(pctldev, group); + if (!grp) + return -EINVAL; + + func = pinmux_generic_get_function(pctldev, selector); + if (!func) + return -EINVAL; + + npins = grp->grp.npins; + return 0; +} + +const struct pinmux_ops adi_pmx_ops = { + .get_functions_count = pinmux_generic_get_function_count, + .get_function_name = pinmux_generic_get_function_name, + .get_function_groups = pinmux_generic_get_function_groups, + .set_mux = adi_pmx_set, +}; + +static void adi_pinctrl_parse_pin(struct adi_pinctrl *ipctl, + unsigned int *pin_id, struct adi_pin *pin, + const __be32 **list_p, + struct device_node *np) +{ + struct adi_pin_mio *pin_mio = &pin->conf.mio; + struct adi_pin_reg *pin_reg; + const __be32 *list = *list_p; + uint32_t pin_num, mux_reg, conf_reg; + + pin_num = be32_to_cpu(*list++); + mux_reg = be32_to_cpu(*list++); + conf_reg = be32_to_cpu(*list++); + pin_mio->input_pin = pin_num; + pin_mio->mux_sel = mux_reg; + pin_mio->config = (unsigned long)conf_reg; + *pin_id = pin_num; + pin_reg = &ipctl->pin_regs[*pin_id]; + pin->pin = *pin_id; + pin_reg->pin_num = pin_num; + pin_reg->mux_reg = mux_reg; + pin_reg->conf_reg = conf_reg; + *list_p = list; +} + +static int adi_pinconf_get(struct pinctrl_dev *pctldev, + unsigned int pin_id, unsigned long *config) +{ + struct adi_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct adi_pinctrl_soc_info *info = ipctl->info; + + if (!info->adi_pinconf_get) + return -EINVAL; + + /* + * Call the registered function to get the pinconf + */ + return info->adi_pinconf_get(pctldev, pin_id, config); +} + + +static int adi_pinconf_set(struct pinctrl_dev *pctldev, + unsigned int pin_id, unsigned long *configs, + unsigned int num_configs) +{ + struct adi_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct adi_pinctrl_soc_info *info = ipctl->info; + + if (!info->adi_pinconf_set) + return -EINVAL; + + /* + * Call the registered function to set the pinconf + */ + return info->adi_pinconf_set(pctldev, pin_id, + configs, num_configs); +} + +static void adi_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned int pin_id) +{ + struct adi_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct adi_pinctrl_soc_info *info = ipctl->info; + unsigned long config; + int ret; + + ret = info->adi_pinconf_get(pctldev, pin_id, &config); + if (ret) { + seq_puts(s, "N/A"); + return; + } + + seq_printf(s, "0x%lx", config); +} + +static void adi_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned int group) +{ + struct group_desc *grp; + unsigned long config; + const char *name; + int i, ret; + + if (group >= pctldev->num_groups) + return; + + seq_puts(s, "\n"); + grp = pinctrl_generic_get_group(pctldev, group); + if (!grp) + return; + + for (i = 0; i < grp->grp.npins; i++) { + struct adi_pin *pin = &((struct adi_pin *)(grp->data))[i]; + + name = pin_get_name(pctldev, pin->pin); + ret = adi_pinconf_get(pctldev, pin->pin, &config); + if (ret) + return; + seq_printf(s, " %s: 0x%lx\n", name, config); + } +} + +static const struct pinconf_ops adi_pinconf_ops = { + .pin_config_get = adi_pinconf_get, + .pin_config_set = adi_pinconf_set, + .pin_config_dbg_show = adi_pinconf_dbg_show, + .pin_config_group_dbg_show = adi_pinconf_group_dbg_show, +}; + +/* + * Each pin represented in adi,pins consists of a number of u32 PIN_FUNC_ID + * and 1 u32 CONFIG, the total size is PIN_FUNC_ID + CONFIG for each pin. + * + * PIN_FUNC_ID format: + * Default: + * + */ +#define ADI_PIN_SIZE 12 + +static int adi_pinctrl_parse_groups(struct device_node *np, + struct group_desc *grp, + struct adi_pinctrl *ipctl, + u32 index) +{ + const struct adi_pinctrl_soc_info *info = ipctl->info; + struct adi_pin *pin; + unsigned int *pins; + int size, pin_size; + const __be32 *list; + int i; + + pin_size = ADI_PIN_SIZE; + grp->grp.name = np->name; + + /* + * the binding format is adi,pins = , + * do sanity check and calculate pins number + */ + list = of_get_property(np, "adi,pins", &size); + if (!list) + return -EINVAL; + + /* we do not check return since it's safe node passed down */ + if (!size || size % pin_size) + return -EINVAL; + + grp->grp.npins = size / pin_size; + grp->data = devm_kcalloc(ipctl->dev, + grp->grp.npins, sizeof(struct adi_pin), + GFP_KERNEL); + pins = devm_kcalloc(ipctl->dev, + grp->grp.npins, sizeof(unsigned int), + GFP_KERNEL); + grp->grp.pins = pins; + + if (!grp->grp.pins || !grp->data) + return -ENOMEM; + + for (i = 0; i < grp->grp.npins; i++) { + pin = &((struct adi_pin *)(grp->data))[i]; + if (info->adi_pinctrl_parse_pin) + info->adi_pinctrl_parse_pin(ipctl, &pins[i], + pin, &list); + else + adi_pinctrl_parse_pin(ipctl, &pins[i], + pin, &list, np); + } + + return 0; +} + +static int adi_pinctrl_parse_functions(struct device_node *np, + struct adi_pinctrl *ipctl, + u32 index) +{ + struct pinctrl_dev *pctl = ipctl->pctl; + struct device_node *child; + struct function_desc *func; + struct group_desc *grp; + const char **group_names; + u32 i = 0; + + func = pinmux_generic_get_function(pctl, index); + if (!func) + return -EINVAL; + + func->func.name = np->name; + func->func.ngroups = of_get_child_count(np); + if (func->func.ngroups == 0) + return -EINVAL; + + group_names = devm_kcalloc(ipctl->dev, func->func.ngroups, + sizeof(char *), GFP_KERNEL); + if (!group_names) + return -ENOMEM; + + func->func.groups = group_names; + + for_each_child_of_node(np, child) { + group_names[i] = child->name; + grp = devm_kzalloc(ipctl->dev, sizeof(struct group_desc), + GFP_KERNEL); + if (!grp) { + of_node_put(child); + return -ENOMEM; + } + + mutex_lock(&ipctl->mutex); + radix_tree_insert(&pctl->pin_group_tree, + ipctl->group_index++, grp); + mutex_unlock(&ipctl->mutex); + adi_pinctrl_parse_groups(child, grp, ipctl, i++); + } + + return 0; +} + +/* + * Check if the DT contains pins in the direct child nodes. This indicates the + * newer DT format to store pins. This function returns true if the first found + * adi,pins property is in a child of np. Otherwise false is returned. + */ +static bool adi_pinctrl_dt_is_flat_functions(struct device_node *np) +{ + struct device_node *function_np; + struct device_node *pinctrl_np; + + for_each_child_of_node(np, function_np) { + if (of_property_read_bool(function_np, "adi,pins")) { + of_node_put(function_np); + return true; + } + + for_each_child_of_node(function_np, pinctrl_np) { + if (of_property_read_bool(pinctrl_np, "adi,pins")) { + of_node_put(pinctrl_np); + of_node_put(function_np); + return false; + } + } + } + + return true; +} + +static int adi_pinctrl_probe_dt(struct platform_device *pdev, + struct adi_pinctrl *ipctl) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *child; + struct pinctrl_dev *pctl = ipctl->pctl; + u32 nfuncs = 0; + u32 i = 0; + bool flat_funcs; + + if (!np) + return -ENODEV; + + flat_funcs = adi_pinctrl_dt_is_flat_functions(np); + if (flat_funcs) { + nfuncs = 1; + } else { + nfuncs = of_get_child_count(np); + if (nfuncs == 0) + return -EINVAL; + } + + for (i = 0; i < nfuncs; i++) { + struct function_desc *function; + + function = devm_kzalloc(&pdev->dev, sizeof(*function), + GFP_KERNEL); + if (!function) + return -ENOMEM; + + mutex_lock(&ipctl->mutex); + radix_tree_insert(&pctl->pin_function_tree, i, function); + mutex_unlock(&ipctl->mutex); + } + + pctl->num_functions = nfuncs; + ipctl->group_index = 0; + if (flat_funcs) { + pctl->num_groups = of_get_child_count(np); + } else { + pctl->num_groups = 0; + for_each_child_of_node(np, child) + pctl->num_groups += of_get_child_count(child); + } + + if (flat_funcs) { + adi_pinctrl_parse_functions(np, ipctl, 0); + } else { + i = 0; + for_each_child_of_node(np, child) + adi_pinctrl_parse_functions(child, ipctl, i++); + } + + return 0; +} + +int adi_pinctrl_probe(struct platform_device *pdev, + const struct adi_pinctrl_soc_info *info) +{ + struct pinctrl_desc *adi_pinctrl_desc; + struct adi_pinctrl *ipctl; + int ret, i; + const __be32 *prop; + + if (!info || !info->pins || !info->npins) + return -EINVAL; + + /* Create state holders etc for this driver */ + ipctl = devm_kzalloc(&pdev->dev, sizeof(*ipctl), GFP_KERNEL); + if (!ipctl) + return -ENOMEM; + ipctl->pin_regs = devm_kmalloc_array(&pdev->dev, info->npins, + sizeof(*ipctl->pin_regs), + GFP_KERNEL); + if (!ipctl->pin_regs) + return -ENOMEM; + + for (i = 0; i < info->npins; i++) { + ipctl->pin_regs[i].mux_reg = -1; + ipctl->pin_regs[i].conf_reg = -1; + } + + adi_pinctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*adi_pinctrl_desc), + GFP_KERNEL); + if (!adi_pinctrl_desc) + return -ENOMEM; + adi_pinctrl_desc->name = dev_name(&pdev->dev); + adi_pinctrl_desc->pins = info->pins; + adi_pinctrl_desc->npins = info->npins; + adi_pinctrl_desc->pctlops = &adi_pctrl_ops; + adi_pinctrl_desc->pmxops = &adi_pmx_ops; + adi_pinctrl_desc->confops = &adi_pinconf_ops; + adi_pinctrl_desc->owner = THIS_MODULE; + mutex_init(&ipctl->mutex); + ipctl->info = info; + ipctl->dev = &pdev->dev; + + prop = of_get_property(pdev->dev.of_node, "reg", NULL); + if (!prop) + return -EINVAL; + ipctl->phys_addr = of_read_number(prop, 1); + + platform_set_drvdata(pdev, ipctl); + ret = devm_pinctrl_register_and_init(&pdev->dev, + adi_pinctrl_desc, ipctl, + &ipctl->pctl); + if (ret) + return ret; + + ret = adi_pinctrl_probe_dt(pdev, ipctl); + if (ret) + return ret; + + dev_info(&pdev->dev, "initialized ADI pinctrl driver\n"); + return pinctrl_enable(ipctl->pctl); +} +EXPORT_SYMBOL_GPL(adi_pinctrl_probe); + +static int __maybe_unused adi_pinctrl_suspend(struct device *dev) +{ + struct adi_pinctrl *ipctl = dev_get_drvdata(dev); + + return pinctrl_force_sleep(ipctl->pctl); +} + +static int __maybe_unused adi_pinctrl_resume(struct device *dev) +{ + struct adi_pinctrl *ipctl = dev_get_drvdata(dev); + + return pinctrl_force_default(ipctl->pctl); +} + +const struct dev_pm_ops adi_pinctrl_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(adi_pinctrl_suspend, + adi_pinctrl_resume) +}; +EXPORT_SYMBOL_GPL(adi_pinctrl_pm_ops); + +MODULE_AUTHOR("Howard Massey "); +MODULE_DESCRIPTION("ADI pinctrl driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/adi/pinctrl-adi.h b/drivers/pinctrl/adi/pinctrl-adi.h new file mode 100644 index 00000000000000..c6b338cc9b8bbb --- /dev/null +++ b/drivers/pinctrl/adi/pinctrl-adi.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2022 ~ 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __DRIVERS_PINCTRL_ADI_H +#define __DRIVERS_PINCTRL_ADI_H + +#define ADI_PINCTRL_PIN(pin) PINCTRL_PIN(pin, #pin) + +/* + * Bit Mask Info for ADI's Pinctrl Word + */ +#define ADI_CONFIG_DRIVE_STRENGTH_MASK (0x0000000FU) +#define ADI_CONFIG_DRIVE_STRENGTH_MASK_BIT_POSITION (0U) +#define ADI_CONFIG_SCHMITT_TRIG_ENABLE_MASK (0x00000010U) +#define ADI_CONFIG_SCHMITT_TRIG_ENABLE_MASK_BIT_POSITION (4U) +#define ADI_CONFIG_PULL_UP_DOWN_ENABLEMENT_MASK (0x00000020U) +#define ADI_CONFIG_PULL_UP_DOWN_ENABLEMENT_MASK_BIT_POSITION (5U) +#define ADI_CONFIG_PULLUP_ENABLE_MASK (0x00000040U) +#define ADI_CONFIG_PULLUP_ENABLE_MASK_BIT_POSITION (6) +#define ADI_CONFIG_MUX_SEL_MASK (0x000000FFU) + +struct platform_device; +extern const struct pinmux_ops adi_pmx_ops; +extern const struct dev_pm_ops adi_pinctrl_pm_ops; + +/** + * struct adi_pin_mio - PIO pin configurations + * @input_pin: Pin Number + * @mux_sel: source mux select value + * @config: Configuration data for pin + */ +struct adi_pin_mio { + unsigned int input_pin; + unsigned int mux_sel; + unsigned long config; +}; + +/** + * struct adi_pin - describes a single pintctrl pin + * @pin: the pin_id of this pin + * @conf: config info for this pin + */ +struct adi_pin { + unsigned int pin; + union { + struct adi_pin_mio mio; + } conf; +}; + +/** + * struct adi_pin_reg - describe a pin configuration + * @pin_num: Pin Number + * @mux_reg: mux register + * @conf_reg: config register + */ +struct adi_pin_reg { + uint32_t pin_num; + uint32_t mux_reg; + uint32_t conf_reg; +}; + +/** + * structure containing the ADI pinctrl context + * + * @dev: a pointer back to containing device + * @pctl: pin control class device + * @info: soc specific information + * @pin_regs: pin configuration information + * @group_index: group + * @mutex: mutex context + */ +struct adi_pinctrl { + struct device *dev; + struct pinctrl_dev *pctl; + const struct adi_pinctrl_soc_info *info; + struct adi_pin_reg *pin_regs; + unsigned int group_index; + struct mutex mutex; + phys_addr_t phys_addr; +}; + +/** + * SOC pinctrl data structure + * + * @pins struct containing pin information + * @npins Number of pins + * @adi_pinconf_get hook for registered handler, get pin configuration + * @adi_pinconf_set hook for registered handler, set pin configuration + * @adi_pinctrl_parse_pin hook for registered handler, parse pin info + * + */ +struct adi_pinctrl_soc_info { + const struct pinctrl_pin_desc *pins; + unsigned int npins; + + int (*adi_pinconf_get)(struct pinctrl_dev *pctldev, unsigned int pin_id, unsigned long *config); + int (*adi_pinconf_set)(struct pinctrl_dev *pctldev, unsigned int pin_id, unsigned long *configs, unsigned int num_configs); + void (*adi_pinctrl_parse_pin)(struct adi_pinctrl *ipctl, unsigned int *pin_id, struct adi_pin *pin, const __be32 **list_p); +}; + +int adi_pinctrl_probe(struct platform_device *pdev, const struct adi_pinctrl_soc_info *info); +int adi_pinconf_get_smc(struct pinctrl_dev *pctldev, unsigned int pin_id, unsigned long *configs); +int adi_pinconf_set_smc(struct pinctrl_dev *pctldev, unsigned int pin_id, unsigned long *configs, unsigned int num_configs); + +#endif /* __DRIVERS_PINCTRL_ADI_H */ diff --git a/drivers/pinctrl/adi/pinctrl-adrv906x-init-tbl.h b/drivers/pinctrl/adi/pinctrl-adrv906x-init-tbl.h new file mode 100644 index 00000000000000..c6c7f4003a9ad1 --- /dev/null +++ b/drivers/pinctrl/adi/pinctrl-adrv906x-init-tbl.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2025, Analog Devices Incorporated, All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __DRIVERS_PINCTRL_ADRV906X_INIT_TBL_H +#define __DRIVERS_PINCTRL_ADRV906X_INIT_TBL_H + +#include +#include +#include "pinctrl-adi.h" + +const struct pinctrl_pin_desc adi_adrv906x_pinctrl_pads[] = { + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_0), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_1), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_2), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_3), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_4), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_5), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_6), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_7), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_8), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_9), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_10), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_11), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_12), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_13), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_14), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_15), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_16), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_17), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_18), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_19), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_20), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_21), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_22), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_23), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_24), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_25), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_26), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_27), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_28), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_29), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_30), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_31), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_32), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_33), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_34), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_35), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_36), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_37), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_38), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_39), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_40), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_41), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_42), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_43), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_44), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_45), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_46), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_47), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_48), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_49), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_50), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_51), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_52), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_53), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_54), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_55), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_56), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_57), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_58), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_59), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_60), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_61), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_62), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_63), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_64), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_65), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_66), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_67), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_68), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_69), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_70), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_71), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_72), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_73), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_74), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_75), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_76), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_77), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_78), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_79), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_80), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_81), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_82), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_83), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_84), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_85), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_86), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_87), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_88), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_89), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_90), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_91), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_92), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_93), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_94), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_95), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_96), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_97), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_98), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_99), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_100), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_101), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_102), + /* Dedicated IO */ + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_115), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_116), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_117), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_118), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_119), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_120), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_121), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_122), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_123), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_124), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_125), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_126), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_127), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_128), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_129), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_130), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_131), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_132), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_133), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_134), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_135), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_136), + ADI_PINCTRL_PIN(ADI_ADRV906X_PIN_137), +}; + +#endif /* __DRIVERS_PINCTRL_ADRV906X_INIT_TBL_H */ diff --git a/drivers/pinctrl/adi/pinctrl-adrv906x.c b/drivers/pinctrl/adi/pinctrl-adrv906x.c new file mode 100644 index 00000000000000..f11c43cc3447d7 --- /dev/null +++ b/drivers/pinctrl/adi/pinctrl-adrv906x.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2022 ~ 2024, Analog Devices Incorporated, All Rights Reserved + * + * The sysfs file I/O for user space application to set/get configs such as: + * -config:[all, 6:0] + * --drive_strength=config[3:0] + * --schmitt_trig_enable=config[4] + * --pin_pull_enablement=config[5] + * --pin_pull_up_enable=config[6] + * -mux_sel + * + * Location example('20218000.pinctrl' is the dev_name of this driver): + * /sys/devices/platform/20218000.pinctrl/control/ + * |-- pin + * |-- config + * |-- drive_strength + * |-- mux_sel + * |-- pin_pull_enablement + * |-- pin_pull_up_enable + * `-- schmitt_trig_enable + * + * Usage examples: + * 1) Commands to set pin 5' drive_strength to 7: + * echo 5 > ./pin + * echo 7 > ./drive_strength + * 2) Commands to get pin 5' drive_strength: + * echo 5 > ./pin (this can be omitted when following a set command) + * cat ./drive_strength + * Result will be showed on the console. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pinctrl-adi.h" +#include "pinctrl-adrv906x-init-tbl.h" +#include "../core.h" + +struct adrv906x_pinctrl_driver { + struct device *dev; + int config; + int drive_strength; + int schmitt_trig_enable; + int pin_pull_enablement; + int pin_pull_up_enable; + int mux_sel; +}; + +typedef enum { + CONFIG = 0, + DRIVE_STRENGTH = 1, + SCHMITT_TRIG_ENABLE = 2, + PIN_PULL_ENABLEMENT = 3, + PIN_PULL_UP_ENABLE = 4, + MUX_SEL = 5 +} store_type_t; + +struct adrv906x_pinconf_state { + struct pinctrl *p; + struct pinctrl_state *s; +}; + +static int pin; + +static int adrv906x_pinctrl_get_config_direct(const char *dev_name, unsigned pin, unsigned long *configs, size_t nconfigs) +{ + const struct pinconf_ops *ops; + int ret; + + struct pinctrl_dev *pctldev = get_pinctrl_dev_from_devname(dev_name); + + if (!pctldev) + return -EPROBE_DEFER; + + mutex_lock(&pctldev->mutex); + ops = pctldev->desc->confops; + if (!ops || !ops->pin_config_set) { + mutex_unlock(&pctldev->mutex); + return -ENOTSUPP; + } + + ret = ops->pin_config_get(pctldev, pin, configs); + mutex_unlock(&pctldev->mutex); + + return ret; +} + +static int adrv906x_pinctrl_set_config_direct(const char *dev_name, unsigned pin, unsigned long *configs, size_t nconfigs) +{ + const struct pinconf_ops *ops; + int ret; + + struct pinctrl_dev *pctldev = get_pinctrl_dev_from_devname(dev_name); + + if (!pctldev) + return -EPROBE_DEFER; + + mutex_lock(&pctldev->mutex); + ops = pctldev->desc->confops; + if (!ops || !ops->pin_config_set) { + mutex_unlock(&pctldev->mutex); + return -ENOTSUPP; + } + + ret = ops->pin_config_set(pctldev, pin, configs, nconfigs); + mutex_unlock(&pctldev->mutex); + + return ret; +} + +static ssize_t adrv906x_pinctrl_common_store(struct device *dev, const char *buf, size_t count, store_type_t st) +{ + int result = 0, scan_count = 0, config_val; + struct adi_pin_mio conf = { 0 }; + + struct adrv906x_pinctrl_driver *adrv906x_pinctrl_drv = dev_get_drvdata(dev); + + if (!adrv906x_pinctrl_drv) + return -EIO; + + scan_count = sscanf(buf, "%d", &config_val); + if (scan_count == 1) { + conf.input_pin = pin; + result = adrv906x_pinctrl_get_config_direct(dev_name(dev), pin, (unsigned long *)&conf, 1); + if (result) { + pr_err("getting config failed\n"); + return -EIO; + } + + switch (st) { + case CONFIG: + conf.config = (unsigned long)config_val; + break; + case DRIVE_STRENGTH: + conf.config &= (~ADI_CONFIG_DRIVE_STRENGTH_MASK); + conf.config |= config_val & ADI_CONFIG_DRIVE_STRENGTH_MASK; + break; + case SCHMITT_TRIG_ENABLE: + conf.config &= (~ADI_CONFIG_SCHMITT_TRIG_ENABLE_MASK); + if (config_val & 0x1) + conf.config |= ADI_CONFIG_SCHMITT_TRIG_ENABLE_MASK; + break; + case PIN_PULL_ENABLEMENT: + conf.config &= (~ADI_CONFIG_PULL_UP_DOWN_ENABLEMENT_MASK); + if (config_val & 0x1) + conf.config |= ADI_CONFIG_PULL_UP_DOWN_ENABLEMENT_MASK; + break; + case PIN_PULL_UP_ENABLE: + conf.config &= (~ADI_CONFIG_PULLUP_ENABLE_MASK); + if (config_val & 0x1) + conf.config |= ADI_CONFIG_PULLUP_ENABLE_MASK; + break; + case MUX_SEL: + conf.mux_sel = config_val & ADI_CONFIG_MUX_SEL_MASK; + break; + } + + result = adrv906x_pinctrl_set_config_direct(dev_name(dev), pin, (unsigned long *)&conf, 1); + if (result == 0) { + switch (st) { + case CONFIG: + adrv906x_pinctrl_drv->config = config_val; + break; + case DRIVE_STRENGTH: + adrv906x_pinctrl_drv->drive_strength = config_val & ADI_CONFIG_DRIVE_STRENGTH_MASK; + break; + case SCHMITT_TRIG_ENABLE: + adrv906x_pinctrl_drv->schmitt_trig_enable = config_val & 0x1; + break; + case PIN_PULL_ENABLEMENT: + adrv906x_pinctrl_drv->pin_pull_enablement = config_val & 0x1; + break; + case PIN_PULL_UP_ENABLE: + adrv906x_pinctrl_drv->pin_pull_up_enable = config_val & 0x1; + break; + case MUX_SEL: + adrv906x_pinctrl_drv->mux_sel = config_val & ADI_CONFIG_MUX_SEL_MASK; + break; + } + return count; + } else { + pr_err("Set pinconfig failed!\n"); + return -EIO; + } + } else { + pr_err("invalid input\n"); + return -EINVAL; + } +} + +static ssize_t adrv906x_pinctrl_drive_strength_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adrv906x_pinctrl_driver *adrv906x_pinctrl_drv = dev_get_drvdata(dev); + struct adi_pin_mio conf = { 0 }; + int result = 0; + + if (!adrv906x_pinctrl_drv) + return -EIO; + + conf.input_pin = pin; + result = adrv906x_pinctrl_get_config_direct(dev_name(dev), pin, (unsigned long *)&conf, 1); + if (result == 0) + return snprintf(buf, sizeof(buf) - 1, "%d\n", (unsigned int)(conf.config & ADI_CONFIG_DRIVE_STRENGTH_MASK)); + else + return -EIO; +} + +static ssize_t adrv906x_pinctrl_drive_strength_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + return adrv906x_pinctrl_common_store(dev, buf, count, DRIVE_STRENGTH); +} + +static ssize_t adrv906x_pinctrl_pin_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return snprintf(buf, sizeof(buf) - 1, "%d\n", pin); +} + +static ssize_t adrv906x_pinctrl_pin_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int tmp_pin = 0, result = 0; + + result = sscanf(buf, "%du", &tmp_pin); + if (result == 1) { + if (tmp_pin >= 0 && tmp_pin <= ADI_ADRV906X_PIN_COUNT) { + pin = tmp_pin; + } else { + pr_err("Failed in %s, setting for pin = %d is out of range!\n", __func__, tmp_pin); + return -EINVAL; + } + return count; + } else { + return -EIO; + } +} + +#define ADRV906X_DEVICE_ATTR(_name) \ + DEVICE_ATTR(_name, S_IRUGO | S_IWUSR, adrv906x_pinctrl_ ## _name ## _show, adrv906x_pinctrl_ ## _name ## _store) +static ADRV906X_DEVICE_ATTR(drive_strength); +static ADRV906X_DEVICE_ATTR(pin); + +#ifdef ADRV906X_PINCTRL_MORE_CONFIGS_OPT_IN +static ADRV906X_DEVICE_ATTR(config); +static ADRV906X_DEVICE_ATTR(schmitt_trig_enable); +static ADRV906X_DEVICE_ATTR(pin_pull_enablement); +static ADRV906X_DEVICE_ATTR(pin_pull_up_enable); +static ADRV906X_DEVICE_ATTR(mux_sel); + +static ssize_t adrv906x_pinctrl_config_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adrv906x_pinctrl_driver *adrv906x_pinctrl_drv = dev_get_drvdata(dev); + + if (!adrv906x_pinctrl_drv) + return -EIO; + + return snprintf(buf, sizeof(buf) - 1, "0x%x\n", adrv906x_pinctrl_drv->config); +} + +static ssize_t adrv906x_pinctrl_config_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + return adrv906x_pinctrl_common_store(dev, buf, count, CONFIG); +} + +static ssize_t adrv906x_pinctrl_schmitt_trig_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adrv906x_pinctrl_driver *adrv906x_pinctrl_drv = dev_get_drvdata(dev); + + if (!adrv906x_pinctrl_drv) + return -EIO; + + return snprintf(buf, sizeof(buf) - 1, "0x%x\n", adrv906x_pinctrl_drv->schmitt_trig_enable); +} + +static ssize_t adrv906x_pinctrl_schmitt_trig_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + return adrv906x_pinctrl_common_store(dev, buf, count, SCHMITT_TRIG_ENABLE); +} + +static ssize_t adrv906x_pinctrl_pin_pull_enablement_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adrv906x_pinctrl_driver *adrv906x_pinctrl_drv = dev_get_drvdata(dev); + + if (!adrv906x_pinctrl_drv) + return -EIO; + + return snprintf(buf, sizeof(buf) - 1, "0x%x\n", adrv906x_pinctrl_drv->pin_pull_enablement); +} + +static ssize_t adrv906x_pinctrl_pin_pull_enablement_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + return adrv906x_pinctrl_common_store(dev, buf, count, PIN_PULL_ENABLEMENT); +} + +static ssize_t adrv906x_pinctrl_pin_pull_up_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adrv906x_pinctrl_driver *adrv906x_pinctrl_drv = dev_get_drvdata(dev); + + if (!adrv906x_pinctrl_drv) + return -EIO; + + return snprintf(buf, sizeof(buf) - 1, "0x%x\n", adrv906x_pinctrl_drv->pin_pull_up_enable); +} + +static ssize_t adrv906x_pinctrl_pin_pull_up_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + return adrv906x_pinctrl_common_store(dev, buf, count, PIN_PULL_UP_ENABLE); +} + +static ssize_t adrv906x_pinctrl_mux_sel_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adrv906x_pinctrl_driver *adrv906x_pinctrl_drv = dev_get_drvdata(dev); + + if (!adrv906x_pinctrl_drv) + return -EIO; + + return snprintf(buf, sizeof(buf) - 1, "0x%x\n", adrv906x_pinctrl_drv->mux_sel); +} + +static ssize_t adrv906x_pinctrl_mux_sel_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + return adrv906x_pinctrl_common_store(dev, buf, count, MUX_SEL); +} +#endif + +static struct attribute *adrv906x_pinctrl_attrs[] = { + &dev_attr_drive_strength.attr, + &dev_attr_pin.attr, +#ifdef ADRV906X_PINCTRL_MORE_CONFIGS_OPT_IN + &dev_attr_config.attr, + &dev_attr_schmitt_trig_enable.attr, + &dev_attr_pin_pull_enablement.attr, + &dev_attr_pin_pull_up_enable.attr, + &dev_attr_mux_sel.attr, +#endif + NULL +}; + +static struct attribute_group adrv906x_pinctrl_group = { + .name = "control", + .attrs = adrv906x_pinctrl_attrs, +}; + +static struct attribute_group *adrv906x_pinctrl_groups[] = { + &adrv906x_pinctrl_group, + NULL +}; + +static const struct of_device_id adi_adrv906x_pinctrl_of_match[] = { + { .compatible = "adi,adrv906x-pinctrl", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, adi_adrv906x_pinctrl_of_match); + +static struct adi_pinctrl_soc_info adi_adrv906x_pinctrl_info = { + .pins = adi_adrv906x_pinctrl_pads, + .npins = ARRAY_SIZE(adi_adrv906x_pinctrl_pads), + .adi_pinconf_get = adi_pinconf_get_smc, + .adi_pinconf_set = adi_pinconf_set_smc, + .adi_pinctrl_parse_pin = NULL, +}; + +static int adi_adrv906x_pinctrl_probe(struct platform_device *pdev) +{ + struct adrv906x_pinctrl_driver *adrv906x_pinctrl_drv; + int ret; + + ret = adi_pinctrl_probe(pdev, &adi_adrv906x_pinctrl_info); + if (ret) + return ret; + + adrv906x_pinctrl_drv = devm_kzalloc(&pdev->dev, sizeof(*adrv906x_pinctrl_drv), GFP_KERNEL); + adrv906x_pinctrl_drv->dev = &pdev->dev; + platform_set_drvdata(pdev, adrv906x_pinctrl_drv); + + ret = sysfs_create_groups(&pdev->dev.kobj, (const struct attribute_group **)adrv906x_pinctrl_groups); + if (ret) { + dev_err(&pdev->dev, "sysfs creation failed\n"); + return ret; + } + + return ret; +} + +static void adi_adrv906x_pinctrl_remove(struct platform_device *pdev) +{ + sysfs_remove_groups(&pdev->dev.kobj, (const struct attribute_group **)adrv906x_pinctrl_groups); +} + +static struct platform_driver adi_adrv906x_pinctrl_driver = { + .driver ={ + .name = "adrv906x-pinctrl", + .of_match_table = of_match_ptr(adi_adrv906x_pinctrl_of_match), + .suppress_bind_attrs = true, + }, + .probe = adi_adrv906x_pinctrl_probe, + .remove = adi_adrv906x_pinctrl_remove, +}; + +static int __init adi_adrv906x_pinctrl_init(void) +{ + pin = 0; + return platform_driver_register(&adi_adrv906x_pinctrl_driver); +} +arch_initcall(adi_adrv906x_pinctrl_init); + +MODULE_AUTHOR("Howard Massey "); +MODULE_DESCRIPTION("ADI ADRV906X pinctrl driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/adi/pinctrl-smc.c b/drivers/pinctrl/adi/pinctrl-smc.c new file mode 100644 index 00000000000000..cec8cff1578d40 --- /dev/null +++ b/drivers/pinctrl/adi/pinctrl-smc.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2022 ~ 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "pinctrl-adi.h" + +/* PINCTRL Function ID*/ +#define ADI_PINCTRL_SIP_SERVICE_FUNCTION_ID 0xC2000001 + +/* ADI Pinctrl SIP Service Functions*/ +#define ADI_PINCTRL_INIT (0U) +#define ADI_PINCTRL_SET (1U) +#define ADI_PINCTRL_GET (2U) + +#define MASK_AND_SHIFT(value, mask, shift) ((value & mask) >> shift) + +/* SMC Handler return Status Values (res.a0 return value) */ +#define ADI_PINCTRL_SMC_RETURN_SUCCESS (0U) +#define ADI_PINCTRL_SMC_RETURN_UNSUPPORTED_REQUEST (0xFFFFFFFFFFFFFFFFU) + +/* SMC Pinctrl Handler return values (res.a1 return value) */ +#define ADI_TFA_PINCTRL_HANDLER_FAILURE (0U) +#define ADI_TFA_PINCTRL_HANDLER_SUCCESS (1U) + +/* SMC Config Bitfield Config Word */ +#define ADI_BITFIELD_ST_BIT_POSITION (0U) +#define ADI_BITFIELD_PULL_ENABLEMENT_BIT_POSITION (1U) +#define ADI_BITFIELD_PULLUP_ENABLE_BIT_POSITION (2U) + +/* SMC GET result defines*/ +#define ADI_GET_BITFIELD_1_PIN_CONFIGURED_BIT_POSITION (63U) + +int adi_pinconf_get_smc(struct pinctrl_dev *pctldev, unsigned int pin_id, + unsigned long *configs) +{ + struct arm_smccc_res res; + struct adi_pinctrl *ipctl; + unsigned int drive_strength; + unsigned int schmitt_trig_enable; + unsigned int pin_pull_enablement; + unsigned int pin_pull_up_enable; + const struct adi_pin_reg *pin_reg; + struct adi_pin_mio *adi_pin = (struct adi_pin_mio *)configs; + + if (!pctldev || !configs) + return -EINVAL; + + ipctl = pinctrl_dev_get_drvdata(pctldev); + pin_reg = &ipctl->pin_regs[pin_id]; + + if (pin_reg->conf_reg == -1) + return -EINVAL; + + /* + * Setup smc call to perform the pinconf_get operation + * + * arm_smccc_smc expected params: + * param1: SMC SIP SERVICE ID + * param2: ADI Pinctrl request (GET, SET, INIT) + * param3: Pin Number requested + * param4: Currently UNUSED/UNDEFINED + * param5: Currently UNUSED/UNDEFINED + * param6: Currently UNUSED/UNDEFINED + * param7: Currently UNUSED/UNDEFINED + * param8: Currently UNUSED/UNDEFINED + * param9: response output of the SMC call + * a0= SMC return value + * a1= ADI TFA Pinctrl Handler return status + * a2= 64bit RESPONSE_BITFIELD_1 {PIN_CONFIGURED a2[63], //0=NOT Configured, 1=Configured + * undefined a2[62:32], //Undefined + * PIN# a2[31:16], //The requested Pin # (valid if PIN_CONFIGURED=1) + * SourceMuxSetting a2[15:0]} //The source mux setting (valid if PIN_CONFIGURED=1) + * a3= 64bit RESPONSE_BITFIELD_2 {undefined a3[63:19], + * '3bit field' (SchmittTrigEnable | PU PD Enablement | PU Enable) a3[6:4] //(valid if PIN_CONFIGURED=1) + * DriveStrength a3[3:0]} //(valid only if PIN_CONFIGURED=1) + * + */ + arm_smccc_smc(ADI_PINCTRL_SIP_SERVICE_FUNCTION_ID, + ADI_PINCTRL_GET, + pin_reg->pin_num, + 0, 0, 0, ipctl->phys_addr, 0, + &res); + + /* + * The SMC call return status is present in res.a0, + * the pinctrl TFA Handler is present in res.a1 + */ + if (res.a0 != ADI_PINCTRL_SMC_RETURN_SUCCESS || res.a1 != ADI_TFA_PINCTRL_HANDLER_SUCCESS) + return -EINVAL; + + /* + * Here we output the received mux settings {3-bit field} , drivestrength + */ + if ((res.a2 & BIT(ADI_GET_BITFIELD_1_PIN_CONFIGURED_BIT_POSITION)) == 0) { + adi_pin->config = 0U; + } else { + drive_strength = res.a3 & ADI_CONFIG_DRIVE_STRENGTH_MASK; + schmitt_trig_enable = res.a3 & ADI_CONFIG_SCHMITT_TRIG_ENABLE_MASK; + pin_pull_enablement = res.a3 & ADI_CONFIG_PULL_UP_DOWN_ENABLEMENT_MASK; + pin_pull_up_enable = res.a3 & ADI_CONFIG_PULLUP_ENABLE_MASK; + adi_pin->mux_sel = res.a2 & ADI_CONFIG_MUX_SEL_MASK; + + adi_pin->config = drive_strength; + if (schmitt_trig_enable) + adi_pin->config |= BIT(ADI_CONFIG_SCHMITT_TRIG_ENABLE_MASK_BIT_POSITION); + if (pin_pull_enablement) + adi_pin->config |= BIT(ADI_CONFIG_PULL_UP_DOWN_ENABLEMENT_MASK_BIT_POSITION); + if (pin_pull_up_enable) + adi_pin->config |= BIT(ADI_CONFIG_PULLUP_ENABLE_MASK_BIT_POSITION); + } + + return 0; +} + +int adi_pinconf_set_smc(struct pinctrl_dev *pctldev, unsigned int pin_id, + unsigned long *configs, unsigned int num_configs) +{ + struct arm_smccc_res res; + struct adi_pinctrl *ipctl; + int drive_strength; + int schmitt_trig_enable; + int pin_pull_enablement; + int pin_pull_up_enable; + int config_bitfield; + unsigned int pin_num; + unsigned int mux_sel; + unsigned long config; + + if (!pctldev) + return -EINVAL; + + ipctl = pinctrl_dev_get_drvdata(pctldev); + + pin_num = ((struct adi_pin_mio *)(configs))->input_pin; + mux_sel = ((struct adi_pin_mio *)(configs))->mux_sel; + config = ((struct adi_pin_mio *)(configs))->config; + + /* + * Setup smc call to perform the pinconf_set operation + * + * arm_smccc_smc expected params: + * param1: SMC SIP SERVICE ID + * param2: ADI Pinctrl request (GET, SET, INIT) + * param3: Pin Number requested + * param4: Source Mux setting requested + * param5: Drive Strength + * param6: BIT_FIELD-3bits-(SchmittTrigEnable | PU PD Enablement | PU Enable) + * param7: Base Address of Pinctrl + * param8: Currently UNUSED/UNDEFINED + * param9: response output of the SMC call + * a0 = SMC return value + * a1 = ADI TFA Pinctrl Handler return status + * a2 = ADI unused + * a3 = ADI unused + */ + + drive_strength = config & ADI_CONFIG_DRIVE_STRENGTH_MASK; + schmitt_trig_enable = (config & ADI_CONFIG_SCHMITT_TRIG_ENABLE_MASK) ? 1 : 0; + pin_pull_enablement = (config & ADI_CONFIG_PULL_UP_DOWN_ENABLEMENT_MASK) ? 1 : 0; + pin_pull_up_enable = (config & ADI_CONFIG_PULLUP_ENABLE_MASK) ? 1 : 0; + config_bitfield = (schmitt_trig_enable << ADI_BITFIELD_ST_BIT_POSITION) | + (pin_pull_enablement << ADI_BITFIELD_PULL_ENABLEMENT_BIT_POSITION) | + (pin_pull_up_enable << ADI_BITFIELD_PULLUP_ENABLE_BIT_POSITION); + + arm_smccc_smc(ADI_PINCTRL_SIP_SERVICE_FUNCTION_ID, + ADI_PINCTRL_SET, + pin_num, + mux_sel, + drive_strength, + config_bitfield, + ipctl->phys_addr, 0, &res); + + + /* + * The SMC call return status is present in res.a0, + * the pinctrl TFA Handler is present in res.a1 + */ + if (res.a0 != ADI_PINCTRL_SMC_RETURN_SUCCESS || res.a1 != ADI_TFA_PINCTRL_HANDLER_SUCCESS) + return -EINVAL; + + return 0; +} + +MODULE_AUTHOR("Howard Massey "); +MODULE_DESCRIPTION("ADI common SMC pinctrl driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig index 604541dcb32065..9722232791f908 100644 --- a/drivers/ptp/Kconfig +++ b/drivers/ptp/Kconfig @@ -39,6 +39,28 @@ config PTP_1588_CLOCK_OPTIONAL If PTP support is disabled, this dependency will still be met, and drivers refer to dummy helpers. +config PTP_1588_CLOCK_ADRV906X + tristate "ToD counter for ADRV906X" + depends on PTP_1588_CLOCK + help + This driver adds support for using the hardware ToD module + in the ADRV906X SoC as a PHC clock to get/set timestamps. + + To compile this driver as a module, choose M here: the module + will be called adrv906x_ptp_tod. + +config PTP_1588_CLOCK_ADRV906X_SOC + tristate "PTP clock for ADRV906X" + depends on PTP_1588_CLOCK + select PTP_1588_CLOCK_ADRV906X + help + This in an example driver that adds support for using the hardware + ToD module in the ADRV906X SoC in combination with a clock chip to + have a full PHC implementation. + + To compile this driver as a module, choose M here: the module + will be called adrv906x_ptp_soc. + config PTP_1588_CLOCK_DTE tristate "Broadcom DTE as PTP clock" depends on PTP_1588_CLOCK diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile index 68bf02078053b8..55550ed70b6082 100644 --- a/drivers/ptp/Makefile +++ b/drivers/ptp/Makefile @@ -7,6 +7,8 @@ ptp-y := ptp_clock.o ptp_chardev.o ptp_sysfs.o ptp_vclock.o ptp_kvm-$(CONFIG_X86) := ptp_kvm_x86.o ptp_kvm_common.o ptp_kvm-$(CONFIG_HAVE_ARM_SMCCC) := ptp_kvm_arm.o ptp_kvm_common.o obj-$(CONFIG_PTP_1588_CLOCK) += ptp.o +obj-$(CONFIG_PTP_1588_CLOCK_ADRV906X)+= ptp_adrv906x_tod.o +obj-$(CONFIG_PTP_1588_CLOCK_ADRV906X_SOC)+= ptp_adrv906x_tod.o ptp_adrv906x_soc.o obj-$(CONFIG_PTP_1588_CLOCK_DTE) += ptp_dte.o obj-$(CONFIG_PTP_1588_CLOCK_INES) += ptp_ines.o obj-$(CONFIG_PTP_1588_CLOCK_PCH) += ptp_pch.o diff --git a/drivers/ptp/ptp_adrv906x_soc.c b/drivers/ptp/ptp_adrv906x_soc.c new file mode 100644 index 00000000000000..203727cf4ab094 --- /dev/null +++ b/drivers/ptp/ptp_adrv906x_soc.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ptp_private.h" +#include "ptp_adrv906x_tod.h" + +MODULE_DESCRIPTION("Example driver for integrating the time-of-day in adrv906x to work with a clock pll"); +MODULE_AUTHOR("Landau Zhang "); +MODULE_AUTHOR("Kim Holdt "); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); + +struct adrv906x_hw_pll; + +struct phc_pll_ops { + int (*adjfine)(struct adrv906x_hw_pll *phc_clk, long scaled_ppm); + int (*adjfreq)(struct adrv906x_hw_pll *phc_clk, s32 delta); + int (*close)(struct adrv906x_hw_pll *phc_clk); +}; + +struct phc_pll_i2c_attr { + u32 bus_addr; + struct i2c_adapter *adpt; +}; + +struct adrv906x_hw_pll { + long scaled_ppm; + struct phc_pll_i2c_attr pll_i2c; + spinlock_t reg_lock; + struct phc_pll_ops pll_ops; +}; + +struct adrv906x_phc_pll { + struct device *dev; + struct adrv906x_hw_pll hw_pll; +}; + +#define AD9545_I2C_BUF_SIZE 128 + +#define AD9545_ADDR_IO_UPDATE 0x00F +#define AD9545_ADDR_NCO0_CENTER_FREQ_LSB 0x2800 +#define AD9545_ADDR_MAX 0x3A3F + +#define ADDR_NCO0_CENTER_FREQ_CNT 7 + +#define ADRV906X_PHC_NCO_FREQ_TO_HZ(freq) (freq >> 40) +#define ADRV906X_PHC_PPB_TO_PPT(ppb) (ppb * 1000) + +static int adrv906x_pll_i2c_read(struct adrv906x_hw_pll *hw_pll, u16 addr, u8 *buffer, size_t len) +{ + struct phc_pll_i2c_attr *pll_i2c = &hw_pll->pll_i2c; + struct i2c_msg msg; + int ret = -EIO; + int rc = 0; + u8 temp[2]; + + if ((addr + (u16)len) >= AD9545_ADDR_MAX) + return -EINVAL; + + temp[0] = (addr >> 8) & 0xFF; + temp[1] = addr & 0xFF; + + msg.addr = pll_i2c->bus_addr; + msg.flags = 0; + msg.len = sizeof(temp); + msg.buf = temp; + + rc = i2c_transfer(pll_i2c->adpt, &msg, 1); + if (rc == 1) + ret = 0; + else if (rc > 0) + return -EREMOTEIO; + else + return rc; + + msg.addr = pll_i2c->bus_addr; + msg.flags = I2C_M_RD; + msg.len = len; + msg.buf = buffer; + + rc = i2c_transfer(pll_i2c->adpt, &msg, 1); + if (rc == 1) + ret = 0; + else if (rc > 0) + return -EREMOTEIO; + else + return rc; + + return ret; +} + +static int adrv906x_pll_i2c_write(struct adrv906x_hw_pll *hw_pll, u16 addr, const u8 *buffer, size_t len) +{ + struct phc_pll_i2c_attr *pll_i2c = &hw_pll->pll_i2c; + struct i2c_msg msg; + int ret = -EIO; + int rc = 0; + int i; + u8 temp[ADDR_NCO0_CENTER_FREQ_CNT + 2]; + + if (((addr + (u16)len) >= AD9545_ADDR_MAX) || (len >= AD9545_I2C_BUF_SIZE - 2)) + return -EINVAL; + + temp[0] = (addr >> 8) & 0xFF; + temp[1] = addr & 0xFF; + for (i = 2; i < sizeof(temp); i++) + temp[i] = buffer[i - 2]; + + msg.addr = pll_i2c->bus_addr; + msg.flags = 0; + msg.len = sizeof(temp); + msg.buf = temp; + + rc = i2c_transfer(pll_i2c->adpt, &msg, 1); + if (rc == 1) + ret = 0; + else if (rc > 0) + return -EREMOTEIO; + else + return rc; + + return ret; +} + +static int adrv906x_pll_sync_ad9545(struct adrv906x_hw_pll *hw_pll) +{ + u8 sync = 0x01; + + return adrv906x_pll_i2c_write(hw_pll, AD9545_ADDR_IO_UPDATE, &sync, 1); +} + +static int adrv906x_pll_get_freq_ad9545(struct adrv906x_hw_pll *hw_pll, u64 *freq) +{ + u8 data_bytes[8]; + int i; + int ret = + adrv906x_pll_i2c_read(hw_pll, AD9545_ADDR_NCO0_CENTER_FREQ_LSB, + data_bytes, ADDR_NCO0_CENTER_FREQ_CNT); + + *freq = 0; + + for (i = 0; i < 8; i++) + *freq |= ((u64)data_bytes[i] << (8 * i)); + + return ret; +} + +static int adrv906x_pll_set_freq_ad9545(struct adrv906x_hw_pll *hw_pll, u64 freq) +{ + u8 data_bytes[ADDR_NCO0_CENTER_FREQ_CNT + 1]; + int i; + + for (i = 0; i < ADDR_NCO0_CENTER_FREQ_CNT; i++) + data_bytes[i] = (freq >> (8 * i)) & 0xFF; + + return adrv906x_pll_i2c_write(hw_pll, AD9545_ADDR_NCO0_CENTER_FREQ_LSB, + data_bytes, ADDR_NCO0_CENTER_FREQ_CNT); +} + +/* + * @brief Adjusts the frequency of the hardware clock. + * + * This function should be implemented by the user and depend on the clock chip used + * param: + * adrv906x_hw_pll - hardware clock operation information structure + * scaled_ppm - Desired frequency offset from nominal frequency in parts per million, but with a + * 16 bit binary fractional field. + */ +static int adrv906x_pll_adjfine_ad9545(struct adrv906x_hw_pll *hw_pll, long scaled_ppm) +{ + int neg_adj = 0; + static u64 org_freq; + u64 updated_freq; + u64 tar_freq; + u64 freq_hz; + u64 adj; + s64 ppt; + s32 ppb; + int err; + + ppb = scaled_ppm_to_ppb(scaled_ppm); + if (ppb < 0) { + neg_adj = 1; + ppb = -ppb; + } + + /* + * Get the original NCO value which is a 7 * 8(bit) = 56(bit) value + * Center frequency of AUX-PLL in ad9545: + * --------------------------------------------------------------- + * | 55 | ... | 40 | 39 | .... | 0 | + * | -> INTEGER Hz <- | -> FRACTIONAL HZ <- | + * | | + * 1 Hz 2^(-40) Hz + */ + err = adrv906x_pll_get_freq_ad9545(hw_pll, &updated_freq); + updated_freq &= ~__GENMASK(63, 56); /* Zero out the 8 most significant bits. */ + if (err) + return err; + if (ADRV906X_PHC_NCO_FREQ_TO_HZ(abs(updated_freq - org_freq)) > + ADRV906X_PHC_NCO_FREQ_TO_HZ(updated_freq) - 1) + org_freq = updated_freq; /* Keeping only the base frequency for updates. */ + + /* Convert ppb to ppt so that we can easily calculate the offset frequency value */ + ppt = ADRV906X_PHC_PPB_TO_PPT(ppb); + + freq_hz = ADRV906X_PHC_NCO_FREQ_TO_HZ(org_freq); /* Get in the HZ */ + adj = freq_hz * ppt; /* Find requested adjustment in ppt - i.e. LSB corresponds to approximately 2^(-40) Hz */ + + /* Update the target NCO value */ + if (neg_adj == 1) + tar_freq = org_freq - adj; + else + tar_freq = org_freq + adj; + + /* adjust the frequency */ + err = adrv906x_pll_set_freq_ad9545(hw_pll, tar_freq); + if (err) + return err; + + err = adrv906x_pll_sync_ad9545(hw_pll); + + return err; +} + +static int adrv906x_pll_get_adapter(struct adrv906x_hw_pll *hw_pll) +{ + struct adrv906x_phc_pll *pll_phc = container_of(hw_pll, struct adrv906x_phc_pll, hw_pll); + struct device *dev = pll_phc->dev; + struct device_node *i2c_pll_node; + struct device_node *i2c_mux_node; + struct device_node *pll_np; + + pll_np = of_get_child_by_name(dev->of_node, "clock-pll"); + if (!pll_np) + return -ENODEV; + + i2c_pll_node = of_parse_phandle(pll_np, "adi,i2c-clk", 0); + if (!i2c_pll_node) { + dev_err(dev, "No clk node is found"); + return -EINVAL; + } + of_property_read_u32(i2c_pll_node, "reg", &hw_pll->pll_i2c.bus_addr); + + i2c_mux_node = of_get_parent(i2c_pll_node); + if (!i2c_mux_node) { + dev_err(dev, "No parent device node of clk node is found"); + of_node_put(i2c_pll_node); + return -EINVAL; + } + + hw_pll->pll_i2c.adpt = of_find_i2c_adapter_by_node(i2c_mux_node); + + of_node_put(i2c_pll_node); + of_node_put(i2c_mux_node); + + if (!hw_pll->pll_i2c.adpt) { + dev_err(dev, "No adapter of the clk node is found"); + return -ENODEV; + } + + return 0; +} + +static int adrv906x_pll_i2c_probe(struct adrv906x_hw_pll *hw_pll) +{ + struct adrv906x_phc_pll *pll_phc = container_of(hw_pll, struct adrv906x_phc_pll, hw_pll); + struct device *dev = pll_phc->dev; + struct device_node *iic_pll_node; + struct device_node *pll_np; + int ret; + + pll_np = of_get_child_by_name(dev->of_node, "clock-pll"); + if (!pll_np) + return -ENODEV; + + iic_pll_node = of_parse_phandle(pll_np, "adi,i2c-clk", 0); + + if (iic_pll_node) { + ret = adrv906x_pll_get_adapter(hw_pll); + } else { + dev_err(dev, "clk node not found"); + ret = -ENODEV; + } + + return ret; +} + +static int adrv906x_pll_i2c_remove(struct adrv906x_hw_pll *hw_pll) +{ + i2c_put_adapter(hw_pll->pll_i2c.adpt); + return 0; +} + +struct phc_pll_ops adrv906x_pll_ops = { + .adjfine = &adrv906x_pll_adjfine_ad9545, + .close = &adrv906x_pll_i2c_remove, +}; + +static int adrv906x_phc_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct adrv906x_tod_counter *counter = container_of(ptp, struct adrv906x_tod_counter, caps); + struct adrv906x_tod *tod = counter->parent; + struct platform_device *pdev = container_of(tod->dev, struct platform_device, dev); + struct adrv906x_phc_pll *pll_phc = platform_get_drvdata(pdev); + struct adrv906x_hw_pll *hw_pll = &pll_phc->hw_pll; + int err; + + if (hw_pll->pll_ops.adjfine) { + err = hw_pll->pll_ops.adjfine(hw_pll, scaled_ppm); + } else { + dev_err(pll_phc->dev, "function not supported"); + err = -EOPNOTSUPP; + } + return err; +} + +static struct ptp_clock_info adrv906x_pll_caps = { + .owner = THIS_MODULE, + .name = "adrv906x soc ptp", + .max_adj = 5000, + .adjfine = &adrv906x_phc_adjfine, +}; + +int adrv906x_phc_pll_probe(struct adrv906x_phc_pll *pll_phc) +{ + struct adrv906x_hw_pll *hw_pll = &pll_phc->hw_pll; + struct device *dev = pll_phc->dev; + struct device_node *pll_np; + struct device_node *np; + int ret; + + np = dev->of_node; + pll_np = of_get_child_by_name(np, "clock-pll"); + if (!pll_np) { + dev_err(dev, "miss clock pll device node"); + ret = -ENODEV; + goto probe_error; + } + + if (of_find_property(pll_np, "adi,i2c-clk", NULL)) { + ret = adrv906x_pll_i2c_probe(hw_pll); + if (ret == -ENODEV) { + ret = -EPROBE_DEFER; + dev_err(dev, "miss i2c clock device node"); + goto probe_error; + } + if (ret == 0) { + hw_pll->pll_ops = adrv906x_pll_ops; + goto probe_ok; + } + } else { + dev_err(dev, "No valid phc hardware clock chip"); + ret = -ENODEV; + goto probe_error; + } + +probe_ok: + if (ret == 0) + spin_lock_init(&hw_pll->reg_lock); +probe_error: + if (ret == -EPROBE_DEFER) + dev_err(dev, "No valid phc hardware clock chip, defer probing"); + else if (ret != 0) + dev_err(dev, "PHC pll clock probe error"); + else + dev_info(dev, "PHC pll clock probe ok"); + + return ret; +} + +int adrv906x_pll_remove(struct adrv906x_phc_pll *pll_phc) +{ + struct adrv906x_hw_pll *hw_pll = &pll_phc->hw_pll; + + if (hw_pll->pll_ops.close) + return hw_pll->pll_ops.close(hw_pll); + + return 0; +} + +static int adrv906x_ptp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct adrv906x_phc_pll *pll_phc; + int ret; + + pll_phc = devm_kzalloc(dev, sizeof(struct adrv906x_phc_pll), GFP_KERNEL); + if (!pll_phc) + return -ENOMEM; + + pll_phc->dev = dev; + + ret = adrv906x_tod_probe(pdev); + if (ret) + return ret; + + ret = adrv906x_phc_pll_probe(pll_phc); + if (ret) + goto err_out; + + ret = adrv906x_tod_register_pll(&adrv906x_pll_caps); + if (ret) + return ret; + + platform_set_drvdata(pdev, pll_phc); + return 0; + +err_out: + adrv906x_tod_remove(pdev); + return ret; +} + +static int adrv906x_ptp_remove(struct platform_device *pdev) +{ + struct adrv906x_phc_pll *pll_phc = platform_get_drvdata(pdev); + int ret; + + ret = adrv906x_tod_remove(pdev); + if (ret) + return ret; + + return adrv906x_pll_remove(pll_phc); +} + +static const struct of_device_id ptp_adrv906x_soc_of_match[] = { + { .compatible = "adi,adrv906x-ptp", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, ptp_adrv906x_soc_of_match); + +static struct platform_driver ptp_adrv906x_soc_driver = { + .driver = { + .name = "adrv906x-ptp", + .of_match_table = ptp_adrv906x_soc_of_match, + }, + .probe = adrv906x_ptp_probe, + .remove = adrv906x_ptp_remove, +}; + +module_platform_driver(ptp_adrv906x_soc_driver); diff --git a/drivers/ptp/ptp_adrv906x_tod.c b/drivers/ptp/ptp_adrv906x_tod.c new file mode 100644 index 00000000000000..9df6b92fc5f00f --- /dev/null +++ b/drivers/ptp/ptp_adrv906x_tod.c @@ -0,0 +1,1593 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ptp_private.h" +#include "ptp_adrv906x_tod.h" + +MODULE_DESCRIPTION("Driver for time-of-day module in adrv906x-based devices"); +MODULE_AUTHOR("Landau Zhang "); +MODULE_AUTHOR("Kim Holdt "); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); + +static struct adrv906x_tod *adrv906x_tod; + +#define TOD_FRAC_NANO_NUM 0x10000 +#define TOD_MAX_DELAY_COUNT 10 +#define TOD_PPSX_PULSE_WIDTH (10 * NSEC_PER_MSEC) + +#define ADRV906X_TOD_VERSION (0x4U) +#define ADRV906X_TOD_VERSION_MINOR GENMASK(15, 0) +#define ADRV906X_TOD_VERSION_MAJOR GENMASK(31, 16) +#define ADRV906X_TOD_CFG_INCR (0x10U) +#define ADRV906X_TOD_CFG_INCR_FRAC_NS_PER_CLK_MASK GENMASK(15, 0) +#define ADRV906X_TOD_CFG_INCR_NS_PER_CLK_MASK GENMASK(19, 16) +#define ADRV906X_TOD_CFG_INCR_CNT_CTRL_MASK GENMASK(26, 20) +#define ADRV906X_TOD_CFG_INCR_CFG_TOD_CNT_EN_MASK BIT(28) + +#define ADRV906X_TOD_IRQ_EVENT (0x14U) + +#define ADRV906X_TOD_IRQ_MASK (0x18U) +#define ADRV906X_TOD_IRQ_MASK_INTERNAL_0 BIT(0) +#define ADRV906X_TOD_IRQ_MASK_INTERNAL_1 BIT(1) +#define ADRV906X_TOD_IRQ_MASK_INTERNAL_GNSS BIT(2) +#define ADRV906X_TOD_IRQ_MASK_EXTERNAL_PPS BIT(3) +#define ADRV906X_TOD_IRQ_MASK_MASK GENMASK(3, 0) + +#define ADRV906X_TOD_IRQ_STATUS (0x1CU) + +#define ADRV906X_TOD_CFG_TOD_OP (0x20U) +#define ADRV906X_TOD_CFG_TOD_OP_WR_TOD_MASK GENMASK(2, 0) +#define ADRV906X_TOD_CFG_TOD_OP_RD_TOD_MASK GENMASK(6, 4) +#define ADRV906X_TOD_CFG_TOD_OP_WR_TOD_PPS_MASK GENMASK(10, 8) +#define ADRV906X_TOD_CFG_TOD_OP_RD_TOD_PPS_MASK GENMASK(14, 12) + +#define ADRV906X_TOD_CFG_TV_NSEC (0x24U) +#define ADRV906X_TOD_CFG_TV_NSEC_FRAC_NSEC_MASK GENMASK(15, 0) +#define ADRV906X_TOD_CFG_TV_NSEC_NSEC_MASK GENMASK(31, 16) + +#define ADRV906X_TOD_CFG_TV_SEC_0 (0x28U) +#define ADRV906X_TOD_CFG_TV_SEC_0_NSEC_MASK GENMASK(15, 0) +#define ADRV906X_TOD_CFG_TV_SEC_0_SEC_MASK GENMASK(31, 16) + +#define ADRV906X_TOD_CFG_TV_SEC_1 (0x2CU) +#define ADRV906X_TOD_CFG_TV_SEC_1_SEC_MASK GENMASK(31, 0) + +#define ADRV906X_TOD_CFG_OP_GC_VAL_0 (0x30U) +#define ADRV906X_TOD_CFG_OP_GC_VAL_1 (0x34U) + +#define ADRV906X_TOD_CFG_OP_GC (0x38U) +#define ADRV906X_TOD_CFG_OP_GC_RD_GC_MASK BIT(0) + +#define ADRV906X_TOD_CFG_IO_SOURCE (0x3CU) +#define ADRV906X_TOD_CFG_IO_PPS_OUT_SRC_MASK GENMASK(3, 0) +#define ADRV906X_TOD_CFG_IO_PPS_OUT_SRC_SEL(x) \ + FIELD_PREP(ADRV906X_TOD_CFG_IO_PPS_OUT_SRC_MASK, x) +#define ADRV906X_TOD_CFG_IO_TOD_OUT_SRC_MASK GENMASK(11, 8) +#define ADRV906X_TOD_CFG_IO_TOD_OUT_SRC_SEL(x) \ + FIELD_PREP(ADRV906X_TOD_CFG_IO_TOD_OUT_SRC_MASK, x) +#define ADRV906X_TOD_CFG_IO_WR_OUTPUT_CFG_MASK BIT(16) + +#define ADRV906X_TOD_CFG_IO_CTRL (0x40U) +#define ADRV906X_TOD_CFG_IO_CTRL_PPS_OUT_EN_MASK BIT(0) +#define ADRV906X_TOD_CFG_IO_CTRL_TOD_OUT_EN_MASK BIT(4) +#define ADRV906X_TOD_CFG_IO_CTRL_TOD_STAT_SEL_MASK GENMASK(30, 28) +#define ADRV906X_TOD_CFG_IO_CTRL_TOD_STAT_SEL(x) \ + FIELD_PREP(ADRV906X_TOD_CFG_IO_CTRL_TOD_STAT_SEL_MASK, x) + +#define ADRV906X_TOD_CFG_PPSX_START (0x44U) +#define ADRV906X_TOD_CFG_PPSX_STOP (0x48U) + +#define ADRV906X_TOD_CFG_TEST_OUT_SRC (0x4CU) +#define ADRV906X_TOD_CFG_TEST_OUT_SRC_PPSX_SRC_MASK GENMASK(31, 28) +#define ADRV906X_TOD_CFG_TEST_OUT_SRC_PPSX_SRC(x) \ + FIELD_PREP(ADRV906X_TOD_CFG_TEST_OUT_SRC_PPSX_SRC_MASK, x) + +#define ADRV906X_TOD_CFG_TSU_TOD (0x50U) +#define ADRV906X_TOD_CFG_TSU_TOD_MASK GENMASK(1, 0) + +#define ADRV906X_TOD_STAT_GC_0 (0x70U) +#define ADRV906X_TOD_STAT_GC_1 (0x74U) + +#define ADRV906X_TOD_STAT_TV_NSEC (0x78U) +#define ADRV906X_TOD_STAT_TV_FRAC_NSEC_MASK GENMASK(15, 0) +#define ADRV906X_TOD_STAT_TV_NSEC_NSEC_MASK GENMASK(31, 16) + +#define ADRV906X_TOD_STAT_TV_SEC_0 (0x7CU) +#define ADRV906X_TOD_STAT_TV_SEC_0_NSEC_MASK GENMASK(15, 0) +#define ADRV906X_TOD_STAT_TV_SEC_0_SEC_MASK GENMASK(31, 16) + +#define ADRV906X_TOD_STAT_TV_SEC_1 (0x80U) + +#define ADRV906X_TOD_STAT_TOD_OP (0x90U) +#define ADRV906X_TOD_STAT_TOD_OP_WR_TOD_MASK GENMASK(2, 0) +#define ADRV906X_TOD_STAT_TOD_OP_WR_TOD_SHIFT (0) +#define ADRV906X_TOD_STAT_TOD_OP_RD_TOD_MASK GENMASK(6, 4) +#define ADRV906X_TOD_STAT_TOD_OP_RD_TOD_SHIFT (4) +#define ADRV906X_TOD_STAT_TOD_OP_WR_TOD_PPS_MASK GENMASK(10, 8) +#define ADRV906X_TOD_STAT_TOD_OP_WR_TOD_PPS_SHIFT (8) +#define ADRV906X_TOD_STAT_TOD_OP_RD_TOD_PPS_MASK GENMASK(22, 20) +#define ADRV906X_TOD_STAT_TOD_OP_RD_TOD_PPS_SHIFT (12) + +#define ADRV906X_TOD_CFG_CDC_DELAY (0x100U) +#define ADRV906X_TOD_CFG_CDC_DELAY_CDC_MASK GENMASK(4, 0) + +int adrv906x_phc_index = -1; +EXPORT_SYMBOL(adrv906x_phc_index); + +int adrv906x_tod_cfg_cdc_delay = -1; +EXPORT_SYMBOL(adrv906x_tod_cfg_cdc_delay); + +struct adrv906x_tod_lc_clk_cfg adrv906x_lc_clk_cfg[HW_TOD_LC_CLK_FREQ_CNT] = { + [HW_TOD_LC_100_P_000_M] = { 100000U, 10U, 0x0000U, 0x00U }, + [HW_TOD_LC_122_P_880_M] = { 122880U, 8U, 0x2355U, 0x04U }, + [HW_TOD_LC_125_P_000_M] = { 125000U, 8U, 0x0000U, 0x00U }, + [HW_TOD_LC_156_P_250_M] = { 156250U, 6U, 0x6666U, 0x01U }, + [HW_TOD_LC_245_P_760_M] = { 245760U, 4U, 0x11AAU, 0x02U }, + [HW_TOD_LC_250_P_000_M] = { 250000U, 4U, 0x0000U, 0x00U }, + [HW_TOD_LC_312_P_500_M] = { 312500U, 3U, 0x3333U, 0x08U }, + [HW_TOD_LC_322_P_265_M] = { 322265U, 3U, 0x1A60U, 0x20U }, + [HW_TOD_LC_390_P_625_M] = { 390625U, 2U, 0x8F5CU, 0x10U }, + [HW_TOD_LC_491_P_520_M] = { 491520U, 2U, 0x08D5U, 0x04U }, + [HW_TOD_LC_500_P_000_M] = { 500000U, 2U, 0x0000U, 0x00U }, + [HW_TOD_LC_983_P_040_M] = { 983040U, 1U, 0x046AU, 0x02U } +}; + +/** + * @brief Configure the ToD IP for the system clock frequency + * @param counter Context struct + * @return 0 Success + * @return -EINVAL The chosen clock frequency isn't supported. + */ +static int adrv906x_tod_cfg_lc_clk(struct adrv906x_tod_counter *counter) +{ + struct adrv906x_tod *tod = counter->parent; + int err = -EINVAL; + u32 wr_val; + int lp; + + for (lp = 0; lp < HW_TOD_LC_CLK_FREQ_CNT; lp++) { + if (tod->lc_freq_khz == adrv906x_lc_clk_cfg[lp].freq_khz) { + wr_val = FIELD_PREP(ADRV906X_TOD_CFG_INCR_FRAC_NS_PER_CLK_MASK, + adrv906x_lc_clk_cfg[lp].frac_ns_per_clk) | + FIELD_PREP(ADRV906X_TOD_CFG_INCR_NS_PER_CLK_MASK, + adrv906x_lc_clk_cfg[lp].ns_per_clk) | + FIELD_PREP(ADRV906X_TOD_CFG_INCR_CNT_CTRL_MASK, + adrv906x_lc_clk_cfg[lp].cnt_ctrl); + ADRV906X_REG_WRITE_DUAL(tod, ADRV906X_TOD_CFG_INCR, wr_val); + err = 0; + break; + } + } + + return err; +} + +/** + * @brief Convert a kernel tstamp to HW format. + * @param tstamp HW tstamp container + * @param ts Kernel tstamp container + */ +static inline void timespec_to_tstamp(struct adrv906x_tod_tstamp *tstamp, + const struct timespec64 *ts) +{ + tstamp->nanoseconds = ts->tv_nsec; + tstamp->frac_nanoseconds = 0U; + tstamp->seconds = ts->tv_sec; +} + +/** + * @brief Convert a HW tstamp to kernel format. + * @param tstamp HW tstamp container + * @param ts Kernel tstamp container + */ +static inline void tstamp_to_timespec(struct timespec64 *ts, + const struct adrv906x_tod_tstamp *tstamp) +{ + ts->tv_sec = tstamp->seconds; + + if (tstamp->frac_nanoseconds < (TOD_FRAC_NANO_NUM / 2)) + ts->tv_nsec = tstamp->nanoseconds; + else + ts->tv_nsec = tstamp->nanoseconds + 1U; +} + +/** + * @brief Generate a command mask for the HW register + * @param op_flag HW operation + * @param is_pps Operational mode + * @param tod_idx Index of the ToD the operation should be for + * @return Returns the mask to use in the HW register + */ +static inline u32 adrv906x_tod_op_to_mask(u8 op_flag, bool is_pps, u32 tod_idx) +{ + u32 mask = 0U; + + if (op_flag == HW_TOD_TRIG_OP_WR) { + if (is_pps) + mask = FIELD_PREP(ADRV906X_TOD_CFG_TOD_OP_WR_TOD_PPS_MASK, tod_idx); + else + mask = FIELD_PREP(ADRV906X_TOD_CFG_TOD_OP_WR_TOD_MASK, tod_idx); + } else { + if (is_pps) + mask = FIELD_PREP(ADRV906X_TOD_CFG_TOD_OP_RD_TOD_PPS_MASK, tod_idx); + else + mask = FIELD_PREP(ADRV906X_TOD_CFG_TOD_OP_RD_TOD_MASK, tod_idx); + } + + return mask; +} + +/** + * @brief Obtain a Golden Count from HW for the given ToD + * @param counter Context struct for ToD counter + * @return Golden Count for given ToD counter + */ +static u64 adrv906x_tod_hw_gc_get_cnt(struct adrv906x_tod_counter *counter) +{ + struct adrv906x_tod *tod = counter->parent; + u32 gc_reg_cnt[2] = { 0U, 0U }; + u64 gc_cnt; + u32 gc_rd = 1U; + + /* Write the OP_GC:RD_GC_MASK to latch the GC counter register */ + ADRV906X_REG_WRITE(tod, ADRV906X_TOD_CFG_OP_GC, gc_rd); + ADRV906X_REG_WRITE(tod, ADRV906X_TOD_CFG_OP_GC, 0); + + /* Read back the Golden Counter */ + gc_reg_cnt[0] = ADRV906X_REG_READ(tod, ADRV906X_TOD_STAT_GC_0); + gc_reg_cnt[1] = ADRV906X_REG_READ(tod, ADRV906X_TOD_STAT_GC_1); + + gc_cnt = gc_reg_cnt[0] | ((u64)(gc_reg_cnt[1] & 0xFFFFU) << 32); + + return gc_cnt; +} + +/** + * @brief Write a Golden Count to the HW for the given ToD + * @param counter Context struct for ToD counter + * @param gc_cnt The Golden Count to write to HW + */ +static void adrv906x_tod_hw_gc_set_cnt(struct adrv906x_tod_counter *counter, u64 gc_cnt) +{ + struct adrv906x_tod *tod = counter->parent; + u32 gc_reg_cnt[2] = { 0U, 0U }; + + gc_reg_cnt[0] = gc_cnt & 0xFFFFFFFFU; + gc_reg_cnt[1] = (gc_cnt >> 32) & 0xFFFFU; + + /* Write the GC value */ + ADRV906X_REG_WRITE_DUAL(tod, ADRV906X_TOD_CFG_OP_GC_VAL_0, gc_reg_cnt[0]); + ADRV906X_REG_WRITE_DUAL(tod, ADRV906X_TOD_CFG_OP_GC_VAL_1, gc_reg_cnt[1]); +} + +/** + * @brief Helper function to clear the 'soft PPS' and trigger pending operations + * @param work Context struct + */ +static void adrv906x_tod_clear_soft_pps(struct work_struct *work) +{ + struct adrv906x_tod *tod = container_of(work, struct adrv906x_tod, pps_work.work); + + atomic_set(&tod->pps_state, 0); + + wake_up_all(&tod->pps_queue); +} + +/** + * @brief Trigger or clear the flag for an operation + * @note All the registers required for or provided by an operation must be written or read in + * advance of calling this function + * @param counter Context struct + * @param op_flag Choice of operation + * @param is_pps Non-zero if a PPS operation + * @param set_flag Non-zero to trigger operation, zero to clear it + */ +static void adrv906x_tod_hw_op_trig(struct adrv906x_tod_counter *counter, u8 op_flag, bool is_pps, + bool set_flag) +{ + struct adrv906x_tod *tod = counter->parent; + u8 tod_idx = BIT(counter->id); + u32 mask, val; + + mask = adrv906x_tod_op_to_mask(op_flag, is_pps, tod_idx); + + val = ADRV906X_REG_READ(tod, ADRV906X_TOD_CFG_TOD_OP); + if (set_flag) + val |= mask; + else + val &= ~mask; + ADRV906X_REG_WRITE_DUAL(tod, ADRV906X_TOD_CFG_TOD_OP, val); +} + +/** + * @brief Convenience function to set a trigger + * @param counter Context struct + * @param op_flag Operation selection + */ +static void adrv906x_tod_hw_op_trig_set(struct adrv906x_tod_counter *counter, u8 op_flag) +{ + adrv906x_tod_hw_op_trig(counter, op_flag, counter->trigger_mode, true); +} + +/** + * @brief Convenience function to clear a trigger + * @param counter Context struct + * @param op_flag Operation selection + */ +static void adrv906x_tod_hw_op_trig_clear(struct adrv906x_tod_counter *counter, u8 op_flag) +{ + adrv906x_tod_hw_op_trig(counter, op_flag, counter->trigger_mode, false); +} + +/** + * @brief Polls the provided register for the provided bit mask + * @param counter Context struct + * @param regaddr Address (offset from base) to poll + * @param bit_mask Bit mask to poll for + * @param p_delay Timespan to wait between each poll + * @param done_high If non-zero, the function matches for '1's at the bit mask's indices + * @return 0 Operation completed as expected + * @return -EAGAIN The trigger timed out + */ +static int adrv906x_tod_hw_op_poll_reg(struct adrv906x_tod_counter *counter, u32 regaddr, + u32 bit_mask, const struct adrv906x_tod_trig_delay *p_delay, + bool done_high) +{ + u32 delay_cnt = TOD_MAX_DELAY_COUNT; + u8 done = 0U; + int err = 0; + u32 val; + + while (!done && (delay_cnt != 0U)) { + ndelay(p_delay->ns); + val = ADRV906X_REG_READ(counter->parent, regaddr); + + if (!done_high) + val = ~val; + + done = (val & bit_mask) == bit_mask; + delay_cnt--; + } + + if (!done) { + dev_err(counter->parent->dev, + "trigger operation on reg 0x%x bit(s) 0x%x missed, delay configured: %llu us", + regaddr, bit_mask, p_delay->ns / NSEC_PER_USEC); + err = -EAGAIN; + } + + return err; +} + +/** + * @brief Convenience function to poll an operation completion + * @param counter Context struct + * @param op_flag Selected operation to poll for + * @param p_delay Time between each poll + * @return See adrv906x_tod_hw_op_poll_reg() + */ +static int adrv906x_tod_hw_op_poll(struct adrv906x_tod_counter *counter, u8 op_flag, + const struct adrv906x_tod_trig_delay *p_delay) +{ + u8 tod_idx = BIT(counter->id); + u32 mask; + + mask = adrv906x_tod_op_to_mask(op_flag, counter->trigger_mode, tod_idx); + return adrv906x_tod_hw_op_poll_reg(counter, ADRV906X_TOD_STAT_TOD_OP, mask, p_delay, true); +} + +/** + * @brief Compensate tstamps to write to HW register before a set operation + * @param counter Context struct + * @param tstamp Tstamp to compensate + * @param trig_delay Timespan to compensate + */ +static void adrv906x_tod_compensate_tstamp(struct adrv906x_tod_counter *counter, + struct adrv906x_tod_tstamp *tstamp, + const struct adrv906x_tod_trig_delay *trig_delay) +{ + struct adrv906x_tod *tod = counter->parent; + struct adrv906x_tod_tstamp old_tstamp; + u32 frac_ns_tstamp; + u32 seconds; + u32 ns; + + /* + * Update the ToD vector value: + * new_tstamp_value = old_tstamp + trigger_delay + */ + memcpy(&old_tstamp, tstamp, sizeof(struct adrv906x_tod_tstamp)); + + /* + * Fraction part of the nanosecond stored as a 16bit value in the ToD tstamp: + * frac_ns_tstamp = (trig_delay.rem_ns / gc_clk_frequency) * 2^16 + */ + frac_ns_tstamp = (u32)div_u64(trig_delay->rem_ns * TOD_FRAC_NANO_NUM, tod->gc_clk_freq_khz); + + /* Update the fraction part of nanosecond and the nanosecond part in the tstamp */ + if ((old_tstamp.frac_nanoseconds + frac_ns_tstamp) < TOD_FRAC_NANO_NUM) { + tstamp->frac_nanoseconds = (u16)(old_tstamp.frac_nanoseconds + frac_ns_tstamp); + tstamp->nanoseconds = old_tstamp.nanoseconds + trig_delay->ns; + } else { + tstamp->frac_nanoseconds = + (u16)((old_tstamp.frac_nanoseconds + frac_ns_tstamp) - TOD_FRAC_NANO_NUM); + tstamp->nanoseconds = old_tstamp.nanoseconds + trig_delay->ns + 1U; + } + + /* Update the second part in the tstamp */ + if (tstamp->nanoseconds >= NSEC_PER_SEC) { + seconds = div_u64_rem(tstamp->nanoseconds, NSEC_PER_SEC, &ns); + tstamp->nanoseconds = ns; + tstamp->seconds = old_tstamp.seconds + seconds; + } else { + tstamp->seconds = old_tstamp.seconds; + } +} + +/** + * @brief Write tstamp to HW + * @param counter Context struct + * @param tstamp Tstamp to write + */ +static void adrv906x_tod_hw_settstamp_to_reg(struct adrv906x_tod_counter *counter, + const struct adrv906x_tod_tstamp *tstamp) +{ + struct adrv906x_tod *tod = counter->parent; + u32 reg_tstamp[3] = { 0U, 0U, 0U }; + + reg_tstamp[0] |= FIELD_PREP(ADRV906X_TOD_CFG_TV_NSEC_FRAC_NSEC_MASK, + tstamp->frac_nanoseconds); + reg_tstamp[0] |= FIELD_PREP(ADRV906X_TOD_CFG_TV_NSEC_NSEC_MASK, + tstamp->nanoseconds & 0xFFFF); + reg_tstamp[1] |= FIELD_PREP(ADRV906X_TOD_CFG_TV_SEC_0_NSEC_MASK, + (tstamp->nanoseconds & 0xFFFF0000) >> 16); + reg_tstamp[1] |= FIELD_PREP(ADRV906X_TOD_CFG_TV_SEC_0_SEC_MASK, + (tstamp->seconds & 0xFFFF)); + reg_tstamp[2] |= FIELD_PREP(ADRV906X_TOD_CFG_TV_SEC_1_SEC_MASK, + (tstamp->seconds & 0xFFFFFFFF0000) >> 16); + + ADRV906X_REG_WRITE_DUAL(tod, ADRV906X_TOD_CFG_TV_NSEC, reg_tstamp[0]); + ADRV906X_REG_WRITE_DUAL(tod, ADRV906X_TOD_CFG_TV_SEC_0, reg_tstamp[1]); + ADRV906X_REG_WRITE_DUAL(tod, ADRV906X_TOD_CFG_TV_SEC_1, reg_tstamp[2]); +} + +/** + * @brief Read tstamp from HW + * @param counter Context struct + * @param tstamp Return struct for tstamp + */ +static void adrv906x_tod_hw_gettstamp_from_reg(struct adrv906x_tod_counter *counter, + struct adrv906x_tod_tstamp *tstamp) +{ + struct adrv906x_tod *tod = counter->parent; + u32 reg_tstamp[3] = { 0U }; + u8 tod_idx; + u32 val; + + tod_idx = counter->id; + + val = ADRV906X_REG_READ(tod, ADRV906X_TOD_CFG_IO_CTRL); + val &= ~ADRV906X_TOD_CFG_IO_CTRL_TOD_STAT_SEL_MASK; + val |= ADRV906X_TOD_CFG_IO_CTRL_TOD_STAT_SEL(BIT(tod_idx)); + ADRV906X_REG_WRITE(tod, ADRV906X_TOD_CFG_IO_CTRL, val); + + reg_tstamp[0] = ADRV906X_REG_READ(tod, ADRV906X_TOD_STAT_TV_NSEC); + reg_tstamp[1] = ADRV906X_REG_READ(tod, ADRV906X_TOD_STAT_TV_SEC_0); + reg_tstamp[2] = ADRV906X_REG_READ(tod, ADRV906X_TOD_STAT_TV_SEC_1); + + tstamp->frac_nanoseconds = reg_tstamp[0] & 0xFFFFU; + tstamp->nanoseconds = ((reg_tstamp[0] >> 16) & 0xFFFFU) | ((reg_tstamp[1] & 0xFFFFU) << 16); + tstamp->seconds = ((reg_tstamp[1] >> 16) & 0xFFFFU) | (reg_tstamp[2] << 16); +} + +/** + * @brief Prepare the HW for an operation + * @param counter Context struct + */ +static void adrv906x_tod_hw_set_trigger_delay(struct adrv906x_tod_counter *counter) +{ + u64 gc_cnt = 0U; + + /* Set the trigger delay to GC value register */ + gc_cnt = adrv906x_tod_hw_gc_get_cnt(counter); + gc_cnt += counter->trig_delay_tick; + adrv906x_tod_hw_gc_set_cnt(counter, gc_cnt); +} + +/** + * @brief Get the trigger delay for the referenced counter + * @param counter Context struct + * @param trig_delay Return struct for trigger delay + */ +static void adrv906x_tod_get_trigger_delay(struct adrv906x_tod_counter *counter, + struct adrv906x_tod_trig_delay *trig_delay) +{ + struct adrv906x_tod *tod = counter->parent; + + /* + * The trigger delay value depends on the counter->trig_delay_tick. + * adrv906x_tod_trig_delay.ns = counter->trig_delay_tick * 1e6 / tod->gc_clk_freq_khz + * adrv906x_tod_trig_delay.frac_ns = counter->trig_delay_tick * 1e6 % tod->gc_clk_freq_khz + * 1e6 is used to calculate the nano-second of the trigger tick so that the + * "counter->trig_delay_tick * 1e6" will not overflow unless counter->trig_delay_tick beyond + * the value "2^44". + */ + trig_delay->ns = div_u64_rem(counter->trig_delay_tick * USEC_PER_SEC, + tod->gc_clk_freq_khz, + &trig_delay->rem_ns); +} + +/** + * @brief Make the referenced counter to continue counting from the provided tstamp + * @param counter Context struct + * @param vector Tstamp to set + * @return See adrv906x_tod_hw_op_poll() + */ +static int adrv906x_tod_hw_settstamp(struct adrv906x_tod_counter *counter, + const struct adrv906x_tod_tstamp *vector) +{ + struct adrv906x_tod_trig_delay trig_delay = { 0U, 0U }; + struct adrv906x_tod_tstamp tstamp = { 0U, 0U, 0U }; + int err; + + memcpy(&tstamp, vector, sizeof(struct adrv906x_tod_tstamp)); + + if (counter->trigger_mode == HW_TOD_TRIG_MODE_GC) { + adrv906x_tod_get_trigger_delay(counter, &trig_delay); + adrv906x_tod_compensate_tstamp(counter, &tstamp, &trig_delay); + adrv906x_tod_hw_set_trigger_delay(counter); + } else { + /* We set the delay to time out polling after a second. */ + trig_delay.ns = 100 * NSEC_PER_MSEC; + } + + adrv906x_tod_hw_settstamp_to_reg(counter, &tstamp); + + adrv906x_tod_hw_op_trig_set(counter, HW_TOD_TRIG_OP_WR); + err = adrv906x_tod_hw_op_poll(counter, HW_TOD_TRIG_OP_WR, &trig_delay); + + if (!err) + adrv906x_tod_hw_op_trig_clear(counter, HW_TOD_TRIG_OP_WR); + + return err; +} + +/** + * @brief Get the current tstamp from the reference counter + * @param counter Context structure + * @param tstamp Return structure for tstamp + * @return See adrv906x_tod_hw_op_poll() + */ +static int adrv906x_tod_hw_get_tstamp(struct adrv906x_tod_counter *counter, + struct adrv906x_tod_tstamp *tstamp) +{ + struct adrv906x_tod_trig_delay trig_delay = { 0U, 0U }; + struct adrv906x_tod *tod = counter->parent; + int err; + + adrv906x_tod_get_trigger_delay(counter, &trig_delay); + + if (counter->trigger_mode == HW_TOD_TRIG_MODE_GC) + adrv906x_tod_hw_set_trigger_delay(counter); + else + /* We set the delay to time out polling after a second. */ + trig_delay.ns = 100 * NSEC_PER_MSEC; + + /* In PPS mode and HW version 2.2 and below, only trigger reads when PPS signal is low. */ + if (counter->trigger_mode == HW_TOD_TRIG_MODE_PPS && + tod->ver_major == 2 && tod->ver_minor <= 2) + wait_event(tod->pps_queue, atomic_read(&tod->pps_state) == 0); + + adrv906x_tod_hw_op_trig_set(counter, HW_TOD_TRIG_OP_RD); + err = adrv906x_tod_hw_op_poll(counter, HW_TOD_TRIG_OP_RD, &trig_delay); + + if (!err) + adrv906x_tod_hw_gettstamp_from_reg(counter, tstamp); + + adrv906x_tod_hw_op_trig_clear(counter, HW_TOD_TRIG_OP_RD); + + return err; +} + +/** + * @brief Adjust the counter by the input time, where a negative value is backwards in time + * @param counter Context struct + * @param delta The adjustment in signed ns + * @return See adrv906x_tod_hw_op_poll_reg() + */ +static int adrv906x_tod_hw_adjust_time(struct adrv906x_tod_counter *counter, s64 delta) +{ + struct adrv906x_tod_trig_delay trig_delay = { 0U, 0U }; + struct adrv906x_tod_tstamp ts0 = { 0U }, ts1 = { 0U }; + u64 gc0, gc1, gc2; + struct timespec64 tmp; + ktime_t kt0, kt1; + u32 op_mask; + int err; + + adrv906x_tod_get_trigger_delay(counter, &trig_delay); + + /* + * The time adjustment will need to know the tstamp for a specific GC value + * to be able to adjust the time correctly. + * gc0 gc1 gc2 + * |---*-----*-----*-----| + * ts0 ts1 + */ + + gc0 = adrv906x_tod_hw_gc_get_cnt(counter); + + gc1 = gc0 + counter->trig_delay_tick; + adrv906x_tod_hw_gc_set_cnt(counter, gc1); + + adrv906x_tod_hw_op_trig(counter, HW_TOD_TRIG_OP_RD, false, true); + op_mask = FIELD_PREP(ADRV906X_TOD_CFG_TOD_OP_RD_TOD_MASK, BIT(counter->id)); + err = adrv906x_tod_hw_op_poll_reg(counter, ADRV906X_TOD_STAT_TOD_OP, op_mask, &trig_delay, + true); + adrv906x_tod_hw_gettstamp_from_reg(counter, &ts0); + adrv906x_tod_hw_op_trig(counter, HW_TOD_TRIG_OP_RD, false, false); + if (err) + return err; + + /* + * We leverage the 'delta' variable to do the adjustment calculation before + * converting to the ADRV906X format. + */ + kt0 = ktime_set(ts0.seconds, ts0.nanoseconds); + kt1 = ktime_add_ns(kt0, delta); + tmp = ktime_to_timespec64(kt1); + timespec_to_tstamp(&ts1, &tmp); + ts1.frac_nanoseconds = ts0.frac_nanoseconds; + + adrv906x_tod_compensate_tstamp(counter, &ts1, &trig_delay); + + gc2 = gc1 + counter->trig_delay_tick; + adrv906x_tod_hw_gc_set_cnt(counter, gc2); + adrv906x_tod_hw_settstamp_to_reg(counter, &ts1); + + adrv906x_tod_hw_op_trig(counter, HW_TOD_TRIG_OP_WR, false, true); + op_mask = FIELD_PREP(ADRV906X_TOD_CFG_TOD_OP_WR_TOD_MASK, BIT(counter->id)); + err = adrv906x_tod_hw_op_poll_reg(counter, ADRV906X_TOD_STAT_TOD_OP, op_mask, &trig_delay, + true); + + adrv906x_tod_hw_op_trig(counter, HW_TOD_TRIG_OP_WR, false, false); + + return err; +} + +/** + * @brief Enable the ToD output in the CDC domain + * @param counter Context struct + * @param enable Enable flag, non-zero to enable + * @return 0 Success + * @return -ENODEV Requesting to enable output for a disabled counter + */ +static int adrv906x_tod_hw_cdc_output_enable(struct adrv906x_tod_counter *counter, u8 enable) +{ + struct adrv906x_tod *tod = counter->parent; + u8 tod_idx; + u32 val = 0U; + int i; + + if (counter->en && counter->id != TOD_INTERNAL_GNSS) + tod_idx = counter->id; + else + return -ENODEV; + + if (enable) + val |= BIT(tod_idx); + + for (i = 0; i < ADRV906X_HW_TOD_CDC_DOMAIN_CNT; i++) + ADRV906X_REG_WRITE_DUAL(tod, ADRV906X_TOD_CFG_TSU_TOD + i * 0x4, val); + + return 0; +} + +/** + * @brief Instruct HW to enable the ToD output + * @param counter Context struct + * @param enable Enable flag, non-zero to enable + * @return See adrv906x_tod_hw_op_poll_reg() + */ +static int adrv906x_tod_hw_extts_enable(struct adrv906x_tod_counter *counter, u8 enable) +{ + struct adrv906x_tod_trig_delay trig_delay = { 0U, 0U }; + struct adrv906x_tod *tod = counter->parent; + u8 tod_idx = counter->id; + u32 val; + int ret; + + if (!counter->en) { + dev_err(tod->dev, "tod %d is disabled, cannot enable output", tod_idx); + return -EOPNOTSUPP; + } + + adrv906x_tod_get_trigger_delay(counter, &trig_delay); + adrv906x_tod_hw_set_trigger_delay(counter); + + val = ADRV906X_REG_READ(tod, ADRV906X_TOD_CFG_IO_SOURCE); + val &= ~ADRV906X_TOD_CFG_IO_TOD_OUT_SRC_MASK; + val |= ADRV906X_TOD_CFG_IO_WR_OUTPUT_CFG_MASK; + + if (enable) + val |= ADRV906X_TOD_CFG_IO_TOD_OUT_SRC_SEL(BIT(tod_idx)); + else + val &= ~ADRV906X_TOD_CFG_IO_TOD_OUT_SRC_SEL(BIT(tod_idx)); + ADRV906X_REG_WRITE_DUAL(tod, ADRV906X_TOD_CFG_IO_SOURCE, val); + + ret = adrv906x_tod_hw_op_poll_reg(counter, ADRV906X_TOD_CFG_IO_SOURCE, + ADRV906X_TOD_CFG_IO_WR_OUTPUT_CFG_MASK, &trig_delay, + false); + + return ret; +} + +/** + * @brief Enable the interrupt lines for the referenced counter + * @param counter Context struct + * @param enable Enable flag, non-zero to enable + * @return 0 Success + * @return -ENODEV Referred counter not active + */ +static int adrv906x_tod_pps_irq_enable(struct adrv906x_tod_counter *counter, u8 enable) +{ + struct adrv906x_tod *tod = counter->parent; + int tod_idx = counter->id; + u32 val; + + if (!counter->en) + return -ENODEV; + + val = ADRV906X_REG_READ(tod, ADRV906X_TOD_IRQ_MASK); + + if (enable) + val &= ~BIT(tod_idx); + else + val |= BIT(tod_idx); + + ADRV906X_REG_WRITE(tod, ADRV906X_TOD_IRQ_MASK, val); + + return 0; +} + +/** + * @brief Configure interrupt line to be driven by the external PPS input + * @param tod Context struct + */ +static void adrv906x_tod_hw_pps_irq_external_enable(struct adrv906x_tod *tod) +{ + u32 val = 0U; + + val = ADRV906X_REG_READ(tod, ADRV906X_TOD_IRQ_MASK); + val &= ~ADRV906X_TOD_IRQ_MASK_EXTERNAL_PPS; + ADRV906X_REG_WRITE(tod, ADRV906X_TOD_IRQ_MASK, val); +} + +/** + * @brief Disable all interrupts + * @param tod Context struct + */ +static void adrv906x_tod_hw_pps_irq_disable_all(struct adrv906x_tod *tod) +{ + u32 val = ADRV906X_TOD_IRQ_MASK_EXTERNAL_PPS | + ADRV906X_TOD_IRQ_MASK_INTERNAL_0 | + ADRV906X_TOD_IRQ_MASK_INTERNAL_1 | + ADRV906X_TOD_IRQ_MASK_INTERNAL_GNSS; + + ADRV906X_REG_WRITE(tod, ADRV906X_TOD_IRQ_MASK, val); +} + +/** + * @brief Select the referenced counter as the PPS source and en-/disable the PPS output + * @note This function doesn't change the output if the external PPS is enabled + * @param counter Context struct + * @param enable Enable flag, non-zero to enable + * @return See adrv906x_tod_hw_op_poll_reg() + */ +static int adrv906x_tod_hw_pps_enable(struct adrv906x_tod_counter *counter, u8 enable) +{ + struct adrv906x_tod_trig_delay trig_delay = { 0U, 0U }; + struct adrv906x_tod *tod = counter->parent; + u32 val; + int ret; + + adrv906x_tod_get_trigger_delay(counter, &trig_delay); + adrv906x_tod_hw_set_trigger_delay(counter); + + val = ADRV906X_REG_READ(tod, ADRV906X_TOD_CFG_IO_SOURCE); + val &= ~ADRV906X_TOD_CFG_IO_PPS_OUT_SRC_MASK; + val |= ADRV906X_TOD_CFG_IO_WR_OUTPUT_CFG_MASK; + + /* Do not change source when external pps is enabled */ + if (enable) { + if (tod->external_pps) { + val |= ADRV906X_TOD_CFG_IO_PPS_OUT_SRC_SEL(BIT(TOD_EXTERNAL)); + dev_info(tod->dev, "using external pps"); + } else { + val |= ADRV906X_TOD_CFG_IO_PPS_OUT_SRC_SEL(BIT(counter->id)); + } + } + ADRV906X_REG_WRITE_DUAL(tod, ADRV906X_TOD_CFG_IO_SOURCE, val); + + ret = adrv906x_tod_hw_op_poll_reg(counter, ADRV906X_TOD_CFG_IO_SOURCE, + ADRV906X_TOD_CFG_IO_WR_OUTPUT_CFG_MASK, &trig_delay, + false); + + return ret; +} + +/** + * @brief Enable or disable the PPS output for the referenced counter + * @param counter Context struct + * @param enable Flag to enable or disable the PPS + * @return See adrv906x_tod_hw_pps_enable() + */ +static int adrv906x_tod_pps_enable(struct adrv906x_tod_counter *counter, u8 enable) +{ + struct adrv906x_tod *tod = counter->parent; + int err; + + mutex_lock(&tod->reg_lock); + err = adrv906x_tod_hw_pps_enable(counter, enable); + mutex_unlock(&tod->reg_lock); + + return err; +} + +static irqreturn_t adrv906x_tod_pps_isr(int irq, void *dev_id) +{ + struct adrv906x_tod *tod = dev_id; + struct adrv906x_tod_counter *counter; + struct ptp_clock_event event; + u32 irq_val; + u8 i; + + irq_val = ADRV906X_REG_READ(tod, ADRV906X_TOD_IRQ_STATUS); + ADRV906X_REG_WRITE(tod, ADRV906X_TOD_IRQ_EVENT, irq_val); + + for (i = 0U; i < ADRV906X_HW_TOD_COUNTER_CNT; i++) { + if (irq_val & BIT(i) || (tod->external_pps && + irq_val == ADRV906X_TOD_IRQ_MASK_EXTERNAL_PPS)) { + counter = &tod->counter[i]; + if (counter->en) { + event.type = PTP_CLOCK_PPS; + ptp_clock_event(counter->ptp_clk, &event); + } + } + } + + if (irq_val & ADRV906X_TOD_IRQ_MASK_EXTERNAL_PPS && + tod->ver_major == 2 && tod->ver_minor <= 2) { + atomic_set(&tod->pps_state, 1); + schedule_delayed_work(&tod->pps_work, msecs_to_jiffies(tod->pps_in_pulse_width_ms)); + } + + return IRQ_HANDLED; +} + +/** + * @brief Configure the PPSX output + * @note The HW is restricted to a PPS pulse of configurable width + * @param counter Context struct + * @param rq Request struct + */ +static void adrv906x_tod_hw_cfg_ppsx(struct adrv906x_tod_counter *counter, + struct ptp_perout_request *rq) +{ + struct adrv906x_tod *tod = counter->parent; + u32 stop, val; + + val = ADRV906X_REG_READ(tod, ADRV906X_TOD_CFG_TEST_OUT_SRC); + val &= ~ADRV906X_TOD_CFG_TEST_OUT_SRC_PPSX_SRC_MASK; + + if (!(rq->period.sec == 0 && rq->period.nsec == 0U)) { + ADRV906X_REG_WRITE_DUAL(tod, ADRV906X_TOD_CFG_PPSX_START, rq->start.nsec); + stop = (rq->start.nsec + tod->ppsx_pulse_width_ns) & 0xFFFFFFFFU; + ADRV906X_REG_WRITE_DUAL(tod, ADRV906X_TOD_CFG_PPSX_STOP, stop); + + val |= ADRV906X_TOD_CFG_TEST_OUT_SRC_PPSX_SRC(BIT(counter->id)); + } + + ADRV906X_REG_WRITE_DUAL(tod, ADRV906X_TOD_CFG_TEST_OUT_SRC, val); +} + +/** + * @brief Configure the periodic output for the referenced counter + * @param counter Counter struct + * @param rq Request struct + */ +static void adrv906x_tod_perout_enable(struct adrv906x_tod_counter *counter, + struct ptp_perout_request *rq) +{ + struct adrv906x_tod *tod = counter->parent; + + mutex_lock(&tod->reg_lock); + adrv906x_tod_hw_cfg_ppsx(counter, rq); + mutex_unlock(&tod->reg_lock); +} + +/** + * @brief Configure the CDC parameters for the referenced counter + * @param counter Context struct + */ +static void adrv906x_tod_cfg_cdc_delay_set(struct adrv906x_tod_counter *counter) +{ + struct adrv906x_tod *tod = counter->parent; + u32 i; + + for (i = 0U; i < ADRV906X_HW_TOD_CDC_DOMAIN_CNT; i++) + ADRV906X_REG_WRITE_DUAL(tod, ADRV906X_TOD_CFG_CDC_DELAY + i * sizeof(u32), + tod->cdc.delay_cnt[i]); + + /* According to the user manual, all CFG_CDC_DELAY:CDC register fields + * shall have the same value. So we just pick the first one. + */ + adrv906x_tod_cfg_cdc_delay = tod->cdc.delay_cnt[0]; +} + +/** + * @brief Initialize the counter HW + * @param counter Context struct + * @return See adrv906x_tod_cfg_lc_clk() + */ +static int adrv906x_tod_module_init(struct adrv906x_tod_counter *counter) +{ + struct adrv906x_tod *tod = counter->parent; + u32 val; + int ret; + + /* Update the ns and frac_ns part to the CFG_INCR */ + ret = adrv906x_tod_cfg_lc_clk(counter); + /* Enable the ToD counter */ + if (!ret) { + val = ADRV906X_REG_READ(tod, ADRV906X_TOD_CFG_INCR); + val |= ADRV906X_TOD_CFG_INCR_CFG_TOD_CNT_EN_MASK; + ADRV906X_REG_WRITE_DUAL(tod, ADRV906X_TOD_CFG_INCR, val); + } + + return ret; +} + +/** + * @brief Parse the ToD config from the device tree + * @param counter Context struct + * @param np Pointer to device tree node + */ +static void adrv906x_tod_dt_parse(struct adrv906x_tod_counter *counter, struct device_node *np) +{ + struct adrv906x_tod *tod = counter->parent; + struct device *dev = tod->dev; + int ret; + u32 val; + + counter->trigger_mode = of_property_read_bool(np, "adi,pps-mode"); + dev_info(dev, "tod trigger mode: %s", counter->trigger_mode == + HW_TOD_TRIG_MODE_GC ? "gc mode" : "pps mode"); + + ret = of_property_read_u32(np, "adi,trigger-delay-tick", &val); + if (ret) { + /* Use a default of 500 us based on the provided clock speed */ + val = tod->gc_clk_freq_khz * 500U / 1000U; + dev_info(dev, "'adi,trigger-delay-tick' not set, using '%u'", val); + } + counter->trig_delay_tick = val; +} + +/** + * @brief Configure CDC and enable tstamp output for the referenced counter + * @param counter Context struct + * @param enable Enable flag, non-zero to enable + * @return See adrv906x_tod_hw_extts_enable() or adrv906x_tod_hw_cdc_output_enable() + */ +static int adrv906x_tod_extts_enable(struct adrv906x_tod_counter *counter, u8 enable) +{ + struct adrv906x_tod *tod = counter->parent; + int ret; + + if (!counter->en) + return -ENODEV; + + mutex_lock(&tod->reg_lock); + if (counter->id != TOD_INTERNAL_GNSS) { + ret = adrv906x_tod_hw_cdc_output_enable(counter, enable); + if (ret) + goto exit; + + adrv906x_phc_index = ptp_clock_index(counter->ptp_clk); + } + ret = adrv906x_tod_hw_extts_enable(counter, enable); + +exit: + mutex_unlock(&tod->reg_lock); + + return ret; +} + +/** + * @brief Configure the tstamp output + * @param counter Context struct + * @param rq Request struct + * @param enable Enable flag, non-zero to enable + * @return 0 Success + * @return -EOPNOTSUPP Unsupported request + * @return -EINVAL Only aligned PPS pulses are supported + */ +static int adrv906x_tod_enable(struct adrv906x_tod_counter *counter, + struct ptp_clock_request *rq, int enable) +{ + struct adrv906x_tod *tod = counter->parent; + int ret; + + switch (rq->type) { + case PTP_CLK_REQ_EXTTS: + /* Reject requests with unsupported flags */ + if (rq->extts.flags & ~(PTP_ENABLE_FEATURE | + PTP_RISING_EDGE | + PTP_FALLING_EDGE | + PTP_STRICT_FLAGS)) + return -EOPNOTSUPP; + + /* Reject requests to enable time stamping on falling edges */ + if ((rq->extts.flags & PTP_STRICT_FLAGS) && + (rq->extts.flags & PTP_ENABLE_FEATURE) && + (rq->extts.flags & PTP_FALLING_EDGE)) + return -EOPNOTSUPP; + + ret = adrv906x_tod_extts_enable(counter, enable); + break; + case PTP_CLK_REQ_PEROUT: + /* Reject requests with unsupported flags */ + /* Add PTP_PEROUT_PHASE & PTP_PEROUT_DUTY_CYCLE when supported */ + if (rq->perout.flags & PTP_PEROUT_ONE_SHOT) + return -EOPNOTSUPP; + + /* Reject requests that are not pps */ + if (enable && (rq->perout.period.sec != 1 || + rq->perout.period.nsec != 0U)) + return -EINVAL; + + if (enable && (rq->perout.start.nsec + tod->ppsx_pulse_width_ns) + >= NSEC_PER_SEC) { + dev_err(tod->dev, "periodic pulse crosses 1 second boundary"); + return -EINVAL; + } + + /* Enable ppsx for periodic output for given tod counter */ + adrv906x_tod_perout_enable(counter, &rq->perout); + break; + case PTP_CLK_REQ_PPS: + /* Enable internal pps output for given tod counter */ + ret = adrv906x_tod_pps_enable(counter, enable); + break; + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +/** + * @brief Set tstamp for the referenced counter + * @param counter Context struct + * @param ts tstamp to set + * @return See adrv906x_tod_hw_settstamp() + */ +static int adrv906x_tod_settime(struct adrv906x_tod_counter *counter, const struct timespec64 *ts) +{ + struct adrv906x_tod *tod = counter->parent; + struct adrv906x_tod_tstamp tstamp; + int err; + + timespec_to_tstamp(&tstamp, ts); + mutex_lock(&tod->reg_lock); + err = adrv906x_tod_hw_settstamp(counter, &tstamp); + mutex_unlock(&tod->reg_lock); + + return err; +} + +/** + * @brief Shift time for the referenced counter + * @param counter Context struct + * @param delta Time to shift counter + * @return See adrv906x_tod_hw_adjust_time() + */ +static int adrv906x_tod_adjtime(struct adrv906x_tod_counter *counter, s64 delta) +{ + struct adrv906x_tod *tod = counter->parent; + int err; + + mutex_lock(&tod->reg_lock); + err = adrv906x_tod_hw_adjust_time(counter, delta); + mutex_unlock(&tod->reg_lock); + + return err; +} + +/** + * @brief Get the tstamp for the referenced counter + * @param counter Context struct + * @param ts Time to shift counter + * @param sts System tstamp + * @return See adrv906x_tod_hw_get_tstamp() + */ +static int adrv906x_tod_gettimex(struct adrv906x_tod_counter *counter, + struct timespec64 *ts, + struct ptp_system_timestamp *sts) +{ + struct adrv906x_tod *tod = counter->parent; + struct adrv906x_tod_tstamp tstamp; + int err; + + mutex_lock(&tod->reg_lock); + ptp_read_system_prets(sts); + err = adrv906x_tod_hw_get_tstamp(counter, &tstamp); + ptp_read_system_postts(sts); + tstamp_to_timespec(ts, &tstamp); + mutex_unlock(&tod->reg_lock); + + return err; +} + +/** + * @brief Configure the CDC delay for all active counters + * @param tod Context struct + */ +static void adrv906x_tod_cfg_cdc_delay_all(struct adrv906x_tod *tod) +{ + int i; + + for (i = 0; i < ADRV906X_HW_TOD_COUNTER_CNT; i++) { + if (tod->counter[i].en) { + adrv906x_tod_cfg_cdc_delay_set(&tod->counter[i]); + break; + } + } +} + +/** + * @brief Enable the external PPS override + * @param tod Context struct + */ +static void adrv906x_tod_hw_external_pps_override(struct adrv906x_tod *tod) +{ + adrv906x_tod_hw_pps_irq_disable_all(tod); + adrv906x_tod_hw_pps_irq_external_enable(tod); +} + +static int adrv906x_phc_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int enable) +{ + struct adrv906x_tod_counter *counter = container_of(ptp, struct adrv906x_tod_counter, caps); + + return adrv906x_tod_enable(counter, rq, enable); +} + +static int adrv906x_phc_settime(struct ptp_clock_info *ptp, const struct timespec64 *ts) +{ + struct adrv906x_tod_counter *counter = container_of(ptp, struct adrv906x_tod_counter, caps); + + return adrv906x_tod_settime(counter, ts); +} + +static int adrv906x_phc_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct adrv906x_tod_counter *counter = container_of(ptp, struct adrv906x_tod_counter, caps); + + return adrv906x_tod_adjtime(counter, delta); +} + +static int adrv906x_phc_gettimex(struct ptp_clock_info *ptp, + struct timespec64 *ts, + struct ptp_system_timestamp *sts) +{ + struct adrv906x_tod_counter *counter = container_of(ptp, struct adrv906x_tod_counter, caps); + + return adrv906x_tod_gettimex(counter, ts, sts); +} + +static struct ptp_clock_info adrv906x_tod_caps = { + .owner = THIS_MODULE, + .n_per_out = 1, + .n_ext_ts = 1, + .pps = 1, + .adjfine = NULL, + .adjtime = &adrv906x_phc_adjtime, + .gettimex64 = &adrv906x_phc_gettimex, + .getcrosststamp = NULL, + .settime64 = &adrv906x_phc_settime, + .enable = &adrv906x_phc_enable, + .do_aux_work = NULL, /* Use the aux */ +}; + +/** + * @brief Obtain and apply configuration for the referenced counter + * @param tod Context struct + * @param np Pointer to device tree node + * @return 0 Success + * @return -EINVAL Invalid counter selected or invalid DT node. See kernel log. + */ +static int adrv906x_tod_add_counter(struct adrv906x_tod *tod, struct device_node *np) +{ + struct adrv906x_tod_counter *counter; + int ret; + u32 val; + + /* Get the tod index */ + ret = of_property_read_u32(np, "reg", &val); + if (ret) { + dev_err(tod->dev, "dt: tod 'reg' property missing"); + return ret; + } + + if (val >= ADRV906X_HW_TOD_COUNTER_CNT) + return -EINVAL; + + counter = &tod->counter[val]; + + if (counter->en) { + dev_err(tod->dev, "dt: 'reg' value of '%d' used more than once", val); + return -EINVAL; + } + + counter->en = true; + counter->id = val; + counter->parent = tod; + + adrv906x_tod_dt_parse(counter, np); + ret = adrv906x_tod_module_init(counter); + if (ret) { + counter->en = false; + return ret; + } + + counter->caps = adrv906x_tod_caps; + snprintf(counter->caps.name, 16U, "adrv906x-ptp-tod%d", counter->id); + counter->ptp_clk = ptp_clock_register(&counter->caps, tod->dev); + if (IS_ERR(counter->ptp_clk)) { + ret = PTR_ERR(counter->ptp_clk); + counter->en = false; + return ret; + } + + adrv906x_tod_pps_irq_enable(counter, ADRV906X_ENABLE); + dev_info(tod->dev, "added counter %d as /dev/ptp%d", + counter->id, ptp_clock_index(counter->ptp_clk)); + + return 0; +} + +/** + * @brief Register capabilities to adjust a PLL supplying clock to the counters + * @param pll_caps PLL capabilities struct + * @return 0 Success + * @return -ENODEV No ToD device instantiated + */ +int adrv906x_tod_register_pll(struct ptp_clock_info *pll_caps) +{ + int i; + + if (!adrv906x_tod) + return -ENODEV; + + for (i = 0; i < ADRV906X_HW_TOD_COUNTER_CNT; i++) { + adrv906x_tod->counter[i].caps.adjfine = pll_caps->adjfine; + memcpy(adrv906x_tod->counter[i].caps.name, pll_caps->name, + sizeof(adrv906x_tod->counter[i].caps.name)); + } + + return 0; +} +EXPORT_SYMBOL(adrv906x_tod_register_pll); + +/** + * @brief Disable all counter outputs + * @param tod Context struct + */ +static void adrv906x_tod_hw_disable_all(struct adrv906x_tod *tod) +{ + /* Disable debug outputs */ + ADRV906X_REG_WRITE(tod, ADRV906X_TOD_CFG_IO_CTRL, 0); + /* Disable all IRQs */ + adrv906x_tod_hw_pps_irq_disable_all(tod); +} + +/** + * @brief Read and report HW version + * @param tod Context struct + * @return 0 Success + * @return -ERANGE Unsupported HW version + */ +static int adrv906x_tod_get_version(struct adrv906x_tod *tod) +{ + u32 val; + + val = ADRV906X_REG_READ(tod, ADRV906X_TOD_VERSION); + tod->ver_major = FIELD_GET(ADRV906X_TOD_VERSION_MAJOR, val); + tod->ver_minor = FIELD_GET(ADRV906X_TOD_VERSION_MINOR, val); + + if (tod->sec_regs) { + val = ADRV906X_REG_READ_SEC(tod, ADRV906X_TOD_VERSION); + tod->sec_ver_major = FIELD_GET(ADRV906X_TOD_VERSION_MAJOR, val); + tod->sec_ver_minor = FIELD_GET(ADRV906X_TOD_VERSION_MINOR, val); + + if (tod->ver_major != tod->sec_ver_major || tod->ver_minor != tod->sec_ver_minor) { + dev_err(tod->dev, "tod versions don't match: %d.%d != %d.%d", + tod->ver_major, tod->ver_minor, + tod->sec_ver_major, tod->sec_ver_minor); + return -ERANGE; + } + } + + return 0; +} + +static unsigned int platform_get_num_of_resources(struct platform_device *dev, + unsigned int type) +{ + unsigned int num = 0; + u32 i; + + for (i = 0; i < dev->num_resources; i++) { + struct resource *r = &dev->resource[i]; + if (type == resource_type(r)) + num++; + } + + return num; +} + +int adrv906x_tod_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct device_node *tod_np = NULL; + struct adrv906x_tod_counter *counter; + struct device_node *child; + struct clk *lc_clk; + struct clk *gc_clk; + unsigned long rate; + void __iomem *regs; + unsigned int num; + u32 val; + int ret; + int i; + + adrv906x_tod = devm_kzalloc(dev, sizeof(*adrv906x_tod), GFP_KERNEL); + if (!adrv906x_tod) + return -ENOMEM; + adrv906x_tod->dev = dev; + + if (!np) { + dev_err(dev, "platform device data missing"); + return -EINVAL; + } + + num = platform_get_num_of_resources(pdev, IORESOURCE_MEM); + if (num != 1 && num != 2) { + dev_err(dev, "invalid number of resources"); + return -EINVAL; + } + + regs = devm_platform_ioremap_resource(pdev, 0U); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); + return ret; + } + adrv906x_tod->regs = regs; + adrv906x_tod->sec_regs = NULL; + + if (num == 2) { + dev_info(dev, "operating in dual-tile mode"); + regs = devm_platform_ioremap_resource(pdev, 1U); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); + return ret; + } + adrv906x_tod->sec_regs = regs; + } + + ret = adrv906x_tod_get_version(adrv906x_tod); + if (ret) + goto err_out; + dev_info(dev, "tod version %d.%d", adrv906x_tod->ver_major, adrv906x_tod->ver_minor); + + lc_clk = devm_clk_get(dev, "lc_clk"); + if (IS_ERR(lc_clk)) { + dev_err(dev, "can not get 'lc_clk'"); + ret = PTR_ERR(lc_clk); + goto err_out; + } + + gc_clk = devm_clk_get(dev, "gc_clk"); + if (IS_ERR(gc_clk)) { + dev_err(dev, "can not get 'gc_clk'"); + ret = PTR_ERR(gc_clk); + goto err_out; + } + + adrv906x_tod->gc_clk = gc_clk; + adrv906x_tod->lc_clk = lc_clk; + /* get the gc and local clock frequency from the clock */ + rate = clk_get_rate(adrv906x_tod->gc_clk); + adrv906x_tod->gc_clk_freq_khz = (u32)div_u64((u64)rate, 1000U); + + rate = clk_get_rate(adrv906x_tod->lc_clk); + adrv906x_tod->lc_freq_khz = (u32)div_u64((u64)rate, 1000U); + + ret = of_property_read_u32(np, "adi,pps-in-pulse-width-ms", &val); + if (ret) { + dev_info(dev, "'adi,pps-in-pulse-width-ms' not set, using 40ms"); + val = 40 * USEC_PER_MSEC; + } else if (val >= 1000U) { + dev_err(dev, "'adi,pps-in-pulse-width-ms' out of range, using 40ms"); + val = 40 * USEC_PER_MSEC; + } + adrv906x_tod->pps_in_pulse_width_ms = val; + + adrv906x_tod->irq = platform_get_irq_byname(pdev, "pps"); + if (adrv906x_tod->irq < 0) { + dev_err(dev, "dt: irq node missing"); + ret = -ENOENT; + goto err_out; + } + + ret = devm_request_irq(&pdev->dev, adrv906x_tod->irq, + adrv906x_tod_pps_isr, 0U, pdev->name, + adrv906x_tod); + if (ret) { + dev_err(dev, "irq %d unavailable", adrv906x_tod->irq); + ret = -ENOENT; + goto err_out; + } + + tod_np = of_get_child_by_name(np, "adrv906x-tod"); + if (!tod_np) + goto err_out; + + mutex_init(&adrv906x_tod->reg_lock); + + adrv906x_tod_hw_disable_all(adrv906x_tod); + + child = NULL; + for_each_child_of_node(tod_np, child) { + ret = adrv906x_tod_add_counter(adrv906x_tod, child); + if (ret) { + dev_warn(dev, "cannot add tod counter: %s", child->full_name); + continue; + } + } + + for (i = 0; i < ADRV906X_HW_TOD_CDC_DOMAIN_CNT; i++) { + ret = of_property_read_u32_index(tod_np, "adi,cdc-delay-value", i, &val); + if (ret) + val = 0U; + adrv906x_tod->cdc.delay_cnt[i] = val; + } + + adrv906x_tod_cfg_cdc_delay_all(adrv906x_tod); + counter = &adrv906x_tod->counter[adrv906x_tod->tod_counter_src]; + if (counter->en) { + ret = adrv906x_tod_extts_enable(counter, 1U); + if (ret) { + dev_err(dev, "default tod counter enable failed"); + goto err_out_unreg; + } + } + + ret = of_property_read_u32(tod_np, "adi,default-tod-counter", &val); + if (ret) { + dev_warn(dev, "adi,default-tod-counter not set, using default %d", 0); + adrv906x_tod->tod_counter_src = 0U; + } else if (adrv906x_tod->counter[val].en) { + adrv906x_tod->tod_counter_src = val; + } else { + dev_err(dev, "selected default tod not enabled - exiting"); + goto err_out_unreg; + } + + ret = of_property_read_u32(np, "adi,ppsx-pulse-width-ns", &val); + if (ret) { + dev_err(dev, "'adi,ppsx-pulse-width-ns' not set, using 10ms"); + val = TOD_PPSX_PULSE_WIDTH; + } + if (val >= (1000 * NSEC_PER_MSEC)) { + dev_err(dev, "'adi,ppsx-pulse-width-ns' out of range, using 10ms"); + val = TOD_PPSX_PULSE_WIDTH; + } + adrv906x_tod->ppsx_pulse_width_ns = val; + + adrv906x_tod->external_pps = of_property_read_bool(np, "adi,external-pps"); + if (adrv906x_tod->external_pps) { + dev_info(dev, "using external pps"); + adrv906x_tod_hw_external_pps_override(adrv906x_tod); + } + + if (adrv906x_tod->ver_major == 2 && adrv906x_tod->ver_minor <= 2) { + init_waitqueue_head(&adrv906x_tod->pps_queue); + INIT_DELAYED_WORK(&adrv906x_tod->pps_work, adrv906x_tod_clear_soft_pps); + atomic_set(&adrv906x_tod->pps_state, 0); + adrv906x_tod_hw_pps_irq_external_enable(adrv906x_tod); + } + + dev_info(dev, "adrv906x tod probe ok"); + + return 0; + +err_out_unreg: + for (i = 0; i < ADRV906X_HW_TOD_COUNTER_CNT; i++) + if (adrv906x_tod->counter[i].ptp_clk) + ptp_clock_unregister(adrv906x_tod->counter[i].ptp_clk); + +err_out: + adrv906x_tod_hw_disable_all(adrv906x_tod); + return ret; +} +EXPORT_SYMBOL(adrv906x_tod_probe); + +/** + * @brief Stop and remove the driver + * @param pdev Context struct + * @return See kernel log for error descriptions + */ +void adrv906x_tod_remove(struct platform_device *pdev) +{ + struct adrv906x_tod_counter *counter; + int i; + + if (!adrv906x_tod) + return; + + adrv906x_tod_hw_disable_all(adrv906x_tod); + + for (i = 0; i < ADRV906X_HW_TOD_COUNTER_CNT; i++) { + counter = &adrv906x_tod->counter[i]; + if (counter->en) { + adrv906x_tod_extts_enable(counter, ADRV906X_DISABLE); + adrv906x_tod_pps_enable(counter, ADRV906X_DISABLE); + } + if (adrv906x_tod->counter[i].ptp_clk) + ptp_clock_unregister(adrv906x_tod->counter[i].ptp_clk); + } + + if (adrv906x_tod->ver_major == 2 && adrv906x_tod->ver_minor <= 2) + cancel_delayed_work_sync(&adrv906x_tod->pps_work); + + mutex_destroy(&adrv906x_tod->reg_lock); +} +EXPORT_SYMBOL(adrv906x_tod_remove); + +static const struct of_device_id ptp_adrv906x_tod_of_match[] = { + { .compatible = "adi,adrv906x-tod", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, ptp_adrv906x_tod_of_match); + +static struct platform_driver ptp_adrv906x_tod_driver = { + .driver = { + .name = "adrv906x-tod", + .of_match_table = ptp_adrv906x_tod_of_match, + }, + .probe = adrv906x_tod_probe, + .remove = adrv906x_tod_remove, +}; + +module_platform_driver(ptp_adrv906x_tod_driver); diff --git a/drivers/ptp/ptp_adrv906x_tod.h b/drivers/ptp/ptp_adrv906x_tod.h new file mode 100644 index 00000000000000..f978ffc5ab6dd8 --- /dev/null +++ b/drivers/ptp/ptp_adrv906x_tod.h @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __PTP_ADRV906X_H +#define __PTP_ADRV906X_H + +#include +#include +#include +#include + +#define ADRV906X_HW_TOD_CDC_DOMAIN_CNT (4u) +#define ADRV906X_HW_TOD_COUNTER_CNT (3u) +#define ADRV906X_HW_TOD_PPS_CNT (4u) + +#define ADRV906X_DISABLE (0) +#define ADRV906X_ENABLE (1) + +enum adrv906x_hw_tod_trig_mode { + HW_TOD_TRIG_MODE_GC = 0, /* ToD triggered by the Golden Counter */ + HW_TOD_TRIG_MODE_PPS = 1, /* ToD triggered by the PPS */ + HW_TOD_TRIG_MODE_CNT, +}; + +enum adrv906x_hw_tod_lc_clk_freq { + HW_TOD_LC_100_P_000_M = 0, + HW_TOD_LC_122_P_880_M, + HW_TOD_LC_125_P_000_M, + HW_TOD_LC_156_P_250_M, + HW_TOD_LC_245_P_760_M, + HW_TOD_LC_250_P_000_M, + HW_TOD_LC_312_P_500_M, + HW_TOD_LC_322_P_265_M, + HW_TOD_LC_390_P_625_M, + HW_TOD_LC_491_P_520_M, + HW_TOD_LC_500_P_000_M, + HW_TOD_LC_983_P_040_M, + HW_TOD_LC_CLK_FREQ_CNT, +}; + +enum adrv906x_hw_tod_trig_op { + HW_TOD_TRIG_OP_WR = 0, /* Trigger reading the ToD */ + HW_TOD_TRIG_OP_RD = 1, /* Trigger writing the ToD */ + HW_TOD_TRIG_OP_CNT +}; + +enum adrv906x_hw_tod_trig_set_flag { + HW_TOD_TRIG_SET_FLAG_CLEAR = 0, + HW_TOD_TRIG_SET_FLAG_TRIG = 1, + HW_TOD_TRIG_SET_FALG_CNT +}; + +enum adrv906x_hw_tod_source { + TOD_INTERNAL_0 = 0, + TOD_INTERNAL_1, + TOD_INTERNAL_GNSS, + TOD_EXTERNAL, + TOD_SOURCE_MAX +}; + +struct adrv906x_tod_tstamp { + u16 frac_nanoseconds; + u32 nanoseconds; + u64 seconds; +}; + +struct adrv906x_tod_trig_delay { + u64 ns; + u32 rem_ns; /* remainder part of the clock tick in kHz */ +}; + +struct adrv906x_tod_lc_clk_cfg { + u32 freq_khz; /* frequency of the local clock */ + u32 ns_per_clk; /* nanosecond per clock */ + u32 frac_ns_per_clk; /* fraction part of nanosecond per clock */ + u32 cnt_ctrl; /* correction control word */ +}; + +struct adrv906x_tod_cdc { + u32 delay_cnt[ADRV906X_HW_TOD_CDC_DOMAIN_CNT]; +}; + +/* PTP Hardware Clock interface */ +struct adrv906x_tod_counter { + u8 id; + u8 trigger_mode; /* Trigger mode of ToD, 0 for GC, 1 for PPS */ + bool en; + u64 trig_delay_tick; + struct adrv906x_tod *parent; + struct ptp_clock_info caps; + struct ptp_clock *ptp_clk; +}; + +/* ADRV906X ToD module */ +struct adrv906x_tod { + struct device *dev; + void __iomem *regs; + void __iomem *sec_regs; + u16 ver_major; + u16 ver_minor; + u16 sec_ver_major; + u16 sec_ver_minor; + int irq; + u8 tod_counter_src; + u8 external_pps; + u32 ppsx_pulse_width_ns; + u32 lc_freq_khz; /* Clock frequency for the ToD counter block */ + u32 gc_clk_freq_khz; /* Clock frequency for the Golden counter block */ + u16 pps_in_pulse_width_ms; /* Input PPS pulse width in milliseconds */ + atomic_t pps_state; /* PPS state */ + wait_queue_head_t pps_queue; /* Wait queue for processes waiting on PPS signal */ + struct delayed_work pps_work; /* Clear PPS boolean work structure */ + struct adrv906x_tod_cdc cdc; + struct adrv906x_tod_counter counter[ADRV906X_HW_TOD_COUNTER_CNT]; + struct clk *lc_clk; + struct clk *gc_clk; + void *priv_data; + struct mutex reg_lock; /* Serialize access to hw_registers of the ToD module */ +}; + +int adrv906x_tod_probe(struct platform_device *pdev); +void adrv906x_tod_remove(struct platform_device *pdev); +int adrv906x_tod_register_pll(struct ptp_clock_info *pll_caps); + +/* + * These macros should be used to read and write registers. If the register should be written to + * both tiles, ADRV906X_REG_WRITE_DUAL should be used. + */ +#define ADRV906X_REG_WRITE(tod, offset, value) \ + iowrite32(value, (tod)->regs + (offset)) +#define ADRV906X_REG_WRITE_DUAL(tod, offset, value) \ + do { \ + iowrite32((value), (tod)->regs + (offset)); \ + if ((tod)->sec_regs) { \ + iowrite32((value), (tod)->sec_regs + (offset)); \ + } \ + } while (0) +#define ADRV906X_REG_READ(tod, offset) \ + ioread32((tod)->regs + (offset)) +#define ADRV906X_REG_READ_SEC(tod, offset) \ + ioread32((tod)->sec_regs + (offset)) + +#endif /* __PTP_ADRV906X_H */ diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index 6a8daeb8c4b96c..40681d0827e460 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only menu "SOC (System On Chip) specific Drivers" +source "drivers/soc/adi/Kconfig" source "drivers/soc/amlogic/Kconfig" source "drivers/soc/apple/Kconfig" source "drivers/soc/aspeed/Kconfig" diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 2037a8695cb289..f4b4388412d777 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -35,3 +35,4 @@ obj-y += ti/ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-y += versatile/ obj-y += xilinx/ +obj-$(CONFIG_SOC_ADRV906X) += adi/ diff --git a/drivers/soc/adi/Kconfig b/drivers/soc/adi/Kconfig new file mode 100644 index 00000000000000..66f11a7e857aca --- /dev/null +++ b/drivers/soc/adi/Kconfig @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 + +config SOC_ADRV906X + bool "ADI ADRV906X SoC driver support" + depends on ARCH_ADRV906X + default ARCH_ADRV906X + help + Say yes here to add support for ADI ADRV906X SoC diff --git a/drivers/soc/adi/Makefile b/drivers/soc/adi/Makefile new file mode 100644 index 00000000000000..58fadf7555c417 --- /dev/null +++ b/drivers/soc/adi/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_SOC_ADRV906X) += adrv906x-err.o adrv906x-reboot-mode.o diff --git a/drivers/soc/adi/adrv906x-err.c b/drivers/soc/adi/adrv906x-err.c new file mode 100644 index 00000000000000..bf1fbbbdcf8722 --- /dev/null +++ b/drivers/soc/adi/adrv906x-err.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include +#include +#include + +#include "adrv906x-status-reg.h" + +/* This value MUST MATCH the implementation the following repos + * + * U-boot: /arch/arm/mach-adrv906x/adrv906x_status_reg.c + * TF-A: /plat/adi/adrv/adrv906x/adrv906x_status_reg.c + * OP-TEE os: /core/drivers/adi/adrv906x/adrv906x_status_reg.c + */ +#define RESET_CAUSE_NS_OFFSET 0 + +/* + * List of reasons reset was performed which gets stored in RESET_CAUSE + * This enum MUST MATCH those defined in the following repos + * + * U-boot: /arch/arm/mach-adrv906x/include/plat_status_reg.h + * TF-A: /plat/adi/adrv/common/include/plat_status_reg.h + * OP-TEE os: /core/drivers/adi/adrv906x/adrv906x_status_reg.c + */ +enum reset_cause_t { + COLD_BOOT, + WARM_RESET, + IMG_VERIFY_FAIL, + WATCHDOG_RESET, + CACHE_ECC_ERROR, + DRAM_ECC_ERROR, + DRAM_INIT_ERROR, + MCS_FAIL, + MBIAS_CAL_FAIL, + OTHER_RESET_CAUSE, +}; + +static struct kobject *err_kobj; +static int reset_cause; + +static int wr_reset_cause(enum reset_cause_t cause) +{ + void *io; + + io = memremap(A55_SYS_CFG + SCRATCH_NS + RESET_CAUSE_NS_OFFSET, SZ_4K, MEMREMAP_WT); + + if (io == NULL) { + pr_err("Unable to map to virtual address\n"); + return 0; + } + + iowrite32(cause, io); + + return 0; +} + +static int rd_reset_cause(void) +{ + void *io; + + io = memremap(A55_SYS_CFG + SCRATCH_NS + RESET_CAUSE_NS_OFFSET, SZ_4K, MEMREMAP_WT); + + if (io == NULL) { + pr_err("Unable to map to virtual address\n"); + return 0; + } + + return ioread32(io); +} + +static int plat_panic_handler(struct notifier_block *nb, unsigned long reason, void *arg) +{ + if (strcmp(arg, "dm-verity device corrupted") == 0) + wr_reset_cause(IMG_VERIFY_FAIL); + else + wr_reset_cause(OTHER_RESET_CAUSE); + + return NOTIFY_DONE; +} + +static struct notifier_block plat_panic_notifier = { + .notifier_call = plat_panic_handler +}; + +static int plat_reboot_handler(struct notifier_block *nb, unsigned long reason, void *arg) +{ + /* In a reboot scenario, assuming that userspace triggered the reboot and will have to set the reset cause */ + + return NOTIFY_DONE; +} + +static struct notifier_block plat_reboot_notifier = { + .notifier_call = plat_reboot_handler +}; + +static ssize_t reset_cause_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", rd_reset_cause()); +} + +static ssize_t reset_cause_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) +{ + if (sscanf(buf, "%du", &reset_cause) == 1) + wr_reset_cause(reset_cause); + + return count; +} + +static struct kobj_attribute reset_cause_attribute = __ATTR(reset_cause, 0660, reset_cause_show, reset_cause_store); + +static int __init err_handler_init(void) +{ + int ret = 0; + + err_kobj = kobject_create_and_add("err", kernel_kobj); + + if (!err_kobj) + return -ENOMEM; + + ret = sysfs_create_file(err_kobj, &reset_cause_attribute.attr); + if (ret) { + pr_err("Failed to create the reset_cause file in /sys/kernel/err\n"); + return ret; + } + + ret = register_reboot_notifier(&plat_reboot_notifier); + if (ret) { + pr_err("Unable to register reboot notifier\n"); + return ret; + } + + atomic_notifier_chain_register(&panic_notifier_list, &plat_panic_notifier); + if (ret) + pr_err("Unable to register panic notifier\n"); + + return ret; +} + +static void __exit err_handler_exit(void) +{ + kobject_put(err_kobj); + sysfs_remove_file(err_kobj, &reset_cause_attribute.attr); + + unregister_reboot_notifier(&plat_reboot_notifier); + atomic_notifier_chain_unregister(&panic_notifier_list, &plat_panic_notifier); +} + +module_init(err_handler_init); +module_exit(err_handler_exit); + +MODULE_AUTHOR("Analog Devices, Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/adi/adrv906x-reboot-mode.c b/drivers/soc/adi/adrv906x-reboot-mode.c new file mode 100644 index 00000000000000..af8a1e337b10cd --- /dev/null +++ b/drivers/soc/adi/adrv906x-reboot-mode.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024, Analog Devices Incorporated, All Rights Reserved + */ + +#include +#include +#include + +enum { + ADRV_REBOOT_WARM = 0, + ADRV_REBOOT_COLD, +}; + +static struct kobject *reboot_mode_kobj; +static int mode; + +static ssize_t reboot_mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", mode); +} + +static ssize_t reboot_mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) +{ + int tmp_mode = 0, result = 0; + + result = sscanf(buf, "%du", &tmp_mode); + if (result == 1) { + if (tmp_mode == ADRV_REBOOT_WARM || tmp_mode == ADRV_REBOOT_COLD) { + mode = tmp_mode; + reboot_mode = (mode == ADRV_REBOOT_WARM ? REBOOT_WARM : REBOOT_COLD); + } else { + pr_err("Failed in %s, setting for mode = %d is not supported!\n", __func__, tmp_mode); + return -EINVAL; + } + + return count; + } else { + return -EIO; + } +} + +static struct kobj_attribute reboot_mode_attribute = __ATTR(mode, 0660, reboot_mode_show, reboot_mode_store); + +static int __init reboot_mode_handler_init(void) +{ + int ret = 0; + + reboot_mode_kobj = kobject_create_and_add("adrv-reboot", kernel_kobj); + + if (!reboot_mode_kobj) + return -ENOMEM; + + ret = sysfs_create_file(reboot_mode_kobj, &reboot_mode_attribute.attr); + if (ret) { + pr_err("Failed to create the mode file in /sys/kernel/reboot.\n"); + return ret; + } + + return ret; +} + +static void __exit reboot_mode_handler_exit(void) +{ + kobject_put(reboot_mode_kobj); + sysfs_remove_file(reboot_mode_kobj, &reboot_mode_attribute.attr); +} + +module_init(reboot_mode_handler_init); +module_exit(reboot_mode_handler_exit); + +MODULE_AUTHOR("Analog Devices, Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/adi/adrv906x-status-reg.h b/drivers/soc/adi/adrv906x-status-reg.h new file mode 100644 index 00000000000000..de64b51de69c10 --- /dev/null +++ b/drivers/soc/adi/adrv906x-status-reg.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2025, Analog Devices Incorporated, All Rights Reserved + */ + +#ifndef __ADI_ADRV906X_STATUS_REG_H__ +#define __ADI_ADRV906X_STATUS_REG_H__ + +#define A55_SYS_CFG 0x20100000 +#define SCRATCH_NS 0x80000 + + +#endif /* __ADI_ADRV906X_STATUS_REG_H__ */ diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 82379721740480..c20e925a023e8c 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -192,6 +192,11 @@ config SPI_BCM2835AUX "universal SPI master", and the regular SPI controller. This driver is for the universal/auxiliary SPI controller. +config SPI_ADI_V3 + tristate "SPI controller v3 for ADI" + help + This is the SPI controller v3 master driver found on ADI ADRV906X SoC. + config SPI_BCM63XX tristate "Broadcom BCM63xx SPI controller" depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index a9b1bc259b68d1..f4618611f8bf91 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -133,6 +133,7 @@ obj-$(CONFIG_SPI_SLAVE_MT27XX) += spi-slave-mt27xx.o obj-$(CONFIG_SPI_SN_F_OSPI) += spi-sn-f-ospi.o obj-$(CONFIG_SPI_SPRD) += spi-sprd.o obj-$(CONFIG_SPI_SPRD_ADI) += spi-sprd-adi.o +obj-$(CONFIG_SPI_ADI_V3) += spi-adi-v3.o obj-$(CONFIG_SPI_STM32) += spi-stm32.o obj-$(CONFIG_SPI_STM32_QSPI) += spi-stm32-qspi.o obj-$(CONFIG_SPI_ST_SSC4) += spi-st-ssc4.o diff --git a/drivers/spi/spi-adi-v3.c b/drivers/spi/spi-adi-v3.c new file mode 100644 index 00000000000000..7b74831be743eb --- /dev/null +++ b/drivers/spi/spi-adi-v3.c @@ -0,0 +1,928 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices SPI3 controller driver + * + * Copyright (c) 2014 - 2024 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * ADI SPI registers layout + */ +#define ADI_SPI_REVID 0x00 + +#define ADI_SPI_CTL 0x04 +#define SPI_CTL_SOSI BIT(22) /* Start on MOSI */ +#define SPI_CTL_MIOM GENMASK(21, 20) /* Multiple I/O Mode */ +#define SPI_CTL_MIO_DIS 0x00000000 /* MIOM: Disable */ +#define SPI_CTL_MIO_DUAL 0x00100000 /* MIOM: Enable DIOM (Dual I/O Mode) */ +#define SPI_CTL_MIO_QUAD 0x00200000 /* MIOM: Enable QUAD (Quad SPI Mode) */ +#define SPI_CTL_FMODE BIT(18) /* Fast-mode Enable */ +#define SPI_CTL_FCWM GENMASK(17, 16) /* Flow-Control Watermark */ +#define SPI_CTL_FIFO0 0x00000000 /* FCWM: TFIFO empty or RFIFO Full */ +#define SPI_CTL_FIFO1 0x00010000 /* FCWM: TFIFO 75% or more empty or RFIFO 75% or more full */ +#define SPI_CTL_FIFO2 0x00020000 /* FCWM: TFIFO 50% or more empty or RFIFO 50% or more full */ +#define SPI_CTL_FCPL BIT(15) /* Flow-Control Polarity */ +#define SPI_CTL_FCCH BIT(14) /* Flow-Control Channel Selection */ +#define SPI_CTL_FCEN BIT(13) /* Flow-Control Enable */ +#define SPI_CTL_LSBF BIT(12) /* LSB First */ +#define SPI_CTL_SIZE GENMASK(10, 9) /* Word Transfer Size */ +#define SPI_CTL_SIZE08 0x00000000 /* SIZE: 8 bits */ +#define SPI_CTL_SIZE16 0x00000200 /* SIZE: 16 bits */ +#define SPI_CTL_SIZE32 0x00000400 /* SIZE: 32 bits */ +#define SPI_CTL_EMISO BIT(8) /* Enable MISO */ +#define SPI_CTL_SELST BIT(7) /* Slave Select Polarity Between Transfers */ +#define SPI_CTL_ASSEL BIT(6) /* Slave Select Pin Control */ +#define SPI_CTL_CPOL BIT(5) /* Clock Polarity */ +#define SPI_CTL_CPHA BIT(4) /* Clock Phase */ +#define SPI_CTL_ODM BIT(3) /* Open Drain Mode */ +#define SPI_CTL_PSSE BIT(2) /* Protected Slave Select Enable */ +#define SPI_CTL_MSTR BIT(1) /* Master/Slave */ +#define SPI_CTL_EN BIT(0) /* Enable */ + +#define ADI_SPI_RXCTL 0x08 +#define SPI_RXCTL_RUWM GENMASK(18, 16) /* FIFO Urgent Watermark */ +#define SPI_RXCTL_UWM_DIS 0x00000000 /* RUWM: Disabled */ +#define SPI_RXCTL_UWM_25 0x00010000 /* RUWM: RFIFO 25% full */ +#define SPI_RXCTL_UWM_50 0x00020000 /* RUWM: RFIFO 50% full */ +#define SPI_RXCTL_UWM_75 0x00030000 /* RUWM: RFIFO 75% full */ +#define SPI_RXCTL_UWM_FULL 0x00040000 /* RUWM: RFIFO full */ +#define SPI_RXCTL_RRWM GENMASK(13, 12) /* FIFO Regular Watermark */ +#define SPI_RXCTL_RWM_0 0x00000000 /* RRWM: RFIFO Empty */ +#define SPI_RXCTL_RWM_25 0x00001000 /* RRWM: RFIFO 25% full */ +#define SPI_RXCTL_RWM_50 0x00002000 /* RRWM: RFIFO 50% full */ +#define SPI_RXCTL_RWM_75 0x00003000 /* RRWM: RFIFO 75% full */ +#define SPI_RXCTL_RDO BIT(8) /* Receive Data Overrun */ +#define SPI_RXCTL_RDR GENMASK(6, 4) /* Receive Data Request */ +#define SPI_RXCTL_RDR_DIS 0x00000000 /* RDR: Disabled */ +#define SPI_RXCTL_RDR_NE 0x00000010 /* RDR: RFIFO not empty */ +#define SPI_RXCTL_RDR_25 0x00000020 /* RDR: RFIFO 25% full */ +#define SPI_RXCTL_RDR_50 0x00000030 /* RDR: RFIFO 50% full */ +#define SPI_RXCTL_RDR_75 0x00000040 /* RDR: RFIFO 75% full */ +#define SPI_RXCTL_RDR_FULL 0x00000050 /* RDR: RFIFO full */ +#define SPI_RXCTL_RWCEN BIT(3) /* Receive Word Counter Enable */ +#define SPI_RXCTL_RTI BIT(2) /* Receive Transfer Initiate */ +#define SPI_RXCTL_REN BIT(0) /* Receive Channel Enable */ + +#define ADI_SPI_TXCTL 0x0c +#define SPI_TXCTL_TUWM GENMASK(18, 16) /* FIFO Urgent Watermark */ +#define SPI_TXCTL_UWM_DIS 0x00000000 /* TUWM: Disabled */ +#define SPI_TXCTL_UWM_25 0x00010000 /* TUWM: TFIFO 25% empty */ +#define SPI_TXCTL_UWM_50 0x00020000 /* TUWM: TFIFO 50% empty */ +#define SPI_TXCTL_UWM_75 0x00030000 /* TUWM: TFIFO 75% empty */ +#define SPI_TXCTL_UWM_EMPTY 0x00040000 /* TUWM: TFIFO empty */ +#define SPI_TXCTL_TRWM GENMASK(13, 12) /* FIFO Regular Watermark */ +#define SPI_TXCTL_RWM_FULL 0x00000000 /* TRWM: TFIFO full */ +#define SPI_TXCTL_RWM_25 0x00001000 /* TRWM: TFIFO 25% empty */ +#define SPI_TXCTL_RWM_50 0x00002000 /* TRWM: TFIFO 50% empty */ +#define SPI_TXCTL_RWM_75 0x00003000 /* TRWM: TFIFO 75% empty */ +#define SPI_TXCTL_TDU BIT(8) /* Transmit Data Under-Run */ +#define SPI_TXCTL_TDR GENMASK(6, 4) /* Transmit Data Request */ +#define SPI_TXCTL_TDR_DIS 0x00000000 /* TDR: Disabled */ +#define SPI_TXCTL_TDR_NF 0x00000010 /* TDR: TFIFO not full */ +#define SPI_TXCTL_TDR_25 0x00000020 /* TDR: TFIFO 25% empty */ +#define SPI_TXCTL_TDR_50 0x00000030 /* TDR: TFIFO 50% empty */ +#define SPI_TXCTL_TDR_75 0x00000040 /* TDR: TFIFO 75% empty */ +#define SPI_TXCTL_TDR_EMPTY 0x00000050 /* TDR: TFIFO empty */ +#define SPI_TXCTL_TWCEN BIT(3) /* Transmit Word Counter Enable */ +#define SPI_TXCTL_TTI BIT(2) /* Transmit Transfer Initiate */ +#define SPI_TXCTL_TEN BIT(0) /* Transmit Channel Enable */ + +#define ADI_SPI_CLK 0x10 +#define SPI_CLK_BAUD GENMASK(15, 0) /* Baud Rate */ + +#define ADI_SPI_DLY 0x14 +#define SPI_DLY_LAGX BIT(9) /* Extended (1 SCK) LAG control */ +#define SPI_DLY_LEADX BIT(8) /* Extended (1 SCK) LEAD Control */ +#define SPI_DLY_STOP GENMASK(7, 0) /* Transfer delay time in multiples of SCK period */ + +#define ADI_SPI_SLVSEL 0x18 +#define SPI_SLVSEL_SSEL7 BIT(15) /* SPISSEL7 Value */ +#define SPI_SLVSEL_SSEL6 BIT(14) /* SPISSEL6 Value */ +#define SPI_SLVSEL_SSEL5 BIT(13) /* SPISSEL5 Value */ +#define SPI_SLVSEL_SSEL4 BIT(12) /* SPISSEL4 Value */ +#define SPI_SLVSEL_SSEL3 BIT(11) /* SPISSEL3 Value */ +#define SPI_SLVSEL_SSEL2 BIT(10) /* SPISSEL2 Value */ +#define SPI_SLVSEL_SSEL1 BIT(9) /* SPISSEL1 Value */ +#define SPI_SLVSEL_SSE7 BIT(7) /* SPISSEL7 Enable */ +#define SPI_SLVSEL_SSE6 BIT(6) /* SPISSEL6 Enable */ +#define SPI_SLVSEL_SSE5 BIT(5) /* SPISSEL5 Enable */ +#define SPI_SLVSEL_SSE4 BIT(4) /* SPISSEL4 Enable */ +#define SPI_SLVSEL_SSE3 BIT(3) /* SPISSEL3 Enable */ +#define SPI_SLVSEL_SSE2 BIT(2) /* SPISSEL2 Enable */ +#define SPI_SLVSEL_SSE1 BIT(1) /* SPISSEL1 Enable */ + +#define ADI_SPI_RWC 0x1c +#define SPI_RWC_VALUE GENMASK(15, 0) /* Received Word-Count */ + +#define ADI_SPI_RWCR 0x20 +#define SPI_RWCR_VALUE GENMASK(15, 0) /* Received Word-Count Reload */ + +#define ADI_SPI_TWC 0x24 +#define SPI_TWC_VALUE GENMASK(15, 0) /* Transmitted Word-Count */ + +#define ADI_SPI_TWCR 0x28 +#define SPI_TWCR_VALUE GENMASK(15, 0) /* Transmitted Word-Count Reload */ + +#define ADI_SPI_IMSK 0x30 +#define SPI_IMSK_TFM BIT(11) /* Transmit Finish Interrupt Mask */ +#define SPI_IMSK_RFM BIT(10) /* Receive Finish Interrupt Mask */ +#define SPI_IMSK_TSM BIT(9) /* Transmit Start Interrupt Mask */ +#define SPI_IMSK_RSM BIT(8) /* Receive Start Interrupt Mask */ +#define SPI_IMSK_MFM BIT(7) /* Mode Fault Error Interrupt Mask */ +#define SPI_IMSK_TCM BIT(6) /* Transmit Collision Error Interrupt Mask */ +#define SPI_IMSK_TUM BIT(5) /* Transmit Under-Run Error Interrupt Mask */ +#define SPI_IMSK_ROM BIT(4) /* Receive Overrun Error Interrupt Mask */ +#define SPI_IMSK_TUWM BIT(2) /* Transmit Urgent Watermark Interrupt Mask */ +#define SPI_IMSK_RUWM BIT(1) /* Receive Urgent Watermark Interrupt Mask */ + +#define ADI_SPI_IMSK_CLR 0x34 +#define SPI_IMSK_CLR_TFM BIT(11) /* Clear Transmit Finish Interrupt Mask */ +#define SPI_IMSK_CLR_RFM BIT(10) /* Clear Receive Finish Interrupt Mask */ +#define SPI_IMSK_CLR_TSM BIT(9) /* Clear Transmit Start Interrupt Mask */ +#define SPI_IMSK_CLR_RSM BIT(8) /* Clear Receive Start Interrupt Mask */ +#define SPI_IMSK_CLR_MFM BIT(7) /* Clear Mode Fault Interrupt Mask */ +#define SPI_IMSK_CLR_TCM BIT(6) /* Clear Transmit Collision Interrupt Mask */ +#define SPI_IMSK_CLR_TUM BIT(5) /* Clear Transmit Under-run Interrupt Mask */ +#define SPI_IMSK_CLR_ROM BIT(4) /* Clear Receive Overrun Interrupt Mask */ +#define SPI_IMSK_CLR_TUWM BIT(2) /* Clear Transmit Urgent Watermark Interrupt Mask */ +#define SPI_IMSK_CLR_RUW BIT(1) /* Clear Receive Urgent Watermark Interrupt Mask */ + +#define ADI_SPI_IMSK_SET 0x38 +#define SPI_IMSK_SET_TFM BIT(11) /* Set Transmit Finish Interrupt Mask */ +#define SPI_IMSK_SET_RFM BIT(10) /* Set Receive Finish Interrupt Mask */ +#define SPI_IMSK_SET_TSM BIT(9) /* Set Transmit Start Interrupt Mask */ +#define SPI_IMSK_SET_RSM BIT(8) /* Set Receive Start Interrupt Mask */ +#define SPI_IMSK_SET_MFM BIT(7) /* Set Mode Fault Interrupt Mask */ +#define SPI_IMSK_SET_TCM BIT(6) /* Set Transmit Collision Interrupt Mask */ +#define SPI_IMSK_SET_TUM BIT(5) /* Set Transmit Under-run Interrupt Mask */ +#define SPI_IMSK_SET_ROM BIT(4) /* Set Receive Overrun Interrupt Mask */ +#define SPI_IMSK_SET_TUWM BIT(2) /* Set Transmit Urgent Watermark Interrupt Mask */ +#define SPI_IMSK_SET_RUWM BIT(1) /* Set Receive Urgent Watermark Interrupt Mask */ + +#define ADI_SPI_STAT 0x40 +#define SPI_STAT_TFF BIT(23) /* SPI_TFIFO Full */ +#define SPI_STAT_RFE BIT(22) /* SPI_RFIFO Empty */ +#define SPI_STAT_FCS BIT(20) /* Flow-Control Stall Indication */ +#define SPI_STAT_TFS GENMASK(18, 16) /* SPI_TFIFO status */ +#define SPI_STAT_TFIFO_FULL 0x00000000 /* TFS: TFIFO full */ +#define SPI_STAT_TFIFO_25 0x00010000 /* TFS: TFIFO 25% empty */ +#define SPI_STAT_TFIFO_50 0x00020000 /* TFS: TFIFO 50% empty */ +#define SPI_STAT_TFIFO_75 0x00030000 /* TFS: TFIFO 75% empty */ +#define SPI_STAT_TFIFO_EMPTY 0x00040000 /* TFS: TFIFO empty */ +#define SPI_STAT_RFS GENMASK(14, 12) /* SPI_RFIFO status */ +#define SPI_STAT_RFIFO_EMPTY 0x00000000 /* RFS: RFIFO Empty */ +#define SPI_STAT_RFIFO_25 0x00001000 /* RFS: RFIFO 25% Full */ +#define SPI_STAT_RFIFO_50 0x00002000 /* RFS: RFIFO 50% Full */ +#define SPI_STAT_RFIFO_75 0x00003000 /* RFS: RFIFO 75% Full */ +#define SPI_STAT_RFIFO_FULL 0x00004000 /* RFS: RFIFO Full */ +#define SPI_STAT_TF BIT(11) /* Transmit Finish Indication */ +#define SPI_STAT_RF BIT(10) /* Receive Finish Indication */ +#define SPI_STAT_TS BIT(9) /* Transmit Start */ +#define SPI_STAT_RS BIT(8) /* Receive Start */ +#define SPI_STAT_MODF BIT(7) /* Mode Fault Error Indication */ +#define SPI_STAT_TCE BIT(6) /* Transmit Collision Error Indication */ +#define SPI_STAT_TUE BIT(5) /* Transmit Under-Run Error Indication */ +#define SPI_STAT_ROE BIT(4) /* Receive Overrun Error Indication */ +#define SPI_STAT_TUWM BIT(2) /* Transmit Urgent Watermark Breached */ +#define SPI_STAT_RUWM BIT(1) /* Receive Urgent Watermark Breached */ +#define SPI_STAT_SPIF BIT(0) /* SPI Finished */ + +#define ADI_SPI_ILAT 0x44 +#define SPI_ILAT_TFI BIT(11) /* Transmit Finish Interrupt Latch */ +#define SPI_ILAT_RFI BIT(10) /* Receive Finish Interrupt Latch */ +#define SPI_ILAT_TSI BIT(9) /* Transmit Start Interrupt Latch */ +#define SPI_ILAT_RSI BIT(8) /* Receive Start Interrupt Latch */ +#define SPI_ILAT_MFI BIT(7) /* Mode Fault Interrupt Latch */ +#define SPI_ILAT_TCI BIT(6) /* Transmit Collision Interrupt Latch */ +#define SPI_ILAT_TUI BIT(5) /* Transmit Under-run Interrupt Latch */ +#define SPI_ILAT_ROI BIT(4) /* Receive Overrun Interrupt Latch */ +#define SPI_ILAT_TUWMI BIT(2) /* Transmit Urgent Watermark Interrupt Latch */ +#define SPI_ILAT_RUWMI BIT(1) /* Receive Urgent Watermark Interrupt Latch */ + +#define ADI_SPI_ILAT_CLR 0x48 +#define SPI_ILAT_CLR_TFI BIT(11) /* Clear Transmit Finish Interrupt Latch */ +#define SPI_ILAT_CLR_RFI BIT(10) /* Clear Receive Finish Interrupt Latch */ +#define SPI_ILAT_CLR_TSI BIT(9) /* Clear Transmit Start Interrupt Latch */ +#define SPI_ILAT_CLR_RSI BIT(8) /* Clear Receive Start Interrupt Latch */ +#define SPI_ILAT_CLR_MFI BIT(7) /* Clear Mode Fault Interrupt Latch */ +#define SPI_ILAT_CLR_TCI BIT(6) /* Clear Transmit Collision Interrupt Latch */ +#define SPI_ILAT_CLR_TUI BIT(5) /* Clear Transmit Under-run Interrupt Latch */ +#define SPI_ILAT_CLR_ROI BIT(4) /* Clear Receive Overrun Interrupt Latch */ +#define SPI_ILAT_CLR_TUWMI BIT(2) /* Clear Transmit Urgent Watermark Interrupt Latch */ +#define SPI_ILAT_CLR_RUWMI BIT(1) /* Clear Receive Urgent Watermark Interrupt Latch */ + +#define ADI_SPI_RFIFO 0x50 +#define ADI_SPI_TFIFO 0x58 + +#define SPI_MAX_SS 7 /* Maximum number of native slave selects */ +#define SPI_SSE(n) BIT((n) + 1) /* Slave Select Enable (SSE-x) Bit Select */ +#define SPI_SSEL(n) BIT((n) + 9) /* Slave Select Value (SSEL-x) Bit Select */ + +#define MAX_SPI_TRANSFER_SIZE ((64 * 1024) - 4096) /* Arbitrary value lower than 64K (DDE transfer limit) */ + +struct adi_spi_controller; + +struct adi_spi_transfer_ops { + void (*write)(struct adi_spi_controller *drv, struct spi_transfer *xfer); + void (*read)(struct adi_spi_controller *drv, struct spi_transfer *xfer); + void (*duplex)(struct adi_spi_controller *drv, struct spi_transfer *xfer); +}; + +/* runtime info for spi controller */ +struct adi_spi_controller { + /* SPI framework hookup */ + struct spi_controller *ctlr; + struct device *dev; + + /* Regs base of SPI controller */ + void __iomem *regs; + + int irq; + + /* Current message transfer state info */ + struct spi_transfer *cur_transfer; + const struct adi_spi_transfer_ops *ops; + dma_cookie_t tx_cookie; + dma_cookie_t rx_cookie; + + /* store register value for suspend/resume */ + u32 control; + u32 ssel; + + struct clk *sclk; + unsigned long sclk_rate; +}; + +struct adi_spi_device { + bool dma; + u32 control; +}; + +static void adi_spi_disable(struct adi_spi_controller *drv_data) +{ + u32 ctl; + + ctl = ioread32(drv_data->regs + ADI_SPI_CTL); + ctl &= ~SPI_CTL_EN; + iowrite32(ctl, drv_data->regs + ADI_SPI_CTL); +} + +static void adi_spi_dma_terminate(struct adi_spi_controller *drv_data) +{ + dmaengine_terminate_sync(drv_data->ctlr->dma_tx); + dmaengine_terminate_sync(drv_data->ctlr->dma_rx); +} + +/* Caculate the SPI_CLOCK register value based on input HZ */ +static u32 hz_to_spi_clock(u32 sclk, u32 speed_hz) +{ + u32 spi_clock = DIV_ROUND_UP(sclk, speed_hz); + + if (spi_clock) + spi_clock--; + + return spi_clock; +} + +static void adi_spi_u8_write(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + iowrite32(*(u8 *)(xfer->tx_buf + i), drv->regs + ADI_SPI_TFIFO); + while (ioread32(drv->regs + ADI_SPI_STAT) & SPI_STAT_RFE) + cpu_relax(); + ioread32(drv->regs + ADI_SPI_RFIFO); + } +} + +static void adi_spi_u16_write(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + iowrite32(*(u16 *)(xfer->tx_buf + 2 * i), drv->regs + ADI_SPI_TFIFO); + while (ioread32(drv->regs + ADI_SPI_STAT) & SPI_STAT_RFE) + cpu_relax(); + ioread32(drv->regs + ADI_SPI_RFIFO); + } +} + +static void adi_spi_u32_write(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + iowrite32(*(u32 *)(xfer->tx_buf + 4 * i), drv->regs + ADI_SPI_TFIFO); + while (ioread32(drv->regs + ADI_SPI_STAT) & SPI_STAT_RFE) + cpu_relax(); + ioread32(drv->regs + ADI_SPI_RFIFO); + } +} + +static void adi_spi_u8_read(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + while (ioread32(drv->regs + ADI_SPI_STAT) & SPI_STAT_RFE) + cpu_relax(); + *(u8 *)(xfer->rx_buf + i) = ioread32(drv->regs + ADI_SPI_RFIFO); + } +} + +static void adi_spi_u16_read(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + while (ioread32(drv->regs + ADI_SPI_STAT) & SPI_STAT_RFE) + cpu_relax(); + *(u16 *)(xfer->rx_buf + 2 * i) = ioread32(drv->regs + ADI_SPI_RFIFO); + } +} + +static void adi_spi_u32_read(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + while (ioread32(drv->regs + ADI_SPI_STAT) & SPI_STAT_RFE) + cpu_relax(); + *(u32 *)(xfer->rx_buf + 4 * i) = ioread32(drv->regs + ADI_SPI_RFIFO); + } +} + +static void adi_spi_u8_duplex(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + iowrite32(*(u8 *)(xfer->tx_buf + i), drv->regs + ADI_SPI_TFIFO); + while (ioread32(drv->regs + ADI_SPI_STAT) & SPI_STAT_RFE) + cpu_relax(); + *(u8 *)(xfer->rx_buf + i) = ioread32(drv->regs + ADI_SPI_RFIFO); + } +} + +static void adi_spi_u16_duplex(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + iowrite32(*(u16 *)(xfer->tx_buf + 2 * i), drv->regs + ADI_SPI_TFIFO); + while (ioread32(drv->regs + ADI_SPI_STAT) & SPI_STAT_RFE) + cpu_relax(); + *(u16 *)(xfer->rx_buf + 2 * i) = ioread32(drv->regs + ADI_SPI_RFIFO); + } +} + +static void adi_spi_u32_duplex(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + iowrite32(*(u32 *)(xfer->tx_buf + 4 * i), drv->regs + ADI_SPI_TFIFO); + while (ioread32(drv->regs + ADI_SPI_STAT) & SPI_STAT_RFE) + cpu_relax(); + *(u32 *)(xfer->rx_buf + 4 * i) = ioread32(drv->regs + ADI_SPI_RFIFO); + } +} + +static const struct adi_spi_transfer_ops adi_spi_transfer_ops_u8 = { + .write = adi_spi_u8_write, + .read = adi_spi_u8_read, + .duplex = adi_spi_u8_duplex, +}; + +static const struct adi_spi_transfer_ops adi_spi_transfer_ops_u16 = { + .write = adi_spi_u16_write, + .read = adi_spi_u16_read, + .duplex = adi_spi_u16_duplex, +}; + +static const struct adi_spi_transfer_ops adi_spi_transfer_ops_u32 = { + .write = adi_spi_u32_write, + .read = adi_spi_u32_read, + .duplex = adi_spi_u32_duplex, +}; + +static int adi_spi_pio_xfer(struct spi_controller *ctlr, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct adi_spi_controller *drv = spi_controller_get_devdata(ctlr); + + if (!xfer->rx_buf) { + iowrite32(SPI_RXCTL_REN, drv->regs + ADI_SPI_RXCTL); + iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI, drv->regs + ADI_SPI_TXCTL); + drv->ops->write(drv, xfer); + } else if (!xfer->tx_buf) { + iowrite32(0, drv->regs + ADI_SPI_TXCTL); + iowrite32(SPI_RXCTL_REN | SPI_RXCTL_RTI, drv->regs + ADI_SPI_RXCTL); + drv->ops->read(drv, xfer); + } else { + iowrite32(SPI_RXCTL_REN, drv->regs + ADI_SPI_RXCTL); + iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI, drv->regs + ADI_SPI_TXCTL); + drv->ops->duplex(drv, xfer); + } + + iowrite32(0, drv->regs + ADI_SPI_TXCTL); + iowrite32(0, drv->regs + ADI_SPI_RXCTL); + return 0; +} + +/* + * Disable both paths and alert spi core that this transfer is done + */ +static void adi_spi_rx_dma_isr(void *data) +{ + struct adi_spi_controller *drv_data = data; + + struct dma_tx_state state; + enum dma_status status; + + status = dmaengine_tx_status(drv_data->ctlr->dma_rx, drv_data->rx_cookie, &state); + if (status == DMA_ERROR) + dev_err(&drv_data->ctlr->dev, "spi rx dma error\n"); + + iowrite32(0, drv_data->regs + ADI_SPI_TXCTL); + iowrite32(0, drv_data->regs + ADI_SPI_RXCTL); + spi_finalize_current_transfer(drv_data->ctlr); +} + +/* + * Disable tx path and enable rx path for dual/quad modes + */ +static void adi_spi_tx_dma_isr(void *data) +{ + struct adi_spi_controller *drv = data; + struct dma_tx_state state; + enum dma_status status; + + status = dmaengine_tx_status(drv->ctlr->dma_tx, drv->tx_cookie, &state); + if (status == DMA_ERROR) + dev_err(&drv->ctlr->dev, "spi tx dma error\n"); + + iowrite32(0, drv->regs + ADI_SPI_TXCTL); + + if (drv->cur_transfer->rx_buf) { + iowrite32(SPI_RXCTL_REN | SPI_RXCTL_RTI | SPI_RXCTL_RDR_NE, + drv->regs + ADI_SPI_RXCTL); + dma_async_issue_pending(drv->ctlr->dma_rx); + } else { + spi_finalize_current_transfer(drv->ctlr); + } +} + +static int adi_spi_dma_xfer(struct spi_controller *ctlr, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct adi_spi_controller *drv = spi_controller_get_devdata(ctlr); + struct dma_async_tx_descriptor *tx_desc; + struct dma_async_tx_descriptor *rx_desc; + + if (xfer->tx_buf) { + tx_desc = dmaengine_prep_slave_sg(ctlr->dma_tx, xfer->tx_sg.sgl, + xfer->tx_sg.nents, DMA_MEM_TO_DEV, 0); + if (!tx_desc) { + dev_err(drv->dev, "Unable to allocate TX DMA descriptor\n"); + goto error; + } + + if (!xfer->rx_buf) { + tx_desc->callback = adi_spi_tx_dma_isr; + tx_desc->callback_param = drv; + } + drv->tx_cookie = dmaengine_submit(tx_desc); + + iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI | SPI_TXCTL_TDR_NF, + drv->regs + ADI_SPI_TXCTL); + dma_async_issue_pending(ctlr->dma_tx); + } + + if (xfer->rx_buf) { + rx_desc = dmaengine_prep_slave_sg(ctlr->dma_rx, xfer->rx_sg.sgl, + xfer->rx_sg.nents, DMA_DEV_TO_MEM, 0); + if (!rx_desc) { + dev_err(drv->dev, "Unable to allocate RX DMA descriptor\n"); + goto error; + } + + rx_desc->callback = adi_spi_rx_dma_isr; + rx_desc->callback_param = drv; + drv->rx_cookie = dmaengine_submit(rx_desc); + iowrite32(SPI_RXCTL_REN | SPI_RXCTL_RTI | SPI_RXCTL_RDR_NE, + drv->regs + ADI_SPI_RXCTL); + dma_async_issue_pending(ctlr->dma_rx); + } + + return 1; + +error: + adi_spi_dma_terminate(drv); + return -ENOENT; +} + +static bool adi_spi_can_dma(struct spi_controller *ctlr, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct adi_spi_device *chip = spi_get_ctldata(spi); + + if (chip->dma) + return true; + return false; +} + +static int adi_spi_transfer_one(struct spi_controller *ctlr, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct adi_spi_controller *drv = spi_controller_get_devdata(ctlr); + u32 cr; + + drv->cur_transfer = xfer; + + cr = ioread32(drv->regs + ADI_SPI_CTL) & ~SPI_CTL_MIOM; + + if (xfer->rx_nbits == SPI_NBITS_QUAD || xfer->tx_nbits == SPI_NBITS_QUAD) + cr |= SPI_CTL_MIO_QUAD; + else if (xfer->rx_nbits == SPI_NBITS_DUAL || xfer->tx_nbits == SPI_NBITS_DUAL) + cr |= SPI_CTL_MIO_DUAL; + + iowrite32(cr, drv->regs + ADI_SPI_CTL); + + if (adi_spi_can_dma(ctlr, spi, xfer)) + return adi_spi_dma_xfer(ctlr, spi, xfer); + return adi_spi_pio_xfer(ctlr, spi, xfer); +} + +/* + * Settings like clock speed and bits per word are assumed to be the same for all + * transfers in a message. tx_nbits and rx_nbits can change, however + */ +static int adi_spi_prepare_message(struct spi_controller *ctlr, struct spi_message *msg) +{ + struct adi_spi_controller *drv = spi_controller_get_devdata(ctlr); + struct adi_spi_device *chip = spi_get_ctldata(msg->spi); + struct dma_slave_config dma_config = { 0 }; + struct spi_transfer *xfer; + int ret; + u32 cr, cr_width; + u32 words; + + xfer = list_first_entry(&msg->transfers, struct spi_transfer, transfer_list); + words = DIV_ROUND_UP(xfer->bits_per_word, 8); + iowrite32(hz_to_spi_clock(drv->sclk_rate, xfer->speed_hz), drv->regs + ADI_SPI_CLK); + + switch (words) { + case 1: + cr_width = SPI_CTL_SIZE08; + drv->ops = &adi_spi_transfer_ops_u8; + break; + case 2: + cr_width = SPI_CTL_SIZE16; + drv->ops = &adi_spi_transfer_ops_u16; + break; + case 4: + cr_width = SPI_CTL_SIZE32; + drv->ops = &adi_spi_transfer_ops_u32; + break; + default: + dev_err(&ctlr->dev, "invalid word size in incoming message\n"); + return -EINVAL; + } + + cr = chip->control; + cr |= cr_width | SPI_CTL_EN; + cr &= ~SPI_CTL_SOSI; + iowrite32(cr, drv->regs + ADI_SPI_CTL); + + if (adi_spi_can_dma(ctlr, msg->spi, xfer)) { + dma_config.direction = DMA_MEM_TO_DEV; + dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_config.src_maxburst = words; + dma_config.dst_maxburst = words; + ret = dmaengine_slave_config(ctlr->dma_tx, &dma_config); + if (ret) { + dev_err(drv->dev, "tx dma slave config failed: %d\n", ret); + return ret; + } + + dma_config.direction = DMA_DEV_TO_MEM; + ret = dmaengine_slave_config(ctlr->dma_rx, &dma_config); + if (ret) { + dev_err(drv->dev, "rx dma slave config failed: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int adi_spi_unprepare_message(struct spi_controller *ctlr, struct spi_message *msg) +{ + struct adi_spi_controller *drv = spi_controller_get_devdata(ctlr); + + adi_spi_disable(drv); + return 0; +} + +static void adi_spi_set_cs(struct spi_device *spi, bool enable) +{ + struct adi_spi_controller *drv = spi_controller_get_devdata(spi->controller); + u32 ssel; + + ssel = ioread32(drv->regs + ADI_SPI_SLVSEL); + ssel |= SPI_SSE(spi_get_chipselect(spi, 0)); + + if (enable) + ssel |= SPI_SSEL(spi_get_chipselect(spi, 0)); /* CS deassert */ + else + ssel &= ~SPI_SSEL(spi_get_chipselect(spi, 0)); /* CS assert */ + + /* Required double write to get result on SLVSEL port */ + iowrite32(ssel, drv->regs + ADI_SPI_SLVSEL); + iowrite32(ssel, drv->regs + ADI_SPI_SLVSEL); +} + +static int adi_spi_setup(struct spi_device *spi) +{ + struct adi_spi_device *chip; + struct device_node *np = spi->dev.of_node; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + spi_set_ctldata(spi, chip); + + chip->dma = false; + if (of_property_read_bool(np, "adi,enable-dma")) + chip->dma = true; + + chip->control = 0; + if (of_property_read_bool(np, "adi,open-drain-mode")) + chip->control |= SPI_CTL_ODM; + + if (of_property_read_bool(np, "adi,psse")) + chip->control |= SPI_CTL_PSSE; + + if (spi->mode & SPI_CPOL) + chip->control |= SPI_CTL_CPOL; + if (spi->mode & SPI_CPHA) + chip->control |= SPI_CTL_CPHA; + if (spi->mode & SPI_LSB_FIRST) + chip->control |= SPI_CTL_LSBF; + chip->control |= SPI_CTL_MSTR; + chip->control &= ~SPI_CTL_ASSEL; + + /* Fast Mode */ + chip->control |= SPI_CTL_FMODE; + + return 0; +} + +static void adi_spi_cleanup(struct spi_device *spi) +{ + struct adi_spi_device *chip = spi_get_ctldata(spi); + + if (!chip) + return; + + spi_set_ctldata(spi, NULL); + kfree(chip); +} + +static irqreturn_t spi_irq_err(int irq, void *dev_id) +{ + struct adi_spi_controller *drv_data = dev_id; + u32 status; + + status = ioread32(drv_data->regs + ADI_SPI_STAT); + dev_err(drv_data->dev, "spi error irq, status = 0x%x\n", status); + iowrite32(status, drv_data->regs + ADI_SPI_STAT); + + iowrite32(0, drv_data->regs + ADI_SPI_TXCTL); + iowrite32(0, drv_data->regs + ADI_SPI_RXCTL); + adi_spi_disable(drv_data); + adi_spi_dma_terminate(drv_data); + + return IRQ_HANDLED; +} + +static const struct of_device_id adi_spi_of_match[] = { + { + .compatible = "adi,spi3", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, adi_spi_of_match); + +static int adi_qspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) +{ + if (op->data.nbytes > MAX_SPI_TRANSFER_SIZE) + op->data.nbytes = MAX_SPI_TRANSFER_SIZE; + + return 0; +} + +static int adi_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + /* Once ops is defined, this callback needs to be present + * Do nothing and return error. Fallback will be the regular core + * spi_meme_exec_op implmentation */ + return -ENOTSUPP; +} + +static const struct spi_controller_mem_ops adi_qspi_mem_ops = { + .adjust_op_size = adi_qspi_adjust_op_size, + .exec_op = adi_qspi_exec_op, +}; + +static int adi_spi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spi_controller *ctlr; + struct adi_spi_controller *drv_data; + struct resource *mem; + struct clk *sclk; + int ret; + + sclk = devm_clk_get(dev, "spi"); + if (IS_ERR(sclk)) { + dev_err(dev, "can not get spi clock\n"); + return PTR_ERR(sclk); + } + + ctlr = devm_spi_alloc_master(dev, sizeof(*drv_data)); + if (!ctlr) { + dev_err(dev, "can not alloc spi_controller\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, ctlr); + + /* the mode bits supported by this driver */ + ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | + SPI_TX_DUAL | SPI_TX_QUAD | + SPI_RX_DUAL | SPI_RX_QUAD; + + ctlr->dev.of_node = dev->of_node; + ctlr->bus_num = -1; + ctlr->num_chipselect = SPI_MAX_SS; + ctlr->use_gpio_descriptors = true; + ctlr->cleanup = adi_spi_cleanup; + ctlr->setup = adi_spi_setup; + ctlr->set_cs = adi_spi_set_cs; + ctlr->prepare_message = adi_spi_prepare_message; + ctlr->unprepare_message = adi_spi_unprepare_message; + ctlr->transfer_one = adi_spi_transfer_one; + ctlr->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1); + ctlr->max_native_cs = SPI_MAX_SS; + ctlr->mem_ops = &adi_qspi_mem_ops; + + drv_data = spi_controller_get_devdata(ctlr); + drv_data->ctlr = ctlr; + drv_data->sclk = sclk; + drv_data->sclk_rate = clk_get_rate(sclk); + drv_data->dev = dev; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + drv_data->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(drv_data->regs)) { + dev_err(dev, "Could not map spiv3 memory, check device tree\n"); + return PTR_ERR(drv_data->regs); + } + + drv_data->irq = platform_get_irq(pdev, 0); + if (drv_data->irq <= 0) { + ret = drv_data->irq ? drv_data->irq : -ENXIO; + return ret; + } + ret = devm_request_irq(dev, drv_data->irq, spi_irq_err, + 0, "SPI ERROR", drv_data); + if (ret) { + dev_err(dev, "can not request spi error irq\n"); + return ret; + } + + iowrite32(SPI_CTL_MSTR | SPI_CTL_CPHA, drv_data->regs + ADI_SPI_CTL); + iowrite32(0x0000FE00, drv_data->regs + ADI_SPI_SLVSEL); + iowrite32(0x0, drv_data->regs + ADI_SPI_DLY); + iowrite32(SPI_IMSK_SET_ROM, drv_data->regs + ADI_SPI_IMSK_SET); + + ctlr->dma_tx = dma_request_chan(dev, "tx"); + if (IS_ERR(ctlr->dma_tx)) { + dev_warn(dev, "DMA TX channel not available, SPI unable to use DMA\n"); + ctlr->dma_tx = NULL; + } else { + ctlr->dma_rx = dma_request_chan(dev, "rx"); + if (IS_ERR(ctlr->dma_rx)) { + dev_warn(dev, "DMA RX channel not available, SPI unable to use DMA\n"); + dma_release_channel(ctlr->dma_tx); + ctlr->dma_tx = NULL; + ctlr->dma_rx = NULL; + } + } + + if (ctlr->dma_rx && ctlr->dma_tx) + ctlr->can_dma = adi_spi_can_dma; + + ret = clk_prepare_enable(drv_data->sclk); + if (ret) { + dev_err(dev, "Could not enable SPI clock\n"); + goto err_free_dma; + } + + ret = devm_spi_register_controller(dev, ctlr); + if (ret) { + dev_err(dev, "cannot register spi controller\n"); + goto err_free_dma; + } + + dev_info(dev, "registered ADI SPI controller %s\n", + dev_name(&ctlr->dev)); + return ret; + +err_free_dma: + if (ctlr->dma_tx) { + dma_release_channel(ctlr->dma_rx); + dma_release_channel(ctlr->dma_tx); + ctlr->dma_tx = NULL; + ctlr->dma_rx = NULL; + } + + return ret; +} + +static void adi_spi_remove(struct platform_device *pdev) +{ + struct spi_controller *ctlr = platform_get_drvdata(pdev); + struct adi_spi_controller *drv_data = spi_controller_get_devdata(ctlr); + + adi_spi_disable(drv_data); + clk_disable_unprepare(drv_data->sclk); + + if (ctlr->dma_tx) + dma_release_channel(ctlr->dma_tx); + if (ctlr->dma_rx) + dma_release_channel(ctlr->dma_rx); +} + +static int __maybe_unused adi_spi_suspend(struct device *dev) +{ + struct spi_controller *ctlr = dev_get_drvdata(dev); + + return spi_controller_suspend(ctlr); +} + +static int __maybe_unused adi_spi_resume(struct device *dev) +{ + struct spi_controller *ctlr = dev_get_drvdata(dev); + + return spi_controller_resume(ctlr); +} + +static const struct dev_pm_ops adi_spi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(adi_spi_suspend, adi_spi_resume) +}; + +MODULE_ALIAS("platform:adi-spi3"); +static struct platform_driver adi_spi_driver = { + .driver = { + .name = "adi-spi3", + .pm = &adi_spi_pm_ops, + .of_match_table = adi_spi_of_match, + }, + .probe = adi_spi_probe, + .remove = adi_spi_remove, +}; + +module_platform_driver(adi_spi_driver); + +MODULE_DESCRIPTION("Analog Devices SPI3 controller driver"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 653f82984216c3..0815182ffdcf06 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -699,6 +699,7 @@ static const struct class spidev_class = { }; static const struct spi_device_id spidev_spi_ids[] = { + { .name = "adrv906x-rffe-header" }, { .name = "bh2228fv" }, { .name = "dh2228fv" }, { .name = "jg10309-01" }, @@ -711,6 +712,7 @@ static const struct spi_device_id spidev_spi_ids[] = { { .name = "spi-authenta" }, { .name = "em3581" }, { .name = "si3210" }, + { .name = "zl30732" }, {}, }; MODULE_DEVICE_TABLE(spi, spidev_spi_ids); @@ -729,12 +731,14 @@ static int spidev_of_check(struct device *dev) } static const struct of_device_id spidev_dt_ids[] = { + { .compatible = "adi,adrv906x-rffe-header", .data = &spidev_of_check }, { .compatible = "cisco,spi-petra", .data = &spidev_of_check }, { .compatible = "dh,dhcom-board", .data = &spidev_of_check }, { .compatible = "elgin,jg10309-01", .data = &spidev_of_check }, { .compatible = "lineartechnology,ltc2488", .data = &spidev_of_check }, { .compatible = "lwn,bk4", .data = &spidev_of_check }, { .compatible = "menlo,m53cpld", .data = &spidev_of_check }, + { .compatible = "microchip,zl30732", .data = &spidev_of_check }, { .compatible = "micron,spi-authenta", .data = &spidev_of_check }, { .compatible = "rohm,bh2228fv", .data = &spidev_of_check }, { .compatible = "rohm,dh2228fv", .data = &spidev_of_check }, diff --git a/include/dt-bindings/pinctrl/pinctrl-adi-adrv906x-io-pad.h b/include/dt-bindings/pinctrl/pinctrl-adi-adrv906x-io-pad.h new file mode 100644 index 00000000000000..003cf29c5297d3 --- /dev/null +++ b/include/dt-bindings/pinctrl/pinctrl-adi-adrv906x-io-pad.h @@ -0,0 +1,657 @@ +/* + * Copyright (c) 2025, Analog Devices Incorporated, All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _ADI_ADRV906X_PADS_H +#define _ADI_ADRV906X_PADS_H + +#define ADI_ADRV906X_PIN_COUNT (103U) +#define ADI_ADRV906X_PINMUX_SRC_PER_PIN (6U) +#define ADI_ADRV906X_PINMUX_NUM_SRCS (447U) + +/* Dedicated IO Parameters*/ +#define ADI_ADRV906X_DIO_PIN_COUNT (23U) /* Total number of dedicated IO */ +#define ADI_ADRV906X_DIO_PIN_START (115U) /* Pin# of first Dedicated IO Pin */ + +/* ADI ADRV906X PIN NUMBERS */ +#define ADI_ADRV906X_PIN_0 (0U) +#define ADI_ADRV906X_PIN_1 (1U) +#define ADI_ADRV906X_PIN_2 (2U) +#define ADI_ADRV906X_PIN_3 (3U) +#define ADI_ADRV906X_PIN_4 (4U) +#define ADI_ADRV906X_PIN_5 (5U) +#define ADI_ADRV906X_PIN_6 (6U) +#define ADI_ADRV906X_PIN_7 (7U) +#define ADI_ADRV906X_PIN_8 (8U) +#define ADI_ADRV906X_PIN_9 (9U) +#define ADI_ADRV906X_PIN_10 (10U) +#define ADI_ADRV906X_PIN_11 (11U) +#define ADI_ADRV906X_PIN_12 (12U) +#define ADI_ADRV906X_PIN_13 (13U) +#define ADI_ADRV906X_PIN_14 (14U) +#define ADI_ADRV906X_PIN_15 (15U) +#define ADI_ADRV906X_PIN_16 (16U) +#define ADI_ADRV906X_PIN_17 (17U) +#define ADI_ADRV906X_PIN_18 (18U) +#define ADI_ADRV906X_PIN_19 (19U) +#define ADI_ADRV906X_PIN_20 (20U) +#define ADI_ADRV906X_PIN_21 (21U) +#define ADI_ADRV906X_PIN_22 (22U) +#define ADI_ADRV906X_PIN_23 (23U) +#define ADI_ADRV906X_PIN_24 (24U) +#define ADI_ADRV906X_PIN_25 (25U) +#define ADI_ADRV906X_PIN_26 (26U) +#define ADI_ADRV906X_PIN_27 (27U) +#define ADI_ADRV906X_PIN_28 (28U) +#define ADI_ADRV906X_PIN_29 (29U) +#define ADI_ADRV906X_PIN_30 (30U) +#define ADI_ADRV906X_PIN_31 (31U) +#define ADI_ADRV906X_PIN_32 (32U) +#define ADI_ADRV906X_PIN_33 (33U) +#define ADI_ADRV906X_PIN_34 (34U) +#define ADI_ADRV906X_PIN_35 (35U) +#define ADI_ADRV906X_PIN_36 (36U) +#define ADI_ADRV906X_PIN_37 (37U) +#define ADI_ADRV906X_PIN_38 (38U) +#define ADI_ADRV906X_PIN_39 (39U) +#define ADI_ADRV906X_PIN_40 (40U) +#define ADI_ADRV906X_PIN_41 (41U) +#define ADI_ADRV906X_PIN_42 (42U) +#define ADI_ADRV906X_PIN_43 (43U) +#define ADI_ADRV906X_PIN_44 (44U) +#define ADI_ADRV906X_PIN_45 (45U) +#define ADI_ADRV906X_PIN_46 (46U) +#define ADI_ADRV906X_PIN_47 (47U) +#define ADI_ADRV906X_PIN_48 (48U) +#define ADI_ADRV906X_PIN_49 (49U) +#define ADI_ADRV906X_PIN_50 (50U) +#define ADI_ADRV906X_PIN_51 (51U) +#define ADI_ADRV906X_PIN_52 (52U) +#define ADI_ADRV906X_PIN_53 (53U) +#define ADI_ADRV906X_PIN_54 (54U) +#define ADI_ADRV906X_PIN_55 (55U) +#define ADI_ADRV906X_PIN_56 (56U) +#define ADI_ADRV906X_PIN_57 (57U) +#define ADI_ADRV906X_PIN_58 (58U) +#define ADI_ADRV906X_PIN_59 (59U) +#define ADI_ADRV906X_PIN_60 (60U) +#define ADI_ADRV906X_PIN_61 (61U) +#define ADI_ADRV906X_PIN_62 (62U) +#define ADI_ADRV906X_PIN_63 (63U) +#define ADI_ADRV906X_PIN_64 (64U) +#define ADI_ADRV906X_PIN_65 (65U) +#define ADI_ADRV906X_PIN_66 (66U) +#define ADI_ADRV906X_PIN_67 (67U) +#define ADI_ADRV906X_PIN_68 (68U) +#define ADI_ADRV906X_PIN_69 (69U) +#define ADI_ADRV906X_PIN_70 (70U) +#define ADI_ADRV906X_PIN_71 (71U) +#define ADI_ADRV906X_PIN_72 (72U) +#define ADI_ADRV906X_PIN_73 (73U) +#define ADI_ADRV906X_PIN_74 (74U) +#define ADI_ADRV906X_PIN_75 (75U) +#define ADI_ADRV906X_PIN_76 (76U) +#define ADI_ADRV906X_PIN_77 (77U) +#define ADI_ADRV906X_PIN_78 (78U) +#define ADI_ADRV906X_PIN_79 (79U) +#define ADI_ADRV906X_PIN_80 (80U) +#define ADI_ADRV906X_PIN_81 (81U) +#define ADI_ADRV906X_PIN_82 (82U) +#define ADI_ADRV906X_PIN_83 (83U) +#define ADI_ADRV906X_PIN_84 (84U) +#define ADI_ADRV906X_PIN_85 (85U) +#define ADI_ADRV906X_PIN_86 (86U) +#define ADI_ADRV906X_PIN_87 (87U) +#define ADI_ADRV906X_PIN_88 (88U) +#define ADI_ADRV906X_PIN_89 (89U) +#define ADI_ADRV906X_PIN_90 (90U) +#define ADI_ADRV906X_PIN_91 (91U) +#define ADI_ADRV906X_PIN_92 (92U) +#define ADI_ADRV906X_PIN_93 (93U) +#define ADI_ADRV906X_PIN_94 (94U) +#define ADI_ADRV906X_PIN_95 (95U) +#define ADI_ADRV906X_PIN_96 (96U) +#define ADI_ADRV906X_PIN_97 (97U) +#define ADI_ADRV906X_PIN_98 (98U) +#define ADI_ADRV906X_PIN_99 (99U) +#define ADI_ADRV906X_PIN_100 (100U) +#define ADI_ADRV906X_PIN_101 (101U) +#define ADI_ADRV906X_PIN_102 (102U) + +#define ADI_ADRV906X_PIN_115 (115U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_116 (116U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_117 (117U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_118 (118U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_119 (119U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_120 (120U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_121 (121U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_122 (122U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_123 (123U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_124 (124U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_125 (125U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_126 (126U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_127 (127U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_128 (128U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_129 (129U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_130 (130U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_131 (131U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_132 (132U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_133 (133U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_134 (134U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_135 (135U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_136 (136U) /* Dedicated IO */ +#define ADI_ADRV906X_PIN_137 (137U) /* Dedicated IO */ + +/* ADI ADRV906X SOURCE MUX SEL */ +#define ADI_PINMUX_SRC_SEL_0 (0U) +#define ADI_PINMUX_SRC_SEL_1 (1U) +#define ADI_PINMUX_SRC_SEL_2 (2U) +#define ADI_PINMUX_SRC_SEL_3 (3U) +#define ADI_PINMUX_SRC_SEL_4 (4U) +#define ADI_PINMUX_SRC_SEL_5 (5U) +#define ADI_PINMUX_SRC_SEL_NONE (0xffffU) + +/* Pin-ID PIN# PinMuxSource */ +#define A55_GPIO_NS_0_PIN ADI_ADRV906X_PIN_0 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_1_PIN ADI_ADRV906X_PIN_1 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_10_PIN ADI_ADRV906X_PIN_10 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_100_PIN ADI_ADRV906X_PIN_100 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_101_PIN ADI_ADRV906X_PIN_101 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_102_PIN ADI_ADRV906X_PIN_102 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_11_PIN ADI_ADRV906X_PIN_11 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_12_PIN ADI_ADRV906X_PIN_12 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_13_PIN ADI_ADRV906X_PIN_13 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_14_PIN ADI_ADRV906X_PIN_14 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_15_PIN ADI_ADRV906X_PIN_15 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_16_PIN ADI_ADRV906X_PIN_16 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_17_PIN ADI_ADRV906X_PIN_17 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_18_PIN ADI_ADRV906X_PIN_18 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_19_PIN ADI_ADRV906X_PIN_19 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_2_PIN ADI_ADRV906X_PIN_2 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_20_PIN ADI_ADRV906X_PIN_20 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_21_PIN ADI_ADRV906X_PIN_21 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_22_PIN ADI_ADRV906X_PIN_22 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_23_PIN ADI_ADRV906X_PIN_23 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_24_PIN ADI_ADRV906X_PIN_24 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_25_PIN ADI_ADRV906X_PIN_25 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_26_PIN ADI_ADRV906X_PIN_26 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_27_PIN ADI_ADRV906X_PIN_27 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_28_PIN ADI_ADRV906X_PIN_28 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_29_PIN ADI_ADRV906X_PIN_29 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_3_PIN ADI_ADRV906X_PIN_3 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_30_PIN ADI_ADRV906X_PIN_30 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_31_PIN ADI_ADRV906X_PIN_31 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_32_PIN ADI_ADRV906X_PIN_32 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_33_PIN ADI_ADRV906X_PIN_33 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_34_PIN ADI_ADRV906X_PIN_34 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_35_PIN ADI_ADRV906X_PIN_35 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_36_PIN ADI_ADRV906X_PIN_36 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_37_PIN ADI_ADRV906X_PIN_37 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_38_PIN ADI_ADRV906X_PIN_38 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_39_PIN ADI_ADRV906X_PIN_39 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_4_PIN ADI_ADRV906X_PIN_4 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_40_PIN ADI_ADRV906X_PIN_40 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_41_PIN ADI_ADRV906X_PIN_41 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_42_PIN ADI_ADRV906X_PIN_42 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_43_PIN ADI_ADRV906X_PIN_43 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_44_PIN ADI_ADRV906X_PIN_44 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_45_PIN ADI_ADRV906X_PIN_45 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_46_PIN ADI_ADRV906X_PIN_46 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_47_PIN ADI_ADRV906X_PIN_47 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_48_PIN ADI_ADRV906X_PIN_48 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_49_PIN ADI_ADRV906X_PIN_49 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_5_PIN ADI_ADRV906X_PIN_5 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_50_PIN ADI_ADRV906X_PIN_50 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_51_PIN ADI_ADRV906X_PIN_51 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_52_PIN ADI_ADRV906X_PIN_52 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_53_PIN ADI_ADRV906X_PIN_53 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_54_PIN ADI_ADRV906X_PIN_54 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_55_PIN ADI_ADRV906X_PIN_55 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_56_PIN ADI_ADRV906X_PIN_56 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_57_PIN ADI_ADRV906X_PIN_57 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_58_PIN ADI_ADRV906X_PIN_58 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_59_PIN ADI_ADRV906X_PIN_59 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_6_PIN ADI_ADRV906X_PIN_6 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_60_PIN ADI_ADRV906X_PIN_60 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_61_PIN ADI_ADRV906X_PIN_61 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_62_PIN ADI_ADRV906X_PIN_62 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_63_PIN ADI_ADRV906X_PIN_63 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_64_PIN ADI_ADRV906X_PIN_64 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_65_PIN ADI_ADRV906X_PIN_65 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_66_PIN ADI_ADRV906X_PIN_66 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_67_PIN ADI_ADRV906X_PIN_67 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_68_PIN ADI_ADRV906X_PIN_68 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_69_PIN ADI_ADRV906X_PIN_69 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_7_PIN ADI_ADRV906X_PIN_7 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_70_PIN ADI_ADRV906X_PIN_70 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_71_PIN ADI_ADRV906X_PIN_71 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_72_PIN ADI_ADRV906X_PIN_72 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_73_PIN ADI_ADRV906X_PIN_73 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_74_PIN ADI_ADRV906X_PIN_74 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_75_PIN ADI_ADRV906X_PIN_75 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_76_PIN ADI_ADRV906X_PIN_76 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_77_PIN ADI_ADRV906X_PIN_77 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_78_PIN ADI_ADRV906X_PIN_78 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_79_PIN ADI_ADRV906X_PIN_79 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_8_PIN ADI_ADRV906X_PIN_8 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_80_PIN ADI_ADRV906X_PIN_80 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_81_PIN ADI_ADRV906X_PIN_81 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_82_PIN ADI_ADRV906X_PIN_82 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_83_PIN ADI_ADRV906X_PIN_83 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_84_PIN ADI_ADRV906X_PIN_84 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_85_PIN ADI_ADRV906X_PIN_85 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_86_PIN ADI_ADRV906X_PIN_86 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_87_PIN ADI_ADRV906X_PIN_87 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_88_PIN ADI_ADRV906X_PIN_88 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_89_PIN ADI_ADRV906X_PIN_89 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_9_PIN ADI_ADRV906X_PIN_9 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_90_PIN ADI_ADRV906X_PIN_90 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_91_PIN ADI_ADRV906X_PIN_91 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_92_PIN ADI_ADRV906X_PIN_92 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_93_PIN ADI_ADRV906X_PIN_93 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_94_PIN ADI_ADRV906X_PIN_94 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_95_PIN ADI_ADRV906X_PIN_95 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_96_PIN ADI_ADRV906X_PIN_96 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_97_PIN ADI_ADRV906X_PIN_97 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_98_PIN ADI_ADRV906X_PIN_98 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_NS_99_PIN ADI_ADRV906X_PIN_99 ADI_PINMUX_SRC_SEL_4 +#define A55_GPIO_S_0_PIN ADI_ADRV906X_PIN_0 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_1_PIN ADI_ADRV906X_PIN_1 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_10_PIN ADI_ADRV906X_PIN_10 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_100_PIN ADI_ADRV906X_PIN_100 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_101_PIN ADI_ADRV906X_PIN_101 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_102_PIN ADI_ADRV906X_PIN_102 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_11_PIN ADI_ADRV906X_PIN_11 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_12_PIN ADI_ADRV906X_PIN_12 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_13_PIN ADI_ADRV906X_PIN_13 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_14_PIN ADI_ADRV906X_PIN_14 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_15_PIN ADI_ADRV906X_PIN_15 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_16_PIN ADI_ADRV906X_PIN_16 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_17_PIN ADI_ADRV906X_PIN_17 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_18_PIN ADI_ADRV906X_PIN_18 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_19_PIN ADI_ADRV906X_PIN_19 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_2_PIN ADI_ADRV906X_PIN_2 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_20_PIN ADI_ADRV906X_PIN_20 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_21_PIN ADI_ADRV906X_PIN_21 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_22_PIN ADI_ADRV906X_PIN_22 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_23_PIN ADI_ADRV906X_PIN_23 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_24_PIN ADI_ADRV906X_PIN_24 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_25_PIN ADI_ADRV906X_PIN_25 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_26_PIN ADI_ADRV906X_PIN_26 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_27_PIN ADI_ADRV906X_PIN_27 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_28_PIN ADI_ADRV906X_PIN_28 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_29_PIN ADI_ADRV906X_PIN_29 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_3_PIN ADI_ADRV906X_PIN_3 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_30_PIN ADI_ADRV906X_PIN_30 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_31_PIN ADI_ADRV906X_PIN_31 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_32_PIN ADI_ADRV906X_PIN_32 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_33_PIN ADI_ADRV906X_PIN_33 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_34_PIN ADI_ADRV906X_PIN_34 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_35_PIN ADI_ADRV906X_PIN_35 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_36_PIN ADI_ADRV906X_PIN_36 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_37_PIN ADI_ADRV906X_PIN_37 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_38_PIN ADI_ADRV906X_PIN_38 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_39_PIN ADI_ADRV906X_PIN_39 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_4_PIN ADI_ADRV906X_PIN_4 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_40_PIN ADI_ADRV906X_PIN_40 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_41_PIN ADI_ADRV906X_PIN_41 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_42_PIN ADI_ADRV906X_PIN_42 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_43_PIN ADI_ADRV906X_PIN_43 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_44_PIN ADI_ADRV906X_PIN_44 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_45_PIN ADI_ADRV906X_PIN_45 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_46_PIN ADI_ADRV906X_PIN_46 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_47_PIN ADI_ADRV906X_PIN_47 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_48_PIN ADI_ADRV906X_PIN_48 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_49_PIN ADI_ADRV906X_PIN_49 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_5_PIN ADI_ADRV906X_PIN_5 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_50_PIN ADI_ADRV906X_PIN_50 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_51_PIN ADI_ADRV906X_PIN_51 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_52_PIN ADI_ADRV906X_PIN_52 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_53_PIN ADI_ADRV906X_PIN_53 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_54_PIN ADI_ADRV906X_PIN_54 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_55_PIN ADI_ADRV906X_PIN_55 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_56_PIN ADI_ADRV906X_PIN_56 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_57_PIN ADI_ADRV906X_PIN_57 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_58_PIN ADI_ADRV906X_PIN_58 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_59_PIN ADI_ADRV906X_PIN_59 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_6_PIN ADI_ADRV906X_PIN_6 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_60_PIN ADI_ADRV906X_PIN_60 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_61_PIN ADI_ADRV906X_PIN_61 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_62_PIN ADI_ADRV906X_PIN_62 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_63_PIN ADI_ADRV906X_PIN_63 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_64_PIN ADI_ADRV906X_PIN_64 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_65_PIN ADI_ADRV906X_PIN_65 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_66_PIN ADI_ADRV906X_PIN_66 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_67_PIN ADI_ADRV906X_PIN_67 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_68_PIN ADI_ADRV906X_PIN_68 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_69_PIN ADI_ADRV906X_PIN_69 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_7_PIN ADI_ADRV906X_PIN_7 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_70_PIN ADI_ADRV906X_PIN_70 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_71_PIN ADI_ADRV906X_PIN_71 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_72_PIN ADI_ADRV906X_PIN_72 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_73_PIN ADI_ADRV906X_PIN_73 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_74_PIN ADI_ADRV906X_PIN_74 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_75_PIN ADI_ADRV906X_PIN_75 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_76_PIN ADI_ADRV906X_PIN_76 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_77_PIN ADI_ADRV906X_PIN_77 ADI_PINMUX_SRC_SEL_0 +#define A55_GPIO_S_78_PIN ADI_ADRV906X_PIN_78 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_79_PIN ADI_ADRV906X_PIN_79 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_8_PIN ADI_ADRV906X_PIN_8 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_80_PIN ADI_ADRV906X_PIN_80 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_81_PIN ADI_ADRV906X_PIN_81 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_82_PIN ADI_ADRV906X_PIN_82 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_83_PIN ADI_ADRV906X_PIN_83 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_84_PIN ADI_ADRV906X_PIN_84 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_85_PIN ADI_ADRV906X_PIN_85 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_86_PIN ADI_ADRV906X_PIN_86 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_87_PIN ADI_ADRV906X_PIN_87 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_88_PIN ADI_ADRV906X_PIN_88 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_89_PIN ADI_ADRV906X_PIN_89 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_9_PIN ADI_ADRV906X_PIN_9 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_90_PIN ADI_ADRV906X_PIN_90 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_91_PIN ADI_ADRV906X_PIN_91 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_92_PIN ADI_ADRV906X_PIN_92 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_93_PIN ADI_ADRV906X_PIN_93 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_94_PIN ADI_ADRV906X_PIN_94 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_95_PIN ADI_ADRV906X_PIN_95 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_96_PIN ADI_ADRV906X_PIN_96 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_97_PIN ADI_ADRV906X_PIN_97 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_98_PIN ADI_ADRV906X_PIN_98 ADI_PINMUX_SRC_SEL_3 +#define A55_GPIO_S_99_PIN ADI_ADRV906X_PIN_99 ADI_PINMUX_SRC_SEL_3 +#define DEBUG_CLK_INPUT_PIN ADI_ADRV906X_PIN_33 ADI_PINMUX_SRC_SEL_3 +#define EMAC_CLK_RX_PIN ADI_ADRV906X_PIN_96 ADI_PINMUX_SRC_SEL_0 +#define EMAC_CLK_TX_PIN ADI_ADRV906X_PIN_90 ADI_PINMUX_SRC_SEL_0 +#define EMAC_GMII_MDC_PIN ADI_ADRV906X_PIN_86 ADI_PINMUX_SRC_SEL_0 +#define EMAC_GMII_MDIO_PIN ADI_ADRV906X_PIN_85 ADI_PINMUX_SRC_SEL_0 +#define EMAC_PHY_INTR_PIN ADI_ADRV906X_PIN_87 ADI_PINMUX_SRC_SEL_0 +#define EMAC_PHY_RXD_0_PIN ADI_ADRV906X_PIN_100 ADI_PINMUX_SRC_SEL_0 +#define EMAC_PHY_RXD_1_PIN ADI_ADRV906X_PIN_99 ADI_PINMUX_SRC_SEL_0 +#define EMAC_PHY_RXD_2_PIN ADI_ADRV906X_PIN_98 ADI_PINMUX_SRC_SEL_0 +#define EMAC_PHY_RXD_3_PIN ADI_ADRV906X_PIN_97 ADI_PINMUX_SRC_SEL_0 +#define EMAC_PHY_RX_DV_PIN ADI_ADRV906X_PIN_89 ADI_PINMUX_SRC_SEL_0 +#define EMAC_PHY_TXD_0_PIN ADI_ADRV906X_PIN_95 ADI_PINMUX_SRC_SEL_0 +#define EMAC_PHY_TXD_1_PIN ADI_ADRV906X_PIN_94 ADI_PINMUX_SRC_SEL_0 +#define EMAC_PHY_TXD_2_PIN ADI_ADRV906X_PIN_93 ADI_PINMUX_SRC_SEL_0 +#define EMAC_PHY_TXD_3_PIN ADI_ADRV906X_PIN_92 ADI_PINMUX_SRC_SEL_0 +#define EMAC_PHY_TXEN_PIN ADI_ADRV906X_PIN_91 ADI_PINMUX_SRC_SEL_0 +#define EMAC_RESETN_PIN ADI_ADRV906X_PIN_88 ADI_PINMUX_SRC_SEL_0 +#define GNSS_INTERRUPT_PIN ADI_ADRV906X_PIN_54 ADI_PINMUX_SRC_SEL_1 +#define GPINT_INTERRUPT_INPUT_SECONDARY_TO_PRIMARY_PIN ADI_ADRV906X_PIN_101 ADI_PINMUX_SRC_SEL_0 +#define GPINT_OUTPUT_0_PIN ADI_ADRV906X_PIN_32 ADI_PINMUX_SRC_SEL_3 +#define GPINT_OUTPUT_1_PIN ADI_ADRV906X_PIN_40 ADI_PINMUX_SRC_SEL_3 +#define GPIO_DEBUG_0_PIN ADI_ADRV906X_PIN_66 ADI_PINMUX_SRC_SEL_0 +#define GPIO_DEBUG_1_PIN ADI_ADRV906X_PIN_67 ADI_PINMUX_SRC_SEL_0 +#define GPIO_DEBUG_2_PIN ADI_ADRV906X_PIN_68 ADI_PINMUX_SRC_SEL_0 +#define GPIO_DEBUG_3_PIN ADI_ADRV906X_PIN_69 ADI_PINMUX_SRC_SEL_0 +#define GPIO_DEBUG_4_PIN ADI_ADRV906X_PIN_70 ADI_PINMUX_SRC_SEL_0 +#define GPIO_DEBUG_5_PIN ADI_ADRV906X_PIN_71 ADI_PINMUX_SRC_SEL_0 +#define GPIO_DEBUG_6_PIN ADI_ADRV906X_PIN_72 ADI_PINMUX_SRC_SEL_0 +#define GPIO_DEBUG_7_PIN ADI_ADRV906X_PIN_73 ADI_PINMUX_SRC_SEL_0 +#define I2C1_SCL_PIN ADI_ADRV906X_PIN_20 ADI_PINMUX_SRC_SEL_0 +#define I2C1_SDA_PIN ADI_ADRV906X_PIN_21 ADI_PINMUX_SRC_SEL_0 +#define I2C2_SCL_PIN ADI_ADRV906X_PIN_35 ADI_PINMUX_SRC_SEL_2 +#define I2C2_SDA_PIN ADI_ADRV906X_PIN_36 ADI_PINMUX_SRC_SEL_2 +#define I2C3_SCL_PIN ADI_ADRV906X_PIN_4 ADI_PINMUX_SRC_SEL_1 +#define I2C3_SDA_PIN ADI_ADRV906X_PIN_5 ADI_PINMUX_SRC_SEL_1 +#define I2C4_SCL_PIN ADI_ADRV906X_PIN_70 ADI_PINMUX_SRC_SEL_1 +#define I2C4_SDA_PIN ADI_ADRV906X_PIN_71 ADI_PINMUX_SRC_SEL_1 +#define I2C5_SCL_PIN ADI_ADRV906X_PIN_72 ADI_PINMUX_SRC_SEL_1 +#define I2C5_SDA_PIN ADI_ADRV906X_PIN_73 ADI_PINMUX_SRC_SEL_1 +#define I2C6_SCL_PIN ADI_ADRV906X_PIN_74 ADI_PINMUX_SRC_SEL_1 +#define I2C6_SDA_PIN ADI_ADRV906X_PIN_75 ADI_PINMUX_SRC_SEL_1 +#define I2C7_SCL_PIN ADI_ADRV906X_PIN_38 ADI_PINMUX_SRC_SEL_1 +#define I2C7_SDA_PIN ADI_ADRV906X_PIN_39 ADI_PINMUX_SRC_SEL_1 +#define JTAG_TCK_A55_PIN ADI_ADRV906X_PIN_63 ADI_PINMUX_SRC_SEL_3 +#define JTAG_TCK_M4_PIN ADI_ADRV906X_PIN_37 ADI_PINMUX_SRC_SEL_3 +#define JTAG_TDI_A55_PIN ADI_ADRV906X_PIN_61 ADI_PINMUX_SRC_SEL_3 +#define JTAG_TDI_M4_PIN ADI_ADRV906X_PIN_35 ADI_PINMUX_SRC_SEL_3 +#define JTAG_TDO_A55_PIN ADI_ADRV906X_PIN_60 ADI_PINMUX_SRC_SEL_3 +#define JTAG_TDO_M4_PIN ADI_ADRV906X_PIN_34 ADI_PINMUX_SRC_SEL_3 +#define JTAG_TMS_A55_PIN ADI_ADRV906X_PIN_62 ADI_PINMUX_SRC_SEL_3 +#define JTAG_TMS_M4_PIN ADI_ADRV906X_PIN_36 ADI_PINMUX_SRC_SEL_3 +#define JTAG_TRSTB_A55_PIN ADI_ADRV906X_PIN_64 ADI_PINMUX_SRC_SEL_3 +#define JTAG_TRSTB_M4_PIN ADI_ADRV906X_PIN_38 ADI_PINMUX_SRC_SEL_3 +#define LED_STATUS_0_PIN ADI_ADRV906X_PIN_74 ADI_PINMUX_SRC_SEL_5 +#define LED_STATUS_1_PIN ADI_ADRV906X_PIN_75 ADI_PINMUX_SRC_SEL_5 +#define LED_STATUS_2_PIN ADI_ADRV906X_PIN_76 ADI_PINMUX_SRC_SEL_5 +#define LED_STATUS_3_PIN ADI_ADRV906X_PIN_77 ADI_PINMUX_SRC_SEL_5 + +#define ONE_PPS_CLK_OUTPUT_SE_PIN ADI_ADRV906X_PIN_45 ADI_PINMUX_SRC_SEL_1 +#define OTP_CLK_OUTPUT_PIN ADI_ADRV906X_PIN_39 ADI_PINMUX_SRC_SEL_3 +#define POWERDOWN_PIN ADI_ADRV906X_PIN_51 ADI_PINMUX_SRC_SEL_5 +#define POWERREBOOTREGULATOR_ENABLE_PULLDOWN_PIN ADI_ADRV906X_PIN_102 ADI_PINMUX_SRC_SEL_0 +#define PPI16_CLK_PIN ADI_ADRV906X_PIN_31 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PDI_0_PIN ADI_ADRV906X_PIN_6 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PDI_1_PIN ADI_ADRV906X_PIN_7 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PDI_10_PIN ADI_ADRV906X_PIN_45 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PDI_11_PIN ADI_ADRV906X_PIN_18 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PDI_12_PIN ADI_ADRV906X_PIN_19 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PDI_13_PIN ADI_ADRV906X_PIN_20 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PDI_14_PIN ADI_ADRV906X_PIN_21 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PDI_15_PIN ADI_ADRV906X_PIN_30 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PDI_2_PIN ADI_ADRV906X_PIN_8 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PDI_3_PIN ADI_ADRV906X_PIN_74 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PDI_4_PIN ADI_ADRV906X_PIN_12 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PDI_5_PIN ADI_ADRV906X_PIN_13 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PDI_6_PIN ADI_ADRV906X_PIN_14 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PDI_7_PIN ADI_ADRV906X_PIN_15 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PDI_8_PIN ADI_ADRV906X_PIN_16 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PDI_9_PIN ADI_ADRV906X_PIN_17 ADI_PINMUX_SRC_SEL_2 +#define PPI16_PEB_PIN ADI_ADRV906X_PIN_34 ADI_PINMUX_SRC_SEL_2 +#define PWM_0_PIN ADI_ADRV906X_PIN_6 ADI_PINMUX_SRC_SEL_1 +#define PWM_1_PIN ADI_ADRV906X_PIN_0 ADI_PINMUX_SRC_SEL_5 +#define PWM_10_PIN ADI_ADRV906X_PIN_40 ADI_PINMUX_SRC_SEL_2 +#define PWM_11_PIN ADI_ADRV906X_PIN_41 ADI_PINMUX_SRC_SEL_2 +#define PWM_12_PIN ADI_ADRV906X_PIN_46 ADI_PINMUX_SRC_SEL_1 +#define PWM_13_PIN ADI_ADRV906X_PIN_47 ADI_PINMUX_SRC_SEL_1 +#define PWM_14_PIN ADI_ADRV906X_PIN_48 ADI_PINMUX_SRC_SEL_1 +#define PWM_15_PIN ADI_ADRV906X_PIN_49 ADI_PINMUX_SRC_SEL_1 +#define PWM_2_PIN ADI_ADRV906X_PIN_1 ADI_PINMUX_SRC_SEL_5 +#define PWM_3_PIN ADI_ADRV906X_PIN_2 ADI_PINMUX_SRC_SEL_5 +#define PWM_4_PIN ADI_ADRV906X_PIN_3 ADI_PINMUX_SRC_SEL_5 +#define PWM_5_PIN ADI_ADRV906X_PIN_4 ADI_PINMUX_SRC_SEL_5 +#define PWM_6_PIN ADI_ADRV906X_PIN_5 ADI_PINMUX_SRC_SEL_5 +#define PWM_7_PIN ADI_ADRV906X_PIN_37 ADI_PINMUX_SRC_SEL_2 +#define PWM_8_PIN ADI_ADRV906X_PIN_38 ADI_PINMUX_SRC_SEL_2 +#define PWM_9_PIN ADI_ADRV906X_PIN_39 ADI_PINMUX_SRC_SEL_2 +#define QSFP_INTERRUPT_PIN ADI_ADRV906X_PIN_57 ADI_PINMUX_SRC_SEL_0 +#define QSFP_MODPRS_0_PIN ADI_ADRV906X_PIN_59 ADI_PINMUX_SRC_SEL_0 +#define QSFP_MODPRS_1_PIN ADI_ADRV906X_PIN_53 ADI_PINMUX_SRC_SEL_2 +#define QSFP_MODSEL_0_PIN ADI_ADRV906X_PIN_58 ADI_PINMUX_SRC_SEL_0 +#define QSFP_MODSEL_1_PIN ADI_ADRV906X_PIN_52 ADI_PINMUX_SRC_SEL_2 +#define QSFP_RESET_PIN ADI_ADRV906X_PIN_56 ADI_PINMUX_SRC_SEL_0 +#define QSPI_FLASH_CLK_PIN ADI_ADRV906X_PIN_82 ADI_PINMUX_SRC_SEL_0 +#define QSPI_FLASH_D0_PIN ADI_ADRV906X_PIN_78 ADI_PINMUX_SRC_SEL_0 +#define QSPI_FLASH_D1_PIN ADI_ADRV906X_PIN_79 ADI_PINMUX_SRC_SEL_0 +#define QSPI_FLASH_D2_PIN ADI_ADRV906X_PIN_80 ADI_PINMUX_SRC_SEL_0 +#define QSPI_FLASH_D3_PIN ADI_ADRV906X_PIN_81 ADI_PINMUX_SRC_SEL_0 +#define QSPI_FLASH_RESETN_PIN ADI_ADRV906X_PIN_84 ADI_PINMUX_SRC_SEL_0 +#define QSPI_FLASH_SELB_PIN ADI_ADRV906X_PIN_83 ADI_PINMUX_SRC_SEL_0 +#define QSPI_FLOW_READY_PIN ADI_ADRV906X_PIN_76 ADI_PINMUX_SRC_SEL_1 +#define REBOOTB_PIN ADI_ADRV906X_PIN_46 ADI_PINMUX_SRC_SEL_5 +#define RFFE_0_PIN ADI_ADRV906X_PIN_7 ADI_PINMUX_SRC_SEL_0 +#define RFFE_1_PIN ADI_ADRV906X_PIN_8 ADI_PINMUX_SRC_SEL_0 +#define RFFE_10_PIN ADI_ADRV906X_PIN_17 ADI_PINMUX_SRC_SEL_0 +#define RFFE_11_PIN ADI_ADRV906X_PIN_18 ADI_PINMUX_SRC_SEL_0 +#define RFFE_12_PIN ADI_ADRV906X_PIN_19 ADI_PINMUX_SRC_SEL_0 +#define RFFE_13_PIN ADI_ADRV906X_PIN_0 ADI_PINMUX_SRC_SEL_2 +#define RFFE_14_PIN ADI_ADRV906X_PIN_1 ADI_PINMUX_SRC_SEL_2 +#define RFFE_15_PIN ADI_ADRV906X_PIN_2 ADI_PINMUX_SRC_SEL_2 +#define RFFE_16_PIN ADI_ADRV906X_PIN_3 ADI_PINMUX_SRC_SEL_2 +#define RFFE_17_PIN ADI_ADRV906X_PIN_4 ADI_PINMUX_SRC_SEL_2 +#define RFFE_18_PIN ADI_ADRV906X_PIN_5 ADI_PINMUX_SRC_SEL_2 +#define RFFE_19_PIN ADI_ADRV906X_PIN_66 ADI_PINMUX_SRC_SEL_2 +#define RFFE_2_PIN ADI_ADRV906X_PIN_9 ADI_PINMUX_SRC_SEL_0 +#define RFFE_20_PIN ADI_ADRV906X_PIN_67 ADI_PINMUX_SRC_SEL_2 +#define RFFE_21_PIN ADI_ADRV906X_PIN_68 ADI_PINMUX_SRC_SEL_2 +#define RFFE_22_PIN ADI_ADRV906X_PIN_69 ADI_PINMUX_SRC_SEL_2 +#define RFFE_23_PIN ADI_ADRV906X_PIN_70 ADI_PINMUX_SRC_SEL_2 +#define RFFE_24_PIN ADI_ADRV906X_PIN_71 ADI_PINMUX_SRC_SEL_2 +#define RFFE_25_PIN ADI_ADRV906X_PIN_72 ADI_PINMUX_SRC_SEL_2 +#define RFFE_26_PIN ADI_ADRV906X_PIN_73 ADI_PINMUX_SRC_SEL_2 +#define RFFE_27_PIN ADI_ADRV906X_PIN_75 ADI_PINMUX_SRC_SEL_2 +#define RFFE_28_PIN ADI_ADRV906X_PIN_76 ADI_PINMUX_SRC_SEL_2 +#define RFFE_29_PIN ADI_ADRV906X_PIN_77 ADI_PINMUX_SRC_SEL_2 +#define RFFE_3_PIN ADI_ADRV906X_PIN_10 ADI_PINMUX_SRC_SEL_0 +#define RFFE_30_PIN ADI_ADRV906X_PIN_33 ADI_PINMUX_SRC_SEL_5 +#define RFFE_31_PIN ADI_ADRV906X_PIN_34 ADI_PINMUX_SRC_SEL_5 +#define RFFE_32_PIN ADI_ADRV906X_PIN_35 ADI_PINMUX_SRC_SEL_5 +#define RFFE_33_PIN ADI_ADRV906X_PIN_36 ADI_PINMUX_SRC_SEL_5 +#define RFFE_34_PIN ADI_ADRV906X_PIN_37 ADI_PINMUX_SRC_SEL_5 +#define RFFE_35_PIN ADI_ADRV906X_PIN_38 ADI_PINMUX_SRC_SEL_5 +#define RFFE_36_PIN ADI_ADRV906X_PIN_39 ADI_PINMUX_SRC_SEL_5 +#define RFFE_37_PIN ADI_ADRV906X_PIN_40 ADI_PINMUX_SRC_SEL_5 +#define RFFE_38_PIN ADI_ADRV906X_PIN_41 ADI_PINMUX_SRC_SEL_5 +#define RFFE_39_PIN ADI_ADRV906X_PIN_43 ADI_PINMUX_SRC_SEL_5 +#define RFFE_4_PIN ADI_ADRV906X_PIN_11 ADI_PINMUX_SRC_SEL_0 +#define RFFE_40_PIN ADI_ADRV906X_PIN_47 ADI_PINMUX_SRC_SEL_5 +#define RFFE_41_PIN ADI_ADRV906X_PIN_48 ADI_PINMUX_SRC_SEL_5 +#define RFFE_42_PIN ADI_ADRV906X_PIN_49 ADI_PINMUX_SRC_SEL_5 +#define RFFE_43_PIN ADI_ADRV906X_PIN_50 ADI_PINMUX_SRC_SEL_5 +#define RFFE_44_PIN ADI_ADRV906X_PIN_54 ADI_PINMUX_SRC_SEL_5 +#define RFFE_45_PIN ADI_ADRV906X_PIN_55 ADI_PINMUX_SRC_SEL_5 +#define RFFE_46_PIN ADI_ADRV906X_PIN_65 ADI_PINMUX_SRC_SEL_0 +#define RFFE_5_PIN ADI_ADRV906X_PIN_12 ADI_PINMUX_SRC_SEL_0 +#define RFFE_6_PIN ADI_ADRV906X_PIN_13 ADI_PINMUX_SRC_SEL_0 +#define RFFE_7_PIN ADI_ADRV906X_PIN_14 ADI_PINMUX_SRC_SEL_0 +#define RFFE_8_PIN ADI_ADRV906X_PIN_15 ADI_PINMUX_SRC_SEL_0 +#define RFFE_9_PIN ADI_ADRV906X_PIN_16 ADI_PINMUX_SRC_SEL_0 +#define RTC_INT_PIN ADI_ADRV906X_PIN_55 ADI_PINMUX_SRC_SEL_1 +#define SD_CARDDETECT_PIN ADI_ADRV906X_PIN_6 ADI_PINMUX_SRC_SEL_0 +#define SD_CLK_SEL_PIN ADI_ADRV906X_PIN_1 ADI_PINMUX_SRC_SEL_0 +#define SD_CMD_PIN ADI_ADRV906X_PIN_0 ADI_PINMUX_SRC_SEL_0 +#define SD_DATA0_PIN ADI_ADRV906X_PIN_2 ADI_PINMUX_SRC_SEL_0 +#define SD_DATA1_PIN ADI_ADRV906X_PIN_3 ADI_PINMUX_SRC_SEL_0 +#define SD_DATA2_PIN ADI_ADRV906X_PIN_4 ADI_PINMUX_SRC_SEL_0 +#define SD_DATA3_PIN ADI_ADRV906X_PIN_5 ADI_PINMUX_SRC_SEL_0 +#define SFP0_MOD_ABS_PIN ADI_ADRV906X_PIN_59 ADI_PINMUX_SRC_SEL_1 +#define SFP0_RS0_PIN ADI_ADRV906X_PIN_52 ADI_PINMUX_SRC_SEL_5 +#define SFP0_RS1_PIN ADI_ADRV906X_PIN_53 ADI_PINMUX_SRC_SEL_5 +#define SFP0_RX_LOS_PIN ADI_ADRV906X_PIN_58 ADI_PINMUX_SRC_SEL_1 +#define SFP0_TXFAULT_PIN ADI_ADRV906X_PIN_57 ADI_PINMUX_SRC_SEL_1 +#define SFP0_TX_DISABLE_PIN ADI_ADRV906X_PIN_56 ADI_PINMUX_SRC_SEL_1 +#define SFP1_MOD_ABS_PIN ADI_ADRV906X_PIN_73 ADI_PINMUX_SRC_SEL_5 +#define SFP1_RS0_PIN ADI_ADRV906X_PIN_44 ADI_PINMUX_SRC_SEL_5 +#define SFP1_RS1_PIN ADI_ADRV906X_PIN_45 ADI_PINMUX_SRC_SEL_5 +#define SFP1_RX_LOS_PIN ADI_ADRV906X_PIN_72 ADI_PINMUX_SRC_SEL_5 +#define SFP1_TXFAULT_PIN ADI_ADRV906X_PIN_71 ADI_PINMUX_SRC_SEL_5 +#define SFP1_TX_DISABLE_PIN ADI_ADRV906X_PIN_70 ADI_PINMUX_SRC_SEL_5 +#define SPI_MASTER0_SELB_1_PIN ADI_ADRV906X_PIN_42 ADI_PINMUX_SRC_SEL_0 +#define SPI_MASTER0_SELB_2_PIN ADI_ADRV906X_PIN_43 ADI_PINMUX_SRC_SEL_0 +#define SPI_MASTER0_SELB_3_PIN ADI_ADRV906X_PIN_44 ADI_PINMUX_SRC_SEL_0 +#define SPI_MASTER1_CLK_PIN ADI_ADRV906X_PIN_47 ADI_PINMUX_SRC_SEL_0 +#define SPI_MASTER1_MISO_PIN ADI_ADRV906X_PIN_45 ADI_PINMUX_SRC_SEL_0 +#define SPI_MASTER1_MOSI_PIN ADI_ADRV906X_PIN_46 ADI_PINMUX_SRC_SEL_0 +#define SPI_MASTER1_SELB_0_PIN ADI_ADRV906X_PIN_48 ADI_PINMUX_SRC_SEL_0 +#define SPI_MASTER1_SELB_1_PIN ADI_ADRV906X_PIN_49 ADI_PINMUX_SRC_SEL_0 +#define SPI_MASTER1_SELB_2_PIN ADI_ADRV906X_PIN_50 ADI_PINMUX_SRC_SEL_0 +#define SPI_MASTER1_SELB_3_PIN ADI_ADRV906X_PIN_51 ADI_PINMUX_SRC_SEL_0 +#define SPI_MASTER2_CLK_PIN ADI_ADRV906X_PIN_54 ADI_PINMUX_SRC_SEL_0 +#define SPI_MASTER2_MISO_PIN ADI_ADRV906X_PIN_52 ADI_PINMUX_SRC_SEL_0 +#define SPI_MASTER2_MOSI_PIN ADI_ADRV906X_PIN_53 ADI_PINMUX_SRC_SEL_0 +#define SPI_MASTER2_SELB_PIN ADI_ADRV906X_PIN_55 ADI_PINMUX_SRC_SEL_0 +#define SPI_MASTER3_CLK_PIN ADI_ADRV906X_PIN_2 ADI_PINMUX_SRC_SEL_1 +#define SPI_MASTER3_MISO_PIN ADI_ADRV906X_PIN_0 ADI_PINMUX_SRC_SEL_1 +#define SPI_MASTER3_MOSI_PIN ADI_ADRV906X_PIN_1 ADI_PINMUX_SRC_SEL_1 +#define SPI_MASTER3_SELB_PIN ADI_ADRV906X_PIN_3 ADI_PINMUX_SRC_SEL_1 +#define SPI_MASTER4_CLK_PIN ADI_ADRV906X_PIN_36 ADI_PINMUX_SRC_SEL_1 +#define SPI_MASTER4_MISO_PIN ADI_ADRV906X_PIN_34 ADI_PINMUX_SRC_SEL_1 +#define SPI_MASTER4_MOSI_PIN ADI_ADRV906X_PIN_35 ADI_PINMUX_SRC_SEL_1 +#define SPI_MASTER4_SELB_PIN ADI_ADRV906X_PIN_37 ADI_PINMUX_SRC_SEL_1 +#define SPI_MASTER5_CLK_PIN ADI_ADRV906X_PIN_68 ADI_PINMUX_SRC_SEL_1 +#define SPI_MASTER5_MISO_PIN ADI_ADRV906X_PIN_66 ADI_PINMUX_SRC_SEL_1 +#define SPI_MASTER5_MOSI_PIN ADI_ADRV906X_PIN_67 ADI_PINMUX_SRC_SEL_1 +#define SPI_MASTER5_SELB_PIN ADI_ADRV906X_PIN_69 ADI_PINMUX_SRC_SEL_1 +#define SPI_SLAVE_CLK_PIN ADI_ADRV906X_PIN_76 ADI_PINMUX_SRC_SEL_3 +#define SPI_SLAVE_MISO_PIN ADI_ADRV906X_PIN_74 ADI_PINMUX_SRC_SEL_3 +#define SPI_SLAVE_MOSI_PIN ADI_ADRV906X_PIN_75 ADI_PINMUX_SRC_SEL_3 +#define SPI_SLAVE_SELB_PIN ADI_ADRV906X_PIN_77 ADI_PINMUX_SRC_SEL_3 +#define TEMP_SENSOR_INT0_PIN ADI_ADRV906X_PIN_22 ADI_PINMUX_SRC_SEL_0 +#define TEMP_SENSOR_INT1_PIN ADI_ADRV906X_PIN_23 ADI_PINMUX_SRC_SEL_0 +#define TE_UART_RXD_PIN ADI_ADRV906X_PIN_24 ADI_PINMUX_SRC_SEL_1 +#define TE_UART_TXD_PIN ADI_ADRV906X_PIN_26 ADI_PINMUX_SRC_SEL_1 +#define TRACE_CLK_PIN ADI_ADRV906X_PIN_51 ADI_PINMUX_SRC_SEL_2 +#define TRACE_D0_PIN ADI_ADRV906X_PIN_42 ADI_PINMUX_SRC_SEL_2 +#define TRACE_D1_PIN ADI_ADRV906X_PIN_43 ADI_PINMUX_SRC_SEL_2 +#define TRACE_D2_PIN ADI_ADRV906X_PIN_44 ADI_PINMUX_SRC_SEL_2 +#define TRACE_D3_PIN ADI_ADRV906X_PIN_46 ADI_PINMUX_SRC_SEL_2 +#define TRACE_D4_PIN ADI_ADRV906X_PIN_47 ADI_PINMUX_SRC_SEL_2 +#define TRACE_D5_PIN ADI_ADRV906X_PIN_48 ADI_PINMUX_SRC_SEL_2 +#define TRACE_D6_PIN ADI_ADRV906X_PIN_49 ADI_PINMUX_SRC_SEL_2 +#define TRACE_D7_PIN ADI_ADRV906X_PIN_50 ADI_PINMUX_SRC_SEL_2 +#define UART0_CTSIN_PIN ADI_ADRV906X_PIN_27 ADI_PINMUX_SRC_SEL_0 +#define UART0_RTSOUT_PIN ADI_ADRV906X_PIN_25 ADI_PINMUX_SRC_SEL_0 +#define UART0_RXSIN_PIN ADI_ADRV906X_PIN_24 ADI_PINMUX_SRC_SEL_0 +#define UART0_TXSOUT_PIN ADI_ADRV906X_PIN_26 ADI_PINMUX_SRC_SEL_0 +#define UART1_CTSIN_PIN ADI_ADRV906X_PIN_77 ADI_PINMUX_SRC_SEL_1 +#define UART1_RTSOUT_PIN ADI_ADRV906X_PIN_33 ADI_PINMUX_SRC_SEL_1 +#define UART1_RXSIN_PIN ADI_ADRV906X_PIN_28 ADI_PINMUX_SRC_SEL_0 +#define UART1_TXSOUT_PIN ADI_ADRV906X_PIN_29 ADI_PINMUX_SRC_SEL_0 +#define UART2_CTSIN_PIN ADI_ADRV906X_PIN_44 ADI_PINMUX_SRC_SEL_1 +#define UART2_RTSOUT_PIN ADI_ADRV906X_PIN_43 ADI_PINMUX_SRC_SEL_1 +#define UART2_RXSIN_PIN ADI_ADRV906X_PIN_30 ADI_PINMUX_SRC_SEL_0 +#define UART2_TXSOUT_PIN ADI_ADRV906X_PIN_31 ADI_PINMUX_SRC_SEL_0 +#define UART3_CTSIN_PIN ADI_ADRV906X_PIN_53 ADI_PINMUX_SRC_SEL_1 +#define UART3_RTSOUT_PIN ADI_ADRV906X_PIN_52 ADI_PINMUX_SRC_SEL_1 +#define UART3_RXSIN_PIN ADI_ADRV906X_PIN_40 ADI_PINMUX_SRC_SEL_1 +#define UART3_TXSOUT_PIN ADI_ADRV906X_PIN_41 ADI_PINMUX_SRC_SEL_1 +#define UART4_CTSIN_PIN ADI_ADRV906X_PIN_51 ADI_PINMUX_SRC_SEL_1 +#define UART4_RTSOUT_PIN ADI_ADRV906X_PIN_50 ADI_PINMUX_SRC_SEL_1 +#define UART4_RXSIN_PIN ADI_ADRV906X_PIN_30 ADI_PINMUX_SRC_SEL_1 +#define UART4_TXSOUT_PIN ADI_ADRV906X_PIN_31 ADI_PINMUX_SRC_SEL_1 + +/* Pin-ID Dedicated I/O */ +#define BOOT_MODE_0_PIN ADI_ADRV906X_PIN_115 ADI_PINMUX_SRC_SEL_NONE +#define BOOT_MODE_1_PIN ADI_ADRV906X_PIN_116 ADI_PINMUX_SRC_SEL_NONE +#define BOOT_MODE_2_PIN ADI_ADRV906X_PIN_117 ADI_PINMUX_SRC_SEL_NONE +#define BOOT_MODE_3_PIN ADI_ADRV906X_PIN_118 ADI_PINMUX_SRC_SEL_NONE +#define SPI_MASTER0_MISO_PIN ADI_ADRV906X_PIN_119 ADI_PINMUX_SRC_SEL_NONE +#define SPI_MASTER0_MOSI_PIN ADI_ADRV906X_PIN_120 ADI_PINMUX_SRC_SEL_NONE +#define SPI_MASTER0_CLK_PIN ADI_ADRV906X_PIN_121 ADI_PINMUX_SRC_SEL_NONE +#define SPI_MASTER0_SELB_0_PIN ADI_ADRV906X_PIN_122 ADI_PINMUX_SRC_SEL_NONE +#define I2C0_SCL_PIN ADI_ADRV906X_PIN_123 ADI_PINMUX_SRC_SEL_NONE +#define I2C0_SDA_PIN ADI_ADRV906X_PIN_124 ADI_PINMUX_SRC_SEL_NONE +#define TEST_EN_PIN ADI_ADRV906X_PIN_125 ADI_PINMUX_SRC_SEL_NONE +#define TEST_CONTROL_3_PIN ADI_ADRV906X_PIN_126 ADI_PINMUX_SRC_SEL_NONE +#define TEST_CONTROL_2_PIN ADI_ADRV906X_PIN_127 ADI_PINMUX_SRC_SEL_NONE +#define TEST_CONTROL_1_PIN ADI_ADRV906X_PIN_128 ADI_PINMUX_SRC_SEL_NONE +#define TEST_CONTROL_0_PIN ADI_ADRV906X_PIN_129 ADI_PINMUX_SRC_SEL_NONE +#define RESETFORSECONDARYSAMANA_PIN ADI_ADRV906X_PIN_130 ADI_PINMUX_SRC_SEL_NONE +#define CLOCK_STATUS_IN_0_PIN ADI_ADRV906X_PIN_131 ADI_PINMUX_SRC_SEL_NONE +#define CLOCK_STATUS_IN_1_PIN ADI_ADRV906X_PIN_132 ADI_PINMUX_SRC_SEL_NONE +#define DYINGGASPDETECTION_POWERCONTROL_PIN ADI_ADRV906X_PIN_133 ADI_PINMUX_SRC_SEL_NONE +#define ETHERNET_RECOVERED_CLK_PIN ADI_ADRV906X_PIN_134 ADI_PINMUX_SRC_SEL_NONE +#define CLK_RESET_OUT_PIN ADI_ADRV906X_PIN_135 ADI_PINMUX_SRC_SEL_NONE +#define CLK_SEL_PIN ADI_ADRV906X_PIN_136 ADI_PINMUX_SRC_SEL_NONE +#define ROSC_BYPASS_INPUT_PIN ADI_ADRV906X_PIN_137 ADI_PINMUX_SRC_SEL_NONE + +/* + * Configuration Word helper MACROS + */ + +#define ADI_CONFIG_NO_PULL 0x00 +#define ADI_CONFIG_ENABLE_PULLUP 0x60 +#define ADI_CONFIG_ENABLE_PULLDOWN 0x20 +#define ADI_CONFIG_ENABLE_SCHMITT_TRIGGER 0x10 + +/* Configuration Drive Strength Setting */ +#define ADI_CONFIG_DRIVE_STRENGTH_0 0x0 +#define ADI_CONFIG_DRIVE_STRENGTH_1 0x1 +#define ADI_CONFIG_DRIVE_STRENGTH_2 0x2 +#define ADI_CONFIG_DRIVE_STRENGTH_3 0x3 +#define ADI_CONFIG_DRIVE_STRENGTH_4 0x4 +#define ADI_CONFIG_DRIVE_STRENGTH_5 0x5 +#define ADI_CONFIG_DRIVE_STRENGTH_6 0x6 +#define ADI_CONFIG_DRIVE_STRENGTH_7 0x7 +#define ADI_CONFIG_DRIVE_STRENGTH_8 0x8 +#define ADI_CONFIG_DRIVE_STRENGTH_9 0x9 +#define ADI_CONFIG_DRIVE_STRENGTH_10 0xa +#define ADI_CONFIG_DRIVE_STRENGTH_11 0xb +#define ADI_CONFIG_DRIVE_STRENGTH_12 0xc +#define ADI_CONFIG_DRIVE_STRENGTH_13 0xd +#define ADI_CONFIG_DRIVE_STRENGTH_14 0xe +#define ADI_CONFIG_DRIVE_STRENGTH_15 0xf + +#endif /* _ADI_ADRV906X_PADS_H */ diff --git a/include/linux/adi-sdei.h b/include/linux/adi-sdei.h new file mode 100644 index 00000000000000..ef55e113edafe2 --- /dev/null +++ b/include/linux/adi-sdei.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2025, Analog Devices Incorporated, All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0-or-Later + */ + +#ifndef _ADI_SDEI_H +#define _ADI_SDEI_H + +/* SDEI Event Number Defines */ +#define GPINT_DEFAULT_SDEI_EVENT 2000 +#define L4_ECC_WRN_INTR_0_EVENT 2079 +#define L4_ECC_ERR_INTR_0_EVENT 2080 +#define L4_ECC_WRN_INTR_1_EVENT 2081 +#define L4_ECC_ERR_INTR_1_EVENT 2082 +#define L4_ECC_WRN_INTR_2_EVENT 2083 +#define L4_ECC_ERR_INTR_2_EVENT 2084 +#define WATCHDOG_A55_TIMEOUT_PIPED_0_EVENT 2018 +#define WATCHDOG_A55_TIMEOUT_PIPED_1_EVENT 2019 +#define WATCHDOG_A55_TIMEOUT_PIPED_2_EVENT 2020 +#define WATCHDOG_A55_TIMEOUT_PIPED_3_EVENT 2021 +#define XCORR_ECC_ERROR_IRQ_PIPED_EVENT 2032 +#define XCORR_ECC_ERROR_WARNING_PIPED_EVENT 2031 +#define GIC_FAULT_INT_EVENT 2015 +#define GIC_ERR_INT_EVENT 2014 +#define O_DFI_INTERNAL_ERR_INTR_EVENT 2005 +#define O_DFI_PHYUPD_ERR_INTR_EVENT 2006 +#define O_DFI_ALERT_ERR_INTR_EVENT 2008 +#define O_ECC_AP_ERR_INTR_EVENT 2010 +#define O_ECC_AP_ERR_INTR_FAULT_EVENT 2009 +#define O_ECC_UNCORRECTED_ERR_INTR_EVENT 2012 +#define O_ECC_UNCORRECTED_ERR_INTR_FAULT_EVENT 2011 +#define O_DWC_DDRPHY_INT_N_EVENT 2007 +#define NFAULTIRQ_0_EVENT 2017 +#define NFAULTIRQ_1_EVENT 2026 +#define NFAULTIRQ_2_EVENT 2027 +#define NFAULTIRQ_3_EVENT 2028 +#define NFAULTIRQ_4_EVENT 2029 +#define NERRIRQ_0_EVENT 2022 +#define NERRIRQ_1_EVENT 2023 +#define NERRIRQ_2_EVENT 2024 +#define NERRIRQ_3_EVENT 2025 +#define NERRIRQ_4_EVENT 2016 +#define EAST_RFPLL_PLL_LOCKED_SYNC_EVENT 2072 +#define WEST_RFPLL_PLL_LOCKED_SYNC_EVENT 2071 +#define CLKPLL_PLL_LOCKED_SYNC_EVENT 2070 +#define TE_FAULT_GP_INTR_PIPED_EVENT 2078 +#define CLK_PLL_CP_OVER_RANGE_EVENT 2067 +#define ETHPLL_LOCKED_SYNC_EVENT 2066 +#define ARM0_MEMORY_ECC_ERROR_EVENT 2053 +#define ARM1_MEMORY_ECC_ERROR_EVENT 2048 +#define GPINT_INTERRUPT_SECONDARY_TO_PRIMARY_EVENT 2030 +#define NVMB_ERR_FLAG_BOOT_EVENT 2077 +#define NVMB_ERR_FLAG_TIMEOUT_EVENT 2076 +#define C2C_PINT_OUT_EVENT 2013 +#define TX3_NPD_ARM_IRQ_PIPED_8_EVENT 2046 +#define TX2_NPD_ARM_IRQ_PIPED_8_EVENT 2045 +#define TX1_NPD_ARM_IRQ_PIPED_8_EVENT 2044 +#define TX0_NPD_ARM_IRQ_PIPED_8_EVENT 2043 +#define O_STREAM_PROC_ERROR_EVENT 2042 +#define ORX0_ARM_IRQ_PIPED_8_EVENT 2041 +#define TX3_ARM_IRQ_PIPED_8_EVENT 2040 +#define TX2_ARM_IRQ_PIPED_8_EVENT 2039 +#define TX1_ARM_IRQ_PIPED_8_EVENT 2038 +#define TX0_ARM_IRQ_PIPED_8_EVENT 2037 +#define RX3_ARM_IRQ_PIPED_8_EVENT 2036 +#define RX2_ARM_IRQ_PIPED_8_EVENT 2035 +#define RX1_ARM_IRQ_PIPED_8_EVENT 2034 +#define RX0_ARM_IRQ_PIPED_8_EVENT 2033 +#define ERROR_SPI0_PAGING_EVENT 2075 +#define SOURCE_REDUCER_ERROR_INDICATION_0_EVENT 2073 +#define EAST_RFPLL_CP_OVER_RANGE_EVENT 2069 +#define WEST_RFPLL_CP_OVER_RANGE_EVENT 2068 +#define SPI0_ABORT_EVENT 2074 +#define SCAN_MUXED_I_TX3_GP_INTERRUPT_0_EVENT 2064 +#define SCAN_MUXED_I_TX3_GP_INTERRUPT_1_EVENT 2065 +#define SCAN_MUXED_I_TX2_GP_INTERRUPT_0_EVENT 2062 +#define SCAN_MUXED_I_TX2_GP_INTERRUPT_1_EVENT 2063 +#define SCAN_MUXED_I_TX1_GP_INTERRUPT_0_EVENT 2060 +#define SCAN_MUXED_I_TX1_GP_INTERRUPT_1_EVENT 2061 +#define SCAN_MUXED_I_TX0_GP_INTERRUPT_0_EVENT 2058 +#define SCAN_MUXED_I_TX0_GP_INTERRUPT_1_EVENT 2059 +#define SPI_REG_ARM0_FORCE_GP_INTERRUPT_EVENT 2057 +#define SPI_REG_ARM0_ERROR_EVENT 2056 +#define SPI_REG_ARM0_CALIBRATION_ERROR_EVENT 2055 +#define SPI_REG_ARM0_SYSTEM_ERROR_EVENT 2054 +#define SPI_REG_ARM1_FORCE_GP_INTERRUPT_EVENT 2052 +#define SPI_REG_ARM1_ERROR_EVENT 2051 +#define SPI_REG_ARM1_CALIBRATION_ERROR_EVENT 2050 +#define SPI_REG_ARM1_SYSTEM_ERROR_EVENT 2049 +#define RADIO_SQR_ERROR_EVENT 2093 +#define SW_PINT_0_EVENT 2085 +#define SW_PINT_1_EVENT 2086 +#define SW_PINT_2_EVENT 2087 +#define SW_PINT_3_EVENT 2088 +#define SW_PINT_4_EVENT 2089 +#define SW_PINT_5_EVENT 2090 +#define SW_PINT_6_EVENT 2091 +#define SW_PINT_7_EVENT 2092 +#define SEC_GPINT_DEFAULT_SDEI_EVENT 2100 +#define SEC_L4_ECC_WRN_INTR_0_EVENT 2179 +#define SEC_L4_ECC_ERR_INTR_0_EVENT 2180 +#define SEC_L4_ECC_WRN_INTR_1_EVENT 2181 +#define SEC_L4_ECC_ERR_INTR_1_EVENT 2182 +#define SEC_L4_ECC_WRN_INTR_2_EVENT 2183 +#define SEC_L4_ECC_ERR_INTR_2_EVENT 2184 +#define SEC_WATCHDOG_A55_TIMEOUT_PIPED_0_EVENT 2118 +#define SEC_WATCHDOG_A55_TIMEOUT_PIPED_1_EVENT 2119 +#define SEC_WATCHDOG_A55_TIMEOUT_PIPED_2_EVENT 2120 +#define SEC_WATCHDOG_A55_TIMEOUT_PIPED_3_EVENT 2121 +#define SEC_XCORR_ECC_ERROR_IRQ_PIPED_EVENT 2132 +#define SEC_XCORR_ECC_ERROR_WARNING_PIPED_EVENT 2131 +#define SEC_GIC_FAULT_INT_EVENT 2115 +#define SEC_GIC_ERR_INT_EVENT 2114 +#define SEC_O_DFI_INTERNAL_ERR_INTR_EVENT 2105 +#define SEC_O_DFI_PHYUPD_ERR_INTR_EVENT 2106 +#define SEC_O_DFI_ALERT_ERR_INTR_EVENT 2108 +#define SEC_O_ECC_AP_ERR_INTR_EVENT 2110 +#define SEC_O_ECC_AP_ERR_INTR_FAULT_EVENT 2109 +#define SEC_O_ECC_UNCORRECTED_ERR_INTR_EVENT 2112 +#define SEC_O_ECC_UNCORRECTED_ERR_INTR_FAULT_EVENT 2111 +#define SEC_O_DWC_DDRPHY_INT_N_EVENT 2107 +#define SEC_NFAULTIRQ_0_EVENT 2117 +#define SEC_NFAULTIRQ_1_EVENT 2126 +#define SEC_NFAULTIRQ_2_EVENT 2127 +#define SEC_NFAULTIRQ_3_EVENT 2128 +#define SEC_NFAULTIRQ_4_EVENT 2129 +#define SEC_NERRIRQ_0_EVENT 2122 +#define SEC_NERRIRQ_1_EVENT 2123 +#define SEC_NERRIRQ_2_EVENT 2124 +#define SEC_NERRIRQ_3_EVENT 2125 +#define SEC_NERRIRQ_4_EVENT 2116 +#define SEC_EAST_RFPLL_PLL_LOCKED_SYNC_EVENT 2172 +#define SEC_WEST_RFPLL_PLL_LOCKED_SYNC_EVENT 2171 +#define SEC_CLKPLL_PLL_LOCKED_SYNC_EVENT 2170 +#define SEC_TE_FAULT_GP_INTR_PIPED_EVENT 2178 +#define SEC_CLK_PLL_CP_OVER_RANGE_EVENT 2167 +#define SEC_ETHPLL_LOCKED_SYNC_EVENT 2166 +#define SEC_ARM0_MEMORY_ECC_ERROR_EVENT 2153 +#define SEC_ARM1_MEMORY_ECC_ERROR_EVENT 2148 +#define SEC_GPINT_INTERRUPT_SECONDARY_TO_PRIMARY_EVENT 2130 +#define SEC_NVMB_ERR_FLAG_BOOT_EVENT 2177 +#define SEC_NVMB_ERR_FLAG_TIMEOUT_EVENT 2176 +#define SEC_C2C_PINT_OUT_EVENT 2113 +#define SEC_TX3_NPD_ARM_IRQ_PIPED_8_EVENT 2146 +#define SEC_TX2_NPD_ARM_IRQ_PIPED_8_EVENT 2145 +#define SEC_TX1_NPD_ARM_IRQ_PIPED_8_EVENT 2144 +#define SEC_TX0_NPD_ARM_IRQ_PIPED_8_EVENT 2143 +#define SEC_O_STREAM_PROC_ERROR_EVENT 2142 +#define SEC_ORX0_ARM_IRQ_PIPED_8_EVENT 2141 +#define SEC_TX3_ARM_IRQ_PIPED_8_EVENT 2140 +#define SEC_TX2_ARM_IRQ_PIPED_8_EVENT 2139 +#define SEC_TX1_ARM_IRQ_PIPED_8_EVENT 2138 +#define SEC_TX0_ARM_IRQ_PIPED_8_EVENT 2137 +#define SEC_RX3_ARM_IRQ_PIPED_8_EVENT 2136 +#define SEC_RX2_ARM_IRQ_PIPED_8_EVENT 2135 +#define SEC_RX1_ARM_IRQ_PIPED_8_EVENT 2134 +#define SEC_RX0_ARM_IRQ_PIPED_8_EVENT 2133 +#define SEC_ERROR_SPI0_PAGING_EVENT 2175 +#define SEC_SOURCE_REDUCER_ERROR_INDICATION_0_EVENT 2173 +#define SEC_EAST_RFPLL_CP_OVER_RANGE_EVENT 2169 +#define SEC_WEST_RFPLL_CP_OVER_RANGE_EVENT 2168 +#define SEC_SPI0_ABORT_EVENT 2174 +#define SEC_SCAN_MUXED_I_TX3_GP_INTERRUPT_0_EVENT 2164 +#define SEC_SCAN_MUXED_I_TX3_GP_INTERRUPT_1_EVENT 2165 +#define SEC_SCAN_MUXED_I_TX2_GP_INTERRUPT_0_EVENT 2162 +#define SEC_SCAN_MUXED_I_TX2_GP_INTERRUPT_1_EVENT 2163 +#define SEC_SCAN_MUXED_I_TX1_GP_INTERRUPT_0_EVENT 2160 +#define SEC_SCAN_MUXED_I_TX1_GP_INTERRUPT_1_EVENT 2161 +#define SEC_SCAN_MUXED_I_TX0_GP_INTERRUPT_0_EVENT 2158 +#define SEC_SCAN_MUXED_I_TX0_GP_INTERRUPT_1_EVENT 2159 +#define SEC_SPI_REG_ARM0_FORCE_GP_INTERRUPT_EVENT 2157 +#define SEC_SPI_REG_ARM0_ERROR_EVENT 2156 +#define SEC_SPI_REG_ARM0_CALIBRATION_ERROR_EVENT 2155 +#define SEC_SPI_REG_ARM0_SYSTEM_ERROR_EVENT 2154 +#define SEC_SPI_REG_ARM1_FORCE_GP_INTERRUPT_EVENT 2152 +#define SEC_SPI_REG_ARM1_ERROR_EVENT 2151 +#define SEC_SPI_REG_ARM1_CALIBRATION_ERROR_EVENT 2150 +#define SEC_SPI_REG_ARM1_SYSTEM_ERROR_EVENT 2149 +#define SEC_RADIO_SQR_ERROR_EVENT 2193 +#define SEC_SW_PINT_0_EVENT 2185 +#define SEC_SW_PINT_1_EVENT 2186 +#define SEC_SW_PINT_2_EVENT 2187 +#define SEC_SW_PINT_3_EVENT 2188 +#define SEC_SW_PINT_4_EVENT 2189 +#define SEC_SW_PINT_5_EVENT 2190 +#define SEC_SW_PINT_6_EVENT 2191 +#define SEC_SW_PINT_7_EVENT 2192 + +#endif /* _ADI_SDEI_H */ diff --git a/include/linux/adi-tru.h b/include/linux/adi-tru.h new file mode 100644 index 00000000000000..ee2df9a01bb77f --- /dev/null +++ b/include/linux/adi-tru.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices Trigger Routing Unit (TRU) driver + * + * Copyright 2022 Analog Devices Inc. + */ + +#ifndef ADI_TRU_H +#define ADI_TRU_H + +#include + +struct adi_tru { + struct device *dev; + void __iomem *base; + struct mutex lock; + u32 last_source_id; + u32 last_target_id; + u32 alias_id; + bool preset_locked; + struct list_head node; +}; + +/* Get TRU device by its alias ID */ +struct adi_tru *adi_tru_get(u32 alias_id); + +int adi_tru_enable(struct adi_tru *tru); + +int adi_tru_disable(struct adi_tru *tru); + +int adi_tru_soft_reset(struct adi_tru *tru); + +int adi_tru_trigger(struct adi_tru *tru, int n, ...); + +int adi_tru_connect_source_to_target(struct adi_tru *tru, u32 source, u32 target, bool locked); + +#endif /* ADI_TRU_H */ diff --git a/include/uapi/linux/if_adi_macsec.h b/include/uapi/linux/if_adi_macsec.h new file mode 100644 index 00000000000000..428b3609970d8b --- /dev/null +++ b/include/uapi/linux/if_adi_macsec.h @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +// ----------------------------------------------------------------------------- +// Comcores ApS (R) all rights reserved. +// +// ***************************************************************************** +#ifndef _UAPI_CCO_MACSEC_H +#define _UAPI_CCO_MACSEC_H + +#include + +#define CCO_MACSEC_GENL_NAME "cco_macsec" +#define CCO_MACSEC_GENL_VERSION 1 + +// Comcores MACsec capabilities +#define CCO_CS_AES_GCM_128 1 +#define CCO_CS_AES_GCM_256 2 +#define CCO_CS_AES_GCM_XPN_128 4 +#define CCO_CS_AES_GCM_XPN_256 8 +struct cco_macsec_capabilities { + u64 aes_gcm_128_cs_id; // AES-GCM-128 Cipher Suite globally unique 64-bit (EUI-64) identifier. + u64 aes_gcm_256_cs_id; // AES-GCM-256 Cipher Suite globally unique 64-bit (EUI-64) identifier. + u16 no_of_peers; // Maximum number of peers per CA. + u16 no_of_key_entries_rx; // Maximum number of supported keys in ingress. + u16 no_of_key_entries_tx; // Maximum number of supported keys in egress. + u16 no_of_secys; // Maximum number of virtual ports/SecYs instantiated. + u16 no_tt_entries_rx; // Maximum number of rules for traffic mapping table in ingress. + u16 no_tt_entries_tx; // Maximum number of rules for traffic mapping table in egress. + u8 confidentiality_offs; // Confidentiality offset supported (bit per SecY). + u8 available_ciphersuites; // Bitmask, see CCO_CS_* defines. + u8 vlan_in_clear; // VLAN in clear supported (bit per SecY). + u8 ICVLength; // Number of octets in the ICV. + u8 changesDataLength; // 1 if Cipher Suite changes data length. + u8 offsConfidentiality; // 1 if a selectable offset for confidentiality can be provided. + u8 integrityProtection; // 1 if integrity protection without confidentiality can be provided. + u8 maxTxKeys; // Max. number of keys in simultaneous use for Tx (per SecY). + u8 maxTxChannels; // Max. number of Tx channels (per SecY). + u8 maxRxKeys; // Max. number of keys in simultaneous use for Rx (per SecY). + u8 maxRxChannels; // Max. number of Rx channels (per SecY). + u8 spare; +}; + +// Comcores MACsec SecY extended configuration +struct cco_macsec_secy_ext { + u8 vlan_in_clear; // 1 to select VLAN-in-clear + u8 confidentiality_offset; // Confidentiality offset in bytes (default 0) + u8 spare[2]; + u32 secy_txsc_pn_thr; // SecY / Tx-SC packet number (PN) threshold +}; + +// Comcores MACsec SecY port statistics +struct cco_macsec_port_stats { + u32 ifInOctets; + u32 ifInUcPkts; + u32 ifInMcPkts; + u32 ifInBcPkts; + u32 ifInDiscards; + u32 ifInErrors; + u32 ifOutOctets; + u32 ifOutUcPkts; + u32 ifOutMcPkts; + u32 ifOutBcPkts; + u32 ifOutErrors; +}; + +// Comcores MACsec SecY extra port statistics +struct cco_macsec_ext_port_stats { + u32 compTxDisable; // if common port is disabled + u32 compRxDisable; // if common port is disabled + u32 txSecYDisable; // if common port is enabled, but control port is disabled + u32 rxSecYDisable; // if common port is enabled, but control port is disabled + u32 txReceivingDisable; // if common port is enabled but reception is disabled + u32 rxTransmittingDisable; // if common port is enabled but transmission is disabled +}; + +// Comcores MACsec traffic rule +struct cco_macsec_traffic_rule { + u8 macAddr[6]; + u16 vlanId; + u16 ethType; + u8 reserved; +#define CCO_TRAFFIC_RULE_SEL_MACADDR (1 << 0) // bit 0 +#define CCO_TRAFFIC_RULE_SEL_VLAN (1 << 1) // bit 1 +#define CCO_TRAFFIC_RULE_SEL_ETHTYPE (1 << 2) // bit 2 +#define CCO_TRAFFIC_RULE_SEL_OTHER (1 << 3) // bit 3 + u8 fieldSelect; + u32 other; + u32 secy_index; // index of SecY associated with the rule +}; + +// Comcores MACsec time stats (jiffies) +struct cco_macsec_time_stats { + u32 createdTime; + u32 startedTime; + u32 stoppedTime; + u32 spare; +}; + +enum cco_macsec_attrs { + CCO_MACSEC_ATTR_UNSPEC, + CCO_MACSEC_ATTR_CAPABILITIES, /* struct cco_macsec_capabilities */ + CCO_MACSEC_ATTR_SECY_EXT, /* struct cco_macsec_secy_ext */ + CCO_MACSEC_ATTR_IFINDEX, /* u32, ifindex of the MACsec netdevice */ + CCO_MACSEC_ATTR_INDEX, /* u32, index */ + CCO_MACSEC_ATTR_SCI, /* u64 (sci_t), identifies Rx-SC */ + CCO_MACSEC_ATTR_TRAFFIC_RULE, /* struct cco_macsec_traffic_rule */ + CCO_MACSEC_ATTR_PORT_STATS, /* struct cco_macsec_port_stats */ + CCO_MACSEC_ATTR_EXT_PORT_STATS, /* struct cco_macsec_ext_port_stats */ + CCO_MACSEC_ATTR_TIME_STATS, /* struct cco_macsec_time_stats */ + __CCO_MACSEC_ATTR_END, + NUM_CCO_MACSEC_ATTR = __CCO_MACSEC_ATTR_END, + CCO_MACSEC_ATTR_MAX = __CCO_MACSEC_ATTR_END - 1, +}; + +enum cco_macsec_nl_commands { + CCO_MACSEC_CMD_GET_CAPABILITIES, // returns CCO_MACSEC_ATTR_CAPABILITIES + CCO_MACSEC_CMD_GET_SECY_EXT, // CCO_MACSEC_ATTR_IFINDEX, returns CCO_MACSEC_ATTR_SECY_EXT + CCO_MACSEC_CMD_SET_SECY_EXT, // CCO_MACSEC_ATTR_IFINDEX + CCO_MACSEC_ATTR_SECY_EXT + CCO_MACSEC_CMD_GET_RX_TRAFFIC_RULE, // CCO_MACSEC_ATTR_INDEX, returns CCO_MACSEC_ATTR_TRAFFIC_RULE + CCO_MACSEC_CMD_SET_RX_TRAFFIC_RULE, // CCO_MACSEC_ATTR_INDEX + CCO_MACSEC_ATTR_TRAFFIC_RULE + CCO_MACSEC_CMD_GET_TX_TRAFFIC_RULE, // CCO_MACSEC_ATTR_INDEX, returns CCO_MACSEC_ATTR_TRAFFIC_RULE + CCO_MACSEC_CMD_SET_TX_TRAFFIC_RULE, // CCO_MACSEC_ATTR_INDEX + CCO_MACSEC_ATTR_TRAFFIC_RULE + CCO_MACSEC_CMD_GET_PORT_STATS, // CCO_MACSEC_ATTR_IFINDEX, returns CCO_MACSEC_ATTR_PORT_STATS (controlled port) + CCO_MACSEC_CMD_GET_UPORT_STATS, // CCO_MACSEC_ATTR_IFINDEX, returns CCO_MACSEC_ATTR_PORT_STATS (uncontrolled port) + CCO_MACSEC_CMD_GET_EXT_PORT_STATS, // CCO_MACSEC_ATTR_IFINDEX, returns CCO_MACSEC_ATTR_EXT_PORT_STATS + CCO_MACSEC_CMD_GET_TXSC_EXT, // CCO_MACSEC_ATTR_IFINDEX, returns CCO_MACSEC_ATTR_TIME_STATS + CCO_MACSEC_CMD_GET_RXSC_EXT, // CCO_MACSEC_ATTR_IFINDEX + CCO_MACSEC_ATTR_SCI, returns CCO_MACSEC_ATTR_TIME_STATS + CCO_MACSEC_CMD_GET_TXSA_EXT, // CCO_MACSEC_ATTR_IFINDEX + CCO_MACSEC_ATTR_INDEX (0-3), returns CCO_MACSEC_ATTR_TIME_STATS + CCO_MACSEC_CMD_GET_RXSA_EXT, // CCO_MACSEC_ATTR_IFINDEX + CCO_MACSEC_ATTR_SCI + CCO_MACSEC_ATTR_INDEX (0-3), returns CCO_MACSEC_ATTR_TIME_STATS + CCO_MACSEC_CMD_CLEAR_STATS, // CCO_MACSEC_ATTR_IFINDEX, clear all MACsec stats for ifIndex +}; + +#endif /* _UAPI_CCO_MACSEC_H */