1/*
2 * Copyright (c) 2003-2004 Stefano Ceccherini (burton666@libero.it)
3 * Copyright (c) 1997, 1998
4 *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 *    must display the following acknowledgement:
16 *	This product includes software developed by Bill Paul.
17 * 4. Neither the name of the author nor the names of any co-contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31 * THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
34
35#include "wb840.h"
36#include "device.h"
37#include "interface.h"
38
39#include <ByteOrder.h>
40#include <KernelExport.h>
41
42#include <string.h>
43
44
45#define SIO_SET(x)	\
46	write32(device->reg_base + WB_SIO,	\
47		read32(device->reg_base + WB_SIO) | x)
48
49#define SIO_CLR(x)	\
50	write32(device->reg_base + WB_SIO,	\
51		read32(device->reg_base + WB_SIO) & ~x)
52
53#define MII_DELAY(x)	read32(x->reg_base + WB_SIO)
54
55
56static void
57mii_sync(struct wb_device *device)
58{
59	// Set data bit and strobe the clock 32 times
60	int bits = 32;
61
62	SIO_SET(WB_SIO_MII_DIR|WB_SIO_MII_DATAIN);
63
64	while (--bits >= 0) {
65		SIO_SET(WB_SIO_MII_CLK);
66		MII_DELAY(device);
67		SIO_CLR(WB_SIO_MII_CLK);
68		MII_DELAY(device);
69	}
70}
71
72
73static void
74mii_send(wb_device *device, uint32 bits, int count)
75{
76	int	i;
77
78	SIO_CLR(WB_SIO_MII_CLK);
79
80	for (i = (0x1 << (count - 1)); i; i >>= 1) {
81    	if (bits & i)
82			SIO_SET(WB_SIO_MII_DATAIN);
83        else
84        	SIO_CLR(WB_SIO_MII_DATAIN);
85		MII_DELAY(device);
86		SIO_CLR(WB_SIO_MII_CLK);
87		MII_DELAY(device);
88		SIO_SET(WB_SIO_MII_CLK);
89	}
90}
91
92/*
93 * Read an PHY register through the MII.
94 */
95static int
96wb_mii_readreg(wb_device *device, wb_mii_frame *frame)
97{
98	int	i, ack;
99
100	/*
101	 * Set up frame for RX.
102	 */
103	frame->mii_stdelim = WB_MII_STARTDELIM;
104	frame->mii_opcode = WB_MII_READOP;
105	frame->mii_turnaround = 0;
106	frame->mii_data = 0;
107
108	write32(device->reg_base + WB_SIO, 0);
109
110	/*
111 	 * Turn on data xmit.
112	 */
113	SIO_SET(WB_SIO_MII_DIR);
114
115	mii_sync(device);
116
117	/*
118	 * Send command/address info.
119	 */
120	mii_send(device, frame->mii_stdelim, 2);
121	mii_send(device, frame->mii_opcode, 2);
122	mii_send(device, frame->mii_phyaddr, 5);
123	mii_send(device, frame->mii_regaddr, 5);
124
125	/* Idle bit */
126	SIO_CLR((WB_SIO_MII_CLK|WB_SIO_MII_DATAIN));
127	MII_DELAY(device);
128	SIO_SET(WB_SIO_MII_CLK);
129	MII_DELAY(device);
130
131	/* Turn off xmit. */
132	SIO_CLR(WB_SIO_MII_DIR);
133	/* Check for ack */
134	SIO_CLR(WB_SIO_MII_CLK);
135	MII_DELAY(device);
136	ack = read32(device->reg_base + WB_SIO) & WB_SIO_MII_DATAOUT;
137	SIO_SET(WB_SIO_MII_CLK);
138	MII_DELAY(device);
139	SIO_CLR(WB_SIO_MII_CLK);
140	MII_DELAY(device);
141	SIO_SET(WB_SIO_MII_CLK);
142	MII_DELAY(device);
143
144	/*
145	 * Now try reading data bits. If the ack failed, we still
146	 * need to clock through 16 cycles to keep the PHY(s) in sync.
147	 */
148	if (ack) {
149		for(i = 0; i < 16; i++) {
150			SIO_CLR(WB_SIO_MII_CLK);
151			MII_DELAY(device);
152			SIO_SET(WB_SIO_MII_CLK);
153			MII_DELAY(device);
154		}
155		goto fail;
156	}
157
158	for (i = 0x8000; i; i >>= 1) {
159		SIO_CLR(WB_SIO_MII_CLK);
160		MII_DELAY(device);
161		if (!ack) {
162			if (read32(device->reg_base + WB_SIO) & WB_SIO_MII_DATAOUT)
163				frame->mii_data |= i;
164			MII_DELAY(device);
165		}
166		SIO_SET(WB_SIO_MII_CLK);
167		MII_DELAY(device);
168	}
169
170fail:
171
172	SIO_CLR(WB_SIO_MII_CLK);
173	MII_DELAY(device);
174	SIO_SET(WB_SIO_MII_CLK);
175	MII_DELAY(device);
176
177	if (ack)
178		return 1;
179	return 0;
180}
181
182/*
183 * Write to a PHY register through the MII.
184 */
185static int
186wb_mii_writereg(wb_device *device, wb_mii_frame	*frame)
187{
188	/*
189	 * Set up frame for TX.
190	 */
191
192	frame->mii_stdelim = WB_MII_STARTDELIM;
193	frame->mii_opcode = WB_MII_WRITEOP;
194	frame->mii_turnaround = WB_MII_TURNAROUND;
195
196	/*
197 	 * Turn on data output.
198	 */
199	SIO_SET(WB_SIO_MII_DIR);
200
201	mii_sync(device);
202
203	mii_send(device, frame->mii_stdelim, 2);
204	mii_send(device, frame->mii_opcode, 2);
205	mii_send(device, frame->mii_phyaddr, 5);
206	mii_send(device, frame->mii_regaddr, 5);
207	mii_send(device, frame->mii_turnaround, 2);
208	mii_send(device, frame->mii_data, 16);
209
210	/* Idle bit. */
211	SIO_SET(WB_SIO_MII_CLK);
212	MII_DELAY(device);
213	SIO_CLR(WB_SIO_MII_CLK);
214	MII_DELAY(device);
215
216	/*
217	 * Turn off xmit.
218	 */
219	SIO_CLR(WB_SIO_MII_DIR);
220
221	return 0;
222}
223
224
225int
226wb_miibus_readreg(wb_device *device, int phy, int reg)
227{
228	struct wb_mii_frame frame;
229
230	memset(&frame, 0, sizeof(frame));
231
232	frame.mii_phyaddr = phy;
233	frame.mii_regaddr = reg;
234	wb_mii_readreg(device, &frame);
235
236	return frame.mii_data;
237}
238
239
240void
241wb_miibus_writereg(wb_device *device, int phy, int reg, int data)
242{
243	struct wb_mii_frame frame;
244
245	memset(&frame, 0, sizeof(frame));
246
247	frame.mii_phyaddr = phy;
248	frame.mii_regaddr = reg;
249	frame.mii_data = data;
250
251	wb_mii_writereg(device, &frame);
252
253	return;
254}
255
256
257#define EEPROM_DELAY(x)	read32(x->reg_base + WB_SIO)
258
259#if 0
260static void
261wb_eeprom_putbyte(wb_device *device, int addr)
262{
263	int	d, i;
264	int delay;
265
266	d = addr | WB_EECMD_READ;
267
268	/*
269	 * Feed in each bit and strobe the clock.
270	 */
271	for (i = 0x400; i; i >>= 1) {
272		if (d & i) {
273			SIO_SET(WB_SIO_EE_DATAIN);
274		} else {
275			SIO_CLR(WB_SIO_EE_DATAIN);
276		}
277		for (delay = 0; delay < 100; delay++)
278			MII_DELAY(device);
279
280		SIO_SET(WB_SIO_EE_CLK);
281
282		for (delay = 0; delay < 150; delay++)
283			MII_DELAY(device);
284
285		SIO_CLR(WB_SIO_EE_CLK);
286
287		for (delay = 0; delay < 100; delay++)
288			MII_DELAY(device);
289
290	}
291
292	return;
293}
294#endif
295
296
297static void
298wb_eeprom_askdata(wb_device *device, int addr)
299{
300	int command, i;
301	int delay;
302
303	command = addr | WB_EECMD_READ;
304
305	/* Feed in each bit and strobe the clock. */
306	for(i = 0x400; i; i >>= 1) {
307		if (command & i)
308			SIO_SET(WB_SIO_EE_DATAIN);
309		else
310			SIO_CLR(WB_SIO_EE_DATAIN);
311
312		SIO_SET(WB_SIO_EE_CLK);
313
314		SIO_CLR(WB_SIO_EE_CLK);
315		for (delay = 0; delay < 100; delay++)
316			EEPROM_DELAY(device);
317	}
318}
319
320
321/* Read a word of data stored in the EEPROM at address "addr". */
322static void
323wb_eeprom_getword(wb_device *device, int addr, uint16 *dest)
324{
325	int i;
326	uint16 word = 0;
327
328	/* Enter EEPROM access mode */
329	write32(device->reg_base + WB_SIO, WB_SIO_EESEL|WB_SIO_EE_CS);
330
331	/* Send address of word we want to read. */
332	wb_eeprom_askdata(device, addr);
333
334	write32(device->reg_base + WB_SIO, WB_SIO_EESEL|WB_SIO_EE_CS);
335
336	/* Start reading bits from EEPROM */
337	for (i = 0x8000; i > 0; i >>= 1) {
338		SIO_SET(WB_SIO_EE_CLK);
339		if (read32(device->reg_base + WB_SIO) & WB_SIO_EE_DATAOUT)
340			word |= i;
341		SIO_CLR(WB_SIO_EE_CLK);
342	}
343
344	/* Turn off EEPROM access mode */
345	write32(device->reg_base + WB_SIO, 0);
346
347	*dest = word;
348}
349
350
351void
352wb_read_eeprom(wb_device *device, void* dest,
353	int offset, int count, bool swap)
354{
355	int i;
356	uint16 word = 0, *ptr;
357
358	for (i = 0; i < count; i++) {
359		wb_eeprom_getword(device, offset + i, &word);
360		ptr = (uint16 *)((uint8 *)dest + (i * 2));
361		if (swap)
362			*ptr = B_BENDIAN_TO_HOST_INT16(word);
363		else
364			*ptr = word;
365	}
366}
367