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