// SPDX-License-Identifier: GPL-2.0 /* * Microchip KSZ8795 switch driver * * Copyright (C) 2017 Microchip Technology Inc. * Tristram Ha <[email protected]> */ #include <linux/bitfield.h> #include <linux/delay.h> #include <linux/export.h> #include <linux/gpio.h> #include <linux/if_vlan.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_data/microchip-ksz.h> #include <linux/phy.h> #include <linux/etherdevice.h> #include <linux/if_bridge.h> #include <linux/micrel_phy.h> #include <net/dsa.h> #include <net/switchdev.h> #include <linux/phylink.h> #include "ksz_common.h" #include "ksz8795_reg.h" #include "ksz8.h" static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set) { … } static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits, bool set) { … } static int ksz8_ind_write8(struct ksz_device *dev, u8 table, u16 addr, u8 data) { … } int ksz8_reset_switch(struct ksz_device *dev) { … } static int ksz8863_change_mtu(struct ksz_device *dev, int frame_size) { … } static int ksz8795_change_mtu(struct ksz_device *dev, int frame_size) { … } int ksz8_change_mtu(struct ksz_device *dev, int port, int mtu) { … } static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues) { … } int ksz8_all_queues_split(struct ksz_device *dev, int queues) { … } void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt) { … } static void ksz8795_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, u64 *dropped, u64 *cnt) { … } static void ksz8863_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, u64 *dropped, u64 *cnt) { … } void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, u64 *dropped, u64 *cnt) { … } void ksz8_freeze_mib(struct ksz_device *dev, int port, bool freeze) { … } void ksz8_port_init_cnt(struct ksz_device *dev, int port) { … } static int ksz8_r_table(struct ksz_device *dev, int table, u16 addr, u64 *data) { … } static int ksz8_w_table(struct ksz_device *dev, int table, u16 addr, u64 data) { … } static int ksz8_valid_dyn_entry(struct ksz_device *dev, u8 *data) { … } static int ksz8_r_dyn_mac_table(struct ksz_device *dev, u16 addr, u8 *mac_addr, u8 *fid, u8 *src_port, u16 *entries) { … } static int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr, struct alu_struct *alu, bool *valid) { … } static int ksz8_w_sta_mac_table(struct ksz_device *dev, u16 addr, struct alu_struct *alu) { … } static void ksz8_from_vlan(struct ksz_device *dev, u32 vlan, u8 *fid, u8 *member, u8 *valid) { … } static void ksz8_to_vlan(struct ksz_device *dev, u8 fid, u8 member, u8 valid, u16 *vlan) { … } static void ksz8_r_vlan_entries(struct ksz_device *dev, u16 addr) { … } static void ksz8_r_vlan_table(struct ksz_device *dev, u16 vid, u16 *vlan) { … } static void ksz8_w_vlan_table(struct ksz_device *dev, u16 vid, u16 vlan) { … } /** * ksz879x_get_loopback - KSZ879x specific function to get loopback * configuration status for a specific port * @dev: Pointer to the device structure * @port: Port number to query * @val: Pointer to store the result * * This function reads the SMI registers to determine whether loopback mode * is enabled for a specific port. * * Return: 0 on success, error code on failure. */ static int ksz879x_get_loopback(struct ksz_device *dev, u16 port, u16 *val) { … } /** * ksz879x_set_loopback - KSZ879x specific function to set loopback mode for * a specific port * @dev: Pointer to the device structure. * @port: Port number to modify. * @val: Value indicating whether to enable or disable loopback mode. * * This function translates loopback bit of the BMCR register into the * corresponding hardware register bit value and writes it to the SMI interface. * * Return: 0 on success, error code on failure. */ static int ksz879x_set_loopback(struct ksz_device *dev, u16 port, u16 val) { … } /** * ksz8_r_phy_ctrl - Translates and reads from the SMI interface to a MIIM PHY * Control register (Reg. 31). * @dev: The KSZ device instance. * @port: The port number to be read. * @val: The value read from the SMI interface. * * This function reads the SMI interface and translates the hardware register * bit values into their corresponding control settings for a MIIM PHY Control * register. * * Return: 0 on success, error code on failure. */ static int ksz8_r_phy_ctrl(struct ksz_device *dev, int port, u16 *val) { … } /** * ksz8_r_phy_bmcr - Translates and reads from the SMI interface to a MIIM PHY * Basic mode control register (Reg. 0). * @dev: The KSZ device instance. * @port: The port number to be read. * @val: The value read from the SMI interface. * * This function reads the SMI interface and translates the hardware register * bit values into their corresponding control settings for a MIIM PHY Basic * mode control register. * * MIIM Bit Mapping Comparison between KSZ8794 and KSZ8873 * ------------------------------------------------------------------- * MIIM Bit | KSZ8794 Reg/Bit | KSZ8873 Reg/Bit * ----------------------------+-----------------------------+---------------- * Bit 15 - Soft Reset | 0xF/4 | Not supported * Bit 14 - Loopback | 0xD/0 (MAC), 0xF/7 (PHY) ~ 0xD/0 (PHY) * Bit 13 - Force 100 | 0xC/6 = 0xC/6 * Bit 12 - AN Enable | 0xC/7 (reverse logic) ~ 0xC/7 * Bit 11 - Power Down | 0xD/3 = 0xD/3 * Bit 10 - PHY Isolate | 0xF/5 | Not supported * Bit 9 - Restart AN | 0xD/5 = 0xD/5 * Bit 8 - Force Full-Duplex | 0xC/5 = 0xC/5 * Bit 7 - Collision Test/Res. | Not supported | Not supported * Bit 6 - Reserved | Not supported | Not supported * Bit 5 - Hp_mdix | 0x9/7 ~ 0xF/7 * Bit 4 - Force MDI | 0xD/1 = 0xD/1 * Bit 3 - Disable MDIX | 0xD/2 = 0xD/2 * Bit 2 - Disable Far-End F. | ???? | 0xD/4 * Bit 1 - Disable Transmit | 0xD/6 = 0xD/6 * Bit 0 - Disable LED | 0xD/7 = 0xD/7 * ------------------------------------------------------------------- * * Return: 0 on success, error code on failure. */ static int ksz8_r_phy_bmcr(struct ksz_device *dev, u16 port, u16 *val) { … } int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) { … } /** * ksz8_w_phy_ctrl - Translates and writes to the SMI interface from a MIIM PHY * Control register (Reg. 31). * @dev: The KSZ device instance. * @port: The port number to be configured. * @val: The register value to be written. * * This function translates control settings from a MIIM PHY Control register * into their corresponding hardware register bit values for the SMI * interface. * * Return: 0 on success, error code on failure. */ static int ksz8_w_phy_ctrl(struct ksz_device *dev, int port, u16 val) { … } /** * ksz8_w_phy_bmcr - Translates and writes to the SMI interface from a MIIM PHY * Basic mode control register (Reg. 0). * @dev: The KSZ device instance. * @port: The port number to be configured. * @val: The register value to be written. * * This function translates control settings from a MIIM PHY Basic mode control * register into their corresponding hardware register bit values for the SMI * interface. * * MIIM Bit Mapping Comparison between KSZ8794 and KSZ8873 * ------------------------------------------------------------------- * MIIM Bit | KSZ8794 Reg/Bit | KSZ8873 Reg/Bit * ----------------------------+-----------------------------+---------------- * Bit 15 - Soft Reset | 0xF/4 | Not supported * Bit 14 - Loopback | 0xD/0 (MAC), 0xF/7 (PHY) ~ 0xD/0 (PHY) * Bit 13 - Force 100 | 0xC/6 = 0xC/6 * Bit 12 - AN Enable | 0xC/7 (reverse logic) ~ 0xC/7 * Bit 11 - Power Down | 0xD/3 = 0xD/3 * Bit 10 - PHY Isolate | 0xF/5 | Not supported * Bit 9 - Restart AN | 0xD/5 = 0xD/5 * Bit 8 - Force Full-Duplex | 0xC/5 = 0xC/5 * Bit 7 - Collision Test/Res. | Not supported | Not supported * Bit 6 - Reserved | Not supported | Not supported * Bit 5 - Hp_mdix | 0x9/7 ~ 0xF/7 * Bit 4 - Force MDI | 0xD/1 = 0xD/1 * Bit 3 - Disable MDIX | 0xD/2 = 0xD/2 * Bit 2 - Disable Far-End F. | ???? | 0xD/4 * Bit 1 - Disable Transmit | 0xD/6 = 0xD/6 * Bit 0 - Disable LED | 0xD/7 = 0xD/7 * ------------------------------------------------------------------- * * Return: 0 on success, error code on failure. */ static int ksz8_w_phy_bmcr(struct ksz_device *dev, u16 port, u16 val) { … } int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) { … } void ksz8_cfg_port_member(struct ksz_device *dev, int port, u8 member) { … } void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port) { … } int ksz8_fdb_dump(struct ksz_device *dev, int port, dsa_fdb_dump_cb_t *cb, void *data) { … } static int ksz8_add_sta_mac(struct ksz_device *dev, int port, const unsigned char *addr, u16 vid) { … } static int ksz8_del_sta_mac(struct ksz_device *dev, int port, const unsigned char *addr, u16 vid) { … } int ksz8_mdb_add(struct ksz_device *dev, int port, const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) { … } int ksz8_mdb_del(struct ksz_device *dev, int port, const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) { … } int ksz8_fdb_add(struct ksz_device *dev, int port, const unsigned char *addr, u16 vid, struct dsa_db db) { … } int ksz8_fdb_del(struct ksz_device *dev, int port, const unsigned char *addr, u16 vid, struct dsa_db db) { … } int ksz8_port_vlan_filtering(struct ksz_device *dev, int port, bool flag, struct netlink_ext_ack *extack) { … } static void ksz8_port_enable_pvid(struct ksz_device *dev, int port, bool state) { … } int ksz8_port_vlan_add(struct ksz_device *dev, int port, const struct switchdev_obj_port_vlan *vlan, struct netlink_ext_ack *extack) { … } int ksz8_port_vlan_del(struct ksz_device *dev, int port, const struct switchdev_obj_port_vlan *vlan) { … } int ksz8_port_mirror_add(struct ksz_device *dev, int port, struct dsa_mall_mirror_tc_entry *mirror, bool ingress, struct netlink_ext_ack *extack) { … } void ksz8_port_mirror_del(struct ksz_device *dev, int port, struct dsa_mall_mirror_tc_entry *mirror) { … } static void ksz8795_cpu_interface_select(struct ksz_device *dev, int port) { … } void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port) { … } static void ksz88x3_config_rmii_clk(struct ksz_device *dev) { … } void ksz8_config_cpu_port(struct dsa_switch *ds) { … } /** * ksz8_phy_port_link_up - Configures ports with integrated PHYs * @dev: The KSZ device instance. * @port: The port number to configure. * @duplex: The desired duplex mode. * @tx_pause: If true, enables transmit pause. * @rx_pause: If true, enables receive pause. * * Description: * The function configures flow control settings for a given port based on the * desired settings and current duplex mode. * * According to the KSZ8873 datasheet, the PORT_FORCE_FLOW_CTRL bit in the * Port Control 2 register (0x1A for Port 1, 0x22 for Port 2, 0x32 for Port 3) * determines how flow control is handled on the port: * "1 = will always enable full-duplex flow control on the port, regardless * of AN result. * 0 = full-duplex flow control is enabled based on AN result." * * This means that the flow control behavior depends on the state of this bit: * - If PORT_FORCE_FLOW_CTRL is set to 1, the switch will ignore AN results and * force flow control on the port. * - If PORT_FORCE_FLOW_CTRL is set to 0, the switch will enable or disable * flow control based on the AN results. * * However, there is a potential limitation in this configuration. It is * currently not possible to force disable flow control on a port if we still * advertise pause support. While such a configuration is not currently * supported by Linux, and may not make practical sense, it's important to be * aware of this limitation when working with the KSZ8873 and similar devices. */ static void ksz8_phy_port_link_up(struct ksz_device *dev, int port, int duplex, bool tx_pause, bool rx_pause) { … } /** * ksz8_cpu_port_link_up - Configures the CPU port of the switch. * @dev: The KSZ device instance. * @speed: The desired link speed. * @duplex: The desired duplex mode. * @tx_pause: If true, enables transmit pause. * @rx_pause: If true, enables receive pause. * * Description: * The function configures flow control and speed settings for the CPU * port of the switch based on the desired settings, current duplex mode, and * speed. */ static void ksz8_cpu_port_link_up(struct ksz_device *dev, int speed, int duplex, bool tx_pause, bool rx_pause) { … } void ksz8_phylink_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) { … } static int ksz8_handle_global_errata(struct dsa_switch *ds) { … } int ksz8_enable_stp_addr(struct ksz_device *dev) { … } int ksz8_setup(struct dsa_switch *ds) { … } void ksz8_get_caps(struct ksz_device *dev, int port, struct phylink_config *config) { … } u32 ksz8_get_port_addr(int port, int offset) { … } int ksz8_switch_init(struct ksz_device *dev) { … } void ksz8_switch_exit(struct ksz_device *dev) { … } MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;