Merge remote-tracking branch 'qemu/master'
[qemu/ar7.git] / hw / phy.c
blobe8f34ae43ac606923a1fc869c282e54dafea0f48
1 /*
2 * QEMU Ethernet Physical Layer (PHY) support
4 * Copyright (c) 2007 Stefan Weil
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 /* This code emulates a National Semiconductor DP83840A PHY. */
23 #define DEBUG_PHY
25 typedef enum {
26 PHY_BMCR = 0x00, /* Basic Mode Control Register */
27 PHY_BMSR = 0x01, /* Basic Mode Status */
28 PHY_PHYIDR1 = 0x02, /* PHY Identifier 1 */
29 PHY_PHYIDR2 = 0x03, /* PHY Identifier 2 */
30 PHY_ANAR = 0x04, /* Auto-Negotiation Advertisement */
31 PHY_ANLPAR = 0x05, /* Auto-Negotiation Link Partner Ability */
32 PHY_ANER = 0x06, /* Auto-Negotiation Expansion */
33 PHY_DCR = 0x12, /* Disconnect Counter */
34 PHY_FCSCR = 0x13, /* False Carrier Sense Counter */
35 PHY_RECR = 0x15, /* Receive Error Counter */
36 PHY_SRR = 0x16, /* Silicon Revision */
37 PHY_PCR = 0x17, /* PCS Sublayer Configuration */
38 PHY_LBREMR = 0x18, /* Loopback, Bypass and Receiver Error Mask */
39 PHY_PAR = 0x19, /* PHY Address */
40 PHY_10BTSR = 0x1b, /* 10Base-T Status */
41 PHY_10BTCR = 0x1c, /* 10Base-T Configuration */
42 } phy_register_t;
44 typedef enum {
45 PHY_RESET = BIT(15),
46 PHY_LOOP = BIT(14),
47 PHY_100 = BIT(13),
48 AUTO_NEGOTIATE_EN = BIT(12),
49 PHY_PDOWN = BIT(11),
50 PHY_ISOLATE = BIT(10),
51 RENEGOTIATE = BIT(9),
52 PHY_FD = BIT(8),
53 PHY_COLLISION_TEST = BIT(7),
54 } phy_bmcr_bits;
56 typedef enum {
57 PHY_100BASE_T4 = BIT(15),
58 PHY_100BASE_TX_FD = BIT(14),
59 PHY_100BASE_TX_HD = BIT(13),
60 PHY_10BASE_T_FD = BIT(12),
61 PHY_10BASE_T_HD = BIT(11),
62 NWAY_COMPLETE = BIT(5),
63 NWAY_CAPABLE = BIT(3),
64 PHY_LINKED = BIT(2),
65 PHY_EXTENDED_CAPABILITY = BIT(0),
66 } phy_bmsr_bits;
68 typedef enum {
69 PHY_Identifier_1 = 2,
70 } phy_phyidr1_bits;
72 typedef enum {
73 PHY_Identifier_2 = 3,
74 } phy_phyidr2_bits;
76 typedef enum {
77 NWAY_FD100 = BIT(8),
78 NWAY_HD100 = BIT(7),
79 NWAY_FD10 = BIT(6),
80 NWAY_HD10 = BIT(5),
81 NWAY_SEL = BITS(4, 0),
82 NWAY_AUTO = BIT(0),
83 } phy_anar_bits;
85 #define PHY_AUTO_NEG_EXPANSION 6
86 #define PHY_GENERIC_CONFIG_REG 0x10
87 #define PHY_IFSEL (3<<14)
88 #define PHY_LBKMD (3<<12)
89 /*--- #define PHY_ (3<<10) ---*/
90 #define PHY_FLTLED (1<<9)
91 #define PHY_CONV (1<<8)
92 /*--- #define PHY_ (1<<5) ---*/
93 #define PHY_XOVEN (1<<4)
94 /*--- #define PHY_ (3<<2) ---*/
95 #define PHY_ENREG8 (1<<1)
96 #define PHY_DISPMG (1<<0)
97 #define PHY_GENERIC_STATUS_REG 0x16
98 #define PHY_STATUS_MD (1<<10)
99 #define PHY_SPECIFIC_STATUS_REG 0x17
100 #define PHY_STATUS_LINK (1<<4)
101 #define PHY_INTERRUPT_STATUS 0x19
102 #define PHY_INT_XOVCHG (1<<9)
103 #define PHY_INT_SPDCHG (1<<8)
104 #define PHY_INT_DUPCHG (1<<7)
105 #define PHY_INT_PGRCHG (1<<6)
106 #define PHY_INT_LNKCHG (1<<5)
107 #define PHY_INT_SYMERR (1<<4)
108 #define PHY_INT_FCAR (1<<3)
109 #define PHY_INT_TJABINT (1<<2)
110 #define PHY_INT_RJABINT (1<<1)
111 #define PHY_INT_ESDERR (1<<0)
112 #define PHY_RXERR_COUNT 0x1D
114 typedef struct {
115 /* Hardware registers for physical layer emulation. */
116 uint16_t reg[32];
117 int enabled;
118 } phy_t;
120 static phy_t phy;
122 #if defined(DEBUG_PHY)
124 static int trace_phy;
126 #define PHY trace_phy
128 #undef SET_TRACEFLAG
129 #define SET_TRACEFLAG(name) \
130 do { \
131 char *substring = strstr(env, #name); \
132 if (substring) { \
133 name = ((substring > env && substring[-1] == '-') ? 0 : 1); \
135 TRACE_PHY(logout("Logging enabled for " #name "\n")); \
136 } while(0)
138 #define TRACE_PHY(statement) ((PHY) ? (statement) : (void)0)
140 static void set_phy_traceflags(const char *envname)
142 const char *env = getenv(envname);
143 if (env != 0) {
144 unsigned long ul = strtoul(env, 0, 0);
145 if ((ul == 0) && strstr(env, "ALL")) ul = 0xffffffff;
146 PHY = ul;
147 SET_TRACEFLAG(PHY);
151 #else
153 #define TRACE_PHY(statement) ((void)0)
155 #endif /* DEBUG_PHY */
157 static void phy_reset(void)
159 //~ phy register 1:
160 //~ 0xA8611E80 09 78 3F 20
161 //~ 0xA8611E80 2D 78 3F 20
163 //~ phy register 5:
164 //~ 0xA8611E80 01 00 BF 20
165 //~ 0xA8611E80 E1 85 BF 20
166 const int linked = 1;
167 TRACE_PHY(logout("\n"));
168 phy.reg[PHY_BMCR] = PHY_100 | AUTO_NEGOTIATE_EN | PHY_FD;
169 //~ phy.reg[PHY_BMCR] |= PHY_ISOLATE;
170 phy.reg[PHY_BMSR] = PHY_100BASE_TX_FD | PHY_100BASE_TX_HD;
171 phy.reg[PHY_BMSR] |= PHY_10BASE_T_FD | PHY_10BASE_T_HD;
172 //~ phy.reg[PHY_BMSR] |= BIT(6);
173 phy.reg[PHY_BMSR] |= NWAY_CAPABLE;
174 phy.reg[PHY_BMSR] |= PHY_EXTENDED_CAPABILITY;
175 if (linked) {
176 phy.reg[PHY_BMSR] |= NWAY_COMPLETE + PHY_LINKED;
178 phy.reg[PHY_PHYIDR1] = 0x0000;
179 //~ phy.reg[PHY_PHYIDR1] = OUI Bits 3...18;
180 phy.reg[PHY_PHYIDR2] = 0x0000;
181 //~ phy.reg[PHY_PHYIDR2] = OUI Bits 19...24 + vendor + revision;
182 phy.reg[PHY_ANAR] = NWAY_FD100 + NWAY_HD100 + NWAY_FD10 + NWAY_HD10 + NWAY_AUTO;
183 phy.reg[PHY_ANLPAR] = NWAY_AUTO;
184 if (linked) {
185 phy.reg[PHY_ANLPAR] |= 0x8400 + (phy.reg[4] & BITS(8, 5));
187 //~ phy.reg[PHY_ANLPAR] = 0;
190 static void phy_autoneg(void)
192 TRACE_PHY(logout("\n"));
193 phy_reset();
194 phy.reg[PHY_BMSR] |= NWAY_COMPLETE + PHY_LINKED;
197 static void phy_enable(void)
199 static int first = 1;
200 TRACE_PHY(logout("\n"));
201 if (first) {
202 phy_reset();
203 first = 0;
205 phy.enabled = 1;
208 static void phy_disable(void)
210 TRACE_PHY(logout("\n"));
211 phy.enabled = 0;
214 static uint16_t phy_read(unsigned addr)
216 uint16_t val = phy.reg[addr];
217 TRACE_PHY(logout("\n"));
218 if (!phy.enabled) return 0;
219 #if 0
220 if (addr == PHY_BMCR) {
221 if (val & PHY_RESET) {
222 phy.reg[addr] =
223 ((val & ~PHY_RESET) | AUTO_NEGOTIATE_EN);
224 } else if (val & RENEGOTIATE) {
225 val &= ~RENEGOTIATE;
226 phy.reg[addr] = val;
227 //~ 0x0000782d 0x00007809
228 phy.reg[1] = 0x782d;
229 phy.reg[5] = phy.reg[4] | PHY_ISOLATE | PHY_RESET;
230 reg_write(av.mdio, MDIO_LINK, 0x80000000);
232 } else if (addr == PHY_BMSR) {
233 val |= PHY_LINKED | NWAY_CAPABLE | NWAY_COMPLETE;
235 #endif
236 return val;
239 static void phy_write(unsigned addr, uint16_t val)
241 TRACE_PHY(logout("\n"));
242 if (!phy.enabled) return;
244 if (addr == PHY_BMCR) {
245 if (val & PHY_RESET) {
246 val &= ~PHY_RESET;
247 phy_reset();
249 if (val & PHY_LOOP) {
250 MISSING();
252 if (val & RENEGOTIATE) {
253 val &= ~RENEGOTIATE;
254 if (phy.reg[PHY_BMCR] & AUTO_NEGOTIATE_EN) {
255 phy_autoneg();
258 if (val & PHY_COLLISION_TEST) {
259 MISSING();
261 } else if (addr == PHY_BMSR || addr == PHY_PHYIDR1 || addr == PHY_PHYIDR2) {
262 UNEXPECTED();
263 val = phy.reg[addr];
264 } else if (addr == PHY_ANAR) {
266 } else {
268 //~ 1000 7809 0000 0000 01e1 0001
269 //~ mdio_useraccess_data[0][PHY_BMCR] = 0x1000;
270 //~ mdio_useraccess_data[0][PHY_BMSR] = 0x782d;
271 //~ mdio_useraccess_data[0][NWAY_ADVERTIZE_REG] = 0x01e1;
272 /* 100FD=Yes, 100HD=Yes, 10FD=Yes, 10HD=Yes */
273 //~ mdio_useraccess_data[0][NWAY_REMADVERTISE_REG] = 0x85e1;
274 //~ }
275 phy.reg[addr] = val;
278 static void phy_init(void)
280 #if defined(DEBUG_PHY)
281 set_phy_traceflags("DEBUG_AR7");
282 #endif
283 TRACE_PHY(logout("\n"));
286 #if 0
287 /* phy code from eepro100 */
288 uint8_t raiseint = (val & 0x20000000) >> 29;
289 uint8_t opcode = (val & 0x0c000000) >> 26;
290 uint8_t phy = (val & 0x03e00000) >> 21;
291 uint8_t reg = (val & 0x001f0000) >> 16;
292 uint16_t data = (val & 0x0000ffff);
293 if (phy != 1) {
294 /* Unsupported PHY address. */
295 //~ logout("phy must be 1 but is %u\n", phy);
296 data = 0;
297 } else if (opcode != 1 && opcode != 2) {
298 /* Unsupported opcode. */
299 logout("opcode must be 1 or 2 but is %u\n", opcode);
300 data = 0;
301 } else if (reg > 6) {
302 /* Unsupported register. */
303 logout("register must be 0...6 but is %u\n", reg);
304 data = 0;
305 } else {
306 TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
307 val, raiseint, mdi_op_name[opcode], phy,
308 mdi_reg_name[reg], data));
309 if (opcode == 1) {
310 /* MDI write */
311 switch (reg) {
312 case 0: /* Control Register */
313 if (data & 0x8000) {
314 /* Reset status and control registers to default. */
315 s->mdimem[0] = eepro100_mdi_default[0];
316 s->mdimem[1] = eepro100_mdi_default[1];
317 data = s->mdimem[reg];
318 } else {
319 /* Restart Auto Configuration = Normal Operation */
320 data &= ~0x0200;
322 break;
323 case 1: /* Status Register */
324 missing("not writable");
325 data = s->mdimem[reg];
326 break;
327 case 2: /* PHY Identification Register (Word 1) */
328 case 3: /* PHY Identification Register (Word 2) */
329 missing("not implemented");
330 break;
331 case 4: /* Auto-Negotiation Advertisement Register */
332 case 5: /* Auto-Negotiation Link Partner Ability Register */
333 break;
334 case 6: /* Auto-Negotiation Expansion Register */
335 default:
336 missing("not implemented");
338 s->mdimem[reg] = data;
339 } else if (opcode == 2) {
340 /* MDI read */
341 switch (reg) {
342 case 0: /* Control Register */
343 if (data & 0x8000) {
344 /* Reset status and control registers to default. */
345 s->mdimem[0] = eepro100_mdi_default[0];
346 s->mdimem[1] = eepro100_mdi_default[1];
348 break;
349 case 1: /* Status Register */
350 s->mdimem[reg] |= 0x0020;
351 break;
352 case 2: /* PHY Identification Register (Word 1) */
353 case 3: /* PHY Identification Register (Word 2) */
354 case 4: /* Auto-Negotiation Advertisement Register */
355 break;
356 case 5: /* Auto-Negotiation Link Partner Ability Register */
357 s->mdimem[reg] = 0x41fe;
358 break;
359 case 6: /* Auto-Negotiation Expansion Register */
360 s->mdimem[reg] = 0x0001;
361 break;
363 data = s->mdimem[reg];
365 /* Emulation takes no time to finish MDI transaction.
366 * Set MDI bit in SCB status register. */
367 s->mem[SCBAck] |= 0x08;
368 val |= BIT(28);
369 if (raiseint) {
370 eepro100_mdi_interrupt(s);
373 val = (val & 0xffff0000) + data;
374 memcpy(&s->mem[0x10], &val, sizeof(val));
375 #endif