|
@@ -1,4116 +0,0 @@
|
|
-From 401c3c22aab4b09918b43d09c2e90fea2e4a5841 Mon Sep 17 00:00:00 2001
|
|
|
|
-From: Dim Fish <[email protected]>
|
|
|
|
-Date: Fri, 11 Oct 2024 19:25:29 +0300
|
|
|
|
-Subject: [PATCH] mediatek: add Airoha AN8855 gigabit switch driver
|
|
|
|
-
|
|
|
|
-New revisions of Xiaomi AX3000T with 1.0.84+ stock firmware contain new hardware.
|
|
|
|
-This commit add support for Airoha AN8855 gigabit switch driver with 6.6 kernel patches
|
|
|
|
-
|
|
|
|
-Based on https://patchwork.kernel.org/project/netdevbpf/cover/[email protected]/
|
|
|
|
-
|
|
|
|
-Signed-off-by: Dim Fish <[email protected]>
|
|
|
|
----
|
|
|
|
- .../dts/mt7981b-xiaomi-mi-router-common.dtsi | 171 ++
|
|
|
|
- .../files-6.6/drivers/net/dsa/an8855.c | 2585 +++++++++++++++++
|
|
|
|
- .../files-6.6/drivers/net/dsa/an8855.h | 753 +++++
|
|
|
|
- .../files-6.6/drivers/net/phy/air_an8855.c | 268 ++
|
|
|
|
- target/linux/mediatek/filogic/config-6.6 | 2 +
|
|
|
|
- target/linux/mediatek/mt7622/config-6.6 | 2 +
|
|
|
|
- target/linux/mediatek/mt7623/config-6.6 | 2 +
|
|
|
|
- target/linux/mediatek/mt7629/config-6.6 | 2 +
|
|
|
|
- .../737-net-dsa-add-Airoha-AN8855.patch | 197 ++
|
|
|
|
- 9 files changed, 3982 insertions(+)
|
|
|
|
- create mode 100644 target/linux/mediatek/files-6.6/drivers/net/dsa/an8855.c
|
|
|
|
- create mode 100644 target/linux/mediatek/files-6.6/drivers/net/dsa/an8855.h
|
|
|
|
- create mode 100644 target/linux/mediatek/files-6.6/drivers/net/phy/air_an8855.c
|
|
|
|
- create mode 100644 target/linux/mediatek/patches-6.6/737-net-dsa-add-Airoha-AN8855.patch
|
|
|
|
-
|
|
|
|
-diff --git a/target/linux/mediatek/dts/mt7981b-xiaomi-mi-router-common.dtsi b/target/linux/mediatek/dts/mt7981b-xiaomi-mi-router-common.dtsi
|
|
|
|
-index d6872395a9017a..8ddc4c20a07cbe 100644
|
|
|
|
---- a/target/linux/mediatek/dts/mt7981b-xiaomi-mi-router-common.dtsi
|
|
|
|
-+++ b/target/linux/mediatek/dts/mt7981b-xiaomi-mi-router-common.dtsi
|
|
|
|
-@@ -83,6 +83,14 @@
|
|
|
|
- interrupt-parent = <&pio>;
|
|
|
|
- interrupts = <38 IRQ_TYPE_LEVEL_HIGH>;
|
|
|
|
- };
|
|
|
|
-+
|
|
|
|
-+ switch2: switch@1 {
|
|
|
|
-+ compatible = "airoha,an8855";
|
|
|
|
-+ reg = <1>;
|
|
|
|
-+ reset-gpios = <&pio 39 GPIO_ACTIVE_HIGH>;
|
|
|
|
-+ airoha,ext-surge;
|
|
|
|
-+ #nvmem-cell-cells = <0>;
|
|
|
|
-+ };
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- &switch {
|
|
|
|
-@@ -124,6 +132,169 @@
|
|
|
|
- };
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
-+&switch2 {
|
|
|
|
-+ nvmem-layout {
|
|
|
|
-+ compatible = "fixed-layout";
|
|
|
|
-+ #address-cells = <1>;
|
|
|
|
-+ #size-cells = <1>;
|
|
|
|
-+
|
|
|
|
-+ shift_sel_port0_tx_a: shift-sel-port0-tx-a@c {
|
|
|
|
-+ reg = <0xc 0x4>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ shift_sel_port0_tx_b: shift-sel-port0-tx-b@10 {
|
|
|
|
-+ reg = <0x10 0x4>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ shift_sel_port0_tx_c: shift-sel-port0-tx-c@14 {
|
|
|
|
-+ reg = <0x14 0x4>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ shift_sel_port0_tx_d: shift-sel-port0-tx-d@18 {
|
|
|
|
-+ reg = <0x18 0x4>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ shift_sel_port1_tx_a: shift-sel-port1-tx-a@1c {
|
|
|
|
-+ reg = <0x1c 0x4>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ shift_sel_port1_tx_b: shift-sel-port1-tx-b@20 {
|
|
|
|
-+ reg = <0x20 0x4>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ shift_sel_port1_tx_c: shift-sel-port1-tx-c@24 {
|
|
|
|
-+ reg = <0x24 0x4>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ shift_sel_port1_tx_d: shift-sel-port1-tx-d@28 {
|
|
|
|
-+ reg = <0x28 0x4>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ shift_sel_port2_tx_a: shift-sel-port2-tx-a@2c {
|
|
|
|
-+ reg = <0x2c 0x4>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ shift_sel_port2_tx_b: shift-sel-port2-tx-b@30 {
|
|
|
|
-+ reg = <0x30 0x4>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ shift_sel_port2_tx_c: shift-sel-port2-tx-c@34 {
|
|
|
|
-+ reg = <0x34 0x4>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ shift_sel_port2_tx_d: shift-sel-port2-tx-d@38 {
|
|
|
|
-+ reg = <0x38 0x4>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ shift_sel_port3_tx_a: shift-sel-port3-tx-a@4c {
|
|
|
|
-+ reg = <0x4c 0x4>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ shift_sel_port3_tx_b: shift-sel-port3-tx-b@50 {
|
|
|
|
-+ reg = <0x50 0x4>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ shift_sel_port3_tx_c: shift-sel-port3-tx-c@54 {
|
|
|
|
-+ reg = <0x54 0x4>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ shift_sel_port3_tx_d: shift-sel-port3-tx-d@58 {
|
|
|
|
-+ reg = <0x58 0x4>;
|
|
|
|
-+ };
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ ports {
|
|
|
|
-+ #address-cells = <1>;
|
|
|
|
-+ #size-cells = <0>;
|
|
|
|
-+
|
|
|
|
-+ port@0 {
|
|
|
|
-+ reg = <0>;
|
|
|
|
-+ label = "wan";
|
|
|
|
-+ phy-mode = "internal";
|
|
|
|
-+ phy-handle = <&internal_phy1>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ port@1 {
|
|
|
|
-+ reg = <1>;
|
|
|
|
-+ label = "lan2";
|
|
|
|
-+ phy-mode = "internal";
|
|
|
|
-+ phy-handle = <&internal_phy2>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ port@2 {
|
|
|
|
-+ reg = <2>;
|
|
|
|
-+ label = "lan3";
|
|
|
|
-+ phy-mode = "internal";
|
|
|
|
-+ phy-handle = <&internal_phy3>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ port@3 {
|
|
|
|
-+ reg = <3>;
|
|
|
|
-+ label = "lan4";
|
|
|
|
-+ phy-mode = "internal";
|
|
|
|
-+ phy-handle = <&internal_phy4>;
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ port@5 {
|
|
|
|
-+ reg = <5>;
|
|
|
|
-+ label = "cpu";
|
|
|
|
-+ ethernet = <&gmac0>;
|
|
|
|
-+ phy-mode = "2500base-x";
|
|
|
|
-+
|
|
|
|
-+ fixed-link {
|
|
|
|
-+ speed = <2500>;
|
|
|
|
-+ full-duplex;
|
|
|
|
-+ pause;
|
|
|
|
-+ };
|
|
|
|
-+ };
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ mdio {
|
|
|
|
-+ #address-cells = <1>;
|
|
|
|
-+ #size-cells = <0>;
|
|
|
|
-+
|
|
|
|
-+ internal_phy1: phy@1 {
|
|
|
|
-+ reg = <1>;
|
|
|
|
-+
|
|
|
|
-+ nvmem-cells = <&shift_sel_port0_tx_a>,
|
|
|
|
-+ <&shift_sel_port0_tx_b>,
|
|
|
|
-+ <&shift_sel_port0_tx_c>,
|
|
|
|
-+ <&shift_sel_port0_tx_d>;
|
|
|
|
-+ nvmem-cell-names = "tx_a", "tx_b", "tx_c", "tx_d";
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ internal_phy2: phy@2 {
|
|
|
|
-+ reg = <2>;
|
|
|
|
-+
|
|
|
|
-+ nvmem-cells = <&shift_sel_port1_tx_a>,
|
|
|
|
-+ <&shift_sel_port1_tx_b>,
|
|
|
|
-+ <&shift_sel_port1_tx_c>,
|
|
|
|
-+ <&shift_sel_port1_tx_d>;
|
|
|
|
-+ nvmem-cell-names = "tx_a", "tx_b", "tx_c", "tx_d";
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ internal_phy3: phy@3 {
|
|
|
|
-+ reg = <3>;
|
|
|
|
-+
|
|
|
|
-+ nvmem-cells = <&shift_sel_port2_tx_a>,
|
|
|
|
-+ <&shift_sel_port2_tx_b>,
|
|
|
|
-+ <&shift_sel_port2_tx_c>,
|
|
|
|
-+ <&shift_sel_port2_tx_d>;
|
|
|
|
-+ nvmem-cell-names = "tx_a", "tx_b", "tx_c", "tx_d";
|
|
|
|
-+ };
|
|
|
|
-+
|
|
|
|
-+ internal_phy4: phy@4 {
|
|
|
|
-+ reg = <4>;
|
|
|
|
-+
|
|
|
|
-+ nvmem-cells = <&shift_sel_port3_tx_a>,
|
|
|
|
-+ <&shift_sel_port3_tx_b>,
|
|
|
|
-+ <&shift_sel_port3_tx_c>,
|
|
|
|
-+ <&shift_sel_port3_tx_d>;
|
|
|
|
-+ nvmem-cell-names = "tx_a", "tx_b", "tx_c", "tx_d";
|
|
|
|
-+ };
|
|
|
|
-+ };
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
- &spi0 {
|
|
|
|
- pinctrl-names = "default";
|
|
|
|
- pinctrl-0 = <&spi0_flash_pins>;
|
|
|
|
-diff --git a/target/linux/mediatek/files-6.6/drivers/net/dsa/an8855.c b/target/linux/mediatek/files-6.6/drivers/net/dsa/an8855.c
|
|
|
|
-new file mode 100644
|
|
|
|
-index 00000000000000..6ce7574255c32d
|
|
|
|
---- /dev/null
|
|
|
|
-+++ b/target/linux/mediatek/files-6.6/drivers/net/dsa/an8855.c
|
|
|
|
-@@ -0,0 +1,2585 @@
|
|
|
|
-+// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
-+/*
|
|
|
|
-+ * Airoha AN8855 DSA Switch driver
|
|
|
|
-+ * Copyright (C) 2023 Min Yao <[email protected]>
|
|
|
|
-+ * Copyright (C) 2024 Christian Marangi <[email protected]>
|
|
|
|
-+ */
|
|
|
|
-+#include <linux/bitfield.h>
|
|
|
|
-+#include <linux/ethtool.h>
|
|
|
|
-+#include <linux/etherdevice.h>
|
|
|
|
-+#include <linux/gpio/consumer.h>
|
|
|
|
-+#include <linux/if_bridge.h>
|
|
|
|
-+#include <linux/iopoll.h>
|
|
|
|
-+#include <linux/mdio.h>
|
|
|
|
-+#include <linux/netdevice.h>
|
|
|
|
-+#include <linux/nvmem-provider.h>
|
|
|
|
-+#include <linux/of_mdio.h>
|
|
|
|
-+#include <linux/of_net.h>
|
|
|
|
-+#include <linux/of_platform.h>
|
|
|
|
-+#include <linux/phylink.h>
|
|
|
|
-+#include <linux/regmap.h>
|
|
|
|
-+#include <net/dsa.h>
|
|
|
|
-+
|
|
|
|
-+#include "an8855.h"
|
|
|
|
-+
|
|
|
|
-+static const struct an8855_mib_desc an8855_mib[] = {
|
|
|
|
-+ MIB_DESC(1, AN8855_PORT_MIB_TX_DROP, "TxDrop"),
|
|
|
|
-+ MIB_DESC(1, AN8855_PORT_MIB_TX_CRC_ERR, "TxCrcErr"),
|
|
|
|
-+ MIB_DESC(1, AN8855_PORT_MIB_TX_COLLISION, "TxCollision"),
|
|
|
|
-+ MIB_DESC(1, AN8855_PORT_MIB_TX_OVERSIZE_DROP, "TxOversizeDrop"),
|
|
|
|
-+ MIB_DESC(2, AN8855_PORT_MIB_TX_BAD_PKT_BYTES, "TxBadPktBytes"),
|
|
|
|
-+ MIB_DESC(1, AN8855_PORT_MIB_RX_DROP, "RxDrop"),
|
|
|
|
-+ MIB_DESC(1, AN8855_PORT_MIB_RX_FILTERING, "RxFiltering"),
|
|
|
|
-+ MIB_DESC(1, AN8855_PORT_MIB_RX_CRC_ERR, "RxCrcErr"),
|
|
|
|
-+ MIB_DESC(1, AN8855_PORT_MIB_RX_CTRL_DROP, "RxCtrlDrop"),
|
|
|
|
-+ MIB_DESC(1, AN8855_PORT_MIB_RX_INGRESS_DROP, "RxIngressDrop"),
|
|
|
|
-+ MIB_DESC(1, AN8855_PORT_MIB_RX_ARL_DROP, "RxArlDrop"),
|
|
|
|
-+ MIB_DESC(1, AN8855_PORT_MIB_FLOW_CONTROL_DROP, "FlowControlDrop"),
|
|
|
|
-+ MIB_DESC(1, AN8855_PORT_MIB_WRED_DROP, "WredDrop"),
|
|
|
|
-+ MIB_DESC(1, AN8855_PORT_MIB_MIRROR_DROP, "MirrorDrop"),
|
|
|
|
-+ MIB_DESC(2, AN8855_PORT_MIB_RX_BAD_PKT_BYTES, "RxBadPktBytes"),
|
|
|
|
-+ MIB_DESC(1, AN8855_PORT_MIB_RXS_FLOW_SAMPLING_PKT_DROP, "RxsFlowSamplingPktDrop"),
|
|
|
|
-+ MIB_DESC(1, AN8855_PORT_MIB_RXS_FLOW_TOTAL_PKT_DROP, "RxsFlowTotalPktDrop"),
|
|
|
|
-+ MIB_DESC(1, AN8855_PORT_MIB_PORT_CONTROL_DROP, "PortControlDrop"),
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+static int an8855_mii_set_page(struct mii_bus *bus, u8 phy_id, u8 page)
|
|
|
|
-+{
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ ret = __mdiobus_write(bus, phy_id, AN8855_PHY_SELECT_PAGE, page);
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ dev_err_ratelimited(&bus->dev,
|
|
|
|
-+ "failed to set an8855 mii page\n");
|
|
|
|
-+
|
|
|
|
-+ return ret;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_mii_read32(struct mii_bus *bus, u8 phy_id, u32 reg, u32 *val)
|
|
|
|
-+{
|
|
|
|
-+ int lo, hi, ret;
|
|
|
|
-+
|
|
|
|
-+ ret = __mdiobus_write(bus, phy_id, AN8855_PBUS_MODE,
|
|
|
|
-+ AN8855_PBUS_MODE_ADDR_FIXED);
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ goto err;
|
|
|
|
-+
|
|
|
|
-+ ret = __mdiobus_write(bus, phy_id, AN8855_PBUS_RD_ADDR_HIGH,
|
|
|
|
-+ upper_16_bits(reg));
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ goto err;
|
|
|
|
-+ ret = __mdiobus_write(bus, phy_id, AN8855_PBUS_RD_ADDR_LOW,
|
|
|
|
-+ lower_16_bits(reg));
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ goto err;
|
|
|
|
-+
|
|
|
|
-+ hi = __mdiobus_read(bus, phy_id, AN8855_PBUS_RD_DATA_HIGH);
|
|
|
|
-+ if (hi < 0) {
|
|
|
|
-+ ret = hi;
|
|
|
|
-+ goto err;
|
|
|
|
-+ }
|
|
|
|
-+ lo = __mdiobus_read(bus, phy_id, AN8855_PBUS_RD_DATA_LOW);
|
|
|
|
-+ if (lo < 0) {
|
|
|
|
-+ ret = lo;
|
|
|
|
-+ goto err;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ *val = ((u16)hi << 16) | ((u16)lo & 0xffff);
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+err:
|
|
|
|
-+ dev_err_ratelimited(&bus->dev,
|
|
|
|
-+ "failed to read an8855 register\n");
|
|
|
|
-+ return ret;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ctx;
|
|
|
|
-+ struct mii_bus *bus = priv->bus;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
|
|
|
|
-+ ret = an8855_mii_set_page(bus, priv->phy_base, AN8855_PHY_PAGE_EXTENDED_4);
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ goto exit;
|
|
|
|
-+
|
|
|
|
-+ ret = an8855_mii_read32(bus, priv->phy_base,
|
|
|
|
-+ reg, val);
|
|
|
|
-+
|
|
|
|
-+exit:
|
|
|
|
-+ an8855_mii_set_page(bus, priv->phy_base, AN8855_PHY_PAGE_STANDARD);
|
|
|
|
-+ mutex_unlock(&bus->mdio_lock);
|
|
|
|
-+
|
|
|
|
-+ return ret < 0 ? ret : 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_mii_write32(struct mii_bus *bus, u8 phy_id, u32 reg, u32 val)
|
|
|
|
-+{
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ ret = __mdiobus_write(bus, phy_id, AN8855_PBUS_MODE,
|
|
|
|
-+ AN8855_PBUS_MODE_ADDR_FIXED);
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ goto err;
|
|
|
|
-+
|
|
|
|
-+ ret = __mdiobus_write(bus, phy_id, AN8855_PBUS_WR_ADDR_HIGH,
|
|
|
|
-+ upper_16_bits(reg));
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ goto err;
|
|
|
|
-+ ret = __mdiobus_write(bus, phy_id, AN8855_PBUS_WR_ADDR_LOW,
|
|
|
|
-+ lower_16_bits(reg));
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ goto err;
|
|
|
|
-+
|
|
|
|
-+ ret = __mdiobus_write(bus, phy_id, AN8855_PBUS_WR_DATA_HIGH,
|
|
|
|
-+ upper_16_bits(val));
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ goto err;
|
|
|
|
-+ ret = __mdiobus_write(bus, phy_id, AN8855_PBUS_WR_DATA_LOW,
|
|
|
|
-+ lower_16_bits(val));
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ goto err;
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+err:
|
|
|
|
-+ dev_err_ratelimited(&bus->dev,
|
|
|
|
-+ "failed to write an8855 register\n");
|
|
|
|
-+ return ret;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int
|
|
|
|
-+an8855_regmap_write(void *ctx, uint32_t reg, uint32_t val)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ctx;
|
|
|
|
-+ struct mii_bus *bus = priv->bus;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
|
|
|
|
-+ ret = an8855_mii_set_page(bus, priv->phy_base, AN8855_PHY_PAGE_EXTENDED_4);
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ goto exit;
|
|
|
|
-+
|
|
|
|
-+ ret = an8855_mii_write32(priv->bus, priv->phy_base,
|
|
|
|
-+ reg, val);
|
|
|
|
-+
|
|
|
|
-+exit:
|
|
|
|
-+ an8855_mii_set_page(bus, priv->phy_base, AN8855_PHY_PAGE_STANDARD);
|
|
|
|
-+ mutex_unlock(&bus->mdio_lock);
|
|
|
|
-+
|
|
|
|
-+ return ret < 0 ? ret : 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int
|
|
|
|
-+an8855_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ctx;
|
|
|
|
-+ struct mii_bus *bus = priv->bus;
|
|
|
|
-+ u32 val;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
|
|
|
|
-+ ret = an8855_mii_set_page(bus, priv->phy_base, AN8855_PHY_PAGE_EXTENDED_4);
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ goto exit;
|
|
|
|
-+
|
|
|
|
-+ ret = an8855_mii_read32(bus, priv->phy_base, reg, &val);
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ goto exit;
|
|
|
|
-+
|
|
|
|
-+ val &= ~mask;
|
|
|
|
-+ val |= write_val;
|
|
|
|
-+ ret = an8855_mii_write32(bus, priv->phy_base, reg, val);
|
|
|
|
-+
|
|
|
|
-+exit:
|
|
|
|
-+ an8855_mii_set_page(bus, priv->phy_base, AN8855_PHY_PAGE_STANDARD);
|
|
|
|
-+ mutex_unlock(&bus->mdio_lock);
|
|
|
|
-+
|
|
|
|
-+ return ret < 0 ? ret : 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static const struct regmap_range an8855_readable_ranges[] = {
|
|
|
|
-+ regmap_reg_range(0x10000000, 0x10000fff), /* SCU */
|
|
|
|
-+ regmap_reg_range(0x10001000, 0x10001fff), /* RBUS */
|
|
|
|
-+ regmap_reg_range(0x10002000, 0x10002fff), /* MCU */
|
|
|
|
-+ regmap_reg_range(0x10005000, 0x10005fff), /* SYS SCU */
|
|
|
|
-+ regmap_reg_range(0x10007000, 0x10007fff), /* I2C Slave */
|
|
|
|
-+ regmap_reg_range(0x10008000, 0x10008fff), /* I2C Master */
|
|
|
|
-+ regmap_reg_range(0x10009000, 0x10009fff), /* PDMA */
|
|
|
|
-+ regmap_reg_range(0x1000a100, 0x1000a2ff), /* General Purpose Timer */
|
|
|
|
-+ regmap_reg_range(0x1000a200, 0x1000a2ff), /* GPU timer */
|
|
|
|
-+ regmap_reg_range(0x1000a300, 0x1000a3ff), /* GPIO */
|
|
|
|
-+ regmap_reg_range(0x1000a400, 0x1000a5ff), /* EFUSE */
|
|
|
|
-+ regmap_reg_range(0x1000c000, 0x1000cfff), /* GDMP CSR */
|
|
|
|
-+ regmap_reg_range(0x10010000, 0x1001ffff), /* GDMP SRAM */
|
|
|
|
-+ regmap_reg_range(0x10200000, 0x10203fff), /* Switch - ARL Global */
|
|
|
|
-+ regmap_reg_range(0x10204000, 0x10207fff), /* Switch - BMU */
|
|
|
|
-+ regmap_reg_range(0x10208000, 0x1020bfff), /* Switch - ARL Port */
|
|
|
|
-+ regmap_reg_range(0x1020c000, 0x1020cfff), /* Switch - SCH */
|
|
|
|
-+ regmap_reg_range(0x10210000, 0x10213fff), /* Switch - MAC */
|
|
|
|
-+ regmap_reg_range(0x10214000, 0x10217fff), /* Switch - MIB */
|
|
|
|
-+ regmap_reg_range(0x10218000, 0x1021bfff), /* Switch - Port Control */
|
|
|
|
-+ regmap_reg_range(0x1021c000, 0x1021ffff), /* Switch - TOP */
|
|
|
|
-+ regmap_reg_range(0x10220000, 0x1022ffff), /* SerDes */
|
|
|
|
-+ regmap_reg_range(0x10286000, 0x10286fff), /* RG Batcher */
|
|
|
|
-+ regmap_reg_range(0x1028c000, 0x1028ffff), /* ETHER_SYS */
|
|
|
|
-+ regmap_reg_range(0x30000000, 0x37ffffff), /* I2C EEPROM */
|
|
|
|
-+ regmap_reg_range(0x38000000, 0x3fffffff), /* BOOT_ROM */
|
|
|
|
-+ regmap_reg_range(0xa0000000, 0xbfffffff), /* GPHY */
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+static const struct regmap_access_table an8855_readable_table = {
|
|
|
|
-+ .yes_ranges = an8855_readable_ranges,
|
|
|
|
-+ .n_yes_ranges = ARRAY_SIZE(an8855_readable_ranges),
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+static const struct regmap_config an8855_regmap_config = {
|
|
|
|
-+ .reg_bits = 32,
|
|
|
|
-+ .val_bits = 32,
|
|
|
|
-+ .reg_stride = 4,
|
|
|
|
-+ .max_register = 0xbfffffff,
|
|
|
|
-+ .reg_read = an8855_regmap_read,
|
|
|
|
-+ .reg_write = an8855_regmap_write,
|
|
|
|
-+ .reg_update_bits = an8855_regmap_update_bits,
|
|
|
|
-+ .disable_locking = true,
|
|
|
|
-+ .rd_table = &an8855_readable_table,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+static int
|
|
|
|
-+an8855_mib_init(struct an8855_priv *priv)
|
|
|
|
-+{
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_MIB_CCR,
|
|
|
|
-+ AN8855_CCR_MIB_ENABLE);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ return regmap_write(priv->regmap, AN8855_MIB_CCR,
|
|
|
|
-+ AN8855_CCR_MIB_ACTIVATE);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void an8855_fdb_write(struct an8855_priv *priv, u16 vid,
|
|
|
|
-+ u8 port_mask, const u8 *mac,
|
|
|
|
-+ bool add) __must_hold(&priv->reg_mutex)
|
|
|
|
-+{
|
|
|
|
-+ u32 mac_reg[2] = { };
|
|
|
|
-+ u32 reg;
|
|
|
|
-+
|
|
|
|
-+ mac_reg[0] |= FIELD_PREP(AN8855_ATA1_MAC0, mac[0]);
|
|
|
|
-+ mac_reg[0] |= FIELD_PREP(AN8855_ATA1_MAC1, mac[1]);
|
|
|
|
-+ mac_reg[0] |= FIELD_PREP(AN8855_ATA1_MAC2, mac[2]);
|
|
|
|
-+ mac_reg[0] |= FIELD_PREP(AN8855_ATA1_MAC3, mac[3]);
|
|
|
|
-+ mac_reg[1] |= FIELD_PREP(AN8855_ATA2_MAC4, mac[4]);
|
|
|
|
-+ mac_reg[1] |= FIELD_PREP(AN8855_ATA2_MAC5, mac[5]);
|
|
|
|
-+
|
|
|
|
-+ regmap_bulk_write(priv->regmap, AN8855_ATA1, mac_reg,
|
|
|
|
-+ ARRAY_SIZE(mac_reg));
|
|
|
|
-+
|
|
|
|
-+ reg = AN8855_ATWD_IVL;
|
|
|
|
-+ if (add)
|
|
|
|
-+ reg |= AN8855_ATWD_VLD;
|
|
|
|
-+ reg |= FIELD_PREP(AN8855_ATWD_VID, vid);
|
|
|
|
-+ reg |= FIELD_PREP(AN8855_ATWD_FID, AN8855_FID_BRIDGED);
|
|
|
|
-+ regmap_write(priv->regmap, AN8855_ATWD, reg);
|
|
|
|
-+ regmap_write(priv->regmap, AN8855_ATWD2,
|
|
|
|
-+ FIELD_PREP(AN8855_ATWD2_PORT, port_mask));
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void an8855_fdb_read(struct an8855_priv *priv, struct an8855_fdb *fdb)
|
|
|
|
-+{
|
|
|
|
-+ u32 reg[4];
|
|
|
|
-+
|
|
|
|
-+ regmap_bulk_read(priv->regmap, AN8855_ATRD0, reg,
|
|
|
|
-+ ARRAY_SIZE(reg));
|
|
|
|
-+
|
|
|
|
-+ fdb->live = FIELD_GET(AN8855_ATRD0_LIVE, reg[0]);
|
|
|
|
-+ fdb->type = FIELD_GET(AN8855_ATRD0_TYPE, reg[0]);
|
|
|
|
-+ fdb->ivl = FIELD_GET(AN8855_ATRD0_IVL, reg[0]);
|
|
|
|
-+ fdb->vid = FIELD_GET(AN8855_ATRD0_VID, reg[0]);
|
|
|
|
-+ fdb->fid = FIELD_GET(AN8855_ATRD0_FID, reg[0]);
|
|
|
|
-+ fdb->aging = FIELD_GET(AN8855_ATRD1_AGING, reg[1]);
|
|
|
|
-+ fdb->port_mask = FIELD_GET(AN8855_ATRD3_PORTMASK, reg[3]);
|
|
|
|
-+ fdb->mac[0] = FIELD_GET(AN8855_ATRD2_MAC0, reg[2]);
|
|
|
|
-+ fdb->mac[1] = FIELD_GET(AN8855_ATRD2_MAC1, reg[2]);
|
|
|
|
-+ fdb->mac[2] = FIELD_GET(AN8855_ATRD2_MAC2, reg[2]);
|
|
|
|
-+ fdb->mac[3] = FIELD_GET(AN8855_ATRD2_MAC3, reg[2]);
|
|
|
|
-+ fdb->mac[4] = FIELD_GET(AN8855_ATRD1_MAC4, reg[1]);
|
|
|
|
-+ fdb->mac[5] = FIELD_GET(AN8855_ATRD1_MAC5, reg[1]);
|
|
|
|
-+ fdb->noarp = !!FIELD_GET(AN8855_ATRD0_ARP, reg[0]);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_fdb_cmd(struct an8855_priv *priv, u32 cmd,
|
|
|
|
-+ u32 *rsp) __must_hold(&priv->reg_mutex)
|
|
|
|
-+{
|
|
|
|
-+ u32 val;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ /* Set the command operating upon the MAC address entries */
|
|
|
|
-+ val = AN8855_ATC_BUSY | cmd;
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_ATC, val);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_read_poll_timeout(priv->regmap, AN8855_ATC, val,
|
|
|
|
-+ !(val & AN8855_ATC_BUSY), 20, 200000);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ if (rsp)
|
|
|
|
-+ *rsp = val;
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void
|
|
|
|
-+an8855_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
|
|
|
|
-+{
|
|
|
|
-+ struct dsa_port *dp = dsa_to_port(ds, port);
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ bool learning = false;
|
|
|
|
-+ u32 stp_state;
|
|
|
|
-+
|
|
|
|
-+ switch (state) {
|
|
|
|
-+ case BR_STATE_DISABLED:
|
|
|
|
-+ stp_state = AN8855_STP_DISABLED;
|
|
|
|
-+ break;
|
|
|
|
-+ case BR_STATE_BLOCKING:
|
|
|
|
-+ stp_state = AN8855_STP_BLOCKING;
|
|
|
|
-+ break;
|
|
|
|
-+ case BR_STATE_LISTENING:
|
|
|
|
-+ stp_state = AN8855_STP_LISTENING;
|
|
|
|
-+ break;
|
|
|
|
-+ case BR_STATE_LEARNING:
|
|
|
|
-+ stp_state = AN8855_STP_LEARNING;
|
|
|
|
-+ learning = dp->learning;
|
|
|
|
-+ break;
|
|
|
|
-+ case BR_STATE_FORWARDING:
|
|
|
|
-+ learning = dp->learning;
|
|
|
|
-+ fallthrough;
|
|
|
|
-+ default:
|
|
|
|
-+ stp_state = AN8855_STP_FORWARDING;
|
|
|
|
-+ break;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ regmap_update_bits(priv->regmap, AN8855_SSP_P(port),
|
|
|
|
-+ AN8855_FID_PST_MASK(AN8855_FID_BRIDGED),
|
|
|
|
-+ AN8855_FID_PST_VAL(AN8855_FID_BRIDGED, stp_state));
|
|
|
|
-+
|
|
|
|
-+ regmap_update_bits(priv->regmap, AN8855_PSC_P(port), AN8855_SA_DIS,
|
|
|
|
-+ learning ? 0 : AN8855_SA_DIS);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void an8855_port_fast_age(struct dsa_switch *ds, int port)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ /* Set to clean Dynamic entry */
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_ATA2, AN8855_ATA2_TYPE);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return;
|
|
|
|
-+
|
|
|
|
-+ /* Set Port */
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_ATWD2,
|
|
|
|
-+ FIELD_PREP(AN8855_ATWD2_PORT, BIT(port)));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return;
|
|
|
|
-+
|
|
|
|
-+ /* Flush Dynamic entry at port */
|
|
|
|
-+ an8855_fdb_cmd(priv, AN8855_ATC_MAT(AND8855_FDB_MAT_MAC_TYPE_PORT) |
|
|
|
|
-+ AN8855_FDB_FLUSH, NULL);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_update_port_member(struct dsa_switch *ds, int port,
|
|
|
|
-+ const struct net_device *bridge_dev,
|
|
|
|
-+ bool join)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ bool isolated, other_isolated;
|
|
|
|
-+ struct dsa_port *dp;
|
|
|
|
-+ u32 port_mask = 0;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ isolated = !!(priv->port_isolated_map & BIT(port));
|
|
|
|
-+
|
|
|
|
-+ dsa_switch_for_each_user_port(dp, ds) {
|
|
|
|
-+ if (dp->index == port)
|
|
|
|
-+ continue;
|
|
|
|
-+
|
|
|
|
-+ if (!dsa_port_offloads_bridge_dev(dp, bridge_dev))
|
|
|
|
-+ continue;
|
|
|
|
-+
|
|
|
|
-+ other_isolated = !!(priv->port_isolated_map & BIT(dp->index));
|
|
|
|
-+ port_mask |= BIT(dp->index);
|
|
|
|
-+ /* Add/remove this port to the portvlan mask of the other
|
|
|
|
-+ * ports in the bridge
|
|
|
|
-+ */
|
|
|
|
-+ if (join && !(isolated && other_isolated))
|
|
|
|
-+ ret = regmap_set_bits(priv->regmap,
|
|
|
|
-+ AN8855_PORTMATRIX_P(dp->index),
|
|
|
|
-+ FIELD_PREP(AN8855_USER_PORTMATRIX,
|
|
|
|
-+ BIT(port)));
|
|
|
|
-+ else
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap,
|
|
|
|
-+ AN8855_PORTMATRIX_P(dp->index),
|
|
|
|
-+ FIELD_PREP(AN8855_USER_PORTMATRIX,
|
|
|
|
-+ BIT(port)));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ /* Add/remove all other ports to this port's portvlan mask */
|
|
|
|
-+ return regmap_update_bits(priv->regmap, AN8855_PORTMATRIX_P(port),
|
|
|
|
-+ AN8855_USER_PORTMATRIX,
|
|
|
|
-+ join ? port_mask : ~port_mask);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_port_pre_bridge_flags(struct dsa_switch *ds, int port,
|
|
|
|
-+ struct switchdev_brport_flags flags,
|
|
|
|
-+ struct netlink_ext_ack *extack)
|
|
|
|
-+{
|
|
|
|
-+ if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD |
|
|
|
|
-+ BR_BCAST_FLOOD | BR_ISOLATED))
|
|
|
|
-+ return -EINVAL;
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_port_bridge_flags(struct dsa_switch *ds, int port,
|
|
|
|
-+ struct switchdev_brport_flags flags,
|
|
|
|
-+ struct netlink_ext_ack *extack)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ if (flags.mask & BR_LEARNING) {
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PSC_P(port), AN8855_SA_DIS,
|
|
|
|
-+ flags.val & BR_LEARNING ? 0 : AN8855_SA_DIS);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ if (flags.mask & BR_FLOOD) {
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_UNUF, BIT(port),
|
|
|
|
-+ flags.val & BR_FLOOD ? BIT(port) : 0);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ if (flags.mask & BR_MCAST_FLOOD) {
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_UNMF, BIT(port),
|
|
|
|
-+ flags.val & BR_MCAST_FLOOD ? BIT(port) : 0);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ if (flags.mask & BR_BCAST_FLOOD) {
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_BCF, BIT(port),
|
|
|
|
-+ flags.val & BR_BCAST_FLOOD ? BIT(port) : 0);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ if (flags.mask & BR_ISOLATED) {
|
|
|
|
-+ struct dsa_port *dp = dsa_to_port(ds, port);
|
|
|
|
-+ struct net_device *bridge_dev = dsa_port_bridge_dev_get(dp);
|
|
|
|
-+
|
|
|
|
-+ if (flags.val & BR_ISOLATED)
|
|
|
|
-+ priv->port_isolated_map |= BIT(port);
|
|
|
|
-+ else
|
|
|
|
-+ priv->port_isolated_map &= ~BIT(port);
|
|
|
|
-+
|
|
|
|
-+ ret = an8855_update_port_member(ds, port, bridge_dev, true);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ u32 age_count, age_unit, val;
|
|
|
|
-+
|
|
|
|
-+ /* Convert msec in AN8855_L2_AGING_MS_CONSTANT counter */
|
|
|
|
-+ val = msecs / AN8855_L2_AGING_MS_CONSTANT;
|
|
|
|
-+ /* Derive the count unit */
|
|
|
|
-+ age_unit = val / FIELD_MAX(AN8855_AGE_UNIT);
|
|
|
|
-+ /* Get the count in unit, age_unit is always incremented by 1 internally */
|
|
|
|
-+ age_count = val / (age_unit + 1);
|
|
|
|
-+
|
|
|
|
-+ return regmap_update_bits(priv->regmap, AN8855_AAC,
|
|
|
|
-+ AN8855_AGE_CNT | AN8855_AGE_UNIT,
|
|
|
|
-+ FIELD_PREP(AN8855_AGE_CNT, age_count) |
|
|
|
|
-+ FIELD_PREP(AN8855_AGE_UNIT, age_unit));
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_port_bridge_join(struct dsa_switch *ds, int port,
|
|
|
|
-+ struct dsa_bridge bridge,
|
|
|
|
-+ bool *tx_fwd_offload,
|
|
|
|
-+ struct netlink_ext_ack *extack)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ ret = an8855_update_port_member(ds, port, bridge.dev, true);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Set to fallback mode for independent VLAN learning if in a bridge */
|
|
|
|
-+ return regmap_update_bits(priv->regmap, AN8855_PCR_P(port),
|
|
|
|
-+ AN8855_PORT_VLAN,
|
|
|
|
-+ FIELD_PREP(AN8855_PORT_VLAN,
|
|
|
|
-+ AN8855_PORT_FALLBACK_MODE));
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void an8855_port_bridge_leave(struct dsa_switch *ds, int port,
|
|
|
|
-+ struct dsa_bridge bridge)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+
|
|
|
|
-+ an8855_update_port_member(ds, port, bridge.dev, false);
|
|
|
|
-+
|
|
|
|
-+ /* When a port is removed from the bridge, the port would be set up
|
|
|
|
-+ * back to the default as is at initial boot which is a VLAN-unaware
|
|
|
|
-+ * port.
|
|
|
|
-+ */
|
|
|
|
-+ regmap_update_bits(priv->regmap, AN8855_PCR_P(port),
|
|
|
|
-+ AN8855_PORT_VLAN,
|
|
|
|
-+ FIELD_PREP(AN8855_PORT_VLAN,
|
|
|
|
-+ AN8855_PORT_MATRIX_MODE));
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_port_fdb_add(struct dsa_switch *ds, int port,
|
|
|
|
-+ const unsigned char *addr, u16 vid,
|
|
|
|
-+ struct dsa_db db)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ u8 port_mask = BIT(port);
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ /* Set the vid to the port vlan id if no vid is set */
|
|
|
|
-+ if (!vid)
|
|
|
|
-+ vid = AN8855_PORT_VID_DEFAULT;
|
|
|
|
-+
|
|
|
|
-+ mutex_lock(&priv->reg_mutex);
|
|
|
|
-+ an8855_fdb_write(priv, vid, port_mask, addr, true);
|
|
|
|
-+ ret = an8855_fdb_cmd(priv, AN8855_FDB_WRITE, NULL);
|
|
|
|
-+ mutex_unlock(&priv->reg_mutex);
|
|
|
|
-+
|
|
|
|
-+ return ret;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_port_fdb_del(struct dsa_switch *ds, int port,
|
|
|
|
-+ const unsigned char *addr, u16 vid,
|
|
|
|
-+ struct dsa_db db)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ u8 port_mask = BIT(port);
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ /* Set the vid to the port vlan id if no vid is set */
|
|
|
|
-+ if (!vid)
|
|
|
|
-+ vid = AN8855_PORT_VID_DEFAULT;
|
|
|
|
-+
|
|
|
|
-+ mutex_lock(&priv->reg_mutex);
|
|
|
|
-+ an8855_fdb_write(priv, vid, port_mask, addr, false);
|
|
|
|
-+ ret = an8855_fdb_cmd(priv, AN8855_FDB_WRITE, NULL);
|
|
|
|
-+ mutex_unlock(&priv->reg_mutex);
|
|
|
|
-+
|
|
|
|
-+ return ret;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_port_fdb_dump(struct dsa_switch *ds, int port,
|
|
|
|
-+ dsa_fdb_dump_cb_t *cb, void *data)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ int banks, count = 0;
|
|
|
|
-+ u32 rsp;
|
|
|
|
-+ int ret;
|
|
|
|
-+ int i;
|
|
|
|
-+
|
|
|
|
-+ mutex_lock(&priv->reg_mutex);
|
|
|
|
-+
|
|
|
|
-+ /* Load search port */
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_ATWD2,
|
|
|
|
-+ FIELD_PREP(AN8855_ATWD2_PORT, BIT(port)));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ goto exit;
|
|
|
|
-+ ret = an8855_fdb_cmd(priv, AN8855_ATC_MAT(AND8855_FDB_MAT_MAC_PORT) |
|
|
|
|
-+ AN8855_FDB_START, &rsp);
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ goto exit;
|
|
|
|
-+
|
|
|
|
-+ do {
|
|
|
|
-+ /* From response get the number of banks to read, exit if 0 */
|
|
|
|
-+ banks = FIELD_GET(AN8855_ATC_HIT, rsp);
|
|
|
|
-+ if (!banks)
|
|
|
|
-+ break;
|
|
|
|
-+
|
|
|
|
-+ /* Each banks have 4 entry */
|
|
|
|
-+ for (i = 0; i < 4; i++) {
|
|
|
|
-+ struct an8855_fdb _fdb = { };
|
|
|
|
-+
|
|
|
|
-+ count++;
|
|
|
|
-+
|
|
|
|
-+ /* Check if bank is present */
|
|
|
|
-+ if (!(banks & BIT(i)))
|
|
|
|
-+ continue;
|
|
|
|
-+
|
|
|
|
-+ /* Select bank entry index */
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_ATRDS,
|
|
|
|
-+ FIELD_PREP(AN8855_ATRD_SEL, i));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ break;
|
|
|
|
-+ /* wait 1ms for the bank entry to be filled */
|
|
|
|
-+ usleep_range(1000, 1500);
|
|
|
|
-+ an8855_fdb_read(priv, &_fdb);
|
|
|
|
-+
|
|
|
|
-+ if (!_fdb.live)
|
|
|
|
-+ continue;
|
|
|
|
-+ ret = cb(_fdb.mac, _fdb.vid, _fdb.noarp, data);
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ break;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ /* Stop if reached max FDB number */
|
|
|
|
-+ if (count >= AN8855_NUM_FDB_RECORDS)
|
|
|
|
-+ break;
|
|
|
|
-+
|
|
|
|
-+ /* Read next bank */
|
|
|
|
-+ ret = an8855_fdb_cmd(priv, AN8855_ATC_MAT(AND8855_FDB_MAT_MAC_PORT) |
|
|
|
|
-+ AN8855_FDB_NEXT, &rsp);
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ break;
|
|
|
|
-+ } while (true);
|
|
|
|
-+
|
|
|
|
-+exit:
|
|
|
|
-+ mutex_unlock(&priv->reg_mutex);
|
|
|
|
-+ return ret;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_vlan_cmd(struct an8855_priv *priv, enum an8855_vlan_cmd cmd,
|
|
|
|
-+ u16 vid) __must_hold(&priv->reg_mutex)
|
|
|
|
-+{
|
|
|
|
-+ u32 val;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ val = AN8855_VTCR_BUSY | FIELD_PREP(AN8855_VTCR_FUNC, cmd) |
|
|
|
|
-+ FIELD_PREP(AN8855_VTCR_VID, vid);
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_VTCR, val);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ return regmap_read_poll_timeout(priv->regmap, AN8855_VTCR, val,
|
|
|
|
-+ !(val & AN8855_VTCR_BUSY), 20, 200000);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_vlan_add(struct an8855_priv *priv, u8 port, u16 vid,
|
|
|
|
-+ bool untagged) __must_hold(&priv->reg_mutex)
|
|
|
|
-+{
|
|
|
|
-+ u32 port_mask;
|
|
|
|
-+ u32 val;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ /* Fetch entry */
|
|
|
|
-+ ret = an8855_vlan_cmd(priv, AN8855_VTCR_RD_VID, vid);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_read(priv->regmap, AN8855_VARD0, &val);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ port_mask = FIELD_GET(AN8855_VA0_PORT, val) | BIT(port);
|
|
|
|
-+
|
|
|
|
-+ /* Validate the entry with independent learning, create egress tag per
|
|
|
|
-+ * VLAN and joining the port as one of the port members.
|
|
|
|
-+ */
|
|
|
|
-+ val = (val & AN8855_VA0_ETAG) | AN8855_VA0_IVL_MAC |
|
|
|
|
-+ AN8855_VA0_VTAG_EN | AN8855_VA0_VLAN_VALID |
|
|
|
|
-+ FIELD_PREP(AN8855_VA0_PORT, port_mask) |
|
|
|
|
-+ FIELD_PREP(AN8855_VA0_FID, AN8855_FID_BRIDGED);
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_VAWD0, val);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_VAWD1, 0);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* CPU port is always taken as a tagged port for serving more than one
|
|
|
|
-+ * VLANs across and also being applied with egress type stack mode for
|
|
|
|
-+ * that VLAN tags would be appended after hardware special tag used as
|
|
|
|
-+ * DSA tag.
|
|
|
|
-+ */
|
|
|
|
-+ if (port == AN8855_CPU_PORT)
|
|
|
|
-+ val = AN8855_VLAN_EGRESS_STACK;
|
|
|
|
-+ /* Decide whether adding tag or not for those outgoing packets from the
|
|
|
|
-+ * port inside the VLAN.
|
|
|
|
-+ */
|
|
|
|
-+ else
|
|
|
|
-+ val = untagged ? AN8855_VLAN_EGRESS_UNTAG : AN8855_VLAN_EGRESS_TAG;
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_VAWD0,
|
|
|
|
-+ AN8855_VA0_ETAG_PORT_MASK(port),
|
|
|
|
-+ AN8855_VA0_ETAG_PORT_VAL(port, val));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Flush result to hardware */
|
|
|
|
-+ return an8855_vlan_cmd(priv, AN8855_VTCR_WR_VID, vid);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_vlan_del(struct an8855_priv *priv, u8 port,
|
|
|
|
-+ u16 vid) __must_hold(&priv->reg_mutex)
|
|
|
|
-+{
|
|
|
|
-+ u32 port_mask;
|
|
|
|
-+ u32 val;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ /* Fetch entry */
|
|
|
|
-+ ret = an8855_vlan_cmd(priv, AN8855_VTCR_RD_VID, vid);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_read(priv->regmap, AN8855_VARD0, &val);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ port_mask = FIELD_GET(AN8855_VA0_PORT, val) & ~BIT(port);
|
|
|
|
-+
|
|
|
|
-+ if (!(val & AN8855_VA0_VLAN_VALID)) {
|
|
|
|
-+ dev_err(priv->dev, "Cannot be deleted due to invalid entry\n");
|
|
|
|
-+ return -EINVAL;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ if (port_mask) {
|
|
|
|
-+ val = (val & AN8855_VA0_ETAG) | AN8855_VA0_IVL_MAC |
|
|
|
|
-+ AN8855_VA0_VTAG_EN | AN8855_VA0_VLAN_VALID |
|
|
|
|
-+ FIELD_PREP(AN8855_VA0_PORT, port_mask);
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_VAWD0, val);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ } else {
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_VAWD0, 0);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_VAWD1, 0);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Flush result to hardware */
|
|
|
|
-+ return an8855_vlan_cmd(priv, AN8855_VTCR_WR_VID, vid);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_port_set_vlan_mode(struct an8855_priv *priv, int port,
|
|
|
|
-+ enum an8855_port_mode port_mode,
|
|
|
|
-+ enum an8855_vlan_port_eg_tag eg_tag,
|
|
|
|
-+ enum an8855_vlan_port_attr vlan_attr,
|
|
|
|
-+ enum an8855_vlan_port_acc_frm acc_frm)
|
|
|
|
-+{
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PCR_P(port),
|
|
|
|
-+ AN8855_PORT_VLAN,
|
|
|
|
-+ FIELD_PREP(AN8855_PORT_VLAN, port_mode));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ return regmap_update_bits(priv->regmap, AN8855_PVC_P(port),
|
|
|
|
-+ AN8855_PVC_EG_TAG | AN8855_VLAN_ATTR | AN8855_ACC_FRM,
|
|
|
|
-+ FIELD_PREP(AN8855_PVC_EG_TAG, eg_tag) |
|
|
|
|
-+ FIELD_PREP(AN8855_VLAN_ATTR, vlan_attr) |
|
|
|
|
-+ FIELD_PREP(AN8855_ACC_FRM, acc_frm));
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_port_set_pid(struct an8855_priv *priv, int port,
|
|
|
|
-+ u16 pid)
|
|
|
|
-+{
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PPBV1_P(port),
|
|
|
|
-+ AN8855_PPBV_G0_PORT_VID,
|
|
|
|
-+ FIELD_PREP(AN8855_PPBV_G0_PORT_VID, pid));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ return regmap_update_bits(priv->regmap, AN8855_PVID_P(port),
|
|
|
|
-+ AN8855_G0_PORT_VID,
|
|
|
|
-+ FIELD_PREP(AN8855_G0_PORT_VID, pid));
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_port_vlan_filtering(struct dsa_switch *ds, int port,
|
|
|
|
-+ bool vlan_filtering,
|
|
|
|
-+ struct netlink_ext_ack *extack)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ u32 val;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ /* The port is being kept as VLAN-unaware port when bridge is
|
|
|
|
-+ * set up with vlan_filtering not being set, Otherwise, the
|
|
|
|
-+ * port and the corresponding CPU port is required the setup
|
|
|
|
-+ * for becoming a VLAN-aware port.
|
|
|
|
-+ */
|
|
|
|
-+ if (vlan_filtering) {
|
|
|
|
-+ u32 acc_frm;
|
|
|
|
-+ /* CPU port is set to fallback mode to let untagged
|
|
|
|
-+ * frames pass through.
|
|
|
|
-+ */
|
|
|
|
-+ ret = an8855_port_set_vlan_mode(priv, AN8855_CPU_PORT,
|
|
|
|
-+ AN8855_PORT_FALLBACK_MODE,
|
|
|
|
-+ AN8855_VLAN_EG_CONSISTENT,
|
|
|
|
-+ AN8855_VLAN_USER,
|
|
|
|
-+ AN8855_VLAN_ACC_ALL);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_read(priv->regmap, AN8855_PVID_P(port), &val);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Only accept tagged frames if PVID is not set */
|
|
|
|
-+ if (FIELD_GET(AN8855_G0_PORT_VID, val) != AN8855_PORT_VID_DEFAULT)
|
|
|
|
-+ acc_frm = AN8855_VLAN_ACC_TAGGED;
|
|
|
|
-+ else
|
|
|
|
-+ acc_frm = AN8855_VLAN_ACC_ALL;
|
|
|
|
-+
|
|
|
|
-+ /* Trapped into security mode allows packet forwarding through VLAN
|
|
|
|
-+ * table lookup.
|
|
|
|
-+ * Set the port as a user port which is to be able to recognize VID
|
|
|
|
-+ * from incoming packets before fetching entry within the VLAN table.
|
|
|
|
-+ */
|
|
|
|
-+ ret = an8855_port_set_vlan_mode(priv, port,
|
|
|
|
-+ AN8855_PORT_SECURITY_MODE,
|
|
|
|
-+ AN8855_VLAN_EG_DISABLED,
|
|
|
|
-+ AN8855_VLAN_USER,
|
|
|
|
-+ acc_frm);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ } else {
|
|
|
|
-+ bool disable_cpu_vlan = true;
|
|
|
|
-+ struct dsa_port *dp;
|
|
|
|
-+ u32 port_mode;
|
|
|
|
-+
|
|
|
|
-+ /* This is called after .port_bridge_leave when leaving a VLAN-aware
|
|
|
|
-+ * bridge. Don't set standalone ports to fallback mode.
|
|
|
|
-+ */
|
|
|
|
-+ if (dsa_port_bridge_dev_get(dsa_to_port(ds, port)))
|
|
|
|
-+ port_mode = AN8855_PORT_FALLBACK_MODE;
|
|
|
|
-+ else
|
|
|
|
-+ port_mode = AN8855_PORT_MATRIX_MODE;
|
|
|
|
-+
|
|
|
|
-+ /* When a port is removed from the bridge, the port would be set up
|
|
|
|
-+ * back to the default as is at initial boot which is a VLAN-unaware
|
|
|
|
-+ * port.
|
|
|
|
-+ */
|
|
|
|
-+ ret = an8855_port_set_vlan_mode(priv, port, port_mode,
|
|
|
|
-+ AN8855_VLAN_EG_CONSISTENT,
|
|
|
|
-+ AN8855_VLAN_TRANSPARENT,
|
|
|
|
-+ AN8855_VLAN_ACC_ALL);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Restore default PVID */
|
|
|
|
-+ ret = an8855_port_set_pid(priv, port, AN8855_PORT_VID_DEFAULT);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ dsa_switch_for_each_user_port(dp, ds) {
|
|
|
|
-+ if (dsa_port_is_vlan_filtering(dp)) {
|
|
|
|
-+ disable_cpu_vlan = false;
|
|
|
|
-+ break;
|
|
|
|
-+ }
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ if (disable_cpu_vlan) {
|
|
|
|
-+ ret = an8855_port_set_vlan_mode(priv, AN8855_CPU_PORT,
|
|
|
|
-+ AN8855_PORT_MATRIX_MODE,
|
|
|
|
-+ AN8855_VLAN_EG_CONSISTENT,
|
|
|
|
-+ AN8855_VLAN_USER,
|
|
|
|
-+ AN8855_VLAN_ACC_ALL);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_port_vlan_add(struct dsa_switch *ds, int port,
|
|
|
|
-+ const struct switchdev_obj_port_vlan *vlan,
|
|
|
|
-+ struct netlink_ext_ack *extack)
|
|
|
|
-+{
|
|
|
|
-+ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
|
|
|
|
-+ bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ u32 val;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ mutex_lock(&priv->reg_mutex);
|
|
|
|
-+ ret = an8855_vlan_add(priv, port, vlan->vid, untagged);
|
|
|
|
-+ mutex_unlock(&priv->reg_mutex);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ if (pvid) {
|
|
|
|
-+ /* Accept all frames if PVID is set */
|
|
|
|
-+ regmap_update_bits(priv->regmap, AN8855_PVC_P(port), AN8855_ACC_FRM,
|
|
|
|
-+ FIELD_PREP(AN8855_ACC_FRM, AN8855_VLAN_ACC_ALL));
|
|
|
|
-+
|
|
|
|
-+ /* Only configure PVID if VLAN filtering is enabled */
|
|
|
|
-+ if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) {
|
|
|
|
-+ ret = an8855_port_set_pid(priv, port, vlan->vid);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+ } else if (vlan->vid) {
|
|
|
|
-+ ret = regmap_read(priv->regmap, AN8855_PVID_P(port), &val);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ if (FIELD_GET(AN8855_G0_PORT_VID, val) != vlan->vid)
|
|
|
|
-+ return 0;
|
|
|
|
-+
|
|
|
|
-+ /* This VLAN is overwritten without PVID, so unset it */
|
|
|
|
-+ if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) {
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PVC_P(port),
|
|
|
|
-+ AN8855_ACC_FRM,
|
|
|
|
-+ FIELD_PREP(AN8855_ACC_FRM,
|
|
|
|
-+ AN8855_VLAN_ACC_TAGGED));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ ret = an8855_port_set_pid(priv, port, AN8855_PORT_VID_DEFAULT);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_port_vlan_del(struct dsa_switch *ds, int port,
|
|
|
|
-+ const struct switchdev_obj_port_vlan *vlan)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ u32 val;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ mutex_lock(&priv->reg_mutex);
|
|
|
|
-+ ret = an8855_vlan_del(priv, port, vlan->vid);
|
|
|
|
-+ mutex_unlock(&priv->reg_mutex);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_read(priv->regmap, AN8855_PVID_P(port), &val);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* PVID is being restored to the default whenever the PVID port
|
|
|
|
-+ * is being removed from the VLAN.
|
|
|
|
-+ */
|
|
|
|
-+ if (FIELD_GET(AN8855_G0_PORT_VID, val) == vlan->vid) {
|
|
|
|
-+ /* Only accept tagged frames if the port is VLAN-aware */
|
|
|
|
-+ if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) {
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PVC_P(port),
|
|
|
|
-+ AN8855_ACC_FRM,
|
|
|
|
-+ FIELD_PREP(AN8855_ACC_FRM,
|
|
|
|
-+ AN8855_VLAN_ACC_TAGGED));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ ret = an8855_port_set_pid(priv, port, AN8855_PORT_VID_DEFAULT);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int
|
|
|
|
-+an8855_port_mdb_add(struct dsa_switch *ds, int port,
|
|
|
|
-+ const struct switchdev_obj_port_mdb *mdb,
|
|
|
|
-+ struct dsa_db db)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ const u8 *addr = mdb->addr;
|
|
|
|
-+ u16 vid = mdb->vid;
|
|
|
|
-+ u8 port_mask = 0;
|
|
|
|
-+ u32 val;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ /* Set the vid to the port vlan id if no vid is set */
|
|
|
|
-+ if (!vid)
|
|
|
|
-+ vid = AN8855_PORT_VID_DEFAULT;
|
|
|
|
-+
|
|
|
|
-+ mutex_lock(&priv->reg_mutex);
|
|
|
|
-+
|
|
|
|
-+ an8855_fdb_write(priv, vid, 0, addr, false);
|
|
|
|
-+ if (!an8855_fdb_cmd(priv, AN8855_FDB_READ, NULL)) {
|
|
|
|
-+ ret = regmap_read(priv->regmap, AN8855_ATRD3, &val);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ goto exit;
|
|
|
|
-+
|
|
|
|
-+ port_mask = FIELD_GET(AN8855_ATRD3_PORTMASK, val);
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ port_mask |= BIT(port);
|
|
|
|
-+ an8855_fdb_write(priv, vid, port_mask, addr, true);
|
|
|
|
-+ ret = an8855_fdb_cmd(priv, AN8855_FDB_WRITE, NULL);
|
|
|
|
-+
|
|
|
|
-+exit:
|
|
|
|
-+ mutex_unlock(&priv->reg_mutex);
|
|
|
|
-+
|
|
|
|
-+ return ret;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int
|
|
|
|
-+an8855_port_mdb_del(struct dsa_switch *ds, int port,
|
|
|
|
-+ const struct switchdev_obj_port_mdb *mdb,
|
|
|
|
-+ struct dsa_db db)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ const u8 *addr = mdb->addr;
|
|
|
|
-+ u16 vid = mdb->vid;
|
|
|
|
-+ u8 port_mask = 0;
|
|
|
|
-+ u32 val;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ /* Set the vid to the port vlan id if no vid is set */
|
|
|
|
-+ if (!vid)
|
|
|
|
-+ vid = AN8855_PORT_VID_DEFAULT;
|
|
|
|
-+
|
|
|
|
-+ mutex_lock(&priv->reg_mutex);
|
|
|
|
-+
|
|
|
|
-+ an8855_fdb_write(priv, vid, 0, addr, 0);
|
|
|
|
-+ if (!an8855_fdb_cmd(priv, AN8855_FDB_READ, NULL)) {
|
|
|
|
-+ ret = regmap_read(priv->regmap, AN8855_ATRD3, &val);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ goto exit;
|
|
|
|
-+
|
|
|
|
-+ port_mask = FIELD_GET(AN8855_ATRD3_PORTMASK, val);
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ port_mask &= ~BIT(port);
|
|
|
|
-+ an8855_fdb_write(priv, vid, port_mask, addr, port_mask ? true : false);
|
|
|
|
-+ ret = an8855_fdb_cmd(priv, AN8855_FDB_WRITE, NULL);
|
|
|
|
-+
|
|
|
|
-+exit:
|
|
|
|
-+ mutex_unlock(&priv->reg_mutex);
|
|
|
|
-+
|
|
|
|
-+ return ret;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int
|
|
|
|
-+an8855_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ int length;
|
|
|
|
-+ u32 val;
|
|
|
|
-+
|
|
|
|
-+ /* When a new MTU is set, DSA always set the CPU port's MTU to the
|
|
|
|
-+ * largest MTU of the slave ports. Because the switch only has a global
|
|
|
|
-+ * RX length register, only allowing CPU port here is enough.
|
|
|
|
-+ */
|
|
|
|
-+ if (!dsa_is_cpu_port(ds, port))
|
|
|
|
-+ return 0;
|
|
|
|
-+
|
|
|
|
-+ /* RX length also includes Ethernet header, MTK tag, and FCS length */
|
|
|
|
-+ length = new_mtu + ETH_HLEN + MTK_TAG_LEN + ETH_FCS_LEN;
|
|
|
|
-+ if (length <= 1522)
|
|
|
|
-+ val = AN8855_MAX_RX_PKT_1518_1522;
|
|
|
|
-+ else if (length <= 1536)
|
|
|
|
-+ val = AN8855_MAX_RX_PKT_1536;
|
|
|
|
-+ else if (length <= 1552)
|
|
|
|
-+ val = AN8855_MAX_RX_PKT_1552;
|
|
|
|
-+ else if (length <= 3072)
|
|
|
|
-+ val = AN8855_MAX_RX_JUMBO_3K;
|
|
|
|
-+ else if (length <= 4096)
|
|
|
|
-+ val = AN8855_MAX_RX_JUMBO_4K;
|
|
|
|
-+ else if (length <= 5120)
|
|
|
|
-+ val = AN8855_MAX_RX_JUMBO_5K;
|
|
|
|
-+ else if (length <= 6144)
|
|
|
|
-+ val = AN8855_MAX_RX_JUMBO_6K;
|
|
|
|
-+ else if (length <= 7168)
|
|
|
|
-+ val = AN8855_MAX_RX_JUMBO_7K;
|
|
|
|
-+ else if (length <= 8192)
|
|
|
|
-+ val = AN8855_MAX_RX_JUMBO_8K;
|
|
|
|
-+ else if (length <= 9216)
|
|
|
|
-+ val = AN8855_MAX_RX_JUMBO_9K;
|
|
|
|
-+ else if (length <= 12288)
|
|
|
|
-+ val = AN8855_MAX_RX_JUMBO_12K;
|
|
|
|
-+ else if (length <= 15360)
|
|
|
|
-+ val = AN8855_MAX_RX_JUMBO_15K;
|
|
|
|
-+ else
|
|
|
|
-+ val = AN8855_MAX_RX_JUMBO_16K;
|
|
|
|
-+
|
|
|
|
-+ /* Enable JUMBO packet */
|
|
|
|
-+ if (length > 1552)
|
|
|
|
-+ val |= AN8855_MAX_RX_PKT_JUMBO;
|
|
|
|
-+
|
|
|
|
-+ return regmap_update_bits(priv->regmap, AN8855_GMACCR,
|
|
|
|
-+ AN8855_MAX_RX_JUMBO | AN8855_MAX_RX_PKT_LEN,
|
|
|
|
-+ val);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int
|
|
|
|
-+an8855_port_max_mtu(struct dsa_switch *ds, int port)
|
|
|
|
-+{
|
|
|
|
-+ return AN8855_MAX_MTU;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void
|
|
|
|
-+an8855_get_strings(struct dsa_switch *ds, int port, u32 stringset,
|
|
|
|
-+ uint8_t *data)
|
|
|
|
-+{
|
|
|
|
-+ int i;
|
|
|
|
-+
|
|
|
|
-+ if (stringset != ETH_SS_STATS)
|
|
|
|
-+ return;
|
|
|
|
-+
|
|
|
|
-+ for (i = 0; i < ARRAY_SIZE(an8855_mib); i++)
|
|
|
|
-+ ethtool_puts(&data, an8855_mib[i].name);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void
|
|
|
|
-+an8855_read_port_stats(struct an8855_priv *priv, int port, u32 offset, u8 size,
|
|
|
|
-+ uint64_t *data)
|
|
|
|
-+{
|
|
|
|
-+ u32 val, reg = AN8855_PORT_MIB_COUNTER(port) + offset;
|
|
|
|
-+
|
|
|
|
-+ regmap_read(priv->regmap, reg, &val);
|
|
|
|
-+ *data = val;
|
|
|
|
-+
|
|
|
|
-+ if (size == 2) {
|
|
|
|
-+ regmap_read(priv->regmap, reg + 4, &val);
|
|
|
|
-+ *data |= (u64)val << 32;
|
|
|
|
-+ }
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void
|
|
|
|
-+an8855_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ const struct an8855_mib_desc *mib;
|
|
|
|
-+ int i;
|
|
|
|
-+
|
|
|
|
-+ for (i = 0; i < ARRAY_SIZE(an8855_mib); i++) {
|
|
|
|
-+ mib = &an8855_mib[i];
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, mib->offset, mib->size,
|
|
|
|
-+ data + i);
|
|
|
|
-+ }
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int
|
|
|
|
-+an8855_get_sset_count(struct dsa_switch *ds, int port, int sset)
|
|
|
|
-+{
|
|
|
|
-+ if (sset != ETH_SS_STATS)
|
|
|
|
-+ return 0;
|
|
|
|
-+
|
|
|
|
-+ return ARRAY_SIZE(an8855_mib);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void
|
|
|
|
-+an8855_get_eth_mac_stats(struct dsa_switch *ds, int port,
|
|
|
|
-+ struct ethtool_eth_mac_stats *mac_stats)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+
|
|
|
|
-+ /* MIB counter doesn't provide a FramesTransmittedOK but instead
|
|
|
|
-+ * provide stats for Unicast, Broadcast and Multicast frames separately.
|
|
|
|
-+ * To simulate a global frame counter, read Unicast and addition Multicast
|
|
|
|
-+ * and Broadcast later
|
|
|
|
-+ */
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_UNICAST, 1,
|
|
|
|
-+ &mac_stats->FramesTransmittedOK);
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_SINGLE_COLLISION, 1,
|
|
|
|
-+ &mac_stats->SingleCollisionFrames);
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_MULTIPLE_COLLISION, 1,
|
|
|
|
-+ &mac_stats->MultipleCollisionFrames);
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_UNICAST, 1,
|
|
|
|
-+ &mac_stats->FramesReceivedOK);
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_BYTES, 2,
|
|
|
|
-+ &mac_stats->OctetsTransmittedOK);
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_ALIGN_ERR, 1,
|
|
|
|
-+ &mac_stats->AlignmentErrors);
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_DEFERRED, 1,
|
|
|
|
-+ &mac_stats->FramesWithDeferredXmissions);
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_LATE_COLLISION, 1,
|
|
|
|
-+ &mac_stats->LateCollisions);
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_EXCESSIVE_COLLISION, 1,
|
|
|
|
-+ &mac_stats->FramesAbortedDueToXSColls);
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_BYTES, 2,
|
|
|
|
-+ &mac_stats->OctetsReceivedOK);
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_MULTICAST, 1,
|
|
|
|
-+ &mac_stats->MulticastFramesXmittedOK);
|
|
|
|
-+ mac_stats->FramesTransmittedOK += mac_stats->MulticastFramesXmittedOK;
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_BROADCAST, 1,
|
|
|
|
-+ &mac_stats->BroadcastFramesXmittedOK);
|
|
|
|
-+ mac_stats->FramesTransmittedOK += mac_stats->BroadcastFramesXmittedOK;
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_MULTICAST, 1,
|
|
|
|
-+ &mac_stats->MulticastFramesReceivedOK);
|
|
|
|
-+ mac_stats->FramesReceivedOK += mac_stats->MulticastFramesReceivedOK;
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_BROADCAST, 1,
|
|
|
|
-+ &mac_stats->BroadcastFramesReceivedOK);
|
|
|
|
-+ mac_stats->FramesReceivedOK += mac_stats->BroadcastFramesReceivedOK;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static const struct ethtool_rmon_hist_range an8855_rmon_ranges[] = {
|
|
|
|
-+ { 0, 64 },
|
|
|
|
-+ { 65, 127 },
|
|
|
|
-+ { 128, 255 },
|
|
|
|
-+ { 256, 511 },
|
|
|
|
-+ { 512, 1023 },
|
|
|
|
-+ { 1024, 1518 },
|
|
|
|
-+ { 1519, AN8855_MAX_MTU },
|
|
|
|
-+ {}
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+static void an8855_get_rmon_stats(struct dsa_switch *ds, int port,
|
|
|
|
-+ struct ethtool_rmon_stats *rmon_stats,
|
|
|
|
-+ const struct ethtool_rmon_hist_range **ranges)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_UNDER_SIZE_ERR, 1,
|
|
|
|
-+ &rmon_stats->undersize_pkts);
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_OVER_SZ_ERR, 1,
|
|
|
|
-+ &rmon_stats->oversize_pkts);
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_FRAG_ERR, 1,
|
|
|
|
-+ &rmon_stats->fragments);
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_JABBER_ERR, 1,
|
|
|
|
-+ &rmon_stats->jabbers);
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_PKT_SZ_64, 1,
|
|
|
|
-+ &rmon_stats->hist[0]);
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_PKT_SZ_65_TO_127, 1,
|
|
|
|
-+ &rmon_stats->hist[1]);
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_PKT_SZ_128_TO_255, 1,
|
|
|
|
-+ &rmon_stats->hist[2]);
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_PKT_SZ_256_TO_511, 1,
|
|
|
|
-+ &rmon_stats->hist[3]);
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_PKT_SZ_512_TO_1023, 1,
|
|
|
|
-+ &rmon_stats->hist[4]);
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_PKT_SZ_1024_TO_1518, 1,
|
|
|
|
-+ &rmon_stats->hist[5]);
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_PKT_SZ_1519_TO_MAX, 1,
|
|
|
|
-+ &rmon_stats->hist[6]);
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_PKT_SZ_64, 1,
|
|
|
|
-+ &rmon_stats->hist_tx[0]);
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_PKT_SZ_65_TO_127, 1,
|
|
|
|
-+ &rmon_stats->hist_tx[1]);
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_PKT_SZ_128_TO_255, 1,
|
|
|
|
-+ &rmon_stats->hist_tx[2]);
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_PKT_SZ_256_TO_511, 1,
|
|
|
|
-+ &rmon_stats->hist_tx[3]);
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_PKT_SZ_512_TO_1023, 1,
|
|
|
|
-+ &rmon_stats->hist_tx[4]);
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_PKT_SZ_1024_TO_1518, 1,
|
|
|
|
-+ &rmon_stats->hist_tx[5]);
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_PKT_SZ_1519_TO_MAX, 1,
|
|
|
|
-+ &rmon_stats->hist_tx[6]);
|
|
|
|
-+
|
|
|
|
-+ *ranges = an8855_rmon_ranges;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void an8855_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
|
|
|
|
-+ struct ethtool_eth_ctrl_stats *ctrl_stats)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_TX_PAUSE, 1,
|
|
|
|
-+ &ctrl_stats->MACControlFramesTransmitted);
|
|
|
|
-+
|
|
|
|
-+ an8855_read_port_stats(priv, port, AN8855_PORT_MIB_RX_PAUSE, 1,
|
|
|
|
-+ &ctrl_stats->MACControlFramesReceived);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_port_mirror_add(struct dsa_switch *ds, int port,
|
|
|
|
-+ struct dsa_mall_mirror_tc_entry *mirror,
|
|
|
|
-+ bool ingress,
|
|
|
|
-+ struct netlink_ext_ack *extack)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ int monitor_port;
|
|
|
|
-+ u32 val;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ /* Check for existent entry */
|
|
|
|
-+ if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port))
|
|
|
|
-+ return -EEXIST;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_read(priv->regmap, AN8855_MIR, &val);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* AN8855 supports 4 monitor port, but only use first group */
|
|
|
|
-+ monitor_port = FIELD_GET(AN8855_MIRROR_PORT, val);
|
|
|
|
-+ if (val & AN8855_MIRROR_EN && monitor_port != mirror->to_local_port)
|
|
|
|
-+ return -EEXIST;
|
|
|
|
-+
|
|
|
|
-+ val = AN8855_MIRROR_EN;
|
|
|
|
-+ val |= FIELD_PREP(AN8855_MIRROR_PORT, mirror->to_local_port);
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_MIR,
|
|
|
|
-+ AN8855_MIRROR_EN | AN8855_MIRROR_PORT,
|
|
|
|
-+ val);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_set_bits(priv->regmap, AN8855_PCR_P(port),
|
|
|
|
-+ ingress ? AN8855_PORT_RX_MIR : AN8855_PORT_TX_MIR);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ if (ingress)
|
|
|
|
-+ priv->mirror_rx |= BIT(port);
|
|
|
|
-+ else
|
|
|
|
-+ priv->mirror_tx |= BIT(port);
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void an8855_port_mirror_del(struct dsa_switch *ds, int port,
|
|
|
|
-+ struct dsa_mall_mirror_tc_entry *mirror)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+
|
|
|
|
-+ if (mirror->ingress)
|
|
|
|
-+ priv->mirror_rx &= ~BIT(port);
|
|
|
|
-+ else
|
|
|
|
-+ priv->mirror_tx &= ~BIT(port);
|
|
|
|
-+
|
|
|
|
-+ regmap_clear_bits(priv->regmap, AN8855_PCR_P(port),
|
|
|
|
-+ mirror->ingress ? AN8855_PORT_RX_MIR :
|
|
|
|
-+ AN8855_PORT_TX_MIR);
|
|
|
|
-+
|
|
|
|
-+ if (!priv->mirror_rx && !priv->mirror_tx)
|
|
|
|
-+ regmap_clear_bits(priv->regmap, AN8855_MIR, AN8855_MIRROR_EN);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_port_set_status(struct an8855_priv *priv, int port,
|
|
|
|
-+ bool enable)
|
|
|
|
-+{
|
|
|
|
-+ if (enable)
|
|
|
|
-+ return regmap_set_bits(priv->regmap, AN8855_PMCR_P(port),
|
|
|
|
-+ AN8855_PMCR_TX_EN | AN8855_PMCR_RX_EN);
|
|
|
|
-+ else
|
|
|
|
-+ return regmap_clear_bits(priv->regmap, AN8855_PMCR_P(port),
|
|
|
|
-+ AN8855_PMCR_TX_EN | AN8855_PMCR_RX_EN);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_port_enable(struct dsa_switch *ds, int port,
|
|
|
|
-+ struct phy_device *phy)
|
|
|
|
-+{
|
|
|
|
-+ return an8855_port_set_status(ds->priv, port, true);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void an8855_port_disable(struct dsa_switch *ds, int port)
|
|
|
|
-+{
|
|
|
|
-+ an8855_port_set_status(ds->priv, port, false);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_set_mac_eee(struct dsa_switch *ds, int port,
|
|
|
|
-+ struct ethtool_eee *eee)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ u32 reg;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ if (eee->eee_enabled) {
|
|
|
|
-+ ret = regmap_read(priv->regmap, AN8855_PMCR_P(port), ®);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ /* Force enable EEE if force mode and LINK */
|
|
|
|
-+ if (reg & AN8855_PMCR_FORCE_MODE &&
|
|
|
|
-+ reg & AN8855_PMCR_FORCE_LNK) {
|
|
|
|
-+ switch (reg & AN8855_PMCR_FORCE_SPEED) {
|
|
|
|
-+ case AN8855_PMCR_FORCE_SPEED_1000:
|
|
|
|
-+ reg |= AN8855_PMCR_FORCE_EEE1G;
|
|
|
|
-+ break;
|
|
|
|
-+ case AN8855_PMCR_FORCE_SPEED_100:
|
|
|
|
-+ reg |= AN8855_PMCR_FORCE_EEE100;
|
|
|
|
-+ break;
|
|
|
|
-+ default:
|
|
|
|
-+ break;
|
|
|
|
-+ }
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_PMCR_P(port), reg);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PMEEECR_P(port),
|
|
|
|
-+ AN8855_LPI_MODE_EN,
|
|
|
|
-+ eee->tx_lpi_enabled ? AN8855_LPI_MODE_EN : 0);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ } else {
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_PMCR_P(port),
|
|
|
|
-+ AN8855_PMCR_FORCE_EEE1G |
|
|
|
|
-+ AN8855_PMCR_FORCE_EEE100);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_PMEEECR_P(port),
|
|
|
|
-+ AN8855_LPI_MODE_EN);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_get_mac_eee(struct dsa_switch *ds, int port,
|
|
|
|
-+ struct ethtool_eee *eee)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ u32 reg;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_read(priv->regmap, AN8855_PMEEECR_P(port), ®);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ eee->tx_lpi_enabled = reg & AN8855_LPI_MODE_EN;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_read(priv->regmap, AN8855_CKGCR, ®);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ /* Global LPI TXIDLE Threshold, default 60ms (unit 2us) */
|
|
|
|
-+ eee->tx_lpi_timer = FIELD_GET(AN8855_LPI_TXIDLE_THD_MASK, reg) / 500;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_read(priv->regmap, AN8855_PMSR_P(port), ®);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static u32 en8855_get_phy_flags(struct dsa_switch *ds, int port)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+
|
|
|
|
-+ /* PHY doesn't need calibration */
|
|
|
|
-+ if (!priv->phy_require_calib)
|
|
|
|
-+ return 0;
|
|
|
|
-+
|
|
|
|
-+ /* Use AN8855_PHY_FLAGS_EN_CALIBRATION to signal
|
|
|
|
-+ * calibration needed.
|
|
|
|
-+ */
|
|
|
|
-+ return AN8855_PHY_FLAGS_EN_CALIBRATION;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static enum dsa_tag_protocol
|
|
|
|
-+an8855_get_tag_protocol(struct dsa_switch *ds, int port,
|
|
|
|
-+ enum dsa_tag_protocol mp)
|
|
|
|
-+{
|
|
|
|
-+ return DSA_TAG_PROTO_MTK;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_phy_read(struct mii_bus *bus, int phy, int regnum)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = bus->priv;
|
|
|
|
-+
|
|
|
|
-+ return mdiobus_read_nested(priv->bus, phy, regnum);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_phy_write(struct mii_bus *bus, int phy, int regnum, u16 val)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = bus->priv;
|
|
|
|
-+
|
|
|
|
-+ return mdiobus_write_nested(priv->bus, phy, regnum, val);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_mdio_setup(struct an8855_priv *priv)
|
|
|
|
-+{
|
|
|
|
-+ struct dsa_switch *ds = priv->ds;
|
|
|
|
-+ struct device *dev = priv->dev;
|
|
|
|
-+ struct device_node *np;
|
|
|
|
-+ struct mii_bus *bus;
|
|
|
|
-+ int ret = 0;
|
|
|
|
-+
|
|
|
|
-+ np = of_get_child_by_name(priv->dev->of_node, "mdio");
|
|
|
|
-+ if (!np || !of_device_is_available(np))
|
|
|
|
-+ goto exit;
|
|
|
|
-+
|
|
|
|
-+ bus = devm_mdiobus_alloc(priv->dev);
|
|
|
|
-+ if (!bus) {
|
|
|
|
-+ ret = -ENOMEM;
|
|
|
|
-+ goto exit;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ bus->priv = priv;
|
|
|
|
-+ bus->name = KBUILD_MODNAME "-mii";
|
|
|
|
-+ snprintf(bus->id, MII_BUS_ID_SIZE, KBUILD_MODNAME "-%d.%d",
|
|
|
|
-+ ds->dst->index, ds->index);
|
|
|
|
-+ bus->parent = dev;
|
|
|
|
-+ bus->read = an8855_phy_read;
|
|
|
|
-+ bus->write = an8855_phy_write;
|
|
|
|
-+
|
|
|
|
-+ ret = devm_of_mdiobus_register(dev, bus, np);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ dev_err(dev, "failed to register MDIO bus: %d", ret);
|
|
|
|
-+
|
|
|
|
-+exit:
|
|
|
|
-+ of_node_put(np);
|
|
|
|
-+ return ret;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int
|
|
|
|
-+an8855_setup_pvid_vlan(struct an8855_priv *priv)
|
|
|
|
-+{
|
|
|
|
-+ u32 val;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ /* Validate the entry with independent learning, keep the original
|
|
|
|
-+ * ingress tag attribute.
|
|
|
|
-+ */
|
|
|
|
-+ val = AN8855_VA0_IVL_MAC | AN8855_VA0_EG_CON |
|
|
|
|
-+ FIELD_PREP(AN8855_VA0_FID, AN8855_FID_BRIDGED) |
|
|
|
|
-+ AN8855_VA0_PORT | AN8855_VA0_VLAN_VALID;
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_VAWD0, val);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ return an8855_vlan_cmd(priv, AN8855_VTCR_WR_VID,
|
|
|
|
-+ AN8855_PORT_VID_DEFAULT);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_setup(struct dsa_switch *ds)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = ds->priv;
|
|
|
|
-+ struct dsa_port *dp;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ /* Setup mdio BUS for internal PHY */
|
|
|
|
-+ ret = an8855_mdio_setup(priv);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Enable and reset MIB counters */
|
|
|
|
-+ ret = an8855_mib_init(priv);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ dsa_switch_for_each_user_port(dp, ds) {
|
|
|
|
-+ /* Disable MAC by default on all user ports */
|
|
|
|
-+ ret = an8855_port_set_status(priv, dp->index, false);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Individual user ports get connected to CPU port only */
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_PORTMATRIX_P(dp->index),
|
|
|
|
-+ FIELD_PREP(AN8855_PORTMATRIX, BIT(AN8855_CPU_PORT)));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Disable Learning on user ports */
|
|
|
|
-+ ret = regmap_set_bits(priv->regmap, AN8855_PSC_P(dp->index),
|
|
|
|
-+ AN8855_SA_DIS);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Disable Broadcast Forward on user ports */
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_BCF, BIT(dp->index));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Disable Unknown Unicast Forward on user ports */
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_UNUF, BIT(dp->index));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Disable Unknown Multicast Forward on user ports */
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_UNMF, BIT(dp->index));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Set default PVID to on all user ports */
|
|
|
|
-+ ret = an8855_port_set_pid(priv, dp->index, AN8855_PORT_VID_DEFAULT);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ /* Enable Airoha header mode on the cpu port */
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_PVC_P(AN8855_CPU_PORT),
|
|
|
|
-+ AN8855_PORT_SPEC_REPLACE_MODE | AN8855_PORT_SPEC_TAG);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Unknown multicast frame forwarding to the cpu port */
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_UNMF, BIT(AN8855_CPU_PORT));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Set CPU port number */
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_MFC,
|
|
|
|
-+ AN8855_CPU_EN | AN8855_CPU_PORT_IDX,
|
|
|
|
-+ AN8855_CPU_EN |
|
|
|
|
-+ FIELD_PREP(AN8855_CPU_PORT_IDX, AN8855_CPU_PORT));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* CPU port gets connected to all user ports of
|
|
|
|
-+ * the switch.
|
|
|
|
-+ */
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_PORTMATRIX_P(AN8855_CPU_PORT),
|
|
|
|
-+ FIELD_PREP(AN8855_PORTMATRIX, dsa_user_ports(ds)));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* CPU port is set to fallback mode to let untagged
|
|
|
|
-+ * frames pass through.
|
|
|
|
-+ */
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PCR_P(AN8855_CPU_PORT),
|
|
|
|
-+ AN8855_PORT_VLAN,
|
|
|
|
-+ FIELD_PREP(AN8855_PORT_VLAN, AN8855_PORT_FALLBACK_MODE));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Enable Learning on CPU port */
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_PSC_P(AN8855_CPU_PORT), AN8855_SA_DIS);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Enable Broadcast Forward on CPU port */
|
|
|
|
-+ ret = regmap_set_bits(priv->regmap, AN8855_BCF, BIT(AN8855_CPU_PORT));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Enable Unknown Unicast Forward on CPU port */
|
|
|
|
-+ ret = regmap_set_bits(priv->regmap, AN8855_UNUF, BIT(AN8855_CPU_PORT));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Enable Unknown Multicast Forward on CPU port */
|
|
|
|
-+ ret = regmap_set_bits(priv->regmap, AN8855_UNMF, BIT(AN8855_CPU_PORT));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* BPDU to CPU port */
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_BPC, AN8855_BPDU_PORT_FW,
|
|
|
|
-+ FIELD_PREP(AN8855_BPDU_PORT_FW, AN8855_BPDU_CPU_ONLY));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ dsa_switch_for_each_port(dp, ds) {
|
|
|
|
-+ /* Enable consistent egress tag (for VLAN unware VLAN-passtrough) */
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PVC_P(dp->index),
|
|
|
|
-+ AN8855_PVC_EG_TAG,
|
|
|
|
-+ FIELD_PREP(AN8855_PVC_EG_TAG, AN8855_VLAN_EG_CONSISTENT));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ /* Setup VLAN for Default PVID */
|
|
|
|
-+ ret = an8855_setup_pvid_vlan(priv);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_CKGCR,
|
|
|
|
-+ AN8855_CKG_LNKDN_GLB_STOP | AN8855_CKG_LNKDN_PORT_STOP);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Release global PHY power down */
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_RG_GPHY_AFE_PWD, 0x0);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ds->configure_vlan_while_not_filtering = true;
|
|
|
|
-+
|
|
|
|
-+ /* Flush the FDB table */
|
|
|
|
-+ ret = an8855_fdb_cmd(priv, AN8855_FDB_FLUSH, NULL);
|
|
|
|
-+ if (ret < 0)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Set min a max ageing value supported */
|
|
|
|
-+ ds->ageing_time_min = AN8855_L2_AGING_MS_CONSTANT;
|
|
|
|
-+ ds->ageing_time_max = FIELD_MAX(AN8855_AGE_CNT) *
|
|
|
|
-+ FIELD_MAX(AN8855_AGE_UNIT) *
|
|
|
|
-+ AN8855_L2_AGING_MS_CONSTANT;
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static struct phylink_pcs *
|
|
|
|
-+an8855_phylink_mac_select_pcs(struct phylink_config *config,
|
|
|
|
-+ phy_interface_t interface)
|
|
|
|
-+{
|
|
|
|
-+ struct dsa_port *dp = dsa_phylink_to_port(config);
|
|
|
|
-+ struct an8855_priv *priv = dp->ds->priv;
|
|
|
|
-+
|
|
|
|
-+ switch (interface) {
|
|
|
|
-+ case PHY_INTERFACE_MODE_SGMII:
|
|
|
|
-+ case PHY_INTERFACE_MODE_2500BASEX:
|
|
|
|
-+ return &priv->pcs;
|
|
|
|
-+ default:
|
|
|
|
-+ return NULL;
|
|
|
|
-+ }
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void
|
|
|
|
-+an8855_phylink_mac_config(struct phylink_config *config, unsigned int mode,
|
|
|
|
-+ const struct phylink_link_state *state)
|
|
|
|
-+{
|
|
|
|
-+ struct dsa_port *dp = dsa_phylink_to_port(config);
|
|
|
|
-+ struct dsa_switch *ds = dp->ds;
|
|
|
|
-+ struct an8855_priv *priv;
|
|
|
|
-+ int port = dp->index;
|
|
|
|
-+
|
|
|
|
-+ priv = ds->priv;
|
|
|
|
-+
|
|
|
|
-+ if (port != 5) {
|
|
|
|
-+ if (port > 5)
|
|
|
|
-+ dev_err(ds->dev, "unsupported port: %d", port);
|
|
|
|
-+ return;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ regmap_update_bits(priv->regmap, AN8855_PMCR_P(port),
|
|
|
|
-+ AN8855_PMCR_IFG_XMIT | AN8855_PMCR_MAC_MODE |
|
|
|
|
-+ AN8855_PMCR_BACKOFF_EN | AN8855_PMCR_BACKPR_EN,
|
|
|
|
-+ FIELD_PREP(AN8855_PMCR_IFG_XMIT, 0x1) |
|
|
|
|
-+ AN8855_PMCR_MAC_MODE | AN8855_PMCR_BACKOFF_EN |
|
|
|
|
-+ AN8855_PMCR_BACKPR_EN);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void an8855_phylink_get_caps(struct dsa_switch *ds, int port,
|
|
|
|
-+ struct phylink_config *config)
|
|
|
|
-+{
|
|
|
|
-+ switch (port) {
|
|
|
|
-+ case 0:
|
|
|
|
-+ case 1:
|
|
|
|
-+ case 2:
|
|
|
|
-+ case 3:
|
|
|
|
-+ case 4:
|
|
|
|
-+ __set_bit(PHY_INTERFACE_MODE_GMII,
|
|
|
|
-+ config->supported_interfaces);
|
|
|
|
-+ __set_bit(PHY_INTERFACE_MODE_INTERNAL,
|
|
|
|
-+ config->supported_interfaces);
|
|
|
|
-+ break;
|
|
|
|
-+ case 5:
|
|
|
|
-+ phy_interface_set_rgmii(config->supported_interfaces);
|
|
|
|
-+ __set_bit(PHY_INTERFACE_MODE_SGMII,
|
|
|
|
-+ config->supported_interfaces);
|
|
|
|
-+ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
|
|
|
|
-+ config->supported_interfaces);
|
|
|
|
-+ break;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
|
|
|
|
-+ MAC_10 | MAC_100 | MAC_1000FD;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void
|
|
|
|
-+an8855_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
|
|
|
|
-+ phy_interface_t interface)
|
|
|
|
-+{
|
|
|
|
-+ struct dsa_port *dp = dsa_phylink_to_port(config);
|
|
|
|
-+ struct an8855_priv *priv = dp->ds->priv;
|
|
|
|
-+
|
|
|
|
-+ /* With autoneg just disable TX/RX else also force link down */
|
|
|
|
-+ if (phylink_autoneg_inband(mode)) {
|
|
|
|
-+ regmap_clear_bits(priv->regmap, AN8855_PMCR_P(dp->index),
|
|
|
|
-+ AN8855_PMCR_TX_EN | AN8855_PMCR_RX_EN);
|
|
|
|
-+ } else {
|
|
|
|
-+ regmap_update_bits(priv->regmap, AN8855_PMCR_P(dp->index),
|
|
|
|
-+ AN8855_PMCR_TX_EN | AN8855_PMCR_RX_EN |
|
|
|
|
-+ AN8855_PMCR_FORCE_MODE | AN8855_PMCR_FORCE_LNK,
|
|
|
|
-+ AN8855_PMCR_FORCE_MODE);
|
|
|
|
-+ }
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void
|
|
|
|
-+an8855_phylink_mac_link_up(struct phylink_config *config,
|
|
|
|
-+ struct phy_device *phydev, unsigned int mode,
|
|
|
|
-+ phy_interface_t interface, int speed, int duplex,
|
|
|
|
-+ bool tx_pause, bool rx_pause)
|
|
|
|
-+{
|
|
|
|
-+ struct dsa_port *dp = dsa_phylink_to_port(config);
|
|
|
|
-+ struct an8855_priv *priv = dp->ds->priv;
|
|
|
|
-+ int port = dp->index;
|
|
|
|
-+ u32 reg;
|
|
|
|
-+
|
|
|
|
-+ reg = regmap_read(priv->regmap, AN8855_PMCR_P(port), ®);
|
|
|
|
-+ if (phylink_autoneg_inband(mode)) {
|
|
|
|
-+ reg &= ~AN8855_PMCR_FORCE_MODE;
|
|
|
|
-+ } else {
|
|
|
|
-+ reg |= AN8855_PMCR_FORCE_MODE | AN8855_PMCR_FORCE_LNK;
|
|
|
|
-+
|
|
|
|
-+ reg &= ~AN8855_PMCR_FORCE_SPEED;
|
|
|
|
-+ switch (speed) {
|
|
|
|
-+ case SPEED_10:
|
|
|
|
-+ reg |= AN8855_PMCR_FORCE_SPEED_10;
|
|
|
|
-+ break;
|
|
|
|
-+ case SPEED_100:
|
|
|
|
-+ reg |= AN8855_PMCR_FORCE_SPEED_100;
|
|
|
|
-+ break;
|
|
|
|
-+ case SPEED_1000:
|
|
|
|
-+ reg |= AN8855_PMCR_FORCE_SPEED_1000;
|
|
|
|
-+ break;
|
|
|
|
-+ case SPEED_2500:
|
|
|
|
-+ reg |= AN8855_PMCR_FORCE_SPEED_2500;
|
|
|
|
-+ break;
|
|
|
|
-+ case SPEED_5000:
|
|
|
|
-+ reg |= AN8855_PMCR_FORCE_SPEED_5000;
|
|
|
|
-+ break;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ reg &= ~AN8855_PMCR_FORCE_FDX;
|
|
|
|
-+ if (duplex == DUPLEX_FULL)
|
|
|
|
-+ reg |= AN8855_PMCR_FORCE_FDX;
|
|
|
|
-+
|
|
|
|
-+ reg &= ~AN8855_PMCR_RX_FC_EN;
|
|
|
|
-+ if (rx_pause || dsa_port_is_cpu(dp))
|
|
|
|
-+ reg |= AN8855_PMCR_RX_FC_EN;
|
|
|
|
-+
|
|
|
|
-+ reg &= ~AN8855_PMCR_TX_FC_EN;
|
|
|
|
-+ if (rx_pause || dsa_port_is_cpu(dp))
|
|
|
|
-+ reg |= AN8855_PMCR_TX_FC_EN;
|
|
|
|
-+
|
|
|
|
-+ /* Disable any EEE options */
|
|
|
|
-+ reg &= ~(AN8855_PMCR_FORCE_EEE5G | AN8855_PMCR_FORCE_EEE2P5G |
|
|
|
|
-+ AN8855_PMCR_FORCE_EEE1G | AN8855_PMCR_FORCE_EEE100);
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ reg |= AN8855_PMCR_TX_EN | AN8855_PMCR_RX_EN;
|
|
|
|
-+
|
|
|
|
-+ regmap_write(priv->regmap, AN8855_PMCR_P(port), reg);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void an8855_pcs_get_state(struct phylink_pcs *pcs,
|
|
|
|
-+ struct phylink_link_state *state)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = container_of(pcs, struct an8855_priv, pcs);
|
|
|
|
-+ u32 val;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_read(priv->regmap, AN8855_PMSR_P(AN8855_CPU_PORT), &val);
|
|
|
|
-+ if (ret < 0) {
|
|
|
|
-+ state->link = false;
|
|
|
|
-+ return;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ state->link = !!(val & AN8855_PMSR_LNK);
|
|
|
|
-+ state->an_complete = state->link;
|
|
|
|
-+ state->duplex = (val & AN8855_PMSR_DPX) ? DUPLEX_FULL :
|
|
|
|
-+ DUPLEX_HALF;
|
|
|
|
-+
|
|
|
|
-+ switch (val & AN8855_PMSR_SPEED) {
|
|
|
|
-+ case AN8855_PMSR_SPEED_10:
|
|
|
|
-+ state->speed = SPEED_10;
|
|
|
|
-+ break;
|
|
|
|
-+ case AN8855_PMSR_SPEED_100:
|
|
|
|
-+ state->speed = SPEED_100;
|
|
|
|
-+ break;
|
|
|
|
-+ case AN8855_PMSR_SPEED_1000:
|
|
|
|
-+ state->speed = SPEED_1000;
|
|
|
|
-+ break;
|
|
|
|
-+ case AN8855_PMSR_SPEED_2500:
|
|
|
|
-+ state->speed = SPEED_2500;
|
|
|
|
-+ break;
|
|
|
|
-+ case AN8855_PMSR_SPEED_5000:
|
|
|
|
-+ state->speed = SPEED_5000;
|
|
|
|
-+ break;
|
|
|
|
-+ default:
|
|
|
|
-+ state->speed = SPEED_UNKNOWN;
|
|
|
|
-+ break;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ if (val & AN8855_PMSR_RX_FC)
|
|
|
|
-+ state->pause |= MLO_PAUSE_RX;
|
|
|
|
-+ if (val & AN8855_PMSR_TX_FC)
|
|
|
|
-+ state->pause |= MLO_PAUSE_TX;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
|
|
|
-+ phy_interface_t interface,
|
|
|
|
-+ const unsigned long *advertising,
|
|
|
|
-+ bool permit_pause_to_mac)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = container_of(pcs, struct an8855_priv, pcs);
|
|
|
|
-+ u32 val;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ switch (interface) {
|
|
|
|
-+ case PHY_INTERFACE_MODE_SGMII:
|
|
|
|
-+ break;
|
|
|
|
-+ case PHY_INTERFACE_MODE_2500BASEX:
|
|
|
|
-+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
|
|
|
-+ dev_err(priv->dev, "in-band negotiation unsupported");
|
|
|
|
-+ return -EINVAL;
|
|
|
|
-+ }
|
|
|
|
-+ break;
|
|
|
|
-+ default:
|
|
|
|
-+ return -EINVAL;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ /* !!! WELCOME TO HELL !!! */
|
|
|
|
-+
|
|
|
|
-+ /* TX FIR - improve TX EYE */
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_INTF_CTRL_10,
|
|
|
|
-+ AN8855_RG_DA_QP_TX_FIR_C2_SEL |
|
|
|
|
-+ AN8855_RG_DA_QP_TX_FIR_C2_FORCE |
|
|
|
|
-+ AN8855_RG_DA_QP_TX_FIR_C1_SEL |
|
|
|
|
-+ AN8855_RG_DA_QP_TX_FIR_C1_FORCE,
|
|
|
|
-+ AN8855_RG_DA_QP_TX_FIR_C2_SEL |
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_TX_FIR_C2_FORCE, 0x4) |
|
|
|
|
-+ AN8855_RG_DA_QP_TX_FIR_C1_SEL |
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_TX_FIR_C1_FORCE, 0x0));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
|
|
|
|
-+ val = 0x0;
|
|
|
|
-+ else
|
|
|
|
-+ val = 0xd;
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_INTF_CTRL_11,
|
|
|
|
-+ AN8855_RG_DA_QP_TX_FIR_C0B_SEL |
|
|
|
|
-+ AN8855_RG_DA_QP_TX_FIR_C0B_FORCE,
|
|
|
|
-+ AN8855_RG_DA_QP_TX_FIR_C0B_SEL |
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_TX_FIR_C0B_FORCE, val));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* RX CDR - improve RX Jitter Tolerance */
|
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
|
|
|
|
-+ val = 0x5;
|
|
|
|
-+ else
|
|
|
|
-+ val = 0x6;
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RG_QP_CDR_LPF_BOT_LIM,
|
|
|
|
-+ AN8855_RG_QP_CDR_LPF_KP_GAIN |
|
|
|
|
-+ AN8855_RG_QP_CDR_LPF_KI_GAIN,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_CDR_LPF_KP_GAIN, val) |
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_CDR_LPF_KI_GAIN, val));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* PLL */
|
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
|
|
|
|
-+ val = 0x1;
|
|
|
|
-+ else
|
|
|
|
-+ val = 0x0;
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_QP_DIG_MODE_CTRL_1,
|
|
|
|
-+ AN8855_RG_TPHY_SPEED,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_TPHY_SPEED, val));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* PLL - LPF */
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PLL_CTRL_2,
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_RICO_SEL_INTF |
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_FBKSEL_INTF |
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_BR_INTF |
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_BPD_INTF |
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_BPA_INTF |
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_BC_INTF,
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_RICO_SEL_INTF |
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_PLL_FBKSEL_INTF, 0x0) |
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_PLL_BR_INTF, 0x3) |
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_PLL_BPD_INTF, 0x0) |
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_PLL_BPA_INTF, 0x5) |
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_PLL_BC_INTF, 0x1));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* PLL - ICO */
|
|
|
|
-+ ret = regmap_set_bits(priv->regmap, AN8855_PLL_CTRL_4,
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_ICOLP_EN_INTF);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_PLL_CTRL_2,
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_ICOIQ_EN_INTF);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* PLL - CHP */
|
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
|
|
|
|
-+ val = 0x6;
|
|
|
|
-+ else
|
|
|
|
-+ val = 0x4;
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PLL_CTRL_2,
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_IR_INTF,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_PLL_IR_INTF, val));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* PLL - PFD */
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PLL_CTRL_2,
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_PFD_OFFSET_EN_INTRF |
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_PFD_OFFSET_INTF |
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_KBAND_PREDIV_INTF,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_PLL_PFD_OFFSET_INTF, 0x1) |
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_PLL_KBAND_PREDIV_INTF, 0x1));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* PLL - POSTDIV */
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PLL_CTRL_2,
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_POSTDIV_EN_INTF |
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_PHY_CK_EN_INTF |
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_PCK_SEL_INTF,
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_PCK_SEL_INTF);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* PLL - SDM */
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PLL_CTRL_2,
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_SDM_HREN_INTF,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_PLL_SDM_HREN_INTF, 0x0));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_PLL_CTRL_2,
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_SDM_IFM_INTF);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_SS_LCPLL_PWCTL_SETTING_2,
|
|
|
|
-+ AN8855_RG_NCPO_ANA_MSB,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_NCPO_ANA_MSB, 0x1));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
|
|
|
|
-+ val = 0x7a000000;
|
|
|
|
-+ else
|
|
|
|
-+ val = 0x48000000;
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_SS_LCPLL_TDC_FLT_2,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_LCPLL_NCPO_VALUE, val));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_SS_LCPLL_TDC_PCW_1,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_LCPLL_PON_HRDDS_PCW_NCPO_GPON, val));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_SS_LCPLL_TDC_FLT_5,
|
|
|
|
-+ AN8855_RG_LCPLL_NCPO_CHG);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_PLL_CK_CTRL_0,
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_SDM_DI_EN_INTF);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* PLL - SS */
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PLL_CTRL_3,
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_SSC_DELTA_INTF,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_PLL_SSC_DELTA_INTF, 0x0));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PLL_CTRL_4,
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_SSC_DIR_DLY_INTF,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_PLL_SSC_DIR_DLY_INTF, 0x0));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PLL_CTRL_3,
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_SSC_PERIOD_INTF,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_PLL_SSC_PERIOD_INTF, 0x0));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* PLL - TDC */
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_PLL_CK_CTRL_0,
|
|
|
|
-+ AN8855_RG_DA_QP_PLL_TDC_TXCK_SEL_INTF);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_set_bits(priv->regmap, AN8855_RG_QP_PLL_SDM_ORD,
|
|
|
|
-+ AN8855_RG_QP_PLL_SSC_TRI_EN);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ ret = regmap_set_bits(priv->regmap, AN8855_RG_QP_PLL_SDM_ORD,
|
|
|
|
-+ AN8855_RG_QP_PLL_SSC_PHASE_INI);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RG_QP_RX_DAC_EN,
|
|
|
|
-+ AN8855_RG_QP_SIGDET_HF,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_SIGDET_HF, 0x2));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* TCL Disable (only for Co-SIM) */
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_PON_RXFEDIG_CTRL_0,
|
|
|
|
-+ AN8855_RG_QP_EQ_RX500M_CK_SEL);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* TX Init */
|
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
|
|
|
|
-+ val = 0x4;
|
|
|
|
-+ else
|
|
|
|
-+ val = 0x0;
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RG_QP_TX_MODE,
|
|
|
|
-+ AN8855_RG_QP_TX_RESERVE |
|
|
|
|
-+ AN8855_RG_QP_TX_MODE_16B_EN,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_TX_RESERVE, val));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* RX Control/Init */
|
|
|
|
-+ ret = regmap_set_bits(priv->regmap, AN8855_RG_QP_RXAFE_RESERVE,
|
|
|
|
-+ AN8855_RG_QP_CDR_PD_10B_EN);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
|
|
|
|
-+ val = 0x1;
|
|
|
|
-+ else
|
|
|
|
-+ val = 0x2;
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RG_QP_CDR_LPF_MJV_LIM,
|
|
|
|
-+ AN8855_RG_QP_CDR_LPF_RATIO,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_CDR_LPF_RATIO, val));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RG_QP_CDR_LPF_SETVALUE,
|
|
|
|
-+ AN8855_RG_QP_CDR_PR_BUF_IN_SR |
|
|
|
|
-+ AN8855_RG_QP_CDR_PR_BETA_SEL,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_CDR_PR_BUF_IN_SR, 0x6) |
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_CDR_PR_BETA_SEL, 0x1));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
|
|
|
|
-+ val = 0xf;
|
|
|
|
-+ else
|
|
|
|
-+ val = 0xc;
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RG_QP_CDR_PR_CKREF_DIV1,
|
|
|
|
-+ AN8855_RG_QP_CDR_PR_DAC_BAND,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_CDR_PR_DAC_BAND, val));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE,
|
|
|
|
-+ AN8855_RG_QP_CDR_PR_KBAND_PCIE_MODE |
|
|
|
|
-+ AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE_MASK,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE_MASK, 0x19));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RG_QP_CDR_FORCE_IBANDLPF_R_OFF,
|
|
|
|
-+ AN8855_RG_QP_CDR_PHYCK_SEL |
|
|
|
|
-+ AN8855_RG_QP_CDR_PHYCK_RSTB |
|
|
|
|
-+ AN8855_RG_QP_CDR_PHYCK_DIV,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_CDR_PHYCK_SEL, 0x2) |
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_CDR_PHYCK_DIV, 0x21));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE,
|
|
|
|
-+ AN8855_RG_QP_CDR_PR_XFICK_EN);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RG_QP_CDR_PR_CKREF_DIV1,
|
|
|
|
-+ AN8855_RG_QP_CDR_PR_KBAND_DIV,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_CDR_PR_KBAND_DIV, 0x4));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RX_CTRL_26,
|
|
|
|
-+ AN8855_RG_QP_EQ_RETRAIN_ONLY_EN |
|
|
|
|
-+ AN8855_RG_LINK_NE_EN |
|
|
|
|
-+ AN8855_RG_LINK_ERRO_EN,
|
|
|
|
-+ AN8855_RG_QP_EQ_RETRAIN_ONLY_EN |
|
|
|
|
-+ AN8855_RG_LINK_ERRO_EN);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RX_DLY_0,
|
|
|
|
-+ AN8855_RG_QP_RX_SAOSC_EN_H_DLY |
|
|
|
|
-+ AN8855_RG_QP_RX_PI_CAL_EN_H_DLY,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_RX_SAOSC_EN_H_DLY, 0x3f) |
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_RX_PI_CAL_EN_H_DLY, 0x6f));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RX_CTRL_42,
|
|
|
|
-+ AN8855_RG_QP_EQ_EN_DLY,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_EQ_EN_DLY, 0x150));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RX_CTRL_2,
|
|
|
|
-+ AN8855_RG_QP_RX_EQ_EN_H_DLY,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_RX_EQ_EN_H_DLY, 0x150));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_PON_RXFEDIG_CTRL_9,
|
|
|
|
-+ AN8855_RG_QP_EQ_LEQOSC_DLYCNT,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_QP_EQ_LEQOSC_DLYCNT, 0x1));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RX_CTRL_8,
|
|
|
|
-+ AN8855_RG_DA_QP_SAOSC_DONE_TIME |
|
|
|
|
-+ AN8855_RG_DA_QP_LEQOS_EN_TIME,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_SAOSC_DONE_TIME, 0x200) |
|
|
|
|
-+ FIELD_PREP(AN8855_RG_DA_QP_LEQOS_EN_TIME, 0xfff));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Frequency meter */
|
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
|
|
|
|
-+ val = 0x10;
|
|
|
|
-+ else
|
|
|
|
-+ val = 0x28;
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RX_CTRL_5,
|
|
|
|
-+ AN8855_RG_FREDET_CHK_CYCLE,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_FREDET_CHK_CYCLE, val));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RX_CTRL_6,
|
|
|
|
-+ AN8855_RG_FREDET_GOLDEN_CYCLE,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_FREDET_GOLDEN_CYCLE, 0x64));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RX_CTRL_7,
|
|
|
|
-+ AN8855_RG_FREDET_TOLERATE_CYCLE,
|
|
|
|
-+ FIELD_PREP(AN8855_RG_FREDET_TOLERATE_CYCLE, 0x2710));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_set_bits(priv->regmap, AN8855_PLL_CTRL_0,
|
|
|
|
-+ AN8855_RG_PHYA_AUTO_INIT);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* PCS Init */
|
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_SGMII &&
|
|
|
|
-+ neg_mode == PHYLINK_PCS_NEG_INBAND_DISABLED) {
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_QP_DIG_MODE_CTRL_0,
|
|
|
|
-+ AN8855_RG_SGMII_MODE | AN8855_RG_SGMII_AN_EN);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_RG_HSGMII_PCS_CTROL_1,
|
|
|
|
-+ AN8855_RG_TBI_10B_MODE);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
|
|
|
-+ /* Set AN Ability - Interrupt */
|
|
|
|
-+ ret = regmap_set_bits(priv->regmap, AN8855_SGMII_REG_AN_FORCE_CL37,
|
|
|
|
-+ AN8855_RG_FORCE_AN_DONE);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_SGMII_REG_AN_13,
|
|
|
|
-+ AN8855_SGMII_REMOTE_FAULT_DIS |
|
|
|
|
-+ AN8855_SGMII_IF_MODE,
|
|
|
|
-+ AN8855_SGMII_REMOTE_FAULT_DIS |
|
|
|
|
-+ FIELD_PREP(AN8855_SGMII_IF_MODE, 0xb));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ /* Rate Adaption - GMII path config. */
|
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_2500BASEX) {
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_RATE_ADP_P0_CTRL_0,
|
|
|
|
-+ AN8855_RG_P0_DIS_MII_MODE);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ } else {
|
|
|
|
-+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
|
|
|
-+ ret = regmap_set_bits(priv->regmap, AN8855_MII_RA_AN_ENABLE,
|
|
|
|
-+ AN8855_RG_P0_RA_AN_EN);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ } else {
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_RG_AN_SGMII_MODE_FORCE,
|
|
|
|
-+ AN8855_RG_FORCE_CUR_SGMII_MODE |
|
|
|
|
-+ AN8855_RG_FORCE_CUR_SGMII_SEL,
|
|
|
|
-+ AN8855_RG_FORCE_CUR_SGMII_SEL);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_clear_bits(priv->regmap, AN8855_RATE_ADP_P0_CTRL_0,
|
|
|
|
-+ AN8855_RG_P0_MII_RA_RX_EN |
|
|
|
|
-+ AN8855_RG_P0_MII_RA_TX_EN |
|
|
|
|
-+ AN8855_RG_P0_MII_RA_RX_MODE |
|
|
|
|
-+ AN8855_RG_P0_MII_RA_TX_MODE);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_set_bits(priv->regmap, AN8855_RATE_ADP_P0_CTRL_0,
|
|
|
|
-+ AN8855_RG_P0_MII_MODE);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_set_bits(priv->regmap, AN8855_RG_RATE_ADAPT_CTRL_0,
|
|
|
|
-+ AN8855_RG_RATE_ADAPT_RX_BYPASS |
|
|
|
|
-+ AN8855_RG_RATE_ADAPT_TX_BYPASS |
|
|
|
|
-+ AN8855_RG_RATE_ADAPT_RX_EN |
|
|
|
|
-+ AN8855_RG_RATE_ADAPT_TX_EN);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Disable AN if not in autoneg */
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_SGMII_REG_AN0, BMCR_ANENABLE,
|
|
|
|
-+ neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED ? BMCR_ANENABLE :
|
|
|
|
-+ 0);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_SGMII &&
|
|
|
|
-+ neg_mode == PHYLINK_PCS_NEG_INBAND_DISABLED) {
|
|
|
|
-+ ret = regmap_set_bits(priv->regmap, AN8855_PHY_RX_FORCE_CTRL_0,
|
|
|
|
-+ AN8855_RG_FORCE_TXC_SEL);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ /* Force Speed with fixed-link or 2500base-x as doesn't support aneg */
|
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_2500BASEX ||
|
|
|
|
-+ neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
|
|
|
|
-+ val = AN8855_RG_LINK_MODE_P0_SPEED_2500;
|
|
|
|
-+ else
|
|
|
|
-+ val = AN8855_RG_LINK_MODE_P0_SPEED_1000;
|
|
|
|
-+ ret = regmap_update_bits(priv->regmap, AN8855_SGMII_STS_CTRL_0,
|
|
|
|
-+ AN8855_RG_LINK_MODE_P0 |
|
|
|
|
-+ AN8855_RG_FORCE_SPD_MODE_P0,
|
|
|
|
-+ val | AN8855_RG_FORCE_SPD_MODE_P0);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ /* bypass flow control to MAC */
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_MSG_RX_LIK_STS_0,
|
|
|
|
-+ AN8855_RG_DPX_STS_P3 | AN8855_RG_DPX_STS_P2 |
|
|
|
|
-+ AN8855_RG_DPX_STS_P1 | AN8855_RG_TXFC_STS_P0 |
|
|
|
|
-+ AN8855_RG_RXFC_STS_P0 | AN8855_RG_DPX_STS_P0);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ ret = regmap_write(priv->regmap, AN8855_MSG_RX_LIK_STS_2,
|
|
|
|
-+ AN8855_RG_RXFC_AN_BYPASS_P3 |
|
|
|
|
-+ AN8855_RG_RXFC_AN_BYPASS_P2 |
|
|
|
|
-+ AN8855_RG_RXFC_AN_BYPASS_P1 |
|
|
|
|
-+ AN8855_RG_TXFC_AN_BYPASS_P3 |
|
|
|
|
-+ AN8855_RG_TXFC_AN_BYPASS_P2 |
|
|
|
|
-+ AN8855_RG_TXFC_AN_BYPASS_P1 |
|
|
|
|
-+ AN8855_RG_DPX_AN_BYPASS_P3 |
|
|
|
|
-+ AN8855_RG_DPX_AN_BYPASS_P2 |
|
|
|
|
-+ AN8855_RG_DPX_AN_BYPASS_P1 |
|
|
|
|
-+ AN8855_RG_DPX_AN_BYPASS_P0);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static void an8855_pcs_an_restart(struct phylink_pcs *pcs)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = container_of(pcs, struct an8855_priv, pcs);
|
|
|
|
-+
|
|
|
|
-+ regmap_set_bits(priv->regmap, AN8855_SGMII_REG_AN0, BMCR_ANRESTART);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static const struct phylink_pcs_ops an8855_pcs_ops = {
|
|
|
|
-+ .pcs_get_state = an8855_pcs_get_state,
|
|
|
|
-+ .pcs_config = an8855_pcs_config,
|
|
|
|
-+ .pcs_an_restart = an8855_pcs_an_restart,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+static const struct phylink_mac_ops an8855_phylink_mac_ops = {
|
|
|
|
-+ .mac_select_pcs = an8855_phylink_mac_select_pcs,
|
|
|
|
-+ .mac_config = an8855_phylink_mac_config,
|
|
|
|
-+ .mac_link_down = an8855_phylink_mac_link_down,
|
|
|
|
-+ .mac_link_up = an8855_phylink_mac_link_up,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+static const struct dsa_switch_ops an8855_switch_ops = {
|
|
|
|
-+ .get_tag_protocol = an8855_get_tag_protocol,
|
|
|
|
-+ .setup = an8855_setup,
|
|
|
|
-+ .get_phy_flags = en8855_get_phy_flags,
|
|
|
|
-+ .phylink_get_caps = an8855_phylink_get_caps,
|
|
|
|
-+ .get_strings = an8855_get_strings,
|
|
|
|
-+ .get_ethtool_stats = an8855_get_ethtool_stats,
|
|
|
|
-+ .get_sset_count = an8855_get_sset_count,
|
|
|
|
-+ .get_eth_mac_stats = an8855_get_eth_mac_stats,
|
|
|
|
-+ .get_eth_ctrl_stats = an8855_get_eth_ctrl_stats,
|
|
|
|
-+ .get_rmon_stats = an8855_get_rmon_stats,
|
|
|
|
-+ .port_enable = an8855_port_enable,
|
|
|
|
-+ .port_disable = an8855_port_disable,
|
|
|
|
-+ .get_mac_eee = an8855_get_mac_eee,
|
|
|
|
-+ .set_mac_eee = an8855_set_mac_eee,
|
|
|
|
-+ .set_ageing_time = an8855_set_ageing_time,
|
|
|
|
-+ .port_bridge_join = an8855_port_bridge_join,
|
|
|
|
-+ .port_bridge_leave = an8855_port_bridge_leave,
|
|
|
|
-+ .port_fast_age = an8855_port_fast_age,
|
|
|
|
-+ .port_stp_state_set = an8855_port_stp_state_set,
|
|
|
|
-+ .port_pre_bridge_flags = an8855_port_pre_bridge_flags,
|
|
|
|
-+ .port_bridge_flags = an8855_port_bridge_flags,
|
|
|
|
-+ .port_vlan_filtering = an8855_port_vlan_filtering,
|
|
|
|
-+ .port_vlan_add = an8855_port_vlan_add,
|
|
|
|
-+ .port_vlan_del = an8855_port_vlan_del,
|
|
|
|
-+ .port_fdb_add = an8855_port_fdb_add,
|
|
|
|
-+ .port_fdb_del = an8855_port_fdb_del,
|
|
|
|
-+ .port_fdb_dump = an8855_port_fdb_dump,
|
|
|
|
-+ .port_mdb_add = an8855_port_mdb_add,
|
|
|
|
-+ .port_mdb_del = an8855_port_mdb_del,
|
|
|
|
-+ .port_change_mtu = an8855_port_change_mtu,
|
|
|
|
-+ .port_max_mtu = an8855_port_max_mtu,
|
|
|
|
-+ .port_mirror_add = an8855_port_mirror_add,
|
|
|
|
-+ .port_mirror_del = an8855_port_mirror_del,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+static int an8855_read_switch_id(struct an8855_priv *priv)
|
|
|
|
-+{
|
|
|
|
-+ u32 id;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ ret = regmap_read(priv->regmap, AN8855_CREV, &id);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ if (id != AN8855_ID) {
|
|
|
|
-+ dev_err(priv->dev,
|
|
|
|
-+ "Switch id detected %x but expected %x",
|
|
|
|
-+ id, AN8855_ID);
|
|
|
|
-+ return -ENODEV;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_efuse_read(void *context, unsigned int offset,
|
|
|
|
-+ void *val, size_t bytes)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv = context;
|
|
|
|
-+
|
|
|
|
-+ return regmap_bulk_read(priv->regmap, AN8855_EFUSE_DATA0 + offset,
|
|
|
|
-+ val, bytes / sizeof(u32));
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static struct nvmem_config an8855_nvmem_config = {
|
|
|
|
-+ .name = "an8855-efuse",
|
|
|
|
-+ .size = AN8855_EFUSE_CELL * sizeof(u32),
|
|
|
|
-+ .stride = sizeof(u32),
|
|
|
|
-+ .word_size = sizeof(u32),
|
|
|
|
-+ .reg_read = an8855_efuse_read,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+static int an8855_sw_register_nvmem(struct an8855_priv *priv)
|
|
|
|
-+{
|
|
|
|
-+ struct nvmem_device *nvmem;
|
|
|
|
-+
|
|
|
|
-+ an8855_nvmem_config.priv = priv;
|
|
|
|
-+ an8855_nvmem_config.dev = priv->dev;
|
|
|
|
-+ nvmem = devm_nvmem_register(priv->dev, &an8855_nvmem_config);
|
|
|
|
-+ if (IS_ERR(nvmem))
|
|
|
|
-+ return PTR_ERR(nvmem);
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int
|
|
|
|
-+an8855_sw_probe(struct mdio_device *mdiodev)
|
|
|
|
-+{
|
|
|
|
-+ struct an8855_priv *priv;
|
|
|
|
-+ u32 val;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
|
|
|
|
-+ if (!priv)
|
|
|
|
-+ return -ENOMEM;
|
|
|
|
-+
|
|
|
|
-+ priv->bus = mdiodev->bus;
|
|
|
|
-+ priv->dev = &mdiodev->dev;
|
|
|
|
-+ priv->phy_base = mdiodev->addr;
|
|
|
|
-+ priv->phy_require_calib = of_property_read_bool(priv->dev->of_node,
|
|
|
|
-+ "airoha,ext-surge");
|
|
|
|
-+
|
|
|
|
-+ priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset",
|
|
|
|
-+ GPIOD_OUT_LOW);
|
|
|
|
-+ if (IS_ERR(priv->reset_gpio))
|
|
|
|
-+ return PTR_ERR(priv->reset_gpio);
|
|
|
|
-+
|
|
|
|
-+ priv->regmap = devm_regmap_init(priv->dev, NULL, priv,
|
|
|
|
-+ &an8855_regmap_config);
|
|
|
|
-+ if (IS_ERR(priv->regmap)) {
|
|
|
|
-+ dev_err(priv->dev, "regmap initialization failed");
|
|
|
|
-+ return PTR_ERR(priv->regmap);
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ if (priv->reset_gpio) {
|
|
|
|
-+ usleep_range(100000, 150000);
|
|
|
|
-+ gpiod_set_value_cansleep(priv->reset_gpio, 0);
|
|
|
|
-+ usleep_range(100000, 150000);
|
|
|
|
-+ gpiod_set_value_cansleep(priv->reset_gpio, 1);
|
|
|
|
-+
|
|
|
|
-+ /* Poll HWTRAP reg to wait for Switch to fully Init */
|
|
|
|
-+ ret = regmap_read_poll_timeout(priv->regmap, AN8855_HWTRAP, val,
|
|
|
|
-+ val, 20, 200000);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ ret = an8855_read_switch_id(priv);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ priv->ds = devm_kzalloc(priv->dev, sizeof(*priv->ds), GFP_KERNEL);
|
|
|
|
-+ if (!priv->ds)
|
|
|
|
-+ return -ENOMEM;
|
|
|
|
-+
|
|
|
|
-+ priv->ds->dev = priv->dev;
|
|
|
|
-+ priv->ds->num_ports = AN8855_NUM_PORTS;
|
|
|
|
-+ priv->ds->priv = priv;
|
|
|
|
-+ priv->ds->ops = &an8855_switch_ops;
|
|
|
|
-+ devm_mutex_init(priv->dev, &priv->reg_mutex);
|
|
|
|
-+ priv->ds->phylink_mac_ops = &an8855_phylink_mac_ops;
|
|
|
|
-+
|
|
|
|
-+ priv->pcs.ops = &an8855_pcs_ops;
|
|
|
|
-+ priv->pcs.neg_mode = true;
|
|
|
|
-+ priv->pcs.poll = true;
|
|
|
|
-+
|
|
|
|
-+ ret = an8855_sw_register_nvmem(priv);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ dev_set_drvdata(priv->dev, priv);
|
|
|
|
-+
|
|
|
|
-+ return devm_dsa_register_switch(priv->dev, priv->ds);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static const struct of_device_id an8855_of_match[] = {
|
|
|
|
-+ { .compatible = "airoha,an8855" },
|
|
|
|
-+ { /* sentinel */ }
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+static struct mdio_driver an8855_mdio_driver = {
|
|
|
|
-+ .probe = an8855_sw_probe,
|
|
|
|
-+ .mdiodrv.driver = {
|
|
|
|
-+ .name = "an8855",
|
|
|
|
-+ .of_match_table = an8855_of_match,
|
|
|
|
-+ },
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+mdio_module_driver(an8855_mdio_driver);
|
|
|
|
-+
|
|
|
|
-+MODULE_AUTHOR("Min Yao <[email protected]>");
|
|
|
|
-+MODULE_AUTHOR("Christian Marangi <[email protected]>");
|
|
|
|
-+MODULE_DESCRIPTION("Driver for Airoha AN8855 Switch");
|
|
|
|
-+MODULE_LICENSE("GPL");
|
|
|
|
-diff --git a/target/linux/mediatek/files-6.6/drivers/net/dsa/an8855.h b/target/linux/mediatek/files-6.6/drivers/net/dsa/an8855.h
|
|
|
|
-new file mode 100644
|
|
|
|
-index 00000000000000..632c0e511bc374
|
|
|
|
---- /dev/null
|
|
|
|
-+++ b/target/linux/mediatek/files-6.6/drivers/net/dsa/an8855.h
|
|
|
|
-@@ -0,0 +1,753 @@
|
|
|
|
-+/* SPDX-License-Identifier: GPL-2.0-only */
|
|
|
|
-+/*
|
|
|
|
-+ * Copyright (C) 2023 Min Yao <[email protected]>
|
|
|
|
-+ * Copyright (C) 2024 Christian Marangi <[email protected]>
|
|
|
|
-+ */
|
|
|
|
-+
|
|
|
|
-+#ifndef __AN8855_H
|
|
|
|
-+#define __AN8855_H
|
|
|
|
-+
|
|
|
|
-+#include <linux/bitfield.h>
|
|
|
|
-+
|
|
|
|
-+#define AN8855_NUM_PORTS 6
|
|
|
|
-+#define AN8855_CPU_PORT 5
|
|
|
|
-+#define AN8855_NUM_FDB_RECORDS 2048
|
|
|
|
-+#define AN8855_GPHY_SMI_ADDR_DEFAULT 1
|
|
|
|
-+#define AN8855_PORT_VID_DEFAULT 0
|
|
|
|
-+#define AN8855_EFUSE_CELL 50
|
|
|
|
-+
|
|
|
|
-+#define MTK_TAG_LEN 4
|
|
|
|
-+#define AN8855_MAX_MTU (15360 - ETH_HLEN - ETH_FCS_LEN - MTK_TAG_LEN)
|
|
|
|
-+
|
|
|
|
-+#define AN8855_L2_AGING_MS_CONSTANT 1024
|
|
|
|
-+
|
|
|
|
-+#define AN8855_PHY_FLAGS_EN_CALIBRATION BIT(0)
|
|
|
|
-+
|
|
|
|
-+/* MII Registers */
|
|
|
|
-+#define AN8855_PHY_SELECT_PAGE 0x1f
|
|
|
|
-+#define AN8855_PHY_PAGE GENMASK(2, 0)
|
|
|
|
-+#define AN8855_PHY_PAGE_STANDARD FIELD_PREP_CONST(AN8855_PHY_PAGE, 0x0)
|
|
|
|
-+#define AN8855_PHY_PAGE_EXTENDED_1 FIELD_PREP_CONST(AN8855_PHY_PAGE, 0x1)
|
|
|
|
-+#define AN8855_PHY_PAGE_EXTENDED_4 FIELD_PREP_CONST(AN8855_PHY_PAGE, 0x4)
|
|
|
|
-+
|
|
|
|
-+/* MII Registers Page 4 */
|
|
|
|
-+#define AN8855_PBUS_MODE 0x10
|
|
|
|
-+#define AN8855_PBUS_MODE_ADDR_FIXED 0x0
|
|
|
|
-+#define AN8855_PBUS_MODE_ADDR_INCR BIT(15)
|
|
|
|
-+#define AN8855_PBUS_WR_ADDR_HIGH 0x11
|
|
|
|
-+#define AN8855_PBUS_WR_ADDR_LOW 0x12
|
|
|
|
-+#define AN8855_PBUS_WR_DATA_HIGH 0x13
|
|
|
|
-+#define AN8855_PBUS_WR_DATA_LOW 0x14
|
|
|
|
-+#define AN8855_PBUS_RD_ADDR_HIGH 0x15
|
|
|
|
-+#define AN8855_PBUS_RD_ADDR_LOW 0x16
|
|
|
|
-+#define AN8855_PBUS_RD_DATA_HIGH 0x17
|
|
|
|
-+#define AN8855_PBUS_RD_DATA_LOW 0x18
|
|
|
|
-+
|
|
|
|
-+/* AN8855_SCU 0x10000000 */
|
|
|
|
-+#define AN8855_RG_GPIO_LED_MODE 0x10000054
|
|
|
|
-+#define AN8855_RG_GPIO_LED_SEL(i) (0x10000000 + (0x0058 + ((i) * 4)))
|
|
|
|
-+#define AN8855_RG_INTB_MODE 0x10000080
|
|
|
|
-+#define AN8855_RG_RGMII_TXCK_C 0x100001d0
|
|
|
|
-+
|
|
|
|
-+#define AN8855_PKG_SEL 0x10000094
|
|
|
|
-+#define AN8855_PAG_SEL_AN8855H 0x2
|
|
|
|
-+
|
|
|
|
-+/* Register for hw trap status */
|
|
|
|
-+#define AN8855_HWTRAP 0x1000009c
|
|
|
|
-+
|
|
|
|
-+#define AN8855_RG_GPIO_L_INV 0x10000010
|
|
|
|
-+#define AN8855_RG_GPIO_CTRL 0x1000a300
|
|
|
|
-+#define AN8855_RG_GPIO_DATA 0x1000a304
|
|
|
|
-+#define AN8855_RG_GPIO_OE 0x1000a314
|
|
|
|
-+
|
|
|
|
-+#define AN8855_EFUSE_DATA0 0x1000a500
|
|
|
|
-+#define AN8855_EFUSE_R50O GENMASK(30, 24)
|
|
|
|
-+
|
|
|
|
-+#define AN8855_CREV 0x10005000
|
|
|
|
-+#define AN8855_ID 0x8855
|
|
|
|
-+
|
|
|
|
-+/* Register for system reset */
|
|
|
|
-+#define AN8855_RST_CTRL 0x100050c0
|
|
|
|
-+#define AN8855_SYS_CTRL_SYS_RST BIT(31)
|
|
|
|
-+
|
|
|
|
-+#define AN8855_INT_MASK 0x100050f0
|
|
|
|
-+#define AN8855_INT_SYS BIT(15)
|
|
|
|
-+
|
|
|
|
-+#define AN8855_RG_CLK_CPU_ICG 0x10005034
|
|
|
|
-+#define AN8855_MCU_ENABLE BIT(3)
|
|
|
|
-+
|
|
|
|
-+#define AN8855_RG_TIMER_CTL 0x1000a100
|
|
|
|
-+#define AN8855_WDOG_ENABLE BIT(25)
|
|
|
|
-+
|
|
|
|
-+#define AN8855_RG_GDMP_RAM 0x10010000
|
|
|
|
-+
|
|
|
|
-+/* Registers to mac forward control for unknown frames */
|
|
|
|
-+#define AN8855_MFC 0x10200010
|
|
|
|
-+#define AN8855_CPU_EN BIT(15)
|
|
|
|
-+#define AN8855_CPU_PORT_IDX GENMASK(12, 8)
|
|
|
|
-+
|
|
|
|
-+#define AN8855_AAC 0x102000a0
|
|
|
|
-+#define AN8855_MAC_AUTO_FLUSH BIT(28)
|
|
|
|
-+/* Control Address Table Age time.
|
|
|
|
-+ * (AN8855_AGE_CNT + 1) * ( AN8855_AGE_UNIT + 1 ) * AN8855_L2_AGING_MS_CONSTANT
|
|
|
|
-+ */
|
|
|
|
-+#define AN8855_AGE_CNT GENMASK(20, 12)
|
|
|
|
-+/* Value in seconds. Value is always incremented of 1 */
|
|
|
|
-+#define AN8855_AGE_UNIT GENMASK(10, 0)
|
|
|
|
-+
|
|
|
|
-+/* Registers for ARL Unknown Unicast Forward control */
|
|
|
|
-+#define AN8855_UNUF 0x102000b4
|
|
|
|
-+
|
|
|
|
-+/* Registers for ARL Unknown Multicast Forward control */
|
|
|
|
-+#define AN8855_UNMF 0x102000b8
|
|
|
|
-+
|
|
|
|
-+/* Registers for ARL Broadcast forward control */
|
|
|
|
-+#define AN8855_BCF 0x102000bc
|
|
|
|
-+
|
|
|
|
-+/* Registers for port address age disable */
|
|
|
|
-+#define AN8855_AGDIS 0x102000c0
|
|
|
|
-+
|
|
|
|
-+/* Registers for mirror port control */
|
|
|
|
-+#define AN8855_MIR 0x102000cc
|
|
|
|
-+#define AN8855_MIRROR_EN BIT(7)
|
|
|
|
-+#define AN8855_MIRROR_PORT GENMASK(4, 0)
|
|
|
|
-+
|
|
|
|
-+/* Registers for BPDU and PAE frame control*/
|
|
|
|
-+#define AN8855_BPC 0x102000D0
|
|
|
|
-+#define AN8855_BPDU_PORT_FW GENMASK(2, 0)
|
|
|
|
-+
|
|
|
|
-+enum an8855_bpdu_port_fw {
|
|
|
|
-+ AN8855_BPDU_FOLLOW_MFC = 0,
|
|
|
|
-+ AN8855_BPDU_CPU_EXCLUDE = 4,
|
|
|
|
-+ AN8855_BPDU_CPU_INCLUDE = 5,
|
|
|
|
-+ AN8855_BPDU_CPU_ONLY = 6,
|
|
|
|
-+ AN8855_BPDU_DROP = 7,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+/* Register for address table control */
|
|
|
|
-+#define AN8855_ATC 0x10200300
|
|
|
|
-+#define AN8855_ATC_BUSY BIT(31)
|
|
|
|
-+#define AN8855_ATC_HASH GENMASK(24, 16)
|
|
|
|
-+#define AN8855_ATC_HIT GENMASK(15, 12)
|
|
|
|
-+#define AN8855_ATC_MAT_MASK GENMASK(11, 7)
|
|
|
|
-+#define AN8855_ATC_MAT(x) FIELD_PREP(AN8855_ATC_MAT_MASK, x)
|
|
|
|
-+#define AN8855_ATC_SAT GENMASK(5, 4)
|
|
|
|
-+#define AN8855_ATC_CMD GENMASK(2, 0)
|
|
|
|
-+
|
|
|
|
-+enum an8855_fdb_mat_cmds {
|
|
|
|
-+ AND8855_FDB_MAT_ALL = 0,
|
|
|
|
-+ AND8855_FDB_MAT_MAC, /* All MAC address */
|
|
|
|
-+ AND8855_FDB_MAT_DYNAMIC_MAC, /* All Dynamic MAC address */
|
|
|
|
-+ AND8855_FDB_MAT_STATIC_MAC, /* All Static Mac Address */
|
|
|
|
-+ AND8855_FDB_MAT_DIP, /* All DIP/GA address */
|
|
|
|
-+ AND8855_FDB_MAT_DIP_IPV4, /* All DIP/GA IPv4 address */
|
|
|
|
-+ AND8855_FDB_MAT_DIP_IPV6, /* All DIP/GA IPv6 address */
|
|
|
|
-+ AND8855_FDB_MAT_DIP_SIP, /* All DIP_SIP address */
|
|
|
|
-+ AND8855_FDB_MAT_DIP_SIP_IPV4, /* All DIP_SIP IPv4 address */
|
|
|
|
-+ AND8855_FDB_MAT_DIP_SIP_IPV6, /* All DIP_SIP IPv6 address */
|
|
|
|
-+ AND8855_FDB_MAT_MAC_CVID, /* All MAC address with CVID */
|
|
|
|
-+ AND8855_FDB_MAT_MAC_FID, /* All MAC address with Filter ID */
|
|
|
|
-+ AND8855_FDB_MAT_MAC_PORT, /* All MAC address with port */
|
|
|
|
-+ AND8855_FDB_MAT_DIP_SIP_DIP_IPV4, /* All DIP_SIP address with DIP_IPV4 */
|
|
|
|
-+ AND8855_FDB_MAT_DIP_SIP_SIP_IPV4, /* All DIP_SIP address with SIP_IPV4 */
|
|
|
|
-+ AND8855_FDB_MAT_DIP_SIP_DIP_IPV6, /* All DIP_SIP address with DIP_IPV6 */
|
|
|
|
-+ AND8855_FDB_MAT_DIP_SIP_SIP_IPV6, /* All DIP_SIP address with SIP_IPV6 */
|
|
|
|
-+ /* All MAC address with MAC type (dynamic or static) with CVID */
|
|
|
|
-+ AND8855_FDB_MAT_MAC_TYPE_CVID,
|
|
|
|
-+ /* All MAC address with MAC type (dynamic or static) with Filter ID */
|
|
|
|
-+ AND8855_FDB_MAT_MAC_TYPE_FID,
|
|
|
|
-+ /* All MAC address with MAC type (dynamic or static) with port */
|
|
|
|
-+ AND8855_FDB_MAT_MAC_TYPE_PORT,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+enum an8855_fdb_cmds {
|
|
|
|
-+ AN8855_FDB_READ = 0,
|
|
|
|
-+ AN8855_FDB_WRITE = 1,
|
|
|
|
-+ AN8855_FDB_FLUSH = 2,
|
|
|
|
-+ AN8855_FDB_START = 4,
|
|
|
|
-+ AN8855_FDB_NEXT = 5,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+/* Registers for address table access */
|
|
|
|
-+#define AN8855_ATA1 0x10200304
|
|
|
|
-+#define AN8855_ATA1_MAC0 GENMASK(31, 24)
|
|
|
|
-+#define AN8855_ATA1_MAC1 GENMASK(23, 16)
|
|
|
|
-+#define AN8855_ATA1_MAC2 GENMASK(15, 8)
|
|
|
|
-+#define AN8855_ATA1_MAC3 GENMASK(7, 0)
|
|
|
|
-+#define AN8855_ATA2 0x10200308
|
|
|
|
-+#define AN8855_ATA2_MAC4 GENMASK(31, 24)
|
|
|
|
-+#define AN8855_ATA2_MAC5 GENMASK(23, 16)
|
|
|
|
-+#define AN8855_ATA2_UNAUTH BIT(10)
|
|
|
|
-+#define AN8855_ATA2_TYPE BIT(9) /* 1: dynamic, 0: static */
|
|
|
|
-+#define AN8855_ATA2_AGE GENMASK(8, 0)
|
|
|
|
-+
|
|
|
|
-+/* Register for address table write data */
|
|
|
|
-+#define AN8855_ATWD 0x10200324
|
|
|
|
-+#define AN8855_ATWD_FID GENMASK(31, 28)
|
|
|
|
-+#define AN8855_ATWD_VID GENMASK(27, 16)
|
|
|
|
-+#define AN8855_ATWD_IVL BIT(15)
|
|
|
|
-+#define AN8855_ATWD_EG_TAG GENMASK(14, 12)
|
|
|
|
-+#define AN8855_ATWD_SA_MIR GENMASK(9, 8)
|
|
|
|
-+#define AN8855_ATWD_SA_FWD GENMASK(7, 5)
|
|
|
|
-+#define AN8855_ATWD_UPRI GENMASK(4, 2)
|
|
|
|
-+#define AN8855_ATWD_LEAKY BIT(1)
|
|
|
|
-+#define AN8855_ATWD_VLD BIT(0) /* vid LOAD */
|
|
|
|
-+#define AN8855_ATWD2 0x10200328
|
|
|
|
-+#define AN8855_ATWD2_PORT GENMASK(7, 0)
|
|
|
|
-+
|
|
|
|
-+/* Registers for table search read address */
|
|
|
|
-+#define AN8855_ATRDS 0x10200330
|
|
|
|
-+#define AN8855_ATRD_SEL GENMASK(1, 0)
|
|
|
|
-+#define AN8855_ATRD0 0x10200334
|
|
|
|
-+#define AN8855_ATRD0_FID GENMASK(28, 25)
|
|
|
|
-+#define AN8855_ATRD0_VID GENMASK(21, 10)
|
|
|
|
-+#define AN8855_ATRD0_IVL BIT(9)
|
|
|
|
-+#define AN8855_ATRD0_TYPE GENMASK(4, 3)
|
|
|
|
-+#define AN8855_ATRD0_ARP GENMASK(2, 1)
|
|
|
|
-+#define AN8855_ATRD0_LIVE BIT(0)
|
|
|
|
-+#define AN8855_ATRD1 0x10200338
|
|
|
|
-+#define AN8855_ATRD1_MAC4 GENMASK(31, 24)
|
|
|
|
-+#define AN8855_ATRD1_MAC5 GENMASK(23, 16)
|
|
|
|
-+#define AN8855_ATRD1_AGING GENMASK(10, 3)
|
|
|
|
-+#define AN8855_ATRD2 0x1020033c
|
|
|
|
-+#define AN8855_ATRD2_MAC0 GENMASK(31, 24)
|
|
|
|
-+#define AN8855_ATRD2_MAC1 GENMASK(23, 16)
|
|
|
|
-+#define AN8855_ATRD2_MAC2 GENMASK(15, 8)
|
|
|
|
-+#define AN8855_ATRD2_MAC3 GENMASK(7, 0)
|
|
|
|
-+#define AN8855_ATRD3 0x10200340
|
|
|
|
-+#define AN8855_ATRD3_PORTMASK GENMASK(7, 0)
|
|
|
|
-+
|
|
|
|
-+enum an8855_fdb_type {
|
|
|
|
-+ AN8855_MAC_TB_TY_MAC = 0,
|
|
|
|
-+ AN8855_MAC_TB_TY_DIP = 1,
|
|
|
|
-+ AN8855_MAC_TB_TY_DIP_SIP = 2,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+/* Register for vlan table control */
|
|
|
|
-+#define AN8855_VTCR 0x10200600
|
|
|
|
-+#define AN8855_VTCR_BUSY BIT(31)
|
|
|
|
-+#define AN8855_VTCR_FUNC GENMASK(15, 12)
|
|
|
|
-+#define AN8855_VTCR_VID GENMASK(11, 0)
|
|
|
|
-+
|
|
|
|
-+enum an8855_vlan_cmd {
|
|
|
|
-+ /* Read/Write the specified VID entry from VAWD register based
|
|
|
|
-+ * on VID.
|
|
|
|
-+ */
|
|
|
|
-+ AN8855_VTCR_RD_VID = 0,
|
|
|
|
-+ AN8855_VTCR_WR_VID = 1,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+/* Register for setup vlan write data */
|
|
|
|
-+#define AN8855_VAWD0 0x10200604
|
|
|
|
-+/* VLAN Member Control */
|
|
|
|
-+#define AN8855_VA0_PORT GENMASK(31, 26)
|
|
|
|
-+/* Egress Tag Control */
|
|
|
|
-+#define AN8855_VA0_ETAG GENMASK(23, 12)
|
|
|
|
-+#define AN8855_VA0_ETAG_PORT GENMASK(13, 12)
|
|
|
|
-+#define AN8855_VA0_ETAG_PORT_SHIFT(port) ((port) * 2)
|
|
|
|
-+#define AN8855_VA0_ETAG_PORT_MASK(port) (AN8855_VA0_ETAG_PORT << \
|
|
|
|
-+ AN8855_VA0_ETAG_PORT_SHIFT(port))
|
|
|
|
-+#define AN8855_VA0_ETAG_PORT_VAL(port, val) (FIELD_PREP(AN8855_VA0_ETAG_PORT, (val)) << \
|
|
|
|
-+ AN8855_VA0_ETAG_PORT_SHIFT(port))
|
|
|
|
-+#define AN8855_VA0_EG_CON BIT(11)
|
|
|
|
-+#define AN8855_VA0_VTAG_EN BIT(10) /* Per VLAN Egress Tag Control */
|
|
|
|
-+#define AN8855_VA0_IVL_MAC BIT(5) /* Independent VLAN Learning */
|
|
|
|
-+#define AN8855_VA0_FID GENMASK(4, 1)
|
|
|
|
-+#define AN8855_VA0_VLAN_VALID BIT(0) /* VLAN Entry Valid */
|
|
|
|
-+#define AN8855_VAWD1 0x10200608
|
|
|
|
-+#define AN8855_VA1_PORT_STAG BIT(1)
|
|
|
|
-+
|
|
|
|
-+enum an8855_fid {
|
|
|
|
-+ AN8855_FID_STANDALONE = 0,
|
|
|
|
-+ AN8855_FID_BRIDGED = 1,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+/* Same register field of VAWD0 */
|
|
|
|
-+#define AN8855_VARD0 0x10200618
|
|
|
|
-+
|
|
|
|
-+enum an8855_vlan_egress_attr {
|
|
|
|
-+ AN8855_VLAN_EGRESS_UNTAG = 0,
|
|
|
|
-+ AN8855_VLAN_EGRESS_TAG = 2,
|
|
|
|
-+ AN8855_VLAN_EGRESS_STACK = 3,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+/* Register for port STP state control */
|
|
|
|
-+#define AN8855_SSP_P(x) (0x10208000 + ((x) * 0x200))
|
|
|
|
-+/* Up to 16 FID supported, each with the same mask */
|
|
|
|
-+#define AN8855_FID_PST GENMASK(1, 0)
|
|
|
|
-+#define AN8855_FID_PST_SHIFT(fid) (2 * (fid))
|
|
|
|
-+#define AN8855_FID_PST_MASK(fid) (AN8855_FID_PST << \
|
|
|
|
-+ AN8855_FID_PST_SHIFT(fid))
|
|
|
|
-+#define AN8855_FID_PST_VAL(fid, val) (FIELD_PREP(AN8855_FID_PST, (val)) << \
|
|
|
|
-+ AN8855_FID_PST_SHIFT(fid))
|
|
|
|
-+
|
|
|
|
-+enum an8855_stp_state {
|
|
|
|
-+ AN8855_STP_DISABLED = 0,
|
|
|
|
-+ AN8855_STP_BLOCKING = 1,
|
|
|
|
-+ AN8855_STP_LISTENING = AN8855_STP_BLOCKING,
|
|
|
|
-+ AN8855_STP_LEARNING = 2,
|
|
|
|
-+ AN8855_STP_FORWARDING = 3
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+/* Register for port control */
|
|
|
|
-+#define AN8855_PCR_P(x) (0x10208004 + ((x) * 0x200))
|
|
|
|
-+#define AN8855_EG_TAG GENMASK(29, 28)
|
|
|
|
-+#define AN8855_PORT_PRI GENMASK(26, 24)
|
|
|
|
-+#define AN8855_PORT_TX_MIR BIT(20)
|
|
|
|
-+#define AN8855_PORT_RX_MIR BIT(16)
|
|
|
|
-+#define AN8855_PORT_VLAN GENMASK(1, 0)
|
|
|
|
-+
|
|
|
|
-+enum an8855_port_mode {
|
|
|
|
-+ /* Port Matrix Mode: Frames are forwarded by the PCR_MATRIX members. */
|
|
|
|
-+ AN8855_PORT_MATRIX_MODE = 0,
|
|
|
|
-+
|
|
|
|
-+ /* Fallback Mode: Forward received frames with ingress ports that do
|
|
|
|
-+ * not belong to the VLAN member. Frames whose VID is not listed on
|
|
|
|
-+ * the VLAN table are forwarded by the PCR_MATRIX members.
|
|
|
|
-+ */
|
|
|
|
-+ AN8855_PORT_FALLBACK_MODE = 1,
|
|
|
|
-+
|
|
|
|
-+ /* Check Mode: Forward received frames whose ingress do not
|
|
|
|
-+ * belong to the VLAN member. Discard frames if VID ismiddes on the
|
|
|
|
-+ * VLAN table.
|
|
|
|
-+ */
|
|
|
|
-+ AN8855_PORT_CHECK_MODE = 2,
|
|
|
|
-+
|
|
|
|
-+ /* Security Mode: Discard any frame due to ingress membership
|
|
|
|
-+ * violation or VID missed on the VLAN table.
|
|
|
|
-+ */
|
|
|
|
-+ AN8855_PORT_SECURITY_MODE = 3,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+/* Register for port security control */
|
|
|
|
-+#define AN8855_PSC_P(x) (0x1020800c + ((x) * 0x200))
|
|
|
|
-+#define AN8855_SA_DIS BIT(4)
|
|
|
|
-+
|
|
|
|
-+/* Register for port vlan control */
|
|
|
|
-+#define AN8855_PVC_P(x) (0x10208010 + ((x) * 0x200))
|
|
|
|
-+#define AN8855_PORT_SPEC_REPLACE_MODE BIT(11)
|
|
|
|
-+#define AN8855_PVC_EG_TAG GENMASK(10, 8)
|
|
|
|
-+#define AN8855_VLAN_ATTR GENMASK(7, 6)
|
|
|
|
-+#define AN8855_PORT_SPEC_TAG BIT(5)
|
|
|
|
-+#define AN8855_ACC_FRM GENMASK(1, 0)
|
|
|
|
-+
|
|
|
|
-+enum an8855_vlan_port_eg_tag {
|
|
|
|
-+ AN8855_VLAN_EG_DISABLED = 0,
|
|
|
|
-+ AN8855_VLAN_EG_CONSISTENT = 1,
|
|
|
|
-+ AN8855_VLAN_EG_UNTAGGED = 4,
|
|
|
|
-+ AN8855_VLAN_EG_SWAP = 5,
|
|
|
|
-+ AN8855_VLAN_EG_TAGGED = 6,
|
|
|
|
-+ AN8855_VLAN_EG_STACK = 7,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+enum an8855_vlan_port_attr {
|
|
|
|
-+ AN8855_VLAN_USER = 0,
|
|
|
|
-+ AN8855_VLAN_STACK = 1,
|
|
|
|
-+ AN8855_VLAN_TRANSPARENT = 3,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+enum an8855_vlan_port_acc_frm {
|
|
|
|
-+ AN8855_VLAN_ACC_ALL = 0,
|
|
|
|
-+ AN8855_VLAN_ACC_TAGGED = 1,
|
|
|
|
-+ AN8855_VLAN_ACC_UNTAGGED = 2,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+#define AN8855_PPBV1_P(x) (0x10208014 + ((x) * 0x200))
|
|
|
|
-+#define AN8855_PPBV_G0_PORT_VID GENMASK(11, 0)
|
|
|
|
-+
|
|
|
|
-+#define AN8855_PORTMATRIX_P(x) (0x10208044 + ((x) * 0x200))
|
|
|
|
-+#define AN8855_PORTMATRIX GENMASK(5, 0)
|
|
|
|
-+/* Port matrix without the CPU port that should never be removed */
|
|
|
|
-+#define AN8855_USER_PORTMATRIX GENMASK(4, 0)
|
|
|
|
-+
|
|
|
|
-+/* Register for port PVID */
|
|
|
|
-+#define AN8855_PVID_P(x) (0x10208048 + ((x) * 0x200))
|
|
|
|
-+#define AN8855_G0_PORT_VID GENMASK(11, 0)
|
|
|
|
-+
|
|
|
|
-+/* Register for port MAC control register */
|
|
|
|
-+#define AN8855_PMCR_P(x) (0x10210000 + ((x) * 0x200))
|
|
|
|
-+#define AN8855_PMCR_FORCE_MODE BIT(31)
|
|
|
|
-+#define AN8855_PMCR_FORCE_SPEED GENMASK(30, 28)
|
|
|
|
-+#define AN8855_PMCR_FORCE_SPEED_5000 FIELD_PREP_CONST(AN8855_PMCR_FORCE_SPEED, 0x4)
|
|
|
|
-+#define AN8855_PMCR_FORCE_SPEED_2500 FIELD_PREP_CONST(AN8855_PMCR_FORCE_SPEED, 0x3)
|
|
|
|
-+#define AN8855_PMCR_FORCE_SPEED_1000 FIELD_PREP_CONST(AN8855_PMCR_FORCE_SPEED, 0x2)
|
|
|
|
-+#define AN8855_PMCR_FORCE_SPEED_100 FIELD_PREP_CONST(AN8855_PMCR_FORCE_SPEED, 0x1)
|
|
|
|
-+#define AN8855_PMCR_FORCE_SPEED_10 FIELD_PREP_CONST(AN8855_PMCR_FORCE_SPEED, 0x1)
|
|
|
|
-+#define AN8855_PMCR_FORCE_FDX BIT(25)
|
|
|
|
-+#define AN8855_PMCR_FORCE_LNK BIT(24)
|
|
|
|
-+#define AN8855_PMCR_IFG_XMIT GENMASK(21, 20)
|
|
|
|
-+#define AN8855_PMCR_EXT_PHY BIT(19)
|
|
|
|
-+#define AN8855_PMCR_MAC_MODE BIT(18)
|
|
|
|
-+#define AN8855_PMCR_TX_EN BIT(16)
|
|
|
|
-+#define AN8855_PMCR_RX_EN BIT(15)
|
|
|
|
-+#define AN8855_PMCR_BACKOFF_EN BIT(12)
|
|
|
|
-+#define AN8855_PMCR_BACKPR_EN BIT(11)
|
|
|
|
-+#define AN8855_PMCR_FORCE_EEE5G BIT(9)
|
|
|
|
-+#define AN8855_PMCR_FORCE_EEE2P5G BIT(8)
|
|
|
|
-+#define AN8855_PMCR_FORCE_EEE1G BIT(7)
|
|
|
|
-+#define AN8855_PMCR_FORCE_EEE100 BIT(6)
|
|
|
|
-+#define AN8855_PMCR_TX_FC_EN BIT(5)
|
|
|
|
-+#define AN8855_PMCR_RX_FC_EN BIT(4)
|
|
|
|
-+
|
|
|
|
-+#define AN8855_PMSR_P(x) (0x10210010 + (x) * 0x200)
|
|
|
|
-+#define AN8855_PMSR_SPEED GENMASK(30, 28)
|
|
|
|
-+#define AN8855_PMSR_SPEED_5000 FIELD_PREP_CONST(AN8855_PMSR_SPEED, 0x4)
|
|
|
|
-+#define AN8855_PMSR_SPEED_2500 FIELD_PREP_CONST(AN8855_PMSR_SPEED, 0x3)
|
|
|
|
-+#define AN8855_PMSR_SPEED_1000 FIELD_PREP_CONST(AN8855_PMSR_SPEED, 0x2)
|
|
|
|
-+#define AN8855_PMSR_SPEED_100 FIELD_PREP_CONST(AN8855_PMSR_SPEED, 0x1)
|
|
|
|
-+#define AN8855_PMSR_SPEED_10 FIELD_PREP_CONST(AN8855_PMSR_SPEED, 0x0)
|
|
|
|
-+#define AN8855_PMSR_DPX BIT(25)
|
|
|
|
-+#define AN8855_PMSR_LNK BIT(24)
|
|
|
|
-+#define AN8855_PMSR_EEE1G BIT(7)
|
|
|
|
-+#define AN8855_PMSR_EEE100M BIT(6)
|
|
|
|
-+#define AN8855_PMSR_RX_FC BIT(5)
|
|
|
|
-+#define AN8855_PMSR_TX_FC BIT(4)
|
|
|
|
-+
|
|
|
|
-+#define AN8855_PMEEECR_P(x) (0x10210004 + (x) * 0x200)
|
|
|
|
-+#define AN8855_LPI_MODE_EN BIT(31)
|
|
|
|
-+#define AN8855_WAKEUP_TIME_2500 GENMASK(23, 16)
|
|
|
|
-+#define AN8855_WAKEUP_TIME_1000 GENMASK(15, 8)
|
|
|
|
-+#define AN8855_WAKEUP_TIME_100 GENMASK(7, 0)
|
|
|
|
-+#define AN8855_PMEEECR2_P(x) (0x10210008 + (x) * 0x200)
|
|
|
|
-+#define AN8855_WAKEUP_TIME_5000 GENMASK(7, 0)
|
|
|
|
-+
|
|
|
|
-+#define AN8855_GMACCR 0x10213e00
|
|
|
|
-+#define AN8855_MAX_RX_JUMBO GENMASK(7, 4)
|
|
|
|
-+/* 2K for 0x0, 0x1, 0x2 */
|
|
|
|
-+#define AN8855_MAX_RX_JUMBO_2K FIELD_PREP_CONST(AN8855_MAX_RX_JUMBO, 0x0)
|
|
|
|
-+#define AN8855_MAX_RX_JUMBO_3K FIELD_PREP_CONST(AN8855_MAX_RX_JUMBO, 0x3)
|
|
|
|
-+#define AN8855_MAX_RX_JUMBO_4K FIELD_PREP_CONST(AN8855_MAX_RX_JUMBO, 0x4)
|
|
|
|
-+#define AN8855_MAX_RX_JUMBO_5K FIELD_PREP_CONST(AN8855_MAX_RX_JUMBO, 0x5)
|
|
|
|
-+#define AN8855_MAX_RX_JUMBO_6K FIELD_PREP_CONST(AN8855_MAX_RX_JUMBO, 0x6)
|
|
|
|
-+#define AN8855_MAX_RX_JUMBO_7K FIELD_PREP_CONST(AN8855_MAX_RX_JUMBO, 0x7)
|
|
|
|
-+#define AN8855_MAX_RX_JUMBO_8K FIELD_PREP_CONST(AN8855_MAX_RX_JUMBO, 0x8)
|
|
|
|
-+#define AN8855_MAX_RX_JUMBO_9K FIELD_PREP_CONST(AN8855_MAX_RX_JUMBO, 0x9)
|
|
|
|
-+#define AN8855_MAX_RX_JUMBO_12K FIELD_PREP_CONST(AN8855_MAX_RX_JUMBO, 0xa)
|
|
|
|
-+#define AN8855_MAX_RX_JUMBO_15K FIELD_PREP_CONST(AN8855_MAX_RX_JUMBO, 0xb)
|
|
|
|
-+#define AN8855_MAX_RX_JUMBO_16K FIELD_PREP_CONST(AN8855_MAX_RX_JUMBO, 0xc)
|
|
|
|
-+#define AN8855_MAX_RX_PKT_LEN GENMASK(1, 0)
|
|
|
|
-+#define AN8855_MAX_RX_PKT_1518_1522 FIELD_PREP_CONST(AN8855_MAX_RX_PKT_LEN, 0x0)
|
|
|
|
-+#define AN8855_MAX_RX_PKT_1536 FIELD_PREP_CONST(AN8855_MAX_RX_PKT_LEN, 0x1)
|
|
|
|
-+#define AN8855_MAX_RX_PKT_1552 FIELD_PREP_CONST(AN8855_MAX_RX_PKT_LEN, 0x2)
|
|
|
|
-+#define AN8855_MAX_RX_PKT_JUMBO FIELD_PREP_CONST(AN8855_MAX_RX_PKT_LEN, 0x3)
|
|
|
|
-+
|
|
|
|
-+#define AN8855_CKGCR 0x10213e1c
|
|
|
|
-+#define AN8855_LPI_TXIDLE_THD_MASK GENMASK(31, 14)
|
|
|
|
-+#define AN8855_CKG_LNKDN_PORT_STOP BIT(1)
|
|
|
|
-+#define AN8855_CKG_LNKDN_GLB_STOP BIT(0)
|
|
|
|
-+
|
|
|
|
-+/* Register for MIB */
|
|
|
|
-+#define AN8855_PORT_MIB_COUNTER(x) (0x10214000 + (x) * 0x200)
|
|
|
|
-+/* Each define is an offset of AN8855_PORT_MIB_COUNTER */
|
|
|
|
-+#define AN8855_PORT_MIB_TX_DROP 0x00
|
|
|
|
-+#define AN8855_PORT_MIB_TX_CRC_ERR 0x04
|
|
|
|
-+#define AN8855_PORT_MIB_TX_UNICAST 0x08
|
|
|
|
-+#define AN8855_PORT_MIB_TX_MULTICAST 0x0c
|
|
|
|
-+#define AN8855_PORT_MIB_TX_BROADCAST 0x10
|
|
|
|
-+#define AN8855_PORT_MIB_TX_COLLISION 0x14
|
|
|
|
-+#define AN8855_PORT_MIB_TX_SINGLE_COLLISION 0x18
|
|
|
|
-+#define AN8855_PORT_MIB_TX_MULTIPLE_COLLISION 0x1c
|
|
|
|
-+#define AN8855_PORT_MIB_TX_DEFERRED 0x20
|
|
|
|
-+#define AN8855_PORT_MIB_TX_LATE_COLLISION 0x24
|
|
|
|
-+#define AN8855_PORT_MIB_TX_EXCESSIVE_COLLISION 0x28
|
|
|
|
-+#define AN8855_PORT_MIB_TX_PAUSE 0x2c
|
|
|
|
-+#define AN8855_PORT_MIB_TX_PKT_SZ_64 0x30
|
|
|
|
-+#define AN8855_PORT_MIB_TX_PKT_SZ_65_TO_127 0x34
|
|
|
|
-+#define AN8855_PORT_MIB_TX_PKT_SZ_128_TO_255 0x38
|
|
|
|
-+#define AN8855_PORT_MIB_TX_PKT_SZ_256_TO_511 0x3
|
|
|
|
-+#define AN8855_PORT_MIB_TX_PKT_SZ_512_TO_1023 0x40
|
|
|
|
-+#define AN8855_PORT_MIB_TX_PKT_SZ_1024_TO_1518 0x44
|
|
|
|
-+#define AN8855_PORT_MIB_TX_PKT_SZ_1519_TO_MAX 0x48
|
|
|
|
-+#define AN8855_PORT_MIB_TX_BYTES 0x4c /* 64 bytes */
|
|
|
|
-+#define AN8855_PORT_MIB_TX_OVERSIZE_DROP 0x54
|
|
|
|
-+#define AN8855_PORT_MIB_TX_BAD_PKT_BYTES 0x58 /* 64 bytes */
|
|
|
|
-+#define AN8855_PORT_MIB_RX_DROP 0x80
|
|
|
|
-+#define AN8855_PORT_MIB_RX_FILTERING 0x84
|
|
|
|
-+#define AN8855_PORT_MIB_RX_UNICAST 0x88
|
|
|
|
-+#define AN8855_PORT_MIB_RX_MULTICAST 0x8c
|
|
|
|
-+#define AN8855_PORT_MIB_RX_BROADCAST 0x90
|
|
|
|
-+#define AN8855_PORT_MIB_RX_ALIGN_ERR 0x94
|
|
|
|
-+#define AN8855_PORT_MIB_RX_CRC_ERR 0x98
|
|
|
|
-+#define AN8855_PORT_MIB_RX_UNDER_SIZE_ERR 0x9c
|
|
|
|
-+#define AN8855_PORT_MIB_RX_FRAG_ERR 0xa0
|
|
|
|
-+#define AN8855_PORT_MIB_RX_OVER_SZ_ERR 0xa4
|
|
|
|
-+#define AN8855_PORT_MIB_RX_JABBER_ERR 0xa8
|
|
|
|
-+#define AN8855_PORT_MIB_RX_PAUSE 0xac
|
|
|
|
-+#define AN8855_PORT_MIB_RX_PKT_SZ_64 0xb0
|
|
|
|
-+#define AN8855_PORT_MIB_RX_PKT_SZ_65_TO_127 0xb4
|
|
|
|
-+#define AN8855_PORT_MIB_RX_PKT_SZ_128_TO_255 0xb8
|
|
|
|
-+#define AN8855_PORT_MIB_RX_PKT_SZ_256_TO_511 0xbc
|
|
|
|
-+#define AN8855_PORT_MIB_RX_PKT_SZ_512_TO_1023 0xc0
|
|
|
|
-+#define AN8855_PORT_MIB_RX_PKT_SZ_1024_TO_1518 0xc4
|
|
|
|
-+#define AN8855_PORT_MIB_RX_PKT_SZ_1519_TO_MAX 0xc8
|
|
|
|
-+#define AN8855_PORT_MIB_RX_BYTES 0xcc /* 64 bytes */
|
|
|
|
-+#define AN8855_PORT_MIB_RX_CTRL_DROP 0xd4
|
|
|
|
-+#define AN8855_PORT_MIB_RX_INGRESS_DROP 0xd8
|
|
|
|
-+#define AN8855_PORT_MIB_RX_ARL_DROP 0xdc
|
|
|
|
-+#define AN8855_PORT_MIB_FLOW_CONTROL_DROP 0xe0
|
|
|
|
-+#define AN8855_PORT_MIB_WRED_DROP 0xe4
|
|
|
|
-+#define AN8855_PORT_MIB_MIRROR_DROP 0xe8
|
|
|
|
-+#define AN8855_PORT_MIB_RX_BAD_PKT_BYTES 0xec /* 64 bytes */
|
|
|
|
-+#define AN8855_PORT_MIB_RXS_FLOW_SAMPLING_PKT_DROP 0xf4
|
|
|
|
-+#define AN8855_PORT_MIB_RXS_FLOW_TOTAL_PKT_DROP 0xf8
|
|
|
|
-+#define AN8855_PORT_MIB_PORT_CONTROL_DROP 0xfc
|
|
|
|
-+#define AN8855_MIB_CCR 0x10213e30
|
|
|
|
-+#define AN8855_CCR_MIB_ENABLE BIT(31)
|
|
|
|
-+#define AN8855_CCR_RX_OCT_CNT_GOOD BIT(7)
|
|
|
|
-+#define AN8855_CCR_RX_OCT_CNT_BAD BIT(6)
|
|
|
|
-+#define AN8855_CCR_TX_OCT_CNT_GOOD BIT(5)
|
|
|
|
-+#define AN8855_CCR_TX_OCT_CNT_BAD BIT(4)
|
|
|
|
-+#define AN8855_CCR_RX_OCT_CNT_GOOD_2 BIT(3)
|
|
|
|
-+#define AN8855_CCR_RX_OCT_CNT_BAD_2 BIT(2)
|
|
|
|
-+#define AN8855_CCR_TX_OCT_CNT_GOOD_2 BIT(1)
|
|
|
|
-+#define AN8855_CCR_TX_OCT_CNT_BAD_2 BIT(0)
|
|
|
|
-+#define AN8855_CCR_MIB_ACTIVATE (AN8855_CCR_MIB_ENABLE | \
|
|
|
|
-+ AN8855_CCR_RX_OCT_CNT_GOOD | \
|
|
|
|
-+ AN8855_CCR_RX_OCT_CNT_BAD | \
|
|
|
|
-+ AN8855_CCR_TX_OCT_CNT_GOOD | \
|
|
|
|
-+ AN8855_CCR_TX_OCT_CNT_BAD | \
|
|
|
|
-+ AN8855_CCR_RX_OCT_CNT_BAD_2 | \
|
|
|
|
-+ AN8855_CCR_TX_OCT_CNT_BAD_2)
|
|
|
|
-+#define AN8855_MIB_CLR 0x10213e34
|
|
|
|
-+#define AN8855_MIB_PORT6_CLR BIT(6)
|
|
|
|
-+#define AN8855_MIB_PORT5_CLR BIT(5)
|
|
|
|
-+#define AN8855_MIB_PORT4_CLR BIT(4)
|
|
|
|
-+#define AN8855_MIB_PORT3_CLR BIT(3)
|
|
|
|
-+#define AN8855_MIB_PORT2_CLR BIT(2)
|
|
|
|
-+#define AN8855_MIB_PORT1_CLR BIT(1)
|
|
|
|
-+#define AN8855_MIB_PORT0_CLR BIT(0)
|
|
|
|
-+
|
|
|
|
-+/* HSGMII/SGMII Configuration register */
|
|
|
|
-+/* AN8855_HSGMII_AN_CSR_BASE 0x10220000 */
|
|
|
|
-+#define AN8855_SGMII_REG_AN0 0x10220000
|
|
|
|
-+/* AN8855_SGMII_AN_ENABLE BMCR_ANENABLE */
|
|
|
|
-+/* AN8855_SGMII_AN_RESTART BMCR_ANRESTART */
|
|
|
|
-+#define AN8855_SGMII_REG_AN_13 0x10220034
|
|
|
|
-+#define AN8855_SGMII_REMOTE_FAULT_DIS BIT(8)
|
|
|
|
-+#define AN8855_SGMII_IF_MODE GENMASK(5, 0)
|
|
|
|
-+#define AN8855_SGMII_REG_AN_FORCE_CL37 0x10220060
|
|
|
|
-+#define AN8855_RG_FORCE_AN_DONE BIT(0)
|
|
|
|
-+
|
|
|
|
-+/* AN8855_HSGMII_CSR_PCS_BASE 0x10220000 */
|
|
|
|
-+#define AN8855_RG_HSGMII_PCS_CTROL_1 0x10220a00
|
|
|
|
-+#define AN8855_RG_TBI_10B_MODE BIT(30)
|
|
|
|
-+#define AN8855_RG_AN_SGMII_MODE_FORCE 0x10220a24
|
|
|
|
-+#define AN8855_RG_FORCE_CUR_SGMII_MODE GENMASK(5, 4)
|
|
|
|
-+#define AN8855_RG_FORCE_CUR_SGMII_SEL BIT(0)
|
|
|
|
-+
|
|
|
|
-+/* AN8855_MULTI_SGMII_CSR_BASE 0x10224000 */
|
|
|
|
-+#define AN8855_SGMII_STS_CTRL_0 0x10224018
|
|
|
|
-+#define AN8855_RG_LINK_MODE_P0 GENMASK(5, 4)
|
|
|
|
-+#define AN8855_RG_LINK_MODE_P0_SPEED_2500 FIELD_PREP_CONST(AN8855_RG_LINK_MODE_P0, 0x3)
|
|
|
|
-+#define AN8855_RG_LINK_MODE_P0_SPEED_1000 FIELD_PREP_CONST(AN8855_RG_LINK_MODE_P0, 0x2)
|
|
|
|
-+#define AN8855_RG_LINK_MODE_P0_SPEED_100 FIELD_PREP_CONST(AN8855_RG_LINK_MODE_P0, 0x1)
|
|
|
|
-+#define AN8855_RG_LINK_MODE_P0_SPEED_10 FIELD_PREP_CONST(AN8855_RG_LINK_MODE_P0, 0x0)
|
|
|
|
-+#define AN8855_RG_FORCE_SPD_MODE_P0 BIT(2)
|
|
|
|
-+#define AN8855_MSG_RX_CTRL_0 0x10224100
|
|
|
|
-+#define AN8855_MSG_RX_LIK_STS_0 0x10224514
|
|
|
|
-+#define AN8855_RG_DPX_STS_P3 BIT(24)
|
|
|
|
-+#define AN8855_RG_DPX_STS_P2 BIT(16)
|
|
|
|
-+#define AN8855_RG_EEE1G_STS_P1 BIT(12)
|
|
|
|
-+#define AN8855_RG_DPX_STS_P1 BIT(8)
|
|
|
|
-+#define AN8855_RG_TXFC_STS_P0 BIT(2)
|
|
|
|
-+#define AN8855_RG_RXFC_STS_P0 BIT(1)
|
|
|
|
-+#define AN8855_RG_DPX_STS_P0 BIT(0)
|
|
|
|
-+#define AN8855_MSG_RX_LIK_STS_2 0x1022451c
|
|
|
|
-+#define AN8855_RG_RXFC_AN_BYPASS_P3 BIT(11)
|
|
|
|
-+#define AN8855_RG_RXFC_AN_BYPASS_P2 BIT(10)
|
|
|
|
-+#define AN8855_RG_RXFC_AN_BYPASS_P1 BIT(9)
|
|
|
|
-+#define AN8855_RG_TXFC_AN_BYPASS_P3 BIT(7)
|
|
|
|
-+#define AN8855_RG_TXFC_AN_BYPASS_P2 BIT(6)
|
|
|
|
-+#define AN8855_RG_TXFC_AN_BYPASS_P1 BIT(5)
|
|
|
|
-+#define AN8855_RG_DPX_AN_BYPASS_P3 BIT(3)
|
|
|
|
-+#define AN8855_RG_DPX_AN_BYPASS_P2 BIT(2)
|
|
|
|
-+#define AN8855_RG_DPX_AN_BYPASS_P1 BIT(1)
|
|
|
|
-+#define AN8855_RG_DPX_AN_BYPASS_P0 BIT(0)
|
|
|
|
-+#define AN8855_PHY_RX_FORCE_CTRL_0 0x10224520
|
|
|
|
-+#define AN8855_RG_FORCE_TXC_SEL BIT(4)
|
|
|
|
-+
|
|
|
|
-+/* AN8855_XFI_CSR_PCS_BASE 0x10225000 */
|
|
|
|
-+#define AN8855_RG_USXGMII_AN_CONTROL_0 0x10225bf8
|
|
|
|
-+
|
|
|
|
-+/* AN8855_MULTI_PHY_RA_CSR_BASE 0x10226000 */
|
|
|
|
-+#define AN8855_RG_RATE_ADAPT_CTRL_0 0x10226000
|
|
|
|
-+#define AN8855_RG_RATE_ADAPT_RX_BYPASS BIT(27)
|
|
|
|
-+#define AN8855_RG_RATE_ADAPT_TX_BYPASS BIT(26)
|
|
|
|
-+#define AN8855_RG_RATE_ADAPT_RX_EN BIT(4)
|
|
|
|
-+#define AN8855_RG_RATE_ADAPT_TX_EN BIT(0)
|
|
|
|
-+#define AN8855_RATE_ADP_P0_CTRL_0 0x10226100
|
|
|
|
-+#define AN8855_RG_P0_DIS_MII_MODE BIT(31)
|
|
|
|
-+#define AN8855_RG_P0_MII_MODE BIT(28)
|
|
|
|
-+#define AN8855_RG_P0_MII_RA_RX_EN BIT(3)
|
|
|
|
-+#define AN8855_RG_P0_MII_RA_TX_EN BIT(2)
|
|
|
|
-+#define AN8855_RG_P0_MII_RA_RX_MODE BIT(1)
|
|
|
|
-+#define AN8855_RG_P0_MII_RA_TX_MODE BIT(0)
|
|
|
|
-+#define AN8855_MII_RA_AN_ENABLE 0x10226300
|
|
|
|
-+#define AN8855_RG_P0_RA_AN_EN BIT(0)
|
|
|
|
-+
|
|
|
|
-+/* AN8855_QP_DIG_CSR_BASE 0x1022a000 */
|
|
|
|
-+#define AN8855_QP_CK_RST_CTRL_4 0x1022a310
|
|
|
|
-+#define AN8855_QP_DIG_MODE_CTRL_0 0x1022a324
|
|
|
|
-+#define AN8855_RG_SGMII_MODE GENMASK(5, 4)
|
|
|
|
-+#define AN8855_RG_SGMII_AN_EN BIT(0)
|
|
|
|
-+#define AN8855_QP_DIG_MODE_CTRL_1 0x1022a330
|
|
|
|
-+#define AN8855_RG_TPHY_SPEED GENMASK(3, 2)
|
|
|
|
-+
|
|
|
|
-+/* AN8855_SERDES_WRAPPER_BASE 0x1022c000 */
|
|
|
|
-+#define AN8855_USGMII_CTRL_0 0x1022c000
|
|
|
|
-+
|
|
|
|
-+/* AN8855_QP_PMA_TOP_BASE 0x1022e000 */
|
|
|
|
-+#define AN8855_PON_RXFEDIG_CTRL_0 0x1022e100
|
|
|
|
-+#define AN8855_RG_QP_EQ_RX500M_CK_SEL BIT(12)
|
|
|
|
-+#define AN8855_PON_RXFEDIG_CTRL_9 0x1022e124
|
|
|
|
-+#define AN8855_RG_QP_EQ_LEQOSC_DLYCNT GENMASK(2, 0)
|
|
|
|
-+
|
|
|
|
-+#define AN8855_SS_LCPLL_PWCTL_SETTING_2 0x1022e208
|
|
|
|
-+#define AN8855_RG_NCPO_ANA_MSB GENMASK(17, 16)
|
|
|
|
-+#define AN8855_SS_LCPLL_TDC_FLT_2 0x1022e230
|
|
|
|
-+#define AN8855_RG_LCPLL_NCPO_VALUE GENMASK(30, 0)
|
|
|
|
-+#define AN8855_SS_LCPLL_TDC_FLT_5 0x1022e23c
|
|
|
|
-+#define AN8855_RG_LCPLL_NCPO_CHG BIT(24)
|
|
|
|
-+#define AN8855_SS_LCPLL_TDC_PCW_1 0x1022e248
|
|
|
|
-+#define AN8855_RG_LCPLL_PON_HRDDS_PCW_NCPO_GPON GENMASK(30, 0)
|
|
|
|
-+#define AN8855_INTF_CTRL_8 0x1022e320
|
|
|
|
-+#define AN8855_INTF_CTRL_9 0x1022e324
|
|
|
|
-+#define AN8855_INTF_CTRL_10 0x1022e328
|
|
|
|
-+#define AN8855_RG_DA_QP_TX_FIR_C2_SEL BIT(29)
|
|
|
|
-+#define AN8855_RG_DA_QP_TX_FIR_C2_FORCE GENMASK(28, 24)
|
|
|
|
-+#define AN8855_RG_DA_QP_TX_FIR_C1_SEL BIT(21)
|
|
|
|
-+#define AN8855_RG_DA_QP_TX_FIR_C1_FORCE GENMASK(20, 16)
|
|
|
|
-+#define AN8855_INTF_CTRL_11 0x1022e32c
|
|
|
|
-+#define AN8855_RG_DA_QP_TX_FIR_C0B_SEL BIT(6)
|
|
|
|
-+#define AN8855_RG_DA_QP_TX_FIR_C0B_FORCE GENMASK(5, 0)
|
|
|
|
-+#define AN8855_PLL_CTRL_0 0x1022e400
|
|
|
|
-+#define AN8855_RG_PHYA_AUTO_INIT BIT(0)
|
|
|
|
-+#define AN8855_PLL_CTRL_2 0x1022e408
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_SDM_IFM_INTF BIT(30)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_RICO_SEL_INTF BIT(29)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_POSTDIV_EN_INTF BIT(28)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_PHY_CK_EN_INTF BIT(27)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_PFD_OFFSET_EN_INTRF BIT(26)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_PFD_OFFSET_INTF GENMASK(25, 24)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_PCK_SEL_INTF BIT(22)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_KBAND_PREDIV_INTF GENMASK(21, 20)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_IR_INTF GENMASK(19, 16)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_ICOIQ_EN_INTF BIT(14)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_FBKSEL_INTF GENMASK(13, 12)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_BR_INTF GENMASK(10, 8)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_BPD_INTF GENMASK(7, 6)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_BPA_INTF GENMASK(4, 2)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_BC_INTF GENMASK(1, 0)
|
|
|
|
-+#define AN8855_PLL_CTRL_3 0x1022e40c
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_SSC_PERIOD_INTF GENMASK(31, 16)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_SSC_DELTA_INTF GENMASK(15, 0)
|
|
|
|
-+#define AN8855_PLL_CTRL_4 0x1022e410
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_SDM_HREN_INTF GENMASK(4, 3)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_ICOLP_EN_INTF BIT(2)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_SSC_DIR_DLY_INTF GENMASK(1, 0)
|
|
|
|
-+#define AN8855_PLL_CK_CTRL_0 0x1022e414
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_TDC_TXCK_SEL_INTF BIT(9)
|
|
|
|
-+#define AN8855_RG_DA_QP_PLL_SDM_DI_EN_INTF BIT(8)
|
|
|
|
-+#define AN8855_RX_DLY_0 0x1022e614
|
|
|
|
-+#define AN8855_RG_QP_RX_SAOSC_EN_H_DLY GENMASK(13, 8)
|
|
|
|
-+#define AN8855_RG_QP_RX_PI_CAL_EN_H_DLY GENMASK(7, 0)
|
|
|
|
-+#define AN8855_RX_CTRL_2 0x1022e630
|
|
|
|
-+#define AN8855_RG_QP_RX_EQ_EN_H_DLY GENMASK(28, 16)
|
|
|
|
-+#define AN8855_RX_CTRL_5 0x1022e63c
|
|
|
|
-+#define AN8855_RG_FREDET_CHK_CYCLE GENMASK(29, 10)
|
|
|
|
-+#define AN8855_RX_CTRL_6 0x1022e640
|
|
|
|
-+#define AN8855_RG_FREDET_GOLDEN_CYCLE GENMASK(19, 0)
|
|
|
|
-+#define AN8855_RX_CTRL_7 0x1022e644
|
|
|
|
-+#define AN8855_RG_FREDET_TOLERATE_CYCLE GENMASK(19, 0)
|
|
|
|
-+#define AN8855_RX_CTRL_8 0x1022e648
|
|
|
|
-+#define AN8855_RG_DA_QP_SAOSC_DONE_TIME GENMASK(27, 16)
|
|
|
|
-+#define AN8855_RG_DA_QP_LEQOS_EN_TIME GENMASK(14, 0)
|
|
|
|
-+#define AN8855_RX_CTRL_26 0x1022e690
|
|
|
|
-+#define AN8855_RG_QP_EQ_RETRAIN_ONLY_EN BIT(26)
|
|
|
|
-+#define AN8855_RG_LINK_NE_EN BIT(24)
|
|
|
|
-+#define AN8855_RG_LINK_ERRO_EN BIT(23)
|
|
|
|
-+#define AN8855_RX_CTRL_42 0x1022e6d0
|
|
|
|
-+#define AN8855_RG_QP_EQ_EN_DLY GENMASK(12, 0)
|
|
|
|
-+
|
|
|
|
-+/* AN8855_QP_ANA_CSR_BASE 0x1022f000 */
|
|
|
|
-+#define AN8855_RG_QP_RX_DAC_EN 0x1022f000
|
|
|
|
-+#define AN8855_RG_QP_SIGDET_HF GENMASK(17, 16)
|
|
|
|
-+#define AN8855_RG_QP_RXAFE_RESERVE 0x1022f004
|
|
|
|
-+#define AN8855_RG_QP_CDR_PD_10B_EN BIT(11)
|
|
|
|
-+#define AN8855_RG_QP_CDR_LPF_BOT_LIM 0x1022f008
|
|
|
|
-+#define AN8855_RG_QP_CDR_LPF_KP_GAIN GENMASK(26, 24)
|
|
|
|
-+#define AN8855_RG_QP_CDR_LPF_KI_GAIN GENMASK(22, 20)
|
|
|
|
-+#define AN8855_RG_QP_CDR_LPF_MJV_LIM 0x1022f00c
|
|
|
|
-+#define AN8855_RG_QP_CDR_LPF_RATIO GENMASK(5, 4)
|
|
|
|
-+#define AN8855_RG_QP_CDR_LPF_SETVALUE 0x1022f014
|
|
|
|
-+#define AN8855_RG_QP_CDR_PR_BUF_IN_SR GENMASK(31, 29)
|
|
|
|
-+#define AN8855_RG_QP_CDR_PR_BETA_SEL GENMASK(28, 25)
|
|
|
|
-+#define AN8855_RG_QP_CDR_PR_CKREF_DIV1 0x1022f018
|
|
|
|
-+#define AN8855_RG_QP_CDR_PR_KBAND_DIV GENMASK(26, 24)
|
|
|
|
-+#define AN8855_RG_QP_CDR_PR_DAC_BAND GENMASK(12, 8)
|
|
|
|
-+#define AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE 0x1022f01c
|
|
|
|
-+#define AN8855_RG_QP_CDR_PR_XFICK_EN BIT(30)
|
|
|
|
-+#define AN8855_RG_QP_CDR_PR_KBAND_PCIE_MODE BIT(6)
|
|
|
|
-+#define AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE_MASK GENMASK(5, 0)
|
|
|
|
-+#define AN8855_RG_QP_CDR_FORCE_IBANDLPF_R_OFF 0x1022f020
|
|
|
|
-+#define AN8855_RG_QP_CDR_PHYCK_SEL GENMASK(17, 16)
|
|
|
|
-+#define AN8855_RG_QP_CDR_PHYCK_RSTB BIT(13)
|
|
|
|
-+#define AN8855_RG_QP_CDR_PHYCK_DIV GENMASK(12, 6)
|
|
|
|
-+#define AN8855_RG_QP_TX_MODE 0x1022f028
|
|
|
|
-+#define AN8855_RG_QP_TX_RESERVE GENMASK(31, 16)
|
|
|
|
-+#define AN8855_RG_QP_TX_MODE_16B_EN BIT(0)
|
|
|
|
-+#define AN8855_RG_QP_PLL_IPLL_DIG_PWR_SEL 0x1022f03c
|
|
|
|
-+#define AN8855_RG_QP_PLL_SDM_ORD 0x1022f040
|
|
|
|
-+#define AN8855_RG_QP_PLL_SSC_PHASE_INI BIT(4)
|
|
|
|
-+#define AN8855_RG_QP_PLL_SSC_TRI_EN BIT(3)
|
|
|
|
-+
|
|
|
|
-+/* AN8855_ETHER_SYS_BASE 0x1028c800 */
|
|
|
|
-+#define AN8855_RG_GPHY_AFE_PWD 0x1028c840
|
|
|
|
-+#define AN8855_RG_GPHY_SMI_ADDR 0x1028c848
|
|
|
|
-+
|
|
|
|
-+#define MIB_DESC(_s, _o, _n) \
|
|
|
|
-+ { \
|
|
|
|
-+ .size = (_s), \
|
|
|
|
-+ .offset = (_o), \
|
|
|
|
-+ .name = (_n), \
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+struct an8855_mib_desc {
|
|
|
|
-+ unsigned int size;
|
|
|
|
-+ unsigned int offset;
|
|
|
|
-+ const char *name;
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+struct an8855_fdb {
|
|
|
|
-+ u16 vid;
|
|
|
|
-+ u8 port_mask;
|
|
|
|
-+ u8 aging;
|
|
|
|
-+ u8 mac[6];
|
|
|
|
-+ bool noarp;
|
|
|
|
-+ u8 live;
|
|
|
|
-+ u8 type;
|
|
|
|
-+ u8 fid;
|
|
|
|
-+ u8 ivl;
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+struct an8855_priv {
|
|
|
|
-+ struct device *dev;
|
|
|
|
-+ struct dsa_switch *ds;
|
|
|
|
-+ struct mii_bus *bus;
|
|
|
|
-+ struct regmap *regmap;
|
|
|
|
-+ struct gpio_desc *reset_gpio;
|
|
|
|
-+ /* Protect ATU or VLAN table access */
|
|
|
|
-+ struct mutex reg_mutex;
|
|
|
|
-+
|
|
|
|
-+ struct phylink_pcs pcs;
|
|
|
|
-+
|
|
|
|
-+ unsigned int phy_base;
|
|
|
|
-+
|
|
|
|
-+ u8 mirror_rx;
|
|
|
|
-+ u8 mirror_tx;
|
|
|
|
-+ u8 port_isolated_map;
|
|
|
|
-+
|
|
|
|
-+ bool phy_require_calib;
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+#endif /* __AN8855_H */
|
|
|
|
-diff --git a/target/linux/mediatek/files-6.6/drivers/net/phy/air_an8855.c b/target/linux/mediatek/files-6.6/drivers/net/phy/air_an8855.c
|
|
|
|
-new file mode 100644
|
|
|
|
-index 00000000000000..10627ea2be3873
|
|
|
|
---- /dev/null
|
|
|
|
-+++ b/target/linux/mediatek/files-6.6/drivers/net/phy/air_an8855.c
|
|
|
|
-@@ -0,0 +1,268 @@
|
|
|
|
-+// SPDX-License-Identifier: GPL-2.0+
|
|
|
|
-+/*
|
|
|
|
-+ * Copyright (C) 2024 Christian Marangi <[email protected]>
|
|
|
|
-+ */
|
|
|
|
-+
|
|
|
|
-+#include <linux/phy.h>
|
|
|
|
-+#include <linux/module.h>
|
|
|
|
-+#include <linux/bitfield.h>
|
|
|
|
-+#include <linux/nvmem-consumer.h>
|
|
|
|
-+
|
|
|
|
-+#define AN8855_PHY_SELECT_PAGE 0x1f
|
|
|
|
-+/* Mask speculation based on page up to 0x4 */
|
|
|
|
-+#define AN8855_PHY_PAGE GENMASK(2, 0)
|
|
|
|
-+#define AN8855_PHY_PAGE_STANDARD FIELD_PREP_CONST(AN8855_PHY_PAGE, 0x0)
|
|
|
|
-+#define AN8855_PHY_PAGE_EXTENDED_1 FIELD_PREP_CONST(AN8855_PHY_PAGE, 0x1)
|
|
|
|
-+
|
|
|
|
-+/* MII Registers Page 1 */
|
|
|
|
-+#define AN8855_PHY_EXT_REG_14 0x14
|
|
|
|
-+#define AN8855_PHY_EN_DOWN_SHIFT BIT(4)
|
|
|
|
-+
|
|
|
|
-+/* R50 Calibration regs in MDIO_MMD_VEND1 */
|
|
|
|
-+#define AN8855_PHY_R500HM_RSEL_TX_AB 0x174
|
|
|
|
-+#define AN8855_PHY_R50OHM_RSEL_TX_A_EN BIT(15)
|
|
|
|
-+#define AN8855_PHY_R50OHM_RSEL_TX_A GENMASK(14, 8)
|
|
|
|
-+#define AN8855_PHY_R50OHM_RSEL_TX_B_EN BIT(7)
|
|
|
|
-+#define AN8855_PHY_R50OHM_RSEL_TX_B GENMASK(6, 0)
|
|
|
|
-+#define AN8855_PHY_R500HM_RSEL_TX_CD 0x175
|
|
|
|
-+#define AN8855_PHY_R50OHM_RSEL_TX_C_EN BIT(15)
|
|
|
|
-+#define AN8855_PHY_R50OHM_RSEL_TX_C GENMASK(14, 8)
|
|
|
|
-+#define AN8855_PHY_R50OHM_RSEL_TX_D_EN BIT(7)
|
|
|
|
-+#define AN8855_PHY_R50OHM_RSEL_TX_D GENMASK(6, 0)
|
|
|
|
-+
|
|
|
|
-+#define AN8855_SWITCH_EFUSE_R50O GENMASK(30, 24)
|
|
|
|
-+
|
|
|
|
-+/* PHY TX PAIR DELAY SELECT Register */
|
|
|
|
-+#define AN8855_PHY_TX_PAIR_DLY_SEL_GBE 0x013
|
|
|
|
-+#define AN8855_PHY_CR_DA_TX_PAIR_DELKAY_SEL_A_GBE GENMASK(14, 12)
|
|
|
|
-+#define AN8855_PHY_CR_DA_TX_PAIR_DELKAY_SEL_B_GBE GENMASK(10, 8)
|
|
|
|
-+#define AN8855_PHY_CR_DA_TX_PAIR_DELKAY_SEL_C_GBE GENMASK(6, 4)
|
|
|
|
-+#define AN8855_PHY_CR_DA_TX_PAIR_DELKAY_SEL_D_GBE GENMASK(2, 0)
|
|
|
|
-+/* PHY ADC Register */
|
|
|
|
-+#define AN8855_PHY_RXADC_CTRL 0x0d8
|
|
|
|
-+#define AN8855_PHY_RG_AD_SAMNPLE_PHSEL_A BIT(12)
|
|
|
|
-+#define AN8855_PHY_RG_AD_SAMNPLE_PHSEL_B BIT(8)
|
|
|
|
-+#define AN8855_PHY_RG_AD_SAMNPLE_PHSEL_C BIT(4)
|
|
|
|
-+#define AN8855_PHY_RG_AD_SAMNPLE_PHSEL_D BIT(0)
|
|
|
|
-+#define AN8855_PHY_RXADC_REV_0 0x0d9
|
|
|
|
-+#define AN8855_PHY_RG_AD_RESERVE0_A GENMASK(15, 8)
|
|
|
|
-+#define AN8855_PHY_RG_AD_RESERVE0_B GENMASK(7, 0)
|
|
|
|
-+#define AN8855_PHY_RXADC_REV_1 0x0da
|
|
|
|
-+#define AN8855_PHY_RG_AD_RESERVE0_C GENMASK(15, 8)
|
|
|
|
-+#define AN8855_PHY_RG_AD_RESERVE0_D GENMASK(7, 0)
|
|
|
|
-+
|
|
|
|
-+#define AN8855_PHY_ID 0xc0ff0410
|
|
|
|
-+
|
|
|
|
-+#define AN8855_PHY_FLAGS_EN_CALIBRATION BIT(0)
|
|
|
|
-+
|
|
|
|
-+struct air_an8855_priv {
|
|
|
|
-+ u8 calibration_data[4];
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+static const u8 dsa_r50ohm_table[] = {
|
|
|
|
-+ 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
|
|
|
|
-+ 127, 127, 127, 127, 127, 127, 127, 126, 122, 117,
|
|
|
|
-+ 112, 109, 104, 101, 97, 94, 90, 88, 84, 80,
|
|
|
|
-+ 78, 74, 72, 68, 66, 64, 61, 58, 56, 53,
|
|
|
|
-+ 51, 48, 47, 44, 42, 40, 38, 36, 34, 32,
|
|
|
|
-+ 31, 28, 27, 24, 24, 22, 20, 18, 16, 16,
|
|
|
|
-+ 14, 12, 11, 9
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+static int en8855_get_r50ohm_val(struct device *dev, const char *calib_name,
|
|
|
|
-+ u8 *dest)
|
|
|
|
-+{
|
|
|
|
-+ u32 shift_sel, val;
|
|
|
|
-+ int ret;
|
|
|
|
-+ int i;
|
|
|
|
-+
|
|
|
|
-+ ret = nvmem_cell_read_u32(dev, calib_name, &val);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ shift_sel = FIELD_GET(AN8855_SWITCH_EFUSE_R50O, val);
|
|
|
|
-+ for (i = 0; i < ARRAY_SIZE(dsa_r50ohm_table); i++)
|
|
|
|
-+ if (dsa_r50ohm_table[i] == shift_sel)
|
|
|
|
-+ break;
|
|
|
|
-+
|
|
|
|
-+ if (i < 8 || i >= ARRAY_SIZE(dsa_r50ohm_table))
|
|
|
|
-+ *dest = dsa_r50ohm_table[25];
|
|
|
|
-+ else
|
|
|
|
-+ *dest = dsa_r50ohm_table[i - 8];
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_probe(struct phy_device *phydev)
|
|
|
|
-+{
|
|
|
|
-+ struct device *dev = &phydev->mdio.dev;
|
|
|
|
-+ struct device_node *node = dev->of_node;
|
|
|
|
-+ struct air_an8855_priv *priv;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ /* If we don't have a node, skip get calib */
|
|
|
|
-+ if (!node)
|
|
|
|
-+ return 0;
|
|
|
|
-+
|
|
|
|
-+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
|
|
|
-+ if (!priv)
|
|
|
|
-+ return -ENOMEM;
|
|
|
|
-+
|
|
|
|
-+ ret = en8855_get_r50ohm_val(dev, "tx_a", &priv->calibration_data[0]);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = en8855_get_r50ohm_val(dev, "tx_b", &priv->calibration_data[1]);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = en8855_get_r50ohm_val(dev, "tx_c", &priv->calibration_data[2]);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ ret = en8855_get_r50ohm_val(dev, "tx_d", &priv->calibration_data[3]);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ phydev->priv = priv;
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_get_downshift(struct phy_device *phydev, u8 *data)
|
|
|
|
-+{
|
|
|
|
-+ int val;
|
|
|
|
-+
|
|
|
|
-+ val = phy_read_paged(phydev, AN8855_PHY_PAGE_EXTENDED_1, AN8855_PHY_EXT_REG_14);
|
|
|
|
-+ if (val < 0)
|
|
|
|
-+ return val;
|
|
|
|
-+
|
|
|
|
-+ *data = val & AN8855_PHY_EN_DOWN_SHIFT ? DOWNSHIFT_DEV_DEFAULT_COUNT :
|
|
|
|
-+ DOWNSHIFT_DEV_DISABLE;
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_set_downshift(struct phy_device *phydev, u8 cnt)
|
|
|
|
-+{
|
|
|
|
-+ u16 ds = cnt != DOWNSHIFT_DEV_DISABLE ? AN8855_PHY_EN_DOWN_SHIFT : 0;
|
|
|
|
-+
|
|
|
|
-+ return phy_modify_paged(phydev, AN8855_PHY_PAGE_EXTENDED_1,
|
|
|
|
-+ AN8855_PHY_EXT_REG_14, AN8855_PHY_EN_DOWN_SHIFT,
|
|
|
|
-+ ds);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_config_init(struct phy_device *phydev)
|
|
|
|
-+{
|
|
|
|
-+ struct air_an8855_priv *priv = phydev->priv;
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ /* Enable HW auto downshift */
|
|
|
|
-+ ret = an8855_set_downshift(phydev, DOWNSHIFT_DEV_DEFAULT_COUNT);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ /* Apply calibration values, if needed.
|
|
|
|
-+ * AN8855_PHY_FLAGS_EN_CALIBRATION signal this.
|
|
|
|
-+ */
|
|
|
|
-+ if (priv && phydev->dev_flags & AN8855_PHY_FLAGS_EN_CALIBRATION) {
|
|
|
|
-+ u8 *calibration_data = priv->calibration_data;
|
|
|
|
-+
|
|
|
|
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, AN8855_PHY_R500HM_RSEL_TX_AB,
|
|
|
|
-+ AN8855_PHY_R50OHM_RSEL_TX_A | AN8855_PHY_R50OHM_RSEL_TX_B,
|
|
|
|
-+ FIELD_PREP(AN8855_PHY_R50OHM_RSEL_TX_A, calibration_data[0]) |
|
|
|
|
-+ FIELD_PREP(AN8855_PHY_R50OHM_RSEL_TX_B, calibration_data[1]));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, AN8855_PHY_R500HM_RSEL_TX_CD,
|
|
|
|
-+ AN8855_PHY_R50OHM_RSEL_TX_C | AN8855_PHY_R50OHM_RSEL_TX_D,
|
|
|
|
-+ FIELD_PREP(AN8855_PHY_R50OHM_RSEL_TX_C, calibration_data[2]) |
|
|
|
|
-+ FIELD_PREP(AN8855_PHY_R50OHM_RSEL_TX_D, calibration_data[3]));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ /* Apply values to reduce signal noise */
|
|
|
|
-+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AN8855_PHY_TX_PAIR_DLY_SEL_GBE,
|
|
|
|
-+ FIELD_PREP(AN8855_PHY_CR_DA_TX_PAIR_DELKAY_SEL_A_GBE, 0x4) |
|
|
|
|
-+ FIELD_PREP(AN8855_PHY_CR_DA_TX_PAIR_DELKAY_SEL_C_GBE, 0x4));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AN8855_PHY_RXADC_CTRL,
|
|
|
|
-+ AN8855_PHY_RG_AD_SAMNPLE_PHSEL_A |
|
|
|
|
-+ AN8855_PHY_RG_AD_SAMNPLE_PHSEL_C);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AN8855_PHY_RXADC_REV_0,
|
|
|
|
-+ FIELD_PREP(AN8855_PHY_RG_AD_RESERVE0_A, 0x1));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AN8855_PHY_RXADC_REV_1,
|
|
|
|
-+ FIELD_PREP(AN8855_PHY_RG_AD_RESERVE0_C, 0x1));
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
-+
|
|
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_get_tunable(struct phy_device *phydev,
|
|
|
|
-+ struct ethtool_tunable *tuna, void *data)
|
|
|
|
-+{
|
|
|
|
-+ switch (tuna->id) {
|
|
|
|
-+ case ETHTOOL_PHY_DOWNSHIFT:
|
|
|
|
-+ return an8855_get_downshift(phydev, data);
|
|
|
|
-+ default:
|
|
|
|
-+ return -EOPNOTSUPP;
|
|
|
|
-+ }
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_set_tunable(struct phy_device *phydev,
|
|
|
|
-+ struct ethtool_tunable *tuna, const void *data)
|
|
|
|
-+{
|
|
|
|
-+ switch (tuna->id) {
|
|
|
|
-+ case ETHTOOL_PHY_DOWNSHIFT:
|
|
|
|
-+ return an8855_set_downshift(phydev, *(const u8 *)data);
|
|
|
|
-+ default:
|
|
|
|
-+ return -EOPNOTSUPP;
|
|
|
|
-+ }
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_read_page(struct phy_device *phydev)
|
|
|
|
-+{
|
|
|
|
-+ return __phy_read(phydev, AN8855_PHY_SELECT_PAGE);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static int an8855_write_page(struct phy_device *phydev, int page)
|
|
|
|
-+{
|
|
|
|
-+ return __phy_write(phydev, AN8855_PHY_SELECT_PAGE, page);
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static struct phy_driver an8855_driver[] = {
|
|
|
|
-+{
|
|
|
|
-+ PHY_ID_MATCH_EXACT(AN8855_PHY_ID),
|
|
|
|
-+ .name = "Airoha AN8855 internal PHY",
|
|
|
|
-+ /* PHY_GBIT_FEATURES */
|
|
|
|
-+ .flags = PHY_IS_INTERNAL,
|
|
|
|
-+ .probe = an8855_probe,
|
|
|
|
-+ .config_init = an8855_config_init,
|
|
|
|
-+ .soft_reset = genphy_soft_reset,
|
|
|
|
-+ .get_tunable = an8855_get_tunable,
|
|
|
|
-+ .set_tunable = an8855_set_tunable,
|
|
|
|
-+ .suspend = genphy_suspend,
|
|
|
|
-+ .resume = genphy_resume,
|
|
|
|
-+ .read_page = an8855_read_page,
|
|
|
|
-+ .write_page = an8855_write_page,
|
|
|
|
-+}, };
|
|
|
|
-+
|
|
|
|
-+module_phy_driver(an8855_driver);
|
|
|
|
-+
|
|
|
|
-+static struct mdio_device_id __maybe_unused an8855_tbl[] = {
|
|
|
|
-+ { PHY_ID_MATCH_EXACT(AN8855_PHY_ID) },
|
|
|
|
-+ { }
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+MODULE_DEVICE_TABLE(mdio, an8855_tbl);
|
|
|
|
-+
|
|
|
|
-+MODULE_DESCRIPTION("Airoha AN8855 PHY driver");
|
|
|
|
-+MODULE_AUTHOR("Christian Marangi <[email protected]>");
|
|
|
|
-+MODULE_LICENSE("GPL");
|
|
|
|
-diff --git a/target/linux/mediatek/filogic/config-6.6 b/target/linux/mediatek/filogic/config-6.6
|
|
|
|
-index 85e7367f41cfe5..aa2d74be4b4364 100644
|
|
|
|
---- a/target/linux/mediatek/filogic/config-6.6
|
|
|
|
-+++ b/target/linux/mediatek/filogic/config-6.6
|
|
|
|
-@@ -1,5 +1,6 @@
|
|
|
|
- CONFIG_64BIT=y
|
|
|
|
- # CONFIG_AHCI_MTK is not set
|
|
|
|
-+CONFIG_AIR_AN8855_PHY=y
|
|
|
|
- CONFIG_AIROHA_EN8801SC_PHY=y
|
|
|
|
- CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y
|
|
|
|
- CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y
|
|
|
|
-@@ -291,6 +292,7 @@ CONFIG_NEED_DMA_MAP_STATE=y
|
|
|
|
- CONFIG_NEED_SG_DMA_LENGTH=y
|
|
|
|
- CONFIG_NET_DEVLINK=y
|
|
|
|
- CONFIG_NET_DSA=y
|
|
|
|
-+CONFIG_NET_DSA_AN8855=y
|
|
|
|
- CONFIG_NET_DSA_MT7530=y
|
|
|
|
- CONFIG_NET_DSA_MT7530_MDIO=y
|
|
|
|
- CONFIG_NET_DSA_MT7530_MMIO=y
|
|
|
|
-diff --git a/target/linux/mediatek/mt7622/config-6.6 b/target/linux/mediatek/mt7622/config-6.6
|
|
|
|
-index ec3be8df9aa574..b18fc848a27fa3 100644
|
|
|
|
---- a/target/linux/mediatek/mt7622/config-6.6
|
|
|
|
-+++ b/target/linux/mediatek/mt7622/config-6.6
|
|
|
|
-@@ -1,5 +1,6 @@
|
|
|
|
- CONFIG_64BIT=y
|
|
|
|
- # CONFIG_AHCI_MTK is not set
|
|
|
|
-+# CONFIG_AIR_AN8855_PHY is not set
|
|
|
|
- # CONFIG_AIROHA_EN8801SC_PHY is not set
|
|
|
|
- CONFIG_AQUANTIA_PHY=y
|
|
|
|
- CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y
|
|
|
|
-@@ -294,6 +295,7 @@ CONFIG_NEED_DMA_MAP_STATE=y
|
|
|
|
- CONFIG_NEED_SG_DMA_LENGTH=y
|
|
|
|
- CONFIG_NET_DEVLINK=y
|
|
|
|
- CONFIG_NET_DSA=y
|
|
|
|
-+# CONFIG_NET_DSA_AN8855 is not set
|
|
|
|
- CONFIG_NET_DSA_MT7530=y
|
|
|
|
- CONFIG_NET_DSA_MT7530_MDIO=y
|
|
|
|
- # CONFIG_NET_DSA_MT7530_MMIO is not set
|
|
|
|
-diff --git a/target/linux/mediatek/mt7623/config-6.6 b/target/linux/mediatek/mt7623/config-6.6
|
|
|
|
-index 6bc92a09dce30e..ce9f817a62a353 100644
|
|
|
|
---- a/target/linux/mediatek/mt7623/config-6.6
|
|
|
|
-+++ b/target/linux/mediatek/mt7623/config-6.6
|
|
|
|
-@@ -1,4 +1,5 @@
|
|
|
|
- # CONFIG_AIO is not set
|
|
|
|
-+# CONFIG_AIR_AN8855_PHY is not set
|
|
|
|
- # CONFIG_AIROHA_EN8801SC_PHY is not set
|
|
|
|
- CONFIG_ALIGNMENT_TRAP=y
|
|
|
|
- CONFIG_ARCH_32BIT_OFF_T=y
|
|
|
|
-@@ -410,6 +411,7 @@ CONFIG_NEED_SRCU_NMI_SAFE=y
|
|
|
|
- CONFIG_NEON=y
|
|
|
|
- CONFIG_NET_DEVLINK=y
|
|
|
|
- CONFIG_NET_DSA=y
|
|
|
|
-+# CONFIG_NET_DSA_AN8855 is not set
|
|
|
|
- CONFIG_NET_DSA_MT7530=y
|
|
|
|
- CONFIG_NET_DSA_MT7530_MDIO=y
|
|
|
|
- # CONFIG_NET_DSA_MT7530_MMIO is not set
|
|
|
|
-diff --git a/target/linux/mediatek/mt7629/config-6.6 b/target/linux/mediatek/mt7629/config-6.6
|
|
|
|
-index 9f57bda3e9e7a7..21c1862e679a35 100644
|
|
|
|
---- a/target/linux/mediatek/mt7629/config-6.6
|
|
|
|
-+++ b/target/linux/mediatek/mt7629/config-6.6
|
|
|
|
-@@ -1,3 +1,4 @@
|
|
|
|
-+# CONFIG_AIR_AN8855_PHY is not set
|
|
|
|
- # CONFIG_AIROHA_EN8801SC_PHY is not set
|
|
|
|
- CONFIG_ALIGNMENT_TRAP=y
|
|
|
|
- CONFIG_ARCH_32BIT_OFF_T=y
|
|
|
|
-@@ -216,6 +217,7 @@ CONFIG_NETFILTER=y
|
|
|
|
- CONFIG_NETFILTER_BPF_LINK=y
|
|
|
|
- CONFIG_NET_DEVLINK=y
|
|
|
|
- CONFIG_NET_DSA=y
|
|
|
|
-+# CONFIG_NET_DSA_AN8855 is not set
|
|
|
|
- CONFIG_NET_DSA_MT7530=y
|
|
|
|
- CONFIG_NET_DSA_MT7530_MDIO=y
|
|
|
|
- # CONFIG_NET_DSA_MT7530_MMIO is not set
|
|
|
|
-diff --git a/target/linux/mediatek/patches-6.6/737-net-dsa-add-Airoha-AN8855.patch b/target/linux/mediatek/patches-6.6/737-net-dsa-add-Airoha-AN8855.patch
|
|
|
|
-new file mode 100644
|
|
|
|
-index 00000000000000..fcfe61997856cb
|
|
|
|
---- /dev/null
|
|
|
|
-+++ b/target/linux/mediatek/patches-6.6/737-net-dsa-add-Airoha-AN8855.patch
|
|
|
|
-@@ -0,0 +1,197 @@
|
|
|
|
-+From: Christian Marangi <[email protected]>
|
|
|
|
-+To: Christian Marangi <[email protected]>,
|
|
|
|
-+ Andrew Lunn <[email protected]>,
|
|
|
|
-+ Florian Fainelli <[email protected]>,
|
|
|
|
-+ Vladimir Oltean <[email protected]>,
|
|
|
|
-+ "David S. Miller" <[email protected]>,
|
|
|
|
-+ Eric Dumazet <[email protected]>,
|
|
|
|
-+ Jakub Kicinski <[email protected]>, Paolo Abeni <[email protected]>,
|
|
|
|
-+ Rob Herring <[email protected]>,
|
|
|
|
-+ Krzysztof Kozlowski <[email protected]>,
|
|
|
|
-+ Conor Dooley <[email protected]>,
|
|
|
|
-+ Heiner Kallweit <[email protected]>,
|
|
|
|
-+ Russell King <[email protected]>,
|
|
|
|
-+ Matthias Brugger <[email protected]>,
|
|
|
|
-+ AngeloGioacchino Del Regno
|
|
|
|
-+ <[email protected]>,
|
|
|
|
-+ [email protected],
|
|
|
|
-+ [email protected], [email protected],
|
|
|
|
-+ [email protected], [email protected],
|
|
|
|
-+ [email protected]
|
|
|
|
-+Subject: [net-next PATCH v4 0/3] net: dsa: Add Airoha AN8855 support
|
|
|
|
-+Date: Fri, 8 Nov 2024 14:24:13 +0100 [thread overview]
|
|
|
|
-+Message-ID: <[email protected]> (raw)
|
|
|
|
-+
|
|
|
|
-+This small series add the initial support for the Airoha AN8855 Switch.
|
|
|
|
-+
|
|
|
|
-+It's a 5 port Gigabit Switch with SGMII/HSGMII upstream port.
|
|
|
|
-+
|
|
|
|
-+This is starting to get in the wild and there are already some router
|
|
|
|
-+having this switch chip.
|
|
|
|
-+
|
|
|
|
-+It's conceptually similar to mediatek switch but register and bits
|
|
|
|
-+are different. And there is that massive Hell that is the PCS
|
|
|
|
-+configuration.
|
|
|
|
-+Saddly for that part we have absolutely NO documentation currently.
|
|
|
|
-+
|
|
|
|
-+There is this special thing where PHY needs to be calibrated with values
|
|
|
|
-+from the switch efuse. (the thing have a whole cpu timer and MCU)
|
|
|
|
-+
|
|
|
|
-+Changes v4:
|
|
|
|
-+- Set regmap readable_table static (mute compilation warning)
|
|
|
|
-+- Add support for port_bridge flags (LEARNING, FLOOD)
|
|
|
|
-+- Reset fdb struct in fdb_dump
|
|
|
|
-+- Drop support_asym_pause in port_enable
|
|
|
|
-+- Add define for get_phy_flags
|
|
|
|
-+- Fix bug for port not inititially part of a bridge
|
|
|
|
-+ (in an8855_setup the port matrix was always cleared but
|
|
|
|
-+ the CPU port was never initially added)
|
|
|
|
-+- Disable learning and flood for user port by default
|
|
|
|
-+- Set CPU port to flood and learning by default
|
|
|
|
-+- Correctly AND force duplex and flow control in an8855_phylink_mac_link_up
|
|
|
|
-+- Drop RGMII from pcs_config
|
|
|
|
-+- Check ret in "Disable AN if not in autoneg"
|
|
|
|
-+- Use devm_mutex_init
|
|
|
|
-+- Fix typo for AN8855_PORT_CHECK_MODE
|
|
|
|
-+- Better define AN8855_STP_LISTENING = AN8855_STP_BLOCKING
|
|
|
|
-+- Fix typo in AN8855_PHY_EN_DOWN_SHIFT
|
|
|
|
-+- Use paged helper for PHY
|
|
|
|
-+- Skip calibration in config_init if priv not defined
|
|
|
|
-+Changes v3:
|
|
|
|
-+- Out of RFC
|
|
|
|
-+- Switch PHY code to select_page API
|
|
|
|
-+- Better describe masks and bits in PHY driver for ADC register
|
|
|
|
-+- Drop raw values and use define for mii read/write
|
|
|
|
-+- Switch to absolute PHY address
|
|
|
|
-+- Replace raw values with mask and bits for pcs_config
|
|
|
|
-+- Fix typo for ext-surge property name
|
|
|
|
-+- Drop support for relocating Switch base PHY address on the bus
|
|
|
|
-+Changes v2:
|
|
|
|
-+- Drop mutex guard patch
|
|
|
|
-+- Drop guard usage in DSA driver
|
|
|
|
-+- Use __mdiobus_write/read
|
|
|
|
-+- Check return condition and return errors for mii read/write
|
|
|
|
-+- Fix wrong logic for EEE
|
|
|
|
-+- Fix link_down (don't force link down with autoneg)
|
|
|
|
-+- Fix forcing speed on sgmii autoneg
|
|
|
|
-+- Better document link speed for sgmii reg
|
|
|
|
-+- Use standard define for sgmii reg
|
|
|
|
-+- Imlement nvmem support to expose switch EFUSE
|
|
|
|
-+- Rework PHY calibration with the use of NVMEM producer/consumer
|
|
|
|
-+- Update DT with new NVMEM property
|
|
|
|
-+- Move aneg validation for 2500-basex in pcs_config
|
|
|
|
-+- Move r50Ohm table and function to PHY driver
|
|
|
|
-+
|
|
|
|
-+Christian Marangi (3):
|
|
|
|
-+ dt-bindings: net: dsa: Add Airoha AN8855 Gigabit Switch documentation
|
|
|
|
-+ net: dsa: Add Airoha AN8855 5-Port Gigabit DSA Switch driver
|
|
|
|
-+ net: phy: Add Airoha AN8855 Internal Switch Gigabit PHY
|
|
|
|
-+
|
|
|
|
-+ .../bindings/net/dsa/airoha,an8855.yaml | 242 ++
|
|
|
|
-+ MAINTAINERS | 11 +
|
|
|
|
-+ drivers/net/dsa/Kconfig | 9 +
|
|
|
|
-+ drivers/net/dsa/Makefile | 1 +
|
|
|
|
-+ drivers/net/dsa/an8855.c | 2138 +++++++++++++++++
|
|
|
|
-+ drivers/net/dsa/an8855.h | 638 +++++
|
|
|
|
-+ drivers/net/phy/Kconfig | 5 +
|
|
|
|
-+ drivers/net/phy/Makefile | 1 +
|
|
|
|
-+ drivers/net/phy/air_an8855.c | 268 +++
|
|
|
|
-+ 9 files changed, 3313 insertions(+)
|
|
|
|
-+ create mode 100644 Documentation/devicetree/bindings/net/dsa/airoha,an8855.yaml
|
|
|
|
-+ create mode 100644 drivers/net/dsa/an8855.c
|
|
|
|
-+ create mode 100644 drivers/net/dsa/an8855.h
|
|
|
|
-+ create mode 100644 drivers/net/phy/air_an8855.c
|
|
|
|
-+
|
|
|
|
-+--
|
|
|
|
-+2.45.2
|
|
|
|
-+
|
|
|
|
-+--- a/drivers/net/dsa/Kconfig
|
|
|
|
-++++ b/drivers/net/dsa/Kconfig
|
|
|
|
-+@@ -24,6 +24,15 @@ config NET_DSA_LOOP
|
|
|
|
-+ This enables support for a fake mock-up switch chip which
|
|
|
|
-+ exercises the DSA APIs.
|
|
|
|
-+
|
|
|
|
-++
|
|
|
|
-++config NET_DSA_AN8855
|
|
|
|
-++ tristate "Airoha AN8855 Ethernet switch support"
|
|
|
|
-++ depends on NET_DSA
|
|
|
|
-++ select NET_DSA_TAG_MTK
|
|
|
|
-++ help
|
|
|
|
-++ This enables support for the Airoha AN8855 Ethernet switch
|
|
|
|
-++ chip.
|
|
|
|
-++
|
|
|
|
-+ source "drivers/net/dsa/hirschmann/Kconfig"
|
|
|
|
-+
|
|
|
|
-+ config NET_DSA_LANTIQ_GSWIP
|
|
|
|
-+--- a/drivers/net/dsa/Makefile
|
|
|
|
-++++ b/drivers/net/dsa/Makefile
|
|
|
|
-+@@ -5,6 +5,7 @@ obj-$(CONFIG_NET_DSA_LOOP) += dsa_loop.o
|
|
|
|
-+ ifdef CONFIG_NET_DSA_LOOP
|
|
|
|
-+ obj-$(CONFIG_FIXED_PHY) += dsa_loop_bdinfo.o
|
|
|
|
-+ endif
|
|
|
|
-++obj-$(CONFIG_NET_DSA_AN8855) += an8855.o
|
|
|
|
-+ obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o
|
|
|
|
-+ obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o
|
|
|
|
-+ obj-$(CONFIG_NET_DSA_MT7530_MDIO) += mt7530-mdio.o
|
|
|
|
-+--- a/drivers/net/phy/Kconfig
|
|
|
|
-++++ b/drivers/net/phy/Kconfig
|
|
|
|
-+@@ -147,6 +147,11 @@ config AIROHA_EN8801SC_PHY
|
|
|
|
-+ help
|
|
|
|
-+ Currently supports the Airoha EN8801SC PHY.
|
|
|
|
-+
|
|
|
|
-++config AIR_AN8855_PHY
|
|
|
|
-++ tristate "Airoha AN8855 Internal Gigabit PHY"
|
|
|
|
-++ help
|
|
|
|
-++ Currently supports the internal Airoha AN8855 Switch PHY.
|
|
|
|
-++
|
|
|
|
-+ config AIR_EN8811H_PHY
|
|
|
|
-+ tristate "Airoha EN8811H 2.5 Gigabit PHY"
|
|
|
|
-+ help
|
|
|
|
-+--- a/drivers/net/phy/Makefile
|
|
|
|
-++++ b/drivers/net/phy/Makefile
|
|
|
|
-+@@ -50,6 +50,7 @@ obj-y += $(sfp-obj-y) $(sfp-obj-m)
|
|
|
|
-+ obj-$(CONFIG_ADIN_PHY) += adin.o
|
|
|
|
-+ obj-$(CONFIG_ADIN1100_PHY) += adin1100.o
|
|
|
|
-+ obj-$(CONFIG_AIROHA_EN8801SC_PHY) += en8801sc.o
|
|
|
|
-++obj-$(CONFIG_AIR_AN8855_PHY) += air_an8855.o
|
|
|
|
-+ obj-$(CONFIG_AIR_EN8811H_PHY) += air_en8811h.o
|
|
|
|
-+ obj-$(CONFIG_AMD_PHY) += amd.o
|
|
|
|
-+ obj-$(CONFIG_AQUANTIA_PHY) += aquantia/
|
|
|
|
-+--- a/include/net/dsa.h
|
|
|
|
-++++ b/include/net/dsa.h
|
|
|
|
-+@@ -1356,6 +1356,7 @@ static inline void dsa_tag_generic_flow_
|
|
|
|
-+
|
|
|
|
-+ void dsa_unregister_switch(struct dsa_switch *ds);
|
|
|
|
-+ int dsa_register_switch(struct dsa_switch *ds);
|
|
|
|
-++int devm_dsa_register_switch(struct device *dev, struct dsa_switch *ds);
|
|
|
|
-+ void dsa_switch_shutdown(struct dsa_switch *ds);
|
|
|
|
-+ struct dsa_switch *dsa_switch_find(int tree_index, int sw_index);
|
|
|
|
-+ void dsa_flush_workqueue(void);
|
|
|
|
-+--- a/net/dsa/dsa.c
|
|
|
|
-++++ b/net/dsa/dsa.c
|
|
|
|
-+@@ -1560,6 +1560,25 @@ int dsa_register_switch(struct dsa_switc
|
|
|
|
-+ }
|
|
|
|
-+ EXPORT_SYMBOL_GPL(dsa_register_switch);
|
|
|
|
-+
|
|
|
|
-++static void devm_dsa_unregister_switch(void *data)
|
|
|
|
-++{
|
|
|
|
-++ struct dsa_switch *ds = data;
|
|
|
|
-++
|
|
|
|
-++ dsa_unregister_switch(ds);
|
|
|
|
-++}
|
|
|
|
-++
|
|
|
|
-++int devm_dsa_register_switch(struct device *dev, struct dsa_switch *ds)
|
|
|
|
-++{
|
|
|
|
-++ int err;
|
|
|
|
-++
|
|
|
|
-++ err = dsa_register_switch(ds);
|
|
|
|
-++ if (err)
|
|
|
|
-++ return err;
|
|
|
|
-++
|
|
|
|
-++ return devm_add_action_or_reset(dev, devm_dsa_unregister_switch, ds);
|
|
|
|
-++}
|
|
|
|
-++EXPORT_SYMBOL_GPL(devm_dsa_register_switch);
|
|
|
|
-++
|
|
|
|
-+ static void dsa_switch_remove(struct dsa_switch *ds)
|
|
|
|
-+ {
|
|
|
|
-+ struct dsa_switch_tree *dst = ds->dst;
|
|
|