1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. */ |
3 | |
4 | #include <linux/ethtool.h> |
5 | #include <linux/iopoll.h> |
6 | #include <linux/pci.h> |
7 | #include <linux/phy.h> |
8 | |
9 | #include "../libwx/wx_type.h" |
10 | #include "../libwx/wx_hw.h" |
11 | #include "ngbe_type.h" |
12 | #include "ngbe_mdio.h" |
13 | |
14 | static int ngbe_phy_read_reg_internal(struct mii_bus *bus, int phy_addr, int regnum) |
15 | { |
16 | struct wx *wx = bus->priv; |
17 | |
18 | if (phy_addr != 0) |
19 | return 0xffff; |
20 | return (u16)rd32(wx, NGBE_PHY_CONFIG(regnum)); |
21 | } |
22 | |
23 | static int ngbe_phy_write_reg_internal(struct mii_bus *bus, int phy_addr, int regnum, u16 value) |
24 | { |
25 | struct wx *wx = bus->priv; |
26 | |
27 | if (phy_addr == 0) |
28 | wr32(wx, NGBE_PHY_CONFIG(regnum), value); |
29 | return 0; |
30 | } |
31 | |
32 | static int ngbe_phy_read_reg_c22(struct mii_bus *bus, int phy_addr, int regnum) |
33 | { |
34 | struct wx *wx = bus->priv; |
35 | u16 phy_data; |
36 | |
37 | if (wx->mac_type == em_mac_type_mdi) |
38 | phy_data = ngbe_phy_read_reg_internal(bus, phy_addr, regnum); |
39 | else |
40 | phy_data = wx_phy_read_reg_mdi_c22(bus, phy_addr, regnum); |
41 | |
42 | return phy_data; |
43 | } |
44 | |
45 | static int ngbe_phy_write_reg_c22(struct mii_bus *bus, int phy_addr, |
46 | int regnum, u16 value) |
47 | { |
48 | struct wx *wx = bus->priv; |
49 | int ret; |
50 | |
51 | if (wx->mac_type == em_mac_type_mdi) |
52 | ret = ngbe_phy_write_reg_internal(bus, phy_addr, regnum, value); |
53 | else |
54 | ret = wx_phy_write_reg_mdi_c22(bus, phy_addr, regnum, value); |
55 | |
56 | return ret; |
57 | } |
58 | |
59 | static void ngbe_mac_config(struct phylink_config *config, unsigned int mode, |
60 | const struct phylink_link_state *state) |
61 | { |
62 | } |
63 | |
64 | static void ngbe_mac_link_down(struct phylink_config *config, |
65 | unsigned int mode, phy_interface_t interface) |
66 | { |
67 | } |
68 | |
69 | static void ngbe_mac_link_up(struct phylink_config *config, |
70 | struct phy_device *phy, |
71 | unsigned int mode, phy_interface_t interface, |
72 | int speed, int duplex, |
73 | bool tx_pause, bool rx_pause) |
74 | { |
75 | struct wx *wx = phylink_to_wx(config); |
76 | u32 lan_speed, reg; |
77 | |
78 | wx_fc_enable(wx, tx_pause, rx_pause); |
79 | |
80 | switch (speed) { |
81 | case SPEED_10: |
82 | lan_speed = 0; |
83 | break; |
84 | case SPEED_100: |
85 | lan_speed = 1; |
86 | break; |
87 | case SPEED_1000: |
88 | default: |
89 | lan_speed = 2; |
90 | break; |
91 | } |
92 | |
93 | wr32m(wx, NGBE_CFG_LAN_SPEED, mask: 0x3, field: lan_speed); |
94 | |
95 | reg = rd32(wx, WX_MAC_TX_CFG); |
96 | reg &= ~WX_MAC_TX_CFG_SPEED_MASK; |
97 | reg |= WX_MAC_TX_CFG_SPEED_1G | WX_MAC_TX_CFG_TE; |
98 | wr32(wx, WX_MAC_TX_CFG, reg); |
99 | |
100 | /* Re configure MAC Rx */ |
101 | reg = rd32(wx, WX_MAC_RX_CFG); |
102 | wr32(wx, WX_MAC_RX_CFG, reg); |
103 | wr32(wx, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR); |
104 | reg = rd32(wx, WX_MAC_WDG_TIMEOUT); |
105 | wr32(wx, WX_MAC_WDG_TIMEOUT, reg); |
106 | } |
107 | |
108 | static const struct phylink_mac_ops ngbe_mac_ops = { |
109 | .mac_config = ngbe_mac_config, |
110 | .mac_link_down = ngbe_mac_link_down, |
111 | .mac_link_up = ngbe_mac_link_up, |
112 | }; |
113 | |
114 | static int ngbe_phylink_init(struct wx *wx) |
115 | { |
116 | struct phylink_config *config; |
117 | phy_interface_t phy_mode; |
118 | struct phylink *phylink; |
119 | |
120 | config = &wx->phylink_config; |
121 | config->dev = &wx->netdev->dev; |
122 | config->type = PHYLINK_NETDEV; |
123 | config->mac_capabilities = MAC_1000FD | MAC_100FD | MAC_10FD | |
124 | MAC_SYM_PAUSE | MAC_ASYM_PAUSE; |
125 | config->mac_managed_pm = true; |
126 | |
127 | phy_mode = PHY_INTERFACE_MODE_RGMII_ID; |
128 | __set_bit(PHY_INTERFACE_MODE_RGMII_ID, config->supported_interfaces); |
129 | |
130 | phylink = phylink_create(config, NULL, phy_mode, &ngbe_mac_ops); |
131 | if (IS_ERR(ptr: phylink)) |
132 | return PTR_ERR(ptr: phylink); |
133 | |
134 | wx->phylink = phylink; |
135 | |
136 | return 0; |
137 | } |
138 | |
139 | int ngbe_mdio_init(struct wx *wx) |
140 | { |
141 | struct pci_dev *pdev = wx->pdev; |
142 | struct mii_bus *mii_bus; |
143 | int ret; |
144 | |
145 | mii_bus = devm_mdiobus_alloc(dev: &pdev->dev); |
146 | if (!mii_bus) |
147 | return -ENOMEM; |
148 | |
149 | mii_bus->name = "ngbe_mii_bus" ; |
150 | mii_bus->read = ngbe_phy_read_reg_c22; |
151 | mii_bus->write = ngbe_phy_write_reg_c22; |
152 | mii_bus->phy_mask = GENMASK(31, 4); |
153 | mii_bus->parent = &pdev->dev; |
154 | mii_bus->priv = wx; |
155 | |
156 | if (wx->mac_type == em_mac_type_rgmii) { |
157 | mii_bus->read_c45 = wx_phy_read_reg_mdi_c45; |
158 | mii_bus->write_c45 = wx_phy_write_reg_mdi_c45; |
159 | } |
160 | |
161 | snprintf(buf: mii_bus->id, MII_BUS_ID_SIZE, fmt: "ngbe-%x" , pci_dev_id(dev: pdev)); |
162 | ret = devm_mdiobus_register(&pdev->dev, mii_bus); |
163 | if (ret) |
164 | return ret; |
165 | |
166 | wx->phydev = phy_find_first(bus: mii_bus); |
167 | if (!wx->phydev) |
168 | return -ENODEV; |
169 | |
170 | phy_attached_info(phydev: wx->phydev); |
171 | |
172 | wx->link = 0; |
173 | wx->speed = 0; |
174 | wx->duplex = 0; |
175 | |
176 | ret = ngbe_phylink_init(wx); |
177 | if (ret) { |
178 | wx_err(wx, "failed to init phylink: %d\n" , ret); |
179 | return ret; |
180 | } |
181 | |
182 | return 0; |
183 | } |
184 | |