xref: /illumos-gate/usr/src/uts/common/io/ecpp.c (revision f70049b7)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
519397407SSherry Moore  * Common Development and Distribution License (the "License").
619397407SSherry Moore  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22d3d50737SRafael Vanoni  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  *
297c478bd9Sstevel@tonic-gate  * IEEE 1284 Parallel Port Device Driver
307c478bd9Sstevel@tonic-gate  *
317c478bd9Sstevel@tonic-gate  */
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #include <sys/param.h>
347c478bd9Sstevel@tonic-gate #include <sys/errno.h>
357c478bd9Sstevel@tonic-gate #include <sys/file.h>
367c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
377c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
387c478bd9Sstevel@tonic-gate #include <sys/debug.h>
397c478bd9Sstevel@tonic-gate #include <sys/stream.h>
407c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
417c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
427c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
437c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
447c478bd9Sstevel@tonic-gate #include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
457c478bd9Sstevel@tonic-gate #include <sys/modctl.h>		/* for modldrv */
467c478bd9Sstevel@tonic-gate #include <sys/stat.h>		/* ddi_create_minor_node S_IFCHR */
477c478bd9Sstevel@tonic-gate #include <sys/open.h>
487c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
497c478bd9Sstevel@tonic-gate #include <sys/kstat.h>
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate #include <sys/prnio.h>
527c478bd9Sstevel@tonic-gate #include <sys/ecppreg.h>	/* hw description */
537c478bd9Sstevel@tonic-gate #include <sys/ecppio.h>		/* ioctl description */
547c478bd9Sstevel@tonic-gate #include <sys/ecppvar.h>	/* driver description */
557c478bd9Sstevel@tonic-gate #include <sys/dma_engine.h>
567c478bd9Sstevel@tonic-gate #include <sys/dma_i8237A.h>
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate /*
597c478bd9Sstevel@tonic-gate  * Background
607c478bd9Sstevel@tonic-gate  * ==========
617c478bd9Sstevel@tonic-gate  * IEEE 1284-1994 standard defines "a signalling method for asynchronous,
627c478bd9Sstevel@tonic-gate  * fully interlocked, bidirectional parallel communications between hosts
637c478bd9Sstevel@tonic-gate  * and printers or other peripherals." (1.1) The standard defines 5 modes
647c478bd9Sstevel@tonic-gate  * of operation - Compatibility, Nibble, Byte, ECP and EPP - which differ
657c478bd9Sstevel@tonic-gate  * in direction, bandwidth, pins assignment, DMA capability, etc.
667c478bd9Sstevel@tonic-gate  *
677c478bd9Sstevel@tonic-gate  * Negotiation is a mechanism for moving between modes. Compatibility mode
687c478bd9Sstevel@tonic-gate  * is a default mode, from which negotiations to other modes occur and
697c478bd9Sstevel@tonic-gate  * to which both host and peripheral break in case of interface errors.
707c478bd9Sstevel@tonic-gate  * Compatibility mode provides a unidirectional (forward) channel for
717c478bd9Sstevel@tonic-gate  * communicating with old pre-1284 peripherals.
727c478bd9Sstevel@tonic-gate  *
737c478bd9Sstevel@tonic-gate  * Each mode has a number of phases. [Mode, phase] pair represents the
747c478bd9Sstevel@tonic-gate  * interface state. Host initiates all transfers, though peripheral can
757c478bd9Sstevel@tonic-gate  * request backchannel transfer by asserting nErr pin.
767c478bd9Sstevel@tonic-gate  *
777c478bd9Sstevel@tonic-gate  * Ecpp driver implements an IEEE 1284-compliant host using a combination
787c478bd9Sstevel@tonic-gate  * of hardware and software. Hardware part is represented by a controller,
797c478bd9Sstevel@tonic-gate  * which is a part of the SuperIO chip. Ecpp supports the following SuperIOs:
807c478bd9Sstevel@tonic-gate  * PC82332/PC82336 (U5/U10/U60), PC97317 (U100), M1553 (Grover).
817c478bd9Sstevel@tonic-gate  * Struct ecpp_hw describes each SuperIO and is determined in ecpp_attach().
827c478bd9Sstevel@tonic-gate  *
837c478bd9Sstevel@tonic-gate  * Negotiation is performed in software. Transfer may be performed either
847c478bd9Sstevel@tonic-gate  * in software by driving output pins for each byte (PIO method), or with
857c478bd9Sstevel@tonic-gate  * hardware assistance - SuperIO has a 16-byte FIFO, which is filled by
867c478bd9Sstevel@tonic-gate  * the driver (normally using DMA), while the chip performs the actual xfer.
877c478bd9Sstevel@tonic-gate  * PIO is used for Nibble and Compat, DMA is used for ECP and Compat modes.
887c478bd9Sstevel@tonic-gate  *
897c478bd9Sstevel@tonic-gate  * Driver currently supports the following modes:
907c478bd9Sstevel@tonic-gate  *
917c478bd9Sstevel@tonic-gate  * - Compatibility mode: byte-wide forward channel ~50KB/sec;
927c478bd9Sstevel@tonic-gate  *   pp->io_mode defines PIO or DMA method of transfer;
937c478bd9Sstevel@tonic-gate  * - Nibble mode: nibble-wide (4-bit) reverse channel ~30KB/sec;
947c478bd9Sstevel@tonic-gate  * - ECP mode: byte-wide bidirectional channel (~1MB/sec);
957c478bd9Sstevel@tonic-gate  *
967c478bd9Sstevel@tonic-gate  * Theory of operation
977c478bd9Sstevel@tonic-gate  * ===================
987c478bd9Sstevel@tonic-gate  * The manner in which ecpp drives 1284 interface is that of a state machine.
997c478bd9Sstevel@tonic-gate  * State is a combination of 1284 mode {ECPP_*_MODE}, 1284 phase {ECPP_PHASE_*}
1007c478bd9Sstevel@tonic-gate  * and transfer method {PIO, DMA}. State is a function of application actions
1017c478bd9Sstevel@tonic-gate  * {write(2), ioctl(2)} and peripheral reaction.
1027c478bd9Sstevel@tonic-gate  *
1037c478bd9Sstevel@tonic-gate  * 1284 interface state is described by the following variables:
1047c478bd9Sstevel@tonic-gate  *   pp->current_mode  -- 1284 mode used for forward transfers;
1057c478bd9Sstevel@tonic-gate  *   pp->backchannel   -- 1284 mode used for backward transfers;
1067c478bd9Sstevel@tonic-gate  *   pp->curent_phase  -- 1284 phase;
1077c478bd9Sstevel@tonic-gate  *
1087c478bd9Sstevel@tonic-gate  * Bidirectional operation in Compatibility mode is provided by a combination:
1097c478bd9Sstevel@tonic-gate  * pp->current_mode == ECPP_COMPAT_MODE && pp->backchannel == ECPP_NIBBLE_MODE
1107c478bd9Sstevel@tonic-gate  * ECPP_CENTRONICS means no backchannel
1117c478bd9Sstevel@tonic-gate  *
1127c478bd9Sstevel@tonic-gate  * Driver internal state is defined by pp->e_busy as follows:
1137c478bd9Sstevel@tonic-gate  *   ECPP_IDLE	-- idle, no active transfers;
1147c478bd9Sstevel@tonic-gate  *   ECPP_BUSY	-- transfer is in progress;
1157c478bd9Sstevel@tonic-gate  *   ECPP_ERR	-- have data to transfer, but peripheral can`t receive data;
1167c478bd9Sstevel@tonic-gate  *   ECPP_FLUSH	-- flushing the queues;
1177c478bd9Sstevel@tonic-gate  *
1187c478bd9Sstevel@tonic-gate  * When opened, driver is in ECPP_IDLE state, current mode is ECPP_CENTRONICS
1197c478bd9Sstevel@tonic-gate  * Default negotiation tries to negotiate to the best mode supported by printer,
1207c478bd9Sstevel@tonic-gate  * sets pp->current_mode and pp->backchannel accordingly.
1217c478bd9Sstevel@tonic-gate  *
1227c478bd9Sstevel@tonic-gate  * When output data arrives in M_DATA mblks ecpp_wput() puts them on the queue
1237c478bd9Sstevel@tonic-gate  * to let ecpp_wsrv() concatenate small blocks into one big transfer
1247c478bd9Sstevel@tonic-gate  * by copying them into pp->ioblock. If first the mblk data is bigger than
1257c478bd9Sstevel@tonic-gate  * pp->ioblock, then it is used instead of i/o block (pointed by pp->msg)
1267c478bd9Sstevel@tonic-gate  *
1277c478bd9Sstevel@tonic-gate  * Before starting the transfer the driver will check if peripheral is ready
1287c478bd9Sstevel@tonic-gate  * by calling ecpp_check_status() and if it is not, driver goes ECPP_ERR state
1297c478bd9Sstevel@tonic-gate  * and schedules ecpp_wsrv_timer() which would qenable() the wq, effectively
1307c478bd9Sstevel@tonic-gate  * rechecking the peripheral readiness and restarting itself until it is ready.
1317c478bd9Sstevel@tonic-gate  * The transfer is then started by calling ecpp_start(), driver goes ECPP_BUSY
1327c478bd9Sstevel@tonic-gate  *
1337c478bd9Sstevel@tonic-gate  * While transfer is in progress all arriving messages will be queued up.
1347c478bd9Sstevel@tonic-gate  * Transfer can end up in either of two ways:
1357c478bd9Sstevel@tonic-gate  * - interrupt occurs, ecpp_isr() checks if all the data was transferred, if so
1367c478bd9Sstevel@tonic-gate  *   cleanup and go ECPP_IDLE, otherwise putback untransferred and qenable();
1377c478bd9Sstevel@tonic-gate  * - ecpp_xfer_timeout() cancels the transfer and puts back untransferred data;
1387c478bd9Sstevel@tonic-gate  *
1397c478bd9Sstevel@tonic-gate  * PIO transfer method is very CPU intensive: for each sent byte the peripheral
1407c478bd9Sstevel@tonic-gate  * state is checked, then the byte is transfered and driver waits for an nAck
1417c478bd9Sstevel@tonic-gate  * interrupt; ecpp_isr() will then look if there is more data and if so
1427c478bd9Sstevel@tonic-gate  * triggers the soft interrupt, which transfers the next byte. PIO method
1437c478bd9Sstevel@tonic-gate  * is needed only for legacy printers which are sensitive to strobe problem
1447c478bd9Sstevel@tonic-gate  * (Bugid 4192788).
1457c478bd9Sstevel@tonic-gate  *
1467c478bd9Sstevel@tonic-gate  * ecpp_wsrv() is responsible for both starting transfers (ecpp_start()) and
1477c478bd9Sstevel@tonic-gate  * going idle (ecpp_idle_phase()). Many routines qenable() the write queue,
1487c478bd9Sstevel@tonic-gate  * meaning "check if there are pending requests, process them and go idle".
1497c478bd9Sstevel@tonic-gate  *
1507c478bd9Sstevel@tonic-gate  * In it`s idle state the driver will always try to listen to the backchannel
1517c478bd9Sstevel@tonic-gate  * (as advised by 1284).
1527c478bd9Sstevel@tonic-gate  *
1537c478bd9Sstevel@tonic-gate  * The mechanism for handling backchannel requests is as follows:
1547c478bd9Sstevel@tonic-gate  * - when the peripheral has data to send it asserts nErr pin
1557c478bd9Sstevel@tonic-gate  *   (and also nAck in Nibble Mode) which results in an interrupt on the host;
1567c478bd9Sstevel@tonic-gate  * - ISR creates M_CTL message containing an ECPP_BACKCHANNEL byte and
1577c478bd9Sstevel@tonic-gate  *   puts it back on the write queue;
1587c478bd9Sstevel@tonic-gate  * - ecpp_wsrv() gets M_CTL and calls ecpp_peripheral2host(), which kicks off
1597c478bd9Sstevel@tonic-gate  *   the transfer;
1607c478bd9Sstevel@tonic-gate  *
1617c478bd9Sstevel@tonic-gate  * This way Nibble and ECP mode backchannel are implemented.
1627c478bd9Sstevel@tonic-gate  * If the read queue gets full, backchannel request is rejected.
1637c478bd9Sstevel@tonic-gate  * As the application reads data and queue size falls below the low watermark,
1647c478bd9Sstevel@tonic-gate  * ecpp_rsrv() gets called and enables the backchannel again.
1657c478bd9Sstevel@tonic-gate  *
1667c478bd9Sstevel@tonic-gate  * Future enhancements
1677c478bd9Sstevel@tonic-gate  * ===================
1687c478bd9Sstevel@tonic-gate  *
1697c478bd9Sstevel@tonic-gate  * Support new modes: Byte and EPP.
1707c478bd9Sstevel@tonic-gate  */
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate #ifndef ECPP_DEBUG
1737c478bd9Sstevel@tonic-gate #define	ECPP_DEBUG 0
1747c478bd9Sstevel@tonic-gate #endif	/* ECPP_DEBUG */
1757c478bd9Sstevel@tonic-gate int ecpp_debug = ECPP_DEBUG;
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate int noecp = 0;	/* flag not to use ECP mode */
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate /* driver entry point fn definitions */
1802520aea3SToomas Soome static int	ecpp_open(queue_t *, dev_t *, int, int, cred_t *);
1817c478bd9Sstevel@tonic-gate static int	ecpp_close(queue_t *, int, cred_t *);
1822520aea3SToomas Soome static uint_t	ecpp_isr(caddr_t);
1837c478bd9Sstevel@tonic-gate static uint_t	ecpp_softintr(caddr_t);
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate /* configuration entry point fn definitions */
1862520aea3SToomas Soome static int	ecpp_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1877c478bd9Sstevel@tonic-gate static int	ecpp_attach(dev_info_t *, ddi_attach_cmd_t);
1887c478bd9Sstevel@tonic-gate static int	ecpp_detach(dev_info_t *, ddi_detach_cmd_t);
1897c478bd9Sstevel@tonic-gate static struct ecpp_hw_bind *ecpp_determine_sio_type(struct ecppunit *);
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate /* isr support routines */
1922520aea3SToomas Soome static uint_t	ecpp_nErr_ihdlr(struct ecppunit *);
1937c478bd9Sstevel@tonic-gate static uint_t	ecpp_pio_ihdlr(struct ecppunit *);
1947c478bd9Sstevel@tonic-gate static uint_t	ecpp_dma_ihdlr(struct ecppunit *);
1957c478bd9Sstevel@tonic-gate static uint_t	ecpp_M1553_intr(struct ecppunit *);
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate /* configuration support routines */
1987c478bd9Sstevel@tonic-gate static void	ecpp_get_props(struct ecppunit *);
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate /* Streams Routines */
2017c478bd9Sstevel@tonic-gate static int	ecpp_wput(queue_t *, mblk_t *);
2027c478bd9Sstevel@tonic-gate static int	ecpp_wsrv(queue_t *);
2037c478bd9Sstevel@tonic-gate static int	ecpp_rsrv(queue_t *);
2047c478bd9Sstevel@tonic-gate static void	ecpp_flush(struct ecppunit *, int);
2057c478bd9Sstevel@tonic-gate static void	ecpp_start(struct ecppunit *, caddr_t, size_t);
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate /* ioctl handling */
2087c478bd9Sstevel@tonic-gate static void	ecpp_putioc(queue_t *, mblk_t *);
2097c478bd9Sstevel@tonic-gate static void	ecpp_srvioc(queue_t *, mblk_t *);
2107c478bd9Sstevel@tonic-gate static void	ecpp_wput_iocdata_devid(queue_t *, mblk_t *, uintptr_t);
2117c478bd9Sstevel@tonic-gate static void	ecpp_putioc_copyout(queue_t *, mblk_t *, void *, int);
2127c478bd9Sstevel@tonic-gate static void	ecpp_putioc_stateful_copyin(queue_t *, mblk_t *, size_t);
2137c478bd9Sstevel@tonic-gate static void	ecpp_srvioc_devid(queue_t *, mblk_t *,
2147c478bd9Sstevel@tonic-gate 				struct ecpp_device_id *, int *);
2157c478bd9Sstevel@tonic-gate static void	ecpp_srvioc_prnif(queue_t *, mblk_t *);
2162520aea3SToomas Soome static void	ecpp_ack_ioctl(queue_t *, mblk_t *);
2172520aea3SToomas Soome static void	ecpp_nack_ioctl(queue_t *, mblk_t *, int);
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate /* kstat routines */
2207c478bd9Sstevel@tonic-gate static void	ecpp_kstat_init(struct ecppunit *);
2217c478bd9Sstevel@tonic-gate static int	ecpp_kstat_update(kstat_t *, int);
2227c478bd9Sstevel@tonic-gate static int	ecpp_kstatintr_update(kstat_t *, int);
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate /* dma routines */
2257c478bd9Sstevel@tonic-gate static void	ecpp_putback_untransfered(struct ecppunit *, void *, uint_t);
2267c478bd9Sstevel@tonic-gate static uint8_t	ecpp_setup_dma_resources(struct ecppunit *, caddr_t, size_t);
2277c478bd9Sstevel@tonic-gate static uint8_t	ecpp_init_dma_xfer(struct ecppunit *, caddr_t, size_t);
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate /* pio routines */
2307c478bd9Sstevel@tonic-gate static void	ecpp_pio_writeb(struct ecppunit *);
2317c478bd9Sstevel@tonic-gate static void	ecpp_xfer_cleanup(struct ecppunit *);
2327c478bd9Sstevel@tonic-gate static uint8_t	ecpp_prep_pio_xfer(struct ecppunit *, caddr_t, size_t);
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate /* misc */
2357c478bd9Sstevel@tonic-gate static uchar_t	ecpp_reset_port_regs(struct ecppunit *);
2367c478bd9Sstevel@tonic-gate static void	ecpp_xfer_timeout(void *);
2377c478bd9Sstevel@tonic-gate static void	ecpp_fifo_timer(void *);
2387c478bd9Sstevel@tonic-gate static void	ecpp_wsrv_timer(void *);
2397c478bd9Sstevel@tonic-gate static uchar_t	dcr_write(struct ecppunit *, uint8_t);
2407c478bd9Sstevel@tonic-gate static uchar_t	ecr_write(struct ecppunit *, uint8_t);
2417c478bd9Sstevel@tonic-gate static uchar_t	ecpp_check_status(struct ecppunit *);
2427c478bd9Sstevel@tonic-gate static int	ecpp_backchan_req(struct ecppunit *);
2437c478bd9Sstevel@tonic-gate static void	ecpp_untimeout_unblock(struct ecppunit *, timeout_id_t *);
2447c478bd9Sstevel@tonic-gate static uint_t	ecpp_get_prn_ifcap(struct ecppunit *);
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate /* stubs */
2477c478bd9Sstevel@tonic-gate static void	empty_config_mode(struct ecppunit *);
2487c478bd9Sstevel@tonic-gate static void	empty_mask_intr(struct ecppunit *);
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate /* PC87332 support */
2517c478bd9Sstevel@tonic-gate static int	pc87332_map_regs(struct ecppunit *);
2527c478bd9Sstevel@tonic-gate static void	pc87332_unmap_regs(struct ecppunit *);
2537c478bd9Sstevel@tonic-gate static int	pc87332_config_chip(struct ecppunit *);
2547c478bd9Sstevel@tonic-gate static void	pc87332_config_mode(struct ecppunit *);
2557c478bd9Sstevel@tonic-gate static uint8_t	pc87332_read_config_reg(struct ecppunit *, uint8_t);
2567c478bd9Sstevel@tonic-gate static void	pc87332_write_config_reg(struct ecppunit *, uint8_t, uint8_t);
2577c478bd9Sstevel@tonic-gate static void	cheerio_mask_intr(struct ecppunit *);
2587c478bd9Sstevel@tonic-gate static void	cheerio_unmask_intr(struct ecppunit *);
2597c478bd9Sstevel@tonic-gate static int	cheerio_dma_start(struct ecppunit *);
2607c478bd9Sstevel@tonic-gate static int	cheerio_dma_stop(struct ecppunit *, size_t *);
2617c478bd9Sstevel@tonic-gate static size_t	cheerio_getcnt(struct ecppunit *);
2627c478bd9Sstevel@tonic-gate static void	cheerio_reset_dcsr(struct ecppunit *);
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate /* PC97317 support */
2657c478bd9Sstevel@tonic-gate static int	pc97317_map_regs(struct ecppunit *);
2667c478bd9Sstevel@tonic-gate static void	pc97317_unmap_regs(struct ecppunit *);
2677c478bd9Sstevel@tonic-gate static int	pc97317_config_chip(struct ecppunit *);
2687c478bd9Sstevel@tonic-gate static void	pc97317_config_mode(struct ecppunit *);
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate /* M1553 Southbridge support */
2717c478bd9Sstevel@tonic-gate static int	m1553_map_regs(struct ecppunit *pp);
2727c478bd9Sstevel@tonic-gate static void	m1553_unmap_regs(struct ecppunit *pp);
2737c478bd9Sstevel@tonic-gate static int	m1553_config_chip(struct ecppunit *);
2747c478bd9Sstevel@tonic-gate static uint8_t	m1553_read_config_reg(struct ecppunit *, uint8_t);
2757c478bd9Sstevel@tonic-gate static void	m1553_write_config_reg(struct ecppunit *, uint8_t, uint8_t);
2767c478bd9Sstevel@tonic-gate 
2777c478bd9Sstevel@tonic-gate /* M1553 Southbridge DMAC 8237 support routines */
2782520aea3SToomas Soome static int	dma8237_dma_start(struct ecppunit *);
2797c478bd9Sstevel@tonic-gate static int	dma8237_dma_stop(struct ecppunit *, size_t *);
2807c478bd9Sstevel@tonic-gate static size_t	dma8237_getcnt(struct ecppunit *);
2812520aea3SToomas Soome static void	dma8237_write_addr(struct ecppunit *, uint32_t);
2827c478bd9Sstevel@tonic-gate static void	dma8237_write_count(struct ecppunit *, uint32_t);
2837c478bd9Sstevel@tonic-gate static uint32_t	dma8237_read_count(struct ecppunit *);
2847c478bd9Sstevel@tonic-gate static void	dma8237_write(struct ecppunit *, int, uint8_t);
2857c478bd9Sstevel@tonic-gate static uint8_t	dma8237_read(struct ecppunit *, int);
2867c478bd9Sstevel@tonic-gate #ifdef INCLUDE_DMA8237_READ_ADDR
2877c478bd9Sstevel@tonic-gate static uint32_t	dma8237_read_addr(struct ecppunit *);
2887c478bd9Sstevel@tonic-gate #endif
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate /* i86 PC support rountines */
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate #if defined(__x86)
2937c478bd9Sstevel@tonic-gate static int	x86_dma_start(struct ecppunit *);
2947c478bd9Sstevel@tonic-gate static int	x86_dma_stop(struct ecppunit *, size_t *);
2957c478bd9Sstevel@tonic-gate static int	x86_map_regs(struct ecppunit *);
2967c478bd9Sstevel@tonic-gate static void	x86_unmap_regs(struct ecppunit *);
2977c478bd9Sstevel@tonic-gate static int	x86_config_chip(struct ecppunit *);
2987c478bd9Sstevel@tonic-gate static size_t	x86_getcnt(struct ecppunit *);
2997c478bd9Sstevel@tonic-gate #endif
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate /* IEEE 1284 phase transitions */
3027c478bd9Sstevel@tonic-gate static void	ecpp_1284_init_interface(struct ecppunit *);
3037c478bd9Sstevel@tonic-gate static int	ecpp_1284_termination(struct ecppunit *);
3042520aea3SToomas Soome static uchar_t	ecpp_idle_phase(struct ecppunit *);
3057c478bd9Sstevel@tonic-gate static int	ecp_forward2reverse(struct ecppunit *);
3067c478bd9Sstevel@tonic-gate static int	ecp_reverse2forward(struct ecppunit *);
3077c478bd9Sstevel@tonic-gate static int	read_nibble_backchan(struct ecppunit *);
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate /* reverse transfers */
3107c478bd9Sstevel@tonic-gate static uint_t	ecpp_peripheral2host(struct ecppunit *);
3117c478bd9Sstevel@tonic-gate static uchar_t	ecp_peripheral2host(struct ecppunit *);
3127c478bd9Sstevel@tonic-gate static uchar_t	nibble_peripheral2host(struct ecppunit *pp, uint8_t *);
3137c478bd9Sstevel@tonic-gate static int	ecpp_getdevid(struct ecppunit *, uint8_t *, int *, int);
3147c478bd9Sstevel@tonic-gate static void	ecpp_ecp_read_timeout(void *);
3157c478bd9Sstevel@tonic-gate static void	ecpp_ecp_read_completion(struct ecppunit *);
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate /* IEEE 1284 mode transitions */
3182520aea3SToomas Soome static void	ecpp_default_negotiation(struct ecppunit *);
3192520aea3SToomas Soome static int	ecpp_mode_negotiation(struct ecppunit *, uchar_t);
3207c478bd9Sstevel@tonic-gate static int	ecpp_1284_negotiation(struct ecppunit *, uint8_t, uint8_t *);
3217c478bd9Sstevel@tonic-gate static int	ecp_negotiation(struct ecppunit *);
3227c478bd9Sstevel@tonic-gate static int	nibble_negotiation(struct ecppunit *);
3237c478bd9Sstevel@tonic-gate static int	devidnib_negotiation(struct ecppunit *);
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate /* IEEE 1284 utility routines */
3267c478bd9Sstevel@tonic-gate static int	wait_dsr(struct ecppunit *, uint8_t, uint8_t, int);
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate /* debugging functions */
3297c478bd9Sstevel@tonic-gate static void	ecpp_error(dev_info_t *, char *, ...);
3307c478bd9Sstevel@tonic-gate static uchar_t	ecpp_get_error_status(uchar_t);
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate /*
3337c478bd9Sstevel@tonic-gate  * Chip-dependent structures
3347c478bd9Sstevel@tonic-gate  */
3357c478bd9Sstevel@tonic-gate static ddi_dma_attr_t cheerio_dma_attr = {
3367c478bd9Sstevel@tonic-gate 	DMA_ATTR_VERSION,	/* version */
3377c478bd9Sstevel@tonic-gate 	0x00000000ull,		/* dlim_addr_lo */
3387c478bd9Sstevel@tonic-gate 	0xfffffffeull,		/* dlim_addr_hi */
3397c478bd9Sstevel@tonic-gate 	0xffffff,		/* DMA counter register */
3407c478bd9Sstevel@tonic-gate 	1,			/* DMA address alignment */
3417c478bd9Sstevel@tonic-gate 	0x74,			/* burst sizes */
3427c478bd9Sstevel@tonic-gate 	0x0001,			/* min effective DMA size */
3437c478bd9Sstevel@tonic-gate 	0xffff,			/* maximum transfer size */
3447c478bd9Sstevel@tonic-gate 	0xffff,			/* segment boundary */
3457c478bd9Sstevel@tonic-gate 	1,			/* s/g list length */
3467c478bd9Sstevel@tonic-gate 	1,			/* granularity of device */
3477c478bd9Sstevel@tonic-gate 	0			/* DMA flags */
3487c478bd9Sstevel@tonic-gate };
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate static struct ecpp_hw pc87332 = {
3517c478bd9Sstevel@tonic-gate 	pc87332_map_regs,
3527c478bd9Sstevel@tonic-gate 	pc87332_unmap_regs,
3537c478bd9Sstevel@tonic-gate 	pc87332_config_chip,
3547c478bd9Sstevel@tonic-gate 	pc87332_config_mode,
3557c478bd9Sstevel@tonic-gate 	cheerio_mask_intr,
3567c478bd9Sstevel@tonic-gate 	cheerio_unmask_intr,
3577c478bd9Sstevel@tonic-gate 	cheerio_dma_start,
3587c478bd9Sstevel@tonic-gate 	cheerio_dma_stop,
3597c478bd9Sstevel@tonic-gate 	cheerio_getcnt,
3607c478bd9Sstevel@tonic-gate 	&cheerio_dma_attr
3617c478bd9Sstevel@tonic-gate };
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate static struct ecpp_hw pc97317 = {
3647c478bd9Sstevel@tonic-gate 	pc97317_map_regs,
3657c478bd9Sstevel@tonic-gate 	pc97317_unmap_regs,
3667c478bd9Sstevel@tonic-gate 	pc97317_config_chip,
3677c478bd9Sstevel@tonic-gate 	pc97317_config_mode,
3687c478bd9Sstevel@tonic-gate 	cheerio_mask_intr,
3697c478bd9Sstevel@tonic-gate 	cheerio_unmask_intr,
3707c478bd9Sstevel@tonic-gate 	cheerio_dma_start,
3717c478bd9Sstevel@tonic-gate 	cheerio_dma_stop,
3727c478bd9Sstevel@tonic-gate 	cheerio_getcnt,
3737c478bd9Sstevel@tonic-gate 	&cheerio_dma_attr
3747c478bd9Sstevel@tonic-gate };
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate static ddi_dma_attr_t i8237_dma_attr = {
3777c478bd9Sstevel@tonic-gate 	DMA_ATTR_VERSION,	/* version */
3787c478bd9Sstevel@tonic-gate 	0x00000000ull,		/* dlim_addr_lo */
3797c478bd9Sstevel@tonic-gate 	0xfffffffeull,		/* dlim_addr_hi */
3807c478bd9Sstevel@tonic-gate 	0xffff,			/* DMA counter register */
3817c478bd9Sstevel@tonic-gate 	1,			/* DMA address alignment */
3827c478bd9Sstevel@tonic-gate 	0x01,			/* burst sizes */
3837c478bd9Sstevel@tonic-gate 	0x0001,			/* min effective DMA size */
3847c478bd9Sstevel@tonic-gate 	0xffff,			/* maximum transfer size */
3857c478bd9Sstevel@tonic-gate 	0x7fff,			/* segment boundary */
3867c478bd9Sstevel@tonic-gate 	1,			/* s/g list length */
3877c478bd9Sstevel@tonic-gate 	1,			/* granularity of device */
3887c478bd9Sstevel@tonic-gate 	0			/* DMA flags */
3897c478bd9Sstevel@tonic-gate };
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate static struct ecpp_hw m1553 = {
3927c478bd9Sstevel@tonic-gate 	m1553_map_regs,
3937c478bd9Sstevel@tonic-gate 	m1553_unmap_regs,
3947c478bd9Sstevel@tonic-gate 	m1553_config_chip,
3957c478bd9Sstevel@tonic-gate 	empty_config_mode,	/* no config_mode */
3967c478bd9Sstevel@tonic-gate 	empty_mask_intr,	/* no mask_intr */
3977c478bd9Sstevel@tonic-gate 	empty_mask_intr,	/* no unmask_intr */
3987c478bd9Sstevel@tonic-gate 	dma8237_dma_start,
3997c478bd9Sstevel@tonic-gate 	dma8237_dma_stop,
4007c478bd9Sstevel@tonic-gate 	dma8237_getcnt,
4017c478bd9Sstevel@tonic-gate 	&i8237_dma_attr
4027c478bd9Sstevel@tonic-gate };
4037c478bd9Sstevel@tonic-gate 
4047c478bd9Sstevel@tonic-gate #if defined(__x86)
4057c478bd9Sstevel@tonic-gate static ddi_dma_attr_t sb_dma_attr = {
4067c478bd9Sstevel@tonic-gate 	DMA_ATTR_VERSION,	/* version */
4077c478bd9Sstevel@tonic-gate 	0x00000000ull,		/* dlim_addr_lo */
4087c478bd9Sstevel@tonic-gate 	0xffffff,		/* dlim_addr_hi */
4097c478bd9Sstevel@tonic-gate 	0xffff,			/* DMA counter register */
4107c478bd9Sstevel@tonic-gate 	1,			/* DMA address alignment */
4117c478bd9Sstevel@tonic-gate 	0x01,			/* burst sizes */
4127c478bd9Sstevel@tonic-gate 	0x0001,			/* min effective DMA size */
4137c478bd9Sstevel@tonic-gate 	0xffffffff,		/* maximum transfer size */
4147c478bd9Sstevel@tonic-gate 	0xffff,			/* segment boundary */
4157c478bd9Sstevel@tonic-gate 	1,			/* s/g list length */
4167c478bd9Sstevel@tonic-gate 	1,			/* granularity of device */
4177c478bd9Sstevel@tonic-gate 	0			/* DMA flags */
4187c478bd9Sstevel@tonic-gate };
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate static struct ecpp_hw x86 = {
4217c478bd9Sstevel@tonic-gate 	x86_map_regs,
4227c478bd9Sstevel@tonic-gate 	x86_unmap_regs,
4237c478bd9Sstevel@tonic-gate 	x86_config_chip,
4247c478bd9Sstevel@tonic-gate 	empty_config_mode,	/* no config_mode */
4257c478bd9Sstevel@tonic-gate 	empty_mask_intr,	/* no mask_intr */
4267c478bd9Sstevel@tonic-gate 	empty_mask_intr,	/* no unmask_intr */
4277c478bd9Sstevel@tonic-gate 	x86_dma_start,
4287c478bd9Sstevel@tonic-gate 	x86_dma_stop,
4297c478bd9Sstevel@tonic-gate 	x86_getcnt,
4307c478bd9Sstevel@tonic-gate 	&sb_dma_attr
4317c478bd9Sstevel@tonic-gate };
4327c478bd9Sstevel@tonic-gate #endif
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate /*
4357c478bd9Sstevel@tonic-gate  * list of supported devices
4367c478bd9Sstevel@tonic-gate  */
4377c478bd9Sstevel@tonic-gate struct ecpp_hw_bind ecpp_hw_bind[] = {
4387c478bd9Sstevel@tonic-gate 	{ "ns87317-ecpp",	&pc97317,	"PC97317" },
4397c478bd9Sstevel@tonic-gate 	{ "pnpALI,1533,3",	&m1553,		"M1553" },
4407c478bd9Sstevel@tonic-gate 	{ "ecpp",		&pc87332,	"PC87332" },
4417c478bd9Sstevel@tonic-gate #if defined(__x86)
4427c478bd9Sstevel@tonic-gate 	{ "lp",			&x86,		"i86pc"},
4437c478bd9Sstevel@tonic-gate #endif
4447c478bd9Sstevel@tonic-gate };
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate static ddi_device_acc_attr_t acc_attr = {
4477c478bd9Sstevel@tonic-gate 	DDI_DEVICE_ATTR_V0,
4487c478bd9Sstevel@tonic-gate 	DDI_STRUCTURE_LE_ACC,
4497c478bd9Sstevel@tonic-gate 	DDI_STRICTORDER_ACC
4507c478bd9Sstevel@tonic-gate };
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate static struct ecpp_transfer_parms default_xfer_parms = {
4537c478bd9Sstevel@tonic-gate 	FWD_TIMEOUT_DEFAULT,	/* write timeout in seconds */
4547c478bd9Sstevel@tonic-gate 	ECPP_CENTRONICS		/* supported mode */
4557c478bd9Sstevel@tonic-gate };
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate /* prnio interface info string */
4587c478bd9Sstevel@tonic-gate static const char prn_ifinfo[] = PRN_PARALLEL;
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate /* prnio timeouts */
4617c478bd9Sstevel@tonic-gate static const struct prn_timeouts prn_timeouts_default = {
4627c478bd9Sstevel@tonic-gate 	FWD_TIMEOUT_DEFAULT,	/* forward timeout */
4637c478bd9Sstevel@tonic-gate 	REV_TIMEOUT_DEFAULT	/* reverse timeout */
4647c478bd9Sstevel@tonic-gate };
4657c478bd9Sstevel@tonic-gate 
4667c478bd9Sstevel@tonic-gate static int ecpp_isr_max_delay = ECPP_ISR_MAX_DELAY;
4677c478bd9Sstevel@tonic-gate static int ecpp_def_timeout = 90;  /* left in for 2.7 compatibility */
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate static void    *ecppsoft_statep;
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate /*
4727c478bd9Sstevel@tonic-gate  * STREAMS framework manages locks for these structures
4737c478bd9Sstevel@tonic-gate  */
4747c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", iocblk))
4757c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", datab))
4767c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", msgb))
4777c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", queue))
4787c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", copyreq))
4797c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", stroptions))
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate struct module_info ecppinfo = {
4827c478bd9Sstevel@tonic-gate 	/* id, name, min pkt siz, max pkt siz, hi water, low water */
4837c478bd9Sstevel@tonic-gate 	42, "ecpp", 0, IO_BLOCK_SZ, ECPPHIWAT, ECPPLOWAT
4847c478bd9Sstevel@tonic-gate };
4857c478bd9Sstevel@tonic-gate 
4867c478bd9Sstevel@tonic-gate static struct qinit ecpp_rinit = {
4877c478bd9Sstevel@tonic-gate 	putq, ecpp_rsrv, ecpp_open, ecpp_close, NULL, &ecppinfo, NULL
4887c478bd9Sstevel@tonic-gate };
4897c478bd9Sstevel@tonic-gate 
4907c478bd9Sstevel@tonic-gate static struct qinit ecpp_wint = {
4917c478bd9Sstevel@tonic-gate 	ecpp_wput, ecpp_wsrv, ecpp_open, ecpp_close, NULL, &ecppinfo, NULL
4927c478bd9Sstevel@tonic-gate };
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate struct streamtab ecpp_str_info = {
4957c478bd9Sstevel@tonic-gate 	&ecpp_rinit, &ecpp_wint, NULL, NULL
4967c478bd9Sstevel@tonic-gate };
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate static struct cb_ops ecpp_cb_ops = {
4997c478bd9Sstevel@tonic-gate 	nodev,			/* cb_open */
5007c478bd9Sstevel@tonic-gate 	nodev,			/* cb_close */
5017c478bd9Sstevel@tonic-gate 	nodev,			/* cb_strategy */
5027c478bd9Sstevel@tonic-gate 	nodev,			/* cb_print */
5037c478bd9Sstevel@tonic-gate 	nodev,			/* cb_dump */
5047c478bd9Sstevel@tonic-gate 	nodev,			/* cb_read */
5057c478bd9Sstevel@tonic-gate 	nodev,			/* cb_write */
5067c478bd9Sstevel@tonic-gate 	nodev,			/* cb_ioctl */
5077c478bd9Sstevel@tonic-gate 	nodev,			/* cb_devmap */
5087c478bd9Sstevel@tonic-gate 	nodev,			/* cb_mmap */
5097c478bd9Sstevel@tonic-gate 	nodev,			/* cb_segmap */
5107c478bd9Sstevel@tonic-gate 	nochpoll,		/* cb_chpoll */
5117c478bd9Sstevel@tonic-gate 	ddi_prop_op,		/* cb_prop_op */
5127c478bd9Sstevel@tonic-gate 	&ecpp_str_info,		/* cb_stream */
5137c478bd9Sstevel@tonic-gate 	(D_NEW | D_MP | D_MTPERQ)	/* cb_flag */
5147c478bd9Sstevel@tonic-gate };
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate /*
5177c478bd9Sstevel@tonic-gate  * Declare ops vectors for auto configuration.
5187c478bd9Sstevel@tonic-gate  */
5197c478bd9Sstevel@tonic-gate struct dev_ops  ecpp_ops = {
5207c478bd9Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev */
5217c478bd9Sstevel@tonic-gate 	0,			/* devo_refcnt */
5227c478bd9Sstevel@tonic-gate 	ecpp_getinfo,		/* devo_getinfo */
5237c478bd9Sstevel@tonic-gate 	nulldev,		/* devo_identify */
5247c478bd9Sstevel@tonic-gate 	nulldev,		/* devo_probe */
5257c478bd9Sstevel@tonic-gate 	ecpp_attach,		/* devo_attach */
5267c478bd9Sstevel@tonic-gate 	ecpp_detach,		/* devo_detach */
5277c478bd9Sstevel@tonic-gate 	nodev,			/* devo_reset */
5287c478bd9Sstevel@tonic-gate 	&ecpp_cb_ops,		/* devo_cb_ops */
5297c478bd9Sstevel@tonic-gate 	(struct bus_ops *)NULL,	/* devo_bus_ops */
53019397407SSherry Moore 	nulldev,		/* devo_power */
53119397407SSherry Moore 	ddi_quiesce_not_needed,	/* devo_quiesce */
5327c478bd9Sstevel@tonic-gate };
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate extern struct mod_ops mod_driverops;
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate static struct modldrv ecppmodldrv = {
5377c478bd9Sstevel@tonic-gate 	&mod_driverops,		/* type of module - driver */
53819397407SSherry Moore 	"parallel port driver",
5397c478bd9Sstevel@tonic-gate 	&ecpp_ops,
5407c478bd9Sstevel@tonic-gate };
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate static struct modlinkage ecppmodlinkage = {
5437c478bd9Sstevel@tonic-gate 	MODREV_1,
5447c478bd9Sstevel@tonic-gate 	&ecppmodldrv,
5457c478bd9Sstevel@tonic-gate 	0
5467c478bd9Sstevel@tonic-gate };
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate /*
5507c478bd9Sstevel@tonic-gate  *
5517c478bd9Sstevel@tonic-gate  * DDI/DKI entry points and supplementary routines
5527c478bd9Sstevel@tonic-gate  *
5537c478bd9Sstevel@tonic-gate  */
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate int
_init(void)5577c478bd9Sstevel@tonic-gate _init(void)
5587c478bd9Sstevel@tonic-gate {
5597c478bd9Sstevel@tonic-gate 	int    error;
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate 	if ((error = mod_install(&ecppmodlinkage)) == 0) {
5627c478bd9Sstevel@tonic-gate 		(void) ddi_soft_state_init(&ecppsoft_statep,
5637c478bd9Sstevel@tonic-gate 		    sizeof (struct ecppunit), 1);
5647c478bd9Sstevel@tonic-gate 	}
5657c478bd9Sstevel@tonic-gate 
5667c478bd9Sstevel@tonic-gate 	return (error);
5677c478bd9Sstevel@tonic-gate }
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate int
_fini(void)5707c478bd9Sstevel@tonic-gate _fini(void)
5717c478bd9Sstevel@tonic-gate {
5727c478bd9Sstevel@tonic-gate 	int    error;
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 	if ((error = mod_remove(&ecppmodlinkage)) == 0) {
5757c478bd9Sstevel@tonic-gate 		ddi_soft_state_fini(&ecppsoft_statep);
5767c478bd9Sstevel@tonic-gate 	}
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	return (error);
5797c478bd9Sstevel@tonic-gate }
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)5827c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
5837c478bd9Sstevel@tonic-gate {
5847c478bd9Sstevel@tonic-gate 	return (mod_info(&ecppmodlinkage, modinfop));
5857c478bd9Sstevel@tonic-gate }
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate static int
ecpp_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)5887c478bd9Sstevel@tonic-gate ecpp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5897c478bd9Sstevel@tonic-gate {
5907c478bd9Sstevel@tonic-gate 	int			instance;
5917c478bd9Sstevel@tonic-gate 	char			name[16];
5927c478bd9Sstevel@tonic-gate 	struct ecppunit		*pp;
5937c478bd9Sstevel@tonic-gate 	struct ecpp_hw_bind	*hw_bind;
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate 	switch (cmd) {
5987c478bd9Sstevel@tonic-gate 	case DDI_ATTACH:
5997c478bd9Sstevel@tonic-gate 		break;
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 	case DDI_RESUME:
6027c478bd9Sstevel@tonic-gate 		if (!(pp = ddi_get_soft_state(ecppsoft_statep, instance))) {
6037c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
6047c478bd9Sstevel@tonic-gate 		}
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate 		pp->suspended = FALSE;
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 		/*
6117c478bd9Sstevel@tonic-gate 		 * Initialize the chip and restore current mode if needed
6127c478bd9Sstevel@tonic-gate 		 */
6137c478bd9Sstevel@tonic-gate 		(void) ECPP_CONFIG_CHIP(pp);
6147c478bd9Sstevel@tonic-gate 		(void) ecpp_reset_port_regs(pp);
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 		if (pp->oflag == TRUE) {
6177c478bd9Sstevel@tonic-gate 			int current_mode = pp->current_mode;
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 			(void) ecpp_1284_termination(pp);
6207c478bd9Sstevel@tonic-gate 			(void) ecpp_mode_negotiation(pp, current_mode);
6217c478bd9Sstevel@tonic-gate 		}
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate 	default:
6287c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
6297c478bd9Sstevel@tonic-gate 	}
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(ecppsoft_statep, instance) != 0) {
6327c478bd9Sstevel@tonic-gate 		ecpp_error(dip, "ddi_soft_state_zalloc failed\n");
6337c478bd9Sstevel@tonic-gate 		goto fail;
6347c478bd9Sstevel@tonic-gate 	}
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate 	pp = ddi_get_soft_state(ecppsoft_statep, instance);
6377c478bd9Sstevel@tonic-gate 
6387c478bd9Sstevel@tonic-gate 	pp->dip = dip;
6397c478bd9Sstevel@tonic-gate 	pp->suspended = FALSE;
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 	/*
6427c478bd9Sstevel@tonic-gate 	 * Determine SuperIO type and set chip-dependent variables
6437c478bd9Sstevel@tonic-gate 	 */
6447c478bd9Sstevel@tonic-gate 	hw_bind = ecpp_determine_sio_type(pp);
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 	if (hw_bind == NULL) {
6477c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE, "parallel port controller not supported");
6487c478bd9Sstevel@tonic-gate 		goto fail_sio;
6497c478bd9Sstevel@tonic-gate 	} else {
6507c478bd9Sstevel@tonic-gate 		pp->hw = hw_bind->hw;
6517c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "SuperIO type: %s\n", hw_bind->info);
6527c478bd9Sstevel@tonic-gate 	}
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 	/*
6557c478bd9Sstevel@tonic-gate 	 * Map registers
6567c478bd9Sstevel@tonic-gate 	 */
6577c478bd9Sstevel@tonic-gate 	if (ECPP_MAP_REGS(pp) != SUCCESS) {
6587c478bd9Sstevel@tonic-gate 		goto fail_map;
6597c478bd9Sstevel@tonic-gate 	}
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate 	if (ddi_dma_alloc_handle(dip, pp->hw->attr, DDI_DMA_DONTWAIT,
6627c478bd9Sstevel@tonic-gate 	    NULL, &pp->dma_handle) != DDI_SUCCESS) {
6637c478bd9Sstevel@tonic-gate 		ecpp_error(dip, "ecpp_attach: failed ddi_dma_alloc_handle\n");
6647c478bd9Sstevel@tonic-gate 		goto fail_dma;
6657c478bd9Sstevel@tonic-gate 	}
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 	if (ddi_get_iblock_cookie(dip, 0,
6687c478bd9Sstevel@tonic-gate 	    &pp->ecpp_trap_cookie) != DDI_SUCCESS) {
6697c478bd9Sstevel@tonic-gate 		ecpp_error(dip, "ecpp_attach: failed ddi_get_iblock_cookie\n");
6707c478bd9Sstevel@tonic-gate 		goto fail_ibc;
6717c478bd9Sstevel@tonic-gate 	}
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate 	mutex_init(&pp->umutex, NULL, MUTEX_DRIVER,
67419397407SSherry Moore 	    (void *)pp->ecpp_trap_cookie);
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate 	cv_init(&pp->pport_cv, NULL, CV_DRIVER, NULL);
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 	if (ddi_add_intr(dip, 0, &pp->ecpp_trap_cookie, NULL, ecpp_isr,
6797c478bd9Sstevel@tonic-gate 	    (caddr_t)pp) != DDI_SUCCESS) {
6807c478bd9Sstevel@tonic-gate 		ecpp_error(dip, "ecpp_attach: failed to add hard intr\n");
6817c478bd9Sstevel@tonic-gate 		goto fail_intr;
6827c478bd9Sstevel@tonic-gate 	}
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 	if (ddi_add_softintr(dip, DDI_SOFTINT_LOW,
6857c478bd9Sstevel@tonic-gate 	    &pp->softintr_id, 0, 0, ecpp_softintr,
6867c478bd9Sstevel@tonic-gate 	    (caddr_t)pp) != DDI_SUCCESS) {
6877c478bd9Sstevel@tonic-gate 		ecpp_error(dip, "ecpp_attach: failed to add soft intr\n");
6887c478bd9Sstevel@tonic-gate 		goto fail_softintr;
6897c478bd9Sstevel@tonic-gate 	}
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate 	(void) sprintf(name, "ecpp%d", instance);
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate 	if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
6942520aea3SToomas Soome 	    DDI_NT_PRINTER, 0) == DDI_FAILURE) {
6957c478bd9Sstevel@tonic-gate 		ecpp_error(dip, "ecpp_attach: create_minor_node failed\n");
6967c478bd9Sstevel@tonic-gate 		goto fail_minor;
6977c478bd9Sstevel@tonic-gate 	}
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate 	pp->ioblock = (caddr_t)kmem_alloc(IO_BLOCK_SZ, KM_SLEEP);
7007c478bd9Sstevel@tonic-gate 	if (pp->ioblock == NULL) {
7017c478bd9Sstevel@tonic-gate 		ecpp_error(dip, "ecpp_attach: kmem_alloc failed\n");
7027c478bd9Sstevel@tonic-gate 		goto fail_iob;
7037c478bd9Sstevel@tonic-gate 	} else {
7047c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_attach: ioblock=0x%x\n", pp->ioblock);
7057c478bd9Sstevel@tonic-gate 	}
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate 	ecpp_get_props(pp);
7087c478bd9Sstevel@tonic-gate #if defined(__x86)
7097c478bd9Sstevel@tonic-gate 	if (pp->hw == &x86 && pp->uh.x86.chn != 0xff) {
7107c478bd9Sstevel@tonic-gate 		if (ddi_dmae_alloc(dip, pp->uh.x86.chn,
7117c478bd9Sstevel@tonic-gate 		    DDI_DMA_DONTWAIT, NULL) == DDI_SUCCESS)
7127c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "dmae_alloc success!\n");
7137c478bd9Sstevel@tonic-gate 	}
7147c478bd9Sstevel@tonic-gate #endif
7157c478bd9Sstevel@tonic-gate 	if (ECPP_CONFIG_CHIP(pp) == FAILURE) {
7167c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "config_chip failed.\n");
7177c478bd9Sstevel@tonic-gate 		goto fail_config;
7187c478bd9Sstevel@tonic-gate 	}
7197c478bd9Sstevel@tonic-gate 
7207c478bd9Sstevel@tonic-gate 	ecpp_kstat_init(pp);
7217c478bd9Sstevel@tonic-gate 
7227c478bd9Sstevel@tonic-gate 	ddi_report_dev(dip);
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate fail_config:
7277c478bd9Sstevel@tonic-gate 	ddi_prop_remove_all(dip);
7287c478bd9Sstevel@tonic-gate 	kmem_free(pp->ioblock, IO_BLOCK_SZ);
7297c478bd9Sstevel@tonic-gate fail_iob:
7307c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(dip, NULL);
7317c478bd9Sstevel@tonic-gate fail_minor:
7327c478bd9Sstevel@tonic-gate 	ddi_remove_softintr(pp->softintr_id);
7337c478bd9Sstevel@tonic-gate fail_softintr:
7347c478bd9Sstevel@tonic-gate 	ddi_remove_intr(dip, (uint_t)0, pp->ecpp_trap_cookie);
7357c478bd9Sstevel@tonic-gate fail_intr:
7367c478bd9Sstevel@tonic-gate 	mutex_destroy(&pp->umutex);
7377c478bd9Sstevel@tonic-gate 	cv_destroy(&pp->pport_cv);
7387c478bd9Sstevel@tonic-gate fail_ibc:
7397c478bd9Sstevel@tonic-gate 	ddi_dma_free_handle(&pp->dma_handle);
7407c478bd9Sstevel@tonic-gate fail_dma:
7417c478bd9Sstevel@tonic-gate 	ECPP_UNMAP_REGS(pp);
7427c478bd9Sstevel@tonic-gate fail_map:
7437c478bd9Sstevel@tonic-gate fail_sio:
7447c478bd9Sstevel@tonic-gate 	ddi_soft_state_free(ecppsoft_statep, instance);
7457c478bd9Sstevel@tonic-gate fail:
7467c478bd9Sstevel@tonic-gate 	ecpp_error(dip, "ecpp_attach: failed.\n");
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
7497c478bd9Sstevel@tonic-gate }
7507c478bd9Sstevel@tonic-gate 
7517c478bd9Sstevel@tonic-gate static int
ecpp_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)7527c478bd9Sstevel@tonic-gate ecpp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
7537c478bd9Sstevel@tonic-gate {
7547c478bd9Sstevel@tonic-gate 	int		instance;
7557c478bd9Sstevel@tonic-gate 	struct ecppunit *pp;
7567c478bd9Sstevel@tonic-gate 
7577c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
7587c478bd9Sstevel@tonic-gate 
7597c478bd9Sstevel@tonic-gate 	switch (cmd) {
7607c478bd9Sstevel@tonic-gate 	case DDI_DETACH:
7617c478bd9Sstevel@tonic-gate 		break;
7627c478bd9Sstevel@tonic-gate 
7637c478bd9Sstevel@tonic-gate 	case DDI_SUSPEND:
7647c478bd9Sstevel@tonic-gate 		if (!(pp = ddi_get_soft_state(ecppsoft_statep, instance))) {
7657c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
7667c478bd9Sstevel@tonic-gate 		}
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
7697c478bd9Sstevel@tonic-gate 		ASSERT(pp->suspended == FALSE);
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 		pp->suspended = TRUE;	/* prevent new transfers */
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate 		/*
7747c478bd9Sstevel@tonic-gate 		 * Wait if there's any activity on the port
7757c478bd9Sstevel@tonic-gate 		 */
7767c478bd9Sstevel@tonic-gate 		if ((pp->e_busy == ECPP_BUSY) || (pp->e_busy == ECPP_FLUSH)) {
777d3d50737SRafael Vanoni 			(void) cv_reltimedwait(&pp->pport_cv, &pp->umutex,
778d3d50737SRafael Vanoni 			    SUSPEND_TOUT * drv_usectohz(1000000),
779d3d50737SRafael Vanoni 			    TR_CLOCK_TICK);
7807c478bd9Sstevel@tonic-gate 			if ((pp->e_busy == ECPP_BUSY) ||
7817c478bd9Sstevel@tonic-gate 			    (pp->e_busy == ECPP_FLUSH)) {
7827c478bd9Sstevel@tonic-gate 				pp->suspended = FALSE;
7837c478bd9Sstevel@tonic-gate 				mutex_exit(&pp->umutex);
7847c478bd9Sstevel@tonic-gate 				ecpp_error(pp->dip,
78519397407SSherry Moore 				    "ecpp_detach: suspend timeout\n");
7867c478bd9Sstevel@tonic-gate 				return (DDI_FAILURE);
7877c478bd9Sstevel@tonic-gate 			}
7887c478bd9Sstevel@tonic-gate 		}
7897c478bd9Sstevel@tonic-gate 
7907c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
7917c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
7927c478bd9Sstevel@tonic-gate 
7937c478bd9Sstevel@tonic-gate 	default:
7947c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
7957c478bd9Sstevel@tonic-gate 	}
7967c478bd9Sstevel@tonic-gate 
7977c478bd9Sstevel@tonic-gate 	pp = ddi_get_soft_state(ecppsoft_statep, instance);
7987c478bd9Sstevel@tonic-gate #if defined(__x86)
7997c478bd9Sstevel@tonic-gate 	if (pp->hw == &x86 && pp->uh.x86.chn != 0xff)
8007c478bd9Sstevel@tonic-gate 		(void) ddi_dmae_release(pp->dip, pp->uh.x86.chn);
8017c478bd9Sstevel@tonic-gate #endif
8027c478bd9Sstevel@tonic-gate 	if (pp->dma_handle != NULL)
8037c478bd9Sstevel@tonic-gate 		ddi_dma_free_handle(&pp->dma_handle);
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(dip, NULL);
8067c478bd9Sstevel@tonic-gate 
8077c478bd9Sstevel@tonic-gate 	ddi_remove_softintr(pp->softintr_id);
8087c478bd9Sstevel@tonic-gate 
8097c478bd9Sstevel@tonic-gate 	ddi_remove_intr(dip, (uint_t)0, pp->ecpp_trap_cookie);
8107c478bd9Sstevel@tonic-gate 
8117c478bd9Sstevel@tonic-gate 	if (pp->ksp) {
8127c478bd9Sstevel@tonic-gate 		kstat_delete(pp->ksp);
8137c478bd9Sstevel@tonic-gate 	}
8147c478bd9Sstevel@tonic-gate 	if (pp->intrstats) {
8157c478bd9Sstevel@tonic-gate 		kstat_delete(pp->intrstats);
8167c478bd9Sstevel@tonic-gate 	}
8177c478bd9Sstevel@tonic-gate 
8187c478bd9Sstevel@tonic-gate 	cv_destroy(&pp->pport_cv);
8197c478bd9Sstevel@tonic-gate 
8207c478bd9Sstevel@tonic-gate 	mutex_destroy(&pp->umutex);
8217c478bd9Sstevel@tonic-gate 
8227c478bd9Sstevel@tonic-gate 	ECPP_UNMAP_REGS(pp);
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 	kmem_free(pp->ioblock, IO_BLOCK_SZ);
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate 	ddi_prop_remove_all(dip);
8277c478bd9Sstevel@tonic-gate 
8287c478bd9Sstevel@tonic-gate 	ddi_soft_state_free(ecppsoft_statep, instance);
8297c478bd9Sstevel@tonic-gate 
8307c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate }
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate /*
8357c478bd9Sstevel@tonic-gate  * ecpp_get_props() reads ecpp.conf for user defineable tuneables.
8367c478bd9Sstevel@tonic-gate  * If the file or a particular variable is not there, a default value
8377c478bd9Sstevel@tonic-gate  * is assigned.
8387c478bd9Sstevel@tonic-gate  */
8397c478bd9Sstevel@tonic-gate 
8407c478bd9Sstevel@tonic-gate static void
ecpp_get_props(struct ecppunit * pp)8417c478bd9Sstevel@tonic-gate ecpp_get_props(struct ecppunit *pp)
8427c478bd9Sstevel@tonic-gate {
8437c478bd9Sstevel@tonic-gate 	char	*prop;
8447c478bd9Sstevel@tonic-gate #if defined(__x86)
8457c478bd9Sstevel@tonic-gate 	int	len;
8467c478bd9Sstevel@tonic-gate 	int	value;
8477c478bd9Sstevel@tonic-gate #endif
8487c478bd9Sstevel@tonic-gate 	/*
8497c478bd9Sstevel@tonic-gate 	 * If fast_centronics is TRUE, non-compliant IEEE 1284
8507c478bd9Sstevel@tonic-gate 	 * peripherals ( Centronics peripherals) will operate in DMA mode.
8517c478bd9Sstevel@tonic-gate 	 * Transfers betwee main memory and the device will be via DMA;
8527c478bd9Sstevel@tonic-gate 	 * peripheral handshaking will be conducted by superio logic.
8537c478bd9Sstevel@tonic-gate 	 * If ecpp can not read the variable correctly fast_centronics will
8547c478bd9Sstevel@tonic-gate 	 * be set to FALSE.  In this case, transfers and handshaking
8557c478bd9Sstevel@tonic-gate 	 * will be conducted by PIO for Centronics devices.
8567c478bd9Sstevel@tonic-gate 	 */
8577c478bd9Sstevel@tonic-gate 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pp->dip, 0,
85819397407SSherry Moore 	    "fast-centronics", &prop) == DDI_PROP_SUCCESS) {
8597c478bd9Sstevel@tonic-gate 		pp->fast_centronics =
86019397407SSherry Moore 		    (strcmp(prop, "true") == 0) ? TRUE : FALSE;
8617c478bd9Sstevel@tonic-gate 		ddi_prop_free(prop);
8627c478bd9Sstevel@tonic-gate 	} else {
8637c478bd9Sstevel@tonic-gate 		pp->fast_centronics = FALSE;
8647c478bd9Sstevel@tonic-gate 	}
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate 	/*
8677c478bd9Sstevel@tonic-gate 	 * If fast-1284-compatible is set to TRUE, when ecpp communicates
8687c478bd9Sstevel@tonic-gate 	 * with IEEE 1284 compliant peripherals, data transfers between
8697c478bd9Sstevel@tonic-gate 	 * main memory and the parallel port will be conducted by DMA.
8707c478bd9Sstevel@tonic-gate 	 * Handshaking between the port and peripheral will be conducted
8717c478bd9Sstevel@tonic-gate 	 * by superio logic.  This is the default characteristic.  If
8727c478bd9Sstevel@tonic-gate 	 * fast-1284-compatible is set to FALSE, transfers and handshaking
8737c478bd9Sstevel@tonic-gate 	 * will be conducted by PIO.
8747c478bd9Sstevel@tonic-gate 	 */
8757c478bd9Sstevel@tonic-gate 
8767c478bd9Sstevel@tonic-gate 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pp->dip, 0,
87719397407SSherry Moore 	    "fast-1284-compatible", &prop) == DDI_PROP_SUCCESS) {
8787c478bd9Sstevel@tonic-gate 		pp->fast_compat = (strcmp(prop, "true") == 0) ? TRUE : FALSE;
8797c478bd9Sstevel@tonic-gate 		ddi_prop_free(prop);
8807c478bd9Sstevel@tonic-gate 	} else {
8817c478bd9Sstevel@tonic-gate 		pp->fast_compat = TRUE;
8827c478bd9Sstevel@tonic-gate 	}
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate 	/*
8857c478bd9Sstevel@tonic-gate 	 * Some centronics peripherals require the nInit signal to be
8867c478bd9Sstevel@tonic-gate 	 * toggled to reset the device.  If centronics_init_seq is set
8877c478bd9Sstevel@tonic-gate 	 * to TRUE, ecpp will toggle the nInit signal upon every ecpp_open().
8887c478bd9Sstevel@tonic-gate 	 * Applications have the opportunity to toggle the nInit signal
8897c478bd9Sstevel@tonic-gate 	 * with ioctl(2) calls as well.  The default is to set it to FALSE.
8907c478bd9Sstevel@tonic-gate 	 */
8917c478bd9Sstevel@tonic-gate 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pp->dip, 0,
89219397407SSherry Moore 	    "centronics-init-seq", &prop) == DDI_PROP_SUCCESS) {
8937c478bd9Sstevel@tonic-gate 		pp->init_seq = (strcmp(prop, "true") == 0) ? TRUE : FALSE;
8947c478bd9Sstevel@tonic-gate 		ddi_prop_free(prop);
8957c478bd9Sstevel@tonic-gate 	} else {
8967c478bd9Sstevel@tonic-gate 		pp->init_seq = FALSE;
8977c478bd9Sstevel@tonic-gate 	}
8987c478bd9Sstevel@tonic-gate 
8997c478bd9Sstevel@tonic-gate 	/*
9007c478bd9Sstevel@tonic-gate 	 * If one of the centronics status signals are in an erroneous
9017c478bd9Sstevel@tonic-gate 	 * state, ecpp_wsrv() will be reinvoked centronics-retry ms to
9027c478bd9Sstevel@tonic-gate 	 * check if the status is ok to transfer.  If the property is not
9037c478bd9Sstevel@tonic-gate 	 * found, wsrv_retry will be set to CENTRONICS_RETRY ms.
9047c478bd9Sstevel@tonic-gate 	 */
9057c478bd9Sstevel@tonic-gate 	pp->wsrv_retry = ddi_prop_get_int(DDI_DEV_T_ANY, pp->dip, 0,
90619397407SSherry Moore 	    "centronics-retry", CENTRONICS_RETRY);
9077c478bd9Sstevel@tonic-gate 
9087c478bd9Sstevel@tonic-gate 	/*
9097c478bd9Sstevel@tonic-gate 	 * In PIO mode, ecpp_isr() will loop for wait for the busy signal
9107c478bd9Sstevel@tonic-gate 	 * to be deasserted before transferring the next byte. wait_for_busy
9117c478bd9Sstevel@tonic-gate 	 * is specificied in microseconds.  If the property is not found
9127c478bd9Sstevel@tonic-gate 	 * ecpp_isr() will wait for a maximum of WAIT_FOR_BUSY us.
9137c478bd9Sstevel@tonic-gate 	 */
9147c478bd9Sstevel@tonic-gate 	pp->wait_for_busy = ddi_prop_get_int(DDI_DEV_T_ANY, pp->dip, 0,
91519397407SSherry Moore 	    "centronics-wait-for-busy", WAIT_FOR_BUSY);
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate 	/*
9187c478bd9Sstevel@tonic-gate 	 * In PIO mode, centronics transfers must hold the data signals
9197c478bd9Sstevel@tonic-gate 	 * for a data_setup_time milliseconds before the strobe is asserted.
9207c478bd9Sstevel@tonic-gate 	 */
9217c478bd9Sstevel@tonic-gate 	pp->data_setup_time = ddi_prop_get_int(DDI_DEV_T_ANY, pp->dip, 0,
92219397407SSherry Moore 	    "centronics-data-setup-time", DATA_SETUP_TIME);
9237c478bd9Sstevel@tonic-gate 
9247c478bd9Sstevel@tonic-gate 	/*
9257c478bd9Sstevel@tonic-gate 	 * In PIO mode, centronics transfers asserts the strobe signal
9267c478bd9Sstevel@tonic-gate 	 * for a period of strobe_pulse_width milliseconds.
9277c478bd9Sstevel@tonic-gate 	 */
9287c478bd9Sstevel@tonic-gate 	pp->strobe_pulse_width = ddi_prop_get_int(DDI_DEV_T_ANY, pp->dip, 0,
92919397407SSherry Moore 	    "centronics-strobe-pulse-width", STROBE_PULSE_WIDTH);
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate 	/*
9327c478bd9Sstevel@tonic-gate 	 * Upon a transfer the peripheral, ecpp waits write_timeout seconds
9337c478bd9Sstevel@tonic-gate 	 * for the transmission to complete.
9347c478bd9Sstevel@tonic-gate 	 */
9357c478bd9Sstevel@tonic-gate 	default_xfer_parms.write_timeout = ddi_prop_get_int(DDI_DEV_T_ANY,
93619397407SSherry Moore 	    pp->dip, 0, "ecpp-transfer-timeout", ecpp_def_timeout);
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate 	pp->xfer_parms = default_xfer_parms;
9397c478bd9Sstevel@tonic-gate 
9407c478bd9Sstevel@tonic-gate 	/*
9417c478bd9Sstevel@tonic-gate 	 * Get dma channel for M1553
9427c478bd9Sstevel@tonic-gate 	 */
9437c478bd9Sstevel@tonic-gate 	if (pp->hw == &m1553) {
9447c478bd9Sstevel@tonic-gate 		pp->uh.m1553.chn = ddi_prop_get_int(DDI_DEV_T_ANY,
94519397407SSherry Moore 		    pp->dip, 0, "dma-channel", 0x1);
9467c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_get_prop:chn=%x\n", pp->uh.m1553.chn);
9477c478bd9Sstevel@tonic-gate 	}
9487c478bd9Sstevel@tonic-gate #if defined(__x86)
9497c478bd9Sstevel@tonic-gate 	len = sizeof (value);
9507c478bd9Sstevel@tonic-gate 	/* Get dma channel for i86 pc */
9517c478bd9Sstevel@tonic-gate 	if (pp->hw == &x86) {
9527c478bd9Sstevel@tonic-gate 		if (ddi_prop_op(DDI_DEV_T_ANY, pp->dip, PROP_LEN_AND_VAL_BUF,
9537c478bd9Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "dma-channels", (caddr_t)&value, &len)
9547c478bd9Sstevel@tonic-gate 		    != DDI_PROP_SUCCESS) {
9557c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "No dma channel found\n");
9567c478bd9Sstevel@tonic-gate 			pp->uh.x86.chn = 0xff;
9577c478bd9Sstevel@tonic-gate 			pp->fast_compat = FALSE;
9587c478bd9Sstevel@tonic-gate 			pp->noecpregs = TRUE;
9597c478bd9Sstevel@tonic-gate 		} else
9607c478bd9Sstevel@tonic-gate 			pp->uh.x86.chn = (uint8_t)value;
9617c478bd9Sstevel@tonic-gate 	}
9627c478bd9Sstevel@tonic-gate #endif
9637c478bd9Sstevel@tonic-gate 	/*
9647c478bd9Sstevel@tonic-gate 	 * these properties are not yet public
9657c478bd9Sstevel@tonic-gate 	 */
9667c478bd9Sstevel@tonic-gate 	pp->ecp_rev_speed = ddi_prop_get_int(DDI_DEV_T_ANY, pp->dip, 0,
96719397407SSherry Moore 	    "ecp-rev-speed", ECP_REV_SPEED);
9687c478bd9Sstevel@tonic-gate 
9697c478bd9Sstevel@tonic-gate 	pp->rev_watchdog = ddi_prop_get_int(DDI_DEV_T_ANY, pp->dip, 0,
97019397407SSherry Moore 	    "rev-watchdog", REV_WATCHDOG);
9717c478bd9Sstevel@tonic-gate 
9727c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip,
97319397407SSherry Moore 	    "ecpp_get_prop: fast_centronics=%x, fast-1284=%x\n"
97419397407SSherry Moore 	    "ecpp_get_prop: wsrv_retry=%d, wait_for_busy=%d\n"
97519397407SSherry Moore 	    "ecpp_get_prop: data_setup=%d, strobe_pulse=%d\n"
97619397407SSherry Moore 	    "ecpp_get_prop: transfer-timeout=%d\n",
97719397407SSherry Moore 	    pp->fast_centronics, pp->fast_compat,
97819397407SSherry Moore 	    pp->wsrv_retry, pp->wait_for_busy,
97919397407SSherry Moore 	    pp->data_setup_time, pp->strobe_pulse_width,
98019397407SSherry Moore 	    pp->xfer_parms.write_timeout);
9817c478bd9Sstevel@tonic-gate }
9827c478bd9Sstevel@tonic-gate 
9837c478bd9Sstevel@tonic-gate /*ARGSUSED*/
9847c478bd9Sstevel@tonic-gate int
ecpp_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)9857c478bd9Sstevel@tonic-gate ecpp_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
9867c478bd9Sstevel@tonic-gate {
9877c478bd9Sstevel@tonic-gate 	dev_t	dev = (dev_t)arg;
9887c478bd9Sstevel@tonic-gate 	struct ecppunit *pp;
9897c478bd9Sstevel@tonic-gate 	int	instance, ret;
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate 	instance = getminor(dev);
9927c478bd9Sstevel@tonic-gate 
9937c478bd9Sstevel@tonic-gate 	switch (infocmd) {
9947c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
9957c478bd9Sstevel@tonic-gate 		pp = ddi_get_soft_state(ecppsoft_statep, instance);
9967c478bd9Sstevel@tonic-gate 		if (pp != NULL) {
9977c478bd9Sstevel@tonic-gate 			*result = pp->dip;
9987c478bd9Sstevel@tonic-gate 			ret = DDI_SUCCESS;
9997c478bd9Sstevel@tonic-gate 		} else {
10007c478bd9Sstevel@tonic-gate 			ret = DDI_FAILURE;
10017c478bd9Sstevel@tonic-gate 		}
10027c478bd9Sstevel@tonic-gate 		break;
10037c478bd9Sstevel@tonic-gate 
10047c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
10057c478bd9Sstevel@tonic-gate 		*result = (void *)(uintptr_t)instance;
10067c478bd9Sstevel@tonic-gate 		ret = DDI_SUCCESS;
10077c478bd9Sstevel@tonic-gate 		break;
10087c478bd9Sstevel@tonic-gate 
10097c478bd9Sstevel@tonic-gate 	default:
10107c478bd9Sstevel@tonic-gate 		ret = DDI_FAILURE;
10117c478bd9Sstevel@tonic-gate 		break;
10127c478bd9Sstevel@tonic-gate 	}
10137c478bd9Sstevel@tonic-gate 
10147c478bd9Sstevel@tonic-gate 	return (ret);
10157c478bd9Sstevel@tonic-gate }
10167c478bd9Sstevel@tonic-gate 
10177c478bd9Sstevel@tonic-gate /*ARGSUSED2*/
10187c478bd9Sstevel@tonic-gate static int
ecpp_open(queue_t * q,dev_t * dev,int flag,int sflag,cred_t * credp)10197c478bd9Sstevel@tonic-gate ecpp_open(queue_t *q, dev_t *dev, int flag, int sflag, cred_t *credp)
10207c478bd9Sstevel@tonic-gate {
10217c478bd9Sstevel@tonic-gate 	struct ecppunit *pp;
10227c478bd9Sstevel@tonic-gate 	int		instance;
10237c478bd9Sstevel@tonic-gate 	struct stroptions *sop;
10247c478bd9Sstevel@tonic-gate 	mblk_t		*mop;
10257c478bd9Sstevel@tonic-gate 
10267c478bd9Sstevel@tonic-gate 	instance = getminor(*dev);
10277c478bd9Sstevel@tonic-gate 
10287c478bd9Sstevel@tonic-gate 	if (instance < 0) {
10297c478bd9Sstevel@tonic-gate 		return (ENXIO);
10307c478bd9Sstevel@tonic-gate 	}
10317c478bd9Sstevel@tonic-gate 
10327c478bd9Sstevel@tonic-gate 	pp = (struct ecppunit *)ddi_get_soft_state(ecppsoft_statep, instance);
10337c478bd9Sstevel@tonic-gate 
10347c478bd9Sstevel@tonic-gate 	if (pp == NULL) {
10357c478bd9Sstevel@tonic-gate 		return (ENXIO);
10367c478bd9Sstevel@tonic-gate 	}
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->umutex);
10397c478bd9Sstevel@tonic-gate 
10407c478bd9Sstevel@tonic-gate 	/*
10417c478bd9Sstevel@tonic-gate 	 * Parallel port is an exclusive-use device
10427c478bd9Sstevel@tonic-gate 	 * thus providing print job integrity
10437c478bd9Sstevel@tonic-gate 	 */
10447c478bd9Sstevel@tonic-gate 	if (pp->oflag == TRUE) {
10457c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp open failed");
10467c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
10477c478bd9Sstevel@tonic-gate 		return (EBUSY);
10487c478bd9Sstevel@tonic-gate 	}
10497c478bd9Sstevel@tonic-gate 
10507c478bd9Sstevel@tonic-gate 	pp->oflag = TRUE;
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate 	/* initialize state variables */
10537c478bd9Sstevel@tonic-gate 	pp->prn_timeouts = prn_timeouts_default;
10547c478bd9Sstevel@tonic-gate 	pp->xfer_parms = default_xfer_parms;
10557c478bd9Sstevel@tonic-gate 	pp->current_mode = ECPP_CENTRONICS;
10567c478bd9Sstevel@tonic-gate 	pp->backchannel = ECPP_CENTRONICS;
10577c478bd9Sstevel@tonic-gate 	pp->current_phase = ECPP_PHASE_PO;
10587c478bd9Sstevel@tonic-gate 	pp->port = ECPP_PORT_DMA;
10597c478bd9Sstevel@tonic-gate 	pp->instance = instance;
10607c478bd9Sstevel@tonic-gate 	pp->timeout_error = 0;
10617c478bd9Sstevel@tonic-gate 	pp->saved_dsr = DSR_READ(pp);
10627c478bd9Sstevel@tonic-gate 	pp->ecpp_drain_counter = 0;
10637c478bd9Sstevel@tonic-gate 	pp->dma_cancelled = FALSE;
10647c478bd9Sstevel@tonic-gate 	pp->io_mode = ECPP_DMA;
10657c478bd9Sstevel@tonic-gate 	pp->joblen = 0;
10667c478bd9Sstevel@tonic-gate 	pp->tfifo_intr = 0;
10677c478bd9Sstevel@tonic-gate 	pp->softintr_pending = 0;
10687c478bd9Sstevel@tonic-gate 	pp->nread = 0;
10697c478bd9Sstevel@tonic-gate 
10707c478bd9Sstevel@tonic-gate 	/* clear the state flag */
10717c478bd9Sstevel@tonic-gate 	pp->e_busy = ECPP_IDLE;
10727c478bd9Sstevel@tonic-gate 
10737c478bd9Sstevel@tonic-gate 	pp->readq = RD(q);
10747c478bd9Sstevel@tonic-gate 	pp->writeq = WR(q);
10757c478bd9Sstevel@tonic-gate 	pp->msg = NULL;
10767c478bd9Sstevel@tonic-gate 
10777c478bd9Sstevel@tonic-gate 	RD(q)->q_ptr = WR(q)->q_ptr = (caddr_t)pp;
10787c478bd9Sstevel@tonic-gate 
10797c478bd9Sstevel@tonic-gate 	/*
10807c478bd9Sstevel@tonic-gate 	 * Get ready: check host/peripheral, negotiate into default mode
10817c478bd9Sstevel@tonic-gate 	 */
10827c478bd9Sstevel@tonic-gate 	if (ecpp_reset_port_regs(pp) == FAILURE) {
10837c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
10847c478bd9Sstevel@tonic-gate 		return (EIO);
10857c478bd9Sstevel@tonic-gate 	}
10867c478bd9Sstevel@tonic-gate 
10877c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->umutex);
10887c478bd9Sstevel@tonic-gate 
10897c478bd9Sstevel@tonic-gate 	/*
10907c478bd9Sstevel@tonic-gate 	 * Configure the Stream head and enable the Stream
10917c478bd9Sstevel@tonic-gate 	 */
10927c478bd9Sstevel@tonic-gate 	if (!(mop = allocb(sizeof (struct stroptions), BPRI_MED))) {
10937c478bd9Sstevel@tonic-gate 		return (EAGAIN);
10947c478bd9Sstevel@tonic-gate 	}
10957c478bd9Sstevel@tonic-gate 
10967c478bd9Sstevel@tonic-gate 	mop->b_datap->db_type = M_SETOPTS;
10977c478bd9Sstevel@tonic-gate 	mop->b_wptr += sizeof (struct stroptions);
10987c478bd9Sstevel@tonic-gate 
10997c478bd9Sstevel@tonic-gate 	/*
11007c478bd9Sstevel@tonic-gate 	 * if device is open with O_NONBLOCK flag set, let read(2) return 0
11017c478bd9Sstevel@tonic-gate 	 * if no data waiting to be read.  Writes will block on flow control.
11027c478bd9Sstevel@tonic-gate 	 */
11037c478bd9Sstevel@tonic-gate 	sop = (struct stroptions *)mop->b_rptr;
11047c478bd9Sstevel@tonic-gate 	sop->so_flags = SO_HIWAT | SO_LOWAT | SO_NDELON | SO_MREADON;
11057c478bd9Sstevel@tonic-gate 	sop->so_hiwat = ECPPHIWAT;
11067c478bd9Sstevel@tonic-gate 	sop->so_lowat = ECPPLOWAT;
11077c478bd9Sstevel@tonic-gate 
11087c478bd9Sstevel@tonic-gate 	/* enable the stream */
11097c478bd9Sstevel@tonic-gate 	qprocson(q);
11107c478bd9Sstevel@tonic-gate 
11117c478bd9Sstevel@tonic-gate 	putnext(q, mop);
11127c478bd9Sstevel@tonic-gate 
11137c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->umutex);
11147c478bd9Sstevel@tonic-gate 
11157c478bd9Sstevel@tonic-gate 	ecpp_default_negotiation(pp);
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate 	/* go revidle */
11187c478bd9Sstevel@tonic-gate 	(void) ecpp_idle_phase(pp);
11197c478bd9Sstevel@tonic-gate 
11207c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip,
112119397407SSherry Moore 	    "ecpp_open: mode=%x, phase=%x ecr=%x, dsr=%x, dcr=%x\n",
112219397407SSherry Moore 	    pp->current_mode, pp->current_phase,
112319397407SSherry Moore 	    ECR_READ(pp), DSR_READ(pp), DCR_READ(pp));
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->umutex);
11267c478bd9Sstevel@tonic-gate 
11277c478bd9Sstevel@tonic-gate 	return (0);
11287c478bd9Sstevel@tonic-gate }
11297c478bd9Sstevel@tonic-gate 
11307c478bd9Sstevel@tonic-gate /*ARGSUSED1*/
11317c478bd9Sstevel@tonic-gate static int
ecpp_close(queue_t * q,int flag,cred_t * cred_p)11327c478bd9Sstevel@tonic-gate ecpp_close(queue_t *q, int flag, cred_t *cred_p)
11337c478bd9Sstevel@tonic-gate {
11347c478bd9Sstevel@tonic-gate 	struct ecppunit *pp;
11357c478bd9Sstevel@tonic-gate 	timeout_id_t	timeout_id, fifo_timer_id, wsrv_timer_id;
11367c478bd9Sstevel@tonic-gate 
11377c478bd9Sstevel@tonic-gate 	pp = (struct ecppunit *)q->q_ptr;
11387c478bd9Sstevel@tonic-gate 
11397c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecpp_close: entering ...\n");
11407c478bd9Sstevel@tonic-gate 
11417c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->umutex);
11427c478bd9Sstevel@tonic-gate 
11437c478bd9Sstevel@tonic-gate 	/*
11447c478bd9Sstevel@tonic-gate 	 * ecpp_close() will continue to loop until the
11457c478bd9Sstevel@tonic-gate 	 * queue has been drained or if the thread
11467c478bd9Sstevel@tonic-gate 	 * has received a SIG.  Typically, when the queue
11477c478bd9Sstevel@tonic-gate 	 * has data, the port will be ECPP_BUSY.  However,
11487c478bd9Sstevel@tonic-gate 	 * after a dma completes and before the wsrv
11497c478bd9Sstevel@tonic-gate 	 * starts the next transfer, the port may be IDLE.
11507c478bd9Sstevel@tonic-gate 	 * In this case, ecpp_close() will loop within this
11517c478bd9Sstevel@tonic-gate 	 * while(qsize) segment.  Since, ecpp_wsrv() runs
11527c478bd9Sstevel@tonic-gate 	 * at software interupt level, this shouldn't loop
11537c478bd9Sstevel@tonic-gate 	 * very long.
11547c478bd9Sstevel@tonic-gate 	 */
11557c478bd9Sstevel@tonic-gate 	while (pp->e_busy != ECPP_IDLE || qsize(WR(q))) {
11567c478bd9Sstevel@tonic-gate 		if (!cv_wait_sig(&pp->pport_cv, &pp->umutex)) {
11577c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "ecpp_close:B: received SIG\n");
11587c478bd9Sstevel@tonic-gate 			/*
11597c478bd9Sstevel@tonic-gate 			 * Returning from a signal such as
11607c478bd9Sstevel@tonic-gate 			 * SIGTERM or SIGKILL
11617c478bd9Sstevel@tonic-gate 			 */
11627c478bd9Sstevel@tonic-gate 			ecpp_flush(pp, FWRITE);
11637c478bd9Sstevel@tonic-gate 			break;
11647c478bd9Sstevel@tonic-gate 		} else {
11657c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "ecpp_close:rcvd cv-sig\n");
11667c478bd9Sstevel@tonic-gate 		}
11677c478bd9Sstevel@tonic-gate 	}
11687c478bd9Sstevel@tonic-gate 
11697c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecpp_close: joblen=%d, ctx_cf=%d, "
117019397407SSherry Moore 	    "qsize(WR(q))=%d, qsize(RD(q))=%d\n",
117119397407SSherry Moore 	    pp->joblen, pp->ctx_cf, qsize(pp->writeq), qsize(q));
11727c478bd9Sstevel@tonic-gate 
11737c478bd9Sstevel@tonic-gate 	/*
11747c478bd9Sstevel@tonic-gate 	 * Cancel all timeouts, disable interrupts
11757c478bd9Sstevel@tonic-gate 	 *
11767c478bd9Sstevel@tonic-gate 	 * Note that we can`t call untimeout(9F) with mutex held:
11777c478bd9Sstevel@tonic-gate 	 * callout may be blocked on the same mutex, and untimeout() will
11787c478bd9Sstevel@tonic-gate 	 * cv_wait() while callout is executing, thus creating a deadlock
11797c478bd9Sstevel@tonic-gate 	 * So we zero the timeout id's inside mutex and call untimeout later
11807c478bd9Sstevel@tonic-gate 	 */
11817c478bd9Sstevel@tonic-gate 	timeout_id = pp->timeout_id;
11827c478bd9Sstevel@tonic-gate 	fifo_timer_id = pp->fifo_timer_id;
11837c478bd9Sstevel@tonic-gate 	wsrv_timer_id = pp->wsrv_timer_id;
11847c478bd9Sstevel@tonic-gate 
11857c478bd9Sstevel@tonic-gate 	pp->timeout_id = pp->fifo_timer_id = pp->wsrv_timer_id = 0;
11867c478bd9Sstevel@tonic-gate 
11877c478bd9Sstevel@tonic-gate 	pp->softintr_pending = 0;
11887c478bd9Sstevel@tonic-gate 	pp->dma_cancelled = TRUE;
11897c478bd9Sstevel@tonic-gate 	ECPP_MASK_INTR(pp);
11907c478bd9Sstevel@tonic-gate 
11917c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->umutex);
11927c478bd9Sstevel@tonic-gate 
11937c478bd9Sstevel@tonic-gate 	qprocsoff(q);
11947c478bd9Sstevel@tonic-gate 
11957c478bd9Sstevel@tonic-gate 	if (timeout_id) {
11967c478bd9Sstevel@tonic-gate 		(void) untimeout(timeout_id);
11977c478bd9Sstevel@tonic-gate 	}
11987c478bd9Sstevel@tonic-gate 	if (fifo_timer_id) {
11997c478bd9Sstevel@tonic-gate 		(void) untimeout(fifo_timer_id);
12007c478bd9Sstevel@tonic-gate 	}
12017c478bd9Sstevel@tonic-gate 	if (wsrv_timer_id) {
12027c478bd9Sstevel@tonic-gate 		(void) untimeout(wsrv_timer_id);
12037c478bd9Sstevel@tonic-gate 	}
12047c478bd9Sstevel@tonic-gate 
12057c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->umutex);
12067c478bd9Sstevel@tonic-gate 
12077c478bd9Sstevel@tonic-gate 	/* set link to Compatible mode */
12087c478bd9Sstevel@tonic-gate 	if ((pp->current_mode == ECPP_ECP_MODE) &&
12097c478bd9Sstevel@tonic-gate 	    (pp->current_phase != ECPP_PHASE_ECP_FWD_IDLE)) {
12107c478bd9Sstevel@tonic-gate 		(void) ecp_reverse2forward(pp);
12117c478bd9Sstevel@tonic-gate 	}
12127c478bd9Sstevel@tonic-gate 
12137c478bd9Sstevel@tonic-gate 	(void) ecpp_1284_termination(pp);
12147c478bd9Sstevel@tonic-gate 
12157c478bd9Sstevel@tonic-gate 	pp->oflag = FALSE;
12167c478bd9Sstevel@tonic-gate 	q->q_ptr = WR(q)->q_ptr = NULL;
12177c478bd9Sstevel@tonic-gate 	pp->readq = pp->writeq = NULL;
12187c478bd9Sstevel@tonic-gate 	pp->msg = NULL;
12197c478bd9Sstevel@tonic-gate 
12207c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecpp_close: ecr=%x, dsr=%x, dcr=%x\n",
122119397407SSherry Moore 	    ECR_READ(pp), DSR_READ(pp), DCR_READ(pp));
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->umutex);
12247c478bd9Sstevel@tonic-gate 
12257c478bd9Sstevel@tonic-gate 	return (0);
12267c478bd9Sstevel@tonic-gate }
12277c478bd9Sstevel@tonic-gate 
12287c478bd9Sstevel@tonic-gate /*
12297c478bd9Sstevel@tonic-gate  * standard put procedure for ecpp
12307c478bd9Sstevel@tonic-gate  */
12317c478bd9Sstevel@tonic-gate static int
ecpp_wput(queue_t * q,mblk_t * mp)12327c478bd9Sstevel@tonic-gate ecpp_wput(queue_t *q, mblk_t *mp)
12337c478bd9Sstevel@tonic-gate {
12347c478bd9Sstevel@tonic-gate 	struct msgb *nmp;
12357c478bd9Sstevel@tonic-gate 	struct ecppunit *pp;
12367c478bd9Sstevel@tonic-gate 
12377c478bd9Sstevel@tonic-gate 	pp = (struct ecppunit *)q->q_ptr;
12387c478bd9Sstevel@tonic-gate 
12397c478bd9Sstevel@tonic-gate 	if (!mp) {
12407c478bd9Sstevel@tonic-gate 		return (0);
12417c478bd9Sstevel@tonic-gate 	}
12427c478bd9Sstevel@tonic-gate 
12437c478bd9Sstevel@tonic-gate 	if ((mp->b_wptr - mp->b_rptr) <= 0) {
12447c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
124519397407SSherry Moore 		    "ecpp_wput:bogus packet recieved mp=%x\n", mp);
12467c478bd9Sstevel@tonic-gate 		freemsg(mp);
12477c478bd9Sstevel@tonic-gate 		return (0);
12487c478bd9Sstevel@tonic-gate 	}
12497c478bd9Sstevel@tonic-gate 
12507c478bd9Sstevel@tonic-gate 	switch (DB_TYPE(mp)) {
12517c478bd9Sstevel@tonic-gate 	case M_DATA:
12527c478bd9Sstevel@tonic-gate 		/*
12537c478bd9Sstevel@tonic-gate 		 * This is a quick fix for multiple message block problem,
12547c478bd9Sstevel@tonic-gate 		 * it will be changed later with better performance code.
12557c478bd9Sstevel@tonic-gate 		 */
12567c478bd9Sstevel@tonic-gate 		if (mp->b_cont) {
12577c478bd9Sstevel@tonic-gate 			/*
12587c478bd9Sstevel@tonic-gate 			 * mblk has scattered data ... do msgpullup
12597c478bd9Sstevel@tonic-gate 			 * if it fails, continue with the current mblk
12607c478bd9Sstevel@tonic-gate 			 */
12617c478bd9Sstevel@tonic-gate 			if ((nmp = msgpullup(mp, -1)) != NULL) {
12627c478bd9Sstevel@tonic-gate 				freemsg(mp);
12637c478bd9Sstevel@tonic-gate 				mp = nmp;
12647c478bd9Sstevel@tonic-gate 				ecpp_error(pp->dip,
12657c478bd9Sstevel@tonic-gate 				    "ecpp_wput:msgpullup: mp=%p len=%d\n",
12667c478bd9Sstevel@tonic-gate 				    mp, mp->b_wptr - mp->b_rptr);
12677c478bd9Sstevel@tonic-gate 			}
12687c478bd9Sstevel@tonic-gate 		}
12697c478bd9Sstevel@tonic-gate 
12707c478bd9Sstevel@tonic-gate 		/* let ecpp_wsrv() concatenate small blocks */
12717c478bd9Sstevel@tonic-gate 		(void) putq(q, mp);
12727c478bd9Sstevel@tonic-gate 
12737c478bd9Sstevel@tonic-gate 		break;
12747c478bd9Sstevel@tonic-gate 
12757c478bd9Sstevel@tonic-gate 	case M_CTL:
12767c478bd9Sstevel@tonic-gate 		(void) putq(q, mp);
12777c478bd9Sstevel@tonic-gate 
12787c478bd9Sstevel@tonic-gate 		break;
12797c478bd9Sstevel@tonic-gate 
12807c478bd9Sstevel@tonic-gate 	case M_IOCTL: {
12817c478bd9Sstevel@tonic-gate 		struct iocblk *iocbp;
12827c478bd9Sstevel@tonic-gate 
12837c478bd9Sstevel@tonic-gate 		iocbp = (struct iocblk *)mp->b_rptr;
12847c478bd9Sstevel@tonic-gate 
12857c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_wput:M_IOCTL %x\n", iocbp->ioc_cmd);
12867c478bd9Sstevel@tonic-gate 
12877c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
12887c478bd9Sstevel@tonic-gate 
12897c478bd9Sstevel@tonic-gate 		/* TESTIO and GET_STATUS can be used during transfer */
12907c478bd9Sstevel@tonic-gate 		if ((pp->e_busy == ECPP_BUSY) &&
12917c478bd9Sstevel@tonic-gate 		    (iocbp->ioc_cmd != BPPIOC_TESTIO) &&
12927c478bd9Sstevel@tonic-gate 		    (iocbp->ioc_cmd != PRNIOC_GET_STATUS)) {
12937c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
12947c478bd9Sstevel@tonic-gate 			(void) putq(q, mp);
12957c478bd9Sstevel@tonic-gate 		} else {
12967c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
12977c478bd9Sstevel@tonic-gate 			ecpp_putioc(q, mp);
12987c478bd9Sstevel@tonic-gate 		}
12997c478bd9Sstevel@tonic-gate 
13007c478bd9Sstevel@tonic-gate 		break;
13017c478bd9Sstevel@tonic-gate 	}
13027c478bd9Sstevel@tonic-gate 
13037c478bd9Sstevel@tonic-gate 	case M_IOCDATA: {
13047c478bd9Sstevel@tonic-gate 		struct copyresp *csp;
13057c478bd9Sstevel@tonic-gate 
13067c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_wput:M_IOCDATA\n");
13077c478bd9Sstevel@tonic-gate 
13087c478bd9Sstevel@tonic-gate 		csp = (struct copyresp *)mp->b_rptr;
13097c478bd9Sstevel@tonic-gate 
13107c478bd9Sstevel@tonic-gate 		/*
13117c478bd9Sstevel@tonic-gate 		 * If copy request failed, quit now
13127c478bd9Sstevel@tonic-gate 		 */
13137c478bd9Sstevel@tonic-gate 		if (csp->cp_rval != 0) {
13147c478bd9Sstevel@tonic-gate 			freemsg(mp);
13157c478bd9Sstevel@tonic-gate 			return (0);
13167c478bd9Sstevel@tonic-gate 		}
13177c478bd9Sstevel@tonic-gate 
13187c478bd9Sstevel@tonic-gate 		switch (csp->cp_cmd) {
13197c478bd9Sstevel@tonic-gate 		case ECPPIOC_SETPARMS:
13207c478bd9Sstevel@tonic-gate 		case ECPPIOC_SETREGS:
13217c478bd9Sstevel@tonic-gate 		case ECPPIOC_SETPORT:
13227c478bd9Sstevel@tonic-gate 		case ECPPIOC_SETDATA:
13237c478bd9Sstevel@tonic-gate 		case PRNIOC_SET_IFCAP:
13247c478bd9Sstevel@tonic-gate 		case PRNIOC_SET_TIMEOUTS:
13257c478bd9Sstevel@tonic-gate 			/*
13267c478bd9Sstevel@tonic-gate 			 * need to retrieve and use the data, but if the
13277c478bd9Sstevel@tonic-gate 			 * device is busy, wait.
13287c478bd9Sstevel@tonic-gate 			 */
13297c478bd9Sstevel@tonic-gate 			(void) putq(q, mp);
13307c478bd9Sstevel@tonic-gate 			break;
13317c478bd9Sstevel@tonic-gate 
13327c478bd9Sstevel@tonic-gate 		case ECPPIOC_GETPARMS:
13337c478bd9Sstevel@tonic-gate 		case ECPPIOC_GETREGS:
13347c478bd9Sstevel@tonic-gate 		case ECPPIOC_GETPORT:
13357c478bd9Sstevel@tonic-gate 		case ECPPIOC_GETDATA:
13367c478bd9Sstevel@tonic-gate 		case BPPIOC_GETERR:
13377c478bd9Sstevel@tonic-gate 		case BPPIOC_TESTIO:
13387c478bd9Sstevel@tonic-gate 		case PRNIOC_GET_IFCAP:
13397c478bd9Sstevel@tonic-gate 		case PRNIOC_GET_STATUS:
13407c478bd9Sstevel@tonic-gate 		case PRNIOC_GET_1284_STATUS:
13417c478bd9Sstevel@tonic-gate 		case PRNIOC_GET_TIMEOUTS:
13427c478bd9Sstevel@tonic-gate 			/* data transfered to user space okay */
13437c478bd9Sstevel@tonic-gate 			ecpp_ack_ioctl(q, mp);
13447c478bd9Sstevel@tonic-gate 			break;
13457c478bd9Sstevel@tonic-gate 
13467c478bd9Sstevel@tonic-gate 		case ECPPIOC_GETDEVID:
13477c478bd9Sstevel@tonic-gate 			ecpp_wput_iocdata_devid(q, mp,
134819397407SSherry Moore 			    offsetof(struct ecpp_device_id, rlen));
13497c478bd9Sstevel@tonic-gate 			break;
13507c478bd9Sstevel@tonic-gate 
13517c478bd9Sstevel@tonic-gate 		case PRNIOC_GET_1284_DEVID:
13527c478bd9Sstevel@tonic-gate 			ecpp_wput_iocdata_devid(q, mp,
135319397407SSherry Moore 			    offsetof(struct prn_1284_device_id, id_rlen));
13547c478bd9Sstevel@tonic-gate 			break;
13557c478bd9Sstevel@tonic-gate 
13567c478bd9Sstevel@tonic-gate 		case PRNIOC_GET_IFINFO:
13577c478bd9Sstevel@tonic-gate 			ecpp_wput_iocdata_devid(q, mp,
135819397407SSherry Moore 			    offsetof(struct prn_interface_info, if_rlen));
13597c478bd9Sstevel@tonic-gate 			break;
13607c478bd9Sstevel@tonic-gate 
13617c478bd9Sstevel@tonic-gate 		default:
13627c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
13637c478bd9Sstevel@tonic-gate 			break;
13647c478bd9Sstevel@tonic-gate 		}
13657c478bd9Sstevel@tonic-gate 
13667c478bd9Sstevel@tonic-gate 		break;
13677c478bd9Sstevel@tonic-gate 	}
13687c478bd9Sstevel@tonic-gate 
13697c478bd9Sstevel@tonic-gate 	case M_FLUSH:
13707c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_wput:M_FLUSH\n");
13717c478bd9Sstevel@tonic-gate 
13727c478bd9Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHW) {
13737c478bd9Sstevel@tonic-gate 			mutex_enter(&pp->umutex);
13747c478bd9Sstevel@tonic-gate 			ecpp_flush(pp, FWRITE);
13757c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
13767c478bd9Sstevel@tonic-gate 		}
13777c478bd9Sstevel@tonic-gate 
13787c478bd9Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHR) {
13797c478bd9Sstevel@tonic-gate 			mutex_enter(&pp->umutex);
13807c478bd9Sstevel@tonic-gate 			ecpp_flush(pp, FREAD);
13817c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
13827c478bd9Sstevel@tonic-gate 			qreply(q, mp);
13837c478bd9Sstevel@tonic-gate 		} else {
13847c478bd9Sstevel@tonic-gate 			freemsg(mp);
13857c478bd9Sstevel@tonic-gate 		}
13867c478bd9Sstevel@tonic-gate 
13877c478bd9Sstevel@tonic-gate 		break;
13887c478bd9Sstevel@tonic-gate 
13897c478bd9Sstevel@tonic-gate 	case M_READ:
13907c478bd9Sstevel@tonic-gate 		/*
13917c478bd9Sstevel@tonic-gate 		 * When the user calls read(2), M_READ message is sent to us,
13927c478bd9Sstevel@tonic-gate 		 * first byte of which is the number of requested bytes
13937c478bd9Sstevel@tonic-gate 		 * We add up user requests and use resulting number
13947c478bd9Sstevel@tonic-gate 		 * to calculate the reverse transfer block size
13957c478bd9Sstevel@tonic-gate 		 */
13967c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
13977c478bd9Sstevel@tonic-gate 		if (pp->e_busy == ECPP_IDLE) {
13987c478bd9Sstevel@tonic-gate 			pp->nread += *(size_t *)mp->b_rptr;
13997c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "ecpp_wput: M_READ %d", pp->nread);
14007c478bd9Sstevel@tonic-gate 			freemsg(mp);
14017c478bd9Sstevel@tonic-gate 		} else {
14027c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "ecpp_wput: M_READ queueing");
14037c478bd9Sstevel@tonic-gate 			(void) putq(q, mp);
14047c478bd9Sstevel@tonic-gate 		}
14057c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
14067c478bd9Sstevel@tonic-gate 		break;
14077c478bd9Sstevel@tonic-gate 
14087c478bd9Sstevel@tonic-gate 	default:
14097c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_wput: bad messagetype 0x%x\n",
14107c478bd9Sstevel@tonic-gate 		    DB_TYPE(mp));
14117c478bd9Sstevel@tonic-gate 		freemsg(mp);
14127c478bd9Sstevel@tonic-gate 		break;
14137c478bd9Sstevel@tonic-gate 	}
14147c478bd9Sstevel@tonic-gate 
14157c478bd9Sstevel@tonic-gate 	return (0);
14167c478bd9Sstevel@tonic-gate }
14177c478bd9Sstevel@tonic-gate 
14187c478bd9Sstevel@tonic-gate /*
14197c478bd9Sstevel@tonic-gate  * Process ECPPIOC_GETDEVID-like ioctls
14207c478bd9Sstevel@tonic-gate  */
14217c478bd9Sstevel@tonic-gate static void
ecpp_wput_iocdata_devid(queue_t * q,mblk_t * mp,uintptr_t rlen_offset)14227c478bd9Sstevel@tonic-gate ecpp_wput_iocdata_devid(queue_t *q, mblk_t *mp, uintptr_t rlen_offset)
14237c478bd9Sstevel@tonic-gate {
14247c478bd9Sstevel@tonic-gate 	struct copyresp		*csp;
14257c478bd9Sstevel@tonic-gate 	struct ecpp_copystate	*stp;
14267c478bd9Sstevel@tonic-gate 	mblk_t			*datamp;
14277c478bd9Sstevel@tonic-gate 
14287c478bd9Sstevel@tonic-gate 	csp = (struct copyresp *)mp->b_rptr;
14297c478bd9Sstevel@tonic-gate 	stp = (struct ecpp_copystate *)csp->cp_private->b_rptr;
14307c478bd9Sstevel@tonic-gate 
14317c478bd9Sstevel@tonic-gate 	/* determine the state of copyin/copyout process */
14327c478bd9Sstevel@tonic-gate 	switch (stp->state) {
14337c478bd9Sstevel@tonic-gate 	case ECPP_STRUCTIN:
14347c478bd9Sstevel@tonic-gate 		/* user structure has arrived */
14357c478bd9Sstevel@tonic-gate 		(void) putq(q, mp);
14367c478bd9Sstevel@tonic-gate 		break;
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate 	case ECPP_ADDROUT:
14397c478bd9Sstevel@tonic-gate 		/*
14407c478bd9Sstevel@tonic-gate 		 * data transfered to user space okay
14417c478bd9Sstevel@tonic-gate 		 * now update user structure
14427c478bd9Sstevel@tonic-gate 		 */
14437c478bd9Sstevel@tonic-gate 		datamp = allocb(sizeof (int), BPRI_MED);
14447c478bd9Sstevel@tonic-gate 		if (datamp == NULL) {
14457c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, ENOSR);
14467c478bd9Sstevel@tonic-gate 			break;
14477c478bd9Sstevel@tonic-gate 		}
14487c478bd9Sstevel@tonic-gate 
14497c478bd9Sstevel@tonic-gate 		*(int *)datamp->b_rptr =
145019397407SSherry Moore 		    *(int *)((char *)&stp->un + rlen_offset);
14517c478bd9Sstevel@tonic-gate 		stp->state = ECPP_STRUCTOUT;
14527c478bd9Sstevel@tonic-gate 
14537c478bd9Sstevel@tonic-gate 		mcopyout(mp, csp->cp_private, sizeof (int),
145419397407SSherry Moore 		    (char *)stp->uaddr + rlen_offset, datamp);
14557c478bd9Sstevel@tonic-gate 		qreply(q, mp);
14567c478bd9Sstevel@tonic-gate 		break;
14577c478bd9Sstevel@tonic-gate 
14587c478bd9Sstevel@tonic-gate 	case ECPP_STRUCTOUT:
14597c478bd9Sstevel@tonic-gate 		/* user structure was updated okay */
14607c478bd9Sstevel@tonic-gate 		freemsg(csp->cp_private);
14617c478bd9Sstevel@tonic-gate 		ecpp_ack_ioctl(q, mp);
14627c478bd9Sstevel@tonic-gate 		break;
14637c478bd9Sstevel@tonic-gate 
14647c478bd9Sstevel@tonic-gate 	default:
14657c478bd9Sstevel@tonic-gate 		ecpp_nack_ioctl(q, mp, EINVAL);
14667c478bd9Sstevel@tonic-gate 		break;
14677c478bd9Sstevel@tonic-gate 	}
14687c478bd9Sstevel@tonic-gate }
14697c478bd9Sstevel@tonic-gate 
14707c478bd9Sstevel@tonic-gate static uchar_t
ecpp_get_error_status(uchar_t status)14717c478bd9Sstevel@tonic-gate ecpp_get_error_status(uchar_t status)
14727c478bd9Sstevel@tonic-gate {
14737c478bd9Sstevel@tonic-gate 	uchar_t pin_status = 0;
14747c478bd9Sstevel@tonic-gate 
14757c478bd9Sstevel@tonic-gate 	if (!(status & ECPP_nERR)) {
14767c478bd9Sstevel@tonic-gate 		pin_status |= BPP_ERR_ERR;
14777c478bd9Sstevel@tonic-gate 	}
14787c478bd9Sstevel@tonic-gate 
14797c478bd9Sstevel@tonic-gate 	if (status & ECPP_PE) {
14807c478bd9Sstevel@tonic-gate 		pin_status |= BPP_PE_ERR;
14817c478bd9Sstevel@tonic-gate 	}
14827c478bd9Sstevel@tonic-gate 
14837c478bd9Sstevel@tonic-gate 	if (!(status & ECPP_SLCT)) {
14847c478bd9Sstevel@tonic-gate 		pin_status |= BPP_SLCT_ERR;
14857c478bd9Sstevel@tonic-gate 	}
14867c478bd9Sstevel@tonic-gate 
14877c478bd9Sstevel@tonic-gate 	if (!(status & ECPP_nBUSY)) {
14887c478bd9Sstevel@tonic-gate 		pin_status |= BPP_SLCT_ERR;
14897c478bd9Sstevel@tonic-gate 	}
14907c478bd9Sstevel@tonic-gate 
14917c478bd9Sstevel@tonic-gate 	return (pin_status);
14927c478bd9Sstevel@tonic-gate }
14937c478bd9Sstevel@tonic-gate 
14947c478bd9Sstevel@tonic-gate /*
14957c478bd9Sstevel@tonic-gate  * ioctl handler for output PUT procedure.
14967c478bd9Sstevel@tonic-gate  */
14977c478bd9Sstevel@tonic-gate static void
ecpp_putioc(queue_t * q,mblk_t * mp)14987c478bd9Sstevel@tonic-gate ecpp_putioc(queue_t *q, mblk_t *mp)
14997c478bd9Sstevel@tonic-gate {
15007c478bd9Sstevel@tonic-gate 	struct iocblk	*iocbp;
15017c478bd9Sstevel@tonic-gate 	struct ecppunit *pp;
15027c478bd9Sstevel@tonic-gate 
15037c478bd9Sstevel@tonic-gate 	pp = (struct ecppunit *)q->q_ptr;
15047c478bd9Sstevel@tonic-gate 
15057c478bd9Sstevel@tonic-gate 	iocbp = (struct iocblk *)mp->b_rptr;
15067c478bd9Sstevel@tonic-gate 
15077c478bd9Sstevel@tonic-gate 	/* I_STR ioctls are invalid */
15087c478bd9Sstevel@tonic-gate 	if (iocbp->ioc_count != TRANSPARENT) {
15097c478bd9Sstevel@tonic-gate 		ecpp_nack_ioctl(q, mp, EINVAL);
15107c478bd9Sstevel@tonic-gate 		return;
15117c478bd9Sstevel@tonic-gate 	}
15127c478bd9Sstevel@tonic-gate 
15137c478bd9Sstevel@tonic-gate 	switch (iocbp->ioc_cmd) {
15147c478bd9Sstevel@tonic-gate 	case ECPPIOC_SETPARMS: {
15157c478bd9Sstevel@tonic-gate 		mcopyin(mp, NULL, sizeof (struct ecpp_transfer_parms), NULL);
15167c478bd9Sstevel@tonic-gate 		qreply(q, mp);
15177c478bd9Sstevel@tonic-gate 		break;
15187c478bd9Sstevel@tonic-gate 	}
15197c478bd9Sstevel@tonic-gate 
15207c478bd9Sstevel@tonic-gate 	case ECPPIOC_GETPARMS: {
15217c478bd9Sstevel@tonic-gate 		struct ecpp_transfer_parms xfer_parms;
15227c478bd9Sstevel@tonic-gate 
15237c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
15247c478bd9Sstevel@tonic-gate 
15257c478bd9Sstevel@tonic-gate 		pp->xfer_parms.mode = pp->current_mode;
15267c478bd9Sstevel@tonic-gate 		xfer_parms = pp->xfer_parms;
15277c478bd9Sstevel@tonic-gate 
15287c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
15297c478bd9Sstevel@tonic-gate 
15307c478bd9Sstevel@tonic-gate 		ecpp_putioc_copyout(q, mp, &xfer_parms, sizeof (xfer_parms));
15317c478bd9Sstevel@tonic-gate 		break;
15327c478bd9Sstevel@tonic-gate 	}
15337c478bd9Sstevel@tonic-gate 
15347c478bd9Sstevel@tonic-gate 	case ECPPIOC_SETREGS: {
15357c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
15367c478bd9Sstevel@tonic-gate 		if (pp->current_mode != ECPP_DIAG_MODE) {
15377c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
15387c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
15397c478bd9Sstevel@tonic-gate 			break;
15407c478bd9Sstevel@tonic-gate 		}
15417c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
15427c478bd9Sstevel@tonic-gate 
15437c478bd9Sstevel@tonic-gate 		mcopyin(mp, NULL, sizeof (struct ecpp_regs), NULL);
15447c478bd9Sstevel@tonic-gate 		qreply(q, mp);
15457c478bd9Sstevel@tonic-gate 		break;
15467c478bd9Sstevel@tonic-gate 	}
15477c478bd9Sstevel@tonic-gate 
15487c478bd9Sstevel@tonic-gate 	case ECPPIOC_GETREGS: {
15497c478bd9Sstevel@tonic-gate 		struct ecpp_regs rg;
15507c478bd9Sstevel@tonic-gate 
15517c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
15527c478bd9Sstevel@tonic-gate 
15537c478bd9Sstevel@tonic-gate 		if (pp->current_mode != ECPP_DIAG_MODE) {
15547c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
15557c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
15567c478bd9Sstevel@tonic-gate 			break;
15577c478bd9Sstevel@tonic-gate 		}
15587c478bd9Sstevel@tonic-gate 
15597c478bd9Sstevel@tonic-gate 		rg.dsr = DSR_READ(pp);
15607c478bd9Sstevel@tonic-gate 		rg.dcr = DCR_READ(pp);
15617c478bd9Sstevel@tonic-gate 
15627c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
15637c478bd9Sstevel@tonic-gate 
15647c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ECPPIOC_GETREGS: dsr=%x,dcr=%x\n",
156519397407SSherry Moore 		    rg.dsr, rg.dcr);
15667c478bd9Sstevel@tonic-gate 
15677c478bd9Sstevel@tonic-gate 		/* these bits must be 1 */
15687c478bd9Sstevel@tonic-gate 		rg.dsr |= ECPP_SETREGS_DSR_MASK;
15697c478bd9Sstevel@tonic-gate 		rg.dcr |= ECPP_SETREGS_DCR_MASK;
15707c478bd9Sstevel@tonic-gate 
15717c478bd9Sstevel@tonic-gate 		ecpp_putioc_copyout(q, mp, &rg, sizeof (rg));
15727c478bd9Sstevel@tonic-gate 		break;
15737c478bd9Sstevel@tonic-gate 	}
15747c478bd9Sstevel@tonic-gate 
15757c478bd9Sstevel@tonic-gate 	case ECPPIOC_SETPORT:
15767c478bd9Sstevel@tonic-gate 	case ECPPIOC_SETDATA: {
15777c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
15787c478bd9Sstevel@tonic-gate 		if (pp->current_mode != ECPP_DIAG_MODE) {
15797c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
15807c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
15817c478bd9Sstevel@tonic-gate 			break;
15827c478bd9Sstevel@tonic-gate 		}
15837c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
15847c478bd9Sstevel@tonic-gate 
15857c478bd9Sstevel@tonic-gate 		/*
15867c478bd9Sstevel@tonic-gate 		 * each of the commands fetches a byte quantity.
15877c478bd9Sstevel@tonic-gate 		 */
15887c478bd9Sstevel@tonic-gate 		mcopyin(mp, NULL, sizeof (uchar_t), NULL);
15897c478bd9Sstevel@tonic-gate 		qreply(q, mp);
15907c478bd9Sstevel@tonic-gate 		break;
15917c478bd9Sstevel@tonic-gate 	}
15927c478bd9Sstevel@tonic-gate 
15937c478bd9Sstevel@tonic-gate 	case ECPPIOC_GETDATA:
15947c478bd9Sstevel@tonic-gate 	case ECPPIOC_GETPORT: {
15957c478bd9Sstevel@tonic-gate 		uchar_t	byte;
15967c478bd9Sstevel@tonic-gate 
15977c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
15987c478bd9Sstevel@tonic-gate 
15997c478bd9Sstevel@tonic-gate 		/* must be in diagnostic mode for these commands to work */
16007c478bd9Sstevel@tonic-gate 		if (pp->current_mode != ECPP_DIAG_MODE) {
16017c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
16027c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
16037c478bd9Sstevel@tonic-gate 			break;
16047c478bd9Sstevel@tonic-gate 		}
16057c478bd9Sstevel@tonic-gate 
16067c478bd9Sstevel@tonic-gate 		if (iocbp->ioc_cmd == ECPPIOC_GETPORT) {
16077c478bd9Sstevel@tonic-gate 			byte = pp->port;
16087c478bd9Sstevel@tonic-gate 		} else if (iocbp->ioc_cmd == ECPPIOC_GETDATA) {
16097c478bd9Sstevel@tonic-gate 			switch (pp->port) {
16107c478bd9Sstevel@tonic-gate 			case ECPP_PORT_PIO:
16117c478bd9Sstevel@tonic-gate 				byte = DATAR_READ(pp);
16127c478bd9Sstevel@tonic-gate 				break;
16137c478bd9Sstevel@tonic-gate 			case ECPP_PORT_TDMA:
16147c478bd9Sstevel@tonic-gate 				byte = TFIFO_READ(pp);
16157c478bd9Sstevel@tonic-gate 				ecpp_error(pp->dip, "GETDATA=0x%x\n", byte);
16167c478bd9Sstevel@tonic-gate 				break;
16177c478bd9Sstevel@tonic-gate 			default:
16187c478bd9Sstevel@tonic-gate 				ecpp_nack_ioctl(q, mp, EINVAL);
16197c478bd9Sstevel@tonic-gate 				break;
16207c478bd9Sstevel@tonic-gate 			}
16217c478bd9Sstevel@tonic-gate 		} else {
16227c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
16237c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "weird command");
16247c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
16257c478bd9Sstevel@tonic-gate 			break;
16267c478bd9Sstevel@tonic-gate 		}
16277c478bd9Sstevel@tonic-gate 
16287c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
16297c478bd9Sstevel@tonic-gate 
16307c478bd9Sstevel@tonic-gate 		ecpp_putioc_copyout(q, mp, &byte, sizeof (byte));
16317c478bd9Sstevel@tonic-gate 
16327c478bd9Sstevel@tonic-gate 		break;
16337c478bd9Sstevel@tonic-gate 	}
16347c478bd9Sstevel@tonic-gate 
16357c478bd9Sstevel@tonic-gate 	case BPPIOC_GETERR: {
16367c478bd9Sstevel@tonic-gate 		struct bpp_error_status bpp_status;
16377c478bd9Sstevel@tonic-gate 
16387c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
16397c478bd9Sstevel@tonic-gate 
16407c478bd9Sstevel@tonic-gate 		bpp_status.timeout_occurred = pp->timeout_error;
16417c478bd9Sstevel@tonic-gate 		bpp_status.bus_error = 0;	/* not used */
16427c478bd9Sstevel@tonic-gate 		bpp_status.pin_status = ecpp_get_error_status(pp->saved_dsr);
16437c478bd9Sstevel@tonic-gate 
16447c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
16457c478bd9Sstevel@tonic-gate 
16467c478bd9Sstevel@tonic-gate 		ecpp_putioc_copyout(q, mp, &bpp_status, sizeof (bpp_status));
16477c478bd9Sstevel@tonic-gate 
16487c478bd9Sstevel@tonic-gate 		break;
16497c478bd9Sstevel@tonic-gate 	}
16507c478bd9Sstevel@tonic-gate 
16517c478bd9Sstevel@tonic-gate 	case BPPIOC_TESTIO: {
16527c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
16537c478bd9Sstevel@tonic-gate 
16547c478bd9Sstevel@tonic-gate 		if (!((pp->current_mode == ECPP_CENTRONICS) ||
165519397407SSherry Moore 		    (pp->current_mode == ECPP_COMPAT_MODE))) {
16567c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
16577c478bd9Sstevel@tonic-gate 		} else {
16587c478bd9Sstevel@tonic-gate 			pp->saved_dsr = DSR_READ(pp);
16597c478bd9Sstevel@tonic-gate 
16607c478bd9Sstevel@tonic-gate 			if ((pp->saved_dsr & ECPP_PE) ||
16617c478bd9Sstevel@tonic-gate 			    !(pp->saved_dsr & ECPP_SLCT) ||
16627c478bd9Sstevel@tonic-gate 			    !(pp->saved_dsr & ECPP_nERR)) {
16637c478bd9Sstevel@tonic-gate 				ecpp_nack_ioctl(q, mp, EIO);
16647c478bd9Sstevel@tonic-gate 			} else {
16657c478bd9Sstevel@tonic-gate 				ecpp_ack_ioctl(q, mp);
16667c478bd9Sstevel@tonic-gate 			}
16677c478bd9Sstevel@tonic-gate 		}
16687c478bd9Sstevel@tonic-gate 
16697c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
16707c478bd9Sstevel@tonic-gate 
16717c478bd9Sstevel@tonic-gate 		break;
16727c478bd9Sstevel@tonic-gate 	}
16737c478bd9Sstevel@tonic-gate 
16747c478bd9Sstevel@tonic-gate 	case PRNIOC_RESET:
16757c478bd9Sstevel@tonic-gate 		/*
16767c478bd9Sstevel@tonic-gate 		 * Initialize interface only if no transfer is in progress
16777c478bd9Sstevel@tonic-gate 		 */
16787c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
16797c478bd9Sstevel@tonic-gate 		if (pp->e_busy == ECPP_BUSY) {
16807c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
16817c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EIO);
16827c478bd9Sstevel@tonic-gate 		} else {
16837c478bd9Sstevel@tonic-gate 			(void) ecpp_mode_negotiation(pp, ECPP_CENTRONICS);
16847c478bd9Sstevel@tonic-gate 
16857c478bd9Sstevel@tonic-gate 			DCR_WRITE(pp, ECPP_SLCTIN);
16867c478bd9Sstevel@tonic-gate 			drv_usecwait(2);
16877c478bd9Sstevel@tonic-gate 			DCR_WRITE(pp, ECPP_SLCTIN | ECPP_nINIT);
16887c478bd9Sstevel@tonic-gate 
16897c478bd9Sstevel@tonic-gate 			ecpp_default_negotiation(pp);
16907c478bd9Sstevel@tonic-gate 
16917c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
16927c478bd9Sstevel@tonic-gate 			ecpp_ack_ioctl(q, mp);
16937c478bd9Sstevel@tonic-gate 		}
16947c478bd9Sstevel@tonic-gate 		break;
16957c478bd9Sstevel@tonic-gate 
16967c478bd9Sstevel@tonic-gate 	case PRNIOC_GET_IFCAP: {
16977c478bd9Sstevel@tonic-gate 		uint_t		ifcap;
16987c478bd9Sstevel@tonic-gate 
16997c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
17007c478bd9Sstevel@tonic-gate 
17017c478bd9Sstevel@tonic-gate 		ifcap = ecpp_get_prn_ifcap(pp);
17027c478bd9Sstevel@tonic-gate 
17037c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
17047c478bd9Sstevel@tonic-gate 
17057c478bd9Sstevel@tonic-gate 		ecpp_putioc_copyout(q, mp, &ifcap, sizeof (ifcap));
17067c478bd9Sstevel@tonic-gate 		break;
17077c478bd9Sstevel@tonic-gate 	}
17087c478bd9Sstevel@tonic-gate 
17097c478bd9Sstevel@tonic-gate 	case PRNIOC_SET_IFCAP: {
17107c478bd9Sstevel@tonic-gate 		mcopyin(mp, NULL, sizeof (uint_t), NULL);
17117c478bd9Sstevel@tonic-gate 		qreply(q, mp);
17127c478bd9Sstevel@tonic-gate 		break;
17137c478bd9Sstevel@tonic-gate 	}
17147c478bd9Sstevel@tonic-gate 
17157c478bd9Sstevel@tonic-gate 	case PRNIOC_GET_TIMEOUTS: {
17167c478bd9Sstevel@tonic-gate 		struct prn_timeouts timeouts;
17177c478bd9Sstevel@tonic-gate 
17187c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
17197c478bd9Sstevel@tonic-gate 		timeouts = pp->prn_timeouts;
17207c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
17217c478bd9Sstevel@tonic-gate 
17227c478bd9Sstevel@tonic-gate 		ecpp_putioc_copyout(q, mp, &timeouts, sizeof (timeouts));
17237c478bd9Sstevel@tonic-gate 
17247c478bd9Sstevel@tonic-gate 		break;
17257c478bd9Sstevel@tonic-gate 	}
17267c478bd9Sstevel@tonic-gate 
17277c478bd9Sstevel@tonic-gate 	case PRNIOC_SET_TIMEOUTS:
17287c478bd9Sstevel@tonic-gate 		mcopyin(mp, NULL, sizeof (struct prn_timeouts),
172919397407SSherry Moore 		    *(caddr_t *)(void *)mp->b_cont->b_rptr);
17307c478bd9Sstevel@tonic-gate 		qreply(q, mp);
17317c478bd9Sstevel@tonic-gate 		break;
17327c478bd9Sstevel@tonic-gate 
17337c478bd9Sstevel@tonic-gate 	case PRNIOC_GET_STATUS: {
17347c478bd9Sstevel@tonic-gate 		uint8_t	dsr;
17357c478bd9Sstevel@tonic-gate 		uint_t	status;
17367c478bd9Sstevel@tonic-gate 
17377c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
17387c478bd9Sstevel@tonic-gate 
17397c478bd9Sstevel@tonic-gate 		/* DSR only makes sense in Centronics & Compat mode */
17407c478bd9Sstevel@tonic-gate 		if (pp->current_mode == ECPP_CENTRONICS ||
17417c478bd9Sstevel@tonic-gate 		    pp->current_mode == ECPP_COMPAT_MODE) {
17427c478bd9Sstevel@tonic-gate 			dsr = DSR_READ(pp);
17437c478bd9Sstevel@tonic-gate 			if ((dsr & ECPP_PE) ||
17447c478bd9Sstevel@tonic-gate 			    !(dsr & ECPP_SLCT) || !(dsr & ECPP_nERR)) {
17457c478bd9Sstevel@tonic-gate 				status = PRN_ONLINE;
17467c478bd9Sstevel@tonic-gate 			} else {
17477c478bd9Sstevel@tonic-gate 				status = PRN_ONLINE | PRN_READY;
17487c478bd9Sstevel@tonic-gate 			}
17497c478bd9Sstevel@tonic-gate 		} else {
17507c478bd9Sstevel@tonic-gate 			status = PRN_ONLINE | PRN_READY;
17517c478bd9Sstevel@tonic-gate 		}
17527c478bd9Sstevel@tonic-gate 
17537c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
17547c478bd9Sstevel@tonic-gate 
17557c478bd9Sstevel@tonic-gate 		ecpp_putioc_copyout(q, mp, &status, sizeof (status));
17567c478bd9Sstevel@tonic-gate 		break;
17577c478bd9Sstevel@tonic-gate 	}
17587c478bd9Sstevel@tonic-gate 
17597c478bd9Sstevel@tonic-gate 	case PRNIOC_GET_1284_STATUS: {
17607c478bd9Sstevel@tonic-gate 		uint8_t	dsr;
17617c478bd9Sstevel@tonic-gate 		uchar_t	status;
17627c478bd9Sstevel@tonic-gate 
17637c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
17647c478bd9Sstevel@tonic-gate 
17657c478bd9Sstevel@tonic-gate 		/* status only makes sense in Centronics & Compat mode */
17667c478bd9Sstevel@tonic-gate 		if (pp->current_mode != ECPP_COMPAT_MODE &&
17677c478bd9Sstevel@tonic-gate 		    pp->current_mode != ECPP_CENTRONICS) {
17687c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
17697c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
17707c478bd9Sstevel@tonic-gate 			break;
17717c478bd9Sstevel@tonic-gate 		}
17727c478bd9Sstevel@tonic-gate 
17737c478bd9Sstevel@tonic-gate 		dsr = DSR_READ(pp);		/* read status */
17747c478bd9Sstevel@tonic-gate 
17757c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
17767c478bd9Sstevel@tonic-gate 
17777c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "PRNIOC_GET_STATUS: %x\n", dsr);
17787c478bd9Sstevel@tonic-gate 
17797c478bd9Sstevel@tonic-gate 		status = (dsr & (ECPP_SLCT | ECPP_PE | ECPP_nERR)) |
178019397407SSherry Moore 		    (~dsr & ECPP_nBUSY);
17817c478bd9Sstevel@tonic-gate 
17827c478bd9Sstevel@tonic-gate 		ecpp_putioc_copyout(q, mp, &status, sizeof (status));
17837c478bd9Sstevel@tonic-gate 		break;
17847c478bd9Sstevel@tonic-gate 	}
17857c478bd9Sstevel@tonic-gate 
17867c478bd9Sstevel@tonic-gate 	case ECPPIOC_GETDEVID:
17877c478bd9Sstevel@tonic-gate 		ecpp_putioc_stateful_copyin(q, mp,
178819397407SSherry Moore 		    sizeof (struct ecpp_device_id));
17897c478bd9Sstevel@tonic-gate 		break;
17907c478bd9Sstevel@tonic-gate 
17917c478bd9Sstevel@tonic-gate 	case PRNIOC_GET_1284_DEVID:
17927c478bd9Sstevel@tonic-gate 		ecpp_putioc_stateful_copyin(q, mp,
179319397407SSherry Moore 		    sizeof (struct prn_1284_device_id));
17947c478bd9Sstevel@tonic-gate 		break;
17957c478bd9Sstevel@tonic-gate 
17967c478bd9Sstevel@tonic-gate 	case PRNIOC_GET_IFINFO:
17977c478bd9Sstevel@tonic-gate 		ecpp_putioc_stateful_copyin(q, mp,
179819397407SSherry Moore 		    sizeof (struct prn_interface_info));
17997c478bd9Sstevel@tonic-gate 		break;
18007c478bd9Sstevel@tonic-gate 
18017c478bd9Sstevel@tonic-gate 	default:
18027c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "putioc: unknown IOCTL: %x\n",
180319397407SSherry Moore 		    iocbp->ioc_cmd);
18047c478bd9Sstevel@tonic-gate 		ecpp_nack_ioctl(q, mp, EINVAL);
18057c478bd9Sstevel@tonic-gate 		break;
18067c478bd9Sstevel@tonic-gate 	}
18077c478bd9Sstevel@tonic-gate }
18087c478bd9Sstevel@tonic-gate 
18097c478bd9Sstevel@tonic-gate /*
18107c478bd9Sstevel@tonic-gate  * allocate mblk and copyout the requested number of bytes
18117c478bd9Sstevel@tonic-gate  */
18127c478bd9Sstevel@tonic-gate static void
ecpp_putioc_copyout(queue_t * q,mblk_t * mp,void * buf,int len)18137c478bd9Sstevel@tonic-gate ecpp_putioc_copyout(queue_t *q, mblk_t *mp, void *buf, int len)
18147c478bd9Sstevel@tonic-gate {
18157c478bd9Sstevel@tonic-gate 	mblk_t	*tmp;
18167c478bd9Sstevel@tonic-gate 
18177c478bd9Sstevel@tonic-gate 	if ((tmp = allocb(len, BPRI_MED)) == NULL) {
18187c478bd9Sstevel@tonic-gate 		ecpp_nack_ioctl(q, mp, ENOSR);
18197c478bd9Sstevel@tonic-gate 		return;
18207c478bd9Sstevel@tonic-gate 	}
18217c478bd9Sstevel@tonic-gate 
18227c478bd9Sstevel@tonic-gate 	bcopy(buf, tmp->b_wptr, len);
18237c478bd9Sstevel@tonic-gate 
18247c478bd9Sstevel@tonic-gate 	mcopyout(mp, NULL, len, NULL, tmp);
18257c478bd9Sstevel@tonic-gate 	qreply(q, mp);
18267c478bd9Sstevel@tonic-gate }
18277c478bd9Sstevel@tonic-gate 
18287c478bd9Sstevel@tonic-gate /*
18297c478bd9Sstevel@tonic-gate  * copyin the structure using struct ecpp_copystate
18307c478bd9Sstevel@tonic-gate  */
18317c478bd9Sstevel@tonic-gate static void
ecpp_putioc_stateful_copyin(queue_t * q,mblk_t * mp,size_t size)18327c478bd9Sstevel@tonic-gate ecpp_putioc_stateful_copyin(queue_t *q, mblk_t *mp, size_t size)
18337c478bd9Sstevel@tonic-gate {
18347c478bd9Sstevel@tonic-gate 	mblk_t *tmp;
18357c478bd9Sstevel@tonic-gate 	struct ecpp_copystate *stp;
18367c478bd9Sstevel@tonic-gate 
18377c478bd9Sstevel@tonic-gate 	if ((tmp = allocb(sizeof (struct ecpp_copystate), BPRI_MED)) == NULL) {
18387c478bd9Sstevel@tonic-gate 		ecpp_nack_ioctl(q, mp, EAGAIN);
18397c478bd9Sstevel@tonic-gate 		return;
18407c478bd9Sstevel@tonic-gate 	}
18417c478bd9Sstevel@tonic-gate 
18427c478bd9Sstevel@tonic-gate 	stp = (struct ecpp_copystate *)tmp->b_rptr;
18437c478bd9Sstevel@tonic-gate 	stp->state = ECPP_STRUCTIN;
18447c478bd9Sstevel@tonic-gate 	stp->uaddr = *(caddr_t *)mp->b_cont->b_rptr;
18457c478bd9Sstevel@tonic-gate 
18467c478bd9Sstevel@tonic-gate 	tmp->b_wptr += sizeof (struct ecpp_copystate);
18477c478bd9Sstevel@tonic-gate 
18487c478bd9Sstevel@tonic-gate 	mcopyin(mp, tmp, size, stp->uaddr);
18497c478bd9Sstevel@tonic-gate 	qreply(q, mp);
18507c478bd9Sstevel@tonic-gate }
18517c478bd9Sstevel@tonic-gate 
18527c478bd9Sstevel@tonic-gate /*
18537c478bd9Sstevel@tonic-gate  * read queue is only used when the peripheral sends data faster,
18547c478bd9Sstevel@tonic-gate  * then the application consumes it;
18557c478bd9Sstevel@tonic-gate  * once the low water mark is reached, this routine will be scheduled
18567c478bd9Sstevel@tonic-gate  */
18577c478bd9Sstevel@tonic-gate static int
ecpp_rsrv(queue_t * q)18587c478bd9Sstevel@tonic-gate ecpp_rsrv(queue_t *q)
18597c478bd9Sstevel@tonic-gate {
18607c478bd9Sstevel@tonic-gate 	struct msgb	*mp;
18617c478bd9Sstevel@tonic-gate 
18627c478bd9Sstevel@tonic-gate 	/*
18637c478bd9Sstevel@tonic-gate 	 * send data upstream until next queue is full or the queue is empty
18647c478bd9Sstevel@tonic-gate 	 */
18657c478bd9Sstevel@tonic-gate 	while (canputnext(q) && (mp = getq(q))) {
18667c478bd9Sstevel@tonic-gate 		putnext(q, mp);
18677c478bd9Sstevel@tonic-gate 	}
18687c478bd9Sstevel@tonic-gate 
18697c478bd9Sstevel@tonic-gate 	/*
18707c478bd9Sstevel@tonic-gate 	 * if there is still space on the queue, enable backchannel
18717c478bd9Sstevel@tonic-gate 	 */
18727c478bd9Sstevel@tonic-gate 	if (canputnext(q)) {
18737c478bd9Sstevel@tonic-gate 		struct ecppunit	*pp = (struct ecppunit *)q->q_ptr;
18747c478bd9Sstevel@tonic-gate 
18757c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
18767c478bd9Sstevel@tonic-gate 
18777c478bd9Sstevel@tonic-gate 		if (pp->e_busy == ECPP_IDLE) {
18787c478bd9Sstevel@tonic-gate 			(void) ecpp_idle_phase(pp);
18797c478bd9Sstevel@tonic-gate 			cv_signal(&pp->pport_cv);  /* signal ecpp_close() */
18807c478bd9Sstevel@tonic-gate 		}
18817c478bd9Sstevel@tonic-gate 
18827c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
18837c478bd9Sstevel@tonic-gate 	}
18847c478bd9Sstevel@tonic-gate 
18857c478bd9Sstevel@tonic-gate 	return (0);
18867c478bd9Sstevel@tonic-gate }
18877c478bd9Sstevel@tonic-gate 
18887c478bd9Sstevel@tonic-gate static int
ecpp_wsrv(queue_t * q)18897c478bd9Sstevel@tonic-gate ecpp_wsrv(queue_t *q)
18907c478bd9Sstevel@tonic-gate {
18917c478bd9Sstevel@tonic-gate 	struct ecppunit	*pp = (struct ecppunit *)q->q_ptr;
18927c478bd9Sstevel@tonic-gate 	struct msgb	*mp;
18937c478bd9Sstevel@tonic-gate 	size_t		len, total_len;
18947c478bd9Sstevel@tonic-gate 	size_t		my_ioblock_sz;
18957c478bd9Sstevel@tonic-gate 	caddr_t		my_ioblock;
18967c478bd9Sstevel@tonic-gate 	caddr_t		start_addr;
18977c478bd9Sstevel@tonic-gate 
18987c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->umutex);
18997c478bd9Sstevel@tonic-gate 
19007c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecpp_wsrv: e_busy=%x\n", pp->e_busy);
19017c478bd9Sstevel@tonic-gate 
19027c478bd9Sstevel@tonic-gate 	/* if channel is actively doing work, wait till completed */
19037c478bd9Sstevel@tonic-gate 	if (pp->e_busy == ECPP_BUSY || pp->e_busy == ECPP_FLUSH) {
19047c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
19057c478bd9Sstevel@tonic-gate 		return (0);
19067c478bd9Sstevel@tonic-gate 	} else if (pp->suspended == TRUE) {
19077c478bd9Sstevel@tonic-gate 		/*
19087c478bd9Sstevel@tonic-gate 		 * if the system is about to suspend and ecpp_detach()
19097c478bd9Sstevel@tonic-gate 		 * is blocked due to active transfers, wake it up and exit
19107c478bd9Sstevel@tonic-gate 		 */
19117c478bd9Sstevel@tonic-gate 		cv_signal(&pp->pport_cv);
19127c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
19137c478bd9Sstevel@tonic-gate 		return (0);
19147c478bd9Sstevel@tonic-gate 	}
19157c478bd9Sstevel@tonic-gate 
19167c478bd9Sstevel@tonic-gate 	/* peripheral status should be okay before starting transfer */
19177c478bd9Sstevel@tonic-gate 	if (pp->e_busy == ECPP_ERR) {
19187c478bd9Sstevel@tonic-gate 		if (ecpp_check_status(pp) == FAILURE) {
19197c478bd9Sstevel@tonic-gate 			if (pp->wsrv_timer_id == 0) {
19207c478bd9Sstevel@tonic-gate 				ecpp_error(pp->dip, "wsrv: start wrsv_timer\n");
19217c478bd9Sstevel@tonic-gate 				pp->wsrv_timer_id = timeout(ecpp_wsrv_timer,
192219397407SSherry Moore 				    (caddr_t)pp,
192319397407SSherry Moore 				    drv_usectohz(pp->wsrv_retry * 1000));
19247c478bd9Sstevel@tonic-gate 			} else {
19257c478bd9Sstevel@tonic-gate 				ecpp_error(pp->dip,
192619397407SSherry Moore 				    "ecpp_wsrv: wrsv_timer is active\n");
19277c478bd9Sstevel@tonic-gate 			}
19287c478bd9Sstevel@tonic-gate 
19297c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
19307c478bd9Sstevel@tonic-gate 			return (0);
19317c478bd9Sstevel@tonic-gate 		} else {
19327c478bd9Sstevel@tonic-gate 			pp->e_busy = ECPP_IDLE;
19337c478bd9Sstevel@tonic-gate 		}
19347c478bd9Sstevel@tonic-gate 	}
19357c478bd9Sstevel@tonic-gate 
19367c478bd9Sstevel@tonic-gate 	my_ioblock = pp->ioblock;
19377c478bd9Sstevel@tonic-gate 	my_ioblock_sz = IO_BLOCK_SZ;
19387c478bd9Sstevel@tonic-gate 
19397c478bd9Sstevel@tonic-gate 	/*
19407c478bd9Sstevel@tonic-gate 	 * it`s important to null pp->msg here,
19417c478bd9Sstevel@tonic-gate 	 * cleaning up from the previous transfer attempts
19427c478bd9Sstevel@tonic-gate 	 */
19437c478bd9Sstevel@tonic-gate 	pp->msg = NULL;
19447c478bd9Sstevel@tonic-gate 
19457c478bd9Sstevel@tonic-gate 	start_addr = NULL;
19467c478bd9Sstevel@tonic-gate 	len = total_len = 0;
19477c478bd9Sstevel@tonic-gate 	/*
19487c478bd9Sstevel@tonic-gate 	 * The following loop is implemented to gather the
19497c478bd9Sstevel@tonic-gate 	 * many small writes that the lp subsystem makes and
19507c478bd9Sstevel@tonic-gate 	 * compile them into one large dma transfer. The len and
19517c478bd9Sstevel@tonic-gate 	 * total_len variables are a running count of the number of
19527c478bd9Sstevel@tonic-gate 	 * bytes that have been gathered. They are bcopied to the
19537c478bd9Sstevel@tonic-gate 	 * ioblock buffer. The pp->e_busy is set to E_BUSY as soon as
19547c478bd9Sstevel@tonic-gate 	 * we start gathering packets to indicate the following transfer.
19557c478bd9Sstevel@tonic-gate 	 */
19567c478bd9Sstevel@tonic-gate 	while (mp = getq(q)) {
19577c478bd9Sstevel@tonic-gate 		switch (DB_TYPE(mp)) {
19587c478bd9Sstevel@tonic-gate 		case M_DATA:
19597c478bd9Sstevel@tonic-gate 			pp->e_busy = ECPP_BUSY;
19607c478bd9Sstevel@tonic-gate 			len = mp->b_wptr - mp->b_rptr;
19617c478bd9Sstevel@tonic-gate 
19627c478bd9Sstevel@tonic-gate 			if ((total_len == 0) && (len >= my_ioblock_sz)) {
19637c478bd9Sstevel@tonic-gate 				/*
19647c478bd9Sstevel@tonic-gate 				 * if the first M_DATA is bigger than ioblock,
19657c478bd9Sstevel@tonic-gate 				 * just use this mblk and start the transfer
19667c478bd9Sstevel@tonic-gate 				 */
19677c478bd9Sstevel@tonic-gate 				total_len = len;
19687c478bd9Sstevel@tonic-gate 				start_addr = (caddr_t)mp->b_rptr;
19697c478bd9Sstevel@tonic-gate 				pp->msg = mp;
19707c478bd9Sstevel@tonic-gate 				goto breakout;
19717c478bd9Sstevel@tonic-gate 			} else if (total_len + len > my_ioblock_sz) {
19727c478bd9Sstevel@tonic-gate 				/*
19737c478bd9Sstevel@tonic-gate 				 * current M_DATA does not fit in ioblock,
19747c478bd9Sstevel@tonic-gate 				 * put it back and start the transfer
19757c478bd9Sstevel@tonic-gate 				 */
19767c478bd9Sstevel@tonic-gate 				(void) putbq(q, mp);
19777c478bd9Sstevel@tonic-gate 				goto breakout;
19787c478bd9Sstevel@tonic-gate 			} else {
19797c478bd9Sstevel@tonic-gate 				/*
19807c478bd9Sstevel@tonic-gate 				 * otherwise add data to ioblock and free mblk
19817c478bd9Sstevel@tonic-gate 				 */
19827c478bd9Sstevel@tonic-gate 				bcopy(mp->b_rptr, my_ioblock, len);
19837c478bd9Sstevel@tonic-gate 				my_ioblock += len;
19847c478bd9Sstevel@tonic-gate 				total_len += len;
19857c478bd9Sstevel@tonic-gate 				start_addr = (caddr_t)pp->ioblock;
19867c478bd9Sstevel@tonic-gate 				freemsg(mp);
19877c478bd9Sstevel@tonic-gate 			}
19887c478bd9Sstevel@tonic-gate 			break;
19897c478bd9Sstevel@tonic-gate 
19907c478bd9Sstevel@tonic-gate 		case M_IOCTL:
19917c478bd9Sstevel@tonic-gate 			/*
19927c478bd9Sstevel@tonic-gate 			 * Assume a simple loopback test: an application
19937c478bd9Sstevel@tonic-gate 			 * writes data into the TFIFO, reads it using
19947c478bd9Sstevel@tonic-gate 			 * ECPPIOC_GETDATA and compares. If the transfer
19957c478bd9Sstevel@tonic-gate 			 * times out (which is only possible on Grover),
19967c478bd9Sstevel@tonic-gate 			 * the ioctl might be processed before the data
19977c478bd9Sstevel@tonic-gate 			 * got to the TFIFO, which leads to miscompare.
19987c478bd9Sstevel@tonic-gate 			 * So if we met ioctl, postpone it until after xfer.
19997c478bd9Sstevel@tonic-gate 			 */
20007c478bd9Sstevel@tonic-gate 			if (total_len > 0) {
20017c478bd9Sstevel@tonic-gate 				(void) putbq(q, mp);
20027c478bd9Sstevel@tonic-gate 				goto breakout;
20037c478bd9Sstevel@tonic-gate 			}
20047c478bd9Sstevel@tonic-gate 
20057c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "M_IOCTL.\n");
20067c478bd9Sstevel@tonic-gate 
20077c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
20087c478bd9Sstevel@tonic-gate 
20097c478bd9Sstevel@tonic-gate 			ecpp_putioc(q, mp);
20107c478bd9Sstevel@tonic-gate 
20117c478bd9Sstevel@tonic-gate 			mutex_enter(&pp->umutex);
20127c478bd9Sstevel@tonic-gate 
20137c478bd9Sstevel@tonic-gate 			break;
20147c478bd9Sstevel@tonic-gate 
20157c478bd9Sstevel@tonic-gate 		case M_IOCDATA: {
20167c478bd9Sstevel@tonic-gate 			struct copyresp *csp = (struct copyresp *)mp->b_rptr;
20177c478bd9Sstevel@tonic-gate 
20187c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "M_IOCDATA\n");
20197c478bd9Sstevel@tonic-gate 
20207c478bd9Sstevel@tonic-gate 			/*
20217c478bd9Sstevel@tonic-gate 			 * If copy request failed, quit now
20227c478bd9Sstevel@tonic-gate 			 */
20237c478bd9Sstevel@tonic-gate 			if (csp->cp_rval != 0) {
20247c478bd9Sstevel@tonic-gate 				freemsg(mp);
20257c478bd9Sstevel@tonic-gate 				break;
20267c478bd9Sstevel@tonic-gate 			}
20277c478bd9Sstevel@tonic-gate 
20287c478bd9Sstevel@tonic-gate 			switch (csp->cp_cmd) {
20297c478bd9Sstevel@tonic-gate 			case ECPPIOC_SETPARMS:
20307c478bd9Sstevel@tonic-gate 			case ECPPIOC_SETREGS:
20317c478bd9Sstevel@tonic-gate 			case ECPPIOC_SETPORT:
20327c478bd9Sstevel@tonic-gate 			case ECPPIOC_SETDATA:
20337c478bd9Sstevel@tonic-gate 			case ECPPIOC_GETDEVID:
20347c478bd9Sstevel@tonic-gate 			case PRNIOC_SET_IFCAP:
20357c478bd9Sstevel@tonic-gate 			case PRNIOC_GET_1284_DEVID:
20367c478bd9Sstevel@tonic-gate 			case PRNIOC_SET_TIMEOUTS:
20377c478bd9Sstevel@tonic-gate 			case PRNIOC_GET_IFINFO:
20387c478bd9Sstevel@tonic-gate 				ecpp_srvioc(q, mp);
20397c478bd9Sstevel@tonic-gate 				break;
20407c478bd9Sstevel@tonic-gate 
20417c478bd9Sstevel@tonic-gate 			default:
20427c478bd9Sstevel@tonic-gate 				ecpp_nack_ioctl(q, mp, EINVAL);
20437c478bd9Sstevel@tonic-gate 				break;
20447c478bd9Sstevel@tonic-gate 			}
20457c478bd9Sstevel@tonic-gate 
20467c478bd9Sstevel@tonic-gate 			break;
20477c478bd9Sstevel@tonic-gate 		}
20487c478bd9Sstevel@tonic-gate 
20497c478bd9Sstevel@tonic-gate 		case M_CTL:
20507c478bd9Sstevel@tonic-gate 			if (pp->e_busy != ECPP_IDLE) {
20517c478bd9Sstevel@tonic-gate 				ecpp_error(pp->dip, "wsrv: M_CTL postponed\n");
20527c478bd9Sstevel@tonic-gate 				(void) putbq(q, mp);
20537c478bd9Sstevel@tonic-gate 				goto breakout;
20547c478bd9Sstevel@tonic-gate 			} else {
20557c478bd9Sstevel@tonic-gate 				ecpp_error(pp->dip, "wsrv: M_CTL\n");
20567c478bd9Sstevel@tonic-gate 			}
20577c478bd9Sstevel@tonic-gate 
20587c478bd9Sstevel@tonic-gate 			/* sanity check */
20597c478bd9Sstevel@tonic-gate 			if ((mp->b_wptr - mp->b_rptr != sizeof (int)) ||
20607c478bd9Sstevel@tonic-gate 			    (*(int *)mp->b_rptr != ECPP_BACKCHANNEL)) {
20617c478bd9Sstevel@tonic-gate 				ecpp_error(pp->dip, "wsrv: bogus M_CTL");
20627c478bd9Sstevel@tonic-gate 				freemsg(mp);
20637c478bd9Sstevel@tonic-gate 				break;
20647c478bd9Sstevel@tonic-gate 			} else {
20657c478bd9Sstevel@tonic-gate 				freemsg(mp);
20667c478bd9Sstevel@tonic-gate 			}
20677c478bd9Sstevel@tonic-gate 
20687c478bd9Sstevel@tonic-gate 			/* This was a backchannel request */
20697c478bd9Sstevel@tonic-gate 			(void) ecpp_peripheral2host(pp);
20707c478bd9Sstevel@tonic-gate 
20717c478bd9Sstevel@tonic-gate 			/* exit if transfer have been initiated */
20727c478bd9Sstevel@tonic-gate 			if (pp->e_busy == ECPP_BUSY) {
20737c478bd9Sstevel@tonic-gate 				goto breakout;
20747c478bd9Sstevel@tonic-gate 			}
20757c478bd9Sstevel@tonic-gate 			break;
20767c478bd9Sstevel@tonic-gate 
20777c478bd9Sstevel@tonic-gate 		case M_READ:
20787c478bd9Sstevel@tonic-gate 			pp->nread += *(size_t *)mp->b_rptr;
20797c478bd9Sstevel@tonic-gate 			freemsg(mp);
20807c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "wsrv: M_READ %d", pp->nread);
20817c478bd9Sstevel@tonic-gate 			break;
20827c478bd9Sstevel@tonic-gate 
20837c478bd9Sstevel@tonic-gate 		default:
20847c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "wsrv: should never get here\n");
20857c478bd9Sstevel@tonic-gate 			freemsg(mp);
20867c478bd9Sstevel@tonic-gate 			break;
20877c478bd9Sstevel@tonic-gate 		}
20887c478bd9Sstevel@tonic-gate 	}
20897c478bd9Sstevel@tonic-gate breakout:
20907c478bd9Sstevel@tonic-gate 	/*
20917c478bd9Sstevel@tonic-gate 	 * If total_len > 0 then start the transfer, otherwise goto idle state
20927c478bd9Sstevel@tonic-gate 	 */
20937c478bd9Sstevel@tonic-gate 	if (total_len > 0) {
20947c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "wsrv:starting: total_len=%d\n", total_len);
20957c478bd9Sstevel@tonic-gate 		pp->e_busy = ECPP_BUSY;
20967c478bd9Sstevel@tonic-gate 		ecpp_start(pp, start_addr, total_len);
20977c478bd9Sstevel@tonic-gate 	} else {
20987c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "wsrv:finishing: ebusy=%x\n", pp->e_busy);
20997c478bd9Sstevel@tonic-gate 
21007c478bd9Sstevel@tonic-gate 		/* IDLE if xfer_timeout, or FIFO_EMPTY */
21017c478bd9Sstevel@tonic-gate 		if (pp->e_busy == ECPP_IDLE) {
21027c478bd9Sstevel@tonic-gate 			(void) ecpp_idle_phase(pp);
21037c478bd9Sstevel@tonic-gate 			cv_signal(&pp->pport_cv);  /* signal ecpp_close() */
21047c478bd9Sstevel@tonic-gate 		}
21057c478bd9Sstevel@tonic-gate 	}
21067c478bd9Sstevel@tonic-gate 
21077c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->umutex);
21087c478bd9Sstevel@tonic-gate 	return (1);
21097c478bd9Sstevel@tonic-gate }
21107c478bd9Sstevel@tonic-gate 
21117c478bd9Sstevel@tonic-gate /*
21127c478bd9Sstevel@tonic-gate  * Ioctl processor for queued ioctl data transfer messages.
21137c478bd9Sstevel@tonic-gate  */
21147c478bd9Sstevel@tonic-gate static void
ecpp_srvioc(queue_t * q,mblk_t * mp)21157c478bd9Sstevel@tonic-gate ecpp_srvioc(queue_t *q, mblk_t *mp)
21167c478bd9Sstevel@tonic-gate {
21177c478bd9Sstevel@tonic-gate 	struct iocblk	*iocbp;
21187c478bd9Sstevel@tonic-gate 	struct ecppunit *pp;
21197c478bd9Sstevel@tonic-gate 
21207c478bd9Sstevel@tonic-gate 	iocbp = (struct iocblk *)mp->b_rptr;
21217c478bd9Sstevel@tonic-gate 	pp = (struct ecppunit *)q->q_ptr;
21227c478bd9Sstevel@tonic-gate 
21237c478bd9Sstevel@tonic-gate 	switch (iocbp->ioc_cmd) {
21247c478bd9Sstevel@tonic-gate 	case ECPPIOC_SETPARMS: {
21257c478bd9Sstevel@tonic-gate 		struct ecpp_transfer_parms *xferp;
21267c478bd9Sstevel@tonic-gate 
21277c478bd9Sstevel@tonic-gate 		xferp = (struct ecpp_transfer_parms *)mp->b_cont->b_rptr;
21287c478bd9Sstevel@tonic-gate 
21297c478bd9Sstevel@tonic-gate 		if (xferp->write_timeout <= 0 ||
213019397407SSherry Moore 		    xferp->write_timeout >= ECPP_MAX_TIMEOUT) {
21317c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
21327c478bd9Sstevel@tonic-gate 			break;
21337c478bd9Sstevel@tonic-gate 		}
21347c478bd9Sstevel@tonic-gate 
21357c478bd9Sstevel@tonic-gate 		if (!((xferp->mode == ECPP_CENTRONICS) ||
213619397407SSherry Moore 		    (xferp->mode == ECPP_COMPAT_MODE) ||
213719397407SSherry Moore 		    (xferp->mode == ECPP_NIBBLE_MODE) ||
213819397407SSherry Moore 		    (xferp->mode == ECPP_ECP_MODE) ||
213919397407SSherry Moore 		    (xferp->mode == ECPP_DIAG_MODE))) {
21407c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
21417c478bd9Sstevel@tonic-gate 			break;
21427c478bd9Sstevel@tonic-gate 		}
21437c478bd9Sstevel@tonic-gate 
21447c478bd9Sstevel@tonic-gate 		pp->xfer_parms = *xferp;
21457c478bd9Sstevel@tonic-gate 		pp->prn_timeouts.tmo_forward = pp->xfer_parms.write_timeout;
21467c478bd9Sstevel@tonic-gate 
21477c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "srvioc: current_mode =%x new mode=%x\n",
214819397407SSherry Moore 		    pp->current_mode, pp->xfer_parms.mode);
21497c478bd9Sstevel@tonic-gate 
21507c478bd9Sstevel@tonic-gate 		if (ecpp_mode_negotiation(pp, pp->xfer_parms.mode) == FAILURE) {
21517c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EPROTONOSUPPORT);
21527c478bd9Sstevel@tonic-gate 		} else {
21537c478bd9Sstevel@tonic-gate 			/*
21547c478bd9Sstevel@tonic-gate 			 * mode nego was a success.  If nibble mode check
21557c478bd9Sstevel@tonic-gate 			 * back channel and set into REVIDLE.
21567c478bd9Sstevel@tonic-gate 			 */
21577c478bd9Sstevel@tonic-gate 			if ((pp->current_mode == ECPP_NIBBLE_MODE) &&
21587c478bd9Sstevel@tonic-gate 			    (read_nibble_backchan(pp) == FAILURE)) {
21597c478bd9Sstevel@tonic-gate 				/*
21607c478bd9Sstevel@tonic-gate 				 * problems reading the backchannel
21617c478bd9Sstevel@tonic-gate 				 * returned to centronics;
21627c478bd9Sstevel@tonic-gate 				 * ioctl fails.
21637c478bd9Sstevel@tonic-gate 				 */
21647c478bd9Sstevel@tonic-gate 				ecpp_nack_ioctl(q, mp, EPROTONOSUPPORT);
21657c478bd9Sstevel@tonic-gate 				break;
21667c478bd9Sstevel@tonic-gate 			}
21677c478bd9Sstevel@tonic-gate 
21687c478bd9Sstevel@tonic-gate 			ecpp_ack_ioctl(q, mp);
21697c478bd9Sstevel@tonic-gate 		}
21707c478bd9Sstevel@tonic-gate 		if (pp->current_mode != ECPP_DIAG_MODE) {
21717c478bd9Sstevel@tonic-gate 			pp->port = ECPP_PORT_DMA;
21727c478bd9Sstevel@tonic-gate 		} else {
21737c478bd9Sstevel@tonic-gate 			pp->port = ECPP_PORT_PIO;
21747c478bd9Sstevel@tonic-gate 		}
21757c478bd9Sstevel@tonic-gate 
21767c478bd9Sstevel@tonic-gate 		pp->xfer_parms.mode = pp->current_mode;
21777c478bd9Sstevel@tonic-gate 
21787c478bd9Sstevel@tonic-gate 		break;
21797c478bd9Sstevel@tonic-gate 	}
21807c478bd9Sstevel@tonic-gate 
21817c478bd9Sstevel@tonic-gate 	case ECPPIOC_SETREGS: {
21827c478bd9Sstevel@tonic-gate 		struct ecpp_regs *rg;
21837c478bd9Sstevel@tonic-gate 		uint8_t dcr;
21847c478bd9Sstevel@tonic-gate 
21857c478bd9Sstevel@tonic-gate 		rg = (struct ecpp_regs *)mp->b_cont->b_rptr;
21867c478bd9Sstevel@tonic-gate 
21877c478bd9Sstevel@tonic-gate 		/* must be in diagnostic mode for these commands to work */
21887c478bd9Sstevel@tonic-gate 		if (pp->current_mode != ECPP_DIAG_MODE) {
21897c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
21907c478bd9Sstevel@tonic-gate 			break;
21917c478bd9Sstevel@tonic-gate 		}
21927c478bd9Sstevel@tonic-gate 
21937c478bd9Sstevel@tonic-gate 		/* bits 4-7 must be 1 or return EINVAL */
21947c478bd9Sstevel@tonic-gate 		if ((rg->dcr & ECPP_SETREGS_DCR_MASK) !=
219519397407SSherry Moore 		    ECPP_SETREGS_DCR_MASK) {
21967c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
21977c478bd9Sstevel@tonic-gate 			break;
21987c478bd9Sstevel@tonic-gate 		}
21997c478bd9Sstevel@tonic-gate 
22007c478bd9Sstevel@tonic-gate 		/* get the old dcr */
22017c478bd9Sstevel@tonic-gate 		dcr = DCR_READ(pp) & ~ECPP_REV_DIR;
22027c478bd9Sstevel@tonic-gate 		/* get the new dcr */
22037c478bd9Sstevel@tonic-gate 		dcr = (dcr & ECPP_SETREGS_DCR_MASK) |
220419397407SSherry Moore 		    (rg->dcr & ~ECPP_SETREGS_DCR_MASK);
22057c478bd9Sstevel@tonic-gate 		DCR_WRITE(pp, dcr);
22067c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ECPPIOC_SETREGS:dcr=%x\n", dcr);
22077c478bd9Sstevel@tonic-gate 		ecpp_ack_ioctl(q, mp);
22087c478bd9Sstevel@tonic-gate 		break;
22097c478bd9Sstevel@tonic-gate 	}
22107c478bd9Sstevel@tonic-gate 
22117c478bd9Sstevel@tonic-gate 	case ECPPIOC_SETPORT: {
22127c478bd9Sstevel@tonic-gate 		uchar_t *port;
22137c478bd9Sstevel@tonic-gate 
22147c478bd9Sstevel@tonic-gate 		port = (uchar_t *)mp->b_cont->b_rptr;
22157c478bd9Sstevel@tonic-gate 
22167c478bd9Sstevel@tonic-gate 		/* must be in diagnostic mode for these commands to work */
22177c478bd9Sstevel@tonic-gate 		if (pp->current_mode != ECPP_DIAG_MODE) {
22187c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
22197c478bd9Sstevel@tonic-gate 			break;
22207c478bd9Sstevel@tonic-gate 		}
22217c478bd9Sstevel@tonic-gate 
22227c478bd9Sstevel@tonic-gate 		switch (*port) {
22237c478bd9Sstevel@tonic-gate 		case ECPP_PORT_PIO:
22247c478bd9Sstevel@tonic-gate 			/* put superio into PIO mode */
22257c478bd9Sstevel@tonic-gate 			ECR_WRITE(pp,
222619397407SSherry Moore 			    ECR_mode_001 | ECPP_INTR_MASK | ECPP_INTR_SRV);
22277c478bd9Sstevel@tonic-gate 			pp->port = *port;
22287c478bd9Sstevel@tonic-gate 			ecpp_ack_ioctl(q, mp);
22297c478bd9Sstevel@tonic-gate 			break;
22307c478bd9Sstevel@tonic-gate 
22317c478bd9Sstevel@tonic-gate 		case ECPP_PORT_TDMA:
22327c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "SETPORT: to TDMA\n");
22337c478bd9Sstevel@tonic-gate 			pp->tfifo_intr = 1;
22347c478bd9Sstevel@tonic-gate 			/* change to mode 110 */
22357c478bd9Sstevel@tonic-gate 			ECR_WRITE(pp,
223619397407SSherry Moore 			    ECR_mode_110 | ECPP_INTR_MASK | ECPP_INTR_SRV);
22377c478bd9Sstevel@tonic-gate 			pp->port = *port;
22387c478bd9Sstevel@tonic-gate 			ecpp_ack_ioctl(q, mp);
22397c478bd9Sstevel@tonic-gate 			break;
22407c478bd9Sstevel@tonic-gate 
22417c478bd9Sstevel@tonic-gate 		default:
22427c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
22437c478bd9Sstevel@tonic-gate 		}
22447c478bd9Sstevel@tonic-gate 
22457c478bd9Sstevel@tonic-gate 		break;
22467c478bd9Sstevel@tonic-gate 	}
22477c478bd9Sstevel@tonic-gate 
22487c478bd9Sstevel@tonic-gate 	case ECPPIOC_SETDATA: {
22497c478bd9Sstevel@tonic-gate 		uchar_t *data;
22507c478bd9Sstevel@tonic-gate 
22517c478bd9Sstevel@tonic-gate 		data = (uchar_t *)mp->b_cont->b_rptr;
22527c478bd9Sstevel@tonic-gate 
22537c478bd9Sstevel@tonic-gate 		/* must be in diagnostic mode for these commands to work */
22547c478bd9Sstevel@tonic-gate 		if (pp->current_mode != ECPP_DIAG_MODE) {
22557c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
22567c478bd9Sstevel@tonic-gate 			break;
22577c478bd9Sstevel@tonic-gate 		}
22587c478bd9Sstevel@tonic-gate 
22597c478bd9Sstevel@tonic-gate 		switch (pp->port) {
22607c478bd9Sstevel@tonic-gate 		case ECPP_PORT_PIO:
22617c478bd9Sstevel@tonic-gate 			DATAR_WRITE(pp, *data);
22627c478bd9Sstevel@tonic-gate 			ecpp_ack_ioctl(q, mp);
22637c478bd9Sstevel@tonic-gate 			break;
22647c478bd9Sstevel@tonic-gate 
22657c478bd9Sstevel@tonic-gate 		case ECPP_PORT_TDMA:
22667c478bd9Sstevel@tonic-gate 			TFIFO_WRITE(pp, *data);
22677c478bd9Sstevel@tonic-gate 			ecpp_ack_ioctl(q, mp);
22687c478bd9Sstevel@tonic-gate 			break;
22697c478bd9Sstevel@tonic-gate 
22707c478bd9Sstevel@tonic-gate 		default:
22717c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
22727c478bd9Sstevel@tonic-gate 		}
22737c478bd9Sstevel@tonic-gate 
22747c478bd9Sstevel@tonic-gate 		break;
22757c478bd9Sstevel@tonic-gate 	}
22767c478bd9Sstevel@tonic-gate 
22777c478bd9Sstevel@tonic-gate 	case ECPPIOC_GETDEVID: {
22787c478bd9Sstevel@tonic-gate 		struct copyresp		*csp;
22797c478bd9Sstevel@tonic-gate 		struct ecpp_copystate	*stp;
22807c478bd9Sstevel@tonic-gate 		struct ecpp_device_id	*dp;
22817c478bd9Sstevel@tonic-gate 		struct ecpp_device_id	id;
22827c478bd9Sstevel@tonic-gate 
22837c478bd9Sstevel@tonic-gate 		csp = (struct copyresp *)mp->b_rptr;
22847c478bd9Sstevel@tonic-gate 		stp = (struct ecpp_copystate *)csp->cp_private->b_rptr;
22857c478bd9Sstevel@tonic-gate 		dp = (struct ecpp_device_id *)mp->b_cont->b_rptr;
22867c478bd9Sstevel@tonic-gate 
22877c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
22887c478bd9Sstevel@tonic-gate 		if (IOC_CONVERT_FROM(iocbp) == IOC_ILP32) {
22897c478bd9Sstevel@tonic-gate 			struct ecpp_device_id32 *dp32;
22907c478bd9Sstevel@tonic-gate 
22917c478bd9Sstevel@tonic-gate 			dp32 = (struct ecpp_device_id32 *)dp;
22927c478bd9Sstevel@tonic-gate 			id.mode = dp32->mode;
22937c478bd9Sstevel@tonic-gate 			id.len = dp32->len;
22947c478bd9Sstevel@tonic-gate 			id.addr = (char *)(uintptr_t)dp32->addr;
22957c478bd9Sstevel@tonic-gate 		} else {
22967c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
22977c478bd9Sstevel@tonic-gate 			id = *dp;
22987c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
22997c478bd9Sstevel@tonic-gate 		}
23007c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
23017c478bd9Sstevel@tonic-gate 
23027c478bd9Sstevel@tonic-gate 		ecpp_srvioc_devid(q, mp, &id, &stp->un.devid.rlen);
23037c478bd9Sstevel@tonic-gate 		break;
23047c478bd9Sstevel@tonic-gate 	}
23057c478bd9Sstevel@tonic-gate 
23067c478bd9Sstevel@tonic-gate 	case PRNIOC_GET_1284_DEVID: {
23077c478bd9Sstevel@tonic-gate 		struct copyresp			*csp;
23087c478bd9Sstevel@tonic-gate 		struct ecpp_copystate		*stp;
23097c478bd9Sstevel@tonic-gate 		struct prn_1284_device_id	*dp;
23107c478bd9Sstevel@tonic-gate 		struct ecpp_device_id		id;
23117c478bd9Sstevel@tonic-gate 
23127c478bd9Sstevel@tonic-gate 		csp = (struct copyresp *)mp->b_rptr;
23137c478bd9Sstevel@tonic-gate 		stp = (struct ecpp_copystate *)csp->cp_private->b_rptr;
23147c478bd9Sstevel@tonic-gate 		dp = (struct prn_1284_device_id *)mp->b_cont->b_rptr;
23157c478bd9Sstevel@tonic-gate 
23167c478bd9Sstevel@tonic-gate 		/* imitate struct ecpp_device_id */
23177c478bd9Sstevel@tonic-gate 		id.mode = ECPP_NIBBLE_MODE;
23187c478bd9Sstevel@tonic-gate 
23197c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
23207c478bd9Sstevel@tonic-gate 		if (IOC_CONVERT_FROM(iocbp) == IOC_ILP32) {
23217c478bd9Sstevel@tonic-gate 			struct prn_1284_device_id32 *dp32;
23227c478bd9Sstevel@tonic-gate 
23237c478bd9Sstevel@tonic-gate 			dp32 = (struct prn_1284_device_id32 *)dp;
23247c478bd9Sstevel@tonic-gate 			id.len = dp32->id_len;
23257c478bd9Sstevel@tonic-gate 			id.addr = (char *)(uintptr_t)dp32->id_data;
23267c478bd9Sstevel@tonic-gate 		} else {
23277c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
23287c478bd9Sstevel@tonic-gate 			id.len = dp->id_len;
23297c478bd9Sstevel@tonic-gate 			id.addr = (char *)dp->id_data;
23307c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
23317c478bd9Sstevel@tonic-gate 		}
23327c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
23337c478bd9Sstevel@tonic-gate 
23347c478bd9Sstevel@tonic-gate 		ecpp_srvioc_devid(q, mp, &id,
233519397407SSherry Moore 		    (int *)&stp->un.prn_devid.id_rlen);
23367c478bd9Sstevel@tonic-gate 		break;
23377c478bd9Sstevel@tonic-gate 	}
23387c478bd9Sstevel@tonic-gate 
23397c478bd9Sstevel@tonic-gate 	case PRNIOC_SET_IFCAP: {
23407c478bd9Sstevel@tonic-gate 		uint_t	ifcap, new_ifcap;
23417c478bd9Sstevel@tonic-gate 
23427c478bd9Sstevel@tonic-gate 		ifcap = ecpp_get_prn_ifcap(pp);
23437c478bd9Sstevel@tonic-gate 		new_ifcap = *(uint_t *)mp->b_cont->b_rptr;
23447c478bd9Sstevel@tonic-gate 
23457c478bd9Sstevel@tonic-gate 		if (ifcap == new_ifcap) {
23467c478bd9Sstevel@tonic-gate 			ecpp_ack_ioctl(q, mp);
23477c478bd9Sstevel@tonic-gate 			break;
23487c478bd9Sstevel@tonic-gate 		}
23497c478bd9Sstevel@tonic-gate 
23507c478bd9Sstevel@tonic-gate 		/* only changing PRN_BIDI is supported */
23517c478bd9Sstevel@tonic-gate 		if ((ifcap ^ new_ifcap) & ~PRN_BIDI) {
23527c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
23537c478bd9Sstevel@tonic-gate 			break;
23547c478bd9Sstevel@tonic-gate 		}
23557c478bd9Sstevel@tonic-gate 
23562520aea3SToomas Soome 		if (new_ifcap & PRN_BIDI) {	/* go bidirectional */
23577c478bd9Sstevel@tonic-gate 			ecpp_default_negotiation(pp);
23587c478bd9Sstevel@tonic-gate 		} else {			/* go unidirectional */
23597c478bd9Sstevel@tonic-gate 			(void) ecpp_mode_negotiation(pp, ECPP_CENTRONICS);
23607c478bd9Sstevel@tonic-gate 		}
23617c478bd9Sstevel@tonic-gate 
23627c478bd9Sstevel@tonic-gate 		ecpp_ack_ioctl(q, mp);
23637c478bd9Sstevel@tonic-gate 		break;
23647c478bd9Sstevel@tonic-gate 	}
23657c478bd9Sstevel@tonic-gate 
23667c478bd9Sstevel@tonic-gate 	case PRNIOC_SET_TIMEOUTS: {
23677c478bd9Sstevel@tonic-gate 		struct prn_timeouts	*prn_timeouts;
23687c478bd9Sstevel@tonic-gate 
23697c478bd9Sstevel@tonic-gate 		prn_timeouts = (struct prn_timeouts *)mp->b_cont->b_rptr;
23707c478bd9Sstevel@tonic-gate 
23717c478bd9Sstevel@tonic-gate 		if (prn_timeouts->tmo_forward > ECPP_MAX_TIMEOUT) {
23727c478bd9Sstevel@tonic-gate 			ecpp_nack_ioctl(q, mp, EINVAL);
23737c478bd9Sstevel@tonic-gate 			break;
23747c478bd9Sstevel@tonic-gate 		}
23757c478bd9Sstevel@tonic-gate 
23767c478bd9Sstevel@tonic-gate 		pp->prn_timeouts = *prn_timeouts;
23777c478bd9Sstevel@tonic-gate 		pp->xfer_parms.write_timeout = (int)prn_timeouts->tmo_forward;
23787c478bd9Sstevel@tonic-gate 
23797c478bd9Sstevel@tonic-gate 		ecpp_ack_ioctl(q, mp);
23807c478bd9Sstevel@tonic-gate 		break;
23817c478bd9Sstevel@tonic-gate 	}
23827c478bd9Sstevel@tonic-gate 
23837c478bd9Sstevel@tonic-gate 	case PRNIOC_GET_IFINFO:
23847c478bd9Sstevel@tonic-gate 		ecpp_srvioc_prnif(q, mp);
23857c478bd9Sstevel@tonic-gate 		break;
23867c478bd9Sstevel@tonic-gate 
23877c478bd9Sstevel@tonic-gate 	default:		/* unexpected ioctl type */
23887c478bd9Sstevel@tonic-gate 		ecpp_nack_ioctl(q, mp, EINVAL);
23897c478bd9Sstevel@tonic-gate 		break;
23907c478bd9Sstevel@tonic-gate 	}
23917c478bd9Sstevel@tonic-gate }
23927c478bd9Sstevel@tonic-gate 
23937c478bd9Sstevel@tonic-gate static void
ecpp_srvioc_devid(queue_t * q,mblk_t * mp,struct ecpp_device_id * id,int * rlen)23947c478bd9Sstevel@tonic-gate ecpp_srvioc_devid(queue_t *q, mblk_t *mp, struct ecpp_device_id *id, int *rlen)
23957c478bd9Sstevel@tonic-gate {
23962520aea3SToomas Soome 	struct ecppunit		*pp;
23977c478bd9Sstevel@tonic-gate 	struct copyresp		*csp;
23987c478bd9Sstevel@tonic-gate 	struct ecpp_copystate	*stp;
23997c478bd9Sstevel@tonic-gate 	int			error;
24007c478bd9Sstevel@tonic-gate 	int			len;
24017c478bd9Sstevel@tonic-gate 	int			mode;
24027c478bd9Sstevel@tonic-gate 	mblk_t			*datamp;
24037c478bd9Sstevel@tonic-gate 
24047c478bd9Sstevel@tonic-gate 	pp = (struct ecppunit *)q->q_ptr;
24057c478bd9Sstevel@tonic-gate 	csp = (struct copyresp *)mp->b_rptr;
24067c478bd9Sstevel@tonic-gate 	stp = (struct ecpp_copystate *)csp->cp_private->b_rptr;
24077c478bd9Sstevel@tonic-gate 	mode = id->mode;
24087c478bd9Sstevel@tonic-gate 
24097c478bd9Sstevel@tonic-gate 	/* check arguments */
24107c478bd9Sstevel@tonic-gate 	if ((mode < ECPP_CENTRONICS) || (mode > ECPP_ECP_MODE)) {
24117c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_srvioc_devid: mode=%x, len=%x\n",
241219397407SSherry Moore 		    mode, id->len);
24137c478bd9Sstevel@tonic-gate 		ecpp_nack_ioctl(q, mp, EINVAL);
24147c478bd9Sstevel@tonic-gate 		return;
24157c478bd9Sstevel@tonic-gate 	}
24167c478bd9Sstevel@tonic-gate 
24177c478bd9Sstevel@tonic-gate 	/* Currently only Nibble mode is supported */
24187c478bd9Sstevel@tonic-gate 	if (mode != ECPP_NIBBLE_MODE) {
24197c478bd9Sstevel@tonic-gate 		ecpp_nack_ioctl(q, mp, EPROTONOSUPPORT);
24207c478bd9Sstevel@tonic-gate 		return;
24217c478bd9Sstevel@tonic-gate 	}
24227c478bd9Sstevel@tonic-gate 
24237c478bd9Sstevel@tonic-gate 	if ((id->addr == NULL) && (id->len != 0)) {
24247c478bd9Sstevel@tonic-gate 		ecpp_nack_ioctl(q, mp, EFAULT);
24257c478bd9Sstevel@tonic-gate 		return;
24267c478bd9Sstevel@tonic-gate 	}
24277c478bd9Sstevel@tonic-gate 
24287c478bd9Sstevel@tonic-gate 	/* read device ID length */
24297c478bd9Sstevel@tonic-gate 	if (error = ecpp_getdevid(pp, NULL, &len, mode)) {
24307c478bd9Sstevel@tonic-gate 		ecpp_nack_ioctl(q, mp, error);
24317c478bd9Sstevel@tonic-gate 		goto breakout;
24327c478bd9Sstevel@tonic-gate 	}
24337c478bd9Sstevel@tonic-gate 
24347c478bd9Sstevel@tonic-gate 	/* don't take into account two length bytes */
24357c478bd9Sstevel@tonic-gate 	len -= 2;
24367c478bd9Sstevel@tonic-gate 	*rlen = len;
24377c478bd9Sstevel@tonic-gate 
24387c478bd9Sstevel@tonic-gate 	/* limit transfer to user buffer length */
24397c478bd9Sstevel@tonic-gate 	if (id->len < len) {
24407c478bd9Sstevel@tonic-gate 		len = id->len;
24417c478bd9Sstevel@tonic-gate 	}
24427c478bd9Sstevel@tonic-gate 
24437c478bd9Sstevel@tonic-gate 	if (len == 0) {
24447c478bd9Sstevel@tonic-gate 		/* just return rlen */
24457c478bd9Sstevel@tonic-gate 		stp->state = ECPP_ADDROUT;
24467c478bd9Sstevel@tonic-gate 		ecpp_wput_iocdata_devid(q, mp,
244719397407SSherry Moore 		    (uintptr_t)rlen - (uintptr_t)&stp->un);
24487c478bd9Sstevel@tonic-gate 		goto breakout;
24497c478bd9Sstevel@tonic-gate 	}
24507c478bd9Sstevel@tonic-gate 
24517c478bd9Sstevel@tonic-gate 	if ((datamp = allocb(len, BPRI_MED)) == NULL) {
24527c478bd9Sstevel@tonic-gate 		ecpp_nack_ioctl(q, mp, ENOSR);
24537c478bd9Sstevel@tonic-gate 		goto breakout;
24547c478bd9Sstevel@tonic-gate 	}
24557c478bd9Sstevel@tonic-gate 
24567c478bd9Sstevel@tonic-gate 	/* read ID string */
24577c478bd9Sstevel@tonic-gate 	error = ecpp_getdevid(pp, datamp->b_rptr, &len, mode);
24587c478bd9Sstevel@tonic-gate 	if (error) {
24597c478bd9Sstevel@tonic-gate 		freemsg(datamp);
24607c478bd9Sstevel@tonic-gate 		ecpp_nack_ioctl(q, mp, error);
24617c478bd9Sstevel@tonic-gate 		goto breakout;
24627c478bd9Sstevel@tonic-gate 	} else {
24637c478bd9Sstevel@tonic-gate 		datamp->b_wptr += len;
24647c478bd9Sstevel@tonic-gate 
24657c478bd9Sstevel@tonic-gate 		stp->state = ECPP_ADDROUT;
24667c478bd9Sstevel@tonic-gate 		mcopyout(mp, csp->cp_private, len, id->addr, datamp);
24677c478bd9Sstevel@tonic-gate 		qreply(q, mp);
24687c478bd9Sstevel@tonic-gate 	}
24697c478bd9Sstevel@tonic-gate 
24707c478bd9Sstevel@tonic-gate 	return;
24717c478bd9Sstevel@tonic-gate 
24727c478bd9Sstevel@tonic-gate breakout:
24737c478bd9Sstevel@tonic-gate 	(void) ecpp_1284_termination(pp);
24747c478bd9Sstevel@tonic-gate }
24757c478bd9Sstevel@tonic-gate 
24767c478bd9Sstevel@tonic-gate /*
24777c478bd9Sstevel@tonic-gate  * PRNIOC_GET_IFINFO: return prnio interface info string
24787c478bd9Sstevel@tonic-gate  */
24797c478bd9Sstevel@tonic-gate static void
ecpp_srvioc_prnif(queue_t * q,mblk_t * mp)24807c478bd9Sstevel@tonic-gate ecpp_srvioc_prnif(queue_t *q, mblk_t *mp)
24817c478bd9Sstevel@tonic-gate {
24827c478bd9Sstevel@tonic-gate 	struct copyresp			*csp;
24837c478bd9Sstevel@tonic-gate 	struct ecpp_copystate		*stp;
24847c478bd9Sstevel@tonic-gate 	uint_t				len;
24857c478bd9Sstevel@tonic-gate 	struct prn_interface_info	*ip;
24867c478bd9Sstevel@tonic-gate 	struct prn_interface_info	info;
24877c478bd9Sstevel@tonic-gate 	mblk_t				*datamp;
24887c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
24897c478bd9Sstevel@tonic-gate 	struct iocblk		*iocbp = (struct iocblk *)mp->b_rptr;
24907c478bd9Sstevel@tonic-gate #endif
24917c478bd9Sstevel@tonic-gate 
24927c478bd9Sstevel@tonic-gate 	csp = (struct copyresp *)mp->b_rptr;
24937c478bd9Sstevel@tonic-gate 	stp = (struct ecpp_copystate *)csp->cp_private->b_rptr;
24947c478bd9Sstevel@tonic-gate 	ip = (struct prn_interface_info *)mp->b_cont->b_rptr;
24957c478bd9Sstevel@tonic-gate 
24967c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
24977c478bd9Sstevel@tonic-gate 	if (IOC_CONVERT_FROM(iocbp) == IOC_ILP32) {
24987c478bd9Sstevel@tonic-gate 		struct prn_interface_info32 *ip32;
24997c478bd9Sstevel@tonic-gate 
25007c478bd9Sstevel@tonic-gate 		ip32 = (struct prn_interface_info32 *)ip;
25017c478bd9Sstevel@tonic-gate 		info.if_len = ip32->if_len;
25027c478bd9Sstevel@tonic-gate 		info.if_data = (char *)(uintptr_t)ip32->if_data;
25037c478bd9Sstevel@tonic-gate 	} else {
25047c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
25057c478bd9Sstevel@tonic-gate 		info = *ip;
25067c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
25077c478bd9Sstevel@tonic-gate 	}
25087c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
25097c478bd9Sstevel@tonic-gate 
25107c478bd9Sstevel@tonic-gate 	len = strlen(prn_ifinfo);
25117c478bd9Sstevel@tonic-gate 	stp->un.prn_if.if_rlen = len;
25127c478bd9Sstevel@tonic-gate 	stp->state = ECPP_ADDROUT;
25137c478bd9Sstevel@tonic-gate 
25147c478bd9Sstevel@tonic-gate 	/* check arguments */
25157c478bd9Sstevel@tonic-gate 	if ((info.if_data == NULL) && (info.if_len != 0)) {
25167c478bd9Sstevel@tonic-gate 		ecpp_nack_ioctl(q, mp, EFAULT);
25177c478bd9Sstevel@tonic-gate 		return;
25187c478bd9Sstevel@tonic-gate 	}
25197c478bd9Sstevel@tonic-gate 
25207c478bd9Sstevel@tonic-gate 	if (info.if_len == 0) {
25217c478bd9Sstevel@tonic-gate 		/* just copyout rlen */
25227c478bd9Sstevel@tonic-gate 		ecpp_wput_iocdata_devid(q, mp,
252319397407SSherry Moore 		    offsetof(struct prn_interface_info, if_rlen));
25247c478bd9Sstevel@tonic-gate 		return;
25257c478bd9Sstevel@tonic-gate 	}
25267c478bd9Sstevel@tonic-gate 
25277c478bd9Sstevel@tonic-gate 	/* if needed, trim to the buffer size */
25287c478bd9Sstevel@tonic-gate 	if (len > info.if_len) {
25297c478bd9Sstevel@tonic-gate 		len = info.if_len;
25307c478bd9Sstevel@tonic-gate 	}
25317c478bd9Sstevel@tonic-gate 
25327c478bd9Sstevel@tonic-gate 	if ((datamp = allocb(len, BPRI_MED)) == NULL) {
25337c478bd9Sstevel@tonic-gate 		ecpp_nack_ioctl(q, mp, ENOSR);
25347c478bd9Sstevel@tonic-gate 		return;
25357c478bd9Sstevel@tonic-gate 	}
25367c478bd9Sstevel@tonic-gate 
25377c478bd9Sstevel@tonic-gate 	bcopy(&prn_ifinfo[0], datamp->b_wptr, len);
25387c478bd9Sstevel@tonic-gate 	datamp->b_wptr += len;
25397c478bd9Sstevel@tonic-gate 
25407c478bd9Sstevel@tonic-gate 	mcopyout(mp, csp->cp_private, len, info.if_data, datamp);
25417c478bd9Sstevel@tonic-gate 	qreply(q, mp);
25427c478bd9Sstevel@tonic-gate }
25437c478bd9Sstevel@tonic-gate 
25447c478bd9Sstevel@tonic-gate static void
ecpp_flush(struct ecppunit * pp,int cmd)25457c478bd9Sstevel@tonic-gate ecpp_flush(struct ecppunit *pp, int cmd)
25467c478bd9Sstevel@tonic-gate {
25477c478bd9Sstevel@tonic-gate 	queue_t		*q;
25487c478bd9Sstevel@tonic-gate 	uint8_t		ecr, dcr;
25497c478bd9Sstevel@tonic-gate 	timeout_id_t	timeout_id, fifo_timer_id, wsrv_timer_id;
25507c478bd9Sstevel@tonic-gate 
25517c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->umutex));
25527c478bd9Sstevel@tonic-gate 
25537c478bd9Sstevel@tonic-gate 	if (!(cmd & FWRITE)) {
25547c478bd9Sstevel@tonic-gate 		return;
25557c478bd9Sstevel@tonic-gate 	}
25567c478bd9Sstevel@tonic-gate 
25577c478bd9Sstevel@tonic-gate 	q = pp->writeq;
25587c478bd9Sstevel@tonic-gate 	timeout_id = fifo_timer_id = wsrv_timer_id = 0;
25597c478bd9Sstevel@tonic-gate 
25607c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecpp_flush e_busy=%x\n", pp->e_busy);
25617c478bd9Sstevel@tonic-gate 
25627c478bd9Sstevel@tonic-gate 	/* if there is an ongoing DMA, it needs to be turned off. */
25637c478bd9Sstevel@tonic-gate 	switch (pp->e_busy) {
25647c478bd9Sstevel@tonic-gate 	case ECPP_BUSY:
25657c478bd9Sstevel@tonic-gate 		/*
25667c478bd9Sstevel@tonic-gate 		 * Change the port status to ECPP_FLUSH to
25677c478bd9Sstevel@tonic-gate 		 * indicate to ecpp_wsrv that the wq is being flushed.
25687c478bd9Sstevel@tonic-gate 		 */
25697c478bd9Sstevel@tonic-gate 		pp->e_busy = ECPP_FLUSH;
25707c478bd9Sstevel@tonic-gate 
25717c478bd9Sstevel@tonic-gate 		/*
25727c478bd9Sstevel@tonic-gate 		 * dma_cancelled indicates to ecpp_isr() that we have
25737c478bd9Sstevel@tonic-gate 		 * turned off the DMA.  Since the mutex is held, ecpp_isr()
25747c478bd9Sstevel@tonic-gate 		 * may be blocked.  Once ecpp_flush() finishes and ecpp_isr()
25757c478bd9Sstevel@tonic-gate 		 * gains the mutex, ecpp_isr() will have a _reset_ DMAC.  Most
25767c478bd9Sstevel@tonic-gate 		 * significantly, the DMAC will be reset after ecpp_isr() was
25777c478bd9Sstevel@tonic-gate 		 * invoked.  Therefore we need to have a flag "dma_cancelled"
25787c478bd9Sstevel@tonic-gate 		 * to signify when the described condition has occured.  If
25797c478bd9Sstevel@tonic-gate 		 * ecpp_isr() notes a dma_cancelled, it will ignore the DMAC csr
25807c478bd9Sstevel@tonic-gate 		 * and simply claim the interupt.
25817c478bd9Sstevel@tonic-gate 		 */
25827c478bd9Sstevel@tonic-gate 
25837c478bd9Sstevel@tonic-gate 		pp->dma_cancelled = TRUE;
25847c478bd9Sstevel@tonic-gate 
25857c478bd9Sstevel@tonic-gate 		/* either DMA or PIO transfer */
25867c478bd9Sstevel@tonic-gate 		if (COMPAT_DMA(pp) ||
25877c478bd9Sstevel@tonic-gate 		    (pp->current_mode == ECPP_ECP_MODE) ||
25887c478bd9Sstevel@tonic-gate 		    (pp->current_mode == ECPP_DIAG_MODE)) {
25897c478bd9Sstevel@tonic-gate 			/*
25907c478bd9Sstevel@tonic-gate 			 * if the bcr is zero, then DMA is complete and
25917c478bd9Sstevel@tonic-gate 			 * we are waiting for the fifo to drain.  Therefore,
25927c478bd9Sstevel@tonic-gate 			 * turn off dma.
25937c478bd9Sstevel@tonic-gate 			 */
25947c478bd9Sstevel@tonic-gate 			if (ECPP_DMA_STOP(pp, NULL) == FAILURE) {
25957c478bd9Sstevel@tonic-gate 				ecpp_error(pp->dip,
259619397407SSherry Moore 				    "ecpp_flush: dma_stop failed.\n");
25977c478bd9Sstevel@tonic-gate 			}
25987c478bd9Sstevel@tonic-gate 
25997c478bd9Sstevel@tonic-gate 			/*
26007c478bd9Sstevel@tonic-gate 			 * If the status of the port is ECPP_BUSY,
26017c478bd9Sstevel@tonic-gate 			 * the DMA is stopped by either explicitly above, or by
26027c478bd9Sstevel@tonic-gate 			 * ecpp_isr() but the FIFO hasn't drained yet. In either
26037c478bd9Sstevel@tonic-gate 			 * case, we need to unbind the dma mappings.
26047c478bd9Sstevel@tonic-gate 			 */
26057c478bd9Sstevel@tonic-gate 			if (ddi_dma_unbind_handle(
260619397407SSherry Moore 			    pp->dma_handle) != DDI_SUCCESS)
26077c478bd9Sstevel@tonic-gate 				ecpp_error(pp->dip,
260819397407SSherry Moore 				    "ecpp_flush: unbind failed.\n");
26097c478bd9Sstevel@tonic-gate 
26107c478bd9Sstevel@tonic-gate 			if (pp->msg != NULL) {
26117c478bd9Sstevel@tonic-gate 				freemsg(pp->msg);
26127c478bd9Sstevel@tonic-gate 				pp->msg = NULL;
26137c478bd9Sstevel@tonic-gate 			}
26147c478bd9Sstevel@tonic-gate 		} else {
26157c478bd9Sstevel@tonic-gate 			/*
26167c478bd9Sstevel@tonic-gate 			 * PIO transfer: disable nAck interrups
26177c478bd9Sstevel@tonic-gate 			 */
26187c478bd9Sstevel@tonic-gate 			dcr = DCR_READ(pp);
26197c478bd9Sstevel@tonic-gate 			dcr &= ~(ECPP_REV_DIR | ECPP_INTR_EN);
26207c478bd9Sstevel@tonic-gate 			DCR_WRITE(pp, dcr);
26217c478bd9Sstevel@tonic-gate 			ECPP_MASK_INTR(pp);
26227c478bd9Sstevel@tonic-gate 		}
26237c478bd9Sstevel@tonic-gate 
26247c478bd9Sstevel@tonic-gate 		/*
26257c478bd9Sstevel@tonic-gate 		 * The transfer is cleaned up.  There may or may not be data
26267c478bd9Sstevel@tonic-gate 		 * in the fifo.  We don't care at this point.  Ie. SuperIO may
26277c478bd9Sstevel@tonic-gate 		 * transfer the remaining bytes in the fifo or not. it doesn't
26287c478bd9Sstevel@tonic-gate 		 * matter.  All that is important at this stage is that no more
26297c478bd9Sstevel@tonic-gate 		 * fifo timers are started.
26307c478bd9Sstevel@tonic-gate 		 */
26317c478bd9Sstevel@tonic-gate 
26327c478bd9Sstevel@tonic-gate 		timeout_id = pp->timeout_id;
26337c478bd9Sstevel@tonic-gate 		fifo_timer_id = pp->fifo_timer_id;
26347c478bd9Sstevel@tonic-gate 		pp->timeout_id = pp->fifo_timer_id = 0;
26357c478bd9Sstevel@tonic-gate 		pp->softintr_pending = 0;
26367c478bd9Sstevel@tonic-gate 
26377c478bd9Sstevel@tonic-gate 		break;
26387c478bd9Sstevel@tonic-gate 
26397c478bd9Sstevel@tonic-gate 	case ECPP_ERR:
26407c478bd9Sstevel@tonic-gate 		/*
26417c478bd9Sstevel@tonic-gate 		 * Change the port status to ECPP_FLUSH to
26427c478bd9Sstevel@tonic-gate 		 * indicate to ecpp_wsrv that the wq is being flushed.
26437c478bd9Sstevel@tonic-gate 		 */
26447c478bd9Sstevel@tonic-gate 		pp->e_busy = ECPP_FLUSH;
26457c478bd9Sstevel@tonic-gate 
26467c478bd9Sstevel@tonic-gate 		/*
26477c478bd9Sstevel@tonic-gate 		 *  Most likely there are mblks in the queue,
26487c478bd9Sstevel@tonic-gate 		 *  but the driver can not transmit because
26497c478bd9Sstevel@tonic-gate 		 *  of the bad port status.  In this case,
26507c478bd9Sstevel@tonic-gate 		 *  ecpp_flush() should make sure ecpp_wsrv_timer()
26517c478bd9Sstevel@tonic-gate 		 *  is turned off.
26527c478bd9Sstevel@tonic-gate 		 */
26537c478bd9Sstevel@tonic-gate 		wsrv_timer_id = pp->wsrv_timer_id;
26547c478bd9Sstevel@tonic-gate 		pp->wsrv_timer_id = 0;
26557c478bd9Sstevel@tonic-gate 
26567c478bd9Sstevel@tonic-gate 		break;
26577c478bd9Sstevel@tonic-gate 
26587c478bd9Sstevel@tonic-gate 	case ECPP_IDLE:
26597c478bd9Sstevel@tonic-gate 		/* No work to do. Ready to flush */
26607c478bd9Sstevel@tonic-gate 		break;
26617c478bd9Sstevel@tonic-gate 
26627c478bd9Sstevel@tonic-gate 	default:
26637c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
266419397407SSherry Moore 		    "ecpp_flush: illegal state %x\n", pp->e_busy);
26657c478bd9Sstevel@tonic-gate 	}
26667c478bd9Sstevel@tonic-gate 
26677c478bd9Sstevel@tonic-gate 	/* in DIAG mode clear TFIFO if needed */
26687c478bd9Sstevel@tonic-gate 	if (pp->current_mode == ECPP_DIAG_MODE) {
26697c478bd9Sstevel@tonic-gate 		ecr = ECR_READ(pp);
26707c478bd9Sstevel@tonic-gate 		if (!(ecr & ECPP_FIFO_EMPTY)) {
26717c478bd9Sstevel@tonic-gate 			ECR_WRITE(pp,
267219397407SSherry Moore 			    ECPP_INTR_SRV | ECPP_INTR_MASK | ECR_mode_001);
26737c478bd9Sstevel@tonic-gate 			ECR_WRITE(pp, ecr);
26747c478bd9Sstevel@tonic-gate 		}
26757c478bd9Sstevel@tonic-gate 	}
26767c478bd9Sstevel@tonic-gate 
26777c478bd9Sstevel@tonic-gate 	/* Discard all messages on the output queue. */
26787c478bd9Sstevel@tonic-gate 	flushq(q, FLUSHDATA);
26797c478bd9Sstevel@tonic-gate 
26807c478bd9Sstevel@tonic-gate 	/* The port is no longer flushing or dma'ing for that matter. */
26817c478bd9Sstevel@tonic-gate 	pp->e_busy = ECPP_IDLE;
26827c478bd9Sstevel@tonic-gate 
26837c478bd9Sstevel@tonic-gate 	/* Set the right phase */
26847c478bd9Sstevel@tonic-gate 	if (pp->current_mode == ECPP_ECP_MODE) {
26857c478bd9Sstevel@tonic-gate 		if (pp->current_phase == ECPP_PHASE_ECP_REV_XFER) {
26867c478bd9Sstevel@tonic-gate 			pp->current_phase = ECPP_PHASE_ECP_REV_IDLE;
26877c478bd9Sstevel@tonic-gate 		} else {
26887c478bd9Sstevel@tonic-gate 			pp->current_phase = ECPP_PHASE_ECP_FWD_IDLE;
26897c478bd9Sstevel@tonic-gate 		}
26907c478bd9Sstevel@tonic-gate 	}
26917c478bd9Sstevel@tonic-gate 
26927c478bd9Sstevel@tonic-gate 	/* cancel timeouts if any */
26937c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->umutex);
26947c478bd9Sstevel@tonic-gate 
26957c478bd9Sstevel@tonic-gate 	if (timeout_id) {
26967c478bd9Sstevel@tonic-gate 		(void) untimeout(timeout_id);
26977c478bd9Sstevel@tonic-gate 	}
26987c478bd9Sstevel@tonic-gate 	if (fifo_timer_id) {
26997c478bd9Sstevel@tonic-gate 		(void) untimeout(fifo_timer_id);
27007c478bd9Sstevel@tonic-gate 	}
27017c478bd9Sstevel@tonic-gate 	if (wsrv_timer_id) {
27027c478bd9Sstevel@tonic-gate 		(void) untimeout(wsrv_timer_id);
27037c478bd9Sstevel@tonic-gate 	}
27047c478bd9Sstevel@tonic-gate 
27057c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->umutex);
27067c478bd9Sstevel@tonic-gate 
27077c478bd9Sstevel@tonic-gate 	cv_signal(&pp->pport_cv);	/* wake up ecpp_close() */
27087c478bd9Sstevel@tonic-gate }
27097c478bd9Sstevel@tonic-gate 
27107c478bd9Sstevel@tonic-gate static void
ecpp_start(struct ecppunit * pp,caddr_t addr,size_t len)27117c478bd9Sstevel@tonic-gate ecpp_start(struct ecppunit *pp, caddr_t addr, size_t len)
27127c478bd9Sstevel@tonic-gate {
27137c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->umutex));
27147c478bd9Sstevel@tonic-gate 	ASSERT(pp->e_busy == ECPP_BUSY);
27157c478bd9Sstevel@tonic-gate 
27167c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip,
271719397407SSherry Moore 	    "ecpp_start:current_mode=%x,current_phase=%x,ecr=%x,len=%d\n",
271819397407SSherry Moore 	    pp->current_mode, pp->current_phase, ECR_READ(pp), len);
27197c478bd9Sstevel@tonic-gate 
27207c478bd9Sstevel@tonic-gate 	pp->dma_dir = DDI_DMA_WRITE;	/* this is a forward transfer */
27217c478bd9Sstevel@tonic-gate 
27227c478bd9Sstevel@tonic-gate 	switch (pp->current_mode) {
27237c478bd9Sstevel@tonic-gate 	case ECPP_NIBBLE_MODE:
27247c478bd9Sstevel@tonic-gate 		(void) ecpp_1284_termination(pp);
27257c478bd9Sstevel@tonic-gate 
27267c478bd9Sstevel@tonic-gate 		/* After termination we are either Compatible or Centronics */
27277c478bd9Sstevel@tonic-gate 
27287c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
27297c478bd9Sstevel@tonic-gate 
27307c478bd9Sstevel@tonic-gate 	case ECPP_CENTRONICS:
27317c478bd9Sstevel@tonic-gate 	case ECPP_COMPAT_MODE:
27327c478bd9Sstevel@tonic-gate 		if (pp->io_mode == ECPP_DMA) {
27337c478bd9Sstevel@tonic-gate 			if (ecpp_init_dma_xfer(pp, addr, len) == FAILURE) {
27347c478bd9Sstevel@tonic-gate 				return;
27357c478bd9Sstevel@tonic-gate 			}
27367c478bd9Sstevel@tonic-gate 		} else {
27377c478bd9Sstevel@tonic-gate 			/* PIO mode */
27387c478bd9Sstevel@tonic-gate 			if (ecpp_prep_pio_xfer(pp, addr, len) == FAILURE) {
27397c478bd9Sstevel@tonic-gate 				return;
27407c478bd9Sstevel@tonic-gate 			}
27417c478bd9Sstevel@tonic-gate 			(void) ecpp_pio_writeb(pp);
27427c478bd9Sstevel@tonic-gate 		}
27437c478bd9Sstevel@tonic-gate 		break;
27447c478bd9Sstevel@tonic-gate 
27457c478bd9Sstevel@tonic-gate 	case ECPP_DIAG_MODE: {
27467c478bd9Sstevel@tonic-gate 		int	oldlen;
27477c478bd9Sstevel@tonic-gate 
27487c478bd9Sstevel@tonic-gate 		/* put superio into TFIFO mode, if not already */
27497c478bd9Sstevel@tonic-gate 		ECR_WRITE(pp, ECPP_INTR_SRV | ECPP_INTR_MASK | ECR_mode_110);
27507c478bd9Sstevel@tonic-gate 		/*
27517c478bd9Sstevel@tonic-gate 		 * DMA would block if the TFIFO is not empty
27527c478bd9Sstevel@tonic-gate 		 * if by this moment nobody read these bytes, they`re gone
27537c478bd9Sstevel@tonic-gate 		 */
27547c478bd9Sstevel@tonic-gate 		drv_usecwait(1);
27557c478bd9Sstevel@tonic-gate 		if (!(ECR_READ(pp) & ECPP_FIFO_EMPTY)) {
27567c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
275719397407SSherry Moore 			    "ecpp_start: TFIFO not empty, clearing\n");
27587c478bd9Sstevel@tonic-gate 			ECR_WRITE(pp,
275919397407SSherry Moore 			    ECPP_INTR_SRV | ECPP_INTR_MASK | ECR_mode_001);
27607c478bd9Sstevel@tonic-gate 			ECR_WRITE(pp,
276119397407SSherry Moore 			    ECPP_INTR_SRV | ECPP_INTR_MASK | ECR_mode_110);
27627c478bd9Sstevel@tonic-gate 		}
27637c478bd9Sstevel@tonic-gate 
27647c478bd9Sstevel@tonic-gate 		/* we can DMA at most 16 bytes into TFIFO */
27657c478bd9Sstevel@tonic-gate 		oldlen = len;
27667c478bd9Sstevel@tonic-gate 		if (len > ECPP_FIFO_SZ) {
27677c478bd9Sstevel@tonic-gate 			len = ECPP_FIFO_SZ;
27687c478bd9Sstevel@tonic-gate 		}
27697c478bd9Sstevel@tonic-gate 
27707c478bd9Sstevel@tonic-gate 		if (ecpp_init_dma_xfer(pp, addr, len) == FAILURE) {
27717c478bd9Sstevel@tonic-gate 			return;
27727c478bd9Sstevel@tonic-gate 		}
27737c478bd9Sstevel@tonic-gate 
27747c478bd9Sstevel@tonic-gate 		/* put the rest of data back on the queue */
27757c478bd9Sstevel@tonic-gate 		if (oldlen > len) {
27767c478bd9Sstevel@tonic-gate 			ecpp_putback_untransfered(pp, addr + len, oldlen - len);
27777c478bd9Sstevel@tonic-gate 		}
27787c478bd9Sstevel@tonic-gate 
27797c478bd9Sstevel@tonic-gate 		break;
27807c478bd9Sstevel@tonic-gate 	}
27817c478bd9Sstevel@tonic-gate 
27827c478bd9Sstevel@tonic-gate 	case ECPP_ECP_MODE:
27837c478bd9Sstevel@tonic-gate 		ASSERT(pp->current_phase == ECPP_PHASE_ECP_FWD_IDLE ||
278419397407SSherry Moore 		    pp->current_phase == ECPP_PHASE_ECP_REV_IDLE);
27857c478bd9Sstevel@tonic-gate 
27867c478bd9Sstevel@tonic-gate 		/* if in Reverse Phase negotiate to Forward */
27877c478bd9Sstevel@tonic-gate 		if (pp->current_phase == ECPP_PHASE_ECP_REV_IDLE) {
27887c478bd9Sstevel@tonic-gate 			if (ecp_reverse2forward(pp) == FAILURE) {
27897c478bd9Sstevel@tonic-gate 				if (pp->msg) {
27907c478bd9Sstevel@tonic-gate 					(void) putbq(pp->writeq, pp->msg);
27917c478bd9Sstevel@tonic-gate 				} else {
27927c478bd9Sstevel@tonic-gate 					ecpp_putback_untransfered(pp,
279319397407SSherry Moore 					    addr, len);
27947c478bd9Sstevel@tonic-gate 				}
27957c478bd9Sstevel@tonic-gate 			}
27967c478bd9Sstevel@tonic-gate 		}
27977c478bd9Sstevel@tonic-gate 
27987c478bd9Sstevel@tonic-gate 		if (ecpp_init_dma_xfer(pp, addr, len) == FAILURE) {
27997c478bd9Sstevel@tonic-gate 			return;
28007c478bd9Sstevel@tonic-gate 		}
28017c478bd9Sstevel@tonic-gate 
28027c478bd9Sstevel@tonic-gate 		break;
28037c478bd9Sstevel@tonic-gate 	}
28047c478bd9Sstevel@tonic-gate 
28057c478bd9Sstevel@tonic-gate 	/* schedule transfer timeout */
28067c478bd9Sstevel@tonic-gate 	pp->timeout_id = timeout(ecpp_xfer_timeout, (caddr_t)pp,
280719397407SSherry Moore 	    pp->xfer_parms.write_timeout * drv_usectohz(1000000));
28087c478bd9Sstevel@tonic-gate }
28097c478bd9Sstevel@tonic-gate 
28107c478bd9Sstevel@tonic-gate /*
28117c478bd9Sstevel@tonic-gate  * Transfer a PIO "block" a byte at a time.
28127c478bd9Sstevel@tonic-gate  * The block is starts at addr and ends at pp->last_byte
28137c478bd9Sstevel@tonic-gate  */
28147c478bd9Sstevel@tonic-gate static uint8_t
ecpp_prep_pio_xfer(struct ecppunit * pp,caddr_t addr,size_t len)28157c478bd9Sstevel@tonic-gate ecpp_prep_pio_xfer(struct ecppunit *pp, caddr_t addr, size_t len)
28167c478bd9Sstevel@tonic-gate {
28177c478bd9Sstevel@tonic-gate 	pp->next_byte = addr;
28187c478bd9Sstevel@tonic-gate 	pp->last_byte = (caddr_t)((ulong_t)addr + len);
28197c478bd9Sstevel@tonic-gate 
28207c478bd9Sstevel@tonic-gate 	if (ecpp_check_status(pp) == FAILURE) {
28217c478bd9Sstevel@tonic-gate 		/*
28227c478bd9Sstevel@tonic-gate 		 * if status signals are bad, do not start PIO,
28237c478bd9Sstevel@tonic-gate 		 * put everything back on the queue.
28247c478bd9Sstevel@tonic-gate 		 */
28257c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
282619397407SSherry Moore 		    "ecpp_prep_pio_xfer:suspend PIO len=%d\n", len);
28277c478bd9Sstevel@tonic-gate 
28287c478bd9Sstevel@tonic-gate 		if (pp->msg != NULL) {
28297c478bd9Sstevel@tonic-gate 			/*
28307c478bd9Sstevel@tonic-gate 			 * this circumstance we want to copy the
28317c478bd9Sstevel@tonic-gate 			 * untransfered section of msg to a new mblk,
28327c478bd9Sstevel@tonic-gate 			 * then free the orignal one.
28337c478bd9Sstevel@tonic-gate 			 */
28347c478bd9Sstevel@tonic-gate 			ecpp_putback_untransfered(pp,
283519397407SSherry Moore 			    (void *)pp->msg->b_rptr, len);
28367c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
283719397407SSherry Moore 			    "ecpp_prep_pio_xfer: len1=%d\n", len);
28387c478bd9Sstevel@tonic-gate 
28397c478bd9Sstevel@tonic-gate 			freemsg(pp->msg);
28407c478bd9Sstevel@tonic-gate 			pp->msg = NULL;
28417c478bd9Sstevel@tonic-gate 		} else {
28427c478bd9Sstevel@tonic-gate 			ecpp_putback_untransfered(pp, pp->ioblock, len);
28437c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
284419397407SSherry Moore 			    "ecpp_prep_pio_xfer: len2=%d\n", len);
28457c478bd9Sstevel@tonic-gate 		}
28467c478bd9Sstevel@tonic-gate 		qenable(pp->writeq);
28477c478bd9Sstevel@tonic-gate 
28487c478bd9Sstevel@tonic-gate 		return (FAILURE);
28497c478bd9Sstevel@tonic-gate 	}
28507c478bd9Sstevel@tonic-gate 
28517c478bd9Sstevel@tonic-gate 	pp->dma_cancelled = FALSE;
28527c478bd9Sstevel@tonic-gate 
28537c478bd9Sstevel@tonic-gate 	/* pport must be in PIO mode */
28547c478bd9Sstevel@tonic-gate 	if (ecr_write(pp, ECR_mode_001 |
285519397407SSherry Moore 	    ECPP_INTR_MASK | ECPP_INTR_SRV) != SUCCESS) {
28567c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_prep_pio_xfer: failed w/ECR.\n");
28577c478bd9Sstevel@tonic-gate 	}
28587c478bd9Sstevel@tonic-gate 
28597c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecpp_prep_pio_xfer: dcr=%x ecr=%x\n",
286019397407SSherry Moore 	    DCR_READ(pp), ECR_READ(pp));
28617c478bd9Sstevel@tonic-gate 
28627c478bd9Sstevel@tonic-gate 	return (SUCCESS);
28637c478bd9Sstevel@tonic-gate }
28647c478bd9Sstevel@tonic-gate 
28657c478bd9Sstevel@tonic-gate static uint8_t
ecpp_init_dma_xfer(struct ecppunit * pp,caddr_t addr,size_t len)28667c478bd9Sstevel@tonic-gate ecpp_init_dma_xfer(struct ecppunit *pp, caddr_t addr, size_t len)
28677c478bd9Sstevel@tonic-gate {
28687c478bd9Sstevel@tonic-gate 	uint8_t ecr_mode[] = {
28697c478bd9Sstevel@tonic-gate 		0,
28707c478bd9Sstevel@tonic-gate 		ECR_mode_010,	/* Centronix */
28717c478bd9Sstevel@tonic-gate 		ECR_mode_010,	/* Compat */
28727c478bd9Sstevel@tonic-gate 		0,		/* Byte */
28737c478bd9Sstevel@tonic-gate 		0,		/* Nibble */
28747c478bd9Sstevel@tonic-gate 		ECR_mode_011,	/* ECP */
28757c478bd9Sstevel@tonic-gate 		0,		/* Failure */
28767c478bd9Sstevel@tonic-gate 		ECR_mode_110,	/* Diag */
28777c478bd9Sstevel@tonic-gate 	};
28787c478bd9Sstevel@tonic-gate 	uint8_t	ecr;
28797c478bd9Sstevel@tonic-gate 
28807c478bd9Sstevel@tonic-gate 	ASSERT((pp->current_mode <= ECPP_DIAG_MODE) &&
288119397407SSherry Moore 	    (ecr_mode[pp->current_mode] != 0));
28827c478bd9Sstevel@tonic-gate 
28837c478bd9Sstevel@tonic-gate 	if (ecpp_setup_dma_resources(pp, addr, len) == FAILURE) {
28847c478bd9Sstevel@tonic-gate 		qenable(pp->writeq);
28857c478bd9Sstevel@tonic-gate 		return (FAILURE);
28867c478bd9Sstevel@tonic-gate 	}
28877c478bd9Sstevel@tonic-gate 
28887c478bd9Sstevel@tonic-gate 	if (ecpp_check_status(pp) == FAILURE) {
28897c478bd9Sstevel@tonic-gate 		/*
28907c478bd9Sstevel@tonic-gate 		 * if status signals are bad, do not start DMA, but
28917c478bd9Sstevel@tonic-gate 		 * rather put everything back on the queue.
28927c478bd9Sstevel@tonic-gate 		 */
28937c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
289419397407SSherry Moore 		    "ecpp_init_dma_xfer: suspending DMA len=%d\n",
289519397407SSherry Moore 		    pp->dma_cookie.dmac_size);
28967c478bd9Sstevel@tonic-gate 
28977c478bd9Sstevel@tonic-gate 		if (pp->msg != NULL) {
28987c478bd9Sstevel@tonic-gate 			/*
28997c478bd9Sstevel@tonic-gate 			 * this circumstance we want to copy the
29007c478bd9Sstevel@tonic-gate 			 * untransfered section of msg to a new mblk,
29017c478bd9Sstevel@tonic-gate 			 * then free the orignal one.
29027c478bd9Sstevel@tonic-gate 			 */
29037c478bd9Sstevel@tonic-gate 			ecpp_putback_untransfered(pp,
290419397407SSherry Moore 			    (void *)pp->msg->b_rptr, len);
29057c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
290619397407SSherry Moore 			    "ecpp_init_dma_xfer:a:len=%d\n", len);
29077c478bd9Sstevel@tonic-gate 
29087c478bd9Sstevel@tonic-gate 			freemsg(pp->msg);
29097c478bd9Sstevel@tonic-gate 			pp->msg = NULL;
29107c478bd9Sstevel@tonic-gate 		} else {
29117c478bd9Sstevel@tonic-gate 			ecpp_putback_untransfered(pp, pp->ioblock, len);
29127c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
291319397407SSherry Moore 			    "ecpp_init_dma_xfer:b:len=%d\n", len);
29147c478bd9Sstevel@tonic-gate 		}
29157c478bd9Sstevel@tonic-gate 
29167c478bd9Sstevel@tonic-gate 		if (ddi_dma_unbind_handle(pp->dma_handle) != DDI_SUCCESS) {
29177c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
291819397407SSherry Moore 			    "ecpp_init_dma_xfer: unbind FAILURE.\n");
29197c478bd9Sstevel@tonic-gate 		}
29207c478bd9Sstevel@tonic-gate 		qenable(pp->writeq);
29217c478bd9Sstevel@tonic-gate 		return (FAILURE);
29227c478bd9Sstevel@tonic-gate 	}
29237c478bd9Sstevel@tonic-gate 
29247c478bd9Sstevel@tonic-gate 	pp->xfercnt = pp->resid = len;
29257c478bd9Sstevel@tonic-gate 	pp->dma_cancelled = FALSE;
29267c478bd9Sstevel@tonic-gate 	pp->tfifo_intr = 0;
29277c478bd9Sstevel@tonic-gate 
29287c478bd9Sstevel@tonic-gate 	/* set the right ECR mode and disable DMA */
29297c478bd9Sstevel@tonic-gate 	ecr = ecr_mode[pp->current_mode];
29307c478bd9Sstevel@tonic-gate 	(void) ecr_write(pp, ecr | ECPP_INTR_SRV | ECPP_INTR_MASK);
29317c478bd9Sstevel@tonic-gate 
29327c478bd9Sstevel@tonic-gate 	/* prepare DMAC for a transfer */
29337c478bd9Sstevel@tonic-gate 	if (ECPP_DMA_START(pp) == FAILURE) {
29347c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_init_dma_xfer: dma_start FAILED.\n");
29357c478bd9Sstevel@tonic-gate 		return (FAILURE);
29367c478bd9Sstevel@tonic-gate 	}
29377c478bd9Sstevel@tonic-gate 
29387c478bd9Sstevel@tonic-gate 	/* GO! */
29397c478bd9Sstevel@tonic-gate 	(void) ecr_write(pp, ecr | ECPP_DMA_ENABLE | ECPP_INTR_MASK);
29407c478bd9Sstevel@tonic-gate 
29417c478bd9Sstevel@tonic-gate 	return (SUCCESS);
29427c478bd9Sstevel@tonic-gate }
29437c478bd9Sstevel@tonic-gate 
29447c478bd9Sstevel@tonic-gate static uint8_t
ecpp_setup_dma_resources(struct ecppunit * pp,caddr_t addr,size_t len)29457c478bd9Sstevel@tonic-gate ecpp_setup_dma_resources(struct ecppunit *pp, caddr_t addr, size_t len)
29467c478bd9Sstevel@tonic-gate {
29477c478bd9Sstevel@tonic-gate 	int	err;
29487c478bd9Sstevel@tonic-gate 	off_t	woff;
29497c478bd9Sstevel@tonic-gate 	size_t	wlen;
29507c478bd9Sstevel@tonic-gate 
29517c478bd9Sstevel@tonic-gate 	ASSERT(pp->dma_dir == DDI_DMA_READ || pp->dma_dir == DDI_DMA_WRITE);
29527c478bd9Sstevel@tonic-gate 
29537c478bd9Sstevel@tonic-gate 	err = ddi_dma_addr_bind_handle(pp->dma_handle, NULL,
295419397407SSherry Moore 	    addr, len, pp->dma_dir | DDI_DMA_PARTIAL,
295519397407SSherry Moore 	    DDI_DMA_DONTWAIT, NULL,
295619397407SSherry Moore 	    &pp->dma_cookie, &pp->dma_cookie_count);
29577c478bd9Sstevel@tonic-gate 
29587c478bd9Sstevel@tonic-gate 	switch (err) {
29597c478bd9Sstevel@tonic-gate 	case DDI_DMA_MAPPED:
29607c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_setup_dma: DMA_MAPPED\n");
29617c478bd9Sstevel@tonic-gate 
29627c478bd9Sstevel@tonic-gate 		pp->dma_nwin = 1;
29637c478bd9Sstevel@tonic-gate 		pp->dma_curwin = 1;
29647c478bd9Sstevel@tonic-gate 		break;
29657c478bd9Sstevel@tonic-gate 
29667c478bd9Sstevel@tonic-gate 	case DDI_DMA_PARTIAL_MAP: {
29677c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_setup_dma: DMA_PARTIAL_MAP\n");
29687c478bd9Sstevel@tonic-gate 
29697c478bd9Sstevel@tonic-gate 		if (ddi_dma_numwin(pp->dma_handle,
297019397407SSherry Moore 		    &pp->dma_nwin) != DDI_SUCCESS) {
29717c478bd9Sstevel@tonic-gate 			(void) ddi_dma_unbind_handle(pp->dma_handle);
29727c478bd9Sstevel@tonic-gate 			return (FAILURE);
29737c478bd9Sstevel@tonic-gate 		}
29747c478bd9Sstevel@tonic-gate 		pp->dma_curwin = 1;
29757c478bd9Sstevel@tonic-gate 
29767c478bd9Sstevel@tonic-gate 		/*
29777c478bd9Sstevel@tonic-gate 		 * The very first window is returned by bind_handle,
29787c478bd9Sstevel@tonic-gate 		 * but we must do this explicitly here, otherwise
29797c478bd9Sstevel@tonic-gate 		 * next getwin would return wrong cookie dmac_size
29807c478bd9Sstevel@tonic-gate 		 */
29817c478bd9Sstevel@tonic-gate 		if (ddi_dma_getwin(pp->dma_handle, 0, &woff, &wlen,
29827c478bd9Sstevel@tonic-gate 		    &pp->dma_cookie, &pp->dma_cookie_count) != DDI_SUCCESS) {
29837c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
298419397407SSherry Moore 			    "ecpp_setup_dma: ddi_dma_getwin failed!");
29857c478bd9Sstevel@tonic-gate 			(void) ddi_dma_unbind_handle(pp->dma_handle);
29867c478bd9Sstevel@tonic-gate 			return (FAILURE);
29877c478bd9Sstevel@tonic-gate 		}
29887c478bd9Sstevel@tonic-gate 
29897c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
299019397407SSherry Moore 		    "ecpp_setup_dma: cookies=%d, windows=%d"
299119397407SSherry Moore 		    " addr=%lx len=%d\n",
299219397407SSherry Moore 		    pp->dma_cookie_count, pp->dma_nwin,
299319397407SSherry Moore 		    pp->dma_cookie.dmac_address, pp->dma_cookie.dmac_size);
29947c478bd9Sstevel@tonic-gate 
29957c478bd9Sstevel@tonic-gate 		break;
29967c478bd9Sstevel@tonic-gate 	}
29977c478bd9Sstevel@tonic-gate 
29987c478bd9Sstevel@tonic-gate 	default:
29997c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_setup_dma: err=%x\n", err);
30007c478bd9Sstevel@tonic-gate 		return (FAILURE);
30017c478bd9Sstevel@tonic-gate 	}
30027c478bd9Sstevel@tonic-gate 
30037c478bd9Sstevel@tonic-gate 	return (SUCCESS);
30047c478bd9Sstevel@tonic-gate }
30057c478bd9Sstevel@tonic-gate 
30067c478bd9Sstevel@tonic-gate static void
ecpp_ack_ioctl(queue_t * q,mblk_t * mp)30077c478bd9Sstevel@tonic-gate ecpp_ack_ioctl(queue_t *q, mblk_t *mp)
30087c478bd9Sstevel@tonic-gate {
30097c478bd9Sstevel@tonic-gate 	struct iocblk  *iocbp;
30107c478bd9Sstevel@tonic-gate 
30117c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = M_IOCACK;
30127c478bd9Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + sizeof (struct iocblk);
30137c478bd9Sstevel@tonic-gate 
30147c478bd9Sstevel@tonic-gate 	if (mp->b_cont) {
30157c478bd9Sstevel@tonic-gate 		freemsg(mp->b_cont);
30167c478bd9Sstevel@tonic-gate 		mp->b_cont = NULL;
30177c478bd9Sstevel@tonic-gate 	}
30187c478bd9Sstevel@tonic-gate 
30197c478bd9Sstevel@tonic-gate 	iocbp = (struct iocblk *)mp->b_rptr;
30207c478bd9Sstevel@tonic-gate 	iocbp->ioc_error = 0;
30217c478bd9Sstevel@tonic-gate 	iocbp->ioc_count = 0;
30227c478bd9Sstevel@tonic-gate 	iocbp->ioc_rval = 0;
30237c478bd9Sstevel@tonic-gate 
30247c478bd9Sstevel@tonic-gate 	qreply(q, mp);
30257c478bd9Sstevel@tonic-gate }
30267c478bd9Sstevel@tonic-gate 
30277c478bd9Sstevel@tonic-gate static void
ecpp_nack_ioctl(queue_t * q,mblk_t * mp,int err)30287c478bd9Sstevel@tonic-gate ecpp_nack_ioctl(queue_t *q, mblk_t *mp, int err)
30297c478bd9Sstevel@tonic-gate {
30307c478bd9Sstevel@tonic-gate 	struct iocblk  *iocbp;
30317c478bd9Sstevel@tonic-gate 
30327c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = M_IOCNAK;
30337c478bd9Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + sizeof (struct iocblk);
30347c478bd9Sstevel@tonic-gate 	iocbp = (struct iocblk *)mp->b_rptr;
30357c478bd9Sstevel@tonic-gate 	iocbp->ioc_error = err;
30367c478bd9Sstevel@tonic-gate 
30377c478bd9Sstevel@tonic-gate 	if (mp->b_cont) {
30387c478bd9Sstevel@tonic-gate 		freemsg(mp->b_cont);
30397c478bd9Sstevel@tonic-gate 		mp->b_cont = NULL;
30407c478bd9Sstevel@tonic-gate 	}
30417c478bd9Sstevel@tonic-gate 
30427c478bd9Sstevel@tonic-gate 	qreply(q, mp);
30437c478bd9Sstevel@tonic-gate }
30447c478bd9Sstevel@tonic-gate 
30457c478bd9Sstevel@tonic-gate uint_t
ecpp_isr(caddr_t arg)30467c478bd9Sstevel@tonic-gate ecpp_isr(caddr_t arg)
30477c478bd9Sstevel@tonic-gate {
30487c478bd9Sstevel@tonic-gate 	struct ecppunit *pp = (struct ecppunit *)(void *)arg;
30497c478bd9Sstevel@tonic-gate 	uint32_t	dcsr;
30507c478bd9Sstevel@tonic-gate 	uint8_t		dsr;
30517c478bd9Sstevel@tonic-gate 	int		cheerio_pend_counter;
30527c478bd9Sstevel@tonic-gate 	int		retval = DDI_INTR_UNCLAIMED;
30537c478bd9Sstevel@tonic-gate 	hrtime_t	now;
30547c478bd9Sstevel@tonic-gate 
3055*f70049b7SToomas Soome 	dsr = 0;
3056*f70049b7SToomas Soome 	dcsr = 0;
30577c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->umutex);
30587c478bd9Sstevel@tonic-gate 	/*
30597c478bd9Sstevel@tonic-gate 	 * interrupt may occur while other thread is holding the lock
30607c478bd9Sstevel@tonic-gate 	 * and cancels DMA transfer (e.g. ecpp_flush())
30617c478bd9Sstevel@tonic-gate 	 * since it cannot cancel the interrupt thread,
30627c478bd9Sstevel@tonic-gate 	 * it just sets dma_cancelled to TRUE,
30637c478bd9Sstevel@tonic-gate 	 * telling interrupt handler to exit immediately
30647c478bd9Sstevel@tonic-gate 	 */
30657c478bd9Sstevel@tonic-gate 	if (pp->dma_cancelled == TRUE) {
30667c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "dma-cancel isr\n");
30677c478bd9Sstevel@tonic-gate 
30687c478bd9Sstevel@tonic-gate 		pp->intr_hard++;
30697c478bd9Sstevel@tonic-gate 		pp->dma_cancelled = FALSE;
30707c478bd9Sstevel@tonic-gate 
30717c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
30727c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
30737c478bd9Sstevel@tonic-gate 	}
30747c478bd9Sstevel@tonic-gate 
30757c478bd9Sstevel@tonic-gate 	/* Southbridge interrupts are handled separately */
30767c478bd9Sstevel@tonic-gate #if defined(__x86)
30777c478bd9Sstevel@tonic-gate 	if (pp->hw == &x86)
30787c478bd9Sstevel@tonic-gate #else
30797c478bd9Sstevel@tonic-gate 	if (pp->hw == &m1553)
30807c478bd9Sstevel@tonic-gate #endif
30817c478bd9Sstevel@tonic-gate 	{
30827c478bd9Sstevel@tonic-gate 		retval = ecpp_M1553_intr(pp);
30837c478bd9Sstevel@tonic-gate 		if (retval == DDI_INTR_UNCLAIMED) {
30847c478bd9Sstevel@tonic-gate 			goto unexpected;
30857c478bd9Sstevel@tonic-gate 		}
30867c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
30877c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
30887c478bd9Sstevel@tonic-gate 	}
30897c478bd9Sstevel@tonic-gate 
30907c478bd9Sstevel@tonic-gate 	/*
30917c478bd9Sstevel@tonic-gate 	 * the intr is through the motherboard. it is faster than PCI route.
30927c478bd9Sstevel@tonic-gate 	 * sometimes ecpp_isr() is invoked before cheerio csr is updated.
30937c478bd9Sstevel@tonic-gate 	 */
30947c478bd9Sstevel@tonic-gate 	cheerio_pend_counter = ecpp_isr_max_delay;
30957c478bd9Sstevel@tonic-gate 	dcsr = GET_DMAC_CSR(pp);
30967c478bd9Sstevel@tonic-gate 
30977c478bd9Sstevel@tonic-gate 	while (!(dcsr & DCSR_INT_PEND) && cheerio_pend_counter-- > 0) {
30987c478bd9Sstevel@tonic-gate 		drv_usecwait(1);
30997c478bd9Sstevel@tonic-gate 		dcsr = GET_DMAC_CSR(pp);
31007c478bd9Sstevel@tonic-gate 	}
31017c478bd9Sstevel@tonic-gate 
31027c478bd9Sstevel@tonic-gate 	/*
31037c478bd9Sstevel@tonic-gate 	 * This is a workaround for what seems to be a timing problem
31047c478bd9Sstevel@tonic-gate 	 * with the delivery of interrupts and CSR updating with the
31057c478bd9Sstevel@tonic-gate 	 * ebus2 csr, superio and the n_ERR pin from the peripheral.
31067c478bd9Sstevel@tonic-gate 	 *
31077c478bd9Sstevel@tonic-gate 	 * delay is not needed for PIO mode
31087c478bd9Sstevel@tonic-gate 	 */
31097c478bd9Sstevel@tonic-gate 	if (!COMPAT_PIO(pp)) {
31107c478bd9Sstevel@tonic-gate 		drv_usecwait(100);
31117c478bd9Sstevel@tonic-gate 		dcsr = GET_DMAC_CSR(pp);
31127c478bd9Sstevel@tonic-gate 	}
31137c478bd9Sstevel@tonic-gate 
31147c478bd9Sstevel@tonic-gate 	/* on 97317 in Extended mode IRQ_ST of DSR is deasserted when read */
31157c478bd9Sstevel@tonic-gate 	dsr = DSR_READ(pp);
31167c478bd9Sstevel@tonic-gate 
31177c478bd9Sstevel@tonic-gate 	/*
31187c478bd9Sstevel@tonic-gate 	 * check if interrupt is for this device:
31197c478bd9Sstevel@tonic-gate 	 * it should be reflected either in cheerio DCSR register
31207c478bd9Sstevel@tonic-gate 	 * or in IRQ_ST bit of DSR on 97317
31217c478bd9Sstevel@tonic-gate 	 */
31227c478bd9Sstevel@tonic-gate 	if ((dcsr & DCSR_INT_PEND) == 0) {
31237c478bd9Sstevel@tonic-gate 		if (pp->hw != &pc97317) {
31247c478bd9Sstevel@tonic-gate 			goto unclaimed;
31257c478bd9Sstevel@tonic-gate 		}
31267c478bd9Sstevel@tonic-gate 		/*
31277c478bd9Sstevel@tonic-gate 		 * on Excalibur, reading DSR will deassert SuperIO IRQx line
31287c478bd9Sstevel@tonic-gate 		 * RIO's DCSR_INT_PEND seems to follow IRQx transitions,
31297c478bd9Sstevel@tonic-gate 		 * so if DSR is read after interrupt occured, but before
31307c478bd9Sstevel@tonic-gate 		 * we get here, IRQx and hence INT_PEND will be deasserted
31317c478bd9Sstevel@tonic-gate 		 * as a result, we can miss a service interrupt in PIO mode
31327c478bd9Sstevel@tonic-gate 		 *
31337c478bd9Sstevel@tonic-gate 		 * malicious DSR reader is BPPIOC_TESTIO, which is called
31347c478bd9Sstevel@tonic-gate 		 * by LP in between data blocks to check printer status
31357c478bd9Sstevel@tonic-gate 		 * this workaround lets us not to miss an interrupt
31367c478bd9Sstevel@tonic-gate 		 *
31377c478bd9Sstevel@tonic-gate 		 * also, nErr interrupt (ECP mode) not always reflected in DCSR
31387c478bd9Sstevel@tonic-gate 		 */
31397c478bd9Sstevel@tonic-gate 		if (((dsr & ECPP_IRQ_ST) == 0) ||
31407c478bd9Sstevel@tonic-gate 		    ((COMPAT_PIO(pp)) && (pp->e_busy == ECPP_BUSY)) ||
31417c478bd9Sstevel@tonic-gate 		    (((dsr & ECPP_nERR) == 0) &&
31427c478bd9Sstevel@tonic-gate 		    (pp->current_mode == ECPP_ECP_MODE))) {
31437c478bd9Sstevel@tonic-gate 			dcsr = 0;
31447c478bd9Sstevel@tonic-gate 		} else {
31457c478bd9Sstevel@tonic-gate 			goto unclaimed;
31467c478bd9Sstevel@tonic-gate 		}
31477c478bd9Sstevel@tonic-gate 	}
31487c478bd9Sstevel@tonic-gate 
31497c478bd9Sstevel@tonic-gate 	pp->intr_hard++;
31507c478bd9Sstevel@tonic-gate 
31517c478bd9Sstevel@tonic-gate 	/* the intr is for us - check all possible interrupt sources */
31527c478bd9Sstevel@tonic-gate 	if (dcsr & DCSR_ERR_PEND) {
31537c478bd9Sstevel@tonic-gate 		size_t	bcr;
31547c478bd9Sstevel@tonic-gate 
31557c478bd9Sstevel@tonic-gate 		/* we are expecting a data transfer interrupt */
31567c478bd9Sstevel@tonic-gate 		ASSERT(pp->e_busy == ECPP_BUSY);
31577c478bd9Sstevel@tonic-gate 
31587c478bd9Sstevel@tonic-gate 		/*
31597c478bd9Sstevel@tonic-gate 		 * some kind of DMA error
31607c478bd9Sstevel@tonic-gate 		 */
31617c478bd9Sstevel@tonic-gate 		if (ECPP_DMA_STOP(pp, &bcr) == FAILURE) {
31627c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "ecpp_isr: dma_stop failed\n");
31637c478bd9Sstevel@tonic-gate 		}
31647c478bd9Sstevel@tonic-gate 
31657c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_isr: DMAC ERROR bcr=%d\n", bcr);
31667c478bd9Sstevel@tonic-gate 
31677c478bd9Sstevel@tonic-gate 		ecpp_xfer_cleanup(pp);
31687c478bd9Sstevel@tonic-gate 
31697c478bd9Sstevel@tonic-gate 		if (ddi_dma_unbind_handle(pp->dma_handle) != DDI_SUCCESS) {
31707c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "ecpp_isr(e): unbind failed\n");
31717c478bd9Sstevel@tonic-gate 		}
31727c478bd9Sstevel@tonic-gate 
31737c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
31747c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
31757c478bd9Sstevel@tonic-gate 	}
31767c478bd9Sstevel@tonic-gate 
31777c478bd9Sstevel@tonic-gate 	if (dcsr & DCSR_TC) {
31787c478bd9Sstevel@tonic-gate 		retval = ecpp_dma_ihdlr(pp);
31797c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
31807c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
31817c478bd9Sstevel@tonic-gate 	}
31827c478bd9Sstevel@tonic-gate 
31837c478bd9Sstevel@tonic-gate 	if (COMPAT_PIO(pp)) {
31847c478bd9Sstevel@tonic-gate 		retval = ecpp_pio_ihdlr(pp);
31857c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
31867c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
31877c478bd9Sstevel@tonic-gate 	}
31887c478bd9Sstevel@tonic-gate 
31897c478bd9Sstevel@tonic-gate 	/* does peripheral need attention? */
31907c478bd9Sstevel@tonic-gate 	if ((dsr & ECPP_nERR) == 0) {
31917c478bd9Sstevel@tonic-gate 		retval = ecpp_nErr_ihdlr(pp);
31927c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
31937c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
31947c478bd9Sstevel@tonic-gate 	}
31957c478bd9Sstevel@tonic-gate 
31967c478bd9Sstevel@tonic-gate 	pp->intr_hard--;
31977c478bd9Sstevel@tonic-gate 
31987c478bd9Sstevel@tonic-gate unexpected:
31997c478bd9Sstevel@tonic-gate 
32007c478bd9Sstevel@tonic-gate 	pp->intr_spurious++;
32017c478bd9Sstevel@tonic-gate 
32027c478bd9Sstevel@tonic-gate 	/*
32037c478bd9Sstevel@tonic-gate 	 * The following procedure tries to prevent soft hangs
32047c478bd9Sstevel@tonic-gate 	 * in event of peripheral/superio misbehaviour:
32057c478bd9Sstevel@tonic-gate 	 * if number of unexpected interrupts in the last SPUR_PERIOD ns
32067c478bd9Sstevel@tonic-gate 	 * exceeded SPUR_CRITICAL, then shut up interrupts
32077c478bd9Sstevel@tonic-gate 	 */
32087c478bd9Sstevel@tonic-gate 	now = gethrtime();
32097c478bd9Sstevel@tonic-gate 	if (pp->lastspur == 0 || now - pp->lastspur > SPUR_PERIOD) {
32107c478bd9Sstevel@tonic-gate 		/* last unexpected interrupt was long ago */
32117c478bd9Sstevel@tonic-gate 		pp->lastspur = now;
32127c478bd9Sstevel@tonic-gate 		pp->nspur = 1;
32137c478bd9Sstevel@tonic-gate 	} else {
32147c478bd9Sstevel@tonic-gate 		/* last unexpected interrupt was recently */
32157c478bd9Sstevel@tonic-gate 		pp->nspur++;
32167c478bd9Sstevel@tonic-gate 	}
32177c478bd9Sstevel@tonic-gate 
32187c478bd9Sstevel@tonic-gate 	if (pp->nspur >= SPUR_CRITICAL) {
32197c478bd9Sstevel@tonic-gate 		ECPP_MASK_INTR(pp);
32207c478bd9Sstevel@tonic-gate 		ECR_WRITE(pp, ECR_READ(pp) | ECPP_INTR_MASK | ECPP_INTR_SRV);
32217c478bd9Sstevel@tonic-gate 		pp->nspur = 0;
32227c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE, "%s%d: too many interrupt requests",
322319397407SSherry Moore 		    ddi_get_name(pp->dip), ddi_get_instance(pp->dip));
32247c478bd9Sstevel@tonic-gate 	} else {
32257c478bd9Sstevel@tonic-gate 		ECR_WRITE(pp, ECR_READ(pp) | ECPP_INTR_SRV | ECPP_INTR_MASK);
32267c478bd9Sstevel@tonic-gate 	}
32277c478bd9Sstevel@tonic-gate 
32287c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip,
322919397407SSherry Moore 	    "isr:unknown: dcsr=%x ecr=%x dsr=%x dcr=%x\nmode=%x phase=%x\n",
323019397407SSherry Moore 	    dcsr, ECR_READ(pp), dsr, DCR_READ(pp),
323119397407SSherry Moore 	    pp->current_mode, pp->current_phase);
32327c478bd9Sstevel@tonic-gate 
32337c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->umutex);
32347c478bd9Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
32357c478bd9Sstevel@tonic-gate 
32367c478bd9Sstevel@tonic-gate unclaimed:
32377c478bd9Sstevel@tonic-gate 
32387c478bd9Sstevel@tonic-gate 	pp->intr_spurious++;
32397c478bd9Sstevel@tonic-gate 
32407c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip,
324119397407SSherry Moore 	    "isr:UNCL: dcsr=%x ecr=%x dsr=%x dcr=%x\nmode=%x phase=%x\n",
324219397407SSherry Moore 	    dcsr, ECR_READ(pp), DSR_READ(pp), DCR_READ(pp),
324319397407SSherry Moore 	    pp->current_mode, pp->current_phase);
32447c478bd9Sstevel@tonic-gate 
32457c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->umutex);
32467c478bd9Sstevel@tonic-gate 	return (DDI_INTR_UNCLAIMED);
32477c478bd9Sstevel@tonic-gate }
32487c478bd9Sstevel@tonic-gate 
32497c478bd9Sstevel@tonic-gate /*
32507c478bd9Sstevel@tonic-gate  * M1553 intr handler
32517c478bd9Sstevel@tonic-gate  */
32527c478bd9Sstevel@tonic-gate static uint_t
ecpp_M1553_intr(struct ecppunit * pp)32537c478bd9Sstevel@tonic-gate ecpp_M1553_intr(struct ecppunit *pp)
32547c478bd9Sstevel@tonic-gate {
32557c478bd9Sstevel@tonic-gate 	int retval = DDI_INTR_UNCLAIMED;
32567c478bd9Sstevel@tonic-gate 
32577c478bd9Sstevel@tonic-gate 	pp->intr_hard++;
32587c478bd9Sstevel@tonic-gate 
32597c478bd9Sstevel@tonic-gate 	if (pp->e_busy == ECPP_BUSY) {
32607c478bd9Sstevel@tonic-gate 		/* Centronics or Compat PIO transfer */
32617c478bd9Sstevel@tonic-gate 		if (COMPAT_PIO(pp)) {
32627c478bd9Sstevel@tonic-gate 			return (ecpp_pio_ihdlr(pp));
32637c478bd9Sstevel@tonic-gate 		}
32647c478bd9Sstevel@tonic-gate 
32657c478bd9Sstevel@tonic-gate 		/* Centronics or Compat DMA transfer */
32667c478bd9Sstevel@tonic-gate 		if (COMPAT_DMA(pp) ||
32677c478bd9Sstevel@tonic-gate 		    (pp->current_mode == ECPP_ECP_MODE) ||
32687c478bd9Sstevel@tonic-gate 		    (pp->current_mode == ECPP_DIAG_MODE)) {
32697c478bd9Sstevel@tonic-gate 			return (ecpp_dma_ihdlr(pp));
32707c478bd9Sstevel@tonic-gate 		}
32717c478bd9Sstevel@tonic-gate 	}
32727c478bd9Sstevel@tonic-gate 
32737c478bd9Sstevel@tonic-gate 	/* Nibble or ECP backchannel request? */
32747c478bd9Sstevel@tonic-gate 	if ((DSR_READ(pp) & ECPP_nERR) == 0) {
32757c478bd9Sstevel@tonic-gate 		return (ecpp_nErr_ihdlr(pp));
32767c478bd9Sstevel@tonic-gate 	}
32777c478bd9Sstevel@tonic-gate 
32787c478bd9Sstevel@tonic-gate 	return (retval);
32797c478bd9Sstevel@tonic-gate }
32807c478bd9Sstevel@tonic-gate 
32817c478bd9Sstevel@tonic-gate /*
32827c478bd9Sstevel@tonic-gate  * DMA completion interrupt handler
32837c478bd9Sstevel@tonic-gate  */
32847c478bd9Sstevel@tonic-gate static uint_t
ecpp_dma_ihdlr(struct ecppunit * pp)32857c478bd9Sstevel@tonic-gate ecpp_dma_ihdlr(struct ecppunit *pp)
32867c478bd9Sstevel@tonic-gate {
32877c478bd9Sstevel@tonic-gate 	clock_t	tm;
32887c478bd9Sstevel@tonic-gate 
32897c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecpp_dma_ihdlr(%x): ecr=%x, dsr=%x, dcr=%x\n",
329019397407SSherry Moore 	    pp->current_mode, ECR_READ(pp), DSR_READ(pp), DCR_READ(pp));
32917c478bd9Sstevel@tonic-gate 
32927c478bd9Sstevel@tonic-gate 	/* we are expecting a data transfer interrupt */
32937c478bd9Sstevel@tonic-gate 	ASSERT(pp->e_busy == ECPP_BUSY);
32947c478bd9Sstevel@tonic-gate 
32957c478bd9Sstevel@tonic-gate 	/* Intr generated while invoking TFIFO mode. Exit */
32967c478bd9Sstevel@tonic-gate 	if (pp->tfifo_intr == 1) {
32977c478bd9Sstevel@tonic-gate 		pp->tfifo_intr = 0;
32987c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_dma_ihdlr: tfifo_intr is 1\n");
32997c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
33007c478bd9Sstevel@tonic-gate 	}
33017c478bd9Sstevel@tonic-gate 
33027c478bd9Sstevel@tonic-gate 	if (ECPP_DMA_STOP(pp, NULL) == FAILURE) {
33037c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_dma_ihdlr: dma_stop failed\n");
33047c478bd9Sstevel@tonic-gate 	}
33057c478bd9Sstevel@tonic-gate 
33067c478bd9Sstevel@tonic-gate 	if (pp->current_mode == ECPP_ECP_MODE &&
33077c478bd9Sstevel@tonic-gate 	    pp->current_phase == ECPP_PHASE_ECP_REV_XFER) {
33087c478bd9Sstevel@tonic-gate 		ecpp_ecp_read_completion(pp);
33097c478bd9Sstevel@tonic-gate 	} else {
33107c478bd9Sstevel@tonic-gate 		/*
33117c478bd9Sstevel@tonic-gate 		 * fifo_timer() will do the cleanup when the FIFO drains
33127c478bd9Sstevel@tonic-gate 		 */
33137c478bd9Sstevel@tonic-gate 		if ((ECR_READ(pp) & ECPP_FIFO_EMPTY) ||
33147c478bd9Sstevel@tonic-gate 		    (pp->current_mode == ECPP_DIAG_MODE)) {
33157c478bd9Sstevel@tonic-gate 			tm = 0;	/* no use in waiting if FIFO is already empty */
33167c478bd9Sstevel@tonic-gate 		} else {
33177c478bd9Sstevel@tonic-gate 			tm = drv_usectohz(FIFO_DRAIN_PERIOD);
33187c478bd9Sstevel@tonic-gate 		}
33197c478bd9Sstevel@tonic-gate 		pp->fifo_timer_id = timeout(ecpp_fifo_timer, (caddr_t)pp, tm);
33207c478bd9Sstevel@tonic-gate 	}
33217c478bd9Sstevel@tonic-gate 
33227c478bd9Sstevel@tonic-gate 	/*
33237c478bd9Sstevel@tonic-gate 	 * Stop the DMA transfer timeout timer
33247c478bd9Sstevel@tonic-gate 	 * this operation will temporarily give up the mutex,
33257c478bd9Sstevel@tonic-gate 	 * so we do it in the end of the handler to avoid races
33267c478bd9Sstevel@tonic-gate 	 */
33277c478bd9Sstevel@tonic-gate 	ecpp_untimeout_unblock(pp, &pp->timeout_id);
33287c478bd9Sstevel@tonic-gate 
33297c478bd9Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
33307c478bd9Sstevel@tonic-gate }
33317c478bd9Sstevel@tonic-gate 
33327c478bd9Sstevel@tonic-gate /*
33337c478bd9Sstevel@tonic-gate  * ecpp_pio_ihdlr() is a PIO interrupt processing routine
33347c478bd9Sstevel@tonic-gate  * It masks interrupts, updates statistics and initiates next byte transfer
33357c478bd9Sstevel@tonic-gate  */
33367c478bd9Sstevel@tonic-gate static uint_t
ecpp_pio_ihdlr(struct ecppunit * pp)33377c478bd9Sstevel@tonic-gate ecpp_pio_ihdlr(struct ecppunit *pp)
33387c478bd9Sstevel@tonic-gate {
33397c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->umutex));
33407c478bd9Sstevel@tonic-gate 	ASSERT(pp->e_busy == ECPP_BUSY);
33417c478bd9Sstevel@tonic-gate 
33427c478bd9Sstevel@tonic-gate 	/* update statistics */
33437c478bd9Sstevel@tonic-gate 	pp->joblen++;
33447c478bd9Sstevel@tonic-gate 	pp->ctxpio_obytes++;
33457c478bd9Sstevel@tonic-gate 
33467c478bd9Sstevel@tonic-gate 	/* disable nAck interrups */
33477c478bd9Sstevel@tonic-gate 	ECPP_MASK_INTR(pp);
33487c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, DCR_READ(pp) & ~(ECPP_REV_DIR | ECPP_INTR_EN));
33497c478bd9Sstevel@tonic-gate 
33507c478bd9Sstevel@tonic-gate 	/*
33517c478bd9Sstevel@tonic-gate 	 * If it was the last byte of the data block cleanup,
33527c478bd9Sstevel@tonic-gate 	 * otherwise trigger a soft interrupt to send the next byte
33537c478bd9Sstevel@tonic-gate 	 */
33547c478bd9Sstevel@tonic-gate 	if (pp->next_byte >= pp->last_byte) {
33557c478bd9Sstevel@tonic-gate 		ecpp_xfer_cleanup(pp);
33567c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
335719397407SSherry Moore 		    "ecpp_pio_ihdlr: pp->joblen=%d,pp->ctx_cf=%d,\n",
335819397407SSherry Moore 		    pp->joblen, pp->ctx_cf);
33597c478bd9Sstevel@tonic-gate 	} else {
33607c478bd9Sstevel@tonic-gate 		if (pp->softintr_pending) {
33617c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
336219397407SSherry Moore 			    "ecpp_pio_ihdlr:E: next byte in progress\n");
33637c478bd9Sstevel@tonic-gate 		} else {
33647c478bd9Sstevel@tonic-gate 			pp->softintr_flags = ECPP_SOFTINTR_PIONEXT;
33657c478bd9Sstevel@tonic-gate 			pp->softintr_pending = 1;
33667c478bd9Sstevel@tonic-gate 			ddi_trigger_softintr(pp->softintr_id);
33677c478bd9Sstevel@tonic-gate 		}
33687c478bd9Sstevel@tonic-gate 	}
33697c478bd9Sstevel@tonic-gate 
33707c478bd9Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
33717c478bd9Sstevel@tonic-gate }
33727c478bd9Sstevel@tonic-gate 
33737c478bd9Sstevel@tonic-gate /*
33747c478bd9Sstevel@tonic-gate  * ecpp_pio_writeb() sends a byte using Centronics handshake
33757c478bd9Sstevel@tonic-gate  */
33767c478bd9Sstevel@tonic-gate static void
ecpp_pio_writeb(struct ecppunit * pp)33777c478bd9Sstevel@tonic-gate ecpp_pio_writeb(struct ecppunit *pp)
33787c478bd9Sstevel@tonic-gate {
33797c478bd9Sstevel@tonic-gate 	uint8_t	dcr;
33807c478bd9Sstevel@tonic-gate 
33817c478bd9Sstevel@tonic-gate 	dcr = DCR_READ(pp) & ~ECPP_REV_DIR;
33827c478bd9Sstevel@tonic-gate 	dcr |= ECPP_INTR_EN;
33837c478bd9Sstevel@tonic-gate 
33847c478bd9Sstevel@tonic-gate 	/* send the next byte */
33857c478bd9Sstevel@tonic-gate 	DATAR_WRITE(pp, *(pp->next_byte++));
33867c478bd9Sstevel@tonic-gate 
33877c478bd9Sstevel@tonic-gate 	drv_usecwait(pp->data_setup_time);
33887c478bd9Sstevel@tonic-gate 
33897c478bd9Sstevel@tonic-gate 	/* Now Assert (neg logic) nStrobe */
33907c478bd9Sstevel@tonic-gate 	if (dcr_write(pp, dcr | ECPP_STB) == FAILURE) {
33917c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_pio_writeb:1: failed w/DCR\n");
33927c478bd9Sstevel@tonic-gate 	}
33937c478bd9Sstevel@tonic-gate 
33947c478bd9Sstevel@tonic-gate 	/* Enable nAck interrupts */
33957c478bd9Sstevel@tonic-gate 	(void) DSR_READ(pp);	/* ensure IRQ_ST is armed */
33967c478bd9Sstevel@tonic-gate 	ECPP_UNMASK_INTR(pp);
33977c478bd9Sstevel@tonic-gate 
33987c478bd9Sstevel@tonic-gate 	drv_usecwait(pp->strobe_pulse_width);
33997c478bd9Sstevel@tonic-gate 
34007c478bd9Sstevel@tonic-gate 	if (dcr_write(pp, dcr & ~ECPP_STB) == FAILURE) {
34017c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_pio_writeb:2: failed w/DCR\n");
34027c478bd9Sstevel@tonic-gate 	}
34037c478bd9Sstevel@tonic-gate }
34047c478bd9Sstevel@tonic-gate 
34057c478bd9Sstevel@tonic-gate /*
34067c478bd9Sstevel@tonic-gate  * Backchannel request interrupt handler
34077c478bd9Sstevel@tonic-gate  */
34087c478bd9Sstevel@tonic-gate static uint_t
ecpp_nErr_ihdlr(struct ecppunit * pp)34097c478bd9Sstevel@tonic-gate ecpp_nErr_ihdlr(struct ecppunit *pp)
34107c478bd9Sstevel@tonic-gate {
34117c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecpp_nErr_ihdlr: mode=%x, phase=%x\n",
341219397407SSherry Moore 	    pp->current_mode, pp->current_phase);
34137c478bd9Sstevel@tonic-gate 
34147c478bd9Sstevel@tonic-gate 	if (pp->oflag != TRUE) {
34157c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_nErr_ihdlr: not open!\n");
34167c478bd9Sstevel@tonic-gate 		return (DDI_INTR_UNCLAIMED);
34177c478bd9Sstevel@tonic-gate 	}
34187c478bd9Sstevel@tonic-gate 
34197c478bd9Sstevel@tonic-gate 	if (pp->e_busy == ECPP_BUSY) {
34207c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_nErr_ihdlr: busy\n");
34217c478bd9Sstevel@tonic-gate 		ECR_WRITE(pp, ECR_READ(pp) | ECPP_INTR_MASK);
34227c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
34237c478bd9Sstevel@tonic-gate 	}
34247c478bd9Sstevel@tonic-gate 
34257c478bd9Sstevel@tonic-gate 	/* mask nErr & nAck interrupts */
34267c478bd9Sstevel@tonic-gate 	ECPP_MASK_INTR(pp);
34277c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, DCR_READ(pp) & ~(ECPP_INTR_EN | ECPP_REV_DIR));
34287c478bd9Sstevel@tonic-gate 	ECR_WRITE(pp, ECR_READ(pp) | ECPP_INTR_MASK);
34297c478bd9Sstevel@tonic-gate 
34307c478bd9Sstevel@tonic-gate 	/* going reverse */
34317c478bd9Sstevel@tonic-gate 	switch (pp->current_mode) {
34327c478bd9Sstevel@tonic-gate 	case ECPP_ECP_MODE:
34337c478bd9Sstevel@tonic-gate 		/*
34347c478bd9Sstevel@tonic-gate 		 * Peripheral asserts nPeriphRequest (nFault)
34357c478bd9Sstevel@tonic-gate 		 */
34367c478bd9Sstevel@tonic-gate 		break;
34377c478bd9Sstevel@tonic-gate 	case ECPP_NIBBLE_MODE:
34387c478bd9Sstevel@tonic-gate 		/*
34397c478bd9Sstevel@tonic-gate 		 * Event 18: Periph asserts nErr to indicate data avail
34407c478bd9Sstevel@tonic-gate 		 * Event 19: After waiting minimum pulse width,
34417c478bd9Sstevel@tonic-gate 		 *   periph sets nAck high to generate an interrupt
34427c478bd9Sstevel@tonic-gate 		 *
34437c478bd9Sstevel@tonic-gate 		 * Interface is in Interrupt Phase
34447c478bd9Sstevel@tonic-gate 		 */
34457c478bd9Sstevel@tonic-gate 		pp->current_phase = ECPP_PHASE_NIBT_REVINTR;
34467c478bd9Sstevel@tonic-gate 
34477c478bd9Sstevel@tonic-gate 		break;
34487c478bd9Sstevel@tonic-gate 	default:
34497c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_nErr_ihdlr: wrong mode!\n");
34507c478bd9Sstevel@tonic-gate 		return (DDI_INTR_UNCLAIMED);
34517c478bd9Sstevel@tonic-gate 	}
34527c478bd9Sstevel@tonic-gate 
34537c478bd9Sstevel@tonic-gate 	(void) ecpp_backchan_req(pp);	/* put backchannel request on the wq */
34547c478bd9Sstevel@tonic-gate 
34557c478bd9Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
34567c478bd9Sstevel@tonic-gate }
34577c478bd9Sstevel@tonic-gate 
34587c478bd9Sstevel@tonic-gate /*
34597c478bd9Sstevel@tonic-gate  * Softintr handler does work according to softintr_flags:
34607c478bd9Sstevel@tonic-gate  * in case of ECPP_SOFTINTR_PIONEXT it sends next byte of PIO transfer
34617c478bd9Sstevel@tonic-gate  */
34627c478bd9Sstevel@tonic-gate static uint_t
ecpp_softintr(caddr_t arg)34637c478bd9Sstevel@tonic-gate ecpp_softintr(caddr_t arg)
34647c478bd9Sstevel@tonic-gate {
34657c478bd9Sstevel@tonic-gate 	struct ecppunit *pp = (struct ecppunit *)arg;
34667c478bd9Sstevel@tonic-gate 	uint32_t unx_len, ecpp_reattempts = 0;
34677c478bd9Sstevel@tonic-gate 
34687c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->umutex);
34697c478bd9Sstevel@tonic-gate 
34707c478bd9Sstevel@tonic-gate 	pp->intr_soft++;
34717c478bd9Sstevel@tonic-gate 
34727c478bd9Sstevel@tonic-gate 	if (!pp->softintr_pending) {
34737c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
34747c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
34757c478bd9Sstevel@tonic-gate 	} else {
34767c478bd9Sstevel@tonic-gate 		pp->softintr_pending = 0;
34777c478bd9Sstevel@tonic-gate 	}
34787c478bd9Sstevel@tonic-gate 
34797c478bd9Sstevel@tonic-gate 	if (pp->softintr_flags & ECPP_SOFTINTR_PIONEXT) {
34807c478bd9Sstevel@tonic-gate 		pp->softintr_flags &= ~ECPP_SOFTINTR_PIONEXT;
34817c478bd9Sstevel@tonic-gate 		/*
34827c478bd9Sstevel@tonic-gate 		 * Sent next byte in PIO mode
34837c478bd9Sstevel@tonic-gate 		 */
34847c478bd9Sstevel@tonic-gate 		ecpp_reattempts = 0;
34857c478bd9Sstevel@tonic-gate 		do {
34867c478bd9Sstevel@tonic-gate 			if (ecpp_check_status(pp) == SUCCESS) {
34877c478bd9Sstevel@tonic-gate 				pp->e_busy = ECPP_BUSY;
34887c478bd9Sstevel@tonic-gate 				break;
34897c478bd9Sstevel@tonic-gate 			}
34907c478bd9Sstevel@tonic-gate 			drv_usecwait(1);
34917c478bd9Sstevel@tonic-gate 			if (pp->isr_reattempt_high < ecpp_reattempts) {
34927c478bd9Sstevel@tonic-gate 				pp->isr_reattempt_high = ecpp_reattempts;
34937c478bd9Sstevel@tonic-gate 			}
34947c478bd9Sstevel@tonic-gate 		} while (++ecpp_reattempts < pp->wait_for_busy);
34957c478bd9Sstevel@tonic-gate 
34967c478bd9Sstevel@tonic-gate 		/* if the peripheral still not recovered suspend the transfer */
34977c478bd9Sstevel@tonic-gate 		if (pp->e_busy == ECPP_ERR) {
34987c478bd9Sstevel@tonic-gate 			++pp->ctx_cf; /* check status fail */
34997c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "ecpp_softintr:check_status:F: "
350019397407SSherry Moore 			    "dsr=%x jl=%d cf_isr=%d\n",
350119397407SSherry Moore 			    DSR_READ(pp), pp->joblen, pp->ctx_cf);
35027c478bd9Sstevel@tonic-gate 
35037c478bd9Sstevel@tonic-gate 			/*
35047c478bd9Sstevel@tonic-gate 			 * if status signals are bad,
35057c478bd9Sstevel@tonic-gate 			 * put everything back on the wq.
35067c478bd9Sstevel@tonic-gate 			 */
35077c478bd9Sstevel@tonic-gate 			unx_len = pp->last_byte - pp->next_byte;
35087c478bd9Sstevel@tonic-gate 			if (pp->msg != NULL) {
35097c478bd9Sstevel@tonic-gate 				ecpp_putback_untransfered(pp,
351019397407SSherry Moore 				    (void *)pp->msg->b_rptr, unx_len);
35117c478bd9Sstevel@tonic-gate 				ecpp_error(pp->dip,
35127c478bd9Sstevel@tonic-gate 				    "ecpp_softintr:e1:unx_len=%d\n", unx_len);
35137c478bd9Sstevel@tonic-gate 
35147c478bd9Sstevel@tonic-gate 				freemsg(pp->msg);
35157c478bd9Sstevel@tonic-gate 				pp->msg = NULL;
35167c478bd9Sstevel@tonic-gate 			} else {
35177c478bd9Sstevel@tonic-gate 				ecpp_putback_untransfered(pp,
351819397407SSherry Moore 				    pp->next_byte, unx_len);
35197c478bd9Sstevel@tonic-gate 				ecpp_error(pp->dip,
35207c478bd9Sstevel@tonic-gate 				    "ecpp_softintr:e2:unx_len=%d\n", unx_len);
35217c478bd9Sstevel@tonic-gate 			}
35227c478bd9Sstevel@tonic-gate 
35237c478bd9Sstevel@tonic-gate 			ecpp_xfer_cleanup(pp);
35247c478bd9Sstevel@tonic-gate 			pp->e_busy = ECPP_ERR;
35257c478bd9Sstevel@tonic-gate 			qenable(pp->writeq);
35267c478bd9Sstevel@tonic-gate 		} else {
35277c478bd9Sstevel@tonic-gate 			/* send the next one */
35287c478bd9Sstevel@tonic-gate 			pp->e_busy = ECPP_BUSY;
35297c478bd9Sstevel@tonic-gate 			(void) ecpp_pio_writeb(pp);
35307c478bd9Sstevel@tonic-gate 		}
35317c478bd9Sstevel@tonic-gate 	}
35327c478bd9Sstevel@tonic-gate 
35337c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->umutex);
35347c478bd9Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
35357c478bd9Sstevel@tonic-gate }
35367c478bd9Sstevel@tonic-gate 
35377c478bd9Sstevel@tonic-gate 
35387c478bd9Sstevel@tonic-gate /*
35397c478bd9Sstevel@tonic-gate  * Transfer clean-up:
35402520aea3SToomas Soome  *	shut down the DMAC
35417c478bd9Sstevel@tonic-gate  *	stop the transfer timer
35427c478bd9Sstevel@tonic-gate  *	enable write queue
35437c478bd9Sstevel@tonic-gate  */
35447c478bd9Sstevel@tonic-gate static void
ecpp_xfer_cleanup(struct ecppunit * pp)35457c478bd9Sstevel@tonic-gate ecpp_xfer_cleanup(struct ecppunit *pp)
35467c478bd9Sstevel@tonic-gate {
35477c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->umutex));
35487c478bd9Sstevel@tonic-gate 
35497c478bd9Sstevel@tonic-gate 	/*
35507c478bd9Sstevel@tonic-gate 	 * if we did not use the ioblock, the mblk that
35517c478bd9Sstevel@tonic-gate 	 * was used should be freed.
35527c478bd9Sstevel@tonic-gate 	 */
35537c478bd9Sstevel@tonic-gate 	if (pp->msg != NULL) {
35547c478bd9Sstevel@tonic-gate 		freemsg(pp->msg);
35557c478bd9Sstevel@tonic-gate 		pp->msg = NULL;
35567c478bd9Sstevel@tonic-gate 	}
35577c478bd9Sstevel@tonic-gate 
35587c478bd9Sstevel@tonic-gate 	/* The port is no longer active */
35597c478bd9Sstevel@tonic-gate 	pp->e_busy = ECPP_IDLE;
35607c478bd9Sstevel@tonic-gate 
35617c478bd9Sstevel@tonic-gate 	/* Stop the transfer timeout timer */
35627c478bd9Sstevel@tonic-gate 	ecpp_untimeout_unblock(pp, &pp->timeout_id);
35637c478bd9Sstevel@tonic-gate 
35647c478bd9Sstevel@tonic-gate 	qenable(pp->writeq);
35657c478bd9Sstevel@tonic-gate }
35667c478bd9Sstevel@tonic-gate 
35677c478bd9Sstevel@tonic-gate /*VARARGS*/
35687c478bd9Sstevel@tonic-gate static void
ecpp_error(dev_info_t * dip,char * fmt,...)35697c478bd9Sstevel@tonic-gate ecpp_error(dev_info_t *dip, char *fmt, ...)
35707c478bd9Sstevel@tonic-gate {
35717c478bd9Sstevel@tonic-gate 	static	long	last;
35727c478bd9Sstevel@tonic-gate 	static	char	*lastfmt;
35737c478bd9Sstevel@tonic-gate 	char		msg_buffer[255];
35747c478bd9Sstevel@tonic-gate 	va_list	ap;
35757c478bd9Sstevel@tonic-gate 	time_t	now;
35767c478bd9Sstevel@tonic-gate 
35777c478bd9Sstevel@tonic-gate 	if (!ecpp_debug) {
35787c478bd9Sstevel@tonic-gate 		return;
35797c478bd9Sstevel@tonic-gate 	}
35807c478bd9Sstevel@tonic-gate 
35817c478bd9Sstevel@tonic-gate 	/*
35827c478bd9Sstevel@tonic-gate 	 * This function is supposed to be a quick non-blockable
35837c478bd9Sstevel@tonic-gate 	 * wrapper for cmn_err(9F), which provides a sensible degree
35847c478bd9Sstevel@tonic-gate 	 * of debug message throttling.  Not using any type of lock
35857c478bd9Sstevel@tonic-gate 	 * is a requirement, but this also leaves two static variables
35867c478bd9Sstevel@tonic-gate 	 * - last and lastfmt - unprotected. However, this will not do
35877c478bd9Sstevel@tonic-gate 	 * any harm to driver functionality, it can only weaken throttling.
35887c478bd9Sstevel@tonic-gate 	 * The following directive asks warlock to not worry about these
35897c478bd9Sstevel@tonic-gate 	 * variables.
35907c478bd9Sstevel@tonic-gate 	 */
35917c478bd9Sstevel@tonic-gate 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(last, lastfmt))
35927c478bd9Sstevel@tonic-gate 
35937c478bd9Sstevel@tonic-gate 	/*
35947c478bd9Sstevel@tonic-gate 	 * Don't print same error message too often.
35957c478bd9Sstevel@tonic-gate 	 */
35967c478bd9Sstevel@tonic-gate 	now = gethrestime_sec();
35977c478bd9Sstevel@tonic-gate 	if ((last == (now & ~1)) && (lastfmt == fmt))
35987c478bd9Sstevel@tonic-gate 		return;
35997c478bd9Sstevel@tonic-gate 
36007c478bd9Sstevel@tonic-gate 	last = now & ~1;
36017c478bd9Sstevel@tonic-gate 	lastfmt = fmt;
36027c478bd9Sstevel@tonic-gate 
36037c478bd9Sstevel@tonic-gate 	va_start(ap, fmt);
36047c478bd9Sstevel@tonic-gate 	(void) vsprintf(msg_buffer, fmt, ap);
36057c478bd9Sstevel@tonic-gate 	cmn_err(CE_CONT, "%s%d: %s", ddi_get_name(dip),
360619397407SSherry Moore 	    ddi_get_instance(dip), msg_buffer);
36077c478bd9Sstevel@tonic-gate 	va_end(ap);
36087c478bd9Sstevel@tonic-gate }
36097c478bd9Sstevel@tonic-gate 
36107c478bd9Sstevel@tonic-gate /*
36117c478bd9Sstevel@tonic-gate  * Forward transfer timeout
36127c478bd9Sstevel@tonic-gate  */
36137c478bd9Sstevel@tonic-gate static void
ecpp_xfer_timeout(void * arg)36147c478bd9Sstevel@tonic-gate ecpp_xfer_timeout(void *arg)
36157c478bd9Sstevel@tonic-gate {
36167c478bd9Sstevel@tonic-gate 	struct ecppunit	*pp = arg;
36177c478bd9Sstevel@tonic-gate 	void		*unx_addr;
36187c478bd9Sstevel@tonic-gate 	size_t		unx_len, xferd;
36197c478bd9Sstevel@tonic-gate 	uint8_t		dcr;
36207c478bd9Sstevel@tonic-gate 	timeout_id_t	fifo_timer_id;
36217c478bd9Sstevel@tonic-gate 
36227c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->umutex);
36237c478bd9Sstevel@tonic-gate 
36247c478bd9Sstevel@tonic-gate 	if (pp->timeout_id == 0) {
36257c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
36267c478bd9Sstevel@tonic-gate 		return;
36277c478bd9Sstevel@tonic-gate 	} else {
36287c478bd9Sstevel@tonic-gate 		pp->timeout_id = 0;
36297c478bd9Sstevel@tonic-gate 	}
36307c478bd9Sstevel@tonic-gate 
36317c478bd9Sstevel@tonic-gate 	pp->xfer_tout++;
36327c478bd9Sstevel@tonic-gate 
36337c478bd9Sstevel@tonic-gate 	pp->dma_cancelled = TRUE;	/* prevent race with isr() */
36347c478bd9Sstevel@tonic-gate 
36357c478bd9Sstevel@tonic-gate 	if (COMPAT_PIO(pp)) {
36367c478bd9Sstevel@tonic-gate 		/*
36377c478bd9Sstevel@tonic-gate 		 * PIO mode timeout
36387c478bd9Sstevel@tonic-gate 		 */
36397c478bd9Sstevel@tonic-gate 
36407c478bd9Sstevel@tonic-gate 		/* turn off nAck interrupts */
36417c478bd9Sstevel@tonic-gate 		dcr = DCR_READ(pp);
36427c478bd9Sstevel@tonic-gate 		(void) dcr_write(pp, dcr & ~(ECPP_REV_DIR | ECPP_INTR_EN));
36437c478bd9Sstevel@tonic-gate 		ECPP_MASK_INTR(pp);
36447c478bd9Sstevel@tonic-gate 
36457c478bd9Sstevel@tonic-gate 		pp->softintr_pending = 0;
36467c478bd9Sstevel@tonic-gate 		unx_len = pp->last_byte - pp->next_byte;
36477c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "xfer_timeout: unx_len=%d\n", unx_len);
36487c478bd9Sstevel@tonic-gate 
36497c478bd9Sstevel@tonic-gate 		if (unx_len > 0) {
36507c478bd9Sstevel@tonic-gate 			unx_addr = pp->next_byte;
36517c478bd9Sstevel@tonic-gate 		} else {
36527c478bd9Sstevel@tonic-gate 			ecpp_xfer_cleanup(pp);
36537c478bd9Sstevel@tonic-gate 			qenable(pp->writeq);
36547c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
36557c478bd9Sstevel@tonic-gate 			return;
36567c478bd9Sstevel@tonic-gate 		}
36577c478bd9Sstevel@tonic-gate 	} else {
36587c478bd9Sstevel@tonic-gate 		/*
36597c478bd9Sstevel@tonic-gate 		 * DMA mode timeout
36607c478bd9Sstevel@tonic-gate 		 *
36617c478bd9Sstevel@tonic-gate 		 * If DMAC fails to shut off, continue anyways and attempt
36627c478bd9Sstevel@tonic-gate 		 * to put untransfered data back on queue.
36637c478bd9Sstevel@tonic-gate 		 */
36647c478bd9Sstevel@tonic-gate 		if (ECPP_DMA_STOP(pp, &unx_len) == FAILURE) {
36657c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
366619397407SSherry Moore 			    "ecpp_xfer_timeout: failed dma_stop\n");
36677c478bd9Sstevel@tonic-gate 		}
36687c478bd9Sstevel@tonic-gate 
36697c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "xfer_timeout: unx_len=%d\n", unx_len);
36707c478bd9Sstevel@tonic-gate 
36717c478bd9Sstevel@tonic-gate 		if (ddi_dma_unbind_handle(pp->dma_handle) == DDI_FAILURE) {
36727c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
367319397407SSherry Moore 			    "ecpp_xfer_timeout: failed unbind\n");
36747c478bd9Sstevel@tonic-gate 		}
36757c478bd9Sstevel@tonic-gate 
36767c478bd9Sstevel@tonic-gate 		/*
36777c478bd9Sstevel@tonic-gate 		 * if the bcr is zero, then DMA is complete and
36787c478bd9Sstevel@tonic-gate 		 * we are waiting for the fifo to drain.  So let
36797c478bd9Sstevel@tonic-gate 		 * ecpp_fifo_timer() look after the clean up.
36807c478bd9Sstevel@tonic-gate 		 */
36817c478bd9Sstevel@tonic-gate 		if (unx_len == 0) {
36827c478bd9Sstevel@tonic-gate 			qenable(pp->writeq);
36837c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
36847c478bd9Sstevel@tonic-gate 			return;
36857c478bd9Sstevel@tonic-gate 		} else {
36867c478bd9Sstevel@tonic-gate 			xferd = pp->dma_cookie.dmac_size - unx_len;
36877c478bd9Sstevel@tonic-gate 			pp->resid -= xferd;
36887c478bd9Sstevel@tonic-gate 			unx_len = pp->resid;
36897c478bd9Sstevel@tonic-gate 
36907c478bd9Sstevel@tonic-gate 			/* update statistics */
36917c478bd9Sstevel@tonic-gate 			pp->obytes[pp->current_mode] += xferd;
36927c478bd9Sstevel@tonic-gate 			pp->joblen += xferd;
36937c478bd9Sstevel@tonic-gate 
36947c478bd9Sstevel@tonic-gate 			if (pp->msg != NULL) {
36957c478bd9Sstevel@tonic-gate 				unx_addr = (caddr_t)pp->msg->b_wptr - unx_len;
36967c478bd9Sstevel@tonic-gate 			} else {
36977c478bd9Sstevel@tonic-gate 				unx_addr = pp->ioblock +
369819397407SSherry Moore 				    (pp->xfercnt - unx_len);
36997c478bd9Sstevel@tonic-gate 			}
37007c478bd9Sstevel@tonic-gate 		}
37017c478bd9Sstevel@tonic-gate 	}
37027c478bd9Sstevel@tonic-gate 
37037c478bd9Sstevel@tonic-gate 	/* Following code is common for PIO and DMA modes */
37047c478bd9Sstevel@tonic-gate 
37057c478bd9Sstevel@tonic-gate 	ecpp_putback_untransfered(pp, (caddr_t)unx_addr, unx_len);
37067c478bd9Sstevel@tonic-gate 
37077c478bd9Sstevel@tonic-gate 	if (pp->msg != NULL) {
37087c478bd9Sstevel@tonic-gate 		freemsg(pp->msg);
37097c478bd9Sstevel@tonic-gate 		pp->msg = NULL;
37107c478bd9Sstevel@tonic-gate 	}
37117c478bd9Sstevel@tonic-gate 
37127c478bd9Sstevel@tonic-gate 	/* mark the error status structure */
37137c478bd9Sstevel@tonic-gate 	pp->timeout_error = 1;
37147c478bd9Sstevel@tonic-gate 	pp->e_busy = ECPP_ERR;
37157c478bd9Sstevel@tonic-gate 	fifo_timer_id = pp->fifo_timer_id;
37167c478bd9Sstevel@tonic-gate 	pp->fifo_timer_id = 0;
37177c478bd9Sstevel@tonic-gate 
37187c478bd9Sstevel@tonic-gate 	qenable(pp->writeq);
37197c478bd9Sstevel@tonic-gate 
37207c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->umutex);
37217c478bd9Sstevel@tonic-gate 
37227c478bd9Sstevel@tonic-gate 	if (fifo_timer_id) {
37237c478bd9Sstevel@tonic-gate 		(void) untimeout(fifo_timer_id);
37247c478bd9Sstevel@tonic-gate 	}
37257c478bd9Sstevel@tonic-gate }
37267c478bd9Sstevel@tonic-gate 
37277c478bd9Sstevel@tonic-gate static void
ecpp_putback_untransfered(struct ecppunit * pp,void * startp,uint_t len)37287c478bd9Sstevel@tonic-gate ecpp_putback_untransfered(struct ecppunit *pp, void *startp, uint_t len)
37297c478bd9Sstevel@tonic-gate {
37307c478bd9Sstevel@tonic-gate 	mblk_t *new_mp;
37317c478bd9Sstevel@tonic-gate 
37327c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecpp_putback_untrans=%d\n", len);
37337c478bd9Sstevel@tonic-gate 
37347c478bd9Sstevel@tonic-gate 	if (len == 0) {
37357c478bd9Sstevel@tonic-gate 		return;
37367c478bd9Sstevel@tonic-gate 	}
37377c478bd9Sstevel@tonic-gate 
37387c478bd9Sstevel@tonic-gate 	new_mp = allocb(len, BPRI_MED);
37397c478bd9Sstevel@tonic-gate 	if (new_mp == NULL) {
37407c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
374119397407SSherry Moore 		    "ecpp_putback_untransfered: allocb FAILURE.\n");
37427c478bd9Sstevel@tonic-gate 		return;
37437c478bd9Sstevel@tonic-gate 	}
37447c478bd9Sstevel@tonic-gate 
37457c478bd9Sstevel@tonic-gate 	bcopy(startp, new_mp->b_rptr, len);
37467c478bd9Sstevel@tonic-gate 	new_mp->b_wptr = new_mp->b_rptr + len;
37477c478bd9Sstevel@tonic-gate 
37487c478bd9Sstevel@tonic-gate 	if (!putbq(pp->writeq, new_mp)) {
37497c478bd9Sstevel@tonic-gate 		freemsg(new_mp);
37507c478bd9Sstevel@tonic-gate 	}
37517c478bd9Sstevel@tonic-gate }
37527c478bd9Sstevel@tonic-gate 
37537c478bd9Sstevel@tonic-gate static uchar_t
ecr_write(struct ecppunit * pp,uint8_t ecr_byte)37547c478bd9Sstevel@tonic-gate ecr_write(struct ecppunit *pp, uint8_t ecr_byte)
37557c478bd9Sstevel@tonic-gate {
37567c478bd9Sstevel@tonic-gate 	int i, current_ecr;
37577c478bd9Sstevel@tonic-gate 
37587c478bd9Sstevel@tonic-gate 	for (i = ECPP_REG_WRITE_MAX_LOOP; i > 0; i--) {
37597c478bd9Sstevel@tonic-gate 		ECR_WRITE(pp, ecr_byte);
37607c478bd9Sstevel@tonic-gate 
37617c478bd9Sstevel@tonic-gate 		current_ecr = ECR_READ(pp);
37627c478bd9Sstevel@tonic-gate 
37637c478bd9Sstevel@tonic-gate 		/* mask off the lower two read-only bits */
37647c478bd9Sstevel@tonic-gate 		if ((ecr_byte & 0xFC) == (current_ecr & 0xFC))
37657c478bd9Sstevel@tonic-gate 			return (SUCCESS);
37667c478bd9Sstevel@tonic-gate 	}
37677c478bd9Sstevel@tonic-gate 	return (FAILURE);
37687c478bd9Sstevel@tonic-gate }
37697c478bd9Sstevel@tonic-gate 
37707c478bd9Sstevel@tonic-gate static uchar_t
dcr_write(struct ecppunit * pp,uint8_t dcr_byte)37717c478bd9Sstevel@tonic-gate dcr_write(struct ecppunit *pp, uint8_t dcr_byte)
37727c478bd9Sstevel@tonic-gate {
37737c478bd9Sstevel@tonic-gate 	uint8_t current_dcr;
37747c478bd9Sstevel@tonic-gate 	int i;
37757c478bd9Sstevel@tonic-gate 
37767c478bd9Sstevel@tonic-gate 	for (i = ECPP_REG_WRITE_MAX_LOOP; i > 0; i--) {
37777c478bd9Sstevel@tonic-gate 		DCR_WRITE(pp, dcr_byte);
37787c478bd9Sstevel@tonic-gate 
37797c478bd9Sstevel@tonic-gate 		current_dcr = DCR_READ(pp);
37807c478bd9Sstevel@tonic-gate 
37817c478bd9Sstevel@tonic-gate 		/* compare only bits 0-4 (direction bit return 1) */
37827c478bd9Sstevel@tonic-gate 		if ((dcr_byte & 0x1F) == (current_dcr & 0x1F))
37837c478bd9Sstevel@tonic-gate 			return (SUCCESS);
37847c478bd9Sstevel@tonic-gate 	}
37857c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip,
378619397407SSherry Moore 	    "(%d)dcr_write: dcr written =%x, dcr readback =%x\n",
378719397407SSherry Moore 	    i, dcr_byte, current_dcr);
37887c478bd9Sstevel@tonic-gate 
37897c478bd9Sstevel@tonic-gate 	return (FAILURE);
37907c478bd9Sstevel@tonic-gate }
37917c478bd9Sstevel@tonic-gate 
37927c478bd9Sstevel@tonic-gate static uchar_t
ecpp_reset_port_regs(struct ecppunit * pp)37937c478bd9Sstevel@tonic-gate ecpp_reset_port_regs(struct ecppunit *pp)
37947c478bd9Sstevel@tonic-gate {
37957c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, ECPP_SLCTIN | ECPP_nINIT);
37967c478bd9Sstevel@tonic-gate 	ECR_WRITE(pp, ECR_mode_001 | ECPP_INTR_MASK | ECPP_INTR_SRV);
37977c478bd9Sstevel@tonic-gate 	return (SUCCESS);
37987c478bd9Sstevel@tonic-gate }
37997c478bd9Sstevel@tonic-gate 
38007c478bd9Sstevel@tonic-gate /*
38017c478bd9Sstevel@tonic-gate  * The data transferred by the DMA engine goes through the FIFO,
38027c478bd9Sstevel@tonic-gate  * so that when the DMA counter reaches zero (and an interrupt occurs)
38037c478bd9Sstevel@tonic-gate  * the FIFO can still contain data. If this is the case, the ISR will
38047c478bd9Sstevel@tonic-gate  * schedule this callback to wait until the FIFO drains or a timeout occurs.
38057c478bd9Sstevel@tonic-gate  */
38067c478bd9Sstevel@tonic-gate static void
ecpp_fifo_timer(void * arg)38077c478bd9Sstevel@tonic-gate ecpp_fifo_timer(void *arg)
38087c478bd9Sstevel@tonic-gate {
38097c478bd9Sstevel@tonic-gate 	struct ecppunit *pp = arg;
38107c478bd9Sstevel@tonic-gate 	uint8_t	ecr;
38117c478bd9Sstevel@tonic-gate 	timeout_id_t	timeout_id;
38127c478bd9Sstevel@tonic-gate 
38137c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->umutex);
38147c478bd9Sstevel@tonic-gate 
38157c478bd9Sstevel@tonic-gate 	/*
38167c478bd9Sstevel@tonic-gate 	 * If the FIFO timer has been turned off, exit.
38177c478bd9Sstevel@tonic-gate 	 */
38187c478bd9Sstevel@tonic-gate 	if (pp->fifo_timer_id == 0) {
38197c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_fifo_timer: untimedout\n");
38207c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
38217c478bd9Sstevel@tonic-gate 		return;
38227c478bd9Sstevel@tonic-gate 	} else {
38237c478bd9Sstevel@tonic-gate 		pp->fifo_timer_id = 0;
38247c478bd9Sstevel@tonic-gate 	}
38257c478bd9Sstevel@tonic-gate 
38267c478bd9Sstevel@tonic-gate 	/*
38277c478bd9Sstevel@tonic-gate 	 * If the FIFO is not empty restart timer.  Wait FIFO_DRAIN_PERIOD
38287c478bd9Sstevel@tonic-gate 	 * (250 ms) and check FIFO_EMPTY bit again. Repeat until FIFO is
38297c478bd9Sstevel@tonic-gate 	 * empty or until 10 * FIFO_DRAIN_PERIOD expires.
38307c478bd9Sstevel@tonic-gate 	 */
38317c478bd9Sstevel@tonic-gate 	ecr = ECR_READ(pp);
38327c478bd9Sstevel@tonic-gate 
38337c478bd9Sstevel@tonic-gate 	if ((pp->current_mode != ECPP_DIAG_MODE) &&
38347c478bd9Sstevel@tonic-gate 	    (((ecr & ECPP_FIFO_EMPTY) == 0) &&
38357c478bd9Sstevel@tonic-gate 	    (pp->ecpp_drain_counter < 10))) {
38367c478bd9Sstevel@tonic-gate 
38377c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
383819397407SSherry Moore 		    "ecpp_fifo_timer(%d):FIFO not empty:ecr=%x\n",
383919397407SSherry Moore 		    pp->ecpp_drain_counter, ecr);
38407c478bd9Sstevel@tonic-gate 
38417c478bd9Sstevel@tonic-gate 		pp->fifo_timer_id = timeout(ecpp_fifo_timer,
384219397407SSherry Moore 		    (caddr_t)pp, drv_usectohz(FIFO_DRAIN_PERIOD));
38437c478bd9Sstevel@tonic-gate 		++pp->ecpp_drain_counter;
38447c478bd9Sstevel@tonic-gate 
38457c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
38467c478bd9Sstevel@tonic-gate 		return;
38477c478bd9Sstevel@tonic-gate 	}
38487c478bd9Sstevel@tonic-gate 
38497c478bd9Sstevel@tonic-gate 	if (pp->current_mode != ECPP_DIAG_MODE) {
38507c478bd9Sstevel@tonic-gate 		/*
38517c478bd9Sstevel@tonic-gate 		 * If the FIFO won't drain after 10 FIFO_DRAIN_PERIODs
38527c478bd9Sstevel@tonic-gate 		 * then don't wait any longer.  Simply clean up the transfer.
38537c478bd9Sstevel@tonic-gate 		 */
38547c478bd9Sstevel@tonic-gate 		if (pp->ecpp_drain_counter >= 10) {
38557c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "ecpp_fifo_timer(%d):"
385619397407SSherry Moore 			    " clearing FIFO,can't wait:ecr=%x\n",
385719397407SSherry Moore 			    pp->ecpp_drain_counter, ecr);
38587c478bd9Sstevel@tonic-gate 		} else {
38597c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
386019397407SSherry Moore 			    "ecpp_fifo_timer(%d):FIFO empty:ecr=%x\n",
386119397407SSherry Moore 			    pp->ecpp_drain_counter, ecr);
38627c478bd9Sstevel@tonic-gate 		}
38637c478bd9Sstevel@tonic-gate 
38647c478bd9Sstevel@tonic-gate 		pp->ecpp_drain_counter = 0;
38657c478bd9Sstevel@tonic-gate 	}
38667c478bd9Sstevel@tonic-gate 
38677c478bd9Sstevel@tonic-gate 	/*
38687c478bd9Sstevel@tonic-gate 	 * Main section of routine:
38697c478bd9Sstevel@tonic-gate 	 *  - stop the DMA transfer timer
38707c478bd9Sstevel@tonic-gate 	 *  - program DMA with next cookie/window or unbind the DMA mapping
38717c478bd9Sstevel@tonic-gate 	 *  - update stats
38727c478bd9Sstevel@tonic-gate 	 *  - if last mblk in queue, signal to close() & return to idle state
38737c478bd9Sstevel@tonic-gate 	 */
38747c478bd9Sstevel@tonic-gate 
38757c478bd9Sstevel@tonic-gate 	/* Stop the DMA transfer timeout timer */
38767c478bd9Sstevel@tonic-gate 	timeout_id = pp->timeout_id;
38777c478bd9Sstevel@tonic-gate 	pp->timeout_id = 0;
38787c478bd9Sstevel@tonic-gate 
38797c478bd9Sstevel@tonic-gate 	/* data has drained from fifo, it is ok to free dma resource */
38807c478bd9Sstevel@tonic-gate 	if (pp->current_mode == ECPP_ECP_MODE ||
38817c478bd9Sstevel@tonic-gate 	    pp->current_mode == ECPP_DIAG_MODE ||
38827c478bd9Sstevel@tonic-gate 	    COMPAT_DMA(pp)) {
38837c478bd9Sstevel@tonic-gate 		off_t	off;
38847c478bd9Sstevel@tonic-gate 		size_t	len;
38857c478bd9Sstevel@tonic-gate 
38867c478bd9Sstevel@tonic-gate 		/* update residual */
38877c478bd9Sstevel@tonic-gate 		pp->resid -= pp->dma_cookie.dmac_size;
38887c478bd9Sstevel@tonic-gate 
38897c478bd9Sstevel@tonic-gate 		/* update statistics */
38907c478bd9Sstevel@tonic-gate 		pp->joblen += pp->dma_cookie.dmac_size;
38917c478bd9Sstevel@tonic-gate 		if (pp->dma_dir == DDI_DMA_WRITE) {
38927c478bd9Sstevel@tonic-gate 			pp->obytes[pp->current_mode] +=
389319397407SSherry Moore 			    pp->dma_cookie.dmac_size;
38947c478bd9Sstevel@tonic-gate 		} else {
38957c478bd9Sstevel@tonic-gate 			pp->ibytes[pp->current_mode] +=
389619397407SSherry Moore 			    pp->dma_cookie.dmac_size;
38977c478bd9Sstevel@tonic-gate 		}
38987c478bd9Sstevel@tonic-gate 
38997c478bd9Sstevel@tonic-gate 		/*
39007c478bd9Sstevel@tonic-gate 		 * Look if any cookies/windows left
39017c478bd9Sstevel@tonic-gate 		 */
39027c478bd9Sstevel@tonic-gate 		if (--pp->dma_cookie_count > 0) {
39037c478bd9Sstevel@tonic-gate 			/* process the next cookie */
39047c478bd9Sstevel@tonic-gate 			ddi_dma_nextcookie(pp->dma_handle,
390519397407SSherry Moore 			    &pp->dma_cookie);
39067c478bd9Sstevel@tonic-gate 		} else if (pp->dma_curwin < pp->dma_nwin) {
39077c478bd9Sstevel@tonic-gate 			/* process the next window */
39087c478bd9Sstevel@tonic-gate 			if (ddi_dma_getwin(pp->dma_handle,
39097c478bd9Sstevel@tonic-gate 			    pp->dma_curwin, &off, &len,
39107c478bd9Sstevel@tonic-gate 			    &pp->dma_cookie,
39117c478bd9Sstevel@tonic-gate 			    &pp->dma_cookie_count) != DDI_SUCCESS) {
39127c478bd9Sstevel@tonic-gate 				ecpp_error(pp->dip,
39137c478bd9Sstevel@tonic-gate 				    "ecpp_fifo_timer: ddi_dma_getwin failed\n");
39147c478bd9Sstevel@tonic-gate 				goto dma_done;
39157c478bd9Sstevel@tonic-gate 			}
39167c478bd9Sstevel@tonic-gate 
39177c478bd9Sstevel@tonic-gate 			pp->dma_curwin++;
39187c478bd9Sstevel@tonic-gate 		} else {
39197c478bd9Sstevel@tonic-gate 			goto dma_done;
39207c478bd9Sstevel@tonic-gate 		}
39217c478bd9Sstevel@tonic-gate 
39227c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_fifo_timer: next addr=%llx len=%d\n",
392319397407SSherry Moore 		    pp->dma_cookie.dmac_address,
392419397407SSherry Moore 		    pp->dma_cookie.dmac_size);
39257c478bd9Sstevel@tonic-gate 
39267c478bd9Sstevel@tonic-gate 		/* kick off new transfer */
39277c478bd9Sstevel@tonic-gate 		if (ECPP_DMA_START(pp) != SUCCESS) {
39287c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
392919397407SSherry Moore 			    "ecpp_fifo_timer: dma_start failed\n");
39307c478bd9Sstevel@tonic-gate 			goto dma_done;
39317c478bd9Sstevel@tonic-gate 		}
39327c478bd9Sstevel@tonic-gate 
39337c478bd9Sstevel@tonic-gate 		(void) ecr_write(pp, (ecr & 0xe0) |
393419397407SSherry Moore 		    ECPP_DMA_ENABLE | ECPP_INTR_MASK);
39357c478bd9Sstevel@tonic-gate 
39367c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
39377c478bd9Sstevel@tonic-gate 
39387c478bd9Sstevel@tonic-gate 		if (timeout_id) {
39397c478bd9Sstevel@tonic-gate 			(void) untimeout(timeout_id);
39407c478bd9Sstevel@tonic-gate 		}
39417c478bd9Sstevel@tonic-gate 		return;
39427c478bd9Sstevel@tonic-gate 
39437c478bd9Sstevel@tonic-gate 	dma_done:
39447c478bd9Sstevel@tonic-gate 		if (ddi_dma_unbind_handle(pp->dma_handle) != DDI_SUCCESS) {
39457c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "ecpp_fifo_timer: unbind failed\n");
39467c478bd9Sstevel@tonic-gate 		} else {
39477c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "ecpp_fifo_timer: unbind ok\n");
39487c478bd9Sstevel@tonic-gate 		}
39497c478bd9Sstevel@tonic-gate 	}
39507c478bd9Sstevel@tonic-gate 
39517c478bd9Sstevel@tonic-gate 	/*
39527c478bd9Sstevel@tonic-gate 	 * if we did not use the dmablock, the mblk that
39537c478bd9Sstevel@tonic-gate 	 * was used should be freed.
39547c478bd9Sstevel@tonic-gate 	 */
39557c478bd9Sstevel@tonic-gate 	if (pp->msg != NULL) {
39567c478bd9Sstevel@tonic-gate 		freemsg(pp->msg);
39577c478bd9Sstevel@tonic-gate 		pp->msg = NULL;
39587c478bd9Sstevel@tonic-gate 	}
39597c478bd9Sstevel@tonic-gate 
39607c478bd9Sstevel@tonic-gate 	/* The port is no longer active */
39617c478bd9Sstevel@tonic-gate 	pp->e_busy = ECPP_IDLE;
39627c478bd9Sstevel@tonic-gate 
39637c478bd9Sstevel@tonic-gate 	qenable(pp->writeq);
39647c478bd9Sstevel@tonic-gate 
39657c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->umutex);
39667c478bd9Sstevel@tonic-gate 
39677c478bd9Sstevel@tonic-gate 	if (timeout_id) {
39687c478bd9Sstevel@tonic-gate 		(void) untimeout(timeout_id);
39697c478bd9Sstevel@tonic-gate 	}
39707c478bd9Sstevel@tonic-gate }
39717c478bd9Sstevel@tonic-gate 
39727c478bd9Sstevel@tonic-gate /*
39737c478bd9Sstevel@tonic-gate  * In Compatibility mode, check if the peripheral is ready to accept data
39747c478bd9Sstevel@tonic-gate  */
39757c478bd9Sstevel@tonic-gate static uint8_t
ecpp_check_status(struct ecppunit * pp)39767c478bd9Sstevel@tonic-gate ecpp_check_status(struct ecppunit *pp)
39777c478bd9Sstevel@tonic-gate {
39787c478bd9Sstevel@tonic-gate 	uint8_t	dsr;
39797c478bd9Sstevel@tonic-gate 	uint8_t statmask;
39807c478bd9Sstevel@tonic-gate 
39817c478bd9Sstevel@tonic-gate 	if (pp->current_mode == ECPP_ECP_MODE ||
39827c478bd9Sstevel@tonic-gate 	    pp->current_mode == ECPP_DIAG_MODE)
39837c478bd9Sstevel@tonic-gate 		return (SUCCESS);
39847c478bd9Sstevel@tonic-gate 
39857c478bd9Sstevel@tonic-gate 	statmask = ECPP_nERR | ECPP_SLCT | ECPP_nBUSY | ECPP_nACK;
39867c478bd9Sstevel@tonic-gate 
39877c478bd9Sstevel@tonic-gate 	dsr = DSR_READ(pp);
39887c478bd9Sstevel@tonic-gate 	if ((dsr & ECPP_PE) || ((dsr & statmask) != statmask)) {
39897c478bd9Sstevel@tonic-gate 		pp->e_busy = ECPP_ERR;
39907c478bd9Sstevel@tonic-gate 		return (FAILURE);
39917c478bd9Sstevel@tonic-gate 	} else {
39927c478bd9Sstevel@tonic-gate 		return (SUCCESS);
39937c478bd9Sstevel@tonic-gate 	}
39947c478bd9Sstevel@tonic-gate }
39957c478bd9Sstevel@tonic-gate 
39967c478bd9Sstevel@tonic-gate /*
39977c478bd9Sstevel@tonic-gate  * if the peripheral is not ready to accept data, write service routine
39987c478bd9Sstevel@tonic-gate  * periodically reschedules itself to recheck peripheral status
39997c478bd9Sstevel@tonic-gate  * and start data transfer as soon as possible
40007c478bd9Sstevel@tonic-gate  */
40017c478bd9Sstevel@tonic-gate static void
ecpp_wsrv_timer(void * arg)40027c478bd9Sstevel@tonic-gate ecpp_wsrv_timer(void *arg)
40037c478bd9Sstevel@tonic-gate {
40047c478bd9Sstevel@tonic-gate 	struct ecppunit *pp = arg;
40057c478bd9Sstevel@tonic-gate 
40067c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecpp_wsrv_timer: starting\n");
40077c478bd9Sstevel@tonic-gate 
40087c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->umutex);
40097c478bd9Sstevel@tonic-gate 
40107c478bd9Sstevel@tonic-gate 	if (pp->wsrv_timer_id == 0) {
40117c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
40127c478bd9Sstevel@tonic-gate 		return;
40137c478bd9Sstevel@tonic-gate 	} else {
40147c478bd9Sstevel@tonic-gate 		pp->wsrv_timer_id = 0;
40157c478bd9Sstevel@tonic-gate 	}
40167c478bd9Sstevel@tonic-gate 
40177c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecpp_wsrv_timer: qenabling...\n");
40187c478bd9Sstevel@tonic-gate 
40197c478bd9Sstevel@tonic-gate 	qenable(pp->writeq);
40207c478bd9Sstevel@tonic-gate 
40217c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->umutex);
40227c478bd9Sstevel@tonic-gate }
40237c478bd9Sstevel@tonic-gate 
40247c478bd9Sstevel@tonic-gate /*
40257c478bd9Sstevel@tonic-gate  * Allocate a message indicating a backchannel request
40267c478bd9Sstevel@tonic-gate  * and put it on the write queue
40277c478bd9Sstevel@tonic-gate  */
40287c478bd9Sstevel@tonic-gate static int
ecpp_backchan_req(struct ecppunit * pp)40297c478bd9Sstevel@tonic-gate ecpp_backchan_req(struct ecppunit *pp)
40307c478bd9Sstevel@tonic-gate {
40317c478bd9Sstevel@tonic-gate 	mblk_t	*mp;
40327c478bd9Sstevel@tonic-gate 
40337c478bd9Sstevel@tonic-gate 	if ((mp = allocb(sizeof (int), BPRI_MED)) == NULL) {
40347c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_backchan_req: allocb failed\n");
40357c478bd9Sstevel@tonic-gate 		return (FAILURE);
40367c478bd9Sstevel@tonic-gate 	} else {
40377c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_CTL;
40387c478bd9Sstevel@tonic-gate 		*(int *)mp->b_rptr = ECPP_BACKCHANNEL;
40397c478bd9Sstevel@tonic-gate 		mp->b_wptr = mp->b_rptr + sizeof (int);
40407c478bd9Sstevel@tonic-gate 		if (!putbq(pp->writeq, mp)) {
40417c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "ecpp_backchan_req:putbq failed\n");
40427c478bd9Sstevel@tonic-gate 			freemsg(mp);
40437c478bd9Sstevel@tonic-gate 			return (FAILURE);
40447c478bd9Sstevel@tonic-gate 		}
40457c478bd9Sstevel@tonic-gate 		return (SUCCESS);
40467c478bd9Sstevel@tonic-gate 	}
40477c478bd9Sstevel@tonic-gate }
40487c478bd9Sstevel@tonic-gate 
40497c478bd9Sstevel@tonic-gate /*
40507c478bd9Sstevel@tonic-gate  * Cancel the function scheduled with timeout(9F)
40517c478bd9Sstevel@tonic-gate  * This function is to be called with the mutex held
40527c478bd9Sstevel@tonic-gate  */
40537c478bd9Sstevel@tonic-gate static void
ecpp_untimeout_unblock(struct ecppunit * pp,timeout_id_t * id)40547c478bd9Sstevel@tonic-gate ecpp_untimeout_unblock(struct ecppunit *pp, timeout_id_t *id)
40557c478bd9Sstevel@tonic-gate {
40567c478bd9Sstevel@tonic-gate 	timeout_id_t	saved_id;
40577c478bd9Sstevel@tonic-gate 
40587c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->umutex));
40597c478bd9Sstevel@tonic-gate 
40607c478bd9Sstevel@tonic-gate 	if (*id) {
40617c478bd9Sstevel@tonic-gate 		saved_id = *id;
40627c478bd9Sstevel@tonic-gate 		*id = 0;
40637c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
40647c478bd9Sstevel@tonic-gate 		(void) untimeout(saved_id);
40657c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
40667c478bd9Sstevel@tonic-gate 	}
40677c478bd9Sstevel@tonic-gate }
40687c478bd9Sstevel@tonic-gate 
40697c478bd9Sstevel@tonic-gate /*
40707c478bd9Sstevel@tonic-gate  * get prnio interface capabilities
40717c478bd9Sstevel@tonic-gate  */
40727c478bd9Sstevel@tonic-gate static uint_t
ecpp_get_prn_ifcap(struct ecppunit * pp)40737c478bd9Sstevel@tonic-gate ecpp_get_prn_ifcap(struct ecppunit *pp)
40747c478bd9Sstevel@tonic-gate {
40757c478bd9Sstevel@tonic-gate 	uint_t	ifcap;
40767c478bd9Sstevel@tonic-gate 
40777c478bd9Sstevel@tonic-gate 	ifcap = PRN_1284_DEVID | PRN_TIMEOUTS | PRN_STREAMS;
40787c478bd9Sstevel@tonic-gate 
40797c478bd9Sstevel@tonic-gate 	/* status (DSR) only makes sense in Centronics & Compat modes */
40807c478bd9Sstevel@tonic-gate 	if (pp->current_mode == ECPP_CENTRONICS ||
40817c478bd9Sstevel@tonic-gate 	    pp->current_mode == ECPP_COMPAT_MODE) {
40827c478bd9Sstevel@tonic-gate 		ifcap |= PRN_1284_STATUS;
40837c478bd9Sstevel@tonic-gate 	} else if (pp->current_mode == ECPP_NIBBLE_MODE ||
408419397407SSherry Moore 	    pp->current_mode == ECPP_ECP_MODE) {
40857c478bd9Sstevel@tonic-gate 		ifcap |= PRN_BIDI;
40867c478bd9Sstevel@tonic-gate 	}
40877c478bd9Sstevel@tonic-gate 
40887c478bd9Sstevel@tonic-gate 	return (ifcap);
40897c478bd9Sstevel@tonic-gate }
40907c478bd9Sstevel@tonic-gate 
40917c478bd9Sstevel@tonic-gate /*
40927c478bd9Sstevel@tonic-gate  * Determine SuperI/O type
40937c478bd9Sstevel@tonic-gate  */
40947c478bd9Sstevel@tonic-gate static struct ecpp_hw_bind *
ecpp_determine_sio_type(struct ecppunit * pp)40957c478bd9Sstevel@tonic-gate ecpp_determine_sio_type(struct ecppunit *pp)
40967c478bd9Sstevel@tonic-gate {
40977c478bd9Sstevel@tonic-gate 	struct ecpp_hw_bind	*hw_bind;
40987c478bd9Sstevel@tonic-gate 	char			*name;
40997c478bd9Sstevel@tonic-gate 	int			i;
41007c478bd9Sstevel@tonic-gate 
41017c478bd9Sstevel@tonic-gate 	name = ddi_binding_name(pp->dip);
41027c478bd9Sstevel@tonic-gate 
41037c478bd9Sstevel@tonic-gate 	for (hw_bind = NULL, i = 0; i < NELEM(ecpp_hw_bind); i++) {
41047c478bd9Sstevel@tonic-gate 		if (strcmp(name, ecpp_hw_bind[i].name) == 0) {
41057c478bd9Sstevel@tonic-gate 			hw_bind = &ecpp_hw_bind[i];
41067c478bd9Sstevel@tonic-gate 			break;
41077c478bd9Sstevel@tonic-gate 		}
41087c478bd9Sstevel@tonic-gate 	}
41097c478bd9Sstevel@tonic-gate 
41107c478bd9Sstevel@tonic-gate 	return (hw_bind);
41117c478bd9Sstevel@tonic-gate }
41127c478bd9Sstevel@tonic-gate 
41137c478bd9Sstevel@tonic-gate 
41147c478bd9Sstevel@tonic-gate /*
41157c478bd9Sstevel@tonic-gate  *
41167c478bd9Sstevel@tonic-gate  * IEEE 1284 support routines:
41172520aea3SToomas Soome  *	negotiation and termination;
41187c478bd9Sstevel@tonic-gate  *	phase transitions;
41197c478bd9Sstevel@tonic-gate  *	device ID;
41207c478bd9Sstevel@tonic-gate  *
41217c478bd9Sstevel@tonic-gate  */
41227c478bd9Sstevel@tonic-gate 
41237c478bd9Sstevel@tonic-gate /*
41247c478bd9Sstevel@tonic-gate  * Interface initialization, abnormal termination into Compatibility mode
41257c478bd9Sstevel@tonic-gate  *
41267c478bd9Sstevel@tonic-gate  * Peripheral may be non-1284, so we set current mode to ECPP_CENTRONICS
41277c478bd9Sstevel@tonic-gate  */
41287c478bd9Sstevel@tonic-gate static void
ecpp_1284_init_interface(struct ecppunit * pp)41297c478bd9Sstevel@tonic-gate ecpp_1284_init_interface(struct ecppunit *pp)
41307c478bd9Sstevel@tonic-gate {
41317c478bd9Sstevel@tonic-gate 	ECR_WRITE(pp, ECPP_INTR_SRV | ECPP_INTR_MASK | ECR_mode_001);
41327c478bd9Sstevel@tonic-gate 
41337c478bd9Sstevel@tonic-gate 	/*
41347c478bd9Sstevel@tonic-gate 	 * Toggle the nInit signal if configured in ecpp.conf
41357c478bd9Sstevel@tonic-gate 	 * for most peripherals it is not needed
41367c478bd9Sstevel@tonic-gate 	 */
41377c478bd9Sstevel@tonic-gate 	if (pp->init_seq == TRUE) {
41387c478bd9Sstevel@tonic-gate 		DCR_WRITE(pp, ECPP_SLCTIN);
41397c478bd9Sstevel@tonic-gate 		drv_usecwait(50);	/* T(ER) = 50us */
41407c478bd9Sstevel@tonic-gate 	}
41417c478bd9Sstevel@tonic-gate 
41427c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, ECPP_nINIT | ECPP_SLCTIN);
41437c478bd9Sstevel@tonic-gate 
41447c478bd9Sstevel@tonic-gate 	pp->current_mode = pp->backchannel = ECPP_CENTRONICS;
41457c478bd9Sstevel@tonic-gate 	pp->current_phase = ECPP_PHASE_C_IDLE;
41467c478bd9Sstevel@tonic-gate 	ECPP_CONFIG_MODE(pp);
41477c478bd9Sstevel@tonic-gate 	pp->to_mode[pp->current_mode]++;
41487c478bd9Sstevel@tonic-gate 
41497c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecpp_1284_init_interface: ok\n");
41507c478bd9Sstevel@tonic-gate }
41517c478bd9Sstevel@tonic-gate 
41527c478bd9Sstevel@tonic-gate /*
41537c478bd9Sstevel@tonic-gate  * ECP mode negotiation
41547c478bd9Sstevel@tonic-gate  */
41557c478bd9Sstevel@tonic-gate static int
ecp_negotiation(struct ecppunit * pp)41567c478bd9Sstevel@tonic-gate ecp_negotiation(struct ecppunit *pp)
41577c478bd9Sstevel@tonic-gate {
41587c478bd9Sstevel@tonic-gate 	uint8_t dsr;
41597c478bd9Sstevel@tonic-gate 
41607c478bd9Sstevel@tonic-gate 	/* ECP mode negotiation */
41617c478bd9Sstevel@tonic-gate 
41627c478bd9Sstevel@tonic-gate 	if (ecpp_1284_negotiation(pp, ECPP_XREQ_ECP, &dsr) == FAILURE)
41637c478bd9Sstevel@tonic-gate 		return (FAILURE);
41647c478bd9Sstevel@tonic-gate 
41657c478bd9Sstevel@tonic-gate 	/* Event 5: peripheral deasserts PError and Busy, asserts Select */
41667c478bd9Sstevel@tonic-gate 	if ((dsr & (ECPP_PE | ECPP_nBUSY | ECPP_SLCT)) !=
416719397407SSherry Moore 	    (ECPP_nBUSY | ECPP_SLCT)) {
41687c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
416919397407SSherry Moore 		    "ecp_negotiation: failed event 5 %x\n", DSR_READ(pp));
41707c478bd9Sstevel@tonic-gate 		(void) ecpp_1284_termination(pp);
41717c478bd9Sstevel@tonic-gate 		return (FAILURE);
41727c478bd9Sstevel@tonic-gate 	}
41737c478bd9Sstevel@tonic-gate 
41747c478bd9Sstevel@tonic-gate 	/* entered Setup Phase */
41757c478bd9Sstevel@tonic-gate 	pp->current_phase = ECPP_PHASE_ECP_SETUP;
41767c478bd9Sstevel@tonic-gate 
41777c478bd9Sstevel@tonic-gate 	/* Event 30: host asserts nAutoFd */
41787c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, ECPP_nINIT | ECPP_AFX);
41797c478bd9Sstevel@tonic-gate 
41807c478bd9Sstevel@tonic-gate 	/* Event 31: peripheral asserts PError */
41817c478bd9Sstevel@tonic-gate 	if (wait_dsr(pp, ECPP_PE, ECPP_PE, 35000) < 0) {
41827c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
418319397407SSherry Moore 		    "ecp_negotiation: failed event 31 %x\n", DSR_READ(pp));
41847c478bd9Sstevel@tonic-gate 		(void) ecpp_1284_termination(pp);
41857c478bd9Sstevel@tonic-gate 		return (FAILURE);
41867c478bd9Sstevel@tonic-gate 	}
41877c478bd9Sstevel@tonic-gate 
41887c478bd9Sstevel@tonic-gate 	/* entered Forward Idle Phase */
41897c478bd9Sstevel@tonic-gate 	pp->current_phase = ECPP_PHASE_ECP_FWD_IDLE;
41907c478bd9Sstevel@tonic-gate 
41917c478bd9Sstevel@tonic-gate 	/* successful negotiation into ECP mode */
41927c478bd9Sstevel@tonic-gate 	pp->current_mode = ECPP_ECP_MODE;
41937c478bd9Sstevel@tonic-gate 	pp->backchannel = ECPP_ECP_MODE;
41947c478bd9Sstevel@tonic-gate 
41957c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecp_negotiation: ok\n");
41967c478bd9Sstevel@tonic-gate 
41977c478bd9Sstevel@tonic-gate 	return (SUCCESS);
41987c478bd9Sstevel@tonic-gate }
41997c478bd9Sstevel@tonic-gate 
42007c478bd9Sstevel@tonic-gate /*
42017c478bd9Sstevel@tonic-gate  * Nibble mode negotiation
42027c478bd9Sstevel@tonic-gate  */
42037c478bd9Sstevel@tonic-gate static int
nibble_negotiation(struct ecppunit * pp)42047c478bd9Sstevel@tonic-gate nibble_negotiation(struct ecppunit *pp)
42057c478bd9Sstevel@tonic-gate {
42067c478bd9Sstevel@tonic-gate 	uint8_t	dsr;
42077c478bd9Sstevel@tonic-gate 
42087c478bd9Sstevel@tonic-gate 	if (ecpp_1284_negotiation(pp, ECPP_XREQ_NIBBLE, &dsr) == FAILURE) {
42097c478bd9Sstevel@tonic-gate 		return (FAILURE);
42107c478bd9Sstevel@tonic-gate 	}
42117c478bd9Sstevel@tonic-gate 
42127c478bd9Sstevel@tonic-gate 	/*
42137c478bd9Sstevel@tonic-gate 	 * If peripheral has data available, PE and nErr will
42147c478bd9Sstevel@tonic-gate 	 * be set low at Event 5 & 6.
42157c478bd9Sstevel@tonic-gate 	 */
42167c478bd9Sstevel@tonic-gate 	if ((dsr & (ECPP_PE | ECPP_nERR)) == 0) {
42177c478bd9Sstevel@tonic-gate 		pp->current_phase = ECPP_PHASE_NIBT_AVAIL;
42187c478bd9Sstevel@tonic-gate 	} else {
42197c478bd9Sstevel@tonic-gate 		pp->current_phase = ECPP_PHASE_NIBT_NAVAIL;
42207c478bd9Sstevel@tonic-gate 	}
42217c478bd9Sstevel@tonic-gate 
42227c478bd9Sstevel@tonic-gate 	/* successful negotiation into Nibble mode */
42237c478bd9Sstevel@tonic-gate 	pp->current_mode = ECPP_NIBBLE_MODE;
42247c478bd9Sstevel@tonic-gate 	pp->backchannel = ECPP_NIBBLE_MODE;
42257c478bd9Sstevel@tonic-gate 
42267c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "nibble_negotiation: ok (phase=%x)\n",
422719397407SSherry Moore 	    pp->current_phase);
42287c478bd9Sstevel@tonic-gate 
42297c478bd9Sstevel@tonic-gate 	return (SUCCESS);
42307c478bd9Sstevel@tonic-gate 
42317c478bd9Sstevel@tonic-gate }
42327c478bd9Sstevel@tonic-gate 
42337c478bd9Sstevel@tonic-gate /*
42347c478bd9Sstevel@tonic-gate  * Wait ptimeout usec for periph to set 'mask' bits to 'val' state
42357c478bd9Sstevel@tonic-gate  *
42367c478bd9Sstevel@tonic-gate  * return value < 0 indicates timeout
42377c478bd9Sstevel@tonic-gate  */
42387c478bd9Sstevel@tonic-gate static int
wait_dsr(struct ecppunit * pp,uint8_t mask,uint8_t val,int ptimeout)42397c478bd9Sstevel@tonic-gate wait_dsr(struct ecppunit *pp, uint8_t mask, uint8_t val, int ptimeout)
42407c478bd9Sstevel@tonic-gate {
42417c478bd9Sstevel@tonic-gate 	while (((DSR_READ(pp) & mask) != val) && ptimeout--) {
42427c478bd9Sstevel@tonic-gate 		drv_usecwait(1);
42437c478bd9Sstevel@tonic-gate 	}
42447c478bd9Sstevel@tonic-gate 
42457c478bd9Sstevel@tonic-gate 	return (ptimeout);
42467c478bd9Sstevel@tonic-gate }
42477c478bd9Sstevel@tonic-gate 
42487c478bd9Sstevel@tonic-gate /*
42497c478bd9Sstevel@tonic-gate  * 1284 negotiation Events 0..6
42507c478bd9Sstevel@tonic-gate  * required mode is indicated by extensibility request value
42517c478bd9Sstevel@tonic-gate  *
42527c478bd9Sstevel@tonic-gate  * After successful negotiation SUCCESS is returned and
42537c478bd9Sstevel@tonic-gate  * current mode is set according to xreq,
42547c478bd9Sstevel@tonic-gate  * otherwise FAILURE is returned and current mode is set to
42557c478bd9Sstevel@tonic-gate  * either COMPAT (1284 periph) or CENTRONICS (non-1284 periph)
42567c478bd9Sstevel@tonic-gate  *
42577c478bd9Sstevel@tonic-gate  * Current phase must be set by the caller (mode-specific negotiation)
42587c478bd9Sstevel@tonic-gate  *
42597c478bd9Sstevel@tonic-gate  * If rdsr is not NULL, DSR value after Event 6 is stored here
42607c478bd9Sstevel@tonic-gate  */
42617c478bd9Sstevel@tonic-gate static int
ecpp_1284_negotiation(struct ecppunit * pp,uint8_t xreq,uint8_t * rdsr)42627c478bd9Sstevel@tonic-gate ecpp_1284_negotiation(struct ecppunit *pp, uint8_t xreq, uint8_t *rdsr)
42637c478bd9Sstevel@tonic-gate {
42647c478bd9Sstevel@tonic-gate 	int xflag;
42657c478bd9Sstevel@tonic-gate 
42667c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "nego(%x): entering...\n", xreq);
42677c478bd9Sstevel@tonic-gate 
42687c478bd9Sstevel@tonic-gate 	/* negotiation should start in Compatibility mode */
42697c478bd9Sstevel@tonic-gate 	(void) ecpp_1284_termination(pp);
42707c478bd9Sstevel@tonic-gate 
42717c478bd9Sstevel@tonic-gate 	/* Set host into Compat mode */
42727c478bd9Sstevel@tonic-gate 	ECR_WRITE(pp, ECPP_INTR_SRV | ECPP_INTR_MASK | ECR_mode_001);
42737c478bd9Sstevel@tonic-gate 
42747c478bd9Sstevel@tonic-gate 	pp->current_phase = ECPP_PHASE_NEGO;
42757c478bd9Sstevel@tonic-gate 
42767c478bd9Sstevel@tonic-gate 	/* Event 0: host sets extensibility request on data lines */
42777c478bd9Sstevel@tonic-gate 	DATAR_WRITE(pp, xreq);
42787c478bd9Sstevel@tonic-gate 
42797c478bd9Sstevel@tonic-gate 	/* Event 1: host deassert nSelectin and assert nAutoFd */
42807c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, ECPP_nINIT | ECPP_AFX);
42817c478bd9Sstevel@tonic-gate 
42827c478bd9Sstevel@tonic-gate 	drv_usecwait(1);	/* Tp(ecp) == 0.5us */
42837c478bd9Sstevel@tonic-gate 
42847c478bd9Sstevel@tonic-gate 	/*
42857c478bd9Sstevel@tonic-gate 	 * Event 2: peripheral asserts nAck, deasserts nFault,
42862520aea3SToomas Soome 	 *			asserts Select, asserts PError
42877c478bd9Sstevel@tonic-gate 	 */
42887c478bd9Sstevel@tonic-gate 	if (wait_dsr(pp, ECPP_nERR | ECPP_SLCT | ECPP_PE | ECPP_nACK,
428919397407SSherry Moore 	    ECPP_nERR | ECPP_SLCT | ECPP_PE, 35000) < 0) {
42907c478bd9Sstevel@tonic-gate 		/* peripheral is not 1284-compliant */
42917c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
429219397407SSherry Moore 		    "nego(%x): failed event 2 %x\n", xreq, DSR_READ(pp));
42937c478bd9Sstevel@tonic-gate 		(void) ecpp_1284_termination(pp);
42947c478bd9Sstevel@tonic-gate 		return (FAILURE);
42957c478bd9Sstevel@tonic-gate 	}
42967c478bd9Sstevel@tonic-gate 
42977c478bd9Sstevel@tonic-gate 	/*
42987c478bd9Sstevel@tonic-gate 	 * Event 3: host asserts nStrobe, latching extensibility value into
42997c478bd9Sstevel@tonic-gate 	 * peripherals input latch.
43007c478bd9Sstevel@tonic-gate 	 */
43017c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, ECPP_nINIT | ECPP_AFX | ECPP_STB);
43027c478bd9Sstevel@tonic-gate 
43037c478bd9Sstevel@tonic-gate 	drv_usecwait(2);	/* Tp(ecp) = 0.5us */
43047c478bd9Sstevel@tonic-gate 
43057c478bd9Sstevel@tonic-gate 	/*
43067c478bd9Sstevel@tonic-gate 	 * Event 4: hosts deasserts nStrobe and nAutoFD to acknowledge that
43077c478bd9Sstevel@tonic-gate 	 * it has recognized an 1284 compatible peripheral
43087c478bd9Sstevel@tonic-gate 	 */
43097c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, ECPP_nINIT);
43107c478bd9Sstevel@tonic-gate 
43117c478bd9Sstevel@tonic-gate 	/*
43127c478bd9Sstevel@tonic-gate 	 * Event 5: Peripheral confirms it supports requested extension
43137c478bd9Sstevel@tonic-gate 	 * For Nibble mode Xflag must be low, otherwise it must be high
43147c478bd9Sstevel@tonic-gate 	 */
43157c478bd9Sstevel@tonic-gate 	xflag = (xreq == ECPP_XREQ_NIBBLE) ? 0 : ECPP_SLCT;
43167c478bd9Sstevel@tonic-gate 
43177c478bd9Sstevel@tonic-gate 	/*
43187c478bd9Sstevel@tonic-gate 	 * Event 6: Peripheral sets nAck high
43197c478bd9Sstevel@tonic-gate 	 * indicating that status lines are valid
43207c478bd9Sstevel@tonic-gate 	 */
43217c478bd9Sstevel@tonic-gate 	if (wait_dsr(pp, ECPP_nACK, ECPP_nACK, 35000) < 0) {
43227c478bd9Sstevel@tonic-gate 		/* Something wrong with peripheral */
43237c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
432419397407SSherry Moore 		    "nego(%x): failed event 6 %x\n", xreq, DSR_READ(pp));
43257c478bd9Sstevel@tonic-gate 		(void) ecpp_1284_termination(pp);
43267c478bd9Sstevel@tonic-gate 		return (FAILURE);
43277c478bd9Sstevel@tonic-gate 	}
43287c478bd9Sstevel@tonic-gate 
43297c478bd9Sstevel@tonic-gate 	if ((DSR_READ(pp) & ECPP_SLCT) != xflag) {
43307c478bd9Sstevel@tonic-gate 		/* Extensibility value is not supported */
43317c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
433219397407SSherry Moore 		    "nego(%x): failed event 5 %x\n", xreq, DSR_READ(pp));
43337c478bd9Sstevel@tonic-gate 		(void) ecpp_1284_termination(pp);
43347c478bd9Sstevel@tonic-gate 		return (FAILURE);
43357c478bd9Sstevel@tonic-gate 	}
43367c478bd9Sstevel@tonic-gate 
43377c478bd9Sstevel@tonic-gate 	if (rdsr) {
43387c478bd9Sstevel@tonic-gate 		*rdsr = DSR_READ(pp);
43397c478bd9Sstevel@tonic-gate 	}
43407c478bd9Sstevel@tonic-gate 
43417c478bd9Sstevel@tonic-gate 	return (SUCCESS);
43427c478bd9Sstevel@tonic-gate }
43437c478bd9Sstevel@tonic-gate 
43447c478bd9Sstevel@tonic-gate /*
43457c478bd9Sstevel@tonic-gate  * 1284 Termination: Events 22..28 - set link to Compatibility mode
43467c478bd9Sstevel@tonic-gate  *
43477c478bd9Sstevel@tonic-gate  * This routine is not designed for Immediate termination,
43487c478bd9Sstevel@tonic-gate  * caller must take care of waiting for a valid state,
43497c478bd9Sstevel@tonic-gate  * (in particular, in ECP mode current phase must be Forward Idle)
43507c478bd9Sstevel@tonic-gate  * otherwise interface will be reinitialized
43517c478bd9Sstevel@tonic-gate  *
43527c478bd9Sstevel@tonic-gate  * In case of Valid state termination SUCCESS is returned and
43537c478bd9Sstevel@tonic-gate  * current_mode is ECPP_COMPAT_MODE, current phase is ECPP_PHASE_C_IDLE
43547c478bd9Sstevel@tonic-gate  * Otherwise interface is reinitialized, FAILURE is returned and
43557c478bd9Sstevel@tonic-gate  * current mode is ECPP_CENTRONICS, current phase is ECPP_PHASE_C_IDLE
43567c478bd9Sstevel@tonic-gate  */
43577c478bd9Sstevel@tonic-gate static int
ecpp_1284_termination(struct ecppunit * pp)43587c478bd9Sstevel@tonic-gate ecpp_1284_termination(struct ecppunit *pp)
43597c478bd9Sstevel@tonic-gate {
43607c478bd9Sstevel@tonic-gate 	int	previous_mode = pp->current_mode;
43617c478bd9Sstevel@tonic-gate 
43627c478bd9Sstevel@tonic-gate 	if (((pp->current_mode == ECPP_COMPAT_MODE ||
43637c478bd9Sstevel@tonic-gate 	    pp->current_mode == ECPP_CENTRONICS) &&
43647c478bd9Sstevel@tonic-gate 	    pp->current_phase == ECPP_PHASE_C_IDLE) ||
43657c478bd9Sstevel@tonic-gate 	    pp->current_mode == ECPP_DIAG_MODE) {
43667c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "termination: not needed\n");
43677c478bd9Sstevel@tonic-gate 		return (SUCCESS);
43687c478bd9Sstevel@tonic-gate 	}
43697c478bd9Sstevel@tonic-gate 
43707c478bd9Sstevel@tonic-gate 	/* Set host into Compat mode, interrupts disabled */
43717c478bd9Sstevel@tonic-gate 	ECPP_MASK_INTR(pp);
43727c478bd9Sstevel@tonic-gate 	ECR_WRITE(pp, ECPP_INTR_SRV | ECPP_INTR_MASK | ECR_mode_001);
43737c478bd9Sstevel@tonic-gate 
43747c478bd9Sstevel@tonic-gate 	pp->current_mode = ECPP_COMPAT_MODE;	/* needed by next function */
43757c478bd9Sstevel@tonic-gate 
43767c478bd9Sstevel@tonic-gate 	ECPP_CONFIG_MODE(pp);
43777c478bd9Sstevel@tonic-gate 
43787c478bd9Sstevel@tonic-gate 	/*
43797c478bd9Sstevel@tonic-gate 	 * EPP mode uses simple nInit pulse for termination
43807c478bd9Sstevel@tonic-gate 	 */
43817c478bd9Sstevel@tonic-gate 	if (previous_mode == ECPP_EPP_MODE) {
43827c478bd9Sstevel@tonic-gate 		/* Event 68: host sets nInit low */
43837c478bd9Sstevel@tonic-gate 		DCR_WRITE(pp, 0);
43847c478bd9Sstevel@tonic-gate 
43857c478bd9Sstevel@tonic-gate 		drv_usecwait(55);	/* T(ER) = 50us */
43867c478bd9Sstevel@tonic-gate 
43877c478bd9Sstevel@tonic-gate 		/* Event 69: host sets nInit high */
43887c478bd9Sstevel@tonic-gate 		DCR_WRITE(pp, ECPP_nINIT | ECPP_SLCTIN);
43897c478bd9Sstevel@tonic-gate 
43907c478bd9Sstevel@tonic-gate 		goto endterm;
43917c478bd9Sstevel@tonic-gate 	}
43927c478bd9Sstevel@tonic-gate 
43937c478bd9Sstevel@tonic-gate 	/* terminate peripheral to Compat mode */
43947c478bd9Sstevel@tonic-gate 	pp->current_phase = ECPP_PHASE_TERM;
43957c478bd9Sstevel@tonic-gate 
43967c478bd9Sstevel@tonic-gate 	/* Event 22: hosts sets nSelectIn low and nAutoFd high */
43977c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, ECPP_nINIT | ECPP_SLCTIN);
43987c478bd9Sstevel@tonic-gate 
43997c478bd9Sstevel@tonic-gate 	/* Event 23: peripheral deasserts nFault and nBusy */
44007c478bd9Sstevel@tonic-gate 	/* Event 24: peripheral asserts nAck */
44017c478bd9Sstevel@tonic-gate 	if (wait_dsr(pp, ECPP_nERR | ECPP_nBUSY | ECPP_nACK,
440219397407SSherry Moore 	    ECPP_nERR, 35000) < 0) {
44037c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
440419397407SSherry Moore 		    "termination: failed events 23,24 %x\n", DSR_READ(pp));
44057c478bd9Sstevel@tonic-gate 		ecpp_1284_init_interface(pp);
44067c478bd9Sstevel@tonic-gate 		return (FAILURE);
44077c478bd9Sstevel@tonic-gate 	}
44087c478bd9Sstevel@tonic-gate 
44097c478bd9Sstevel@tonic-gate 	drv_usecwait(1);	/* Tp = 0.5us */
44107c478bd9Sstevel@tonic-gate 
44117c478bd9Sstevel@tonic-gate 	/* Event 25: hosts sets nAutoFd low */
44127c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, ECPP_nINIT | ECPP_SLCTIN | ECPP_AFX);
44137c478bd9Sstevel@tonic-gate 
44147c478bd9Sstevel@tonic-gate 	/* Event 26: the peripheral puts itself in Compatible mode */
44157c478bd9Sstevel@tonic-gate 
44167c478bd9Sstevel@tonic-gate 	/* Event 27: peripheral deasserts nAck */
44177c478bd9Sstevel@tonic-gate 	if (wait_dsr(pp, ECPP_nACK, ECPP_nACK, 35000) < 0) {
44187c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
441919397407SSherry Moore 		    "termination: failed event 27 %x\n", DSR_READ(pp));
44207c478bd9Sstevel@tonic-gate 		ecpp_1284_init_interface(pp);
44217c478bd9Sstevel@tonic-gate 		return (FAILURE);
44227c478bd9Sstevel@tonic-gate 	}
44237c478bd9Sstevel@tonic-gate 
44247c478bd9Sstevel@tonic-gate 	drv_usecwait(1);	/* Tp = 0.5us */
44257c478bd9Sstevel@tonic-gate 
44267c478bd9Sstevel@tonic-gate 	/* Event 28: hosts deasserts nAutoFd */
44277c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, ECPP_nINIT | ECPP_SLCTIN);
44287c478bd9Sstevel@tonic-gate 
44297c478bd9Sstevel@tonic-gate 	drv_usecwait(1);	/* Tp = 0.5us */
44307c478bd9Sstevel@tonic-gate 
44317c478bd9Sstevel@tonic-gate endterm:
44327c478bd9Sstevel@tonic-gate 	/* Compatible mode Idle Phase */
44337c478bd9Sstevel@tonic-gate 	pp->current_phase = ECPP_PHASE_C_IDLE;
44347c478bd9Sstevel@tonic-gate 
44357c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "termination: completed %x %x\n",
443619397407SSherry Moore 	    DSR_READ(pp), DCR_READ(pp));
44377c478bd9Sstevel@tonic-gate 
44387c478bd9Sstevel@tonic-gate 	return (SUCCESS);
44397c478bd9Sstevel@tonic-gate }
44407c478bd9Sstevel@tonic-gate 
44417c478bd9Sstevel@tonic-gate /*
44427c478bd9Sstevel@tonic-gate  * Initiate ECP backchannel DMA transfer
44437c478bd9Sstevel@tonic-gate  */
44447c478bd9Sstevel@tonic-gate static uchar_t
ecp_peripheral2host(struct ecppunit * pp)44457c478bd9Sstevel@tonic-gate ecp_peripheral2host(struct ecppunit *pp)
44467c478bd9Sstevel@tonic-gate {
44477c478bd9Sstevel@tonic-gate 	mblk_t		*mp = NULL;
44487c478bd9Sstevel@tonic-gate 	size_t		len;
44497c478bd9Sstevel@tonic-gate 	uint32_t	xfer_time;
44507c478bd9Sstevel@tonic-gate 
44517c478bd9Sstevel@tonic-gate 	ASSERT(pp->current_mode == ECPP_ECP_MODE &&
445219397407SSherry Moore 	    pp->current_phase == ECPP_PHASE_ECP_REV_IDLE);
44537c478bd9Sstevel@tonic-gate 
44547c478bd9Sstevel@tonic-gate 	/*
44557c478bd9Sstevel@tonic-gate 	 * hardware generates cycles to receive data from the peripheral
44567c478bd9Sstevel@tonic-gate 	 * we only need to read from FIFO
44577c478bd9Sstevel@tonic-gate 	 */
44587c478bd9Sstevel@tonic-gate 
44597c478bd9Sstevel@tonic-gate 	/*
44607c478bd9Sstevel@tonic-gate 	 * If user issued read(2) of rev_resid bytes, xfer exactly this amount
44617c478bd9Sstevel@tonic-gate 	 * unless it exceeds ECP_REV_BLKSZ_MAX; otherwise try to read
44627c478bd9Sstevel@tonic-gate 	 * ECP_REV_BLKSZ_MAX or at least ECP_REV_BLKSZ bytes
44637c478bd9Sstevel@tonic-gate 	 */
44647c478bd9Sstevel@tonic-gate 	if (pp->nread > 0) {
44657c478bd9Sstevel@tonic-gate 		len = min(pp->nread, ECP_REV_BLKSZ_MAX);
44667c478bd9Sstevel@tonic-gate 	} else {
44677c478bd9Sstevel@tonic-gate 		len = ECP_REV_BLKSZ_MAX;
44687c478bd9Sstevel@tonic-gate 	}
44697c478bd9Sstevel@tonic-gate 
44707c478bd9Sstevel@tonic-gate 	pp->nread = 0;	/* clear after use */
44717c478bd9Sstevel@tonic-gate 
44727c478bd9Sstevel@tonic-gate 	/*
44737c478bd9Sstevel@tonic-gate 	 * Allocate mblk for data, make max 2 attepmts:
44747c478bd9Sstevel@tonic-gate 	 * if len bytes block fails, try our block size
44757c478bd9Sstevel@tonic-gate 	 */
44767c478bd9Sstevel@tonic-gate 	while ((mp = allocb(len, BPRI_MED)) == NULL) {
44777c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
447819397407SSherry Moore 		    "ecp_periph2host: failed allocb(%d)\n", len);
44797c478bd9Sstevel@tonic-gate 		if (len > ECP_REV_BLKSZ) {
44807c478bd9Sstevel@tonic-gate 			len = ECP_REV_BLKSZ;
44817c478bd9Sstevel@tonic-gate 		} else {
44827c478bd9Sstevel@tonic-gate 			break;
44837c478bd9Sstevel@tonic-gate 		}
44847c478bd9Sstevel@tonic-gate 	}
44857c478bd9Sstevel@tonic-gate 
44867c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
44877c478bd9Sstevel@tonic-gate 		goto fail;
44887c478bd9Sstevel@tonic-gate 	}
44897c478bd9Sstevel@tonic-gate 
44907c478bd9Sstevel@tonic-gate 	pp->msg = mp;
44917c478bd9Sstevel@tonic-gate 	pp->e_busy = ECPP_BUSY;
44927c478bd9Sstevel@tonic-gate 	pp->dma_dir = DDI_DMA_READ;
44937c478bd9Sstevel@tonic-gate 	pp->current_phase = ECPP_PHASE_ECP_REV_XFER;
44947c478bd9Sstevel@tonic-gate 
44957c478bd9Sstevel@tonic-gate 	if (ecpp_init_dma_xfer(pp, (caddr_t)mp->b_rptr, len) == FAILURE) {
44967c478bd9Sstevel@tonic-gate 		goto fail;
44977c478bd9Sstevel@tonic-gate 	}
44987c478bd9Sstevel@tonic-gate 
44997c478bd9Sstevel@tonic-gate 	/*
45007c478bd9Sstevel@tonic-gate 	 * there are two problems with defining ECP backchannel xfer timeout
45017c478bd9Sstevel@tonic-gate 	 *
45027c478bd9Sstevel@tonic-gate 	 * a) IEEE 1284 allows infinite time between backchannel bytes,
45037c478bd9Sstevel@tonic-gate 	 *    but we must stop at some point to send the data upstream,
45047c478bd9Sstevel@tonic-gate 	 *    look if any forward transfer requests are pending, etc;
45057c478bd9Sstevel@tonic-gate 	 *    all that done, we can continue with backchannel data;
45067c478bd9Sstevel@tonic-gate 	 *
45077c478bd9Sstevel@tonic-gate 	 * b) we don`t know how much data peripheral has;
45087c478bd9Sstevel@tonic-gate 	 *    DMA counter is set to our buffer size, which can be bigger
45097c478bd9Sstevel@tonic-gate 	 *    than needed - in this case a timeout must detect this;
45107c478bd9Sstevel@tonic-gate 	 *
45117c478bd9Sstevel@tonic-gate 	 * The timeout we schedule here serves as both the transfer timeout
45127c478bd9Sstevel@tonic-gate 	 * and a means of detecting backchannel stalls; in fact, there are
45137c478bd9Sstevel@tonic-gate 	 * two timeouts in one:
45147c478bd9Sstevel@tonic-gate 	 *
45157c478bd9Sstevel@tonic-gate 	 * - transfer timeout is based on the ECP bandwidth of ~1MB/sec and
45167c478bd9Sstevel@tonic-gate 	 *   equals the time needed to transfer the whole buffer
45177c478bd9Sstevel@tonic-gate 	 *   (but not less than ECP_REV_MINTOUT ms); if it occurs,
45187c478bd9Sstevel@tonic-gate 	 *   DMA is stopped and the data is sent upstream;
45197c478bd9Sstevel@tonic-gate 	 *
45207c478bd9Sstevel@tonic-gate 	 * - backchannel watchdog, which would look at DMA counter
45217c478bd9Sstevel@tonic-gate 	 *   every rev_watchdog ms and stop the transfer only
45227c478bd9Sstevel@tonic-gate 	 *   if the counter hasn`t changed since the last time;
45237c478bd9Sstevel@tonic-gate 	 *   otherwise it would save DMA counter value and restart itself;
45247c478bd9Sstevel@tonic-gate 	 *
45257c478bd9Sstevel@tonic-gate 	 * transfer timeout is a multiple of rev_watchdog
45267c478bd9Sstevel@tonic-gate 	 * and implemented as a downward counter
45277c478bd9Sstevel@tonic-gate 	 *
45287c478bd9Sstevel@tonic-gate 	 * on Grover, we can`t access DMAC registers while DMA is in flight,
45297c478bd9Sstevel@tonic-gate 	 * so we can`t have watchdog on Grover, only timeout
45307c478bd9Sstevel@tonic-gate 	 */
45317c478bd9Sstevel@tonic-gate 
45327c478bd9Sstevel@tonic-gate 	/* calculate number of watchdog invocations equal to the xfer timeout */
45337c478bd9Sstevel@tonic-gate 	xfer_time = max((1000 * len) / pp->ecp_rev_speed, ECP_REV_MINTOUT);
45347c478bd9Sstevel@tonic-gate #if defined(__x86)
45357c478bd9Sstevel@tonic-gate 	pp->rev_timeout_cnt = (pp->hw == &x86) ? 1 :
453619397407SSherry Moore 	    max(xfer_time / pp->rev_watchdog, 1);
45377c478bd9Sstevel@tonic-gate #else
45387c478bd9Sstevel@tonic-gate 	pp->rev_timeout_cnt = (pp->hw == &m1553) ? 1 :
453919397407SSherry Moore 	    max(xfer_time / pp->rev_watchdog, 1);
45407c478bd9Sstevel@tonic-gate #endif
45417c478bd9Sstevel@tonic-gate 
45427c478bd9Sstevel@tonic-gate 	pp->last_dmacnt = len;	/* nothing xferred yet */
45437c478bd9Sstevel@tonic-gate 
45447c478bd9Sstevel@tonic-gate 	pp->timeout_id = timeout(ecpp_ecp_read_timeout, (caddr_t)pp,
454519397407SSherry Moore 	    drv_usectohz(pp->rev_watchdog * 1000));
45467c478bd9Sstevel@tonic-gate 
45477c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecp_periph2host: DMA started len=%d\n"
454819397407SSherry Moore 	    "xfer_time=%d wdog=%d cnt=%d\n",
454919397407SSherry Moore 	    len, xfer_time, pp->rev_watchdog, pp->rev_timeout_cnt);
45507c478bd9Sstevel@tonic-gate 
45517c478bd9Sstevel@tonic-gate 	return (SUCCESS);
45527c478bd9Sstevel@tonic-gate 
45537c478bd9Sstevel@tonic-gate fail:
45547c478bd9Sstevel@tonic-gate 	if (mp) {
45557c478bd9Sstevel@tonic-gate 		freemsg(mp);
45567c478bd9Sstevel@tonic-gate 	}
45577c478bd9Sstevel@tonic-gate 	pp->e_busy = ECPP_IDLE;
45587c478bd9Sstevel@tonic-gate 	pp->current_phase = ECPP_PHASE_ECP_REV_IDLE;
45597c478bd9Sstevel@tonic-gate 
45607c478bd9Sstevel@tonic-gate 	return (FAILURE);
45617c478bd9Sstevel@tonic-gate }
45627c478bd9Sstevel@tonic-gate 
45637c478bd9Sstevel@tonic-gate /*
45647c478bd9Sstevel@tonic-gate  * ECP backchannel read timeout
45657c478bd9Sstevel@tonic-gate  * implements both backchannel watchdog and transfer timeout in ECP mode
45667c478bd9Sstevel@tonic-gate  * if the transfer is still in progress, reschedule itself,
45677c478bd9Sstevel@tonic-gate  * otherwise call completion routine
45687c478bd9Sstevel@tonic-gate  */
45697c478bd9Sstevel@tonic-gate static void
ecpp_ecp_read_timeout(void * arg)45707c478bd9Sstevel@tonic-gate ecpp_ecp_read_timeout(void *arg)
45717c478bd9Sstevel@tonic-gate {
45727c478bd9Sstevel@tonic-gate 	struct ecppunit	*pp = arg;
45737c478bd9Sstevel@tonic-gate 	size_t		dmacnt;
45747c478bd9Sstevel@tonic-gate 
45757c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->umutex);
45767c478bd9Sstevel@tonic-gate 
45777c478bd9Sstevel@tonic-gate 	if (pp->timeout_id == 0) {
45787c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
45797c478bd9Sstevel@tonic-gate 		return;
45807c478bd9Sstevel@tonic-gate 	} else {
45817c478bd9Sstevel@tonic-gate 		pp->timeout_id = 0;
45827c478bd9Sstevel@tonic-gate 	}
45837c478bd9Sstevel@tonic-gate 
45847c478bd9Sstevel@tonic-gate 	if (--pp->rev_timeout_cnt == 0) {
45857c478bd9Sstevel@tonic-gate 		/*
45867c478bd9Sstevel@tonic-gate 		 * Transfer timed out
45877c478bd9Sstevel@tonic-gate 		 */
45887c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecp_read_timeout: timeout\n");
45897c478bd9Sstevel@tonic-gate 		pp->xfer_tout++;
45907c478bd9Sstevel@tonic-gate 		ecpp_ecp_read_completion(pp);
45917c478bd9Sstevel@tonic-gate 	} else {
45927c478bd9Sstevel@tonic-gate 		/*
45937c478bd9Sstevel@tonic-gate 		 * Backchannel watchdog:
45947c478bd9Sstevel@tonic-gate 		 * look if DMA made any progress from the last time
45957c478bd9Sstevel@tonic-gate 		 */
45967c478bd9Sstevel@tonic-gate 		dmacnt = ECPP_DMA_GETCNT(pp);
45977c478bd9Sstevel@tonic-gate 		if (dmacnt - pp->last_dmacnt == 0) {
45987c478bd9Sstevel@tonic-gate 			/*
45997c478bd9Sstevel@tonic-gate 			 * No progress - stop the transfer and send
46007c478bd9Sstevel@tonic-gate 			 * whatever has been read so far up the stream
46017c478bd9Sstevel@tonic-gate 			 */
46027c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "ecp_read_timeout: no progress\n");
46037c478bd9Sstevel@tonic-gate 			pp->xfer_tout++;
46047c478bd9Sstevel@tonic-gate 			ecpp_ecp_read_completion(pp);
46057c478bd9Sstevel@tonic-gate 		} else {
46067c478bd9Sstevel@tonic-gate 			/*
46077c478bd9Sstevel@tonic-gate 			 * Something was transferred - restart ourselves
46087c478bd9Sstevel@tonic-gate 			 */
46097c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "ecp_read_timeout: restarting\n");
46107c478bd9Sstevel@tonic-gate 			pp->last_dmacnt = dmacnt;
46117c478bd9Sstevel@tonic-gate 			pp->timeout_id = timeout(ecpp_ecp_read_timeout,
461219397407SSherry Moore 			    (caddr_t)pp,
461319397407SSherry Moore 			    drv_usectohz(pp->rev_watchdog * 1000));
46147c478bd9Sstevel@tonic-gate 		}
46157c478bd9Sstevel@tonic-gate 	}
46167c478bd9Sstevel@tonic-gate 
46177c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->umutex);
46187c478bd9Sstevel@tonic-gate }
46197c478bd9Sstevel@tonic-gate 
46207c478bd9Sstevel@tonic-gate /*
46217c478bd9Sstevel@tonic-gate  * ECP backchannel read completion:
46227c478bd9Sstevel@tonic-gate  * stop the DMA, free DMA resources and send read data upstream
46237c478bd9Sstevel@tonic-gate  */
46247c478bd9Sstevel@tonic-gate static void
ecpp_ecp_read_completion(struct ecppunit * pp)46257c478bd9Sstevel@tonic-gate ecpp_ecp_read_completion(struct ecppunit *pp)
46267c478bd9Sstevel@tonic-gate {
46277c478bd9Sstevel@tonic-gate 	size_t	xfer_len, unx_len;
46287c478bd9Sstevel@tonic-gate 	mblk_t	*mp;
46297c478bd9Sstevel@tonic-gate 
46307c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->umutex));
46317c478bd9Sstevel@tonic-gate 	ASSERT(pp->current_mode == ECPP_ECP_MODE &&
463219397407SSherry Moore 	    pp->current_phase == ECPP_PHASE_ECP_REV_XFER);
46337c478bd9Sstevel@tonic-gate 	ASSERT(pp->msg != NULL);
46347c478bd9Sstevel@tonic-gate 
46357c478bd9Sstevel@tonic-gate 	/*
46367c478bd9Sstevel@tonic-gate 	 * Stop the transfer and unbind DMA handle
46377c478bd9Sstevel@tonic-gate 	 */
46387c478bd9Sstevel@tonic-gate 	if (ECPP_DMA_STOP(pp, &unx_len) == FAILURE) {
46397c478bd9Sstevel@tonic-gate 		unx_len = pp->resid;
46407c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecp_read_completion: failed dma_stop\n");
46417c478bd9Sstevel@tonic-gate 	}
46427c478bd9Sstevel@tonic-gate 
46437c478bd9Sstevel@tonic-gate 	mp = pp->msg;
46447c478bd9Sstevel@tonic-gate 	xfer_len = pp->resid - unx_len;	/* how much data was transferred */
46457c478bd9Sstevel@tonic-gate 
46467c478bd9Sstevel@tonic-gate 	if (ddi_dma_unbind_handle(pp->dma_handle) != DDI_SUCCESS) {
46477c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecp_read_completion: unbind failed.\n");
46487c478bd9Sstevel@tonic-gate 	}
46497c478bd9Sstevel@tonic-gate 
46507c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecp_read_completion: xfered %d bytes of %d\n",
465119397407SSherry Moore 	    xfer_len, pp->resid);
46527c478bd9Sstevel@tonic-gate 
46537c478bd9Sstevel@tonic-gate 	/* clean up and update statistics */
46547c478bd9Sstevel@tonic-gate 	pp->msg = NULL;
46557c478bd9Sstevel@tonic-gate 	pp->resid -= xfer_len;
46567c478bd9Sstevel@tonic-gate 	pp->ibytes[pp->current_mode] += xfer_len;
46577c478bd9Sstevel@tonic-gate 	pp->e_busy = ECPP_IDLE;
46587c478bd9Sstevel@tonic-gate 	pp->current_phase = ECPP_PHASE_ECP_REV_IDLE;
46597c478bd9Sstevel@tonic-gate 
46607c478bd9Sstevel@tonic-gate 	/*
46617c478bd9Sstevel@tonic-gate 	 * Send the read data up the stream
46627c478bd9Sstevel@tonic-gate 	 */
46637c478bd9Sstevel@tonic-gate 	mp->b_wptr += xfer_len;
46647c478bd9Sstevel@tonic-gate 	if (canputnext(pp->readq)) {
46657c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->umutex);
46667c478bd9Sstevel@tonic-gate 		putnext(pp->readq, mp);
46677c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->umutex);
46687c478bd9Sstevel@tonic-gate 	} else {
46697c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecp_read_completion: fail canputnext\n");
46707c478bd9Sstevel@tonic-gate 		if (!putq(pp->readq, mp)) {
46717c478bd9Sstevel@tonic-gate 			freemsg(mp);
46727c478bd9Sstevel@tonic-gate 		}
46737c478bd9Sstevel@tonic-gate 	}
46747c478bd9Sstevel@tonic-gate 
46757c478bd9Sstevel@tonic-gate 	/* if bytes left in the FIFO another transfer is needed */
46767c478bd9Sstevel@tonic-gate 	if (!(ECR_READ(pp) & ECPP_FIFO_EMPTY)) {
46777c478bd9Sstevel@tonic-gate 		(void) ecpp_backchan_req(pp);
46787c478bd9Sstevel@tonic-gate 	}
46797c478bd9Sstevel@tonic-gate 
46807c478bd9Sstevel@tonic-gate 	qenable(pp->writeq);
46817c478bd9Sstevel@tonic-gate }
46827c478bd9Sstevel@tonic-gate 
46837c478bd9Sstevel@tonic-gate /*
46847c478bd9Sstevel@tonic-gate  * Read one byte in the Nibble mode
46857c478bd9Sstevel@tonic-gate  */
46867c478bd9Sstevel@tonic-gate static uchar_t
nibble_peripheral2host(struct ecppunit * pp,uint8_t * byte)46877c478bd9Sstevel@tonic-gate nibble_peripheral2host(struct ecppunit *pp, uint8_t *byte)
46887c478bd9Sstevel@tonic-gate {
46897c478bd9Sstevel@tonic-gate 	uint8_t	n[2];	/* two nibbles */
46907c478bd9Sstevel@tonic-gate 	int	i;
46917c478bd9Sstevel@tonic-gate 
46927c478bd9Sstevel@tonic-gate 	/*
46937c478bd9Sstevel@tonic-gate 	 * One byte is made of two nibbles
46947c478bd9Sstevel@tonic-gate 	 */
46957c478bd9Sstevel@tonic-gate 	for (i = 0; i < 2; i++) {
46967c478bd9Sstevel@tonic-gate 		/* Event 7, 12: host asserts nAutoFd to move to read a nibble */
46977c478bd9Sstevel@tonic-gate 		DCR_WRITE(pp, ECPP_nINIT | ECPP_AFX);
46987c478bd9Sstevel@tonic-gate 
46997c478bd9Sstevel@tonic-gate 		/* Event 8: peripheral puts data on the status lines */
47007c478bd9Sstevel@tonic-gate 
47017c478bd9Sstevel@tonic-gate 		/* Event 9: peripheral asserts nAck, data available */
47027c478bd9Sstevel@tonic-gate 		if (wait_dsr(pp, ECPP_nACK, 0, 35000) < 0) {
47037c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
470419397407SSherry Moore 			    "nibble_periph2host(%d): failed event 9 %x\n",
470519397407SSherry Moore 			    i + 1, DSR_READ(pp));
47067c478bd9Sstevel@tonic-gate 			(void) ecpp_1284_termination(pp);
47077c478bd9Sstevel@tonic-gate 			return (FAILURE);
47087c478bd9Sstevel@tonic-gate 		}
47097c478bd9Sstevel@tonic-gate 
47107c478bd9Sstevel@tonic-gate 		n[i] = DSR_READ(pp);	/* get a nibble */
47117c478bd9Sstevel@tonic-gate 
47127c478bd9Sstevel@tonic-gate 		/* Event 10: host deasserts nAutoFd to say it grabbed data */
47137c478bd9Sstevel@tonic-gate 		DCR_WRITE(pp, ECPP_nINIT);
47147c478bd9Sstevel@tonic-gate 
47157c478bd9Sstevel@tonic-gate 		/* (2) Event 13: peripheral asserts PE - end of data phase */
47167c478bd9Sstevel@tonic-gate 
47177c478bd9Sstevel@tonic-gate 		/* Event 11: peripheral deasserts nAck to finish handshake */
47187c478bd9Sstevel@tonic-gate 		if (wait_dsr(pp, ECPP_nACK, ECPP_nACK, 35000) < 0) {
47197c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
472019397407SSherry Moore 			    "nibble_periph2host(%d): failed event 11 %x\n",
472119397407SSherry Moore 			    i + 1, DSR_READ(pp));
47227c478bd9Sstevel@tonic-gate 			(void) ecpp_1284_termination(pp);
47237c478bd9Sstevel@tonic-gate 			return (FAILURE);
47247c478bd9Sstevel@tonic-gate 		}
47257c478bd9Sstevel@tonic-gate 	}
47267c478bd9Sstevel@tonic-gate 
47277c478bd9Sstevel@tonic-gate 	/* extract data byte from two nibbles - optimized formula */
47287c478bd9Sstevel@tonic-gate 	*byte = ((((n[1] & ~ECPP_nACK) << 1) | (~n[1] & ECPP_nBUSY)) & 0xf0) |
47297c478bd9Sstevel@tonic-gate 	    ((((n[0] & ~ECPP_nACK) >> 3) | ((~n[0] & ECPP_nBUSY) >> 4)) & 0x0f);
47307c478bd9Sstevel@tonic-gate 
47317c478bd9Sstevel@tonic-gate 	pp->ibytes[ECPP_NIBBLE_MODE]++;
47327c478bd9Sstevel@tonic-gate 	return (SUCCESS);
47337c478bd9Sstevel@tonic-gate }
47347c478bd9Sstevel@tonic-gate 
47357c478bd9Sstevel@tonic-gate /*
47367c478bd9Sstevel@tonic-gate  * process data transfers requested by the peripheral
47377c478bd9Sstevel@tonic-gate  */
47387c478bd9Sstevel@tonic-gate static uint_t
ecpp_peripheral2host(struct ecppunit * pp)47397c478bd9Sstevel@tonic-gate ecpp_peripheral2host(struct ecppunit *pp)
47407c478bd9Sstevel@tonic-gate {
47417c478bd9Sstevel@tonic-gate 	if (!canputnext(pp->readq)) {
47427c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_peripheral2host: readq full\n");
47437c478bd9Sstevel@tonic-gate 		return (SUCCESS);
47447c478bd9Sstevel@tonic-gate 	}
47457c478bd9Sstevel@tonic-gate 
47467c478bd9Sstevel@tonic-gate 	switch (pp->backchannel) {
47477c478bd9Sstevel@tonic-gate 	case ECPP_CENTRONICS:
47487c478bd9Sstevel@tonic-gate 		/* no backchannel */
47497c478bd9Sstevel@tonic-gate 		return (SUCCESS);
47507c478bd9Sstevel@tonic-gate 
47517c478bd9Sstevel@tonic-gate 	case ECPP_NIBBLE_MODE:
47527c478bd9Sstevel@tonic-gate 		ASSERT(pp->current_mode == ECPP_NIBBLE_MODE);
47537c478bd9Sstevel@tonic-gate 
47547c478bd9Sstevel@tonic-gate 		/*
47557c478bd9Sstevel@tonic-gate 		 * Event 20: Host sets nAutoFd high to ack request
47567c478bd9Sstevel@tonic-gate 		 */
47577c478bd9Sstevel@tonic-gate 		DCR_WRITE(pp, ECPP_nINIT);
47587c478bd9Sstevel@tonic-gate 
47597c478bd9Sstevel@tonic-gate 		/* Event 21: Periph sets PError low to ack host */
47607c478bd9Sstevel@tonic-gate 		if (wait_dsr(pp, ECPP_PE, 0, 35000) < 0) {
47617c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
476219397407SSherry Moore 			    "ecpp_periph2host: failed event 21 %x\n",
476319397407SSherry Moore 			    DSR_READ(pp));
47647c478bd9Sstevel@tonic-gate 			(void) ecpp_1284_termination(pp);
47657c478bd9Sstevel@tonic-gate 			return (FAILURE);
47667c478bd9Sstevel@tonic-gate 		}
47677c478bd9Sstevel@tonic-gate 
47687c478bd9Sstevel@tonic-gate 		pp->current_phase = ECPP_PHASE_NIBT_AVAIL;
47697c478bd9Sstevel@tonic-gate 
47707c478bd9Sstevel@tonic-gate 		/* this routine will read the data in Nibble mode */
47717c478bd9Sstevel@tonic-gate 		return (ecpp_idle_phase(pp));
47727c478bd9Sstevel@tonic-gate 
47737c478bd9Sstevel@tonic-gate 	case ECPP_ECP_MODE:
47747c478bd9Sstevel@tonic-gate 		if ((pp->current_phase == ECPP_PHASE_ECP_FWD_IDLE) &&
47757c478bd9Sstevel@tonic-gate 		    (ecp_forward2reverse(pp) == FAILURE)) {
47767c478bd9Sstevel@tonic-gate 			return (FAILURE);
47777c478bd9Sstevel@tonic-gate 		}
47787c478bd9Sstevel@tonic-gate 
47797c478bd9Sstevel@tonic-gate 		return (ecp_peripheral2host(pp));	/* start the transfer */
47807c478bd9Sstevel@tonic-gate 
47817c478bd9Sstevel@tonic-gate 	case ECPP_DIAG_MODE: {
47827c478bd9Sstevel@tonic-gate 		mblk_t		*mp;
47837c478bd9Sstevel@tonic-gate 		int		i;
47847c478bd9Sstevel@tonic-gate 
47857c478bd9Sstevel@tonic-gate 		if (ECR_READ(pp) & ECPP_FIFO_EMPTY) {
47867c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "ecpp_periph2host: fifo empty\n");
47877c478bd9Sstevel@tonic-gate 			return (SUCCESS);
47887c478bd9Sstevel@tonic-gate 		}
47897c478bd9Sstevel@tonic-gate 
47907c478bd9Sstevel@tonic-gate 		/* allocate the FIFO size */
47917c478bd9Sstevel@tonic-gate 		if ((mp = allocb(ECPP_FIFO_SZ, BPRI_MED)) == NULL) {
47927c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
479319397407SSherry Moore 			    "ecpp_periph2host: allocb FAILURE.\n");
47947c478bd9Sstevel@tonic-gate 			return (FAILURE);
47957c478bd9Sstevel@tonic-gate 		}
47967c478bd9Sstevel@tonic-gate 
47977c478bd9Sstevel@tonic-gate 		/*
47987c478bd9Sstevel@tonic-gate 		 * For the time being just read it byte by byte
47997c478bd9Sstevel@tonic-gate 		 */
48007c478bd9Sstevel@tonic-gate 		i = ECPP_FIFO_SZ;
48017c478bd9Sstevel@tonic-gate 		while (i-- && (!(ECR_READ(pp) & ECPP_FIFO_EMPTY))) {
48027c478bd9Sstevel@tonic-gate 			*mp->b_wptr++ = TFIFO_READ(pp);
48037c478bd9Sstevel@tonic-gate 			drv_usecwait(1); /* ECR is sometimes slow to update */
48047c478bd9Sstevel@tonic-gate 		}
48057c478bd9Sstevel@tonic-gate 
48067c478bd9Sstevel@tonic-gate 		if (canputnext(pp->readq)) {
48077c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
48087c478bd9Sstevel@tonic-gate 			mp->b_datap->db_type = M_DATA;
48097c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
481019397407SSherry Moore 			    "ecpp_periph2host: sending %d bytes\n",
481119397407SSherry Moore 			    mp->b_wptr - mp->b_rptr);
48127c478bd9Sstevel@tonic-gate 			putnext(pp->readq, mp);
48137c478bd9Sstevel@tonic-gate 			mutex_enter(&pp->umutex);
48147c478bd9Sstevel@tonic-gate 			return (SUCCESS);
48157c478bd9Sstevel@tonic-gate 		} else {
48167c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
481719397407SSherry Moore 			    "ecpp_periph2host: !canputnext data lost\n");
48187c478bd9Sstevel@tonic-gate 			freemsg(mp);
48197c478bd9Sstevel@tonic-gate 			return (FAILURE);
48207c478bd9Sstevel@tonic-gate 		}
48217c478bd9Sstevel@tonic-gate 	}
48227c478bd9Sstevel@tonic-gate 
48237c478bd9Sstevel@tonic-gate 	default:
48247c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_peripheraltohost: illegal back");
48257c478bd9Sstevel@tonic-gate 		return (FAILURE);
48267c478bd9Sstevel@tonic-gate 	}
48277c478bd9Sstevel@tonic-gate }
48287c478bd9Sstevel@tonic-gate 
48297c478bd9Sstevel@tonic-gate /*
48307c478bd9Sstevel@tonic-gate  * Negotiate from ECP Forward Idle to Reverse Idle Phase
48317c478bd9Sstevel@tonic-gate  *
48327c478bd9Sstevel@tonic-gate  * (manipulations with dcr/ecr are according to ECP Specification)
48337c478bd9Sstevel@tonic-gate  */
48347c478bd9Sstevel@tonic-gate static int
ecp_forward2reverse(struct ecppunit * pp)48357c478bd9Sstevel@tonic-gate ecp_forward2reverse(struct ecppunit *pp)
48367c478bd9Sstevel@tonic-gate {
48377c478bd9Sstevel@tonic-gate 	ASSERT(pp->current_mode == ECPP_ECP_MODE &&
483819397407SSherry Moore 	    pp->current_phase == ECPP_PHASE_ECP_FWD_IDLE);
48397c478bd9Sstevel@tonic-gate 
48407c478bd9Sstevel@tonic-gate 	/* place port into PS2 mode */
48417c478bd9Sstevel@tonic-gate 	ECR_WRITE(pp, ECR_mode_001 | ECPP_INTR_SRV | ECPP_INTR_MASK);
48427c478bd9Sstevel@tonic-gate 
48437c478bd9Sstevel@tonic-gate 	/* set direction bit (DCR3-0 must be 0100 - National) */
48447c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, ECPP_REV_DIR | ECPP_nINIT);
48457c478bd9Sstevel@tonic-gate 
48467c478bd9Sstevel@tonic-gate 	/* enable hardware assist */
48477c478bd9Sstevel@tonic-gate 	ECR_WRITE(pp, ECR_mode_011 | ECPP_INTR_SRV | ECPP_INTR_MASK);
48487c478bd9Sstevel@tonic-gate 
48497c478bd9Sstevel@tonic-gate 	drv_usecwait(1);	/* Tp(ecp) = 0.5us */
48507c478bd9Sstevel@tonic-gate 
48517c478bd9Sstevel@tonic-gate 	/* Event 39: host sets nInit low */
48527c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, ECPP_REV_DIR);
48537c478bd9Sstevel@tonic-gate 
48547c478bd9Sstevel@tonic-gate 	/* Event 40: peripheral sets PError low */
48557c478bd9Sstevel@tonic-gate 
48567c478bd9Sstevel@tonic-gate 	pp->current_phase = ECPP_PHASE_ECP_REV_IDLE;
48577c478bd9Sstevel@tonic-gate 
48587c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecp_forward2reverse ok\n");
48597c478bd9Sstevel@tonic-gate 
48607c478bd9Sstevel@tonic-gate 	return (SUCCESS);
48617c478bd9Sstevel@tonic-gate }
48627c478bd9Sstevel@tonic-gate 
48637c478bd9Sstevel@tonic-gate /*
48647c478bd9Sstevel@tonic-gate  * Negotiate from ECP Reverse Idle to Forward Idle Phase
48657c478bd9Sstevel@tonic-gate  *
48667c478bd9Sstevel@tonic-gate  * (manipulations with dcr/ecr are according to ECP Specification)
48677c478bd9Sstevel@tonic-gate  */
48687c478bd9Sstevel@tonic-gate static int
ecp_reverse2forward(struct ecppunit * pp)48697c478bd9Sstevel@tonic-gate ecp_reverse2forward(struct ecppunit *pp)
48707c478bd9Sstevel@tonic-gate {
48717c478bd9Sstevel@tonic-gate 	ASSERT(pp->current_mode == ECPP_ECP_MODE &&
487219397407SSherry Moore 	    pp->current_phase == ECPP_PHASE_ECP_REV_IDLE);
48737c478bd9Sstevel@tonic-gate 
48747c478bd9Sstevel@tonic-gate 	/* Event 47: host deasserts nInit */
48757c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, ECPP_REV_DIR | ECPP_nINIT);
48767c478bd9Sstevel@tonic-gate 
48777c478bd9Sstevel@tonic-gate 	/*
48787c478bd9Sstevel@tonic-gate 	 * Event 48: peripheral deasserts nAck
48797c478bd9Sstevel@tonic-gate 	 * Event 49: peripheral asserts PError
48807c478bd9Sstevel@tonic-gate 	 */
48817c478bd9Sstevel@tonic-gate 	if (wait_dsr(pp, ECPP_PE, ECPP_PE, 35000) < 0) {
48827c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
48837c478bd9Sstevel@tonic-gate 		    "ecp_reverse2forward: failed event 49 %x\n", DSR_READ(pp));
48847c478bd9Sstevel@tonic-gate 		(void) ecpp_1284_termination(pp);
48857c478bd9Sstevel@tonic-gate 		return (FAILURE);
48867c478bd9Sstevel@tonic-gate 	}
48877c478bd9Sstevel@tonic-gate 
48887c478bd9Sstevel@tonic-gate 	/* place port into PS2 mode */
48897c478bd9Sstevel@tonic-gate 	ECR_WRITE(pp, ECR_mode_001 | ECPP_INTR_SRV | ECPP_INTR_MASK);
48907c478bd9Sstevel@tonic-gate 
48917c478bd9Sstevel@tonic-gate 	/* clear direction bit */
48927c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, ECPP_nINIT);
48937c478bd9Sstevel@tonic-gate 
48947c478bd9Sstevel@tonic-gate 	/* reenable hardware assist */
48957c478bd9Sstevel@tonic-gate 	ECR_WRITE(pp, ECR_mode_011 | ECPP_INTR_SRV | ECPP_INTR_MASK);
48967c478bd9Sstevel@tonic-gate 
48977c478bd9Sstevel@tonic-gate 	pp->current_phase = ECPP_PHASE_ECP_FWD_IDLE;
48987c478bd9Sstevel@tonic-gate 
48997c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecp_reverse2forward ok\n");
49007c478bd9Sstevel@tonic-gate 
49017c478bd9Sstevel@tonic-gate 	return (SUCCESS);
49027c478bd9Sstevel@tonic-gate }
49037c478bd9Sstevel@tonic-gate 
49047c478bd9Sstevel@tonic-gate /*
49057c478bd9Sstevel@tonic-gate  * Default negotiation chooses the best mode supported by peripheral
49067c478bd9Sstevel@tonic-gate  * Note that backchannel mode may be different from forward mode
49077c478bd9Sstevel@tonic-gate  */
49087c478bd9Sstevel@tonic-gate static void
ecpp_default_negotiation(struct ecppunit * pp)49097c478bd9Sstevel@tonic-gate ecpp_default_negotiation(struct ecppunit *pp)
49107c478bd9Sstevel@tonic-gate {
49117c478bd9Sstevel@tonic-gate 	if (!noecp && (ecpp_mode_negotiation(pp, ECPP_ECP_MODE) == SUCCESS)) {
49127c478bd9Sstevel@tonic-gate 		/* 1284 compatible device */
49137c478bd9Sstevel@tonic-gate 		pp->io_mode = (pp->fast_compat == TRUE) ? ECPP_DMA : ECPP_PIO;
49147c478bd9Sstevel@tonic-gate 		return;
49157c478bd9Sstevel@tonic-gate 	} else if (ecpp_mode_negotiation(pp, ECPP_NIBBLE_MODE) == SUCCESS) {
49167c478bd9Sstevel@tonic-gate 		/* 1284 compatible device */
49177c478bd9Sstevel@tonic-gate 		pp->io_mode = (pp->fast_compat == TRUE) ? ECPP_DMA : ECPP_PIO;
49187c478bd9Sstevel@tonic-gate 	} else {
49197c478bd9Sstevel@tonic-gate 		/* Centronics device */
49207c478bd9Sstevel@tonic-gate 		pp->io_mode =
492119397407SSherry Moore 		    (pp->fast_centronics == TRUE) ? ECPP_DMA : ECPP_PIO;
49227c478bd9Sstevel@tonic-gate 	}
49237c478bd9Sstevel@tonic-gate 	ECPP_CONFIG_MODE(pp);
49247c478bd9Sstevel@tonic-gate }
49257c478bd9Sstevel@tonic-gate 
49267c478bd9Sstevel@tonic-gate /*
49277c478bd9Sstevel@tonic-gate  * Negotiate to the mode indicated by newmode
49287c478bd9Sstevel@tonic-gate  */
49297c478bd9Sstevel@tonic-gate static int
ecpp_mode_negotiation(struct ecppunit * pp,uchar_t newmode)49307c478bd9Sstevel@tonic-gate ecpp_mode_negotiation(struct ecppunit *pp, uchar_t newmode)
49317c478bd9Sstevel@tonic-gate {
49327c478bd9Sstevel@tonic-gate 	/* any other mode is impossible */
49337c478bd9Sstevel@tonic-gate 	ASSERT(pp->current_mode == ECPP_CENTRONICS ||
493419397407SSherry Moore 	    pp->current_mode == ECPP_COMPAT_MODE ||
493519397407SSherry Moore 	    pp->current_mode == ECPP_NIBBLE_MODE ||
493619397407SSherry Moore 	    pp->current_mode == ECPP_ECP_MODE ||
493719397407SSherry Moore 	    pp->current_mode == ECPP_DIAG_MODE);
49387c478bd9Sstevel@tonic-gate 
49397c478bd9Sstevel@tonic-gate 	if (pp->current_mode == newmode) {
49407c478bd9Sstevel@tonic-gate 		return (SUCCESS);
49417c478bd9Sstevel@tonic-gate 	}
49427c478bd9Sstevel@tonic-gate 
49437c478bd9Sstevel@tonic-gate 	/* termination from ECP is only allowed from the Forward Idle Phase */
49447c478bd9Sstevel@tonic-gate 	if ((pp->current_mode == ECPP_ECP_MODE) &&
49457c478bd9Sstevel@tonic-gate 	    (pp->current_phase != ECPP_PHASE_ECP_FWD_IDLE)) {
49467c478bd9Sstevel@tonic-gate 		/* this may break into Centronics */
49477c478bd9Sstevel@tonic-gate 		(void) ecp_reverse2forward(pp);
49487c478bd9Sstevel@tonic-gate 	}
49497c478bd9Sstevel@tonic-gate 
49507c478bd9Sstevel@tonic-gate 	switch (newmode) {
49517c478bd9Sstevel@tonic-gate 	case ECPP_CENTRONICS:
49527c478bd9Sstevel@tonic-gate 		(void) ecpp_1284_termination(pp);
49537c478bd9Sstevel@tonic-gate 
49547c478bd9Sstevel@tonic-gate 		/* put superio into PIO mode */
49557c478bd9Sstevel@tonic-gate 		ECR_WRITE(pp, ECR_mode_001 | ECPP_INTR_MASK | ECPP_INTR_SRV);
49567c478bd9Sstevel@tonic-gate 
49577c478bd9Sstevel@tonic-gate 		pp->current_mode = ECPP_CENTRONICS;
49587c478bd9Sstevel@tonic-gate 		pp->backchannel = ECPP_CENTRONICS;
49597c478bd9Sstevel@tonic-gate 		ECPP_CONFIG_MODE(pp);
49607c478bd9Sstevel@tonic-gate 
49617c478bd9Sstevel@tonic-gate 		pp->to_mode[pp->current_mode]++;
49627c478bd9Sstevel@tonic-gate 		return (SUCCESS);
49637c478bd9Sstevel@tonic-gate 
49647c478bd9Sstevel@tonic-gate 	case ECPP_COMPAT_MODE:
49657c478bd9Sstevel@tonic-gate 		/* ECPP_COMPAT_MODE should support Nibble as a backchannel */
49667c478bd9Sstevel@tonic-gate 		if (pp->current_mode == ECPP_NIBBLE_MODE) {
49677c478bd9Sstevel@tonic-gate 			if (ecpp_1284_termination(pp) == SUCCESS) {
49687c478bd9Sstevel@tonic-gate 				pp->current_mode = ECPP_COMPAT_MODE;
49697c478bd9Sstevel@tonic-gate 				pp->backchannel = ECPP_NIBBLE_MODE;
49707c478bd9Sstevel@tonic-gate 				ECPP_CONFIG_MODE(pp);
49717c478bd9Sstevel@tonic-gate 				pp->to_mode[pp->current_mode]++;
49727c478bd9Sstevel@tonic-gate 				return (SUCCESS);
49737c478bd9Sstevel@tonic-gate 			} else {
49747c478bd9Sstevel@tonic-gate 				return (FAILURE);
49757c478bd9Sstevel@tonic-gate 			}
49767c478bd9Sstevel@tonic-gate 		}
49777c478bd9Sstevel@tonic-gate 
49787c478bd9Sstevel@tonic-gate 		if ((nibble_negotiation(pp) == SUCCESS) &&
49797c478bd9Sstevel@tonic-gate 		    (ecpp_1284_termination(pp) == SUCCESS)) {
49807c478bd9Sstevel@tonic-gate 			pp->backchannel = ECPP_NIBBLE_MODE;
49817c478bd9Sstevel@tonic-gate 			pp->current_mode = ECPP_COMPAT_MODE;
49827c478bd9Sstevel@tonic-gate 			ECPP_CONFIG_MODE(pp);
49837c478bd9Sstevel@tonic-gate 			pp->to_mode[pp->current_mode]++;
49847c478bd9Sstevel@tonic-gate 			return (SUCCESS);
49857c478bd9Sstevel@tonic-gate 		} else {
49867c478bd9Sstevel@tonic-gate 			return (FAILURE);
49877c478bd9Sstevel@tonic-gate 		}
49887c478bd9Sstevel@tonic-gate 
49897c478bd9Sstevel@tonic-gate 	case ECPP_NIBBLE_MODE:
49907c478bd9Sstevel@tonic-gate 		if (nibble_negotiation(pp) == FAILURE) {
49917c478bd9Sstevel@tonic-gate 			return (FAILURE);
49927c478bd9Sstevel@tonic-gate 		}
49937c478bd9Sstevel@tonic-gate 
49947c478bd9Sstevel@tonic-gate 		pp->backchannel = ECPP_NIBBLE_MODE;
49957c478bd9Sstevel@tonic-gate 		ECPP_CONFIG_MODE(pp);
49967c478bd9Sstevel@tonic-gate 		pp->to_mode[pp->current_mode]++;
49977c478bd9Sstevel@tonic-gate 
49987c478bd9Sstevel@tonic-gate 		return (SUCCESS);
49997c478bd9Sstevel@tonic-gate 
50007c478bd9Sstevel@tonic-gate 	case ECPP_ECP_MODE:
50017c478bd9Sstevel@tonic-gate 		if (pp->noecpregs)
50027c478bd9Sstevel@tonic-gate 			return (FAILURE);
50037c478bd9Sstevel@tonic-gate 		if (ecp_negotiation(pp) == FAILURE) {
50047c478bd9Sstevel@tonic-gate 			return (FAILURE);
50057c478bd9Sstevel@tonic-gate 		}
50067c478bd9Sstevel@tonic-gate 
50077c478bd9Sstevel@tonic-gate 		/*
50087c478bd9Sstevel@tonic-gate 		 * National says CTR[3:0] should be 0100b before moving to 011
50097c478bd9Sstevel@tonic-gate 		 */
50107c478bd9Sstevel@tonic-gate 		DCR_WRITE(pp, ECPP_nINIT);
50117c478bd9Sstevel@tonic-gate 
50127c478bd9Sstevel@tonic-gate 		if (ecr_write(pp, ECR_mode_011 |
501319397407SSherry Moore 		    ECPP_INTR_MASK | ECPP_INTR_SRV) == FAILURE) {
50147c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "mode_nego:ECP: failed w/ecr\n");
50157c478bd9Sstevel@tonic-gate 			return (FAILURE);
50167c478bd9Sstevel@tonic-gate 		}
50177c478bd9Sstevel@tonic-gate 
50187c478bd9Sstevel@tonic-gate 		ECPP_CONFIG_MODE(pp);
50197c478bd9Sstevel@tonic-gate 		pp->to_mode[pp->current_mode]++;
50207c478bd9Sstevel@tonic-gate 
50217c478bd9Sstevel@tonic-gate 		return (SUCCESS);
50227c478bd9Sstevel@tonic-gate 
50237c478bd9Sstevel@tonic-gate 	case ECPP_DIAG_MODE:
50247c478bd9Sstevel@tonic-gate 		/*
50257c478bd9Sstevel@tonic-gate 		 * In DIAG mode application can do nasty things(e.g drive pins)
50267c478bd9Sstevel@tonic-gate 		 * To keep peripheral sane, terminate to Compatibility mode
50277c478bd9Sstevel@tonic-gate 		 */
50287c478bd9Sstevel@tonic-gate 		(void) ecpp_1284_termination(pp);
50297c478bd9Sstevel@tonic-gate 
50307c478bd9Sstevel@tonic-gate 		/* put superio into TFIFO mode */
50317c478bd9Sstevel@tonic-gate 		if (ecr_write(pp, ECR_mode_001 |
50327c478bd9Sstevel@tonic-gate 		    ECPP_INTR_MASK | ECPP_INTR_SRV) == FAILURE) {
50337c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "put to TFIFO: failed w/ecr\n");
50347c478bd9Sstevel@tonic-gate 			return (FAILURE);
50357c478bd9Sstevel@tonic-gate 		}
50367c478bd9Sstevel@tonic-gate 
50377c478bd9Sstevel@tonic-gate 		pp->current_mode = ECPP_DIAG_MODE;
50387c478bd9Sstevel@tonic-gate 		pp->backchannel = ECPP_DIAG_MODE;
50397c478bd9Sstevel@tonic-gate 		ECPP_CONFIG_MODE(pp);
50407c478bd9Sstevel@tonic-gate 		pp->to_mode[pp->current_mode]++;
50417c478bd9Sstevel@tonic-gate 
50427c478bd9Sstevel@tonic-gate 		return (SUCCESS);
50437c478bd9Sstevel@tonic-gate 
50447c478bd9Sstevel@tonic-gate 	default:
50457c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip,
50467c478bd9Sstevel@tonic-gate 		    "ecpp_mode_negotiation: mode %d not supported\n", newmode);
50477c478bd9Sstevel@tonic-gate 		return (FAILURE);
50487c478bd9Sstevel@tonic-gate 	}
50497c478bd9Sstevel@tonic-gate }
50507c478bd9Sstevel@tonic-gate 
50517c478bd9Sstevel@tonic-gate /*
50527c478bd9Sstevel@tonic-gate  * Standard (9.1): Peripheral data is available only when the host places
50537c478bd9Sstevel@tonic-gate  * the interface in a mode capable of peripheral-to-host data transfer.
50547c478bd9Sstevel@tonic-gate  * This requires the host periodically to place the interface in such a mode.
50557c478bd9Sstevel@tonic-gate  * Polling can be eliminated by leaving the interface in an 1284 idle phase.
50567c478bd9Sstevel@tonic-gate  */
50577c478bd9Sstevel@tonic-gate static uchar_t
ecpp_idle_phase(struct ecppunit * pp)50587c478bd9Sstevel@tonic-gate ecpp_idle_phase(struct ecppunit *pp)
50597c478bd9Sstevel@tonic-gate {
50607c478bd9Sstevel@tonic-gate 	uchar_t		rval = FAILURE;
50617c478bd9Sstevel@tonic-gate 
50627c478bd9Sstevel@tonic-gate 	/*
50637c478bd9Sstevel@tonic-gate 	 * If there is no space on the read queue, do not reverse channel
50647c478bd9Sstevel@tonic-gate 	 */
50657c478bd9Sstevel@tonic-gate 	if (!canputnext(pp->readq)) {
50667c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_idle_phase: readq full\n");
50677c478bd9Sstevel@tonic-gate 		return (SUCCESS);
50687c478bd9Sstevel@tonic-gate 	}
50697c478bd9Sstevel@tonic-gate 
50707c478bd9Sstevel@tonic-gate 	switch (pp->backchannel) {
50717c478bd9Sstevel@tonic-gate 	case ECPP_CENTRONICS:
50727c478bd9Sstevel@tonic-gate 	case ECPP_COMPAT_MODE:
50737c478bd9Sstevel@tonic-gate 	case ECPP_DIAG_MODE:
50747c478bd9Sstevel@tonic-gate 		/* nothing */
50757c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_idle_phase: compat idle\n");
50767c478bd9Sstevel@tonic-gate 		return (SUCCESS);
50777c478bd9Sstevel@tonic-gate 
50787c478bd9Sstevel@tonic-gate 	case ECPP_NIBBLE_MODE:
50797c478bd9Sstevel@tonic-gate 		/*
50807c478bd9Sstevel@tonic-gate 		 * read as much data as possible, ending up in either
50817c478bd9Sstevel@tonic-gate 		 * Reverse Idle or Host Busy Data Available phase
50827c478bd9Sstevel@tonic-gate 		 */
50837c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_idle_phase: nibble backchannel\n");
50847c478bd9Sstevel@tonic-gate 		if ((pp->current_mode != ECPP_NIBBLE_MODE) &&
50857c478bd9Sstevel@tonic-gate 		    (ecpp_mode_negotiation(pp, ECPP_NIBBLE_MODE) == FAILURE)) {
50867c478bd9Sstevel@tonic-gate 			break;
50877c478bd9Sstevel@tonic-gate 		}
50887c478bd9Sstevel@tonic-gate 
50897c478bd9Sstevel@tonic-gate 		rval = read_nibble_backchan(pp);
50907c478bd9Sstevel@tonic-gate 
50917c478bd9Sstevel@tonic-gate 		/* put interface into Reverse Idle phase */
50927c478bd9Sstevel@tonic-gate 		if (pp->current_phase == ECPP_PHASE_NIBT_NAVAIL &&
50937c478bd9Sstevel@tonic-gate 		    canputnext(pp->readq)) {
50947c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "ecpp_idle_phase: going revidle\n");
50957c478bd9Sstevel@tonic-gate 
50967c478bd9Sstevel@tonic-gate 			/*
50977c478bd9Sstevel@tonic-gate 			 * Event 7: host asserts nAutoFd
50987c478bd9Sstevel@tonic-gate 			 * enable nAck interrupt to get a backchannel request
50997c478bd9Sstevel@tonic-gate 			 */
51007c478bd9Sstevel@tonic-gate 			DCR_WRITE(pp, ECPP_nINIT | ECPP_AFX | ECPP_INTR_EN);
51017c478bd9Sstevel@tonic-gate 
51027c478bd9Sstevel@tonic-gate 			ECPP_UNMASK_INTR(pp);
51037c478bd9Sstevel@tonic-gate 		}
51047c478bd9Sstevel@tonic-gate 
51057c478bd9Sstevel@tonic-gate 		break;
51067c478bd9Sstevel@tonic-gate 
51077c478bd9Sstevel@tonic-gate 	case ECPP_ECP_MODE:
51087c478bd9Sstevel@tonic-gate 		/*
51097c478bd9Sstevel@tonic-gate 		 * if data is already available, request the backchannel xfer
51107c478bd9Sstevel@tonic-gate 		 * otherwise stay in Forward Idle and enable nErr interrupts
51117c478bd9Sstevel@tonic-gate 		 */
51127c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_idle_phase: ECP forward\n");
51137c478bd9Sstevel@tonic-gate 
51147c478bd9Sstevel@tonic-gate 		ASSERT(pp->current_phase == ECPP_PHASE_ECP_FWD_IDLE ||
511519397407SSherry Moore 		    pp->current_phase == ECPP_PHASE_ECP_REV_IDLE);
51167c478bd9Sstevel@tonic-gate 
51177c478bd9Sstevel@tonic-gate 		/* put interface into Forward Idle phase */
51187c478bd9Sstevel@tonic-gate 		if ((pp->current_phase == ECPP_PHASE_ECP_REV_IDLE) &&
51197c478bd9Sstevel@tonic-gate 		    (ecp_reverse2forward(pp) == FAILURE)) {
51207c478bd9Sstevel@tonic-gate 			return (FAILURE);
51217c478bd9Sstevel@tonic-gate 		}
51227c478bd9Sstevel@tonic-gate 
51237c478bd9Sstevel@tonic-gate 		/*
51247c478bd9Sstevel@tonic-gate 		 * if data already available, put backchannel request on the wq
51257c478bd9Sstevel@tonic-gate 		 * otherwise enable nErr interrupts
51267c478bd9Sstevel@tonic-gate 		 */
51277c478bd9Sstevel@tonic-gate 		if ((DSR_READ(pp) & ECPP_nERR) == 0) {
51287c478bd9Sstevel@tonic-gate 			(void) ecpp_backchan_req(pp);
51297c478bd9Sstevel@tonic-gate 		} else {
51307c478bd9Sstevel@tonic-gate 			ECR_WRITE(pp,
513119397407SSherry Moore 			    ECR_READ(pp) & ~ECPP_INTR_MASK | ECPP_INTR_SRV);
51327c478bd9Sstevel@tonic-gate 
51337c478bd9Sstevel@tonic-gate 			ECPP_UNMASK_INTR(pp);
51347c478bd9Sstevel@tonic-gate 		}
51357c478bd9Sstevel@tonic-gate 
51367c478bd9Sstevel@tonic-gate 		return (SUCCESS);
51377c478bd9Sstevel@tonic-gate 
51387c478bd9Sstevel@tonic-gate 	default:
51397c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_idle_phase: illegal backchannel");
51407c478bd9Sstevel@tonic-gate 	}
51417c478bd9Sstevel@tonic-gate 
51427c478bd9Sstevel@tonic-gate 	return (rval);
51437c478bd9Sstevel@tonic-gate }
51447c478bd9Sstevel@tonic-gate 
51457c478bd9Sstevel@tonic-gate /*
51467c478bd9Sstevel@tonic-gate  * This routine will leave the port in ECPP_PHASE_NIBT_REVIDLE
51477c478bd9Sstevel@tonic-gate  * Due to flow control, though, it may stop at ECPP_PHASE_NIBT_AVAIL,
51487c478bd9Sstevel@tonic-gate  * and continue later as the user consumes data from the read queue
51497c478bd9Sstevel@tonic-gate  *
51507c478bd9Sstevel@tonic-gate  * The current phase should be NIBT_AVAIL or NIBT_NAVAIL
51517c478bd9Sstevel@tonic-gate  * If some events fail during transfer, termination puts link
51527c478bd9Sstevel@tonic-gate  * to Compatibility mode and FAILURE is returned
51537c478bd9Sstevel@tonic-gate  */
51547c478bd9Sstevel@tonic-gate static int
read_nibble_backchan(struct ecppunit * pp)51557c478bd9Sstevel@tonic-gate read_nibble_backchan(struct ecppunit *pp)
51567c478bd9Sstevel@tonic-gate {
51577c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
51587c478bd9Sstevel@tonic-gate 	int		i;
51597c478bd9Sstevel@tonic-gate 	int		rval = SUCCESS;
51607c478bd9Sstevel@tonic-gate 
51617c478bd9Sstevel@tonic-gate 	ASSERT(pp->current_mode == ECPP_NIBBLE_MODE);
51627c478bd9Sstevel@tonic-gate 
51637c478bd9Sstevel@tonic-gate 	pp->current_phase = (DSR_READ(pp) & (ECPP_nERR | ECPP_PE))
516419397407SSherry Moore 	    ? ECPP_PHASE_NIBT_NAVAIL : ECPP_PHASE_NIBT_AVAIL;
51657c478bd9Sstevel@tonic-gate 
51667c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "read_nibble_backchan: %x\n", DSR_READ(pp));
51677c478bd9Sstevel@tonic-gate 
51687c478bd9Sstevel@tonic-gate 	/*
51697c478bd9Sstevel@tonic-gate 	 * While data is available, read it in NIBBLE_REV_BLKSZ byte chunks
51707c478bd9Sstevel@tonic-gate 	 * and send up the stream
51717c478bd9Sstevel@tonic-gate 	 */
51727c478bd9Sstevel@tonic-gate 	while (pp->current_phase == ECPP_PHASE_NIBT_AVAIL && rval == SUCCESS) {
51737c478bd9Sstevel@tonic-gate 		/* see if there's space on the queue */
51747c478bd9Sstevel@tonic-gate 		if (!canputnext(pp->readq)) {
51757c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
517619397407SSherry Moore 			    "read_nibble_backchan: canputnext failed\n");
51777c478bd9Sstevel@tonic-gate 			return (SUCCESS);
51787c478bd9Sstevel@tonic-gate 		}
51797c478bd9Sstevel@tonic-gate 
51807c478bd9Sstevel@tonic-gate 		if ((mp = allocb(NIBBLE_REV_BLKSZ, BPRI_MED)) == NULL) {
51817c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
518219397407SSherry Moore 			    "read_nibble_backchan: allocb failed\n");
51837c478bd9Sstevel@tonic-gate 			return (SUCCESS);
51847c478bd9Sstevel@tonic-gate 		}
51857c478bd9Sstevel@tonic-gate 
51867c478bd9Sstevel@tonic-gate 		/* read a chunk of data from the peripheral byte by byte */
51877c478bd9Sstevel@tonic-gate 		i = NIBBLE_REV_BLKSZ;
51887c478bd9Sstevel@tonic-gate 		while (i-- && !(DSR_READ(pp) & ECPP_nERR)) {
51897c478bd9Sstevel@tonic-gate 			if (nibble_peripheral2host(pp, mp->b_wptr) != SUCCESS) {
51907c478bd9Sstevel@tonic-gate 				rval = FAILURE;
51917c478bd9Sstevel@tonic-gate 				break;
51927c478bd9Sstevel@tonic-gate 			}
51937c478bd9Sstevel@tonic-gate 			mp->b_wptr++;
51947c478bd9Sstevel@tonic-gate 		}
51957c478bd9Sstevel@tonic-gate 
51967c478bd9Sstevel@tonic-gate 		pp->current_phase = (DSR_READ(pp) & (ECPP_nERR | ECPP_PE))
519719397407SSherry Moore 		    ? ECPP_PHASE_NIBT_NAVAIL
519819397407SSherry Moore 		    : ECPP_PHASE_NIBT_AVAIL;
51997c478bd9Sstevel@tonic-gate 
52007c478bd9Sstevel@tonic-gate 		if (mp->b_wptr - mp->b_rptr > 0) {
52017c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
520219397407SSherry Moore 			    "read_nibble_backchan: sending %d bytes\n",
520319397407SSherry Moore 			    mp->b_wptr - mp->b_rptr);
52047c478bd9Sstevel@tonic-gate 			pp->nread = 0;
52057c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->umutex);
52067c478bd9Sstevel@tonic-gate 			putnext(pp->readq, mp);
52077c478bd9Sstevel@tonic-gate 			mutex_enter(&pp->umutex);
52087c478bd9Sstevel@tonic-gate 		} else {
52097c478bd9Sstevel@tonic-gate 			freemsg(mp);
52107c478bd9Sstevel@tonic-gate 		}
52117c478bd9Sstevel@tonic-gate 	}
52127c478bd9Sstevel@tonic-gate 
52137c478bd9Sstevel@tonic-gate 	return (rval);
52147c478bd9Sstevel@tonic-gate }
52157c478bd9Sstevel@tonic-gate 
52167c478bd9Sstevel@tonic-gate /*
52177c478bd9Sstevel@tonic-gate  * 'Request Device ID using nibble mode' negotiation
52187c478bd9Sstevel@tonic-gate  */
52197c478bd9Sstevel@tonic-gate static int
devidnib_negotiation(struct ecppunit * pp)52207c478bd9Sstevel@tonic-gate devidnib_negotiation(struct ecppunit *pp)
52217c478bd9Sstevel@tonic-gate {
52227c478bd9Sstevel@tonic-gate 	uint8_t dsr;
52237c478bd9Sstevel@tonic-gate 
52247c478bd9Sstevel@tonic-gate 	if (ecpp_1284_negotiation(pp,
522519397407SSherry Moore 	    ECPP_XREQ_NIBBLE | ECPP_XREQ_ID, &dsr) == FAILURE) {
52267c478bd9Sstevel@tonic-gate 		return (FAILURE);
52277c478bd9Sstevel@tonic-gate 	}
52287c478bd9Sstevel@tonic-gate 
52297c478bd9Sstevel@tonic-gate 	/*
52307c478bd9Sstevel@tonic-gate 	 * If peripheral has data available, PE and nErr will
52317c478bd9Sstevel@tonic-gate 	 * be set low at Event 5 & 6.
52327c478bd9Sstevel@tonic-gate 	 */
52337c478bd9Sstevel@tonic-gate 	if ((dsr & (ECPP_PE | ECPP_nERR)) == 0) {
52347c478bd9Sstevel@tonic-gate 		pp->current_phase = ECPP_PHASE_NIBT_AVAIL;
52357c478bd9Sstevel@tonic-gate 	} else {
52367c478bd9Sstevel@tonic-gate 		pp->current_phase = ECPP_PHASE_NIBT_NAVAIL;
52377c478bd9Sstevel@tonic-gate 	}
52387c478bd9Sstevel@tonic-gate 
52397c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecpp_devidnib_nego: current_phase=%x\n",
524019397407SSherry Moore 	    pp->current_phase);
52417c478bd9Sstevel@tonic-gate 
52427c478bd9Sstevel@tonic-gate 	/* successful negotiation into Nibble mode */
52437c478bd9Sstevel@tonic-gate 	pp->current_mode = ECPP_NIBBLE_MODE;
52447c478bd9Sstevel@tonic-gate 	pp->backchannel = ECPP_NIBBLE_MODE;
52457c478bd9Sstevel@tonic-gate 
52467c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "ecpp_devidnib_nego: ok\n");
52477c478bd9Sstevel@tonic-gate 
52487c478bd9Sstevel@tonic-gate 	return (SUCCESS);
52497c478bd9Sstevel@tonic-gate }
52507c478bd9Sstevel@tonic-gate 
52517c478bd9Sstevel@tonic-gate /*
52527c478bd9Sstevel@tonic-gate  * Read 1284 device ID sequence
52537c478bd9Sstevel@tonic-gate  *
52547c478bd9Sstevel@tonic-gate  * This function should be called two times:
52557c478bd9Sstevel@tonic-gate  * 1) ecpp_getdevid(pp, NULL, &len) - to retrieve ID length;
52567c478bd9Sstevel@tonic-gate  * 2) ecpp_getdevid(pp, buffer, &len) - to read len bytes into buffer
52577c478bd9Sstevel@tonic-gate  *
52587c478bd9Sstevel@tonic-gate  * After 2) port is in Compatible mode
52597c478bd9Sstevel@tonic-gate  * If the caller fails to make second call, it must reset port to Centronics
52607c478bd9Sstevel@tonic-gate  *
52617c478bd9Sstevel@tonic-gate  */
52627c478bd9Sstevel@tonic-gate static int
ecpp_getdevid(struct ecppunit * pp,uint8_t * id,int * lenp,int mode)52637c478bd9Sstevel@tonic-gate ecpp_getdevid(struct ecppunit *pp, uint8_t *id, int *lenp, int mode)
52647c478bd9Sstevel@tonic-gate {
52657c478bd9Sstevel@tonic-gate 	uint8_t lenhi, lenlo;
52667c478bd9Sstevel@tonic-gate 	uint8_t dsr;
52677c478bd9Sstevel@tonic-gate 	int i;
52687c478bd9Sstevel@tonic-gate 
52697c478bd9Sstevel@tonic-gate 	switch (mode) {
52707c478bd9Sstevel@tonic-gate 	case ECPP_NIBBLE_MODE:
52717c478bd9Sstevel@tonic-gate 		/* negotiate only if neccessary */
52727c478bd9Sstevel@tonic-gate 		if ((pp->current_mode != mode) || (id == NULL)) {
52737c478bd9Sstevel@tonic-gate 			if (devidnib_negotiation(pp) == FAILURE) {
52747c478bd9Sstevel@tonic-gate 				return (EIO);
52757c478bd9Sstevel@tonic-gate 			}
52767c478bd9Sstevel@tonic-gate 		}
52777c478bd9Sstevel@tonic-gate 
52787c478bd9Sstevel@tonic-gate 		if (pp->current_phase != ECPP_PHASE_NIBT_AVAIL) {
52797c478bd9Sstevel@tonic-gate 			return (EIO);
52807c478bd9Sstevel@tonic-gate 		}
52817c478bd9Sstevel@tonic-gate 
52827c478bd9Sstevel@tonic-gate 		/*
52837c478bd9Sstevel@tonic-gate 		 * Event 14: Host tristates data bus, peripheral
52847c478bd9Sstevel@tonic-gate 		 * asserts nERR if data available, usually the
52857c478bd9Sstevel@tonic-gate 		 * status bits (7-0) and requires two reads since
52867c478bd9Sstevel@tonic-gate 		 * only nibbles are transfered.
52877c478bd9Sstevel@tonic-gate 		 */
52887c478bd9Sstevel@tonic-gate 		dsr = DSR_READ(pp);
52897c478bd9Sstevel@tonic-gate 
52907c478bd9Sstevel@tonic-gate 		if (id == NULL) {
52917c478bd9Sstevel@tonic-gate 			/*
52927c478bd9Sstevel@tonic-gate 			 * first two bytes are the length of the sequence
52937c478bd9Sstevel@tonic-gate 			 * (incl. these bytes)
52947c478bd9Sstevel@tonic-gate 			 * first byte is MSB
52957c478bd9Sstevel@tonic-gate 			 */
52967c478bd9Sstevel@tonic-gate 			if ((dsr & ECPP_nERR) ||
52977c478bd9Sstevel@tonic-gate 			    (nibble_peripheral2host(pp, &lenhi) == FAILURE) ||
52987c478bd9Sstevel@tonic-gate 			    (dsr & ECPP_nERR) ||
52997c478bd9Sstevel@tonic-gate 			    (nibble_peripheral2host(pp, &lenlo) == FAILURE)) {
53007c478bd9Sstevel@tonic-gate 				ecpp_error(pp->dip,
53017c478bd9Sstevel@tonic-gate 				    "ecpp_getdevid: id length read error\n");
53027c478bd9Sstevel@tonic-gate 				return (EIO);
53037c478bd9Sstevel@tonic-gate 			}
53047c478bd9Sstevel@tonic-gate 
53057c478bd9Sstevel@tonic-gate 			*lenp = (lenhi << 8) | (lenlo);
53067c478bd9Sstevel@tonic-gate 
53077c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
530819397407SSherry Moore 			    "ecpp_getdevid: id length = %d\n", *lenp);
53097c478bd9Sstevel@tonic-gate 
53107c478bd9Sstevel@tonic-gate 			if (*lenp < 2) {
53117c478bd9Sstevel@tonic-gate 				return (EIO);
53127c478bd9Sstevel@tonic-gate 			}
53137c478bd9Sstevel@tonic-gate 		} else {
53147c478bd9Sstevel@tonic-gate 			/*
53157c478bd9Sstevel@tonic-gate 			 * read the rest of the data
53167c478bd9Sstevel@tonic-gate 			 */
53177c478bd9Sstevel@tonic-gate 			i = *lenp;
53187c478bd9Sstevel@tonic-gate 			while (i && ((dsr & ECPP_nERR) == 0)) {
53197c478bd9Sstevel@tonic-gate 				if (nibble_peripheral2host(pp, id++) == FAILURE)
53207c478bd9Sstevel@tonic-gate 					break;
53217c478bd9Sstevel@tonic-gate 
53227c478bd9Sstevel@tonic-gate 				i--;
53237c478bd9Sstevel@tonic-gate 				dsr = DSR_READ(pp);
53247c478bd9Sstevel@tonic-gate 			}
53257c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip,
532619397407SSherry Moore 			    "ecpp_getdevid: read %d bytes\n", *lenp - i);
53277c478bd9Sstevel@tonic-gate 
53287c478bd9Sstevel@tonic-gate 			/*
53297c478bd9Sstevel@tonic-gate 			 * 1284: After receiving the sequence, the host is
53307c478bd9Sstevel@tonic-gate 			 * required to return the link to the Compatibility mode
53317c478bd9Sstevel@tonic-gate 			 */
53327c478bd9Sstevel@tonic-gate 			(void) ecpp_1284_termination(pp);
53337c478bd9Sstevel@tonic-gate 		}
53347c478bd9Sstevel@tonic-gate 
53357c478bd9Sstevel@tonic-gate 		break;
53367c478bd9Sstevel@tonic-gate 
53377c478bd9Sstevel@tonic-gate 	/* Other modes are not yet supported */
53387c478bd9Sstevel@tonic-gate 	default:
53397c478bd9Sstevel@tonic-gate 		return (EINVAL);
53407c478bd9Sstevel@tonic-gate 	}
53417c478bd9Sstevel@tonic-gate 
53427c478bd9Sstevel@tonic-gate 	return (0);
53437c478bd9Sstevel@tonic-gate }
53447c478bd9Sstevel@tonic-gate 
53457c478bd9Sstevel@tonic-gate /*
53467c478bd9Sstevel@tonic-gate  * Various hardware support
53477c478bd9Sstevel@tonic-gate  *
53487c478bd9Sstevel@tonic-gate  * First define some stubs for functions that do nothing
53497c478bd9Sstevel@tonic-gate  */
53507c478bd9Sstevel@tonic-gate 
53517c478bd9Sstevel@tonic-gate /*ARGSUSED*/
53527c478bd9Sstevel@tonic-gate static void
empty_config_mode(struct ecppunit * pp)53537c478bd9Sstevel@tonic-gate empty_config_mode(struct ecppunit *pp)
53547c478bd9Sstevel@tonic-gate {
53557c478bd9Sstevel@tonic-gate }
53567c478bd9Sstevel@tonic-gate 
53577c478bd9Sstevel@tonic-gate /*ARGSUSED*/
53587c478bd9Sstevel@tonic-gate static void
empty_mask_intr(struct ecppunit * pp)53597c478bd9Sstevel@tonic-gate empty_mask_intr(struct ecppunit *pp)
53607c478bd9Sstevel@tonic-gate {
53617c478bd9Sstevel@tonic-gate }
53627c478bd9Sstevel@tonic-gate 
53637c478bd9Sstevel@tonic-gate #if defined(__x86)
53647c478bd9Sstevel@tonic-gate static size_t
x86_getcnt(struct ecppunit * pp)53657c478bd9Sstevel@tonic-gate x86_getcnt(struct ecppunit *pp)
53667c478bd9Sstevel@tonic-gate {
53677c478bd9Sstevel@tonic-gate 	int count;
53687c478bd9Sstevel@tonic-gate 
53697c478bd9Sstevel@tonic-gate 	(void) ddi_dmae_getcnt(pp->dip, pp->uh.x86.chn, &count);
53707c478bd9Sstevel@tonic-gate 	return (count);
53717c478bd9Sstevel@tonic-gate }
53727c478bd9Sstevel@tonic-gate #endif
53737c478bd9Sstevel@tonic-gate 
53747c478bd9Sstevel@tonic-gate /*
53757c478bd9Sstevel@tonic-gate  *
53767c478bd9Sstevel@tonic-gate  * National PC87332 and PC97317 SuperIOs support routines
53777c478bd9Sstevel@tonic-gate  * These chips are used in PCI-based Darwin, Quark, Quasar, Excalibur
53787c478bd9Sstevel@tonic-gate  * and use EBus DMA facilities (Cheerio or RIO)
53797c478bd9Sstevel@tonic-gate  *
53807c478bd9Sstevel@tonic-gate  */
53817c478bd9Sstevel@tonic-gate 
53827c478bd9Sstevel@tonic-gate static int
pc87332_map_regs(struct ecppunit * pp)53837c478bd9Sstevel@tonic-gate pc87332_map_regs(struct ecppunit *pp)
53847c478bd9Sstevel@tonic-gate {
53857c478bd9Sstevel@tonic-gate 	if (ddi_regs_map_setup(pp->dip, 1, (caddr_t *)&pp->uh.ebus.c_reg, 0,
53867c478bd9Sstevel@tonic-gate 	    sizeof (struct config_reg), &acc_attr,
53877c478bd9Sstevel@tonic-gate 	    &pp->uh.ebus.c_handle) != DDI_SUCCESS) {
53887c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "pc87332_map_regs: failed c_reg\n");
53897c478bd9Sstevel@tonic-gate 		goto fail;
53907c478bd9Sstevel@tonic-gate 	}
53917c478bd9Sstevel@tonic-gate 
53927c478bd9Sstevel@tonic-gate 	if (ddi_regs_map_setup(pp->dip, 0, (caddr_t *)&pp->i_reg, 0,
53937c478bd9Sstevel@tonic-gate 	    sizeof (struct info_reg), &acc_attr, &pp->i_handle)
53947c478bd9Sstevel@tonic-gate 	    != DDI_SUCCESS) {
53957c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "pc87332_map_regs: failed i_reg\n");
53967c478bd9Sstevel@tonic-gate 		goto fail;
53977c478bd9Sstevel@tonic-gate 	}
53987c478bd9Sstevel@tonic-gate 
53997c478bd9Sstevel@tonic-gate 	if (ddi_regs_map_setup(pp->dip, 0, (caddr_t *)&pp->f_reg, 0x400,
54007c478bd9Sstevel@tonic-gate 	    sizeof (struct fifo_reg), &acc_attr, &pp->f_handle)
54017c478bd9Sstevel@tonic-gate 	    != DDI_SUCCESS) {
54027c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "pc87332_map_regs: failed f_reg\n");
54037c478bd9Sstevel@tonic-gate 		goto fail;
54047c478bd9Sstevel@tonic-gate 	}
54057c478bd9Sstevel@tonic-gate 
54067c478bd9Sstevel@tonic-gate 	if (ddi_regs_map_setup(pp->dip, 2, (caddr_t *)&pp->uh.ebus.dmac, 0,
54077c478bd9Sstevel@tonic-gate 	    sizeof (struct cheerio_dma_reg), &acc_attr,
54087c478bd9Sstevel@tonic-gate 	    &pp->uh.ebus.d_handle) != DDI_SUCCESS) {
54097c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "pc87332_map_regs: failed dmac\n");
54107c478bd9Sstevel@tonic-gate 		goto fail;
54117c478bd9Sstevel@tonic-gate 	}
54127c478bd9Sstevel@tonic-gate 
54137c478bd9Sstevel@tonic-gate 	return (SUCCESS);
54147c478bd9Sstevel@tonic-gate 
54157c478bd9Sstevel@tonic-gate fail:
54167c478bd9Sstevel@tonic-gate 	pc87332_unmap_regs(pp);
54177c478bd9Sstevel@tonic-gate 	return (FAILURE);
54187c478bd9Sstevel@tonic-gate }
54197c478bd9Sstevel@tonic-gate 
54207c478bd9Sstevel@tonic-gate static void
pc87332_unmap_regs(struct ecppunit * pp)54217c478bd9Sstevel@tonic-gate pc87332_unmap_regs(struct ecppunit *pp)
54227c478bd9Sstevel@tonic-gate {
54237c478bd9Sstevel@tonic-gate 	if (pp->uh.ebus.c_handle) {
54247c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&pp->uh.ebus.c_handle);
54257c478bd9Sstevel@tonic-gate 	}
54267c478bd9Sstevel@tonic-gate 	if (pp->uh.ebus.d_handle) {
54277c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&pp->uh.ebus.d_handle);
54287c478bd9Sstevel@tonic-gate 	}
54297c478bd9Sstevel@tonic-gate 	if (pp->i_handle) {
54307c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&pp->i_handle);
54317c478bd9Sstevel@tonic-gate 	}
54327c478bd9Sstevel@tonic-gate 	if (pp->f_handle) {
54337c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&pp->f_handle);
54347c478bd9Sstevel@tonic-gate 	}
54357c478bd9Sstevel@tonic-gate }
54367c478bd9Sstevel@tonic-gate 
54377c478bd9Sstevel@tonic-gate static uint8_t
pc87332_read_config_reg(struct ecppunit * pp,uint8_t reg_num)54387c478bd9Sstevel@tonic-gate pc87332_read_config_reg(struct ecppunit *pp, uint8_t reg_num)
54397c478bd9Sstevel@tonic-gate {
54407c478bd9Sstevel@tonic-gate 	uint8_t retval;
54417c478bd9Sstevel@tonic-gate 
54427c478bd9Sstevel@tonic-gate 	PP_PUTB(pp->uh.ebus.c_handle, &pp->uh.ebus.c_reg->index, reg_num);
54437c478bd9Sstevel@tonic-gate 	retval = PP_GETB(pp->uh.ebus.c_handle, &pp->uh.ebus.c_reg->data);
54447c478bd9Sstevel@tonic-gate 
54457c478bd9Sstevel@tonic-gate 	return (retval);
54467c478bd9Sstevel@tonic-gate }
54477c478bd9Sstevel@tonic-gate 
54487c478bd9Sstevel@tonic-gate static void
pc87332_write_config_reg(struct ecppunit * pp,uint8_t reg_num,uint8_t val)54497c478bd9Sstevel@tonic-gate pc87332_write_config_reg(struct ecppunit *pp, uint8_t reg_num, uint8_t val)
54507c478bd9Sstevel@tonic-gate {
54517c478bd9Sstevel@tonic-gate 	PP_PUTB(pp->uh.ebus.c_handle, &pp->uh.ebus.c_reg->index, reg_num);
54527c478bd9Sstevel@tonic-gate 	PP_PUTB(pp->uh.ebus.c_handle, &pp->uh.ebus.c_reg->data, val);
54537c478bd9Sstevel@tonic-gate 
54547c478bd9Sstevel@tonic-gate 	/*
54557c478bd9Sstevel@tonic-gate 	 * second write to this register is needed.  the register behaves as
54567c478bd9Sstevel@tonic-gate 	 * a fifo.  the first value written goes to the data register.  the
54577c478bd9Sstevel@tonic-gate 	 * second write pushes the initial value to the register indexed.
54587c478bd9Sstevel@tonic-gate 	 */
54597c478bd9Sstevel@tonic-gate 
54607c478bd9Sstevel@tonic-gate 	PP_PUTB(pp->uh.ebus.c_handle, &pp->uh.ebus.c_reg->data, val);
54617c478bd9Sstevel@tonic-gate }
54627c478bd9Sstevel@tonic-gate 
54637c478bd9Sstevel@tonic-gate static int
pc87332_config_chip(struct ecppunit * pp)54647c478bd9Sstevel@tonic-gate pc87332_config_chip(struct ecppunit *pp)
54657c478bd9Sstevel@tonic-gate {
54667c478bd9Sstevel@tonic-gate 	uint8_t pmc, fcr;
54677c478bd9Sstevel@tonic-gate 
54687c478bd9Sstevel@tonic-gate 	pp->current_phase = ECPP_PHASE_INIT;
54697c478bd9Sstevel@tonic-gate 
54707c478bd9Sstevel@tonic-gate 	/* ECP DMA configuration bit (PMC4) must be set */
54717c478bd9Sstevel@tonic-gate 	pmc = pc87332_read_config_reg(pp, PMC);
54727c478bd9Sstevel@tonic-gate 	if (!(pmc & PC87332_PMC_ECP_DMA_CONFIG)) {
54737c478bd9Sstevel@tonic-gate 		pc87332_write_config_reg(pp, PMC,
547419397407SSherry Moore 		    pmc | PC87332_PMC_ECP_DMA_CONFIG);
54757c478bd9Sstevel@tonic-gate 	}
54767c478bd9Sstevel@tonic-gate 
54777c478bd9Sstevel@tonic-gate 	/*
54787c478bd9Sstevel@tonic-gate 	 * The Parallel Port Multiplexor pins must be driven.
54797c478bd9Sstevel@tonic-gate 	 * Check to see if FCR3 is zero, if not clear FCR3.
54807c478bd9Sstevel@tonic-gate 	 */
54817c478bd9Sstevel@tonic-gate 	fcr = pc87332_read_config_reg(pp, FCR);
54827c478bd9Sstevel@tonic-gate 	if (fcr & PC87332_FCR_PPM_FLOAT_CTL) {
54837c478bd9Sstevel@tonic-gate 		pc87332_write_config_reg(pp, FCR,
548419397407SSherry Moore 		    fcr & ~PC87332_FCR_PPM_FLOAT_CTL);
54857c478bd9Sstevel@tonic-gate 	}
54867c478bd9Sstevel@tonic-gate 
54877c478bd9Sstevel@tonic-gate 	/*
54887c478bd9Sstevel@tonic-gate 	 * clear bits 3-0 in CTR (aka DCR) prior to enabling ECP mode
54897c478bd9Sstevel@tonic-gate 	 * CTR5 can not be cleared in SPP mode, CTR5 will return 1.
54907c478bd9Sstevel@tonic-gate 	 * "FAILURE" in this case is ok.  Better to use dcr_write()
54917c478bd9Sstevel@tonic-gate 	 * to ensure reliable writing to DCR.
54927c478bd9Sstevel@tonic-gate 	 */
54937c478bd9Sstevel@tonic-gate 	if (dcr_write(pp, ECPP_DCR_SET | ECPP_nINIT) == FAILURE) {
54947c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_config_87332: DCR config\n");
54957c478bd9Sstevel@tonic-gate 	}
54967c478bd9Sstevel@tonic-gate 
54977c478bd9Sstevel@tonic-gate 	/* enable ECP mode, level intr (note that DCR bits 3-0 == 0x0) */
54987c478bd9Sstevel@tonic-gate 	pc87332_write_config_reg(pp, PCR,
549919397407SSherry Moore 	    PC87332_PCR_INTR_LEVL | PC87332_PCR_ECP_EN);
55007c478bd9Sstevel@tonic-gate 
55017c478bd9Sstevel@tonic-gate 	/* put SuperIO in initial state */
55027c478bd9Sstevel@tonic-gate 	if (ecr_write(pp, ECR_mode_001 |
550319397407SSherry Moore 	    ECPP_INTR_MASK | ECPP_INTR_SRV) == FAILURE) {
55047c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_config_87332: ECR\n");
55057c478bd9Sstevel@tonic-gate 	}
55067c478bd9Sstevel@tonic-gate 
55077c478bd9Sstevel@tonic-gate 	if (dcr_write(pp, ECPP_DCR_SET | ECPP_SLCTIN | ECPP_nINIT) == FAILURE) {
55087c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_config_87332: w/DCR failed2.\n");
55097c478bd9Sstevel@tonic-gate 		return (FAILURE);
55107c478bd9Sstevel@tonic-gate 
55117c478bd9Sstevel@tonic-gate 	}
55127c478bd9Sstevel@tonic-gate 	/* we are in centronic mode */
55137c478bd9Sstevel@tonic-gate 	pp->current_mode = ECPP_CENTRONICS;
55147c478bd9Sstevel@tonic-gate 
55157c478bd9Sstevel@tonic-gate 	/* in compatible mode with no data transfer in progress */
55167c478bd9Sstevel@tonic-gate 	pp->current_phase = ECPP_PHASE_C_IDLE;
55177c478bd9Sstevel@tonic-gate 
55187c478bd9Sstevel@tonic-gate 	return (SUCCESS);
55197c478bd9Sstevel@tonic-gate }
55207c478bd9Sstevel@tonic-gate 
55217c478bd9Sstevel@tonic-gate /*
55227c478bd9Sstevel@tonic-gate  * A new mode was set, do some mode specific reconfiguration
55237c478bd9Sstevel@tonic-gate  * in this case - set interrupt characteristic
55247c478bd9Sstevel@tonic-gate  */
55257c478bd9Sstevel@tonic-gate static void
pc87332_config_mode(struct ecppunit * pp)55267c478bd9Sstevel@tonic-gate pc87332_config_mode(struct ecppunit *pp)
55277c478bd9Sstevel@tonic-gate {
55287c478bd9Sstevel@tonic-gate 	if (COMPAT_PIO(pp)) {
55297c478bd9Sstevel@tonic-gate 		pc87332_write_config_reg(pp, PCR, 0x04);
55307c478bd9Sstevel@tonic-gate 	} else {
55317c478bd9Sstevel@tonic-gate 		pc87332_write_config_reg(pp, PCR, 0x14);
55327c478bd9Sstevel@tonic-gate 	}
55337c478bd9Sstevel@tonic-gate }
55347c478bd9Sstevel@tonic-gate 
55357c478bd9Sstevel@tonic-gate static int
pc97317_map_regs(struct ecppunit * pp)55367c478bd9Sstevel@tonic-gate pc97317_map_regs(struct ecppunit *pp)
55377c478bd9Sstevel@tonic-gate {
55387c478bd9Sstevel@tonic-gate 	if (pc87332_map_regs(pp) != SUCCESS) {
55397c478bd9Sstevel@tonic-gate 		return (FAILURE);
55407c478bd9Sstevel@tonic-gate 	}
55417c478bd9Sstevel@tonic-gate 
55427c478bd9Sstevel@tonic-gate 	if (ddi_regs_map_setup(pp->dip, 0, (caddr_t *)&pp->uh.ebus.c2_reg,
554319397407SSherry Moore 	    0x403, sizeof (struct config2_reg), &acc_attr,
554419397407SSherry Moore 	    &pp->uh.ebus.c2_handle) != DDI_SUCCESS) {
55457c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "pc97317_map_regs: failed c2_reg\n");
55467c478bd9Sstevel@tonic-gate 		pc87332_unmap_regs(pp);
55477c478bd9Sstevel@tonic-gate 		return (FAILURE);
55487c478bd9Sstevel@tonic-gate 	} else {
55497c478bd9Sstevel@tonic-gate 		return (SUCCESS);
55507c478bd9Sstevel@tonic-gate 	}
55517c478bd9Sstevel@tonic-gate }
55527c478bd9Sstevel@tonic-gate 
55537c478bd9Sstevel@tonic-gate static void
pc97317_unmap_regs(struct ecppunit * pp)55547c478bd9Sstevel@tonic-gate pc97317_unmap_regs(struct ecppunit *pp)
55557c478bd9Sstevel@tonic-gate {
55567c478bd9Sstevel@tonic-gate 	if (pp->uh.ebus.c2_handle) {
55577c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&pp->uh.ebus.c2_handle);
55587c478bd9Sstevel@tonic-gate 	}
55597c478bd9Sstevel@tonic-gate 
55607c478bd9Sstevel@tonic-gate 	pc87332_unmap_regs(pp);
55617c478bd9Sstevel@tonic-gate }
55627c478bd9Sstevel@tonic-gate 
55637c478bd9Sstevel@tonic-gate /*
55647c478bd9Sstevel@tonic-gate  * OBP should configure the PC97317 such that it does not need further
55657c478bd9Sstevel@tonic-gate  * configuration.  Upon sustaining, it may be necessary to examine
55667c478bd9Sstevel@tonic-gate  * or change the configuration registers.  This routine is left in
55677c478bd9Sstevel@tonic-gate  * the file for that purpose.
55687c478bd9Sstevel@tonic-gate  */
55697c478bd9Sstevel@tonic-gate static int
pc97317_config_chip(struct ecppunit * pp)55707c478bd9Sstevel@tonic-gate pc97317_config_chip(struct ecppunit *pp)
55717c478bd9Sstevel@tonic-gate {
55727c478bd9Sstevel@tonic-gate 	uint8_t conreg;
55737c478bd9Sstevel@tonic-gate 
55747c478bd9Sstevel@tonic-gate 	/* set the logical device name */
55757c478bd9Sstevel@tonic-gate 	pc87332_write_config_reg(pp, PC97317_CONFIG_DEV_NO, 0x4);
55767c478bd9Sstevel@tonic-gate 
55777c478bd9Sstevel@tonic-gate 	/* SPP Compatibility */
55787c478bd9Sstevel@tonic-gate 	PP_PUTB(pp->uh.ebus.c2_handle,
557919397407SSherry Moore 	    &pp->uh.ebus.c2_reg->eir, PC97317_CONFIG2_CONTROL2);
55807c478bd9Sstevel@tonic-gate 	PP_PUTB(pp->uh.ebus.c2_handle, &pp->uh.ebus.c2_reg->edr, 0x80);
55817c478bd9Sstevel@tonic-gate 
55827c478bd9Sstevel@tonic-gate 	/* low interrupt polarity */
55837c478bd9Sstevel@tonic-gate 	pc87332_write_config_reg(pp, PC97317_CONFIG_INTR_TYPE, 0x00);
55847c478bd9Sstevel@tonic-gate 
55857c478bd9Sstevel@tonic-gate 	/* ECP mode */
55867c478bd9Sstevel@tonic-gate 	pc87332_write_config_reg(pp, PC97317_CONFIG_PP_CONFIG, 0xf2);
55877c478bd9Sstevel@tonic-gate 
55887c478bd9Sstevel@tonic-gate 	if (dcr_write(pp, ECPP_SLCTIN | ECPP_nINIT) == FAILURE) {
55897c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "pc97317_config_chip: failed w/DCR\n");
55907c478bd9Sstevel@tonic-gate 	}
55917c478bd9Sstevel@tonic-gate 
55927c478bd9Sstevel@tonic-gate 	if (ecr_write(pp, ECR_mode_001 |
559319397407SSherry Moore 	    ECPP_INTR_MASK | ECPP_INTR_SRV) == FAILURE) {
55947c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "pc97317_config_chip: failed w/ECR\n");
55957c478bd9Sstevel@tonic-gate 	}
55967c478bd9Sstevel@tonic-gate 
55977c478bd9Sstevel@tonic-gate #ifdef DEBUG
55987c478bd9Sstevel@tonic-gate 	conreg = pc87332_read_config_reg(pp, PC97317_CONFIG_DEV_NO);
55997c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "97317:conreg7(logical dev)=%x\n", conreg);
56007c478bd9Sstevel@tonic-gate 
56017c478bd9Sstevel@tonic-gate 	conreg = pc87332_read_config_reg(pp, PC97317_CONFIG_BASE_ADDR_MSB);
56027c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "97317:conreg60(addrHi)=%x\n", conreg);
56037c478bd9Sstevel@tonic-gate 
56047c478bd9Sstevel@tonic-gate 	conreg = pc87332_read_config_reg(pp, PC97317_CONFIG_BASE_ADDR_LSB);
56057c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "97317:conreg61(addrLo)=%x\n", conreg);
56067c478bd9Sstevel@tonic-gate 
56077c478bd9Sstevel@tonic-gate 	conreg = pc87332_read_config_reg(pp, PC97317_CONFIG_INTR_SEL);
56087c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "97317:conreg70(IRQL)=%x\n", conreg);
56097c478bd9Sstevel@tonic-gate 
56107c478bd9Sstevel@tonic-gate 	conreg = pc87332_read_config_reg(pp, PC97317_CONFIG_INTR_TYPE);
56117c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "97317:conreg71(intr type)=%x\n", conreg);
56127c478bd9Sstevel@tonic-gate 
56137c478bd9Sstevel@tonic-gate 	conreg = pc87332_read_config_reg(pp, PC97317_CONFIG_ACTIVATE);
56147c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "97317:conreg30(Active)=%x\n", conreg);
56157c478bd9Sstevel@tonic-gate 
56167c478bd9Sstevel@tonic-gate 	conreg = pc87332_read_config_reg(pp, PC97317_CONFIG_IO_RANGE);
56177c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "97317:conreg31(IO Range Check)=%x\n", conreg);
56187c478bd9Sstevel@tonic-gate 
56197c478bd9Sstevel@tonic-gate 	conreg = pc87332_read_config_reg(pp, PC97317_CONFIG_DMA0_CHAN);
56207c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "97317:conreg74(DMA0 Chan)=%x\n", conreg);
56217c478bd9Sstevel@tonic-gate 	conreg = pc87332_read_config_reg(pp, PC97317_CONFIG_DMA1_CHAN);
56227c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "97317:conreg75(DMA1 Chan)=%x\n", conreg);
56237c478bd9Sstevel@tonic-gate 
56247c478bd9Sstevel@tonic-gate 	conreg = pc87332_read_config_reg(pp, PC97317_CONFIG_PP_CONFIG);
56257c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "97317:conregFO(pport conf)=%x\n", conreg);
56267c478bd9Sstevel@tonic-gate 
56277c478bd9Sstevel@tonic-gate 	conreg = pc87332_read_config_reg(pp, PC97317_CONFIG_PP_CONFIG);
56287c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "97317:conregFO(pport conf)=%x\n", conreg);
56297c478bd9Sstevel@tonic-gate #endif /* DEBUG */
56307c478bd9Sstevel@tonic-gate 
56317c478bd9Sstevel@tonic-gate 	return (SUCCESS);
56327c478bd9Sstevel@tonic-gate }
56337c478bd9Sstevel@tonic-gate 
56347c478bd9Sstevel@tonic-gate /*
56357c478bd9Sstevel@tonic-gate  * A new mode was set, do some mode specific reconfiguration
56367c478bd9Sstevel@tonic-gate  * in this case - set interrupt polarity
56377c478bd9Sstevel@tonic-gate  */
56387c478bd9Sstevel@tonic-gate static void
pc97317_config_mode(struct ecppunit * pp)56397c478bd9Sstevel@tonic-gate pc97317_config_mode(struct ecppunit *pp)
56407c478bd9Sstevel@tonic-gate {
56417c478bd9Sstevel@tonic-gate 	/* set the logical device name */
56427c478bd9Sstevel@tonic-gate 	pc87332_write_config_reg(pp, PC97317_CONFIG_DEV_NO, 0x4);
56437c478bd9Sstevel@tonic-gate 
56447c478bd9Sstevel@tonic-gate 	if (COMPAT_PIO(pp) || pp->current_mode == ECPP_NIBBLE_MODE) {
56457c478bd9Sstevel@tonic-gate 		pc87332_write_config_reg(pp, PC97317_CONFIG_INTR_TYPE, 0x02);
56467c478bd9Sstevel@tonic-gate 	} else {
56477c478bd9Sstevel@tonic-gate 		pc87332_write_config_reg(pp, PC97317_CONFIG_INTR_TYPE, 0x00);
56487c478bd9Sstevel@tonic-gate 	}
56497c478bd9Sstevel@tonic-gate }
56507c478bd9Sstevel@tonic-gate 
56517c478bd9Sstevel@tonic-gate static void
cheerio_mask_intr(struct ecppunit * pp)56527c478bd9Sstevel@tonic-gate cheerio_mask_intr(struct ecppunit *pp)
56537c478bd9Sstevel@tonic-gate {
56547c478bd9Sstevel@tonic-gate 	/* mask Cheerio interrupts */
56557c478bd9Sstevel@tonic-gate 	AND_SET_LONG_R(pp->uh.ebus.d_handle,
565619397407SSherry Moore 	    &pp->uh.ebus.dmac->csr, ~DCSR_INT_EN);
56577c478bd9Sstevel@tonic-gate }
56587c478bd9Sstevel@tonic-gate 
56597c478bd9Sstevel@tonic-gate static void
cheerio_unmask_intr(struct ecppunit * pp)56607c478bd9Sstevel@tonic-gate cheerio_unmask_intr(struct ecppunit *pp)
56617c478bd9Sstevel@tonic-gate {
56627c478bd9Sstevel@tonic-gate 	/* unmask Cheerio interrupts */
56637c478bd9Sstevel@tonic-gate 	OR_SET_LONG_R(pp->uh.ebus.d_handle,
566419397407SSherry Moore 	    &pp->uh.ebus.dmac->csr, DCSR_INT_EN | DCSR_TCI_DIS);
56657c478bd9Sstevel@tonic-gate }
56667c478bd9Sstevel@tonic-gate 
56677c478bd9Sstevel@tonic-gate static int
cheerio_dma_start(struct ecppunit * pp)56687c478bd9Sstevel@tonic-gate cheerio_dma_start(struct ecppunit *pp)
56697c478bd9Sstevel@tonic-gate {
56707c478bd9Sstevel@tonic-gate 	cheerio_reset_dcsr(pp);
56717c478bd9Sstevel@tonic-gate 	SET_DMAC_BCR(pp, pp->dma_cookie.dmac_size);
56727c478bd9Sstevel@tonic-gate 	SET_DMAC_ACR(pp, pp->dma_cookie.dmac_address);
56737c478bd9Sstevel@tonic-gate 
56747c478bd9Sstevel@tonic-gate 	if (pp->dma_dir == DDI_DMA_READ) {
56757c478bd9Sstevel@tonic-gate 		SET_DMAC_CSR(pp, DCSR_INT_EN | DCSR_EN_CNT | DCSR_EN_DMA |
56767c478bd9Sstevel@tonic-gate 		    DCSR_CSR_DRAIN | DCSR_BURST_1 | DCSR_BURST_0 | DCSR_WRITE);
56777c478bd9Sstevel@tonic-gate 	} else {
56787c478bd9Sstevel@tonic-gate 		SET_DMAC_CSR(pp, DCSR_INT_EN | DCSR_EN_CNT | DCSR_EN_DMA |
567919397407SSherry Moore 		    DCSR_CSR_DRAIN | DCSR_BURST_1 | DCSR_BURST_0);
56807c478bd9Sstevel@tonic-gate 	}
56817c478bd9Sstevel@tonic-gate 
56827c478bd9Sstevel@tonic-gate 	return (SUCCESS);
56837c478bd9Sstevel@tonic-gate }
56847c478bd9Sstevel@tonic-gate 
56857c478bd9Sstevel@tonic-gate /*
56867c478bd9Sstevel@tonic-gate  * Note: BCR is reset to 0, so counter should always be read before dma_stop
56877c478bd9Sstevel@tonic-gate  */
56887c478bd9Sstevel@tonic-gate static int
cheerio_dma_stop(struct ecppunit * pp,size_t * countp)56897c478bd9Sstevel@tonic-gate cheerio_dma_stop(struct ecppunit *pp, size_t *countp)
56907c478bd9Sstevel@tonic-gate {
56917c478bd9Sstevel@tonic-gate 	uint8_t ecr;
56927c478bd9Sstevel@tonic-gate 
56937c478bd9Sstevel@tonic-gate 	/* disable DMA and byte counter */
56947c478bd9Sstevel@tonic-gate 	AND_SET_LONG_R(pp->uh.ebus.d_handle, &pp->uh.ebus.dmac->csr,
569519397407SSherry Moore 	    ~(DCSR_EN_DMA | DCSR_EN_CNT| DCSR_INT_EN));
56967c478bd9Sstevel@tonic-gate 
56977c478bd9Sstevel@tonic-gate 	/* ACK and disable the TC interrupt */
56987c478bd9Sstevel@tonic-gate 	OR_SET_LONG_R(pp->uh.ebus.d_handle, &pp->uh.ebus.dmac->csr,
569919397407SSherry Moore 	    DCSR_TC | DCSR_TCI_DIS);
57007c478bd9Sstevel@tonic-gate 
57017c478bd9Sstevel@tonic-gate 	/* read DMA count if requested */
57027c478bd9Sstevel@tonic-gate 	if (countp) {
57037c478bd9Sstevel@tonic-gate 		*countp = cheerio_getcnt(pp);
57047c478bd9Sstevel@tonic-gate 	}
57057c478bd9Sstevel@tonic-gate 
57067c478bd9Sstevel@tonic-gate 	cheerio_reset_dcsr(pp);
57077c478bd9Sstevel@tonic-gate 	SET_DMAC_BCR(pp, 0);
57087c478bd9Sstevel@tonic-gate 
57097c478bd9Sstevel@tonic-gate 	/* turn off SuperIO's DMA */
57107c478bd9Sstevel@tonic-gate 	ecr = ECR_READ(pp);
57117c478bd9Sstevel@tonic-gate 	if (ecr_write(pp, ecr & ~ECPP_DMA_ENABLE) == FAILURE) {
57127c478bd9Sstevel@tonic-gate 		return (FAILURE);
57137c478bd9Sstevel@tonic-gate 	}
57147c478bd9Sstevel@tonic-gate 
57157c478bd9Sstevel@tonic-gate 	/* Disable SuperIO interrupts and DMA */
57167c478bd9Sstevel@tonic-gate 	ecr = ECR_READ(pp);
57177c478bd9Sstevel@tonic-gate 
57187c478bd9Sstevel@tonic-gate 	return (ecr_write(pp, ecr | ECPP_INTR_SRV));
57197c478bd9Sstevel@tonic-gate }
57207c478bd9Sstevel@tonic-gate 
57217c478bd9Sstevel@tonic-gate static size_t
cheerio_getcnt(struct ecppunit * pp)57227c478bd9Sstevel@tonic-gate cheerio_getcnt(struct ecppunit *pp)
57237c478bd9Sstevel@tonic-gate {
57247c478bd9Sstevel@tonic-gate 	return (GET_DMAC_BCR(pp));
57257c478bd9Sstevel@tonic-gate }
57267c478bd9Sstevel@tonic-gate 
57277c478bd9Sstevel@tonic-gate /*
57287c478bd9Sstevel@tonic-gate  * Reset the DCSR by first setting the RESET bit to 1.  Poll the
57297c478bd9Sstevel@tonic-gate  * DCSR_CYC_PEND bit to make sure there are no more pending DMA cycles.
57307c478bd9Sstevel@tonic-gate  * If there are no more pending cycles, clear the RESET bit.
57317c478bd9Sstevel@tonic-gate  */
57327c478bd9Sstevel@tonic-gate static void
cheerio_reset_dcsr(struct ecppunit * pp)57337c478bd9Sstevel@tonic-gate cheerio_reset_dcsr(struct ecppunit *pp)
57347c478bd9Sstevel@tonic-gate {
57357c478bd9Sstevel@tonic-gate 	int	timeout = DMAC_RESET_TIMEOUT;
57367c478bd9Sstevel@tonic-gate 
57377c478bd9Sstevel@tonic-gate 	SET_DMAC_CSR(pp, DCSR_RESET);
57387c478bd9Sstevel@tonic-gate 
57397c478bd9Sstevel@tonic-gate 	while (GET_DMAC_CSR(pp) & DCSR_CYC_PEND) {
57407c478bd9Sstevel@tonic-gate 		if (timeout == 0) {
57417c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "cheerio_reset_dcsr: timeout\n");
57427c478bd9Sstevel@tonic-gate 			break;
57437c478bd9Sstevel@tonic-gate 		} else {
57447c478bd9Sstevel@tonic-gate 			drv_usecwait(1);
57457c478bd9Sstevel@tonic-gate 			timeout--;
57467c478bd9Sstevel@tonic-gate 		}
57477c478bd9Sstevel@tonic-gate 	}
57487c478bd9Sstevel@tonic-gate 
57497c478bd9Sstevel@tonic-gate 	SET_DMAC_CSR(pp, 0);
57507c478bd9Sstevel@tonic-gate }
57517c478bd9Sstevel@tonic-gate 
57527c478bd9Sstevel@tonic-gate /*
57537c478bd9Sstevel@tonic-gate  *
57547c478bd9Sstevel@tonic-gate  * Grover Southbridge (M1553) support routines
57557c478bd9Sstevel@tonic-gate  * Southbridge contains an Intel 8237 DMAC onboard which is used
57567c478bd9Sstevel@tonic-gate  * to transport data to/from PCI space to superio parallel port
57577c478bd9Sstevel@tonic-gate  *
57587c478bd9Sstevel@tonic-gate  */
57597c478bd9Sstevel@tonic-gate 
57607c478bd9Sstevel@tonic-gate 
57617c478bd9Sstevel@tonic-gate static int
m1553_map_regs(struct ecppunit * pp)57627c478bd9Sstevel@tonic-gate m1553_map_regs(struct ecppunit *pp)
57637c478bd9Sstevel@tonic-gate {
57647c478bd9Sstevel@tonic-gate 	if (ddi_regs_map_setup(pp->dip, 1, (caddr_t *)&pp->uh.m1553.isa_space,
576519397407SSherry Moore 	    0, sizeof (struct isaspace), &acc_attr,
576619397407SSherry Moore 	    &pp->uh.m1553.d_handle) != DDI_SUCCESS) {
57677c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "m1553_map_regs: failed isa space\n");
57687c478bd9Sstevel@tonic-gate 		goto fail;
57697c478bd9Sstevel@tonic-gate 	}
57707c478bd9Sstevel@tonic-gate 
57717c478bd9Sstevel@tonic-gate 	if (ddi_regs_map_setup(pp->dip, 0, (caddr_t *)&pp->i_reg, 0,
577219397407SSherry Moore 	    sizeof (struct info_reg), &acc_attr, &pp->i_handle)
577319397407SSherry Moore 	    != DDI_SUCCESS) {
57747c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "m1553_map_regs: failed i_reg\n");
57757c478bd9Sstevel@tonic-gate 		goto fail;
57767c478bd9Sstevel@tonic-gate 	}
57777c478bd9Sstevel@tonic-gate 
57787c478bd9Sstevel@tonic-gate 	if (ddi_regs_map_setup(pp->dip, 0, (caddr_t *)&pp->f_reg, 0x400,
577919397407SSherry Moore 	    sizeof (struct fifo_reg), &acc_attr, &pp->f_handle)
578019397407SSherry Moore 	    != DDI_SUCCESS) {
57817c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "m1553_map_regs: failed f_reg\n");
57827c478bd9Sstevel@tonic-gate 		goto fail;
57837c478bd9Sstevel@tonic-gate 	}
57847c478bd9Sstevel@tonic-gate 
57857c478bd9Sstevel@tonic-gate 	return (SUCCESS);
57867c478bd9Sstevel@tonic-gate 
57877c478bd9Sstevel@tonic-gate fail:
57887c478bd9Sstevel@tonic-gate 	m1553_unmap_regs(pp);
57897c478bd9Sstevel@tonic-gate 	return (FAILURE);
57907c478bd9Sstevel@tonic-gate }
57917c478bd9Sstevel@tonic-gate 
57927c478bd9Sstevel@tonic-gate static void
m1553_unmap_regs(struct ecppunit * pp)57937c478bd9Sstevel@tonic-gate m1553_unmap_regs(struct ecppunit *pp)
57947c478bd9Sstevel@tonic-gate {
57957c478bd9Sstevel@tonic-gate 	if (pp->uh.m1553.d_handle) {
57967c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&pp->uh.m1553.d_handle);
57977c478bd9Sstevel@tonic-gate 	}
57987c478bd9Sstevel@tonic-gate 	if (pp->i_handle) {
57997c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&pp->i_handle);
58007c478bd9Sstevel@tonic-gate 	}
58017c478bd9Sstevel@tonic-gate 	if (pp->f_handle) {
58027c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&pp->f_handle);
58037c478bd9Sstevel@tonic-gate 	}
58047c478bd9Sstevel@tonic-gate }
58057c478bd9Sstevel@tonic-gate 
58067c478bd9Sstevel@tonic-gate #if defined(__x86)
58077c478bd9Sstevel@tonic-gate static int
x86_map_regs(struct ecppunit * pp)58087c478bd9Sstevel@tonic-gate x86_map_regs(struct ecppunit *pp)
58097c478bd9Sstevel@tonic-gate {
58107c478bd9Sstevel@tonic-gate 	int nregs = 0;
58117c478bd9Sstevel@tonic-gate 
58127c478bd9Sstevel@tonic-gate 	if (ddi_regs_map_setup(pp->dip, 0, (caddr_t *)&pp->i_reg, 0,
58137c478bd9Sstevel@tonic-gate 	    sizeof (struct info_reg), &acc_attr, &pp->i_handle)
58147c478bd9Sstevel@tonic-gate 	    != DDI_SUCCESS) {
58157c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "x86_map_regs: failed i_reg\n");
58167c478bd9Sstevel@tonic-gate 		goto fail;
58177c478bd9Sstevel@tonic-gate 	}
58187c478bd9Sstevel@tonic-gate 	if (ddi_dev_nregs(pp->dip, &nregs) == DDI_SUCCESS && nregs == 2) {
58197c478bd9Sstevel@tonic-gate 		if (ddi_regs_map_setup(pp->dip, 1, (caddr_t *)&pp->f_reg, 0,
58207c478bd9Sstevel@tonic-gate 		    sizeof (struct fifo_reg), &acc_attr, &pp->f_handle)
58217c478bd9Sstevel@tonic-gate 		    != DDI_SUCCESS) {
58227c478bd9Sstevel@tonic-gate 			ecpp_error(pp->dip, "x86_map_regs: failed f_reg\n");
58237c478bd9Sstevel@tonic-gate 			goto fail;
58247c478bd9Sstevel@tonic-gate 		} else
58257c478bd9Sstevel@tonic-gate 			pp->noecpregs = FALSE;
58267c478bd9Sstevel@tonic-gate 	} else {
58277c478bd9Sstevel@tonic-gate 		pp->noecpregs = TRUE;
58287c478bd9Sstevel@tonic-gate 	}
58297c478bd9Sstevel@tonic-gate 	return (SUCCESS);
58307c478bd9Sstevel@tonic-gate fail:
58317c478bd9Sstevel@tonic-gate 	x86_unmap_regs(pp);
58327c478bd9Sstevel@tonic-gate 	return (FAILURE);
58337c478bd9Sstevel@tonic-gate }
58347c478bd9Sstevel@tonic-gate 
58357c478bd9Sstevel@tonic-gate static void
x86_unmap_regs(struct ecppunit * pp)58367c478bd9Sstevel@tonic-gate x86_unmap_regs(struct ecppunit *pp)
58377c478bd9Sstevel@tonic-gate {
58387c478bd9Sstevel@tonic-gate 	if (pp->i_handle) {
58397c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&pp->i_handle);
58407c478bd9Sstevel@tonic-gate 	}
58417c478bd9Sstevel@tonic-gate 	if (pp->f_handle) {
58427c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&pp->f_handle);
58437c478bd9Sstevel@tonic-gate 	}
58447c478bd9Sstevel@tonic-gate }
58457c478bd9Sstevel@tonic-gate #endif
58467c478bd9Sstevel@tonic-gate 
58477c478bd9Sstevel@tonic-gate static uint8_t
m1553_read_config_reg(struct ecppunit * pp,uint8_t reg_num)58487c478bd9Sstevel@tonic-gate m1553_read_config_reg(struct ecppunit *pp, uint8_t reg_num)
58497c478bd9Sstevel@tonic-gate {
58507c478bd9Sstevel@tonic-gate 	uint8_t retval;
58517c478bd9Sstevel@tonic-gate 
58527c478bd9Sstevel@tonic-gate 	dma8237_write(pp, 0x3F0, reg_num);
58537c478bd9Sstevel@tonic-gate 	retval = dma8237_read(pp, 0x3F1);
58547c478bd9Sstevel@tonic-gate 
58557c478bd9Sstevel@tonic-gate 	return (retval);
58567c478bd9Sstevel@tonic-gate }
58577c478bd9Sstevel@tonic-gate 
58587c478bd9Sstevel@tonic-gate static void
m1553_write_config_reg(struct ecppunit * pp,uint8_t reg_num,uint8_t val)58597c478bd9Sstevel@tonic-gate m1553_write_config_reg(struct ecppunit *pp, uint8_t reg_num, uint8_t val)
58607c478bd9Sstevel@tonic-gate {
58617c478bd9Sstevel@tonic-gate 	dma8237_write(pp, 0x3F0, reg_num);
58627c478bd9Sstevel@tonic-gate 	dma8237_write(pp, 0x3F1, val);
58637c478bd9Sstevel@tonic-gate }
58647c478bd9Sstevel@tonic-gate 
58657c478bd9Sstevel@tonic-gate static int
m1553_config_chip(struct ecppunit * pp)58667c478bd9Sstevel@tonic-gate m1553_config_chip(struct ecppunit *pp)
58677c478bd9Sstevel@tonic-gate {
58687c478bd9Sstevel@tonic-gate 	uint8_t conreg;
58697c478bd9Sstevel@tonic-gate 
58707c478bd9Sstevel@tonic-gate 	/* Unlock configuration regs with "key sequence" */
58717c478bd9Sstevel@tonic-gate 	dma8237_write(pp, 0x3F0, 0x51);
58727c478bd9Sstevel@tonic-gate 	dma8237_write(pp, 0x3F0, 0x23);
58737c478bd9Sstevel@tonic-gate 
58747c478bd9Sstevel@tonic-gate 	m1553_write_config_reg(pp, PnP_CONFIG_DEV_NO, 0x3);
58757c478bd9Sstevel@tonic-gate 	conreg = m1553_read_config_reg(pp, PnP_CONFIG_DEV_NO);
58767c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "M1553:conreg7(logical dev)=%x\n", conreg);
58777c478bd9Sstevel@tonic-gate 
58787c478bd9Sstevel@tonic-gate 	conreg = m1553_read_config_reg(pp, PnP_CONFIG_ACTIVATE);
58797c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "M1553:conreg30(Active)=%x\n", conreg);
58807c478bd9Sstevel@tonic-gate 
58817c478bd9Sstevel@tonic-gate 	conreg = m1553_read_config_reg(pp, PnP_CONFIG_BASE_ADDR_MSB);
58827c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "M1553:conreg60(addrHi)=%x\n", conreg);
58837c478bd9Sstevel@tonic-gate 	conreg = m1553_read_config_reg(pp, PnP_CONFIG_BASE_ADDR_LSB);
58847c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "M1553:conreg61(addrLo)=%x\n", conreg);
58857c478bd9Sstevel@tonic-gate 
58867c478bd9Sstevel@tonic-gate 	conreg = m1553_read_config_reg(pp, PnP_CONFIG_INTR_SEL);
58877c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "M1553:conreg70(IRQL)=%x\n", conreg);
58887c478bd9Sstevel@tonic-gate 
58897c478bd9Sstevel@tonic-gate 	conreg = m1553_read_config_reg(pp, PnP_CONFIG_DMA0_CHAN);
58907c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "M1553:conreg74(DMA0 Chan)=%x\n", conreg);
58917c478bd9Sstevel@tonic-gate 
58927c478bd9Sstevel@tonic-gate 	/* set FIFO threshold 1 and ECP mode, preserve bit 7 (IRQ polarity) */
58937c478bd9Sstevel@tonic-gate 	conreg = m1553_read_config_reg(pp, PnP_CONFIG_PP_CONFIG0);
58947c478bd9Sstevel@tonic-gate 	conreg = (conreg & ~0x7F) | 0x0A;
58957c478bd9Sstevel@tonic-gate 	m1553_write_config_reg(pp, PnP_CONFIG_PP_CONFIG0, conreg);
58967c478bd9Sstevel@tonic-gate 	conreg = m1553_read_config_reg(pp, PnP_CONFIG_PP_CONFIG0);
58977c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "M1553:conregFO(pport conf)=%x\n", conreg);
58987c478bd9Sstevel@tonic-gate 
58997c478bd9Sstevel@tonic-gate 	m1553_write_config_reg(pp, PnP_CONFIG_PP_CONFIG1, 0x04);
59007c478bd9Sstevel@tonic-gate 	conreg = m1553_read_config_reg(pp, PnP_CONFIG_PP_CONFIG1);
59017c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "M1553:conregF1(outconf)=%x\n", conreg);
59027c478bd9Sstevel@tonic-gate 
59037c478bd9Sstevel@tonic-gate 	/* lock configuration regs with key */
59047c478bd9Sstevel@tonic-gate 	dma8237_write(pp, 0x3F0, 0xBB);
59057c478bd9Sstevel@tonic-gate 
59067c478bd9Sstevel@tonic-gate 	/* Set ECR, DCR in known state */
59077c478bd9Sstevel@tonic-gate 	ECR_WRITE(pp, ECR_mode_001 | ECPP_INTR_MASK | ECPP_INTR_SRV);
59087c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, ECPP_SLCTIN | ECPP_nINIT);
59097c478bd9Sstevel@tonic-gate 
59107c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "m1553_config_chip: ecr=%x, dsr=%x, dcr=%x\n",
591119397407SSherry Moore 	    ECR_READ(pp), DSR_READ(pp), DCR_READ(pp));
59127c478bd9Sstevel@tonic-gate 
59137c478bd9Sstevel@tonic-gate 	return (SUCCESS);
59147c478bd9Sstevel@tonic-gate }
59157c478bd9Sstevel@tonic-gate 
59167c478bd9Sstevel@tonic-gate #if defined(__x86)
59177c478bd9Sstevel@tonic-gate static int
x86_config_chip(struct ecppunit * pp)59187c478bd9Sstevel@tonic-gate x86_config_chip(struct ecppunit *pp)
59197c478bd9Sstevel@tonic-gate {
59207c478bd9Sstevel@tonic-gate 	if (ecr_write(pp, ECR_mode_001 |
59217c478bd9Sstevel@tonic-gate 	    ECPP_INTR_MASK | ECPP_INTR_SRV) == FAILURE) {
59227c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "config chip: failed w/ecr\n");
59237c478bd9Sstevel@tonic-gate 		pp->noecpregs = TRUE;
59247c478bd9Sstevel@tonic-gate 	}
59257c478bd9Sstevel@tonic-gate 	if (pp->noecpregs)
59267c478bd9Sstevel@tonic-gate 		pp->fast_compat = FALSE;
59277c478bd9Sstevel@tonic-gate 	DCR_WRITE(pp, ECPP_SLCTIN | ECPP_nINIT);
59287c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "x86_config_chip: ecr=%x, dsr=%x, dcr=%x\n",
59297c478bd9Sstevel@tonic-gate 	    ECR_READ(pp), DSR_READ(pp), DCR_READ(pp));
59307c478bd9Sstevel@tonic-gate 	return (SUCCESS);
59317c478bd9Sstevel@tonic-gate }
59327c478bd9Sstevel@tonic-gate #endif
59337c478bd9Sstevel@tonic-gate 
59347c478bd9Sstevel@tonic-gate /*
59357c478bd9Sstevel@tonic-gate  * dma8237_dma_start() programs the selected 8 bit channel
59367c478bd9Sstevel@tonic-gate  * of DMAC1 with the dma cookie.  pp->dma_cookie must
59377c478bd9Sstevel@tonic-gate  * be set before this routine is called.
59387c478bd9Sstevel@tonic-gate  */
59397c478bd9Sstevel@tonic-gate static int
dma8237_dma_start(struct ecppunit * pp)59407c478bd9Sstevel@tonic-gate dma8237_dma_start(struct ecppunit *pp)
59417c478bd9Sstevel@tonic-gate {
59427c478bd9Sstevel@tonic-gate 	uint8_t chn;
59437c478bd9Sstevel@tonic-gate 
59447c478bd9Sstevel@tonic-gate 	chn = pp->uh.m1553.chn;
59457c478bd9Sstevel@tonic-gate 
59467c478bd9Sstevel@tonic-gate 	ASSERT(chn <= DMAE_CH3 &&
594719397407SSherry Moore 	    pp->dma_cookie.dmac_size != 0 &&
594861d4f4d1SToomas Soome 	    pp->dma_cookie.dmac_address != 0);
59497c478bd9Sstevel@tonic-gate 
59507c478bd9Sstevel@tonic-gate 	/* At this point Southbridge has not yet asserted DREQ */
59517c478bd9Sstevel@tonic-gate 
59527c478bd9Sstevel@tonic-gate 	/* set mode to read-from-memory. */
59537c478bd9Sstevel@tonic-gate 	dma8237_write(pp, DMAC2_MODE, DMAMODE_CASC);
59547c478bd9Sstevel@tonic-gate 	if (pp->dma_dir == DDI_DMA_READ) {
59557c478bd9Sstevel@tonic-gate 		dma8237_write(pp, DMAC1_MODE, DMAMODE_SINGLE |
595619397407SSherry Moore 		    DMAMODE_READ | chn);
59577c478bd9Sstevel@tonic-gate 	} else {
59587c478bd9Sstevel@tonic-gate 		dma8237_write(pp, DMAC1_MODE, DMAMODE_SINGLE |
595919397407SSherry Moore 		    DMAMODE_WRITE | chn);
59607c478bd9Sstevel@tonic-gate 	}
59617c478bd9Sstevel@tonic-gate 
59627c478bd9Sstevel@tonic-gate 	dma8237_write_addr(pp, pp->dma_cookie.dmac_address);
59637c478bd9Sstevel@tonic-gate 	dma8237_write_count(pp, pp->dma_cookie.dmac_size - 1);
59647c478bd9Sstevel@tonic-gate 
59657c478bd9Sstevel@tonic-gate 	/*
59667c478bd9Sstevel@tonic-gate 	 * M1553 chip does not permit to access DMA register banks
59677c478bd9Sstevel@tonic-gate 	 * while DMA is in flight. As a result, ecpp and floppy drivers
59687c478bd9Sstevel@tonic-gate 	 * can potentially corrupt each other's DMA. The interlocking mechanism
59697c478bd9Sstevel@tonic-gate 	 * is provided by a parent nexus driver (isadma), which is enabled
59707c478bd9Sstevel@tonic-gate 	 * indirectly through a DMAC1_ALLMASK register access:
59717c478bd9Sstevel@tonic-gate 	 *
59727c478bd9Sstevel@tonic-gate 	 * writing a non-zero value to this register enters a lock,
59737c478bd9Sstevel@tonic-gate 	 * writing zero releases the lock.
59747c478bd9Sstevel@tonic-gate 	 *
59757c478bd9Sstevel@tonic-gate 	 * DMA transfer must only occur after entering a lock.
59767c478bd9Sstevel@tonic-gate 	 * If the lock is already owned by other driver, we will block.
59777c478bd9Sstevel@tonic-gate 	 *
59787c478bd9Sstevel@tonic-gate 	 * The following operation unmasks our channel and masks all others
59797c478bd9Sstevel@tonic-gate 	 */
59807c478bd9Sstevel@tonic-gate 	dma8237_write(pp, DMAC1_ALLMASK, ~(1 << chn));
59817c478bd9Sstevel@tonic-gate 	pp->uh.m1553.isadma_entered = 1;
59827c478bd9Sstevel@tonic-gate 
59837c478bd9Sstevel@tonic-gate 	return (SUCCESS);
59847c478bd9Sstevel@tonic-gate }
59857c478bd9Sstevel@tonic-gate 
59867c478bd9Sstevel@tonic-gate static int
dma8237_dma_stop(struct ecppunit * pp,size_t * countp)59877c478bd9Sstevel@tonic-gate dma8237_dma_stop(struct ecppunit *pp, size_t *countp)
59887c478bd9Sstevel@tonic-gate {
59897c478bd9Sstevel@tonic-gate 	uint8_t ecr;
59907c478bd9Sstevel@tonic-gate 
59917c478bd9Sstevel@tonic-gate 	/* stop DMA */
59927c478bd9Sstevel@tonic-gate 	ecr = (ECR_READ(pp) & 0xe0) | ECPP_INTR_MASK | ECPP_INTR_SRV;
59937c478bd9Sstevel@tonic-gate 	(void) ecr_write(pp, ecr);
59947c478bd9Sstevel@tonic-gate 
59957c478bd9Sstevel@tonic-gate 	if (pp->uh.m1553.isadma_entered) {
59967c478bd9Sstevel@tonic-gate 		/* reset the channel mask so we can issue PIO's to our device */
59977c478bd9Sstevel@tonic-gate 		dma8237_write(pp, DMAC1_ALLMASK, 0);
59987c478bd9Sstevel@tonic-gate 		pp->uh.m1553.isadma_entered = 0;
59997c478bd9Sstevel@tonic-gate 
60007c478bd9Sstevel@tonic-gate 	}
60017c478bd9Sstevel@tonic-gate 
60027c478bd9Sstevel@tonic-gate 	/* read DMA count if requested */
60037c478bd9Sstevel@tonic-gate 	if (countp) {
60047c478bd9Sstevel@tonic-gate 		*countp = dma8237_getcnt(pp);
60057c478bd9Sstevel@tonic-gate 		if (pp->dma_dir == DDI_DMA_READ && *countp > 0) {
60067c478bd9Sstevel@tonic-gate 			(*countp)++;	/* need correction for reverse xfers */
60077c478bd9Sstevel@tonic-gate 		}
60087c478bd9Sstevel@tonic-gate 	}
60097c478bd9Sstevel@tonic-gate 	return (SUCCESS);
60107c478bd9Sstevel@tonic-gate }
60117c478bd9Sstevel@tonic-gate #if defined(__x86)
60127c478bd9Sstevel@tonic-gate static int
x86_dma_start(struct ecppunit * pp)60137c478bd9Sstevel@tonic-gate x86_dma_start(struct ecppunit *pp)
60147c478bd9Sstevel@tonic-gate {
60157c478bd9Sstevel@tonic-gate 	uint8_t chn;
60167c478bd9Sstevel@tonic-gate 	struct ddi_dmae_req dmaereq;
60177c478bd9Sstevel@tonic-gate 
60187c478bd9Sstevel@tonic-gate 	chn = pp->uh.x86.chn;
60197c478bd9Sstevel@tonic-gate 	ASSERT(chn <= DMAE_CH3 &&
60207c478bd9Sstevel@tonic-gate 	    pp->dma_cookie.dmac_size != 0 &&
602161d4f4d1SToomas Soome 	    pp->dma_cookie.dmac_address != 0);
60227c478bd9Sstevel@tonic-gate 	bzero(&dmaereq, sizeof (struct ddi_dmae_req));
60237c478bd9Sstevel@tonic-gate 	dmaereq.der_command =
60247c478bd9Sstevel@tonic-gate 	    (pp->dma_dir & DDI_DMA_READ) ? DMAE_CMD_READ : DMAE_CMD_WRITE;
60257c478bd9Sstevel@tonic-gate 	if (ddi_dmae_prog(pp->dip, &dmaereq, &pp->dma_cookie, chn)
60267c478bd9Sstevel@tonic-gate 	    != DDI_SUCCESS)
60277c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "prog failed !!!\n");
60287c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "dma_started..\n");
60297c478bd9Sstevel@tonic-gate 	return (SUCCESS);
60307c478bd9Sstevel@tonic-gate }
60317c478bd9Sstevel@tonic-gate 
60327c478bd9Sstevel@tonic-gate static int
x86_dma_stop(struct ecppunit * pp,size_t * countp)60337c478bd9Sstevel@tonic-gate x86_dma_stop(struct ecppunit *pp, size_t *countp)
60347c478bd9Sstevel@tonic-gate {
60357c478bd9Sstevel@tonic-gate 	uint8_t ecr;
60367c478bd9Sstevel@tonic-gate 
60377c478bd9Sstevel@tonic-gate 	/* stop DMA */
60387c478bd9Sstevel@tonic-gate 	if (pp->uh.x86.chn == 0xff)
60397c478bd9Sstevel@tonic-gate 		return (FAILURE);
60407c478bd9Sstevel@tonic-gate 	ecr = (ECR_READ(pp) & 0xe0) | ECPP_INTR_MASK | ECPP_INTR_SRV;
60417c478bd9Sstevel@tonic-gate 	(void) ecr_write(pp, ecr);
60427c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "dma_stop\n");
60437c478bd9Sstevel@tonic-gate 
60447c478bd9Sstevel@tonic-gate 	/* read DMA count if requested */
60457c478bd9Sstevel@tonic-gate 	if (countp) {
60467c478bd9Sstevel@tonic-gate 		*countp = x86_getcnt(pp);
60477c478bd9Sstevel@tonic-gate 	}
60487c478bd9Sstevel@tonic-gate 	ecpp_error(pp->dip, "dma_stoped..\n");
60497c478bd9Sstevel@tonic-gate 	return (SUCCESS);
60507c478bd9Sstevel@tonic-gate }
60517c478bd9Sstevel@tonic-gate #endif
60527c478bd9Sstevel@tonic-gate 
60537c478bd9Sstevel@tonic-gate /* channel must be masked */
60547c478bd9Sstevel@tonic-gate static void
dma8237_write_addr(struct ecppunit * pp,uint32_t addr)60557c478bd9Sstevel@tonic-gate dma8237_write_addr(struct ecppunit *pp, uint32_t addr)
60567c478bd9Sstevel@tonic-gate {
60577c478bd9Sstevel@tonic-gate 	uint8_t c_addr, c_lpage;
60587c478bd9Sstevel@tonic-gate 	uint16_t c_hpage, *p;
60597c478bd9Sstevel@tonic-gate 
60607c478bd9Sstevel@tonic-gate 	switch (pp->uh.m1553.chn) {
60617c478bd9Sstevel@tonic-gate 	case DMAE_CH0:
60627c478bd9Sstevel@tonic-gate 		c_addr = DMA_0ADR;
60637c478bd9Sstevel@tonic-gate 		c_lpage = DMA_0PAGE;
60647c478bd9Sstevel@tonic-gate 		c_hpage = DMA_0HPG;
60657c478bd9Sstevel@tonic-gate 		break;
60667c478bd9Sstevel@tonic-gate 
60677c478bd9Sstevel@tonic-gate 	case DMAE_CH1:
60687c478bd9Sstevel@tonic-gate 		c_addr = DMA_1ADR;
60697c478bd9Sstevel@tonic-gate 		c_lpage = DMA_1PAGE;
60707c478bd9Sstevel@tonic-gate 		c_hpage = DMA_1HPG;
60717c478bd9Sstevel@tonic-gate 		break;
60727c478bd9Sstevel@tonic-gate 
60737c478bd9Sstevel@tonic-gate 	case DMAE_CH2:
60747c478bd9Sstevel@tonic-gate 		c_addr = DMA_2ADR;
60757c478bd9Sstevel@tonic-gate 		c_lpage = DMA_2PAGE;
60767c478bd9Sstevel@tonic-gate 		c_hpage = DMA_2HPG;
60777c478bd9Sstevel@tonic-gate 		break;
60787c478bd9Sstevel@tonic-gate 
60797c478bd9Sstevel@tonic-gate 	case DMAE_CH3:
60807c478bd9Sstevel@tonic-gate 		c_addr = DMA_3ADR;
60817c478bd9Sstevel@tonic-gate 		c_lpage = DMA_3PAGE;
60827c478bd9Sstevel@tonic-gate 		c_hpage = DMA_3HPG;
60837c478bd9Sstevel@tonic-gate 		break;
60847c478bd9Sstevel@tonic-gate 
60857c478bd9Sstevel@tonic-gate 	default:
60867c478bd9Sstevel@tonic-gate 		return;
60877c478bd9Sstevel@tonic-gate 	}
60887c478bd9Sstevel@tonic-gate 
60897c478bd9Sstevel@tonic-gate 	p = (uint16_t *)&pp->uh.m1553.isa_space->isa_reg[c_addr];
60907c478bd9Sstevel@tonic-gate 	ddi_put16(pp->uh.m1553.d_handle, p, addr & 0xFFFF);
60917c478bd9Sstevel@tonic-gate 
60927c478bd9Sstevel@tonic-gate 	dma8237_write(pp, c_lpage, (addr & 0xFF0000) >> 16);
60937c478bd9Sstevel@tonic-gate 	dma8237_write(pp, c_hpage, (addr & 0xFF000000) >> 24);
60947c478bd9Sstevel@tonic-gate 
60957c478bd9Sstevel@tonic-gate }
60967c478bd9Sstevel@tonic-gate 
60977c478bd9Sstevel@tonic-gate /*
60987c478bd9Sstevel@tonic-gate  * This function may be useful during debugging,
60997c478bd9Sstevel@tonic-gate  * so we leave it in, but do not include in the binary
61007c478bd9Sstevel@tonic-gate  */
61017c478bd9Sstevel@tonic-gate #ifdef INCLUDE_DMA8237_READ_ADDR
61027c478bd9Sstevel@tonic-gate static uint32_t
dma8237_read_addr(struct ecppunit * pp)61037c478bd9Sstevel@tonic-gate dma8237_read_addr(struct ecppunit *pp)
61047c478bd9Sstevel@tonic-gate {
61057c478bd9Sstevel@tonic-gate 	uint8_t rval3, rval4;
61067c478bd9Sstevel@tonic-gate 	uint16_t rval16;
61077c478bd9Sstevel@tonic-gate 	uint32_t rval;
61087c478bd9Sstevel@tonic-gate 	uint8_t c_addr, c_lpage;
61097c478bd9Sstevel@tonic-gate 	uint16_t c_hpage, *p;
61107c478bd9Sstevel@tonic-gate 
61117c478bd9Sstevel@tonic-gate 	switch (pp->uh.m1553.chn) {
61127c478bd9Sstevel@tonic-gate 	case DMAE_CH0:
61137c478bd9Sstevel@tonic-gate 		c_addr = DMA_0ADR;
61147c478bd9Sstevel@tonic-gate 		c_lpage = DMA_0PAGE;
61157c478bd9Sstevel@tonic-gate 		c_hpage = DMA_0HPG;
61167c478bd9Sstevel@tonic-gate 		break;
61177c478bd9Sstevel@tonic-gate 
61187c478bd9Sstevel@tonic-gate 	case DMAE_CH1:
61197c478bd9Sstevel@tonic-gate 		c_addr = DMA_1ADR;
61207c478bd9Sstevel@tonic-gate 		c_lpage = DMA_1PAGE;
61217c478bd9Sstevel@tonic-gate 		c_hpage = DMA_1HPG;
61227c478bd9Sstevel@tonic-gate 		break;
61237c478bd9Sstevel@tonic-gate 
61247c478bd9Sstevel@tonic-gate 	case DMAE_CH2:
61257c478bd9Sstevel@tonic-gate 		c_addr = DMA_2ADR;
61267c478bd9Sstevel@tonic-gate 		c_lpage = DMA_2PAGE;
61277c478bd9Sstevel@tonic-gate 		c_hpage = DMA_2HPG;
61287c478bd9Sstevel@tonic-gate 		break;
61297c478bd9Sstevel@tonic-gate 
61307c478bd9Sstevel@tonic-gate 	case DMAE_CH3:
61317c478bd9Sstevel@tonic-gate 		c_addr = DMA_3ADR;
61327c478bd9Sstevel@tonic-gate 		c_lpage = DMA_3PAGE;
61337c478bd9Sstevel@tonic-gate 		c_hpage = DMA_3HPG;
61347c478bd9Sstevel@tonic-gate 		break;
61357c478bd9Sstevel@tonic-gate 
61367c478bd9Sstevel@tonic-gate 	default:
61377c478bd9Sstevel@tonic-gate 		return (NULL);
61387c478bd9Sstevel@tonic-gate 	}
61397c478bd9Sstevel@tonic-gate 
61407c478bd9Sstevel@tonic-gate 	p = (uint16_t *)&pp->uh.m1553.isa_space->isa_reg[c_addr];
61417c478bd9Sstevel@tonic-gate 	rval16 = ddi_get16(pp->uh.m1553.d_handle, p);
61427c478bd9Sstevel@tonic-gate 
61437c478bd9Sstevel@tonic-gate 	rval3 = dma8237_read(pp, c_lpage);
61447c478bd9Sstevel@tonic-gate 	rval4 = dma8237_read(pp, c_hpage);
61457c478bd9Sstevel@tonic-gate 
61467c478bd9Sstevel@tonic-gate 	rval = rval16 | (rval3 << 16) | (rval4 <<24);
61477c478bd9Sstevel@tonic-gate 
61487c478bd9Sstevel@tonic-gate 	return (rval);
61497c478bd9Sstevel@tonic-gate }
61507c478bd9Sstevel@tonic-gate #endif
61517c478bd9Sstevel@tonic-gate 
61527c478bd9Sstevel@tonic-gate static void
dma8237_write_count(struct ecppunit * pp,uint32_t count)61537c478bd9Sstevel@tonic-gate dma8237_write_count(struct ecppunit *pp, uint32_t count)
61547c478bd9Sstevel@tonic-gate {
61557c478bd9Sstevel@tonic-gate 	uint8_t c_wcnt;
61567c478bd9Sstevel@tonic-gate 	uint16_t *p;
61577c478bd9Sstevel@tonic-gate 
61587c478bd9Sstevel@tonic-gate 	switch (pp->uh.m1553.chn) {
61597c478bd9Sstevel@tonic-gate 	case DMAE_CH0:
61607c478bd9Sstevel@tonic-gate 		c_wcnt = DMA_0WCNT;
61617c478bd9Sstevel@tonic-gate 		break;
61627c478bd9Sstevel@tonic-gate 
61637c478bd9Sstevel@tonic-gate 	case DMAE_CH1:
61647c478bd9Sstevel@tonic-gate 		c_wcnt = DMA_1WCNT;
61657c478bd9Sstevel@tonic-gate 		break;
61667c478bd9Sstevel@tonic-gate 
61677c478bd9Sstevel@tonic-gate 	case DMAE_CH2:
61687c478bd9Sstevel@tonic-gate 		c_wcnt = DMA_2WCNT;
61697c478bd9Sstevel@tonic-gate 		break;
61707c478bd9Sstevel@tonic-gate 
61717c478bd9Sstevel@tonic-gate 	case DMAE_CH3:
61727c478bd9Sstevel@tonic-gate 		c_wcnt = DMA_3WCNT;
61737c478bd9Sstevel@tonic-gate 		break;
61747c478bd9Sstevel@tonic-gate 
61757c478bd9Sstevel@tonic-gate 	default:
61767c478bd9Sstevel@tonic-gate 		return;
61777c478bd9Sstevel@tonic-gate 	}
61787c478bd9Sstevel@tonic-gate 
61797c478bd9Sstevel@tonic-gate 	p = (uint16_t *)&pp->uh.m1553.isa_space->isa_reg[c_wcnt];
61807c478bd9Sstevel@tonic-gate 	ddi_put16(pp->uh.m1553.d_handle, p, count & 0xFFFF);
61817c478bd9Sstevel@tonic-gate 
61827c478bd9Sstevel@tonic-gate }
61837c478bd9Sstevel@tonic-gate 
61847c478bd9Sstevel@tonic-gate static uint32_t
dma8237_read_count(struct ecppunit * pp)61857c478bd9Sstevel@tonic-gate dma8237_read_count(struct ecppunit *pp)
61867c478bd9Sstevel@tonic-gate {
61877c478bd9Sstevel@tonic-gate 	uint8_t c_wcnt;
61887c478bd9Sstevel@tonic-gate 	uint16_t *p;
61897c478bd9Sstevel@tonic-gate 
61907c478bd9Sstevel@tonic-gate 	switch (pp->uh.m1553.chn) {
61917c478bd9Sstevel@tonic-gate 	case DMAE_CH0:
61927c478bd9Sstevel@tonic-gate 		c_wcnt = DMA_0WCNT;
61937c478bd9Sstevel@tonic-gate 		break;
61947c478bd9Sstevel@tonic-gate 
61957c478bd9Sstevel@tonic-gate 	case DMAE_CH1:
61967c478bd9Sstevel@tonic-gate 		c_wcnt = DMA_1WCNT;
61977c478bd9Sstevel@tonic-gate 		break;
61987c478bd9Sstevel@tonic-gate 
61997c478bd9Sstevel@tonic-gate 	case DMAE_CH2:
62007c478bd9Sstevel@tonic-gate 		c_wcnt = DMA_2WCNT;
62017c478bd9Sstevel@tonic-gate 		break;
62027c478bd9Sstevel@tonic-gate 
62037c478bd9Sstevel@tonic-gate 	case DMAE_CH3:
62047c478bd9Sstevel@tonic-gate 		c_wcnt = DMA_3WCNT;
62057c478bd9Sstevel@tonic-gate 		break;
62067c478bd9Sstevel@tonic-gate 
62077c478bd9Sstevel@tonic-gate 	default:
62082520aea3SToomas Soome 		return (0);
62097c478bd9Sstevel@tonic-gate 	}
62107c478bd9Sstevel@tonic-gate 
62117c478bd9Sstevel@tonic-gate 	p = (uint16_t *)&pp->uh.m1553.isa_space->isa_reg[c_wcnt];
62127c478bd9Sstevel@tonic-gate 	return (ddi_get16(pp->uh.m1553.d_handle, p));
62137c478bd9Sstevel@tonic-gate 
62147c478bd9Sstevel@tonic-gate }
62157c478bd9Sstevel@tonic-gate 
62167c478bd9Sstevel@tonic-gate static void
dma8237_write(struct ecppunit * pp,int reg_num,uint8_t val)62177c478bd9Sstevel@tonic-gate dma8237_write(struct ecppunit *pp, int reg_num, uint8_t val)
62187c478bd9Sstevel@tonic-gate {
62197c478bd9Sstevel@tonic-gate 	ddi_put8(pp->uh.m1553.d_handle,
622019397407SSherry Moore 	    &pp->uh.m1553.isa_space->isa_reg[reg_num], val);
62217c478bd9Sstevel@tonic-gate }
62227c478bd9Sstevel@tonic-gate 
62237c478bd9Sstevel@tonic-gate static uint8_t
dma8237_read(struct ecppunit * pp,int reg_num)62247c478bd9Sstevel@tonic-gate dma8237_read(struct ecppunit *pp, int reg_num)
62257c478bd9Sstevel@tonic-gate {
62267c478bd9Sstevel@tonic-gate 	return (ddi_get8(pp->uh.m1553.d_handle,
62277c478bd9Sstevel@tonic-gate 	    &pp->uh.m1553.isa_space->isa_reg[reg_num]));
62287c478bd9Sstevel@tonic-gate }
62297c478bd9Sstevel@tonic-gate 
62307c478bd9Sstevel@tonic-gate static size_t
dma8237_getcnt(struct ecppunit * pp)62317c478bd9Sstevel@tonic-gate dma8237_getcnt(struct ecppunit *pp)
62327c478bd9Sstevel@tonic-gate {
62337c478bd9Sstevel@tonic-gate 	uint32_t cnt;
62347c478bd9Sstevel@tonic-gate 
62357c478bd9Sstevel@tonic-gate 	if ((cnt = dma8237_read_count(pp)) == 0xffff)
62367c478bd9Sstevel@tonic-gate 		cnt = 0;
62377c478bd9Sstevel@tonic-gate 	else
62387c478bd9Sstevel@tonic-gate 		cnt++;
62397c478bd9Sstevel@tonic-gate 	return (cnt);
62407c478bd9Sstevel@tonic-gate }
62417c478bd9Sstevel@tonic-gate 
62427c478bd9Sstevel@tonic-gate 
62437c478bd9Sstevel@tonic-gate /*
62447c478bd9Sstevel@tonic-gate  *
62457c478bd9Sstevel@tonic-gate  * Kstat support routines
62467c478bd9Sstevel@tonic-gate  *
62477c478bd9Sstevel@tonic-gate  */
62487c478bd9Sstevel@tonic-gate static void
ecpp_kstat_init(struct ecppunit * pp)62497c478bd9Sstevel@tonic-gate ecpp_kstat_init(struct ecppunit *pp)
62507c478bd9Sstevel@tonic-gate {
62517c478bd9Sstevel@tonic-gate 	struct ecppkstat *ekp;
62527c478bd9Sstevel@tonic-gate 	char buf[16];
62537c478bd9Sstevel@tonic-gate 
62547c478bd9Sstevel@tonic-gate 	/*
62557c478bd9Sstevel@tonic-gate 	 * Allocate, initialize and install interrupt counter kstat
62567c478bd9Sstevel@tonic-gate 	 */
62577c478bd9Sstevel@tonic-gate 	(void) sprintf(buf, "ecppc%d", pp->instance);
62587c478bd9Sstevel@tonic-gate 	pp->intrstats = kstat_create("ecpp", pp->instance, buf, "controller",
625919397407SSherry Moore 	    KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT);
62607c478bd9Sstevel@tonic-gate 	if (pp->intrstats == NULL) {
62617c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_kstat_init:1: kstat_create failed");
62627c478bd9Sstevel@tonic-gate 	} else {
62637c478bd9Sstevel@tonic-gate 		pp->intrstats->ks_update = ecpp_kstatintr_update;
62647c478bd9Sstevel@tonic-gate 		pp->intrstats->ks_private = (void *) pp;
62657c478bd9Sstevel@tonic-gate 		kstat_install(pp->intrstats);
62667c478bd9Sstevel@tonic-gate 	}
62677c478bd9Sstevel@tonic-gate 
62687c478bd9Sstevel@tonic-gate 	/*
62697c478bd9Sstevel@tonic-gate 	 * Allocate, initialize and install misc stats kstat
62707c478bd9Sstevel@tonic-gate 	 */
62717c478bd9Sstevel@tonic-gate 	pp->ksp = kstat_create("ecpp", pp->instance, NULL, "misc",
627219397407SSherry Moore 	    KSTAT_TYPE_NAMED,
627319397407SSherry Moore 	    sizeof (struct ecppkstat) / sizeof (kstat_named_t),
627419397407SSherry Moore 	    KSTAT_FLAG_PERSISTENT);
62757c478bd9Sstevel@tonic-gate 	if (pp->ksp == NULL) {
62767c478bd9Sstevel@tonic-gate 		ecpp_error(pp->dip, "ecpp_kstat_init:2: kstat_create failed");
62777c478bd9Sstevel@tonic-gate 		return;
62787c478bd9Sstevel@tonic-gate 	}
62797c478bd9Sstevel@tonic-gate 
62807c478bd9Sstevel@tonic-gate 	ekp = (struct ecppkstat *)pp->ksp->ks_data;
62817c478bd9Sstevel@tonic-gate 
62827c478bd9Sstevel@tonic-gate #define	EK_NAMED_INIT(name) \
62837c478bd9Sstevel@tonic-gate 	kstat_named_init(&ekp->ek_##name, #name, KSTAT_DATA_UINT32)
62847c478bd9Sstevel@tonic-gate 
62857c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(ctx_obytes);
62867c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(ctxpio_obytes);
62877c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(nib_ibytes);
62887c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(ecp_obytes);
62897c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(ecp_ibytes);
62907c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(epp_obytes);
62917c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(epp_ibytes);
62927c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(diag_obytes);
62937c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(to_ctx);
62947c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(to_nib);
62957c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(to_ecp);
62967c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(to_epp);
62977c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(to_diag);
62987c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(xfer_tout);
62997c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(ctx_cf);
63007c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(joblen);
63017c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(isr_reattempt_high);
63027c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(mode);
63037c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(phase);
63047c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(backchan);
63057c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(iomode);
63067c478bd9Sstevel@tonic-gate 	EK_NAMED_INIT(state);
63077c478bd9Sstevel@tonic-gate 
63087c478bd9Sstevel@tonic-gate 	pp->ksp->ks_update = ecpp_kstat_update;
63097c478bd9Sstevel@tonic-gate 	pp->ksp->ks_private = (void *) pp;
63107c478bd9Sstevel@tonic-gate 	kstat_install(pp->ksp);
63117c478bd9Sstevel@tonic-gate }
63127c478bd9Sstevel@tonic-gate 
63137c478bd9Sstevel@tonic-gate static int
ecpp_kstat_update(kstat_t * ksp,int rw)63147c478bd9Sstevel@tonic-gate ecpp_kstat_update(kstat_t *ksp, int rw)
63157c478bd9Sstevel@tonic-gate {
63167c478bd9Sstevel@tonic-gate 	struct ecppunit *pp;
63177c478bd9Sstevel@tonic-gate 	struct ecppkstat *ekp;
63187c478bd9Sstevel@tonic-gate 
63197c478bd9Sstevel@tonic-gate 	/*
63207c478bd9Sstevel@tonic-gate 	 * For the time being there is no point
63217c478bd9Sstevel@tonic-gate 	 * in supporting writable kstats
63227c478bd9Sstevel@tonic-gate 	 */
63237c478bd9Sstevel@tonic-gate 	if (rw == KSTAT_WRITE) {
63247c478bd9Sstevel@tonic-gate 		return (EACCES);
63257c478bd9Sstevel@tonic-gate 	}
63267c478bd9Sstevel@tonic-gate 
63277c478bd9Sstevel@tonic-gate 	pp = (struct ecppunit *)ksp->ks_private;
63287c478bd9Sstevel@tonic-gate 	ekp = (struct ecppkstat *)ksp->ks_data;
63297c478bd9Sstevel@tonic-gate 
63307c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->umutex);
63317c478bd9Sstevel@tonic-gate 
63327c478bd9Sstevel@tonic-gate 	ekp->ek_ctx_obytes.value.ui32	= pp->obytes[ECPP_CENTRONICS] +
633319397407SSherry Moore 	    pp->obytes[ECPP_COMPAT_MODE];
63347c478bd9Sstevel@tonic-gate 	ekp->ek_ctxpio_obytes.value.ui32 = pp->ctxpio_obytes;
63357c478bd9Sstevel@tonic-gate 	ekp->ek_nib_ibytes.value.ui32	= pp->ibytes[ECPP_NIBBLE_MODE];
63367c478bd9Sstevel@tonic-gate 	ekp->ek_ecp_obytes.value.ui32	= pp->obytes[ECPP_ECP_MODE];
63377c478bd9Sstevel@tonic-gate 	ekp->ek_ecp_ibytes.value.ui32	= pp->ibytes[ECPP_ECP_MODE];
63387c478bd9Sstevel@tonic-gate 	ekp->ek_epp_obytes.value.ui32	= pp->obytes[ECPP_EPP_MODE];
63397c478bd9Sstevel@tonic-gate 	ekp->ek_epp_ibytes.value.ui32	= pp->ibytes[ECPP_EPP_MODE];
63407c478bd9Sstevel@tonic-gate 	ekp->ek_diag_obytes.value.ui32	= pp->obytes[ECPP_DIAG_MODE];
63417c478bd9Sstevel@tonic-gate 	ekp->ek_to_ctx.value.ui32	= pp->to_mode[ECPP_CENTRONICS] +
634219397407SSherry Moore 	    pp->to_mode[ECPP_COMPAT_MODE];
63437c478bd9Sstevel@tonic-gate 	ekp->ek_to_nib.value.ui32	= pp->to_mode[ECPP_NIBBLE_MODE];
63447c478bd9Sstevel@tonic-gate 	ekp->ek_to_ecp.value.ui32	= pp->to_mode[ECPP_ECP_MODE];
63457c478bd9Sstevel@tonic-gate 	ekp->ek_to_epp.value.ui32	= pp->to_mode[ECPP_EPP_MODE];
63467c478bd9Sstevel@tonic-gate 	ekp->ek_to_diag.value.ui32	= pp->to_mode[ECPP_DIAG_MODE];
63477c478bd9Sstevel@tonic-gate 	ekp->ek_xfer_tout.value.ui32	= pp->xfer_tout;
63487c478bd9Sstevel@tonic-gate 	ekp->ek_ctx_cf.value.ui32	= pp->ctx_cf;
63497c478bd9Sstevel@tonic-gate 	ekp->ek_joblen.value.ui32	= pp->joblen;
63507c478bd9Sstevel@tonic-gate 	ekp->ek_isr_reattempt_high.value.ui32	= pp->isr_reattempt_high;
63517c478bd9Sstevel@tonic-gate 	ekp->ek_mode.value.ui32		= pp->current_mode;
63527c478bd9Sstevel@tonic-gate 	ekp->ek_phase.value.ui32	= pp->current_phase;
63537c478bd9Sstevel@tonic-gate 	ekp->ek_backchan.value.ui32	= pp->backchannel;
63547c478bd9Sstevel@tonic-gate 	ekp->ek_iomode.value.ui32	= pp->io_mode;
63557c478bd9Sstevel@tonic-gate 	ekp->ek_state.value.ui32	= pp->e_busy;
63567c478bd9Sstevel@tonic-gate 
63577c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->umutex);
63587c478bd9Sstevel@tonic-gate 
63597c478bd9Sstevel@tonic-gate 	return (0);
63607c478bd9Sstevel@tonic-gate }
63617c478bd9Sstevel@tonic-gate 
63627c478bd9Sstevel@tonic-gate static int
ecpp_kstatintr_update(kstat_t * ksp,int rw)63637c478bd9Sstevel@tonic-gate ecpp_kstatintr_update(kstat_t *ksp, int rw)
63647c478bd9Sstevel@tonic-gate {
63657c478bd9Sstevel@tonic-gate 	struct ecppunit *pp;
63667c478bd9Sstevel@tonic-gate 
63677c478bd9Sstevel@tonic-gate 	/*
63687c478bd9Sstevel@tonic-gate 	 * For the time being there is no point
63697c478bd9Sstevel@tonic-gate 	 * in supporting writable kstats
63707c478bd9Sstevel@tonic-gate 	 */
63717c478bd9Sstevel@tonic-gate 	if (rw == KSTAT_WRITE) {
63727c478bd9Sstevel@tonic-gate 		return (EACCES);
63737c478bd9Sstevel@tonic-gate 	}
63747c478bd9Sstevel@tonic-gate 
63757c478bd9Sstevel@tonic-gate 	pp = (struct ecppunit *)ksp->ks_private;
63767c478bd9Sstevel@tonic-gate 
63777c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->umutex);
63787c478bd9Sstevel@tonic-gate 
63797c478bd9Sstevel@tonic-gate 	KSTAT_INTR_PTR(ksp)->intrs[KSTAT_INTR_HARD] = pp->intr_hard;
63807c478bd9Sstevel@tonic-gate 	KSTAT_INTR_PTR(ksp)->intrs[KSTAT_INTR_SPURIOUS] = pp->intr_spurious;
63817c478bd9Sstevel@tonic-gate 	KSTAT_INTR_PTR(ksp)->intrs[KSTAT_INTR_SOFT] = pp->intr_soft;
63827c478bd9Sstevel@tonic-gate 
63837c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->umutex);
63847c478bd9Sstevel@tonic-gate 
63857c478bd9Sstevel@tonic-gate 	return (0);
63867c478bd9Sstevel@tonic-gate }
6387