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)