@@ -186,6 +186,72 @@ static const struct ksz_mib_names ksz9477_mib_names[] = {
186
186
{ 0x83 , "tx_discards" },
187
187
};
188
188
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
+
189
255
static const struct ksz_dev_ops ksz8_dev_ops = {
190
256
.setup = ksz8_setup ,
191
257
.get_port_addr = ksz8_get_port_addr ,
@@ -3530,6 +3596,245 @@ static void ksz_parse_rgmii_delay(struct ksz_device *dev, int port_num,
3530
3596
dev -> ports [port_num ].rgmii_tx_val = tx_delay ;
3531
3597
}
3532
3598
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
+
3533
3838
int ksz_switch_register (struct ksz_device * dev )
3534
3839
{
3535
3840
const struct ksz_chip_data * info ;
@@ -3612,6 +3917,10 @@ int ksz_switch_register(struct ksz_device *dev)
3612
3917
for (port_num = 0 ; port_num < dev -> info -> port_cnt ; ++ port_num )
3613
3918
dev -> ports [port_num ].interface = PHY_INTERFACE_MODE_NA ;
3614
3919
if (dev -> dev -> of_node ) {
3920
+ ret = ksz_parse_drive_strength (dev );
3921
+ if (ret )
3922
+ return ret ;
3923
+
3615
3924
ret = of_get_phy_mode (dev -> dev -> of_node , & interface );
3616
3925
if (ret == 0 )
3617
3926
dev -> compat_interface = interface ;
0 commit comments