/* * ihcp_driver.c * code module for Linux 2.6.x driver for PCI hardcopy boards * * Tahoma Technology * (formerly Ikon Corporation) * 107 2nd Avenue North * Seattle, WA, USA 98109 * * 206.728.6465 * http://www.tahomatech.com * tahoma@tahomatech.com * * This code released under the GPL, and in the public domain * References to IKON left in place for compatibility and historical reasons * * Please send us code modifications and BUG reports */ /* * linux includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * tahoma ("ikon") includes */ #include "ihcp_changes.h" #include "ihcp_io.h" #include "ihcp_reg.h" #include "ihcp_var.h" /* * prototypes */ static int ihcp_init_module(void); static void ihcp_cleanup_module(void); static void ihcp_free_units(void); static int ihcp_open(struct inode *, struct file *); static int ihcp_close(struct inode *, struct file *); static ssize_t ihcp_write(struct file *, const char *, size_t, loff_t *); static long ihcp_new_ioctl(struct file *, u_int, u_long); static int ihcp_ioctl(struct inode *, struct file *, u_int, u_long); static irqreturn_t ihcp_intr(int, void * IHCP__PT_REGS); static int ihcp_strategy(struct ihcp_unit_t *, char *, int); static void plx_soft_reset(struct ihcp_unit_t *); static void ihcp_set_global_defaults(void); static int ihcp_init_one_board(struct ihcp_unit_t *, int); static void ihcp_set_board_defaults(struct ihcp_unit_t *, int); static int ihcp_dma_alloc(struct ihcp_unit_t *, int); static void ihcp_dma_free(struct ihcp_unit_t *, int); static int ihcp_map_regs(struct ihcp_unit_t *, int); static void ihcp_unmap_regs(struct ihcp_unit_t *, int); static int ihcp_map_or_copy_buf(struct ihcp_unit_t *, int, char *, int, int); static int ihcp_unmap_or_copy_buf(struct ihcp_unit_t *, int); static void ihcp_unmap_on_error(struct ihcp_unit_t *, int); static void ihcp_unmap_user_buf(struct ihcp_unit_t *, int); static int ihcp_map_user_buf(struct ihcp_unit_t *, int); static int ihcp_maplist_to_sg_list(struct ihcp_unit_t *, int); static int ihcp_sg_list_to_iopb_list(struct ihcp_unit_t *, int); static int ihcp_maplist_check(struct ihcp_unit_t *, int); static void ihcp_set_byte_order(struct ihcp_unit_t *); /* * mutex and conditional variable stuff - was in ikon_mutex.h (Kaz Kylheku & wdw) */ void ihcp_mutex_init(ihcp_mutex_t *); void ihcp_mutex_lock(ihcp_mutex_t *); void ihcp_mutex_unlock(ihcp_mutex_t *); void ihcp_cond_init(ihcp_cond_t *); int ihcp_cond_timed_wait_rel(ihcp_cond_t *, ihcp_mutex_t *, long); void ihcp_cond_broadcast(ihcp_cond_t *); /* * configuration variables that may be specified at module install time * see ihcp_io.h for possible values * * each internal variable is paired with one that can be modified by insmod */ static int byte_order_def = -1; static int vers_speed_def = -1; static int cent_speed_def = -1; static int mode_def = -1; static int dma_time_def = -1; static int fifo_time_def = -1; static int max_boards = -1; static int max_phys_order = -1; module_param(byte_order_def, int, 0); module_param(vers_speed_def, int, 0); module_param(cent_speed_def, int, 0); module_param(mode_def, int, 0); module_param(dma_time_def, int, 0); module_param(fifo_time_def, int, 0); module_param(max_boards, int, 0); module_param(max_phys_order, int, 0); /* * internal variables will use defaults if paired modifiable variables are still -1 after insmod * most of these variables have working versions kept in the unit structures on a per-board basis */ static int ihcp_byte_order_def; static int ihcp_vers_speed_def; static int ihcp_cent_speed_def; static int ihcp_mode_def; static int ihcp_dma_time_def; static int ihcp_fifo_time_def; static int ihcp_max_boards; static int ihcp_max_phys_order; /* * some global variables */ int ihcp_device_id_list[] = {IHCP_DEVICE_ID_LIST, 0}; /* supported board PCI IDs */ static int ihcp_boards_found; /* number of boards actually detected */ static int ihcp_boards_attached; /* number of boards successfully attached */ static int ihcp_module_initialized; /* non zero if any board attaches and module initialized successfully */ static int ihcp_device_major; /* major number dynamically assigned to this driver */ static int ihcp_max_buf_pages; /* # of pages in copy buf = 2**ihcp_max_phys_order */ static int ihcp_max_buf_bytes; /* in bytes = ihcp_max_buf_pages * PAGE_SIZE */ static int ihcp_max_sg_length; /* ihcp_max_buf_pages + 1 (allows for offset) */ /* MUST TRACK KIOBUF MAP SIZE AND COPY BUF SIZE */ static int ihcp_iopb_mem_size; /* size of mem allocated for iopb list */ /* * pointer at base of unit structure array * array, rather than linked list, to try to be a little more efficient with kernel memory */ static struct ihcp_unit_t *ihcp_unit_base_p; /* * declare dummy variables used to the left of = when we read a board * register to force the write cache to flush a register write to the bus */ static volatile u_long read_dump_0; static volatile u_long read_dump_1; /* * declare our file-ops structure * * some of these have moved since 2.0 * * use named fields as of 2.3 */ struct file_operations ihcp_fops = { owner:THIS_MODULE, write:ihcp_write, ioctl:ihcp_ioctl, IHCP_UNLOCKED_IOCTL IHCP_COMPAT_IOCTL open:ihcp_open, release:ihcp_close, }; /* * get the global parameters, kmalloc and zero the unit structure array * * register interrupts, map the working registers, get the dma iopb memory & * scatterlistlist, and copy buffer if required, register the driver and initialize * things on a per-instance basis and register the driver * * unless debug printks are enabled, init_module and cleanup_module are silent, * with the exception of a dma/copy mode anouncement and a single success or * failure message for each board instance */ static int ihcp_init_module(void) { register struct ihcp_unit_t *unit_p; int instance; int unit_array_size; u_long temp; struct pci_dev *temp_pci_dev_p; int *device_id_list_p = ihcp_device_id_list; DPRINT("entering init_module"); temp = LINUX_VERSION_CODE; DPRINT("compiled against base version: %ld.%ld.%ld uts release:\n" " %s", (temp >> 16) & 0xffff, (temp >> 8) & 0xff, temp & 0xff, UTS_RELEASE); /* * identify our dma mode/copy buffer status in msg log */ IPRINT(IHCP_DMA_MODE_MSG); ihcp_module_initialized = 0; /* * establish driver and board global defaults from parameters * includes things like iopb mem size */ ihcp_set_global_defaults(); /* * aloocate and zero space for max_boards unit structs */ unit_array_size = ihcp_max_boards * sizeof(struct ihcp_unit_t); ihcp_unit_base_p = (struct ihcp_unit_t *) kmalloc(unit_array_size, GFP_KERNEL); if (ihcp_unit_base_p == NULL) { EPRINT("can't allocate unit struct array (kmalloc error)!"); return -ENOMEM; } memset(ihcp_unit_base_p, 0, unit_array_size); DPRINT("allocated unit structure array, ihcp_unit_base_p = 0x%p,\n" " size = 0x%x", ihcp_unit_base_p, unit_array_size); /* * loop, looking for our boards - there better be at least one */ DPRINT("starting search for boards"); ihcp_boards_found = 0; ihcp_boards_attached = 0; instance =0; while(*device_id_list_p) { temp_pci_dev_p = NULL; DPRINT("probing for %x boards", *device_id_list_p); /* * loop while we keep finding boards w/this id * * make sure we don't exceed max boards and go past the end * of the unit struct array * * each get_device call decrements the previous pci dev ref count, * so bump the ref count for each found - dec ("put") at unload time * (we need pci dev to persist since we use it elsewhere) */ while((temp_pci_dev_p = pci_get_device(IKON_VENDOR_ID, *device_id_list_p, temp_pci_dev_p)) != NULL) { /* * if we are still finding boards beyond max allowed, warn, * drop the ref count on the board we just found, and exit the loop * * we just exit the inner loop - and may drop in again if more * IDs in the list - is this OK ??? */ if(instance >= ihcp_max_boards) { WPRINTI("boards found > max boards!\n" " max boards should be increased"); pci_dev_put(temp_pci_dev_p); break; } unit_p = ihcp_unit_base_p + instance; unit_p->instance = instance; /* * save pointer to this board's pci dev structure * and bump the ref count, since next loop through pci_get_device * will decrement it */ unit_p->pci_dev_p = temp_pci_dev_p; pci_dev_get(unit_p->pci_dev_p); /* * do per-board init and set attach flag and bump * attached count if successful (==0) * bump instance even if not attached since board exists * and takes space in the array */ if(!ihcp_init_one_board(unit_p, instance)) { DPRINTI("%x board attached", *device_id_list_p); unit_p->unit_attached = 1; ihcp_boards_attached++; } else { EPRINTI("ihcp_init_one_board error! board not attached!"); } ihcp_boards_found++; instance++; } /* while board found */ device_id_list_p++; } /* while *device_id_list_p */ DPRINT("no (more) boards found, ending search"); /* * if no boards have attached, give back the unit structure array and return an error * if no boards are found, do the same, but print a different message */ if (!ihcp_boards_attached) { if (!ihcp_boards_found) WPRINT("no boards found! module not initialized!"); else WPRINT("%d board(s) found but no boards attached! module not initialized!", ihcp_boards_found); kfree(ihcp_unit_base_p); return -ENODEV; } /* * register the module, and get dynamic major number assigned * **TODO** go to new style cdev-type registration */ ihcp_device_major = register_chrdev(0, "ihcp", &ihcp_fops); if (ihcp_device_major < 0) { EPRINT("register_chrdev error! module not initialized!"); /* * free all resources owned by attached boards * and give back the unit array when done */ ihcp_free_units(); kfree(ihcp_unit_base_p); return -ENODEV; } DPRINT("driver registered, major # = %d", ihcp_device_major); /* * flag module as initialized and return good status to insmod */ ihcp_module_initialized = 1; IPRINT("%d board(s) found, %d board(s) attached, module initialized", ihcp_boards_found, ihcp_boards_attached); return 0; } /* end of init_module */ /* * ihcp_free_units walks through the allocated unit structure array and frees all the * resources of any board that was successfully attached, including iopb memory, * scatterlist, user buf mapping space, copy buffer (if allocated), register mappings, * and interrupt lines and disabled device * * attached boards are fully reset before anything is freed * * it looks at all the slots, up to max_boards, not up to boards_attached, because it is * theoretically possible for pcibios errors to cause some boards to be found, but not * attach (VERY UNLIKELY) * * even if not attached, it "puts" the board to decrement the ref count * * THIS ROUTINE DOES NOT RETURN THE UNIT STRUCTURE ARRAY - THAT IS LEFT TO THE CALLING ROUTINE */ static void ihcp_free_units(void) { register struct ihcp_unit_t *unit_p; int instance; int boards_detached = 0; /* * look at each unit structure element of array * if board exists (pci_dev_p) not NULL) then check for * attached and do the appropriate stuff */ DPRINT("max boards = %d, boards found = %d, boards attached = %d", ihcp_max_boards, ihcp_boards_found, ihcp_boards_attached); for (instance = 0; instance < ihcp_max_boards; instance++) { unit_p = ihcp_unit_base_p + instance; if(unit_p->pci_dev_p) { if (unit_p->unit_attached) { DPRINTI("disabling dma and interrupts"); /* * fist disable all plx interrupt enable bits * then the ihcp masks */ PLX_PUTL(PLX_INT_CSTAT, 0); IHCP_PUTL(IHCP_INTERRUPT_MASK, 0); /* * now make sure dma channel(s) and interrupts are off * we will just force all zeros into both channel's registers */ PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, 0); DPRINTI("freeing interrupt, mappings, copy buffer (if any),\n" " scatterlist, iopb memory, and disabling devcice"); free_irq(unit_p->pci_dev_int_level, (void *) unit_p); ihcp_unmap_regs(unit_p, instance); ihcp_dma_free(unit_p, instance); pci_disable_device(unit_p->pci_dev_p); unit_p->unit_attached = 0; boards_detached++; } else { DPRINTI("this instance not attached - no action taken"); } /* * dec ref count incremented w/pci_get_device - if board exists */ DPRINTI("decrementing ref count"); pci_dev_put(unit_p->pci_dev_p); } } /* end of for (instance) */ IPRINT("%d board(s) detached", boards_detached); } /* end of ihcp_free_units */ /* * When our driver is unloaded, cleanup_module cleans up and frees the resources * we allocated in init_module * unregister the driver, return resources on a per-unit basis (interrupts, * user buffer mappings, iopbs, etc) and free the unit structure array */ static void ihcp_cleanup_module(void) { DPRINT("entering cleanup_module"); if (!ihcp_module_initialized) { EPRINT("cleanup called when module not previously initialized!"); return; } /* * unregister_chrdev always returned 0, now it's void */ unregister_chrdev(ihcp_device_major, "ihcp"); DPRINT("device (driver) successfully unregistered"); /* * call ihcp_free_units to release resources * and turn off attached flag(s) * * return unit structure memory */ ihcp_free_units(); kfree(ihcp_unit_base_p); ihcp_module_initialized = 0; /* * 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; return; } /* * do various error checking, then make the board available for write * and ioctl calls * * bump the usage count so driver can't be unlaoded until close called * * **TODO** we don't protect the already open test that enforces * exclusive open. that race is unlikely, and the way our boards get * used 2 different processes will never try to open the same board * anyway - but to be complete we should probably protect w/a semaphore * or mutex (or atomic test - but that will also require an add'l state * variable - open flag is wrong sense */ static int ihcp_open(struct inode *inode_p, struct file *file_p) { int instance; register struct ihcp_unit_t *unit_p; instance = MINOR(inode_p->i_rdev); DPRINTI("entering ihcp_open"); if ((instance < 0) || (instance >= ihcp_max_boards)) { EPRINTI("instance out of range!"); return -ENODEV; } unit_p = ihcp_unit_base_p + instance; DPRINTI("unit pointer = 0x%p", unit_p); if (unit_p->unit_attached == 0) { WPRINTI("open attempted when board not attached!"); return -ENODEV; } /* * Only allow a single open */ spin_lock(&unit_p->open_lock); if (unit_p->unit_open != 0) { DPRINTI("open called when already open!"); spin_unlock(&unit_p->open_lock); return -EBUSY; } unit_p->unit_open = 1; spin_unlock(&unit_p->open_lock); /* * clear unit flags - but not board type */ unit_p->unit_flags &= IHCP_CLEAR_FLAGS; /* * set default timeouts here * timeout parameters are stored as ticks */ unit_p->dma_time = (u_long) (HZ * unit_p->dma_time_def); unit_p->fifo_time = (u_long) (HZ * unit_p->fifo_time_def); DPRINTI("open complete"); return 0; } /* * make the board unavailable for write and ioctl calls */ static int ihcp_close(struct inode *inode_p, struct file *file_p) { int instance; register struct ihcp_unit_t *unit_p; instance = MINOR(inode_p->i_rdev); DPRINTI("entering ihcp_close"); if ((instance < 0) || (instance >= ihcp_max_boards)) { EPRINTI("instance out of range!"); return -ENODEV; } unit_p = ihcp_unit_base_p + instance; DPRINTI("unit pointer = 0x%p", unit_p); if (unit_p->unit_open == 0) { WPRINTI("close attempted when board not open!"); return -ENODEV; } unit_p->unit_open = 0; /* * we don't reset the board and device here - that might kill a job that * was in the device but not yet complete - reset in detach */ DPRINTI("close complete"); return 0; } /* * do data transfer to device - and leading and trailing bytes in p-i/o mode, if * necessary. in solaris and hpux drivers, the leading and trailing bytes were * handled in strategy - we do them in write in this driver (for some reason) * * the heavy lifting is done in the strategy routine (user dma or buffer copy * & dma) * * NOTE THAT THERE ARE LOTS OF ERROR RETURNS IN THIS CODE -- IF WE EVER NEED A * MUTEX HERE, OR ITS EQUIVALENT, THESE WILL ALL NEED TO SET AN ERROR CODE AND * GOTO ... TO AVOID LEAKING OUT THE SIDE OF THE MUTEX */ static ssize_t ihcp_write(struct file *file_p, const char *user_buf_p, size_t user_count, loff_t * off_p) { struct inode *inode_p; register struct ihcp_unit_t *unit_p; int retval; int instance; int dma_count; u_char temp_char; int strategy_retval; /* * we always say we xferred all the bytes, unless error * the plx chip can't read back the dma count anyway * * even if it could, we don't know how many bytes are still in fifos at timeout */ retval = user_count; inode_p = file_p->f_dentry->d_inode; instance = MINOR(inode_p->i_rdev); DPRINTI("entering write routine, user_buf_p = 0x%p, count = 0x%x", user_buf_p, (unsigned int)user_count); if ((instance < 0) || (instance >= ihcp_max_boards)) { EPRINTI("instance out of range!"); return -ENODEV; } unit_p = ihcp_unit_base_p + instance; if (!unit_p->unit_open) { EPRINTI("write called when unit not open!"); return -EIO; } /* * check user buffer pointer for leading bytes not quad byte aligned, * and output them, up to user_count (max) */ while (((u_long) user_buf_p & 0x3) && (user_count)) { if (copy_from_user (&temp_char, user_buf_p, sizeof(temp_char))) { EPRINTI("copy_from_user error!"); return -EINVAL; } DPRINTI("sending initial byte = 0x%x, buf_p = 0x%p, count = 0x%x", temp_char, user_buf_p, (unsigned int)user_count); /* * test for fifo full while cranking out bytes - should never happen */ if ((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("fifo full while sending leading bytes!"); return -EIO; } if (unit_p->byte_order == HIGH_BYTE_FIRST) temp_char = temp_char << 24; IHCP_PUTL(IHCP_8_BIT_DATA_OUT, temp_char); user_buf_p++; user_count--; } /* end of while */ /* * we should have an aligned user buffer pointer at this point, and 0 * or more bytes left * * clear the timeout and sig rx'd flags -- also clears "waiting for" flags * strategy will set the "waiting for dma" flag. */ unit_p->unit_flags &= IHCP_CLEAR_FLAGS; /* * loop on dma out while user count >= 4 * use do..while loop so we go through at least once * this will cause strategy to wait for fifo <1/2 full, even if no * dma output to do.....protects against many small non-dma buffers filling * fifo * * strategy expects the virtual address of the next chunk of user buffer, * and a dma count * that is 0 mod 4 (and may be 0) */ do { /* * use the min of remaining user count and max dma buffer size * (masked to 0 mod 4) as the dma count */ dma_count = (user_count < ihcp_max_buf_bytes) ? user_count : ihcp_max_buf_bytes; dma_count &= 0xfffffffc; DPRINTI("calling strategy, user_buf_p = 0x%p, dma_count = 0x%x", user_buf_p, dma_count); strategy_retval = ihcp_strategy(unit_p, (char *)user_buf_p, dma_count); DPRINTI("ihcp_strategy returns 0x%x", strategy_retval); if (strategy_retval) { WPRINTI("ihcp_strategy returns error = %d!", strategy_retval); return strategy_retval; } user_buf_p += dma_count; user_count -= dma_count; DPRINTI("bottom of do..while, bytes remaining = 0x%x", (unsigned int)user_count); } while (user_count >= 4); /* * figure out if there are any trailing bytes, update user buf pointer * and count and send the bytes via p-i/o */ while (user_count) { if (copy_from_user (&temp_char, user_buf_p, sizeof(temp_char))) { EPRINTI("copy_from_user error!"); return -EINVAL; } DPRINTI("sending trailing byte = 0x%x, buf_p = 0x%p, count = 0x%x", temp_char, user_buf_p, (unsigned int)user_count); /* * test for fifo full while cranking out bytes - should never happen */ if ((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("fifo full while sending trailing bytes!"); return -EIO; } if (unit_p->byte_order == HIGH_BYTE_FIRST) temp_char = temp_char << 24; IHCP_PUTL(IHCP_8_BIT_DATA_OUT, temp_char); user_buf_p++; user_count--; } /* end of while */ DPRINTI("leaving write routine"); /* * retval should be the original byte count if we get to this point */ return (retval); } /* * old-style wrapper for new ioctl entry points (for older kernels) */ static int ihcp_ioctl(struct inode *inode_p, struct file *file_p, u_int cmd, u_long arg) { DPRINT("instance %d: old ioctl called, calling new ioctl", MINOR(file_p->f_dentry->d_inode->i_rdev)); return ihcp_new_ioctl(file_p, cmd, arg); } /* * provide mechanism for controlling device and driver * * THIS CODE USES A FEW ERROR RETURNS - IF WE SOMEDAY NEED A MUTEX * OR EQUIVALENT, GO BACK TO SOLARIS STYLE .. SET RETVAL ERROR CODES AND * JUMP TO IOCTLEXIT, SO WE DON'T RISK LEAKING OUT THE SIDE OF A MUTEX! * * note that the ioctl routine treats arg as a pointer - to u32 in this version */ static long ihcp_new_ioctl(struct file *file_p, u_int cmd, u_long arg) { struct inode *inode_p; register struct ihcp_unit_t *unit_p; u32 return_array[IHCP_RETURN_ARRAY_SIZE]; u8 data_array[256]; struct lpregs lp_regs; u32 bits; u8 ct; u16 st; u32 lt; int count; int i, t; int instance; int command; int wait_return; inode_p = file_p->f_dentry->d_inode; instance = MINOR(inode_p->i_rdev); DPRINTI("entering new ioctl routine, command = 0x%x, arg = 0x%lx", cmd, arg); if ((instance < 0) || (instance >= ihcp_max_boards)) { EPRINTI("instance out of range!"); return -ENODEV; } unit_p = ihcp_unit_base_p + instance; if (!unit_p->unit_open) { EPRINTI("ioctl called when unit not open!"); return -EIO; } /* * get command and argument size */ command = cmd & IHCPIO_CMD_MASK; count = (cmd & IHCPIO_COUNT_MASK) >> _IOC_SIZESHIFT; DPRINTI("masked command = 0x%x, argument size = 0x%x", command, count); /* * get first word of argument (even if we are eventually doing a * copyout operation) - this saves some typeing */ if (copy_from_user(&bits, (caddr_t) arg, sizeof(bits))) { EPRINTI("copy_from_user error!"); return -EINVAL; } DPRINTI("arg = 0x%lx, *arg = 0x%x", arg, bits); /* * define mask used to strip the count and upper control bits from cases */ #define MASK IHCPIO_CMD_MASK switch (command) { case MASK & IHCPIO_DEV_RESET: /* send ~ 1us reset pulse to attached device */ DPRINTI("at DEV_RESET"); /* * set and reset device clear bit, with a delay so the pulse is long enough * for even slow devices * * udelay will make the cpu delay, but doesn't guarantee that the writes won't * stack in a write buffer * * so we will do a read before the wait to force the buffer to flush */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, (IHCP_GETL(IHCP_INTERFACE_CONTROL) | RESET_DEVICE)); read_dump_0 = IHCP_GETL(IHCP_MODE); udelay(1); IHCP_PUTL(IHCP_INTERFACE_CONTROL, (IHCP_GETL(IHCP_INTERFACE_CONTROL) & ~RESET_DEVICE)); break; case MASK & IHCPIO_SET_CONFIG: /* set speed and if to use busy */ DPRINTI("at SET_CONFIG"); /* * mask to pertinent bits only * the calling program will use the old sbus speed definitions * we will have to convert them to the pci card bits here */ bits &= (IHCPIO_V_BURST | IHCPIO_BUSY_HANDSHAKE | IHCPIO_4_EDGE | IHCPIO_IGNORE_BUSY | IHCPIO_SPEED3); /* * if request is a centronics function and board is strapped for * versatec - error! */ if ( ((bits & (IHCPIO_BUSY_HANDSHAKE | IHCPIO_4_EDGE | IHCPIO_IGNORE_BUSY)) != 0) && ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT)) { WPRINTI("SET_CONFIG - attempt to set centronics mode on board strapped for versatec!"); return -ENOTTY; } /* * if request is a versatec only function and board is strapped for centronics - error! */ if (((bits & IHCPIO_V_BURST) != 0) && ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT)) { WPRINTI("at SET_CONFIG - attempt to set versatec mode on baord straped for centronics!"); return -ENOTTY; } /* * shuffle bits to match new board */ t = SPEED_MASK & (3 - ((bits & IHCPIO_SPEED3) >> 2)); if (bits & IHCPIO_BUSY_HANDSHAKE) t |= BUSY_NOT_ACK; if (bits & IHCPIO_4_EDGE) t |= FOUR_EDGE_HANDSHAKE; if (bits & IHCPIO_IGNORE_BUSY) t |= IGNORE_BUSY; if (bits & IHCPIO_V_BURST) t |= V_BURST; /* * plug into mode register - being careful not to disturb other bits */ IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) & (BYTE_SWIZZLE | REVERSE_DATA_ENB)) | t); break; case MASK & IHCPIO_SET_DMATIME: /* set dma timeout parameter */ DPRINTI("at SET_DMATIME"); 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 = (u_long) (bits * HZ); /* set ticks */ break; case MASK & IHCPIO_SET_FIFOTIME: /* set timeout for fifo <1/2 full */ /* or fifo empty & dev rdy wait */ DPRINTI("at SET_FIFOTIME"); if (bits < FIFO_TIME_MIN) bits = FIFO_TIME_MIN; /* not less than */ if (bits > FIFO_TIME_MAX) bits = FIFO_TIME_MAX; /* or more than.. */ unit_p->fifo_time = (u_long) (bits * HZ); /* ticks */ break; case MASK & IHCPIO_GET_REGS: /* get all the board's registers */ DPRINTI("at GET_REGS"); return_array[0] = unit_p->dev_and_vendor_id; return_array[1] = unit_p->revision_id; return_array[2] = PLX_GETL(PLX_INT_CSTAT); return_array[3] = PLX_GETL(PLX_EEPROM_USER); return_array[4] = PLX_GETL(PLX_DMA_MODE_0); return_array[5] = PLX_GETL(PLX_DMA_PCI_ADD_0); return_array[6] = PLX_GETL(PLX_DMA_LOC_ADD_0); return_array[7] = PLX_GETL(PLX_DMA_COUNT_0); return_array[8] = PLX_GETL(PLX_DMA_DESC_PTR_0); return_array[9] = PLX_GETL(PLX_DMA_CMD_STAT_BOTH); return_array[10] = IHCP_GETL(IHCP_INTERRUPT_MASK); return_array[11] = IHCP_GETL(IHCP_MODE); return_array[12] = IHCP_GETL(IHCP_DEVICE_CONTROL); return_array[13] = IHCP_GETL(IHCP_INTERFACE_CONTROL); return_array[14] = IHCP_GETL(IHCP_INTERFACE_STATUS); return_array[15] = IHCP_GETL(IHCP_DEVICE_STATUS); return_array[16] = IHCP_GETL(IHCP_REVERSE_DATA); return_array[17] = IHCP_GETL(IHCP_AUTO_LTR_LOW); return_array[18] = IHCP_GETL(IHCP_AUTO_LTR_HIGH); if (copy_to_user((caddr_t) arg, return_array, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_GET_STATUS: /* get formatted version of status */ DPRINTI("at GET_STATUS"); /* * get device status bits */ ct = IHCP_GETL(IHCP_DEVICE_STATUS) & (FAULT | ONLINE | PAPER_OUT | CENTRONICS_BUSY | VERSATEC_READY); /* * shuffle status bits to match sbus format */ bits = 0; if (ct & FAULT) bits |= IHCPIO_DEV_FAULT; if (ct & ONLINE) bits |= IHCPIO_DEV_SEL; if (ct & PAPER_OUT) bits |= IHCPIO_DEV_POUT; if (ct & CENTRONICS_BUSY) bits |= IHCPIO_DEV_BUSY; if (ct & VERSATEC_READY) bits |= IHCPIO_DEV_RDY; if (copy_to_user((caddr_t) arg, &bits, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_GET_BOARD: /* get board type */ DPRINTI("at GET_BOARD"); bits = IHCP_GETL(IHCP_DEVICE_STATUS) & INTERFACE_STRAP_MASK; if (copy_to_user((caddr_t) arg, &bits, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_SET_VMODE: /* set versatec mode - both leave rdy ff on on pci card */ case MASK & IHCPIO_SET_VMODEX: DPRINTI("at SET_VMODE"); /* * make sure the board is strapped for versatec */ if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at SET_VMODE - attempt to do versatec mode set on baord strapped for centronics!"); return -ENOTTY; } /* * this command talks to the fifo - make sure there is room * we could wait for room with an interrupt, but the way the board is to * be used, there should always be room */ if ((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at SET_VMODE - no room in fifo for versatec mode set!"); return -EIO; } /* * bottom two bits contain print/plot/spp control - same for sbus and pci * command byte will be in low byte unless byte swizzling selected */ lt = SET_VERSATEC_MODE | (bits & 0x03); if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); /* * save mode in unit structure */ unit_p->mode = bits & 0x03; break; case MASK & IHCPIO_V_CMD: /* issue versatec pulse command */ DPRINTI("at V_CMD"); /* * make sure the board is strapped for versatec - can't do this w/centr */ if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at V_CMD - attempt to issue versatec command to board strapped for centronics!"); return -ENOTTY; } /* * this command talks to the fifo - make sure there is room !! */ if ((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at V_CMD - no room in fifo for versatec command!"); return -EIO; } /* * bottom two bits contain command - we have to shuffle them * for compatibility with sbus driver */ if (bits == IHCPIO_VLTR) lt = VERSATEC_LINE_TERM; if (bits == IHCPIO_VEOT) lt = VERSATEC_EOT; if (bits == IHCPIO_VFED) lt = VERSATEC_FORM_FEED; if (bits == IHCPIO_VCLR) lt = VERSATEC_CLEAR; DPRINTI("at V_CMD, command sent = 0x%x", lt); if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); break; case MASK & IHCPIO_DATA_OUT: /* output chars from arg - # of chars in bottom of cmd */ DPRINTI("at DATA_OUT, byte count = 0x%x", count); /* * make sure # of characterss is <=255 * that is the max # of characters in arg array - for historical reasons * * we will check for room in fifo before each byte but will not wait on * an interrupt if the fifo is full - we will flag an error and return * * each byte will be sent by writing to the 8 bit data out register * this is much less effecient than writing quad bytes to the 32 bit * data out register, or to the burst out range * * it is not the intent of this driver to use this ioctl for large data * blocks, or frequent data transfers - those funtions should be served via * the write call * * this ioctl should be used for the output of a few bytes - perhaps as a * header to a write call, and should be used between write calls, or waits for * fifo less than half full * * if this ioctl is used repeatedly, without interspersed dma transfers, which * leave the fifo approximately half full, or without wait for half full ioctls * the fifo may eventually overflow * * it would be possible to incorporate a wait for less than half full interrupt * in the data out loop, but that would be overkill, considering the intended * use of this ioctl */ if (count > 255) { WPRINTI("at DATA_OUT - too many bytes!"); return -EINVAL; } if (copy_from_user(data_array, (caddr_t) arg, count)) { WPRINTI("copy_from_user error!"); return -EINVAL; } for (i = 0; i < count; i++) { if ( (IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at DATA_OUT - fifo full! 0x%x bytes requested,\n" " 0x%x bytes transferred", count, i); return -EIO; } lt = data_array[i]; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_8_BIT_DATA_OUT, lt); } break; case MASK & IHCPIO_RDY_WAIT: /* wait for rdy & fifo empty - or timeout */ DPRINTI("at RDY_WAIT, wait time = %d (jiffies)", unit_p->fifo_time); /* * if already ready and fifo empty, just return * first clear any stale flags */ unit_p->unit_flags &= IHCP_CLEAR_FLAGS; if (IHCP_GETL(IHCP_INTERFACE_STATUS) & DEVICE_AND_INT_READY) return 0; /* * make sure no left over interrupt flag */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * get the mutex, set sleeping so we can detect an abnormal * return, and indicate why we wait */ ihcp_mutex_lock(&unit_p->mutex); unit_p->sleeping = 1; unit_p->unit_flags |= IHCP_RDY_WAIT; /* * enable plx and ihcp interrupts for device and interface ready * we don't or into ihcp or plx mask reg since we know exactly * which ints to enb */ IHCP_PUTL(IHCP_INTERRUPT_MASK, DEV_AND_INT_RDY_INT_ENB); PLX_PUTL(PLX_INT_CSTAT, (PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE)); /* * snooze until ready interrupt, timeout, or signal */ DPRINTI("at RDY_WAIT, time_now = %ld, unit_p->fifo_time = %d", jiffies, unit_p->fifo_time); wait_return = ihcp_cond_timed_wait_rel(&unit_p->cond, &unit_p->mutex, unit_p->fifo_time); /* * make sure interrupt enables are off * and clear interrupt flag in case we got here via timeout just * as interrupt set the flag */ IHCP_PUTL(IHCP_INTERRUPT_MASK, 0); PLX_PUTL(PLX_INT_CSTAT, 0); IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); ihcp_mutex_unlock(&unit_p->mutex); /* * find out why we woke up (we could probably dump if sleeping * and use the return value of cond_timed_wait_rel directly, but ... */ if (unit_p->sleeping) { unit_p->sleeping = 0; DPRINTI("at RDY_WAIT, abnormal return from ihcp_cond_timed_wait_rel()!"); if (wait_return == IHCP_COND_WAIT_TIMEOUT) { /* * timeout may not be an error, so no error message just flag and return * caller can test for timeout on error return */ DPRINTI("at RDY_WAIT, timeout while waiting for interrupt!"); unit_p->unit_flags |= IHCP_RDY_TIMEOUT; return -EIO; } if (wait_return == IHCP_COND_WAIT_SIGNAL) { /* * flag signal received and print error message */ WPRINTI("at RDY_WAIT, signal received while waiting for interrupt"); unit_p->unit_flags |= IHCP_SIG_RECEIVED; return -EINTR; } } /* end of if sleeping */ return 0; break; case MASK & IHCPIO_HALF_WAIT: /* wait for fifo fifo_time); /* * if already less than half full, just return * first clear any stale flags */ unit_p->unit_flags &= IHCP_CLEAR_FLAGS; if (IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_HALF_FULL) return 0; /* * clear any left over interrupt flag */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * get the mutex, set sleeping so we can detect an abnormal * return, and indicate why we wait */ ihcp_mutex_lock(&unit_p->mutex); unit_p->sleeping = 1; unit_p->unit_flags |= IHCP_HALF_WAIT; /* * enable plx and ihcp interrupt for fifo less than half full * we don't or into ihcp or plx mask reg since we know exactly which ints to enb */ IHCP_PUTL(IHCP_INTERRUPT_MASK, FIFO_NOT_HALF_INT_ENB); PLX_PUTL(PLX_INT_CSTAT, (PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE)); /* * snooze until ready interrupt, timeout, or signal */ DPRINTI("at HALF_WAIT, time_now = %ld, unit_p->fifo_time = %d", jiffies, unit_p->fifo_time); wait_return = ihcp_cond_timed_wait_rel(&unit_p->cond, &unit_p->mutex, unit_p->fifo_time); /* * make sure interrupt enables are off and clear flag in case we * got here via timeout just as interrupt happened */ IHCP_PUTL(IHCP_INTERRUPT_MASK, 0); PLX_PUTL(PLX_INT_CSTAT, 0); IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); ihcp_mutex_unlock(&unit_p->mutex); /* * find out why we woke up (we could probably dump if sleeping and * use the return value of cond_timed_wait_rel directly, but ... */ if (unit_p->sleeping) { unit_p->sleeping = 0; DPRINTI("at HALF_WAIT, abnormal return from ihcp_cond_timed_wait_rel()!"); if (wait_return == IHCP_COND_WAIT_TIMEOUT) { /* * timeout may not be an error, so no error message just flag and return * caller can test for timeout on error return */ DPRINTI("at HALF_WAIT, timeout while waiting for interrupt!"); unit_p->unit_flags |= IHCP_HALF_TIMEOUT; return -EIO; } if (wait_return == IHCP_COND_WAIT_SIGNAL) { /* * flag signal received and print error message */ WPRINTI("at HALF_WAIT, signal received while waiting for interrupt"); unit_p->unit_flags |= IHCP_SIG_RECEIVED; return -EINTR; } } /* end of if sleeping */ return 0; break; case MASK & IHCPIO_STREAM_ON: /* set streaming mode if 10106 */ DPRINTI("at STREAM_ON"); if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { WPRINTI("at STREAM_ON - attempt to set streaming mode on board strapped for versatec!"); return -ENOTTY; } /* * this command talks to the fifo - make sure there is room !! */ if ((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at STREAM_ON - no room in fifo for stream on command!"); return -EIO; } lt = SET_IO_MODE | DATA_STREAMING_MODE; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); /* * save streaming mode for compatibility with old vme ioctls */ unit_p->data_stream_mode = DATA_STREAMING_ON; break; case MASK & IHCPIO_STREAM_OFF: /* turn streaming off (if 10106) */ DPRINTI("at STREAM_OFF"); if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { WPRINTI("at STREAM_OFF - attempt to clear streaming mode on board strapped for versatec!"); return -ENOTTY; } /* * this command talks to the fifo - make sure there is room !! */ if ((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at STREAM_OFF - no room in fifo for stream off command!"); return -EIO; } /* * set_io_mode w/ nothing else or'd in turns off streaming * and auto ltr - we won't try to do streaming and auto ltr * at same time in this driver */ lt = SET_IO_MODE; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); /* * save streaming mode for compatibility with old vme ioctls */ unit_p->data_stream_mode = DATA_STREAMING_OFF; break; case MASK & IHCPIO_GET_FLAGS: /* get unit flag longword */ DPRINTI("at GET_FLAGS"); if (copy_to_user ((caddr_t) arg, &unit_p->unit_flags, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_GET_FIFO: /* get formatted fifo flags */ DPRINTI("at GET_FIFO"); /* * get fifo status bits and swizzle and complement * to match sbus driver format */ lt = IHCP_GETL(IHCP_INTERFACE_STATUS); bits = 0; if (!(lt & FIFO_NOT_FULL)) bits |= IHCPIO_FIFO_FULL; if (!(lt & FIFO_NOT_HALF_FULL)) bits |= IHCPIO_FIFO_HALF; if (lt & FIFO_EMPTY) bits |= IHCPIO_FIFO_EMPTY; if (copy_to_user((caddr_t) arg, &bits, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_MASTER_CLEAR: /* master clear board - not for use by wimps! */ /* clear ihcp logic and restore all defaults */ DPRINTI("at MASTER_CLEAR"); IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * make absolutely sure that dma and interrupts are off in plx chip */ PLX_PUTL(PLX_INT_CSTAT, 0); PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, 0); /* * set and clear mclr bit - other bits in this register are also * treated like pulses, and do not persist, so we don't need to * or in a bit, then and it out */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * set and clear the device reset bit, with a delay and write * buffer flush to guarantee the delay * * the mode read between puts should force the write buffer to flush */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, (IHCP_GETL(IHCP_INTERFACE_CONTROL) | RESET_DEVICE)); read_dump_0 = IHCP_GETL(IHCP_MODE); udelay(1); IHCP_PUTL(IHCP_INTERFACE_CONTROL, (IHCP_GETL(IHCP_INTERFACE_CONTROL) & ~RESET_DEVICE)); /* * set to current print mode, default handshake speed, and default * byte ordering * * we don't have to check for fifo space, since we just cleared it * * print mode is set only if the board is strapped for versatec * * set byte order to global default - NOT current board mode */ unit_p->byte_order = ihcp_byte_order_def; ihcp_set_byte_order(unit_p); if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { /* * versatec - set speed and print/plot mode */ IHCP_PUTL(IHCP_MODE, ((IHCP_GETL(IHCP_MODE) & ~SPEED_MASK) | unit_p->vers_speed_def)); lt = SET_VERSATEC_MODE | unit_p->mode_def; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); unit_p->mode = unit_p->mode_def; } else { /* * centronics - set speed only */ IHCP_PUTL(IHCP_MODE, ((IHCP_GETL(IHCP_MODE) & ~SPEED_MASK) | unit_p->cent_speed_def)); /* * clear saved data streaming mode */ unit_p->data_stream_mode = DATA_STREAMING_OFF; } break; case MASK & IHCPIO_SOFT_ACK: /* software ack - looks like an ack from the dev */ DPRINTI("at SOFT_ACK"); /* * set and clear soft ack bit, as in mclr */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, SOFTWARE_ACK); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); break; case MASK & IHCPIO_AUTO_LTR_COUNT: /* set byte count for auto ltr function */ DPRINTI("at AUTO_LTR_COUNT, byte count = %d", bits); if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at AUTO_LTR_COUNT - attempt to set auto ltr count on board strapped for centronics!"); return -ENOTTY; } /* * auto ltr count is 16 bit register, accessed as two * separate bytes * * the count register takes count minus 1 */ IHCP_PUTL(IHCP_AUTO_LTR_LOW, (bits - 1) & 0xFF); IHCP_PUTL(IHCP_AUTO_LTR_HIGH, ((bits - 1) >> 8) & 0xFF); break; case MASK & IHCPIO_AUTO_LTR_ON: /* enable auto ltr operation */ DPRINTI("at AUTO_LTR_ON"); if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at AUTO_LTR_ON - attempt to set auto ltr mode on board strapped for centronics!"); return -ENOTTY; } /* * this command talks to the fifo - make sure there is room !! */ if ((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at AUTO_LTR_ON - no room in fifo for auto ltr on command!"); return -EIO; } lt = SET_IO_MODE | AUTO_LTR_MODE; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); break; case MASK & IHCPIO_AUTO_LTR_OFF: /* disable auto ltr operation */ DPRINTI("at AUTO_LTR_OFF"); if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at AUTO_LTR_OFF - attempt to clear auto ltr mode on board strapped for centronics!"); return -ENOTTY; } /* * this command talks to the fifo - make sure there is room !! */ if ((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at AUTO_LTR_OFF - no room in fifo for auto ltr off command!"); return -EIO; } /* * set io mode with no other bits clears auto ltr and data streaming */ lt = SET_IO_MODE; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); break; case MASK & IHCPIO_DEV_AND_VEND_ID: /* return vendor id in low 16 bits, */ /* device id in high 16 bits */ DPRINTI("at DEV_AND_VEND_ID"); bits = unit_p->dev_and_vendor_id; if (copy_to_user((caddr_t) arg, &bits, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_REVISION_ID: /* return board revision id */ DPRINTI("at REVISION_ID"); bits = unit_p->revision_id; if (copy_to_user((caddr_t) arg, &bits, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_LITTLE_ENDIAN: /* data will go to plotter low byte first */ DPRINTI("at LITTLE_ENDIAN"); /* * low first byte ordering (the hardware default) is set by turning off * the byte swizzle bit in the ihcp logic, and TURNING ON * the user output bit in the plx logic] */ IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) & ~BYTE_SWIZZLE)); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) | USER_OUTPUT)); unit_p->byte_order = LOW_BYTE_FIRST; break; case MASK & IHCPIO_BIG_ENDIAN: /* data will go to plotter high byte first */ DPRINTI("at BIG_ENDIAN"); /* * turn on the ihcp byte swizzle bit and TURN OFF the plx * user output bit */ IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) | BYTE_SWIZZLE)); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) & ~USER_OUTPUT)); unit_p->byte_order = HIGH_BYTE_FIRST; break; case MASK & IHCPIO_DIRECT_MODE: /* direct user write of mode register - use carefully! */ DPRINTI("at DIRECT_MODE"); IHCP_PUTL(IHCP_MODE, bits); break; case MASK & IHCPIO_DEVICE_CONTROL: /* direct write to device control register */ DPRINTI("at DEVICE_CONTROL"); IHCP_PUTL(IHCP_DEVICE_CONTROL, bits); break; case MASK & IHCPIO_INTERFACE_STATUS: /* direct read of interface status register */ DPRINTI("at INTERFACE_STATUS"); bits = IHCP_GETL(IHCP_INTERFACE_STATUS); if (copy_to_user((caddr_t) arg, &bits, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_DEVICE_STATUS: /* direct read of device status register */ DPRINTI("at DEVICE_STATUS"); bits = IHCP_GETL(IHCP_DEVICE_STATUS); if (copy_to_user((caddr_t) arg, &bits, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_REVERSE_DATA: /* direct read of reverse data register - requires */ /* fiddling of tri-state bit, and others to actually */ /* do anything useful with this */ DPRINTI("at REVERSE_DATA"); bits = IHCP_GETL(IHCP_REVERSE_DATA); if (copy_to_user((caddr_t) arg, &bits, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; /* * ioctls for compatibility with versatec and sun drivers for VME and Sbus boards */ case MASK & LPSETVERSATEC: /* compatibility - set versatec port */ DPRINTI("at LPSETVERSATEC"); /* * make sure the board is a versatec type - can't do this w/centr */ if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at LPSETVERSATEC - attempt to select versatec port onn board strapped for centronics!"); return -ENOTTY; } break; case MASK & LPSETCENTRONICS: /* compatibility - set centr port */ DPRINTI("at LPSETCENTRONICS"); if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { WPRINTI("at LPSETCENTRONICS - attempt to select centronics port on board strapped for versatec!"); return -ENOTTY; } break; case MASK & LPCOMMAND: /* compatibility - old style cmds */ DPRINTI("at LPCOMMAND"); /* * as much as possible, allow multiple command bits in the argument. * this is probably not the way it was intended, but we want to * protect ourselves against non-reasonable uses of the old calls */ /* * assert long reset signal */ if (bits & LPC_LRST) IHCP_PUTL(IHCP_INTERFACE_CONTROL, IHCP_GETL(IHCP_INTERFACE_CONTROL) | RESET_DEVICE); /* * de-assert long reset */ if (bits & LPC_DRST) IHCP_PUTL(IHCP_INTERFACE_CONTROL, IHCP_GETL(IHCP_INTERFACE_CONTROL) & ~RESET_DEVICE);; /* * test for option port request - better be strapped for centronics!! */ if (bits & LPC_SOPT) if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { WPRINTI("at LPCOMMAND - attempt to select centronics port on board strapped for versatec!"); return -ENOTTY; } /* * test for versatec port selection */ if (bits & LPC_SVPT) if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at LPCOMMAND - attempt to select versatec port on board strapped for centronics!"); return -ENOTTY; } /* * enable data streaming - if board is strapped for centronics! */ if (bits & LPC_DSTR) { if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { WPRINTI("at LPCOMMAND - attempt to set streaming mode on board strapped for versatec!"); return -ENOTTY; } /* * this command talks to the fifo - make sure there is room !! */ if ( (IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at LPCOMMAND - no room in fifo for stream on command!"); return -EIO; } lt = SET_IO_MODE | DATA_STREAMING_MODE; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); /* * save for posterity */ unit_p->data_stream_mode = DATA_STREAMING_ON; } /* reset data streaming bit */ if (bits & LPC_DDST) { if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { WPRINTI("at LPCOMMAND - attempt to clear streaming mode on non-centronics unit!"); return -ENOTTY; } if ( (IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at LPCOMMAND - no room in fifo for stream off command!"); return -EIO; } /* * set_io_mode w/ nothing else or'd in turns off streaming * and auto ltr - we won't try to do streaming and auto ltr * at same time in this driver */ lt = SET_IO_MODE; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); /* * save streaming mode for later getregs call */ unit_p->data_stream_mode = DATA_STREAMING_OFF; } /* * test for versatec print/plot mode changes - make sure we are * a versatec board !! */ if (bits & LPC_PRINTMASK) { if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at LPCOMMAND - attempt to do versatec mode set on board strapped for centronics!"); return -ENOTTY; } /* * this cmd talks to the fifo - make sure there is room !! */ if ( (IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at LPCOMMAND - no room in fifo for versatec mode set!"); return -EIO; } /* * get current mode setting */ lt = unit_p->mode; /* now look at individual command bits and twiddle mode */ if (bits & LPC_VSPP) lt |= SPP_MODE; /* set spp mode */ if (bits & LPC_DSPP) lt &= ~SPP_MODE; /* clear spp */ if (bits & LPC_VPLT) lt |= PLOT_MODE; /* set plot mode */ if (bits & LPC_DVPT) lt &= ~PLOT_MODE; /* clear plot */ if (bits & LPC_PRNT) lt = NORM_PRINT_MODE; /* 0 = normal print mode */ /* * save final mode - without mode set command bit - * in unit structure */ unit_p->mode = lt; /* * write final mode to fifo - remember to write to high * byte if big-endian data mode selected */ lt |= SET_VERSATEC_MODE; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); } /* * check for software ack pulse request */ if (bits & LPC_SACK) { /* * set and clear soft ack bit, as in mclr */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, SOFTWARE_ACK); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); } /* * check for master clear request - the old vme board also issued a reset * to the device when mclr was pulsed, so we will do that also * * we will also restore the default print/plot mode (if versatec), * the device handshake speed, and the byte ordering */ if (bits & LPC_MCLR) { IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * make absolutely sure that dma and interrupts are off in plx chip */ PLX_PUTL(PLX_INT_CSTAT, 0); PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, 0); /* * set and clear mclr bit - other bits in this register are also * treated like pulses, and do not persist, so we don't need to * or in a bit, then and it out */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * set and clear the device reset bit, with a delay and write * buffer flush to guarantee the delay * * the mode read between puts should force the write buffer to flush */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, (IHCP_GETL(IHCP_INTERFACE_CONTROL) | RESET_DEVICE)); read_dump_0 = IHCP_GETL(IHCP_MODE); udelay(1); IHCP_PUTL(IHCP_INTERFACE_CONTROL, (IHCP_GETL(IHCP_INTERFACE_CONTROL) & ~RESET_DEVICE)); /* * set to current print mode, default handshake speed, and default * byte ordering * * we don't have to check for fifo space, since we just cleared it * * print mode is set only if the board is strapped for versatec * */ ihcp_set_byte_order(unit_p); if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { /* * versatec - set speed and print/plot mode */ IHCP_PUTL(IHCP_MODE, ((IHCP_GETL (IHCP_MODE) & ~SPEED_MASK) | unit_p->vers_speed_def)); lt = SET_VERSATEC_MODE | unit_p->mode_def; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); unit_p->mode = unit_p->mode_def; } else { /* * centronics - set speed only */ IHCP_PUTL(IHCP_MODE, ((IHCP_GETL (IHCP_MODE) & ~SPEED_MASK) | unit_p->cent_speed_def)); } /* * clear saved data streaming mode */ unit_p->data_stream_mode = DATA_STREAMING_OFF; } /* * look for pulsed command request that has to go through fifo */ if (bits & (LPC_VCLR | LPC_VTFF | LPC_VEOT | LPC_VLTR)) { /* make sure the board is a versatec type - can't do w/centr */ if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at LPCOMMAND - attempt to do versatec pulsed command to board strapped for centronics!"); return -ENOTTY; } /* * this cmd talks to the fifo - make sure there is room !! * WE WILL ASSUME THAT THERE WILL ONLY BE ONE PULSE PER CALL */ if ( (IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at LPCOMMAND - no room in fifo for versatec pulsed command!"); return -EIO; } /* * shuffle vme style commands to match pci board */ if (bits & LPC_VLTR) lt = VERSATEC_LINE_TERM; if (bits & LPC_VEOT) lt = VERSATEC_EOT; if (bits & LPC_VTFF) lt = VERSATEC_FORM_FEED; if (bits & LPC_VCLR) lt = VERSATEC_CLEAR; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); } break; case MASK & LPGETREGS: /* old style (VMEbus) get regs command */ DPRINTI("at LPGETREGS"); /* * start work on interface status register */ st = 0; /* * get interface status register * and fiddle interface and device ready bits */ ct = (u_char) IHCP_GETL(IHCP_INTERFACE_STATUS); if (ct & DEVICE_AND_INT_READY) st |= OLD_DIRY; if (ct & DEVICE_READY) st |= OLD_DVRY; /* * get remembered data streaming mode */ if (unit_p->data_stream_mode == DATA_STREAMING_ON) st |= OLD_DSTR; /* * get board type from unit struct * if centronics, flag as old style option port */ if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) st |= OLD_SOPT; /* * save result in cis portion of lp_regs structure * * we used to put it in the top of a u_long, and the cds portion * in the bottom, but we get into trouble with "endian-ness" * moving across the various platforms * * starting with this port, we will use the struct instead */ lp_regs.cisreg = st; /* * do the same ugly things with the device status register -- * it has two halves: versatec and centronics -- only * work on the appropriate half */ st = 0; if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { /* * work on centronics part * get the device status register and * shuffle bit positions and polarities */ ct = (u_char) IHCP_GETL(IHCP_DEVICE_STATUS); if (ct & VERSATEC_READY) st |= OLD_OACK; if (!(ct & CENTRONICS_BUSY)) st |= OLD_ONBY; if (!(ct & PAPER_OUT)) st |= OLD_OPPR; if (!(ct & ONLINE)) st |= OLD_ONSL; if (ct & FAULT) st |= OLD_OFLT; /* * look for device reset latch */ if (IHCP_GETL(IHCP_INTERFACE_CONTROL) & RESET_DEVICE) st |= OLD_OLRS; /* long reset */ } else { /* * work on versatec part */ if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_VERS_TTL) st |= OLD_VTTL; /* * get device status and twiddle bits */ ct = (u_char) IHCP_GETL(IHCP_DEVICE_STATUS); if (ct & VERSATEC_READY) st |= OLD_VRDY; /* vers ready */ if (ct & PAPER_OUT) st |= OLD_VPPR; /* paper ok */ if (ct & ONLINE) st |= OLD_VONL; /* vers onlin */ /* * get current mode from unit structure */ ct = unit_p->mode; if (ct & SPP_MODE) st |= OLD_VSPP; /* spp mode */ if (ct & PLOT_MODE) st |= OLD_VPLT; /* plot mode */ } /* * save in cds portion of status structure */ lp_regs.cdsreg = st; if (copy_to_user((caddr_t) arg, &lp_regs, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & LPSETTIMVAL: /* set timeout value - old style - for dma and fifo empty */ DPRINTI("at LPSETTIMVAL"); /* * old style timeout call used fiftieths of a second as argument */ if (bits < DMA_TIME_MIN * 50) bits = DMA_TIME_MIN * 50; if (bits > DMA_TIME_MAX * 50) bits = DMA_TIME_MAX * 50; unit_p->fifo_time = (u_long) ((bits * HZ) / 50); unit_p->dma_time = (u_long) ((bits * HZ) / 50); break; case MASK & LPGETTIMVAL: /* get current timeout (in ticks ?) */ DPRINTI("at LPGETTIMVAL"); /* * return # of fiftieths of a second */ lt = (unit_p->dma_time / HZ) * 50; if (copy_to_user((caddr_t) arg, <, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; default: /* * we don't print an error message here * if we are involved in a pipe, the pipe logic may send * us a generic ioctl to determine if we are a terminal * we have to return an error or we will give a false * terminal indication. */ return -EINVAL; break; } return 0; } /* * our interrupt handler - registered with the system in init_module * each board's instance will register the same handler, but with a different unit * structure pointer * * when called, dev_id is a pointer at the soft state structure for this instance * * as of the 2.3.x kiobuf version, we use mutex and conditional variable for SMP * compatibility * * 2.6.x adds an interrupt claimed return value */ static irqreturn_t ihcp_intr(int irq, void *dev_id IHCP__PT_REGS) { struct ihcp_unit_t *unit_p; volatile u_long temp; int instance; int wake_up_needed; /* * get the unit struct pointer this way to avoid parameter type * complaints by the compiler, get instance for debugging, * clear wake up needed flag */ unit_p = (struct ihcp_unit_t *) dev_id; instance = unit_p->instance; wake_up_needed = 0; DPRINTI("entering ihcp_intr (may not be our interrupt)"); /* * grab mutex - in case strategy and int thread are running on different cpu's ??? */ ihcp_mutex_lock(&unit_p->mutex); /* * get plx interrupt control/status register and check for interrupt * check for master interrupt enable bit and either local int rq or dma int rq */ temp = PLX_GETL(PLX_INT_CSTAT); if ((temp & PCI_INTERRUPT_ENABLE) && ((temp & LOCAL_INTERRUPT) || (temp & DMA_0_INTERRUPT))) { DPRINTI("interrupt claimed, unit_p = 0x%p", unit_p); /* * force off dma enable, interrupt enables, and ihcp interrupt mask bits * which should release the interrupt request * * then (after masks are off) toggle reset int flag bit on & off to clear * interrupt flag (which is a latch) * and do the same to the dma done interrupt */ PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, 0); PLX_PUTL(PLX_INT_CSTAT, 0); IHCP_PUTL(IHCP_INTERRUPT_MASK, 0); PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_CLEAR_INTERRUPT); IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * do register reads to get the previous writes to flush to the pci bus * so the writes that clear the interrupt will be complete before we * exit - we will hope that the optimizer doesn't delete these! * * may not be necessary on PC */ read_dump_0 = PLX_GETL(PLX_EEPROM_USER); read_dump_1 = IHCP_GETL(IHCP_MODE); /* * clear sleeping flag so sleeping process will know we got a normal interrupt * set wake up needed flag */ unit_p->sleeping = 0; wake_up_needed = 1; } /* * release mutex */ ihcp_mutex_unlock(&unit_p->mutex); if (wake_up_needed) { /* * wake up sleeping process */ DPRINTI("claiming int and waking up sleeping process"); ihcp_cond_broadcast(&unit_p->cond); return IRQ_HANDLED; } else { DPRINTI("interrupt not claimed"); return IRQ_NONE; } } /* * strategy expects the virtual address of the user buffer (portion) to be output * at this point, it must be quad byte aligned and a byte count that must be 0 mod 4 * * if we are in user buffer DMA mode, strategy will map and pin the buffer, construct * an sg list of buffer pages, map the pages to the bus (using the dynamic mapping stuff), * build the dma scatter-gather list in iopb memory, synch the iopb memory, and do a * single dma block output * * if we are in copy user buffer mode ( kernel is configured for >4G of RAM) * strategy will copy the to kernel memory buffer, and do dma from that buffer * * if the count parameter is zero, strategy will wait for fifo less than 1/2 full, * but not do any output * * THERE ARE SOME ERROR AND OTHER RETURNS IN THIS CODE - IF WE EVER NEED A MUTEX, GATHER * THEM WITH GOTOs, SO WE WON'T ESCAPE OUT THE SIDE OF THE MUTEX -- BE CAREFUL TO UNMAP * THINGS ON ERROR (IF ANYTHING WAS MAPPED AT ERROR TIME) */ static int ihcp_strategy(struct ihcp_unit_t *unit_p, char *dma_buf_p, int dma_count) { int instance; unsigned int temp_mode; unsigned int temp_dev_ctrl; int wait_return; int error; int doing_dma; instance = unit_p->instance; DPRINTI("entering strategy, unit_p = 0x%p, buf_p = 0x%p, count = 0x%x", unit_p, dma_buf_p, dma_count); /* * make sure buffer is quad byte aligned and count is zero mod four * this is not strictly necessary, but makes for a more generic strategy * routine and check for max allowable size */ if ((u_long) dma_buf_p & 0x3) { EPRINTI("dma buffer not quad word aligned!"); return -EINVAL; } if (dma_count & 0x3) { EPRINTI("dma count not 0 mod 4!"); return -EINVAL; } if (dma_count > ihcp_max_buf_bytes) { EPRINTI("dma count too large!"); return -EINVAL; } /* * set flags to indicate dma wait - even if we don't wait, just so flags * will reflect that we were in strategy * error flags are not cleared here so they will survive * multiple calls to strategy by write */ unit_p->unit_flags |= IHCP_DMA_WAIT; /* * we should now have an aligned buffer, with 0 or more bytes in it * if total buffer size (remaining) is less than 4, skip dma stuff, * and wait for 3) { doing_dma = 1; /* * map and pin - or copy - user buffer */ if(ihcp_map_or_copy_buf(unit_p, instance, dma_buf_p, dma_count, WRITE)) { WPRINTI("ihcp_map_or_copy_failure!"); return -EIO; } if(ihcp_maplist_to_sg_list(unit_p, instance)) { WPRINTI("ihcp_maplist_to_sg_list failure!"); ihcp_unmap_on_error(unit_p, instance); return -EIO; } /* * map the sg list (which may combine contiguous entries * there is no error return, as I am told that the mapping resources are huge */ unit_p->nr_iopbs = pci_map_sg(unit_p->pci_dev_p, unit_p->sg_list_p, unit_p->maplist_nr_pages, PCI_DMA_TODEVICE); DPRINTI("scatter list mapped, nr_iopbs = 0x%x", unit_p->nr_iopbs); /* * synch iopb memory for cpu before accessing it - this is commented out * since we don't care if it is up to date, since we will overwrite * it and sync for device - might need to use it later if kernel gods * assign some necessary side-effects pci_dma_sync_single_for_cpu(unit_p->pci_dev_p, unit_p->dma_handle, unit_p->nr_iopbs * IOPB_SIZE, PCI_DMA_TODEVICE); */ /* * convert sg list to iopb list for plx dma engine */ if(ihcp_sg_list_to_iopb_list(unit_p, instance)) { EPRINTI("ihcp_sg_list_to_iopb_list failure!"); pci_unmap_sg(unit_p->pci_dev_p, unit_p->sg_list_p, unit_p->maplist_nr_pages, PCI_DMA_TODEVICE); ihcp_unmap_on_error(unit_p, instance); return -EIO; } /* * synch iopb memory for dma - NOP on a pc - necessary on other * architectures to ensure coherence betweene list and device * synch only as much iopb space as was touched this time */ pci_dma_sync_single_for_device(unit_p->pci_dev_p, unit_p->dma_handle, unit_p->nr_iopbs * IOPB_SIZE, PCI_DMA_TODEVICE); DPRINTI("iopb memory synched for DMA"); /* * program plx dma logic on board - in chaining mode * buffer is already aligned, and count masked * * make sure that any left over dma done interrupt * is cleared */ DPRINTI("programming PLX chip for DMA"); PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_CLEAR_INTERRUPT); /* mode register */ PLX_PUTL(PLX_DMA_MODE_0, DMA_BUS_32_BIT | DMA_WAIT_1 | DMA_BURST_ENABLE | DMA_CHAIN_ENABLE | DMA_DONE_INTERRUPT_ENB | DMA_LOCAL_ADD_HOLD | DMA_DEMAND_MODE); /* output, list in pci mem, list pointer */ PLX_PUTL(PLX_DMA_DESC_PTR_0, DMA_CHAIN_IN_PCI_MEM | unit_p->dma_handle); /* start DMA in two steps - PLX bug */ PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_ENABLE); /* actually fire up xfer */ PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_ENABLE | DMA_START); /* * 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 */ /* * save processor flags, disable interrupts, and take spinlock w/mutex * before enabling board interrupts! * * THERE ARE TWO PLACES IN STRATEGY WHERE THIS MAY BE DONE, * BUT ONLY ONE PLACE WHERE FLAGS ARE RESTORED */ DPRINTI("disabling cpu ints and enabling board dma done int"); ihcp_mutex_lock(&unit_p->mutex); PLX_PUTL(PLX_INT_CSTAT, PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE | LOCAL_INT_OUT_ENABLE | LOCAL_DMA_0_INT_ENABLE); } else { doing_dma = 0; DPRINTI("no dma, checking for <1/2 full"); /* * we need to wait for fifo less than half full * it may already be less than half full - if so * return good status */ if (IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_HALF_FULL) return 0; DPRINTI("not <1/2 full, setting up for interrupt"); /* * clear any left over interrupt flag, then * enable plx and ihcp interrupt for fifo less than half full * we don't or into ihcp or plx mask reg since we know exactly * which ints to enb */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * save processor flags, disable interrupts, and take spinlock w/mutex * before enabling board interrupts! * * THERE ARE TWO PLACES IN STRATEGY WHERE THIS MAY BE DONE, * BUT ONLY ONE PLACE WHERE FLAGS ARE RESTORED */ DPRINTI("disabling cpu ints, and enabling board fifo < 1/2 full int"); ihcp_mutex_lock(&unit_p->mutex); IHCP_PUTL(IHCP_INTERRUPT_MASK, FIFO_NOT_HALF_INT_ENB); PLX_PUTL(PLX_INT_CSTAT, (PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE)); } /* * we arrive here after setting up for a dma wait, or a less than half full wait * * set sleeping flag - so we can detect abnormal sleep termination later * (int code will clear sleeping flag) * * board interrupts are already enabled, and processor interrupts disabled, * to avoid race conditions - sleep will re-enable * * snooze until done or sleeping = 1; DPRINTI("calling cond_timed_wait_rel, jiffies = %ld, unit_p->dma_time = %d", jiffies, unit_p->dma_time); wait_return = ihcp_cond_timed_wait_rel(&unit_p->cond, &unit_p->mutex, unit_p->dma_time); /* * make sure interrupt enables are off and pause dma (which will normally * already be done) */ PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, 0); IHCP_PUTL(IHCP_INTERRUPT_MASK, 0); PLX_PUTL(PLX_INT_CSTAT, 0); ihcp_mutex_unlock(&unit_p->mutex); /* * if we were doing dma, and * if dma is not done (after sig or timeout), force an abort * and issue a master clear to flush ihcp buffers * * abort may take some time to complete, so use delay and * register read to give it some time * * we have to be sure we were actually doing dma - since we need * to unmap the sg list and user buf (if used) */ if (doing_dma && !(PLX_GETL(PLX_DMA_CMD_STAT_BOTH) & DMA_DONE)) { DPRINTI("dma not done after ihcp_cond_timed_wait_rel() return"); PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_ABORT); /* * get mode and dev control regs so we can restore them after a mclr * also restore the byte ordering * * mclr clears the fifos, and also makes them assert dma rq * but...mclr holds off dma rq while mclr is asserted, and since the 9060 * likes a synchronous dma rq, we will wait a while before hitting mclr * * 10us of delay seems like a lot, but we will only be in this code * following an error * * 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 */ udelay(5); temp_dev_ctrl = IHCP_GETL(IHCP_DEVICE_CONTROL); temp_mode = IHCP_GETL(IHCP_MODE); IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * force write cache to flush - so delay appears on the bus */ read_dump_0 = IHCP_GETL(IHCP_INTERRUPT_MASK); udelay(5); IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); IHCP_PUTL(IHCP_DEVICE_CONTROL, temp_dev_ctrl); IHCP_PUTL(IHCP_MODE, temp_mode); ihcp_set_byte_order(unit_p); /* * make sure dma actually says done */ if (!(PLX_GETL(PLX_DMA_CMD_STAT_BOTH) & DMA_DONE)) { EPRINTI("dma not done after abort!"); pci_unmap_sg(unit_p->pci_dev_p, unit_p->sg_list_p, unit_p->maplist_nr_pages, PCI_DMA_TODEVICE); ihcp_unmap_on_error(unit_p, instance); return -EIO; } } /* * now we can safely make sure that the done interrupt is clear, * and reset the ihcp interrupt flag * * (the above is probably not necessary after normal dma done) */ PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_CLEAR_INTERRUPT); IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * if dma was performed, unmap the sg list (NOP on pc, release iommu mappings * on other machines) unmap sg wants the original sg list length * * unmap user buffer (if used) */ if (doing_dma) { DPRINTI("unmapping sg list and user buffer (if mapped) after dma"); pci_unmap_sg(unit_p->pci_dev_p, unit_p->sg_list_p, unit_p->maplist_nr_pages, PCI_DMA_TODEVICE); if(ihcp_unmap_or_copy_buf(unit_p, instance)) { EPRINTI("ihcp_unmap_or_copy_buf failure!"); return -EIO; } } /* * find out why we woke up */ if (unit_p->sleeping) { /* * flag some kind of error - in case both the following tests fail */ unit_p->sleeping = 0; error = -EIO; DPRINTI("abnormal return from ihcp_cond_timed_wait_rel()!"); /* * check for timeout */ if (wait_return == IHCP_COND_WAIT_TIMEOUT) { /* * timeout is always an error so print message and return error code to write * and flag timeout in unit flags */ EPRINTI("timeout while waiting for interrupt!"); unit_p->unit_flags |= IHCP_DMA_TIMEOUT; error = -EIO; } /* * check for signal if not masked (blocked is a signal bitmask) */ if (wait_return == IHCP_COND_WAIT_SIGNAL) { /* * flag signal received and print error message */ WPRINTI("signal received while waiting for interrupt"); unit_p->unit_flags |= IHCP_SIG_RECEIVED; error = -EINTR; } /* * save latched registers and do a soft reset * restore saved registers and byte ordering * * this is added for 2.6.x */ temp_dev_ctrl = IHCP_GETL(IHCP_DEVICE_CONTROL); temp_mode = IHCP_GETL(IHCP_MODE); plx_soft_reset(unit_p); IHCP_PUTL(IHCP_DEVICE_CONTROL, temp_dev_ctrl); IHCP_PUTL(IHCP_MODE, temp_mode); ihcp_set_byte_order(unit_p); return error; } /* end of if sleeping * * if we get here - just return good status * there is no way to figure a residual count with the hardware at hand * so we don't even try */ return 0; } /* * mutex and conditional variable routines - were in ikon_mutex.c * Kaz Kylheku & wdw */ void ihcp_mutex_init(ihcp_mutex_t * mx) { spin_lock_init(&mx->spin); } void ihcp_mutex_lock(ihcp_mutex_t * mx) { unsigned long flags; spin_lock_irqsave(&mx->spin, flags); mx->flags = flags; } void ihcp_mutex_unlock(ihcp_mutex_t * mx) { unsigned long flags = mx->flags; spin_unlock_irqrestore(&mx->spin, flags); } void ihcp_cond_init(ihcp_cond_t * cv) { init_waitqueue_head(&cv->queue); } int ihcp_cond_timed_wait_rel(ihcp_cond_t * cv, ihcp_mutex_t * mx, long jiff_delta) { wait_queue_t wait; int ret; long remaining; init_waitqueue_entry(&wait, current); add_wait_queue(&cv->queue, &wait); current->state = TASK_INTERRUPTIBLE; ihcp_mutex_unlock(mx); remaining = schedule_timeout(jiff_delta); ret = remaining ? signal_pending(current) ? IHCP_COND_WAIT_SIGNAL : IHCP_COND_WAIT_SUCCESS : IHCP_COND_WAIT_TIMEOUT; current->state = TASK_RUNNING; remove_wait_queue(&cv->queue, &wait); ihcp_mutex_lock(mx); return ret; } void ihcp_cond_broadcast(ihcp_cond_t * cv) { wake_up_interruptible(&cv->queue); } /* * do a soft reset of the plx chip - also clears the ihcp logic * * the pci_dev handle, and int_level elements of the unit structure * MUST be set before calling this routine! * * ALSO - THIS ROUTINE MUST NOT BE CALLED FROM THE INTERRUPT SIDE OF THINGS!! * (it calls schedule to do a delay) */ static void plx_soft_reset(struct ihcp_unit_t *unit_p) { u_long wait_j; /* wait jiffies */ /* * 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.2s) 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 * * do a read dump before the reload to force any write buffer to flush, so our wait * timing is reflected on the bus (hopefully). if we try to access the plx chip * during reload, retry cycles happen, and a short bus timer could cause a panic * * 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 RESTORE THE INT LINE AFTER SOFT_RESET * * soft reset also clears the endian setting and other latches so the calling * could should set/reset as appropriate * * we probably don't have to clear the config reload bit after writing it, * but we will to be sure */ /* * there is really no good way to return an error from here - so just put it * on the console and return without touching anything */ PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) | PLX_SOFT_RESET)); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) & ~PLX_SOFT_RESET)); read_dump_0 = PLX_GETL(PLX_INT_CSTAT); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) | CONFIGURATION_RELOAD)); /* * wait ... */ wait_j = jiffies + HZ / 5; while (time_before(jiffies, wait_j)) schedule(); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) & ~CONFIGURATION_RELOAD)); pci_write_config_byte(unit_p->pci_dev_p, PCI_INTERRUPT_LINE, unit_p->int_level); DPRINT("instance %d: soft reset and configuration reload complete", unit_p->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_PUTL(PLX_INT_CSTAT, 0); /* * restore endian mode default * the reset sets us back to little-endian * nothing to do if still little-endian */ ihcp_set_byte_order(unit_p); /* * disable address space 0 pre-fetch */ PLX_PUTL(PLX_ADD_0_ROM_DESC, PLX_GETL(PLX_ADD_0_ROM_DESC) | ADD_0_PREFETCH_DISABLE); } /* * establish driver and board defaults - some of these will be used to establish * initial configuration on a per-board basis * * there are a lot of these things - to try to stay consistent with solaris driver * and conf file. we could probably eliminate one level of defaults (in unit structure) * but this way will avoid some code modification, and let the insmod parameters match * the solaris .conf parameters * * the config variables that end in _def may be later modified by ioctl calls on a per * board basis * * examine the variables that may be set during insmod - if not -1, and within * permitted range, use the insmod supplied value - if -1, use the default * * some of the range checks look redundant ( .. >= 0 ..), but I prefer to keep the * insmod set detection separate from the range check, and not assume anything */ static void ihcp_set_global_defaults(void) { DPRINT("entering ihcp_set_global_defaults"); ihcp_byte_order_def = BYTE_ORDER_DEF; if (byte_order_def >= 0) { if ((byte_order_def == LOW_BYTE_FIRST) || (byte_order_def == HIGH_BYTE_FIRST)) ihcp_byte_order_def = byte_order_def; else WPRINT("byte_order_def out of range! using default"); } DPRINT("ihcp_byte_order_def = 0x%x", ihcp_byte_order_def); /* * select one of the four versatec speeds as a default */ ihcp_vers_speed_def = VERS_SPEED_DEF; if (vers_speed_def >= 0) { if ((vers_speed_def >= 0) && (vers_speed_def <= 3)) ihcp_vers_speed_def = vers_speed_def; else WPRINT("vers_speed_def out of range! using default"); } DPRINT("ihcp_vers_speed_def = 0x%x", ihcp_vers_speed_def); /* * select one of the four centronics speeds as the default */ ihcp_cent_speed_def = CENT_SPEED_DEF; if (cent_speed_def >= 0) { if ((cent_speed_def >= 0) && (cent_speed_def <= 3)) ihcp_cent_speed_def = cent_speed_def; else WPRINT("cent_speed_def out of range! using default"); } DPRINT("ihcp_cent_speed_def = 0x%x", ihcp_cent_speed_def); /* * select power-up print/plot mode for versatec boards * 0 = print, 1 = plot (probably should use #macros, not numeric values, but ... */ ihcp_mode_def = MODE_DEF; if (mode_def >= 0) { if ((mode_def == 0) || (mode_def == 1)) ihcp_mode_def = mode_def; else WPRINT("mode_def out of range! using default"); } DPRINT("ihcp_mode_def = 0x%x", ihcp_mode_def); /* * set default time to wait for dma to complete */ ihcp_dma_time_def = DMA_TIME_DEF; if (dma_time_def >= 0) { if ((dma_time_def >= DMA_TIME_MIN) && (dma_time_def <= DMA_TIME_MAX)) ihcp_dma_time_def = dma_time_def; else WPRINT("dma_time_def out of range! using default"); } DPRINT("ihcp_dma_time_def = 0x%x", ihcp_dma_time_def); /* * set default time to wait for fifo empty and board and device ready */ ihcp_fifo_time_def = FIFO_TIME_DEF; if (fifo_time_def >= 0) { if ((fifo_time_def >= FIFO_TIME_MIN) && (fifo_time_def <= FIFO_TIME_MAX)) ihcp_fifo_time_def = fifo_time_def; else WPRINT("fifo_time_def out of range! using default"); } DPRINT("ihcp_fifo_time_def = 0x%x", ihcp_fifo_time_def); /* * set max board number - this will determine how much we kmalloc for the * unit structures. we will do a sanity check, and require this number to * be a single digit */ ihcp_max_boards = MAX_BOARDS_DEF; if (max_boards >= 0) { if ((max_boards >= 0) && (max_boards <= 9)) ihcp_max_boards = max_boards; else WPRINT("max_boards out of range! using default"); } DPRINT("ihcp_max_boards = %d", ihcp_max_boards); /* * set max_phys_order and several values * that derive from it * * max buffer pages = 2**max phys order * max bytes per dma xfer = max buffer pages * PAGE_SIZE * max size of scatter/gather list = max buffer pages + 1 * * these calculations must track w/copy buf and user buf map size as well * * size of iopb memory allocated = max sg size * IOPB_SIZE * * we will check for a request less than zero, but not limit the * upper end - USER BEWARE! */ ihcp_max_phys_order = MAX_PHYS_ORDER_DEF; if (max_phys_order >= 0) ihcp_max_phys_order = max_phys_order; ihcp_max_buf_pages = (1 << ihcp_max_phys_order); ihcp_max_buf_bytes = ihcp_max_buf_pages * PAGE_SIZE; ihcp_max_sg_length = ihcp_max_buf_pages + 1; ihcp_iopb_mem_size = ihcp_max_sg_length * IOPB_SIZE; DPRINT("max_phys_order = 0x%x, max_bytes = 0x%x,\n" " max_sg_length = 0x%x, iopb_mem_size = 0x%x", ihcp_max_phys_order, ihcp_max_buf_bytes, ihcp_max_sg_length, ihcp_iopb_mem_size); } /* * do the per-board initialization * return 0 if successful, error if not */ static int ihcp_init_one_board(struct ihcp_unit_t *unit_p, int instance) { u32 temp; u_char c_temp; /* * init flags, mutex, and conditional variable */ ihcp_mutex_init(&unit_p->mutex); ihcp_cond_init(&unit_p->cond); unit_p->sleeping = 0; unit_p->unit_attached = 0; unit_p->unit_open = 0; spin_lock_init(&unit_p->open_lock); unit_p->unit_flags = 0; /* * allocate dma resources - iopb list memory, sg list memory, user buffer map list, * and copy buffer if direct user dma not to be used - and set dma mask */ if(ihcp_dma_alloc(unit_p, instance)) { EPRINT("ihcp_dma_alloc error! board not attached!"); return -EIO; } /* * grind through the config registers to get register base addresses and other * values * * use the new pci interface, but keep the rest of the old code (lazy) * * the base address registers contain the bus addresses of our two register sets - * plx and ihcp * unfortunately, the plx register set is smaller than a page, so bios may align it * on an address that isn't a page boundary * * ioremap doesn't tolerate that, so we have to figure out the appropriate (maybe * lower) page boundary and the appropriate size to ask for * we will save the mapped base and the actual virtual pointer to the register set * * I hope linux allows multiple processes to map the same bus page, or we may * clobber someone else */ /* * make sure io and memory access is enabled * make sure that the pci master enable bit is set * * pci_enable_device does some things on non-x86 arch's * that must be done before accessing device resources registers */ if(pci_enable_device(unit_p->pci_dev_p)) { EPRINTI("pci_enable_device error! board not attached"); ihcp_dma_free(unit_p, instance); return -EIO; } pci_set_master(unit_p->pci_dev_p); DPRINTI("master enable bits set"); /* * map the board's registers for slave access */ if(ihcp_map_regs(unit_p, instance)) { EPRINTI("ihcp_map_regs error! board not attached"); ihcp_dma_free(unit_p, instance); return -EIO; } /* * get (possibly) mapped int level from pci_dev for use in kernel calls, and * the hardware level from the board for use when restoring the level after * an eeprom reload */ unit_p->pci_dev_int_level = unit_p->pci_dev_p->irq; if (pci_read_config_byte (unit_p->pci_dev_p, PCI_INTERRUPT_LINE, (u_char *) & c_temp)) { EPRINTI("error reading pci interrupt line!"); ihcp_unmap_regs(unit_p, instance); ihcp_dma_free(unit_p, instance); return -EIO; } unit_p->int_level = c_temp; DPRINTI("board int line = 0x%x, pci_dev int line = 0x%x", unit_p->int_level, unit_p->pci_dev_int_level); /* * save device and vendor ids and rev level */ if (pci_read_config_dword (unit_p->pci_dev_p, PCI_VENDOR_ID, (int *) &temp)) { EPRINTI("error reading pci vendor id! board not attached!"); ihcp_unmap_regs(unit_p, instance); ihcp_dma_free(unit_p, instance); return -EIO; } unit_p->dev_and_vendor_id = temp; DPRINTI("device and vendor id = 0x%x", temp); if (pci_read_config_byte (unit_p->pci_dev_p, PCI_REVISION_ID, (char *) &temp)) { EPRINTI("error reading revision id! board not attached!"); ihcp_unmap_regs(unit_p, instance); ihcp_dma_free(unit_p, instance); return -EIO; } unit_p->revision_id = temp & 0xff; DPRINTI("revision id = 0x%x", temp & 0xff); /* * do a soft reset of the plx chip - which also clears the ihcp logic * this will also cause an init to be sent to the device * (soft reset function also clears big-endian if set, but the * soft reset code will restore it - as long as it is correctly * set in the unit struct prior to calling soft_reset) */ plx_soft_reset(unit_p); /* * get the board strapping and save it * zeros rest of flag register at same time * MUST DO BEFORE CALLING ihcp_set_board_defaults() */ unit_p->unit_flags = INTERFACE_STRAP_MASK & IHCP_GETL(IHCP_DEVICE_STATUS); DPRINTI("sanity check: interface strapping = 0x%x", IHCP_BOARD_MASK & unit_p->unit_flags); /* * set defaults on a per-board basis - MUST BE DONE AFTER SOFT RESET NOT BEFORE! * soft reset clears latches and BYTE ORDERING bits! */ ihcp_set_board_defaults(unit_p, instance); /* * get a plx register and dprint it for a sanity check - make sure we can get to plx regs */ DPRINTI("sanity check: PLX EEPROM reg = 0x%x (s.b. xxxx767e)", PLX_GETL(PLX_EEPROM_USER)); /* * get the board strapping and save it * zeros rest of flag register at same time */ unit_p->unit_flags = INTERFACE_STRAP_MASK & IHCP_GETL(IHCP_DEVICE_STATUS); DPRINTI("sanity check: interface strapping = 0x%x", IHCP_BOARD_MASK & unit_p->unit_flags); /* * register the interrupt line for this board & link to our interrupt handler * pass our unit pointer to the interrupt code * * we will use our node name as a label for the interrupt - that way * /proc/interrupts will have a unique name per interrupt (per board) rather * than all of them being named ihcp (we could use this when mknod is invoked) * * we have to keep the name in the unit structure, since request_irq just * saves the pointer, and doesn't copy the string - so by the time * /proc/interrupts gets read, the stack has changed, and the name would be * garbage if not kept in "permanent" memory * * (thank you Mr. Rubini!) */ (void) sprintf((char *) unit_p->minor_node_name, "ihcp%d", instance); if (request_irq (unit_p->pci_dev_int_level, ihcp_intr, IRQF_SHARED, (const char *) unit_p->minor_node_name, (void *) unit_p)) { EPRINTI("error requesting interrupt! board not attached!"); ihcp_unmap_regs(unit_p, instance); ihcp_dma_free(unit_p, instance); return -EIO; } if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) IPRINTI("board attached, irq = 0x%x, strapped for CENTRONICS", unit_p->pci_dev_int_level); else IPRINTI("board attached, irq = 0x%x, strapped for VERSATEC", unit_p->pci_dev_int_level); return 0; } /* * sort out and save the defaults on a per-board basis * some of this is probably redundant, but we are trying * to stay with the solaris code as much as possible * * the ranges of any insmod-supplied parameters have already been checked */ static void ihcp_set_board_defaults(struct ihcp_unit_t *unit_p, int instance) { unsigned int temp; DPRINTI("entering routine"); switch (ihcp_vers_speed_def) { case 0: unit_p->vers_speed_def = SPEED_0; break; case 1: unit_p->vers_speed_def = SPEED_1; break; case 2: unit_p->vers_speed_def = SPEED_2; break; case 3: unit_p->vers_speed_def = SPEED_3; break; } DPRINTI("versatec speed default set to 0x%x", unit_p->vers_speed_def); switch (ihcp_cent_speed_def) { case 0: unit_p->cent_speed_def = SPEED_0; break; case 1: unit_p->cent_speed_def = SPEED_1; break; case 2: unit_p->cent_speed_def = SPEED_2; break; case 3: unit_p->cent_speed_def = SPEED_3; break; } DPRINTI("centronics speed default set to 0x%x", unit_p->cent_speed_def); if (ihcp_mode_def == 1) unit_p->mode_def = PLOT_MODE; else unit_p->mode_def = NORM_PRINT_MODE; DPRINTI("mode default set to 0x%x", unit_p->mode_def); unit_p->dma_time_def = ihcp_dma_time_def; DPRINTI("dma time default set to 0x%x", unit_p->dma_time_def); unit_p->fifo_time_def = ihcp_fifo_time_def; DPRINTI("fifo time default set to 0x%x", unit_p->fifo_time_def); /* * set the data byte ordering default mode * the native ordering for the board is low byte first * * if high byte first mode is selected, the single byte data out and command out * bytes must be sent to the fifos in the HIGH byte of the longword! * * ordering is set here, and stored in the unit structure, since it will affect * all later accesses to the 8 bit data and command registes * * we only take action if high_byte_first is selected, since the preceeding reset * should have set us to low_byte_first mode * MUST CALL SOFT RESET BEFORE THIS INIT ROUTINE!!!! * * setting high byte first mode requires turning on the byte_swizzle bit in the * ihcp logic, and TURNING OFF the user output bit in the plx logic */ if (ihcp_byte_order_def == HIGH_BYTE_FIRST) DPRINTI("setting ordering mode to high byte first"); else DPRINTI("ordering mode left at low byte first"); unit_p->byte_order = ihcp_byte_order_def; ihcp_set_byte_order(unit_p); /* * write the default handshake speeds for versatec and centronics boards (as * determined by strapping), * respectively, and the default print/plot mode for versatec boards * the print/plot mode is also saved in the unit structure, since it may be * referred to later - and isn't visible in the board's registers * * NOTE THAT THE SAME DEFAULTS ARE USED FOR ALL BOARDS OF THE SAME TYPE!!!!!! */ if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { /* * versatec - set speed and print/plot mode */ DPRINTI("setting default versatec speed (on board) to 0x%x", unit_p->vers_speed_def); DPRINTI("setting default versatec print/plot mode (on board) to 0x%x", unit_p->mode_def); IHCP_PUTL(IHCP_MODE, ((IHCP_GETL(IHCP_MODE) & ~SPEED_MASK) | unit_p->vers_speed_def)); temp = SET_VERSATEC_MODE | unit_p->mode_def; if (unit_p->byte_order == HIGH_BYTE_FIRST) temp = temp << 24; IHCP_PUTL(IHCP_COMMAND_OUT, temp); unit_p->mode = unit_p->mode_def; } else { /* * centronics - set speed only */ DPRINTI("setting default centronics speed (on board) to 0x%x", unit_p->cent_speed_def); IHCP_PUTL(IHCP_MODE, ((IHCP_GETL(IHCP_MODE) & ~SPEED_MASK) | unit_p->cent_speed_def)); } /* * set saved data streaming mode - for old vme getregs call compatibility, since * we can't read data streaming status from the logic on the other side of the * fifo in the pci card */ unit_p->data_stream_mode = DATA_STREAMING_OFF; } /* * allocate dma resources - iopb list memory, sg list memory, user buffer map list, * and copy buffer if direct user dma not to be used - and set dma mask */ static int ihcp_dma_alloc(struct ihcp_unit_t *unit_p, int instance) { int i; DPRINTI("entering routine"); /* * get contiguous pages of kernel memory to contain to iopbs (dma chaining list) * GFP_KERNEL should get us contiguous memory, don't set dma flag so it won't be * forced into low memory */ unit_p->iopb_base_p = kmalloc(ihcp_iopb_mem_size, GFP_KERNEL); if (!unit_p->iopb_base_p) { EPRINTI("kmalloc failure (iopb memory)!"); return -1; } DPRINTI("iopb memory allocated, virtual address = 0x%p", unit_p->iopb_base_p); /* * check for alignment - we could have asked for more and forced it, * but I don't think the mem caches have any * chunks w/smaller alignments than we need */ if ((unsigned long) (unit_p->iopb_base_p) & (IOPB_SIZE - 1)) { EPRINTI("iopb memory not IOPB_SIZE aligned!"); kfree(unit_p->iopb_base_p); return -1; } /* * inform the kernel what our dma engine can address * (32 bits at this time/with this hardware) */ if (pci_set_dma_mask(unit_p->pci_dev_p, IHCP_DMA_MASK)) { EPRINTI("DMA addressing not compatible!"); kfree(unit_p->iopb_base_p); return -1; } /* * get the bus address of iopb memory using the dynamic DMA mapping stuff, * (doesn't amount to much on a PC, but will help w/compatibility on SPARC, * and other arch's w/iommu hardware * * make sure it isn't larger than our dma addressing capability * if this fails, the kernel pci routines are borked, or we didn't * specify our DMA mask correctly */ unit_p->dma_handle = pci_map_single(unit_p->pci_dev_p, unit_p->iopb_base_p, ihcp_iopb_mem_size, PCI_DMA_TODEVICE); if(unit_p->dma_handle > IHCP_DMA_MASK) { EPRINTI("dma_handle out of range!"); pci_unmap_single(unit_p->pci_dev_p, unit_p->dma_handle, ihcp_iopb_mem_size, PCI_DMA_TODEVICE); kfree(unit_p->iopb_base_p); return -1; } DPRINTI("iopb memory mapped, dma_handle = 0x%lx", (unsigned long)unit_p->dma_handle); /* * get space for the scatterlist ... this probably isn't necessary on a pc, since * we should be able to convert the user buff page mappings direcly into bus adds, * and build the iopb list, but to try for compatibility with other architectures, * we will go through a scatterlist on the way to building an iopb list * * the newer architectures may take advantage of IOMMU mapping, others may * implement bounce buffers via the scatterlist * * as of 2.6.24, it is possible to chain scatterlist pages - we will just * ask for more than a page if that's what the user really wants and not * use the chaining feature. if the user's unreasonable demand can't be * met, just let kmalloc barf * * we now have to zero the allocated memory, since the kernel gods have added new * .page and .offset to the scatterlist struct, and if .page AND .address are either * both zero or both non-zero, they BUG()! (maybe not in 2.6...) */ unit_p->sg_list_p = kmalloc(ihcp_max_sg_length * sizeof(struct scatterlist), GFP_KERNEL); if (unit_p->sg_list_p == NULL) { EPRINTI("can't allocate scatter list (kmalloc error)!"); pci_unmap_single(unit_p->pci_dev_p, unit_p->dma_handle, ihcp_iopb_mem_size, PCI_DMA_TODEVICE); kfree(unit_p->iopb_base_p); return -1; } memset(unit_p->sg_list_p, 0, ihcp_max_sg_length * sizeof(struct scatterlist)); DPRINTI("scatter list allocated, sg_list_p = 0x%p", unit_p->sg_list_p); /* * allocate an array of page pointers to hold ptrs to mapped * user pages * same size as max sg list since user bfr alignment may require * an extra page */ if((unit_p->maplist_p = kmalloc(ihcp_max_sg_length * sizeof(*unit_p->maplist_p), GFP_KERNEL)) == NULL) { EPRINTI("can't allocate maplist!"); kfree(unit_p->sg_list_p); pci_unmap_single(unit_p->pci_dev_p, unit_p->dma_handle, ihcp_iopb_mem_size, PCI_DMA_TODEVICE); kfree(unit_p->iopb_base_p); return -1; } DPRINTI("maplist allocated, address = 0x%p", unit_p->maplist_p); /* * if a copy buffer is required, allocate it here and stuff the maplist w/its * pages * * we only allocate just enough pages to satisfy the max buffer size, since we will always * copy to the buffer starting at offset = 0, so we don't need an extra page to handle * alignment (like the maplist and sg list do) */ if (IHCP_COPY_BUF) { DPRINTI("allocating copy buffer"); unit_p->dma_copy_buf_p = (char *)__get_free_pages(GFP_KERNEL, ihcp_max_phys_order); if(unit_p->dma_copy_buf_p == NULL) { EPRINTI("can't allocate copy buffer!"); kfree(unit_p->maplist_p); kfree(unit_p->sg_list_p); pci_unmap_single(unit_p->pci_dev_p, unit_p->dma_handle, ihcp_iopb_mem_size, PCI_DMA_TODEVICE); kfree(unit_p->iopb_base_p); return -1; } DPRINTI("copy buffer allocated, address = 0x%p", unit_p->dma_copy_buf_p); /* * fill maplist array with pointers to buffer page structures * since we are in copy mode, these pointers will never change */ for (i=0; imaplist_p[i] = virt_to_page(unit_p->dma_copy_buf_p + ( i * PAGE_SIZE)); DPRINTI("maplist loaded w/copy buff pages"); } else { DPRINTI("user DMA mode, no copy buffer allocated"); unit_p->dma_copy_buf_p = NULL; } return 0; } /* * free the above */ static void ihcp_dma_free(struct ihcp_unit_t *unit_p, int instance) { DPRINTI("entering routine"); if(unit_p->dma_copy_buf_p) free_pages((unsigned long)unit_p->dma_copy_buf_p, ihcp_max_phys_order); kfree(unit_p->maplist_p); kfree(unit_p->sg_list_p); pci_unmap_single(unit_p->pci_dev_p, unit_p->dma_handle, ihcp_iopb_mem_size, PCI_DMA_TODEVICE); kfree(unit_p->iopb_base_p); } /* * map the board registers for slave access */ static int ihcp_map_regs(struct ihcp_unit_t *unit_p, int instance) { u_long bus_address; /* temp base address */ u_long bus_base; /* temp address base */ u_long bus_offset; /* temp address offset */ DPRINTI("entering routine"); bus_address = unit_p->pci_dev_p->resource[0].start; DPRINTI("pci_base_address_0 (plx regs) = 0x%lx", bus_address); bus_address &= PCI_BASE_ADDRESS_MEM_MASK; bus_base = bus_address & ~(PAGE_SIZE - 1); bus_offset = bus_address & (PAGE_SIZE - 1); DPRINTI("plx bus add = 0x%lx, base = 0x%lx, offset = 0x%lx", bus_address, bus_base, bus_offset); unit_p->plx_page_base_p = ioremap(bus_base, PLX_REG_SIZE + bus_offset); unit_p->plx_base_p = unit_p->plx_page_base_p + bus_offset; if (unit_p->plx_page_base_p == NULL) { EPRINTI("can't ioremap plx registers!"); return -1; } DPRINTI("plx mapped base = 0x%p, virt addr = 0x%p", unit_p->plx_page_base_p, unit_p->plx_base_p); bus_address = unit_p->pci_dev_p->resource[2].start; DPRINTI("pci_base_address_2 (ihcp regs) = 0x%lx", bus_address); bus_address &= PCI_BASE_ADDRESS_MEM_MASK; bus_base = bus_address & ~(PAGE_SIZE - 1); bus_offset = bus_address & (PAGE_SIZE - 1); DPRINTI("ihcp bus add = 0x%lx, base = 0x%lx, offset = 0x%lx", bus_address, bus_base, bus_offset); unit_p->ihcp_page_base_p = ioremap(bus_base, IHCP_REG_SIZE + bus_offset); unit_p->ihcp_base_p = unit_p->ihcp_page_base_p + bus_offset; if (unit_p->ihcp_page_base_p == NULL) { EPRINTI("can't ioremap ihcp registers!"); iounmap(unit_p->plx_page_base_p); return -1; } DPRINTI("ihcp mapped base = 0x%p, virt addr = 0x%p", unit_p->ihcp_page_base_p, unit_p->ihcp_base_p); return 0; } /* * unmap the board's registers */ static void ihcp_unmap_regs(struct ihcp_unit_t *unit_p, int instance) { DPRINTI("entering routine"); iounmap(unit_p->ihcp_page_base_p); iounmap(unit_p->plx_page_base_p); } /* * check maplist values for max/min permissible */ static int ihcp_maplist_check(struct ihcp_unit_t *unit_p, int instance) { DPRINTI("entering routine"); if((unit_p->maplist_user_buf_p + unit_p->maplist_length) < unit_p->maplist_user_buf_p) { EPRINTI("buffer add + length overflow!"); return -EINVAL; } if(unit_p->maplist_nr_pages == 0) { EPRINTI("maplist_nr_pages = 0!"); return -EINVAL; } if(unit_p->maplist_nr_pages > ihcp_max_sg_length) { EPRINTI("maplist_nr_pages too large!"); return -EINVAL; } if(unit_p->maplist_length == 0) { EPRINTI("maplist_length = 0!"); return -EINVAL; } if(unit_p->maplist_length > ihcp_max_buf_bytes) { EPRINTI("maplist_length too large!"); return -EINVAL; } return 0; } /* * map and pin user buffer or copy user buffer to kernel buffer * maplist_rw_mode must be set before calling this * (might be cleaner to make that a parameter...) * * set the ->maplist_xx items to appropriate values for * either case * * CHECK FOR SANE VALUES in either case */ static int ihcp_map_or_copy_buf(struct ihcp_unit_t *unit_p, int instance, char *user_buf_p, int buf_size, int rw_mode) { DPRINTI("user_buf_p = 0x%p, size = 0x%x, rw_mode = 0x%x", user_buf_p, buf_size, rw_mode); unit_p->maplist_rw_mode = rw_mode; unit_p->maplist_user_buf_p = user_buf_p; /* not used in copy mode? */ if(unit_p->dma_copy_buf_p) { /* * we are in copy mode - if OUTPUT copy user data to buffer * set up maplist - we don't need to map since buffer already in kernel * offset always zero when using copy buffer */ unit_p->maplist_offset = 0; unit_p->maplist_length = buf_size; unit_p->maplist_nr_pages = (buf_size + PAGE_SIZE -1)/PAGE_SIZE; if(rw_mode != READ) { DPRINTI("copying user buffer to dma buffer"); if(ihcp_maplist_check(unit_p, instance)) { EPRINTI("maplist value(s) out of range!"); return -EINVAL; } if(copy_from_user(unit_p->dma_copy_buf_p, user_buf_p, buf_size)) { EPRINTI("copy_from_user error!"); return -EIO; } } } else { /* * we will be mapping and pinning the user buffer for direct DMA */ DPRINTI("mapping and pinning user buffer"); unit_p->maplist_offset = (unsigned long)user_buf_p & ~PAGE_MASK; unit_p->maplist_length = buf_size; unit_p->maplist_nr_pages = (unit_p->maplist_offset + buf_size -1 + ~PAGE_MASK) >> PAGE_SHIFT; if(ihcp_maplist_check(unit_p, instance)) { EPRINTI("maplist value(s) out of range!"); return -EINVAL; } if(ihcp_map_user_buf(unit_p, instance)) { EPRINTI("ihcp_map_user_buf failure!"); return -EIO; } } DPRINTI("buf_p = 0x%p, nr_pages = 0x%x, offset = 0x%x, length = 0x%x", unit_p->maplist_user_buf_p, unit_p->maplist_nr_pages, unit_p->maplist_offset, unit_p->maplist_length); return 0; } /* * unmap user buffer or copy kernel buffer to user buffer */ static int ihcp_unmap_or_copy_buf(struct ihcp_unit_t *unit_p, int instance) { DPRINTI("entering routine"); if(unit_p->dma_copy_buf_p) { /* * copy buffer mode, copy if input operation */ if(unit_p->maplist_rw_mode == READ) { DPRINTI("copying dma data to user buffer"); if(ihcp_maplist_check(unit_p, instance)) { EPRINTI("maplist value(s) out of range!"); return -EINVAL; } if(copy_to_user(unit_p->maplist_user_buf_p, unit_p->dma_copy_buf_p, unit_p->maplist_length)) { EPRINTI("copy_to_user error!"); return -EIO; } } } else { /* * direct user buffer dma mode, unmap user buf * maplist values already checked */ DPRINTI("unmapping user buffer"); ihcp_unmap_user_buf(unit_p, instance); } return 0; } /* * unmap if error requires undoing mapping - if copy mode, do nothing */ static void ihcp_unmap_on_error(struct ihcp_unit_t *unit_p, int instance) { DPRINTI("entering routine"); if(!unit_p->dma_copy_buf_p) { DPRINTI("calling ihcp_unmap_user_buf"); ihcp_unmap_user_buf(unit_p, instance); } else DPRINTI("copy buf mode, doing nothing"); } /* * map and pin user buffer for direct DMA * * code adapted from st.c */ static int ihcp_map_user_buf(struct ihcp_unit_t *unit_p, int instance) { int nr_pages_mapped; int i; DPRINT("entering routine"); /* * Try to fault in all of the necessary pages * * rw==READ means read from drive, write into memory area * 0 arg = don't force */ down_read(¤t->mm->mmap_sem); nr_pages_mapped = get_user_pages(current, current->mm, (unsigned long)unit_p->maplist_user_buf_p, unit_p->maplist_nr_pages, unit_p->maplist_rw_mode == READ, 0, unit_p->maplist_p, NULL); up_read(¤t->mm->mmap_sem); /* * result == nr pages requested if all pages mapped * or - if error */ if(nr_pages_mapped < unit_p->maplist_nr_pages) { EPRINTI("couldn't map all (any?) pages!"); if(nr_pages_mapped > 0) { for(i=0; i < nr_pages_mapped; i++) put_page(unit_p->maplist_p[i]); } return -EIO; } /* * st.c includes this call here, but I believe * it is included in get_user_pages w/2.6 * * for (i=0; i < nr_pages; i++) * flush_dcache_page(unit_p->maplist_p[i]); */ return 0; } /* * unmap user buffer * * (comment from st.c) * FIXME: cache flush missing for rw==READ * FIXME: call the correct reference counting function */ static void ihcp_unmap_user_buf(struct ihcp_unit_t *unit_p, int instance) { int i; DPRINT("entering routine"); for (i=0; i < unit_p->maplist_nr_pages; i++) { if (unit_p->maplist_rw_mode == READ) SetPageDirty(unit_p->maplist_p[i]); put_page(unit_p->maplist_p[i]); } } /* * convert maplist int sg list */ static int ihcp_maplist_to_sg_list(struct ihcp_unit_t *unit_p, int instance) { struct page **maplist; int map_nr_pages; int map_length; int map_offset; int temp_length; int i; DPRINT("entering routine"); /* * do a sanity check on the number of pages - it better not exceed the sg list max size */ if (unit_p->maplist_nr_pages > ihcp_max_sg_length) { EPRINTI("too many maplist pages!"); return -EIO; } /* * grind through the map list, extracting addresses and lengths for the sg list * don't forget the initial offset, and total length */ maplist = unit_p->maplist_p; map_length = unit_p->maplist_length; map_offset = unit_p->maplist_offset; map_nr_pages = unit_p->maplist_nr_pages; for (i = 0; i < map_nr_pages; i++) { temp_length = (PAGE_SIZE - map_offset) < map_length ? PAGE_SIZE - map_offset : map_length; /* * newer kernels with chained-scatterlist support don't * allow accessing the page element directly * * use macro to set page, offset, and length (in ihcp_var.h) * * this is what it used to look like: * * unit_p->sg_list_p[i].page = maplist[i]; * unit_p->sg_list_p[i].offset = map_offset; * unit_p->sg_list_p[i].length = temp_length; */ IHCP_SET_PAGE((unit_p->sg_list_p + i), maplist[i], temp_length, map_offset); DPRINTI("page_ptr = 0x%p, page_offset = 0x%x, page_len = 0x%x", maplist[i], map_offset, temp_length); map_length -= temp_length; map_offset = 0; } return 0; } /* * convert sg list to iopb list for plx dma logic */ static int ihcp_sg_list_to_iopb_list(struct ihcp_unit_t *unit_p, int instance) { int num_iopbs; int iopb_number; uint32_t temp_bus_addr; int temp_length; uint32_t next_iopb_add; uint32_t plx_direction; DPRINT("entering routine"); /* * make sure num_iopbs <= kio_pages (we should probably just trust the mapping code) */ num_iopbs = unit_p->nr_iopbs; if (num_iopbs > unit_p->maplist_nr_pages) { EPRINTI("num_iopbs > maplist_nr_pages!"); return -EIO; } plx_direction = unit_p->maplist_rw_mode == READ ? DMA_INPUT : 0; /* * now assemble the scatter list into a form that the PLX chip wants to see, and build iopb list * use IOPB macros to ease possible later mods to swizzle byte lanes */ for (iopb_number = 0; iopb_number < num_iopbs; iopb_number++) { temp_bus_addr = (uint32_t) sg_dma_address(&(unit_p->sg_list_p[iopb_number])); temp_length = sg_dma_len(&(unit_p->sg_list_p[iopb_number])); IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_PCI_ADDRESS, temp_bus_addr); /* buf segment bus address */ IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_TRANSFER_SIZE, temp_length); /* buf segment size */ IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_LOCAL_ADDRESS, IHCP_32_BIT_DATA_OUT); /* data destination */ DPRINTI("iopb number 0x%x, buf seg addr = 0x%x, buf seg length = 0x%x", iopb_number, temp_bus_addr, temp_length); /* * the next descriptor pointer element of the iopb includes the location bit * (pci or local memory), the end of chain bit, the interrupt after terminal * countbit (not used), the direction of transfer bit (0 = output to device), * and the bus add of the next iopb * * if the iopb_number = num_iopbs -1, this is the last one so flag it, and * don't set a next iopb address */ if (iopb_number < (num_iopbs - 1)) { next_iopb_add = DMA_NEXT_ADDRESS_MASK & ((uint32_t)unit_p->dma_handle + ((iopb_number + 1) * IOPB_SIZE)); DPRINTI("not last iopb, next iopb add = 0x%x", next_iopb_add); IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_NEXT_IOPB, DMA_CHAIN_IN_PCI_MEM | next_iopb_add | plx_direction); } else { DPRINTI("last iopb, setting end of chain bit"); IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_NEXT_IOPB, DMA_CHAIN_IN_PCI_MEM | DMA_END_OF_CHAIN | plx_direction); } } return 0; } /* * set hardware byte order to board's current ordering mode */ static void ihcp_set_byte_order(struct ihcp_unit_t *unit_p) { if (unit_p->byte_order == HIGH_BYTE_FIRST) { IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) | BYTE_SWIZZLE)); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) & ~USER_OUTPUT)); } else { IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) & ~BYTE_SWIZZLE)); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) | USER_OUTPUT)); } } /* * new-style init and cleanup routine invocations */ module_init(ihcp_init_module); module_exit(ihcp_cleanup_module);