Pull request net-20260312.

net:
- Move network PHY under NETDEVICES
- s/DM_CLK/CLK/ in HIFEMAC_{ETH,MDIO}
- Add support for Airoha AN8811HB PHY
- airoha: PCS and MDIO support for Airoha AN7581 SoC

net-lwip:
- Fix issue when TFTP blocksize is >8192
- Adjust PBUF_POOL_SIZE/IP_REASS_MAX_PBUFS for better performance and
  resource usage.
- Enable mii command for NET_LWIP
This commit is contained in:
Tom Rini 2026-03-13 09:00:54 -06:00
commit 6dc75d440d
17 changed files with 4506 additions and 89 deletions

View File

@ -35,6 +35,48 @@
reg = <0x0 0x1fa20000 0x0 0x388>;
};
pon_pcs: pcs@1fa08000 {
compatible = "airoha,an7581-pcs-pon";
reg = <0x0 0x1fa08000 0x0 0x1000>,
<0x0 0x1fa80000 0x0 0x60>,
<0x0 0x1fa80a00 0x0 0x164>,
<0x0 0x1fa84000 0x0 0x450>,
<0x0 0x1fa85900 0x0 0x338>,
<0x0 0x1fa86000 0x0 0x300>,
<0x0 0x1fa8a000 0x0 0x1000>,
<0x0 0x1fa8b000 0x0 0x1000>;
reg-names = "xfi_mac", "hsgmii_an", "hsgmii_pcs",
"multi_sgmii", "usxgmii",
"hsgmii_rate_adp", "xfi_ana", "xfi_pma";
resets = <&scuclk EN7581_XPON_MAC_RST>,
<&scuclk EN7581_XPON_PHY_RST>;
reset-names = "mac", "phy";
airoha,scu = <&scuclk>;
};
eth_pcs: pcs@1fa09000 {
compatible = "airoha,an7581-pcs-eth";
reg = <0x0 0x1fa09000 0x0 0x1000>,
<0x0 0x1fa70000 0x0 0x60>,
<0x0 0x1fa70a00 0x0 0x164>,
<0x0 0x1fa74000 0x0 0x450>,
<0x0 0x1fa75900 0x0 0x338>,
<0x0 0x1fa76000 0x0 0x300>,
<0x0 0x1fa7a000 0x0 0x1000>,
<0x0 0x1fa7b000 0x0 0x1000>;
reg-names = "xfi_mac", "hsgmii_an", "hsgmii_pcs",
"multi_sgmii", "usxgmii",
"hsgmii_rate_adp", "xfi_ana", "xfi_pma";
resets = <&scuclk EN7581_XSI_MAC_RST>,
<&scuclk EN7581_XSI_PHY_RST>;
reset-names = "mac", "phy";
airoha,scu = <&scuclk>;
};
eth: ethernet@1fb50000 {
compatible = "airoha,en7581-eth";
reg = <0 0x1fb50000 0 0x2600>,
@ -52,6 +94,35 @@
reset-names = "fe", "pdma", "qdma",
"hsi0-mac", "hsi1-mac", "hsi-mac",
"xfp-mac";
gdm1: ethernet@1 {
compatible = "airoha,eth-mac";
reg = <1>;
phy-mode = "internal";
status = "disabled";
fixed-link {
speed = <10000>;
full-duplex;
pause;
};
};
gdm2: ethernet@2 {
compatible = "airoha,eth-mac";
reg = <2>;
pcs = <&pon_pcs>;
status = "disabled";
};
gdm4: ethernet@4 {
compatible = "airoha,eth-mac";
reg = <4>;
pcs = <&eth_pcs>;
status = "disabled";
};
};
switch: switch@1fb58000 {

View File

@ -9,3 +9,7 @@
};
#include "en7523-u-boot.dtsi"
&gdm1 {
status = "okay";
};

View File

@ -37,6 +37,19 @@
<&scu EN7523_HSI_MAC_RST>;
reset-names = "fe", "pdma", "qdma",
"hsi0-mac", "hsi1-mac", "hsi-mac";
gdm1: ethernet@1 {
compatible = "airoha,eth-mac";
reg = <1>;
phy-mode = "internal";
status = "disabled";
fixed-link {
speed = <10000>;
full-duplex;
pause;
};
};
};
switch: switch@1fb58000 {

View File

@ -9,3 +9,21 @@
};
#include "an7581-u-boot.dtsi"
&gdm1 {
status = "okay";
};
&gdm2 {
status = "okay";
managed = "in-band-status";
phy-mode = "10gbase-r";
};
&gdm4 {
status = "okay";
managed = "in-band-status";
phy-mode = "usxgmii";
};

View File

@ -434,7 +434,7 @@ config BOOT_DEFAULTS_CMDS
select CMD_PXE if CMD_NET
select CMD_BOOTI if ARM64
select CMD_BOOTZ if ARM && !ARM64
imply CMD_MII if NET
imply CMD_MII if NET || NET_LWIP
config BOOT_DEFAULTS
bool # Common defaults for standard boot and distroboot

View File

@ -68,6 +68,8 @@ CONFIG_SPI_FLASH_WINBOND=y
CONFIG_SPI_FLASH_MTD=y
CONFIG_DM_MDIO=y
CONFIG_AIROHA_ETH=y
CONFIG_PCS_AIROHA_AN7581=y
CONFIG_PHYLIB=y
CONFIG_PHY=y
CONFIG_PINCTRL=y
CONFIG_PINCONF=y

View File

@ -1,7 +1,3 @@
source "drivers/net/phy/Kconfig"
source "drivers/net/pfe_eth/Kconfig"
source "drivers/net/fsl-mc/Kconfig"
config ETH
def_bool y
@ -121,11 +117,15 @@ config AG7XXX
This driver supports the Atheros AG7xxx Ethernet MAC. This MAC is
present in the Atheros AR7xxx, AR9xxx and QCA9xxx MIPS chips.
source "drivers/net/airoha/Kconfig"
config AIROHA_ETH
bool "Airoha Ethernet QDMA Driver"
depends on ARCH_AIROHA
select MISC
select PHYLIB
select DEVRES
select DM_ETH_PHY
select DM_RESET
select MDIO_MT7531_MMIO
help
@ -979,7 +979,7 @@ source "drivers/net/mtk_eth/Kconfig"
config HIFEMAC_ETH
bool "HiSilicon Fast Ethernet Controller"
select DM_CLK
select CLK
select DM_RESET
select PHYLIB
help
@ -989,7 +989,7 @@ config HIFEMAC_ETH
config HIFEMAC_MDIO
bool "HiSilicon Fast Ethernet Controller MDIO interface"
depends on DM_MDIO
select DM_CLK
select CLK
help
This driver supports the internal MDIO interface of HIFEMAC
Ethernet controller.
@ -1098,4 +1098,8 @@ config MDIO_MUX_MESON_GXL
This driver is used for the MDIO mux found on the Amlogic GXL & compatible
SoCs.
source "drivers/net/phy/Kconfig"
source "drivers/net/pfe_eth/Kconfig"
source "drivers/net/fsl-mc/Kconfig"
endif # NETDEVICES

View File

@ -5,6 +5,7 @@
obj-$(CONFIG_AG7XXX) += ag7xxx.o
obj-y += airoha/
obj-$(CONFIG_AIROHA_ETH) += airoha_eth.o
obj-$(CONFIG_ALTERA_TSE) += altera_tse.o
obj-$(CONFIG_ASPEED_MDIO) += aspeed_mdio.o

View File

@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0-only
config PCS_AIROHA
bool
select MISC
config PCS_AIROHA_AN7581
bool "Airoha AN7581 PCS driver"
select PCS_AIROHA
help
This module provides helper to phylink for managing the Airoha
AN7581 PCS for SoC Ethernet and PON SERDES.

View File

@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PCS_AIROHA) += pcs-airoha-common.o
obj-$(CONFIG_PCS_AIROHA_AN7581) += pcs-an7581.o

View File

