1 // SPDX-License-Identifier: GPL-2.0
2 /* $Date: 2005/10/24 23:18:13 $ $RCSfile: mv88e1xxx.c,v $ $Revision: 1.49 $ */
8 /* MV88E1XXX MDI crossover register values */
9 #define CROSSOVER_MDI 0
10 #define CROSSOVER_MDIX 1
11 #define CROSSOVER_AUTO 3
13 #define INTR_ENABLE_MASK 0x6CA0
16 * Set the bits given by 'bitval' in PHY register 'reg'.
18 static void mdio_set_bit(struct cphy
*cphy
, int reg
, u32 bitval
)
22 (void) simple_mdio_read(cphy
, reg
, &val
);
23 (void) simple_mdio_write(cphy
, reg
, val
| bitval
);
27 * Clear the bits given by 'bitval' in PHY register 'reg'.
29 static void mdio_clear_bit(struct cphy
*cphy
, int reg
, u32 bitval
)
33 (void) simple_mdio_read(cphy
, reg
, &val
);
34 (void) simple_mdio_write(cphy
, reg
, val
& ~bitval
);
40 * DESC: Reset the given PHY's port. NOTE: This is not a global
43 * PARAMS: cphy - Pointer to PHY instance data.
45 * RETURN: 0 - Successful reset.
48 static int mv88e1xxx_reset(struct cphy
*cphy
, int wait
)
53 mdio_set_bit(cphy
, MII_BMCR
, BMCR_RESET
);
56 (void) simple_mdio_read(cphy
, MII_BMCR
, &ctl
);
60 } while (ctl
&& --time_out
);
65 static int mv88e1xxx_interrupt_enable(struct cphy
*cphy
)
67 /* Enable PHY interrupts. */
68 (void) simple_mdio_write(cphy
, MV88E1XXX_INTERRUPT_ENABLE_REGISTER
,
71 /* Enable Marvell interrupts through Elmer0. */
72 if (t1_is_asic(cphy
->adapter
)) {
75 t1_tpi_read(cphy
->adapter
, A_ELMER0_INT_ENABLE
, &elmer
);
76 elmer
|= ELMER0_GP_BIT1
;
77 if (is_T2(cphy
->adapter
))
78 elmer
|= ELMER0_GP_BIT2
| ELMER0_GP_BIT3
| ELMER0_GP_BIT4
;
79 t1_tpi_write(cphy
->adapter
, A_ELMER0_INT_ENABLE
, elmer
);
84 static int mv88e1xxx_interrupt_disable(struct cphy
*cphy
)
86 /* Disable all phy interrupts. */
87 (void) simple_mdio_write(cphy
, MV88E1XXX_INTERRUPT_ENABLE_REGISTER
, 0);
89 /* Disable Marvell interrupts through Elmer0. */
90 if (t1_is_asic(cphy
->adapter
)) {
93 t1_tpi_read(cphy
->adapter
, A_ELMER0_INT_ENABLE
, &elmer
);
94 elmer
&= ~ELMER0_GP_BIT1
;
95 if (is_T2(cphy
->adapter
))
96 elmer
&= ~(ELMER0_GP_BIT2
|ELMER0_GP_BIT3
|ELMER0_GP_BIT4
);
97 t1_tpi_write(cphy
->adapter
, A_ELMER0_INT_ENABLE
, elmer
);
102 static int mv88e1xxx_interrupt_clear(struct cphy
*cphy
)
106 /* Clear PHY interrupts by reading the register. */
107 (void) simple_mdio_read(cphy
,
108 MV88E1XXX_INTERRUPT_STATUS_REGISTER
, &elmer
);
110 /* Clear Marvell interrupts through Elmer0. */
111 if (t1_is_asic(cphy
->adapter
)) {
112 t1_tpi_read(cphy
->adapter
, A_ELMER0_INT_CAUSE
, &elmer
);
113 elmer
|= ELMER0_GP_BIT1
;
114 if (is_T2(cphy
->adapter
))
115 elmer
|= ELMER0_GP_BIT2
|ELMER0_GP_BIT3
|ELMER0_GP_BIT4
;
116 t1_tpi_write(cphy
->adapter
, A_ELMER0_INT_CAUSE
, elmer
);
122 * Set the PHY speed and duplex. This also disables auto-negotiation, except
123 * for 1Gb/s, where auto-negotiation is mandatory.
125 static int mv88e1xxx_set_speed_duplex(struct cphy
*phy
, int speed
, int duplex
)
129 (void) simple_mdio_read(phy
, MII_BMCR
, &ctl
);
131 ctl
&= ~(BMCR_SPEED100
| BMCR_SPEED1000
| BMCR_ANENABLE
);
132 if (speed
== SPEED_100
)
133 ctl
|= BMCR_SPEED100
;
134 else if (speed
== SPEED_1000
)
135 ctl
|= BMCR_SPEED1000
;
138 ctl
&= ~(BMCR_FULLDPLX
| BMCR_ANENABLE
);
139 if (duplex
== DUPLEX_FULL
)
140 ctl
|= BMCR_FULLDPLX
;
142 if (ctl
& BMCR_SPEED1000
) /* auto-negotiation required for 1Gb/s */
143 ctl
|= BMCR_ANENABLE
;
144 (void) simple_mdio_write(phy
, MII_BMCR
, ctl
);
148 static int mv88e1xxx_crossover_set(struct cphy
*cphy
, int crossover
)
152 (void) simple_mdio_read(cphy
,
153 MV88E1XXX_SPECIFIC_CNTRL_REGISTER
, &data32
);
154 data32
&= ~V_PSCR_MDI_XOVER_MODE(M_PSCR_MDI_XOVER_MODE
);
155 data32
|= V_PSCR_MDI_XOVER_MODE(crossover
);
156 (void) simple_mdio_write(cphy
,
157 MV88E1XXX_SPECIFIC_CNTRL_REGISTER
, data32
);
161 static int mv88e1xxx_autoneg_enable(struct cphy
*cphy
)
165 (void) mv88e1xxx_crossover_set(cphy
, CROSSOVER_AUTO
);
167 (void) simple_mdio_read(cphy
, MII_BMCR
, &ctl
);
168 /* restart autoneg for change to take effect */
169 ctl
|= BMCR_ANENABLE
| BMCR_ANRESTART
;
170 (void) simple_mdio_write(cphy
, MII_BMCR
, ctl
);
174 static int mv88e1xxx_autoneg_disable(struct cphy
*cphy
)
179 * Crossover *must* be set to manual in order to disable auto-neg.
180 * The Alaska FAQs document highlights this point.
182 (void) mv88e1xxx_crossover_set(cphy
, CROSSOVER_MDI
);
185 * Must include autoneg reset when disabling auto-neg. This
186 * is described in the Alaska FAQ document.
188 (void) simple_mdio_read(cphy
, MII_BMCR
, &ctl
);
189 ctl
&= ~BMCR_ANENABLE
;
190 (void) simple_mdio_write(cphy
, MII_BMCR
, ctl
| BMCR_ANRESTART
);
194 static int mv88e1xxx_autoneg_restart(struct cphy
*cphy
)
196 mdio_set_bit(cphy
, MII_BMCR
, BMCR_ANRESTART
);
200 static int mv88e1xxx_advertise(struct cphy
*phy
, unsigned int advertise_map
)
205 (ADVERTISED_1000baseT_Half
| ADVERTISED_1000baseT_Full
)) {
206 (void) simple_mdio_read(phy
, MII_GBCR
, &val
);
207 val
&= ~(GBCR_ADV_1000HALF
| GBCR_ADV_1000FULL
);
208 if (advertise_map
& ADVERTISED_1000baseT_Half
)
209 val
|= GBCR_ADV_1000HALF
;
210 if (advertise_map
& ADVERTISED_1000baseT_Full
)
211 val
|= GBCR_ADV_1000FULL
;
213 (void) simple_mdio_write(phy
, MII_GBCR
, val
);
216 if (advertise_map
& ADVERTISED_10baseT_Half
)
217 val
|= ADVERTISE_10HALF
;
218 if (advertise_map
& ADVERTISED_10baseT_Full
)
219 val
|= ADVERTISE_10FULL
;
220 if (advertise_map
& ADVERTISED_100baseT_Half
)
221 val
|= ADVERTISE_100HALF
;
222 if (advertise_map
& ADVERTISED_100baseT_Full
)
223 val
|= ADVERTISE_100FULL
;
224 if (advertise_map
& ADVERTISED_PAUSE
)
225 val
|= ADVERTISE_PAUSE
;
226 if (advertise_map
& ADVERTISED_ASYM_PAUSE
)
227 val
|= ADVERTISE_PAUSE_ASYM
;
228 (void) simple_mdio_write(phy
, MII_ADVERTISE
, val
);
232 static int mv88e1xxx_set_loopback(struct cphy
*cphy
, int on
)
235 mdio_set_bit(cphy
, MII_BMCR
, BMCR_LOOPBACK
);
237 mdio_clear_bit(cphy
, MII_BMCR
, BMCR_LOOPBACK
);
241 static int mv88e1xxx_get_link_status(struct cphy
*cphy
, int *link_ok
,
242 int *speed
, int *duplex
, int *fc
)
245 int sp
= -1, dplx
= -1, pause
= 0;
247 (void) simple_mdio_read(cphy
,
248 MV88E1XXX_SPECIFIC_STATUS_REGISTER
, &status
);
249 if ((status
& V_PSSR_STATUS_RESOLVED
) != 0) {
250 if (status
& V_PSSR_RX_PAUSE
)
252 if (status
& V_PSSR_TX_PAUSE
)
254 dplx
= (status
& V_PSSR_DUPLEX
) ? DUPLEX_FULL
: DUPLEX_HALF
;
255 sp
= G_PSSR_SPEED(status
);
264 *link_ok
= (status
& V_PSSR_LINK
) != 0;
274 static int mv88e1xxx_downshift_set(struct cphy
*cphy
, int downshift_enable
)
278 (void) simple_mdio_read(cphy
,
279 MV88E1XXX_EXT_PHY_SPECIFIC_CNTRL_REGISTER
, &val
);
282 * Set the downshift counter to 2 so we try to establish Gb link
283 * twice before downshifting.
285 val
&= ~(V_DOWNSHIFT_ENABLE
| V_DOWNSHIFT_CNT(M_DOWNSHIFT_CNT
));
287 if (downshift_enable
)
288 val
|= V_DOWNSHIFT_ENABLE
| V_DOWNSHIFT_CNT(2);
289 (void) simple_mdio_write(cphy
,
290 MV88E1XXX_EXT_PHY_SPECIFIC_CNTRL_REGISTER
, val
);
294 static int mv88e1xxx_interrupt_handler(struct cphy
*cphy
)
300 * Loop until cause reads zero. Need to handle bouncing interrupts.
305 (void) simple_mdio_read(cphy
,
306 MV88E1XXX_INTERRUPT_STATUS_REGISTER
,
308 cause
&= INTR_ENABLE_MASK
;
312 if (cause
& MV88E1XXX_INTR_LINK_CHNG
) {
313 (void) simple_mdio_read(cphy
,
314 MV88E1XXX_SPECIFIC_STATUS_REGISTER
, &status
);
316 if (status
& MV88E1XXX_INTR_LINK_CHNG
)
317 cphy
->state
|= PHY_LINK_UP
;
319 cphy
->state
&= ~PHY_LINK_UP
;
320 if (cphy
->state
& PHY_AUTONEG_EN
)
321 cphy
->state
&= ~PHY_AUTONEG_RDY
;
322 cphy_cause
|= cphy_cause_link_change
;
326 if (cause
& MV88E1XXX_INTR_AUTONEG_DONE
)
327 cphy
->state
|= PHY_AUTONEG_RDY
;
329 if ((cphy
->state
& (PHY_LINK_UP
| PHY_AUTONEG_RDY
)) ==
330 (PHY_LINK_UP
| PHY_AUTONEG_RDY
))
331 cphy_cause
|= cphy_cause_link_change
;
336 static void mv88e1xxx_destroy(struct cphy
*cphy
)
341 static const struct cphy_ops mv88e1xxx_ops
= {
342 .destroy
= mv88e1xxx_destroy
,
343 .reset
= mv88e1xxx_reset
,
344 .interrupt_enable
= mv88e1xxx_interrupt_enable
,
345 .interrupt_disable
= mv88e1xxx_interrupt_disable
,
346 .interrupt_clear
= mv88e1xxx_interrupt_clear
,
347 .interrupt_handler
= mv88e1xxx_interrupt_handler
,
348 .autoneg_enable
= mv88e1xxx_autoneg_enable
,
349 .autoneg_disable
= mv88e1xxx_autoneg_disable
,
350 .autoneg_restart
= mv88e1xxx_autoneg_restart
,
351 .advertise
= mv88e1xxx_advertise
,
352 .set_loopback
= mv88e1xxx_set_loopback
,
353 .set_speed_duplex
= mv88e1xxx_set_speed_duplex
,
354 .get_link_status
= mv88e1xxx_get_link_status
,
357 static struct cphy
*mv88e1xxx_phy_create(struct net_device
*dev
, int phy_addr
,
358 const struct mdio_ops
*mdio_ops
)
360 struct adapter
*adapter
= netdev_priv(dev
);
361 struct cphy
*cphy
= kzalloc(sizeof(*cphy
), GFP_KERNEL
);
366 cphy_init(cphy
, dev
, phy_addr
, &mv88e1xxx_ops
, mdio_ops
);
368 /* Configure particular PHY's to run in a different mode. */
369 if ((board_info(adapter
)->caps
& SUPPORTED_TP
) &&
370 board_info(adapter
)->chip_phy
== CHBT_PHY_88E1111
) {
372 * Configure the PHY transmitter as class A to reduce EMI.
374 (void) simple_mdio_write(cphy
,
375 MV88E1XXX_EXTENDED_ADDR_REGISTER
, 0xB);
376 (void) simple_mdio_write(cphy
,
377 MV88E1XXX_EXTENDED_REGISTER
, 0x8004);
379 (void) mv88e1xxx_downshift_set(cphy
, 1); /* Enable downshift */
382 if (is_T2(adapter
)) {
383 (void) simple_mdio_write(cphy
,
384 MV88E1XXX_LED_CONTROL_REGISTER
, 0x1);
390 static int mv88e1xxx_phy_reset(adapter_t
* adapter
)
395 const struct gphy t1_mv88e1xxx_ops
= {
396 .create
= mv88e1xxx_phy_create
,
397 .reset
= mv88e1xxx_phy_reset