1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Applied Micro X-Gene SoC Ethernet v2 Driver |
4 | * |
5 | * Copyright (c) 2017, Applied Micro Circuits Corporation |
6 | * Author(s): Iyappan Subramanian <isubramanian@apm.com> |
7 | * Keyur Chudgar <kchudgar@apm.com> |
8 | */ |
9 | |
10 | #include "main.h" |
11 | |
12 | static int xge_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 data) |
13 | { |
14 | struct xge_pdata *pdata = bus->priv; |
15 | u32 done, val = 0; |
16 | u8 wait = 10; |
17 | |
18 | SET_REG_BITS(&val, PHY_ADDR, phy_id); |
19 | SET_REG_BITS(&val, REG_ADDR, reg); |
20 | xge_wr_csr(pdata, MII_MGMT_ADDRESS, val); |
21 | |
22 | xge_wr_csr(pdata, MII_MGMT_CONTROL, val: data); |
23 | do { |
24 | usleep_range(min: 5, max: 10); |
25 | done = xge_rd_csr(pdata, MII_MGMT_INDICATORS); |
26 | } while ((done & MII_MGMT_BUSY) && wait--); |
27 | |
28 | if (done & MII_MGMT_BUSY) { |
29 | dev_err(&bus->dev, "MII_MGMT write failed\n" ); |
30 | return -ETIMEDOUT; |
31 | } |
32 | |
33 | return 0; |
34 | } |
35 | |
36 | static int xge_mdio_read(struct mii_bus *bus, int phy_id, int reg) |
37 | { |
38 | struct xge_pdata *pdata = bus->priv; |
39 | u32 data, done, val = 0; |
40 | u8 wait = 10; |
41 | |
42 | SET_REG_BITS(&val, PHY_ADDR, phy_id); |
43 | SET_REG_BITS(&val, REG_ADDR, reg); |
44 | xge_wr_csr(pdata, MII_MGMT_ADDRESS, val); |
45 | |
46 | xge_wr_csr(pdata, MII_MGMT_COMMAND, MII_READ_CYCLE); |
47 | do { |
48 | usleep_range(min: 5, max: 10); |
49 | done = xge_rd_csr(pdata, MII_MGMT_INDICATORS); |
50 | } while ((done & MII_MGMT_BUSY) && wait--); |
51 | |
52 | if (done & MII_MGMT_BUSY) { |
53 | dev_err(&bus->dev, "MII_MGMT read failed\n" ); |
54 | return -ETIMEDOUT; |
55 | } |
56 | |
57 | data = xge_rd_csr(pdata, MII_MGMT_STATUS); |
58 | xge_wr_csr(pdata, MII_MGMT_COMMAND, val: 0); |
59 | |
60 | return data; |
61 | } |
62 | |
63 | static void xge_adjust_link(struct net_device *ndev) |
64 | { |
65 | struct xge_pdata *pdata = netdev_priv(dev: ndev); |
66 | struct phy_device *phydev = ndev->phydev; |
67 | |
68 | if (phydev->link) { |
69 | if (pdata->phy_speed != phydev->speed) { |
70 | pdata->phy_speed = phydev->speed; |
71 | xge_mac_set_speed(pdata); |
72 | xge_mac_enable(pdata); |
73 | phy_print_status(phydev); |
74 | } |
75 | } else { |
76 | if (pdata->phy_speed != SPEED_UNKNOWN) { |
77 | pdata->phy_speed = SPEED_UNKNOWN; |
78 | xge_mac_disable(pdata); |
79 | phy_print_status(phydev); |
80 | } |
81 | } |
82 | } |
83 | |
84 | void xge_mdio_remove(struct net_device *ndev) |
85 | { |
86 | struct xge_pdata *pdata = netdev_priv(dev: ndev); |
87 | struct mii_bus *mdio_bus = pdata->mdio_bus; |
88 | |
89 | if (ndev->phydev) |
90 | phy_disconnect(phydev: ndev->phydev); |
91 | |
92 | if (mdio_bus->state == MDIOBUS_REGISTERED) |
93 | mdiobus_unregister(bus: mdio_bus); |
94 | |
95 | mdiobus_free(bus: mdio_bus); |
96 | } |
97 | |
98 | int xge_mdio_config(struct net_device *ndev) |
99 | { |
100 | __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; |
101 | struct xge_pdata *pdata = netdev_priv(dev: ndev); |
102 | struct device *dev = &pdata->pdev->dev; |
103 | struct mii_bus *mdio_bus; |
104 | struct phy_device *phydev; |
105 | int ret; |
106 | |
107 | mdio_bus = mdiobus_alloc(); |
108 | if (!mdio_bus) |
109 | return -ENOMEM; |
110 | |
111 | mdio_bus->name = "APM X-Gene Ethernet (v2) MDIO Bus" ; |
112 | mdio_bus->read = xge_mdio_read; |
113 | mdio_bus->write = xge_mdio_write; |
114 | mdio_bus->priv = pdata; |
115 | mdio_bus->parent = dev; |
116 | snprintf(buf: mdio_bus->id, MII_BUS_ID_SIZE, fmt: "%s-mii" , dev_name(dev)); |
117 | pdata->mdio_bus = mdio_bus; |
118 | |
119 | mdio_bus->phy_mask = 0x1; |
120 | ret = mdiobus_register(mdio_bus); |
121 | if (ret) |
122 | goto err; |
123 | |
124 | phydev = phy_find_first(bus: mdio_bus); |
125 | if (!phydev) { |
126 | dev_err(dev, "no PHY found\n" ); |
127 | ret = -ENODEV; |
128 | goto err; |
129 | } |
130 | phydev = phy_connect(dev: ndev, bus_id: phydev_name(phydev), |
131 | handler: &xge_adjust_link, |
132 | interface: pdata->resources.phy_mode); |
133 | |
134 | if (IS_ERR(ptr: phydev)) { |
135 | netdev_err(dev: ndev, format: "Could not attach to PHY\n" ); |
136 | ret = PTR_ERR(ptr: phydev); |
137 | goto err; |
138 | } |
139 | |
140 | linkmode_set_bit_array(array: phy_10_100_features_array, |
141 | ARRAY_SIZE(phy_10_100_features_array), |
142 | addr: mask); |
143 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_1000baseT_Half_BIT, addr: mask); |
144 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_AUI_BIT, addr: mask); |
145 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_MII_BIT, addr: mask); |
146 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_FIBRE_BIT, addr: mask); |
147 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_BNC_BIT, addr: mask); |
148 | |
149 | linkmode_andnot(dst: phydev->supported, src1: phydev->supported, src2: mask); |
150 | linkmode_copy(dst: phydev->advertising, src: phydev->supported); |
151 | pdata->phy_speed = SPEED_UNKNOWN; |
152 | |
153 | return 0; |
154 | err: |
155 | xge_mdio_remove(ndev); |
156 | |
157 | return ret; |
158 | } |
159 | |