@@ -65,6 +65,9 @@ config QCOM_PPE
tristate "Qualcomm Technologies, Inc. PPE Ethernet support"
depends on HAS_IOMEM && OF
depends on COMMON_CLK
+ select PHYLINK
+ select HWMON
+ select SFP
help
This driver supports the Qualcomm Technologies, Inc. packet
process engine(PPE) available with IPQ SoC. The PPE houses
@@ -13,6 +13,8 @@
#include <linux/regmap.h>
#include <linux/platform_device.h>
#include <linux/if_ether.h>
+#include <linux/of_net.h>
+#include <linux/rtnetlink.h>
#include <linux/soc/qcom/ppe.h>
#include "ppe.h"
#include "ppe_regs.h"
@@ -197,6 +199,19 @@ struct reset_control **ppe_reset_get(struct ppe_device *ppe_dev)
return ppe_dev_priv->rst;
}
+static struct ppe_port *ppe_port_get(struct ppe_device *ppe_dev, int port)
+{
+ struct ppe_ports *ppe_ports = (struct ppe_ports *)ppe_dev->ports;
+ int i = 0;
+
+ for (i = 0; i < ppe_ports->num; i++) {
+ if (ppe_ports->port[i].port_id == port)
+ return &ppe_ports->port[i];
+ }
+
+ return NULL;
+}
+
static int ppe_clock_set_enable(struct ppe_device *ppe_dev,
enum ppe_clk_id clk_id, unsigned long rate)
{
@@ -302,6 +317,869 @@ static int ppe_clock_config(struct platform_device *pdev)
return 0;
}
+static int ppe_port_mac_reset(struct ppe_device *ppe_dev, int port)
+{
+ struct ppe_data *ppe_dev_priv = ppe_dev->ppe_priv;
+
+ reset_control_assert(ppe_dev_priv->rst[PPE_NSS_PORT1_MAC_RST + port - 1]);
+ if (ppe_dev_priv->ppe_type == PPE_TYPE_APPE) {
+ reset_control_assert(ppe_dev_priv->rst[PPE_NSS_PORT1_RST + port]);
+ } else if (ppe_dev_priv->ppe_type == PPE_TYPE_MPPE) {
+ reset_control_assert(ppe_dev_priv->rst[PPE_NSS_PORT1_RX_RST + ((port - 1) << 1)]);
+ reset_control_assert(ppe_dev_priv->rst[PPE_NSS_PORT1_TX_RST + ((port - 1) << 1)]);
+ }
+ fsleep(150000);
+
+ reset_control_deassert(ppe_dev_priv->rst[PPE_NSS_PORT1_MAC_RST + port - 1]);
+ if (ppe_dev_priv->ppe_type == PPE_TYPE_APPE) {
+ reset_control_deassert(ppe_dev_priv->rst[PPE_NSS_PORT1_RST + port]);
+ } else if (ppe_dev_priv->ppe_type == PPE_TYPE_MPPE) {
+ reset_control_deassert(ppe_dev_priv->rst[PPE_NSS_PORT1_RX_RST + ((port - 1) << 1)]);
+ reset_control_deassert(ppe_dev_priv->rst[PPE_NSS_PORT1_TX_RST + ((port - 1) << 1)]);
+ }
+ fsleep(150000);
+
+ return 0;
+}
+
+static int ppe_gcc_port_speed_clk_set(struct ppe_device *ppe_dev,
+ int port, int speed, phy_interface_t interface)
+{
+ struct ppe_data *ppe_dev_priv = ppe_dev->ppe_priv;
+ enum ppe_clk_id rx_id, tx_id;
+ unsigned long rate = 0;
+ int err = 0;
+
+ rx_id = PPE_NSS_PORT1_RX_CLK + ((port - 1) << 1);
+ tx_id = PPE_NSS_PORT1_TX_CLK + ((port - 1) << 1);
+
+ switch (interface) {
+ case PHY_INTERFACE_MODE_USXGMII:
+ case PHY_INTERFACE_MODE_10GKR:
+ case PHY_INTERFACE_MODE_QUSGMII:
+ case PHY_INTERFACE_MODE_10GBASER:
+ if (speed == SPEED_10)
+ rate = 1250000;
+ else if (speed == SPEED_100)
+ rate = 12500000;
+ else if (speed == SPEED_1000)
+ rate = 125000000;
+ else if (speed == SPEED_2500)
+ rate = 78125000;
+ else if (speed == SPEED_5000)
+ rate = 156250000;
+ else if (speed == SPEED_10000)
+ rate = 312500000;
+ break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+ if (speed == SPEED_2500)
+ rate = 312500000;
+ break;
+ case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_SGMII:
+ if (speed == SPEED_10)
+ rate = 2500000;
+ else if (speed == SPEED_100)
+ rate = 25000000;
+ else if (speed == SPEED_1000)
+ rate = 125000000;
+ break;
+ default:
+ break;
+ }
+
+ if (!IS_ERR(ppe_dev_priv->clk[rx_id])) {
+ err = clk_set_rate(ppe_dev_priv->clk[rx_id], rate);
+ if (err) {
+ dev_err(ppe_dev->dev,
+ "Failed to set ppe port %d speed rx clk(%d)\n",
+ port, rx_id);
+ return err;
+ }
+ }
+
+ if (!IS_ERR(ppe_dev_priv->clk[tx_id])) {
+ err = clk_set_rate(ppe_dev_priv->clk[tx_id], rate);
+ if (err) {
+ dev_err(ppe_dev->dev,
+ "Failed to set ppe port %d speed rx clk(%d)\n",
+ port, rx_id);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int ppe_mac_speed_set(struct ppe_device *ppe_dev,
+ int port, int speed, phy_interface_t interface)
+{
+ struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+ u32 val;
+
+ if (!ppe_port) {
+ dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+ return -ENOENT;
+ }
+
+ if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
+ ppe_read(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_SPEED,
+ &val);
+ val &= ~GMAC_SPEED_MASK;
+ switch (speed) {
+ case SPEED_10:
+ val |= GMAC_SPEED_10;
+ break;
+ case SPEED_100:
+ val |= GMAC_SPEED_100;
+ break;
+ case SPEED_1000:
+ val |= GMAC_SPEED_1000;
+ break;
+ default:
+ break;
+ }
+ ppe_write(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_SPEED,
+ val);
+ } else if (ppe_port->mac_type == PPE_MAC_TYPE_XGMAC) {
+ ppe_read(ppe_dev,
+ PPE_PORT_XGMAC_ADDR(port) + XGMAC_TX_CONFIGURATION,
+ &val);
+ val &= ~XGMAC_SPEED_MASK;
+ switch (speed) {
+ case SPEED_10000:
+ if (interface == PHY_INTERFACE_MODE_USXGMII ||
+ interface == PHY_INTERFACE_MODE_QUSGMII)
+ 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_QUSGMII)
+ 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:
+ break;
+ }
+ ppe_write(ppe_dev,
+ PPE_PORT_XGMAC_ADDR(port) + XGMAC_TX_CONFIGURATION,
+ val);
+ }
+
+ return 0;
+}
+
+static int ppe_mac_duplex_set(struct ppe_device *ppe_dev, int port, int duplex)
+{
+ struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+ u32 val;
+
+ if (!ppe_port) {
+ dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+ return -ENOENT;
+ }
+
+ if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
+ ppe_read(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+ &val);
+ if (duplex == DUPLEX_FULL)
+ val |= GMAC_DUPLEX_FULL;
+ else
+ val &= ~GMAC_DUPLEX_FULL;
+ ppe_write(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+ val);
+ }
+
+ return 0;
+}
+
+static int ppe_mac_txfc_status_set(struct ppe_device *ppe_dev, int port, bool enable)
+{
+ struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+ u32 val;
+
+ if (!ppe_port) {
+ dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+ return -ENOENT;
+ }
+
+ if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
+ ppe_read(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+ &val);
+ if (enable)
+ val |= GMAC_TX_FLOW_EN;
+ else
+ val &= ~GMAC_TX_FLOW_EN;
+ ppe_write(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+ val);
+ } else if (ppe_port->mac_type == PPE_MAC_TYPE_XGMAC) {
+ ppe_read(ppe_dev,
+ PPE_PORT_XGMAC_ADDR(port) + XGMAC_Q0_TX_FLOW_CTRL,
+ &val);
+ if (enable) {
+ val &= ~XGMAC_PT_MASK;
+ val |= (XGMAC_PAUSE_TIME | XGMAC_TFE);
+ } else {
+ val &= ~XGMAC_TFE;
+ }
+ ppe_write(ppe_dev,
+ PPE_PORT_XGMAC_ADDR(port) + XGMAC_Q0_TX_FLOW_CTRL,
+ val);
+ }
+
+ ppe_read(ppe_dev,
+ PPE_BM_PORT_FC_MODE + PPE_BM_PORT_FC_MODE_INC * (port + 7),
+ &val);
+ if (enable)
+ val |= PPE_BM_PORT_FC_MODE_EN;
+ else
+ val &= ~PPE_BM_PORT_FC_MODE_EN;
+ ppe_write(ppe_dev,
+ PPE_BM_PORT_FC_MODE + PPE_BM_PORT_FC_MODE_INC * (port + 7),
+ val);
+
+ return 0;
+}
+
+static int ppe_mac_rxfc_status_set(struct ppe_device *ppe_dev, int port, bool enable)
+{
+ struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+ u32 val;
+
+ if (!ppe_port) {
+ dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+ return -ENOENT;
+ }
+
+ if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
+ ppe_read(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+ &val);
+ if (enable)
+ val |= GMAC_RX_FLOW_EN;
+ else
+ val &= ~GMAC_RX_FLOW_EN;
+ ppe_write(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+ val);
+ } else if (ppe_port->mac_type == PPE_MAC_TYPE_XGMAC) {
+ ppe_read(ppe_dev,
+ PPE_PORT_XGMAC_ADDR(port) + XGMAC_RX_FLOW_CTRL,
+ &val);
+ if (enable)
+ val |= XGMAC_RFE;
+ else
+ val &= ~XGMAC_RFE;
+ ppe_write(ppe_dev,
+ PPE_PORT_XGMAC_ADDR(port) + XGMAC_RX_FLOW_CTRL,
+ val);
+ }
+
+ return 0;
+}
+
+static int ppe_mac_txmac_en_set(struct ppe_device *ppe_dev, int port, bool enable)
+{
+ struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+ u32 val;
+
+ if (!ppe_port) {
+ dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+ return -ENOENT;
+ }
+
+ if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
+ ppe_read(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+ &val);
+ if (enable)
+ val |= GMAC_TXMAC_EN;
+ else
+ val &= ~GMAC_TXMAC_EN;
+ ppe_write(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+ val);
+ } else if (ppe_port->mac_type == PPE_MAC_TYPE_XGMAC) {
+ ppe_read(ppe_dev,
+ PPE_PORT_XGMAC_ADDR(port) + XGMAC_TX_CONFIGURATION,
+ &val);
+ if (enable)
+ val |= XGMAC_TE;
+ else
+ val &= ~XGMAC_TE;
+ ppe_write(ppe_dev,
+ PPE_PORT_XGMAC_ADDR(port) + XGMAC_TX_CONFIGURATION,
+ val);
+ }
+
+ return 0;
+}
+
+static int ppe_mac_rxmac_en_set(struct ppe_device *ppe_dev, int port, bool enable)
+{
+ struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+ u32 val;
+
+ if (!ppe_port) {
+ dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+ return -ENOENT;
+ }
+
+ if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
+ ppe_read(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+ &val);
+ if (enable)
+ val |= GMAC_RXMAC_EN;
+ else
+ val &= ~GMAC_RXMAC_EN;
+ ppe_write(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+ val);
+ } else if (ppe_port->mac_type == PPE_MAC_TYPE_XGMAC) {
+ ppe_read(ppe_dev,
+ PPE_PORT_XGMAC_ADDR(port) + XGMAC_RX_CONFIGURATION,
+ &val);
+ if (enable)
+ val |= XGMAC_RE;
+ else
+ val &= ~XGMAC_RE;
+ ppe_write(ppe_dev,
+ PPE_PORT_XGMAC_ADDR(port) + XGMAC_RX_CONFIGURATION,
+ val);
+ }
+
+ return 0;
+}
+
+static int ppe_port_bridge_txmac_en_set(struct ppe_device *ppe_dev, int port, bool enable)
+{
+ u32 val;
+
+ ppe_read(ppe_dev,
+ PPE_PORT_BRIDGE_CTRL + PPE_PORT_BRIDGE_CTRL_INC * port,
+ &val);
+
+ if (enable)
+ val |= PPE_PORT_BRIDGE_CTRL_TXMAC_EN;
+ else
+ val &= ~PPE_PORT_BRIDGE_CTRL_TXMAC_EN;
+
+ ppe_write(ppe_dev,
+ PPE_PORT_BRIDGE_CTRL + PPE_PORT_BRIDGE_CTRL_INC * port,
+ val);
+
+ return 0;
+}
+
+static void ppe_phylink_mac_config(struct ppe_device *ppe_dev, int port,
+ unsigned int mode, const struct phylink_link_state *state)
+{
+ struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+ int mac_type;
+ u32 val;
+
+ if (!ppe_port) {
+ dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+ return;
+ }
+
+ switch (state->interface) {
+ case PHY_INTERFACE_MODE_USXGMII:
+ case PHY_INTERFACE_MODE_2500BASEX:
+ case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_QUSGMII:
+ mac_type = PPE_MAC_TYPE_XGMAC;
+ break;
+ default:
+ mac_type = PPE_MAC_TYPE_GMAC;
+ break;
+ }
+
+ if (ppe_port->mac_type != mac_type) {
+ /* Reset port mac for gmac */
+ if (mac_type == PPE_MAC_TYPE_GMAC)
+ ppe_port_mac_reset(ppe_dev, port);
+
+ /* Port mux to select gmac or xgmac */
+ mutex_lock(&ppe_dev->reg_mutex);
+ ppe_read(ppe_dev, PPE_PORT_MUX_CTRL, &val);
+ if (mac_type == PPE_MAC_TYPE_GMAC)
+ val &= ~PPE_PORT_MAC_SEL(port);
+ else
+ val |= PPE_PORT_MAC_SEL(port);
+ if (port == PPE_PORT5)
+ val |= PPE_PORT5_PCS_SEL;
+
+ ppe_write(ppe_dev, PPE_PORT_MUX_CTRL, val);
+ mutex_unlock(&ppe_dev->reg_mutex);
+ ppe_port->mac_type = mac_type;
+ }
+
+ /* Reset ppe port link status when interface changes,
+ * this allows PPE MAC and UNIPHY to be configured
+ * according to the port link up status in ppe phylink
+ * mac link up.
+ */
+ if (state->interface != ppe_port->interface) {
+ ppe_port->speed = SPEED_UNKNOWN;
+ ppe_port->duplex = DUPLEX_UNKNOWN;
+ ppe_port->pause = MLO_PAUSE_NONE;
+ ppe_port->interface = state->interface;
+ }
+
+ dev_info(ppe_dev->dev, "PPE port %d mac config: interface %s, mac_type %d\n",
+ port, phy_modes(state->interface), mac_type);
+}
+
+static struct phylink_pcs *ppe_phylink_mac_select_pcs(struct ppe_device *ppe_dev,
+ int port, phy_interface_t interface)
+{
+ struct ppe_uniphy *uniphy = (struct ppe_uniphy *)ppe_dev->uniphy;
+ int ppe_type = ppe_type_get(ppe_dev);
+ int index;
+
+ switch (port) {
+ case PPE_PORT6:
+ index = 2;
+ break;
+ case PPE_PORT5:
+ index = 1;
+ break;
+ case PPE_PORT4:
+ case PPE_PORT3:
+ index = 0;
+ break;
+ case PPE_PORT2:
+ if (ppe_type == PPE_TYPE_MPPE)
+ index = 1;
+ else if (ppe_type == PPE_TYPE_APPE)
+ index = 0;
+ break;
+ case PPE_PORT1:
+ index = 0;
+ break;
+ default:
+ index = -1;
+ break;
+ }
+
+ if (index >= 0)
+ return &uniphy[index].pcs;
+ else
+ return NULL;
+}
+
+static void ppe_phylink_mac_link_up(struct ppe_device *ppe_dev, int port,
+ struct phy_device *phy,
+ unsigned int mode, phy_interface_t interface,
+ int speed, int duplex, bool tx_pause, bool rx_pause)
+{
+ struct phylink_pcs *pcs = ppe_phylink_mac_select_pcs(ppe_dev, port, interface);
+ struct ppe_uniphy *uniphy = pcs_to_ppe_uniphy(pcs);
+ struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+
+ /* Wait uniphy auto-negotiation completion */
+ ppe_uniphy_autoneg_complete_check(uniphy, port);
+
+ if (speed != ppe_port->speed ||
+ duplex != ppe_port->duplex ||
+ tx_pause != !!(ppe_port->pause & MLO_PAUSE_TX) ||
+ rx_pause != !!(ppe_port->pause & MLO_PAUSE_RX)) {
+ /* Disable gcc uniphy port clk */
+ ppe_uniphy_port_gcc_clock_en_set(uniphy, port, false);
+
+ if (speed != ppe_port->speed) {
+ /* Set gcc port speed clock */
+ ppe_gcc_port_speed_clk_set(ppe_dev, port, speed, interface);
+ fsleep(10000);
+ /* Set uniphy channel speed */
+ ppe_uniphy_speed_set(uniphy, port, speed);
+ /* Set mac speed */
+ ppe_mac_speed_set(ppe_dev, port, speed, interface);
+ ppe_port->speed = speed;
+ }
+
+ if (duplex != ppe_port->duplex) {
+ /* Set uniphy channel duplex */
+ ppe_uniphy_duplex_set(uniphy, port, duplex);
+ /* Set mac duplex */
+ ppe_mac_duplex_set(ppe_dev, port, duplex);
+ ppe_port->duplex = duplex;
+ }
+
+ if (tx_pause != !!(ppe_port->pause & MLO_PAUSE_TX)) {
+ /* Set mac tx flow ctrl */
+ ppe_mac_txfc_status_set(ppe_dev, port, tx_pause);
+ if (tx_pause)
+ ppe_port->pause |= MLO_PAUSE_TX;
+ else
+ ppe_port->pause &= ~MLO_PAUSE_TX;
+ }
+
+ if (rx_pause != !!(ppe_port->pause & MLO_PAUSE_RX)) {
+ /* Set mac rx flow ctrl */
+ ppe_mac_rxfc_status_set(ppe_dev, port, rx_pause);
+ if (rx_pause)
+ ppe_port->pause |= MLO_PAUSE_RX;
+ else
+ ppe_port->pause &= ~MLO_PAUSE_RX;
+ }
+
+ /* Enable gcc uniphy port clk */
+ ppe_uniphy_port_gcc_clock_en_set(uniphy, port, true);
+
+ /* Reset uniphy channel adapter */
+ ppe_uniphy_adapter_reset(uniphy, port);
+ }
+
+ /* Enable ppe mac tx and rx */
+ ppe_mac_txmac_en_set(ppe_dev, port, true);
+ ppe_mac_rxmac_en_set(ppe_dev, port, true);
+
+ /* Enable ppe bridge port tx mac */
+ ppe_port_bridge_txmac_en_set(ppe_dev, port, true);
+
+ dev_info(ppe_dev->dev,
+ "PPE port %d interface %s link up - %s%s - pause tx %d rx %d\n",
+ port, phy_modes(interface), phy_speed_to_str(speed),
+ phy_duplex_to_str(duplex), tx_pause, rx_pause);
+}
+
+static void ppe_phylink_mac_link_down(struct ppe_device *ppe_dev, int port,
+ unsigned int mode, phy_interface_t interface)
+{
+ struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+
+ if (!ppe_port)
+ dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+
+ /* Disable ppe port bridge tx mac */
+ ppe_port_bridge_txmac_en_set(ppe_dev, port, false);
+
+ /* Disable ppe mac rx */
+ ppe_mac_rxmac_en_set(ppe_dev, port, false);
+ fsleep(10000);
+
+ /* Disable ppe mac tx */
+ ppe_mac_txmac_en_set(ppe_dev, port, false);
+
+ dev_info(ppe_dev->dev, "PPE port %d interface %s link down\n",
+ port, phy_modes(interface));
+}
+
+static int ppe_mac_init(struct platform_device *pdev)
+{
+ struct device_node *ports_node, *port_node;
+ struct ppe_device *ppe_dev = platform_get_drvdata(pdev);
+ struct ppe_ports *ppe_ports = NULL;
+ phy_interface_t phy_mode = PHY_INTERFACE_MODE_NA;
+ int i = 0, port = 0, err = 0, port_num = 0;
+
+ ports_node = of_get_child_by_name(pdev->dev.of_node, "qcom,port_phyinfo");
+ if (!ports_node) {
+ dev_err(&pdev->dev, "Failed to get qcom port phy info node\n");
+ return -ENODEV;
+ }
+
+ port_num = of_get_child_count(ports_node);
+
+ ppe_ports = devm_kzalloc(&pdev->dev,
+ struct_size(ppe_ports, port, port_num),
+ GFP_KERNEL);
+ if (!ppe_ports) {
+ err = -ENOMEM;
+ goto err_ports_node_put;
+ }
+
+ ppe_dev->ports = ppe_ports;
+ ppe_ports->num = port_num;
+
+ for_each_available_child_of_node(ports_node, port_node) {
+ err = of_property_read_u32(port_node, "port_id", &port);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to get port id\n");
+ goto err_port_node_put;
+ }
+
+ err = of_get_phy_mode(port_node, &phy_mode);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to get phy mode\n");
+ goto err_port_node_put;
+ }
+
+ 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;
+ ppe_ports->port[i].mac_type = PPE_MAC_TYPE_NA;
+ ppe_ports->port[i].speed = SPEED_UNKNOWN;
+ ppe_ports->port[i].duplex = DUPLEX_UNKNOWN;
+ ppe_ports->port[i].pause = MLO_PAUSE_NONE;
+ i++;
+
+ /* Port gmac HW initialization */
+ ppe_mask(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE,
+ GMAC_MAC_EN, 0);
+
+ ppe_mask(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_MAC_JUMBO_SIZE,
+ GMAC_JUMBO_SIZE_MASK,
+ FIELD_PREP(GMAC_JUMBO_SIZE_MASK, MAC_MAX_FRAME_SIZE));
+
+ ppe_mask(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_MAC_CTRL2,
+ GMAC_INIT_CTRL2_FIELD, GMAC_INIT_CTRL2);
+
+ ppe_mask(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_MAC_DBG_CTRL,
+ GMAC_HIGH_IPG_MASK,
+ FIELD_PREP(GMAC_HIGH_IPG_MASK, GMAC_IPG_CHECK));
+
+ ppe_mask(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_MAC_MIB_CTRL,
+ MAC_MIB_EN | MAC_MIB_RD_CLR | MAC_MIB_RESET,
+ MAC_MIB_EN | MAC_MIB_RD_CLR | MAC_MIB_RESET);
+
+ ppe_mask(ppe_dev,
+ PPE_PORT_GMAC_ADDR(port) + GMAC_MAC_MIB_CTRL,
+ MAC_MIB_RESET, 0);
+
+ /* Port xgmac HW initialization */
+ ppe_mask(ppe_dev,
+ PPE_PORT_XGMAC_ADDR(port) + XGMAC_TX_CONFIGURATION,
+ XGMAC_INIT_TX_CONFIG_FIELD, XGMAC_INIT_TX_CONFIG);
+
+ ppe_mask(ppe_dev,
+ PPE_PORT_XGMAC_ADDR(port) + XGMAC_RX_CONFIGURATION,
+ XGMAC_INIT_RX_CONFIG_FIELD, XGMAC_INIT_RX_CONFIG);
+
+ ppe_mask(ppe_dev,
+ PPE_PORT_XGMAC_ADDR(port) + XGMAC_WATCHDOG_TIMEOUT,
+ XGMAC_INIT_WATCHDOG_FIELD, XGMAC_INIT_WATCHDOG);
+
+ ppe_mask(ppe_dev,
+ PPE_PORT_XGMAC_ADDR(port) + XGMAC_PACKET_FILTER,
+ XGMAC_INIT_FILTER_FIELD, XGMAC_INIT_FILTER);
+
+ ppe_mask(ppe_dev,
+ PPE_PORT_XGMAC_ADDR(port) + XGMAC_MMC_CONTROL,
+ XGMAC_MCF | XGMAC_CNTRST, XGMAC_CNTRST);
+ }
+
+ of_node_put(ports_node);
+ dev_info(ppe_dev->dev, "QCOM PPE MAC init success\n");
+ return 0;
+
+err_port_node_put:
+ of_node_put(port_node);
+err_ports_node_put:
+ of_node_put(ports_node);
+ return err;
+}
+
+static void ppe_mac_config(struct phylink_config *config, unsigned int mode,
+ const struct phylink_link_state *state)
+{
+ struct ppe_device *ppe_dev = NULL;
+ struct ppe_port *ppe_port = container_of(config,
+ struct ppe_port,
+ phylink_config);
+
+ if (!ppe_port)
+ dev_err(ppe_dev->dev, "Failed to find ppe port\n");
+
+ ppe_dev = ppe_port->ppe_dev;
+
+ if (ppe_dev && ppe_dev->ppe_ops &&
+ ppe_dev->ppe_ops->phylink_mac_config) {
+ ppe_dev->ppe_ops->phylink_mac_config(ppe_dev,
+ ppe_port->port_id,
+ mode, state);
+ } else {
+ dev_err(ppe_dev->dev,
+ "Failed to find ppe device mac config operation\n");
+ }
+}
+
+static void ppe_mac_link_down(struct phylink_config *config, unsigned int mode,
+ phy_interface_t interface)
+{
+ struct ppe_device *ppe_dev = NULL;
+ struct ppe_port *ppe_port = container_of(config,
+ struct ppe_port,
+ phylink_config);
+
+ if (!ppe_port)
+ dev_err(ppe_dev->dev, "Failed to find ppe port\n");
+
+ ppe_dev = ppe_port->ppe_dev;
+
+ if (ppe_dev && ppe_dev->ppe_ops &&
+ ppe_dev->ppe_ops->phylink_mac_link_down) {
+ ppe_dev->ppe_ops->phylink_mac_link_down(ppe_dev,
+ ppe_port->port_id,
+ mode, interface);
+ } else {
+ dev_err(ppe_dev->dev,
+ "Failed to find ppe device link down operation\n");
+ }
+}
+
+static void ppe_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_device *ppe_dev = NULL;
+ struct ppe_port *ppe_port = container_of(config,
+ struct ppe_port,
+ phylink_config);
+
+ if (!ppe_port)
+ dev_err(ppe_dev->dev, "Failed to find ppe port\n");
+
+ ppe_dev = ppe_port->ppe_dev;
+
+ if (ppe_dev && ppe_dev->ppe_ops &&
+ ppe_dev->ppe_ops->phylink_mac_link_up) {
+ ppe_dev->ppe_ops->phylink_mac_link_up(ppe_dev,
+ ppe_port->port_id,
+ phy, mode, interface,
+ speed, duplex,
+ tx_pause, rx_pause);
+ } else {
+ dev_err(ppe_dev->dev,
+ "Failed to find ppe device link up operation\n");
+ }
+}
+
+static struct phylink_pcs *ppe_mac_select_pcs(struct phylink_config *config,
+ phy_interface_t interface)
+{
+ struct ppe_device *ppe_dev = NULL;
+ struct ppe_port *ppe_port = container_of(config,
+ struct ppe_port,
+ phylink_config);
+
+ if (!ppe_port) {
+ dev_err(ppe_dev->dev, "Failed to find ppe port");
+ return NULL;
+ }
+
+ ppe_dev = ppe_port->ppe_dev;
+
+ if (ppe_dev && ppe_dev->ppe_ops &&
+ ppe_dev->ppe_ops->phylink_mac_select_pcs) {
+ return ppe_dev->ppe_ops->phylink_mac_select_pcs(ppe_dev,
+ ppe_port->port_id,
+ interface);
+ } else {
+ dev_err(ppe_dev->dev,
+ "Failed to find ppe device pcs select operation\n");
+ return NULL;
+ }
+}
+
+static const struct phylink_mac_ops ppe_phylink_ops = {
+ .mac_config = ppe_mac_config,
+ .mac_link_down = ppe_mac_link_down,
+ .mac_link_up = ppe_mac_link_up,
+ .mac_select_pcs = ppe_mac_select_pcs,
+};
+
+static struct phylink *ppe_phylink_setup(struct ppe_device *ppe_dev,
+ struct net_device *netdev,
+ int port)
+{
+ struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+ int err;
+
+ if (!ppe_port) {
+ dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+ return NULL;
+ }
+
+ /* per 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_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_QSGMII,
+ ppe_port->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_QUSGMII,
+ 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, "Failed to create phylink for port %d\n", port);
+ return NULL;
+ }
+
+ /* connect phylink */
+ err = phylink_of_phy_connect(ppe_port->phylink, ppe_port->np, 0);
+ if (err) {
+ dev_err(ppe_dev->dev, "Failed to connect phylink for port %d\n", port);
+ phylink_destroy(ppe_port->phylink);
+ ppe_port->phylink = NULL;
+ return NULL;
+ }
+
+ return ppe_port->phylink;
+}
+
+static void ppe_phylink_destroy(struct ppe_device *ppe_dev, int port)
+{
+ struct ppe_port *ppe_port = ppe_port_get(ppe_dev, port);
+
+ if (!ppe_port)
+ dev_err(ppe_dev->dev, "Failed to find ppe port %d\n", port);
+
+ if (ppe_port->phylink) {
+ rtnl_lock();
+ phylink_disconnect_phy(ppe_port->phylink);
+ rtnl_unlock();
+ phylink_destroy(ppe_port->phylink);
+ ppe_port->phylink = NULL;
+ }
+}
+
bool ppe_is_probed(struct platform_device *pdev)
{
struct ppe_device *ppe_dev = platform_get_drvdata(pdev);
@@ -352,6 +1230,12 @@ static int ppe_port_maxframe_set(struct ppe_device *ppe_dev,
}
static struct ppe_device_ops qcom_ppe_ops = {
+ .phylink_setup = ppe_phylink_setup,
+ .phylink_destroy = ppe_phylink_destroy,
+ .phylink_mac_config = ppe_phylink_mac_config,
+ .phylink_mac_link_up = ppe_phylink_mac_link_up,
+ .phylink_mac_link_down = ppe_phylink_mac_link_down,
+ .phylink_mac_select_pcs = ppe_phylink_mac_select_pcs,
.set_maxframe = ppe_port_maxframe_set,
};
@@ -1407,6 +2291,7 @@ static int qcom_ppe_probe(struct platform_device *pdev)
PTR_ERR(ppe_dev->ppe_priv),
"Fail to init ppe data\n");
+ mutex_init(&ppe_dev->reg_mutex);
platform_set_drvdata(pdev, ppe_dev);
ret = ppe_clock_config(pdev);
if (ret)
@@ -1426,6 +2311,10 @@ static int qcom_ppe_probe(struct platform_device *pdev)
ret,
"ppe device hw init failed\n");
+ ret = ppe_mac_init(pdev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "ppe mac initialization failed\n");
+
ppe_dev->uniphy = ppe_uniphy_setup(pdev);
if (IS_ERR(ppe_dev->uniphy))
return dev_err_probe(&pdev->dev, ret, "ppe uniphy initialization failed\n");
@@ -1440,10 +2329,25 @@ static int qcom_ppe_probe(struct platform_device *pdev)
static int qcom_ppe_remove(struct platform_device *pdev)
{
struct ppe_device *ppe_dev;
+ struct ppe_ports *ppe_ports;
+ struct ppe_data *ppe_dev_priv;
+ int i, port;
ppe_dev = platform_get_drvdata(pdev);
+ ppe_dev_priv = ppe_dev->ppe_priv;
+ ppe_ports = (struct ppe_ports *)ppe_dev->ports;
+
ppe_debugfs_teardown(ppe_dev);
+ for (i = 0; i < ppe_ports->num; i++) {
+ /* Reset ppe port parent clock to XO clock */
+ port = ppe_ports->port[i].port_id;
+ clk_set_rate(ppe_dev_priv->clk[PPE_NSS_PORT1_RX_CLK + ((port - 1) << 1)],
+ P_XO_CLOCK_RATE);
+ clk_set_rate(ppe_dev_priv->clk[PPE_NSS_PORT1_TX_CLK + ((port - 1) << 1)],
+ P_XO_CLOCK_RATE);
+ }
+
return 0;
}
@@ -21,6 +21,9 @@
#define PPE_PORT6 6
#define PPE_PORT7 7
+/* PPE Port XO Clock Rate */
+#define P_XO_CLOCK_RATE 24000000
+
enum ppe_clk_id {
/* clocks for CMN PLL */
PPE_CMN_AHB_CLK,
@@ -152,6 +155,14 @@ enum {
PPE_ACTION_REDIRECTED_TO_CPU
};
+/* PPE MAC Type */
+enum {
+ PPE_MAC_TYPE_NA,
+ PPE_MAC_TYPE_GMAC,
+ PPE_MAC_TYPE_XGMAC,
+ PPE_MAC_TYPE_MAX
+};
+
/* PPE private data of different PPE type device */
struct ppe_data {
int ppe_type;
@@ -172,6 +183,28 @@ struct ppe_scheduler_port_resource {
int l1edrr[2];
};
+/* PPE per port data type to record port settings such as phylink
+ * setting, mac type, interface mode and link speed.
+ */
+struct ppe_port {
+ struct phylink *phylink;
+ struct phylink_config phylink_config;
+ struct device_node *np;
+ struct ppe_device *ppe_dev;
+ phy_interface_t interface;
+ int mac_type;
+ int port_id;
+ int speed;
+ int duplex;
+ int pause;
+};
+
+/* PPE ports data type */
+struct ppe_ports {
+ unsigned int num;
+ struct ppe_port port[];
+};
+
int ppe_type_get(struct ppe_device *ppe_dev);
struct clk **ppe_clock_get(struct ppe_device *ppe_dev);
struct reset_control **ppe_reset_get(struct ppe_device *ppe_dev);
@@ -7,6 +7,16 @@
#ifndef __PPE_REGS_H__
#define __PPE_REGS_H__
+#define PPE_PORT_MUX_CTRL 0x10
+#define PPE_PORT6_MAC_SEL BIT(13)
+#define PPE_PORT5_MAC_SEL BIT(12)
+#define PPE_PORT4_MAC_SEL BIT(11)
+#define PPE_PORT3_MAC_SEL BIT(10)
+#define PPE_PORT2_MAC_SEL BIT(9)
+#define PPE_PORT1_MAC_SEL BIT(8)
+#define PPE_PORT5_PCS_SEL BIT(4)
+#define PPE_PORT_MAC_SEL(x) (PPE_PORT1_MAC_SEL << ((x) - 1))
+
#define PPE_BM_TDM_CTRL 0xb000
#define PPE_BM_TDM_CTRL_NUM 1
#define PPE_BM_TDM_CTRL_INC 4
@@ -819,4 +829,106 @@ union ppe_ac_grp_cfg_u {
#define PPE_ENQ_OPR_TBL_INC 0x10
#define PPE_ENQ_OPR_TBL_ENQ_DISABLE BIT(0)
+/* PPE MAC Address */
+#define PPE_PORT_GMAC_ADDR(x) (0x001000 + ((x) - 1) * 0x200)
+#define PPE_PORT_XGMAC_ADDR(x) (0x500000 + ((x) - 1) * 0x4000)
+
+/* GMAC Registers */
+#define GMAC_ENABLE 0x0
+#define GMAC_TX_FLOW_EN BIT(6)
+#define GMAC_RX_FLOW_EN BIT(5)
+#define GMAC_DUPLEX_FULL BIT(4)
+#define GMAC_TXMAC_EN BIT(1)
+#define GMAC_RXMAC_EN BIT(0)
+#define GMAC_MAC_EN (GMAC_RXMAC_EN | GMAC_TXMAC_EN)
+
+#define GMAC_SPEED 0x4
+#define GMAC_SPEED_MASK GENMASK(1, 0)
+#define GMAC_SPEED_10 0
+#define GMAC_SPEED_100 1
+#define GMAC_SPEED_1000 2
+
+#define GMAC_MAC_CTRL2 0x18
+#define GMAC_TX_THD_MASK GENMASK(27, 24)
+#define GMAC_MAXFR_MASK GENMASK(21, 8)
+#define GMAC_CRS_SEL BIT(6)
+#define GMAC_TX_THD 0x1
+#define GMAC_INIT_CTRL2_FIELD (GMAC_MAXFR_MASK | \
+ GMAC_CRS_SEL | GMAC_TX_THD_MASK)
+#define GMAC_INIT_CTRL2 (FIELD_PREP(GMAC_MAXFR_MASK, \
+ MAC_MAX_FRAME_SIZE) | FIELD_PREP(GMAC_TX_THD_MASK, GMAC_TX_THD))
+
+#define GMAC_MAC_DBG_CTRL 0x1c
+#define GMAC_HIGH_IPG_MASK GENMASK(15, 8)
+#define GMAC_IPG_CHECK 0xc
+
+#define GMAC_MAC_JUMBO_SIZE 0x30
+#define GMAC_JUMBO_SIZE_MASK GENMASK(13, 0)
+#define MAC_MAX_FRAME_SIZE 0x3000
+
+#define GMAC_MAC_MIB_CTRL 0x34
+#define MAC_MIB_RD_CLR BIT(2)
+#define MAC_MIB_RESET BIT(1)
+#define MAC_MIB_EN BIT(0)
+
+/* XGMAC Registers */
+#define XGMAC_TX_CONFIGURATION 0x0
+#define XGMAC_SPEED_MASK GENMASK(31, 29)
+#define XGMAC_SPEED_10000_USXGMII FIELD_PREP(XGMAC_SPEED_MASK, 4)
+#define XGMAC_SPEED_10000 FIELD_PREP(XGMAC_SPEED_MASK, 0)
+#define XGMAC_SPEED_5000 FIELD_PREP(XGMAC_SPEED_MASK, 5)
+#define XGMAC_SPEED_2500_USXGMII FIELD_PREP(XGMAC_SPEED_MASK, 6)
+#define XGMAC_SPEED_2500 FIELD_PREP(XGMAC_SPEED_MASK, 2)
+#define XGMAC_SPEED_1000 FIELD_PREP(XGMAC_SPEED_MASK, 3)
+#define XGMAC_SPEED_100 XGMAC_SPEED_1000
+#define XGMAC_SPEED_10 XGMAC_SPEED_1000
+
+#define XGMAC_JD BIT(16)
+#define XGMAC_TE BIT(0)
+#define XGMAC_INIT_TX_CONFIG_FIELD (XGMAC_JD | XGMAC_TE)
+#define XGMAC_INIT_TX_CONFIG XGMAC_JD
+
+#define XGMAC_RX_CONFIGURATION 0x4
+#define XGMAC_GPSL_MASK GENMASK(29, 16)
+#define XGMAC_WD BIT(7)
+#define XGMAC_GPSLCE BIT(6)
+#define XGMAC_CST BIT(2)
+#define XGMAC_ACS BIT(1)
+#define XGMAC_RE BIT(0)
+#define XGMAC_INIT_RX_CONFIG_FIELD (XGMAC_RE | XGMAC_ACS | \
+ XGMAC_CST | XGMAC_WD | XGMAC_GPSLCE | XGMAC_GPSL_MASK)
+#define XGMAC_INIT_RX_CONFIG (XGMAC_ACS | XGMAC_CST | \
+ XGMAC_GPSLCE | FIELD_PREP(XGMAC_GPSL_MASK, MAC_MAX_FRAME_SIZE))
+
+#define XGMAC_PACKET_FILTER 0x8
+#define XGMAC_RA BIT(31)
+#define XGMAC_PCF_MASK GENMASK(7, 6)
+#define XGMAC_PR BIT(0)
+#define XGMAC_PASS_CONTROL_PACKET 0x2
+#define XGMAC_INIT_FILTER_FIELD (XGMAC_RA | XGMAC_PR | \
+ XGMAC_PCF_MASK)
+#define XGMAC_INIT_FILTER (XGMAC_RA | XGMAC_PR | \
+ FIELD_PREP(XGMAC_PCF_MASK, \
+ XGMAC_PASS_CONTROL_PACKET))
+
+#define XGMAC_WATCHDOG_TIMEOUT 0xc
+#define XGMAC_PWE BIT(8)
+#define XGMAC_WTO_MASK GENMASK(3, 0)
+#define XGMAC_WTO_LIMIT_13K 0xb
+#define XGMAC_INIT_WATCHDOG_FIELD (XGMAC_PWE | XGMAC_WTO_MASK)
+#define XGMAC_INIT_WATCHDOG (XGMAC_PWE | \
+ FIELD_PREP(XGMAC_WTO_MASK, XGMAC_WTO_LIMIT_13K))
+
+#define XGMAC_Q0_TX_FLOW_CTRL 0x70
+#define XGMAC_PT_MASK GENMASK(31, 16)
+#define XGMAC_PAUSE_TIME FIELD_PREP(XGMAC_PT_MASK, 0xffff)
+#define XGMAC_TFE BIT(1)
+
+#define XGMAC_RX_FLOW_CTRL 0x90
+#define XGMAC_RFE BIT(0)
+
+#define XGMAC_MMC_CONTROL 0x800
+#define XGMAC_MCF BIT(3)
+#define XGMAC_CNTRST BIT(0)
+
#endif
@@ -9,6 +9,7 @@
#define __QCOM_PPE_H__
#include <linux/platform_device.h>
+#include <linux/phylink.h>
/* PPE platform private data, which is used by external driver like
* Ethernet DMA driver.
@@ -20,6 +21,8 @@ struct ppe_device {
struct dentry *debugfs_root;
bool is_ppe_probed;
void *ppe_priv;
+ struct mutex reg_mutex; /* Protects ppe reg operation */
+ void *ports;
void *uniphy;
};
@@ -27,6 +30,36 @@ struct ppe_device {
* DMA driver to configure PPE.
*/
struct ppe_device_ops {
+ /*
+ * PHYLINK integration
+ */
+ struct phylink *(*phylink_setup)(struct ppe_device *ppe_dev,
+ struct net_device *netdev, int port);
+ void (*phylink_destroy)(struct ppe_device *ppe_dev,
+ int port);
+ void (*phylink_mac_config)(struct ppe_device *ppe_dev,
+ int port,
+ unsigned int mode,
+ const struct phylink_link_state *state);
+ void (*phylink_mac_link_up)(struct ppe_device *ppe_dev,
+ int port,
+ struct phy_device *phy,
+ unsigned int mode,
+ phy_interface_t interface,
+ int speed,
+ int duplex,
+ bool tx_pause,
+ bool rx_pause);
+ void (*phylink_mac_link_down)(struct ppe_device *ppe_dev,
+ int port,
+ unsigned int mode,
+ phy_interface_t interface);
+ struct phylink_pcs *(*phylink_mac_select_pcs)(struct ppe_device *ppe_dev,
+ int port,
+ phy_interface_t interface);
+ /*
+ * Port maximum frame size setting
+ */
int (*set_maxframe)(struct ppe_device *ppe_dev, int port,
int maxframe_size);
};