OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | /* This program is free software; you can redistribute it and/or modify |
2 | * it under the terms of the GNU General Public License as published by |
||
3 | * the Free Software Foundation; version 2 of the License |
||
4 | * |
||
5 | * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org> |
||
6 | * Copyright (C) 2009-2015 Felix Fietkau <nbd@nbd.name> |
||
7 | * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com> |
||
8 | */ |
||
9 | |||
10 | #include <linux/module.h> |
||
11 | #include <linux/kernel.h> |
||
12 | #include <linux/phy.h> |
||
13 | #include <linux/of_net.h> |
||
14 | #include <linux/of_mdio.h> |
||
15 | |||
16 | #include "mtk_eth_soc.h" |
||
17 | #include "mdio.h" |
||
18 | |||
19 | static int fe_mdio_reset(struct mii_bus *bus) |
||
20 | { |
||
21 | /* TODO */ |
||
22 | return 0; |
||
23 | } |
||
24 | |||
25 | static void fe_phy_link_adjust(struct net_device *dev) |
||
26 | { |
||
27 | struct fe_priv *priv = netdev_priv(dev); |
||
28 | unsigned long flags; |
||
29 | int i; |
||
30 | |||
31 | spin_lock_irqsave(&priv->phy->lock, flags); |
||
32 | for (i = 0; i < 8; i++) { |
||
33 | if (priv->phy->phy_node[i]) { |
||
34 | struct phy_device *phydev = priv->phy->phy[i]; |
||
35 | int status_change = 0; |
||
36 | |||
37 | if (phydev->link) |
||
38 | if (priv->phy->duplex[i] != phydev->duplex || |
||
39 | priv->phy->speed[i] != phydev->speed) |
||
40 | status_change = 1; |
||
41 | |||
42 | if (phydev->link != priv->link[i]) |
||
43 | status_change = 1; |
||
44 | |||
45 | switch (phydev->speed) { |
||
46 | case SPEED_1000: |
||
47 | case SPEED_100: |
||
48 | case SPEED_10: |
||
49 | priv->link[i] = phydev->link; |
||
50 | priv->phy->duplex[i] = phydev->duplex; |
||
51 | priv->phy->speed[i] = phydev->speed; |
||
52 | |||
53 | if (status_change && |
||
54 | priv->soc->mdio_adjust_link) |
||
55 | priv->soc->mdio_adjust_link(priv, i); |
||
56 | break; |
||
57 | } |
||
58 | } |
||
59 | } |
||
60 | spin_unlock_irqrestore(&priv->phy->lock, flags); |
||
61 | } |
||
62 | |||
63 | int fe_connect_phy_node(struct fe_priv *priv, struct device_node *phy_node) |
||
64 | { |
||
65 | const __be32 *_port = NULL; |
||
66 | struct phy_device *phydev; |
||
67 | int phy_mode, port; |
||
68 | |||
69 | _port = of_get_property(phy_node, "reg", NULL); |
||
70 | |||
71 | if (!_port || (be32_to_cpu(*_port) >= 0x20)) { |
||
72 | pr_err("%s: invalid port id\n", phy_node->name); |
||
73 | return -EINVAL; |
||
74 | } |
||
75 | port = be32_to_cpu(*_port); |
||
76 | phy_mode = of_get_phy_mode(phy_node); |
||
77 | if (phy_mode < 0) { |
||
78 | dev_err(priv->dev, "incorrect phy-mode %d\n", phy_mode); |
||
79 | priv->phy->phy_node[port] = NULL; |
||
80 | return -EINVAL; |
||
81 | } |
||
82 | |||
83 | phydev = of_phy_connect(priv->netdev, phy_node, fe_phy_link_adjust, |
||
84 | 0, phy_mode); |
||
85 | if (!phydev) { |
||
86 | dev_err(priv->dev, "could not connect to PHY\n"); |
||
87 | priv->phy->phy_node[port] = NULL; |
||
88 | return -ENODEV; |
||
89 | } |
||
90 | |||
91 | phydev->supported &= PHY_GBIT_FEATURES; |
||
92 | phydev->advertising = phydev->supported; |
||
93 | phydev->no_auto_carrier_off = 1; |
||
94 | |||
95 | dev_info(priv->dev, |
||
96 | "connected port %d to PHY at %s [uid=%08x, driver=%s]\n", |
||
97 | port, dev_name(&phydev->mdio.dev), phydev->phy_id, |
||
98 | phydev->drv->name); |
||
99 | |||
100 | priv->phy->phy[port] = phydev; |
||
101 | priv->link[port] = 0; |
||
102 | |||
103 | return 0; |
||
104 | } |
||
105 | |||
106 | static void phy_init(struct fe_priv *priv, struct phy_device *phy) |
||
107 | { |
||
108 | phy_attach(priv->netdev, dev_name(&phy->mdio.dev), PHY_INTERFACE_MODE_MII); |
||
109 | |||
110 | phy->autoneg = AUTONEG_ENABLE; |
||
111 | phy->speed = 0; |
||
112 | phy->duplex = 0; |
||
113 | phy->supported &= IS_ENABLED(CONFIG_NET_MEDIATEK_MDIO_MT7620) ? |
||
114 | PHY_GBIT_FEATURES : PHY_BASIC_FEATURES; |
||
115 | phy->advertising = phy->supported | ADVERTISED_Autoneg; |
||
116 | |||
117 | phy_start_aneg(phy); |
||
118 | } |
||
119 | |||
120 | static int fe_phy_connect(struct fe_priv *priv) |
||
121 | { |
||
122 | int i; |
||
123 | |||
124 | for (i = 0; i < 8; i++) { |
||
125 | if (priv->phy->phy_node[i]) { |
||
126 | if (!priv->phy_dev) { |
||
127 | priv->phy_dev = priv->phy->phy[i]; |
||
128 | priv->phy_flags = FE_PHY_FLAG_PORT; |
||
129 | } |
||
130 | } else if (priv->mii_bus && mdiobus_get_phy(priv->mii_bus, i)) { |
||
131 | phy_init(priv, mdiobus_get_phy(priv->mii_bus, i)); |
||
132 | if (!priv->phy_dev) { |
||
133 | priv->phy_dev = mdiobus_get_phy(priv->mii_bus, i); |
||
134 | priv->phy_flags = FE_PHY_FLAG_ATTACH; |
||
135 | } |
||
136 | } |
||
137 | } |
||
138 | |||
139 | return 0; |
||
140 | } |
||
141 | |||
142 | static void fe_phy_disconnect(struct fe_priv *priv) |
||
143 | { |
||
144 | unsigned long flags; |
||
145 | int i; |
||
146 | |||
147 | for (i = 0; i < 8; i++) |
||
148 | if (priv->phy->phy_fixed[i]) { |
||
149 | spin_lock_irqsave(&priv->phy->lock, flags); |
||
150 | priv->link[i] = 0; |
||
151 | if (priv->soc->mdio_adjust_link) |
||
152 | priv->soc->mdio_adjust_link(priv, i); |
||
153 | spin_unlock_irqrestore(&priv->phy->lock, flags); |
||
154 | } else if (priv->phy->phy[i]) { |
||
155 | phy_disconnect(priv->phy->phy[i]); |
||
156 | } else if (priv->mii_bus && mdiobus_get_phy(priv->mii_bus, i)) { |
||
157 | phy_detach(mdiobus_get_phy(priv->mii_bus, i)); |
||
158 | } |
||
159 | } |
||
160 | |||
161 | static void fe_phy_start(struct fe_priv *priv) |
||
162 | { |
||
163 | unsigned long flags; |
||
164 | int i; |
||
165 | |||
166 | for (i = 0; i < 8; i++) { |
||
167 | if (priv->phy->phy_fixed[i]) { |
||
168 | spin_lock_irqsave(&priv->phy->lock, flags); |
||
169 | priv->link[i] = 1; |
||
170 | if (priv->soc->mdio_adjust_link) |
||
171 | priv->soc->mdio_adjust_link(priv, i); |
||
172 | spin_unlock_irqrestore(&priv->phy->lock, flags); |
||
173 | } else if (priv->phy->phy[i]) { |
||
174 | phy_start(priv->phy->phy[i]); |
||
175 | } |
||
176 | } |
||
177 | } |
||
178 | |||
179 | static void fe_phy_stop(struct fe_priv *priv) |
||
180 | { |
||
181 | unsigned long flags; |
||
182 | int i; |
||
183 | |||
184 | for (i = 0; i < 8; i++) |
||
185 | if (priv->phy->phy_fixed[i]) { |
||
186 | spin_lock_irqsave(&priv->phy->lock, flags); |
||
187 | priv->link[i] = 0; |
||
188 | if (priv->soc->mdio_adjust_link) |
||
189 | priv->soc->mdio_adjust_link(priv, i); |
||
190 | spin_unlock_irqrestore(&priv->phy->lock, flags); |
||
191 | } else if (priv->phy->phy[i]) { |
||
192 | phy_stop(priv->phy->phy[i]); |
||
193 | } |
||
194 | } |
||
195 | |||
196 | static struct fe_phy phy_ralink = { |
||
197 | .connect = fe_phy_connect, |
||
198 | .disconnect = fe_phy_disconnect, |
||
199 | .start = fe_phy_start, |
||
200 | .stop = fe_phy_stop, |
||
201 | }; |
||
202 | |||
203 | int fe_mdio_init(struct fe_priv *priv) |
||
204 | { |
||
205 | struct device_node *mii_np; |
||
206 | int err; |
||
207 | |||
208 | if (!priv->soc->mdio_read || !priv->soc->mdio_write) |
||
209 | return 0; |
||
210 | |||
211 | spin_lock_init(&phy_ralink.lock); |
||
212 | priv->phy = &phy_ralink; |
||
213 | |||
214 | mii_np = of_get_child_by_name(priv->dev->of_node, "mdio-bus"); |
||
215 | if (!mii_np) { |
||
216 | dev_err(priv->dev, "no %s child node found", "mdio-bus"); |
||
217 | return -ENODEV; |
||
218 | } |
||
219 | |||
220 | if (!of_device_is_available(mii_np)) { |
||
221 | err = 0; |
||
222 | goto err_put_node; |
||
223 | } |
||
224 | |||
225 | priv->mii_bus = mdiobus_alloc(); |
||
226 | if (!priv->mii_bus) { |
||
227 | err = -ENOMEM; |
||
228 | goto err_put_node; |
||
229 | } |
||
230 | |||
231 | priv->mii_bus->name = "mdio"; |
||
232 | priv->mii_bus->read = priv->soc->mdio_read; |
||
233 | priv->mii_bus->write = priv->soc->mdio_write; |
||
234 | priv->mii_bus->reset = fe_mdio_reset; |
||
235 | priv->mii_bus->priv = priv; |
||
236 | priv->mii_bus->parent = priv->dev; |
||
237 | |||
238 | snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s", mii_np->name); |
||
239 | err = of_mdiobus_register(priv->mii_bus, mii_np); |
||
240 | if (err) |
||
241 | goto err_free_bus; |
||
242 | |||
243 | return 0; |
||
244 | |||
245 | err_free_bus: |
||
246 | kfree(priv->mii_bus); |
||
247 | err_put_node: |
||
248 | of_node_put(mii_np); |
||
249 | priv->mii_bus = NULL; |
||
250 | return err; |
||
251 | } |
||
252 | |||
253 | void fe_mdio_cleanup(struct fe_priv *priv) |
||
254 | { |
||
255 | if (!priv->mii_bus) |
||
256 | return; |
||
257 | |||
258 | mdiobus_unregister(priv->mii_bus); |
||
259 | of_node_put(priv->mii_bus->dev.of_node); |
||
260 | kfree(priv->mii_bus); |
||
261 | } |