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"	/* my3126.c */
27
28#include "cphy.h"
29#include "elmer0.h"
30#include "suni1x10gexp_regs.h"
31
32/* Port Reset */
33/* ARGSUSED */
34static int my3126_reset(struct cphy *cphy, int wait)
35{
36	/*
37	 * This can be done through registers.  It is not required since
38	 * a full chip reset is used.
39	 */
40	return (0);
41}
42
43/* ARGSUSED */
44static int my3126_interrupt_enable(struct cphy *cphy)
45{
46	/* T1 Elmer does not support link/act LED. */
47	if (!is_T2(cphy->adapter))
48		return (0);
49	ch_start_cyclic(&cphy->phy_update_cyclic, 30);
50	(void) t1_tpi_read(cphy->adapter, A_ELMER0_GPO, &cphy->elmer_gpo);
51	return (0);
52}
53
54/* ARGSUSED */
55static int my3126_interrupt_disable(struct cphy *cphy)
56{
57	/* T1 Elmer does not support link/act LED. */
58	if (is_T2(cphy->adapter))
59		ch_stop_cyclic(&cphy->phy_update_cyclic);
60	return (0);
61}
62
63/* ARGSUSED */
64static int my3126_interrupt_clear(struct cphy *cphy)
65{
66	return (0);
67}
68
69#define OFFSET(REG_ADDR)    (REG_ADDR << 2)
70
71static int my3126_interrupt_handler(struct cphy *cphy)
72{
73	u32 val;
74	u16 val16;
75	u16 status;
76	u32 act_count;
77	adapter_t *adapter;
78
79	/* T1 Elmer does not support link/act LED. */
80	if (!is_T2(cphy->adapter))
81		return (cphy_cause_link_change);
82
83	adapter = cphy->adapter;
84	if (cphy->count == 50) {
85		(void) mdio_read(cphy, 0x1, 0x1, &val);
86		val16 = (u16) val;
87		status = cphy->bmsr ^ val16;
88
89		if (status & BMSR_LSTATUS) {
90			link_changed(adapter, 0);
91		}
92		cphy->bmsr = val16;
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 */
125static int my3126_set_loopback(struct cphy *cphy, int on)
126{
127	return (0);
128}
129
130/* To check the activity LED */
131static 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
165static void my3126_destroy(struct cphy *cphy)
166{
167	t1_os_free((void *) cphy, sizeof(*cphy));
168}
169
170#ifdef C99_NOT_SUPPORTED
171static 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
187static 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
199static struct cphy *my3126_phy_create(adapter_t *adapter, int phy_addr,
200				      struct mdio_ops *mdio_ops)
201{
202	struct cphy *cphy = t1_os_malloc_wait_zero(sizeof(*cphy));
203
204	if (cphy)
205		cphy_init(cphy, adapter, phy_addr, &my3126_ops, mdio_ops);
206
207	if (is_T2(adapter)) {
208        	ch_init_cyclic(adapter, &cphy->phy_update_cyclic,
209				(void (*)(void *))my3126_interrupt_handler,
210				cphy);
211		cphy->bmsr = 0;
212	}
213
214	return (cphy);
215}
216
217/* Chip Reset */
218static int my3126_phy_reset(adapter_t * adapter)
219{
220	u32 val;
221
222	(void) t1_tpi_read(adapter, A_ELMER0_GPO, &val);
223	val &= ~4;
224	(void) t1_tpi_write(adapter, A_ELMER0_GPO, val);
225	DELAY_MS(100);
226
227	(void) t1_tpi_write(adapter, A_ELMER0_GPO, val | 4);
228	DELAY_MS(1000);
229
230	/* Now lets enable the Laser. Delay 100us */
231	(void) t1_tpi_read(adapter, A_ELMER0_GPO, &val);
232	val |= 0x8000;
233	(void) t1_tpi_write(adapter, A_ELMER0_GPO, val);
234	DELAY_US(100);
235	return (0);
236}
237
238struct gphy t1_my3126_ops = {
239	my3126_phy_create,
240	my3126_phy_reset
241};
242