micphy.c revision 310852
1273380Sbr/*-
2273380Sbr * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
3273380Sbr * All rights reserved.
4273380Sbr *
5273380Sbr * This software was developed by SRI International and the University of
6273380Sbr * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7273380Sbr * ("CTSRD"), as part of the DARPA CRASH research programme.
8273380Sbr *
9273380Sbr * Redistribution and use in source and binary forms, with or without
10273380Sbr * modification, are permitted provided that the following conditions
11273380Sbr * are met:
12273380Sbr * 1. Redistributions of source code must retain the above copyright
13273380Sbr *    notice, this list of conditions and the following disclaimer.
14273380Sbr * 2. Redistributions in binary form must reproduce the above copyright
15273380Sbr *    notice, this list of conditions and the following disclaimer in the
16273380Sbr *    documentation and/or other materials provided with the distribution.
17273380Sbr *
18273380Sbr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19273380Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20273380Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21273380Sbr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22273380Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23273380Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24273380Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25273380Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26273380Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27273380Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28273380Sbr * SUCH DAMAGE.
29273380Sbr */
30273380Sbr
31273380Sbr#include <sys/cdefs.h>
32273380Sbr__FBSDID("$FreeBSD: stable/11/sys/dev/mii/micphy.c 310852 2016-12-30 19:55:04Z loos $");
33273380Sbr
34273380Sbr/*
35273380Sbr * Micrel KSZ9021 Gigabit Ethernet Transceiver
36273380Sbr */
37273380Sbr
38273380Sbr#include <sys/param.h>
39273380Sbr#include <sys/systm.h>
40273380Sbr#include <sys/kernel.h>
41273380Sbr#include <sys/socket.h>
42273380Sbr#include <sys/errno.h>
43273380Sbr#include <sys/module.h>
44273380Sbr#include <sys/bus.h>
45273380Sbr#include <sys/malloc.h>
46273380Sbr
47273380Sbr#include <machine/bus.h>
48273380Sbr
49273380Sbr#include <net/if.h>
50273380Sbr#include <net/if_media.h>
51273380Sbr
52273380Sbr#include <dev/mii/mii.h>
53273380Sbr#include <dev/mii/miivar.h>
54273380Sbr#include "miidevs.h"
55273380Sbr
56273380Sbr#include "miibus_if.h"
57273380Sbr
58273380Sbr#include <dev/fdt/fdt_common.h>
59273380Sbr#include <dev/ofw/openfirm.h>
60273380Sbr#include <dev/ofw/ofw_bus.h>
61273380Sbr#include <dev/ofw/ofw_bus_subr.h>
62273380Sbr
63273380Sbr#define	MII_KSZPHY_EXTREG			0x0b
64273380Sbr#define	 KSZPHY_EXTREG_WRITE			(1 << 15)
65273380Sbr#define	MII_KSZPHY_EXTREG_WRITE			0x0c
66273380Sbr#define	MII_KSZPHY_EXTREG_READ			0x0d
67273380Sbr#define	MII_KSZPHY_CLK_CONTROL_PAD_SKEW		0x104
68273380Sbr#define	MII_KSZPHY_RX_DATA_PAD_SKEW		0x105
69273380Sbr#define	MII_KSZPHY_TX_DATA_PAD_SKEW		0x106
70310852Sloos/* KSZ9031 */
71310852Sloos#define	MII_KSZ9031_MMD_ACCESS_CTRL		0x0d
72310852Sloos#define	MII_KSZ9031_MMD_ACCESS_DATA		0x0e
73310852Sloos#define	 MII_KSZ9031_MMD_DATA_NOINC		(1 << 14)
74310852Sloos#define	MII_KSZ9031_CONTROL_PAD_SKEW		0x4
75310852Sloos#define	MII_KSZ9031_RX_DATA_PAD_SKEW		0x5
76310852Sloos#define	MII_KSZ9031_TX_DATA_PAD_SKEW		0x6
77310852Sloos#define	MII_KSZ9031_CLOCK_PAD_SKEW		0x8
78273380Sbr
79275676Sbr#define	PS_TO_REG(p)	((p) / 200)
80273380Sbr
81273380Sbrstatic int micphy_probe(device_t);
82273380Sbrstatic int micphy_attach(device_t);
83273380Sbrstatic int micphy_service(struct mii_softc *, struct mii_data *, int);
84273380Sbr
85273380Sbrstatic device_method_t micphy_methods[] = {
86273380Sbr	/* device interface */
87273380Sbr	DEVMETHOD(device_probe,		micphy_probe),
88273380Sbr	DEVMETHOD(device_attach,	micphy_attach),
89273380Sbr	DEVMETHOD(device_detach,	mii_phy_detach),
90273380Sbr	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
91273380Sbr	DEVMETHOD_END
92273380Sbr};
93273380Sbr
94273380Sbrstatic devclass_t micphy_devclass;
95273380Sbr
96273380Sbrstatic driver_t micphy_driver = {
97273380Sbr	"micphy",
98273380Sbr	micphy_methods,
99273380Sbr	sizeof(struct mii_softc)
100273380Sbr};
101273380Sbr
102273380SbrDRIVER_MODULE(micphy, miibus, micphy_driver, micphy_devclass, 0, 0);
103273380Sbr
104273380Sbrstatic const struct mii_phydesc micphys[] = {
105273380Sbr	MII_PHY_DESC(MICREL, KSZ9021),
106310852Sloos	MII_PHY_DESC(MICREL, KSZ9031),
107273380Sbr	MII_PHY_END
108273380Sbr};
109273380Sbr
110273380Sbrstatic const struct mii_phy_funcs micphy_funcs = {
111273380Sbr	micphy_service,
112273380Sbr	ukphy_status,
113273380Sbr	mii_phy_reset
114273380Sbr};
115273380Sbr
116310852Sloosstatic uint32_t
117310852Sloosksz9031_read(struct mii_softc *sc, uint32_t devaddr, uint32_t reg)
118310852Sloos{
119310852Sloos	/* Set up device address and register. */
120310852Sloos        PHY_WRITE(sc, MII_KSZ9031_MMD_ACCESS_CTRL, devaddr);
121310852Sloos        PHY_WRITE(sc, MII_KSZ9031_MMD_ACCESS_DATA, reg);
122310852Sloos
123310852Sloos	/* Select register data for MMD and read the value. */
124310852Sloos        PHY_WRITE(sc, MII_KSZ9031_MMD_ACCESS_CTRL,
125310852Sloos	    MII_KSZ9031_MMD_DATA_NOINC | devaddr);
126310852Sloos
127310852Sloos	return (PHY_READ(sc, MII_KSZ9031_MMD_ACCESS_DATA));
128310852Sloos}
129310852Sloos
130275676Sbrstatic void
131310852Sloosksz9031_write(struct mii_softc *sc, uint32_t devaddr, uint32_t reg,
132310852Sloos	uint32_t val)
133273380Sbr{
134273380Sbr
135310852Sloos	/* Set up device address and register. */
136310852Sloos	PHY_WRITE(sc, MII_KSZ9031_MMD_ACCESS_CTRL, devaddr);
137310852Sloos	PHY_WRITE(sc, MII_KSZ9031_MMD_ACCESS_DATA, reg);
138310852Sloos
139310852Sloos	/* Select register data for MMD and write the value. */
140310852Sloos	PHY_WRITE(sc, MII_KSZ9031_MMD_ACCESS_CTRL,
141310852Sloos	    MII_KSZ9031_MMD_DATA_NOINC | devaddr);
142310852Sloos	PHY_WRITE(sc, MII_KSZ9031_MMD_ACCESS_DATA, val);
143310852Sloos}
144310852Sloos
145310852Sloosstatic uint32_t
146310852Sloosksz9021_read(struct mii_softc *sc, uint32_t reg)
147310852Sloos{
148310852Sloos
149310852Sloos	PHY_WRITE(sc, MII_KSZPHY_EXTREG, reg);
150310852Sloos
151310852Sloos	return (PHY_READ(sc, MII_KSZPHY_EXTREG_READ));
152310852Sloos}
153310852Sloos
154310852Sloosstatic void
155310852Sloosksz9021_write(struct mii_softc *sc, uint32_t reg, uint32_t val)
156310852Sloos{
157310852Sloos
158273380Sbr	PHY_WRITE(sc, MII_KSZPHY_EXTREG, KSZPHY_EXTREG_WRITE | reg);
159273380Sbr	PHY_WRITE(sc, MII_KSZPHY_EXTREG_WRITE, val);
160273380Sbr}
161273380Sbr
162310852Sloosstatic void
163310852Sloosksz90x1_load_values(struct mii_softc *sc, phandle_t node,
164310852Sloos    uint32_t dev, uint32_t reg, char *field1, uint32_t f1mask, int f1off,
165310852Sloos    char *field2, uint32_t f2mask, int f2off, char *field3, uint32_t f3mask,
166310852Sloos    int f3off, char *field4, uint32_t f4mask, int f4off)
167273380Sbr{
168273380Sbr	pcell_t dts_value[1];
169273380Sbr	int len;
170273380Sbr	int val;
171273380Sbr
172310852Sloos	if (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ9031)
173310852Sloos		val = ksz9031_read(sc, dev, reg);
174310852Sloos	else
175310852Sloos		val = ksz9021_read(sc, reg);
176273380Sbr
177273380Sbr	if ((len = OF_getproplen(node, field1)) > 0) {
178273380Sbr		OF_getencprop(node, field1, dts_value, len);
179310852Sloos		val &= ~(f1mask << f1off);
180310852Sloos		val |= (PS_TO_REG(dts_value[0]) & f1mask) << f1off;
181273380Sbr	}
182273380Sbr
183310852Sloos	if (field2 != NULL && (len = OF_getproplen(node, field2)) > 0) {
184273380Sbr		OF_getencprop(node, field2, dts_value, len);
185310852Sloos		val &= ~(f2mask << f2off);
186310852Sloos		val |= (PS_TO_REG(dts_value[0]) & f2mask) << f2off;
187273380Sbr	}
188273380Sbr
189310852Sloos	if (field3 != NULL && (len = OF_getproplen(node, field3)) > 0) {
190273380Sbr		OF_getencprop(node, field3, dts_value, len);
191310852Sloos		val &= ~(f3mask << f3off);
192310852Sloos		val |= (PS_TO_REG(dts_value[0]) & f3mask) << f3off;
193273380Sbr	}
194273380Sbr
195310852Sloos	if (field4 != NULL && (len = OF_getproplen(node, field4)) > 0) {
196273380Sbr		OF_getencprop(node, field4, dts_value, len);
197310852Sloos		val &= ~(f4mask << f4off);
198310852Sloos		val |= (PS_TO_REG(dts_value[0]) & f4mask) << f4off;
199273380Sbr	}
200273380Sbr
201310852Sloos	if (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ9031)
202310852Sloos		ksz9031_write(sc, dev, reg, val);
203310852Sloos	else
204310852Sloos		ksz9021_write(sc, reg, val);
205310852Sloos}
206273380Sbr
207310852Sloosstatic void
208310852Sloosksz9031_load_values(struct mii_softc *sc, phandle_t node)
209310852Sloos{
210310852Sloos
211310852Sloos	ksz90x1_load_values(sc, node, 2, MII_KSZ9031_CONTROL_PAD_SKEW,
212310852Sloos	    "txen-skew-ps", 0xf, 0, "rxdv-skew-ps", 0xf, 4,
213310852Sloos	    NULL, 0, 0, NULL, 0, 0);
214310852Sloos	ksz90x1_load_values(sc, node, 2, MII_KSZ9031_RX_DATA_PAD_SKEW,
215310852Sloos	    "rxd0-skew-ps", 0xf, 0, "rxd1-skew-ps", 0xf, 4,
216310852Sloos	    "rxd2-skew-ps", 0xf, 8, "rxd3-skew-ps", 0xf, 12);
217310852Sloos	ksz90x1_load_values(sc, node, 2, MII_KSZ9031_TX_DATA_PAD_SKEW,
218310852Sloos	    "txd0-skew-ps", 0xf, 0, "txd1-skew-ps", 0xf, 4,
219310852Sloos	    "txd2-skew-ps", 0xf, 8, "txd3-skew-ps", 0xf, 12);
220310852Sloos	ksz90x1_load_values(sc, node, 2, MII_KSZ9031_CLOCK_PAD_SKEW,
221310852Sloos	    "rxc-skew-ps", 0x1f, 0, "txc-skew-ps", 0x1f, 5,
222310852Sloos	    NULL, 0, 0, NULL, 0, 0);
223273380Sbr}
224273380Sbr
225310852Sloosstatic void
226310852Sloosksz9021_load_values(struct mii_softc *sc, phandle_t node)
227310852Sloos{
228310852Sloos
229310852Sloos	ksz90x1_load_values(sc, node, 0, MII_KSZPHY_CLK_CONTROL_PAD_SKEW,
230310852Sloos	    "txen-skew-ps", 0xf, 0, "txc-skew-ps", 0xf, 4,
231310852Sloos	    "rxdv-skew-ps", 0xf, 8, "rxc-skew-ps", 0xf, 12);
232310852Sloos	ksz90x1_load_values(sc, node, 0, MII_KSZPHY_RX_DATA_PAD_SKEW,
233310852Sloos	    "rxd0-skew-ps", 0xf, 0, "rxd1-skew-ps", 0xf, 4,
234310852Sloos	    "rxd2-skew-ps", 0xf, 8, "rxd3-skew-ps", 0xf, 12);
235310852Sloos	ksz90x1_load_values(sc, node, 0, MII_KSZPHY_TX_DATA_PAD_SKEW,
236310852Sloos	    "txd0-skew-ps", 0xf, 0, "txd1-skew-ps", 0xf, 4,
237310852Sloos	    "txd2-skew-ps", 0xf, 8, "txd3-skew-ps", 0xf, 12);
238310852Sloos}
239310852Sloos
240273380Sbrstatic int
241273380Sbrmicphy_probe(device_t dev)
242273380Sbr{
243273380Sbr
244273380Sbr	return (mii_phy_dev_probe(dev, micphys, BUS_PROBE_DEFAULT));
245273380Sbr}
246273380Sbr
247273380Sbrstatic int
248273380Sbrmicphy_attach(device_t dev)
249273380Sbr{
250273380Sbr	struct mii_softc *sc;
251273380Sbr	phandle_t node;
252273380Sbr	device_t miibus;
253273380Sbr	device_t parent;
254273380Sbr
255273380Sbr	sc = device_get_softc(dev);
256273380Sbr
257273380Sbr	mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &micphy_funcs, 1);
258273380Sbr	mii_phy_setmedia(sc);
259273380Sbr
260273380Sbr	miibus = device_get_parent(dev);
261273380Sbr	parent = device_get_parent(miibus);
262273380Sbr
263273380Sbr	if ((node = ofw_bus_get_node(parent)) == -1)
264273380Sbr		return (ENXIO);
265273380Sbr
266310852Sloos	if (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ9031)
267310852Sloos		ksz9031_load_values(sc, node);
268310852Sloos	else
269310852Sloos		ksz9021_load_values(sc, node);
270273380Sbr
271273380Sbr	return (0);
272273380Sbr}
273273380Sbr
274273380Sbrstatic int
275273380Sbrmicphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
276273380Sbr{
277273380Sbr
278273380Sbr	switch (cmd) {
279273380Sbr	case MII_POLLSTAT:
280273380Sbr		break;
281273380Sbr
282273380Sbr	case MII_MEDIACHG:
283273380Sbr		mii_phy_setmedia(sc);
284273380Sbr		break;
285273380Sbr
286273380Sbr	case MII_TICK:
287273380Sbr		if (mii_phy_tick(sc) == EJUSTRETURN)
288273380Sbr			return (0);
289273380Sbr		break;
290273380Sbr	}
291273380Sbr
292273380Sbr	/* Update the media status. */
293273380Sbr	PHY_STATUS(sc);
294273380Sbr
295273380Sbr	/* Callback if something changed. */
296273380Sbr	mii_phy_update(sc, cmd);
297273380Sbr	return (0);
298273380Sbr}
299