From 028ed86f08a4fdf25213af5f5afd63b30fb7b029 Mon Sep 17 00:00:00 2001 From: Lei Wei Date: Thu, 29 Feb 2024 16:59:53 +0800 Subject: [PATCH 32/50] net: ethernet: qualcomm: Add phylink support for PPE MAC ports Add MAC initialization and phylink functions for PPE MAC ports. Change-Id: I39dcba671732392bcfa2e734473fd083989bfbec Signed-off-by: Lei Wei --- drivers/net/ethernet/qualcomm/Kconfig | 3 + drivers/net/ethernet/qualcomm/ppe/Makefile | 2 +- drivers/net/ethernet/qualcomm/ppe/ppe.c | 9 + drivers/net/ethernet/qualcomm/ppe/ppe.h | 2 + drivers/net/ethernet/qualcomm/ppe/ppe_port.c | 728 +++++++++++++++++++ drivers/net/ethernet/qualcomm/ppe/ppe_port.h | 76 ++ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h | 123 ++++ 7 files changed, 942 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/qualcomm/ppe/ppe_port.c create mode 100644 drivers/net/ethernet/qualcomm/ppe/ppe_port.h diff --git a/drivers/net/ethernet/qualcomm/Kconfig b/drivers/net/ethernet/qualcomm/Kconfig index 8cc24da48777..a96f6acd4561 100644 --- a/drivers/net/ethernet/qualcomm/Kconfig +++ b/drivers/net/ethernet/qualcomm/Kconfig @@ -66,6 +66,9 @@ config QCOM_PPE depends on HAS_IOMEM && OF depends on COMMON_CLK select REGMAP_MMIO + select PHYLINK + select PCS_QCOM_IPQ_UNIPHY + select SFP help This driver supports the Qualcomm Technologies, Inc. packet process engine (PPE) available with IPQ SoC. The PPE houses diff --git a/drivers/net/ethernet/qualcomm/ppe/Makefile b/drivers/net/ethernet/qualcomm/ppe/Makefile index 227af2168224..76cdc423a8cc 100644 --- a/drivers/net/ethernet/qualcomm/ppe/Makefile +++ b/drivers/net/ethernet/qualcomm/ppe/Makefile @@ -4,4 +4,4 @@ # obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o -qcom-ppe-objs := ppe.o ppe_config.o ppe_api.o ppe_debugfs.o +qcom-ppe-objs := ppe.o ppe_config.o ppe_api.o ppe_debugfs.o ppe_port.o diff --git a/drivers/net/ethernet/qualcomm/ppe/ppe.c b/drivers/net/ethernet/qualcomm/ppe/ppe.c index 8cf6c1161c4b..bcf21c838e05 100644 --- a/drivers/net/ethernet/qualcomm/ppe/ppe.c +++ b/drivers/net/ethernet/qualcomm/ppe/ppe.c @@ -17,6 +17,7 @@ #include "ppe.h" #include "ppe_config.h" #include "ppe_debugfs.h" +#include "ppe_port.h" #define PPE_PORT_MAX 8 #define PPE_CLK_RATE 353000000 @@ -207,6 +208,11 @@ static int qcom_ppe_probe(struct platform_device *pdev) if (ret) return dev_err_probe(dev, ret, "PPE HW config failed\n"); + ret = ppe_port_mac_init(ppe_dev); + if (ret) + return dev_err_probe(dev, ret, + "PPE Port MAC initialization failed\n"); + ppe_debugfs_setup(ppe_dev); platform_set_drvdata(pdev, ppe_dev); @@ -219,6 +225,9 @@ static void qcom_ppe_remove(struct platform_device *pdev) ppe_dev = platform_get_drvdata(pdev); ppe_debugfs_teardown(ppe_dev); + ppe_port_mac_deinit(ppe_dev); + + platform_set_drvdata(pdev, NULL); } static const struct of_device_id qcom_ppe_of_match[] = { diff --git a/drivers/net/ethernet/qualcomm/ppe/ppe.h b/drivers/net/ethernet/qualcomm/ppe/ppe.h index a2a5d1901547..020d5df2c5e3 100644 --- a/drivers/net/ethernet/qualcomm/ppe/ppe.h +++ b/drivers/net/ethernet/qualcomm/ppe/ppe.h @@ -20,6 +20,7 @@ struct dentry; * @clk_rate: PPE clock rate. * @num_ports: Number of PPE ports. * @debugfs_root: PPE debug root entry. + * @ports: PPE MAC ports. * @num_icc_paths: Number of interconnect paths. * @icc_paths: Interconnect path array. * @@ -33,6 +34,7 @@ struct ppe_device { unsigned long clk_rate; unsigned int num_ports; struct dentry *debugfs_root; + struct ppe_ports *ports; unsigned int num_icc_paths; struct icc_bulk_data icc_paths[] __counted_by(num_icc_paths); }; diff --git a/drivers/net/ethernet/qualcomm/ppe/ppe_port.c b/drivers/net/ethernet/qualcomm/ppe/ppe_port.c new file mode 100644 index 000000000000..dcc13889089e --- /dev/null +++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.c @@ -0,0 +1,728 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +/* PPE Port MAC initialization and PPE port MAC functions. */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ppe.h" +#include "ppe_port.h" +#include "ppe_regs.h" + +/* PPE MAC max frame size which including 4bytes FCS */ +#define PPE_PORT_MAC_MAX_FRAME_SIZE 0x3000 + +/* PPE BM port start for PPE MAC ports */ +#define PPE_BM_PORT_MAC_START 7 + +/* PPE port clock and reset name */ +static const char * const ppe_port_clk_rst_name[] = { + [PPE_PORT_CLK_RST_MAC] = "port_mac", + [PPE_PORT_CLK_RST_RX] = "port_rx", + [PPE_PORT_CLK_RST_TX] = "port_tx", +}; + +/* PPE port and MAC reset */ +static int ppe_port_mac_reset(struct ppe_port *ppe_port) +{ + struct ppe_device *ppe_dev = ppe_port->ppe_dev; + int ret; + + ret = reset_control_assert(ppe_port->rstcs[PPE_PORT_CLK_RST_MAC]); + if (ret) + goto error; + + ret = reset_control_assert(ppe_port->rstcs[PPE_PORT_CLK_RST_RX]); + if (ret) + goto error; + + ret = reset_control_assert(ppe_port->rstcs[PPE_PORT_CLK_RST_TX]); + if (ret) + goto error; + + /* 150ms delay is required by hardware to reset PPE port and MAC */ + msleep(150); + + ret = reset_control_deassert(ppe_port->rstcs[PPE_PORT_CLK_RST_MAC]); + if (ret) + goto error; + + ret = reset_control_deassert(ppe_port->rstcs[PPE_PORT_CLK_RST_RX]); + if (ret) + goto error; + + ret = reset_control_deassert(ppe_port->rstcs[PPE_PORT_CLK_RST_TX]); + if (ret) + goto error; + + return ret; + +error: + dev_err(ppe_dev->dev, "%s: port %d reset fail %d\n", + __func__, ppe_port->port_id, ret); + return ret; +} + +/* PPE port MAC configuration for phylink */ +static void ppe_port_mac_config(struct phylink_config *config, + unsigned int mode, + const struct phylink_link_state *state) +{ + struct ppe_port *ppe_port = container_of(config, struct ppe_port, + phylink_config); + struct ppe_device *ppe_dev = ppe_port->ppe_dev; + int port = ppe_port->port_id; + enum ppe_mac_type mac_type; + u32 val, mask; + int ret; + + switch (state->interface) { + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_10G_QXGMII: + mac_type = PPE_MAC_TYPE_XGMAC; + break; + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_PSGMII: + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + mac_type = PPE_MAC_TYPE_GMAC; + break; + default: + dev_err(ppe_dev->dev, "%s: Unsupport interface %s\n", + __func__, phy_modes(state->interface)); + return; + } + + /* Reset Port MAC for GMAC */ + if (mac_type == PPE_MAC_TYPE_GMAC) { + ret = ppe_port_mac_reset(ppe_port); + if (ret) + goto err_mac_config; + } + + /* Port mux to select GMAC or XGMAC */ + mask = PPE_PORT_SEL_XGMAC(port); + val = mac_type == PPE_MAC_TYPE_GMAC ? 0 : mask; + ret = regmap_update_bits(ppe_dev->regmap, + PPE_PORT_MUX_CTRL_ADDR, + mask, val); + if (ret) + goto err_mac_config; + + ppe_port->mac_type = mac_type; + + return; + +err_mac_config: + dev_err(ppe_dev->dev, "%s: port %d MAC config fail %d\n", + __func__, port, ret); +} + +/* PPE port GMAC link up configuration */ +static int ppe_port_gmac_link_up(struct ppe_port *ppe_port, int speed, + int duplex, bool tx_pause, bool rx_pause) +{ + struct ppe_device *ppe_dev = ppe_port->ppe_dev; + int ret, port = ppe_port->port_id; + u32 reg, val; + + /* Set GMAC speed */ + switch (speed) { + case SPEED_1000: + val = GMAC_SPEED_1000; + break; + case SPEED_100: + val = GMAC_SPEED_100; + break; + case SPEED_10: + val = GMAC_SPEED_10; + break; + default: + dev_err(ppe_dev->dev, "%s: Invalid GMAC speed %s\n", + __func__, phy_speed_to_str(speed)); + return -EINVAL; + } + + reg = PPE_PORT_GMAC_ADDR(port); + ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_SPEED_ADDR, + GMAC_SPEED_M, val); + if (ret) + return ret; + + /* Set duplex, flow control and enable GMAC */ + val = GMAC_TRXEN; + if (duplex == DUPLEX_FULL) + val |= GMAC_DUPLEX_FULL; + if (tx_pause) + val |= GMAC_TXFCEN; + if (rx_pause) + val |= GMAC_RXFCEN; + + ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_ENABLE_ADDR, + GMAC_ENABLE_ALL, val); + + return ret; +} + +/* PPE port XGMAC link up configuration */ +static int ppe_port_xgmac_link_up(struct ppe_port *ppe_port, + phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct ppe_device *ppe_dev = ppe_port->ppe_dev; + int ret, port = ppe_port->port_id; + u32 reg, val; + + /* Set XGMAC TX speed and enable TX */ + switch (speed) { + case SPEED_10000: + if (interface == PHY_INTERFACE_MODE_USXGMII) + val = XGMAC_SPEED_10000_USXGMII; + else + val = XGMAC_SPEED_10000; + break; + case SPEED_5000: + val = XGMAC_SPEED_5000; + break; + case SPEED_2500: + if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10G_QXGMII) + val = XGMAC_SPEED_2500_USXGMII; + else + val = XGMAC_SPEED_2500; + break; + case SPEED_1000: + val = XGMAC_SPEED_1000; + break; + case SPEED_100: + val = XGMAC_SPEED_100; + break; + case SPEED_10: + val = XGMAC_SPEED_10; + break; + default: + dev_err(ppe_dev->dev, "%s: Invalid XGMAC speed %s\n", + __func__, phy_speed_to_str(speed)); + return -EINVAL; + } + + reg = PPE_PORT_XGMAC_ADDR(port); + val |= XGMAC_TXEN; + ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_TX_CONFIG_ADDR, + XGMAC_SPEED_M | XGMAC_TXEN, val); + if (ret) + return ret; + + /* Set XGMAC TX flow control */ + val = FIELD_PREP(XGMAC_PAUSE_TIME_M, FIELD_MAX(XGMAC_PAUSE_TIME_M)); + val |= tx_pause ? XGMAC_TXFCEN : 0; + ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_TX_FLOW_CTRL_ADDR, + XGMAC_PAUSE_TIME_M | XGMAC_TXFCEN, val); + if (ret) + return ret; + + /* Set XGMAC RX flow control */ + val = rx_pause ? XGMAC_RXFCEN : 0; + ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_RX_FLOW_CTRL_ADDR, + XGMAC_RXFCEN, val); + if (ret) + return ret; + + /* Enable XGMAC RX*/ + ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_RX_CONFIG_ADDR, + XGMAC_RXEN, XGMAC_RXEN); + + return ret; +} + +/* PPE port MAC link up configuration for phylink */ +static void ppe_port_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, + phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct ppe_port *ppe_port = container_of(config, struct ppe_port, + phylink_config); + enum ppe_mac_type mac_type = ppe_port->mac_type; + struct ppe_device *ppe_dev = ppe_port->ppe_dev; + int ret, port = ppe_port->port_id; + u32 reg, val; + + if (mac_type == PPE_MAC_TYPE_GMAC) + ret = ppe_port_gmac_link_up(ppe_port, + speed, duplex, tx_pause, rx_pause); + else + ret = ppe_port_xgmac_link_up(ppe_port, interface, + speed, duplex, tx_pause, rx_pause); + if (ret) + goto err_port_mac_link_up; + + /* Set PPE port BM flow control */ + reg = PPE_BM_PORT_FC_MODE_ADDR + + PPE_BM_PORT_FC_MODE_INC * (port + PPE_BM_PORT_MAC_START); + val = tx_pause ? PPE_BM_PORT_FC_MODE_EN : 0; + ret = regmap_update_bits(ppe_dev->regmap, reg, + PPE_BM_PORT_FC_MODE_EN, val); + if (ret) + goto err_port_mac_link_up; + + /* Enable PPE port TX */ + reg = PPE_PORT_BRIDGE_CTRL_ADDR + PPE_PORT_BRIDGE_CTRL_INC * port; + ret = regmap_update_bits(ppe_dev->regmap, reg, + PPE_PORT_BRIDGE_TXMAC_EN, + PPE_PORT_BRIDGE_TXMAC_EN); + if (ret) + goto err_port_mac_link_up; + + return; + +err_port_mac_link_up: + dev_err(ppe_dev->dev, "%s: port %d link up fail %d\n", + __func__, port, ret); +} + +/* PPE port MAC link down configuration for phylink */ +static void ppe_port_mac_link_down(struct phylink_config *config, + unsigned int mode, + phy_interface_t interface) +{ + struct ppe_port *ppe_port = container_of(config, struct ppe_port, + phylink_config); + enum ppe_mac_type mac_type = ppe_port->mac_type; + struct ppe_device *ppe_dev = ppe_port->ppe_dev; + int ret, port = ppe_port->port_id; + u32 reg; + + /* Disable PPE port TX */ + reg = PPE_PORT_BRIDGE_CTRL_ADDR + PPE_PORT_BRIDGE_CTRL_INC * port; + ret = regmap_update_bits(ppe_dev->regmap, reg, + PPE_PORT_BRIDGE_TXMAC_EN, 0); + if (ret) + goto err_port_mac_link_down; + + /* Disable PPE MAC */ + if (mac_type == PPE_MAC_TYPE_GMAC) { + reg = PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE_ADDR; + ret = regmap_update_bits(ppe_dev->regmap, reg, GMAC_TRXEN, 0); + if (ret) + goto err_port_mac_link_down; + } else { + reg = PPE_PORT_XGMAC_ADDR(port); + ret = regmap_update_bits(ppe_dev->regmap, + reg + XGMAC_RX_CONFIG_ADDR, + XGMAC_RXEN, 0); + if (ret) + goto err_port_mac_link_down; + + ret = regmap_update_bits(ppe_dev->regmap, + reg + XGMAC_TX_CONFIG_ADDR, + XGMAC_TXEN, 0); + if (ret) + goto err_port_mac_link_down; + } + + return; + +err_port_mac_link_down: + dev_err(ppe_dev->dev, "%s: port %d link down fail %d\n", + __func__, port, ret); +} + +/* PPE port MAC PCS selection for phylink */ +static +struct phylink_pcs *ppe_port_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct ppe_port *ppe_port = container_of(config, struct ppe_port, + phylink_config); + struct ppe_device *ppe_dev = ppe_port->ppe_dev; + int ret, port = ppe_port->port_id; + u32 val; + + /* PPE port5 can connects with PCS0 or PCS1. In PSGMII + * mode, it selects PCS0; otherwise, it selects PCS1. + */ + if (port == 5) { + val = interface == PHY_INTERFACE_MODE_PSGMII ? + 0 : PPE_PORT5_SEL_PCS1; + ret = regmap_update_bits(ppe_dev->regmap, + PPE_PORT_MUX_CTRL_ADDR, + PPE_PORT5_SEL_PCS1, val); + if (ret) { + dev_err(ppe_dev->dev, "%s: port5 select PCS fail %d\n", + __func__, ret); + return NULL; + } + } + + return ppe_port->pcs; +} + +static const struct phylink_mac_ops ppe_phylink_ops = { + .mac_config = ppe_port_mac_config, + .mac_link_up = ppe_port_mac_link_up, + .mac_link_down = ppe_port_mac_link_down, + .mac_select_pcs = ppe_port_mac_select_pcs, +}; + +/** + * ppe_port_phylink_setup() - Set phylink instance for the given PPE port + * @ppe_port: PPE port + * @netdev: Netdevice + * + * Description: Wrapper function to help setup phylink for the PPE port + * specified by @ppe_port and associated with the net device @netdev. + * + * Return: 0 upon success or a negative error upon failure. + */ +int ppe_port_phylink_setup(struct ppe_port *ppe_port, struct net_device *netdev) +{ + struct ppe_device *ppe_dev = ppe_port->ppe_dev; + struct device_node *pcs_node; + int ret; + + /* Create PCS */ + pcs_node = of_parse_phandle(ppe_port->np, "pcs-handle", 0); + if (!pcs_node) + return -ENODEV; + + ppe_port->pcs = ipq_unipcs_create(pcs_node); + of_node_put(pcs_node); + if (IS_ERR(ppe_port->pcs)) { + dev_err(ppe_dev->dev, "%s: port %d failed to create PCS\n", + __func__, ppe_port->port_id); + return PTR_ERR(ppe_port->pcs); + } + + /* Port phylink capability */ + ppe_port->phylink_config.dev = &netdev->dev; + ppe_port->phylink_config.type = PHYLINK_NETDEV; + ppe_port->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | + MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000 | + MAC_2500FD | MAC_5000FD | MAC_10000FD; + __set_bit(PHY_INTERFACE_MODE_QSGMII, + ppe_port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_PSGMII, + ppe_port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_SGMII, + ppe_port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + ppe_port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + ppe_port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_USXGMII, + ppe_port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10GBASER, + ppe_port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10G_QXGMII, + ppe_port->phylink_config.supported_interfaces); + + /* Create phylink */ + ppe_port->phylink = phylink_create(&ppe_port->phylink_config, + of_fwnode_handle(ppe_port->np), + ppe_port->interface, + &ppe_phylink_ops); + if (IS_ERR(ppe_port->phylink)) { + dev_err(ppe_dev->dev, "%s: port %d failed to create phylink\n", + __func__, ppe_port->port_id); + ret = PTR_ERR(ppe_port->phylink); + goto err_free_pcs; + } + + /* Connect phylink */ + ret = phylink_of_phy_connect(ppe_port->phylink, ppe_port->np, 0); + if (ret) { + dev_err(ppe_dev->dev, "%s: port %d failed to connect phylink\n", + __func__, ppe_port->port_id); + goto err_free_phylink; + } + + return 0; + +err_free_phylink: + phylink_destroy(ppe_port->phylink); + ppe_port->phylink = NULL; +err_free_pcs: + ipq_unipcs_destroy(ppe_port->pcs); + ppe_port->pcs = NULL; + return ret; +} + +/** + * ppe_port_phylink_destroy() - Destroy phylink instance for the given PPE port + * @ppe_port: PPE port + * + * Description: Wrapper function to help destroy phylink for the PPE port + * specified by @ppe_port. + */ +void ppe_port_phylink_destroy(struct ppe_port *ppe_port) +{ + /* Destroy phylink */ + if (ppe_port->phylink) { + rtnl_lock(); + phylink_disconnect_phy(ppe_port->phylink); + rtnl_unlock(); + phylink_destroy(ppe_port->phylink); + ppe_port->phylink = NULL; + } + + /* Destroy PCS */ + if (ppe_port->pcs) { + ipq_unipcs_destroy(ppe_port->pcs); + ppe_port->pcs = NULL; + } +} + +/* PPE port clock initialization */ +static int ppe_port_clock_init(struct ppe_port *ppe_port) +{ + struct device_node *port_node = ppe_port->np; + struct reset_control *rstc; + struct clk *clk; + int i, j, ret; + + for (i = 0; i < PPE_PORT_CLK_RST_MAX; i++) { + /* Get PPE port resets which will be used to reset PPE + * port and MAC. + */ + rstc = of_reset_control_get_exclusive(port_node, + ppe_port_clk_rst_name[i]); + if (IS_ERR(rstc)) { + ret = PTR_ERR(rstc); + goto err_rst; + } + + clk = of_clk_get_by_name(port_node, ppe_port_clk_rst_name[i]); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto err_clk_get; + } + + ret = clk_prepare_enable(clk); + if (ret) + goto err_clk_en; + + ppe_port->clks[i] = clk; + ppe_port->rstcs[i] = rstc; + } + + return 0; + +err_clk_en: + clk_put(clk); +err_clk_get: + reset_control_put(rstc); +err_rst: + for (j = 0; j < i; j++) { + clk_disable_unprepare(ppe_port->clks[j]); + clk_put(ppe_port->clks[j]); + reset_control_put(ppe_port->rstcs[j]); + } + + return ret; +} + +/* PPE port clock deinitialization */ +static void ppe_port_clock_deinit(struct ppe_port *ppe_port) +{ + int i; + + for (i = 0; i < PPE_PORT_CLK_RST_MAX; i++) { + clk_disable_unprepare(ppe_port->clks[i]); + clk_put(ppe_port->clks[i]); + reset_control_put(ppe_port->rstcs[i]); + } +} + +/* PPE port MAC hardware init configuration */ +static int ppe_port_mac_hw_init(struct ppe_port *ppe_port) +{ + struct ppe_device *ppe_dev = ppe_port->ppe_dev; + int ret, port = ppe_port->port_id; + u32 reg, val; + + /* GMAC RX and TX are initialized as disabled */ + reg = PPE_PORT_GMAC_ADDR(port); + ret = regmap_update_bits(ppe_dev->regmap, + reg + GMAC_ENABLE_ADDR, GMAC_TRXEN, 0); + if (ret) + return ret; + + /* GMAC max frame size configuration */ + val = FIELD_PREP(GMAC_JUMBO_SIZE_M, PPE_PORT_MAC_MAX_FRAME_SIZE); + ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_JUMBO_SIZE_ADDR, + GMAC_JUMBO_SIZE_M, val); + if (ret) + return ret; + + val = FIELD_PREP(GMAC_MAXFRAME_SIZE_M, PPE_PORT_MAC_MAX_FRAME_SIZE); + val |= FIELD_PREP(GMAC_TX_THD_M, 0x1); + ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_CTRL_ADDR, + GMAC_CTRL_MASK, val); + if (ret) + return ret; + + val = FIELD_PREP(GMAC_HIGH_IPG_M, 0xc); + ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_DBG_CTRL_ADDR, + GMAC_HIGH_IPG_M, val); + if (ret) + return ret; + + /* Enable and reset GMAC MIB counters and set as read clear + * mode, the GMAC MIB counters will be cleared after reading. + */ + ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_MIB_CTRL_ADDR, + GMAC_MIB_CTRL_MASK, GMAC_MIB_CTRL_MASK); + if (ret) + return ret; + + ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_MIB_CTRL_ADDR, + GMAC_MIB_RST, 0); + if (ret) + return ret; + + /* XGMAC RX and TX disabled and max frame size configuration */ + reg = PPE_PORT_XGMAC_ADDR(port); + ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_TX_CONFIG_ADDR, + XGMAC_TXEN | XGMAC_JD, XGMAC_JD); + if (ret) + return ret; + + val = FIELD_PREP(XGMAC_GPSL_M, PPE_PORT_MAC_MAX_FRAME_SIZE); + val |= XGMAC_GPSLEN; + val |= XGMAC_CST; + val |= XGMAC_ACS; + ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_RX_CONFIG_ADDR, + XGMAC_RX_CONFIG_MASK, val); + if (ret) + return ret; + + ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_WD_TIMEOUT_ADDR, + XGMAC_WD_TIMEOUT_MASK, XGMAC_WD_TIMEOUT_VAL); + if (ret) + return ret; + + ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_PKT_FILTER_ADDR, + XGMAC_PKT_FILTER_MASK, XGMAC_PKT_FILTER_VAL); + if (ret) + return ret; + + /* Enable and reset XGMAC MIB counters */ + ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_MMC_CTRL_ADDR, + XGMAC_MCF | XGMAC_CNTRST, XGMAC_CNTRST); + + return ret; +} + +/** + * ppe_port_mac_init() - Initialization of PPE ports for the PPE device + * @ppe_dev: PPE device + * + * Description: Initialize the PPE MAC ports on the PPE device specified + * by @ppe_dev. + * + * Return: 0 upon success or a negative error upon failure. + */ +int ppe_port_mac_init(struct ppe_device *ppe_dev) +{ + struct device_node *ports_node, *port_node; + int port, num, ret, j, i = 0; + struct ppe_ports *ppe_ports; + phy_interface_t phy_mode; + + ports_node = of_get_child_by_name(ppe_dev->dev->of_node, + "ethernet-ports"); + if (!ports_node) { + dev_err(ppe_dev->dev, "Failed to get ports node\n"); + return -ENODEV; + } + + num = of_get_available_child_count(ports_node); + + ppe_ports = devm_kzalloc(ppe_dev->dev, + struct_size(ppe_ports, port, num), + GFP_KERNEL); + if (!ppe_ports) { + ret = -ENOMEM; + goto err_ports_node; + } + + ppe_dev->ports = ppe_ports; + ppe_ports->num = num; + + for_each_available_child_of_node(ports_node, port_node) { + ret = of_property_read_u32(port_node, "reg", &port); + if (ret) { + dev_err(ppe_dev->dev, "Failed to get port id\n"); + goto err_port_node; + } + + ret = of_get_phy_mode(port_node, &phy_mode); + if (ret) { + dev_err(ppe_dev->dev, "Failed to get phy mode\n"); + goto err_port_node; + } + + ppe_ports->port[i].ppe_dev = ppe_dev; + ppe_ports->port[i].port_id = port; + ppe_ports->port[i].np = port_node; + ppe_ports->port[i].interface = phy_mode; + + ret = ppe_port_clock_init(&ppe_ports->port[i]); + if (ret) { + dev_err(ppe_dev->dev, "Failed to initialize port clocks\n"); + goto err_port_clk; + } + + ret = ppe_port_mac_hw_init(&ppe_ports->port[i]); + if (ret) { + dev_err(ppe_dev->dev, "Failed to initialize MAC hardware\n"); + goto err_port_node; + } + + i++; + } + + of_node_put(ports_node); + return 0; + +err_port_clk: + for (j = 0; j < i; j++) + ppe_port_clock_deinit(&ppe_ports->port[j]); +err_port_node: + of_node_put(port_node); +err_ports_node: + of_node_put(ports_node); + return ret; +} + +/** + * ppe_port_mac_deinit() - Deinitialization of PPE ports for the PPE device + * @ppe_dev: PPE device + * + * Description: Deinitialize the PPE MAC ports on the PPE device specified + * by @ppe_dev. + */ +void ppe_port_mac_deinit(struct ppe_device *ppe_dev) +{ + struct ppe_port *ppe_port; + int i; + + for (i = 0; i < ppe_dev->ports->num; i++) { + ppe_port = &ppe_dev->ports->port[i]; + ppe_port_clock_deinit(ppe_port); + } +} diff --git a/drivers/net/ethernet/qualcomm/ppe/ppe_port.h b/drivers/net/ethernet/qualcomm/ppe/ppe_port.h new file mode 100644 index 000000000000..194f65815011 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __PPE_PORT_H__ +#define __PPE_PORT_H__ + +#include + +/** + * enum ppe_port_clk_rst_type - PPE port clock and reset ID type + * @PPE_PORT_CLK_RST_MAC: The clock and reset ID for port MAC + * @PPE_PORT_CLK_RST_RX: The clock and reset ID for port receive path + * @PPE_PORT_CLK_RST_TX: The clock and reset for port transmit path + * @PPE_PORT_CLK_RST_MAX: The maximum of port clock and reset + */ +enum ppe_port_clk_rst_type { + PPE_PORT_CLK_RST_MAC, + PPE_PORT_CLK_RST_RX, + PPE_PORT_CLK_RST_TX, + PPE_PORT_CLK_RST_MAX, +}; + +/** + * enum ppe_mac_type - PPE MAC type + * @PPE_MAC_TYPE_GMAC: GMAC type + * @PPE_MAC_TYPE_XGMAC: XGMAC type + */ +enum ppe_mac_type { + PPE_MAC_TYPE_GMAC, + PPE_MAC_TYPE_XGMAC, +}; + +/** + * struct ppe_port - Private data for each PPE port + * @phylink: Linux phylink instance + * @phylink_config: Linux phylink configurations + * @pcs: Linux phylink PCS instance + * @np: Port device tree node + * @ppe_dev: Back pointer to PPE device private data + * @interface: Port interface mode + * @mac_type: Port MAC type, GMAC or XGMAC + * @port_id: Port ID + * @clks: Port clocks + * @rstcs: Port resets + */ +struct ppe_port { + struct phylink *phylink; + struct phylink_config phylink_config; + struct phylink_pcs *pcs; + struct device_node *np; + struct ppe_device *ppe_dev; + phy_interface_t interface; + enum ppe_mac_type mac_type; + int port_id; + struct clk *clks[PPE_PORT_CLK_RST_MAX]; + struct reset_control *rstcs[PPE_PORT_CLK_RST_MAX]; +}; + +/** + * struct ppe_ports - Array of PPE ports + * @num: Number of PPE ports + * @port: Each PPE port private data + */ +struct ppe_ports { + unsigned int num; + struct ppe_port port[] __counted_by(num); +}; + +int ppe_port_mac_init(struct ppe_device *ppe_dev); +void ppe_port_mac_deinit(struct ppe_device *ppe_dev); +int ppe_port_phylink_setup(struct ppe_port *ppe_port, + struct net_device *netdev); +void ppe_port_phylink_destroy(struct ppe_port *ppe_port); +#endif diff --git a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h index e84633d0f572..34b659ac0c37 100644 --- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h +++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h @@ -7,6 +7,17 @@ #ifndef __PPE_REGS_H__ #define __PPE_REGS_H__ +/* PPE port mux select control register */ +#define PPE_PORT_MUX_CTRL_ADDR 0x10 +#define PPE_PORT6_SEL_XGMAC BIT(13) +#define PPE_PORT5_SEL_XGMAC BIT(12) +#define PPE_PORT4_SEL_XGMAC BIT(11) +#define PPE_PORT3_SEL_XGMAC BIT(10) +#define PPE_PORT2_SEL_XGMAC BIT(9) +#define PPE_PORT1_SEL_XGMAC BIT(8) +#define PPE_PORT5_SEL_PCS1 BIT(4) +#define PPE_PORT_SEL_XGMAC(x) (BIT(8) << ((x) - 1)) + /* There are 15 BM ports and 4 BM groups supported by PPE, * BM port (0-7) is matched to EDMA port 0, BM port (8-13) is matched * to PPE physical port 1-6, BM port 14 is matched to EIP. @@ -545,4 +556,116 @@ #define PPE_ENQ_OPR_TBL_INC 0x10 #define PPE_ENQ_OPR_TBL_ENQ_DISABLE BIT(0) +/* PPE GMAC and XGMAC register base address */ +#define PPE_PORT_GMAC_ADDR(x) (0x001000 + ((x) - 1) * 0x200) +#define PPE_PORT_XGMAC_ADDR(x) (0x500000 + ((x) - 1) * 0x4000) + +/* GMAC enable register */ +#define GMAC_ENABLE_ADDR 0x0 +#define GMAC_TXFCEN BIT(6) +#define GMAC_RXFCEN BIT(5) +#define GMAC_DUPLEX_FULL BIT(4) +#define GMAC_TXEN BIT(1) +#define GMAC_RXEN BIT(0) + +#define GMAC_TRXEN \ + (GMAC_TXEN | GMAC_RXEN) +#define GMAC_ENABLE_ALL \ + (GMAC_TXFCEN | GMAC_RXFCEN | GMAC_DUPLEX_FULL | GMAC_TXEN | GMAC_RXEN) + +/* GMAC speed register */ +#define GMAC_SPEED_ADDR 0x4 +#define GMAC_SPEED_M GENMASK(1, 0) +#define GMAC_SPEED_10 0 +#define GMAC_SPEED_100 1 +#define GMAC_SPEED_1000 2 + +/* GMAC control register */ +#define GMAC_CTRL_ADDR 0x18 +#define GMAC_TX_THD_M GENMASK(27, 24) +#define GMAC_MAXFRAME_SIZE_M GENMASK(21, 8) +#define GMAC_CRS_SEL BIT(6) + +#define GMAC_CTRL_MASK \ + (GMAC_TX_THD_M | GMAC_MAXFRAME_SIZE_M | GMAC_CRS_SEL) + +/* GMAC debug control register */ +#define GMAC_DBG_CTRL_ADDR 0x1c +#define GMAC_HIGH_IPG_M GENMASK(15, 8) + +/* GMAC jumbo size register */ +#define GMAC_JUMBO_SIZE_ADDR 0x30 +#define GMAC_JUMBO_SIZE_M GENMASK(13, 0) + +/* GMAC MIB control register */ +#define GMAC_MIB_CTRL_ADDR 0x34 +#define GMAC_MIB_RD_CLR BIT(2) +#define GMAC_MIB_RST BIT(1) +#define GMAC_MIB_EN BIT(0) + +#define GMAC_MIB_CTRL_MASK \ + (GMAC_MIB_RD_CLR | GMAC_MIB_RST | GMAC_MIB_EN) + +/* XGMAC TX configuration register */ +#define XGMAC_TX_CONFIG_ADDR 0x0 +#define XGMAC_SPEED_M GENMASK(31, 29) +#define XGMAC_SPEED_10000_USXGMII FIELD_PREP(XGMAC_SPEED_M, 4) +#define XGMAC_SPEED_10000 FIELD_PREP(XGMAC_SPEED_M, 0) +#define XGMAC_SPEED_5000 FIELD_PREP(XGMAC_SPEED_M, 5) +#define XGMAC_SPEED_2500_USXGMII FIELD_PREP(XGMAC_SPEED_M, 6) +#define XGMAC_SPEED_2500 FIELD_PREP(XGMAC_SPEED_M, 2) +#define XGMAC_SPEED_1000 FIELD_PREP(XGMAC_SPEED_M, 3) +#define XGMAC_SPEED_100 XGMAC_SPEED_1000 +#define XGMAC_SPEED_10 XGMAC_SPEED_1000 +#define XGMAC_JD BIT(16) +#define XGMAC_TXEN BIT(0) + +/* XGMAC RX configuration register */ +#define XGMAC_RX_CONFIG_ADDR 0x4 +#define XGMAC_GPSL_M GENMASK(29, 16) +#define XGMAC_WD BIT(7) +#define XGMAC_GPSLEN BIT(6) +#define XGMAC_CST BIT(2) +#define XGMAC_ACS BIT(1) +#define XGMAC_RXEN BIT(0) + +#define XGMAC_RX_CONFIG_MASK \ + (XGMAC_GPSL_M | XGMAC_WD | XGMAC_GPSLEN | XGMAC_CST | \ + XGMAC_ACS | XGMAC_RXEN) + +/* XGMAC packet filter register */ +#define XGMAC_PKT_FILTER_ADDR 0x8 +#define XGMAC_RA BIT(31) +#define XGMAC_PCF_M GENMASK(7, 6) +#define XGMAC_PR BIT(0) + +#define XGMAC_PKT_FILTER_MASK \ + (XGMAC_RA | XGMAC_PCF_M | XGMAC_PR) +#define XGMAC_PKT_FILTER_VAL \ + (XGMAC_RA | XGMAC_PR | FIELD_PREP(XGMAC_PCF_M, 0x2)) + +/* XGMAC watchdog timeout register */ +#define XGMAC_WD_TIMEOUT_ADDR 0xc +#define XGMAC_PWE BIT(8) +#define XGMAC_WTO_M GENMASK(3, 0) + +#define XGMAC_WD_TIMEOUT_MASK \ + (XGMAC_PWE | XGMAC_WTO_M) +#define XGMAC_WD_TIMEOUT_VAL \ + (XGMAC_PWE | FIELD_PREP(XGMAC_WTO_M, 0xb)) + +/* XGMAC TX flow control register */ +#define XGMAC_TX_FLOW_CTRL_ADDR 0x70 +#define XGMAC_PAUSE_TIME_M GENMASK(31, 16) +#define XGMAC_TXFCEN BIT(1) + +/* XGMAC RX flow control register */ +#define XGMAC_RX_FLOW_CTRL_ADDR 0x90 +#define XGMAC_RXFCEN BIT(0) + +/* XGMAC management counters control register */ +#define XGMAC_MMC_CTRL_ADDR 0x800 +#define XGMAC_MCF BIT(3) +#define XGMAC_CNTRST BIT(0) + #endif -- 2.45.2