1 /* $OpenBSD: cn30xxsmi.c,v 1.13 2024/07/06 06:15:17 landry Exp $ */
2
3 /*
4 * Copyright (c) 2007 Internet Initiative Japan, Inc.
5 * All rights reserved.
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 AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/stdint.h>
32
33 #include <dev/ofw/fdt.h>
34 #include <dev/ofw/openfirm.h>
35
36 #include <machine/fdt.h>
37 #include <machine/octeonvar.h>
38
39 #include <octeon/dev/cn30xxsmireg.h>
40 #include <octeon/dev/cn30xxsmivar.h>
41
42 int cn30xxsmi_match(struct device *, void *, void *);
43 void cn30xxsmi_attach(struct device *, struct device *, void *);
44
45 const struct cfattach octsmi_ca = {
46 sizeof(struct cn30xxsmi_softc), cn30xxsmi_match, cn30xxsmi_attach
47 };
48
49 struct cfdriver octsmi_cd = {
50 NULL, "octsmi", DV_DULL
51 };
52
53 static SLIST_HEAD(, cn30xxsmi_softc) smi_list =
54 SLIST_HEAD_INITIALIZER(smi_list);
55
56 #define _SMI_RD8(sc, off) \
57 bus_space_read_8((sc)->sc_regt, (sc)->sc_regh, (off))
58 #define _SMI_WR8(sc, off, v) \
59 bus_space_write_8((sc)->sc_regt, (sc)->sc_regh, (off), (v))
60
61 int
cn30xxsmi_match(struct device * parent,void * match,void * aux)62 cn30xxsmi_match(struct device *parent, void *match, void *aux)
63 {
64 struct fdt_attach_args *faa = aux;
65
66 return OF_is_compatible(faa->fa_node, "cavium,octeon-3860-mdio");
67 }
68
69 void
cn30xxsmi_attach(struct device * parent,struct device * self,void * aux)70 cn30xxsmi_attach(struct device *parent, struct device *self, void *aux)
71 {
72 struct fdt_attach_args *faa = aux;
73 struct cn30xxsmi_softc *sc = (struct cn30xxsmi_softc *)self;
74
75 if (faa->fa_nreg != 1)
76 return;
77
78 sc->sc_node = faa->fa_node;
79 sc->sc_regt = faa->fa_iot;
80
81 if (bus_space_map(sc->sc_regt, faa->fa_reg[0].addr, faa->fa_reg[0].size,
82 0, &sc->sc_regh)) {
83 printf(": could not map registers\n");
84 return;
85 }
86
87 SLIST_INSERT_HEAD(&smi_list, sc, sc_link);
88
89 printf("\n");
90
91 _SMI_WR8(sc, SMI_CLK_OFFSET, 0x1464);
92 _SMI_WR8(sc, SMI_EN_OFFSET, SMI_EN_EN);
93 }
94
95 int
cn30xxsmi_read(struct cn30xxsmi_softc * sc,int phy_addr,int reg)96 cn30xxsmi_read(struct cn30xxsmi_softc *sc, int phy_addr, int reg)
97 {
98 uint64_t smi_rd;
99 int timo;
100
101 _SMI_WR8(sc, SMI_CMD_OFFSET, SMI_CMD_PHY_OP |
102 (phy_addr << SMI_CMD_PHY_ADR_SHIFT) |
103 (reg << SMI_CMD_REG_ADR_SHIFT));
104
105 timo = 10000;
106 smi_rd = _SMI_RD8(sc, SMI_RD_DAT_OFFSET);
107 while (ISSET(smi_rd, SMI_RD_DAT_PENDING)) {
108 if (timo-- == 0)
109 break;
110 delay(10);
111 smi_rd = _SMI_RD8(sc, SMI_RD_DAT_OFFSET);
112 }
113 if (ISSET(smi_rd, SMI_RD_DAT_PENDING)) {
114 return -1;
115 }
116
117 return ISSET(smi_rd, SMI_RD_DAT_VAL) ? (smi_rd & SMI_RD_DAT_DAT) : 0;
118 }
119
120 void
cn30xxsmi_write(struct cn30xxsmi_softc * sc,int phy_addr,int reg,int value)121 cn30xxsmi_write(struct cn30xxsmi_softc *sc, int phy_addr, int reg, int value)
122 {
123 uint64_t smi_wr;
124 int timo;
125
126 smi_wr = 0;
127 SET(smi_wr, value);
128 _SMI_WR8(sc, SMI_WR_DAT_OFFSET, smi_wr);
129
130 _SMI_WR8(sc, SMI_CMD_OFFSET, (phy_addr << SMI_CMD_PHY_ADR_SHIFT) |
131 (reg << SMI_CMD_REG_ADR_SHIFT));
132
133 timo = 10000;
134 smi_wr = _SMI_RD8(sc, SMI_WR_DAT_OFFSET);
135 while (ISSET(smi_wr, SMI_WR_DAT_PENDING)) {
136 if (timo-- == 0)
137 break;
138 delay(10);
139 smi_wr = _SMI_RD8(sc, SMI_WR_DAT_OFFSET);
140 }
141 if (ISSET(smi_wr, SMI_WR_DAT_PENDING)) {
142 /* XXX log */
143 printf("ERROR: cnmac_mii_writereg(0x%x, 0x%x, 0x%x) timed out.\n",
144 phy_addr, reg, value);
145 }
146 }
147
148 int
cn30xxsmi_get_phy(int phandle,int port,struct cn30xxsmi_softc ** psmi,int * preg)149 cn30xxsmi_get_phy(int phandle, int port, struct cn30xxsmi_softc **psmi,
150 int *preg)
151 {
152 /* PHY addresses for Portwell CAM-0100 */
153 static const int cam0100_phys[] = {
154 0x02, 0x03, 0x22
155 };
156 /* PHY addresses for Check Point UTM-1 EDGE N */
157 static const int cpn100_phys[] = {
158 0x0c, 0x11, 0x0d
159 };
160 /* PHY addresses for Netgear ProSecure UTM25 */
161 static const int nutm25_phys[] = {
162 0x00, 0x04, 0x09
163 };
164
165 struct cn30xxsmi_softc *smi;
166 int parent, phynode;
167 int reg;
168
169 if (phandle != 0) {
170 phynode = OF_getnodebyphandle(phandle);
171 if (phynode == 0)
172 return ENOENT;
173 reg = OF_getpropint(phynode, "reg", UINT32_MAX);
174 if (reg == UINT32_MAX)
175 return ENOENT;
176
177 parent = OF_parent(phynode);
178 SLIST_FOREACH(smi, &smi_list, sc_link) {
179 if (smi->sc_node == parent)
180 goto found;
181 }
182 return ENOENT;
183 } else {
184 smi = SLIST_FIRST(&smi_list);
185 if (smi == NULL)
186 return ENOENT;
187
188 switch (octeon_board) {
189 case BOARD_CHECKPOINT_N100:
190 if (port >= nitems(cpn100_phys))
191 return ENOENT;
192 reg = cpn100_phys[port];
193 break;
194 case BOARD_NETGEAR_UTM25:
195 if (port >= nitems(nutm25_phys))
196 return ENOENT;
197 reg = nutm25_phys[port];
198 break;
199 case BOARD_UBIQUITI_E100:
200 /* XXX Skip the switch port on ERPoe-5.
201 * XXX There is no driver for it. */
202 if (port > 1 && octeon_boot_info->board_rev_major == 1)
203 return ENOENT;
204 case BOARD_UBIQUITI_E120:
205 if (port > 2)
206 return ENOENT;
207 reg = 7 - port;
208 break;
209 case BOARD_CN3010_EVB_HS5:
210 if (port >= nitems(cam0100_phys))
211 return ENOENT;
212 reg = cam0100_phys[port];
213 break;
214 default:
215 return ENOENT;
216 }
217 }
218
219 found:
220 *psmi = smi;
221 *preg = reg;
222 return 0;
223 }
224