diff mbox series

[RFC,net,1/2] net: dsa: sja1105: split out the probing code into a separate driver

Message ID 20210909001736.3769910-2-vladimir.oltean@nxp.com
State New
Headers show
Series Break circular dependency between sja1105.ko and tag_sja1105.ko | expand

Commit Message

Vladimir Oltean Sept. 9, 2021, 12:17 a.m. UTC
The tag_sja1105.ko depends on the sja1105_switch_ops being exported by
the kernel before it can be inserted as a module, but at the same time,
it must be inserted into the kernel before the sja1105 driver can probe.

Solve this circular dependency by adding a third kernel module, which is
only responsible for probing the hardware and depends on the driver
which provides sja1105_switch_ops and interacts with DSA and with the
hardware at runtime, generally speaking.

Fixes: 994d2cbb08ca ("net: dsa: tag_sja1105: be dsa_loop-safe")
Link: https://lore.kernel.org/netdev/20210908220834.d7gmtnwrorhharna@skbuf/
Suggested-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
 drivers/net/dsa/sja1105/Kconfig               |   4 +
 drivers/net/dsa/sja1105/Makefile              |   2 +
 drivers/net/dsa/sja1105/sja1105_main.c        | 316 -----------------
 drivers/net/dsa/sja1105/sja1105_probe.c       | 322 ++++++++++++++++++
 drivers/net/dsa/sja1105/sja1105_spi.c         |  12 +
 .../net/dsa/sja1105/sja1105_static_config.c   |   1 +
 6 files changed, 341 insertions(+), 316 deletions(-)
 create mode 100644 drivers/net/dsa/sja1105/sja1105_probe.c
diff mbox series

Patch

diff --git a/drivers/net/dsa/sja1105/Kconfig b/drivers/net/dsa/sja1105/Kconfig
index 1291bba3f3b6..33f948d9ea18 100644
--- a/drivers/net/dsa/sja1105/Kconfig
+++ b/drivers/net/dsa/sja1105/Kconfig
@@ -23,6 +23,10 @@  tristate "NXP SJA1105 Ethernet switch family support"
 	    - SJA1110C (Gen. 3, SGMII, TT-Ethernet, 100base-TX PHY, 7 ports)
 	    - SJA1110D (Gen. 3, SGMII, TT-Ethernet, no 100base-TX PHY, 7 ports)
 
+config NET_DSA_SJA1105_PROBE
+tristate "Actual driver that probes the hardware"
+	depends on NET_DSA_SJA1105
+
 config NET_DSA_SJA1105_PTP
 	bool "Support for the PTP clock on the NXP SJA1105 Ethernet switch"
 	depends on NET_DSA_SJA1105
diff --git a/drivers/net/dsa/sja1105/Makefile b/drivers/net/dsa/sja1105/Makefile
index 40d69e6c0bae..73b4281f8218 100644
--- a/drivers/net/dsa/sja1105/Makefile
+++ b/drivers/net/dsa/sja1105/Makefile
@@ -23,3 +23,5 @@  endif
 ifdef CONFIG_NET_DSA_SJA1105_VL
 sja1105-objs += sja1105_vl.o
 endif
+
+obj-$(CONFIG_NET_DSA_SJA1105_PROBE) += sja1105_probe.o
diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c
index 2f8cc6686c38..01ef4839f631 100644
--- a/drivers/net/dsa/sja1105/sja1105_main.c
+++ b/drivers/net/dsa/sja1105/sja1105_main.c
@@ -10,12 +10,8 @@ 
 #include <linux/printk.h>
 #include <linux/spi/spi.h>
 #include <linux/errno.h>
-#include <linux/gpio/consumer.h>
 #include <linux/phylink.h>
 #include <linux/of.h>
-#include <linux/of_net.h>
-#include <linux/of_mdio.h>
-#include <linux/of_device.h>
 #include <linux/pcs/pcs-xpcs.h>
 #include <linux/netdev_features.h>
 #include <linux/netdevice.h>
@@ -27,17 +23,6 @@ 
 
 #define SJA1105_UNKNOWN_MULTICAST	0x010000000000ull
 
