blob: c1b724ab5f25888a4662029fa15c0520d4f3a070 [file] [log] [blame]
Andy Fleming00db8182005-07-30 19:31:23 -04001/*
2 * drivers/net/phy/marvell.c
3 *
4 * Driver for Marvell PHYs
5 *
6 * Author: Andy Fleming
7 *
8 * Copyright (c) 2004 Freescale Semiconductor, Inc.
9 *
Michael Stapelberg3871c382013-03-11 13:56:45 +000010 * Copyright (c) 2013 Michael Stapelberg <michael@stapelberg.de>
11 *
Andy Fleming00db8182005-07-30 19:31:23 -040012 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version.
16 *
17 */
Andy Fleming00db8182005-07-30 19:31:23 -040018#include <linux/kernel.h>
Andy Fleming00db8182005-07-30 19:31:23 -040019#include <linux/string.h>
Andrew Lunn0b046802017-01-20 01:37:49 +010020#include <linux/ctype.h>
Andy Fleming00db8182005-07-30 19:31:23 -040021#include <linux/errno.h>
22#include <linux/unistd.h>
Andrew Lunn0b046802017-01-20 01:37:49 +010023#include <linux/hwmon.h>
Andy Fleming00db8182005-07-30 19:31:23 -040024#include <linux/interrupt.h>
25#include <linux/init.h>
26#include <linux/delay.h>
27#include <linux/netdevice.h>
28#include <linux/etherdevice.h>
29#include <linux/skbuff.h>
30#include <linux/spinlock.h>
31#include <linux/mm.h>
32#include <linux/module.h>
Andy Fleming00db8182005-07-30 19:31:23 -040033#include <linux/mii.h>
34#include <linux/ethtool.h>
35#include <linux/phy.h>
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +100036#include <linux/marvell_phy.h>
David Daneycf41a512010-11-19 12:13:18 +000037#include <linux/of.h>
Andy Fleming00db8182005-07-30 19:31:23 -040038
Avinash Kumareea3b202013-09-30 09:36:44 +053039#include <linux/io.h>
Andy Fleming00db8182005-07-30 19:31:23 -040040#include <asm/irq.h>
Avinash Kumareea3b202013-09-30 09:36:44 +053041#include <linux/uaccess.h>
Andy Fleming00db8182005-07-30 19:31:23 -040042
David Daney27d916d2010-11-19 11:58:52 +000043#define MII_MARVELL_PHY_PAGE 22
Andrew Lunn52295662017-05-25 21:42:08 +020044#define MII_MARVELL_COPPER_PAGE 0x00
45#define MII_MARVELL_FIBER_PAGE 0x01
46#define MII_MARVELL_MSCR_PAGE 0x02
47#define MII_MARVELL_LED_PAGE 0x03
48#define MII_MARVELL_MISC_TEST_PAGE 0x06
49#define MII_MARVELL_WOL_PAGE 0x11
David Daney27d916d2010-11-19 11:58:52 +000050
Andy Fleming00db8182005-07-30 19:31:23 -040051#define MII_M1011_IEVENT 0x13
52#define MII_M1011_IEVENT_CLEAR 0x0000
53
54#define MII_M1011_IMASK 0x12
55#define MII_M1011_IMASK_INIT 0x6400
56#define MII_M1011_IMASK_CLEAR 0x0000
57
Andy Fleming76884672007-02-09 18:13:58 -060058#define MII_M1011_PHY_SCR 0x10
David Thomson239aa552015-07-10 16:28:25 +120059#define MII_M1011_PHY_SCR_MDI 0x0000
60#define MII_M1011_PHY_SCR_MDI_X 0x0020
Andy Fleming76884672007-02-09 18:13:58 -060061#define MII_M1011_PHY_SCR_AUTO_CROSS 0x0060
62
Viet Nga Daob0224172014-10-23 19:41:53 -070063#define MII_M1145_PHY_EXT_SR 0x1b
Vince Bridgers99d881f2014-10-26 14:22:24 -050064#define MII_M1145_HWCFG_MODE_SGMII_NO_CLK 0x4
65#define MII_M1145_HWCFG_MODE_MASK 0xf
66#define MII_M1145_HWCFG_FIBER_COPPER_AUTO 0x8000
67
Andy Fleming76884672007-02-09 18:13:58 -060068#define MII_M1111_PHY_LED_CONTROL 0x18
69#define MII_M1111_PHY_LED_DIRECT 0x4100
70#define MII_M1111_PHY_LED_COMBINE 0x411c
Kim Phillips895ee682007-06-05 18:46:47 +080071#define MII_M1111_PHY_EXT_CR 0x14
Andrew Lunn61111592017-07-30 22:41:46 +020072#define MII_M1111_RGMII_RX_DELAY BIT(7)
73#define MII_M1111_RGMII_TX_DELAY BIT(1)
Kim Phillips895ee682007-06-05 18:46:47 +080074#define MII_M1111_PHY_EXT_SR 0x1b
Alexandr Smirnovbe937f12008-03-19 00:37:24 +030075
76#define MII_M1111_HWCFG_MODE_MASK 0xf
77#define MII_M1111_HWCFG_MODE_COPPER_RGMII 0xb
78#define MII_M1111_HWCFG_MODE_FIBER_RGMII 0x3
Kapil Juneja4117b5b2007-05-11 18:25:18 -050079#define MII_M1111_HWCFG_MODE_SGMII_NO_CLK 0x4
Liu Yu-B132015f8cbc12010-01-13 22:13:19 +000080#define MII_M1111_HWCFG_MODE_COPPER_RTBI 0x9
Alexandr Smirnovbe937f12008-03-19 00:37:24 +030081#define MII_M1111_HWCFG_FIBER_COPPER_AUTO 0x8000
82#define MII_M1111_HWCFG_FIBER_COPPER_RES 0x2000
83
Cyril Chemparathyc477d042010-08-02 09:44:53 +000084#define MII_88E1121_PHY_MSCR_REG 21
85#define MII_88E1121_PHY_MSCR_RX_DELAY BIT(5)
86#define MII_88E1121_PHY_MSCR_TX_DELAY BIT(4)
87#define MII_88E1121_PHY_MSCR_DELAY_MASK (~(0x3 << 4))
88
Andrew Lunn0b046802017-01-20 01:37:49 +010089#define MII_88E1121_MISC_TEST 0x1a
90#define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK 0x1f00
91#define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT 8
92#define MII_88E1510_MISC_TEST_TEMP_IRQ_EN BIT(7)
93#define MII_88E1510_MISC_TEST_TEMP_IRQ BIT(6)
94#define MII_88E1121_MISC_TEST_TEMP_SENSOR_EN BIT(5)
95#define MII_88E1121_MISC_TEST_TEMP_MASK 0x1f
96
97#define MII_88E1510_TEMP_SENSOR 0x1b
98#define MII_88E1510_TEMP_SENSOR_MASK 0xff
99
Cyril Chemparathy337ac9d2010-10-29 13:50:25 -0700100#define MII_88E1318S_PHY_MSCR1_REG 16
101#define MII_88E1318S_PHY_MSCR1_PAD_ODD BIT(6)
Cyril Chemparathy3ff1c252010-08-03 19:36:06 -0700102
Michael Stapelberg3871c382013-03-11 13:56:45 +0000103/* Copper Specific Interrupt Enable Register */
Andrew Lunn8cf8b872017-07-30 22:41:44 +0200104#define MII_88E1318S_PHY_CSIER 0x12
Michael Stapelberg3871c382013-03-11 13:56:45 +0000105/* WOL Event Interrupt Enable */
Andrew Lunn8cf8b872017-07-30 22:41:44 +0200106#define MII_88E1318S_PHY_CSIER_WOL_EIE BIT(7)
Michael Stapelberg3871c382013-03-11 13:56:45 +0000107
108/* LED Timer Control Register */
Andrew Lunn8cf8b872017-07-30 22:41:44 +0200109#define MII_88E1318S_PHY_LED_TCR 0x12
110#define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15)
111#define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7)
112#define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW BIT(11)
Michael Stapelberg3871c382013-03-11 13:56:45 +0000113
114/* Magic Packet MAC address registers */
Andrew Lunn8cf8b872017-07-30 22:41:44 +0200115#define MII_88E1318S_PHY_MAGIC_PACKET_WORD2 0x17
116#define MII_88E1318S_PHY_MAGIC_PACKET_WORD1 0x18
117#define MII_88E1318S_PHY_MAGIC_PACKET_WORD0 0x19
Michael Stapelberg3871c382013-03-11 13:56:45 +0000118
Andrew Lunn8cf8b872017-07-30 22:41:44 +0200119#define MII_88E1318S_PHY_WOL_CTRL 0x10
120#define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS BIT(12)
121#define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE BIT(14)
Michael Stapelberg3871c382013-03-11 13:56:45 +0000122
Sergei Poselenov140bc922009-04-07 02:01:41 +0000123#define MII_88E1121_PHY_LED_CTRL 16
Sergei Poselenov140bc922009-04-07 02:01:41 +0000124#define MII_88E1121_PHY_LED_DEF 0x0030
Sergei Poselenov140bc922009-04-07 02:01:41 +0000125
Alexandr Smirnovbe937f12008-03-19 00:37:24 +0300126#define MII_M1011_PHY_STATUS 0x11
127#define MII_M1011_PHY_STATUS_1000 0x8000
128#define MII_M1011_PHY_STATUS_100 0x4000
129#define MII_M1011_PHY_STATUS_SPD_MASK 0xc000
130#define MII_M1011_PHY_STATUS_FULLDUPLEX 0x2000
131#define MII_M1011_PHY_STATUS_RESOLVED 0x0800
132#define MII_M1011_PHY_STATUS_LINK 0x0400
133
Michal Simek3da09a52013-05-30 20:08:26 +0000134#define MII_M1116R_CONTROL_REG_MAC 21
135
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +0200136#define MII_88E3016_PHY_SPEC_CTRL 0x10
137#define MII_88E3016_DISABLE_SCRAMBLER 0x0200
138#define MII_88E3016_AUTO_MDIX_CROSSOVER 0x0030
Andy Fleming76884672007-02-09 18:13:58 -0600139
Stefan Roese930b37e2016-02-18 10:59:07 +0100140#define MII_88E1510_GEN_CTRL_REG_1 0x14
141#define MII_88E1510_GEN_CTRL_REG_1_MODE_MASK 0x7
142#define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII 0x1 /* SGMII to copper */
143#define MII_88E1510_GEN_CTRL_REG_1_RESET 0x8000 /* Soft reset */
144
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +0200145#define LPA_FIBER_1000HALF 0x40
146#define LPA_FIBER_1000FULL 0x20
147
Andrew Lunn8cf8b872017-07-30 22:41:44 +0200148#define LPA_PAUSE_FIBER 0x180
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +0200149#define LPA_PAUSE_ASYM_FIBER 0x100
150
151#define ADVERTISE_FIBER_1000HALF 0x40
152#define ADVERTISE_FIBER_1000FULL 0x20
153
154#define ADVERTISE_PAUSE_FIBER 0x180
155#define ADVERTISE_PAUSE_ASYM_FIBER 0x100
156
157#define REGISTER_LINK_STATUS 0x400
Charles-Antoine Couret2170fef2016-07-19 11:13:11 +0200158#define NB_FIBER_STATS 1
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +0200159
Andy Fleming00db8182005-07-30 19:31:23 -0400160MODULE_DESCRIPTION("Marvell PHY driver");
161MODULE_AUTHOR("Andy Fleming");
162MODULE_LICENSE("GPL");
163
Andrew Lunnd2fa47d2015-12-30 16:28:26 +0100164struct marvell_hw_stat {
165 const char *string;
166 u8 page;
167 u8 reg;
168 u8 bits;
169};
170
171static struct marvell_hw_stat marvell_hw_stats[] = {
Charles-Antoine Couret2170fef2016-07-19 11:13:11 +0200172 { "phy_receive_errors_copper", 0, 21, 16},
Andrew Lunnd2fa47d2015-12-30 16:28:26 +0100173 { "phy_idle_errors", 0, 10, 8 },
Charles-Antoine Couret2170fef2016-07-19 11:13:11 +0200174 { "phy_receive_errors_fiber", 1, 21, 16},
Andrew Lunnd2fa47d2015-12-30 16:28:26 +0100175};
176
177struct marvell_priv {
178 u64 stats[ARRAY_SIZE(marvell_hw_stats)];
Andrew Lunn0b046802017-01-20 01:37:49 +0100179 char *hwmon_name;
180 struct device *hwmon_dev;
Andrew Lunnd2fa47d2015-12-30 16:28:26 +0100181};
182
Andrew Lunn6427bb22017-05-17 03:26:03 +0200183static int marvell_get_page(struct phy_device *phydev)
184{
185 return phy_read(phydev, MII_MARVELL_PHY_PAGE);
186}
187
188static int marvell_set_page(struct phy_device *phydev, int page)
189{
190 return phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
191}
192
Andrew Lunn53798322017-05-25 21:42:07 +0200193static int marvell_get_set_page(struct phy_device *phydev, int page)
194{
195 int oldpage = marvell_get_page(phydev);
196
197 if (oldpage < 0)
198 return oldpage;
199
200 if (page != oldpage)
201 return marvell_set_page(phydev, page);
202
203 return 0;
204}
205
Andy Fleming00db8182005-07-30 19:31:23 -0400206static int marvell_ack_interrupt(struct phy_device *phydev)
207{
208 int err;
209
210 /* Clear the interrupts by reading the reg */
211 err = phy_read(phydev, MII_M1011_IEVENT);
212
213 if (err < 0)
214 return err;
215
216 return 0;
217}
218
219static int marvell_config_intr(struct phy_device *phydev)
220{
221 int err;
222
Andy Fleming76884672007-02-09 18:13:58 -0600223 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
Andrew Lunn23beb382017-05-17 03:26:04 +0200224 err = phy_write(phydev, MII_M1011_IMASK,
225 MII_M1011_IMASK_INIT);
Andy Fleming00db8182005-07-30 19:31:23 -0400226 else
Andrew Lunn23beb382017-05-17 03:26:04 +0200227 err = phy_write(phydev, MII_M1011_IMASK,
228 MII_M1011_IMASK_CLEAR);
Andy Fleming00db8182005-07-30 19:31:23 -0400229
230 return err;
231}
232
David Thomson239aa552015-07-10 16:28:25 +1200233static int marvell_set_polarity(struct phy_device *phydev, int polarity)
234{
235 int reg;
236 int err;
237 int val;
238
239 /* get the current settings */
240 reg = phy_read(phydev, MII_M1011_PHY_SCR);
241 if (reg < 0)
242 return reg;
243
244 val = reg;
245 val &= ~MII_M1011_PHY_SCR_AUTO_CROSS;
246 switch (polarity) {
247 case ETH_TP_MDI:
248 val |= MII_M1011_PHY_SCR_MDI;
249 break;
250 case ETH_TP_MDI_X:
251 val |= MII_M1011_PHY_SCR_MDI_X;
252 break;
253 case ETH_TP_MDI_AUTO:
254 case ETH_TP_MDI_INVALID:
255 default:
256 val |= MII_M1011_PHY_SCR_AUTO_CROSS;
257 break;
258 }
259
260 if (val != reg) {
261 /* Set the new polarity value in the register */
262 err = phy_write(phydev, MII_M1011_PHY_SCR, val);
263 if (err)
264 return err;
265 }
266
267 return 0;
268}
269
Andy Fleming00db8182005-07-30 19:31:23 -0400270static int marvell_config_aneg(struct phy_device *phydev)
271{
272 int err;
273
Raju Lakkaraju4e26c5c2016-11-29 15:16:49 +0530274 err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
Andy Fleming76884672007-02-09 18:13:58 -0600275 if (err < 0)
276 return err;
277
278 err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
279 MII_M1111_PHY_LED_DIRECT);
280 if (err < 0)
281 return err;
Andy Fleming00db8182005-07-30 19:31:23 -0400282
283 err = genphy_config_aneg(phydev);
Anton Vorontsov8ff44982009-09-09 16:01:30 +0000284 if (err < 0)
285 return err;
Andy Fleming00db8182005-07-30 19:31:23 -0400286
Anton Vorontsov8ff44982009-09-09 16:01:30 +0000287 if (phydev->autoneg != AUTONEG_ENABLE) {
Andrew Lunn0c3439b2017-05-17 03:25:59 +0200288 /* A write to speed/duplex bits (that is performed by
Anton Vorontsov8ff44982009-09-09 16:01:30 +0000289 * genphy_config_aneg() call above) must be followed by
290 * a software reset. Otherwise, the write has no effect.
291 */
Andrew Lunn34386342017-07-30 22:41:45 +0200292 err = genphy_soft_reset(phydev);
Anton Vorontsov8ff44982009-09-09 16:01:30 +0000293 if (err < 0)
294 return err;
295 }
296
297 return 0;
Andy Fleming00db8182005-07-30 19:31:23 -0400298}
299
Andrew Lunnf2899782017-05-23 17:49:13 +0200300static int m88e1101_config_aneg(struct phy_device *phydev)
301{
302 int err;
303
304 /* This Marvell PHY has an errata which requires
305 * that certain registers get written in order
306 * to restart autonegotiation
307 */
Andrew Lunn34386342017-07-30 22:41:45 +0200308 err = genphy_soft_reset(phydev);
Andrew Lunnf2899782017-05-23 17:49:13 +0200309 if (err < 0)
310 return err;
311
312 err = phy_write(phydev, 0x1d, 0x1f);
313 if (err < 0)
314 return err;
315
316 err = phy_write(phydev, 0x1e, 0x200c);
317 if (err < 0)
318 return err;
319
320 err = phy_write(phydev, 0x1d, 0x5);
321 if (err < 0)
322 return err;
323
324 err = phy_write(phydev, 0x1e, 0);
325 if (err < 0)
326 return err;
327
328 err = phy_write(phydev, 0x1e, 0x100);
329 if (err < 0)
330 return err;
331
332 return marvell_config_aneg(phydev);
333}
334
Harini Katakam3ec0a0f2016-06-27 13:09:59 +0530335static int m88e1111_config_aneg(struct phy_device *phydev)
336{
337 int err;
338
339 /* The Marvell PHY has an errata which requires
340 * that certain registers get written in order
341 * to restart autonegotiation
342 */
Andrew Lunn34386342017-07-30 22:41:45 +0200343 err = genphy_soft_reset(phydev);
Harini Katakam3ec0a0f2016-06-27 13:09:59 +0530344
Raju Lakkaraju4e26c5c2016-11-29 15:16:49 +0530345 err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
Harini Katakam3ec0a0f2016-06-27 13:09:59 +0530346 if (err < 0)
347 return err;
348
349 err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
350 MII_M1111_PHY_LED_DIRECT);
351 if (err < 0)
352 return err;
353
354 err = genphy_config_aneg(phydev);
355 if (err < 0)
356 return err;
357
358 if (phydev->autoneg != AUTONEG_ENABLE) {
Harini Katakam3ec0a0f2016-06-27 13:09:59 +0530359 /* A write to speed/duplex bits (that is performed by
360 * genphy_config_aneg() call above) must be followed by
361 * a software reset. Otherwise, the write has no effect.
362 */
Andrew Lunn34386342017-07-30 22:41:45 +0200363 err = genphy_soft_reset(phydev);
Harini Katakam3ec0a0f2016-06-27 13:09:59 +0530364 if (err < 0)
365 return err;
366 }
367
368 return 0;
369}
370
David Daneycf41a512010-11-19 12:13:18 +0000371#ifdef CONFIG_OF_MDIO
Andrew Lunn0c3439b2017-05-17 03:25:59 +0200372/* Set and/or override some configuration registers based on the
David Daneycf41a512010-11-19 12:13:18 +0000373 * marvell,reg-init property stored in the of_node for the phydev.
374 *
375 * marvell,reg-init = <reg-page reg mask value>,...;
376 *
377 * There may be one or more sets of <reg-page reg mask value>:
378 *
379 * reg-page: which register bank to use.
380 * reg: the register.
381 * mask: if non-zero, ANDed with existing register value.
382 * value: ORed with the masked value and written to the regiser.
383 *
384 */
385static int marvell_of_reg_init(struct phy_device *phydev)
386{
387 const __be32 *paddr;
Uwe Kleine-Königb5718b52016-11-10 15:03:01 +0100388 int len, i, saved_page, current_page, ret;
David Daneycf41a512010-11-19 12:13:18 +0000389
Andrew Lunne5a03bf2016-01-06 20:11:16 +0100390 if (!phydev->mdio.dev.of_node)
David Daneycf41a512010-11-19 12:13:18 +0000391 return 0;
392
Andrew Lunne5a03bf2016-01-06 20:11:16 +0100393 paddr = of_get_property(phydev->mdio.dev.of_node,
394 "marvell,reg-init", &len);
David Daneycf41a512010-11-19 12:13:18 +0000395 if (!paddr || len < (4 * sizeof(*paddr)))
396 return 0;
397
Andrew Lunn6427bb22017-05-17 03:26:03 +0200398 saved_page = marvell_get_page(phydev);
David Daneycf41a512010-11-19 12:13:18 +0000399 if (saved_page < 0)
400 return saved_page;
David Daneycf41a512010-11-19 12:13:18 +0000401 current_page = saved_page;
402
403 ret = 0;
404 len /= sizeof(*paddr);
405 for (i = 0; i < len - 3; i += 4) {
Andrew Lunn6427bb22017-05-17 03:26:03 +0200406 u16 page = be32_to_cpup(paddr + i);
David Daneycf41a512010-11-19 12:13:18 +0000407 u16 reg = be32_to_cpup(paddr + i + 1);
408 u16 mask = be32_to_cpup(paddr + i + 2);
409 u16 val_bits = be32_to_cpup(paddr + i + 3);
410 int val;
411
Andrew Lunn6427bb22017-05-17 03:26:03 +0200412 if (page != current_page) {
413 current_page = page;
414 ret = marvell_set_page(phydev, page);
David Daneycf41a512010-11-19 12:13:18 +0000415 if (ret < 0)
416 goto err;
417 }
418
419 val = 0;
420 if (mask) {
421 val = phy_read(phydev, reg);
422 if (val < 0) {
423 ret = val;
424 goto err;
425 }
426 val &= mask;
427 }
428 val |= val_bits;
429
430 ret = phy_write(phydev, reg, val);
431 if (ret < 0)
432 goto err;
David Daneycf41a512010-11-19 12:13:18 +0000433 }
434err:
Uwe Kleine-Königb5718b52016-11-10 15:03:01 +0100435 if (current_page != saved_page) {
Andrew Lunn6427bb22017-05-17 03:26:03 +0200436 i = marvell_set_page(phydev, saved_page);
David Daneycf41a512010-11-19 12:13:18 +0000437 if (ret == 0)
438 ret = i;
439 }
440 return ret;
441}
442#else
443static int marvell_of_reg_init(struct phy_device *phydev)
444{
445 return 0;
446}
447#endif /* CONFIG_OF_MDIO */
448
Sergei Poselenov140bc922009-04-07 02:01:41 +0000449static int m88e1121_config_aneg(struct phy_device *phydev)
450{
Cyril Chemparathyc477d042010-08-02 09:44:53 +0000451 int err, oldpage, mscr;
452
Andrew Lunn52295662017-05-25 21:42:08 +0200453 oldpage = marvell_get_set_page(phydev, MII_MARVELL_MSCR_PAGE);
Andrew Lunn53798322017-05-25 21:42:07 +0200454 if (oldpage < 0)
455 return oldpage;
Cyril Chemparathyc477d042010-08-02 09:44:53 +0000456
Florian Fainelli32a64162015-05-26 12:19:59 -0700457 if (phy_interface_is_rgmii(phydev)) {
Arnaud Patardbe8c6482010-10-21 03:59:57 -0700458 mscr = phy_read(phydev, MII_88E1121_PHY_MSCR_REG) &
459 MII_88E1121_PHY_MSCR_DELAY_MASK;
460
461 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
462 mscr |= (MII_88E1121_PHY_MSCR_RX_DELAY |
463 MII_88E1121_PHY_MSCR_TX_DELAY);
464 else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
465 mscr |= MII_88E1121_PHY_MSCR_RX_DELAY;
466 else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
467 mscr |= MII_88E1121_PHY_MSCR_TX_DELAY;
468
469 err = phy_write(phydev, MII_88E1121_PHY_MSCR_REG, mscr);
470 if (err < 0)
471 return err;
472 }
Cyril Chemparathyc477d042010-08-02 09:44:53 +0000473
Andrew Lunn6427bb22017-05-17 03:26:03 +0200474 marvell_set_page(phydev, oldpage);
Sergei Poselenov140bc922009-04-07 02:01:41 +0000475
Andrew Lunn34386342017-07-30 22:41:45 +0200476 err = genphy_soft_reset(phydev);
Sergei Poselenov140bc922009-04-07 02:01:41 +0000477 if (err < 0)
478 return err;
479
480 err = phy_write(phydev, MII_M1011_PHY_SCR,
481 MII_M1011_PHY_SCR_AUTO_CROSS);
482 if (err < 0)
483 return err;
484
Clemens Gruberfdecf362016-06-11 17:21:26 +0200485 return genphy_config_aneg(phydev);
Sergei Poselenov140bc922009-04-07 02:01:41 +0000486}
487
Cyril Chemparathy337ac9d2010-10-29 13:50:25 -0700488static int m88e1318_config_aneg(struct phy_device *phydev)
Cyril Chemparathy3ff1c252010-08-03 19:36:06 -0700489{
490 int err, oldpage, mscr;
491
Andrew Lunn52295662017-05-25 21:42:08 +0200492 oldpage = marvell_get_set_page(phydev, MII_MARVELL_MSCR_PAGE);
Andrew Lunn53798322017-05-25 21:42:07 +0200493 if (oldpage < 0)
494 return oldpage;
Cyril Chemparathy3ff1c252010-08-03 19:36:06 -0700495
Cyril Chemparathy337ac9d2010-10-29 13:50:25 -0700496 mscr = phy_read(phydev, MII_88E1318S_PHY_MSCR1_REG);
497 mscr |= MII_88E1318S_PHY_MSCR1_PAD_ODD;
Cyril Chemparathy3ff1c252010-08-03 19:36:06 -0700498
Cyril Chemparathy337ac9d2010-10-29 13:50:25 -0700499 err = phy_write(phydev, MII_88E1318S_PHY_MSCR1_REG, mscr);
Cyril Chemparathy3ff1c252010-08-03 19:36:06 -0700500 if (err < 0)
501 return err;
502
Andrew Lunn6427bb22017-05-17 03:26:03 +0200503 err = marvell_set_page(phydev, oldpage);
Cyril Chemparathy3ff1c252010-08-03 19:36:06 -0700504 if (err < 0)
505 return err;
506
507 return m88e1121_config_aneg(phydev);
508}
509
Charles-Antoine Couret78301eb2016-07-19 11:13:12 +0200510/**
511 * ethtool_adv_to_fiber_adv_t
512 * @ethadv: the ethtool advertisement settings
513 *
514 * A small helper function that translates ethtool advertisement
515 * settings to phy autonegotiation advertisements for the
516 * MII_ADV register for fiber link.
517 */
518static inline u32 ethtool_adv_to_fiber_adv_t(u32 ethadv)
519{
520 u32 result = 0;
521
522 if (ethadv & ADVERTISED_1000baseT_Half)
523 result |= ADVERTISE_FIBER_1000HALF;
524 if (ethadv & ADVERTISED_1000baseT_Full)
525 result |= ADVERTISE_FIBER_1000FULL;
526
527 if ((ethadv & ADVERTISE_PAUSE_ASYM) && (ethadv & ADVERTISE_PAUSE_CAP))
528 result |= LPA_PAUSE_ASYM_FIBER;
529 else if (ethadv & ADVERTISE_PAUSE_CAP)
530 result |= (ADVERTISE_PAUSE_FIBER
531 & (~ADVERTISE_PAUSE_ASYM_FIBER));
532
533 return result;
534}
535
536/**
537 * marvell_config_aneg_fiber - restart auto-negotiation or write BMCR
538 * @phydev: target phy_device struct
539 *
540 * Description: If auto-negotiation is enabled, we configure the
541 * advertising, and then restart auto-negotiation. If it is not
542 * enabled, then we write the BMCR. Adapted for fiber link in
543 * some Marvell's devices.
544 */
545static int marvell_config_aneg_fiber(struct phy_device *phydev)
546{
547 int changed = 0;
548 int err;
549 int adv, oldadv;
550 u32 advertise;
551
552 if (phydev->autoneg != AUTONEG_ENABLE)
553 return genphy_setup_forced(phydev);
554
555 /* Only allow advertising what this PHY supports */
556 phydev->advertising &= phydev->supported;
557 advertise = phydev->advertising;
558
559 /* Setup fiber advertisement */
560 adv = phy_read(phydev, MII_ADVERTISE);
561 if (adv < 0)
562 return adv;
563
564 oldadv = adv;
565 adv &= ~(ADVERTISE_FIBER_1000HALF | ADVERTISE_FIBER_1000FULL
566 | LPA_PAUSE_FIBER);
567 adv |= ethtool_adv_to_fiber_adv_t(advertise);
568
569 if (adv != oldadv) {
570 err = phy_write(phydev, MII_ADVERTISE, adv);
571 if (err < 0)
572 return err;
573
574 changed = 1;
575 }
576
577 if (changed == 0) {
578 /* Advertisement hasn't changed, but maybe aneg was never on to
Andrew Lunn8cf8b872017-07-30 22:41:44 +0200579 * begin with? Or maybe phy was isolated?
Charles-Antoine Couret78301eb2016-07-19 11:13:12 +0200580 */
581 int ctl = phy_read(phydev, MII_BMCR);
582
583 if (ctl < 0)
584 return ctl;
585
586 if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
587 changed = 1; /* do restart aneg */
588 }
589
590 /* Only restart aneg if we are advertising something different
591 * than we were before.
592 */
593 if (changed > 0)
594 changed = genphy_restart_aneg(phydev);
595
596 return changed;
597}
598
Michal Simek10e24caa2013-05-30 20:08:27 +0000599static int m88e1510_config_aneg(struct phy_device *phydev)
600{
601 int err;
602
Andrew Lunn52295662017-05-25 21:42:08 +0200603 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret78301eb2016-07-19 11:13:12 +0200604 if (err < 0)
605 goto error;
606
607 /* Configure the copper link first */
Michal Simek10e24caa2013-05-30 20:08:27 +0000608 err = m88e1318_config_aneg(phydev);
609 if (err < 0)
Charles-Antoine Couret78301eb2016-07-19 11:13:12 +0200610 goto error;
Michal Simek10e24caa2013-05-30 20:08:27 +0000611
Charles-Antoine Couret78301eb2016-07-19 11:13:12 +0200612 /* Then the fiber link */
Andrew Lunn52295662017-05-25 21:42:08 +0200613 err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
Charles-Antoine Couret78301eb2016-07-19 11:13:12 +0200614 if (err < 0)
615 goto error;
616
617 err = marvell_config_aneg_fiber(phydev);
618 if (err < 0)
619 goto error;
620
Andrew Lunn52295662017-05-25 21:42:08 +0200621 return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret78301eb2016-07-19 11:13:12 +0200622
623error:
Andrew Lunn52295662017-05-25 21:42:08 +0200624 marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret78301eb2016-07-19 11:13:12 +0200625 return err;
Clemens Gruber79be1a12016-02-15 23:46:45 +0100626}
627
628static int marvell_config_init(struct phy_device *phydev)
629{
630 /* Set registers from marvell,reg-init DT property */
Michal Simek10e24caa2013-05-30 20:08:27 +0000631 return marvell_of_reg_init(phydev);
632}
633
Michal Simek3da09a52013-05-30 20:08:26 +0000634static int m88e1116r_config_init(struct phy_device *phydev)
635{
636 int temp;
637 int err;
638
Andrew Lunn34386342017-07-30 22:41:45 +0200639 err = genphy_soft_reset(phydev);
Michal Simek3da09a52013-05-30 20:08:26 +0000640 if (err < 0)
641 return err;
642
643 mdelay(500);
644
Andrew Lunn52295662017-05-25 21:42:08 +0200645 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Michal Simek3da09a52013-05-30 20:08:26 +0000646 if (err < 0)
647 return err;
648
649 temp = phy_read(phydev, MII_M1011_PHY_SCR);
650 temp |= (7 << 12); /* max number of gigabit attempts */
651 temp |= (1 << 11); /* enable downshift */
652 temp |= MII_M1011_PHY_SCR_AUTO_CROSS;
653 err = phy_write(phydev, MII_M1011_PHY_SCR, temp);
654 if (err < 0)
655 return err;
656
Andrew Lunn52295662017-05-25 21:42:08 +0200657 err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
Michal Simek3da09a52013-05-30 20:08:26 +0000658 if (err < 0)
659 return err;
660 temp = phy_read(phydev, MII_M1116R_CONTROL_REG_MAC);
661 temp |= (1 << 5);
662 temp |= (1 << 4);
663 err = phy_write(phydev, MII_M1116R_CONTROL_REG_MAC, temp);
664 if (err < 0)
665 return err;
Andrew Lunn52295662017-05-25 21:42:08 +0200666 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Michal Simek3da09a52013-05-30 20:08:26 +0000667 if (err < 0)
668 return err;
669
Andrew Lunn34386342017-07-30 22:41:45 +0200670 err = genphy_soft_reset(phydev);
Michal Simek3da09a52013-05-30 20:08:26 +0000671 if (err < 0)
672 return err;
673
Clemens Gruber79be1a12016-02-15 23:46:45 +0100674 return marvell_config_init(phydev);
Michal Simek3da09a52013-05-30 20:08:26 +0000675}
676
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +0200677static int m88e3016_config_init(struct phy_device *phydev)
678{
679 int reg;
680
681 /* Enable Scrambler and Auto-Crossover */
682 reg = phy_read(phydev, MII_88E3016_PHY_SPEC_CTRL);
683 if (reg < 0)
684 return reg;
685
686 reg &= ~MII_88E3016_DISABLE_SCRAMBLER;
687 reg |= MII_88E3016_AUTO_MDIX_CROSSOVER;
688
689 reg = phy_write(phydev, MII_88E3016_PHY_SPEC_CTRL, reg);
690 if (reg < 0)
691 return reg;
692
Clemens Gruber79be1a12016-02-15 23:46:45 +0100693 return marvell_config_init(phydev);
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +0200694}
695
Andrew Lunn61111592017-07-30 22:41:46 +0200696static int m88e1111_config_init_rgmii_delays(struct phy_device *phydev)
Kim Phillips895ee682007-06-05 18:46:47 +0800697{
Alexandr Smirnovbe937f12008-03-19 00:37:24 +0300698 int temp;
Alexandr Smirnovbe937f12008-03-19 00:37:24 +0300699
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200700 temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
701 if (temp < 0)
702 return temp;
703
704 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
Andrew Lunn61111592017-07-30 22:41:46 +0200705 temp |= (MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY);
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200706 } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
Andrew Lunn61111592017-07-30 22:41:46 +0200707 temp &= ~MII_M1111_RGMII_TX_DELAY;
708 temp |= MII_M1111_RGMII_RX_DELAY;
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200709 } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
Andrew Lunn61111592017-07-30 22:41:46 +0200710 temp &= ~MII_M1111_RGMII_RX_DELAY;
711 temp |= MII_M1111_RGMII_TX_DELAY;
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200712 }
713
Andrew Lunn61111592017-07-30 22:41:46 +0200714 return phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
715}
716
717static int m88e1111_config_init_rgmii(struct phy_device *phydev)
718{
719 int temp;
720 int err;
721
722 err = m88e1111_config_init_rgmii_delays(phydev);
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200723 if (err < 0)
724 return err;
725
726 temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
727 if (temp < 0)
728 return temp;
729
730 temp &= ~(MII_M1111_HWCFG_MODE_MASK);
731
732 if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES)
733 temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII;
734 else
735 temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII;
736
737 return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
738}
739
740static int m88e1111_config_init_sgmii(struct phy_device *phydev)
741{
742 int err;
743 int temp;
744
745 temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
746 if (temp < 0)
747 return temp;
748
749 temp &= ~(MII_M1111_HWCFG_MODE_MASK);
750 temp |= MII_M1111_HWCFG_MODE_SGMII_NO_CLK;
751 temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO;
752
753 err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
754 if (err < 0)
755 return err;
756
757 /* make sure copper is selected */
Andrew Lunn52295662017-05-25 21:42:08 +0200758 return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200759}
760
761static int m88e1111_config_init_rtbi(struct phy_device *phydev)
762{
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200763 int temp;
Andrew Lunn61111592017-07-30 22:41:46 +0200764 int err;
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200765
Andrew Lunn61111592017-07-30 22:41:46 +0200766 err = m88e1111_config_init_rgmii_delays(phydev);
767 if (err)
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200768 return err;
769
770 temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
771 if (temp < 0)
772 return temp;
773
774 temp &= ~(MII_M1111_HWCFG_MODE_MASK |
775 MII_M1111_HWCFG_FIBER_COPPER_RES);
776 temp |= 0x7 | MII_M1111_HWCFG_FIBER_COPPER_AUTO;
777
778 err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
779 if (err < 0)
780 return err;
781
782 /* soft reset */
Andrew Lunn34386342017-07-30 22:41:45 +0200783 err = genphy_soft_reset(phydev);
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200784 if (err < 0)
785 return err;
786
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200787 temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
788 if (temp < 0)
789 return temp;
790
791 temp &= ~(MII_M1111_HWCFG_MODE_MASK |
792 MII_M1111_HWCFG_FIBER_COPPER_RES);
793 temp |= MII_M1111_HWCFG_MODE_COPPER_RTBI |
794 MII_M1111_HWCFG_FIBER_COPPER_AUTO;
795
796 return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
797}
798
799static int m88e1111_config_init(struct phy_device *phydev)
800{
801 int err;
802
Florian Fainelli32a64162015-05-26 12:19:59 -0700803 if (phy_interface_is_rgmii(phydev)) {
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200804 err = m88e1111_config_init_rgmii(phydev);
805 if (err)
Kim Phillips895ee682007-06-05 18:46:47 +0800806 return err;
807 }
808
Kapil Juneja4117b5b2007-05-11 18:25:18 -0500809 if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200810 err = m88e1111_config_init_sgmii(phydev);
Madalin Bucur07151bc2015-08-07 17:07:50 +0800811 if (err < 0)
812 return err;
Kapil Juneja4117b5b2007-05-11 18:25:18 -0500813 }
814
Liu Yu-B132015f8cbc12010-01-13 22:13:19 +0000815 if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200816 err = m88e1111_config_init_rtbi(phydev);
Liu Yu-B132015f8cbc12010-01-13 22:13:19 +0000817 if (err < 0)
818 return err;
819 }
820
David Daneycf41a512010-11-19 12:13:18 +0000821 err = marvell_of_reg_init(phydev);
822 if (err < 0)
823 return err;
Liu Yu-B132015f8cbc12010-01-13 22:13:19 +0000824
Andrew Lunn34386342017-07-30 22:41:45 +0200825 return genphy_soft_reset(phydev);
Kim Phillips895ee682007-06-05 18:46:47 +0800826}
827
Clemens Gruberfdecf362016-06-11 17:21:26 +0200828static int m88e1121_config_init(struct phy_device *phydev)
829{
830 int err, oldpage;
831
Andrew Lunn52295662017-05-25 21:42:08 +0200832 oldpage = marvell_get_set_page(phydev, MII_MARVELL_LED_PAGE);
Andrew Lunn53798322017-05-25 21:42:07 +0200833 if (oldpage < 0)
834 return oldpage;
Clemens Gruberfdecf362016-06-11 17:21:26 +0200835
836 /* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */
837 err = phy_write(phydev, MII_88E1121_PHY_LED_CTRL,
838 MII_88E1121_PHY_LED_DEF);
839 if (err < 0)
840 return err;
841
Andrew Lunn6427bb22017-05-17 03:26:03 +0200842 marvell_set_page(phydev, oldpage);
Clemens Gruberfdecf362016-06-11 17:21:26 +0200843
844 /* Set marvell,reg-init configuration from device tree */
845 return marvell_config_init(phydev);
846}
847
Clemens Gruber407353e2016-02-23 20:16:58 +0100848static int m88e1510_config_init(struct phy_device *phydev)
849{
850 int err;
851 int temp;
852
853 /* SGMII-to-Copper mode initialization */
854 if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
855 /* Select page 18 */
Andrew Lunn6427bb22017-05-17 03:26:03 +0200856 err = marvell_set_page(phydev, 18);
Clemens Gruber407353e2016-02-23 20:16:58 +0100857 if (err < 0)
858 return err;
859
860 /* In reg 20, write MODE[2:0] = 0x1 (SGMII to Copper) */
861 temp = phy_read(phydev, MII_88E1510_GEN_CTRL_REG_1);
862 temp &= ~MII_88E1510_GEN_CTRL_REG_1_MODE_MASK;
863 temp |= MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII;
864 err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp);
865 if (err < 0)
866 return err;
867
868 /* PHY reset is necessary after changing MODE[2:0] */
869 temp |= MII_88E1510_GEN_CTRL_REG_1_RESET;
870 err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp);
871 if (err < 0)
872 return err;
873
874 /* Reset page selection */
Andrew Lunn52295662017-05-25 21:42:08 +0200875 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Clemens Gruber407353e2016-02-23 20:16:58 +0100876 if (err < 0)
877 return err;
878 }
879
Clemens Gruberfdecf362016-06-11 17:21:26 +0200880 return m88e1121_config_init(phydev);
Clemens Gruber407353e2016-02-23 20:16:58 +0100881}
882
Ron Madrid605f1962008-11-06 09:05:26 +0000883static int m88e1118_config_aneg(struct phy_device *phydev)
884{
885 int err;
886
Andrew Lunn34386342017-07-30 22:41:45 +0200887 err = genphy_soft_reset(phydev);
Ron Madrid605f1962008-11-06 09:05:26 +0000888 if (err < 0)
889 return err;
890
891 err = phy_write(phydev, MII_M1011_PHY_SCR,
892 MII_M1011_PHY_SCR_AUTO_CROSS);
893 if (err < 0)
894 return err;
895
896 err = genphy_config_aneg(phydev);
897 return 0;
898}
899
900static int m88e1118_config_init(struct phy_device *phydev)
901{
902 int err;
903
904 /* Change address */
Andrew Lunn52295662017-05-25 21:42:08 +0200905 err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
Ron Madrid605f1962008-11-06 09:05:26 +0000906 if (err < 0)
907 return err;
908
909 /* Enable 1000 Mbit */
910 err = phy_write(phydev, 0x15, 0x1070);
911 if (err < 0)
912 return err;
913
914 /* Change address */
Andrew Lunn52295662017-05-25 21:42:08 +0200915 err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE);
Ron Madrid605f1962008-11-06 09:05:26 +0000916 if (err < 0)
917 return err;
918
919 /* Adjust LED Control */
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +1000920 if (phydev->dev_flags & MARVELL_PHY_M1118_DNS323_LEDS)
921 err = phy_write(phydev, 0x10, 0x1100);
922 else
923 err = phy_write(phydev, 0x10, 0x021e);
Ron Madrid605f1962008-11-06 09:05:26 +0000924 if (err < 0)
925 return err;
926
David Daneycf41a512010-11-19 12:13:18 +0000927 err = marvell_of_reg_init(phydev);
928 if (err < 0)
929 return err;
930
Ron Madrid605f1962008-11-06 09:05:26 +0000931 /* Reset address */
Andrew Lunn52295662017-05-25 21:42:08 +0200932 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Ron Madrid605f1962008-11-06 09:05:26 +0000933 if (err < 0)
934 return err;
935
Andrew Lunn34386342017-07-30 22:41:45 +0200936 return genphy_soft_reset(phydev);
Ron Madrid605f1962008-11-06 09:05:26 +0000937}
938
David Daney90600732010-11-19 11:58:53 +0000939static int m88e1149_config_init(struct phy_device *phydev)
940{
941 int err;
942
943 /* Change address */
Andrew Lunn52295662017-05-25 21:42:08 +0200944 err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
David Daney90600732010-11-19 11:58:53 +0000945 if (err < 0)
946 return err;
947
948 /* Enable 1000 Mbit */
949 err = phy_write(phydev, 0x15, 0x1048);
950 if (err < 0)
951 return err;
952
David Daneycf41a512010-11-19 12:13:18 +0000953 err = marvell_of_reg_init(phydev);
954 if (err < 0)
955 return err;
956
David Daney90600732010-11-19 11:58:53 +0000957 /* Reset address */
Andrew Lunn52295662017-05-25 21:42:08 +0200958 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
David Daney90600732010-11-19 11:58:53 +0000959 if (err < 0)
960 return err;
961
Andrew Lunn34386342017-07-30 22:41:45 +0200962 return genphy_soft_reset(phydev);
David Daney90600732010-11-19 11:58:53 +0000963}
964
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200965static int m88e1145_config_init_rgmii(struct phy_device *phydev)
966{
Andrew Lunn61111592017-07-30 22:41:46 +0200967 int temp;
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200968 int err;
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200969
Andrew Lunn61111592017-07-30 22:41:46 +0200970 err = m88e1111_config_init_rgmii_delays(phydev);
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200971 if (err < 0)
972 return err;
973
974 if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) {
975 err = phy_write(phydev, 0x1d, 0x0012);
976 if (err < 0)
977 return err;
978
979 temp = phy_read(phydev, 0x1e);
980 if (temp < 0)
981 return temp;
982
983 temp &= 0xf03f;
984 temp |= 2 << 9; /* 36 ohm */
985 temp |= 2 << 6; /* 39 ohm */
986
987 err = phy_write(phydev, 0x1e, temp);
988 if (err < 0)
989 return err;
990
991 err = phy_write(phydev, 0x1d, 0x3);
992 if (err < 0)
993 return err;
994
995 err = phy_write(phydev, 0x1e, 0x8000);
996 }
997 return err;
998}
999
1000static int m88e1145_config_init_sgmii(struct phy_device *phydev)
1001{
1002 int temp = phy_read(phydev, MII_M1145_PHY_EXT_SR);
1003
1004 if (temp < 0)
1005 return temp;
1006
1007 temp &= ~MII_M1145_HWCFG_MODE_MASK;
1008 temp |= MII_M1145_HWCFG_MODE_SGMII_NO_CLK;
1009 temp |= MII_M1145_HWCFG_FIBER_COPPER_AUTO;
1010
1011 return phy_write(phydev, MII_M1145_PHY_EXT_SR, temp);
1012}
1013
Andy Fleming76884672007-02-09 18:13:58 -06001014static int m88e1145_config_init(struct phy_device *phydev)
1015{
1016 int err;
1017
1018 /* Take care of errata E0 & E1 */
1019 err = phy_write(phydev, 0x1d, 0x001b);
1020 if (err < 0)
1021 return err;
1022
1023 err = phy_write(phydev, 0x1e, 0x418f);
1024 if (err < 0)
1025 return err;
1026
1027 err = phy_write(phydev, 0x1d, 0x0016);
1028 if (err < 0)
1029 return err;
1030
1031 err = phy_write(phydev, 0x1e, 0xa2da);
1032 if (err < 0)
1033 return err;
1034
Kim Phillips895ee682007-06-05 18:46:47 +08001035 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001036 err = m88e1145_config_init_rgmii(phydev);
Andy Fleming76884672007-02-09 18:13:58 -06001037 if (err < 0)
1038 return err;
Andy Fleming76884672007-02-09 18:13:58 -06001039 }
1040
Viet Nga Daob0224172014-10-23 19:41:53 -07001041 if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001042 err = m88e1145_config_init_sgmii(phydev);
Viet Nga Daob0224172014-10-23 19:41:53 -07001043 if (err < 0)
1044 return err;
1045 }
1046
David Daneycf41a512010-11-19 12:13:18 +00001047 err = marvell_of_reg_init(phydev);
1048 if (err < 0)
1049 return err;
1050
Andy Fleming76884672007-02-09 18:13:58 -06001051 return 0;
1052}
Andy Fleming00db8182005-07-30 19:31:23 -04001053
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001054/**
1055 * fiber_lpa_to_ethtool_lpa_t
1056 * @lpa: value of the MII_LPA register for fiber link
Alexandr Smirnovbe937f12008-03-19 00:37:24 +03001057 *
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001058 * A small helper function that translates MII_LPA
1059 * bits to ethtool LP advertisement settings.
1060 */
1061static u32 fiber_lpa_to_ethtool_lpa_t(u32 lpa)
1062{
1063 u32 result = 0;
1064
1065 if (lpa & LPA_FIBER_1000HALF)
1066 result |= ADVERTISED_1000baseT_Half;
1067 if (lpa & LPA_FIBER_1000FULL)
1068 result |= ADVERTISED_1000baseT_Full;
1069
1070 return result;
1071}
1072
1073/**
1074 * marvell_update_link - update link status in real time in @phydev
1075 * @phydev: target phy_device struct
1076 *
1077 * Description: Update the value in phydev->link to reflect the
1078 * current link value.
1079 */
1080static int marvell_update_link(struct phy_device *phydev, int fiber)
1081{
1082 int status;
1083
1084 /* Use the generic register for copper link, or specific
Andrew Lunn0c3439b2017-05-17 03:25:59 +02001085 * register for fiber case
1086 */
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001087 if (fiber) {
1088 status = phy_read(phydev, MII_M1011_PHY_STATUS);
1089 if (status < 0)
1090 return status;
1091
1092 if ((status & REGISTER_LINK_STATUS) == 0)
1093 phydev->link = 0;
1094 else
1095 phydev->link = 1;
1096 } else {
1097 return genphy_update_link(phydev);
1098 }
1099
1100 return 0;
1101}
1102
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001103static int marvell_read_status_page_an(struct phy_device *phydev,
1104 int fiber)
1105{
1106 int status;
1107 int lpa;
1108 int lpagb;
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001109
1110 status = phy_read(phydev, MII_M1011_PHY_STATUS);
1111 if (status < 0)
1112 return status;
1113
1114 lpa = phy_read(phydev, MII_LPA);
1115 if (lpa < 0)
1116 return lpa;
1117
1118 lpagb = phy_read(phydev, MII_STAT1000);
1119 if (lpagb < 0)
1120 return lpagb;
1121
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001122 if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
1123 phydev->duplex = DUPLEX_FULL;
1124 else
1125 phydev->duplex = DUPLEX_HALF;
1126
1127 status = status & MII_M1011_PHY_STATUS_SPD_MASK;
1128 phydev->pause = 0;
1129 phydev->asym_pause = 0;
1130
1131 switch (status) {
1132 case MII_M1011_PHY_STATUS_1000:
1133 phydev->speed = SPEED_1000;
1134 break;
1135
1136 case MII_M1011_PHY_STATUS_100:
1137 phydev->speed = SPEED_100;
1138 break;
1139
1140 default:
1141 phydev->speed = SPEED_10;
1142 break;
1143 }
1144
1145 if (!fiber) {
1146 phydev->lp_advertising =
1147 mii_stat1000_to_ethtool_lpa_t(lpagb) |
1148 mii_lpa_to_ethtool_lpa_t(lpa);
1149
1150 if (phydev->duplex == DUPLEX_FULL) {
1151 phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
1152 phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
1153 }
1154 } else {
1155 /* The fiber link is only 1000M capable */
1156 phydev->lp_advertising = fiber_lpa_to_ethtool_lpa_t(lpa);
1157
1158 if (phydev->duplex == DUPLEX_FULL) {
1159 if (!(lpa & LPA_PAUSE_FIBER)) {
1160 phydev->pause = 0;
1161 phydev->asym_pause = 0;
1162 } else if ((lpa & LPA_PAUSE_ASYM_FIBER)) {
1163 phydev->pause = 1;
1164 phydev->asym_pause = 1;
1165 } else {
1166 phydev->pause = 1;
1167 phydev->asym_pause = 0;
1168 }
1169 }
1170 }
1171 return 0;
1172}
1173
1174static int marvell_read_status_page_fixed(struct phy_device *phydev)
1175{
1176 int bmcr = phy_read(phydev, MII_BMCR);
1177
1178 if (bmcr < 0)
1179 return bmcr;
1180
1181 if (bmcr & BMCR_FULLDPLX)
1182 phydev->duplex = DUPLEX_FULL;
1183 else
1184 phydev->duplex = DUPLEX_HALF;
1185
1186 if (bmcr & BMCR_SPEED1000)
1187 phydev->speed = SPEED_1000;
1188 else if (bmcr & BMCR_SPEED100)
1189 phydev->speed = SPEED_100;
1190 else
1191 phydev->speed = SPEED_10;
1192
1193 phydev->pause = 0;
1194 phydev->asym_pause = 0;
1195 phydev->lp_advertising = 0;
1196
1197 return 0;
1198}
1199
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001200/* marvell_read_status_page
1201 *
Jeff Garzikf0c88f92008-03-25 23:53:24 -04001202 * Description:
Alexandr Smirnovbe937f12008-03-19 00:37:24 +03001203 * Check the link, then figure out the current state
1204 * by comparing what we advertise with what the link partner
1205 * advertises. Start by checking the gigabit possibilities,
1206 * then move on to 10/100.
1207 */
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001208static int marvell_read_status_page(struct phy_device *phydev, int page)
Alexandr Smirnovbe937f12008-03-19 00:37:24 +03001209{
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001210 int fiber;
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001211 int err;
Alexandr Smirnovbe937f12008-03-19 00:37:24 +03001212
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001213 /* Detect and update the link, but return if there
Andrew Lunn0c3439b2017-05-17 03:25:59 +02001214 * was an error
1215 */
Andrew Lunn52295662017-05-25 21:42:08 +02001216 if (page == MII_MARVELL_FIBER_PAGE)
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001217 fiber = 1;
1218 else
1219 fiber = 0;
1220
1221 err = marvell_update_link(phydev, fiber);
Alexandr Smirnovbe937f12008-03-19 00:37:24 +03001222 if (err)
1223 return err;
1224
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001225 if (phydev->autoneg == AUTONEG_ENABLE)
1226 err = marvell_read_status_page_an(phydev, fiber);
1227 else
1228 err = marvell_read_status_page_fixed(phydev);
Alexandr Smirnovbe937f12008-03-19 00:37:24 +03001229
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001230 return err;
Alexandr Smirnovbe937f12008-03-19 00:37:24 +03001231}
1232
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001233/* marvell_read_status
1234 *
1235 * Some Marvell's phys have two modes: fiber and copper.
1236 * Both need status checked.
1237 * Description:
1238 * First, check the fiber link and status.
1239 * If the fiber link is down, check the copper link and status which
1240 * will be the default value if both link are down.
1241 */
1242static int marvell_read_status(struct phy_device *phydev)
1243{
1244 int err;
1245
1246 /* Check the fiber mode first */
Russell Kinga13c06522017-01-10 23:13:45 +00001247 if (phydev->supported & SUPPORTED_FIBRE &&
1248 phydev->interface != PHY_INTERFACE_MODE_SGMII) {
Andrew Lunn52295662017-05-25 21:42:08 +02001249 err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001250 if (err < 0)
1251 goto error;
1252
Andrew Lunn52295662017-05-25 21:42:08 +02001253 err = marvell_read_status_page(phydev, MII_MARVELL_FIBER_PAGE);
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001254 if (err < 0)
1255 goto error;
1256
Andrew Lunn0c3439b2017-05-17 03:25:59 +02001257 /* If the fiber link is up, it is the selected and
1258 * used link. In this case, we need to stay in the
1259 * fiber page. Please to be careful about that, avoid
1260 * to restore Copper page in other functions which
1261 * could break the behaviour for some fiber phy like
1262 * 88E1512.
1263 */
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001264 if (phydev->link)
1265 return 0;
1266
1267 /* If fiber link is down, check and save copper mode state */
Andrew Lunn52295662017-05-25 21:42:08 +02001268 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001269 if (err < 0)
1270 goto error;
1271 }
1272
Andrew Lunn52295662017-05-25 21:42:08 +02001273 return marvell_read_status_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001274
1275error:
Andrew Lunn52295662017-05-25 21:42:08 +02001276 marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001277 return err;
1278}
Charles-Antoine Couret3758be32016-07-19 11:13:13 +02001279
1280/* marvell_suspend
1281 *
1282 * Some Marvell's phys have two modes: fiber and copper.
1283 * Both need to be suspended
1284 */
1285static int marvell_suspend(struct phy_device *phydev)
1286{
1287 int err;
1288
1289 /* Suspend the fiber mode first */
1290 if (!(phydev->supported & SUPPORTED_FIBRE)) {
Andrew Lunn52295662017-05-25 21:42:08 +02001291 err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
Charles-Antoine Couret3758be32016-07-19 11:13:13 +02001292 if (err < 0)
1293 goto error;
1294
1295 /* With the page set, use the generic suspend */
1296 err = genphy_suspend(phydev);
1297 if (err < 0)
1298 goto error;
1299
1300 /* Then, the copper link */
Andrew Lunn52295662017-05-25 21:42:08 +02001301 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret3758be32016-07-19 11:13:13 +02001302 if (err < 0)
1303 goto error;
1304 }
1305
1306 /* With the page set, use the generic suspend */
1307 return genphy_suspend(phydev);
1308
1309error:
Andrew Lunn52295662017-05-25 21:42:08 +02001310 marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret3758be32016-07-19 11:13:13 +02001311 return err;
1312}
1313
1314/* marvell_resume
1315 *
1316 * Some Marvell's phys have two modes: fiber and copper.
1317 * Both need to be resumed
1318 */
1319static int marvell_resume(struct phy_device *phydev)
1320{
1321 int err;
1322
1323 /* Resume the fiber mode first */
1324 if (!(phydev->supported & SUPPORTED_FIBRE)) {
Andrew Lunn52295662017-05-25 21:42:08 +02001325 err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
Charles-Antoine Couret3758be32016-07-19 11:13:13 +02001326 if (err < 0)
1327 goto error;
1328
1329 /* With the page set, use the generic resume */
1330 err = genphy_resume(phydev);
1331 if (err < 0)
1332 goto error;
1333
1334 /* Then, the copper link */
Andrew Lunn52295662017-05-25 21:42:08 +02001335 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret3758be32016-07-19 11:13:13 +02001336 if (err < 0)
1337 goto error;
1338 }
1339
1340 /* With the page set, use the generic resume */
1341 return genphy_resume(phydev);
1342
1343error:
Andrew Lunn52295662017-05-25 21:42:08 +02001344 marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret3758be32016-07-19 11:13:13 +02001345 return err;
1346}
1347
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +02001348static int marvell_aneg_done(struct phy_device *phydev)
1349{
1350 int retval = phy_read(phydev, MII_M1011_PHY_STATUS);
Andrew Lunne69d9ed2017-05-17 03:26:00 +02001351
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +02001352 return (retval < 0) ? retval : (retval & MII_M1011_PHY_STATUS_RESOLVED);
1353}
1354
Anatolij Gustschindcd07be2009-04-07 02:01:43 +00001355static int m88e1121_did_interrupt(struct phy_device *phydev)
1356{
1357 int imask;
1358
1359 imask = phy_read(phydev, MII_M1011_IEVENT);
1360
1361 if (imask & MII_M1011_IMASK_INIT)
1362 return 1;
1363
1364 return 0;
1365}
1366
Andrew Lunn23beb382017-05-17 03:26:04 +02001367static void m88e1318_get_wol(struct phy_device *phydev,
1368 struct ethtool_wolinfo *wol)
Michael Stapelberg3871c382013-03-11 13:56:45 +00001369{
1370 wol->supported = WAKE_MAGIC;
1371 wol->wolopts = 0;
1372
Andrew Lunn52295662017-05-25 21:42:08 +02001373 if (marvell_set_page(phydev, MII_MARVELL_WOL_PAGE) < 0)
Michael Stapelberg3871c382013-03-11 13:56:45 +00001374 return;
1375
1376 if (phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL) &
1377 MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
1378 wol->wolopts |= WAKE_MAGIC;
1379
Andrew Lunn52295662017-05-25 21:42:08 +02001380 if (marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE) < 0)
Michael Stapelberg3871c382013-03-11 13:56:45 +00001381 return;
1382}
1383
Andrew Lunn23beb382017-05-17 03:26:04 +02001384static int m88e1318_set_wol(struct phy_device *phydev,
1385 struct ethtool_wolinfo *wol)
Michael Stapelberg3871c382013-03-11 13:56:45 +00001386{
1387 int err, oldpage, temp;
1388
Andrew Lunn6427bb22017-05-17 03:26:03 +02001389 oldpage = marvell_get_page(phydev);
Michael Stapelberg3871c382013-03-11 13:56:45 +00001390
1391 if (wol->wolopts & WAKE_MAGIC) {
1392 /* Explicitly switch to page 0x00, just to be sure */
Andrew Lunn52295662017-05-25 21:42:08 +02001393 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Michael Stapelberg3871c382013-03-11 13:56:45 +00001394 if (err < 0)
1395 return err;
1396
1397 /* Enable the WOL interrupt */
1398 temp = phy_read(phydev, MII_88E1318S_PHY_CSIER);
1399 temp |= MII_88E1318S_PHY_CSIER_WOL_EIE;
1400 err = phy_write(phydev, MII_88E1318S_PHY_CSIER, temp);
1401 if (err < 0)
1402 return err;
1403
Andrew Lunn52295662017-05-25 21:42:08 +02001404 err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE);
Michael Stapelberg3871c382013-03-11 13:56:45 +00001405 if (err < 0)
1406 return err;
1407
1408 /* Setup LED[2] as interrupt pin (active low) */
1409 temp = phy_read(phydev, MII_88E1318S_PHY_LED_TCR);
1410 temp &= ~MII_88E1318S_PHY_LED_TCR_FORCE_INT;
1411 temp |= MII_88E1318S_PHY_LED_TCR_INTn_ENABLE;
1412 temp |= MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW;
1413 err = phy_write(phydev, MII_88E1318S_PHY_LED_TCR, temp);
1414 if (err < 0)
1415 return err;
1416
Andrew Lunn52295662017-05-25 21:42:08 +02001417 err = marvell_set_page(phydev, MII_MARVELL_WOL_PAGE);
Michael Stapelberg3871c382013-03-11 13:56:45 +00001418 if (err < 0)
1419 return err;
1420
1421 /* Store the device address for the magic packet */
1422 err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2,
1423 ((phydev->attached_dev->dev_addr[5] << 8) |
1424 phydev->attached_dev->dev_addr[4]));
1425 if (err < 0)
1426 return err;
1427 err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1,
1428 ((phydev->attached_dev->dev_addr[3] << 8) |
1429 phydev->attached_dev->dev_addr[2]));
1430 if (err < 0)
1431 return err;
1432 err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0,
1433 ((phydev->attached_dev->dev_addr[1] << 8) |
1434 phydev->attached_dev->dev_addr[0]));
1435 if (err < 0)
1436 return err;
1437
1438 /* Clear WOL status and enable magic packet matching */
1439 temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
1440 temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS;
1441 temp |= MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE;
1442 err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp);
1443 if (err < 0)
1444 return err;
1445 } else {
Andrew Lunn52295662017-05-25 21:42:08 +02001446 err = marvell_set_page(phydev, MII_MARVELL_WOL_PAGE);
Michael Stapelberg3871c382013-03-11 13:56:45 +00001447 if (err < 0)
1448 return err;
1449
1450 /* Clear WOL status and disable magic packet matching */
1451 temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
1452 temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS;
1453 temp &= ~MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE;
1454 err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp);
1455 if (err < 0)
1456 return err;
1457 }
1458
Andrew Lunn6427bb22017-05-17 03:26:03 +02001459 err = marvell_set_page(phydev, oldpage);
Michael Stapelberg3871c382013-03-11 13:56:45 +00001460 if (err < 0)
1461 return err;
1462
1463 return 0;
1464}
1465
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001466static int marvell_get_sset_count(struct phy_device *phydev)
1467{
Charles-Antoine Couret2170fef2016-07-19 11:13:11 +02001468 if (phydev->supported & SUPPORTED_FIBRE)
1469 return ARRAY_SIZE(marvell_hw_stats);
1470 else
1471 return ARRAY_SIZE(marvell_hw_stats) - NB_FIBER_STATS;
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001472}
1473
1474static void marvell_get_strings(struct phy_device *phydev, u8 *data)
1475{
1476 int i;
1477
1478 for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++) {
1479 memcpy(data + i * ETH_GSTRING_LEN,
1480 marvell_hw_stats[i].string, ETH_GSTRING_LEN);
1481 }
1482}
1483
1484#ifndef UINT64_MAX
Andrew Lunn8cf8b872017-07-30 22:41:44 +02001485#define UINT64_MAX (u64)(~((u64)0))
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001486#endif
1487static u64 marvell_get_stat(struct phy_device *phydev, int i)
1488{
1489 struct marvell_hw_stat stat = marvell_hw_stats[i];
1490 struct marvell_priv *priv = phydev->priv;
Andrew Lunn53798322017-05-25 21:42:07 +02001491 int oldpage, val;
Andrew Lunn321b4d42016-02-20 00:35:29 +01001492 u64 ret;
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001493
Andrew Lunn53798322017-05-25 21:42:07 +02001494 oldpage = marvell_get_set_page(phydev, stat.page);
1495 if (oldpage < 0)
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001496 return UINT64_MAX;
1497
1498 val = phy_read(phydev, stat.reg);
1499 if (val < 0) {
Andrew Lunn321b4d42016-02-20 00:35:29 +01001500 ret = UINT64_MAX;
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001501 } else {
1502 val = val & ((1 << stat.bits) - 1);
1503 priv->stats[i] += val;
Andrew Lunn321b4d42016-02-20 00:35:29 +01001504 ret = priv->stats[i];
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001505 }
1506
Andrew Lunn6427bb22017-05-17 03:26:03 +02001507 marvell_set_page(phydev, oldpage);
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001508
Andrew Lunn321b4d42016-02-20 00:35:29 +01001509 return ret;
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001510}
1511
1512static void marvell_get_stats(struct phy_device *phydev,
1513 struct ethtool_stats *stats, u64 *data)
1514{
1515 int i;
1516
1517 for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++)
1518 data[i] = marvell_get_stat(phydev, i);
1519}
1520
Andrew Lunn0b046802017-01-20 01:37:49 +01001521#ifdef CONFIG_HWMON
1522static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
1523{
Andrew Lunn975b3882017-05-25 21:42:06 +02001524 int oldpage;
Andrew Lunn0b046802017-01-20 01:37:49 +01001525 int ret;
1526 int val;
1527
1528 *temp = 0;
1529
1530 mutex_lock(&phydev->lock);
1531
Andrew Lunn52295662017-05-25 21:42:08 +02001532 oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
Andrew Lunn975b3882017-05-25 21:42:06 +02001533 if (oldpage < 0) {
1534 mutex_unlock(&phydev->lock);
1535 return oldpage;
1536 }
1537
Andrew Lunn0b046802017-01-20 01:37:49 +01001538 /* Enable temperature sensor */
1539 ret = phy_read(phydev, MII_88E1121_MISC_TEST);
1540 if (ret < 0)
1541 goto error;
1542
1543 ret = phy_write(phydev, MII_88E1121_MISC_TEST,
1544 ret | MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
1545 if (ret < 0)
1546 goto error;
1547
1548 /* Wait for temperature to stabilize */
1549 usleep_range(10000, 12000);
1550
1551 val = phy_read(phydev, MII_88E1121_MISC_TEST);
1552 if (val < 0) {
1553 ret = val;
1554 goto error;
1555 }
1556
1557 /* Disable temperature sensor */
1558 ret = phy_write(phydev, MII_88E1121_MISC_TEST,
1559 ret & ~MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
1560 if (ret < 0)
1561 goto error;
1562
1563 *temp = ((val & MII_88E1121_MISC_TEST_TEMP_MASK) - 5) * 5000;
1564
1565error:
Andrew Lunn975b3882017-05-25 21:42:06 +02001566 marvell_set_page(phydev, oldpage);
Andrew Lunn0b046802017-01-20 01:37:49 +01001567 mutex_unlock(&phydev->lock);
1568
1569 return ret;
1570}
1571
1572static int m88e1121_hwmon_read(struct device *dev,
1573 enum hwmon_sensor_types type,
1574 u32 attr, int channel, long *temp)
1575{
1576 struct phy_device *phydev = dev_get_drvdata(dev);
1577 int err;
1578
1579 switch (attr) {
1580 case hwmon_temp_input:
1581 err = m88e1121_get_temp(phydev, temp);
1582 break;
1583 default:
1584 return -EOPNOTSUPP;
1585 }
1586
1587 return err;
1588}
1589
1590static umode_t m88e1121_hwmon_is_visible(const void *data,
1591 enum hwmon_sensor_types type,
1592 u32 attr, int channel)
1593{
1594 if (type != hwmon_temp)
1595 return 0;
1596
1597 switch (attr) {
1598 case hwmon_temp_input:
1599 return 0444;
1600 default:
1601 return 0;
1602 }
1603}
1604
1605static u32 m88e1121_hwmon_chip_config[] = {
1606 HWMON_C_REGISTER_TZ,
1607 0
1608};
1609
1610static const struct hwmon_channel_info m88e1121_hwmon_chip = {
1611 .type = hwmon_chip,
1612 .config = m88e1121_hwmon_chip_config,
1613};
1614
1615static u32 m88e1121_hwmon_temp_config[] = {
1616 HWMON_T_INPUT,
1617 0
1618};
1619
1620static const struct hwmon_channel_info m88e1121_hwmon_temp = {
1621 .type = hwmon_temp,
1622 .config = m88e1121_hwmon_temp_config,
1623};
1624
1625static const struct hwmon_channel_info *m88e1121_hwmon_info[] = {
1626 &m88e1121_hwmon_chip,
1627 &m88e1121_hwmon_temp,
1628 NULL
1629};
1630
1631static const struct hwmon_ops m88e1121_hwmon_hwmon_ops = {
1632 .is_visible = m88e1121_hwmon_is_visible,
1633 .read = m88e1121_hwmon_read,
1634};
1635
1636static const struct hwmon_chip_info m88e1121_hwmon_chip_info = {
1637 .ops = &m88e1121_hwmon_hwmon_ops,
1638 .info = m88e1121_hwmon_info,
1639};
1640
1641static int m88e1510_get_temp(struct phy_device *phydev, long *temp)
1642{
Andrew Lunn975b3882017-05-25 21:42:06 +02001643 int oldpage;
Andrew Lunn0b046802017-01-20 01:37:49 +01001644 int ret;
1645
1646 *temp = 0;
1647
1648 mutex_lock(&phydev->lock);
1649
Andrew Lunn52295662017-05-25 21:42:08 +02001650 oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
Andrew Lunn975b3882017-05-25 21:42:06 +02001651 if (oldpage < 0) {
1652 mutex_unlock(&phydev->lock);
1653 return oldpage;
1654 }
1655
Andrew Lunn0b046802017-01-20 01:37:49 +01001656 ret = phy_read(phydev, MII_88E1510_TEMP_SENSOR);
1657 if (ret < 0)
1658 goto error;
1659
1660 *temp = ((ret & MII_88E1510_TEMP_SENSOR_MASK) - 25) * 1000;
1661
1662error:
Andrew Lunn975b3882017-05-25 21:42:06 +02001663 marvell_set_page(phydev, oldpage);
Andrew Lunn0b046802017-01-20 01:37:49 +01001664 mutex_unlock(&phydev->lock);
1665
1666 return ret;
1667}
1668
Colin Ian Kingf0a45812017-06-02 15:13:34 +01001669static int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp)
Andrew Lunn0b046802017-01-20 01:37:49 +01001670{
Andrew Lunn975b3882017-05-25 21:42:06 +02001671 int oldpage;
Andrew Lunn0b046802017-01-20 01:37:49 +01001672 int ret;
1673
1674 *temp = 0;
1675
1676 mutex_lock(&phydev->lock);
Andrew Lunn53798322017-05-25 21:42:07 +02001677
Andrew Lunn52295662017-05-25 21:42:08 +02001678 oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
Andrew Lunn975b3882017-05-25 21:42:06 +02001679 if (oldpage < 0) {
1680 mutex_unlock(&phydev->lock);
1681 return oldpage;
1682 }
Andrew Lunn0b046802017-01-20 01:37:49 +01001683
Andrew Lunn0b046802017-01-20 01:37:49 +01001684 ret = phy_read(phydev, MII_88E1121_MISC_TEST);
1685 if (ret < 0)
1686 goto error;
1687
1688 *temp = (((ret & MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) >>
1689 MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT) * 5) - 25;
1690 /* convert to mC */
1691 *temp *= 1000;
1692
1693error:
Andrew Lunn975b3882017-05-25 21:42:06 +02001694 marvell_set_page(phydev, oldpage);
Andrew Lunn0b046802017-01-20 01:37:49 +01001695 mutex_unlock(&phydev->lock);
1696
1697 return ret;
1698}
1699
Colin Ian Kingf0a45812017-06-02 15:13:34 +01001700static int m88e1510_set_temp_critical(struct phy_device *phydev, long temp)
Andrew Lunn0b046802017-01-20 01:37:49 +01001701{
Andrew Lunn975b3882017-05-25 21:42:06 +02001702 int oldpage;
Andrew Lunn0b046802017-01-20 01:37:49 +01001703 int ret;
1704
1705 mutex_lock(&phydev->lock);
1706
Andrew Lunn52295662017-05-25 21:42:08 +02001707 oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
Andrew Lunn975b3882017-05-25 21:42:06 +02001708 if (oldpage < 0) {
1709 mutex_unlock(&phydev->lock);
1710 return oldpage;
1711 }
1712
Andrew Lunn0b046802017-01-20 01:37:49 +01001713 ret = phy_read(phydev, MII_88E1121_MISC_TEST);
1714 if (ret < 0)
1715 goto error;
1716
1717 temp = temp / 1000;
1718 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
1719 ret = phy_write(phydev, MII_88E1121_MISC_TEST,
1720 (ret & ~MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) |
1721 (temp << MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT));
1722
1723error:
Andrew Lunn975b3882017-05-25 21:42:06 +02001724 marvell_set_page(phydev, oldpage);
Andrew Lunn0b046802017-01-20 01:37:49 +01001725 mutex_unlock(&phydev->lock);
1726
1727 return ret;
1728}
1729
Colin Ian Kingf0a45812017-06-02 15:13:34 +01001730static int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm)
Andrew Lunn0b046802017-01-20 01:37:49 +01001731{
Andrew Lunn975b3882017-05-25 21:42:06 +02001732 int oldpage;
Andrew Lunn0b046802017-01-20 01:37:49 +01001733 int ret;
1734
1735 *alarm = false;
1736
1737 mutex_lock(&phydev->lock);
1738
Andrew Lunn52295662017-05-25 21:42:08 +02001739 oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
Andrew Lunn975b3882017-05-25 21:42:06 +02001740 if (oldpage < 0) {
1741 mutex_unlock(&phydev->lock);
1742 return oldpage;
1743 }
1744
Andrew Lunn0b046802017-01-20 01:37:49 +01001745 ret = phy_read(phydev, MII_88E1121_MISC_TEST);
1746 if (ret < 0)
1747 goto error;
1748 *alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ);
1749
1750error:
Andrew Lunn975b3882017-05-25 21:42:06 +02001751 marvell_set_page(phydev, oldpage);
Andrew Lunn0b046802017-01-20 01:37:49 +01001752 mutex_unlock(&phydev->lock);
1753
1754 return ret;
1755}
1756
1757static int m88e1510_hwmon_read(struct device *dev,
1758 enum hwmon_sensor_types type,
1759 u32 attr, int channel, long *temp)
1760{
1761 struct phy_device *phydev = dev_get_drvdata(dev);
1762 int err;
1763
1764 switch (attr) {
1765 case hwmon_temp_input:
1766 err = m88e1510_get_temp(phydev, temp);
1767 break;
1768 case hwmon_temp_crit:
1769 err = m88e1510_get_temp_critical(phydev, temp);
1770 break;
1771 case hwmon_temp_max_alarm:
1772 err = m88e1510_get_temp_alarm(phydev, temp);
1773 break;
1774 default:
1775 return -EOPNOTSUPP;
1776 }
1777
1778 return err;
1779}
1780
1781static int m88e1510_hwmon_write(struct device *dev,
1782 enum hwmon_sensor_types type,
1783 u32 attr, int channel, long temp)
1784{
1785 struct phy_device *phydev = dev_get_drvdata(dev);
1786 int err;
1787
1788 switch (attr) {
1789 case hwmon_temp_crit:
1790 err = m88e1510_set_temp_critical(phydev, temp);
1791 break;
1792 default:
1793 return -EOPNOTSUPP;
1794 }
1795 return err;
1796}
1797
1798static umode_t m88e1510_hwmon_is_visible(const void *data,
1799 enum hwmon_sensor_types type,
1800 u32 attr, int channel)
1801{
1802 if (type != hwmon_temp)
1803 return 0;
1804
1805 switch (attr) {
1806 case hwmon_temp_input:
1807 case hwmon_temp_max_alarm:
1808 return 0444;
1809 case hwmon_temp_crit:
1810 return 0644;
1811 default:
1812 return 0;
1813 }
1814}
1815
1816static u32 m88e1510_hwmon_temp_config[] = {
1817 HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM,
1818 0
1819};
1820
1821static const struct hwmon_channel_info m88e1510_hwmon_temp = {
1822 .type = hwmon_temp,
1823 .config = m88e1510_hwmon_temp_config,
1824};
1825
1826static const struct hwmon_channel_info *m88e1510_hwmon_info[] = {
1827 &m88e1121_hwmon_chip,
1828 &m88e1510_hwmon_temp,
1829 NULL
1830};
1831
1832static const struct hwmon_ops m88e1510_hwmon_hwmon_ops = {
1833 .is_visible = m88e1510_hwmon_is_visible,
1834 .read = m88e1510_hwmon_read,
1835 .write = m88e1510_hwmon_write,
1836};
1837
1838static const struct hwmon_chip_info m88e1510_hwmon_chip_info = {
1839 .ops = &m88e1510_hwmon_hwmon_ops,
1840 .info = m88e1510_hwmon_info,
1841};
1842
1843static int marvell_hwmon_name(struct phy_device *phydev)
1844{
1845 struct marvell_priv *priv = phydev->priv;
1846 struct device *dev = &phydev->mdio.dev;
1847 const char *devname = dev_name(dev);
1848 size_t len = strlen(devname);
1849 int i, j;
1850
1851 priv->hwmon_name = devm_kzalloc(dev, len, GFP_KERNEL);
1852 if (!priv->hwmon_name)
1853 return -ENOMEM;
1854
1855 for (i = j = 0; i < len && devname[i]; i++) {
1856 if (isalnum(devname[i]))
1857 priv->hwmon_name[j++] = devname[i];
1858 }
1859
1860 return 0;
1861}
1862
1863static int marvell_hwmon_probe(struct phy_device *phydev,
1864 const struct hwmon_chip_info *chip)
1865{
1866 struct marvell_priv *priv = phydev->priv;
1867 struct device *dev = &phydev->mdio.dev;
1868 int err;
1869
1870 err = marvell_hwmon_name(phydev);
1871 if (err)
1872 return err;
1873
1874 priv->hwmon_dev = devm_hwmon_device_register_with_info(
1875 dev, priv->hwmon_name, phydev, chip, NULL);
1876
1877 return PTR_ERR_OR_ZERO(priv->hwmon_dev);
1878}
1879
1880static int m88e1121_hwmon_probe(struct phy_device *phydev)
1881{
1882 return marvell_hwmon_probe(phydev, &m88e1121_hwmon_chip_info);
1883}
1884
1885static int m88e1510_hwmon_probe(struct phy_device *phydev)
1886{
1887 return marvell_hwmon_probe(phydev, &m88e1510_hwmon_chip_info);
1888}
1889#else
1890static int m88e1121_hwmon_probe(struct phy_device *phydev)
1891{
1892 return 0;
1893}
1894
1895static int m88e1510_hwmon_probe(struct phy_device *phydev)
1896{
1897 return 0;
1898}
1899#endif
1900
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001901static int marvell_probe(struct phy_device *phydev)
1902{
1903 struct marvell_priv *priv;
1904
Andrew Lunne5a03bf2016-01-06 20:11:16 +01001905 priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001906 if (!priv)
1907 return -ENOMEM;
1908
1909 phydev->priv = priv;
1910
1911 return 0;
1912}
1913
Andrew Lunn0b046802017-01-20 01:37:49 +01001914static int m88e1121_probe(struct phy_device *phydev)
1915{
1916 int err;
1917
1918 err = marvell_probe(phydev);
1919 if (err)
1920 return err;
1921
1922 return m88e1121_hwmon_probe(phydev);
1923}
1924
1925static int m88e1510_probe(struct phy_device *phydev)
1926{
1927 int err;
1928
1929 err = marvell_probe(phydev);
1930 if (err)
1931 return err;
1932
1933 return m88e1510_hwmon_probe(phydev);
1934}
1935
Olof Johanssone5479232007-07-03 16:23:46 -05001936static struct phy_driver marvell_drivers[] = {
1937 {
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +10001938 .phy_id = MARVELL_PHY_ID_88E1101,
1939 .phy_id_mask = MARVELL_PHY_ID_MASK,
Olof Johanssone5479232007-07-03 16:23:46 -05001940 .name = "Marvell 88E1101",
1941 .features = PHY_GBIT_FEATURES,
1942 .flags = PHY_HAS_INTERRUPT,
Arnd Bergmann18702412017-01-23 13:18:41 +01001943 .probe = marvell_probe,
Clemens Gruber79be1a12016-02-15 23:46:45 +01001944 .config_init = &marvell_config_init,
Andrew Lunnf2899782017-05-23 17:49:13 +02001945 .config_aneg = &m88e1101_config_aneg,
Olof Johanssone5479232007-07-03 16:23:46 -05001946 .read_status = &genphy_read_status,
1947 .ack_interrupt = &marvell_ack_interrupt,
1948 .config_intr = &marvell_config_intr,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01001949 .resume = &genphy_resume,
1950 .suspend = &genphy_suspend,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001951 .get_sset_count = marvell_get_sset_count,
1952 .get_strings = marvell_get_strings,
1953 .get_stats = marvell_get_stats,
Olof Johanssone5479232007-07-03 16:23:46 -05001954 },
1955 {
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +10001956 .phy_id = MARVELL_PHY_ID_88E1112,
1957 .phy_id_mask = MARVELL_PHY_ID_MASK,
Olof Johansson85cfb532007-07-03 16:24:32 -05001958 .name = "Marvell 88E1112",
1959 .features = PHY_GBIT_FEATURES,
1960 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001961 .probe = marvell_probe,
Olof Johansson85cfb532007-07-03 16:24:32 -05001962 .config_init = &m88e1111_config_init,
1963 .config_aneg = &marvell_config_aneg,
1964 .read_status = &genphy_read_status,
1965 .ack_interrupt = &marvell_ack_interrupt,
1966 .config_intr = &marvell_config_intr,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01001967 .resume = &genphy_resume,
1968 .suspend = &genphy_suspend,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001969 .get_sset_count = marvell_get_sset_count,
1970 .get_strings = marvell_get_strings,
1971 .get_stats = marvell_get_stats,
Olof Johansson85cfb532007-07-03 16:24:32 -05001972 },
1973 {
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +10001974 .phy_id = MARVELL_PHY_ID_88E1111,
1975 .phy_id_mask = MARVELL_PHY_ID_MASK,
Olof Johanssone5479232007-07-03 16:23:46 -05001976 .name = "Marvell 88E1111",
1977 .features = PHY_GBIT_FEATURES,
1978 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001979 .probe = marvell_probe,
Olof Johanssone5479232007-07-03 16:23:46 -05001980 .config_init = &m88e1111_config_init,
Harini Katakam3ec0a0f2016-06-27 13:09:59 +05301981 .config_aneg = &m88e1111_config_aneg,
Alexandr Smirnovbe937f12008-03-19 00:37:24 +03001982 .read_status = &marvell_read_status,
Olof Johanssone5479232007-07-03 16:23:46 -05001983 .ack_interrupt = &marvell_ack_interrupt,
1984 .config_intr = &marvell_config_intr,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01001985 .resume = &genphy_resume,
1986 .suspend = &genphy_suspend,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001987 .get_sset_count = marvell_get_sset_count,
1988 .get_strings = marvell_get_strings,
1989 .get_stats = marvell_get_stats,
Olof Johanssone5479232007-07-03 16:23:46 -05001990 },
1991 {
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +10001992 .phy_id = MARVELL_PHY_ID_88E1118,
1993 .phy_id_mask = MARVELL_PHY_ID_MASK,
Ron Madrid605f1962008-11-06 09:05:26 +00001994 .name = "Marvell 88E1118",
1995 .features = PHY_GBIT_FEATURES,
1996 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001997 .probe = marvell_probe,
Ron Madrid605f1962008-11-06 09:05:26 +00001998 .config_init = &m88e1118_config_init,
1999 .config_aneg = &m88e1118_config_aneg,
2000 .read_status = &genphy_read_status,
2001 .ack_interrupt = &marvell_ack_interrupt,
2002 .config_intr = &marvell_config_intr,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01002003 .resume = &genphy_resume,
2004 .suspend = &genphy_suspend,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002005 .get_sset_count = marvell_get_sset_count,
2006 .get_strings = marvell_get_strings,
2007 .get_stats = marvell_get_stats,
Ron Madrid605f1962008-11-06 09:05:26 +00002008 },
2009 {
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +10002010 .phy_id = MARVELL_PHY_ID_88E1121R,
2011 .phy_id_mask = MARVELL_PHY_ID_MASK,
Sergei Poselenov140bc922009-04-07 02:01:41 +00002012 .name = "Marvell 88E1121R",
2013 .features = PHY_GBIT_FEATURES,
2014 .flags = PHY_HAS_INTERRUPT,
Arnd Bergmann18702412017-01-23 13:18:41 +01002015 .probe = &m88e1121_probe,
Clemens Gruberfdecf362016-06-11 17:21:26 +02002016 .config_init = &m88e1121_config_init,
Sergei Poselenov140bc922009-04-07 02:01:41 +00002017 .config_aneg = &m88e1121_config_aneg,
2018 .read_status = &marvell_read_status,
2019 .ack_interrupt = &marvell_ack_interrupt,
2020 .config_intr = &marvell_config_intr,
Anatolij Gustschindcd07be2009-04-07 02:01:43 +00002021 .did_interrupt = &m88e1121_did_interrupt,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01002022 .resume = &genphy_resume,
2023 .suspend = &genphy_suspend,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002024 .get_sset_count = marvell_get_sset_count,
2025 .get_strings = marvell_get_strings,
2026 .get_stats = marvell_get_stats,
Sergei Poselenov140bc922009-04-07 02:01:41 +00002027 },
2028 {
Cyril Chemparathy337ac9d2010-10-29 13:50:25 -07002029 .phy_id = MARVELL_PHY_ID_88E1318S,
Linus Torvalds6ba74012010-08-04 11:47:58 -07002030 .phy_id_mask = MARVELL_PHY_ID_MASK,
Cyril Chemparathy337ac9d2010-10-29 13:50:25 -07002031 .name = "Marvell 88E1318S",
Cyril Chemparathy3ff1c252010-08-03 19:36:06 -07002032 .features = PHY_GBIT_FEATURES,
2033 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002034 .probe = marvell_probe,
Clemens Gruberfdecf362016-06-11 17:21:26 +02002035 .config_init = &m88e1121_config_init,
Cyril Chemparathy337ac9d2010-10-29 13:50:25 -07002036 .config_aneg = &m88e1318_config_aneg,
Cyril Chemparathy3ff1c252010-08-03 19:36:06 -07002037 .read_status = &marvell_read_status,
2038 .ack_interrupt = &marvell_ack_interrupt,
2039 .config_intr = &marvell_config_intr,
2040 .did_interrupt = &m88e1121_did_interrupt,
Michael Stapelberg3871c382013-03-11 13:56:45 +00002041 .get_wol = &m88e1318_get_wol,
2042 .set_wol = &m88e1318_set_wol,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01002043 .resume = &genphy_resume,
2044 .suspend = &genphy_suspend,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002045 .get_sset_count = marvell_get_sset_count,
2046 .get_strings = marvell_get_strings,
2047 .get_stats = marvell_get_stats,
Cyril Chemparathy3ff1c252010-08-03 19:36:06 -07002048 },
2049 {
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +10002050 .phy_id = MARVELL_PHY_ID_88E1145,
2051 .phy_id_mask = MARVELL_PHY_ID_MASK,
Olof Johanssone5479232007-07-03 16:23:46 -05002052 .name = "Marvell 88E1145",
2053 .features = PHY_GBIT_FEATURES,
2054 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002055 .probe = marvell_probe,
Olof Johanssone5479232007-07-03 16:23:46 -05002056 .config_init = &m88e1145_config_init,
2057 .config_aneg = &marvell_config_aneg,
2058 .read_status = &genphy_read_status,
2059 .ack_interrupt = &marvell_ack_interrupt,
2060 .config_intr = &marvell_config_intr,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01002061 .resume = &genphy_resume,
2062 .suspend = &genphy_suspend,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002063 .get_sset_count = marvell_get_sset_count,
2064 .get_strings = marvell_get_strings,
2065 .get_stats = marvell_get_stats,
Olof Johanssonac8c6352007-11-04 16:08:51 -06002066 },
2067 {
David Daney90600732010-11-19 11:58:53 +00002068 .phy_id = MARVELL_PHY_ID_88E1149R,
2069 .phy_id_mask = MARVELL_PHY_ID_MASK,
2070 .name = "Marvell 88E1149R",
2071 .features = PHY_GBIT_FEATURES,
2072 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002073 .probe = marvell_probe,
David Daney90600732010-11-19 11:58:53 +00002074 .config_init = &m88e1149_config_init,
2075 .config_aneg = &m88e1118_config_aneg,
2076 .read_status = &genphy_read_status,
2077 .ack_interrupt = &marvell_ack_interrupt,
2078 .config_intr = &marvell_config_intr,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01002079 .resume = &genphy_resume,
2080 .suspend = &genphy_suspend,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002081 .get_sset_count = marvell_get_sset_count,
2082 .get_strings = marvell_get_strings,
2083 .get_stats = marvell_get_stats,
David Daney90600732010-11-19 11:58:53 +00002084 },
2085 {
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +10002086 .phy_id = MARVELL_PHY_ID_88E1240,
2087 .phy_id_mask = MARVELL_PHY_ID_MASK,
Olof Johanssonac8c6352007-11-04 16:08:51 -06002088 .name = "Marvell 88E1240",
2089 .features = PHY_GBIT_FEATURES,
2090 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002091 .probe = marvell_probe,
Olof Johanssonac8c6352007-11-04 16:08:51 -06002092 .config_init = &m88e1111_config_init,
2093 .config_aneg = &marvell_config_aneg,
2094 .read_status = &genphy_read_status,
2095 .ack_interrupt = &marvell_ack_interrupt,
2096 .config_intr = &marvell_config_intr,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01002097 .resume = &genphy_resume,
2098 .suspend = &genphy_suspend,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002099 .get_sset_count = marvell_get_sset_count,
2100 .get_strings = marvell_get_strings,
2101 .get_stats = marvell_get_stats,
Olof Johanssonac8c6352007-11-04 16:08:51 -06002102 },
Michal Simek3da09a52013-05-30 20:08:26 +00002103 {
2104 .phy_id = MARVELL_PHY_ID_88E1116R,
2105 .phy_id_mask = MARVELL_PHY_ID_MASK,
2106 .name = "Marvell 88E1116R",
2107 .features = PHY_GBIT_FEATURES,
2108 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002109 .probe = marvell_probe,
Michal Simek3da09a52013-05-30 20:08:26 +00002110 .config_init = &m88e1116r_config_init,
2111 .config_aneg = &genphy_config_aneg,
2112 .read_status = &genphy_read_status,
2113 .ack_interrupt = &marvell_ack_interrupt,
2114 .config_intr = &marvell_config_intr,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01002115 .resume = &genphy_resume,
2116 .suspend = &genphy_suspend,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002117 .get_sset_count = marvell_get_sset_count,
2118 .get_strings = marvell_get_strings,
2119 .get_stats = marvell_get_stats,
Michal Simek3da09a52013-05-30 20:08:26 +00002120 },
Michal Simek10e24caa2013-05-30 20:08:27 +00002121 {
2122 .phy_id = MARVELL_PHY_ID_88E1510,
2123 .phy_id_mask = MARVELL_PHY_ID_MASK,
2124 .name = "Marvell 88E1510",
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02002125 .features = PHY_GBIT_FEATURES | SUPPORTED_FIBRE,
Arnd Bergmann18702412017-01-23 13:18:41 +01002126 .flags = PHY_HAS_INTERRUPT,
Andrew Lunn0b046802017-01-20 01:37:49 +01002127 .probe = &m88e1510_probe,
Stefan Roese930b37e2016-02-18 10:59:07 +01002128 .config_init = &m88e1510_config_init,
Michal Simek10e24caa2013-05-30 20:08:27 +00002129 .config_aneg = &m88e1510_config_aneg,
2130 .read_status = &marvell_read_status,
2131 .ack_interrupt = &marvell_ack_interrupt,
2132 .config_intr = &marvell_config_intr,
2133 .did_interrupt = &m88e1121_did_interrupt,
Jingju Houf39aac72017-01-22 18:20:56 +08002134 .get_wol = &m88e1318_get_wol,
2135 .set_wol = &m88e1318_set_wol,
Charles-Antoine Couret3758be32016-07-19 11:13:13 +02002136 .resume = &marvell_resume,
2137 .suspend = &marvell_suspend,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002138 .get_sset_count = marvell_get_sset_count,
2139 .get_strings = marvell_get_strings,
2140 .get_stats = marvell_get_stats,
Lin Yun Shengf0f9b4e2017-06-30 17:44:15 +08002141 .set_loopback = genphy_loopback,
Michal Simek10e24caa2013-05-30 20:08:27 +00002142 },
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +02002143 {
Andrew Lunn819ec8e2015-11-16 23:34:41 +01002144 .phy_id = MARVELL_PHY_ID_88E1540,
2145 .phy_id_mask = MARVELL_PHY_ID_MASK,
2146 .name = "Marvell 88E1540",
2147 .features = PHY_GBIT_FEATURES,
2148 .flags = PHY_HAS_INTERRUPT,
Arnd Bergmann18702412017-01-23 13:18:41 +01002149 .probe = m88e1510_probe,
Clemens Gruber79be1a12016-02-15 23:46:45 +01002150 .config_init = &marvell_config_init,
Andrew Lunn819ec8e2015-11-16 23:34:41 +01002151 .config_aneg = &m88e1510_config_aneg,
2152 .read_status = &marvell_read_status,
2153 .ack_interrupt = &marvell_ack_interrupt,
2154 .config_intr = &marvell_config_intr,
2155 .did_interrupt = &m88e1121_did_interrupt,
2156 .resume = &genphy_resume,
2157 .suspend = &genphy_suspend,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002158 .get_sset_count = marvell_get_sset_count,
2159 .get_strings = marvell_get_strings,
2160 .get_stats = marvell_get_stats,
Andrew Lunn819ec8e2015-11-16 23:34:41 +01002161 },
2162 {
Andrew Lunn60f06fd2017-02-02 00:35:03 +01002163 .phy_id = MARVELL_PHY_ID_88E1545,
2164 .phy_id_mask = MARVELL_PHY_ID_MASK,
2165 .name = "Marvell 88E1545",
2166 .probe = m88e1510_probe,
Andrew Lunn60f06fd2017-02-02 00:35:03 +01002167 .features = PHY_GBIT_FEATURES,
2168 .flags = PHY_HAS_INTERRUPT,
2169 .config_init = &marvell_config_init,
2170 .config_aneg = &m88e1510_config_aneg,
2171 .read_status = &marvell_read_status,
2172 .ack_interrupt = &marvell_ack_interrupt,
2173 .config_intr = &marvell_config_intr,
2174 .did_interrupt = &m88e1121_did_interrupt,
2175 .resume = &genphy_resume,
2176 .suspend = &genphy_suspend,
2177 .get_sset_count = marvell_get_sset_count,
2178 .get_strings = marvell_get_strings,
2179 .get_stats = marvell_get_stats,
2180 },
2181 {
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +02002182 .phy_id = MARVELL_PHY_ID_88E3016,
2183 .phy_id_mask = MARVELL_PHY_ID_MASK,
2184 .name = "Marvell 88E3016",
2185 .features = PHY_BASIC_FEATURES,
2186 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002187 .probe = marvell_probe,
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +02002188 .config_aneg = &genphy_config_aneg,
2189 .config_init = &m88e3016_config_init,
2190 .aneg_done = &marvell_aneg_done,
2191 .read_status = &marvell_read_status,
2192 .ack_interrupt = &marvell_ack_interrupt,
2193 .config_intr = &marvell_config_intr,
2194 .did_interrupt = &m88e1121_did_interrupt,
2195 .resume = &genphy_resume,
2196 .suspend = &genphy_suspend,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002197 .get_sset_count = marvell_get_sset_count,
2198 .get_strings = marvell_get_strings,
2199 .get_stats = marvell_get_stats,
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +02002200 },
Andrew Lunne4cf8a32017-02-01 03:40:06 +01002201 {
2202 .phy_id = MARVELL_PHY_ID_88E6390,
2203 .phy_id_mask = MARVELL_PHY_ID_MASK,
2204 .name = "Marvell 88E6390",
2205 .features = PHY_GBIT_FEATURES,
2206 .flags = PHY_HAS_INTERRUPT,
2207 .probe = m88e1510_probe,
2208 .config_init = &marvell_config_init,
2209 .config_aneg = &m88e1510_config_aneg,
2210 .read_status = &marvell_read_status,
2211 .ack_interrupt = &marvell_ack_interrupt,
2212 .config_intr = &marvell_config_intr,
2213 .did_interrupt = &m88e1121_did_interrupt,
2214 .resume = &genphy_resume,
2215 .suspend = &genphy_suspend,
2216 .get_sset_count = marvell_get_sset_count,
2217 .get_strings = marvell_get_strings,
2218 .get_stats = marvell_get_stats,
2219 },
Andy Fleming00db8182005-07-30 19:31:23 -04002220};
2221
Johan Hovold50fd7152014-11-11 19:45:59 +01002222module_phy_driver(marvell_drivers);
David Woodhouse4e4f10f2010-04-02 01:05:56 +00002223
Uwe Kleine-Königcf93c942010-10-03 23:43:32 +00002224static struct mdio_device_id __maybe_unused marvell_tbl[] = {
Michal Simekf5e1cab2013-05-30 20:08:25 +00002225 { MARVELL_PHY_ID_88E1101, MARVELL_PHY_ID_MASK },
2226 { MARVELL_PHY_ID_88E1112, MARVELL_PHY_ID_MASK },
2227 { MARVELL_PHY_ID_88E1111, MARVELL_PHY_ID_MASK },
2228 { MARVELL_PHY_ID_88E1118, MARVELL_PHY_ID_MASK },
2229 { MARVELL_PHY_ID_88E1121R, MARVELL_PHY_ID_MASK },
2230 { MARVELL_PHY_ID_88E1145, MARVELL_PHY_ID_MASK },
2231 { MARVELL_PHY_ID_88E1149R, MARVELL_PHY_ID_MASK },
2232 { MARVELL_PHY_ID_88E1240, MARVELL_PHY_ID_MASK },
2233 { MARVELL_PHY_ID_88E1318S, MARVELL_PHY_ID_MASK },
Michal Simek3da09a52013-05-30 20:08:26 +00002234 { MARVELL_PHY_ID_88E1116R, MARVELL_PHY_ID_MASK },
Michal Simek10e24caa2013-05-30 20:08:27 +00002235 { MARVELL_PHY_ID_88E1510, MARVELL_PHY_ID_MASK },
Andrew Lunn819ec8e2015-11-16 23:34:41 +01002236 { MARVELL_PHY_ID_88E1540, MARVELL_PHY_ID_MASK },
Andrew Lunn60f06fd2017-02-02 00:35:03 +01002237 { MARVELL_PHY_ID_88E1545, MARVELL_PHY_ID_MASK },
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +02002238 { MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK },
Andrew Lunne4cf8a32017-02-01 03:40:06 +01002239 { MARVELL_PHY_ID_88E6390, MARVELL_PHY_ID_MASK },
David Woodhouse4e4f10f2010-04-02 01:05:56 +00002240 { }
2241};
2242
2243MODULE_DEVICE_TABLE(mdio, marvell_tbl);