xref: /openbmc/linux/drivers/net/ethernet/ti/netcp_sgmii.c (revision 6f8d3f33385cf809eaf816e56302f84a66e2d003)
1*6f8d3f33SWingman Kwok /*
2*6f8d3f33SWingman Kwok  * SGMI module initialisation
3*6f8d3f33SWingman Kwok  *
4*6f8d3f33SWingman Kwok  * Copyright (C) 2014 Texas Instruments Incorporated
5*6f8d3f33SWingman Kwok  * Authors:	Sandeep Nair <sandeep_n@ti.com>
6*6f8d3f33SWingman Kwok  *		Sandeep Paulraj <s-paulraj@ti.com>
7*6f8d3f33SWingman Kwok  *		Wingman Kwok <w-kwok2@ti.com>
8*6f8d3f33SWingman Kwok  *
9*6f8d3f33SWingman Kwok  * This program is free software; you can redistribute it and/or
10*6f8d3f33SWingman Kwok  * modify it under the terms of the GNU General Public License as
11*6f8d3f33SWingman Kwok  * published by the Free Software Foundation version 2.
12*6f8d3f33SWingman Kwok  *
13*6f8d3f33SWingman Kwok  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
14*6f8d3f33SWingman Kwok  * kind, whether express or implied; without even the implied warranty
15*6f8d3f33SWingman Kwok  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16*6f8d3f33SWingman Kwok  * GNU General Public License for more details.
17*6f8d3f33SWingman Kwok  */
18*6f8d3f33SWingman Kwok 
19*6f8d3f33SWingman Kwok #include "netcp.h"
20*6f8d3f33SWingman Kwok 
21*6f8d3f33SWingman Kwok #define SGMII_REG_STATUS_LOCK		BIT(4)
22*6f8d3f33SWingman Kwok #define	SGMII_REG_STATUS_LINK		BIT(0)
23*6f8d3f33SWingman Kwok #define SGMII_REG_STATUS_AUTONEG	BIT(2)
24*6f8d3f33SWingman Kwok #define SGMII_REG_CONTROL_AUTONEG	BIT(0)
25*6f8d3f33SWingman Kwok 
26*6f8d3f33SWingman Kwok #define SGMII23_OFFSET(x)	((x - 2) * 0x100)
27*6f8d3f33SWingman Kwok #define SGMII_OFFSET(x)		((x <= 1) ? (x * 0x100) : (SGMII23_OFFSET(x)))
28*6f8d3f33SWingman Kwok 
29*6f8d3f33SWingman Kwok /* SGMII registers */
30*6f8d3f33SWingman Kwok #define SGMII_SRESET_REG(x)   (SGMII_OFFSET(x) + 0x004)
31*6f8d3f33SWingman Kwok #define SGMII_CTL_REG(x)      (SGMII_OFFSET(x) + 0x010)
32*6f8d3f33SWingman Kwok #define SGMII_STATUS_REG(x)   (SGMII_OFFSET(x) + 0x014)
33*6f8d3f33SWingman Kwok #define SGMII_MRADV_REG(x)    (SGMII_OFFSET(x) + 0x018)
34*6f8d3f33SWingman Kwok 
35*6f8d3f33SWingman Kwok static void sgmii_write_reg(void __iomem *base, int reg, u32 val)
36*6f8d3f33SWingman Kwok {
37*6f8d3f33SWingman Kwok 	writel(val, base + reg);
38*6f8d3f33SWingman Kwok }
39*6f8d3f33SWingman Kwok 
40*6f8d3f33SWingman Kwok static u32 sgmii_read_reg(void __iomem *base, int reg)
41*6f8d3f33SWingman Kwok {
42*6f8d3f33SWingman Kwok 	return readl(base + reg);
43*6f8d3f33SWingman Kwok }
44*6f8d3f33SWingman Kwok 
45*6f8d3f33SWingman Kwok static void sgmii_write_reg_bit(void __iomem *base, int reg, u32 val)
46*6f8d3f33SWingman Kwok {
47*6f8d3f33SWingman Kwok 	writel((readl(base + reg) | val), base + reg);
48*6f8d3f33SWingman Kwok }
49*6f8d3f33SWingman Kwok 
50*6f8d3f33SWingman Kwok /* port is 0 based */
51*6f8d3f33SWingman Kwok int netcp_sgmii_reset(void __iomem *sgmii_ofs, int port)
52*6f8d3f33SWingman Kwok {
53*6f8d3f33SWingman Kwok 	/* Soft reset */
54*6f8d3f33SWingman Kwok 	sgmii_write_reg_bit(sgmii_ofs, SGMII_SRESET_REG(port), 0x1);
55*6f8d3f33SWingman Kwok 	while (sgmii_read_reg(sgmii_ofs, SGMII_SRESET_REG(port)) != 0x0)
56*6f8d3f33SWingman Kwok 		;
57*6f8d3f33SWingman Kwok 	return 0;
58*6f8d3f33SWingman Kwok }
59*6f8d3f33SWingman Kwok 
60*6f8d3f33SWingman Kwok int netcp_sgmii_get_port_link(void __iomem *sgmii_ofs, int port)
61*6f8d3f33SWingman Kwok {
62*6f8d3f33SWingman Kwok 	u32 status = 0, link = 0;
63*6f8d3f33SWingman Kwok 
64*6f8d3f33SWingman Kwok 	status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port));
65*6f8d3f33SWingman Kwok 	if ((status & SGMII_REG_STATUS_LINK) != 0)
66*6f8d3f33SWingman Kwok 		link = 1;
67*6f8d3f33SWingman Kwok 	return link;
68*6f8d3f33SWingman Kwok }
69*6f8d3f33SWingman Kwok 
70*6f8d3f33SWingman Kwok int netcp_sgmii_config(void __iomem *sgmii_ofs, int port, u32 interface)
71*6f8d3f33SWingman Kwok {
72*6f8d3f33SWingman Kwok 	unsigned int i, status, mask;
73*6f8d3f33SWingman Kwok 	u32 mr_adv_ability;
74*6f8d3f33SWingman Kwok 	u32 control;
75*6f8d3f33SWingman Kwok 
76*6f8d3f33SWingman Kwok 	switch (interface) {
77*6f8d3f33SWingman Kwok 	case SGMII_LINK_MAC_MAC_AUTONEG:
78*6f8d3f33SWingman Kwok 		mr_adv_ability	= 0x9801;
79*6f8d3f33SWingman Kwok 		control		= 0x21;
80*6f8d3f33SWingman Kwok 		break;
81*6f8d3f33SWingman Kwok 
82*6f8d3f33SWingman Kwok 	case SGMII_LINK_MAC_PHY:
83*6f8d3f33SWingman Kwok 	case SGMII_LINK_MAC_PHY_NO_MDIO:
84*6f8d3f33SWingman Kwok 		mr_adv_ability	= 1;
85*6f8d3f33SWingman Kwok 		control		= 1;
86*6f8d3f33SWingman Kwok 		break;
87*6f8d3f33SWingman Kwok 
88*6f8d3f33SWingman Kwok 	case SGMII_LINK_MAC_MAC_FORCED:
89*6f8d3f33SWingman Kwok 		mr_adv_ability	= 0x9801;
90*6f8d3f33SWingman Kwok 		control		= 0x20;
91*6f8d3f33SWingman Kwok 		break;
92*6f8d3f33SWingman Kwok 
93*6f8d3f33SWingman Kwok 	case SGMII_LINK_MAC_FIBER:
94*6f8d3f33SWingman Kwok 		mr_adv_ability	= 0x20;
95*6f8d3f33SWingman Kwok 		control		= 0x1;
96*6f8d3f33SWingman Kwok 		break;
97*6f8d3f33SWingman Kwok 
98*6f8d3f33SWingman Kwok 	default:
99*6f8d3f33SWingman Kwok 		WARN_ONCE(1, "Invalid sgmii interface: %d\n", interface);
100*6f8d3f33SWingman Kwok 		return -EINVAL;
101*6f8d3f33SWingman Kwok 	}
102*6f8d3f33SWingman Kwok 
103*6f8d3f33SWingman Kwok 	sgmii_write_reg(sgmii_ofs, SGMII_CTL_REG(port), 0);
104*6f8d3f33SWingman Kwok 
105*6f8d3f33SWingman Kwok 	/* Wait for the SerDes pll to lock */
106*6f8d3f33SWingman Kwok 	for (i = 0; i < 1000; i++)  {
107*6f8d3f33SWingman Kwok 		usleep_range(1000, 2000);
108*6f8d3f33SWingman Kwok 		status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port));
109*6f8d3f33SWingman Kwok 		if ((status & SGMII_REG_STATUS_LOCK) != 0)
110*6f8d3f33SWingman Kwok 			break;
111*6f8d3f33SWingman Kwok 	}
112*6f8d3f33SWingman Kwok 
113*6f8d3f33SWingman Kwok 	if ((status & SGMII_REG_STATUS_LOCK) == 0)
114*6f8d3f33SWingman Kwok 		pr_err("serdes PLL not locked\n");
115*6f8d3f33SWingman Kwok 
116*6f8d3f33SWingman Kwok 	sgmii_write_reg(sgmii_ofs, SGMII_MRADV_REG(port), mr_adv_ability);
117*6f8d3f33SWingman Kwok 	sgmii_write_reg(sgmii_ofs, SGMII_CTL_REG(port), control);
118*6f8d3f33SWingman Kwok 
119*6f8d3f33SWingman Kwok 	mask = SGMII_REG_STATUS_LINK;
120*6f8d3f33SWingman Kwok 	if (control & SGMII_REG_CONTROL_AUTONEG)
121*6f8d3f33SWingman Kwok 		mask |= SGMII_REG_STATUS_AUTONEG;
122*6f8d3f33SWingman Kwok 
123*6f8d3f33SWingman Kwok 	for (i = 0; i < 1000; i++)  {
124*6f8d3f33SWingman Kwok 		usleep_range(200, 500);
125*6f8d3f33SWingman Kwok 		status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port));
126*6f8d3f33SWingman Kwok 		if ((status & mask) == mask)
127*6f8d3f33SWingman Kwok 			break;
128*6f8d3f33SWingman Kwok 	}
129*6f8d3f33SWingman Kwok 
130*6f8d3f33SWingman Kwok 	return 0;
131*6f8d3f33SWingman Kwok }
132