-static void sja1105_hw_reset(struct gpio_desc *gpio, unsigned int pulse_len,
-			     unsigned int startup_delay)
-{
-	gpiod_set_value_cansleep(gpio, 1);
-	/* Wait for minimum reset pulse length */
-	msleep(pulse_len);
-	gpiod_set_value_cansleep(gpio, 0);
-	/* Wait until chip is ready after reset */
-	msleep(startup_delay);
-}
-
 static void
 sja1105_port_allow_traffic(struct sja1105_l2_forwarding_entry *l2_fwd,
 			   int from, int to, bool allow)
@@ -1095,103 +1080,6 @@  static int sja1105_static_config_load(struct sja1105_private *priv)
 	return sja1105_static_config_upload(priv);
 }
 
-static int sja1105_parse_rgmii_delays(struct sja1105_private *priv)
-{
-	struct dsa_switch *ds = priv->ds;
-	int port;
-
-	for (port = 0; port < ds->num_ports; port++) {
-		if (!priv->fixed_link[port])
-			continue;
-
-		if (priv->phy_mode[port] == PHY_INTERFACE_MODE_RGMII_RXID ||
-		    priv->phy_mode[port] == PHY_INTERFACE_MODE_RGMII_ID)
-			priv->rgmii_rx_delay[port] = true;
-
-		if (priv->phy_mode[port] == PHY_INTERFACE_MODE_RGMII_TXID ||
-		    priv->phy_mode[port] == PHY_INTERFACE_MODE_RGMII_ID)
-			priv->rgmii_tx_delay[port] = true;
-
-		if ((priv->rgmii_rx_delay[port] || priv->rgmii_tx_delay[port]) &&
-		    !priv->info->setup_rgmii_delay)
-			return -EINVAL;
-	}
-	return 0;
-}
-
-static int sja1105_parse_ports_node(struct sja1105_private *priv,
-				    struct device_node *ports_node)
-{
-	struct device *dev = &priv->spidev->dev;
-	struct device_node *child;
-
-	for_each_available_child_of_node(ports_node, child) {
-		struct device_node *phy_node;
-		phy_interface_t phy_mode;
-		u32 index;
-		int err;
-
-		/* Get switch port number from DT */
-		if (of_property_read_u32(child, "reg", &index) < 0) {
-			dev_err(dev, "Port number not defined in device tree "
-				"(property \"reg\")\n");
-			of_node_put(child);
-			return -ENODEV;
-		}
-
-		/* Get PHY mode from DT */
-		err = of_get_phy_mode(child, &phy_mode);
-		if (err) {
-			dev_err(dev, "Failed to read phy-mode or "
-				"phy-interface-type property for port %d\n",
-				index);
-			of_node_put(child);
-			return -ENODEV;
-		}
-
-		phy_node = of_parse_phandle(child, "phy-handle", 0);
-		if (!phy_node) {
-			if (!of_phy_is_fixed_link(child)) {
-				dev_err(dev, "phy-handle or fixed-link "
-					"properties missing!\n");
-				of_node_put(child);
-				return -ENODEV;
-			}
-			/* phy-handle is missing, but fixed-link isn't.
-			 * So it's a fixed link. Default to PHY role.
-			 */
-			priv->fixed_link[index] = true;
-		} else {
-			of_node_put(phy_node);
-		}
-
-		priv->phy_mode[index] = phy_mode;
-	}
-
-	return 0;
-}
-
-static int sja1105_parse_dt(struct sja1105_private *priv)
-{
-	struct device *dev = &priv->spidev->dev;
-	struct device_node *switch_node = dev->of_node;
-	struct device_node *ports_node;
-	int rc;
-
-	ports_node = of_get_child_by_name(switch_node, "ports");
-	if (!ports_node)
-		ports_node = of_get_child_by_name(switch_node, "ethernet-ports");
-	if (!ports_node) {
-		dev_err(dev, "Incorrect bindings: absent \"ports\" node\n");
-		return -ENODEV;
-	}
-
-	rc = sja1105_parse_ports_node(priv, ports_node);
-	of_node_put(ports_node);
-
-	return rc;
-}
-
 /* Convert link speed from SJA1105 to ethtool encoding */
 static int sja1105_port_speed_to_ethtool(struct sja1105_private *priv,
 					 u64 speed)
