|
2 | 2 | /*
|
3 | 3 | * Microchip KSZ9477 switch driver main logic
|
4 | 4 | *
|
5 |
| - * Copyright (C) 2017-2019 Microchip Technology Inc. |
| 5 | + * Copyright (C) 2017-2024 Microchip Technology Inc. |
6 | 6 | */
|
7 | 7 |
|
8 | 8 | #include <linux/kernel.h>
|
@@ -983,26 +983,51 @@ void ksz9477_get_caps(struct ksz_device *dev, int port,
|
983 | 983 | int ksz9477_set_ageing_time(struct ksz_device *dev, unsigned int msecs)
|
984 | 984 | {
|
985 | 985 | u32 secs = msecs / 1000;
|
986 |
| - u8 value; |
987 |
| - u8 data; |
| 986 | + u8 data, mult, value; |
| 987 | + u32 max_val; |
988 | 988 | int ret;
|
989 | 989 |
|
990 |
| - value = FIELD_GET(SW_AGE_PERIOD_7_0_M, secs); |
| 990 | +#define MAX_TIMER_VAL ((1 << 8) - 1) |
991 | 991 |
|
992 |
| - ret = ksz_write8(dev, REG_SW_LUE_CTRL_3, value); |
993 |
| - if (ret < 0) |
994 |
| - return ret; |
| 992 | + /* The aging timer comprises a 3-bit multiplier and an 8-bit second |
| 993 | + * value. Either of them cannot be zero. The maximum timer is then |
| 994 | + * 7 * 255 = 1785 seconds. |
| 995 | + */ |
| 996 | + if (!secs) |
| 997 | + secs = 1; |
995 | 998 |
|
996 |
| - data = FIELD_GET(SW_AGE_PERIOD_10_8_M, secs); |
| 999 | + /* Return error if too large. */ |
| 1000 | + else if (secs > 7 * MAX_TIMER_VAL) |
| 1001 | + return -EINVAL; |
997 | 1002 |
|
998 | 1003 | ret = ksz_read8(dev, REG_SW_LUE_CTRL_0, &value);
|
999 | 1004 | if (ret < 0)
|
1000 | 1005 | return ret;
|
1001 | 1006 |
|
1002 |
| - value &= ~SW_AGE_CNT_M; |
1003 |
| - value |= FIELD_PREP(SW_AGE_CNT_M, data); |
| 1007 | + /* Check whether there is need to update the multiplier. */ |
| 1008 | + mult = FIELD_GET(SW_AGE_CNT_M, value); |
| 1009 | + max_val = MAX_TIMER_VAL; |
| 1010 | + if (mult > 0) { |
| 1011 | + /* Try to use the same multiplier already in the register as |
| 1012 | + * the hardware default uses multiplier 4 and 75 seconds for |
| 1013 | + * 300 seconds. |
| 1014 | + */ |
| 1015 | + max_val = DIV_ROUND_UP(secs, mult); |
| 1016 | + if (max_val > MAX_TIMER_VAL || max_val * mult != secs) |
| 1017 | + max_val = MAX_TIMER_VAL; |
| 1018 | + } |
| 1019 | + |
| 1020 | + data = DIV_ROUND_UP(secs, max_val); |
| 1021 | + if (mult != data) { |
| 1022 | + value &= ~SW_AGE_CNT_M; |
| 1023 | + value |= FIELD_PREP(SW_AGE_CNT_M, data); |
| 1024 | + ret = ksz_write8(dev, REG_SW_LUE_CTRL_0, value); |
| 1025 | + if (ret < 0) |
| 1026 | + return ret; |
| 1027 | + } |
1004 | 1028 |
|
1005 |
| - return ksz_write8(dev, REG_SW_LUE_CTRL_0, value); |
| 1029 | + value = DIV_ROUND_UP(secs, data); |
| 1030 | + return ksz_write8(dev, REG_SW_LUE_CTRL_3, value); |
1006 | 1031 | }
|
1007 | 1032 |
|
1008 | 1033 | void ksz9477_port_queue_split(struct ksz_device *dev, int port)
|
|
0 commit comments