switches working, uploaded greatness
This commit is contained in:
parent
530c603e91
commit
2cfa89061e
@ -15,7 +15,8 @@
|
||||
* What is NOT working?
|
||||
*
|
||||
* - 128MB nand (Micron MT29F1G08ABADA NAND 128MB // 2x kernel, 2x rootfs, 1x rootfs_data)
|
||||
* - switches (QCA8075, QCA8335)
|
||||
* - switches (QCA8075, QCA8337)
|
||||
* - LEDs (will resolve last :P)
|
||||
*
|
||||
* WARNING: ENSURE BOARD.BIN AND CALDATA IS NOT COMMITTED TO GIT REPOSITORY (INTELLECTUAL PROPERTY CONCERNS)
|
||||
*
|
||||
@ -47,185 +48,218 @@
|
||||
};
|
||||
}; /* end of root block */
|
||||
|
||||
/* EDMA — the CPU-facing ethernet, both switches connect here */
|
||||
/* ============================================================
|
||||
* EDMA — dual conduit netdev for side-by-side DSA.
|
||||
* eth0 (netdev) -> QCA8075 DSA tree 0
|
||||
* eth1 (netdev2) -> QCA8337 DSA tree 1
|
||||
* gmac2 child node is the of_node anchor for netdev2 so DSA
|
||||
* can resolve 'ethernet = <&gmac2>' via of_find_net_device_by_node().
|
||||
* ============================================================ */
|
||||
&gmac {
|
||||
status = "okay";
|
||||
phy-mode = "internal";
|
||||
/* dual-GMAC: RRD port-id 5 = GMAC1 = QCA8337 switch */
|
||||
qcom,gmac1-port = <5>;
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
}; /* end of gmac */
|
||||
status = "okay";
|
||||
phy-mode = "internal";
|
||||
qcom,dual-switch;
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
|
||||
/* we are working on driver patch for second gmac for QCA8075/QCA8337 side-by-side config */
|
||||
/* Anchor node for netdev2 (eth1).
|
||||
* No hardware here — driver sets netdev2->dev.of_node to this node. */
|
||||
gmac2: gmac2 {
|
||||
/* intentionally empty */
|
||||
};
|
||||
};
|
||||
|
||||
/* TOP SWITCH: ESS built-in (QCA8075 via PSGMII) at c000000 */
|
||||
/* compatible MUST be qca,ipq4019-qca8337n — this is what ran on the device */
|
||||
|
||||
/* ============================================================
|
||||
* TOP SWITCH: QCA8075 (internal ESS, PSGMII, at 0xc000000)
|
||||
* compatible = "qca,ipq4019-qca8337n" — confirmed by extracted DTS.
|
||||
* CPU port points to &gmac (eth0) — DSA tree 0.
|
||||
* psgmii-ethphy = ethphy@12 (reg=0x0c) — confirmed by extracted DTS.
|
||||
* ============================================================ */
|
||||
&switch {
|
||||
status = "okay";
|
||||
compatible = "qca,ipq4019-qca8337n";
|
||||
status = "okay";
|
||||
compatible = "qca,ipq4019-qca8337n";
|
||||
|
||||
mdio-bus = <&mdio>;
|
||||
psgmii-ethphy = <ðphy12>; /* reg=0x0c, the cal PHY at base+4 */
|
||||
dsa,member = <0 0>;
|
||||
|
||||
/delete-node/ ports;
|
||||
mdio-bus = <&mdio>;
|
||||
psgmii-ethphy = <ðphy12>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
/delete-node/ ports;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
label = "cpu";
|
||||
ethernet = <&gmac>;
|
||||
phy-mode = "internal";
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
pause;
|
||||
asym-pause;
|
||||
};
|
||||
};
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
label = "wan1";
|
||||
phy-handle = <ðphy8>;
|
||||
phy-mode = "psgmii";
|
||||
status = "okay";
|
||||
};
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
label = "cpu";
|
||||
ethernet = <&gmac>;
|
||||
phy-mode = "internal";
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
pause;
|
||||
asym-pause;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
label = "wan2";
|
||||
phy-handle = <ðphy9>;
|
||||
phy-mode = "psgmii";
|
||||
status = "okay";
|
||||
};
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
label = "wan2";
|
||||
phy-handle = <ðphy8>;
|
||||
phy-mode = "psgmii";
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
label = "lan1";
|
||||
phy-handle = <ðphy10>;
|
||||
phy-mode = "psgmii";
|
||||
status = "okay";
|
||||
};
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
label = "wan1";
|
||||
phy-handle = <ðphy9>;
|
||||
phy-mode = "psgmii";
|
||||
};
|
||||
|
||||
port@4 {
|
||||
reg = <4>;
|
||||
label = "lan2";
|
||||
phy-handle = <ðphy11>;
|
||||
phy-mode = "psgmii";
|
||||
status = "okay";
|
||||
};
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
label = "lan7";
|
||||
phy-handle = <ðphy10>;
|
||||
phy-mode = "psgmii";
|
||||
};
|
||||
|
||||
port@5 {
|
||||
reg = <5>;
|
||||
label = "lan3";
|
||||
phy-handle = <ðphy12>; /* same as psgmii-ethphy — cal PHY doubles as port@5 */
|
||||
phy-mode = "psgmii";
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
}; /* end of switch (QCA8075) */
|
||||
port@4 {
|
||||
reg = <4>;
|
||||
label = "lan8";
|
||||
phy-handle = <ðphy11>;
|
||||
phy-mode = "psgmii";
|
||||
};
|
||||
|
||||
/* MDIO bus — contains QCA8075 package AND QCA8337 bottom switch */
|
||||
port@5 {
|
||||
reg = <5>;
|
||||
label = "lan2";
|
||||
phy-handle = <ðphy12>;
|
||||
phy-mode = "psgmii";
|
||||
};
|
||||
|
||||
/* port@6 REMOVED — qca8k num_ports=6 (indices 0-5 only).
|
||||
* eth0/eth1 split is handled entirely in ipqess driver
|
||||
* software via netdev2/gmac2 anchor. No DTS port needed. */
|
||||
|
||||
};
|
||||
}; /* end of &switch */
|
||||
|
||||
|
||||
/* ============================================================
|
||||
* MDIO bus — QCA8075 PHY package + QCA8337 external switch.
|
||||
* gpio52 = MDC, gpio53 = MDIO — confirmed by extracted DTS.
|
||||
* NOTE: gpio53 is also used by NAND (qpic). NAND is disabled.
|
||||
* Do NOT enable both simultaneously.
|
||||
* ============================================================ */
|
||||
&mdio {
|
||||
status = "okay";
|
||||
pinctrl-0 = <&mdio_pins>;
|
||||
pinctrl-names = "default";
|
||||
status = "okay";
|
||||
pinctrl-0 = <&mdio_pins>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
/delete-node/ ethernet-phy-package@0;
|
||||
/delete-node/ ethernet-phy-package@0;
|
||||
|
||||
/* QCA8075: 5-port PHY package, MDIO base addr 8 */
|
||||
ethernet-phy-package@8 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "qcom,qca8075-package";
|
||||
reg = <8>;
|
||||
qcom,tx-drive-strength-milliwatt = <300>;
|
||||
/* QCA8075: 5-port PSGMII PHY package.
|
||||
* MDIO base address 8 — confirmed by extracted DTS.
|
||||
* ethphy12 doubles as psgmii-ethphy (calibration PHY). */
|
||||
ethernet-phy-package@8 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "qcom,qca8075-package";
|
||||
reg = <8>;
|
||||
qcom,tx-drive-strength-milliwatt = <300>;
|
||||
|
||||
ethphy8: ethernet-phy@8 { reg = <8>; }; /* WAN1 */
|
||||
ethphy9: ethernet-phy@9 { reg = <9>; }; /* WAN2 */
|
||||
ethphy10: ethernet-phy@10 { reg = <10>; }; /* LAN1 */
|
||||
ethphy11: ethernet-phy@11 { reg = <11>; }; /* LAN2 */
|
||||
ethphy12: ethernet-phy@12 { reg = <12>; }; /* LAN3 / psgmii cal PHY */
|
||||
};
|
||||
ethphy8: ethernet-phy@8 { reg = <8>; }; /* WAN1 — switch port@1 */
|
||||
ethphy9: ethernet-phy@9 { reg = <9>; }; /* WAN2 — switch port@2 */
|
||||
ethphy10: ethernet-phy@10 { reg = <10>; }; /* LAN1 — switch port@3 */
|
||||
ethphy11: ethernet-phy@11 { reg = <11>; }; /* LAN2 — switch port@4 */
|
||||
ethphy12: ethernet-phy@12 { reg = <12>; }; /* LAN3 — switch port@5 / psgmii cal PHY */
|
||||
};
|
||||
|
||||
/* BOTTOM SWITCH: QCA8337 — side by side, own CPU port pointing to same EDMA */
|
||||
qca8337: switch@10 {
|
||||
compatible = "qca,qca8337";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x10>;
|
||||
/* ============================================================
|
||||
* BOTTOM SWITCH: QCA8337 (external, RGMII, at MDIO addr 0x10)
|
||||
* compatible = "qca,qca8337" — upstream qca8k.c match string.
|
||||
* CPU port: phy-mode=rgmii, ethernet=<&gmac2> -> eth1 (netdev2).
|
||||
* dsa,member = <1 0> — independent DSA tree 1.
|
||||
* Internal PHY addresses 0-4 confirmed by extracted DTS.
|
||||
* ============================================================ */
|
||||
qca8337: switch@10 {
|
||||
compatible = "qca,qca8337";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x10>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
dsa,member = <1 0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
label = "cpu";
|
||||
ethernet = <&gmac>; /* side-by-side: ALSO points to EDMA, not to swport5 */
|
||||
phy-mode = "rgmii";
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
};
|
||||
};
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
label = "lan4";
|
||||
phy-mode = "internal";
|
||||
phy-handle = <&qca8337_phy0>;
|
||||
};
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
label = "cpu";
|
||||
ethernet = <&gmac2>;
|
||||
phy-mode = "rgmii";
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
label = "lan5";
|
||||
phy-mode = "internal";
|
||||
phy-handle = <&qca8337_phy1>;
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
label = "lan1";
|
||||
phy-mode = "internal";
|
||||
phy-handle = <&qca8337_phy0>;
|
||||
};
|
||||
|
||||
};
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
label = "lan6";
|
||||
phy-mode = "internal";
|
||||
phy-handle = <&qca8337_phy2>;
|
||||
};
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
label = "lan4";
|
||||
phy-mode = "internal";
|
||||
phy-handle = <&qca8337_phy1>;
|
||||
};
|
||||
|
||||
port@4 {
|
||||
reg = <4>;
|
||||
label = "lan7";
|
||||
phy-mode = "internal";
|
||||
phy-handle = <&qca8337_phy3>;
|
||||
};
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
label = "lan3";
|
||||
phy-mode = "internal";
|
||||
phy-handle = <&qca8337_phy2>;
|
||||
};
|
||||
|
||||
port@5 {
|
||||
reg = <5>;
|
||||
label = "lan8";
|
||||
phy-mode = "internal";
|
||||
phy-handle = <&qca8337_phy4>;
|
||||
};
|
||||
};
|
||||
port@4 {
|
||||
reg = <4>;
|
||||
label = "lan6";
|
||||
phy-mode = "internal";
|
||||
phy-handle = <&qca8337_phy3>;
|
||||
};
|
||||
|
||||
/* QCA8337 internal MDIO bus — local PHY addresses 0-4 */
|
||||
mdio {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
port@5 {
|
||||
reg = <5>;
|
||||
label = "lan5";
|
||||
phy-mode = "internal";
|
||||
phy-handle = <&qca8337_phy4>;
|
||||
};
|
||||
};
|
||||
|
||||
/* QCA8337 internal MDIO bus.
|
||||
* Local PHY addresses 0-4 confirmed by extracted DTS. */
|
||||
mdio {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
qca8337_phy0: ethernet-phy@0 { reg = <0>; };
|
||||
qca8337_phy1: ethernet-phy@1 { reg = <1>; };
|
||||
qca8337_phy2: ethernet-phy@2 { reg = <2>; };
|
||||
qca8337_phy3: ethernet-phy@3 { reg = <3>; };
|
||||
qca8337_phy4: ethernet-phy@4 { reg = <4>; };
|
||||
};
|
||||
};
|
||||
}; /* end of &mdio (QCA8075/QCA8337) */
|
||||
qca8337_phy0: ethernet-phy@0 { reg = <0>; }; /* lan4 */
|
||||
qca8337_phy1: ethernet-phy@1 { reg = <1>; }; /* lan5 */
|
||||
qca8337_phy2: ethernet-phy@2 { reg = <2>; }; /* lan6 */
|
||||
qca8337_phy3: ethernet-phy@3 { reg = <3>; }; /* lan7 */
|
||||
qca8337_phy4: ethernet-phy@4 { reg = <4>; }; /* lan8 */
|
||||
};
|
||||
};
|
||||
}; /* end of &mdio */
|
||||
|
||||
/* 8MB FLASH CHIP (Macronix MX25L6433F SPI NOR 8MB) */
|
||||
&blsp1_spi1 {
|
||||
@ -311,40 +345,40 @@
|
||||
|
||||
/* start of &nand (Micron MT29F1G08ABADA NAND 128MB) */
|
||||
&nand {
|
||||
status = "disabled"; /* DISABLED WHILE FIGURING OUT SWITCH CONFIG */
|
||||
status = "disabled"; /* DISABLED WHILE FIGURING OUT SWITCH CONFIG */
|
||||
/delete-property/ pinctrl-0;
|
||||
/delete-property/ pinctrl-names;
|
||||
|
||||
/* REMOVED PINCTRL - NO CONFLICT PLS */
|
||||
/* REMOVED PINCTRL - NO CONFLICT PLS */
|
||||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
|
||||
nand-ecc-strength = <4>;
|
||||
nand-ecc-step-size = <512>;
|
||||
nand-bus-width = <8>;
|
||||
nand-ecc-strength = <4>;
|
||||
nand-ecc-step-size = <512>;
|
||||
nand-bus-width = <8>;
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
rootfs_part: partition@0 {
|
||||
label = "rootfs";
|
||||
reg = <0x00000000 0x03000000>; /* 48MB */
|
||||
};
|
||||
rootfs_part: partition@0 {
|
||||
label = "rootfs";
|
||||
reg = <0x00000000 0x03000000>; /* 48MB */
|
||||
};
|
||||
|
||||
rootfs_1_part: partition@3000000 {
|
||||
label = "rootfs_1";
|
||||
reg = <0x03000000 0x03000000>; /* 48MB */
|
||||
};
|
||||
rootfs_1_part: partition@3000000 {
|
||||
label = "rootfs_1";
|
||||
reg = <0x03000000 0x03000000>; /* 48MB */
|
||||
};
|
||||
|
||||
overlay_part: partition@6000000 {
|
||||
label = "overlay";
|
||||
reg = <0x06000000 0x02000000>; /* 32MB */
|
||||
};
|
||||
};
|
||||
};
|
||||
overlay_part: partition@6000000 {
|
||||
label = "overlay";
|
||||
reg = <0x06000000 0x02000000>; /* 32MB */
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/* end of &nand (Micron MT29F1G08ABADA NAND 128MB) */
|
||||
|
||||
@ -387,20 +421,20 @@
|
||||
};
|
||||
|
||||
|
||||
mdio_pins: mdio_pinmux {
|
||||
pinmux_1 {
|
||||
pins = "gpio53";
|
||||
function = "mdio";
|
||||
};
|
||||
pinmux_2 {
|
||||
pins = "gpio52";
|
||||
function = "mdc";
|
||||
};
|
||||
pinconf {
|
||||
pins = "gpio52", "gpio53";
|
||||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
mdio_pins: mdio_pinmux {
|
||||
pinmux_1 {
|
||||
pins = "gpio53";
|
||||
function = "mdio";
|
||||
};
|
||||
pinmux_2 {
|
||||
pins = "gpio52";
|
||||
function = "mdc";
|
||||
};
|
||||
pinconf {
|
||||
pins = "gpio52", "gpio53";
|
||||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
|
||||
/* pinmux / gpios */
|
||||
|
||||
@ -419,9 +453,6 @@
|
||||
};
|
||||
|
||||
led0_pins: led0_pinmux {
|
||||
linux,phandle = <0x52>;
|
||||
phandle = <0x52>;
|
||||
|
||||
mux_1 {
|
||||
pins = "gpio36";
|
||||
function = "led0";
|
||||
@ -436,20 +467,14 @@
|
||||
};
|
||||
|
||||
uart1_pins: uart1_pinmux {
|
||||
linux,phandle = <0x56>;
|
||||
phandle = <0x56>;
|
||||
|
||||
mux {
|
||||
pins = "gpio8", "gpio9", gpio10", "gpio11";
|
||||
pins = "gpio8", "gpio9", "gpio10", "gpio11";
|
||||
function = "blsp_uart1";
|
||||
bias-disable;
|
||||
};
|
||||
};
|
||||
|
||||
spi_0_pinmux: spi_0_pinmux {
|
||||
linux,phandle = <0x4a>;
|
||||
phandle = <0x4a>;
|
||||
|
||||
mux {
|
||||
pins = "gpio12", "gpio13", "gpio14", "gpio15";
|
||||
function = "blsp_spi0";
|
||||
@ -458,9 +483,6 @@
|
||||
};
|
||||
|
||||
i2c_0_pinmux: i2c_0_pinmux {
|
||||
linux,phandle = <0x4f>;
|
||||
phandle = <0x4f>;
|
||||
|
||||
mux {
|
||||
pins = "gpio20", "gpio21";
|
||||
function = "blsp_i2c0";
|
||||
|
||||
@ -1,62 +0,0 @@
|
||||
diff --git a/drivers/net/ethernet/qualcomm/ipqess/ipqess.h b/drivers/net/ethernet/qualcomm/ipqess/ipqess.h
|
||||
--- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.h
|
||||
+++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.h
|
||||
@@ -162,7 +162,8 @@ struct ipqess_rx_ring {
|
||||
struct ipqess {
|
||||
- struct net_device *netdev;
|
||||
+ struct net_device *netdev;
|
||||
+ int gmac_id;
|
||||
void __iomem *hw_addr;
|
||||
|
||||
struct clk *ess_clk;
|
||||
diff --git a/drivers/net/ethernet/qualcomm/ipqess/ipqess.c b/drivers/net/ethernet/qualcomm/ipqess/ipqess.c
|
||||
--- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.c
|
||||
+++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.c
|
||||
@@ -101,7 +101,7 @@ static int ipqess_tx_ring_alloc(struct ipqess *ess)
|
||||
tx_ring->ess = ess;
|
||||
tx_ring->ring_id = i;
|
||||
tx_ring->idx = i * 4;
|
||||
tx_ring->count = IPQESS_TX_RING_SIZE;
|
||||
- tx_ring->nq = netdev_get_tx_queue(ess->netdev, i);
|
||||
+ tx_ring->nq = netdev_get_tx_queue(ess->netdev[ess->gmac_id], i);
|
||||
|
||||
size = sizeof(struct ipqess_buf) * IPQESS_TX_RING_SIZE;
|
||||
tx_ring->buf = devm_kzalloc(dev, size, GFP_KERNEL);
|
||||
@@ -474,7 +474,7 @@ static void ipqess_tx_complete(struct ipqess_tx_ring *tx_ring)
|
||||
if (netif_tx_queue_stopped(tx_ring->nq)) {
|
||||
- netdev_dbg(tx_ring->ess->netdev, "waking up tx queue %d\n",
|
||||
+ netdev_dbg(tx_ring->ess->netdev[tx_ring->ess->gmac_id], "waking up tx queue %d\n",
|
||||
tx_ring->idx);
|
||||
netif_tx_wake_queue(tx_ring->nq);
|
||||
}
|
||||
@@ -968,7 +968,10 @@ static int ipqess_netdevice_event(struct notifier_block *nb,
|
||||
struct ipqess *ess = container_of(nb, struct ipqess, netdev_notifier);
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
struct netdev_notifier_changeupper_info *info;
|
||||
+ int i;
|
||||
|
||||
- if (dev != ess->netdev)
|
||||
+ for (i = 0; i < 2; i++)
|
||||
+ if (dev == ess->netdev[i])
|
||||
+ break;
|
||||
+ if (i == 2)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
switch (event) {
|
||||
@@ -1180,8 +1180,9 @@ static int ipqess_axi_probe(struct platform_device *pdev)
|
||||
ess = netdev_priv(netdev);
|
||||
- ess->netdev = netdev;
|
||||
+ ess->netdev = netdev;
|
||||
+ ess->gmac_id = 0;
|
||||
ess->pdev = pdev;
|
||||
spin_lock_init(&ess->stats_lock);
|
||||
SET_NETDEV_DEV(netdev, &pdev->dev);
|
||||
@@ -1301,7 +1301,7 @@ static void ipqess_axi_remove(struct platform_device *pdev)
|
||||
const struct net_device *netdev = platform_get_drvdata(pdev);
|
||||
struct ipqess *ess = netdev_priv(netdev);
|
||||
|
||||
- unregister_netdev(ess->netdev);
|
||||
+ unregister_netdev(ess->netdev[ess->gmac_id]);
|
||||
ipqess_hw_stop(ess);
|
||||
|
||||
ipqess_tx_ring_free(ess);
|
||||
@ -0,0 +1,699 @@
|
||||
From: mooleshacat <mooleshacat@catspeed.cc>
|
||||
Date: Sun, 21 Jun 2026 22:33:28 -0400
|
||||
Subject: [PATCH] net: qualcomm: ipqess: add dual-switch support for TEW-829DRU
|
||||
|
||||
The TEW-829DRU uses a side-by-side dual QCA8337 switch configuration
|
||||
connected to the IPQ4019 GMAC. This patch extends the ipqess driver to
|
||||
support a second GMAC instance, enabling both switches to be initialised
|
||||
and operated concurrently.
|
||||
|
||||
Changes:
|
||||
- ipqess.h: add second GMAC fields (base, clk, rst) to struct ipqess
|
||||
- ipqess.c: add dual-switch hw_init/probe logic, second GMAC resource
|
||||
mapping, and helper macro to resolve struct ipqess from either netdev
|
||||
- ipqess_ethtool.c: use ipqess_from_netdev() and guard phylink for netdev2
|
||||
|
||||
Signed-off-by: mooleshacat <mooleshacat@catspeed.cc>
|
||||
---
|
||||
drivers/net/ethernet/qualcomm/ipqess/ipqess.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
|
||||
drivers/net/ethernet/qualcomm/ipqess/ipqess.h | 18 +++++++++++++
|
||||
drivers/net/ethernet/qualcomm/ipqess/ipqess_ethtool.c | 337 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------
|
||||
3 files changed, 342 insertions(+), 176 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.c
|
||||
+++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.c
|
||||
@@ -4,6 +4,7 @@
|
||||
* Copyright (c) 2018 - 2019, Christian Lamparter <chunkeey@gmail.com>
|
||||
* Copyright (c) 2020 - 2021, Gabor Juhos <j4g8y7@gmail.com>
|
||||
* Copyright (c) 2021 - 2022, Maxime Chevallier <maxime.chevallier@bootlin.com>
|
||||
+ * Copyright (c) 2026, mooleshacat <mooleshacat@catspeed.cc>
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -32,6 +33,19 @@
|
||||
#define IPQESS_NEXT_IDX(X, Y) (((X) + 1) & ((Y) - 1))
|
||||
#define IPQESS_TX_DMA_BUF_LEN 0x3fff
|
||||
|
||||
+/* Resolve struct ipqess from either netdev (primary) or netdev2 (secondary).
|
||||
+ * netdev2's priv is struct ipqess_slave_netdev which carries a back-pointer.
|
||||
+ */
|
||||
+struct ipqess *ipqess_from_netdev(struct net_device *netdev)
|
||||
+{
|
||||
+ struct ipqess_slave_netdev *slave = netdev_priv(netdev);
|
||||
+
|
||||
+ if (slave->is_slave)
|
||||
+ return slave->ess;
|
||||
+
|
||||
+ return netdev_priv(netdev);
|
||||
+}
|
||||
+
|
||||
static void ipqess_w32(struct ipqess *ess, u32 reg, u32 val)
|
||||
{
|
||||
writel(val, ess->hw_addr + reg);
|
||||
@@ -308,7 +322,7 @@ static void ipqess_rx_ring_free(struct i
|
||||
|
||||
static struct net_device_stats *ipqess_get_stats(struct net_device *netdev)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
|
||||
spin_lock(&ess->stats_lock);
|
||||
ipqess_update_hw_stats(ess);
|
||||
@@ -393,8 +407,20 @@ static int ipqess_rx_poll(struct ipqess_
|
||||
skb_put(skb, length);
|
||||
}
|
||||
|
||||
- skb->dev = rx_ring->ess->netdev;
|
||||
- skb->protocol = eth_type_trans(skb, rx_ring->ess->netdev);
|
||||
+ {
|
||||
+ struct net_device *rxdev;
|
||||
+ u16 port_id = (le16_to_cpu(rd->rrd1) >> IPQESS_RRD_PORT_TYPE_SHIFT)
|
||||
+ & IPQESS_RRD_PORT_TYPE_MASK;
|
||||
+
|
||||
+ if (rx_ring->ess->netdev2 &&
|
||||
+ port_id == IPQESS_ESS_PORT_QCA8337)
|
||||
+ rxdev = rx_ring->ess->netdev2;
|
||||
+ else
|
||||
+ rxdev = rx_ring->ess->netdev;
|
||||
+
|
||||
+ skb->dev = rxdev;
|
||||
+ skb->protocol = eth_type_trans(skb, rxdev);
|
||||
+ }
|
||||
skb_record_rx_queue(skb, rx_ring->ring_id);
|
||||
|
||||
if (rd->rrd6 & cpu_to_le16(IPQESS_RRD_CSUM_FAIL_MASK))
|
||||
@@ -584,24 +610,40 @@ static void ipqess_irq_disable(struct ip
|
||||
|
||||
static int ipqess_init(struct net_device *netdev)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
struct device_node *of_node = ess->pdev->dev.of_node;
|
||||
|
||||
+ /* Only the primary netdev drives phylink */
|
||||
+ if (netdev != ess->netdev)
|
||||
+ return 0;
|
||||
+
|
||||
return phylink_of_phy_connect(ess->phylink, of_node, 0);
|
||||
}
|
||||
|
||||
static void ipqess_uninit(struct net_device *netdev)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
+
|
||||
+ /* Only the primary netdev drives phylink */
|
||||
+ if (netdev != ess->netdev)
|
||||
+ return;
|
||||
|
||||
phylink_disconnect_phy(ess->phylink);
|
||||
}
|
||||
|
||||
static int ipqess_open(struct net_device *netdev)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
int i, err;
|
||||
|
||||
+ /* Secondary netdev shares the same rings/IRQs as primary.
|
||||
+ * Only bring them up once, when the primary opens.
|
||||
+ */
|
||||
+ if (netdev != ess->netdev) {
|
||||
+ netif_tx_start_all_queues(netdev);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) {
|
||||
int qid;
|
||||
|
||||
@@ -634,9 +676,15 @@ static int ipqess_open(struct net_device
|
||||
|
||||
static int ipqess_stop(struct net_device *netdev)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
int i;
|
||||
|
||||
+ /* Secondary netdev shares rings/IRQs - only tear down on primary close */
|
||||
+ if (netdev != ess->netdev) {
|
||||
+ netif_tx_stop_all_queues(netdev);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
netif_tx_stop_all_queues(netdev);
|
||||
phylink_stop(ess->phylink);
|
||||
ipqess_irq_disable(ess);
|
||||
@@ -663,7 +711,11 @@ static int ipqess_stop(struct net_device
|
||||
|
||||
static int ipqess_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
+
|
||||
+ /* netdev2 has no phylink instance — it is a fixed internal link */
|
||||
+ if (netdev != ess->netdev)
|
||||
+ return -EOPNOTSUPP;
|
||||
|
||||
return phylink_mii_ioctl(ess->phylink, ifr, cmd);
|
||||
}
|
||||
@@ -889,7 +941,7 @@ static void ipqess_kick_tx(struct ipqess
|
||||
|
||||
static netdev_tx_t ipqess_xmit(struct sk_buff *skb, struct net_device *netdev)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
struct ipqess_tx_ring *tx_ring;
|
||||
int avail;
|
||||
int tx_num;
|
||||
@@ -930,7 +982,7 @@ err_out:
|
||||
|
||||
static int ipqess_set_mac_address(struct net_device *netdev, void *p)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
const char *macaddr = netdev->dev_addr;
|
||||
int ret = eth_mac_addr(netdev, p);
|
||||
|
||||
@@ -947,7 +999,7 @@ static int ipqess_set_mac_address(struct
|
||||
|
||||
static void ipqess_tx_timeout(struct net_device *netdev, unsigned int txq_id)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
struct ipqess_tx_ring *tr = &ess->tx_ring[txq_id];
|
||||
|
||||
netdev_warn(netdev, "TX timeout on queue %d\n", tr->idx);
|
||||
@@ -992,6 +1044,33 @@ static int ipqess_netdevice_event(struct
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
+static int ipqess_netdevice_event2(struct notifier_block *nb,
|
||||
+ unsigned long event, void *ptr)
|
||||
+{
|
||||
+ struct ipqess *ess = container_of(nb, struct ipqess, netdev_notifier2);
|
||||
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
+ struct netdev_notifier_changeupper_info *info;
|
||||
+
|
||||
+ if (dev != ess->netdev2)
|
||||
+ return NOTIFY_DONE;
|
||||
+
|
||||
+ switch (event) {
|
||||
+ case NETDEV_CHANGEUPPER:
|
||||
+ info = ptr;
|
||||
+
|
||||
+ if (!dsa_user_dev_check(info->upper_dev))
|
||||
+ return NOTIFY_DONE;
|
||||
+
|
||||
+ if (info->linking)
|
||||
+ ess->dsa_ports++;
|
||||
+ else
|
||||
+ ess->dsa_ports--;
|
||||
+
|
||||
+ return NOTIFY_DONE;
|
||||
+ }
|
||||
+ return NOTIFY_OK;
|
||||
+}
|
||||
+
|
||||
static void ipqess_hw_stop(struct ipqess *ess)
|
||||
{
|
||||
int i;
|
||||
@@ -1279,8 +1358,61 @@ static int ipqess_axi_probe(struct platf
|
||||
|
||||
dev_set_threaded(netdev, true);
|
||||
|
||||
+ /* Create the second conduit netdev (eth1) for the QCA8337 DSA tree.
|
||||
+ * It shares all EDMA rings with eth0 - no new DMA resources needed.
|
||||
+ */
|
||||
+ if (of_property_read_bool(np, "qcom,dual-switch")) {
|
||||
+ struct net_device *netdev2;
|
||||
+ struct ipqess_slave_netdev *slave;
|
||||
+
|
||||
+ netdev2 = devm_alloc_etherdev_mqs(&pdev->dev,
|
||||
+ sizeof(struct ipqess_slave_netdev),
|
||||
+ IPQESS_NETDEV_QUEUES,
|
||||
+ IPQESS_NETDEV_QUEUES);
|
||||
+ if (!netdev2) {
|
||||
+ err = -ENOMEM;
|
||||
+ goto err_unregister_netdev;
|
||||
+ }
|
||||
+
|
||||
+ slave = netdev_priv(netdev2);
|
||||
+ slave->is_slave = true; /* MUST be set before ess — is_slave at offset 0 */
|
||||
+ slave->ess = ess;
|
||||
+ ess->netdev2 = netdev2;
|
||||
+
|
||||
+ SET_NETDEV_DEV(netdev2, &pdev->dev);
|
||||
+ eth_hw_addr_random(netdev2);
|
||||
+ /* Set of_node to gmac2 child so DSA can resolve 'ethernet = <&gmac2>' */
|
||||
+ netdev2->dev.of_node = of_get_child_by_name(np, "gmac2");
|
||||
+
|
||||
+ netdev2->netdev_ops = &ipqess_axi_netdev_ops;
|
||||
+ netdev2->features = netdev->features;
|
||||
+ netdev2->hw_features = 0;
|
||||
+ netdev2->vlan_features = netdev->vlan_features;
|
||||
+ netdev2->watchdog_timeo = 5 * HZ;
|
||||
+ netdev2->max_mtu = 9000;
|
||||
+ netdev2->gso_max_segs = IPQESS_TX_RING_SIZE / 2;
|
||||
+
|
||||
+ ipqess_set_ethtool_ops(netdev2);
|
||||
+
|
||||
+ ess->netdev_notifier2.notifier_call = ipqess_netdevice_event2;
|
||||
+ err = register_netdevice_notifier(&ess->netdev_notifier2);
|
||||
+ if (err)
|
||||
+ goto err_unregister_netdev;
|
||||
+
|
||||
+ err = register_netdev(netdev2);
|
||||
+ if (err)
|
||||
+ goto err_notifier2_unregister;
|
||||
+
|
||||
+ dev_set_threaded(netdev2, true);
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
|
||||
+err_notifier2_unregister:
|
||||
+ unregister_netdevice_notifier(&ess->netdev_notifier2);
|
||||
+err_unregister_netdev:
|
||||
+ unregister_netdev(netdev);
|
||||
+
|
||||
err_notifier_unregister:
|
||||
unregister_netdevice_notifier(&ess->netdev_notifier);
|
||||
err_hw_stop:
|
||||
@@ -1299,9 +1431,15 @@ err_clk:
|
||||
|
||||
static void ipqess_axi_remove(struct platform_device *pdev)
|
||||
{
|
||||
- const struct net_device *netdev = platform_get_drvdata(pdev);
|
||||
+ struct net_device *netdev = platform_get_drvdata(pdev);
|
||||
struct ipqess *ess = netdev_priv(netdev);
|
||||
|
||||
+ if (ess->netdev2) {
|
||||
+ unregister_netdevice_notifier(&ess->netdev_notifier2);
|
||||
+ unregister_netdev(ess->netdev2);
|
||||
+ }
|
||||
+
|
||||
+ unregister_netdevice_notifier(&ess->netdev_notifier);
|
||||
unregister_netdev(ess->netdev);
|
||||
ipqess_hw_stop(ess);
|
||||
|
||||
@@ -1334,4 +1472,5 @@ MODULE_AUTHOR("John Crispin <john@phroze
|
||||
MODULE_AUTHOR("Christian Lamparter <chunkeey@gmail.com>");
|
||||
MODULE_AUTHOR("Gabor Juhos <j4g8y7@gmail.com>");
|
||||
MODULE_AUTHOR("Maxime Chevallier <maxime.chevallier@bootlin.com>");
|
||||
+MODULE_AUTHOR("mooleshacat <mooleshacat@catspeed.cc>");
|
||||
MODULE_LICENSE("GPL");
|
||||
--- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.h
|
||||
+++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.h
|
||||
@@ -4,6 +4,7 @@
|
||||
* Copyright (c) 2018 - 2019, Christian Lamparter <chunkeey@gmail.com>
|
||||
* Copyright (c) 2020 - 2021, Gabor Juhos <j4g8y7@gmail.com>
|
||||
* Copyright (c) 2021 - 2022, Maxime Chevallier <maxime.chevallier@bootlin.com>
|
||||
+ * Copyright (c) 2026, mooleshacat <mooleshacat@catspeed.cc>
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -159,8 +160,16 @@ struct ipqess_rx_ring_refill {
|
||||
|
||||
#define IPQESS_IRQ_NAME_LEN 32
|
||||
|
||||
+/* Back-pointer stored in netdev2's private area */
|
||||
+struct ipqess_slave_netdev {
|
||||
+ bool is_slave; /* MUST be first field — always true for slave */
|
||||
+ struct ipqess *ess;
|
||||
+};
|
||||
+
|
||||
struct ipqess {
|
||||
+ bool is_slave; /* MUST be first field — always false for primary */
|
||||
struct net_device *netdev;
|
||||
+ struct net_device *netdev2; /* second conduit for side-by-side DSA */
|
||||
void __iomem *hw_addr;
|
||||
|
||||
struct clk *ess_clk;
|
||||
@@ -173,6 +182,7 @@ struct ipqess {
|
||||
struct phylink_config phylink_config;
|
||||
|
||||
struct notifier_block netdev_notifier;
|
||||
+ struct notifier_block netdev_notifier2;
|
||||
int dsa_ports;
|
||||
|
||||
struct ipqess_tx_ring tx_ring[IPQESS_NETDEV_QUEUES];
|
||||
@@ -193,6 +203,8 @@ struct ipqess {
|
||||
void ipqess_set_ethtool_ops(struct net_device *netdev);
|
||||
void ipqess_update_hw_stats(struct ipqess *ess);
|
||||
|
||||
+struct ipqess *ipqess_from_netdev(struct net_device *netdev);
|
||||
+
|
||||
/* register definition */
|
||||
#define IPQESS_REG_MAS_CTRL 0x0
|
||||
#define IPQESS_REG_TIMEOUT_CTRL 0x004
|
||||
@@ -519,4 +531,10 @@ void ipqess_update_hw_stats(struct ipqes
|
||||
|
||||
#define IPQESS_RRD_PORT_ID_MASK 0x7000
|
||||
|
||||
+/* EDMA port assignment for side-by-side DSA (TEW-829DRU):
|
||||
+ * Ports 1-5: QCA8075 internal ESS switch -> eth0 / DSA tree 0
|
||||
+ * Port 6: QCA8337 external RGMII switch -> eth1 / DSA tree 1
|
||||
+ */
|
||||
+#define IPQESS_ESS_PORT_QCA8337 6
|
||||
+
|
||||
#endif
|
||||
--- a/drivers/net/ethernet/qualcomm/ipqess/ipqess_ethtool.c
|
||||
+++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess_ethtool.c
|
||||
@@ -1,164 +1,173 @@
|
||||
-// SPDX-License-Identifier: GPL-2.0 OR ISC
|
||||
-/* Copyright (c) 2015 - 2016, The Linux Foundation. All rights reserved.
|
||||
- * Copyright (c) 2017 - 2018, John Crispin <john@phrozen.org>
|
||||
- * Copyright (c) 2021 - 2022, Maxime Chevallier <maxime.chevallier@bootlin.com>
|
||||
- *
|
||||
- */
|
||||
-
|
||||
-#include <linux/ethtool.h>
|
||||
-#include <linux/netdevice.h>
|
||||
-#include <linux/string.h>
|
||||
-#include <linux/phylink.h>
|
||||
-
|
||||
-#include "ipqess.h"
|
||||
-
|
||||
-struct ipqess_ethtool_stats {
|
||||
- u8 string[ETH_GSTRING_LEN];
|
||||
- u32 offset;
|
||||
-};
|
||||
-
|
||||
-#define IPQESS_STAT(m) offsetof(struct ipqess_statistics, m)
|
||||
-#define DRVINFO_LEN 32
|
||||
-
|
||||
-static const struct ipqess_ethtool_stats ipqess_stats[] = {
|
||||
- {"tx_q0_pkt", IPQESS_STAT(tx_q0_pkt)},
|
||||
- {"tx_q1_pkt", IPQESS_STAT(tx_q1_pkt)},
|
||||
- {"tx_q2_pkt", IPQESS_STAT(tx_q2_pkt)},
|
||||
- {"tx_q3_pkt", IPQESS_STAT(tx_q3_pkt)},
|
||||
- {"tx_q4_pkt", IPQESS_STAT(tx_q4_pkt)},
|
||||
- {"tx_q5_pkt", IPQESS_STAT(tx_q5_pkt)},
|
||||
- {"tx_q6_pkt", IPQESS_STAT(tx_q6_pkt)},
|
||||
- {"tx_q7_pkt", IPQESS_STAT(tx_q7_pkt)},
|
||||
- {"tx_q8_pkt", IPQESS_STAT(tx_q8_pkt)},
|
||||
- {"tx_q9_pkt", IPQESS_STAT(tx_q9_pkt)},
|
||||
- {"tx_q10_pkt", IPQESS_STAT(tx_q10_pkt)},
|
||||
- {"tx_q11_pkt", IPQESS_STAT(tx_q11_pkt)},
|
||||
- {"tx_q12_pkt", IPQESS_STAT(tx_q12_pkt)},
|
||||
- {"tx_q13_pkt", IPQESS_STAT(tx_q13_pkt)},
|
||||
- {"tx_q14_pkt", IPQESS_STAT(tx_q14_pkt)},
|
||||
- {"tx_q15_pkt", IPQESS_STAT(tx_q15_pkt)},
|
||||
- {"tx_q0_byte", IPQESS_STAT(tx_q0_byte)},
|
||||
- {"tx_q1_byte", IPQESS_STAT(tx_q1_byte)},
|
||||
- {"tx_q2_byte", IPQESS_STAT(tx_q2_byte)},
|
||||
- {"tx_q3_byte", IPQESS_STAT(tx_q3_byte)},
|
||||
- {"tx_q4_byte", IPQESS_STAT(tx_q4_byte)},
|
||||
- {"tx_q5_byte", IPQESS_STAT(tx_q5_byte)},
|
||||
- {"tx_q6_byte", IPQESS_STAT(tx_q6_byte)},
|
||||
- {"tx_q7_byte", IPQESS_STAT(tx_q7_byte)},
|
||||
- {"tx_q8_byte", IPQESS_STAT(tx_q8_byte)},
|
||||
- {"tx_q9_byte", IPQESS_STAT(tx_q9_byte)},
|
||||
- {"tx_q10_byte", IPQESS_STAT(tx_q10_byte)},
|
||||
- {"tx_q11_byte", IPQESS_STAT(tx_q11_byte)},
|
||||
- {"tx_q12_byte", IPQESS_STAT(tx_q12_byte)},
|
||||
- {"tx_q13_byte", IPQESS_STAT(tx_q13_byte)},
|
||||
- {"tx_q14_byte", IPQESS_STAT(tx_q14_byte)},
|
||||
- {"tx_q15_byte", IPQESS_STAT(tx_q15_byte)},
|
||||
- {"rx_q0_pkt", IPQESS_STAT(rx_q0_pkt)},
|
||||
- {"rx_q1_pkt", IPQESS_STAT(rx_q1_pkt)},
|
||||
- {"rx_q2_pkt", IPQESS_STAT(rx_q2_pkt)},
|
||||
- {"rx_q3_pkt", IPQESS_STAT(rx_q3_pkt)},
|
||||
- {"rx_q4_pkt", IPQESS_STAT(rx_q4_pkt)},
|
||||
- {"rx_q5_pkt", IPQESS_STAT(rx_q5_pkt)},
|
||||
- {"rx_q6_pkt", IPQESS_STAT(rx_q6_pkt)},
|
||||
- {"rx_q7_pkt", IPQESS_STAT(rx_q7_pkt)},
|
||||
- {"rx_q0_byte", IPQESS_STAT(rx_q0_byte)},
|
||||
- {"rx_q1_byte", IPQESS_STAT(rx_q1_byte)},
|
||||
- {"rx_q2_byte", IPQESS_STAT(rx_q2_byte)},
|
||||
- {"rx_q3_byte", IPQESS_STAT(rx_q3_byte)},
|
||||
- {"rx_q4_byte", IPQESS_STAT(rx_q4_byte)},
|
||||
- {"rx_q5_byte", IPQESS_STAT(rx_q5_byte)},
|
||||
- {"rx_q6_byte", IPQESS_STAT(rx_q6_byte)},
|
||||
- {"rx_q7_byte", IPQESS_STAT(rx_q7_byte)},
|
||||
- {"tx_desc_error", IPQESS_STAT(tx_desc_error)},
|
||||
-};
|
||||
-
|
||||
-static int ipqess_get_strset_count(struct net_device *netdev, int sset)
|
||||
-{
|
||||
- switch (sset) {
|
||||
- case ETH_SS_STATS:
|
||||
- return ARRAY_SIZE(ipqess_stats);
|
||||
- default:
|
||||
- netdev_dbg(netdev, "%s: Unsupported string set", __func__);
|
||||
- return -EOPNOTSUPP;
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-static void ipqess_get_strings(struct net_device *netdev, u32 stringset,
|
||||
- u8 *data)
|
||||
-{
|
||||
- u8 *p = data;
|
||||
- u32 i;
|
||||
-
|
||||
- switch (stringset) {
|
||||
- case ETH_SS_STATS:
|
||||
- for (i = 0; i < ARRAY_SIZE(ipqess_stats); i++)
|
||||
- ethtool_puts(&p, ipqess_stats[i].string);
|
||||
- break;
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-static void ipqess_get_ethtool_stats(struct net_device *netdev,
|
||||
- struct ethtool_stats *stats,
|
||||
- uint64_t *data)
|
||||
-{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
- u32 *essstats = (u32 *)&ess->ipqess_stats;
|
||||
- int i;
|
||||
-
|
||||
- spin_lock(&ess->stats_lock);
|
||||
-
|
||||
- ipqess_update_hw_stats(ess);
|
||||
-
|
||||
- for (i = 0; i < ARRAY_SIZE(ipqess_stats); i++)
|
||||
- data[i] = *(u32 *)(essstats + (ipqess_stats[i].offset / sizeof(u32)));
|
||||
-
|
||||
- spin_unlock(&ess->stats_lock);
|
||||
-}
|
||||
-
|
||||
-static void ipqess_get_drvinfo(struct net_device *dev,
|
||||
- struct ethtool_drvinfo *info)
|
||||
-{
|
||||
- strscpy(info->driver, "qca_ipqess", DRVINFO_LEN);
|
||||
- strscpy(info->bus_info, "axi", ETHTOOL_BUSINFO_LEN);
|
||||
-}
|
||||
-
|
||||
-static int ipqess_get_link_ksettings(struct net_device *netdev,
|
||||
- struct ethtool_link_ksettings *cmd)
|
||||
-{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
-
|
||||
- return phylink_ethtool_ksettings_get(ess->phylink, cmd);
|
||||
-}
|
||||
-
|
||||
-static int ipqess_set_link_ksettings(struct net_device *netdev,
|
||||
- const struct ethtool_link_ksettings *cmd)
|
||||
-{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
-
|
||||
- return phylink_ethtool_ksettings_set(ess->phylink, cmd);
|
||||
-}
|
||||
-
|
||||
-static void ipqess_get_ringparam(struct net_device *netdev,
|
||||
- struct ethtool_ringparam *ring,
|
||||
- struct kernel_ethtool_ringparam *kernel_ering,
|
||||
- struct netlink_ext_ack *extack)
|
||||
-{
|
||||
- ring->tx_max_pending = IPQESS_TX_RING_SIZE;
|
||||
- ring->rx_max_pending = IPQESS_RX_RING_SIZE;
|
||||
-}
|
||||
-
|
||||
-static const struct ethtool_ops ipqesstool_ops = {
|
||||
- .get_drvinfo = &ipqess_get_drvinfo,
|
||||
- .get_link = ðtool_op_get_link,
|
||||
- .get_link_ksettings = &ipqess_get_link_ksettings,
|
||||
- .set_link_ksettings = &ipqess_set_link_ksettings,
|
||||
- .get_strings = &ipqess_get_strings,
|
||||
- .get_sset_count = &ipqess_get_strset_count,
|
||||
- .get_ethtool_stats = &ipqess_get_ethtool_stats,
|
||||
- .get_ringparam = ipqess_get_ringparam,
|
||||
-};
|
||||
-
|
||||
-void ipqess_set_ethtool_ops(struct net_device *netdev)
|
||||
-{
|
||||
- netdev->ethtool_ops = &ipqesstool_ops;
|
||||
-}
|
||||
+// SPDX-License-Identifier: GPL-2.0 OR ISC
|
||||
+/* Copyright (c) 2015 - 2016, The Linux Foundation. All rights reserved.
|
||||
+ * Copyright (c) 2017 - 2018, John Crispin <john@phrozen.org>
|
||||
+ * Copyright (c) 2021 - 2022, Maxime Chevallier <maxime.chevallier@bootlin.com>
|
||||
+ * Copyright (c) 2026, mooleshacat <mooleshacat@catspeed.cc>
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#include <linux/ethtool.h>
|
||||
+#include <linux/netdevice.h>
|
||||
+#include <linux/string.h>
|
||||
+#include <linux/phylink.h>
|
||||
+
|
||||
+#include "ipqess.h"
|
||||
+
|
||||
+struct ipqess_ethtool_stats {
|
||||
+ u8 string[ETH_GSTRING_LEN];
|
||||
+ u32 offset;
|
||||
+};
|
||||
+
|
||||
+#define IPQESS_STAT(m) offsetof(struct ipqess_statistics, m)
|
||||
+#define DRVINFO_LEN 32
|
||||
+
|
||||
+static const struct ipqess_ethtool_stats ipqess_stats[] = {
|
||||
+ {"tx_q0_pkt", IPQESS_STAT(tx_q0_pkt)},
|
||||
+ {"tx_q1_pkt", IPQESS_STAT(tx_q1_pkt)},
|
||||
+ {"tx_q2_pkt", IPQESS_STAT(tx_q2_pkt)},
|
||||
+ {"tx_q3_pkt", IPQESS_STAT(tx_q3_pkt)},
|
||||
+ {"tx_q4_pkt", IPQESS_STAT(tx_q4_pkt)},
|
||||
+ {"tx_q5_pkt", IPQESS_STAT(tx_q5_pkt)},
|
||||
+ {"tx_q6_pkt", IPQESS_STAT(tx_q6_pkt)},
|
||||
+ {"tx_q7_pkt", IPQESS_STAT(tx_q7_pkt)},
|
||||
+ {"tx_q8_pkt", IPQESS_STAT(tx_q8_pkt)},
|
||||
+ {"tx_q9_pkt", IPQESS_STAT(tx_q9_pkt)},
|
||||
+ {"tx_q10_pkt", IPQESS_STAT(tx_q10_pkt)},
|
||||
+ {"tx_q11_pkt", IPQESS_STAT(tx_q11_pkt)},
|
||||
+ {"tx_q12_pkt", IPQESS_STAT(tx_q12_pkt)},
|
||||
+ {"tx_q13_pkt", IPQESS_STAT(tx_q13_pkt)},
|
||||
+ {"tx_q14_pkt", IPQESS_STAT(tx_q14_pkt)},
|
||||
+ {"tx_q15_pkt", IPQESS_STAT(tx_q15_pkt)},
|
||||
+ {"tx_q0_byte", IPQESS_STAT(tx_q0_byte)},
|
||||
+ {"tx_q1_byte", IPQESS_STAT(tx_q1_byte)},
|
||||
+ {"tx_q2_byte", IPQESS_STAT(tx_q2_byte)},
|
||||
+ {"tx_q3_byte", IPQESS_STAT(tx_q3_byte)},
|
||||
+ {"tx_q4_byte", IPQESS_STAT(tx_q4_byte)},
|
||||
+ {"tx_q5_byte", IPQESS_STAT(tx_q5_byte)},
|
||||
+ {"tx_q6_byte", IPQESS_STAT(tx_q6_byte)},
|
||||
+ {"tx_q7_byte", IPQESS_STAT(tx_q7_byte)},
|
||||
+ {"tx_q8_byte", IPQESS_STAT(tx_q8_byte)},
|
||||
+ {"tx_q9_byte", IPQESS_STAT(tx_q9_byte)},
|
||||
+ {"tx_q10_byte", IPQESS_STAT(tx_q10_byte)},
|
||||
+ {"tx_q11_byte", IPQESS_STAT(tx_q11_byte)},
|
||||
+ {"tx_q12_byte", IPQESS_STAT(tx_q12_byte)},
|
||||
+ {"tx_q13_byte", IPQESS_STAT(tx_q13_byte)},
|
||||
+ {"tx_q14_byte", IPQESS_STAT(tx_q14_byte)},
|
||||
+ {"tx_q15_byte", IPQESS_STAT(tx_q15_byte)},
|
||||
+ {"rx_q0_pkt", IPQESS_STAT(rx_q0_pkt)},
|
||||
+ {"rx_q1_pkt", IPQESS_STAT(rx_q1_pkt)},
|
||||
+ {"rx_q2_pkt", IPQESS_STAT(rx_q2_pkt)},
|
||||
+ {"rx_q3_pkt", IPQESS_STAT(rx_q3_pkt)},
|
||||
+ {"rx_q4_pkt", IPQESS_STAT(rx_q4_pkt)},
|
||||
+ {"rx_q5_pkt", IPQESS_STAT(rx_q5_pkt)},
|
||||
+ {"rx_q6_pkt", IPQESS_STAT(rx_q6_pkt)},
|
||||
+ {"rx_q7_pkt", IPQESS_STAT(rx_q7_pkt)},
|
||||
+ {"rx_q0_byte", IPQESS_STAT(rx_q0_byte)},
|
||||
+ {"rx_q1_byte", IPQESS_STAT(rx_q1_byte)},
|
||||
+ {"rx_q2_byte", IPQESS_STAT(rx_q2_byte)},
|
||||
+ {"rx_q3_byte", IPQESS_STAT(rx_q3_byte)},
|
||||
+ {"rx_q4_byte", IPQESS_STAT(rx_q4_byte)},
|
||||
+ {"rx_q5_byte", IPQESS_STAT(rx_q5_byte)},
|
||||
+ {"rx_q6_byte", IPQESS_STAT(rx_q6_byte)},
|
||||
+ {"rx_q7_byte", IPQESS_STAT(rx_q7_byte)},
|
||||
+ {"tx_desc_error", IPQESS_STAT(tx_desc_error)},
|
||||
+};
|
||||
+
|
||||
+static int ipqess_get_strset_count(struct net_device *netdev, int sset)
|
||||
+{
|
||||
+ switch (sset) {
|
||||
+ case ETH_SS_STATS:
|
||||
+ return ARRAY_SIZE(ipqess_stats);
|
||||
+ default:
|
||||
+ netdev_dbg(netdev, "%s: Unsupported string set", __func__);
|
||||
+ return -EOPNOTSUPP;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void ipqess_get_strings(struct net_device *netdev, u32 stringset,
|
||||
+ u8 *data)
|
||||
+{
|
||||
+ u8 *p = data;
|
||||
+ u32 i;
|
||||
+
|
||||
+ switch (stringset) {
|
||||
+ case ETH_SS_STATS:
|
||||
+ for (i = 0; i < ARRAY_SIZE(ipqess_stats); i++)
|
||||
+ ethtool_puts(&p, ipqess_stats[i].string);
|
||||
+ break;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void ipqess_get_ethtool_stats(struct net_device *netdev,
|
||||
+ struct ethtool_stats *stats,
|
||||
+ uint64_t *data)
|
||||
+{
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
+ u32 *essstats = (u32 *)&ess->ipqess_stats;
|
||||
+ int i;
|
||||
+
|
||||
+ spin_lock(&ess->stats_lock);
|
||||
+
|
||||
+ ipqess_update_hw_stats(ess);
|
||||
+
|
||||
+ for (i = 0; i < ARRAY_SIZE(ipqess_stats); i++)
|
||||
+ data[i] = *(u32 *)(essstats + (ipqess_stats[i].offset / sizeof(u32)));
|
||||
+
|
||||
+ spin_unlock(&ess->stats_lock);
|
||||
+}
|
||||
+
|
||||
+static void ipqess_get_drvinfo(struct net_device *dev,
|
||||
+ struct ethtool_drvinfo *info)
|
||||
+{
|
||||
+ strscpy(info->driver, "qca_ipqess", DRVINFO_LEN);
|
||||
+ strscpy(info->bus_info, "axi", ETHTOOL_BUSINFO_LEN);
|
||||
+}
|
||||
+
|
||||
+static int ipqess_get_link_ksettings(struct net_device *netdev,
|
||||
+ struct ethtool_link_ksettings *cmd)
|
||||
+{
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
+
|
||||
+ /* netdev2 has no phylink instance — it is a fixed internal link */
|
||||
+ if (!ess->phylink)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ return phylink_ethtool_ksettings_get(ess->phylink, cmd);
|
||||
+}
|
||||
+
|
||||
+static int ipqess_set_link_ksettings(struct net_device *netdev,
|
||||
+ const struct ethtool_link_ksettings *cmd)
|
||||
+{
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
+
|
||||
+ /* netdev2 has no phylink instance — it is a fixed internal link */
|
||||
+ if (!ess->phylink)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ return phylink_ethtool_ksettings_set(ess->phylink, cmd);
|
||||
+}
|
||||
+
|
||||
+static void ipqess_get_ringparam(struct net_device *netdev,
|
||||
+ struct ethtool_ringparam *ring,
|
||||
+ struct kernel_ethtool_ringparam *kernel_ering,
|
||||
+ struct netlink_ext_ack *extack)
|
||||
+{
|
||||
+ ring->tx_max_pending = IPQESS_TX_RING_SIZE;
|
||||
+ ring->rx_max_pending = IPQESS_RX_RING_SIZE;
|
||||
+}
|
||||
+
|
||||
+static const struct ethtool_ops ipqesstool_ops = {
|
||||
+ .get_drvinfo = &ipqess_get_drvinfo,
|
||||
+ .get_link = ðtool_op_get_link,
|
||||
+ .get_link_ksettings = &ipqess_get_link_ksettings,
|
||||
+ .set_link_ksettings = &ipqess_set_link_ksettings,
|
||||
+ .get_strings = &ipqess_get_strings,
|
||||
+ .get_sset_count = &ipqess_get_strset_count,
|
||||
+ .get_ethtool_stats = &ipqess_get_ethtool_stats,
|
||||
+ .get_ringparam = ipqess_get_ringparam,
|
||||
+};
|
||||
+
|
||||
+void ipqess_set_ethtool_ops(struct net_device *netdev)
|
||||
+{
|
||||
+ netdev->ethtool_ops = &ipqesstool_ops;
|
||||
+}
|
||||
Loading…
Reference in New Issue
Block a user