diff options
Diffstat (limited to 'drivers/net/ethernet/mscc/ocelot.c')
| -rw-r--r-- | drivers/net/ethernet/mscc/ocelot.c | 157 | 
1 files changed, 156 insertions, 1 deletions
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 08acb7b89086..1f5f00b30441 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -7,6 +7,9 @@  #include <linux/dsa/ocelot.h>  #include <linux/if_bridge.h>  #include <linux/iopoll.h> +#include <linux/phy/phy.h> +#include <net/pkt_sched.h> +#include <soc/mscc/ocelot_hsio.h>  #include <soc/mscc/ocelot_vcap.h>  #include "ocelot.h"  #include "ocelot_vcap.h" @@ -211,6 +214,36 @@ static void ocelot_mact_init(struct ocelot *ocelot)  	ocelot_write(ocelot, MACACCESS_CMD_INIT, ANA_TABLES_MACACCESS);  } +void ocelot_pll5_init(struct ocelot *ocelot) +{ +	/* Configure PLL5. This will need a proper CCF driver +	 * The values are coming from the VTSS API for Ocelot +	 */ +	regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG4, +		     HSIO_PLL5G_CFG4_IB_CTRL(0x7600) | +		     HSIO_PLL5G_CFG4_IB_BIAS_CTRL(0x8)); +	regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG0, +		     HSIO_PLL5G_CFG0_CORE_CLK_DIV(0x11) | +		     HSIO_PLL5G_CFG0_CPU_CLK_DIV(2) | +		     HSIO_PLL5G_CFG0_ENA_BIAS | +		     HSIO_PLL5G_CFG0_ENA_VCO_BUF | +		     HSIO_PLL5G_CFG0_ENA_CP1 | +		     HSIO_PLL5G_CFG0_SELCPI(2) | +		     HSIO_PLL5G_CFG0_LOOP_BW_RES(0xe) | +		     HSIO_PLL5G_CFG0_SELBGV820(4) | +		     HSIO_PLL5G_CFG0_DIV4 | +		     HSIO_PLL5G_CFG0_ENA_CLKTREE | +		     HSIO_PLL5G_CFG0_ENA_LANE); +	regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG2, +		     HSIO_PLL5G_CFG2_EN_RESET_FRQ_DET | +		     HSIO_PLL5G_CFG2_EN_RESET_OVERRUN | +		     HSIO_PLL5G_CFG2_GAIN_TEST(0x8) | +		     HSIO_PLL5G_CFG2_ENA_AMPCTRL | +		     HSIO_PLL5G_CFG2_PWD_AMPCTRL_N | +		     HSIO_PLL5G_CFG2_AMPC_SEL(0x10)); +} +EXPORT_SYMBOL(ocelot_pll5_init); +  static void ocelot_vcap_enable(struct ocelot *ocelot, int port)  {  	ocelot_write_gix(ocelot, ANA_PORT_VCAP_S2_CFG_S2_ENA | @@ -778,6 +811,71 @@ static int ocelot_port_flush(struct ocelot *ocelot, int port)  	return err;  } +int ocelot_port_configure_serdes(struct ocelot *ocelot, int port, +				 struct device_node *portnp) +{ +	struct ocelot_port *ocelot_port = ocelot->ports[port]; +	struct device *dev = ocelot->dev; +	int err; + +	/* Ensure clock signals and speed are set on all QSGMII links */ +	if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_QSGMII) +		ocelot_port_rmwl(ocelot_port, 0, +				 DEV_CLOCK_CFG_MAC_TX_RST | +				 DEV_CLOCK_CFG_MAC_RX_RST, +				 DEV_CLOCK_CFG); + +	if (ocelot_port->phy_mode != PHY_INTERFACE_MODE_INTERNAL) { +		struct phy *serdes = of_phy_get(portnp, NULL); + +		if (IS_ERR(serdes)) { +			err = PTR_ERR(serdes); +			dev_err_probe(dev, err, +				      "missing SerDes phys for port %d\n", +				      port); +			return err; +		} + +		err = phy_set_mode_ext(serdes, PHY_MODE_ETHERNET, +				       ocelot_port->phy_mode); +		of_phy_put(serdes); +		if (err) { +			dev_err(dev, "Could not SerDes mode on port %d: %pe\n", +				port, ERR_PTR(err)); +			return err; +		} +	} + +	return 0; +} +EXPORT_SYMBOL_GPL(ocelot_port_configure_serdes); + +void ocelot_phylink_mac_config(struct ocelot *ocelot, int port, +			       unsigned int link_an_mode, +			       const struct phylink_link_state *state) +{ +	struct ocelot_port *ocelot_port = ocelot->ports[port]; + +	/* Disable HDX fast control */ +	ocelot_port_writel(ocelot_port, DEV_PORT_MISC_HDX_FAST_DIS, +			   DEV_PORT_MISC); + +	/* SGMII only for now */ +	ocelot_port_writel(ocelot_port, PCS1G_MODE_CFG_SGMII_MODE_ENA, +			   PCS1G_MODE_CFG); +	ocelot_port_writel(ocelot_port, PCS1G_SD_CFG_SD_SEL, PCS1G_SD_CFG); + +	/* Enable PCS */ +	ocelot_port_writel(ocelot_port, PCS1G_CFG_PCS_ENA, PCS1G_CFG); + +	/* No aneg on SGMII */ +	ocelot_port_writel(ocelot_port, 0, PCS1G_ANEG_CFG); + +	/* No loopback */ +	ocelot_port_writel(ocelot_port, 0, PCS1G_LB_CFG); +} +EXPORT_SYMBOL_GPL(ocelot_phylink_mac_config); +  void ocelot_phylink_mac_link_down(struct ocelot *ocelot, int port,  				  unsigned int link_an_mode,  				  phy_interface_t interface, @@ -908,7 +1006,12 @@ void ocelot_phylink_mac_link_up(struct ocelot *ocelot, int port,  	 */  	if (ocelot->ops->cut_through_fwd) {  		mutex_lock(&ocelot->fwd_domain_lock); -		ocelot->ops->cut_through_fwd(ocelot); +		/* Workaround for hardware bug - FP doesn't work +		 * at all link speeds for all PHY modes. The function +		 * below also calls ocelot->ops->cut_through_fwd(), +		 * so we don't need to do it twice. +		 */ +		ocelot_port_update_active_preemptible_tcs(ocelot, port);  		mutex_unlock(&ocelot->fwd_domain_lock);  	} @@ -2602,6 +2705,58 @@ void ocelot_port_mirror_del(struct ocelot *ocelot, int from, bool ingress)  }  EXPORT_SYMBOL_GPL(ocelot_port_mirror_del); +static void ocelot_port_reset_mqprio(struct ocelot *ocelot, int port) +{ +	struct net_device *dev = ocelot->ops->port_to_netdev(ocelot, port); + +	netdev_reset_tc(dev); +	ocelot_port_change_fp(ocelot, port, 0); +} + +int ocelot_port_mqprio(struct ocelot *ocelot, int port, +		       struct tc_mqprio_qopt_offload *mqprio) +{ +	struct net_device *dev = ocelot->ops->port_to_netdev(ocelot, port); +	struct netlink_ext_ack *extack = mqprio->extack; +	struct tc_mqprio_qopt *qopt = &mqprio->qopt; +	int num_tc = qopt->num_tc; +	int tc, err; + +	if (!num_tc) { +		ocelot_port_reset_mqprio(ocelot, port); +		return 0; +	} + +	err = netdev_set_num_tc(dev, num_tc); +	if (err) +		return err; + +	for (tc = 0; tc < num_tc; tc++) { +		if (qopt->count[tc] != 1) { +			NL_SET_ERR_MSG_MOD(extack, +					   "Only one TXQ per TC supported"); +			return -EINVAL; +		} + +		err = netdev_set_tc_queue(dev, tc, 1, qopt->offset[tc]); +		if (err) +			goto err_reset_tc; +	} + +	err = netif_set_real_num_tx_queues(dev, num_tc); +	if (err) +		goto err_reset_tc; + +	ocelot_port_change_fp(ocelot, port, mqprio->preemptible_tcs); + +	return 0; + +err_reset_tc: +	ocelot_port_reset_mqprio(ocelot, port); +	return err; +} +EXPORT_SYMBOL_GPL(ocelot_port_mqprio); +  void ocelot_init_port(struct ocelot *ocelot, int port)  {  	struct ocelot_port *ocelot_port = ocelot->ports[port];  |