1 | // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) |
2 | /* Copyright 2019 NXP */ |
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 /* 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 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 | /* external MDIO only - driven on neg MDC edge */ |
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 | |
34 | static 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 | |
39 | static 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 | |
45 | static 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 | |
50 | static 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 | |
58 | int enetc_mdio_write_c22(struct mii_bus *bus, int phy_id, int regnum, |
59 | u16 value) |
60 | { |
61 | struct enetc_mdio_priv *mdio_priv = bus->priv; |
62 | u32 mdio_ctl, mdio_cfg; |
63 | u16 dev_addr; |
64 | int ret; |
65 | |
66 | mdio_cfg = ENETC_EMDIO_CFG; |
67 | dev_addr = regnum & 0x1f; |
68 | mdio_cfg &= ~MDIO_CFG_ENC45; |
69 | |
70 | enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, val: mdio_cfg); |
71 | |
72 | ret = enetc_mdio_wait_complete(mdio_priv); |
73 | if (ret) |
74 | return ret; |
75 | |
76 | /* set port and dev addr */ |
77 | mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); |
78 | enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, val: mdio_ctl); |
79 | |
80 | /* write the value */ |
81 | enetc_mdio_wr(mdio_priv, ENETC_MDIO_DATA, val: value); |
82 | |
83 | ret = enetc_mdio_wait_complete(mdio_priv); |
84 | if (ret) |
85 | return ret; |
86 | |
87 | return 0; |
88 | } |
89 | EXPORT_SYMBOL_GPL(enetc_mdio_write_c22); |
90 | |
91 | int enetc_mdio_write_c45(struct mii_bus *bus, int phy_id, int dev_addr, |
92 | int regnum, u16 value) |
93 | { |
94 | struct enetc_mdio_priv *mdio_priv = bus->priv; |
95 | u32 mdio_ctl, mdio_cfg; |
96 | int ret; |
97 | |
98 | mdio_cfg = ENETC_EMDIO_CFG; |
99 | mdio_cfg |= MDIO_CFG_ENC45; |
100 | |
101 | enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, val: mdio_cfg); |
102 | |
103 | ret = enetc_mdio_wait_complete(mdio_priv); |
104 | if (ret) |
105 | return ret; |
106 | |
107 | /* set port and dev addr */ |
108 | mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); |
109 | enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, val: mdio_ctl); |
110 | |
111 | /* set the register address */ |
112 | enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, val: regnum & 0xffff); |
113 | |
114 | ret = enetc_mdio_wait_complete(mdio_priv); |
115 | if (ret) |
116 | return ret; |
117 | |
118 | /* write the value */ |
119 | enetc_mdio_wr(mdio_priv, ENETC_MDIO_DATA, val: value); |
120 | |
121 | ret = enetc_mdio_wait_complete(mdio_priv); |
122 | if (ret) |
123 | return ret; |
124 | |
125 | return 0; |
126 | } |
127 | EXPORT_SYMBOL_GPL(enetc_mdio_write_c45); |
128 | |
129 | int enetc_mdio_read_c22(struct mii_bus *bus, int phy_id, int regnum) |
130 | { |
131 | struct enetc_mdio_priv *mdio_priv = bus->priv; |
132 | u32 mdio_ctl, mdio_cfg; |
133 | u16 dev_addr, value; |
134 | int ret; |
135 | |
136 | mdio_cfg = ENETC_EMDIO_CFG; |
137 | dev_addr = regnum & 0x1f; |
138 | mdio_cfg &= ~MDIO_CFG_ENC45; |
139 | |
140 | enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, val: mdio_cfg); |
141 | |
142 | ret = enetc_mdio_wait_complete(mdio_priv); |
143 | if (ret) |
144 | return ret; |
145 | |
146 | /* set port and device addr */ |
147 | mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); |
148 | enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, val: mdio_ctl); |
149 | |
150 | /* initiate the read */ |
151 | enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, val: mdio_ctl | MDIO_CTL_READ); |
152 | |
153 | ret = enetc_mdio_wait_complete(mdio_priv); |
154 | if (ret) |
155 | return ret; |
156 | |
157 | /* return all Fs if nothing was there */ |
158 | if (enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_RD_ER) { |
159 | dev_dbg(&bus->dev, |
160 | "Error while reading PHY%d reg at %d.%d\n" , |
161 | phy_id, dev_addr, regnum); |
162 | return 0xffff; |
163 | } |
164 | |
165 | value = enetc_mdio_rd(mdio_priv, ENETC_MDIO_DATA) & 0xffff; |
166 | |
167 | return value; |
168 | } |
169 | EXPORT_SYMBOL_GPL(enetc_mdio_read_c22); |
170 | |
171 | int enetc_mdio_read_c45(struct mii_bus *bus, int phy_id, int dev_addr, |
172 | int regnum) |
173 | { |
174 | struct enetc_mdio_priv *mdio_priv = bus->priv; |
175 | u32 mdio_ctl, mdio_cfg; |
176 | u16 value; |
177 | int ret; |
178 | |
179 | mdio_cfg = ENETC_EMDIO_CFG; |
180 | mdio_cfg |= MDIO_CFG_ENC45; |
181 | |
182 | enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, val: mdio_cfg); |
183 | |
184 | ret = enetc_mdio_wait_complete(mdio_priv); |
185 | if (ret) |
186 | return ret; |
187 | |
188 | /* set port and device addr */ |
189 | mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); |
190 | enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, val: mdio_ctl); |
191 | |
192 | /* set the register address */ |
193 | enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, val: regnum & 0xffff); |
194 | |
195 | ret = enetc_mdio_wait_complete(mdio_priv); |
196 | if (ret) |
197 | return ret; |
198 | |
199 | /* initiate the read */ |
200 | enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, val: mdio_ctl | MDIO_CTL_READ); |
201 | |
202 | ret = enetc_mdio_wait_complete(mdio_priv); |
203 | if (ret) |
204 | return ret; |
205 | |
206 | /* return all Fs if nothing was there */ |
207 | if (enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_RD_ER) { |
208 | dev_dbg(&bus->dev, |
209 | "Error while reading PHY%d reg at %d.%d\n" , |
210 | phy_id, dev_addr, regnum); |
211 | return 0xffff; |
212 | } |
213 | |
214 | value = enetc_mdio_rd(mdio_priv, ENETC_MDIO_DATA) & 0xffff; |
215 | |
216 | return value; |
217 | } |
218 | EXPORT_SYMBOL_GPL(enetc_mdio_read_c45); |
219 | |
220 | struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs) |
221 | { |
222 | struct enetc_hw *hw; |
223 | |
224 | hw = devm_kzalloc(dev, size: sizeof(*hw), GFP_KERNEL); |
225 | if (!hw) |
226 | return ERR_PTR(error: -ENOMEM); |
227 | |
228 | hw->port = port_regs; |
229 | |
230 | return hw; |
231 | } |
232 | EXPORT_SYMBOL_GPL(enetc_hw_alloc); |
233 | |
234 | /* Lock for MDIO access errata on LS1028A */ |
235 | DEFINE_RWLOCK(enetc_mdio_lock); |
236 | EXPORT_SYMBOL_GPL(enetc_mdio_lock); |
237 | |