]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/enetc/enetc_mdio.c
Merge llvm-project main llvmorg-18-init-18359-g93248729cfae
[FreeBSD/FreeBSD.git] / sys / dev / enetc / enetc_mdio.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021 Alstom Group.
5  * Copyright (c) 2021 Semihalf.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <sys/param.h>
29 #include <sys/bus.h>
30 #include <sys/endian.h>
31 #include <sys/kernel.h>
32 #include <sys/module.h>
33 #include <sys/rman.h>
34
35 #include <machine/bus.h>
36 #include <machine/resource.h>
37
38 #include <dev/enetc/enetc_hw.h>
39 #include <dev/enetc/enetc_mdio.h>
40
41 #define ENETC_MDIO_RD4(regs, base, off) \
42         bus_read_4((regs), (base) + (off))
43 #define ENETC_MDIO_WR4(regs, base, off, value) \
44         bus_write_4((regs), (base) + (off), (value))
45
46 static int
47 enetc_mdio_wait(struct resource *regs, int mdio_base)
48 {
49         int i;
50         uint32_t val;
51
52         i = 0;
53         do {
54                 DELAY(100);
55                 val = ENETC_MDIO_RD4(regs, mdio_base, ENETC_MDIO_CFG);
56                 if ((val & MDIO_CFG_BSY) == 0)
57                         return (0);
58         } while (i++ < ENETC_TIMEOUT);
59
60         return (ETIMEDOUT);
61 }
62
63 int
64 enetc_mdio_read(struct resource *regs, int mdio_base, int phy, int reg)
65 {
66         uint32_t mdio_cfg, mdio_ctl;
67         uint16_t dev_addr;
68
69         mdio_cfg = MDIO_CFG_CLKDIV(ENETC_MDC_DIV) | MDIO_CFG_NEG;
70         if (reg & MII_ADDR_C45) {
71                 /* clause 45 */
72                 dev_addr = (reg >> 16) & 0x1f;
73                 mdio_cfg |= MDIO_CFG_ENC45;
74         } else {
75                 /* clause 22 */
76                 dev_addr = reg & 0x1f;
77                 mdio_cfg &= ~MDIO_CFG_ENC45;
78         }
79
80         ENETC_MDIO_WR4(regs, mdio_base, ENETC_MDIO_CFG, mdio_cfg);
81
82         if (enetc_mdio_wait(regs, mdio_base) == ETIMEDOUT)
83                 return (EIO);
84
85         /* Set port and device addr. */
86         mdio_ctl = MDIO_CTL_PORT_ADDR(phy) | MDIO_CTL_DEV_ADDR(dev_addr);
87         ENETC_MDIO_WR4(regs, mdio_base, ENETC_MDIO_CTL, mdio_ctl);
88
89         /* Set the register address. */
90         if (reg & MII_ADDR_C45) {
91                 ENETC_MDIO_WR4(regs, mdio_base, ENETC_MDIO_ADDR, reg & 0xffff);
92
93                 if (enetc_mdio_wait(regs, mdio_base) == ETIMEDOUT)
94                         return (EIO);
95         }
96
97         /* Initiate the read. */
98         ENETC_MDIO_WR4(regs, mdio_base, ENETC_MDIO_CTL, mdio_ctl | MDIO_CTL_READ);
99
100         if (enetc_mdio_wait(regs, mdio_base) == ETIMEDOUT)
101                 return (EIO);
102
103         /* Check if any error occurred while reading PHY register. */
104         if (ENETC_MDIO_RD4(regs, mdio_base, ENETC_MDIO_CFG) & MDIO_CFG_RD_ER)
105                 return (ENXIO);
106
107         return (MDIO_DATA(ENETC_MDIO_RD4(regs, mdio_base, ENETC_MDIO_DATA)));
108 }
109
110 int
111 enetc_mdio_write(struct resource *regs, int mdio_base, int phy, int reg,
112     int data)
113 {
114         uint32_t mdio_cfg, mdio_ctl;
115         uint16_t dev_addr;
116
117         mdio_cfg = MDIO_CFG_CLKDIV(ENETC_MDC_DIV) | MDIO_CFG_NEG;
118         if (reg & MII_ADDR_C45) {
119                 /* clause 45 */
120                 dev_addr = (reg >> 16) & 0x1f;
121                 mdio_cfg |= MDIO_CFG_ENC45;
122         } else {
123                 /* clause 22 */
124                 dev_addr = reg & 0x1f;
125                 mdio_cfg &= ~MDIO_CFG_ENC45;
126         }
127
128         ENETC_MDIO_WR4(regs, mdio_base, ENETC_MDIO_CFG, mdio_cfg);
129
130         if (enetc_mdio_wait(regs, mdio_base) == ETIMEDOUT)
131                 return (EIO);
132
133         /* Set port and device addr. */
134         mdio_ctl = MDIO_CTL_PORT_ADDR(phy) | MDIO_CTL_DEV_ADDR(dev_addr);
135         ENETC_MDIO_WR4(regs, mdio_base, ENETC_MDIO_CTL, mdio_ctl);
136
137         /* Set the register address. */
138         if (reg & MII_ADDR_C45) {
139                 ENETC_MDIO_WR4(regs, mdio_base, ENETC_MDIO_ADDR, reg & 0xffff);
140
141                 if (enetc_mdio_wait(regs, mdio_base) == ETIMEDOUT)
142                         return (EIO);
143         }
144
145         /* Write the value. */
146         ENETC_MDIO_WR4(regs, mdio_base, ENETC_MDIO_DATA, MDIO_DATA(data));
147
148         if (enetc_mdio_wait(regs, mdio_base) == ETIMEDOUT)
149                 return (EIO);
150
151         return (0);
152 }