Commit 26271394 authored by David S. Miller's avatar David S. Miller

Merge branch 'ocelot-external-ports'

Colin Foster says:

====================
add support for ocelot external ports

This is the start of part 3 of what is hopefully a 3-part series to add
Ethernet switching support to Ocelot chips.

Part 1 of the series (A New Chip) added general support for Ocelot chips
that were controlled externally via SPI.
https://lore.kernel.org/all/20220815005553.1450359-1-colin.foster@in-advantage.com/

Part 2 of the series (The Ethernet Strikes Back) added DSA Ethernet
support for ports 0-3, which are the four copper ports that are internal
to the chip.
https://lore.kernel.org/all/20230127193559.1001051-1-colin.foster@in-advantage.com/

Part 3 will, at a minimum, add support for ports 4-7, which are
configured to use QSGMII to an external phy (Return Of The QSGMII). With
any luck, and some guidance, support for SGMII, SFPs, etc. will also be
part of this series.

V1 was submitted as an RFC - and that was rightly so. I suspected I
wasn't doing something right, and that was certainly the case. V2 is
much cleaner, so hopefully upgrading it to PATCH status is welcomed.

Thanks to Russell and Vladimir for correcting my course from V1.

In V1 I included a device tree snippet. I won't repeat that here, but
I will include a boot log snippet, in case it is of use:
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents d4671cb9 4c05e5ce
......@@ -45,6 +45,9 @@
#define VSC7512_SIO_CTRL_RES_START 0x710700f8
#define VSC7512_SIO_CTRL_RES_SIZE 0x00000100
#define VSC7512_HSIO_RES_START 0x710d0000
#define VSC7512_HSIO_RES_SIZE 0x00000128
#define VSC7512_ANA_RES_START 0x71880000
#define VSC7512_ANA_RES_SIZE 0x00010000
......@@ -129,8 +132,13 @@ static const struct resource vsc7512_sgpio_resources[] = {
DEFINE_RES_REG_NAMED(VSC7512_SIO_CTRL_RES_START, VSC7512_SIO_CTRL_RES_SIZE, "gcb_sio"),
};
static const struct resource vsc7512_serdes_resources[] = {
DEFINE_RES_REG_NAMED(VSC7512_HSIO_RES_START, VSC7512_HSIO_RES_SIZE, "hsio"),
};
static const struct resource vsc7512_switch_resources[] = {
DEFINE_RES_REG_NAMED(VSC7512_ANA_RES_START, VSC7512_ANA_RES_SIZE, "ana"),
DEFINE_RES_REG_NAMED(VSC7512_HSIO_RES_START, VSC7512_HSIO_RES_SIZE, "hsio"),
DEFINE_RES_REG_NAMED(VSC7512_QS_RES_START, VSC7512_QS_RES_SIZE, "qs"),
DEFINE_RES_REG_NAMED(VSC7512_QSYS_RES_START, VSC7512_QSYS_RES_SIZE, "qsys"),
DEFINE_RES_REG_NAMED(VSC7512_REW_RES_START, VSC7512_REW_RES_SIZE, "rew"),
......@@ -176,6 +184,11 @@ static const struct mfd_cell vsc7512_devs[] = {
.use_of_reg = true,
.num_resources = ARRAY_SIZE(vsc7512_miim1_resources),
.resources = vsc7512_miim1_resources,
}, {
.name = "ocelot-serdes",
.of_compatible = "mscc,vsc7514-serdes",
.num_resources = ARRAY_SIZE(vsc7512_serdes_resources),
.resources = vsc7512_serdes_resources,
}, {
.name = "ocelot-ext-switch",
.of_compatible = "mscc,vsc7512-switch",
......
......@@ -1056,6 +1056,17 @@ static void felix_phylink_get_caps(struct dsa_switch *ds, int port,
config->supported_interfaces);
}
static void felix_phylink_mac_config(struct dsa_switch *ds, int port,
unsigned int mode,
const struct phylink_link_state *state)
{
struct ocelot *ocelot = ds->priv;
struct felix *felix = ocelot_to_felix(ocelot);
if (felix->info->phylink_mac_config)
felix->info->phylink_mac_config(ocelot, port, mode, state);
}
static struct phylink_pcs *felix_phylink_mac_select_pcs(struct dsa_switch *ds,
int port,
phy_interface_t iface)
......@@ -1555,6 +1566,9 @@ static int felix_setup(struct dsa_switch *ds)
if (err)
return err;
if (ocelot->targets[HSIO])
ocelot_pll5_init(ocelot);
err = ocelot_init(ocelot);
if (err)
goto out_mdiobus_free;
......@@ -1571,6 +1585,10 @@ static int felix_setup(struct dsa_switch *ds)
dsa_switch_for_each_available_port(dp, ds) {
ocelot_init_port(ocelot, dp->index);
if (felix->info->configure_serdes)
felix->info->configure_serdes(ocelot, dp->index,
dp->dn);
/* Set the default QoS Classification based on PCP and DEI
* bits of vlan tag.
*/
......@@ -2085,6 +2103,7 @@ const struct dsa_switch_ops felix_switch_ops = {
.get_sset_count = felix_get_sset_count,
.get_ts_info = felix_get_ts_info,
.phylink_get_caps = felix_phylink_get_caps,
.phylink_mac_config = felix_phylink_mac_config,
.phylink_mac_select_pcs = felix_phylink_mac_select_pcs,
.phylink_mac_link_down = felix_phylink_mac_link_down,
.phylink_mac_link_up = felix_phylink_mac_link_up,
......
......@@ -15,6 +15,8 @@
#define OCELOT_PORT_MODE_USXGMII BIT(4)
#define OCELOT_PORT_MODE_1000BASEX BIT(5)
struct device_node;
/* Platform-specific information */
struct felix_info {
/* Hardcoded resources provided by the hardware instantiation. */
......@@ -58,6 +60,11 @@ struct felix_info {
void (*tas_guard_bands_update)(struct ocelot *ocelot, int port);
void (*port_sched_speed_set)(struct ocelot *ocelot, int port,
u32 speed);
void (*phylink_mac_config)(struct ocelot *ocelot, int port,
unsigned int mode,
const struct phylink_link_state *state);
int (*configure_serdes)(struct ocelot *ocelot, int port,
struct device_node *portnp);
};
/* Methods for initializing the hardware resources specific to a tagging
......
......@@ -20,13 +20,13 @@ static const u32 vsc7512_port_modes[VSC7514_NUM_PORTS] = {
OCELOT_PORT_MODE_INTERNAL,
OCELOT_PORT_MODE_INTERNAL,
OCELOT_PORT_MODE_INTERNAL,
OCELOT_PORT_MODE_NONE,
OCELOT_PORT_MODE_NONE,
OCELOT_PORT_MODE_NONE,
OCELOT_PORT_MODE_NONE,
OCELOT_PORT_MODE_NONE,
OCELOT_PORT_MODE_NONE,
OCELOT_PORT_MODE_NONE,
OCELOT_PORT_MODE_SERDES,
OCELOT_PORT_MODE_SERDES,
OCELOT_PORT_MODE_SERDES,
OCELOT_PORT_MODE_SERDES,
OCELOT_PORT_MODE_SERDES,
OCELOT_PORT_MODE_SGMII,
OCELOT_PORT_MODE_SERDES,
};
static const struct ocelot_ops ocelot_ext_ops = {
......@@ -59,6 +59,8 @@ static const struct felix_info vsc7512_info = {
.num_ports = VSC7514_NUM_PORTS,
.num_tx_queues = OCELOT_NUM_TC,
.port_modes = vsc7512_port_modes,
.phylink_mac_config = ocelot_phylink_mac_config,
.configure_serdes = ocelot_port_configure_serdes,
};
static int ocelot_ext_probe(struct platform_device *pdev)
......
......@@ -7,6 +7,8 @@
#include <linux/dsa/ocelot.h>
#include <linux/if_bridge.h>
#include <linux/iopoll.h>
#include <linux/phy/phy.h>
#include <soc/mscc/ocelot_hsio.h>
#include <soc/mscc/ocelot_vcap.h>
#include "ocelot.h"
#include "ocelot_vcap.h"
......@@ -211,6 +213,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 +810,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,
......
......@@ -1675,25 +1675,10 @@ static void vsc7514_phylink_mac_config(struct phylink_config *config,
{
struct net_device *ndev = to_net_dev(config->dev);
struct ocelot_port_private *priv = netdev_priv(ndev);
struct ocelot_port *ocelot_port = &priv->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);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->port.index;
/* No loopback */
ocelot_port_writel(ocelot_port, 0, PCS1G_LB_CFG);
ocelot_phylink_mac_config(ocelot, port, link_an_mode, state);
}
static void vsc7514_phylink_mac_link_down(struct phylink_config *config,
......@@ -1757,34 +1742,11 @@ static int ocelot_port_phylink_create(struct ocelot *ocelot, int port,
return -EINVAL;
}
/* Ensure clock signals and speed are set on all QSGMII links */
if (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);
ocelot_port->phy_mode = phy_mode;
if (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, 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;
}
}
err = ocelot_port_configure_serdes(ocelot, port, portnp);
if (err)
return err;
priv = container_of(ocelot_port, struct ocelot_port_private, port);
......
......@@ -18,7 +18,6 @@
#include <soc/mscc/ocelot.h>
#include <soc/mscc/ocelot_vcap.h>
#include <soc/mscc/ocelot_hsio.h>
#include <soc/mscc/vsc7514_regs.h>
#include "ocelot_fdma.h"
#include "ocelot.h"
......@@ -26,35 +25,6 @@
#define VSC7514_VCAP_POLICER_BASE 128
#define VSC7514_VCAP_POLICER_MAX 191
static 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));
}
static int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops)
{
int ret;
......
......@@ -494,6 +494,7 @@ static int serdes_probe(struct platform_device *pdev)
{
struct phy_provider *provider;
struct serdes_ctrl *ctrl;
struct resource *res;
unsigned int i;
int ret;
......@@ -503,6 +504,14 @@ static int serdes_probe(struct platform_device *pdev)
ctrl->dev = &pdev->dev;
ctrl->regs = syscon_node_to_regmap(pdev->dev.parent->of_node);
if (IS_ERR(ctrl->regs)) {
/* Fall back to using IORESOURCE_REG, if possible */
res = platform_get_resource(pdev, IORESOURCE_REG, 0);
if (res)
ctrl->regs = dev_get_regmap(ctrl->dev->parent,
res->name);
}
if (IS_ERR(ctrl->regs))
return PTR_ERR(ctrl->regs);
......
......@@ -644,6 +644,7 @@ enum ocelot_tag_prefix {
};
struct ocelot;
struct device_node;
struct ocelot_ops {
struct net_device *(*port_to_netdev)(struct ocelot *ocelot, int port);
......@@ -1111,6 +1112,12 @@ int ocelot_sb_occ_tc_port_bind_get(struct ocelot *ocelot, int port,
enum devlink_sb_pool_type pool_type,
u32 *p_cur, u32 *p_max);
int ocelot_port_configure_serdes(struct ocelot *ocelot, int port,
struct device_node *portnp);
void ocelot_phylink_mac_config(struct ocelot *ocelot, int port,
unsigned int link_an_mode,
const struct phylink_link_state *state);
void ocelot_phylink_mac_link_down(struct ocelot *ocelot, int port,
unsigned int link_an_mode,
phy_interface_t interface,
......@@ -1183,4 +1190,6 @@ ocelot_mrp_del_ring_role(struct ocelot *ocelot, int port,
}
#endif
void ocelot_pll5_init(struct ocelot *ocelot);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment