/* * idr_driver.c * * code module for Solaris 7 & later driver for Tahoma Technology * 10116/10118 PCI DR11-W emulators * * * Tahoma Technology * (formerly Ikon Corporation) * 107 2nd Avenue North * Seattle, WA, USA 98109 * * 206.728.6465 * http://www.tahomatech.com * tahoma@tahomatech.com * * This driver (including source) is placed in the public domain in the * hope that it will prove useful in using and understanding Tahoma Technolgy's * 10116 and 10118 PCI DR11-W emulators. * * This code is intended to be working and (relatively) bug free driver when * running on the host computers and operating systems available to Tahoma Technology. * Tahoma Technology will attempt to keep the code running on contemporary OSs * and hardware from SUN and others, but does not guarantee this. * * References to Ikon Corporation left in place for compatibility and historical reasons. * * Comments, suggestions, requests, and BUG REPORTS are encouraged. * * 1 October, 1996 initial version for SPARC * * 16 January, 1997 added byte swap and endian mode support for 9060sd and later chips * (default is big endian - which will un-swap the swapping that * happens between dma and memory) * selecting byte swapping for dma will actually turn OFF the big * endian mode (with the end result that the bytes are swapped on SPARC) * * switched to dma channel 1, since the 9060 SD has no dma channel 0 (!) * this should still work with two-channel 9060 and future 9080 * * 17 June, 1997 added PLX DMA PCI ADD to GET_REGS ioctl * * added suspend and resume to detach and attach for system power * management * * modified attach to get driver and device defaults from idr.conf * * 27 August, 1997 moved dma_handle to soft state * * added endian reg to saved state * * moved ddi_dma_alloc_handle to attach & fixed cast of dip in call * * added remove_minor_node to detach code * * fixed error in dma_attr that had no effect on sparc, but crashed * x86 kernel * * 9 July, 1998 modified for compatibility with 64 bit Solaris * * added ignore_minphys parameter * * added code to preserve uio.offset during read & write, * so we don't have to "rewind" the iova counter * * added code to restore the plx int line register after an eeprom * reload (not necessary on sparc, but added for consistency * with x86 code) * * 11 November, 1999 fixed a potential bug in PLX config eerpom reload code that could * cause a panic in some future host with a short pci bus timeout timer * * 4 May, 2004 added code to DATA_IN and DATA_OUT ioctls to force pci writes to be * in order - to avoid problem with S8 07/03 on some platforms * * added similar code to other places that might fail if ordering * not enforced -- RMW ops don't need this (because of the read) * only done where necessary (?) * * IDR_SYNC_x macros introduced to avoid some typing * * 21 May, 2004 correct one IDR_SYNC_x macro call that worked but didn't * look consistent w/trying to avoid two identical calls in * sequence * * apparently not necessary to do all this tap-dancing to avoid * compiler optimization, but we will stick with it for now * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "idr_reg.h" #include "idr_var.h" #include "idr_io.h" static void *state_head ; /* opaque handle top of state structs */ /* * dummy variables for register reads to force cache flush * used with IDR_SYNC_x and PLX_SYNC_x macros */ static volatile uint32_t read_dump_0; static volatile uint32_t read_dump_1; /* These are the entry points into our driver that are called when the driver is loaded, during a system call, or in response to an interrupt. */ static int idr_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result); static int idr_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); static int idr_open(dev_t *dev, int openflags, int otyp, cred_t *credp); static int idr_close(dev_t dev, int openflags, int otyp, cred_t *credp); static int idr_read(dev_t dev, struct uio *uiop, cred_t *credp); static int idr_write(dev_t dev, struct uio *uiop, cred_t *credp); static int idr_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp, int *rvalp); static u_int idr_soft_intr(caddr_t arg); static u_int idr_high_intr(caddr_t arg); static int idr_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); /* These are internal private functions - although strategy is called by physio, it is not made visible externally */ static int idr_strategy(struct buf *bp); static void idr_minphys(struct buf *bp); static void plx_soft_reset(struct idr_unit *unit_p); /* declare device data access structure for plx and idr registers and config registers we will try to treat each register as 32 bits wide - so we don't want any byte swapping however, it seems that the sparc machines swizzle the bytes between host and pci if we say never swap (at least for 32 bit accesses) - so we will use the little endian flag this works fine for byte devices like the IKON hardcopy boards, we will see if it holds true for 16 bit data used by the DR11s */ static struct ddi_device_acc_attr slave_attr = { DDI_DEVICE_ATTR_V0, DDI_STRUCTURE_LE_ACC, DDI_STRICTORDER_ACC, } ; /* declare dma attribute structure */ static struct ddi_dma_attr dma_attr = { (uint_t)DMA_ATTR_V0, /* version */ (uint64_t)0x0, /* lower address boundary */ (uint64_t)0xFFFFFFFF, /* upper boundary - 32 bit max */ (uint64_t)0x7FFFFF, /* 23 bit address counter */ (uint64_t)0x2, /* 16 bit word alignment */ (uint_t)0x0, /* burst sizes seem to be ignored for pci (sbus only?) */ (uint32_t)0x2, /* min xfer size is 1 word */ (uint64_t)0x7FFFFF, /* non chain, so total same as single bfr size */ (uint64_t)0xFFFFFFFF, /* no segment restriction */ (int)1, /* for now - no dma chaining */ (uint32_t)2, /* granularity - not sure about this one! */ (uint_t)0, /* flags - sun says 0 for now */ }; /* declare structures used for dynamic loading and unloading of the driver */ static struct cb_ops idr_cb_ops = { idr_open, idr_close, nulldev, /* not a block driver */ nodev, /* no print routine */ nodev, /* no dump routine */ idr_read, idr_write, idr_ioctl, nodev, /* no devmap routine */ nulldev, /* no mmap routine */ nulldev, /* no segmap routine */ nochpoll, /* no chpoll routine */ ddi_prop_op, (int)0, /* not a STREAMS driver */ (int)(D_NEW | D_MP), /* safe for multi-thread/multi-processor */ }; /* NOTE: declare and initialize the dev_ops structure. if it is necessary to prevent the system from unloading the driver on a timed basis (it will if no driver calls are made for some (unknown!) length of time) replace idr_detach with nodev. this will keep the driver from unloading. if this is done, it will be necessary to do a reconfiguration boot any time a new version of the driver is to be loaded. we do not support power operations for this driver/device combination we do allow system-wide suspend/resume operations when this device is not open */ static struct dev_ops idr_ops = { DEVO_REV, /* DEVO_REV indicated by manual */ (int)0, /* device reference count */ idr_getinfo, nulldev, /* identify no longer required */ nulldev, /* device probe for non-self-id */ idr_attach, idr_detach, /* REPLACE W/nodev IF DRIVER SHOULD STAY ATTACHED */ nodev, /* device reset routine */ &idr_cb_ops, (struct bus_ops *)0, /* bus operations */ nulldev, /* power operations */ }; extern struct mod_ops mod_driverops; static struct modldrv modldrv = { &mod_driverops, MODINFOBANNER, &idr_ops, }; static struct modlinkage modlinkage = { (int)MODREV_1, /* MODREV_1 indicated by manual */ (void *)&modldrv, NULL, /* termination of list of linkage structures */ }; /* register interrupts, map the working registers, and initialize things of a per-instance basis */ static int idr_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { register struct idr_unit *unit_p; int instance; int result; char minor_node_name[16] ; uint32_t temp; /* save some typing */ #define getprop(devi, name, def) ddi_getprop(DDI_DEV_T_ANY, (devi),\ DDI_PROP_DONTPASS, (name), (def)) instance = ddi_get_instance(dip); /* test for expected commands */ switch(cmd) { case DDI_RESUME: unit_p = (struct idr_unit *)ddi_get_soft_state(state_head, instance); /* we are being asked to resoter register state due to power up following power management power down */ DPRINT((CE_CONT, "idr_attach: instance %d: restoring state & clearing suspended flag\n", instance)); mutex_enter(&unit_p->soft_mutex); /* restore hardware state we really don't need to save and restore the range counter, since we don't allow suspension while the device is open - but it may be useful for testing the suspend/resume code */ PLX_PUT32(PLX_ENDIAN_REG, unit_p->saved_plx_endian_reg); PLX_PUT32(PLX_INT_CSTAT, unit_p->saved_plx_int_cstat); PLX_PUT32(PLX_EEPROM_USER, unit_p->saved_plx_eeprom_user); IDR_PUT32(IDR_LATCHED_FUNCTIONS, unit_p->saved_latched_functions); IDR_PUT32(IDR_MODE, unit_p->saved_mode); IDR_PUT32(IDR_RANGE_LOW, unit_p->saved_range_low); IDR_PUT32(IDR_RANGE_MID, unit_p->saved_range_mid); IDR_PUT32(IDR_RANGE_HIGH, unit_p->saved_range_high); PLX_SYNC_0; IDR_SYNC_1; unit_p->unit_suspended = 0; /* wake up idr_open() which may be waiting on suspended flag */ cv_broadcast(&unit_p->power_cv); mutex_exit(&unit_p->soft_mutex); return(DDI_SUCCESS); case DDI_ATTACH: DPRINT((CE_CONT,"idr_attach: instance %d: command is DDI_ATTACH\n", instance)); /* allocate soft state structure and add to list */ if (ddi_soft_state_zalloc(state_head, instance) != 0) { cmn_err(CE_CONT,"idr_attach: instance %d: ddi_soft_state_zalloc failure!\n",instance) ; return (DDI_FAILURE); } unit_p = (struct idr_unit *)ddi_get_soft_state(state_head, instance); ddi_set_driver_private(dip, (caddr_t)unit_p); unit_p->dip = dip; unit_p->saved_instance = instance; unit_p->soft_intr_req = 0; /* unlike other architectures (sbus or isa) where the interrupt level is determined by the hardware characteristics of the board, in pci/solaris land, interrupt priority is assigned to us based on device class code - or so it seems we look like a bridge chip (although we should probably be none of the above) -- we may change the eeprom to say we are class simple communication with a sub class of none of the above -- we will also probably use a xx.comf file to force the interrupt priority to be somewhat below the scheduler -- this would allow us to get rid of the extra level of interrupt handling, but for now it will stay this (looking like a bridge chip) causes us to be assigned to the same priority as the scheduler - at least in one implementation the test for high level interrupts doesn't complain since we aren't higher, we are the same this causes a recursive mutex enter panic at interrupt time if we end up with the interrupt code running the same thread as the strategy routine we could assign a different priority with a xx.conf file, but this might not solve the problem in another implementation our solution, for now, is to implement a high level interrupt handler which triggers a soft interrupt (running at a different level) that can grab the mutex that strategy is using without causing a panic we will use ddi_get_iblock_cookie rather than getting our cookie via ddi_add_intr to avoid a potential race that can occur if the interrupt routine gets registered and polled before our mutex is initialized */ /* get iblock cookie for high level interrupt we use only one interrupt level */ if(ddi_get_iblock_cookie(dip, 0, &unit_p->high_iblock_cookie) != DDI_SUCCESS) { ddi_soft_state_free(state_head, instance); cmn_err(CE_CONT,"idr_attach: instance %d: ddi_get_iblock_cookie (high level) error!\n", instance); return(DDI_FAILURE); } /* initialize high level MUTEX */ mutex_init(&unit_p->high_mutex, "idr high mutex", MUTEX_DRIVER, (void *)unit_p->high_iblock_cookie); /* register the high level interrupt */ if(ddi_add_intr(dip, 0, &unit_p->high_iblock_cookie, (ddi_idevice_cookie_t *) NULL, idr_high_intr, (caddr_t) unit_p) != DDI_SUCCESS) { cmn_err(CE_CONT,"idr_attach: instance %d: ddi_add_intr (high level) error!\n",instance) ; mutex_destroy(&unit_p->high_mutex); ddi_soft_state_free(state_head, instance); return (DDI_FAILURE); } /* get an iblock cookie for the soft interrupt */ if(ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_HIGH, &unit_p->soft_iblock_cookie) != DDI_SUCCESS) { cmn_err(CE_CONT,"idr_attach: instance %d: ddi_get_iblock_cookie error!\n", instance); ddi_remove_intr(dip, 0, unit_p->high_iblock_cookie); mutex_destroy(&unit_p->high_mutex); ddi_soft_state_free(state_head, instance); return(DDI_FAILURE); } /* initialize the low level mutex */ mutex_init(&unit_p->soft_mutex, "idr soft mutex", MUTEX_DRIVER, (void *)unit_p->soft_iblock_cookie); /* initialize conditional variable for normal operations */ cv_init(&unit_p->cv,"idr cv", CV_DRIVER, (void *)unit_p->soft_iblock_cookie); /* initialize another cv for use with power management */ cv_init(&unit_p->power_cv,"idr power cv", CV_DRIVER, (void *)unit_p->soft_iblock_cookie); /* register the soft interrupt */ if (ddi_add_softintr(dip, DDI_SOFTINT_HIGH, &unit_p->soft_intr_id, &unit_p->soft_iblock_cookie, (ddi_idevice_cookie_t *) NULL, idr_soft_intr, (caddr_t) unit_p) != DDI_SUCCESS) { cmn_err(CE_CONT,"idr_attach: instance %d: ddi_add_softintr error!\n",instance) ; ddi_remove_intr(dip, 0, unit_p->high_iblock_cookie); mutex_destroy(&unit_p->high_mutex); cv_destroy(&unit_p->cv); cv_destroy(&unit_p->power_cv); mutex_destroy(&unit_p->soft_mutex); ddi_soft_state_free(state_head, instance); return (DDI_FAILURE); } /* get the number of device regsiters since plx may fiddle with this in later chip revs, we won't check the number, just save it for possible diagnostic purposes */ if(ddi_dev_nregs(dip, &result) != DDI_SUCCESS) { cmn_err(CE_CONT,"idr_attach: instance %d: ddi_dev_nregs error!\n", instance); ddi_remove_intr(dip, 0, unit_p->high_iblock_cookie); mutex_destroy(&unit_p->high_mutex); ddi_remove_softintr(unit_p->soft_intr_id); cv_destroy(&unit_p->cv); cv_destroy(&unit_p->power_cv); mutex_destroy(&unit_p->soft_mutex); ddi_soft_state_free(state_head, instance); return(DDI_FAILURE); } DPRINT((CE_CONT,"idr_attach: instance %d: nregs = 0x%x\n", instance, result)); /* map in the plx and idr registers set length and offset to so entire reg ranges are mapped this is necessary since plx may change their register ranges */ if (ddi_regs_map_setup(dip, PLX_REG_RNUMBER, (caddr_t *)&unit_p->plx_base, 0, 0, &slave_attr, &unit_p->plx_acc_handle) != DDI_SUCCESS) { cmn_err(CE_CONT,"idr_attach: instance %d: ddi_regs_map_setup error!\n",instance) ; ddi_remove_intr(dip, 0, unit_p->high_iblock_cookie); mutex_destroy(&unit_p->high_mutex); ddi_remove_softintr(unit_p->soft_intr_id); cv_destroy(&unit_p->cv); cv_destroy(&unit_p->power_cv); mutex_destroy(&unit_p->soft_mutex); ddi_soft_state_free(state_head, instance); return (DDI_FAILURE); } if (ddi_regs_map_setup(dip, IDR_REG_RNUMBER, (caddr_t *)&unit_p->idr_base, 0, 0, &slave_attr, &unit_p->idr_acc_handle) != DDI_SUCCESS) { cmn_err(CE_CONT,"idr_attach: instance %d: ddi_regs_map_setup error!\n",instance) ; ddi_regs_map_free(&unit_p->plx_acc_handle); ddi_remove_intr(dip, 0, unit_p->high_iblock_cookie); mutex_destroy(&unit_p->high_mutex); ddi_remove_softintr(unit_p->soft_intr_id); cv_destroy(&unit_p->cv); cv_destroy(&unit_p->power_cv); mutex_destroy(&unit_p->soft_mutex); ddi_soft_state_free(state_head, instance); return (DDI_FAILURE); } /* allocate dma handle */ if(ddi_dma_alloc_handle(dip, &dma_attr, DDI_DMA_DONTWAIT, (caddr_t)0, &unit_p->dma_handle) != DDI_SUCCESS) { cmn_err(CE_CONT,"idr_attach: instance %d: ddi_dma_alloc_handle failure!\n",instance) ; ddi_regs_map_free(&unit_p->plx_acc_handle); ddi_regs_map_free(&unit_p->idr_acc_handle); ddi_remove_intr(dip, 0, unit_p->high_iblock_cookie); mutex_destroy(&unit_p->high_mutex); ddi_remove_softintr(unit_p->soft_intr_id); cv_destroy(&unit_p->cv); cv_destroy(&unit_p->power_cv); mutex_destroy(&unit_p->soft_mutex); ddi_soft_state_free(state_head, instance); return (DDI_FAILURE); } /* ddi_create_minor_node creates an entry in an internal kernel table; the actual entry in the file system is created by drvconfig(1) when you run add_drv(1); DDI_PSEUDO seems like a strange node type but we don't seem to fit the other possibilities we will save the instance as our minor # for future availability to write and ioctl routines use string format to generate idrx node name */ (void) sprintf(minor_node_name,"idr%d",instance) ; if (ddi_create_minor_node(dip, minor_node_name, S_IFCHR, instance, DDI_PSEUDO, NULL) == DDI_FAILURE) { cmn_err(CE_CONT,"idr_attach: instance %d: ddi_create_minor_node error!\n",instance) ; ddi_dma_free_handle(&unit_p->dma_handle); ddi_regs_map_free(&unit_p->plx_acc_handle); ddi_regs_map_free(&unit_p->idr_acc_handle); ddi_remove_intr(dip, 0, unit_p->high_iblock_cookie); mutex_destroy(&unit_p->high_mutex); ddi_remove_softintr(unit_p->soft_intr_id); cv_destroy(&unit_p->cv); cv_destroy(&unit_p->power_cv); mutex_destroy(&unit_p->soft_mutex); ddi_soft_state_free(state_head, instance); return (DDI_FAILURE); } /* map config register set temporarily so we can get device id and revision level use the offsets canned in pci.h from sun */ if(pci_config_setup(dip, &unit_p->config_acc_handle) != DDI_SUCCESS) { cmn_err(CE_CONT,"idr_attach: instance %d: pci_config_setup error!\n",instance) ; ddi_dma_free_handle(&unit_p->dma_handle); ddi_regs_map_free(&unit_p->plx_acc_handle); ddi_regs_map_free(&unit_p->idr_acc_handle); ddi_remove_intr(dip, 0, unit_p->high_iblock_cookie); mutex_destroy(&unit_p->high_mutex); ddi_remove_softintr(unit_p->soft_intr_id); cv_destroy(&unit_p->cv); cv_destroy(&unit_p->power_cv); mutex_destroy(&unit_p->soft_mutex); ddi_soft_state_free(state_head, instance); return (DDI_FAILURE); } DPRINT((CE_CONT,"idr_attach: instance %d: ints registered, node created, regs mapped, plx_base = 0x%x, idr_base = 0x%x\n", instance, unit_p->plx_base, unit_p->idr_base)); /* get and save device and vendor ids */ unit_p->dev_and_vendor_id = pci_config_get32(unit_p->config_acc_handle, (off_t)PCI_CONF_VENID); unit_p->revision_id = REV_ID_MASK & pci_config_get32(unit_p->config_acc_handle, (off_t)PCI_CONF_REVID); DPRINT((CE_CONT,"idr_attach: instance %d: vendor & device id = 0x%08x, revision id = 0x%02x\n", instance, unit_p->dev_and_vendor_id, unit_p->revision_id)); /* solaris may not enable the board to respond to non-config pci accesses, so or (probably don't need to or) the appropriate bits into the config command reg to enable slave io and mem access, and master access */ temp = pci_config_get32(unit_p->config_acc_handle, (off_t)PCI_CONF_COMM); pci_config_put32(unit_p->config_acc_handle, (off_t)PCI_CONF_COMM, (uint32_t)(temp | PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME)); pci_config_teardown(&unit_p->config_acc_handle); /* read default properties from idr.conf check for out of range properties and convert to appropraite bit formats for board registers the saved defaults are in a form used directly by the board and driver - the default properties in idr.conf may be flags that don't map directly into board registers if property isn't defined, use the PROPERTY default from idr_io.h */ /* get ignore minphys property if set, we won't call the system's minphys along with idr_minphys, but will rely on idr_minphys alone (and hope we don't run up against the system's allocation limits */ temp = getprop(dip,"ignore_minphys",IGNORE_MINPHYS_DEF); if((temp !=0) && (temp != 1)) { temp = IGNORE_MINPHYS_DEF; cmn_err(CE_CONT,"idr_attach: instance %d: ignore_minphys property out of range!\n", instance); } unit_p->ignore_minphys = temp; DPRINT((CE_CONT,"idr_attach: instance %d: ignore_minphys = 0x%x\n", instance, temp)); /* get and save timer default values */ temp = getprop(dip, "dma_time_def", DMA_TIME_DEF); unit_p->dma_time_def = temp; DPRINT((CE_CONT, "idr_attach: instance %d: dma_time_def = %d\n", instance, temp)); temp = getprop(dip, "rdy_time_def", RDY_TIME_DEF); unit_p->rdy_time_def = temp; DPRINT((CE_CONT, "idr_attach: instance %d: rdy_time_def = %d\n", instance, temp)); temp = getprop(dip, "attn_time_def", ATTN_TIME_DEF); unit_p->attn_time_def = temp; DPRINT((CE_CONT, "idr_attach: instance %d: attn_time_def = %d\n", instance, temp)); /* get the default byte swap property in a sparc system, we must tell the plx chip to swap dma bytes, to reverse the byte swapping that the host always does for 16 bit values if the byte swap property says to swap bytes, we turn byte swapping OFF in the plx part */ temp = getprop(dip, "byte_swap_def", BYTE_SWAP_DEF); if(temp & (uint32_t)0xFFFFFFFE) { temp = BYTE_SWAP_DEF; cmn_err(CE_WARN, "idr_attach: instance %d: byte_swap_def property out of range!\n", instance); } DPRINT((CE_CONT, "idr_attach: instance %d: byte_swap_def = %d\n", instance, temp)); if(temp == 0) unit_p->endian_def = DMA_1_BIG_ENDIAN; else unit_p->endian_def = 0; /* start assembling the mode register default */ unit_p->mode_reg_def = 0; temp = getprop(dip, "speed_def", SPEED_DEF); if((temp != 0) && (temp != 1) && (temp != 2) && (temp != 3)) { temp = SPEED_DEF; cmn_err(CE_WARN, "idr_attach: instance %d: speed_def property out of range!\n", instance); } DPRINT((CE_CONT, "idr_attach: instance %d: speed_def = %d\n", instance, temp)); switch(temp) { case 0: unit_p->mode_reg_def = SPEED_0; break; case 1: unit_p->mode_reg_def = SPEED_1; break; case 2: unit_p->mode_reg_def = SPEED_2; break; case 3: unit_p->mode_reg_def = SPEED_3; break; } temp = getprop(dip, "cycle_pol_def", CYCLE_POL_DEF); if(temp & (uint32_t)0xFFFFFFFE) { temp = CYCLE_POL_DEF; cmn_err(CE_WARN, "idr_attach: instance %d: cycle_pol_def property out of range!\n", instance); } DPRINT((CE_CONT, "idr_attach: instance %d: cycle_pol_def = %d\n", instance, temp)); if(temp == 1) unit_p->mode_reg_def |= CYCLE_POLARITY; temp = getprop(dip, "busy_pol_def", BUSY_POL_DEF); if(temp & (uint32_t)0xFFFFFFFE) { temp = CYCLE_POL_DEF; cmn_err(CE_WARN, "idr_attach: instance %d: busy_pol_def property out of range!\n", instance); } DPRINT((CE_CONT, "idr_attach: instance %d: busy_pol_def = %d\n", instance, temp)); if(temp == 1) unit_p->mode_reg_def |= BUSY_POLARITY; /* assemble read pulse default - ALWAYS INCLUDE GO! */ unit_p->read_pulse_def = GO; temp = getprop(dip, "read_cycle_def", READ_CYCLE_DEF); if(temp & (uint32_t)0xFFFFFFFE) { temp = READ_CYCLE_DEF; cmn_err(CE_WARN, "idr_attach: instance %d: read_cycle_def property out of range!\n", instance); } DPRINT((CE_CONT, "idr_attach: instance %d: read_cycle_def = %d\n", instance, temp)); if(temp == 1) unit_p->read_pulse_def |= SOFT_CYCLE; temp = getprop( dip, "read_acf2_def", READ_ACF2_DEF); if(temp & (uint32_t)0xFFFFFFFE) { temp = READ_ACF2_DEF; cmn_err(CE_WARN, "idr_attach: instance %d: read_acf2_def property out of range!\n", instance); } DPRINT((CE_CONT, "idr_attach: instance %d: read_acf2_def = %d\n", instance, temp)); if(temp == 1) unit_p->read_pulse_def |= ACLO_FCN_2; /* assemble write pulse default - ALWAYS INCLUDE GO! */ unit_p->write_pulse_def = GO; temp = getprop(dip, "write_cycle_def", WRITE_CYCLE_DEF); if(temp & (uint32_t)0xFFFFFFFE) { temp = WRITE_CYCLE_DEF; cmn_err(CE_WARN, "idr_attach: instance %d: write_cycle_def property out of range!\n", instance); } DPRINT((CE_CONT, "idr_attach: instance %d: write_cycle_def = %d\n", instance, temp)); if(temp == 1) unit_p->write_pulse_def |= SOFT_CYCLE; /* start compiling the open, read, and write function bit defaults there is a separate property for each function bit for each of the three conditions -- it would be simlpler to combine all 3 bits into one mask, but it wouldn't read as well, and wouldn't allow using the idr_reg.h bit definitions */ /* start with read function bits */ unit_p->read_fcn_def = 0; temp = getprop(dip, "read_f3_def", READ_F3_DEF); if(temp & (uint32_t)0xFFFFFFFE) { temp = READ_F3_DEF; cmn_err(CE_WARN, "idr_attach: instance %d: read_f3_def property out of range!\n", instance); } DPRINT((CE_CONT, "idr_attach: instance %d: read_f3_def = %d\n", instance, temp)); if(temp == 1) unit_p->read_fcn_def |= FUNCTION_3; temp = getprop(dip, "read_f2_def", READ_F2_DEF); if(temp & (uint32_t)0xFFFFFFFE) { temp = READ_F2_DEF; cmn_err(CE_WARN, "idr_attach: instance %d: read_f2_def property out of range!\n", instance); } DPRINT((CE_CONT, "idr_attach: instance %d: read_f2_def = %d\n", instance, temp)); if(temp == 1) unit_p->read_fcn_def |= FUNCTION_2; temp = getprop(dip, "read_f1_def", READ_F1_DEF); if(temp & (uint32_t)0xFFFFFFFE) { temp = READ_F1_DEF; cmn_err(CE_WARN, "idr_attach: instance %d: read_f1_def property out of range!\n", instance); } DPRINT((CE_CONT, "idr_attach: instance %d: read_f1_def = %d\n", instance, temp)); if(temp == 1) unit_p->read_fcn_def |= FUNCTION_1; /* now write function bits */ unit_p->write_fcn_def = 0; temp = getprop(dip, "write_f3_def", WRITE_F3_DEF); if(temp & (uint32_t)0xFFFFFFFE) { temp = WRITE_F3_DEF; cmn_err(CE_WARN, "idr_attach: instance %d: write_f3_def property out of range!\n", instance); } DPRINT((CE_CONT, "idr_attach: instance %d: write_f3_def = %d\n", instance, temp)); if(temp == 1) unit_p->write_fcn_def |= FUNCTION_3; temp = getprop(dip, "write_f2_def", WRITE_F2_DEF); if(temp & (uint32_t)0xFFFFFFFE) { temp = WRITE_F2_DEF; cmn_err(CE_WARN, "idr_attach: instance %d: write_f2_def property out of range!\n", instance); } DPRINT((CE_CONT, "idr_attach: instance %d: write_f2_def = %d\n", instance, temp)); if(temp == 1) unit_p->write_fcn_def |= FUNCTION_2; temp = getprop(dip, "write_f1_def", WRITE_F1_DEF); if(temp & (uint32_t)0xFFFFFFFE) { temp = WRITE_F1_DEF; cmn_err(CE_WARN, "idr_attach: instance %d: write_f1_def property out of range!\n", instance); } DPRINT((CE_CONT, "idr_attach: instance %d: write_f1_def = %d\n", instance, temp)); if(temp == 1) unit_p->write_fcn_def |= FUNCTION_1; /* now the open function bits, which will be pluggen into the latched function register at open time */ unit_p->latch_reg_def = 0; temp = getprop(dip, "open_f3_def", OPEN_F3_DEF); if(temp & (uint32_t)0xFFFFFFFE) { temp = OPEN_F3_DEF; cmn_err(CE_WARN, "idr_attach: instance %d: open_f3_def property out of range!\n", instance); } DPRINT((CE_CONT, "idr_attach: instance %d: open_f3_def = %d\n", instance, temp)); if(temp == 1) unit_p->latch_reg_def |= FUNCTION_3; temp = getprop(dip, "open_f2_def", OPEN_F2_DEF); if(temp & (uint32_t)0xFFFFFFFE) { temp = OPEN_F2_DEF; cmn_err(CE_WARN, "idr_attach: instance %d: open_f2_def property out of range!\n", instance); } DPRINT((CE_CONT, "idr_attach: instance %d: open_f2_def = %d\n", instance, temp)); if(temp == 1) unit_p->latch_reg_def |= FUNCTION_2; temp = getprop(dip, "open_f1_def", OPEN_F1_DEF); if(temp & (uint32_t)0xFFFFFFFE) { temp = OPEN_F1_DEF; cmn_err(CE_WARN, "idr_attach: instance %d: open_f1_def property out of range!\n", instance); } DPRINT((CE_CONT, "idr_attach: instance %d: open_f1_def = %d\n", instance, temp)); if(temp == 1) unit_p->latch_reg_def |= FUNCTION_1; /* do a soft reset of the plx chip - which also clears the idr logic this will also cause an init to be sent to the device */ plx_soft_reset(unit_p); /* clear unit flags */ unit_p->unit_flags = 0 ; /* set attach complete flag for use in open */ unit_p->unit_attached = 1; ddi_report_dev(dip); return (DDI_SUCCESS); default: /* unrecognized commands may not be error - so only print if debugging is on */ DPRINT((CE_CONT,"idr_attach: instance %d: unrecognized command\n", instance)); return(DDI_FAILURE); } } /* This is a pretty generic getinfo routine as described in the manual. */ /*ARGSUSED*/ static int idr_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) { register int error; register int instance; register struct idr_unit *unit_p; /*LINTED cast OK*/ instance = (int)getminor((dev_t)arg); DPRINT((CE_CONT,"idr_getinfo: instance %d: entering idr_getinfo\n", instance)); switch (infocmd) { case DDI_INFO_DEVT2DEVINFO: unit_p = (struct idr_unit *)ddi_get_soft_state(state_head, instance); if (unit_p == NULL) { *result = NULL; cmn_err(CE_CONT,"idr_getinfo: instance %d: null unit_pointer!\n", instance); error = DDI_FAILURE; } else { *result = unit_p->dip; error = DDI_SUCCESS; } break; case DDI_INFO_DEVT2INSTANCE: /*LINTED cast OK*/ *result = (void *)instance; error = DDI_SUCCESS; break; default: *result = NULL; error = DDI_FAILURE; cmn_err(CE_CONT,"idr_getinfo: instance %d: unrecognized info_command!\n", instance) ; } DPRINT((CE_CONT,"idr_getinfo: instance %d: leaving idr_getinfo\n", instance)); return(error); } /* _init, _info, and _fini support loading and unloading the driver. */ int _init(void) { register int error; DPRINT((CE_CONT,"idr (_init): compiled %s, %s\n", __TIME__, __DATE__)); if ((error = ddi_soft_state_init(&state_head, sizeof (struct idr_unit), 1)) != 0) { DPRINT((CE_CONT,"idr (_init): _init error!, error # = 0x%x\n",error)); return(error); } if ((error = mod_install(&modlinkage)) != 0) { DPRINT((CE_CONT,"idr (_init): mod_install error! error # = 0x%x\n",error)); ddi_soft_state_fini(&state_head); } DPRINT((CE_CONT,"idr (_init): _init done, return value = 0x%x\n",error)); return(error); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } int _fini(void) { int error; if ((error = mod_remove(&modlinkage)) != 0) { DPRINT((CE_CONT,"idr (_fini): mod_remove error! error # = 0x%x\n",error)); return (error); } ddi_soft_state_fini(&state_head); DPRINT((CE_CONT,"idr (_fini): _fini done\n")); return (0); } /* When our driver is unloaded, idr_detach cleans up and frees the resources we allocated in idr_attach. */ static int idr_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { register struct idr_unit *unit_p; int instance; instance = ddi_get_instance(dip); unit_p = (struct idr_unit *)ddi_get_soft_state(state_head, instance); /* test for expected commands */ switch(cmd) { case DDI_SUSPEND: DPRINT((CE_CONT, "idr_detach: instance %d: command is DDI_SUSPEND\n", instance)); mutex_enter(&unit_p->soft_mutex); /* check for open - if so, return failure if not, save state, flag as suspended, return success */ if(unit_p->unit_open) { DPRINT((CE_CONT, "idr_attach: instance %d: unit open, returning DDI_FAILURE\n", instance)); mutex_exit(&unit_p->soft_mutex); return(DDI_FAILURE); } else { DPRINT((CE_CONT, "idr_detach: instance %d: saving state & setting suspended flag\n", instance)); unit_p->saved_plx_endian_reg = PLX_GET32(PLX_ENDIAN_REG); unit_p->saved_plx_int_cstat = PLX_GET32(PLX_INT_CSTAT); unit_p->saved_plx_eeprom_user = PLX_GET32(PLX_EEPROM_USER); unit_p->saved_latched_functions = IDR_GET32(IDR_LATCHED_FUNCTIONS); unit_p->saved_mode = IDR_GET32(IDR_MODE); unit_p->saved_range_low = IDR_GET32(IDR_RANGE_LOW); unit_p->saved_range_mid = IDR_GET32(IDR_RANGE_MID); unit_p->saved_range_high = IDR_GET32(IDR_RANGE_HIGH); unit_p->unit_suspended = 1; mutex_exit(&unit_p->soft_mutex); return(DDI_SUCCESS); } case DDI_DETACH: DPRINT((CE_CONT,"idr_detach: instance %d: command is DDI_DETACH\n", instance)); /* clear attached flag */ unit_p->unit_attached = 0; /* do a soft reset of the plx chip - which also clears the idr logic */ plx_soft_reset(unit_p); /* this is as good a place as any to do phony references to the read dump varaibles to try to absolutely guarantee that the optimizer won't remove our cache flush reads and to try to shut lint up */ read_dump_0 += read_dump_1; /* free the various resources that were allocated in attach */ ddi_remove_minor_node(dip, NULL); ddi_dma_free_handle(&unit_p->dma_handle); ddi_regs_map_free(&unit_p->plx_acc_handle); ddi_regs_map_free(&unit_p->idr_acc_handle); ddi_remove_intr(dip, 0, unit_p->high_iblock_cookie); mutex_destroy(&unit_p->high_mutex); ddi_remove_softintr(unit_p->soft_intr_id); cv_destroy(&unit_p->cv); cv_destroy(&unit_p->power_cv); mutex_destroy(&unit_p->soft_mutex); ddi_soft_state_free(state_head, instance); DPRINT((CE_CONT,"idr_detach: instance %d: detach complete\n", instance)); return (DDI_SUCCESS); default: /* other commands may not be an error - so remain silent unless debugging is on */ DPRINT((CE_CONT, "idr_detach: instance %d: unrecognized command!\n", instance)); return(DDI_FAILURE); } } /* idr_open is called in response to the open() system call */ /*ARGSUSED*/ static int idr_open(dev_t *dev, int openflags, int otyp, cred_t *credp) { int retval = 0; int instance; register struct idr_unit *unit_p; /*LINTED cast OK*/ instance = (int)getminor(*dev); /* get unit structure pointer and make sure driver is (completely) attached */ if((unit_p = (struct idr_unit *)ddi_get_soft_state(state_head, instance)) == NULL) { cmn_err(CE_CONT,"idr_open: instance %d: null unit pointer!\n", instance); return(ENXIO); } if(unit_p->unit_attached == 0) { cmn_err(CE_CONT,"idr open: instance %d: open attempted before attach complete!\n", instance); return(ENXIO); } /* verify that we are being opened as a character device */ if(otyp != OTYP_CHR) { cmn_err(CE_CONT,"idr_open: instance %d: wrong open_type!\n",instance); return(EINVAL); } /* lock unit structure (& flags & registers) */ mutex_enter(&unit_p->soft_mutex); /* check for system suspended (powered down) - if so, wait for cv_broadcast from resume code in attach routine */ while(unit_p->unit_suspended) cv_wait(&unit_p->power_cv, &unit_p->soft_mutex); /* exclusive open device */ if(unit_p->unit_open != 0) { DPRINT((CE_CONT, "idr_open: instance %d: open called when already open!\n", instance)); retval = EBUSY; } else { /* mark device as open and flag as busy (open) for power management (used in detach) */ unit_p->unit_open = 1; /* initialize DVMA and DR11 logic SOFT RESET WILL ALSO CAUSE AN INIT TO BE SENT TO THE DEVICE! if this causes problems, the plx_soft_reset() should be commented out */ plx_soft_reset(unit_p); /* initialize various defaults using values in soft state soft state values were derived from idr.conf properties in idr_attach() */ /* initialize time out values to defaults */ unit_p->dma_time = (clock_t)(drv_usectohz((clock_t)1000000) * unit_p->dma_time_def); unit_p->attn_time = (clock_t)(drv_usectohz((clock_t)1000000) * unit_p->attn_time_def); unit_p->rdy_time = (clock_t)(drv_usectohz((clock_t)1000000) * unit_p->rdy_time_def); /* set read and write pulse and function values to defaults */ unit_p->read_fcn = unit_p->read_fcn_def; unit_p->write_fcn = unit_p->write_fcn_def; unit_p->read_pulse = unit_p->read_pulse_def; unit_p->write_pulse = unit_p->write_pulse_def; /* set the mode and latch registers to their default values */ IDR_PUT32(IDR_LATCHED_FUNCTIONS,unit_p->latch_reg_def); IDR_PUT32(IDR_MODE,unit_p->mode_reg_def); /* set plx chip (9060sd and later) to default endian mode note that normal default is big_endian to un-swap the byte swapping that the memory path does to 16 bit words in SPARC */ PLX_PUT32(PLX_ENDIAN_REG, unit_p->endian_def); IDR_SYNC_0; PLX_SYNC_1; /* clear the various flags in unit_flags */ unit_p->unit_flags = 0; DPRINT((CE_CONT,"idr_open: instance %d: open complete\n", instance)); } mutex_exit(&unit_p->soft_mutex); return (retval); } /* idr_close is called after the last process that has the device open calls close() since we are exclusive open, idr_close will always be calles when the user process calls close (or when the user process exits) */ /*ARGSUSED*/ static int idr_close(dev_t dev, int openflags, int otyp, cred_t *credp) { register struct idr_unit *unit_p; /* get unit pointer and make sure it is not null (big trouble at this point!) */ /*LINTED cast of getminor OK*/ if((unit_p = (struct idr_unit *)ddi_get_soft_state(state_head,(int)getminor(dev))) == NULL) { cmn_err(CE_CONT,"idr_close: null unit pointer!\n"); return(ENXIO); } /* lock unit structure (and flags and registers) */ mutex_enter(&unit_p->soft_mutex); /* reset dma logic, DR11 logic, and flush fifo THIS CAUSES AN INIT TO BE SENT TO THE ATTACHED DEVICE! if the init causes problems the soft reset should be commented out */ plx_soft_reset(unit_p); /* let the next user open the device this also tells power management that we will permit a system suspend */ unit_p->unit_open = 0; mutex_exit(&unit_p->soft_mutex); DPRINT((CE_CONT,"idr_close: instance %d: close complete\n", unit_p->saved_instance)); return (0); } /*ARGSUSED*/ static int idr_read(dev_t dev, struct uio *uiop, cred_t *credp) { register struct idr_unit *unit_p ; int retval ; int instance ; off_t temp_offset; retval = 0 ; /*LINTED cast OK*/ instance = (int)getminor(dev); if((unit_p = (struct idr_unit *)ddi_get_soft_state(state_head,instance)) == NULL) { cmn_err(CE_CONT,"idr_read: instance: %d: unit_pointer null!\n",instance) ; return(ENXIO); } DPRINT((CE_CONT,"idr_read: instance %d: entering read routine\n", instance)); /* since we are exclusive open - we shouldn't need a mutex -- we will leave it for strategy to pick up */ /* clear the timeout, multicycle, parity, sig received, and "waiting for" flags. strategy will set the waiting for dma or eor flag. CLEAR_FLAGS = ~(a bunch of flags or'd together) */ unit_p->unit_flags &= IDR_CLEAR_FLAGS; /* set input flag */ unit_p->unit_flags |= IDR_INPUT; /* save the offset in uio - we will restore it after the call to physio if we dont, eventually the iova counter will overflow, and the system will return errors on further write calls this counter may be appropriate for "seekable" devices, but not for us! */ temp_offset = uiop->uio_offset; /* call physio (and strategy) to start the read. NULL means use buffer from pool */ retval = physio(idr_strategy, (struct buf *)NULL, dev, B_READ, idr_minphys, uiop); uiop->uio_offset = temp_offset; /* check flags for timeout or multicycle error or sig received most dr11 devices do not check parity on the dr11 cable most ikon dr11s check dr11 cable parity and flag an error we will not report parity errors as read or write errors here, since most applications (that don't support parity) will cause the flag to set often parity errors can be tested for by examining the driver flag word, or the board's flag register using the IDRIO_GET_NEW_FLAGS ioctl if parity checking is desired within the read or write routine, add the following code and re make the driver if(unit_p->unit_flags & IDR_PAR_ERR) { retval = EIO; cmn_err(CE_CONT,"idr_read: istance %d: parity error!\n", instance); } */ if(unit_p->unit_flags & (IDR_DVMA_TIMEOUT|IDR_EOR_TIMEOUT)) { retval = EIO; cmn_err(CE_CONT,"idr_read: instance %d: DMA timeout! \n",instance); } if(unit_p->unit_flags & IDR_MCYL_ERR) { retval = EIO; cmn_err(CE_CONT,"idr_read: instance %d: Multicycle error! \n",instance); } if(unit_p->unit_flags & IDR_SIG_RECEIVED) { retval = EINTR; cmn_err(CE_CONT,"idr_read: instance %d: signal received! \n",instance); } DPRINT((CE_CONT,"idr_read: instance %d: leaving read routine\n", instance)); return (retval); } /*ARGSUSED*/ static int idr_write(dev_t dev, struct uio *uiop, cred_t *credp) { register struct idr_unit *unit_p; int retval; int instance; off_t temp_offset; retval = 0; /*LINTED cast OK*/ instance = (int)getminor(dev); if((unit_p = (struct idr_unit *)ddi_get_soft_state(state_head, instance)) == NULL) { cmn_err(CE_CONT,"idr_write: instance %d: unit pointer NULL!\n", instance); return(ENXIO); } DPRINT((CE_CONT,"idr_write: instance %d: entering write routine\n", instance)); /* we will leave the mutex for strategy */ /* clear the timeout, multicycle error, parity, sig received, and "waiting for" flags strategy will set the waiting for dvma or eor flag. */ unit_p->unit_flags &= IDR_CLEAR_FLAGS; /* clear the input flag (=output) */ unit_p->unit_flags &= ~IDR_INPUT; /* we will use idr_minphys number and a system buf - see idr_read */ /* save the offset in uio - we will restore it after the call to physio if we dont, eventually the iova counter will overflow, and the system will return errors on further write calls this counter may be appropriate for "seekable" devices, but not for us! */ temp_offset = uiop->uio_offset; retval = physio(idr_strategy, (struct buf *)NULL, dev, B_WRITE, idr_minphys, uiop); uiop->uio_offset = temp_offset; /* check flags for timeout or multicycle error or sig received and parity error if desired - see idr_read for description of parity error flag */ if(unit_p->unit_flags & (IDR_DVMA_TIMEOUT|IDR_EOR_TIMEOUT)) { retval = EIO; cmn_err(CE_CONT,"idr_write: instance %d: DMA timeout! \n",instance); } if(unit_p->unit_flags & IDR_MCYL_ERR) { retval = EIO; cmn_err(CE_CONT,"idr_write: instance %d: Multicycle error! \n", instance); } if(unit_p->unit_flags & IDR_SIG_RECEIVED) { retval = EINTR; cmn_err(CE_CONT,"idr_write: instance %d: signal received! \n", instance); } DPRINT((CE_CONT,"idr_write: instance %d: leaving write routine\n", instance)); return (retval); } /* note that the ioctl routine treats arg as a pointer */ /*ARGSUSED*/ static int idr_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp, int *rvalp) { register struct idr_unit *unit_p; uint32_t bits; /* will contain incoming or outgoing arg value */ uint32_t lt; /* temp 32 bit */ int retval = 0; /* return value (errno) for system call */ u_int command; /* extracted ioctl command */ int instance; /* our instance - from softstate */ clock_t waitval; /* value returned by cv_timedwait_sig */ clock_t time_now; /* time in ticks since last re-boot */ int i; /* generic counter */ uint32_t return_array[19]; /* will contain copy of registers to return to caller */ /*LINTED cast OK*/ instance = (int)getminor(dev); if((unit_p = (struct idr_unit *)ddi_get_soft_state(state_head, instance)) == NULL) { cmn_err(CE_CONT,"ir_ioctl: instance %d: unit_pointer NULL!\n",instance); return(ENXIO); } DPRINT((CE_CONT,"idr_ioctl: instance %d: entering ioctl routine, command = 0x%x\n", instance, cmd)); /* lock our unit structure */ mutex_enter(&unit_p->soft_mutex); /* get command */ command = cmd & IDRIO_CMD_MASK; /* get first word of arg (even if we are eventually doing a copy out) this saves coding a copyin for each case we can use standard copyin since we don't do recursive ioctl calls */ copyin((void *)arg, (void *)&bits, sizeof(bits)) ; DPRINT((CE_CONT,"idr_ioctl: instance %d: arg = %p, *arg = 0x%x\n", instance, arg, bits)); /* use MASK to strip the count from cases */ #define MASK IDRIO_CMD_MASK switch(command) { case MASK & IDRIO_SET_MODE: /* set operating mode using old sbus bit */ /* definitions to preserve compatibility with */ /* existing applications */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at set mode\n", instance)); lt = bits & IDR_SPEED_MASK; /* speed bits are in the same place */ if(bits & IDR_RDYT) lt |= LONG_READY; if(bits & IDR_BDIS) lt |= DISABLE_CRQ_B; if(bits & IDR_CRQP) lt |= CYCLE_POLARITY; if(bits & IDR_BSYP) lt |= BUSY_POLARITY; if(bits & IDR_RDISX) lt |= DISABLE_RANGE; IDR_PUT32(IDR_MODE,lt); /* check dma byte swap mode - request for swapping will turn off big endian mode (SPARC) SPARC swaps bytes between dma and memory, so big_endian = swap again, to give un-swapped data */ if(bits & IDR_SWAP) PLX_PUT32(PLX_ENDIAN_REG, PLX_GET32(PLX_ENDIAN_REG) & ~DMA_1_BIG_ENDIAN); else PLX_PUT32(PLX_ENDIAN_REG, PLX_GET32(PLX_ENDIAN_REG) | DMA_1_BIG_ENDIAN); IDR_SYNC_0; PLX_SYNC_1; break; case MASK & IDRIO_IMM_FCN: /* set function bits immediately */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at immediate funtion\n", instance)); /* just mask the function bits and stuff them directly into the latched function register - the other register bits should all be either zero or don't care at this point! the pci and sbus boards have the funtions bits in the same places in their respective registers - so no bit fiddling necessary */ IDR_PUT32(IDR_LATCHED_FUNCTIONS, bits & FUNCTION_MASK); IDR_SYNC_1; break; case MASK & IDRIO_READ_FCN: /* save fcn bits for use at read start */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at read function\n", instance)); unit_p->read_fcn = (bits & FUNCTION_MASK); break; case MASK & IDRIO_WRITE_FCN: /* save fcn bits for use at write start */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at write function\n", instance)); unit_p->write_fcn = (bits & FUNCTION_MASK); break; case MASK & IDRIO_IMM_PULSE: /* issue pulses asap */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at immediate pulse\n", instance)); /* wait for attention off if issuing go pulse wait 100 us max - ?????? */ if(bits & IDR_GO) { i = 0; while((IDR_GET32(IDR_STATUS) & ATTENTION)&&(i<100)) { drv_usecwait(1) ; i++ ; } if(IDR_GET32(IDR_STATUS) & ATTENTION) { cmn_err(CE_CONT,"idr_ioctl: instance %d at IMM_PULSE: attempt to issue GO while ATTENTION true!\n", instance) ; retval = EIO ; goto IOCTLEXIT ; } } /* fiddle bits to maintain sbus driver compatibility */ lt = 0; if(bits & IDR_TERM) lt |= SET_READY; if(bits & IDR_INIT) lt |= DEVICE_INIT; if(bits & IDR_ACF2) lt |= ACLO_FCN_2; if(bits & IDR_CYCL) lt |= SOFT_CYCLE; if(bits & IDR_GO) lt |= GO; IDR_PUT32(IDR_DEVICE_PULSES, lt); IDR_SYNC_0; /* we will allow a master clear pulse here - since the sbus driver did but it should not be used lightly it clears the idr logic only, not the plx logic we will also permit a reset attention flag pulse - for compatibility */ lt = 0; if(bits & IDR_RATN) lt |= RESET_ATTENTION_FLAG; if(bits & IDR_MCLR) lt |= MASTER_CLEAR; IDR_PUT32(IDR_116_PULSES, lt); IDR_SYNC_1; break ; case MASK & IDRIO_READ_PULSE: /* save pulses for read start (in pci format) */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at read pulse\n", instance)); /* fiddle bits to maintain sbus driver compatibility */ lt = 0; if(bits & IDR_TERM) lt |= SET_READY; if(bits & IDR_INIT) lt |= DEVICE_INIT; if(bits & IDR_ACF2) lt |= ACLO_FCN_2; if(bits & IDR_CYCL) lt |= SOFT_CYCLE; if(bits & IDR_GO) lt |= GO; unit_p->read_pulse = lt ; break ; case MASK & IDRIO_WRITE_PULSE: /* save pulses for write start */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at write pulse\n", instance)); /* fiddle bits to maintain sbus driver compatibility */ lt = 0; if(bits & IDR_TERM) lt |= SET_READY; if(bits & IDR_INIT) lt |= DEVICE_INIT; if(bits & IDR_ACF2) lt |= ACLO_FCN_2; if(bits & IDR_CYCL) lt |= SOFT_CYCLE; if(bits & IDR_GO) lt |= GO; unit_p->write_pulse = lt ; break ; case MASK & IDRIO_SET_DMA_TIME: /* set dma timeout parameter - in ticks */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at set dma time\n", instance)); if(bits < DMA_TIME_MIN ) bits = DMA_TIME_MIN ; /* not less than min */ if(bits > DMA_TIME_MAX ) bits = DMA_TIME_MAX ; /* or more than max */ unit_p->dma_time = (clock_t)(bits * drv_usectohz((clock_t)1000000)); break ; case MASK & IDRIO_SET_ATTN_TIME: /* set wait for attention timeout value */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at set attn time\n", instance)); if(bits < ATTN_TIME_MIN) bits = ATTN_TIME_MIN ; if(bits > ATTN_TIME_MAX) bits = ATTN_TIME_MAX ; unit_p->attn_time = (clock_t)(bits * drv_usectohz((clock_t)1000000)); break ; case MASK & IDRIO_SET_RDY_TIME: /* set wait for ready timeout value */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at set rdy time\n", instance)); if(bits < RDY_TIME_MIN) bits = RDY_TIME_MIN ; if(bits > RDY_TIME_MAX) bits = RDY_TIME_MAX ; unit_p->rdy_time = (clock_t)(bits * drv_usectohz((clock_t)1000000)); break ; case MASK & IDRIO_ATTN_WAIT: /* wait for attention to set attf */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at attn wait\n", instance)); /* clear the "waiting-for" flags in the soft state these flags are maintained for compatibility with the sbus driver they are probably not necessary */ unit_p->unit_flags &= IDR_CLEAR_FLAGS; /* if the attention flag is already set, clear it and return */ if(IDR_GET32(IDR_FLAGS) & ATTENTION_FLAG) { DPRINT((CE_CONT,"idr_ioctl: instance %d: at attn wait, attf already set, returning w/out waiting\n", instance)); IDR_PUT32(IDR_116_PULSES, RESET_ATTENTION_FLAG); IDR_SYNC_0; goto IOCTLEXIT; } /* set "waiting-for" flag */ unit_p->unit_flags |= IDR_ATTN_WAIT; /* get current time in ticks */ time_now = ddi_get_lbolt(); /* enable attention flag interrupts in the idr logic, and local ints in the plx logic we have to or in the idr bits, since the speed bits are also in that register */ IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) | ATTENTION_INT_ENB); IDR_SYNC_1; PLX_PUT32(PLX_INT_CSTAT, PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE); PLX_SYNC_0; /* snooze until attention interrupt, timeout, or signal */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at attn wait, time_now = %d, unit_p->attn_time = %d\n", instance, time_now, unit_p->attn_time)); waitval = cv_timedwait_sig(&unit_p->cv, &unit_p->soft_mutex, (clock_t)(time_now + unit_p->attn_time)); /* make sure interrupt enables are off - we may have gotten here via signal or timeout */ IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) & ~ATTENTION_INT_ENB); IDR_SYNC_1; PLX_PUT32(PLX_INT_CSTAT, 0); PLX_SYNC_0; /* find out why we woke up */ if(waitval < 0) { /* timeout may not be an error - so no error msg let caller test flags for timeout */ DPRINT((CE_CONT,"idr_ioctl: instance %d: timeout waiting for attention!\n", instance)); unit_p->unit_flags |= IDR_ATTN_TIMEOUT; retval = EIO; goto IOCTLEXIT; } if(waitval == 0) { /* signal is not exactly an error - but send to console log return EINTR to caller can decide what to do */ cmn_err(CE_CONT,"idr_ioctl: instance %d: signal while waiting for attention!\n", instance); unit_p->unit_flags |= IDR_SIG_RECEIVED; retval = EINTR; goto IOCTLEXIT; } /* clear attention flag */ IDR_PUT32(IDR_116_PULSES, RESET_ATTENTION_FLAG); IDR_SYNC_0; break; case MASK & IDRIO_RDY_WAIT: /* wait for REDY to go true from attn or eor */ /* if already set - just return */ /* look at flags to figure out why redy was */ /* set. if we were waiting for eor and got */ /* an attention also - the attn flag is left */ /* on since it might represent an incoming */ /* attention that followed the block xfer */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at ready wait\n", instance)); unit_p->unit_flags &= IDR_CLEAR_FLAGS; if(IDR_GET32(IDR_STATUS) & READY) { DPRINT((CE_CONT,"idr_ioctl: instance %d: at ready wait - ready already set\n", instance)); goto RDYWAITEXIT; } /* set ready wait flag for future reference and sbus compatibility */ unit_p->unit_flags |= IDR_RDY_WAIT; /* get current time in ticks */ time_now = ddi_get_lbolt(); /* enable attention and eor flag interrupts in the idr logic, and local ints in the plx logic we have to or in the idr bits, since the speed bits are also in that register */ IDR_PUT32(IDR_LATCHED_FUNCTIONS, (IDR_GET32(IDR_LATCHED_FUNCTIONS) | ATTENTION_INT_ENB | END_OF_RANGE_INT_ENB)); IDR_SYNC_1; PLX_PUT32(PLX_INT_CSTAT, (PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE)); PLX_SYNC_0; /* snooze until attention interrupt, timeout, or signal */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at ready wait, time_now = %d, unit_p->rdy_time = %d\n", instance, time_now, unit_p->rdy_time)); waitval = cv_timedwait_sig(&unit_p->cv, &unit_p->soft_mutex, (clock_t)(time_now + unit_p->rdy_time)); /* make sure interrupt enables are off - we may have gotten here via signal or timeout */ IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) & ~(ATTENTION_INT_ENB | END_OF_RANGE_INT_ENB)); IDR_SYNC_1; PLX_PUT32(PLX_INT_CSTAT, 0); PLX_SYNC_0; /* find out why we woke up */ if(waitval < 0) { /* timeout may not be an error - so no error msg let caller test flags for timeout */ DPRINT((CE_CONT,"idr_ioctl: instance %d: timeout waiting for ready!\n", instance)); unit_p->unit_flags |= IDR_RDY_TIMEOUT; retval = EIO; goto IOCTLEXIT; } if(waitval == 0) { /* signal is not exactly an error - but send to console log return EINTR to caller can decide what to do */ cmn_err(CE_CONT,"idr_ioctl: instance %d: signal while waiting for ready!\n", instance); unit_p->unit_flags |= IDR_SIG_RECEIVED; retval = EINTR; goto IOCTLEXIT; } RDYWAITEXIT: /* examine dr11 flag bits to figure out why ready was set if attf is set and eor is not - clear attf */ if((IDR_GET32(IDR_FLAGS) & ATTENTION_FLAG) && !(IDR_GET32(IDR_FLAGS) & END_OF_RANGE_FLAG)) { DPRINT((CE_CONT,"idr_ioctl: instance %d: at ready wait - attf w/out eorf\n", instance)); IDR_PUT32(IDR_116_PULSES, RESET_ATTENTION_FLAG); IDR_SYNC_0; } /* always clear eorf */ IDR_PUT32(IDR_116_PULSES, RESET_EOR_FLAG); IDR_SYNC_1; break; case MASK & IDRIO_GET_STATUS: /* return dr11 status register - in sbus format */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at get status\n", instance)); /* get flags and status registers and fiddle bits to look like sbus status register */ bits = 0; lt = IDR_GET32(IDR_FLAGS); if(lt & END_OF_RANGE_FLAG) bits |= IDR_EORF; if(lt & ATTENTION_FLAG) bits |= IDR_ATTF; if(lt & MULTI_CYCLE_ERROR_FLAG) bits |= IDR_MCER; lt = IDR_GET32(IDR_STATUS); if(lt & READY) bits |= IDR_REDY; if(lt & ATTENTION) bits |= IDR_ATTN; /* dr11 status bits are in the same place in sbus and pci */ bits |= (lt & STATUS_MASK); copyout((void *)&bits,(void *)arg,sizeof(bits)); break; case MASK & IDRIO_GET_RANGE: /* gets 24 bit range counter value */ /* in raw form: = word count minus 1 */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at get range\n", instance)); bits = ((IDR_GET32(IDR_RANGE_HIGH) & 0xFF) << 16) | ((IDR_GET32(IDR_RANGE_MID) & 0xFF) << 8) | (IDR_GET32(IDR_RANGE_LOW) & 0xFF); copyout((void *)&bits, (void *)arg, sizeof(bits)); break; case MASK & IDRIO_GET_REGS: /* get most of the board's registers */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at get regs\n", instance)); return_array[0] = unit_p->dev_and_vendor_id; /* device and vendor id */ return_array[1] = unit_p->revision_id; /* revision id */ return_array[2] = PLX_GET32(PLX_INT_CSTAT); /* interrupt control-status */ return_array[3] = PLX_GET32(PLX_EEPROM_USER); /* eeprom control and user bits */ return_array[4] = IPLX_GET32(IPLX_DMA_MODE_1); /* dma mode reg 1 */ return_array[5] = IPLX_GET32(IPLX_DMA_PCI_ADD_1); /* dma pci add 1 */ return_array[6] = IPLX_GET32(IPLX_DMA_LOC_ADD_1); /* dma local add 1 */ return_array[7] = IPLX_GET32(IPLX_DMA_COUNT_1); /* dma transfer count 1 */ return_array[8] = IPLX_GET32(IPLX_DMA_DESC_PTR_1); /* dma descriptor pointer 1 */ return_array[9] = IPLX_GET32(IPLX_DMA_CMD_STAT_BOTH); /* dma command/status - both channels */ return_array[10]= IDR_GET32(IDR_LATCHED_FUNCTIONS) & 0xFF; /* idr latched functions register */ return_array[11]= IDR_GET32(IDR_FLAGS) & 0xFF; /* idr flags register */ return_array[12]= IDR_GET32(IDR_STATUS) & 0xFF; /* idr status register */ return_array[13]= IDR_GET32(IDR_MODE) & 0xFF; /* idr mode register */ return_array[14]= IDR_GET32(IDR_RANGE_LOW) & 0xFF; /* dr11 range register low byte */ return_array[15]= IDR_GET32(IDR_RANGE_MID) & 0xFF; /* dr11 range register middle byte */ return_array[16]= IDR_GET32(IDR_RANGE_HIGH) & 0xFF; /* dr11 range register high byte */ return_array[17]= IDR_GET32(IDR_FIFO_STATUS) & 0xFF; /* idr fifo status bits */ /* reading the data in lines requires clearing the input fifo, forcing a dr11 input word into the input fifo, and then reading in */ IDR_PUT32(IDR_116_PULSES, CLEAR_INPUT_FIFO); IDR_SYNC_0; IDR_PUT32(IDR_116_PULSES, WRITE_INPUT_FIFO); IDR_SYNC_1; return_array[18]= IDR_GET32(IDR_DATA_IN) & 0xFFFF; /* dr11 data input register - 16 bits */ copyout((void *)return_array, (void *)arg, sizeof(return_array)); break; case MASK & IDRIO_GET_FLAGS: /* return unit flags longword to caller */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at get flags, flags = 0x%x\n", instance, unit_p->unit_flags)); bits = unit_p->unit_flags; copyout((void *)&bits, (void *)arg, sizeof(bits)); break; case MASK & IDRIO_DATA_OUT: /* output 16 bit word to data out reg */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at data out, data = 0x%x\n", instance, bits)); /* first we clear the output fifo, then write to it, then force a read from it into the output data latches */ IDR_SYNC_0; IDR_PUT32(IDR_116_PULSES, CLEAR_OUTPUT_FIFO); IDR_SYNC_1; IDR_PUT32(IDR_DATA_OUT, bits); IDR_SYNC_2; IDR_PUT32(IDR_116_PULSES, READ_OUTPUT_FIFO); IDR_SYNC_3; break; case MASK & IDRIO_DATA_IN: /* read dr11 input data register */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at data in\n", instance)); /* clear the input fifo, force a write into it from the data in lines then read the fifo */ IDR_SYNC_0; IDR_PUT32(IDR_116_PULSES, CLEAR_INPUT_FIFO); IDR_SYNC_1; IDR_PUT32(IDR_116_PULSES, WRITE_INPUT_FIFO); IDR_SYNC_2; bits = IDR_GET32(IDR_DATA_IN) & 0xFFFF; copyout((void *)&bits, (void *)arg, sizeof(bits)); break; case MASK & IDRIO_SET_RANGE: /* set dr11 range counter to desired value */ /* count is not adjusted - calling argument */ /* will be plugged directly into range */ /* arg should be word count minus one!!! */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at set range, range = 0x%x\n", instance, bits)); /* make sure that requested count will fit in our range counter */ if(bits > IDR_DR11_MAXBLOCK) { cmn_err(CE_CONT,"idr_ioctl: instance %d at SET_RANGE - range count too big! \n", instance); retval = EINVAL; goto IOCTLEXIT; } IDR_PUT32(IDR_RANGE_HIGH, (bits >> 16)); IDR_PUT32(IDR_RANGE_MID, (bits >> 8)); IDR_PUT32(IDR_RANGE_LOW, bits); IDR_SYNC_0; break; case MASK & IDRIO_AUTO: /* set automatic mode (default) */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at idr auto\n", instance)); unit_p->unit_flags &= ~IDR_MANUAL; break; case MASK & IDRIO_MANUAL: /* set manual mode */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at idr manual\n", instance)); unit_p->unit_flags |= IDR_MANUAL; break; case MASK & IDRIO_START_READ: /* enables block read in manual mode */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at start read\n", instance)); /* make sure that the "end" flags are clear and reset error flags also clear the input fifo */ IDR_PUT32(IDR_116_PULSES, (RESET_ATTENTION_FLAG | RESET_EOR_FLAG | RESET_ERROR_FLAGS | CLEAR_INPUT_FIFO)); IDR_SYNC_0; /* make sure dma rqs are off, switch to input mode, and enable dma the plx dma logic will be set up and enabled by strategy */ IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) & ~IDR_DMA_ENABLE); IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) | DMA_INPUT_MODE); IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) | IDR_DMA_ENABLE); IDR_SYNC_1; break; case MASK & IDRIO_START_WRITE: /* enables block write in man mode */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at start write\n", instance)); /* make sure "end" flags are clean and reset error flags and output fifo */ IDR_PUT32(IDR_116_PULSES, (RESET_ATTENTION_FLAG | RESET_EOR_FLAG | RESET_ERROR_FLAGS | CLEAR_OUTPUT_FIFO)); IDR_SYNC_0; /* make sure dma rqs are off, switch to output mode, and enable dma the plx dma logic will be set up and enabled by strategy */ IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) & ~IDR_DMA_ENABLE); IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) & ~DMA_INPUT_MODE); IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) | IDR_DMA_ENABLE); IDR_SYNC_1; break; case MASK & IDRIO_BLOCK_END: /* terminate overall block transfer */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at block end\n", instance)); /* turn off dma enable bit and interrupt masks interrupt masks should already be off */ IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) & ~(END_OF_RANGE_INT_ENB | ATTENTION_INT_ENB | IDR_DMA_ENABLE)); IDR_SYNC_1; /* make sure ready is true */ IDR_PUT32(IDR_DEVICE_PULSES, SET_READY); IDR_SYNC_2; /* check for multicycle error or parity error and flag in unit structure parity error will occur frequently when connected to device that don't support parity (most!) so don't do a cmn_err print */ if(IDR_GET32(IDR_FLAGS) & PARITY_ERROR_FLAG) { DPRINT((CE_CONT,"idr_ioctl: instance %d: at BLOCK_END, parity error!\n", instance)); unit_p->unit_flags |= IDR_PAR_ERR; } if(IDR_GET32(IDR_FLAGS) & MULTI_CYCLE_ERROR_FLAG) { cmn_err(CE_CONT,"idr_ioctl: instance %d: at BLOCK_END, multi-cycle error!\n", instance); unit_p->unit_flags |= IDR_MCYL_ERR; } /* just to be sure, force off plx dma and interrupt enable bits */ PLX_PUT32(PLX_INT_CSTAT, 0); PLX_SYNC_0; IPLX_PUT32(IPLX_DMA_CMD_STAT_BOTH, 0); IDR_SYNC_1; break; case MASK & IDRIO_DEV_AND_VEND_ID: /* return vendor id in low 16 bits, device id in high 16 bits */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at dev and vend id\n", instance)); bits = unit_p->dev_and_vendor_id; copyout((void *)&bits, (void *)arg, sizeof(bits)); break; case MASK & IDRIO_REVISION_ID: /* return board revision id */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at revision id\n", instance)); bits = unit_p->revision_id; copyout((void *)&bits, (void *)arg, sizeof(bits)); break; case MASK & IDRIO_SET_NEW_MODE: /* direct write to pci dr11 mode register */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at set new mode\n", instance)); IDR_PUT32(IDR_MODE, bits); IDR_SYNC_0; break; case MASK & IDRIO_GET_NEW_STATUS: /* direct read of pci dr11 status register */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at get new status\n", instance)); bits = IDR_GET32(IDR_STATUS) & 0xFF; copyout((void *)&bits, (void *)arg, sizeof(bits)); break; case MASK & IDRIO_GET_NEW_FLAGS: /* direct read of pci dr11 flags register */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at get new flags\n", instance)); bits = IDR_GET32(IDR_FLAGS) & 0xFF; copyout((void *)&bits, (void *)arg, sizeof(bits)); break; case MASK & IDRIO_MASTER_CLEAR: /* do a total reset of the board - don't try this at home! */ DPRINT((CE_CONT,"idr_ioctl: instance %d: at master clear\n", instance)); /* call soft reset to completely reset the board - including the dr11 mode register this will also cause an init pulse to be sent to the attached device */ plx_soft_reset(unit_p); break; default: /* an unrecognized ioctl command may not be an error - we may get probed by pipe logic w/ a generic TCGETA ioctl. return an error so we don`t look like a terminal, but don`t print an error. */ retval = EINVAL; break; } IOCTLEXIT: /* release mutex */ mutex_exit(&unit_p->soft_mutex) ; return (retval); } /* this is the high level interrupt catcher it quiets the board and requests a soft interrupt, which does a cv_sig to wake up the top half of the driver this was done this way to avoid recursive mutex enter panics if the interrupt happened to be at the same level as the scheduler - which is possible since the interrupt priority is assigned based on calss code (at least for now), and it is hard to predict also - it seems that the kernel function that we can call to test for a high level interrupt doesn't tell us if the level is the same as the scheduler - only if it is higher - and the problem occurs if the levels are the same we choose to implement a high level handler in all cases to avoid having to worry about the problem - it should work regardless of current or future interrupt priority assignments we aren't allowed to call any system services from within a high level interrupt handler - except mutex_enter and _exit, and ddi_trigger_softintr the argument is a pointer at the soft state structure for this instance */ static u_int idr_high_intr(caddr_t arg) { struct idr_unit *unit_p; /* will point at our soft state */ register struct buf *bp; /* may point to buffer */ uint32_t temp; /* just what it says */ u_int int_serviced = DDI_INTR_UNCLAIMED; /* tells kernel if our interrupt*/ int soft_intr_needed = 0; /* 1 if we need a soft interrupt*/ uint32_t flags; /* get unit pointer */ /*LINTED cast ok*/ unit_p = (struct idr_unit *)arg; /* grab high-level mutex to lock our soft intr routine */ mutex_enter(&unit_p->high_mutex); /* clear flag that tells soft intr routine that we called it - soft intr code may get polled for other reasons must not touch this flag unless within mutex */ unit_p->soft_intr_req = 0; /* get plx interrupt control/status register and check for interrupt look for master interrupt enable bit and either local int rq or dma int rq */ temp = IPLX_GET32(IPLX_INT_CSTAT); if((temp & PCI_INTERRUPT_ENABLE) && ((temp & LOCAL_INTERRUPT) || (temp & DMA_1_INTERRUPT))) { /* let the kernel polling routine know that we accept the interrupt and flag the need to issue a soft intr request - this could probably be done within the mutex, but the sample code does it outside for some reason, and we will follow the example also let our soft intr routine know that it has been specifically triggered, not just polled because of someone else's soft interrupt */ int_serviced = DDI_INTR_CLAIMED; soft_intr_needed = 1; unit_p->soft_intr_req = 1; /* turn off interrupts now - but leave dma enabled if it is not already off in input mode, it is possible to terminate a block before the dr11 or dma range count is exhausted, by pulsing attention the idr portion of the logic will hold off the attention interrupt until the big fifo is empty, but there may still be a few long words in the plx fifo we will leave dma on (if it is still on) in the plx logic, and turn it off later in strategy */ IPLX_PUT32(IPLX_INT_CSTAT, 0); IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) & ~(END_OF_RANGE_INT_ENB | ATTENTION_INT_ENB )); IDR_SYNC_1; /* figure out why we were waiting for an interrupt unfortunately, we can't treat all interrupts the same way */ flags = unit_p->unit_flags & (IDR_DVMA_WAIT | IDR_EOR_WAIT | IDR_ATTN_WAIT | IDR_RDY_WAIT) ; switch(flags) { case IDR_DVMA_WAIT: /* we are waiting for dma t/c interrupt - attention interrupts will also be enabled, so we will have to figure out what caused the interrupt and whether the transfer is complete */ case IDR_EOR_WAIT: /* or we are waiting for a dr11 end of range in auto output mode, eor is the desired interrupt in input mode, or manual modr (either in or out), dma tc is the desired interrupt */ /* since we know that strategy causes us to be called, we have a buffer pointer, get it from unit structure */ bp = unit_p->buf_p; /* if we are in automatic mode, turn off the idr dma enable now, otherwise, leave it on until block end ioctl */ if(!(unit_p->unit_flags & IDR_MANUAL)) IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) & ~IDR_DMA_ENABLE); IDR_SYNC_1; /* if we are waiting for an output interrupt, force the dma logic off, since either we are done with dma, or an attention has terminated the dr11 transfer, and any further output data is destined for the bit bucket if we are doing input, the attention or eor interrupt will be held off until the big fifos are empty, but there may still be a few long words in the plx fifos, so leave dma on for now - strategy will clean things up */ if(!(bp->b_flags & B_READ)) IPLX_PUT32(IPLX_DMA_CMD_STAT_BOTH, 0); if(!((IPLX_GET32(IPLX_DMA_CMD_STAT_BOTH) & DMA_1_DONE) || (IDR_GET32(IDR_FLAGS) & END_OF_RANGE_FLAG))) { /* if not eor or end of range, we must have been terminated by an attention pulse - in this case, do not save the attn flag */ IDR_PUT32(IDR_116_PULSES, RESET_ATTENTION_FLAG); IDR_SYNC_0; } if(unit_p->unit_flags & IDR_EOR_WAIT) { /* if waiting for dr11 eor, turn it off - if not, it may be useful later, so leave it on for now */ IDR_PUT32(IDR_116_PULSES, RESET_EOR_FLAG); IDR_SYNC_0; } /* turn off "waiting for" flags for dma t/c and eor */ unit_p->unit_flags &= ~(IDR_DVMA_WAIT | IDR_EOR_WAIT); break; case IDR_ATTN_WAIT: /* we are waiting for an attention interrupt i would like to make sure attf is set, but there is no way to flag an error, since no buf, and i can't call cmn_err from high level, so we will just reset the "waiting for" flag the attention flag gets reset in the ioctl code we could do it here, but we will stick with the way it was done in the Sbus driver */ unit_p->unit_flags &= ~IDR_ATTN_WAIT; break; case IDR_RDY_WAIT: /* waiting for ready - either attention or eor like attn wait, we could check for either flag set and report an error if neither was set (serious hardware error) but there is no good way to do so we will just turn off the "waiting for" flag to try to keep some similarity with the Sbus driver */ unit_p->unit_flags &= ~IDR_RDY_WAIT; break; } } mutex_exit(&unit_p->high_mutex); /* if we decided it was our interrupt, trigger a soft interrupt */ if(soft_intr_needed) ddi_trigger_softintr(unit_p->soft_intr_id); return(int_serviced); } /* this is the soft interrupt routine that issues the actual wakeup signal to strategy or the ioctl code we do it this way because the "bios" may assign us an interrupt priority based on our class code that is the same as the scheduler (unfortunately the kernel function that can be used to test for a high-level interrupt fails if the levels are the same!) this can cause a recursive mutex enter panic if the interrupt code ends up running on the same thread as the waiting code */ static u_int idr_soft_intr(caddr_t arg) { struct idr_unit *unit_p; u_int int_serviced = DDI_INTR_UNCLAIMED; int signal_needed = 0; /*LINTED cast ok*/ unit_p = (struct idr_unit *)arg; DPRINT((CE_CONT,"idr_soft_intr: instance %d: entering soft_intr\n", unit_p->saved_instance)); /* get high level mutex and test for our soft interrupt */ mutex_enter(&unit_p->high_mutex); if(unit_p->soft_intr_req) { /* turn off soft int rq'd flag so we don't accept a later soft intr that isn't ours */ unit_p->soft_intr_req =0; /* tell later code to signal waiting routine and indicate to soft intr polling code that we accept the interrupt */ signal_needed = 1; int_serviced = DDI_INTR_CLAIMED; } mutex_exit(&unit_p->high_mutex); if(signal_needed) { DPRINT((CE_CONT,"idr_soft_intr: instance %d: sending signal\n", unit_p->saved_instance)); /* grab the low level mutex so we can safely signal the top half of the driver */ mutex_enter(&unit_p->soft_mutex); cv_signal(&unit_p->cv); mutex_exit(&unit_p->soft_mutex); } return(int_serviced); } static int idr_strategy(struct buf *bp) { register struct idr_unit *unit_p; int instance; ddi_dma_cookie_t dma_cookie; u_int cookie_count; uint32_t dma_direction; uint32_t plx_direction; uint32_t plx_dma_done_int; uint32_t plx_local_dma_int; uint32_t temp_mode; uint32_t temp_latches; clock_t time_now; clock_t waitval; uint32_t range; int i; /* get instance number from buf and make pointer to soft state */ /*LINTED cast OK*/ instance = (int)getminor(bp->b_edev) ; unit_p = (struct idr_unit *)ddi_get_soft_state(state_head,instance) ; DPRINT((CE_CONT,"idr_strategy: instance %d: entering strategy\n", instance)); /* lock our unit structure */ mutex_enter(&unit_p->soft_mutex); /* save buffer pointer for interrupt routine */ unit_p->buf_p = bp; /* make sure dma byte count size is non-zero and within what we can handle - especially since the first version of this driver doesn't do chaining */ if((bp->b_bcount > dma_attr.dma_attr_maxxfer) || (bp->b_bcount == 0)) { cmn_err(CE_CONT,"idr_strategy: instance %d: DMA byte count zero or too large!\n", instance); bp->b_error |= EINVAL; bp->b_flags |= B_ERROR; goto EXITSTRATEGY; } /* bind handle and get dma cookie for dma hardware */ if(bp->b_flags & B_READ) dma_direction = DDI_DMA_READ; else dma_direction = DDI_DMA_WRITE; if(ddi_dma_buf_bind_handle(unit_p->dma_handle, bp, dma_direction | DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, (caddr_t)0, &dma_cookie, &cookie_count) != DDI_DMA_MAPPED) { bp->b_error |= EIO; bp->b_flags |= B_ERROR; cmn_err(CE_CONT,"idr_strategy: instance %d: ddi_dma_buf_bind_handle failure!\n",instance); goto EXITSTRATEGY; } /* make sure that only a single cookie was created - there should only be one since our attribute struct says no chaining, and sparc/pci has an iommu */ if(cookie_count != 1) { bp->b_error |= EIO; bp->b_flags |= B_ERROR; cmn_err(CE_CONT,"idr_strategy: instance %d: too many dma cookies!\n",instance); goto ERRORSTRATEGY; } DPRINT((CE_CONT,"idr_strategy: instance %d: dmac_address = 0x%x, dmac_size = 0x%x\n", instance, dma_cookie.dmac_address, dma_cookie.dmac_size)); /* buffer must be 16 bit word aligned and an integer # of words (not odd byte count) */ if((dma_cookie.dmac_address & 1) || (dma_cookie.dmac_size & 1)) { bp->b_error |= EINVAL; bp->b_flags |= B_ERROR; cmn_err(CE_CONT,"idr_strategy: instance %d: buffer not word aligned or odd byte count!\n", instance); goto ERRORSTRATEGY; } /* pulse any flags (including errors) off if in auto mode, leave on for later code if manual */ if(!(unit_p->unit_flags & IDR_MANUAL)) IDR_PUT32(IDR_116_PULSES, RESET_EOR_FLAG | RESET_ATTENTION_FLAG | RESET_ERROR_FLAGS); IDR_SYNC_0; /* if automatic mode, set range counter - check for out of limit count */ if(!(unit_p->unit_flags & IDR_MANUAL)) { DPRINT((CE_CONT,"idr_strategy: instance %d: auto mode, setting range counter to 0x%x (words - 1)\n", instance, (bp->b_bcount >> 1) - 1)); /* dr11 counter wants WORD count minus 1 */ /*LINTED cast OK, already tested against maxxfer*/ range = (uint32_t)((bp->b_bcount >> 1) - 1); if(range > IDR_DR11_MAXBLOCK) { cmn_err(CE_CONT,"idr_strategy: instance %d DR11 range count too large!\n", instance) ; bp->b_error |= EINVAL ; bp->b_flags |= B_ERROR ; goto ERRORSTRATEGY ; } IDR_PUT32(IDR_RANGE_HIGH, range >> 16); IDR_PUT32(IDR_RANGE_MID, range >> 8); IDR_PUT32(IDR_RANGE_LOW, range); /* force off dma enb and direction bit */ IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) & ~(IDR_DMA_ENABLE | DMA_INPUT_MODE)); if(bp->b_flags & B_READ) { DPRINT((CE_CONT,"idr_strategy: instance %d: auto read mode, setting dvma_wait\n", instance)); /* we are in automatic read mode, so set direction to input and set read function bits to pre-selected pattern also enable attention interrupt, but not eor interrupt */ IDR_PUT32(IDR_LATCHED_FUNCTIONS, (IDR_GET32(IDR_LATCHED_FUNCTIONS) & ~FUNCTION_MASK) | DMA_INPUT_MODE | ATTENTION_INT_ENB | unit_p->read_fcn); /* in automatic read, we will clear the input fifo before starting transfers in manual mode, that is done in start read */ IDR_PUT32(IDR_116_PULSES, CLEAR_INPUT_FIFO); IDR_SYNC_0; /* set to dma t/c wait - the one to use for input mode */ unit_p->unit_flags |= IDR_DVMA_WAIT ; } else { DPRINT((CE_CONT,"idr_strategy: instance %d: auto write mode, setting eor_wait\n", instance)); /* automatic write mode - leave input mode bit off set write function bits enable eor or attention interrupt */ IDR_PUT32(IDR_LATCHED_FUNCTIONS, (IDR_GET32(IDR_LATCHED_FUNCTIONS) & ~FUNCTION_MASK) | END_OF_RANGE_INT_ENB | ATTENTION_INT_ENB | unit_p->write_fcn); /* in auto write mode, clear the output fifo before transfers begin */ IDR_PUT32(IDR_116_PULSES, CLEAR_OUTPUT_FIFO); IDR_SYNC_0; /* since automatic output, wait for end-of-range */ unit_p->unit_flags |= IDR_EOR_WAIT ; } /* enable idr logic to request dma transfer we could probably have combined this with above idr dma requests will be enabled in start read or start write if manual mode */ IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) | IDR_DMA_ENABLE); IDR_SYNC_1; } else { DPRINT((CE_CONT,"idr_strategy: instance %d: manual mode, setting dvma_wait\n", instance)); /* manual mode - always wait for dma terminal count and always enable attention interrupt direction and idr dma enable bit will be set in start read or start write */ unit_p->unit_flags |= IDR_DVMA_WAIT ; IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) | ATTENTION_INT_ENB); IDR_SYNC_1; } /* the error flags are not cleared here so they will persist for multiple calls to strategy by physio */ /* we assume that we will always be issuing a go here if in automatic mode in manual mode, issuing go is up to the calling program we first make sure that attention is not on - if it is go will be suppressed by the hardware, and transfer will be aborted attention may be arbitrarily long if generated by software at the other end of the dr11 cable - so we will wait for a "reasonable" number of useconds if we find it on it may be necessary to modify this wait time if the application generates a particularly long attention pulse *************** we will issue the dr11 pulses before starting dma if we are doing output, and after starting dma if doing input the reasoning is as follows: if we do the go before dma is started for input, a very fast device might fill our fifos before we could get dma started -- very unlikely with 16K fifos, but there is a small chance that our code could be pre-empted for a long time (thank you unix) if we start dma first for output, the bus will get very busy, and it is not clear that the p-i/o accesses necessary to issue the function and go bits will get through (this was a serious problem on the sbus - probably not an issue with plx on pci) */ if(!(unit_p->unit_flags & IDR_MANUAL)) { /* test for attention stuck on - it will prevent the block from starting if it is on do it here since we need to check for both input and output modes - in automatic mode */ i = 0; while((IDR_GET32(IDR_STATUS) & ATTENTION)&&(i<100)) { drv_usecwait(1) ; i++ ; } if(IDR_GET32(IDR_STATUS) & ATTENTION) { cmn_err(CE_CONT,"idr_strategy: instance %d attempt to issue GO while ATTENTION true!\n", instance) ; /* DMON may be on at this point - shut down everything!! */ IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) & ~(IDR_DMA_ENABLE | END_OF_RANGE_INT_ENB | ATTENTION_INT_ENB)); IDR_SYNC_1; bp->b_error |= EIO ; bp->b_flags |= B_ERROR ; goto ERRORSTRATEGY ; } if(!(bp->b_flags & B_READ)) { /* auto write issues pulses before dma starts */ IDR_PUT32(IDR_DEVICE_PULSES, unit_p->write_pulse | GO); IDR_SYNC_0; } } /* get current time for use in cv_timedwait_sig drv_getparm returns current time in ticks since last reboot */ time_now = ddi_get_lbolt(); /* program plx dma logic on board set direction bit depending on r/w mode and do not enable dma done interrupts in dma mode or interrupt control registers if auto out mode (eor interrupt used instead) */ if(bp->b_flags & B_READ) plx_direction = DMA_INPUT; else plx_direction = 0; if((unit_p->unit_flags & IDR_MANUAL) || (bp->b_flags & B_READ)) { DPRINT((CE_CONT,"idr_strategy: instance %d: manual read/write or auto read, enabling dma done interrupt\n", instance)); plx_dma_done_int = DMA_DONE_INTERRUPT_ENB; plx_local_dma_int = LOCAL_DMA_1_INT_ENABLE; } else { DPRINT((CE_CONT,"idr_strategy: instance %d: auto write, disabling dma done interrupt\n", instance)); plx_dma_done_int = 0; plx_local_dma_int = 0; } DPRINT((CE_CONT,"idr_strategy: instance %d: starting dma, latched fcn reg = 0x%x\n", instance, IDR_GET32(IDR_LATCHED_FUNCTIONS))); /* make sure that any left over dma done interrupt is cleared */ IPLX_PUT32(IPLX_DMA_CMD_STAT_BOTH, DMA_1_CLEAR_INTERRUPT); IDR_SYNC_0; /*LINTED cast from 64 bits OK, size already tested*/ IPLX_PUT32(IPLX_DMA_COUNT_1, (uint32_t)dma_cookie.dmac_size); /* byte count */ IPLX_PUT32(IPLX_DMA_PCI_ADD_1, dma_cookie.dmac_address); /* pci address */ IPLX_PUT32(IPLX_DMA_LOC_ADD_1, IDR_DATA_OUT); /* local bus address */ IPLX_PUT32(IPLX_DMA_MODE_1, DMA_BUS_16_BIT | DMA_WAIT_1 | DMA_BURST_ENABLE | plx_dma_done_int | DMA_LOCAL_ADD_HOLD | DMA_DEMAND_MODE); /* mode register */ IPLX_PUT32(IPLX_DMA_DESC_PTR_1, plx_direction); /* direction */ IDR_SYNC_1; IPLX_PUT32(IPLX_DMA_CMD_STAT_BOTH, DMA_1_ENABLE | DMA_1_START); /* actually fire up xfer*/ IDR_SYNC_2; /* the 9060 drives the local interrupt out true when a dma interrupt happens we have to loop that output back to the local interrupt input external to the chip to get a dma interrupt to the pci bus */ PLX_PUT32(PLX_INT_CSTAT, PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE | LOCAL_INT_OUT_ENABLE | plx_local_dma_int); PLX_SYNC_0; /* if automatic and read mode - issue go AFTER starting dma */ if((!(unit_p->unit_flags & IDR_MANUAL)) && (bp->b_flags & B_READ)) IDR_PUT32(IDR_DEVICE_PULSES, unit_p->read_pulse | GO); IDR_SYNC_0; DPRINT((CE_CONT,"idr_strategy: instance %d: unit_flags = 0x%x, dma mode = 0x%x, int cstat = 0x%x\n", instance, unit_p->unit_flags, IPLX_GET32(IPLX_DMA_MODE_1), PLX_GET32(PLX_INT_CSTAT))); DPRINT((CE_CONT,"idr_strategy: instance %d: calling cv_timedwait_sig, time_now = %d, dma_time = %d\n", instance, time_now, unit_p->dma_time)); waitval = cv_timedwait_sig(&unit_p->cv, &unit_p->soft_mutex, (clock_t)(time_now + unit_p->dma_time)); /* turn off idr dma enable if automatic mode */ if(!(unit_p->unit_flags & IDR_MANUAL)) IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) & ~IDR_DMA_ENABLE); /* make sure interrupt enables are off and pause dma in input mode, plx dma will still be enabled at this point to allow any data in the plx fifo that was left after a block terminated early by attention to make it to memory if the application uses attention to do early block termination, it may be necessary to add some delay here to make sure that the (small) plx fifo has drained there doesn't seem to be any way to determine whether there is data in the plx fifos so for now, we will trust to luck! */ IDR_PUT32(IDR_LATCHED_FUNCTIONS, IDR_GET32(IDR_LATCHED_FUNCTIONS) & ~(END_OF_RANGE_INT_ENB | ATTENTION_INT_ENB)); IDR_SYNC_1; PLX_PUT32(PLX_INT_CSTAT, 0); PLX_SYNC_0; /* if dma is not done (after sig or timeout or short input block), force a plx dma abort and issue a master clear to flush idr buffers and turn off the dma enable bit in the idr logic abort may take some time to complete, so use delay and register read to give it some time */ if(!(IPLX_GET32(IPLX_DMA_CMD_STAT_BOTH) & DMA_1_DONE)) { DPRINT((CE_CONT,"idr_strategy: instance %d: dma not done after cv_timedwait_sig\n", instance)); IPLX_PUT32(IPLX_DMA_CMD_STAT_BOTH, DMA_1_ABORT); IDR_SYNC_0; /* get latched functions and mode - so we can restore them after a mclr it is not clear whether the 9060 needs dma rq in order to finish an abort if it does, it may transfer a word or two after the mclr, so we will repeat the mclr later */ drv_usecwait(5); temp_latches = IDR_GET32(IDR_LATCHED_FUNCTIONS) & ~IDR_DMA_ENABLE; temp_mode = IDR_GET32(IDR_MODE); IDR_PUT32(IDR_116_PULSES, MASTER_CLEAR); /* force write cache to flush - so delay appears on the bus explicitly, so later removal of SYNC macros doesn't make this one go away! */ read_dump_0 = IDR_GET32(IDR_STATUS); drv_usecwait(5); IDR_PUT32(IDR_116_PULSES, MASTER_CLEAR); IDR_SYNC_1; IDR_PUT32(IDR_LATCHED_FUNCTIONS, temp_latches); IDR_PUT32(IDR_MODE, temp_mode); IDR_SYNC_2; /* make sure dma actually says done */ if(!(IPLX_GET32(IPLX_DMA_CMD_STAT_BOTH) & DMA_1_DONE)) { cmn_err(CE_CONT,"idr_strategy: instance %d: dma not done after abort!\n", instance); bp->b_error |= EIO; bp->b_flags |= B_ERROR; goto ERRORSTRATEGY ; } } /* now we can safely make sure that the done interrupt is clear, (the above is probably not necessary after normal dma done) */ IPLX_PUT32(IPLX_DMA_CMD_STAT_BOTH, DMA_1_CLEAR_INTERRUPT); IDR_SYNC_0; /* find out why we woke up if we got here via timeout or signal do a soft reset of the plx and idr logic - saving and restoring the latched functions and mode registers */ if(waitval <= 0) { if(waitval == 0) { /* flag signal received and print error message */ cmn_err(CE_CONT,"idr_strategy: instance %d: signal received while waiting for interrupt!\n",instance); unit_p->unit_flags |= IDR_SIG_RECEIVED; bp->b_error |= EINTR; bp->b_flags |= B_ERROR; } else { /* timeout ! */ cmn_err(CE_CONT,"idr_strategy: instance %d: timeout while waiting for interrupt!\n", instance); unit_p->unit_flags |= IDR_DVMA_TIMEOUT; bp->b_error |= EIO; bp->b_flags |= B_ERROR; } /* save latched functions and mode and do a soft reset, then restore saved registers */ temp_latches = IDR_GET32(IDR_LATCHED_FUNCTIONS); temp_mode = IDR_GET32(IDR_MODE); plx_soft_reset(unit_p); IDR_PUT32(IDR_LATCHED_FUNCTIONS, temp_latches); IDR_PUT32(IDR_MODE, temp_mode); IDR_SYNC_1; goto ERRORSTRATEGY; } /* normal return from interrupt wait */ /* if automatic mode, check for multicycle error or parity error and flag in unit structure in manual mode, this stuff is done in block_end parity error will occur frequently when connected to device that don't support parity (most!) so don't do a cmn_err print */ if(!(unit_p->unit_flags & IDR_MANUAL)) { if(IDR_GET32(IDR_FLAGS) & PARITY_ERROR_FLAG) { DPRINT((CE_CONT,"idr_strategy: instance %d: parity error!\n", instance)); unit_p->unit_flags |= IDR_PAR_ERR; } if(IDR_GET32(IDR_FLAGS) & MULTI_CYCLE_ERROR_FLAG) { cmn_err(CE_CONT,"idr_strategy: instance %d: multi-cycle error!\n", instance); unit_p->unit_flags |= IDR_MCYL_ERR; } } ERRORSTRATEGY: /* come here if error after alloc handle, and bind handle are all complete, or in normal flow */ ddi_dma_unbind_handle(unit_p->dma_handle); EXITSTRATEGY: /* come here if error before all the mappings are done or in normal flow if error - be sure to do the appropriate unmapping(s) before arriving here */ /* we can't read back the range counter in all versions of the plx chip so we will just set resid to 0! */ bp->b_resid = 0; /* tell physio that we are done */ biodone(bp) ; /* unlock unit structure */ mutex_exit(&unit_p->soft_mutex) ; return(0) ; } /* do a soft reset of the plx chip - also clears the idr logic */ static void plx_soft_reset(struct idr_unit *unit_p) { uint8_t temp8; /* we would like to do a full reset of the plx and idr logic here, but.... it isn't easy! soft reset clears the local config registers, which turns off add space 0 accesses so we will force an eeprom reload, and wait a while (0.1s) to avoid too many retry cycles while the eeprom is reloading - we will use delay, not drv_usec_wait, so we don't tie up the cpu on machines with write buffers (and/or bridge chips w/buffers) and short bus timers, trying to access the PLX part during the config reload (1-2 ms) will cause retry cycles, and could trigger a panic -- so do a read just before the reload to flush the buffers and wait a very long time to try to ensure that this doesn't happen an earlier rev cleared the int line byte in the configuration registers during soft reset, but that is not supposed to be true anymore IT SEEMS THAT IT IS STILL TRUE _ SO SAVE AND RESTORE THE INT LINE AFTER SOFT_RESET we probably don't have to clear the config reload bit after writing it, but we will to be sure */ /* map config register set temporarily so we can read and write the int line reg use offsets canned in pci.h from sun there is really no good way to return an error from here - so just put it on the console and return without touching anything */ if(pci_config_setup(unit_p->dip, &unit_p->config_acc_handle) != DDI_SUCCESS) { cmn_err(CE_CONT,"idr_attach: instance %d: soft_reset: pci_config_setup error!\n",unit_p->saved_instance); return; } temp8 = pci_config_get8(unit_p->config_acc_handle, (off_t)PCI_CONF_ILINE); PLX_PUT32(PLX_EEPROM_USER, (PLX_GET32(PLX_EEPROM_USER) | PLX_SOFT_RESET)); PLX_PUT32(PLX_EEPROM_USER, (PLX_GET32(PLX_EEPROM_USER) & ~PLX_SOFT_RESET)); read_dump_0 = PLX_GET32(PLX_INT_CSTAT); PLX_PUT32(PLX_EEPROM_USER, (PLX_GET32(PLX_EEPROM_USER) | CONFIGURATION_RELOAD)); delay(drv_usectohz((clock_t)100000)); PLX_PUT32(PLX_EEPROM_USER, (PLX_GET32(PLX_EEPROM_USER) & ~CONFIGURATION_RELOAD)); pci_config_put8(unit_p->config_acc_handle, (off_t)PCI_CONF_ILINE, temp8); pci_config_teardown(&unit_p->config_acc_handle); DPRINT((CE_CONT,"idr_??? (plx_soft_reset): instance %d: soft reset and configuration reload complete\n", unit_p->saved_instance)); /* soft reset leaves pci master int enable on, so turn it off for safety don't bother with oring bits - just hammer them all off! */ PLX_PUT32(PLX_INT_CSTAT, 0); PLX_SYNC_0; /* restore endian mode default */ PLX_PUT32(PLX_ENDIAN_REG, unit_p->endian_def); PLX_SYNC_1; } static void idr_minphys(struct buf *bp) { register struct idr_unit *unit_p; /* points to unit structure */ int instance; /* get instance number from buffer and get pointer to soft state */ /*LINTED cast OK*/ instance = (int)getminor(bp->b_edev) ; unit_p = (struct idr_unit *)ddi_get_soft_state(state_head,instance) ; DPRINT((CE_CONT,"idr_minphys: instance %d: bp->b_bcount = 0x%x\n", instance, bp->b_bcount)); if(!unit_p->ignore_minphys) { DPRINT((CE_CONT,"idr_minphys: instance %d: calling system minphys\n", instance)); minphys(bp); DPRINT((CE_CONT,"idr_minphys: instance %d: bp->b_bcount = 0x%x\n", instance, bp->b_bcount)); } DPRINT((CE_CONT,"idr_minphys: instance %d: setting bp->b_count to min(b_bcount,IDR_MAXPHYS)\n",instance)); bp->b_bcount = min(bp->b_bcount, IDR_MAXPHYS); DPRINT((CE_CONT,"idr_minphys: instance %d: bp->b_bcount = 0x%x\n", instance, bp->b_bcount)); }