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