Re: [PATCH net-next 2/3] net: dsa: add Arrow SpeedChips XRS700x driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi George,

On Fri, Nov 20, 2020 at 12:16:26PM -0600, George McCollister wrote:
> Add a driver with initial support for the Arrow SpeedChips XRS7000
> series of gigabit Ethernet switch chips which are typically used in
> critical networking applications.
> 
> The switches have up to three RGMII ports and one RMII port.
> Management to the switches can be performed over i2c or mdio.
> 
> Support for advanced features such as PTP and
> HSR/PRP (IEC 62439-3 Clause 5 & 4) is not included in this patch and
> may be added at a later date.
> 
> Signed-off-by: George McCollister <george.mccollister@xxxxxxxxx>
> ---
>  drivers/net/dsa/Kconfig        |  26 ++
>  drivers/net/dsa/Makefile       |   3 +
>  drivers/net/dsa/xrs700x.c      | 529 +++++++++++++++++++++++++++++++++++++++++
>  drivers/net/dsa/xrs700x.h      |  27 +++
>  drivers/net/dsa/xrs700x_i2c.c  | 148 ++++++++++++
>  drivers/net/dsa/xrs700x_mdio.c | 160 +++++++++++++
>  drivers/net/dsa/xrs700x_reg.h  | 205 ++++++++++++++++

How much code do you plan to add to this driver? If it's going to
include IEEE 1588 and HSR/PRP offloading, would it make sense to put its
source code in a new folder now, to avoid doing that later?

