WDC(9) | Kernel Developer's Manual | WDC(9) |
wdc
—
#include <dev/ata/atavar.h>
#include <sys/dev/ic/wdcvar.h>
int
wdcprobe
(struct
channel_softc * chp);
void
wdcattach
(struct
channel_softc * chp);
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:
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:
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_flags handles the following flags:
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.
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.
April 18, 2010 | NetBSD 9.0 |