This appendix contains the source listing for the /dev/pnvram device driver.
#ifndef _PNVRAM_H_ #define _PNVRAM_H_
/************************************************************************/ /* */ /* pnvram.h */ /* */ /* Digital PCI NVRAM presto driver */ /* */ /************************************************************************/
#include <io/common/devdriver.h>
/* * PCI NVRAM register definitions */
/* * Define offsets to nvram device registers * note: Mem-space mapped CSR's in same * location/offset in PCI config space, * and PCI Config. regs. in same location/offset * in PCI Mem.-mapped space. */ #define PNV_ID 0x00L /* PCI VID/DID regs. */ #define PNV_CMD 0x04L /* PCI Cmd reg. */ #define PNV_STAT 0x08L /* PCI Status reg. */ #define PNV_ERROR 0x40L /* Error Register */ #define PNV_FADDR 0x44L /* Failing Address Register */ #define PNV_MEM_CFG 0x48L /* Memory Config Register */ #define PNV_EDC_CTRL 0x4CL /* EDC Control Register */ #define PNV_EDC_SYND 0x50L /* EDC Syndrome Register */ #define PNV_BAT_CTRL 0x54L /* Battery Control/Status Reg. */ #define PNV_BAT_DIAG_RST 0x58L /* Battery Diagnostic Reset Reg.*/ #define PNV_EEPROM_IF 0x5CL /* EEPROM Interface Register */ #define PNV_SLAVE_ADDR 0x60L /* DMA Slave Address Register */ #define PNV_MASTER_ADDR 0x64L /* DMA Master Address Register */ #define PNV_BYTE_COUNT 0x68L /* DMA Byte Count Register */ #define PNV_DMA_CMD 0x6cL /* DMA Command Register */ #define PNV_INTR_CTRL 0x70L /* Interrupt Control Register */
/* * Bit definitions for Error Register */ #define PNVR_ERR_SUM 0x01 /* Error Summary bit */ /* Failing Addr & EDC Syndrome regs. * frozen until this bit is cleared */ #define PNVR_CMD_PAR 0x0010 /* Command/Address parity error */ #define PNVR_WR_PAR 0x0020 /* Write data parity error */ #define PNVR_CRD_ERR 0x0040 /* Correctable EDC error */ #define PNVR_UCRD_ERR 0x0080 /* UnCorrectable EDC error */ #define PNVR_M_CMD_ERR 0x1000 /* Missed Command/Addr. parity error */ #define PNVR_M_WR_PAR 0x2000 /* Missed Write data parity error */ #define PNVR_M_CRD_ERR 0x4000 /* Missed Correctable EDC error */ #define PNVR_M_UCRD_ERR 0x8000 /* Missed UnCorrectable EDC error */
/* * Bit definitions for (NVRAM) Memory Configuration Register */ #define PNV_BANK_MASK 0x0003 /* Number of banks (1,2,4,rsvd) */ #define PNV_SIZE_MASK 0x000c /* SRAM size: 256K,1M,4M, rsvd */ #define PNV_MOD_REV_MASK 0x0070 /* Module Rev. */ #define PNV_CHIP_REV_MASK 0x0F00 /* PCI chip rev. */ /* useful defines wrt Mem. Cnfg. Reg. to determine NVRAM size */ #define PNV_SIZE_SHIFT 1 #define PNV_512K 0x80000
/* * Bit definitions for EDC Control Register */ #define PNVR_DIS_CRD_LOG 0x1000 /* Disable EDC Status reg. logging for CRD's */ #define PNVR_DIS_EDC_SERR 0x8000 /* Disable assertion of PCI SERR on Uncorrectable EDC error */
/* * Bit definitions for Battery Control and Status Register */ #define PNV_BCHRG 0x001 /* Battery charge status */ #define PNV_BDISC 0x002 /* Battery disconnet bit */ #define PNV_BFAIL 0x004 /* Battery Failed to charge */ #define PNV_BAT_OK 0x008 /* State of battery */ #define PNV_SPEED_MASK 0x300 /* PCI bus speed mask */
/* * Bit definitions for EEPROM Interface Register */ #define PNV_SCL 0x01 /* Serial Clock */ #define PNV_XMT_SDA 0x20 /* Serial Write Data */ #define PNV_RCV_SDA 0x40 /* Serial Read Data */
/* * define's for Byte Count register */ #define PNV_MAX_XFER_SIZE 65532 /* 64K less 4 bytes */
/* * Bit definitions for DMA Command Register */ #define PNV_NV_READ 0x01 /* Move data from nvram to mem */ #define PNV_NV_WRITE 0x02 /* Move data from mem to nvram */
/* * Bit definitions for Interrupt Control Register */ #define PNV_ENAB_DMA 0x01 #define PNV_ENAB_CRD 0x02 #define PNV_DISAB_ABRT 0x04 /* Set to 1 to not intr. on DMA abort */ #define PNV_DMA_INTR 0x100 #define PNV_CRD_INTR 0x200 #define PNV_ABRT_INTR 0x400 /* DMA Abort Intr */ #define PNV_INTR_OCCURRED (PNV_DMA_INTR | PNV_CRD_INTR | PNV_ABRT_INTR)
#define PNV_CACHE_OFFSET 0x400 /* reserved space */ #define PNV_DIAG_RESERVED 0x400 /* reserved space to store diag results */ #define PNV_DIAG_RESULT 0x08L /* neg. offset from cache start where diag. results are placed */ #define PNV_DIAG_SIZE 0x04 /* size of diag. results */ #define BOARD_FAILED 0x0008 /* bit is set if board failed diags */
/* * Define constants used for upper layer presto commuication */ #define PNVRAM_MAPPED 1 /* buffer is mapped */ #define PNVRAM_NOTMAPPED 0 /* buffer is not mapped */ #define PNVRAM_CACHED 1 /* Use kseg space */ #define PNVRAM_NOTCACHED 0 /* Use a cached space */
/* * Define the softc for the PCI NVRAM driver */ struct pnvram_softc { io_handle_t csr_base; /* io-handle for PCI mem-space CSRs */ vm_offset_t cache_phys_start; /* CPU-physical address of NVRAM cache */ vm_offset_t cache_base; /* base address of NVRAM on PCI bus that controller is attached to */ vm_offset_t cache_kseg_start; /* KSEG addr of the presto cache */ u_int cache_size; /* size of NVRAM cache */ u_int cache_offset; /* Offset from cache_base */ u_int diag_status; /* If the board passed diags or not */ volatile u_int dma_in_progress; /* If DMA is completed or not */ dma_handle_t dma_p; /* handle to dma resources */ struct controller *ctlr; /* pointer to nvram controller struct */ ihandler_id_t *hid; /* ihander_id_t rtn from handler_add */ u_int chip_rev; /* hw rev of PINK chip */ }; #endif /* _PNVRAM_H_ */
/************************************************************************/ /* */ /* pnvram_data.c */ /* */ /* Digital PCI non-volitile ram (PNVRAM) device driver */ /* */ /************************************************************************/ /* Generated by config; config counts up the * number of "pnvram" in the config file and * assigns NPNVRAM to it. */ #include "pnvram.h"
/* structure def's in this file */ #include <io/dec/pci/pnvram.h>
struct pnvram_softc *pnvram_softc[NPNVRAM]; struct controller *pnvram_info[NPNVRAM];
/************************************************************************/ /* */ /* pnvram.c */ /* */ /* Digital PCI non-volitile ram (PNVRAM) device driver */ /* */ /************************************************************************/
#include <sys/malloc.h> #include <sys/presto.h> #include <io/common/devdriver.h> #include <io/dec/pci/pci.h> #include <io/dec/pci/pnvram.h>
/* * external references */ extern struct pnvram_softc *pnvram_softc[]; 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;
/* * PCI NVRAM I/O register Read/Write Macros * * Based on Standard bus routines with sc->csrbase as base address, * simply || the register offset */
/* * All PNVRAM CSR's are 32-bit registers, * so one read macro and one write macro can be * used for all csr accesses. */ #define PNV_READIO(a) READ_BUS_D32((io_handle_t)(sc->csr_base | a)) #define PNV_WRITEIO(a,d) WRITE_BUS_D32((io_handle_t)(sc->csr_base | a),(long)d)
/* * PNVRAM PCI Config. Space Command & Status regs. are reset * on error. 32-bit writes to these 2 16-bit regs. may or may not * work, so will go safe route -- 16-bit writes. */ #define PNV_READ_CNFG(a) \ READ_BUS_D16((io_handle_t)(((struct pci_config_hdr *) \ (sc->ctlr->private[0]))->config_base | a)) #define PNV_WRITE_CNFG(a,d) \ WRITE_BUS_D16((io_handle_t)(((struct pci_config_hdr *) \ (sc->ctlr->private[0]))->config_base | a), (long)d)
/* * Forward delarations */ int pnvram_probe(), pnvram_attach(); 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
#ifdef PNVRAM_DMA_INTR /* * This following is for the worst case (very heavy bus traffic). */ static int dma_delay = 10000000; #else /* * paramaters for tuned poll loop */ static int poll_delay = 1000000; #endif /* PNVRAM_DMA_INTR */
/* * Driver definitions */ caddr_t pnvramstd[] = { 0 }; struct driver pnvramdriver = { pnvram_probe, 0, pnvram_attach, 0, 0, 0, 0, 0, "pnvram", pnvram_info};
/* #define DEP_DEBUG */ #ifdef DEP_DEBUG #define DEP(x) printf("**** PNVRAM **** "); printf x #else #define DEP(x) #endif
/************************************************************************/ /* */ /* pnvram_probe() */ /* This routine checks the PCI config header of the device to */ /* ensure it was configurable/configured (allocated/enabled memory */ /* space on PCI). If configured, it allocates the controller's */ /* data structures. */ /* */ /* Arguments: */ /* pci_cfg_hdr -- Pointer to device's PCI config header */ /* ctlr -- Pointer to the controller structure */ /* */ /* Returns: */ /* Success -- 1. */ /* Failure -- 0. */ /* */ /************************************************************************/ int pnvram_probe(struct pci_config_hdr *pci_cfg_hdr, struct controller *ctlr) { /* Begin pnvram_probe */
register struct pnvram_softc *sc; unsigned int mem_cfg; /* memory configuration paramaters */ int unit = ctlr->ctlr_num; /* unit number */ #ifdef PNVRAM_DMA_INTR struct handler_intr_info pnvram_intr_info; ihandler_t pnvram_ihandle; #endif volatile unsigned int synch_data;
/* * Check that the Bus Master and Memory Space enable bits * have been set. If not, then the PCI POST code must not * have been able to configure this device into the system. * If not configured in by PCI POST code, then barx values * are invalid (and cannot be used for csr accesses). */ if (!(pci_cfg_hdr->command & CMD_MEM_SPACE) \ || !(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); }
/* * Allocate memory for softc structure. */ MALLOC(sc, struct pnvram_softc*, sizeof(struct pnvram_softc), 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 can't get DMA resources, then don't pass probe/configure. */ if (dma_map_alloc(PNV_MAX_XFER_SIZE, ctlr, &sc->dma_p, DMA_CONTIG) == 0) { FREE(sc, M_DEVBUF); /* free mem. resources */ return(0); }
/* * Save the ctlr struct in softc */ sc->ctlr = ctlr;
/* * Save PCI configuration header read at bootstrap * to reset Command & Status regs. on errors. * Save the address of the structure in controller's * private storage element. */ ctlr->private[0] = pci_cfg_hdr;
/* * Get the controller's csr base address. * The csr base is the device's bar1 assignment * mapped into an io-handle by the kernel PCI bus auto-config code. */ sc->csr_base = pci_cfg_hdr->bar1; DEP(("pnvram%d pci_cfg_hdr->bar1:0x%lx\n", unit,pci_cfg_hdr->bar1));
/* * Set up softc entries for location and offset of NVRAM cache * for Presto. */ sc->cache_offset = PNV_CACHE_OFFSET;
/* * Calculate the nvram size from the ram size and number of banks. */ mem_cfg = PNV_READIO(PNV_MEM_CFG); DEP(("pnvram%d mem_cfg: 0x%x\n", unit,mem_cfg));
sc->cache_size = ((PNV_512K << (mem_cfg & PNV_BANK_MASK)) << ((mem_cfg & PNV_SIZE_MASK) >> PNV_SIZE_SHIFT)); DEP(("pnvram%d sc->cache_size: 0x%x\n", unit,sc->cache_size));
/* * Check revision of PINK chip. */
sc->chip_rev = PNV_READIO(PNV_MEM_CFG) & PNV_CHIP_REV_MASK; DEP(("pnvram%d chip_rev: 0x%x\n", unit, sc->chip_rev));
/* * Read the nvram base address. * BAR0 contains the PCI (prefetchable) memory space address * that the PNVRAM's (NV)RAM-buffer is located. * Note: PCI bus code maps prefetchable mem. space into dense-space, * Even dense-space addresses are io-handles, that are * not system physical addresses. Use iohandle_to_phys() * to get system pa from dense-space io-handle. */
sc->cache_base = iohandle_to_phys(pci_cfg_hdr->bar0, HANDLE_DENSE_SPACE); DEP(("pnvram%d cache_base:0x%lx\n", unit,sc->cache_base));
/* * System Physical Address where presto will start using nvram. */ sc->cache_phys_start = sc->cache_base + sc->cache_offset; DEP(("pnvram%d cache_phys_start:0x%lx\n", unit,sc->cache_phys_start));
/* * Presto uses kseg addresses. */ sc->cache_kseg_start = PHYS_TO_KSEG(sc->cache_phys_start); DEP(("pnvram%d cache_kseg_start:0x%lx\n", unit, sc->cache_kseg_start));
/* * account for diag space */ sc->cache_size = sc->cache_size - PNV_CACHE_OFFSET;
/* * Check the console diagnostic results * note: pnvram_read's 1st arg is a KSEG address. * : since pnvram read size is small, won't do DMA, * thus, don't need DMA & intr's setup for this check. */ pnvram_read((sc->cache_kseg_start - PNV_DIAG_RESULT), &sc->diag_status, PNV_DIAG_SIZE); DEP(("pnvram%d Diag Register: 0x%x\n", unit,sc->diag_status));
/* * Some kind of pattern would be much better. */ if (sc->diag_status & BOARD_FAILED) { printf("pnvram%d diag reg: 0x%x\n", unit, sc->diag_status); sc->diag_status = 0; /* Leave it configured in so it can be read for further diagnosis */ } else { DEP(("pnvram%d diag reg 0x%x\n", unit, sc->diag_status)); sc->diag_status = 1; }
/* * Enable device for operation -- init. regs.; * clear extraneous intr. bits; enable various interrupts. */ PNV_WRITEIO(PNV_DMA_CMD, 0); /* first, clear stray intr's */ PNV_WRITEIO(PNV_INTR_CTRL, (PNV_DMA_INTR | PNV_CRD_INTR | PNV_ABRT_INTR)); mb(); /* clr any possible error bits */ 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(); /* read forces any posted writes out */ synch_data = PNV_READIO(PNV_ERROR);
#ifdef PNVRAM_DMA_INTR /* * Register and enable our interrupt routine. We won't be * getting any interrupts until after the interface has been * initialized (when we enabled the interrupt masks within PNVRAM). */ 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 dma resources */ FREE(sc, M_DEVBUF); /* free mem resources */ return(0); } else { handler_enable(sc->hid); PNV_WRITEIO(PNV_INTR_CTRL, (PNV_ENAB_DMA | PNV_ENAB_CRD)); mb(); /* read forces any posted writes out */ synch_data = PNV_READIO(PNV_INTR_CTRL); } #endif /* PNVRAM_DMA_INTR */
/* * Print bootstrap informational message */ printf("pnvram%d: Module Revision %d, Cache size: %d\n", unit, (mem_cfg & PNV_MOD_REV_MASK), sc->cache_size);
/* Success! */ return(1);
} /* End pnvram_probe */
/************************************************************************/ /* */ /* pnvram_attach() */ /* Interface exists: make available by defining presto interface */ /* routines and calling presto_init. */ /* */ /* Arguments: */ /* ctlr -- Pointer to interface's kernel controller structure */ /* note: controller number = pnvram unit number */ /* */ /* Returns: */ /* None. */ /* */ /************************************************************************/ int pnvram_attach(struct controller *ctlr) { register int unit = ctlr->ctlr_num; register struct pnvram_softc *sc = pnvram_softc[unit]; /* * Initialize presto interface entry points which allow * Prestoserve access to the NVRAM cache */
presto_interface[unit].nvram_status = pnvram_status; 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;
/* * The following define access routines for the Presto * driver to use for its access to the PCI NVRAM. * Note that the ioreg routines and the block routines * are all expected to be of the format with src,dest,count,unit * parameters. The zero routines take arguments of addr,length,unit * */ 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;
/* * The PCI granularity is a byte, but force the * use of 32bit quantities for performance reasons */
/* Min size of a "small" ioreg data block */ presto_interface[unit].nvram_min_ioreg = sizeof(int);
/* byte alignment restriction for ioreg block */ presto_interface[unit].nvram_ioreg_align = sizeof(int);
/* min size of a "large" block data transfer (in bytes) */ presto_interface[unit].nvram_min_block = PRFSIZE;
/* byte alignment restriction for a block data transfers */ presto_interface[unit].nvram_block_align = PRFSIZE;
/* * Call presto_init to initialize the Prestoserve driver * interfaces */ presto_init(sc->cache_kseg_start, sc->cache_size, PNVRAM_NOTMAPPED, PNVRAM_CACHED, pnvram_ssn(unit), unit); }
/************************************************************************/ /* */ /* pnvram_status() */ /* Provide Prestoserve with status of diagnostics run on nvram. */ /* */ /* Arguments: */ /* unit -- Unit number of pnvram to be checked. */ /* */ /* Returns: */ /* */ /* NVRAM_RDONLY -- Passed RO diags */ /* NVRAM_BAD -- Failed diags */ /* */ /************************************************************************/ int pnvram_status(int unit) { register struct pnvram_softc *sc = pnvram_softc[unit];
if (sc->diag_status) return(NVRAM_RDONLY); /* Passed RO diags */ else return(NVRAM_BAD); /* failed diags */ }
/************************************************************************/ /* */ /* pnvram_battery_status() */ /* Provide Prestoserve with status of nvram battery. */ /* */ /* Arguments: */ /* unit -- Unit number of pnvram to be checked. */ /* */ /* Returns: */ /* */ /* 0 -- Batteries OK */ /* 1 -- Battery Problem */ /* */ /************************************************************************/ int pnvram_battery_status(int unit) { register struct pnvram_softc *sc = pnvram_softc[unit]; int tmp_reg;
nvram_batteries[unit].nv_nbatteries = 1; /* always one battery */ nvram_batteries[unit].nv_minimum_ok = 1; /* it must be good */ nvram_batteries[unit].nv_primary_mandatory = 1; /* primary must be OK */ nvram_batteries[unit].nv_test_retries = 1; /* call routine 1 time */
tmp_reg = PNV_READIO(PNV_BAT_CTRL); DEP(("pnvram%d battery_status: csr:0x%x\n", unit, tmp_reg));
if ((tmp_reg & PNV_BAT_OK) && (tmp_reg & PNV_BCHRG)) { nvram_batteries[unit].nv_status[0] = BATT_OK; DEP(("Status is BATT_OK\n")); return(0); } else { DEP(("Status is BAD\n")); return(1); } }
/************************************************************************/ /* */ /* pnvram_battery_enable() */ /* Provide Prestoserve with the ability to enable the battery on */ /* the pci nvram. */ /* */ /* Arguments: */ /* unit -- Unit number of pnvram to be enabled. */ /* */ /* Returns: */ /* 0 -- Enabled successfully */ /* 1 -- Not enabled */ /* */ /************************************************************************/ int pnvram_battery_enable(int unit) { register struct pnvram_softc *sc = pnvram_softc[unit]; unsigned int tmp_reg;
/* * Required action is to do a write if BDISC control bit set. * We must save & restore the state of this reg for * some of the other bits. BDISC bit is zero'd-on-write to * ensure a subsequent battery_disable call will not be * interpreted as 111001 instead of 11001 to the BDISC bit. */
tmp_reg = PNV_READIO(PNV_BAT_CTRL); DEP(("pnvram%d battery_enable() BAT_CTRL:0x%x\n", unit, tmp_reg)); if (tmp_reg & PNV_BDISC) { PNV_WRITEIO(PNV_BAT_CTRL, (tmp_reg & ~PNV_BDISC)); mb(); /* read forces any posted writes out */ tmp_reg = PNV_READIO(PNV_BAT_CTRL); }
if (tmp_reg & PNV_BDISC) return(1); else return(0); }
/************************************************************************/ /* */ /* pnvram_battery_disable() */ /* Provide Prestoserve with the ability to disable the battery on */ /* the pci nvram. */ /* */ /* Arguments: */ /* unit -- Unit number of pnvram to be disabled. */ /* */ /* Returns: */ /* 0 -- Disabled successfully */ /* 1 -- Not Disabled */ /* */ /************************************************************************/ int pnvram_battery_disable(int unit) { register struct pnvram_softc *sc = pnvram_softc[unit]; unsigned int tmp_reg;
/* The required action is to send sequence "11001" to * the battery disconnect register. This enables the battery * disconnect circuit. */ tmp_reg = PNV_READIO(PNV_BAT_CTRL);
if (!(tmp_reg & PNV_BDISC)) { 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(); /* read forces any posted writes out */ 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) return(0); else return(1); }
/************************************************************************/ /* */ /* pnvram_write() */ /* write the length block of data pointed to by the source address */ /* parameter to the PCI NVRAM destination paramter. This routine */ /* uses DMA for transfers > page-size and programmed I/O for */ /* smaller transfers. */ /* */ /* Arguments: */ /* source -- address of the source of the data to be written */ /* Because this is passed by presto interface, the */ /* format will be a KSEG'd physical address. */ /* dest -- destination for the data */ /* Because this is passed by presto interface, the format */ /* will be a KSEG'd physical address. */ /* len -- length of the block in bytes. */ /* unit -- Unit number of pnvram to be disabled. */ /* */ /* Returns: */ /* none */ /* */ /************************************************************************/ void pnvram_write(vm_offset_t source, vm_offset_t dest, u_int len, int unit) { register struct pnvram_softc *sc = pnvram_softc[unit]; vm_offset_t srcptr; vm_offset_t destptr; volatile int retry; /* retry counter */ sg_entry_t sg; /* sg_entry pointer */ u_int xfer; /* intermediate byte counter */ #if defined(PNVRAM_DMA_SLEEP) && defined(PNVRAM_DMA_INTR) int sleep_status; /* rtn value from sleep */ #endif
/* Presto WRITE operation: Write to NVRAM from Main Memory */ DEP(("pnvram_write(source:0x%lx dest:0x%lx len:0x%x, unit:%d)\n", source,dest,len,unit));
while (len) {
xfer = len; /* PNVRAM can at most do 64Kbyte-4byte transfers */
if (xfer > PNV_MAX_XFER_SIZE) xfer = PNV_MAX_XFER_SIZE;
/* Use DMA if size is 1K or greater */ if (xfer >= 1024) { /* * Set up destination address passed from Presto, * the dest is a main memory virtual address that is not * actually in main memory, but is in pci nvram address space. * Truncate significant bits to 32-bits for this PCI device. */ destptr = KSEG_TO_PHYS(dest) & 0x0FFFFFFFFL;
/* * Let the dma interfaces do the mapping. * Since DMA resources are pre-allocated, dma_map_load() * should never fail, unless the xfer or source params * are n-g. */ if (!(dma_map_load(xfer,source,(struct proc *)0, sc->ctlr, &sc->dma_p, 0,(DMA_ALL | DMA_CONTIG))) ) panic("pnvram_write: dma_map_load failure\n");
/* * There should be one entry for this transfer, since * dma mapping request set DMA_CONTIG flag. */ sg = dma_get_curr_sgentry(sc->dma_p);
/* * Setup system memory source address. */ srcptr = (u_long)sg->ba & 0x0FFFFFFFFL;
/* * Set the dma_in_progress flag before starting the DMA */ mb(); /* For MP coherency */ sc->dma_in_progress = 1; PNV_WRITEIO(PNV_SLAVE_ADDR, srcptr);
/* * Setup NVRAM destination address on PCI bus. */ PNV_WRITEIO(PNV_MASTER_ADDR, destptr);
/* * Load the byte count. */ PNV_WRITEIO(PNV_BYTE_COUNT, len);
/* * Must synch. above dstream writes before the next one * or else ALPHA could re-order and send out CMD write * before address & byte-count writes. */ mb();
/* * Start NVRAM transfer */ PNV_WRITEIO(PNV_DMA_CMD, PNV_NV_WRITE); mb();
#ifdef PNVRAM_DMA_INTR #ifdef PNVRAM_DMA_SLEEP /* * Sleep until DMA completes. * -- non-intr-able sleep; no messages; * 1-second timeout; no locks, no flags */ sleep_status = mpsleep(sc, 0, 0, 1*hz, 0, 0);
/* * if timed-out, sleep rtn status is EWOULDBLOCK */ if (sleep_status == EWOULDBLOCK) { panic("pnvram_write: Timed-out DMA\n"); } else { return; } #else /* !PNVRAM_DMA_SLEEP */ /* * Loop until the DMA is done (interrupt routine clears the flag) */ retry = dma_delay; while (--retry) { mb(); /* For MP coherency */ if (!(sc->dma_in_progress)) break; } if (!retry) { panic("pnvram_write: Timed-out DMA\n"); }
#endif /* PNVRAM_DMA_SLEEP */ #else /* !PNVRAM_DMA_INTR */
/* * Spin on the byte count to get to zero. */ retry = poll_delay; while (--retry) { if (!(PNV_READIO(PNV_BYTE_COUNT) & 0x0ffffL)) break; }
/* * If retry expires the hardware is broken. */ if (!retry) { panic("pnvram_write: DMA retry expired\n"); }
#endif /* PNVRAM_DMA_INTR */
} /* end of if xfer DMA */ else {
/* * For small transfers, do a bcopy. * * note: if the buffer was sparse-space mapped, then * driver would use io_copyout(src, dest, len), * where src = sys va; dest = io-handle; * len = bytes to move/copy. */
bcopy(source, dest, xfer); } len -= xfer; source += xfer; dest += xfer;
} /* end of while loop */
}
/************************************************************************/ /* */ /* pnvram_read() */ /* Read the length block of data pointed to by the source */ /* NVRAM address to system memory. */ /* */ /* Arguments: */ /* source -- address of the source of the data to read. */ /* Because this is passed by presto interface, the format */ /* will be a KSEG'd logical physical address. */ /* dest -- destination address for data to be written */ /* Because this is passed by presto interface, the format */ /* will be a KSEG'd logical physical address. */ /* len -- length of the block in bytes */ /* */ /* Returns: */ /* none */ /* */ /************************************************************************/ void pnvram_read(vm_offset_t source, vm_offset_t dest, u_int len, int unit) { register struct pnvram_softc *sc = pnvram_softc[unit]; vm_offset_t srcptr; vm_offset_t destptr; volatile int retry; sg_entry_t sg; /* sg_entry pointer */ u_int xfer; #if defined(PNVRAM_DMA_SLEEP) && defined(PNVRAM_DMA_INTR) int sleep_status; /* rtn value from sleep */ #endif
/* Presto Read operation: Read NVRAM to Main Memory */ DEP(("pnvram_read(source:0x%lx dest:0x%lx len:0x%x, unit:%d)\n", source,dest,len,unit));
while (len) {
xfer = len; /* PNVRAM can at most do 655536 byte transfers */
if (xfer > PNV_MAX_XFER_SIZE) xfer = PNV_MAX_XFER_SIZE;
/* Use DMA if size is 1K or greater */ if (xfer >= 1024) {
/* * Set up source address passed from Presto, * the src is a main memory virtual address that is not * actually in main memory, but is in pci nvram address space. * Truncate significant bits to 32-bits for this PCI device. */ srcptr = KSEG_TO_PHYS(source) & 0x0FFFFFFFFL;
/* * Let the dma interfaces do the mapping. * Since DMA resources are pre-allocated, dma_map_load() * should never fail, unless the xfer or source params * are n-g. */ if (!(dma_map_load(xfer, dest,(struct proc *)0, sc->ctlr, &sc->dma_p,0,(DMA_ALL | DMA_CONTIG))) ) panic("pnvram_read: dma_map_load failure\n");
/* * There should be one entry for this transfer, since * dma mapping request set DMA_CONTIG flag. */ sg = dma_get_curr_sgentry(sc->dma_p);
/* * Setup system memory source address. */ destptr = (u_long)sg->ba & 0x0FFFFFFFFL; /* * Set the dma_in_progress flag before starting the DMA */ mb(); /* For MP coherency */ sc->dma_in_progress = 1; PNV_WRITEIO(PNV_SLAVE_ADDR, destptr);
/* * Setup NVRAM destination address on PCI bus. */ PNV_WRITEIO(PNV_MASTER_ADDR, srcptr);
/* * Load the byte count. */ PNV_WRITEIO(PNV_BYTE_COUNT, len);
/* * Must synch. above dstream writes before the next one * or else ALPHA could re-order and send out CMD write * before address & byte-count writes. */ mb();
/* * Start NVRAM transfer */ PNV_WRITEIO(PNV_DMA_CMD, PNV_NV_READ); mb();
#ifdef PNVRAM_DMA_INTR #ifdef PNVRAM_DMA_SLEEP /* * Sleep until DMA completes. * -- non-intr-able sleep; no messages; * 1-second timeout; no locks, no flags */ sleep_status = mpsleep(sc, 0, 0, 1*hz, 0, 0);
/* * if timed-out, sleep rtn status is EWOULDBLOCK */ if (sleep_status == EWOULDBLOCK) { panic("pnvram_read: Timed-out DMA\n"); } else { return; } #else /* !PNVRAM_DMA_SLEEP */ /* * Loop until the DMA is done (interrupt routine clears the flag) */ retry = dma_delay; while (--retry) { mb(); /* For MP coherency */ if (!(sc->dma_in_progress)) break; } if (!retry) { panic("pnvram_read: Timed-out DMA\n"); }
#endif /* PNVRAM_DMA_SLEEP */ #else /* !PNVRAM_DMA_INTR */ /* * Spin on the byte count to get to zero. */ retry = poll_delay; while (--retry) { if (!(PNV_READIO(PNV_BYTE_COUNT) & 0x0ffffL)) break; } /* * If retry expires the hardware is broken. */ if (!retry) panic("pnvram_read: DMA retry expired\n");
#endif /* PNVRAM_DMA_INTR */
} /* end of if for DMA xfer */ else {
/* * For small transfers, do a bcopy. * * note: if the buffer was sparse-space mapped, then * driver would use io_copyin(src, dest, len), * where src = io-handle; dest = system va; * len = bytes to move/copy. */
bcopy(source, dest, xfer); } len -= xfer; source += xfer; dest += xfer;
} /* end of xfer while-loop */
}
/************************************************************************/ /* */ /* pnvram_zero() */ /* Zero the "len" bytes of PCI NVRAM memory starting at "addr" */ /* */ /* Arguments: */ /* addr - starting address of PCI NVRAM to zero */ /* Because this is passed by presto interface, the format */ /* will be a KSEG'd CPU-system physical address. */ /* len - number of bytes to zero */ /* unit -- Unit number of pnvram. */ /* */ /* Returns: */ /* None. */ /* */ /************************************************************************/ void pnvram_zero(vm_offset_t addr, u_int len, int unit) { /* register struct pnvram_softc *sc = pnvram_softc[unit]; register io_handle_t io_handle; int zero_stat; */
/* * For small transfers, do an io_zero. * * note: if the buffer was sparse-space mapped, then * driver would use io_zero(addr, len), * where addr = io-handle; len = bytes to move/copy. */ /* * io_handle = busphys_to_iohandle((KSEG_TO_PHYS(addr) & 0x0FFFFFFFFL), * BUS_MEMORY, sc->ctlr); * zero_stat = io_zero(io_handle, len); * if (zero_stat == -1) * panic("pnvram_zero: zero-ing pnvram failed\n"); */ DEP(("pnvram%d pnvram_zero(addr:0x%lx len:0x%x)\n",unit, addr,len)); bzero(addr, len); }
/************************************************************************/ /* */ /* pnvram_ssn() */ /* Generate a unique, system- and unit-level i.d. for this device. */ /* Arguments: */ /* unit -- Unit number of pnvram. */ /* */ /* Returns: */ /* 64-bit integer useful to uniquely i.d. this system and unit */ /* within this system. */ /* Lower 32-bits: Sysid; Upper 32-bits: unit # */ /* */ /************************************************************************/ u_long pnvram_ssn(int unit) { u_int info_rtn; struct item_list item_list; u_long ssn;
item_list.function = GET_SYSID;
info_rtn = get_info(&item_list);
if (info_rtn == TRUE) { 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); /* use just the unit # */ } }
/************************************************************************/ /* */ /* pnvram_intr() */ /* Interrupt service routine for pnvram device. */ /* */ /* Arguments: */ /* unit -- Unit number of pnvram. */ /* */ /* Returns: */ /* none */ /* */ /* Interrupts can occur for 2 reasons: */ /* 1) DMA completion (success or aborted) */ /* 2) CRD during NVRAM read. */ /* */ /************************************************************************/ int pnvram_intr(int unit) { /* Begin pnvram_intr */
register struct pnvram_softc *sc = pnvram_softc[unit]; int intr_status; /* Intr ctrl reg. after intr. occurred */ int error_status; /* Error reg. after intr. occurred */ u_short pci_status; /* PCI config-space Status reg. */
/* * read intr. status */ intr_status = PNV_READIO(PNV_INTR_CTRL);
/* * Determine if the interrrupt is for us. Return INTR_NOT_SERVICED * if we have no interrupt status bits set. */ if (!(intr_status & PNV_INTR_OCCURRED)) return(INTR_NOT_SERVICED);
/* * Check for high-frequency, normal DMA case first */ if ((intr_status & PNV_DMA_INTR) && !(intr_status & PNV_ABRT_INTR)) { /* * W1C DMA intr bit; * make sure other intr bits & enables not cleared */ 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 */ /* * Clear the dma_in_progress flag to show that DMA is completed. */ sc->dma_in_progress = 0; mb(); /* for MP coherency */ #endif /* PNVRAM_DMA_SLEEP */ }
/* Continue to see if other interrupt bits set simultaneously */
/* * Next highest-freq. case: CRD */ if (intr_status & PNV_CRD_INTR) { 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)); mb(); PNV_WRITEIO(PNV_ERROR, PNVR_CRD_ERR); mb(); /* clear error bit */ /* * W1C CRD intr bit; * make sure other intr bits & enables not cleared */ PNV_WRITEIO(PNV_INTR_CTRL, (intr_status & ~(PNV_DMA_INTR | PNV_ABRT_INTR))); mb(); /* clear intr */ }
/* * Big problem in this case: * Current presto<->driver model is to panic * on hw errors and let the (re-)bootstrap presto * layer ask the operator what to do with * existing NVRAM data. * * DMA_ABORT can occur due to: * Master Detected error during DMA operation. * Master Received Target Abort during DMA operation. * Master Signaled Master Abort during DMA operation. * Uncorrectable Error from SRAM memory. */ if (intr_status & PNV_ABRT_INTR) { 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); printf(" PCI Device CSR : 0x%x\n", PNV_READIO(PNV_CMD)); /* * Now that error information has been fetched * reset the module so on re-boot, the device * can be checked by diags. */ 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); /* W1C error bits */ mb(); /* Synch writes before re-enabling Master bit in Cmd reg. */
PNV_WRITE_CNFG(PNV_CMD, ((struct pci_config_hdr *)(sc->ctlr->private[0]))->command); PNV_WRITE_CNFG(PNV_STAT, pci_status); /* W1C error bits */ mb(); /* * bye for now... */ panic("pnvram_intr: fatal dma-abort error\n"); } return(INTR_SERVICED); } /* End of pnvram_intr */