010ca9b68aac75d914a0db6e38d356c9fabc31b8
[openwrt/staging/blogic.git] /
1 From 7a561e9351ae7e3fb1f08584d40b49c1e55dde60 Mon Sep 17 00:00:00 2001
2 From: Samin Guo <samin.guo@starfivetech.com>
3 Date: Thu, 20 Jul 2023 19:15:09 +0800
4 Subject: [PATCH] net: phy: motorcomm: Add pad drive strength cfg support
5
6 The motorcomm phy (YT8531) supports the ability to adjust the drive
7 strength of the rx_clk/rx_data, and the default strength may not be
8 suitable for all boards. So add configurable options to better match
9 the boards.(e.g. StarFive VisionFive 2)
10
11 When we configure the drive strength, we need to read the current
12 LDO voltage value to ensure that it is a legal value at that LDO
13 voltage.
14
15 Reviewed-by: Hal Feng <hal.feng@starfivetech.com>
16 Signed-off-by: Samin Guo <samin.guo@starfivetech.com>
17 Reviewed-by: Andrew Lunn <andrew@lunn.ch>
18 Signed-off-by: David S. Miller <davem@davemloft.net>
19 ---
20 drivers/net/phy/motorcomm.c | 118 ++++++++++++++++++++++++++++++++++++
21 1 file changed, 118 insertions(+)
22
23 --- a/drivers/net/phy/motorcomm.c
24 +++ b/drivers/net/phy/motorcomm.c
25 @@ -163,6 +163,10 @@
26
27 #define YT8521_CHIP_CONFIG_REG 0xA001
28 #define YT8521_CCR_SW_RST BIT(15)
29 +#define YT8531_RGMII_LDO_VOL_MASK GENMASK(5, 4)
30 +#define YT8531_LDO_VOL_3V3 0x0
31 +#define YT8531_LDO_VOL_1V8 0x2
32 +
33 /* 1b0 disable 1.9ns rxc clock delay *default*
34 * 1b1 enable 1.9ns rxc clock delay
35 */
36 @@ -236,6 +240,12 @@
37 */
38 #define YTPHY_WCR_TYPE_PULSE BIT(0)
39
40 +#define YTPHY_PAD_DRIVE_STRENGTH_REG 0xA010
41 +#define YT8531_RGMII_RXC_DS_MASK GENMASK(15, 13)
42 +#define YT8531_RGMII_RXD_DS_HI_MASK BIT(12) /* Bit 2 of rxd_ds */
43 +#define YT8531_RGMII_RXD_DS_LOW_MASK GENMASK(5, 4) /* Bit 1/0 of rxd_ds */
44 +#define YT8531_RGMII_RX_DS_DEFAULT 0x3
45 +
46 #define YTPHY_SYNCE_CFG_REG 0xA012
47 #define YT8521_SCR_SYNCE_ENABLE BIT(5)
48 /* 1b0 output 25m clock
49 @@ -835,6 +845,110 @@ static int ytphy_rgmii_clk_delay_config_
50 }
51
52 /**
53 + * struct ytphy_ldo_vol_map - map a current value to a register value
54 + * @vol: ldo voltage
55 + * @ds: value in the register
56 + * @cur: value in device configuration
57 + */
58 +struct ytphy_ldo_vol_map {
59 + u32 vol;
60 + u32 ds;
61 + u32 cur;
62 +};
63 +
64 +static const struct ytphy_ldo_vol_map yt8531_ldo_vol[] = {
65 + {.vol = YT8531_LDO_VOL_1V8, .ds = 0, .cur = 1200},
66 + {.vol = YT8531_LDO_VOL_1V8, .ds = 1, .cur = 2100},
67 + {.vol = YT8531_LDO_VOL_1V8, .ds = 2, .cur = 2700},
68 + {.vol = YT8531_LDO_VOL_1V8, .ds = 3, .cur = 2910},
69 + {.vol = YT8531_LDO_VOL_1V8, .ds = 4, .cur = 3110},
70 + {.vol = YT8531_LDO_VOL_1V8, .ds = 5, .cur = 3600},
71 + {.vol = YT8531_LDO_VOL_1V8, .ds = 6, .cur = 3970},
72 + {.vol = YT8531_LDO_VOL_1V8, .ds = 7, .cur = 4350},
73 + {.vol = YT8531_LDO_VOL_3V3, .ds = 0, .cur = 3070},
74 + {.vol = YT8531_LDO_VOL_3V3, .ds = 1, .cur = 4080},
75 + {.vol = YT8531_LDO_VOL_3V3, .ds = 2, .cur = 4370},
76 + {.vol = YT8531_LDO_VOL_3V3, .ds = 3, .cur = 4680},
77 + {.vol = YT8531_LDO_VOL_3V3, .ds = 4, .cur = 5020},
78 + {.vol = YT8531_LDO_VOL_3V3, .ds = 5, .cur = 5450},
79 + {.vol = YT8531_LDO_VOL_3V3, .ds = 6, .cur = 5740},
80 + {.vol = YT8531_LDO_VOL_3V3, .ds = 7, .cur = 6140},
81 +};
82 +
83 +static u32 yt8531_get_ldo_vol(struct phy_device *phydev)
84 +{
85 + u32 val;
86 +
87 + val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG);
88 + val = FIELD_GET(YT8531_RGMII_LDO_VOL_MASK, val);
89 +
90 + return val <= YT8531_LDO_VOL_1V8 ? val : YT8531_LDO_VOL_1V8;
91 +}
92 +
93 +static int yt8531_get_ds_map(struct phy_device *phydev, u32 cur)
94 +{
95 + u32 vol;
96 + int i;
97 +
98 + vol = yt8531_get_ldo_vol(phydev);
99 + for (i = 0; i < ARRAY_SIZE(yt8531_ldo_vol); i++) {
100 + if (yt8531_ldo_vol[i].vol == vol && yt8531_ldo_vol[i].cur == cur)
101 + return yt8531_ldo_vol[i].ds;
102 + }
103 +
104 + return -EINVAL;
105 +}
106 +
107 +static int yt8531_set_ds(struct phy_device *phydev)
108 +{
109 + struct device_node *node = phydev->mdio.dev.of_node;
110 + u32 ds_field_low, ds_field_hi, val;
111 + int ret, ds;
112 +
113 + /* set rgmii rx clk driver strength */
114 + if (!of_property_read_u32(node, "motorcomm,rx-clk-drv-microamp", &val)) {
115 + ds = yt8531_get_ds_map(phydev, val);
116 + if (ds < 0)
117 + return dev_err_probe(&phydev->mdio.dev, ds,
118 + "No matching current value was found.\n");
119 + } else {
120 + ds = YT8531_RGMII_RX_DS_DEFAULT;
121 + }
122 +
123 + ret = ytphy_modify_ext_with_lock(phydev,
124 + YTPHY_PAD_DRIVE_STRENGTH_REG,
125 + YT8531_RGMII_RXC_DS_MASK,
126 + FIELD_PREP(YT8531_RGMII_RXC_DS_MASK, ds));
127 + if (ret < 0)
128 + return ret;
129 +
130 + /* set rgmii rx data driver strength */
131 + if (!of_property_read_u32(node, "motorcomm,rx-data-drv-microamp", &val)) {
132 + ds = yt8531_get_ds_map(phydev, val);
133 + if (ds < 0)
134 + return dev_err_probe(&phydev->mdio.dev, ds,
135 + "No matching current value was found.\n");
136 + } else {
137 + ds = YT8531_RGMII_RX_DS_DEFAULT;
138 + }
139 +
140 + ds_field_hi = FIELD_GET(BIT(2), ds);
141 + ds_field_hi = FIELD_PREP(YT8531_RGMII_RXD_DS_HI_MASK, ds_field_hi);
142 +
143 + ds_field_low = FIELD_GET(GENMASK(1, 0), ds);
144 + ds_field_low = FIELD_PREP(YT8531_RGMII_RXD_DS_LOW_MASK, ds_field_low);
145 +
146 + ret = ytphy_modify_ext_with_lock(phydev,
147 + YTPHY_PAD_DRIVE_STRENGTH_REG,
148 + YT8531_RGMII_RXD_DS_LOW_MASK | YT8531_RGMII_RXD_DS_HI_MASK,
149 + ds_field_low | ds_field_hi);
150 + if (ret < 0)
151 + return ret;
152 +
153 + return 0;
154 +}
155 +
156 +/**
157 * yt8521_probe() - read chip config then set suitable polling_mode
158 * @phydev: a pointer to a &struct phy_device
159 *
160 @@ -1518,6 +1632,10 @@ static int yt8531_config_init(struct phy
161 return ret;
162 }
163
164 + ret = yt8531_set_ds(phydev);
165 + if (ret < 0)
166 + return ret;
167 +
168 return 0;
169 }
170