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
;
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
);
98 t1_tpi_write(cphy
->adapter
, A_ELMER0_INT_ENABLE
, elmer
);
103 static int mv88e1xxx_interrupt_clear(struct cphy
*cphy
)
107 /* Clear PHY interrupts by reading the register. */
108 (void) simple_mdio_read(cphy
,
109 MV88E1XXX_INTERRUPT_STATUS_REGISTER
, &elmer
);
111 /* Clear Marvell interrupts through Elmer0. */
112 if (t1_is_asic(cphy
->adapter
)) {
113 t1_tpi_read(cphy
->adapter
, A_ELMER0_INT_CAUSE
, &elmer
);
114 elmer
|= ELMER0_GP_BIT1
;
115 if (is_T2(cphy
->adapter
)) {
116 elmer
|= ELMER0_GP_BIT2
|ELMER0_GP_BIT3
|ELMER0_GP_BIT4
;
118 t1_tpi_write(cphy
->adapter
, A_ELMER0_INT_CAUSE
, elmer
);
124 * Set the PHY speed and duplex. This also disables auto-negotiation, except
125 * for 1Gb/s, where auto-negotiation is mandatory.
127 static int mv88e1xxx_set_speed_duplex(struct cphy
*phy
, int speed
, int duplex
)
131 (void) simple_mdio_read(phy
, MII_BMCR
, &ctl
);
133 ctl
&= ~(BMCR_SPEED100
| BMCR_SPEED1000
| BMCR_ANENABLE
);
134 if (speed
== SPEED_100
)
135 ctl
|= BMCR_SPEED100
;
136 else if (speed
== SPEED_1000
)
137 ctl
|= BMCR_SPEED1000
;
140 ctl
&= ~(BMCR_FULLDPLX
| BMCR_ANENABLE
);
141 if (duplex
== DUPLEX_FULL
)
142 ctl
|= BMCR_FULLDPLX
;
144 if (ctl
& BMCR_SPEED1000
) /* auto-negotiation required for 1Gb/s */
145 ctl
|= BMCR_ANENABLE
;
146 (void) simple_mdio_write(phy
, MII_BMCR
, ctl
);
150 static int mv88e1xxx_crossover_set(struct cphy
*cphy
, int crossover
)
154 (void) simple_mdio_read(cphy
,
155 MV88E1XXX_SPECIFIC_CNTRL_REGISTER
, &data32
);
156 data32
&= ~V_PSCR_MDI_XOVER_MODE(M_PSCR_MDI_XOVER_MODE
);
157 data32
|= V_PSCR_MDI_XOVER_MODE(crossover
);
158 (void) simple_mdio_write(cphy
,
159 MV88E1XXX_SPECIFIC_CNTRL_REGISTER
, data32
);
163 static int mv88e1xxx_autoneg_enable(struct cphy
*cphy
)
167 (void) mv88e1xxx_crossover_set(cphy
, CROSSOVER_AUTO
);
169 (void) simple_mdio_read(cphy
, MII_BMCR
, &ctl
);
170 /* restart autoneg for change to take effect */
171 ctl
|= BMCR_ANENABLE
| BMCR_ANRESTART
;
172 (void) simple_mdio_write(cphy
, MII_BMCR
, ctl
);
176 static int mv88e1xxx_autoneg_disable(struct cphy
*cphy
)
181 * Crossover *must* be set to manual in order to disable auto-neg.
182 * The Alaska FAQs document highlights this point.
184 (void) mv88e1xxx_crossover_set(cphy
, CROSSOVER_MDI
);
187 * Must include autoneg reset when disabling auto-neg. This
188 * is described in the Alaska FAQ document.
190 (void) simple_mdio_read(cphy
, MII_BMCR
, &ctl
);
191 ctl
&= ~BMCR_ANENABLE
;
192 (void) simple_mdio_write(cphy
, MII_BMCR
, ctl
| BMCR_ANRESTART
);
196 static int mv88e1xxx_autoneg_restart(struct cphy
*cphy
)
198 mdio_set_bit(cphy
, MII_BMCR
, BMCR_ANRESTART
);
202 static int mv88e1xxx_advertise(struct cphy
*phy
, unsigned int advertise_map
)
207 (ADVERTISED_1000baseT_Half
| ADVERTISED_1000baseT_Full
)) {
208 (void) simple_mdio_read(phy
, MII_GBCR
, &val
);
209 val
&= ~(GBCR_ADV_1000HALF
| GBCR_ADV_1000FULL
);
210 if (advertise_map
& ADVERTISED_1000baseT_Half
)
211 val
|= GBCR_ADV_1000HALF
;
212 if (advertise_map
& ADVERTISED_1000baseT_Full
)
213 val
|= GBCR_ADV_1000FULL
;
215 (void) simple_mdio_write(phy
, MII_GBCR
, val
);
218 if (advertise_map
& ADVERTISED_10baseT_Half
)
219 val
|= ADVERTISE_10HALF
;
220 if (advertise_map
& ADVERTISED_10baseT_Full
)
221 val
|= ADVERTISE_10FULL
;
222 if (advertise_map
& ADVERTISED_100baseT_Half
)
223 val
|= ADVERTISE_100HALF
;
224 if (advertise_map
& ADVERTISED_100baseT_Full
)
225 val
|= ADVERTISE_100FULL
;
226 if (advertise_map
& ADVERTISED_PAUSE
)
227 val
|= ADVERTISE_PAUSE
;
228 if (advertise_map
& ADVERTISED_ASYM_PAUSE
)
229 val
|= ADVERTISE_PAUSE_ASYM
;
230 (void) simple_mdio_write(phy
, MII_ADVERTISE
, val
);
234 static int mv88e1xxx_set_loopback(struct cphy
*cphy
, int on
)
237 mdio_set_bit(cphy
, MII_BMCR
, BMCR_LOOPBACK
);
239 mdio_clear_bit(cphy
, MII_BMCR
, BMCR_LOOPBACK
);
243 static int mv88e1xxx_get_link_status(struct cphy
*cphy
, int *link_ok
,
244 int *speed
, int *duplex
, int *fc
)
247 int sp
= -1, dplx
= -1, pause
= 0;
249 (void) simple_mdio_read(cphy
,
250 MV88E1XXX_SPECIFIC_STATUS_REGISTER
, &status
);
251 if ((status
& V_PSSR_STATUS_RESOLVED
) != 0) {
252 if (status
& V_PSSR_RX_PAUSE
)
254 if (status
& V_PSSR_TX_PAUSE
)
256 dplx
= (status
& V_PSSR_DUPLEX
) ? DUPLEX_FULL
: DUPLEX_HALF
;
257 sp
= G_PSSR_SPEED(status
);
266 *link_ok
= (status
& V_PSSR_LINK
) != 0;
276 static int mv88e1xxx_downshift_set(struct cphy
*cphy
, int downshift_enable
)
280 (void) simple_mdio_read(cphy
,
281 MV88E1XXX_EXT_PHY_SPECIFIC_CNTRL_REGISTER
, &val
);
284 * Set the downshift counter to 2 so we try to establish Gb link
285 * twice before downshifting.
287 val
&= ~(V_DOWNSHIFT_ENABLE
| V_DOWNSHIFT_CNT(M_DOWNSHIFT_CNT
));
289 if (downshift_enable
)
290 val
|= V_DOWNSHIFT_ENABLE
| V_DOWNSHIFT_CNT(2);
291 (void) simple_mdio_write(cphy
,
292 MV88E1XXX_EXT_PHY_SPECIFIC_CNTRL_REGISTER
, val
);
296 static int mv88e1xxx_interrupt_handler(struct cphy
*cphy
)
302 * Loop until cause reads zero. Need to handle bouncing interrupts.
307 (void) simple_mdio_read(cphy
,
308 MV88E1XXX_INTERRUPT_STATUS_REGISTER
,
310 cause
&= INTR_ENABLE_MASK
;
313 if (cause
& MV88E1XXX_INTR_LINK_CHNG
) {
314 (void) simple_mdio_read(cphy
,
315 MV88E1XXX_SPECIFIC_STATUS_REGISTER
, &status
);
317 if (status
& MV88E1XXX_INTR_LINK_CHNG
) {
318 cphy
->state
|= PHY_LINK_UP
;
320 cphy
->state
&= ~PHY_LINK_UP
;
321 if (cphy
->state
& PHY_AUTONEG_EN
)
322 cphy
->state
&= ~PHY_AUTONEG_RDY
;
323 cphy_cause
|= cphy_cause_link_change
;
327 if (cause
& MV88E1XXX_INTR_AUTONEG_DONE
)
328 cphy
->state
|= PHY_AUTONEG_RDY
;
330 if ((cphy
->state
& (PHY_LINK_UP
| PHY_AUTONEG_RDY
)) ==
331 (PHY_LINK_UP
| PHY_AUTONEG_RDY
))
332 cphy_cause
|= cphy_cause_link_change
;
337 static void mv88e1xxx_destroy(struct cphy
*cphy
)
342 static struct cphy_ops mv88e1xxx_ops
= {
343 .destroy
= mv88e1xxx_destroy
,
344 .reset
= mv88e1xxx_reset
,
345 .interrupt_enable
= mv88e1xxx_interrupt_enable
,
346 .interrupt_disable
= mv88e1xxx_interrupt_disable
,
347 .interrupt_clear
= mv88e1xxx_interrupt_clear
,
348 .interrupt_handler
= mv88e1xxx_interrupt_handler
,
349 .autoneg_enable
= mv88e1xxx_autoneg_enable
,
350 .autoneg_disable
= mv88e1xxx_autoneg_disable
,
351 .autoneg_restart
= mv88e1xxx_autoneg_restart
,
352 .advertise
= mv88e1xxx_advertise
,
353 .set_loopback
= mv88e1xxx_set_loopback
,
354 .set_speed_duplex
= mv88e1xxx_set_speed_duplex
,
355 .get_link_status
= mv88e1xxx_get_link_status
,
358 static struct cphy
*mv88e1xxx_phy_create(adapter_t
*adapter
, int phy_addr
,
359 struct mdio_ops
*mdio_ops
)
361 struct cphy
*cphy
= kzalloc(sizeof(*cphy
), GFP_KERNEL
);
363 if (!cphy
) return NULL
;
365 cphy_init(cphy
, adapter
, phy_addr
, &mv88e1xxx_ops
, mdio_ops
);
367 /* Configure particular PHY's to run in a different mode. */
368 if ((board_info(adapter
)->caps
& SUPPORTED_TP
) &&
369 board_info(adapter
)->chip_phy
== CHBT_PHY_88E1111
) {
371 * Configure the PHY transmitter as class A to reduce EMI.
373 (void) simple_mdio_write(cphy
,
374 MV88E1XXX_EXTENDED_ADDR_REGISTER
, 0xB);
375 (void) simple_mdio_write(cphy
,
376 MV88E1XXX_EXTENDED_REGISTER
, 0x8004);
378 (void) mv88e1xxx_downshift_set(cphy
, 1); /* Enable downshift */
381 if (is_T2(adapter
)) {
382 (void) simple_mdio_write(cphy
,
383 MV88E1XXX_LED_CONTROL_REGISTER
, 0x1);
389 static int mv88e1xxx_phy_reset(adapter_t
* adapter
)
394 struct gphy t1_mv88e1xxx_ops
= {
395 mv88e1xxx_phy_create
,