1/*	$NetBSD: mii_bitbang.c,v 1.12 2008/05/04 17:06:09 xtraeme Exp $	*/
2
3/*-
4 * SPDX-License-Identifier: BSD-2-Clause-NetBSD
5 *
6 * Copyright (c) 1999 The NetBSD Foundation, Inc.
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to The NetBSD Foundation
10 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
11 * NASA Ames Research Center.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following didevlaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following didevlaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35/*
36 * Common module for bit-bang'ing the MII.
37 */
38
39#include <sys/cdefs.h>
40__FBSDID("$FreeBSD$");
41
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <sys/module.h>
45
46#include <dev/mii/mii.h>
47#include <dev/mii/mii_bitbang.h>
48
49MODULE_VERSION(mii_bitbang, 1);
50
51static void mii_bitbang_sendbits(device_t dev, mii_bitbang_ops_t ops,
52    uint32_t data, int nbits);
53
54#define	MWRITE(x)							\
55do {									\
56	ops->mbo_write(dev, (x));					\
57	DELAY(1);							\
58} while (/* CONSTCOND */ 0)
59
60#define	MREAD		ops->mbo_read(dev)
61
62#define	MDO		ops->mbo_bits[MII_BIT_MDO]
63#define	MDI		ops->mbo_bits[MII_BIT_MDI]
64#define	MDC		ops->mbo_bits[MII_BIT_MDC]
65#define	MDIRPHY		ops->mbo_bits[MII_BIT_DIR_HOST_PHY]
66#define	MDIRHOST	ops->mbo_bits[MII_BIT_DIR_PHY_HOST]
67
68/*
69 * mii_bitbang_sync:
70 *
71 *	Synchronize the MII.
72 */
73void
74mii_bitbang_sync(device_t dev, mii_bitbang_ops_t ops)
75{
76	int i;
77	uint32_t v;
78
79	v = MDIRPHY | MDO;
80
81	MWRITE(v);
82	for (i = 0; i < 32; i++) {
83		MWRITE(v | MDC);
84		MWRITE(v);
85	}
86}
87
88/*
89 * mii_bitbang_sendbits:
90 *
91 *	Send a series of bits to the MII.
92 */
93static void
94mii_bitbang_sendbits(device_t dev, mii_bitbang_ops_t ops, uint32_t data,
95    int nbits)
96{
97	int i;
98	uint32_t v;
99
100	v = MDIRPHY;
101	MWRITE(v);
102
103	for (i = 1 << (nbits - 1); i != 0; i >>= 1) {
104		if (data & i)
105			v |= MDO;
106		else
107			v &= ~MDO;
108		MWRITE(v);
109		MWRITE(v | MDC);
110		MWRITE(v);
111	}
112}
113
114/*
115 * mii_bitbang_readreg:
116 *
117 *	Read a PHY register by bit-bang'ing the MII.
118 */
119int
120mii_bitbang_readreg(device_t dev, mii_bitbang_ops_t ops, int phy, int reg)
121{
122	int i, error, val;
123
124	mii_bitbang_sync(dev, ops);
125
126	mii_bitbang_sendbits(dev, ops, MII_COMMAND_START, 2);
127	mii_bitbang_sendbits(dev, ops, MII_COMMAND_READ, 2);
128	mii_bitbang_sendbits(dev, ops, phy, 5);
129	mii_bitbang_sendbits(dev, ops, reg, 5);
130
131	/* Switch direction to PHY->host, without a clock transition. */
132	MWRITE(MDIRHOST);
133
134	/* Turnaround clock. */
135	MWRITE(MDIRHOST | MDC);
136	MWRITE(MDIRHOST);
137
138	/* Check for error. */
139	error = MREAD & MDI;
140
141	/* Idle clock. */
142	MWRITE(MDIRHOST | MDC);
143	MWRITE(MDIRHOST);
144
145	val = 0;
146	for (i = 0; i < 16; i++) {
147		val <<= 1;
148		/* Read data prior to clock low-high transition. */
149		if (error == 0 && (MREAD & MDI) != 0)
150			val |= 1;
151
152		MWRITE(MDIRHOST | MDC);
153		MWRITE(MDIRHOST);
154	}
155
156	/* Set direction to host->PHY, without a clock transition. */
157	MWRITE(MDIRPHY);
158
159	return (error != 0 ? 0 : val);
160}
161
162/*
163 * mii_bitbang_writereg:
164 *
165 *	Write a PHY register by bit-bang'ing the MII.
166 */
167void
168mii_bitbang_writereg(device_t dev, mii_bitbang_ops_t ops, int phy, int reg,
169    int val)
170{
171
172	mii_bitbang_sync(dev, ops);
173
174	mii_bitbang_sendbits(dev, ops, MII_COMMAND_START, 2);
175	mii_bitbang_sendbits(dev, ops, MII_COMMAND_WRITE, 2);
176	mii_bitbang_sendbits(dev, ops, phy, 5);
177	mii_bitbang_sendbits(dev, ops, reg, 5);
178	mii_bitbang_sendbits(dev, ops, MII_COMMAND_ACK, 2);
179	mii_bitbang_sendbits(dev, ops, val, 16);
180
181	MWRITE(MDIRPHY);
182}
183