1d39a76e7Sxw /*
2d39a76e7Sxw  * CDDL HEADER START
3d39a76e7Sxw  *
4d39a76e7Sxw  * The contents of this file are subject to the terms of the
5d39a76e7Sxw  * Common Development and Distribution License (the "License").
6d39a76e7Sxw  * You may not use this file except in compliance with the License.
7d39a76e7Sxw  *
8d39a76e7Sxw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d39a76e7Sxw  * or http://www.opensolaris.org/os/licensing.
10d39a76e7Sxw  * See the License for the specific language governing permissions
11d39a76e7Sxw  * and limitations under the License.
12d39a76e7Sxw  *
13d39a76e7Sxw  * When distributing Covered Code, include this CDDL HEADER in each
14d39a76e7Sxw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d39a76e7Sxw  * If applicable, add the following below this CDDL HEADER, with the
16d39a76e7Sxw  * fields enclosed by brackets "[]" replaced with your own identifying
17d39a76e7Sxw  * information: Portions Copyright [yyyy] [name of copyright owner]
18d39a76e7Sxw  *
19d39a76e7Sxw  * CDDL HEADER END
20d39a76e7Sxw  */
21d39a76e7Sxw 
22d39a76e7Sxw /*
23d39a76e7Sxw  * Copyright (C) 2003-2005 Chelsio Communications.  All rights reserved.
24d39a76e7Sxw  */
25d39a76e7Sxw 
26d39a76e7Sxw #include "common.h"
27d39a76e7Sxw #include "mv88e1xxx.h"
28d39a76e7Sxw #include "cphy.h"
29d39a76e7Sxw #include "elmer0.h"
30d39a76e7Sxw 
31d39a76e7Sxw /* MV88E1XXX MDI crossover register values */
32d39a76e7Sxw #define CROSSOVER_MDI   0
33d39a76e7Sxw #define CROSSOVER_MDIX  1
34d39a76e7Sxw #define CROSSOVER_AUTO  3
35d39a76e7Sxw 
36d39a76e7Sxw #define INTR_ENABLE_MASK 0x6CA0
37d39a76e7Sxw 
38d39a76e7Sxw /*
39d39a76e7Sxw  * Set the bits given by 'bitval' in PHY register 'reg'.
40d39a76e7Sxw  */
mdio_set_bit(struct cphy * cphy,int reg,u32 bitval)41d39a76e7Sxw static void mdio_set_bit(struct cphy *cphy, int reg, u32 bitval)
42d39a76e7Sxw {
43d39a76e7Sxw 	u32 val;
44d39a76e7Sxw 
45d39a76e7Sxw 	(void) simple_mdio_read(cphy, reg, &val);
46d39a76e7Sxw 	(void) simple_mdio_write(cphy, reg, val | bitval);
47d39a76e7Sxw }
48d39a76e7Sxw 
49d39a76e7Sxw /*
50d39a76e7Sxw  * Clear the bits given by 'bitval' in PHY register 'reg'.
51d39a76e7Sxw  */
mdio_clear_bit(struct cphy * cphy,int reg,u32 bitval)52d39a76e7Sxw static void mdio_clear_bit(struct cphy *cphy, int reg, u32 bitval)
53d39a76e7Sxw {
54d39a76e7Sxw 	u32 val;
55d39a76e7Sxw 
56d39a76e7Sxw 	(void) simple_mdio_read(cphy, reg, &val);
57d39a76e7Sxw 	(void) simple_mdio_write(cphy, reg, val & ~bitval);
58d39a76e7Sxw }
59d39a76e7Sxw 
60d39a76e7Sxw /*
61d39a76e7Sxw  * NAME:   phy_reset
62d39a76e7Sxw  *
63d39a76e7Sxw  * DESC:   Reset the given PHY's port. NOTE: This is not a global
64d39a76e7Sxw  *         chip reset.
65d39a76e7Sxw  *
66d39a76e7Sxw  * PARAMS: cphy     - Pointer to PHY instance data.
67*2d6eb4a5SToomas Soome  *
68d39a76e7Sxw  * RETURN:  0 - Successfull reset.
69d39a76e7Sxw  *         -1 - Timeout.
70d39a76e7Sxw  */
71d39a76e7Sxw /* ARGSUSED */
mv88e1xxx_reset(struct cphy * cphy,int wait)72d39a76e7Sxw static int mv88e1xxx_reset(struct cphy *cphy, int wait)
73d39a76e7Sxw {
74d39a76e7Sxw 	u32 ctl;
75d39a76e7Sxw 	int time_out = 1000;
76d39a76e7Sxw 
77d39a76e7Sxw 	mdio_set_bit(cphy, MII_BMCR, BMCR_RESET);
78d39a76e7Sxw 
79d39a76e7Sxw 	do {
80d39a76e7Sxw 		(void) simple_mdio_read(cphy, MII_BMCR, &ctl);
81d39a76e7Sxw 		ctl &= BMCR_RESET;
82d39a76e7Sxw 		if (ctl)
83d39a76e7Sxw 			DELAY_US(1);
84d39a76e7Sxw 	} while (ctl && --time_out);
85d39a76e7Sxw 
86d39a76e7Sxw 	return ctl ? -1 : 0;
87d39a76e7Sxw }
88d39a76e7Sxw 
mv88e1xxx_interrupt_enable(struct cphy * cphy)89d39a76e7Sxw static int mv88e1xxx_interrupt_enable(struct cphy *cphy)
90d39a76e7Sxw {
91d39a76e7Sxw 	/* Enable PHY interrupts. */
92d39a76e7Sxw 	(void) simple_mdio_write(cphy, MV88E1XXX_INTERRUPT_ENABLE_REGISTER,
93d39a76e7Sxw 		   INTR_ENABLE_MASK);
94d39a76e7Sxw 
95d39a76e7Sxw 	/* Enable Marvell interrupts through Elmer0. */
96d39a76e7Sxw 	if (t1_is_asic(cphy->adapter)) {
97d39a76e7Sxw 		u32 elmer;
98d39a76e7Sxw 
99d39a76e7Sxw 		(void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
100d39a76e7Sxw 		elmer |= ELMER0_GP_BIT1;
101d39a76e7Sxw 		if (is_T2(cphy->adapter)) {
102d39a76e7Sxw 			elmer |= ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4;
103d39a76e7Sxw 		}
104d39a76e7Sxw 		(void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
105d39a76e7Sxw 	}
106d39a76e7Sxw 	return 0;
107d39a76e7Sxw }
108d39a76e7Sxw 
mv88e1xxx_interrupt_disable(struct cphy * cphy)109d39a76e7Sxw static int mv88e1xxx_interrupt_disable(struct cphy *cphy)
110d39a76e7Sxw {
111d39a76e7Sxw 	/* Disable all phy interrupts. */
112d39a76e7Sxw 	(void) simple_mdio_write(cphy, MV88E1XXX_INTERRUPT_ENABLE_REGISTER, 0);
113d39a76e7Sxw 
114d39a76e7Sxw 	/* Disable Marvell interrupts through Elmer0. */
115d39a76e7Sxw 	if (t1_is_asic(cphy->adapter)) {
116d39a76e7Sxw 		u32 elmer;
117d39a76e7Sxw 
118d39a76e7Sxw 		(void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
119d39a76e7Sxw 		elmer &= ~ELMER0_GP_BIT1;
120d39a76e7Sxw 		if (is_T2(cphy->adapter)) {
121d39a76e7Sxw 			elmer &= ~(ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4);
122d39a76e7Sxw 		}
123d39a76e7Sxw 		(void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
124d39a76e7Sxw 	}
125d39a76e7Sxw 	return 0;
126d39a76e7Sxw }
127d39a76e7Sxw 
mv88e1xxx_interrupt_clear(struct cphy * cphy)128d39a76e7Sxw static int mv88e1xxx_interrupt_clear(struct cphy *cphy)
129d39a76e7Sxw {
130d39a76e7Sxw 	u32 elmer;
131d39a76e7Sxw 
132d39a76e7Sxw 	/* Clear PHY interrupts by reading the register. */
133d39a76e7Sxw 	(void) simple_mdio_read(cphy, MV88E1XXX_INTERRUPT_STATUS_REGISTER, &elmer);
134d39a76e7Sxw 
135d39a76e7Sxw 	/* Clear Marvell interrupts through Elmer0. */
136d39a76e7Sxw 	if (t1_is_asic(cphy->adapter)) {
137d39a76e7Sxw 		(void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer);
138d39a76e7Sxw 		elmer |= ELMER0_GP_BIT1;
139d39a76e7Sxw 		if (is_T2(cphy->adapter)) {
140d39a76e7Sxw 			elmer |= ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4;
141d39a76e7Sxw 		}
142d39a76e7Sxw 		(void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer);
143d39a76e7Sxw 	}
144d39a76e7Sxw 	return 0;
145d39a76e7Sxw }
146d39a76e7Sxw 
147d39a76e7Sxw /*
148d39a76e7Sxw  * Set the PHY speed and duplex.  This also disables auto-negotiation, except
149d39a76e7Sxw  * for 1Gb/s, where auto-negotiation is mandatory.
150d39a76e7Sxw  */
mv88e1xxx_set_speed_duplex(struct cphy * phy,int speed,int duplex)151d39a76e7Sxw static int mv88e1xxx_set_speed_duplex(struct cphy *phy, int speed, int duplex)
152d39a76e7Sxw {
153d39a76e7Sxw 	u32 ctl;
154d39a76e7Sxw 
155d39a76e7Sxw 	(void) simple_mdio_read(phy, MII_BMCR, &ctl);
156d39a76e7Sxw 	if (speed >= 0) {
157d39a76e7Sxw 		ctl &= ~(BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE);
158d39a76e7Sxw 		if (speed == SPEED_100)
159d39a76e7Sxw 			ctl |= BMCR_SPEED100;
160d39a76e7Sxw 		else if (speed == SPEED_1000)
161d39a76e7Sxw 			ctl |= BMCR_SPEED1000;
162d39a76e7Sxw 	}
163d39a76e7Sxw 	if (duplex >= 0) {
164d39a76e7Sxw 		ctl &= ~(BMCR_FULLDPLX | BMCR_ANENABLE);
165d39a76e7Sxw 		if (duplex == DUPLEX_FULL)
166d39a76e7Sxw 			ctl |= BMCR_FULLDPLX;
167d39a76e7Sxw 	}
168d39a76e7Sxw 	if (ctl & BMCR_SPEED1000)  /* auto-negotiation required for 1Gb/s */
169d39a76e7Sxw 		ctl |= BMCR_ANENABLE;
170d39a76e7Sxw 	(void) simple_mdio_write(phy, MII_BMCR, ctl);
171d39a76e7Sxw 	return 0;
172d39a76e7Sxw }
173d39a76e7Sxw 
mv88e1xxx_crossover_set(struct cphy * cphy,int crossover)174d39a76e7Sxw static int mv88e1xxx_crossover_set(struct cphy *cphy, int crossover)
175d39a76e7Sxw {
176d39a76e7Sxw 	u32 data32;
177d39a76e7Sxw 
178d39a76e7Sxw 	(void) simple_mdio_read(cphy, MV88E1XXX_SPECIFIC_CNTRL_REGISTER, &data32);
179d39a76e7Sxw 	data32 &= ~V_PSCR_MDI_XOVER_MODE(M_PSCR_MDI_XOVER_MODE);
180d39a76e7Sxw 	data32 |= V_PSCR_MDI_XOVER_MODE(crossover);
181d39a76e7Sxw 	(void) simple_mdio_write(cphy, MV88E1XXX_SPECIFIC_CNTRL_REGISTER, data32);
182d39a76e7Sxw 	return 0;
183d39a76e7Sxw }
184d39a76e7Sxw 
mv88e1xxx_autoneg_enable(struct cphy * cphy)185d39a76e7Sxw static int mv88e1xxx_autoneg_enable(struct cphy *cphy)
186d39a76e7Sxw {
187d39a76e7Sxw 	u32 ctl;
188d39a76e7Sxw 
189d39a76e7Sxw 	(void) mv88e1xxx_crossover_set(cphy, CROSSOVER_AUTO);
190d39a76e7Sxw 
191d39a76e7Sxw 	(void) simple_mdio_read(cphy, MII_BMCR, &ctl);
192d39a76e7Sxw 	/* restart autoneg for change to take effect */
193d39a76e7Sxw 	ctl |= BMCR_ANENABLE | BMCR_ANRESTART;
194d39a76e7Sxw 	(void) simple_mdio_write(cphy, MII_BMCR, ctl);
195d39a76e7Sxw 	return 0;
196d39a76e7Sxw }
197d39a76e7Sxw 
mv88e1xxx_autoneg_disable(struct cphy * cphy)198d39a76e7Sxw static int mv88e1xxx_autoneg_disable(struct cphy *cphy)
199d39a76e7Sxw {
200d39a76e7Sxw 	u32 ctl;
201d39a76e7Sxw 
202d39a76e7Sxw 	/*
203d39a76e7Sxw 	 * Crossover *must* be set to manual in order to disable auto-neg.
204d39a76e7Sxw 	 * The Alaska FAQs document highlights this point.
205d39a76e7Sxw 	 */
206d39a76e7Sxw 	(void) mv88e1xxx_crossover_set(cphy, CROSSOVER_MDI);
207d39a76e7Sxw 
208d39a76e7Sxw 	/*
209d39a76e7Sxw 	 * Must include autoneg reset when disabling auto-neg. This
210d39a76e7Sxw 	 * is described in the Alaska FAQ document.
211d39a76e7Sxw 	 */
212d39a76e7Sxw 	(void) simple_mdio_read(cphy, MII_BMCR, &ctl);
213d39a76e7Sxw 	ctl &= ~BMCR_ANENABLE;
214d39a76e7Sxw 	(void) simple_mdio_write(cphy, MII_BMCR, ctl | BMCR_ANRESTART);
215d39a76e7Sxw 	return 0;
216d39a76e7Sxw }
217d39a76e7Sxw 
mv88e1xxx_autoneg_restart(struct cphy * cphy)218d39a76e7Sxw static int mv88e1xxx_autoneg_restart(struct cphy *cphy)
219d39a76e7Sxw {
220d39a76e7Sxw 	mdio_set_bit(cphy, MII_BMCR, BMCR_ANRESTART);
221d39a76e7Sxw 	return 0;
222d39a76e7Sxw }
223d39a76e7Sxw 
mv88e1xxx_advertise(struct cphy * phy,unsigned int advertise_map)224d39a76e7Sxw static int mv88e1xxx_advertise(struct cphy *phy, unsigned int advertise_map)
225d39a76e7Sxw {
226d39a76e7Sxw 	u32 val = 0;
227d39a76e7Sxw 
228d39a76e7Sxw 	if (advertise_map &
229d39a76e7Sxw 	    (ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full)) {
230d39a76e7Sxw 		(void) simple_mdio_read(phy, MII_GBCR, &val);
231d39a76e7Sxw 		val &= ~(GBCR_ADV_1000HALF | GBCR_ADV_1000FULL);
232d39a76e7Sxw 		if (advertise_map & ADVERTISED_1000baseT_Half)
233d39a76e7Sxw 			val |= GBCR_ADV_1000HALF;
234d39a76e7Sxw 		if (advertise_map & ADVERTISED_1000baseT_Full)
235d39a76e7Sxw 			val |= GBCR_ADV_1000FULL;
236d39a76e7Sxw 	}
237d39a76e7Sxw 	(void) simple_mdio_write(phy, MII_GBCR, val);
238d39a76e7Sxw 
239d39a76e7Sxw 	val = 1;
240d39a76e7Sxw 	if (advertise_map & ADVERTISED_10baseT_Half)
241d39a76e7Sxw 		val |= ADVERTISE_10HALF;
242d39a76e7Sxw 	if (advertise_map & ADVERTISED_10baseT_Full)
243d39a76e7Sxw 		val |= ADVERTISE_10FULL;
244d39a76e7Sxw 	if (advertise_map & ADVERTISED_100baseT_Half)
245d39a76e7Sxw 		val |= ADVERTISE_100HALF;
246d39a76e7Sxw 	if (advertise_map & ADVERTISED_100baseT_Full)
247d39a76e7Sxw 		val |= ADVERTISE_100FULL;
248d39a76e7Sxw 	if (advertise_map & ADVERTISED_PAUSE)
249d39a76e7Sxw 		val |= ADVERTISE_PAUSE;
250d39a76e7Sxw 	if (advertise_map & ADVERTISED_ASYM_PAUSE)
251d39a76e7Sxw 		val |= ADVERTISE_PAUSE_ASYM;
252d39a76e7Sxw 	(void) simple_mdio_write(phy, MII_ADVERTISE, val);
253d39a76e7Sxw 	return 0;
254d39a76e7Sxw }
255d39a76e7Sxw 
mv88e1xxx_set_loopback(struct cphy * cphy,int on)256d39a76e7Sxw static int mv88e1xxx_set_loopback(struct cphy *cphy, int on)
257d39a76e7Sxw {
258d39a76e7Sxw 	if (on)
259d39a76e7Sxw 		mdio_set_bit(cphy, MII_BMCR, BMCR_LOOPBACK);
260d39a76e7Sxw 	else
261d39a76e7Sxw 		mdio_clear_bit(cphy, MII_BMCR, BMCR_LOOPBACK);
262d39a76e7Sxw 	return 0;
263d39a76e7Sxw }
264d39a76e7Sxw 
mv88e1xxx_get_link_status(struct cphy * cphy,int * link_ok,int * speed,int * duplex,int * fc)265d39a76e7Sxw static int mv88e1xxx_get_link_status(struct cphy *cphy, int *link_ok,
266d39a76e7Sxw 				     int *speed, int *duplex, int *fc)
267d39a76e7Sxw {
268d39a76e7Sxw 	u32 status;
269d39a76e7Sxw 	int sp = -1, dplx = -1, pause = 0;
270d39a76e7Sxw 
271d39a76e7Sxw 	(void) simple_mdio_read(cphy, MV88E1XXX_SPECIFIC_STATUS_REGISTER, &status);
272d39a76e7Sxw 	if ((status & V_PSSR_STATUS_RESOLVED) != 0) {
273d39a76e7Sxw 		if (status & V_PSSR_RX_PAUSE)
274d39a76e7Sxw 			pause |= PAUSE_RX;
275d39a76e7Sxw 		if (status & V_PSSR_TX_PAUSE)
276d39a76e7Sxw 			pause |= PAUSE_TX;
277d39a76e7Sxw 		dplx = (status & V_PSSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
278d39a76e7Sxw 		sp = G_PSSR_SPEED(status);
279d39a76e7Sxw 		if (sp == 0)
280d39a76e7Sxw 			sp = SPEED_10;
281d39a76e7Sxw 		else if (sp == 1)
282d39a76e7Sxw 			sp = SPEED_100;
283d39a76e7Sxw 		else
284d39a76e7Sxw 			sp = SPEED_1000;
285d39a76e7Sxw 	}
286d39a76e7Sxw 	if (link_ok)
287d39a76e7Sxw 		*link_ok = (status & V_PSSR_LINK) != 0;
288d39a76e7Sxw 	if (speed)
289d39a76e7Sxw 		*speed = sp;
290d39a76e7Sxw 	if (duplex)
291d39a76e7Sxw 		*duplex = dplx;
292d39a76e7Sxw 	if (fc)
293d39a76e7Sxw 		*fc = pause;
294d39a76e7Sxw 	return 0;
295d39a76e7Sxw }
296d39a76e7Sxw 
mv88e1xxx_downshift_set(struct cphy * cphy,int downshift_enable)297d39a76e7Sxw static int mv88e1xxx_downshift_set(struct cphy *cphy, int downshift_enable)
298d39a76e7Sxw {
299d39a76e7Sxw 	u32 val;
300d39a76e7Sxw 
301d39a76e7Sxw 	(void) simple_mdio_read(cphy, MV88E1XXX_EXT_PHY_SPECIFIC_CNTRL_REGISTER, &val);
302d39a76e7Sxw 
303d39a76e7Sxw 	/*
304d39a76e7Sxw 	 * Set the downshift counter to 2 so we try to establish Gb link
305d39a76e7Sxw 	 * twice before downshifting.
306d39a76e7Sxw 	 */
307d39a76e7Sxw 	val &= ~(V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(M_DOWNSHIFT_CNT));
308*2d6eb4a5SToomas Soome 
309d39a76e7Sxw 	if (downshift_enable)
310d39a76e7Sxw 		val |= V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(2);
311d39a76e7Sxw 	(void) simple_mdio_write(cphy, MV88E1XXX_EXT_PHY_SPECIFIC_CNTRL_REGISTER, val);
312d39a76e7Sxw 	return 0;
313d39a76e7Sxw }
314d39a76e7Sxw 
mv88e1xxx_interrupt_handler(struct cphy * cphy)315d39a76e7Sxw static int mv88e1xxx_interrupt_handler(struct cphy *cphy)
316d39a76e7Sxw {
317d39a76e7Sxw 	int cphy_cause = 0;
318d39a76e7Sxw 	u32 status;
319d39a76e7Sxw 
320d39a76e7Sxw 	/*
321d39a76e7Sxw 	 * Loop until cause reads zero. Need to handle bouncing interrupts.
322*2d6eb4a5SToomas Soome          */
323d39a76e7Sxw 	/*CONSTCOND*/
324d39a76e7Sxw 	while (1) {
325d39a76e7Sxw 		u32 cause;
326d39a76e7Sxw 
327d39a76e7Sxw 		(void) simple_mdio_read(cphy, MV88E1XXX_INTERRUPT_STATUS_REGISTER,
328d39a76e7Sxw 				 &cause);
329d39a76e7Sxw 		cause &= INTR_ENABLE_MASK;
330d39a76e7Sxw 		if (!cause) break;
331d39a76e7Sxw 
332d39a76e7Sxw 		if (cause & MV88E1XXX_INTR_LINK_CHNG) {
333d39a76e7Sxw 			(void) simple_mdio_read(cphy,
334d39a76e7Sxw 				MV88E1XXX_SPECIFIC_STATUS_REGISTER, &status);
335d39a76e7Sxw 
336d39a76e7Sxw 			if (status & MV88E1XXX_INTR_LINK_CHNG) {
337d39a76e7Sxw 				cphy->state |= PHY_LINK_UP;
338d39a76e7Sxw 			} else {
339d39a76e7Sxw 				cphy->state &= ~PHY_LINK_UP;
340d39a76e7Sxw 				if (cphy->state & PHY_AUTONEG_EN)
341d39a76e7Sxw 					cphy->state &= ~PHY_AUTONEG_RDY;
342d39a76e7Sxw 				cphy_cause |= cphy_cause_link_change;
343d39a76e7Sxw 			}
344d39a76e7Sxw 		}
345d39a76e7Sxw 
346d39a76e7Sxw 		if (cause & MV88E1XXX_INTR_AUTONEG_DONE)
347d39a76e7Sxw 			cphy->state |= PHY_AUTONEG_RDY;
348d39a76e7Sxw 
349d39a76e7Sxw 		if ((cphy->state & (PHY_LINK_UP | PHY_AUTONEG_RDY)) ==
350d39a76e7Sxw 			(PHY_LINK_UP | PHY_AUTONEG_RDY))
351d39a76e7Sxw 				cphy_cause |= cphy_cause_link_change;
352d39a76e7Sxw 	}
353d39a76e7Sxw 	return cphy_cause;
354d39a76e7Sxw }
355d39a76e7Sxw 
mv88e1xxx_destroy(struct cphy * cphy)356d39a76e7Sxw static void mv88e1xxx_destroy(struct cphy *cphy)
357d39a76e7Sxw {
358d39a76e7Sxw 	t1_os_free((void *)cphy, sizeof(*cphy));
359d39a76e7Sxw }
360d39a76e7Sxw 
361d39a76e7Sxw #ifdef C99_NOT_SUPPORTED
362d39a76e7Sxw static struct cphy_ops mv88e1xxx_ops = {
363d39a76e7Sxw 	mv88e1xxx_destroy,
364d39a76e7Sxw 	mv88e1xxx_reset,
365d39a76e7Sxw 	mv88e1xxx_interrupt_enable,
366d39a76e7Sxw 	mv88e1xxx_interrupt_disable,
367d39a76e7Sxw 	mv88e1xxx_interrupt_clear,
368d39a76e7Sxw 	mv88e1xxx_interrupt_handler,
369d39a76e7Sxw 	mv88e1xxx_autoneg_enable,
370d39a76e7Sxw 	mv88e1xxx_autoneg_disable,
371d39a76e7Sxw 	mv88e1xxx_autoneg_restart,
372d39a76e7Sxw 	mv88e1xxx_advertise,
373d39a76e7Sxw 	mv88e1xxx_set_loopback,
374d39a76e7Sxw 	mv88e1xxx_set_speed_duplex,
375d39a76e7Sxw 	mv88e1xxx_get_link_status,
376d39a76e7Sxw };
377d39a76e7Sxw #else
378d39a76e7Sxw static struct cphy_ops mv88e1xxx_ops = {
379d39a76e7Sxw 	.destroy              = mv88e1xxx_destroy,
380d39a76e7Sxw 	.reset                = mv88e1xxx_reset,
381d39a76e7Sxw 	.interrupt_enable     = mv88e1xxx_interrupt_enable,
382d39a76e7Sxw 	.interrupt_disable    = mv88e1xxx_interrupt_disable,
383d39a76e7Sxw 	.interrupt_clear      = mv88e1xxx_interrupt_clear,
384d39a76e7Sxw 	.interrupt_handler    = mv88e1xxx_interrupt_handler,
385d39a76e7Sxw 	.autoneg_enable       = mv88e1xxx_autoneg_enable,
386d39a76e7Sxw 	.autoneg_disable      = mv88e1xxx_autoneg_disable,
387d39a76e7Sxw 	.autoneg_restart      = mv88e1xxx_autoneg_restart,
388d39a76e7Sxw 	.advertise            = mv88e1xxx_advertise,
389d39a76e7Sxw 	.set_loopback         = mv88e1xxx_set_loopback,
390d39a76e7Sxw 	.set_speed_duplex     = mv88e1xxx_set_speed_duplex,
391d39a76e7Sxw 	.get_link_status      = mv88e1xxx_get_link_status,
392d39a76e7Sxw };
393d39a76e7Sxw #endif
394d39a76e7Sxw 
mv88e1xxx_phy_create(adapter_t * adapter,int phy_addr,struct mdio_ops * mdio_ops)395d39a76e7Sxw static struct cphy *mv88e1xxx_phy_create(adapter_t *adapter, int phy_addr,
396d39a76e7Sxw 					 struct mdio_ops *mdio_ops)
397d39a76e7Sxw {
398d39a76e7Sxw 	struct cphy *cphy = t1_os_malloc_wait_zero(sizeof(*cphy));
399d39a76e7Sxw 
400d39a76e7Sxw 	if (!cphy) return NULL;
401d39a76e7Sxw 
402d39a76e7Sxw 	cphy_init(cphy, adapter, phy_addr, &mv88e1xxx_ops, mdio_ops);
403d39a76e7Sxw 
404d39a76e7Sxw 	/* Configure particular PHY's to run in a different mode. */
405d39a76e7Sxw 	if ((board_info(adapter)->caps & SUPPORTED_TP) &&
406d39a76e7Sxw 	    board_info(adapter)->chip_phy == CHBT_PHY_88E1111) {
407d39a76e7Sxw 		/*
408d39a76e7Sxw 		 * Configure the PHY transmitter as class A to reduce EMI.
409d39a76e7Sxw 		 */
410d39a76e7Sxw 		(void) simple_mdio_write(cphy, MV88E1XXX_EXTENDED_ADDR_REGISTER, 0xB);
411d39a76e7Sxw 		(void) simple_mdio_write(cphy, MV88E1XXX_EXTENDED_REGISTER, 0x8004);
412d39a76e7Sxw 	}
413d39a76e7Sxw 	(void) mv88e1xxx_downshift_set(cphy, 1);   /* Enable downshift */
414d39a76e7Sxw 
415d39a76e7Sxw 	/* LED */
416d39a76e7Sxw 	if (is_T2(adapter)) {
417d39a76e7Sxw 		(void) simple_mdio_write(cphy,
418d39a76e7Sxw 			MV88E1XXX_LED_CONTROL_REGISTER, 0x1);
419d39a76e7Sxw 	}
420d39a76e7Sxw 
421d39a76e7Sxw 	return cphy;
422d39a76e7Sxw }
423d39a76e7Sxw 
424d39a76e7Sxw /* ARGSUSED */
mv88e1xxx_phy_reset(adapter_t * adapter)425d39a76e7Sxw static int mv88e1xxx_phy_reset(adapter_t* adapter)
426d39a76e7Sxw {
427d39a76e7Sxw 	return 0;
428d39a76e7Sxw }
429d39a76e7Sxw 
430d39a76e7Sxw struct gphy t1_mv88e1xxx_ops = {
431d39a76e7Sxw 	mv88e1xxx_phy_create,
432d39a76e7Sxw 	mv88e1xxx_phy_reset
433d39a76e7Sxw };
434