Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
mv88e1xxx.c
Go to the documentation of this file.
1 /* $Date: 2005/10/24 23:18:13 $ $RCSfile: mv88e1xxx.c,v $ $Revision: 1.49 $ */
2 #include "common.h"
3 #include "mv88e1xxx.h"
4 #include "cphy.h"
5 #include "elmer0.h"
6 
7 /* MV88E1XXX MDI crossover register values */
8 #define CROSSOVER_MDI 0
9 #define CROSSOVER_MDIX 1
10 #define CROSSOVER_AUTO 3
11 
12 #define INTR_ENABLE_MASK 0x6CA0
13 
14 /*
15  * Set the bits given by 'bitval' in PHY register 'reg'.
16  */
17 static void mdio_set_bit(struct cphy *cphy, int reg, u32 bitval)
18 {
19  u32 val;
20 
21  (void) simple_mdio_read(cphy, reg, &val);
22  (void) simple_mdio_write(cphy, reg, val | bitval);
23 }
24 
25 /*
26  * Clear the bits given by 'bitval' in PHY register 'reg'.
27  */
28 static void mdio_clear_bit(struct cphy *cphy, int reg, u32 bitval)
29 {
30  u32 val;
31 
32  (void) simple_mdio_read(cphy, reg, &val);
33  (void) simple_mdio_write(cphy, reg, val & ~bitval);
34 }
35 
36 /*
37  * NAME: phy_reset
38  *
39  * DESC: Reset the given PHY's port. NOTE: This is not a global
40  * chip reset.
41  *
42  * PARAMS: cphy - Pointer to PHY instance data.
43  *
44  * RETURN: 0 - Successful reset.
45  * -1 - Timeout.
46  */
47 static int mv88e1xxx_reset(struct cphy *cphy, int wait)
48 {
49  u32 ctl;
50  int time_out = 1000;
51 
52  mdio_set_bit(cphy, MII_BMCR, BMCR_RESET);
53 
54  do {
55  (void) simple_mdio_read(cphy, MII_BMCR, &ctl);
56  ctl &= BMCR_RESET;
57  if (ctl)
58  udelay(1);
59  } while (ctl && --time_out);
60 
61  return ctl ? -1 : 0;
62 }
63 
64 static int mv88e1xxx_interrupt_enable(struct cphy *cphy)
65 {
66  /* Enable PHY interrupts. */
67  (void) simple_mdio_write(cphy, MV88E1XXX_INTERRUPT_ENABLE_REGISTER,
69 
70  /* Enable Marvell interrupts through Elmer0. */
71  if (t1_is_asic(cphy->adapter)) {
72  u32 elmer;
73 
74  t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
75  elmer |= ELMER0_GP_BIT1;
76  if (is_T2(cphy->adapter))
79  }
80  return 0;
81 }
82 
83 static int mv88e1xxx_interrupt_disable(struct cphy *cphy)
84 {
85  /* Disable all phy interrupts. */
86  (void) simple_mdio_write(cphy, MV88E1XXX_INTERRUPT_ENABLE_REGISTER, 0);
87 
88  /* Disable Marvell interrupts through Elmer0. */
89  if (t1_is_asic(cphy->adapter)) {
90  u32 elmer;
91 
92  t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
93  elmer &= ~ELMER0_GP_BIT1;
94  if (is_T2(cphy->adapter))
97  }
98  return 0;
99 }
100 
101 static int mv88e1xxx_interrupt_clear(struct cphy *cphy)
102 {
103  u32 elmer;
104 
105  /* Clear PHY interrupts by reading the register. */
106  (void) simple_mdio_read(cphy,
108 
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))
115  t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer);
116  }
117  return 0;
118 }
119 
120 /*
121  * Set the PHY speed and duplex. This also disables auto-negotiation, except
122  * for 1Gb/s, where auto-negotiation is mandatory.
123  */
124 static int mv88e1xxx_set_speed_duplex(struct cphy *phy, int speed, int duplex)
125 {
126  u32 ctl;
127 
128  (void) simple_mdio_read(phy, MII_BMCR, &ctl);
129  if (speed >= 0) {
131  if (speed == SPEED_100)
132  ctl |= BMCR_SPEED100;
133  else if (speed == SPEED_1000)
134  ctl |= BMCR_SPEED1000;
135  }
136  if (duplex >= 0) {
137  ctl &= ~(BMCR_FULLDPLX | BMCR_ANENABLE);
138  if (duplex == DUPLEX_FULL)
139  ctl |= BMCR_FULLDPLX;
140  }
141  if (ctl & BMCR_SPEED1000) /* auto-negotiation required for 1Gb/s */
142  ctl |= BMCR_ANENABLE;
143  (void) simple_mdio_write(phy, MII_BMCR, ctl);
144  return 0;
145 }
146 
147 static int mv88e1xxx_crossover_set(struct cphy *cphy, int crossover)
148 {
149  u32 data32;
150 
151  (void) simple_mdio_read(cphy,
154  data32 |= V_PSCR_MDI_XOVER_MODE(crossover);
155  (void) simple_mdio_write(cphy,
157  return 0;
158 }
159 
160 static int mv88e1xxx_autoneg_enable(struct cphy *cphy)
161 {
162  u32 ctl;
163 
164  (void) mv88e1xxx_crossover_set(cphy, CROSSOVER_AUTO);
165 
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);
170  return 0;
171 }
172 
173 static int mv88e1xxx_autoneg_disable(struct cphy *cphy)
174 {
175  u32 ctl;
176 
177  /*
178  * Crossover *must* be set to manual in order to disable auto-neg.
179  * The Alaska FAQs document highlights this point.
180  */
181  (void) mv88e1xxx_crossover_set(cphy, CROSSOVER_MDI);
182 
183  /*
184  * Must include autoneg reset when disabling auto-neg. This
185  * is described in the Alaska FAQ document.
186  */
187  (void) simple_mdio_read(cphy, MII_BMCR, &ctl);
188  ctl &= ~BMCR_ANENABLE;
189  (void) simple_mdio_write(cphy, MII_BMCR, ctl | BMCR_ANRESTART);
190  return 0;
191 }
192 
193 static int mv88e1xxx_autoneg_restart(struct cphy *cphy)
194 {
195  mdio_set_bit(cphy, MII_BMCR, BMCR_ANRESTART);
196  return 0;
197 }
198 
199 static int mv88e1xxx_advertise(struct cphy *phy, unsigned int advertise_map)
200 {
201  u32 val = 0;
202 
203  if (advertise_map &
205  (void) simple_mdio_read(phy, MII_GBCR, &val);
207  if (advertise_map & ADVERTISED_1000baseT_Half)
208  val |= GBCR_ADV_1000HALF;
209  if (advertise_map & ADVERTISED_1000baseT_Full)
210  val |= GBCR_ADV_1000FULL;
211  }
212  (void) simple_mdio_write(phy, MII_GBCR, val);
213 
214  val = 1;
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);
228  return 0;
229 }
230 
231 static int mv88e1xxx_set_loopback(struct cphy *cphy, int on)
232 {
233  if (on)
234  mdio_set_bit(cphy, MII_BMCR, BMCR_LOOPBACK);
235  else
236  mdio_clear_bit(cphy, MII_BMCR, BMCR_LOOPBACK);
237  return 0;
238 }
239 
240 static int mv88e1xxx_get_link_status(struct cphy *cphy, int *link_ok,
241  int *speed, int *duplex, int *fc)
242 {
243  u32 status;
244  int sp = -1, dplx = -1, pause = 0;
245 
246  (void) simple_mdio_read(cphy,
248  if ((status & V_PSSR_STATUS_RESOLVED) != 0) {
249  if (status & V_PSSR_RX_PAUSE)
250  pause |= PAUSE_RX;
251  if (status & V_PSSR_TX_PAUSE)
252  pause |= PAUSE_TX;
253  dplx = (status & V_PSSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
254  sp = G_PSSR_SPEED(status);
255  if (sp == 0)
256  sp = SPEED_10;
257  else if (sp == 1)
258  sp = SPEED_100;
259  else
260  sp = SPEED_1000;
261  }
262  if (link_ok)
263  *link_ok = (status & V_PSSR_LINK) != 0;
264  if (speed)
265  *speed = sp;
266  if (duplex)
267  *duplex = dplx;
268  if (fc)
269  *fc = pause;
270  return 0;
271 }
272 
273 static int mv88e1xxx_downshift_set(struct cphy *cphy, int downshift_enable)
274 {
275  u32 val;
276 
277  (void) simple_mdio_read(cphy,
279 
280  /*
281  * Set the downshift counter to 2 so we try to establish Gb link
282  * twice before downshifting.
283  */
285 
286  if (downshift_enable)
288  (void) simple_mdio_write(cphy,
290  return 0;
291 }
292 
293 static int mv88e1xxx_interrupt_handler(struct cphy *cphy)
294 {
295  int cphy_cause = 0;
296  u32 status;
297 
298  /*
299  * Loop until cause reads zero. Need to handle bouncing interrupts.
300  */
301  while (1) {
302  u32 cause;
303 
304  (void) simple_mdio_read(cphy,
306  &cause);
307  cause &= INTR_ENABLE_MASK;
308  if (!cause)
309  break;
310 
311  if (cause & MV88E1XXX_INTR_LINK_CHNG) {
312  (void) simple_mdio_read(cphy,
314 
315  if (status & MV88E1XXX_INTR_LINK_CHNG)
316  cphy->state |= PHY_LINK_UP;
317  else {
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;
322  }
323  }
324 
325  if (cause & MV88E1XXX_INTR_AUTONEG_DONE)
326  cphy->state |= PHY_AUTONEG_RDY;
327 
328  if ((cphy->state & (PHY_LINK_UP | PHY_AUTONEG_RDY)) ==
330  cphy_cause |= cphy_cause_link_change;
331  }
332  return cphy_cause;
333 }
334 
335 static void mv88e1xxx_destroy(struct cphy *cphy)
336 {
337  kfree(cphy);
338 }
339 
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,
354 };
355 
356 static struct cphy *mv88e1xxx_phy_create(struct net_device *dev, int phy_addr,
357  const struct mdio_ops *mdio_ops)
358 {
359  struct adapter *adapter = netdev_priv(dev);
360  struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL);
361 
362  if (!cphy)
363  return NULL;
364 
365  cphy_init(cphy, dev, phy_addr, &mv88e1xxx_ops, mdio_ops);
366 
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) {
370  /*
371  * Configure the PHY transmitter as class A to reduce EMI.
372  */
373  (void) simple_mdio_write(cphy,
375  (void) simple_mdio_write(cphy,
377  }
378  (void) mv88e1xxx_downshift_set(cphy, 1); /* Enable downshift */
379 
380  /* LED */
381  if (is_T2(adapter)) {
382  (void) simple_mdio_write(cphy,
384  }
385 
386  return cphy;
387 }
388 
389 static int mv88e1xxx_phy_reset(adapter_t* adapter)
390 {
391  return 0;
392 }
393 
394 const struct gphy t1_mv88e1xxx_ops = {
395  .create = mv88e1xxx_phy_create,
396  .reset = mv88e1xxx_phy_reset
397 };