>  7 files changed, 1098 insertions(+)
>  create mode 100644 drivers/net/dsa/xrs700x.c
>  create mode 100644 drivers/net/dsa/xrs700x.h
>  create mode 100644 drivers/net/dsa/xrs700x_i2c.c
>  create mode 100644 drivers/net/dsa/xrs700x_mdio.c
>  create mode 100644 drivers/net/dsa/xrs700x_reg.h
> 
> diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
> index f6a0488589fc..e5ec3c602bcb 100644
> --- a/drivers/net/dsa/Kconfig
> +++ b/drivers/net/dsa/Kconfig
> @@ -134,4 +134,30 @@ config NET_DSA_VITESSE_VSC73XX_PLATFORM
>  	  This enables support for the Vitesse VSC7385, VSC7388, VSC7395
>  	  and VSC7398 SparX integrated ethernet switches, connected over
>  	  a CPU-attached address bus and work in memory-mapped I/O mode.
> +
> +config NET_DSA_XRS700X
> +	tristate
> +	depends on NET_DSA
> +	select NET_DSA_TAG_XRS700X
> +	select REGMAP
> +	help
> +	  This enables support for Arrow SpeedChips XRS7003/7004 gigabit
> +	  Ethernet switches.
> +
> +config NET_DSA_XRS700X_I2C
> +	tristate "Arrow XRS7000X series switch in I2C mode"
> +	depends on NET_DSA && I2C
> +	select NET_DSA_XRS700X
> +	select REGMAP_I2C
> +	help
> +	  Enable I2C support for Arrow SpeedChips XRS7003/7004 gigabit Ethernet
> +	  switches.
> +
> +config NET_DSA_XRS700X_MDIO
> +	tristate "Arrow XRS7000X series switch in MDIO mode"
> +	depends on NET_DSA
> +	select NET_DSA_XRS700X
> +	help
> +	  Enable MDIO support for Arrow SpeedChips XRS7003/7004 gigabit Ethernet
> +	  switches.
>  endmenu
> diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
> index a84adb140a04..4528d6b57fc8 100644
> --- a/drivers/net/dsa/Makefile
> +++ b/drivers/net/dsa/Makefile
> @@ -17,6 +17,9 @@ obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o
>  obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) += vitesse-vsc73xx-core.o
>  obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM) += vitesse-vsc73xx-platform.o
>  obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_SPI) += vitesse-vsc73xx-spi.o
> +obj-$(CONFIG_NET_DSA_XRS700X) += xrs700x.o
> +obj-$(CONFIG_NET_DSA_XRS700X_I2C) += xrs700x_i2c.o
> +obj-$(CONFIG_NET_DSA_XRS700X_MDIO) += xrs700x_mdio.o
>  obj-y				+= b53/
>  obj-y				+= hirschmann/
>  obj-y				+= microchip/
> diff --git a/drivers/net/dsa/xrs700x.c b/drivers/net/dsa/xrs700x.c
> new file mode 100644
> index 000000000000..6cef3b534d5d
> --- /dev/null
> +++ b/drivers/net/dsa/xrs700x.c
> @@ -0,0 +1,529 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 NovaTech LLC
> + * George McCollister <george.mccollister@xxxxxxxxx>
> + */
> +
> +#include <net/dsa.h>
> +#include <linux/if_bridge.h>
> +#include "xrs700x.h"
> +#include "xrs700x_reg.h"
> +
> +#define XRS700X_MIB_INTERVAL msecs_to_jiffies(30000)
> +
> +#define XRS7003E_ID	0x100
> +#define XRS7003F_ID	0x101
> +#define XRS7004E_ID	0x200
> +#define XRS7004F_ID	0x201
> +
> +struct xrs700x_model {
> +	unsigned int id;
> +	const char *name;
> +	size_t num_ports;
> +};
> +
> +static const struct xrs700x_model xrs700x_models[] = {
> +	{XRS7003E_ID, "XRS7003E", 3},
> +	{XRS7003F_ID, "XRS7003F", 3},
> +	{XRS7004E_ID, "XRS7004E", 4},
> +	{XRS7004F_ID, "XRS7004F", 4},
> +};
> +
> +struct xrs700x_mib {
> +	unsigned int offset;
> +	const char *name;
> +};
> +
> +static const struct xrs700x_mib xrs700x_mibs[] = {
> +	{XRS_RX_GOOD_OCTETS_L(0), "rx_good_octets"},
> +	{XRS_RX_BAD_OCTETS_L(0), "rx_bad_octets"},
> +	{XRS_RX_UNICAST_L(0), "rx_unicast"},
> +	{XRS_RX_BROADCAST_L(0), "rx_broadcast"},
> +	{XRS_RX_MULTICAST_L(0), "rx_multicast"},
> +	{XRS_RX_UNDERSIZE_L(0), "rx_undersize"},
> +	{XRS_RX_FRAGMENTS_L(0), "rx_fragments"},
> +	{XRS_RX_OVERSIZE_L(0), "rx_oversize"},
> +	{XRS_RX_JABBER_L(0), "rx_jabber"},
> +	{XRS_RX_ERR_L(0), "rx_err"},
> +	{XRS_RX_CRC_L(0), "rx_crc"},
> +	{XRS_RX_64_L(0), "rx_64"},
> +	{XRS_RX_65_127_L(0), "rx_65_127"},
> +	{XRS_RX_128_255_L(0), "rx_128_255"},
> +	{XRS_RX_256_511_L(0), "rx_256_511"},
> +	{XRS_RX_512_1023_L(0), "rx_512_1023"},
> +	{XRS_RX_1024_1536_L(0), "rx_1024_1536"},

Uh-oh, Jakub might not like these RMON counters being exposed to
ethtool. See:
https://patchwork.kernel.org/project/netdevbpf/patch/20201115073533.1366-1-o.rempel@xxxxxxxxxxxxxx/

> +	{XRS_RX_HSR_PRP_L(0), "rx_hsr_prp"},
> +	{XRS_RX_WRONGLAN_L(0), "rx_wronglan"},
> +	{XRS_RX_DUPLICATE_L(0), "rx_duplicate"},
> +	{XRS_TX_OCTETS_L(0), "tx_octets"},
> +	{XRS_TX_UNICAST_L(0), "tx_unicast"},
> +	{XRS_TX_BROADCAST_L(0), "tx_broadcast"},
> +	{XRS_TX_MULTICAST_L(0), "tx_multicast"},
> +	{XRS_TX_HSR_PRP_L(0), "tx_hsr_prp"},
> +	{XRS_PRIQ_DROP_L(0), "priq_drop"},
> +	{XRS_EARLY_DROP_L(0), "early_drop"},
> +};
> +
> +static void xrs700x_get_strings(struct dsa_switch *ds, int port,
> +				u32 stringset, uint8_t *data)
> +{
> +	int i;
> +
> +	if (stringset != ETH_SS_STATS)
> +		return;
> +
> +	for (i = 0; i < ARRAY_SIZE(xrs700x_mibs); i++) {
> +		strlcpy(data, xrs700x_mibs[i].name, ETH_GSTRING_LEN);
> +		data += ETH_GSTRING_LEN;
> +	}
> +}
> +
> +static int xrs700x_get_sset_count(struct dsa_switch *ds, int port, int sset)
> +{
> +	if (sset != ETH_SS_STATS)
> +		return -EOPNOTSUPP;
> +
> +	return ARRAY_SIZE(xrs700x_mibs);
> +}
> +
> +static void xrs700x_read_port_counters(struct xrs700x *priv, int port)
> +{
> +	int i;
> +	struct xrs700x_port *p = &priv->ports[port];
> +
> +	mutex_lock(&p->mib_mutex);
> +
> +	/* Capture counter values */
> +	regmap_write(priv->regmap, XRS_CNT_CTRL(port), 1);
> +
> +	for (i = 0; i < ARRAY_SIZE(xrs700x_mibs); i++) {
> +		unsigned int high = 0, low = 0, reg;
> +
> +		reg = xrs700x_mibs[i].offset + XRS_PORT_OFFSET * port;
> +		regmap_read(priv->regmap, reg, &low);
> +		regmap_read(priv->regmap, reg + 2, &high);
> +
> +		p->mib_data[i] += (high << 16) | low;
> +	}
> +
> +	mutex_unlock(&p->mib_mutex);
> +}
> +
> +static void xrs700x_mib_work(struct work_struct *work)
> +{
> +	struct xrs700x *priv = container_of(work, struct xrs700x,
> +					    mib_work.work);
> +	int i;
> +
> +	for (i = 0; i < priv->ds->num_ports; i++)
> +		xrs700x_read_port_counters(priv, i);
> +
> +	schedule_delayed_work(&priv->mib_work, XRS700X_MIB_INTERVAL);
> +}
> +
> +static void xrs700x_get_ethtool_stats(struct dsa_switch *ds, int port,
> +				      uint64_t *data)
> +{
> +	struct xrs700x *priv = ds->priv;
> +	struct xrs700x_port *p = &priv->ports[port];
> +
> +	xrs700x_read_port_counters(priv, port);
> +
> +	mutex_lock(&p->mib_mutex);
> +	memcpy(data, p->mib_data, sizeof(*data) * ARRAY_SIZE(xrs700x_mibs));
> +	mutex_unlock(&p->mib_mutex);
> +}
> +
> +static int xrs700x_setup_regmap_range(struct xrs700x *priv)
> +{
> +	struct reg_field ps_forward = REG_FIELD_ID(XRS_PORT_STATE(0), 0, 1,
> +						   priv->ds->num_ports,
> +						   XRS_PORT_OFFSET);

Oh, hey, another REG_FIELD_ID user!

> +	struct reg_field ps_management = REG_FIELD_ID(XRS_PORT_STATE(0), 2, 3,
> +						      priv->ds->num_ports,
> +						      XRS_PORT_OFFSET);
> +	struct reg_field ps_sel_speed = REG_FIELD_ID(XRS_PORT_STATE(0), 4, 9,
> +						     priv->ds->num_ports,
> +						     XRS_PORT_OFFSET);
> +	struct reg_field ps_cur_speed = REG_FIELD_ID(XRS_PORT_STATE(0), 10, 11,
> +						     priv->ds->num_ports,
> +						     XRS_PORT_OFFSET);
> +
> +	priv->ps_forward = devm_regmap_field_alloc(priv->dev, priv->regmap,
> +						   ps_forward);
> +	if (IS_ERR(priv->ps_forward))
> +		return PTR_ERR(priv->ps_forward);
> +
> +	priv->ps_management = devm_regmap_field_alloc(priv->dev, priv->regmap,
> +						      ps_management);
> +	if (IS_ERR(priv->ps_management))
> +		return PTR_ERR(priv->ps_management);
> +
> +	priv->ps_sel_speed = devm_regmap_field_alloc(priv->dev, priv->regmap,
> +						     ps_sel_speed);
> +	if (IS_ERR(priv->ps_sel_speed))
> +		return PTR_ERR(priv->ps_sel_speed);
> +
> +	priv->ps_cur_speed = devm_regmap_field_alloc(priv->dev, priv->regmap,
> +						     ps_cur_speed);
> +	if (IS_ERR(priv->ps_cur_speed))
> +		return PTR_ERR(priv->ps_cur_speed);

Should you try to automate allocating these? You might get tired of
adding and adding and adding to this function really quick. You might
get some inspiration from ocelot_regfields_init() and that driver's use
of regmap in general.

> +
> +	return 0;
> +}
> +
> +static enum dsa_tag_protocol xrs700x_get_tag_protocol(struct dsa_switch *ds,
> +						      int port,
> +						      enum dsa_tag_protocol m)
> +{
> +	return DSA_TAG_PROTO_XRS700X;
> +}
> +
> +static int xrs700x_reset(struct dsa_switch *ds)
> +{
> +	struct xrs700x *priv = ds->priv;
> +	int ret;
> +	unsigned int val;
> +
> +	ret = regmap_write(priv->regmap, XRS_GENERAL, XRS_GENERAL_RESET);
> +	if (ret)
> +		goto error;
> +
> +	ret = regmap_read_poll_timeout(priv->regmap, XRS_GENERAL,
> +				       val, !(val & XRS_GENERAL_RESET),
> +				       10, 1000);
> +error:
> +	if (ret) {
> +		dev_err_ratelimited(priv->dev, "error resetting switch: %d\n",
> +				    ret);
> +	}
> +
> +	return ret;
> +}
> +
> +static void xrs700x_port_stp_state_set(struct dsa_switch *ds, int port,
> +				       u8 state)
> +{
> +	struct xrs700x *priv = ds->priv;
> +	unsigned int val;
> +
> +	switch (state) {
> +	case BR_STATE_DISABLED:
> +		val = XRS_PORT_DISABLED;
> +		break;
> +	case BR_STATE_LISTENING:
> +		val = XRS_PORT_DISABLED;
> +		break;
> +	case BR_STATE_LEARNING:
> +		val = XRS_PORT_LEARNING;
> +		break;
> +	case BR_STATE_FORWARDING:
> +		val = XRS_PORT_FORWARDING;
> +		break;
> +	case BR_STATE_BLOCKING:
> +		val = XRS_PORT_DISABLED;

Why not just put BR_STATE_DISABLED and BR_STATE_BLOCKING one under the
other?

	case BR_STATE_BLOCKING:
	case BR_STATE_DISABLED:
		val = XRS_PORT_DISABLED;

> +		break;
> +	default:
> +		dev_err(ds->dev, "invalid STP state: %d\n", state);
> +		return;
> +	}
> +
> +	regmap_fields_write(priv->ps_forward, port, val);
> +
> +	dev_dbg_ratelimited(priv->dev, "%s - port: %d, state: %u, val: 0x%x\n",
> +			    __func__, port, state, val);
> +}
> +
> +static int xrs700x_port_setup(struct dsa_switch *ds, int port)
> +{
> +	struct xrs700x *priv = ds->priv;
> +	bool cpu_port = dsa_is_cpu_port(ds, port);

Reverse Christmas tree notation please.

> +	unsigned int val;

Ugh, you couldn't have initialized this with zero here? It looks ugly
putting that in the for loop.

> +	int ret, i;
> +
> +	xrs700x_port_stp_state_set(ds, port, BR_STATE_DISABLED);
> +
> +	/* Disable forwarding to non-CPU ports */
> +	for (val = 0, i = 0; i < ds->num_ports; i++) {
> +		if (!dsa_is_cpu_port(ds, i))
> +			val |= BIT(i);
> +	}
> +
> +	ret = regmap_write(priv->regmap, XRS_PORT_FWD_MASK(port), val);
> +	if (ret)
> +		return ret;
> +
> +	val = cpu_port ? XRS_PORT_MODE_MANAGEMENT : XRS_PORT_MODE_NORMAL;
> +	ret = regmap_fields_write(priv->ps_management, port, val);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int xrs700x_setup(struct dsa_switch *ds)
> +{
> +	struct xrs700x *priv = ds->priv;
> +	int ret, i;
> +
> +	ret = xrs700x_reset(ds);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < ds->num_ports; i++) {
> +		ret = xrs700x_port_setup(ds, i);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	schedule_delayed_work(&priv->mib_work, XRS700X_MIB_INTERVAL);
> +
> +	return 0;
> +}
> +
> +static void xrs700x_phylink_validate(struct dsa_switch *ds, int port,
> +				     unsigned long *supported,
> +				     struct phylink_link_state *state)
> +{
> +	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
> +
> +	switch (port) {
> +	case 0:
> +		break;
> +	case 1:
> +	case 2:
> +	case 3:
> +		phylink_set(mask, 1000baseT_Full);
> +		break;
> +	default:
> +		bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
> +		dev_err(ds->dev, "Unsupported port: %i\n", port);
> +		return;
> +	}
> +
> +	phylink_set_port_modes(mask);
> +
> +	/* The switch only supports full duplex. */
> +	phylink_set(mask, 10baseT_Full);
> +	phylink_set(mask, 100baseT_Full);
> +
> +	bitmap_and(supported, supported, mask,
> +		   __ETHTOOL_LINK_MODE_MASK_NBITS);
> +	bitmap_and(state->advertising, state->advertising, mask,
> +		   __ETHTOOL_LINK_MODE_MASK_NBITS);
> +}
> +
> +static void xrs700x_phylink_mac_config(struct dsa_switch *ds, int port,
> +				       unsigned int mode,
> +				       const struct phylink_link_state *state)

As far as I understand phylink, you should be programming the link speed
of the RGMII/RMII MAC from the .mac_link_up callback.

> +{
> +	struct xrs700x *priv = ds->priv;
> +	unsigned int val;
> +
> +	switch (state->speed) {
> +	case SPEED_1000:
> +		val = XRS_PORT_SPEED_1000;
> +		break;
> +	case SPEED_100:
> +		val = XRS_PORT_SPEED_100;
> +		break;
> +	case SPEED_10:
> +		val = XRS_PORT_SPEED_10;
> +		break;
> +	default:
> +		return;
> +	}
> +
> +	regmap_fields_write(priv->ps_sel_speed, port, val);
> +
> +	dev_dbg_ratelimited(priv->dev, "%s: port: %d mode: %u speed: %u\n",
> +			    __func__, port, mode, state->speed);
> +}
> +
> +static int xrs700x_bridge_join(struct dsa_switch *ds, int port,
> +			       struct net_device *bridge)
> +{
> +	struct xrs700x *priv = ds->priv;
> +	unsigned int i, ret, mask = 0;
> +
> +	for (i = 0; i < ds->num_ports; i++) {
> +		if (dsa_to_port(ds, i)->bridge_dev == bridge ||
> +		    dsa_is_cpu_port(ds, i))
> +			continue;
> +
> +		mask |= BIT(i);
> +	}
> +
> +	dev_dbg(priv->dev, "%s: port: %d mask: 0x%x\n", __func__,
> +		port, mask);
> +
> +	for (i = 0; i < ds->num_ports; i++) {
> +		if (dsa_to_port(ds, i)->bridge_dev != bridge)
> +			continue;
> +
> +		ret = regmap_write(priv->regmap, XRS_PORT_FWD_MASK(i), mask);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void xrs700x_bridge_leave(struct dsa_switch *ds, int port,
> +				 struct net_device *bridge)

Don't be lazy, you can avoid copy-pasting the implementation for this
one...

> +{
> +	struct xrs700x *priv = ds->priv;
> +	unsigned int i, cpu_mask = 0, mask = 0;
> +
> +	for (i = 0; i < ds->num_ports; i++) {
> +		if (dsa_is_cpu_port(ds, i))
> +			continue;
> +
> +		cpu_mask |= BIT(i);
> +
> +		if (dsa_to_port(ds, i)->bridge_dev == bridge)
> +			continue;
> +
> +		mask |= BIT(i);
> +	}
> +
> +	dev_dbg(priv->dev, "%s: port: %d mask: 0x%x\n", __func__,
> +		port, mask);
> +
> +	for (i = 0; i < ds->num_ports; i++) {
> +		if (dsa_to_port(ds, i)->bridge_dev != bridge)
> +			continue;
> +
> +		regmap_write(priv->regmap, XRS_PORT_FWD_MASK(i), mask);
> +	}
> +
> +	regmap_write(priv->regmap, XRS_PORT_FWD_MASK(port), cpu_mask);
> +}
> +
> +static const struct dsa_switch_ops xrs700x_ops = {
> +	.get_tag_protocol	= xrs700x_get_tag_protocol,
> +	.setup			= xrs700x_setup,
> +	.port_stp_state_set	= xrs700x_port_stp_state_set,
> +	.phylink_validate	= xrs700x_phylink_validate,
> +	.phylink_mac_config	= xrs700x_phylink_mac_config,
> +	.get_strings		= xrs700x_get_strings,
> +	.get_sset_count		= xrs700x_get_sset_count,
> +	.get_ethtool_stats	= xrs700x_get_ethtool_stats,
> +	.port_bridge_join	= xrs700x_bridge_join,
> +	.port_bridge_leave	= xrs700x_bridge_leave,
> +};
> +
> +static int xrs700x_detect(struct xrs700x *dev)
> +{
> +	int i, ret;
> +	unsigned int id;
> +
> +	ret = regmap_read(dev->regmap, XRS_DEV_ID0, &id);
> +	if (ret) {
> +		dev_err(dev->dev, "error %d while reading switch id.\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(xrs700x_models); i++) {
> +		if (xrs700x_models[i].id == id) {
> +			dev->ds->num_ports = xrs700x_models[i].num_ports;
> +			dev_info(dev->dev, "%s detected.\n",
> +				 xrs700x_models[i].name);
> +			return 0;
> +		}
> +	}
> +
> +	dev_err(dev->dev, "unknown switch id 0x%x.\n", id);
> +
> +	return -ENODEV;
> +}
> +
> +struct xrs700x *xrs700x_switch_alloc(struct device *base, void *priv)
> +{
> +	struct dsa_switch *ds;
> +	struct xrs700x *dev;
> +
> +	ds = devm_kzalloc(base, sizeof(*ds), GFP_KERNEL);
> +	if (!ds)
> +		return NULL;
> +
> +	ds->dev = base;
> +	ds->num_ports = DSA_MAX_PORTS;

Why so many?

> +
> +	dev = devm_kzalloc(base, sizeof(*dev), GFP_KERNEL);
> +	if (!dev)
> +		return NULL;
> +
> +	INIT_DELAYED_WORK(&dev->mib_work, xrs700x_mib_work);
> +
> +	ds->ops = &xrs700x_ops;
> +	ds->priv = dev;
> +	dev->dev = base;
> +
> +	dev->ds = ds;
> +	dev->priv = priv;
> +
> +	return dev;
> +}
> +EXPORT_SYMBOL(xrs700x_switch_alloc);
> +
> +static int xrs700x_alloc_port_mib(struct xrs700x *dev, int port)
> +{
> +	struct xrs700x_port *p = &dev->ports[port];
> +	size_t mib_size = sizeof(*p->mib_data) * ARRAY_SIZE(xrs700x_mibs);
> +
> +	p->mib_data = devm_kzalloc(dev->dev, mib_size, GFP_KERNEL);
> +	if (!p->mib_data)
> +		return -ENOMEM;
> +
> +	mutex_init(&p->mib_mutex);
> +
> +	return 0;
> +}
> +
> +int xrs700x_switch_register(struct xrs700x *dev)
> +{
> +	int ret;
> +	int i;
> +
> +	ret = xrs700x_detect(dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = xrs700x_setup_regmap_range(dev);
> +	if (ret)
> +		return ret;
> +
> +	dev->ports = devm_kzalloc(dev->dev,
> +				  sizeof(*dev->ports) * dev->ds->num_ports,
> +				  GFP_KERNEL);
> +	if (!dev->ports)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < dev->ds->num_ports; i++) {
> +		ret = xrs700x_alloc_port_mib(dev, i);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = dsa_register_switch(dev->ds);
> +
> +	if (ret)
> +		cancel_delayed_work_sync(&dev->mib_work);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(xrs700x_switch_register);
> +
> +void xrs700x_switch_remove(struct xrs700x *dev)
> +{
> +	cancel_delayed_work_sync(&dev->mib_work);
> +
> +	dsa_unregister_switch(dev->ds);
> +}
> +EXPORT_SYMBOL(xrs700x_switch_remove);
> +
> +MODULE_AUTHOR("George McCollister <george.mccollister@xxxxxxxxx>");
> +MODULE_DESCRIPTION("Arrow SpeedChips XRS700x DSA driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/net/dsa/xrs700x.h b/drivers/net/dsa/xrs700x.h
> new file mode 100644
> index 000000000000..53acf4359477
> --- /dev/null
> +++ b/drivers/net/dsa/xrs700x.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#include <linux/device.h>
> +#include <linux/mutex.h>
> +#include <linux/regmap.h>
> +#include <linux/workqueue.h>
> +
> +struct xrs700x_port {
> +	struct mutex mib_mutex; /* protects mib_data */
> +	uint64_t *mib_data;
> +};
> +
> +struct xrs700x {
> +	struct dsa_switch *ds;
> +	struct device *dev;
> +	void *priv;
> +	struct regmap *regmap;
> +	struct regmap_field *ps_forward;
> +	struct regmap_field *ps_management;
> +	struct regmap_field *ps_sel_speed;
> +	struct regmap_field *ps_cur_speed;
> +	struct delayed_work mib_work;
> +	struct xrs700x_port *ports;
> +};
> +
> +struct xrs700x *xrs700x_switch_alloc(struct device *base, void *priv);
> +int xrs700x_switch_register(struct xrs700x *dev);
> +void xrs700x_switch_remove(struct xrs700x *dev);
> diff --git a/drivers/net/dsa/xrs700x_i2c.c b/drivers/net/dsa/xrs700x_i2c.c
> new file mode 100644
> index 000000000000..30f6c5ce825b
> --- /dev/null
> +++ b/drivers/net/dsa/xrs700x_i2c.c
> @@ -0,0 +1,148 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 NovaTech LLC
> + * George McCollister <george.mccollister@xxxxxxxxx>
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include "xrs700x.h"
> +#include "xrs700x_reg.h"
> +
> +static int xrs700x_i2c_reg_read(void *context, unsigned int reg,
> +				unsigned int *val)
> +{
> +	int ret;
> +	unsigned char buf[4];
> +	struct device *dev = context;
> +	struct i2c_client *i2c = to_i2c_client(dev);

Please sort variable declaration in the order of decreasing line length.

> +
> +	buf[0] = reg >> 23 & 0xff;
> +	buf[1] = reg >> 15 & 0xff;
> +	buf[2] = reg >> 7 & 0xff;
> +	buf[3] = (reg & 0x7f) << 1;
> +
> +	ret = i2c_master_send(i2c, buf, sizeof(buf));
> +	if (ret < 0) {
> +		dev_err(dev, "xrs i2c_master_send returned %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = i2c_master_recv(i2c, buf, 2);
> +	if (ret < 0) {
> +		dev_err(dev, "xrs i2c_master_recv returned %d\n", ret);
> +		return ret;
> +	}
> +
> +	*val = buf[0] << 8 | buf[1];
> +
> +	return 0;
> +}



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux