Linux-libre 5.4.48-gnu
[librecmc/linux-libre.git] / drivers / net / ethernet / freescale / enetc / enetc_mdio.c
1 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2 /* Copyright 2019 NXP */
3
4 #include <linux/mdio.h>
5 #include <linux/of_mdio.h>
6 #include <linux/iopoll.h>
7 #include <linux/of.h>
8
9 #include "enetc_mdio.h"
10
11 #define ENETC_MDIO_REG_OFFSET   0x1c00
12 #define ENETC_MDIO_CFG  0x0     /* MDIO configuration and status */
13 #define ENETC_MDIO_CTL  0x4     /* MDIO control */
14 #define ENETC_MDIO_DATA 0x8     /* MDIO data */
15 #define ENETC_MDIO_ADDR 0xc     /* MDIO address */
16
17 #define enetc_mdio_rd(hw, off) \
18         enetc_port_rd(hw, ENETC_##off + ENETC_MDIO_REG_OFFSET)
19 #define enetc_mdio_wr(hw, off, val) \
20         enetc_port_wr(hw, ENETC_##off + ENETC_MDIO_REG_OFFSET, val)
21 #define enetc_mdio_rd_reg(off)  enetc_mdio_rd(hw, off)
22
23 #define ENETC_MDC_DIV           258
24
25 #define MDIO_CFG_CLKDIV(x)      ((((x) >> 1) & 0xff) << 8)
26 #define MDIO_CFG_BSY            BIT(0)
27 #define MDIO_CFG_RD_ER          BIT(1)
28 #define MDIO_CFG_ENC45          BIT(6)
29  /* external MDIO only - driven on neg MDC edge */
30 #define MDIO_CFG_NEG            BIT(23)
31
32 #define MDIO_CTL_DEV_ADDR(x)    ((x) & 0x1f)
33 #define MDIO_CTL_PORT_ADDR(x)   (((x) & 0x1f) << 5)
34 #define MDIO_CTL_READ           BIT(15)
35 #define MDIO_DATA(x)            ((x) & 0xffff)
36
37 #define TIMEOUT 1000
38 static int enetc_mdio_wait_complete(struct enetc_hw *hw)
39 {
40         u32 val;
41
42         return readx_poll_timeout(enetc_mdio_rd_reg, MDIO_CFG, val,
43                                   !(val & MDIO_CFG_BSY), 10, 10 * TIMEOUT);
44 }
45
46 int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value)
47 {
48         struct enetc_mdio_priv *mdio_priv = bus->priv;
49         struct enetc_hw *hw = mdio_priv->hw;
50         u32 mdio_ctl, mdio_cfg;
51         u16 dev_addr;
52         int ret;
53
54         mdio_cfg = MDIO_CFG_CLKDIV(ENETC_MDC_DIV) | MDIO_CFG_NEG;
55         if (regnum & MII_ADDR_C45) {
56                 dev_addr = (regnum >> 16) & 0x1f;
57                 mdio_cfg |= MDIO_CFG_ENC45;
58         } else {
59                 /* clause 22 (ie 1G) */
60                 dev_addr = regnum & 0x1f;
61                 mdio_cfg &= ~MDIO_CFG_ENC45;
62         }
63
64         enetc_mdio_wr(hw, MDIO_CFG, mdio_cfg);
65
66         ret = enetc_mdio_wait_complete(hw);
67         if (ret)
68                 return ret;
69
70         /* set port and dev addr */
71         mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
72         enetc_mdio_wr(hw, MDIO_CTL, mdio_ctl);
73
74         /* set the register address */
75         if (regnum & MII_ADDR_C45) {
76                 enetc_mdio_wr(hw, MDIO_ADDR, regnum & 0xffff);
77
78                 ret = enetc_mdio_wait_complete(hw);
79                 if (ret)
80                         return ret;
81         }
82
83         /* write the value */
84         enetc_mdio_wr(hw, MDIO_DATA, MDIO_DATA(value));
85
86         ret = enetc_mdio_wait_complete(hw);
87         if (ret)
88                 return ret;
89
90         return 0;
91 }
92
93 int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
94 {
95         struct enetc_mdio_priv *mdio_priv = bus->priv;
96         struct enetc_hw *hw = mdio_priv->hw;
97         u32 mdio_ctl, mdio_cfg;
98         u16 dev_addr, value;
99         int ret;
100
101         mdio_cfg = MDIO_CFG_CLKDIV(ENETC_MDC_DIV) | MDIO_CFG_NEG;
102         if (regnum & MII_ADDR_C45) {
103                 dev_addr = (regnum >> 16) & 0x1f;
104                 mdio_cfg |= MDIO_CFG_ENC45;
105         } else {
106                 dev_addr = regnum & 0x1f;
107                 mdio_cfg &= ~MDIO_CFG_ENC45;
108         }
109
110         enetc_mdio_wr(hw, MDIO_CFG, mdio_cfg);
111
112         ret = enetc_mdio_wait_complete(hw);
113         if (ret)
114                 return ret;
115
116         /* set port and device addr */
117         mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
118         enetc_mdio_wr(hw, MDIO_CTL, mdio_ctl);
119
120         /* set the register address */
121         if (regnum & MII_ADDR_C45) {
122                 enetc_mdio_wr(hw, MDIO_ADDR, regnum & 0xffff);
123
124                 ret = enetc_mdio_wait_complete(hw);
125                 if (ret)
126                         return ret;
127         }
128
129         /* initiate the read */
130         enetc_mdio_wr(hw, MDIO_CTL, mdio_ctl | MDIO_CTL_READ);
131
132         ret = enetc_mdio_wait_complete(hw);
133         if (ret)
134                 return ret;
135
136         /* return all Fs if nothing was there */
137         if (enetc_mdio_rd(hw, MDIO_CFG) & MDIO_CFG_RD_ER) {
138                 dev_dbg(&bus->dev,
139                         "Error while reading PHY%d reg at %d.%hhu\n",
140                         phy_id, dev_addr, regnum);
141                 return 0xffff;
142         }
143
144         value = enetc_mdio_rd(hw, MDIO_DATA) & 0xffff;
145
146         return value;
147 }
148
149 int enetc_mdio_probe(struct enetc_pf *pf)
150 {
151         struct device *dev = &pf->si->pdev->dev;
152         struct enetc_mdio_priv *mdio_priv;
153         struct device_node *np;
154         struct mii_bus *bus;
155         int err;
156
157         bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
158         if (!bus)
159                 return -ENOMEM;
160
161         bus->name = "Freescale ENETC MDIO Bus";
162         bus->read = enetc_mdio_read;
163         bus->write = enetc_mdio_write;
164         bus->parent = dev;
165         mdio_priv = bus->priv;
166         mdio_priv->hw = &pf->si->hw;
167         snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
168
169         np = of_get_child_by_name(dev->of_node, "mdio");
170         if (!np) {
171                 dev_err(dev, "MDIO node missing\n");
172                 return -EINVAL;
173         }
174
175         err = of_mdiobus_register(bus, np);
176         if (err) {
177                 of_node_put(np);
178                 dev_err(dev, "cannot register MDIO bus\n");
179                 return err;
180         }
181
182         of_node_put(np);
183         pf->mdio = bus;
184
185         return 0;
186 }
187
188 void enetc_mdio_remove(struct enetc_pf *pf)
189 {
190         if (pf->mdio)
191                 mdiobus_unregister(pf->mdio);
192 }