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




Reply via email to