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
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_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, 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, mdio_ctl);
79
80	/* write the value */
81	enetc_mdio_wr(mdio_priv, ENETC_MDIO_DATA, value);
82
83	ret = enetc_mdio_wait_complete(mdio_priv);
84	if (ret)
85		return ret;
86
87	return 0;
88}
89EXPORT_SYMBOL_GPL(enetc_mdio_write_c22);
90
91int 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, 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, mdio_ctl);
110
111	/* set the register address */
112	enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, 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, value);
120
121	ret = enetc_mdio_wait_complete(mdio_priv);
122	if (ret)
123		return ret;
124
125	return 0;
126}
127EXPORT_SYMBOL_GPL(enetc_mdio_write_c45);
128
129int 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, 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, mdio_ctl);
149
150	/* initiate the read */
151	enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, 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}
169EXPORT_SYMBOL_GPL(enetc_mdio_read_c22);
170
171int 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, 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, mdio_ctl);
191
192	/* set the register address */
193	enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, 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, 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}
218EXPORT_SYMBOL_GPL(enetc_mdio_read_c45);
219
220struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs)
221{
222	struct enetc_hw *hw;
223
224	hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
225	if (!hw)
226		return ERR_PTR(-ENOMEM);
227
228	hw->port = port_regs;
229
230	return hw;
231}
232EXPORT_SYMBOL_GPL(enetc_hw_alloc);
233
234/* Lock for MDIO access errata on LS1028A */
235DEFINE_RWLOCK(enetc_mdio_lock);
236EXPORT_SYMBOL_GPL(enetc_mdio_lock);
237