1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* Xilinx GMII2RGMII Converter driver |
3 | * |
4 | * Copyright (C) 2016 Xilinx, Inc. |
5 | * Copyright (C) 2016 Andrew Lunn <andrew@lunn.ch> |
6 | * |
7 | * Author: Andrew Lunn <andrew@lunn.ch> |
8 | * Author: Kedareswara rao Appana <appanad@xilinx.com> |
9 | * |
10 | * Description: |
11 | * This driver is developed for Xilinx GMII2RGMII Converter |
12 | */ |
13 | #include <linux/module.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/mii.h> |
16 | #include <linux/mdio.h> |
17 | #include <linux/phy.h> |
18 | #include <linux/of_mdio.h> |
19 | |
20 | #define XILINX_GMII2RGMII_REG 0x10 |
21 | #define XILINX_GMII2RGMII_SPEED_MASK (BMCR_SPEED1000 | BMCR_SPEED100) |
22 | |
23 | struct gmii2rgmii { |
24 | struct phy_device *phy_dev; |
25 | struct phy_driver *phy_drv; |
26 | struct phy_driver conv_phy_drv; |
27 | struct mdio_device *mdio; |
28 | }; |
29 | |
30 | static void xgmiitorgmii_configure(struct gmii2rgmii *priv, int speed) |
31 | { |
32 | struct mii_bus *bus = priv->mdio->bus; |
33 | int addr = priv->mdio->addr; |
34 | u16 val; |
35 | |
36 | val = mdiobus_read(bus, addr, XILINX_GMII2RGMII_REG); |
37 | val &= ~XILINX_GMII2RGMII_SPEED_MASK; |
38 | |
39 | if (speed == SPEED_1000) |
40 | val |= BMCR_SPEED1000; |
41 | else if (speed == SPEED_100) |
42 | val |= BMCR_SPEED100; |
43 | else |
44 | val |= BMCR_SPEED10; |
45 | |
46 | mdiobus_write(bus, addr, XILINX_GMII2RGMII_REG, val); |
47 | } |
48 | |
49 | static int xgmiitorgmii_read_status(struct phy_device *phydev) |
50 | { |
51 | struct gmii2rgmii *priv = mdiodev_get_drvdata(mdio: &phydev->mdio); |
52 | int err; |
53 | |
54 | if (priv->phy_drv->read_status) |
55 | err = priv->phy_drv->read_status(phydev); |
56 | else |
57 | err = genphy_read_status(phydev); |
58 | if (err < 0) |
59 | return err; |
60 | |
61 | xgmiitorgmii_configure(priv, speed: phydev->speed); |
62 | |
63 | return 0; |
64 | } |
65 | |
66 | static int xgmiitorgmii_set_loopback(struct phy_device *phydev, bool enable) |
67 | { |
68 | struct gmii2rgmii *priv = mdiodev_get_drvdata(mdio: &phydev->mdio); |
69 | int err; |
70 | |
71 | if (priv->phy_drv->set_loopback) |
72 | err = priv->phy_drv->set_loopback(phydev, enable); |
73 | else |
74 | err = genphy_loopback(phydev, enable); |
75 | if (err < 0) |
76 | return err; |
77 | |
78 | xgmiitorgmii_configure(priv, speed: phydev->speed); |
79 | |
80 | return 0; |
81 | } |
82 | |
83 | static int xgmiitorgmii_probe(struct mdio_device *mdiodev) |
84 | { |
85 | struct device *dev = &mdiodev->dev; |
86 | struct device_node *np = dev->of_node, *phy_node; |
87 | struct gmii2rgmii *priv; |
88 | |
89 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
90 | if (!priv) |
91 | return -ENOMEM; |
92 | |
93 | phy_node = of_parse_phandle(np, phandle_name: "phy-handle" , index: 0); |
94 | if (!phy_node) { |
95 | dev_err(dev, "Couldn't parse phy-handle\n" ); |
96 | return -ENODEV; |
97 | } |
98 | |
99 | priv->phy_dev = of_phy_find_device(phy_np: phy_node); |
100 | of_node_put(node: phy_node); |
101 | if (!priv->phy_dev) { |
102 | dev_info(dev, "Couldn't find phydev\n" ); |
103 | return -EPROBE_DEFER; |
104 | } |
105 | |
106 | if (!priv->phy_dev->drv) { |
107 | dev_info(dev, "Attached phy not ready\n" ); |
108 | put_device(dev: &priv->phy_dev->mdio.dev); |
109 | return -EPROBE_DEFER; |
110 | } |
111 | |
112 | priv->mdio = mdiodev; |
113 | priv->phy_drv = priv->phy_dev->drv; |
114 | memcpy(&priv->conv_phy_drv, priv->phy_dev->drv, |
115 | sizeof(struct phy_driver)); |
116 | priv->conv_phy_drv.read_status = xgmiitorgmii_read_status; |
117 | priv->conv_phy_drv.set_loopback = xgmiitorgmii_set_loopback; |
118 | mdiodev_set_drvdata(mdio: &priv->phy_dev->mdio, data: priv); |
119 | priv->phy_dev->drv = &priv->conv_phy_drv; |
120 | |
121 | return 0; |
122 | } |
123 | |
124 | static const struct of_device_id xgmiitorgmii_of_match[] = { |
125 | { .compatible = "xlnx,gmii-to-rgmii-1.0" }, |
126 | {}, |
127 | }; |
128 | MODULE_DEVICE_TABLE(of, xgmiitorgmii_of_match); |
129 | |
130 | static struct mdio_driver xgmiitorgmii_driver = { |
131 | .probe = xgmiitorgmii_probe, |
132 | .mdiodrv.driver = { |
133 | .name = "xgmiitorgmii" , |
134 | .of_match_table = xgmiitorgmii_of_match, |
135 | }, |
136 | }; |
137 | |
138 | mdio_module_driver(xgmiitorgmii_driver); |
139 | |
140 | MODULE_DESCRIPTION("Xilinx GMII2RGMII converter driver" ); |
141 | MODULE_LICENSE("GPL" ); |
142 | |