blob: ac39d909d07a864372f92e30592794673e0bc79e [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
Andrew Lunnfecd5e92017-07-30 22:41:49 +020058#define MII_M1011_PHY_SCR 0x10
59#define MII_M1011_PHY_SCR_DOWNSHIFT_EN BIT(11)
60#define MII_M1011_PHY_SCR_DOWNSHIFT_SHIFT 12
Andrew Lunn6ef05eb2017-07-30 22:41:50 +020061#define MII_M1011_PHY_SRC_DOWNSHIFT_MASK 0x7800
Andrew Lunnfecd5e92017-07-30 22:41:49 +020062#define MII_M1011_PHY_SCR_MDI (0x0 << 5)
63#define MII_M1011_PHY_SCR_MDI_X (0x1 << 5)
64#define MII_M1011_PHY_SCR_AUTO_CROSS (0x3 << 5)
Andy Fleming76884672007-02-09 18:13:58 -060065
Andy Fleming76884672007-02-09 18:13:58 -060066#define MII_M1111_PHY_LED_CONTROL 0x18
67#define MII_M1111_PHY_LED_DIRECT 0x4100
68#define MII_M1111_PHY_LED_COMBINE 0x411c
Kim Phillips895ee682007-06-05 18:46:47 +080069#define MII_M1111_PHY_EXT_CR 0x14
Andrew Lunn61111592017-07-30 22:41:46 +020070#define MII_M1111_RGMII_RX_DELAY BIT(7)
71#define MII_M1111_RGMII_TX_DELAY BIT(1)
Kim Phillips895ee682007-06-05 18:46:47 +080072#define MII_M1111_PHY_EXT_SR 0x1b
Alexandr Smirnovbe937f12008-03-19 00:37:24 +030073
74#define MII_M1111_HWCFG_MODE_MASK 0xf
Alexandr Smirnovbe937f12008-03-19 00:37:24 +030075#define MII_M1111_HWCFG_MODE_FIBER_RGMII 0x3
Kapil Juneja4117b5b2007-05-11 18:25:18 -050076#define MII_M1111_HWCFG_MODE_SGMII_NO_CLK 0x4
Andrew Lunn865b813a2017-07-30 22:41:47 +020077#define MII_M1111_HWCFG_MODE_RTBI 0x7
Liu Yu-B132015f8cbc12010-01-13 22:13:19 +000078#define MII_M1111_HWCFG_MODE_COPPER_RTBI 0x9
Andrew Lunn865b813a2017-07-30 22:41:47 +020079#define MII_M1111_HWCFG_MODE_COPPER_RGMII 0xb
80#define MII_M1111_HWCFG_FIBER_COPPER_RES BIT(13)
81#define MII_M1111_HWCFG_FIBER_COPPER_AUTO BIT(15)
Alexandr Smirnovbe937f12008-03-19 00:37:24 +030082
Cyril Chemparathyc477d042010-08-02 09:44:53 +000083#define MII_88E1121_PHY_MSCR_REG 21
84#define MII_88E1121_PHY_MSCR_RX_DELAY BIT(5)
85#define MII_88E1121_PHY_MSCR_TX_DELAY BIT(4)
Russell King424ca4c2018-01-02 10:58:48 +000086#define MII_88E1121_PHY_MSCR_DELAY_MASK (BIT(5) | BIT(4))
Cyril Chemparathyc477d042010-08-02 09:44:53 +000087
Andrew Lunn0b046802017-01-20 01:37:49 +010088#define MII_88E1121_MISC_TEST 0x1a
89#define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK 0x1f00
90#define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT 8
91#define MII_88E1510_MISC_TEST_TEMP_IRQ_EN BIT(7)
92#define MII_88E1510_MISC_TEST_TEMP_IRQ BIT(6)
93#define MII_88E1121_MISC_TEST_TEMP_SENSOR_EN BIT(5)
94#define MII_88E1121_MISC_TEST_TEMP_MASK 0x1f
95
96#define MII_88E1510_TEMP_SENSOR 0x1b
97#define MII_88E1510_TEMP_SENSOR_MASK 0xff
98
Cyril Chemparathy337ac9d2010-10-29 13:50:25 -070099#define MII_88E1318S_PHY_MSCR1_REG 16
100#define MII_88E1318S_PHY_MSCR1_PAD_ODD BIT(6)
Cyril Chemparathy3ff1c252010-08-03 19:36:06 -0700101
Michael Stapelberg3871c382013-03-11 13:56:45 +0000102/* Copper Specific Interrupt Enable Register */
Andrew Lunn8cf8b872017-07-30 22:41:44 +0200103#define MII_88E1318S_PHY_CSIER 0x12
Michael Stapelberg3871c382013-03-11 13:56:45 +0000104/* WOL Event Interrupt Enable */
Andrew Lunn8cf8b872017-07-30 22:41:44 +0200105#define MII_88E1318S_PHY_CSIER_WOL_EIE BIT(7)
Michael Stapelberg3871c382013-03-11 13:56:45 +0000106
107/* LED Timer Control Register */
Andrew Lunn8cf8b872017-07-30 22:41:44 +0200108#define MII_88E1318S_PHY_LED_TCR 0x12
109#define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15)
110#define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7)
111#define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW BIT(11)
Michael Stapelberg3871c382013-03-11 13:56:45 +0000112
113/* Magic Packet MAC address registers */
Andrew Lunn8cf8b872017-07-30 22:41:44 +0200114#define MII_88E1318S_PHY_MAGIC_PACKET_WORD2 0x17
115#define MII_88E1318S_PHY_MAGIC_PACKET_WORD1 0x18
116#define MII_88E1318S_PHY_MAGIC_PACKET_WORD0 0x19
Michael Stapelberg3871c382013-03-11 13:56:45 +0000117
Andrew Lunn8cf8b872017-07-30 22:41:44 +0200118#define MII_88E1318S_PHY_WOL_CTRL 0x10
119#define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS BIT(12)
120#define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE BIT(14)
Michael Stapelberg3871c382013-03-11 13:56:45 +0000121
Sergei Poselenov140bc922009-04-07 02:01:41 +0000122#define MII_88E1121_PHY_LED_CTRL 16
Sergei Poselenov140bc922009-04-07 02:01:41 +0000123#define MII_88E1121_PHY_LED_DEF 0x0030
Sergei Poselenov140bc922009-04-07 02:01:41 +0000124
Alexandr Smirnovbe937f12008-03-19 00:37:24 +0300125#define MII_M1011_PHY_STATUS 0x11
126#define MII_M1011_PHY_STATUS_1000 0x8000
127#define MII_M1011_PHY_STATUS_100 0x4000
128#define MII_M1011_PHY_STATUS_SPD_MASK 0xc000
129#define MII_M1011_PHY_STATUS_FULLDUPLEX 0x2000
130#define MII_M1011_PHY_STATUS_RESOLVED 0x0800
131#define MII_M1011_PHY_STATUS_LINK 0x0400
132
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +0200133#define MII_88E3016_PHY_SPEC_CTRL 0x10
134#define MII_88E3016_DISABLE_SCRAMBLER 0x0200
135#define MII_88E3016_AUTO_MDIX_CROSSOVER 0x0030
Andy Fleming76884672007-02-09 18:13:58 -0600136
Stefan Roese930b37e2016-02-18 10:59:07 +0100137#define MII_88E1510_GEN_CTRL_REG_1 0x14
138#define MII_88E1510_GEN_CTRL_REG_1_MODE_MASK 0x7
139#define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII 0x1 /* SGMII to copper */
140#define MII_88E1510_GEN_CTRL_REG_1_RESET 0x8000 /* Soft reset */
141
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +0200142#define LPA_FIBER_1000HALF 0x40
143#define LPA_FIBER_1000FULL 0x20
144
Andrew Lunn8cf8b872017-07-30 22:41:44 +0200145#define LPA_PAUSE_FIBER 0x180
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +0200146#define LPA_PAUSE_ASYM_FIBER 0x100
147
148#define ADVERTISE_FIBER_1000HALF 0x40
149#define ADVERTISE_FIBER_1000FULL 0x20
150
151#define ADVERTISE_PAUSE_FIBER 0x180
152#define ADVERTISE_PAUSE_ASYM_FIBER 0x100
153
154#define REGISTER_LINK_STATUS 0x400
Charles-Antoine Couret2170fef2016-07-19 11:13:11 +0200155#define NB_FIBER_STATS 1
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +0200156
Andy Fleming00db8182005-07-30 19:31:23 -0400157MODULE_DESCRIPTION("Marvell PHY driver");
158MODULE_AUTHOR("Andy Fleming");
159MODULE_LICENSE("GPL");
160
Andrew Lunnd2fa47d2015-12-30 16:28:26 +0100161struct marvell_hw_stat {
162 const char *string;
163 u8 page;
164 u8 reg;
165 u8 bits;
166};
167
168static struct marvell_hw_stat marvell_hw_stats[] = {
Charles-Antoine Couret2170fef2016-07-19 11:13:11 +0200169 { "phy_receive_errors_copper", 0, 21, 16},
Andrew Lunnd2fa47d2015-12-30 16:28:26 +0100170 { "phy_idle_errors", 0, 10, 8 },
Charles-Antoine Couret2170fef2016-07-19 11:13:11 +0200171 { "phy_receive_errors_fiber", 1, 21, 16},
Andrew Lunnd2fa47d2015-12-30 16:28:26 +0100172};
173
174struct marvell_priv {
175 u64 stats[ARRAY_SIZE(marvell_hw_stats)];
Andrew Lunn0b046802017-01-20 01:37:49 +0100176 char *hwmon_name;
177 struct device *hwmon_dev;
Andrew Lunnd2fa47d2015-12-30 16:28:26 +0100178};
179
Russell King424ca4c2018-01-02 10:58:48 +0000180static int marvell_read_page(struct phy_device *phydev)
Andrew Lunn6427bb22017-05-17 03:26:03 +0200181{
Russell King424ca4c2018-01-02 10:58:48 +0000182 return __phy_read(phydev, MII_MARVELL_PHY_PAGE);
183}
184
185static int marvell_write_page(struct phy_device *phydev, int page)
186{
187 return __phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
Andrew Lunn6427bb22017-05-17 03:26:03 +0200188}
189
190static int marvell_set_page(struct phy_device *phydev, int page)
191{
192 return phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
193}
194
Andy Fleming00db8182005-07-30 19:31:23 -0400195static int marvell_ack_interrupt(struct phy_device *phydev)
196{
197 int err;
198
199 /* Clear the interrupts by reading the reg */
200 err = phy_read(phydev, MII_M1011_IEVENT);
201
202 if (err < 0)
203 return err;
204
205 return 0;
206}
207
208static int marvell_config_intr(struct phy_device *phydev)
209{
210 int err;
211
Andy Fleming76884672007-02-09 18:13:58 -0600212 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
Andrew Lunn23beb382017-05-17 03:26:04 +0200213 err = phy_write(phydev, MII_M1011_IMASK,
214 MII_M1011_IMASK_INIT);
Andy Fleming00db8182005-07-30 19:31:23 -0400215 else
Andrew Lunn23beb382017-05-17 03:26:04 +0200216 err = phy_write(phydev, MII_M1011_IMASK,
217 MII_M1011_IMASK_CLEAR);
Andy Fleming00db8182005-07-30 19:31:23 -0400218
219 return err;
220}
221
David Thomson239aa552015-07-10 16:28:25 +1200222static int marvell_set_polarity(struct phy_device *phydev, int polarity)
223{
224 int reg;
225 int err;
226 int val;
227
228 /* get the current settings */
229 reg = phy_read(phydev, MII_M1011_PHY_SCR);
230 if (reg < 0)
231 return reg;
232
233 val = reg;
234 val &= ~MII_M1011_PHY_SCR_AUTO_CROSS;
235 switch (polarity) {
236 case ETH_TP_MDI:
237 val |= MII_M1011_PHY_SCR_MDI;
238 break;
239 case ETH_TP_MDI_X:
240 val |= MII_M1011_PHY_SCR_MDI_X;
241 break;
242 case ETH_TP_MDI_AUTO:
243 case ETH_TP_MDI_INVALID:
244 default:
245 val |= MII_M1011_PHY_SCR_AUTO_CROSS;
246 break;
247 }
248
249 if (val != reg) {
250 /* Set the new polarity value in the register */
251 err = phy_write(phydev, MII_M1011_PHY_SCR, val);
252 if (err)
253 return err;
254 }
255
256 return 0;
257}
258
Andrew Lunn6ef05eb2017-07-30 22:41:50 +0200259static int marvell_set_downshift(struct phy_device *phydev, bool enable,
260 u8 retries)
261{
262 int reg;
263
264 reg = phy_read(phydev, MII_M1011_PHY_SCR);
265 if (reg < 0)
266 return reg;
267
268 reg &= MII_M1011_PHY_SRC_DOWNSHIFT_MASK;
269 reg |= ((retries - 1) << MII_M1011_PHY_SCR_DOWNSHIFT_SHIFT);
270 if (enable)
271 reg |= MII_M1011_PHY_SCR_DOWNSHIFT_EN;
272
273 return phy_write(phydev, MII_M1011_PHY_SCR, reg);
274}
275
Andy Fleming00db8182005-07-30 19:31:23 -0400276static int marvell_config_aneg(struct phy_device *phydev)
277{
278 int err;
279
Raju Lakkaraju4e26c5c2016-11-29 15:16:49 +0530280 err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
Andy Fleming76884672007-02-09 18:13:58 -0600281 if (err < 0)
282 return err;
283
284 err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
285 MII_M1111_PHY_LED_DIRECT);
286 if (err < 0)
287 return err;
Andy Fleming00db8182005-07-30 19:31:23 -0400288
289 err = genphy_config_aneg(phydev);
Anton Vorontsov8ff44982009-09-09 16:01:30 +0000290 if (err < 0)
291 return err;
Andy Fleming00db8182005-07-30 19:31:23 -0400292
Anton Vorontsov8ff44982009-09-09 16:01:30 +0000293 if (phydev->autoneg != AUTONEG_ENABLE) {
Andrew Lunn0c3439b2017-05-17 03:25:59 +0200294 /* A write to speed/duplex bits (that is performed by
Anton Vorontsov8ff44982009-09-09 16:01:30 +0000295 * genphy_config_aneg() call above) must be followed by
296 * a software reset. Otherwise, the write has no effect.
297 */
Andrew Lunn34386342017-07-30 22:41:45 +0200298 err = genphy_soft_reset(phydev);
Anton Vorontsov8ff44982009-09-09 16:01:30 +0000299 if (err < 0)
300 return err;
301 }
302
303 return 0;
Andy Fleming00db8182005-07-30 19:31:23 -0400304}
305
Andrew Lunnf2899782017-05-23 17:49:13 +0200306static int m88e1101_config_aneg(struct phy_device *phydev)
307{
308 int err;
309
310 /* This Marvell PHY has an errata which requires
311 * that certain registers get written in order
312 * to restart autonegotiation
313 */
Andrew Lunn34386342017-07-30 22:41:45 +0200314 err = genphy_soft_reset(phydev);
Andrew Lunnf2899782017-05-23 17:49:13 +0200315 if (err < 0)
316 return err;
317
318 err = phy_write(phydev, 0x1d, 0x1f);
319 if (err < 0)
320 return err;
321
322 err = phy_write(phydev, 0x1e, 0x200c);
323 if (err < 0)
324 return err;
325
326 err = phy_write(phydev, 0x1d, 0x5);
327 if (err < 0)
328 return err;
329
330 err = phy_write(phydev, 0x1e, 0);
331 if (err < 0)
332 return err;
333
334 err = phy_write(phydev, 0x1e, 0x100);
335 if (err < 0)
336 return err;
337
338 return marvell_config_aneg(phydev);
339}
340
Harini Katakam3ec0a0f2016-06-27 13:09:59 +0530341static int m88e1111_config_aneg(struct phy_device *phydev)
342{
343 int err;
344
345 /* The Marvell PHY has an errata which requires
346 * that certain registers get written in order
347 * to restart autonegotiation
348 */
Andrew Lunn34386342017-07-30 22:41:45 +0200349 err = genphy_soft_reset(phydev);
Harini Katakam3ec0a0f2016-06-27 13:09:59 +0530350
Raju Lakkaraju4e26c5c2016-11-29 15:16:49 +0530351 err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
Harini Katakam3ec0a0f2016-06-27 13:09:59 +0530352 if (err < 0)
353 return err;
354
355 err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
356 MII_M1111_PHY_LED_DIRECT);
357 if (err < 0)
358 return err;
359
360 err = genphy_config_aneg(phydev);
361 if (err < 0)
362 return err;
363
364 if (phydev->autoneg != AUTONEG_ENABLE) {
Harini Katakam3ec0a0f2016-06-27 13:09:59 +0530365 /* A write to speed/duplex bits (that is performed by
366 * genphy_config_aneg() call above) must be followed by
367 * a software reset. Otherwise, the write has no effect.
368 */
Andrew Lunn34386342017-07-30 22:41:45 +0200369 err = genphy_soft_reset(phydev);
Harini Katakam3ec0a0f2016-06-27 13:09:59 +0530370 if (err < 0)
371 return err;
372 }
373
374 return 0;
375}
376
David Daneycf41a512010-11-19 12:13:18 +0000377#ifdef CONFIG_OF_MDIO
Andrew Lunn0c3439b2017-05-17 03:25:59 +0200378/* Set and/or override some configuration registers based on the
David Daneycf41a512010-11-19 12:13:18 +0000379 * marvell,reg-init property stored in the of_node for the phydev.
380 *
381 * marvell,reg-init = <reg-page reg mask value>,...;
382 *
383 * There may be one or more sets of <reg-page reg mask value>:
384 *
385 * reg-page: which register bank to use.
386 * reg: the register.
387 * mask: if non-zero, ANDed with existing register value.
388 * value: ORed with the masked value and written to the regiser.
389 *
390 */
391static int marvell_of_reg_init(struct phy_device *phydev)
392{
393 const __be32 *paddr;
Russell King424ca4c2018-01-02 10:58:48 +0000394 int len, i, saved_page, current_page, ret = 0;
David Daneycf41a512010-11-19 12:13:18 +0000395
Andrew Lunne5a03bf2016-01-06 20:11:16 +0100396 if (!phydev->mdio.dev.of_node)
David Daneycf41a512010-11-19 12:13:18 +0000397 return 0;
398
Andrew Lunne5a03bf2016-01-06 20:11:16 +0100399 paddr = of_get_property(phydev->mdio.dev.of_node,
400 "marvell,reg-init", &len);
David Daneycf41a512010-11-19 12:13:18 +0000401 if (!paddr || len < (4 * sizeof(*paddr)))
402 return 0;
403
Russell King424ca4c2018-01-02 10:58:48 +0000404 saved_page = phy_save_page(phydev);
David Daneycf41a512010-11-19 12:13:18 +0000405 if (saved_page < 0)
Russell King424ca4c2018-01-02 10:58:48 +0000406 goto err;
David Daneycf41a512010-11-19 12:13:18 +0000407 current_page = saved_page;
408
David Daneycf41a512010-11-19 12:13:18 +0000409 len /= sizeof(*paddr);
410 for (i = 0; i < len - 3; i += 4) {
Andrew Lunn6427bb22017-05-17 03:26:03 +0200411 u16 page = be32_to_cpup(paddr + i);
David Daneycf41a512010-11-19 12:13:18 +0000412 u16 reg = be32_to_cpup(paddr + i + 1);
413 u16 mask = be32_to_cpup(paddr + i + 2);
414 u16 val_bits = be32_to_cpup(paddr + i + 3);
415 int val;
416
Andrew Lunn6427bb22017-05-17 03:26:03 +0200417 if (page != current_page) {
418 current_page = page;
Russell King424ca4c2018-01-02 10:58:48 +0000419 ret = marvell_write_page(phydev, page);
David Daneycf41a512010-11-19 12:13:18 +0000420 if (ret < 0)
421 goto err;
422 }
423
424 val = 0;
425 if (mask) {
Russell King424ca4c2018-01-02 10:58:48 +0000426 val = __phy_read(phydev, reg);
David Daneycf41a512010-11-19 12:13:18 +0000427 if (val < 0) {
428 ret = val;
429 goto err;
430 }
431 val &= mask;
432 }
433 val |= val_bits;
434
Russell King424ca4c2018-01-02 10:58:48 +0000435 ret = __phy_write(phydev, reg, val);
David Daneycf41a512010-11-19 12:13:18 +0000436 if (ret < 0)
437 goto err;
David Daneycf41a512010-11-19 12:13:18 +0000438 }
439err:
Russell King424ca4c2018-01-02 10:58:48 +0000440 return phy_restore_page(phydev, saved_page, ret);
David Daneycf41a512010-11-19 12:13:18 +0000441}
442#else
443static int marvell_of_reg_init(struct phy_device *phydev)
444{
445 return 0;
446}
447#endif /* CONFIG_OF_MDIO */
448
Andrew Lunn864dc722017-07-30 22:41:48 +0200449static int m88e1121_config_aneg_rgmii_delays(struct phy_device *phydev)
Sergei Poselenov140bc922009-04-07 02:01:41 +0000450{
Russell King424ca4c2018-01-02 10:58:48 +0000451 int mscr;
Andrew Lunn864dc722017-07-30 22:41:48 +0200452
453 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
Russell King424ca4c2018-01-02 10:58:48 +0000454 mscr = MII_88E1121_PHY_MSCR_RX_DELAY |
455 MII_88E1121_PHY_MSCR_TX_DELAY;
Andrew Lunn864dc722017-07-30 22:41:48 +0200456 else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
Russell King424ca4c2018-01-02 10:58:48 +0000457 mscr = MII_88E1121_PHY_MSCR_RX_DELAY;
Andrew Lunn864dc722017-07-30 22:41:48 +0200458 else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
Russell King424ca4c2018-01-02 10:58:48 +0000459 mscr = MII_88E1121_PHY_MSCR_TX_DELAY;
460 else
461 mscr = 0;
Andrew Lunn864dc722017-07-30 22:41:48 +0200462
Russell King424ca4c2018-01-02 10:58:48 +0000463 return phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
464 MII_88E1121_PHY_MSCR_REG,
465 MII_88E1121_PHY_MSCR_DELAY_MASK, mscr);
Andrew Lunn864dc722017-07-30 22:41:48 +0200466}
467
468static int m88e1121_config_aneg(struct phy_device *phydev)
469{
470 int err = 0;
471
472 if (phy_interface_is_rgmii(phydev)) {
473 err = m88e1121_config_aneg_rgmii_delays(phydev);
474 if (err)
475 return err;
476 }
477
Andrew Lunn34386342017-07-30 22:41:45 +0200478 err = genphy_soft_reset(phydev);
Sergei Poselenov140bc922009-04-07 02:01:41 +0000479 if (err < 0)
480 return err;
481
Andrew Lunnfecd5e92017-07-30 22:41:49 +0200482 err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
Sergei Poselenov140bc922009-04-07 02:01:41 +0000483 if (err < 0)
484 return err;
485
Clemens Gruberfdecf362016-06-11 17:21:26 +0200486 return genphy_config_aneg(phydev);
Sergei Poselenov140bc922009-04-07 02:01:41 +0000487}
488
Cyril Chemparathy337ac9d2010-10-29 13:50:25 -0700489static int m88e1318_config_aneg(struct phy_device *phydev)
Cyril Chemparathy3ff1c252010-08-03 19:36:06 -0700490{
Russell King424ca4c2018-01-02 10:58:48 +0000491 int err;
Cyril Chemparathy3ff1c252010-08-03 19:36:06 -0700492
Russell King424ca4c2018-01-02 10:58:48 +0000493 err = phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
494 MII_88E1318S_PHY_MSCR1_REG,
495 0, MII_88E1318S_PHY_MSCR1_PAD_ODD);
Cyril Chemparathy3ff1c252010-08-03 19:36:06 -0700496 if (err < 0)
497 return err;
498
499 return m88e1121_config_aneg(phydev);
500}
501
Charles-Antoine Couret78301eb2016-07-19 11:13:12 +0200502/**
503 * ethtool_adv_to_fiber_adv_t
504 * @ethadv: the ethtool advertisement settings
505 *
506 * A small helper function that translates ethtool advertisement
507 * settings to phy autonegotiation advertisements for the
508 * MII_ADV register for fiber link.
509 */
510static inline u32 ethtool_adv_to_fiber_adv_t(u32 ethadv)
511{
512 u32 result = 0;
513
514 if (ethadv & ADVERTISED_1000baseT_Half)
515 result |= ADVERTISE_FIBER_1000HALF;
516 if (ethadv & ADVERTISED_1000baseT_Full)
517 result |= ADVERTISE_FIBER_1000FULL;
518
519 if ((ethadv & ADVERTISE_PAUSE_ASYM) && (ethadv & ADVERTISE_PAUSE_CAP))
520 result |= LPA_PAUSE_ASYM_FIBER;
521 else if (ethadv & ADVERTISE_PAUSE_CAP)
522 result |= (ADVERTISE_PAUSE_FIBER
523 & (~ADVERTISE_PAUSE_ASYM_FIBER));
524
525 return result;
526}
527
528/**
529 * marvell_config_aneg_fiber - restart auto-negotiation or write BMCR
530 * @phydev: target phy_device struct
531 *
532 * Description: If auto-negotiation is enabled, we configure the
533 * advertising, and then restart auto-negotiation. If it is not
534 * enabled, then we write the BMCR. Adapted for fiber link in
535 * some Marvell's devices.
536 */
537static int marvell_config_aneg_fiber(struct phy_device *phydev)
538{
539 int changed = 0;
540 int err;
541 int adv, oldadv;
542 u32 advertise;
543
544 if (phydev->autoneg != AUTONEG_ENABLE)
545 return genphy_setup_forced(phydev);
546
547 /* Only allow advertising what this PHY supports */
548 phydev->advertising &= phydev->supported;
549 advertise = phydev->advertising;
550
551 /* Setup fiber advertisement */
552 adv = phy_read(phydev, MII_ADVERTISE);
553 if (adv < 0)
554 return adv;
555
556 oldadv = adv;
557 adv &= ~(ADVERTISE_FIBER_1000HALF | ADVERTISE_FIBER_1000FULL
558 | LPA_PAUSE_FIBER);
559 adv |= ethtool_adv_to_fiber_adv_t(advertise);
560
561 if (adv != oldadv) {
562 err = phy_write(phydev, MII_ADVERTISE, adv);
563 if (err < 0)
564 return err;
565
566 changed = 1;
567 }
568
569 if (changed == 0) {
570 /* Advertisement hasn't changed, but maybe aneg was never on to
Andrew Lunn8cf8b872017-07-30 22:41:44 +0200571 * begin with? Or maybe phy was isolated?
Charles-Antoine Couret78301eb2016-07-19 11:13:12 +0200572 */
573 int ctl = phy_read(phydev, MII_BMCR);
574
575 if (ctl < 0)
576 return ctl;
577
578 if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
579 changed = 1; /* do restart aneg */
580 }
581
582 /* Only restart aneg if we are advertising something different
583 * than we were before.
584 */
585 if (changed > 0)
586 changed = genphy_restart_aneg(phydev);
587
588 return changed;
589}
590
Michal Simek10e24caa2013-05-30 20:08:27 +0000591static int m88e1510_config_aneg(struct phy_device *phydev)
592{
593 int err;
594
Andrew Lunn52295662017-05-25 21:42:08 +0200595 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret78301eb2016-07-19 11:13:12 +0200596 if (err < 0)
597 goto error;
598
599 /* Configure the copper link first */
Michal Simek10e24caa2013-05-30 20:08:27 +0000600 err = m88e1318_config_aneg(phydev);
601 if (err < 0)
Charles-Antoine Couret78301eb2016-07-19 11:13:12 +0200602 goto error;
Michal Simek10e24caa2013-05-30 20:08:27 +0000603
Russell Kingde9c4e02017-12-13 09:22:03 +0000604 /* Do not touch the fiber page if we're in copper->sgmii mode */
605 if (phydev->interface == PHY_INTERFACE_MODE_SGMII)
606 return 0;
607
Charles-Antoine Couret78301eb2016-07-19 11:13:12 +0200608 /* Then the fiber link */
Andrew Lunn52295662017-05-25 21:42:08 +0200609 err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
Charles-Antoine Couret78301eb2016-07-19 11:13:12 +0200610 if (err < 0)
611 goto error;
612
613 err = marvell_config_aneg_fiber(phydev);
614 if (err < 0)
615 goto error;
616
Andrew Lunn52295662017-05-25 21:42:08 +0200617 return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret78301eb2016-07-19 11:13:12 +0200618
619error:
Andrew Lunn52295662017-05-25 21:42:08 +0200620 marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret78301eb2016-07-19 11:13:12 +0200621 return err;
Clemens Gruber79be1a12016-02-15 23:46:45 +0100622}
623
624static int marvell_config_init(struct phy_device *phydev)
625{
626 /* Set registers from marvell,reg-init DT property */
Michal Simek10e24caa2013-05-30 20:08:27 +0000627 return marvell_of_reg_init(phydev);
628}
629
Michal Simek3da09a52013-05-30 20:08:26 +0000630static int m88e1116r_config_init(struct phy_device *phydev)
631{
Michal Simek3da09a52013-05-30 20:08:26 +0000632 int err;
633
Andrew Lunn34386342017-07-30 22:41:45 +0200634 err = genphy_soft_reset(phydev);
Michal Simek3da09a52013-05-30 20:08:26 +0000635 if (err < 0)
636 return err;
637
638 mdelay(500);
639
Andrew Lunn52295662017-05-25 21:42:08 +0200640 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Michal Simek3da09a52013-05-30 20:08:26 +0000641 if (err < 0)
642 return err;
643
Andrew Lunnfecd5e92017-07-30 22:41:49 +0200644 err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
645 if (err < 0)
646 return err;
647
Andrew Lunn6ef05eb2017-07-30 22:41:50 +0200648 err = marvell_set_downshift(phydev, true, 8);
Michal Simek3da09a52013-05-30 20:08:26 +0000649 if (err < 0)
650 return err;
651
Andrew Lunn14fc0ab2017-10-31 20:31:28 +0100652 if (phy_interface_is_rgmii(phydev)) {
653 err = m88e1121_config_aneg_rgmii_delays(phydev);
654 if (err < 0)
655 return err;
656 }
Michal Simek3da09a52013-05-30 20:08:26 +0000657
Andrew Lunn34386342017-07-30 22:41:45 +0200658 err = genphy_soft_reset(phydev);
Michal Simek3da09a52013-05-30 20:08:26 +0000659 if (err < 0)
660 return err;
661
Clemens Gruber79be1a12016-02-15 23:46:45 +0100662 return marvell_config_init(phydev);
Michal Simek3da09a52013-05-30 20:08:26 +0000663}
664
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +0200665static int m88e3016_config_init(struct phy_device *phydev)
666{
667 int reg;
668
669 /* Enable Scrambler and Auto-Crossover */
670 reg = phy_read(phydev, MII_88E3016_PHY_SPEC_CTRL);
671 if (reg < 0)
672 return reg;
673
674 reg &= ~MII_88E3016_DISABLE_SCRAMBLER;
675 reg |= MII_88E3016_AUTO_MDIX_CROSSOVER;
676
677 reg = phy_write(phydev, MII_88E3016_PHY_SPEC_CTRL, reg);
678 if (reg < 0)
679 return reg;
680
Clemens Gruber79be1a12016-02-15 23:46:45 +0100681 return marvell_config_init(phydev);
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +0200682}
683
Andrew Lunn865b813a2017-07-30 22:41:47 +0200684static int m88e1111_config_init_hwcfg_mode(struct phy_device *phydev,
685 u16 mode,
686 int fibre_copper_auto)
687{
688 int temp;
689
690 temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
691 if (temp < 0)
692 return temp;
693
694 temp &= ~(MII_M1111_HWCFG_MODE_MASK |
695 MII_M1111_HWCFG_FIBER_COPPER_AUTO |
696 MII_M1111_HWCFG_FIBER_COPPER_RES);
697 temp |= mode;
698
699 if (fibre_copper_auto)
700 temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO;
701
702 return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
703}
704
Andrew Lunn61111592017-07-30 22:41:46 +0200705static int m88e1111_config_init_rgmii_delays(struct phy_device *phydev)
Kim Phillips895ee682007-06-05 18:46:47 +0800706{
Alexandr Smirnovbe937f12008-03-19 00:37:24 +0300707 int temp;
Alexandr Smirnovbe937f12008-03-19 00:37:24 +0300708
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200709 temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
710 if (temp < 0)
711 return temp;
712
713 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
Andrew Lunn61111592017-07-30 22:41:46 +0200714 temp |= (MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY);
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200715 } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
Andrew Lunn61111592017-07-30 22:41:46 +0200716 temp &= ~MII_M1111_RGMII_TX_DELAY;
717 temp |= MII_M1111_RGMII_RX_DELAY;
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200718 } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
Andrew Lunn61111592017-07-30 22:41:46 +0200719 temp &= ~MII_M1111_RGMII_RX_DELAY;
720 temp |= MII_M1111_RGMII_TX_DELAY;
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200721 }
722
Andrew Lunn61111592017-07-30 22:41:46 +0200723 return phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
724}
725
726static int m88e1111_config_init_rgmii(struct phy_device *phydev)
727{
728 int temp;
729 int err;
730
731 err = m88e1111_config_init_rgmii_delays(phydev);
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200732 if (err < 0)
733 return err;
734
735 temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
736 if (temp < 0)
737 return temp;
738
739 temp &= ~(MII_M1111_HWCFG_MODE_MASK);
740
741 if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES)
742 temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII;
743 else
744 temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII;
745
746 return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
747}
748
749static int m88e1111_config_init_sgmii(struct phy_device *phydev)
750{
751 int err;
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200752
Andrew Lunn865b813a2017-07-30 22:41:47 +0200753 err = m88e1111_config_init_hwcfg_mode(
754 phydev,
755 MII_M1111_HWCFG_MODE_SGMII_NO_CLK,
756 MII_M1111_HWCFG_FIBER_COPPER_AUTO);
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200757 if (err < 0)
758 return err;
759
760 /* make sure copper is selected */
Andrew Lunn52295662017-05-25 21:42:08 +0200761 return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200762}
763
764static int m88e1111_config_init_rtbi(struct phy_device *phydev)
765{
Andrew Lunn61111592017-07-30 22:41:46 +0200766 int err;
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200767
Andrew Lunn61111592017-07-30 22:41:46 +0200768 err = m88e1111_config_init_rgmii_delays(phydev);
769 if (err)
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200770 return err;
771
Andrew Lunn865b813a2017-07-30 22:41:47 +0200772 err = m88e1111_config_init_hwcfg_mode(
773 phydev,
774 MII_M1111_HWCFG_MODE_RTBI,
775 MII_M1111_HWCFG_FIBER_COPPER_AUTO);
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200776 if (err < 0)
777 return err;
778
779 /* soft reset */
Andrew Lunn34386342017-07-30 22:41:45 +0200780 err = genphy_soft_reset(phydev);
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200781 if (err < 0)
782 return err;
783
Andrew Lunn865b813a2017-07-30 22:41:47 +0200784 return m88e1111_config_init_hwcfg_mode(
785 phydev,
786 MII_M1111_HWCFG_MODE_RTBI,
787 MII_M1111_HWCFG_FIBER_COPPER_AUTO);
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200788}
789
790static int m88e1111_config_init(struct phy_device *phydev)
791{
792 int err;
793
Florian Fainelli32a64162015-05-26 12:19:59 -0700794 if (phy_interface_is_rgmii(phydev)) {
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200795 err = m88e1111_config_init_rgmii(phydev);
796 if (err)
Kim Phillips895ee682007-06-05 18:46:47 +0800797 return err;
798 }
799
Kapil Juneja4117b5b2007-05-11 18:25:18 -0500800 if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200801 err = m88e1111_config_init_sgmii(phydev);
Madalin Bucur07151bc2015-08-07 17:07:50 +0800802 if (err < 0)
803 return err;
Kapil Juneja4117b5b2007-05-11 18:25:18 -0500804 }
805
Liu Yu-B132015f8cbc12010-01-13 22:13:19 +0000806 if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200807 err = m88e1111_config_init_rtbi(phydev);
Liu Yu-B132015f8cbc12010-01-13 22:13:19 +0000808 if (err < 0)
809 return err;
810 }
811
David Daneycf41a512010-11-19 12:13:18 +0000812 err = marvell_of_reg_init(phydev);
813 if (err < 0)
814 return err;
Liu Yu-B132015f8cbc12010-01-13 22:13:19 +0000815
Andrew Lunn34386342017-07-30 22:41:45 +0200816 return genphy_soft_reset(phydev);
Kim Phillips895ee682007-06-05 18:46:47 +0800817}
818
Clemens Gruberfdecf362016-06-11 17:21:26 +0200819static int m88e1121_config_init(struct phy_device *phydev)
820{
Russell King424ca4c2018-01-02 10:58:48 +0000821 int err;
Clemens Gruberfdecf362016-06-11 17:21:26 +0200822
823 /* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */
Russell King424ca4c2018-01-02 10:58:48 +0000824 err = phy_write_paged(phydev, MII_MARVELL_LED_PAGE,
825 MII_88E1121_PHY_LED_CTRL,
826 MII_88E1121_PHY_LED_DEF);
Clemens Gruberfdecf362016-06-11 17:21:26 +0200827 if (err < 0)
828 return err;
829
Clemens Gruberfdecf362016-06-11 17:21:26 +0200830 /* Set marvell,reg-init configuration from device tree */
831 return marvell_config_init(phydev);
832}
833
Clemens Gruber407353e2016-02-23 20:16:58 +0100834static int m88e1510_config_init(struct phy_device *phydev)
835{
836 int err;
837 int temp;
838
839 /* SGMII-to-Copper mode initialization */
840 if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
Russell King6623c0f2017-12-15 16:10:20 +0000841 u32 pause;
842
Clemens Gruber407353e2016-02-23 20:16:58 +0100843 /* Select page 18 */
Andrew Lunn6427bb22017-05-17 03:26:03 +0200844 err = marvell_set_page(phydev, 18);
Clemens Gruber407353e2016-02-23 20:16:58 +0100845 if (err < 0)
846 return err;
847
848 /* In reg 20, write MODE[2:0] = 0x1 (SGMII to Copper) */
849 temp = phy_read(phydev, MII_88E1510_GEN_CTRL_REG_1);
850 temp &= ~MII_88E1510_GEN_CTRL_REG_1_MODE_MASK;
851 temp |= MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII;
852 err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp);
853 if (err < 0)
854 return err;
855
856 /* PHY reset is necessary after changing MODE[2:0] */
857 temp |= MII_88E1510_GEN_CTRL_REG_1_RESET;
858 err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp);
859 if (err < 0)
860 return err;
861
862 /* Reset page selection */
Andrew Lunn52295662017-05-25 21:42:08 +0200863 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Clemens Gruber407353e2016-02-23 20:16:58 +0100864 if (err < 0)
865 return err;
Russell King6623c0f2017-12-15 16:10:20 +0000866
867 /* There appears to be a bug in the 88e1512 when used in
868 * SGMII to copper mode, where the AN advertisment register
869 * clears the pause bits each time a negotiation occurs.
870 * This means we can never be truely sure what was advertised,
871 * so disable Pause support.
872 */
873 pause = SUPPORTED_Pause | SUPPORTED_Asym_Pause;
874 phydev->supported &= ~pause;
875 phydev->advertising &= ~pause;
Clemens Gruber407353e2016-02-23 20:16:58 +0100876 }
877
Clemens Gruberfdecf362016-06-11 17:21:26 +0200878 return m88e1121_config_init(phydev);
Clemens Gruber407353e2016-02-23 20:16:58 +0100879}
880
Ron Madrid605f1962008-11-06 09:05:26 +0000881static int m88e1118_config_aneg(struct phy_device *phydev)
882{
883 int err;
884
Andrew Lunn34386342017-07-30 22:41:45 +0200885 err = genphy_soft_reset(phydev);
Ron Madrid605f1962008-11-06 09:05:26 +0000886 if (err < 0)
887 return err;
888
Andrew Lunnfecd5e92017-07-30 22:41:49 +0200889 err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
Ron Madrid605f1962008-11-06 09:05:26 +0000890 if (err < 0)
891 return err;
892
893 err = genphy_config_aneg(phydev);
894 return 0;
895}
896
897static int m88e1118_config_init(struct phy_device *phydev)
898{
899 int err;
900
901 /* Change address */
Andrew Lunn52295662017-05-25 21:42:08 +0200902 err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
Ron Madrid605f1962008-11-06 09:05:26 +0000903 if (err < 0)
904 return err;
905
906 /* Enable 1000 Mbit */
907 err = phy_write(phydev, 0x15, 0x1070);
908 if (err < 0)
909 return err;
910
911 /* Change address */
Andrew Lunn52295662017-05-25 21:42:08 +0200912 err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE);
Ron Madrid605f1962008-11-06 09:05:26 +0000913 if (err < 0)
914 return err;
915
916 /* Adjust LED Control */
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +1000917 if (phydev->dev_flags & MARVELL_PHY_M1118_DNS323_LEDS)
918 err = phy_write(phydev, 0x10, 0x1100);
919 else
920 err = phy_write(phydev, 0x10, 0x021e);
Ron Madrid605f1962008-11-06 09:05:26 +0000921 if (err < 0)
922 return err;
923
David Daneycf41a512010-11-19 12:13:18 +0000924 err = marvell_of_reg_init(phydev);
925 if (err < 0)
926 return err;
927
Ron Madrid605f1962008-11-06 09:05:26 +0000928 /* Reset address */
Andrew Lunn52295662017-05-25 21:42:08 +0200929 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Ron Madrid605f1962008-11-06 09:05:26 +0000930 if (err < 0)
931 return err;
932
Andrew Lunn34386342017-07-30 22:41:45 +0200933 return genphy_soft_reset(phydev);
Ron Madrid605f1962008-11-06 09:05:26 +0000934}
935
David Daney90600732010-11-19 11:58:53 +0000936static int m88e1149_config_init(struct phy_device *phydev)
937{
938 int err;
939
940 /* Change address */
Andrew Lunn52295662017-05-25 21:42:08 +0200941 err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
David Daney90600732010-11-19 11:58:53 +0000942 if (err < 0)
943 return err;
944
945 /* Enable 1000 Mbit */
946 err = phy_write(phydev, 0x15, 0x1048);
947 if (err < 0)
948 return err;
949
David Daneycf41a512010-11-19 12:13:18 +0000950 err = marvell_of_reg_init(phydev);
951 if (err < 0)
952 return err;
953
David Daney90600732010-11-19 11:58:53 +0000954 /* Reset address */
Andrew Lunn52295662017-05-25 21:42:08 +0200955 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
David Daney90600732010-11-19 11:58:53 +0000956 if (err < 0)
957 return err;
958
Andrew Lunn34386342017-07-30 22:41:45 +0200959 return genphy_soft_reset(phydev);
David Daney90600732010-11-19 11:58:53 +0000960}
961
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200962static int m88e1145_config_init_rgmii(struct phy_device *phydev)
963{
Andrew Lunn61111592017-07-30 22:41:46 +0200964 int temp;
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200965 int err;
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200966
Andrew Lunn61111592017-07-30 22:41:46 +0200967 err = m88e1111_config_init_rgmii_delays(phydev);
Andrew Lunne1dde8d2017-05-17 03:26:02 +0200968 if (err < 0)
969 return err;
970
971 if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) {
972 err = phy_write(phydev, 0x1d, 0x0012);
973 if (err < 0)
974 return err;
975
976 temp = phy_read(phydev, 0x1e);
977 if (temp < 0)
978 return temp;
979
980 temp &= 0xf03f;
981 temp |= 2 << 9; /* 36 ohm */
982 temp |= 2 << 6; /* 39 ohm */
983
984 err = phy_write(phydev, 0x1e, temp);
985 if (err < 0)
986 return err;
987
988 err = phy_write(phydev, 0x1d, 0x3);
989 if (err < 0)
990 return err;
991
992 err = phy_write(phydev, 0x1e, 0x8000);
993 }
994 return err;
995}
996
997static int m88e1145_config_init_sgmii(struct phy_device *phydev)
998{
Andrew Lunn865b813a2017-07-30 22:41:47 +0200999 return m88e1111_config_init_hwcfg_mode(
1000 phydev, MII_M1111_HWCFG_MODE_SGMII_NO_CLK,
1001 MII_M1111_HWCFG_FIBER_COPPER_AUTO);
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001002}
1003
Andy Fleming76884672007-02-09 18:13:58 -06001004static int m88e1145_config_init(struct phy_device *phydev)
1005{
1006 int err;
1007
1008 /* Take care of errata E0 & E1 */
1009 err = phy_write(phydev, 0x1d, 0x001b);
1010 if (err < 0)
1011 return err;
1012
1013 err = phy_write(phydev, 0x1e, 0x418f);
1014 if (err < 0)
1015 return err;
1016
1017 err = phy_write(phydev, 0x1d, 0x0016);
1018 if (err < 0)
1019 return err;
1020
1021 err = phy_write(phydev, 0x1e, 0xa2da);
1022 if (err < 0)
1023 return err;
1024
Kim Phillips895ee682007-06-05 18:46:47 +08001025 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001026 err = m88e1145_config_init_rgmii(phydev);
Andy Fleming76884672007-02-09 18:13:58 -06001027 if (err < 0)
1028 return err;
Andy Fleming76884672007-02-09 18:13:58 -06001029 }
1030
Viet Nga Daob0224172014-10-23 19:41:53 -07001031 if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001032 err = m88e1145_config_init_sgmii(phydev);
Viet Nga Daob0224172014-10-23 19:41:53 -07001033 if (err < 0)
1034 return err;
1035 }
1036
David Daneycf41a512010-11-19 12:13:18 +00001037 err = marvell_of_reg_init(phydev);
1038 if (err < 0)
1039 return err;
1040
Andy Fleming76884672007-02-09 18:13:58 -06001041 return 0;
1042}
Andy Fleming00db8182005-07-30 19:31:23 -04001043
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001044/**
1045 * fiber_lpa_to_ethtool_lpa_t
1046 * @lpa: value of the MII_LPA register for fiber link
Alexandr Smirnovbe937f12008-03-19 00:37:24 +03001047 *
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001048 * A small helper function that translates MII_LPA
1049 * bits to ethtool LP advertisement settings.
1050 */
1051static u32 fiber_lpa_to_ethtool_lpa_t(u32 lpa)
1052{
1053 u32 result = 0;
1054
1055 if (lpa & LPA_FIBER_1000HALF)
1056 result |= ADVERTISED_1000baseT_Half;
1057 if (lpa & LPA_FIBER_1000FULL)
1058 result |= ADVERTISED_1000baseT_Full;
1059
1060 return result;
1061}
1062
1063/**
1064 * marvell_update_link - update link status in real time in @phydev
1065 * @phydev: target phy_device struct
1066 *
1067 * Description: Update the value in phydev->link to reflect the
1068 * current link value.
1069 */
1070static int marvell_update_link(struct phy_device *phydev, int fiber)
1071{
1072 int status;
1073
1074 /* Use the generic register for copper link, or specific
Andrew Lunn0c3439b2017-05-17 03:25:59 +02001075 * register for fiber case
1076 */
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001077 if (fiber) {
1078 status = phy_read(phydev, MII_M1011_PHY_STATUS);
1079 if (status < 0)
1080 return status;
1081
1082 if ((status & REGISTER_LINK_STATUS) == 0)
1083 phydev->link = 0;
1084 else
1085 phydev->link = 1;
1086 } else {
1087 return genphy_update_link(phydev);
1088 }
1089
1090 return 0;
1091}
1092
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001093static int marvell_read_status_page_an(struct phy_device *phydev,
1094 int fiber)
1095{
1096 int status;
1097 int lpa;
1098 int lpagb;
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001099
1100 status = phy_read(phydev, MII_M1011_PHY_STATUS);
1101 if (status < 0)
1102 return status;
1103
1104 lpa = phy_read(phydev, MII_LPA);
1105 if (lpa < 0)
1106 return lpa;
1107
1108 lpagb = phy_read(phydev, MII_STAT1000);
1109 if (lpagb < 0)
1110 return lpagb;
1111
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001112 if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
1113 phydev->duplex = DUPLEX_FULL;
1114 else
1115 phydev->duplex = DUPLEX_HALF;
1116
1117 status = status & MII_M1011_PHY_STATUS_SPD_MASK;
1118 phydev->pause = 0;
1119 phydev->asym_pause = 0;
1120
1121 switch (status) {
1122 case MII_M1011_PHY_STATUS_1000:
1123 phydev->speed = SPEED_1000;
1124 break;
1125
1126 case MII_M1011_PHY_STATUS_100:
1127 phydev->speed = SPEED_100;
1128 break;
1129
1130 default:
1131 phydev->speed = SPEED_10;
1132 break;
1133 }
1134
1135 if (!fiber) {
1136 phydev->lp_advertising =
1137 mii_stat1000_to_ethtool_lpa_t(lpagb) |
1138 mii_lpa_to_ethtool_lpa_t(lpa);
1139
1140 if (phydev->duplex == DUPLEX_FULL) {
1141 phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
1142 phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
1143 }
1144 } else {
1145 /* The fiber link is only 1000M capable */
1146 phydev->lp_advertising = fiber_lpa_to_ethtool_lpa_t(lpa);
1147
1148 if (phydev->duplex == DUPLEX_FULL) {
1149 if (!(lpa & LPA_PAUSE_FIBER)) {
1150 phydev->pause = 0;
1151 phydev->asym_pause = 0;
1152 } else if ((lpa & LPA_PAUSE_ASYM_FIBER)) {
1153 phydev->pause = 1;
1154 phydev->asym_pause = 1;
1155 } else {
1156 phydev->pause = 1;
1157 phydev->asym_pause = 0;
1158 }
1159 }
1160 }
1161 return 0;
1162}
1163
1164static int marvell_read_status_page_fixed(struct phy_device *phydev)
1165{
1166 int bmcr = phy_read(phydev, MII_BMCR);
1167
1168 if (bmcr < 0)
1169 return bmcr;
1170
1171 if (bmcr & BMCR_FULLDPLX)
1172 phydev->duplex = DUPLEX_FULL;
1173 else
1174 phydev->duplex = DUPLEX_HALF;
1175
1176 if (bmcr & BMCR_SPEED1000)
1177 phydev->speed = SPEED_1000;
1178 else if (bmcr & BMCR_SPEED100)
1179 phydev->speed = SPEED_100;
1180 else
1181 phydev->speed = SPEED_10;
1182
1183 phydev->pause = 0;
1184 phydev->asym_pause = 0;
1185 phydev->lp_advertising = 0;
1186
1187 return 0;
1188}
1189
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001190/* marvell_read_status_page
1191 *
Jeff Garzikf0c88f92008-03-25 23:53:24 -04001192 * Description:
Alexandr Smirnovbe937f12008-03-19 00:37:24 +03001193 * Check the link, then figure out the current state
1194 * by comparing what we advertise with what the link partner
1195 * advertises. Start by checking the gigabit possibilities,
1196 * then move on to 10/100.
1197 */
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001198static int marvell_read_status_page(struct phy_device *phydev, int page)
Alexandr Smirnovbe937f12008-03-19 00:37:24 +03001199{
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001200 int fiber;
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001201 int err;
Alexandr Smirnovbe937f12008-03-19 00:37:24 +03001202
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001203 /* Detect and update the link, but return if there
Andrew Lunn0c3439b2017-05-17 03:25:59 +02001204 * was an error
1205 */
Andrew Lunn52295662017-05-25 21:42:08 +02001206 if (page == MII_MARVELL_FIBER_PAGE)
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001207 fiber = 1;
1208 else
1209 fiber = 0;
1210
1211 err = marvell_update_link(phydev, fiber);
Alexandr Smirnovbe937f12008-03-19 00:37:24 +03001212 if (err)
1213 return err;
1214
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001215 if (phydev->autoneg == AUTONEG_ENABLE)
1216 err = marvell_read_status_page_an(phydev, fiber);
1217 else
1218 err = marvell_read_status_page_fixed(phydev);
Alexandr Smirnovbe937f12008-03-19 00:37:24 +03001219
Andrew Lunne1dde8d2017-05-17 03:26:02 +02001220 return err;
Alexandr Smirnovbe937f12008-03-19 00:37:24 +03001221}
1222
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001223/* marvell_read_status
1224 *
1225 * Some Marvell's phys have two modes: fiber and copper.
1226 * Both need status checked.
1227 * Description:
1228 * First, check the fiber link and status.
1229 * If the fiber link is down, check the copper link and status which
1230 * will be the default value if both link are down.
1231 */
1232static int marvell_read_status(struct phy_device *phydev)
1233{
1234 int err;
1235
1236 /* Check the fiber mode first */
Russell Kinga13c06522017-01-10 23:13:45 +00001237 if (phydev->supported & SUPPORTED_FIBRE &&
1238 phydev->interface != PHY_INTERFACE_MODE_SGMII) {
Andrew Lunn52295662017-05-25 21:42:08 +02001239 err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001240 if (err < 0)
1241 goto error;
1242
Andrew Lunn52295662017-05-25 21:42:08 +02001243 err = marvell_read_status_page(phydev, MII_MARVELL_FIBER_PAGE);
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001244 if (err < 0)
1245 goto error;
1246
Andrew Lunn0c3439b2017-05-17 03:25:59 +02001247 /* If the fiber link is up, it is the selected and
1248 * used link. In this case, we need to stay in the
1249 * fiber page. Please to be careful about that, avoid
1250 * to restore Copper page in other functions which
1251 * could break the behaviour for some fiber phy like
1252 * 88E1512.
1253 */
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001254 if (phydev->link)
1255 return 0;
1256
1257 /* If fiber link is down, check and save copper mode state */
Andrew Lunn52295662017-05-25 21:42:08 +02001258 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001259 if (err < 0)
1260 goto error;
1261 }
1262
Andrew Lunn52295662017-05-25 21:42:08 +02001263 return marvell_read_status_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001264
1265error:
Andrew Lunn52295662017-05-25 21:42:08 +02001266 marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02001267 return err;
1268}
Charles-Antoine Couret3758be32016-07-19 11:13:13 +02001269
1270/* marvell_suspend
1271 *
1272 * Some Marvell's phys have two modes: fiber and copper.
1273 * Both need to be suspended
1274 */
1275static int marvell_suspend(struct phy_device *phydev)
1276{
1277 int err;
1278
1279 /* Suspend the fiber mode first */
1280 if (!(phydev->supported & SUPPORTED_FIBRE)) {
Andrew Lunn52295662017-05-25 21:42:08 +02001281 err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
Charles-Antoine Couret3758be32016-07-19 11:13:13 +02001282 if (err < 0)
1283 goto error;
1284
1285 /* With the page set, use the generic suspend */
1286 err = genphy_suspend(phydev);
1287 if (err < 0)
1288 goto error;
1289
1290 /* Then, the copper link */
Andrew Lunn52295662017-05-25 21:42:08 +02001291 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret3758be32016-07-19 11:13:13 +02001292 if (err < 0)
1293 goto error;
1294 }
1295
1296 /* With the page set, use the generic suspend */
1297 return genphy_suspend(phydev);
1298
1299error:
Andrew Lunn52295662017-05-25 21:42:08 +02001300 marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret3758be32016-07-19 11:13:13 +02001301 return err;
1302}
1303
1304/* marvell_resume
1305 *
1306 * Some Marvell's phys have two modes: fiber and copper.
1307 * Both need to be resumed
1308 */
1309static int marvell_resume(struct phy_device *phydev)
1310{
1311 int err;
1312
1313 /* Resume the fiber mode first */
1314 if (!(phydev->supported & SUPPORTED_FIBRE)) {
Andrew Lunn52295662017-05-25 21:42:08 +02001315 err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
Charles-Antoine Couret3758be32016-07-19 11:13:13 +02001316 if (err < 0)
1317 goto error;
1318
1319 /* With the page set, use the generic resume */
1320 err = genphy_resume(phydev);
1321 if (err < 0)
1322 goto error;
1323
1324 /* Then, the copper link */
Andrew Lunn52295662017-05-25 21:42:08 +02001325 err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret3758be32016-07-19 11:13:13 +02001326 if (err < 0)
1327 goto error;
1328 }
1329
1330 /* With the page set, use the generic resume */
1331 return genphy_resume(phydev);
1332
1333error:
Andrew Lunn52295662017-05-25 21:42:08 +02001334 marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
Charles-Antoine Couret3758be32016-07-19 11:13:13 +02001335 return err;
1336}
1337
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +02001338static int marvell_aneg_done(struct phy_device *phydev)
1339{
1340 int retval = phy_read(phydev, MII_M1011_PHY_STATUS);
Andrew Lunne69d9ed2017-05-17 03:26:00 +02001341
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +02001342 return (retval < 0) ? retval : (retval & MII_M1011_PHY_STATUS_RESOLVED);
1343}
1344
Anatolij Gustschindcd07be2009-04-07 02:01:43 +00001345static int m88e1121_did_interrupt(struct phy_device *phydev)
1346{
1347 int imask;
1348
1349 imask = phy_read(phydev, MII_M1011_IEVENT);
1350
1351 if (imask & MII_M1011_IMASK_INIT)
1352 return 1;
1353
1354 return 0;
1355}
1356
Andrew Lunn23beb382017-05-17 03:26:04 +02001357static void m88e1318_get_wol(struct phy_device *phydev,
1358 struct ethtool_wolinfo *wol)
Michael Stapelberg3871c382013-03-11 13:56:45 +00001359{
Russell King424ca4c2018-01-02 10:58:48 +00001360 int oldpage, ret = 0;
1361
Michael Stapelberg3871c382013-03-11 13:56:45 +00001362 wol->supported = WAKE_MAGIC;
1363 wol->wolopts = 0;
1364
Russell King424ca4c2018-01-02 10:58:48 +00001365 oldpage = phy_select_page(phydev, MII_MARVELL_WOL_PAGE);
1366 if (oldpage < 0)
1367 goto error;
Michael Stapelberg3871c382013-03-11 13:56:45 +00001368
Russell King424ca4c2018-01-02 10:58:48 +00001369 ret = __phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
1370 if (ret & MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
Michael Stapelberg3871c382013-03-11 13:56:45 +00001371 wol->wolopts |= WAKE_MAGIC;
1372
Russell King424ca4c2018-01-02 10:58:48 +00001373error:
1374 phy_restore_page(phydev, oldpage, ret);
Michael Stapelberg3871c382013-03-11 13:56:45 +00001375}
1376
Andrew Lunn23beb382017-05-17 03:26:04 +02001377static int m88e1318_set_wol(struct phy_device *phydev,
1378 struct ethtool_wolinfo *wol)
Michael Stapelberg3871c382013-03-11 13:56:45 +00001379{
Russell King424ca4c2018-01-02 10:58:48 +00001380 int err = 0, oldpage;
Michael Stapelberg3871c382013-03-11 13:56:45 +00001381
Russell King424ca4c2018-01-02 10:58:48 +00001382 oldpage = phy_save_page(phydev);
1383 if (oldpage < 0)
1384 goto error;
Michael Stapelberg3871c382013-03-11 13:56:45 +00001385
1386 if (wol->wolopts & WAKE_MAGIC) {
1387 /* Explicitly switch to page 0x00, just to be sure */
Russell King424ca4c2018-01-02 10:58:48 +00001388 err = marvell_write_page(phydev, MII_MARVELL_COPPER_PAGE);
Michael Stapelberg3871c382013-03-11 13:56:45 +00001389 if (err < 0)
Russell King424ca4c2018-01-02 10:58:48 +00001390 goto error;
Michael Stapelberg3871c382013-03-11 13:56:45 +00001391
1392 /* Enable the WOL interrupt */
Russell King424ca4c2018-01-02 10:58:48 +00001393 err = __phy_modify(phydev, MII_88E1318S_PHY_CSIER, 0,
1394 MII_88E1318S_PHY_CSIER_WOL_EIE);
Michael Stapelberg3871c382013-03-11 13:56:45 +00001395 if (err < 0)
Russell King424ca4c2018-01-02 10:58:48 +00001396 goto error;
Michael Stapelberg3871c382013-03-11 13:56:45 +00001397
Russell King424ca4c2018-01-02 10:58:48 +00001398 err = marvell_write_page(phydev, MII_MARVELL_LED_PAGE);
Michael Stapelberg3871c382013-03-11 13:56:45 +00001399 if (err < 0)
Russell King424ca4c2018-01-02 10:58:48 +00001400 goto error;
Michael Stapelberg3871c382013-03-11 13:56:45 +00001401
1402 /* Setup LED[2] as interrupt pin (active low) */
Russell King424ca4c2018-01-02 10:58:48 +00001403 err = __phy_modify(phydev, MII_88E1318S_PHY_LED_TCR,
1404 (u16)~MII_88E1318S_PHY_LED_TCR_FORCE_INT,
1405 MII_88E1318S_PHY_LED_TCR_INTn_ENABLE |
1406 MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW);
Michael Stapelberg3871c382013-03-11 13:56:45 +00001407 if (err < 0)
Russell King424ca4c2018-01-02 10:58:48 +00001408 goto error;
Michael Stapelberg3871c382013-03-11 13:56:45 +00001409
Russell King424ca4c2018-01-02 10:58:48 +00001410 err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
Michael Stapelberg3871c382013-03-11 13:56:45 +00001411 if (err < 0)
Russell King424ca4c2018-01-02 10:58:48 +00001412 goto error;
Michael Stapelberg3871c382013-03-11 13:56:45 +00001413
1414 /* Store the device address for the magic packet */
Russell King424ca4c2018-01-02 10:58:48 +00001415 err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2,
Michael Stapelberg3871c382013-03-11 13:56:45 +00001416 ((phydev->attached_dev->dev_addr[5] << 8) |
1417 phydev->attached_dev->dev_addr[4]));
1418 if (err < 0)
Russell King424ca4c2018-01-02 10:58:48 +00001419 goto error;
1420 err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1,
Michael Stapelberg3871c382013-03-11 13:56:45 +00001421 ((phydev->attached_dev->dev_addr[3] << 8) |
1422 phydev->attached_dev->dev_addr[2]));
1423 if (err < 0)
Russell King424ca4c2018-01-02 10:58:48 +00001424 goto error;
1425 err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0,
Michael Stapelberg3871c382013-03-11 13:56:45 +00001426 ((phydev->attached_dev->dev_addr[1] << 8) |
1427 phydev->attached_dev->dev_addr[0]));
1428 if (err < 0)
Russell King424ca4c2018-01-02 10:58:48 +00001429 goto error;
Michael Stapelberg3871c382013-03-11 13:56:45 +00001430
1431 /* Clear WOL status and enable magic packet matching */
Russell King424ca4c2018-01-02 10:58:48 +00001432 err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL, 0,
1433 MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS |
1434 MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE);
Michael Stapelberg3871c382013-03-11 13:56:45 +00001435 if (err < 0)
Russell King424ca4c2018-01-02 10:58:48 +00001436 goto error;
Michael Stapelberg3871c382013-03-11 13:56:45 +00001437 } else {
Russell King424ca4c2018-01-02 10:58:48 +00001438 err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
Michael Stapelberg3871c382013-03-11 13:56:45 +00001439 if (err < 0)
Russell King424ca4c2018-01-02 10:58:48 +00001440 goto error;
Michael Stapelberg3871c382013-03-11 13:56:45 +00001441
1442 /* Clear WOL status and disable magic packet matching */
Russell King424ca4c2018-01-02 10:58:48 +00001443 err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL,
1444 (u16)~MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE,
1445 MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS);
Michael Stapelberg3871c382013-03-11 13:56:45 +00001446 if (err < 0)
Russell King424ca4c2018-01-02 10:58:48 +00001447 goto error;
Michael Stapelberg3871c382013-03-11 13:56:45 +00001448 }
1449
Russell King424ca4c2018-01-02 10:58:48 +00001450error:
1451 return phy_restore_page(phydev, oldpage, err);
Michael Stapelberg3871c382013-03-11 13:56:45 +00001452}
1453
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001454static int marvell_get_sset_count(struct phy_device *phydev)
1455{
Charles-Antoine Couret2170fef2016-07-19 11:13:11 +02001456 if (phydev->supported & SUPPORTED_FIBRE)
1457 return ARRAY_SIZE(marvell_hw_stats);
1458 else
1459 return ARRAY_SIZE(marvell_hw_stats) - NB_FIBER_STATS;
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001460}
1461
1462static void marvell_get_strings(struct phy_device *phydev, u8 *data)
1463{
1464 int i;
1465
1466 for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++) {
1467 memcpy(data + i * ETH_GSTRING_LEN,
1468 marvell_hw_stats[i].string, ETH_GSTRING_LEN);
1469 }
1470}
1471
1472#ifndef UINT64_MAX
Andrew Lunn8cf8b872017-07-30 22:41:44 +02001473#define UINT64_MAX (u64)(~((u64)0))
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001474#endif
1475static u64 marvell_get_stat(struct phy_device *phydev, int i)
1476{
1477 struct marvell_hw_stat stat = marvell_hw_stats[i];
1478 struct marvell_priv *priv = phydev->priv;
Russell King424ca4c2018-01-02 10:58:48 +00001479 int val;
Andrew Lunn321b4d42016-02-20 00:35:29 +01001480 u64 ret;
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001481
Russell King424ca4c2018-01-02 10:58:48 +00001482 val = phy_read_paged(phydev, stat.page, stat.reg);
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001483 if (val < 0) {
Andrew Lunn321b4d42016-02-20 00:35:29 +01001484 ret = UINT64_MAX;
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001485 } else {
1486 val = val & ((1 << stat.bits) - 1);
1487 priv->stats[i] += val;
Andrew Lunn321b4d42016-02-20 00:35:29 +01001488 ret = priv->stats[i];
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001489 }
1490
Andrew Lunn321b4d42016-02-20 00:35:29 +01001491 return ret;
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001492}
1493
1494static void marvell_get_stats(struct phy_device *phydev,
1495 struct ethtool_stats *stats, u64 *data)
1496{
1497 int i;
1498
1499 for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++)
1500 data[i] = marvell_get_stat(phydev, i);
1501}
1502
Andrew Lunn0b046802017-01-20 01:37:49 +01001503#ifdef CONFIG_HWMON
1504static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
1505{
Andrew Lunn975b3882017-05-25 21:42:06 +02001506 int oldpage;
Russell King424ca4c2018-01-02 10:58:48 +00001507 int ret = 0;
Andrew Lunn0b046802017-01-20 01:37:49 +01001508 int val;
1509
1510 *temp = 0;
1511
Russell King424ca4c2018-01-02 10:58:48 +00001512 oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
1513 if (oldpage < 0)
1514 goto error;
Andrew Lunn975b3882017-05-25 21:42:06 +02001515
Andrew Lunn0b046802017-01-20 01:37:49 +01001516 /* Enable temperature sensor */
Russell King424ca4c2018-01-02 10:58:48 +00001517 ret = __phy_read(phydev, MII_88E1121_MISC_TEST);
Andrew Lunn0b046802017-01-20 01:37:49 +01001518 if (ret < 0)
1519 goto error;
1520
Russell King424ca4c2018-01-02 10:58:48 +00001521 ret = __phy_write(phydev, MII_88E1121_MISC_TEST,
1522 ret | MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
Andrew Lunn0b046802017-01-20 01:37:49 +01001523 if (ret < 0)
1524 goto error;
1525
1526 /* Wait for temperature to stabilize */
1527 usleep_range(10000, 12000);
1528
Russell King424ca4c2018-01-02 10:58:48 +00001529 val = __phy_read(phydev, MII_88E1121_MISC_TEST);
Andrew Lunn0b046802017-01-20 01:37:49 +01001530 if (val < 0) {
1531 ret = val;
1532 goto error;
1533 }
1534
1535 /* Disable temperature sensor */
Russell King424ca4c2018-01-02 10:58:48 +00001536 ret = __phy_write(phydev, MII_88E1121_MISC_TEST,
1537 ret & ~MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
Andrew Lunn0b046802017-01-20 01:37:49 +01001538 if (ret < 0)
1539 goto error;
1540
1541 *temp = ((val & MII_88E1121_MISC_TEST_TEMP_MASK) - 5) * 5000;
1542
1543error:
Russell King424ca4c2018-01-02 10:58:48 +00001544 return phy_restore_page(phydev, oldpage, ret);
Andrew Lunn0b046802017-01-20 01:37:49 +01001545}
1546
1547static int m88e1121_hwmon_read(struct device *dev,
1548 enum hwmon_sensor_types type,
1549 u32 attr, int channel, long *temp)
1550{
1551 struct phy_device *phydev = dev_get_drvdata(dev);
1552 int err;
1553
1554 switch (attr) {
1555 case hwmon_temp_input:
1556 err = m88e1121_get_temp(phydev, temp);
1557 break;
1558 default:
1559 return -EOPNOTSUPP;
1560 }
1561
1562 return err;
1563}
1564
1565static umode_t m88e1121_hwmon_is_visible(const void *data,
1566 enum hwmon_sensor_types type,
1567 u32 attr, int channel)
1568{
1569 if (type != hwmon_temp)
1570 return 0;
1571
1572 switch (attr) {
1573 case hwmon_temp_input:
1574 return 0444;
1575 default:
1576 return 0;
1577 }
1578}
1579
1580static u32 m88e1121_hwmon_chip_config[] = {
1581 HWMON_C_REGISTER_TZ,
1582 0
1583};
1584
1585static const struct hwmon_channel_info m88e1121_hwmon_chip = {
1586 .type = hwmon_chip,
1587 .config = m88e1121_hwmon_chip_config,
1588};
1589
1590static u32 m88e1121_hwmon_temp_config[] = {
1591 HWMON_T_INPUT,
1592 0
1593};
1594
1595static const struct hwmon_channel_info m88e1121_hwmon_temp = {
1596 .type = hwmon_temp,
1597 .config = m88e1121_hwmon_temp_config,
1598};
1599
1600static const struct hwmon_channel_info *m88e1121_hwmon_info[] = {
1601 &m88e1121_hwmon_chip,
1602 &m88e1121_hwmon_temp,
1603 NULL
1604};
1605
1606static const struct hwmon_ops m88e1121_hwmon_hwmon_ops = {
1607 .is_visible = m88e1121_hwmon_is_visible,
1608 .read = m88e1121_hwmon_read,
1609};
1610
1611static const struct hwmon_chip_info m88e1121_hwmon_chip_info = {
1612 .ops = &m88e1121_hwmon_hwmon_ops,
1613 .info = m88e1121_hwmon_info,
1614};
1615
1616static int m88e1510_get_temp(struct phy_device *phydev, long *temp)
1617{
1618 int ret;
1619
1620 *temp = 0;
1621
Russell King424ca4c2018-01-02 10:58:48 +00001622 ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1623 MII_88E1510_TEMP_SENSOR);
Andrew Lunn0b046802017-01-20 01:37:49 +01001624 if (ret < 0)
Russell King424ca4c2018-01-02 10:58:48 +00001625 return ret;
Andrew Lunn0b046802017-01-20 01:37:49 +01001626
1627 *temp = ((ret & MII_88E1510_TEMP_SENSOR_MASK) - 25) * 1000;
1628
Russell King424ca4c2018-01-02 10:58:48 +00001629 return 0;
Andrew Lunn0b046802017-01-20 01:37:49 +01001630}
1631
Colin Ian Kingf0a45812017-06-02 15:13:34 +01001632static int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp)
Andrew Lunn0b046802017-01-20 01:37:49 +01001633{
1634 int ret;
1635
1636 *temp = 0;
1637
Russell King424ca4c2018-01-02 10:58:48 +00001638 ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1639 MII_88E1121_MISC_TEST);
Andrew Lunn0b046802017-01-20 01:37:49 +01001640 if (ret < 0)
Russell King424ca4c2018-01-02 10:58:48 +00001641 return ret;
Andrew Lunn0b046802017-01-20 01:37:49 +01001642
1643 *temp = (((ret & MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) >>
1644 MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT) * 5) - 25;
1645 /* convert to mC */
1646 *temp *= 1000;
1647
Russell King424ca4c2018-01-02 10:58:48 +00001648 return 0;
Andrew Lunn0b046802017-01-20 01:37:49 +01001649}
1650
Colin Ian Kingf0a45812017-06-02 15:13:34 +01001651static int m88e1510_set_temp_critical(struct phy_device *phydev, long temp)
Andrew Lunn0b046802017-01-20 01:37:49 +01001652{
Andrew Lunn0b046802017-01-20 01:37:49 +01001653 temp = temp / 1000;
1654 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
Andrew Lunn0b046802017-01-20 01:37:49 +01001655
Russell King424ca4c2018-01-02 10:58:48 +00001656 return phy_modify_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1657 MII_88E1121_MISC_TEST,
1658 MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK,
1659 temp << MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT);
Andrew Lunn0b046802017-01-20 01:37:49 +01001660}
1661
Colin Ian Kingf0a45812017-06-02 15:13:34 +01001662static int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm)
Andrew Lunn0b046802017-01-20 01:37:49 +01001663{
1664 int ret;
1665
1666 *alarm = false;
1667
Russell King424ca4c2018-01-02 10:58:48 +00001668 ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
1669 MII_88E1121_MISC_TEST);
Andrew Lunn0b046802017-01-20 01:37:49 +01001670 if (ret < 0)
Russell King424ca4c2018-01-02 10:58:48 +00001671 return ret;
1672
Andrew Lunn0b046802017-01-20 01:37:49 +01001673 *alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ);
1674
Russell King424ca4c2018-01-02 10:58:48 +00001675 return 0;
Andrew Lunn0b046802017-01-20 01:37:49 +01001676}
1677
1678static int m88e1510_hwmon_read(struct device *dev,
1679 enum hwmon_sensor_types type,
1680 u32 attr, int channel, long *temp)
1681{
1682 struct phy_device *phydev = dev_get_drvdata(dev);
1683 int err;
1684
1685 switch (attr) {
1686 case hwmon_temp_input:
1687 err = m88e1510_get_temp(phydev, temp);
1688 break;
1689 case hwmon_temp_crit:
1690 err = m88e1510_get_temp_critical(phydev, temp);
1691 break;
1692 case hwmon_temp_max_alarm:
1693 err = m88e1510_get_temp_alarm(phydev, temp);
1694 break;
1695 default:
1696 return -EOPNOTSUPP;
1697 }
1698
1699 return err;
1700}
1701
1702static int m88e1510_hwmon_write(struct device *dev,
1703 enum hwmon_sensor_types type,
1704 u32 attr, int channel, long temp)
1705{
1706 struct phy_device *phydev = dev_get_drvdata(dev);
1707 int err;
1708
1709 switch (attr) {
1710 case hwmon_temp_crit:
1711 err = m88e1510_set_temp_critical(phydev, temp);
1712 break;
1713 default:
1714 return -EOPNOTSUPP;
1715 }
1716 return err;
1717}
1718
1719static umode_t m88e1510_hwmon_is_visible(const void *data,
1720 enum hwmon_sensor_types type,
1721 u32 attr, int channel)
1722{
1723 if (type != hwmon_temp)
1724 return 0;
1725
1726 switch (attr) {
1727 case hwmon_temp_input:
1728 case hwmon_temp_max_alarm:
1729 return 0444;
1730 case hwmon_temp_crit:
1731 return 0644;
1732 default:
1733 return 0;
1734 }
1735}
1736
1737static u32 m88e1510_hwmon_temp_config[] = {
1738 HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM,
1739 0
1740};
1741
1742static const struct hwmon_channel_info m88e1510_hwmon_temp = {
1743 .type = hwmon_temp,
1744 .config = m88e1510_hwmon_temp_config,
1745};
1746
1747static const struct hwmon_channel_info *m88e1510_hwmon_info[] = {
1748 &m88e1121_hwmon_chip,
1749 &m88e1510_hwmon_temp,
1750 NULL
1751};
1752
1753static const struct hwmon_ops m88e1510_hwmon_hwmon_ops = {
1754 .is_visible = m88e1510_hwmon_is_visible,
1755 .read = m88e1510_hwmon_read,
1756 .write = m88e1510_hwmon_write,
1757};
1758
1759static const struct hwmon_chip_info m88e1510_hwmon_chip_info = {
1760 .ops = &m88e1510_hwmon_hwmon_ops,
1761 .info = m88e1510_hwmon_info,
1762};
1763
1764static int marvell_hwmon_name(struct phy_device *phydev)
1765{
1766 struct marvell_priv *priv = phydev->priv;
1767 struct device *dev = &phydev->mdio.dev;
1768 const char *devname = dev_name(dev);
1769 size_t len = strlen(devname);
1770 int i, j;
1771
1772 priv->hwmon_name = devm_kzalloc(dev, len, GFP_KERNEL);
1773 if (!priv->hwmon_name)
1774 return -ENOMEM;
1775
1776 for (i = j = 0; i < len && devname[i]; i++) {
1777 if (isalnum(devname[i]))
1778 priv->hwmon_name[j++] = devname[i];
1779 }
1780
1781 return 0;
1782}
1783
1784static int marvell_hwmon_probe(struct phy_device *phydev,
1785 const struct hwmon_chip_info *chip)
1786{
1787 struct marvell_priv *priv = phydev->priv;
1788 struct device *dev = &phydev->mdio.dev;
1789 int err;
1790
1791 err = marvell_hwmon_name(phydev);
1792 if (err)
1793 return err;
1794
1795 priv->hwmon_dev = devm_hwmon_device_register_with_info(
1796 dev, priv->hwmon_name, phydev, chip, NULL);
1797
1798 return PTR_ERR_OR_ZERO(priv->hwmon_dev);
1799}
1800
1801static int m88e1121_hwmon_probe(struct phy_device *phydev)
1802{
1803 return marvell_hwmon_probe(phydev, &m88e1121_hwmon_chip_info);
1804}
1805
1806static int m88e1510_hwmon_probe(struct phy_device *phydev)
1807{
1808 return marvell_hwmon_probe(phydev, &m88e1510_hwmon_chip_info);
1809}
1810#else
1811static int m88e1121_hwmon_probe(struct phy_device *phydev)
1812{
1813 return 0;
1814}
1815
1816static int m88e1510_hwmon_probe(struct phy_device *phydev)
1817{
1818 return 0;
1819}
1820#endif
1821
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001822static int marvell_probe(struct phy_device *phydev)
1823{
1824 struct marvell_priv *priv;
1825
Andrew Lunne5a03bf2016-01-06 20:11:16 +01001826 priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001827 if (!priv)
1828 return -ENOMEM;
1829
1830 phydev->priv = priv;
1831
1832 return 0;
1833}
1834
Andrew Lunn0b046802017-01-20 01:37:49 +01001835static int m88e1121_probe(struct phy_device *phydev)
1836{
1837 int err;
1838
1839 err = marvell_probe(phydev);
1840 if (err)
1841 return err;
1842
1843 return m88e1121_hwmon_probe(phydev);
1844}
1845
1846static int m88e1510_probe(struct phy_device *phydev)
1847{
1848 int err;
1849
1850 err = marvell_probe(phydev);
1851 if (err)
1852 return err;
1853
1854 return m88e1510_hwmon_probe(phydev);
1855}
1856
Olof Johanssone5479232007-07-03 16:23:46 -05001857static struct phy_driver marvell_drivers[] = {
1858 {
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +10001859 .phy_id = MARVELL_PHY_ID_88E1101,
1860 .phy_id_mask = MARVELL_PHY_ID_MASK,
Olof Johanssone5479232007-07-03 16:23:46 -05001861 .name = "Marvell 88E1101",
1862 .features = PHY_GBIT_FEATURES,
1863 .flags = PHY_HAS_INTERRUPT,
Arnd Bergmann18702412017-01-23 13:18:41 +01001864 .probe = marvell_probe,
Clemens Gruber79be1a12016-02-15 23:46:45 +01001865 .config_init = &marvell_config_init,
Andrew Lunnf2899782017-05-23 17:49:13 +02001866 .config_aneg = &m88e1101_config_aneg,
Olof Johanssone5479232007-07-03 16:23:46 -05001867 .ack_interrupt = &marvell_ack_interrupt,
1868 .config_intr = &marvell_config_intr,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01001869 .resume = &genphy_resume,
1870 .suspend = &genphy_suspend,
Russell King424ca4c2018-01-02 10:58:48 +00001871 .read_page = marvell_read_page,
1872 .write_page = marvell_write_page,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001873 .get_sset_count = marvell_get_sset_count,
1874 .get_strings = marvell_get_strings,
1875 .get_stats = marvell_get_stats,
Olof Johanssone5479232007-07-03 16:23:46 -05001876 },
1877 {
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +10001878 .phy_id = MARVELL_PHY_ID_88E1112,
1879 .phy_id_mask = MARVELL_PHY_ID_MASK,
Olof Johansson85cfb532007-07-03 16:24:32 -05001880 .name = "Marvell 88E1112",
1881 .features = PHY_GBIT_FEATURES,
1882 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001883 .probe = marvell_probe,
Olof Johansson85cfb532007-07-03 16:24:32 -05001884 .config_init = &m88e1111_config_init,
1885 .config_aneg = &marvell_config_aneg,
Olof Johansson85cfb532007-07-03 16:24:32 -05001886 .ack_interrupt = &marvell_ack_interrupt,
1887 .config_intr = &marvell_config_intr,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01001888 .resume = &genphy_resume,
1889 .suspend = &genphy_suspend,
Russell King424ca4c2018-01-02 10:58:48 +00001890 .read_page = marvell_read_page,
1891 .write_page = marvell_write_page,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001892 .get_sset_count = marvell_get_sset_count,
1893 .get_strings = marvell_get_strings,
1894 .get_stats = marvell_get_stats,
Olof Johansson85cfb532007-07-03 16:24:32 -05001895 },
1896 {
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +10001897 .phy_id = MARVELL_PHY_ID_88E1111,
1898 .phy_id_mask = MARVELL_PHY_ID_MASK,
Olof Johanssone5479232007-07-03 16:23:46 -05001899 .name = "Marvell 88E1111",
1900 .features = PHY_GBIT_FEATURES,
1901 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001902 .probe = marvell_probe,
Olof Johanssone5479232007-07-03 16:23:46 -05001903 .config_init = &m88e1111_config_init,
Harini Katakam3ec0a0f2016-06-27 13:09:59 +05301904 .config_aneg = &m88e1111_config_aneg,
Alexandr Smirnovbe937f12008-03-19 00:37:24 +03001905 .read_status = &marvell_read_status,
Olof Johanssone5479232007-07-03 16:23:46 -05001906 .ack_interrupt = &marvell_ack_interrupt,
1907 .config_intr = &marvell_config_intr,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01001908 .resume = &genphy_resume,
1909 .suspend = &genphy_suspend,
Russell King424ca4c2018-01-02 10:58:48 +00001910 .read_page = marvell_read_page,
1911 .write_page = marvell_write_page,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001912 .get_sset_count = marvell_get_sset_count,
1913 .get_strings = marvell_get_strings,
1914 .get_stats = marvell_get_stats,
Olof Johanssone5479232007-07-03 16:23:46 -05001915 },
1916 {
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +10001917 .phy_id = MARVELL_PHY_ID_88E1118,
1918 .phy_id_mask = MARVELL_PHY_ID_MASK,
Ron Madrid605f1962008-11-06 09:05:26 +00001919 .name = "Marvell 88E1118",
1920 .features = PHY_GBIT_FEATURES,
1921 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001922 .probe = marvell_probe,
Ron Madrid605f1962008-11-06 09:05:26 +00001923 .config_init = &m88e1118_config_init,
1924 .config_aneg = &m88e1118_config_aneg,
Ron Madrid605f1962008-11-06 09:05:26 +00001925 .ack_interrupt = &marvell_ack_interrupt,
1926 .config_intr = &marvell_config_intr,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01001927 .resume = &genphy_resume,
1928 .suspend = &genphy_suspend,
Russell King424ca4c2018-01-02 10:58:48 +00001929 .read_page = marvell_read_page,
1930 .write_page = marvell_write_page,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001931 .get_sset_count = marvell_get_sset_count,
1932 .get_strings = marvell_get_strings,
1933 .get_stats = marvell_get_stats,
Ron Madrid605f1962008-11-06 09:05:26 +00001934 },
1935 {
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +10001936 .phy_id = MARVELL_PHY_ID_88E1121R,
1937 .phy_id_mask = MARVELL_PHY_ID_MASK,
Sergei Poselenov140bc922009-04-07 02:01:41 +00001938 .name = "Marvell 88E1121R",
1939 .features = PHY_GBIT_FEATURES,
1940 .flags = PHY_HAS_INTERRUPT,
Arnd Bergmann18702412017-01-23 13:18:41 +01001941 .probe = &m88e1121_probe,
Clemens Gruberfdecf362016-06-11 17:21:26 +02001942 .config_init = &m88e1121_config_init,
Sergei Poselenov140bc922009-04-07 02:01:41 +00001943 .config_aneg = &m88e1121_config_aneg,
1944 .read_status = &marvell_read_status,
1945 .ack_interrupt = &marvell_ack_interrupt,
1946 .config_intr = &marvell_config_intr,
Anatolij Gustschindcd07be2009-04-07 02:01:43 +00001947 .did_interrupt = &m88e1121_did_interrupt,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01001948 .resume = &genphy_resume,
1949 .suspend = &genphy_suspend,
Russell King424ca4c2018-01-02 10:58:48 +00001950 .read_page = marvell_read_page,
1951 .write_page = marvell_write_page,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001952 .get_sset_count = marvell_get_sset_count,
1953 .get_strings = marvell_get_strings,
1954 .get_stats = marvell_get_stats,
Sergei Poselenov140bc922009-04-07 02:01:41 +00001955 },
1956 {
Cyril Chemparathy337ac9d2010-10-29 13:50:25 -07001957 .phy_id = MARVELL_PHY_ID_88E1318S,
Linus Torvalds6ba74012010-08-04 11:47:58 -07001958 .phy_id_mask = MARVELL_PHY_ID_MASK,
Cyril Chemparathy337ac9d2010-10-29 13:50:25 -07001959 .name = "Marvell 88E1318S",
Cyril Chemparathy3ff1c252010-08-03 19:36:06 -07001960 .features = PHY_GBIT_FEATURES,
1961 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001962 .probe = marvell_probe,
Clemens Gruberfdecf362016-06-11 17:21:26 +02001963 .config_init = &m88e1121_config_init,
Cyril Chemparathy337ac9d2010-10-29 13:50:25 -07001964 .config_aneg = &m88e1318_config_aneg,
Cyril Chemparathy3ff1c252010-08-03 19:36:06 -07001965 .read_status = &marvell_read_status,
1966 .ack_interrupt = &marvell_ack_interrupt,
1967 .config_intr = &marvell_config_intr,
1968 .did_interrupt = &m88e1121_did_interrupt,
Michael Stapelberg3871c382013-03-11 13:56:45 +00001969 .get_wol = &m88e1318_get_wol,
1970 .set_wol = &m88e1318_set_wol,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01001971 .resume = &genphy_resume,
1972 .suspend = &genphy_suspend,
Russell King424ca4c2018-01-02 10:58:48 +00001973 .read_page = marvell_read_page,
1974 .write_page = marvell_write_page,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001975 .get_sset_count = marvell_get_sset_count,
1976 .get_strings = marvell_get_strings,
1977 .get_stats = marvell_get_stats,
Cyril Chemparathy3ff1c252010-08-03 19:36:06 -07001978 },
1979 {
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +10001980 .phy_id = MARVELL_PHY_ID_88E1145,
1981 .phy_id_mask = MARVELL_PHY_ID_MASK,
Olof Johanssone5479232007-07-03 16:23:46 -05001982 .name = "Marvell 88E1145",
1983 .features = PHY_GBIT_FEATURES,
1984 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001985 .probe = marvell_probe,
Olof Johanssone5479232007-07-03 16:23:46 -05001986 .config_init = &m88e1145_config_init,
Zhao Qiangc5058732017-12-18 10:26:43 +08001987 .config_aneg = &m88e1101_config_aneg,
Olof Johanssone5479232007-07-03 16:23:46 -05001988 .read_status = &genphy_read_status,
1989 .ack_interrupt = &marvell_ack_interrupt,
1990 .config_intr = &marvell_config_intr,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01001991 .resume = &genphy_resume,
1992 .suspend = &genphy_suspend,
Russell King424ca4c2018-01-02 10:58:48 +00001993 .read_page = marvell_read_page,
1994 .write_page = marvell_write_page,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01001995 .get_sset_count = marvell_get_sset_count,
1996 .get_strings = marvell_get_strings,
1997 .get_stats = marvell_get_stats,
Olof Johanssonac8c6352007-11-04 16:08:51 -06001998 },
1999 {
David Daney90600732010-11-19 11:58:53 +00002000 .phy_id = MARVELL_PHY_ID_88E1149R,
2001 .phy_id_mask = MARVELL_PHY_ID_MASK,
2002 .name = "Marvell 88E1149R",
2003 .features = PHY_GBIT_FEATURES,
2004 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002005 .probe = marvell_probe,
David Daney90600732010-11-19 11:58:53 +00002006 .config_init = &m88e1149_config_init,
2007 .config_aneg = &m88e1118_config_aneg,
David Daney90600732010-11-19 11:58:53 +00002008 .ack_interrupt = &marvell_ack_interrupt,
2009 .config_intr = &marvell_config_intr,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01002010 .resume = &genphy_resume,
2011 .suspend = &genphy_suspend,
Russell King424ca4c2018-01-02 10:58:48 +00002012 .read_page = marvell_read_page,
2013 .write_page = marvell_write_page,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002014 .get_sset_count = marvell_get_sset_count,
2015 .get_strings = marvell_get_strings,
2016 .get_stats = marvell_get_stats,
David Daney90600732010-11-19 11:58:53 +00002017 },
2018 {
Benjamin Herrenschmidt2f495c32010-06-21 13:20:46 +10002019 .phy_id = MARVELL_PHY_ID_88E1240,
2020 .phy_id_mask = MARVELL_PHY_ID_MASK,
Olof Johanssonac8c6352007-11-04 16:08:51 -06002021 .name = "Marvell 88E1240",
2022 .features = PHY_GBIT_FEATURES,
2023 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002024 .probe = marvell_probe,
Olof Johanssonac8c6352007-11-04 16:08:51 -06002025 .config_init = &m88e1111_config_init,
2026 .config_aneg = &marvell_config_aneg,
Olof Johanssonac8c6352007-11-04 16:08:51 -06002027 .ack_interrupt = &marvell_ack_interrupt,
2028 .config_intr = &marvell_config_intr,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01002029 .resume = &genphy_resume,
2030 .suspend = &genphy_suspend,
Russell King424ca4c2018-01-02 10:58:48 +00002031 .read_page = marvell_read_page,
2032 .write_page = marvell_write_page,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002033 .get_sset_count = marvell_get_sset_count,
2034 .get_strings = marvell_get_strings,
2035 .get_stats = marvell_get_stats,
Olof Johanssonac8c6352007-11-04 16:08:51 -06002036 },
Michal Simek3da09a52013-05-30 20:08:26 +00002037 {
2038 .phy_id = MARVELL_PHY_ID_88E1116R,
2039 .phy_id_mask = MARVELL_PHY_ID_MASK,
2040 .name = "Marvell 88E1116R",
2041 .features = PHY_GBIT_FEATURES,
2042 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002043 .probe = marvell_probe,
Michal Simek3da09a52013-05-30 20:08:26 +00002044 .config_init = &m88e1116r_config_init,
Michal Simek3da09a52013-05-30 20:08:26 +00002045 .ack_interrupt = &marvell_ack_interrupt,
2046 .config_intr = &marvell_config_intr,
Sebastian Hesselbarth0898b442013-12-13 10:20:26 +01002047 .resume = &genphy_resume,
2048 .suspend = &genphy_suspend,
Russell King424ca4c2018-01-02 10:58:48 +00002049 .read_page = marvell_read_page,
2050 .write_page = marvell_write_page,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002051 .get_sset_count = marvell_get_sset_count,
2052 .get_strings = marvell_get_strings,
2053 .get_stats = marvell_get_stats,
Michal Simek3da09a52013-05-30 20:08:26 +00002054 },
Michal Simek10e24caa2013-05-30 20:08:27 +00002055 {
2056 .phy_id = MARVELL_PHY_ID_88E1510,
2057 .phy_id_mask = MARVELL_PHY_ID_MASK,
2058 .name = "Marvell 88E1510",
Charles-Antoine Couret6cfb3bc2016-07-19 11:13:10 +02002059 .features = PHY_GBIT_FEATURES | SUPPORTED_FIBRE,
Arnd Bergmann18702412017-01-23 13:18:41 +01002060 .flags = PHY_HAS_INTERRUPT,
Andrew Lunn0b046802017-01-20 01:37:49 +01002061 .probe = &m88e1510_probe,
Stefan Roese930b37e2016-02-18 10:59:07 +01002062 .config_init = &m88e1510_config_init,
Michal Simek10e24caa2013-05-30 20:08:27 +00002063 .config_aneg = &m88e1510_config_aneg,
2064 .read_status = &marvell_read_status,
2065 .ack_interrupt = &marvell_ack_interrupt,
2066 .config_intr = &marvell_config_intr,
2067 .did_interrupt = &m88e1121_did_interrupt,
Jingju Houf39aac72017-01-22 18:20:56 +08002068 .get_wol = &m88e1318_get_wol,
2069 .set_wol = &m88e1318_set_wol,
Charles-Antoine Couret3758be32016-07-19 11:13:13 +02002070 .resume = &marvell_resume,
2071 .suspend = &marvell_suspend,
Russell King424ca4c2018-01-02 10:58:48 +00002072 .read_page = marvell_read_page,
2073 .write_page = marvell_write_page,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002074 .get_sset_count = marvell_get_sset_count,
2075 .get_strings = marvell_get_strings,
2076 .get_stats = marvell_get_stats,
Lin Yun Shengf0f9b4e2017-06-30 17:44:15 +08002077 .set_loopback = genphy_loopback,
Michal Simek10e24caa2013-05-30 20:08:27 +00002078 },
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +02002079 {
Andrew Lunn819ec8e2015-11-16 23:34:41 +01002080 .phy_id = MARVELL_PHY_ID_88E1540,
2081 .phy_id_mask = MARVELL_PHY_ID_MASK,
2082 .name = "Marvell 88E1540",
2083 .features = PHY_GBIT_FEATURES,
2084 .flags = PHY_HAS_INTERRUPT,
Arnd Bergmann18702412017-01-23 13:18:41 +01002085 .probe = m88e1510_probe,
Clemens Gruber79be1a12016-02-15 23:46:45 +01002086 .config_init = &marvell_config_init,
Andrew Lunn819ec8e2015-11-16 23:34:41 +01002087 .config_aneg = &m88e1510_config_aneg,
2088 .read_status = &marvell_read_status,
2089 .ack_interrupt = &marvell_ack_interrupt,
2090 .config_intr = &marvell_config_intr,
2091 .did_interrupt = &m88e1121_did_interrupt,
2092 .resume = &genphy_resume,
2093 .suspend = &genphy_suspend,
Russell King424ca4c2018-01-02 10:58:48 +00002094 .read_page = marvell_read_page,
2095 .write_page = marvell_write_page,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002096 .get_sset_count = marvell_get_sset_count,
2097 .get_strings = marvell_get_strings,
2098 .get_stats = marvell_get_stats,
Andrew Lunn819ec8e2015-11-16 23:34:41 +01002099 },
2100 {
Andrew Lunn60f06fd2017-02-02 00:35:03 +01002101 .phy_id = MARVELL_PHY_ID_88E1545,
2102 .phy_id_mask = MARVELL_PHY_ID_MASK,
2103 .name = "Marvell 88E1545",
2104 .probe = m88e1510_probe,
Andrew Lunn60f06fd2017-02-02 00:35:03 +01002105 .features = PHY_GBIT_FEATURES,
2106 .flags = PHY_HAS_INTERRUPT,
2107 .config_init = &marvell_config_init,
2108 .config_aneg = &m88e1510_config_aneg,
2109 .read_status = &marvell_read_status,
2110 .ack_interrupt = &marvell_ack_interrupt,
2111 .config_intr = &marvell_config_intr,
2112 .did_interrupt = &m88e1121_did_interrupt,
2113 .resume = &genphy_resume,
2114 .suspend = &genphy_suspend,
Russell King424ca4c2018-01-02 10:58:48 +00002115 .read_page = marvell_read_page,
2116 .write_page = marvell_write_page,
Andrew Lunn60f06fd2017-02-02 00:35:03 +01002117 .get_sset_count = marvell_get_sset_count,
2118 .get_strings = marvell_get_strings,
2119 .get_stats = marvell_get_stats,
2120 },
2121 {
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +02002122 .phy_id = MARVELL_PHY_ID_88E3016,
2123 .phy_id_mask = MARVELL_PHY_ID_MASK,
2124 .name = "Marvell 88E3016",
2125 .features = PHY_BASIC_FEATURES,
2126 .flags = PHY_HAS_INTERRUPT,
Andrew Lunnd2fa47d2015-12-30 16:28:26 +01002127 .probe = marvell_probe,
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +02002128 .config_init = &m88e3016_config_init,
2129 .aneg_done = &marvell_aneg_done,
2130 .read_status = &marvell_read_status,
2131 .ack_interrupt = &marvell_ack_interrupt,
2132 .config_intr = &marvell_config_intr,
2133 .did_interrupt = &m88e1121_did_interrupt,
2134 .resume = &genphy_resume,
2135 .suspend = &genphy_suspend,
Russell King424ca4c2018-01-02 10:58:48 +00002136 .read_page = marvell_read_page,
2137 .write_page = marvell_write_page,
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,
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +02002141 },
Andrew Lunne4cf8a32017-02-01 03:40:06 +01002142 {
2143 .phy_id = MARVELL_PHY_ID_88E6390,
2144 .phy_id_mask = MARVELL_PHY_ID_MASK,
2145 .name = "Marvell 88E6390",
2146 .features = PHY_GBIT_FEATURES,
2147 .flags = PHY_HAS_INTERRUPT,
2148 .probe = m88e1510_probe,
2149 .config_init = &marvell_config_init,
2150 .config_aneg = &m88e1510_config_aneg,
2151 .read_status = &marvell_read_status,
2152 .ack_interrupt = &marvell_ack_interrupt,
2153 .config_intr = &marvell_config_intr,
2154 .did_interrupt = &m88e1121_did_interrupt,
2155 .resume = &genphy_resume,
2156 .suspend = &genphy_suspend,
Russell King424ca4c2018-01-02 10:58:48 +00002157 .read_page = marvell_read_page,
2158 .write_page = marvell_write_page,
Andrew Lunne4cf8a32017-02-01 03:40:06 +01002159 .get_sset_count = marvell_get_sset_count,
2160 .get_strings = marvell_get_strings,
2161 .get_stats = marvell_get_stats,
2162 },
Andy Fleming00db8182005-07-30 19:31:23 -04002163};
2164
Johan Hovold50fd7152014-11-11 19:45:59 +01002165module_phy_driver(marvell_drivers);
David Woodhouse4e4f10f2010-04-02 01:05:56 +00002166
Uwe Kleine-Königcf93c942010-10-03 23:43:32 +00002167static struct mdio_device_id __maybe_unused marvell_tbl[] = {
Michal Simekf5e1cab2013-05-30 20:08:25 +00002168 { MARVELL_PHY_ID_88E1101, MARVELL_PHY_ID_MASK },
2169 { MARVELL_PHY_ID_88E1112, MARVELL_PHY_ID_MASK },
2170 { MARVELL_PHY_ID_88E1111, MARVELL_PHY_ID_MASK },
2171 { MARVELL_PHY_ID_88E1118, MARVELL_PHY_ID_MASK },
2172 { MARVELL_PHY_ID_88E1121R, MARVELL_PHY_ID_MASK },
2173 { MARVELL_PHY_ID_88E1145, MARVELL_PHY_ID_MASK },
2174 { MARVELL_PHY_ID_88E1149R, MARVELL_PHY_ID_MASK },
2175 { MARVELL_PHY_ID_88E1240, MARVELL_PHY_ID_MASK },
2176 { MARVELL_PHY_ID_88E1318S, MARVELL_PHY_ID_MASK },
Michal Simek3da09a52013-05-30 20:08:26 +00002177 { MARVELL_PHY_ID_88E1116R, MARVELL_PHY_ID_MASK },
Michal Simek10e24caa2013-05-30 20:08:27 +00002178 { MARVELL_PHY_ID_88E1510, MARVELL_PHY_ID_MASK },
Andrew Lunn819ec8e2015-11-16 23:34:41 +01002179 { MARVELL_PHY_ID_88E1540, MARVELL_PHY_ID_MASK },
Andrew Lunn60f06fd2017-02-02 00:35:03 +01002180 { MARVELL_PHY_ID_88E1545, MARVELL_PHY_ID_MASK },
Sebastian Hesselbarth6b358ae2014-10-22 20:26:44 +02002181 { MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK },
Andrew Lunne4cf8a32017-02-01 03:40:06 +01002182 { MARVELL_PHY_ID_88E6390, MARVELL_PHY_ID_MASK },
David Woodhouse4e4f10f2010-04-02 01:05:56 +00002183 { }
2184};
2185
2186MODULE_DEVICE_TABLE(mdio, marvell_tbl);