diff options
Diffstat (limited to 'drivers/net/dsa/microchip/ksz_common.c')
-rw-r--r-- | drivers/net/dsa/microchip/ksz_common.c | 1126 |
1 files changed, 1113 insertions, 13 deletions
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index ed7d137cba99..d612181b3226 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -14,6 +14,9 @@ #include <linux/phy.h> #include <linux/etherdevice.h> #include <linux/if_bridge.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/of_mdio.h> #include <linux/of_device.h> #include <linux/of_net.h> #include <linux/micrel_phy.h> @@ -170,12 +173,20 @@ static const struct ksz_dev_ops ksz8_dev_ops = { .exit = ksz8_switch_exit, }; +static void ksz9477_phylink_mac_link_up(struct ksz_device *dev, int port, + unsigned int mode, + phy_interface_t interface, + struct phy_device *phydev, int speed, + int duplex, bool tx_pause, + bool rx_pause); + static const struct ksz_dev_ops ksz9477_dev_ops = { .setup = ksz9477_setup, .get_port_addr = ksz9477_get_port_addr, .cfg_port_member = ksz9477_cfg_port_member, .flush_dyn_mac_table = ksz9477_flush_dyn_mac_table, .port_setup = ksz9477_port_setup, + .set_ageing_time = ksz9477_set_ageing_time, .r_phy = ksz9477_r_phy, .w_phy = ksz9477_w_phy, .r_mib_cnt = ksz9477_r_mib_cnt, @@ -196,6 +207,7 @@ static const struct ksz_dev_ops ksz9477_dev_ops = { .mdb_del = ksz9477_mdb_del, .change_mtu = ksz9477_change_mtu, .max_mtu = ksz9477_max_mtu, + .phylink_mac_link_up = ksz9477_phylink_mac_link_up, .config_cpu_port = ksz9477_config_cpu_port, .enable_stp_addr = ksz9477_enable_stp_addr, .reset = ksz9477_reset_switch, @@ -205,10 +217,12 @@ static const struct ksz_dev_ops ksz9477_dev_ops = { static const struct ksz_dev_ops lan937x_dev_ops = { .setup = lan937x_setup, + .teardown = lan937x_teardown, .get_port_addr = ksz9477_get_port_addr, .cfg_port_member = ksz9477_cfg_port_member, .flush_dyn_mac_table = ksz9477_flush_dyn_mac_table, .port_setup = lan937x_port_setup, + .set_ageing_time = lan937x_set_ageing_time, .r_phy = lan937x_r_phy, .w_phy = lan937x_w_phy, .r_mib_cnt = ksz9477_r_mib_cnt, @@ -230,6 +244,7 @@ static const struct ksz_dev_ops lan937x_dev_ops = { .mdb_del = ksz9477_mdb_del, .change_mtu = lan937x_change_mtu, .max_mtu = ksz9477_max_mtu, + .phylink_mac_link_up = ksz9477_phylink_mac_link_up, .config_cpu_port = lan937x_config_cpu_port, .enable_stp_addr = ksz9477_enable_stp_addr, .reset = lan937x_reset_switch, @@ -412,7 +427,636 @@ static const u8 lan937x_shifts[] = { [ALU_STAT_INDEX] = 8, }; +static const struct regmap_range ksz8563_valid_regs[] = { + regmap_reg_range(0x0000, 0x0003), + regmap_reg_range(0x0006, 0x0006), + regmap_reg_range(0x000f, 0x001f), + regmap_reg_range(0x0100, 0x0100), + regmap_reg_range(0x0104, 0x0107), + regmap_reg_range(0x010d, 0x010d), + regmap_reg_range(0x0110, 0x0113), + regmap_reg_range(0x0120, 0x012b), + regmap_reg_range(0x0201, 0x0201), + regmap_reg_range(0x0210, 0x0213), + regmap_reg_range(0x0300, 0x0300), + regmap_reg_range(0x0302, 0x031b), + regmap_reg_range(0x0320, 0x032b), + regmap_reg_range(0x0330, 0x0336), + regmap_reg_range(0x0338, 0x033e), + regmap_reg_range(0x0340, 0x035f), + regmap_reg_range(0x0370, 0x0370), + regmap_reg_range(0x0378, 0x0378), + regmap_reg_range(0x037c, 0x037d), + regmap_reg_range(0x0390, 0x0393), + regmap_reg_range(0x0400, 0x040e), + regmap_reg_range(0x0410, 0x042f), + regmap_reg_range(0x0500, 0x0519), + regmap_reg_range(0x0520, 0x054b), + regmap_reg_range(0x0550, 0x05b3), + + /* port 1 */ + regmap_reg_range(0x1000, 0x1001), + regmap_reg_range(0x1004, 0x100b), + regmap_reg_range(0x1013, 0x1013), + regmap_reg_range(0x1017, 0x1017), + regmap_reg_range(0x101b, 0x101b), + regmap_reg_range(0x101f, 0x1021), + regmap_reg_range(0x1030, 0x1030), + regmap_reg_range(0x1100, 0x1111), + regmap_reg_range(0x111a, 0x111d), + regmap_reg_range(0x1122, 0x1127), + regmap_reg_range(0x112a, 0x112b), + regmap_reg_range(0x1136, 0x1139), + regmap_reg_range(0x113e, 0x113f), + regmap_reg_range(0x1400, 0x1401), + regmap_reg_range(0x1403, 0x1403), + regmap_reg_range(0x1410, 0x1417), + regmap_reg_range(0x1420, 0x1423), + regmap_reg_range(0x1500, 0x1507), + regmap_reg_range(0x1600, 0x1612), + regmap_reg_range(0x1800, 0x180f), + regmap_reg_range(0x1900, 0x1907), + regmap_reg_range(0x1914, 0x191b), + regmap_reg_range(0x1a00, 0x1a03), + regmap_reg_range(0x1a04, 0x1a08), + regmap_reg_range(0x1b00, 0x1b01), + regmap_reg_range(0x1b04, 0x1b04), + regmap_reg_range(0x1c00, 0x1c05), + regmap_reg_range(0x1c08, 0x1c1b), + + /* port 2 */ + regmap_reg_range(0x2000, 0x2001), + regmap_reg_range(0x2004, 0x200b), + regmap_reg_range(0x2013, 0x2013), + regmap_reg_range(0x2017, 0x2017), + regmap_reg_range(0x201b, 0x201b), + regmap_reg_range(0x201f, 0x2021), + regmap_reg_range(0x2030, 0x2030), + regmap_reg_range(0x2100, 0x2111), + regmap_reg_range(0x211a, 0x211d), + regmap_reg_range(0x2122, 0x2127), + regmap_reg_range(0x212a, 0x212b), + regmap_reg_range(0x2136, 0x2139), + regmap_reg_range(0x213e, 0x213f), + regmap_reg_range(0x2400, 0x2401), + regmap_reg_range(0x2403, 0x2403), + regmap_reg_range(0x2410, 0x2417), + regmap_reg_range(0x2420, 0x2423), + regmap_reg_range(0x2500, 0x2507), + regmap_reg_range(0x2600, 0x2612), + regmap_reg_range(0x2800, 0x280f), + regmap_reg_range(0x2900, 0x2907), + regmap_reg_range(0x2914, 0x291b), + regmap_reg_range(0x2a00, 0x2a03), + regmap_reg_range(0x2a04, 0x2a08), + regmap_reg_range(0x2b00, 0x2b01), + regmap_reg_range(0x2b04, 0x2b04), + regmap_reg_range(0x2c00, 0x2c05), + regmap_reg_range(0x2c08, 0x2c1b), + + /* port 3 */ + regmap_reg_range(0x3000, 0x3001), + regmap_reg_range(0x3004, 0x300b), + regmap_reg_range(0x3013, 0x3013), + regmap_reg_range(0x3017, 0x3017), + regmap_reg_range(0x301b, 0x301b), + regmap_reg_range(0x301f, 0x3021), + regmap_reg_range(0x3030, 0x3030), + regmap_reg_range(0x3300, 0x3301), + regmap_reg_range(0x3303, 0x3303), + regmap_reg_range(0x3400, 0x3401), + regmap_reg_range(0x3403, 0x3403), + regmap_reg_range(0x3410, 0x3417), + regmap_reg_range(0x3420, 0x3423), + regmap_reg_range(0x3500, 0x3507), + regmap_reg_range(0x3600, 0x3612), + regmap_reg_range(0x3800, 0x380f), + regmap_reg_range(0x3900, 0x3907), + regmap_reg_range(0x3914, 0x391b), + regmap_reg_range(0x3a00, 0x3a03), + regmap_reg_range(0x3a04, 0x3a08), + regmap_reg_range(0x3b00, 0x3b01), + regmap_reg_range(0x3b04, 0x3b04), + regmap_reg_range(0x3c00, 0x3c05), + regmap_reg_range(0x3c08, 0x3c1b), +}; + +static const struct regmap_access_table ksz8563_register_set = { + .yes_ranges = ksz8563_valid_regs, + .n_yes_ranges = ARRAY_SIZE(ksz8563_valid_regs), +}; + +static const struct regmap_range ksz9477_valid_regs[] = { + regmap_reg_range(0x0000, 0x0003), + regmap_reg_range(0x0006, 0x0006), + regmap_reg_range(0x0010, 0x001f), + regmap_reg_range(0x0100, 0x0100), + regmap_reg_range(0x0103, 0x0107), + regmap_reg_range(0x010d, 0x010d), + regmap_reg_range(0x0110, 0x0113), + regmap_reg_range(0x0120, 0x012b), + regmap_reg_range(0x0201, 0x0201), + regmap_reg_range(0x0210, 0x0213), + regmap_reg_range(0x0300, 0x0300), + regmap_reg_range(0x0302, 0x031b), + regmap_reg_range(0x0320, 0x032b), + regmap_reg_range(0x0330, 0x0336), + regmap_reg_range(0x0338, 0x033b), + regmap_reg_range(0x033e, 0x033e), + regmap_reg_range(0x0340, 0x035f), + regmap_reg_range(0x0370, 0x0370), + regmap_reg_range(0x0378, 0x0378), + regmap_reg_range(0x037c, 0x037d), + regmap_reg_range(0x0390, 0x0393), + regmap_reg_range(0x0400, 0x040e), + regmap_reg_range(0x0410, 0x042f), + regmap_reg_range(0x0444, 0x044b), + regmap_reg_range(0x0450, 0x046f), + regmap_reg_range(0x0500, 0x0519), + regmap_reg_range(0x0520, 0x054b), + regmap_reg_range(0x0550, 0x05b3), + regmap_reg_range(0x0604, 0x060b), + regmap_reg_range(0x0610, 0x0612), + regmap_reg_range(0x0614, 0x062c), + regmap_reg_range(0x0640, 0x0645), + regmap_reg_range(0x0648, 0x064d), + + /* port 1 */ + regmap_reg_range(0x1000, 0x1001), + regmap_reg_range(0x1013, 0x1013), + regmap_reg_range(0x1017, 0x1017), + regmap_reg_range(0x101b, 0x101b), + regmap_reg_range(0x101f, 0x1020), + regmap_reg_range(0x1030, 0x1030), + regmap_reg_range(0x1100, 0x1115), + regmap_reg_range(0x111a, 0x111f), + regmap_reg_range(0x1122, 0x1127), + regmap_reg_range(0x112a, 0x112b), + regmap_reg_range(0x1136, 0x1139), + regmap_reg_range(0x113e, 0x113f), + regmap_reg_range(0x1400, 0x1401), + regmap_reg_range(0x1403, 0x1403), + regmap_reg_range(0x1410, 0x1417), + regmap_reg_range(0x1420, 0x1423), + regmap_reg_range(0x1500, 0x1507), + regmap_reg_range(0x1600, 0x1613), + regmap_reg_range(0x1800, 0x180f), + regmap_reg_range(0x1820, 0x1827), + regmap_reg_range(0x1830, 0x1837), + regmap_reg_range(0x1840, 0x184b), + regmap_reg_range(0x1900, 0x1907), + regmap_reg_range(0x1914, 0x191b), + regmap_reg_range(0x1920, 0x1920), + regmap_reg_range(0x1923, 0x1927), + regmap_reg_range(0x1a00, 0x1a03), + regmap_reg_range(0x1a04, 0x1a07), + regmap_reg_range(0x1b00, 0x1b01), + regmap_reg_range(0x1b04, 0x1b04), + regmap_reg_range(0x1c00, 0x1c05), + regmap_reg_range(0x1c08, 0x1c1b), + + /* port 2 */ + regmap_reg_range(0x2000, 0x2001), + regmap_reg_range(0x2013, 0x2013), + regmap_reg_range(0x2017, 0x2017), + regmap_reg_range(0x201b, 0x201b), + regmap_reg_range(0x201f, 0x2020), + regmap_reg_range(0x2030, 0x2030), + regmap_reg_range(0x2100, 0x2115), + regmap_reg_range(0x211a, 0x211f), + regmap_reg_range(0x2122, 0x2127), + regmap_reg_range(0x212a, 0x212b), + regmap_reg_range(0x2136, 0x2139), + regmap_reg_range(0x213e, 0x213f), + regmap_reg_range(0x2400, 0x2401), + regmap_reg_range(0x2403, 0x2403), + regmap_reg_range(0x2410, 0x2417), + regmap_reg_range(0x2420, 0x2423), + regmap_reg_range(0x2500, 0x2507), + regmap_reg_range(0x2600, 0x2613), + regmap_reg_range(0x2800, 0x280f), + regmap_reg_range(0x2820, 0x2827), + regmap_reg_range(0x2830, 0x2837), + regmap_reg_range(0x2840, 0x284b), + regmap_reg_range(0x2900, 0x2907), + regmap_reg_range(0x2914, 0x291b), + regmap_reg_range(0x2920, 0x2920), + regmap_reg_range(0x2923, 0x2927), + regmap_reg_range(0x2a00, 0x2a03), + regmap_reg_range(0x2a04, 0x2a07), + regmap_reg_range(0x2b00, 0x2b01), + regmap_reg_range(0x2b04, 0x2b04), + regmap_reg_range(0x2c00, 0x2c05), + regmap_reg_range(0x2c08, 0x2c1b), + + /* port 3 */ + regmap_reg_range(0x3000, 0x3001), + regmap_reg_range(0x3013, 0x3013), + regmap_reg_range(0x3017, 0x3017), + regmap_reg_range(0x301b, 0x301b), + regmap_reg_range(0x301f, 0x3020), + regmap_reg_range(0x3030, 0x3030), + regmap_reg_range(0x3100, 0x3115), + regmap_reg_range(0x311a, 0x311f), + regmap_reg_range(0x3122, 0x3127), + regmap_reg_range(0x312a, 0x312b), + regmap_reg_range(0x3136, 0x3139), + regmap_reg_range(0x313e, 0x313f), + regmap_reg_range(0x3400, 0x3401), + regmap_reg_range(0x3403, 0x3403), + regmap_reg_range(0x3410, 0x3417), + regmap_reg_range(0x3420, 0x3423), + regmap_reg_range(0x3500, 0x3507), + regmap_reg_range(0x3600, 0x3613), + regmap_reg_range(0x3800, 0x380f), + regmap_reg_range(0x3820, 0x3827), + regmap_reg_range(0x3830, 0x3837), + regmap_reg_range(0x3840, 0x384b), + regmap_reg_range(0x3900, 0x3907), + regmap_reg_range(0x3914, 0x391b), + regmap_reg_range(0x3920, 0x3920), + regmap_reg_range(0x3923, 0x3927), + regmap_reg_range(0x3a00, 0x3a03), + regmap_reg_range(0x3a04, 0x3a07), + regmap_reg_range(0x3b00, 0x3b01), + regmap_reg_range(0x3b04, 0x3b04), + regmap_reg_range(0x3c00, 0x3c05), + regmap_reg_range(0x3c08, 0x3c1b), + + /* port 4 */ + regmap_reg_range(0x4000, 0x4001), + regmap_reg_range(0x4013, 0x4013), + regmap_reg_range(0x4017, 0x4017), + regmap_reg_range(0x401b, 0x401b), + regmap_reg_range(0x401f, 0x4020), + regmap_reg_range(0x4030, 0x4030), + regmap_reg_range(0x4100, 0x4115), + regmap_reg_range(0x411a, 0x411f), + regmap_reg_range(0x4122, 0x4127), + regmap_reg_range(0x412a, 0x412b), + regmap_reg_range(0x4136, 0x4139), + regmap_reg_range(0x413e, 0x413f), + regmap_reg_range(0x4400, 0x4401), + regmap_reg_range(0x4403, 0x4403), + regmap_reg_range(0x4410, 0x4417), + regmap_reg_range(0x4420, 0x4423), + regmap_reg_range(0x4500, 0x4507), + regmap_reg_range(0x4600, 0x4613), + regmap_reg_range(0x4800, 0x480f), + regmap_reg_range(0x4820, 0x4827), + regmap_reg_range(0x4830, 0x4837), + regmap_reg_range(0x4840, 0x484b), + regmap_reg_range(0x4900, 0x4907), + regmap_reg_range(0x4914, 0x491b), + regmap_reg_range(0x4920, 0x4920), + regmap_reg_range(0x4923, 0x4927), + regmap_reg_range(0x4a00, 0x4a03), + regmap_reg_range(0x4a04, 0x4a07), + regmap_reg_range(0x4b00, 0x4b01), + regmap_reg_range(0x4b04, 0x4b04), + regmap_reg_range(0x4c00, 0x4c05), + regmap_reg_range(0x4c08, 0x4c1b), + + /* port 5 */ + regmap_reg_range(0x5000, 0x5001), + regmap_reg_range(0x5013, 0x5013), + regmap_reg_range(0x5017, 0x5017), + regmap_reg_range(0x501b, 0x501b), + regmap_reg_range(0x501f, 0x5020), + regmap_reg_range(0x5030, 0x5030), + regmap_reg_range(0x5100, 0x5115), + regmap_reg_range(0x511a, 0x511f), + regmap_reg_range(0x5122, 0x5127), + regmap_reg_range(0x512a, 0x512b), + regmap_reg_range(0x5136, 0x5139), + regmap_reg_range(0x513e, 0x513f), + regmap_reg_range(0x5400, 0x5401), + regmap_reg_range(0x5403, 0x5403), + regmap_reg_range(0x5410, 0x5417), + regmap_reg_range(0x5420, 0x5423), + regmap_reg_range(0x5500, 0x5507), + regmap_reg_range(0x5600, 0x5613), + regmap_reg_range(0x5800, 0x580f), + regmap_reg_range(0x5820, 0x5827), + regmap_reg_range(0x5830, 0x5837), + regmap_reg_range(0x5840, 0x584b), + regmap_reg_range(0x5900, 0x5907), + regmap_reg_range(0x5914, 0x591b), + regmap_reg_range(0x5920, 0x5920), + regmap_reg_range(0x5923, 0x5927), + regmap_reg_range(0x5a00, 0x5a03), + regmap_reg_range(0x5a04, 0x5a07), + regmap_reg_range(0x5b00, 0x5b01), + regmap_reg_range(0x5b04, 0x5b04), + regmap_reg_range(0x5c00, 0x5c05), + regmap_reg_range(0x5c08, 0x5c1b), + + /* port 6 */ + regmap_reg_range(0x6000, 0x6001), + regmap_reg_range(0x6013, 0x6013), + regmap_reg_range(0x6017, 0x6017), + regmap_reg_range(0x601b, 0x601b), + regmap_reg_range(0x601f, 0x6020), + regmap_reg_range(0x6030, 0x6030), + regmap_reg_range(0x6300, 0x6301), + regmap_reg_range(0x6400, 0x6401), + regmap_reg_range(0x6403, 0x6403), + regmap_reg_range(0x6410, 0x6417), + regmap_reg_range(0x6420, 0x6423), + regmap_reg_range(0x6500, 0x6507), + regmap_reg_range(0x6600, 0x6613), + regmap_reg_range(0x6800, 0x680f), + regmap_reg_range(0x6820, 0x6827), + regmap_reg_range(0x6830, 0x6837), + regmap_reg_range(0x6840, 0x684b), + regmap_reg_range(0x6900, 0x6907), + regmap_reg_range(0x6914, 0x691b), + regmap_reg_range(0x6920, 0x6920), + regmap_reg_range(0x6923, 0x6927), + regmap_reg_range(0x6a00, 0x6a03), + regmap_reg_range(0x6a04, 0x6a07), + regmap_reg_range(0x6b00, 0x6b01), + regmap_reg_range(0x6b04, 0x6b04), + regmap_reg_range(0x6c00, 0x6c05), + regmap_reg_range(0x6c08, 0x6c1b), + + /* port 7 */ + regmap_reg_range(0x7000, 0x7001), + regmap_reg_range(0x7013, 0x7013), + regmap_reg_range(0x7017, 0x7017), + regmap_reg_range(0x701b, 0x701b), + regmap_reg_range(0x701f, 0x7020), + regmap_reg_range(0x7030, 0x7030), + regmap_reg_range(0x7200, 0x7203), + regmap_reg_range(0x7206, 0x7207), + regmap_reg_range(0x7300, 0x7301), + regmap_reg_range(0x7400, 0x7401), + regmap_reg_range(0x7403, 0x7403), + regmap_reg_range(0x7410, 0x7417), + regmap_reg_range(0x7420, 0x7423), + regmap_reg_range(0x7500, 0x7507), + regmap_reg_range(0x7600, 0x7613), + regmap_reg_range(0x7800, 0x780f), + regmap_reg_range(0x7820, 0x7827), + regmap_reg_range(0x7830, 0x7837), + regmap_reg_range(0x7840, 0x784b), + regmap_reg_range(0x7900, 0x7907), + regmap_reg_range(0x7914, 0x791b), + regmap_reg_range(0x7920, 0x7920), + regmap_reg_range(0x7923, 0x7927), + regmap_reg_range(0x7a00, 0x7a03), + regmap_reg_range(0x7a04, 0x7a07), + regmap_reg_range(0x7b00, 0x7b01), + regmap_reg_range(0x7b04, 0x7b04), + regmap_reg_range(0x7c00, 0x7c05), + regmap_reg_range(0x7c08, 0x7c1b), +}; + +static const struct regmap_access_table ksz9477_register_set = { + .yes_ranges = ksz9477_valid_regs, + .n_yes_ranges = ARRAY_SIZE(ksz9477_valid_regs), +}; + +static const struct regmap_range ksz9896_valid_regs[] = { + regmap_reg_range(0x0000, 0x0003), + regmap_reg_range(0x0006, 0x0006), + regmap_reg_range(0x0010, 0x001f), + regmap_reg_range(0x0100, 0x0100), + regmap_reg_range(0x0103, 0x0107), + regmap_reg_range(0x010d, 0x010d), + regmap_reg_range(0x0110, 0x0113), + regmap_reg_range(0x0120, 0x0127), + regmap_reg_range(0x0201, 0x0201), + regmap_reg_range(0x0210, 0x0213), + regmap_reg_range(0x0300, 0x0300), + regmap_reg_range(0x0302, 0x030b), + regmap_reg_range(0x0310, 0x031b), + regmap_reg_range(0x0320, 0x032b), + regmap_reg_range(0x0330, 0x0336), + regmap_reg_range(0x0338, 0x033b), + regmap_reg_range(0x033e, 0x033e), + regmap_reg_range(0x0340, 0x035f), + regmap_reg_range(0x0370, 0x0370), + regmap_reg_range(0x0378, 0x0378), + regmap_reg_range(0x037c, 0x037d), + regmap_reg_range(0x0390, 0x0393), + regmap_reg_range(0x0400, 0x040e), + regmap_reg_range(0x0410, 0x042f), + + /* port 1 */ + regmap_reg_range(0x1000, 0x1001), + regmap_reg_range(0x1013, 0x1013), + regmap_reg_range(0x1017, 0x1017), + regmap_reg_range(0x101b, 0x101b), + regmap_reg_range(0x101f, 0x1020), + regmap_reg_range(0x1030, 0x1030), + regmap_reg_range(0x1100, 0x1115), + regmap_reg_range(0x111a, 0x111f), + regmap_reg_range(0x1122, 0x1127), + regmap_reg_range(0x112a, 0x112b), + regmap_reg_range(0x1136, 0x1139), + regmap_reg_range(0x113e, 0x113f), + regmap_reg_range(0x1400, 0x1401), + regmap_reg_range(0x1403, 0x1403), + regmap_reg_range(0x1410, 0x1417), + regmap_reg_range(0x1420, 0x1423), + regmap_reg_range(0x1500, 0x1507), + regmap_reg_range(0x1600, 0x1612), + regmap_reg_range(0x1800, 0x180f), + regmap_reg_range(0x1820, 0x1827), + regmap_reg_range(0x1830, 0x1837), + regmap_reg_range(0x1840, 0x184b), + regmap_reg_range(0x1900, 0x1907), + regmap_reg_range(0x1914, 0x1915), + regmap_reg_range(0x1a00, 0x1a03), + regmap_reg_range(0x1a04, 0x1a07), + regmap_reg_range(0x1b00, 0x1b01), + regmap_reg_range(0x1b04, 0x1b04), + + /* port 2 */ + regmap_reg_range(0x2000, 0x2001), + regmap_reg_range(0x2013, 0x2013), + regmap_reg_range(0x2017, 0x2017), + regmap_reg_range(0x201b, 0x201b), + regmap_reg_range(0x201f, 0x2020), + regmap_reg_range(0x2030, 0x2030), + regmap_reg_range(0x2100, 0x2115), + regmap_reg_range(0x211a, 0x211f), + regmap_reg_range(0x2122, 0x2127), + regmap_reg_range(0x212a, 0x212b), + regmap_reg_range(0x2136, 0x2139), + regmap_reg_range(0x213e, 0x213f), + regmap_reg_range(0x2400, 0x2401), + regmap_reg_range(0x2403, 0x2403), + regmap_reg_range(0x2410, 0x2417), + regmap_reg_range(0x2420, 0x2423), + regmap_reg_range(0x2500, 0x2507), + regmap_reg_range(0x2600, 0x2612), + regmap_reg_range(0x2800, 0x280f), + regmap_reg_range(0x2820, 0x2827), + regmap_reg_range(0x2830, 0x2837), + regmap_reg_range(0x2840, 0x284b), + regmap_reg_range(0x2900, 0x2907), + regmap_reg_range(0x2914, 0x2915), + regmap_reg_range(0x2a00, 0x2a03), + regmap_reg_range(0x2a04, 0x2a07), + regmap_reg_range(0x2b00, 0x2b01), + regmap_reg_range(0x2b04, 0x2b04), + + /* port 3 */ + regmap_reg_range(0x3000, 0x3001), + regmap_reg_range(0x3013, 0x3013), + regmap_reg_range(0x3017, 0x3017), + regmap_reg_range(0x301b, 0x301b), + regmap_reg_range(0x301f, 0x3020), + regmap_reg_range(0x3030, 0x3030), + regmap_reg_range(0x3100, 0x3115), + regmap_reg_range(0x311a, 0x311f), + regmap_reg_range(0x3122, 0x3127), + regmap_reg_range(0x312a, 0x312b), + regmap_reg_range(0x3136, 0x3139), + regmap_reg_range(0x313e, 0x313f), + regmap_reg_range(0x3400, 0x3401), + regmap_reg_range(0x3403, 0x3403), + regmap_reg_range(0x3410, 0x3417), + regmap_reg_range(0x3420, 0x3423), + regmap_reg_range(0x3500, 0x3507), + regmap_reg_range(0x3600, 0x3612), + regmap_reg_range(0x3800, 0x380f), + regmap_reg_range(0x3820, 0x3827), + regmap_reg_range(0x3830, 0x3837), + regmap_reg_range(0x3840, 0x384b), + regmap_reg_range(0x3900, 0x3907), + regmap_reg_range(0x3914, 0x3915), + regmap_reg_range(0x3a00, 0x3a03), + regmap_reg_range(0x3a04, 0x3a07), + regmap_reg_range(0x3b00, 0x3b01), + regmap_reg_range(0x3b04, 0x3b04), + + /* port 4 */ + regmap_reg_range(0x4000, 0x4001), + regmap_reg_range(0x4013, 0x4013), + regmap_reg_range(0x4017, 0x4017), + regmap_reg_range(0x401b, 0x401b), + regmap_reg_range(0x401f, 0x4020), + regmap_reg_range(0x4030, 0x4030), + regmap_reg_range(0x4100, 0x4115), + regmap_reg_range(0x411a, 0x411f), + regmap_reg_range(0x4122, 0x4127), + regmap_reg_range(0x412a, 0x412b), + regmap_reg_range(0x4136, 0x4139), + regmap_reg_range(0x413e, 0x413f), + regmap_reg_range(0x4400, 0x4401), + regmap_reg_range(0x4403, 0x4403), + regmap_reg_range(0x4410, 0x4417), + regmap_reg_range(0x4420, 0x4423), + regmap_reg_range(0x4500, 0x4507), + regmap_reg_range(0x4600, 0x4612), + regmap_reg_range(0x4800, 0x480f), + regmap_reg_range(0x4820, 0x4827), + regmap_reg_range(0x4830, 0x4837), + regmap_reg_range(0x4840, 0x484b), + regmap_reg_range(0x4900, 0x4907), + regmap_reg_range(0x4914, 0x4915), + regmap_reg_range(0x4a00, 0x4a03), + regmap_reg_range(0x4a04, 0x4a07), + regmap_reg_range(0x4b00, 0x4b01), + regmap_reg_range(0x4b04, 0x4b04), + + /* port 5 */ + regmap_reg_range(0x5000, 0x5001), + regmap_reg_range(0x5013, 0x5013), + regmap_reg_range(0x5017, 0x5017), + regmap_reg_range(0x501b, 0x501b), + regmap_reg_range(0x501f, 0x5020), + regmap_reg_range(0x5030, 0x5030), + regmap_reg_range(0x5100, 0x5115), + regmap_reg_range(0x511a, 0x511f), + regmap_reg_range(0x5122, 0x5127), + regmap_reg_range(0x512a, 0x512b), + regmap_reg_range(0x5136, 0x5139), + regmap_reg_range(0x513e, 0x513f), + regmap_reg_range(0x5400, 0x5401), + regmap_reg_range(0x5403, 0x5403), + regmap_reg_range(0x5410, 0x5417), + regmap_reg_range(0x5420, 0x5423), + regmap_reg_range(0x5500, 0x5507), + regmap_reg_range(0x5600, 0x5612), + regmap_reg_range(0x5800, 0x580f), + regmap_reg_range(0x5820, 0x5827), + regmap_reg_range(0x5830, 0x5837), + regmap_reg_range(0x5840, 0x584b), + regmap_reg_range(0x5900, 0x5907), + regmap_reg_range(0x5914, 0x5915), + regmap_reg_range(0x5a00, 0x5a03), + regmap_reg_range(0x5a04, 0x5a07), + regmap_reg_range(0x5b00, 0x5b01), + regmap_reg_range(0x5b04, 0x5b04), + + /* port 6 */ + regmap_reg_range(0x6000, 0x6001), + regmap_reg_range(0x6013, 0x6013), + regmap_reg_range(0x6017, 0x6017), + regmap_reg_range(0x601b, 0x601b), + regmap_reg_range(0x601f, 0x6020), + regmap_reg_range(0x6030, 0x6030), + regmap_reg_range(0x6100, 0x6115), + regmap_reg_range(0x611a, 0x611f), + regmap_reg_range(0x6122, 0x6127), + regmap_reg_range(0x612a, 0x612b), + regmap_reg_range(0x6136, 0x6139), + regmap_reg_range(0x613e, 0x613f), + regmap_reg_range(0x6300, 0x6301), + regmap_reg_range(0x6400, 0x6401), + regmap_reg_range(0x6403, 0x6403), + regmap_reg_range(0x6410, 0x6417), + regmap_reg_range(0x6420, 0x6423), + regmap_reg_range(0x6500, 0x6507), + regmap_reg_range(0x6600, 0x6612), + regmap_reg_range(0x6800, 0x680f), + regmap_reg_range(0x6820, 0x6827), + regmap_reg_range(0x6830, 0x6837), + regmap_reg_range(0x6840, 0x684b), + regmap_reg_range(0x6900, 0x6907), + regmap_reg_range(0x6914, 0x6915), + regmap_reg_range(0x6a00, 0x6a03), + regmap_reg_range(0x6a04, 0x6a07), + regmap_reg_range(0x6b00, 0x6b01), + regmap_reg_range(0x6b04, 0x6b04), +}; + +static const struct regmap_access_table ksz9896_register_set = { + .yes_ranges = ksz9896_valid_regs, + .n_yes_ranges = ARRAY_SIZE(ksz9896_valid_regs), +}; + const struct ksz_chip_data ksz_switch_chips[] = { + [KSZ8563] = { + .chip_id = KSZ8563_CHIP_ID, + .dev_name = "KSZ8563", + .num_vlans = 4096, + .num_alus = 4096, + .num_statics = 16, + .cpu_ports = 0x07, /* can be configured as cpu port */ + .port_cnt = 3, /* total port count */ + .ops = &ksz9477_dev_ops, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .regs = ksz9477_regs, + .masks = ksz9477_masks, + .shifts = ksz9477_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz8795_xmii_ctrl1, /* Same as ksz8795 */ + .supports_mii = {false, false, true}, + .supports_rmii = {false, false, true}, + .supports_rgmii = {false, false, true}, + .internal_phy = {true, true, false}, + .gbit_capable = {false, false, true}, + .wr_table = &ksz8563_register_set, + .rd_table = &ksz8563_register_set, + }, + [KSZ8795] = { .chip_id = KSZ8795_CHIP_ID, .dev_name = "KSZ8795", @@ -527,6 +1171,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_statics = 16, .cpu_ports = 0x7F, /* can be configured as cpu port */ .port_cnt = 7, /* total physical port count */ + .port_nirqs = 4, .ops = &ksz9477_dev_ops, .phy_errata_9477 = true, .mib_names = ksz9477_mib_names, @@ -545,6 +1190,41 @@ const struct ksz_chip_data ksz_switch_chips[] = { false, true, false}, .internal_phy = {true, true, true, true, true, false, false}, + .gbit_capable = {true, true, true, true, true, true, true}, + .wr_table = &ksz9477_register_set, + .rd_table = &ksz9477_register_set, + }, + + [KSZ9896] = { + .chip_id = KSZ9896_CHIP_ID, + .dev_name = "KSZ9896", + .num_vlans = 4096, + .num_alus = 4096, + .num_statics = 16, + .cpu_ports = 0x3F, /* can be configured as cpu port */ + .port_cnt = 6, /* total physical port count */ + .port_nirqs = 2, + .ops = &ksz9477_dev_ops, + .phy_errata_9477 = true, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .regs = ksz9477_regs, + .masks = ksz9477_masks, + .shifts = ksz9477_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, + .supports_mii = {false, false, false, false, + false, true}, + .supports_rmii = {false, false, false, false, + false, true}, + .supports_rgmii = {false, false, false, false, + false, true}, + .internal_phy = {true, true, true, true, + true, false}, + .gbit_capable = {true, true, true, true, true, true}, + .wr_table = &ksz9896_register_set, + .rd_table = &ksz9896_register_set, }, [KSZ9897] = { @@ -555,6 +1235,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_statics = 16, .cpu_ports = 0x7F, /* can be configured as cpu port */ .port_cnt = 7, /* total physical port count */ + .port_nirqs = 2, .ops = &ksz9477_dev_ops, .phy_errata_9477 = true, .mib_names = ksz9477_mib_names, @@ -573,6 +1254,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { false, true, true}, .internal_phy = {true, true, true, true, true, false, false}, + .gbit_capable = {true, true, true, true, true, true, true}, }, [KSZ9893] = { @@ -583,6 +1265,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_statics = 16, .cpu_ports = 0x07, /* can be configured as cpu port */ .port_cnt = 3, /* total port count */ + .port_nirqs = 2, .ops = &ksz9477_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -596,6 +1279,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .supports_rmii = {false, false, true}, .supports_rgmii = {false, false, true}, .internal_phy = {true, true, false}, + .gbit_capable = {true, true, true}, }, [KSZ9567] = { @@ -606,6 +1290,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_statics = 16, .cpu_ports = 0x7F, /* can be configured as cpu port */ .port_cnt = 7, /* total physical port count */ + .port_nirqs = 3, .ops = &ksz9477_dev_ops, .phy_errata_9477 = true, .mib_names = ksz9477_mib_names, @@ -624,6 +1309,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { false, true, true}, .internal_phy = {true, true, true, true, true, false, false}, + .gbit_capable = {true, true, true, true, true, true, true}, }, [LAN9370] = { @@ -634,6 +1320,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_statics = 256, .cpu_ports = 0x10, /* can be configured as cpu port */ .port_cnt = 5, /* total physical port count */ + .port_nirqs = 6, .ops = &lan937x_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -657,6 +1344,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_statics = 256, .cpu_ports = 0x30, /* can be configured as cpu port */ .port_cnt = 6, /* total physical port count */ + .port_nirqs = 6, .ops = &lan937x_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -680,6 +1368,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_statics = 256, .cpu_ports = 0x30, /* can be configured as cpu port */ .port_cnt = 8, /* total physical port count */ + .port_nirqs = 6, .ops = &lan937x_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -707,6 +1396,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_statics = 256, .cpu_ports = 0x38, /* can be configured as cpu port */ .port_cnt = 5, /* total physical port count */ + .port_nirqs = 6, .ops = &lan937x_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -734,6 +1424,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_statics = 256, .cpu_ports = 0x30, /* can be configured as cpu port */ .port_cnt = 8, /* total physical port count */ + .port_nirqs = 6, .ops = &lan937x_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -803,9 +1494,15 @@ static void ksz_phylink_get_caps(struct dsa_switch *ds, int port, if (dev->info->supports_rgmii[port]) phy_interface_set_rgmii(config->supported_interfaces); - if (dev->info->internal_phy[port]) + if (dev->info->internal_phy[port]) { __set_bit(PHY_INTERFACE_MODE_INTERNAL, config->supported_interfaces); + /* Compatibility for phylib's default interface type when the + * phy-mode property is absent + */ + __set_bit(PHY_INTERFACE_MODE_GMII, + config->supported_interfaces); + } if (dev->dev_ops->get_caps) dev->dev_ops->get_caps(dev, port, config); @@ -959,9 +1656,281 @@ static void ksz_update_port_member(struct ksz_device *dev, int port) dev->dev_ops->cfg_port_member(dev, port, port_member | cpu_port); } +static int ksz_sw_mdio_read(struct mii_bus *bus, int addr, int regnum) +{ + struct ksz_device *dev = bus->priv; + u16 val; + int ret; + + if (regnum & MII_ADDR_C45) + return -EOPNOTSUPP; + + ret = dev->dev_ops->r_phy(dev, addr, regnum, &val); + if (ret < 0) + return ret; + + return val; +} + +static int ksz_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, + u16 val) +{ + struct ksz_device *dev = bus->priv; + + if (regnum & MII_ADDR_C45) + return -EOPNOTSUPP; + + return dev->dev_ops->w_phy(dev, addr, regnum, val); +} + +static int ksz_irq_phy_setup(struct ksz_device *dev) +{ + struct dsa_switch *ds = dev->ds; + int phy; + int irq; + int ret; + + for (phy = 0; phy < KSZ_MAX_NUM_PORTS; phy++) { + if (BIT(phy) & ds->phys_mii_mask) { + irq = irq_find_mapping(dev->ports[phy].pirq.domain, + PORT_SRC_PHY_INT); + if (irq < 0) { + ret = irq; + goto out; + } + ds->slave_mii_bus->irq[phy] = irq; + } + } + return 0; +out: + while (phy--) + if (BIT(phy) & ds->phys_mii_mask) + irq_dispose_mapping(ds->slave_mii_bus->irq[phy]); + + return ret; +} + +static void ksz_irq_phy_free(struct ksz_device *dev) +{ + struct dsa_switch *ds = dev->ds; + int phy; + + for (phy = 0; phy < KSZ_MAX_NUM_PORTS; phy++) + if (BIT(phy) & ds->phys_mii_mask) + irq_dispose_mapping(ds->slave_mii_bus->irq[phy]); +} + +static int ksz_mdio_register(struct ksz_device *dev) +{ + struct dsa_switch *ds = dev->ds; + struct device_node *mdio_np; + struct mii_bus *bus; + int ret; + + mdio_np = of_get_child_by_name(dev->dev->of_node, "mdio"); + if (!mdio_np) + return 0; + + bus = devm_mdiobus_alloc(ds->dev); + if (!bus) { + of_node_put(mdio_np); + return -ENOMEM; + } + + bus->priv = dev; + bus->read = ksz_sw_mdio_read; + bus->write = ksz_sw_mdio_write; + bus->name = "ksz slave smi"; + snprintf(bus->id, MII_BUS_ID_SIZE, "SMI-%d", ds->index); + bus->parent = ds->dev; + bus->phy_mask = ~ds->phys_mii_mask; + + ds->slave_mii_bus = bus; + + if (dev->irq > 0) { + ret = ksz_irq_phy_setup(dev); + if (ret) { + of_node_put(mdio_np); + return ret; + } + } + + ret = devm_of_mdiobus_register(ds->dev, bus, mdio_np); + if (ret) { + dev_err(ds->dev, "unable to register MDIO bus %s\n", + bus->id); + if (dev->irq > 0) + ksz_irq_phy_free(dev); + } + + of_node_put(mdio_np); + + return ret; +} + +static void ksz_irq_mask(struct irq_data *d) +{ + struct ksz_irq *kirq = irq_data_get_irq_chip_data(d); + + kirq->masked |= BIT(d->hwirq); +} + +static void ksz_irq_unmask(struct irq_data *d) +{ + struct ksz_irq *kirq = irq_data_get_irq_chip_data(d); + + kirq->masked &= ~BIT(d->hwirq); +} + +static void ksz_irq_bus_lock(struct irq_data *d) +{ + struct ksz_irq *kirq = irq_data_get_irq_chip_data(d); + + mutex_lock(&kirq->dev->lock_irq); +} + +static void ksz_irq_bus_sync_unlock(struct irq_data *d) +{ + struct ksz_irq *kirq = irq_data_get_irq_chip_data(d); + struct ksz_device *dev = kirq->dev; + int ret; + + ret = ksz_write32(dev, kirq->reg_mask, kirq->masked); + if (ret) + dev_err(dev->dev, "failed to change IRQ mask\n"); + + mutex_unlock(&dev->lock_irq); +} + +static const struct irq_chip ksz_irq_chip = { + .name = "ksz-irq", + .irq_mask = ksz_irq_mask, + .irq_unmask = ksz_irq_unmask, + .irq_bus_lock = ksz_irq_bus_lock, + .irq_bus_sync_unlock = ksz_irq_bus_sync_unlock, +}; + +static int ksz_irq_domain_map(struct irq_domain *d, + unsigned int irq, irq_hw_number_t hwirq) +{ + irq_set_chip_data(irq, d->host_data); + irq_set_chip_and_handler(irq, &ksz_irq_chip, handle_level_irq); + irq_set_noprobe(irq); + + return 0; +} + +static const struct irq_domain_ops ksz_irq_domain_ops = { + .map = ksz_irq_domain_map, + .xlate = irq_domain_xlate_twocell, +}; + +static void ksz_irq_free(struct ksz_irq *kirq) +{ + int irq, virq; + + free_irq(kirq->irq_num, kirq); + + for (irq = 0; irq < kirq->nirqs; irq++) { + virq = irq_find_mapping(kirq->domain, irq); + irq_dispose_mapping(virq); + } + + irq_domain_remove(kirq->domain); +} + +static irqreturn_t ksz_irq_thread_fn(int irq, void *dev_id) +{ + struct ksz_irq *kirq = dev_id; + unsigned int nhandled = 0; + struct ksz_device *dev; + unsigned int sub_irq; + u8 data; + int ret; + u8 n; + + dev = kirq->dev; + + /* Read interrupt status register */ + ret = ksz_read8(dev, kirq->reg_status, &data); + if (ret) + goto out; + + for (n = 0; n < kirq->nirqs; ++n) { + if (data & BIT(n)) { + sub_irq = irq_find_mapping(kirq->domain, n); + handle_nested_irq(sub_irq); + ++nhandled; + } + } +out: + return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); +} + +static int ksz_irq_common_setup(struct ksz_device *dev, struct ksz_irq *kirq) +{ + int ret, n; + + kirq->dev = dev; + kirq->masked = ~0; + + kirq->domain = irq_domain_add_simple(dev->dev->of_node, kirq->nirqs, 0, + &ksz_irq_domain_ops, kirq); + if (!kirq->domain) + return -ENOMEM; + + for (n = 0; n < kirq->nirqs; n++) + irq_create_mapping(kirq->domain, n); + + ret = request_threaded_irq(kirq->irq_num, NULL, ksz_irq_thread_fn, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING, + kirq->name, kirq); + if (ret) + goto out; + + return 0; + +out: + ksz_irq_free(kirq); + + return ret; +} + +static int ksz_girq_setup(struct ksz_device *dev) +{ + struct ksz_irq *girq = &dev->girq; + + girq->nirqs = dev->info->port_cnt; + girq->reg_mask = REG_SW_PORT_INT_MASK__1; + girq->reg_status = REG_SW_PORT_INT_STATUS__1; + snprintf(girq->name, sizeof(girq->name), "global_port_irq"); + + girq->irq_num = dev->irq; + + return ksz_irq_common_setup(dev, girq); +} + +static int ksz_pirq_setup(struct ksz_device *dev, u8 p) +{ + struct ksz_irq *pirq = &dev->ports[p].pirq; + + pirq->nirqs = dev->info->port_nirqs; + pirq->reg_mask = dev->dev_ops->get_port_addr(p, REG_PORT_INT_MASK); + pirq->reg_status = dev->dev_ops->get_port_addr(p, REG_PORT_INT_STATUS); + snprintf(pirq->name, sizeof(pirq->name), "port_irq-%d", p); + + pirq->irq_num = irq_find_mapping(dev->girq.domain, p); + if (pirq->irq_num < 0) + return pirq->irq_num; + + return ksz_irq_common_setup(dev, pirq); +} + static int ksz_setup(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; + struct dsa_port *dp; + struct ksz_port *p; const u16 *regs; int ret; @@ -1001,11 +1970,63 @@ static int ksz_setup(struct dsa_switch *ds) return ret; } + /* Start with learning disabled on standalone user ports, and enabled + * on the CPU port. In lack of other finer mechanisms, learning on the + * CPU port will avoid flooding bridge local addresses on the network + * in some cases. + */ + p = &dev->ports[dev->cpu_port]; + p->learning = true; + + if (dev->irq > 0) { + ret = ksz_girq_setup(dev); + if (ret) + return ret; + + dsa_switch_for_each_user_port(dp, dev->ds) { + ret = ksz_pirq_setup(dev, dp->index); + if (ret) + goto out_girq; + } + } + + ret = ksz_mdio_register(dev); + if (ret < 0) { + dev_err(dev->dev, "failed to register the mdio"); + goto out_pirq; + } + /* start switch */ regmap_update_bits(dev->regmap[0], regs[S_START_CTRL], SW_START, SW_START); return 0; + +out_pirq: + if (dev->irq > 0) + dsa_switch_for_each_user_port(dp, dev->ds) + ksz_irq_free(&dev->ports[dp->index].pirq); +out_girq: + if (dev->irq > 0) + ksz_irq_free(&dev->girq); + + return ret; +} + +static void ksz_teardown(struct dsa_switch *ds) +{ + struct ksz_device *dev = ds->priv; + struct dsa_port *dp; + + if (dev->irq > 0) { + dsa_switch_for_each_user_port(dp, dev->ds) + ksz_irq_free(&dev->ports[dp->index].pirq); + + ksz_irq_free(&dev->girq); + } + + if (dev->dev_ops->teardown) + dev->dev_ops->teardown(ds); } static void port_r_cnt(struct ksz_device *dev, int port) @@ -1089,8 +2110,11 @@ static int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg) { struct ksz_device *dev = ds->priv; u16 val = 0xffff; + int ret; - dev->dev_ops->r_phy(dev, addr, reg, &val); + ret = dev->dev_ops->r_phy(dev, addr, reg, &val); + if (ret) + return ret; return val; } @@ -1098,8 +2122,11 @@ static int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg) static int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val) { struct ksz_device *dev = ds->priv; + int ret; - dev->dev_ops->w_phy(dev, addr, reg, val); + ret = dev->dev_ops->w_phy(dev, addr, reg, val); + if (ret) + return ret; return 0; } @@ -1188,6 +2215,16 @@ static void ksz_port_fast_age(struct dsa_switch *ds, int port) dev->dev_ops->flush_dyn_mac_table(dev, port); } +static int ksz_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) +{ + struct ksz_device *dev = ds->priv; + + if (!dev->dev_ops->set_ageing_time) + return -EOPNOTSUPP; + + return dev->dev_ops->set_ageing_time(dev, msecs); +} + static int ksz_port_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid, struct dsa_db db) @@ -1277,6 +2314,8 @@ void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) ksz_pread8(dev, port, regs[P_STP_CTRL], &data); data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE); + p = &dev->ports[port]; + switch (state) { case BR_STATE_DISABLED: data |= PORT_LEARN_DISABLE; @@ -1286,9 +2325,13 @@ void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) break; case BR_STATE_LEARNING: data |= PORT_RX_ENABLE; + if (!p->learning) + data |= PORT_LEARN_DISABLE; break; case BR_STATE_FORWARDING: data |= (PORT_TX_ENABLE | PORT_RX_ENABLE); + if (!p->learning) + data |= PORT_LEARN_DISABLE; break; case BR_STATE_BLOCKING: data |= PORT_LEARN_DISABLE; @@ -1300,12 +2343,38 @@ void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) ksz_pwrite8(dev, port, regs[P_STP_CTRL], data); - p = &dev->ports[port]; p->stp_state = state; ksz_update_port_member(dev, port); } +static int ksz_port_pre_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + if (flags.mask & ~BR_LEARNING) + return -EINVAL; + + return 0; +} + +static int ksz_port_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + struct ksz_device *dev = ds->priv; + struct ksz_port *p = &dev->ports[port]; + + if (flags.mask & BR_LEARNING) { + p->learning = !!(flags.val & BR_LEARNING); + + /* Make the change take effect immediately */ + ksz_port_stp_state_set(ds, port, p->stp_state); + } + + return 0; +} + static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds, int port, enum dsa_tag_protocol mp) @@ -1319,10 +2388,12 @@ static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds, proto = DSA_TAG_PROTO_KSZ8795; if (dev->chip_id == KSZ8830_CHIP_ID || + dev->chip_id == KSZ8563_CHIP_ID || dev->chip_id == KSZ9893_CHIP_ID) proto = DSA_TAG_PROTO_KSZ9893; if (dev->chip_id == KSZ9477_CHIP_ID || + dev->chip_id == KSZ9896_CHIP_ID || dev->chip_id == KSZ9897_CHIP_ID || dev->chip_id == KSZ9567_CHIP_ID) proto = DSA_TAG_PROTO_KSZ9477; @@ -1437,7 +2508,8 @@ static void ksz_set_xmii(struct ksz_device *dev, int port, case PHY_INTERFACE_MODE_RGMII_RXID: data8 |= bitval[P_RGMII_SEL]; /* On KSZ9893, disable RGMII in-band status support */ - if (dev->features & IS_9893) + if (dev->chip_id == KSZ9893_CHIP_ID || + dev->chip_id == KSZ8563_CHIP_ID) data8 &= ~P_MII_MAC_MODE; break; default: @@ -1609,13 +2681,13 @@ static void ksz_duplex_flowctrl(struct ksz_device *dev, int port, int duplex, ksz_prmw8(dev, port, regs[P_XMII_CTRL_0], mask, val); } -static void ksz_phylink_mac_link_up(struct dsa_switch *ds, int port, - unsigned int mode, - phy_interface_t interface, - struct phy_device *phydev, int speed, - int duplex, bool tx_pause, bool rx_pause) +static void ksz9477_phylink_mac_link_up(struct ksz_device *dev, int port, + unsigned int mode, + phy_interface_t interface, + struct phy_device *phydev, int speed, + int duplex, bool tx_pause, + bool rx_pause) { - struct ksz_device *dev = ds->priv; struct ksz_port *p; p = &dev->ports[port]; @@ -1629,6 +2701,15 @@ static void ksz_phylink_mac_link_up(struct dsa_switch *ds, int port, ksz_port_set_xmii_speed(dev, port, speed); ksz_duplex_flowctrl(dev, port, duplex, tx_pause, rx_pause); +} + +static void ksz_phylink_mac_link_up(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface, + struct phy_device *phydev, int speed, + int duplex, bool tx_pause, bool rx_pause) +{ + struct ksz_device *dev = ds->priv; if (dev->dev_ops->phylink_mac_link_up) dev->dev_ops->phylink_mac_link_up(dev, port, mode, interface, @@ -1638,7 +2719,7 @@ static void ksz_phylink_mac_link_up(struct dsa_switch *ds, int port, static int ksz_switch_detect(struct ksz_device *dev) { - u8 id1, id2; + u8 id1, id2, id4; u16 id16; u32 id32; int ret; @@ -1683,8 +2764,8 @@ static int ksz_switch_detect(struct ksz_device *dev) switch (id32) { case KSZ9477_CHIP_ID: + case KSZ9896_CHIP_ID: case KSZ9897_CHIP_ID: - case KSZ9893_CHIP_ID: case KSZ9567_CHIP_ID: case LAN9370_CHIP_ID: case LAN9371_CHIP_ID: @@ -1693,6 +2774,18 @@ static int ksz_switch_detect(struct ksz_device *dev) case LAN9374_CHIP_ID: dev->chip_id = id32; break; + case KSZ9893_CHIP_ID: + ret = ksz_read8(dev, REG_CHIP_ID4, + &id4); + if (ret) + return ret; + + if (id4 == SKU_ID_KSZ8563) + dev->chip_id = KSZ8563_CHIP_ID; + else + dev->chip_id = KSZ9893_CHIP_ID; + + break; default: dev_err(dev->dev, "unsupported switch detected %x)\n", id32); @@ -1706,6 +2799,7 @@ static const struct dsa_switch_ops ksz_switch_ops = { .get_tag_protocol = ksz_get_tag_protocol, .get_phy_flags = ksz_get_phy_flags, .setup = ksz_setup, + .teardown = ksz_teardown, .phy_read = ksz_phy_read16, .phy_write = ksz_phy_write16, .phylink_get_caps = ksz_phylink_get_caps, @@ -1713,12 +2807,15 @@ static const struct dsa_switch_ops ksz_switch_ops = { .phylink_mac_link_up = ksz_phylink_mac_link_up, .phylink_mac_link_down = ksz_mac_link_down, .port_enable = ksz_enable_port, + .set_ageing_time = ksz_set_ageing_time, .get_strings = ksz_get_strings, .get_ethtool_stats = ksz_get_ethtool_stats, .get_sset_count = ksz_sset_count, .port_bridge_join = ksz_port_bridge_join, .port_bridge_leave = ksz_port_bridge_leave, .port_stp_state_set = ksz_port_stp_state_set, + .port_pre_bridge_flags = ksz_port_pre_bridge_flags, + .port_bridge_flags = ksz_port_bridge_flags, .port_fast_age = ksz_port_fast_age, .port_vlan_filtering = ksz_port_vlan_filtering, .port_vlan_add = ksz_port_vlan_add, @@ -1868,6 +2965,9 @@ int ksz_switch_register(struct ksz_device *dev) GFP_KERNEL); if (!dev->ports[i].mib.counters) return -ENOMEM; + + dev->ports[i].ksz_dev = dev; + dev->ports[i].num = i; } /* set the real number of ports */ |