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