Commit 8be040ec authored by Luiz Angelo Daros de Luca's avatar Luiz Angelo Daros de Luca Committed by David S. Miller
Browse files

net: dsa: realtek: common rtl83xx module



Some code can be shared between both interface modules (MDIO and SMI)
and among variants. These interface functions migrated to a common
module:

- rtl83xx_lock
- rtl83xx_unlock
- rtl83xx_probe
- rtl83xx_register_switch
- rtl83xx_unregister_switch
- rtl83xx_shutdown
- rtl83xx_remove

The reset during probe was moved to the end of the common probe. This way,
we avoid a reset if anything else fails.

Signed-off-by: default avatarLuiz Angelo Daros de Luca <luizluca@gmail.com>
Reviewed-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4667a1db
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_NET_DSA_REALTEK)		+= realtek_dsa.o
realtek_dsa-objs			:= rtl83xx.o
obj-$(CONFIG_NET_DSA_REALTEK_MDIO) 	+= realtek-mdio.o
obj-$(CONFIG_NET_DSA_REALTEK_SMI) 	+= realtek-smi.o
obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366.o
+20 −132
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@

#include "realtek.h"
#include "realtek-mdio.h"
#include "rtl83xx.h"

/* Read/write via mdiobus */
#define REALTEK_MDIO_CTRL0_REG		31
@@ -100,147 +101,41 @@ static int realtek_mdio_read(void *ctx, u32 reg, u32 *val)
	return ret;
}

static void realtek_mdio_lock(void *ctx)
{
	struct realtek_priv *priv = ctx;

	mutex_lock(&priv->map_lock);
}

static void realtek_mdio_unlock(void *ctx)
{
	struct realtek_priv *priv = ctx;

	mutex_unlock(&priv->map_lock);
}

static const struct regmap_config realtek_mdio_regmap_config = {
	.reg_bits = 10, /* A4..A0 R4..R0 */
	.val_bits = 16,
	.reg_stride = 1,
	/* PHY regs are at 0x8000 */
	.max_register = 0xffff,
	.reg_format_endian = REGMAP_ENDIAN_BIG,
	.reg_read = realtek_mdio_read,
	.reg_write = realtek_mdio_write,
	.cache_type = REGCACHE_NONE,
	.lock = realtek_mdio_lock,
	.unlock = realtek_mdio_unlock,
};

