Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'microchip_t1s-update-on-microchip-10base-t1s-phy-driver'

Parthiban Veerasooran says:

====================
microchip_t1s: Update on Microchip 10BASE-T1S PHY driver

This patch series contain the below updates,
- Fixes on the Microchip LAN8670/1/2 10BASE-T1S PHYs support in the
net/phy/microchip_t1s.c driver.
- Adds support for the Microchip LAN8650/1 Rev.B0 10BASE-T1S Internal
PHYs in the net/phy/microchip_t1s.c driver.
====================

Link: https://lore.kernel.org/r/20230526152348.70781-1-Parthiban.Veerasooran@microchip.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

+226 -57
+3 -2
drivers/net/phy/Kconfig
··· 243 243 Supports the KSZ9021, VSC8201, KS8001 PHYs. 244 244 245 245 config MICROCHIP_T1S_PHY 246 - tristate "Microchip 10BASE-T1S Ethernet PHY" 246 + tristate "Microchip 10BASE-T1S Ethernet PHYs" 247 247 help 248 - Currently supports the LAN8670, LAN8671, LAN8672 248 + Currently supports the LAN8670/1/2 Rev.B1 and LAN8650/1 Rev.B0 Internal 249 + PHYs. 249 250 250 251 config MICROCHIP_PHY 251 252 tristate "Microchip PHYs"
+223 -55
drivers/net/phy/microchip_t1s.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0+ 2 2 /* 3 - * Driver for Microchip 10BASE-T1S LAN867X PHY 3 + * Driver for Microchip 10BASE-T1S PHYs 4 4 * 5 5 * Support: Microchip Phys: 6 - * lan8670, lan8671, lan8672 6 + * lan8670/1/2 Rev.B1 7 + * lan8650/1 Rev.B0 Internal PHYs 7 8 */ 8 9 9 10 #include <linux/kernel.h> 10 11 #include <linux/module.h> 11 12 #include <linux/phy.h> 12 13 13 - #define PHY_ID_LAN867X 0x0007C160 14 + #define PHY_ID_LAN867X_REVB1 0x0007C162 15 + #define PHY_ID_LAN865X_REVB0 0x0007C1B3 14 16 15 - #define LAN867X_REG_IRQ_1_CTL 0x001C 16 - #define LAN867X_REG_IRQ_2_CTL 0x001D 17 + #define LAN867X_REG_STS2 0x0019 18 + 19 + #define LAN867x_RESET_COMPLETE_STS BIT(11) 20 + 21 + #define LAN865X_REG_CFGPARAM_ADDR 0x00D8 22 + #define LAN865X_REG_CFGPARAM_DATA 0x00D9 23 + #define LAN865X_REG_CFGPARAM_CTRL 0x00DA 24 + #define LAN865X_REG_STS2 0x0019 25 + 26 + #define LAN865X_CFGPARAM_READ_ENABLE BIT(1) 17 27 18 28 /* The arrays below are pulled from the following table from AN1699 19 29 * Access MMD Address Value Mask ··· 41 31 * W 0x1F 0x0099 0x7F80 ------ 42 32 */ 43 33 44 - static const int lan867x_fixup_registers[12] = { 34 + static const u32 lan867x_revb1_fixup_registers[12] = { 45 35 0x00D0, 0x00D1, 0x0084, 0x0085, 46 36 0x008A, 0x0087, 0x0088, 0x008B, 47 37 0x0080, 0x00F1, 0x0096, 0x0099, 48 38 }; 49 39 50 - static const int lan867x_fixup_values[12] = { 40 + static const u16 lan867x_revb1_fixup_values[12] = { 51 41 0x0002, 0x0000, 0x3380, 0x0006, 52 42 0xC000, 0x801C, 0x033F, 0x0404, 53 43 0x0600, 0x2400, 0x2000, 0x7F80, 54 44 }; 55 45 56 - static const int lan867x_fixup_masks[12] = { 46 + static const u16 lan867x_revb1_fixup_masks[12] = { 57 47 0x0E03, 0x0300, 0xFFC0, 0x000F, 58 48 0xF800, 0x801C, 0x1FFF, 0xFFFF, 59 49 0x0600, 0x7F00, 0x2000, 0xFFFF, 60 50 }; 61 51 62 - static int lan867x_config_init(struct phy_device *phydev) 52 + /* LAN865x Rev.B0 configuration parameters from AN1760 */ 53 + static const u32 lan865x_revb0_fixup_registers[28] = { 54 + 0x0091, 0x0081, 0x0043, 0x0044, 55 + 0x0045, 0x0053, 0x0054, 0x0055, 56 + 0x0040, 0x0050, 0x00D0, 0x00E9, 57 + 0x00F5, 0x00F4, 0x00F8, 0x00F9, 58 + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 59 + 0x00B4, 0x00B5, 0x00B6, 0x00B7, 60 + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 61 + }; 62 + 63 + static const u16 lan865x_revb0_fixup_values[28] = { 64 + 0x9660, 0x00C0, 0x00FF, 0xFFFF, 65 + 0x0000, 0x00FF, 0xFFFF, 0x0000, 66 + 0x0002, 0x0002, 0x5F21, 0x9E50, 67 + 0x1CF8, 0xC020, 0x9B00, 0x4E53, 68 + 0x0103, 0x0910, 0x1D26, 0x002A, 69 + 0x0103, 0x070D, 0x1720, 0x0027, 70 + 0x0509, 0x0E13, 0x1C25, 0x002B, 71 + }; 72 + 73 + static const u16 lan865x_revb0_fixup_cfg_regs[5] = { 74 + 0x0084, 0x008A, 0x00AD, 0x00AE, 0x00AF 75 + }; 76 + 77 + /* Pulled from AN1760 describing 'indirect read' 78 + * 79 + * write_register(0x4, 0x00D8, addr) 80 + * write_register(0x4, 0x00DA, 0x2) 81 + * return (int8)(read_register(0x4, 0x00D9)) 82 + * 83 + * 0x4 refers to memory map selector 4, which maps to MDIO_MMD_VEND2 84 + */ 85 + static int lan865x_revb0_indirect_read(struct phy_device *phydev, u16 addr) 63 86 { 64 - /* HW quirk: Microchip states in the application note (AN1699) for the phy 65 - * that a set of read-modify-write (rmw) operations has to be performed 66 - * on a set of seemingly magic registers. 67 - * The result of these operations is just described as 'optimal performance' 68 - * Microchip gives no explanation as to what these mmd regs do, 69 - * in fact they are marked as reserved in the datasheet. 70 - * It is unclear if phy_modify_mmd would be safe to use or if a write 71 - * really has to happen to each register. 72 - * In order to exactly conform to what is stated in the AN phy_write_mmd is 73 - * used, which might then write the same value back as read + modified. 74 - */ 87 + int ret; 75 88 76 - int reg_value; 89 + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, LAN865X_REG_CFGPARAM_ADDR, 90 + addr); 91 + if (ret) 92 + return ret; 93 + 94 + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, LAN865X_REG_CFGPARAM_CTRL, 95 + LAN865X_CFGPARAM_READ_ENABLE); 96 + if (ret) 97 + return ret; 98 + 99 + return phy_read_mmd(phydev, MDIO_MMD_VEND2, LAN865X_REG_CFGPARAM_DATA); 100 + } 101 + 102 + /* This is pulled straight from AN1760 from 'calculation of offset 1' & 103 + * 'calculation of offset 2' 104 + */ 105 + static int lan865x_generate_cfg_offsets(struct phy_device *phydev, s8 offsets[2]) 106 + { 107 + const u16 fixup_regs[2] = {0x0004, 0x0008}; 108 + int ret; 109 + 110 + for (int i = 0; i < ARRAY_SIZE(fixup_regs); i++) { 111 + ret = lan865x_revb0_indirect_read(phydev, fixup_regs[i]); 112 + if (ret < 0) 113 + return ret; 114 + if (ret & BIT(4)) 115 + offsets[i] = ret | 0xE0; 116 + else 117 + offsets[i] = ret; 118 + } 119 + 120 + return 0; 121 + } 122 + 123 + static int lan865x_read_cfg_params(struct phy_device *phydev, u16 cfg_params[]) 124 + { 125 + int ret; 126 + 127 + for (int i = 0; i < ARRAY_SIZE(lan865x_revb0_fixup_cfg_regs); i++) { 128 + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, 129 + lan865x_revb0_fixup_cfg_regs[i]); 130 + if (ret < 0) 131 + return ret; 132 + cfg_params[i] = (u16)ret; 133 + } 134 + 135 + return 0; 136 + } 137 + 138 + static int lan865x_write_cfg_params(struct phy_device *phydev, u16 cfg_params[]) 139 + { 140 + int ret; 141 + 142 + for (int i = 0; i < ARRAY_SIZE(lan865x_revb0_fixup_cfg_regs); i++) { 143 + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, 144 + lan865x_revb0_fixup_cfg_regs[i], 145 + cfg_params[i]); 146 + if (ret) 147 + return ret; 148 + } 149 + 150 + return 0; 151 + } 152 + 153 + static int lan865x_setup_cfgparam(struct phy_device *phydev) 154 + { 155 + u16 cfg_params[ARRAY_SIZE(lan865x_revb0_fixup_cfg_regs)]; 156 + u16 cfg_results[5]; 157 + s8 offsets[2]; 158 + int ret; 159 + 160 + ret = lan865x_generate_cfg_offsets(phydev, offsets); 161 + if (ret) 162 + return ret; 163 + 164 + ret = lan865x_read_cfg_params(phydev, cfg_params); 165 + if (ret) 166 + return ret; 167 + 168 + cfg_results[0] = (cfg_params[0] & 0x000F) | 169 + FIELD_PREP(GENMASK(15, 10), 9 + offsets[0]) | 170 + FIELD_PREP(GENMASK(15, 4), 14 + offsets[0]); 171 + cfg_results[1] = (cfg_params[1] & 0x03FF) | 172 + FIELD_PREP(GENMASK(15, 10), 40 + offsets[1]); 173 + cfg_results[2] = (cfg_params[2] & 0xC0C0) | 174 + FIELD_PREP(GENMASK(15, 8), 5 + offsets[0]) | 175 + (9 + offsets[0]); 176 + cfg_results[3] = (cfg_params[3] & 0xC0C0) | 177 + FIELD_PREP(GENMASK(15, 8), 9 + offsets[0]) | 178 + (14 + offsets[0]); 179 + cfg_results[4] = (cfg_params[4] & 0xC0C0) | 180 + FIELD_PREP(GENMASK(15, 8), 17 + offsets[0]) | 181 + (22 + offsets[0]); 182 + 183 + return lan865x_write_cfg_params(phydev, cfg_results); 184 + } 185 + 186 + static int lan865x_revb0_config_init(struct phy_device *phydev) 187 + { 188 + int ret; 189 + 190 + /* Reference to AN1760 191 + * https://ww1.microchip.com/downloads/aemDocuments/documents/AIS/ProductDocuments/SupportingCollateral/AN-LAN8650-1-Configuration-60001760.pdf 192 + */ 193 + for (int i = 0; i < ARRAY_SIZE(lan865x_revb0_fixup_registers); i++) { 194 + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, 195 + lan865x_revb0_fixup_registers[i], 196 + lan865x_revb0_fixup_values[i]); 197 + if (ret) 198 + return ret; 199 + } 200 + /* Function to calculate and write the configuration parameters in the 201 + * 0x0084, 0x008A, 0x00AD, 0x00AE and 0x00AF registers (from AN1760) 202 + */ 203 + return lan865x_setup_cfgparam(phydev); 204 + } 205 + 206 + static int lan867x_revb1_config_init(struct phy_device *phydev) 207 + { 77 208 int err; 78 - int reg; 79 209 80 - /* Read-Modified Write Pseudocode (from AN1699) 81 - * current_val = read_register(mmd, addr) // Read current register value 82 - * new_val = current_val AND (NOT mask) // Clear bit fields to be written 83 - * new_val = new_val OR value // Set bits 84 - * write_register(mmd, addr, new_val) // Write back updated register value 210 + /* The chip completes a reset in 3us, we might get here earlier than 211 + * that, as an added margin we'll conditionally sleep 5us. 85 212 */ 86 - for (int i = 0; i < ARRAY_SIZE(lan867x_fixup_registers); i++) { 87 - reg = lan867x_fixup_registers[i]; 88 - reg_value = phy_read_mmd(phydev, MDIO_MMD_VEND2, reg); 89 - reg_value &= ~lan867x_fixup_masks[i]; 90 - reg_value |= lan867x_fixup_values[i]; 91 - err = phy_write_mmd(phydev, MDIO_MMD_VEND2, reg, reg_value); 92 - if (err != 0) 213 + err = phy_read_mmd(phydev, MDIO_MMD_VEND2, LAN867X_REG_STS2); 214 + if (err < 0) 215 + return err; 216 + 217 + if (!(err & LAN867x_RESET_COMPLETE_STS)) { 218 + udelay(5); 219 + err = phy_read_mmd(phydev, MDIO_MMD_VEND2, LAN867X_REG_STS2); 220 + if (err < 0) 221 + return err; 222 + if (!(err & LAN867x_RESET_COMPLETE_STS)) { 223 + phydev_err(phydev, "PHY reset failed\n"); 224 + return -ENODEV; 225 + } 226 + } 227 + 228 + /* Reference to AN1699 229 + * https://ww1.microchip.com/downloads/aemDocuments/documents/AIS/ProductDocuments/SupportingCollateral/AN-LAN8670-1-2-config-60001699.pdf 230 + * AN1699 says Read, Modify, Write, but the Write is not required if the 231 + * register already has the required value. So it is safe to use 232 + * phy_modify_mmd here. 233 + */ 234 + for (int i = 0; i < ARRAY_SIZE(lan867x_revb1_fixup_registers); i++) { 235 + err = phy_modify_mmd(phydev, MDIO_MMD_VEND2, 236 + lan867x_revb1_fixup_registers[i], 237 + lan867x_revb1_fixup_masks[i], 238 + lan867x_revb1_fixup_values[i]); 239 + if (err) 93 240 return err; 94 241 } 95 242 96 - /* None of the interrupts in the lan867x phy seem relevant. 97 - * Other phys inspect the link status and call phy_trigger_machine 98 - * in the interrupt handler. 99 - * This phy does not support link status, and thus has no interrupt 100 - * for it either. 101 - * So we'll just disable all interrupts on the chip. 102 - */ 103 - err = phy_write_mmd(phydev, MDIO_MMD_VEND2, LAN867X_REG_IRQ_1_CTL, 0xFFFF); 104 - if (err != 0) 105 - return err; 106 - return phy_write_mmd(phydev, MDIO_MMD_VEND2, LAN867X_REG_IRQ_2_CTL, 0xFFFF); 243 + return 0; 107 244 } 108 245 109 - static int lan867x_read_status(struct phy_device *phydev) 246 + static int lan86xx_read_status(struct phy_device *phydev) 110 247 { 111 248 /* The phy has some limitations, namely: 112 249 * - always reports link up ··· 268 111 return 0; 269 112 } 270 113 271 - static struct phy_driver lan867x_driver[] = { 114 + static struct phy_driver microchip_t1s_driver[] = { 272 115 { 273 - PHY_ID_MATCH_MODEL(PHY_ID_LAN867X), 274 - .name = "LAN867X", 116 + PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVB1), 117 + .name = "LAN867X Rev.B1", 275 118 .features = PHY_BASIC_T1S_P2MP_FEATURES, 276 - .config_init = lan867x_config_init, 277 - .read_status = lan867x_read_status, 119 + .config_init = lan867x_revb1_config_init, 120 + .read_status = lan86xx_read_status, 278 121 .get_plca_cfg = genphy_c45_plca_get_cfg, 279 122 .set_plca_cfg = genphy_c45_plca_set_cfg, 280 123 .get_plca_status = genphy_c45_plca_get_status, 281 - } 124 + }, 125 + { 126 + PHY_ID_MATCH_EXACT(PHY_ID_LAN865X_REVB0), 127 + .name = "LAN865X Rev.B0 Internal Phy", 128 + .features = PHY_BASIC_T1S_P2MP_FEATURES, 129 + .config_init = lan865x_revb0_config_init, 130 + .read_status = lan86xx_read_status, 131 + .get_plca_cfg = genphy_c45_plca_get_cfg, 132 + .set_plca_cfg = genphy_c45_plca_set_cfg, 133 + .get_plca_status = genphy_c45_plca_get_status, 134 + }, 282 135 }; 283 136 284 - module_phy_driver(lan867x_driver); 137 + module_phy_driver(microchip_t1s_driver); 285 138 286 139 static struct mdio_device_id __maybe_unused tbl[] = { 287 - { PHY_ID_MATCH_MODEL(PHY_ID_LAN867X) }, 140 + { PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVB1) }, 141 + { PHY_ID_MATCH_EXACT(PHY_ID_LAN865X_REVB0) }, 288 142 { } 289 143 }; 290 144 291 145 MODULE_DEVICE_TABLE(mdio, tbl); 292 146 293 - MODULE_DESCRIPTION("Microchip 10BASE-T1S lan867x Phy driver"); 147 + MODULE_DESCRIPTION("Microchip 10BASE-T1S PHYs driver"); 294 148 MODULE_AUTHOR("Ramón Nordin Rodriguez"); 295 149 MODULE_LICENSE("GPL");