]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/dev/cxgb/common/cxgb_tn1010.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / dev / cxgb / common / cxgb_tn1010.c
1 /**************************************************************************
2
3 Copyright (c) 2008, Chelsio Inc.
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8
9  1. Redistributions of source code must retain the above copyright notice,
10     this list of conditions and the following disclaimer.
11
12  2. Neither the name of the Chelsio Corporation nor the names of its
13     contributors may be used to endorse or promote products derived from
14     this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 POSSIBILITY OF SUCH DAMAGE.
27
28 ***************************************************************************/
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #ifdef CONFIG_DEFINED
34 #include <cxgb_include.h>
35 #else
36 #include <dev/cxgb/cxgb_include.h>
37 #endif
38
39 #undef msleep
40 #define msleep t3_os_sleep
41
42 /* TN1010 PHY specific registers. */
43 enum {
44         TN1010_VEND1_STAT = 1,
45 };
46
47 /* IEEE auto-negotiation 10GBASE-T registers */
48 enum {
49         ANEG_ADVER    = 16,
50         ANEG_LPA      = 19,
51         ANEG_10G_CTRL = 32,
52         ANEG_10G_STAT = 33
53 };
54
55 #define ADVERTISE_ENPAGE      (1 << 12)
56 #define ADVERTISE_10000FULL   (1 << 12)
57 #define ADVERTISE_LOOP_TIMING (1 << 0)
58
59 /* vendor specific status register fields */
60 #define F_XS_LANE_ALIGN_STAT (1 << 0)
61 #define F_PCS_BLK_LOCK       (1 << 1)
62 #define F_PMD_SIGNAL_OK      (1 << 2)
63 #define F_LINK_STAT          (1 << 3)
64 #define F_ANEG_SPEED_1G      (1 << 4)
65 #define F_ANEG_MASTER        (1 << 5)
66
67 #define S_ANEG_STAT    6
68 #define M_ANEG_STAT    0x3
69 #define G_ANEG_STAT(x) (((x) >> S_ANEG_STAT) & M_ANEG_STAT)
70
71 enum {                        /* autonegotiation status */
72         ANEG_IN_PROGR = 0,
73         ANEG_COMPLETE = 1,
74         ANEG_FAILED   = 3
75 };
76
77 /*
78  * Reset the PHY.  May take up to 500ms to complete.
79  */
80 static int tn1010_reset(struct cphy *phy, int wait)
81 {
82         int err = t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait);
83         msleep(500);
84         return err;
85 }
86
87 static int tn1010_power_down(struct cphy *phy, int enable)
88 {
89         return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
90                                    BMCR_PDOWN, enable ? BMCR_PDOWN : 0);
91 }
92
93 static int tn1010_autoneg_enable(struct cphy *phy)
94 {
95         int err;
96
97         err = tn1010_power_down(phy, 0);
98         if (!err)
99                 err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, MII_BMCR, 0,
100                                           BMCR_ANENABLE | BMCR_ANRESTART);
101         return err;
102 }
103
104 static int tn1010_autoneg_restart(struct cphy *phy)
105 {
106         int err;
107
108         err = tn1010_power_down(phy, 0);
109         if (!err)
110                 err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, MII_BMCR, 0,
111                                           BMCR_ANRESTART);
112         return err;
113 }
114
115 static int tn1010_advertise(struct cphy *phy, unsigned int advert)
116 {
117         int err, val;
118
119         if (!(advert & ADVERTISED_1000baseT_Full))
120                 return -EINVAL;               /* PHY can't disable 1000BASE-T */
121
122         val = ADVERTISE_CSMA | ADVERTISE_ENPAGE | ADVERTISE_NPAGE;
123         if (advert & ADVERTISED_Pause)
124                 val |= ADVERTISE_PAUSE_CAP;
125         if (advert & ADVERTISED_Asym_Pause)
126                 val |= ADVERTISE_PAUSE_ASYM;
127         err = mdio_write(phy, MDIO_DEV_ANEG, ANEG_ADVER, val);
128         if (err)
129                 return err;
130
131         val = (advert & ADVERTISED_10000baseT_Full) ? ADVERTISE_10000FULL : 0;
132         return mdio_write(phy, MDIO_DEV_ANEG, ANEG_10G_CTRL, val |
133                           ADVERTISE_LOOP_TIMING);
134 }
135
136 static int tn1010_get_link_status(struct cphy *phy, int *link_ok,
137                                   int *speed, int *duplex, int *fc)
138 {
139         unsigned int status, lpa, adv;
140         int err, sp = -1, pause = 0;
141
142         err = mdio_read(phy, MDIO_DEV_VEND1, TN1010_VEND1_STAT, &status);
143         if (err)
144                 return err;
145
146         if (link_ok)
147                 *link_ok = (status & F_LINK_STAT) != 0;
148
149         if (G_ANEG_STAT(status) == ANEG_COMPLETE) {
150                 sp = (status & F_ANEG_SPEED_1G) ? SPEED_1000 : SPEED_10000;
151
152                 if (fc) {
153                         err = mdio_read(phy, MDIO_DEV_ANEG, ANEG_LPA, &lpa);
154                         if (!err)
155                                 err = mdio_read(phy, MDIO_DEV_ANEG, ANEG_ADVER,
156                                                 &adv);
157                         if (err)
158                                 return err;
159
160                         if (lpa & adv & ADVERTISE_PAUSE_CAP)
161                                 pause = PAUSE_RX | PAUSE_TX;
162                         else if ((lpa & ADVERTISE_PAUSE_CAP) &&
163                                  (lpa & ADVERTISE_PAUSE_ASYM) &&
164                                  (adv & ADVERTISE_PAUSE_ASYM))
165                                 pause = PAUSE_TX;
166                         else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
167                                  (adv & ADVERTISE_PAUSE_CAP))
168                                 pause = PAUSE_RX;
169                 }
170         }
171         if (speed)
172                 *speed = sp;
173         if (duplex)
174                 *duplex = DUPLEX_FULL;
175         if (fc)
176                 *fc = pause;
177         return 0;
178 }
179
180 static int tn1010_set_speed_duplex(struct cphy *phy, int speed, int duplex)
181 {
182         return -EINVAL;    /* require autoneg */
183 }
184
185 #ifdef C99_NOT_SUPPORTED
186 static struct cphy_ops tn1010_ops = {
187         tn1010_reset,
188         t3_phy_lasi_intr_enable,
189         t3_phy_lasi_intr_disable,
190         t3_phy_lasi_intr_clear,
191         t3_phy_lasi_intr_handler,
192         tn1010_autoneg_enable,
193         tn1010_autoneg_restart,
194         tn1010_advertise,
195         NULL,
196         tn1010_set_speed_duplex,
197         tn1010_get_link_status,
198         tn1010_power_down,
199 };
200 #else
201 static struct cphy_ops tn1010_ops = {
202         .reset             = tn1010_reset,
203         .intr_enable       = t3_phy_lasi_intr_enable,
204         .intr_disable      = t3_phy_lasi_intr_disable,
205         .intr_clear        = t3_phy_lasi_intr_clear,
206         .intr_handler      = t3_phy_lasi_intr_handler,
207         .autoneg_enable    = tn1010_autoneg_enable,
208         .autoneg_restart   = tn1010_autoneg_restart,
209         .advertise         = tn1010_advertise,
210         .set_speed_duplex  = tn1010_set_speed_duplex,
211         .get_link_status   = tn1010_get_link_status,
212         .power_down        = tn1010_power_down,
213 };
214 #endif
215
216 int t3_tn1010_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
217                        const struct mdio_ops *mdio_ops)
218 {
219         cphy_init(phy, adapter, phy_addr, &tn1010_ops, mdio_ops,
220                   SUPPORTED_1000baseT_Full | SUPPORTED_10000baseT_Full |
221                   SUPPORTED_Autoneg | SUPPORTED_AUI | SUPPORTED_TP,
222                   "1000/10GBASE-T");
223         msleep(500);    /* PHY needs up to 500ms to start responding to MDIO */
224         return 0;
225 }