.. | .. |
---|
16 | 16 | #include <linux/mfd/core.h> |
---|
17 | 17 | #include <linux/mii.h> |
---|
18 | 18 | #include <linux/netdevice.h> |
---|
| 19 | +#include <linux/nvmem-consumer.h> |
---|
19 | 20 | #include <linux/of_irq.h> |
---|
20 | 21 | #include <linux/phy.h> |
---|
21 | 22 | #include <linux/platform_device.h> |
---|
.. | .. |
---|
24 | 25 | #define RK630_PHY_ID 0x00441400 |
---|
25 | 26 | |
---|
26 | 27 | /* PAGE 0 */ |
---|
| 28 | +#define REG_MMD_ACCESS_CONTROL 0x0d |
---|
| 29 | +#define REG_MMD_ACCESS_DATA_ADDRESS 0x0e |
---|
27 | 30 | #define REG_INTERRUPT_STATUS 0X10 |
---|
28 | 31 | #define REG_INTERRUPT_MASK 0X11 |
---|
29 | 32 | #define REG_GLOBAL_CONFIGURATION 0X13 |
---|
.. | .. |
---|
50 | 53 | #define REG_PAGE6_CP_CURRENT 0x17 |
---|
51 | 54 | #define REG_PAGE6_ADC_OP_BIAS 0x18 |
---|
52 | 55 | #define REG_PAGE6_RX_DECTOR 0x19 |
---|
| 56 | +#define REG_PAGE6_TX_MOS_DRV 0x1B |
---|
53 | 57 | #define REG_PAGE6_AFE_PDCW 0x1c |
---|
54 | 58 | |
---|
55 | 59 | /* PAGE 8 */ |
---|
.. | .. |
---|
61 | 65 | * Addr: 1 --- RK630@S40 |
---|
62 | 66 | * 2 --- RV1106@T22 |
---|
63 | 67 | */ |
---|
64 | | -#define PHY_ADDR_S40 1 |
---|
65 | | -#define PHY_ADDR_T22 2 |
---|
| 68 | +#define PHY_ADDR_S40 1 |
---|
| 69 | +#define PHY_ADDR_T22 2 |
---|
| 70 | + |
---|
| 71 | +#define T22_TX_LEVEL_100M 0x2d |
---|
| 72 | +#define T22_TX_LEVEL_10M 0x32 |
---|
66 | 73 | |
---|
67 | 74 | struct rk630_phy_priv { |
---|
68 | 75 | struct phy_device *phydev; |
---|
69 | 76 | bool ieee; |
---|
70 | 77 | int wol_irq; |
---|
71 | 78 | struct wake_lock wol_wake_lock; |
---|
| 79 | + int tx_level_100M; |
---|
| 80 | + int tx_level_10M; |
---|
72 | 81 | }; |
---|
| 82 | + |
---|
| 83 | +static void rk630_phy_t22_get_tx_level_from_efuse(struct phy_device *phydev) |
---|
| 84 | +{ |
---|
| 85 | + struct rk630_phy_priv *priv = phydev->priv; |
---|
| 86 | + unsigned int tx_level_100M = T22_TX_LEVEL_100M; |
---|
| 87 | + unsigned int tx_level_10M = T22_TX_LEVEL_10M; |
---|
| 88 | + unsigned char *efuse_buf; |
---|
| 89 | + struct nvmem_cell *cell; |
---|
| 90 | + size_t len; |
---|
| 91 | + |
---|
| 92 | + cell = nvmem_cell_get(&phydev->mdio.dev, "txlevel"); |
---|
| 93 | + if (IS_ERR(cell)) { |
---|
| 94 | + phydev_err(phydev, "failed to get txlevel cell: %ld, use default\n", |
---|
| 95 | + PTR_ERR(cell)); |
---|
| 96 | + } else { |
---|
| 97 | + efuse_buf = nvmem_cell_read(cell, &len); |
---|
| 98 | + nvmem_cell_put(cell); |
---|
| 99 | + if (!IS_ERR(efuse_buf)) { |
---|
| 100 | + if (len == 2 && efuse_buf[0] > 0 && efuse_buf[1] > 0) { |
---|
| 101 | + tx_level_100M = efuse_buf[1]; |
---|
| 102 | + tx_level_10M = efuse_buf[0]; |
---|
| 103 | + } |
---|
| 104 | + kfree(efuse_buf); |
---|
| 105 | + } else { |
---|
| 106 | + phydev_err(phydev, "failed to get efuse buf, use default\n"); |
---|
| 107 | + } |
---|
| 108 | + } |
---|
| 109 | + |
---|
| 110 | + priv->tx_level_100M = tx_level_100M; |
---|
| 111 | + priv->tx_level_10M = tx_level_10M; |
---|
| 112 | +} |
---|
73 | 113 | |
---|
74 | 114 | static void rk630_phy_wol_enable(struct phy_device *phydev) |
---|
75 | 115 | { |
---|
.. | .. |
---|
120 | 160 | phy_write(phydev, REG_PAGE_SEL, 0x0000); |
---|
121 | 161 | } |
---|
122 | 162 | |
---|
123 | | -static void rk630_phy_set_uaps(struct phy_device *phydev) |
---|
| 163 | +static void rk630_phy_set_aps(struct phy_device *phydev, bool enable) |
---|
| 164 | +{ |
---|
| 165 | + u32 value; |
---|
| 166 | + |
---|
| 167 | + /* Switch to page 1 */ |
---|
| 168 | + phy_write(phydev, REG_PAGE_SEL, 0x0100); |
---|
| 169 | + value = phy_read(phydev, REG_PAGE1_APS_CTRL); |
---|
| 170 | + if (enable) |
---|
| 171 | + value |= BIT(15); |
---|
| 172 | + else |
---|
| 173 | + value &= ~BIT(15); |
---|
| 174 | + phy_write(phydev, REG_PAGE1_APS_CTRL, value); |
---|
| 175 | + /* Switch to page 0 */ |
---|
| 176 | + phy_write(phydev, REG_PAGE_SEL, 0x0000); |
---|
| 177 | +} |
---|
| 178 | + |
---|
| 179 | +static void rk630_phy_set_uaps(struct phy_device *phydev, bool enable) |
---|
124 | 180 | { |
---|
125 | 181 | u32 value; |
---|
126 | 182 | |
---|
127 | 183 | /* Switch to page 1 */ |
---|
128 | 184 | phy_write(phydev, REG_PAGE_SEL, 0x0100); |
---|
129 | 185 | value = phy_read(phydev, REG_PAGE1_UAPS_CONFIGURE); |
---|
130 | | - value |= BIT(15); |
---|
| 186 | + if (enable) |
---|
| 187 | + value |= BIT(15); |
---|
| 188 | + else |
---|
| 189 | + value &= ~BIT(15); |
---|
131 | 190 | phy_write(phydev, REG_PAGE1_UAPS_CONFIGURE, value); |
---|
132 | 191 | /* Switch to page 0 */ |
---|
133 | 192 | phy_write(phydev, REG_PAGE_SEL, 0x0000); |
---|
.. | .. |
---|
164 | 223 | |
---|
165 | 224 | static void rk630_phy_t22_config_init(struct phy_device *phydev) |
---|
166 | 225 | { |
---|
| 226 | + struct rk630_phy_priv *priv = phydev->priv; |
---|
| 227 | + |
---|
167 | 228 | /* Switch to page 1 */ |
---|
168 | 229 | phy_write(phydev, REG_PAGE_SEL, 0x0100); |
---|
| 230 | + /* Enable offset clock */ |
---|
| 231 | + phy_write(phydev, 0x10, 0xfbfe); |
---|
169 | 232 | /* Disable APS */ |
---|
170 | 233 | phy_write(phydev, REG_PAGE1_APS_CTRL, 0x4824); |
---|
171 | 234 | /* Switch to page 2 */ |
---|
.. | .. |
---|
180 | 243 | phy_write(phydev, REG_PAGE6_GAIN_ANONTROL, 0x0400); |
---|
181 | 244 | /* PHYAFE EQ optimization */ |
---|
182 | 245 | phy_write(phydev, REG_PAGE6_AFE_TX_CTRL, 0x1088); |
---|
| 246 | + |
---|
| 247 | + if (priv->tx_level_100M <= 0 || priv->tx_level_10M <= 0) |
---|
| 248 | + rk630_phy_t22_get_tx_level_from_efuse(phydev); |
---|
| 249 | + |
---|
183 | 250 | /* PHYAFE TX optimization */ |
---|
184 | | - phy_write(phydev, REG_PAGE6_AFE_DRIVER2, 0x3030); |
---|
| 251 | + phy_write(phydev, REG_PAGE6_AFE_DRIVER2, |
---|
| 252 | + (priv->tx_level_100M << 8) | priv->tx_level_10M); |
---|
185 | 253 | /* PHYAFE CP current optimization */ |
---|
186 | 254 | phy_write(phydev, REG_PAGE6_CP_CURRENT, 0x0575); |
---|
187 | 255 | /* ADC OP BIAS optimization */ |
---|
.. | .. |
---|
190 | 258 | phy_write(phydev, REG_PAGE6_RX_DECTOR, 0x0408); |
---|
191 | 259 | /* PHYAFE PDCW optimization */ |
---|
192 | 260 | phy_write(phydev, REG_PAGE6_AFE_PDCW, 0x8880); |
---|
| 261 | + /* Add PHY Tx mos drive, reduce power noise/jitter */ |
---|
| 262 | + phy_write(phydev, REG_PAGE6_TX_MOS_DRV, 0x888e); |
---|
193 | 263 | |
---|
194 | 264 | /* Switch to page 8 */ |
---|
195 | 265 | phy_write(phydev, REG_PAGE_SEL, 0x0800); |
---|
196 | 266 | /* Disable auto-cal */ |
---|
197 | 267 | phy_write(phydev, REG_PAGE8_AUTO_CAL, 0x0844); |
---|
| 268 | + /* Reatart offset calibration */ |
---|
| 269 | + phy_write(phydev, 0x13, 0xc096); |
---|
198 | 270 | |
---|
199 | 271 | /* Switch to page 0 */ |
---|
200 | 272 | phy_write(phydev, REG_PAGE_SEL, 0x0000); |
---|
| 273 | + |
---|
| 274 | + /* Disable eee mode advertised */ |
---|
| 275 | + phy_write(phydev, REG_MMD_ACCESS_CONTROL, 0x0007); |
---|
| 276 | + phy_write(phydev, REG_MMD_ACCESS_DATA_ADDRESS, 0x003c); |
---|
| 277 | + phy_write(phydev, REG_MMD_ACCESS_CONTROL, 0x4007); |
---|
| 278 | + phy_write(phydev, REG_MMD_ACCESS_DATA_ADDRESS, 0x0000); |
---|
201 | 279 | } |
---|
202 | 280 | |
---|
203 | 281 | static int rk630_phy_config_init(struct phy_device *phydev) |
---|
.. | .. |
---|
205 | 283 | switch (phydev->mdio.addr) { |
---|
206 | 284 | case PHY_ADDR_S40: |
---|
207 | 285 | rk630_phy_s40_config_init(phydev); |
---|
| 286 | + /* |
---|
| 287 | + * Ultra Auto-Power Saving Mode (UAPS) is designed to |
---|
| 288 | + * save power when cable is not plugged into PHY. |
---|
| 289 | + */ |
---|
| 290 | + rk630_phy_set_uaps(phydev, true); |
---|
208 | 291 | break; |
---|
209 | 292 | case PHY_ADDR_T22: |
---|
210 | 293 | rk630_phy_t22_config_init(phydev); |
---|
| 294 | + rk630_phy_set_aps(phydev, false); |
---|
| 295 | + rk630_phy_set_uaps(phydev, false); |
---|
211 | 296 | break; |
---|
212 | 297 | default: |
---|
213 | 298 | phydev_err(phydev, "Unsupported address for current phy: %d\n", |
---|
.. | .. |
---|
216 | 301 | } |
---|
217 | 302 | |
---|
218 | 303 | rk630_phy_ieee_set(phydev, true); |
---|
219 | | - /* |
---|
220 | | - * Ultra Auto-Power Saving Mode (UAPS) is designed to |
---|
221 | | - * save power when cable is not plugged into PHY. |
---|
222 | | - */ |
---|
223 | | - rk630_phy_set_uaps(phydev); |
---|
224 | 304 | |
---|
225 | 305 | return 0; |
---|
| 306 | +} |
---|
| 307 | + |
---|
| 308 | +static void rk630_link_change_notify(struct phy_device *phydev) |
---|
| 309 | +{ |
---|
| 310 | + unsigned int val; |
---|
| 311 | + |
---|
| 312 | + if (phydev->state == PHY_RUNNING || phydev->state == PHY_NOLINK) { |
---|
| 313 | + /* Switch to page 6 */ |
---|
| 314 | + phy_write(phydev, REG_PAGE_SEL, 0x0600); |
---|
| 315 | + val = phy_read(phydev, REG_PAGE6_AFE_TX_CTRL); |
---|
| 316 | + val &= ~GENMASK(14, 13); |
---|
| 317 | + if (phydev->speed == SPEED_10 && phydev->link) |
---|
| 318 | + val |= BIT(13); |
---|
| 319 | + phy_write(phydev, REG_PAGE6_AFE_TX_CTRL, val); |
---|
| 320 | + /* Switch to page 0 */ |
---|
| 321 | + phy_write(phydev, REG_PAGE_SEL, 0x0000); |
---|
| 322 | + } |
---|
226 | 323 | } |
---|
227 | 324 | |
---|
228 | 325 | static irqreturn_t rk630_wol_irq_thread(int irq, void *dev_id) |
---|
.. | .. |
---|
310 | 407 | .name = "RK630 PHY", |
---|
311 | 408 | .features = PHY_BASIC_FEATURES, |
---|
312 | 409 | .flags = 0, |
---|
| 410 | + .link_change_notify = rk630_link_change_notify, |
---|
313 | 411 | .probe = rk630_phy_probe, |
---|
314 | 412 | .remove = rk630_phy_remove, |
---|
315 | 413 | .soft_reset = genphy_soft_reset, |
---|
.. | .. |
---|
326 | 424 | { } |
---|
327 | 425 | }; |
---|
328 | 426 | |
---|
329 | | -MODULE_DEVICE_TABLE(mdio, rockchip_phy_tbl); |
---|
| 427 | +MODULE_DEVICE_TABLE(mdio, rk630_phy_tbl); |
---|
330 | 428 | |
---|
331 | 429 | module_phy_driver(rk630_phy_driver); |
---|
332 | 430 | |
---|