1 |
dpavlin |
32 |
/* |
2 |
dpavlin |
34 |
* Copyright (C) 2006-2007 Anders Gavare. All rights reserved. |
3 |
dpavlin |
32 |
* |
4 |
|
|
* Redistribution and use in source and binary forms, with or without |
5 |
|
|
* modification, are permitted provided that the following conditions are met: |
6 |
|
|
* |
7 |
|
|
* 1. Redistributions of source code must retain the above copyright |
8 |
|
|
* notice, this list of conditions and the following disclaimer. |
9 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
10 |
|
|
* notice, this list of conditions and the following disclaimer in the |
11 |
|
|
* documentation and/or other materials provided with the distribution. |
12 |
|
|
* 3. The name of the author may not be used to endorse or promote products |
13 |
|
|
* derived from this software without specific prior written permission. |
14 |
|
|
* |
15 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
|
|
* SUCH DAMAGE. |
26 |
|
|
* |
27 |
|
|
* |
28 |
dpavlin |
42 |
* $Id: dev_mb8696x.c,v 1.4 2007/06/15 19:11:15 debug Exp $ |
29 |
dpavlin |
32 |
* |
30 |
dpavlin |
42 |
* COMMENT: Fujitsu MB8696x Ethernet interface (used in the Dreamcast) |
31 |
dpavlin |
32 |
* |
32 |
|
|
* Used as the LAN adapter (MB86967) in the Dreamcast machine mode. |
33 |
|
|
* |
34 |
|
|
* |
35 |
|
|
* Note: This is just a bogus module so far. |
36 |
|
|
* |
37 |
|
|
* (TODO: "Reverse engineer" more of NetBSD's mb86960.c to implement this.) |
38 |
|
|
*/ |
39 |
|
|
|
40 |
|
|
#include <stdio.h> |
41 |
|
|
#include <stdlib.h> |
42 |
|
|
#include <string.h> |
43 |
|
|
#include <sys/time.h> |
44 |
|
|
|
45 |
|
|
#include "cpu.h" |
46 |
|
|
#include "device.h" |
47 |
|
|
#include "machine.h" |
48 |
|
|
#include "memory.h" |
49 |
|
|
#include "misc.h" |
50 |
|
|
#include "net.h" |
51 |
|
|
|
52 |
|
|
#include "mb86960reg.h" |
53 |
|
|
|
54 |
|
|
|
55 |
|
|
#ifdef UNSTABLE_DEVEL |
56 |
|
|
#define MB8696X_DEBUG |
57 |
|
|
#endif |
58 |
|
|
|
59 |
|
|
#ifdef MB8696X_DEBUG |
60 |
|
|
#define debug fatal |
61 |
|
|
#endif |
62 |
|
|
|
63 |
|
|
struct mb8696x_data { |
64 |
|
|
int addr_mult; |
65 |
|
|
|
66 |
|
|
/* |
67 |
|
|
* Registers: |
68 |
|
|
* |
69 |
|
|
* reg contains 32 registers. However, registers 8..15 are bank- |
70 |
|
|
* switched, based on a setting in FE_DLCR7. mar_8_15 are "multicast |
71 |
|
|
* address registers", and bmpr_8_15 are "buffer memory port |
72 |
|
|
* registers". |
73 |
|
|
*/ |
74 |
|
|
uint8_t reg[MB8696X_NREGS]; |
75 |
|
|
uint8_t mar_8_15[8]; |
76 |
|
|
uint8_t bmpr_8_15[8]; |
77 |
|
|
|
78 |
|
|
/* EEPROM contents and internal state during a read operation: */ |
79 |
|
|
uint8_t eeprom[FE_EEPROM_SIZE]; |
80 |
|
|
int eeprom_state; |
81 |
|
|
uint8_t eeprom_bit_count; |
82 |
|
|
uint8_t eeprom_command; |
83 |
|
|
uint16_t eeprom_data; |
84 |
|
|
}; |
85 |
|
|
|
86 |
|
|
#define EEPROM_STATE_NOTHING 0 |
87 |
|
|
#define EEPROM_STATE_READY 1 /* Waiting for start bit */ |
88 |
|
|
#define EEPROM_STATE_COMMAND 2 /* Waiting for 8 command bits */ |
89 |
|
|
#define EEPROM_STATE_READ 3 |
90 |
|
|
|
91 |
|
|
|
92 |
|
|
DEVICE_ACCESS(mb8696x) |
93 |
|
|
{ |
94 |
dpavlin |
42 |
struct mb8696x_data *d = extra; |
95 |
dpavlin |
32 |
uint64_t idata = 0, odata = 0; |
96 |
|
|
uint8_t *reg_ptr; |
97 |
|
|
|
98 |
|
|
if (writeflag == MEM_WRITE) |
99 |
|
|
idata = memory_readmax64(cpu, data, len); |
100 |
|
|
|
101 |
|
|
relative_addr /= d->addr_mult; |
102 |
|
|
|
103 |
|
|
/* |
104 |
|
|
* Default result on reads: (Note special treatment of banked regs.) |
105 |
|
|
*/ |
106 |
|
|
reg_ptr = &d->reg[relative_addr]; |
107 |
|
|
if (relative_addr >= 8 && relative_addr <= 15 && |
108 |
|
|
(d->reg[FE_DLCR7] & FE_D7_RBS) == FE_D7_RBS_MAR) |
109 |
|
|
reg_ptr = &d->mar_8_15[relative_addr - 8]; |
110 |
|
|
if (relative_addr >= 8 && relative_addr <= 15 && |
111 |
|
|
(d->reg[FE_DLCR7] & FE_D7_RBS) == FE_D7_RBS_BMPR) |
112 |
|
|
reg_ptr = &d->bmpr_8_15[relative_addr - 8]; |
113 |
|
|
|
114 |
|
|
odata = *reg_ptr; |
115 |
|
|
|
116 |
|
|
|
117 |
|
|
switch (relative_addr) { |
118 |
|
|
|
119 |
|
|
case FE_DLCR0: /* TX interrupt status */ |
120 |
|
|
case FE_DLCR1: /* RX interrupt status */ |
121 |
|
|
/* Write-1-to-clear: */ |
122 |
|
|
if (writeflag == MEM_WRITE) |
123 |
|
|
(*reg_ptr) &= ~idata; |
124 |
|
|
|
125 |
|
|
/* TODO: reassert interrupts */ |
126 |
|
|
|
127 |
|
|
break; |
128 |
|
|
|
129 |
|
|
case FE_DLCR2: /* TX interrupt control */ |
130 |
|
|
case FE_DLCR3: /* RX interrupt control */ |
131 |
|
|
if (writeflag == MEM_WRITE) |
132 |
|
|
(*reg_ptr) = idata; |
133 |
|
|
|
134 |
|
|
/* TODO: reassert interrupts */ |
135 |
|
|
|
136 |
|
|
break; |
137 |
|
|
|
138 |
|
|
case FE_DLCR6: |
139 |
|
|
case FE_DLCR8: /* Ethernet addr byte 0 */ |
140 |
|
|
case FE_DLCR9: /* Ethernet addr byte 1 */ |
141 |
|
|
case FE_DLCR10: /* Ethernet addr byte 2 */ |
142 |
|
|
case FE_DLCR11: /* Ethernet addr byte 3 */ |
143 |
|
|
case FE_DLCR12: /* Ethernet addr byte 4 */ |
144 |
|
|
case FE_DLCR13: /* Ethernet addr byte 5 */ |
145 |
|
|
if (writeflag == MEM_WRITE) |
146 |
|
|
(*reg_ptr) = idata; |
147 |
|
|
break; |
148 |
|
|
|
149 |
|
|
case FE_DLCR7: |
150 |
|
|
if (writeflag == MEM_WRITE) { |
151 |
|
|
/* Identification cannot be overwritten: */ |
152 |
|
|
(*reg_ptr) &= FE_D7_IDENT; |
153 |
|
|
(*reg_ptr) |= (idata & ~FE_D7_IDENT); |
154 |
|
|
} |
155 |
|
|
break; |
156 |
|
|
|
157 |
|
|
case FE_BMPR16: |
158 |
|
|
/* EEPROM control */ |
159 |
|
|
if (writeflag == MEM_WRITE) { |
160 |
|
|
if (idata & ~(FE_B16_DOUT | FE_B16_DIN | |
161 |
|
|
FE_B16_SELECT | FE_B16_CLOCK)) { |
162 |
|
|
fatal("mb8696x: UNIMPLEMENTED bits when " |
163 |
|
|
"writing to FE_BMPR16: 0x%02x\n", |
164 |
|
|
(int)idata); |
165 |
|
|
exit(1); |
166 |
|
|
} |
167 |
|
|
|
168 |
|
|
/* Dropped out of select state? */ |
169 |
|
|
if (!(idata & FE_B16_SELECT)) |
170 |
|
|
d->eeprom_state = EEPROM_STATE_NOTHING; |
171 |
|
|
|
172 |
|
|
/* Switching to select state? */ |
173 |
|
|
if (!((*reg_ptr) & FE_B16_SELECT) && |
174 |
|
|
idata & FE_B16_SELECT) |
175 |
|
|
d->eeprom_state = EEPROM_STATE_READY; |
176 |
|
|
|
177 |
|
|
/* Bit clock? */ |
178 |
|
|
if (!((*reg_ptr) & FE_B16_CLOCK) && |
179 |
|
|
idata & FE_B16_CLOCK) { |
180 |
|
|
int bit = d->reg[FE_BMPR17] & FE_B17_DATA? 1:0; |
181 |
|
|
switch (d->eeprom_state) { |
182 |
|
|
case EEPROM_STATE_READY: |
183 |
|
|
d->eeprom_state = EEPROM_STATE_COMMAND; |
184 |
|
|
d->eeprom_bit_count = 0; |
185 |
|
|
break; |
186 |
|
|
case EEPROM_STATE_COMMAND: |
187 |
|
|
d->eeprom_bit_count ++; |
188 |
|
|
d->eeprom_command <<= 1; |
189 |
|
|
d->eeprom_command |= bit; |
190 |
|
|
if (d->eeprom_bit_count == 8) { |
191 |
|
|
int addr = (d->eeprom_command |
192 |
|
|
& 0x7f) << 1; |
193 |
|
|
/* printf("COMMAND=%08x\n", |
194 |
|
|
d->eeprom_command); */ |
195 |
|
|
if (!(d->eeprom_command&0x80)) { |
196 |
|
|
fatal("WRITES to the " |
197 |
|
|
"EEPROM are not yet" |
198 |
|
|
" implemented.\n"); |
199 |
|
|
exit(1); |
200 |
|
|
} |
201 |
|
|
/* This is a read command. */ |
202 |
|
|
d->eeprom_bit_count = 0; |
203 |
|
|
d->eeprom_state = |
204 |
|
|
EEPROM_STATE_READ; |
205 |
|
|
d->eeprom_data = d->eeprom[addr] |
206 |
|
|
* 256 + d->eeprom[addr+1]; |
207 |
|
|
} |
208 |
|
|
break; |
209 |
|
|
case EEPROM_STATE_READ: |
210 |
|
|
d->reg[FE_BMPR17] = 0; |
211 |
|
|
if (d->eeprom_data & 0x8000) |
212 |
|
|
d->reg[FE_BMPR17] = FE_B17_DATA; |
213 |
|
|
d->eeprom_data <<= 1; |
214 |
|
|
d->eeprom_bit_count ++; |
215 |
|
|
if (d->eeprom_bit_count > 16) |
216 |
|
|
fatal("[ WARNING: more than 16" |
217 |
|
|
" bits of EEPROM data " |
218 |
|
|
"read? ]\n"); |
219 |
|
|
break; |
220 |
|
|
} |
221 |
|
|
} |
222 |
|
|
|
223 |
|
|
(*reg_ptr) = idata; |
224 |
|
|
} |
225 |
|
|
break; |
226 |
|
|
|
227 |
|
|
case FE_BMPR17: |
228 |
|
|
/* EEPROM data */ |
229 |
|
|
if (writeflag == MEM_WRITE) { |
230 |
|
|
if (idata & ~FE_B17_DATA) { |
231 |
|
|
fatal("mb8696x: UNIMPLEMENTED bits when " |
232 |
|
|
"writing to FE_BMPR17: 0x%02x\n", |
233 |
|
|
(int)idata); |
234 |
|
|
exit(1); |
235 |
|
|
} |
236 |
|
|
(*reg_ptr) = idata; |
237 |
|
|
} |
238 |
|
|
break; |
239 |
|
|
|
240 |
|
|
default: |
241 |
|
|
{ |
242 |
|
|
char *bank = ""; |
243 |
|
|
if ((d->reg[FE_DLCR7] & FE_D7_RBS) == FE_D7_RBS_MAR) |
244 |
|
|
bank = " (bank MAR)"; |
245 |
|
|
if ((d->reg[FE_DLCR7] & FE_D7_RBS) == FE_D7_RBS_BMPR) |
246 |
|
|
bank = " (bank BMPR)"; |
247 |
|
|
if (writeflag == MEM_READ) { |
248 |
|
|
fatal("[ mb8696x: read from UNIMPLEMENTED reg " |
249 |
|
|
"%i%s ]\n", (int)relative_addr, bank); |
250 |
|
|
} else { |
251 |
|
|
fatal("[ mb8696x: write to UNIMPLEMENTED reg " |
252 |
|
|
"%i%s: 0x%02x ]\n", (int)relative_addr, |
253 |
|
|
bank, (int)idata); |
254 |
|
|
} |
255 |
|
|
|
256 |
|
|
#ifdef MB8696X_DEBUG |
257 |
|
|
exit(1); |
258 |
|
|
#endif |
259 |
|
|
} |
260 |
|
|
} |
261 |
|
|
|
262 |
|
|
if (writeflag == MEM_READ) |
263 |
|
|
memory_writemax64(cpu, data, len, odata); |
264 |
|
|
|
265 |
|
|
return 1; |
266 |
|
|
} |
267 |
|
|
|
268 |
|
|
|
269 |
|
|
DEVINIT(mb8696x) |
270 |
|
|
{ |
271 |
dpavlin |
42 |
struct mb8696x_data *d; |
272 |
|
|
|
273 |
|
|
CHECK_ALLOCATION(d = malloc(sizeof(struct mb8696x_data))); |
274 |
dpavlin |
32 |
memset(d, 0, sizeof(struct mb8696x_data)); |
275 |
dpavlin |
42 |
|
276 |
dpavlin |
32 |
d->addr_mult = devinit->addr_mult; |
277 |
|
|
|
278 |
dpavlin |
42 |
memory_device_register(devinit->machine->memory, devinit->name, |
279 |
|
|
devinit->addr, MB8696X_NREGS * d->addr_mult, dev_mb8696x_access, d, |
280 |
dpavlin |
32 |
DM_DEFAULT, NULL); |
281 |
|
|
|
282 |
|
|
/* NetBSD/dreamcast expects ident = 86967. */ |
283 |
|
|
d->reg[FE_DLCR7] = FE_D7_IDENT_86967; |
284 |
|
|
|
285 |
|
|
/* |
286 |
|
|
* Generate the MAC address, both in the first 6 bytes of the |
287 |
|
|
* EEPROM, and in DLCR8..13: |
288 |
|
|
*/ |
289 |
dpavlin |
42 |
net_generate_unique_mac(devinit->machine, &d->eeprom[0]); |
290 |
dpavlin |
32 |
memcpy(&d->reg[FE_DLCR8], &d->eeprom[0], 6); |
291 |
|
|
|
292 |
|
|
return 1; |
293 |
|
|
} |
294 |
|
|
|