[Return to Library] [Contents] [Previous Chapter] [Next Section] [Next Chapter] [Index] [Help]


6    PCI Bus Device Driver Example

This chapter provides you with an opportunity to study a PCI device driver called /dev/pnvram. You can use the /dev/pnvram device driver as the basis for writing your own working PCI bus device drivers. The /dev/pnvram device driver operates on a PCI bus and implements many of the device driver interfaces shown in Chapter 3. It also implements other sections that the PCI bus nonvolatile random access memory (NVRAM) expansion board needs.

The chapter begins with an overview of the tasks performed by the /dev/pnvram device driver. Following this overview are sections that describe each piece of the /dev/pnvram device driver. Table 6-1 lists the parts of the /dev/pnvram device driver and the sections of the chapter where each is described.

Table 6-1: Parts of the /dev/pnvram Device Driver

Part Section
The pnvram.h Header File Section 6.2
Defining Data Structures Section 6.3
Include Files Section Section 6.4
Declarations Section Section 6.5
Autoconfiguration Support Section Section 6.6
Status Section Section 6.7
Battery Status Section Section 6.8
Read and Write Device Section Section 6.9
Zero NVRAM Section Section 6.10
Interrupt Section Section 6.11

The source code uses the following convention:

#define PNVRAM_MAPPED 1 [1]

  1. Numbers appear after some line or lines of code in the /dev/pnvram device driver example. Following the example, a corresponding number appears that contains an explanation for the associated line or lines. The source code does not contain any inline comments. If you prefer to read the /dev/pnvram driver source code in its entirety with the inline comments, see Appendix B. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Next Section] [Next Chapter] [Index] [Help]


6.1    Overview of the /dev/pnvram Device Driver

The /dev/pnvram device driver is a character device driver that provides read and write services to the /dev/presto device driver. The /dev/presto device driver is a disk driver that uses nonvolatile memory as a cache. It works as a layer between other drivers and the rest of the Digital UNIX kernel.

The /dev/presto device driver's interfaces appear as the entry points in the dsent switch table structure, instead of the interfaces of the other drivers (including the /dev/pnvram driver) it works with. Whenever /dev/presto needs to perform actual I/O operations (for example, when the cache needs filling or draining), it calls the layered driver's entry points (strategy, close, read, and write).

Figure 6-1 shows the relationship between the /dev/pnvram and /dev/presto device drivers. The figure shows the following flow of data between the different layers:

  1. The file system makes a read or write request.

  2. The /dev/presto device driver interprets the request as an actual I/O operation. It calls the /dev/pnvram device driver.

  3. The /dev/pnvram device driver performs the actual work of reading data from and writing data to the PCI bus NVRAM expansion board. In addition to providing services to the /dev/presto driver, the /dev/pnvram device driver also:

Figure 6-1: Relationship of the /dev/pnvram and /dev/presto Device Drivers


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.2    The pnvram.h Header File

The pnvram.h header file is the header file for the /dev/pnvram device driver. It defines the following items:

The pnvram.h header file contains the following code:

#define PNV_ID           0x00L  [1]
#define PNV_CMD          0x04L
#define PNV_STAT         0x06L
#define PNV_ERROR        0x40L
#define PNV_FADDR        0x44L
#define PNV_MEM_CFG      0x48L
#define PNV_EDC_CTRL     0x4CL
#define PNV_EDC_SYND     0x50L
#define PNV_BAT_CTRL     0x54L
#define PNV_BAT_DIAG_RST 0x58L
#define PNV_EEPROM_IF    0x5CL
#define PNV_SLAVE_ADDR   0x60L
#define PNV_MASTER_ADDR  0x64L
#define PNV_BYTE_COUNT   0x68L
#define PNV_DMA_CMD      0x6cL
#define PNV_INTR_CTRL    0x70L

 
#define PNVR_ERR_SUM 0x01 [2] #define PNVR_CMD_PAR 0x0010 #define PNVR_WR_PAR 0x0020 #define PNVR_CRD_ERR 0x0040 #define PNVR_UCRD_ERR 0x0080 #define PNVR_M_CMD_ERR 0x1000 #define PNVR_M_WR_PAR 0x2000 #define PNVR_M_CRD_ERR 0x4000 #define PNVR_M_UCRD_ERR 0x8000
 
#define PNV_BANK_MASK 0x0003 [3] #define PNV_SIZE_MASK 0x000c #define PNV_MOD_REV_MASK 0x0070 #define PNV_CHIP_REV_MASK 0x0F00
 
#define PNV_SIZE_SHIFT 1 [4] #define PNV_512K 0x80000
 
#define PNVR_DIS_CRD_LOG 0x1000 [5] #define PNVR_DIS_EDC_SERR 0x8000
 
#define PNV_BCHRG 0x001 [6] #define PNV_BDISC 0x002 #define PNV_BFAIL 0x004 #define PNV_BAT_OK 0x008 #define PNV_SPEED_MASK 0x300
 
#define PNV_SCL 0x01 [7] #define PNV_XMT_SDA 0x20 #define PNV_RCV_SDA 0x40
 
#define PNV_MAX_XFER_SIZE 65532 [8]
 
#define PNV_NV_READ 0x01 [9] #define PNV_NV_WRITE 0x02
 
#define PNV_ENAB_DMA 0x01 [10] #define PNV_ENAB_CRD 0x02 #define PNV_DISAB_ABRT 0x04 #define PNV_DMA_INTR 0x100 #define PNV_CRD_INTR 0x200 #define PNV_ABRT_INTR 0x400 #define PNV_INTR_OCCURRED (PNV_DMA_INTR | PNV_CRD_INTR | PNV_ABRT_INTR)
 
#define PNV_CACHE_OFFSET 0x400 [11] #define PNV_DIAG_RESERVED 0x400 #define PNV_DIAG_RESULT 0x08L #define PNV_DIAG_SIZE 0x04 #define BOARD_FAILED 0x00000008
 
#define PNVRAM_MAPPED 1 [12] #define PNVRAM_NOTMAPPED 0 #define PNVRAM_CACHED 1 #define PNVRAM_NOTCACHED 0
 
