source: rtems-libbsd/rtemsbsd/sys/powerpc/fdt_phy.c @ cd089b9

55-freebsd-126-freebsd-12
Last change on this file since cd089b9 was cd089b9, checked in by Sebastian Huber <sebastian.huber@…>, on 05/05/17 at 06:47:39

Linux update to 4.11-rc5

Linux baseline a71c9a1c779f2499fb2afc0553e543f18aff6edf (4.11-rc5).

  • Property mode set to 100644
File size: 7.0 KB
Line 
1#include <machine/rtems-bsd-kernel-space.h>
2#include <rtems/bsd/local/opt_dpaa.h>
3
4/*
5 * Copyright (c) 2016 embedded brains GmbH
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <machine/rtems-bsd-kernel-space.h>
31
32#include <sys/param.h>
33#include <sys/lock.h>
34#include <sys/time.h>
35#include <sys/queue.h>
36#include <sys/mutex.h>
37#include <sys/kernel.h>
38#include <sys/malloc.h>
39
40#include <libfdt.h>
41
42#include <rtems/bsd.h>
43
44#include <bsp/fdt.h>
45
46#include <linux/of_mdio.h>
47
48#define MDIO_LOCK()     mtx_lock(&mdio.mutex)
49#define MDIO_UNLOCK()   mtx_unlock(&mdio.mutex)
50
51static struct {
52        SLIST_HEAD(, mdio_bus) instances;
53        struct mtx mutex;
54} mdio = {
55        .instances = SLIST_HEAD_INITIALIZER(mdio.instances)
56};
57
58MTX_SYSINIT(mdio_mutex, &mdio.mutex, "FDT MDIO", MTX_DEF);
59
60static uint64_t
61fdt_get_address(const void *fdt, int node)
62{
63        uint64_t addr;
64        int nodes[16];
65        size_t i;
66        int ac;
67
68        i = 0;
69        do {
70                nodes[i] = node;
71                ++i;
72                node = fdt_parent_offset(fdt, node);
73        } while (node >= 0 && i < nitems(nodes));
74
75        if (node >= 0) {
76                return (0);
77        }
78
79        ac = 1;
80        addr = 0;
81        while (i > 0) {
82                const fdt32_t *p;
83                int len;
84
85                p = fdt_getprop(fdt, nodes[i - 1], "reg", &len);
86                if (p != NULL) {
87                        if (ac == 1 && len >= 4) {
88                                addr += fdt32_to_cpu(p[0]);
89                        } else if (ac == 2 && len >= 8) {
90                                addr += fdt32_to_cpu(p[1]);
91                                addr += (uint64_t)fdt32_to_cpu(p[0]) << 32;
92                        } else {
93                                return (0);
94                        }
95                }
96
97                p = fdt_getprop(fdt, nodes[i - 1], "#address-cells", &len);
98                if (p != NULL) {
99                        if (len != 4) {
100                                return (0);
101                        }
102                        ac = (int)fdt32_to_cpu(p[0]);
103                        if (ac != 1 && ac != 2) {
104                                return (0);
105                        }
106                }
107
108                --i;
109        }
110
111        return (addr);
112}
113
114struct fman_mdio_regs {
115        uint32_t reserved[12];
116        uint32_t mdio_cfg;
117        uint32_t mdio_ctrl;
118        uint32_t mdio_data;
119        uint32_t mdio_addr;
120};
121
122#define MDIO_CFG_BSY            (1U << 31)
123#define MDIO_CFG_ENC45          (1U << 6)
124#define MDIO_CFG_RD_ERR         (1U << 1)
125
126#define MDIO_CTRL_READ          (1U << 15)
127#define MDIO_CTRL_REG_ADDR(x)   ((x) & 0x1fU)
128#define MDIO_CTRL_PHY_ADDR(x)   (((x) & 0x1fU) << 5)
129
130struct fman_mdio_bus {
131        struct mdio_bus base;
132        volatile struct fman_mdio_regs *regs;
133};
134
135static int
136fman_mdio_wait(volatile struct fman_mdio_regs *regs)
137{
138        struct bintime start;
139
140        rtems_bsd_binuptime(&start);
141
142        while ((regs->mdio_cfg & MDIO_CFG_BSY) != 0) {
143                struct bintime now;
144
145                rtems_bsd_binuptime(&now);
146                if (bttosbt(now) - bttosbt(start) > 100 * SBT_1US) {
147                        break;
148                }
149        }
150
151        /* Check again, to take thread pre-emption into account */
152        if ((regs->mdio_cfg & MDIO_CFG_BSY) != 0) {
153                return (EIO);
154        }
155
156        return (0);
157}
158
159static int
160fman_mdio_read(struct mdio_bus *base, int phy, int reg)
161{
162        struct fman_mdio_bus *fm;
163        volatile struct fman_mdio_regs *regs;
164        int val;
165        int err;
166
167        fm = (struct fman_mdio_bus *)base;
168        regs = fm->regs;
169
170        MDIO_LOCK();
171
172        err = fman_mdio_wait(regs);
173        if (err == 0) {
174                uint32_t mdio_cfg;
175                uint32_t mdio_ctrl;
176
177                mdio_cfg = regs->mdio_cfg;
178                mdio_cfg &= ~MDIO_CFG_ENC45;
179                regs->mdio_cfg = mdio_cfg;
180
181                mdio_ctrl = MDIO_CTRL_PHY_ADDR(phy) | MDIO_CTRL_REG_ADDR(reg);
182                regs->mdio_ctrl = mdio_ctrl;
183                mdio_ctrl |= MDIO_CTRL_READ;
184                regs->mdio_ctrl = mdio_ctrl;
185
186                err = fman_mdio_wait(regs);
187                if (err == 0 && (regs->mdio_cfg & MDIO_CFG_RD_ERR) == 0) {
188                        val = (int)(regs->mdio_data & 0xffff);
189                } else {
190                        val = 0xffff;
191                }
192        } else {
193                val = 0xffff;
194        }
195
196        MDIO_UNLOCK();
197
198        return (val);
199}
200
201static int
202fman_mdio_write(struct mdio_bus *base, int phy, int reg, int val)
203{
204        struct fman_mdio_bus *fm;
205        volatile struct fman_mdio_regs *regs;
206        int err;
207
208        fm = (struct fman_mdio_bus *)base;
209        regs = fm->regs;
210
211        MDIO_LOCK();
212
213        err = fman_mdio_wait(regs);
214        if (err == 0) {
215                uint32_t mdio_cfg;
216                uint32_t mdio_ctrl;
217
218                mdio_cfg = regs->mdio_cfg;
219                mdio_cfg &= ~MDIO_CFG_ENC45;
220                regs->mdio_cfg = mdio_cfg;
221
222                mdio_ctrl = MDIO_CTRL_PHY_ADDR(phy) | MDIO_CTRL_REG_ADDR(reg);
223                regs->mdio_ctrl = mdio_ctrl;
224
225                regs->mdio_data = (uint32_t)(val & 0xffff);
226
227                fman_mdio_wait(regs);
228        }
229
230        MDIO_UNLOCK();
231
232        return (0);
233}
234
235static struct mdio_bus *
236create_fman_mdio(const void *fdt, int mdio_node)
237{
238        struct fman_mdio_bus *fm = NULL;
239
240        fm = malloc(sizeof(*fm), M_TEMP, M_WAITOK | M_ZERO);
241        if (fm == NULL) {
242                return (NULL);
243        }
244
245        fm->base.read = fman_mdio_read;
246        fm->base.write = fman_mdio_write;
247        fm->base.node = mdio_node;
248        fm->regs = (volatile struct fman_mdio_regs *)(uintptr_t)
249            fdt_get_address(fdt, mdio_node);
250
251        return (&fm->base);
252}
253
254static struct mdio_bus *
255create_mdio_bus(const void *fdt, int mdio_node)
256{
257
258        if (fdt_node_check_compatible(fdt, mdio_node,
259            "fsl,fman-memac-mdio") == 0 ||
260            fdt_node_check_compatible(fdt, mdio_node,
261            "fsl,fman-xmdio") == 0) {
262                return (create_fman_mdio(fdt, mdio_node));
263        } else {
264                return (NULL);
265        }
266}
267
268static int
269find_mdio_bus(const void *fdt, int mdio_node,
270    struct phy_device *phy_dev)
271{
272        struct mdio_bus *mdio_bus = NULL;
273
274        SLIST_FOREACH(mdio_bus, &mdio.instances, next) {
275                if (mdio_bus->node == mdio_node) {
276                        break;
277                }
278        }
279
280        if (mdio_bus == NULL) {
281                mdio_bus = create_mdio_bus(fdt, mdio_node);
282        }
283
284        if (mdio_bus == NULL) {
285                return (ENXIO);
286        }
287
288        phy_dev->mdio.bus = mdio_bus;
289        return (0);
290}
291
292static struct phy_device *
293phy_obtain(const void *fdt, int mdio_node, int phy)
294{
295        struct phy_device *phy_dev;
296        int err;
297
298        phy_dev = malloc(sizeof(*phy_dev), M_TEMP, M_WAITOK | M_ZERO);
299        if (phy_dev == NULL) {
300                return (NULL);
301        }
302
303        phy_dev->mdio.addr = phy;
304        MDIO_LOCK();
305        err = find_mdio_bus(fdt, mdio_node, phy_dev);
306        MDIO_UNLOCK();
307
308        if (err != 0) {
309                free(phy_dev, M_TEMP);
310                return (NULL);
311        }
312
313        return (phy_dev);
314}
315
316struct phy_device *
317of_phy_find_device(struct device_node *dn)
318{
319        const void *fdt;
320        const fdt32_t *phy;
321        int len;
322        int mdio_node;
323
324        fdt = bsp_fdt_get();
325
326        phy = fdt_getprop(fdt, dn->offset, "reg", &len);
327        if (phy == NULL || len != sizeof(*phy)) {
328                return (NULL);
329        }
330
331        mdio_node = fdt_parent_offset(fdt, dn->offset);
332        if (mdio_node < 0) {
333                return (NULL);
334        }
335
336        return (phy_obtain(fdt, mdio_node, (int)fdt32_to_cpu(*phy)));
337}
Note: See TracBrowser for help on using the repository browser.