GNU Linux-libre 4.19.264-gnu1
[releases.git] / drivers / net / ethernet / apm / xgene-v2 / mdio.c
1 /*
2  * Applied Micro X-Gene SoC Ethernet v2 Driver
3  *
4  * Copyright (c) 2017, Applied Micro Circuits Corporation
5  * Author(s): Iyappan Subramanian <isubramanian@apm.com>
6  *            Keyur Chudgar <kchudgar@apm.com>
7  *
8  * This program is free software; you can redistribute  it and/or modify it
9  * under  the terms of  the GNU General  Public License as published by the
10  * Free Software Foundation;  either version 2 of the  License, or (at your
11  * option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "main.h"
23
24 static int xge_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 data)
25 {
26         struct xge_pdata *pdata = bus->priv;
27         u32 done, val = 0;
28         u8 wait = 10;
29
30         SET_REG_BITS(&val, PHY_ADDR, phy_id);
31         SET_REG_BITS(&val, REG_ADDR, reg);
32         xge_wr_csr(pdata, MII_MGMT_ADDRESS, val);
33
34         xge_wr_csr(pdata, MII_MGMT_CONTROL, data);
35         do {
36                 usleep_range(5, 10);
37                 done = xge_rd_csr(pdata, MII_MGMT_INDICATORS);
38         } while ((done & MII_MGMT_BUSY) && wait--);
39
40         if (done & MII_MGMT_BUSY) {
41                 dev_err(&bus->dev, "MII_MGMT write failed\n");
42                 return -ETIMEDOUT;
43         }
44
45         return 0;
46 }
47
48 static int xge_mdio_read(struct mii_bus *bus, int phy_id, int reg)
49 {
50         struct xge_pdata *pdata = bus->priv;
51         u32 data, done, val = 0;
52         u8 wait = 10;
53
54         SET_REG_BITS(&val, PHY_ADDR, phy_id);
55         SET_REG_BITS(&val, REG_ADDR, reg);
56         xge_wr_csr(pdata, MII_MGMT_ADDRESS, val);
57
58         xge_wr_csr(pdata, MII_MGMT_COMMAND, MII_READ_CYCLE);
59         do {
60                 usleep_range(5, 10);
61                 done = xge_rd_csr(pdata, MII_MGMT_INDICATORS);
62         } while ((done & MII_MGMT_BUSY) && wait--);
63
64         if (done & MII_MGMT_BUSY) {
65                 dev_err(&bus->dev, "MII_MGMT read failed\n");
66                 return -ETIMEDOUT;
67         }
68
69         data = xge_rd_csr(pdata, MII_MGMT_STATUS);
70         xge_wr_csr(pdata, MII_MGMT_COMMAND, 0);
71
72         return data;
73 }
74
75 static void xge_adjust_link(struct net_device *ndev)
76 {
77         struct xge_pdata *pdata = netdev_priv(ndev);
78         struct phy_device *phydev = ndev->phydev;
79
80         if (phydev->link) {
81                 if (pdata->phy_speed != phydev->speed) {
82                         pdata->phy_speed = phydev->speed;
83                         xge_mac_set_speed(pdata);
84                         xge_mac_enable(pdata);
85                         phy_print_status(phydev);
86                 }
87         } else {
88                 if (pdata->phy_speed != SPEED_UNKNOWN) {
89                         pdata->phy_speed = SPEED_UNKNOWN;
90                         xge_mac_disable(pdata);
91                         phy_print_status(phydev);
92                 }
93         }
94 }
95
96 void xge_mdio_remove(struct net_device *ndev)
97 {
98         struct xge_pdata *pdata = netdev_priv(ndev);
99         struct mii_bus *mdio_bus = pdata->mdio_bus;
100
101         if (ndev->phydev)
102                 phy_disconnect(ndev->phydev);
103
104         if (mdio_bus->state == MDIOBUS_REGISTERED)
105                 mdiobus_unregister(mdio_bus);
106
107         mdiobus_free(mdio_bus);
108 }
109
110 int xge_mdio_config(struct net_device *ndev)
111 {
112         struct xge_pdata *pdata = netdev_priv(ndev);
113         struct device *dev = &pdata->pdev->dev;
114         struct mii_bus *mdio_bus;
115         struct phy_device *phydev;
116         int ret;
117
118         mdio_bus = mdiobus_alloc();
119         if (!mdio_bus)
120                 return -ENOMEM;
121
122         mdio_bus->name = "APM X-Gene Ethernet (v2) MDIO Bus";
123         mdio_bus->read = xge_mdio_read;
124         mdio_bus->write = xge_mdio_write;
125         mdio_bus->priv = pdata;
126         mdio_bus->parent = dev;
127         snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev));
128         pdata->mdio_bus = mdio_bus;
129
130         mdio_bus->phy_mask = 0x1;
131         ret = mdiobus_register(mdio_bus);
132         if (ret)
133                 goto err;
134
135         phydev = phy_find_first(mdio_bus);
136         if (!phydev) {
137                 dev_err(dev, "no PHY found\n");
138                 ret = -ENODEV;
139                 goto err;
140         }
141         phydev = phy_connect(ndev, phydev_name(phydev),
142                              &xge_adjust_link,
143                              pdata->resources.phy_mode);
144
145         if (IS_ERR(phydev)) {
146                 netdev_err(ndev, "Could not attach to PHY\n");
147                 ret = PTR_ERR(phydev);
148                 goto err;
149         }
150
151         phydev->supported &= ~(SUPPORTED_10baseT_Half |
152                                SUPPORTED_10baseT_Full |
153                                SUPPORTED_100baseT_Half |
154                                SUPPORTED_100baseT_Full |
155                                SUPPORTED_1000baseT_Half |
156                                SUPPORTED_AUI |
157                                SUPPORTED_MII |
158                                SUPPORTED_FIBRE |
159                                SUPPORTED_BNC);
160         phydev->advertising = phydev->supported;
161         pdata->phy_speed = SPEED_UNKNOWN;
162
163         return 0;
164 err:
165         xge_mdio_remove(ndev);
166
167         return ret;
168 }