1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2009-2010, 2013 Freescale Semiconductor, Inc.
4 *	Jun-jie Zhang <b18070@freescale.com>
5 *	Mingkai Hu <Mingkai.hu@freescale.com>
6 */
7
8#include <common.h>
9#include <miiphy.h>
10#include <phy.h>
11#include <fsl_mdio.h>
12#include <asm/io.h>
13#include <linux/errno.h>
14#include <tsec.h>
15
16#ifdef CONFIG_DM_MDIO
17struct tsec_mdio_priv {
18	struct tsec_mii_mng __iomem *regs;
19};
20#endif
21
22void tsec_local_mdio_write(struct tsec_mii_mng __iomem *phyregs, int port_addr,
23		int dev_addr, int regnum, int value)
24{
25	int timeout = 1000000;
26
27	out_be32(&phyregs->miimadd, (port_addr << 8) | (regnum & 0x1f));
28	out_be32(&phyregs->miimcon, value);
29	/* Memory barrier */
30	mb();
31
32	while ((in_be32(&phyregs->miimind) & MIIMIND_BUSY) && timeout--)
33		;
34}
35
36int tsec_local_mdio_read(struct tsec_mii_mng __iomem *phyregs, int port_addr,
37		int dev_addr, int regnum)
38{
39	int value;
40	int timeout = 1000000;
41
42	/* Put the address of the phy, and the register number into MIIMADD */
43	out_be32(&phyregs->miimadd, (port_addr << 8) | (regnum & 0x1f));
44
45	/* Clear the command register, and wait */
46	out_be32(&phyregs->miimcom, 0);
47	/* Memory barrier */
48	mb();
49
50	/* Initiate a read command, and wait */
51	out_be32(&phyregs->miimcom, MIIMCOM_READ_CYCLE);
52	/* Memory barrier */
53	mb();
54
55	/* Wait for the the indication that the read is done */
56	while ((in_be32(&phyregs->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
57			&& timeout--)
58		;
59
60	/* Grab the value read from the PHY */
61	value = in_be32(&phyregs->miimstat);
62
63	return value;
64}
65
66#if defined(CONFIG_PHYLIB)
67static int fsl_pq_mdio_reset(struct mii_dev *bus)
68{
69	struct tsec_mii_mng __iomem *regs;
70#ifndef CONFIG_DM_MDIO
71	regs = (struct tsec_mii_mng __iomem *)bus->priv;
72#else
73	struct tsec_mdio_priv *priv;
74
75	if (!bus->priv)
76		return -EINVAL;
77
78	priv = dev_get_priv(bus->priv);
79	regs = priv->regs;
80#endif
81
82	/* Reset MII (due to new addresses) */
83	out_be32(&regs->miimcfg, MIIMCFG_RESET_MGMT);
84
85	out_be32(&regs->miimcfg, MIIMCFG_INIT_VALUE);
86
87	while (in_be32(&regs->miimind) & MIIMIND_BUSY)
88		;
89
90	return 0;
91}
92#endif
93
94int tsec_phy_read(struct mii_dev *bus, int addr, int dev_addr, int regnum)
95{
96	struct tsec_mii_mng __iomem *phyregs;
97#ifndef CONFIG_DM_MDIO
98	phyregs = (struct tsec_mii_mng __iomem *)bus->priv;
99#else
100	struct tsec_mdio_priv *priv;
101
102	if (!bus->priv)
103		return -EINVAL;
104
105	priv = dev_get_priv(bus->priv);
106	phyregs = priv->regs;
107#endif
108
109	return tsec_local_mdio_read(phyregs, addr, dev_addr, regnum);
110}
111
112int tsec_phy_write(struct mii_dev *bus, int addr, int dev_addr, int regnum,
113			u16 value)
114{
115	struct tsec_mii_mng __iomem *phyregs;
116#ifndef CONFIG_DM_MDIO
117	phyregs = (struct tsec_mii_mng __iomem *)bus->priv;
118#else
119	struct tsec_mdio_priv *priv;
120
121	if (!bus->priv)
122		return -EINVAL;
123
124	priv = dev_get_priv(bus->priv);
125	phyregs = priv->regs;
126#endif
127
128	tsec_local_mdio_write(phyregs, addr, dev_addr, regnum, value);
129
130	return 0;
131}
132
133#ifndef CONFIG_DM_MDIO
134int fsl_pq_mdio_init(struct bd_info *bis, struct fsl_pq_mdio_info *info)
135{
136	struct mii_dev *bus = mdio_alloc();
137
138	if (!bus) {
139		printf("Failed to allocate FSL MDIO bus\n");
140		return -1;
141	}
142
143	bus->read = tsec_phy_read;
144	bus->write = tsec_phy_write;
145	bus->reset = fsl_pq_mdio_reset;
146	strcpy(bus->name, info->name);
147
148	bus->priv = (void *)info->regs;
149
150	return mdio_register(bus);
151}
152#else /* CONFIG_DM_MDIO */
153#if defined(CONFIG_PHYLIB)
154static int tsec_mdio_read(struct udevice *dev, int addr, int devad, int reg)
155{
156	struct mdio_perdev_priv *pdata = (dev) ? dev_get_uclass_priv(dev) :
157						 NULL;
158
159	if (pdata && pdata->mii_bus)
160		return tsec_phy_read(pdata->mii_bus, addr, devad, reg);
161
162	return -1;
163}
164
165static int tsec_mdio_write(struct udevice *dev, int addr, int devad, int reg,
166			   u16 val)
167{
168	struct mdio_perdev_priv *pdata = (dev) ? dev_get_uclass_priv(dev) :
169						 NULL;
170
171	if (pdata && pdata->mii_bus)
172		return tsec_phy_write(pdata->mii_bus, addr, devad, reg, val);
173
174	return -1;
175}
176
177static int tsec_mdio_reset(struct udevice *dev)
178{
179	struct mdio_perdev_priv *pdata = (dev) ? dev_get_uclass_priv(dev) :
180						 NULL;
181
182	if (pdata && pdata->mii_bus)
183		return fsl_pq_mdio_reset(pdata->mii_bus);
184
185	return -1;
186}
187
188static const struct mdio_ops tsec_mdio_ops = {
189	.read = tsec_mdio_read,
190	.write = tsec_mdio_write,
191	.reset = tsec_mdio_reset,
192};
193
194static struct fsl_pq_mdio_data etsec2_data = {
195	.mdio_regs_off = TSEC_MDIO_REGS_OFFSET,
196};
197
198static struct fsl_pq_mdio_data gianfar_data = {
199	.mdio_regs_off = 0x0,
200};
201
202static struct fsl_pq_mdio_data fman_data = {
203	.mdio_regs_off = 0x0,
204};
205
206static const struct udevice_id tsec_mdio_ids[] = {
207	{ .compatible = "fsl,gianfar-tbi", .data = (ulong)&gianfar_data },
208	{ .compatible = "fsl,gianfar-mdio", .data = (ulong)&gianfar_data },
209	{ .compatible = "fsl,etsec2-tbi", .data = (ulong)&etsec2_data },
210	{ .compatible = "fsl,etsec2-mdio", .data = (ulong)&etsec2_data },
211	{ .compatible = "fsl,fman-mdio", .data = (ulong)&fman_data },
212	{}
213};
214
215static int tsec_mdio_probe(struct udevice *dev)
216{
217	struct fsl_pq_mdio_data *data;
218	struct tsec_mdio_priv *priv = (dev) ? dev_get_priv(dev) : NULL;
219	struct mdio_perdev_priv *pdata = (dev) ? dev_get_uclass_priv(dev) :
220						 NULL;
221
222	if (!dev) {
223		printf("%s dev = NULL\n", __func__);
224		return -1;
225	}
226	if (!priv) {
227		printf("dev_get_priv(dev %p) = NULL\n", dev);
228		return -1;
229	}
230
231	data = (struct fsl_pq_mdio_data *)dev_get_driver_data(dev);
232	priv->regs = dev_remap_addr(dev) + data->mdio_regs_off;
233	debug("%s priv %p @ regs %p, pdata %p\n", __func__,
234	      priv, priv->regs, pdata);
235
236	return 0;
237}
238
239static int tsec_mdio_remove(struct udevice *dev)
240{
241	return 0;
242}
243
244U_BOOT_DRIVER(tsec_mdio) = {
245	.name = "tsec_mdio",
246	.id = UCLASS_MDIO,
247	.of_match = tsec_mdio_ids,
248	.probe = tsec_mdio_probe,
249	.remove = tsec_mdio_remove,
250	.ops = &tsec_mdio_ops,
251	.priv_auto	= sizeof(struct tsec_mdio_priv),
252	.plat_auto	= sizeof(struct mdio_perdev_priv),
253};
254#endif /* CONFIG_PHYLIB */
255#endif /* CONFIG_DM_MDIO */
256