xref: /freebsd/sys/dev/cxgb/common/cxgb_vsc8211.c (revision 10faa568707403e7f72d9e20467c2d06b5517e43)
1b6d90eb7SKip Macy /**************************************************************************
2b6d90eb7SKip Macy 
3b6d90eb7SKip Macy Copyright (c) 2007, Chelsio Inc.
4b6d90eb7SKip Macy All rights reserved.
5b6d90eb7SKip Macy 
6b6d90eb7SKip Macy Redistribution and use in source and binary forms, with or without
7b6d90eb7SKip Macy modification, are permitted provided that the following conditions are met:
8b6d90eb7SKip Macy 
9b6d90eb7SKip Macy  1. Redistributions of source code must retain the above copyright notice,
10b6d90eb7SKip Macy     this list of conditions and the following disclaimer.
11b6d90eb7SKip Macy 
1210faa568SKip Macy  2. Neither the name of the Chelsio Corporation nor the names of its
13b6d90eb7SKip Macy     contributors may be used to endorse or promote products derived from
14b6d90eb7SKip Macy     this software without specific prior written permission.
15b6d90eb7SKip Macy 
16b6d90eb7SKip Macy THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17b6d90eb7SKip Macy AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18b6d90eb7SKip Macy IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19b6d90eb7SKip Macy ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20b6d90eb7SKip Macy LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21b6d90eb7SKip Macy CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22b6d90eb7SKip Macy SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23b6d90eb7SKip Macy INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24b6d90eb7SKip Macy CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25b6d90eb7SKip Macy ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26b6d90eb7SKip Macy POSSIBILITY OF SUCH DAMAGE.
27b6d90eb7SKip Macy 
28b6d90eb7SKip Macy ***************************************************************************/
29b6d90eb7SKip Macy 
30b6d90eb7SKip Macy #include <sys/cdefs.h>
31b6d90eb7SKip Macy __FBSDID("$FreeBSD$");
32b6d90eb7SKip Macy 
3310faa568SKip Macy #ifdef CONFIG_DEFINED
3410faa568SKip Macy #include <cxgb_include.h>
3510faa568SKip Macy #else
3610faa568SKip Macy #include <dev/cxgb/cxgb_include.h>
3710faa568SKip Macy #endif
38b6d90eb7SKip Macy 
39b6d90eb7SKip Macy /* VSC8211 PHY specific registers. */
40b6d90eb7SKip Macy enum {
41b6d90eb7SKip Macy 	VSC8211_INTR_ENABLE   = 25,
42b6d90eb7SKip Macy 	VSC8211_INTR_STATUS   = 26,
43b6d90eb7SKip Macy 	VSC8211_AUX_CTRL_STAT = 28,
44b6d90eb7SKip Macy };
45b6d90eb7SKip Macy 
46b6d90eb7SKip Macy enum {
47b6d90eb7SKip Macy 	VSC_INTR_RX_ERR     = 1 << 0,
48b6d90eb7SKip Macy 	VSC_INTR_MS_ERR     = 1 << 1,  /* master/slave resolution error */
49b6d90eb7SKip Macy 	VSC_INTR_CABLE      = 1 << 2,  /* cable impairment */
50b6d90eb7SKip Macy 	VSC_INTR_FALSE_CARR = 1 << 3,  /* false carrier */
51b6d90eb7SKip Macy 	VSC_INTR_MEDIA_CHG  = 1 << 4,  /* AMS media change */
52b6d90eb7SKip Macy 	VSC_INTR_RX_FIFO    = 1 << 5,  /* Rx FIFO over/underflow */
53b6d90eb7SKip Macy 	VSC_INTR_TX_FIFO    = 1 << 6,  /* Tx FIFO over/underflow */
54b6d90eb7SKip Macy 	VSC_INTR_DESCRAMBL  = 1 << 7,  /* descrambler lock-lost */
55b6d90eb7SKip Macy 	VSC_INTR_SYMBOL_ERR = 1 << 8,  /* symbol error */
56b6d90eb7SKip Macy 	VSC_INTR_NEG_DONE   = 1 << 10, /* autoneg done */
57b6d90eb7SKip Macy 	VSC_INTR_NEG_ERR    = 1 << 11, /* autoneg error */
58b6d90eb7SKip Macy 	VSC_INTR_LINK_CHG   = 1 << 13, /* link change */
59b6d90eb7SKip Macy 	VSC_INTR_ENABLE     = 1 << 15, /* interrupt enable */
60b6d90eb7SKip Macy };
61b6d90eb7SKip Macy 
62b6d90eb7SKip Macy #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
63b6d90eb7SKip Macy 	 		   VSC_INTR_NEG_DONE)
64b6d90eb7SKip Macy #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
65b6d90eb7SKip Macy 		   VSC_INTR_ENABLE)
66b6d90eb7SKip Macy 
67b6d90eb7SKip Macy /* PHY specific auxiliary control & status register fields */
68b6d90eb7SKip Macy #define S_ACSR_ACTIPHY_TMR    0
69b6d90eb7SKip Macy #define M_ACSR_ACTIPHY_TMR    0x3
70b6d90eb7SKip Macy #define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
71b6d90eb7SKip Macy 
72b6d90eb7SKip Macy #define S_ACSR_SPEED    3
73b6d90eb7SKip Macy #define M_ACSR_SPEED    0x3
74b6d90eb7SKip Macy #define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
75b6d90eb7SKip Macy 
76b6d90eb7SKip Macy #define S_ACSR_DUPLEX 5
77b6d90eb7SKip Macy #define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
78b6d90eb7SKip Macy 
79b6d90eb7SKip Macy #define S_ACSR_ACTIPHY 6
80b6d90eb7SKip Macy #define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
81b6d90eb7SKip Macy 
82b6d90eb7SKip Macy /*
83b6d90eb7SKip Macy  * Reset the PHY.  This PHY completes reset immediately so we never wait.
84b6d90eb7SKip Macy  */
85b6d90eb7SKip Macy static int vsc8211_reset(struct cphy *cphy, int wait)
86b6d90eb7SKip Macy {
87b6d90eb7SKip Macy 	return t3_phy_reset(cphy, 0, 0);
88b6d90eb7SKip Macy }
89b6d90eb7SKip Macy 
90b6d90eb7SKip Macy static int vsc8211_intr_enable(struct cphy *cphy)
91b6d90eb7SKip Macy {
92b6d90eb7SKip Macy 	return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
93b6d90eb7SKip Macy }
94b6d90eb7SKip Macy 
95b6d90eb7SKip Macy static int vsc8211_intr_disable(struct cphy *cphy)
96b6d90eb7SKip Macy {
97b6d90eb7SKip Macy 	return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
98b6d90eb7SKip Macy }
99b6d90eb7SKip Macy 
100b6d90eb7SKip Macy static int vsc8211_intr_clear(struct cphy *cphy)
101b6d90eb7SKip Macy {
102b6d90eb7SKip Macy 	u32 val;
103b6d90eb7SKip Macy 
104b6d90eb7SKip Macy 	/* Clear PHY interrupts by reading the register. */
105b6d90eb7SKip Macy 	return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val);
106b6d90eb7SKip Macy }
107b6d90eb7SKip Macy 
108b6d90eb7SKip Macy static int vsc8211_autoneg_enable(struct cphy *cphy)
109b6d90eb7SKip Macy {
110b6d90eb7SKip Macy 	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
111b6d90eb7SKip Macy 				   BMCR_ANENABLE | BMCR_ANRESTART);
112b6d90eb7SKip Macy }
113b6d90eb7SKip Macy 
114b6d90eb7SKip Macy static int vsc8211_autoneg_restart(struct cphy *cphy)
115b6d90eb7SKip Macy {
116b6d90eb7SKip Macy 	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
117b6d90eb7SKip Macy 				   BMCR_ANRESTART);
118b6d90eb7SKip Macy }
119b6d90eb7SKip Macy 
120b6d90eb7SKip Macy static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok,
121b6d90eb7SKip Macy 				     int *speed, int *duplex, int *fc)
122b6d90eb7SKip Macy {
123b6d90eb7SKip Macy 	unsigned int bmcr, status, lpa, adv;
124b6d90eb7SKip Macy 	int err, sp = -1, dplx = -1, pause = 0;
125b6d90eb7SKip Macy 
126b6d90eb7SKip Macy 	err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
127b6d90eb7SKip Macy 	if (!err)
128b6d90eb7SKip Macy 		err = mdio_read(cphy, 0, MII_BMSR, &status);
129b6d90eb7SKip Macy 	if (err)
130b6d90eb7SKip Macy 		return err;
131b6d90eb7SKip Macy 
132b6d90eb7SKip Macy 	if (link_ok) {
133b6d90eb7SKip Macy 		/*
134b6d90eb7SKip Macy 		 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
135b6d90eb7SKip Macy 		 * once more to get the current link state.
136b6d90eb7SKip Macy 		 */
137b6d90eb7SKip Macy 		if (!(status & BMSR_LSTATUS))
138b6d90eb7SKip Macy 			err = mdio_read(cphy, 0, MII_BMSR, &status);
139b6d90eb7SKip Macy 		if (err)
140b6d90eb7SKip Macy 			return err;
141b6d90eb7SKip Macy 		*link_ok = (status & BMSR_LSTATUS) != 0;
142b6d90eb7SKip Macy 	}
143b6d90eb7SKip Macy 	if (!(bmcr & BMCR_ANENABLE)) {
144b6d90eb7SKip Macy 		dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
145b6d90eb7SKip Macy 		if (bmcr & BMCR_SPEED1000)
146b6d90eb7SKip Macy 			sp = SPEED_1000;
147b6d90eb7SKip Macy 		else if (bmcr & BMCR_SPEED100)
148b6d90eb7SKip Macy 			sp = SPEED_100;
149b6d90eb7SKip Macy 		else
150b6d90eb7SKip Macy 			sp = SPEED_10;
151b6d90eb7SKip Macy 	} else if (status & BMSR_ANEGCOMPLETE) {
152b6d90eb7SKip Macy 		err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status);
153b6d90eb7SKip Macy 		if (err)
154b6d90eb7SKip Macy 			return err;
155b6d90eb7SKip Macy 
156b6d90eb7SKip Macy 		dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
157b6d90eb7SKip Macy 		sp = G_ACSR_SPEED(status);
158b6d90eb7SKip Macy 		if (sp == 0)
159b6d90eb7SKip Macy 			sp = SPEED_10;
160b6d90eb7SKip Macy 		else if (sp == 1)
161b6d90eb7SKip Macy 			sp = SPEED_100;
162b6d90eb7SKip Macy 		else
163b6d90eb7SKip Macy 			sp = SPEED_1000;
164b6d90eb7SKip Macy 
165b6d90eb7SKip Macy 		if (fc && dplx == DUPLEX_FULL) {
166b6d90eb7SKip Macy 			err = mdio_read(cphy, 0, MII_LPA, &lpa);
167b6d90eb7SKip Macy 			if (!err)
168b6d90eb7SKip Macy 				err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
169b6d90eb7SKip Macy 			if (err)
170b6d90eb7SKip Macy 				return err;
171b6d90eb7SKip Macy 
172b6d90eb7SKip Macy 			if (lpa & adv & ADVERTISE_PAUSE_CAP)
173b6d90eb7SKip Macy 				pause = PAUSE_RX | PAUSE_TX;
174b6d90eb7SKip Macy 			else if ((lpa & ADVERTISE_PAUSE_CAP) &&
175b6d90eb7SKip Macy 				 (lpa & ADVERTISE_PAUSE_ASYM) &&
176b6d90eb7SKip Macy 				 (adv & ADVERTISE_PAUSE_ASYM))
177b6d90eb7SKip Macy 				pause = PAUSE_TX;
178b6d90eb7SKip Macy 			else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
179b6d90eb7SKip Macy 				 (adv & ADVERTISE_PAUSE_CAP))
180b6d90eb7SKip Macy 				pause = PAUSE_RX;
181b6d90eb7SKip Macy 		}
182b6d90eb7SKip Macy 	}
183b6d90eb7SKip Macy 	if (speed)
184b6d90eb7SKip Macy 		*speed = sp;
185b6d90eb7SKip Macy 	if (duplex)
186b6d90eb7SKip Macy 		*duplex = dplx;
187b6d90eb7SKip Macy 	if (fc)
188b6d90eb7SKip Macy 		*fc = pause;
189b6d90eb7SKip Macy 	return 0;
190b6d90eb7SKip Macy }
191b6d90eb7SKip Macy 
192b6d90eb7SKip Macy static int vsc8211_power_down(struct cphy *cphy, int enable)
193b6d90eb7SKip Macy {
194b6d90eb7SKip Macy 	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
195b6d90eb7SKip Macy 				   enable ? BMCR_PDOWN : 0);
196b6d90eb7SKip Macy }
197b6d90eb7SKip Macy 
198b6d90eb7SKip Macy static int vsc8211_intr_handler(struct cphy *cphy)
199b6d90eb7SKip Macy {
200b6d90eb7SKip Macy 	unsigned int cause;
201b6d90eb7SKip Macy 	int err, cphy_cause = 0;
202b6d90eb7SKip Macy 
203b6d90eb7SKip Macy 	err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause);
204b6d90eb7SKip Macy 	if (err)
205b6d90eb7SKip Macy 		return err;
206b6d90eb7SKip Macy 
207b6d90eb7SKip Macy 	cause &= INTR_MASK;
208b6d90eb7SKip Macy 	if (cause & CFG_CHG_INTR_MASK)
209b6d90eb7SKip Macy 		cphy_cause |= cphy_cause_link_change;
210b6d90eb7SKip Macy 	if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
211b6d90eb7SKip Macy 		cphy_cause |= cphy_cause_fifo_error;
212b6d90eb7SKip Macy 	return cphy_cause;
213b6d90eb7SKip Macy }
214b6d90eb7SKip Macy 
215b6d90eb7SKip Macy #ifdef C99_NOT_SUPPORTED
216b6d90eb7SKip Macy static struct cphy_ops vsc8211_ops = {
217b6d90eb7SKip Macy 	NULL,
218b6d90eb7SKip Macy 	vsc8211_reset,
219b6d90eb7SKip Macy 	vsc8211_intr_enable,
220b6d90eb7SKip Macy 	vsc8211_intr_disable,
221b6d90eb7SKip Macy 	vsc8211_intr_clear,
222b6d90eb7SKip Macy 	vsc8211_intr_handler,
223b6d90eb7SKip Macy 	vsc8211_autoneg_enable,
224b6d90eb7SKip Macy 	vsc8211_autoneg_restart,
225b6d90eb7SKip Macy 	t3_phy_advertise,
226b6d90eb7SKip Macy 	NULL,
227b6d90eb7SKip Macy 	t3_set_phy_speed_duplex,
228b6d90eb7SKip Macy 	vsc8211_get_link_status,
229b6d90eb7SKip Macy 	vsc8211_power_down,
230b6d90eb7SKip Macy };
231b6d90eb7SKip Macy #else
232b6d90eb7SKip Macy static struct cphy_ops vsc8211_ops = {
233b6d90eb7SKip Macy 	.reset             = vsc8211_reset,
234b6d90eb7SKip Macy 	.intr_enable       = vsc8211_intr_enable,
235b6d90eb7SKip Macy 	.intr_disable      = vsc8211_intr_disable,
236b6d90eb7SKip Macy 	.intr_clear        = vsc8211_intr_clear,
237b6d90eb7SKip Macy 	.intr_handler      = vsc8211_intr_handler,
238b6d90eb7SKip Macy 	.autoneg_enable    = vsc8211_autoneg_enable,
239b6d90eb7SKip Macy 	.autoneg_restart   = vsc8211_autoneg_restart,
240b6d90eb7SKip Macy 	.advertise         = t3_phy_advertise,
241b6d90eb7SKip Macy 	.set_speed_duplex  = t3_set_phy_speed_duplex,
242b6d90eb7SKip Macy 	.get_link_status   = vsc8211_get_link_status,
243b6d90eb7SKip Macy 	.power_down        = vsc8211_power_down,
244b6d90eb7SKip Macy };
245b6d90eb7SKip Macy #endif
246b6d90eb7SKip Macy 
247b6d90eb7SKip Macy void t3_vsc8211_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
248b6d90eb7SKip Macy 			 const struct mdio_ops *mdio_ops)
249b6d90eb7SKip Macy {
250b6d90eb7SKip Macy 	cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops);
251b6d90eb7SKip Macy }
252