1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (C) 2003-2005 Chelsio Communications. All rights reserved.
24 */
25
26 #include "cphy.h"
27 #include "elmer0.h"
28
29 /*
30 * The 88x2010 Rev C. requires some link status registers * to be read
31 * twice in order to get the right values. Future * revisions will fix
32 * this problem and then this macro * can disappear.
33 */
34 #define MV88x2010_LINK_STATUS_BUGS 1
35
led_init(struct cphy * cphy)36 static int led_init(struct cphy *cphy)
37 {
38 /* Setup the LED registers so we can turn on/off.
39 * Writing these bits maps control to another
40 * register. mmd(0x1) addr(0x7)
41 */
42 (void) mdio_write(cphy, 0x3, 0x8304, 0xdddd);
43 return 0;
44 }
45
led_link(struct cphy * cphy,u32 do_enable)46 static int led_link(struct cphy *cphy, u32 do_enable)
47 {
48 u32 led = 0;
49 #define LINK_ENABLE_BIT 0x1
50
51 (void) mdio_read(cphy, 0x1, 0x7, &led);
52
53 if (do_enable & LINK_ENABLE_BIT) {
54 led |= LINK_ENABLE_BIT;
55 (void) mdio_write(cphy, 0x1, 0x7, led);
56 } else {
57 led &= ~LINK_ENABLE_BIT;
58 (void) mdio_write(cphy, 0x1, 0x7, led);
59 }
60 return 0;
61 }
62
63 /* Port Reset */
64 /* ARGSUSED */
mv88x201x_reset(struct cphy * cphy,int wait)65 static int mv88x201x_reset(struct cphy *cphy, int wait)
66 {
67 /* This can be done through registers. It is not required since
68 * a full chip reset is used.
69 */
70 return 0;
71 }
72
mv88x201x_interrupt_enable(struct cphy * cphy)73 static int mv88x201x_interrupt_enable(struct cphy *cphy)
74 {
75 /* Enable PHY LASI interrupts. */
76 (void) mdio_write(cphy, 0x1, 0x9002, 0x1);
77
78 /* Enable Marvell interrupts through Elmer0. */
79 if (t1_is_asic(cphy->adapter)) {
80 u32 elmer;
81
82 (void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
83 elmer |= ELMER0_GP_BIT6;
84 (void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
85 }
86 return 0;
87 }
88
mv88x201x_interrupt_disable(struct cphy * cphy)89 static int mv88x201x_interrupt_disable(struct cphy *cphy)
90 {
91 /* Disable PHY LASI interrupts. */
92 (void) mdio_write(cphy, 0x1, 0x9002, 0x0);
93
94 /* Disable Marvell interrupts through Elmer0. */
95 if (t1_is_asic(cphy->adapter)) {
96 u32 elmer;
97
98 (void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
99 elmer &= ~ELMER0_GP_BIT6;
100 (void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
101 }
102 return 0;
103 }
104
mv88x201x_interrupt_clear(struct cphy * cphy)105 static int mv88x201x_interrupt_clear(struct cphy *cphy)
106 {
107 u32 elmer;
108 u32 val;
109
110 #ifdef MV88x2010_LINK_STATUS_BUGS
111 /* Required to read twice before clear takes affect. */
112 (void) mdio_read(cphy, 0x1, 0x9003, &val);
113 (void) mdio_read(cphy, 0x1, 0x9004, &val);
114 (void) mdio_read(cphy, 0x1, 0x9005, &val);
115
116 /* Read this register after the others above it else
117 * the register doesn't clear correctly.
118 */
119 (void) mdio_read(cphy, 0x1, 0x1, &val);
120 #endif
121
122 /* Clear link status. */
123 (void) mdio_read(cphy, 0x1, 0x1, &val);
124 /* Clear PHY LASI interrupts. */
125 (void) mdio_read(cphy, 0x1, 0x9005, &val);
126
127 #ifdef MV88x2010_LINK_STATUS_BUGS
128 /* Do it again. */
129 (void) mdio_read(cphy, 0x1, 0x9003, &val);
130 (void) mdio_read(cphy, 0x1, 0x9004, &val);
131 #endif
132
133 /* Clear Marvell interrupts through Elmer0. */
134 if (t1_is_asic(cphy->adapter)) {
135 (void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer);
136 elmer |= ELMER0_GP_BIT6;
137 (void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer);
138 }
139 return 0;
140 }
141
mv88x201x_interrupt_handler(struct cphy * cphy)142 static int mv88x201x_interrupt_handler(struct cphy *cphy)
143 {
144 /* Clear interrupts */
145 (void) mv88x201x_interrupt_clear(cphy);
146
147 /* We have only enabled link change interrupts and so
148 * cphy_cause must be a link change interrupt.
149 */
150 return cphy_cause_link_change;
151 }
152
153 /* ARGSUSED */
mv88x201x_set_loopback(struct cphy * cphy,int on)154 static int mv88x201x_set_loopback(struct cphy *cphy, int on)
155 {
156 return 0;
157 }
158
mv88x201x_get_link_status(struct cphy * cphy,int * link_ok,int * speed,int * duplex,int * fc)159 static int mv88x201x_get_link_status(struct cphy *cphy, int *link_ok,
160 int *speed, int *duplex, int *fc)
161 {
162 u32 val = 0;
163 #define LINK_STATUS_BIT 0x4
164
165 if (link_ok) {
166 /* Read link status. */
167 (void) mdio_read(cphy, 0x1, 0x1, &val);
168 val &= LINK_STATUS_BIT;
169 *link_ok = (val == LINK_STATUS_BIT);
170 /* Turn on/off Link LED */
171 (void) led_link(cphy, *link_ok);
172 }
173 if (speed)
174 *speed = SPEED_10000;
175 if (duplex)
176 *duplex = DUPLEX_FULL;
177 if (fc)
178 *fc = PAUSE_RX | PAUSE_TX;
179 return 0;
180 }
181
mv88x201x_destroy(struct cphy * cphy)182 static void mv88x201x_destroy(struct cphy *cphy)
183 {
184 t1_os_free((void *) cphy, sizeof(*cphy));
185 }
186
187 #ifdef C99_NOT_SUPPORTED
188 static struct cphy_ops mv88x201x_ops = {
189 mv88x201x_destroy,
190 mv88x201x_reset,
191 mv88x201x_interrupt_enable,
192 mv88x201x_interrupt_disable,
193 mv88x201x_interrupt_clear,
194 mv88x201x_interrupt_handler,
195 NULL,
196 NULL,
197 NULL,
198 NULL,
199 mv88x201x_set_loopback,
200 NULL,
201 mv88x201x_get_link_status,
202 };
203 #else
204 static struct cphy_ops mv88x201x_ops = {
205 .destroy = mv88x201x_destroy,
206 .reset = mv88x201x_reset,
207 .interrupt_enable = mv88x201x_interrupt_enable,
208 .interrupt_disable = mv88x201x_interrupt_disable,
209 .interrupt_clear = mv88x201x_interrupt_clear,
210 .interrupt_handler = mv88x201x_interrupt_handler,
211 .get_link_status = mv88x201x_get_link_status,
212 .set_loopback = mv88x201x_set_loopback,
213 };
214 #endif
215
mv88x201x_phy_create(adapter_t * adapter,int phy_addr,struct mdio_ops * mdio_ops)216 static struct cphy *mv88x201x_phy_create(adapter_t *adapter, int phy_addr,
217 struct mdio_ops *mdio_ops)
218 {
219 u32 val;
220 struct cphy *cphy = t1_os_malloc_wait_zero(sizeof(*cphy));
221
222 if (!cphy)
223 return NULL;
224
225 cphy_init(cphy, adapter, phy_addr, &mv88x201x_ops, mdio_ops);
226
227 /* Commands the PHY to enable XFP's clock. */
228 (void) mdio_read(cphy, 0x3, 0x8300, &val);
229 (void) mdio_write(cphy, 0x3, 0x8300, val | 1);
230
231 /* Clear link status. Required because of a bug in the PHY. */
232 (void) mdio_read(cphy, 0x1, 0x8, &val);
233 (void) mdio_read(cphy, 0x3, 0x8, &val);
234
235 /* Allows for Link,Ack LED turn on/off */
236 (void) led_init(cphy);
237 return cphy;
238 }
239
240 /* Chip Reset */
mv88x201x_phy_reset(adapter_t * adapter)241 static int mv88x201x_phy_reset(adapter_t *adapter)
242 {
243 u32 val;
244
245 (void) t1_tpi_read(adapter, A_ELMER0_GPO, &val);
246 val &= ~4;
247 (void) t1_tpi_write(adapter, A_ELMER0_GPO, val);
248 DELAY_MS(100);
249
250 (void) t1_tpi_write(adapter, A_ELMER0_GPO, val | 4);
251 DELAY_MS(1000);
252
253 /* Now lets enable the Laser. Delay 100us */
254 (void) t1_tpi_read(adapter, A_ELMER0_GPO, &val);
255 val |= 0x8000;
256 (void) t1_tpi_write(adapter, A_ELMER0_GPO, val);
257 DELAY_US(100);
258 return 0;
259 }
260
261 struct gphy t1_mv88x201x_ops = {
262 mv88x201x_phy_create,
263 mv88x201x_phy_reset
264 };
265