xref: /haiku/src/add-ons/kernel/drivers/network/ether/wb840/interface.c (revision 97f11716bfaa0f385eb0e28a52bf56a5023b9e99)
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 
56 static void
mii_sync(struct wb_device * device)57 mii_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 
73 static void
mii_send(wb_device * device,uint32 bits,int count)74 mii_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  */
95 static int
wb_mii_readreg(wb_device * device,wb_mii_frame * frame)96 wb_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 
170 fail:
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  */
185 static int
wb_mii_writereg(wb_device * device,wb_mii_frame * frame)186 wb_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 
225 int
wb_miibus_readreg(wb_device * device,int phy,int reg)226 wb_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 
240 void
wb_miibus_writereg(wb_device * device,int phy,int reg,int data)241 wb_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
260 static void
261 wb_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 
297 static void
wb_eeprom_askdata(wb_device * device,int addr)298 wb_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". */
322 static void
wb_eeprom_getword(wb_device * device,int addr,uint16 * dest)323 wb_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 
351 void
wb_read_eeprom(wb_device * device,void * dest,int offset,int count,bool swap)352 wb_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