@@ -186,6 +186,72 @@ static const struct ksz_mib_names ksz9477_mib_names[] = {
186186 { 0x83 , "tx_discards" },
187187};
188188
189+ struct ksz_driver_strength_prop {
190+ const char * name ;
191+ int offset ;
192+ int value ;
193+ };
194+
195+ enum ksz_driver_strength_type {
196+ KSZ_DRIVER_STRENGTH_HI ,
197+ KSZ_DRIVER_STRENGTH_LO ,
198+ KSZ_DRIVER_STRENGTH_IO ,
199+ };
200+
201+ /**
202+ * struct ksz_drive_strength - drive strength mapping
203+ * @reg_val: register value
204+ * @microamp: microamp value
205+ */
206+ struct ksz_drive_strength {
207+ u32 reg_val ;
208+ u32 microamp ;
209+ };
210+
211+ /* ksz9477_drive_strengths - Drive strength mapping for KSZ9477 variants
212+ *
213+ * This values are not documented in KSZ9477 variants but confirmed by
214+ * Microchip that KSZ9477, KSZ9567, KSZ8567, KSZ9897, KSZ9896, KSZ9563, KSZ9893
215+ * and KSZ8563 are using same register (drive strength) settings like KSZ8795.
216+ *
217+ * Documentation in KSZ8795CLX provides more information with some
218+ * recommendations:
219+ * - for high speed signals
220+ * 1. 4 mA or 8 mA is often used for MII, RMII, and SPI interface with using
221+ * 2.5V or 3.3V VDDIO.
222+ * 2. 12 mA or 16 mA is often used for MII, RMII, and SPI interface with
223+ * using 1.8V VDDIO.
224+ * 3. 20 mA or 24 mA is often used for GMII/RGMII interface with using 2.5V
225+ * or 3.3V VDDIO.
226+ * 4. 28 mA is often used for GMII/RGMII interface with using 1.8V VDDIO.
227+ * 5. In same interface, the heavy loading should use higher one of the
228+ * drive current strength.
229+ * - for low speed signals
230+ * 1. 3.3V VDDIO, use either 4 mA or 8 mA.
231+ * 2. 2.5V VDDIO, use either 8 mA or 12 mA.
232+ * 3. 1.8V VDDIO, use either 12 mA or 16 mA.
233+ * 4. If it is heavy loading, can use higher drive current strength.
234+ */
235+ static const struct ksz_drive_strength ksz9477_drive_strengths [] = {
236+ { SW_DRIVE_STRENGTH_2MA , 2000 },
237+ { SW_DRIVE_STRENGTH_4MA , 4000 },
238+ { SW_DRIVE_STRENGTH_8MA , 8000 },
239+ { SW_DRIVE_STRENGTH_12MA , 12000 },
240+ { SW_DRIVE_STRENGTH_16MA , 16000 },
241+ { SW_DRIVE_STRENGTH_20MA , 20000 },
242+ { SW_DRIVE_STRENGTH_24MA , 24000 },
243+ { SW_DRIVE_STRENGTH_28MA , 28000 },
244+ };
245+
246+ /* ksz8830_drive_strengths - Drive strength mapping for KSZ8830, KSZ8873, ..
247+ * variants.
248+ * This values are documented in KSZ8873 and KSZ8863 datasheets.
249+ */
250+ static const struct ksz_drive_strength ksz8830_drive_strengths [] = {
251+ { 0 , 8000 },
252+ { KSZ8873_DRIVE_STRENGTH_16MA , 16000 },
253+ };
254+
189255static const struct ksz_dev_ops ksz8_dev_ops = {
190256 .setup = ksz8_setup ,
191257 .get_port_addr = ksz8_get_port_addr ,
@@ -3530,6 +3596,245 @@ static void ksz_parse_rgmii_delay(struct ksz_device *dev, int port_num,
35303596 dev -> ports [port_num ].rgmii_tx_val = tx_delay ;
35313597}
35323598
3599+ /**
3600+ * ksz_drive_strength_to_reg() - Convert drive strength value to corresponding
3601+ * register value.
3602+ * @array: The array of drive strength values to search.
3603+ * @array_size: The size of the array.
3604+ * @microamp: The drive strength value in microamp to be converted.
3605+ *
3606+ * This function searches the array of drive strength values for the given
3607+ * microamp value and returns the corresponding register value for that drive.
3608+ *
3609+ * Returns: If found, the corresponding register value for that drive strength
3610+ * is returned. Otherwise, -EINVAL is returned indicating an invalid value.
3611+ */
3612+ static int ksz_drive_strength_to_reg (const struct ksz_drive_strength * array ,
3613+ size_t array_size , int microamp )
3614+ {
3615+ int i ;
3616+
3617+ for (i = 0 ; i < array_size ; i ++ ) {
3618+ if (array [i ].microamp == microamp )
3619+ return array [i ].reg_val ;
3620+ }
3621+
3622+ return - EINVAL ;
3623+ }
3624+
3625+ /**
3626+ * ksz_drive_strength_error() - Report invalid drive strength value
3627+ * @dev: ksz device
3628+ * @array: The array of drive strength values to search.
3629+ * @array_size: The size of the array.
3630+ * @microamp: Invalid drive strength value in microamp
3631+ *
3632+ * This function logs an error message when an unsupported drive strength value
3633+ * is detected. It lists out all the supported drive strength values for
3634+ * reference in the error message.
3635+ */
3636+ static void ksz_drive_strength_error (struct ksz_device * dev ,
3637+ const struct ksz_drive_strength * array ,
3638+ size_t array_size , int microamp )
3639+ {
3640+ char supported_values [100 ];
3641+ size_t remaining_size ;
3642+ int added_len ;
3643+ char * ptr ;
3644+ int i ;
3645+
3646+ remaining_size = sizeof (supported_values );
3647+ ptr = supported_values ;
3648+
3649+ for (i = 0 ; i < array_size ; i ++ ) {
3650+ added_len = snprintf (ptr , remaining_size ,
3651+ i == 0 ? "%d" : ", %d" , array [i ].microamp );
3652+
3653+ if (added_len >= remaining_size )
3654+ break ;
3655+
3656+ ptr += added_len ;
3657+ remaining_size -= added_len ;
3658+ }
3659+
3660+ dev_err (dev -> dev , "Invalid drive strength %d, supported values are %s\n" ,
3661+ microamp , supported_values );
3662+ }
3663+
3664+ /**
3665+ * ksz9477_drive_strength_write() - Set the drive strength for specific KSZ9477
3666+ * chip variants.
3667+ * @dev: ksz device
3668+ * @props: Array of drive strength properties to be applied
3669+ * @num_props: Number of properties in the array
3670+ *
3671+ * This function configures the drive strength for various KSZ9477 chip variants
3672+ * based on the provided properties. It handles chip-specific nuances and
3673+ * ensures only valid drive strengths are written to the respective chip.
3674+ *
3675+ * Return: 0 on successful configuration, a negative error code on failure.
3676+ */
3677+ static int ksz9477_drive_strength_write (struct ksz_device * dev ,
3678+ struct ksz_driver_strength_prop * props ,
3679+ int num_props )
3680+ {
3681+ size_t array_size = ARRAY_SIZE (ksz9477_drive_strengths );
3682+ int i , ret , reg ;
3683+ u8 mask = 0 ;
3684+ u8 val = 0 ;
3685+
3686+ if (props [KSZ_DRIVER_STRENGTH_IO ].value != -1 )
3687+ dev_warn (dev -> dev , "%s is not supported by this chip variant\n" ,
3688+ props [KSZ_DRIVER_STRENGTH_IO ].name );
3689+
3690+ if (dev -> chip_id == KSZ8795_CHIP_ID ||
3691+ dev -> chip_id == KSZ8794_CHIP_ID ||
3692+ dev -> chip_id == KSZ8765_CHIP_ID )
3693+ reg = KSZ8795_REG_SW_CTRL_20 ;
3694+ else
3695+ reg = KSZ9477_REG_SW_IO_STRENGTH ;
3696+
3697+ for (i = 0 ; i < num_props ; i ++ ) {
3698+ if (props [i ].value == -1 )
3699+ continue ;
3700+
3701+ ret = ksz_drive_strength_to_reg (ksz9477_drive_strengths ,
3702+ array_size , props [i ].value );
3703+ if (ret < 0 ) {
3704+ ksz_drive_strength_error (dev , ksz9477_drive_strengths ,
3705+ array_size , props [i ].value );
3706+ return ret ;
3707+ }
3708+
3709+ mask |= SW_DRIVE_STRENGTH_M << props [i ].offset ;
3710+ val |= ret << props [i ].offset ;
3711+ }
3712+
3713+ return ksz_rmw8 (dev , reg , mask , val );
3714+ }
3715+
3716+ /**
3717+ * ksz8830_drive_strength_write() - Set the drive strength configuration for
3718+ * KSZ8830 compatible chip variants.
3719+ * @dev: ksz device
3720+ * @props: Array of drive strength properties to be set
3721+ * @num_props: Number of properties in the array
3722+ *
3723+ * This function applies the specified drive strength settings to KSZ8830 chip
3724+ * variants (KSZ8873, KSZ8863).
3725+ * It ensures the configurations align with what the chip variant supports and
3726+ * warns or errors out on unsupported settings.
3727+ *
3728+ * Return: 0 on success, error code otherwise
3729+ */
3730+ static int ksz8830_drive_strength_write (struct ksz_device * dev ,
3731+ struct ksz_driver_strength_prop * props ,
3732+ int num_props )
3733+ {
3734+ size_t array_size = ARRAY_SIZE (ksz8830_drive_strengths );
3735+ int microamp ;
3736+ int i , ret ;
3737+
3738+ for (i = 0 ; i < num_props ; i ++ ) {
3739+ if (props [i ].value == -1 || i == KSZ_DRIVER_STRENGTH_IO )
3740+ continue ;
3741+
3742+ dev_warn (dev -> dev , "%s is not supported by this chip variant\n" ,
3743+ props [i ].name );
3744+ }
3745+
3746+ microamp = props [KSZ_DRIVER_STRENGTH_IO ].value ;
3747+ ret = ksz_drive_strength_to_reg (ksz8830_drive_strengths , array_size ,
3748+ microamp );
3749+ if (ret < 0 ) {
3750+ ksz_drive_strength_error (dev , ksz8830_drive_strengths ,
3751+ array_size , microamp );
3752+ return ret ;
3753+ }
3754+
3755+ return ksz_rmw8 (dev , KSZ8873_REG_GLOBAL_CTRL_12 ,
3756+ KSZ8873_DRIVE_STRENGTH_16MA , ret );
3757+ }
3758+
3759+ /**
3760+ * ksz_parse_drive_strength() - Extract and apply drive strength configurations
3761+ * from device tree properties.
3762+ * @dev: ksz device
3763+ *
3764+ * This function reads the specified drive strength properties from the
3765+ * device tree, validates against the supported chip variants, and sets
3766+ * them accordingly. An error should be critical here, as the drive strength
3767+ * settings are crucial for EMI compliance.
3768+ *
3769+ * Return: 0 on success, error code otherwise
3770+ */
3771+ static int ksz_parse_drive_strength (struct ksz_device * dev )
3772+ {
3773+ struct ksz_driver_strength_prop of_props [] = {
3774+ [KSZ_DRIVER_STRENGTH_HI ] = {
3775+ .name = "microchip,hi-drive-strength-microamp" ,
3776+ .offset = SW_HI_SPEED_DRIVE_STRENGTH_S ,
3777+ .value = -1 ,
3778+ },
3779+ [KSZ_DRIVER_STRENGTH_LO ] = {
3780+ .name = "microchip,lo-drive-strength-microamp" ,
3781+ .offset = SW_LO_SPEED_DRIVE_STRENGTH_S ,
3782+ .value = -1 ,
3783+ },
3784+ [KSZ_DRIVER_STRENGTH_IO ] = {
3785+ .name = "microchip,io-drive-strength-microamp" ,
3786+ .offset = 0 , /* don't care */
3787+ .value = -1 ,
3788+ },
3789+ };
3790+ struct device_node * np = dev -> dev -> of_node ;
3791+ bool have_any_prop = false;
3792+ int i , ret ;
3793+
3794+ for (i = 0 ; i < ARRAY_SIZE (of_props ); i ++ ) {
3795+ ret = of_property_read_u32 (np , of_props [i ].name ,
3796+ & of_props [i ].value );
3797+ if (ret && ret != - EINVAL )
3798+ dev_warn (dev -> dev , "Failed to read %s\n" ,
3799+ of_props [i ].name );
3800+ if (ret )
3801+ continue ;
3802+
3803+ have_any_prop = true;
3804+ }
3805+
3806+ if (!have_any_prop )
3807+ return 0 ;
3808+
3809+ switch (dev -> chip_id ) {
3810+ case KSZ8830_CHIP_ID :
3811+ return ksz8830_drive_strength_write (dev , of_props ,
3812+ ARRAY_SIZE (of_props ));
3813+ case KSZ8795_CHIP_ID :
3814+ case KSZ8794_CHIP_ID :
3815+ case KSZ8765_CHIP_ID :
3816+ case KSZ8563_CHIP_ID :
3817+ case KSZ9477_CHIP_ID :
3818+ case KSZ9563_CHIP_ID :
3819+ case KSZ9567_CHIP_ID :
3820+ case KSZ9893_CHIP_ID :
3821+ case KSZ9896_CHIP_ID :
3822+ case KSZ9897_CHIP_ID :
3823+ return ksz9477_drive_strength_write (dev , of_props ,
3824+ ARRAY_SIZE (of_props ));
3825+ default :
3826+ for (i = 0 ; i < ARRAY_SIZE (of_props ); i ++ ) {
3827+ if (of_props [i ].value == -1 )
3828+ continue ;
3829+
3830+ dev_warn (dev -> dev , "%s is not supported by this chip variant\n" ,
3831+ of_props [i ].name );
3832+ }
3833+ }
3834+
3835+ return 0 ;
3836+ }
3837+
35333838int ksz_switch_register (struct ksz_device * dev )
35343839{
35353840 const struct ksz_chip_data * info ;
@@ -3612,6 +3917,10 @@ int ksz_switch_register(struct ksz_device *dev)
36123917 for (port_num = 0 ; port_num < dev -> info -> port_cnt ; ++ port_num )
36133918 dev -> ports [port_num ].interface = PHY_INTERFACE_MODE_NA ;
36143919 if (dev -> dev -> of_node ) {
3920+ ret = ksz_parse_drive_strength (dev );
3921+ if (ret )
3922+ return ret ;
3923+
36153924 ret = of_get_phy_mode (dev -> dev -> of_node , & interface );
36163925 if (ret == 0 )
36173926 dev -> compat_interface = interface ;
0 commit comments