@ -0,0 +1,827 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2024 AIROHA Inc
* Author: Christian Marangi <ansuelsmth@gmail.com>
*/
#include <dm.h>
#include <dm/devres.h>
#include <linux/ethtool.h>
#include <net.h>
#include <regmap.h>
#include <reset.h>
#include <syscon.h>
#include "pcs-airoha.h"
static void airoha_pcs_setup_scu_eth(struct airoha_pcs_priv *priv,
phy_interface_t interface)
{
u32 xsi_sel;
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_2500BASEX:
xsi_sel = AIROHA_SCU_ETH_XSI_HSGMII;
break;
case PHY_INTERFACE_MODE_USXGMII:
case PHY_INTERFACE_MODE_10GBASER:
default:
xsi_sel = AIROHA_SCU_ETH_XSI_USXGMII;
}
regmap_update_bits(priv->scu, AIROHA_SCU_SSR3,
AIROHA_SCU_ETH_XSI_SEL,
xsi_sel);
}
static void airoha_pcs_setup_scu_pon(struct airoha_pcs_priv *priv,
phy_interface_t interface)
{
u32 xsi_sel, wan_sel;
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_1000BASEX:
wan_sel = AIROHA_SCU_WAN_SEL_SGMII;
xsi_sel = AIROHA_SCU_PON_XSI_HSGMII;
break;
case PHY_INTERFACE_MODE_2500BASEX:
wan_sel = AIROHA_SCU_WAN_SEL_HSGMII;
xsi_sel = AIROHA_SCU_PON_XSI_HSGMII;
break;
case PHY_INTERFACE_MODE_USXGMII:
case PHY_INTERFACE_MODE_10GBASER:
default:
wan_sel = AIROHA_SCU_WAN_SEL_USXGMII;
xsi_sel = AIROHA_SCU_PON_XSI_USXGMII;
}
regmap_update_bits(priv->scu, AIROHA_SCU_SSTR,
AIROHA_SCU_PON_XSI_SEL,
xsi_sel);
regmap_update_bits(priv->scu, AIROHA_SCU_WAN_CONF,
AIROHA_SCU_WAN_SEL,
wan_sel);
}
static int airoha_pcs_setup_scu(struct airoha_pcs_priv *priv,
phy_interface_t interface)
{
const struct airoha_pcs_match_data *data = priv->data;
int ret;
if (priv->xfi_rst) {
ret = reset_assert(priv->xfi_rst);
if (ret)
return ret;
}
switch (data->port_type) {
case AIROHA_PCS_ETH:
airoha_pcs_setup_scu_eth(priv, interface);
break;
case AIROHA_PCS_PON:
airoha_pcs_setup_scu_pon(priv, interface);
break;
}
if (priv->xfi_rst) {
ret = reset_deassert(priv->xfi_rst);
if (ret)
return ret;
}
/* TODO better handle reset from MAC */
ret = reset_assert_bulk(&priv->rsts);
if (ret)
return ret;
ret = reset_deassert_bulk(&priv->rsts);
if (ret)
return ret;
return 0;
}
static void airoha_pcs_init_usxgmii(struct airoha_pcs_priv *priv)
{
const struct airoha_pcs_match_data *data = priv->data;
regmap_set_bits(priv->multi_sgmii, AIROHA_PCS_MULTI_SGMII_MSG_RX_CTRL_0,
AIROHA_PCS_HSGMII_XFI_SEL);
/* Disable Hibernation */
if (data->hibernation_workaround)
regmap_clear_bits(priv->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_CTROL_1,
AIROHA_PCS_USXGMII_SPEED_SEL_H);
/* FIXME: wait Airoha */
/* Avoid PCS sending garbage to MAC in some HW revision (E0) */
if (data->usxgmii_ber_time_fixup)
regmap_write(priv->usxgmii_pcs, AIROHA_PCS_USGMII_VENDOR_DEFINE_116, 0);
if (data->usxgmii_rx_gb_out_vld_tweak)
regmap_clear_bits(priv->usxgmii_pcs, AN7583_PCS_USXGMII_RTL_MODIFIED,
AIROHA_PCS_USXGMII_MODIFIED_RX_GB_OUT_VLD);
}
static void airoha_pcs_init_hsgmii(struct airoha_pcs_priv *priv)
{
regmap_clear_bits(priv->multi_sgmii, AIROHA_PCS_MULTI_SGMII_MSG_RX_CTRL_0,
AIROHA_PCS_HSGMII_XFI_SEL);
regmap_set_bits(priv->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_1,
AIROHA_PCS_TBI_10B_MODE);
}
static void airoha_pcs_init_sgmii(struct airoha_pcs_priv *priv)
{
regmap_clear_bits(priv->multi_sgmii, AIROHA_PCS_MULTI_SGMII_MSG_RX_CTRL_0,
AIROHA_PCS_HSGMII_XFI_SEL);
regmap_set_bits(priv->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_1,
AIROHA_PCS_TBI_10B_MODE);
regmap_update_bits(priv->hsgmii_rate_adp, AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_6,
AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_DOUT_L,
FIELD_PREP(AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_DOUT_L, 0x07070707));
regmap_update_bits(priv->hsgmii_rate_adp, AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_8,
AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_DOUT_C,
FIELD_PREP(AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_DOUT_C, 0xff));
}
static void airoha_pcs_init(struct airoha_pcs_priv *priv,
phy_interface_t interface)
{
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_1000BASEX:
airoha_pcs_init_sgmii(priv);
break;
case PHY_INTERFACE_MODE_2500BASEX:
airoha_pcs_init_hsgmii(priv);
break;
case PHY_INTERFACE_MODE_USXGMII:
case PHY_INTERFACE_MODE_10GBASER:
airoha_pcs_init_usxgmii(priv);
break;
default:
return;
}
}
static void airoha_pcs_interrupt_init_sgmii(struct airoha_pcs_priv *priv)
{
/* Disable every interrupt */
regmap_clear_bits(priv->usxgmii_pcs, AIROHA_PCS_HSGMII_PCS_HSGMII_MODE_INTERRUPT,
AIROHA_PCS_HSGMII_MODE2_REMOVE_FAULT_OCCUR_INT |
AIROHA_PCS_HSGMII_MODE2_AN_CL37_TIMERDONE_INT |
AIROHA_PCS_HSGMII_MODE2_AN_MIS_INT |
AIROHA_PCS_HSGMII_MODE2_RX_SYN_DONE_INT |
AIROHA_PCS_HSGMII_MODE2_AN_DONE_INT);
/* Clear interrupt */
regmap_set_bits(priv->usxgmii_pcs, AIROHA_PCS_HSGMII_PCS_HSGMII_MODE_INTERRUPT,
AIROHA_PCS_HSGMII_MODE2_REMOVE_FAULT_OCCUR_INT_CLEAR |
AIROHA_PCS_HSGMII_MODE2_AN_CL37_TIMERDONE_INT_CLEAR |
AIROHA_PCS_HSGMII_MODE2_AN_MIS_INT_CLEAR |
AIROHA_PCS_HSGMII_MODE2_RX_SYN_DONE_INT_CLEAR |
AIROHA_PCS_HSGMII_MODE2_AN_DONE_INT_CLEAR);
regmap_clear_bits(priv->usxgmii_pcs, AIROHA_PCS_HSGMII_PCS_HSGMII_MODE_INTERRUPT,
AIROHA_PCS_HSGMII_MODE2_REMOVE_FAULT_OCCUR_INT_CLEAR |
AIROHA_PCS_HSGMII_MODE2_AN_CL37_TIMERDONE_INT_CLEAR |
AIROHA_PCS_HSGMII_MODE2_AN_MIS_INT_CLEAR |
AIROHA_PCS_HSGMII_MODE2_RX_SYN_DONE_INT_CLEAR |
AIROHA_PCS_HSGMII_MODE2_AN_DONE_INT_CLEAR);
}
static void airoha_pcs_interrupt_init_usxgmii(struct airoha_pcs_priv *priv)
{
/* Disable every Interrupt */
regmap_clear_bits(priv->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_CTRL_0,
AIROHA_PCS_USXGMII_T_TYPE_T_INT_EN |
AIROHA_PCS_USXGMII_T_TYPE_D_INT_EN |
AIROHA_PCS_USXGMII_T_TYPE_C_INT_EN |
AIROHA_PCS_USXGMII_T_TYPE_S_INT_EN);
regmap_clear_bits(priv->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_CTRL_1,
AIROHA_PCS_USXGMII_R_TYPE_C_INT_EN |
AIROHA_PCS_USXGMII_R_TYPE_S_INT_EN |
AIROHA_PCS_USXGMII_TXPCS_FSM_ENC_ERR_INT_EN |
AIROHA_PCS_USXGMII_T_TYPE_E_INT_EN);
regmap_clear_bits(priv->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_CTRL_2,
AIROHA_PCS_USXGMII_RPCS_FSM_DEC_ERR_INT_EN |
AIROHA_PCS_USXGMII_R_TYPE_E_INT_EN |
AIROHA_PCS_USXGMII_R_TYPE_T_INT_EN |
AIROHA_PCS_USXGMII_R_TYPE_D_INT_EN);
regmap_clear_bits(priv->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_CTRL_3,
AIROHA_PCS_USXGMII_FAIL_SYNC_XOR_ST_INT_EN |
AIROHA_PCS_USXGMII_RX_BLOCK_LOCK_ST_INT_EN |
AIROHA_PCS_USXGMII_LINK_UP_ST_INT_EN |
AIROHA_PCS_USXGMII_HI_BER_ST_INT_EN);
regmap_clear_bits(priv->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_CTRL_4,
AIROHA_PCS_USXGMII_LINK_DOWN_ST_INT_EN);
/* Clear any pending interrupt */
regmap_set_bits(priv->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_INT_STA_2,
AIROHA_PCS_USXGMII_RPCS_FSM_DEC_ERR_INT |
AIROHA_PCS_USXGMII_R_TYPE_E_INT |
AIROHA_PCS_USXGMII_R_TYPE_T_INT |
AIROHA_PCS_USXGMII_R_TYPE_D_INT);
regmap_set_bits(priv->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_INT_STA_3,
AIROHA_PCS_USXGMII_FAIL_SYNC_XOR_ST_INT |
AIROHA_PCS_USXGMII_RX_BLOCK_LOCK_ST_INT |
AIROHA_PCS_USXGMII_LINK_UP_ST_INT |
AIROHA_PCS_USXGMII_HI_BER_ST_INT);
regmap_set_bits(priv->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_INT_STA_4,
AIROHA_PCS_USXGMII_LINK_DOWN_ST_INT);
/* Interrupt saddly seems to be not weel supported for Link Down.
* PCS Poll is a must to correctly read and react on Cable Deatch
* as only cable attach interrupt are fired and Link Down interrupt
* are fired only in special case like AN restart.
*/
}
static void airoha_pcs_interrupt_init(struct airoha_pcs_priv *priv,
phy_interface_t interface)
{
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_2500BASEX:
return airoha_pcs_interrupt_init_sgmii(priv);
case PHY_INTERFACE_MODE_USXGMII:
case PHY_INTERFACE_MODE_10GBASER:
return airoha_pcs_interrupt_init_usxgmii(priv);
default:
return;
}
}
int airoha_pcs_config(struct udevice *dev, bool neg_mode,
phy_interface_t interface,
const unsigned long *advertising,
bool permit_pause_to_mac)
{
struct airoha_pcs_priv *priv = dev_get_priv(dev);
const struct airoha_pcs_match_data *data;
u32 rate_adapt;
int ret;
priv->interface = interface;
data = priv->data;
/* Apply Analog and Digital configuration for PCS */
if (data->bringup) {
ret = data->bringup(priv, interface);
if (ret)
return ret;
}
/* Set final configuration for various modes */
airoha_pcs_init(priv, interface);
/* Configure Interrupt for various modes */
airoha_pcs_interrupt_init(priv, interface);
rate_adapt = AIROHA_PCS_HSGMII_RATE_ADAPT_RX_EN |
AIROHA_PCS_HSGMII_RATE_ADAPT_TX_EN;
if (interface == PHY_INTERFACE_MODE_SGMII)
rate_adapt |= AIROHA_PCS_HSGMII_RATE_ADAPT_RX_BYPASS |
AIROHA_PCS_HSGMII_RATE_ADAPT_TX_BYPASS;
/* AN Auto Settings (Rate Adaptation) */
regmap_update_bits(priv->hsgmii_rate_adp, AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_0,
AIROHA_PCS_HSGMII_RATE_ADAPT_RX_BYPASS |
AIROHA_PCS_HSGMII_RATE_ADAPT_TX_BYPASS |
AIROHA_PCS_HSGMII_RATE_ADAPT_RX_EN |
AIROHA_PCS_HSGMII_RATE_ADAPT_TX_EN, rate_adapt);
/* FIXME: With an attached Aeonsemi PHY, AN is needed
* even with no inband.
*/
if (interface == PHY_INTERFACE_MODE_USXGMII ||
interface == PHY_INTERFACE_MODE_10GBASER) {
if (interface == PHY_INTERFACE_MODE_USXGMII)
regmap_set_bits(priv->usxgmii_pcs,
AIROHA_PCS_USXGMII_PCS_AN_CONTROL_0,
AIROHA_PCS_USXGMII_AN_ENABLE);
else
regmap_clear_bits(priv->usxgmii_pcs,
AIROHA_PCS_USXGMII_PCS_AN_CONTROL_0,
AIROHA_PCS_USXGMII_AN_ENABLE);
if (data->usxgmii_xfi_mode_sel && neg_mode)
regmap_set_bits(priv->usxgmii_pcs,
AIROHA_PCS_USXGMII_PCS_AN_CONTROL_7,
AIROHA_PCS_USXGMII_XFI_MODE_TX_SEL |
AIROHA_PCS_USXGMII_XFI_MODE_RX_SEL);
}
/* Clear any force bit that my be set by bootloader */
if (interface == PHY_INTERFACE_MODE_SGMII ||
interface == PHY_INTERFACE_MODE_1000BASEX ||
interface == PHY_INTERFACE_MODE_2500BASEX) {
regmap_clear_bits(priv->multi_sgmii, AIROHA_PCS_MULTI_SGMII_SGMII_STS_CTRL_0,
AIROHA_PCS_LINK_MODE_P0 |
AIROHA_PCS_FORCE_SPD_MODE_P0 |
AIROHA_PCS_FORCE_LINKDOWN_P0 |
AIROHA_PCS_FORCE_LINKUP_P0);
}
/* Toggle Rate Adaption for SGMII/HSGMII mode */ /* TODO */
if (interface == PHY_INTERFACE_MODE_SGMII ||
interface == PHY_INTERFACE_MODE_1000BASEX ||
interface == PHY_INTERFACE_MODE_2500BASEX) {
if (neg_mode)
regmap_clear_bits(priv->hsgmii_rate_adp,
AIROHA_PCS_HSGMII_RATE_ADP_P0_CTRL_0,
AIROHA_PCS_HSGMII_P0_DIS_MII_MODE);
else
regmap_set_bits(priv->hsgmii_rate_adp,
AIROHA_PCS_HSGMII_RATE_ADP_P0_CTRL_0,
AIROHA_PCS_HSGMII_P0_DIS_MII_MODE);
}
/* Setup SGMII AN and advertisement in DEV_ABILITY */ /* TODO */
if (interface == PHY_INTERFACE_MODE_SGMII) {
if (neg_mode) {
int advertise = 0x1;
regmap_update_bits(priv->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_4,
AIROHA_PCS_HSGMII_AN_SGMII_DEV_ABILITY,
FIELD_PREP(AIROHA_PCS_HSGMII_AN_SGMII_DEV_ABILITY,
advertise));
regmap_set_bits(priv->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_0,
AIROHA_PCS_HSGMII_AN_SGMII_RA_ENABLE);
} else {
regmap_clear_bits(priv->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_0,
AIROHA_PCS_HSGMII_AN_SGMII_RA_ENABLE);
}
}
if (interface == PHY_INTERFACE_MODE_2500BASEX) {
regmap_clear_bits(priv->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_0,
AIROHA_PCS_HSGMII_AN_SGMII_RA_ENABLE);
regmap_set_bits(priv->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_6,
AIROHA_PCS_HSGMII_PCS_TX_ENABLE);
}
if (interface == PHY_INTERFACE_MODE_SGMII ||
interface == PHY_INTERFACE_MODE_1000BASEX) {
u32 if_mode = AIROHA_PCS_HSGMII_AN_SIDEBAND_EN;
/* Toggle SGMII or 1000base-x mode */
if (interface == PHY_INTERFACE_MODE_SGMII)
if_mode |= AIROHA_PCS_HSGMII_AN_SGMII_EN;
if (neg_mode)
regmap_set_bits(priv->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_13,
AIROHA_PCS_HSGMII_AN_SGMII_REMOTE_FAULT_DIS);
else
regmap_clear_bits(priv->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_13,
AIROHA_PCS_HSGMII_AN_SGMII_REMOTE_FAULT_DIS);
if (neg_mode) {
/* Clear force speed bits and MAC mode */
regmap_clear_bits(priv->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_6,
AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_10 |
AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_100 |
AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_1000 |
AIROHA_PCS_HSGMII_PCS_MAC_MODE |
AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL |
AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT);
} else {
/* Enable compatibility with MAC PCS Layer */
if_mode |= AIROHA_PCS_HSGMII_AN_SGMII_COMPAT_EN;
/* AN off force rate adaption, speed is set later in Link Up */
regmap_set_bits(priv->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_6,
AIROHA_PCS_HSGMII_PCS_MAC_MODE |
AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT);
}
regmap_update_bits(priv->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_13,
AIROHA_PCS_HSGMII_AN_SGMII_IF_MODE_5_0, if_mode);
regmap_set_bits(priv->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_6,
AIROHA_PCS_HSGMII_PCS_TX_ENABLE |
AIROHA_PCS_HSGMII_PCS_MODE2_EN);
}
if (interface == PHY_INTERFACE_MODE_1000BASEX &&
!neg_mode) {
regmap_set_bits(priv->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_1,
AIROHA_PCS_SGMII_SEND_AN_ERR_EN);
regmap_set_bits(priv->hsgmii_pcs, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_FORCE_CL37,
AIROHA_PCS_HSGMII_AN_FORCE_AN_DONE);
}
/* Configure Flow Control on XFI */
regmap_update_bits(priv->xfi_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG,
AIROHA_PCS_XFI_TX_FC_EN | AIROHA_PCS_XFI_RX_FC_EN,
permit_pause_to_mac ?
AIROHA_PCS_XFI_TX_FC_EN | AIROHA_PCS_XFI_RX_FC_EN :
0);
return 0;
}
void airoha_pcs_link_up(struct udevice *dev, unsigned int neg_mode,
phy_interface_t interface, int speed, int duplex)
{
struct airoha_pcs_priv *priv = dev_get_priv(dev);
const struct airoha_pcs_match_data *data;
data = priv->data;
if (neg_mode) {
if (interface == PHY_INTERFACE_MODE_SGMII) {
regmap_update_bits(priv->hsgmii_rate_adp,
AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_1,
AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_WR_THR |
AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_RD_THR,
FIELD_PREP(AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_WR_THR, 0x0) |
FIELD_PREP(AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_RD_THR, 0x0));
udelay(1);
regmap_update_bits(priv->hsgmii_rate_adp,
AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_1,
AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_WR_THR |
AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_RD_THR,
FIELD_PREP(AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_WR_THR, 0xf) |
FIELD_PREP(AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_RD_THR, 0x5));
}
} else {
if (interface == PHY_INTERFACE_MODE_USXGMII ||
interface == PHY_INTERFACE_MODE_10GBASER) {
u32 mode;
u32 rate_adapt;
switch (speed) {
case SPEED_10000:
rate_adapt = AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_10000;
mode = AIROHA_PCS_USXGMII_MODE_10000;
break;
/* case SPEED_5000: not supported in U-Boot
rate_adapt = AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_5000;
mode = AIROHA_PCS_USXGMII_MODE_5000;
break; */
case SPEED_2500:
rate_adapt = AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_2500;
mode = AIROHA_PCS_USXGMII_MODE_2500;
break;
case SPEED_1000:
rate_adapt = AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_1000;
mode = AIROHA_PCS_USXGMII_MODE_1000;
break;
case SPEED_100:
rate_adapt = AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_100;
mode = AIROHA_PCS_USXGMII_MODE_100;
break;
}
/* Trigger USXGMII change mode and force selected speed */
regmap_update_bits(priv->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_AN_CONTROL_7,
AIROHA_PCS_USXGMII_RATE_UPDATE_MODE |
AIROHA_PCS_USXGMII_MODE,
AIROHA_PCS_USXGMII_RATE_UPDATE_MODE | mode);
regmap_update_bits(priv->hsgmii_rate_adp, AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_11,
AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_EN |
AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE,
AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_EN |
rate_adapt);
}
if (interface == PHY_INTERFACE_MODE_SGMII ||
interface == PHY_INTERFACE_MODE_1000BASEX) {
u32 force_speed;
u32 rate_adapt;
switch (speed) {
case SPEED_1000:
force_speed = AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_1000;
rate_adapt = AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL_1000;
break;
case SPEED_100:
force_speed = AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_100;
rate_adapt = AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL_100;
break;
case SPEED_10:
force_speed = AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_10;
rate_adapt = AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL_10;
break;
}
regmap_update_bits(priv->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_6,
AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_10 |
AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_100 |
AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_1000 |
AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL,
force_speed | rate_adapt);
}
if (interface == PHY_INTERFACE_MODE_SGMII ||
interface == PHY_INTERFACE_MODE_2500BASEX) {
u32 ck_gen_mode;
u32 speed_reg;
u32 if_mode;
switch (speed) {
case SPEED_2500:
speed_reg = AIROHA_PCS_LINK_MODE_P0_2_5G;
break;
case SPEED_1000:
speed_reg = AIROHA_PCS_LINK_MODE_P0_1G;
if_mode = AIROHA_PCS_HSGMII_AN_SPEED_FORCE_MODE_1000;
ck_gen_mode = AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE_1000;
break;
case SPEED_100:
speed_reg = AIROHA_PCS_LINK_MODE_P0_100M;
if_mode = AIROHA_PCS_HSGMII_AN_SPEED_FORCE_MODE_100;
ck_gen_mode = AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE_100;
break;
case SPEED_10:
speed_reg = AIROHA_PCS_LINK_MODE_P0_100M;
if_mode = AIROHA_PCS_HSGMII_AN_SPEED_FORCE_MODE_10;
ck_gen_mode = AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE_10;
break;
}
if (interface == PHY_INTERFACE_MODE_SGMII) {
regmap_update_bits(priv->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_13,
AIROHA_PCS_HSGMII_AN_SPEED_FORCE_MODE,
if_mode);
regmap_update_bits(priv->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_AN_SGMII_MODE_FORCE,
AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE |
AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE_SEL,
ck_gen_mode |
AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE_SEL);
}
regmap_update_bits(priv->multi_sgmii, AIROHA_PCS_MULTI_SGMII_SGMII_STS_CTRL_0,
AIROHA_PCS_LINK_MODE_P0 |
AIROHA_PCS_FORCE_SPD_MODE_P0,
speed_reg |
AIROHA_PCS_FORCE_SPD_MODE_P0);
}
}
if (data->link_up)
data->link_up(priv);
/* BPI BMI enable */
regmap_clear_bits(priv->xfi_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG,
AIROHA_PCS_XFI_RXMPI_STOP |
AIROHA_PCS_XFI_RXMBI_STOP |
AIROHA_PCS_XFI_TXMPI_STOP |
AIROHA_PCS_XFI_TXMBI_STOP);
}
void airoha_pcs_link_down(struct udevice *dev)
{
struct airoha_pcs_priv *priv = dev_get_priv(dev);
/* MPI MBI disable */
regmap_set_bits(priv->xfi_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG,
AIROHA_PCS_XFI_RXMPI_STOP |
AIROHA_PCS_XFI_RXMBI_STOP |
AIROHA_PCS_XFI_TXMPI_STOP |
AIROHA_PCS_XFI_TXMBI_STOP);
}
void airoha_pcs_pre_config(struct udevice *dev, phy_interface_t interface)
{
struct airoha_pcs_priv *priv = dev_get_priv(dev);
/* Select HSGMII or USXGMII in SCU regs */
airoha_pcs_setup_scu(priv, interface);
/* MPI MBI disable */
regmap_set_bits(priv->xfi_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG,
AIROHA_PCS_XFI_RXMPI_STOP |
AIROHA_PCS_XFI_RXMBI_STOP |
AIROHA_PCS_XFI_TXMPI_STOP |
AIROHA_PCS_XFI_TXMBI_STOP);
/* Write 1 to trigger reset and clear */
regmap_clear_bits(priv->xfi_mac, AIROHA_PCS_XFI_MAC_XFI_LOGIC_RST,
AIROHA_PCS_XFI_MAC_LOGIC_RST);
regmap_set_bits(priv->xfi_mac, AIROHA_PCS_XFI_MAC_XFI_LOGIC_RST,
AIROHA_PCS_XFI_MAC_LOGIC_RST);
udelay(1000);
/* Clear XFI MAC counter */
regmap_set_bits(priv->xfi_mac, AIROHA_PCS_XFI_MAC_XFI_CNT_CLR,
AIROHA_PCS_XFI_GLB_CNT_CLR);
}
int airoha_pcs_post_config(struct udevice *dev, phy_interface_t interface)
{
struct airoha_pcs_priv *priv = dev_get_priv(dev);
/* Frag disable */
regmap_update_bits(priv->xfi_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG,
AIROHA_PCS_XFI_RX_FRAG_LEN,
FIELD_PREP(AIROHA_PCS_XFI_RX_FRAG_LEN, 31));
regmap_update_bits(priv->xfi_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG,
AIROHA_PCS_XFI_TX_FRAG_LEN,
FIELD_PREP(AIROHA_PCS_XFI_TX_FRAG_LEN, 31));
/* IPG NUM */
regmap_update_bits(priv->xfi_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG,
AIROHA_PCS_XFI_IPG_NUM,
FIELD_PREP(AIROHA_PCS_XFI_IPG_NUM, 10));
/* Enable TX/RX flow control */
regmap_set_bits(priv->xfi_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG,
AIROHA_PCS_XFI_TX_FC_EN);
regmap_set_bits(priv->xfi_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG,
AIROHA_PCS_XFI_RX_FC_EN);
return 0;
}
static const struct regmap_config airoha_pcs_regmap_config = {
.width = REGMAP_SIZE_32,
};
static int airoha_pcs_probe(struct udevice *dev)
{
struct regmap_config syscon_config = airoha_pcs_regmap_config;
struct airoha_pcs_priv *priv = dev_get_priv(dev);
fdt_addr_t base;
fdt_size_t size;
int ret;
priv->dev = dev;
priv->data = (void *)dev_get_driver_data(dev);
base = dev_read_addr_size_name(dev, "xfi_mac", &size);
if (base == FDT_ADDR_T_NONE)
return -EINVAL;
syscon_config.r_start = base;
syscon_config.r_size = size;
priv->xfi_mac = devm_regmap_init(dev, NULL, NULL, &syscon_config);
if (IS_ERR(priv->xfi_mac))
return PTR_ERR(priv->xfi_mac);
base = dev_read_addr_size_name(dev, "hsgmii_an", &size);
if (base == FDT_ADDR_T_NONE)
return -EINVAL;
syscon_config.r_start = base;
syscon_config.r_size = size;
priv->hsgmii_an = devm_regmap_init(dev, NULL, NULL, &syscon_config);
if (IS_ERR(priv->hsgmii_an))
return PTR_ERR(priv->hsgmii_an);
base = dev_read_addr_size_name(dev, "hsgmii_pcs", &size);
if (base == FDT_ADDR_T_NONE)
return -EINVAL;
syscon_config.r_start = base;
syscon_config.r_size = size;
priv->hsgmii_pcs = devm_regmap_init(dev, NULL, NULL, &syscon_config);
if (IS_ERR(priv->hsgmii_pcs))
return PTR_ERR(priv->hsgmii_pcs);
base = dev_read_addr_size_name(dev, "hsgmii_rate_adp", &size);
if (base == FDT_ADDR_T_NONE)
return -EINVAL;
syscon_config.r_start = base;
syscon_config.r_size = size;
priv->hsgmii_rate_adp = devm_regmap_init(dev, NULL, NULL, &syscon_config);
if (IS_ERR(priv->hsgmii_rate_adp))
return PTR_ERR(priv->hsgmii_rate_adp);
base = dev_read_addr_size_name(dev, "multi_sgmii", &size);
if (base == FDT_ADDR_T_NONE)
return -EINVAL;
syscon_config.r_start = base;
syscon_config.r_size = size;
priv->multi_sgmii = devm_regmap_init(dev, NULL, NULL, &syscon_config);
if (IS_ERR(priv->multi_sgmii))
return PTR_ERR(priv->multi_sgmii);
base = dev_read_addr_size_name(dev, "usxgmii", &size);
if (base == FDT_ADDR_T_NONE)
return -EINVAL;
syscon_config.r_start = base;
syscon_config.r_size = size;
priv->usxgmii_pcs = devm_regmap_init(dev, NULL, NULL, &syscon_config);
if (IS_ERR(priv->usxgmii_pcs))
return PTR_ERR(priv->usxgmii_pcs);
base = dev_read_addr_size_name(dev, "xfi_pma", &size);
if (base == FDT_ADDR_T_NONE)
return -EINVAL;
syscon_config.r_start = base;
syscon_config.r_size = size;
priv->xfi_pma = devm_regmap_init(dev, NULL, NULL, &syscon_config);
if (IS_ERR(priv->xfi_pma))
return PTR_ERR(priv->xfi_pma);
base = dev_read_addr_size_name(dev, "xfi_ana", &size);
if (base == FDT_ADDR_T_NONE)
return -EINVAL;
syscon_config.r_start = base;
syscon_config.r_size = size;
priv->xfi_ana = devm_regmap_init(dev, NULL, NULL, &syscon_config);
if (IS_ERR(priv->xfi_ana))
return PTR_ERR(priv->xfi_ana);
/* SCU is used to toggle XFI or HSGMII in global SoC registers */
priv->scu = syscon_regmap_lookup_by_phandle(dev, "airoha,scu");
if (IS_ERR(priv->scu))
return PTR_ERR(priv->scu);
priv->rsts.resets = devm_kcalloc(dev, AIROHA_PCS_MAX_NUM_RSTS,
sizeof(struct reset_ctl), GFP_KERNEL);
if (!priv->rsts.resets)
return -ENOMEM;
priv->rsts.count = AIROHA_PCS_MAX_NUM_RSTS;
ret = reset_get_by_name(dev, "mac", &priv->rsts.resets[0]);
if (ret)
return ret;
ret = reset_get_by_name(dev, "phy", &priv->rsts.resets[1]);
if (ret)
return ret;
priv->xfi_rst = devm_reset_control_get_optional(dev, "xfi");
/* For Ethernet PCS, read the AN7581 SoC revision to check if
* manual rx calibration is needed. This is only limited to
* any SoC revision before E2.
*/
if (device_is_compatible(dev, "airoha,an7581-pcs-eth") &&
priv->data->port_type == AIROHA_PCS_ETH) {
u32 val;
ret = regmap_read(priv->scu, AIROHA_SCU_PDIDR, &val);
if (ret)
return ret;
if (FIELD_GET(AIROHA_SCU_PRODUCT_ID, val) < 0x2)
priv->manual_rx_calib = true;
}
return 0;
}
static const struct airoha_pcs_match_data an7581_pcs_eth = {
.port_type = AIROHA_PCS_ETH,
.hibernation_workaround = true,
.usxgmii_ber_time_fixup = true,
.bringup = an7581_pcs_bringup,
.link_up = an7581_pcs_phya_link_up,
};
static const struct airoha_pcs_match_data an7581_pcs_pon = {
.port_type = AIROHA_PCS_PON,
.hibernation_workaround = true,
.usxgmii_ber_time_fixup = true,
.bringup = an7581_pcs_bringup,
.link_up = an7581_pcs_phya_link_up,
};
static const struct udevice_id airoha_pcs_of_table[] = {
{ .compatible = "airoha,an7581-pcs-eth",
.data = (ulong)&an7581_pcs_eth },
{ .compatible = "airoha,an7581-pcs-pon",
.data = (ulong)&an7581_pcs_pon },
{ },
};
U_BOOT_DRIVER(airoha_pcs) = {
.name = "airoha-pcs",
.id = UCLASS_MISC,
.of_match = airoha_pcs_of_table,
.probe = airoha_pcs_probe,
.priv_auto = sizeof(struct airoha_pcs_priv),
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -9,9 +9,12 @@
*/
#include <dm.h>
#include <dm/device-internal.h>
#include <dm/devres.h>
#include <dm/lists.h>
#include <eth_phy.h>
#include <mapmem.h>
#include <miiphy.h>
#include <net.h>
#include <regmap.h>
#include <reset.h>
@ -19,12 +22,15 @@
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/ethtool.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/time.h>
#include <asm/arch/scu-regmap.h>
#define AIROHA_MAX_NUM_GDM_PORTS 1
#include "airoha/pcs-airoha.h"
#define AIROHA_MAX_NUM_GDM_PORTS 4
#define AIROHA_MAX_NUM_QDMA 1
#define AIROHA_MAX_NUM_RSTS 3
#define AIROHA_MAX_NUM_XSI_RSTS 4
@ -38,6 +44,8 @@
#define TX_DSCP_NUM 16
#define RX_DSCP_NUM PKTBUFSRX
#define AIROHA_GDM_PORT_STRING_LEN sizeof("airoha-gdmX")
/* SCU */
#define SCU_SHARE_FEMEM_SEL 0x958
@ -246,6 +254,21 @@
#define QDMA_ETH_RXMSG_CRSN_MASK GENMASK(20, 16)
#define QDMA_ETH_RXMSG_PPE_ENTRY_MASK GENMASK(15, 0)
enum {
FE_PSE_PORT_CDM1,
FE_PSE_PORT_GDM1,
FE_PSE_PORT_GDM2,
FE_PSE_PORT_GDM3,
FE_PSE_PORT_PPE1,
FE_PSE_PORT_CDM2,
FE_PSE_PORT_CDM3,
FE_PSE_PORT_CDM4,
FE_PSE_PORT_PPE2,
FE_PSE_PORT_GDM4,
FE_PSE_PORT_CDM5,
FE_PSE_PORT_DROP = 0xf,
};
struct airoha_qdma_desc {
__le32 rsv;
__le32 ctrl;
@ -301,20 +324,30 @@ struct airoha_qdma {
struct airoha_gdm_port {
struct airoha_qdma *qdma;
int id;
struct udevice *pcs_dev;
phy_interface_t mode;
bool neg_mode;
struct phy_device *phydev;
};
struct airoha_eth {
void __iomem *fe_regs;
void __iomem *switch_regs;
struct udevice *switch_mdio_dev;
struct reset_ctl_bulk rsts;
struct reset_ctl_bulk xsi_rsts;
struct airoha_eth_soc_data *soc;
struct airoha_qdma qdma[AIROHA_MAX_NUM_QDMA];
struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS];
char gdm_port_str[AIROHA_MAX_NUM_GDM_PORTS + 1][AIROHA_GDM_PORT_STRING_LEN];
};
struct airoha_eth_soc_data {
u32 version;
int num_xsi_rsts;
const char * const *xsi_rsts_names;
const char *switch_compatible;
@ -397,22 +430,33 @@ static inline void dma_unmap_unaligned(dma_addr_t addr, size_t len,
dma_unmap_single(start, end - start, dir);
}
static void airoha_fe_maccr_init(struct airoha_eth *eth)
static int airoha_get_fe_port(struct airoha_gdm_port *port)
{
int p;
struct airoha_qdma *qdma = port->qdma;
struct airoha_eth *eth = qdma->eth;
for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) {
/*
* Disable any kind of CRC drop or offload.
* Enable padding of short TX packets to 60 bytes.
*/
airoha_fe_wr(eth, REG_GDM_FWD_CFG(p), GDM_PAD_EN);
switch (eth->soc->version) {
case 0x7523:
/* FIXME: GDM1 is the only supported port */
return FE_PSE_PORT_GDM1;
case 0x7581:
default:
return port->id == 4 ? FE_PSE_PORT_GDM4 : port->id;
}
}
static int airoha_fe_init(struct airoha_eth *eth)
static void airoha_fe_maccr_init(struct airoha_gdm_port *port)
{
airoha_fe_maccr_init(eth);
/*
* Disable any kind of CRC drop or offload.
* Enable padding of short TX packets to 60 bytes.
*/
airoha_fe_wr(port->qdma->eth, REG_GDM_FWD_CFG(port->id), GDM_PAD_EN);
}
static int airoha_fe_init(struct airoha_gdm_port *port)
{
airoha_fe_maccr_init(port);
return 0;
}
@ -662,6 +706,36 @@ static int airoha_qdma_init(struct udevice *dev,
return airoha_qdma_hw_init(qdma);
}
#if defined(CONFIG_PCS_AIROHA)
static int airoha_pcs_init(struct udevice *dev)
{
struct airoha_gdm_port *port = dev_get_priv(dev);
struct udevice *pcs_dev;
const char *managed;
int ret;
ret = uclass_get_device_by_phandle(UCLASS_MISC, dev, "pcs",
&pcs_dev);
if (ret || !pcs_dev)
return ret;
port->pcs_dev = pcs_dev;
port->mode = dev_read_phy_mode(dev);
managed = dev_read_string(dev, "managed");
port->neg_mode = !strncmp(managed, "in-band-status",
sizeof("in-band-status"));
airoha_pcs_pre_config(pcs_dev, port->mode);
ret = airoha_pcs_post_config(pcs_dev, port->mode);
if (ret)
return ret;
return airoha_pcs_config(pcs_dev, port->neg_mode,
port->mode, NULL, true);
}
#endif
static int airoha_hw_init(struct udevice *dev,
struct airoha_eth *eth)
{
@ -682,12 +756,12 @@ static int airoha_hw_init(struct udevice *dev,
if (ret)
return ret;
mdelay(20);
ret = airoha_fe_init(eth);
ret = reset_deassert_bulk(&eth->xsi_rsts);
if (ret)
return ret;
mdelay(20);
for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) {
ret = airoha_qdma_init(dev, eth, &eth->qdma[i]);
if (ret)
@ -739,11 +813,45 @@ static int airoha_switch_init(struct udevice *dev, struct airoha_eth *eth)
return 0;
}
static int airoha_alloc_gdm_port(struct udevice *dev, ofnode node)
{
struct airoha_eth *eth = dev_get_priv(dev);
struct udevice *gdm_dev;
struct driver *gdm_drv;
char *str;
int ret;
u32 id;
gdm_drv = lists_driver_lookup_name("airoha-eth-port");
if (!gdm_drv)
return -ENOENT;
ret = ofnode_read_u32(node, "reg", &id);
if (ret)
return ret;
if (id > AIROHA_MAX_NUM_GDM_PORTS)
return -EINVAL;
#if !defined(CONFIG_PCS_AIROHA)
if (id != 1)
return -ENOTSUPP;
#endif
str = eth->gdm_port_str[id];
snprintf(str, AIROHA_GDM_PORT_STRING_LEN,
"airoha-gdm%d", id);
return device_bind_with_driver_data(dev, gdm_drv, str,
(ulong)eth, node, &gdm_dev);
}
static int airoha_eth_probe(struct udevice *dev)
{
struct airoha_eth_soc_data *data = (void *)dev_get_driver_data(dev);
struct airoha_eth *eth = dev_get_priv(dev);
struct regmap *scu_regmap;
ofnode node;
int i, ret;
scu_regmap = airoha_get_scu_regmap();
@ -756,6 +864,8 @@ static int airoha_eth_probe(struct udevice *dev)
*/
regmap_write(scu_regmap, SCU_SHARE_FEMEM_SEL, 0x0);
eth->soc = data;
eth->fe_regs = dev_remap_addr_name(dev, "fe");
if (!eth->fe_regs)
return -ENOMEM;
@ -795,13 +905,79 @@ static int airoha_eth_probe(struct udevice *dev)
if (ret)
return ret;
return airoha_switch_init(dev, eth);
ret = airoha_switch_init(dev, eth);
if (ret)
return ret;
ofnode_for_each_subnode(node, dev_ofnode(dev)) {
if (!ofnode_device_is_compatible(node, "airoha,eth-mac"))
continue;
if (!ofnode_is_enabled(node))
continue;
ret = airoha_alloc_gdm_port(dev, node);
if (ret && ret != -ENOTSUPP)
return ret;
}
return 0;
}
static int airoha_eth_port_of_to_plat(struct udevice *dev)
{
struct airoha_gdm_port *port = dev_get_priv(dev);
return dev_read_u32(dev, "reg", &port->id);
}
static int airoha_eth_port_probe(struct udevice *dev)
{
struct airoha_eth *eth = (void *)dev_get_driver_data(dev);
struct airoha_gdm_port *port = dev_get_priv(dev);
struct mdio_perdev_priv *pdata;
struct mii_dev *mdio_bus;
int ret;
port->qdma = &eth->qdma[0];
ret = airoha_fe_init(port);
if (ret)
return ret;
mdio_bus = NULL;
if (port->id > 1) {
#if defined(CONFIG_PCS_AIROHA)
ret = airoha_pcs_init(dev);
if (ret)
return ret;
port->phydev = dm_eth_phy_connect(dev);
if (port->phydev)
mdio_bus = port->phydev->bus;
#else
return -EINVAL;
#endif
} else {
if (eth->switch_mdio_dev &&
!device_probe(eth->switch_mdio_dev)) {
pdata = dev_get_uclass_priv(eth->switch_mdio_dev);
mdio_bus = pdata->mii_bus;
}
}
#ifdef CONFIG_DM_ETH_PHY
if (!IS_ERR_OR_NULL(mdio_bus))
eth_phy_set_mdio_bus(dev, mdio_bus);
#endif
return 0;
}
static int airoha_eth_init(struct udevice *dev)
{
struct airoha_eth *eth = dev_get_priv(dev);
struct airoha_qdma *qdma = &eth->qdma[0];
struct airoha_gdm_port *port = dev_get_priv(dev);
struct airoha_qdma *qdma = port->qdma;
struct airoha_queue *q;
int qid;
@ -814,13 +990,65 @@ static int airoha_eth_init(struct udevice *dev)
GLOBAL_CFG_TX_DMA_EN_MASK |
GLOBAL_CFG_RX_DMA_EN_MASK);
#if defined(CONFIG_PCS_AIROHA)
if (port->id > 1) {
struct phy_device *phydev = port->phydev;
int speed, duplex;
int ret;
if (phydev) {
ret = phy_config(phydev);
if (ret)
return ret;
ret = phy_startup(phydev);
if (ret)
return ret;
speed = phydev->speed;
duplex = phydev->duplex;
} else {
duplex = DUPLEX_FULL;
/* Hardcode speed for linkup */
switch (port->mode) {
case PHY_INTERFACE_MODE_USXGMII:
case PHY_INTERFACE_MODE_10GBASER:
speed = SPEED_10000;
break;
case PHY_INTERFACE_MODE_2500BASEX:
speed = SPEED_2500;
break;
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_1000BASEX:
speed = SPEED_1000;
break;
default:
return -EINVAL;
}
}
airoha_pcs_link_up(port->pcs_dev, port->neg_mode, port->mode,
speed, duplex);
}
#endif
return 0;
}
static void airoha_eth_stop(struct udevice *dev)
{
struct airoha_eth *eth = dev_get_priv(dev);
struct airoha_qdma *qdma = &eth->qdma[0];
struct airoha_gdm_port *port = dev_get_priv(dev);
struct airoha_qdma *qdma = port->qdma;
#if defined(CONFIG_PCS_AIROHA)
if (port->id > 1) {
if (port->phydev)
phy_shutdown(port->phydev);
airoha_pcs_link_down(port->pcs_dev);
}
#endif
airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
GLOBAL_CFG_TX_DMA_EN_MASK |
@ -829,8 +1057,8 @@ static void airoha_eth_stop(struct udevice *dev)
static int airoha_eth_send(struct udevice *dev, void *packet, int length)
{
struct airoha_eth *eth = dev_get_priv(dev);
struct airoha_qdma *qdma = &eth->qdma[0];
struct airoha_gdm_port *port = dev_get_priv(dev);
struct airoha_qdma *qdma = port->qdma;
struct airoha_qdma_desc *desc;
struct airoha_queue *q;
dma_addr_t dma_addr;
@ -852,7 +1080,7 @@ static int airoha_eth_send(struct udevice *dev, void *packet, int length)
desc = &q->desc[q->head];
index = (q->head + 1) % q->ndesc;
fport = 1;
fport = airoha_get_fe_port(port);
msg0 = 0;
msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
@ -894,8 +1122,8 @@ static int airoha_eth_send(struct udevice *dev, void *packet, int length)
static int airoha_eth_recv(struct udevice *dev, int flags, uchar **packetp)
{
struct airoha_eth *eth = dev_get_priv(dev);
struct airoha_qdma *qdma = &eth->qdma[0];
struct airoha_gdm_port *port = dev_get_priv(dev);
struct airoha_qdma *qdma = port->qdma;
struct airoha_qdma_desc *desc;
struct airoha_queue *q;
u16 length;
@ -922,8 +1150,8 @@ static int airoha_eth_recv(struct udevice *dev, int flags, uchar **packetp)
static int arht_eth_free_pkt(struct udevice *dev, uchar *packet, int length)
{
struct airoha_eth *eth = dev_get_priv(dev);
struct airoha_qdma *qdma = &eth->qdma[0];
struct airoha_gdm_port *port = dev_get_priv(dev);
struct airoha_qdma *qdma = port->qdma;
struct airoha_queue *q;
int qid;
@ -964,8 +1192,9 @@ static int arht_eth_free_pkt(struct udevice *dev, uchar *packet, int length)
static int arht_eth_write_hwaddr(struct udevice *dev)
{
struct airoha_gdm_port *port = dev_get_priv(dev);
struct eth_pdata *pdata = dev_get_plat(dev);
struct airoha_eth *eth = dev_get_priv(dev);
struct airoha_qdma *qdma = port->qdma;
unsigned char *mac = pdata->enetaddr;
u32 macaddr_lsb, macaddr_msb;
@ -977,8 +1206,8 @@ static int arht_eth_write_hwaddr(struct udevice *dev)
FIELD_PREP(SMACCR1_MAC0, mac[0]);
/* Set MAC for Switch */
airoha_switch_wr(eth, SWITCH_SMACCR0, macaddr_lsb);
airoha_switch_wr(eth, SWITCH_SMACCR1, macaddr_msb);
airoha_switch_wr(qdma->eth, SWITCH_SMACCR0, macaddr_lsb);
airoha_switch_wr(qdma->eth, SWITCH_SMACCR1, macaddr_msb);
return 0;
}
@ -986,9 +1215,15 @@ static int arht_eth_write_hwaddr(struct udevice *dev)
static int airoha_eth_bind(struct udevice *dev)
{
struct airoha_eth_soc_data *data = (void *)dev_get_driver_data(dev);
struct airoha_eth *eth = dev_get_priv(dev);
ofnode switch_node, mdio_node;
struct udevice *mdio_dev;
int ret = 0;
int ret;
/*
* Force Probe as we set the Main ETH driver as misc
* to register multiple eth port for each GDM
*/
dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND);
if (!CONFIG_IS_ENABLED(MDIO_MT7531_MMIO))
return 0;
@ -1006,8 +1241,8 @@ static int airoha_eth_bind(struct udevice *dev)
return 0;
}
ret = device_bind_driver_to_node(dev, "mt7531-mdio-mmio", "mdio",
mdio_node, &mdio_dev);
ret = device_bind_driver_to_node(dev, "mt7531-mdio-mmio", "mt7531-mdio",
mdio_node, &eth->switch_mdio_dev);
if (ret)
debug("Warning: failed to bind mdio controller\n");
@ -1015,12 +1250,14 @@ static int airoha_eth_bind(struct udevice *dev)
}
static const struct airoha_eth_soc_data en7523_data = {
.version = 0x7523,
.xsi_rsts_names = en7523_xsi_rsts_names,
.num_xsi_rsts = ARRAY_SIZE(en7523_xsi_rsts_names),
.switch_compatible = "airoha,en7523-switch",
};
static const struct airoha_eth_soc_data en7581_data = {
.version = 0x7581,
.xsi_rsts_names = en7581_xsi_rsts_names,
.num_xsi_rsts = ARRAY_SIZE(en7581_xsi_rsts_names),
.switch_compatible = "airoha,en7581-switch",
@ -1045,13 +1282,21 @@ static const struct eth_ops airoha_eth_ops = {
.write_hwaddr = arht_eth_write_hwaddr,
};
U_BOOT_DRIVER(airoha_eth_port) = {
.name = "airoha-eth-port",
.id = UCLASS_ETH,
.of_to_plat = airoha_eth_port_of_to_plat,
.probe = airoha_eth_port_probe,
.ops = &airoha_eth_ops,
.priv_auto = sizeof(struct airoha_gdm_port),
.plat_auto = sizeof(struct eth_pdata),
};
U_BOOT_DRIVER(airoha_eth) = {
.name = "airoha-eth",
.id = UCLASS_ETH,
.id = UCLASS_MISC,
.of_match = airoha_eth_ids,
.probe = airoha_eth_probe,
.bind = airoha_eth_bind,
.ops = &airoha_eth_ops,
.priv_auto = sizeof(struct airoha_eth),
.plat_auto = sizeof(struct eth_pdata),
};

View File

@ -8,4 +8,4 @@ config PHY_AIROHA_EN8811
select FW_LOADER
help
AIROHA EN8811H supported.
AIROHA AN8811HB supported.

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for the Airoha EN8811H 2.5 Gigabit PHY.
* Driver for the Airoha EN8811H and AN8811HB 2.5 Gigabit PHY.
*
* Limitations of the EN8811H:
* - Only full duplex supported
@ -8,9 +8,8 @@
*
* Source originated from linux air_en8811h.c
*
* Copyright (C) 2025 Airoha Technology Corp.
* Copyright (C) 2025, 2026 Airoha Technology Corp.
*/
#include <phy.h>
#include <errno.h>
#include <log.h>
@ -20,27 +19,15 @@
#include <asm/unaligned.h>
#include <linux/iopoll.h>
#include <linux/bitops.h>
#include <linux/bitfield.h>
#include <linux/compat.h>
#include <dm/device_compat.h>
#include <u-boot/crc.h>
#define EN8811H_PHY_ID 0x03a2a411
#define AIR_FW_ADDR_DM 0x00000000
#define AIR_FW_ADDR_DSP 0x00100000
#define EN8811H_MD32_DM_SIZE 0x4000
#define EN8811H_MD32_DSP_SIZE 0x20000
#define EN8811H_FW_CTRL_1 0x0f0018
#define EN8811H_FW_CTRL_1_START 0x0
#define EN8811H_FW_CTRL_1_FINISH 0x1
#define EN8811H_FW_CTRL_2 0x800000
#define EN8811H_FW_CTRL_2_LOADING BIT(11)
/* MII Registers */
#define AIR_AUX_CTRL_STATUS 0x1d
#define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2)
#define AIR_AUX_CTRL_STATUS_SPEED_10 0x0
#define AIR_AUX_CTRL_STATUS_SPEED_100 0x4
#define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8
#define AIR_AUX_CTRL_STATUS_SPEED_2500 0xc
@ -49,6 +36,7 @@
#define AIR_PHY_PAGE_STANDARD 0x0000
#define AIR_PHY_PAGE_EXTENDED_4 0x0004
#define AIR_PBUS_MODE_ADDR_HIGH 0x1c
/* MII Registers Page 4 */
#define AIR_BPBUS_MODE 0x10
#define AIR_BPBUS_MODE_ADDR_FIXED 0x0000
@ -63,8 +51,16 @@
#define AIR_BPBUS_RD_DATA_LOW 0x18
/* Registers on MDIO_MMD_VEND1 */
#define EN8811H_PHY_FW_STATUS 0x8009
#define EN8811H_PHY_READY 0x02
#define AIR_PHY_MCU_CMD_1 0x800c
#define AIR_PHY_MCU_CMD_1_MODE1 0x0
#define AIR_PHY_MCU_CMD_2 0x800d
#define AIR_PHY_MCU_CMD_2_MODE1 0x0
#define AIR_PHY_MCU_CMD_3 0x800e
#define AIR_PHY_MCU_CMD_3_MODE1 0x1101
#define AIR_PHY_MCU_CMD_3_DOCMD 0x1100
#define AIR_PHY_MCU_CMD_4 0x800f
#define AIR_PHY_MCU_CMD_4_MODE1 0x0002
#define AIR_PHY_MCU_CMD_4_INTCLR 0x00e4
/* Registers on MDIO_MMD_VEND2 */
#define AIR_PHY_LED_BCR 0x021
@ -77,7 +73,7 @@
#define AIR_PHY_LED_DUR_BLINK 0x023
#define AIR_PHY_LED_ON(i) (0x024 + ((i) * 2))
#define AIR_PHY_LED_ON(i) (0x024 + ((i) * 2))
#define AIR_PHY_LED_ON_MASK (GENMASK(6, 0) | BIT(8))
#define AIR_PHY_LED_ON_LINK1000 BIT(0)
#define AIR_PHY_LED_ON_LINK100 BIT(1)
@ -90,7 +86,7 @@
#define AIR_PHY_LED_ON_POLARITY BIT(14)
#define AIR_PHY_LED_ON_ENABLE BIT(15)
#define AIR_PHY_LED_BLINK(i) (0x025 + ((i) * 2))
#define AIR_PHY_LED_BLINK(i) (0x025 + ((i) * 2))
#define AIR_PHY_LED_BLINK_1000TX BIT(0)
#define AIR_PHY_LED_BLINK_1000RX BIT(1)
#define AIR_PHY_LED_BLINK_100TX BIT(2)
@ -104,21 +100,101 @@
#define AIR_PHY_LED_BLINK_2500TX BIT(10)
#define AIR_PHY_LED_BLINK_2500RX BIT(11)
/* Registers on BUCKPBUS */
#define AIR_PHY_CONTROL 0x3a9c
#define AIR_PHY_CONTROL_SURGE_5R BIT(3)
#define AIR_PHY_CONTROL_INTERNAL BIT(11)
/* Led definitions */
#define EN8811H_LED_COUNT 3
/* Firmware registers */
#define AIR_FW_ADDR_DM 0x00000000
#define AIR_FW_ADDR_DSP 0x00100000
#define EN8811H_FW_CTRL_1 0x0f0018
#define EN8811H_FW_CTRL_1_START 0x0
#define EN8811H_FW_CTRL_1_FINISH 0x1
#define EN8811H_FW_CTRL_2 0x800000
#define EN8811H_FW_CTRL_2_LOADING BIT(11)
#define EN8811H_PHY_FW_STATUS 0x8009
#define EN8811H_PHY_READY 0x02
#define AIR_PHY_FW_STATUS 0x8009
#define AIR_PHY_READY 0x02
#define AIR_PHY_FW_CTRL_1 0x0f0018
#define AIR_PHY_FW_CTRL_1_START 0x0
#define AIR_PHY_FW_CTRL_1_FINISH 0x1
/* EN8811H */
#define EN8811H_PHY_ID 0x03a2a411
#define EN8811H_MD32_DM_SIZE 0x4000
#define EN8811H_MD32_DSP_SIZE 0x20000
#define EN8811H_FW_VERSION 0x3b3c
#define EN8811H_POLARITY 0xca0f8
#define EN8811H_POLARITY_TX_NORMAL BIT(0)
#define EN8811H_POLARITY_RX_REVERSE BIT(1)
#define EN8811H_CLK_CGM 0xcf958
#define EN8811H_CLK_CGM_CKO BIT(26)
#define EN8811H_HWTRAP1 0xcf914
#define EN8811H_HWTRAP1_CKO BIT(12)
#define clear_bit(bit, bitmap) __clear_bit(bit, bitmap)
/* AN8811HB */
#define AN8811HB_PHY_ID 0xc0ff04a0
#define AIR_MD32_DM_SIZE 0x8000
#define AIR_MD32_DSP_SIZE 0x20000
#define AIR_PHY_MD32FW_VERSION 0x3b3c
/* Led definitions */
#define EN8811H_LED_COUNT 3
#define AN8811HB_GPIO_OUTPUT 0x5cf8b8
#define AN8811HB_GPIO_OUTPUT_MASK GENMASK(15, 0)
#define AN8811HB_GPIO_OUTPUT_345 (BIT(3) | BIT(4) | BIT(5))
#define AN8811HB_GPIO_OUTPUT_0115 (BIT(0) | BIT(1) | BIT(15))
#define AN8811HB_GPIO_SEL_1 0x5cf8bc
#define AN8811HB_GPIO_SEL_1_0_MASK GENMASK(2, 0)
#define AN8811HB_GPIO_SEL_1_1_MASK GENMASK(6, 4)
#define AN8811HB_GPIO_SEL_1_0 FIELD_PREP(AN8811HB_GPIO_SEL_1_0_MASK, 1)
#define AN8811HB_GPIO_SEL_1_1 FIELD_PREP(AN8811HB_GPIO_SEL_1_1_MASK, 0)
#define AN8811HB_GPIO_SEL_2 0x5cf8c0
#define AN8811HB_GPIO_SEL_2_15_MASK GENMASK(30, 28)
#define AN8811HB_GPIO_SEL_2_15 FIELD_PREP(AN8811HB_GPIO_SEL_2_15_MASK, 2)
#define AN8811HB_CRC_PM_SET1 0xf020c
#define AN8811HB_CRC_PM_MON2 0xf0218
#define AN8811HB_CRC_PM_MON3 0xf021c
#define AN8811HB_CRC_DM_SET1 0xf0224
#define AN8811HB_CRC_DM_MON2 0xf0230
#define AN8811HB_CRC_DM_MON3 0xf0234
#define AN8811HB_CRC_RD_EN BIT(0)
#define AN8811HB_CRC_ST (BIT(0) | BIT(1))
#define AN8811HB_CRC_CHECK_PASS BIT(0)
#define AN8811HB_TX_POLARITY 0x5ce004
#define AN8811HB_TX_POLARITY_NORMAL BIT(7)
#define AN8811HB_RX_POLARITY 0x5ce61c
#define AN8811HB_RX_POLARITY_NORMAL BIT(7)
#define AN8811HB_HWTRAP1 0x5cf910
#define AN8811HB_HWTRAP2 0x5cf914
#define AN8811HB_HWTRAP2_CKO BIT(28)
#define AN8811HB_HWTRAP2_PKG (BIT(12) | BIT(13) | BIT(14))
#define AN8811HB_PRO_ID 0x5cf920
#define AN8811HB_PRO_ID_VERSION GENMASK(3, 0)
#define AN8811HB_CLK_DRV 0x5cf9e4
#define AN8811HB_CLK_DRV_CKO_MASK GENMASK(14, 12)
#define AN8811HB_CLK_DRV_CKOPWD BIT(12)
#define AN8811HB_CLK_DRV_CKO_LDPWD BIT(13)
#define AN8811HB_CLK_DRV_CKO_LPPWD BIT(14)
#define AN8811HB_MCU_SW_RST 0x5cf9f8
#define AN8811HB_MCU_SW_RST_HOLD BIT(16)
#define AN8811HB_MCU_SW_RST_RUN (BIT(16) | BIT(0))
#define AN8811HB_MCU_SW_START 0x5cf9fc
#define AN8811HB_MCU_SW_START_EN BIT(16)
#define clear_bit(bit, bitmap) __clear_bit(bit, bitmap)
#define SCRIPT_NAME(name) #name "_load_firmware"
struct led {
unsigned long rules;
@ -191,11 +267,48 @@ enum air_led_trigger_netdev_modes {
#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64MS)
struct en8811h_priv {
int firmware_version;
u32 firmware_version;
bool mcu_needs_restart;
struct led led[EN8811H_LED_COUNT];
u32 pro_id;
u32 pkg_sel;
u32 mem_size;
const char *script_name;
};
static int air_pbus_reg_write(struct phy_device *phydev,
u32 pbus_reg, u32 pbus_data)
{
int pbus_addr = (phydev->addr) + 8;
struct mii_dev *bus = phydev->bus;
int ret;
ret = bus->write(bus, pbus_addr, MDIO_DEVAD_NONE,
AIR_EXT_PAGE_ACCESS,
(pbus_reg >> 16));
if (ret < 0)
return ret;
ret = bus->write(bus, pbus_addr, MDIO_DEVAD_NONE,
AIR_PBUS_MODE_ADDR_HIGH,
((pbus_reg & GENMASK(15, 6)) >> 6));
if (ret < 0)
return ret;
ret = bus->write(bus, pbus_addr, MDIO_DEVAD_NONE,
((pbus_reg & GENMASK(5, 2)) >> 2),
(pbus_data & GENMASK(15, 0)));
if (ret < 0)
return ret;
ret = bus->write(bus, pbus_addr, MDIO_DEVAD_NONE, 0x10,
((pbus_data & GENMASK(31, 16)) >> 16));
if (ret < 0)
return ret;
return ret;
}
static int air_buckpbus_reg_write(struct phy_device *phydev,
u32 pbus_address, u32 pbus_data)
{
@ -359,8 +472,8 @@ restore_page:
static int air_write_buf(struct phy_device *phydev, unsigned long address,
unsigned long array_size, const unsigned char *buffer)
{
unsigned int offset;
int ret, saved_page;
u32 offset;
u16 val;
saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
@ -419,18 +532,144 @@ static int en8811h_wait_mcu_ready(struct phy_device *phydev)
return ret;
}
int en8811h_read_fw(void **fw, size_t *fwsize)
static int an8811hb_check_crc(struct phy_device *phydev,
u32 set1, u32 mon2, u32 mon3)
{
int ret, retry = 10;
u32 pbus_value;
/* Configure CRC */
ret = air_buckpbus_reg_modify(phydev, set1, AN8811HB_CRC_RD_EN,
AN8811HB_CRC_RD_EN);
if (ret < 0)
return ret;
ret = air_buckpbus_reg_read(phydev, set1, &pbus_value);
if (ret < 0)
return ret;
debug("%d: reg 0x%x val 0x%x!\n", __LINE__, set1, pbus_value);
do {
mdelay(300);
ret = air_buckpbus_reg_read(phydev, mon2, &pbus_value);
if (ret < 0)
return ret;
debug("%d: reg 0x%x val 0x%x!\n", __LINE__, mon2, pbus_value);
if (pbus_value & AN8811HB_CRC_ST) {
ret = air_buckpbus_reg_read(phydev, mon3, &pbus_value);
if (ret < 0)
return ret;
debug("%d: reg 0x%x val 0x%x!\n", __LINE__, mon3,
pbus_value);
if (pbus_value & AN8811HB_CRC_CHECK_PASS)
debug("CRC Check PASS!\n");
else
dev_err(phydev->dev, "CRC Check FAIL!(0x%lx)\n",
pbus_value & AN8811HB_CRC_CHECK_PASS);
break;
}
if (!retry) {
dev_err(phydev->dev,
"CRC Check is not ready.(Status %u)\n",
pbus_value);
return -ENODEV;
}
} while (--retry);
ret = air_buckpbus_reg_modify(phydev, set1, AN8811HB_CRC_RD_EN, 0);
if (ret < 0)
return ret;
ret = air_buckpbus_reg_read(phydev, set1, &pbus_value);
if (ret < 0)
return ret;
debug("%d: reg 0x%x val 0x%x!\n", __LINE__, set1, pbus_value);
return ret;
}
static int an8811hb_mcu_assert(struct phy_device *phydev)
{
int ret;
ret = air_pbus_reg_write(phydev, AN8811HB_MCU_SW_RST,
AN8811HB_MCU_SW_RST_HOLD);
if (ret < 0)
return ret;
ret = air_pbus_reg_write(phydev, AN8811HB_MCU_SW_START, 0);
if (ret < 0)
return ret;
debug("MCU asserted\n");
mdelay(50);
return ret;
}
static int an8811hb_mcu_deassert(struct phy_device *phydev)
{
int ret;
ret = air_pbus_reg_write(phydev, AN8811HB_MCU_SW_START,
AN8811HB_MCU_SW_START_EN);
if (ret < 0)
return ret;
ret = air_pbus_reg_write(phydev, AN8811HB_MCU_SW_RST,
AN8811HB_MCU_SW_RST_RUN);
if (ret < 0)
return ret;
debug("MCU deasserted\n");
mdelay(50);
return ret;
}
static int an8811hb_surge_protect_cfg(struct phy_device *phydev)
{
ofnode node = phy_get_ofnode(phydev);
int ret = 0;
if (!ofnode_read_bool(node, "airoha,surge-5r")) {
debug("Surge Protection mode - 0R\n");
return ret;
}
ret = air_buckpbus_reg_modify(phydev, AIR_PHY_CONTROL,
AIR_PHY_CONTROL_SURGE_5R,
AIR_PHY_CONTROL_SURGE_5R);
if (ret < 0)
return ret;
debug("Surge Protection mode - 5R\n");
return ret;
}
static int en8811h_read_fw(void **fw, size_t *fwsize, struct en8811h_priv *priv)
{
const char *script_name = priv->script_name;
u32 mem_size = priv->mem_size;
void *buffer;
int ret;
buffer = malloc(EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE);
buffer = malloc(mem_size);
if (!buffer)
return -ENOMEM;
ret = request_firmware_into_buf_via_script(buffer,
EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE,
"en8811h_load_firmware", fwsize);
ret = request_firmware_into_buf_via_script(buffer, mem_size,
script_name, fwsize);
if (ret) {
free(buffer);
return ret;
@ -450,7 +689,10 @@ static int en8811h_load_firmware(struct phy_device *phydev)
void *buffer;
int ret;
ret = en8811h_read_fw(&buffer, &fw_size);
priv->script_name = SCRIPT_NAME(en8811h);
priv->mem_size = EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE;
ret = en8811h_read_fw(&buffer, &fw_size, priv);
if (ret < 0) {
dev_err(phydev->dev, "Failed to get firmware data\n");
return -EINVAL;
@ -496,9 +738,12 @@ static int en8811h_load_firmware(struct phy_device *phydev)
goto en8811h_load_firmware_out;
ret = en8811h_wait_mcu_ready(phydev);
if (ret < 0)
goto en8811h_load_firmware_out;
air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION,
&priv->firmware_version);
dev_info(phydev->dev, "MD32 firmware version: %08x\n",
priv->firmware_version);
@ -510,6 +755,130 @@ en8811h_load_firmware_out:
return ret;
}
static int an8811hb_load_firmware(struct phy_device *phydev)
{
struct en8811h_priv *priv = phydev->priv;
int ret, retry = 10;
size_t fw_size;
void *buffer;
u32 reg_val;
ret = an8811hb_mcu_assert(phydev);
if (ret < 0)
return ret;
ret = an8811hb_mcu_deassert(phydev);
if (ret < 0)
return ret;
priv->script_name = SCRIPT_NAME(an8811hb);
priv->mem_size = AIR_MD32_DM_SIZE + AIR_MD32_DSP_SIZE;
ret = en8811h_read_fw(&buffer, &fw_size, priv);
if (ret < 0)
goto an8811hb_load_firmware_out;
ret = air_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,
AIR_PHY_FW_CTRL_1_START);
if (ret < 0)
goto an8811hb_load_firmware_out;
ret = air_write_buf(phydev, AIR_FW_ADDR_DM, AIR_MD32_DM_SIZE,
(unsigned char *)buffer);
if (ret < 0)
goto an8811hb_load_firmware_out;
ret = an8811hb_check_crc(phydev, AN8811HB_CRC_DM_SET1,
AN8811HB_CRC_DM_MON2, AN8811HB_CRC_DM_MON3);
if (ret < 0)
goto an8811hb_load_firmware_out;
ret = air_write_buf(phydev, AIR_FW_ADDR_DSP, AIR_MD32_DSP_SIZE,
(unsigned char *)buffer + AIR_MD32_DM_SIZE);
if (ret < 0)
goto an8811hb_load_firmware_out;
ret = an8811hb_check_crc(phydev, AN8811HB_CRC_PM_SET1,
AN8811HB_CRC_PM_MON2, AN8811HB_CRC_PM_MON3);
if (ret < 0)
goto an8811hb_load_firmware_out;
ret = air_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,
AIR_PHY_FW_CTRL_1_FINISH);
if (ret < 0)
goto an8811hb_load_firmware_out;
ret = an8811hb_surge_protect_cfg(phydev);
if (ret < 0) {
dev_err(phydev->dev, "an8811hb_surge_protect_cfg fail. (ret=%d)\n", ret);
goto an8811hb_load_firmware_out;
}
do {
mdelay(300);
ret = air_buckpbus_reg_read(phydev, AIR_PHY_FW_CTRL_1, &reg_val);
if (ret < 0)
goto an8811hb_load_firmware_out;
if (reg_val == AIR_PHY_FW_CTRL_1_FINISH)
break;
debug("%d: reg 0x%x val 0x%x!\n", __LINE__, AIR_PHY_FW_CTRL_1,
reg_val);
ret = air_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,
AIR_PHY_FW_CTRL_1_FINISH);
if (ret < 0)
goto an8811hb_load_firmware_out;
} while (--retry);
ret = en8811h_wait_mcu_ready(phydev);
if (ret < 0)
goto an8811hb_load_firmware_out;
air_buckpbus_reg_read(phydev, AIR_PHY_MD32FW_VERSION,
&priv->firmware_version);
debug("MD32 firmware version: %08x\n", priv->firmware_version);
an8811hb_load_firmware_out:
free(buffer);
if (ret < 0)
dev_err(phydev->dev, "Firmware loading failed: %d\n", ret);
return ret;
}
int an8811hb_cko_cfg(struct phy_device *phydev)
{
ofnode node = phy_get_ofnode(phydev);
u32 pbus_value;
int ret = 0;
if (!ofnode_read_bool(node, "airoha,phy-output-clock")) {
ret = air_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV,
AN8811HB_CLK_DRV_CKO_MASK,
AN8811HB_CLK_DRV_CKOPWD |
AN8811HB_CLK_DRV_CKO_LDPWD |
AN8811HB_CLK_DRV_CKO_LPPWD);
if (ret < 0)
return ret;
debug("CKO Output mode - Disabled\n");
} else {
ret = air_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value);
if (ret < 0)
return ret;
debug("CKO Output %dMHz - Enabled\n",
(pbus_value & AN8811HB_HWTRAP2_CKO) ? 50 : 25);
}
return ret;
}
static int en8811h_restart_mcu(struct phy_device *phydev)
{
int ret;
@ -613,13 +982,30 @@ static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol)
return 0;
}
/**
* air_leds_init - Initialize and configure LEDs for a phy device.
*
* @phydev: Pointer to the phy_device structure.
* @num: Number of LEDs to initialize.
* @dur: Duration for LED blink in milliseconds. It sets the duration
* for both the ON and OFF periods (OFF period will be half of `dur`).
* @mode: LED operation mode. Supported modes are:
* - AIR_LED_MODE_DISABLE: Disables LED control.
* - AIR_LED_MODE_USER_DEFINE: Enables user-defined LED control.
*
* Initializes and configures LEDs on a phy device with a specified blink duration
* and mode. Supports disabling or enabling user-defined control.
* Return:
* On success, returns 0. On error, it returns a negative value that denotes
* the error code.
*/
static int air_leds_init(struct phy_device *phydev, int num, u16 dur, int mode)
{
struct en8811h_priv *priv = phydev->priv;
int ret, i;
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK,
dur);
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK, dur);
if (ret < 0)
return ret;
@ -707,7 +1093,8 @@ static int en8811h_config(struct phy_device *phydev)
pbus_value |= EN8811H_POLARITY_TX_NORMAL;
ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
EN8811H_POLARITY_RX_REVERSE |
EN8811H_POLARITY_TX_NORMAL, pbus_value);
EN8811H_POLARITY_TX_NORMAL,
pbus_value);
if (ret < 0)
return ret;
@ -721,6 +1108,198 @@ static int en8811h_config(struct phy_device *phydev)
return 0;
}
static int an8811hb_config(struct phy_device *phydev)
{
struct en8811h_priv *priv = phydev->priv;
u32 pbus_value = 0;
ofnode node;
int ret = 0;
node = phy_get_ofnode(phydev);
if (!ofnode_valid(node))
return 0;
/* If restart happened in .probe(), no need to restart now */
if (priv->mcu_needs_restart) {
ret = an8811hb_mcu_assert(phydev);
if (ret < 0)
return ret;
ret = an8811hb_mcu_deassert(phydev);
if (ret < 0)
return ret;
ret = en8811h_restart_mcu(phydev);
if (ret < 0)
return ret;
} else {
ret = an8811hb_load_firmware(phydev);
if (ret) {
dev_err(phydev->dev, "Load firmware fail.\n");
return ret;
}
/* Next calls to .config() mcu needs to restart */
priv->mcu_needs_restart = true;
}
ret = air_buckpbus_reg_read(phydev, AN8811HB_PRO_ID, &pbus_value);
if (ret < 0)
return ret;
priv->pro_id = (pbus_value & AN8811HB_PRO_ID_VERSION) + 1;
ret = air_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value);
if (ret < 0)
return ret;
priv->pkg_sel = (pbus_value & AN8811HB_HWTRAP2_PKG) >> 12;
debug("%s(%d) Version: E%d\n",
priv->pkg_sel ? "AN8811HBCN" : "AN8811HBN", priv->pkg_sel,
priv->pro_id);
/* Serdes polarity */
pbus_value = 0;
if (ofnode_read_bool(node, "airoha,pnswap-rx"))
pbus_value &= ~AN8811HB_RX_POLARITY_NORMAL;
else
pbus_value |= AN8811HB_RX_POLARITY_NORMAL;
debug("1 pbus_value 0x%x\n", pbus_value);
ret = air_buckpbus_reg_modify(phydev, AN8811HB_RX_POLARITY,
AN8811HB_RX_POLARITY_NORMAL, pbus_value);
if (ret < 0)
return ret;
pbus_value = 0;
if (ofnode_read_bool(node, "airoha,pnswap-tx"))
pbus_value &= ~AN8811HB_TX_POLARITY_NORMAL;
else
pbus_value |= AN8811HB_TX_POLARITY_NORMAL;
debug("2 pbus_value 0x%x\n", pbus_value);
ret = air_buckpbus_reg_modify(phydev, AN8811HB_TX_POLARITY,
AN8811HB_TX_POLARITY_NORMAL, pbus_value);
if (ret < 0)
return ret;
/* Configure led gpio pins as output */
if (priv->pkg_sel) {
ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT,
AN8811HB_GPIO_OUTPUT_MASK,
AN8811HB_GPIO_OUTPUT_0115);
if (ret < 0)
return ret;
ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_SEL_1,
AN8811HB_GPIO_SEL_1_0_MASK |
AN8811HB_GPIO_SEL_1_1_MASK,
AN8811HB_GPIO_SEL_1_0 |
AN8811HB_GPIO_SEL_1_1);
if (ret < 0)
return ret;
ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_SEL_2,
AN8811HB_GPIO_SEL_2_15_MASK,
AN8811HB_GPIO_SEL_2_15);
if (ret < 0)
return ret;
} else {
ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT,
AN8811HB_GPIO_OUTPUT_345,
AN8811HB_GPIO_OUTPUT_345);
if (ret < 0)
return ret;
}
ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR,
AIR_LED_MODE_USER_DEFINE);
if (ret < 0) {
dev_err(phydev->dev, "Failed to disable leds: %d\n", ret);
return ret;
}
/* Co-Clock Output */
ret = an8811hb_cko_cfg(phydev);
if (ret)
return ret;
printf("AN8811HB initialize OK !\n");
return 0;
}
static int an8811hb_update_duplex(struct phy_device *phydev)
{
int lpa;
if (phydev->autoneg == AUTONEG_ENABLE) {
lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA);
if (lpa < 0)
return lpa;
switch (phydev->speed) {
case SPEED_2500:
case SPEED_1000:
phydev->duplex = DUPLEX_FULL;
break;
case SPEED_100:
phydev->duplex = (lpa & LPA_100FULL) ? DUPLEX_FULL :
DUPLEX_HALF;
break;
case SPEED_10:
phydev->duplex = (lpa & LPA_10FULL) ? DUPLEX_FULL :
DUPLEX_HALF;
break;
}
} else {
int bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
if (phydev->speed == SPEED_2500)
phydev->duplex = DUPLEX_FULL;
else
phydev->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL :
DUPLEX_HALF;
}
return 0;
}
static int an8811hb_parse_status(struct phy_device *phydev)
{
int ret = 0, reg_value;
reg_value = phy_read(phydev, MDIO_DEVAD_NONE, AIR_AUX_CTRL_STATUS);
if (reg_value < 0)
return reg_value;
switch (reg_value & AIR_AUX_CTRL_STATUS_SPEED_MASK) {
case AIR_AUX_CTRL_STATUS_SPEED_2500:
phydev->speed = SPEED_2500;
break;
case AIR_AUX_CTRL_STATUS_SPEED_1000:
phydev->speed = SPEED_1000;
break;
case AIR_AUX_CTRL_STATUS_SPEED_100:
phydev->speed = SPEED_100;
break;
case AIR_AUX_CTRL_STATUS_SPEED_10:
phydev->speed = SPEED_10;
break;
default:
dev_err(phydev->dev,
"Auto-neg error, defaulting to 2500M/FD\n");
phydev->speed = SPEED_2500;
phydev->duplex = DUPLEX_FULL;
return 0;
}
/* Update duplex mode based on speed and negotiation status */
ret = an8811hb_update_duplex(phydev);
if (ret < 0)
return ret;
debug("Speed: %d, %s duplex\n", phydev->speed,
(phydev->duplex) ? "full" : "half");
return ret;
}
static int en8811h_parse_status(struct phy_device *phydev)
{
int ret = 0, reg_value;
@ -742,7 +1321,8 @@ static int en8811h_parse_status(struct phy_device *phydev)
phydev->speed = SPEED_100;
break;
default:
dev_err(phydev->dev, "Auto-neg error, defaulting to 2500M/FD\n");
dev_err(phydev->dev,
"Auto-neg error, defaulting to 2500M/FD\n");
phydev->speed = SPEED_2500;
break;
}
@ -752,24 +1332,35 @@ static int en8811h_parse_status(struct phy_device *phydev)
static int en8811h_startup(struct phy_device *phydev)
{
u32 phy_id = phydev->phy_id;
int ret = 0;
ret = genphy_update_link(phydev);
if (ret)
return ret;
return en8811h_parse_status(phydev);
if (phy_id == EN8811H_PHY_ID)
ret = en8811h_parse_status(phydev);
else if (phy_id == AN8811HB_PHY_ID)
ret = an8811hb_parse_status(phydev);
return ret;
}
static int en8811h_probe(struct phy_device *phydev)
{
struct en8811h_priv *priv;
int phy_id;
priv = malloc(sizeof(*priv));
if (!priv)
return -ENOMEM;
memset(priv, 0, sizeof(*priv));
debug("%s driver is probed.\n", phydev->drv->name);
get_phy_id(phydev->bus, phydev->addr, MDIO_DEVAD_NONE, &phy_id);
debug("phy id is 0x%x.\n", phy_id);
priv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0;
priv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1;
priv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2;
@ -782,12 +1373,12 @@ static int en8811h_probe(struct phy_device *phydev)
return 0;
}
static int en8811h_read_page(struct phy_device *phydev)
static int air_phy_read_page(struct phy_device *phydev)
{
return phy_read(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS);
}
static int en8811h_write_page(struct phy_device *phydev, int page)
static int air_phy_write_page(struct phy_device *phydev, int page)
{
return phy_write(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS, page);
}
@ -798,8 +1389,20 @@ U_BOOT_PHY_DRIVER(en8811h) = {
.mask = 0x0ffffff0,
.config = &en8811h_config,
.probe = &en8811h_probe,
.read_page = &en8811h_read_page,
.write_page = &en8811h_write_page,
.read_page = &air_phy_read_page,
.write_page = &air_phy_write_page,
.startup = &en8811h_startup,
.shutdown = &genphy_shutdown,
};
U_BOOT_PHY_DRIVER(an8811hb) = {
.name = "Airoha AN8811HB",
.uid = AN8811HB_PHY_ID,
.mask = 0x0ffffff0,
.config = &an8811hb_config,
.probe = &en8811h_probe,
.read_page = &air_phy_read_page,
.write_page = &air_phy_write_page,
.startup = &en8811h_startup,
.shutdown = &genphy_shutdown,
};

