xref: /freebsd/sys/dev/cxgb/common/cxgb_tn1010.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
138ddd4d3SKip Macy /**************************************************************************
2*4d846d26SWarner Losh SPDX-License-Identifier: BSD-2-Clause
338ddd4d3SKip Macy 
438ddd4d3SKip Macy Copyright (c) 2008, Chelsio Inc.
538ddd4d3SKip Macy All rights reserved.
638ddd4d3SKip Macy 
738ddd4d3SKip Macy Redistribution and use in source and binary forms, with or without
838ddd4d3SKip Macy modification, are permitted provided that the following conditions are met:
938ddd4d3SKip Macy 
1038ddd4d3SKip Macy  1. Redistributions of source code must retain the above copyright notice,
1138ddd4d3SKip Macy     this list of conditions and the following disclaimer.
1238ddd4d3SKip Macy 
1338ddd4d3SKip Macy  2. Neither the name of the Chelsio Corporation nor the names of its
1438ddd4d3SKip Macy     contributors may be used to endorse or promote products derived from
1538ddd4d3SKip Macy     this software without specific prior written permission.
1638ddd4d3SKip Macy 
1738ddd4d3SKip Macy THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1838ddd4d3SKip Macy AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1938ddd4d3SKip Macy IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2038ddd4d3SKip Macy ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2138ddd4d3SKip Macy LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2238ddd4d3SKip Macy CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2338ddd4d3SKip Macy SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2438ddd4d3SKip Macy INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2538ddd4d3SKip Macy CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2638ddd4d3SKip Macy ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2738ddd4d3SKip Macy POSSIBILITY OF SUCH DAMAGE.
2838ddd4d3SKip Macy 
2938ddd4d3SKip Macy ***************************************************************************/
3038ddd4d3SKip Macy 
3138ddd4d3SKip Macy #include <sys/cdefs.h>
3238ddd4d3SKip Macy #include <cxgb_include.h>
3338ddd4d3SKip Macy 
3438ddd4d3SKip Macy #undef msleep
3538ddd4d3SKip Macy #define msleep t3_os_sleep
3638ddd4d3SKip Macy 
3738ddd4d3SKip Macy /* TN1010 PHY specific registers. */
3838ddd4d3SKip Macy enum {
3938ddd4d3SKip Macy 	TN1010_VEND1_STAT = 1,
4038ddd4d3SKip Macy };
4138ddd4d3SKip Macy 
4238ddd4d3SKip Macy /* IEEE auto-negotiation 10GBASE-T registers */
4338ddd4d3SKip Macy enum {
4438ddd4d3SKip Macy 	ANEG_ADVER    = 16,
4538ddd4d3SKip Macy 	ANEG_LPA      = 19,
4638ddd4d3SKip Macy 	ANEG_10G_CTRL = 32,
4738ddd4d3SKip Macy 	ANEG_10G_STAT = 33
4838ddd4d3SKip Macy };
4938ddd4d3SKip Macy 
5038ddd4d3SKip Macy #define ADVERTISE_ENPAGE      (1 << 12)
5138ddd4d3SKip Macy #define ADVERTISE_10000FULL   (1 << 12)
5238ddd4d3SKip Macy #define ADVERTISE_LOOP_TIMING (1 << 0)
5338ddd4d3SKip Macy 
5438ddd4d3SKip Macy /* vendor specific status register fields */
5538ddd4d3SKip Macy #define F_XS_LANE_ALIGN_STAT (1 << 0)
5638ddd4d3SKip Macy #define F_PCS_BLK_LOCK       (1 << 1)
5738ddd4d3SKip Macy #define F_PMD_SIGNAL_OK      (1 << 2)
5838ddd4d3SKip Macy #define F_LINK_STAT          (1 << 3)
5938ddd4d3SKip Macy #define F_ANEG_SPEED_1G      (1 << 4)
6038ddd4d3SKip Macy #define F_ANEG_MASTER        (1 << 5)
6138ddd4d3SKip Macy 
6238ddd4d3SKip Macy #define S_ANEG_STAT    6
6338ddd4d3SKip Macy #define M_ANEG_STAT    0x3
6438ddd4d3SKip Macy #define G_ANEG_STAT(x) (((x) >> S_ANEG_STAT) & M_ANEG_STAT)
6538ddd4d3SKip Macy 
6638ddd4d3SKip Macy enum {                        /* autonegotiation status */
6738ddd4d3SKip Macy 	ANEG_IN_PROGR = 0,
6838ddd4d3SKip Macy 	ANEG_COMPLETE = 1,
6938ddd4d3SKip Macy 	ANEG_FAILED   = 3
7038ddd4d3SKip Macy };
7138ddd4d3SKip Macy 
7238ddd4d3SKip Macy /*
7338ddd4d3SKip Macy  * Reset the PHY.  May take up to 500ms to complete.
7438ddd4d3SKip Macy  */
tn1010_reset(struct cphy * phy,int wait)7538ddd4d3SKip Macy static int tn1010_reset(struct cphy *phy, int wait)
7638ddd4d3SKip Macy {
7738ddd4d3SKip Macy 	int err = t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait);
7838ddd4d3SKip Macy 	msleep(500);
7938ddd4d3SKip Macy 	return err;
8038ddd4d3SKip Macy }
8138ddd4d3SKip Macy 
tn1010_power_down(struct cphy * phy,int enable)8238ddd4d3SKip Macy static int tn1010_power_down(struct cphy *phy, int enable)
8338ddd4d3SKip Macy {
8438ddd4d3SKip Macy 	return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
8538ddd4d3SKip Macy 				   BMCR_PDOWN, enable ? BMCR_PDOWN : 0);
8638ddd4d3SKip Macy }
8738ddd4d3SKip Macy 
tn1010_autoneg_enable(struct cphy * phy)8838ddd4d3SKip Macy static int tn1010_autoneg_enable(struct cphy *phy)
8938ddd4d3SKip Macy {
9038ddd4d3SKip Macy 	int err;
9138ddd4d3SKip Macy 
9238ddd4d3SKip Macy 	err = tn1010_power_down(phy, 0);
9338ddd4d3SKip Macy 	if (!err)
9438ddd4d3SKip Macy 		err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, MII_BMCR, 0,
9538ddd4d3SKip Macy 					  BMCR_ANENABLE | BMCR_ANRESTART);
9638ddd4d3SKip Macy 	return err;
9738ddd4d3SKip Macy }
9838ddd4d3SKip Macy 
tn1010_autoneg_restart(struct cphy * phy)9938ddd4d3SKip Macy static int tn1010_autoneg_restart(struct cphy *phy)
10038ddd4d3SKip Macy {
10138ddd4d3SKip Macy 	int err;
10238ddd4d3SKip Macy 
10338ddd4d3SKip Macy 	err = tn1010_power_down(phy, 0);
10438ddd4d3SKip Macy 	if (!err)
10538ddd4d3SKip Macy 		err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, MII_BMCR, 0,
10638ddd4d3SKip Macy 					  BMCR_ANRESTART);
10738ddd4d3SKip Macy 	return err;
10838ddd4d3SKip Macy }
10938ddd4d3SKip Macy 
tn1010_advertise(struct cphy * phy,unsigned int advert)11038ddd4d3SKip Macy static int tn1010_advertise(struct cphy *phy, unsigned int advert)
11138ddd4d3SKip Macy {
11238ddd4d3SKip Macy 	int err, val;
11338ddd4d3SKip Macy 
11438ddd4d3SKip Macy 	if (!(advert & ADVERTISED_1000baseT_Full))
11538ddd4d3SKip Macy 		return -EINVAL;               /* PHY can't disable 1000BASE-T */
11638ddd4d3SKip Macy 
11738ddd4d3SKip Macy 	val = ADVERTISE_CSMA | ADVERTISE_ENPAGE | ADVERTISE_NPAGE;
11838ddd4d3SKip Macy 	if (advert & ADVERTISED_Pause)
11938ddd4d3SKip Macy 		val |= ADVERTISE_PAUSE_CAP;
12038ddd4d3SKip Macy 	if (advert & ADVERTISED_Asym_Pause)
12138ddd4d3SKip Macy 		val |= ADVERTISE_PAUSE_ASYM;
12238ddd4d3SKip Macy 	err = mdio_write(phy, MDIO_DEV_ANEG, ANEG_ADVER, val);
12338ddd4d3SKip Macy 	if (err)
12438ddd4d3SKip Macy 		return err;
12538ddd4d3SKip Macy 
12638ddd4d3SKip Macy 	val = (advert & ADVERTISED_10000baseT_Full) ? ADVERTISE_10000FULL : 0;
12738ddd4d3SKip Macy 	return mdio_write(phy, MDIO_DEV_ANEG, ANEG_10G_CTRL, val |
12838ddd4d3SKip Macy 			  ADVERTISE_LOOP_TIMING);
12938ddd4d3SKip Macy }
13038ddd4d3SKip Macy 
tn1010_get_link_status(struct cphy * phy,int * link_state,int * speed,int * duplex,int * fc)131a5eb009bSNavdeep Parhar static int tn1010_get_link_status(struct cphy *phy, int *link_state,
13238ddd4d3SKip Macy 				  int *speed, int *duplex, int *fc)
13338ddd4d3SKip Macy {
13438ddd4d3SKip Macy 	unsigned int status, lpa, adv;
13538ddd4d3SKip Macy 	int err, sp = -1, pause = 0;
13638ddd4d3SKip Macy 
13738ddd4d3SKip Macy 	err = mdio_read(phy, MDIO_DEV_VEND1, TN1010_VEND1_STAT, &status);
13838ddd4d3SKip Macy 	if (err)
13938ddd4d3SKip Macy 		return err;
14038ddd4d3SKip Macy 
141a5eb009bSNavdeep Parhar 	if (link_state)
142a5eb009bSNavdeep Parhar 		*link_state = status & F_LINK_STAT ? PHY_LINK_UP :
143a5eb009bSNavdeep Parhar 		    PHY_LINK_DOWN;
14438ddd4d3SKip Macy 
14538ddd4d3SKip Macy 	if (G_ANEG_STAT(status) == ANEG_COMPLETE) {
14638ddd4d3SKip Macy 		sp = (status & F_ANEG_SPEED_1G) ? SPEED_1000 : SPEED_10000;
14738ddd4d3SKip Macy 
14838ddd4d3SKip Macy 		if (fc) {
14938ddd4d3SKip Macy 			err = mdio_read(phy, MDIO_DEV_ANEG, ANEG_LPA, &lpa);
15038ddd4d3SKip Macy 			if (!err)
15138ddd4d3SKip Macy 				err = mdio_read(phy, MDIO_DEV_ANEG, ANEG_ADVER,
15238ddd4d3SKip Macy 						&adv);
15338ddd4d3SKip Macy 			if (err)
15438ddd4d3SKip Macy 				return err;
15538ddd4d3SKip Macy 
15638ddd4d3SKip Macy 			if (lpa & adv & ADVERTISE_PAUSE_CAP)
15738ddd4d3SKip Macy 				pause = PAUSE_RX | PAUSE_TX;
15838ddd4d3SKip Macy 			else if ((lpa & ADVERTISE_PAUSE_CAP) &&
15938ddd4d3SKip Macy 				 (lpa & ADVERTISE_PAUSE_ASYM) &&
16038ddd4d3SKip Macy 				 (adv & ADVERTISE_PAUSE_ASYM))
16138ddd4d3SKip Macy 				pause = PAUSE_TX;
16238ddd4d3SKip Macy 			else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
16338ddd4d3SKip Macy 				 (adv & ADVERTISE_PAUSE_CAP))
16438ddd4d3SKip Macy 				pause = PAUSE_RX;
16538ddd4d3SKip Macy 		}
16638ddd4d3SKip Macy 	}
16738ddd4d3SKip Macy 	if (speed)
16838ddd4d3SKip Macy 		*speed = sp;
16938ddd4d3SKip Macy 	if (duplex)
17038ddd4d3SKip Macy 		*duplex = DUPLEX_FULL;
17138ddd4d3SKip Macy 	if (fc)
17238ddd4d3SKip Macy 		*fc = pause;
17338ddd4d3SKip Macy 	return 0;
17438ddd4d3SKip Macy }
17538ddd4d3SKip Macy 
tn1010_set_speed_duplex(struct cphy * phy,int speed,int duplex)17638ddd4d3SKip Macy static int tn1010_set_speed_duplex(struct cphy *phy, int speed, int duplex)
17738ddd4d3SKip Macy {
17838ddd4d3SKip Macy 	return -EINVAL;    /* require autoneg */
17938ddd4d3SKip Macy }
18038ddd4d3SKip Macy 
18138ddd4d3SKip Macy #ifdef C99_NOT_SUPPORTED
18238ddd4d3SKip Macy static struct cphy_ops tn1010_ops = {
18338ddd4d3SKip Macy 	tn1010_reset,
18438ddd4d3SKip Macy 	t3_phy_lasi_intr_enable,
18538ddd4d3SKip Macy 	t3_phy_lasi_intr_disable,
18638ddd4d3SKip Macy 	t3_phy_lasi_intr_clear,
18738ddd4d3SKip Macy 	t3_phy_lasi_intr_handler,
18838ddd4d3SKip Macy 	tn1010_autoneg_enable,
18938ddd4d3SKip Macy 	tn1010_autoneg_restart,
19038ddd4d3SKip Macy 	tn1010_advertise,
19138ddd4d3SKip Macy 	NULL,
19238ddd4d3SKip Macy 	tn1010_set_speed_duplex,
19338ddd4d3SKip Macy 	tn1010_get_link_status,
19438ddd4d3SKip Macy 	tn1010_power_down,
19538ddd4d3SKip Macy };
19638ddd4d3SKip Macy #else
19738ddd4d3SKip Macy static struct cphy_ops tn1010_ops = {
19838ddd4d3SKip Macy 	.reset             = tn1010_reset,
19938ddd4d3SKip Macy 	.intr_enable       = t3_phy_lasi_intr_enable,
20038ddd4d3SKip Macy 	.intr_disable      = t3_phy_lasi_intr_disable,
20138ddd4d3SKip Macy 	.intr_clear        = t3_phy_lasi_intr_clear,
20238ddd4d3SKip Macy 	.intr_handler      = t3_phy_lasi_intr_handler,
20338ddd4d3SKip Macy 	.autoneg_enable    = tn1010_autoneg_enable,
20438ddd4d3SKip Macy 	.autoneg_restart   = tn1010_autoneg_restart,
20538ddd4d3SKip Macy 	.advertise         = tn1010_advertise,
20638ddd4d3SKip Macy 	.set_speed_duplex  = tn1010_set_speed_duplex,
20738ddd4d3SKip Macy 	.get_link_status   = tn1010_get_link_status,
20838ddd4d3SKip Macy 	.power_down        = tn1010_power_down,
20938ddd4d3SKip Macy };
21038ddd4d3SKip Macy #endif
21138ddd4d3SKip Macy 
t3_tn1010_phy_prep(pinfo_t * pinfo,int phy_addr,const struct mdio_ops * mdio_ops)212c01f2b83SNavdeep Parhar int t3_tn1010_phy_prep(pinfo_t *pinfo, int phy_addr,
21338ddd4d3SKip Macy 		       const struct mdio_ops *mdio_ops)
21438ddd4d3SKip Macy {
215c01f2b83SNavdeep Parhar 	cphy_init(&pinfo->phy, pinfo->adapter, pinfo, phy_addr, &tn1010_ops, mdio_ops,
21638ddd4d3SKip Macy 		  SUPPORTED_1000baseT_Full | SUPPORTED_10000baseT_Full |
21738ddd4d3SKip Macy 		  SUPPORTED_Autoneg | SUPPORTED_AUI | SUPPORTED_TP,
21838ddd4d3SKip Macy 		  "1000/10GBASE-T");
21938ddd4d3SKip Macy 	msleep(500);    /* PHY needs up to 500ms to start responding to MDIO */
22038ddd4d3SKip Macy 	return 0;
22138ddd4d3SKip Macy }
222