static const struct regmap_config realtek_mdio_nolock_regmap_config = {
	.reg_bits = 10, /* A4..A0 R4..R0 */
	.val_bits = 16,
	.reg_stride = 1,
	/* PHY regs are at 0x8000 */
	.max_register = 0xffff,
	.reg_format_endian = REGMAP_ENDIAN_BIG,
static const struct realtek_interface_info realtek_mdio_info = {
	.reg_read = realtek_mdio_read,
	.reg_write = realtek_mdio_write,
	.cache_type = REGCACHE_NONE,
	.disable_locking = true,
};

/**
 * realtek_mdio_probe() - Probe a platform device for an MDIO-connected switch
 * @mdiodev: mdio_device to probe on.
 *
 * This function should be used as the .probe in an mdio_driver. It
 * initializes realtek_priv and read data from the device-tree node. The switch
 * is hard reset if a method is provided. It checks the switch chip ID and,
 * finally, a DSA switch is registered.
 * This function should be used as the .probe in an mdio_driver. After
 * calling the common probe function for both interfaces, it initializes the
 * values specific for MDIO-connected devices. Finally, it calls a common
 * function to register the DSA switch.
 *
 * Context: Can sleep. Takes and releases priv->map_lock.
 * Return: Returns 0 on success, a negative error on failure.
 */
int realtek_mdio_probe(struct mdio_device *mdiodev)
{
	struct realtek_priv *priv;
	struct device *dev = &mdiodev->dev;
	const struct realtek_variant *var;
	struct regmap_config rc;
	struct device_node *np;
	struct realtek_priv *priv;
	int ret;

	var = of_device_get_match_data(dev);
	if (!var)
		return -EINVAL;

	priv = devm_kzalloc(&mdiodev->dev,
			    size_add(sizeof(*priv), var->chip_data_sz),
			    GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	mutex_init(&priv->map_lock);

	rc = realtek_mdio_regmap_config;
	rc.lock_arg = priv;
	priv->map = devm_regmap_init(dev, NULL, priv, &rc);
	if (IS_ERR(priv->map)) {
		ret = PTR_ERR(priv->map);
		dev_err(dev, "regmap init failed: %d\n", ret);
		return ret;
	}
	priv = rtl83xx_probe(dev, &realtek_mdio_info);
	if (IS_ERR(priv))
		return PTR_ERR(priv);

	rc = realtek_mdio_nolock_regmap_config;
	priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc);
	if (IS_ERR(priv->map_nolock)) {
		ret = PTR_ERR(priv->map_nolock);
		dev_err(dev, "regmap init failed: %d\n", ret);
		return ret;
	}

	priv->mdio_addr = mdiodev->addr;
	priv->bus = mdiodev->bus;
	priv->dev = &mdiodev->dev;
	priv->chip_data = (void *)priv + sizeof(*priv);

	priv->variant = var;
	priv->ops = var->ops;

	priv->mdio_addr = mdiodev->addr;
	priv->write_reg_noack = realtek_mdio_write;
	priv->ds_ops = priv->variant->ds_ops_mdio;

	np = dev->of_node;

	dev_set_drvdata(dev, priv);

	/* TODO: if power is software controlled, set up any regulators here */
	priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds");

	priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
	if (IS_ERR(priv->reset)) {
		dev_err(dev, "failed to get RESET GPIO\n");
		return PTR_ERR(priv->reset);
	}

	if (priv->reset) {
		gpiod_set_value(priv->reset, 1);
		dev_dbg(dev, "asserted RESET\n");
		msleep(REALTEK_HW_STOP_DELAY);
		gpiod_set_value(priv->reset, 0);
		msleep(REALTEK_HW_START_DELAY);
		dev_dbg(dev, "deasserted RESET\n");
	}

	ret = priv->ops->detect(priv);
	if (ret) {
		dev_err(dev, "unable to detect switch\n");
		return ret;
	}

	priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL);
	if (!priv->ds)
		return -ENOMEM;

	priv->ds->dev = dev;
	priv->ds->num_ports = priv->num_ports;
	priv->ds->priv = priv;
	priv->ds->ops = var->ds_ops_mdio;

	ret = dsa_register_switch(priv->ds);
	ret = rtl83xx_register_switch(priv);
	if (ret) {
		dev_err(priv->dev, "unable to register switch ret = %d\n", ret);
		rtl83xx_remove(priv);
		return ret;
	}

@@ -253,8 +148,7 @@ EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe, REALTEK_DSA);
 * @mdiodev: mdio_device to be removed.
 *
 * This function should be used as the .remove_new in an mdio_driver. First
 * it unregisters the DSA switch and cleans internal data. If a method is
 * provided, the hard reset is asserted to avoid traffic leakage.
 * it unregisters the DSA switch and then it calls the common remove function.
 *
 * Context: Can sleep.
 * Return: Nothing.
@@ -266,11 +160,9 @@ void realtek_mdio_remove(struct mdio_device *mdiodev)
	if (!priv)
		return;

	dsa_unregister_switch(priv->ds);
	rtl83xx_unregister_switch(priv);

	/* leave the device reset asserted */
	if (priv->reset)
		gpiod_set_value(priv->reset, 1);
	rtl83xx_remove(priv);
}
EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, REALTEK_DSA);

@@ -278,10 +170,8 @@ EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, REALTEK_DSA);
 * realtek_mdio_shutdown() - Shutdown the driver of a MDIO-connected switch
 * @mdiodev: mdio_device shutting down.
 *
 * This function should be used as the .shutdown in an mdio_driver. It shuts
 * down the DSA switch and cleans the platform driver data, to prevent
 * realtek_mdio_remove() from running afterwards, which is possible if the
 * parent bus implements its own .shutdown() as .remove().
 * This function should be used as the .shutdown in a platform_driver. It calls
 * the common shutdown function.
 *
 * Context: Can sleep.
 * Return: Nothing.
@@ -293,9 +183,7 @@ void realtek_mdio_shutdown(struct mdio_device *mdiodev)
	if (!priv)
		return;

	dsa_switch_shutdown(priv->ds);

	dev_set_drvdata(&mdiodev->dev, NULL);
	rtl83xx_shutdown(priv);
}
EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA);

+33 −133
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@

#include "realtek.h"
#include "realtek-smi.h"
#include "rtl83xx.h"

#define REALTEK_SMI_ACK_RETRY_COUNT		5

@@ -311,47 +312,6 @@ static int realtek_smi_read(void *ctx, u32 reg, u32 *val)
	return realtek_smi_read_reg(priv, reg, val);
}

static void realtek_smi_lock(void *ctx)
{
	struct realtek_priv *priv = ctx;

	mutex_lock(&priv->map_lock);
}

static void realtek_smi_unlock(void *ctx)
{
	struct realtek_priv *priv = ctx;

	mutex_unlock(&priv->map_lock);
}

