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 323408 2017-09-11 00:51:47Z ian $");
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
79323408Sian#define	MII_KSZ8081_PHYCTL2			0x1f
80323408Sian
81275676Sbr#define	PS_TO_REG(p)	((p) / 200)
82273380Sbr
83273380Sbrstatic int micphy_probe(device_t);
84273380Sbrstatic int micphy_attach(device_t);
85323408Sianstatic void micphy_reset(struct mii_softc *);
86273380Sbrstatic int micphy_service(struct mii_softc *, struct mii_data *, int);
87273380Sbr
88273380Sbrstatic device_method_t micphy_methods[] = {
89273380Sbr	/* device interface */
90273380Sbr	DEVMETHOD(device_probe,		micphy_probe),
91273380Sbr	DEVMETHOD(device_attach,	micphy_attach),
92273380Sbr	DEVMETHOD(device_detach,	mii_phy_detach),
93273380Sbr	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
94273380Sbr	DEVMETHOD_END
95273380Sbr};
96273380Sbr
97273380Sbrstatic devclass_t micphy_devclass;
98273380Sbr
99273380Sbrstatic driver_t micphy_driver = {
100273380Sbr	"micphy",
101273380Sbr	micphy_methods,
102273380Sbr	sizeof(struct mii_softc)
103273380Sbr};
104273380Sbr
105273380SbrDRIVER_MODULE(micphy, miibus, micphy_driver, micphy_devclass, 0, 0);
106273380Sbr
107273380Sbrstatic const struct mii_phydesc micphys[] = {
108323408Sian	MII_PHY_DESC(MICREL, KSZ8081),
109273380Sbr	MII_PHY_DESC(MICREL, KSZ9021),
110310852Sloos	MII_PHY_DESC(MICREL, KSZ9031),
111273380Sbr	MII_PHY_END
112273380Sbr};
113273380Sbr
114273380Sbrstatic const struct mii_phy_funcs micphy_funcs = {
115273380Sbr	micphy_service,
116273380Sbr	ukphy_status,
117323408Sian	micphy_reset
118273380Sbr};
119273380Sbr
120310852Sloosstatic uint32_t
121310852Sloosksz9031_read(struct mii_softc *sc, uint32_t devaddr, uint32_t reg)
122310852Sloos{
123310852Sloos	/* Set up device address and register. */
124310852Sloos        PHY_WRITE(sc, MII_KSZ9031_MMD_ACCESS_CTRL, devaddr);
125310852Sloos        PHY_WRITE(sc, MII_KSZ9031_MMD_ACCESS_DATA, reg);
126310852Sloos
127310852Sloos	/* Select register data for MMD and read the value. */
128310852Sloos        PHY_WRITE(sc, MII_KSZ9031_MMD_ACCESS_CTRL,
129310852Sloos	    MII_KSZ9031_MMD_DATA_NOINC | devaddr);
130310852Sloos
131310852Sloos	return (PHY_READ(sc, MII_KSZ9031_MMD_ACCESS_DATA));
132310852Sloos}
133310852Sloos
134275676Sbrstatic void
135310852Sloosksz9031_write(struct mii_softc *sc, uint32_t devaddr, uint32_t reg,
136310852Sloos	uint32_t val)
137273380Sbr{
138273380Sbr
139310852Sloos	/* Set up device address and register. */
140310852Sloos	PHY_WRITE(sc, MII_KSZ9031_MMD_ACCESS_CTRL, devaddr);
141310852Sloos	PHY_WRITE(sc, MII_KSZ9031_MMD_ACCESS_DATA, reg);
142310852Sloos
143310852Sloos	/* Select register data for MMD and write the value. */
144310852Sloos	PHY_WRITE(sc, MII_KSZ9031_MMD_ACCESS_CTRL,
145310852Sloos	    MII_KSZ9031_MMD_DATA_NOINC | devaddr);
146310852Sloos	PHY_WRITE(sc, MII_KSZ9031_MMD_ACCESS_DATA, val);
147310852Sloos}
148310852Sloos
149310852Sloosstatic uint32_t
150310852Sloosksz9021_read(struct mii_softc *sc, uint32_t reg)
151310852Sloos{
152310852Sloos
153310852Sloos	PHY_WRITE(sc, MII_KSZPHY_EXTREG, reg);
154310852Sloos
155310852Sloos	return (PHY_READ(sc, MII_KSZPHY_EXTREG_READ));
156310852Sloos}
157310852Sloos
158310852Sloosstatic void
159310852Sloosksz9021_write(struct mii_softc *sc, uint32_t reg, uint32_t val)
160310852Sloos{
161310852Sloos
162273380Sbr	PHY_WRITE(sc, MII_KSZPHY_EXTREG, KSZPHY_EXTREG_WRITE | reg);
163273380Sbr	PHY_WRITE(sc, MII_KSZPHY_EXTREG_WRITE, val);
164273380Sbr}
165273380Sbr
166310852Sloosstatic void
167310852Sloosksz90x1_load_values(struct mii_softc *sc, phandle_t node,
168310852Sloos    uint32_t dev, uint32_t reg, char *field1, uint32_t f1mask, int f1off,
169310852Sloos    char *field2, uint32_t f2mask, int f2off, char *field3, uint32_t f3mask,
170310852Sloos    int f3off, char *field4, uint32_t f4mask, int f4off)
171273380Sbr{
172273380Sbr	pcell_t dts_value[1];
173273380Sbr	int len;
174273380Sbr	int val;
175273380Sbr
176310852Sloos	if (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ9031)
177310852Sloos		val = ksz9031_read(sc, dev, reg);
178310852Sloos	else
179310852Sloos		val = ksz9021_read(sc, reg);
180273380Sbr
181273380Sbr	if ((len = OF_getproplen(node, field1)) > 0) {
182273380Sbr		OF_getencprop(node, field1, dts_value, len);
183310852Sloos		val &= ~(f1mask << f1off);
184310852Sloos		val |= (PS_TO_REG(dts_value[0]) & f1mask) << f1off;
185273380Sbr	}
186273380Sbr
187310852Sloos	if (field2 != NULL && (len = OF_getproplen(node, field2)) > 0) {
188273380Sbr		OF_getencprop(node, field2, dts_value, len);
189310852Sloos		val &= ~(f2mask << f2off);
190310852Sloos		val |= (PS_TO_REG(dts_value[0]) & f2mask) << f2off;
191273380Sbr	}
192273380Sbr
193310852Sloos	if (field3 != NULL && (len = OF_getproplen(node, field3)) > 0) {
194273380Sbr		OF_getencprop(node, field3, dts_value, len);
195310852Sloos		val &= ~(f3mask << f3off);
196310852Sloos		val |= (PS_TO_REG(dts_value[0]) & f3mask) << f3off;
197273380Sbr	}
198273380Sbr
199310852Sloos	if (field4 != NULL && (len = OF_getproplen(node, field4)) > 0) {
200273380Sbr		OF_getencprop(node, field4, dts_value, len);
201310852Sloos		val &= ~(f4mask << f4off);
202310852Sloos		val |= (PS_TO_REG(dts_value[0]) & f4mask) << f4off;
203273380Sbr	}
204273380Sbr
205310852Sloos	if (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ9031)
206310852Sloos		ksz9031_write(sc, dev, reg, val);
207310852Sloos	else
208310852Sloos		ksz9021_write(sc, reg, val);
209310852Sloos}
210273380Sbr
211310852Sloosstatic void
212310852Sloosksz9031_load_values(struct mii_softc *sc, phandle_t node)
213310852Sloos{
214310852Sloos
215310852Sloos	ksz90x1_load_values(sc, node, 2, MII_KSZ9031_CONTROL_PAD_SKEW,
216310852Sloos	    "txen-skew-ps", 0xf, 0, "rxdv-skew-ps", 0xf, 4,
217310852Sloos	    NULL, 0, 0, NULL, 0, 0);
218310852Sloos	ksz90x1_load_values(sc, node, 2, MII_KSZ9031_RX_DATA_PAD_SKEW,
219310852Sloos	    "rxd0-skew-ps", 0xf, 0, "rxd1-skew-ps", 0xf, 4,
220310852Sloos	    "rxd2-skew-ps", 0xf, 8, "rxd3-skew-ps", 0xf, 12);
221310852Sloos	ksz90x1_load_values(sc, node, 2, MII_KSZ9031_TX_DATA_PAD_SKEW,
222310852Sloos	    "txd0-skew-ps", 0xf, 0, "txd1-skew-ps", 0xf, 4,
223310852Sloos	    "txd2-skew-ps", 0xf, 8, "txd3-skew-ps", 0xf, 12);
224310852Sloos	ksz90x1_load_values(sc, node, 2, MII_KSZ9031_CLOCK_PAD_SKEW,
225310852Sloos	    "rxc-skew-ps", 0x1f, 0, "txc-skew-ps", 0x1f, 5,
226310852Sloos	    NULL, 0, 0, NULL, 0, 0);
227273380Sbr}
228273380Sbr
229310852Sloosstatic void
230310852Sloosksz9021_load_values(struct mii_softc *sc, phandle_t node)
231310852Sloos{
232310852Sloos
233310852Sloos	ksz90x1_load_values(sc, node, 0, MII_KSZPHY_CLK_CONTROL_PAD_SKEW,
234310852Sloos	    "txen-skew-ps", 0xf, 0, "txc-skew-ps", 0xf, 4,
235310852Sloos	    "rxdv-skew-ps", 0xf, 8, "rxc-skew-ps", 0xf, 12);
236310852Sloos	ksz90x1_load_values(sc, node, 0, MII_KSZPHY_RX_DATA_PAD_SKEW,
237310852Sloos	    "rxd0-skew-ps", 0xf, 0, "rxd1-skew-ps", 0xf, 4,
238310852Sloos	    "rxd2-skew-ps", 0xf, 8, "rxd3-skew-ps", 0xf, 12);
239310852Sloos	ksz90x1_load_values(sc, node, 0, MII_KSZPHY_TX_DATA_PAD_SKEW,
240310852Sloos	    "txd0-skew-ps", 0xf, 0, "txd1-skew-ps", 0xf, 4,
241310852Sloos	    "txd2-skew-ps", 0xf, 8, "txd3-skew-ps", 0xf, 12);
242310852Sloos}
243310852Sloos
244273380Sbrstatic int
245273380Sbrmicphy_probe(device_t dev)
246273380Sbr{
247273380Sbr
248273380Sbr	return (mii_phy_dev_probe(dev, micphys, BUS_PROBE_DEFAULT));
249273380Sbr}
250273380Sbr
251273380Sbrstatic int
252273380Sbrmicphy_attach(device_t dev)
253273380Sbr{
254273380Sbr	struct mii_softc *sc;
255273380Sbr	phandle_t node;
256273380Sbr	device_t miibus;
257273380Sbr	device_t parent;
258273380Sbr
259273380Sbr	sc = device_get_softc(dev);
260273380Sbr
261273380Sbr	mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &micphy_funcs, 1);
262273380Sbr	mii_phy_setmedia(sc);
263273380Sbr
264323408Sian	/* Nothing further to configure for 8081 model. */
265323408Sian	if (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ8081)
266323408Sian		return (0);
267323408Sian
268273380Sbr	miibus = device_get_parent(dev);
269273380Sbr	parent = device_get_parent(miibus);
270273380Sbr
271273380Sbr	if ((node = ofw_bus_get_node(parent)) == -1)
272273380Sbr		return (ENXIO);
273273380Sbr
274310852Sloos	if (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ9031)
275310852Sloos		ksz9031_load_values(sc, node);
276310852Sloos	else
277310852Sloos		ksz9021_load_values(sc, node);
278273380Sbr
279273380Sbr	return (0);
280273380Sbr}
281273380Sbr
282323408Sianstatic void
283323408Sianmicphy_reset(struct mii_softc *sc)
284323408Sian{
285323408Sian	int reg;
286323408Sian
287323408Sian	/*
288323408Sian	 * The 8081 has no "sticky bits" that survive a soft reset; several bits
289323408Sian	 * in the Phy Control Register 2 must be preserved across the reset.
290323408Sian	 * These bits are set up by the bootloader; they control how the phy
291323408Sian	 * interfaces to the board (such as clock frequency and LED behavior).
292323408Sian	 */
293323408Sian	if (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ8081)
294323408Sian		reg = PHY_READ(sc, MII_KSZ8081_PHYCTL2);
295323408Sian	mii_phy_reset(sc);
296323408Sian	if (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ8081)
297323408Sian		PHY_WRITE(sc, MII_KSZ8081_PHYCTL2, reg);
298323408Sian}
299323408Sian
300273380Sbrstatic int
301273380Sbrmicphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
302273380Sbr{
303273380Sbr
304273380Sbr	switch (cmd) {
305273380Sbr	case MII_POLLSTAT:
306273380Sbr		break;
307273380Sbr
308273380Sbr	case MII_MEDIACHG:
309273380Sbr		mii_phy_setmedia(sc);
310273380Sbr		break;
311273380Sbr
312273380Sbr	case MII_TICK:
313273380Sbr		if (mii_phy_tick(sc) == EJUSTRETURN)
314273380Sbr			return (0);
315273380Sbr		break;
316273380Sbr	}
317273380Sbr
318273380Sbr	/* Update the media status. */
319273380Sbr	PHY_STATUS(sc);
320273380Sbr
321273380Sbr	/* Callback if something changed. */
322273380Sbr	mii_phy_update(sc, cmd);
323273380Sbr	return (0);
324273380Sbr}
325