1 /*
2 * Copyright (C) 2007-2009 Anders Gavare. All rights reserved.
3 *
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 * COMMENT: Realtek 8139 ethernet controller
29 *
30 * TODO: Pretty much everything.
31 */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "cpu.h"
38 #include "device.h"
39 #include "emul.h"
40 #include "interrupt.h"
41 #include "machine.h"
42 #include "memory.h"
43 #include "misc.h"
44 #include "net.h"
45
46 #include "thirdparty/rtl81x9reg.h"
47
48
49 #define DEV_RTL8139C_LENGTH 0x100
50 #define EEPROM_SIZE 0x100
51
52 struct rtl8139c_data {
53 struct nic_data nic;
54
55 struct interrupt irq;
56
57 /* Registers: */
58 uint8_t rl_command;
59 uint8_t rl_eecmd;
60
61 /* EEPROM: */
62 int eeprom_address_width;
63 int eeprom_selected;
64 int8_t eeprom_cur_cmd_bit;
65 uint16_t eeprom_cur_cmd;
66 uint16_t eeprom_cur_data;
67 uint16_t eeprom_reg[EEPROM_SIZE];
68 };
69
70
71 /*
72 * eeprom_clk():
73 *
74 * Called whenever the eeprom CLK bit is toggled from 0 to 1.
75 */
eeprom_clk(struct rtl8139c_data * d)76 static void eeprom_clk(struct rtl8139c_data *d)
77 {
78 int data_in = d->rl_eecmd & RL_EE_DATAIN? 1 : 0;
79
80 if (d->eeprom_cur_cmd_bit < d->eeprom_address_width + 4) {
81 d->eeprom_cur_cmd <<= 1;
82 d->eeprom_cur_cmd |= data_in;
83 }
84
85 if (d->eeprom_cur_cmd_bit == d->eeprom_address_width + 3) {
86 int cmd = d->eeprom_cur_cmd >> d->eeprom_address_width;
87 int addr = d->eeprom_cur_cmd & ((1<<d->eeprom_address_width)-1);
88
89 debug("[ rtl8139c eeprom cmd=0x%x addr=0x%02x ]\n", cmd, addr);
90
91 switch (cmd) {
92
93 case RL_9346_READ:
94 d->eeprom_cur_data = d->eeprom_reg[addr % EEPROM_SIZE];
95 break;
96
97 default:fatal("[ rtl8139c eeprom: only the read command has"
98 " been implemented. sorry. ]\n");
99 exit(1);
100 }
101 }
102
103 /* Data output: (Note: Only the READ command has been implemented.) */
104 if (d->eeprom_cur_cmd_bit >= d->eeprom_address_width + 4) {
105 int cur_out_bit = d->eeprom_cur_cmd_bit -
106 (d->eeprom_address_width + 4);
107 int bit = d->eeprom_cur_data & (1 << (15-cur_out_bit));
108
109 if (bit)
110 d->rl_eecmd |= RL_EE_DATAOUT;
111 else
112 d->rl_eecmd &= ~RL_EE_DATAOUT;
113 }
114
115 d->eeprom_cur_cmd_bit ++;
116
117 if (d->eeprom_cur_cmd_bit >= d->eeprom_address_width + 4 + 16) {
118 d->eeprom_cur_cmd = 0;
119 d->eeprom_cur_cmd_bit = 0;
120 }
121 }
122
123
DEVICE_ACCESS(rtl8139c)124 DEVICE_ACCESS(rtl8139c)
125 {
126 struct rtl8139c_data *d = (struct rtl8139c_data *) extra;
127 uint64_t idata = 0, odata = 0;
128
129 if (writeflag == MEM_WRITE)
130 idata = memory_readmax64(cpu, data, len);
131
132 switch (relative_addr) {
133
134 case RL_COMMAND:
135 if (writeflag == MEM_WRITE) {
136 if (idata & RL_CMD_RESET) {
137 /* Reset. TODO */
138
139 /* ... and then clear the reset bit: */
140 idata &= ~RL_CMD_RESET;
141 }
142
143 d->rl_command = idata;
144 } else {
145 odata = d->rl_command;
146 }
147 break;
148
149 case RL_EECMD:
150 if (writeflag == MEM_WRITE) {
151 uint8_t old = d->rl_eecmd;
152 d->rl_eecmd = idata;
153
154 if (!d->eeprom_selected && d->rl_eecmd & RL_EE_SEL) {
155 /* Reset eeprom cmd bit state: */
156 d->eeprom_cur_cmd = 0;
157 d->eeprom_cur_cmd_bit = 0;
158 }
159 d->eeprom_selected = d->rl_eecmd & RL_EE_SEL;
160
161 if (idata & RL_EE_CLK && !(old & RL_EE_CLK))
162 eeprom_clk(d);
163 } else {
164 odata = d->rl_eecmd;
165 }
166 break;
167
168 case 0x82:
169 /* Unknown address, but OpenBSD's re driver writes
170 a 0x01 to this address, in re_reset(). */
171 if (writeflag == MEM_WRITE) {
172 if (idata != 0x01) {
173 fatal("rtl8139c: unimplemented write to"
174 " register 0x82.\n");
175 exit(1);
176 }
177 }
178 break;
179
180 default:if (writeflag == MEM_WRITE) {
181 fatal("[ rtl8139c: unimplemented write to "
182 "offset 0x%x: data=0x%x ]\n", (int)
183 relative_addr, (int)idata);
184 } else {
185 fatal("[ rtl8139c: unimplemented read from "
186 "offset 0x%x ]\n", (int)relative_addr);
187 }
188 exit(1);
189 }
190
191 if (writeflag == MEM_READ)
192 memory_writemax64(cpu, data, len, odata);
193
194 return 1;
195 }
196
197
DEVINIT(rtl8139c)198 DEVINIT(rtl8139c)
199 {
200 char *name2;
201 size_t nlen = 100;
202 struct rtl8139c_data *d;
203
204 CHECK_ALLOCATION(d = (struct rtl8139c_data *) malloc(sizeof(struct rtl8139c_data)));
205 memset(d, 0, sizeof(struct rtl8139c_data));
206
207 INTERRUPT_CONNECT(devinit->interrupt_path, d->irq);
208
209 net_generate_unique_mac(devinit->machine, d->nic.mac_address);
210
211 /* TODO: eeprom address width = 6 on 8129? */
212 d->eeprom_address_width = 8;
213 d->eeprom_reg[0] = 0x8139;
214 d->eeprom_reg[7] = d->nic.mac_address[0] + (d->nic.mac_address[1] << 8);
215 d->eeprom_reg[8] = d->nic.mac_address[2] + (d->nic.mac_address[3] << 8);
216 d->eeprom_reg[9] = d->nic.mac_address[4] + (d->nic.mac_address[5] << 8);
217
218 CHECK_ALLOCATION(name2 = (char *) malloc(nlen));
219 snprintf(name2, nlen, "%s [%02x:%02x:%02x:%02x:%02x:%02x]",
220 devinit->name, d->nic.mac_address[0], d->nic.mac_address[1],
221 d->nic.mac_address[2], d->nic.mac_address[3],
222 d->nic.mac_address[4], d->nic.mac_address[5]);
223
224 memory_device_register(devinit->machine->memory, name2,
225 devinit->addr, DEV_RTL8139C_LENGTH, dev_rtl8139c_access, (void *)d,
226 DM_DEFAULT, NULL);
227
228 net_add_nic(devinit->machine->emul->net, &d->nic);
229
230 return 1;
231 }
232
233