1/**************************************************************************
2SPDX-License-Identifier: BSD-2-Clause
3
4Copyright (c) 2007, Chelsio Inc.
5All rights reserved.
6
7Redistribution and use in source and binary forms, with or without
8modification, are permitted provided that the following conditions are met:
9
10 1. Redistributions of source code must retain the above copyright notice,
11    this list of conditions and the following disclaimer.
12
13 2. Neither the name of the Chelsio Corporation nor the names of its
14    contributors may be used to endorse or promote products derived from
15    this software without specific prior written permission.
16
17THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27POSSIBILITY OF SUCH DAMAGE.
28
29***************************************************************************/
30
31#include <sys/cdefs.h>
32#include <cxgb_include.h>
33
34#undef msleep
35#define msleep t3_os_sleep
36
37/* VSC8211 PHY specific registers. */
38enum {
39	VSC8211_SIGDET_CTRL   = 19,
40	VSC8211_EXT_CTRL      = 23,
41	VSC8211_PHY_CTRL      = 24,
42	VSC8211_INTR_ENABLE   = 25,
43	VSC8211_INTR_STATUS   = 26,
44	VSC8211_LED_CTRL      = 27,
45	VSC8211_AUX_CTRL_STAT = 28,
46	VSC8211_EXT_PAGE_AXS  = 31,
47};
48
49enum {
50	VSC_INTR_RX_ERR     = 1 << 0,
51	VSC_INTR_MS_ERR     = 1 << 1,  /* master/slave resolution error */
52	VSC_INTR_CABLE      = 1 << 2,  /* cable impairment */
53	VSC_INTR_FALSE_CARR = 1 << 3,  /* false carrier */
54	VSC_INTR_MEDIA_CHG  = 1 << 4,  /* AMS media change */
55	VSC_INTR_RX_FIFO    = 1 << 5,  /* Rx FIFO over/underflow */
56	VSC_INTR_TX_FIFO    = 1 << 6,  /* Tx FIFO over/underflow */
57	VSC_INTR_DESCRAMBL  = 1 << 7,  /* descrambler lock-lost */
58	VSC_INTR_SYMBOL_ERR = 1 << 8,  /* symbol error */
59	VSC_INTR_NEG_DONE   = 1 << 10, /* autoneg done */
60	VSC_INTR_NEG_ERR    = 1 << 11, /* autoneg error */
61	VSC_INTR_DPLX_CHG   = 1 << 12, /* duplex change */
62	VSC_INTR_LINK_CHG   = 1 << 13, /* link change */
63	VSC_INTR_SPD_CHG    = 1 << 14, /* speed change */
64	VSC_INTR_ENABLE     = 1 << 15, /* interrupt enable */
65};
66
67enum {
68	VSC_CTRL_CLAUSE37_VIEW = 1 << 4,   /* Switch to Clause 37 view */
69	VSC_CTRL_MEDIA_MODE_HI = 0xf000    /* High part of media mode select */
70};
71
72#define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
73			   VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \
74	 		   VSC_INTR_NEG_DONE)
75#define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
76		   VSC_INTR_ENABLE)
77
78/* PHY specific auxiliary control & status register fields */
79#define S_ACSR_ACTIPHY_TMR    0
80#define M_ACSR_ACTIPHY_TMR    0x3
81#define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
82
83#define S_ACSR_SPEED    3
84#define M_ACSR_SPEED    0x3
85#define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
86
87#define S_ACSR_DUPLEX 5
88#define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
89
90#define S_ACSR_ACTIPHY 6
91#define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
92
93/*
94 * Reset the PHY.  This PHY completes reset immediately so we never wait.
95 */
96static int vsc8211_reset(struct cphy *cphy, int wait)
97{
98	return t3_phy_reset(cphy, 0, 0);
99}
100
101static int vsc8211_intr_enable(struct cphy *cphy)
102{
103	return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
104}
105
106static int vsc8211_intr_disable(struct cphy *cphy)
107{
108	return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
109}
110
111static int vsc8211_intr_clear(struct cphy *cphy)
112{
113	u32 val;
114
115	/* Clear PHY interrupts by reading the register. */
116	return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val);
117}
118
119static int vsc8211_autoneg_enable(struct cphy *cphy)
120{
121	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
122				   BMCR_ANENABLE | BMCR_ANRESTART);
123}
124
125static int vsc8211_autoneg_restart(struct cphy *cphy)
126{
127	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
128				   BMCR_ANRESTART);
129}
130
131static int vsc8211_get_link_status(struct cphy *cphy, int *link_state,
132				     int *speed, int *duplex, int *fc)
133{
134	unsigned int bmcr, status, lpa, adv;
135	int err, sp = -1, dplx = -1, pause = 0;
136
137	err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
138	if (!err)
139		err = mdio_read(cphy, 0, MII_BMSR, &status);
140	if (err)
141		return err;
142
143	if (link_state) {
144		/*
145		 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
146		 * once more to get the current link state.
147		 */
148		if (!(status & BMSR_LSTATUS))
149			err = mdio_read(cphy, 0, MII_BMSR, &status);
150		if (err)
151			return err;
152		*link_state = status & BMSR_LSTATUS ? PHY_LINK_UP :
153		    PHY_LINK_DOWN;
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_state,
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_state) {
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_state = status & BMSR_LSTATUS ? PHY_LINK_UP :
226		    PHY_LINK_DOWN;
227	}
228	if (!(bmcr & BMCR_ANENABLE)) {
229		dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
230		if (bmcr & BMCR_SPEED1000)
231			sp = SPEED_1000;
232		else if (bmcr & BMCR_SPEED100)
233			sp = SPEED_100;
234		else
235			sp = SPEED_10;
236	} else if (status & BMSR_ANEGCOMPLETE) {
237		err = mdio_read(cphy, 0, MII_LPA, &lpa);
238		if (!err)
239			err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
240		if (err)
241			return err;
242
243		if (adv & lpa & ADVERTISE_1000XFULL) {
244			dplx = DUPLEX_FULL;
245			sp = SPEED_1000;
246		} else if (adv & lpa & ADVERTISE_1000XHALF) {
247			dplx = DUPLEX_HALF;
248			sp = SPEED_1000;
249		}
250
251		if (fc && dplx == DUPLEX_FULL) {
252			if (lpa & adv & ADVERTISE_1000XPAUSE)
253				pause = PAUSE_RX | PAUSE_TX;
254			else if ((lpa & ADVERTISE_1000XPAUSE) &&
255				 (adv & lpa & ADVERTISE_1000XPSE_ASYM))
256				pause = PAUSE_TX;
257			else if ((lpa & ADVERTISE_1000XPSE_ASYM) &&
258				 (adv & ADVERTISE_1000XPAUSE))
259				pause = PAUSE_RX;
260		}
261	}
262	if (speed)
263		*speed = sp;
264	if (duplex)
265		*duplex = dplx;
266	if (fc)
267		*fc = pause;
268	return 0;
269}
270
271/*
272 * Enable/disable auto MDI/MDI-X in forced link speed mode.
273 */
274static int vsc8211_set_automdi(struct cphy *phy, int enable)
275{
276	int err;
277
278	if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5)) != 0 ||
279	    (err = mdio_write(phy, 0, 18, 0x12)) != 0 ||
280	    (err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003)) != 0 ||
281	    (err = mdio_write(phy, 0, 16, 0x87fa)) != 0 ||
282	    (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0)
283		return err;
284	return 0;
285}
286
287static int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex)
288{
289	int err;
290
291	err = t3_set_phy_speed_duplex(phy, speed, duplex);
292	if (!err)
293		err = vsc8211_set_automdi(phy, 1);
294	return err;
295}
296
297static int vsc8211_power_down(struct cphy *cphy, int enable)
298{
299	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
300				   enable ? BMCR_PDOWN : 0);
301}
302
303static int vsc8211_intr_handler(struct cphy *cphy)
304{
305	unsigned int cause;
306	int err, cphy_cause = 0;
307
308	err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause);
309	if (err)
310		return err;
311
312	cause &= INTR_MASK;
313	if (cause & CFG_CHG_INTR_MASK)
314		cphy_cause |= cphy_cause_link_change;
315	if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
316		cphy_cause |= cphy_cause_fifo_error;
317	return cphy_cause;
318}
319
320#ifdef C99_NOT_SUPPORTED
321static struct cphy_ops vsc8211_ops = {
322	vsc8211_reset,
323	vsc8211_intr_enable,
324	vsc8211_intr_disable,
325	vsc8211_intr_clear,
326	vsc8211_intr_handler,
327	vsc8211_autoneg_enable,
328	vsc8211_autoneg_restart,
329	t3_phy_advertise,
330	NULL,
331	vsc8211_set_speed_duplex,
332	vsc8211_get_link_status,
333	vsc8211_power_down,
334};
335
336static struct cphy_ops vsc8211_fiber_ops = {
337	vsc8211_reset,
338	vsc8211_intr_enable,
339	vsc8211_intr_disable,
340	vsc8211_intr_clear,
341	vsc8211_intr_handler,
342	vsc8211_autoneg_enable,
343	vsc8211_autoneg_restart,
344	t3_phy_advertise_fiber,
345	NULL,
346	t3_set_phy_speed_duplex,
347	vsc8211_get_link_status_fiber,
348	vsc8211_power_down,
349};
350#else
351static struct cphy_ops vsc8211_ops = {
352	.reset             = vsc8211_reset,
353	.intr_enable       = vsc8211_intr_enable,
354	.intr_disable      = vsc8211_intr_disable,
355	.intr_clear        = vsc8211_intr_clear,
356	.intr_handler      = vsc8211_intr_handler,
357	.autoneg_enable    = vsc8211_autoneg_enable,
358	.autoneg_restart   = vsc8211_autoneg_restart,
359	.advertise         = t3_phy_advertise,
360	.set_speed_duplex  = vsc8211_set_speed_duplex,
361	.get_link_status   = vsc8211_get_link_status,
362	.power_down        = vsc8211_power_down,
363};
364
365static struct cphy_ops vsc8211_fiber_ops = {
366	.reset             = vsc8211_reset,
367	.intr_enable       = vsc8211_intr_enable,
368	.intr_disable      = vsc8211_intr_disable,
369	.intr_clear        = vsc8211_intr_clear,
370	.intr_handler      = vsc8211_intr_handler,
371	.autoneg_enable    = vsc8211_autoneg_enable,
372	.autoneg_restart   = vsc8211_autoneg_restart,
373	.advertise         = t3_phy_advertise_fiber,
374	.set_speed_duplex  = t3_set_phy_speed_duplex,
375	.get_link_status   = vsc8211_get_link_status_fiber,
376	.power_down        = vsc8211_power_down,
377};
378#endif
379
380#define VSC8211_PHY_CTRL 24
381
382#define S_VSC8211_TXFIFODEPTH    7
383#define M_VSC8211_TXFIFODEPTH    0x7
384#define V_VSC8211_TXFIFODEPTH(x) ((x) << S_VSC8211_TXFIFODEPTH)
385#define G_VSC8211_TXFIFODEPTH(x) (((x) >> S_VSC8211_TXFIFODEPTH) & M_VSC8211_TXFIFODEPTH)
386
387#define S_VSC8211_RXFIFODEPTH    4
388#define M_VSC8211_RXFIFODEPTH    0x7
389#define V_VSC8211_RXFIFODEPTH(x) ((x) << S_VSC8211_RXFIFODEPTH)
390#define G_VSC8211_RXFIFODEPTH(x) (((x) >> S_VSC8211_RXFIFODEPTH) & M_VSC8211_RXFIFODEPTH)
391
392int t3_vsc8211_fifo_depth(adapter_t *adap, unsigned int mtu, int port)
393{
394	/* TX FIFO Depth set bits 9:7 to 100 (IEEE mode) */
395	unsigned int val = 4;
396	unsigned int currentregval;
397	unsigned int regval;
398	int err;
399
400	/* Retrieve the port info structure from adater_t */
401	struct port_info *portinfo = adap2pinfo(adap, port);
402
403	/* What phy is this */
404	struct cphy *phy = &portinfo->phy;
405
406	/* Read the current value of the PHY control Register */
407	err = mdio_read(phy, 0, VSC8211_PHY_CTRL, &currentregval);
408
409	if (err)
410		return err;
411
412	/* IEEE mode supports up to 1518 bytes */
413	/* mtu does not contain the header + FCS (18 bytes) */
414	if (mtu > 1500)
415		/*
416		 * If using a packet size > 1500  set TX FIFO Depth bits
417		 * 9:7 to 011 (Jumbo packet mode)
418		 */
419		val = 3;
420
421	regval = V_VSC8211_TXFIFODEPTH(val) | V_VSC8211_RXFIFODEPTH(val) |
422		(currentregval & ~V_VSC8211_TXFIFODEPTH(M_VSC8211_TXFIFODEPTH) &
423		~V_VSC8211_RXFIFODEPTH(M_VSC8211_RXFIFODEPTH));
424
425	return  mdio_write(phy, 0, VSC8211_PHY_CTRL, regval);
426}
427
428int t3_vsc8211_phy_prep(pinfo_t *pinfo, int phy_addr,
429			const struct mdio_ops *mdio_ops)
430{
431	struct cphy *phy = &pinfo->phy;
432	int err;
433	unsigned int val;
434
435	cphy_init(&pinfo->phy, pinfo->adapter, pinfo, phy_addr, &vsc8211_ops, mdio_ops,
436		  SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
437		  SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
438		  SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
439	msleep(20);       /* PHY needs ~10ms to start responding to MDIO */
440
441	err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val);
442	if (err)
443		return err;
444	if (val & VSC_CTRL_MEDIA_MODE_HI) {
445		/* copper interface, just need to configure the LEDs */
446		return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100);
447	}
448
449	phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
450		    SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ;
451	phy->desc = "1000BASE-X";
452	phy->ops = &vsc8211_fiber_ops;
453
454	if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 ||
455	    (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 ||
456	    (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 ||
457	    (err = mdio_write(phy, 0, VSC8211_EXT_CTRL,
458			      val | VSC_CTRL_CLAUSE37_VIEW)) != 0 ||
459	    (err = vsc8211_reset(phy, 0)) != 0)
460		return err;
461
462	udelay(5); /* delay after reset before next SMI */
463	return 0;
464}
465