diff options
Diffstat (limited to 'drivers/net')
25 files changed, 3765 insertions, 1345 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 88ff025a37b..1563404ca17 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -966,26 +966,7 @@ config TSEC_ENET This driver implements support for the (Enhanced) Three-Speed Ethernet Controller found on Freescale SoCs. -config MEDIATEK_ETH - bool "MediaTek Ethernet GMAC Driver" - select PHYLIB - select DM_GPIO - select DM_RESET - help - This Driver support MediaTek Ethernet GMAC - Say Y to enable support for the MediaTek Ethernet GMAC. - -if MEDIATEK_ETH - -config MTK_ETH_SGMII - bool - default y if ARCH_MEDIATEK && !TARGET_MT7623 - -config MTK_ETH_XGMII - bool - default y if TARGET_MT7987 || TARGET_MT7988 - -endif # MEDIATEK_ETH +source "drivers/net/mtk_eth/Kconfig" config HIFEMAC_ETH bool "HiSilicon Fast Ethernet Controller" @@ -1019,6 +1000,20 @@ config FSL_ENETC This driver supports the NXP ENETC Ethernet controller found on some of the NXP SoCs. +config FSL_ENETC_NETC_BLK_CTRL + bool "NXP ENETC NETC blocks control driver" + depends on FSL_ENETC && IMX95 + default y if IMX95 + help + This driver configures Integrated Endpoint Register Block (IERB) and + Privileged Register Block (PRB) of NETC. For i.MX platforms, it also + includes the configuration of NETCMIX block. + The IERB contains registers that are used for pre-boot initialization, + debug, and non-customer configuration. The PRB controls global reset + and global error handling for NETC. The NETCMIX block is mainly used + to set MII protocol and PCS protocol of the links, it also contains + settings for some other functions. + config MDIO_GPIO_BITBANG bool "GPIO bitbanging MDIO driver" depends on DM_MDIO && DM_GPIO diff --git a/drivers/net/Makefile b/drivers/net/Makefile index e51a917933e..80d70212971 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_FEC_MXC) += fec_mxc.o obj-$(CONFIG_FMAN_ENET) += fm/ obj-$(CONFIG_FMAN_ENET) += fsl_mdio.o obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o fsl_enetc_mdio.o +obj-$(CONFIG_FSL_ENETC_NETC_BLK_CTRL) += fsl_enetc_netc_blk_ctrl.o obj-$(CONFIG_FSL_LS_MDIO) += fsl_ls_mdio.o obj-$(CONFIG_FSL_MC_ENET) += fsl-mc/ obj-$(CONFIG_FSL_MC_ENET) += ldpaa_eth/ @@ -68,7 +69,7 @@ obj-$(CONFIG_MDIO_MUX_MESON_GXL) += mdio_mux_meson_gxl.o obj-$(CONFIG_MDIO_MUX_MMIOREG) += mdio_mux_mmioreg.o obj-$(CONFIG_MDIO_MUX_SANDBOX) += mdio_mux_sandbox.o obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o -obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o +obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth/ obj-$(CONFIG_MPC8XX_FEC) += mpc8xx_fec.o obj-$(CONFIG_MT7620_ETH) += mt7620-eth.o obj-$(CONFIG_MT7628_ETH) += mt7628-eth.o diff --git a/drivers/net/designware.c b/drivers/net/designware.c index 07b0f49ef58..0a1fff38727 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -741,6 +741,18 @@ int designware_eth_probe(struct udevice *dev) puts("Error enabling phy supply\n"); return ret; } +#if IS_ENABLED(CONFIG_ARCH_NPCM8XX) + int phy_uv; + + phy_uv = dev_read_u32_default(dev, "phy-supply-microvolt", 0); + if (phy_uv) { + ret = regulator_set_value(phy_supply, phy_uv); + if (ret) { + puts("Error setting phy voltage\n"); + return ret; + } + } +#endif } #endif @@ -784,6 +796,39 @@ int designware_eth_probe(struct udevice *dev) priv->bus = miiphy_get_dev_by_name(dev->name); priv->dev = dev; +#if IS_ENABLED(CONFIG_BITBANGMII) && IS_ENABLED(CONFIG_DM_GPIO) + if (dev_read_bool(dev, "snps,bitbang-mii")) { + int bus_idx; + + debug("\n%s: use bitbang mii..\n", dev->name); + ret = gpio_request_by_name(dev, "snps,mdc-gpio", 0, + &priv->mdc_gpio, GPIOD_IS_OUT + | GPIOD_IS_OUT_ACTIVE); + if (ret) { + debug("no mdc-gpio\n"); + return ret; + } + ret = gpio_request_by_name(dev, "snps,mdio-gpio", 0, + &priv->mdio_gpio, GPIOD_IS_OUT + | GPIOD_IS_OUT_ACTIVE); + if (ret) { + debug("no mdio-gpio\n"); + return ret; + } + priv->bb_delay = dev_read_u32_default(dev, "snps,bitbang-delay", 1); + + for (bus_idx = 0; bus_idx < bb_miiphy_buses_num; bus_idx++) { + if (!bb_miiphy_buses[bus_idx].priv) { + bb_miiphy_buses[bus_idx].priv = priv; + strlcpy(bb_miiphy_buses[bus_idx].name, priv->bus->name, + MDIO_NAME_LEN); + priv->bus->read = bb_miiphy_read; + priv->bus->write = bb_miiphy_write; + break; + } + } + } +#endif ret = dw_phy_init(priv, dev); debug("%s, ret=%d\n", __func__, ret); if (!ret) @@ -894,3 +939,83 @@ static struct pci_device_id supported[] = { }; U_BOOT_PCI_DEVICE(eth_designware, supported); + +#if IS_ENABLED(CONFIG_BITBANGMII) && IS_ENABLED(CONFIG_DM_GPIO) +static int dw_eth_bb_mdio_active(struct bb_miiphy_bus *bus) +{ + struct dw_eth_dev *priv = bus->priv; + struct gpio_desc *desc = &priv->mdio_gpio; + + desc->flags = 0; + dm_gpio_set_dir_flags(&priv->mdio_gpio, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + + return 0; +} + +static int dw_eth_bb_mdio_tristate(struct bb_miiphy_bus *bus) +{ + struct dw_eth_dev *priv = bus->priv; + struct gpio_desc *desc = &priv->mdio_gpio; + + desc->flags = 0; + dm_gpio_set_dir_flags(&priv->mdio_gpio, GPIOD_IS_IN); + + return 0; +} + +static int dw_eth_bb_set_mdio(struct bb_miiphy_bus *bus, int v) +{ + struct dw_eth_dev *priv = bus->priv; + + if (v) + dm_gpio_set_value(&priv->mdio_gpio, 1); + else + dm_gpio_set_value(&priv->mdio_gpio, 0); + + return 0; +} + +static int dw_eth_bb_get_mdio(struct bb_miiphy_bus *bus, int *v) +{ + struct dw_eth_dev *priv = bus->priv; + + *v = dm_gpio_get_value(&priv->mdio_gpio); + + return 0; +} + +static int dw_eth_bb_set_mdc(struct bb_miiphy_bus *bus, int v) +{ + struct dw_eth_dev *priv = bus->priv; + + if (v) + dm_gpio_set_value(&priv->mdc_gpio, 1); + else + dm_gpio_set_value(&priv->mdc_gpio, 0); + + return 0; +} + +static int dw_eth_bb_delay(struct bb_miiphy_bus *bus) +{ + struct dw_eth_dev *priv = bus->priv; + + udelay(priv->bb_delay); + return 0; +} + +struct bb_miiphy_bus bb_miiphy_buses[] = { + { + .name = BB_MII_DEVNAME, + .mdio_active = dw_eth_bb_mdio_active, + .mdio_tristate = dw_eth_bb_mdio_tristate, + .set_mdio = dw_eth_bb_set_mdio, + .get_mdio = dw_eth_bb_get_mdio, + .set_mdc = dw_eth_bb_set_mdc, + .delay = dw_eth_bb_delay, + .priv = NULL, + } +}; + +int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses); +#endif diff --git a/drivers/net/designware.h b/drivers/net/designware.h index e47101ccaf6..cccf9d54e02 100644 --- a/drivers/net/designware.h +++ b/drivers/net/designware.h @@ -229,7 +229,11 @@ struct dw_eth_dev { u32 max_speed; u32 tx_currdescnum; u32 rx_currdescnum; - +#if IS_ENABLED(CONFIG_BITBANGMII) && IS_ENABLED(CONFIG_DM_GPIO) + u32 bb_delay; + struct gpio_desc mdc_gpio; + struct gpio_desc mdio_gpio; +#endif struct eth_mac_regs *mac_regs_p; struct eth_dma_regs *dma_regs_p; #if CONFIG_IS_ENABLED(DM_GPIO) diff --git a/drivers/net/dwc_eth_xgmac.c b/drivers/net/dwc_eth_xgmac.c index d3e5f9255f5..cf8227b1b4d 100644 --- a/drivers/net/dwc_eth_xgmac.c +++ b/drivers/net/dwc_eth_xgmac.c @@ -152,7 +152,9 @@ static int xgmac_mdio_read(struct mii_dev *bus, int mdio_addr, int mdio_devad, ret = xgmac_mdio_wait_idle(xgmac); if (ret) { - pr_err("MDIO not idle at entry: %d\n", ret); + pr_err("%s MDIO not idle at entry: %d\n", + xgmac->dev->name, ret); + return ret; } @@ -172,7 +174,9 @@ static int xgmac_mdio_read(struct mii_dev *bus, int mdio_addr, int mdio_devad, ret = xgmac_mdio_wait_idle(xgmac); if (ret) { - pr_err("MDIO not idle at entry: %d\n", ret); + pr_err("%s MDIO not idle at entry: %d\n", + xgmac->dev->name, ret); + return ret; } @@ -181,7 +185,9 @@ static int xgmac_mdio_read(struct mii_dev *bus, int mdio_addr, int mdio_devad, ret = xgmac_mdio_wait_idle(xgmac); if (ret) { - pr_err("MDIO read didn't complete: %d\n", ret); + pr_err("%s MDIO read didn't complete: %d\n", + xgmac->dev->name, ret); + return ret; } @@ -206,7 +212,9 @@ static int xgmac_mdio_write(struct mii_dev *bus, int mdio_addr, int mdio_devad, ret = xgmac_mdio_wait_idle(xgmac); if (ret) { - pr_err("MDIO not idle at entry: %d\n", ret); + pr_err("%s MDIO not idle at entry: %d\n", + xgmac->dev->name, ret); + return ret; } @@ -229,7 +237,9 @@ static int xgmac_mdio_write(struct mii_dev *bus, int mdio_addr, int mdio_devad, ret = xgmac_mdio_wait_idle(xgmac); if (ret) { - pr_err("MDIO not idle at entry: %d\n", ret); + pr_err("%s MDIO not idle at entry: %d\n", + xgmac->dev->name, ret); + return ret; } @@ -238,7 +248,9 @@ static int xgmac_mdio_write(struct mii_dev *bus, int mdio_addr, int mdio_devad, ret = xgmac_mdio_wait_idle(xgmac); if (ret) { - pr_err("MDIO write didn't complete: %d\n", ret); + pr_err("%s MDIO write didn't complete: %d\n", + xgmac->dev->name, ret); + return ret; } @@ -323,7 +335,7 @@ static int xgmac_adjust_link(struct udevice *dev) else ret = xgmac_set_half_duplex(dev); if (ret < 0) { - pr_err("xgmac_set_*_duplex() failed: %d\n", ret); + pr_err("%s xgmac_set_*_duplex() failed: %d\n", dev->name, ret); return ret; } @@ -341,26 +353,28 @@ static int xgmac_adjust_link(struct udevice *dev) ret = xgmac_set_mii_speed_10(dev); break; default: - pr_err("invalid speed %d\n", xgmac->phy->speed); + pr_err("%s invalid speed %d\n", dev->name, xgmac->phy->speed); return -EINVAL; } if (ret < 0) { - pr_err("xgmac_set_*mii_speed*() failed: %d\n", ret); + pr_err("%s xgmac_set_*mii_speed*() failed: %d\n", dev->name, ret); return ret; } if (en_calibration) { ret = xgmac->config->ops->xgmac_calibrate_pads(dev); if (ret < 0) { - pr_err("xgmac_calibrate_pads() failed: %d\n", - ret); + pr_err("%s xgmac_calibrate_pads() failed: %d\n", + dev->name, ret); + return ret; } } else { ret = xgmac->config->ops->xgmac_disable_calibration(dev); if (ret < 0) { - pr_err("xgmac_disable_calibration() failed: %d\n", - ret); + pr_err("%s xgmac_disable_calibration() failed: %d\n", + dev->name, ret); + return ret; } } @@ -456,7 +470,7 @@ static int xgmac_start(struct udevice *dev) ret = xgmac->config->ops->xgmac_start_resets(dev); if (ret < 0) { - pr_err("xgmac_start_resets() failed: %d\n", ret); + pr_err("%s xgmac_start_resets() failed: %d\n", dev->name, ret); goto err; } @@ -466,13 +480,13 @@ static int xgmac_start(struct udevice *dev) XGMAC_DMA_MODE_SWR, false, xgmac->config->swr_wait, false); if (ret) { - pr_err("XGMAC_DMA_MODE_SWR stuck: %d\n", ret); + pr_err("%s XGMAC_DMA_MODE_SWR stuck: %d\n", dev->name, ret); goto err_stop_resets; } ret = xgmac->config->ops->xgmac_calibrate_pads(dev); if (ret < 0) { - pr_err("xgmac_calibrate_pads() failed: %d\n", ret); + pr_err("%s xgmac_calibrate_pads() failed: %d\n", dev->name, ret); goto err_stop_resets; } @@ -485,14 +499,16 @@ static int xgmac_start(struct udevice *dev) xgmac->phy = phy_connect(xgmac->mii, addr, dev, xgmac->config->interface(dev)); if (!xgmac->phy) { - pr_err("phy_connect() failed\n"); + pr_err("%s phy_connect() failed\n", dev->name); goto err_stop_resets; } if (xgmac->max_speed) { ret = phy_set_supported(xgmac->phy, xgmac->max_speed); if (ret) { - pr_err("phy_set_supported() failed: %d\n", ret); + pr_err("%s phy_set_supported() failed: %d\n", + dev->name, ret); + goto err_shutdown_phy; } } @@ -500,25 +516,25 @@ static int xgmac_start(struct udevice *dev) xgmac->phy->node = xgmac->phy_of_node; ret = phy_config(xgmac->phy); if (ret < 0) { - pr_err("phy_config() failed: %d\n", ret); + pr_err("%s phy_config() failed: %d\n", dev->name, ret); goto err_shutdown_phy; } } ret = phy_startup(xgmac->phy); if (ret < 0) { - pr_err("phy_startup() failed: %d\n", ret); + pr_err("%s phy_startup() failed: %d\n", dev->name, ret); goto err_shutdown_phy; } if (!xgmac->phy->link) { - pr_err("No link\n"); + pr_err("%s No link\n", dev->name); goto err_shutdown_phy; } ret = xgmac_adjust_link(dev); if (ret < 0) { - pr_err("xgmac_adjust_link() failed: %d\n", ret); + pr_err("%s xgmac_adjust_link() failed: %d\n", dev->name, ret); goto err_shutdown_phy; } @@ -611,7 +627,7 @@ static int xgmac_start(struct udevice *dev) ret = xgmac_write_hwaddr(dev); if (ret < 0) { - pr_err("xgmac_write_hwaddr() failed: %d\n", ret); + pr_err("%s xgmac_write_hwaddr() failed: %d\n", dev->name, ret); goto err; } @@ -738,7 +754,7 @@ err_shutdown_phy: err_stop_resets: xgmac->config->ops->xgmac_stop_resets(dev); err: - pr_err("FAILED: %d\n", ret); + pr_err("%s FAILED: %d\n", dev->name, ret); return ret; } @@ -1047,7 +1063,7 @@ static int xgmac_probe(struct udevice *dev) xgmac->regs = dev_read_addr(dev); if (xgmac->regs == FDT_ADDR_T_NONE) { - pr_err("dev_read_addr() failed\n"); + pr_err("%s dev_read_addr() failed\n", dev->name); return -ENODEV; } xgmac->mac_regs = (void *)(xgmac->regs + XGMAC_MAC_REGS_BASE); @@ -1058,19 +1074,23 @@ static int xgmac_probe(struct udevice *dev) ret = xgmac_probe_resources_core(dev); if (ret < 0) { - pr_err("xgmac_probe_resources_core() failed: %d\n", ret); + pr_err("%s xgmac_probe_resources_core() failed: %d\n", + dev->name, ret); + return ret; } ret = xgmac->config->ops->xgmac_probe_resources(dev); if (ret < 0) { - pr_err("xgmac_probe_resources() failed: %d\n", ret); + pr_err("%s xgmac_probe_resources() failed: %d\n", + dev->name, ret); + goto err_remove_resources_core; } ret = xgmac->config->ops->xgmac_start_clks(dev); if (ret < 0) { - pr_err("xgmac_start_clks() failed: %d\n", ret); + pr_err("%s xgmac_start_clks() failed: %d\n", dev->name, ret); return ret; } @@ -1080,7 +1100,7 @@ static int xgmac_probe(struct udevice *dev) if (!xgmac->mii) { xgmac->mii = mdio_alloc(); if (!xgmac->mii) { - pr_err("mdio_alloc() failed\n"); + pr_err("%s mdio_alloc() failed\n", dev->name); ret = -ENOMEM; goto err_stop_clks; } @@ -1091,7 +1111,9 @@ static int xgmac_probe(struct udevice *dev) ret = mdio_register(xgmac->mii); if (ret < 0) { - pr_err("mdio_register() failed: %d\n", ret); + pr_err("%s mdio_register() failed: %d\n", + dev->name, ret); + goto err_free_mdio; } } diff --git a/drivers/net/dwc_eth_xgmac_socfpga.c b/drivers/net/dwc_eth_xgmac_socfpga.c index 87fb7e887e7..c89c8a188b7 100644 --- a/drivers/net/dwc_eth_xgmac_socfpga.c +++ b/drivers/net/dwc_eth_xgmac_socfpga.c @@ -29,6 +29,25 @@ #define SOCFPGA_XGMAC_SYSCON_ARG_COUNT 2 +phy_interface_t dwxgmac_of_get_mac_mode(struct udevice *dev) +{ + const char *mac_mode; + int i; + + debug("%s(dev=%p):\n", __func__, dev); + mac_mode = dev_read_string(dev, "mac-mode"); + if (!mac_mode) + return PHY_INTERFACE_MODE_NA; + + if (mac_mode) { + for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) { + if (!strcmp(mac_mode, phy_interface_strings[i])) + return i; + } + } + return PHY_INTERFACE_MODE_NA; +} + static int dwxgmac_socfpga_do_setphy(struct udevice *dev, u32 modereg) { struct xgmac_priv *xgmac = dev_get_priv(dev); @@ -66,12 +85,17 @@ static int xgmac_probe_resources_socfpga(struct udevice *dev) struct ofnode_phandle_args args; void *range; phy_interface_t interface; + phy_interface_t mac_mode; int ret; u32 modereg; interface = xgmac->config->interface(dev); + mac_mode = dwxgmac_of_get_mac_mode(dev); + + if (mac_mode == PHY_INTERFACE_MODE_NA) + mac_mode = interface; - switch (interface) { + switch (mac_mode) { case PHY_INTERFACE_MODE_MII: case PHY_INTERFACE_MODE_GMII: modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII; @@ -80,6 +104,7 @@ static int xgmac_probe_resources_socfpga(struct udevice *dev) modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII; break; case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII; break; default: diff --git a/drivers/net/dwmac_socfpga.c b/drivers/net/dwmac_socfpga.c index a9e2d8c0972..a55f3e29f9f 100644 --- a/drivers/net/dwmac_socfpga.c +++ b/drivers/net/dwmac_socfpga.c @@ -106,6 +106,9 @@ static int dwmac_socfpga_probe(struct udevice *dev) modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII; break; case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII; break; default: diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index a6b0bafc8c6..52fa820f518 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -2,8 +2,10 @@ /* * ENETC ethernet controller driver * Copyright 2017-2021 NXP + * Copyright 2023-2025 NXP */ +#include <clk.h> #include <dm.h> #include <errno.h> #include <fdt_support.h> @@ -16,52 +18,267 @@ #include <miiphy.h> #include <linux/bug.h> #include <linux/delay.h> +#include <linux/build_bug.h> + +#ifdef CONFIG_ARCH_IMX9 +#include <asm/mach-imx/sys_proto.h> +#include <cpu_func.h> +#endif #include "fsl_enetc.h" #define ENETC_DRIVER_NAME "enetc_eth" +/* + * Calculate number of buffer descriptors per cacheline, and compile-time + * validate that: + * - the RX and TX descriptors are the same size + * - the descriptors fit exactly into cachelines without overlap + * - all descriptors fit exactly into cachelines + */ +#define ENETC_NUM_BD_IN_CL \ + ((ARCH_DMA_MINALIGN / sizeof(struct enetc_tx_bd)) + \ + BUILD_BUG_ON_ZERO(sizeof(struct enetc_tx_bd) != \ + sizeof(union enetc_rx_bd)) + \ + BUILD_BUG_ON_ZERO(ARCH_DMA_MINALIGN % sizeof(struct enetc_tx_bd)) + \ + BUILD_BUG_ON_ZERO(ARCH_DMA_MINALIGN % sizeof(union enetc_rx_bd)) + \ + BUILD_BUG_ON_ZERO(ENETC_BD_CNT % \ + (ARCH_DMA_MINALIGN / sizeof(struct enetc_tx_bd)))) + static int enetc_remove(struct udevice *dev); +static int enetc_is_imx95(struct udevice *dev) +{ + struct pci_child_plat *pplat = dev_get_parent_plat(dev); + + /* Test whether this is i.MX95 ENETCv4. This may be optimized out. */ + return IS_ENABLED(CONFIG_ARCH_IMX9) && + pplat->vendor == PCI_VENDOR_ID_PHILIPS; +} + +static int enetc_is_ls1028a(struct udevice *dev) +{ + struct pci_child_plat *pplat = dev_get_parent_plat(dev); + + /* Test whether this is LS1028A ENETC. This may be optimized out. */ + return IS_ENABLED(CONFIG_ARCH_LS1028A) && + pplat->vendor == PCI_VENDOR_ID_FREESCALE; +} + +static int enetc_dev_id(struct udevice *dev) +{ + if (enetc_is_imx95(dev)) + return PCI_DEV(pci_get_devfn(dev)) >> 3; + if (enetc_is_ls1028a(dev)) + return PCI_FUNC(pci_get_devfn(dev)); + + return 0; +} + +static void enetc_inval_rxbd(struct udevice *dev) +{ + struct enetc_priv *priv = dev_get_priv(dev); + union enetc_rx_bd *desc = &priv->enetc_rxbd[priv->rx_bdr.next_prod_idx]; + unsigned long start = rounddown((unsigned long)desc, ARCH_DMA_MINALIGN); + unsigned long end = roundup((unsigned long)desc + sizeof(*desc), + ARCH_DMA_MINALIGN); + + if (enetc_is_imx95(dev)) + invalidate_dcache_range(start, end); +} + +static void enetc_flush_bd(struct udevice *dev, int pi, bool tx) +{ + struct enetc_priv *priv = dev_get_priv(dev); + union enetc_rx_bd *rxdesc = &priv->enetc_rxbd[pi]; + struct enetc_tx_bd *txdesc = &priv->enetc_txbd[pi]; + unsigned long desc = tx ? (unsigned long)txdesc : (unsigned long)rxdesc; + unsigned long size = tx ? sizeof(*txdesc) : sizeof(*rxdesc); + unsigned long start = rounddown(desc, ARCH_DMA_MINALIGN); + unsigned long end = roundup(desc + size, ARCH_DMA_MINALIGN); + + if (enetc_is_imx95(dev)) + flush_dcache_range(start, end); +} + +static void enetc_inval_buffer(struct udevice *dev, void *buf, size_t size) +{ + unsigned long start = rounddown((unsigned long)buf, ARCH_DMA_MINALIGN); + unsigned long end = roundup((unsigned long)buf + size, + ARCH_DMA_MINALIGN); + + if (enetc_is_imx95(dev)) + invalidate_dcache_range(start, end); +} + +static void enetc_flush_buffer(struct udevice *dev, void *buf, size_t size) +{ + unsigned long start = rounddown((unsigned long)buf, ARCH_DMA_MINALIGN); + unsigned long end = roundup((unsigned long)buf + size, + ARCH_DMA_MINALIGN); + + if (enetc_is_imx95(dev)) + flush_dcache_range(start, end); +} + +/* register accessors */ +static u32 enetc_read_reg(void __iomem *addr) +{ + return readl(addr); +} + +static void enetc_write_reg(void __iomem *addr, u32 val) +{ + writel(val, addr); +} + +static void enetc_write(struct enetc_priv *priv, u32 off, u32 val) +{ + enetc_write_reg(priv->regs_base + off, val); +} + +/* base port register accessors */ +static void enetc_write_pmr(struct udevice *dev, u32 val) +{ + struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev); + struct enetc_priv *priv = dev_get_priv(dev); + const u32 off = ENETC_PMR + data->reg_offset_pmr; + + enetc_write_reg(priv->port_regs + off, val); +} + +static void enetc_write_psipmar(struct udevice *dev, int n, u32 val) +{ + struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev); + struct enetc_priv *priv = dev_get_priv(dev); + const u32 off = (n ? ENETC_PSIPMAR1 : ENETC_PSIPMAR0) + + data->reg_offset_psipmar; + + enetc_write_reg(priv->port_regs + off, val); +} + +/* port station register accessors */ +static void enetc_write_psicfgr(struct udevice *dev, int port, u32 val) +{ + struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev); + struct enetc_priv *priv = dev_get_priv(dev); + const u32 off = ENETC_PSICFGR(port, ENETC_PSICFGR_SHIFT_LS) + + data->reg_offset_psicfgr; + + enetc_write_reg(priv->port_regs + off, val); +} + +/* port register accessors */ +static u32 enetc_read_pcapr_mdio(struct udevice *dev) +{ + struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev); + struct enetc_priv *priv = dev_get_priv(dev); + const u32 off = ENETC_PCAPR0 + data->reg_offset_pcapr; + const u32 reg = enetc_read_reg(priv->port_regs + off); + + if (enetc_is_imx95(dev)) + return reg & ENETC_PCS_PROT; + else if (enetc_is_ls1028a(dev)) + return reg & ENETC_PCAPRO_MDIO; + + return 0; +} + +static void enetc_write_port(struct enetc_priv *priv, u32 off, u32 val) +{ + enetc_write_reg(priv->port_regs + off, val); +} + +/* MAC port register accessors */ +static u32 enetc_read_mac_port(struct udevice *dev, u32 off) +{ + struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev); + struct enetc_priv *priv = dev_get_priv(dev); + + return enetc_read_reg(priv->port_regs + data->reg_offset_mac + off); +} + +static void enetc_write_mac_port(struct udevice *dev, u32 off, u32 val) +{ + struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev); + struct enetc_priv *priv = dev_get_priv(dev); + + enetc_write_reg(priv->port_regs + data->reg_offset_mac + off, val); +} + +/* BDR register accessor, see also ENETC_BDR() */ +static void enetc_bdr_write(struct enetc_priv *priv, int type, int n, + u32 off, u32 val) +{ + enetc_write(priv, ENETC_BDR(type, n, off), val); +} + /* * sets the MAC address in IERB registers, this setting is persistent and * carried over to Linux. */ -static void enetc_set_ierb_primary_mac(struct udevice *dev, int devfn, - const u8 *enetaddr) -{ -#ifdef CONFIG_ARCH_LS1028A -/* - * LS1028A is the only part with IERB at this time and there are plans to change - * its structure, keep this LS1028A specific for now - */ #define IERB_BASE 0x1f0800000ULL #define IERB_PFMAC(pf, vf, n) (IERB_BASE + 0x8000 + (pf) * 0x100 + (vf) * 8 \ + (n) * 4) -static int ierb_fn_to_pf[] = {0, 1, 2, -1, -1, -1, 3}; - +static void enetc_set_ierb_primary_mac(struct udevice *dev, void *blob) +{ + static int ierb_fn_to_pf[] = { 0, 1, 2, -1, -1, -1, 3 }; + struct pci_child_plat *ppdata = dev_get_parent_plat(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + struct enetc_priv *priv = dev_get_priv(dev); + const u8 *enetaddr = pdata->enetaddr; u16 lower = *(const u16 *)(enetaddr + 4); u32 upper = *(const u32 *)enetaddr; + int devfn, offset; + char path[256]; - if (ierb_fn_to_pf[devfn] < 0) + if (enetc_is_imx95(dev)) { + /* + * Configure the ENETC primary MAC addresses - Set register + * PMAR0/1 for SI 0 and PSIaPMAR0/1 for SI 1, 2 .. a + * (optionally pre-configured in IERB). + */ + devfn = enetc_dev_id(dev); + if (devfn > 2) + return; + + enetc_write(priv, IMX95_ENETC_SIPMAR0, upper); + enetc_write(priv, IMX95_ENETC_SIPMAR1, lower); + + snprintf(path, 256, "/soc/pcie@%x/ethernet@%x,%x", + PCI_BUS(dm_pci_get_bdf(dev)), PCI_DEV(ppdata->devfn), + PCI_FUNC(ppdata->devfn)); + } else if (enetc_is_ls1028a(dev)) { + /* + * LS1028A is the only part with IERB at this time and + * there are plans to change its structure, keep this + * LS1028A specific for now. + */ + devfn = PCI_FUNC(ppdata->devfn); + + if (ierb_fn_to_pf[devfn] < 0) + return; + + out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 0), upper); + out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 1), (u32)lower); + + snprintf(path, 256, "/soc/pcie@1f0000000/ethernet@%x,%x", + PCI_DEV(ppdata->devfn), PCI_FUNC(ppdata->devfn)); + } else { return; + } - out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 0), upper); - out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 1), (u32)lower); -#endif + offset = fdt_path_offset(blob, path); + if (offset >= 0) + fdt_setprop(blob, offset, "mac-address", pdata->enetaddr, 6); } /* sets up primary MAC addresses in DT/IERB */ void fdt_fixup_enetc_mac(void *blob) { - struct pci_child_plat *ppdata; - struct eth_pdata *pdata; struct udevice *dev; struct uclass *uc; - char path[256]; - int offset; - int devfn; uclass_get(UCLASS_ETH, &uc); uclass_foreach_dev(dev, uc) { @@ -69,18 +286,7 @@ void fdt_fixup_enetc_mac(void *blob) strcmp(dev->driver->name, ENETC_DRIVER_NAME)) continue; - pdata = dev_get_plat(dev); - ppdata = dev_get_parent_plat(dev); - devfn = PCI_FUNC(ppdata->devfn); - - enetc_set_ierb_primary_mac(dev, devfn, pdata->enetaddr); - - snprintf(path, 256, "/soc/pcie@1f0000000/ethernet@%x,%x", - PCI_DEV(ppdata->devfn), PCI_FUNC(ppdata->devfn)); - offset = fdt_path_offset(blob, path); - if (offset < 0) - continue; - fdt_setprop(blob, offset, "mac-address", pdata->enetaddr, 6); + enetc_set_ierb_primary_mac(dev, blob); } } @@ -101,7 +307,7 @@ static int enetc_bind(struct udevice *dev) * PCI function # and enetc#N based on interface count */ if (ofnode_valid(dev_ofnode(dev))) - sprintf(name, "enetc-%u", PCI_FUNC(pci_get_devfn(dev))); + sprintf(name, "enetc-%u", enetc_dev_id(dev)); else sprintf(name, "enetc#%u", eth_num_devices++); device_set_name(dev, name); @@ -181,10 +387,9 @@ static int enetc_init_sgmii(struct udevice *dev) /* set up MAC for RGMII */ static void enetc_init_rgmii(struct udevice *dev, struct phy_device *phydev) { - struct enetc_priv *priv = dev_get_priv(dev); - u32 old_val, val; + u32 old_val, val, dpx = 0; - old_val = val = enetc_read_port(priv, ENETC_PM_IF_MODE); + old_val = val = enetc_read_mac_port(dev, ENETC_PM_IF_MODE); /* disable unreliable RGMII in-band signaling and force the MAC into * the speed negotiated by the PHY. @@ -202,15 +407,20 @@ static void enetc_init_rgmii(struct udevice *dev, struct phy_device *phydev) val |= ENETC_PM_IFM_SSP_10; } + if (enetc_is_imx95(dev)) + dpx = ENETC_PM_IFM_FULL_DPX_IMX; + else if (enetc_is_ls1028a(dev)) + dpx = ENETC_PM_IFM_FULL_DPX_LS; + if (phydev->duplex == DUPLEX_FULL) - val |= ENETC_PM_IFM_FULL_DPX; + val |= dpx; else - val &= ~ENETC_PM_IFM_FULL_DPX; + val &= ~dpx; if (val == old_val) return; - enetc_write_port(priv, ENETC_PM_IF_MODE, val); + enetc_write_mac_port(dev, ENETC_PM_IF_MODE, val); } /* set up MAC configuration for the given interface type */ @@ -230,9 +440,12 @@ static void enetc_setup_mac_iface(struct udevice *dev, case PHY_INTERFACE_MODE_USXGMII: case PHY_INTERFACE_MODE_10GBASER: /* set ifmode to (US)XGMII */ - if_mode = enetc_read_port(priv, ENETC_PM_IF_MODE); - if_mode &= ~ENETC_PM_IF_IFMODE_MASK; - enetc_write_port(priv, ENETC_PM_IF_MODE, if_mode); + if_mode = enetc_read_mac_port(dev, ENETC_PM_IF_MODE); + if (enetc_is_imx95(dev)) + if_mode &= ~ENETC_PM_IF_IFMODE_MASK_IMX; + else if (enetc_is_ls1028a(dev)) + if_mode &= ~ENETC_PM_IF_IFMODE_MASK_LS; + enetc_write_mac_port(dev, ENETC_PM_IF_MODE, if_mode); break; }; } @@ -263,7 +476,7 @@ static void enetc_start_pcs(struct udevice *dev) struct enetc_priv *priv = dev_get_priv(dev); /* register internal MDIO for debug purposes */ - if (enetc_read_port(priv, ENETC_PCAPR0) & ENETC_PCAPRO_MDIO) { + if (enetc_read_pcapr_mdio(dev)) { priv->imdio.read = enetc_mdio_read; priv->imdio.write = enetc_mdio_write; priv->imdio.priv = priv->port_regs + ENETC_PM_IMDIO_BASE; @@ -375,6 +588,21 @@ static int enetc_remove(struct udevice *dev) return 0; } +static int enetc_imx95_write_hwaddr(struct udevice *dev) +{ + struct eth_pdata *plat = dev_get_plat(dev); + struct enetc_priv *priv = dev_get_priv(dev); + u8 *addr = plat->enetaddr; + + u16 lower = *(const u16 *)(addr + 4); + u32 upper = *(const u32 *)addr; + + enetc_write_port(priv, IMX95_ENETC_PMAR0, upper); + enetc_write_port(priv, IMX95_ENETC_PMAR1, lower); + + return 0; +} + /* * LS1028A is the only part with IERB at this time and there are plans to * change its structure, keep this LS1028A specific for now. @@ -413,39 +641,46 @@ static int enetc_ls1028a_write_hwaddr(struct udevice *dev) static int enetc_write_hwaddr(struct udevice *dev) { struct eth_pdata *plat = dev_get_plat(dev); - struct enetc_priv *priv = dev_get_priv(dev); u8 *addr = plat->enetaddr; - if (IS_ENABLED(CONFIG_ARCH_LS1028A)) + if (enetc_is_imx95(dev)) + return enetc_imx95_write_hwaddr(dev); + if (enetc_is_ls1028a(dev)) return enetc_ls1028a_write_hwaddr(dev); u16 lower = *(const u16 *)(addr + 4); u32 upper = *(const u32 *)addr; - enetc_write_port(priv, ENETC_PSIPMAR0, upper); - enetc_write_port(priv, ENETC_PSIPMAR1, lower); + enetc_write_psipmar(dev, 0, upper); + enetc_write_psipmar(dev, 1, lower); return 0; } /* Configure port parameters (# of rings, frame size, enable port) */ -static void enetc_enable_si_port(struct enetc_priv *priv) +static void enetc_enable_si_port(struct udevice *dev) { - u32 val; + struct enetc_priv *priv = dev_get_priv(dev); + u32 val = ENETC_PM_CC_TXP_IMX | ENETC_PM_CC_TX | ENETC_PM_CC_RX; /* set Rx/Tx BDR count */ - val = ENETC_PSICFGR_SET_TXBDR(ENETC_TX_BDR_CNT); - val |= ENETC_PSICFGR_SET_RXBDR(ENETC_RX_BDR_CNT); - enetc_write_port(priv, ENETC_PSICFGR(0), val); + enetc_write_psicfgr(dev, 0, ENETC_PSICFGR_SET_BDR(ENETC_RX_BDR_CNT, + ENETC_TX_BDR_CNT)); /* set Rx max frame size */ - enetc_write_port(priv, ENETC_PM_MAXFRM, ENETC_RX_MAXFRM_SIZE); + enetc_write_mac_port(dev, ENETC_PM_MAXFRM, ENETC_RX_MAXFRM_SIZE); /* enable MAC port */ - enetc_write_port(priv, ENETC_PM_CC, ENETC_PM_CC_RX_TX_EN); + if (enetc_is_ls1028a(dev)) + val |= ENETC_PM_CC_TXP_LS | ENETC_PM_CC_PROMIS; + enetc_write_mac_port(dev, ENETC_PM_CC, val); /* enable port */ - enetc_write_port(priv, ENETC_PMR, ENETC_PMR_SI0_EN); + if (enetc_is_imx95(dev)) + enetc_write_port(priv, ENETC_POR, 0x0); + enetc_write_pmr(dev, ENETC_PMR_SI0_EN); /* set SI cache policy */ - enetc_write(priv, ENETC_SICAR0, - ENETC_SICAR_RD_CFG | ENETC_SICAR_WR_CFG); + enetc_write(priv, ENETC_SICAR0, ENETC_SICAR_WR_CFG | + (enetc_is_imx95(dev) ? + ENETC_SICAR_RD_CFG_IMX : + ENETC_SICAR_RD_CFG_LS)); /* enable SI */ enetc_write(priv, ENETC_SIMR, ENETC_SIMR_EN); } @@ -536,6 +771,8 @@ static void enetc_setup_rx_bdr(struct udevice *dev) priv->enetc_rxbd[i].w.addr = enetc_rxb_address(dev, i); /* each RX buffer must be aligned to 64B */ WARN_ON(priv->enetc_rxbd[i].w.addr & (ARCH_DMA_MINALIGN - 1)); + + enetc_flush_bd(dev, i, false); } /* reset producer (ENETC owned) and consumer (SW owned) index */ @@ -556,6 +793,7 @@ static void enetc_setup_rx_bdr(struct udevice *dev) */ static int enetc_start(struct udevice *dev) { + int ret; struct enetc_priv *priv = dev_get_priv(dev); /* reset and enable the PCI device */ @@ -563,15 +801,19 @@ static int enetc_start(struct udevice *dev) dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - enetc_enable_si_port(priv); + enetc_enable_si_port(dev); /* setup Tx/Rx buffer descriptors */ enetc_setup_tx_bdr(dev); enetc_setup_rx_bdr(dev); + ret = phy_startup(priv->phy); + if (ret) + return ret; + enetc_setup_mac_iface(dev, priv->phy); - return phy_startup(priv->phy); + return 0; } /* @@ -614,6 +856,8 @@ static int enetc_send(struct udevice *dev, void *packet, int length) enetc_dbg(dev, "TxBD[%d]send: pkt_len=%d, buff @0x%x%08x\n", pi, length, upper_32_bits((u64)nv_packet), lower_32_bits((u64)nv_packet)); + enetc_flush_buffer(dev, packet, length); + /* prepare Tx BD */ memset(&priv->enetc_txbd[pi], 0x0, sizeof(struct enetc_tx_bd)); priv->enetc_txbd[pi].addr = @@ -621,7 +865,10 @@ static int enetc_send(struct udevice *dev, void *packet, int length) priv->enetc_txbd[pi].buf_len = cpu_to_le16(length); priv->enetc_txbd[pi].frm_len = cpu_to_le16(length); priv->enetc_txbd[pi].flags = cpu_to_le16(ENETC_TXBD_FLAGS_F); + dmb(); + enetc_flush_bd(dev, pi, true); + /* send frame: increment producer index */ pi = (pi + 1) % txr->bd_count; txr->next_prod_idx = pi; @@ -643,15 +890,15 @@ static int enetc_recv(struct udevice *dev, int flags, uchar **packetp) { struct enetc_priv *priv = dev_get_priv(dev); struct bd_ring *rxr = &priv->rx_bdr; - int tries = ENETC_POLL_TRIES; int pi = rxr->next_prod_idx; - int ci = rxr->next_cons_idx; + int tries = ENETC_POLL_TRIES; u32 status; int len; u8 rdy; do { dmb(); + enetc_inval_rxbd(dev); status = le32_to_cpu(priv->enetc_rxbd[pi].r.lstatus); /* check if current BD is ready to be consumed */ rdy = ENETC_RXBD_STATUS_R(status); @@ -663,45 +910,162 @@ static int enetc_recv(struct udevice *dev, int flags, uchar **packetp) dmb(); len = le16_to_cpu(priv->enetc_rxbd[pi].r.buf_len); *packetp = (uchar *)enetc_rxb_address(dev, pi); + enetc_inval_buffer(dev, *packetp, len); enetc_dbg(dev, "RxBD[%d]: len=%d err=%d pkt=0x%x%08x\n", pi, len, ENETC_RXBD_STATUS_ERRORS(status), upper_32_bits((u64)*packetp), lower_32_bits((u64)*packetp)); - /* BD clean up and advance to next in ring */ - memset(&priv->enetc_rxbd[pi], 0, sizeof(union enetc_rx_bd)); - priv->enetc_rxbd[pi].w.addr = enetc_rxb_address(dev, pi); + return len; +} + +static int enetc_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + const int bd_num_in_cl = enetc_is_imx95(dev) ? ENETC_NUM_BD_IN_CL : 1; + struct enetc_priv *priv = dev_get_priv(dev); + struct bd_ring *rxr = &priv->rx_bdr; + int pi = rxr->next_prod_idx; + int ci = rxr->next_cons_idx; + uchar *packet_expected; + int i; + + packet_expected = (uchar *)enetc_rxb_address(dev, pi); + if (packet != packet_expected) { + printf("%s: Unexpected packet (expected %p)\n", __func__, + packet_expected); + return -EINVAL; + } + rxr->next_prod_idx = (pi + 1) % rxr->bd_count; ci = (ci + 1) % rxr->bd_count; rxr->next_cons_idx = ci; dmb(); - /* free up the slot in the ring for HW */ - enetc_write_reg(rxr->cons_idx, ci); - return len; + if ((pi + 1) % bd_num_in_cl == 0) { + /* BD clean up and advance to next in ring */ + for (i = 0; i < bd_num_in_cl; i++) { + memset(&priv->enetc_rxbd[pi - i], 0, sizeof(union enetc_rx_bd)); + priv->enetc_rxbd[pi - i].w.addr = enetc_rxb_address(dev, pi - i); + } + + /* Will flush all bds in one cacheline */ + enetc_flush_bd(dev, pi - bd_num_in_cl + 1, false); + + /* free up the slot in the ring for HW */ + enetc_write_reg(rxr->cons_idx, ci); + } + + return 0; +} + +#if IS_ENABLED(CONFIG_ARCH_IMX9) +static int enetc_read_rom_hwaddr(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + unsigned int dev_id = enetc_dev_id(dev); + unsigned char *mac = pdata->enetaddr; + + if (dev_id > 2) + return -EINVAL; + + imx_get_mac_from_fuse(dev_id, mac); + + return !is_valid_ethaddr(mac); } -static const struct eth_ops enetc_ops = { +static const struct eth_ops enetc_ops_imx = { + .start = enetc_start, + .send = enetc_send, + .recv = enetc_recv, + .stop = enetc_stop, + .free_pkt = enetc_free_pkt, + .write_hwaddr = enetc_write_hwaddr, + .read_rom_hwaddr = enetc_read_rom_hwaddr, +}; + +static int enetc_probe_imx(struct udevice *dev) +{ + struct clk *clk; + int ret; + + clk = devm_clk_get_optional(dev, "ref"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ret = clk_enable(clk); + if (ret) + return ret; + + ret = enetc_probe(dev); + if (ret) + clk_disable(clk); + + return ret; +} + +U_BOOT_DRIVER(eth_enetc_imx) = { + .name = ENETC_DRIVER_NAME, + .id = UCLASS_ETH, + .bind = enetc_bind, + .probe = enetc_probe_imx, + .remove = enetc_remove, + .ops = &enetc_ops_imx, + .priv_auto = sizeof(struct enetc_priv), + .plat_auto = sizeof(struct eth_pdata), +}; + +static const struct enetc_data enetc_data_imx = { + .reg_offset_pmr = ENETC_PMR_OFFSET_IMX, + .reg_offset_psipmar = ENETC_PSIPMARn_OFFSET_IMX, + .reg_offset_pcapr = ENETC_PCAPR_OFFSET_IMX, + .reg_offset_psicfgr = ENETC_PSICFGR_OFFSET_IMX, + .reg_offset_mac = ENETC_PM_OFFSET_IMX, +}; + +static struct pci_device_id enetc_ids_imx[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_ENETC4_ETH), + .driver_data = (ulong)&enetc_data_imx, + }, + {} +}; + +U_BOOT_PCI_DEVICE(eth_enetc_imx, enetc_ids_imx); +#endif + +static const struct eth_ops enetc_ops_ls = { .start = enetc_start, .send = enetc_send, .recv = enetc_recv, .stop = enetc_stop, + .free_pkt = enetc_free_pkt, .write_hwaddr = enetc_write_hwaddr, }; -U_BOOT_DRIVER(eth_enetc) = { +U_BOOT_DRIVER(eth_enetc_ls) = { .name = ENETC_DRIVER_NAME, .id = UCLASS_ETH, .bind = enetc_bind, .probe = enetc_probe, .remove = enetc_remove, - .ops = &enetc_ops, + .ops = &enetc_ops_ls, .priv_auto = sizeof(struct enetc_priv), .plat_auto = sizeof(struct eth_pdata), }; -static struct pci_device_id enetc_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_ETH) }, +static const struct enetc_data enetc_data_ls = { + .reg_offset_pmr = ENETC_PMR_OFFSET_LS, + .reg_offset_psipmar = ENETC_PSIPMARn_OFFSET_LS, + .reg_offset_pcapr = ENETC_PCAPR_OFFSET_LS, + .reg_offset_psicfgr = ENETC_PSICFGR_OFFSET_LS, + .reg_offset_mac = ENETC_PM_OFFSET_LS, +}; + +static struct pci_device_id enetc_ids_ls[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_ETH), + .driver_data = (ulong)&enetc_data_ls, + }, {} }; -U_BOOT_PCI_DEVICE(eth_enetc, enetc_ids); +U_BOOT_PCI_DEVICE(eth_enetc_ls, enetc_ids_ls); diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h index f2acf367aa3..804df853bf5 100644 --- a/drivers/net/fsl_enetc.h +++ b/drivers/net/fsl_enetc.h @@ -12,7 +12,9 @@ /* PCI function IDs */ #define PCI_DEVICE_ID_ENETC_ETH 0xE100 +#define PCI_DEVICE_ID_ENETC4_ETH 0xE101 #define PCI_DEVICE_ID_ENETC_MDIO 0xEE01 +#define PCI_DEVICE_ID_ENETC4_EMDIO 0xEE00 /* ENETC Ethernet controller registers */ /* Station interface register offsets */ @@ -22,7 +24,8 @@ /* write cache cfg: snoop, no allocate, data & BD coherent */ #define ENETC_SICAR_WR_CFG 0x6767 /* read cache cfg: coherent copy, look up, don't alloc in cache */ -#define ENETC_SICAR_RD_CFG 0x27270000 +#define ENETC_SICAR_RD_CFG_LS 0x27270000 +#define ENETC_SICAR_RD_CFG_IMX 0x2b2b0000 #define ENETC_SIROCT 0x300 #define ENETC_SIRFRM 0x308 #define ENETC_SITOCT 0x320 @@ -57,32 +60,60 @@ enum enetc_bdr_type {TX, RX}; #define ENETC_PORT_REGS_OFF 0x10000 /* Port registers */ +#define ENETC_PMR_OFFSET_IMX 0x0010 +#define ENETC_PMR_OFFSET_LS 0x0000 #define ENETC_PMR 0x0000 #define ENETC_PMR_SI0_EN BIT(16) #define ENETC_PSIPMMR 0x0018 -#define ENETC_PSIPMAR0 0x0100 -#define ENETC_PSIPMAR1 0x0104 -#define ENETC_PCAPR0 0x0900 -#define ENETC_PCAPRO_MDIO BIT(11) -#define ENETC_PSICFGR(n) (0x0940 + (n) * 0x10) -#define ENETC_PSICFGR_SET_TXBDR(val) ((val) & 0xff) -#define ENETC_PSICFGR_SET_RXBDR(val) (((val) & 0xff) << 16) +#define ENETC_PSIPMARn_OFFSET_IMX 0x0000 +#define ENETC_PSIPMARn_OFFSET_LS 0x0080 +#define ENETC_PSIPMAR0 0x0080 +#define ENETC_PSIPMAR1 0x0084 +#define ENETC_PCAPR_OFFSET_IMX 0x4008 +#define ENETC_PCAPR_OFFSET_LS 0x0900 +#define ENETC_PCAPR0 0x0000 +#define ENETC_PCAPRO_MDIO BIT(11) /* LS only */ +#define ENETC_PCS_PROT GENMASK(15, 0) /* IMX only */ +/* ENETC base registers */ +#define ENETC_PSICFGR_OFFSET_LS 0x0940 +#define ENETC_PSICFGR_SHIFT_LS 0x10 +#define ENETC_PSICFGR_OFFSET_IMX 0x2010 +#define ENETC_PSICFGR_SHIFT_IMX 0x80 +#define ENETC_PSICFGR(n, s) ((n) * (s)) +#define ENETC_PSICFGR_SET_BDR(rx, tx) (((rx) << 16) | (tx)) /* MAC configuration */ -#define ENETC_PM_CC 0x8008 +#define ENETC_PM_OFFSET_IMX 0x5000 +#define ENETC_PM_OFFSET_LS 0x8000 +#define ENETC_PM_CC 0x0008 #define ENETC_PM_CC_DEFAULT 0x0810 -#define ENETC_PM_CC_RX_TX_EN 0x8813 -#define ENETC_PM_MAXFRM 0x8014 +#define ENETC_PM_CC_TXP_IMX BIT(15) +#define ENETC_PM_CC_TXP_LS BIT(11) +#define ENETC_PM_CC_PROMIS BIT(4) +#define ENETC_PM_CC_TX BIT(1) +#define ENETC_PM_CC_RX BIT(0) +#define ENETC_PM_MAXFRM 0x0014 #define ENETC_RX_MAXFRM_SIZE PKTSIZE_ALIGN -#define ENETC_PM_IMDIO_BASE 0x8030 -#define ENETC_PM_IF_MODE 0x8300 +#define ENETC_PM_IMDIO_BASE 0x0030 +#define ENETC_PM_IF_MODE 0x0300 #define ENETC_PM_IF_MODE_RG BIT(2) #define ENETC_PM_IF_MODE_AN_ENA BIT(15) #define ENETC_PM_IFM_SSP_MASK GENMASK(14, 13) #define ENETC_PM_IFM_SSP_1000 (2 << 13) #define ENETC_PM_IFM_SSP_100 (0 << 13) #define ENETC_PM_IFM_SSP_10 (1 << 13) -#define ENETC_PM_IFM_FULL_DPX BIT(12) -#define ENETC_PM_IF_IFMODE_MASK GENMASK(1, 0) +#define ENETC_PM_IFM_FULL_DPX_IMX BIT(6) +#define ENETC_PM_IFM_FULL_DPX_LS BIT(12) +#define ENETC_PM_IF_IFMODE_MASK_IMX GENMASK(2, 0) +#define ENETC_PM_IF_IFMODE_MASK_LS GENMASK(1, 0) + +/* i.MX95 specific registers */ +#define IMX95_ENETC_SIPMAR0 0x80 +#define IMX95_ENETC_SIPMAR1 0x84 + +/* Port registers */ +#define IMX95_ENETC_PMAR0 0x4020 +#define IMX95_ENETC_PMAR1 0x4024 +#define ENETC_POR 0x4100 /* buffer descriptors count must be multiple of 8 and aligned to 128 bytes */ #define ENETC_BD_CNT CONFIG_SYS_RX_ETH_BUFFER @@ -163,25 +194,14 @@ struct enetc_priv { struct phy_device *phy; }; -/* register accessors */ -#define enetc_read_reg(x) readl((x)) -#define enetc_write_reg(x, val) writel((val), (x)) -#define enetc_read(priv, off) enetc_read_reg((priv)->regs_base + (off)) -#define enetc_write(priv, off, v) \ - enetc_write_reg((priv)->regs_base + (off), v) - -/* port register accessors */ -#define enetc_port_regs(priv, off) ((priv)->port_regs + (off)) -#define enetc_read_port(priv, off) \ - enetc_read_reg(enetc_port_regs((priv), (off))) -#define enetc_write_port(priv, off, v) \ - enetc_write_reg(enetc_port_regs((priv), (off)), v) - -/* BDR register accessors, see ENETC_BDR() */ -#define enetc_bdr_read(priv, t, n, off) \ - enetc_read(priv, ENETC_BDR(t, n, off)) -#define enetc_bdr_write(priv, t, n, off, val) \ - enetc_write(priv, ENETC_BDR(t, n, off), val) +struct enetc_data { + /* Register layout offsets */ + u16 reg_offset_pmr; + u16 reg_offset_psipmar; + u16 reg_offset_pcapr; + u16 reg_offset_psicfgr; + u16 reg_offset_mac; +}; /* PCS / internal SoC PHY ID, it defaults to 0 on all interfaces */ #define ENETC_PCS_PHY_ADDR 0 diff --git a/drivers/net/fsl_enetc_mdio.c b/drivers/net/fsl_enetc_mdio.c index 2d5fcbb6dbd..c1d491f2c5a 100644 --- a/drivers/net/fsl_enetc_mdio.c +++ b/drivers/net/fsl_enetc_mdio.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * ENETC ethernet controller driver - * Copyright 2019 NXP + * Copyright 2019-2025 NXP */ #include <dm.h> @@ -14,6 +14,16 @@ #include "fsl_enetc.h" +static u32 enetc_read(struct enetc_mdio_priv *priv, u32 off) +{ + return readl(priv->regs_base + off); +} + +static void enetc_write(struct enetc_mdio_priv *priv, u32 off, u32 val) +{ + writel(val, priv->regs_base + off); +} + static void enetc_mdio_wait_bsy(struct enetc_mdio_priv *priv) { int to = 10000; @@ -122,7 +132,9 @@ static int enetc_mdio_bind(struct udevice *dev) static int enetc_mdio_probe(struct udevice *dev) { + struct pci_child_plat *pplat = dev_get_parent_plat(dev); struct enetc_mdio_priv *priv = dev_get_priv(dev); + u16 cmd = PCI_COMMAND_MEMORY; priv->regs_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0, 0, PCI_REGION_TYPE, 0); if (!priv->regs_base) { @@ -132,7 +144,10 @@ static int enetc_mdio_probe(struct udevice *dev) priv->regs_base += ENETC_MDIO_BASE; - dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY); + if (pplat->vendor == PCI_VENDOR_ID_PHILIPS) /* i.MX95 */ + cmd |= PCI_COMMAND_MASTER; + + dm_pci_clrset_config16(dev, PCI_COMMAND, 0, cmd); return 0; } @@ -148,6 +163,7 @@ U_BOOT_DRIVER(enetc_mdio) = { static struct pci_device_id enetc_mdio_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_MDIO) }, + { PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_ENETC4_EMDIO) }, { } }; diff --git a/drivers/net/fsl_enetc_netc_blk_ctrl.c b/drivers/net/fsl_enetc_netc_blk_ctrl.c new file mode 100644 index 00000000000..46b68d3d8a4 --- /dev/null +++ b/drivers/net/fsl_enetc_netc_blk_ctrl.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * NXP NETC Blocks Control Driver + * + * Copyright 2024 NXP + * + * This driver is used for pre-initialization of NETC, such as PCS and MII + * protocols, LDID, warm reset, etc. Therefore, all NETC device drivers can + * only be probed after the netc-blk-crtl driver has completed initialization. + * In addition, when the system enters suspend mode, IERB, PRB, and NETCMIX + * will be powered off, except for WOL. Therefore, when the system resumes, + * these blocks need to be reinitialized. + */ + +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/iopoll.h> +#include <phy_interface.h> + +/* NETCMIX registers */ +#define IMX95_CFG_LINK_IO_VAR 0x0 +#define IO_VAR_16FF_16G_SERDES 0x1 +#define IO_VAR(port, var) (((var) & 0xf) << ((port) << 2)) + +#define IMX95_CFG_LINK_MII_PROT 0x4 +#define CFG_LINK_MII_PORT_0 GENMASK(3, 0) +#define CFG_LINK_MII_PORT_1 GENMASK(7, 4) +#define MII_PROT_MII 0x0 +#define MII_PROT_RMII 0x1 +#define MII_PROT_RGMII 0x2 +#define MII_PROT_SERIAL 0x3 +#define MII_PROT(port, prot) (((prot) & 0xf) << ((port) << 2)) + +#define IMX95_CFG_LINK_PCS_PROT(a) (0x8 + (a) * 4) +#define PCS_PROT_1G_SGMII BIT(0) +#define PCS_PROT_2500M_SGMII BIT(1) +#define PCS_PROT_XFI BIT(3) +#define PCS_PROT_SFI BIT(4) +#define PCS_PROT_10G_SXGMII BIT(6) + +/* NETC privileged register block register */ +#define PRB_NETCRR 0x100 +#define NETCRR_SR BIT(0) +#define NETCRR_LOCK BIT(1) + +#define PRB_NETCSR 0x104 +#define NETCSR_ERROR BIT(0) +#define NETCSR_STATE BIT(1) + +/* NETC integrated endpoint register block register */ +#define IERB_EMDIOFAUXR 0x344 +#define IERB_T0FAUXR 0x444 +#define IERB_EFAUXR(a) (0x3044 + 0x100 * (a)) +#define IERB_VFAUXR(a) (0x4004 + 0x40 * (a)) +#define FAUXR_LDID GENMASK(3, 0) + +/* Platform information */ +#define IMX95_ENETC0_BUS_DEVFN 0x0 +#define IMX95_ENETC1_BUS_DEVFN 0x40 +#define IMX95_ENETC2_BUS_DEVFN 0x80 + +/* Flags for different platforms */ +#define NETC_HAS_NETCMIX BIT(0) + +struct netc_blk_ctrl { + void __iomem *prb; + void __iomem *ierb; + void __iomem *netcmix; +}; + +static void netc_reg_write(void __iomem *base, u32 offset, u32 val) +{ + writel(val, base + offset); +} + +static u32 netc_reg_read(void __iomem *base, u32 offset) +{ + return readl(base + offset); +} + +static int netc_of_pci_get_bus_devfn(ofnode node) +{ + u32 reg[5]; + int error; + + error = ofnode_read_u32_array(node, "reg", reg, ARRAY_SIZE(reg)); + if (error) + return error; + + return (reg[0] >> 8) & 0xffff; +} + +static int netc_get_link_mii_protocol(phy_interface_t interface) +{ + switch (interface) { + case PHY_INTERFACE_MODE_MII: + return MII_PROT_MII; + case PHY_INTERFACE_MODE_RMII: + return MII_PROT_RMII; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + return MII_PROT_RGMII; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_XGMII: + case PHY_INTERFACE_MODE_USXGMII: + return MII_PROT_SERIAL; + default: + return -EINVAL; + } +} + +static int imx95_netcmix_init(struct udevice *dev) +{ + struct netc_blk_ctrl *priv = dev_get_priv(dev); + ofnode child, gchild; + phy_interface_t interface; + int bus_devfn, mii_proto; + u32 val; + + /* Default setting of MII protocol */ + val = MII_PROT(0, MII_PROT_RGMII) | MII_PROT(1, MII_PROT_RGMII) | + MII_PROT(2, MII_PROT_SERIAL); + + /* Update the link MII protocol through parsing phy-mode */ + dev_for_each_subnode(child, dev) { + if (!ofnode_is_enabled(child)) + continue; + + ofnode_for_each_subnode(gchild, child) { + if (!ofnode_is_enabled(gchild)) + continue; + + if (!ofnode_device_is_compatible(gchild, "pci1131,e101")) + continue; + + bus_devfn = netc_of_pci_get_bus_devfn(gchild); + if (bus_devfn < 0) + return -EINVAL; + + if (bus_devfn == IMX95_ENETC2_BUS_DEVFN) + continue; + + interface = ofnode_read_phy_mode(gchild); + if (interface == -1) + continue; + + mii_proto = netc_get_link_mii_protocol(interface); + if (mii_proto < 0) + return -EINVAL; + + switch (bus_devfn) { + case IMX95_ENETC0_BUS_DEVFN: + val &= ~CFG_LINK_MII_PORT_0; + val |= FIELD_PREP(CFG_LINK_MII_PORT_0, mii_proto); + break; + case IMX95_ENETC1_BUS_DEVFN: + val &= ~CFG_LINK_MII_PORT_1; + val |= FIELD_PREP(CFG_LINK_MII_PORT_1, mii_proto); + break; + default: + return -EINVAL; + } + } + } + + /* Configure Link I/O variant */ + netc_reg_write(priv->netcmix, IMX95_CFG_LINK_IO_VAR, + IO_VAR(2, IO_VAR_16FF_16G_SERDES)); + /* Configure Link 2 PCS protocol */ + netc_reg_write(priv->netcmix, IMX95_CFG_LINK_PCS_PROT(2), + PCS_PROT_10G_SXGMII); + netc_reg_write(priv->netcmix, IMX95_CFG_LINK_MII_PROT, val); + + return 0; +} + +static bool netc_ierb_is_locked(struct netc_blk_ctrl *priv) +{ + return !!(netc_reg_read(priv->prb, PRB_NETCRR) & NETCRR_LOCK); +} + +static int netc_lock_ierb(struct netc_blk_ctrl *priv) +{ + u32 val; + + netc_reg_write(priv->prb, PRB_NETCRR, NETCRR_LOCK); + + return readl_poll_timeout(priv->prb + PRB_NETCSR, val, + !(val & NETCSR_STATE), 2000); +} + +static int netc_unlock_ierb_with_warm_reset(struct netc_blk_ctrl *priv) +{ + u32 val; + + netc_reg_write(priv->prb, PRB_NETCRR, 0); + + return readl_poll_timeout(priv->prb + PRB_NETCRR, val, + !(val & NETCRR_LOCK), 100000); +} + +static int imx95_ierb_init(struct udevice *dev) +{ + struct netc_blk_ctrl *priv = dev_get_priv(dev); + + /* EMDIO : No MSI-X intterupt */ + netc_reg_write(priv->ierb, IERB_EMDIOFAUXR, 0); + /* ENETC0 PF */ + netc_reg_write(priv->ierb, IERB_EFAUXR(0), 0); + /* ENETC0 VF0 */ + netc_reg_write(priv->ierb, IERB_VFAUXR(0), 1); + /* ENETC0 VF1 */ + netc_reg_write(priv->ierb, IERB_VFAUXR(1), 2); + /* ENETC1 PF */ + netc_reg_write(priv->ierb, IERB_EFAUXR(1), 3); + /* ENETC1 VF0 */ + netc_reg_write(priv->ierb, IERB_VFAUXR(2), 5); + /* ENETC1 VF1 */ + netc_reg_write(priv->ierb, IERB_VFAUXR(3), 6); + /* ENETC2 PF */ + netc_reg_write(priv->ierb, IERB_EFAUXR(2), 4); + /* ENETC2 VF0 */ + netc_reg_write(priv->ierb, IERB_VFAUXR(4), 5); + /* ENETC2 VF1 */ + netc_reg_write(priv->ierb, IERB_VFAUXR(5), 6); + /* NETC TIMER */ + netc_reg_write(priv->ierb, IERB_T0FAUXR, 7); + + return 0; +} + +static int netc_ierb_init(struct udevice *dev) +{ + struct netc_blk_ctrl *priv = dev_get_priv(dev); + int err; + + if (netc_ierb_is_locked(priv)) { + err = netc_unlock_ierb_with_warm_reset(priv); + if (err) { + dev_err(dev, "Unlock IERB failed.\n"); + return err; + } + } + + err = imx95_ierb_init(dev); + if (err) + return err; + + err = netc_lock_ierb(priv); + if (err) { + dev_err(dev, "Lock IERB failed.\n"); + return err; + } + + return 0; +} + +static int netc_prb_check_error(struct netc_blk_ctrl *priv) +{ + if (netc_reg_read(priv->prb, PRB_NETCSR) & NETCSR_ERROR) + return -1; + + return 0; +} + +static const struct udevice_id netc_blk_ctrl_match[] = { + { .compatible = "nxp,imx95-netc-blk-ctrl" }, + {}, +}; + +static int netc_blk_ctrl_probe(struct udevice *dev) +{ + struct netc_blk_ctrl *priv = dev_get_priv(dev); + struct clk *ipg_clk; + fdt_addr_t regs; + int err; + + ipg_clk = devm_clk_get_optional(dev, "ipg"); + if (IS_ERR(ipg_clk)) { + dev_err(dev, "Set ipg clock failed\n"); + return PTR_ERR(ipg_clk); + } + + err = clk_prepare_enable(ipg_clk); + if (err) { + dev_err(dev, "Enable ipg clock failed\n"); + return PTR_ERR(ipg_clk); + } + + regs = dev_read_addr_name(dev, "ierb"); + if (regs == FDT_ADDR_T_NONE) { + dev_err(dev, "Missing IERB resource\n"); + return -EINVAL; + } + + priv->ierb = (void __iomem *)regs; + regs = dev_read_addr_name(dev, "prb"); + if (regs == FDT_ADDR_T_NONE) { + dev_err(dev, "Missing PRB resource\n"); + return -EINVAL; + } + + priv->prb = (void __iomem *)regs; + regs = dev_read_addr_name(dev, "netcmix"); + if (regs == FDT_ADDR_T_NONE) { + dev_err(dev, "Missing NETCMIX resource\n"); + return -EINVAL; + } + + priv->netcmix = (void __iomem *)regs; + + err = imx95_netcmix_init(dev); + if (err) { + dev_err(dev, "Initializing NETCMIX failed\n"); + return err; + } + + err = netc_ierb_init(dev); + if (err) { + dev_err(dev, "Initializing IERB failed\n"); + return err; + } + + if (netc_prb_check_error(priv) < 0) + dev_warn(dev, "The current IERB configuration is invalid\n"); + + return 0; +} + +U_BOOT_DRIVER(netc_blk_ctrl_drv) = { + .name = "netc_blk_ctrl", + .id = UCLASS_SIMPLE_BUS, + .of_match = netc_blk_ctrl_match, + .probe = netc_blk_ctrl_probe, + .priv_auto = sizeof(struct netc_blk_ctrl), + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/net/mtk_eth/Kconfig b/drivers/net/mtk_eth/Kconfig new file mode 100644 index 00000000000..e8cdf408237 --- /dev/null +++ b/drivers/net/mtk_eth/Kconfig @@ -0,0 +1,39 @@ + +config MEDIATEK_ETH + bool "MediaTek Ethernet GMAC Driver" + select PHYLIB + select DM_GPIO + select DM_RESET + help + This Driver support MediaTek Ethernet GMAC + Say Y to enable support for the MediaTek Ethernet GMAC. + +if MEDIATEK_ETH + +config MTK_ETH_SGMII + bool + default y if ARCH_MEDIATEK && !TARGET_MT7623 + +config MTK_ETH_XGMII + bool + default y if TARGET_MT7987 || TARGET_MT7988 + +config MTK_ETH_SWITCH_MT7530 + bool "Support for MediaTek MT7530 ethernet switch" + default y if TARGET_MT7623 || SOC_MT7621 + +config MTK_ETH_SWITCH_MT7531 + bool "Support for MediaTek MT7531 ethernet switch" + default y if TARGET_MT7622 || TARGET_MT7629 || TARGET_MT7981 || \ + TARGET_MT7986 || TARGET_MT7987 + +config MTK_ETH_SWITCH_MT7988 + bool "Support for MediaTek MT7988 built-in ethernet switch" + depends on TARGET_MT7988 + default y + +config MTK_ETH_SWITCH_AN8855 + bool "Support for Airoha AN8855 ethernet switch" + default y if TARGET_MT7981 || TARGET_MT7987 + +endif # MEDIATEK_ETH diff --git a/drivers/net/mtk_eth/Makefile b/drivers/net/mtk_eth/Makefile new file mode 100644 index 00000000000..a342325ed5d --- /dev/null +++ b/drivers/net/mtk_eth/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2025 MediaTek Inc. +# Author: Weijie Gao <weijie.gao@mediatek.com> + +obj-y += mtk_eth.o +obj-$(CONFIG_MTK_ETH_SWITCH_MT7530) += mt753x.o mt7530.o +obj-$(CONFIG_MTK_ETH_SWITCH_MT7531) += mt753x.o mt7531.o +obj-$(CONFIG_MTK_ETH_SWITCH_MT7988) += mt753x.o mt7988.o +obj-$(CONFIG_MTK_ETH_SWITCH_AN8855) += an8855.o diff --git a/drivers/net/mtk_eth/an8855.c b/drivers/net/mtk_eth/an8855.c new file mode 100644 index 00000000000..25a98e0f935 --- /dev/null +++ b/drivers/net/mtk_eth/an8855.c @@ -0,0 +1,1095 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 MediaTek Inc. + * + * Author: Neal Yen <neal.yen@mediatek.com> + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +#include <phy.h> +#include <miiphy.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/mdio.h> +#include <linux/mii.h> +#include "mtk_eth.h" + +/* AN8855 Register Definitions */ +#define AN8855_SYS_CTRL_REG 0x100050c0 +#define AN8855_SW_SYS_RST BIT(31) + +#define AN8855_PMCR_REG(p) (0x10210000 + (p) * 0x200) +#define AN8855_FORCE_MODE_LNK BIT(31) +#define AN8855_FORCE_MODE 0xb31593f0 + +#define AN8855_PORT_CTRL_BASE 0x10208000 +#define AN8855_PORT_CTRL_REG(p, r) (AN8855_PORT_CTRL_BASE + (p) * 0x200 + (r)) + +#define AN8855_PORTMATRIX_REG(p) AN8855_PORT_CTRL_REG(p, 0x44) + +#define AN8855_PVC(p) AN8855_PORT_CTRL_REG(p, 0x10) +#define AN8855_STAG_VPID_S 16 +#define AN8855_STAG_VPID_M 0xffff0000 +#define AN8855_VLAN_ATTR_S 6 +#define AN8855_VLAN_ATTR_M 0xc0 + +#define VLAN_ATTR_USER 0 + +#define AN8855_INT_MASK 0x100050F0 +#define AN8855_INT_SYS_BIT BIT(15) + +#define AN8855_RG_CLK_CPU_ICG 0x10005034 +#define AN8855_MCU_ENABLE BIT(3) + +#define AN8855_RG_TIMER_CTL 0x1000a100 +#define AN8855_WDOG_ENABLE BIT(25) + +#define AN8855_CKGCR 0x10213e1c + +#define AN8855_SCU_BASE 0x10000000 +#define AN8855_RG_RGMII_TXCK_C (AN8855_SCU_BASE + 0x1d0) +#define AN8855_RG_GPIO_LED_MODE (AN8855_SCU_BASE + 0x0054) +#define AN8855_RG_GPIO_LED_SEL(i) (AN8855_SCU_BASE + (0x0058 + ((i) * 4))) +#define AN8855_RG_INTB_MODE (AN8855_SCU_BASE + 0x0080) +#define AN8855_RG_GDMP_RAM (AN8855_SCU_BASE + 0x10000) +#define AN8855_RG_GPIO_L_INV (AN8855_SCU_BASE + 0x0010) +#define AN8855_RG_GPIO_CTRL (AN8855_SCU_BASE + 0xa300) +#define AN8855_RG_GPIO_DATA (AN8855_SCU_BASE + 0xa304) +#define AN8855_RG_GPIO_OE (AN8855_SCU_BASE + 0xa314) + +#define AN8855_HSGMII_AN_CSR_BASE 0x10220000 +#define AN8855_SGMII_REG_AN0 (AN8855_HSGMII_AN_CSR_BASE + 0x000) +#define AN8855_SGMII_REG_AN_13 (AN8855_HSGMII_AN_CSR_BASE + 0x034) +#define AN8855_SGMII_REG_AN_FORCE_CL37 (AN8855_HSGMII_AN_CSR_BASE + 0x060) + +#define AN8855_HSGMII_CSR_PCS_BASE 0x10220000 +#define AN8855_RG_HSGMII_PCS_CTROL_1 (AN8855_HSGMII_CSR_PCS_BASE + 0xa00) +#define AN8855_RG_AN_SGMII_MODE_FORCE (AN8855_HSGMII_CSR_PCS_BASE + 0xa24) + +#define AN8855_MULTI_SGMII_CSR_BASE 0x10224000 +#define AN8855_SGMII_STS_CTRL_0 (AN8855_MULTI_SGMII_CSR_BASE + 0x018) +#define AN8855_MSG_RX_CTRL_0 (AN8855_MULTI_SGMII_CSR_BASE + 0x100) +#define AN8855_MSG_RX_LIK_STS_0 (AN8855_MULTI_SGMII_CSR_BASE + 0x514) +#define AN8855_MSG_RX_LIK_STS_2 (AN8855_MULTI_SGMII_CSR_BASE + 0x51c) +#define AN8855_PHY_RX_FORCE_CTRL_0 (AN8855_MULTI_SGMII_CSR_BASE + 0x520) + +#define AN8855_XFI_CSR_PCS_BASE 0x10225000 +#define AN8855_RG_USXGMII_AN_CONTROL_0 (AN8855_XFI_CSR_PCS_BASE + 0xbf8) + +#define AN8855_MULTI_PHY_RA_CSR_BASE 0x10226000 +#define AN8855_RG_RATE_ADAPT_CTRL_0 (AN8855_MULTI_PHY_RA_CSR_BASE + 0x000) +#define AN8855_RATE_ADP_P0_CTRL_0 (AN8855_MULTI_PHY_RA_CSR_BASE + 0x100) +#define AN8855_MII_RA_AN_ENABLE (AN8855_MULTI_PHY_RA_CSR_BASE + 0x300) + +#define AN8855_QP_DIG_CSR_BASE 0x1022a000 +#define AN8855_QP_CK_RST_CTRL_4 (AN8855_QP_DIG_CSR_BASE + 0x310) +#define AN8855_QP_DIG_MODE_CTRL_0 (AN8855_QP_DIG_CSR_BASE + 0x324) +#define AN8855_QP_DIG_MODE_CTRL_1 (AN8855_QP_DIG_CSR_BASE + 0x330) + +#define AN8855_QP_PMA_TOP_BASE 0x1022e000 +#define AN8855_PON_RXFEDIG_CTRL_0 (AN8855_QP_PMA_TOP_BASE + 0x100) +#define AN8855_PON_RXFEDIG_CTRL_9 (AN8855_QP_PMA_TOP_BASE + 0x124) + +#define AN8855_SS_LCPLL_PWCTL_SETTING_2 (AN8855_QP_PMA_TOP_BASE + 0x208) +#define AN8855_SS_LCPLL_TDC_FLT_2 (AN8855_QP_PMA_TOP_BASE + 0x230) +#define AN8855_SS_LCPLL_TDC_FLT_5 (AN8855_QP_PMA_TOP_BASE + 0x23c) +#define AN8855_SS_LCPLL_TDC_PCW_1 (AN8855_QP_PMA_TOP_BASE + 0x248) +#define AN8855_INTF_CTRL_8 (AN8855_QP_PMA_TOP_BASE + 0x320) +#define AN8855_INTF_CTRL_9 (AN8855_QP_PMA_TOP_BASE + 0x324) +#define AN8855_PLL_CTRL_0 (AN8855_QP_PMA_TOP_BASE + 0x400) +#define AN8855_PLL_CTRL_2 (AN8855_QP_PMA_TOP_BASE + 0x408) +#define AN8855_PLL_CTRL_3 (AN8855_QP_PMA_TOP_BASE + 0x40c) +#define AN8855_PLL_CTRL_4 (AN8855_QP_PMA_TOP_BASE + 0x410) +#define AN8855_PLL_CK_CTRL_0 (AN8855_QP_PMA_TOP_BASE + 0x414) +#define AN8855_RX_DLY_0 (AN8855_QP_PMA_TOP_BASE + 0x614) +#define AN8855_RX_CTRL_2 (AN8855_QP_PMA_TOP_BASE + 0x630) +#define AN8855_RX_CTRL_5 (AN8855_QP_PMA_TOP_BASE + 0x63c) +#define AN8855_RX_CTRL_6 (AN8855_QP_PMA_TOP_BASE + 0x640) +#define AN8855_RX_CTRL_7 (AN8855_QP_PMA_TOP_BASE + 0x644) +#define AN8855_RX_CTRL_8 (AN8855_QP_PMA_TOP_BASE + 0x648) +#define AN8855_RX_CTRL_26 (AN8855_QP_PMA_TOP_BASE + 0x690) +#define AN8855_RX_CTRL_42 (AN8855_QP_PMA_TOP_BASE + 0x6d0) + +#define AN8855_QP_ANA_CSR_BASE 0x1022f000 +#define AN8855_RG_QP_RX_DAC_EN (AN8855_QP_ANA_CSR_BASE + 0x00) +#define AN8855_RG_QP_RXAFE_RESERVE (AN8855_QP_ANA_CSR_BASE + 0x04) +#define AN8855_RG_QP_CDR_LPF_MJV_LIM (AN8855_QP_ANA_CSR_BASE + 0x0c) +#define AN8855_RG_QP_CDR_LPF_SETVALUE (AN8855_QP_ANA_CSR_BASE + 0x14) +#define AN8855_RG_QP_CDR_PR_CKREF_DIV1 (AN8855_QP_ANA_CSR_BASE + 0x18) +#define AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE (AN8855_QP_ANA_CSR_BASE + 0x1c) +#define AN8855_RG_QP_CDR_FORCE_IBANDLPF_R_OFF (AN8855_QP_ANA_CSR_BASE + 0x20) +#define AN8855_RG_QP_TX_MODE_16B_EN (AN8855_QP_ANA_CSR_BASE + 0x28) +#define AN8855_RG_QP_PLL_IPLL_DIG_PWR_SEL (AN8855_QP_ANA_CSR_BASE + 0x3c) +#define AN8855_RG_QP_PLL_SDM_ORD (AN8855_QP_ANA_CSR_BASE + 0x40) + +#define AN8855_ETHER_SYS_BASE 0x1028c800 +#define RG_GPHY_AFE_PWD (AN8855_ETHER_SYS_BASE + 0x40) + +#define AN8855_PKG_SEL 0x10000094 +#define PAG_SEL_AN8855H 0x2 + +/* PHY LED Register bitmap of define */ +#define PHY_LED_CTRL_SELECT 0x3e8 +#define PHY_SINGLE_LED_ON_CTRL(i) (0x3e0 + ((i) * 2)) +#define PHY_SINGLE_LED_BLK_CTRL(i) (0x3e1 + ((i) * 2)) +#define PHY_SINGLE_LED_ON_DUR(i) (0x3e9 + ((i) * 2)) +#define PHY_SINGLE_LED_BLK_DUR(i) (0x3ea + ((i) * 2)) + +#define PHY_PMA_CTRL 0x340 + +#define PHY_DEV1F 0x1f + +#define PHY_LED_ON_CTRL(i) (0x24 + ((i) * 2)) +#define LED_ON_EN BIT(15) +#define LED_ON_POL BIT(14) +#define LED_ON_EVT_MASK 0x7f + +/* LED ON Event */ +#define LED_ON_EVT_FORCE BIT(6) +#define LED_ON_EVT_LINK_HD BIT(5) +#define LED_ON_EVT_LINK_FD BIT(4) +#define LED_ON_EVT_LINK_DOWN BIT(3) +#define LED_ON_EVT_LINK_10M BIT(2) +#define LED_ON_EVT_LINK_100M BIT(1) +#define LED_ON_EVT_LINK_1000M BIT(0) + +#define PHY_LED_BLK_CTRL(i) (0x25 + ((i) * 2)) +#define LED_BLK_EVT_MASK 0x3ff +/* LED Blinking Event */ +#define LED_BLK_EVT_FORCE BIT(9) +#define LED_BLK_EVT_10M_RX_ACT BIT(5) +#define LED_BLK_EVT_10M_TX_ACT BIT(4) +#define LED_BLK_EVT_100M_RX_ACT BIT(3) +#define LED_BLK_EVT_100M_TX_ACT BIT(2) +#define LED_BLK_EVT_1000M_RX_ACT BIT(1) +#define LED_BLK_EVT_1000M_TX_ACT BIT(0) + +#define PHY_LED_BCR (0x21) +#define LED_BCR_EXT_CTRL BIT(15) +#define LED_BCR_CLK_EN BIT(3) +#define LED_BCR_TIME_TEST BIT(2) +#define LED_BCR_MODE_MASK 3 +#define LED_BCR_MODE_DISABLE 0 + +#define PHY_LED_ON_DUR 0x22 +#define LED_ON_DUR_MASK 0xffff + +#define PHY_LED_BLK_DUR 0x23 +#define LED_BLK_DUR_MASK 0xffff + +#define PHY_LED_BLINK_DUR_CTRL 0x720 + +/* Definition of LED */ +#define LED_ON_EVENT (LED_ON_EVT_LINK_1000M | \ + LED_ON_EVT_LINK_100M | LED_ON_EVT_LINK_10M |\ + LED_ON_EVT_LINK_HD | LED_ON_EVT_LINK_FD) + +#define LED_BLK_EVENT (LED_BLK_EVT_1000M_TX_ACT | \ + LED_BLK_EVT_1000M_RX_ACT | \ + LED_BLK_EVT_100M_TX_ACT | \ + LED_BLK_EVT_100M_RX_ACT | \ + LED_BLK_EVT_10M_TX_ACT | \ + LED_BLK_EVT_10M_RX_ACT) + +#define LED_FREQ AIR_LED_BLK_DUR_64M + +#define AN8855_NUM_PHYS 5 +#define AN8855_NUM_PORTS 6 +#define AN8855_PHY_ADDR(base, addr) (((base) + (addr)) & 0x1f) + +/* PHY LED Register bitmap of define */ +#define PHY_LED_CTRL_SELECT 0x3e8 +#define PHY_SINGLE_LED_ON_CTRL(i) (0x3e0 + ((i) * 2)) +#define PHY_SINGLE_LED_BLK_CTRL(i) (0x3e1 + ((i) * 2)) +#define PHY_SINGLE_LED_ON_DUR(i) (0x3e9 + ((i) * 2)) +#define PHY_SINGLE_LED_BLK_DUR(i) (0x3ea + ((i) * 2)) + +/* AN8855 LED */ +enum an8855_led_blk_dur { + AIR_LED_BLK_DUR_32M, + AIR_LED_BLK_DUR_64M, + AIR_LED_BLK_DUR_128M, + AIR_LED_BLK_DUR_256M, + AIR_LED_BLK_DUR_512M, + AIR_LED_BLK_DUR_1024M, + AIR_LED_BLK_DUR_LAST +}; + +enum an8855_led_polarity { + LED_LOW, + LED_HIGH, +}; + +enum an8855_led_mode { + AN8855_LED_MODE_DISABLE, + AN8855_LED_MODE_USER_DEFINE, + AN8855_LED_MODE_LAST +}; + +enum phy_led_idx { + P0_LED0, + P0_LED1, + P0_LED2, + P0_LED3, + P1_LED0, + P1_LED1, + P1_LED2, + P1_LED3, + P2_LED0, + P2_LED1, + P2_LED2, + P2_LED3, + P3_LED0, + P3_LED1, + P3_LED2, + P3_LED3, + P4_LED0, + P4_LED1, + P4_LED2, + P4_LED3, + PHY_LED_MAX +}; + +struct an8855_led_cfg { + u16 en; + u8 phy_led_idx; + u16 pol; + u16 on_cfg; + u16 blk_cfg; + u8 led_freq; +}; + +struct an8855_switch_priv { + struct mtk_eth_switch_priv epriv; + struct mii_dev *mdio_bus; + u32 phy_base; +}; + +/* AN8855 Reference Board */ +static const struct an8855_led_cfg led_cfg[] = { +/************************************************************************* + * Enable, LED idx, LED Polarity, LED ON event, LED Blink event LED Freq + ************************************************************************* + */ + /* GPIO0 */ + {1, P4_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO1 */ + {1, P4_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO2 */ + {1, P0_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO3 */ + {1, P0_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO4 */ + {1, P1_LED0, LED_LOW, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO5 */ + {1, P1_LED1, LED_LOW, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO6 */ + {0, PHY_LED_MAX, LED_LOW, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO7 */ + {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO8 */ + {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO9 */ + {1, P2_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO10 */ + {1, P2_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO11 */ + {1, P3_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO12 */ + {1, P3_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO13 */ + {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO14 */ + {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO15 */ + {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO16 */ + {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO17 */ + {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO18 */ + {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO19 */ + {0, PHY_LED_MAX, LED_LOW, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, + /* GPIO20 */ + {0, PHY_LED_MAX, LED_LOW, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, +}; + +static int __an8855_reg_read(struct mtk_eth_priv *priv, u8 phy_base, u32 reg, u32 *data) +{ + int ret, low_word, high_word; + + ret = mtk_mii_write(priv, phy_base, 0x1f, 0x4); + if (ret) + return ret; + + ret = mtk_mii_write(priv, phy_base, 0x10, 0); + if (ret) + return ret; + + ret = mtk_mii_write(priv, phy_base, 0x15, ((reg >> 16) & 0xFFFF)); + if (ret) + return ret; + + ret = mtk_mii_write(priv, phy_base, 0x16, (reg & 0xFFFF)); + if (ret) + return ret; + + low_word = mtk_mii_read(priv, phy_base, 0x18); + if (low_word < 0) + return low_word; + + high_word = mtk_mii_read(priv, phy_base, 0x17); + if (high_word < 0) + return high_word; + + ret = mtk_mii_write(priv, phy_base, 0x1f, 0); + if (ret) + return ret; + + ret = mtk_mii_write(priv, phy_base, 0x10, 0); + if (ret) + return ret; + + if (data) + *data = ((u32)high_word << 16) | (low_word & 0xffff); + + return 0; +} + +static int an8855_reg_read(struct an8855_switch_priv *priv, u32 reg, u32 *data) +{ + return __an8855_reg_read(priv->epriv.eth, priv->phy_base, reg, data); +} + +static int an8855_reg_write(struct an8855_switch_priv *priv, u32 reg, u32 data) +{ + int ret; + + ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x1f, 0x4); + if (ret) + return ret; + + ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x10, 0); + if (ret) + return ret; + + ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x11, + ((reg >> 16) & 0xFFFF)); + if (ret) + return ret; + + ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x12, + (reg & 0xFFFF)); + if (ret) + return ret; + + ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x13, + ((data >> 16) & 0xFFFF)); + if (ret) + return ret; + + ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x14, + (data & 0xFFFF)); + if (ret) + return ret; + + ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x1f, 0); + if (ret) + return ret; + + ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x10, 0); + if (ret) + return ret; + + return 0; +} + +static int an8855_phy_cl45_read(struct an8855_switch_priv *priv, int port, + int devad, int regnum, u16 *data) +{ + u16 phy_addr = AN8855_PHY_ADDR(priv->phy_base, port); + + *data = mtk_mmd_ind_read(priv->epriv.eth, phy_addr, devad, regnum); + + return 0; +} + +static int an8855_phy_cl45_write(struct an8855_switch_priv *priv, int port, + int devad, int regnum, u16 data) +{ + u16 phy_addr = AN8855_PHY_ADDR(priv->phy_base, port); + + mtk_mmd_ind_write(priv->epriv.eth, phy_addr, devad, regnum, data); + + return 0; +} + +static int an8855_port_sgmii_init(struct an8855_switch_priv *priv, u32 port) +{ + u32 val = 0; + + if (port != 5) { + printf("an8855: port %d is not a SGMII port\n", port); + return -EINVAL; + } + + /* PLL */ + an8855_reg_read(priv, AN8855_QP_DIG_MODE_CTRL_1, &val); + val &= ~(0x3 << 2); + val |= (0x1 << 2); + an8855_reg_write(priv, AN8855_QP_DIG_MODE_CTRL_1, val); + + /* PLL - LPF */ + an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val); + val &= ~(0x3 << 0); + val |= (0x1 << 0); + val &= ~(0x7 << 2); + val |= (0x5 << 2); + val &= ~GENMASK(7, 6); + val &= ~(0x7 << 8); + val |= (0x3 << 8); + val |= BIT(29); + val &= ~GENMASK(13, 12); + an8855_reg_write(priv, AN8855_PLL_CTRL_2, val); + + /* PLL - ICO */ + an8855_reg_read(priv, AN8855_PLL_CTRL_4, &val); + val |= BIT(2); + an8855_reg_write(priv, AN8855_PLL_CTRL_4, val); + + an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val); + val &= ~BIT(14); + an8855_reg_write(priv, AN8855_PLL_CTRL_2, val); + + /* PLL - CHP */ + an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val); + val &= ~(0xf << 16); + val |= (0x6 << 16); + an8855_reg_write(priv, AN8855_PLL_CTRL_2, val); + + /* PLL - PFD */ + an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val); + val &= ~(0x3 << 20); + val |= (0x1 << 20); + val &= ~(0x3 << 24); + val |= (0x1 << 24); + val &= ~BIT(26); + an8855_reg_write(priv, AN8855_PLL_CTRL_2, val); + + /* PLL - POSTDIV */ + an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val); + val |= BIT(22); + val &= ~BIT(27); + val &= ~BIT(28); + an8855_reg_write(priv, AN8855_PLL_CTRL_2, val); + + /* PLL - SDM */ + an8855_reg_read(priv, AN8855_PLL_CTRL_4, &val); + val &= ~GENMASK(4, 3); + an8855_reg_write(priv, AN8855_PLL_CTRL_4, val); + + an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val); + val &= ~BIT(30); + an8855_reg_write(priv, AN8855_PLL_CTRL_2, val); + + an8855_reg_read(priv, AN8855_SS_LCPLL_PWCTL_SETTING_2, &val); + val &= ~(0x3 << 16); + val |= (0x1 << 16); + an8855_reg_write(priv, AN8855_SS_LCPLL_PWCTL_SETTING_2, val); + + an8855_reg_write(priv, AN8855_SS_LCPLL_TDC_FLT_2, 0x7a000000); + an8855_reg_write(priv, AN8855_SS_LCPLL_TDC_PCW_1, 0x7a000000); + + an8855_reg_read(priv, AN8855_SS_LCPLL_TDC_FLT_5, &val); + val &= ~BIT(24); + an8855_reg_write(priv, AN8855_SS_LCPLL_TDC_FLT_5, val); + + an8855_reg_read(priv, AN8855_PLL_CK_CTRL_0, &val); + val &= ~BIT(8); + an8855_reg_write(priv, AN8855_PLL_CK_CTRL_0, val); + + /* PLL - SS */ + an8855_reg_read(priv, AN8855_PLL_CTRL_3, &val); + val &= ~GENMASK(15, 0); + an8855_reg_write(priv, AN8855_PLL_CTRL_3, val); + + an8855_reg_read(priv, AN8855_PLL_CTRL_4, &val); + val &= ~GENMASK(1, 0); + an8855_reg_write(priv, AN8855_PLL_CTRL_4, val); + + an8855_reg_read(priv, AN8855_PLL_CTRL_3, &val); + val &= ~GENMASK(31, 16); + an8855_reg_write(priv, AN8855_PLL_CTRL_3, val); + + /* PLL - TDC */ + an8855_reg_read(priv, AN8855_PLL_CK_CTRL_0, &val); + val &= ~BIT(9); + an8855_reg_write(priv, AN8855_PLL_CK_CTRL_0, val); + + an8855_reg_read(priv, AN8855_RG_QP_PLL_SDM_ORD, &val); + val |= BIT(3); + val |= BIT(4); + an8855_reg_write(priv, AN8855_RG_QP_PLL_SDM_ORD, val); + + an8855_reg_read(priv, AN8855_RG_QP_RX_DAC_EN, &val); + val &= ~(0x3 << 16); + val |= (0x2 << 16); + an8855_reg_write(priv, AN8855_RG_QP_RX_DAC_EN, val); + + /* TCL Disable (only for Co-SIM) */ + an8855_reg_read(priv, AN8855_PON_RXFEDIG_CTRL_0, &val); + val &= ~BIT(12); + an8855_reg_write(priv, AN8855_PON_RXFEDIG_CTRL_0, val); + + /* TX Init */ + an8855_reg_read(priv, AN8855_RG_QP_TX_MODE_16B_EN, &val); + val &= ~BIT(0); + val &= ~(0xffff << 16); + val |= (0x4 << 16); + an8855_reg_write(priv, AN8855_RG_QP_TX_MODE_16B_EN, val); + + /* RX Control */ + an8855_reg_read(priv, AN8855_RG_QP_RXAFE_RESERVE, &val); + val |= BIT(11); + an8855_reg_write(priv, AN8855_RG_QP_RXAFE_RESERVE, val); + + an8855_reg_read(priv, AN8855_RG_QP_CDR_LPF_MJV_LIM, &val); + val &= ~(0x3 << 4); + val |= (0x1 << 4); + an8855_reg_write(priv, AN8855_RG_QP_CDR_LPF_MJV_LIM, val); + + an8855_reg_read(priv, AN8855_RG_QP_CDR_LPF_SETVALUE, &val); + val &= ~(0xf << 25); + val |= (0x1 << 25); + val &= ~(0x7 << 29); + val |= (0x3 << 29); + an8855_reg_write(priv, AN8855_RG_QP_CDR_LPF_SETVALUE, val); + + an8855_reg_read(priv, AN8855_RG_QP_CDR_PR_CKREF_DIV1, &val); + val &= ~(0x1f << 8); + val |= (0xf << 8); + an8855_reg_write(priv, AN8855_RG_QP_CDR_PR_CKREF_DIV1, val); + + an8855_reg_read(priv, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE, &val); + val &= ~(0x3f << 0); + val |= (0x19 << 0); + val &= ~BIT(6); + an8855_reg_write(priv, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE, val); + + an8855_reg_read(priv, AN8855_RG_QP_CDR_FORCE_IBANDLPF_R_OFF, &val); + val &= ~(0x7f << 6); + val |= (0x21 << 6); + val &= ~(0x3 << 16); + val |= (0x2 << 16); + val &= ~BIT(13); + an8855_reg_write(priv, AN8855_RG_QP_CDR_FORCE_IBANDLPF_R_OFF, val); + + an8855_reg_read(priv, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE, &val); + val &= ~BIT(30); + an8855_reg_write(priv, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE, val); + + an8855_reg_read(priv, AN8855_RG_QP_CDR_PR_CKREF_DIV1, &val); + val &= ~(0x7 << 24); + val |= (0x4 << 24); + an8855_reg_write(priv, AN8855_RG_QP_CDR_PR_CKREF_DIV1, val); + + an8855_reg_read(priv, AN8855_PLL_CTRL_0, &val); + val |= BIT(0); + an8855_reg_write(priv, AN8855_PLL_CTRL_0, val); + + an8855_reg_read(priv, AN8855_RX_CTRL_26, &val); + val &= ~BIT(23); + val |= BIT(26); + an8855_reg_write(priv, AN8855_RX_CTRL_26, val); + + an8855_reg_read(priv, AN8855_RX_DLY_0, &val); + val &= ~(0xff << 0); + val |= (0x6f << 0); + val |= GENMASK(13, 8); + an8855_reg_write(priv, AN8855_RX_DLY_0, val); + + an8855_reg_read(priv, AN8855_RX_CTRL_42, &val); + val &= ~(0x1fff << 0); + val |= (0x150 << 0); + an8855_reg_write(priv, AN8855_RX_CTRL_42, val); + + an8855_reg_read(priv, AN8855_RX_CTRL_2, &val); + val &= ~(0x1fff << 16); + val |= (0x150 << 16); + an8855_reg_write(priv, AN8855_RX_CTRL_2, val); + + an8855_reg_read(priv, AN8855_PON_RXFEDIG_CTRL_9, &val); + val &= ~(0x7 << 0); + val |= (0x1 << 0); + an8855_reg_write(priv, AN8855_PON_RXFEDIG_CTRL_9, val); + + an8855_reg_read(priv, AN8855_RX_CTRL_8, &val); + val &= ~(0xfff << 16); + val |= (0x200 << 16); + val &= ~(0x7fff << 14); + val |= (0xfff << 14); + an8855_reg_write(priv, AN8855_RX_CTRL_8, val); + + /* Frequency memter */ + an8855_reg_read(priv, AN8855_RX_CTRL_5, &val); + val &= ~(0xfffff << 10); + val |= (0x10 << 10); + an8855_reg_write(priv, AN8855_RX_CTRL_5, val); + + an8855_reg_read(priv, AN8855_RX_CTRL_6, &val); + val &= ~(0xfffff << 0); + val |= (0x64 << 0); + an8855_reg_write(priv, AN8855_RX_CTRL_6, val); + + an8855_reg_read(priv, AN8855_RX_CTRL_7, &val); + val &= ~(0xfffff << 0); + val |= (0x2710 << 0); + an8855_reg_write(priv, AN8855_RX_CTRL_7, val); + + /* PCS Init */ + an8855_reg_read(priv, AN8855_RG_HSGMII_PCS_CTROL_1, &val); + val &= ~BIT(30); + an8855_reg_write(priv, AN8855_RG_HSGMII_PCS_CTROL_1, val); + + /* Rate Adaption */ + an8855_reg_read(priv, AN8855_RATE_ADP_P0_CTRL_0, &val); + val &= ~BIT(31); + an8855_reg_write(priv, AN8855_RATE_ADP_P0_CTRL_0, val); + + an8855_reg_read(priv, AN8855_RG_RATE_ADAPT_CTRL_0, &val); + val |= BIT(0); + val |= BIT(4); + val |= GENMASK(27, 26); + an8855_reg_write(priv, AN8855_RG_RATE_ADAPT_CTRL_0, val); + + /* Disable AN */ + an8855_reg_read(priv, AN8855_SGMII_REG_AN0, &val); + val &= ~BIT(12); + an8855_reg_write(priv, AN8855_SGMII_REG_AN0, val); + + /* Force Speed */ + an8855_reg_read(priv, AN8855_SGMII_STS_CTRL_0, &val); + val |= BIT(2); + val |= GENMASK(5, 4); + an8855_reg_write(priv, AN8855_SGMII_STS_CTRL_0, val); + + /* bypass flow control to MAC */ + an8855_reg_write(priv, AN8855_MSG_RX_LIK_STS_0, 0x01010107); + an8855_reg_write(priv, AN8855_MSG_RX_LIK_STS_2, 0x00000EEF); + + return 0; +} + +static void an8855_led_set_usr_def(struct an8855_switch_priv *priv, u8 entity, + enum an8855_led_polarity pol, u16 on_evt, + u16 blk_evt, u8 led_freq) +{ + u32 cl45_data; + + if (pol == LED_HIGH) + on_evt |= LED_ON_POL; + else + on_evt &= ~LED_ON_POL; + + /* LED on event */ + an8855_phy_cl45_write(priv, (entity / 4), 0x1e, + PHY_SINGLE_LED_ON_CTRL(entity % 4), + on_evt | LED_ON_EN); + + /* LED blink event */ + an8855_phy_cl45_write(priv, (entity / 4), 0x1e, + PHY_SINGLE_LED_BLK_CTRL(entity % 4), + blk_evt); + + /* LED freq */ + switch (led_freq) { + case AIR_LED_BLK_DUR_32M: + cl45_data = 0x30e; + break; + + case AIR_LED_BLK_DUR_64M: + cl45_data = 0x61a; + break; + + case AIR_LED_BLK_DUR_128M: + cl45_data = 0xc35; + break; + + case AIR_LED_BLK_DUR_256M: + cl45_data = 0x186a; + break; + + case AIR_LED_BLK_DUR_512M: + cl45_data = 0x30d4; + break; + + case AIR_LED_BLK_DUR_1024M: + cl45_data = 0x61a8; + break; + + default: + cl45_data = 0; + break; + } + + an8855_phy_cl45_write(priv, (entity / 4), 0x1e, + PHY_SINGLE_LED_BLK_DUR(entity % 4), + cl45_data); + + an8855_phy_cl45_write(priv, (entity / 4), 0x1e, + PHY_SINGLE_LED_ON_DUR(entity % 4), + (cl45_data >> 1)); + + /* Disable DATA & BAD_SSD for port LED blink behavior */ + cl45_data = mtk_mmd_ind_read(priv->epriv.eth, (entity / 4), 0x1e, PHY_PMA_CTRL); + cl45_data &= ~BIT(0); + cl45_data &= ~BIT(15); + an8855_phy_cl45_write(priv, (entity / 4), 0x1e, PHY_PMA_CTRL, cl45_data); +} + +static int an8855_led_set_mode(struct an8855_switch_priv *priv, u8 mode) +{ + u16 cl45_data; + + an8855_phy_cl45_read(priv, 0, 0x1f, PHY_LED_BCR, &cl45_data); + + switch (mode) { + case AN8855_LED_MODE_DISABLE: + cl45_data &= ~LED_BCR_EXT_CTRL; + cl45_data &= ~LED_BCR_MODE_MASK; + cl45_data |= LED_BCR_MODE_DISABLE; + break; + + case AN8855_LED_MODE_USER_DEFINE: + cl45_data |= LED_BCR_EXT_CTRL; + cl45_data |= LED_BCR_CLK_EN; + break; + + default: + printf("an8855: LED mode%d is not supported!\n", mode); + return -EINVAL; + } + + an8855_phy_cl45_write(priv, 0, 0x1f, PHY_LED_BCR, cl45_data); + + return 0; +} + +static int an8855_led_set_state(struct an8855_switch_priv *priv, u8 entity, + u8 state) +{ + u16 cl45_data = 0; + + /* Change to per port contorl */ + an8855_phy_cl45_read(priv, (entity / 4), 0x1e, PHY_LED_CTRL_SELECT, + &cl45_data); + + if (state == 1) + cl45_data |= (1 << (entity % 4)); + else + cl45_data &= ~(1 << (entity % 4)); + + an8855_phy_cl45_write(priv, (entity / 4), 0x1e, PHY_LED_CTRL_SELECT, + cl45_data); + + /* LED enable setting */ + an8855_phy_cl45_read(priv, (entity / 4), 0x1e, + PHY_SINGLE_LED_ON_CTRL(entity % 4), &cl45_data); + + if (state == 1) + cl45_data |= LED_ON_EN; + else + cl45_data &= ~LED_ON_EN; + + an8855_phy_cl45_write(priv, (entity / 4), 0x1e, + PHY_SINGLE_LED_ON_CTRL(entity % 4), cl45_data); + + return 0; +} + +static int an8855_led_init(struct an8855_switch_priv *priv) +{ + u32 val, id, tmp_id = 0; + int ret; + + ret = an8855_led_set_mode(priv, AN8855_LED_MODE_USER_DEFINE); + if (ret) { + printf("an8855: led_set_mode failed with %d!\n", ret); + return ret; + } + + for (id = 0; id < ARRAY_SIZE(led_cfg); id++) { + ret = an8855_led_set_state(priv, led_cfg[id].phy_led_idx, + led_cfg[id].en); + if (ret != 0) { + printf("an8855: led_set_state failed with %d!\n", ret); + return ret; + } + + if (led_cfg[id].en == 1) { + an8855_led_set_usr_def(priv, + led_cfg[id].phy_led_idx, + led_cfg[id].pol, + led_cfg[id].on_cfg, + led_cfg[id].blk_cfg, + led_cfg[id].led_freq); + } + } + + /* Setting for System LED & Loop LED */ + an8855_reg_write(priv, AN8855_RG_GPIO_OE, 0x0); + an8855_reg_write(priv, AN8855_RG_GPIO_CTRL, 0x0); + an8855_reg_write(priv, AN8855_RG_GPIO_L_INV, 0); + + an8855_reg_write(priv, AN8855_RG_GPIO_CTRL, 0x1001); + an8855_reg_read(priv, AN8855_RG_GPIO_DATA, &val); + val |= GENMASK(3, 1); + val &= ~(BIT(0)); + val &= ~(BIT(6)); + an8855_reg_write(priv, AN8855_RG_GPIO_DATA, val); + + an8855_reg_read(priv, AN8855_RG_GPIO_OE, &val); + val |= 0x41; + an8855_reg_write(priv, AN8855_RG_GPIO_OE, val); + + /* Mapping between GPIO & LED */ + val = 0; + for (id = 0; id < ARRAY_SIZE(led_cfg); id++) { + /* Skip GPIO6, due to GPIO6 does not support PORT LED */ + if (id == 6) + continue; + + if (led_cfg[id].en == 1) { + if (id < 7) + val |= led_cfg[id].phy_led_idx << ((id % 4) * 8); + else + val |= led_cfg[id].phy_led_idx << (((id - 1) % 4) * 8); + } + + if (id < 7) + tmp_id = id; + else + tmp_id = id - 1; + + if ((tmp_id % 4) == 0x3) { + an8855_reg_write(priv, + AN8855_RG_GPIO_LED_SEL(tmp_id / 4), + val); + val = 0; + } + } + + /* Turn on LAN LED mode */ + val = 0; + for (id = 0; id < ARRAY_SIZE(led_cfg); id++) { + if (led_cfg[id].en == 1) + val |= 0x1 << id; + } + an8855_reg_write(priv, AN8855_RG_GPIO_LED_MODE, val); + + /* Force clear blink pulse for per port LED */ + an8855_phy_cl45_write(priv, 0, 0x1f, PHY_LED_BLINK_DUR_CTRL, 0x1f); + udelay(1000); + an8855_phy_cl45_write(priv, 0, 0x1f, PHY_LED_BLINK_DUR_CTRL, 0); + + return 0; +} + +static void an8855_port_isolation(struct an8855_switch_priv *priv) +{ + u32 i; + + for (i = 0; i < AN8855_NUM_PORTS; i++) { + /* Set port matrix mode */ + if (i != 5) + an8855_reg_write(priv, AN8855_PORTMATRIX_REG(i), 0x20); + else + an8855_reg_write(priv, AN8855_PORTMATRIX_REG(i), 0x1f); + + /* Set port mode to user port */ + an8855_reg_write(priv, AN8855_PVC(i), + (0x8100 << AN8855_STAG_VPID_S) | + (VLAN_ATTR_USER << AN8855_VLAN_ATTR_S)); + } +} + +static void an8855_mac_control(struct mtk_eth_switch_priv *swpriv, bool enable) +{ + struct an8855_switch_priv *priv = (struct an8855_switch_priv *)swpriv; + u32 pmcr = AN8855_FORCE_MODE_LNK; + + if (enable) + pmcr = AN8855_FORCE_MODE; + + an8855_reg_write(priv, AN8855_PMCR_REG(5), pmcr); +} + +static int an8855_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct an8855_switch_priv *priv = bus->priv; + + if (devad < 0) + return mtk_mii_read(priv->epriv.eth, addr, reg); + + return mtk_mmd_ind_read(priv->epriv.eth, addr, devad, reg); +} + +static int an8855_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct an8855_switch_priv *priv = bus->priv; + + if (devad < 0) + return mtk_mii_write(priv->epriv.eth, addr, reg, val); + + return mtk_mmd_ind_write(priv->epriv.eth, addr, devad, reg, val); +} + +static int an8855_mdio_register(struct an8855_switch_priv *priv) +{ + struct mii_dev *mdio_bus = mdio_alloc(); + int ret; + + if (!mdio_bus) + return -ENOMEM; + + mdio_bus->read = an8855_mdio_read; + mdio_bus->write = an8855_mdio_write; + snprintf(mdio_bus->name, sizeof(mdio_bus->name), priv->epriv.sw->name); + + mdio_bus->priv = priv; + + ret = mdio_register(mdio_bus); + if (ret) { + mdio_free(mdio_bus); + return ret; + } + + priv->mdio_bus = mdio_bus; + + return 0; +} + +static int an8855_setup(struct mtk_eth_switch_priv *swpriv) +{ + struct an8855_switch_priv *priv = (struct an8855_switch_priv *)swpriv; + u16 phy_addr, phy_val; + u32 i, id, val = 0; + int ret; + + priv->phy_base = 1; + + /* Turn off PHYs */ + for (i = 0; i < AN8855_NUM_PHYS; i++) { + phy_addr = AN8855_PHY_ADDR(priv->phy_base, i); + phy_val = mtk_mii_read(priv->epriv.eth, phy_addr, MII_BMCR); + phy_val |= BMCR_PDOWN; + mtk_mii_write(priv->epriv.eth, phy_addr, MII_BMCR, phy_val); + } + + /* Force MAC link down before reset */ + an8855_reg_write(priv, AN8855_PMCR_REG(5), AN8855_FORCE_MODE_LNK); + + /* Switch soft reset */ + an8855_reg_write(priv, AN8855_SYS_CTRL_REG, AN8855_SW_SYS_RST); + mdelay(100); + + an8855_reg_read(priv, AN8855_PKG_SEL, &val); + if ((val & 0x7) == PAG_SEL_AN8855H) { + /* Release power down */ + an8855_reg_write(priv, RG_GPHY_AFE_PWD, 0x0); + + /* Invert for LED activity change */ + an8855_reg_read(priv, AN8855_RG_GPIO_L_INV, &val); + for (id = 0; id < ARRAY_SIZE(led_cfg); id++) { + if (led_cfg[id].pol == LED_HIGH && led_cfg[id].en == 1) + val |= 0x1 << id; + } + an8855_reg_write(priv, AN8855_RG_GPIO_L_INV, (val | 0x1)); + + /* MCU NOP CMD */ + an8855_reg_write(priv, AN8855_RG_GDMP_RAM, 0x846); + an8855_reg_write(priv, AN8855_RG_GDMP_RAM + 4, 0x4a); + + /* Enable MCU */ + an8855_reg_read(priv, AN8855_RG_CLK_CPU_ICG, &val); + an8855_reg_write(priv, AN8855_RG_CLK_CPU_ICG, + val | AN8855_MCU_ENABLE); + udelay(1000); + + /* Disable MCU watchdog */ + an8855_reg_read(priv, AN8855_RG_TIMER_CTL, &val); + an8855_reg_write(priv, AN8855_RG_TIMER_CTL, + (val & (~AN8855_WDOG_ENABLE))); + + /* LED settings for T830 reference board */ + ret = an8855_led_init(priv); + if (ret < 0) { + printf("an8855: an8855_led_init failed with %d\n", ret); + return ret; + } + } + + switch (priv->epriv.phy_interface) { + case PHY_INTERFACE_MODE_2500BASEX: + an8855_port_sgmii_init(priv, 5); + break; + + default: + break; + } + + an8855_reg_read(priv, AN8855_CKGCR, &val); + val &= ~(0x3); + an8855_reg_write(priv, AN8855_CKGCR, val); + + /* Enable port isolation to block inter-port communication */ + an8855_port_isolation(priv); + + /* Turn on PHYs */ + for (i = 0; i < AN8855_NUM_PHYS; i++) { + phy_addr = AN8855_PHY_ADDR(priv->phy_base, i); + phy_val = mtk_mii_read(priv->epriv.eth, phy_addr, MII_BMCR); + phy_val &= ~BMCR_PDOWN; + mtk_mii_write(priv->epriv.eth, phy_addr, MII_BMCR, phy_val); + } + + return an8855_mdio_register(priv); +} + +static int an8855_cleanup(struct mtk_eth_switch_priv *swpriv) +{ + struct an8855_switch_priv *priv = (struct an8855_switch_priv *)swpriv; + + mdio_unregister(priv->mdio_bus); + + return 0; +} + +static int an8855_detect(struct mtk_eth_priv *priv) +{ + int ret; + u32 val; + + ret = __an8855_reg_read(priv, 1, 0x10005000, &val); + if (ret) + return ret; + + if (val == 0x8855) + return 0; + + return -ENODEV; +} + +MTK_ETH_SWITCH(an8855) = { + .name = "an8855", + .desc = "Airoha AN8855", + .priv_size = sizeof(struct an8855_switch_priv), + .reset_wait_time = 100, + + .detect = an8855_detect, + .setup = an8855_setup, + .cleanup = an8855_cleanup, + .mac_control = an8855_mac_control, +}; diff --git a/drivers/net/mtk_eth/mt7530.c b/drivers/net/mtk_eth/mt7530.c new file mode 100644 index 00000000000..3065d35e126 --- /dev/null +++ b/drivers/net/mtk_eth/mt7530.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + * Author: Mark Lee <mark-mc.lee@mediatek.com> + */ + +#include <miiphy.h> +#include <linux/delay.h> +#include <linux/mdio.h> +#include <linux/mii.h> +#include "mtk_eth.h" +#include "mt753x.h" + +#define CHIP_REV 0x7ffc +#define CHIP_NAME_S 16 +#define CHIP_NAME_M 0xffff0000 +#define CHIP_REV_S 0 +#define CHIP_REV_M 0x0f + +static void mt7530_core_reg_write(struct mt753x_switch_priv *priv, u32 reg, + u32 val) +{ + u8 phy_addr = MT753X_PHY_ADDR(priv->phy_base, 0); + + mtk_mmd_ind_write(priv->epriv.eth, phy_addr, 0x1f, reg, val); +} + +static int mt7530_pad_clk_setup(struct mt753x_switch_priv *priv, int mode) +{ + u32 ncpo1, ssc_delta; + + switch (mode) { + case PHY_INTERFACE_MODE_RGMII: + ncpo1 = 0x0c80; + ssc_delta = 0x87; + break; + + default: + printf("error: xMII mode %d is not supported\n", mode); + return -EINVAL; + } + + /* Disable MT7530 core clock */ + mt7530_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, 0); + + /* Disable MT7530 PLL */ + mt7530_core_reg_write(priv, CORE_GSWPLL_GRP1, + (2 << RG_GSWPLL_POSDIV_200M_S) | + (32 << RG_GSWPLL_FBKDIV_200M_S)); + + /* For MT7530 core clock = 500Mhz */ + mt7530_core_reg_write(priv, CORE_GSWPLL_GRP2, + (1 << RG_GSWPLL_POSDIV_500M_S) | + (25 << RG_GSWPLL_FBKDIV_500M_S)); + + /* Enable MT7530 PLL */ + mt7530_core_reg_write(priv, CORE_GSWPLL_GRP1, + (2 << RG_GSWPLL_POSDIV_200M_S) | + (32 << RG_GSWPLL_FBKDIV_200M_S) | + RG_GSWPLL_EN_PRE); + + udelay(20); + + mt7530_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN); + + /* Setup the MT7530 TRGMII Tx Clock */ + mt7530_core_reg_write(priv, CORE_PLL_GROUP5, ncpo1); + mt7530_core_reg_write(priv, CORE_PLL_GROUP6, 0); + mt7530_core_reg_write(priv, CORE_PLL_GROUP10, ssc_delta); + mt7530_core_reg_write(priv, CORE_PLL_GROUP11, ssc_delta); + mt7530_core_reg_write(priv, CORE_PLL_GROUP4, RG_SYSPLL_DDSFBK_EN | + RG_SYSPLL_BIAS_EN | RG_SYSPLL_BIAS_LPF_EN); + + mt7530_core_reg_write(priv, CORE_PLL_GROUP2, + RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN | + (1 << RG_SYSPLL_POSDIV_S)); + + mt7530_core_reg_write(priv, CORE_PLL_GROUP7, + RG_LCDDS_PCW_NCPO_CHG | (3 << RG_LCCDS_C_S) | + RG_LCDDS_PWDB | RG_LCDDS_ISO_EN); + + /* Enable MT7530 core clock */ + mt7530_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, + REG_GSWCK_EN | REG_TRGMIICK_EN); + + return 0; +} + +static void mt7530_mac_control(struct mtk_eth_switch_priv *swpriv, bool enable) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + u32 pmcr = FORCE_MODE; + + if (enable) + pmcr = priv->pmcr; + + mt753x_reg_write(priv, PMCR_REG(6), pmcr); +} + +static int mt7530_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct mt753x_switch_priv *priv = bus->priv; + + if (devad < 0) + return mtk_mii_read(priv->epriv.eth, addr, reg); + + return mtk_mmd_ind_read(priv->epriv.eth, addr, devad, reg); +} + +static int mt7530_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct mt753x_switch_priv *priv = bus->priv; + + if (devad < 0) + return mtk_mii_write(priv->epriv.eth, addr, reg, val); + + return mtk_mmd_ind_write(priv->epriv.eth, addr, devad, reg, val); +} + +static int mt7530_mdio_register(struct mt753x_switch_priv *priv) +{ + struct mii_dev *mdio_bus = mdio_alloc(); + int ret; + + if (!mdio_bus) + return -ENOMEM; + + mdio_bus->read = mt7530_mdio_read; + mdio_bus->write = mt7530_mdio_write; + snprintf(mdio_bus->name, sizeof(mdio_bus->name), priv->epriv.sw->name); + + mdio_bus->priv = priv; + + ret = mdio_register(mdio_bus); + if (ret) { + mdio_free(mdio_bus); + return ret; + } + + priv->mdio_bus = mdio_bus; + + return 0; +} + +static int mt7530_setup(struct mtk_eth_switch_priv *swpriv) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + u16 phy_addr, phy_val; + u32 i, val, txdrv; + + priv->smi_addr = MT753X_DFL_SMI_ADDR; + priv->reg_read = mt753x_mdio_reg_read; + priv->reg_write = mt753x_mdio_reg_write; + + if (!MTK_HAS_CAPS(priv->epriv.soc->caps, MTK_TRGMII_MT7621_CLK)) { + /* Select 250MHz clk for RGMII mode */ + mtk_ethsys_rmw(priv->epriv.eth, ETHSYS_CLKCFG0_REG, + ETHSYS_TRGMII_CLK_SEL362_5, 0); + + txdrv = 8; + } else { + txdrv = 4; + } + + /* Modify HWTRAP first to allow direct access to internal PHYs */ + mt753x_reg_read(priv, HWTRAP_REG, &val); + val |= CHG_TRAP; + val &= ~C_MDIO_BPS; + mt753x_reg_write(priv, MHWTRAP_REG, val); + + /* Calculate the phy base address */ + val = ((val & SMI_ADDR_M) >> SMI_ADDR_S) << 3; + priv->phy_base = (val | 0x7) + 1; + + /* Turn off PHYs */ + for (i = 0; i < MT753X_NUM_PHYS; i++) { + phy_addr = MT753X_PHY_ADDR(priv->phy_base, i); + phy_val = mtk_mii_read(priv->epriv.eth, phy_addr, MII_BMCR); + phy_val |= BMCR_PDOWN; + mtk_mii_write(priv->epriv.eth, phy_addr, MII_BMCR, phy_val); + } + + /* Force MAC link down before reset */ + mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE); + mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE); + + /* MT7530 reset */ + mt753x_reg_write(priv, SYS_CTRL_REG, SW_SYS_RST | SW_REG_RST); + udelay(100); + + val = (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | + MAC_MODE | FORCE_MODE | + MAC_TX_EN | MAC_RX_EN | + BKOFF_EN | BACKPR_EN | + (SPEED_1000M << FORCE_SPD_S) | + FORCE_DPX | FORCE_LINK; + + /* MT7530 Port6: Forced 1000M/FD, FC disabled */ + priv->pmcr = val; + + /* MT7530 Port5: Forced link down */ + mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE); + + /* Keep MAC link down before starting eth */ + mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE); + + /* MT7530 Port6: Set to RGMII */ + mt753x_reg_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_M, P6_INTF_MODE_RGMII); + + /* Hardware Trap: Enable Port6, Disable Port5 */ + mt753x_reg_read(priv, HWTRAP_REG, &val); + val |= CHG_TRAP | LOOPDET_DIS | P5_INTF_DIS | + (P5_INTF_SEL_GMAC5 << P5_INTF_SEL_S) | + (P5_INTF_MODE_RGMII << P5_INTF_MODE_S); + val &= ~(C_MDIO_BPS | P6_INTF_DIS); + mt753x_reg_write(priv, MHWTRAP_REG, val); + + /* Setup switch core pll */ + mt7530_pad_clk_setup(priv, priv->epriv.phy_interface); + + /* Lower Tx Driving for TRGMII path */ + for (i = 0 ; i < NUM_TRGMII_CTRL ; i++) + mt753x_reg_write(priv, MT7530_TRGMII_TD_ODT(i), + (txdrv << TD_DM_DRVP_S) | + (txdrv << TD_DM_DRVN_S)); + + for (i = 0 ; i < NUM_TRGMII_CTRL; i++) + mt753x_reg_rmw(priv, MT7530_TRGMII_RD(i), RD_TAP_M, 16); + + /* Enable port isolation to block inter-port communication */ + mt753x_port_isolation(priv); + + /* Turn on PHYs */ + for (i = 0; i < MT753X_NUM_PHYS; i++) { + phy_addr = MT753X_PHY_ADDR(priv->phy_base, i); + phy_val = mtk_mii_read(priv->epriv.eth, phy_addr, MII_BMCR); + phy_val &= ~BMCR_PDOWN; + mtk_mii_write(priv->epriv.eth, phy_addr, MII_BMCR, phy_val); + } + + return mt7530_mdio_register(priv); +} + +static int mt7530_cleanup(struct mtk_eth_switch_priv *swpriv) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + + mdio_unregister(priv->mdio_bus); + + return 0; +} + +static int mt7530_detect(struct mtk_eth_priv *priv) +{ + int ret; + u32 rev; + + ret = __mt753x_mdio_reg_read(priv, MT753X_DFL_SMI_ADDR, CHIP_REV, &rev); + if (ret) + return ret; + + if (((rev & CHIP_NAME_M) >> CHIP_NAME_S) == 0x7530) + return 0; + + return -ENODEV; +} + +MTK_ETH_SWITCH(mt7530) = { + .name = "mt7530", + .desc = "MediaTek MT7530", + .priv_size = sizeof(struct mt753x_switch_priv), + .reset_wait_time = 1000, + + .detect = mt7530_detect, + .setup = mt7530_setup, + .cleanup = mt7530_cleanup, + .mac_control = mt7530_mac_control, +}; diff --git a/drivers/net/mtk_eth/mt7531.c b/drivers/net/mtk_eth/mt7531.c new file mode 100644 index 00000000000..32d6bebbbdb --- /dev/null +++ b/drivers/net/mtk_eth/mt7531.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + * Author: Mark Lee <mark-mc.lee@mediatek.com> + */ + +#include <miiphy.h> +#include <linux/delay.h> +#include <linux/mdio.h> +#include <linux/mii.h> +#include "mtk_eth.h" +#include "mt753x.h" + +#define CHIP_REV 0x781C +#define CHIP_NAME_S 16 +#define CHIP_NAME_M 0xffff0000 +#define CHIP_REV_S 0 +#define CHIP_REV_M 0x0f +#define CHIP_REV_E1 0x0 + +static int mt7531_core_reg_read(struct mt753x_switch_priv *priv, u32 reg) +{ + u8 phy_addr = MT753X_PHY_ADDR(priv->phy_base, 0); + + return mt7531_mmd_read(priv, phy_addr, 0x1f, reg); +} + +static void mt7531_core_reg_write(struct mt753x_switch_priv *priv, u32 reg, + u32 val) +{ + u8 phy_addr = MT753X_PHY_ADDR(priv->phy_base, 0); + + mt7531_mmd_write(priv, phy_addr, 0x1f, reg, val); +} + +static void mt7531_core_pll_setup(struct mt753x_switch_priv *priv) +{ + /* Step 1 : Disable MT7531 COREPLL */ + mt753x_reg_rmw(priv, MT7531_PLLGP_EN, EN_COREPLL, 0); + + /* Step 2: switch to XTAL output */ + mt753x_reg_rmw(priv, MT7531_PLLGP_EN, SW_CLKSW, SW_CLKSW); + + mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_EN, 0); + + /* Step 3: disable PLLGP and enable program PLLGP */ + mt753x_reg_rmw(priv, MT7531_PLLGP_EN, SW_PLLGP, SW_PLLGP); + + /* Step 4: program COREPLL output frequency to 500MHz */ + mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_POSDIV_M, + 2 << RG_COREPLL_POSDIV_S); + udelay(25); + + /* Currently, support XTAL 25Mhz only */ + mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_M, + 0x140000 << RG_COREPLL_SDM_PCW_S); + + /* Set feedback divide ratio update signal to high */ + mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_CHG, + RG_COREPLL_SDM_PCW_CHG); + + /* Wait for at least 16 XTAL clocks */ + udelay(10); + + /* Step 5: set feedback divide ratio update signal to low */ + mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_CHG, 0); + + /* add enable 325M clock for SGMII */ + mt753x_reg_write(priv, MT7531_ANA_PLLGP_CR5, 0xad0000); + + /* add enable 250SSC clock for RGMII */ + mt753x_reg_write(priv, MT7531_ANA_PLLGP_CR2, 0x4f40000); + + /*Step 6: Enable MT7531 PLL */ + mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_EN, RG_COREPLL_EN); + + mt753x_reg_rmw(priv, MT7531_PLLGP_EN, EN_COREPLL, EN_COREPLL); + + udelay(25); +} + +static int mt7531_port_sgmii_init(struct mt753x_switch_priv *priv, u32 port) +{ + if (port != 5 && port != 6) { + printf("mt7531: port %d is not a SGMII port\n", port); + return -EINVAL; + } + + /* Set SGMII GEN2 speed(2.5G) */ + mt753x_reg_rmw(priv, MT7531_PHYA_CTRL_SIGNAL3(port), SGMSYS_SPEED_MASK, + FIELD_PREP(SGMSYS_SPEED_MASK, SGMSYS_SPEED_2500)); + + /* Disable SGMII AN */ + mt753x_reg_rmw(priv, MT7531_PCS_CONTROL_1(port), + SGMII_AN_ENABLE, 0); + + /* SGMII force mode setting */ + mt753x_reg_write(priv, MT7531_SGMII_MODE(port), SGMII_FORCE_MODE); + + /* Release PHYA power down state */ + mt753x_reg_rmw(priv, MT7531_QPHY_PWR_STATE_CTRL(port), + SGMII_PHYA_PWD, 0); + + return 0; +} + +static int mt7531_port_rgmii_init(struct mt753x_switch_priv *priv, u32 port) +{ + u32 val; + + if (port != 5) { + printf("error: RGMII mode is not available for port %d\n", + port); + return -EINVAL; + } + + mt753x_reg_read(priv, MT7531_CLKGEN_CTRL, &val); + val |= GP_CLK_EN; + val &= ~GP_MODE_M; + val |= GP_MODE_RGMII << GP_MODE_S; + val |= TXCLK_NO_REVERSE; + val |= RXCLK_NO_DELAY; + val &= ~CLK_SKEW_IN_M; + val |= CLK_SKEW_IN_NO_CHANGE << CLK_SKEW_IN_S; + val &= ~CLK_SKEW_OUT_M; + val |= CLK_SKEW_OUT_NO_CHANGE << CLK_SKEW_OUT_S; + mt753x_reg_write(priv, MT7531_CLKGEN_CTRL, val); + + return 0; +} + +static void mt7531_phy_setting(struct mt753x_switch_priv *priv) +{ + int i; + u32 val; + + for (i = 0; i < MT753X_NUM_PHYS; i++) { + /* Enable HW auto downshift */ + mt7531_mii_write(priv, i, 0x1f, 0x1); + val = mt7531_mii_read(priv, i, PHY_EXT_REG_14); + val |= PHY_EN_DOWN_SHFIT; + mt7531_mii_write(priv, i, PHY_EXT_REG_14, val); + + /* PHY link down power saving enable */ + val = mt7531_mii_read(priv, i, PHY_EXT_REG_17); + val |= PHY_LINKDOWN_POWER_SAVING_EN; + mt7531_mii_write(priv, i, PHY_EXT_REG_17, val); + + val = mt7531_mmd_read(priv, i, 0x1e, PHY_DEV1E_REG_0C6); + val &= ~PHY_POWER_SAVING_M; + val |= PHY_POWER_SAVING_TX << PHY_POWER_SAVING_S; + mt7531_mmd_write(priv, i, 0x1e, PHY_DEV1E_REG_0C6, val); + } +} + +static void mt7531_mac_control(struct mtk_eth_switch_priv *swpriv, bool enable) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + u32 pmcr = FORCE_MODE_LNK; + + if (enable) + pmcr = priv->pmcr; + + mt753x_reg_write(priv, PMCR_REG(5), pmcr); + mt753x_reg_write(priv, PMCR_REG(6), pmcr); +} + +static int mt7531_setup(struct mtk_eth_switch_priv *swpriv) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + u32 i, val, pmcr, port5_sgmii; + u16 phy_addr, phy_val; + + priv->smi_addr = MT753X_DFL_SMI_ADDR; + priv->phy_base = (priv->smi_addr + 1) & MT753X_SMI_ADDR_MASK; + priv->reg_read = mt753x_mdio_reg_read; + priv->reg_write = mt753x_mdio_reg_write; + + /* Turn off PHYs */ + for (i = 0; i < MT753X_NUM_PHYS; i++) { + phy_addr = MT753X_PHY_ADDR(priv->phy_base, i); + phy_val = mt7531_mii_read(priv, phy_addr, MII_BMCR); + phy_val |= BMCR_PDOWN; + mt7531_mii_write(priv, phy_addr, MII_BMCR, phy_val); + } + + /* Force MAC link down before reset */ + mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE_LNK); + mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK); + + /* Switch soft reset */ + mt753x_reg_write(priv, SYS_CTRL_REG, SW_SYS_RST | SW_REG_RST); + udelay(100); + + /* Enable MDC input Schmitt Trigger */ + mt753x_reg_rmw(priv, MT7531_SMT0_IOLB, SMT_IOLB_5_SMI_MDC_EN, + SMT_IOLB_5_SMI_MDC_EN); + + mt7531_core_pll_setup(priv); + + mt753x_reg_read(priv, MT7531_TOP_SIG_SR, &val); + port5_sgmii = !!(val & PAD_DUAL_SGMII_EN); + + /* port5 support either RGMII or SGMII, port6 only support SGMII. */ + switch (priv->epriv.phy_interface) { + case PHY_INTERFACE_MODE_RGMII: + if (!port5_sgmii) + mt7531_port_rgmii_init(priv, 5); + break; + + case PHY_INTERFACE_MODE_2500BASEX: + mt7531_port_sgmii_init(priv, 6); + if (port5_sgmii) + mt7531_port_sgmii_init(priv, 5); + break; + + default: + break; + } + + pmcr = MT7531_FORCE_MODE | + (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | + MAC_MODE | MAC_TX_EN | MAC_RX_EN | + BKOFF_EN | BACKPR_EN | + FORCE_RX_FC | FORCE_TX_FC | + (SPEED_1000M << FORCE_SPD_S) | FORCE_DPX | + FORCE_LINK; + + priv->pmcr = pmcr; + + /* Keep MAC link down before starting eth */ + mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE_LNK); + mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK); + + /* Enable port isolation to block inter-port communication */ + mt753x_port_isolation(priv); + + /* Turn on PHYs */ + for (i = 0; i < MT753X_NUM_PHYS; i++) { + phy_addr = MT753X_PHY_ADDR(priv->phy_base, i); + phy_val = mt7531_mii_read(priv, phy_addr, MII_BMCR); + phy_val &= ~BMCR_PDOWN; + mt7531_mii_write(priv, phy_addr, MII_BMCR, phy_val); + } + + mt7531_phy_setting(priv); + + /* Enable Internal PHYs */ + val = mt7531_core_reg_read(priv, CORE_PLL_GROUP4); + val |= MT7531_BYPASS_MODE; + val &= ~MT7531_POWER_ON_OFF; + mt7531_core_reg_write(priv, CORE_PLL_GROUP4, val); + + return mt7531_mdio_register(priv); +} + +static int mt7531_cleanup(struct mtk_eth_switch_priv *swpriv) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + + mdio_unregister(priv->mdio_bus); + + return 0; +} + +static int mt7531_detect(struct mtk_eth_priv *priv) +{ + int ret; + u32 rev; + + ret = __mt753x_mdio_reg_read(priv, MT753X_DFL_SMI_ADDR, CHIP_REV, &rev); + if (ret) + return ret; + + if (((rev & CHIP_NAME_M) >> CHIP_NAME_S) == 0x7531) + return 0; + + return -ENODEV; +} + +MTK_ETH_SWITCH(mt7531) = { + .name = "mt7531", + .desc = "MediaTek MT7531", + .priv_size = sizeof(struct mt753x_switch_priv), + .reset_wait_time = 200, + + .detect = mt7531_detect, + .setup = mt7531_setup, + .cleanup = mt7531_cleanup, + .mac_control = mt7531_mac_control, +}; diff --git a/drivers/net/mtk_eth/mt753x.c b/drivers/net/mtk_eth/mt753x.c new file mode 100644 index 00000000000..cdd52f3ff1b --- /dev/null +++ b/drivers/net/mtk_eth/mt753x.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + * Author: Mark Lee <mark-mc.lee@mediatek.com> + */ + +#include <errno.h> +#include <time.h> +#include "mtk_eth.h" +#include "mt753x.h" + +/* + * MT753x Internal Register Address Bits + * ------------------------------------------------------------------- + * | 15 14 13 12 11 10 9 8 7 6 | 5 4 3 2 | 1 0 | + * |----------------------------------------|---------------|--------| + * | Page Address | Reg Address | Unused | + * ------------------------------------------------------------------- + */ + +int __mt753x_mdio_reg_read(struct mtk_eth_priv *priv, u32 smi_addr, u32 reg, + u32 *data) +{ + int ret, low_word, high_word; + + /* Write page address */ + ret = mtk_mii_write(priv, smi_addr, 0x1f, reg >> 6); + if (ret) + return ret; + + /* Read low word */ + low_word = mtk_mii_read(priv, smi_addr, (reg >> 2) & 0xf); + if (low_word < 0) + return low_word; + + /* Read high word */ + high_word = mtk_mii_read(priv, smi_addr, 0x10); + if (high_word < 0) + return high_word; + + if (data) + *data = ((u32)high_word << 16) | (low_word & 0xffff); + + return 0; +} + +int mt753x_mdio_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data) +{ + return __mt753x_mdio_reg_read(priv->epriv.eth, priv->smi_addr, reg, + data); +} + +int mt753x_mdio_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data) +{ + int ret; + + /* Write page address */ + ret = mtk_mii_write(priv->epriv.eth, priv->smi_addr, 0x1f, reg >> 6); + if (ret) + return ret; + + /* Write low word */ + ret = mtk_mii_write(priv->epriv.eth, priv->smi_addr, (reg >> 2) & 0xf, + data & 0xffff); + if (ret) + return ret; + + /* Write high word */ + return mtk_mii_write(priv->epriv.eth, priv->smi_addr, 0x10, data >> 16); +} + +int mt753x_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data) +{ + return priv->reg_read(priv, reg, data); +} + +int mt753x_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data) +{ + return priv->reg_write(priv, reg, data); +} + +void mt753x_reg_rmw(struct mt753x_switch_priv *priv, u32 reg, u32 clr, u32 set) +{ + u32 val; + + priv->reg_read(priv, reg, &val); + val &= ~clr; + val |= set; + priv->reg_write(priv, reg, val); +} + +/* Indirect MDIO clause 22/45 access */ +static int mt7531_mii_rw(struct mt753x_switch_priv *priv, int phy, int reg, + u16 data, u32 cmd, u32 st) +{ + u32 val, timeout_ms; + ulong timeout; + int ret = 0; + + val = (st << MDIO_ST_S) | + ((cmd << MDIO_CMD_S) & MDIO_CMD_M) | + ((phy << MDIO_PHY_ADDR_S) & MDIO_PHY_ADDR_M) | + ((reg << MDIO_REG_ADDR_S) & MDIO_REG_ADDR_M); + + if (cmd == MDIO_CMD_WRITE || cmd == MDIO_CMD_ADDR) + val |= data & MDIO_RW_DATA_M; + + mt753x_reg_write(priv, MT7531_PHY_IAC, val | PHY_ACS_ST); + + timeout_ms = 100; + timeout = get_timer(0); + while (1) { + mt753x_reg_read(priv, MT7531_PHY_IAC, &val); + + if ((val & PHY_ACS_ST) == 0) + break; + + if (get_timer(timeout) > timeout_ms) + return -ETIMEDOUT; + } + + if (cmd == MDIO_CMD_READ || cmd == MDIO_CMD_READ_C45) { + mt753x_reg_read(priv, MT7531_PHY_IAC, &val); + ret = val & MDIO_RW_DATA_M; + } + + return ret; +} + +int mt7531_mii_read(struct mt753x_switch_priv *priv, u8 phy, u8 reg) +{ + u8 phy_addr; + + if (phy >= MT753X_NUM_PHYS) + return -EINVAL; + + phy_addr = MT753X_PHY_ADDR(priv->phy_base, phy); + + return mt7531_mii_rw(priv, phy_addr, reg, 0, MDIO_CMD_READ, + MDIO_ST_C22); +} + +int mt7531_mii_write(struct mt753x_switch_priv *priv, u8 phy, u8 reg, u16 val) +{ + u8 phy_addr; + + if (phy >= MT753X_NUM_PHYS) + return -EINVAL; + + phy_addr = MT753X_PHY_ADDR(priv->phy_base, phy); + + return mt7531_mii_rw(priv, phy_addr, reg, val, MDIO_CMD_WRITE, + MDIO_ST_C22); +} + +int mt7531_mmd_read(struct mt753x_switch_priv *priv, u8 addr, u8 devad, + u16 reg) +{ + u8 phy_addr; + int ret; + + if (addr >= MT753X_NUM_PHYS) + return -EINVAL; + + phy_addr = MT753X_PHY_ADDR(priv->phy_base, addr); + + ret = mt7531_mii_rw(priv, phy_addr, devad, reg, MDIO_CMD_ADDR, + MDIO_ST_C45); + if (ret) + return ret; + + return mt7531_mii_rw(priv, phy_addr, devad, 0, MDIO_CMD_READ_C45, + MDIO_ST_C45); +} + +int mt7531_mmd_write(struct mt753x_switch_priv *priv, u8 addr, u8 devad, + u16 reg, u16 val) +{ + u8 phy_addr; + int ret; + + if (addr >= MT753X_NUM_PHYS) + return 0; + + phy_addr = MT753X_PHY_ADDR(priv->phy_base, addr); + + ret = mt7531_mii_rw(priv, phy_addr, devad, reg, MDIO_CMD_ADDR, + MDIO_ST_C45); + if (ret) + return ret; + + return mt7531_mii_rw(priv, phy_addr, devad, val, MDIO_CMD_WRITE, + MDIO_ST_C45); +} + +static int mt7531_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct mt753x_switch_priv *priv = bus->priv; + + if (devad < 0) + return mt7531_mii_read(priv, addr, reg); + + return mt7531_mmd_read(priv, addr, devad, reg); +} + +static int mt7531_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct mt753x_switch_priv *priv = bus->priv; + + if (devad < 0) + return mt7531_mii_write(priv, addr, reg, val); + + return mt7531_mmd_write(priv, addr, devad, reg, val); +} + +int mt7531_mdio_register(struct mt753x_switch_priv *priv) +{ + struct mii_dev *mdio_bus = mdio_alloc(); + int ret; + + if (!mdio_bus) + return -ENOMEM; + + mdio_bus->read = mt7531_mdio_read; + mdio_bus->write = mt7531_mdio_write; + snprintf(mdio_bus->name, sizeof(mdio_bus->name), priv->epriv.sw->name); + + mdio_bus->priv = priv; + + ret = mdio_register(mdio_bus); + if (ret) { + mdio_free(mdio_bus); + return ret; + } + + priv->mdio_bus = mdio_bus; + + return 0; +} + +void mt753x_port_isolation(struct mt753x_switch_priv *priv) +{ + u32 i; + + for (i = 0; i < MT753X_NUM_PORTS; i++) { + /* Set port matrix mode */ + if (i != 6) + mt753x_reg_write(priv, PCR_REG(i), + (0x40 << PORT_MATRIX_S)); + else + mt753x_reg_write(priv, PCR_REG(i), + (0x3f << PORT_MATRIX_S)); + + /* Set port mode to user port */ + mt753x_reg_write(priv, PVC_REG(i), + (0x8100 << STAG_VPID_S) | + (VLAN_ATTR_USER << VLAN_ATTR_S)); + } +} diff --git a/drivers/net/mtk_eth/mt753x.h b/drivers/net/mtk_eth/mt753x.h new file mode 100644 index 00000000000..65046a1421c --- /dev/null +++ b/drivers/net/mtk_eth/mt753x.h @@ -0,0 +1,286 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + * Author: Mark Lee <mark-mc.lee@mediatek.com> + */ + +#ifndef _MTK_ETH_MT753X_H_ +#define _MTK_ETH_MT753X_H_ + +#include <phy.h> +#include <miiphy.h> +#include <linux/bitops.h> +#include <linux/bitfield.h> + +struct mtk_eth_priv; + +#define MT753X_NUM_PHYS 5 +#define MT753X_NUM_PORTS 7 +#define MT753X_DFL_SMI_ADDR 31 +#define MT753X_SMI_ADDR_MASK 0x1f + +#define MT753X_PHY_ADDR(base, addr) \ + (((base) + (addr)) & 0x1f) + +/* MT7530 Registers */ +#define PCR_REG(p) (0x2004 + (p) * 0x100) +#define PORT_MATRIX_S 16 +#define PORT_MATRIX_M 0xff0000 + +#define PVC_REG(p) (0x2010 + (p) * 0x100) +#define STAG_VPID_S 16 +#define STAG_VPID_M 0xffff0000 +#define VLAN_ATTR_S 6 +#define VLAN_ATTR_M 0xc0 + +/* VLAN_ATTR: VLAN attributes */ +#define VLAN_ATTR_USER 0 +#define VLAN_ATTR_STACK 1 +#define VLAN_ATTR_TRANSLATION 2 +#define VLAN_ATTR_TRANSPARENT 3 + +#define PMCR_REG(p) (0x3000 + (p) * 0x100) +/* XXX: all fields of MT7530 are defined under GMAC_PORT_MCR + * MT7531 specific fields are defined below + */ +#define FORCE_MODE_EEE1G BIT(25) +#define FORCE_MODE_EEE100 BIT(26) +#define FORCE_MODE_TX_FC BIT(27) +#define FORCE_MODE_RX_FC BIT(28) +#define FORCE_MODE_DPX BIT(29) +#define FORCE_MODE_SPD BIT(30) +#define FORCE_MODE_LNK BIT(31) +#define MT7531_FORCE_MODE FORCE_MODE_TX_FC | FORCE_MODE_RX_FC | \ + FORCE_MODE_DPX | FORCE_MODE_SPD | \ + FORCE_MODE_LNK +#define MT7988_FORCE_MODE FORCE_MODE_TX_FC | FORCE_MODE_RX_FC | \ + FORCE_MODE_DPX | FORCE_MODE_SPD | \ + FORCE_MODE_LNK + +/* MT7531 SGMII Registers */ +#define MT7531_SGMII_REG_BASE 0x5000 +#define MT7531_SGMII_REG_PORT_BASE 0x1000 +#define MT7531_SGMII_REG(p, r) (MT7531_SGMII_REG_BASE + \ + (p) * MT7531_SGMII_REG_PORT_BASE + (r)) +#define MT7531_PCS_CONTROL_1(p) MT7531_SGMII_REG(((p) - 5), 0x00) +#define MT7531_SGMII_MODE(p) MT7531_SGMII_REG(((p) - 5), 0x20) +#define MT7531_QPHY_PWR_STATE_CTRL(p) MT7531_SGMII_REG(((p) - 5), 0xe8) +#define MT7531_PHYA_CTRL_SIGNAL3(p) MT7531_SGMII_REG(((p) - 5), 0x128) +#define MT7531_PHYA_ANA_SYSPLL(p) MT7531_SGMII_REG(((p) - 5), 0x158) +/* XXX: all fields of MT7531 SGMII are defined under SGMSYS */ + +/* MT753x System Control Register */ +#define SYS_CTRL_REG 0x7000 +#define SW_PHY_RST BIT(2) +#define SW_SYS_RST BIT(1) +#define SW_REG_RST BIT(0) + +/* MT7531 */ +#define MT7531_PHY_IAC 0x701c +/* XXX: all fields are defined under GMAC_PIAC_REG */ + +#define MT7531_CLKGEN_CTRL 0x7500 +#define CLK_SKEW_OUT_S 8 +#define CLK_SKEW_OUT_M 0x300 +#define CLK_SKEW_IN_S 6 +#define CLK_SKEW_IN_M 0xc0 +#define RXCLK_NO_DELAY BIT(5) +#define TXCLK_NO_REVERSE BIT(4) +#define GP_MODE_S 1 +#define GP_MODE_M 0x06 +#define GP_CLK_EN BIT(0) + +/* Values of GP_MODE */ +#define GP_MODE_RGMII 0 +#define GP_MODE_MII 1 +#define GP_MODE_REV_MII 2 + +/* Values of CLK_SKEW_IN */ +#define CLK_SKEW_IN_NO_CHANGE 0 +#define CLK_SKEW_IN_DELAY_100PPS 1 +#define CLK_SKEW_IN_DELAY_200PPS 2 +#define CLK_SKEW_IN_REVERSE 3 + +/* Values of CLK_SKEW_OUT */ +#define CLK_SKEW_OUT_NO_CHANGE 0 +#define CLK_SKEW_OUT_DELAY_100PPS 1 +#define CLK_SKEW_OUT_DELAY_200PPS 2 +#define CLK_SKEW_OUT_REVERSE 3 + +#define HWTRAP_REG 0x7800 +/* MT7530 Modified Hardware Trap Status Registers */ +#define MHWTRAP_REG 0x7804 +#define CHG_TRAP BIT(16) +#define LOOPDET_DIS BIT(14) +#define P5_INTF_SEL_S 13 +#define P5_INTF_SEL_M 0x2000 +#define SMI_ADDR_S 11 +#define SMI_ADDR_M 0x1800 +#define XTAL_FSEL_S 9 +#define XTAL_FSEL_M 0x600 +#define P6_INTF_DIS BIT(8) +#define P5_INTF_MODE_S 7 +#define P5_INTF_MODE_M 0x80 +#define P5_INTF_DIS BIT(6) +#define C_MDIO_BPS BIT(5) +#define CHIP_MODE_S 0 +#define CHIP_MODE_M 0x0f + +/* P5_INTF_SEL: Interface type of Port5 */ +#define P5_INTF_SEL_GPHY 0 +#define P5_INTF_SEL_GMAC5 1 + +/* P5_INTF_MODE: Interface mode of Port5 */ +#define P5_INTF_MODE_GMII_MII 0 +#define P5_INTF_MODE_RGMII 1 + +#define MT7530_P6ECR 0x7830 +#define P6_INTF_MODE_M 0x3 +#define P6_INTF_MODE_S 0 + +/* P6_INTF_MODE: Interface mode of Port6 */ +#define P6_INTF_MODE_RGMII 0 +#define P6_INTF_MODE_TRGMII 1 + +#define MT7530_TRGMII_RD(n) (0x7a10 + (n) * 8) +#define RD_TAP_S 0 +#define RD_TAP_M 0x7f + +#define MT7530_TRGMII_TD_ODT(n) (0x7a54 + (n) * 8) +/* XXX: all fields are defined under GMAC_TRGMII_TD_ODT */ + +/* TOP Signals Status Register */ +#define MT7531_TOP_SIG_SR 0x780c +#define PAD_MCM_SMI_EN BIT(0) +#define PAD_DUAL_SGMII_EN BIT(1) + +/* MT7531 PLLGP Registers */ +#define MT7531_PLLGP_EN 0x7820 +#define EN_COREPLL BIT(2) +#define SW_CLKSW BIT(1) +#define SW_PLLGP BIT(0) + +#define MT7531_PLLGP_CR0 0x78a8 +#define RG_COREPLL_EN BIT(22) +#define RG_COREPLL_POSDIV_S 23 +#define RG_COREPLL_POSDIV_M 0x3800000 +#define RG_COREPLL_SDM_PCW_S 1 +#define RG_COREPLL_SDM_PCW_M 0x3ffffe +#define RG_COREPLL_SDM_PCW_CHG BIT(0) + +/* MT7531 RGMII and SGMII PLL clock */ +#define MT7531_ANA_PLLGP_CR2 0x78b0 +#define MT7531_ANA_PLLGP_CR5 0x78bc + +/* MT7531 GPIO GROUP IOLB SMT0 Control */ +#define MT7531_SMT0_IOLB 0x7f04 +#define SMT_IOLB_5_SMI_MDC_EN BIT(5) + +/* MT7530 GPHY MDIO MMD Registers */ +#define CORE_PLL_GROUP2 0x401 +#define RG_SYSPLL_EN_NORMAL BIT(15) +#define RG_SYSPLL_VODEN BIT(14) +#define RG_SYSPLL_POSDIV_S 5 +#define RG_SYSPLL_POSDIV_M 0x60 + +#define CORE_PLL_GROUP4 0x403 +#define MT7531_BYPASS_MODE BIT(4) +#define MT7531_POWER_ON_OFF BIT(5) +#define RG_SYSPLL_DDSFBK_EN BIT(12) +#define RG_SYSPLL_BIAS_EN BIT(11) +#define RG_SYSPLL_BIAS_LPF_EN BIT(10) + +#define CORE_PLL_GROUP5 0x404 +#define RG_LCDDS_PCW_NCPO1_S 0 +#define RG_LCDDS_PCW_NCPO1_M 0xffff + +#define CORE_PLL_GROUP6 0x405 +#define RG_LCDDS_PCW_NCPO0_S 0 +#define RG_LCDDS_PCW_NCPO0_M 0xffff + +#define CORE_PLL_GROUP7 0x406 +#define RG_LCDDS_PWDB BIT(15) +#define RG_LCDDS_ISO_EN BIT(13) +#define RG_LCCDS_C_S 4 +#define RG_LCCDS_C_M 0x70 +#define RG_LCDDS_PCW_NCPO_CHG BIT(3) + +#define CORE_PLL_GROUP10 0x409 +#define RG_LCDDS_SSC_DELTA_S 0 +#define RG_LCDDS_SSC_DELTA_M 0xfff + +#define CORE_PLL_GROUP11 0x40a +#define RG_LCDDS_SSC_DELTA1_S 0 +#define RG_LCDDS_SSC_DELTA1_M 0xfff + +#define CORE_GSWPLL_GRP1 0x40d +#define RG_GSWPLL_POSDIV_200M_S 12 +#define RG_GSWPLL_POSDIV_200M_M 0x3000 +#define RG_GSWPLL_EN_PRE BIT(11) +#define RG_GSWPLL_FBKDIV_200M_S 0 +#define RG_GSWPLL_FBKDIV_200M_M 0xff + +#define CORE_GSWPLL_GRP2 0x40e +#define RG_GSWPLL_POSDIV_500M_S 8 +#define RG_GSWPLL_POSDIV_500M_M 0x300 +#define RG_GSWPLL_FBKDIV_500M_S 0 +#define RG_GSWPLL_FBKDIV_500M_M 0xff + +#define CORE_TRGMII_GSW_CLK_CG 0x410 +#define REG_GSWCK_EN BIT(0) +#define REG_TRGMIICK_EN BIT(1) + +/* Extend PHY Control Register 3 */ +#define PHY_EXT_REG_14 0x14 + +/* Fields of PHY_EXT_REG_14 */ +#define PHY_EN_DOWN_SHFIT BIT(4) + +/* Extend PHY Control Register 4 */ +#define PHY_EXT_REG_17 0x17 + +/* Fields of PHY_EXT_REG_17 */ +#define PHY_LINKDOWN_POWER_SAVING_EN BIT(4) + +/* PHY RXADC Control Register 7 */ +#define PHY_DEV1E_REG_0C6 0x0c6 + +/* Fields of PHY_DEV1E_REG_0C6 */ +#define PHY_POWER_SAVING_S 8 +#define PHY_POWER_SAVING_M 0x300 +#define PHY_POWER_SAVING_TX 0x0 + +struct mt753x_switch_priv { + struct mtk_eth_switch_priv epriv; + struct mii_dev *mdio_bus; + u32 smi_addr; + u32 phy_base; + u32 pmcr; + + int (*reg_read)(struct mt753x_switch_priv *priv, u32 reg, u32 *data); + int (*reg_write)(struct mt753x_switch_priv *priv, u32 reg, u32 data); +}; + +int __mt753x_mdio_reg_read(struct mtk_eth_priv *priv, u32 smi_addr, u32 reg, + u32 *data); +int mt753x_mdio_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data); +int mt753x_mdio_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data); + +int mt753x_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data); +int mt753x_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data); +void mt753x_reg_rmw(struct mt753x_switch_priv *priv, u32 reg, u32 clr, u32 set); + +int mt7531_mii_read(struct mt753x_switch_priv *priv, u8 phy, u8 reg); +int mt7531_mii_write(struct mt753x_switch_priv *priv, u8 phy, u8 reg, u16 val); +int mt7531_mmd_read(struct mt753x_switch_priv *priv, u8 addr, u8 devad, + u16 reg); +int mt7531_mmd_write(struct mt753x_switch_priv *priv, u8 addr, u8 devad, + u16 reg, u16 val); + +int mt7531_mdio_register(struct mt753x_switch_priv *priv); + +void mt753x_port_isolation(struct mt753x_switch_priv *priv); + +#endif /* _MTK_ETH_MT753X_H_ */ diff --git a/drivers/net/mtk_eth/mt7988.c b/drivers/net/mtk_eth/mt7988.c new file mode 100644 index 00000000000..a416d87840c --- /dev/null +++ b/drivers/net/mtk_eth/mt7988.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + * Author: Mark Lee <mark-mc.lee@mediatek.com> + */ + +#include <miiphy.h> +#include <linux/delay.h> +#include <linux/mdio.h> +#include <linux/mii.h> +#include <linux/io.h> +#include "mtk_eth.h" +#include "mt753x.h" + +static int mt7988_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data) +{ + *data = readl(priv->epriv.ethsys_base + GSW_BASE + reg); + + return 0; +} + +static int mt7988_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data) +{ + writel(data, priv->epriv.ethsys_base + GSW_BASE + reg); + + return 0; +} + +static void mt7988_phy_setting(struct mt753x_switch_priv *priv) +{ + u16 val; + u32 i; + + for (i = 0; i < MT753X_NUM_PHYS; i++) { + /* Enable HW auto downshift */ + mt7531_mii_write(priv, i, 0x1f, 0x1); + val = mt7531_mii_read(priv, i, PHY_EXT_REG_14); + val |= PHY_EN_DOWN_SHFIT; + mt7531_mii_write(priv, i, PHY_EXT_REG_14, val); + + /* PHY link down power saving enable */ + val = mt7531_mii_read(priv, i, PHY_EXT_REG_17); + val |= PHY_LINKDOWN_POWER_SAVING_EN; + mt7531_mii_write(priv, i, PHY_EXT_REG_17, val); + } +} + +static void mt7988_mac_control(struct mtk_eth_switch_priv *swpriv, bool enable) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + u32 pmcr = FORCE_MODE_LNK; + + if (enable) + pmcr = priv->pmcr; + + mt7988_reg_write(priv, PMCR_REG(6), pmcr); +} + +static int mt7988_setup(struct mtk_eth_switch_priv *swpriv) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + u16 phy_addr, phy_val; + u32 pmcr; + int i; + + priv->smi_addr = MT753X_DFL_SMI_ADDR; + priv->phy_base = (priv->smi_addr + 1) & MT753X_SMI_ADDR_MASK; + priv->reg_read = mt7988_reg_read; + priv->reg_write = mt7988_reg_write; + + /* Turn off PHYs */ + for (i = 0; i < MT753X_NUM_PHYS; i++) { + phy_addr = MT753X_PHY_ADDR(priv->phy_base, i); + phy_val = mt7531_mii_read(priv, phy_addr, MII_BMCR); + phy_val |= BMCR_PDOWN; + mt7531_mii_write(priv, phy_addr, MII_BMCR, phy_val); + } + + switch (priv->epriv.phy_interface) { + case PHY_INTERFACE_MODE_USXGMII: + /* Use CPU bridge instead of actual USXGMII path */ + + /* Disable GDM1 RX CRC stripping */ + /* mtk_fe_rmw(priv, 0x500, BIT(16), 0); */ + + /* Set GDM1 no drop */ + mtk_fe_rmw(priv->epriv.eth, PSE_NO_DROP_CFG_REG, 0, + PSE_NO_DROP_GDM1); + + /* Enable GSW CPU bridge as USXGMII */ + /* mtk_fe_rmw(priv, 0x504, BIT(31), BIT(31)); */ + + /* Enable GDM1 to GSW CPU bridge */ + mtk_gmac_rmw(priv->epriv.eth, GMAC_MAC_MISC_REG, 0, BIT(0)); + + /* XGMAC force link up */ + mtk_gmac_rmw(priv->epriv.eth, GMAC_XGMAC_STS_REG, 0, + P1_XGMAC_FORCE_LINK); + + /* Setup GSW CPU bridge IPG */ + mtk_gmac_rmw(priv->epriv.eth, GMAC_GSW_CFG_REG, + GSWTX_IPG_M | GSWRX_IPG_M, + (0xB << GSWTX_IPG_S) | (0xB << GSWRX_IPG_S)); + break; + default: + printf("Error: MT7988 GSW does not support %s interface\n", + phy_string_for_interface(priv->epriv.phy_interface)); + break; + } + + pmcr = MT7988_FORCE_MODE | + (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | + MAC_MODE | MAC_TX_EN | MAC_RX_EN | + BKOFF_EN | BACKPR_EN | + FORCE_RX_FC | FORCE_TX_FC | + (SPEED_1000M << FORCE_SPD_S) | FORCE_DPX | + FORCE_LINK; + + priv->pmcr = pmcr; + + /* Keep MAC link down before starting eth */ + mt7988_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK); + + /* Enable port isolation to block inter-port communication */ + mt753x_port_isolation(priv); + + /* Turn on PHYs */ + for (i = 0; i < MT753X_NUM_PHYS; i++) { + phy_addr = MT753X_PHY_ADDR(priv->phy_base, i); + phy_val = mt7531_mii_read(priv, phy_addr, MII_BMCR); + phy_val &= ~BMCR_PDOWN; + mt7531_mii_write(priv, phy_addr, MII_BMCR, phy_val); + } + + mt7988_phy_setting(priv); + + return mt7531_mdio_register(priv); +} + +static int mt7531_cleanup(struct mtk_eth_switch_priv *swpriv) +{ + struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; + + mdio_unregister(priv->mdio_bus); + + return 0; +} + +MTK_ETH_SWITCH(mt7988) = { + .name = "mt7988", + .desc = "MediaTek MT7988 built-in switch", + .priv_size = sizeof(struct mt753x_switch_priv), + .reset_wait_time = 50, + + .setup = mt7988_setup, + .cleanup = mt7531_cleanup, + .mac_control = mt7988_mac_control, +}; diff --git a/drivers/net/mtk_eth.c b/drivers/net/mtk_eth/mtk_eth.c index 454caa3cd3a..5d6a42bceb4 100644 --- a/drivers/net/mtk_eth.c +++ b/drivers/net/mtk_eth/mtk_eth.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 MediaTek Inc. + * Copyright (C) 2025 MediaTek Inc. * * Author: Weijie Gao <weijie.gao@mediatek.com> * Author: Mark Lee <mark-mc.lee@mediatek.com> @@ -35,14 +35,6 @@ #define RX_TOTAL_BUF_SIZE (NUM_RX_DESC * PKTSIZE_ALIGN) #define TOTAL_PKT_BUF_SIZE (TX_TOTAL_BUF_SIZE + RX_TOTAL_BUF_SIZE) -#define MT753X_NUM_PHYS 5 -#define MT753X_NUM_PORTS 7 -#define MT753X_DFL_SMI_ADDR 31 -#define MT753X_SMI_ADDR_MASK 0x1f - -#define MT753X_PHY_ADDR(base, addr) \ - (((base) + (addr)) & 0x1f) - #define GDMA_FWD_TO_CPU \ (0x20000000 | \ GDM_ICS_EN | \ @@ -75,32 +67,6 @@ (DP_DISCARD << MC_DP_S) | \ (DP_DISCARD << UN_DP_S)) -enum mtk_switch { - SW_NONE, - SW_MT7530, - SW_MT7531, - SW_MT7988, -}; - -/* struct mtk_soc_data - This is the structure holding all differences - * among various plaforms - * @caps Flags shown the extra capability for the SoC - * @ana_rgc3: The offset for register ANA_RGC3 related to - * sgmiisys syscon - * @gdma_count: Number of GDMAs - * @pdma_base: Register base of PDMA block - * @txd_size: Tx DMA descriptor size. - * @rxd_size: Rx DMA descriptor size. - */ -struct mtk_soc_data { - u32 caps; - u32 ana_rgc3; - u32 gdma_count; - u32 pdma_base; - u32 txd_size; - u32 rxd_size; -}; - struct mtk_eth_priv { char pkt_pool[TOTAL_PKT_BUF_SIZE] __aligned(ARCH_DMA_MINALIGN); @@ -113,7 +79,6 @@ struct mtk_eth_priv { void __iomem *fe_base; void __iomem *gmac_base; void __iomem *sgmii_base; - void __iomem *gsw_base; struct regmap *ethsys_regmap; @@ -125,11 +90,6 @@ struct mtk_eth_priv { struct regmap *toprgu_regmap; struct mii_dev *mdio_bus; - int (*mii_read)(struct mtk_eth_priv *priv, u8 phy, u8 reg); - int (*mii_write)(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 val); - int (*mmd_read)(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg); - int (*mmd_write)(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg, - u16 val); const struct mtk_soc_data *soc; int gmac_id; @@ -143,13 +103,8 @@ struct mtk_eth_priv { int phy_interface; int phy_addr; - enum mtk_switch sw; - int (*switch_init)(struct mtk_eth_priv *priv); - void (*switch_mac_control)(struct mtk_eth_priv *priv, bool enable); - u32 mt753x_smi_addr; - u32 mt753x_phy_base; - u32 mt753x_pmcr; - u32 mt753x_reset_wait_time; + struct mtk_eth_switch_priv *swpriv; + const char *swname; struct gpio_desc rst_gpio; int mcm; @@ -184,7 +139,7 @@ static void mtk_gdma_write(struct mtk_eth_priv *priv, int no, u32 reg, writel(val, priv->fe_base + gdma_base + reg); } -static void mtk_fe_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set) +void mtk_fe_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set) { clrsetbits_le32(priv->fe_base + reg, clr, set); } @@ -199,13 +154,12 @@ static void mtk_gmac_write(struct mtk_eth_priv *priv, u32 reg, u32 val) writel(val, priv->gmac_base + reg); } -static void mtk_gmac_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set) +void mtk_gmac_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set) { clrsetbits_le32(priv->gmac_base + reg, clr, set); } -static void mtk_ethsys_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, - u32 set) +void mtk_ethsys_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set) { uint val; @@ -226,16 +180,6 @@ static void mtk_infra_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, regmap_write(priv->infra_regmap, reg, val); } -static u32 mtk_gsw_read(struct mtk_eth_priv *priv, u32 reg) -{ - return readl(priv->gsw_base + reg); -} - -static void mtk_gsw_write(struct mtk_eth_priv *priv, u32 reg, u32 val) -{ - writel(val, priv->gsw_base + reg); -} - /* Direct MDIO clause 22/45 access via SoC */ static int mtk_mii_rw(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 data, u32 cmd, u32 st) @@ -269,19 +213,19 @@ static int mtk_mii_rw(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 data, } /* Direct MDIO clause 22 read via SoC */ -static int mtk_mii_read(struct mtk_eth_priv *priv, u8 phy, u8 reg) +int mtk_mii_read(struct mtk_eth_priv *priv, u8 phy, u8 reg) { return mtk_mii_rw(priv, phy, reg, 0, MDIO_CMD_READ, MDIO_ST_C22); } /* Direct MDIO clause 22 write via SoC */ -static int mtk_mii_write(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 data) +int mtk_mii_write(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 data) { return mtk_mii_rw(priv, phy, reg, data, MDIO_CMD_WRITE, MDIO_ST_C22); } /* Direct MDIO clause 45 read via SoC */ -static int mtk_mmd_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg) +int mtk_mmd_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg) { int ret; @@ -294,8 +238,8 @@ static int mtk_mmd_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg) } /* Direct MDIO clause 45 write via SoC */ -static int mtk_mmd_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, - u16 reg, u16 val) +int mtk_mmd_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg, + u16 val) { int ret; @@ -308,232 +252,52 @@ static int mtk_mmd_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, } /* Indirect MDIO clause 45 read via MII registers */ -static int mtk_mmd_ind_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, - u16 reg) +int mtk_mmd_ind_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg) { int ret; - ret = priv->mii_write(priv, addr, MII_MMD_ACC_CTL_REG, - (MMD_ADDR << MMD_CMD_S) | - ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); + ret = mtk_mii_write(priv, addr, MII_MMD_ACC_CTL_REG, + (MMD_ADDR << MMD_CMD_S) | + ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); if (ret) return ret; - ret = priv->mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, reg); + ret = mtk_mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, reg); if (ret) return ret; - ret = priv->mii_write(priv, addr, MII_MMD_ACC_CTL_REG, - (MMD_DATA << MMD_CMD_S) | - ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); + ret = mtk_mii_write(priv, addr, MII_MMD_ACC_CTL_REG, + (MMD_DATA << MMD_CMD_S) | + ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); if (ret) return ret; - return priv->mii_read(priv, addr, MII_MMD_ADDR_DATA_REG); + return mtk_mii_read(priv, addr, MII_MMD_ADDR_DATA_REG); } /* Indirect MDIO clause 45 write via MII registers */ -static int mtk_mmd_ind_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, - u16 reg, u16 val) +int mtk_mmd_ind_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg, + u16 val) { int ret; - ret = priv->mii_write(priv, addr, MII_MMD_ACC_CTL_REG, - (MMD_ADDR << MMD_CMD_S) | - ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); + ret = mtk_mii_write(priv, addr, MII_MMD_ACC_CTL_REG, + (MMD_ADDR << MMD_CMD_S) | + ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); if (ret) return ret; - ret = priv->mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, reg); + ret = mtk_mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, reg); if (ret) return ret; - ret = priv->mii_write(priv, addr, MII_MMD_ACC_CTL_REG, - (MMD_DATA << MMD_CMD_S) | - ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); + ret = mtk_mii_write(priv, addr, MII_MMD_ACC_CTL_REG, + (MMD_DATA << MMD_CMD_S) | + ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); if (ret) return ret; - return priv->mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, val); -} - -/* - * MT7530 Internal Register Address Bits - * ------------------------------------------------------------------- - * | 15 14 13 12 11 10 9 8 7 6 | 5 4 3 2 | 1 0 | - * |----------------------------------------|---------------|--------| - * | Page Address | Reg Address | Unused | - * ------------------------------------------------------------------- - */ - -static int mt753x_reg_read(struct mtk_eth_priv *priv, u32 reg, u32 *data) -{ - int ret, low_word, high_word; - - if (priv->sw == SW_MT7988) { - *data = mtk_gsw_read(priv, reg); - return 0; - } - - /* Write page address */ - ret = mtk_mii_write(priv, priv->mt753x_smi_addr, 0x1f, reg >> 6); - if (ret) - return ret; - - /* Read low word */ - low_word = mtk_mii_read(priv, priv->mt753x_smi_addr, (reg >> 2) & 0xf); - if (low_word < 0) - return low_word; - - /* Read high word */ - high_word = mtk_mii_read(priv, priv->mt753x_smi_addr, 0x10); - if (high_word < 0) - return high_word; - - if (data) - *data = ((u32)high_word << 16) | (low_word & 0xffff); - - return 0; -} - -static int mt753x_reg_write(struct mtk_eth_priv *priv, u32 reg, u32 data) -{ - int ret; - - if (priv->sw == SW_MT7988) { - mtk_gsw_write(priv, reg, data); - return 0; - } - - /* Write page address */ - ret = mtk_mii_write(priv, priv->mt753x_smi_addr, 0x1f, reg >> 6); - if (ret) - return ret; - - /* Write low word */ - ret = mtk_mii_write(priv, priv->mt753x_smi_addr, (reg >> 2) & 0xf, - data & 0xffff); - if (ret) - return ret; - - /* Write high word */ - return mtk_mii_write(priv, priv->mt753x_smi_addr, 0x10, data >> 16); -} - -static void mt753x_reg_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, - u32 set) -{ - u32 val; - - mt753x_reg_read(priv, reg, &val); - val &= ~clr; - val |= set; - mt753x_reg_write(priv, reg, val); -} - -/* Indirect MDIO clause 22/45 access */ -static int mt7531_mii_rw(struct mtk_eth_priv *priv, int phy, int reg, u16 data, - u32 cmd, u32 st) -{ - ulong timeout; - u32 val, timeout_ms; - int ret = 0; - - val = (st << MDIO_ST_S) | - ((cmd << MDIO_CMD_S) & MDIO_CMD_M) | - ((phy << MDIO_PHY_ADDR_S) & MDIO_PHY_ADDR_M) | - ((reg << MDIO_REG_ADDR_S) & MDIO_REG_ADDR_M); - - if (cmd == MDIO_CMD_WRITE || cmd == MDIO_CMD_ADDR) - val |= data & MDIO_RW_DATA_M; - - mt753x_reg_write(priv, MT7531_PHY_IAC, val | PHY_ACS_ST); - - timeout_ms = 100; - timeout = get_timer(0); - while (1) { - mt753x_reg_read(priv, MT7531_PHY_IAC, &val); - - if ((val & PHY_ACS_ST) == 0) - break; - - if (get_timer(timeout) > timeout_ms) - return -ETIMEDOUT; - } - - if (cmd == MDIO_CMD_READ || cmd == MDIO_CMD_READ_C45) { - mt753x_reg_read(priv, MT7531_PHY_IAC, &val); - ret = val & MDIO_RW_DATA_M; - } - - return ret; -} - -static int mt7531_mii_ind_read(struct mtk_eth_priv *priv, u8 phy, u8 reg) -{ - u8 phy_addr; - - if (phy >= MT753X_NUM_PHYS) - return -EINVAL; - - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, phy); - - return mt7531_mii_rw(priv, phy_addr, reg, 0, MDIO_CMD_READ, - MDIO_ST_C22); -} - -static int mt7531_mii_ind_write(struct mtk_eth_priv *priv, u8 phy, u8 reg, - u16 val) -{ - u8 phy_addr; - - if (phy >= MT753X_NUM_PHYS) - return -EINVAL; - - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, phy); - - return mt7531_mii_rw(priv, phy_addr, reg, val, MDIO_CMD_WRITE, - MDIO_ST_C22); -} - -static int mt7531_mmd_ind_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, - u16 reg) -{ - u8 phy_addr; - int ret; - - if (addr >= MT753X_NUM_PHYS) - return -EINVAL; - - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, addr); - - ret = mt7531_mii_rw(priv, phy_addr, devad, reg, MDIO_CMD_ADDR, - MDIO_ST_C45); - if (ret) - return ret; - - return mt7531_mii_rw(priv, phy_addr, devad, 0, MDIO_CMD_READ_C45, - MDIO_ST_C45); -} - -static int mt7531_mmd_ind_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, - u16 reg, u16 val) -{ - u8 phy_addr; - int ret; - - if (addr >= MT753X_NUM_PHYS) - return 0; - - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, addr); - - ret = mt7531_mii_rw(priv, phy_addr, devad, reg, MDIO_CMD_ADDR, - MDIO_ST_C45); - if (ret) - return ret; - - return mt7531_mii_rw(priv, phy_addr, devad, val, MDIO_CMD_WRITE, - MDIO_ST_C45); + return mtk_mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, val); } static int mtk_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) @@ -541,9 +305,9 @@ static int mtk_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) struct mtk_eth_priv *priv = bus->priv; if (devad < 0) - return priv->mii_read(priv, addr, reg); - else - return priv->mmd_read(priv, addr, devad, reg); + return mtk_mii_read(priv, addr, reg); + + return mtk_mmd_read(priv, addr, devad, reg); } static int mtk_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, @@ -552,9 +316,9 @@ static int mtk_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, struct mtk_eth_priv *priv = bus->priv; if (devad < 0) - return priv->mii_write(priv, addr, reg, val); - else - return priv->mmd_write(priv, addr, devad, reg, val); + return mtk_mii_write(priv, addr, reg, val); + + return mtk_mmd_write(priv, addr, devad, reg, val); } static int mtk_mdio_register(struct udevice *dev) @@ -566,28 +330,6 @@ static int mtk_mdio_register(struct udevice *dev) if (!mdio_bus) return -ENOMEM; - /* Assign MDIO access APIs according to the switch/phy */ - switch (priv->sw) { - case SW_MT7530: - priv->mii_read = mtk_mii_read; - priv->mii_write = mtk_mii_write; - priv->mmd_read = mtk_mmd_ind_read; - priv->mmd_write = mtk_mmd_ind_write; - break; - case SW_MT7531: - case SW_MT7988: - priv->mii_read = mt7531_mii_ind_read; - priv->mii_write = mt7531_mii_ind_write; - priv->mmd_read = mt7531_mmd_ind_read; - priv->mmd_write = mt7531_mmd_ind_write; - break; - default: - priv->mii_read = mtk_mii_read; - priv->mii_write = mtk_mii_write; - priv->mmd_read = mtk_mmd_read; - priv->mmd_write = mtk_mmd_write; - } - mdio_bus->read = mtk_mdio_read; mdio_bus->write = mtk_mdio_write; snprintf(mdio_bus->name, sizeof(mdio_bus->name), dev->name); @@ -604,531 +346,91 @@ static int mtk_mdio_register(struct udevice *dev) return 0; } -static int mt753x_core_reg_read(struct mtk_eth_priv *priv, u32 reg) -{ - u8 phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, 0); - - return priv->mmd_read(priv, phy_addr, 0x1f, reg); -} - -static void mt753x_core_reg_write(struct mtk_eth_priv *priv, u32 reg, u32 val) -{ - u8 phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, 0); - - priv->mmd_write(priv, phy_addr, 0x1f, reg, val); -} - -static int mt7530_pad_clk_setup(struct mtk_eth_priv *priv, int mode) -{ - u32 ncpo1, ssc_delta; - - switch (mode) { - case PHY_INTERFACE_MODE_RGMII: - ncpo1 = 0x0c80; - ssc_delta = 0x87; - break; - default: - printf("error: xMII mode %d not supported\n", mode); - return -EINVAL; - } - - /* Disable MT7530 core clock */ - mt753x_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, 0); - - /* Disable MT7530 PLL */ - mt753x_core_reg_write(priv, CORE_GSWPLL_GRP1, - (2 << RG_GSWPLL_POSDIV_200M_S) | - (32 << RG_GSWPLL_FBKDIV_200M_S)); - - /* For MT7530 core clock = 500Mhz */ - mt753x_core_reg_write(priv, CORE_GSWPLL_GRP2, - (1 << RG_GSWPLL_POSDIV_500M_S) | - (25 << RG_GSWPLL_FBKDIV_500M_S)); - - /* Enable MT7530 PLL */ - mt753x_core_reg_write(priv, CORE_GSWPLL_GRP1, - (2 << RG_GSWPLL_POSDIV_200M_S) | - (32 << RG_GSWPLL_FBKDIV_200M_S) | - RG_GSWPLL_EN_PRE); - - udelay(20); - - mt753x_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN); - - /* Setup the MT7530 TRGMII Tx Clock */ - mt753x_core_reg_write(priv, CORE_PLL_GROUP5, ncpo1); - mt753x_core_reg_write(priv, CORE_PLL_GROUP6, 0); - mt753x_core_reg_write(priv, CORE_PLL_GROUP10, ssc_delta); - mt753x_core_reg_write(priv, CORE_PLL_GROUP11, ssc_delta); - mt753x_core_reg_write(priv, CORE_PLL_GROUP4, RG_SYSPLL_DDSFBK_EN | - RG_SYSPLL_BIAS_EN | RG_SYSPLL_BIAS_LPF_EN); - - mt753x_core_reg_write(priv, CORE_PLL_GROUP2, - RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN | - (1 << RG_SYSPLL_POSDIV_S)); - - mt753x_core_reg_write(priv, CORE_PLL_GROUP7, - RG_LCDDS_PCW_NCPO_CHG | (3 << RG_LCCDS_C_S) | - RG_LCDDS_PWDB | RG_LCDDS_ISO_EN); - - /* Enable MT7530 core clock */ - mt753x_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, - REG_GSWCK_EN | REG_TRGMIICK_EN); - - return 0; -} - -static void mt7530_mac_control(struct mtk_eth_priv *priv, bool enable) -{ - u32 pmcr = FORCE_MODE; - - if (enable) - pmcr = priv->mt753x_pmcr; - - mt753x_reg_write(priv, PMCR_REG(6), pmcr); -} - -static int mt7530_setup(struct mtk_eth_priv *priv) -{ - u16 phy_addr, phy_val; - u32 val, txdrv; - int i; - - if (!MTK_HAS_CAPS(priv->soc->caps, MTK_TRGMII_MT7621_CLK)) { - /* Select 250MHz clk for RGMII mode */ - mtk_ethsys_rmw(priv, ETHSYS_CLKCFG0_REG, - ETHSYS_TRGMII_CLK_SEL362_5, 0); - - txdrv = 8; - } else { - txdrv = 4; - } - - /* Modify HWTRAP first to allow direct access to internal PHYs */ - mt753x_reg_read(priv, HWTRAP_REG, &val); - val |= CHG_TRAP; - val &= ~C_MDIO_BPS; - mt753x_reg_write(priv, MHWTRAP_REG, val); - - /* Calculate the phy base address */ - val = ((val & SMI_ADDR_M) >> SMI_ADDR_S) << 3; - priv->mt753x_phy_base = (val | 0x7) + 1; - - /* Turn off PHYs */ - for (i = 0; i < MT753X_NUM_PHYS; i++) { - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i); - phy_val = priv->mii_read(priv, phy_addr, MII_BMCR); - phy_val |= BMCR_PDOWN; - priv->mii_write(priv, phy_addr, MII_BMCR, phy_val); - } - - /* Force MAC link down before reset */ - mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE); - mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE); - - /* MT7530 reset */ - mt753x_reg_write(priv, SYS_CTRL_REG, SW_SYS_RST | SW_REG_RST); - udelay(100); - - val = (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | - MAC_MODE | FORCE_MODE | - MAC_TX_EN | MAC_RX_EN | - BKOFF_EN | BACKPR_EN | - (SPEED_1000M << FORCE_SPD_S) | - FORCE_DPX | FORCE_LINK; - - /* MT7530 Port6: Forced 1000M/FD, FC disabled */ - priv->mt753x_pmcr = val; - - /* MT7530 Port5: Forced link down */ - mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE); - - /* Keep MAC link down before starting eth */ - mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE); - - /* MT7530 Port6: Set to RGMII */ - mt753x_reg_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_M, P6_INTF_MODE_RGMII); - - /* Hardware Trap: Enable Port6, Disable Port5 */ - mt753x_reg_read(priv, HWTRAP_REG, &val); - val |= CHG_TRAP | LOOPDET_DIS | P5_INTF_DIS | - (P5_INTF_SEL_GMAC5 << P5_INTF_SEL_S) | - (P5_INTF_MODE_RGMII << P5_INTF_MODE_S); - val &= ~(C_MDIO_BPS | P6_INTF_DIS); - mt753x_reg_write(priv, MHWTRAP_REG, val); - - /* Setup switch core pll */ - mt7530_pad_clk_setup(priv, priv->phy_interface); - - /* Lower Tx Driving for TRGMII path */ - for (i = 0 ; i < NUM_TRGMII_CTRL ; i++) - mt753x_reg_write(priv, MT7530_TRGMII_TD_ODT(i), - (txdrv << TD_DM_DRVP_S) | - (txdrv << TD_DM_DRVN_S)); - - for (i = 0 ; i < NUM_TRGMII_CTRL; i++) - mt753x_reg_rmw(priv, MT7530_TRGMII_RD(i), RD_TAP_M, 16); - - /* Turn on PHYs */ - for (i = 0; i < MT753X_NUM_PHYS; i++) { - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i); - phy_val = priv->mii_read(priv, phy_addr, MII_BMCR); - phy_val &= ~BMCR_PDOWN; - priv->mii_write(priv, phy_addr, MII_BMCR, phy_val); - } - - return 0; -} - -static void mt7531_core_pll_setup(struct mtk_eth_priv *priv, int mcm) +static int mtk_switch_init(struct mtk_eth_priv *priv) { - /* Step 1 : Disable MT7531 COREPLL */ - mt753x_reg_rmw(priv, MT7531_PLLGP_EN, EN_COREPLL, 0); - - /* Step 2: switch to XTAL output */ - mt753x_reg_rmw(priv, MT7531_PLLGP_EN, SW_CLKSW, SW_CLKSW); - - mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_EN, 0); - - /* Step 3: disable PLLGP and enable program PLLGP */ - mt753x_reg_rmw(priv, MT7531_PLLGP_EN, SW_PLLGP, SW_PLLGP); - - /* Step 4: program COREPLL output frequency to 500MHz */ - mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_POSDIV_M, - 2 << RG_COREPLL_POSDIV_S); - udelay(25); - - /* Currently, support XTAL 25Mhz only */ - mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_M, - 0x140000 << RG_COREPLL_SDM_PCW_S); - - /* Set feedback divide ratio update signal to high */ - mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_CHG, - RG_COREPLL_SDM_PCW_CHG); - - /* Wait for at least 16 XTAL clocks */ - udelay(10); - - /* Step 5: set feedback divide ratio update signal to low */ - mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_CHG, 0); - - /* add enable 325M clock for SGMII */ - mt753x_reg_write(priv, MT7531_ANA_PLLGP_CR5, 0xad0000); - - /* add enable 250SSC clock for RGMII */ - mt753x_reg_write(priv, MT7531_ANA_PLLGP_CR2, 0x4f40000); - - /*Step 6: Enable MT7531 PLL */ - mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_EN, RG_COREPLL_EN); - - mt753x_reg_rmw(priv, MT7531_PLLGP_EN, EN_COREPLL, EN_COREPLL); - - udelay(25); -} - -static int mt7531_port_sgmii_init(struct mtk_eth_priv *priv, - u32 port) -{ - if (port != 5 && port != 6) { - printf("mt7531: port %d is not a SGMII port\n", port); - return -EINVAL; - } - - /* Set SGMII GEN2 speed(2.5G) */ - mt753x_reg_rmw(priv, MT7531_PHYA_CTRL_SIGNAL3(port), SGMSYS_SPEED_MASK, - FIELD_PREP(SGMSYS_SPEED_MASK, SGMSYS_SPEED_2500)); - - /* Disable SGMII AN */ - mt753x_reg_rmw(priv, MT7531_PCS_CONTROL_1(port), - SGMII_AN_ENABLE, 0); - - /* SGMII force mode setting */ - mt753x_reg_write(priv, MT7531_SGMII_MODE(port), SGMII_FORCE_MODE); - - /* Release PHYA power down state */ - mt753x_reg_rmw(priv, MT7531_QPHY_PWR_STATE_CTRL(port), - SGMII_PHYA_PWD, 0); - - return 0; -} - -static int mt7531_port_rgmii_init(struct mtk_eth_priv *priv, u32 port) -{ - u32 val; - - if (port != 5) { - printf("error: RGMII mode is not available for port %d\n", - port); - return -EINVAL; - } - - mt753x_reg_read(priv, MT7531_CLKGEN_CTRL, &val); - val |= GP_CLK_EN; - val &= ~GP_MODE_M; - val |= GP_MODE_RGMII << GP_MODE_S; - val |= TXCLK_NO_REVERSE; - val |= RXCLK_NO_DELAY; - val &= ~CLK_SKEW_IN_M; - val |= CLK_SKEW_IN_NO_CHANGE << CLK_SKEW_IN_S; - val &= ~CLK_SKEW_OUT_M; - val |= CLK_SKEW_OUT_NO_CHANGE << CLK_SKEW_OUT_S; - mt753x_reg_write(priv, MT7531_CLKGEN_CTRL, val); - - return 0; -} - -static void mt7531_phy_setting(struct mtk_eth_priv *priv) -{ - int i; - u32 val; - - for (i = 0; i < MT753X_NUM_PHYS; i++) { - /* Enable HW auto downshift */ - priv->mii_write(priv, i, 0x1f, 0x1); - val = priv->mii_read(priv, i, PHY_EXT_REG_14); - val |= PHY_EN_DOWN_SHFIT; - priv->mii_write(priv, i, PHY_EXT_REG_14, val); - - /* PHY link down power saving enable */ - val = priv->mii_read(priv, i, PHY_EXT_REG_17); - val |= PHY_LINKDOWN_POWER_SAVING_EN; - priv->mii_write(priv, i, PHY_EXT_REG_17, val); - - val = priv->mmd_read(priv, i, 0x1e, PHY_DEV1E_REG_0C6); - val &= ~PHY_POWER_SAVING_M; - val |= PHY_POWER_SAVING_TX << PHY_POWER_SAVING_S; - priv->mmd_write(priv, i, 0x1e, PHY_DEV1E_REG_0C6, val); - } -} - -static void mt7531_mac_control(struct mtk_eth_priv *priv, bool enable) -{ - u32 pmcr = FORCE_MODE_LNK; - - if (enable) - pmcr = priv->mt753x_pmcr; - - mt753x_reg_write(priv, PMCR_REG(5), pmcr); - mt753x_reg_write(priv, PMCR_REG(6), pmcr); -} - -static int mt7531_setup(struct mtk_eth_priv *priv) -{ - u16 phy_addr, phy_val; - u32 val; - u32 pmcr; - u32 port5_sgmii; - int i; - - priv->mt753x_phy_base = (priv->mt753x_smi_addr + 1) & - MT753X_SMI_ADDR_MASK; - - /* Turn off PHYs */ - for (i = 0; i < MT753X_NUM_PHYS; i++) { - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i); - phy_val = priv->mii_read(priv, phy_addr, MII_BMCR); - phy_val |= BMCR_PDOWN; - priv->mii_write(priv, phy_addr, MII_BMCR, phy_val); - } - - /* Force MAC link down before reset */ - mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE_LNK); - mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK); - - /* Switch soft reset */ - mt753x_reg_write(priv, SYS_CTRL_REG, SW_SYS_RST | SW_REG_RST); - udelay(100); - - /* Enable MDC input Schmitt Trigger */ - mt753x_reg_rmw(priv, MT7531_SMT0_IOLB, SMT_IOLB_5_SMI_MDC_EN, - SMT_IOLB_5_SMI_MDC_EN); - - mt7531_core_pll_setup(priv, priv->mcm); - - mt753x_reg_read(priv, MT7531_TOP_SIG_SR, &val); - port5_sgmii = !!(val & PAD_DUAL_SGMII_EN); - - /* port5 support either RGMII or SGMII, port6 only support SGMII. */ - switch (priv->phy_interface) { - case PHY_INTERFACE_MODE_RGMII: - if (!port5_sgmii) - mt7531_port_rgmii_init(priv, 5); - break; - case PHY_INTERFACE_MODE_2500BASEX: - mt7531_port_sgmii_init(priv, 6); - if (port5_sgmii) - mt7531_port_sgmii_init(priv, 5); - break; - default: - break; - } - - pmcr = MT7531_FORCE_MODE | - (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | - MAC_MODE | MAC_TX_EN | MAC_RX_EN | - BKOFF_EN | BACKPR_EN | - FORCE_RX_FC | FORCE_TX_FC | - (SPEED_1000M << FORCE_SPD_S) | FORCE_DPX | - FORCE_LINK; - - priv->mt753x_pmcr = pmcr; - - /* Keep MAC link down before starting eth */ - mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE_LNK); - mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK); - - /* Turn on PHYs */ - for (i = 0; i < MT753X_NUM_PHYS; i++) { - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i); - phy_val = priv->mii_read(priv, phy_addr, MII_BMCR); - phy_val &= ~BMCR_PDOWN; - priv->mii_write(priv, phy_addr, MII_BMCR, phy_val); - } - - mt7531_phy_setting(priv); - - /* Enable Internal PHYs */ - val = mt753x_core_reg_read(priv, CORE_PLL_GROUP4); - val |= MT7531_BYPASS_MODE; - val &= ~MT7531_POWER_ON_OFF; - mt753x_core_reg_write(priv, CORE_PLL_GROUP4, val); - - return 0; -} - -static void mt7988_phy_setting(struct mtk_eth_priv *priv) -{ - u16 val; - u32 i; - - for (i = 0; i < MT753X_NUM_PHYS; i++) { - /* Enable HW auto downshift */ - priv->mii_write(priv, i, 0x1f, 0x1); - val = priv->mii_read(priv, i, PHY_EXT_REG_14); - val |= PHY_EN_DOWN_SHFIT; - priv->mii_write(priv, i, PHY_EXT_REG_14, val); - - /* PHY link down power saving enable */ - val = priv->mii_read(priv, i, PHY_EXT_REG_17); - val |= PHY_LINKDOWN_POWER_SAVING_EN; - priv->mii_write(priv, i, PHY_EXT_REG_17, val); - } -} - -static void mt7988_mac_control(struct mtk_eth_priv *priv, bool enable) -{ - u32 pmcr = FORCE_MODE_LNK; - - if (enable) - pmcr = priv->mt753x_pmcr; - - mt753x_reg_write(priv, PMCR_REG(6), pmcr); -} - -static int mt7988_setup(struct mtk_eth_priv *priv) -{ - u16 phy_addr, phy_val; - u32 pmcr; - int i; - - priv->gsw_base = regmap_get_range(priv->ethsys_regmap, 0) + GSW_BASE; - - priv->mt753x_phy_base = (priv->mt753x_smi_addr + 1) & - MT753X_SMI_ADDR_MASK; - - /* Turn off PHYs */ - for (i = 0; i < MT753X_NUM_PHYS; i++) { - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i); - phy_val = priv->mii_read(priv, phy_addr, MII_BMCR); - phy_val |= BMCR_PDOWN; - priv->mii_write(priv, phy_addr, MII_BMCR, phy_val); - } - - switch (priv->phy_interface) { - case PHY_INTERFACE_MODE_USXGMII: - /* Use CPU bridge instead of actual USXGMII path */ - - /* Set GDM1 no drop */ - mtk_fe_rmw(priv, PSE_NO_DROP_CFG_REG, 0, PSE_NO_DROP_GDM1); - - /* Enable GDM1 to GSW CPU bridge */ - mtk_gmac_rmw(priv, GMAC_MAC_MISC_REG, 0, BIT(0)); - - /* XGMAC force link up */ - mtk_gmac_rmw(priv, GMAC_XGMAC_STS_REG, 0, P1_XGMAC_FORCE_LINK); - - /* Setup GSW CPU bridge IPG */ - mtk_gmac_rmw(priv, GMAC_GSW_CFG_REG, GSWTX_IPG_M | GSWRX_IPG_M, - (0xB << GSWTX_IPG_S) | (0xB << GSWRX_IPG_S)); - break; - default: - printf("Error: MT7988 GSW does not support %s interface\n", - phy_string_for_interface(priv->phy_interface)); - break; - } + struct mtk_eth_switch *swdrvs = ll_entry_start(struct mtk_eth_switch, + mtk_eth_switch); + const u32 n_swdrvs = ll_entry_count(struct mtk_eth_switch, + mtk_eth_switch); + struct mtk_eth_switch *tmp, *swdrv = NULL; + u32 reset_wait_time = 500; + size_t priv_size; + int ret; - pmcr = MT7988_FORCE_MODE | - (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | - MAC_MODE | MAC_TX_EN | MAC_RX_EN | - BKOFF_EN | BACKPR_EN | - FORCE_RX_FC | FORCE_TX_FC | - (SPEED_1000M << FORCE_SPD_S) | FORCE_DPX | - FORCE_LINK; - - priv->mt753x_pmcr = pmcr; - - /* Keep MAC link down before starting eth */ - mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK); - - /* Turn on PHYs */ - for (i = 0; i < MT753X_NUM_PHYS; i++) { - phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i); - phy_val = priv->mii_read(priv, phy_addr, MII_BMCR); - phy_val &= ~BMCR_PDOWN; - priv->mii_write(priv, phy_addr, MII_BMCR, phy_val); + if (strcmp(priv->swname, "auto")) { + for (tmp = swdrvs; tmp < swdrvs + n_swdrvs; tmp++) { + if (!strcmp(tmp->name, priv->swname)) { + swdrv = tmp; + break; + } + } } - mt7988_phy_setting(priv); - - return 0; -} - -static int mt753x_switch_init(struct mtk_eth_priv *priv) -{ - int ret; - int i; + if (swdrv) + reset_wait_time = swdrv->reset_wait_time; /* Global reset switch */ if (priv->mcm) { reset_assert(&priv->rst_mcm); udelay(1000); reset_deassert(&priv->rst_mcm); - mdelay(priv->mt753x_reset_wait_time); + mdelay(reset_wait_time); } else if (dm_gpio_is_valid(&priv->rst_gpio)) { dm_gpio_set_value(&priv->rst_gpio, 0); udelay(1000); dm_gpio_set_value(&priv->rst_gpio, 1); - mdelay(priv->mt753x_reset_wait_time); + mdelay(reset_wait_time); } - ret = priv->switch_init(priv); - if (ret) - return ret; + if (!swdrv) { + for (tmp = swdrvs; tmp < swdrvs + n_swdrvs; tmp++) { + if (!tmp->detect) + continue; - /* Set port isolation */ - for (i = 0; i < MT753X_NUM_PORTS; i++) { - /* Set port matrix mode */ - if (i != 6) - mt753x_reg_write(priv, PCR_REG(i), - (0x40 << PORT_MATRIX_S)); - else - mt753x_reg_write(priv, PCR_REG(i), - (0x3f << PORT_MATRIX_S)); + ret = tmp->detect(priv); + if (!ret) { + swdrv = tmp; + break; + } + } + + if (!swdrv) { + printf("Error: unable to detect switch\n"); + return -ENODEV; + } + } else { + if (swdrv->detect) { + ret = swdrv->detect(priv); + if (ret) { + printf("Error: switch probing failed\n"); + return -ENODEV; + } + } + } + + printf("%s\n", swdrv->desc); + + priv_size = swdrv->priv_size; + if (priv_size < sizeof(struct mtk_eth_switch_priv)) + priv_size = sizeof(struct mtk_eth_switch_priv); + + priv->swpriv = calloc(1, priv_size); + if (!priv->swpriv) { + printf("Error: no memory for switch data\n"); + return -ENOMEM; + } + + priv->swpriv->eth = priv; + priv->swpriv->soc = priv->soc; + priv->swpriv->phy_interface = priv->phy_interface; + priv->swpriv->sw = swdrv; + priv->swpriv->ethsys_base = regmap_get_range(priv->ethsys_regmap, 0); - /* Set port mode to user port */ - mt753x_reg_write(priv, PVC_REG(i), - (0x8100 << STAG_VPID_S) | - (VLAN_ATTR_USER << VLAN_ATTR_S)); + ret = swdrv->setup(priv->swpriv); + if (ret) { + free(priv->swpriv); + priv->swpriv = NULL; + return ret; } return 0; @@ -1759,7 +1061,8 @@ static int mtk_eth_start(struct udevice *dev) } if (MTK_HAS_CAPS(priv->soc->caps, MTK_NETSYS_V3)) { - if (priv->sw == SW_MT7988 && priv->gmac_id == 0) { + if (priv->swpriv && !strcmp(priv->swpriv->sw->name, "mt7988") && + priv->gmac_id == 0) { mtk_gdma_write(priv, priv->gmac_id, GDMA_IG_CTRL_REG, GDMA_BRIDGE_TO_CPU); @@ -1778,11 +1081,12 @@ static int mtk_eth_start(struct udevice *dev) mtk_eth_fifo_init(priv); - if (priv->switch_mac_control) - priv->switch_mac_control(priv, true); - - /* Start PHY */ - if (priv->sw == SW_NONE) { + if (priv->swpriv) { + /* Enable communication with switch */ + if (priv->swpriv->sw->mac_control) + priv->swpriv->sw->mac_control(priv->swpriv, true); + } else { + /* Start PHY */ ret = mtk_phy_start(priv); if (ret) return ret; @@ -1799,8 +1103,10 @@ static void mtk_eth_stop(struct udevice *dev) { struct mtk_eth_priv *priv = dev_get_priv(dev); - if (priv->switch_mac_control) - priv->switch_mac_control(priv, false); + if (priv->swpriv) { + if (priv->swpriv->sw->mac_control) + priv->swpriv->sw->mac_control(priv->swpriv, false); + } mtk_pdma_rmw(priv, PDMA_GLO_CFG_REG, TX_WB_DDONE | RX_DMA_EN | TX_DMA_EN, 0); @@ -1953,11 +1259,11 @@ static int mtk_eth_probe(struct udevice *dev) return ret; /* Probe phy if switch is not specified */ - if (priv->sw == SW_NONE) + if (!priv->swname) return mtk_phy_probe(dev); /* Initialize switch */ - return mt753x_switch_init(priv); + return mtk_switch_init(priv); } static int mtk_eth_remove(struct udevice *dev) @@ -1971,6 +1277,12 @@ static int mtk_eth_remove(struct udevice *dev) /* Stop possibly started DMA */ mtk_eth_stop(dev); + if (priv->swpriv) { + if (priv->swpriv->sw->cleanup) + priv->swpriv->sw->cleanup(priv->swpriv); + free(priv->swpriv); + } + return 0; } @@ -1980,7 +1292,6 @@ static int mtk_eth_of_to_plat(struct udevice *dev) struct mtk_eth_priv *priv = dev_get_priv(dev); struct ofnode_phandle_args args; struct regmap *regmap; - const char *str; ofnode subnode; int ret; @@ -2126,36 +1437,8 @@ static int mtk_eth_of_to_plat(struct udevice *dev) return PTR_ERR(priv->toprgu_regmap); } - /* check for switch first, otherwise phy will be used */ - priv->sw = SW_NONE; - priv->switch_init = NULL; - priv->switch_mac_control = NULL; - str = dev_read_string(dev, "mediatek,switch"); - - if (str) { - if (!strcmp(str, "mt7530")) { - priv->sw = SW_MT7530; - priv->switch_init = mt7530_setup; - priv->switch_mac_control = mt7530_mac_control; - priv->mt753x_smi_addr = MT753X_DFL_SMI_ADDR; - priv->mt753x_reset_wait_time = 1000; - } else if (!strcmp(str, "mt7531")) { - priv->sw = SW_MT7531; - priv->switch_init = mt7531_setup; - priv->switch_mac_control = mt7531_mac_control; - priv->mt753x_smi_addr = MT753X_DFL_SMI_ADDR; - priv->mt753x_reset_wait_time = 200; - } else if (!strcmp(str, "mt7988")) { - priv->sw = SW_MT7988; - priv->switch_init = mt7988_setup; - priv->switch_mac_control = mt7988_mac_control; - priv->mt753x_smi_addr = MT753X_DFL_SMI_ADDR; - priv->mt753x_reset_wait_time = 50; - } else { - printf("error: unsupported switch\n"); - return -EINVAL; - } - + priv->swname = dev_read_string(dev, "mediatek,switch"); + if (priv->swname) { priv->mcm = dev_read_bool(dev, "mediatek,mcm"); if (priv->mcm) { ret = reset_get_by_name(dev, "mcm", &priv->rst_mcm); @@ -2194,6 +1477,15 @@ static const struct mtk_soc_data mt7988_data = { .rxd_size = sizeof(struct mtk_rx_dma_v2), }; +static const struct mtk_soc_data mt7987_data = { + .caps = MT7987_CAPS, + .ana_rgc3 = 0x128, + .gdma_count = 3, + .pdma_base = PDMA_V3_BASE, + .txd_size = sizeof(struct mtk_tx_dma_v2), + .rxd_size = sizeof(struct mtk_rx_dma_v2), +}; + static const struct mtk_soc_data mt7986_data = { .caps = MT7986_CAPS, .ana_rgc3 = 0x128, @@ -2248,6 +1540,7 @@ static const struct mtk_soc_data mt7621_data = { static const struct udevice_id mtk_eth_ids[] = { { .compatible = "mediatek,mt7988-eth", .data = (ulong)&mt7988_data }, + { .compatible = "mediatek,mt7987-eth", .data = (ulong)&mt7987_data }, { .compatible = "mediatek,mt7986-eth", .data = (ulong)&mt7986_data }, { .compatible = "mediatek,mt7981-eth", .data = (ulong)&mt7981_data }, { .compatible = "mediatek,mt7629-eth", .data = (ulong)&mt7629_data }, @@ -2271,10 +1564,10 @@ U_BOOT_DRIVER(mtk_eth) = { .id = UCLASS_ETH, .of_match = mtk_eth_ids, .of_to_plat = mtk_eth_of_to_plat, - .plat_auto = sizeof(struct eth_pdata), + .plat_auto = sizeof(struct eth_pdata), .probe = mtk_eth_probe, .remove = mtk_eth_remove, .ops = &mtk_eth_ops, - .priv_auto = sizeof(struct mtk_eth_priv), + .priv_auto = sizeof(struct mtk_eth_priv), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; diff --git a/drivers/net/mtk_eth.h b/drivers/net/mtk_eth/mtk_eth.h index 1aa037907c5..0ad32799128 100644 --- a/drivers/net/mtk_eth.h +++ b/drivers/net/mtk_eth/mtk_eth.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 MediaTek Inc. + * Copyright (C) 2025 MediaTek Inc. * * Author: Weijie Gao <weijie.gao@mediatek.com> * Author: Mark Lee <mark-mc.lee@mediatek.com> @@ -9,9 +9,55 @@ #ifndef _MTK_ETH_H_ #define _MTK_ETH_H_ +#include <linker_lists.h> #include <linux/bitops.h> #include <linux/bitfield.h> +struct mtk_eth_priv; +struct mtk_eth_switch_priv; + +/* struct mtk_soc_data - This is the structure holding all differences + * among various plaforms + * @caps Flags shown the extra capability for the SoC + * @ana_rgc3: The offset for register ANA_RGC3 related to + * sgmiisys syscon + * @gdma_count: Number of GDMAs + * @pdma_base: Register base of PDMA block + * @txd_size: Tx DMA descriptor size. + * @rxd_size: Rx DMA descriptor size. + */ +struct mtk_soc_data { + u32 caps; + u32 ana_rgc3; + u32 gdma_count; + u32 pdma_base; + u32 txd_size; + u32 rxd_size; +}; + +struct mtk_eth_switch { + const char *name; + const char *desc; + size_t priv_size; + u32 reset_wait_time; + + int (*detect)(struct mtk_eth_priv *priv); + int (*setup)(struct mtk_eth_switch_priv *priv); + int (*cleanup)(struct mtk_eth_switch_priv *priv); + void (*mac_control)(struct mtk_eth_switch_priv *priv, bool enable); +}; + +#define MTK_ETH_SWITCH(__name) \ + ll_entry_declare(struct mtk_eth_switch, __name, mtk_eth_switch) + +struct mtk_eth_switch_priv { + struct mtk_eth_priv *eth; + const struct mtk_eth_switch *sw; + const struct mtk_soc_data *soc; + void *ethsys_base; + int phy_interface; +}; + enum mkt_eth_capabilities { MTK_TRGMII_BIT, MTK_TRGMII_MT7621_CLK_BIT, @@ -36,7 +82,6 @@ enum mkt_eth_capabilities { /* Supported path present on SoCs */ #define MTK_ETH_PATH_GMAC1_TRGMII BIT(MTK_ETH_PATH_GMAC1_TRGMII_BIT) - #define MTK_ETH_PATH_GMAC2_SGMII BIT(MTK_ETH_PATH_GMAC2_SGMII_BIT) #define MTK_ETH_PATH_MT7622_SGMII BIT(MTK_ETH_PATH_MT7622_SGMII_BIT) #define MTK_ETH_PATH_MT7629_GMAC2 BIT(MTK_ETH_PATH_MT7629_GMAC2_BIT) @@ -59,6 +104,8 @@ enum mkt_eth_capabilities { #define MT7986_CAPS (MTK_NETSYS_V2) +#define MT7987_CAPS (MTK_NETSYS_V3 | MTK_GMAC2_U3_QPHY | MTK_INFRA) + #define MT7988_CAPS (MTK_NETSYS_V3 | MTK_INFRA) /* Frame Engine Register Bases */ @@ -72,7 +119,6 @@ enum mkt_eth_capabilities { #define GSW_BASE 0x20000 /* Ethernet subsystem registers */ - #define ETHSYS_SYSCFG1_REG 0x14 #define SYSCFG1_GE_MODE_S(n) (12 + ((n) * 2)) #define SYSCFG1_GE_MODE_M 0x3 @@ -191,7 +237,6 @@ enum mkt_eth_capabilities { #define DP_DISCARD 7 /* GMAC Registers */ - #define GMAC_PPSC_REG 0x0000 #define PHY_MDC_CFG GENMASK(29, 24) #define MDC_TURBO BIT(20) @@ -272,6 +317,8 @@ enum mkt_eth_capabilities { #define RX_RST BIT(31) #define RXC_DQSISEL BIT(30) +#define NUM_TRGMII_CTRL 5 + #define GMAC_TRGMII_TD_ODT(n) (0x354 + (n) * 8) #define TD_DM_DRVN_S 4 #define TD_DM_DRVN_M 0xf0 @@ -288,164 +335,7 @@ enum mkt_eth_capabilities { #define XGMAC_FORCE_TX_FC BIT(5) #define XGMAC_FORCE_RX_FC BIT(4) -/* MT7530 Registers */ - -#define PCR_REG(p) (0x2004 + (p) * 0x100) -#define PORT_MATRIX_S 16 -#define PORT_MATRIX_M 0xff0000 - -#define PVC_REG(p) (0x2010 + (p) * 0x100) -#define STAG_VPID_S 16 -#define STAG_VPID_M 0xffff0000 -#define VLAN_ATTR_S 6 -#define VLAN_ATTR_M 0xc0 - -/* VLAN_ATTR: VLAN attributes */ -#define VLAN_ATTR_USER 0 -#define VLAN_ATTR_STACK 1 -#define VLAN_ATTR_TRANSLATION 2 -#define VLAN_ATTR_TRANSPARENT 3 - -#define PMCR_REG(p) (0x3000 + (p) * 0x100) -/* XXX: all fields of MT7530 are defined under GMAC_PORT_MCR - * MT7531 specific fields are defined below - */ -#define FORCE_MODE_EEE1G BIT(25) -#define FORCE_MODE_EEE100 BIT(26) -#define FORCE_MODE_TX_FC BIT(27) -#define FORCE_MODE_RX_FC BIT(28) -#define FORCE_MODE_DPX BIT(29) -#define FORCE_MODE_SPD BIT(30) -#define FORCE_MODE_LNK BIT(31) -#define MT7531_FORCE_MODE FORCE_MODE_EEE1G | FORCE_MODE_EEE100 |\ - FORCE_MODE_TX_FC | FORCE_MODE_RX_FC | \ - FORCE_MODE_DPX | FORCE_MODE_SPD | \ - FORCE_MODE_LNK -#define MT7988_FORCE_MODE FORCE_MODE_TX_FC | FORCE_MODE_RX_FC | \ - FORCE_MODE_DPX | FORCE_MODE_SPD | \ - FORCE_MODE_LNK - -/* MT7531 SGMII Registers */ -#define MT7531_SGMII_REG_BASE 0x5000 -#define MT7531_SGMII_REG_PORT_BASE 0x1000 -#define MT7531_SGMII_REG(p, r) (MT7531_SGMII_REG_BASE + \ - (p) * MT7531_SGMII_REG_PORT_BASE + (r)) -#define MT7531_PCS_CONTROL_1(p) MT7531_SGMII_REG(((p) - 5), 0x00) -#define MT7531_SGMII_MODE(p) MT7531_SGMII_REG(((p) - 5), 0x20) -#define MT7531_QPHY_PWR_STATE_CTRL(p) MT7531_SGMII_REG(((p) - 5), 0xe8) -#define MT7531_PHYA_CTRL_SIGNAL3(p) MT7531_SGMII_REG(((p) - 5), 0x128) -/* XXX: all fields of MT7531 SGMII are defined under SGMSYS */ - -/* MT753x System Control Register */ -#define SYS_CTRL_REG 0x7000 -#define SW_PHY_RST BIT(2) -#define SW_SYS_RST BIT(1) -#define SW_REG_RST BIT(0) - -/* MT7531 */ -#define MT7531_PHY_IAC 0x701c -/* XXX: all fields are defined under GMAC_PIAC_REG */ - -#define MT7531_CLKGEN_CTRL 0x7500 -#define CLK_SKEW_OUT_S 8 -#define CLK_SKEW_OUT_M 0x300 -#define CLK_SKEW_IN_S 6 -#define CLK_SKEW_IN_M 0xc0 -#define RXCLK_NO_DELAY BIT(5) -#define TXCLK_NO_REVERSE BIT(4) -#define GP_MODE_S 1 -#define GP_MODE_M 0x06 -#define GP_CLK_EN BIT(0) - -/* Values of GP_MODE */ -#define GP_MODE_RGMII 0 -#define GP_MODE_MII 1 -#define GP_MODE_REV_MII 2 - -/* Values of CLK_SKEW_IN */ -#define CLK_SKEW_IN_NO_CHANGE 0 -#define CLK_SKEW_IN_DELAY_100PPS 1 -#define CLK_SKEW_IN_DELAY_200PPS 2 -#define CLK_SKEW_IN_REVERSE 3 - -/* Values of CLK_SKEW_OUT */ -#define CLK_SKEW_OUT_NO_CHANGE 0 -#define CLK_SKEW_OUT_DELAY_100PPS 1 -#define CLK_SKEW_OUT_DELAY_200PPS 2 -#define CLK_SKEW_OUT_REVERSE 3 - -#define HWTRAP_REG 0x7800 -/* MT7530 Modified Hardware Trap Status Registers */ -#define MHWTRAP_REG 0x7804 -#define CHG_TRAP BIT(16) -#define LOOPDET_DIS BIT(14) -#define P5_INTF_SEL_S 13 -#define P5_INTF_SEL_M 0x2000 -#define SMI_ADDR_S 11 -#define SMI_ADDR_M 0x1800 -#define XTAL_FSEL_S 9 -#define XTAL_FSEL_M 0x600 -#define P6_INTF_DIS BIT(8) -#define P5_INTF_MODE_S 7 -#define P5_INTF_MODE_M 0x80 -#define P5_INTF_DIS BIT(6) -#define C_MDIO_BPS BIT(5) -#define CHIP_MODE_S 0 -#define CHIP_MODE_M 0x0f - -/* P5_INTF_SEL: Interface type of Port5 */ -#define P5_INTF_SEL_GPHY 0 -#define P5_INTF_SEL_GMAC5 1 - -/* P5_INTF_MODE: Interface mode of Port5 */ -#define P5_INTF_MODE_GMII_MII 0 -#define P5_INTF_MODE_RGMII 1 - -#define MT7530_P6ECR 0x7830 -#define P6_INTF_MODE_M 0x3 -#define P6_INTF_MODE_S 0 - -/* P6_INTF_MODE: Interface mode of Port6 */ -#define P6_INTF_MODE_RGMII 0 -#define P6_INTF_MODE_TRGMII 1 - -#define NUM_TRGMII_CTRL 5 - -#define MT7530_TRGMII_RD(n) (0x7a10 + (n) * 8) -#define RD_TAP_S 0 -#define RD_TAP_M 0x7f - -#define MT7530_TRGMII_TD_ODT(n) (0x7a54 + (n) * 8) -/* XXX: all fields are defined under GMAC_TRGMII_TD_ODT */ - -/* TOP Signals Status Register */ -#define MT7531_TOP_SIG_SR 0x780c -#define PAD_MCM_SMI_EN BIT(0) -#define PAD_DUAL_SGMII_EN BIT(1) - -/* MT7531 PLLGP Registers */ -#define MT7531_PLLGP_EN 0x7820 -#define EN_COREPLL BIT(2) -#define SW_CLKSW BIT(1) -#define SW_PLLGP BIT(0) - -#define MT7531_PLLGP_CR0 0x78a8 -#define RG_COREPLL_EN BIT(22) -#define RG_COREPLL_POSDIV_S 23 -#define RG_COREPLL_POSDIV_M 0x3800000 -#define RG_COREPLL_SDM_PCW_S 1 -#define RG_COREPLL_SDM_PCW_M 0x3ffffe -#define RG_COREPLL_SDM_PCW_CHG BIT(0) - -/* MT7531 RGMII and SGMII PLL clock */ -#define MT7531_ANA_PLLGP_CR2 0x78b0 -#define MT7531_ANA_PLLGP_CR5 0x78bc - -/* MT7531 GPIO GROUP IOLB SMT0 Control */ -#define MT7531_SMT0_IOLB 0x7f04 -#define SMT_IOLB_5_SMI_MDC_EN BIT(5) - -/* MT7530 GPHY MDIO Indirect Access Registers */ +/* MDIO Indirect Access Registers */ #define MII_MMD_ACC_CTL_REG 0x0d #define MMD_CMD_S 14 #define MMD_CMD_M 0xc000 @@ -460,80 +350,6 @@ enum mkt_eth_capabilities { #define MII_MMD_ADDR_DATA_REG 0x0e -/* MT7530 GPHY MDIO MMD Registers */ -#define CORE_PLL_GROUP2 0x401 -#define RG_SYSPLL_EN_NORMAL BIT(15) -#define RG_SYSPLL_VODEN BIT(14) -#define RG_SYSPLL_POSDIV_S 5 -#define RG_SYSPLL_POSDIV_M 0x60 - -#define CORE_PLL_GROUP4 0x403 -#define MT7531_BYPASS_MODE BIT(4) -#define MT7531_POWER_ON_OFF BIT(5) -#define RG_SYSPLL_DDSFBK_EN BIT(12) -#define RG_SYSPLL_BIAS_EN BIT(11) -#define RG_SYSPLL_BIAS_LPF_EN BIT(10) - -#define CORE_PLL_GROUP5 0x404 -#define RG_LCDDS_PCW_NCPO1_S 0 -#define RG_LCDDS_PCW_NCPO1_M 0xffff - -#define CORE_PLL_GROUP6 0x405 -#define RG_LCDDS_PCW_NCPO0_S 0 -#define RG_LCDDS_PCW_NCPO0_M 0xffff - -#define CORE_PLL_GROUP7 0x406 -#define RG_LCDDS_PWDB BIT(15) -#define RG_LCDDS_ISO_EN BIT(13) -#define RG_LCCDS_C_S 4 -#define RG_LCCDS_C_M 0x70 -#define RG_LCDDS_PCW_NCPO_CHG BIT(3) - -#define CORE_PLL_GROUP10 0x409 -#define RG_LCDDS_SSC_DELTA_S 0 -#define RG_LCDDS_SSC_DELTA_M 0xfff - -#define CORE_PLL_GROUP11 0x40a -#define RG_LCDDS_SSC_DELTA1_S 0 -#define RG_LCDDS_SSC_DELTA1_M 0xfff - -#define CORE_GSWPLL_GRP1 0x40d -#define RG_GSWPLL_POSDIV_200M_S 12 -#define RG_GSWPLL_POSDIV_200M_M 0x3000 -#define RG_GSWPLL_EN_PRE BIT(11) -#define RG_GSWPLL_FBKDIV_200M_S 0 -#define RG_GSWPLL_FBKDIV_200M_M 0xff - -#define CORE_GSWPLL_GRP2 0x40e -#define RG_GSWPLL_POSDIV_500M_S 8 -#define RG_GSWPLL_POSDIV_500M_M 0x300 -#define RG_GSWPLL_FBKDIV_500M_S 0 -#define RG_GSWPLL_FBKDIV_500M_M 0xff - -#define CORE_TRGMII_GSW_CLK_CG 0x410 -#define REG_GSWCK_EN BIT(0) -#define REG_TRGMIICK_EN BIT(1) - -/* Extend PHY Control Register 3 */ -#define PHY_EXT_REG_14 0x14 - -/* Fields of PHY_EXT_REG_14 */ -#define PHY_EN_DOWN_SHFIT BIT(4) - -/* Extend PHY Control Register 4 */ -#define PHY_EXT_REG_17 0x17 - -/* Fields of PHY_EXT_REG_17 */ -#define PHY_LINKDOWN_POWER_SAVING_EN BIT(4) - -/* PHY RXADC Control Register 7 */ -#define PHY_DEV1E_REG_0C6 0x0c6 - -/* Fields of PHY_DEV1E_REG_0C6 */ -#define PHY_POWER_SAVING_S 8 -#define PHY_POWER_SAVING_M 0x300 -#define PHY_POWER_SAVING_TX 0x0 - /* PDMA descriptors */ struct mtk_rx_dma { unsigned int rxd1; @@ -597,4 +413,17 @@ struct mtk_tx_dma_v2 { #define PDMA_V2_RXD2_PLEN0_GET(_v) FIELD_GET(PDMA_V2_RXD2_PLEN0_M, (_v)) #define PDMA_V2_RXD2_PLEN0_SET(_v) FIELD_PREP(PDMA_V2_RXD2_PLEN0_M, (_v)) +void mtk_fe_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set); +void mtk_gmac_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set); +void mtk_ethsys_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set); + +int mtk_mii_read(struct mtk_eth_priv *priv, u8 phy, u8 reg); +int mtk_mii_write(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 data); +int mtk_mmd_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg); +int mtk_mmd_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg, + u16 val); +int mtk_mmd_ind_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg); +int mtk_mmd_ind_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg, + u16 val); + #endif /* _MTK_ETH_H_ */ diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 13e73810ad6..3132718e4f8 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -2,10 +2,6 @@ config BITBANGMII bool "Bit-banged ethernet MII management channel support" -config BITBANGMII_MULTI - bool "Enable the multi bus support" - depends on BITBANGMII - config MV88E6352_SWITCH bool "Marvell 88E6352 switch support" diff --git a/drivers/net/phy/miiphybb.c b/drivers/net/phy/miiphybb.c index b143137893f..9f5f9b12c9f 100644 --- a/drivers/net/phy/miiphybb.c +++ b/drivers/net/phy/miiphybb.c @@ -17,90 +17,6 @@ #include <miiphy.h> #include <asm/global_data.h> -#ifndef CONFIG_BITBANGMII_MULTI - -/* - * If CONFIG_BITBANGMII_MULTI is not defined we use a - * compatibility layer with the previous miiphybb implementation - * based on macros usage. - * - */ -static int bb_mii_init_wrap(struct bb_miiphy_bus *bus) -{ -#ifdef MII_INIT - MII_INIT; -#endif - return 0; -} - -static int bb_mdio_active_wrap(struct bb_miiphy_bus *bus) -{ -#ifdef MDIO_DECLARE - MDIO_DECLARE; -#endif - MDIO_ACTIVE; - return 0; -} - -static int bb_mdio_tristate_wrap(struct bb_miiphy_bus *bus) -{ -#ifdef MDIO_DECLARE - MDIO_DECLARE; -#endif - MDIO_TRISTATE; - return 0; -} - -static int bb_set_mdio_wrap(struct bb_miiphy_bus *bus, int v) -{ -#ifdef MDIO_DECLARE - MDIO_DECLARE; -#endif - MDIO(v); - return 0; -} - -static int bb_get_mdio_wrap(struct bb_miiphy_bus *bus, int *v) -{ -#ifdef MDIO_DECLARE - MDIO_DECLARE; -#endif - *v = MDIO_READ; - return 0; -} - -static int bb_set_mdc_wrap(struct bb_miiphy_bus *bus, int v) -{ -#ifdef MDC_DECLARE - MDC_DECLARE; -#endif - MDC(v); - return 0; -} - -static int bb_delay_wrap(struct bb_miiphy_bus *bus) -{ - MIIDELAY; - return 0; -} - -struct bb_miiphy_bus bb_miiphy_buses[] = { - { - .name = BB_MII_DEVNAME, - .init = bb_mii_init_wrap, - .mdio_active = bb_mdio_active_wrap, - .mdio_tristate = bb_mdio_tristate_wrap, - .set_mdio = bb_set_mdio_wrap, - .get_mdio = bb_get_mdio_wrap, - .set_mdc = bb_set_mdc_wrap, - .delay = bb_delay_wrap, - } -}; - -int bb_miiphy_buses_num = sizeof(bb_miiphy_buses) / - sizeof(bb_miiphy_buses[0]); -#endif - int bb_miiphy_init(void) { int i; @@ -114,7 +30,6 @@ int bb_miiphy_init(void) static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname) { -#ifdef CONFIG_BITBANGMII_MULTI int i; /* Search the correct bus */ @@ -124,10 +39,6 @@ static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname) } } return NULL; -#else - /* We have just one bitbanging bus */ - return &bb_miiphy_buses[0]; -#endif } /***************************************************************************** @@ -272,9 +183,7 @@ int bb_miiphy_read(struct mii_dev *miidev, int addr, int devad, int reg) bus->set_mdc(bus, 1); bus->delay(bus); -#ifdef DEBUG - printf("miiphy_read(0x%x) @ 0x%x = 0x%04x\n", reg, addr, rdreg); -#endif + debug("%s[%s](0x%x) @ 0x%x = 0x%04x\n", __func__, miidev->name, reg, addr, rdreg); return rdreg; } diff --git a/drivers/net/phy/ti_phy_init.c b/drivers/net/phy/ti_phy_init.c index a0878193ac0..850c0cbec96 100644 --- a/drivers/net/phy/ti_phy_init.c +++ b/drivers/net/phy/ti_phy_init.c @@ -10,12 +10,58 @@ #include <phy.h> #include "ti_phy_init.h" +#define DP83822_DEVADDR 0x1f + +#define MII_DP83822_RCSR 0x17 + +/* RCSR bits */ +#define DP83822_RX_CLK_SHIFT BIT(12) +#define DP83822_TX_CLK_SHIFT BIT(11) + +/* DP83822 specific RGMII RX/TX delay configuration. */ +static int dp83822_config(struct phy_device *phydev) +{ + ofnode node = phy_get_ofnode(phydev); + u32 rgmii_delay = 0; + u32 rx_delay = 0; + u32 tx_delay = 0; + int ret; + + ret = ofnode_read_u32(node, "rx-internal-delay-ps", &rx_delay); + if (ret) { + rx_delay = phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID; + } + + ret = ofnode_read_u32(node, "tx-internal-delay-ps", &tx_delay); + if (ret) { + tx_delay = phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID; + } + + /* Bit set means Receive path internal clock shift is ENABLED */ + if (rx_delay) + rgmii_delay |= DP83822_RX_CLK_SHIFT; + + /* Bit set means Transmit path internal clock shift is DISABLED */ + if (!tx_delay) + rgmii_delay |= DP83822_TX_CLK_SHIFT; + + ret = phy_modify_mmd(phydev, DP83822_DEVADDR, MII_DP83822_RCSR, + DP83822_RX_CLK_SHIFT | DP83822_TX_CLK_SHIFT, + rgmii_delay); + if (ret) + return ret; + + return genphy_config_aneg(phydev); +} + U_BOOT_PHY_DRIVER(dp83822) = { .name = "TI DP83822", .uid = 0x2000a240, .mask = 0xfffffff0, .features = PHY_BASIC_FEATURES, - .config = &genphy_config_aneg, + .config = &dp83822_config, .startup = &genphy_startup, .shutdown = &genphy_shutdown, }; diff --git a/drivers/net/ravb.c b/drivers/net/ravb.c index 231764e60b5..7286ad19598 100644 --- a/drivers/net/ravb.c +++ b/drivers/net/ravb.c @@ -560,12 +560,12 @@ static int ravb_remove(struct udevice *dev) return 0; } -int ravb_bb_init(struct bb_miiphy_bus *bus) +static int ravb_bb_init(struct bb_miiphy_bus *bus) { return 0; } -int ravb_bb_mdio_active(struct bb_miiphy_bus *bus) +static int ravb_bb_mdio_active(struct bb_miiphy_bus *bus) { struct ravb_priv *eth = bus->priv; @@ -574,7 +574,7 @@ int ravb_bb_mdio_active(struct bb_miiphy_bus *bus) return 0; } -int ravb_bb_mdio_tristate(struct bb_miiphy_bus *bus) +static int ravb_bb_mdio_tristate(struct bb_miiphy_bus *bus) { struct ravb_priv *eth = bus->priv; @@ -583,7 +583,7 @@ int ravb_bb_mdio_tristate(struct bb_miiphy_bus *bus) return 0; } -int ravb_bb_set_mdio(struct bb_miiphy_bus *bus, int v) +static int ravb_bb_set_mdio(struct bb_miiphy_bus *bus, int v) { struct ravb_priv *eth = bus->priv; @@ -595,7 +595,7 @@ int ravb_bb_set_mdio(struct bb_miiphy_bus *bus, int v) return 0; } -int ravb_bb_get_mdio(struct bb_miiphy_bus *bus, int *v) +static int ravb_bb_get_mdio(struct bb_miiphy_bus *bus, int *v) { struct ravb_priv *eth = bus->priv; @@ -604,7 +604,7 @@ int ravb_bb_get_mdio(struct bb_miiphy_bus *bus, int *v) return 0; } -int ravb_bb_set_mdc(struct bb_miiphy_bus *bus, int v) +static int ravb_bb_set_mdc(struct bb_miiphy_bus *bus, int v) { struct ravb_priv *eth = bus->priv; @@ -616,7 +616,7 @@ int ravb_bb_set_mdc(struct bb_miiphy_bus *bus, int v) return 0; } -int ravb_bb_delay(struct bb_miiphy_bus *bus) +static int ravb_bb_delay(struct bb_miiphy_bus *bus) { udelay(10); |