Merge branch 'dsa-microchip-drive-strength-support'
Oleksij Rempel says: ==================== net: dsa: microchip: add drive strength support changes v5: - rename milliamp to microamp - do not expect negative error code on snprintf - set coma after last struct element - rename found to have_any_prop changes v4: - integrate microchip feedback to the ksz9477_drive_strengths comment. - add Reviewed-by: Rob Herring <robh@kernel.org> changes v3: - yaml: use enum instead of min/max - do not use snprintf() on overlapping buffer. - unify ksz_drive_strength_to_reg() and ksz_drive_strength_error(). Make it usable for KSZ9477 and KSZ8830 variants. - use ksz_rmw8() in ksz9477_drive_strength_write() ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
50675d84e3
@ -49,6 +49,26 @@ properties:
|
||||
Set if the output SYNCLKO clock should be disabled. Do not mix with
|
||||
microchip,synclko-125.
|
||||
|
||||
microchip,io-drive-strength-microamp:
|
||||
description:
|
||||
IO Pad Drive Strength
|
||||
enum: [8000, 16000]
|
||||
default: 16000
|
||||
|
||||
microchip,hi-drive-strength-microamp:
|
||||
description:
|
||||
High Speed Drive Strength. Controls drive strength of GMII / RGMII /
|
||||
MII / RMII (except TX_CLK/REFCLKI, COL and CRS) and CLKO_25_125 lines.
|
||||
enum: [2000, 4000, 8000, 12000, 16000, 20000, 24000, 28000]
|
||||
default: 24000
|
||||
|
||||
microchip,lo-drive-strength-microamp:
|
||||
description:
|
||||
Low Speed Drive Strength. Controls drive strength of TX_CLK / REFCLKI,
|
||||
COL, CRS, LEDs, PME_N, NTRP_N, SDO and SDI/SDA/MDIO lines.
|
||||
enum: [2000, 4000, 8000, 12000, 16000, 20000, 24000, 28000]
|
||||
default: 8000
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
|
@ -442,20 +442,6 @@
|
||||
#define TOS_PRIO_M KS_PRIO_M
|
||||
#define TOS_PRIO_S KS_PRIO_S
|
||||
|
||||
#define REG_SW_CTRL_20 0xA3
|
||||
|
||||
#define SW_GMII_DRIVE_STRENGTH_S 4
|
||||
#define SW_DRIVE_STRENGTH_M 0x7
|
||||
#define SW_DRIVE_STRENGTH_2MA 0
|
||||
#define SW_DRIVE_STRENGTH_4MA 1
|
||||
#define SW_DRIVE_STRENGTH_8MA 2
|
||||
#define SW_DRIVE_STRENGTH_12MA 3
|
||||
#define SW_DRIVE_STRENGTH_16MA 4
|
||||
#define SW_DRIVE_STRENGTH_20MA 5
|
||||
#define SW_DRIVE_STRENGTH_24MA 6
|
||||
#define SW_DRIVE_STRENGTH_28MA 7
|
||||
#define SW_MII_DRIVE_STRENGTH_S 0
|
||||
|
||||
#define REG_SW_CTRL_21 0xA4
|
||||
|
||||
#define SW_IPV6_MLD_OPTION BIT(3)
|
||||
|
@ -112,19 +112,6 @@
|
||||
|
||||
#define REG_SW_IBA_SYNC__1 0x010C
|
||||
|
||||
#define REG_SW_IO_STRENGTH__1 0x010D
|
||||
#define SW_DRIVE_STRENGTH_M 0x7
|
||||
#define SW_DRIVE_STRENGTH_2MA 0
|
||||
#define SW_DRIVE_STRENGTH_4MA 1
|
||||
#define SW_DRIVE_STRENGTH_8MA 2
|
||||
#define SW_DRIVE_STRENGTH_12MA 3
|
||||
#define SW_DRIVE_STRENGTH_16MA 4
|
||||
#define SW_DRIVE_STRENGTH_20MA 5
|
||||
#define SW_DRIVE_STRENGTH_24MA 6
|
||||
#define SW_DRIVE_STRENGTH_28MA 7
|
||||
#define SW_HI_SPEED_DRIVE_STRENGTH_S 4
|
||||
#define SW_LO_SPEED_DRIVE_STRENGTH_S 0
|
||||
|
||||
#define REG_SW_IBA_STATUS__4 0x0110
|
||||
|
||||
#define SW_IBA_REQ BIT(31)
|
||||
|
@ -186,6 +186,72 @@ static const struct ksz_mib_names ksz9477_mib_names[] = {
|
||||
{ 0x83, "tx_discards" },
|
||||
};
|
||||
|
||||
struct ksz_driver_strength_prop {
|
||||
const char *name;
|
||||
int offset;
|
||||
int value;
|
||||
};
|
||||
|
||||
enum ksz_driver_strength_type {
|
||||
KSZ_DRIVER_STRENGTH_HI,
|
||||
KSZ_DRIVER_STRENGTH_LO,
|
||||
KSZ_DRIVER_STRENGTH_IO,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ksz_drive_strength - drive strength mapping
|
||||
* @reg_val: register value
|
||||
* @microamp: microamp value
|
||||
*/
|
||||
struct ksz_drive_strength {
|
||||
u32 reg_val;
|
||||
u32 microamp;
|
||||
};
|
||||
|
||||
/* ksz9477_drive_strengths - Drive strength mapping for KSZ9477 variants
|
||||
*
|
||||
* This values are not documented in KSZ9477 variants but confirmed by
|
||||
* Microchip that KSZ9477, KSZ9567, KSZ8567, KSZ9897, KSZ9896, KSZ9563, KSZ9893
|
||||
* and KSZ8563 are using same register (drive strength) settings like KSZ8795.
|
||||
*
|
||||
* Documentation in KSZ8795CLX provides more information with some
|
||||
* recommendations:
|
||||
* - for high speed signals
|
||||
* 1. 4 mA or 8 mA is often used for MII, RMII, and SPI interface with using
|
||||
* 2.5V or 3.3V VDDIO.
|
||||
* 2. 12 mA or 16 mA is often used for MII, RMII, and SPI interface with
|
||||
* using 1.8V VDDIO.
|
||||
* 3. 20 mA or 24 mA is often used for GMII/RGMII interface with using 2.5V
|
||||
* or 3.3V VDDIO.
|
||||
* 4. 28 mA is often used for GMII/RGMII interface with using 1.8V VDDIO.
|
||||
* 5. In same interface, the heavy loading should use higher one of the
|
||||
* drive current strength.
|
||||
* - for low speed signals
|
||||
* 1. 3.3V VDDIO, use either 4 mA or 8 mA.
|
||||
* 2. 2.5V VDDIO, use either 8 mA or 12 mA.
|
||||
* 3. 1.8V VDDIO, use either 12 mA or 16 mA.
|
||||
* 4. If it is heavy loading, can use higher drive current strength.
|
||||
*/
|
||||
static const struct ksz_drive_strength ksz9477_drive_strengths[] = {
|
||||
{ SW_DRIVE_STRENGTH_2MA, 2000 },
|
||||
{ SW_DRIVE_STRENGTH_4MA, 4000 },
|
||||
{ SW_DRIVE_STRENGTH_8MA, 8000 },
|
||||
{ SW_DRIVE_STRENGTH_12MA, 12000 },
|
||||
{ SW_DRIVE_STRENGTH_16MA, 16000 },
|
||||
{ SW_DRIVE_STRENGTH_20MA, 20000 },
|
||||
{ SW_DRIVE_STRENGTH_24MA, 24000 },
|
||||
{ SW_DRIVE_STRENGTH_28MA, 28000 },
|
||||
};
|
||||
|
||||
/* ksz8830_drive_strengths - Drive strength mapping for KSZ8830, KSZ8873, ..
|
||||
* variants.
|
||||
* This values are documented in KSZ8873 and KSZ8863 datasheets.
|
||||
*/
|
||||
static const struct ksz_drive_strength ksz8830_drive_strengths[] = {
|
||||
{ 0, 8000 },
|
||||
{ KSZ8873_DRIVE_STRENGTH_16MA, 16000 },
|
||||
};
|
||||
|
||||
static const struct ksz_dev_ops ksz8_dev_ops = {
|
||||
.setup = ksz8_setup,
|
||||
.get_port_addr = ksz8_get_port_addr,
|
||||
@ -3530,6 +3596,245 @@ static void ksz_parse_rgmii_delay(struct ksz_device *dev, int port_num,
|
||||
dev->ports[port_num].rgmii_tx_val = tx_delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksz_drive_strength_to_reg() - Convert drive strength value to corresponding
|
||||
* register value.
|
||||
* @array: The array of drive strength values to search.
|
||||
* @array_size: The size of the array.
|
||||
* @microamp: The drive strength value in microamp to be converted.
|
||||
*
|
||||
* This function searches the array of drive strength values for the given
|
||||
* microamp value and returns the corresponding register value for that drive.
|
||||
*
|
||||
* Returns: If found, the corresponding register value for that drive strength
|
||||
* is returned. Otherwise, -EINVAL is returned indicating an invalid value.
|
||||
*/
|
||||
static int ksz_drive_strength_to_reg(const struct ksz_drive_strength *array,
|
||||
size_t array_size, int microamp)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < array_size; i++) {
|
||||
if (array[i].microamp == microamp)
|
||||
return array[i].reg_val;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksz_drive_strength_error() - Report invalid drive strength value
|
||||
* @dev: ksz device
|
||||
* @array: The array of drive strength values to search.
|
||||
* @array_size: The size of the array.
|
||||
* @microamp: Invalid drive strength value in microamp
|
||||
*
|
||||
* This function logs an error message when an unsupported drive strength value
|
||||
* is detected. It lists out all the supported drive strength values for
|
||||
* reference in the error message.
|
||||
*/
|
||||
static void ksz_drive_strength_error(struct ksz_device *dev,
|
||||
const struct ksz_drive_strength *array,
|
||||
size_t array_size, int microamp)
|
||||
{
|
||||
char supported_values[100];
|
||||
size_t remaining_size;
|
||||
int added_len;
|
||||
char *ptr;
|
||||
int i;
|
||||
|
||||
remaining_size = sizeof(supported_values);
|
||||
ptr = supported_values;
|
||||
|
||||
for (i = 0; i < array_size; i++) {
|
||||
added_len = snprintf(ptr, remaining_size,
|
||||
i == 0 ? "%d" : ", %d", array[i].microamp);
|
||||
|
||||
if (added_len >= remaining_size)
|
||||
break;
|
||||
|
||||
ptr += added_len;
|
||||
remaining_size -= added_len;
|
||||
}
|
||||
|
||||
dev_err(dev->dev, "Invalid drive strength %d, supported values are %s\n",
|
||||
microamp, supported_values);
|
||||
}
|
||||
|
||||
/**
|
||||
* ksz9477_drive_strength_write() - Set the drive strength for specific KSZ9477
|
||||
* chip variants.
|
||||
* @dev: ksz device
|
||||
* @props: Array of drive strength properties to be applied
|
||||
* @num_props: Number of properties in the array
|
||||
*
|
||||
* This function configures the drive strength for various KSZ9477 chip variants
|
||||
* based on the provided properties. It handles chip-specific nuances and
|
||||
* ensures only valid drive strengths are written to the respective chip.
|
||||
*
|
||||
* Return: 0 on successful configuration, a negative error code on failure.
|
||||
*/
|
||||
static int ksz9477_drive_strength_write(struct ksz_device *dev,
|
||||
struct ksz_driver_strength_prop *props,
|
||||
int num_props)
|
||||
{
|
||||
size_t array_size = ARRAY_SIZE(ksz9477_drive_strengths);
|
||||
int i, ret, reg;
|
||||
u8 mask = 0;
|
||||
u8 val = 0;
|
||||
|
||||
if (props[KSZ_DRIVER_STRENGTH_IO].value != -1)
|
||||
dev_warn(dev->dev, "%s is not supported by this chip variant\n",
|
||||
props[KSZ_DRIVER_STRENGTH_IO].name);
|
||||
|
||||
if (dev->chip_id == KSZ8795_CHIP_ID ||
|
||||
dev->chip_id == KSZ8794_CHIP_ID ||
|
||||
dev->chip_id == KSZ8765_CHIP_ID)
|
||||
reg = KSZ8795_REG_SW_CTRL_20;
|
||||
else
|
||||
reg = KSZ9477_REG_SW_IO_STRENGTH;
|
||||
|
||||
for (i = 0; i < num_props; i++) {
|
||||
if (props[i].value == -1)
|
||||
continue;
|
||||
|
||||
ret = ksz_drive_strength_to_reg(ksz9477_drive_strengths,
|
||||
array_size, props[i].value);
|
||||
if (ret < 0) {
|
||||
ksz_drive_strength_error(dev, ksz9477_drive_strengths,
|
||||
array_size, props[i].value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mask |= SW_DRIVE_STRENGTH_M << props[i].offset;
|
||||
val |= ret << props[i].offset;
|
||||
}
|
||||
|
||||
return ksz_rmw8(dev, reg, mask, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* ksz8830_drive_strength_write() - Set the drive strength configuration for
|
||||
* KSZ8830 compatible chip variants.
|
||||
* @dev: ksz device
|
||||
* @props: Array of drive strength properties to be set
|
||||
* @num_props: Number of properties in the array
|
||||
*
|
||||
* This function applies the specified drive strength settings to KSZ8830 chip
|
||||
* variants (KSZ8873, KSZ8863).
|
||||
* It ensures the configurations align with what the chip variant supports and
|
||||
* warns or errors out on unsupported settings.
|
||||
*
|
||||
* Return: 0 on success, error code otherwise
|
||||
*/
|
||||
static int ksz8830_drive_strength_write(struct ksz_device *dev,
|
||||
struct ksz_driver_strength_prop *props,
|
||||
int num_props)
|
||||
{
|
||||
size_t array_size = ARRAY_SIZE(ksz8830_drive_strengths);
|
||||
int microamp;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < num_props; i++) {
|
||||
if (props[i].value == -1 || i == KSZ_DRIVER_STRENGTH_IO)
|
||||
continue;
|
||||
|
||||
dev_warn(dev->dev, "%s is not supported by this chip variant\n",
|
||||
props[i].name);
|
||||
}
|
||||
|
||||
microamp = props[KSZ_DRIVER_STRENGTH_IO].value;
|
||||
ret = ksz_drive_strength_to_reg(ksz8830_drive_strengths, array_size,
|
||||
microamp);
|
||||
if (ret < 0) {
|
||||
ksz_drive_strength_error(dev, ksz8830_drive_strengths,
|
||||
array_size, microamp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ksz_rmw8(dev, KSZ8873_REG_GLOBAL_CTRL_12,
|
||||
KSZ8873_DRIVE_STRENGTH_16MA, ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* ksz_parse_drive_strength() - Extract and apply drive strength configurations
|
||||
* from device tree properties.
|
||||
* @dev: ksz device
|
||||
*
|
||||
* This function reads the specified drive strength properties from the
|
||||
* device tree, validates against the supported chip variants, and sets
|
||||
* them accordingly. An error should be critical here, as the drive strength
|
||||
* settings are crucial for EMI compliance.
|
||||
*
|
||||
* Return: 0 on success, error code otherwise
|
||||
*/
|
||||
static int ksz_parse_drive_strength(struct ksz_device *dev)
|
||||
{
|
||||
struct ksz_driver_strength_prop of_props[] = {
|
||||
[KSZ_DRIVER_STRENGTH_HI] = {
|
||||
.name = "microchip,hi-drive-strength-microamp",
|
||||
.offset = SW_HI_SPEED_DRIVE_STRENGTH_S,
|
||||
.value = -1,
|
||||
},
|
||||
[KSZ_DRIVER_STRENGTH_LO] = {
|
||||
.name = "microchip,lo-drive-strength-microamp",
|
||||
.offset = SW_LO_SPEED_DRIVE_STRENGTH_S,
|
||||
.value = -1,
|
||||
},
|
||||
[KSZ_DRIVER_STRENGTH_IO] = {
|
||||
.name = "microchip,io-drive-strength-microamp",
|
||||
.offset = 0, /* don't care */
|
||||
.value = -1,
|
||||
},
|
||||
};
|
||||
struct device_node *np = dev->dev->of_node;
|
||||
bool have_any_prop = false;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(of_props); i++) {
|
||||
ret = of_property_read_u32(np, of_props[i].name,
|
||||
&of_props[i].value);
|
||||
if (ret && ret != -EINVAL)
|
||||
dev_warn(dev->dev, "Failed to read %s\n",
|
||||
of_props[i].name);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
have_any_prop = true;
|
||||
}
|
||||
|
||||
if (!have_any_prop)
|
||||
return 0;
|
||||
|
||||
switch (dev->chip_id) {
|
||||
case KSZ8830_CHIP_ID:
|
||||
return ksz8830_drive_strength_write(dev, of_props,
|
||||
ARRAY_SIZE(of_props));
|
||||
case KSZ8795_CHIP_ID:
|
||||
case KSZ8794_CHIP_ID:
|
||||
case KSZ8765_CHIP_ID:
|
||||
case KSZ8563_CHIP_ID:
|
||||
case KSZ9477_CHIP_ID:
|
||||
case KSZ9563_CHIP_ID:
|
||||
case KSZ9567_CHIP_ID:
|
||||
case KSZ9893_CHIP_ID:
|
||||
case KSZ9896_CHIP_ID:
|
||||
case KSZ9897_CHIP_ID:
|
||||
return ksz9477_drive_strength_write(dev, of_props,
|
||||
ARRAY_SIZE(of_props));
|
||||
default:
|
||||
for (i = 0; i < ARRAY_SIZE(of_props); i++) {
|
||||
if (of_props[i].value == -1)
|
||||
continue;
|
||||
|
||||
dev_warn(dev->dev, "%s is not supported by this chip variant\n",
|
||||
of_props[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksz_switch_register(struct ksz_device *dev)
|
||||
{
|
||||
const struct ksz_chip_data *info;
|
||||
@ -3612,6 +3917,10 @@ int ksz_switch_register(struct ksz_device *dev)
|
||||
for (port_num = 0; port_num < dev->info->port_cnt; ++port_num)
|
||||
dev->ports[port_num].interface = PHY_INTERFACE_MODE_NA;
|
||||
if (dev->dev->of_node) {
|
||||
ret = ksz_parse_drive_strength(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_get_phy_mode(dev->dev->of_node, &interface);
|
||||
if (ret == 0)
|
||||
dev->compat_interface = interface;
|
||||
|
@ -689,6 +689,26 @@ static inline int is_lan937x(struct ksz_device *dev)
|
||||
#define KSZ8_LEGAL_PACKET_SIZE 1518
|
||||
#define KSZ9477_MAX_FRAME_SIZE 9000
|
||||
|
||||
#define KSZ8873_REG_GLOBAL_CTRL_12 0x0e
|
||||
/* Drive Strength of I/O Pad
|
||||
* 0: 8mA, 1: 16mA
|
||||
*/
|
||||
#define KSZ8873_DRIVE_STRENGTH_16MA BIT(6)
|
||||
|
||||
#define KSZ8795_REG_SW_CTRL_20 0xa3
|
||||
#define KSZ9477_REG_SW_IO_STRENGTH 0x010d
|
||||
#define SW_DRIVE_STRENGTH_M 0x7
|
||||
#define SW_DRIVE_STRENGTH_2MA 0
|
||||
#define SW_DRIVE_STRENGTH_4MA 1
|
||||
#define SW_DRIVE_STRENGTH_8MA 2
|
||||
#define SW_DRIVE_STRENGTH_12MA 3
|
||||
#define SW_DRIVE_STRENGTH_16MA 4
|
||||
#define SW_DRIVE_STRENGTH_20MA 5
|
||||
#define SW_DRIVE_STRENGTH_24MA 6
|
||||
#define SW_DRIVE_STRENGTH_28MA 7
|
||||
#define SW_HI_SPEED_DRIVE_STRENGTH_S 4
|
||||
#define SW_LO_SPEED_DRIVE_STRENGTH_S 0
|
||||
|
||||
#define KSZ9477_REG_PORT_OUT_RATE_0 0x0420
|
||||
#define KSZ9477_OUT_RATE_NO_LIMIT 0
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user