xref: /illumos-gate/usr/src/uts/common/io/chxge/com/my3126.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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 #include "suni1x10gexp_regs.h"
29 
30 /* Port Reset */
31 /* ARGSUSED */
32 static int my3126_reset(struct cphy *cphy, int wait)
33 {
34 	/*
35 	 * This can be done through registers.  It is not required since
36 	 * a full chip reset is used.
37 	 */
38 	return (0);
39 }
40 
41 /* ARGSUSED */
42 static int my3126_interrupt_enable(struct cphy *cphy)
43 {
44 	/* T1 Elmer does not support link/act LED. */
45 	if (!is_T2(cphy->adapter))
46 		return (0);
47 	ch_start_cyclic(&cphy->phy_update_cyclic, 30);
48 	(void) t1_tpi_read(cphy->adapter, A_ELMER0_GPO, &cphy->elmer_gpo);
49 	return (0);
50 }
51 
52 /* ARGSUSED */
53 static int my3126_interrupt_disable(struct cphy *cphy)
54 {
55 	/* T1 Elmer does not support link/act LED. */
56 	if (is_T2(cphy->adapter))
57 		ch_stop_cyclic(&cphy->phy_update_cyclic);
58 	return (0);
59 }
60 
61 /* ARGSUSED */
62 static int my3126_interrupt_clear(struct cphy *cphy)
63 {
64 	return (0);
65 }
66 
67 #define	OFFSET(REG_ADDR)    (REG_ADDR << 2)
68 
69 static int
70 my3126_interrupt_handler(struct cphy *cphy)
71 {
72 	u32 val;
73 	u16 val16;
74 	u16 status;
75 	u32 act_count;
76 	adapter_t *adapter;
77 
78 	/* T1 Elmer does not support link/act LED. */
79 	if (!is_T2(cphy->adapter))
80 		return (cphy_cause_link_change);
81 
82 	adapter = cphy->adapter;
83 	if (cphy->count == 50) {
84 		(void) mdio_read(cphy, 0x1, 0x1, &val);
85 		val16 = (u16) val;
86 		status = cphy->bmsr ^ val16;
87 
88 		if (status & BMSR_LSTATUS) {
89 			link_changed(adapter, 0);
90 		}
91 		cphy->bmsr = val16;
92 
93 		/*
94 		 * We have only enabled link change interrupts so it
95 		 * must be that.
96 		 */
97 		cphy->count = 0;
98 	}
99 	(void) t1_tpi_write(adapter, OFFSET(SUNI1x10GEXP_REG_MSTAT_CONTROL),
100 	    SUNI1x10GEXP_BITMSK_MSTAT_SNAP);
101 	(void) t1_tpi_read(adapter,
102 	    OFFSET(SUNI1x10GEXP_REG_MSTAT_COUNTER_1_LOW), &act_count);
103 	(void) t1_tpi_read(adapter,
104 	    OFFSET(SUNI1x10GEXP_REG_MSTAT_COUNTER_33_LOW), &val);
105 	act_count += val;
106 	val = cphy->elmer_gpo;
107 	if ((val & (1 << 8)) ||
108 	    (cphy->act_count == act_count) || (cphy->act_on)) {
109 		val |= (1 << 9);
110 		(void) t1_tpi_write(adapter, A_ELMER0_GPO, val);
111 		cphy->act_on = 0;
112 	} else {
113 		val &= ~(1 << 9);
114 		(void) t1_tpi_write(adapter, A_ELMER0_GPO, val);
115 		cphy->act_on = 1;
116 	}
117 	cphy->elmer_gpo = val;
118 	cphy->act_count = act_count;
119 	cphy->count++;
120 
121 	return (cphy_cause_link_change);
122 }
123 
124 /* ARGSUSED */
125 static int my3126_set_loopback(struct cphy *cphy, int on)
126 {
127 	return (0);
128 }
129 
130 /* To check the activity LED */
131 static int my3126_get_link_status(struct cphy *cphy,
132 			int *link_ok, int *speed, int *duplex, int *fc)
133 {
134 	u32 val;
135 	u16 val16;
136 	adapter_t *adapter;
137 
138 	/* T1 Elmer does not support link/act LED. */
139 	if (!is_T2(cphy->adapter))
140 		return (0);
141 
142 	adapter = cphy->adapter;
143 	(void) mdio_read(cphy, 0x1, 0x1, &val);
144 	val16 = (u16) val;
145 	val = cphy->elmer_gpo;
146 	*link_ok = (val16 & BMSR_LSTATUS);
147 	if (*link_ok) {
148 		/* Light the LED. */
149 		val &= ~(1 << 8);
150 	} else {
151 		/* Turn off the LED. */
152 		val |= (1 << 8);
153 	}
154 	(void) t1_tpi_write(adapter, A_ELMER0_GPO, val);
155 	cphy->elmer_gpo = val;
156 	*speed = SPEED_10000;
157 	*duplex = DUPLEX_FULL;
158 	/* need to add flow control */
159 	if (fc)
160 		*fc = PAUSE_RX | PAUSE_TX;
161 
162 	return (0);
163 }
164 
165 static void my3126_destroy(struct cphy *cphy)
166 {
167 	t1_os_free(cphy, sizeof (*cphy));
168 }
169 
170 #ifdef C99_NOT_SUPPORTED
171 static struct cphy_ops my3126_ops = {
172 	my3126_destroy,
173 	my3126_reset,
174 	my3126_interrupt_enable,
175 	my3126_interrupt_disable,
176 	my3126_interrupt_clear,
177 	my3126_interrupt_handler,
178 	NULL,
179 	NULL,
180 	NULL,
181 	NULL,
182 	my3126_set_loopback,
183 	NULL,
184 	my3126_get_link_status,
185 };
186 #else
187 static struct cphy_ops my3126_ops = {
188 	.destroy		= my3126_destroy,
189 	.reset			= my3126_reset,
190 	.interrupt_enable	= my3126_interrupt_enable,
191 	.interrupt_disable	= my3126_interrupt_disable,
192 	.interrupt_clear	= my3126_interrupt_clear,
193 	.interrupt_handler	= my3126_interrupt_handler,
194 	.get_link_status	= my3126_get_link_status,
195 	.set_loopback		= my3126_set_loopback,
196 };
197 #endif
198 
199 static void
200 my3126_cyclic_cb(void *ptr)
201 {
202 	(void) my3126_interrupt_handler(ptr);
203 }
204 
205 static struct cphy *my3126_phy_create(adapter_t *adapter, int phy_addr,
206     struct mdio_ops *mdio_ops)
207 {
208 	struct cphy *cphy = t1_os_malloc_wait_zero(sizeof (*cphy));
209 
210 	if (cphy)
211 		cphy_init(cphy, adapter, phy_addr, &my3126_ops, mdio_ops);
212 
213 	if (is_T2(adapter)) {
214 		ch_init_cyclic(adapter, &cphy->phy_update_cyclic,
215 		    my3126_cyclic_cb, cphy);
216 		cphy->bmsr = 0;
217 	}
218 
219 	return (cphy);
220 }
221 
222 /* Chip Reset */
223 static int my3126_phy_reset(adapter_t *adapter)
224 {
225 	u32 val;
226 
227 	(void) t1_tpi_read(adapter, A_ELMER0_GPO, &val);
228 	val &= ~4;
229 	(void) t1_tpi_write(adapter, A_ELMER0_GPO, val);
230 	DELAY_MS(100);
231 
232 	(void) t1_tpi_write(adapter, A_ELMER0_GPO, val | 4);
233 	DELAY_MS(1000);
234 
235 	/* Now lets enable the Laser. Delay 100us */
236 	(void) t1_tpi_read(adapter, A_ELMER0_GPO, &val);
237 	val |= 0x8000;
238 	(void) t1_tpi_write(adapter, A_ELMER0_GPO, val);
239 	DELAY_US(100);
240 	return (0);
241 }
242 
243 struct gphy t1_my3126_ops = {
244 	my3126_phy_create,
245 	my3126_phy_reset
246 };
247