diff options
Diffstat (limited to 'drivers/net/dsa/realtek')
-rw-r--r-- | drivers/net/dsa/realtek/Kconfig | 6 | ||||
-rw-r--r-- | drivers/net/dsa/realtek/Makefile | 3 | ||||
-rw-r--r-- | drivers/net/dsa/realtek/realtek-mdio.c | 8 | ||||
-rw-r--r-- | drivers/net/dsa/realtek/realtek-smi.c | 8 | ||||
-rw-r--r-- | drivers/net/dsa/realtek/realtek.h | 2 | ||||
-rw-r--r-- | drivers/net/dsa/realtek/rtl8365mb.c | 42 | ||||
-rw-r--r-- | drivers/net/dsa/realtek/rtl8366-core.c | 22 | ||||
-rw-r--r-- | drivers/net/dsa/realtek/rtl8366rb-leds.c | 177 | ||||
-rw-r--r-- | drivers/net/dsa/realtek/rtl8366rb.c | 223 | ||||
-rw-r--r-- | drivers/net/dsa/realtek/rtl8366rb.h | 107 | ||||
-rw-r--r-- | drivers/net/dsa/realtek/rtl83xx.c | 32 |
11 files changed, 438 insertions, 192 deletions
diff --git a/drivers/net/dsa/realtek/Kconfig b/drivers/net/dsa/realtek/Kconfig index 6989972eebc3..d6eb6713e5f6 100644 --- a/drivers/net/dsa/realtek/Kconfig +++ b/drivers/net/dsa/realtek/Kconfig @@ -43,4 +43,10 @@ config NET_DSA_REALTEK_RTL8366RB help Select to enable support for Realtek RTL8366RB. +config NET_DSA_REALTEK_RTL8366RB_LEDS + bool + depends on (LEDS_CLASS=y || LEDS_CLASS=NET_DSA_REALTEK_RTL8366RB) + depends on NET_DSA_REALTEK_RTL8366RB + default NET_DSA_REALTEK_RTL8366RB + endif diff --git a/drivers/net/dsa/realtek/Makefile b/drivers/net/dsa/realtek/Makefile index 35491dc20d6d..17367bcba496 100644 --- a/drivers/net/dsa/realtek/Makefile +++ b/drivers/net/dsa/realtek/Makefile @@ -12,4 +12,7 @@ endif obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366.o rtl8366-objs := rtl8366-core.o rtl8366rb.o +ifdef CONFIG_NET_DSA_REALTEK_RTL8366RB_LEDS +rtl8366-objs += rtl8366rb-leds.o +endif obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o diff --git a/drivers/net/dsa/realtek/realtek-mdio.c b/drivers/net/dsa/realtek/realtek-mdio.c index 04b758e5a680..a5e7dff96e91 100644 --- a/drivers/net/dsa/realtek/realtek-mdio.c +++ b/drivers/net/dsa/realtek/realtek-mdio.c @@ -140,13 +140,13 @@ int realtek_mdio_probe(struct mdio_device *mdiodev) return 0; } -EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe, "REALTEK_DSA"); /** * realtek_mdio_remove() - Remove the driver of an MDIO-connected switch * @mdiodev: mdio_device to be removed. * - * This function should be used as the .remove_new in an mdio_driver. First + * This function should be used as the .remove in an mdio_driver. First * it unregisters the DSA switch and then it calls the common remove function. * * Context: Can sleep. @@ -163,7 +163,7 @@ void realtek_mdio_remove(struct mdio_device *mdiodev) rtl83xx_remove(priv); } -EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, "REALTEK_DSA"); /** * realtek_mdio_shutdown() - Shutdown the driver of a MDIO-connected switch @@ -184,4 +184,4 @@ void realtek_mdio_shutdown(struct mdio_device *mdiodev) rtl83xx_shutdown(priv); } -EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, "REALTEK_DSA"); diff --git a/drivers/net/dsa/realtek/realtek-smi.c b/drivers/net/dsa/realtek/realtek-smi.c index 88590ae95a75..972e22218418 100644 --- a/drivers/net/dsa/realtek/realtek-smi.c +++ b/drivers/net/dsa/realtek/realtek-smi.c @@ -361,13 +361,13 @@ int realtek_smi_probe(struct platform_device *pdev) return 0; } -EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, "REALTEK_DSA"); /** * realtek_smi_remove() - Remove the driver of a SMI-connected switch * @pdev: platform_device to be removed. * - * This function should be used as the .remove_new in a platform_driver. First + * This function should be used as the .remove in a platform_driver. First * it unregisters the DSA switch and then it calls the common remove function. * * Context: Can sleep. @@ -384,7 +384,7 @@ void realtek_smi_remove(struct platform_device *pdev) rtl83xx_remove(priv); } -EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, "REALTEK_DSA"); /** * realtek_smi_shutdown() - Shutdown the driver of a SMI-connected switch @@ -405,4 +405,4 @@ void realtek_smi_shutdown(struct platform_device *pdev) rtl83xx_shutdown(priv); } -EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, "REALTEK_DSA"); diff --git a/drivers/net/dsa/realtek/realtek.h b/drivers/net/dsa/realtek/realtek.h index e0b1aa01337b..a1b2e0b529d5 100644 --- a/drivers/net/dsa/realtek/realtek.h +++ b/drivers/net/dsa/realtek/realtek.h @@ -17,6 +17,7 @@ #define REALTEK_HW_STOP_DELAY 25 /* msecs */ #define REALTEK_HW_START_DELAY 100 /* msecs */ +struct phylink_mac_ops; struct realtek_ops; struct dentry; struct inode; @@ -117,6 +118,7 @@ struct realtek_ops { struct realtek_variant { const struct dsa_switch_ops *ds_ops; const struct realtek_ops *ops; + const struct phylink_mac_ops *phylink_mac_ops; unsigned int clk_delay; u8 cmd_read; u8 cmd_write; diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c index 12665a8a3412..964a56ee16cc 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -1048,11 +1048,13 @@ static void rtl8365mb_phylink_get_caps(struct dsa_switch *ds, int port, phy_interface_set_rgmii(config->supported_interfaces); } -static void rtl8365mb_phylink_mac_config(struct dsa_switch *ds, int port, +static void rtl8365mb_phylink_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { - struct realtek_priv *priv = ds->priv; + struct dsa_port *dp = dsa_phylink_to_port(config); + struct realtek_priv *priv = dp->ds->priv; + u8 port = dp->index; int ret; if (mode != MLO_AN_PHY && mode != MLO_AN_FIXED) { @@ -1076,13 +1078,15 @@ static void rtl8365mb_phylink_mac_config(struct dsa_switch *ds, int port, */ } -static void rtl8365mb_phylink_mac_link_down(struct dsa_switch *ds, int port, +static void rtl8365mb_phylink_mac_link_down(struct phylink_config *config, unsigned int mode, phy_interface_t interface) { - struct realtek_priv *priv = ds->priv; + struct dsa_port *dp = dsa_phylink_to_port(config); + struct realtek_priv *priv = dp->ds->priv; struct rtl8365mb_port *p; struct rtl8365mb *mb; + u8 port = dp->index; int ret; mb = priv->chip_data; @@ -1101,16 +1105,18 @@ static void rtl8365mb_phylink_mac_link_down(struct dsa_switch *ds, int port, } } -static void rtl8365mb_phylink_mac_link_up(struct dsa_switch *ds, int port, +static void rtl8365mb_phylink_mac_link_up(struct phylink_config *config, + struct phy_device *phydev, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev, int speed, - int duplex, bool tx_pause, + int speed, int duplex, bool tx_pause, bool rx_pause) { - struct realtek_priv *priv = ds->priv; + struct dsa_port *dp = dsa_phylink_to_port(config); + struct realtek_priv *priv = dp->ds->priv; struct rtl8365mb_port *p; struct rtl8365mb *mb; + u8 port = dp->index; int ret; mb = priv->chip_data; @@ -1713,8 +1719,8 @@ static int rtl8365mb_irq_setup(struct realtek_priv *priv) goto out_put_node; } - priv->irqdomain = irq_domain_add_linear(intc, priv->num_ports, - &rtl8365mb_irqdomain_ops, priv); + priv->irqdomain = irq_domain_create_linear(of_fwnode_handle(intc), priv->num_ports, + &rtl8365mb_irqdomain_ops, priv); if (!priv->irqdomain) { dev_err(priv->dev, "failed to add irq domain\n"); ret = -ENOMEM; @@ -1734,7 +1740,7 @@ static int rtl8365mb_irq_setup(struct realtek_priv *priv) } /* Configure chip interrupt signal polarity */ - irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq)); + irq_trig = irq_get_trigger_type(irq); switch (irq_trig) { case IRQF_TRIGGER_RISING: case IRQF_TRIGGER_HIGH: @@ -2106,15 +2112,18 @@ static int rtl8365mb_detect(struct realtek_priv *priv) return 0; } +static const struct phylink_mac_ops rtl8365mb_phylink_mac_ops = { + .mac_config = rtl8365mb_phylink_mac_config, + .mac_link_down = rtl8365mb_phylink_mac_link_down, + .mac_link_up = rtl8365mb_phylink_mac_link_up, +}; + static const struct dsa_switch_ops rtl8365mb_switch_ops = { .get_tag_protocol = rtl8365mb_get_tag_protocol, .change_tag_protocol = rtl8365mb_change_tag_protocol, .setup = rtl8365mb_setup, .teardown = rtl8365mb_teardown, .phylink_get_caps = rtl8365mb_phylink_get_caps, - .phylink_mac_config = rtl8365mb_phylink_mac_config, - .phylink_mac_link_down = rtl8365mb_phylink_mac_link_down, - .phylink_mac_link_up = rtl8365mb_phylink_mac_link_up, .port_stp_state_set = rtl8365mb_port_stp_state_set, .get_strings = rtl8365mb_get_strings, .get_ethtool_stats = rtl8365mb_get_ethtool_stats, @@ -2136,6 +2145,7 @@ static const struct realtek_ops rtl8365mb_ops = { const struct realtek_variant rtl8365mb_variant = { .ds_ops = &rtl8365mb_switch_ops, .ops = &rtl8365mb_ops, + .phylink_mac_ops = &rtl8365mb_phylink_mac_ops, .clk_delay = 10, .cmd_read = 0xb9, .cmd_write = 0xb8, @@ -2154,7 +2164,7 @@ static struct platform_driver rtl8365mb_smi_driver = { .of_match_table = rtl8365mb_of_match, }, .probe = realtek_smi_probe, - .remove_new = realtek_smi_remove, + .remove = realtek_smi_remove, .shutdown = realtek_smi_shutdown, }; @@ -2196,4 +2206,4 @@ module_exit(rtl8365mb_exit); MODULE_AUTHOR("Alvin Šipraga <alsi@bang-olufsen.dk>"); MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(REALTEK_DSA); +MODULE_IMPORT_NS("REALTEK_DSA"); diff --git a/drivers/net/dsa/realtek/rtl8366-core.c b/drivers/net/dsa/realtek/rtl8366-core.c index 7c6520ba3a26..047feeed96a2 100644 --- a/drivers/net/dsa/realtek/rtl8366-core.c +++ b/drivers/net/dsa/realtek/rtl8366-core.c @@ -34,7 +34,7 @@ int rtl8366_mc_is_used(struct realtek_priv *priv, int mc_index, int *used) return 0; } -EXPORT_SYMBOL_NS_GPL(rtl8366_mc_is_used, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl8366_mc_is_used, "REALTEK_DSA"); /** * rtl8366_obtain_mc() - retrieve or allocate a VLAN member configuration @@ -187,7 +187,7 @@ int rtl8366_set_vlan(struct realtek_priv *priv, int vid, u32 member, return ret; } -EXPORT_SYMBOL_NS_GPL(rtl8366_set_vlan, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl8366_set_vlan, "REALTEK_DSA"); int rtl8366_set_pvid(struct realtek_priv *priv, unsigned int port, unsigned int vid) @@ -217,7 +217,7 @@ int rtl8366_set_pvid(struct realtek_priv *priv, unsigned int port, return 0; } -EXPORT_SYMBOL_NS_GPL(rtl8366_set_pvid, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl8366_set_pvid, "REALTEK_DSA"); int rtl8366_enable_vlan4k(struct realtek_priv *priv, bool enable) { @@ -243,7 +243,7 @@ int rtl8366_enable_vlan4k(struct realtek_priv *priv, bool enable) priv->vlan4k_enabled = enable; return 0; } -EXPORT_SYMBOL_NS_GPL(rtl8366_enable_vlan4k, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl8366_enable_vlan4k, "REALTEK_DSA"); int rtl8366_enable_vlan(struct realtek_priv *priv, bool enable) { @@ -265,7 +265,7 @@ int rtl8366_enable_vlan(struct realtek_priv *priv, bool enable) return ret; } -EXPORT_SYMBOL_NS_GPL(rtl8366_enable_vlan, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl8366_enable_vlan, "REALTEK_DSA"); int rtl8366_reset_vlan(struct realtek_priv *priv) { @@ -290,7 +290,7 @@ int rtl8366_reset_vlan(struct realtek_priv *priv) return 0; } -EXPORT_SYMBOL_NS_GPL(rtl8366_reset_vlan, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl8366_reset_vlan, "REALTEK_DSA"); int rtl8366_vlan_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan, @@ -345,7 +345,7 @@ int rtl8366_vlan_add(struct dsa_switch *ds, int port, return 0; } -EXPORT_SYMBOL_NS_GPL(rtl8366_vlan_add, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl8366_vlan_add, "REALTEK_DSA"); int rtl8366_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) @@ -389,7 +389,7 @@ int rtl8366_vlan_del(struct dsa_switch *ds, int port, return 0; } -EXPORT_SYMBOL_NS_GPL(rtl8366_vlan_del, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl8366_vlan_del, "REALTEK_DSA"); void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) @@ -403,7 +403,7 @@ void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset, for (i = 0; i < priv->num_mib_counters; i++) ethtool_puts(&data, priv->mib_counters[i].name); } -EXPORT_SYMBOL_NS_GPL(rtl8366_get_strings, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl8366_get_strings, "REALTEK_DSA"); int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset) { @@ -417,7 +417,7 @@ int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset) return priv->num_mib_counters; } -EXPORT_SYMBOL_NS_GPL(rtl8366_get_sset_count, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl8366_get_sset_count, "REALTEK_DSA"); void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) { @@ -441,4 +441,4 @@ void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) data[i] = mibvalue; } } -EXPORT_SYMBOL_NS_GPL(rtl8366_get_ethtool_stats, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl8366_get_ethtool_stats, "REALTEK_DSA"); diff --git a/drivers/net/dsa/realtek/rtl8366rb-leds.c b/drivers/net/dsa/realtek/rtl8366rb-leds.c new file mode 100644 index 000000000000..99c890681ae6 --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8366rb-leds.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/bitops.h> +#include <linux/regmap.h> +#include <net/dsa.h> +#include "rtl83xx.h" +#include "rtl8366rb.h" + +static inline u32 rtl8366rb_led_group_port_mask(u8 led_group, u8 port) +{ + switch (led_group) { + case 0: + return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); + case 1: + return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); + case 2: + return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); + case 3: + return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); + default: + return 0; + } +} + +static int rb8366rb_get_port_led(struct rtl8366rb_led *led) +{ + struct realtek_priv *priv = led->priv; + u8 led_group = led->led_group; + u8 port_num = led->port_num; + int ret; + u32 val; + + ret = regmap_read(priv->map, RTL8366RB_LED_X_X_CTRL_REG(led_group), + &val); + if (ret) { + dev_err(priv->dev, "error reading LED on port %d group %d\n", + led_group, port_num); + return ret; + } + + return !!(val & rtl8366rb_led_group_port_mask(led_group, port_num)); +} + +static int rb8366rb_set_port_led(struct rtl8366rb_led *led, bool enable) +{ + struct realtek_priv *priv = led->priv; + u8 led_group = led->led_group; + u8 port_num = led->port_num; + int ret; + + ret = regmap_update_bits(priv->map, + RTL8366RB_LED_X_X_CTRL_REG(led_group), + rtl8366rb_led_group_port_mask(led_group, + port_num), + enable ? 0xffff : 0); + if (ret) { + dev_err(priv->dev, "error updating LED on port %d group %d\n", + led_group, port_num); + return ret; + } + + /* Change the LED group to manual controlled LEDs if required */ + ret = rb8366rb_set_ledgroup_mode(priv, led_group, + RTL8366RB_LEDGROUP_FORCE); + + if (ret) { + dev_err(priv->dev, "error updating LED GROUP group %d\n", + led_group); + return ret; + } + + return 0; +} + +static int +rtl8366rb_cled_brightness_set_blocking(struct led_classdev *ldev, + enum led_brightness brightness) +{ + struct rtl8366rb_led *led = container_of(ldev, struct rtl8366rb_led, + cdev); + + return rb8366rb_set_port_led(led, brightness == LED_ON); +} + +static int rtl8366rb_setup_led(struct realtek_priv *priv, struct dsa_port *dp, + struct fwnode_handle *led_fwnode) +{ + struct rtl8366rb *rb = priv->chip_data; + struct led_init_data init_data = { }; + enum led_default_state state; + struct rtl8366rb_led *led; + u32 led_group; + int ret; + + ret = fwnode_property_read_u32(led_fwnode, "reg", &led_group); + if (ret) + return ret; + + if (led_group >= RTL8366RB_NUM_LEDGROUPS) { + dev_warn(priv->dev, "Invalid LED reg %d defined for port %d", + led_group, dp->index); + return -EINVAL; + } + + led = &rb->leds[dp->index][led_group]; + led->port_num = dp->index; + led->led_group = led_group; + led->priv = priv; + + state = led_init_default_state_get(led_fwnode); + switch (state) { + case LEDS_DEFSTATE_ON: + led->cdev.brightness = 1; + rb8366rb_set_port_led(led, 1); + break; + case LEDS_DEFSTATE_KEEP: + led->cdev.brightness = + rb8366rb_get_port_led(led); + break; + case LEDS_DEFSTATE_OFF: + default: + led->cdev.brightness = 0; + rb8366rb_set_port_led(led, 0); + } + + led->cdev.max_brightness = 1; + led->cdev.brightness_set_blocking = + rtl8366rb_cled_brightness_set_blocking; + init_data.fwnode = led_fwnode; + init_data.devname_mandatory = true; + + init_data.devicename = kasprintf(GFP_KERNEL, "Realtek-%d:0%d:%d", + dp->ds->index, dp->index, led_group); + if (!init_data.devicename) + return -ENOMEM; + + ret = devm_led_classdev_register_ext(priv->dev, &led->cdev, &init_data); + if (ret) { + dev_warn(priv->dev, "Failed to init LED %d for port %d", + led_group, dp->index); + return ret; + } + + return 0; +} + +int rtl8366rb_setup_leds(struct realtek_priv *priv) +{ + struct dsa_switch *ds = &priv->ds; + struct device_node *leds_np; + struct dsa_port *dp; + int ret = 0; + + dsa_switch_for_each_port(dp, ds) { + if (!dp->dn) + continue; + + leds_np = of_get_child_by_name(dp->dn, "leds"); + if (!leds_np) { + dev_dbg(priv->dev, "No leds defined for port %d", + dp->index); + continue; + } + + for_each_child_of_node_scoped(leds_np, led_np) { + ret = rtl8366rb_setup_led(priv, dp, + of_fwnode_handle(led_np)); + if (ret) + break; + } + + of_node_put(leds_np); + if (ret) + return ret; + } + return 0; +} diff --git a/drivers/net/dsa/realtek/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c index e10ae94cf771..8bdb52b5fdcb 100644 --- a/drivers/net/dsa/realtek/rtl8366rb.c +++ b/drivers/net/dsa/realtek/rtl8366rb.c @@ -21,16 +21,13 @@ #include <linux/irqchip/chained_irq.h> #include <linux/of_irq.h> #include <linux/regmap.h> +#include <linux/string_choices.h> #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 -#define RTL8366RB_PHY_NO_MAX 4 -#define RTL8366RB_PHY_ADDR_MAX 31 +#include "rtl8366rb.h" /* Switch Global Configuration register */ #define RTL8366RB_SGCR 0x0000 @@ -175,38 +172,6 @@ */ #define RTL8366RB_VLAN_INGRESS_CTRL2_REG 0x037f -/* LED control registers */ -#define RTL8366RB_LED_BLINKRATE_REG 0x0430 -#define RTL8366RB_LED_BLINKRATE_MASK 0x0007 -#define RTL8366RB_LED_BLINKRATE_28MS 0x0000 -#define RTL8366RB_LED_BLINKRATE_56MS 0x0001 -#define RTL8366RB_LED_BLINKRATE_84MS 0x0002 -#define RTL8366RB_LED_BLINKRATE_111MS 0x0003 -#define RTL8366RB_LED_BLINKRATE_222MS 0x0004 -#define RTL8366RB_LED_BLINKRATE_446MS 0x0005 - -#define RTL8366RB_LED_CTRL_REG 0x0431 -#define RTL8366RB_LED_OFF 0x0 -#define RTL8366RB_LED_DUP_COL 0x1 -#define RTL8366RB_LED_LINK_ACT 0x2 -#define RTL8366RB_LED_SPD1000 0x3 -#define RTL8366RB_LED_SPD100 0x4 -#define RTL8366RB_LED_SPD10 0x5 -#define RTL8366RB_LED_SPD1000_ACT 0x6 -#define RTL8366RB_LED_SPD100_ACT 0x7 -#define RTL8366RB_LED_SPD10_ACT 0x8 -#define RTL8366RB_LED_SPD100_10_ACT 0x9 -#define RTL8366RB_LED_FIBER 0xa -#define RTL8366RB_LED_AN_FAULT 0xb -#define RTL8366RB_LED_LINK_RX 0xc -#define RTL8366RB_LED_LINK_TX 0xd -#define RTL8366RB_LED_MASTER 0xe -#define RTL8366RB_LED_FORCE 0xf -#define RTL8366RB_LED_0_1_CTRL_REG 0x0432 -#define RTL8366RB_LED_1_OFFSET 6 -#define RTL8366RB_LED_2_3_CTRL_REG 0x0433 -#define RTL8366RB_LED_3_OFFSET 6 - #define RTL8366RB_MIB_COUNT 33 #define RTL8366RB_GLOBAL_MIB_COUNT 1 #define RTL8366RB_MIB_COUNTER_PORT_OFFSET 0x0050 @@ -242,7 +207,6 @@ #define RTL8366RB_PORT_STATUS_AN_MASK 0x0080 #define RTL8366RB_NUM_VLANS 16 -#define RTL8366RB_NUM_LEDGROUPS 4 #define RTL8366RB_NUM_VIDS 4096 #define RTL8366RB_PRIORITYMAX 7 #define RTL8366RB_NUM_FIDS 8 @@ -349,16 +313,6 @@ #define RTL8366RB_GREEN_FEATURE_TX BIT(0) #define RTL8366RB_GREEN_FEATURE_RX BIT(2) -/** - * struct rtl8366rb - RTL8366RB-specific data - * @max_mtu: per-port max MTU setting - * @pvid_enabled: if PVID is set for respective port - */ -struct rtl8366rb { - unsigned int max_mtu[RTL8366RB_NUM_PORTS]; - bool pvid_enabled[RTL8366RB_NUM_PORTS]; -}; - static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = { { 0, 0, 4, "IfInOctets" }, { 0, 4, 4, "EtherStatsOctets" }, @@ -568,7 +522,7 @@ static int rtl8366rb_setup_cascaded_irq(struct realtek_priv *priv) } /* Fetch IRQ edge information from the descriptor */ - irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq)); + irq_trig = irq_get_trigger_type(irq); switch (irq_trig) { case IRQF_TRIGGER_RISING: case IRQF_TRIGGER_HIGH: @@ -596,10 +550,8 @@ static int rtl8366rb_setup_cascaded_irq(struct realtek_priv *priv) dev_err(priv->dev, "unable to request irq: %d\n", ret); goto out_put_node; } - priv->irqdomain = irq_domain_add_linear(intc, - RTL8366RB_NUM_INTERRUPT, - &rtl8366rb_irqdomain_ops, - priv); + priv->irqdomain = irq_domain_create_linear(of_fwnode_handle(intc), RTL8366RB_NUM_INTERRUPT, + &rtl8366rb_irqdomain_ops, priv); if (!priv->irqdomain) { dev_err(priv->dev, "failed to create IRQ domain\n"); ret = -EINVAL; @@ -799,6 +751,47 @@ static int rtl8366rb_jam_table(const struct rtl8366rb_jam_tbl_entry *jam_table, return 0; } +/* This code is used also with LEDs disabled */ +int rb8366rb_set_ledgroup_mode(struct realtek_priv *priv, + u8 led_group, + enum rtl8366_ledgroup_mode mode) +{ + int ret; + u32 val; + + val = mode << RTL8366RB_LED_CTRL_OFFSET(led_group); + + ret = regmap_update_bits(priv->map, + RTL8366RB_LED_CTRL_REG, + RTL8366RB_LED_CTRL_MASK(led_group), + val); + if (ret) + return ret; + + return 0; +} + +/* This code is used also with LEDs disabled */ +static int rtl8366rb_setup_all_leds_off(struct realtek_priv *priv) +{ + int ret = 0; + int i; + + regmap_update_bits(priv->map, + RTL8366RB_INTERRUPT_CONTROL_REG, + RTL8366RB_P4_RGMII_LED, + 0); + + for (i = 0; i < RTL8366RB_NUM_LEDGROUPS; i++) { + ret = rb8366rb_set_ledgroup_mode(priv, i, + RTL8366RB_LEDGROUP_OFF); + if (ret) + return ret; + } + + return ret; +} + static int rtl8366rb_setup(struct dsa_switch *ds) { struct realtek_priv *priv = ds->priv; @@ -807,7 +800,6 @@ static int rtl8366rb_setup(struct dsa_switch *ds) u32 chip_ver = 0; u32 chip_id = 0; int jam_size; - u32 val; int ret; int i; @@ -987,7 +979,9 @@ static int rtl8366rb_setup(struct dsa_switch *ds) if (ret) return ret; - /* Set blinking, TODO: make this configurable */ + /* Set blinking, used by all LED groups using HW triggers. + * TODO: make this configurable + */ ret = regmap_update_bits(priv->map, RTL8366RB_LED_BLINKRATE_REG, RTL8366RB_LED_BLINKRATE_MASK, RTL8366RB_LED_BLINKRATE_56MS); @@ -995,32 +989,17 @@ static int rtl8366rb_setup(struct dsa_switch *ds) return ret; /* Set up LED activity: - * Each port has 4 LEDs, we configure all ports to the same - * behaviour (no individual config) but we can set up each - * LED separately. + * Each port has 4 LEDs on fixed groups. Each group shares the same + * hardware trigger across all ports. LEDs can only be indiviually + * controlled setting the LED group to fixed mode and using the driver + * to toggle them LEDs on/off. */ if (priv->leds_disabled) { - /* Turn everything off */ - regmap_update_bits(priv->map, - RTL8366RB_LED_0_1_CTRL_REG, - 0x0FFF, 0); - regmap_update_bits(priv->map, - RTL8366RB_LED_2_3_CTRL_REG, - 0x0FFF, 0); - regmap_update_bits(priv->map, - RTL8366RB_INTERRUPT_CONTROL_REG, - RTL8366RB_P4_RGMII_LED, - 0); - val = RTL8366RB_LED_OFF; + ret = rtl8366rb_setup_all_leds_off(priv); + if (ret) + return ret; } else { - /* TODO: make this configurable per LED */ - val = RTL8366RB_LED_FORCE; - } - for (i = 0; i < 4; i++) { - ret = regmap_update_bits(priv->map, - RTL8366RB_LED_CTRL_REG, - 0xf << (i * 4), - val << (i * 4)); + ret = rtl8366rb_setup_leds(priv); if (ret) return ret; } @@ -1077,11 +1056,19 @@ static void rtl8366rb_phylink_get_caps(struct dsa_switch *ds, int port, } static void -rtl8366rb_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, - phy_interface_t interface, struct phy_device *phydev, +rtl8366rb_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ +} + +static void +rtl8366rb_mac_link_up(struct phylink_config *config, struct phy_device *phydev, + unsigned int mode, phy_interface_t interface, int speed, int duplex, bool tx_pause, bool rx_pause) { - struct realtek_priv *priv = ds->priv; + struct dsa_port *dp = dsa_phylink_to_port(config); + struct realtek_priv *priv = dp->ds->priv; + int port = dp->index; unsigned int val; int ret; @@ -1147,10 +1134,12 @@ rtl8366rb_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, } static void -rtl8366rb_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, +rtl8366rb_mac_link_down(struct phylink_config *config, unsigned int mode, phy_interface_t interface) { - struct realtek_priv *priv = ds->priv; + struct dsa_port *dp = dsa_phylink_to_port(config); + struct realtek_priv *priv = dp->ds->priv; + int port = dp->index; int ret; if (port != priv->cpu_port) @@ -1167,52 +1156,6 @@ rtl8366rb_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, } } -static void rb8366rb_set_port_led(struct realtek_priv *priv, - int port, bool enable) -{ - u16 val = enable ? 0x3f : 0; - int ret; - - if (priv->leds_disabled) - return; - - switch (port) { - case 0: - ret = regmap_update_bits(priv->map, - RTL8366RB_LED_0_1_CTRL_REG, - 0x3F, val); - break; - case 1: - ret = regmap_update_bits(priv->map, - RTL8366RB_LED_0_1_CTRL_REG, - 0x3F << RTL8366RB_LED_1_OFFSET, - val << RTL8366RB_LED_1_OFFSET); - break; - case 2: - ret = regmap_update_bits(priv->map, - RTL8366RB_LED_2_3_CTRL_REG, - 0x3F, val); - break; - case 3: - ret = regmap_update_bits(priv->map, - RTL8366RB_LED_2_3_CTRL_REG, - 0x3F << RTL8366RB_LED_3_OFFSET, - val << RTL8366RB_LED_3_OFFSET); - break; - case 4: - ret = regmap_update_bits(priv->map, - RTL8366RB_INTERRUPT_CONTROL_REG, - RTL8366RB_P4_RGMII_LED, - enable ? RTL8366RB_P4_RGMII_LED : 0); - break; - default: - dev_err(priv->dev, "no LED for port %d\n", port); - return; - } - if (ret) - dev_err(priv->dev, "error updating LED on port %d\n", port); -} - static int rtl8366rb_port_enable(struct dsa_switch *ds, int port, struct phy_device *phy) @@ -1226,7 +1169,6 @@ rtl8366rb_port_enable(struct dsa_switch *ds, int port, if (ret) return ret; - rb8366rb_set_port_led(priv, port, true); return 0; } @@ -1241,8 +1183,6 @@ rtl8366rb_port_disable(struct dsa_switch *ds, int port) BIT(port)); if (ret) return; - - rb8366rb_set_port_led(priv, port, false); } static int @@ -1335,7 +1275,7 @@ static int rtl8366rb_vlan_filtering(struct dsa_switch *ds, int port, rb = priv->chip_data; dev_dbg(priv->dev, "port %d: %s VLAN filtering\n", port, - vlan_filtering ? "enable" : "disable"); + str_enable_disable(vlan_filtering)); /* If the port is not in the member set, the frame will be dropped */ ret = regmap_update_bits(priv->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG, @@ -1697,7 +1637,7 @@ static bool rtl8366rb_is_vlan_valid(struct realtek_priv *priv, unsigned int vlan static int rtl8366rb_enable_vlan(struct realtek_priv *priv, bool enable) { - dev_dbg(priv->dev, "%s VLAN\n", enable ? "enable" : "disable"); + dev_dbg(priv->dev, "%s VLAN\n", str_enable_disable(enable)); return regmap_update_bits(priv->map, RTL8366RB_SGCR, RTL8366RB_SGCR_EN_VLAN, enable ? RTL8366RB_SGCR_EN_VLAN : 0); @@ -1705,7 +1645,7 @@ static int rtl8366rb_enable_vlan(struct realtek_priv *priv, bool enable) static int rtl8366rb_enable_vlan4k(struct realtek_priv *priv, bool enable) { - dev_dbg(priv->dev, "%s VLAN 4k\n", enable ? "enable" : "disable"); + dev_dbg(priv->dev, "%s VLAN 4k\n", str_enable_disable(enable)); return regmap_update_bits(priv->map, RTL8366RB_SGCR, RTL8366RB_SGCR_EN_VLAN_4KTB, enable ? RTL8366RB_SGCR_EN_VLAN_4KTB : 0); @@ -1849,12 +1789,16 @@ static int rtl8366rb_detect(struct realtek_priv *priv) return 0; } +static const struct phylink_mac_ops rtl8366rb_phylink_mac_ops = { + .mac_config = rtl8366rb_mac_config, + .mac_link_down = rtl8366rb_mac_link_down, + .mac_link_up = rtl8366rb_mac_link_up, +}; + static const struct dsa_switch_ops rtl8366rb_switch_ops = { .get_tag_protocol = rtl8366_get_tag_protocol, .setup = rtl8366rb_setup, .phylink_get_caps = rtl8366rb_phylink_get_caps, - .phylink_mac_link_up = rtl8366rb_mac_link_up, - .phylink_mac_link_down = rtl8366rb_mac_link_down, .get_strings = rtl8366_get_strings, .get_ethtool_stats = rtl8366_get_ethtool_stats, .get_sset_count = rtl8366_get_sset_count, @@ -1892,6 +1836,7 @@ static const struct realtek_ops rtl8366rb_ops = { const struct realtek_variant rtl8366rb_variant = { .ds_ops = &rtl8366rb_switch_ops, .ops = &rtl8366rb_ops, + .phylink_mac_ops = &rtl8366rb_phylink_mac_ops, .clk_delay = 10, .cmd_read = 0xa9, .cmd_write = 0xa8, @@ -1910,7 +1855,7 @@ static struct platform_driver rtl8366rb_smi_driver = { .of_match_table = rtl8366rb_of_match, }, .probe = realtek_smi_probe, - .remove_new = realtek_smi_remove, + .remove = realtek_smi_remove, .shutdown = realtek_smi_shutdown, }; @@ -1952,4 +1897,4 @@ module_exit(rtl8366rb_exit); MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); MODULE_DESCRIPTION("Driver for RTL8366RB ethernet switch"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(REALTEK_DSA); +MODULE_IMPORT_NS("REALTEK_DSA"); diff --git a/drivers/net/dsa/realtek/rtl8366rb.h b/drivers/net/dsa/realtek/rtl8366rb.h new file mode 100644 index 000000000000..685ff3275faa --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8366rb.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef _RTL8366RB_H +#define _RTL8366RB_H + +#include "realtek.h" + +#define RTL8366RB_PORT_NUM_CPU 5 +#define RTL8366RB_NUM_PORTS 6 +#define RTL8366RB_PHY_NO_MAX 4 +#define RTL8366RB_NUM_LEDGROUPS 4 +#define RTL8366RB_PHY_ADDR_MAX 31 + +/* LED control registers */ +/* The LED blink rate is global; it is used by all triggers in all groups. */ +#define RTL8366RB_LED_BLINKRATE_REG 0x0430 +#define RTL8366RB_LED_BLINKRATE_MASK 0x0007 +#define RTL8366RB_LED_BLINKRATE_28MS 0x0000 +#define RTL8366RB_LED_BLINKRATE_56MS 0x0001 +#define RTL8366RB_LED_BLINKRATE_84MS 0x0002 +#define RTL8366RB_LED_BLINKRATE_111MS 0x0003 +#define RTL8366RB_LED_BLINKRATE_222MS 0x0004 +#define RTL8366RB_LED_BLINKRATE_446MS 0x0005 + +/* LED trigger event for each group */ +#define RTL8366RB_LED_CTRL_REG 0x0431 +#define RTL8366RB_LED_CTRL_OFFSET(led_group) \ + (4 * (led_group)) +#define RTL8366RB_LED_CTRL_MASK(led_group) \ + (0xf << RTL8366RB_LED_CTRL_OFFSET(led_group)) + +/* The RTL8366RB_LED_X_X registers are used to manually set the LED state only + * when the corresponding LED group in RTL8366RB_LED_CTRL_REG is + * RTL8366RB_LEDGROUP_FORCE. Otherwise, it is ignored. + */ +#define RTL8366RB_LED_0_1_CTRL_REG 0x0432 +#define RTL8366RB_LED_2_3_CTRL_REG 0x0433 +#define RTL8366RB_LED_X_X_CTRL_REG(led_group) \ + ((led_group) <= 1 ? \ + RTL8366RB_LED_0_1_CTRL_REG : \ + RTL8366RB_LED_2_3_CTRL_REG) +#define RTL8366RB_LED_0_X_CTRL_MASK GENMASK(5, 0) +#define RTL8366RB_LED_X_1_CTRL_MASK GENMASK(11, 6) +#define RTL8366RB_LED_2_X_CTRL_MASK GENMASK(5, 0) +#define RTL8366RB_LED_X_3_CTRL_MASK GENMASK(11, 6) + +enum rtl8366_ledgroup_mode { + RTL8366RB_LEDGROUP_OFF = 0x0, + RTL8366RB_LEDGROUP_DUP_COL = 0x1, + RTL8366RB_LEDGROUP_LINK_ACT = 0x2, + RTL8366RB_LEDGROUP_SPD1000 = 0x3, + RTL8366RB_LEDGROUP_SPD100 = 0x4, + RTL8366RB_LEDGROUP_SPD10 = 0x5, + RTL8366RB_LEDGROUP_SPD1000_ACT = 0x6, + RTL8366RB_LEDGROUP_SPD100_ACT = 0x7, + RTL8366RB_LEDGROUP_SPD10_ACT = 0x8, + RTL8366RB_LEDGROUP_SPD100_10_ACT = 0x9, + RTL8366RB_LEDGROUP_FIBER = 0xa, + RTL8366RB_LEDGROUP_AN_FAULT = 0xb, + RTL8366RB_LEDGROUP_LINK_RX = 0xc, + RTL8366RB_LEDGROUP_LINK_TX = 0xd, + RTL8366RB_LEDGROUP_MASTER = 0xe, + RTL8366RB_LEDGROUP_FORCE = 0xf, + + __RTL8366RB_LEDGROUP_MODE_MAX +}; + +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB_LEDS) + +struct rtl8366rb_led { + u8 port_num; + u8 led_group; + struct realtek_priv *priv; + struct led_classdev cdev; +}; + +int rtl8366rb_setup_leds(struct realtek_priv *priv); + +#else + +static inline int rtl8366rb_setup_leds(struct realtek_priv *priv) +{ + return 0; +} + +#endif /* IS_ENABLED(CONFIG_LEDS_CLASS) */ + +/** + * struct rtl8366rb - RTL8366RB-specific data + * @max_mtu: per-port max MTU setting + * @pvid_enabled: if PVID is set for respective port + * @leds: per-port and per-ledgroup led info + */ +struct rtl8366rb { + unsigned int max_mtu[RTL8366RB_NUM_PORTS]; + bool pvid_enabled[RTL8366RB_NUM_PORTS]; +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB_LEDS) + struct rtl8366rb_led leds[RTL8366RB_NUM_PORTS][RTL8366RB_NUM_LEDGROUPS]; +#endif +}; + +/* This code is used also with LEDs disabled */ +int rb8366rb_set_ledgroup_mode(struct realtek_priv *priv, + u8 led_group, + enum rtl8366_ledgroup_mode mode); + +#endif /* _RTL8366RB_H */ diff --git a/drivers/net/dsa/realtek/rtl83xx.c b/drivers/net/dsa/realtek/rtl83xx.c index d2e876805393..2b9bd4462714 100644 --- a/drivers/net/dsa/realtek/rtl83xx.c +++ b/drivers/net/dsa/realtek/rtl83xx.c @@ -25,7 +25,7 @@ void rtl83xx_lock(void *ctx) mutex_lock(&priv->map_lock); } -EXPORT_SYMBOL_NS_GPL(rtl83xx_lock, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl83xx_lock, "REALTEK_DSA"); /** * rtl83xx_unlock() - Unlocks the mutex used by regmaps @@ -42,7 +42,7 @@ void rtl83xx_unlock(void *ctx) mutex_unlock(&priv->map_lock); } -EXPORT_SYMBOL_NS_GPL(rtl83xx_unlock, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl83xx_unlock, "REALTEK_DSA"); static int rtl83xx_user_mdio_read(struct mii_bus *bus, int addr, int regnum) { @@ -109,7 +109,7 @@ err_put_node: return ret; } -EXPORT_SYMBOL_NS_GPL(rtl83xx_setup_user_mdio, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl83xx_setup_user_mdio, "REALTEK_DSA"); /** * rtl83xx_probe() - probe a Realtek switch @@ -185,11 +185,9 @@ rtl83xx_probe(struct device *dev, /* TODO: if power is software controlled, set up any regulators here */ priv->reset_ctl = devm_reset_control_get_optional(dev, NULL); - if (IS_ERR(priv->reset_ctl)) { - ret = PTR_ERR(priv->reset_ctl); - dev_err_probe(dev, ret, "failed to get reset control\n"); - return ERR_CAST(priv->reset_ctl); - } + if (IS_ERR(priv->reset_ctl)) + return dev_err_cast_probe(dev, priv->reset_ctl, + "failed to get reset control\n"); priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(priv->reset)) { @@ -210,7 +208,7 @@ rtl83xx_probe(struct device *dev, return priv; } -EXPORT_SYMBOL_NS_GPL(rtl83xx_probe, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl83xx_probe, "REALTEK_DSA"); /** * rtl83xx_register_switch() - detects and register a switch @@ -236,6 +234,7 @@ int rtl83xx_register_switch(struct realtek_priv *priv) ds->priv = priv; ds->dev = priv->dev; ds->ops = priv->variant->ds_ops; + ds->phylink_mac_ops = priv->variant->phylink_mac_ops; ds->num_ports = priv->num_ports; ret = dsa_register_switch(ds); @@ -246,7 +245,7 @@ int rtl83xx_register_switch(struct realtek_priv *priv) return 0; } -EXPORT_SYMBOL_NS_GPL(rtl83xx_register_switch, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl83xx_register_switch, "REALTEK_DSA"); /** * rtl83xx_unregister_switch() - unregister a switch @@ -263,7 +262,7 @@ void rtl83xx_unregister_switch(struct realtek_priv *priv) dsa_unregister_switch(ds); } -EXPORT_SYMBOL_NS_GPL(rtl83xx_unregister_switch, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl83xx_unregister_switch, "REALTEK_DSA"); /** * rtl83xx_shutdown() - shutdown a switch @@ -284,24 +283,21 @@ void rtl83xx_shutdown(struct realtek_priv *priv) dev_set_drvdata(priv->dev, NULL); } -EXPORT_SYMBOL_NS_GPL(rtl83xx_shutdown, REALTEK_DSA); +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. + * Placehold for common cleanup procedures. * - * Context: Might sleep if priv->gdev->chip->can_sleep. + * Context: Any * Return: nothing */ void rtl83xx_remove(struct realtek_priv *priv) { - /* leave the device reset asserted */ - rtl83xx_reset_assert(priv); } -EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA); +EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, "REALTEK_DSA"); void rtl83xx_reset_assert(struct realtek_priv *priv) { |