#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "regs.h" #define REALTEK_HW_STOP_DELAY 25 /* msecs */ #define REALTEK_HW_START_DELAY 100 /* msecs */ #define REALTEK_SMI_ACK_RETRY_COUNT 5 /* Chip-specific data and limits */ #define RTL8365MB_CHIP_ID_8365MB_VC 0x6367 #define RTL8365MB_CHIP_VER_8365MB_VC 0x0040 #define RTL8365MB_CHIP_ID_8367S 0x6367 #define RTL8365MB_CHIP_VER_8367S 0x00A0 #define RTL8365MB_CHIP_ID_8367RB 0x6367 #define RTL8365MB_CHIP_VER_8367RB 0x0020 /* Family-specific data and limits */ #define RTL8365MB_PHYADDRMAX 7 #define RTL8365MB_NUM_PHYREGS 32 #define RTL8365MB_PHYREGMAX (RTL8365MB_NUM_PHYREGS - 1) /* RTL8370MB and RTL8310SR, possibly suportable by this driver, have 10 ports */ #define RTL8365MB_MAX_NUM_PORTS 10 #define RTL8365MB_LEARN_LIMIT_MAX 2112 /* valid for all 6-port or less variants */ static const int rtl8365mb_extint_port_map[] = { -1, -1, -1, -1, -1, -1, 1, 2, -1, -1}; /* Chip identification registers */ #define RTL8365MB_CHIP_ID_REG 0x1300 #define RTL8365MB_CHIP_VER_REG 0x1301 #define RTL8365MB_MAGIC_REG 0x13C2 #define RTL8365MB_MAGIC_VALUE 0x0249 /* Chip reset register */ #define RTL8365MB_CHIP_RESET_REG 0x1322 #define RTL8365MB_CHIP_RESET_SW_MASK 0x0002 #define RTL8365MB_CHIP_RESET_HW_MASK 0x0001 /* Interrupt polarity register */ #define RTL8365MB_INTR_POLARITY_REG 0x1100 #define RTL8365MB_INTR_POLARITY_MASK 0x0001 #define RTL8365MB_INTR_POLARITY_HIGH 0 #define RTL8365MB_INTR_POLARITY_LOW 1 /* Interrupt control/status register - enable/check specific interrupt types */ #define RTL8365MB_INTR_CTRL_REG 0x1101 #define RTL8365MB_INTR_STATUS_REG 0x1102 #define RTL8365MB_INTR_SLIENT_START_2_MASK 0x1000 #define RTL8365MB_INTR_SLIENT_START_MASK 0x0800 #define RTL8365MB_INTR_ACL_ACTION_MASK 0x0200 #define RTL8365MB_INTR_CABLE_DIAG_FIN_MASK 0x0100 #define RTL8365MB_INTR_INTERRUPT_8051_MASK 0x0080 #define RTL8365MB_INTR_LOOP_DETECTION_MASK 0x0040 #define RTL8365MB_INTR_GREEN_TIMER_MASK 0x0020 #define RTL8365MB_INTR_SPECIAL_CONGEST_MASK 0x0010 #define RTL8365MB_INTR_SPEED_CHANGE_MASK 0x0008 #define RTL8365MB_INTR_LEARN_OVER_MASK 0x0004 #define RTL8365MB_INTR_METER_EXCEEDED_MASK 0x0002 #define RTL8365MB_INTR_LINK_CHANGE_MASK 0x0001 #define RTL8365MB_INTR_ALL_MASK \ (RTL8365MB_INTR_SLIENT_START_2_MASK | \ RTL8365MB_INTR_SLIENT_START_MASK | \ RTL8365MB_INTR_ACL_ACTION_MASK | \ RTL8365MB_INTR_CABLE_DIAG_FIN_MASK | \ RTL8365MB_INTR_INTERRUPT_8051_MASK | \ RTL8365MB_INTR_LOOP_DETECTION_MASK | \ RTL8365MB_INTR_GREEN_TIMER_MASK | \ RTL8365MB_INTR_SPECIAL_CONGEST_MASK | \ RTL8365MB_INTR_SPEED_CHANGE_MASK | \ RTL8365MB_INTR_LEARN_OVER_MASK | \ RTL8365MB_INTR_METER_EXCEEDED_MASK | \ RTL8365MB_INTR_LINK_CHANGE_MASK) /* Per-port interrupt type status registers */ #define RTL8365MB_PORT_LINKDOWN_IND_REG 0x1106 #define RTL8365MB_PORT_LINKDOWN_IND_MASK 0x07FF #define RTL8365MB_PORT_LINKUP_IND_REG 0x1107 #define RTL8365MB_PORT_LINKUP_IND_MASK 0x07FF /* PHY indirect access registers */ #define RTL8365MB_INDIRECT_ACCESS_CTRL_REG 0x1F00 #define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK 0x0002 #define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ 0 #define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE 1 #define RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK 0x0001 #define RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE 1 #define RTL8365MB_INDIRECT_ACCESS_STATUS_REG 0x1F01 #define RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG 0x1F02 #define RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_5_1_MASK GENMASK(4, 0) #define RTL8365MB_INDIRECT_ACCESS_ADDRESS_PHYNUM_MASK GENMASK(7, 5) #define RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK GENMASK(11, 8) #define RTL8365MB_PHY_BASE 0x2000 #define RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG 0x1F03 #define RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG 0x1F04 /* PHY OCP address prefix register */ #define RTL8365MB_GPHY_OCP_MSB_0_REG 0x1D15 #define RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK 0x0FC0 #define RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK 0xFC00 /* The PHY OCP addresses of PHY registers 0~31 start here */ #define RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE 0xA400 /* EXT interface port mode values - used in DIGITAL_INTERFACE_SELECT */ #define RTL8365MB_EXT_PORT_MODE_DISABLE 0 #define RTL8365MB_EXT_PORT_MODE_RGMII 1 #define RTL8365MB_EXT_PORT_MODE_MII_MAC 2 #define RTL8365MB_EXT_PORT_MODE_MII_PHY 3 #define RTL8365MB_EXT_PORT_MODE_TMII_MAC 4 #define RTL8365MB_EXT_PORT_MODE_TMII_PHY 5 #define RTL8365MB_EXT_PORT_MODE_GMII 6 #define RTL8365MB_EXT_PORT_MODE_RMII_MAC 7 #define RTL8365MB_EXT_PORT_MODE_RMII_PHY 8 #define RTL8365MB_EXT_PORT_MODE_SGMII 9 #define RTL8365MB_EXT_PORT_MODE_HSGMII 10 #define RTL8365MB_EXT_PORT_MODE_1000X_100FX 11 #define RTL8365MB_EXT_PORT_MODE_1000X 12 #define RTL8365MB_EXT_PORT_MODE_100FX 13 /* Realtek docs and driver uses logic number as EXT_PORT0=16, EXT_PORT1=17, * EXT_PORT2=18, to interact with switch ports. That logic number is internally * converted to either a physical port number (0..9) or an external interface id (0..2), * depending on which function was called. The external interface id is calculated as * (ext_id=logic_port-15), while the logical to physical map depends on the chip id/version. * * EXT_PORT0 mentioned in datasheets and rtl8367c driver is used in this driver * as extid==1, EXT_PORT2, mentioned in Realtek rtl8367c driver for 10-port switches, * would have an ext_id of 3 (out of range for most extint macros) and ext_id 0 does * not seem to be used as well for this family. */ /* EXT interface mode configuration registers 0~1 */ #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 /* EXT1 */ #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 0x13C3 /* EXT2 */ #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(_extint) \ ((_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \ (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 : \ 0x0) #define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(_extint) \ (0xF << (((_extint) % 2))) #define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(_extint) \ (((_extint) % 2) * 4) /* EXT interface RGMII TX/RX delay configuration registers 0~2 */ #define RTL8365MB_EXT_RGMXF_REG0 0x1306 /* EXT0 */ #define RTL8365MB_EXT_RGMXF_REG1 0x1307 /* EXT1 */ #define RTL8365MB_EXT_RGMXF_REG2 0x13C5 /* EXT2 */ #define RTL8365MB_EXT_RGMXF_REG(_extint) \ ((_extint) == 0 ? RTL8365MB_EXT_RGMXF_REG0 : \ (_extint) == 1 ? RTL8365MB_EXT_RGMXF_REG1 : \ (_extint) == 2 ? RTL8365MB_EXT_RGMXF_REG2 : \ 0x0) #define RTL8365MB_EXT_RGMXF_RXDELAY_MASK 0x0007 #define RTL8365MB_EXT_RGMXF_TXDELAY_MASK 0x0008 /* External interface port speed values - used in DIGITAL_INTERFACE_FORCE */ #define RTL8365MB_PORT_SPEED_10M 0 #define RTL8365MB_PORT_SPEED_100M 1 #define RTL8365MB_PORT_SPEED_1000M 2 /* EXT interface force configuration registers 0~2 */ #define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 0x1310 /* EXT0 */ #define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 0x1311 /* EXT1 */ #define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 0x13C4 /* EXT2 */ #define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(_extint) \ ((_extint) == 0 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 : \ (_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 : \ (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 : \ 0x0) #define RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK 0x1000 #define RTL8365MB_DIGITAL_INTERFACE_FORCE_NWAY_MASK 0x0080 #define RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK 0x0040 #define RTL8365MB_DIGITAL_INTERFACE_FORCE_RXPAUSE_MASK 0x0020 #define RTL8365MB_DIGITAL_INTERFACE_FORCE_LINK_MASK 0x0010 #define RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK 0x0004 #define RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK 0x0003 /* CPU port mask register - controls which ports are treated as CPU ports */ #define RTL8365MB_CPU_PORT_MASK_REG 0x1219 #define RTL8365MB_CPU_PORT_MASK_MASK 0x07FF /* CPU control register */ #define RTL8365MB_CPU_CTRL_REG 0x121A #define RTL8365MB_CPU_CTRL_TRAP_PORT_EXT_MASK 0x0400 #define RTL8365MB_CPU_CTRL_TAG_FORMAT_MASK 0x0200 #define RTL8365MB_CPU_CTRL_RXBYTECOUNT_MASK 0x0080 #define RTL8365MB_CPU_CTRL_TAG_POSITION_MASK 0x0040 #define RTL8365MB_CPU_CTRL_TRAP_PORT_MASK 0x0038 #define RTL8365MB_CPU_CTRL_INSERTMODE_MASK 0x0006 #define RTL8365MB_CPU_CTRL_EN_MASK 0x0001 /* Maximum packet length register */ #define RTL8365MB_CFG0_MAX_LEN_REG 0x088C #define RTL8365MB_CFG0_MAX_LEN_MASK 0x3FFF /* Port learning limit registers */ #define RTL8365MB_LUT_PORT_LEARN_LIMIT_BASE 0x0A20 #define RTL8365MB_LUT_PORT_LEARN_LIMIT_REG(_physport) \ (RTL8365MB_LUT_PORT_LEARN_LIMIT_BASE + (_physport)) /* Port isolation (forwarding mask) registers */ #define RTL8365MB_PORT_ISOLATION_REG_BASE 0x08A2 #define RTL8365MB_PORT_ISOLATION_REG(_physport) \ (RTL8365MB_PORT_ISOLATION_REG_BASE + (_physport)) #define RTL8365MB_PORT_ISOLATION_MASK 0x07FF /* MSTP port state registers - indexed by tree instance */ #define RTL8365MB_MSTI_CTRL_BASE 0x0A00 #define RTL8365MB_MSTI_CTRL_REG(_msti, _physport) \ (RTL8365MB_MSTI_CTRL_BASE + ((_msti) << 1) + ((_physport) >> 3)) #define RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET(_physport) ((_physport) << 1) #define RTL8365MB_MSTI_CTRL_PORT_STATE_MASK(_physport) \ (0x3 << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET((_physport))) /* MIB counter value registers */ #define RTL8365MB_MIB_COUNTER_BASE 0x1000 #define RTL8365MB_MIB_COUNTER_REG(_x) (RTL8365MB_MIB_COUNTER_BASE + (_x)) /* MIB counter address register */ #define RTL8365MB_MIB_ADDRESS_REG 0x1004 #define RTL8365MB_MIB_ADDRESS_PORT_OFFSET 0x007C #define RTL8365MB_MIB_ADDRESS(_p, _x) \ (((RTL8365MB_MIB_ADDRESS_PORT_OFFSET) * (_p) + (_x)) >> 2) #define RTL8365MB_MIB_CTRL0_REG 0x1005 #define RTL8365MB_MIB_CTRL0_RESET_MASK 0x0002 #define RTL8365MB_MIB_CTRL0_BUSY_MASK 0x0001 /* The DSA callback .get_stats64 runs in atomic context, so we are not allowed * to block. On the other hand, accessing MIB counters absolutely requires us to * block. The solution is thus to schedule work which polls the MIB counters * asynchronously and updates some private data, which the callback can then * fetch atomically. Three seconds should be a good enough polling interval. */ #define RTL8365MB_STATS_INTERVAL_JIFFIES (3 * HZ) enum rtl8365mb_mib_counter_index { RTL8365MB_MIB_ifInOctets, RTL8365MB_MIB_dot3StatsFCSErrors, RTL8365MB_MIB_dot3StatsSymbolErrors, RTL8365MB_MIB_dot3InPauseFrames, RTL8365MB_MIB_dot3ControlInUnknownOpcodes, RTL8365MB_MIB_etherStatsFragments, RTL8365MB_MIB_etherStatsJabbers, RTL8365MB_MIB_ifInUcastPkts, RTL8365MB_MIB_etherStatsDropEvents, RTL8365MB_MIB_ifInMulticastPkts, RTL8365MB_MIB_ifInBroadcastPkts, RTL8365MB_MIB_inMldChecksumError, RTL8365MB_MIB_inIgmpChecksumError, RTL8365MB_MIB_inMldSpecificQuery, RTL8365MB_MIB_inMldGeneralQuery, RTL8365MB_MIB_inIgmpSpecificQuery, RTL8365MB_MIB_inIgmpGeneralQuery, RTL8365MB_MIB_inMldLeaves, RTL8365MB_MIB_inIgmpLeaves, RTL8365MB_MIB_etherStatsOctets, RTL8365MB_MIB_etherStatsUnderSizePkts, RTL8365MB_MIB_etherOversizeStats, RTL8365MB_MIB_etherStatsPkts64Octets, RTL8365MB_MIB_etherStatsPkts65to127Octets, RTL8365MB_MIB_etherStatsPkts128to255Octets, RTL8365MB_MIB_etherStatsPkts256to511Octets, RTL8365MB_MIB_etherStatsPkts512to1023Octets, RTL8365MB_MIB_etherStatsPkts1024to1518Octets, RTL8365MB_MIB_ifOutOctets, RTL8365MB_MIB_dot3StatsSingleCollisionFrames, RTL8365MB_MIB_dot3StatsMultipleCollisionFrames, RTL8365MB_MIB_dot3StatsDeferredTransmissions, RTL8365MB_MIB_dot3StatsLateCollisions, RTL8365MB_MIB_etherStatsCollisions, RTL8365MB_MIB_dot3StatsExcessiveCollisions, RTL8365MB_MIB_dot3OutPauseFrames, RTL8365MB_MIB_ifOutDiscards, RTL8365MB_MIB_dot1dTpPortInDiscards, RTL8365MB_MIB_ifOutUcastPkts, RTL8365MB_MIB_ifOutMulticastPkts, RTL8365MB_MIB_ifOutBroadcastPkts, RTL8365MB_MIB_outOampduPkts, RTL8365MB_MIB_inOampduPkts, RTL8365MB_MIB_inIgmpJoinsSuccess, RTL8365MB_MIB_inIgmpJoinsFail, RTL8365MB_MIB_inMldJoinsSuccess, RTL8365MB_MIB_inMldJoinsFail, RTL8365MB_MIB_inReportSuppressionDrop, RTL8365MB_MIB_inLeaveSuppressionDrop, RTL8365MB_MIB_outIgmpReports, RTL8365MB_MIB_outIgmpLeaves, RTL8365MB_MIB_outIgmpGeneralQuery, RTL8365MB_MIB_outIgmpSpecificQuery, RTL8365MB_MIB_outMldReports, RTL8365MB_MIB_outMldLeaves, RTL8365MB_MIB_outMldGeneralQuery, RTL8365MB_MIB_outMldSpecificQuery, RTL8365MB_MIB_inKnownMulticastPkts, RTL8365MB_MIB_END, }; struct rtl8365mb_mib_counter { u32 offset; u32 length; const char *name; }; #define RTL8365MB_MAKE_MIB_COUNTER(_offset, _length, _name) \ [RTL8365MB_MIB_ ## _name] = { _offset, _length, #_name } static struct rtl8365mb_mib_counter rtl8365mb_mib_counters[] = { RTL8365MB_MAKE_MIB_COUNTER(0, 4, ifInOctets), RTL8365MB_MAKE_MIB_COUNTER(4, 2, dot3StatsFCSErrors), RTL8365MB_MAKE_MIB_COUNTER(6, 2, dot3StatsSymbolErrors), RTL8365MB_MAKE_MIB_COUNTER(8, 2, dot3InPauseFrames), RTL8365MB_MAKE_MIB_COUNTER(10, 2, dot3ControlInUnknownOpcodes), RTL8365MB_MAKE_MIB_COUNTER(12, 2, etherStatsFragments), RTL8365MB_MAKE_MIB_COUNTER(14, 2, etherStatsJabbers), RTL8365MB_MAKE_MIB_COUNTER(16, 2, ifInUcastPkts), RTL8365MB_MAKE_MIB_COUNTER(18, 2, etherStatsDropEvents), RTL8365MB_MAKE_MIB_COUNTER(20, 2, ifInMulticastPkts), RTL8365MB_MAKE_MIB_COUNTER(22, 2, ifInBroadcastPkts), RTL8365MB_MAKE_MIB_COUNTER(24, 2, inMldChecksumError), RTL8365MB_MAKE_MIB_COUNTER(26, 2, inIgmpChecksumError), RTL8365MB_MAKE_MIB_COUNTER(28, 2, inMldSpecificQuery), RTL8365MB_MAKE_MIB_COUNTER(30, 2, inMldGeneralQuery), RTL8365MB_MAKE_MIB_COUNTER(32, 2, inIgmpSpecificQuery), RTL8365MB_MAKE_MIB_COUNTER(34, 2, inIgmpGeneralQuery), RTL8365MB_MAKE_MIB_COUNTER(36, 2, inMldLeaves), RTL8365MB_MAKE_MIB_COUNTER(38, 2, inIgmpLeaves), RTL8365MB_MAKE_MIB_COUNTER(40, 4, etherStatsOctets), RTL8365MB_MAKE_MIB_COUNTER(44, 2, etherStatsUnderSizePkts), RTL8365MB_MAKE_MIB_COUNTER(46, 2, etherOversizeStats), RTL8365MB_MAKE_MIB_COUNTER(48, 2, etherStatsPkts64Octets), RTL8365MB_MAKE_MIB_COUNTER(50, 2, etherStatsPkts65to127Octets), RTL8365MB_MAKE_MIB_COUNTER(52, 2, etherStatsPkts128to255Octets), RTL8365MB_MAKE_MIB_COUNTER(54, 2, etherStatsPkts256to511Octets), RTL8365MB_MAKE_MIB_COUNTER(56, 2, etherStatsPkts512to1023Octets), RTL8365MB_MAKE_MIB_COUNTER(58, 2, etherStatsPkts1024to1518Octets), RTL8365MB_MAKE_MIB_COUNTER(60, 4, ifOutOctets), RTL8365MB_MAKE_MIB_COUNTER(64, 2, dot3StatsSingleCollisionFrames), RTL8365MB_MAKE_MIB_COUNTER(66, 2, dot3StatsMultipleCollisionFrames), RTL8365MB_MAKE_MIB_COUNTER(68, 2, dot3StatsDeferredTransmissions), RTL8365MB_MAKE_MIB_COUNTER(70, 2, dot3StatsLateCollisions), RTL8365MB_MAKE_MIB_COUNTER(72, 2, etherStatsCollisions), RTL8365MB_MAKE_MIB_COUNTER(74, 2, dot3StatsExcessiveCollisions), RTL8365MB_MAKE_MIB_COUNTER(76, 2, dot3OutPauseFrames), RTL8365MB_MAKE_MIB_COUNTER(78, 2, ifOutDiscards), RTL8365MB_MAKE_MIB_COUNTER(80, 2, dot1dTpPortInDiscards), RTL8365MB_MAKE_MIB_COUNTER(82, 2, ifOutUcastPkts), RTL8365MB_MAKE_MIB_COUNTER(84, 2, ifOutMulticastPkts), RTL8365MB_MAKE_MIB_COUNTER(86, 2, ifOutBroadcastPkts), RTL8365MB_MAKE_MIB_COUNTER(88, 2, outOampduPkts), RTL8365MB_MAKE_MIB_COUNTER(90, 2, inOampduPkts), RTL8365MB_MAKE_MIB_COUNTER(92, 4, inIgmpJoinsSuccess), RTL8365MB_MAKE_MIB_COUNTER(96, 2, inIgmpJoinsFail), RTL8365MB_MAKE_MIB_COUNTER(98, 2, inMldJoinsSuccess), RTL8365MB_MAKE_MIB_COUNTER(100, 2, inMldJoinsFail), RTL8365MB_MAKE_MIB_COUNTER(102, 2, inReportSuppressionDrop), RTL8365MB_MAKE_MIB_COUNTER(104, 2, inLeaveSuppressionDrop), RTL8365MB_MAKE_MIB_COUNTER(106, 2, outIgmpReports), RTL8365MB_MAKE_MIB_COUNTER(108, 2, outIgmpLeaves), RTL8365MB_MAKE_MIB_COUNTER(110, 2, outIgmpGeneralQuery), RTL8365MB_MAKE_MIB_COUNTER(112, 2, outIgmpSpecificQuery), RTL8365MB_MAKE_MIB_COUNTER(114, 2, outMldReports), RTL8365MB_MAKE_MIB_COUNTER(116, 2, outMldLeaves), RTL8365MB_MAKE_MIB_COUNTER(118, 2, outMldGeneralQuery), RTL8365MB_MAKE_MIB_COUNTER(120, 2, outMldSpecificQuery), RTL8365MB_MAKE_MIB_COUNTER(122, 2, inKnownMulticastPkts), }; static_assert(ARRAY_SIZE(rtl8365mb_mib_counters) == RTL8365MB_MIB_END); struct rtl8365mb_jam_tbl_entry { u16 reg; u16 val; }; /* Lifted from the vendor driver sources */ static const struct rtl8365mb_jam_tbl_entry rtl8365mb_init_jam_8365mb_vc[] = { { 0x13EB, 0x15BB }, { 0x1303, 0x06D6 }, { 0x1304, 0x0700 }, { 0x13E2, 0x003F }, { 0x13F9, 0x0090 }, { 0x121E, 0x03CA }, { 0x1233, 0x0352 }, { 0x1237, 0x00A0 }, { 0x123A, 0x0030 }, { 0x1239, 0x0084 }, { 0x0301, 0x1000 }, { 0x1349, 0x001F }, { 0x18E0, 0x4004 }, { 0x122B, 0x241C }, { 0x1305, 0xC000 }, { 0x13F0, 0x0000 }, }; static const struct rtl8365mb_jam_tbl_entry rtl8365mb_init_jam_common[] = { { 0x1200, 0x7FCB }, { 0x0884, 0x0003 }, { 0x06EB, 0x0001 }, { 0x03Fa, 0x0007 }, { 0x08C8, 0x00C0 }, { 0x0A30, 0x020E }, { 0x0800, 0x0000 }, { 0x0802, 0x0000 }, { 0x09DA, 0x0013 }, { 0x1D32, 0x0002 }, }; struct realtek_priv { struct device *dev; struct gpio_desc *reset; struct gpio_desc *mdc; struct gpio_desc *mdio; struct regmap *map; struct regmap *map_nolock; struct mutex map_lock; u32 r_crossread; u32 r_crosswrite; u32 w_crossread; u32 w_crosswrite; u32 read_reg; u32 last_write; struct delayed_work work; unsigned int clk_delay; u8 cmd_read; u8 cmd_write; spinlock_t lock; /* Locks around command writes */ }; static inline void realtek_smi_clk_delay(struct realtek_priv *priv) { ndelay(priv->clk_delay); } static void realtek_smi_start(struct realtek_priv *priv) { /* Set GPIO pins to output mode, with initial state: * SCK = 0, SDA = 1 */ gpiod_direction_output(priv->mdc, 0); gpiod_direction_output(priv->mdio, 1); realtek_smi_clk_delay(priv); /* CLK 1: 0 -> 1, 1 -> 0 */ gpiod_set_value(priv->mdc, 1); realtek_smi_clk_delay(priv); gpiod_set_value(priv->mdc, 0); realtek_smi_clk_delay(priv); /* CLK 2: */ gpiod_set_value(priv->mdc, 1); realtek_smi_clk_delay(priv); gpiod_set_value(priv->mdio, 0); realtek_smi_clk_delay(priv); gpiod_set_value(priv->mdc, 0); realtek_smi_clk_delay(priv); gpiod_set_value(priv->mdio, 1); } static void realtek_smi_stop(struct realtek_priv *priv) { realtek_smi_clk_delay(priv); gpiod_set_value(priv->mdio, 0); gpiod_set_value(priv->mdc, 1); realtek_smi_clk_delay(priv); gpiod_set_value(priv->mdio, 1); realtek_smi_clk_delay(priv); gpiod_set_value(priv->mdc, 1); realtek_smi_clk_delay(priv); gpiod_set_value(priv->mdc, 0); realtek_smi_clk_delay(priv); gpiod_set_value(priv->mdc, 1); /* Add a click */ realtek_smi_clk_delay(priv); gpiod_set_value(priv->mdc, 0); realtek_smi_clk_delay(priv); gpiod_set_value(priv->mdc, 1); /* Set GPIO pins to input mode */ gpiod_direction_input(priv->mdio); gpiod_direction_input(priv->mdc); } static void realtek_smi_write_bits(struct realtek_priv *priv, u32 data, u32 len) { for (; len > 0; len--) { realtek_smi_clk_delay(priv); /* Prepare data */ gpiod_set_value(priv->mdio, !!(data & (1 << (len - 1)))); realtek_smi_clk_delay(priv); /* Clocking */ gpiod_set_value(priv->mdc, 1); realtek_smi_clk_delay(priv); gpiod_set_value(priv->mdc, 0); } } static void realtek_smi_read_bits(struct realtek_priv *priv, u32 len, u32 *data) { gpiod_direction_input(priv->mdio); for (*data = 0; len > 0; len--) { u32 u; realtek_smi_clk_delay(priv); /* Clocking */ gpiod_set_value(priv->mdc, 1); realtek_smi_clk_delay(priv); u = !!gpiod_get_value(priv->mdio); gpiod_set_value(priv->mdc, 0); *data |= (u << (len - 1)); } gpiod_direction_output(priv->mdio, 0); } static int realtek_smi_wait_for_ack(struct realtek_priv *priv) { int retry_cnt; retry_cnt = 0; do { u32 ack; realtek_smi_read_bits(priv, 1, &ack); if (ack == 0) break; if (++retry_cnt > REALTEK_SMI_ACK_RETRY_COUNT) { dev_err(priv->dev, "ACK timeout\n"); return -ETIMEDOUT; } } while (1); return 0; } static int realtek_smi_write_byte(struct realtek_priv *priv, u8 data) { realtek_smi_write_bits(priv, data, 8); return realtek_smi_wait_for_ack(priv); } static int realtek_smi_write_byte_noack(struct realtek_priv *priv, u8 data) { realtek_smi_write_bits(priv, data, 8); return 0; } static int realtek_smi_read_byte0(struct realtek_priv *priv, u8 *data) { u32 t; /* Read data */ realtek_smi_read_bits(priv, 8, &t); *data = (t & 0xff); /* Send an ACK */ realtek_smi_write_bits(priv, 0x00, 1); return 0; } static int realtek_smi_read_byte1(struct realtek_priv *priv, u8 *data) { u32 t; /* Read data */ realtek_smi_read_bits(priv, 8, &t); *data = (t & 0xff); /* Send an ACK */ realtek_smi_write_bits(priv, 0x01, 1); return 0; } static int realtek_smi_read_reg(struct realtek_priv *priv, u32 addr, u32 *data) { unsigned long flags; u8 lo = 0; u8 hi = 0; int ret; spin_lock_irqsave(&priv->lock, flags); realtek_smi_start(priv); /* Send READ command */ ret = realtek_smi_write_byte(priv, priv->cmd_read); if (ret) goto out; /* Set ADDR[7:0] */ ret = realtek_smi_write_byte(priv, addr & 0xff); if (ret) goto out; /* Set ADDR[15:8] */ ret = realtek_smi_write_byte(priv, addr >> 8); if (ret) goto out; /* Read DATA[7:0] */ realtek_smi_read_byte0(priv, &lo); /* Read DATA[15:8] */ realtek_smi_read_byte1(priv, &hi); *data = ((u32)lo) | (((u32)hi) << 8); ret = 0; out: realtek_smi_stop(priv); spin_unlock_irqrestore(&priv->lock, flags); return ret; } static int realtek_smi_write_reg(struct realtek_priv *priv, u32 addr, u32 data, bool ack) { unsigned long flags; int ret; spin_lock_irqsave(&priv->lock, flags); realtek_smi_start(priv); /* Send WRITE command */ ret = realtek_smi_write_byte(priv, priv->cmd_write); if (ret) goto out; /* Set ADDR[7:0] */ ret = realtek_smi_write_byte(priv, addr & 0xff); if (ret) goto out; /* Set ADDR[15:8] */ ret = realtek_smi_write_byte(priv, addr >> 8); if (ret) goto out; /* Write DATA[7:0] */ ret = realtek_smi_write_byte(priv, data & 0xff); if (ret) goto out; /* Write DATA[15:8] */ if (ack) ret = realtek_smi_write_byte(priv, data >> 8); else ret = realtek_smi_write_byte_noack(priv, data >> 8); if (ret) goto out; ret = 0; out: realtek_smi_stop(priv); spin_unlock_irqrestore(&priv->lock, flags); return ret; } /* There is one single case when we need to use this accessor and that * is when issueing soft reset. Since the device reset as soon as we write * that bit, no ACK will come back for natural reasons. */ static int realtek_smi_write_reg_noack(void *ctx, u32 reg, u32 val) { return realtek_smi_write_reg(ctx, reg, val, false); } /* Regmap accessors */ static int realtek_smi_write(void *ctx, u32 reg, u32 val) { struct realtek_priv *priv = ctx; return realtek_smi_write_reg(priv, reg, val, true); } static int realtek_smi_read(void *ctx, u32 reg, u32 *val) { struct realtek_priv *priv = ctx; return realtek_smi_read_reg(priv, reg, val); } static void realtek_smi_lock(void *ctx) { struct realtek_priv *priv = ctx; mutex_lock(&priv->map_lock); } static void realtek_smi_unlock(void *ctx) { struct realtek_priv *priv = ctx; mutex_unlock(&priv->map_lock); } static const struct regmap_config realtek_smi_regmap_config = { .reg_bits = 10, /* A4..A0 R4..R0 */ .val_bits = 16, .reg_stride = 1, /* PHY regs are at 0x8000 */ .max_register = 0xffff, .reg_format_endian = REGMAP_ENDIAN_BIG, .reg_read = realtek_smi_read, .reg_write = realtek_smi_write, .cache_type = REGCACHE_NONE, .lock = realtek_smi_lock, .unlock = realtek_smi_unlock, }; static const struct regmap_config realtek_smi_nolock_regmap_config = { .reg_bits = 10, /* A4..A0 R4..R0 */ .val_bits = 16, .reg_stride = 1, /* PHY regs are at 0x8000 */ .max_register = 0xffff, .reg_format_endian = REGMAP_ENDIAN_BIG, .reg_read = realtek_smi_read, .reg_write = realtek_smi_write, .cache_type = REGCACHE_NONE, .disable_locking = true, }; static u32 last_reg_read[RTL_REG_END] = {}; static bool bad_regs[RTL_REG_END] = {}; static void check_good_regs(struct realtek_priv *priv) { struct device *dev = priv->dev; int ret; int i; int n_checks = 30; int check = 0; int n_good_regs = RTL_REG_END; /* Check over every register we know of and see if its value is subject to change over n_checks. Note that some registers which are defined may be missing in the silicon, in which case reads will fail. Reads silently fail, however, and the value of the last successful read is simply returned. To avoid marking those registers as good - because they cause issues in later sanity checks otherwise - we alrenately read a different value from a known good register beforehand on each iteration. */ for (check = 0; check < n_checks; check++) { /* This will set the CHIP_ID_REG return either 0 or non-0, so that we filter out non-existent regs whose read returns the value of the last successful read */ if (check % 2) { regmap_write(priv->map, RTL8365MB_MAGIC_REG, RTL8365MB_MAGIC_VALUE); } else { regmap_write(priv->map, RTL8365MB_MAGIC_REG, 0); } for (i = 0; i < RTL_REG_END; i++) { u32 val; if (bad_regs[i]) continue; /* read this reg - we alternate its value to avoid read failures showing up as good regs */ regmap_read(priv->map, RTL8365MB_CHIP_ID_REG, &val); ret = regmap_read(priv->map, rtl_regs[i].addr, &val); if (ret) { dev_err(dev, "error reading reg %s (0x%04x): %d\n", rtl_regs[i].name, rtl_regs[i].addr, ret); break; } dev_dbg(dev, "reg %s (0x%04x) = 0x%04x\n", rtl_regs[i].name, rtl_regs[i].addr, val); if (check != 0) { if (val != last_reg_read[i]) { dev_dbg(dev, "read mismatch: reg %s (0x%04x), old=0x%04x, new=0x%04x\n", rtl_regs[i].name, rtl_regs[i].addr, last_reg_read[i], val); bad_regs[i] = true; continue; } } else { last_reg_read[i] = val; } } msleep(100); } regmap_write(priv->map, RTL8365MB_MAGIC_REG, 0); regmap_read(priv->map, RTL8365MB_CHIP_ID_REG, &last_reg_read[RTL_REG_CHIP_NUMBER]); regmap_read(priv->map, RTL8365MB_MAGIC_REG, &last_reg_read[RTL_REG_MAGIC_ID]); bad_regs[RTL_REG_CHIP_NUMBER] = 0; /* we know it's good */ bad_regs[RTL_REG_MAGIC_ID] = 0; /* we know it's good */ for (i = 0; i < RTL_REG_END; i++) { if (bad_regs[i]) { n_good_regs--; dev_dbg(dev, "bad reg %s\n", rtl_regs[i].name); } } dev_err(dev, "n_good_regs = %d\n", n_good_regs); dev_err(dev, "n_bad_regs = %d\n", RTL_REG_END - n_good_regs); } /* Do we initiate a cross{read,write} during PHY READING or PHY WRITING? */ enum rw { READING, WRITING, }; static int cross_read(struct realtek_priv *priv, u32 mask, enum rw rw) { u32 val; int ret; if (rw == READING) { /* only read if enabled for reads */ if (!(mask & priv->r_crossread)) return 0; } else { /* only read if enabled for writes */ if (!(mask & priv->w_crossread)) return 0; } ret = regmap_read(priv->map, rtl_regs[priv->read_reg].addr, &val); if (ret) { dev_err(priv->dev, "crossread fail %s: %d\n", rtl_regs[priv->read_reg].name, ret); return ret; } /* Since we end up doing cross-reads during MIB access (to check it's OK), we can no longer assume that the values of these addresses will stay the same as what was initially read in check_good_regs */ if (val != last_reg_read[priv->read_reg] && priv->read_reg != RTL_REG_MIB_ADDRESS && priv->read_reg != RTL_REG_MIB_CTRL0 && priv->read_reg != RTL_REG_MIB_COUNTER0 && priv->read_reg != RTL_REG_MIB_COUNTER1 && priv->read_reg != RTL_REG_MIB_COUNTER2 && priv->read_reg != RTL_REG_MIB_COUNTER3 ) { dev_err(priv->dev, "crossread mismatch %s: 0x%04x != 0x%04x\n", rtl_regs[priv->read_reg].name, val, last_reg_read[priv->read_reg]); return -1; } return 0; } /* Here, since we cannot assume that all registers will happily be written to without side-effects, we use a simple well-known register to write to. We alternate the value written each time. */ static int cross_write(struct realtek_priv *priv, u32 mask, enum rw rw) { static int A_OR_B_WRITE = 0; /* flip between 0 and 1 to decide what to write */ u32 wval; if (rw == READING) { /* only write if enabled for reads */ if (!(mask & priv->r_crosswrite)) return 0; } else { /* only write if enabled for writes */ if (!(mask & priv->w_crosswrite)) return 0; } if (A_OR_B_WRITE) wval = 0; else wval = RTL8365MB_MAGIC_VALUE; if (regmap_write(priv->map, RTL8365MB_MAGIC_REG, wval)) { dev_err(priv->dev, "crosswrite failed\n"); return -1; } A_OR_B_WRITE = A_OR_B_WRITE ? 0 : 1; priv->last_write = wval; return 0; } #define CROSS_READ(n) if (cross_read(priv, BIT(n), READING)) return 0xFFFF; #define CROSS_WRITE(n) if (cross_write(priv, BIT(n), READING)) return 0xFFFF; #define W_CROSS_READ(n) if (cross_read(priv, BIT(n), WRITING)) return 0xFFFF; #define W_CROSS_WRITE(n) if (cross_write(priv, BIT(n), WRITING)) return 0xFFFF; static int rtl8365mb_mib_counter_read(struct realtek_priv *priv, int port, u32 offset, u32 length, u64 *mibvalue) { u64 tmpvalue = 0; u32 val; int ret; int i; /* The MIB address is an SRAM address. We request a particular address * and then poll the control register before reading the value from some * counter registers. */ ret = regmap_write(priv->map, RTL8365MB_MIB_ADDRESS_REG, RTL8365MB_MIB_ADDRESS(port, offset)); if (ret) return ret; CROSS_READ(0); CROSS_WRITE(0); /* Poll for completion */ while (1) { CROSS_READ(1); CROSS_WRITE(1); regmap_read(priv->map, RTL8365MB_MIB_CTRL0_REG, &val); if (!(val & RTL8365MB_MIB_CTRL0_BUSY_MASK)) break; udelay(10); } CROSS_READ(2); CROSS_WRITE(2); /* Presumably this indicates a MIB counter read failure */ if (val & RTL8365MB_MIB_CTRL0_RESET_MASK) return -EIO; /* There are four MIB counter registers each holding a 16 bit word of a * MIB counter. Depending on the offset, we should read from the upper * two or lower two registers. In case the MIB counter is 4 words, we * read from all four registers. */ if (length == 4) offset = 3; else offset = (offset + 1) % 4; /* Read the MIB counter 16 bits at a time */ for (i = 0; i < length; i++) { CROSS_READ(3); CROSS_WRITE(3); ret = regmap_read(priv->map, RTL8365MB_MIB_COUNTER_REG(offset - i), &val); if (ret) return ret; tmpvalue = ((tmpvalue) << 16) | (val & 0xFFFF); } /* Only commit the result if no error occurred */ *mibvalue = tmpvalue; return 0; } /* Check MIB read at mibidx i is OK - crossreads/writes may occur but the MIB counter should always return 0. */ static int _check_mib_read(struct realtek_priv *priv, int i) { int ret; int port = 0; /* NOTE: hardcoded and assume counters always 0 */ u64 val; struct rtl8365mb_mib_counter *mib = &rtl8365mb_mib_counters[i]; ret = rtl8365mb_mib_counter_read(priv, port, mib->offset, mib->length, &val); if (ret) { dev_err(priv->dev, "failed to read port %d counters: %d\n", port, ret); return -1; } if (val) { dev_err(priv->dev, "mib counter not 0! %llu\n", val); return -1; } return 0; } /* For each register check if reading it at some point during a MIB read will cause a MIB read failure. Every MIB counter is checked. Likewise for writing to a register during a MIB read, although here we only check once because of how the crosswrite is implemented. */ static int check_mib_read(struct realtek_priv *priv) { int mibidx; int bit; u32 reg; for (reg = 0; reg < RTL_REG_END; reg++) { if (bad_regs[reg]) continue; for (bit = 0; bit < 4; bit++) { priv->r_crossread = BIT(bit); priv->read_reg = reg; for (mibidx = 0; mibidx < RTL8365MB_MIB_END; mibidx++) { if (_check_mib_read(priv, mibidx)) return -1; } } priv->r_crossread = 0; } for (bit = 0; bit < 4; bit++) { priv->r_crosswrite = BIT(bit); for (mibidx = 0; mibidx < RTL8365MB_MIB_END; mibidx++) { if (_check_mib_read(priv, mibidx)) return -1; } } priv->r_crosswrite = 0; return 0; } static int rtl8365mb_phy_poll_busy(struct realtek_priv *priv) { u32 val; return regmap_read_poll_timeout(priv->map, RTL8365MB_INDIRECT_ACCESS_STATUS_REG, val, !val, 10, 100); } static int rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy, u32 ocp_addr) { u32 val; int ret; /* Set OCP prefix */ val = FIELD_GET(RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK, ocp_addr); ret = regmap_update_bits( priv->map, RTL8365MB_GPHY_OCP_MSB_0_REG, RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, FIELD_PREP(RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, val)); if (ret) return ret; CROSS_READ(0); CROSS_WRITE(0); /* Set PHY register address */ val = RTL8365MB_PHY_BASE; val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_PHYNUM_MASK, phy); val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_5_1_MASK, (ocp_addr >> 1)&GENMASK(4,0)); val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK, (ocp_addr >> 6)&GENMASK(3,0)); ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG, val); if (ret) return ret; return 0; } /* version for phy_write */ static int W_rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy, u32 ocp_addr) { u32 val; int ret; /* Set OCP prefix */ val = FIELD_GET(RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK, ocp_addr); ret = regmap_update_bits( priv->map, RTL8365MB_GPHY_OCP_MSB_0_REG, RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, FIELD_PREP(RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, val)); if (ret) return ret; W_CROSS_READ(0); W_CROSS_WRITE(0); /* Set PHY register address */ val = RTL8365MB_PHY_BASE; val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_PHYNUM_MASK, phy); val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_5_1_MASK, (ocp_addr >> 1)&GENMASK(4,0)); val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK, (ocp_addr >> 6)&GENMASK(3,0)); ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG, val); if (ret) return ret; return 0; } static int rtl8365mb_phy_ocp_read(struct realtek_priv *priv, int phy, u32 ocp_addr, u16 *data) { u32 val; int ret; CROSS_READ(1); CROSS_WRITE(1); ret = rtl8365mb_phy_poll_busy(priv); if (ret) goto out; CROSS_READ(2); CROSS_WRITE(2); ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); if (ret) goto out; CROSS_READ(3); CROSS_WRITE(3); /* Execute read operation */ val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK, RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) | FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK, RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ); ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, val); if (ret) goto out; CROSS_READ(4); // XXX CROSS_WRITE(4); // XXX ret = rtl8365mb_phy_poll_busy(priv); if (ret) goto out; CROSS_READ(5); // XXX CROSS_WRITE(5); // XXX /* Get PHY register data */ ret = regmap_read(priv->map, RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG, &val); if (ret) goto out; *data = val & 0xFFFF; out: return ret; } static int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy, u32 ocp_addr, u16 data) { u32 val; int ret; W_CROSS_READ(1); W_CROSS_WRITE(1); ret = rtl8365mb_phy_poll_busy(priv); if (ret) goto out; W_CROSS_READ(2); W_CROSS_WRITE(2); ret = W_rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); if (ret) goto out; W_CROSS_READ(3); W_CROSS_WRITE(3); /* Set PHY register data */ ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG, data); if (ret) goto out; W_CROSS_READ(4); W_CROSS_WRITE(4); /* Execute write operation */ val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK, RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) | FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK, RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE); ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, val); if (ret) goto out; W_CROSS_READ(5); W_CROSS_WRITE(5); ret = rtl8365mb_phy_poll_busy(priv); if (ret) goto out; out: return 0; } static int rtl8365mb_phy_read(struct realtek_priv *priv, int phy, int regnum) { u32 ocp_addr; u16 val; int ret; if (phy > RTL8365MB_PHYADDRMAX) return -EINVAL; if (regnum > RTL8365MB_PHYREGMAX) return -EINVAL; ocp_addr = RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE + regnum * 2; ret = rtl8365mb_phy_ocp_read(priv, phy, ocp_addr, &val); if (ret) { dev_err(priv->dev, "failed to read PHY%d reg %02x @ %04x, ret %d\n", phy, regnum, ocp_addr, ret); return ret; } dev_dbg(priv->dev, "read PHY%d register 0x%02x @ %04x, val <- %04x\n", phy, regnum, ocp_addr, val); return val; } static int rtl8365mb_phy_write(struct realtek_priv *priv, int phy, int regnum, u16 val) { u32 ocp_addr; int ret; if (phy > RTL8365MB_PHYADDRMAX) return -EINVAL; if (regnum > RTL8365MB_PHYREGMAX) return -EINVAL; ocp_addr = RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE + regnum * 2; ret = rtl8365mb_phy_ocp_write(priv, phy, ocp_addr, val); if (ret) { dev_err(priv->dev, "failed to write PHY%d reg %02x @ %04x, ret %d\n", phy, regnum, ocp_addr, ret); return ret; } dev_dbg(priv->dev, "write PHY%d register 0x%02x @ %04x, val -> %04x\n", phy, regnum, ocp_addr, val); return 0; } /* For register i, do a PHY read with each possible crossread/crosswrite and check our hypotheses. */ static int check_phy_read(struct realtek_priv *priv, int phynum, u32 i) { struct device *dev = priv->dev; int ret; u32 reg2; u32 reg3; u32 read_bit = 0; int read_bad[6] = {}; int write_bad[6] = {}; reg2 = rtl8365mb_phy_read(priv, phynum, 2); if (!reg2 || reg2 == 0xffff) { dev_err(dev, "reg2 read fail\n"); return -1; } reg3 = rtl8365mb_phy_read(priv, phynum, 3); if (!reg3 || reg3 == 0xffff) { dev_err(dev, "reg3 read fail\n"); return -1; } /* We expect the PHY ID regs to have these values */ if (reg2 != 0x001c || reg3 != 0xc942) { dev_err(dev, "bad initial: reg2=0x%04x reg3=0x%04x\n",reg2,reg3); return -1; } /* check with crossreads */ for (read_bit = 0; read_bit < 6; read_bit++) { u32 r2; u32 r3; priv->r_crossread = BIT(read_bit); priv->read_reg = i; r2 = rtl8365mb_phy_read(priv, phynum, 2); r3 = rtl8365mb_phy_read(priv, phynum, 3); if (r2 != reg2 || r3 != reg3) { read_bad[read_bit] = 1; /* In the event of failure, print an error if the hypothesis of a poisoned read does not hold. Poisoned read means the result of the crossread ends up being returned by the PHY read too. MIB regs are discounted because we manipulate them earlier without updating last_reg_read. */ if (priv->read_reg != RTL_REG_MIB_ADDRESS && priv->read_reg != RTL_REG_MIB_CTRL0 && priv->read_reg != RTL_REG_MIB_COUNTER0 && priv->read_reg != RTL_REG_MIB_COUNTER1 && priv->read_reg != RTL_REG_MIB_COUNTER2 && priv->read_reg != RTL_REG_MIB_COUNTER3) { if (r2 != last_reg_read[priv->read_reg]) dev_err(dev, "r2 = 0x%04x != 0x%04x(swreg read %s)\n", r2, last_reg_read[priv->read_reg], rtl_regs[priv->read_reg].name); if (r3 != last_reg_read[priv->read_reg]) dev_err(dev, "r3 = 0x%04x != 0x%04x(swreg read %s)\n", r3, last_reg_read[priv->read_reg], rtl_regs[priv->read_reg].name); } } } priv->r_crossread = 0;// reset /* Assert that only crossreads @ 4 and 5 are bad. */ if (read_bad[0]) { dev_err(dev, "crossread 0 bad!\n"); return -1; } if (read_bad[1]) { dev_err(dev, "crossread 1 bad!\n"); return -1; } if (read_bad[2]) { dev_err(dev, "crossread 2 bad!\n"); return -1; } if (read_bad[3]) { dev_err(dev, "crossread 3 bad!\n"); return -1; } if (!read_bad[4]) { dev_err(dev, "crossread 4 good?!\n"); return -1; } if (!read_bad[5]) { dev_err(dev, "crossread 5 good?!\n"); return -1; } /* check with crosswrites */ for (read_bit = 0; read_bit < 6; read_bit++) { u32 r2; u32 r3; priv->r_crosswrite = BIT(read_bit); r2 = rtl8365mb_phy_read(priv, phynum, 2); r3 = rtl8365mb_phy_read(priv, phynum, 3); if (r2 != reg2 || r3 != reg3) { write_bad[read_bit] = 1; } } priv->r_crosswrite = 0;// reset /* Assert that only crosswrites @ 4 and 5 are bad. */ if (write_bad[0]) { dev_err(dev, "crosswrite 0 bad!\n"); return -1; } if (write_bad[1]) { dev_err(dev, "crosswrite 1 bad!\n"); return -1; } if (write_bad[2]) { dev_err(dev, "crosswrite 2 bad!\n"); return -1; } if (write_bad[3]) { dev_err(dev, "crosswrite 3 bad!\n"); return -1; } if (!write_bad[4]) { dev_err(dev, "crosswrite 4 good?!\n"); return -1; } if (!write_bad[5]) { dev_err(dev, "crosswrite 5 good?!\n"); return -1; } return 0; } static int _check_phy_write(struct realtek_priv *priv, int phynum) { u32 orig; u32 wval; u32 rval; struct device *dev = priv->dev; orig = rtl8365mb_phy_read(priv, phynum, 0); /* flip all the flippable bits */ wval = orig; wval |= BIT(13); wval &= ~BIT(12); wval |= BIT(10); wval &= ~BIT(8); wval &= BIT(6); rtl8365mb_phy_write(priv, phynum, 0, wval); /* check it got written OK */ rval = rtl8365mb_phy_read(priv, phynum, 0); if (rval != wval) { dev_err(dev, "phy%d rval 0x%04x != wval 0x%04x\n", phynum, rval, wval); return -1; } /* now flip back */ wval = orig; rtl8365mb_phy_write(priv, phynum, 0, wval); /* check it got written OK */ rval = rtl8365mb_phy_read(priv, phynum, 0); if (rval != wval) { dev_err(dev, "phy%d rval 0x%04x != wval 0x%04x\n", phynum, rval, wval); return -1; } return 0; } static int _check_phy_writes(struct realtek_priv *priv) { if (_check_phy_write(priv, 0)) return -1; if (_check_phy_write(priv, 1)) return -1; if (_check_phy_write(priv, 2)) return -1; if (_check_phy_write(priv, 3)) return -1; return 0; } static int check_phy_write(struct realtek_priv *priv) { int count = 0; int bit = 0; for (bit = 0; bit < 6; bit++) { priv->w_crossread = BIT(bit); if (_check_phy_writes(priv)) return -1; } priv->w_crossread = 0; for (bit = 0; bit < 6; bit++) { priv->w_crosswrite = BIT(bit); if (_check_phy_writes(priv)) return -1; } priv->w_crosswrite = 0; return 0; } static void do_reg_work(struct realtek_priv *priv) { struct device *dev = priv->dev; int ret; u32 reg; int n_errors = 0; int n_checks = 3; int check = 0; dev_err(dev, "checking PHY write...\n"); check_phy_write(priv); dev_err(dev, "checking good regs...\n"); check_good_regs(priv); dev_err(dev, "checking MIB read...\n"); check_mib_read(priv); /* For n_checks checks, for each (good) register, perform each possible cross-read/write while doing a PHY read, and make sure that it fails only in the way we expect it to. */ dev_err(dev, "checking PHY read...\n"); for (check = 0; check < n_checks; check++) { int initial_error_count = n_errors; for (reg = 0; reg < RTL_REG_END && n_errors < 5; reg++) { int phynum; if (bad_regs[reg]) continue; for(phynum = 0; phynum < 4 && n_errors < 5; phynum++) { if (check_phy_read(priv, phynum, reg)) { dev_err(dev, "was checking reg %s(0x%04x)\n", rtl_regs[reg].name, rtl_regs[reg].addr); n_errors++; } } } if (n_errors >= 5) { dev_err(dev, "too many errors\n"); return; } if (n_errors == initial_error_count) dev_err(dev, "OK %d/%d\n", check+1, n_checks); } return; } static void reg_work(struct work_struct *work) { struct realtek_priv *priv = container_of(to_delayed_work(work), struct realtek_priv, work); do_reg_work(priv); } static int rtl8365mb_switch_init(struct realtek_priv *priv) { int ret; int i; /* RTL8365MB init jam */ for (i = 0; i < ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc); i++) { ret = regmap_write(priv->map, rtl8365mb_init_jam_8365mb_vc[i].reg, rtl8365mb_init_jam_8365mb_vc[i].val); if (ret) return ret; } /* Common init jam */ for (i = 0; i < ARRAY_SIZE(rtl8365mb_init_jam_common); i++) { ret = regmap_write(priv->map, rtl8365mb_init_jam_common[i].reg, rtl8365mb_init_jam_common[i].val); if (ret) return ret; } return 0; } static int rtl8365mb_reset_chip(struct realtek_priv *priv) { u32 val; realtek_smi_write_reg_noack(priv, RTL8365MB_CHIP_RESET_REG, FIELD_PREP(RTL8365MB_CHIP_RESET_HW_MASK, 1)); /* Realtek documentation says the chip needs 1 second to reset. Sleep * for 100 ms before accessing any registers to prevent ACK timeouts. */ msleep(100); return regmap_read_poll_timeout(priv->map, RTL8365MB_CHIP_RESET_REG, val, !(val & RTL8365MB_CHIP_RESET_HW_MASK), 20000, 1e6); } static int realtek_smi_probe(struct platform_device *pdev) { const struct realtek_variant *var; struct device *dev = &pdev->dev; struct realtek_priv *priv; struct regmap_config rc; struct device_node *np; int ret; var = of_device_get_match_data(dev); np = dev->of_node; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; mutex_init(&priv->map_lock); rc = realtek_smi_regmap_config; rc.lock_arg = priv; priv->map = devm_regmap_init(dev, NULL, priv, &rc); if (IS_ERR(priv->map)) { ret = PTR_ERR(priv->map); dev_err(dev, "regmap init failed: %d\n", ret); return ret; } rc = realtek_smi_nolock_regmap_config; priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); if (IS_ERR(priv->map_nolock)) { ret = PTR_ERR(priv->map_nolock); dev_err(dev, "regmap init failed: %d\n", ret); return ret; } /* Link forward and backward */ priv->dev = dev; priv->clk_delay = 10; priv->cmd_read = 0xb9; priv->cmd_write = 0xb8; dev_set_drvdata(dev, priv); spin_lock_init(&priv->lock); priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(priv->reset)) { dev_err(dev, "failed to get RESET GPIO\n"); return PTR_ERR(priv->reset); } if (priv->reset) { gpiod_set_value(priv->reset, 1); dev_dbg(dev, "asserted RESET\n"); msleep(REALTEK_HW_STOP_DELAY); gpiod_set_value(priv->reset, 0); msleep(REALTEK_HW_START_DELAY); dev_dbg(dev, "deasserted RESET\n"); } /* Fetch MDIO pins */ priv->mdc = devm_gpiod_get_optional(dev, "mdc", GPIOD_OUT_LOW); if (IS_ERR(priv->mdc)) return PTR_ERR(priv->mdc); priv->mdio = devm_gpiod_get_optional(dev, "mdio", GPIOD_OUT_LOW); if (IS_ERR(priv->mdio)) return PTR_ERR(priv->mdio); ret = rtl8365mb_reset_chip(priv); if (ret) { dev_err(priv->dev, "failed to reset chip: %d\n", ret); return ret; } /* Configure switch to vendor-defined initial state */ ret = rtl8365mb_switch_init(priv); if (ret) { dev_err(priv->dev, "failed to initialize switch: %d\n", ret); return ret; } INIT_DELAYED_WORK(&priv->work, reg_work); schedule_delayed_work(&priv->work, HZ); return 0; } static int realtek_smi_remove(struct platform_device *pdev) { struct realtek_priv *priv = platform_get_drvdata(pdev); if (!priv) return 0; cancel_delayed_work_sync(&priv->work); /* leave the device reset asserted */ if (priv->reset) gpiod_set_value(priv->reset, 1); platform_set_drvdata(pdev, NULL); return 0; } static const struct of_device_id realtek_smi_of_match[] = { { .compatible = "realtek,rtl8365mb", }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, realtek_smi_of_match); static struct platform_driver realtek_smi_driver = { .driver = { .name = "rtltest", .of_match_table = of_match_ptr(realtek_smi_of_match), }, .probe = realtek_smi_probe, .remove = realtek_smi_remove, }; module_platform_driver(realtek_smi_driver); MODULE_LICENSE("GPL");