10SN/A/**************************************************************************
2157SN/A
30SN/ACopyright (c) 2007, Chelsio Inc.
40SN/AAll rights reserved.
50SN/A
60SN/ARedistribution and use in source and binary forms, with or without
7157SN/Amodification, are permitted provided that the following conditions are met:
80SN/A
9157SN/A 1. Redistributions of source code must retain the above copyright notice,
100SN/A    this list of conditions and the following disclaimer.
110SN/A
120SN/A 2. Neither the name of the Chelsio Corporation nor the names of its
130SN/A    contributors may be used to endorse or promote products derived from
140SN/A    this software without specific prior written permission.
150SN/A
160SN/ATHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
170SN/AAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
180SN/AIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
190SN/AARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
200SN/ALIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21157SN/ACONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22157SN/ASUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23157SN/AINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
240SN/ACONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
250SN/AARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
260SN/APOSSIBILITY OF SUCH DAMAGE.
270SN/A
280SN/A***************************************************************************/
290SN/A
300SN/A#include <sys/cdefs.h>
310SN/A__FBSDID("$FreeBSD$");
320SN/A
330SN/A#include <cxgb_include.h>
340SN/A
350SN/A#undef msleep
360SN/A#define msleep t3_os_sleep
370SN/A
380SN/A/* VSC8211 PHY specific registers. */
390SN/Aenum {
400SN/A	VSC8211_SIGDET_CTRL   = 19,
410SN/A	VSC8211_EXT_CTRL      = 23,
420SN/A	VSC8211_PHY_CTRL      = 24,
430SN/A	VSC8211_INTR_ENABLE   = 25,
440SN/A	VSC8211_INTR_STATUS   = 26,
450SN/A	VSC8211_LED_CTRL      = 27,
460SN/A	VSC8211_AUX_CTRL_STAT = 28,
470SN/A	VSC8211_EXT_PAGE_AXS  = 31,
480SN/A};
490SN/A
500SN/Aenum {
510SN/A	VSC_INTR_RX_ERR     = 1 << 0,
520SN/A	VSC_INTR_MS_ERR     = 1 << 1,  /* master/slave resolution error */
530SN/A	VSC_INTR_CABLE      = 1 << 2,  /* cable impairment */
540SN/A	VSC_INTR_FALSE_CARR = 1 << 3,  /* false carrier */
550SN/A	VSC_INTR_MEDIA_CHG  = 1 << 4,  /* AMS media change */
560SN/A	VSC_INTR_RX_FIFO    = 1 << 5,  /* Rx FIFO over/underflow */
570SN/A	VSC_INTR_TX_FIFO    = 1 << 6,  /* Tx FIFO over/underflow */
580SN/A	VSC_INTR_DESCRAMBL  = 1 << 7,  /* descrambler lock-lost */
590SN/A	VSC_INTR_SYMBOL_ERR = 1 << 8,  /* symbol error */
600SN/A	VSC_INTR_NEG_DONE   = 1 << 10, /* autoneg done */
610SN/A	VSC_INTR_NEG_ERR    = 1 << 11, /* autoneg error */
620SN/A	VSC_INTR_DPLX_CHG   = 1 << 12, /* duplex change */
630SN/A	VSC_INTR_LINK_CHG   = 1 << 13, /* link change */
640SN/A	VSC_INTR_SPD_CHG    = 1 << 14, /* speed change */
650SN/A	VSC_INTR_ENABLE     = 1 << 15, /* interrupt enable */
660SN/A};
670SN/A
680SN/Aenum {
690SN/A	VSC_CTRL_CLAUSE37_VIEW = 1 << 4,   /* Switch to Clause 37 view */
700SN/A	VSC_CTRL_MEDIA_MODE_HI = 0xf000    /* High part of media mode select */
710SN/A};
720SN/A
730SN/A#define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
740SN/A			   VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \
750SN/A	 		   VSC_INTR_NEG_DONE)
760SN/A#define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
77672Savstepan		   VSC_INTR_ENABLE)
780SN/A
790SN/A/* PHY specific auxiliary control & status register fields */
800SN/A#define S_ACSR_ACTIPHY_TMR    0
810SN/A#define M_ACSR_ACTIPHY_TMR    0x3
820SN/A#define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
830SN/A
840SN/A#define S_ACSR_SPEED    3
850SN/A#define M_ACSR_SPEED    0x3
860SN/A#define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
870SN/A
880SN/A#define S_ACSR_DUPLEX 5
890SN/A#define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
900SN/A
910SN/A#define S_ACSR_ACTIPHY 6
920SN/A#define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
930SN/A
94672Savstepan/*
950SN/A * Reset the PHY.  This PHY completes reset immediately so we never wait.
960SN/A */
970SN/Astatic int vsc8211_reset(struct cphy *cphy, int wait)
980SN/A{
990SN/A	return t3_phy_reset(cphy, 0, 0);
1000SN/A}
1010SN/A
1020SN/Astatic int vsc8211_intr_enable(struct cphy *cphy)
1030SN/A{
1040SN/A	return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
1050SN/A}
1060SN/A
1070SN/Astatic int vsc8211_intr_disable(struct cphy *cphy)
108{
109	return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
110}
111
112static int vsc8211_intr_clear(struct cphy *cphy)
113{
114	u32 val;
115
116	/* Clear PHY interrupts by reading the register. */
117	return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val);
118}
119
120static int vsc8211_autoneg_enable(struct cphy *cphy)
121{
122	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
123				   BMCR_ANENABLE | BMCR_ANRESTART);
124}
125
126static int vsc8211_autoneg_restart(struct cphy *cphy)
127{
128	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
129				   BMCR_ANRESTART);
130}
131
132static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok,
133				     int *speed, int *duplex, int *fc)
134{
135	unsigned int bmcr, status, lpa, adv;
136	int err, sp = -1, dplx = -1, pause = 0;
137
138	err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
139	if (!err)
140		err = mdio_read(cphy, 0, MII_BMSR, &status);
141	if (err)
142		return err;
143
144	if (link_ok) {
145		/*
146		 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
147		 * once more to get the current link state.
148		 */
149		if (!(status & BMSR_LSTATUS))
150			err = mdio_read(cphy, 0, MII_BMSR, &status);
151		if (err)
152			return err;
153		*link_ok = (status & BMSR_LSTATUS) != 0;
154	}
155	if (!(bmcr & BMCR_ANENABLE)) {
156		dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
157		if (bmcr & BMCR_SPEED1000)
158			sp = SPEED_1000;
159		else if (bmcr & BMCR_SPEED100)
160			sp = SPEED_100;
161		else
162			sp = SPEED_10;
163	} else if (status & BMSR_ANEGCOMPLETE) {
164		err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status);
165		if (err)
166			return err;
167
168		dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
169		sp = G_ACSR_SPEED(status);
170		if (sp == 0)
171			sp = SPEED_10;
172		else if (sp == 1)
173			sp = SPEED_100;
174		else
175			sp = SPEED_1000;
176
177		if (fc && dplx == DUPLEX_FULL) {
178			err = mdio_read(cphy, 0, MII_LPA, &lpa);
179			if (!err)
180				err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
181			if (err)
182				return err;
183
184			if (lpa & adv & ADVERTISE_PAUSE_CAP)
185				pause = PAUSE_RX | PAUSE_TX;
186			else if ((lpa & ADVERTISE_PAUSE_CAP) &&
187				 (lpa & ADVERTISE_PAUSE_ASYM) &&
188				 (adv & ADVERTISE_PAUSE_ASYM))
189				pause = PAUSE_TX;
190			else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
191				 (adv & ADVERTISE_PAUSE_CAP))
192				pause = PAUSE_RX;
193		}
194	}
195	if (speed)
196		*speed = sp;
197	if (duplex)
198		*duplex = dplx;
199	if (fc)
200		*fc = pause;
201	return 0;
202}
203
204static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_ok,
205					 int *speed, int *duplex, int *fc)
206{
207	unsigned int bmcr, status, lpa, adv;
208	int err, sp = -1, dplx = -1, pause = 0;
209
210	err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
211	if (!err)
212		err = mdio_read(cphy, 0, MII_BMSR, &status);
213	if (err)
214		return err;
215
216	if (link_ok) {
217		/*
218		 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
219		 * once more to get the current link state.
220		 */
221		if (!(status & BMSR_LSTATUS))
222			err = mdio_read(cphy, 0, MII_BMSR, &status);
223		if (err)
224			return err;
225		*link_ok = (status & BMSR_LSTATUS) != 0;
226	}
227	if (!(bmcr & BMCR_ANENABLE)) {
228		dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
229		if (bmcr & BMCR_SPEED1000)
230			sp = SPEED_1000;
231		else if (bmcr & BMCR_SPEED100)
232			sp = SPEED_100;
233		else
234			sp = SPEED_10;
235	} else if (status & BMSR_ANEGCOMPLETE) {
236		err = mdio_read(cphy, 0, MII_LPA, &lpa);
237		if (!err)
238			err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
239		if (err)
240			return err;
241
242		if (adv & lpa & ADVERTISE_1000XFULL) {
243			dplx = DUPLEX_FULL;
244			sp = SPEED_1000;
245		} else if (adv & lpa & ADVERTISE_1000XHALF) {
246			dplx = DUPLEX_HALF;
247			sp = SPEED_1000;
248		}
249
250		if (fc && dplx == DUPLEX_FULL) {
251			if (lpa & adv & ADVERTISE_1000XPAUSE)
252				pause = PAUSE_RX | PAUSE_TX;
253			else if ((lpa & ADVERTISE_1000XPAUSE) &&
254				 (adv & lpa & ADVERTISE_1000XPSE_ASYM))
255				pause = PAUSE_TX;
256			else if ((lpa & ADVERTISE_1000XPSE_ASYM) &&
257				 (adv & ADVERTISE_1000XPAUSE))
258				pause = PAUSE_RX;
259		}
260	}
261	if (speed)
262		*speed = sp;
263	if (duplex)
264		*duplex = dplx;
265	if (fc)
266		*fc = pause;
267	return 0;
268}
269
270/*
271 * Enable/disable auto MDI/MDI-X in forced link speed mode.
272 */
273static int vsc8211_set_automdi(struct cphy *phy, int enable)
274{
275	int err;
276
277	if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5)) != 0 ||
278	    (err = mdio_write(phy, 0, 18, 0x12)) != 0 ||
279	    (err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003)) != 0 ||
280	    (err = mdio_write(phy, 0, 16, 0x87fa)) != 0 ||
281	    (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0)
282		return err;
283	return 0;
284}
285
286static int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex)
287{
288	int err;
289
290	err = t3_set_phy_speed_duplex(phy, speed, duplex);
291	if (!err)
292		err = vsc8211_set_automdi(phy, 1);
293	return err;
294}
295
296static int vsc8211_power_down(struct cphy *cphy, int enable)
297{
298	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
299				   enable ? BMCR_PDOWN : 0);
300}
301
302static int vsc8211_intr_handler(struct cphy *cphy)
303{
304	unsigned int cause;
305	int err, cphy_cause = 0;
306
307	err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause);
308	if (err)
309		return err;
310
311	cause &= INTR_MASK;
312	if (cause & CFG_CHG_INTR_MASK)
313		cphy_cause |= cphy_cause_link_change;
314	if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
315		cphy_cause |= cphy_cause_fifo_error;
316	return cphy_cause;
317}
318
319#ifdef C99_NOT_SUPPORTED
320static struct cphy_ops vsc8211_ops = {
321	vsc8211_reset,
322	vsc8211_intr_enable,
323	vsc8211_intr_disable,
324	vsc8211_intr_clear,
325	vsc8211_intr_handler,
326	vsc8211_autoneg_enable,
327	vsc8211_autoneg_restart,
328	t3_phy_advertise,
329	NULL,
330	vsc8211_set_speed_duplex,
331	vsc8211_get_link_status,
332	vsc8211_power_down,
333};
334
335static struct cphy_ops vsc8211_fiber_ops = {
336	vsc8211_reset,
337	vsc8211_intr_enable,
338	vsc8211_intr_disable,
339	vsc8211_intr_clear,
340	vsc8211_intr_handler,
341	vsc8211_autoneg_enable,
342	vsc8211_autoneg_restart,
343	t3_phy_advertise_fiber,
344	NULL,
345	t3_set_phy_speed_duplex,
346	vsc8211_get_link_status_fiber,
347	vsc8211_power_down,
348};
349#else
350static struct cphy_ops vsc8211_ops = {
351	.reset             = vsc8211_reset,
352	.intr_enable       = vsc8211_intr_enable,
353	.intr_disable      = vsc8211_intr_disable,
354	.intr_clear        = vsc8211_intr_clear,
355	.intr_handler      = vsc8211_intr_handler,
356	.autoneg_enable    = vsc8211_autoneg_enable,
357	.autoneg_restart   = vsc8211_autoneg_restart,
358	.advertise         = t3_phy_advertise,
359	.set_speed_duplex  = vsc8211_set_speed_duplex,
360	.get_link_status   = vsc8211_get_link_status,
361	.power_down        = vsc8211_power_down,
362};
363
364static struct cphy_ops vsc8211_fiber_ops = {
365	.reset             = vsc8211_reset,
366	.intr_enable       = vsc8211_intr_enable,
367	.intr_disable      = vsc8211_intr_disable,
368	.intr_clear        = vsc8211_intr_clear,
369	.intr_handler      = vsc8211_intr_handler,
370	.autoneg_enable    = vsc8211_autoneg_enable,
371	.autoneg_restart   = vsc8211_autoneg_restart,
372	.advertise         = t3_phy_advertise_fiber,
373	.set_speed_duplex  = t3_set_phy_speed_duplex,
374	.get_link_status   = vsc8211_get_link_status_fiber,
375	.power_down        = vsc8211_power_down,
376};
377#endif
378
379#define VSC8211_PHY_CTRL 24
380
381#define S_VSC8211_TXFIFODEPTH    7
382#define M_VSC8211_TXFIFODEPTH    0x7
383#define V_VSC8211_TXFIFODEPTH(x) ((x) << S_VSC8211_TXFIFODEPTH)
384#define G_VSC8211_TXFIFODEPTH(x) (((x) >> S_VSC8211_TXFIFODEPTH) & M_VSC8211_TXFIFODEPTH)
385
386#define S_VSC8211_RXFIFODEPTH    4
387#define M_VSC8211_RXFIFODEPTH    0x7
388#define V_VSC8211_RXFIFODEPTH(x) ((x) << S_VSC8211_RXFIFODEPTH)
389#define G_VSC8211_RXFIFODEPTH(x) (((x) >> S_VSC8211_RXFIFODEPTH) & M_VSC8211_RXFIFODEPTH)
390
391int t3_vsc8211_fifo_depth(adapter_t *adap, unsigned int mtu, int port)
392{
393	/* TX FIFO Depth set bits 9:7 to 100 (IEEE mode) */
394	unsigned int val = 4;
395	unsigned int currentregval;
396	unsigned int regval;
397	int err;
398
399	/* Retrieve the port info structure from adater_t */
400	struct port_info *portinfo = adap2pinfo(adap, port);
401
402	/* What phy is this */
403	struct cphy *phy = &portinfo->phy;
404
405	/* Read the current value of the PHY control Register */
406	err = mdio_read(phy, 0, VSC8211_PHY_CTRL, &currentregval);
407
408	if (err)
409		return err;
410
411	/* IEEE mode supports up to 1518 bytes */
412	/* mtu does not contain the header + FCS (18 bytes) */
413	if (mtu > 1500)
414		/*
415		 * If using a packet size > 1500  set TX FIFO Depth bits
416		 * 9:7 to 011 (Jumbo packet mode)
417		 */
418		val = 3;
419
420	regval = V_VSC8211_TXFIFODEPTH(val) | V_VSC8211_RXFIFODEPTH(val) |
421		(currentregval & ~V_VSC8211_TXFIFODEPTH(M_VSC8211_TXFIFODEPTH) &
422		~V_VSC8211_RXFIFODEPTH(M_VSC8211_RXFIFODEPTH));
423
424	return  mdio_write(phy, 0, VSC8211_PHY_CTRL, regval);
425}
426
427int t3_vsc8211_phy_prep(pinfo_t *pinfo, int phy_addr,
428			const struct mdio_ops *mdio_ops)
429{
430	struct cphy *phy = &pinfo->phy;
431	int err;
432	unsigned int val;
433
434	cphy_init(&pinfo->phy, pinfo->adapter, pinfo, phy_addr, &vsc8211_ops, mdio_ops,
435		  SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
436		  SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
437		  SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
438	msleep(20);       /* PHY needs ~10ms to start responding to MDIO */
439
440	err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val);
441	if (err)
442		return err;
443	if (val & VSC_CTRL_MEDIA_MODE_HI) {
444		/* copper interface, just need to configure the LEDs */
445		return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100);
446	}
447
448	phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
449		    SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ;
450	phy->desc = "1000BASE-X";
451	phy->ops = &vsc8211_fiber_ops;
452
453	if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 ||
454	    (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 ||
455	    (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 ||
456	    (err = mdio_write(phy, 0, VSC8211_EXT_CTRL,
457			      val | VSC_CTRL_CLAUSE37_VIEW)) != 0 ||
458	    (err = vsc8211_reset(phy, 0)) != 0)
459		return err;
460
461	udelay(5); /* delay after reset before next SMI */
462	return 0;
463}
464