cds.txt
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:39k
- Linux/390
- Common Device Support (CDS)
- Device Driver I/O Support Routines
- Author : Ingo Adlung
- Copyright, IBM Corp. 1999
- Introduction
- This document describes the common device support routines for Linux/390.
- Different than other hardware architectures, ESA/390 hasdefined a unified
- I/O access method. This gives relief to the device drivers as they don't
- have to deal with different bus types, polling versus interrupt
- processing, shared versus non-shared interrupt processing, DMA versus port
- I/O (PIO), and other hardware features more. However, this implies that
- either every single device driver needs to implement the hardware I/O
- attachment functionality itself, or the operating system provides for a
- unified method to access the hardware, providing all the functionality that
- every single device driver would have to provide itself.
- The document does not intend to explain the ESA/390 hardware architecture in
- every detail.This information can be obtained from the ESA/390 Principles of
- Operation manual (IBM Form. No. SA22-7201).
- In order to build common device support for ESA/390 I/O interfaces, a
- functional layer was introduced that provides generic I/O access methods to
- the hardware. The following figure shows the usage of the common device support
- of Linux/390 using a TbCP/IP driven device access an example. Similar figures
- could be drawn for other access methods, e.g. file system access to disk
- devices.
- The common device support layer shown above comprises the I/O support routines
- defined below. Some of them implement common Linux device driver interfaces,
- while some of them are ESA/390 platform specific.
- get_dev_info_by_irq() / get_dev_info_by_devno()
- allow a device driver to determine the devices attached (visible) to the
- system and their current status.
- get_irq_by_devno() / get_devno_by_irq()
- get irq (subchannel) from device number and vice versa.
- read_dev_chars()
- read device characteristics
- request_irq()
- obtain ownership for a specific device.
- free_irq()
- release ownership for a specific device.
- disable_irq()
- disable a device from presenting interrupts.
- enable_irq()
- enable a device, allowing for I/O interrupts.
- do_IO()
- initiate an I/O request.
- halt_IO()
- terminate the current I/O request processed on the device.
- do_IRQ()
- generic interrupt routine. This function is called by the interrupt entry
- routine whenever an I/O interrupt is presented to the system. The do_IRQ()
- routine determines the interrupt status and calls the device specific
- interrupt handler according to the rules (flags) defined during I/O request
- initiation with do_IO().
- The next chapters describe the functions, other than do_IRQ() in more details.
- The do_IRQ() interface is not described, as it is called from the Linux/390
- first level interrupt handler only and does not comprise a device driver
- callable interface. Instead, the functional description of do_IO() also
- describes the input to the device specific interrupt handler.
- Common Device Support (CDS) for Linux/390 Device Drivers
- General Information
- The following chapters describe the I/O related interface routines the
- Linux/390 common device support (CDS) provides to allow for device specific
- driver implementations on the IBM ESA/390 hardware platform. Those interfaces
- intend to provide the functionality required by every device driver
- implementaion to allow to drive a specific hardware device on the ESA/390
- platform. Some of the interface routines are specific to Linux/390 and some
- of them can be found on other Linux platforms implementations too.
- Miscellaneous function prototypes, data declarations, and macro definitions
- can be found in the architecture specific C header file
- linux/arch/s390/kernel/irq.h.
- Overview of CDS interface concepts
- Different to other hardware platforms, the ESA/390 architecture doesn't define
- interrupt lines managed by a specific interrupt controller and bus systems
- that may or may not allow for shared interrupts, DMA processing, etc.. Instead,
- the ESA/390 architecture has implemented a so called channel subsystem, that
- provides a unified view of the devices physically attached to the systems.
- Though the ESA/390 hardware platform knows about a huge variety of different
- peripheral attachments like disk devices (aka. DASDs), tapes, communication
- controllers, etc. they can all by accessed by a well defined access method and
- they are presenting I/O completion a unified way : I/O interruptions. Every
- single device is uniquely identified to the system by a so called subchannel,
- where the ESA/390 architecture allows for 64k devices be attached.
- Linux, however was first built on the Intel PC architecture, with its two
- cascaded 8259 programmable interrupt controllers (PICs), that allow for a
- maximum of 15 different interrupt lines. All devices attached to such a system
- share those 15 interrupt levels. Devices attached to the ISA bus system must
- not share interrupt levels (aka. IRQs), as the ISA bus bases on edge triggered
- interrupts. MCA, EISA, PCI and other bus systems base on level triggered
- interrupts, and therewith allow for shared IRQs. However, if multiple devices
- present their hardware status by the same (shared) IRQ, the operating system
- has to call every single device driver registered on this IRQ in order to
- determine the device driver owning the device that raised the interrupt.
- In order to not introduce a new I/O concept to the common Linux code,
- Linux/390 preserves the IRQ concept and semantically maps the ESA/390
- subchannels to Linux as IRQs. This allows Linux/390 to support up to 64k
- different IRQs, uniquely representig a single device each.
- During its startup the Linux/390 system checks for peripheral devices. Each
- of those devices is uniquely defined by a so called subchannel by the ESA/390
- channel subsystem. While the subchannel numbers are system generated, each
- subchannel also takes a user defined attribute, the so called device number.
- Both, subchannel number and device number can not exceed 65535. The
- init_IRQ() routine gathers the information about control unit type and device
- types that imply specific I/O commands (channel command words - CCWs) in
- order to operate the device. Device drivers can retrieve this set of hardware
- information during their initialization step to recognize the devices they
- support using get_dev_info_by_irq() or get_dev_info_by_devno() respectively.
- This methods implies that Linux/390 doesn't require to probe for free (not
- armed) interrupt request lines (IRQs) to drive its devices with. Where
- applicable, the device drivers can use the read_dev_chars() to retrieve device
- characteristics. This can be done without having to request device ownership
- previously.
- When a device driver has recognized a device it wants to claim ownership for,
- it calls request_irq() with the device's subchannel id serving as pseudo irq
- line. One of the required parameters it has to specify is dev_id, defining a
- device status block, the CDS layer will use to notify the device driver's
- interrupt handler about interrupt information observed. It depends on the
- device driver to properly handle those interrupts.
- In order to allow for easy I/O initiation the CDS layer provides a do_IO()
- interface that takes a device specific channel program (one or more CCWs) as
- input sets up the required architecture specific control blocks and initiates
- an I/O request on behalf of the device driver. The do_IO() routine allows for
- different I/O methods, synchronous and asynchronous, and allows to specify
- whether it expects the CDS layer to notify the device driver for every
- interrupt it observes, or with final status only. It also provides a scheme
- to allow for overlapped I/O processing. See do_IO() for more details. A device
- driver must never issue ESA/390 I/O commands itself, but must use the
- Linux/390 CDS interfaces instead.
- For long running I/O request to be canceled, the CDS layer provides the
- halt_IO() function. Some devices require to initially issue a HALT SUBCHANNEL
- (HSCH) command without having pending I/O requests. This function is also
- covered by halt_IO().
- When done with a device, the device driver calls free_irq() to release its
- ownership for the device. During free_irq() processing the CDS layer also
- disables the device from presenting further interrupts - the device driver
- doesn't need to assure it. The device will be reenabled for interrupts with
- the next call to request_irq().
- get_dev_info_by_irq() / get_dev_info_by_devno() - Retrieve Device Information
- During system startup - init_IRQ() processing - the generic I/O device support
- checks for the devices available. For all devices found it collects the
- SenseID information. For those devices supporting the command it also obtains
- extended SenseID information.
- int get_dev_info_by_irq( int irq,
- dev_info_t *devinfo);
- int get_dev_info_by_devno( unsigned int irq,
- dev_info_t *devinfo);
- irq - defines the subchannel, status information is to be
- returned for.
- devno - device number.
- devinfo - pointer to a user buffer of type dev_info_t that should
- be filled with device specific information.
- typedef struct {
- unsigned int devno; /* device number */
- unsigned int status; /* device status */
- senseid_t sid_data; /* senseID data */
- } dev_info_t;
- devno - device number as configured in the IOCDS.
- status - device status
- sid_data - data obtained by a SenseID call
- Possible status values are :
- DEVSTAT_NOT_OPER - device was found not-operational. In this case
- the caller should disregard the sid_data
- buffer content.
- //
- // SenseID response buffer layout
- //
- typedef struct {
- /* common part */
- unsigned char reserved; /* always 0x'FF' */
- unsigned short cu_type; /* control unit type */
- unsigned char cu_model; /* control unit model */
- unsigned short dev_type; /* device type */
- unsigned char dev_model; /* device model */
- unsigned char unused; /* padding byte */
- /* extended part */
- ciw_t ciw[62]; /* variable # of CIWs */
- } senseid_t;
- The ESA/390 I/O architecture defines certain device specific I/O functions.
- The device returns the device specific command code together with the SenseID
- data in so called Command Information Words (CIW) :
- typedef struct _ciw {
- unsigned int et : 2; // entry type
- unsigned int reserved : 2; // reserved
- unsigned int ct : 4; // command type
- unsigned int cmd : 8; // command
- unsigned int count : 16; // count
- } ciw_t;
- Possible CIW entry types are :
- #define CIW_TYPE_RDC 0x0; // read configuration data
- #define CIW_TYPE_SII 0x1; // set interface identifier
- #define CIW_TYPE_RNI 0x2; // read node identifier
- The device driver may use these commands as appropriate.
- The get_dev_info_by_irq() / get_dev_info_by_devno() functions return:
- 0 - successful completion
- -ENODEV - irq or devno don't specify a known subchannel or device
- number.
- -EINVAL - invalid devinfo value.
- Usage Notes :
- In order to scan for known devices a device driver should scan all irqs by
- calling get_dev_info() until it returns -ENODEV as there aren't any more
- available devices.
- If a device driver wants to request ownership for a specific device it must
- call request_irq() prior to be able to issue any I/O request for it, including
- above mentioned device dependent commands.
- Please see the "ESA/390 Common I/O-Commandss and Self Description" manual,
- with IBM form number SA22-7204 for more details on how to read the Sense-ID
- output, CIWs and device independent commands.
- get_irq_by_devno() / get_devno_by_irq() - Convert device identifiers
- While some device drivers act on the irq (subchannel) only, others take user
- defined device configurations on device number base, according to the device
- numbers configured in the IOCDS. The following routines serve the purpose to
- convert irq values into device numbers and vice versa.
- int get_irq_by_devno( unsigned int devno );
- unsigned int get_devno_by_irq( int irq );
- The functions return :
- the requested irq/devno values
- -1 if the requested conversion can't be accomplished.
- This could either be caused by irq/devno be outside the valid range
- ( value > 0xffff or value < 0 ) or not identifying a known device.
- read_dev_chars() - Read Device Characteristics
- This routine returns the characteristics for the device specified.
- The function is meant to be called without an irq handler be in place.
- However, the irq for the requested device must not be locked or this will
- cause a deadlock situation ! Further, the driver must assure that nobody
- else has claimed ownership for the requested irq yet or the owning device
- driver's internal accounting may be affected.
- In case of a registered interrupt handler, the interrupt handler must be
- able to properly react on interrupts related to the read_dev_chars() I/O
- commands. While the request is procesed synchronously, the device interrupt
- handler is called for final ending status. In case of error situations the
- interrupt handler may recover appropriately. The device irq handler can
- recognize the corresponding interrupts by the interruption parameter be
- 0x00524443. If using the function with an existing device interrupt handler
- in place, the irq must be locked prior to call read_dev_chars().
- The function may be called enabled or disabled.
- int read_dev_chars( int irq, void **buffer, int length );
- irq - specifies the subchannel the device characteristic
- retrieval is requested for
- buffer - pointer to a buffer pointer. The buffer pointer itself
- may be NULL to have the function allocate a buffer or
- must contain a valid buffer area.
- length - length of the buffer provided or to be allocated.
- The read_dev_chars() function returns :
- 0 - successful completion
- -ENODEV - irq doesn't specify a valid subchannel number
- -EINVAL - an invalid parameter was detected
- -EBUSY - an irrecoverable I/O error occurred or the device is not
- operational.
- Usage Notes :
- The function can be used in two ways :
- If the caller doesn't provide a data buffer, read_dev_chars() allocates a
- data buffer and provides the device characteristics together. It's the
- caller's responsability to release the kernel memory if not longer needed.
- This behaviour is triggered by specifying a NULL buffer area (*buffer == NULL).
- Alternatively, if the user specifies a buffer area himself, nothing is
- allocated.
- In either case the caller must provide the data area length - for the buffer
- he specifies, or the buffer he wants to be allocated.
- request_irq() - Request Device Ownership
- As previously discussed a device driver will scan for the devices its supports
- by calling get_dev_info(). Once it has found a device it will call
- request_irq() to request ownership for it. This call causes the subchannel to
- be enabled for interrupts if it was found operational.
- int request_irq( unsigned int irq,
- int (*handler)( int,
- void *,
- struct pt_regs *),
- unsigned long irqflags,
- const char *devname,
- void *dev_id);
- irq : specifies the subchannel the ownership is requested for
- handler : specifies the device driver's interrupt handler to be
- called for interrupt processing
- irqflags : IRQ flags, must be 0 (zero) or SA_SAMPLE_RANDOM
- devname : device name
- dev_id : required pointer to a device specific buffer of type
- devstat_t
- typedef struct {
- unsigned int devno; /* device number from irb */
- unsigned int intparm; /* interrupt parameter */
- unsigned char cstat; /* channel status - accumulated */
- unsigned char dstat; /* device status - accumulated */
- unsigned char lpum; /* last path used mask from irb */
- unsigned char unused; /* not used - reserved */
- unsigned int flag; /* flag : see below */
- unsigned long cpa; /* CCW addr from irb at prim. status */
- unsigned int rescnt; /* count from irb at primary status */
- unsigned int scnt; /* sense count, if available */
- union {
- irb_t irb; /* interruption response block */
- sense_t sense; /* sense information */
- } ii; /* interrupt information */
- } devstat_t;
- During request_irq() processing, the devstat_t layout does not matter as it
- won't be used during request_irq() processing. See do_IO() for a functional
- description of its usage.
- The request_irq() function returns :
- 0 - successful completion
- -EINVAL - an invalid parameter was detected
- -EBUSY - device (subchannel) already owned
- -ENODEV - the device is not-operational
- -ENOMEM - not enough kernel memory to process request
- Usage Notes :
- While Linux for Intel defines dev_id as a unique identifier for shared
- interrupt lines it has a totally different purpose on Linux/390. Here it
- serves as a shared interrupt status area between the generic device support
- layer, and the device specific driver. The value passed to request_irq()
- must therefore point to a valid devstat_t type buffer area the device driver
- must preserve for later usage. I.e. it must not be released prior to a call
- to free_irq()
- The only value parameter irqflags supports is SA_SAMPLE_RANDOM if appropriate.
- The Linux/390 kernel does neither know about "fast" interrupt handlers, nor
- does it allow for interrupt sharing. Remember, the term interrupt level (irq),
- device, and subchannel are used interchangeably in Linux/390.
- If request_irq() was called in enabled state, or if multiple CPUs are present,
- the device may present an interrupt to the specified handler prior to
- request_irq() return to the caller already ! This includes the possibility
- of unsolicited interrupts or a pending interrupt status from an earlier
- solicited I/O request. The device driver must be able to handle this situation
- properly or the device may become unoperational otherwise !
- Although the interrupt handler is defined to be called with a pointer to a
- struct pt_regs buffer area, this is not implemented by the Linux/390 generic
- I/O device driver support layer. The device driver's interrupt handler must
- therefore not rely on this parameter on function entry.
- free_irq() - Release Device Ownership
- A device driver may call free_irq() to release ownership of a previously
- acquired device.
- void free_irq( unsigned int irq,
- void *dev_id);
- irq : specifies the subchannel the ownership is requested for
- dev_id : required pointer to a device specific buffer of type
- devstat_t. This must be the same as the one specified
- during a previous call to request_irq().
- Usage Notes :
- Unfortunately the free_irq() is defined not to return error codes. I.e. if
- called with wrong parameters a device may still be operational although there
- is no device driver available to handle its interrupts. Further, during
- free_irq() processing we may possibly find pending interrupt conditions. As
- those need to be processed, we have to delay free_irq() returning until a
- clean device status is found by synchronously handling them.
- The call to free_irq() will also cause the device (subchannel) be disabled for
- interrupts. The device driver must not release any data areas required for
- interrupt processing prior to free_irq() return to the caller as interrupts
- can occur prior to free_irq() returning. This is also true when called in
- disabled state if either multiple CPUs are presents or a pending interrupt
- status was found during free_irq() processing.
- disable_irq() - Disable Interrupts for a given Device
- This function may be called at any time to disable interrupt processing for
- the specified irq. However, as Linux/390 maps irqs to the device (subchannel)
- one-to-one, this may require more extensive I/O processing than anticipated,
- especially if an interrupt status is found pending on the subchannel that
- requires synchronous error processing.
- int disable_irq( unsigned int irq );
- irq : specifies the subchannel to be disabled
- The disable-irq() routine may return :
- 0 - successful completion
- -EBUSY - device (subchannel) is currently processing an I/O
- request
- -ENODEV - the device is not-operational or irq doesn't specify a
- valid subchannel
- Usage Notes :
- Unlike the Intel based hardware architecture the ESA/390 architecture does
- not have a programmable interrupt controller (PIC) where a specific interrupt
- line can be disabled. Instead the subchannel logically representing the device
- in the channel subsystem must be disabled for interrupts. However, if there
- are still inetrrupt conditions pending they must be processed first in order
- to allow for proper processing after reenabling the device at a later time.
- This may lead to delayed disable processing.
- As described above the disable processing may require extensive processing.
- Therefore disabling and re-enabling the device using disable_irq() /
- enable_irq() should be avoided and is not suitable for high frequency
- operations.
- Linux for Intel defines this function
- void disable_irq( int irq);
- This is suitable for the Intel PC architecture as this only causes to mask
- the requested irq line in the PIC which is not applicable for the ESA/390
- architecture. Therefore we allow for returning error codes.
- enable_irq() - Enable Interrupts for a given Device
- This function is used to enable a previously disabled device (subchannel).
- See disable_irq() for more details.
- int enable_irq( unsigned int irq );
- irq : specifies the subchannel to be enabled
- The enable-irq() routine may return :
- 0 - successful completion
- -EBUSY - device (subchannel) is currently processing an I/O
- request. This implies the device is already in enabled
- state
- -ENODEV - the device is not-operational or irq doesn't specify a
- valid subchannel
- do_IO() - Initiate I/O Request
- The do_IO() routines is the I/O request front-end processor. All device driver
- I/O requests must be issued using this routine. A device driver must not issue
- ESA/390 I/O commands itself. Instead the do_IO() routine provides all
- interfaces required to drive arbitrary devices.
- This description also covers the status information passed to the device
- driver's interrupt handler as this is related to the rules (flags) defined
- with the associated I/O request when calling do_IO().
- int do_IO( int irq,
- ccw1_t *cpa,
- unsigned long intparm,
- unsigned int lpm,
- unsigned long flag);
- irq : irq (subchannel) the I/O request is destined for
- cpa : logical start address of channel program
- intparm : user specific interrupt information; will be presented
- back to the device driver's interrupt handler. Allows a
- device driver to associate the interrupt with a
- particular I/O request.
- lpm : defines the channel path to be used for a specific I/O
- request. Valid with flag value DOIO_VALID_LPM only.
- flag : defines the action to e parformed for I/O processing
- Possible flag values are :
- DOIO_EARLY_NOTIFICATION - allow for early interrupt notification
- DOIO_VALID_LPM - LPM input parameter is valid (see usage
- notes below for details)
- DOIO_WAIT_FOR_INTERRUPT - wait synchronously for final status
- DOIO_REPORT_ALL - report all interrupt conditions
- The cpa parameter points to the first format 1 CCW of a channel program :
- typedef struct {
- char cmd_code; /* command code */
- char flags; /* flags, like IDA addressing, etc. */
- unsigned short count; /* byte count */
- void *cda; /* data address */
- } ccw1_t __attribute__ ((aligned(8)));
- with the following CCW flags values defined :
- CCW_FLAG_DC - data chaining
- CCW_FLAG_CC - command chaining
- CCW_FLAG_SLI - suppress incorrct length
- CCW_FLAG_SKIP - skip
- CCW_FLAG_PCI - PCI
- CCW_FLAG_IDA - indirect addressing
- CCW_FLAG_SUSPEND - suspend
- The do_IO() function returns :
- 0 - successful completion or request successfully initiated
- -EBUSY - the do_io() function was caled out of sequence. The
- device is currently processing a previous I/O request
- -ENODEV - irq doesn't specify a valid subchannel, the device is
- not operational (check dev_id.flags) or the irq is not
- owned.
- -EINVAL - both, DOIO_EARLY_NOTIFICATION and DOIO_REORT_ALL flags
- have been specified. The usage of those flags is mutual
- exclusive.
- When the I/O request completes, the CDS first level interrupt handler will
- setup the dev_id buffer of type devstat_t defined during request_irq()
- processing. See request_irq() for the devstat_t data layout. The
- dev_id->intparm field in the device status area will contain the value the
- device driver has associated with a particular I/O request. If a pending
- device status was recognized dev_id->intparm will be set to 0 (zero). This
- may happen during I/O initiation or delayed by an alert status notification.
- In any case this status is not related to the current (last) I/O request. In
- case of a delayed status notification no special interrupt will be presented
- to indicate I/O completion as the I/O request was never started, even though
- do_IO() returned with successful completion.
- Possible dev_id->flag values are :
- DEVSTAT_FLAG_SENSE_AVAIL - sense data is available
- DEVSTAT_NOT_OPER - device is not-operational
- DEVSTAT_START_FUNCTION - interrupt is presented as result of a
- call to do_IO()
- DEVSTAT_HALT_FUNCTION - interrupt is presented as result of a
- call to halt_IO()
- DEVSTAT_STATUS_PENDING - a pending status was found. The I/O
- resquest (if any) was not initiated.
- This status might have been presented
- delayed, after do_IO() or halt_IO() have
- successfully be started previously.
- DEVSTAT_FINAL_STATUS - This is a final interrupt status for the
- I/O requst identified by intparm.
- If device status DEVSTAT_FLAG_SENSE_AVAIL is indicated in field dev_id->flag,
- field dev_id->scnt describes the numer of device specific sense bytes
- available in the sense area dev_id->ii.sense. No device sensing by the device
- driver itself is required.
- typedef struct {
- unsigned char res[32]; /* reserved */
- unsigned char data[32]; /* sense data */
- } sense_t;
- The device interrupt handler can use the following definitions to investigate
- the primary unit check source coded in sense byte 0 :
- SNS0_CMD_REJECT 0x80
- SNS0_INTERVENTION_REQ 0x40
- SNS0_BUS_OUT_CHECK 0x20
- SNS0_EQUIPMENT_CHECK 0x10
- SNS0_DATA_CHECK 0x08
- SNS0_OVERRUN 0x04
- Depending on the device status, multiple of those values may be set together.
- Please refer to the device specific documentation for details.
- The devi_id->cstat field provides the (accumulated) subchannel status :
- SCHN_STAT_PCI - program controlled interrupt
- SCHN_STAT_INCORR_LEN - incorrect length
- SCHN_STAT_PROG_CHECK - program check
- SCHN_STAT_PROT_CHECK - protection check
- SCHN_STAT_CHN_DATA_CHK - channel data check
- SCHN_STAT_CHN_CTRL_CHK - channel control check
- SCHN_STAT_INTF_CTRL_CHK - interface control check
- SCHN_STAT_CHAIN_CHECK - chaining check
- The dev_id->dstat field provides the (accumulated) device status :
- DEV_STAT_ATTENTION - attention
- DEV_STAT_STAT_MOD - status modifier
- DEV_STAT_CU_END - control unit end
- DEV_STAT_BUSY - busy
- DEV_STAT_CHN_END - channel end
- DEV_STAT_DEV_END - device end
- DEV_STAT_UNIT_CHECK - unit check
- DEV_STAT_UNIT_EXCEP - unit exception
- Please see the ESA/390 Principles of Operation manual for details on the
- individual flag meanings.
- In rare error situations the device driver may require access to the original
- hardware interrupt data beyond the scope of above mentioned infromation. For
- those situations the Linux/390 common device support provides the interrupt
- response block (IRB) as part of the device status block in dev_id->ii.irb.
- Usage Notes :
- Prior to call do_IO() the device driver must
- assure disabled state, i.e. the I/O mask value in the PSW must be disabled.
- This can be accomplished by calling __save_flags( flags). The current PSW
- flags are preserved and can be restored by __restore_flags( flags) at a
- later time.
- If the device driver violates this rule while running in a uni-processor
- environment an interrupt might be presented prior to the do_IO() routine
- returning to the device driver main path. In this case we will end in a
- deadlock situation as the interrupt handler will try to obtain the irq
- lock the device driver still owns (see below) !
- the driver must assure to hold the device specific lock. This can be
- accomplished by
- (i) s390irq_spin_lock( irq), or
- (ii) s390irq_spin_lock_irqsave(irq, flags)
- Option (i) should be used if the calling routine is running disabled for
- I/O interrupts (see above) already. Option (ii) obtains the device gate und
- puts the CPU into I/O disabled state by preserving the current PSW flags.
- See the descriptions of s390irq_spin_lock() or s390irq_spin_lock_irqsave()
- for more details.
- The device driver is allowed to issue the next do_IO() call from within its
- interrupt handler already. It is not required to schedule a bottom-half,
- unless an non deterministicly long running error recovery procedure or
- similar needs to be scheduled. During I/O processing the Linux/390 generic
- I/O device driver support has already obtained the IRQ lock, i.e. the handler
- must not try to obtain it again when calling do_IO() or we end in a deadlock
- situation ! Anyway, the device driver's interrupt handler must only call
- do_IO() if the handler itself can be entered recursively if do_IO() e.g. finds
- a status pending and needs to all the interrupt handler itself.
- Device drivers shouldn't heavily rely on DOIO_WAIT_FOR_INTERRUPT synchronous
- I/O request processing. All I/O devices, but the console device are driven
- using a single shared interrupt subclass (ISC). For sync. processing the
- device is temporarily mapped to a special ISC while the calling CPU waits for
- I/O completion. As this special ISC is gated, all sync. requests in a SMP
- environment are serialized which may cause other CPUs to spin. This service
- is therewith primarily meant to be used during device driver initialization
- for ease of device setup.
- The lpm input parameter might be used for multipath devices shared among
- multiple systems as the Linux/390 CDS isn't grouping channel paths. Therefore
- its use might be required if multiple access paths to a device are available
- and the device was reserved by means of a reserve device command (for devices
- supporting this technique). When issuing this command the device driver needs
- needs to extract the dev_id->lpum value and restrict all subsequent channel
- programs to this channel path until the device is released by a device release
- command. Otherwise a deadlock may occur.
- If a device driver relies on an I/O request to be completed prior to start the
- next it can reduce I/O processing overhead by chaining a NoOp I/O command
- CCW_CMD_NOOP to the end of the submitted CCW chain. This will force Channel-End
- and Device-End status to be presented together, with a single interrupt.
- However, this should be used with care as it implies the channel will remain
- busy, not being able to process I/O requests for other devices on the same
- channel. Therefore e.g. read commands should never use this technique, as the
- result will be presented by a single interrupt anyway.
- In order to minimize I/O overhead, a device driver should use the
- DOIO_REPORT_ALL only if the device can report intermediate interrupt
- information prior to device-end the device driver urgently relies on. In this
- case all I/O interruptions are presented to the device driver until final
- status is recognized.
- If a device is able to recover from asynchronosly presented I/O errors, it can
- perform overlapping I/O using the DOIO_EARLY_NOTIFICATION flag. While some
- devices always report channel-end and device-end together, with a single
- interrupt, others present primary status (channel-end) when the channel is
- ready for the next I/O request and secondary status (device-end) when the data
- transmission has been completed at the device.
- Above flag allows to exploit this feature, e.g. for communication devices that
- can handle lost data on the network to allow for enhanced I/O processing.
- Unless the channel subsystem at any time presents a secondary status interrupt,
- exploiting this feature will cause only primary status interrups to be
- presented to the device driver while overlapping I/O is performed. When a
- secondary status without error (alert status) is presented, this indicates
- successful completion for all overlapping do_IO() requests that have been
- issued since the last secondary (final) status.
- During interrupt processing the device specific interrupt handler should avoid
- basing its processing decisions on the interruption response block (IRB) that
- is part of the dev_id buffer area. The IRB area represents the interruption
- parameters from the last interrupt received. Unless the device driver has
- specified DOIO_REPORT_ALL or is called with a pending status
- (DEVSTAT_STATUS_PENDING), the IRB information may or may not show the complete
- interruption status, but the last interrupt only. Therefore the device driver
- should usually base its processing decisions on the values of dev_id->cstat
- and dev_id->dstat that represent the accumulated subchannel and device status
- information gathered since do_IO() request initiation.
- halt_IO() - Halt I/O Request Processing
- Sometimes a device driver might need a possibility to stop the processing of
- a long-running channel program or the device might require to initially issue
- a halt subchannel (HSCH) I/O command. For those purposes the halt_IO() command
- is provided.
- int halt_IO( int irq, /* subchannel number */
- int intparm, /* dummy intparm */
- unsigned int flag); /* operation mode */
- irq : irq (subchannel) the halt operation is requested for
- intparm : interruption parameter; value is only used if no I/O
- is outstanding, otherwise the intparm associated with
- the I/O request is returned
- flag : 0 (zero) or DOIO_WAIT_FOR_INTERRUPT
- The halt_IO() function returns :
- 0 - successful completion or request successfully initiated
- -EBUSY - the device is currently performing a synchronous I/O
- operation : do_IO() with flag DOIO_WAIT_FOR_INTERRUPT
- or an error was encountered and the device is currently
- be sensed
- -ENODEV - the irq specified doesn't specify a valid subchannel, the
- device is not operational (check dev_id.flags) or the irq
- is not owned.
- Usage Notes :
- A device driver may write a never-ending channel program by writing a channel
- program that at its end loops back to its beginning by means of a transfer in
- channel (TIC) command (CCW_CMD_TIC). Usually this is performed by network
- device drivers by setting the PCI CCW flag (CCW_FLAG_PCI). Once this CCW is
- executed a program controlled interrupt (PCI) is generated. The device driver
- can then perform an appropriate action. Prior to interrupt of an outstanding
- read to a network device (with or without PCI flag) a halt_IO() is required
- to end the pending operation.
- We don't allow to stop sync. I/O requests by means of a halt_IO() call. The
- function will return -EBUSY instead.
- Miscellaneous Support Routines
- This chapter describes various routines to be used in a Linux/390 device
- driver programming environment.
- s390irq_spin_lock() / s390irq_spin_unlock()
- Those two macro definitions are required to obtain the device specific IRQ
- lock. The lock needs to be obtained if the device driver intends to call
- do_IO() or halt_IO() from anywhere but the device interrupt handler (where
- the lock is already owned). Those routines must only be used if running
- disabled for interrupts already. Otherwise use s390irq_spin_lock_irqsave()
- and the corresponding unlock routine instead (see below).
- s390irq_spin_lock( int irq);
- s390irq_spin_unlock( int irq);
- s390irq_spin_lock_irqsave() / s390_irq_spin_unlock_irqrestore()
- Those two macro definitions are required to obtain the device specific IRQ
- lock. The lock needs to be obtained if the device driver intends to call
- do_IO() or halt_IO() from anywhere but the device interrupt handler (where
- the lock is already owned). Those routines should only be used if running
- enabled for interrupts. If running disabled already, the driver should use
- s390irq_spin_lock() and the corresponding unlock routine instead (see above).
- s390irq_spin_lock_irqsave( int irq, unsigned long flags);
- s390irq_spin_unlock_irqrestore( int irq, unsigned long flags);
- Special Console Interface Routines
- This chapter describes the special interface routines required for system
- console processing. Though they are an extension to the Linux/390 device
- driver interface concept, they base on the same principles. It was necessary
- to build those extensions to assure a deterministic behaviour in critical
- situations e.g. printk() messages by other device drivers running disabled
- for interrupts during I/O interrupt handling or in case of a panic() message
- being raised.
- set_cons_dev - Set Console Device
- This routine allows to specify the system console device. This is necessary
- as the console isn't driven by the same ESA/390 interrupt subclass as are
- other devices, but it is assigned ist own interrupt subclass. Only one device
- can act as system console. See wait_cons_dev() for details.
- int set_cons_dev( int irq);
- irq : subchannel identifying the system console device
- The set_cons_dev() function returns
- 0 - successful completion
- -EIO - an unhandled interrupt condition is pending for the
- specified subchannel (irq) - status pending
- -ENODEV - irq doesn't specify a valid subchannel or the devive is
- not operational
- -EBUSY - the console device is already defined
- reset_cons_dev - Reset Console Device
- This routine allows for resetting the console device specification. See
- set_cons_dev() for details.
- int reset_cons_dev( int irq);
- irq : subchannel identifying the system console device
- The reset_cons_dev() function returns
- 0 - successful completion
- -EIO - an unhandled interrupt condition is pending for the
- specified subchannel (irq) - status pending
- -ENODEV - irq doesn't specify a valid subchannel or the devive is
- not operational
- wait_cons_dev - Synchronously Wait for Console Processing
- The wait_cons_dev() routine is used by the console device driver when its
- buffer pool for intermediate request queuing is exhausted and a new output
- request is received. In this case the console driver uses the wait_cons_dev()
- routine to synchronously wait until enough buffer space is gained to enqueue
- the current request. Any pending interrupt condition for the console device
- found during wait_cons_dev() processing causes its interrupt handler to be
- called.
- int wait_cons_dev( int irq);
- irq : subchannel identifying the system console device
- The wait_cons_dev() function returns :
- 0 - successful completion
- -EINVAL - the irq specified doesn't match the irq configured for
- the console device by set_cons_dev()
- Usage Notes :
- The function should be used carefully. Especially in a SMP environment the
- wait_cons_dev() processing requires that all but the special console ISC are
- disabled. In a SMP system this requires the other CPUs to be signaled to
- disable/enable those ISCs.