Linux Audio

Check our new training course

Open-source upstreaming

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