WDC(9)

WDC(9) | Kernel Developer's Manual | WDC(9)

# NAME

`wdc` — machine-independent IDE/ATAPI driver

# SYNOPSIS

`#include <dev/ata/atavar.h>`
`#include <sys/dev/ic/wdcvar.h>`

int
`wdcprobe`(struct channel_softc * chp);

void
`wdcattach`(struct channel_softc * chp);

# DESCRIPTION

The `wdc` driver provides the machine independent core functions for driving IDE devices. IDE devices-specific drivers (wd(4) or atapibus(4)) will use services provided by `wdc`.

The machine-dependent bus front-end provides information to `wdc` with the wdc_softc and channel_softc structures. The first one defines global controller properties, and the second contains per-channel information. `wdc` returns information about the attached devices in the ata_drive_datas structure.

    struct wdc_softc { /* Per controller state */
            struct device sc_dev;
            int           cap;
    #define WDC_CAPABILITY_DATA16 0x0001
    #define WDC_CAPABILITY_DATA32 0x0002
    #define WDC_CAPABILITY_MODE   0x0004
    #define WDC_CAPABILITY_DMA    0x0008
    #define WDC_CAPABILITY_UDMA   0x0010
    #define WDC_CAPABILITY_HWLOCK 0x0020
    #define WDC_CAPABILITY_ATA_NOSTREAM 0x0040
    #define WDC_CAPABILITY_ATAPI_NOSTREAM 0x0080
    #define WDC_CAPABILITY_NO_EXTRA_RESETS 0x0100
    #define WDC_CAPABILITY_PREATA 0x0200
    #define WDC_CAPABILITY_IRQACK 0x0400
    #define WDC_CAPABILITY_SINGLE_DRIVE 0x0800
    #define WDC_CAPABILITY_NOIRQ  0x1000
    #define WDC_CAPABILITY_SELECT  0x2000
            uint8_t      pio_mode;
            uint8_t      dma_mode;
            int nchannels;
            struct channel_softc *channels;

            void            *dma_arg;
            int            (*dma_init)(void *, int, int, void *, size_t, int);
            void           (*dma_start)(void *, int, int, int);
            int            (*dma_finish)(void *, int, int, int);
    #define WDC_DMA_READ 0x01
    #define WDC_DMA_POLL 0x02

            int            (*claim_hw)(void *, int);
            void            (*free_hw)(void *);
    };

    struct channel_softc { /* Per channel data */
            int channel;
            struct wdc_softc *wdc;
            bus_space_tag_t       cmd_iot;
            bus_space_handle_t    cmd_ioh;
            bus_space_tag_t       ctl_iot;
            bus_space_handle_t    ctl_ioh;
            bus_space_tag_t       data32iot;
            bus_space_handle_t    data32ioh;
            int ch_flags;
    #define WDCF_ACTIVE   0x01
    #define WDCF_IRQ_WAIT 0x10
            uint8_t ch_status;
            uint8_t ch_error;
            struct ata_drive_datas ch_drive[2];
            struct channel_queue *ch_queue;
    };

    struct ata_drive_datas {
        uint8_t drive;
        uint8_t drive_flags;
    #define DRIVE_ATA   0x01
    #define DRIVE_ATAPI 0x02
    #define DRIVE (DRIVE_ATA|DRIVE_ATAPI)
    #define DRIVE_CAP32 0x04
    #define DRIVE_DMA   0x08
    #define DRIVE_UDMA  0x10
    #define DRIVE_MODE 0x20
        uint8_t PIO_mode;
        uint8_t DMA_mode;
        uint8_t UDMA_mode;
        uint8_t state;

        struct device *drv_softc;
        void* chnl_softc;
    };

The bus front-end needs to fill in the following elements of wdc_softc:

cap
  supports one or more of the WDC_CAPABILITY flags
nchannels
  number of channels supported by this controller
channels
  array of struct channel_softc of size nchannels properly initialised

The following elements are optional:

pio_mode
dma_mode
dma_arg
dma_init
dma_start
dma_finish
claim_hw
free_hw

The `WDC_CAPABILITY_DATA16` and `WDC_CAPABILITY_DATA32` flags informs `wdc` whether the controller supports 16- or 32-bit I/O accesses on the data port. If both are set, a test will be done for each drive using the ATA or ATAPI IDENTIFY command, to automatically select the working mode.

The `WDC_CAPABILITY_DMA` and `WDC_CAPABILITY_UDMA` flags are set for controllers supporting the DMA and Ultra-DMA modes. The bus front-end needs to provide the `dma_init`(), `dma_start`() and `dma_finish`() functions. `dma_init`() is called just before issuing a DMA command to the IDE device. The arguments are, respectively: dma_arg, the channel number, the drive number on this channel, the virtual address of the DMA buffer, the size of the transfer, and the `WDC_DMA` flags. `dma_start`() is called just after issuing a DMA command to the IDE device. The arguments are, respectively: dma_arg, the channel number, the drive number on this channel, and the `WDC_DMA` flags. `dma_finish`() is called once the transfer is complete. The arguments are, respectively: dma_arg, the channel number, the drive number on this channel, and the `WDC_DMA` flags. `WDC_DMA_READ` indicates the direction of the data transfer, and `WDC_DMA_POLL` indicates if the transfer will use (or used) interrupts.

