Skip to content

Commit d67d724

Browse files
oleremdavem330
authored andcommitted
net: dsa: microchip: Add drive strength configuration
Add device tree based drive strength configuration support. It is needed to pass EMI validation on our hardware. Configuration values are based on the vendor's reference driver. Tested on KSZ9563R. Signed-off-by: Oleksij Rempel <[email protected]> Reviewed-by: Andrew Lunn <[email protected]> Reviewed-by: Vladimir Oltean <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent e26f40a commit d67d724

File tree

4 files changed

+329
-27
lines changed

4 files changed

+329
-27
lines changed

drivers/net/dsa/microchip/ksz8795_reg.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -442,20 +442,6 @@
442442
#define TOS_PRIO_M KS_PRIO_M
443443
#define TOS_PRIO_S KS_PRIO_S
444444

445-
#define REG_SW_CTRL_20 0xA3
446-
447-
#define SW_GMII_DRIVE_STRENGTH_S 4
448-
#define SW_DRIVE_STRENGTH_M 0x7
449-
#define SW_DRIVE_STRENGTH_2MA 0
450-
#define SW_DRIVE_STRENGTH_4MA 1
451-
#define SW_DRIVE_STRENGTH_8MA 2
452-
#define SW_DRIVE_STRENGTH_12MA 3
453-
#define SW_DRIVE_STRENGTH_16MA 4
454-
#define SW_DRIVE_STRENGTH_20MA 5
455-
#define SW_DRIVE_STRENGTH_24MA 6
456-
#define SW_DRIVE_STRENGTH_28MA 7
457-
#define SW_MII_DRIVE_STRENGTH_S 0
458-
459445
#define REG_SW_CTRL_21 0xA4
460446

461447
#define SW_IPV6_MLD_OPTION BIT(3)

drivers/net/dsa/microchip/ksz9477_reg.h

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -112,19 +112,6 @@
112112

113113
#define REG_SW_IBA_SYNC__1 0x010C
114114

115-
#define REG_SW_IO_STRENGTH__1 0x010D
116-
#define SW_DRIVE_STRENGTH_M 0x7
117-
#define SW_DRIVE_STRENGTH_2MA 0
118-
#define SW_DRIVE_STRENGTH_4MA 1
119-
#define SW_DRIVE_STRENGTH_8MA 2
120-
#define SW_DRIVE_STRENGTH_12MA 3
121-
#define SW_DRIVE_STRENGTH_16MA 4
122-
#define SW_DRIVE_STRENGTH_20MA 5
123-
#define SW_DRIVE_STRENGTH_24MA 6
124-
#define SW_DRIVE_STRENGTH_28MA 7
125-
#define SW_HI_SPEED_DRIVE_STRENGTH_S 4
126-
#define SW_LO_SPEED_DRIVE_STRENGTH_S 0
127-
128115
#define REG_SW_IBA_STATUS__4 0x0110
129116

130117
#define SW_IBA_REQ BIT(31)

drivers/net/dsa/microchip/ksz_common.c

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
189255
static 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+
35333838
int 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;

drivers/net/dsa/microchip/ksz_common.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,26 @@ static inline int is_lan937x(struct ksz_device *dev)
689689
#define KSZ8_LEGAL_PACKET_SIZE 1518
690690
#define KSZ9477_MAX_FRAME_SIZE 9000
691691

692+
#define KSZ8873_REG_GLOBAL_CTRL_12 0x0e
693+
/* Drive Strength of I/O Pad
694+
* 0: 8mA, 1: 16mA
695+
*/
696+
#define KSZ8873_DRIVE_STRENGTH_16MA BIT(6)
697+
698+
#define KSZ8795_REG_SW_CTRL_20 0xa3
699+
#define KSZ9477_REG_SW_IO_STRENGTH 0x010d
700+
#define SW_DRIVE_STRENGTH_M 0x7
701+
#define SW_DRIVE_STRENGTH_2MA 0
702+
#define SW_DRIVE_STRENGTH_4MA 1
703+
#define SW_DRIVE_STRENGTH_8MA 2
704+
#define SW_DRIVE_STRENGTH_12MA 3
705+
#define SW_DRIVE_STRENGTH_16MA 4
706+
#define SW_DRIVE_STRENGTH_20MA 5
707+
#define SW_DRIVE_STRENGTH_24MA 6
708+
#define SW_DRIVE_STRENGTH_28MA 7
709+
#define SW_HI_SPEED_DRIVE_STRENGTH_S 4
710+
#define SW_LO_SPEED_DRIVE_STRENGTH_S 0
711+
692712
#define KSZ9477_REG_PORT_OUT_RATE_0 0x0420
693713
#define KSZ9477_OUT_RATE_NO_LIMIT 0
694714

0 commit comments

Comments
 (0)