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