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