xref: /linux/drivers/net/phy/fixed_phy.c (revision 76cd8a2ea98a3d779748ec12744aaed0d85fba7b)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Fixed MDIO bus (MDIO bus emulation with fixed PHYs)
4  *
5  * Author: Vitaly Bordug <vbordug@ru.mvista.com>
6  *         Anton Vorontsov <avorontsov@ru.mvista.com>
7  *
8  * Copyright (c) 2006-2007 MontaVista Software, Inc.
9  */
10 
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/list.h>
14 #include <linux/mii.h>
15 #include <linux/phy.h>
16 #include <linux/phy_fixed.h>
17 #include <linux/err.h>
18 #include <linux/slab.h>
19 #include <linux/of.h>
20 #include <linux/idr.h>
21 #include <linux/netdevice.h>
22 #include <linux/linkmode.h>
23 
24 #include "swphy.h"
25 
26 struct fixed_mdio_bus {
27 	struct mii_bus *mii_bus;
28 	struct list_head phys;
29 };
30 
31 struct fixed_phy {
32 	int addr;
33 	struct phy_device *phydev;
34 	struct fixed_phy_status status;
35 	bool no_carrier;
36 	int (*link_update)(struct net_device *, struct fixed_phy_status *);
37 	struct list_head node;
38 };
39 
40 static struct fixed_mdio_bus platform_fmb = {
41 	.phys = LIST_HEAD_INIT(platform_fmb.phys),
42 };
43 
44 int fixed_phy_change_carrier(struct net_device *dev, bool new_carrier)
45 {
46 	struct fixed_mdio_bus *fmb = &platform_fmb;
47 	struct phy_device *phydev = dev->phydev;
48 	struct fixed_phy *fp;
49 
50 	if (!phydev || !phydev->mdio.bus)
51 		return -EINVAL;
52 
53 	list_for_each_entry(fp, &fmb->phys, node) {
54 		if (fp->addr == phydev->mdio.addr) {
55 			fp->no_carrier = !new_carrier;
56 			return 0;
57 		}
58 	}
59 	return -EINVAL;
60 }
61 EXPORT_SYMBOL_GPL(fixed_phy_change_carrier);
62 
63 static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
64 {
65 	struct fixed_mdio_bus *fmb = bus->priv;
66 	struct fixed_phy *fp;
67 
68 	list_for_each_entry(fp, &fmb->phys, node) {
69 		if (fp->addr == phy_addr) {
70 			fp->status.link = !fp->no_carrier;
71 
72 			/* Issue callback if user registered it. */
73 			if (fp->link_update)
74 				fp->link_update(fp->phydev->attached_dev,
75 						&fp->status);
76 
77 			return swphy_read_reg(reg_num, &fp->status);
78 		}
79 	}
80 
81 	return 0xFFFF;
82 }
83 
84 static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num,
85 			    u16 val)
86 {
87 	return 0;
88 }
89 
90 /*
91  * If something weird is required to be done with link/speed,
92  * network driver is able to assign a function to implement this.
93  * May be useful for PHY's that need to be software-driven.
94  */
95 int fixed_phy_set_link_update(struct phy_device *phydev,
96 			      int (*link_update)(struct net_device *,
97 						 struct fixed_phy_status *))
98 {
99 	struct fixed_mdio_bus *fmb = &platform_fmb;
100 	struct fixed_phy *fp;
101 
102 	if (!phydev || !phydev->mdio.bus)
103 		return -EINVAL;
104 
105 	list_for_each_entry(fp, &fmb->phys, node) {
106 		if (fp->addr == phydev->mdio.addr) {
107 			fp->link_update = link_update;
108 			fp->phydev = phydev;
109 			return 0;
110 		}
111 	}
112 
113 	return -ENOENT;
114 }
115 EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
116 
117 static int __fixed_phy_add(unsigned int irq, int phy_addr,
118 			   const struct fixed_phy_status *status)
119 {
120 	int ret;
121 	struct fixed_mdio_bus *fmb = &platform_fmb;
122 	struct fixed_phy *fp;
123 
124 	ret = swphy_validate_state(status);
125 	if (ret < 0)
126 		return ret;
127 
128 	fp = kzalloc(sizeof(*fp), GFP_KERNEL);
129 	if (!fp)
130 		return -ENOMEM;
131 
132 	if (irq != PHY_POLL)
133 		fmb->mii_bus->irq[phy_addr] = irq;
134 
135 	fp->addr = phy_addr;
136 	fp->status = *status;
137 
138 	list_add_tail(&fp->node, &fmb->phys);
139 
140 	return 0;
141 }
142 
143 void fixed_phy_add(const struct fixed_phy_status *status)
144 {
145 	__fixed_phy_add(PHY_POLL, 0, status);
146 }
147 EXPORT_SYMBOL_GPL(fixed_phy_add);
148 
149 static DEFINE_IDA(phy_fixed_ida);
150 
151 static void fixed_phy_del(int phy_addr)
152 {
153 	struct fixed_mdio_bus *fmb = &platform_fmb;
154 	struct fixed_phy *fp, *tmp;
155 
156 	list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
157 		if (fp->addr == phy_addr) {
158 			list_del(&fp->node);
159 			kfree(fp);
160 			ida_free(&phy_fixed_ida, phy_addr);
161 			return;
162 		}
163 	}
164 }
165 
166 struct phy_device *fixed_phy_register(const struct fixed_phy_status *status,
167 				      struct device_node *np)
168 {
169 	struct fixed_mdio_bus *fmb = &platform_fmb;
170 	struct phy_device *phy;
171 	int phy_addr;
172 	int ret;
173 
174 	if (!fmb->mii_bus || fmb->mii_bus->state != MDIOBUS_REGISTERED)
175 		return ERR_PTR(-EPROBE_DEFER);
176 
177 	/* Get the next available PHY address, up to PHY_MAX_ADDR */
178 	phy_addr = ida_alloc_max(&phy_fixed_ida, PHY_MAX_ADDR - 1, GFP_KERNEL);
179 	if (phy_addr < 0)
180 		return ERR_PTR(phy_addr);
181 
182 	ret = __fixed_phy_add(PHY_POLL, phy_addr, status);
183 	if (ret < 0) {
184 		ida_free(&phy_fixed_ida, phy_addr);
185 		return ERR_PTR(ret);
186 	}
187 
188 	phy = get_phy_device(fmb->mii_bus, phy_addr, false);
189 	if (IS_ERR(phy)) {
190 		fixed_phy_del(phy_addr);
191 		return ERR_PTR(-EINVAL);
192 	}
193 
194 	/* propagate the fixed link values to struct phy_device */
195 	phy->link = status->link;
196 	if (status->link) {
197 		phy->speed = status->speed;
198 		phy->duplex = status->duplex;
199 		phy->pause = status->pause;
200 		phy->asym_pause = status->asym_pause;
201 	}
202 
203 	of_node_get(np);
204 	phy->mdio.dev.of_node = np;
205 	phy->is_pseudo_fixed_link = true;
206 
207 	switch (status->speed) {
208 	case SPEED_1000:
209 		linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
210 				 phy->supported);
211 		linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
212 				 phy->supported);
213 		fallthrough;
214 	case SPEED_100:
215 		linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
216 				 phy->supported);
217 		linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
218 				 phy->supported);
219 		fallthrough;
220 	case SPEED_10:
221 	default:
222 		linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
223 				 phy->supported);
224 		linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
225 				 phy->supported);
226 	}
227 
228 	phy_advertise_supported(phy);
229 
230 	ret = phy_device_register(phy);
231 	if (ret) {
232 		phy_device_free(phy);
233 		of_node_put(np);
234 		fixed_phy_del(phy_addr);
235 		return ERR_PTR(ret);
236 	}
237 
238 	return phy;
239 }
240 EXPORT_SYMBOL_GPL(fixed_phy_register);
241 
242 void fixed_phy_unregister(struct phy_device *phy)
243 {
244 	phy_device_remove(phy);
245 	of_node_put(phy->mdio.dev.of_node);
246 	fixed_phy_del(phy->mdio.addr);
247 	phy_device_free(phy);
248 }
249 EXPORT_SYMBOL_GPL(fixed_phy_unregister);
250 
251 static int __init fixed_mdio_bus_init(void)
252 {
253 	struct fixed_mdio_bus *fmb = &platform_fmb;
254 	int ret;
255 
256 	fmb->mii_bus = mdiobus_alloc();
257 	if (!fmb->mii_bus)
258 		return -ENOMEM;
259 
260 	snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0");
261 	fmb->mii_bus->name = "Fixed MDIO Bus";
262 	fmb->mii_bus->priv = fmb;
263 	fmb->mii_bus->read = &fixed_mdio_read;
264 	fmb->mii_bus->write = &fixed_mdio_write;
265 	fmb->mii_bus->phy_mask = ~0;
266 
267 	ret = mdiobus_register(fmb->mii_bus);
268 	if (ret)
269 		goto err_mdiobus_alloc;
270 
271 	return 0;
272 
273 err_mdiobus_alloc:
274 	mdiobus_free(fmb->mii_bus);
275 	return ret;
276 }
277 module_init(fixed_mdio_bus_init);
278 
279 static void __exit fixed_mdio_bus_exit(void)
280 {
281 	struct fixed_mdio_bus *fmb = &platform_fmb;
282 	struct fixed_phy *fp, *tmp;
283 
284 	mdiobus_unregister(fmb->mii_bus);
285 	mdiobus_free(fmb->mii_bus);
286 
287 	list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
288 		list_del(&fp->node);
289 		kfree(fp);
290 	}
291 	ida_destroy(&phy_fixed_ida);
292 }
293 module_exit(fixed_mdio_bus_exit);
294 
295 MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)");
296 MODULE_AUTHOR("Vitaly Bordug");
297 MODULE_LICENSE("GPL");
298