struct pnvram_softc { [13] io_handle_t csr_base; vm_offset_t cache_phys_start; vm_offset_t cache_base; vm_offset_t cache_kseg_start; u_int cache_size; u_int cache_offset; u_int diag_status; volatile u_int dma_in_progress; dma_handle_t dma_p; struct controller *ctlr; ihandler_id_t *hid; u_int chip_rev; };

  1. The first 16 #define statements provide offsets to the device registers of the PCI bus NVRAM expansion board. The /dev/pnvram device driver uses these register offsets in calls to the PNV_READIO, PNV_WRITEIO, PNV_READ_CNFG, and PNV_WRITE_CONFG interfaces to access the NVRAM's device registers. (See Section 6.5 for a discussion of these interfaces.)

    The following list describes each device register offset definition:

    [Return to example]

  2. The next nine #define statements define the bits of the PNV_ERROR register, as shown in the following table:
    Definition Meaning
    PNVR_ERR_SUM Error summary bit. When set, indicates that other error bits in the register are also set and that the contents of the PNV_FADDR and PNV_EDC_SYND registers are frozen. To clear the PNVR_ERR_SUM bit (and unlatch these registers), you must first clear the other error bits in the register.
    PNVR_CMD_PAR Command or address parity error bit. When set, indicates bad parity during a PCI bus address phase. The PNV_FADDR register contains the address at which the failure occurred. You clear this bit by writing a 1 to it.
    PNVR_WR_PAR Write data parity error bit. When set, indicates bad parity during a PCI write data phase. The PNV_FADDR register contains the address at which the failure occurred. You clear this bit by writing a 1 to it.
    PNVR_CRD_ERR Correctable EDC error bit. When set, indicates an EDC correctable error in fetched NVRAM memory data. The PNV_FADDR register contains the address at which the failure occurred. You clear this bit by writing a 1 to it.
    PNVR_UCRD_ERR Uncorrectable EDC error bit. When set, indicates an EDC uncorrectable error in fetched NVRAM memory data. The PNV_FADDR register contains the address at which the failure occurred. You clear this bit by writing a 1 to it.
    PNVR_M_CMD_ERR Missed command or address parity error bit. When set, indicates that bad parity occurred during a PCI bus address phase and some other error bit was already set in this register. For this error, the PNV_FADDR register does not contain the address at which the failure occurred. You clear this bit by writing a 1 to it.
    PNVR_M_WR_PAR Missed write data parity error bit. When set, indicates that bad parity occurred during a PCI write data phase and some other error bit was already set in this register. For this error, the PNV_FADDR register does not contain the address at which the failure occurred. You clear this bit by writing a 1 to it.
    PNVR_M_CRD_ERR Missed correctable EDC error bit. When set, indicates that an EDC correctable error occurred in fetched memory data and that some other error bit was already set in this register. For this error, the PNV_FADDR register does not contain the address at which the failure occurred. You clear this bit by writing a 1 to it.
    PNVR_M_UCRD_ERR Missed uncorrectable EDC error bit. When set, indicates that an EDC uncorrectable error occurred in fetched memory data and that some other error bit was already set in this register. For this error, the PNV_FADDR register does not contain the address at which the failure occurred. You clear this bit by writing a 1 to it.

    The pnvram_intr interface, discussed in Section 6.11, sets the PNVR_CRD_ERR bit in the PNV_ERROR register to indicate that a CRD error interrupt has occurred during a DMA transfer. This causes the PNVR_ERR_SUM also to be set. [Return to example]

  3. The next four #define statements define the read-only bits of the PNV_MEM_CFG register, as shown in the following table:
    Definition Meaning
    PNV_BANK_MASK Number of banks of SRAMs present (1, 2, or 4)
    PNV_SIZE_MASK SRAM size (256K bit, 1M bit, or 4M bit)
    PNV_MOD_REV_MASK Module revision number
    PNV_CHIP_REV_MASK PCI Integrated NVRAM Controller (PINK) chip revision number

    The pnvram_probe interface, described in Section 6.6.1, reads the PNV_MEM_CFG register and uses the constants PNV_BANK_MASK and PNV_SIZE_MASK in calculating the NVRAM size. It ultimately prints the value of the PNV_MOD_REV_MASK bit mask in the bootstrap informational message. [Return to example]

  4. The next two #define statements, described in the following table, facilitate the pnvram_probe interface's calculation of the NVRAM size, as discussed in Section 6.6.1.
    Definition Meaning
    PNV_SIZE_SHIFT Factor applied to the PNV_SIZE_MASK bit of the PNV_MEM_CFG register to produce the size of an SRAM module
    PNV_512K Size of a bank of SRAM modules

    [Return to example]

  5. The next two #define statements define the bits of the PNV_EDC_CTRL register, as shown in the following table:
    Definition Meaning
    PNVR_DIS_CRD_LOG When set, disables the recording (in the PNV_ERROR and PNV_FADDR registers) of single bit errors detected in the data read from SRAM modules.
    PNVR_DIS_EDC_SERR When set, suppresses the reporting of EDC uncorrectable errors to the PCI system error line. This does not affect the logging of uncorrectable errors to the PNV_ERROR and PNV_FADDR registers.

    The /dev/pnvram device driver uses the default power-on values of the PNV_EDC_CTRL register's bit definitions (that is, single bit errors and EDC uncorrectable errors generate interrupts). It does not directly access this register. [Return to example]

  6. The next five #define statements define the bits of the PNV_BAT_CTRL register, as shown in the following table:
    Definition Meaning
    PNV_BCHRG If this read-only bit is set, the battery is charged; if it is clear, the battery is charging.
    PNV_BDISC When read, indicates, if set, that the battery will be disconnected upon power failure or, if clear, that the battery will be connected upon power failure to back up the NVRAM. The pnvram_battery_disable interface writes the special sequence 1, 1, 0, 0, and 1 to this bit to enable the battery to be disconnected upon power failure.
    PNV_BFAIL When set, this read-only bit indicates that the battery has been charged successfully to at least 2.5 volts. When clear, it indicates that the battery failed to charge before the battery charge timer expired and needs replacement.
    PNV_BAT_OK When set, this read-only bit indicates that the battery's real-time voltage is above 2.5 volts; when clear, indicates that the voltage is below 2.5 volts.
    PNV_SPEED_MASK These two bits are set by system software to indicate one of three PCI bus speeds (33, 16.7, or 8.33 MHz) and are read to adjust the battery charge counter so that its timeout value is the same for each of the three speeds.

    As discussed in Section 6.8.1, Section 6.8.2, and Section 6.8.3, the pnvram_battery_status, pnvram_battery_enable, and pnvram_battery_disable interfaces all access the bits of the PNV_BAT_CTRL register when performing their various tasks. [Return to example]

  7. The next three #define statements define the bits of the PNV_EEPROM_IF register, as shown in the following table:
    Definition Meaning
    PNV_SCL When set, indicates that the EEPROM serial clock input is forced to a high logic level; when clear, indicates that the clock input is forced to a low logic level.
    PNV_XMT_SDA Asserts the EEPROM serial data line to high or low logic levels, enabling software to transfer command, address, and write data to the EEPROM, with regard to the setting of the PNV_SCL bit.
    PNV_RCV_SDA Returns the status of the EEPROM serial data line, enabling software to receive serial read data and EEPROM responses.

    The /dev/pnvram device driver does not access the PNV_EEPROM_IF register or use its bit definitions. [Return to example]

  8. Indicates the maximum transfer size (in bytes) that the PCI bus NVRAM expansion board can handle. As shown in Section 6.6.1, the pnvram_probe interface uses this constant to obtain enough DMA map registers to handle the largest transfer. [Return to example]

  9. The next two #define statements specify command constants that the pnvram_write and pnvram_read interfaces write to the PNV_DMA_CMD register to start a DMA transaction. The following table describes these command constants:
    Definition Meaning
    PNV_NV_READ Starts a DMA transfer from NVRAM memory to host memory.
    PNV_NV_WRITE Starts a DMA transfer from host memory to NVRAM memory.

    [Return to example]

  10. The next six #define statements define the bits of the PNV_INTR_CTRL register, as shown in the following table:
    Definition Meaning
    PNV_ENAB_DMA When set, causes a PCI interrupt to be generated during a DMA transaction when the PNV_BYTE_COUNT register reaches zero; when clear, disables the DMA completion interrupt. The pnvram_probe interface sets this bit to enable DMA completion interrupts.
    PNV_ENAB_CRD When set, causes a PCI interrupt to be generated upon detection of a correctable read data (CRD) error; when clear, disables the CRD detection interrupt. The pnvram_probe interface sets this bit to enable CRD detection interrupts.
    PNV_DISAB_ABRT When clear, causes a DMA transaction to be terminated with an abort interrupt if a hard error occurs; when set, allows a DMA transaction to continue, despite the occurrence of a hard error. The /dev/pnvram device driver leaves this bit clear, thus enabling DMA abort interrupts.
    PNV_DMA_INTR If set when read, indicates that a DMA transaction has completed. Writing a 1 to this bit clears the PCI interrupt if it was caused by DMA completion. The pnvram_intr interface reads the PNV_INTR_CTRL register to determine if the cause of an interrupt was the completion of a DMA transaction. If so, it clears the interrupt by setting the PNV_DMA_INTR bit and, if DMA sleeps are disabled (that is, the PNVRAM_DMA_SLEEP symbol is undefined), clears the dma_in_progress flag in the pnvram_softc structure.
    PNV_CRD_INTR If set when read, indicates that a PCI interrupt was generated because of a CRD error. Writing a 1 to this bit clears the PCI interrupt if it was caused by CRD error detection. The pnvram_intr interface reads the PNV_INTR_CTRL register to determine if the cause of an interrupt was the detection of a CRD error. If so, it clears the interrupt by setting the PNV_CRD_INTR bit.
    PNV_ABRT_INTR If set when read, indicates that a DMA transaction was truncated because of a hard error. Writing a 1 to this bit clears the PCI interrupt if it was caused by an aborted DMA transaction. The pnvram_intr interface reads the PNV_INTR_CTRL register to determine if the cause of an interrupt was the detection of a CRD error. If so, it clears the interrupt by setting the PNV_ABRT_INTR bit.

    [Return to example]

  11. The next five #define statements provide constants that enable the pnvram_probe interface, as discussed in Section 6.6.1, to locate, read, and interpret PCI NVRAM console diagnostic results. The following table defines these constants:
    Definition Meaning
    PNV_CACHE_OFFSET Defines the offset to the first NVRAM location from the start of the PCI slot address. The pnvram_probe interface initializes the cache_offset member of the pnvram_softc data structure with this value.
    PNV_DIAG_RESERVED Indicates the amount of NVRAM address space (1024 bytes) that must be reserved for diagnostics and status information. The /dev/pnvram device driver does not use this constant.
    PNV_DIAG_RESULT Provides an offset from the beginning of NVRAM cache to the lower byte address in the reserved diagnostic region in which the PCI NVRAM module writes its console diagnostic results. The pnvram_probe interface locates the console diagnostic results by subtracting PNV_DIAG_RESULT from the kernel segment address of the NVRAM cache and then reads them into the diag_status member of the pnvram_softc data structure.
    PNV_DIAG_SIZE Defines the size of the PCI NVRAM console diagnostic results as a longword (4 bytes). The pnvram_probe interface uses this constant when reading the console diagnostic results.
    BOARD_FAILED Indicates the bit in the console diagnostics longword that is set when the PCI NVRAM board fails its diagnostics. The pnvram_probe interface compares the value it reads into the diag_status member of the pnvram_softc data structure with this bit mask to determine if the diagnostics failed.

    [Return to example]

  12. The next four #define statements provide constants that the /dev/pnvram device driver uses to communicate with the /dev/presto device driver.

    Note

    These constants are not relevant for the PCI bus NVRAM expansion board, but are used to emulate testing of the /dev/presto device.

    The following table describes each constant:
    Definition Meaning
    PNVRAM_MAPPED

    Indicates that the PNVRAM buffer is mapped. The /dev/pnvram device driver does not currently use this constant.

    PNVRAM_NOTMAPPED

    Indicates that the PNVRAM buffer is unmapped. The pnvram_attach interface uses this constant to indicate to the /dev/presto device driver that the buffer is not mapped.

    PNVRAM_CACHED Indicates to the /dev/presto device driver the use of kernel segment (kseg) space. The pnvram_attach interface passes this constant to the /dev/presto driver.
    PNVRAM_NOTCACHED Indicates to the /dev/presto device driver the use of a cached space. The /dev/pnvram device driver does not currently use this constant.

    [Return to example]

  13. Defines the softc data structure for the /dev/pnvram device driver and calls it pnvram_softc. Most of the sections of the /dev/pnvram device driver declare a pointer to the pnvram_softc structure.

    The following list describes the members contained in the pnvram_softc data structure:

    [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.3    Defining Data Structures

You can allocate the softc and controller structure arrays for the driver by using static or dynamic allocation techniques. Static allocation is usually used when the maximum number of structures is small and the user cannot change that number. Dynamic allocation is usually used when the maximum number of structures is large or the user can change that number. The NVRAM driver defines data structures as follows, where the NPNVRAM constant is defined by the driver to allocate an array of six structures:

#define NPNVRAM 6 [1]

.
.
.
#include <io/dec/pci/pnvram.h> [2]

 
struct pnvram_softc *pnvram_softc[NPNVRAM]; [3] struct controller *pnvram_info[NPNVRAM]; [4]

  1. Defines the NPNVRAM constant, which represents the number of PCI bus NVRAM expansion boards on the system. [Return to example]

  2. Includes the pnvram.h header file described in Section 6.2. This file defines the following items:

    [Return to example]

  3. Defines an array of pnvram_softc data structure pointers. The size of the array is determined by the NPNVRAM constant. [Return to example]

  4. Defines an array of controller data structure pointers. The size of the array is determined by the NPNVRAM constant. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.4    Include Files Section

The following code shows the Include Files Section for the /dev/pnvram device driver. Writing Device Drivers: Reference provides reference page descriptions of the header files most commonly used by Digital UNIX device drivers.

#include <sys/malloc.h>  [1]
#include <sys/presto.h>  [2]
#include <io/common/devdriver.h>  [3]
#include <io/dec/pci/pci.h>  [4]
#include <io/dec/pci/pnvram.h>  [5]

  1. Includes the header file that defines the MALLOC and FREE interfaces the pnvram_probe interfaces uses to allocate memory for the softc structure, and to deallocate it in the event of an error. [Return to example]

  2. Includes the header file that contains the definitions and structure definitions for the presto_interface data structure and the presto_init interface by which the /dev/pnvram device driver communicates its entry points to the /dev/presto device driver. As shown in Section 6.6.2, the pnvram_attach interface performs these tasks. [Return to example]

  3. Defines structures, constants, data types, and external interfaces that device drivers and the autoconfiguration software use. For example, this header file defines the io_handle_t and dma_handle_t data types used throughout the /dev/pnvram device driver. [Return to example]

  4. Contains data structures referenced by PCI bus device drivers and by the PCI bus configuration code. For a summary description of this header file, see Section 6.2. [Return to example]

  5. Includes the pnvram.h header file described in Section 6.2. This file defines the following items:

    [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.5    Declarations Section

The following code shows the Declarations Section for the /dev/pnvram device driver:

extern struct pnvram_softc *pnvram_softc[];  [1]
extern struct controller *pnvram_info[];
extern int presto_init();
extern struct presto_interface presto_interface[];
extern struct nvram_battery_info nvram_batteries[];
extern void bzero();
extern int hz;

 
#define PNV_READIO(a) \ [2] READ_BUS_D32((io_handle_t)(sc->csr_base | a))
 
#define PNV_WRITEIO(a,d) \ [3] WRITE_BUS_D32((io_handle_t)(sc->csr_base | a),(long)d)
 
#define PNV_READ_CNFG(a) READ_BUS_D16((io_handle_t) \ [4] (((struct pci_config_hdr *)(sc->ctlr->private[0]))->config_base | a))
 
#define PNV_WRITE_CNFG(a,d) WRITE_BUS_D16((io_handle_t) \ [5] (((struct pci_config_hdr *)(sc->ctlr->private[0]))->config_base | a), (long)d)
 
int pnvram_probe(), pnvram_attach(); [6] int pnvram_battery_enable(), pnvram_battery_disable(); int pnvram_status(); int pnvram_battery_status(); void pnvram_zero(); void pnvram_write(); void pnvram_read(); u_long pnvram_ssn(); int pnvram_intr();
 
/* #define PNVRAM_DMA_INTR 1 */ #define PNVRAM_DMA_SLEEP 1 [7]
 
#ifdef PNVRAM_DMA_INTR static int dma_delay = 10000000; [8] #else static int poll_delay = 1000000; #endif /* DMA_INTR */
 
caddr_t pnvramstd[] = { 0 }; [9]
 
struct driver pnvramdriver = { [10] pnvram_probe, 0, pnvram_attach, 0, 0, 0, 0, 0, "pnvram", pnvram_info};
 
/* #define DEP_DEBUG */ #ifdef DEP_DEBUG [11] #define DEP(x) printf("**** PNVRAM **** "); printf x #else #define DEP(x) #endif

  1. Lists the driver's external declarations. [Return to example]

  2. To facilitate reads from any of the 32-bit PCI NVRAM device registers in PCI memory space, constructs the PNV_READIO interface from the READ_BUS_D32 macro defined in /usr/sys/include/io/common/devdriver.h.

    The call to the READ_BUS_D32 macro results in a call to the read_io_port interface, a generic interface that maps to a bus- and machine-specific interface that actually performs the task of reading the longword from a device register. Use of this interface to read data from a device register makes the device driver more portable across different buses, different CPU architectures, and different CPU types within the same architecture.

    The read_io_port interface takes three arguments:

    [Return to example]

  3. To facilitate writes to any of the 32-bit PCI NVRAM device registers in PCI memory space, constructs the PNV_WRITEIO interface from the WRITE_BUS_D32 macro defined in /usr/sys/include/io/common/devdriver.h. The call to the WRITE_BUS_D32 macro results in a call to the write_io_port interface, a generic interface that maps to a bus- and machine-specific interface that actually performs the task of writing the longword to a device register. Use of this interface to write data to a device register makes the device driver more portable across different buses, different CPU architectures, and different CPU types within the same architecture.

    The write_io_port interface takes four arguments:

    [Return to example]

  4. To facilitate reads from the PCI NVRAM device's 16-bit command and status registers in PCI bus configuration space, constructs the PNV_READ_CNFG interface from the READ_BUS_D16 macro defined in /usr/sys/include/io/common/devdriver.h.

    The call to the READ_BUS_D16 macro results in a call to the read_io_port interface, a generic interface that maps to a bus- and machine-specific interface that actually performs the task of reading the word from a device register. Use of this interface to read data from a device register makes the device driver more portable across different buses, different CPU architectures, and different CPU types within the same architecture.

    The read_io_port interface takes three arguments:

    [Return to example]

  5. To facilitate writes to the PCI NVRAM device's 16-bit command and status registers in PCI bus configuration space, constructs the PNV_WRITE_CNFG interface from the WRITE_BUS_D16 macro defined in /usr/sys/include/io/common/devdriver.h. The call to the WRITE_BUS_D16 macro results in a call to the write_io_port interface, a generic interface that maps to a bus- and machine-specific interface that actually performs the task of writing the word to a device register. Use of this interface to write data to a device register makes the device driver more portable across different buses, different CPU architectures, and different CPU types within the same architecture.

    The write_io_port interface takes four arguments:

    [Return to example]

  6. Provides the forward declarations for the entry points of the /dev/pnvram device driver. [Return to example]

  7. Defines the symbol PNVRAM_DMA_INTR. The /dev/pnvram device driver is designed so it can either enable or disable interrupts at the completion of a DMA transaction (based on its setting of the PNV_ENAB_DMA bit of the PNV_INTR_CTRL device register). When the PNVRAM_DMA_INTR symbol is defined, the symbol PNVRAM_DMA_SLEEP can also be defined. This allows the /dev/pnvram device driver three different modes of determining DMA request completion:

    [Return to example]

  8. Defines variables the pnvram_read and pnvram_write interfaces use, when DMA interrupts are disabled, to determine how long they wait for the completion of a DMA operation. See Section 6.9.1 and Section 6.9.2 for more information. [Return to example]

  9. Declares the pnvramstd array. The /dev/pnvram driver does not use this array.

    [Return to example]

  10. Defines the driver structure for the /dev/pnvram driver. The value zero (0) indicates that the /dev/pnvram driver does not make use of a specific member of the pnvramdriver structure.

    The following list describes those members initialized to a nonzero value:

    [Return to example]

  11. Determines if the symbol DEP_DEBUG is defined. If so, the driver defines the DEP interface. When debugging is enabled (that is, the DEP_DEBUG symbol is defined), the driver interfaces call the DEP interface to print informational messages to the terminal, each preceded by the string **** PNVRAM ****. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.6    Autoconfiguration Support Section

Table 6-2 lists the three interfaces implemented as part of the Autoconfiguration Support Section for the /dev/pnvram device driver along with the sections in the book where each is described.

Table 6-2: Autoconfiguration Support Section

Interface Section
Implementing the pnvram_probe Interface Section 6.6.1
Implementing the pnvram_attach Interface Section 6.6.2
Implementing the pnvram_ssn Interface Section 6.6.3
Single binary configuration support See Writing Device Drivers: Tutorial


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.6.1    Implementing the pnvram_probe Interface

The pnvram_probe interface is called by the PCI bus code to perform the following tasks:

The following code implements pnvram_probe:

int
pnvram_probe(struct pci_config_hdr *pci_cfg_hdr, struct controller *ctlr) [1]
{
   register struct      pnvram_softc *sc;  [2]
   unsigned int         mem_cfg;  [3]
   int                  unit = ctlr->ctlr_num;  [4]
#ifdef PNVRAM_DMA_INTR
   struct               handler_intr_info pnvram_intr_info;  [5]
   ihandler_t           pnvram_ihandle;  [6]
#endif
   volatile unsigned int         synch_data;  [7]

 

 
if (!(pci_cfg_hdr->command & CMD_MEM_SPACE) || \ [8] !(pci_cfg_hdr->command & CMD_BUS_MASTER) ) { printf("pnvram%d at pci%d not configured by PCI POST code.\n", unit, ctlr->bus_num); return(0); }
 
MALLOC(sc, struct pnvram_softc*, sizeof(struct pnvram_softc), \ [9] M_DEVBUF, M_NOWAIT); if (sc == (struct pnvram_softc *)(NULL)) { return(0); } else { bzero((char *)sc, sizeof(struct pnvram_softc)); pnvram_softc[unit] = sc; }
 
if (dma_map_alloc(PNV_MAX_XFER_SIZE, ctlr, &sc->dma_p, \ DMA_CONTIG) == 0) { [10] FREE(sc, M_DEVBUF); return(0); }
 
sc->ctlr = ctlr; [11]
 
ctlr->private[0] = pci_cfg_hdr; [12]
 
sc->csr_base = pci_cfg_hdr->bar1; [13] DEP(("pnvram%d pci_cfg_hdr->bar1:0x%lx\n", unit,pci_cfg_hdr->bar1));
 
sc->cache_offset = PNV_CACHE_OFFSET; [14]
 
mem_cfg = PNV_READIO(PNV_MEM_CFG); [15] DEP(("pnvram%d mem_cfg: 0x%x\n", unit,mem_cfg));
 
sc->cache_size = ((PNV_512K << (mem_cfg & PNV_BANK_MASK)) [16] << ((mem_cfg & PNV_SIZE_MASK) >> PNV_SIZE_SHIFT)); DEP(("pnvram%d sc->cache_size: 0x%x\n", unit,sc->cache_size));
 
sc->chip_rev = PNV_READIO(PNV_MEM_CFG) & PNV_CHIP_REV_MASK; [17] DEP(("pnvram%d chip_rev: 0x%x\n", unit, sc->chip_rev));
 
sc->cache_base = iohandle_to_phys(pci_cfg_hdr->bar0, [18] HANDLE_DENSE_SPACE); DEP(("pnvram%d cache_base:0x%lx\n", unit,sc->cache_base));
 
sc->cache_phys_start = sc->cache_base + sc->cache_offset; [19] DEP(("pnvram%d cache_phys_start:0x%lx\n", unit,sc->cache_phys_start));
 
sc->cache_kseg_start = PHYS_TO_KSEG(sc->cache_phys_start); [20] DEP(("pnvram%d cache_kseg_start:0x%lx\n", unit, sc->cache_kseg_start));
 
sc->cache_size = sc->cache_size - PNV_CACHE_OFFSET; [21]
 
pnvram_read((sc->cache_kseg_start - PNV_DIAG_RESULT), &sc->diag_status, PNV_DIAG_SIZE); [22] DEP(("pnvram%d Diag Register: 0x%x\n", unit,sc->diag_status));
 
if (sc->diag_status & BOARD_FAILED) { [23] printf("pnvram%d diag reg: 0x%x\n", unit, sc->diag_status); sc->diag_status = 0; } else { DEP(("pnvram%d diag reg 0x%x\n", unit, sc->diag_status)); sc->diag_status = 1; }
 
PNV_WRITEIO(PNV_DMA_CMD, 0); [24] PNV_WRITEIO(PNV_INTR_CTRL, (PNV_DMA_INTR | PNV_CRD_INTR | PNV_ABRT_INTR)); mb(); PNV_WRITEIO(PNV_ERROR, (PNVR_CMD_PAR | PNVR_WR_PAR | PNVR_CRD_ERR | PNVR_UCRD_ERR | PNVR_M_CMD_ERR | PNVR_M_WR_PAR | PNVR_M_CRD_ERR | PNVR_M_UCRD_ERR)); mb(); synch_data = PNV_READIO(PNV_ERROR);
 
#ifdef PNVRAM_DMA_INTR [25] pnvram_intr_info.configuration_st = (caddr_t)ctlr; pnvram_intr_info.intr = pnvram_intr; pnvram_intr_info.param = (caddr_t)unit; pnvram_intr_info.config_type = (CONTROLLER_CONFIG_TYPE | SHARED_INTR_CAPABLE);
 
pnvram_ihandle.ih_bus = ctlr->bus_hd; pnvram_ihandle.ih_bus_info = (char *)&pnvram_intr_info;
 
sc->hid = handler_add(&pnvram_ihandle); if (sc->hid == (ihandler_id_t *)(NULL)) { printf("pnvram%d: interrupt handler_add failed\n", unit); dma_map_dealloc(sc->dma_p); FREE(sc, M_DEVBUF); return(0); } else { handler_enable(sc->hid); PNV_WRITEIO(PNV_INTR_CTRL, (PNV_ENAB_DMA | PNV_ENAB_CRD)); mb(); synch_data = PNV_READIO(PNV_INTR_CTRL); } #endif /* PNVRAM_DMA_INTR */
 
printf("pnvram%d: Module Revision %d, Cache size: %d\n", [26] unit, (mem_cfg & PNV_MOD_REV_MASK), sc->cache_size);
 
return(1); [27]
 
}

  1. Specifies the pci_config_hdr and controller data structures that the PCI bus configuration code passes as input to this interface during device autoconfiguration. [Return to example]

  2. Declares a pointer to a pnvram_softc data structure and calls it sc. The pnvram_softc data structure allows the /dev/pnvram device driver's associated interfaces to share data. This data structure is defined in the /usr/sys/include/io/dec/pci/pnvram.h file. Section 6.2 describes the contents of this file, including the members of pnvram_softc. [Return to example]

  3. Declares the mem_cfg variable to hold the PCI NVRAM's PNV_MEM_CFG register value. [Return to example]

  4. Declares the unit variable and initializes it with the contents of the ctrl_num member of the controller data structure identified by the ctrl pointer. [Return to example]

  5. If DMA interrupts are enabled (that is, the PNVRAM_DMA_INTR symbol is set), declares a handler_intr_info data structure that the pnvram_probe interface uses to register and enable the driver's interrupt service interface. [Return to example]

  6. If DMA interrupts are enabled (that is, the PNVRAM_DMA_INTR symbol is set), declares an ihandler_t data structure. The pnvram_probe interface passes this structure to the handler_add interface to register the driver's interrupt service interface. [Return to example]

  7. Declares the synch_data variable, which is used as the target of special calls to the PNV_READIO interface throughout the pnvram_probe interface. Used in conjunction with mb instructions, these special reads cause pending writes to the PCI NVRAM device registers to be completed.

    The synch_data variable is declared as volatile to ensure that the compiler does not, as an optimization, remove the register read operation because the read data is not used. [Return to example]

  8. Ensures that the CMD_MEM_SPACE and CMD_BUS_MASTER bits are set in the Command register (as copied to the command member of the pci_cfg_hdr data structure by the PCI bus configuration code).

    If these bits are not set, the PCI power-on self-test (POST) code was unable to configure this PCI NVRAM device into the system. As a result, the base address values stored in the bar0 and bar1 members of the pci_cfg_hdr data structure are invalid and cannot be used to access device registers. In this case, the pnvram_probe interface prints a failure message identifying the failed unit and PCI bus number, and returns failure status (0) to the PCI bus configuration code. [Return to example]

  9. Allocates memory for the pnvram_softc data structure by calling the MALLOC interface. MALLOC returns the address of the allocated pnvram_softc structure or zero in the sc variable. If MALLOC returns zero, the pnvram_probe interface returns failure status (0) to the PCI bus configuration code. Otherwise, it calls the bzero interface to initialize the pnvram_softc structure with zeros.

    The bzero interface takes two arguments:

    The pnvram_probe interface then initializes the element that corresponds to this PCI NVRAM expansion board unit in the array of pnvram_softc data structures (see Section 6.3). [Return to example]

  10. Attempts to allocate resources for DMA data transfers by calling the dma_map_alloc interface. The dma_map_alloc interface is a generic interface that maps to a bus- and machine-specific interface that actually performs the allocation of system resources associated with DMA data transfers. Using this interface in DMA read and write operations makes the device driver more portable across different bus and CPU architectures.

    The dma_map_alloc interface takes four arguments:

    If dma_map_alloc cannot allocate the requested DMA resources, pnvram_probe calls the FREE interface to deallocate the memory previously allocated for the pnvram_softc data structure, and returns failure status (0) to the PCI bus configuration code. [Return to example]

  11. Initializes the ctlr member of the pnvram_softc data structure identified by the sc pointer to the ctlr pointer associated with this PCI bus NVRAM expansion board. This assignment allows the /dev/pnvram driver to access the members of this controller structure through this PCI bus NVRAM expansion board's pnvram_softc data structure. [Return to example]

  12. Initializes the private member of the controller data structure identified by the ctrl pointer to the pci_cfg_hdr pointer. This pointer refers to the pci_config_hdr structure passed to the pnvram_probe interface by the PCI bus configuration code. [Return to example]

  13. Obtains the I/O handle to the controller's CSR base address by reading the bar1 member of the pci_config_hdr structure and places it in the csr_base member of the pnvram_softc structure identified by the sc pointer. Section 6.3 shows how the /dev/pnvram driver uses csr_base to construct the PNV_READIO and PNV_WRITEIO interfaces used to access the device registers.

    If debug mode is enabled (that is, the DEP_DEBUG symbol is defined), pnvram_probe echoes the completion of this instruction on the terminal. [Return to example]

  14. Initializes the cache_offset member of the pnvram_softc structure associated with this PCI bus NVRAM expansion board. Section 6.2 shows that the PNV_CACHE_OFFSET device register is defined in the pnvram.h file. [Return to example]

  15. Reads the memory configuration data from the PNV_MEM_CFG device register to the mem_cfg variable. If debug mode is enabled (that is, the DEP_DEBUG symbol is defined), pnvram_probe echoes the completion of this instruction on the terminal. [Return to example]

  16. Manipulates the data returned to the mem_cfg variable to calculate the NVRAM cache size from the SRAM size and number of banks. The constant PNV_512K, and the bit masks PNV_BANK_MASK, PNV_SIZE_MASK, and PNV_SIZE_SHIFT. These symbols are all defined in pnvram.h, as discussed in Section 6.2. It initializes the cache_size member of the pnvram_softc structure with the result of these calculations.

    If debug mode is enabled (that is, the DEP_DEBUG symbol is defined), pnvram_probe echoes the completion of this instruction on the terminal. [Return to example]

  17. Initializes the chip_rev member of the pnvram_softc structure with the PCI Integrated NVRAM Controller (PINK) chip revision number. [Return to example]

  18. Initializes the cache_base member of the pnvram_softc data structure with the system physical address of the base of the NVRAM buffer. To do so, pnvram_probe calls the iohandle_to_phys interface to convert the I/O handle to the PCI dense-space address of the buffer (stored in the bar0 member of the pci_config_hdr data structure the PCI bus configuration code passes into the pnvram_probe interface) to a system physical address. It specifies the flag HANDLE_DENSE_SPACE to identify the type of I/O handle the iohandle_to_phys interface should return to the driver.

    If debug mode is enabled (that is, the DEP_DEBUG symbol is defined), pnvram_probe echoes the completion of this instruction on the terminal. [Return to example]

  19. Initializes the cache_phys_start member of the pnvram_softc structure with the system physical address at which the /dev/presto driver will start using the NVRAM cache. The sum of the cache_base and cache_offset members of the pnvram_softc structure provide this address.

    If debug mode is enabled (that is, the DEP_DEBUG symbol is defined), pnvram_probe echoes the completion of this instruction on the terminal. [Return to example]

  20. Initializes the cache_kseg_start member of the pnvram_softc structure with the kernel segment (kseg) address at which the /dev/presto driver will start using the NVRAM cache. To do so, the pnvram_probe interface passes the cache_phys_start member of the pnvram_softc structure to the PHYS_TO_KSEG interface.

    If debug mode is enabled (that is, the DEP_DEBUG symbol is defined), pnvram_probe echoes the completion of this instruction on the terminal. [Return to example]

  21. Adjusts the NVRAM cache size stored in the cache_size member of the pnvram_softc structure to account for the reserved diagnostic region. To do so, it subtracts the constant PNV_CACHE_OFFSET from the current value of the cache_size member. [Return to example]

  22. Locates and reads the PCI NVRAM console diagnostic results into the diag_status member of the pnvram_softc structure. The console diagnostic results are a longword in size (4 bytes as indicated by the PNV_DIAG_SIZE constant) and are located 8 bytes (the value of the PNV_DIAG_RESULT constant) lower in memory than the kseg address of the NVRAM cache (stored in the cache_kseg_start member of the pnvram_softc structure.

    If debug mode is enabled (that is, the DEP_DEBUG symbol is defined), pnvram_probe echoes the completion of this instruction on the terminal. [Return to example]

  23. Compares the contents of the diag_status member of the pnvram_softc structure against the constant BOARD_FAILED to determine if the console diagnostics failed. If so, pnvram_probe prints the PCI NVRAM unit number and diagnostics results on the terminal, resets the diag_status member to zero (0), and continues. Otherwise, it resets the diag_status member to 1 and, if debug mode is enabled (that is, the DEP_DEBUG symbol is defined), prints the diagnostic results on the terminal. [Return to example]

  24. Initializes the PNV_DMA_CMD, PNV_INTR_CTRL, and PNV_ERROR device registers to 0 by issuing a series of calls to the PNV_WRITEIO interface. It forces the ordering of these writes by interleaving them with mb instructions and issuing a PNV_READIO call to read the PNV_ERROR register. This causes the delivery of any pending writes to the PCI NVRAM device registers. [Return to example]

  25. If DMA interrupts are enabled (that is, the DMA_INTR symbol is defined), performs the following steps to register and enable the interrupt service interface:

    Note

    As mentioned in Section 6.2, DMA abort interrupts are enabled at initialization (that is, the PNV_DISAB_ABRT bit in the PNV_INTR_CTRL register is clear).

    [Return to example]

  26. Prints a bootstrap informational message to the terminal, identifying the PCI NVRAM device unit, its module revision number, and its cache size. [Return to example]

  27. Returns success status (1) to the PCI bus configuration code. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.6.2    Implementing the pnvram_attach Interface

The pnvram_attach interface is called by the PCI bus configuration code at system startup and performs the following tasks:

The pnvram_attach interface has the following implicit input:


.
.
.
ctlr->ctrl_num
.
.
.

This member is used to index into the arrray of softc addresses to obtain device unit-specific information.

The following code implements pnvram_attach:

int
pnvram_attach(struct controller *ctlr)  [1]
{
   register int    unit = ctlr->ctlr_num;  [2]
   register struct pnvram_softc *sc = pnvram_softc[unit];  [3]

 
presto_interface[unit].nvram_status = pnvram_status; [4] presto_interface[unit].nvram_battery_status= pnvram_battery_status; presto_interface[unit].nvram_battery_disable= pnvram_battery_disable; presto_interface[unit].nvram_battery_enable= pnvram_battery_enable; presto_interface[unit].nvram_ioreg_read = pnvram_read; presto_interface[unit].nvram_ioreg_write = pnvram_write; presto_interface[unit].nvram_block_read = pnvram_read; presto_interface[unit].nvram_block_write = pnvram_write; presto_interface[unit].nvram_ioreg_zero = pnvram_zero; presto_interface[unit].nvram_block_zero = pnvram_zero;
 
presto_interface[unit].nvram_min_ioreg = sizeof(int); [5] presto_interface[unit].nvram_ioreg_align = sizeof(int); presto_interface[unit].nvram_min_block = PRFSIZE; presto_interface[unit].nvram_block_align = PRFSIZE;
 
presto_init(sc->cache_kseg_start, sc->cache_size, [6] PNVRAM_NOTMAPPED, PNVRAM_CACHED, pnvram_ssn(unit), unit); }

  1. Specifies the controller data structure that identifies the controller for this PCI bus NVRAM expansion board. The PCI bus configuration code passes the address of this structure as input to the pnvram_attach interface during device autoconfiguration. [Return to example]

  2. Declares the unit variable and initializes it with the contents of the ctrl_num member of the controller data structure identified by the ctrl pointer. [Return to example]

  3. Declares a pointer to a pnvram_softc data structure and calls it sc. The pnvram_softc data structure allows the /dev/pnvram device driver's associated interfaces to share data. This data structure is defined in the pnvram.h file. Section 6.2 describes the contents of this file, including the members of pnvram_softc.

    The pnvram_attach interface initializes this pointer to the address of the element that corresponds to this PCI bus NVRAM expansion board unit in the array of pnvram_softc data structures (see Section 6.3). [Return to example]

  4. Initializes the members of the presto_interface element corresponding with this PCI bus NVRAM expansion board unit in the array of presto_interface data structures defined in the file /usr/sys/include/sys/presto.h. The members of the presto_interface structure allow the /dev/presto device to access the driver interfaces that control access to this PCI bus NVRAM expansion board. These interfaces include pnvram_status, pnvram_battery_status, pnvram_battery_disable, pnvram_battery_enable, pnvram_read, pnvram_write, and pnvram_zero.

    Section 6.7, Section 6.8.1, Section 6.8.2, Section 6.8.3 Section 6.9.1, Section 6.9.2, and Section 6.10 discuss these interfaces.

    The /usr/sys/include/sys/presto.h file provides additional information on the members of the presto_interface structure. [Return to example]

  5. In the next four lines, pnvram_attach sets the minimum size of a small and large I/O device register data block. It also sets the byte alignment restriction for an I/O device register block. The PRFSIZE constant is defined in /usr/sys/include/sys/presto.h as the largest buffer size (in bytes) handled by the /dev/presto driver. See the /usr/sys/include/sys/presto.h file for more information on these members of the presto_interface structure. [Return to example]

  6. Calls presto_init to perform initialization tasks for the /dev/presto driver.

    The presto_init interface takes six arguments:

    Section 6.6.3 describes the pnvram_ssn interface. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.6.3    Implementing the pnvram_ssn Interface

The pnvram_attach interface passes pnvram_ssn as an argument to presto_init. The presto_init interface calls pnvram_ssn to obtain the machine (CPU) ID.

The pnvram_ssn interface generates a unique system-level and unit-level ID for this PCI bus NVRAM expansion board unit and returns it to its caller, the pnvram_attach interface. The following code implements pnvram_ssn:

u_long
pnvram_ssn(int unit)  [1]
{
   u_int info_rtn;  [2]
   struct item_list item_list;  [3]
   u_long ssn;  [4]

 
item_list.function = GET_SYSID; [5]
 
info_rtn = get_info(&item_list); [6]
 
if (info_rtn == TRUE) { [7] if (item_list.rtn_status == INFO_RETURNED) ssn = item_list.output_data | ((u_long)unit << 32); return(ssn); } else { printf("No System Serial Number for pnvram%d\n", unit); return((u_long)unit << 32); } }

  1. Declares the variable unit that is passed to the pnvram_ssn interface by its caller, the pnvram_attach interface. [Return to example]

  2. Declares the variable info_rtn to hold the return status from the call to the get_info interface. [Return to example]

  3. Declares an item_list structure to be used in the call to the get_info interface. [Return to example]

  4. Declares the variable ssn in which pnvram_ssn returns a unique machine ID for this PCI bus NVRAM expansion board unit to presto_attach. [Return to example]

  5. Initializes the function member of the item_list structure to the constant GET_SYSID. [Return to example]

  6. Calls the get_info interface, passing to it the address of the item_list structure. [Return to example]

  7. If the get_info interface returns success status and the rtn_status member of the item_list structure contains the constant value INFO_RETURNED, the pnvram_ssn interface computes the machine ID for this PCI bus NVRAM expansion board unit. It calculates this value by left-shifting the expansion board's unit number by 32 bits and performing a logical OR of the result with the system serial number (ssn) the get_info call returned to the output_data member of the item_list structure. It then returns this ssn value to the pnvram_attach interface.

    If the get_info interface returns success status but the rtn_status member of the item_list structure does not contain the constant value INFO_RETURNED, or if the get_info interface returns failure status, the pnvram_ssn interface prints a message on the terminal indicating that no system serial number was found for this PCI bus NVRAM expansion board unit. In this case, it returns to the pnvram_attach interface the expansion board's unit number left-shifted by 32 bits. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.7    Status Section

The pnvram_status interface provides the /dev/presto device driver with the status of diagnostics run on the NVRAM. Section 6.6.1 shows that the pnvram_probe interface sets the diag_status member to indicate whether the PCI bus NVRAM expansion board passed software diagnostic tests. Section 6.6.2 shows that the pnvram_attach interface sets the nvram_status member of the presto_interface structure to pnvram_status. This is the mechanism the /dev/presto device driver uses to call the interface that returns the diagnostic status of the NVRAM.

The following code implements pnvram_status:

int
pnvram_status(int unit)  [1]
{
   register struct pnvram_softc *sc = pnvram_softc[unit];  [2]

 
if (sc->diag_status) [3] return(NVRAM_RDONLY); else return(NVRAM_BAD); }

  1. Declares the variable unit that is passed to the pnvram_status interface by its caller, the /dev/presto device driver. [Return to example]

  2. Declares a pointer to a pnvram_softc data structure and calls it sc. The pnvram_softc data structure allows the /dev/pnvram device driver's associated interfaces to share data. This data structure is defined in the pnvram.h file. Section 6.2 describes the contents of this file, including the members of pnvram_softc.

    The pnvram_status interface initializes this pointer to the address of the element that corresponds to this PCI bus NVRAM expansion board unit in the array of pnvram_softc data structures (see Section 6.3). [Return to example]

  3. Checks the diag_status member of the pnvram_softc structure identified by the sc pointer to determine whether this PCI bus NVRAM expansion board passed the software diagnostic tests.

    If the board passed these tests, pnvram_status returns the constant NVRAM_RDONLY to the /dev/presto device driver. If the board failed these tests, pnvram_status returns the constant NVRAM_BAD to the /dev/presto device driver. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.8    Battery Status Section

Table 6-3 lists the three interfaces implemented as part of the Battery Status Section for the /dev/pnvram device driver along with the sections in the book where each is described.

Table 6-3: Interfaces Implemented as Part of the Battery Status Section for the /dev/pnvram Device Driver

Part Section
Implementing the pnvram_battery_status Interface Section 6.8.1
Implementing the pnvram_battery_enable Interface Section 6.8.2
Implementing the pnvram_battery_disable Interface Section 6.8.3


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.8.1    Implementing the pnvram_battery_status Interface

The pnvram_battery_status interface provides the /dev/presto device driver with the status of the battery on the NVRAM. Specifically, pnvram_battery_status performs the following tasks:

Section 6.6.2 shows that the pnvram_attach interface sets the nvram_battery_status member of the presto_interface data structure to pnvram_battery_status. This is the mechanism the /dev/presto device driver uses to call the interface that returns the status of the battery for the NVRAM.

The following code implements pnvram_battery_status:

int
pnvram_battery_status(int unit)  [1]
{
   register struct pnvram_softc *sc = pnvram_softc[unit];  [2]
   int tmp_reg;  [3]

 
nvram_batteries[unit].nv_nbatteries = 1; [4] nvram_batteries[unit].nv_minimum_ok = 1; nvram_batteries[unit].nv_primary_mandatory = 1; nvram_batteries[unit].nv_test_retries = 1;
 
tmp_reg = PNV_READIO(PNV_BAT_CTRL); [5] DEP(("pnvram%d battery_status: csr:0x%x\n", unit, tmp_reg));
 
if ((tmp_reg & PNV_BAT_OK) && (tmp_reg & PNV_BCHRG)) { [6] nvram_batteries[unit].nv_status[0] = BATT_OK; DEP(("Status is BATT_OK\n")); return(0); } else { DEP(("Status is BAD\n")); return(1); } }

  1. Declares the variable unit that is passed to the pnvram_battery_status interface by its caller, the /dev/presto device driver. [Return to example]

  2. Declares a pointer to a pnvram_softc data structure and calls it sc. The pnvram_softc data structure allows the /dev/pnvram device driver's associated interfaces to share data. This data structure is defined in the pnvram.h file. Section 6.2 describes the contents of this file, including the members of pnvram_softc.

    The pnvram_battery_status interface initializes this pointer to the address of the element that corresponds to this PCI bus NVRAM expansion board unit in the array of pnvram_softc data structures (see Section 6.3). [Return to example]

  3. Declares the variable tmp_reg to receive data from the PNV_BAT_CTRL register. [Return to example]

  4. Initializes the members of the nvram_batteries structure for this PCI bus NVRAM expansion board unit in the array of nvram_batteries data structures defined in the /usr/sys/include/sys/presto.h file.

    The following list briefly describes these members:

    [Return to example]

  5. Determines if the PCI bus NVRAM expansion board's battery is operational by using the PNV_READIO interface (discussed in Section 6.5) to read the contents of the PNV_BAT_CTRL register into the tmp_reg variable.

    If debug mode is enabled (that is, the DEP_DEBUG symbol is defined), pnvram_battery_status writes the contents of the register to the terminal. [Return to example]

  6. If the battery is charged (PNV_BCHRG set) and its voltage exceeds 2.5 volts (PNV_BAT_OK set), sets the nv_status member of the nvram_batteries structure to the constant BATT_OK, and returns success status (0) to the /dev/presto driver. Otherwise, pnvram_battery_status does not set the nv_status member and returns failure status (1) to the /dev/presto driver. (The nv_status member is defined in /usr/sys/include/sys/presto.h as an array of size BATTCNT.)

    In either case, if debug mode is enabled (that is, the DEP_DEBUG symbol is defined), pnvram_battery_status reports the battery status to the terminal. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.8.2    Implementing the pnvram_battery_enable Interface

The pnvram_battery_enable interface is called by the /dev/presto device driver and performs the following tasks:

Section 6.6.2 shows that the pnvram_attach interface sets the nvram_battery_enable member of the presto_interface data structure to pnvram_battery_enable. This is the mechanism the /dev/presto device driver uses to call the interface that enables the battery on the PCI bus NVRAM expansion board.

The following code implements pnvram_battery_enable:

int
pnvram_battery_enable(int unit)  [1]
{
   register struct pnvram_softc *sc = pnvram_softc[unit];  [2]
   unsigned int tmp_reg;  [3]

 
tmp_reg = PNV_READIO(PNV_BAT_CTRL); [4] DEP(("pnvram%d battery_enable() BAT_CTRL:0x%x\n", unit, tmp_reg));
 
if (tmp_reg & PNV_BDISC) { [5] PNV_WRITEIO(PNV_BAT_CTRL, (tmp_reg & ~PNV_BDISC)); mb(); tmp_reg = PNV_READIO(PNV_BAT_CTRL); }
 
if (tmp_reg & PNV_BDISC) [6] return(1); else [7] return(0); }

  1. Declares the variable unit that is passed to the pnvram_battery_enable interface by its caller, the /dev/presto device driver. [Return to example]

  2. Declares a pointer to a pnvram_softc data structure and calls it sc. The pnvram_softc data structure allows the /dev/pnvram device driver's associated interfaces to share data. This data structure is defined in the pnvram.h file. Section 6.2 describes the contents of this file, including the members of pnvram_softc.

    The pnvram_battery_enable interface initializes this pointer to the address of the element that corresponds to this PCI bus NVRAM expansion board unit in the array of pnvram_softc data structures (see Section 6.3). [Return to example]

  3. Declares the variable tmp_reg as a temporary register in which it receives the current contents of the PNV_BAT_CTRL register. [Return to example]

  4. Uses the PNV_READIO interface (discussed in Section 6.5) to read the contents of the PNV_BAT_CTRL register into the tmp_reg variable.

    If debug mode is enabled (that is, the DEP_DEBUG symbol is defined), the pnvram_battery_enable interface writes the contents of the register to the terminal. [Return to example]

  5. Determines if the battery disconnect circuitry is enabled by examining the PNV_BDISC bit in the tmp_reg variable. If the bit is set, pnvram_battery_enable calls the PNV_WRITEIO interface (discussed in Section 6.5) to write the contents of the tmp_reg variable back to the PNV_BAT_CTRL register. It clears the PNV_BDISC bit in the process, thus disabling the battery's disconnect circuitry.

    It forces the completion of this write by issuing an mb instruction, followed by another PNV_READIO call to read the PNV_BAT_CTRL register to the tmp_reg variable. This causes the delivery of any pending writes to the PCI NVRAM device registers. [Return to example]

  6. If the PNV_BDISC bit is set after this read, pnvram_battery_enable returns failure status (1) to the /dev/presto device driver. [Return to example]

  7. If the PNV_BDISC bit is clear (either after the first or second read of the PNV_BAT_CTRL register), pnvram_battery_enable returns success status (0) to the /dev/presto device driver. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.8.3    Implementing the pnvram_battery_disable Interface

The pnvram_battery_disable interface performs the following tasks:

The pnvram_battery_disable interface is called by the /dev/presto device driver when it needs to disable the battery on the PCI bus NVRAM expansion board. Section 6.6.2 shows that the pnvram_attach interface sets the nvram_battery_disable member of the presto_interface data structure to pnvram_battery_disable. This is the mechanism the /dev/presto device driver uses to call the interface that enables the battery on the PCI bus NVRAM expansion board.

The following code implements pnvram_battery_disable:

int
pnvram_battery_disable(int unit)  [1]
{
   register struct pnvram_softc *sc = pnvram_softc[unit];  [2]
   unsigned int tmp_reg;  [3]

 
tmp_reg = PNV_READIO(PNV_BAT_CTRL); [4]
 
if (!(tmp_reg & PNV_BDISC)) { [5] PNV_WRITEIO(PNV_BAT_CTRL, (tmp_reg | PNV_BDISC) ); mb(); PNV_WRITEIO(PNV_BAT_CTRL, (tmp_reg | PNV_BDISC) ); mb(); PNV_WRITEIO(PNV_BAT_CTRL, (tmp_reg & ~PNV_BDISC) ); mb(); PNV_WRITEIO(PNV_BAT_CTRL, (tmp_reg & ~PNV_BDISC) ); mb(); PNV_WRITEIO(PNV_BAT_CTRL, (tmp_reg | PNV_BDISC) ); mb(); tmp_reg = PNV_READIO(PNV_BAT_CTRL); DEP(("pvnram%d battery_disable() PNV_BAT_CTRL:0x%x\n",unit,tmp_reg)); }
 
if (tmp_reg & PNV_BDISC) [6] return(0); else [7] return(1); }

  1. Declares the variable unit that is passed to the pnvram_battery_disable interface by its caller, the /dev/presto device driver. [Return to example]

  2. Declares a pointer to a pnvram_softc data structure and calls it sc. The pnvram_softc data structure allows the /dev/pnvram device driver's associated interfaces to share data. This data structure is defined in the pnvram.h file. Section 6.2 describes the contents of this file, including the members of pnvram_softc.

    The pnvram_battery_disable interface initializes this pointer to the address of the element that corresponds to this PCI bus NVRAM expansion board unit in the array of pnvram_softc data structures (see Section 6.3). [Return to example]

  3. Declares the variable tmp_reg as a temporary register in which it receives the current contents of the PNV_BAT_CTRL register. [Return to example]

  4. Uses the PNV_READIO interface (discussed in Section 6.5) to read the contents of the PNV_BAT_CTRL register into the tmp_reg variable. [Return to example]

  5. Determines if the battery disconnect circuitry is enabled by examining the PNV_BDISC bit in the tmp_reg variable. If the bit is not set, pnvram_battery_disable calls the PNV_WRITEIO interface (discussed in Section 6.5) five times. Each call causes the previous contents of the PNV_BAT_CTRL register, as read to the tmp_reg variable, to be rewritten, while the PNV_BDISC bit is written successively as 1, 1, 0, 0, and 1. This special sequence enables the battery disconnect circuitry.

    The pnvram_battery_disable interface enforces the order of these writes to the PNV_BAT_CTRL register by interleaving them with mb instructions.

    The final mb instruction is followed by another PNV_READIO call to read the PNV_BAT_CTRL register to the tmp_reg variable. This causes the delivery of any pending writes to the PCI NVRAM device registers. If debug mode is enabled (that is, the DEP_DEBUG symbol is defined), the pnvram_battery_disable interface writes the contents of the register to the terminal. [Return to example]

  6. If the PNV_BDISC bit is set (either after the first or second read of the PNV_BAT_CTRL register), pnvram_battery_disable returns success status (0) to the /dev/presto device driver. [Return to example]

  7. If the PNV_BDISC bit is still clear after the second read, pnvram_battery_disable returns failure status (1) to the /dev/presto device driver. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.9    Read and Write Device Section

Table 6-4 lists the two interfaces implemented as part of the Read and Write Device Section for the /dev/pnvram device driver along with the sections in the book where each is described.

Table 6-4: Interfaces Implemented as Part of the Read and Write Device Section for the /dev/pnvram Device Driver

Part Section
Implementing the pnvram_read Interface Section 6.9.1
Implementing the pnvram_write Interface Section 6.9.2


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.9.1    Implementing the pnvram_read Interface

The pnvram_read interface is called by the pnvram_probe interface and the /dev/presto device driver and performs the following tasks:

Section 6.6.2 shows that the pnvram_attach interface sets the nvram_ioreg_read (read small pieces of NVRAM) and nvram_block_read (read large pieces of NVRAM) members of the presto_interface data structure to pnvram_read. These members both point to the same interface, which means pnvram_read handles both small and large reads from the PCI bus NVRAM expansion board.

Note

An xxread interface implemented on Digital UNIX typically has three arguments: dev, uio, and flag. The reason that pnvram_read has different arguments is that it is not called directly from the I/O system as the result of a read system call. The read request from the I/O system is made to the /dev/presto driver's read entry point, which then calls pnvram_read to perform the actual read operation.

The following code implements pnvram_read:

void
pnvram_read(vm_offset_t source, vm_offset_t dest, u_int len, int unit)  [1]
{
   register struct pnvram_softc *sc = pnvram_softc[unit];  [2]
   vm_offset_t srcptr;  [3]
   vm_offset_t destptr;  [4]
   volatile int retry;  [5]

 
sg_entry_t sg; [6] u_int xfer; [7] #if defined(PNVRAM_DMA_SLEEP) && defined(PNVRAM_DMA_INTR) int sleep_status; [8] #endif
 
DEP(("pnvram_read(source:0x%lx dest:0x%lx len:0x%x, unit:%d)\n", source,dest,len,unit)); [9]
 
while (len) { [10] xfer = len; if (xfer > PNV_MAX_XFER_SIZE) xfer = PNV_MAX_XFER_SIZE;
 
if (xfer >= 1024) { [11]
 
srcptr = KSEG_TO_PHYS(source) & 0x0FFFFFFFFL; [12]
 
if (!(dma_map_load(xfer, dest,(struct proc *)0, sc->ctlr, [13] &sc->dma_p,0,(DMA_ALL | DMA_CONTIG))) ) panic("pnvram_read: dma_map_load failure\n");
 
sg = dma_get_curr_sgentry(sc->dma_p); [14]
 
destptr = (u_long)sg->ba & 0x0FFFFFFFFL; [15]
 
mb(); sc->dma_in_progress = 1; [16] PNV_WRITEIO(PNV_SLAVE_ADDR, destptr); [17] PNV_WRITEIO(PNV_MASTER_ADDR, srcptr); PNV_WRITEIO(PNV_BYTE_COUNT, len); mb();
 
PNV_WRITEIO(PNV_DMA_CMD, PNV_NV_READ); [18] mb();
 
#ifdef PNVRAM_DMA_INTR [19] #ifdef PNVRAM_DMA_SLEEP sleep_status = mpsleep(sc, 0, 0, 1*hz, 0, 0); if (sleep_status == EWOULDBLOCK) { panic("pnvram_read: Timed-out DMA\n"); } else { return; } #else /* !PNVRAM_DMA_SLEEP */ [20] retry = dma_delay; while (--retry) { mb(); if (!(sc->dma_in_progress)) break; } if (!retry) { panic("pnvram_read: Timed-out DMA\n"); } #endif /* PNVRAM_DMA_SLEEP */
 
#else /* !PNVRAM_DMA_INTR */ [21] retry = poll_delay; while (--retry) { if (!(PNV_READIO(PNV_BYTE_COUNT) & 0x0ffffL)) break; if (!retry) panic("pnvram_read: DMA retry expired\n"); #endif /* PNVRAM_DMA_INTR */ } else { bcopy(source, dest, len); [22] } len -= xfer; source += xfer; dest += xfer; [23] } }

  1. Declares its input parameters, passed by the /dev/presto device driver, as follows:

    [Return to example]

  2. Declares a pointer to a pnvram_softc data structure and calls it sc. The pnvram_softc data structure allows the /dev/pnvram device driver's associated interfaces to share data. This data structure is defined in the pnvram.h file. Section 6.2 describes the contents of this file, including the members of pnvram_softc.

    The pnvram_read interface initializes this pointer to the address of the element that corresponds to this PCI bus NVRAM expansion board unit in the array of pnvram_softc data structures (see Section 6.3). [Return to example]

  3. Declares the variable srcptr in which pnvram_read stores the bus physical address of the source of the transfer. [Return to example]

  4. Declares the variable destptr in which pnvram_read stores the system physical address of the destination of the transfer. [Return to example]

  5. Declares the retry variable. This variable allows the pnvram_read interface to spinwait on the dma_in_progress flag in the pnvram_softc structure for a certain length of time, while waiting for the completion of a DMA transaction. [Return to example]

  6. Declares a pointer to an sg_entry structure, containing a bus address/byte count pair, which is used to map a DMA transfer from NVRAM cache to main memory. A bus address/byte count pair is represented by the ba and bc members of an sg_entry structure pointer. [Return to example]

  7. Declares the variable xfer, in which it stores the size of each partial data transfer. [Return to example]

  8. If both DMA operations and DMA sleeps are enabled (that is, the PNVRAM_DMA_INTR and PNVRAM_DMA_SLEEP symbols are defined), declares the variable sleep_status, in which it stores the return value from its call to the mpsleep interface. [Return to example]

  9. If debug mode is enabled (that is, the DEP_DEBUG symbol is defined), the pnvram_read interface writes its name, plus its source, dest, and len parameters to the terminal. [Return to example]

  10. Computes the length of the transfer and segments it if it exceeds the value represented by the PNV_MAX_XFER_SIZE constant. It sets up the variable xfer to represent the size (in bytes) of the current transfer segment, and the variable len to reflect the number of bytes that remain to be transferred. [Return to example]

  11. If the length of the current transfer segment exceeds 1K bytes, performs the transfer as a direct memory access (DMA) transaction. Otherwise, it avoids the overhead of the DMA operation by calling the bcopy interface. [Return to example]

  12. Converts the kernel segment (kseg) unmapped source buffer address the /dev/presto device driver passed in as input to a bus physical address. To do so, pnvram_read calls the KSEG_TO_PHYS interface.

    The KSEG_TO_PHYS interface takes one argument: the buffer virtual address to convert to a physical address.

    The pnvram_read interface truncates the system physical address returned by KSEG_TO_PHYS to 32 bits and stores the result in srcptr. [Return to example]

  13. Loads and sets the allocated system resources for DMA data transfers by calling dma_map_load. If dma_map_load cannot load and set the allocated system resources, pnvram_read calls panic to cause a system crash. Section 6.6.2 shows that these resources were previously allocated by calling dma_map_alloc.

    The dma_map_load interface takes seven arguments:

    [Return to example]

  14. Obtains the single sg_entry pair that has been allocated for this transfer. It does so by calling dma_get_curr_sgentry, passing it the dma_p member of the pnvram_softc structure. [Return to example]

  15. Moves the system memory destination address (the ba member of the sg structure returned by dma_get_curr_sgentry) into destptr, truncating it to 32 bits. [Return to example]

  16. Sets the dma_in_progress flag in the pnvram_softc structure before starting the DMA operation. The pnvram_read interface uses an mb instruction to ensure cache coherency of the dma_in_progress flag in a multiprocessing environment. [Return to example]

  17. Issues three calls to the PNV_WRITEIO interface (discussed in Section 6.5) to set up the device's PCI bus registers that describe the DMA transfer:

    The pnvram_read interface uses an mb instruction to force these writes to be completed before it writes to the device register that initiates the DMA transaction. [Return to example]

  18. Starts the read transaction from PNVRAM cache to main memory by writing the symbolic constant PNV_NV_READ to the NVRAM board's PNV_DMA_CMD register. The pnvram_read interface uses an mb instruction to force this write to be completed after the previous three writes to the PNV_DMA_CMD register. [Return to example]

  19. If both DMA interrupts and DMA sleeps are enabled (that is, the PNVRAM_DMA_INTR and PNVRAM_DMA_SLEEP symbols are defined), calls the mpsleep interface to put itself to sleep while waiting for the completion of the DMA transaction.

    The mp_sleep interface takes six arguments:

    If the call to mpsleep returns the value EWOULDBLOCK, the timeout value of 1 second expired before the PCI bus NVRAM expansion board posted a DMA interrupt. In this case, pnvram_read calls panic to cause a system crash.

    If the call to mpsleep returns the success status (0), pnvram_read returns to its caller, the /dev/presto device driver. [Return to example]

  20. If DMA interrupts are enabled but DMA sleeps are not (that is, the PNVRAM_DMA_INTR symbol is defined, but PNVRAM_DMA_SLEEP is not), sets up a spinwait on the dma_in_progress flag in the pnvram_softc structure to determine if the DMA transaction has completed. The retry count is initialized from the dma_delay constant and is decremented within the spinwait. If it expires before a DMA interrupt occurs, pnvram_read calls panic to cause a system crash. [Return to example]

  21. If neither DMA interrupts nor DMA sleeps are enabled (that is, both PNVRAM_DMA_INTR and PNVRAM_DMA_SLEEP symbols are undefined), repeatedly calls the PNV_READIO interface (discussed in Section 6.5) for the number of times indicated by the retry variable (as initialized from the poll_delay constant), waiting for the value in the PNV_BYTE_COUNT device register to reach zero. If the retry count expires before this happens, pnvram_read calls panic to cause a system crash.

    Note

    Among the events that could cause the retry count to expire would be a DMA abort interrupt posted by the NVRAM expansion board. These interrupts are enabled at initialization (that is, the PNV_DISAB_ABRT bit in the PNV_INTR_CTRL register is clear) and are not affected by the definition or nondefinition of the DMA_INTR symbol.

    [Return to example]

  22. If the length of the current transfer segment is less than 1K bytes, performs the transfer by using the bcopy interface, supplying the source, dest, and len arguments that the pnvram_read interface was passed by the /dev/presto driver. This allows the /dev/pnvram device driver to avoid the overhead of a DMA transfer.

    Note

    Because the PCI NVRAM source buffer is mapped in dense space, the pnvram_read interface can use the bcopy interface to perform the transfer. If the buffer were mapped in sparse space instead, pnvram_read would call the io_copyin interface, passing it an I/O handle for the source address, a system virtual address for the destination address, and a transfer byte count. The io_copyin interface is a generic interface that maps to a bus- and machine-specific interface that actually performs the copy from bus address space to system memory.

    [Return to example]

  23. Recomputes the values of the len, source, and dest variables within the while loop, so that it can proceed with the next segment of the transfer. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.9.2    Implementing the pnvram_write Interface

The pnvram_write interface is called by the /dev/presto device driver and performs the following tasks:

Section 6.6.2 shows that the pnvram_attach interface sets the nvram_ioreg_write (write small pieces of NVRAM) and nvram_block_write (write large pieces of NVRAM) members of the presto_interface data structure to pnvram_write. These members both point to the same interface. This means pnvram_write handles both small and large reads from the PCI bus NVRAM expansion board.

Note

An xxwrite interface implemented on Digital UNIX typically has three arguments: dev, uio, and flag. The reason that pnvram_write has different arguments is that it is not called directly from the I/O system as the result of a write system call. The read request from the I/O system is made to the /dev/presto driver's write entry point, which then calls pnvram_write to perform the actual write operation.

The following code implements pnvram_write:

void
pnvram_write(vm_offset_t source, vm_offset_t dest, u_int len, int unit)  [1]
{
   register struct pnvram_softc *sc = pnvram_softc[unit];  [2]
   vm_offset_t srcptr;  [3]
   vm_offset_t destptr;  [4]
   volatile int retry;  [5]
   sg_entry_t sg;  [6]
   u_int xfer;  [7]
#if defined(PNVRAM_DMA_SLEEP) && defined(PNVRAM_DMA_INTR)
   int  sleep_status;  [8]
#endif

 
DEP(("pnvram_write(source:0x%lx dest:0x%lx len:0x%x, unit:%d)\n", [9] source,dest,len,unit));
 
while (len) { [10] xfer = len; if (xfer > PNV_MAX_XFER_SIZE) xfer = PNV_MAX_XFER_SIZE; if (xfer >= 1024) { destptr = KSEG_TO_PHYS(dest) & 0x0FFFFFFFFL; [11]
 
if (!(dma_map_load(xfer,source,(struct proc *)0, sc->ctlr, [12] &sc->dma_p, 0,(DMA_ALL | DMA_CONTIG))) ) panic("pnvram_write: dma_map_load failure\n");
 
sg = dma_get_curr_sgentry(sc->dma_p); [13]
 
srcptr = (u_long)sg->ba & 0x0FFFFFFFFL; [14]
 
mb(); sc->dma_in_progress = 1; [15] PNV_WRITEIO(PNV_SLAVE_ADDR, srcptr); [16] PNV_WRITEIO(PNV_MASTER_ADDR, destptr); PNV_WRITEIO(PNV_BYTE_COUNT, len); mb();
 
PNV_WRITEIO(PNV_DMA_CMD, PNV_NV_WRITE); [17] mb();
 
#ifdef PNVRAM_DMA_INTR [18] #ifdef PNVRAM_DMA_SLEEP sleep_status = mpsleep(sc, 0, 0, 1*hz, 0, 0); if (sleep_status == EWOULDBLOCK) { panic("pnvram_write: Timed-out DMA\n"); } else { return; } #else /* !PNVRAM_DMA_SLEEP */ [19] retry = dma_delay; while (--retry) { mb(); if (!(sc->dma_in_progress)) break; } if (!retry) { panic("pnvram_write: Timed-out DMA\n"); } #endif
 
#else /* !PNVRAM_DMA_INTR */ [20] retry = poll_delay; while (--retry) { if (!(PNV_READIO(PNV_BYTE_COUNT) & 0x0ffffL)) break; } if (!retry) panic("pnvram_write: DMA retry expired\n");
 
} #endif /* PNVRAM_DMA_INTR */ } else { bcopy(source, dest, len); [21] } len -= xfer; [22] source += xfer; dest += xfer;
 
} }

  1. Declares its input parameters, passed by the /dev/presto device driver, as follows:

    [Return to example]

  2. Declares a pointer to a pnvram_softc data structure and calls it sc. The pnvram_softc data structure allows the /dev/pnvram device driver's associated interfaces to share data. This data structure is defined in the pnvram.h file. Section 6.2 describes the contents of this file, including the members of pnvram_softc.

    The pnvram_write interface initializes this pointer to the address of the element that corresponds to this PCI bus NVRAM expansion board unit in the array of pnvram_softc data structures defined in the pnvram_data.c file (see Section 6.3). [Return to example]

  3. Declares the variable srcptr in which pnvram_write stores the system physical address of the source of the transfer. [Return to example]

  4. Declares the variable destptr in which pnvram_write stores the bus physical address of the destination of the transfer. [Return to example]

  5. Declares the retry variable. This variable allows the pnvram_write interface to spinwait on the dma_in_progress flag in the pnvram_softc structure for a certain length of time, while waiting for the completion of a DMA transaction. [Return to example]

  6. Declares a pointer to an sg_entry structure, containing a bus address/byte count pair, which is used to map a DMA transfer from main memory to NVRAM cache. A bus address/byte count pair is represented by the ba and bc members of an sg_entry structure pointer. [Return to example]

  7. Declares the variable xfer, in which it stores the size of each partial data transfer. [Return to example]

  8. If both DMA operations and DMA sleeps are enabled (that is, the PNVRAM_DMA_INTR and PNVRAM_DMA_SLEEP symbols are defined), declares the variable sleep_status, in which it stores the return value from its call to the mpsleep interface. [Return to example]

  9. If debug mode is enabled (that is, the DEP_DEBUG symbol is defined), the pnvram_write interface writes its name, plus its source, dest, len, and unit parameters to the terminal. [Return to example]

  10. Computes the length of the transfer and segments it if it exceeds the value represented by the PNV_MAX_XFER_SIZE constant. It sets up the variable xfer to represent the size (in bytes) of the current transfer segment, and the variable len to reflect the number of bytes that remain to be transferred.

    If the length of the current transfer segment exceeds 1K bytes, performs the transfer as a direct memory access (DMA) transaction. Otherwise, it avoids the overhead of the DMA operation by calling the bcopy interface. [Return to example]

  11. Converts the kernel segment (kseg) unmapped destination buffer address that the /dev/presto device driver passed in as input to a bus physical address. To do so, the pnvram_write interface calls the KSEG_TO_PHYS interface.

    The KSEG_TO_PHYS interface takes one argument: the buffer virtual address to convert to a physical address.

    The pnvram_write interface truncates the system physical address returned by KSEG_TO_PHYS to 32 bits and stores the result in destptr. [Return to example]

  12. Loads and sets the allocated system resources for DMA data transfers by calling dma_map_load. If dma_map_load cannot load and set the allocated system resources, pnvram_write calls panic to cause a system crash. Section 6.6.2 shows that these resources were previously allocated by calling dma_map_alloc.

    The dma_map_load interface takes seven arguments:

    [Return to example]

  13. Obtains the single sg_entry pair that has been allocated for this transfer. It does so by calling dma_get_curr_sgentry, passing it the dma_p member of the pnvram_softc structure. [Return to example]

  14. Moves the system memory source address (the ba member of the sg structure returned by dma_get_curr_sgentry) into srcptr, truncating it to 32 bits. [Return to example]

  15. Sets the dma_in_progress flag in the pnvram_softc structure before starting the DMA operation. The pnvram_write interface uses an mb instruction to ensure the cache coherency of the dma_in_progress flag in a multiprocessing environment. [Return to example]

  16. Issues three calls to the PNV_WRITEIO interface (discussed in Section 6.5) to set up the device's PCI bus registers that describe the DMA transfer:

    The pnvram_write interface uses an mb instruction to force these writes to be completed before it writes to the device register that initiates the DMA transaction. [Return to example]

  17. Starts the write transaction from main memory to PNVRAM cache by writing the symbolic constant PNV_NV_WRITE to the NVRAM board's PNV_DMA_CMD register. The pnvram_write interface uses an mb instruction to force this write to be completed after the previous three writes to the PNV_DMA_CMD register. [Return to example]

  18. If both DMA interrupts and DMA sleeps are enabled (that is, the PNVRAM_DMA_INTR and PNVRAM_DMA_SLEEP symbols are defined), calls the mpsleep interface to put itself to sleep while waiting for the completion of the DMA transaction.

    The mp_sleep interface takes six arguments:

    If the call to mpsleep returns the value EWOULDBLOCK, the timeout value of 1 second expired before the PCI bus NVRAM expansion board posted a DMA interrupt. In this case, pnvram_write calls panic to cause a system crash.

    If the call to mpsleep returns the success status (0), pnvram_write returns to its caller, the /dev/presto device driver. [Return to example]

  19. If DMA interrupts are enabled but DMA sleeps are not (that is, the PNVRAM_DMA_INTR symbol is defined, but PNVRAM_DMA_SLEEP is not), sets up a spinwait on the dma_in_progress flag in the pnvram_softc structure to determine if the DMA transaction has completed. The retry count is initialized from the dma_delay constant and is decremented within the spinwait. If it expires before a DMA interrupt occurs, pnvram_write calls panic to cause a system crash. [Return to example]

  20. If neither DMA interrupts nor DMA sleeps are enabled (that is, both PNVRAM_DMA_INTR and PNVRAM_DMA_SLEEP symbols are undefined), repeatedly calls the PNV_READIO interface (discussed in Section 6.5) for the number of times indicated by the retry variable (as initialized from the poll_delay constant), waiting for the value in the PNV_BYTE_COUNT device register to reach zero. If the retry count expires before this happens, pnvram_write calls panic to cause a system crash.

    Note

    Among the events that could cause the retry counter to expire would be a DMA abort interrupt posted by the NVRAM expansion board. These interrupts are enabled at initialization (that is, the PNV_DISAB_ABRT bit in the PNV_INTR_CTRL register is clear) and are not affected by the definition or nondefinition of the DMA_INTR symbol.

    [Return to example]

  21. If the length of the current transfer segment is less than 1K bytes, performs the transfer by using the bcopy interface, supplying the source, dest, and len arguments that the pnvram_write interface was passed by the /dev/presto driver. This allows the /dev/pnvram device driver to avoid the overhead of a DMA transfer.

    Note

    Because the PCI NVRAM source buffer is mapped in dense space, the pnvram_write interface can use the bcopy interface to perform the transfer. If the buffer were mapped in sparse space instead, pnvram_write would call the io_copyout interface, passing it an I/O handle for the source address, a system virtual address for the destination address, and a transfer byte count. The io_copyout interface is a generic interface that maps to a bus- and machine-specific interface that actually performs the copy from system memory to bus address space.

    [Return to example]

  22. Recomputes the values of the len, source, and dest variables within the while loop, so that it can proceed with the next segment of the transfer. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


6.10    Zero NVRAM Section

The pnvram_zero interface is called by the /dev/presto device driver to zero (clear) a specified length of NVRAM starting at a specified address. Section 6.6.2 shows that the pnvram_attach interface sets the nvram_ioreg_zero (zero small pieces of NVRAM) and nvram_block_zero (zero large pieces of NVRAM) members of the presto_interface data structure to pnvram_zero. These members both point to the same interface. This means pnvram_zero zeros (clears) both small and large lengths from the PCI bus NVRAM expansion board.

The following code implements pnvram_zero:

void
pnvram_zero(vm_offset_t addr, u_int len, int unit)  [1]
{
   register struct pnvram_softc *sc = pnvram_softc[unit];  [2]
   register io_handle_t io_handle;  [3]
   int  zero_stat;  [4]

 
DEP(("pnvram%d pnvram_zero(addr:0x%lx len:0x%x)\n",unit, addr,len)); bzero(addr, len); [5] }

  1. Declares its input parameters, passed by the /dev/presto device driver, as follows:

    [Return to example]

  2. Declares a pointer to a pnvram_softc data structure and calls it sc. The pnvram_softc data structure allows the /dev/pnvram device driver's associated interfaces to share data. This data structure is defined in the pnvram.h file. Section 6.2 describes the contents of this file, including the members of pnvram_softc.

    The pnvram_zero interface initializes this pointer to the address of the element that corresponds to this PCI bus NVRAM expansion board unit in the array of pnvram_softc data structures defined in the pnvram_data.c file (see Section 6.3). [Return to example]

  3. Declares the variable io_handle in which pnvram_zero stores an I/O handle to the starting address of the buffer on the NVRAM cache that is to be zeroed. The I/O handle would be required if pnvram_zero needed to call the io_zero interface. [Return to example]

  4. Declares the variable zero_stat to receive the completion status of the call to the io_zero interface. This version of the /dev/pnvram driver does not call io_zero. [Return to example]

  5. Calls the bzero interface, supplying the addr and len arguments the pnvram_zero interface was passed by the /dev/presto driver.

    Note

    Because the PCI NVRAM source buffer is mapped in dense space, the pnvram_zero interface can use the bzero interface to perform the transfer. If the buffer were mapped in sparse space instead, pnvram_zero would call the io_zero interface, passing it an I/O handle for the source address, a system virtual address for the destination address, and a transfer byte count. The io_zero interface is a generic interface that maps to a bus- and machine-specific interface that actually performs the clearing of bus address space.

    [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Chapter] [Index] [Help]


6.11    Interrupt Section

The pnvram_intr interface is called if interrupts are enabled from this PCI bus NVRAM expansion board unit, and the unit issues an interrupt. As discussed in Section 6.6.1, the pnvram_probe interface enables direct-memory access (PNV_ENAB_DMA) and correctable read data (PNV_ENAB_CRD) interrupts only if the DMA_INTR symbol is defined. It also registers pnvram_intr as this PCI bus NVRAM expansion board unit's interrupt service interface by calling handler_add and handler_enable.

When DMA interrupts are enabled, the pnvram_intr interface handles DMA completion interrupts, correctable read data (CRD) detection interrupts, and DMA abort interrupts (which are enabled at initialization).

The following code implements pnvram_intr:

int
pnvram_intr(int unit)  [1]
{
   register struct pnvram_softc *sc = pnvram_softc[unit];  [2]
   int          intr_status;  [3]
   int          error_status;  [4]
   u_short      pci_status;  [5]

 
intr_status = PNV_READIO(PNV_INTR_CTRL); [6]
 
if (!(intr_status & PNV_INTR_OCCURRED)) [7] return(INTR_NOT_SERVICED);
 
if ((intr_status & PNV_DMA_INTR) && !(intr_status & PNV_ABRT_INTR)) { [8] PNV_WRITEIO(PNV_INTR_CTRL, (intr_status & ~(PNV_CRD_INTR|PNV_ABRT_INTR))); mb(); #ifdef PNVRAM_DMA_SLEEP wakeup((caddr_t)pnvram_softc[unit]); #else /* !PNVRAM_DMA_SLEEP */ [9] sc->dma_in_progress = 0; mb(); #endif /* PNVRAM_DMA_SLEEP */ }
 
if (intr_status & PNV_CRD_INTR) { [10] printf("CRD detected on pnvram%d:\n", unit); printf(" Failing Addr : 0x%x\n", PNV_READIO(PNV_FADDR)); printf(" EDC Syndrome : 0x%x\n", PNV_READIO(PNV_EDC_SYND)); printf(" Error Reg. : 0x%x\n", PNV_READIO(PNV_ERROR)); PNV_WRITEIO(PNV_ERROR, PNVR_CRD_ERR); mb(); PNV_WRITEIO(PNV_INTR_CTRL, (intr_status & ~(PNV_DMA_INTR | PNV_ABRT_INTR))); mb(); }
 
if (intr_status & PNV_ABRT_INTR) { [11] error_status = PNV_READIO(PNV_ERROR); pci_status = PNV_READ_CNFG(PNV_STAT); printf("*** pnvram%d fatal error ***\n", unit); printf(" DMA Slave Addr: 0x%x\n", PNV_READIO(PNV_SLAVE_ADDR)); printf(" DMA Master Addr: 0x%x\n", PNV_READIO(PNV_MASTER_ADDR)); printf(" DMA Byte Count : 0x%x\n", PNV_READIO(PNV_BYTE_COUNT)); printf(" DMA Command : 0x%x\n", PNV_READIO(PNV_DMA_CMD)); printf(" Failing Addr. : 0x%x\n", PNV_READIO(PNV_FADDR)); printf(" EDC Syndrome : 0x%x\n", PNV_READIO(PNV_EDC_SYND)); printf(" Error Reg. : 0x%x\n", error_status); printf(" PCI Status reg.: 0x%x\n", pci_status);
 
PNV_WRITEIO(PNV_SLAVE_ADDR, 0); PNV_WRITEIO(PNV_MASTER_ADDR, 0); PNV_WRITEIO(PNV_BYTE_COUNT, 0); PNV_WRITEIO(PNV_DMA_CMD, 0); PNV_WRITEIO(PNV_ERROR, error_status); mb();
 
PNV_WRITE_CNFG(PNV_CMD, ((struct pci_config_hdr *)(sc->ctlr->private[0]))->command); PNV_WRITE_CNFG(PNV_STAT, pci_status); mb();
 
panic("pnvram_intr: fatal dma-abort error\n"); } return(INTR_SERVICED); [12] }

  1. Declares the input variable unit corresponding to the PCI bus NVRAM expansion board unit that interrupted. [Return to example]

  2. Declares a pointer to a pnvram_softc data structure and calls it sc. The pnvram_softc data structure allows the /dev/pnvram device driver's associated interfaces to share data. This data structure is defined in the pnvram.h file. Section 6.2 describes the contents of this file, including the members of pnvram_softc.

    The pnvram_intr interface initializes this pointer to the address of the element that corresponds to this PCI bus NVRAM expansion board unit in the array of pnvram_softc data structures defined in the pnvram_data.c file (see Section 6.3). [Return to example]

  3. Declares the variable intr_status to receive the postinterrupt contents of the PNV_INTR_CTRL register. [Return to example]

  4. Declares the variable error_status to receive the postinterrupt contents of the PNV_ERROR register. [Return to example]

  5. Declares the variable pci_status to receive the postinterrupt contents of the PNV_STAT register. [Return to example]

  6. Calls the PNV_READIO interface (discussed in Section 6.5) to read the postinterrupt contents of the PNV_INTR_CTRL register into intr_status. More than one interrupt status bit can be set simultaneously, thus signifying multiple interrupt conditions. The following code attempts to discern all possible reasons for the interrupt. [Return to example]

  7. Determines if the interrupt that caused its execution was truly an interrupt for its device by comparing the mask PNV_INTR_OCCURRED against the postinterrupt contents of the PNV_INTR_CTRL register. If the device was not the source of the interrupt, it returns INTR_NOT_SERVICED status. [Return to example]

  8. Examines the contents of the PNV_INTR_CTRL register. If the PNV_DMA_INTR bit is set but the PNV_ABRT_INTR bit is not set, the interrupt is due to the normal completion of a DMA transaction. In this case, the pnvram_intr interface performs the following actions:

    [Return to example]

  9. If DMA sleeps are not enabled (that is, the symbol PNVRAM_DMA_SLEEP is unset), clears the dma_in_progress flag in the pnvram_softc data structure. This causes either the pnvram_read or pnvram_write call to exit from its spinwait on the dma_in_progress flag. [Return to example]

  10. Further examines the contents of the PNV_INTR_CTRL register. If the PNV_CRD_INTR bit is set, the interrupt is due to the detection of a correctable read data (CRD) error. In this case, the pnvram_intr interface performs the following actions:

    [Return to example]

  11. Further examines the contents of the PNV_INTR_CTRL register. If the PNV_ABRT_INTR bit is set, the DMA transaction has been aborted because, during the DMA transaction, an uncorrectable SRAM memory occurred; or the PCI bus master detected an error, received a target abort, or signaled master abort. In this case, the pnvram_intr interface performs the following actions:

    [Return to example]

  12. If the device was the source of the interrupt, returns INTR_SERVICED status. [Return to example]