1/* MII, MDIO and EEPROM interface of SiS 900
2 *
3 * Copyright 2001-2005 pinc Software. All Rights Reserved.
4 * Distributed under the terms of the MIT license.
5 */
6
7
8#include <OS.h>
9#include <KernelExport.h>
10#include <SupportDefs.h>
11#include <PCI.h>
12
13#include <stdlib.h>
14#include <stdio.h>
15#include <string.h>
16#include <malloc.h>
17
18#include "ether_driver.h"
19#include "driver.h"
20#include "device.h"
21#include "interface.h"
22#include "sis900.h"
23
24
25// EEPROM access definitions
26#define EEPROM_DELAY()			read32(eepromAccess)
27#define EEPROM_READ32()			read32(eepromAccess)
28#define EEPROM_WRITE8(value)	write8(eepromAccess,value)
29#define EEPROM_WRITE32(value)	write32(eepromAccess,value)
30
31
32uint16
33eeprom_read(struct sis_info *info, int address)
34{
35	long eepromAccess = (long)info->registers + SiS900_MAC_EEPROM_ACCESS;
36	uint32 readCmd = SiS900_EEPROM_CMD_READ | address;
37	uint16 returnValue = 0;
38	int i;
39
40	EEPROM_WRITE32(0);
41	EEPROM_DELAY();
42	EEPROM_WRITE32(SiS900_EEPROM_CLOCK);
43	EEPROM_DELAY();
44
45	// Shift the read command (9) bits out.
46	for (i = 8; i >= 0; i--) {
47		uint32 value = (readCmd & (1 << i))	? SiS900_EEPROM_DATA_IN | SiS900_EEPROM_SELECT
48											: SiS900_EEPROM_SELECT;
49
50		EEPROM_WRITE32(value);
51		EEPROM_DELAY();
52		EEPROM_WRITE32(value | SiS900_EEPROM_CLOCK);
53		EEPROM_DELAY();
54	}
55
56	EEPROM_WRITE8(SiS900_EEPROM_SELECT);
57	EEPROM_DELAY();
58
59	// read in the 16 bit data
60	for (i = 16; i > 0; i--) {
61		EEPROM_WRITE32(SiS900_EEPROM_SELECT);
62		EEPROM_DELAY();
63		EEPROM_WRITE32(SiS900_EEPROM_SELECT | SiS900_EEPROM_CLOCK);
64		EEPROM_DELAY();
65		returnValue = (returnValue << 1) | ((EEPROM_READ32() & SiS900_EEPROM_DATA_OUT) ? 1 : 0);
66		EEPROM_DELAY();
67	}
68
69	EEPROM_WRITE32(0);
70	EEPROM_DELAY();
71	EEPROM_WRITE32(SiS900_EEPROM_CLOCK);
72
73	return returnValue;
74}
75
76
77/**************************** MII/MDIO ****************************/
78// #pragma mark -
79
80
81static inline uint32
82mdio_delay(addr_t address)
83{
84	return read32(address);
85}
86
87
88static void
89mdio_idle(uint32 address)
90{
91	write32(address, SiS900_MII_MDIO | SiS900_MII_MDDIR);
92	mdio_delay(address);
93	write32(address, SiS900_MII_MDIO | SiS900_MII_MDDIR | SiS900_MII_MDC);
94}
95
96
97static void
98mdio_reset(uint32 address)
99{
100	int32 i;
101
102	for (i = 32; i-- > 0;) {
103		write32(address, SiS900_MII_MDIO | SiS900_MII_MDDIR);
104		mdio_delay(address);
105		write32(address, SiS900_MII_MDIO | SiS900_MII_MDDIR | SiS900_MII_MDC);
106		mdio_delay(address);
107	}
108}
109
110
111void
112mdio_writeToPHY(struct sis_info *info, uint16 phy, uint16 reg, uint16 value)
113{
114	uint32 address = info->registers + SiS900_MAC_EEPROM_ACCESS;
115	int32 cmd = MII_CMD_WRITE | (phy << MII_PHY_SHIFT) | (reg << MII_REG_SHIFT);
116	int i;
117
118	mdio_reset(address);
119	mdio_idle(address);
120
121	// issue the command
122	for (i = 16; i-- > 0;) {
123		int32 data = SiS900_MII_MDDIR | (cmd & (1 << i) ? SiS900_MII_MDIO : 0);
124
125		write8(address, data);
126		mdio_delay(address);
127		write8(address, data | SiS900_MII_MDC);
128		mdio_delay(address);
129	}
130	mdio_delay(address);
131
132	// write the value
133	for (i = 16; i-- > 0;) {
134		int32 data = SiS900_MII_MDDIR | (value & (1 << i) ? SiS900_MII_MDIO : 0);
135
136		write32(address, data);
137		mdio_delay(address);
138		write32(address, data | SiS900_MII_MDC);
139		mdio_delay(address);
140	}
141	mdio_delay(address);
142
143	// clear extra bits
144	for (i = 2; i-- > 0;) {
145		write8(address, 0);
146		mdio_delay(address);
147		write8(address, SiS900_MII_MDC);
148		mdio_delay(address);
149	}
150
151	write32(address, 0);
152}
153
154
155uint16
156mdio_readFromPHY(struct sis_info *info, uint16 phy, uint16 reg)
157{
158	uint32 address = info->registers + SiS900_MAC_EEPROM_ACCESS;
159	int32 cmd = MII_CMD_READ | (phy << MII_PHY_SHIFT) | (reg << MII_REG_SHIFT);
160	uint16 value = 0;
161	int i;
162
163	mdio_reset(address);
164	mdio_idle(address);
165
166	for (i = 16; i-- > 0;) {
167		int32 data = SiS900_MII_MDDIR | (cmd & (1 << i) ? SiS900_MII_MDIO : 0);
168
169		write32(address, data);
170		mdio_delay(address);
171		write32(address, data | SiS900_MII_MDC);
172		mdio_delay(address);
173	}
174
175	// read the value
176	for (i = 16; i-- > 0;) {
177		write32(address, 0);
178		mdio_delay(address);
179		value = (value << 1) | (read32(address) & SiS900_MII_MDIO ? 1 : 0);
180		write32(address, SiS900_MII_MDC);
181		mdio_delay(address);
182	}
183	write32(address, 0);
184
185	return value;
186}
187
188
189uint16
190mdio_read(struct sis_info *info, uint16 reg)
191{
192	return mdio_readFromPHY(info, info->phy, reg);
193}
194
195
196void
197mdio_write(struct sis_info *info, uint16 reg, uint16 value)
198{
199	mdio_writeToPHY(info,info->phy,reg,value);
200}
201
202
203uint16
204mdio_statusFromPHY(struct sis_info *info, uint16 phy)
205{
206	uint16 status;
207	int i = 0;
208
209	// the status must be retrieved two times, because the first
210	// one may not work on some PHYs (notably ICS 1893)
211	while (i++ < 2)
212		status = mdio_readFromPHY(info, phy, MII_STATUS);
213
214	return status;
215}
216
217
218uint16
219mdio_status(struct sis_info *info)
220{
221	return mdio_statusFromPHY(info, info->phy);
222}
223
224