@@ -3168,208 +3056,4 @@  const struct dsa_switch_ops sja1105_switch_ops = {
 };
 EXPORT_SYMBOL_GPL(sja1105_switch_ops);
 
-static const struct of_device_id sja1105_dt_ids[];
-
-static int sja1105_check_device_id(struct sja1105_private *priv)
-{
-	const struct sja1105_regs *regs = priv->info->regs;
-	u8 prod_id[SJA1105_SIZE_DEVICE_ID] = {0};
-	struct device *dev = &priv->spidev->dev;
-	const struct of_device_id *match;
-	u32 device_id;
-	u64 part_no;
-	int rc;
-
-	rc = sja1105_xfer_u32(priv, SPI_READ, regs->device_id, &device_id,
-			      NULL);
-	if (rc < 0)
-		return rc;
-
-	rc = sja1105_xfer_buf(priv, SPI_READ, regs->prod_id, prod_id,
-			      SJA1105_SIZE_DEVICE_ID);
-	if (rc < 0)
-		return rc;
-
-	sja1105_unpack(prod_id, &part_no, 19, 4, SJA1105_SIZE_DEVICE_ID);
-
-	for (match = sja1105_dt_ids; match->compatible[0]; match++) {
-		const struct sja1105_info *info = match->data;
-
-		/* Is what's been probed in our match table at all? */
-		if (info->device_id != device_id || info->part_no != part_no)
-			continue;
-
-		/* But is it what's in the device tree? */
-		if (priv->info->device_id != device_id ||
-		    priv->info->part_no != part_no) {
-			dev_warn(dev, "Device tree specifies chip %s but found %s, please fix it!\n",
-				 priv->info->name, info->name);
-			/* It isn't. No problem, pick that up. */
-			priv->info = info;
-		}
-
-		return 0;
-	}
-
-	dev_err(dev, "Unexpected {device ID, part number}: 0x%x 0x%llx\n",
-		device_id, part_no);
-
-	return -ENODEV;
-}
-
-static int sja1105_probe(struct spi_device *spi)
-{
-	struct device *dev = &spi->dev;
-	struct sja1105_private *priv;
-	size_t max_xfer, max_msg;
-	struct dsa_switch *ds;
-	int rc;
-
-	if (!dev->of_node) {
-		dev_err(dev, "No DTS bindings for SJA1105 driver\n");
-		return -EINVAL;
-	}
-
-	priv = devm_kzalloc(dev, sizeof(struct sja1105_private), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-
-	/* Configure the optional reset pin and bring up switch */
-	priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
-	if (IS_ERR(priv->reset_gpio))
-		dev_dbg(dev, "reset-gpios not defined, ignoring\n");
-	else
-		sja1105_hw_reset(priv->reset_gpio, 1, 1);
-
-	/* Populate our driver private structure (priv) based on
-	 * the device tree node that was probed (spi)
-	 */
-	priv->spidev = spi;
-	spi_set_drvdata(spi, priv);
-
-	/* Configure the SPI bus */
-	spi->bits_per_word = 8;
-	rc = spi_setup(spi);
-	if (rc < 0) {
-		dev_err(dev, "Could not init SPI\n");
-		return rc;
-	}
-
-	/* In sja1105_xfer, we send spi_messages composed of two spi_transfers:
-	 * a small one for the message header and another one for the current
-	 * chunk of the packed buffer.
-	 * Check that the restrictions imposed by the SPI controller are
-	 * respected: the chunk buffer is smaller than the max transfer size,
-	 * and the total length of the chunk plus its message header is smaller
-	 * than the max message size.
-	 * We do that during probe time since the maximum transfer size is a
-	 * runtime invariant.
-	 */
-	max_xfer = spi_max_transfer_size(spi);
-	max_msg = spi_max_message_size(spi);
-
-	/* We need to send at least one 64-bit word of SPI payload per message
-	 * in order to be able to make useful progress.
-	 */
-	if (max_msg < SJA1105_SIZE_SPI_MSG_HEADER + 8) {
-		dev_err(dev, "SPI master cannot send large enough buffers, aborting\n");
-		return -EINVAL;
-	}
-
-	priv->max_xfer_len = SJA1105_SIZE_SPI_MSG_MAXLEN;
-	if (priv->max_xfer_len > max_xfer)
-		priv->max_xfer_len = max_xfer;
-	if (priv->max_xfer_len > max_msg - SJA1105_SIZE_SPI_MSG_HEADER)
-		priv->max_xfer_len = max_msg - SJA1105_SIZE_SPI_MSG_HEADER;
-
-	priv->info = of_device_get_match_data(dev);
-
-	/* Detect hardware device */
-	rc = sja1105_check_device_id(priv);
-	if (rc < 0) {
-		dev_err(dev, "Device ID check failed: %d\n", rc);
-		return rc;
-	}
-
-	dev_info(dev, "Probed switch chip: %s\n", priv->info->name);
-
-	ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
-	if (!ds)
-		return -ENOMEM;
-
-	ds->dev = dev;
-	ds->num_ports = priv->info->num_ports;
-	ds->ops = &sja1105_switch_ops;
-	ds->priv = priv;
-	priv->ds = ds;
-
-	mutex_init(&priv->ptp_data.lock);
-	mutex_init(&priv->mgmt_lock);
-
-	rc = sja1105_parse_dt(priv);
-	if (rc < 0) {
-		dev_err(ds->dev, "Failed to parse DT: %d\n", rc);
-		return rc;
-	}
-
-	/* Error out early if internal delays are required through DT
-	 * and we can't apply them.
-	 */
-	rc = sja1105_parse_rgmii_delays(priv);
-	if (rc < 0) {
-		dev_err(ds->dev, "RGMII delay not supported\n");
-		return rc;
-	}
-
-	if (IS_ENABLED(CONFIG_NET_SCH_CBS)) {
-		priv->cbs = devm_kcalloc(dev, priv->info->num_cbs_shapers,
-					 sizeof(struct sja1105_cbs_entry),
-					 GFP_KERNEL);
-		if (!priv->cbs)
-			return -ENOMEM;
-	}
-
-	return dsa_register_switch(priv->ds);
-}
-
-static int sja1105_remove(struct spi_device *spi)
-{
-	struct sja1105_private *priv = spi_get_drvdata(spi);
-	struct dsa_switch *ds = priv->ds;
-
-	dsa_unregister_switch(ds);
-
-	return 0;
-}
-
-static const struct of_device_id sja1105_dt_ids[] = {
-	{ .compatible = "nxp,sja1105e", .data = &sja1105e_info },
-	{ .compatible = "nxp,sja1105t", .data = &sja1105t_info },
-	{ .compatible = "nxp,sja1105p", .data = &sja1105p_info },
-	{ .compatible = "nxp,sja1105q", .data = &sja1105q_info },
-	{ .compatible = "nxp,sja1105r", .data = &sja1105r_info },
-	{ .compatible = "nxp,sja1105s", .data = &sja1105s_info },
-	{ .compatible = "nxp,sja1110a", .data = &sja1110a_info },
-	{ .compatible = "nxp,sja1110b", .data = &sja1110b_info },
-	{ .compatible = "nxp,sja1110c", .data = &sja1110c_info },
-	{ .compatible = "nxp,sja1110d", .data = &sja1110d_info },
-	{ /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, sja1105_dt_ids);
-
-static struct spi_driver sja1105_driver = {
-	.driver = {
-		.name  = "sja1105",
-		.owner = THIS_MODULE,
-		.of_match_table = of_match_ptr(sja1105_dt_ids),
-	},
-	.probe  = sja1105_probe,
-	.remove = sja1105_remove,
-};
-
-module_spi_driver(sja1105_driver);
-
-MODULE_AUTHOR("Vladimir Oltean <olteanv@gmail.com>");
-MODULE_AUTHOR("Georg Waibel <georg.waibel@sensor-technik.de>");
-MODULE_DESCRIPTION("SJA1105 Driver");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/dsa/sja1105/sja1105_probe.c b/drivers/net/dsa/sja1105/sja1105_probe.c
new file mode 100644
index 000000000000..debb9bcd788c
--- /dev/null
+++ b/drivers/net/dsa/sja1105/sja1105_probe.c
@@ -0,0 +1,322 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2021 NXP */
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/spi/spi.h>
+#include "sja1105.h"
+
+static void sja1105_hw_reset(struct gpio_desc *gpio, unsigned int pulse_len,
+			     unsigned int startup_delay)
+{
+	gpiod_set_value_cansleep(gpio, 1);
+	/* Wait for minimum reset pulse length */
+	msleep(pulse_len);
+	gpiod_set_value_cansleep(gpio, 0);
+	/* Wait until chip is ready after reset */
+	msleep(startup_delay);
+}
+
+static int sja1105_parse_rgmii_delays(struct sja1105_private *priv)
+{
+	struct dsa_switch *ds = priv->ds;
+	int port;
+
+	for (port = 0; port < ds->num_ports; port++) {
+		if (!priv->fixed_link[port])
+			continue;
+
+		if (priv->phy_mode[port] == PHY_INTERFACE_MODE_RGMII_RXID ||
+		    priv->phy_mode[port] == PHY_INTERFACE_MODE_RGMII_ID)
+			priv->rgmii_rx_delay[port] = true;
+
+		if (priv->phy_mode[port] == PHY_INTERFACE_MODE_RGMII_TXID ||
+		    priv->phy_mode[port] == PHY_INTERFACE_MODE_RGMII_ID)
+			priv->rgmii_tx_delay[port] = true;
+
+		if ((priv->rgmii_rx_delay[port] || priv->rgmii_tx_delay[port]) &&
+		    !priv->info->setup_rgmii_delay)
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static int sja1105_parse_ports_node(struct sja1105_private *priv,
+				    struct device_node *ports_node)
+{
+	struct device *dev = &priv->spidev->dev;
+	struct device_node *child;
+
+	for_each_available_child_of_node(ports_node, child) {
+		struct device_node *phy_node;
+		phy_interface_t phy_mode;
+		u32 index;
+		int err;
+
+		/* Get switch port number from DT */
+		if (of_property_read_u32(child, "reg", &index) < 0) {
+			dev_err(dev, "Port number not defined in device tree "
+				"(property \"reg\")\n");
+			of_node_put(child);
+			return -ENODEV;
+		}
+
+		/* Get PHY mode from DT */
+		err = of_get_phy_mode(child, &phy_mode);
+		if (err) {
+			dev_err(dev, "Failed to read phy-mode or "
+				"phy-interface-type property for port %d\n",
+				index);
+			of_node_put(child);
+			return -ENODEV;
+		}
+
+		phy_node = of_parse_phandle(child, "phy-handle", 0);
+		if (!phy_node) {
+			if (!of_phy_is_fixed_link(child)) {
+				dev_err(dev, "phy-handle or fixed-link "
+					"properties missing!\n");
+				of_node_put(child);
+				return -ENODEV;
+			}
+			/* phy-handle is missing, but fixed-link isn't.
+			 * So it's a fixed link. Default to PHY role.
+			 */
+			priv->fixed_link[index] = true;
+		} else {
+			of_node_put(phy_node);
+		}
+
+		priv->phy_mode[index] = phy_mode;
+	}
+
+	return 0;
+}
+
+static int sja1105_parse_dt(struct sja1105_private *priv)
+{
+	struct device *dev = &priv->spidev->dev;
+	struct device_node *switch_node = dev->of_node;
+	struct device_node *ports_node;
+	int rc;
+
+	ports_node = of_get_child_by_name(switch_node, "ports");
+	if (!ports_node)
+		ports_node = of_get_child_by_name(switch_node, "ethernet-ports");
+	if (!ports_node) {
+		dev_err(dev, "Incorrect bindings: absent \"ports\" node\n");
+		return -ENODEV;
+	}
+
+	rc = sja1105_parse_ports_node(priv, ports_node);
+	of_node_put(ports_node);
+
+	return rc;
+}
+
+static const struct of_device_id sja1105_dt_ids[];
+
+static int sja1105_check_device_id(struct sja1105_private *priv)
+{
+	const struct sja1105_regs *regs = priv->info->regs;
+	u8 prod_id[SJA1105_SIZE_DEVICE_ID] = {0};
+	struct device *dev = &priv->spidev->dev;
+	const struct of_device_id *match;
+	u32 device_id;
+	u64 part_no;
+	int rc;
+
+	rc = sja1105_xfer_u32(priv, SPI_READ, regs->device_id, &device_id,
+			      NULL);
+	if (rc < 0)
+		return rc;
+
+	rc = sja1105_xfer_buf(priv, SPI_READ, regs->prod_id, prod_id,
+			      SJA1105_SIZE_DEVICE_ID);
+	if (rc < 0)
+		return rc;
+
+	sja1105_unpack(prod_id, &part_no, 19, 4, SJA1105_SIZE_DEVICE_ID);
+
+	for (match = sja1105_dt_ids; match->compatible[0]; match++) {
+		const struct sja1105_info *info = match->data;
+
+		/* Is what's been probed in our match table at all? */
+		if (info->device_id != device_id || info->part_no != part_no)
+			continue;
+
+		/* But is it what's in the device tree? */
+		if (priv->info->device_id != device_id ||
+		    priv->info->part_no != part_no) {
+			dev_warn(dev, "Device tree specifies chip %s but found %s, please fix it!\n",
+				 priv->info->name, info->name);
+			/* It isn't. No problem, pick that up. */
+			priv->info = info;
+		}
+
+		return 0;
+	}
+
+	dev_err(dev, "Unexpected {device ID, part number}: 0x%x 0x%llx\n",
+		device_id, part_no);
+
+	return -ENODEV;
+}
+
+static int sja1105_probe(struct spi_device *spi)
+{
+	struct device *dev = &spi->dev;
+	struct sja1105_private *priv;
+	size_t max_xfer, max_msg;
+	struct dsa_switch *ds;
+	int rc;
+
+	if (!dev->of_node) {
+		dev_err(dev, "No DTS bindings for SJA1105 driver\n");
+		return -EINVAL;
+	}
+
+	priv = devm_kzalloc(dev, sizeof(struct sja1105_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	/* Configure the optional reset pin and bring up switch */
+	priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(priv->reset_gpio))
+		dev_dbg(dev, "reset-gpios not defined, ignoring\n");
+	else
+		sja1105_hw_reset(priv->reset_gpio, 1, 1);
+
+	/* Populate our driver private structure (priv) based on
+	 * the device tree node that was probed (spi)
+	 */
+	priv->spidev = spi;
+	spi_set_drvdata(spi, priv);
+
+	/* Configure the SPI bus */
+	spi->bits_per_word = 8;
+	rc = spi_setup(spi);
+	if (rc < 0) {
+		dev_err(dev, "Could not init SPI\n");
+		return rc;
+	}
+
+	/* In sja1105_xfer, we send spi_messages composed of two spi_transfers:
+	 * a small one for the message header and another one for the current
+	 * chunk of the packed buffer.
+	 * Check that the restrictions imposed by the SPI controller are
+	 * respected: the chunk buffer is smaller than the max transfer size,
+	 * and the total length of the chunk plus its message header is smaller
+	 * than the max message size.
+	 * We do that during probe time since the maximum transfer size is a
+	 * runtime invariant.
+	 */
+	max_xfer = spi_max_transfer_size(spi);
+	max_msg = spi_max_message_size(spi);
+
+	/* We need to send at least one 64-bit word of SPI payload per message
+	 * in order to be able to make useful progress.
+	 */
+	if (max_msg < SJA1105_SIZE_SPI_MSG_HEADER + 8) {
+		dev_err(dev, "SPI master cannot send large enough buffers, aborting\n");
+		return -EINVAL;
+	}
+
+	priv->max_xfer_len = SJA1105_SIZE_SPI_MSG_MAXLEN;
+	if (priv->max_xfer_len > max_xfer)
+		priv->max_xfer_len = max_xfer;
+	if (priv->max_xfer_len > max_msg - SJA1105_SIZE_SPI_MSG_HEADER)
+		priv->max_xfer_len = max_msg - SJA1105_SIZE_SPI_MSG_HEADER;
+
+	priv->info = of_device_get_match_data(dev);
+
+	/* Detect hardware device */
+	rc = sja1105_check_device_id(priv);
+	if (rc < 0) {
+		dev_err(dev, "Device ID check failed: %d\n", rc);
+		return rc;
+	}
+
+	dev_info(dev, "Probed switch chip: %s\n", priv->info->name);
+
+	ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
+	if (!ds)
+		return -ENOMEM;
+
+	ds->dev = dev;
+	ds->num_ports = priv->info->num_ports;
+	ds->ops = &sja1105_switch_ops;
+	ds->priv = priv;
+	priv->ds = ds;
+
+	mutex_init(&priv->ptp_data.lock);
+	mutex_init(&priv->mgmt_lock);
+
+	rc = sja1105_parse_dt(priv);
+	if (rc < 0) {
+		dev_err(ds->dev, "Failed to parse DT: %d\n", rc);
+		return rc;
+	}
+
+	/* Error out early if internal delays are required through DT
+	 * and we can't apply them.
+	 */
+	rc = sja1105_parse_rgmii_delays(priv);
+	if (rc < 0) {
+		dev_err(ds->dev, "RGMII delay not supported\n");
+		return rc;
+	}
+
+	if (IS_ENABLED(CONFIG_NET_SCH_CBS)) {
+		priv->cbs = devm_kcalloc(dev, priv->info->num_cbs_shapers,
+					 sizeof(struct sja1105_cbs_entry),
+					 GFP_KERNEL);
+		if (!priv->cbs)
+			return -ENOMEM;
+	}
+
+	return dsa_register_switch(priv->ds);
+}
+
+static int sja1105_remove(struct spi_device *spi)
+{
+	struct sja1105_private *priv = spi_get_drvdata(spi);
+	struct dsa_switch *ds = priv->ds;
+
+	dsa_unregister_switch(ds);
+
+	return 0;
+}
+
+static const struct of_device_id sja1105_dt_ids[] = {
+	{ .compatible = "nxp,sja1105e", .data = &sja1105e_info },
+	{ .compatible = "nxp,sja1105t", .data = &sja1105t_info },
+	{ .compatible = "nxp,sja1105p", .data = &sja1105p_info },
+	{ .compatible = "nxp,sja1105q", .data = &sja1105q_info },
+	{ .compatible = "nxp,sja1105r", .data = &sja1105r_info },
+	{ .compatible = "nxp,sja1105s", .data = &sja1105s_info },
+	{ .compatible = "nxp,sja1110a", .data = &sja1110a_info },
+	{ .compatible = "nxp,sja1110b", .data = &sja1110b_info },
+	{ .compatible = "nxp,sja1110c", .data = &sja1110c_info },
+	{ .compatible = "nxp,sja1110d", .data = &sja1110d_info },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, sja1105_dt_ids);
+
+static struct spi_driver sja1105_driver = {
+	.driver = {
+		.name  = "sja1105",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(sja1105_dt_ids),
+	},
+	.probe  = sja1105_probe,
+	.remove = sja1105_remove,
+};
+
+module_spi_driver(sja1105_driver);
+
+MODULE_AUTHOR("Vladimir Oltean <olteanv@gmail.com>");
+MODULE_AUTHOR("Georg Waibel <georg.waibel@sensor-technik.de>");
+MODULE_DESCRIPTION("SJA1105 Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/dsa/sja1105/sja1105_spi.c b/drivers/net/dsa/sja1105/sja1105_spi.c
index d60a530d0272..420c21bfd6b5 100644
--- a/drivers/net/dsa/sja1105/sja1105_spi.c
+++ b/drivers/net/dsa/sja1105/sja1105_spi.c
@@ -118,6 +118,7 @@  int sja1105_xfer_buf(const struct sja1105_private *priv,
 {
 	return sja1105_xfer(priv, rw, reg_addr, buf, len, NULL);
 }
+EXPORT_SYMBOL_GPL(sja1105_xfer_buf);
 
 /* If @rw is:
  * - SPI_WRITE: creates and sends an SPI write message at absolute
@@ -172,6 +173,7 @@  int sja1105_xfer_u32(const struct sja1105_private *priv,
 
 	return rc;
 }
+EXPORT_SYMBOL_GPL(sja1105_xfer_u32);
 
 static int sja1105et_reset_cmd(struct dsa_switch *ds)
 {
@@ -601,6 +603,7 @@  const struct sja1105_info sja1105e_info = {
 	.supports_rgmii		= {true, true, true, true, true},
 	.name			= "SJA1105E",
 };
+EXPORT_SYMBOL_GPL(sja1105e_info);
 
 const struct sja1105_info sja1105t_info = {
 	.device_id		= SJA1105T_DEVICE_ID,
@@ -633,6 +636,7 @@  const struct sja1105_info sja1105t_info = {
 	.supports_rgmii		= {true, true, true, true, true},
 	.name			= "SJA1105T",
 };
+EXPORT_SYMBOL_GPL(sja1105t_info);
 
 const struct sja1105_info sja1105p_info = {
 	.device_id		= SJA1105PR_DEVICE_ID,
@@ -666,6 +670,7 @@  const struct sja1105_info sja1105p_info = {
 	.supports_rgmii		= {true, true, true, true, true},
 	.name			= "SJA1105P",
 };
+EXPORT_SYMBOL_GPL(sja1105p_info);
 
 const struct sja1105_info sja1105q_info = {
 	.device_id		= SJA1105QS_DEVICE_ID,
@@ -699,6 +704,7 @@  const struct sja1105_info sja1105q_info = {
 	.supports_rgmii		= {true, true, true, true, true},
 	.name			= "SJA1105Q",
 };
+EXPORT_SYMBOL_GPL(sja1105q_info);
 
 const struct sja1105_info sja1105r_info = {
 	.device_id		= SJA1105PR_DEVICE_ID,
@@ -735,6 +741,7 @@  const struct sja1105_info sja1105r_info = {
 	.supports_sgmii		= {false, false, false, false, true},
 	.name			= "SJA1105R",
 };
+EXPORT_SYMBOL_GPL(sja1105r_info);
 
 const struct sja1105_info sja1105s_info = {
 	.device_id		= SJA1105QS_DEVICE_ID,
@@ -771,6 +778,7 @@  const struct sja1105_info sja1105s_info = {
 	.supports_sgmii		= {false, false, false, false, true},
 	.name			= "SJA1105S",
 };
+EXPORT_SYMBOL_GPL(sja1105s_info);
 
 const struct sja1105_info sja1110a_info = {
 	.device_id		= SJA1110_DEVICE_ID,
@@ -821,6 +829,7 @@  const struct sja1105_info sja1110a_info = {
 				   SJA1105_PHY_BASE_T1},
 	.name			= "SJA1110A",
 };
+EXPORT_SYMBOL_GPL(sja1110a_info);
 
 const struct sja1105_info sja1110b_info = {
 	.device_id		= SJA1110_DEVICE_ID,
@@ -871,6 +880,7 @@  const struct sja1105_info sja1110b_info = {
 				   SJA1105_NO_PHY},
 	.name			= "SJA1110B",
 };
+EXPORT_SYMBOL_GPL(sja1110b_info);
 
 const struct sja1105_info sja1110c_info = {
 	.device_id		= SJA1110_DEVICE_ID,
@@ -921,6 +931,7 @@  const struct sja1105_info sja1110c_info = {
 				   SJA1105_NO_PHY},
 	.name			= "SJA1110C",
 };
+EXPORT_SYMBOL_GPL(sja1110c_info);
 
 const struct sja1105_info sja1110d_info = {
 	.device_id		= SJA1110_DEVICE_ID,
@@ -971,3 +982,4 @@  const struct sja1105_info sja1110d_info = {
 				   SJA1105_NO_PHY},
 	.name			= "SJA1110D",
 };
+EXPORT_SYMBOL_GPL(sja1110d_info);
diff --git a/drivers/net/dsa/sja1105/sja1105_static_config.c b/drivers/net/dsa/sja1105/sja1105_static_config.c
index 7a422ef4deb6..d5bca0d43b21 100644
--- a/drivers/net/dsa/sja1105/sja1105_static_config.c
+++ b/drivers/net/dsa/sja1105/sja1105_static_config.c
@@ -52,6 +52,7 @@  void sja1105_unpack(const void *buf, u64 *val, int start, int end, size_t len)
 		       start, end);
 	dump_stack();
 }
+EXPORT_SYMBOL_GPL(sja1105_unpack);
 
 void sja1105_packing(void *buf, u64 *val, int start, int end,
 		     size_t len, enum packing_op op)