1180586Skmacy/**************************************************************************
2180586Skmacy
3180586SkmacyCopyright (c) 2008, Chelsio Inc.
4180586SkmacyAll rights reserved.
5180586Skmacy
6180586SkmacyRedistribution and use in source and binary forms, with or without
7180586Skmacymodification, are permitted provided that the following conditions are met:
8180586Skmacy
9180586Skmacy 1. Redistributions of source code must retain the above copyright notice,
10180586Skmacy    this list of conditions and the following disclaimer.
11180586Skmacy
12180586Skmacy 2. Neither the name of the Chelsio Corporation nor the names of its
13180586Skmacy    contributors may be used to endorse or promote products derived from
14180586Skmacy    this software without specific prior written permission.
15180586Skmacy
16180586SkmacyTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17180586SkmacyAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18180586SkmacyIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19180586SkmacyARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20180586SkmacyLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21180586SkmacyCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22180586SkmacySUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23180586SkmacyINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24180586SkmacyCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25180586SkmacyARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26180586SkmacyPOSSIBILITY OF SUCH DAMAGE.
27180586Skmacy
28180586Skmacy***************************************************************************/
29180586Skmacy
30180586Skmacy#include <sys/cdefs.h>
31180586Skmacy__FBSDID("$FreeBSD$");
32180586Skmacy
33180586Skmacy#include <cxgb_include.h>
34180586Skmacy
35180586Skmacy#undef msleep
36180586Skmacy#define msleep t3_os_sleep
37180586Skmacy
38180586Skmacy/* TN1010 PHY specific registers. */
39180586Skmacyenum {
40180586Skmacy	TN1010_VEND1_STAT = 1,
41180586Skmacy};
42180586Skmacy
43180586Skmacy/* IEEE auto-negotiation 10GBASE-T registers */
44180586Skmacyenum {
45180586Skmacy	ANEG_ADVER    = 16,
46180586Skmacy	ANEG_LPA      = 19,
47180586Skmacy	ANEG_10G_CTRL = 32,
48180586Skmacy	ANEG_10G_STAT = 33
49180586Skmacy};
50180586Skmacy
51180586Skmacy#define ADVERTISE_ENPAGE      (1 << 12)
52180586Skmacy#define ADVERTISE_10000FULL   (1 << 12)
53180586Skmacy#define ADVERTISE_LOOP_TIMING (1 << 0)
54180586Skmacy
55180586Skmacy/* vendor specific status register fields */
56180586Skmacy#define F_XS_LANE_ALIGN_STAT (1 << 0)
57180586Skmacy#define F_PCS_BLK_LOCK       (1 << 1)
58180586Skmacy#define F_PMD_SIGNAL_OK      (1 << 2)
59180586Skmacy#define F_LINK_STAT          (1 << 3)
60180586Skmacy#define F_ANEG_SPEED_1G      (1 << 4)
61180586Skmacy#define F_ANEG_MASTER        (1 << 5)
62180586Skmacy
63180586Skmacy#define S_ANEG_STAT    6
64180586Skmacy#define M_ANEG_STAT    0x3
65180586Skmacy#define G_ANEG_STAT(x) (((x) >> S_ANEG_STAT) & M_ANEG_STAT)
66180586Skmacy
67180586Skmacyenum {                        /* autonegotiation status */
68180586Skmacy	ANEG_IN_PROGR = 0,
69180586Skmacy	ANEG_COMPLETE = 1,
70180586Skmacy	ANEG_FAILED   = 3
71180586Skmacy};
72180586Skmacy
73180586Skmacy/*
74180586Skmacy * Reset the PHY.  May take up to 500ms to complete.
75180586Skmacy */
76180586Skmacystatic int tn1010_reset(struct cphy *phy, int wait)
77180586Skmacy{
78180586Skmacy	int err = t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait);
79180586Skmacy	msleep(500);
80180586Skmacy	return err;
81180586Skmacy}
82180586Skmacy
83180586Skmacystatic int tn1010_power_down(struct cphy *phy, int enable)
84180586Skmacy{
85180586Skmacy	return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
86180586Skmacy				   BMCR_PDOWN, enable ? BMCR_PDOWN : 0);
87180586Skmacy}
88180586Skmacy
89180586Skmacystatic int tn1010_autoneg_enable(struct cphy *phy)
90180586Skmacy{
91180586Skmacy	int err;
92180586Skmacy
93180586Skmacy	err = tn1010_power_down(phy, 0);
94180586Skmacy	if (!err)
95180586Skmacy		err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, MII_BMCR, 0,
96180586Skmacy					  BMCR_ANENABLE | BMCR_ANRESTART);
97180586Skmacy	return err;
98180586Skmacy}
99180586Skmacy
100180586Skmacystatic int tn1010_autoneg_restart(struct cphy *phy)
101180586Skmacy{
102180586Skmacy	int err;
103180586Skmacy
104180586Skmacy	err = tn1010_power_down(phy, 0);
105180586Skmacy	if (!err)
106180586Skmacy		err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, MII_BMCR, 0,
107180586Skmacy					  BMCR_ANRESTART);
108180586Skmacy	return err;
109180586Skmacy}
110180586Skmacy
111180586Skmacystatic int tn1010_advertise(struct cphy *phy, unsigned int advert)
112180586Skmacy{
113180586Skmacy	int err, val;
114180586Skmacy
115180586Skmacy	if (!(advert & ADVERTISED_1000baseT_Full))
116180586Skmacy		return -EINVAL;               /* PHY can't disable 1000BASE-T */
117180586Skmacy
118180586Skmacy	val = ADVERTISE_CSMA | ADVERTISE_ENPAGE | ADVERTISE_NPAGE;
119180586Skmacy	if (advert & ADVERTISED_Pause)
120180586Skmacy		val |= ADVERTISE_PAUSE_CAP;
121180586Skmacy	if (advert & ADVERTISED_Asym_Pause)
122180586Skmacy		val |= ADVERTISE_PAUSE_ASYM;
123180586Skmacy	err = mdio_write(phy, MDIO_DEV_ANEG, ANEG_ADVER, val);
124180586Skmacy	if (err)
125180586Skmacy		return err;
126180586Skmacy
127180586Skmacy	val = (advert & ADVERTISED_10000baseT_Full) ? ADVERTISE_10000FULL : 0;
128180586Skmacy	return mdio_write(phy, MDIO_DEV_ANEG, ANEG_10G_CTRL, val |
129180586Skmacy			  ADVERTISE_LOOP_TIMING);
130180586Skmacy}
131180586Skmacy
132276959Snpstatic int tn1010_get_link_status(struct cphy *phy, int *link_state,
133180586Skmacy				  int *speed, int *duplex, int *fc)
134180586Skmacy{
135180586Skmacy	unsigned int status, lpa, adv;
136180586Skmacy	int err, sp = -1, pause = 0;
137180586Skmacy
138180586Skmacy	err = mdio_read(phy, MDIO_DEV_VEND1, TN1010_VEND1_STAT, &status);
139180586Skmacy	if (err)
140180586Skmacy		return err;
141180586Skmacy
142276959Snp	if (link_state)
143276959Snp		*link_state = status & F_LINK_STAT ? PHY_LINK_UP :
144276959Snp		    PHY_LINK_DOWN;
145180586Skmacy
146180586Skmacy	if (G_ANEG_STAT(status) == ANEG_COMPLETE) {
147180586Skmacy		sp = (status & F_ANEG_SPEED_1G) ? SPEED_1000 : SPEED_10000;
148180586Skmacy
149180586Skmacy		if (fc) {
150180586Skmacy			err = mdio_read(phy, MDIO_DEV_ANEG, ANEG_LPA, &lpa);
151180586Skmacy			if (!err)
152180586Skmacy				err = mdio_read(phy, MDIO_DEV_ANEG, ANEG_ADVER,
153180586Skmacy						&adv);
154180586Skmacy			if (err)
155180586Skmacy				return err;
156180586Skmacy
157180586Skmacy			if (lpa & adv & ADVERTISE_PAUSE_CAP)
158180586Skmacy				pause = PAUSE_RX | PAUSE_TX;
159180586Skmacy			else if ((lpa & ADVERTISE_PAUSE_CAP) &&
160180586Skmacy				 (lpa & ADVERTISE_PAUSE_ASYM) &&
161180586Skmacy				 (adv & ADVERTISE_PAUSE_ASYM))
162180586Skmacy				pause = PAUSE_TX;
163180586Skmacy			else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
164180586Skmacy				 (adv & ADVERTISE_PAUSE_CAP))
165180586Skmacy				pause = PAUSE_RX;
166180586Skmacy		}
167180586Skmacy	}
168180586Skmacy	if (speed)
169180586Skmacy		*speed = sp;
170180586Skmacy	if (duplex)
171180586Skmacy		*duplex = DUPLEX_FULL;
172180586Skmacy	if (fc)
173180586Skmacy		*fc = pause;
174180586Skmacy	return 0;
175180586Skmacy}
176180586Skmacy
177180586Skmacystatic int tn1010_set_speed_duplex(struct cphy *phy, int speed, int duplex)
178180586Skmacy{
179180586Skmacy	return -EINVAL;    /* require autoneg */
180180586Skmacy}
181180586Skmacy
182180586Skmacy#ifdef C99_NOT_SUPPORTED
183180586Skmacystatic struct cphy_ops tn1010_ops = {
184180586Skmacy	tn1010_reset,
185180586Skmacy	t3_phy_lasi_intr_enable,
186180586Skmacy	t3_phy_lasi_intr_disable,
187180586Skmacy	t3_phy_lasi_intr_clear,
188180586Skmacy	t3_phy_lasi_intr_handler,
189180586Skmacy	tn1010_autoneg_enable,
190180586Skmacy	tn1010_autoneg_restart,
191180586Skmacy	tn1010_advertise,
192180586Skmacy	NULL,
193180586Skmacy	tn1010_set_speed_duplex,
194180586Skmacy	tn1010_get_link_status,
195180586Skmacy	tn1010_power_down,
196180586Skmacy};
197180586Skmacy#else
198180586Skmacystatic struct cphy_ops tn1010_ops = {
199180586Skmacy	.reset             = tn1010_reset,
200180586Skmacy	.intr_enable       = t3_phy_lasi_intr_enable,
201180586Skmacy	.intr_disable      = t3_phy_lasi_intr_disable,
202180586Skmacy	.intr_clear        = t3_phy_lasi_intr_clear,
203180586Skmacy	.intr_handler      = t3_phy_lasi_intr_handler,
204180586Skmacy	.autoneg_enable    = tn1010_autoneg_enable,
205180586Skmacy	.autoneg_restart   = tn1010_autoneg_restart,
206180586Skmacy	.advertise         = tn1010_advertise,
207180586Skmacy	.set_speed_duplex  = tn1010_set_speed_duplex,
208180586Skmacy	.get_link_status   = tn1010_get_link_status,
209180586Skmacy	.power_down        = tn1010_power_down,
210180586Skmacy};
211180586Skmacy#endif
212180586Skmacy
213197791Snpint t3_tn1010_phy_prep(pinfo_t *pinfo, int phy_addr,
214180586Skmacy		       const struct mdio_ops *mdio_ops)
215180586Skmacy{
216197791Snp	cphy_init(&pinfo->phy, pinfo->adapter, pinfo, phy_addr, &tn1010_ops, mdio_ops,
217180586Skmacy		  SUPPORTED_1000baseT_Full | SUPPORTED_10000baseT_Full |
218180586Skmacy		  SUPPORTED_Autoneg | SUPPORTED_AUI | SUPPORTED_TP,
219180586Skmacy		  "1000/10GBASE-T");
220180586Skmacy	msleep(500);    /* PHY needs up to 500ms to start responding to MDIO */
221180586Skmacy	return 0;
222180586Skmacy}
223