The `WDC_CAPABILITY_MODE` flag means that the bus front-end can program the PIO and DMA modes, so `wdc` needs to provide back the supported modes for each drive, and set the drives modes. The pio_mode and dma_mode needs to be set to the highest PIO and DMA mode supported. If `WDC_CAPABILITY_UDMA` is set, then dma_mode must be set to the highest Ultra-DMA mode supported. If `WDC_CAPABILITY_MODE` is not set, `wdc` will not attempt to change the current drive's settings, assuming the host's firmware has done it right.

The `WDC_CAPABILITY_HWLOCK` flag is set for controllers needing hardware looking before accessing the I/O ports. If this flag is set, the bus front-end needs to provide the `claim_hw`() and `free_hw`() functions. `claim_hw`() will be called when the driver wants to access the controller ports. The second parameter is set to 1 when it is possible to sleep waiting for the lock, 0 otherwise. It should return 1 when access has been granted, 0 otherwise. When access has not been granted and sleep is not allowed, the bus front-end shall call `wdcrestart`() with the first argument passed to `claim_hw`() as argument. This arguments will also be the one passed to `free_hw`(). This function is called once the transfer is complete, so that the lock can be released.

Accesses to the data port are done by using the bus_space stream functions, unless the `WDC_CAPABILITY_ATA_NOSTREAM` or `WDC_CAPABILITY_ATAPI_NOSTREAM` flags are set. This should not be used, unless the data bus is not wired properly (which seems common on big-endian systems), and byte-order needs to be preserved for compatibility with the host's firmware. Also note that the IDE bus is a little-endian bus, so the bus_space functions used for the bus_space tag passed in the channel_softc have to do the appropriate byte-swapping for big-endian systems.

`WDC_CAPABILITY_NO_EXTRA_RESETS` avoid the controller reset at the end of the disks probe. This reset is needed for some controllers, but causes problems with some others.

`WDC_CAPABILITY_NOIRQ` tells the driver that this controller doesn't have its interrupt lines wired up usefully, so it should always use polled transfers.

The bus front-end needs to fill in the following elements of channel_softc:

channel
  The channel number on the controller
wdc
  A pointer to the controller's wdc_softc
cmd_iot, cmd_ioh
  Bus-space tag and handle for access to the command block registers (which includes the 16-bit data port)
ctl_iot, ctl_ioh
  Bus-space tag and handle for access to the control block registers
ch_queue
  A pointer to a struct channel_queue. This will hold the queues of outstanding commands for this controller.

The following elements are optional:

data32iot, data32ioh
  Bus-space tag and handle for 32-bit data accesses. Only needed if `WDC_CAPABILITY_DATA32` is set in the controller's wdc_softc.

ch_queue can point to a common struct channel_queue if the controller doesn't support concurrent access to its different channels. If all channels are independent, it is recommended that each channel has its own ch_queue (for better performance).

The bus-specific front-end can use the `wdcprobe`() function, with a properly initialised struct channel_softc as argument ( wdc can be set to NULL. This allows `wdcprobe`() to be easily used in bus front-end probe functions). This function will return an integer where bit 0 will be set if the master device has been found, and 1 if the slave device has been found.

The bus-specific attach function has to call `wdcattach`() for each channel, with a pointer to a properly initialised channel softc as argument. This will probe devices attached to the IDE channel and attach them. Once this function returns, the ch_drive array of the channel_softc will contain the drive's capabilities. This can be used to properly initialise the controller's mode, or disable a channel without drives.

The elements of interest in ata_drive_datas for a bus front-end are:

drive
  The drive number
drive_flags
  Flags indicating the drive capabilities. A null drive_flags indicate either that no drive is here, or that no driver was found for this device.
PIO_mode, DMA_mode, UDMA_mode
  the highest supported modes for this drive compatible with the controller's capabilities. Needs to be reset to the mode to use by the drive, if known.
drv_softc
  A pointer to the drive's softc. Can be used to print the drive's name.

drive_flags handles the following flags:

DRIVE_ATA, DRIVE_ATAPI
  Gives the drive type, if any. The shortcut DRIVE can be used to just test the presence/absence of a drive.
DRIVE_CAP32
  This drive works with 32-bit data I/O.
DRIVE_DMA
  This drive supports DMA.
DRIVE_UDMA
  This drive supports Ultra-DMA.
DRIVE_MODE
  This drive properly reported its PIO and DMA mode.

Once the controller has been initialised, it has to reset the `DRIVE_DMA` and `DRIVE_UDMA`, as well as the values of PIO_mode, DMA_mode and UDMA_mode if the modes to be used are not highest ones supported by the drive.

# CODE REFERENCES

The wdc core functions are implemented in sys/dev/ic/wdc.c. Low-level ATA and ATAPI support is provided by sys/dev/ata_wdc.c and sys/dev/scsipi/atapi_wdc.c respectively.

An example of a simple bus front-end can be found in sys/dev/isapnp/wdc_isapnp.c. A more complex one, with multiple channels and bus-master DMA support is sys/dev/pci/pciide.c. sys/arch/atari/dev/wdc_mb.c makes use of hardware locking, and also provides an example of bus-front end for a big-endian system, which needs byte-swapping bus_space functions.

# SEE ALSO

wdc(4), bus_space(9)

April 18, 2010 | NetBSD 11.0