iPXE
3c509.c
Go to the documentation of this file.
1 /*
2  * Split out into 3c509.c and 3c5x9.c, to make it possible to build a
3  * 3c529 module without including ISA, ISAPnP and EISA code.
4  *
5  */
6 
7 FILE_LICENCE ( BSD2 );
8 
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <ipxe/io.h>
14 #include <unistd.h>
15 #include <ipxe/device.h>
16 #include <ipxe/isa.h>
17 #include "3c509.h"
18 
19 /*
20  * 3c509 cards have their own method of contention resolution; this
21  * effectively defines another bus type similar to ISAPnP. Even the
22  * original ISA cards can be programatically mapped to any I/O address
23  * in the range 0x200-0x3e0.
24  *
25  * However, there is a small problem: once you've activated a card,
26  * the only ways to deactivate it will also wipe its tag, meaning that
27  * you won't be able to subsequently reactivate it without going
28  * through the whole ID sequence again. The solution we adopt is to
29  * isolate and tag all cards at the start, and to immediately
30  * re-isolate and re-tag a card after disabling it.
31  *
32  */
33 
34 static void t509bus_remove ( struct root_device *rootdev );
35 
36 static unsigned int t509_id_port = 0;
37 static unsigned int t509_max_tag = 0;
38 
39 /** A 3c509 device */
40 struct t509_device {
41  /** Generic device */
42  struct device dev;
43  /** Tag */
44  unsigned int tag;
45  /** I/O address */
47  /** Driver-private data
48  *
49  * Use t509_set_drvdata() and t509_get_drvdata() to access
50  * this field.
51  */
52  void *priv;
53 };
54 
55 /**
56  * Set 3c509 driver-private data
57  *
58  * @v t509 3c509 device
59  * @v priv Private data
60  */
61 static inline void t509_set_drvdata ( struct t509_device *t509, void *priv ) {
62  t509->priv = priv;
63 }
64 
65 /**
66  * Get 3c509 driver-private data
67  *
68  * @v t509 3c509 device
69  * @ret priv Private data
70  */
71 static inline void * t509_get_drvdata ( struct t509_device *t509 ) {
72  return t509->priv;
73 }
74 
75 /*
76  * t509 utility functions
77  *
78  */
79 
80 static inline void t509_set_id_port ( void ) {
81  outb ( 0x00, t509_id_port );
82 }
83 
84 static inline void t509_wait_for_id_sequence ( void ) {
85  outb ( 0x00, t509_id_port );
86 }
87 
88 static inline void t509_global_reset ( void ) {
89  outb ( 0xc0, t509_id_port );
90 }
91 
92 static inline void t509_reset_tag ( void ) {
93  outb ( 0xd0, t509_id_port );
94 }
95 
96 static inline void t509_set_tag ( uint8_t tag ) {
97  outb ( 0xd0 | tag, t509_id_port );
98 }
99 
100 static inline void t509_select_tag ( uint8_t tag ) {
101  outb ( 0xd8 | tag, t509_id_port );
102 }
103 
104 static inline void t509_activate ( uint16_t ioaddr ) {
105  outb ( 0xe0 | ( ioaddr >> 4 ), t509_id_port );
106 }
107 
110 }
111 
112 static inline void t509_load_eeprom_word ( uint8_t offset ) {
113  outb ( 0x80 | offset, t509_id_port );
114 }
115 
116 /*
117  * Find a suitable ID port
118  *
119  */
120 static inline int t509_find_id_port ( void ) {
121 
125  t509_set_id_port ();
126  /* See if anything's listening */
127  outb ( 0xff, t509_id_port );
128  if ( inb ( t509_id_port ) & 0x01 ) {
129  /* Found a suitable port */
130  DBG ( "T509 using ID port at %04x\n", t509_id_port );
131  return 0;
132  }
133  }
134  /* No id port available */
135  DBG ( "T509 found no available ID port\n" );
136  return -ENOENT;
137 }
138 
139 /*
140  * Send ID sequence to the ID port
141  *
142  */
143 static void t509_send_id_sequence ( void ) {
144  unsigned short lrs_state, i;
145 
146  t509_set_id_port ();
147  /* Reset IDS on cards */
149  lrs_state = 0xff;
150  for ( i = 0; i < 255; i++ ) {
151  outb ( lrs_state, t509_id_port );
152  lrs_state <<= 1;
153  lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
154  }
155 }
156 
157 /*
158  * We get eeprom data from the id_port given an offset into the eeprom.
159  * Basically; after the ID_sequence is sent to all of the cards; they enter
160  * the ID_CMD state where they will accept command requests. 0x80-0xbf loads
161  * the eeprom data. We then read the port 16 times and with every read; the
162  * cards check for contention (ie: if one card writes a 0 bit and another
163  * writes a 1 bit then the host sees a 0. At the end of the cycle; each card
164  * compares the data on the bus; if there is a difference then that card goes
165  * into ID_WAIT state again). In the meantime; one bit of data is returned in
166  * the AX register which is conveniently returned to us by inb(). Hence; we
167  * read 16 times getting one bit of data with each read.
168  */
170  int i, data = 0;
171 
173  /* Do we really need this wait? Won't be noticeable anyway */
174  udelay(10000);
175 
176  for ( i = 0; i < 16; i++ ) {
177  data = ( data << 1 ) | ( inw ( t509_id_port ) & 1 );
178  }
179  return data;
180 }
181 
182 /*
183  * Isolate and tag all t509 cards
184  *
185  */
186 static int t509_isolate ( void ) {
187  unsigned int i;
188  uint16_t contend[3];
189  int rc;
190 
191  /* Find a suitable ID port */
192  if ( ( rc = t509_find_id_port() ) != 0 )
193  return rc;
194 
195  while ( 1 ) {
196 
197  /* All cards are in ID_WAIT state each time we go
198  * through this loop.
199  */
200 
201  /* Send the ID sequence */
203 
204  /* First time through, reset all tags. On subsequent
205  * iterations, kill off any already-tagged cards
206  */
207  if ( t509_max_tag == 0 ) {
208  t509_reset_tag();
209  } else {
210  t509_select_tag ( 0 );
211  }
212 
213  /* Read the manufacturer ID, to see if there are any
214  * more cards
215  */
217  DBG ( "T509 saw %s signs of life\n",
218  t509_max_tag ? "no further" : "no" );
219  break;
220  }
221 
222  /* Perform contention selection on the MAC address */
223  for ( i = 0 ; i < 3 ; i++ ) {
224  contend[i] = t509_id_read_eeprom ( i );
225  }
226 
227  /* Only one device will still be left alive. Tag it. */
228  ++t509_max_tag;
229  DBG ( "T509 found card %04x%04x%04x, assigning tag %02x\n",
230  contend[0], contend[1], contend[2], t509_max_tag );
232 
233  /* Return all cards back to ID_WAIT state */
235  }
236 
237  DBG ( "T509 found %d cards using ID port %04x\n",
239  return 0;
240 }
241 
242 /*
243  * Activate a T509 device
244  *
245  * The device will be enabled at whatever ioaddr is specified in the
246  * struct t509_device; there is no need to stick with the default
247  * ioaddr read from the EEPROM.
248  *
249  */
250 static inline void activate_t509_device ( struct t509_device *t509 ) {
252  t509_select_tag ( t509->tag );
253  t509_activate ( t509->ioaddr );
254  DBG ( "T509 activated device %02x at ioaddr %04x\n",
255  t509->tag, t509->ioaddr );
256 }
257 
258 /*
259  * Deactivate a T509 device
260  *
261  * Disabling also clears the tag, so we immediately isolate and re-tag
262  * this card.
263  *
264  */
265 static inline void deactivate_t509_device ( struct t509_device *t509 ) {
267  udelay ( 1000 );
269  t509_select_tag ( 0 );
270  t509_set_tag ( t509->tag );
272  DBG ( "T509 deactivated device at %04x and re-tagged as %02x\n",
273  t509->ioaddr, t509->tag );
274 }
275 
276 /*
277  * The ISA probe function
278  *
279  */
280 static int legacy_t509_probe ( struct nic *nic, void *hwdev ) {
281  struct t509_device *t509 = hwdev;
282 
283  /* We could change t509->ioaddr if we wanted to */
284  activate_t509_device ( t509 );
285  nic->ioaddr = t509->ioaddr;
286 
287  /* Hand off to generic t5x9 probe routine */
289 }
290 
291 static void legacy_t509_disable ( struct nic *nic, void *hwdev ) {
292  struct t509_device *t509 = hwdev;
293 
294  t5x9_disable ( nic );
295  deactivate_t509_device ( t509 );
296 }
297 
298 static inline void legacy_t509_set_drvdata ( void *hwdev, void *priv ) {
299  t509_set_drvdata ( hwdev, priv );
300 }
301 
302 static inline void * legacy_t509_get_drvdata ( void *hwdev ) {
303  return t509_get_drvdata ( hwdev );
304 }
305 
306 /**
307  * Probe a 3c509 device
308  *
309  * @v t509 3c509 device
310  * @ret rc Return status code
311  *
312  * Searches for a driver for the 3c509 device. If a driver is found,
313  * its probe() routine is called.
314  */
315 static int t509_probe ( struct t509_device *t509 ) {
316  DBG ( "Adding 3c509 device %02x (I/O %04x)\n",
317  t509->tag, t509->ioaddr );
318  return legacy_probe ( t509, legacy_t509_set_drvdata, &t509->dev,
320 }
321 
322 /**
323  * Remove a 3c509 device
324  *
325  * @v t509 3c509 device
326  */
327 static void t509_remove ( struct t509_device *t509 ) {
329  DBG ( "Removed 3c509 device %02x\n", t509->tag );
330 }
331 
332 /**
333  * Probe 3c509 root bus
334  *
335  * @v rootdev 3c509 bus root device
336  *
337  * Scans the 3c509 bus for devices and registers all devices it can
338  * find.
339  */
340 static int t509bus_probe ( struct root_device *rootdev ) {
341  struct t509_device *t509 = NULL;
342  unsigned int tag;
343  unsigned int iobase;
344  int rc;
345 
346  /* Perform isolation and tagging */
347  if ( ( rc = t509_isolate() ) != 0 )
348  return rc;
349 
350  for ( tag = 1 ; tag <= t509_max_tag ; tag++ ) {
351  /* Allocate struct t509_device */
352  if ( ! t509 )
353  t509 = malloc ( sizeof ( *t509 ) );
354  if ( ! t509 ) {
355  rc = -ENOMEM;
356  goto err;
357  }
358  memset ( t509, 0, sizeof ( *t509 ) );
359  t509->tag = tag;
360 
361  /* Send the ID sequence */
363 
364  /* Select the specified tag */
365  t509_select_tag ( t509->tag );
366 
367  /* Read the default I/O address */
369  t509->ioaddr = 0x200 + ( ( iobase & 0x1f ) << 4 );
370 
371  /* Send card back to ID_WAIT */
373 
374  /* Add to device hierarchy */
375  snprintf ( t509->dev.name, sizeof ( t509->dev.name ),
376  "t509%02x", tag );
377  t509->dev.desc.bus_type = BUS_TYPE_ISA;
378  t509->dev.desc.vendor = MFG_ID;
379  t509->dev.desc.device = PROD_ID;
380  t509->dev.parent = &rootdev->dev;
381  list_add ( &t509->dev.siblings, &rootdev->dev.children );
382  INIT_LIST_HEAD ( &t509->dev.children );
383 
384  /* Look for a driver */
385  if ( t509_probe ( t509 ) == 0 ) {
386  /* t509dev registered, we can drop our ref */
387  t509 = NULL;
388  } else {
389  /* Not registered; re-use struct */
390  list_del ( &t509->dev.siblings );
391  }
392  }
393 
394  free ( t509 );
395  return 0;
396 
397  err:
398  free ( t509 );
399  t509bus_remove ( rootdev );
400  return rc;
401 }
402 
403 /**
404  * Remove 3c509 root bus
405  *
406  * @v rootdev 3c509 bus root device
407  */
408 static void t509bus_remove ( struct root_device *rootdev ) {
409  struct t509_device *t509;
410  struct t509_device *tmp;
411 
412  list_for_each_entry_safe ( t509, tmp, &rootdev->dev.children,
413  dev.siblings ) {
414  t509_remove ( t509 );
415  list_del ( &t509->dev.siblings );
416  free ( t509 );
417  }
418 }
419 
420 /** 3c509 bus root device driver */
421 static struct root_driver t509_root_driver = {
422  .probe = t509bus_probe,
423  .remove = t509bus_remove,
424 };
425 
426 /** 3c509 bus root device */
427 struct root_device t509_root_device __root_device = {
428  .dev = { .name = "3c509" },
429  .driver = &t509_root_driver,
430 };
431 
432 ISA_ROM ( "3c509", "3c509" );
static int t509bus_probe(struct root_device *rootdev)
Probe 3c509 root bus.
Definition: 3c509.c:340
iPXE I/O API
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
unsigned short uint16_t
Definition: stdint.h:11
static void activate_t509_device(struct t509_device *t509)
Definition: 3c509.c:250
uint16_t inw(volatile uint16_t *io_addr)
Read 16-bit word from I/O-mapped device.
uint16_t ioaddr
I/O address.
Definition: 3c509.c:46
#define list_add(new, head)
Add a new entry to the head of a list.
Definition: list.h:69
static void t509_reset_tag(void)
Definition: 3c509.c:92
Error codes.
static int t509_find_id_port(void)
Definition: 3c509.c:120
void t5x9_disable(struct nic *nic)
Definition: 3c5x9.c:40
static void t509_deactivate_and_reset_tag(uint16_t ioaddr)
Definition: 3c509.c:108
#define EP_ID_PORT_END
Definition: 3c509.h:56
struct root_device t509_root_device __root_device
3c509 bus root device
Definition: 3c509.c:427
char name[40]
Name.
Definition: device.h:75
#define ENOENT
No such file or directory.
Definition: errno.h:514
A root device.
Definition: device.h:94
unsigned int tag
Tag.
Definition: 3c509.c:44
struct device dev
Device chain.
Definition: device.h:99
unsigned int vendor
Vendor ID.
Definition: device.h:31
struct device * parent
Bus device.
Definition: device.h:85
#define EEPROM_MFG_ID
Definition: 3c509.h:91
static void * t509_get_drvdata(struct t509_device *t509)
Get 3c509 driver-private data.
Definition: 3c509.c:71
static void t509_remove(struct t509_device *t509)
Remove a 3c509 device.
Definition: 3c509.c:327
#define PROD_ID
Definition: 3c509.h:375
#define list_del(list)
Delete an entry from a list.
Definition: list.h:119
#define ENOMEM
Not enough space.
Definition: errno.h:534
A hardware device.
Definition: device.h:73
static void deactivate_t509_device(struct t509_device *t509)
Definition: 3c509.c:265
#define BUS_TYPE_ISA
ISA bus type.
Definition: device.h:55
static unsigned long ioaddr
Definition: davicom.c:129
unsigned int ioaddr
Definition: nic.h:55
static struct root_driver t509_root_driver
3c509 bus root device driver
Definition: 3c509.c:421
static void t509bus_remove(struct root_device *rootdev)
Remove 3c509 root bus.
Definition: 3c509.c:408
void udelay(unsigned long usecs)
Delay for a fixed number of microseconds.
Definition: timer.c:60
static userptr_t size_t offset
Offset of the first segment within the content.
Definition: deflate.h:259
static void * legacy_t509_get_drvdata(void *hwdev)
Definition: 3c509.c:302
static uint16_t t509_id_read_eeprom(int offset)
Definition: 3c509.c:169
static void legacy_t509_set_drvdata(void *hwdev, void *priv)
Definition: 3c509.c:298
#define list_for_each_entry_safe(pos, tmp, head, member)
Iterate over entries in a list, safe against deletion of the current entry.
Definition: list.h:447
static int legacy_t509_probe(struct nic *nic, void *hwdev)
Definition: 3c509.c:280
static unsigned int t509_max_tag
Definition: 3c509.c:37
static void t509_load_eeprom_word(uint8_t offset)
Definition: 3c509.c:112
static void(* free)(struct refcnt *refcnt))
Definition: refcnt.h:54
static void t509_select_tag(uint8_t tag)
Definition: 3c509.c:100
struct list_head siblings
Devices on the same bus.
Definition: device.h:81
static void t509_set_drvdata(struct t509_device *t509, void *priv)
Set 3c509 driver-private data.
Definition: 3c509.c:61
uint8_t * tmp
Definition: entropy.h:156
A root device driver.
Definition: device.h:107
unsigned char uint8_t
Definition: stdint.h:10
uint8_t inb(volatile uint8_t *io_addr)
Read byte from I/O-mapped device.
#define EEPROM_ADDR_CFG
Definition: 3c509.h:92
Definition: nic.h:49
int(* probe)(struct root_device *rootdev)
Add root device.
Definition: device.h:116
void * malloc(size_t size)
Allocate memory.
Definition: malloc.c:583
#define GLOBAL_RESET
Definition: 3c509.h:212
static void t509_set_id_port(void)
Definition: 3c509.c:80
void legacy_remove(void *hwdev, void *(*get_drvdata)(void *hwdev), void(*disable)(struct nic *nic, void *hwdev))
Definition: legacy.c:138
#define MFG_ID
Definition: 3c509.h:374
#define INIT_LIST_HEAD(list)
Initialise a list head.
Definition: list.h:45
#define ISA_PROD_ID(product)
Definition: isa_ids.h:45
static int t509_probe(struct t509_device *t509)
Probe a 3c509 device.
Definition: 3c509.c:315
#define outb(data, io_addr)
Definition: io.h:309
static void t509_activate(uint16_t ioaddr)
Definition: 3c509.c:104
unsigned int bus_type
Bus type.
Definition: device.h:24
static void t509_set_tag(uint8_t tag)
Definition: 3c509.c:96
static struct tlan_private * priv
Definition: tlan.c:224
unsigned int device
Device ID.
Definition: device.h:33
#define EP_ID_PORT_START
Definition: 3c509.h:54
struct list_head children
Devices attached to this device.
Definition: device.h:83
struct device_description desc
Device description.
Definition: device.h:79
Device model.
int snprintf(char *buf, size_t size, const char *fmt,...)
Write a formatted string to a buffer.
Definition: vsprintf.c:382
static int t509_isolate(void)
Definition: 3c509.c:186
void * priv
Driver-private data.
Definition: 3c509.c:52
static void t509_send_id_sequence(void)
Definition: 3c509.c:143
static void t509_global_reset(void)
Definition: 3c509.c:88
int t5x9_probe(struct nic *nic, uint16_t prod_id_check, uint16_t prod_id_mask)
Definition: 3c5x9.c:342
#define EP_ID_PORT_INC
Definition: 3c509.h:55
A 3c509 device.
Definition: 3c509.c:40
struct arbelprm_port_state_change_st data
Message.
Definition: arbel.h:12
static void t509_wait_for_id_sequence(void)
Definition: 3c509.c:84
#define DBG(...)
Print a debugging message.
Definition: compiler.h:498
static void legacy_t509_disable(struct nic *nic, void *hwdev)
Definition: 3c509.c:291
static unsigned int t509_id_port
Definition: 3c509.c:36
#define EP_COMMAND
Definition: 3c509.h:109
uint64_t tag
Identity tag.
Definition: edd.h:30
ISA_ROM("3c509", "3c509")
#define NULL
NULL pointer (VOID *)
Definition: Base.h:362
String functions.
struct device dev
Generic device.
Definition: 3c509.c:42
FILE_LICENCE(BSD2)
void * memset(void *dest, int character, size_t len) __nonnull
int legacy_probe(void *hwdev, void(*set_drvdata)(void *hwdev, void *priv), struct device *dev, int(*probe)(struct nic *nic, void *hwdev), void(*disable)(struct nic *nic, void *hwdev))
Definition: legacy.c:82
#define ISA_PROD_ID_MASK
Definition: isa_ids.h:44