xref: /openbsd-src/sys/arch/octeon/dev/cn30xxsmi.c (revision b9d7ccf36c39b92b7a10b4c20eb8ebbfc49a499e)
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