static const struct regmap_config realtek_smi_regmap_config = {
	.reg_bits = 10, /* A4..A0 R4..R0 */
	.val_bits = 16,
	.reg_stride = 1,
	/* PHY regs are at 0x8000 */
	.max_register = 0xffff,
	.reg_format_endian = REGMAP_ENDIAN_BIG,
	.reg_read = realtek_smi_read,
	.reg_write = realtek_smi_write,
	.cache_type = REGCACHE_NONE,
	.lock = realtek_smi_lock,
	.unlock = realtek_smi_unlock,
};

static const struct regmap_config realtek_smi_nolock_regmap_config = {
	.reg_bits = 10, /* A4..A0 R4..R0 */
	.val_bits = 16,
	.reg_stride = 1,
	/* PHY regs are at 0x8000 */
	.max_register = 0xffff,
	.reg_format_endian = REGMAP_ENDIAN_BIG,
	.reg_read = realtek_smi_read,
	.reg_write = realtek_smi_write,
	.cache_type = REGCACHE_NONE,
	.disable_locking = true,
};

static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum)
{
	struct realtek_priv *priv = bus->priv;
@@ -409,111 +369,56 @@ static int realtek_smi_setup_mdio(struct dsa_switch *ds)
	return ret;
}

static const struct realtek_interface_info realtek_smi_info = {
	.reg_read = realtek_smi_read,
	.reg_write = realtek_smi_write,
};

/**
 * realtek_smi_probe() - Probe a platform device for an SMI-connected switch
 * @pdev: platform_device to probe on.
 *
 * This function should be used as the .probe in a platform_driver. It
 * initializes realtek_priv and read data from the device-tree node. The switch
 * is hard reset if a method is provided. It checks the switch chip ID and,
 * finally, a DSA switch is registered.
 * This function should be used as the .probe in a platform_driver. After
 * calling the common probe function for both interfaces, it initializes the
 * values specific for SMI-connected devices. Finally, it calls a common
 * function to register the DSA switch.
 *
 * Context: Can sleep. Takes and releases priv->map_lock.
 * Return: Returns 0 on success, a negative error on failure.
 */
int realtek_smi_probe(struct platform_device *pdev)
{
	const struct realtek_variant *var;
	struct device *dev = &pdev->dev;
	struct realtek_priv *priv;
	struct regmap_config rc;
	struct device_node *np;
	int ret;

	var = of_device_get_match_data(dev);
	np = dev->of_node;

	priv = devm_kzalloc(dev, sizeof(*priv) + var->chip_data_sz, GFP_KERNEL);
	if (!priv)
		return -ENOMEM;
	priv->chip_data = (void *)priv + sizeof(*priv);

	mutex_init(&priv->map_lock);

	rc = realtek_smi_regmap_config;
	rc.lock_arg = priv;
	priv->map = devm_regmap_init(dev, NULL, priv, &rc);
	if (IS_ERR(priv->map)) {
		ret = PTR_ERR(priv->map);
		dev_err(dev, "regmap init failed: %d\n", ret);
		return ret;
	}

	rc = realtek_smi_nolock_regmap_config;
	priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc);
	if (IS_ERR(priv->map_nolock)) {
		ret = PTR_ERR(priv->map_nolock);
		dev_err(dev, "regmap init failed: %d\n", ret);
		return ret;
	}

	/* Link forward and backward */
	priv->dev = dev;
	priv->variant = var;
	priv->ops = var->ops;

	priv->setup_interface = realtek_smi_setup_mdio;
	priv->write_reg_noack = realtek_smi_write_reg_noack;

	dev_set_drvdata(dev, priv);
	spin_lock_init(&priv->lock);

	/* TODO: if power is software controlled, set up any regulators here */

	priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
	if (IS_ERR(priv->reset)) {
		dev_err(dev, "failed to get RESET GPIO\n");
		return PTR_ERR(priv->reset);
	}
	if (priv->reset) {
		gpiod_set_value(priv->reset, 1);
		dev_dbg(dev, "asserted RESET\n");
		msleep(REALTEK_HW_STOP_DELAY);
		gpiod_set_value(priv->reset, 0);
		msleep(REALTEK_HW_START_DELAY);
		dev_dbg(dev, "deasserted RESET\n");
	}
	priv = rtl83xx_probe(dev, &realtek_smi_info);
	if (IS_ERR(priv))
		return PTR_ERR(priv);

	/* Fetch MDIO pins */
	priv->mdc = devm_gpiod_get_optional(dev, "mdc", GPIOD_OUT_LOW);
	if (IS_ERR(priv->mdc))
	if (IS_ERR(priv->mdc)) {
		rtl83xx_remove(priv);
		return PTR_ERR(priv->mdc);
	}

	priv->mdio = devm_gpiod_get_optional(dev, "mdio", GPIOD_OUT_LOW);
	if (IS_ERR(priv->mdio))
	if (IS_ERR(priv->mdio)) {
		rtl83xx_remove(priv);
		return PTR_ERR(priv->mdio);

	priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds");

	ret = priv->ops->detect(priv);
	if (ret) {
		dev_err(dev, "unable to detect switch\n");
		return ret;
	}

	priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL);
	if (!priv->ds)
		return -ENOMEM;

	priv->ds->dev = dev;
	priv->ds->num_ports = priv->num_ports;
	priv->ds->priv = priv;
	priv->write_reg_noack = realtek_smi_write_reg_noack;
	priv->setup_interface = realtek_smi_setup_mdio;
	priv->ds_ops = priv->variant->ds_ops_smi;

	priv->ds->ops = var->ds_ops_smi;
	ret = dsa_register_switch(priv->ds);
	ret = rtl83xx_register_switch(priv);
	if (ret) {
		dev_err_probe(dev, ret, "unable to register switch\n");
		rtl83xx_remove(priv);
		return ret;
	}

	return 0;
}
EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, REALTEK_DSA);
@@ -523,8 +428,8 @@ EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, REALTEK_DSA);
 * @pdev: platform_device to be removed.
 *
 * This function should be used as the .remove_new in a platform_driver. First
 * it unregisters the DSA switch and cleans internal data. If a method is
 * provided, the hard reset is asserted to avoid traffic leakage.
 * it unregisters the DSA switch and cleans internal data. Finally, it calls
 * the common remove function.
 *
 * Context: Can sleep.
 * Return: Nothing.
