1331722Seadler
2170654Skmacy/**************************************************************************
3170654Skmacy
4170654SkmacyCopyright (c) 2007, Chelsio Inc.
5170654SkmacyAll rights reserved.
6170654Skmacy
7170654SkmacyRedistribution and use in source and binary forms, with or without
8170654Skmacymodification, are permitted provided that the following conditions are met:
9170654Skmacy
10170654Skmacy 1. Redistributions of source code must retain the above copyright notice,
11170654Skmacy    this list of conditions and the following disclaimer.
12170654Skmacy
13170654Skmacy 2. Neither the name of the Chelsio Corporation nor the names of its
14170654Skmacy    contributors may be used to endorse or promote products derived from
15170654Skmacy    this software without specific prior written permission.
16170654Skmacy
17170654SkmacyTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18170654SkmacyAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19170654SkmacyIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20170654SkmacyARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21170654SkmacyLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22170654SkmacyCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23170654SkmacySUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24170654SkmacyINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25170654SkmacyCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26170654SkmacyARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27170654SkmacyPOSSIBILITY OF SUCH DAMAGE.
28170654Skmacy
29170654Skmacy***************************************************************************/
30170654Skmacy
31170654Skmacy#include <sys/cdefs.h>
32170654Skmacy__FBSDID("$FreeBSD$");
33170654Skmacy
34170654Skmacy#include <common/cxgb_common.h>
35170654Skmacy
36170654Skmacyenum {
37170654Skmacy	ELMR_ADDR    = 0,
38170654Skmacy	ELMR_STAT    = 1,
39170654Skmacy	ELMR_DATA_LO = 2,
40170654Skmacy	ELMR_DATA_HI = 3,
41170654Skmacy
42172096Skmacy	ELMR_THRES0  = 0xe000,
43172096Skmacy	ELMR_BW      = 0xe00c,
44172096Skmacy	ELMR_FIFO_SZ = 0xe00d,
45172096Skmacy	ELMR_STATS   = 0xf000,
46172096Skmacy
47170654Skmacy	ELMR_MDIO_ADDR = 10
48170654Skmacy};
49170654Skmacy
50170654Skmacy#define VSC_REG(block, subblock, reg) \
51170654Skmacy	((reg) | ((subblock) << 8) | ((block) << 12))
52170654Skmacy
53170654Skmacyint t3_elmr_blk_write(adapter_t *adap, int start, const u32 *vals, int n)
54170654Skmacy{
55170654Skmacy	int ret;
56170654Skmacy	const struct mdio_ops *mo = adapter_info(adap)->mdio_ops;
57170654Skmacy
58170654Skmacy	ELMR_LOCK(adap);
59170654Skmacy	ret = mo->write(adap, ELMR_MDIO_ADDR, 0, ELMR_ADDR, start);
60170654Skmacy	for ( ; !ret && n; n--, vals++) {
61170654Skmacy		ret = mo->write(adap, ELMR_MDIO_ADDR, 0, ELMR_DATA_LO,
62170654Skmacy				*vals & 0xffff);
63170654Skmacy		if (!ret)
64170654Skmacy			ret = mo->write(adap, ELMR_MDIO_ADDR, 0, ELMR_DATA_HI,
65170654Skmacy					*vals >> 16);
66170654Skmacy	}
67170654Skmacy	ELMR_UNLOCK(adap);
68170654Skmacy	return ret;
69170654Skmacy}
70170654Skmacy
71170654Skmacystatic int elmr_write(adapter_t *adap, int addr, u32 val)
72170654Skmacy{
73170654Skmacy	return t3_elmr_blk_write(adap, addr, &val, 1);
74170654Skmacy}
75170654Skmacy
76170654Skmacyint t3_elmr_blk_read(adapter_t *adap, int start, u32 *vals, int n)
77170654Skmacy{
78172096Skmacy	int i, ret;
79170654Skmacy	unsigned int v;
80170654Skmacy	const struct mdio_ops *mo = adapter_info(adap)->mdio_ops;
81170654Skmacy
82170654Skmacy	ELMR_LOCK(adap);
83170654Skmacy
84170654Skmacy	ret = mo->write(adap, ELMR_MDIO_ADDR, 0, ELMR_ADDR, start);
85170654Skmacy	if (ret)
86170654Skmacy		goto out;
87172096Skmacy
88172096Skmacy	for (i = 0; i < 5; i++) {
89172096Skmacy		ret = mo->read(adap, ELMR_MDIO_ADDR, 0, ELMR_STAT, &v);
90172096Skmacy		if (ret)
91172096Skmacy			goto out;
92172096Skmacy		if (v == 1)
93172096Skmacy			break;
94172096Skmacy		udelay(5);
95172096Skmacy	}
96170654Skmacy	if (v != 1) {
97170654Skmacy		ret = -ETIMEDOUT;
98170654Skmacy		goto out;
99170654Skmacy	}
100170654Skmacy
101170654Skmacy	for ( ; !ret && n; n--, vals++) {
102170654Skmacy		ret = mo->read(adap, ELMR_MDIO_ADDR, 0, ELMR_DATA_LO, vals);
103170654Skmacy		if (!ret) {
104170654Skmacy			ret = mo->read(adap, ELMR_MDIO_ADDR, 0, ELMR_DATA_HI,
105170654Skmacy				       &v);
106170654Skmacy			*vals |= v << 16;
107170654Skmacy		}
108170654Skmacy	}
109170654Skmacyout:	ELMR_UNLOCK(adap);
110170654Skmacy	return ret;
111170654Skmacy}
112170654Skmacy
113170654Skmacyint t3_vsc7323_init(adapter_t *adap, int nports)
114170654Skmacy{
115170654Skmacy	static struct addr_val_pair sys_avp[] = {
116170654Skmacy		{ VSC_REG(7, 15, 0xf),  2 },
117170654Skmacy		{ VSC_REG(7, 15, 0x19), 0xd6 },
118170654Skmacy		{ VSC_REG(7, 15, 7),    0xc },
119170654Skmacy		{ VSC_REG(7, 1, 0),     0x220 },
120170654Skmacy	};
121170654Skmacy	static struct addr_val_pair fifo_avp[] = {
122170654Skmacy		{ VSC_REG(2, 0, 0x2f), 0 },
123170654Skmacy		{ VSC_REG(2, 0, 0xf),  0xa0010291 },
124170654Skmacy		{ VSC_REG(2, 1, 0x2f), 1 },
125171471Skmacy		{ VSC_REG(2, 1, 0xf),  0xa026301 }
126170654Skmacy	};
127170654Skmacy	static struct addr_val_pair xg_avp[] = {
128170654Skmacy		{ VSC_REG(1, 10, 0),    0x600b },
129171471Skmacy		{ VSC_REG(1, 10, 1),    0x70600 }, //QUANTA = 96*1024*8/512
130171471Skmacy		{ VSC_REG(1, 10, 2),    0x2710 },
131170654Skmacy		{ VSC_REG(1, 10, 5),    0x65 },
132171471Skmacy		{ VSC_REG(1, 10, 7),    0x23 },
133170654Skmacy		{ VSC_REG(1, 10, 0x23), 0x800007bf },
134171471Skmacy		{ VSC_REG(1, 10, 0x23), 0x000007bf },
135171471Skmacy		{ VSC_REG(1, 10, 0x23), 0x800007bf },
136170654Skmacy		{ VSC_REG(1, 10, 0x24), 4 }
137170654Skmacy	};
138170654Skmacy
139170654Skmacy	int i, ret, ing_step, egr_step, ing_bot, egr_bot;
140170654Skmacy
141170654Skmacy	for (i = 0; i < ARRAY_SIZE(sys_avp); i++)
142170654Skmacy		if ((ret = t3_elmr_blk_write(adap, sys_avp[i].reg_addr,
143171471Skmacy					     &sys_avp[i].val, 1)))
144170654Skmacy			return ret;
145170654Skmacy
146170654Skmacy	ing_step = 0xc0 / nports;
147170654Skmacy	egr_step = 0x40 / nports;
148170654Skmacy	ing_bot = egr_bot = 0;
149170654Skmacy//	ing_wm = ing_step * 64;
150170654Skmacy//	egr_wm = egr_step * 64;
151170654Skmacy
152170654Skmacy	/* {ING,EGR}_CONTROL.CLR = 1 here */
153171471Skmacy	for (i = 0; i < nports; i++) {
154171471Skmacy		if (
155171471Skmacy		    (ret = elmr_write(adap, VSC_REG(2, 0, 0x10 + i),
156170654Skmacy				((ing_bot + ing_step) << 16) | ing_bot)) ||
157171471Skmacy		    (ret = elmr_write(adap, VSC_REG(2, 0, 0x40 + i),
158172096Skmacy				0x6000bc0)) ||
159171471Skmacy		    (ret = elmr_write(adap, VSC_REG(2, 0, 0x50 + i), 1)) ||
160170654Skmacy		    (ret = elmr_write(adap, VSC_REG(2, 1, 0x10 + i),
161170654Skmacy				((egr_bot + egr_step) << 16) | egr_bot)) ||
162170654Skmacy		    (ret = elmr_write(adap, VSC_REG(2, 1, 0x40 + i),
163170654Skmacy				0x2000280)) ||
164170654Skmacy		    (ret = elmr_write(adap, VSC_REG(2, 1, 0x50 + i), 0)))
165170654Skmacy			return ret;
166171471Skmacy		ing_bot += ing_step;
167171471Skmacy		egr_bot += egr_step;
168171471Skmacy	}
169170654Skmacy
170170654Skmacy	for (i = 0; i < ARRAY_SIZE(fifo_avp); i++)
171170654Skmacy		if ((ret = t3_elmr_blk_write(adap, fifo_avp[i].reg_addr,
172171471Skmacy					     &fifo_avp[i].val, 1)))
173171471Skmacy			return ret;
174170654Skmacy
175170654Skmacy	for (i = 0; i < ARRAY_SIZE(xg_avp); i++)
176170654Skmacy		if ((ret = t3_elmr_blk_write(adap, xg_avp[i].reg_addr,
177170654Skmacy					     &xg_avp[i].val, 1)))
178170654Skmacy			return ret;
179170654Skmacy
180170654Skmacy	for (i = 0; i < nports; i++)
181170654Skmacy		if ((ret = elmr_write(adap, VSC_REG(1, i, 0), 0xa59c)) ||
182170654Skmacy		    (ret = elmr_write(adap, VSC_REG(1, i, 5),
183170654Skmacy				 (i << 12) | 0x63)) ||
184170654Skmacy		    (ret = elmr_write(adap, VSC_REG(1, i, 0xb), 0x96)) ||
185172096Skmacy		    (ret = elmr_write(adap, VSC_REG(1, i, 0x15), 0x21)) ||
186172096Skmacy		    (ret = elmr_write(adap, ELMR_THRES0 + i, 768)))
187170654Skmacy			return ret;
188172096Skmacy
189172096Skmacy	if ((ret = elmr_write(adap, ELMR_BW, 7)))
190172096Skmacy		return ret;
191172096Skmacy
192170654Skmacy	return ret;
193170654Skmacy}
194170654Skmacy
195170654Skmacyint t3_vsc7323_set_speed_fc(adapter_t *adap, int speed, int fc, int port)
196170654Skmacy{
197170654Skmacy	int mode, clk, r;
198170654Skmacy
199170654Skmacy	if (speed >= 0) {
200170654Skmacy		if (speed == SPEED_10)
201170654Skmacy			mode = clk = 1;
202170654Skmacy		else if (speed == SPEED_100)
203170654Skmacy			mode = 1, clk = 2;
204170654Skmacy		else if (speed == SPEED_1000)
205170654Skmacy			mode = clk = 3;
206170654Skmacy		else
207170654Skmacy			return -EINVAL;
208170654Skmacy
209170654Skmacy		if ((r = elmr_write(adap, VSC_REG(1, port, 0),
210170654Skmacy				    0xa590 | (mode << 2))) ||
211170654Skmacy		    (r = elmr_write(adap, VSC_REG(1, port, 0xb),
212170654Skmacy				    0x91 | (clk << 1))) ||
213170654Skmacy		    (r = elmr_write(adap, VSC_REG(1, port, 0xb),
214170654Skmacy				    0x90 | (clk << 1))) ||
215170654Skmacy		    (r = elmr_write(adap, VSC_REG(1, port, 0),
216170654Skmacy				    0xa593 | (mode << 2))))
217170654Skmacy			return r;
218170654Skmacy	}
219170654Skmacy
220171471Skmacy	r = (fc & PAUSE_RX) ? 0x60200 : 0x20200; //QUANTA = 32*1024*8/512
221170654Skmacy	if (fc & PAUSE_TX)
222170654Skmacy		r |= (1 << 19);
223170654Skmacy	return elmr_write(adap, VSC_REG(1, port, 1), r);
224170654Skmacy}
225170654Skmacy
226170654Skmacyint t3_vsc7323_set_mtu(adapter_t *adap, unsigned int mtu, int port)
227170654Skmacy{
228170654Skmacy	return elmr_write(adap, VSC_REG(1, port, 2), mtu);
229170654Skmacy}
230170654Skmacy
231170654Skmacyint t3_vsc7323_set_addr(adapter_t *adap, u8 addr[6], int port)
232170654Skmacy{
233170654Skmacy	int ret;
234171471Skmacy
235170654Skmacy	ret = elmr_write(adap, VSC_REG(1, port, 3),
236171471Skmacy			 (addr[0] << 16) | (addr[1] << 8) | addr[2]);
237170654Skmacy	if (!ret)
238170654Skmacy		ret = elmr_write(adap, VSC_REG(1, port, 4),
239171471Skmacy				 (addr[3] << 16) | (addr[4] << 8) | addr[5]);
240170654Skmacy	return ret;
241170654Skmacy}
242170654Skmacy
243170654Skmacyint t3_vsc7323_enable(adapter_t *adap, int port, int which)
244170654Skmacy{
245170654Skmacy	int ret;
246170654Skmacy	unsigned int v, orig;
247170654Skmacy
248170654Skmacy	ret = t3_elmr_blk_read(adap, VSC_REG(1, port, 0), &v, 1);
249170654Skmacy	if (!ret) {
250170654Skmacy		orig = v;
251170654Skmacy		if (which & MAC_DIRECTION_TX)
252170654Skmacy			v |= 1;
253170654Skmacy		if (which & MAC_DIRECTION_RX)
254170654Skmacy			v |= 2;
255170654Skmacy		if (v != orig)
256170654Skmacy			ret = elmr_write(adap, VSC_REG(1, port, 0), v);
257170654Skmacy	}
258170654Skmacy	return ret;
259170654Skmacy}
260170654Skmacy
261170654Skmacyint t3_vsc7323_disable(adapter_t *adap, int port, int which)
262170654Skmacy{
263170654Skmacy	int ret;
264170654Skmacy	unsigned int v, orig;
265170654Skmacy
266170654Skmacy	ret = t3_elmr_blk_read(adap, VSC_REG(1, port, 0), &v, 1);
267170654Skmacy	if (!ret) {
268170654Skmacy		orig = v;
269170654Skmacy		if (which & MAC_DIRECTION_TX)
270170654Skmacy			v &= ~1;
271170654Skmacy		if (which & MAC_DIRECTION_RX)
272170654Skmacy			v &= ~2;
273170654Skmacy		if (v != orig)
274170654Skmacy			ret = elmr_write(adap, VSC_REG(1, port, 0), v);
275170654Skmacy	}
276170654Skmacy	return ret;
277170654Skmacy}
278170654Skmacy
279170654Skmacy#define STATS0_START 1
280170654Skmacy#define STATS1_START 0x24
281170654Skmacy#define NSTATS0 (0x1d - STATS0_START + 1)
282170654Skmacy#define NSTATS1 (0x2a - STATS1_START + 1)
283170654Skmacy
284172096Skmacy#define ELMR_STAT(port, reg) (ELMR_STATS + port * 0x40 + reg)
285172096Skmacy
286170654Skmacyconst struct mac_stats *t3_vsc7323_update_stats(struct cmac *mac)
287170654Skmacy{
288170654Skmacy	int ret;
289170654Skmacy	u64 rx_ucast, tx_ucast;
290170654Skmacy	u32 stats0[NSTATS0], stats1[NSTATS1];
291170654Skmacy
292170654Skmacy	ret = t3_elmr_blk_read(mac->adapter,
293172096Skmacy			       ELMR_STAT(mac->ext_port, STATS0_START),
294170654Skmacy			       stats0, NSTATS0);
295170654Skmacy	if (!ret)
296170654Skmacy		ret = t3_elmr_blk_read(mac->adapter,
297172096Skmacy				       ELMR_STAT(mac->ext_port, STATS1_START),
298170654Skmacy				       stats1, NSTATS1);
299170654Skmacy	if (ret)
300170654Skmacy		goto out;
301170654Skmacy
302170654Skmacy	/*
303170654Skmacy	 * HW counts Rx/Tx unicast frames but we want all the frames.
304170654Skmacy	 */
305170654Skmacy	rx_ucast = mac->stats.rx_frames - mac->stats.rx_mcast_frames -
306170654Skmacy		   mac->stats.rx_bcast_frames;
307170654Skmacy	rx_ucast += (u64)(stats0[6 - STATS0_START] - (u32)rx_ucast);
308170654Skmacy	tx_ucast = mac->stats.tx_frames - mac->stats.tx_mcast_frames -
309170654Skmacy		   mac->stats.tx_bcast_frames;
310170654Skmacy	tx_ucast += (u64)(stats0[27 - STATS0_START] - (u32)tx_ucast);
311170654Skmacy
312170654Skmacy#define RMON_UPDATE(mac, name, hw_stat) \
313170654Skmacy	mac->stats.name += (u64)((hw_stat) - (u32)(mac->stats.name))
314170654Skmacy
315170654Skmacy	RMON_UPDATE(mac, rx_octets, stats0[4 - STATS0_START]);
316170654Skmacy	RMON_UPDATE(mac, rx_frames, stats0[6 - STATS0_START]);
317170654Skmacy	RMON_UPDATE(mac, rx_frames, stats0[7 - STATS0_START]);
318170654Skmacy	RMON_UPDATE(mac, rx_frames, stats0[8 - STATS0_START]);
319170654Skmacy	RMON_UPDATE(mac, rx_mcast_frames, stats0[7 - STATS0_START]);
320170654Skmacy	RMON_UPDATE(mac, rx_bcast_frames, stats0[8 - STATS0_START]);
321170654Skmacy	RMON_UPDATE(mac, rx_fcs_errs, stats0[9 - STATS0_START]);
322170654Skmacy	RMON_UPDATE(mac, rx_pause, stats0[2 - STATS0_START]);
323170654Skmacy	RMON_UPDATE(mac, rx_jabber, stats0[16 - STATS0_START]);
324170654Skmacy	RMON_UPDATE(mac, rx_short, stats0[11 - STATS0_START]);
325170654Skmacy	RMON_UPDATE(mac, rx_symbol_errs, stats0[1 - STATS0_START]);
326170654Skmacy	RMON_UPDATE(mac, rx_too_long, stats0[15 - STATS0_START]);
327170654Skmacy
328170654Skmacy	RMON_UPDATE(mac, rx_frames_64,        stats0[17 - STATS0_START]);
329170654Skmacy	RMON_UPDATE(mac, rx_frames_65_127,    stats0[18 - STATS0_START]);
330170654Skmacy	RMON_UPDATE(mac, rx_frames_128_255,   stats0[19 - STATS0_START]);
331170654Skmacy	RMON_UPDATE(mac, rx_frames_256_511,   stats0[20 - STATS0_START]);
332170654Skmacy	RMON_UPDATE(mac, rx_frames_512_1023,  stats0[21 - STATS0_START]);
333170654Skmacy	RMON_UPDATE(mac, rx_frames_1024_1518, stats0[22 - STATS0_START]);
334170654Skmacy	RMON_UPDATE(mac, rx_frames_1519_max,  stats0[23 - STATS0_START]);
335170654Skmacy
336170654Skmacy	RMON_UPDATE(mac, tx_octets, stats0[26 - STATS0_START]);
337170654Skmacy	RMON_UPDATE(mac, tx_frames, stats0[27 - STATS0_START]);
338170654Skmacy	RMON_UPDATE(mac, tx_frames, stats0[28 - STATS0_START]);
339170654Skmacy	RMON_UPDATE(mac, tx_frames, stats0[29 - STATS0_START]);
340170654Skmacy	RMON_UPDATE(mac, tx_mcast_frames, stats0[28 - STATS0_START]);
341170654Skmacy	RMON_UPDATE(mac, tx_bcast_frames, stats0[29 - STATS0_START]);
342170654Skmacy	RMON_UPDATE(mac, tx_pause, stats0[25 - STATS0_START]);
343170654Skmacy
344170654Skmacy	RMON_UPDATE(mac, tx_underrun, 0);
345170654Skmacy
346170654Skmacy	RMON_UPDATE(mac, tx_frames_64,        stats1[36 - STATS1_START]);
347170654Skmacy	RMON_UPDATE(mac, tx_frames_65_127,    stats1[37 - STATS1_START]);
348170654Skmacy	RMON_UPDATE(mac, tx_frames_128_255,   stats1[38 - STATS1_START]);
349170654Skmacy	RMON_UPDATE(mac, tx_frames_256_511,   stats1[39 - STATS1_START]);
350170654Skmacy	RMON_UPDATE(mac, tx_frames_512_1023,  stats1[40 - STATS1_START]);
351170654Skmacy	RMON_UPDATE(mac, tx_frames_1024_1518, stats1[41 - STATS1_START]);
352170654Skmacy	RMON_UPDATE(mac, tx_frames_1519_max,  stats1[42 - STATS1_START]);
353170654Skmacy
354170654Skmacy#undef RMON_UPDATE
355170654Skmacy
356170654Skmacy	mac->stats.rx_frames = rx_ucast + mac->stats.rx_mcast_frames +
357170654Skmacy			       mac->stats.rx_bcast_frames;
358170654Skmacy	mac->stats.tx_frames = tx_ucast + mac->stats.tx_mcast_frames +
359170654Skmacy			       mac->stats.tx_bcast_frames;
360170654Skmacyout:    return &mac->stats;
361170654Skmacy}
362