]>
Commit | Line | Data |
---|---|---|
a2443fd1 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
00db8189 AF |
2 | /* |
3 | * drivers/net/phy/marvell.c | |
4 | * | |
5 | * Driver for Marvell PHYs | |
6 | * | |
7 | * Author: Andy Fleming | |
8 | * | |
9 | * Copyright (c) 2004 Freescale Semiconductor, Inc. | |
10 | * | |
3871c387 | 11 | * Copyright (c) 2013 Michael Stapelberg <michael@stapelberg.de> |
00db8189 | 12 | */ |
00db8189 | 13 | #include <linux/kernel.h> |
00db8189 | 14 | #include <linux/string.h> |
0b04680f | 15 | #include <linux/ctype.h> |
00db8189 AF |
16 | #include <linux/errno.h> |
17 | #include <linux/unistd.h> | |
0b04680f | 18 | #include <linux/hwmon.h> |
00db8189 AF |
19 | #include <linux/interrupt.h> |
20 | #include <linux/init.h> | |
21 | #include <linux/delay.h> | |
22 | #include <linux/netdevice.h> | |
23 | #include <linux/etherdevice.h> | |
24 | #include <linux/skbuff.h> | |
25 | #include <linux/spinlock.h> | |
26 | #include <linux/mm.h> | |
27 | #include <linux/module.h> | |
00db8189 AF |
28 | #include <linux/mii.h> |
29 | #include <linux/ethtool.h> | |
30 | #include <linux/phy.h> | |
2f495c39 | 31 | #include <linux/marvell_phy.h> |
69f42be8 | 32 | #include <linux/bitfield.h> |
cf41a51d | 33 | #include <linux/of.h> |
00db8189 | 34 | |
eea3b201 | 35 | #include <linux/io.h> |
00db8189 | 36 | #include <asm/irq.h> |
eea3b201 | 37 | #include <linux/uaccess.h> |
00db8189 | 38 | |
27d916d6 | 39 | #define MII_MARVELL_PHY_PAGE 22 |
52295666 AL |
40 | #define MII_MARVELL_COPPER_PAGE 0x00 |
41 | #define MII_MARVELL_FIBER_PAGE 0x01 | |
42 | #define MII_MARVELL_MSCR_PAGE 0x02 | |
43 | #define MII_MARVELL_LED_PAGE 0x03 | |
44 | #define MII_MARVELL_MISC_TEST_PAGE 0x06 | |
45 | #define MII_MARVELL_WOL_PAGE 0x11 | |
27d916d6 | 46 | |
00db8189 AF |
47 | #define MII_M1011_IEVENT 0x13 |
48 | #define MII_M1011_IEVENT_CLEAR 0x0000 | |
49 | ||
50 | #define MII_M1011_IMASK 0x12 | |
51 | #define MII_M1011_IMASK_INIT 0x6400 | |
52 | #define MII_M1011_IMASK_CLEAR 0x0000 | |
53 | ||
fecd5e91 AL |
54 | #define MII_M1011_PHY_SCR 0x10 |
55 | #define MII_M1011_PHY_SCR_DOWNSHIFT_EN BIT(11) | |
f8d975be | 56 | #define MII_M1011_PHY_SCR_DOWNSHIFT_MASK GENMASK(14, 12) |
a3bdfce7 | 57 | #define MII_M1011_PHY_SCR_DOWNSHIFT_MAX 8 |
fecd5e91 AL |
58 | #define MII_M1011_PHY_SCR_MDI (0x0 << 5) |
59 | #define MII_M1011_PHY_SCR_MDI_X (0x1 << 5) | |
60 | #define MII_M1011_PHY_SCR_AUTO_CROSS (0x3 << 5) | |
76884679 | 61 | |
a3bdfce7 HK |
62 | #define MII_M1011_PHY_SSR 0x11 |
63 | #define MII_M1011_PHY_SSR_DOWNSHIFT BIT(5) | |
64 | ||
76884679 AF |
65 | #define MII_M1111_PHY_LED_CONTROL 0x18 |
66 | #define MII_M1111_PHY_LED_DIRECT 0x4100 | |
67 | #define MII_M1111_PHY_LED_COMBINE 0x411c | |
895ee682 | 68 | #define MII_M1111_PHY_EXT_CR 0x14 |
5c6bc519 HK |
69 | #define MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK GENMASK(11, 9) |
70 | #define MII_M1111_PHY_EXT_CR_DOWNSHIFT_MAX 8 | |
71 | #define MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN BIT(8) | |
61111598 AL |
72 | #define MII_M1111_RGMII_RX_DELAY BIT(7) |
73 | #define MII_M1111_RGMII_TX_DELAY BIT(1) | |
895ee682 | 74 | #define MII_M1111_PHY_EXT_SR 0x1b |
be937f1f AS |
75 | |
76 | #define MII_M1111_HWCFG_MODE_MASK 0xf | |
be937f1f | 77 | #define MII_M1111_HWCFG_MODE_FIBER_RGMII 0x3 |
4117b5be | 78 | #define MII_M1111_HWCFG_MODE_SGMII_NO_CLK 0x4 |
865b813a | 79 | #define MII_M1111_HWCFG_MODE_RTBI 0x7 |
5f8cbc13 | 80 | #define MII_M1111_HWCFG_MODE_COPPER_RTBI 0x9 |
865b813a AL |
81 | #define MII_M1111_HWCFG_MODE_COPPER_RGMII 0xb |
82 | #define MII_M1111_HWCFG_FIBER_COPPER_RES BIT(13) | |
83 | #define MII_M1111_HWCFG_FIBER_COPPER_AUTO BIT(15) | |
be937f1f | 84 | |
c477d044 CC |
85 | #define MII_88E1121_PHY_MSCR_REG 21 |
86 | #define MII_88E1121_PHY_MSCR_RX_DELAY BIT(5) | |
87 | #define MII_88E1121_PHY_MSCR_TX_DELAY BIT(4) | |
424ca4c5 | 88 | #define MII_88E1121_PHY_MSCR_DELAY_MASK (BIT(5) | BIT(4)) |
c477d044 | 89 | |
0b04680f AL |
90 | #define MII_88E1121_MISC_TEST 0x1a |
91 | #define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK 0x1f00 | |
92 | #define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT 8 | |
93 | #define MII_88E1510_MISC_TEST_TEMP_IRQ_EN BIT(7) | |
94 | #define MII_88E1510_MISC_TEST_TEMP_IRQ BIT(6) | |
95 | #define MII_88E1121_MISC_TEST_TEMP_SENSOR_EN BIT(5) | |
96 | #define MII_88E1121_MISC_TEST_TEMP_MASK 0x1f | |
97 | ||
98 | #define MII_88E1510_TEMP_SENSOR 0x1b | |
99 | #define MII_88E1510_TEMP_SENSOR_MASK 0xff | |
100 | ||
69f42be8 HK |
101 | #define MII_88E1540_COPPER_CTRL3 0x1a |
102 | #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK GENMASK(11, 10) | |
103 | #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS 0 | |
104 | #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS 1 | |
105 | #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS 2 | |
106 | #define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS 3 | |
107 | #define MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN BIT(9) | |
108 | ||
fee2d546 AL |
109 | #define MII_88E6390_MISC_TEST 0x1b |
110 | #define MII_88E6390_MISC_TEST_SAMPLE_1S 0 | |
111 | #define MII_88E6390_MISC_TEST_SAMPLE_10MS BIT(14) | |
112 | #define MII_88E6390_MISC_TEST_SAMPLE_DISABLE BIT(15) | |
113 | #define MII_88E6390_MISC_TEST_SAMPLE_ENABLE 0 | |
114 | #define MII_88E6390_MISC_TEST_SAMPLE_MASK (0x3 << 14) | |
115 | ||
116 | #define MII_88E6390_TEMP_SENSOR 0x1c | |
117 | #define MII_88E6390_TEMP_SENSOR_MASK 0xff | |
118 | #define MII_88E6390_TEMP_SENSOR_SAMPLES 10 | |
119 | ||
337ac9d5 CC |
120 | #define MII_88E1318S_PHY_MSCR1_REG 16 |
121 | #define MII_88E1318S_PHY_MSCR1_PAD_ODD BIT(6) | |
3ff1c259 | 122 | |
3871c387 | 123 | /* Copper Specific Interrupt Enable Register */ |
8cf8b87b | 124 | #define MII_88E1318S_PHY_CSIER 0x12 |
3871c387 | 125 | /* WOL Event Interrupt Enable */ |
8cf8b87b | 126 | #define MII_88E1318S_PHY_CSIER_WOL_EIE BIT(7) |
3871c387 MS |
127 | |
128 | /* LED Timer Control Register */ | |
8cf8b87b AL |
129 | #define MII_88E1318S_PHY_LED_TCR 0x12 |
130 | #define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15) | |
131 | #define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7) | |
132 | #define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW BIT(11) | |
3871c387 MS |
133 | |
134 | /* Magic Packet MAC address registers */ | |
8cf8b87b AL |
135 | #define MII_88E1318S_PHY_MAGIC_PACKET_WORD2 0x17 |
136 | #define MII_88E1318S_PHY_MAGIC_PACKET_WORD1 0x18 | |
137 | #define MII_88E1318S_PHY_MAGIC_PACKET_WORD0 0x19 | |
3871c387 | 138 | |
8cf8b87b AL |
139 | #define MII_88E1318S_PHY_WOL_CTRL 0x10 |
140 | #define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS BIT(12) | |
141 | #define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE BIT(14) | |
3871c387 | 142 | |
07777246 | 143 | #define MII_PHY_LED_CTRL 16 |
140bc929 | 144 | #define MII_88E1121_PHY_LED_DEF 0x0030 |
07777246 | 145 | #define MII_88E1510_PHY_LED_DEF 0x1177 |
a93f7fe1 | 146 | #define MII_88E1510_PHY_LED0_LINK_LED1_ACTIVE 0x1040 |
140bc929 | 147 | |
be937f1f AS |
148 | #define MII_M1011_PHY_STATUS 0x11 |
149 | #define MII_M1011_PHY_STATUS_1000 0x8000 | |
150 | #define MII_M1011_PHY_STATUS_100 0x4000 | |
151 | #define MII_M1011_PHY_STATUS_SPD_MASK 0xc000 | |
152 | #define MII_M1011_PHY_STATUS_FULLDUPLEX 0x2000 | |
153 | #define MII_M1011_PHY_STATUS_RESOLVED 0x0800 | |
154 | #define MII_M1011_PHY_STATUS_LINK 0x0400 | |
155 | ||
6b358aed SH |
156 | #define MII_88E3016_PHY_SPEC_CTRL 0x10 |
157 | #define MII_88E3016_DISABLE_SCRAMBLER 0x0200 | |
158 | #define MII_88E3016_AUTO_MDIX_CROSSOVER 0x0030 | |
76884679 | 159 | |
930b37ee SR |
160 | #define MII_88E1510_GEN_CTRL_REG_1 0x14 |
161 | #define MII_88E1510_GEN_CTRL_REG_1_MODE_MASK 0x7 | |
162 | #define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII 0x1 /* SGMII to copper */ | |
163 | #define MII_88E1510_GEN_CTRL_REG_1_RESET 0x8000 /* Soft reset */ | |
164 | ||
6cfb3bcc CAC |
165 | #define LPA_FIBER_1000HALF 0x40 |
166 | #define LPA_FIBER_1000FULL 0x20 | |
167 | ||
8cf8b87b | 168 | #define LPA_PAUSE_FIBER 0x180 |
6cfb3bcc CAC |
169 | #define LPA_PAUSE_ASYM_FIBER 0x100 |
170 | ||
171 | #define ADVERTISE_FIBER_1000HALF 0x40 | |
172 | #define ADVERTISE_FIBER_1000FULL 0x20 | |
173 | ||
174 | #define ADVERTISE_PAUSE_FIBER 0x180 | |
175 | #define ADVERTISE_PAUSE_ASYM_FIBER 0x100 | |
176 | ||
177 | #define REGISTER_LINK_STATUS 0x400 | |
2170fef7 | 178 | #define NB_FIBER_STATS 1 |
6cfb3bcc | 179 | |
00db8189 AF |
180 | MODULE_DESCRIPTION("Marvell PHY driver"); |
181 | MODULE_AUTHOR("Andy Fleming"); | |
182 | MODULE_LICENSE("GPL"); | |
183 | ||
d2fa47d9 AL |
184 | struct marvell_hw_stat { |
185 | const char *string; | |
186 | u8 page; | |
187 | u8 reg; | |
188 | u8 bits; | |
189 | }; | |
190 | ||
191 | static struct marvell_hw_stat marvell_hw_stats[] = { | |
2170fef7 | 192 | { "phy_receive_errors_copper", 0, 21, 16}, |
d2fa47d9 | 193 | { "phy_idle_errors", 0, 10, 8 }, |
2170fef7 | 194 | { "phy_receive_errors_fiber", 1, 21, 16}, |
d2fa47d9 AL |
195 | }; |
196 | ||
197 | struct marvell_priv { | |
198 | u64 stats[ARRAY_SIZE(marvell_hw_stats)]; | |
0b04680f AL |
199 | char *hwmon_name; |
200 | struct device *hwmon_dev; | |
d2fa47d9 AL |
201 | }; |
202 | ||
424ca4c5 | 203 | static int marvell_read_page(struct phy_device *phydev) |
6427bb2d | 204 | { |
424ca4c5 | 205 | return __phy_read(phydev, MII_MARVELL_PHY_PAGE); |
6427bb2d AL |
206 | } |
207 | ||
424ca4c5 | 208 | static int marvell_write_page(struct phy_device *phydev, int page) |
6427bb2d | 209 | { |
424ca4c5 | 210 | return __phy_write(phydev, MII_MARVELL_PHY_PAGE, page); |
6427bb2d AL |
211 | } |
212 | ||
424ca4c5 | 213 | static int marvell_set_page(struct phy_device *phydev, int page) |
53798328 | 214 | { |
424ca4c5 | 215 | return phy_write(phydev, MII_MARVELL_PHY_PAGE, page); |
53798328 AL |
216 | } |
217 | ||
00db8189 AF |
218 | static int marvell_ack_interrupt(struct phy_device *phydev) |
219 | { | |
220 | int err; | |
221 | ||
222 | /* Clear the interrupts by reading the reg */ | |
223 | err = phy_read(phydev, MII_M1011_IEVENT); | |
224 | ||
225 | if (err < 0) | |
226 | return err; | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
231 | static int marvell_config_intr(struct phy_device *phydev) | |
232 | { | |
233 | int err; | |
234 | ||
76884679 | 235 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) |
23beb38f AL |
236 | err = phy_write(phydev, MII_M1011_IMASK, |
237 | MII_M1011_IMASK_INIT); | |
00db8189 | 238 | else |
23beb38f AL |
239 | err = phy_write(phydev, MII_M1011_IMASK, |
240 | MII_M1011_IMASK_CLEAR); | |
00db8189 AF |
241 | |
242 | return err; | |
243 | } | |
244 | ||
239aa55b DT |
245 | static int marvell_set_polarity(struct phy_device *phydev, int polarity) |
246 | { | |
247 | int reg; | |
248 | int err; | |
249 | int val; | |
250 | ||
251 | /* get the current settings */ | |
252 | reg = phy_read(phydev, MII_M1011_PHY_SCR); | |
253 | if (reg < 0) | |
254 | return reg; | |
255 | ||
256 | val = reg; | |
257 | val &= ~MII_M1011_PHY_SCR_AUTO_CROSS; | |
258 | switch (polarity) { | |
259 | case ETH_TP_MDI: | |
260 | val |= MII_M1011_PHY_SCR_MDI; | |
261 | break; | |
262 | case ETH_TP_MDI_X: | |
263 | val |= MII_M1011_PHY_SCR_MDI_X; | |
264 | break; | |
265 | case ETH_TP_MDI_AUTO: | |
266 | case ETH_TP_MDI_INVALID: | |
267 | default: | |
268 | val |= MII_M1011_PHY_SCR_AUTO_CROSS; | |
269 | break; | |
270 | } | |
271 | ||
272 | if (val != reg) { | |
273 | /* Set the new polarity value in the register */ | |
274 | err = phy_write(phydev, MII_M1011_PHY_SCR, val); | |
275 | if (err) | |
276 | return err; | |
277 | } | |
278 | ||
d6ab9336 | 279 | return val != reg; |
239aa55b DT |
280 | } |
281 | ||
00db8189 AF |
282 | static int marvell_config_aneg(struct phy_device *phydev) |
283 | { | |
d6ab9336 | 284 | int changed = 0; |
00db8189 AF |
285 | int err; |
286 | ||
4e26c5c3 | 287 | err = marvell_set_polarity(phydev, phydev->mdix_ctrl); |
76884679 AF |
288 | if (err < 0) |
289 | return err; | |
290 | ||
d6ab9336 FF |
291 | changed = err; |
292 | ||
76884679 AF |
293 | err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL, |
294 | MII_M1111_PHY_LED_DIRECT); | |
295 | if (err < 0) | |
296 | return err; | |
00db8189 AF |
297 | |
298 | err = genphy_config_aneg(phydev); | |
8ff44985 AV |
299 | if (err < 0) |
300 | return err; | |
00db8189 | 301 | |
d6ab9336 | 302 | if (phydev->autoneg != AUTONEG_ENABLE || changed) { |
0c3439bc | 303 | /* A write to speed/duplex bits (that is performed by |
8ff44985 AV |
304 | * genphy_config_aneg() call above) must be followed by |
305 | * a software reset. Otherwise, the write has no effect. | |
306 | */ | |
34386344 | 307 | err = genphy_soft_reset(phydev); |
8ff44985 AV |
308 | if (err < 0) |
309 | return err; | |
310 | } | |
311 | ||
312 | return 0; | |
00db8189 AF |
313 | } |
314 | ||
f2899788 AL |
315 | static int m88e1101_config_aneg(struct phy_device *phydev) |
316 | { | |
317 | int err; | |
318 | ||
319 | /* This Marvell PHY has an errata which requires | |
320 | * that certain registers get written in order | |
321 | * to restart autonegotiation | |
322 | */ | |
34386344 | 323 | err = genphy_soft_reset(phydev); |
f2899788 AL |
324 | if (err < 0) |
325 | return err; | |
326 | ||
327 | err = phy_write(phydev, 0x1d, 0x1f); | |
328 | if (err < 0) | |
329 | return err; | |
330 | ||
331 | err = phy_write(phydev, 0x1e, 0x200c); | |
332 | if (err < 0) | |
333 | return err; | |
334 | ||
335 | err = phy_write(phydev, 0x1d, 0x5); | |
336 | if (err < 0) | |
337 | return err; | |
338 | ||
339 | err = phy_write(phydev, 0x1e, 0); | |
340 | if (err < 0) | |
341 | return err; | |
342 | ||
343 | err = phy_write(phydev, 0x1e, 0x100); | |
344 | if (err < 0) | |
345 | return err; | |
346 | ||
347 | return marvell_config_aneg(phydev); | |
348 | } | |
349 | ||
cf41a51d | 350 | #ifdef CONFIG_OF_MDIO |
0c3439bc | 351 | /* Set and/or override some configuration registers based on the |
cf41a51d DD |
352 | * marvell,reg-init property stored in the of_node for the phydev. |
353 | * | |
354 | * marvell,reg-init = <reg-page reg mask value>,...; | |
355 | * | |
356 | * There may be one or more sets of <reg-page reg mask value>: | |
357 | * | |
358 | * reg-page: which register bank to use. | |
359 | * reg: the register. | |
360 | * mask: if non-zero, ANDed with existing register value. | |
361 | * value: ORed with the masked value and written to the regiser. | |
362 | * | |
363 | */ | |
364 | static int marvell_of_reg_init(struct phy_device *phydev) | |
365 | { | |
366 | const __be32 *paddr; | |
424ca4c5 | 367 | int len, i, saved_page, current_page, ret = 0; |
cf41a51d | 368 | |
e5a03bfd | 369 | if (!phydev->mdio.dev.of_node) |
cf41a51d DD |
370 | return 0; |
371 | ||
e5a03bfd AL |
372 | paddr = of_get_property(phydev->mdio.dev.of_node, |
373 | "marvell,reg-init", &len); | |
cf41a51d DD |
374 | if (!paddr || len < (4 * sizeof(*paddr))) |
375 | return 0; | |
376 | ||
424ca4c5 | 377 | saved_page = phy_save_page(phydev); |
cf41a51d | 378 | if (saved_page < 0) |
424ca4c5 | 379 | goto err; |
cf41a51d DD |
380 | current_page = saved_page; |
381 | ||
cf41a51d DD |
382 | len /= sizeof(*paddr); |
383 | for (i = 0; i < len - 3; i += 4) { | |
6427bb2d | 384 | u16 page = be32_to_cpup(paddr + i); |
cf41a51d DD |
385 | u16 reg = be32_to_cpup(paddr + i + 1); |
386 | u16 mask = be32_to_cpup(paddr + i + 2); | |
387 | u16 val_bits = be32_to_cpup(paddr + i + 3); | |
388 | int val; | |
389 | ||
6427bb2d AL |
390 | if (page != current_page) { |
391 | current_page = page; | |
424ca4c5 | 392 | ret = marvell_write_page(phydev, page); |
cf41a51d DD |
393 | if (ret < 0) |
394 | goto err; | |
395 | } | |
396 | ||
397 | val = 0; | |
398 | if (mask) { | |
424ca4c5 | 399 | val = __phy_read(phydev, reg); |
cf41a51d DD |
400 | if (val < 0) { |
401 | ret = val; | |
402 | goto err; | |
403 | } | |
404 | val &= mask; | |
405 | } | |
406 | val |= val_bits; | |
407 | ||
424ca4c5 | 408 | ret = __phy_write(phydev, reg, val); |
cf41a51d DD |
409 | if (ret < 0) |
410 | goto err; | |
cf41a51d DD |
411 | } |
412 | err: | |
424ca4c5 | 413 | return phy_restore_page(phydev, saved_page, ret); |
cf41a51d DD |
414 | } |
415 | #else | |
416 | static int marvell_of_reg_init(struct phy_device *phydev) | |
417 | { | |
418 | return 0; | |
419 | } | |
420 | #endif /* CONFIG_OF_MDIO */ | |
421 | ||
864dc729 | 422 | static int m88e1121_config_aneg_rgmii_delays(struct phy_device *phydev) |
140bc929 | 423 | { |
424ca4c5 | 424 | int mscr; |
864dc729 AL |
425 | |
426 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) | |
424ca4c5 RK |
427 | mscr = MII_88E1121_PHY_MSCR_RX_DELAY | |
428 | MII_88E1121_PHY_MSCR_TX_DELAY; | |
864dc729 | 429 | else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) |
424ca4c5 | 430 | mscr = MII_88E1121_PHY_MSCR_RX_DELAY; |
864dc729 | 431 | else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) |
424ca4c5 RK |
432 | mscr = MII_88E1121_PHY_MSCR_TX_DELAY; |
433 | else | |
434 | mscr = 0; | |
140bc929 | 435 | |
424ca4c5 RK |
436 | return phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE, |
437 | MII_88E1121_PHY_MSCR_REG, | |
438 | MII_88E1121_PHY_MSCR_DELAY_MASK, mscr); | |
864dc729 AL |
439 | } |
440 | ||
441 | static int m88e1121_config_aneg(struct phy_device *phydev) | |
442 | { | |
d6ab9336 | 443 | int changed = 0; |
864dc729 AL |
444 | int err = 0; |
445 | ||
446 | if (phy_interface_is_rgmii(phydev)) { | |
447 | err = m88e1121_config_aneg_rgmii_delays(phydev); | |
fea23fb5 | 448 | if (err < 0) |
864dc729 AL |
449 | return err; |
450 | } | |
451 | ||
d6ab9336 | 452 | err = marvell_set_polarity(phydev, phydev->mdix_ctrl); |
140bc929 SP |
453 | if (err < 0) |
454 | return err; | |
455 | ||
d6ab9336 FF |
456 | changed = err; |
457 | ||
458 | err = genphy_config_aneg(phydev); | |
140bc929 SP |
459 | if (err < 0) |
460 | return err; | |
461 | ||
4b1bd697 | 462 | if (phydev->autoneg != AUTONEG_ENABLE || changed) { |
d6ab9336 FF |
463 | /* A software reset is used to ensure a "commit" of the |
464 | * changes is done. | |
465 | */ | |
466 | err = genphy_soft_reset(phydev); | |
467 | if (err < 0) | |
468 | return err; | |
469 | } | |
470 | ||
471 | return 0; | |
140bc929 SP |
472 | } |
473 | ||
337ac9d5 | 474 | static int m88e1318_config_aneg(struct phy_device *phydev) |
3ff1c259 | 475 | { |
424ca4c5 | 476 | int err; |
3ff1c259 | 477 | |
424ca4c5 RK |
478 | err = phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE, |
479 | MII_88E1318S_PHY_MSCR1_REG, | |
480 | 0, MII_88E1318S_PHY_MSCR1_PAD_ODD); | |
3ff1c259 CC |
481 | if (err < 0) |
482 | return err; | |
483 | ||
484 | return m88e1121_config_aneg(phydev); | |
485 | } | |
486 | ||
78301ebe | 487 | /** |
3c1bcc86 AL |
488 | * linkmode_adv_to_fiber_adv_t |
489 | * @advertise: the linkmode advertisement settings | |
78301ebe | 490 | * |
3c1bcc86 AL |
491 | * A small helper function that translates linkmode advertisement |
492 | * settings to phy autonegotiation advertisements for the MII_ADV | |
493 | * register for fiber link. | |
78301ebe | 494 | */ |
3c1bcc86 | 495 | static inline u32 linkmode_adv_to_fiber_adv_t(unsigned long *advertise) |
78301ebe CAC |
496 | { |
497 | u32 result = 0; | |
498 | ||
3c1bcc86 | 499 | if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, advertise)) |
78301ebe | 500 | result |= ADVERTISE_FIBER_1000HALF; |
3c1bcc86 | 501 | if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, advertise)) |
78301ebe CAC |
502 | result |= ADVERTISE_FIBER_1000FULL; |
503 | ||
3c1bcc86 AL |
504 | if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertise) && |
505 | linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise)) | |
78301ebe | 506 | result |= LPA_PAUSE_ASYM_FIBER; |
3c1bcc86 | 507 | else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise)) |
78301ebe CAC |
508 | result |= (ADVERTISE_PAUSE_FIBER |
509 | & (~ADVERTISE_PAUSE_ASYM_FIBER)); | |
510 | ||
511 | return result; | |
512 | } | |
513 | ||
514 | /** | |
515 | * marvell_config_aneg_fiber - restart auto-negotiation or write BMCR | |
516 | * @phydev: target phy_device struct | |
517 | * | |
518 | * Description: If auto-negotiation is enabled, we configure the | |
519 | * advertising, and then restart auto-negotiation. If it is not | |
520 | * enabled, then we write the BMCR. Adapted for fiber link in | |
521 | * some Marvell's devices. | |
522 | */ | |
523 | static int marvell_config_aneg_fiber(struct phy_device *phydev) | |
524 | { | |
525 | int changed = 0; | |
526 | int err; | |
527 | int adv, oldadv; | |
78301ebe CAC |
528 | |
529 | if (phydev->autoneg != AUTONEG_ENABLE) | |
530 | return genphy_setup_forced(phydev); | |
531 | ||
532 | /* Only allow advertising what this PHY supports */ | |
3c1bcc86 AL |
533 | linkmode_and(phydev->advertising, phydev->advertising, |
534 | phydev->supported); | |
78301ebe CAC |
535 | |
536 | /* Setup fiber advertisement */ | |
537 | adv = phy_read(phydev, MII_ADVERTISE); | |
538 | if (adv < 0) | |
539 | return adv; | |
540 | ||
541 | oldadv = adv; | |
542 | adv &= ~(ADVERTISE_FIBER_1000HALF | ADVERTISE_FIBER_1000FULL | |
543 | | LPA_PAUSE_FIBER); | |
3c1bcc86 | 544 | adv |= linkmode_adv_to_fiber_adv_t(phydev->advertising); |
78301ebe CAC |
545 | |
546 | if (adv != oldadv) { | |
547 | err = phy_write(phydev, MII_ADVERTISE, adv); | |
548 | if (err < 0) | |
549 | return err; | |
550 | ||
551 | changed = 1; | |
552 | } | |
553 | ||
554 | if (changed == 0) { | |
555 | /* Advertisement hasn't changed, but maybe aneg was never on to | |
8cf8b87b | 556 | * begin with? Or maybe phy was isolated? |
78301ebe CAC |
557 | */ |
558 | int ctl = phy_read(phydev, MII_BMCR); | |
559 | ||
560 | if (ctl < 0) | |
561 | return ctl; | |
562 | ||
563 | if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) | |
564 | changed = 1; /* do restart aneg */ | |
565 | } | |
566 | ||
567 | /* Only restart aneg if we are advertising something different | |
568 | * than we were before. | |
569 | */ | |
570 | if (changed > 0) | |
571 | changed = genphy_restart_aneg(phydev); | |
572 | ||
573 | return changed; | |
574 | } | |
575 | ||
10e24caa MS |
576 | static int m88e1510_config_aneg(struct phy_device *phydev) |
577 | { | |
578 | int err; | |
579 | ||
52295666 | 580 | err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); |
78301ebe CAC |
581 | if (err < 0) |
582 | goto error; | |
583 | ||
584 | /* Configure the copper link first */ | |
10e24caa MS |
585 | err = m88e1318_config_aneg(phydev); |
586 | if (err < 0) | |
78301ebe | 587 | goto error; |
10e24caa | 588 | |
de9c4e06 RK |
589 | /* Do not touch the fiber page if we're in copper->sgmii mode */ |
590 | if (phydev->interface == PHY_INTERFACE_MODE_SGMII) | |
591 | return 0; | |
592 | ||
78301ebe | 593 | /* Then the fiber link */ |
52295666 | 594 | err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE); |
78301ebe CAC |
595 | if (err < 0) |
596 | goto error; | |
597 | ||
598 | err = marvell_config_aneg_fiber(phydev); | |
599 | if (err < 0) | |
600 | goto error; | |
601 | ||
52295666 | 602 | return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); |
78301ebe CAC |
603 | |
604 | error: | |
52295666 | 605 | marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); |
78301ebe | 606 | return err; |
79be1a1c CG |
607 | } |
608 | ||
07777246 WD |
609 | static void marvell_config_led(struct phy_device *phydev) |
610 | { | |
611 | u16 def_config; | |
612 | int err; | |
613 | ||
614 | switch (MARVELL_PHY_FAMILY_ID(phydev->phy_id)) { | |
615 | /* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */ | |
616 | case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1121R): | |
617 | case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1318S): | |
618 | def_config = MII_88E1121_PHY_LED_DEF; | |
619 | break; | |
620 | /* Default PHY LED config: | |
621 | * LED[0] .. 1000Mbps Link | |
622 | * LED[1] .. 100Mbps Link | |
623 | * LED[2] .. Blink, Activity | |
624 | */ | |
625 | case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1510): | |
a93f7fe1 JS |
626 | if (phydev->dev_flags & MARVELL_PHY_LED0_LINK_LED1_ACTIVE) |
627 | def_config = MII_88E1510_PHY_LED0_LINK_LED1_ACTIVE; | |
628 | else | |
629 | def_config = MII_88E1510_PHY_LED_DEF; | |
07777246 WD |
630 | break; |
631 | default: | |
632 | return; | |
633 | } | |
634 | ||
635 | err = phy_write_paged(phydev, MII_MARVELL_LED_PAGE, MII_PHY_LED_CTRL, | |
636 | def_config); | |
637 | if (err < 0) | |
ab2a605f | 638 | phydev_warn(phydev, "Fail to config marvell phy LED.\n"); |
07777246 WD |
639 | } |
640 | ||
79be1a1c CG |
641 | static int marvell_config_init(struct phy_device *phydev) |
642 | { | |
07777246 WD |
643 | /* Set defalut LED */ |
644 | marvell_config_led(phydev); | |
645 | ||
79be1a1c | 646 | /* Set registers from marvell,reg-init DT property */ |
10e24caa MS |
647 | return marvell_of_reg_init(phydev); |
648 | } | |
649 | ||
6b358aed SH |
650 | static int m88e3016_config_init(struct phy_device *phydev) |
651 | { | |
fea23fb5 | 652 | int ret; |
6b358aed SH |
653 | |
654 | /* Enable Scrambler and Auto-Crossover */ | |
fea23fb5 | 655 | ret = phy_modify(phydev, MII_88E3016_PHY_SPEC_CTRL, |
f102852f | 656 | MII_88E3016_DISABLE_SCRAMBLER, |
fea23fb5 RK |
657 | MII_88E3016_AUTO_MDIX_CROSSOVER); |
658 | if (ret < 0) | |
659 | return ret; | |
6b358aed | 660 | |
79be1a1c | 661 | return marvell_config_init(phydev); |
6b358aed SH |
662 | } |
663 | ||
865b813a AL |
664 | static int m88e1111_config_init_hwcfg_mode(struct phy_device *phydev, |
665 | u16 mode, | |
666 | int fibre_copper_auto) | |
667 | { | |
865b813a | 668 | if (fibre_copper_auto) |
fea23fb5 | 669 | mode |= MII_M1111_HWCFG_FIBER_COPPER_AUTO; |
865b813a | 670 | |
fea23fb5 | 671 | return phy_modify(phydev, MII_M1111_PHY_EXT_SR, |
f102852f RK |
672 | MII_M1111_HWCFG_MODE_MASK | |
673 | MII_M1111_HWCFG_FIBER_COPPER_AUTO | | |
674 | MII_M1111_HWCFG_FIBER_COPPER_RES, | |
fea23fb5 | 675 | mode); |
865b813a AL |
676 | } |
677 | ||
61111598 | 678 | static int m88e1111_config_init_rgmii_delays(struct phy_device *phydev) |
895ee682 | 679 | { |
fea23fb5 | 680 | int delay; |
895ee682 | 681 | |
e1dde8dc | 682 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { |
fea23fb5 | 683 | delay = MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY; |
e1dde8dc | 684 | } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { |
fea23fb5 | 685 | delay = MII_M1111_RGMII_RX_DELAY; |
e1dde8dc | 686 | } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { |
fea23fb5 RK |
687 | delay = MII_M1111_RGMII_TX_DELAY; |
688 | } else { | |
689 | delay = 0; | |
e1dde8dc | 690 | } |
895ee682 | 691 | |
fea23fb5 | 692 | return phy_modify(phydev, MII_M1111_PHY_EXT_CR, |
f102852f | 693 | MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY, |
fea23fb5 | 694 | delay); |
61111598 AL |
695 | } |
696 | ||
697 | static int m88e1111_config_init_rgmii(struct phy_device *phydev) | |
698 | { | |
699 | int temp; | |
700 | int err; | |
701 | ||
702 | err = m88e1111_config_init_rgmii_delays(phydev); | |
e1dde8dc AL |
703 | if (err < 0) |
704 | return err; | |
9daf5a76 | 705 | |
e1dde8dc AL |
706 | temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); |
707 | if (temp < 0) | |
708 | return temp; | |
895ee682 | 709 | |
e1dde8dc | 710 | temp &= ~(MII_M1111_HWCFG_MODE_MASK); |
be937f1f | 711 | |
e1dde8dc AL |
712 | if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES) |
713 | temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII; | |
714 | else | |
715 | temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII; | |
895ee682 | 716 | |
e1dde8dc AL |
717 | return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); |
718 | } | |
895ee682 | 719 | |
e1dde8dc AL |
720 | static int m88e1111_config_init_sgmii(struct phy_device *phydev) |
721 | { | |
722 | int err; | |
4117b5be | 723 | |
865b813a AL |
724 | err = m88e1111_config_init_hwcfg_mode( |
725 | phydev, | |
726 | MII_M1111_HWCFG_MODE_SGMII_NO_CLK, | |
727 | MII_M1111_HWCFG_FIBER_COPPER_AUTO); | |
e1dde8dc AL |
728 | if (err < 0) |
729 | return err; | |
07151bc9 | 730 | |
e1dde8dc | 731 | /* make sure copper is selected */ |
52295666 | 732 | return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); |
e1dde8dc | 733 | } |
5f8cbc13 | 734 | |
e1dde8dc AL |
735 | static int m88e1111_config_init_rtbi(struct phy_device *phydev) |
736 | { | |
61111598 | 737 | int err; |
e1dde8dc | 738 | |
61111598 | 739 | err = m88e1111_config_init_rgmii_delays(phydev); |
fea23fb5 | 740 | if (err < 0) |
e1dde8dc AL |
741 | return err; |
742 | ||
865b813a AL |
743 | err = m88e1111_config_init_hwcfg_mode( |
744 | phydev, | |
745 | MII_M1111_HWCFG_MODE_RTBI, | |
746 | MII_M1111_HWCFG_FIBER_COPPER_AUTO); | |
e1dde8dc AL |
747 | if (err < 0) |
748 | return err; | |
749 | ||
750 | /* soft reset */ | |
34386344 | 751 | err = genphy_soft_reset(phydev); |
e1dde8dc AL |
752 | if (err < 0) |
753 | return err; | |
754 | ||
865b813a AL |
755 | return m88e1111_config_init_hwcfg_mode( |
756 | phydev, | |
757 | MII_M1111_HWCFG_MODE_RTBI, | |
758 | MII_M1111_HWCFG_FIBER_COPPER_AUTO); | |
e1dde8dc AL |
759 | } |
760 | ||
761 | static int m88e1111_config_init(struct phy_device *phydev) | |
762 | { | |
763 | int err; | |
764 | ||
765 | if (phy_interface_is_rgmii(phydev)) { | |
766 | err = m88e1111_config_init_rgmii(phydev); | |
fea23fb5 | 767 | if (err < 0) |
5f8cbc13 | 768 | return err; |
e1dde8dc | 769 | } |
5f8cbc13 | 770 | |
e1dde8dc AL |
771 | if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { |
772 | err = m88e1111_config_init_sgmii(phydev); | |
5f8cbc13 LYB |
773 | if (err < 0) |
774 | return err; | |
e1dde8dc | 775 | } |
5f8cbc13 | 776 | |
e1dde8dc AL |
777 | if (phydev->interface == PHY_INTERFACE_MODE_RTBI) { |
778 | err = m88e1111_config_init_rtbi(phydev); | |
5f8cbc13 LYB |
779 | if (err < 0) |
780 | return err; | |
781 | } | |
782 | ||
cf41a51d DD |
783 | err = marvell_of_reg_init(phydev); |
784 | if (err < 0) | |
785 | return err; | |
5f8cbc13 | 786 | |
34386344 | 787 | return genphy_soft_reset(phydev); |
895ee682 KP |
788 | } |
789 | ||
5c6bc519 HK |
790 | static int m88e1111_get_downshift(struct phy_device *phydev, u8 *data) |
791 | { | |
792 | int val, cnt, enable; | |
793 | ||
794 | val = phy_read(phydev, MII_M1111_PHY_EXT_CR); | |
795 | if (val < 0) | |
796 | return val; | |
797 | ||
798 | enable = FIELD_GET(MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN, val); | |
799 | cnt = FIELD_GET(MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK, val) + 1; | |
800 | ||
801 | *data = enable ? cnt : DOWNSHIFT_DEV_DISABLE; | |
802 | ||
803 | return 0; | |
804 | } | |
805 | ||
806 | static int m88e1111_set_downshift(struct phy_device *phydev, u8 cnt) | |
807 | { | |
808 | int val; | |
809 | ||
810 | if (cnt > MII_M1111_PHY_EXT_CR_DOWNSHIFT_MAX) | |
811 | return -E2BIG; | |
812 | ||
813 | if (!cnt) | |
814 | return phy_clear_bits(phydev, MII_M1111_PHY_EXT_CR, | |
815 | MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN); | |
816 | ||
817 | val = MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN; | |
818 | val |= FIELD_PREP(MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK, cnt - 1); | |
819 | ||
820 | return phy_modify(phydev, MII_M1111_PHY_EXT_CR, | |
821 | MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN | | |
822 | MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK, | |
823 | val); | |
824 | } | |
825 | ||
826 | static int m88e1111_get_tunable(struct phy_device *phydev, | |
827 | struct ethtool_tunable *tuna, void *data) | |
828 | { | |
829 | switch (tuna->id) { | |
830 | case ETHTOOL_PHY_DOWNSHIFT: | |
831 | return m88e1111_get_downshift(phydev, data); | |
832 | default: | |
833 | return -EOPNOTSUPP; | |
834 | } | |
835 | } | |
836 | ||
837 | static int m88e1111_set_tunable(struct phy_device *phydev, | |
838 | struct ethtool_tunable *tuna, const void *data) | |
839 | { | |
840 | switch (tuna->id) { | |
841 | case ETHTOOL_PHY_DOWNSHIFT: | |
842 | return m88e1111_set_downshift(phydev, *(const u8 *)data); | |
843 | default: | |
844 | return -EOPNOTSUPP; | |
845 | } | |
846 | } | |
847 | ||
911af5e1 | 848 | static int m88e1011_get_downshift(struct phy_device *phydev, u8 *data) |
a3bdfce7 HK |
849 | { |
850 | int val, cnt, enable; | |
851 | ||
852 | val = phy_read(phydev, MII_M1011_PHY_SCR); | |
853 | if (val < 0) | |
854 | return val; | |
855 | ||
856 | enable = FIELD_GET(MII_M1011_PHY_SCR_DOWNSHIFT_EN, val); | |
f8d975be | 857 | cnt = FIELD_GET(MII_M1011_PHY_SCR_DOWNSHIFT_MASK, val) + 1; |
a3bdfce7 HK |
858 | |
859 | *data = enable ? cnt : DOWNSHIFT_DEV_DISABLE; | |
860 | ||
861 | return 0; | |
862 | } | |
863 | ||
911af5e1 | 864 | static int m88e1011_set_downshift(struct phy_device *phydev, u8 cnt) |
a3bdfce7 HK |
865 | { |
866 | int val; | |
867 | ||
868 | if (cnt > MII_M1011_PHY_SCR_DOWNSHIFT_MAX) | |
869 | return -E2BIG; | |
870 | ||
871 | if (!cnt) | |
872 | return phy_clear_bits(phydev, MII_M1011_PHY_SCR, | |
873 | MII_M1011_PHY_SCR_DOWNSHIFT_EN); | |
874 | ||
875 | val = MII_M1011_PHY_SCR_DOWNSHIFT_EN; | |
f8d975be | 876 | val |= FIELD_PREP(MII_M1011_PHY_SCR_DOWNSHIFT_MASK, cnt - 1); |
a3bdfce7 HK |
877 | |
878 | return phy_modify(phydev, MII_M1011_PHY_SCR, | |
879 | MII_M1011_PHY_SCR_DOWNSHIFT_EN | | |
f8d975be | 880 | MII_M1011_PHY_SCR_DOWNSHIFT_MASK, |
a3bdfce7 HK |
881 | val); |
882 | } | |
883 | ||
911af5e1 | 884 | static int m88e1011_get_tunable(struct phy_device *phydev, |
a3bdfce7 HK |
885 | struct ethtool_tunable *tuna, void *data) |
886 | { | |
887 | switch (tuna->id) { | |
888 | case ETHTOOL_PHY_DOWNSHIFT: | |
911af5e1 | 889 | return m88e1011_get_downshift(phydev, data); |
a3bdfce7 HK |
890 | default: |
891 | return -EOPNOTSUPP; | |
892 | } | |
893 | } | |
894 | ||
911af5e1 | 895 | static int m88e1011_set_tunable(struct phy_device *phydev, |
a3bdfce7 HK |
896 | struct ethtool_tunable *tuna, const void *data) |
897 | { | |
898 | switch (tuna->id) { | |
899 | case ETHTOOL_PHY_DOWNSHIFT: | |
911af5e1 | 900 | return m88e1011_set_downshift(phydev, *(const u8 *)data); |
a3bdfce7 HK |
901 | default: |
902 | return -EOPNOTSUPP; | |
903 | } | |
904 | } | |
905 | ||
911af5e1 | 906 | static void m88e1011_link_change_notify(struct phy_device *phydev) |
a3bdfce7 HK |
907 | { |
908 | int status; | |
909 | ||
910 | if (phydev->state != PHY_RUNNING) | |
911 | return; | |
912 | ||
913 | /* we may be on fiber page currently */ | |
914 | status = phy_read_paged(phydev, MII_MARVELL_COPPER_PAGE, | |
915 | MII_M1011_PHY_SSR); | |
916 | ||
917 | if (status > 0 && status & MII_M1011_PHY_SSR_DOWNSHIFT) | |
918 | phydev_warn(phydev, "Downshift occurred! Cabling may be defective.\n"); | |
919 | } | |
920 | ||
e2d861cc HK |
921 | static int m88e1116r_config_init(struct phy_device *phydev) |
922 | { | |
923 | int err; | |
924 | ||
925 | err = genphy_soft_reset(phydev); | |
926 | if (err < 0) | |
927 | return err; | |
928 | ||
929 | msleep(500); | |
930 | ||
931 | err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); | |
932 | if (err < 0) | |
933 | return err; | |
934 | ||
935 | err = marvell_set_polarity(phydev, phydev->mdix_ctrl); | |
936 | if (err < 0) | |
937 | return err; | |
938 | ||
911af5e1 | 939 | err = m88e1011_set_downshift(phydev, 8); |
e2d861cc HK |
940 | if (err < 0) |
941 | return err; | |
942 | ||
943 | if (phy_interface_is_rgmii(phydev)) { | |
944 | err = m88e1121_config_aneg_rgmii_delays(phydev); | |
945 | if (err < 0) | |
946 | return err; | |
947 | } | |
948 | ||
949 | err = genphy_soft_reset(phydev); | |
950 | if (err < 0) | |
951 | return err; | |
952 | ||
953 | return marvell_config_init(phydev); | |
954 | } | |
955 | ||
dd9a122a EH |
956 | static int m88e1318_config_init(struct phy_device *phydev) |
957 | { | |
958 | if (phy_interrupt_is_valid(phydev)) { | |
959 | int err = phy_modify_paged( | |
960 | phydev, MII_MARVELL_LED_PAGE, | |
961 | MII_88E1318S_PHY_LED_TCR, | |
962 | MII_88E1318S_PHY_LED_TCR_FORCE_INT, | |
963 | MII_88E1318S_PHY_LED_TCR_INTn_ENABLE | | |
964 | MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW); | |
965 | if (err < 0) | |
966 | return err; | |
967 | } | |
968 | ||
07777246 | 969 | return marvell_config_init(phydev); |
dd9a122a EH |
970 | } |
971 | ||
407353ec CG |
972 | static int m88e1510_config_init(struct phy_device *phydev) |
973 | { | |
974 | int err; | |
407353ec CG |
975 | |
976 | /* SGMII-to-Copper mode initialization */ | |
977 | if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { | |
978 | /* Select page 18 */ | |
6427bb2d | 979 | err = marvell_set_page(phydev, 18); |
407353ec CG |
980 | if (err < 0) |
981 | return err; | |
982 | ||
983 | /* In reg 20, write MODE[2:0] = 0x1 (SGMII to Copper) */ | |
fea23fb5 | 984 | err = phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1, |
f102852f | 985 | MII_88E1510_GEN_CTRL_REG_1_MODE_MASK, |
fea23fb5 | 986 | MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII); |
407353ec CG |
987 | if (err < 0) |
988 | return err; | |
989 | ||
990 | /* PHY reset is necessary after changing MODE[2:0] */ | |
fea23fb5 RK |
991 | err = phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1, 0, |
992 | MII_88E1510_GEN_CTRL_REG_1_RESET); | |
407353ec CG |
993 | if (err < 0) |
994 | return err; | |
995 | ||
996 | /* Reset page selection */ | |
52295666 | 997 | err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); |
407353ec CG |
998 | if (err < 0) |
999 | return err; | |
1000 | } | |
1001 | ||
dd9a122a | 1002 | return m88e1318_config_init(phydev); |
407353ec CG |
1003 | } |
1004 | ||
605f196e RM |
1005 | static int m88e1118_config_aneg(struct phy_device *phydev) |
1006 | { | |
1007 | int err; | |
1008 | ||
34386344 | 1009 | err = genphy_soft_reset(phydev); |
605f196e RM |
1010 | if (err < 0) |
1011 | return err; | |
1012 | ||
fecd5e91 | 1013 | err = marvell_set_polarity(phydev, phydev->mdix_ctrl); |
605f196e RM |
1014 | if (err < 0) |
1015 | return err; | |
1016 | ||
1017 | err = genphy_config_aneg(phydev); | |
1018 | return 0; | |
1019 | } | |
1020 | ||
1021 | static int m88e1118_config_init(struct phy_device *phydev) | |
1022 | { | |
1023 | int err; | |
1024 | ||
1025 | /* Change address */ | |
52295666 | 1026 | err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE); |
605f196e RM |
1027 | if (err < 0) |
1028 | return err; | |
1029 | ||
1030 | /* Enable 1000 Mbit */ | |
1031 | err = phy_write(phydev, 0x15, 0x1070); | |
1032 | if (err < 0) | |
1033 | return err; | |
1034 | ||
1035 | /* Change address */ | |
52295666 | 1036 | err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE); |
605f196e RM |
1037 | if (err < 0) |
1038 | return err; | |
1039 | ||
1040 | /* Adjust LED Control */ | |
2f495c39 BH |
1041 | if (phydev->dev_flags & MARVELL_PHY_M1118_DNS323_LEDS) |
1042 | err = phy_write(phydev, 0x10, 0x1100); | |
1043 | else | |
1044 | err = phy_write(phydev, 0x10, 0x021e); | |
605f196e RM |
1045 | if (err < 0) |
1046 | return err; | |
1047 | ||
cf41a51d DD |
1048 | err = marvell_of_reg_init(phydev); |
1049 | if (err < 0) | |
1050 | return err; | |
1051 | ||
605f196e | 1052 | /* Reset address */ |
52295666 | 1053 | err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); |
605f196e RM |
1054 | if (err < 0) |
1055 | return err; | |
1056 | ||
34386344 | 1057 | return genphy_soft_reset(phydev); |
605f196e RM |
1058 | } |
1059 | ||
90600732 DD |
1060 | static int m88e1149_config_init(struct phy_device *phydev) |
1061 | { | |
1062 | int err; | |
1063 | ||
1064 | /* Change address */ | |
52295666 | 1065 | err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE); |
90600732 DD |
1066 | if (err < 0) |
1067 | return err; | |
1068 | ||
1069 | /* Enable 1000 Mbit */ | |
1070 | err = phy_write(phydev, 0x15, 0x1048); | |
1071 | if (err < 0) | |
1072 | return err; | |
1073 | ||
cf41a51d DD |
1074 | err = marvell_of_reg_init(phydev); |
1075 | if (err < 0) | |
1076 | return err; | |
1077 | ||
90600732 | 1078 | /* Reset address */ |
52295666 | 1079 | err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); |
90600732 DD |
1080 | if (err < 0) |
1081 | return err; | |
1082 | ||
34386344 | 1083 | return genphy_soft_reset(phydev); |
90600732 DD |
1084 | } |
1085 | ||
e1dde8dc AL |
1086 | static int m88e1145_config_init_rgmii(struct phy_device *phydev) |
1087 | { | |
1088 | int err; | |
e1dde8dc | 1089 | |
61111598 | 1090 | err = m88e1111_config_init_rgmii_delays(phydev); |
e1dde8dc AL |
1091 | if (err < 0) |
1092 | return err; | |
1093 | ||
1094 | if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) { | |
1095 | err = phy_write(phydev, 0x1d, 0x0012); | |
1096 | if (err < 0) | |
1097 | return err; | |
1098 | ||
f102852f | 1099 | err = phy_modify(phydev, 0x1e, 0x0fc0, |
fea23fb5 RK |
1100 | 2 << 9 | /* 36 ohm */ |
1101 | 2 << 6); /* 39 ohm */ | |
e1dde8dc AL |
1102 | if (err < 0) |
1103 | return err; | |
1104 | ||
1105 | err = phy_write(phydev, 0x1d, 0x3); | |
1106 | if (err < 0) | |
1107 | return err; | |
1108 | ||
1109 | err = phy_write(phydev, 0x1e, 0x8000); | |
1110 | } | |
1111 | return err; | |
1112 | } | |
1113 | ||
1114 | static int m88e1145_config_init_sgmii(struct phy_device *phydev) | |
1115 | { | |
865b813a AL |
1116 | return m88e1111_config_init_hwcfg_mode( |
1117 | phydev, MII_M1111_HWCFG_MODE_SGMII_NO_CLK, | |
1118 | MII_M1111_HWCFG_FIBER_COPPER_AUTO); | |
e1dde8dc AL |
1119 | } |
1120 | ||
76884679 AF |
1121 | static int m88e1145_config_init(struct phy_device *phydev) |
1122 | { | |
1123 | int err; | |
1124 | ||
1125 | /* Take care of errata E0 & E1 */ | |
1126 | err = phy_write(phydev, 0x1d, 0x001b); | |
1127 | if (err < 0) | |
1128 | return err; | |
1129 | ||
1130 | err = phy_write(phydev, 0x1e, 0x418f); | |
1131 | if (err < 0) | |
1132 | return err; | |
1133 | ||
1134 | err = phy_write(phydev, 0x1d, 0x0016); | |
1135 | if (err < 0) | |
1136 | return err; | |
1137 | ||
1138 | err = phy_write(phydev, 0x1e, 0xa2da); | |
1139 | if (err < 0) | |
1140 | return err; | |
1141 | ||
895ee682 | 1142 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { |
e1dde8dc | 1143 | err = m88e1145_config_init_rgmii(phydev); |
76884679 AF |
1144 | if (err < 0) |
1145 | return err; | |
76884679 AF |
1146 | } |
1147 | ||
b0224175 | 1148 | if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { |
e1dde8dc | 1149 | err = m88e1145_config_init_sgmii(phydev); |
b0224175 VND |
1150 | if (err < 0) |
1151 | return err; | |
1152 | } | |
1153 | ||
cf41a51d DD |
1154 | err = marvell_of_reg_init(phydev); |
1155 | if (err < 0) | |
1156 | return err; | |
1157 | ||
76884679 AF |
1158 | return 0; |
1159 | } | |
00db8189 | 1160 | |
69f42be8 HK |
1161 | static int m88e1540_get_fld(struct phy_device *phydev, u8 *msecs) |
1162 | { | |
1163 | int val; | |
1164 | ||
1165 | val = phy_read(phydev, MII_88E1540_COPPER_CTRL3); | |
1166 | if (val < 0) | |
1167 | return val; | |
1168 | ||
1169 | if (!(val & MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN)) { | |
1170 | *msecs = ETHTOOL_PHY_FAST_LINK_DOWN_OFF; | |
1171 | return 0; | |
1172 | } | |
1173 | ||
1174 | val = FIELD_GET(MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val); | |
1175 | ||
1176 | switch (val) { | |
1177 | case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS: | |
1178 | *msecs = 0; | |
1179 | break; | |
1180 | case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS: | |
1181 | *msecs = 10; | |
1182 | break; | |
1183 | case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS: | |
1184 | *msecs = 20; | |
1185 | break; | |
1186 | case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS: | |
1187 | *msecs = 40; | |
1188 | break; | |
1189 | default: | |
1190 | return -EINVAL; | |
1191 | } | |
1192 | ||
1193 | return 0; | |
1194 | } | |
1195 | ||
1196 | static int m88e1540_set_fld(struct phy_device *phydev, const u8 *msecs) | |
1197 | { | |
1198 | struct ethtool_eee eee; | |
1199 | int val, ret; | |
1200 | ||
1201 | if (*msecs == ETHTOOL_PHY_FAST_LINK_DOWN_OFF) | |
1202 | return phy_clear_bits(phydev, MII_88E1540_COPPER_CTRL3, | |
1203 | MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN); | |
1204 | ||
1205 | /* According to the Marvell data sheet EEE must be disabled for | |
1206 | * Fast Link Down detection to work properly | |
1207 | */ | |
1208 | ret = phy_ethtool_get_eee(phydev, &eee); | |
1209 | if (!ret && eee.eee_enabled) { | |
1210 | phydev_warn(phydev, "Fast Link Down detection requires EEE to be disabled!\n"); | |
1211 | return -EBUSY; | |
1212 | } | |
1213 | ||
1214 | if (*msecs <= 5) | |
1215 | val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS; | |
1216 | else if (*msecs <= 15) | |
1217 | val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS; | |
1218 | else if (*msecs <= 30) | |
1219 | val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS; | |
1220 | else | |
1221 | val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS; | |
1222 | ||
1223 | val = FIELD_PREP(MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val); | |
1224 | ||
1225 | ret = phy_modify(phydev, MII_88E1540_COPPER_CTRL3, | |
1226 | MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val); | |
1227 | if (ret) | |
1228 | return ret; | |
1229 | ||
1230 | return phy_set_bits(phydev, MII_88E1540_COPPER_CTRL3, | |
1231 | MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN); | |
1232 | } | |
1233 | ||
1234 | static int m88e1540_get_tunable(struct phy_device *phydev, | |
1235 | struct ethtool_tunable *tuna, void *data) | |
1236 | { | |
1237 | switch (tuna->id) { | |
1238 | case ETHTOOL_PHY_FAST_LINK_DOWN: | |
1239 | return m88e1540_get_fld(phydev, data); | |
a3bdfce7 | 1240 | case ETHTOOL_PHY_DOWNSHIFT: |
911af5e1 | 1241 | return m88e1011_get_downshift(phydev, data); |
69f42be8 HK |
1242 | default: |
1243 | return -EOPNOTSUPP; | |
1244 | } | |
1245 | } | |
1246 | ||
1247 | static int m88e1540_set_tunable(struct phy_device *phydev, | |
1248 | struct ethtool_tunable *tuna, const void *data) | |
1249 | { | |
1250 | switch (tuna->id) { | |
1251 | case ETHTOOL_PHY_FAST_LINK_DOWN: | |
1252 | return m88e1540_set_fld(phydev, data); | |
a3bdfce7 | 1253 | case ETHTOOL_PHY_DOWNSHIFT: |
911af5e1 | 1254 | return m88e1011_set_downshift(phydev, *(const u8 *)data); |
69f42be8 HK |
1255 | default: |
1256 | return -EOPNOTSUPP; | |
1257 | } | |
1258 | } | |
1259 | ||
8cbcdc1a AL |
1260 | /* The VOD can be out of specification on link up. Poke an |
1261 | * undocumented register, in an undocumented page, with a magic value | |
1262 | * to fix this. | |
1263 | */ | |
1264 | static int m88e6390_errata(struct phy_device *phydev) | |
1265 | { | |
1266 | int err; | |
1267 | ||
1268 | err = phy_write(phydev, MII_BMCR, | |
1269 | BMCR_ANENABLE | BMCR_SPEED1000 | BMCR_FULLDPLX); | |
1270 | if (err) | |
1271 | return err; | |
1272 | ||
1273 | usleep_range(300, 400); | |
1274 | ||
1275 | err = phy_write_paged(phydev, 0xf8, 0x08, 0x36); | |
1276 | if (err) | |
1277 | return err; | |
1278 | ||
1279 | return genphy_soft_reset(phydev); | |
1280 | } | |
1281 | ||
1282 | static int m88e6390_config_aneg(struct phy_device *phydev) | |
1283 | { | |
1284 | int err; | |
1285 | ||
1286 | err = m88e6390_errata(phydev); | |
1287 | if (err) | |
1288 | return err; | |
1289 | ||
1290 | return m88e1510_config_aneg(phydev); | |
1291 | } | |
1292 | ||
6cfb3bcc | 1293 | /** |
ab9cb729 | 1294 | * fiber_lpa_mod_linkmode_lpa_t |
c0ec3c27 | 1295 | * @advertising: the linkmode advertisement settings |
6cfb3bcc CAC |
1296 | * @lpa: value of the MII_LPA register for fiber link |
1297 | * | |
ab9cb729 AL |
1298 | * A small helper function that translates MII_LPA bits to linkmode LP |
1299 | * advertisement settings. Other bits in advertising are left | |
1300 | * unchanged. | |
6cfb3bcc | 1301 | */ |
ab9cb729 | 1302 | static void fiber_lpa_mod_linkmode_lpa_t(unsigned long *advertising, u32 lpa) |
6cfb3bcc | 1303 | { |
ab9cb729 AL |
1304 | linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, |
1305 | advertising, lpa & LPA_FIBER_1000HALF); | |
1306 | ||
1307 | linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, | |
1308 | advertising, lpa & LPA_FIBER_1000FULL); | |
6cfb3bcc CAC |
1309 | } |
1310 | ||
1311 | /** | |
1312 | * marvell_update_link - update link status in real time in @phydev | |
1313 | * @phydev: target phy_device struct | |
1314 | * | |
1315 | * Description: Update the value in phydev->link to reflect the | |
1316 | * current link value. | |
1317 | */ | |
1318 | static int marvell_update_link(struct phy_device *phydev, int fiber) | |
1319 | { | |
1320 | int status; | |
1321 | ||
1322 | /* Use the generic register for copper link, or specific | |
0c3439bc AL |
1323 | * register for fiber case |
1324 | */ | |
6cfb3bcc CAC |
1325 | if (fiber) { |
1326 | status = phy_read(phydev, MII_M1011_PHY_STATUS); | |
1327 | if (status < 0) | |
1328 | return status; | |
1329 | ||
1330 | if ((status & REGISTER_LINK_STATUS) == 0) | |
1331 | phydev->link = 0; | |
1332 | else | |
1333 | phydev->link = 1; | |
1334 | } else { | |
1335 | return genphy_update_link(phydev); | |
1336 | } | |
1337 | ||
1338 | return 0; | |
1339 | } | |
1340 | ||
e1dde8dc AL |
1341 | static int marvell_read_status_page_an(struct phy_device *phydev, |
1342 | int fiber) | |
1343 | { | |
1344 | int status; | |
1345 | int lpa; | |
1346 | int lpagb; | |
e1dde8dc AL |
1347 | |
1348 | status = phy_read(phydev, MII_M1011_PHY_STATUS); | |
1349 | if (status < 0) | |
1350 | return status; | |
1351 | ||
1352 | lpa = phy_read(phydev, MII_LPA); | |
1353 | if (lpa < 0) | |
1354 | return lpa; | |
1355 | ||
1356 | lpagb = phy_read(phydev, MII_STAT1000); | |
1357 | if (lpagb < 0) | |
1358 | return lpagb; | |
1359 | ||
e1dde8dc AL |
1360 | if (status & MII_M1011_PHY_STATUS_FULLDUPLEX) |
1361 | phydev->duplex = DUPLEX_FULL; | |
1362 | else | |
1363 | phydev->duplex = DUPLEX_HALF; | |
1364 | ||
1365 | status = status & MII_M1011_PHY_STATUS_SPD_MASK; | |
1366 | phydev->pause = 0; | |
1367 | phydev->asym_pause = 0; | |
1368 | ||
1369 | switch (status) { | |
1370 | case MII_M1011_PHY_STATUS_1000: | |
1371 | phydev->speed = SPEED_1000; | |
1372 | break; | |
1373 | ||
1374 | case MII_M1011_PHY_STATUS_100: | |
1375 | phydev->speed = SPEED_100; | |
1376 | break; | |
1377 | ||
1378 | default: | |
1379 | phydev->speed = SPEED_10; | |
1380 | break; | |
1381 | } | |
1382 | ||
1383 | if (!fiber) { | |
c0ec3c27 | 1384 | mii_lpa_to_linkmode_lpa_t(phydev->lp_advertising, lpa); |
78a24df3 | 1385 | mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, lpagb); |
e1dde8dc | 1386 | |
af006240 | 1387 | phy_resolve_aneg_pause(phydev); |
e1dde8dc AL |
1388 | } else { |
1389 | /* The fiber link is only 1000M capable */ | |
ab9cb729 | 1390 | fiber_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa); |
e1dde8dc AL |
1391 | |
1392 | if (phydev->duplex == DUPLEX_FULL) { | |
1393 | if (!(lpa & LPA_PAUSE_FIBER)) { | |
1394 | phydev->pause = 0; | |
1395 | phydev->asym_pause = 0; | |
1396 | } else if ((lpa & LPA_PAUSE_ASYM_FIBER)) { | |
1397 | phydev->pause = 1; | |
1398 | phydev->asym_pause = 1; | |
1399 | } else { | |
1400 | phydev->pause = 1; | |
1401 | phydev->asym_pause = 0; | |
1402 | } | |
1403 | } | |
1404 | } | |
1405 | return 0; | |
1406 | } | |
1407 | ||
1408 | static int marvell_read_status_page_fixed(struct phy_device *phydev) | |
1409 | { | |
0efc286a | 1410 | int err; |
e1dde8dc | 1411 | |
0efc286a RK |
1412 | err = genphy_read_status_fixed(phydev); |
1413 | if (err < 0) | |
1414 | return err; | |
e1dde8dc AL |
1415 | |
1416 | phydev->pause = 0; | |
1417 | phydev->asym_pause = 0; | |
c0ec3c27 | 1418 | linkmode_zero(phydev->lp_advertising); |
e1dde8dc AL |
1419 | |
1420 | return 0; | |
1421 | } | |
1422 | ||
6cfb3bcc | 1423 | /* marvell_read_status_page |
be937f1f | 1424 | * |
f0c88f9c | 1425 | * Description: |
be937f1f AS |
1426 | * Check the link, then figure out the current state |
1427 | * by comparing what we advertise with what the link partner | |
1428 | * advertises. Start by checking the gigabit possibilities, | |
1429 | * then move on to 10/100. | |
1430 | */ | |
6cfb3bcc | 1431 | static int marvell_read_status_page(struct phy_device *phydev, int page) |
be937f1f | 1432 | { |
6cfb3bcc | 1433 | int fiber; |
e1dde8dc | 1434 | int err; |
be937f1f | 1435 | |
6cfb3bcc | 1436 | /* Detect and update the link, but return if there |
0c3439bc AL |
1437 | * was an error |
1438 | */ | |
52295666 | 1439 | if (page == MII_MARVELL_FIBER_PAGE) |
6cfb3bcc CAC |
1440 | fiber = 1; |
1441 | else | |
1442 | fiber = 0; | |
1443 | ||
1444 | err = marvell_update_link(phydev, fiber); | |
be937f1f AS |
1445 | if (err) |
1446 | return err; | |
1447 | ||
e1dde8dc AL |
1448 | if (phydev->autoneg == AUTONEG_ENABLE) |
1449 | err = marvell_read_status_page_an(phydev, fiber); | |
1450 | else | |
1451 | err = marvell_read_status_page_fixed(phydev); | |
be937f1f | 1452 | |
e1dde8dc | 1453 | return err; |
be937f1f AS |
1454 | } |
1455 | ||
6cfb3bcc CAC |
1456 | /* marvell_read_status |
1457 | * | |
1458 | * Some Marvell's phys have two modes: fiber and copper. | |
1459 | * Both need status checked. | |
1460 | * Description: | |
1461 | * First, check the fiber link and status. | |
1462 | * If the fiber link is down, check the copper link and status which | |
1463 | * will be the default value if both link are down. | |
1464 | */ | |
1465 | static int marvell_read_status(struct phy_device *phydev) | |
1466 | { | |
1467 | int err; | |
1468 | ||
1469 | /* Check the fiber mode first */ | |
3c1bcc86 AL |
1470 | if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, |
1471 | phydev->supported) && | |
a13c0652 | 1472 | phydev->interface != PHY_INTERFACE_MODE_SGMII) { |
52295666 | 1473 | err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE); |
6cfb3bcc CAC |
1474 | if (err < 0) |
1475 | goto error; | |
1476 | ||
52295666 | 1477 | err = marvell_read_status_page(phydev, MII_MARVELL_FIBER_PAGE); |
6cfb3bcc CAC |
1478 | if (err < 0) |
1479 | goto error; | |
1480 | ||
0c3439bc AL |
1481 | /* If the fiber link is up, it is the selected and |
1482 | * used link. In this case, we need to stay in the | |
1483 | * fiber page. Please to be careful about that, avoid | |
1484 | * to restore Copper page in other functions which | |
1485 | * could break the behaviour for some fiber phy like | |
1486 | * 88E1512. | |
1487 | */ | |
6cfb3bcc CAC |
1488 | if (phydev->link) |
1489 | return 0; | |
1490 | ||
1491 | /* If fiber link is down, check and save copper mode state */ | |
52295666 | 1492 | err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); |
6cfb3bcc CAC |
1493 | if (err < 0) |
1494 | goto error; | |
1495 | } | |
1496 | ||
52295666 | 1497 | return marvell_read_status_page(phydev, MII_MARVELL_COPPER_PAGE); |
6cfb3bcc CAC |
1498 | |
1499 | error: | |
52295666 | 1500 | marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); |
6cfb3bcc CAC |
1501 | return err; |
1502 | } | |
3758be3d CAC |
1503 | |
1504 | /* marvell_suspend | |
1505 | * | |
1506 | * Some Marvell's phys have two modes: fiber and copper. | |
1507 | * Both need to be suspended | |
1508 | */ | |
1509 | static int marvell_suspend(struct phy_device *phydev) | |
1510 | { | |
1511 | int err; | |
1512 | ||
1513 | /* Suspend the fiber mode first */ | |
3c1bcc86 AL |
1514 | if (!linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, |
1515 | phydev->supported)) { | |
52295666 | 1516 | err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE); |
3758be3d CAC |
1517 | if (err < 0) |
1518 | goto error; | |
1519 | ||
1520 | /* With the page set, use the generic suspend */ | |
1521 | err = genphy_suspend(phydev); | |
1522 | if (err < 0) | |
1523 | goto error; | |
1524 | ||
1525 | /* Then, the copper link */ | |
52295666 | 1526 | err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); |
3758be3d CAC |
1527 | if (err < 0) |
1528 | goto error; | |
1529 | } | |
1530 | ||
1531 | /* With the page set, use the generic suspend */ | |
1532 | return genphy_suspend(phydev); | |
1533 | ||
1534 | error: | |
52295666 | 1535 | marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); |
3758be3d CAC |
1536 | return err; |
1537 | } | |
1538 | ||
1539 | /* marvell_resume | |
1540 | * | |
1541 | * Some Marvell's phys have two modes: fiber and copper. | |
1542 | * Both need to be resumed | |
1543 | */ | |
1544 | static int marvell_resume(struct phy_device *phydev) | |
1545 | { | |
1546 | int err; | |
1547 | ||
1548 | /* Resume the fiber mode first */ | |
3c1bcc86 AL |
1549 | if (!linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, |
1550 | phydev->supported)) { | |
52295666 | 1551 | err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE); |
3758be3d CAC |
1552 | if (err < 0) |
1553 | goto error; | |
1554 | ||
1555 | /* With the page set, use the generic resume */ | |
1556 | err = genphy_resume(phydev); | |
1557 | if (err < 0) | |
1558 | goto error; | |
1559 | ||
1560 | /* Then, the copper link */ | |
52295666 | 1561 | err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); |
3758be3d CAC |
1562 | if (err < 0) |
1563 | goto error; | |
1564 | } | |
1565 | ||
1566 | /* With the page set, use the generic resume */ | |
1567 | return genphy_resume(phydev); | |
1568 | ||
1569 | error: | |
52295666 | 1570 | marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); |
3758be3d CAC |
1571 | return err; |
1572 | } | |
1573 | ||
6b358aed SH |
1574 | static int marvell_aneg_done(struct phy_device *phydev) |
1575 | { | |
1576 | int retval = phy_read(phydev, MII_M1011_PHY_STATUS); | |
e69d9ed4 | 1577 | |
6b358aed SH |
1578 | return (retval < 0) ? retval : (retval & MII_M1011_PHY_STATUS_RESOLVED); |
1579 | } | |
1580 | ||
dcd07be3 AG |
1581 | static int m88e1121_did_interrupt(struct phy_device *phydev) |
1582 | { | |
1583 | int imask; | |
1584 | ||
1585 | imask = phy_read(phydev, MII_M1011_IEVENT); | |
1586 | ||
1587 | if (imask & MII_M1011_IMASK_INIT) | |
1588 | return 1; | |
1589 | ||
1590 | return 0; | |
1591 | } | |
1592 | ||
23beb38f AL |
1593 | static void m88e1318_get_wol(struct phy_device *phydev, |
1594 | struct ethtool_wolinfo *wol) | |
3871c387 | 1595 | { |
424ca4c5 RK |
1596 | int oldpage, ret = 0; |
1597 | ||
3871c387 MS |
1598 | wol->supported = WAKE_MAGIC; |
1599 | wol->wolopts = 0; | |
1600 | ||
424ca4c5 RK |
1601 | oldpage = phy_select_page(phydev, MII_MARVELL_WOL_PAGE); |
1602 | if (oldpage < 0) | |
1603 | goto error; | |
3871c387 | 1604 | |
424ca4c5 RK |
1605 | ret = __phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL); |
1606 | if (ret & MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE) | |
3871c387 MS |
1607 | wol->wolopts |= WAKE_MAGIC; |
1608 | ||
424ca4c5 RK |
1609 | error: |
1610 | phy_restore_page(phydev, oldpage, ret); | |
3871c387 MS |
1611 | } |
1612 | ||
23beb38f AL |
1613 | static int m88e1318_set_wol(struct phy_device *phydev, |
1614 | struct ethtool_wolinfo *wol) | |
3871c387 | 1615 | { |
424ca4c5 | 1616 | int err = 0, oldpage; |
3871c387 | 1617 | |
424ca4c5 RK |
1618 | oldpage = phy_save_page(phydev); |
1619 | if (oldpage < 0) | |
1620 | goto error; | |
3871c387 MS |
1621 | |
1622 | if (wol->wolopts & WAKE_MAGIC) { | |
1623 | /* Explicitly switch to page 0x00, just to be sure */ | |
424ca4c5 | 1624 | err = marvell_write_page(phydev, MII_MARVELL_COPPER_PAGE); |
3871c387 | 1625 | if (err < 0) |
424ca4c5 | 1626 | goto error; |
3871c387 | 1627 | |
b6a930fa JH |
1628 | /* If WOL event happened once, the LED[2] interrupt pin |
1629 | * will not be cleared unless we reading the interrupt status | |
1630 | * register. If interrupts are in use, the normal interrupt | |
1631 | * handling will clear the WOL event. Clear the WOL event | |
1632 | * before enabling it if !phy_interrupt_is_valid() | |
1633 | */ | |
1634 | if (!phy_interrupt_is_valid(phydev)) | |
e0a7328f | 1635 | __phy_read(phydev, MII_M1011_IEVENT); |
b6a930fa | 1636 | |
3871c387 | 1637 | /* Enable the WOL interrupt */ |
424ca4c5 RK |
1638 | err = __phy_modify(phydev, MII_88E1318S_PHY_CSIER, 0, |
1639 | MII_88E1318S_PHY_CSIER_WOL_EIE); | |
3871c387 | 1640 | if (err < 0) |
424ca4c5 | 1641 | goto error; |
3871c387 | 1642 | |
424ca4c5 | 1643 | err = marvell_write_page(phydev, MII_MARVELL_LED_PAGE); |
3871c387 | 1644 | if (err < 0) |
424ca4c5 | 1645 | goto error; |
3871c387 MS |
1646 | |
1647 | /* Setup LED[2] as interrupt pin (active low) */ | |
424ca4c5 | 1648 | err = __phy_modify(phydev, MII_88E1318S_PHY_LED_TCR, |
f102852f | 1649 | MII_88E1318S_PHY_LED_TCR_FORCE_INT, |
424ca4c5 RK |
1650 | MII_88E1318S_PHY_LED_TCR_INTn_ENABLE | |
1651 | MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW); | |
3871c387 | 1652 | if (err < 0) |
424ca4c5 | 1653 | goto error; |
3871c387 | 1654 | |
424ca4c5 | 1655 | err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE); |
3871c387 | 1656 | if (err < 0) |
424ca4c5 | 1657 | goto error; |
3871c387 MS |
1658 | |
1659 | /* Store the device address for the magic packet */ | |
424ca4c5 | 1660 | err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2, |
3871c387 MS |
1661 | ((phydev->attached_dev->dev_addr[5] << 8) | |
1662 | phydev->attached_dev->dev_addr[4])); | |
1663 | if (err < 0) | |
424ca4c5 RK |
1664 | goto error; |
1665 | err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1, | |
3871c387 MS |
1666 | ((phydev->attached_dev->dev_addr[3] << 8) | |
1667 | phydev->attached_dev->dev_addr[2])); | |
1668 | if (err < 0) | |
424ca4c5 RK |
1669 | goto error; |
1670 | err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0, | |
3871c387 MS |
1671 | ((phydev->attached_dev->dev_addr[1] << 8) | |
1672 | phydev->attached_dev->dev_addr[0])); | |
1673 | if (err < 0) | |
424ca4c5 | 1674 | goto error; |
3871c387 MS |
1675 | |
1676 | /* Clear WOL status and enable magic packet matching */ | |
424ca4c5 RK |
1677 | err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL, 0, |
1678 | MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS | | |
1679 | MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE); | |
3871c387 | 1680 | if (err < 0) |
424ca4c5 | 1681 | goto error; |
3871c387 | 1682 | } else { |
424ca4c5 | 1683 | err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE); |
3871c387 | 1684 | if (err < 0) |
424ca4c5 | 1685 | goto error; |
3871c387 MS |
1686 | |
1687 | /* Clear WOL status and disable magic packet matching */ | |
424ca4c5 | 1688 | err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL, |
f102852f | 1689 | MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE, |
424ca4c5 | 1690 | MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS); |
3871c387 | 1691 | if (err < 0) |
424ca4c5 | 1692 | goto error; |
3871c387 MS |
1693 | } |
1694 | ||
424ca4c5 RK |
1695 | error: |
1696 | return phy_restore_page(phydev, oldpage, err); | |
3871c387 MS |
1697 | } |
1698 | ||
d2fa47d9 AL |
1699 | static int marvell_get_sset_count(struct phy_device *phydev) |
1700 | { | |
3c1bcc86 AL |
1701 | if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, |
1702 | phydev->supported)) | |
2170fef7 CAC |
1703 | return ARRAY_SIZE(marvell_hw_stats); |
1704 | else | |
1705 | return ARRAY_SIZE(marvell_hw_stats) - NB_FIBER_STATS; | |
d2fa47d9 AL |
1706 | } |
1707 | ||
1708 | static void marvell_get_strings(struct phy_device *phydev, u8 *data) | |
1709 | { | |
fdfdf867 | 1710 | int count = marvell_get_sset_count(phydev); |
d2fa47d9 AL |
1711 | int i; |
1712 | ||
fdfdf867 | 1713 | for (i = 0; i < count; i++) { |
98409b2b FF |
1714 | strlcpy(data + i * ETH_GSTRING_LEN, |
1715 | marvell_hw_stats[i].string, ETH_GSTRING_LEN); | |
d2fa47d9 AL |
1716 | } |
1717 | } | |
1718 | ||
d2fa47d9 AL |
1719 | static u64 marvell_get_stat(struct phy_device *phydev, int i) |
1720 | { | |
1721 | struct marvell_hw_stat stat = marvell_hw_stats[i]; | |
1722 | struct marvell_priv *priv = phydev->priv; | |
424ca4c5 | 1723 | int val; |
321b4d4b | 1724 | u64 ret; |
d2fa47d9 | 1725 | |
424ca4c5 | 1726 | val = phy_read_paged(phydev, stat.page, stat.reg); |
d2fa47d9 | 1727 | if (val < 0) { |
6c3442f5 | 1728 | ret = U64_MAX; |
d2fa47d9 AL |
1729 | } else { |
1730 | val = val & ((1 << stat.bits) - 1); | |
1731 | priv->stats[i] += val; | |
321b4d4b | 1732 | ret = priv->stats[i]; |
d2fa47d9 AL |
1733 | } |
1734 | ||
321b4d4b | 1735 | return ret; |
d2fa47d9 AL |
1736 | } |
1737 | ||
1738 | static void marvell_get_stats(struct phy_device *phydev, | |
1739 | struct ethtool_stats *stats, u64 *data) | |
1740 | { | |
fdfdf867 | 1741 | int count = marvell_get_sset_count(phydev); |
d2fa47d9 AL |
1742 | int i; |
1743 | ||
fdfdf867 | 1744 | for (i = 0; i < count; i++) |
d2fa47d9 AL |
1745 | data[i] = marvell_get_stat(phydev, i); |
1746 | } | |
1747 | ||
0b04680f AL |
1748 | #ifdef CONFIG_HWMON |
1749 | static int m88e1121_get_temp(struct phy_device *phydev, long *temp) | |
1750 | { | |
975b388c | 1751 | int oldpage; |
424ca4c5 | 1752 | int ret = 0; |
0b04680f AL |
1753 | int val; |
1754 | ||
1755 | *temp = 0; | |
1756 | ||
424ca4c5 RK |
1757 | oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE); |
1758 | if (oldpage < 0) | |
1759 | goto error; | |
975b388c | 1760 | |
0b04680f | 1761 | /* Enable temperature sensor */ |
424ca4c5 | 1762 | ret = __phy_read(phydev, MII_88E1121_MISC_TEST); |
0b04680f AL |
1763 | if (ret < 0) |
1764 | goto error; | |
1765 | ||
424ca4c5 RK |
1766 | ret = __phy_write(phydev, MII_88E1121_MISC_TEST, |
1767 | ret | MII_88E1121_MISC_TEST_TEMP_SENSOR_EN); | |
0b04680f AL |
1768 | if (ret < 0) |
1769 | goto error; | |
1770 | ||
1771 | /* Wait for temperature to stabilize */ | |
1772 | usleep_range(10000, 12000); | |
1773 | ||
424ca4c5 | 1774 | val = __phy_read(phydev, MII_88E1121_MISC_TEST); |
0b04680f AL |
1775 | if (val < 0) { |
1776 | ret = val; | |
1777 | goto error; | |
1778 | } | |
1779 | ||
1780 | /* Disable temperature sensor */ | |
424ca4c5 RK |
1781 | ret = __phy_write(phydev, MII_88E1121_MISC_TEST, |
1782 | ret & ~MII_88E1121_MISC_TEST_TEMP_SENSOR_EN); | |
0b04680f AL |
1783 | if (ret < 0) |
1784 | goto error; | |
1785 | ||
1786 | *temp = ((val & MII_88E1121_MISC_TEST_TEMP_MASK) - 5) * 5000; | |
1787 | ||
1788 | error: | |
424ca4c5 | 1789 | return phy_restore_page(phydev, oldpage, ret); |
0b04680f AL |
1790 | } |
1791 | ||
1792 | static int m88e1121_hwmon_read(struct device *dev, | |
1793 | enum hwmon_sensor_types type, | |
1794 | u32 attr, int channel, long *temp) | |
1795 | { | |
1796 | struct phy_device *phydev = dev_get_drvdata(dev); | |
1797 | int err; | |
1798 | ||
1799 | switch (attr) { | |
1800 | case hwmon_temp_input: | |
1801 | err = m88e1121_get_temp(phydev, temp); | |
1802 | break; | |
1803 | default: | |
1804 | return -EOPNOTSUPP; | |
1805 | } | |
1806 | ||
1807 | return err; | |
1808 | } | |
1809 | ||
1810 | static umode_t m88e1121_hwmon_is_visible(const void *data, | |
1811 | enum hwmon_sensor_types type, | |
1812 | u32 attr, int channel) | |
1813 | { | |
1814 | if (type != hwmon_temp) | |
1815 | return 0; | |
1816 | ||
1817 | switch (attr) { | |
1818 | case hwmon_temp_input: | |
1819 | return 0444; | |
1820 | default: | |
1821 | return 0; | |
1822 | } | |
1823 | } | |
1824 | ||
1825 | static u32 m88e1121_hwmon_chip_config[] = { | |
1826 | HWMON_C_REGISTER_TZ, | |
1827 | 0 | |
1828 | }; | |
1829 | ||
1830 | static const struct hwmon_channel_info m88e1121_hwmon_chip = { | |
1831 | .type = hwmon_chip, | |
1832 | .config = m88e1121_hwmon_chip_config, | |
1833 | }; | |
1834 | ||
1835 | static u32 m88e1121_hwmon_temp_config[] = { | |
1836 | HWMON_T_INPUT, | |
1837 | 0 | |
1838 | }; | |
1839 | ||
1840 | static const struct hwmon_channel_info m88e1121_hwmon_temp = { | |
1841 | .type = hwmon_temp, | |
1842 | .config = m88e1121_hwmon_temp_config, | |
1843 | }; | |
1844 | ||
1845 | static const struct hwmon_channel_info *m88e1121_hwmon_info[] = { | |
1846 | &m88e1121_hwmon_chip, | |
1847 | &m88e1121_hwmon_temp, | |
1848 | NULL | |
1849 | }; | |
1850 | ||
1851 | static const struct hwmon_ops m88e1121_hwmon_hwmon_ops = { | |
1852 | .is_visible = m88e1121_hwmon_is_visible, | |
1853 | .read = m88e1121_hwmon_read, | |
1854 | }; | |
1855 | ||
1856 | static const struct hwmon_chip_info m88e1121_hwmon_chip_info = { | |
1857 | .ops = &m88e1121_hwmon_hwmon_ops, | |
1858 | .info = m88e1121_hwmon_info, | |
1859 | }; | |
1860 | ||
1861 | static int m88e1510_get_temp(struct phy_device *phydev, long *temp) | |
1862 | { | |
1863 | int ret; | |
1864 | ||
1865 | *temp = 0; | |
1866 | ||
424ca4c5 RK |
1867 | ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE, |
1868 | MII_88E1510_TEMP_SENSOR); | |
0b04680f | 1869 | if (ret < 0) |
424ca4c5 | 1870 | return ret; |
0b04680f AL |
1871 | |
1872 | *temp = ((ret & MII_88E1510_TEMP_SENSOR_MASK) - 25) * 1000; | |
1873 | ||
424ca4c5 | 1874 | return 0; |
0b04680f AL |
1875 | } |
1876 | ||
f0a45816 | 1877 | static int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp) |
0b04680f AL |
1878 | { |
1879 | int ret; | |
1880 | ||
1881 | *temp = 0; | |
1882 | ||
424ca4c5 RK |
1883 | ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE, |
1884 | MII_88E1121_MISC_TEST); | |
0b04680f | 1885 | if (ret < 0) |
424ca4c5 | 1886 | return ret; |
0b04680f AL |
1887 | |
1888 | *temp = (((ret & MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) >> | |
1889 | MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT) * 5) - 25; | |
1890 | /* convert to mC */ | |
1891 | *temp *= 1000; | |
1892 | ||
424ca4c5 | 1893 | return 0; |
0b04680f AL |
1894 | } |
1895 | ||
f0a45816 | 1896 | static int m88e1510_set_temp_critical(struct phy_device *phydev, long temp) |
0b04680f | 1897 | { |
0b04680f AL |
1898 | temp = temp / 1000; |
1899 | temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f); | |
0b04680f | 1900 | |
424ca4c5 RK |
1901 | return phy_modify_paged(phydev, MII_MARVELL_MISC_TEST_PAGE, |
1902 | MII_88E1121_MISC_TEST, | |
1903 | MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK, | |
1904 | temp << MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT); | |
0b04680f AL |
1905 | } |
1906 | ||
f0a45816 | 1907 | static int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm) |
0b04680f AL |
1908 | { |
1909 | int ret; | |
1910 | ||
1911 | *alarm = false; | |
1912 | ||
424ca4c5 RK |
1913 | ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE, |
1914 | MII_88E1121_MISC_TEST); | |
0b04680f | 1915 | if (ret < 0) |
424ca4c5 | 1916 | return ret; |
0b04680f | 1917 | |
424ca4c5 | 1918 | *alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ); |
0b04680f | 1919 | |
424ca4c5 | 1920 | return 0; |
0b04680f AL |
1921 | } |
1922 | ||
1923 | static int m88e1510_hwmon_read(struct device *dev, | |
1924 | enum hwmon_sensor_types type, | |
1925 | u32 attr, int channel, long *temp) | |
1926 | { | |
1927 | struct phy_device *phydev = dev_get_drvdata(dev); | |
1928 | int err; | |
1929 | ||
1930 | switch (attr) { | |
1931 | case hwmon_temp_input: | |
1932 | err = m88e1510_get_temp(phydev, temp); | |
1933 | break; | |
1934 | case hwmon_temp_crit: | |
1935 | err = m88e1510_get_temp_critical(phydev, temp); | |
1936 | break; | |
1937 | case hwmon_temp_max_alarm: | |
1938 | err = m88e1510_get_temp_alarm(phydev, temp); | |
1939 | break; | |
1940 | default: | |
1941 | return -EOPNOTSUPP; | |
1942 | } | |
1943 | ||
1944 | return err; | |
1945 | } | |
1946 | ||
1947 | static int m88e1510_hwmon_write(struct device *dev, | |
1948 | enum hwmon_sensor_types type, | |
1949 | u32 attr, int channel, long temp) | |
1950 | { | |
1951 | struct phy_device *phydev = dev_get_drvdata(dev); | |
1952 | int err; | |
1953 | ||
1954 | switch (attr) { | |
1955 | case hwmon_temp_crit: | |
1956 | err = m88e1510_set_temp_critical(phydev, temp); | |
1957 | break; | |
1958 | default: | |
1959 | return -EOPNOTSUPP; | |
1960 | } | |
1961 | return err; | |
1962 | } | |
1963 | ||
1964 | static umode_t m88e1510_hwmon_is_visible(const void *data, | |
1965 | enum hwmon_sensor_types type, | |
1966 | u32 attr, int channel) | |
1967 | { | |
1968 | if (type != hwmon_temp) | |
1969 | return 0; | |
1970 | ||
1971 | switch (attr) { | |
1972 | case hwmon_temp_input: | |
1973 | case hwmon_temp_max_alarm: | |
1974 | return 0444; | |
1975 | case hwmon_temp_crit: | |
1976 | return 0644; | |
1977 | default: | |
1978 | return 0; | |
1979 | } | |
1980 | } | |
1981 | ||
1982 | static u32 m88e1510_hwmon_temp_config[] = { | |
1983 | HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM, | |
1984 | 0 | |
1985 | }; | |
1986 | ||
1987 | static const struct hwmon_channel_info m88e1510_hwmon_temp = { | |
1988 | .type = hwmon_temp, | |
1989 | .config = m88e1510_hwmon_temp_config, | |
1990 | }; | |
1991 | ||
1992 | static const struct hwmon_channel_info *m88e1510_hwmon_info[] = { | |
1993 | &m88e1121_hwmon_chip, | |
1994 | &m88e1510_hwmon_temp, | |
1995 | NULL | |
1996 | }; | |
1997 | ||
1998 | static const struct hwmon_ops m88e1510_hwmon_hwmon_ops = { | |
1999 | .is_visible = m88e1510_hwmon_is_visible, | |
2000 | .read = m88e1510_hwmon_read, | |
2001 | .write = m88e1510_hwmon_write, | |
2002 | }; | |
2003 | ||
2004 | static const struct hwmon_chip_info m88e1510_hwmon_chip_info = { | |
2005 | .ops = &m88e1510_hwmon_hwmon_ops, | |
2006 | .info = m88e1510_hwmon_info, | |
2007 | }; | |
2008 | ||
fee2d546 AL |
2009 | static int m88e6390_get_temp(struct phy_device *phydev, long *temp) |
2010 | { | |
2011 | int sum = 0; | |
2012 | int oldpage; | |
2013 | int ret = 0; | |
2014 | int i; | |
2015 | ||
2016 | *temp = 0; | |
2017 | ||
2018 | oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE); | |
2019 | if (oldpage < 0) | |
2020 | goto error; | |
2021 | ||
2022 | /* Enable temperature sensor */ | |
2023 | ret = __phy_read(phydev, MII_88E6390_MISC_TEST); | |
2024 | if (ret < 0) | |
2025 | goto error; | |
2026 | ||
2027 | ret = ret & ~MII_88E6390_MISC_TEST_SAMPLE_MASK; | |
2028 | ret |= MII_88E6390_MISC_TEST_SAMPLE_ENABLE | | |
2029 | MII_88E6390_MISC_TEST_SAMPLE_1S; | |
2030 | ||
2031 | ret = __phy_write(phydev, MII_88E6390_MISC_TEST, ret); | |
2032 | if (ret < 0) | |
2033 | goto error; | |
2034 | ||
2035 | /* Wait for temperature to stabilize */ | |
2036 | usleep_range(10000, 12000); | |
2037 | ||
2038 | /* Reading the temperature sense has an errata. You need to read | |
2039 | * a number of times and take an average. | |
2040 | */ | |
2041 | for (i = 0; i < MII_88E6390_TEMP_SENSOR_SAMPLES; i++) { | |
2042 | ret = __phy_read(phydev, MII_88E6390_TEMP_SENSOR); | |
2043 | if (ret < 0) | |
2044 | goto error; | |
2045 | sum += ret & MII_88E6390_TEMP_SENSOR_MASK; | |
2046 | } | |
2047 | ||
2048 | sum /= MII_88E6390_TEMP_SENSOR_SAMPLES; | |
2049 | *temp = (sum - 75) * 1000; | |
2050 | ||
2051 | /* Disable temperature sensor */ | |
2052 | ret = __phy_read(phydev, MII_88E6390_MISC_TEST); | |
2053 | if (ret < 0) | |
2054 | goto error; | |
2055 | ||
2056 | ret = ret & ~MII_88E6390_MISC_TEST_SAMPLE_MASK; | |
2057 | ret |= MII_88E6390_MISC_TEST_SAMPLE_DISABLE; | |
2058 | ||
2059 | ret = __phy_write(phydev, MII_88E6390_MISC_TEST, ret); | |
2060 | ||
2061 | error: | |
2062 | phy_restore_page(phydev, oldpage, ret); | |
2063 | ||
2064 | return ret; | |
2065 | } | |
2066 | ||
2067 | static int m88e6390_hwmon_read(struct device *dev, | |
2068 | enum hwmon_sensor_types type, | |
2069 | u32 attr, int channel, long *temp) | |
2070 | { | |
2071 | struct phy_device *phydev = dev_get_drvdata(dev); | |
2072 | int err; | |
2073 | ||
2074 | switch (attr) { | |
2075 | case hwmon_temp_input: | |
2076 | err = m88e6390_get_temp(phydev, temp); | |
2077 | break; | |
2078 | default: | |
2079 | return -EOPNOTSUPP; | |
2080 | } | |
2081 | ||
2082 | return err; | |
2083 | } | |
2084 | ||
2085 | static umode_t m88e6390_hwmon_is_visible(const void *data, | |
2086 | enum hwmon_sensor_types type, | |
2087 | u32 attr, int channel) | |
2088 | { | |
2089 | if (type != hwmon_temp) | |
2090 | return 0; | |
2091 | ||
2092 | switch (attr) { | |
2093 | case hwmon_temp_input: | |
2094 | return 0444; | |
2095 | default: | |
2096 | return 0; | |
2097 | } | |
2098 | } | |
2099 | ||
2100 | static u32 m88e6390_hwmon_temp_config[] = { | |
2101 | HWMON_T_INPUT, | |
2102 | 0 | |
2103 | }; | |
2104 | ||
2105 | static const struct hwmon_channel_info m88e6390_hwmon_temp = { | |
2106 | .type = hwmon_temp, | |
2107 | .config = m88e6390_hwmon_temp_config, | |
2108 | }; | |
2109 | ||
2110 | static const struct hwmon_channel_info *m88e6390_hwmon_info[] = { | |
2111 | &m88e1121_hwmon_chip, | |
2112 | &m88e6390_hwmon_temp, | |
2113 | NULL | |
2114 | }; | |
2115 | ||
2116 | static const struct hwmon_ops m88e6390_hwmon_hwmon_ops = { | |
2117 | .is_visible = m88e6390_hwmon_is_visible, | |
2118 | .read = m88e6390_hwmon_read, | |
2119 | }; | |
2120 | ||
2121 | static const struct hwmon_chip_info m88e6390_hwmon_chip_info = { | |
2122 | .ops = &m88e6390_hwmon_hwmon_ops, | |
2123 | .info = m88e6390_hwmon_info, | |
2124 | }; | |
2125 | ||
0b04680f AL |
2126 | static int marvell_hwmon_name(struct phy_device *phydev) |
2127 | { | |
2128 | struct marvell_priv *priv = phydev->priv; | |
2129 | struct device *dev = &phydev->mdio.dev; | |
2130 | const char *devname = dev_name(dev); | |
2131 | size_t len = strlen(devname); | |
2132 | int i, j; | |
2133 | ||
2134 | priv->hwmon_name = devm_kzalloc(dev, len, GFP_KERNEL); | |
2135 | if (!priv->hwmon_name) | |
2136 | return -ENOMEM; | |
2137 | ||
2138 | for (i = j = 0; i < len && devname[i]; i++) { | |
2139 | if (isalnum(devname[i])) | |
2140 | priv->hwmon_name[j++] = devname[i]; | |
2141 | } | |
2142 | ||
2143 | return 0; | |
2144 | } | |
2145 | ||
2146 | static int marvell_hwmon_probe(struct phy_device *phydev, | |
2147 | const struct hwmon_chip_info *chip) | |
2148 | { | |
2149 | struct marvell_priv *priv = phydev->priv; | |
2150 | struct device *dev = &phydev->mdio.dev; | |
2151 | int err; | |
2152 | ||
2153 | err = marvell_hwmon_name(phydev); | |
2154 | if (err) | |
2155 | return err; | |
2156 | ||
2157 | priv->hwmon_dev = devm_hwmon_device_register_with_info( | |
2158 | dev, priv->hwmon_name, phydev, chip, NULL); | |
2159 | ||
2160 | return PTR_ERR_OR_ZERO(priv->hwmon_dev); | |
2161 | } | |
2162 | ||
2163 | static int m88e1121_hwmon_probe(struct phy_device *phydev) | |
2164 | { | |
2165 | return marvell_hwmon_probe(phydev, &m88e1121_hwmon_chip_info); | |
2166 | } | |
2167 | ||
2168 | static int m88e1510_hwmon_probe(struct phy_device *phydev) | |
2169 | { | |
2170 | return marvell_hwmon_probe(phydev, &m88e1510_hwmon_chip_info); | |
2171 | } | |
fee2d546 AL |
2172 | |
2173 | static int m88e6390_hwmon_probe(struct phy_device *phydev) | |
2174 | { | |
2175 | return marvell_hwmon_probe(phydev, &m88e6390_hwmon_chip_info); | |
2176 | } | |
0b04680f AL |
2177 | #else |
2178 | static int m88e1121_hwmon_probe(struct phy_device *phydev) | |
2179 | { | |
2180 | return 0; | |
2181 | } | |
2182 | ||
2183 | static int m88e1510_hwmon_probe(struct phy_device *phydev) | |
2184 | { | |
2185 | return 0; | |
2186 | } | |
fee2d546 AL |
2187 | |
2188 | static int m88e6390_hwmon_probe(struct phy_device *phydev) | |
2189 | { | |
2190 | return 0; | |
2191 | } | |
0b04680f AL |
2192 | #endif |
2193 | ||
d2fa47d9 AL |
2194 | static int marvell_probe(struct phy_device *phydev) |
2195 | { | |
2196 | struct marvell_priv *priv; | |
2197 | ||
e5a03bfd | 2198 | priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); |
d2fa47d9 AL |
2199 | if (!priv) |
2200 | return -ENOMEM; | |
2201 | ||
2202 | phydev->priv = priv; | |
2203 | ||
2204 | return 0; | |
2205 | } | |
2206 | ||
0b04680f AL |
2207 | static int m88e1121_probe(struct phy_device *phydev) |
2208 | { | |
2209 | int err; | |
2210 | ||
2211 | err = marvell_probe(phydev); | |
2212 | if (err) | |
2213 | return err; | |
2214 | ||
2215 | return m88e1121_hwmon_probe(phydev); | |
2216 | } | |
2217 | ||
2218 | static int m88e1510_probe(struct phy_device *phydev) | |
2219 | { | |
2220 | int err; | |
2221 | ||
2222 | err = marvell_probe(phydev); | |
2223 | if (err) | |
2224 | return err; | |
2225 | ||
2226 | return m88e1510_hwmon_probe(phydev); | |
2227 | } | |
2228 | ||
fee2d546 AL |
2229 | static int m88e6390_probe(struct phy_device *phydev) |
2230 | { | |
2231 | int err; | |
2232 | ||
2233 | err = marvell_probe(phydev); | |
2234 | if (err) | |
2235 | return err; | |
2236 | ||
2237 | return m88e6390_hwmon_probe(phydev); | |
2238 | } | |
2239 | ||
e5479239 OJ |
2240 | static struct phy_driver marvell_drivers[] = { |
2241 | { | |
2f495c39 BH |
2242 | .phy_id = MARVELL_PHY_ID_88E1101, |
2243 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
e5479239 | 2244 | .name = "Marvell 88E1101", |
dcdecdcf | 2245 | /* PHY_GBIT_FEATURES */ |
18702414 | 2246 | .probe = marvell_probe, |
79be1a1c | 2247 | .config_init = &marvell_config_init, |
f2899788 | 2248 | .config_aneg = &m88e1101_config_aneg, |
e5479239 OJ |
2249 | .ack_interrupt = &marvell_ack_interrupt, |
2250 | .config_intr = &marvell_config_intr, | |
0898b448 SH |
2251 | .resume = &genphy_resume, |
2252 | .suspend = &genphy_suspend, | |
424ca4c5 RK |
2253 | .read_page = marvell_read_page, |
2254 | .write_page = marvell_write_page, | |
d2fa47d9 AL |
2255 | .get_sset_count = marvell_get_sset_count, |
2256 | .get_strings = marvell_get_strings, | |
2257 | .get_stats = marvell_get_stats, | |
e5479239 | 2258 | }, |
85cfb534 | 2259 | { |
2f495c39 BH |
2260 | .phy_id = MARVELL_PHY_ID_88E1112, |
2261 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
85cfb534 | 2262 | .name = "Marvell 88E1112", |
dcdecdcf | 2263 | /* PHY_GBIT_FEATURES */ |
d2fa47d9 | 2264 | .probe = marvell_probe, |
85cfb534 OJ |
2265 | .config_init = &m88e1111_config_init, |
2266 | .config_aneg = &marvell_config_aneg, | |
85cfb534 OJ |
2267 | .ack_interrupt = &marvell_ack_interrupt, |
2268 | .config_intr = &marvell_config_intr, | |
0898b448 SH |
2269 | .resume = &genphy_resume, |
2270 | .suspend = &genphy_suspend, | |
424ca4c5 RK |
2271 | .read_page = marvell_read_page, |
2272 | .write_page = marvell_write_page, | |
d2fa47d9 AL |
2273 | .get_sset_count = marvell_get_sset_count, |
2274 | .get_strings = marvell_get_strings, | |
2275 | .get_stats = marvell_get_stats, | |
262caf47 HK |
2276 | .get_tunable = m88e1011_get_tunable, |
2277 | .set_tunable = m88e1011_set_tunable, | |
2278 | .link_change_notify = m88e1011_link_change_notify, | |
85cfb534 | 2279 | }, |
e5479239 | 2280 | { |
2f495c39 BH |
2281 | .phy_id = MARVELL_PHY_ID_88E1111, |
2282 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
e5479239 | 2283 | .name = "Marvell 88E1111", |
dcdecdcf | 2284 | /* PHY_GBIT_FEATURES */ |
d2fa47d9 | 2285 | .probe = marvell_probe, |
e5479239 | 2286 | .config_init = &m88e1111_config_init, |
d6ab9336 | 2287 | .config_aneg = &marvell_config_aneg, |
be937f1f | 2288 | .read_status = &marvell_read_status, |
e5479239 OJ |
2289 | .ack_interrupt = &marvell_ack_interrupt, |
2290 | .config_intr = &marvell_config_intr, | |
0898b448 SH |
2291 | .resume = &genphy_resume, |
2292 | .suspend = &genphy_suspend, | |
424ca4c5 RK |
2293 | .read_page = marvell_read_page, |
2294 | .write_page = marvell_write_page, | |
d2fa47d9 AL |
2295 | .get_sset_count = marvell_get_sset_count, |
2296 | .get_strings = marvell_get_strings, | |
2297 | .get_stats = marvell_get_stats, | |
5c6bc519 HK |
2298 | .get_tunable = m88e1111_get_tunable, |
2299 | .set_tunable = m88e1111_set_tunable, | |
2300 | .link_change_notify = m88e1011_link_change_notify, | |
e5479239 | 2301 | }, |
605f196e | 2302 | { |
2f495c39 BH |
2303 | .phy_id = MARVELL_PHY_ID_88E1118, |
2304 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
605f196e | 2305 | .name = "Marvell 88E1118", |
dcdecdcf | 2306 | /* PHY_GBIT_FEATURES */ |
d2fa47d9 | 2307 | .probe = marvell_probe, |
605f196e RM |
2308 | .config_init = &m88e1118_config_init, |
2309 | .config_aneg = &m88e1118_config_aneg, | |
605f196e RM |
2310 | .ack_interrupt = &marvell_ack_interrupt, |
2311 | .config_intr = &marvell_config_intr, | |
0898b448 SH |
2312 | .resume = &genphy_resume, |
2313 | .suspend = &genphy_suspend, | |
424ca4c5 RK |
2314 | .read_page = marvell_read_page, |
2315 | .write_page = marvell_write_page, | |
d2fa47d9 AL |
2316 | .get_sset_count = marvell_get_sset_count, |
2317 | .get_strings = marvell_get_strings, | |
2318 | .get_stats = marvell_get_stats, | |
605f196e | 2319 | }, |
140bc929 | 2320 | { |
2f495c39 BH |
2321 | .phy_id = MARVELL_PHY_ID_88E1121R, |
2322 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
140bc929 | 2323 | .name = "Marvell 88E1121R", |
dcdecdcf | 2324 | /* PHY_GBIT_FEATURES */ |
18702414 | 2325 | .probe = &m88e1121_probe, |
07777246 | 2326 | .config_init = &marvell_config_init, |
140bc929 SP |
2327 | .config_aneg = &m88e1121_config_aneg, |
2328 | .read_status = &marvell_read_status, | |
2329 | .ack_interrupt = &marvell_ack_interrupt, | |
2330 | .config_intr = &marvell_config_intr, | |
dcd07be3 | 2331 | .did_interrupt = &m88e1121_did_interrupt, |
0898b448 SH |
2332 | .resume = &genphy_resume, |
2333 | .suspend = &genphy_suspend, | |
424ca4c5 RK |
2334 | .read_page = marvell_read_page, |
2335 | .write_page = marvell_write_page, | |
d2fa47d9 AL |
2336 | .get_sset_count = marvell_get_sset_count, |
2337 | .get_strings = marvell_get_strings, | |
2338 | .get_stats = marvell_get_stats, | |
911af5e1 HK |
2339 | .get_tunable = m88e1011_get_tunable, |
2340 | .set_tunable = m88e1011_set_tunable, | |
2341 | .link_change_notify = m88e1011_link_change_notify, | |
140bc929 | 2342 | }, |
3ff1c259 | 2343 | { |
337ac9d5 | 2344 | .phy_id = MARVELL_PHY_ID_88E1318S, |
6ba74014 | 2345 | .phy_id_mask = MARVELL_PHY_ID_MASK, |
337ac9d5 | 2346 | .name = "Marvell 88E1318S", |
dcdecdcf | 2347 | /* PHY_GBIT_FEATURES */ |
d2fa47d9 | 2348 | .probe = marvell_probe, |
dd9a122a | 2349 | .config_init = &m88e1318_config_init, |
337ac9d5 | 2350 | .config_aneg = &m88e1318_config_aneg, |
3ff1c259 CC |
2351 | .read_status = &marvell_read_status, |
2352 | .ack_interrupt = &marvell_ack_interrupt, | |
2353 | .config_intr = &marvell_config_intr, | |
2354 | .did_interrupt = &m88e1121_did_interrupt, | |
3871c387 MS |
2355 | .get_wol = &m88e1318_get_wol, |
2356 | .set_wol = &m88e1318_set_wol, | |
0898b448 SH |
2357 | .resume = &genphy_resume, |
2358 | .suspend = &genphy_suspend, | |
424ca4c5 RK |
2359 | .read_page = marvell_read_page, |
2360 | .write_page = marvell_write_page, | |
d2fa47d9 AL |
2361 | .get_sset_count = marvell_get_sset_count, |
2362 | .get_strings = marvell_get_strings, | |
2363 | .get_stats = marvell_get_stats, | |
3ff1c259 | 2364 | }, |
e5479239 | 2365 | { |
2f495c39 BH |
2366 | .phy_id = MARVELL_PHY_ID_88E1145, |
2367 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
e5479239 | 2368 | .name = "Marvell 88E1145", |
dcdecdcf | 2369 | /* PHY_GBIT_FEATURES */ |
d2fa47d9 | 2370 | .probe = marvell_probe, |
e5479239 | 2371 | .config_init = &m88e1145_config_init, |
c505873e | 2372 | .config_aneg = &m88e1101_config_aneg, |
e5479239 OJ |
2373 | .read_status = &genphy_read_status, |
2374 | .ack_interrupt = &marvell_ack_interrupt, | |
2375 | .config_intr = &marvell_config_intr, | |
0898b448 SH |
2376 | .resume = &genphy_resume, |
2377 | .suspend = &genphy_suspend, | |
424ca4c5 RK |
2378 | .read_page = marvell_read_page, |
2379 | .write_page = marvell_write_page, | |
d2fa47d9 AL |
2380 | .get_sset_count = marvell_get_sset_count, |
2381 | .get_strings = marvell_get_strings, | |
2382 | .get_stats = marvell_get_stats, | |
a319fb52 HK |
2383 | .get_tunable = m88e1111_get_tunable, |
2384 | .set_tunable = m88e1111_set_tunable, | |
2385 | .link_change_notify = m88e1011_link_change_notify, | |
ac8c635a | 2386 | }, |
90600732 DD |
2387 | { |
2388 | .phy_id = MARVELL_PHY_ID_88E1149R, | |
2389 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
2390 | .name = "Marvell 88E1149R", | |
dcdecdcf | 2391 | /* PHY_GBIT_FEATURES */ |
d2fa47d9 | 2392 | .probe = marvell_probe, |
90600732 DD |
2393 | .config_init = &m88e1149_config_init, |
2394 | .config_aneg = &m88e1118_config_aneg, | |
90600732 DD |
2395 | .ack_interrupt = &marvell_ack_interrupt, |
2396 | .config_intr = &marvell_config_intr, | |
0898b448 SH |
2397 | .resume = &genphy_resume, |
2398 | .suspend = &genphy_suspend, | |
424ca4c5 RK |
2399 | .read_page = marvell_read_page, |
2400 | .write_page = marvell_write_page, | |
d2fa47d9 AL |
2401 | .get_sset_count = marvell_get_sset_count, |
2402 | .get_strings = marvell_get_strings, | |
2403 | .get_stats = marvell_get_stats, | |
90600732 | 2404 | }, |
ac8c635a | 2405 | { |
2f495c39 BH |
2406 | .phy_id = MARVELL_PHY_ID_88E1240, |
2407 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
ac8c635a | 2408 | .name = "Marvell 88E1240", |
dcdecdcf | 2409 | /* PHY_GBIT_FEATURES */ |
d2fa47d9 | 2410 | .probe = marvell_probe, |
ac8c635a OJ |
2411 | .config_init = &m88e1111_config_init, |
2412 | .config_aneg = &marvell_config_aneg, | |
ac8c635a OJ |
2413 | .ack_interrupt = &marvell_ack_interrupt, |
2414 | .config_intr = &marvell_config_intr, | |
0898b448 SH |
2415 | .resume = &genphy_resume, |
2416 | .suspend = &genphy_suspend, | |
424ca4c5 RK |
2417 | .read_page = marvell_read_page, |
2418 | .write_page = marvell_write_page, | |
d2fa47d9 AL |
2419 | .get_sset_count = marvell_get_sset_count, |
2420 | .get_strings = marvell_get_strings, | |
2421 | .get_stats = marvell_get_stats, | |
ac8c635a | 2422 | }, |
3da09a51 MS |
2423 | { |
2424 | .phy_id = MARVELL_PHY_ID_88E1116R, | |
2425 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
2426 | .name = "Marvell 88E1116R", | |
dcdecdcf | 2427 | /* PHY_GBIT_FEATURES */ |
d2fa47d9 | 2428 | .probe = marvell_probe, |
3da09a51 | 2429 | .config_init = &m88e1116r_config_init, |
3da09a51 MS |
2430 | .ack_interrupt = &marvell_ack_interrupt, |
2431 | .config_intr = &marvell_config_intr, | |
0898b448 SH |
2432 | .resume = &genphy_resume, |
2433 | .suspend = &genphy_suspend, | |
424ca4c5 RK |
2434 | .read_page = marvell_read_page, |
2435 | .write_page = marvell_write_page, | |
d2fa47d9 AL |
2436 | .get_sset_count = marvell_get_sset_count, |
2437 | .get_strings = marvell_get_strings, | |
2438 | .get_stats = marvell_get_stats, | |
262caf47 HK |
2439 | .get_tunable = m88e1011_get_tunable, |
2440 | .set_tunable = m88e1011_set_tunable, | |
2441 | .link_change_notify = m88e1011_link_change_notify, | |
3da09a51 | 2442 | }, |
10e24caa MS |
2443 | { |
2444 | .phy_id = MARVELL_PHY_ID_88E1510, | |
2445 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
2446 | .name = "Marvell 88E1510", | |
719655a1 | 2447 | .features = PHY_GBIT_FIBRE_FEATURES, |
0b04680f | 2448 | .probe = &m88e1510_probe, |
930b37ee | 2449 | .config_init = &m88e1510_config_init, |
10e24caa MS |
2450 | .config_aneg = &m88e1510_config_aneg, |
2451 | .read_status = &marvell_read_status, | |
2452 | .ack_interrupt = &marvell_ack_interrupt, | |
2453 | .config_intr = &marvell_config_intr, | |
2454 | .did_interrupt = &m88e1121_did_interrupt, | |
f39aac7e JH |
2455 | .get_wol = &m88e1318_get_wol, |
2456 | .set_wol = &m88e1318_set_wol, | |
3758be3d CAC |
2457 | .resume = &marvell_resume, |
2458 | .suspend = &marvell_suspend, | |
424ca4c5 RK |
2459 | .read_page = marvell_read_page, |
2460 | .write_page = marvell_write_page, | |
d2fa47d9 AL |
2461 | .get_sset_count = marvell_get_sset_count, |
2462 | .get_strings = marvell_get_strings, | |
2463 | .get_stats = marvell_get_stats, | |
f0f9b4ed | 2464 | .set_loopback = genphy_loopback, |
262caf47 HK |
2465 | .get_tunable = m88e1011_get_tunable, |
2466 | .set_tunable = m88e1011_set_tunable, | |
2467 | .link_change_notify = m88e1011_link_change_notify, | |
10e24caa | 2468 | }, |
819ec8e1 AL |
2469 | { |
2470 | .phy_id = MARVELL_PHY_ID_88E1540, | |
2471 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
2472 | .name = "Marvell 88E1540", | |
dcdecdcf | 2473 | /* PHY_GBIT_FEATURES */ |
18702414 | 2474 | .probe = m88e1510_probe, |
79be1a1c | 2475 | .config_init = &marvell_config_init, |
819ec8e1 AL |
2476 | .config_aneg = &m88e1510_config_aneg, |
2477 | .read_status = &marvell_read_status, | |
2478 | .ack_interrupt = &marvell_ack_interrupt, | |
2479 | .config_intr = &marvell_config_intr, | |
2480 | .did_interrupt = &m88e1121_did_interrupt, | |
2481 | .resume = &genphy_resume, | |
2482 | .suspend = &genphy_suspend, | |
424ca4c5 RK |
2483 | .read_page = marvell_read_page, |
2484 | .write_page = marvell_write_page, | |
d2fa47d9 AL |
2485 | .get_sset_count = marvell_get_sset_count, |
2486 | .get_strings = marvell_get_strings, | |
2487 | .get_stats = marvell_get_stats, | |
69f42be8 HK |
2488 | .get_tunable = m88e1540_get_tunable, |
2489 | .set_tunable = m88e1540_set_tunable, | |
911af5e1 | 2490 | .link_change_notify = m88e1011_link_change_notify, |
819ec8e1 | 2491 | }, |
60f06fde AL |
2492 | { |
2493 | .phy_id = MARVELL_PHY_ID_88E1545, | |
2494 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
2495 | .name = "Marvell 88E1545", | |
2496 | .probe = m88e1510_probe, | |
dcdecdcf | 2497 | /* PHY_GBIT_FEATURES */ |
60f06fde AL |
2498 | .config_init = &marvell_config_init, |
2499 | .config_aneg = &m88e1510_config_aneg, | |
2500 | .read_status = &marvell_read_status, | |
2501 | .ack_interrupt = &marvell_ack_interrupt, | |
2502 | .config_intr = &marvell_config_intr, | |
2503 | .did_interrupt = &m88e1121_did_interrupt, | |
2504 | .resume = &genphy_resume, | |
2505 | .suspend = &genphy_suspend, | |
424ca4c5 RK |
2506 | .read_page = marvell_read_page, |
2507 | .write_page = marvell_write_page, | |
60f06fde AL |
2508 | .get_sset_count = marvell_get_sset_count, |
2509 | .get_strings = marvell_get_strings, | |
2510 | .get_stats = marvell_get_stats, | |
262caf47 HK |
2511 | .get_tunable = m88e1540_get_tunable, |
2512 | .set_tunable = m88e1540_set_tunable, | |
2513 | .link_change_notify = m88e1011_link_change_notify, | |
60f06fde | 2514 | }, |
6b358aed SH |
2515 | { |
2516 | .phy_id = MARVELL_PHY_ID_88E3016, | |
2517 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
2518 | .name = "Marvell 88E3016", | |
dcdecdcf | 2519 | /* PHY_BASIC_FEATURES */ |
d2fa47d9 | 2520 | .probe = marvell_probe, |
6b358aed SH |
2521 | .config_init = &m88e3016_config_init, |
2522 | .aneg_done = &marvell_aneg_done, | |
2523 | .read_status = &marvell_read_status, | |
2524 | .ack_interrupt = &marvell_ack_interrupt, | |
2525 | .config_intr = &marvell_config_intr, | |
2526 | .did_interrupt = &m88e1121_did_interrupt, | |
2527 | .resume = &genphy_resume, | |
2528 | .suspend = &genphy_suspend, | |
424ca4c5 RK |
2529 | .read_page = marvell_read_page, |
2530 | .write_page = marvell_write_page, | |
d2fa47d9 AL |
2531 | .get_sset_count = marvell_get_sset_count, |
2532 | .get_strings = marvell_get_strings, | |
2533 | .get_stats = marvell_get_stats, | |
6b358aed | 2534 | }, |
e4cf8a38 AL |
2535 | { |
2536 | .phy_id = MARVELL_PHY_ID_88E6390, | |
2537 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
2538 | .name = "Marvell 88E6390", | |
dcdecdcf | 2539 | /* PHY_GBIT_FEATURES */ |
fee2d546 | 2540 | .probe = m88e6390_probe, |
e4cf8a38 | 2541 | .config_init = &marvell_config_init, |
8cbcdc1a | 2542 | .config_aneg = &m88e6390_config_aneg, |
e4cf8a38 AL |
2543 | .read_status = &marvell_read_status, |
2544 | .ack_interrupt = &marvell_ack_interrupt, | |
2545 | .config_intr = &marvell_config_intr, | |
2546 | .did_interrupt = &m88e1121_did_interrupt, | |
2547 | .resume = &genphy_resume, | |
2548 | .suspend = &genphy_suspend, | |
424ca4c5 RK |
2549 | .read_page = marvell_read_page, |
2550 | .write_page = marvell_write_page, | |
e4cf8a38 AL |
2551 | .get_sset_count = marvell_get_sset_count, |
2552 | .get_strings = marvell_get_strings, | |
2553 | .get_stats = marvell_get_stats, | |
69f42be8 HK |
2554 | .get_tunable = m88e1540_get_tunable, |
2555 | .set_tunable = m88e1540_set_tunable, | |
911af5e1 | 2556 | .link_change_notify = m88e1011_link_change_notify, |
e4cf8a38 | 2557 | }, |
00db8189 AF |
2558 | }; |
2559 | ||
50fd7150 | 2560 | module_phy_driver(marvell_drivers); |
4e4f10f6 | 2561 | |
cf93c945 | 2562 | static struct mdio_device_id __maybe_unused marvell_tbl[] = { |
f5e1cabf MS |
2563 | { MARVELL_PHY_ID_88E1101, MARVELL_PHY_ID_MASK }, |
2564 | { MARVELL_PHY_ID_88E1112, MARVELL_PHY_ID_MASK }, | |
2565 | { MARVELL_PHY_ID_88E1111, MARVELL_PHY_ID_MASK }, | |
2566 | { MARVELL_PHY_ID_88E1118, MARVELL_PHY_ID_MASK }, | |
2567 | { MARVELL_PHY_ID_88E1121R, MARVELL_PHY_ID_MASK }, | |
2568 | { MARVELL_PHY_ID_88E1145, MARVELL_PHY_ID_MASK }, | |
2569 | { MARVELL_PHY_ID_88E1149R, MARVELL_PHY_ID_MASK }, | |
2570 | { MARVELL_PHY_ID_88E1240, MARVELL_PHY_ID_MASK }, | |
2571 | { MARVELL_PHY_ID_88E1318S, MARVELL_PHY_ID_MASK }, | |
3da09a51 | 2572 | { MARVELL_PHY_ID_88E1116R, MARVELL_PHY_ID_MASK }, |
10e24caa | 2573 | { MARVELL_PHY_ID_88E1510, MARVELL_PHY_ID_MASK }, |
819ec8e1 | 2574 | { MARVELL_PHY_ID_88E1540, MARVELL_PHY_ID_MASK }, |
60f06fde | 2575 | { MARVELL_PHY_ID_88E1545, MARVELL_PHY_ID_MASK }, |
6b358aed | 2576 | { MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK }, |
e4cf8a38 | 2577 | { MARVELL_PHY_ID_88E6390, MARVELL_PHY_ID_MASK }, |
4e4f10f6 DW |
2578 | { } |
2579 | }; | |
2580 | ||
2581 | MODULE_DEVICE_TABLE(mdio, marvell_tbl); |