@@ -536,13 +441,12 @@ void realtek_smi_remove(struct platform_device *pdev)
	if (!priv)
		return;

	dsa_unregister_switch(priv->ds);
	rtl83xx_unregister_switch(priv);

	if (priv->user_mii_bus)
		of_node_put(priv->user_mii_bus->dev.of_node);

	/* leave the device reset asserted */
	if (priv->reset)
		gpiod_set_value(priv->reset, 1);
	rtl83xx_remove(priv);
}
EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA);

@@ -550,10 +454,8 @@ EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA);
 * realtek_smi_shutdown() - Shutdown the driver of a SMI-connected switch
 * @pdev: platform_device shutting down.
 *
 * This function should be used as the .shutdown in a platform_driver. It shuts
 * down the DSA switch and cleans the platform driver data, to prevent
 * realtek_smi_remove() from running afterwards, which is possible if the
 * parent bus implements its own .shutdown() as .remove().
 * This function should be used as the .shutdown in a platform_driver. It calls
 * the common shutdown function.
 *
 * Context: Can sleep.
 * Return: Nothing.
@@ -565,9 +467,7 @@ void realtek_smi_shutdown(struct platform_device *pdev)
	if (!priv)
		return;

	dsa_switch_shutdown(priv->ds);

	platform_set_drvdata(pdev, NULL);
	rtl83xx_shutdown(priv);
}
EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, REALTEK_DSA);

+1 −0
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ struct realtek_priv {

	spinlock_t		lock; /* Locks around command writes */
	struct dsa_switch	*ds;
	const struct dsa_switch_ops *ds_ops;
	struct irq_domain	*irqdomain;
	bool			leds_disabled;

+5 −4
Original line number Diff line number Diff line
@@ -103,6 +103,7 @@
#include "realtek.h"
#include "realtek-smi.h"
#include "realtek-mdio.h"
#include "rtl83xx.h"

/* Family-specific data and limits */
#define RTL8365MB_PHYADDRMAX		7
@@ -691,7 +692,7 @@ static int rtl8365mb_phy_ocp_read(struct realtek_priv *priv, int phy,
	u32 val;
	int ret;

	mutex_lock(&priv->map_lock);
	rtl83xx_lock(priv);

	ret = rtl8365mb_phy_poll_busy(priv);
	if (ret)
@@ -724,7 +725,7 @@ static int rtl8365mb_phy_ocp_read(struct realtek_priv *priv, int phy,
	*data = val & 0xFFFF;

out:
	mutex_unlock(&priv->map_lock);
	rtl83xx_unlock(priv);

	return ret;
}
@@ -735,7 +736,7 @@ static int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy,
	u32 val;
	int ret;

	mutex_lock(&priv->map_lock);
	rtl83xx_lock(priv);

	ret = rtl8365mb_phy_poll_busy(priv);
	if (ret)
@@ -766,7 +767,7 @@ static int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy,
		goto out;

out:
	mutex_unlock(&priv->map_lock);
	rtl83xx_unlock(priv);

	return 0;
}
Loading