1
2
3
4#include <linux/fsl/enetc_mdio.h>
5#include <linux/mdio.h>
6#include <linux/of_mdio.h>
7#include <linux/iopoll.h>
8#include <linux/of.h>
9
10#include "enetc_pf.h"
11
12#define ENETC_MDIO_CFG 0x0
13#define ENETC_MDIO_CTL 0x4
14#define ENETC_MDIO_DATA 0x8
15#define ENETC_MDIO_ADDR 0xc
16
17#define MDIO_CFG_CLKDIV(x) ((((x) >> 1) & 0xff) << 8)
18#define MDIO_CFG_BSY BIT(0)
19#define MDIO_CFG_RD_ER BIT(1)
20#define MDIO_CFG_HOLD(x) (((x) << 2) & GENMASK(4, 2))
21#define MDIO_CFG_ENC45 BIT(6)
22
23#define MDIO_CFG_NEG BIT(23)
24
25#define ENETC_EMDIO_CFG \
26 (MDIO_CFG_HOLD(2) | \
27 MDIO_CFG_CLKDIV(258) | \
28 MDIO_CFG_NEG)
29
30#define MDIO_CTL_DEV_ADDR(x) ((x) & 0x1f)
31#define MDIO_CTL_PORT_ADDR(x) (((x) & 0x1f) << 5)
32#define MDIO_CTL_READ BIT(15)
33
34static inline u32 enetc_mdio_rd(struct enetc_mdio_priv *mdio_priv, int off)
35{
36 return enetc_port_rd_mdio(mdio_priv->hw, mdio_priv->mdio_base + off);
37}
38
39static inline void enetc_mdio_wr(struct enetc_mdio_priv *mdio_priv, int off,
40 u32 val)
41{
42 enetc_port_wr_mdio(mdio_priv->hw, mdio_priv->mdio_base + off, val);
43}
44
45static bool enetc_mdio_is_busy(struct enetc_mdio_priv *mdio_priv)
46{
47 return enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_BSY;
48}
49
50static int enetc_mdio_wait_complete(struct enetc_mdio_priv *mdio_priv)
51{
52 bool is_busy;
53
54 return readx_poll_timeout(enetc_mdio_is_busy, mdio_priv,
55 is_busy, !is_busy, 10, 10 * 1000);
56}
57
58int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value)
59{
60 struct enetc_mdio_priv *mdio_priv = bus->priv;
61 u32 mdio_ctl, mdio_cfg;
62 u16 dev_addr;
63 int ret;
64
65 mdio_cfg = ENETC_EMDIO_CFG;
66 if (regnum & MII_ADDR_C45) {
67 dev_addr = (regnum >> 16) & 0x1f;
68 mdio_cfg |= MDIO_CFG_ENC45;
69 } else {
70
71 dev_addr = regnum & 0x1f;
72 mdio_cfg &= ~MDIO_CFG_ENC45;
73 }
74
75 enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);
76
77 ret = enetc_mdio_wait_complete(mdio_priv);
78 if (ret)
79 return ret;
80
81
82 mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
83 enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);
84
85
86 if (regnum & MII_ADDR_C45) {
87 enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, regnum & 0xffff);
88
89 ret = enetc_mdio_wait_complete(mdio_priv);
90 if (ret)
91 return ret;
92 }
93
94
95 enetc_mdio_wr(mdio_priv, ENETC_MDIO_DATA, value);
96
97 ret = enetc_mdio_wait_complete(mdio_priv);
98 if (ret)
99 return ret;
100
101 return 0;
102}
103EXPORT_SYMBOL_GPL(enetc_mdio_write);
104
105int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
106{
107 struct enetc_mdio_priv *mdio_priv = bus->priv;
108 u32 mdio_ctl, mdio_cfg;
109 u16 dev_addr, value;
110 int ret;
111
112 mdio_cfg = ENETC_EMDIO_CFG;
113 if (regnum & MII_ADDR_C45) {
114 dev_addr = (regnum >> 16) & 0x1f;
115 mdio_cfg |= MDIO_CFG_ENC45;
116 } else {
117 dev_addr = regnum & 0x1f;
118 mdio_cfg &= ~MDIO_CFG_ENC45;
119 }
120
121 enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);
122
123 ret = enetc_mdio_wait_complete(mdio_priv);
124 if (ret)
125 return ret;
126
127
128 mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
129 enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);
130
131
132 if (regnum & MII_ADDR_C45) {
133 enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, regnum & 0xffff);
134
135 ret = enetc_mdio_wait_complete(mdio_priv);
136 if (ret)
137 return ret;
138 }
139
140
141 enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl | MDIO_CTL_READ);
142
143 ret = enetc_mdio_wait_complete(mdio_priv);
144 if (ret)
145 return ret;
146
147
148 if (enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_RD_ER) {
149 dev_dbg(&bus->dev,
150 "Error while reading PHY%d reg at %d.%hhu\n",
151 phy_id, dev_addr, regnum);
152 return 0xffff;
153 }
154
155 value = enetc_mdio_rd(mdio_priv, ENETC_MDIO_DATA) & 0xffff;
156
157 return value;
158}
159EXPORT_SYMBOL_GPL(enetc_mdio_read);
160
161struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs)
162{
163 struct enetc_hw *hw;
164
165 hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
166 if (!hw)
167 return ERR_PTR(-ENOMEM);
168
169 hw->port = port_regs;
170
171 return hw;
172}
173EXPORT_SYMBOL_GPL(enetc_hw_alloc);
174
175
176DEFINE_RWLOCK(enetc_mdio_lock);
177EXPORT_SYMBOL_GPL(enetc_mdio_lock);
178