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