Next | Prev | Up | Top | Contents | Index

Writing edtinit()

If you use the VECTOR directive to configure a driver into the kernel, your driver can use a routine of the form drvedtinit(), where drv is the driver prefix. If your device driver object module includes a drvedtinit() routine, the system executes the drvedtinit() routine when the system boots. In general, you can use your drvedtinit() routine to perform any device driver initialization you want.

Synopsis

drvedtinit(e)
struct edt *e
{
   your code here
}

edt Type Structure

When the system calls your drvedtinit() routine, it hands the routine a pointer to a structure of type edt. (This structure type is defined in the sys/edt.h header file.)

Structure Definition

typedef struct iospace {
    unchar    ios_type;      /* io space type in adapter */
    iopaddr_t ios_iopaddr;   /* io base address */
    ulong     ios_size;
    caddr_t   ios_vaddr;     /* kernel virtual address */
} iospace_t;

#define NBASE 3
typedef struct edt {
   uint_t    e_bus_type;     /* vme, scsi, eisa, ... */
   unchar    v_cpuintr;      /* cpu to send intr to */
   unchar    v_setcpuintr;   /* cpu field is valid */
   uint_t    e_adap;         /* adapter */
   uint_t    e_ctlr;         /* controller identifier */
   void*     e_bus_info;     /* bus-dependent info */
   int       (*e_init)( );   /* device init/run-time probe */
   iospace_t e_iospace[NBASE];
};
Based on e_bus_type, lboot will set up e_bus_info to point to the corresponding bus-dependent data structure (that is, vme_intrs). With this two-layer structure, it is easier to extend edt to support EISA, GIO, or other types of buses.

CHALLENGE/Onyx and POWER CHALLENGE/POWER Onyx systems can support more I/O address space than the kernel can, so it is necessary to provide a way of allocating only the part of the I/O space that is needed into the kernel address space. Programming the I/O board/adapter registers dynamically assigns the mapping. The edt structure must describe the device by adapter ID and adapter bus address. The kernel uses this information to initialize the kernel virtual address.

On POWER Series workstations, ranges of VME-bus address space are mapped one-to-one with K2 segment addresses. This makes accessing the VME bus easy, but is also limiting. Only a small amount of K2 space is available for use by VME, so very little of the VME address space is made available. Even worse, for dual VME-bus systems, the space previously available is now halved because the two buses must share it.

The CHALLENGE series supports up to five VME buses. Since K2 space is a limited resource, and dividing up what is available by five makes the extra VME buses next to useless, a new approach was tried. The CHALLENGE series does not have a direct K2 address map into VME-bus space. Each VME-bus adapter has the ability to map fifteen 8 MB windows of VME-bus space into K2 space. These windows can be moved around at will to give the illusion of a much larger address space.

To access a VME space, a user must allocate a PIO map, which provides a translation between a kernel address and VME space. These mappings can be "FIXED" or "UNFIXED." As on POWER Series platforms, a FIXED mapping is a one-to-one mapping of a range of VME-bus space into the driver's address space. An UNFIXED window takes advantage of the sliding window ability on the CHALLENGE series, which supports both FIXED and UNFIXED mappings.

In an UNFIXED map, VME-bus space cannot be accessed directly; instead, access is provided through special bcopy() routines used to move data between VME space and kernel buffers. While it is not always possible to get a FIXED mapping, an UNFIXED mapping is always available. The special bcopy() routines work for both FIXED and UNFIXED mappings. On POWER Series and earlier workstations, UNFIXED mappings are treated as FIXED mappings.

The PIO mapping routines also have a general interface that allows them to be used for mapping in bus spaces other than VME.

The support routines for PIO mapping are:

pio_mapallocAllocate a PIO map.
pio_mapaddrMap bus space to a driver accessible address (FIXED maps only).
pio_mapfreeFree a previously allocated PIO map.
pio_badaddrCheck to see whether a bus address is equipped.
pio_wbadaddrCheck to see whether a bus address is equipped.
pio_bcopyinCopy data from bus space to kernel buffer.
pio_bcopyoutCopy data from kernel buffer to bus space.

