Add support for the latest 1G/10G Chelsio adapter, T3.
[safe/jmp/linux-2.6] / drivers / net / cxgb3 / ael1002.c
1 /*
2  * This file is part of the Chelsio T3 Ethernet driver.
3  *
4  * Copyright (C) 2005-2006 Chelsio Communications.  All rights reserved.
5  *
6  * This program is distributed in the hope that it will be useful, but WITHOUT
7  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
8  * FITNESS FOR A PARTICULAR PURPOSE.  See the LICENSE file included in this
9  * release for licensing terms and conditions.
10  */
11
12 #include "common.h"
13 #include "regs.h"
14
15 enum {
16         AEL100X_TX_DISABLE = 9,
17         AEL100X_TX_CONFIG1 = 0xc002,
18         AEL1002_PWR_DOWN_HI = 0xc011,
19         AEL1002_PWR_DOWN_LO = 0xc012,
20         AEL1002_XFI_EQL = 0xc015,
21         AEL1002_LB_EN = 0xc017,
22
23         LASI_CTRL = 0x9002,
24         LASI_STAT = 0x9005
25 };
26
27 static void ael100x_txon(struct cphy *phy)
28 {
29         int tx_on_gpio = phy->addr == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL;
30
31         msleep(100);
32         t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio);
33         msleep(30);
34 }
35
36 static int ael1002_power_down(struct cphy *phy, int enable)
37 {
38         int err;
39
40         err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_DISABLE, !!enable);
41         if (!err)
42                 err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
43                                           BMCR_PDOWN, enable ? BMCR_PDOWN : 0);
44         return err;
45 }
46
47 static int ael1002_reset(struct cphy *phy, int wait)
48 {
49         int err;
50
51         if ((err = ael1002_power_down(phy, 0)) ||
52             (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_CONFIG1, 1)) ||
53             (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_HI, 0)) ||
54             (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_LO, 0)) ||
55             (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_XFI_EQL, 0x18)) ||
56             (err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, AEL1002_LB_EN,
57                                        0, 1 << 5)))
58                 return err;
59         return 0;
60 }
61
62 static int ael1002_intr_noop(struct cphy *phy)
63 {
64         return 0;
65 }
66
67 static int ael100x_get_link_status(struct cphy *phy, int *link_ok,
68                                    int *speed, int *duplex, int *fc)
69 {
70         if (link_ok) {
71                 unsigned int status;
72                 int err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &status);
73
74                 /*
75                  * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
76                  * once more to get the current link state.
77                  */
78                 if (!err && !(status & BMSR_LSTATUS))
79                         err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR,
80                                         &status);
81                 if (err)
82                         return err;
83                 *link_ok = !!(status & BMSR_LSTATUS);
84         }
85         if (speed)
86                 *speed = SPEED_10000;
87         if (duplex)
88                 *duplex = DUPLEX_FULL;
89         return 0;
90 }
91
92 static struct cphy_ops ael1002_ops = {
93         .reset = ael1002_reset,
94         .intr_enable = ael1002_intr_noop,
95         .intr_disable = ael1002_intr_noop,
96         .intr_clear = ael1002_intr_noop,
97         .intr_handler = ael1002_intr_noop,
98         .get_link_status = ael100x_get_link_status,
99         .power_down = ael1002_power_down,
100 };
101
102 void t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter,
103                          int phy_addr, const struct mdio_ops *mdio_ops)
104 {
105         cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops);
106         ael100x_txon(phy);
107 }
108
109 static int ael1006_reset(struct cphy *phy, int wait)
110 {
111         return t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait);
112 }
113
114 static int ael1006_intr_enable(struct cphy *phy)
115 {
116         return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 1);
117 }
118
119 static int ael1006_intr_disable(struct cphy *phy)
120 {
121         return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 0);
122 }
123
124 static int ael1006_intr_clear(struct cphy *phy)
125 {
126         u32 val;
127
128         return mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &val);
129 }
130
131 static int ael1006_intr_handler(struct cphy *phy)
132 {
133         unsigned int status;
134         int err = mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &status);
135
136         if (err)
137                 return err;
138         return (status & 1) ? cphy_cause_link_change : 0;
139 }
140
141 static int ael1006_power_down(struct cphy *phy, int enable)
142 {
143         return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
144                                    BMCR_PDOWN, enable ? BMCR_PDOWN : 0);
145 }
146
147 static struct cphy_ops ael1006_ops = {
148         .reset = ael1006_reset,
149         .intr_enable = ael1006_intr_enable,
150         .intr_disable = ael1006_intr_disable,
151         .intr_clear = ael1006_intr_clear,
152         .intr_handler = ael1006_intr_handler,
153         .get_link_status = ael100x_get_link_status,
154         .power_down = ael1006_power_down,
155 };
156
157 void t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter,
158                          int phy_addr, const struct mdio_ops *mdio_ops)
159 {
160         cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops);
161         ael100x_txon(phy);
162 }
163
164 static struct cphy_ops qt2045_ops = {
165         .reset = ael1006_reset,
166         .intr_enable = ael1006_intr_enable,
167         .intr_disable = ael1006_intr_disable,
168         .intr_clear = ael1006_intr_clear,
169         .intr_handler = ael1006_intr_handler,
170         .get_link_status = ael100x_get_link_status,
171         .power_down = ael1006_power_down,
172 };
173
174 void t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter,
175                         int phy_addr, const struct mdio_ops *mdio_ops)
176 {
177         unsigned int stat;
178
179         cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops);
180
181         /*
182          * Some cards where the PHY is supposed to be at address 0 actually
183          * have it at 1.
184          */
185         if (!phy_addr && !mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &stat) &&
186             stat == 0xffff)
187                 phy->addr = 1;
188 }
189
190 static int xaui_direct_reset(struct cphy *phy, int wait)
191 {
192         return 0;
193 }
194
195 static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok,
196                                        int *speed, int *duplex, int *fc)
197 {
198         if (link_ok) {
199                 unsigned int status;
200
201                 status = t3_read_reg(phy->adapter,
202                                      XGM_REG(A_XGM_SERDES_STAT0, phy->addr));
203                 *link_ok = !(status & F_LOWSIG0);
204         }
205         if (speed)
206                 *speed = SPEED_10000;
207         if (duplex)
208                 *duplex = DUPLEX_FULL;
209         return 0;
210 }
211
212 static int xaui_direct_power_down(struct cphy *phy, int enable)
213 {
214         return 0;
215 }
216
217 static struct cphy_ops xaui_direct_ops = {
218         .reset = xaui_direct_reset,
219         .intr_enable = ael1002_intr_noop,
220         .intr_disable = ael1002_intr_noop,
221         .intr_clear = ael1002_intr_noop,
222         .intr_handler = ael1002_intr_noop,
223         .get_link_status = xaui_direct_get_link_status,
224         .power_down = xaui_direct_power_down,
225 };
226
227 void t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter,
228                              int phy_addr, const struct mdio_ops *mdio_ops)
229 {
230         cphy_init(phy, adapter, 1, &xaui_direct_ops, mdio_ops);
231 }