1 /* $Date: 2005/10/24 23:18:13 $ $RCSfile: mv88e1xxx.c,v $ $Revision: 1.49 $ */
7 /* MV88E1XXX MDI crossover register values */
8 #define CROSSOVER_MDI 0
9 #define CROSSOVER_MDIX 1
10 #define CROSSOVER_AUTO 3
12 #define INTR_ENABLE_MASK 0x6CA0
15 * Set the bits given by 'bitval' in PHY register 'reg'.
17 static void mdio_set_bit(struct cphy
*cphy
, int reg
, u32 bitval
)
21 (void) simple_mdio_read(cphy
, reg
, &val
);
22 (void) simple_mdio_write(cphy
, reg
, val
| bitval
);
26 * Clear the bits given by 'bitval' in PHY register 'reg'.
28 static void mdio_clear_bit(struct cphy
*cphy
, int reg
, u32 bitval
)
32 (void) simple_mdio_read(cphy
, reg
, &val
);
33 (void) simple_mdio_write(cphy
, reg
, val
& ~bitval
);
39 * DESC: Reset the given PHY's port. NOTE: This is not a global
42 * PARAMS: cphy - Pointer to PHY instance data.
44 * RETURN: 0 - Successfull reset.
47 static int mv88e1xxx_reset(struct cphy
*cphy
, int wait
)
52 mdio_set_bit(cphy
, MII_BMCR
, BMCR_RESET
);
55 (void) simple_mdio_read(cphy
, MII_BMCR
, &ctl
);
59 } while (ctl
&& --time_out
);
64 static int mv88e1xxx_interrupt_enable(struct cphy
*cphy
)
66 /* Enable PHY interrupts. */
67 (void) simple_mdio_write(cphy
, MV88E1XXX_INTERRUPT_ENABLE_REGISTER
,
70 /* Enable Marvell interrupts through Elmer0. */
71 if (t1_is_asic(cphy
->adapter
)) {
74 t1_tpi_read(cphy
->adapter
, A_ELMER0_INT_ENABLE
, &elmer
);
75 elmer
|= ELMER0_GP_BIT1
;
76 if (is_T2(cphy
->adapter
))
77 elmer
|= ELMER0_GP_BIT2
| ELMER0_GP_BIT3
| ELMER0_GP_BIT4
;
78 t1_tpi_write(cphy
->adapter
, A_ELMER0_INT_ENABLE
, elmer
);
83 static int mv88e1xxx_interrupt_disable(struct cphy
*cphy
)
85 /* Disable all phy interrupts. */
86 (void) simple_mdio_write(cphy
, MV88E1XXX_INTERRUPT_ENABLE_REGISTER
, 0);
88 /* Disable Marvell interrupts through Elmer0. */
89 if (t1_is_asic(cphy
->adapter
)) {
92 t1_tpi_read(cphy
->adapter
, A_ELMER0_INT_ENABLE
, &elmer
);
93 elmer
&= ~ELMER0_GP_BIT1
;
94 if (is_T2(cphy
->adapter
))
95 elmer
&= ~(ELMER0_GP_BIT2
|ELMER0_GP_BIT3
|ELMER0_GP_BIT4
);
96 t1_tpi_write(cphy
->adapter
, A_ELMER0_INT_ENABLE
, elmer
);
101 static int mv88e1xxx_interrupt_clear(struct cphy
*cphy
)
105 /* Clear PHY interrupts by reading the register. */
106 (void) simple_mdio_read(cphy
,
107 MV88E1XXX_INTERRUPT_STATUS_REGISTER
, &elmer
);
109 /* Clear Marvell interrupts through Elmer0. */
110 if (t1_is_asic(cphy
->adapter
)) {
111 t1_tpi_read(cphy
->adapter
, A_ELMER0_INT_CAUSE
, &elmer
);
112 elmer
|= ELMER0_GP_BIT1
;
113 if (is_T2(cphy
->adapter
))
114 elmer
|= ELMER0_GP_BIT2
|ELMER0_GP_BIT3
|ELMER0_GP_BIT4
;
115 t1_tpi_write(cphy
->adapter
, A_ELMER0_INT_CAUSE
, elmer
);
121 * Set the PHY speed and duplex. This also disables auto-negotiation, except
122 * for 1Gb/s, where auto-negotiation is mandatory.
124 static int mv88e1xxx_set_speed_duplex(struct cphy
*phy
, int speed
, int duplex
)
128 (void) simple_mdio_read(phy
, MII_BMCR
, &ctl
);
130 ctl
&= ~(BMCR_SPEED100
| BMCR_SPEED1000
| BMCR_ANENABLE
);
131 if (speed
== SPEED_100
)
132 ctl
|= BMCR_SPEED100
;
133 else if (speed
== SPEED_1000
)
134 ctl
|= BMCR_SPEED1000
;
137 ctl
&= ~(BMCR_FULLDPLX
| BMCR_ANENABLE
);
138 if (duplex
== DUPLEX_FULL
)
139 ctl
|= BMCR_FULLDPLX
;
141 if (ctl
& BMCR_SPEED1000
) /* auto-negotiation required for 1Gb/s */
142 ctl
|= BMCR_ANENABLE
;
143 (void) simple_mdio_write(phy
, MII_BMCR
, ctl
);
147 static int mv88e1xxx_crossover_set(struct cphy
*cphy
, int crossover
)
151 (void) simple_mdio_read(cphy
,
152 MV88E1XXX_SPECIFIC_CNTRL_REGISTER
, &data32
);
153 data32
&= ~V_PSCR_MDI_XOVER_MODE(M_PSCR_MDI_XOVER_MODE
);
154 data32
|= V_PSCR_MDI_XOVER_MODE(crossover
);
155 (void) simple_mdio_write(cphy
,
156 MV88E1XXX_SPECIFIC_CNTRL_REGISTER
, data32
);
160 static int mv88e1xxx_autoneg_enable(struct cphy
*cphy
)
164 (void) mv88e1xxx_crossover_set(cphy
, CROSSOVER_AUTO
);
166 (void) simple_mdio_read(cphy
, MII_BMCR
, &ctl
);
167 /* restart autoneg for change to take effect */
168 ctl
|= BMCR_ANENABLE
| BMCR_ANRESTART
;
169 (void) simple_mdio_write(cphy
, MII_BMCR
, ctl
);
173 static int mv88e1xxx_autoneg_disable(struct cphy
*cphy
)
178 * Crossover *must* be set to manual in order to disable auto-neg.
179 * The Alaska FAQs document highlights this point.
181 (void) mv88e1xxx_crossover_set(cphy
, CROSSOVER_MDI
);
184 * Must include autoneg reset when disabling auto-neg. This
185 * is described in the Alaska FAQ document.
187 (void) simple_mdio_read(cphy
, MII_BMCR
, &ctl
);
188 ctl
&= ~BMCR_ANENABLE
;
189 (void) simple_mdio_write(cphy
, MII_BMCR
, ctl
| BMCR_ANRESTART
);
193 static int mv88e1xxx_autoneg_restart(struct cphy
*cphy
)
195 mdio_set_bit(cphy
, MII_BMCR
, BMCR_ANRESTART
);
199 static int mv88e1xxx_advertise(struct cphy
*phy
, unsigned int advertise_map
)
204 (ADVERTISED_1000baseT_Half
| ADVERTISED_1000baseT_Full
)) {
205 (void) simple_mdio_read(phy
, MII_GBCR
, &val
);
206 val
&= ~(GBCR_ADV_1000HALF
| GBCR_ADV_1000FULL
);
207 if (advertise_map
& ADVERTISED_1000baseT_Half
)
208 val
|= GBCR_ADV_1000HALF
;
209 if (advertise_map
& ADVERTISED_1000baseT_Full
)
210 val
|= GBCR_ADV_1000FULL
;
212 (void) simple_mdio_write(phy
, MII_GBCR
, val
);
215 if (advertise_map
& ADVERTISED_10baseT_Half
)
216 val
|= ADVERTISE_10HALF
;
217 if (advertise_map
& ADVERTISED_10baseT_Full
)
218 val
|= ADVERTISE_10FULL
;
219 if (advertise_map
& ADVERTISED_100baseT_Half
)
220 val
|= ADVERTISE_100HALF
;
221 if (advertise_map
& ADVERTISED_100baseT_Full
)
222 val
|= ADVERTISE_100FULL
;
223 if (advertise_map
& ADVERTISED_PAUSE
)
224 val
|= ADVERTISE_PAUSE
;
225 if (advertise_map
& ADVERTISED_ASYM_PAUSE
)
226 val
|= ADVERTISE_PAUSE_ASYM
;
227 (void) simple_mdio_write(phy
, MII_ADVERTISE
, val
);
231 static int mv88e1xxx_set_loopback(struct cphy
*cphy
, int on
)
234 mdio_set_bit(cphy
, MII_BMCR
, BMCR_LOOPBACK
);
236 mdio_clear_bit(cphy
, MII_BMCR
, BMCR_LOOPBACK
);
240 static int mv88e1xxx_get_link_status(struct cphy
*cphy
, int *link_ok
,
241 int *speed
, int *duplex
, int *fc
)
244 int sp
= -1, dplx
= -1, pause
= 0;
246 (void) simple_mdio_read(cphy
,
247 MV88E1XXX_SPECIFIC_STATUS_REGISTER
, &status
);
248 if ((status
& V_PSSR_STATUS_RESOLVED
) != 0) {
249 if (status
& V_PSSR_RX_PAUSE
)
251 if (status
& V_PSSR_TX_PAUSE
)
253 dplx
= (status
& V_PSSR_DUPLEX
) ? DUPLEX_FULL
: DUPLEX_HALF
;
254 sp
= G_PSSR_SPEED(status
);
263 *link_ok
= (status
& V_PSSR_LINK
) != 0;
273 static int mv88e1xxx_downshift_set(struct cphy
*cphy
, int downshift_enable
)
277 (void) simple_mdio_read(cphy
,
278 MV88E1XXX_EXT_PHY_SPECIFIC_CNTRL_REGISTER
, &val
);
281 * Set the downshift counter to 2 so we try to establish Gb link
282 * twice before downshifting.
284 val
&= ~(V_DOWNSHIFT_ENABLE
| V_DOWNSHIFT_CNT(M_DOWNSHIFT_CNT
));
286 if (downshift_enable
)
287 val
|= V_DOWNSHIFT_ENABLE
| V_DOWNSHIFT_CNT(2);
288 (void) simple_mdio_write(cphy
,
289 MV88E1XXX_EXT_PHY_SPECIFIC_CNTRL_REGISTER
, val
);
293 static int mv88e1xxx_interrupt_handler(struct cphy
*cphy
)
299 * Loop until cause reads zero. Need to handle bouncing interrupts.
304 (void) simple_mdio_read(cphy
,
305 MV88E1XXX_INTERRUPT_STATUS_REGISTER
,
307 cause
&= INTR_ENABLE_MASK
;
311 if (cause
& MV88E1XXX_INTR_LINK_CHNG
) {
312 (void) simple_mdio_read(cphy
,
313 MV88E1XXX_SPECIFIC_STATUS_REGISTER
, &status
);
315 if (status
& MV88E1XXX_INTR_LINK_CHNG
)
316 cphy
->state
|= PHY_LINK_UP
;
318 cphy
->state
&= ~PHY_LINK_UP
;
319 if (cphy
->state
& PHY_AUTONEG_EN
)
320 cphy
->state
&= ~PHY_AUTONEG_RDY
;
321 cphy_cause
|= cphy_cause_link_change
;
325 if (cause
& MV88E1XXX_INTR_AUTONEG_DONE
)
326 cphy
->state
|= PHY_AUTONEG_RDY
;
328 if ((cphy
->state
& (PHY_LINK_UP
| PHY_AUTONEG_RDY
)) ==
329 (PHY_LINK_UP
| PHY_AUTONEG_RDY
))
330 cphy_cause
|= cphy_cause_link_change
;
335 static void mv88e1xxx_destroy(struct cphy
*cphy
)
340 static struct cphy_ops mv88e1xxx_ops
= {
341 .destroy
= mv88e1xxx_destroy
,
342 .reset
= mv88e1xxx_reset
,
343 .interrupt_enable
= mv88e1xxx_interrupt_enable
,
344 .interrupt_disable
= mv88e1xxx_interrupt_disable
,
345 .interrupt_clear
= mv88e1xxx_interrupt_clear
,
346 .interrupt_handler
= mv88e1xxx_interrupt_handler
,
347 .autoneg_enable
= mv88e1xxx_autoneg_enable
,
348 .autoneg_disable
= mv88e1xxx_autoneg_disable
,
349 .autoneg_restart
= mv88e1xxx_autoneg_restart
,
350 .advertise
= mv88e1xxx_advertise
,
351 .set_loopback
= mv88e1xxx_set_loopback
,
352 .set_speed_duplex
= mv88e1xxx_set_speed_duplex
,
353 .get_link_status
= mv88e1xxx_get_link_status
,
356 static struct cphy
*mv88e1xxx_phy_create(adapter_t
*adapter
, int phy_addr
,
357 const struct mdio_ops
*mdio_ops
)
359 struct cphy
*cphy
= kzalloc(sizeof(*cphy
), GFP_KERNEL
);
364 cphy_init(cphy
, adapter
, phy_addr
, &mv88e1xxx_ops
, mdio_ops
);
366 /* Configure particular PHY's to run in a different mode. */
367 if ((board_info(adapter
)->caps
& SUPPORTED_TP
) &&
368 board_info(adapter
)->chip_phy
== CHBT_PHY_88E1111
) {
370 * Configure the PHY transmitter as class A to reduce EMI.
372 (void) simple_mdio_write(cphy
,
373 MV88E1XXX_EXTENDED_ADDR_REGISTER
, 0xB);
374 (void) simple_mdio_write(cphy
,
375 MV88E1XXX_EXTENDED_REGISTER
, 0x8004);
377 (void) mv88e1xxx_downshift_set(cphy
, 1); /* Enable downshift */
380 if (is_T2(adapter
)) {
381 (void) simple_mdio_write(cphy
,
382 MV88E1XXX_LED_CONTROL_REGISTER
, 0x1);
388 static int mv88e1xxx_phy_reset(adapter_t
* adapter
)
393 const struct gphy t1_mv88e1xxx_ops
= {
394 .create
= mv88e1xxx_phy_create
,
395 .reset
= mv88e1xxx_phy_reset