View File

@ -65,7 +65,21 @@
#define MEM_ALIGNMENT 8
#define MEMP_NUM_TCP_SEG 16
/* IP fragmentation parameters for TFTP reassembly */
#define IP_FRAG_MTU_USABLE 1480
#define PBUF_POOL_HEADROOM 6
#define PBUF_POOL_RESERVE 4
#define TFTP_BLOCKSIZE_THRESHOLD 4096
#if defined(CONFIG_TFTP_BLOCKSIZE) && (CONFIG_TFTP_BLOCKSIZE > TFTP_BLOCKSIZE_THRESHOLD)
#define PBUF_POOL_SIZE (((CONFIG_TFTP_BLOCKSIZE + (IP_FRAG_MTU_USABLE - 1)) / \
IP_FRAG_MTU_USABLE) + PBUF_POOL_HEADROOM)
#define IP_REASS_MAX_PBUFS (PBUF_POOL_SIZE - PBUF_POOL_RESERVE)
#else
#define PBUF_POOL_SIZE 8
#define IP_REASS_MAX_PBUFS 4
#endif
#define LWIP_ARP 1
#define ARP_TABLE_SIZE 4
@ -76,7 +90,7 @@
#define IP_REASSEMBLY 1
#define IP_FRAG 1
#define IP_REASS_MAXAGE 3
#define IP_REASS_MAX_PBUFS 4
#define IP_FRAG_USES_STATIC_BUF 0
#define IP_DEFAULT_TTL 255
@ -121,9 +135,13 @@
#define LWIP_UDP 0
#endif
/*
* PBUF_POOL_BUFSIZE is derived from TCP_MSS even when
* CONFIG_PROT_TCP_LWIP is not defined
*/
#define TCP_MSS 1460
#if defined(CONFIG_PROT_TCP_LWIP)
#define LWIP_TCP 1
#define TCP_MSS 1460
#define TCP_WND CONFIG_LWIP_TCP_WND
#define LWIP_WND_SCALE 1
#define TCP_RCV_SCALE 0x7