mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 01:53:29 +02:00
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: Luiz Angelo Daros de Luca <luizluca@gmail.com> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
4667a1db2f
commit
8be040ecd9
|
|
@ -1,4 +1,6 @@
|
|||
# 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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
static const struct realtek_interface_info realtek_mdio_info = {
|
||||
.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,
|
||||
.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 = rtl83xx_probe(dev, &realtek_mdio_info);
|
||||
if (IS_ERR(priv))
|
||||
return PTR_ERR(priv);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
ret = rtl83xx_register_switch(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);
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
priv->write_reg_noack = realtek_smi_write_reg_noack;
|
||||
priv->setup_interface = realtek_smi_setup_mdio;
|
||||
priv->ds_ops = priv->variant->ds_ops_smi;
|
||||
|
||||
ret = priv->ops->detect(priv);
|
||||
ret = rtl83xx_register_switch(priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to detect switch\n");
|
||||
rtl83xx_remove(priv);
|
||||
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_smi;
|
||||
ret = dsa_register_switch(priv->ds);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "unable to register switch\n");
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "realtek.h"
|
||||
#include "realtek-smi.h"
|
||||
#include "realtek-mdio.h"
|
||||
#include "rtl83xx.h"
|
||||
|
||||
#define RTL8366RB_PORT_NUM_CPU 5
|
||||
#define RTL8366RB_NUM_PORTS 6
|
||||
|
|
@ -1720,7 +1721,7 @@ static int rtl8366rb_phy_read(struct realtek_priv *priv, int phy, int regnum)
|
|||
if (phy > RTL8366RB_PHY_NO_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&priv->map_lock);
|
||||
rtl83xx_lock(priv);
|
||||
|
||||
ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG,
|
||||
RTL8366RB_PHY_CTRL_READ);
|
||||
|
|
@ -1748,7 +1749,7 @@ static int rtl8366rb_phy_read(struct realtek_priv *priv, int phy, int regnum)
|
|||
phy, regnum, reg, val);
|
||||
|
||||
out:
|
||||
mutex_unlock(&priv->map_lock);
|
||||
rtl83xx_unlock(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1762,7 +1763,7 @@ static int rtl8366rb_phy_write(struct realtek_priv *priv, int phy, int regnum,
|
|||
if (phy > RTL8366RB_PHY_NO_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&priv->map_lock);
|
||||
rtl83xx_lock(priv);
|
||||
|
||||
ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG,
|
||||
RTL8366RB_PHY_CTRL_WRITE);
|
||||
|
|
@ -1779,7 +1780,7 @@ static int rtl8366rb_phy_write(struct realtek_priv *priv, int phy, int regnum,
|
|||
goto out;
|
||||
|
||||
out:
|
||||
mutex_unlock(&priv->map_lock);
|
||||
rtl83xx_unlock(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
235
drivers/net/dsa/realtek/rtl83xx.c
Normal file
235
drivers/net/dsa/realtek/rtl83xx.c
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "realtek.h"
|
||||
#include "rtl83xx.h"
|
||||
|
||||
/**
|
||||
* rtl83xx_lock() - Locks the mutex used by regmaps
|
||||
* @ctx: realtek_priv pointer
|
||||
*
|
||||
* This function is passed to regmap to be used as the lock function.
|
||||
* It is also used externally to block regmap before executing multiple
|
||||
* operations that must happen in sequence (which will use
|
||||
* realtek_priv.map_nolock instead).
|
||||
*
|
||||
* Context: Can sleep. Holds priv->map_lock lock.
|
||||
* Return: nothing
|
||||
*/
|
||||
void rtl83xx_lock(void *ctx)
|
||||
{
|
||||
struct realtek_priv *priv = ctx;
|
||||
|
||||
mutex_lock(&priv->map_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(rtl83xx_lock, REALTEK_DSA);
|
||||
|
||||
/**
|
||||
* rtl83xx_unlock() - Unlocks the mutex used by regmaps
|
||||
* @ctx: realtek_priv pointer
|
||||
*
|
||||
* This function unlocks the lock acquired by rtl83xx_lock.
|
||||
*
|
||||
* Context: Releases priv->map_lock lock.
|
||||
* Return: nothing
|
||||
*/
|
||||
void rtl83xx_unlock(void *ctx)
|
||||
{
|
||||
struct realtek_priv *priv = ctx;
|
||||
|
||||
mutex_unlock(&priv->map_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(rtl83xx_unlock, REALTEK_DSA);
|
||||
|
||||
/**
|
||||
* rtl83xx_probe() - probe a Realtek switch
|
||||
* @dev: the device being probed
|
||||
* @interface_info: specific management interface info.
|
||||
*
|
||||
* This function initializes realtek_priv and reads data from the device tree
|
||||
* node. The switch is hard resetted if a method is provided.
|
||||
*
|
||||
* Context: Can sleep.
|
||||
* Return: Pointer to the realtek_priv or ERR_PTR() in case of failure.
|
||||
*
|
||||
* The realtek_priv pointer does not need to be freed as it is controlled by
|
||||
* devres.
|
||||
*/
|
||||
struct realtek_priv *
|
||||
rtl83xx_probe(struct device *dev,
|
||||
const struct realtek_interface_info *interface_info)
|
||||
{
|
||||
const struct realtek_variant *var;
|
||||
struct realtek_priv *priv;
|
||||
struct regmap_config rc = {
|
||||
.reg_bits = 10, /* A4..A0 R4..R0 */
|
||||
.val_bits = 16,
|
||||
.reg_stride = 1,
|
||||
.max_register = 0xffff,
|
||||
.reg_format_endian = REGMAP_ENDIAN_BIG,
|
||||
.reg_read = interface_info->reg_read,
|
||||
.reg_write = interface_info->reg_write,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
.lock = rtl83xx_lock,
|
||||
.unlock = rtl83xx_unlock,
|
||||
};
|
||||
int ret;
|
||||
|
||||
var = of_device_get_match_data(dev);
|
||||
if (!var)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
priv = devm_kzalloc(dev, size_add(sizeof(*priv), var->chip_data_sz),
|
||||
GFP_KERNEL);
|
||||
if (!priv)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_init(&priv->map_lock);
|
||||
|
||||
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 ERR_PTR(ret);
|
||||
}
|
||||
|
||||
rc.disable_locking = true;
|
||||
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 ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/* Link forward and backward */
|
||||
priv->dev = dev;
|
||||
priv->variant = var;
|
||||
priv->ops = var->ops;
|
||||
priv->chip_data = (void *)priv + sizeof(*priv);
|
||||
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
priv->leds_disabled = of_property_read_bool(dev->of_node,
|
||||
"realtek,disable-leds");
|
||||
|
||||
/* 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 ERR_CAST(priv->reset);
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, priv);
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
return priv;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(rtl83xx_probe, REALTEK_DSA);
|
||||
|
||||
/**
|
||||
* rtl83xx_register_switch() - detects and register a switch
|
||||
* @priv: realtek_priv pointer
|
||||
*
|
||||
* This function first checks the switch chip ID and register a DSA
|
||||
* switch.
|
||||
*
|
||||
* Context: Can sleep. Takes and releases priv->map_lock.
|
||||
* Return: 0 on success, negative value for failure.
|
||||
*/
|
||||
int rtl83xx_register_switch(struct realtek_priv *priv)
|
||||
{
|
||||
struct dsa_switch *ds;
|
||||
int ret;
|
||||
|
||||
ret = priv->ops->detect(priv);
|
||||
if (ret) {
|
||||
dev_err_probe(priv->dev, ret, "unable to detect switch\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ds = devm_kzalloc(priv->dev, sizeof(*ds), GFP_KERNEL);
|
||||
if (!ds)
|
||||
return -ENOMEM;
|
||||
|
||||
ds->priv = priv;
|
||||
ds->dev = priv->dev;
|
||||
ds->ops = priv->ds_ops;
|
||||
ds->num_ports = priv->num_ports;
|
||||
priv->ds = ds;
|
||||
|
||||
ret = dsa_register_switch(ds);
|
||||
if (ret) {
|
||||
dev_err_probe(priv->dev, ret, "unable to register switch\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(rtl83xx_register_switch, REALTEK_DSA);
|
||||
|
||||
/**
|
||||
* rtl83xx_unregister_switch() - unregister a switch
|
||||
* @priv: realtek_priv pointer
|
||||
*
|
||||
* This function unregister a DSA switch.
|
||||
*
|
||||
* Context: Can sleep.
|
||||
* Return: Nothing.
|
||||
*/
|
||||
void rtl83xx_unregister_switch(struct realtek_priv *priv)
|
||||
{
|
||||
dsa_unregister_switch(priv->ds);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(rtl83xx_unregister_switch, REALTEK_DSA);
|
||||
|
||||
/**
|
||||
* rtl83xx_shutdown() - shutdown a switch
|
||||
* @priv: realtek_priv pointer
|
||||
*
|
||||
* This function shuts down the DSA switch and cleans the platform driver data,
|
||||
* to prevent realtek_{smi,mdio}_remove() from running afterwards, which is
|
||||
* possible if the parent bus implements its own .shutdown() as .remove().
|
||||
*
|
||||
* Context: Can sleep.
|
||||
* Return: Nothing.
|
||||
*/
|
||||
void rtl83xx_shutdown(struct realtek_priv *priv)
|
||||
{
|
||||
dsa_switch_shutdown(priv->ds);
|
||||
|
||||
dev_set_drvdata(priv->dev, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(rtl83xx_shutdown, REALTEK_DSA);
|
||||
|
||||
/**
|
||||
* rtl83xx_remove() - Cleanup a realtek switch driver
|
||||
* @priv: realtek_priv pointer
|
||||
*
|
||||
* If a method is provided, this function asserts the hard reset of the switch
|
||||
* in order to avoid leaking traffic when the driver is gone.
|
||||
*
|
||||
* Context: Might sleep if priv->gdev->chip->can_sleep.
|
||||
* Return: nothing
|
||||
*/
|
||||
void rtl83xx_remove(struct realtek_priv *priv)
|
||||
{
|
||||
/* leave the device reset asserted */
|
||||
if (priv->reset)
|
||||
gpiod_set_value(priv->reset, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA);
|
||||
|
||||
MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
|
||||
MODULE_DESCRIPTION("Realtek DSA switches common module");
|
||||
MODULE_LICENSE("GPL");
|
||||
21
drivers/net/dsa/realtek/rtl83xx.h
Normal file
21
drivers/net/dsa/realtek/rtl83xx.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
|
||||
#ifndef _RTL83XX_H
|
||||
#define _RTL83XX_H
|
||||
|
||||
struct realtek_interface_info {
|
||||
int (*reg_read)(void *ctx, u32 reg, u32 *val);
|
||||
int (*reg_write)(void *ctx, u32 reg, u32 val);
|
||||
};
|
||||
|
||||
void rtl83xx_lock(void *ctx);
|
||||
void rtl83xx_unlock(void *ctx);
|
||||
struct realtek_priv *
|
||||
rtl83xx_probe(struct device *dev,
|
||||
const struct realtek_interface_info *interface_info);
|
||||
int rtl83xx_register_switch(struct realtek_priv *priv);
|
||||
void rtl83xx_unregister_switch(struct realtek_priv *priv);
|
||||
void rtl83xx_shutdown(struct realtek_priv *priv);
|
||||
void rtl83xx_remove(struct realtek_priv *priv);
|
||||
|
||||
#endif /* _RTL83XX_H */
|
||||
Loading…
Reference in New Issue
Block a user