These PIO maps are normally set up in the driver's drvedtinit() routine.

e_iospace is enhanced to be a structure of I/O base address, size and type of the I/O mapping, and kernel virtual address space. ios_type, ios_iopaddr, and ios_size are initialized by lboot from the system, and ios is assigned when a driver is initialized.

e_adap is added to specify the adapter number, while e_ctlr is for physical controller ID.

To pass the desired interrupt CPU to the driver via the irix.sm file, use the VECTOR directive. The line

VECTOR: module=XXX intrcpu=3

directs lboot (via autoconfig) to set the v_intrcpu field for the module's edt struct to 3 and the v_setintrcpu field to 1, indicating that v_intrcpu is valid. If no intrcpu= statement appears in the VECTOR line, v_setintrcpu is set to 0. The module's edtinit function may then use these fields to route interrupts as desired.

void
XXXedtinit (struct edt *ep)
{
     if (ep->setcpuintr)
               dest_cpu = ep->cpuintr;
     else
          dest_cpu = <some default>;

     ...machine-specific intr routing ...
}

vme_intrs Structure

In the case of a VME driver, the field e_bus_info will point to the vme_intrs structure.

Structure Definition

typedef struct vme_intrs {
    int     (*v_vintr)();    /* interrupt routine */
    unsigned char  v_vec;    /* vme vector */
    unsigned char  v_brl;    /* interrupt priority level */
    unsigned char  v_unit;   /* software identifier */
} vme_intrs_t;
The only field that must be accessed is v_brl, which contains the ipl=value from the VECTOR line. The v_vec field must be used only if the VECTOR line uses the vector= directive and your device requires a jumpered or hard-wired VME interrupt vector.

Note: Although lboot knows not to include a VME device driver in the kernel for a device not present, it is a good idea for your drvedtinit() routine to probe for its device with badaddr(). This lets you write a driver that is prepared if the device is removed from the system after the kernel has been built or when the kernel runs on another system. Continuing with this mythical VME device driver example, its drvedtinit() routine could look like:

struct drvctlrinfo ctlrinfo[MAXCTLR];
drvedtinit(edt_t *e)
{
   int i, vec;
   struct vme_intrs   *info;
   volatile struct drvdevice *dp;
   struct drviopb      iopb;
   piomap_t *pmap;

   pmap = pio_mapalloc(e->e_bus_type,e->e_adap,&e->e_space[0],
      PIOMAP_FIXED,"DRV");

   /* make sure adapter exists and addresses are valid */
   if( pmap == 0 )
      return;
   dp = pio_mapaddr(pmap,e->e_iobase);
   /* probe for the device */
   if( badaddr(&dp->csr,sizeof(dp->csr)) ) {
      cmn_err(CE_WARN,"drv: ctlr %d not installed\n",e->e_ctlr);
      pio_mapfree(pmap);
      return;
   }

   /* save the controller's device registers pointer */
   ctlrinfo[e->e_ctlr]->devregs = dp;

   /* dynamically allocate an interrupt vector */
   vec = vme_ivec_alloc(e->e_adap);
   if( vec == -1 ) {
      cmn_err(CE_WARN,"drv: ctlr %d, no irq vector\n", e->e_ctlr);
      pio_mapfree(pmap);
      return;
   }

   /* register our interrupt routine with the kernel */
   vme_ivec_set(e->e_adap,vec,drvintr,e->e_ctlr);
   iopb.ipl = info->v_brl;
   iopb.vec = vec;
   .
   .
   .
}
Two new routines vme_ivec_alloc(uint_t, adapter) and
vme_ivec_set(adapter, vec, intr_func, arg) are implemented to dynamically allocate an interrupt vector and register this vector into vme_ivec(). This scheme supports multiple vectors and loadable drivers. vme_ivec_alloc() and vme_ivec_set() are used in an edtinit routine; vme_ivec_free() can be called to free up a vector that has been allocated.

You can specify the vector in the irix.sm file for old VME boards with a hard-wired interrupt vector. 0x30-0x3f and 0x70-0x7f are reserved for customer boards.


Next | Prev | Up | Top | Contents | Index