Here's some info on the new interface. goals: 1) allow multiple superio's per mainboard 2) allow different superios to take on different functions e.g. one takes on IDE, the other keyboard, and so on 3) allow superio config BEFORE pci config to support, e.g., ALI M1535 chips which require this 4) allow superio config AFTER pci config, and at the end of hardwaremain 5) allow parameters to control superio config (via config file) 6) avoid function name space silliness, e.g. smc_enter_pnp, sis_950_enter_pnp, etc. 7) make the common case (one superio) not too hard So here goes. I grabbed a lot of this basic structure from Plan 9, which has similar needs ... Interface to each superio is via a control struct. For each type of superio, we define the following struct superio_control: /* linkages from devices of a type (e.g. superio devices) * to the actual physical PCI device. This type is used in an array of * structs built by NLBConfig.py. We owe this idea to Plan 9. */ struct superio; struct superio_control { void (*pre_pci_init)(struct superio *s); void (*init)(struct superio *s); void (*finishup)(struct superio *s); // this is the vendor and device id unsigned int vendor, device; }; This control structure is for ALL instances of a given superio chip. The pre_pci_init, init, and finishup are called as defined above. The vendor and device are for the superio, so we can find an instance of the superio as needed. Function entries can be left empty if that function is not needed, e.g. struct superio_control superio_sis_950_control = { 0, 0, finishup, 0x1039, 0x8 }; So this chip only has an init function called at the end of hardwaremain.c. Specification of the USE of the superio is done in the config file. The config tool generates initialized C structures to be passed to the superio functions. One of the structures is generated for EACH INSTANCE of a superio. The structure has info about the superio control structure, an optional devfn value, a pci_device pointer to be filled in by linuxbios, and variables that enable different superio functions. Here is that structure: struct superio { struct pci_dev *device; // the device. struct superio_control *super; // the ops for the device. unsigned int devfn; /* the devfn. * if devfn is known, the device can be * configured for PCI discovery. * this is needed for some devices such as acer m1535 */ // com ports. This is not done as an array (yet). // We think it's easier to set up from python if it is not an array. struct com_ports com1, com2, com3, com4; /* flags for each device type. Unsigned int. */ // low order bit ALWAYS means enable. Next bit means to enable // DMA, if it exists. unsigned int ide, floppy, lpt; }; The config tool will create a file with declarations for these structures for each instance of a superio chip. NOTE: here is the com port: struct com_ports { unsigned int enable,baud, base, irq; }; Config file: The config file drives all this. commands in the config file cause code to be created for initialized arrays, add rules to the makefile, and in general set up functions to be called as needed. Here's an example of a chip that should support keyboard, floppy, lpt, and com1 nsuperio sis/950 com1={1} floppy=1 lpt=1 This line will result in the following C code: #include <pci.h> extern struct superio_control superio_sis_950_control; struct superio superio_sis_950= { 0, &superio_sis_950_control, .com1={1}, .floppy=1, .lpt=1}; struct superio *all_superio[] = {&superio_sis_950, }; Note that the 'name=val' parts of the nsuperio command get converted to C code. The first extern declaration is merely our linkage into the superio.c code for the sis 950. The second declaration defines one instance of a 950 chip, and initializes the pointer to the control struct and the non-zero fields for functions that should be enabled in that chip. The third declaration contains pointers to all the superio structs previously declared. Code in hardwaremain.c will initialize the pci_device * pointer for each superio struct at startup time. The three functions (pre_pci_init, init, and finishup) are called with pointers to the superio structure. This allows the functions to see what they are supposed to do to the chip. For Tyson, we might change the nsuperio line above as follows: nsuperio SMC/fdc37n769 com1={1,0,0x3f8,4} com2={1,0,0x3f8,4} com3={etc.} For our m1535 chip we might have: nsuperio acer/m1535 devfn=0x38 ide=3 etc. etc. In hardwaremain.c, the changes are minimal. In three places (before pci, after pci, and at the end) we call the function handle_superio(int pass). Pass is pass number, and has a value of 0, 1, or 2. 0 is pre-pci, 1 is post-pci, and 2 is at the end of hardware main. handle_superio, for pass 0, will walk the all_superio array, see for each superio if devfn is defined, and will call superio->control->pre_pci_init for that part. For the m1535, for example, the pre_pci_init function would check to see if ide is desired, and if so, set the bits in the chip that make it respond to PCI config cycles. For passes 1 and 2, handle_superio will find the device with the given vendor/devid, and call the appropriate function if it is non-zero. OK, comments: Have I gone off the deep end? I know this will work and do what we need. It compiles and I have a feeling there are no real problems with it working correctly. Impact on the source tree is minimal -- in fact, it reduces the size of hardwaremain.c considerably, and also looks cleaner -- fewer #ifdefs everywhere. I think it meets all the goals I outlined above. But -- is it overkill? I don't think I met all of Tyson's needs, esp. for port mapping -- this can be fixed. Any comments welcome. I'm not going to commit until I get a sense that this is the right thing to do. ron