xref: /onnv-gate/usr/src/uts/sun/io/fd.c (revision 0)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * Intel 82077 Floppy Disk Driver
31*0Sstevel@tonic-gate  */
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate /*
34*0Sstevel@tonic-gate  * Notes
35*0Sstevel@tonic-gate  *
36*0Sstevel@tonic-gate  *	0. The driver supports two flavors of hardware design:
37*0Sstevel@tonic-gate  *		"SUNW,fdtwo"	- sun4m	- 82077 with sun4m style Auxio
38*0Sstevel@tonic-gate  *		"fdthree"  - sun4u - 82077 with DMA
39*0Sstevel@tonic-gate  *	   In addition it supports an apparent bug in some versions of
40*0Sstevel@tonic-gate  *	   the 82077 controller.
41*0Sstevel@tonic-gate  *
42*0Sstevel@tonic-gate  *	1. The driver is mostly set up for multiple controllers, multiple
43*0Sstevel@tonic-gate  *	drives. However- we *do* assume the use of the AUXIO register, and
44*0Sstevel@tonic-gate  *	if we ever have > 1 fdc, we'll have to see what that means. This
45*0Sstevel@tonic-gate  *	is all intrinsically machine specific, but there isn't much we
46*0Sstevel@tonic-gate  *	can do about it.
47*0Sstevel@tonic-gate  *
48*0Sstevel@tonic-gate  *	2. The driver also is structured to deal with one drive active at
49*0Sstevel@tonic-gate  *	a time. This is because the 82072 chip (no longer supported) was
50*0Sstevel@tonic-gate  *	known to be buggy with respect to overlapped seeks.
51*0Sstevel@tonic-gate  *
52*0Sstevel@tonic-gate  *	3. The high level interrupt code is in assembler, and runs in a
53*0Sstevel@tonic-gate  *	sparc trap window. It acts as a pseudo-dma engine as well as
54*0Sstevel@tonic-gate  *	handles a couple of other interrupts. When it gets its job done,
55*0Sstevel@tonic-gate  *	it schedules a second stage interrupt (soft interrupt) which
56*0Sstevel@tonic-gate  *	is then fielded here in fd_lointr.  When DMA is used, the fdintr_dma
57*0Sstevel@tonic-gate  *	interrupt handler is used.
58*0Sstevel@tonic-gate  *
59*0Sstevel@tonic-gate  *	4. Nearly all locking is done on a lower level MUTEX_DRIVER
60*0Sstevel@tonic-gate  *	mutex. The locking is quite conservative, and is generally
61*0Sstevel@tonic-gate  *	established very close to any of the entries into the driver.
62*0Sstevel@tonic-gate  *	There is nearly no locking done of the high level MUTEX_DRIVER
63*0Sstevel@tonic-gate  *	mutex (which generally is a SPIN mutex because the floppy usually
64*0Sstevel@tonic-gate  *	interrupts above LOCK_LEVEL). The assembler high level interrupt
65*0Sstevel@tonic-gate  *	handler grabs the high level mutex, but the code in the driver
66*0Sstevel@tonic-gate  *	here is especially structured to not need to do this.
67*0Sstevel@tonic-gate  *
68*0Sstevel@tonic-gate  *	5. Fdrawioctl commands that pass data are not optimized for
69*0Sstevel@tonic-gate  *	speed. If they need to be faster, the driver structure will
70*0Sstevel@tonic-gate  *	have to be redone such that fdrawioctl calls physio after
71*0Sstevel@tonic-gate  *	cons'ing up a uio structure and that fdstart will be able
72*0Sstevel@tonic-gate  *	to detect that a particular buffer is a 'special' buffer.
73*0Sstevel@tonic-gate  *
74*0Sstevel@tonic-gate  *	6. Removable media support is not complete.
75*0Sstevel@tonic-gate  *
76*0Sstevel@tonic-gate  */
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate #include <sys/param.h>
79*0Sstevel@tonic-gate #include <sys/buf.h>
80*0Sstevel@tonic-gate #include <sys/ioctl.h>
81*0Sstevel@tonic-gate #include <sys/uio.h>
82*0Sstevel@tonic-gate #include <sys/open.h>
83*0Sstevel@tonic-gate #include <sys/conf.h>
84*0Sstevel@tonic-gate #include <sys/file.h>
85*0Sstevel@tonic-gate #include <sys/cmn_err.h>
86*0Sstevel@tonic-gate #include <sys/debug.h>
87*0Sstevel@tonic-gate #include <sys/kmem.h>
88*0Sstevel@tonic-gate #include <sys/stat.h>
89*0Sstevel@tonic-gate #include <sys/autoconf.h>
90*0Sstevel@tonic-gate 
91*0Sstevel@tonic-gate #include <sys/dklabel.h>
92*0Sstevel@tonic-gate 
93*0Sstevel@tonic-gate #include <sys/vtoc.h>
94*0Sstevel@tonic-gate #include <sys/dkio.h>
95*0Sstevel@tonic-gate #include <sys/fdio.h>
96*0Sstevel@tonic-gate 
97*0Sstevel@tonic-gate #include <sys/ddi.h>
98*0Sstevel@tonic-gate #include <sys/sunddi.h>
99*0Sstevel@tonic-gate #include <sys/kstat.h>
100*0Sstevel@tonic-gate 
101*0Sstevel@tonic-gate /*
102*0Sstevel@tonic-gate  * included to check for ELC or SLC which report floppy controller that
103*0Sstevel@tonic-gate  */
104*0Sstevel@tonic-gate #include <sys/cpu.h>
105*0Sstevel@tonic-gate 
106*0Sstevel@tonic-gate #include "sys/fdvar.h"
107*0Sstevel@tonic-gate #include "sys/fdreg.h"
108*0Sstevel@tonic-gate #include "sys/dma_i8237A.h"
109*0Sstevel@tonic-gate 
110*0Sstevel@tonic-gate /*
111*0Sstevel@tonic-gate  * Defines
112*0Sstevel@tonic-gate  */
113*0Sstevel@tonic-gate #define	KIOSP	KSTAT_IO_PTR(un->un_iostat)
114*0Sstevel@tonic-gate #define	KIOIP	KSTAT_INTR_PTR(fdc->c_intrstat)
115*0Sstevel@tonic-gate #define	MEDIUM_DENSITY	0x40
116*0Sstevel@tonic-gate #define	SEC_SIZE_CODE	(fdctlr.c_csb->csb_unit]->un_chars->medium ? 3 : 2)
117*0Sstevel@tonic-gate #define	CMD_READ	(MT + SK + FDRAW_RDCMD + MFM)
118*0Sstevel@tonic-gate #define	CMD_WRITE	(MT + FDRAW_WRCMD + MFM)
119*0Sstevel@tonic-gate #define	C		CE_CONT
120*0Sstevel@tonic-gate #define	FD_POLLABLE_PROP	"pollable"	/* prom property */
121*0Sstevel@tonic-gate #define	FD_MANUAL_EJECT		"manual"	/* prom property */
122*0Sstevel@tonic-gate #define	FD_UNIT			"unit"		/* prom property */
123*0Sstevel@tonic-gate 
124*0Sstevel@tonic-gate /*
125*0Sstevel@tonic-gate  * Sony MP-F17W-50D Drive Parameters
126*0Sstevel@tonic-gate  *				High Capacity
127*0Sstevel@tonic-gate  *	Capacity unformatted	2Mb
128*0Sstevel@tonic-gate  *	Capacity formatted	1.47Mb
129*0Sstevel@tonic-gate  *	Encoding method	 MFM
130*0Sstevel@tonic-gate  *	Recording density	17434 bpi
131*0Sstevel@tonic-gate  *	Track density		135 tpi
132*0Sstevel@tonic-gate  *	Cylinders		80
133*0Sstevel@tonic-gate  *	Heads			2
134*0Sstevel@tonic-gate  *	Tracks			160
135*0Sstevel@tonic-gate  *	Rotational speed	300 rpm
136*0Sstevel@tonic-gate  *	Transfer rate		250/500 kbps
137*0Sstevel@tonic-gate  *	Latency (average)	100 ms
138*0Sstevel@tonic-gate  *	Access time
139*0Sstevel@tonic-gate  *		Average		95 ms
140*0Sstevel@tonic-gate  *		Track to track	3 ms
141*0Sstevel@tonic-gate  *	Head settling time	15 ms
142*0Sstevel@tonic-gate  *	Motor start time	500 ms
143*0Sstevel@tonic-gate  *	Head load time		? ms
144*0Sstevel@tonic-gate  */
145*0Sstevel@tonic-gate 
146*0Sstevel@tonic-gate /*
147*0Sstevel@tonic-gate  * The max_fd_dma_len is used only when southbridge is present.
148*0Sstevel@tonic-gate  * It has been observed that when IFB tests are run the floppy dma could get
149*0Sstevel@tonic-gate  * starved and result in underrun errors. After experimenting it was found that
150*0Sstevel@tonic-gate  * doing dma in chunks of 2048 works OK.
151*0Sstevel@tonic-gate  * The reason for making this a global variable is that there could be
152*0Sstevel@tonic-gate  * situations under which the customer would like to get full performance
153*0Sstevel@tonic-gate  * from floppy. He may not be having IFB boards that cause underrun errors.
154*0Sstevel@tonic-gate  * Under those conditions we could set this value to a much higher value
155*0Sstevel@tonic-gate  * by editing /etc/system file.
156*0Sstevel@tonic-gate  */
157*0Sstevel@tonic-gate int	max_fd_dma_len = 2048;
158*0Sstevel@tonic-gate 
159*0Sstevel@tonic-gate static void quiesce_fd_interrupt(struct fdctlr *);
160*0Sstevel@tonic-gate 
161*0Sstevel@tonic-gate /*
162*0Sstevel@tonic-gate  * Character/block entry points function prototypes
163*0Sstevel@tonic-gate  */
164*0Sstevel@tonic-gate static int fd_open(dev_t *, int, int, cred_t *);
165*0Sstevel@tonic-gate static int fd_close(dev_t, int, int, cred_t *);
166*0Sstevel@tonic-gate static int fd_strategy(struct buf *);
167*0Sstevel@tonic-gate static int fd_read(dev_t, struct uio *, cred_t *);
168*0Sstevel@tonic-gate static int fd_write(dev_t, struct uio *, cred_t *);
169*0Sstevel@tonic-gate static int fd_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
170*0Sstevel@tonic-gate static int
171*0Sstevel@tonic-gate fd_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *, caddr_t, int *);
172*0Sstevel@tonic-gate 
173*0Sstevel@tonic-gate /*
174*0Sstevel@tonic-gate  * Device operations (dev_ops) entries function prototypes
175*0Sstevel@tonic-gate  */
176*0Sstevel@tonic-gate static int fd_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
177*0Sstevel@tonic-gate 		void **result);
178*0Sstevel@tonic-gate static int fd_attach(dev_info_t *, ddi_attach_cmd_t);
179*0Sstevel@tonic-gate static int fd_detach(dev_info_t *, ddi_detach_cmd_t);
180*0Sstevel@tonic-gate static int fd_power(dev_info_t *dip, int component, int level);
181*0Sstevel@tonic-gate 
182*0Sstevel@tonic-gate /*
183*0Sstevel@tonic-gate  * Internal functions
184*0Sstevel@tonic-gate  */
185*0Sstevel@tonic-gate static int fd_attach_check_drive(struct fdctlr *fdc);
186*0Sstevel@tonic-gate static int fd_attach_det_ctlr(dev_info_t *dip, struct fdctlr *fdc);
187*0Sstevel@tonic-gate static int fd_attach_map_regs(dev_info_t *dip, struct fdctlr *fdc);
188*0Sstevel@tonic-gate static int fd_attach_register_interrupts(dev_info_t *dip, struct fdctlr *fdc,
189*0Sstevel@tonic-gate     int *hard);
190*0Sstevel@tonic-gate static int fd_build_label_vtoc(struct fdunit *, struct vtoc *);
191*0Sstevel@tonic-gate static void fd_build_user_vtoc(struct fdunit *, struct vtoc *);
192*0Sstevel@tonic-gate static int fdcheckdisk(struct fdctlr *fdc, int unit);
193*0Sstevel@tonic-gate static int fd_check_media(dev_t dev, enum dkio_state state);
194*0Sstevel@tonic-gate static void fd_cleanup(dev_info_t *dip, struct fdctlr *fdc, int hard,
195*0Sstevel@tonic-gate     int locks);
196*0Sstevel@tonic-gate static void fdeject(struct fdctlr *, int unit);
197*0Sstevel@tonic-gate static int fdexec(struct fdctlr *fdc, int flags);
198*0Sstevel@tonic-gate static void fdexec_turn_on_motor(struct fdctlr *fdc, int flags, uint_t unit);
199*0Sstevel@tonic-gate static int fdformat(struct fdctlr *fdc, int unit, int cyl, int hd);
200*0Sstevel@tonic-gate static caddr_t fd_getauxiova();
201*0Sstevel@tonic-gate static struct fdctlr *fd_getctlr(dev_t);
202*0Sstevel@tonic-gate static void fdgetcsb(struct fdctlr *);
203*0Sstevel@tonic-gate static int fdgetlabel(struct fdctlr *fdc, int unit);
204*0Sstevel@tonic-gate enum dkio_state fd_get_media_state(struct fdctlr *, int);
205*0Sstevel@tonic-gate static uint_t fdintr_dma();
206*0Sstevel@tonic-gate static int fd_isauxiodip(dev_info_t *);
207*0Sstevel@tonic-gate static uint_t  fd_lointr(caddr_t arg);
208*0Sstevel@tonic-gate static void fd_media_watch(void *);
209*0Sstevel@tonic-gate static void fdmotoff(void *);
210*0Sstevel@tonic-gate static int fd_part_is_open(struct fdunit *un, int part);
211*0Sstevel@tonic-gate static int fdrawioctl(struct fdctlr *, int, intptr_t, int);
212*0Sstevel@tonic-gate static int fdrecalseek(struct fdctlr *fdc, int unit, int arg, int execflg);
213*0Sstevel@tonic-gate static int fdrecover(struct fdctlr *);
214*0Sstevel@tonic-gate static void fdretcsb(struct fdctlr *);
215*0Sstevel@tonic-gate static int fdreset(struct fdctlr *);
216*0Sstevel@tonic-gate static int fdrw(struct fdctlr *fdc, int, int, int, int, int, caddr_t, uint_t);
217*0Sstevel@tonic-gate static void fdselect(struct fdctlr *fdc, int unit, int onoff);
218*0Sstevel@tonic-gate static int fdsensedrv(struct fdctlr *fdc, int unit);
219*0Sstevel@tonic-gate static int fdsense_chng(struct fdctlr *, int unit);
220*0Sstevel@tonic-gate static void fdstart(struct fdctlr *);
221*0Sstevel@tonic-gate static int fdstart_dma(register struct fdctlr *fdc, caddr_t addr, uint_t len);
222*0Sstevel@tonic-gate static int fd_unit_is_open(struct fdunit *);
223*0Sstevel@tonic-gate static void fdunpacklabel(struct packed_label *, struct dk_label *);
224*0Sstevel@tonic-gate static int fd_unbind_handle(struct fdctlr *);
225*0Sstevel@tonic-gate static void fdwatch(void *);
226*0Sstevel@tonic-gate static void set_rotational_speed(struct fdctlr *, int);
227*0Sstevel@tonic-gate static int fd_get_media_info(struct fdunit *un, caddr_t buf, int flag);
228*0Sstevel@tonic-gate static int fd_pm_lower_power(struct fdctlr *fdc);
229*0Sstevel@tonic-gate static int fd_pm_raise_power(struct fdctlr *fdc);
230*0Sstevel@tonic-gate static void create_pm_components(dev_info_t *dip);
231*0Sstevel@tonic-gate static void set_data_count_register(struct fdctlr *fdc, uint32_t count);
232*0Sstevel@tonic-gate static uint32_t get_data_count_register(struct fdctlr *fdc);
233*0Sstevel@tonic-gate static void reset_dma_controller(struct fdctlr *fdc);
234*0Sstevel@tonic-gate static void set_data_address_register(struct fdctlr *fdc, uint32_t address);
235*0Sstevel@tonic-gate static uint32_t get_dma_control_register(struct fdctlr *fdc);
236*0Sstevel@tonic-gate static void set_dma_mode(struct fdctlr *fdc, int val);
237*0Sstevel@tonic-gate static void set_dma_control_register(struct fdctlr *fdc, uint32_t val);
238*0Sstevel@tonic-gate static void release_sb_dma(struct fdctlr *fdc);
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate /*
241*0Sstevel@tonic-gate  * External functions
242*0Sstevel@tonic-gate  */
243*0Sstevel@tonic-gate extern uint_t fd_intr(caddr_t);	/* defined in fd_asm.s */
244*0Sstevel@tonic-gate extern uint_t fd_fastintr(void); /* defined in fd_asm.s */
245*0Sstevel@tonic-gate extern void set_auxioreg();
246*0Sstevel@tonic-gate extern void call_debug();
247*0Sstevel@tonic-gate 
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate /*
251*0Sstevel@tonic-gate  * The following macro checks whether the device in a SUSPENDED state.
252*0Sstevel@tonic-gate  * As per WDD guide lines the I/O requests to a suspended device should
253*0Sstevel@tonic-gate  * be blocked until the device is resumed.
254*0Sstevel@tonic-gate  * Here we cv_wait on c_suspend_cv, and there is a cv_broadcast() in
255*0Sstevel@tonic-gate  * DDI_RESUME to wake up this thread.
256*0Sstevel@tonic-gate  *
257*0Sstevel@tonic-gate  * NOTE: This code is not tested because the kernel threads are suspended
258*0Sstevel@tonic-gate  * before the device is suspended. So there can not be any I/O requests on
259*0Sstevel@tonic-gate  * a suspended device until the cpr implementation changes..
260*0Sstevel@tonic-gate  */
261*0Sstevel@tonic-gate 
262*0Sstevel@tonic-gate #define	CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc) 	\
263*0Sstevel@tonic-gate 		{\
264*0Sstevel@tonic-gate 			while (fdc->c_un->un_state == FD_STATE_SUSPENDED) {\
265*0Sstevel@tonic-gate 				cv_wait(&fdc->c_suspend_cv, \
266*0Sstevel@tonic-gate 							&fdc->c_lolock);\
267*0Sstevel@tonic-gate 			}\
268*0Sstevel@tonic-gate 		}
269*0Sstevel@tonic-gate 
270*0Sstevel@tonic-gate /*
271*0Sstevel@tonic-gate  * bss (uninitialized data)
272*0Sstevel@tonic-gate  */
273*0Sstevel@tonic-gate struct	fdctlr	*fdctlrs;	/* linked list of controllers */
274*0Sstevel@tonic-gate 
275*0Sstevel@tonic-gate /*
276*0Sstevel@tonic-gate  * initialized data
277*0Sstevel@tonic-gate  */
278*0Sstevel@tonic-gate 
279*0Sstevel@tonic-gate static int fd_check_media_time = 5000000;	/* 5 second state check */
280*0Sstevel@tonic-gate static int fd_pollable = 0;
281*0Sstevel@tonic-gate static uchar_t rwretry = 10;
282*0Sstevel@tonic-gate static uchar_t skretry = 5;
283*0Sstevel@tonic-gate /* This variable allows the dynamic change of the burst size */
284*0Sstevel@tonic-gate static int fd_burstsize = DCSR_BURST_0 | DCSR_BURST_1;
285*0Sstevel@tonic-gate 
286*0Sstevel@tonic-gate static struct driver_minor_data {
287*0Sstevel@tonic-gate 	char	*name;
288*0Sstevel@tonic-gate 	int	minor;
289*0Sstevel@tonic-gate 	int	type;
290*0Sstevel@tonic-gate } fd_minor [] = {
291*0Sstevel@tonic-gate 	{ "a", 0, S_IFBLK},
292*0Sstevel@tonic-gate 	{ "b", 1, S_IFBLK},
293*0Sstevel@tonic-gate 	{ "c", 2, S_IFBLK},
294*0Sstevel@tonic-gate 	{ "a,raw", 0, S_IFCHR},
295*0Sstevel@tonic-gate 	{ "b,raw", 1, S_IFCHR},
296*0Sstevel@tonic-gate 	{ "c,raw", 2, S_IFCHR},
297*0Sstevel@tonic-gate 	{0}
298*0Sstevel@tonic-gate };
299*0Sstevel@tonic-gate 
300*0Sstevel@tonic-gate /*
301*0Sstevel@tonic-gate  * If the interrupt handler is invoked and no controllers expect an
302*0Sstevel@tonic-gate  * interrupt, the kernel panics.  The following message is printed out.
303*0Sstevel@tonic-gate  */
304*0Sstevel@tonic-gate char *panic_msg = "fd_intr: unexpected interrupt\n";
305*0Sstevel@tonic-gate 
306*0Sstevel@tonic-gate /*
307*0Sstevel@tonic-gate  * Specify/Configure cmd parameters
308*0Sstevel@tonic-gate  */
309*0Sstevel@tonic-gate static uchar_t fdspec[2] = { 0xc2, 0x33 };	/*  "specify" parameters */
310*0Sstevel@tonic-gate static uchar_t fdconf[3] = { 0x64, 0x58, 0x00 }; /*  "configure" parameters */
311*0Sstevel@tonic-gate 
312*0Sstevel@tonic-gate /* When DMA is used, set the ND bit to 0 */
313*0Sstevel@tonic-gate #define	SPEC_DMA_MODE	0x32
314*0Sstevel@tonic-gate 
315*0Sstevel@tonic-gate /*
316*0Sstevel@tonic-gate  * default characteristics
317*0Sstevel@tonic-gate  */
318*0Sstevel@tonic-gate static struct fd_char fdtypes[] = {
319*0Sstevel@tonic-gate 	{	/* struct fd_char fdchar_1.7MB density */
320*0Sstevel@tonic-gate 		0,		/* medium */
321*0Sstevel@tonic-gate 		500,		/* transfer rate */
322*0Sstevel@tonic-gate 		80,		/* number of cylinders */
323*0Sstevel@tonic-gate 		2,		/* number of heads */
324*0Sstevel@tonic-gate 		512,		/* sector size */
325*0Sstevel@tonic-gate 		21,		/* sectors per track */
326*0Sstevel@tonic-gate 		-1,		/* (NA) # steps per data track */
327*0Sstevel@tonic-gate 	},
328*0Sstevel@tonic-gate 	{	/* struct fd_char fdchar_highdens */
329*0Sstevel@tonic-gate 		0, 		/* medium */
330*0Sstevel@tonic-gate 		500, 		/* transfer rate */
331*0Sstevel@tonic-gate 		80, 		/* number of cylinders */
332*0Sstevel@tonic-gate 		2, 		/* number of heads */
333*0Sstevel@tonic-gate 		512, 		/* sector size */
334*0Sstevel@tonic-gate 		18, 		/* sectors per track */
335*0Sstevel@tonic-gate 		-1, 		/* (NA) # steps per data track */
336*0Sstevel@tonic-gate 	},
337*0Sstevel@tonic-gate 	{	/* struct fd_char fdchar_meddens */
338*0Sstevel@tonic-gate 		1, 		/* medium */
339*0Sstevel@tonic-gate 		500, 		/* transfer rate */
340*0Sstevel@tonic-gate 		77, 		/* number of cylinders */
341*0Sstevel@tonic-gate 		2, 		/* number of heads */
342*0Sstevel@tonic-gate 		1024, 		/* sector size */
343*0Sstevel@tonic-gate 		8, 		/* sectors per track */
344*0Sstevel@tonic-gate 		-1, 		/* (NA) # steps per data track */
345*0Sstevel@tonic-gate 	},
346*0Sstevel@tonic-gate 	{	/* struct fd_char fdchar_lowdens  */
347*0Sstevel@tonic-gate 		0, 		/* medium */
348*0Sstevel@tonic-gate 		250, 		/* transfer rate */
349*0Sstevel@tonic-gate 		80, 		/* number of cylinders */
350*0Sstevel@tonic-gate 		2, 		/* number of heads */
351*0Sstevel@tonic-gate 		512, 		/* sector size */
352*0Sstevel@tonic-gate 		9, 		/* sectors per track */
353*0Sstevel@tonic-gate 		-1, 		/* (NA) # steps per data track */
354*0Sstevel@tonic-gate 	}
355*0Sstevel@tonic-gate };
356*0Sstevel@tonic-gate 
357*0Sstevel@tonic-gate 
358*0Sstevel@tonic-gate static int nfdtypes = sizeof (fdtypes) / sizeof (fdtypes[0]);
359*0Sstevel@tonic-gate 
360*0Sstevel@tonic-gate 
361*0Sstevel@tonic-gate /*
362*0Sstevel@tonic-gate  * Default Label & partition maps
363*0Sstevel@tonic-gate  */
364*0Sstevel@tonic-gate 
365*0Sstevel@tonic-gate static struct packed_label fdlbl_high_21 = {
366*0Sstevel@tonic-gate 	{ "3.5\" floppy cyl 80 alt 0 hd 2 sec 21" },
367*0Sstevel@tonic-gate 	300,				/* rotations per minute */
368*0Sstevel@tonic-gate 	80,				/* # physical cylinders */
369*0Sstevel@tonic-gate 	0,				/* alternates per cylinder */
370*0Sstevel@tonic-gate 	1,				/* interleave factor */
371*0Sstevel@tonic-gate 	80,				/* # of data cylinders */
372*0Sstevel@tonic-gate 	0,				/* # of alternate cylinders */
373*0Sstevel@tonic-gate 	2,				/* # of heads in this partition */
374*0Sstevel@tonic-gate 	21,				/* # of 512 byte sectors per track */
375*0Sstevel@tonic-gate 	{
376*0Sstevel@tonic-gate 		{ 0, 79 * 2 * 21 },	/* part 0 - all but last cyl */
377*0Sstevel@tonic-gate 		{ 79, 1 * 2 * 21 },	/* part 1 - just the last cyl */
378*0Sstevel@tonic-gate 		{ 0, 80 * 2 * 21 },	/* part 2 - "the whole thing" */
379*0Sstevel@tonic-gate 	},
380*0Sstevel@tonic-gate 	{	0,			/* version */
381*0Sstevel@tonic-gate 		"",			/* volume label */
382*0Sstevel@tonic-gate 		3,			/* no. of partitions */
383*0Sstevel@tonic-gate 		{ 0 },			/* partition hdrs, sec 2 */
384*0Sstevel@tonic-gate 		{ 0 },			/* mboot info.  unsupported */
385*0Sstevel@tonic-gate 		VTOC_SANE,		/* verify vtoc sanity */
386*0Sstevel@tonic-gate 		{ 0 },			/* reserved space */
387*0Sstevel@tonic-gate 		0,			/* timestamp */
388*0Sstevel@tonic-gate 	},
389*0Sstevel@tonic-gate };
390*0Sstevel@tonic-gate 
391*0Sstevel@tonic-gate static struct packed_label fdlbl_high_80 = {
392*0Sstevel@tonic-gate 	{ "3.5\" floppy cyl 80 alt 0 hd 2 sec 18" },
393*0Sstevel@tonic-gate 	300, 				/* rotations per minute */
394*0Sstevel@tonic-gate 	80, 				/* # physical cylinders */
395*0Sstevel@tonic-gate 	0, 				/* alternates per cylinder */
396*0Sstevel@tonic-gate 	1, 				/* interleave factor */
397*0Sstevel@tonic-gate 	80, 				/* # of data cylinders */
398*0Sstevel@tonic-gate 	0, 				/* # of alternate cylinders */
399*0Sstevel@tonic-gate 	2, 				/* # of heads in this partition */
400*0Sstevel@tonic-gate 	18, 				/* # of 512 byte sectors per track */
401*0Sstevel@tonic-gate 	{
402*0Sstevel@tonic-gate 		{ 0, 79 * 2 * 18 }, 	/* part 0 - all but last cyl */
403*0Sstevel@tonic-gate 		{ 79, 1 * 2 * 18 }, 	/* part 1 - just the last cyl */
404*0Sstevel@tonic-gate 		{ 0, 80 * 2 * 18 }, 	/* part 2 - "the whole thing" */
405*0Sstevel@tonic-gate 	},
406*0Sstevel@tonic-gate 	{	0,			/* version */
407*0Sstevel@tonic-gate 		"",			/* volume label */
408*0Sstevel@tonic-gate 		3,			/* no. of partitions */
409*0Sstevel@tonic-gate 		{ 0 },			/* partition hdrs, sec 2 */
410*0Sstevel@tonic-gate 		{ 0 },			/* mboot info.  unsupported */
411*0Sstevel@tonic-gate 		VTOC_SANE,		/* verify vtoc sanity */
412*0Sstevel@tonic-gate 		{ 0 },			/* reserved space */
413*0Sstevel@tonic-gate 		0,			/* timestamp */
414*0Sstevel@tonic-gate 	},
415*0Sstevel@tonic-gate };
416*0Sstevel@tonic-gate 
417*0Sstevel@tonic-gate /*
418*0Sstevel@tonic-gate  * A medium density diskette has 1024 byte sectors.  The dk_label structure
419*0Sstevel@tonic-gate  * assumes a sector is DEVBSIZE (512) bytes.
420*0Sstevel@tonic-gate  */
421*0Sstevel@tonic-gate static struct packed_label fdlbl_medium_80 = {
422*0Sstevel@tonic-gate 	{ "3.5\" floppy cyl 77 alt 0 hd 2 sec 8" },
423*0Sstevel@tonic-gate 	360, 				/* rotations per minute */
424*0Sstevel@tonic-gate 	77, 				/* # physical cylinders */
425*0Sstevel@tonic-gate 	0, 				/* alternates per cylinder */
426*0Sstevel@tonic-gate 	1, 				/* interleave factor */
427*0Sstevel@tonic-gate 	77, 				/* # of data cylinders */
428*0Sstevel@tonic-gate 	0, 				/* # of alternate cylinders */
429*0Sstevel@tonic-gate 	2, 				/* # of heads in this partition */
430*0Sstevel@tonic-gate 	16, 				/* # of 512 byte sectors per track */
431*0Sstevel@tonic-gate 	{
432*0Sstevel@tonic-gate 		{ 0, 76 * 2 * 8 * 2 },  /* part 0 - all but last cyl */
433*0Sstevel@tonic-gate 		{ 76, 1 * 2 * 8 * 2 },  /* part 1 - just the last cyl */
434*0Sstevel@tonic-gate 		{ 0, 77 * 2 * 8 * 2 },  /* part 2 - "the whole thing" */
435*0Sstevel@tonic-gate 	},
436*0Sstevel@tonic-gate 	{	0,			/* version */
437*0Sstevel@tonic-gate 		"",			/* volume label */
438*0Sstevel@tonic-gate 		3,			/* no. of partitions */
439*0Sstevel@tonic-gate 		{ 0 },			/* partition hdrs, sec 2 */
440*0Sstevel@tonic-gate 		{ 0 },			/* mboot info.  unsupported */
441*0Sstevel@tonic-gate 		VTOC_SANE,		/* verify vtoc sanity */
442*0Sstevel@tonic-gate 		{ 0 },			/* reserved space */
443*0Sstevel@tonic-gate 		0,			/* timestamp */
444*0Sstevel@tonic-gate 	},
445*0Sstevel@tonic-gate };
446*0Sstevel@tonic-gate 
447*0Sstevel@tonic-gate static struct packed_label fdlbl_low_80 = {
448*0Sstevel@tonic-gate 	{ "3.5\" floppy cyl 80 alt 0 hd 2 sec 9" },
449*0Sstevel@tonic-gate 	300, 				/* rotations per minute */
450*0Sstevel@tonic-gate 	80, 				/* # physical cylinders */
451*0Sstevel@tonic-gate 	0, 				/* alternates per cylinder */
452*0Sstevel@tonic-gate 	1, 				/* interleave factor */
453*0Sstevel@tonic-gate 	80, 				/* # of data cylinders */
454*0Sstevel@tonic-gate 	0, 				/* # of alternate cylinders */
455*0Sstevel@tonic-gate 	2, 				/* # of heads in this partition */
456*0Sstevel@tonic-gate 	9, 				/* # of 512 byte sectors per track */
457*0Sstevel@tonic-gate 	{
458*0Sstevel@tonic-gate 		{ 0, 79 * 2 * 9 }, 	/* part 0 - all but last cyl */
459*0Sstevel@tonic-gate 		{ 79, 1 * 2 * 9 }, 	/* part 1 - just the last cyl */
460*0Sstevel@tonic-gate 		{ 0, 80 * 2 * 9 }, 	/* part 2 - "the whole thing" */
461*0Sstevel@tonic-gate 	},
462*0Sstevel@tonic-gate 	{	0,			/* version */
463*0Sstevel@tonic-gate 		"",			/* volume label */
464*0Sstevel@tonic-gate 		3,			/* no. of partitions */
465*0Sstevel@tonic-gate 		{ 0 },			/* partition hdrs, sec 2 */
466*0Sstevel@tonic-gate 		{ 0 },			/* mboot info.  unsupported */
467*0Sstevel@tonic-gate 		VTOC_SANE,		/* verify vtoc sanity */
468*0Sstevel@tonic-gate 		{ 0 },			/* reserved space */
469*0Sstevel@tonic-gate 		0,			/* timestamp */
470*0Sstevel@tonic-gate 	},
471*0Sstevel@tonic-gate };
472*0Sstevel@tonic-gate 
473*0Sstevel@tonic-gate static struct fdcmdinfo {
474*0Sstevel@tonic-gate 	char *cmdname;		/* command name */
475*0Sstevel@tonic-gate 	uchar_t ncmdbytes;	/* number of bytes of command */
476*0Sstevel@tonic-gate 	uchar_t nrsltbytes;	/* number of bytes in result */
477*0Sstevel@tonic-gate 	uchar_t cmdtype;		/* characteristics */
478*0Sstevel@tonic-gate } fdcmds[] = {
479*0Sstevel@tonic-gate 	"", 0, 0, 0, 			/* - */
480*0Sstevel@tonic-gate 	"", 0, 0, 0, 			/* - */
481*0Sstevel@tonic-gate 	"read_track", 9, 7, 1, 		/* 2 */
482*0Sstevel@tonic-gate 	"specify", 3, 0, 3, 		/* 3 */
483*0Sstevel@tonic-gate 	"sense_drv_status", 2, 1, 3, 	/* 4 */
484*0Sstevel@tonic-gate 	"write", 9, 7, 1, 		/* 5 */
485*0Sstevel@tonic-gate 	"read", 9, 7, 1, 		/* 6 */
486*0Sstevel@tonic-gate 	"recalibrate", 2, 0, 2, 		/* 7 */
487*0Sstevel@tonic-gate 	"sense_int_status", 1, 2, 3, 	/* 8 */
488*0Sstevel@tonic-gate 	"write_del", 9, 7, 1, 		/* 9 */
489*0Sstevel@tonic-gate 	"read_id", 2, 7, 2, 		/* A */
490*0Sstevel@tonic-gate 	"motor_on/off", 1, 0, 4, 	/* B */
491*0Sstevel@tonic-gate 	"read_del", 9, 7, 1, 		/* C */
492*0Sstevel@tonic-gate 	"format_track", 10, 7, 1, 	/* D */
493*0Sstevel@tonic-gate 	"dump_reg", 1, 10, 4, 		/* E */
494*0Sstevel@tonic-gate 	"seek", 3, 0, 2, 		/* F */
495*0Sstevel@tonic-gate 	"", 0, 0, 0, 			/* - */
496*0Sstevel@tonic-gate 	"", 0, 0, 0, 			/* - */
497*0Sstevel@tonic-gate 	"", 0, 0, 0, 			/* - */
498*0Sstevel@tonic-gate 	"configure", 4, 0, 4, 		/* 13 */
499*0Sstevel@tonic-gate 	/* relative seek */
500*0Sstevel@tonic-gate };
501*0Sstevel@tonic-gate 
502*0Sstevel@tonic-gate static struct cb_ops fd_cb_ops = {
503*0Sstevel@tonic-gate 	fd_open, 		/* open */
504*0Sstevel@tonic-gate 	fd_close, 		/* close */
505*0Sstevel@tonic-gate 	fd_strategy, 		/* strategy */
506*0Sstevel@tonic-gate 	nodev, 			/* print */
507*0Sstevel@tonic-gate 	nodev, 			/* dump */
508*0Sstevel@tonic-gate 	fd_read, 		/* read */
509*0Sstevel@tonic-gate 	fd_write, 		/* write */
510*0Sstevel@tonic-gate 	fd_ioctl, 		/* ioctl */
511*0Sstevel@tonic-gate 	nodev, 			/* devmap */
512*0Sstevel@tonic-gate 	nodev, 			/* mmap */
513*0Sstevel@tonic-gate 	nodev, 			/* segmap */
514*0Sstevel@tonic-gate 	nochpoll, 		/* poll */
515*0Sstevel@tonic-gate 	fd_prop_op, 		/* cb_prop_op */
516*0Sstevel@tonic-gate 	0, 			/* streamtab  */
517*0Sstevel@tonic-gate 	D_NEW | D_MP		/* Driver compatibility flag */
518*0Sstevel@tonic-gate };
519*0Sstevel@tonic-gate 
520*0Sstevel@tonic-gate static struct dev_ops	fd_ops = {
521*0Sstevel@tonic-gate 	DEVO_REV, 		/* devo_rev, */
522*0Sstevel@tonic-gate 	0, 			/* refcnt  */
523*0Sstevel@tonic-gate 	fd_info, 		/* info */
524*0Sstevel@tonic-gate 	nulldev, 		/* identify */
525*0Sstevel@tonic-gate 	nulldev, 		/* probe */
526*0Sstevel@tonic-gate 	fd_attach, 		/* attach */
527*0Sstevel@tonic-gate 	fd_detach, 		/* detach */
528*0Sstevel@tonic-gate 	nodev, 			/* reset */
529*0Sstevel@tonic-gate 	&fd_cb_ops, 		/* driver operations */
530*0Sstevel@tonic-gate 	(struct bus_ops *)0,	/* bus operations */
531*0Sstevel@tonic-gate 	fd_power		/* power */
532*0Sstevel@tonic-gate };
533*0Sstevel@tonic-gate 
534*0Sstevel@tonic-gate 
535*0Sstevel@tonic-gate /*
536*0Sstevel@tonic-gate  * error handling
537*0Sstevel@tonic-gate  *
538*0Sstevel@tonic-gate  * for debugging, set rwretry and skretry = 1
539*0Sstevel@tonic-gate  *		set fderrlevel to 1
540*0Sstevel@tonic-gate  *		set fderrmask  to 224  or 100644
541*0Sstevel@tonic-gate  *
542*0Sstevel@tonic-gate  * after debug set rwretry to 10, skretry to 5, and fderrlevel to 3
543*0Sstevel@tonic-gate  * set fderrmask to FDEM_ALL
544*0Sstevel@tonic-gate  * remove the define FD_DEBUG
545*0Sstevel@tonic-gate  *
546*0Sstevel@tonic-gate  */
547*0Sstevel@tonic-gate 
548*0Sstevel@tonic-gate static unsigned int fderrmask = (unsigned int)FDEM_ALL;
549*0Sstevel@tonic-gate static int fderrlevel = 3;
550*0Sstevel@tonic-gate 
551*0Sstevel@tonic-gate static int tosec = 16;  /* long timeouts for sundiag for now */
552*0Sstevel@tonic-gate 
553*0Sstevel@tonic-gate /*
554*0Sstevel@tonic-gate  * loadable module support
555*0Sstevel@tonic-gate  */
556*0Sstevel@tonic-gate 
557*0Sstevel@tonic-gate #include <sys/modctl.h>
558*0Sstevel@tonic-gate 
559*0Sstevel@tonic-gate extern struct mod_ops mod_driverops;
560*0Sstevel@tonic-gate static struct modldrv modldrv = {
561*0Sstevel@tonic-gate 	&mod_driverops, 		/* Type of module. driver here */
562*0Sstevel@tonic-gate 	"Floppy Driver v%I%", 	/* Name of the module. */
563*0Sstevel@tonic-gate 	&fd_ops, 		/* Driver ops vector */
564*0Sstevel@tonic-gate };
565*0Sstevel@tonic-gate 
566*0Sstevel@tonic-gate static struct modlinkage modlinkage = {
567*0Sstevel@tonic-gate 	MODREV_1,
568*0Sstevel@tonic-gate 	&modldrv,
569*0Sstevel@tonic-gate 	NULL
570*0Sstevel@tonic-gate };
571*0Sstevel@tonic-gate 
572*0Sstevel@tonic-gate int
573*0Sstevel@tonic-gate _init(void)
574*0Sstevel@tonic-gate {
575*0Sstevel@tonic-gate 	return (mod_install(&modlinkage));
576*0Sstevel@tonic-gate }
577*0Sstevel@tonic-gate 
578*0Sstevel@tonic-gate int
579*0Sstevel@tonic-gate _info(struct modinfo *modinfop)
580*0Sstevel@tonic-gate {
581*0Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
582*0Sstevel@tonic-gate }
583*0Sstevel@tonic-gate 
584*0Sstevel@tonic-gate int
585*0Sstevel@tonic-gate _fini(void)
586*0Sstevel@tonic-gate {
587*0Sstevel@tonic-gate 	int e;
588*0Sstevel@tonic-gate 
589*0Sstevel@tonic-gate 	if ((e = mod_remove(&modlinkage)) != 0)
590*0Sstevel@tonic-gate 		return (e);
591*0Sstevel@tonic-gate 
592*0Sstevel@tonic-gate 	/* ddi_soft_state_fini() */
593*0Sstevel@tonic-gate 	return (0);
594*0Sstevel@tonic-gate }
595*0Sstevel@tonic-gate 
596*0Sstevel@tonic-gate /* ARGSUSED */
597*0Sstevel@tonic-gate static int
598*0Sstevel@tonic-gate fd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
599*0Sstevel@tonic-gate {
600*0Sstevel@tonic-gate 	struct 			fdctlr *fdc;
601*0Sstevel@tonic-gate 	struct 			driver_minor_data *dmdp;
602*0Sstevel@tonic-gate 	int			instance = ddi_get_instance(dip);
603*0Sstevel@tonic-gate 	int			hard_intr_set = 0;
604*0Sstevel@tonic-gate 
605*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_ATTA, (C, "fd_attach: start\n"));
606*0Sstevel@tonic-gate 
607*0Sstevel@tonic-gate 	switch (cmd) {
608*0Sstevel@tonic-gate 		case DDI_ATTACH:
609*0Sstevel@tonic-gate 			break;
610*0Sstevel@tonic-gate 		case DDI_RESUME:
611*0Sstevel@tonic-gate 
612*0Sstevel@tonic-gate 			if (!(fdc = fd_getctlr(instance << FDINSTSHIFT))) {
613*0Sstevel@tonic-gate 				return (DDI_FAILURE);
614*0Sstevel@tonic-gate 			}
615*0Sstevel@tonic-gate 			quiesce_fd_interrupt(fdc);
616*0Sstevel@tonic-gate 			if (fdc->c_fdtype & FDCTYPE_SB)
617*0Sstevel@tonic-gate 			    if (ddi_add_intr(dip, 0, &fdc->c_block, 0,
618*0Sstevel@tonic-gate 				fdintr_dma, (caddr_t)0) != DDI_SUCCESS) {
619*0Sstevel@tonic-gate 				return (DDI_FAILURE);
620*0Sstevel@tonic-gate 			}
621*0Sstevel@tonic-gate 
622*0Sstevel@tonic-gate 			(void) pm_raise_power(dip, 0, PM_LEVEL_ON);
623*0Sstevel@tonic-gate 			mutex_enter(&fdc->c_lolock);
624*0Sstevel@tonic-gate 			/*
625*0Sstevel@tonic-gate 			 * Wake up any thread blocked due to I/O requests
626*0Sstevel@tonic-gate 			 * while the device was suspended.
627*0Sstevel@tonic-gate 			 */
628*0Sstevel@tonic-gate 			cv_broadcast(&fdc->c_suspend_cv);
629*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_lolock);
630*0Sstevel@tonic-gate 			return (DDI_SUCCESS);
631*0Sstevel@tonic-gate 
632*0Sstevel@tonic-gate 		default:
633*0Sstevel@tonic-gate 			return (DDI_FAILURE);
634*0Sstevel@tonic-gate 	}
635*0Sstevel@tonic-gate 
636*0Sstevel@tonic-gate 
637*0Sstevel@tonic-gate 	/*
638*0Sstevel@tonic-gate 	 * Check for the pollable property
639*0Sstevel@tonic-gate 	 * A pollable floppy drive currently only exists on the
640*0Sstevel@tonic-gate 	 * Sparcstation Voyager.  This drive does not need to
641*0Sstevel@tonic-gate 	 * be turned on in order to sense whether or not a diskette
642*0Sstevel@tonic-gate 	 * is present.
643*0Sstevel@tonic-gate 	 */
644*0Sstevel@tonic-gate 	if (ddi_getprop(DDI_DEV_T_ANY, dip,
645*0Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, FD_POLLABLE_PROP, 0))
646*0Sstevel@tonic-gate 		fd_pollable = 1;
647*0Sstevel@tonic-gate 
648*0Sstevel@tonic-gate 	fdc = kmem_zalloc(sizeof (*fdc), KM_SLEEP);
649*0Sstevel@tonic-gate 	fdc->c_dip = dip;
650*0Sstevel@tonic-gate 
651*0Sstevel@tonic-gate 
652*0Sstevel@tonic-gate 	fdc->c_next = fdctlrs;
653*0Sstevel@tonic-gate 	fdctlrs = fdc;
654*0Sstevel@tonic-gate 
655*0Sstevel@tonic-gate 	/* Determine which type of controller is present and initialize it */
656*0Sstevel@tonic-gate 	if (fd_attach_det_ctlr(dip, fdc) == DDI_FAILURE) {
657*0Sstevel@tonic-gate 		fd_cleanup(dip, fdc, hard_intr_set, 0);
658*0Sstevel@tonic-gate 		return (DDI_FAILURE);
659*0Sstevel@tonic-gate 	}
660*0Sstevel@tonic-gate 	/* Finish mapping the device registers & setting up structures */
661*0Sstevel@tonic-gate 	if (fd_attach_map_regs(dip, fdc) == DDI_FAILURE) {
662*0Sstevel@tonic-gate 		fd_cleanup(dip, fdc, hard_intr_set, 0);
663*0Sstevel@tonic-gate 		return (DDI_FAILURE);
664*0Sstevel@tonic-gate 	}
665*0Sstevel@tonic-gate 
666*0Sstevel@tonic-gate 	/*
667*0Sstevel@tonic-gate 	 * Initialize the DMA limit structures if it's being used.
668*0Sstevel@tonic-gate 	 */
669*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_DMA) {
670*0Sstevel@tonic-gate 		fdc->c_fd_dma_lim.dma_attr_version = DMA_ATTR_V0;
671*0Sstevel@tonic-gate 		fdc->c_fd_dma_lim.dma_attr_addr_lo = 0x00000000ull;
672*0Sstevel@tonic-gate 		fdc->c_fd_dma_lim.dma_attr_addr_hi = 0xfffffffeull;
673*0Sstevel@tonic-gate 		fdc->c_fd_dma_lim.dma_attr_count_max = 0xffffff;
674*0Sstevel@tonic-gate 		if (fdc->c_fdtype & FDCTYPE_SB) {
675*0Sstevel@tonic-gate 			fdc->c_fd_dma_lim.dma_attr_align = FD_SB_DMA_ALIGN;
676*0Sstevel@tonic-gate 		} else {
677*0Sstevel@tonic-gate 			fdc->c_fd_dma_lim.dma_attr_align = 1;
678*0Sstevel@tonic-gate 		}
679*0Sstevel@tonic-gate 		fdc->c_fd_dma_lim.dma_attr_burstsizes = 0x0;
680*0Sstevel@tonic-gate 		fdc->c_fd_dma_lim.dma_attr_minxfer = 1;
681*0Sstevel@tonic-gate 		fdc->c_fd_dma_lim.dma_attr_maxxfer = 0xffff;
682*0Sstevel@tonic-gate 		fdc->c_fd_dma_lim.dma_attr_seg = 0xffff;
683*0Sstevel@tonic-gate 		fdc->c_fd_dma_lim.dma_attr_sgllen = 1;
684*0Sstevel@tonic-gate 		fdc->c_fd_dma_lim.dma_attr_granular = 512;
685*0Sstevel@tonic-gate 
686*0Sstevel@tonic-gate 		if (ddi_dma_alloc_handle(dip, &fdc->c_fd_dma_lim,
687*0Sstevel@tonic-gate 		    DDI_DMA_DONTWAIT, 0, &fdc->c_dmahandle) != DDI_SUCCESS) {
688*0Sstevel@tonic-gate 			fd_cleanup(dip, fdc, hard_intr_set, 0);
689*0Sstevel@tonic-gate 			return (DDI_FAILURE);
690*0Sstevel@tonic-gate 		}
691*0Sstevel@tonic-gate 
692*0Sstevel@tonic-gate 		if (fdc->c_fdtype & FDCTYPE_SB) {
693*0Sstevel@tonic-gate 			ddi_device_acc_attr_t dev_attr;
694*0Sstevel@tonic-gate 			size_t	rlen;
695*0Sstevel@tonic-gate 
696*0Sstevel@tonic-gate 			dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
697*0Sstevel@tonic-gate 			dev_attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
698*0Sstevel@tonic-gate 			dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
699*0Sstevel@tonic-gate 
700*0Sstevel@tonic-gate 			if (ddi_dma_mem_alloc(fdc->c_dmahandle,
701*0Sstevel@tonic-gate 			    (size_t)(32*1024), &dev_attr, DDI_DMA_CONSISTENT,
702*0Sstevel@tonic-gate 			    DDI_DMA_SLEEP, NULL, (caddr_t *)&fdc->dma_buf,
703*0Sstevel@tonic-gate 			    &rlen, &fdc->c_dma_buf_handle) != DDI_SUCCESS) {
704*0Sstevel@tonic-gate 			    fd_cleanup(dip, fdc, hard_intr_set, 0);
705*0Sstevel@tonic-gate 				return (DDI_FAILURE);
706*0Sstevel@tonic-gate 			}
707*0Sstevel@tonic-gate 
708*0Sstevel@tonic-gate 		}
709*0Sstevel@tonic-gate 	}
710*0Sstevel@tonic-gate 
711*0Sstevel@tonic-gate 
712*0Sstevel@tonic-gate 	/* Register the interrupts */
713*0Sstevel@tonic-gate 	if (fd_attach_register_interrupts(dip, fdc,
714*0Sstevel@tonic-gate 	    &hard_intr_set) == DDI_FAILURE) {
715*0Sstevel@tonic-gate 		fd_cleanup(dip, fdc, hard_intr_set, 0);
716*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA,
717*0Sstevel@tonic-gate 		    (C, "fd_attach: registering interrupts failed\n"));
718*0Sstevel@tonic-gate 		return (DDI_FAILURE);
719*0Sstevel@tonic-gate 	}
720*0Sstevel@tonic-gate 
721*0Sstevel@tonic-gate 
722*0Sstevel@tonic-gate 	/*
723*0Sstevel@tonic-gate 	 * set initial controller/drive/disk "characteristics/geometry"
724*0Sstevel@tonic-gate 	 *
725*0Sstevel@tonic-gate 	 * NOTE:  The driver only supports one floppy drive.  The hardware
726*0Sstevel@tonic-gate 	 * only supports one drive because there is only one auxio register
727*0Sstevel@tonic-gate 	 * for one drive.
728*0Sstevel@tonic-gate 	 */
729*0Sstevel@tonic-gate 	fdc->c_un = kmem_zalloc(sizeof (struct fdunit), KM_SLEEP);
730*0Sstevel@tonic-gate 	fdc->c_un->un_chars = kmem_alloc(sizeof (struct fd_char), KM_SLEEP);
731*0Sstevel@tonic-gate 	fdc->c_un->un_iostat = kstat_create("fd", 0, "fd0", "disk",
732*0Sstevel@tonic-gate 	    KSTAT_TYPE_IO, 1, KSTAT_FLAG_PERSISTENT);
733*0Sstevel@tonic-gate 	if (fdc->c_un->un_iostat) {
734*0Sstevel@tonic-gate 		fdc->c_un->un_iostat->ks_lock = &fdc->c_lolock;
735*0Sstevel@tonic-gate 		kstat_install(fdc->c_un->un_iostat);
736*0Sstevel@tonic-gate 	}
737*0Sstevel@tonic-gate 
738*0Sstevel@tonic-gate 	fdc->c_un->un_drive = kmem_zalloc(sizeof (struct fd_drive), KM_SLEEP);
739*0Sstevel@tonic-gate 
740*0Sstevel@tonic-gate 	/* check for the manual eject property */
741*0Sstevel@tonic-gate 	if (ddi_getprop(DDI_DEV_T_ANY, dip,
742*0Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, FD_MANUAL_EJECT, 0)) {
743*0Sstevel@tonic-gate 		fdc->c_un->un_drive->fdd_ejectable = 0;
744*0Sstevel@tonic-gate 	} else {
745*0Sstevel@tonic-gate 		/* an absence of the property indicates auto eject */
746*0Sstevel@tonic-gate 		fdc->c_un->un_drive->fdd_ejectable = -1;
747*0Sstevel@tonic-gate 	}
748*0Sstevel@tonic-gate 
749*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_ATTA, (C, "fd_attach: ejectable? %d\n",
750*0Sstevel@tonic-gate 	    fdc->c_un->un_drive->fdd_ejectable));
751*0Sstevel@tonic-gate 
752*0Sstevel@tonic-gate 	/*
753*0Sstevel@tonic-gate 	 * Check for the drive id.  If the drive id property doesn't exist
754*0Sstevel@tonic-gate 	 * then the drive id is set to 0
755*0Sstevel@tonic-gate 	 */
756*0Sstevel@tonic-gate 	fdc->c_un->un_unit_no = ddi_getprop(DDI_DEV_T_ANY, dip,
757*0Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, FD_UNIT, 0);
758*0Sstevel@tonic-gate 
759*0Sstevel@tonic-gate 
760*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_SB) {
761*0Sstevel@tonic-gate 		fdc->sb_dma_channel = ddi_getprop(DDI_DEV_T_ANY, dip,
762*0Sstevel@tonic-gate 			DDI_PROP_DONTPASS, "dma-channel", 0);
763*0Sstevel@tonic-gate 	}
764*0Sstevel@tonic-gate 
765*0Sstevel@tonic-gate 
766*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_ATTA, (C, "fd_attach: unit %d\n",
767*0Sstevel@tonic-gate 	    fdc->c_un->un_unit_no));
768*0Sstevel@tonic-gate 
769*0Sstevel@tonic-gate 	/* Initially set the characteristics to high density */
770*0Sstevel@tonic-gate 	fdc->c_un->un_curfdtype = 1;
771*0Sstevel@tonic-gate 	*fdc->c_un->un_chars = fdtypes[fdc->c_un->un_curfdtype];
772*0Sstevel@tonic-gate 	fdunpacklabel(&fdlbl_high_80, &fdc->c_un->un_label);
773*0Sstevel@tonic-gate 
774*0Sstevel@tonic-gate 	/* Make sure drive is present */
775*0Sstevel@tonic-gate 	if (fd_attach_check_drive(fdc) == DDI_FAILURE) {
776*0Sstevel@tonic-gate 		fd_cleanup(dip, fdc, hard_intr_set, 1);
777*0Sstevel@tonic-gate 		return (DDI_FAILURE);
778*0Sstevel@tonic-gate 	}
779*0Sstevel@tonic-gate 
780*0Sstevel@tonic-gate 	for (dmdp = fd_minor; dmdp->name != NULL; dmdp++) {
781*0Sstevel@tonic-gate 		if (ddi_create_minor_node(dip, dmdp->name, dmdp->type,
782*0Sstevel@tonic-gate 		(instance << FDINSTSHIFT) | dmdp->minor,
783*0Sstevel@tonic-gate 		DDI_NT_FD, 0) == DDI_FAILURE) {
784*0Sstevel@tonic-gate 			fd_cleanup(dip, fdc, hard_intr_set, 1);
785*0Sstevel@tonic-gate 			return (DDI_FAILURE);
786*0Sstevel@tonic-gate 		}
787*0Sstevel@tonic-gate 	}
788*0Sstevel@tonic-gate 
789*0Sstevel@tonic-gate 	create_pm_components(dip);
790*0Sstevel@tonic-gate 
791*0Sstevel@tonic-gate 	/*
792*0Sstevel@tonic-gate 	 * Add a zero-length attribute to tell the world we support
793*0Sstevel@tonic-gate 	 * kernel ioctls (for layered drivers)
794*0Sstevel@tonic-gate 	 */
795*0Sstevel@tonic-gate 	(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
796*0Sstevel@tonic-gate 	    DDI_KERNEL_IOCTL, NULL, 0);
797*0Sstevel@tonic-gate 
798*0Sstevel@tonic-gate 	ddi_report_dev(dip);
799*0Sstevel@tonic-gate 
800*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_ATTA,
801*0Sstevel@tonic-gate 	    (C, "attached 0x%x\n", ddi_get_instance(dip)));
802*0Sstevel@tonic-gate 
803*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
804*0Sstevel@tonic-gate }
805*0Sstevel@tonic-gate 
806*0Sstevel@tonic-gate /*
807*0Sstevel@tonic-gate  * Finish mapping the registers and initializing structures
808*0Sstevel@tonic-gate  */
809*0Sstevel@tonic-gate static int
810*0Sstevel@tonic-gate fd_attach_map_regs(dev_info_t *dip, struct fdctlr *fdc)
811*0Sstevel@tonic-gate {
812*0Sstevel@tonic-gate 	ddi_device_acc_attr_t attr;
813*0Sstevel@tonic-gate 
814*0Sstevel@tonic-gate 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
815*0Sstevel@tonic-gate 	attr.devacc_attr_endian_flags  = DDI_STRUCTURE_LE_ACC;
816*0Sstevel@tonic-gate 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
817*0Sstevel@tonic-gate 
818*0Sstevel@tonic-gate 	/* Map the DMA registers of the platform supports DMA */
819*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_SB) {
820*0Sstevel@tonic-gate 		if (ddi_regs_map_setup(dip, 1, (caddr_t *)&fdc->c_dma_regs,
821*0Sstevel@tonic-gate 			0, sizeof (struct sb_dma_reg), &attr,
822*0Sstevel@tonic-gate 			&fdc->c_handlep_dma)) {
823*0Sstevel@tonic-gate 			return (DDI_FAILURE);
824*0Sstevel@tonic-gate 		}
825*0Sstevel@tonic-gate 
826*0Sstevel@tonic-gate 
827*0Sstevel@tonic-gate 	} else if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
828*0Sstevel@tonic-gate 		if (ddi_regs_map_setup(dip, 1, (caddr_t *)&fdc->c_dma_regs,
829*0Sstevel@tonic-gate 			0, sizeof (struct cheerio_dma_reg), &attr,
830*0Sstevel@tonic-gate 			&fdc->c_handlep_dma)) {
831*0Sstevel@tonic-gate 			return (DDI_FAILURE);
832*0Sstevel@tonic-gate 		}
833*0Sstevel@tonic-gate 	}
834*0Sstevel@tonic-gate 
835*0Sstevel@tonic-gate 	/* Reset the DMA engine and enable floppy interrupts */
836*0Sstevel@tonic-gate 	reset_dma_controller(fdc);
837*0Sstevel@tonic-gate 	set_dma_control_register(fdc, DCSR_INIT_BITS);
838*0Sstevel@tonic-gate 
839*0Sstevel@tonic-gate 	/* Finish initializing structures associated with the device regs */
840*0Sstevel@tonic-gate 	switch (fdc->c_fdtype & FDCTYPE_CTRLMASK) {
841*0Sstevel@tonic-gate 	case FDCTYPE_82077:
842*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA, (C, "type is 82077\n"));
843*0Sstevel@tonic-gate 		/*
844*0Sstevel@tonic-gate 		 * Initialize addrs of key registers
845*0Sstevel@tonic-gate 		 */
846*0Sstevel@tonic-gate 		fdc->c_control =
847*0Sstevel@tonic-gate 		    (uchar_t *)&fdc->c_reg->fdc_82077_reg.fdc_control;
848*0Sstevel@tonic-gate 		fdc->c_fifo = (uchar_t *)&fdc->c_reg->fdc_82077_reg.fdc_fifo;
849*0Sstevel@tonic-gate 		fdc->c_dor = (uchar_t *)&fdc->c_reg->fdc_82077_reg.fdc_dor;
850*0Sstevel@tonic-gate 		fdc->c_dir = (uchar_t *)&fdc->c_reg->fdc_82077_reg.fdc_dir;
851*0Sstevel@tonic-gate 
852*0Sstevel@tonic-gate 
853*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA, ((int)C,
854*0Sstevel@tonic-gate 			(char *)"fdattach: msr/dsr at %p\n",
855*0Sstevel@tonic-gate 			(void *)fdc->c_control));
856*0Sstevel@tonic-gate 
857*0Sstevel@tonic-gate 		/*
858*0Sstevel@tonic-gate 		 * The 82077 doesn't use the first configuration parameter
859*0Sstevel@tonic-gate 		 * so let's adjust that while we know we're an 82077.
860*0Sstevel@tonic-gate 		 */
861*0Sstevel@tonic-gate 		fdconf[0] = 0;
862*0Sstevel@tonic-gate 
863*0Sstevel@tonic-gate 		quiesce_fd_interrupt(fdc);
864*0Sstevel@tonic-gate 		break;
865*0Sstevel@tonic-gate 	default:
866*0Sstevel@tonic-gate 		break;
867*0Sstevel@tonic-gate 	}
868*0Sstevel@tonic-gate 
869*0Sstevel@tonic-gate 	return (0);
870*0Sstevel@tonic-gate }
871*0Sstevel@tonic-gate 
872*0Sstevel@tonic-gate /*
873*0Sstevel@tonic-gate  * Determine which type of floppy controller is present and
874*0Sstevel@tonic-gate  * initialize the registers accordingly
875*0Sstevel@tonic-gate  */
876*0Sstevel@tonic-gate static int
877*0Sstevel@tonic-gate fd_attach_det_ctlr(dev_info_t *dip, struct fdctlr *fdc)
878*0Sstevel@tonic-gate {
879*0Sstevel@tonic-gate 	ddi_device_acc_attr_t attr;
880*0Sstevel@tonic-gate 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
881*0Sstevel@tonic-gate 	/* DDI_NEVERSWAP_ACC since the controller has a byte interface. */
882*0Sstevel@tonic-gate 	attr.devacc_attr_endian_flags  = DDI_STRUCTURE_LE_ACC;
883*0Sstevel@tonic-gate 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
884*0Sstevel@tonic-gate 
885*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_ATTA,
886*0Sstevel@tonic-gate 			    (C, "fdattach_det_cltr: start \n"));
887*0Sstevel@tonic-gate 
888*0Sstevel@tonic-gate 	/*
889*0Sstevel@tonic-gate 	 * First, map in the controller's registers
890*0Sstevel@tonic-gate 	 * The controller has an 8-bit interface, so byte
891*0Sstevel@tonic-gate 	 * swapping isn't needed
892*0Sstevel@tonic-gate 	 */
893*0Sstevel@tonic-gate 
894*0Sstevel@tonic-gate 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&fdc->c_reg,
895*0Sstevel@tonic-gate 				0, sizeof (union fdcreg),
896*0Sstevel@tonic-gate 				&attr,
897*0Sstevel@tonic-gate 				&fdc->c_handlep_cont)) {
898*0Sstevel@tonic-gate 			return (DDI_FAILURE);
899*0Sstevel@tonic-gate 	}
900*0Sstevel@tonic-gate 
901*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_ATTA,
902*0Sstevel@tonic-gate 			    (C, "fdattach_det_cltr: mapped floppy regs\n"));
903*0Sstevel@tonic-gate 
904*0Sstevel@tonic-gate 
905*0Sstevel@tonic-gate 	/*
906*0Sstevel@tonic-gate 	 * Set platform specific characteristics based on the device-tree
907*0Sstevel@tonic-gate 	 * node name.
908*0Sstevel@tonic-gate 	 */
909*0Sstevel@tonic-gate 
910*0Sstevel@tonic-gate 
911*0Sstevel@tonic-gate 	if (strcmp(ddi_get_name(dip), "SUNW,fdtwo") == 0) {
912*0Sstevel@tonic-gate 		fdc->c_fdtype |= FDCTYPE_SLAVIO;
913*0Sstevel@tonic-gate 		fdc->c_fdtype |= FDCTYPE_82077;
914*0Sstevel@tonic-gate 		fdc->c_auxiova = fd_getauxiova(dip);
915*0Sstevel@tonic-gate 		fdc->c_auxiodata = (uchar_t)(AUX_MBO4M|AUX_TC4M);
916*0Sstevel@tonic-gate 		fdc->c_auxiodata2 = (uchar_t)AUX_TC4M;
917*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA,
918*0Sstevel@tonic-gate 			    (C, "fdattach: slavio will be used!\n"));
919*0Sstevel@tonic-gate 
920*0Sstevel@tonic-gate 
921*0Sstevel@tonic-gate /*
922*0Sstevel@tonic-gate  * Check the binding name to identify whether it is a South bridge based
923*0Sstevel@tonic-gate  * system or not.
924*0Sstevel@tonic-gate  */
925*0Sstevel@tonic-gate 	} else if (strcmp(ddi_get_name(dip), "pnpALI,1533,0") == 0) {
926*0Sstevel@tonic-gate 
927*0Sstevel@tonic-gate 		fdc->c_fdtype |= FDCTYPE_SB;
928*0Sstevel@tonic-gate 		fdc->c_fdtype |= FDCTYPE_82077;
929*0Sstevel@tonic-gate 		fdc->c_fdtype |= FDCTYPE_DMA;
930*0Sstevel@tonic-gate 
931*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA,
932*0Sstevel@tonic-gate 			(C, "fdattach: southbridge will be used!\n"));
933*0Sstevel@tonic-gate 
934*0Sstevel@tonic-gate 		/*
935*0Sstevel@tonic-gate 		 * The driver assumes high density characteristics until
936*0Sstevel@tonic-gate 		 * the diskette is looked at.
937*0Sstevel@tonic-gate 		 */
938*0Sstevel@tonic-gate 
939*0Sstevel@tonic-gate 		fdc->c_fdtype |= FDCTYPE_DMA8237;
940*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA, (C, "fd_attach: DMA used\n"));
941*0Sstevel@tonic-gate 
942*0Sstevel@tonic-gate 
943*0Sstevel@tonic-gate 	} else if (strcmp(ddi_get_name(dip), "fdthree") == 0) {
944*0Sstevel@tonic-gate 
945*0Sstevel@tonic-gate 		fdc->c_fdtype |= FDCTYPE_CHEERIO;
946*0Sstevel@tonic-gate 		fdc->c_fdtype |= FDCTYPE_82077;
947*0Sstevel@tonic-gate 
948*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA,
949*0Sstevel@tonic-gate 			    (C, "fdattach: cheerio will be used!\n"));
950*0Sstevel@tonic-gate 		/*
951*0Sstevel@tonic-gate 		 * The cheerio auxio register should be memory mapped.  The
952*0Sstevel@tonic-gate 		 * auxio register on other platforms is shared and mapped
953*0Sstevel@tonic-gate 		 * elsewhere in the kernel
954*0Sstevel@tonic-gate 		 */
955*0Sstevel@tonic-gate 		if (ddi_regs_map_setup(dip, 2, (caddr_t *)&fdc->c_auxio_reg,
956*0Sstevel@tonic-gate 		    0, sizeof (uint_t), &attr, &fdc->c_handlep_aux)) {
957*0Sstevel@tonic-gate 			return (DDI_FAILURE);
958*0Sstevel@tonic-gate 		}
959*0Sstevel@tonic-gate 
960*0Sstevel@tonic-gate 		/*
961*0Sstevel@tonic-gate 		 * The driver assumes high density characteristics until
962*0Sstevel@tonic-gate 		 * the diskette is looked at.
963*0Sstevel@tonic-gate 		 */
964*0Sstevel@tonic-gate 		Set_auxio(fdc, AUX_HIGH_DENSITY);
965*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA,
966*0Sstevel@tonic-gate 			    (C, "fdattach: auxio register 0x%x\n",
967*0Sstevel@tonic-gate 				 *fdc->c_auxio_reg));
968*0Sstevel@tonic-gate 
969*0Sstevel@tonic-gate 		fdc->c_fdtype |= FDCTYPE_DMA;
970*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA, (C, "fd_attach: DMA used\n"));
971*0Sstevel@tonic-gate 
972*0Sstevel@tonic-gate 	}
973*0Sstevel@tonic-gate 
974*0Sstevel@tonic-gate 	if (fdc->c_fdtype == 0) {
975*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA,
976*0Sstevel@tonic-gate 			    (C, "fdattach: no controller!\n"));
977*0Sstevel@tonic-gate 		return (DDI_FAILURE);
978*0Sstevel@tonic-gate 	} else {
979*0Sstevel@tonic-gate 		return (0);
980*0Sstevel@tonic-gate 	}
981*0Sstevel@tonic-gate }
982*0Sstevel@tonic-gate 
983*0Sstevel@tonic-gate 
984*0Sstevel@tonic-gate /*
985*0Sstevel@tonic-gate  * Register the floppy interrupts
986*0Sstevel@tonic-gate  */
987*0Sstevel@tonic-gate static int
988*0Sstevel@tonic-gate fd_attach_register_interrupts(dev_info_t *dip, struct fdctlr *fdc, int *hard)
989*0Sstevel@tonic-gate {
990*0Sstevel@tonic-gate 	ddi_iblock_cookie_t  iblock_cookie_soft;
991*0Sstevel@tonic-gate 	int status;
992*0Sstevel@tonic-gate 
993*0Sstevel@tonic-gate 	/*
994*0Sstevel@tonic-gate 	 * First call ddi_get_iblock_cookie() to retrieve the
995*0Sstevel@tonic-gate 	 * the interrupt block cookie so that the mutexes may
996*0Sstevel@tonic-gate 	 * be initialized before adding the interrupt.  If the
997*0Sstevel@tonic-gate 	 * mutexes are initialized after adding the interrupt, there
998*0Sstevel@tonic-gate 	 * could be a race condition.
999*0Sstevel@tonic-gate 	 */
1000*0Sstevel@tonic-gate 	if (ddi_get_iblock_cookie(dip, 0, &fdc->c_block) != DDI_SUCCESS) {
1001*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA,
1002*0Sstevel@tonic-gate 		(C, "fdattach: ddi_get_iblock_cookie failed\n"));
1003*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1004*0Sstevel@tonic-gate 
1005*0Sstevel@tonic-gate 	}
1006*0Sstevel@tonic-gate 
1007*0Sstevel@tonic-gate 	/* Initialize high level mutex */
1008*0Sstevel@tonic-gate 	mutex_init(&fdc->c_hilock, NULL, MUTEX_DRIVER, fdc->c_block);
1009*0Sstevel@tonic-gate 
1010*0Sstevel@tonic-gate 	/*
1011*0Sstevel@tonic-gate 	 * Try to register fast trap handler, if unable try standard
1012*0Sstevel@tonic-gate 	 * interrupt handler, else bad
1013*0Sstevel@tonic-gate 	 */
1014*0Sstevel@tonic-gate 
1015*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_DMA) {
1016*0Sstevel@tonic-gate 		if (ddi_add_intr(dip, 0, &fdc->c_block, 0,
1017*0Sstevel@tonic-gate 			    fdintr_dma, (caddr_t)0) == DDI_SUCCESS) {
1018*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_ATTA,
1019*0Sstevel@tonic-gate 				(C, "fdattach: standard intr\n"));
1020*0Sstevel@tonic-gate 
1021*0Sstevel@tonic-gate 				/*
1022*0Sstevel@tonic-gate 				 * When DMA is used, the low level lock
1023*0Sstevel@tonic-gate 				 * is used in the hard interrupt handler.
1024*0Sstevel@tonic-gate 				 */
1025*0Sstevel@tonic-gate 				mutex_init(&fdc->c_lolock, NULL,
1026*0Sstevel@tonic-gate 					MUTEX_DRIVER, fdc->c_block);
1027*0Sstevel@tonic-gate 
1028*0Sstevel@tonic-gate 				*hard = 1;
1029*0Sstevel@tonic-gate 		} else {
1030*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_ATTA,
1031*0Sstevel@tonic-gate 			(C, "fdattach: can't add dma intr\n"));
1032*0Sstevel@tonic-gate 
1033*0Sstevel@tonic-gate 			mutex_destroy(&fdc->c_hilock);
1034*0Sstevel@tonic-gate 
1035*0Sstevel@tonic-gate 			return (DDI_FAILURE);
1036*0Sstevel@tonic-gate 		}
1037*0Sstevel@tonic-gate 	} else {
1038*0Sstevel@tonic-gate 		/*
1039*0Sstevel@tonic-gate 		 * Platforms that don't support DMA have both hard
1040*0Sstevel@tonic-gate 		 * and soft interrupts.
1041*0Sstevel@tonic-gate 		 */
1042*0Sstevel@tonic-gate 		if (ddi_add_intr(dip, 0, &fdc->c_block, 0,
1043*0Sstevel@tonic-gate 			fd_intr, (caddr_t)0) == DDI_SUCCESS) {
1044*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_ATTA,
1045*0Sstevel@tonic-gate 				(C, "fdattach: standard intr\n"));
1046*0Sstevel@tonic-gate 				*hard = 1;
1047*0Sstevel@tonic-gate 
1048*0Sstevel@tonic-gate 			/* fast traps are not enabled */
1049*0Sstevel@tonic-gate 			fdc->c_fasttrap = 0;
1050*0Sstevel@tonic-gate 
1051*0Sstevel@tonic-gate 		} else {
1052*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_ATTA,
1053*0Sstevel@tonic-gate 			(C, "fdattach: can't add intr\n"));
1054*0Sstevel@tonic-gate 
1055*0Sstevel@tonic-gate 			mutex_destroy(&fdc->c_hilock);
1056*0Sstevel@tonic-gate 
1057*0Sstevel@tonic-gate 			return (DDI_FAILURE);
1058*0Sstevel@tonic-gate 		}
1059*0Sstevel@tonic-gate 
1060*0Sstevel@tonic-gate 
1061*0Sstevel@tonic-gate 		/*
1062*0Sstevel@tonic-gate 		 * Initialize the soft interrupt handler.  First call
1063*0Sstevel@tonic-gate 		 * ddi_get_soft_iblock_cookie() so that the mutex may
1064*0Sstevel@tonic-gate 		 * be initialized before the handler is added.
1065*0Sstevel@tonic-gate 		 */
1066*0Sstevel@tonic-gate 		status = ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW,
1067*0Sstevel@tonic-gate 				&iblock_cookie_soft);
1068*0Sstevel@tonic-gate 
1069*0Sstevel@tonic-gate 
1070*0Sstevel@tonic-gate 		if (status != DDI_SUCCESS) {
1071*0Sstevel@tonic-gate 			mutex_destroy(&fdc->c_hilock);
1072*0Sstevel@tonic-gate 			return (DDI_FAILURE);
1073*0Sstevel@tonic-gate 		}
1074*0Sstevel@tonic-gate 
1075*0Sstevel@tonic-gate 		/*
1076*0Sstevel@tonic-gate 		 * Initialize low level mutex which is used in the soft
1077*0Sstevel@tonic-gate 		 * interrupt handler
1078*0Sstevel@tonic-gate 		 */
1079*0Sstevel@tonic-gate 		mutex_init(&fdc->c_lolock, NULL, MUTEX_DRIVER,
1080*0Sstevel@tonic-gate 			iblock_cookie_soft);
1081*0Sstevel@tonic-gate 
1082*0Sstevel@tonic-gate 		if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &fdc->c_softid,
1083*0Sstevel@tonic-gate 					NULL, NULL,
1084*0Sstevel@tonic-gate 					fd_lointr,
1085*0Sstevel@tonic-gate 					(caddr_t)fdc) != DDI_SUCCESS) {
1086*0Sstevel@tonic-gate 
1087*0Sstevel@tonic-gate 			mutex_destroy(&fdc->c_hilock);
1088*0Sstevel@tonic-gate 			mutex_destroy(&fdc->c_lolock);
1089*0Sstevel@tonic-gate 
1090*0Sstevel@tonic-gate 			return (DDI_FAILURE);
1091*0Sstevel@tonic-gate 		}
1092*0Sstevel@tonic-gate 	}
1093*0Sstevel@tonic-gate 
1094*0Sstevel@tonic-gate 	fdc->c_intrstat = kstat_create("fd", 0, "fdc0", "controller",
1095*0Sstevel@tonic-gate 		KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT);
1096*0Sstevel@tonic-gate 	if (fdc->c_intrstat) {
1097*0Sstevel@tonic-gate 		fdc->c_hiintct = &KIOIP->intrs[KSTAT_INTR_HARD];
1098*0Sstevel@tonic-gate 		kstat_install(fdc->c_intrstat);
1099*0Sstevel@tonic-gate 	}
1100*0Sstevel@tonic-gate 
1101*0Sstevel@tonic-gate 	/* condition variable to wait on while an io transaction occurs */
1102*0Sstevel@tonic-gate 	cv_init(&fdc->c_iocv, NULL, CV_DRIVER, NULL);
1103*0Sstevel@tonic-gate 
1104*0Sstevel@tonic-gate 	/* condition variable for the csb */
1105*0Sstevel@tonic-gate 	cv_init(&fdc->c_csbcv, NULL, CV_DRIVER, NULL);
1106*0Sstevel@tonic-gate 
1107*0Sstevel@tonic-gate 	/* condition variable for motor on waiting period */
1108*0Sstevel@tonic-gate 	cv_init(&fdc->c_motoncv, NULL, CV_DRIVER, NULL);
1109*0Sstevel@tonic-gate 
1110*0Sstevel@tonic-gate 	/* semaphore to serialize opens and closes */
1111*0Sstevel@tonic-gate 	sema_init(&fdc->c_ocsem, 1, NULL, SEMA_DRIVER, NULL);
1112*0Sstevel@tonic-gate 
1113*0Sstevel@tonic-gate 	/* condition variable to wait on suspended floppy controller. */
1114*0Sstevel@tonic-gate 	cv_init(&fdc->c_suspend_cv, NULL, CV_DRIVER, NULL);
1115*0Sstevel@tonic-gate 
1116*0Sstevel@tonic-gate 	return (0);
1117*0Sstevel@tonic-gate }
1118*0Sstevel@tonic-gate 
1119*0Sstevel@tonic-gate /*
1120*0Sstevel@tonic-gate  * Make sure the drive is present
1121*0Sstevel@tonic-gate  * 	- acquires the low level lock
1122*0Sstevel@tonic-gate  */
1123*0Sstevel@tonic-gate static int
1124*0Sstevel@tonic-gate fd_attach_check_drive(struct fdctlr *fdc)
1125*0Sstevel@tonic-gate {
1126*0Sstevel@tonic-gate 	int tmp_fderrlevel;
1127*0Sstevel@tonic-gate 	int unit = fdc->c_un->un_unit_no;
1128*0Sstevel@tonic-gate 
1129*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_ATTA,
1130*0Sstevel@tonic-gate 			(C, "fd_attach_check_drive\n"));
1131*0Sstevel@tonic-gate 
1132*0Sstevel@tonic-gate 
1133*0Sstevel@tonic-gate 	mutex_enter(&fdc->c_lolock);
1134*0Sstevel@tonic-gate 	switch (fdc->c_fdtype & FDCTYPE_CTRLMASK) {
1135*0Sstevel@tonic-gate 
1136*0Sstevel@tonic-gate 	/* insure that the eject line is reset */
1137*0Sstevel@tonic-gate 	case FDCTYPE_82077:
1138*0Sstevel@tonic-gate 
1139*0Sstevel@tonic-gate 		/*
1140*0Sstevel@tonic-gate 		 * Everything but the motor enable, drive select,
1141*0Sstevel@tonic-gate 		 * and reset bits are turned off.  These three
1142*0Sstevel@tonic-gate 		 * bits remain as they are.
1143*0Sstevel@tonic-gate 		 */
1144*0Sstevel@tonic-gate 		/* LINTED */
1145*0Sstevel@tonic-gate 		Set_dor(fdc, ~((MOTEN(unit))|DRVSEL|RESET), 0);
1146*0Sstevel@tonic-gate 
1147*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA,
1148*0Sstevel@tonic-gate 			(C, "fdattach: Dor 0x%x\n", Dor(fdc)));
1149*0Sstevel@tonic-gate 
1150*0Sstevel@tonic-gate 		drv_usecwait(5);
1151*0Sstevel@tonic-gate 		if (unit == 0) {
1152*0Sstevel@tonic-gate 			/* LINTED */
1153*0Sstevel@tonic-gate 			Set_dor(fdc, RESET|DRVSEL, 1);
1154*0Sstevel@tonic-gate 		} else {
1155*0Sstevel@tonic-gate 
1156*0Sstevel@tonic-gate 			/* LINTED */
1157*0Sstevel@tonic-gate 			Set_dor(fdc, DRVSEL, 0);
1158*0Sstevel@tonic-gate 			/* LINTED */
1159*0Sstevel@tonic-gate 			Set_dor(fdc, RESET, 1);
1160*0Sstevel@tonic-gate 		}
1161*0Sstevel@tonic-gate 
1162*0Sstevel@tonic-gate 		drv_usecwait(5);
1163*0Sstevel@tonic-gate 
1164*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA,
1165*0Sstevel@tonic-gate 			(C, "fdattach: Dor 0x%x\n", Dor(fdc)));
1166*0Sstevel@tonic-gate 
1167*0Sstevel@tonic-gate 		if (!((fdc->c_fdtype & FDCTYPE_CHEERIO) ||
1168*0Sstevel@tonic-gate 				(fdc->c_fdtype & FDCTYPE_SB))) {
1169*0Sstevel@tonic-gate 			set_auxioreg(AUX_TC4M, 0);
1170*0Sstevel@tonic-gate 		}
1171*0Sstevel@tonic-gate 		break;
1172*0Sstevel@tonic-gate 	default:
1173*0Sstevel@tonic-gate 		break;
1174*0Sstevel@tonic-gate 	}
1175*0Sstevel@tonic-gate 
1176*0Sstevel@tonic-gate 
1177*0Sstevel@tonic-gate 	fdgetcsb(fdc);
1178*0Sstevel@tonic-gate 	if (fdreset(fdc) != 0) {
1179*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
1180*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1181*0Sstevel@tonic-gate 	}
1182*0Sstevel@tonic-gate 
1183*0Sstevel@tonic-gate 
1184*0Sstevel@tonic-gate 	/* check for drive present */
1185*0Sstevel@tonic-gate 
1186*0Sstevel@tonic-gate 	tmp_fderrlevel = fderrlevel;
1187*0Sstevel@tonic-gate 
1188*0Sstevel@tonic-gate 
1189*0Sstevel@tonic-gate 	fderrlevel = FDEP_LMAX;
1190*0Sstevel@tonic-gate 
1191*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_ATTA,
1192*0Sstevel@tonic-gate 			(C, "fdattach: call fdrecalseek\n"));
1193*0Sstevel@tonic-gate 
1194*0Sstevel@tonic-gate 	/* Make sure the drive is present */
1195*0Sstevel@tonic-gate 	if (fdrecalseek(fdc, unit, -1, 0) != 0) {
1196*0Sstevel@tonic-gate 		timeout_id_t timeid = fdc->c_mtimeid;
1197*0Sstevel@tonic-gate 		fderrlevel = tmp_fderrlevel;
1198*0Sstevel@tonic-gate 		fdc->c_mtimeid = 0;
1199*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
1200*0Sstevel@tonic-gate 
1201*0Sstevel@tonic-gate 
1202*0Sstevel@tonic-gate 		/* Do not hold the mutex over the call to untimeout */
1203*0Sstevel@tonic-gate 		if (timeid) {
1204*0Sstevel@tonic-gate 			(void) untimeout(timeid);
1205*0Sstevel@tonic-gate 		}
1206*0Sstevel@tonic-gate 
1207*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L2, FDEM_ATTA,
1208*0Sstevel@tonic-gate 		    (C, "fd_attach: no drive?\n"));
1209*0Sstevel@tonic-gate 
1210*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1211*0Sstevel@tonic-gate 	}
1212*0Sstevel@tonic-gate 
1213*0Sstevel@tonic-gate 	fderrlevel = tmp_fderrlevel;
1214*0Sstevel@tonic-gate 
1215*0Sstevel@tonic-gate 	fdselect(fdc, unit, 0);    /* deselect drive zero (used in fdreset) */
1216*0Sstevel@tonic-gate 	fdretcsb(fdc);
1217*0Sstevel@tonic-gate 	mutex_exit(&fdc->c_lolock);
1218*0Sstevel@tonic-gate 
1219*0Sstevel@tonic-gate 	return (0);
1220*0Sstevel@tonic-gate }
1221*0Sstevel@tonic-gate 
1222*0Sstevel@tonic-gate /*
1223*0Sstevel@tonic-gate  * Clean up routine used by fd_detach and fd_attach
1224*0Sstevel@tonic-gate  *
1225*0Sstevel@tonic-gate  * Note: if the soft id is non-zero, then ddi_add_softintr() completed
1226*0Sstevel@tonic-gate  * successfully.  I can not make the same assumption about the iblock_cookie
1227*0Sstevel@tonic-gate  * for the high level interrupt handler.  So, the hard parameter indicates
1228*0Sstevel@tonic-gate  * whether or not a high level interrupt handler has been added.
1229*0Sstevel@tonic-gate  *
1230*0Sstevel@tonic-gate  * If the locks parameter is nonzero, then all mutexes, semaphores and
1231*0Sstevel@tonic-gate  * condition variables will be destroyed.
1232*0Sstevel@tonic-gate  *
1233*0Sstevel@tonic-gate  * Does not assume the low level mutex is held.
1234*0Sstevel@tonic-gate  *
1235*0Sstevel@tonic-gate  */
1236*0Sstevel@tonic-gate static void
1237*0Sstevel@tonic-gate fd_cleanup(dev_info_t *dip, struct fdctlr *fdc, int hard, int locks)
1238*0Sstevel@tonic-gate {
1239*0Sstevel@tonic-gate 
1240*0Sstevel@tonic-gate 
1241*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_ATTA,
1242*0Sstevel@tonic-gate 		(C, "fd_cleanup instance: %d ctlr: 0x%x\n",
1243*0Sstevel@tonic-gate 		ddi_get_instance(dip), (int)fdc));
1244*0Sstevel@tonic-gate 
1245*0Sstevel@tonic-gate 
1246*0Sstevel@tonic-gate 	if (fdc == NULL) {
1247*0Sstevel@tonic-gate 		return;
1248*0Sstevel@tonic-gate 	}
1249*0Sstevel@tonic-gate 
1250*0Sstevel@tonic-gate 	/*
1251*0Sstevel@tonic-gate 	 * Remove interrupt handlers first before anything else
1252*0Sstevel@tonic-gate 	 * is deallocated.
1253*0Sstevel@tonic-gate 	 */
1254*0Sstevel@tonic-gate 
1255*0Sstevel@tonic-gate 	/* Remove hard interrupt if one is registered */
1256*0Sstevel@tonic-gate 	if (hard) {
1257*0Sstevel@tonic-gate 		ddi_remove_intr(dip, (uint_t)0, fdc->c_block);
1258*0Sstevel@tonic-gate 	}
1259*0Sstevel@tonic-gate 
1260*0Sstevel@tonic-gate 	/* Remove soft interrupt if one is registered */
1261*0Sstevel@tonic-gate 	if (fdc->c_softid != NULL)
1262*0Sstevel@tonic-gate 		ddi_remove_softintr(fdc->c_softid);
1263*0Sstevel@tonic-gate 
1264*0Sstevel@tonic-gate 
1265*0Sstevel@tonic-gate 	/* Remove timers */
1266*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_82077) {
1267*0Sstevel@tonic-gate 		if (fdc->c_mtimeid)
1268*0Sstevel@tonic-gate 			(void) untimeout(fdc->c_mtimeid);
1269*0Sstevel@tonic-gate 		/*
1270*0Sstevel@tonic-gate 		 * Need to turn off motor (includes select/LED for South Bridge
1271*0Sstevel@tonic-gate 		 * chipset) just in case it was on when timer was removed
1272*0Sstevel@tonic-gate 		 */
1273*0Sstevel@tonic-gate 		fdmotoff(fdc);
1274*0Sstevel@tonic-gate 	}
1275*0Sstevel@tonic-gate 	if (fdc->c_timeid)
1276*0Sstevel@tonic-gate 		(void) untimeout(fdc->c_timeid);
1277*0Sstevel@tonic-gate 
1278*0Sstevel@tonic-gate 
1279*0Sstevel@tonic-gate 	/* Remove memory handles */
1280*0Sstevel@tonic-gate 	if (fdc->c_handlep_cont)
1281*0Sstevel@tonic-gate 		ddi_regs_map_free(&fdc->c_handlep_cont);
1282*0Sstevel@tonic-gate 
1283*0Sstevel@tonic-gate 	if (fdc->c_handlep_aux)
1284*0Sstevel@tonic-gate 		ddi_regs_map_free(&fdc->c_handlep_aux);
1285*0Sstevel@tonic-gate 
1286*0Sstevel@tonic-gate 	if (fdc->c_handlep_dma)
1287*0Sstevel@tonic-gate 		ddi_regs_map_free(&fdc->c_handlep_dma);
1288*0Sstevel@tonic-gate 
1289*0Sstevel@tonic-gate 	if (fdc->c_dma_buf_handle != NULL)
1290*0Sstevel@tonic-gate 		ddi_dma_mem_free(&fdc->c_dma_buf_handle);
1291*0Sstevel@tonic-gate 
1292*0Sstevel@tonic-gate 	if (fdc->c_dmahandle != NULL)
1293*0Sstevel@tonic-gate 		ddi_dma_free_handle(&fdc->c_dmahandle);
1294*0Sstevel@tonic-gate 
1295*0Sstevel@tonic-gate 
1296*0Sstevel@tonic-gate 	/* Remove all minor nodes */
1297*0Sstevel@tonic-gate 	ddi_remove_minor_node(dip, NULL);
1298*0Sstevel@tonic-gate 
1299*0Sstevel@tonic-gate 
1300*0Sstevel@tonic-gate 
1301*0Sstevel@tonic-gate 	/* Remove unit structure if one exists */
1302*0Sstevel@tonic-gate 	if (fdc->c_un != (struct fdunit *)NULL) {
1303*0Sstevel@tonic-gate 
1304*0Sstevel@tonic-gate 		ASSERT(!mutex_owned(&fdc->c_lolock));
1305*0Sstevel@tonic-gate 
1306*0Sstevel@tonic-gate 		if (fdc->c_un->un_iostat)
1307*0Sstevel@tonic-gate 			kstat_delete(fdc->c_un->un_iostat);
1308*0Sstevel@tonic-gate 		fdc->c_un->un_iostat = NULL;
1309*0Sstevel@tonic-gate 
1310*0Sstevel@tonic-gate 		if (fdc->c_un->un_chars)
1311*0Sstevel@tonic-gate 			kmem_free(fdc->c_un->un_chars, sizeof (struct fd_char));
1312*0Sstevel@tonic-gate 
1313*0Sstevel@tonic-gate 		if (fdc->c_un->un_drive)
1314*0Sstevel@tonic-gate 			kmem_free(fdc->c_un->un_drive,
1315*0Sstevel@tonic-gate 			    sizeof (struct fd_drive));
1316*0Sstevel@tonic-gate 
1317*0Sstevel@tonic-gate 		kmem_free((caddr_t)fdc->c_un, sizeof (struct fdunit));
1318*0Sstevel@tonic-gate 	}
1319*0Sstevel@tonic-gate 
1320*0Sstevel@tonic-gate 	if (fdc->c_intrstat) {
1321*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA,
1322*0Sstevel@tonic-gate 				(C, "fd_cleanup: delete intrstat\n"));
1323*0Sstevel@tonic-gate 
1324*0Sstevel@tonic-gate 		kstat_delete(fdc->c_intrstat);
1325*0Sstevel@tonic-gate 	}
1326*0Sstevel@tonic-gate 
1327*0Sstevel@tonic-gate 	fdc->c_intrstat = NULL;
1328*0Sstevel@tonic-gate 
1329*0Sstevel@tonic-gate 	if (locks) {
1330*0Sstevel@tonic-gate 		cv_destroy(&fdc->c_iocv);
1331*0Sstevel@tonic-gate 		cv_destroy(&fdc->c_csbcv);
1332*0Sstevel@tonic-gate 		cv_destroy(&fdc->c_motoncv);
1333*0Sstevel@tonic-gate 		cv_destroy(&fdc->c_suspend_cv);
1334*0Sstevel@tonic-gate 		sema_destroy(&fdc->c_ocsem);
1335*0Sstevel@tonic-gate 		mutex_destroy(&fdc->c_hilock);
1336*0Sstevel@tonic-gate 		mutex_destroy(&fdc->c_lolock);
1337*0Sstevel@tonic-gate 	}
1338*0Sstevel@tonic-gate 
1339*0Sstevel@tonic-gate 
1340*0Sstevel@tonic-gate 	fdctlrs = fdc->c_next;
1341*0Sstevel@tonic-gate 	kmem_free(fdc, sizeof (*fdc));
1342*0Sstevel@tonic-gate 
1343*0Sstevel@tonic-gate 
1344*0Sstevel@tonic-gate }
1345*0Sstevel@tonic-gate 
1346*0Sstevel@tonic-gate 
1347*0Sstevel@tonic-gate static int
1348*0Sstevel@tonic-gate fd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1349*0Sstevel@tonic-gate {
1350*0Sstevel@tonic-gate 	int instance = ddi_get_instance(dip);
1351*0Sstevel@tonic-gate 	struct fdctlr *fdc = fd_getctlr(instance << FDINSTSHIFT);
1352*0Sstevel@tonic-gate 	timeout_id_t c_mtimeid;
1353*0Sstevel@tonic-gate 
1354*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_ATTA, (C, "fd_detach\n"));
1355*0Sstevel@tonic-gate 
1356*0Sstevel@tonic-gate 	switch (cmd) {
1357*0Sstevel@tonic-gate 
1358*0Sstevel@tonic-gate 	case DDI_DETACH:
1359*0Sstevel@tonic-gate 		/*
1360*0Sstevel@tonic-gate 		 * The hard parameter is set to 1.  If detach is called, then
1361*0Sstevel@tonic-gate 		 * attach must have passed meaning that the high level
1362*0Sstevel@tonic-gate 		 * interrupt handler was successfully added.
1363*0Sstevel@tonic-gate 		 * Similarly, the locks parameter is also set to 1.
1364*0Sstevel@tonic-gate 		 */
1365*0Sstevel@tonic-gate 		fd_cleanup(dip, fdc, 1, 1);
1366*0Sstevel@tonic-gate 
1367*0Sstevel@tonic-gate 		ddi_prop_remove_all(dip);
1368*0Sstevel@tonic-gate 
1369*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
1370*0Sstevel@tonic-gate 
1371*0Sstevel@tonic-gate 	case DDI_SUSPEND:
1372*0Sstevel@tonic-gate 		if (!fdc)
1373*0Sstevel@tonic-gate 			return (DDI_FAILURE);
1374*0Sstevel@tonic-gate 
1375*0Sstevel@tonic-gate 
1376*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_lolock);
1377*0Sstevel@tonic-gate 		fdgetcsb(fdc);	/* Wait for I/O to finish */
1378*0Sstevel@tonic-gate 		c_mtimeid = fdc->c_mtimeid;
1379*0Sstevel@tonic-gate 		fdretcsb(fdc);
1380*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
1381*0Sstevel@tonic-gate 
1382*0Sstevel@tonic-gate 		(void) untimeout(c_mtimeid);
1383*0Sstevel@tonic-gate 		/*
1384*0Sstevel@tonic-gate 		 * After suspend, the system could be powered off.
1385*0Sstevel@tonic-gate 		 * When it is later powered on the southbridge floppy
1386*0Sstevel@tonic-gate 		 * controller will tristate the interrupt line causing
1387*0Sstevel@tonic-gate 		 * continuous dma interrupts.
1388*0Sstevel@tonic-gate 		 * To avoid getting continuous fd interrupts we will remove the
1389*0Sstevel@tonic-gate 		 * dma interrupt handler installed. We will re-install the
1390*0Sstevel@tonic-gate 		 * handler when we RESUME.
1391*0Sstevel@tonic-gate 		 */
1392*0Sstevel@tonic-gate 		if (fdc->c_fdtype & FDCTYPE_SB)
1393*0Sstevel@tonic-gate 			ddi_remove_intr(dip, 0, fdc->c_block);
1394*0Sstevel@tonic-gate 
1395*0Sstevel@tonic-gate 		fdc->c_un->un_state = FD_STATE_SUSPENDED;
1396*0Sstevel@tonic-gate 
1397*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
1398*0Sstevel@tonic-gate 
1399*0Sstevel@tonic-gate 	default:
1400*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1401*0Sstevel@tonic-gate 	}
1402*0Sstevel@tonic-gate }
1403*0Sstevel@tonic-gate 
1404*0Sstevel@tonic-gate /* ARGSUSED */
1405*0Sstevel@tonic-gate static int
1406*0Sstevel@tonic-gate fd_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
1407*0Sstevel@tonic-gate {
1408*0Sstevel@tonic-gate 	register struct fdctlr *fdc;
1409*0Sstevel@tonic-gate 	register int error;
1410*0Sstevel@tonic-gate 
1411*0Sstevel@tonic-gate 	switch (infocmd) {
1412*0Sstevel@tonic-gate 
1413*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
1414*0Sstevel@tonic-gate 		if ((fdc = fd_getctlr((dev_t)arg)) == NULL) {
1415*0Sstevel@tonic-gate 			error = DDI_FAILURE;
1416*0Sstevel@tonic-gate 		} else {
1417*0Sstevel@tonic-gate 			*result = fdc->c_dip;
1418*0Sstevel@tonic-gate 			error = DDI_SUCCESS;
1419*0Sstevel@tonic-gate 		}
1420*0Sstevel@tonic-gate 		break;
1421*0Sstevel@tonic-gate 
1422*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
1423*0Sstevel@tonic-gate 		*result = 0;
1424*0Sstevel@tonic-gate 		error = DDI_SUCCESS;
1425*0Sstevel@tonic-gate 		break;
1426*0Sstevel@tonic-gate 
1427*0Sstevel@tonic-gate 	default:
1428*0Sstevel@tonic-gate 		error = DDI_FAILURE;
1429*0Sstevel@tonic-gate 	}
1430*0Sstevel@tonic-gate 	return (error);
1431*0Sstevel@tonic-gate }
1432*0Sstevel@tonic-gate 
1433*0Sstevel@tonic-gate /*
1434*0Sstevel@tonic-gate  * property operation routine.  return the number of blocks for the partition
1435*0Sstevel@tonic-gate  * in question or forward the request to the property facilities.
1436*0Sstevel@tonic-gate  */
1437*0Sstevel@tonic-gate static int
1438*0Sstevel@tonic-gate fd_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int mod_flags,
1439*0Sstevel@tonic-gate     char *name, caddr_t valuep, int *lengthp)
1440*0Sstevel@tonic-gate {
1441*0Sstevel@tonic-gate 	struct fdunit	*un;
1442*0Sstevel@tonic-gate 	struct fdctlr	*fdc;
1443*0Sstevel@tonic-gate 	uint64_t	nblocks64;
1444*0Sstevel@tonic-gate 
1445*0Sstevel@tonic-gate 	/*
1446*0Sstevel@tonic-gate 	 * Our dynamic properties are all device specific and size oriented.
1447*0Sstevel@tonic-gate 	 * Requests issued under conditions where size is valid are passed
1448*0Sstevel@tonic-gate 	 * to ddi_prop_op_nblocks with the size information, otherwise the
1449*0Sstevel@tonic-gate 	 * request is passed to ddi_prop_op.
1450*0Sstevel@tonic-gate 	 */
1451*0Sstevel@tonic-gate 	if (dev == DDI_DEV_T_ANY) {
1452*0Sstevel@tonic-gate pass:  		return (ddi_prop_op(dev, dip, prop_op, mod_flags,
1453*0Sstevel@tonic-gate 		    name, valuep, lengthp));
1454*0Sstevel@tonic-gate 	} else {
1455*0Sstevel@tonic-gate 		fdc = fd_getctlr(dev);
1456*0Sstevel@tonic-gate 		if (fdc == NULL)
1457*0Sstevel@tonic-gate 			goto pass;
1458*0Sstevel@tonic-gate 
1459*0Sstevel@tonic-gate 		/* we have size if diskette opened and label read */
1460*0Sstevel@tonic-gate 		un = fdc->c_un;
1461*0Sstevel@tonic-gate 		if ((un == NULL) || !fd_unit_is_open(fdc->c_un))
1462*0Sstevel@tonic-gate 			goto pass;
1463*0Sstevel@tonic-gate 
1464*0Sstevel@tonic-gate 		/* get nblocks value */
1465*0Sstevel@tonic-gate 		nblocks64 = (ulong_t)
1466*0Sstevel@tonic-gate 		    un->un_label.dkl_map[FDPARTITION(dev)].dkl_nblk;
1467*0Sstevel@tonic-gate 
1468*0Sstevel@tonic-gate 		return (ddi_prop_op_nblocks(dev, dip, prop_op, mod_flags,
1469*0Sstevel@tonic-gate 		    name, valuep, lengthp, nblocks64));
1470*0Sstevel@tonic-gate 	}
1471*0Sstevel@tonic-gate }
1472*0Sstevel@tonic-gate 
1473*0Sstevel@tonic-gate /* ARGSUSED3 */
1474*0Sstevel@tonic-gate static int
1475*0Sstevel@tonic-gate fd_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
1476*0Sstevel@tonic-gate {
1477*0Sstevel@tonic-gate 	dev_t dev;
1478*0Sstevel@tonic-gate 	int  part;
1479*0Sstevel@tonic-gate 	struct fdctlr *fdc;
1480*0Sstevel@tonic-gate 	struct fdunit *un;
1481*0Sstevel@tonic-gate 	struct dk_map32 *dkm;
1482*0Sstevel@tonic-gate 	uchar_t	pbit;
1483*0Sstevel@tonic-gate 	int	err, part_is_open;
1484*0Sstevel@tonic-gate 	int 	unit;
1485*0Sstevel@tonic-gate 
1486*0Sstevel@tonic-gate 	dev = *devp;
1487*0Sstevel@tonic-gate 	fdc = fd_getctlr(dev);
1488*0Sstevel@tonic-gate 	if ((fdc == NULL) || ((un = fdc->c_un) == NULL)) {
1489*0Sstevel@tonic-gate 		return (ENXIO);
1490*0Sstevel@tonic-gate 	}
1491*0Sstevel@tonic-gate 
1492*0Sstevel@tonic-gate 	unit = fdc->c_un->un_unit_no;
1493*0Sstevel@tonic-gate 
1494*0Sstevel@tonic-gate 	/*
1495*0Sstevel@tonic-gate 	 * Serialize opens/closes
1496*0Sstevel@tonic-gate 	 */
1497*0Sstevel@tonic-gate 
1498*0Sstevel@tonic-gate 	sema_p(&fdc->c_ocsem);
1499*0Sstevel@tonic-gate 
1500*0Sstevel@tonic-gate 	/* check partition */
1501*0Sstevel@tonic-gate 	part = FDPARTITION(dev);
1502*0Sstevel@tonic-gate 	pbit = 1 << part;
1503*0Sstevel@tonic-gate 	dkm = &un->un_label.dkl_map[part];
1504*0Sstevel@tonic-gate 	if (dkm->dkl_nblk == 0) {
1505*0Sstevel@tonic-gate 		sema_v(&fdc->c_ocsem);
1506*0Sstevel@tonic-gate 		return (ENXIO);
1507*0Sstevel@tonic-gate 	}
1508*0Sstevel@tonic-gate 
1509*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_OPEN,
1510*0Sstevel@tonic-gate 	    (C, "fdopen: ctlr %d unit %d part %d\n",
1511*0Sstevel@tonic-gate 	    ddi_get_instance(fdc->c_dip), unit, part));
1512*0Sstevel@tonic-gate 
1513*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_OPEN,
1514*0Sstevel@tonic-gate 	    (C, "fdopen: flag 0x%x", flag));
1515*0Sstevel@tonic-gate 
1516*0Sstevel@tonic-gate 
1517*0Sstevel@tonic-gate 	/*
1518*0Sstevel@tonic-gate 	 * Insure that drive is present with a recalibrate on first open.
1519*0Sstevel@tonic-gate 	 */
1520*0Sstevel@tonic-gate 	(void) pm_busy_component(fdc->c_dip, 0);
1521*0Sstevel@tonic-gate 
1522*0Sstevel@tonic-gate 	mutex_enter(&fdc->c_lolock);
1523*0Sstevel@tonic-gate 
1524*0Sstevel@tonic-gate 	CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc);
1525*0Sstevel@tonic-gate 
1526*0Sstevel@tonic-gate 	if (fdc->c_un->un_state == FD_STATE_STOPPED) {
1527*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
1528*0Sstevel@tonic-gate 		if ((pm_raise_power(fdc->c_dip, 0, PM_LEVEL_ON))
1529*0Sstevel@tonic-gate 						!= DDI_SUCCESS) {
1530*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "Power change \
1531*0Sstevel@tonic-gate failed. \n"));
1532*0Sstevel@tonic-gate 
1533*0Sstevel@tonic-gate 				sema_v(&fdc->c_ocsem);
1534*0Sstevel@tonic-gate 				(void) pm_idle_component(fdc->c_dip, 0);
1535*0Sstevel@tonic-gate 				return (EIO);
1536*0Sstevel@tonic-gate 		}
1537*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_lolock);
1538*0Sstevel@tonic-gate 	}
1539*0Sstevel@tonic-gate 	if (fd_unit_is_open(un) == 0) {
1540*0Sstevel@tonic-gate 		fdgetcsb(fdc);
1541*0Sstevel@tonic-gate 		/*
1542*0Sstevel@tonic-gate 		 * no check changed!
1543*0Sstevel@tonic-gate 		 */
1544*0Sstevel@tonic-gate 		err = fdrecalseek(fdc, unit, -1, 0);
1545*0Sstevel@tonic-gate 		fdretcsb(fdc);
1546*0Sstevel@tonic-gate 		if (err) {
1547*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L3, FDEM_OPEN,
1548*0Sstevel@tonic-gate 			    (C, "fd%d: drive not ready\n", 0));
1549*0Sstevel@tonic-gate 			/* deselect drv on last close */
1550*0Sstevel@tonic-gate 			fdselect(fdc, unit, 0);
1551*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_lolock);
1552*0Sstevel@tonic-gate 			sema_v(&fdc->c_ocsem);
1553*0Sstevel@tonic-gate 			(void) pm_idle_component(fdc->c_dip, 0);
1554*0Sstevel@tonic-gate 			return (EIO);
1555*0Sstevel@tonic-gate 		}
1556*0Sstevel@tonic-gate 	}
1557*0Sstevel@tonic-gate 
1558*0Sstevel@tonic-gate 	/*
1559*0Sstevel@tonic-gate 	 * Check for previous exclusive open, or trying to exclusive open
1560*0Sstevel@tonic-gate 	 */
1561*0Sstevel@tonic-gate 	if (otyp == OTYP_LYR) {
1562*0Sstevel@tonic-gate 		part_is_open = (un->un_lyropen[part] != 0);
1563*0Sstevel@tonic-gate 	} else {
1564*0Sstevel@tonic-gate 		part_is_open = fd_part_is_open(un, part);
1565*0Sstevel@tonic-gate 	}
1566*0Sstevel@tonic-gate 	if ((un->un_exclmask & pbit) || ((flag & FEXCL) && part_is_open)) {
1567*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
1568*0Sstevel@tonic-gate 		sema_v(&fdc->c_ocsem);
1569*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L2, FDEM_OPEN, (C, "fd:just return\n"));
1570*0Sstevel@tonic-gate 		(void) pm_idle_component(fdc->c_dip, 0);
1571*0Sstevel@tonic-gate 		return (EBUSY);
1572*0Sstevel@tonic-gate 	}
1573*0Sstevel@tonic-gate 
1574*0Sstevel@tonic-gate 	/* don't attempt access, just return successfully */
1575*0Sstevel@tonic-gate 	if (flag & (FNDELAY | FNONBLOCK)) {
1576*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L2, FDEM_OPEN,
1577*0Sstevel@tonic-gate 		    (C, "fd: return busy..\n"));
1578*0Sstevel@tonic-gate 		goto out;
1579*0Sstevel@tonic-gate 	}
1580*0Sstevel@tonic-gate 
1581*0Sstevel@tonic-gate 	fdc->c_csb.csb_unit = (uchar_t)unit;
1582*0Sstevel@tonic-gate 	if (fdgetlabel(fdc, unit)) {
1583*0Sstevel@tonic-gate 		/* didn't find label (couldn't read anything) */
1584*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L3, FDEM_OPEN,
1585*0Sstevel@tonic-gate 		    (C,
1586*0Sstevel@tonic-gate 		    "fd%d: unformatted diskette or no diskette in the drive\n",
1587*0Sstevel@tonic-gate 		    0));
1588*0Sstevel@tonic-gate 		if (fd_unit_is_open(un) == 0) {
1589*0Sstevel@tonic-gate 			/* deselect drv on last close */
1590*0Sstevel@tonic-gate 			fdselect(fdc, unit, 0);
1591*0Sstevel@tonic-gate 		}
1592*0Sstevel@tonic-gate 
1593*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
1594*0Sstevel@tonic-gate 		sema_v(&fdc->c_ocsem);
1595*0Sstevel@tonic-gate 		(void) pm_idle_component(fdc->c_dip, 0);
1596*0Sstevel@tonic-gate 		return (EIO);
1597*0Sstevel@tonic-gate 	}
1598*0Sstevel@tonic-gate 
1599*0Sstevel@tonic-gate 	/*
1600*0Sstevel@tonic-gate 	 * if opening for writing, check write protect on diskette
1601*0Sstevel@tonic-gate 	 */
1602*0Sstevel@tonic-gate 	if (flag & FWRITE) {
1603*0Sstevel@tonic-gate 		fdgetcsb(fdc);
1604*0Sstevel@tonic-gate 		err = fdsensedrv(fdc, unit) & WP_SR3;
1605*0Sstevel@tonic-gate 		fdretcsb(fdc);
1606*0Sstevel@tonic-gate 		if (err) {
1607*0Sstevel@tonic-gate 			if (fd_unit_is_open(un) == 0)
1608*0Sstevel@tonic-gate 				fdselect(fdc, unit, 0);
1609*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_lolock);
1610*0Sstevel@tonic-gate 			sema_v(&fdc->c_ocsem);
1611*0Sstevel@tonic-gate 			(void) pm_idle_component(fdc->c_dip, 0);
1612*0Sstevel@tonic-gate 			return (EROFS);
1613*0Sstevel@tonic-gate 		}
1614*0Sstevel@tonic-gate 	}
1615*0Sstevel@tonic-gate 
1616*0Sstevel@tonic-gate out:
1617*0Sstevel@tonic-gate 	/*
1618*0Sstevel@tonic-gate 	 * mark open as having succeeded
1619*0Sstevel@tonic-gate 	 */
1620*0Sstevel@tonic-gate 	if (flag & FEXCL) {
1621*0Sstevel@tonic-gate 		un->un_exclmask |= pbit;
1622*0Sstevel@tonic-gate 	}
1623*0Sstevel@tonic-gate 	if (otyp == OTYP_LYR) {
1624*0Sstevel@tonic-gate 		un->un_lyropen[part]++;
1625*0Sstevel@tonic-gate 	} else {
1626*0Sstevel@tonic-gate 		un->un_regopen[otyp] |= pbit;
1627*0Sstevel@tonic-gate 	}
1628*0Sstevel@tonic-gate 	mutex_exit(&fdc->c_lolock);
1629*0Sstevel@tonic-gate 	sema_v(&fdc->c_ocsem);
1630*0Sstevel@tonic-gate 	(void) pm_idle_component(fdc->c_dip, 0);
1631*0Sstevel@tonic-gate 	return (0);
1632*0Sstevel@tonic-gate }
1633*0Sstevel@tonic-gate /*
1634*0Sstevel@tonic-gate  * fd_part_is_open
1635*0Sstevel@tonic-gate  *	return 1 if the partition is open
1636*0Sstevel@tonic-gate  *	return 0 otherwise
1637*0Sstevel@tonic-gate  */
1638*0Sstevel@tonic-gate static int
1639*0Sstevel@tonic-gate fd_part_is_open(struct fdunit *un, int part)
1640*0Sstevel@tonic-gate {
1641*0Sstevel@tonic-gate 	int i;
1642*0Sstevel@tonic-gate 	for (i = 0; i < OTYPCNT - 1; i++)
1643*0Sstevel@tonic-gate 		if (un->un_regopen[i] & (1 << part))
1644*0Sstevel@tonic-gate 			return (1);
1645*0Sstevel@tonic-gate 	return (0);
1646*0Sstevel@tonic-gate }
1647*0Sstevel@tonic-gate 
1648*0Sstevel@tonic-gate 
1649*0Sstevel@tonic-gate /* ARGSUSED */
1650*0Sstevel@tonic-gate static int
1651*0Sstevel@tonic-gate fd_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
1652*0Sstevel@tonic-gate {
1653*0Sstevel@tonic-gate 	int unit, part_is_closed, part;
1654*0Sstevel@tonic-gate 	register struct fdctlr *fdc;
1655*0Sstevel@tonic-gate 	register struct fdunit *un;
1656*0Sstevel@tonic-gate 
1657*0Sstevel@tonic-gate 	fdc = fd_getctlr(dev);
1658*0Sstevel@tonic-gate 	if (!fdc || !(un = fdc->c_un))
1659*0Sstevel@tonic-gate 		return (ENXIO);
1660*0Sstevel@tonic-gate 
1661*0Sstevel@tonic-gate 
1662*0Sstevel@tonic-gate 	unit = fdc->c_un->un_unit_no;
1663*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_CLOS, (C, "fd_close\n"));
1664*0Sstevel@tonic-gate 	part = FDPARTITION(dev);
1665*0Sstevel@tonic-gate 
1666*0Sstevel@tonic-gate 	sema_p(&fdc->c_ocsem);
1667*0Sstevel@tonic-gate 	mutex_enter(&fdc->c_lolock);
1668*0Sstevel@tonic-gate 
1669*0Sstevel@tonic-gate 	if (otyp == OTYP_LYR) {
1670*0Sstevel@tonic-gate 		un->un_lyropen[part]--;
1671*0Sstevel@tonic-gate 		part_is_closed = (un->un_lyropen[part] == 0);
1672*0Sstevel@tonic-gate 	} else {
1673*0Sstevel@tonic-gate 		un->un_regopen[otyp] &= ~(1<<part);
1674*0Sstevel@tonic-gate 		part_is_closed = 1;
1675*0Sstevel@tonic-gate 	}
1676*0Sstevel@tonic-gate 	if (part_is_closed)
1677*0Sstevel@tonic-gate 		un->un_exclmask &= ~(1<<part);
1678*0Sstevel@tonic-gate 
1679*0Sstevel@tonic-gate 	if (fd_unit_is_open(un) == 0) {
1680*0Sstevel@tonic-gate 		/* deselect drive on last close */
1681*0Sstevel@tonic-gate 		fdselect(fdc, unit, 0);
1682*0Sstevel@tonic-gate 		un->un_flags &= ~FDUNIT_CHANGED;
1683*0Sstevel@tonic-gate 	}
1684*0Sstevel@tonic-gate 	mutex_exit(&fdc->c_lolock);
1685*0Sstevel@tonic-gate 	sema_v(&fdc->c_ocsem);
1686*0Sstevel@tonic-gate 
1687*0Sstevel@tonic-gate 	return (0);
1688*0Sstevel@tonic-gate }
1689*0Sstevel@tonic-gate 
1690*0Sstevel@tonic-gate /*
1691*0Sstevel@tonic-gate  * fd_strategy
1692*0Sstevel@tonic-gate  *	checks operation, hangs buf struct off fdctlr, calls fdstart
1693*0Sstevel@tonic-gate  *	if not already busy.  Note that if we call start, then the operation
1694*0Sstevel@tonic-gate  *	will already be done on return (start sleeps).
1695*0Sstevel@tonic-gate  */
1696*0Sstevel@tonic-gate static int
1697*0Sstevel@tonic-gate fd_strategy(register struct buf *bp)
1698*0Sstevel@tonic-gate {
1699*0Sstevel@tonic-gate 	struct fdctlr *fdc;
1700*0Sstevel@tonic-gate 	struct fdunit *un;
1701*0Sstevel@tonic-gate 	uint_t	phys_blkno;
1702*0Sstevel@tonic-gate 	struct dk_map32 *dkm;
1703*0Sstevel@tonic-gate 
1704*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_STRA,
1705*0Sstevel@tonic-gate 	    (C, "fd_strategy: bp = 0x%p, dev = 0x%lx\n",
1706*0Sstevel@tonic-gate 	    (void *)bp, bp->b_edev));
1707*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_STRA,
1708*0Sstevel@tonic-gate 	    (C, "b_blkno=%x b_flags=%x b_count=%x\n",
1709*0Sstevel@tonic-gate 	    (int)bp->b_blkno, bp->b_flags, (int)bp->b_bcount));
1710*0Sstevel@tonic-gate 	fdc = fd_getctlr(bp->b_edev);
1711*0Sstevel@tonic-gate 	un = fdc->c_un;
1712*0Sstevel@tonic-gate 	dkm = &un->un_label.dkl_map[FDPARTITION(bp->b_edev)];
1713*0Sstevel@tonic-gate 
1714*0Sstevel@tonic-gate 	/*
1715*0Sstevel@tonic-gate 	 * If it's medium density and the block no. isn't a multiple
1716*0Sstevel@tonic-gate 	 * of 1K, then return an error.
1717*0Sstevel@tonic-gate 	 */
1718*0Sstevel@tonic-gate 	if (un->un_chars->fdc_medium) {
1719*0Sstevel@tonic-gate 		phys_blkno = (uint_t)bp->b_blkno >> 1;
1720*0Sstevel@tonic-gate 		if (bp->b_blkno & 1) {
1721*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L3, FDEM_STRA,
1722*0Sstevel@tonic-gate 			    (C, "b_blkno=0x%lx is not 1k aligned\n",
1723*0Sstevel@tonic-gate 			    (long)bp->b_blkno));
1724*0Sstevel@tonic-gate 			bp->b_error = EINVAL;
1725*0Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount;
1726*0Sstevel@tonic-gate 			bp->b_flags |= B_ERROR;
1727*0Sstevel@tonic-gate 			biodone(bp);
1728*0Sstevel@tonic-gate 			return (0);
1729*0Sstevel@tonic-gate 		}
1730*0Sstevel@tonic-gate 	} else {
1731*0Sstevel@tonic-gate 		phys_blkno = (uint_t)bp->b_blkno;
1732*0Sstevel@tonic-gate 	}
1733*0Sstevel@tonic-gate 
1734*0Sstevel@tonic-gate 
1735*0Sstevel@tonic-gate 	/* If the block number is past the end, return an error */
1736*0Sstevel@tonic-gate 	if ((phys_blkno > dkm->dkl_nblk)) {
1737*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L3, FDEM_STRA,
1738*0Sstevel@tonic-gate 		    (C, "fd%d: block %ld is past the end! (nblk=%d)\n",
1739*0Sstevel@tonic-gate 		    0, (long)bp->b_blkno, dkm->dkl_nblk));
1740*0Sstevel@tonic-gate 		bp->b_error = ENOSPC;
1741*0Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount;
1742*0Sstevel@tonic-gate 		bp->b_flags |= B_ERROR;
1743*0Sstevel@tonic-gate 		biodone(bp);
1744*0Sstevel@tonic-gate 		return (0);
1745*0Sstevel@tonic-gate 	}
1746*0Sstevel@tonic-gate 
1747*0Sstevel@tonic-gate 	/* if at end of file, skip out now */
1748*0Sstevel@tonic-gate 	if (phys_blkno == dkm->dkl_nblk) {
1749*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_STRA,
1750*0Sstevel@tonic-gate 		    (C, "b_blkno is at the end!\n"));
1751*0Sstevel@tonic-gate 
1752*0Sstevel@tonic-gate 		if ((bp->b_flags & B_READ) == 0) {
1753*0Sstevel@tonic-gate 			/* a write needs to get an error! */
1754*0Sstevel@tonic-gate 			bp->b_error = ENOSPC;
1755*0Sstevel@tonic-gate 			bp->b_flags |= B_ERROR;
1756*0Sstevel@tonic-gate 
1757*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_STRA,
1758*0Sstevel@tonic-gate 			    (C, "block is at end and this is a write\n"));
1759*0Sstevel@tonic-gate 
1760*0Sstevel@tonic-gate 		}
1761*0Sstevel@tonic-gate 
1762*0Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount;
1763*0Sstevel@tonic-gate 		biodone(bp);
1764*0Sstevel@tonic-gate 		return (0);
1765*0Sstevel@tonic-gate 	}
1766*0Sstevel@tonic-gate 
1767*0Sstevel@tonic-gate 	/* if operation not a multiple of sector size, is error! */
1768*0Sstevel@tonic-gate 	if (bp->b_bcount % un->un_chars->fdc_sec_size)	{
1769*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L3, FDEM_STRA,
1770*0Sstevel@tonic-gate 		    (C, "fd%d: requested transfer size(0x%lx) is not"
1771*0Sstevel@tonic-gate 			" multiple of sector size(0x%x)\n", 0,
1772*0Sstevel@tonic-gate 			bp->b_bcount, un->un_chars->fdc_sec_size));
1773*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L3, FDEM_STRA,
1774*0Sstevel@tonic-gate 		    (C, "	b_blkno=0x%lx b_flags=0x%x\n",
1775*0Sstevel@tonic-gate 		    (long)bp->b_blkno, bp->b_flags));
1776*0Sstevel@tonic-gate 		bp->b_error = EINVAL;
1777*0Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount;
1778*0Sstevel@tonic-gate 		bp->b_flags |= B_ERROR;
1779*0Sstevel@tonic-gate 		biodone(bp);
1780*0Sstevel@tonic-gate 		return (0);
1781*0Sstevel@tonic-gate 
1782*0Sstevel@tonic-gate 	}
1783*0Sstevel@tonic-gate 
1784*0Sstevel@tonic-gate 	/*
1785*0Sstevel@tonic-gate 	 * Put the buf request in the controller's queue, FIFO.
1786*0Sstevel@tonic-gate 	 */
1787*0Sstevel@tonic-gate 	bp->av_forw = 0;
1788*0Sstevel@tonic-gate 	sema_p(&fdc->c_ocsem);
1789*0Sstevel@tonic-gate 
1790*0Sstevel@tonic-gate 	(void) pm_busy_component(fdc->c_dip, 0);
1791*0Sstevel@tonic-gate 
1792*0Sstevel@tonic-gate 	mutex_enter(&fdc->c_lolock);
1793*0Sstevel@tonic-gate 
1794*0Sstevel@tonic-gate 	CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc);
1795*0Sstevel@tonic-gate 
1796*0Sstevel@tonic-gate 	if (fdc->c_un->un_state == FD_STATE_STOPPED) {
1797*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
1798*0Sstevel@tonic-gate 		if ((pm_raise_power(fdc->c_dip, 0, PM_LEVEL_ON))
1799*0Sstevel@tonic-gate 						!= DDI_SUCCESS) {
1800*0Sstevel@tonic-gate 				sema_v(&fdc->c_ocsem);
1801*0Sstevel@tonic-gate 				(void) pm_idle_component(fdc->c_dip, 0);
1802*0Sstevel@tonic-gate 				bp->b_error = EIO;
1803*0Sstevel@tonic-gate 				bp->b_resid = bp->b_bcount;
1804*0Sstevel@tonic-gate 				bp->b_flags |= B_ERROR;
1805*0Sstevel@tonic-gate 				biodone(bp);
1806*0Sstevel@tonic-gate 				return (0);
1807*0Sstevel@tonic-gate 		} else {
1808*0Sstevel@tonic-gate 			mutex_enter(&fdc->c_lolock);
1809*0Sstevel@tonic-gate 		}
1810*0Sstevel@tonic-gate 	}
1811*0Sstevel@tonic-gate 	if (un->un_iostat) {
1812*0Sstevel@tonic-gate 		kstat_waitq_enter(KIOSP);
1813*0Sstevel@tonic-gate 	}
1814*0Sstevel@tonic-gate 	if (fdc->c_actf)
1815*0Sstevel@tonic-gate 		fdc->c_actl->av_forw = bp;
1816*0Sstevel@tonic-gate 	else
1817*0Sstevel@tonic-gate 		fdc->c_actf = bp;
1818*0Sstevel@tonic-gate 	fdc->c_actl = bp;
1819*0Sstevel@tonic-gate 
1820*0Sstevel@tonic-gate 
1821*0Sstevel@tonic-gate 	/* call fdstart to start the transfer */
1822*0Sstevel@tonic-gate 	fdstart(fdc);
1823*0Sstevel@tonic-gate 
1824*0Sstevel@tonic-gate 	mutex_exit(&fdc->c_lolock);
1825*0Sstevel@tonic-gate 	sema_v(&fdc->c_ocsem);
1826*0Sstevel@tonic-gate 	(void) pm_idle_component(fdc->c_dip, 0);
1827*0Sstevel@tonic-gate 	return (0);
1828*0Sstevel@tonic-gate }
1829*0Sstevel@tonic-gate 
1830*0Sstevel@tonic-gate /* ARGSUSED2 */
1831*0Sstevel@tonic-gate static int
1832*0Sstevel@tonic-gate fd_read(dev_t dev, struct uio *uio, cred_t *cred_p)
1833*0Sstevel@tonic-gate {
1834*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RDWR, (C, "fd_read\n"));
1835*0Sstevel@tonic-gate 	return (physio(fd_strategy, NULL, dev, B_READ, minphys, uio));
1836*0Sstevel@tonic-gate }
1837*0Sstevel@tonic-gate 
1838*0Sstevel@tonic-gate /* ARGSUSED2 */
1839*0Sstevel@tonic-gate static int
1840*0Sstevel@tonic-gate fd_write(dev_t dev, struct uio *uio, cred_t *cred_p)
1841*0Sstevel@tonic-gate {
1842*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RDWR, (C, "fd_write\n"));
1843*0Sstevel@tonic-gate 	return (physio(fd_strategy, NULL, dev, B_WRITE, minphys, uio));
1844*0Sstevel@tonic-gate }
1845*0Sstevel@tonic-gate 
1846*0Sstevel@tonic-gate static void
1847*0Sstevel@tonic-gate fdmotoff(void *arg)
1848*0Sstevel@tonic-gate {
1849*0Sstevel@tonic-gate 	struct fdctlr *fdc = arg;
1850*0Sstevel@tonic-gate 	int unit = fdc->c_un->un_unit_no;
1851*0Sstevel@tonic-gate 
1852*0Sstevel@tonic-gate 	mutex_enter(&fdc->c_lolock);
1853*0Sstevel@tonic-gate 
1854*0Sstevel@tonic-gate 	/* Just return if we're about to call untimeout */
1855*0Sstevel@tonic-gate 	if (fdc->c_mtimeid == 0) {
1856*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
1857*0Sstevel@tonic-gate 		return;
1858*0Sstevel@tonic-gate 	}
1859*0Sstevel@tonic-gate 
1860*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_MOFF, (C, "fdmotoff\n"));
1861*0Sstevel@tonic-gate 
1862*0Sstevel@tonic-gate 	fdc->c_mtimeid = 0;
1863*0Sstevel@tonic-gate 
1864*0Sstevel@tonic-gate 	if (!(Msr(fdc) & CB) && (Dor(fdc) & (MOTEN(unit)))) {
1865*0Sstevel@tonic-gate 		/* LINTED */
1866*0Sstevel@tonic-gate 		Set_dor(fdc, MOTEN(unit), 0);
1867*0Sstevel@tonic-gate 	}
1868*0Sstevel@tonic-gate 
1869*0Sstevel@tonic-gate 	mutex_exit(&fdc->c_lolock);
1870*0Sstevel@tonic-gate }
1871*0Sstevel@tonic-gate 
1872*0Sstevel@tonic-gate /* ARGSUSED */
1873*0Sstevel@tonic-gate static int
1874*0Sstevel@tonic-gate fd_ioctl(dev_t dev, int cmd, intptr_t arg, int flag,
1875*0Sstevel@tonic-gate 	cred_t *cred_p, int *rval_p)
1876*0Sstevel@tonic-gate {
1877*0Sstevel@tonic-gate 	union {
1878*0Sstevel@tonic-gate 		struct dk_cinfo dki;
1879*0Sstevel@tonic-gate 		struct dk_geom dkg;
1880*0Sstevel@tonic-gate 		struct dk_allmap32 dka;
1881*0Sstevel@tonic-gate 		struct fd_char fdchar;
1882*0Sstevel@tonic-gate 		struct fd_drive drvchar;
1883*0Sstevel@tonic-gate 		int	temp;
1884*0Sstevel@tonic-gate 	} cpy;
1885*0Sstevel@tonic-gate 
1886*0Sstevel@tonic-gate 	struct vtoc	vtoc;
1887*0Sstevel@tonic-gate 	struct fdunit *un;
1888*0Sstevel@tonic-gate 	struct fdctlr *fdc;
1889*0Sstevel@tonic-gate 	int unit, dkunit;
1890*0Sstevel@tonic-gate 	int err = 0;
1891*0Sstevel@tonic-gate 	uint_t	sec_size;
1892*0Sstevel@tonic-gate 	enum dkio_state state;
1893*0Sstevel@tonic-gate 	int	transfer_rate;
1894*0Sstevel@tonic-gate 
1895*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_IOCT,
1896*0Sstevel@tonic-gate 	    (C, "fd_ioctl: cmd 0x%x, arg 0x%lx\n", cmd, (long)arg));
1897*0Sstevel@tonic-gate 
1898*0Sstevel@tonic-gate 	/* The minor number should always be 0 */
1899*0Sstevel@tonic-gate 	if (FDUNIT(dev) != 0)
1900*0Sstevel@tonic-gate 		return (ENXIO);
1901*0Sstevel@tonic-gate 
1902*0Sstevel@tonic-gate 	fdc = fd_getctlr(dev);
1903*0Sstevel@tonic-gate 	unit = fdc->c_un->un_unit_no;
1904*0Sstevel@tonic-gate 	un = fdc->c_un;
1905*0Sstevel@tonic-gate 	sec_size = un->un_chars->fdc_sec_size;
1906*0Sstevel@tonic-gate 	bzero(&cpy, sizeof (cpy));
1907*0Sstevel@tonic-gate 
1908*0Sstevel@tonic-gate 	switch (cmd) {
1909*0Sstevel@tonic-gate 	case DKIOCINFO:
1910*0Sstevel@tonic-gate 		cpy.dki.dki_addr = 0;
1911*0Sstevel@tonic-gate 
1912*0Sstevel@tonic-gate 		/*
1913*0Sstevel@tonic-gate 		 * The meaning of the dki_slave and dki_unit fields
1914*0Sstevel@tonic-gate 		 * is unclear.  The sparc floppy driver follows the same
1915*0Sstevel@tonic-gate 		 * convention as sd.c in that the instance number is
1916*0Sstevel@tonic-gate 		 * returned in the dki_cnum field.  The dki_slave field is
1917*0Sstevel@tonic-gate 		 * ignored.
1918*0Sstevel@tonic-gate 		 *
1919*0Sstevel@tonic-gate 		 * The dki_cnum contains the controller instance
1920*0Sstevel@tonic-gate 		 * and its value can be any positive number. Even
1921*0Sstevel@tonic-gate 		 * though currently Sparc platforms only support
1922*0Sstevel@tonic-gate 		 * one controller, the controller instance number
1923*0Sstevel@tonic-gate 		 * can be any number since it is assigned by the
1924*0Sstevel@tonic-gate 		 * system depending on the device properties.
1925*0Sstevel@tonic-gate 		 */
1926*0Sstevel@tonic-gate 
1927*0Sstevel@tonic-gate 		cpy.dki.dki_cnum = FDCTLR(dev);
1928*0Sstevel@tonic-gate 
1929*0Sstevel@tonic-gate 		/*
1930*0Sstevel@tonic-gate 		 * Sparc platforms support only one floppy drive.
1931*0Sstevel@tonic-gate 		 * The device node for the controller is the same as
1932*0Sstevel@tonic-gate 		 * the device node for the drive.  The x86 driver is
1933*0Sstevel@tonic-gate 		 * different in that it has a node for the controller
1934*0Sstevel@tonic-gate 		 * and a child node for each drive. Since Sparc supports
1935*0Sstevel@tonic-gate 		 * only one drive, the unit number will always be zero.
1936*0Sstevel@tonic-gate 		 */
1937*0Sstevel@tonic-gate 
1938*0Sstevel@tonic-gate 		cpy.dki.dki_unit = FDUNIT(dev);
1939*0Sstevel@tonic-gate 
1940*0Sstevel@tonic-gate 		/*
1941*0Sstevel@tonic-gate 		 * The meaning of the dki_slave field is unclear.
1942*0Sstevel@tonic-gate 		 * So, I will leave it set to 0.
1943*0Sstevel@tonic-gate 		 */
1944*0Sstevel@tonic-gate 
1945*0Sstevel@tonic-gate 		cpy.dki.dki_slave = 0;
1946*0Sstevel@tonic-gate 
1947*0Sstevel@tonic-gate 		cpy.dki.dki_ctype = (ushort_t)-1;
1948*0Sstevel@tonic-gate 		if (fdc->c_fdtype & FDCTYPE_82077)
1949*0Sstevel@tonic-gate 			cpy.dki.dki_ctype = DKC_INTEL82077;
1950*0Sstevel@tonic-gate 		cpy.dki.dki_flags = DKI_FMTTRK;
1951*0Sstevel@tonic-gate 		cpy.dki.dki_partition = FDPARTITION(dev);
1952*0Sstevel@tonic-gate 		cpy.dki.dki_maxtransfer = maxphys / DEV_BSIZE;
1953*0Sstevel@tonic-gate 		if (ddi_copyout((caddr_t)&cpy.dki, (caddr_t)arg,
1954*0Sstevel@tonic-gate 						sizeof (cpy.dki), flag))
1955*0Sstevel@tonic-gate 			err = EFAULT;
1956*0Sstevel@tonic-gate 		break;
1957*0Sstevel@tonic-gate 	case DKIOCGGEOM:
1958*0Sstevel@tonic-gate 		cpy.dkg.dkg_ncyl = un->un_chars->fdc_ncyl;
1959*0Sstevel@tonic-gate 		cpy.dkg.dkg_nhead = un->un_chars->fdc_nhead;
1960*0Sstevel@tonic-gate 		cpy.dkg.dkg_nsect = un->un_chars->fdc_secptrack;
1961*0Sstevel@tonic-gate 		cpy.dkg.dkg_intrlv = un->un_label.dkl_intrlv;
1962*0Sstevel@tonic-gate 		cpy.dkg.dkg_rpm = un->un_label.dkl_rpm;
1963*0Sstevel@tonic-gate 		cpy.dkg.dkg_pcyl = un->un_chars->fdc_ncyl;
1964*0Sstevel@tonic-gate 		cpy.dkg.dkg_read_reinstruct =
1965*0Sstevel@tonic-gate 		    (int)(cpy.dkg.dkg_nsect * cpy.dkg.dkg_rpm * 4) / 60000;
1966*0Sstevel@tonic-gate 		cpy.dkg.dkg_write_reinstruct = cpy.dkg.dkg_read_reinstruct;
1967*0Sstevel@tonic-gate 		if (ddi_copyout((caddr_t)&cpy.dkg, (caddr_t)arg,
1968*0Sstevel@tonic-gate 						sizeof (cpy.dkg), flag))
1969*0Sstevel@tonic-gate 			err = EFAULT;
1970*0Sstevel@tonic-gate 		break;
1971*0Sstevel@tonic-gate 	case DKIOCSGEOM:
1972*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L3, FDEM_IOCT,
1973*0Sstevel@tonic-gate 		    (C, "fd_ioctl: DKIOCSGEOM not supported\n"));
1974*0Sstevel@tonic-gate 		err = ENOTTY;
1975*0Sstevel@tonic-gate 		break;
1976*0Sstevel@tonic-gate 
1977*0Sstevel@tonic-gate 	/*
1978*0Sstevel@tonic-gate 	 * return the map of all logical partitions
1979*0Sstevel@tonic-gate 	 */
1980*0Sstevel@tonic-gate 	case DKIOCGAPART:
1981*0Sstevel@tonic-gate 		/*
1982*0Sstevel@tonic-gate 		 * We don't have anything to do if the application is ILP32
1983*0Sstevel@tonic-gate 		 * because the label map has a 32-bit format. Otherwise
1984*0Sstevel@tonic-gate 		 * convert.
1985*0Sstevel@tonic-gate 		 */
1986*0Sstevel@tonic-gate 		if ((flag & DATAMODEL_MASK) == DATAMODEL_ILP32) {
1987*0Sstevel@tonic-gate 			if (ddi_copyout(&un->un_label.dkl_map,
1988*0Sstevel@tonic-gate 				(void *)arg, sizeof (struct dk_allmap32), flag))
1989*0Sstevel@tonic-gate 				err = EFAULT;
1990*0Sstevel@tonic-gate 		}
1991*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
1992*0Sstevel@tonic-gate 		else {
1993*0Sstevel@tonic-gate 			struct dk_allmap dk_allmap;
1994*0Sstevel@tonic-gate 
1995*0Sstevel@tonic-gate 			ASSERT((flag & DATAMODEL_MASK) == DATAMODEL_LP64);
1996*0Sstevel@tonic-gate 			for (dkunit = 0; dkunit < NDKMAP; dkunit++) {
1997*0Sstevel@tonic-gate 				dk_allmap.dka_map[dkunit].dkl_cylno =
1998*0Sstevel@tonic-gate 				    un->un_label.dkl_map[dkunit].dkl_cylno;
1999*0Sstevel@tonic-gate 				dk_allmap.dka_map[dkunit].dkl_nblk =
2000*0Sstevel@tonic-gate 				    un->un_label.dkl_map[dkunit].dkl_nblk;
2001*0Sstevel@tonic-gate 			}
2002*0Sstevel@tonic-gate 			if (ddi_copyout(&dk_allmap, (void *)arg,
2003*0Sstevel@tonic-gate 					sizeof (struct dk_allmap), flag))
2004*0Sstevel@tonic-gate 				err = EFAULT;
2005*0Sstevel@tonic-gate 		}
2006*0Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
2007*0Sstevel@tonic-gate 		break;
2008*0Sstevel@tonic-gate 
2009*0Sstevel@tonic-gate 	/*
2010*0Sstevel@tonic-gate 	 * Set the map of all logical partitions
2011*0Sstevel@tonic-gate 	 */
2012*0Sstevel@tonic-gate 	case DKIOCSAPART:
2013*0Sstevel@tonic-gate 		if ((flag & DATAMODEL_MASK) == DATAMODEL_ILP32) {
2014*0Sstevel@tonic-gate 			if (ddi_copyin((const void *)arg, &cpy.dka,
2015*0Sstevel@tonic-gate 			    sizeof (cpy.dka), flag))
2016*0Sstevel@tonic-gate 				return (EFAULT);
2017*0Sstevel@tonic-gate 			else {
2018*0Sstevel@tonic-gate 				mutex_enter(&fdc->c_lolock);
2019*0Sstevel@tonic-gate 				for (dkunit = 0; dkunit < NDKMAP; dkunit++) {
2020*0Sstevel@tonic-gate 					un->un_label.dkl_map[dkunit] =
2021*0Sstevel@tonic-gate 						cpy.dka.dka_map[dkunit];
2022*0Sstevel@tonic-gate 				}
2023*0Sstevel@tonic-gate 				mutex_exit(&fdc->c_lolock);
2024*0Sstevel@tonic-gate 			}
2025*0Sstevel@tonic-gate 		}
2026*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
2027*0Sstevel@tonic-gate 		else {
2028*0Sstevel@tonic-gate 			struct dk_allmap dk_allmap;
2029*0Sstevel@tonic-gate 
2030*0Sstevel@tonic-gate 			ASSERT((flag & DATAMODEL_MASK) == DATAMODEL_LP64);
2031*0Sstevel@tonic-gate 			if (ddi_copyin((const void *)arg, &dk_allmap,
2032*0Sstevel@tonic-gate 			    sizeof (dk_allmap), flag))
2033*0Sstevel@tonic-gate 				return (EFAULT);
2034*0Sstevel@tonic-gate 			else {
2035*0Sstevel@tonic-gate 				mutex_enter(&fdc->c_lolock);
2036*0Sstevel@tonic-gate 				for (dkunit = 0; dkunit < NDKMAP; dkunit++) {
2037*0Sstevel@tonic-gate 					un->un_label.dkl_map[dkunit].dkl_cylno =
2038*0Sstevel@tonic-gate 					    dk_allmap.dka_map[dkunit].dkl_cylno;
2039*0Sstevel@tonic-gate 					un->un_label.dkl_map[dkunit].dkl_nblk =
2040*0Sstevel@tonic-gate 					    dk_allmap.dka_map[dkunit].dkl_nblk;
2041*0Sstevel@tonic-gate 				}
2042*0Sstevel@tonic-gate 				mutex_exit(&fdc->c_lolock);
2043*0Sstevel@tonic-gate 			}
2044*0Sstevel@tonic-gate 		}
2045*0Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
2046*0Sstevel@tonic-gate 		break;
2047*0Sstevel@tonic-gate 
2048*0Sstevel@tonic-gate 	case DKIOCGVTOC:
2049*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_lolock);
2050*0Sstevel@tonic-gate 
2051*0Sstevel@tonic-gate 		/*
2052*0Sstevel@tonic-gate 		 * Exit if the diskette has no label.
2053*0Sstevel@tonic-gate 		 * Also, get the label to make sure the
2054*0Sstevel@tonic-gate 		 * correct one is being used since the diskette
2055*0Sstevel@tonic-gate 		 * may have changed
2056*0Sstevel@tonic-gate 		 */
2057*0Sstevel@tonic-gate 		if (fdgetlabel(fdc, unit)) {
2058*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_lolock);
2059*0Sstevel@tonic-gate 			err = EINVAL;
2060*0Sstevel@tonic-gate 			break;
2061*0Sstevel@tonic-gate 		}
2062*0Sstevel@tonic-gate 
2063*0Sstevel@tonic-gate 		/* Build a vtoc from the diskette's label */
2064*0Sstevel@tonic-gate 		fd_build_user_vtoc(un, &vtoc);
2065*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
2066*0Sstevel@tonic-gate 
2067*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
2068*0Sstevel@tonic-gate 		switch (ddi_model_convert_from(flag & FMODELS)) {
2069*0Sstevel@tonic-gate 		case DDI_MODEL_ILP32: {
2070*0Sstevel@tonic-gate 			struct vtoc32 vtoc32;
2071*0Sstevel@tonic-gate 
2072*0Sstevel@tonic-gate 			vtoctovtoc32(vtoc, vtoc32);
2073*0Sstevel@tonic-gate 			if (ddi_copyout(&vtoc32, (void *)arg,
2074*0Sstevel@tonic-gate 			    sizeof (struct vtoc32), flag))
2075*0Sstevel@tonic-gate 				return (EFAULT);
2076*0Sstevel@tonic-gate 			break;
2077*0Sstevel@tonic-gate 		}
2078*0Sstevel@tonic-gate 
2079*0Sstevel@tonic-gate 		case DDI_MODEL_NONE:
2080*0Sstevel@tonic-gate 			if (ddi_copyout(&vtoc, (void *)arg,
2081*0Sstevel@tonic-gate 			    sizeof (vtoc), flag))
2082*0Sstevel@tonic-gate 				return (EFAULT);
2083*0Sstevel@tonic-gate 			break;
2084*0Sstevel@tonic-gate 		}
2085*0Sstevel@tonic-gate #else /* ! _MULTI_DATAMODEL */
2086*0Sstevel@tonic-gate 		if (ddi_copyout(&vtoc, (void *)arg, sizeof (vtoc), flag))
2087*0Sstevel@tonic-gate 			return (EFAULT);
2088*0Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
2089*0Sstevel@tonic-gate 		break;
2090*0Sstevel@tonic-gate 
2091*0Sstevel@tonic-gate 	case DKIOCSVTOC:
2092*0Sstevel@tonic-gate 
2093*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
2094*0Sstevel@tonic-gate 		switch (ddi_model_convert_from(flag & FMODELS)) {
2095*0Sstevel@tonic-gate 		case DDI_MODEL_ILP32: {
2096*0Sstevel@tonic-gate 			struct vtoc32 vtoc32;
2097*0Sstevel@tonic-gate 
2098*0Sstevel@tonic-gate 			if (ddi_copyin((const void *)arg, &vtoc32,
2099*0Sstevel@tonic-gate 			    sizeof (struct vtoc32), flag)) {
2100*0Sstevel@tonic-gate 				return (EFAULT);
2101*0Sstevel@tonic-gate 			}
2102*0Sstevel@tonic-gate 			vtoc32tovtoc(vtoc32, vtoc);
2103*0Sstevel@tonic-gate 			break;
2104*0Sstevel@tonic-gate 		}
2105*0Sstevel@tonic-gate 
2106*0Sstevel@tonic-gate 		case DDI_MODEL_NONE:
2107*0Sstevel@tonic-gate 			if (ddi_copyin((const void *)arg, &vtoc,
2108*0Sstevel@tonic-gate 			    sizeof (vtoc), flag)) {
2109*0Sstevel@tonic-gate 				return (EFAULT);
2110*0Sstevel@tonic-gate 			}
2111*0Sstevel@tonic-gate 			break;
2112*0Sstevel@tonic-gate 		}
2113*0Sstevel@tonic-gate #else /* ! _MULTI_DATAMODEL */
2114*0Sstevel@tonic-gate 		if (ddi_copyin((const void *)arg, &vtoc, sizeof (vtoc), flag))
2115*0Sstevel@tonic-gate 			return (EFAULT);
2116*0Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
2117*0Sstevel@tonic-gate 
2118*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_lolock);
2119*0Sstevel@tonic-gate 
2120*0Sstevel@tonic-gate 		/*
2121*0Sstevel@tonic-gate 		 * The characteristics structure must be filled in because
2122*0Sstevel@tonic-gate 		 * it helps build the vtoc.
2123*0Sstevel@tonic-gate 		 */
2124*0Sstevel@tonic-gate 		if ((un->un_chars->fdc_ncyl == 0) ||
2125*0Sstevel@tonic-gate 				(un->un_chars->fdc_nhead == 0) ||
2126*0Sstevel@tonic-gate 				(un->un_chars->fdc_secptrack == 0)) {
2127*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_lolock);
2128*0Sstevel@tonic-gate 			err = EINVAL;
2129*0Sstevel@tonic-gate 			break;
2130*0Sstevel@tonic-gate 		}
2131*0Sstevel@tonic-gate 
2132*0Sstevel@tonic-gate 		if ((err = fd_build_label_vtoc(un, &vtoc)) != 0) {
2133*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_lolock);
2134*0Sstevel@tonic-gate 			break;
2135*0Sstevel@tonic-gate 		}
2136*0Sstevel@tonic-gate 
2137*0Sstevel@tonic-gate 		(void) pm_busy_component(fdc->c_dip, 0);
2138*0Sstevel@tonic-gate 
2139*0Sstevel@tonic-gate 		err = fdrw(fdc, unit, FDWRITE, 0, 0, 1,
2140*0Sstevel@tonic-gate 		    (caddr_t)&un->un_label, sizeof (struct dk_label));
2141*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
2142*0Sstevel@tonic-gate 		(void) pm_idle_component(fdc->c_dip, 0);
2143*0Sstevel@tonic-gate 		break;
2144*0Sstevel@tonic-gate 
2145*0Sstevel@tonic-gate 	case DKIOCSTATE:
2146*0Sstevel@tonic-gate 		if (ddi_copyin((caddr_t)arg, (caddr_t)&state,
2147*0Sstevel@tonic-gate 					sizeof (int), flag)) {
2148*0Sstevel@tonic-gate 			err = EFAULT;
2149*0Sstevel@tonic-gate 			break;
2150*0Sstevel@tonic-gate 		}
2151*0Sstevel@tonic-gate 		(void) pm_busy_component(fdc->c_dip, 0);
2152*0Sstevel@tonic-gate 
2153*0Sstevel@tonic-gate 		err = fd_check_media(dev, state);
2154*0Sstevel@tonic-gate 		(void) pm_idle_component(fdc->c_dip, 0);
2155*0Sstevel@tonic-gate 
2156*0Sstevel@tonic-gate 		if (ddi_copyout((caddr_t)&un->un_media_state,
2157*0Sstevel@tonic-gate 					(caddr_t)arg, sizeof (int), flag))
2158*0Sstevel@tonic-gate 			err = EFAULT;
2159*0Sstevel@tonic-gate 		break;
2160*0Sstevel@tonic-gate 
2161*0Sstevel@tonic-gate 	case FDIOGCHAR:
2162*0Sstevel@tonic-gate 		if (ddi_copyout((caddr_t)un->un_chars, (caddr_t)arg,
2163*0Sstevel@tonic-gate 					sizeof (struct fd_char), flag))
2164*0Sstevel@tonic-gate 			err = EFAULT;
2165*0Sstevel@tonic-gate 		break;
2166*0Sstevel@tonic-gate 
2167*0Sstevel@tonic-gate 	case FDIOSCHAR:
2168*0Sstevel@tonic-gate 		if (ddi_copyin((caddr_t)arg, (caddr_t)&cpy.fdchar,
2169*0Sstevel@tonic-gate 				sizeof (struct fd_char), flag)) {
2170*0Sstevel@tonic-gate 			err = EFAULT;
2171*0Sstevel@tonic-gate 			break;
2172*0Sstevel@tonic-gate 		}
2173*0Sstevel@tonic-gate 
2174*0Sstevel@tonic-gate 		/*
2175*0Sstevel@tonic-gate 		 * Check the fields in the fdchar structure that are either
2176*0Sstevel@tonic-gate 		 * driver or controller dependent.
2177*0Sstevel@tonic-gate 		 */
2178*0Sstevel@tonic-gate 
2179*0Sstevel@tonic-gate 		transfer_rate = cpy.fdchar.fdc_transfer_rate;
2180*0Sstevel@tonic-gate 		if ((transfer_rate != 500) && (transfer_rate != 300) &&
2181*0Sstevel@tonic-gate 		    (transfer_rate != 250) && (transfer_rate != 1000)) {
2182*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L3, FDEM_IOCT,
2183*0Sstevel@tonic-gate 			(C, "fd_ioctl: FDIOSCHAR odd transfer rate %d\n",
2184*0Sstevel@tonic-gate 			    cpy.fdchar.fdc_transfer_rate));
2185*0Sstevel@tonic-gate 			err = EINVAL;
2186*0Sstevel@tonic-gate 			break;
2187*0Sstevel@tonic-gate 		}
2188*0Sstevel@tonic-gate 
2189*0Sstevel@tonic-gate 		if ((cpy.fdchar.fdc_nhead < 1) ||
2190*0Sstevel@tonic-gate 				(cpy.fdchar.fdc_nhead > 2)) {
2191*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L3, FDEM_IOCT,
2192*0Sstevel@tonic-gate 			(C, "fd_ioctl: FDIOSCHAR bad no. of heads %d\n",
2193*0Sstevel@tonic-gate 			    cpy.fdchar.fdc_nhead));
2194*0Sstevel@tonic-gate 			err = EINVAL;
2195*0Sstevel@tonic-gate 			break;
2196*0Sstevel@tonic-gate 		}
2197*0Sstevel@tonic-gate 
2198*0Sstevel@tonic-gate 		/*
2199*0Sstevel@tonic-gate 		 * The number of cylinders must be between 0 and 255
2200*0Sstevel@tonic-gate 		 */
2201*0Sstevel@tonic-gate 		if ((cpy.fdchar.fdc_ncyl < 0) || (cpy.fdchar.fdc_ncyl > 255)) {
2202*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L3, FDEM_IOCT,
2203*0Sstevel@tonic-gate 			(C, "fd_ioctl: FDIOSCHAR bad cyl no %d\n",
2204*0Sstevel@tonic-gate 			    cpy.fdchar.fdc_ncyl));
2205*0Sstevel@tonic-gate 			err = EINVAL;
2206*0Sstevel@tonic-gate 			break;
2207*0Sstevel@tonic-gate 		}
2208*0Sstevel@tonic-gate 
2209*0Sstevel@tonic-gate 		/* Copy the fdchar structure */
2210*0Sstevel@tonic-gate 
2211*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_lolock);
2212*0Sstevel@tonic-gate 		*(un->un_chars) = cpy.fdchar;
2213*0Sstevel@tonic-gate 
2214*0Sstevel@tonic-gate 		un->un_curfdtype = -1;
2215*0Sstevel@tonic-gate 
2216*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
2217*0Sstevel@tonic-gate 
2218*0Sstevel@tonic-gate 		break;
2219*0Sstevel@tonic-gate 	case FDEJECT:  /* eject disk */
2220*0Sstevel@tonic-gate 	case DKIOCEJECT:
2221*0Sstevel@tonic-gate 
2222*0Sstevel@tonic-gate 		/*
2223*0Sstevel@tonic-gate 		 * Fail the ioctl if auto-eject isn't supported
2224*0Sstevel@tonic-gate 		 */
2225*0Sstevel@tonic-gate 		if (fdc->c_un->un_drive->fdd_ejectable == 0) {
2226*0Sstevel@tonic-gate 
2227*0Sstevel@tonic-gate 			err = ENOSYS;
2228*0Sstevel@tonic-gate 
2229*0Sstevel@tonic-gate 		} else {
2230*0Sstevel@tonic-gate 			(void) pm_busy_component(fdc->c_dip, 0);
2231*0Sstevel@tonic-gate 
2232*0Sstevel@tonic-gate 			mutex_enter(&fdc->c_lolock);
2233*0Sstevel@tonic-gate 
2234*0Sstevel@tonic-gate 			CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc);
2235*0Sstevel@tonic-gate 
2236*0Sstevel@tonic-gate 			if (fdc->c_un->un_state == FD_STATE_STOPPED) {
2237*0Sstevel@tonic-gate 				mutex_exit(&fdc->c_lolock);
2238*0Sstevel@tonic-gate 				if ((pm_raise_power(fdc->c_dip, 0,
2239*0Sstevel@tonic-gate 					PM_LEVEL_ON)) != DDI_SUCCESS) {
2240*0Sstevel@tonic-gate 					(void) pm_idle_component(fdc->c_dip, 0);
2241*0Sstevel@tonic-gate 					err = EIO;
2242*0Sstevel@tonic-gate 				}
2243*0Sstevel@tonic-gate 				mutex_enter(&fdc->c_lolock);
2244*0Sstevel@tonic-gate 			}
2245*0Sstevel@tonic-gate 		}
2246*0Sstevel@tonic-gate 		if (err == 0) {
2247*0Sstevel@tonic-gate 			fdselect(fdc, unit, 1);
2248*0Sstevel@tonic-gate 			fdeject(fdc, unit);
2249*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_lolock);
2250*0Sstevel@tonic-gate 		}
2251*0Sstevel@tonic-gate 
2252*0Sstevel@tonic-gate 		(void) pm_idle_component(fdc->c_dip, 0);
2253*0Sstevel@tonic-gate 
2254*0Sstevel@tonic-gate 		/*
2255*0Sstevel@tonic-gate 		 * Make sure the drive is turned off
2256*0Sstevel@tonic-gate 		 */
2257*0Sstevel@tonic-gate 		if (fdc->c_fdtype & FDCTYPE_82077) {
2258*0Sstevel@tonic-gate 			if (fdc->c_mtimeid == 0) {
2259*0Sstevel@tonic-gate 				fdc->c_mtimeid = timeout(fdmotoff, fdc,
2260*0Sstevel@tonic-gate 					Motoff_delay);
2261*0Sstevel@tonic-gate 			}
2262*0Sstevel@tonic-gate 		}
2263*0Sstevel@tonic-gate 
2264*0Sstevel@tonic-gate 		break;
2265*0Sstevel@tonic-gate 	case FDGETCHANGE: /* disk changed */
2266*0Sstevel@tonic-gate 
2267*0Sstevel@tonic-gate 		if (ddi_copyin((caddr_t)arg, (caddr_t)&cpy.temp,
2268*0Sstevel@tonic-gate 						sizeof (int), flag)) {
2269*0Sstevel@tonic-gate 			err = EFAULT;
2270*0Sstevel@tonic-gate 			break;
2271*0Sstevel@tonic-gate 		}
2272*0Sstevel@tonic-gate 
2273*0Sstevel@tonic-gate 		/* zero out the user's parameter */
2274*0Sstevel@tonic-gate 		cpy.temp = 0;
2275*0Sstevel@tonic-gate 
2276*0Sstevel@tonic-gate 		(void) pm_busy_component(fdc->c_dip, 0);
2277*0Sstevel@tonic-gate 
2278*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_lolock);
2279*0Sstevel@tonic-gate 
2280*0Sstevel@tonic-gate 		CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc);
2281*0Sstevel@tonic-gate 
2282*0Sstevel@tonic-gate 		if (fdc->c_un->un_state == FD_STATE_STOPPED) {
2283*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_lolock);
2284*0Sstevel@tonic-gate 			if ((pm_raise_power(fdc->c_dip, 0, PM_LEVEL_ON))
2285*0Sstevel@tonic-gate 						!= DDI_SUCCESS) {
2286*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "Power \
2287*0Sstevel@tonic-gate change failed. \n"));
2288*0Sstevel@tonic-gate 				(void) pm_idle_component(fdc->c_dip, 0);
2289*0Sstevel@tonic-gate 				return (EIO);
2290*0Sstevel@tonic-gate 			}
2291*0Sstevel@tonic-gate 
2292*0Sstevel@tonic-gate 			mutex_enter(&fdc->c_lolock);
2293*0Sstevel@tonic-gate 		}
2294*0Sstevel@tonic-gate 		if (un->un_flags & FDUNIT_CHANGED)
2295*0Sstevel@tonic-gate 			cpy.temp |= FDGC_HISTORY;
2296*0Sstevel@tonic-gate 		else
2297*0Sstevel@tonic-gate 			cpy.temp &= ~FDGC_HISTORY;
2298*0Sstevel@tonic-gate 		un->un_flags &= ~FDUNIT_CHANGED;
2299*0Sstevel@tonic-gate 
2300*0Sstevel@tonic-gate 		if (fd_pollable) {
2301*0Sstevel@tonic-gate 			/*
2302*0Sstevel@tonic-gate 			 * If it's a "pollable" floppy, then we don't
2303*0Sstevel@tonic-gate 			 * have to do all the fdcheckdisk nastyness to
2304*0Sstevel@tonic-gate 			 * figure out if the thing is still there.
2305*0Sstevel@tonic-gate 			 */
2306*0Sstevel@tonic-gate 			if (fdsense_chng(fdc, unit)) {
2307*0Sstevel@tonic-gate 				cpy.temp |= FDGC_CURRENT;
2308*0Sstevel@tonic-gate 			} else {
2309*0Sstevel@tonic-gate 				cpy.temp &= ~FDGC_CURRENT;
2310*0Sstevel@tonic-gate 			}
2311*0Sstevel@tonic-gate 		} else {
2312*0Sstevel@tonic-gate 
2313*0Sstevel@tonic-gate 			if (fdsense_chng(fdc, unit)) {
2314*0Sstevel@tonic-gate 				/*
2315*0Sstevel@tonic-gate 				 * check disk change signal is asserted.
2316*0Sstevel@tonic-gate 				 * Now find out if the floppy is
2317*0Sstevel@tonic-gate 				 * inserted
2318*0Sstevel@tonic-gate 				 */
2319*0Sstevel@tonic-gate 				if (fdcheckdisk(fdc, unit)) {
2320*0Sstevel@tonic-gate 					cpy.temp |= FDGC_CURRENT;
2321*0Sstevel@tonic-gate 				} else {
2322*0Sstevel@tonic-gate 					/*
2323*0Sstevel@tonic-gate 					 * Yes, the floppy was
2324*0Sstevel@tonic-gate 					 * reinserted. Implies
2325*0Sstevel@tonic-gate 					 * floppy change.
2326*0Sstevel@tonic-gate 					 */
2327*0Sstevel@tonic-gate 					cpy.temp &= ~FDGC_CURRENT;
2328*0Sstevel@tonic-gate 					cpy.temp |= FDGC_HISTORY;
2329*0Sstevel@tonic-gate 				}
2330*0Sstevel@tonic-gate 			} else {
2331*0Sstevel@tonic-gate 				cpy.temp &= ~FDGC_CURRENT;
2332*0Sstevel@tonic-gate 			}
2333*0Sstevel@tonic-gate 		}
2334*0Sstevel@tonic-gate 
2335*0Sstevel@tonic-gate 		/*
2336*0Sstevel@tonic-gate 		 * For a pollable floppy, the floppy_change signal
2337*0Sstevel@tonic-gate 		 * reflects whether the floppy is in there or not.
2338*0Sstevel@tonic-gate 		 * We can not detect a floppy change if we don't poll
2339*0Sstevel@tonic-gate 		 * this signal when the floppy is being changed.
2340*0Sstevel@tonic-gate 		 * Because as soon as the floppy is put back, the
2341*0Sstevel@tonic-gate 		 * signal is reset.
2342*0Sstevel@tonic-gate 		 * BUT the pollable floppies are available only on
2343*0Sstevel@tonic-gate 		 * Sparcstation Voyager Voyagers (Gypsy) only and
2344*0Sstevel@tonic-gate 		 * those are motorized floppies. For motorized floppies,
2345*0Sstevel@tonic-gate 		 * the floppy can only (assuming the user doesn't use a
2346*0Sstevel@tonic-gate 		 * pin to take out the floppy) be taken out by
2347*0Sstevel@tonic-gate 		 * issuing 'eject' command which sets the
2348*0Sstevel@tonic-gate 		 * un->un_ejected flag. So, if the following
2349*0Sstevel@tonic-gate 		 * condition is true, we can assume there
2350*0Sstevel@tonic-gate 		 * was a floppy change.
2351*0Sstevel@tonic-gate 		 */
2352*0Sstevel@tonic-gate 		if (un->un_ejected && !(cpy.temp & FDGC_CURRENT)) {
2353*0Sstevel@tonic-gate 			cpy.temp |= FDGC_HISTORY;
2354*0Sstevel@tonic-gate 		}
2355*0Sstevel@tonic-gate 		un->un_ejected = 0;
2356*0Sstevel@tonic-gate 
2357*0Sstevel@tonic-gate 
2358*0Sstevel@tonic-gate 		/* return the write-protection status */
2359*0Sstevel@tonic-gate 		fdgetcsb(fdc);
2360*0Sstevel@tonic-gate 		if (fdsensedrv(fdc, unit) & WP_SR3) {
2361*0Sstevel@tonic-gate 			cpy.temp |= FDGC_CURWPROT;
2362*0Sstevel@tonic-gate 		}
2363*0Sstevel@tonic-gate 		fdretcsb(fdc);
2364*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
2365*0Sstevel@tonic-gate 
2366*0Sstevel@tonic-gate 		if (ddi_copyout((caddr_t)&cpy.temp, (caddr_t)arg,
2367*0Sstevel@tonic-gate 						sizeof (int), flag))
2368*0Sstevel@tonic-gate 			err = EFAULT;
2369*0Sstevel@tonic-gate 		(void) pm_idle_component(fdc->c_dip, 0);
2370*0Sstevel@tonic-gate 		break;
2371*0Sstevel@tonic-gate 
2372*0Sstevel@tonic-gate 	case FDGETDRIVECHAR:
2373*0Sstevel@tonic-gate 
2374*0Sstevel@tonic-gate 		if (ddi_copyin((caddr_t)arg, (caddr_t)&cpy.drvchar,
2375*0Sstevel@tonic-gate 				sizeof (struct fd_drive), flag)) {
2376*0Sstevel@tonic-gate 			err = EFAULT;
2377*0Sstevel@tonic-gate 			break;
2378*0Sstevel@tonic-gate 		}
2379*0Sstevel@tonic-gate 
2380*0Sstevel@tonic-gate 		/*
2381*0Sstevel@tonic-gate 		 * Return the ejectable value based on the FD_MANUAL_EJECT
2382*0Sstevel@tonic-gate 		 * property
2383*0Sstevel@tonic-gate 		 */
2384*0Sstevel@tonic-gate 		cpy.drvchar.fdd_ejectable = fdc->c_un->un_drive->fdd_ejectable;
2385*0Sstevel@tonic-gate 		cpy.drvchar.fdd_maxsearch = nfdtypes; /* 3 - hi m lo density */
2386*0Sstevel@tonic-gate 		if (fd_pollable)	/* pollable device */
2387*0Sstevel@tonic-gate 			cpy.drvchar.fdd_flags |= FDD_POLLABLE;
2388*0Sstevel@tonic-gate 
2389*0Sstevel@tonic-gate 		/* the rest of the fd_drive struct is meaningless to us */
2390*0Sstevel@tonic-gate 
2391*0Sstevel@tonic-gate 		if (ddi_copyout((caddr_t)&cpy.drvchar, (caddr_t)arg,
2392*0Sstevel@tonic-gate 					sizeof (struct fd_drive), flag))
2393*0Sstevel@tonic-gate 			err = EFAULT;
2394*0Sstevel@tonic-gate 		break;
2395*0Sstevel@tonic-gate 
2396*0Sstevel@tonic-gate 	case FDSETDRIVECHAR:
2397*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L3, FDEM_IOCT,
2398*0Sstevel@tonic-gate 		    (C, "fd_ioctl: FDSETDRIVECHAR not supportedn\n"));
2399*0Sstevel@tonic-gate 		err = ENOTTY;
2400*0Sstevel@tonic-gate 		break;
2401*0Sstevel@tonic-gate 
2402*0Sstevel@tonic-gate 	case DKIOCREMOVABLE: {
2403*0Sstevel@tonic-gate 		int	i = 1;
2404*0Sstevel@tonic-gate 
2405*0Sstevel@tonic-gate 		/* no brainer: floppies are always removable */
2406*0Sstevel@tonic-gate 		if (ddi_copyout((caddr_t)&i, (caddr_t)arg, sizeof (int),
2407*0Sstevel@tonic-gate 		    flag)) {
2408*0Sstevel@tonic-gate 			err = EFAULT;
2409*0Sstevel@tonic-gate 		}
2410*0Sstevel@tonic-gate 		break;
2411*0Sstevel@tonic-gate 	}
2412*0Sstevel@tonic-gate 	case DKIOCGMEDIAINFO:
2413*0Sstevel@tonic-gate 		err = fd_get_media_info(un, (caddr_t)arg, flag);
2414*0Sstevel@tonic-gate 		break;
2415*0Sstevel@tonic-gate 
2416*0Sstevel@tonic-gate 
2417*0Sstevel@tonic-gate 	case FDIOCMD:
2418*0Sstevel@tonic-gate 	{
2419*0Sstevel@tonic-gate 		struct fd_cmd fc;
2420*0Sstevel@tonic-gate 		int cyl, hd, spc, spt;
2421*0Sstevel@tonic-gate 		int nblks; /* total no. of blocks */
2422*0Sstevel@tonic-gate 
2423*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
2424*0Sstevel@tonic-gate 		switch (ddi_model_convert_from(flag & FMODELS)) {
2425*0Sstevel@tonic-gate 		case DDI_MODEL_ILP32: {
2426*0Sstevel@tonic-gate 			struct fd_cmd32 fc32;
2427*0Sstevel@tonic-gate 
2428*0Sstevel@tonic-gate 			if (ddi_copyin((const void *)arg, &fc32,
2429*0Sstevel@tonic-gate 			    sizeof (fc32), flag)) {
2430*0Sstevel@tonic-gate 				return (EFAULT);
2431*0Sstevel@tonic-gate 			}
2432*0Sstevel@tonic-gate 			fc.fdc_cmd	= fc32.fdc_cmd;
2433*0Sstevel@tonic-gate 			fc.fdc_flags	= fc32.fdc_flags;
2434*0Sstevel@tonic-gate 			fc.fdc_blkno	= (daddr_t)fc32.fdc_blkno;
2435*0Sstevel@tonic-gate 			fc.fdc_secnt	= fc32.fdc_secnt;
2436*0Sstevel@tonic-gate 			fc.fdc_bufaddr	= (caddr_t)fc32.fdc_bufaddr;
2437*0Sstevel@tonic-gate 			fc.fdc_buflen	= fc32.fdc_buflen;
2438*0Sstevel@tonic-gate 			fc.fdc_cmd	= fc32.fdc_cmd;
2439*0Sstevel@tonic-gate 
2440*0Sstevel@tonic-gate 			break;
2441*0Sstevel@tonic-gate 		}
2442*0Sstevel@tonic-gate 
2443*0Sstevel@tonic-gate 		case DDI_MODEL_NONE:
2444*0Sstevel@tonic-gate 			if (ddi_copyin((const void *)arg, &fc,
2445*0Sstevel@tonic-gate 			    sizeof (fc), flag)) {
2446*0Sstevel@tonic-gate 				return (EFAULT);
2447*0Sstevel@tonic-gate 			}
2448*0Sstevel@tonic-gate 			break;
2449*0Sstevel@tonic-gate 		}
2450*0Sstevel@tonic-gate #else /* ! _MULTI_DATAMODEL */
2451*0Sstevel@tonic-gate 		if (ddi_copyin((const void *)arg, &fc, sizeof (fc), flag)) {
2452*0Sstevel@tonic-gate 			return (EFAULT);
2453*0Sstevel@tonic-gate 		}
2454*0Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
2455*0Sstevel@tonic-gate 
2456*0Sstevel@tonic-gate 		if (fc.fdc_cmd == FDCMD_READ || fc.fdc_cmd == FDCMD_WRITE) {
2457*0Sstevel@tonic-gate 			auto struct iovec aiov;
2458*0Sstevel@tonic-gate 			auto struct uio auio;
2459*0Sstevel@tonic-gate 			struct uio *uio = &auio;
2460*0Sstevel@tonic-gate 
2461*0Sstevel@tonic-gate 			spc = (fc.fdc_cmd == FDCMD_READ)? B_READ: B_WRITE;
2462*0Sstevel@tonic-gate 
2463*0Sstevel@tonic-gate 			bzero(&auio, sizeof (struct uio));
2464*0Sstevel@tonic-gate 			bzero(&aiov, sizeof (struct iovec));
2465*0Sstevel@tonic-gate 			aiov.iov_base = fc.fdc_bufaddr;
2466*0Sstevel@tonic-gate 			aiov.iov_len = (uint_t)fc.fdc_secnt * sec_size;
2467*0Sstevel@tonic-gate 			uio->uio_iov = &aiov;
2468*0Sstevel@tonic-gate 
2469*0Sstevel@tonic-gate 			uio->uio_iovcnt = 1;
2470*0Sstevel@tonic-gate 			uio->uio_resid = aiov.iov_len;
2471*0Sstevel@tonic-gate 			uio->uio_segflg = UIO_USERSPACE;
2472*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L2, FDEM_IOCT,
2473*0Sstevel@tonic-gate 			    (C, "fd_ioctl: call physio\n"));
2474*0Sstevel@tonic-gate 			err = physio(fd_strategy, NULL, dev,
2475*0Sstevel@tonic-gate 			    spc, minphys, uio);
2476*0Sstevel@tonic-gate 			break;
2477*0Sstevel@tonic-gate 		} else if (fc.fdc_cmd != FDCMD_FORMAT_TRACK) {
2478*0Sstevel@tonic-gate 
2479*0Sstevel@tonic-gate 			/*
2480*0Sstevel@tonic-gate 			 * The manpage states that only the FDCMD_WRITE,
2481*0Sstevel@tonic-gate 			 * FDCMD_READ, and the FDCMD_FORMAT_TR are available.
2482*0Sstevel@tonic-gate 			 */
2483*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_IOCT,
2484*0Sstevel@tonic-gate 			    (C, "fd_ioctl: FDIOCMD invalid command\n"));
2485*0Sstevel@tonic-gate 			err = EINVAL;
2486*0Sstevel@tonic-gate 			break;
2487*0Sstevel@tonic-gate 		}
2488*0Sstevel@tonic-gate 
2489*0Sstevel@tonic-gate 		/* The command is FDCMD_FORMAT_TRACK */
2490*0Sstevel@tonic-gate 
2491*0Sstevel@tonic-gate 		spt = un->un_chars->fdc_secptrack;	/* sec/trk */
2492*0Sstevel@tonic-gate 		spc = un->un_chars->fdc_nhead * spt;	/* sec/cyl */
2493*0Sstevel@tonic-gate 		cyl = fc.fdc_blkno / spc;
2494*0Sstevel@tonic-gate 		hd = (fc.fdc_blkno % spc) / spt;
2495*0Sstevel@tonic-gate 
2496*0Sstevel@tonic-gate 		/*
2497*0Sstevel@tonic-gate 		 * Make sure the specified block number is in the correct
2498*0Sstevel@tonic-gate 		 * range. (block numbers start at 0)
2499*0Sstevel@tonic-gate 		 */
2500*0Sstevel@tonic-gate 		nblks = spc * un->un_chars->fdc_ncyl;
2501*0Sstevel@tonic-gate 
2502*0Sstevel@tonic-gate 		if (fc.fdc_blkno < 0 || fc.fdc_blkno > (nblks - 1)) {
2503*0Sstevel@tonic-gate 			err = EINVAL;
2504*0Sstevel@tonic-gate 			break;
2505*0Sstevel@tonic-gate 		}
2506*0Sstevel@tonic-gate 
2507*0Sstevel@tonic-gate 		(void) pm_busy_component(fdc->c_dip, 0);
2508*0Sstevel@tonic-gate 
2509*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_lolock);
2510*0Sstevel@tonic-gate 		CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc);
2511*0Sstevel@tonic-gate 		if (fdc->c_un->un_state == FD_STATE_STOPPED) {
2512*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_lolock);
2513*0Sstevel@tonic-gate 			if ((pm_raise_power(fdc->c_dip, 0, PM_LEVEL_ON))
2514*0Sstevel@tonic-gate 						!= DDI_SUCCESS) {
2515*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "Power \
2516*0Sstevel@tonic-gate change failed. \n"));
2517*0Sstevel@tonic-gate 				(void) pm_idle_component(fdc->c_dip, 0);
2518*0Sstevel@tonic-gate 				return (EIO);
2519*0Sstevel@tonic-gate 			}
2520*0Sstevel@tonic-gate 
2521*0Sstevel@tonic-gate 			mutex_enter(&fdc->c_lolock);
2522*0Sstevel@tonic-gate 		}
2523*0Sstevel@tonic-gate 
2524*0Sstevel@tonic-gate 		if (fdformat(fdc, unit, cyl, hd))
2525*0Sstevel@tonic-gate 			err = EIO;
2526*0Sstevel@tonic-gate 
2527*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
2528*0Sstevel@tonic-gate 		(void) pm_idle_component(fdc->c_dip, 0);
2529*0Sstevel@tonic-gate 
2530*0Sstevel@tonic-gate 		break;
2531*0Sstevel@tonic-gate 	}
2532*0Sstevel@tonic-gate 
2533*0Sstevel@tonic-gate 	case FDRAW:
2534*0Sstevel@tonic-gate 
2535*0Sstevel@tonic-gate 		(void) pm_busy_component(fdc->c_dip, 0);
2536*0Sstevel@tonic-gate 		err = fdrawioctl(fdc, unit, arg, flag);
2537*0Sstevel@tonic-gate 
2538*0Sstevel@tonic-gate 		(void) pm_idle_component(fdc->c_dip, 0);
2539*0Sstevel@tonic-gate 
2540*0Sstevel@tonic-gate 		break;
2541*0Sstevel@tonic-gate #ifdef FD_DEBUG
2542*0Sstevel@tonic-gate 	case IOCTL_DEBUG:
2543*0Sstevel@tonic-gate 		fderrlevel--;
2544*0Sstevel@tonic-gate 		if (fderrlevel < 0)
2545*0Sstevel@tonic-gate 			fderrlevel = 3;
2546*0Sstevel@tonic-gate 		cmn_err(C, "fdioctl: CHANGING debug to %d", fderrlevel);
2547*0Sstevel@tonic-gate 		return (0);
2548*0Sstevel@tonic-gate #endif /* FD_DEBUG */
2549*0Sstevel@tonic-gate 	default:
2550*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L2, FDEM_IOCT,
2551*0Sstevel@tonic-gate 		    (C, "fd_ioctl: invalid ioctl 0x%x\n", cmd));
2552*0Sstevel@tonic-gate 		err = ENOTTY;
2553*0Sstevel@tonic-gate 		break;
2554*0Sstevel@tonic-gate 	}
2555*0Sstevel@tonic-gate 
2556*0Sstevel@tonic-gate 	return (err);
2557*0Sstevel@tonic-gate }
2558*0Sstevel@tonic-gate 
2559*0Sstevel@tonic-gate /*
2560*0Sstevel@tonic-gate  * fdrawioctl
2561*0Sstevel@tonic-gate  *
2562*0Sstevel@tonic-gate  * 	- acquires the low level lock
2563*0Sstevel@tonic-gate  */
2564*0Sstevel@tonic-gate 
2565*0Sstevel@tonic-gate static int
2566*0Sstevel@tonic-gate fdrawioctl(struct fdctlr *fdc, int unit, intptr_t arg, int mode)
2567*0Sstevel@tonic-gate {
2568*0Sstevel@tonic-gate 	struct fd_raw fdr;
2569*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
2570*0Sstevel@tonic-gate 	struct fd_raw32 fdr32;
2571*0Sstevel@tonic-gate #endif
2572*0Sstevel@tonic-gate 	struct fdcsb *csb;
2573*0Sstevel@tonic-gate 	int i, err, flag;
2574*0Sstevel@tonic-gate 	caddr_t fa;
2575*0Sstevel@tonic-gate 	uint_t	fc;
2576*0Sstevel@tonic-gate 	size_t	real_length;
2577*0Sstevel@tonic-gate 	int	res;
2578*0Sstevel@tonic-gate 	ddi_device_acc_attr_t attr;
2579*0Sstevel@tonic-gate 	ddi_acc_handle_t	mem_handle;
2580*0Sstevel@tonic-gate 
2581*0Sstevel@tonic-gate 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
2582*0Sstevel@tonic-gate 	attr.devacc_attr_endian_flags  = DDI_STRUCTURE_BE_ACC;
2583*0Sstevel@tonic-gate 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
2584*0Sstevel@tonic-gate 
2585*0Sstevel@tonic-gate 	ASSERT(fdc->c_un->un_unit_no == unit);
2586*0Sstevel@tonic-gate 
2587*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RAWI,
2588*0Sstevel@tonic-gate 	    (C, "fdrawioctl: cmd[0]=0x%x\n", fdr.fdr_cmd[0]));
2589*0Sstevel@tonic-gate 
2590*0Sstevel@tonic-gate 	flag = B_READ;
2591*0Sstevel@tonic-gate 	err = 0;
2592*0Sstevel@tonic-gate 	fa = NULL;
2593*0Sstevel@tonic-gate 	fc = (uint_t)0;
2594*0Sstevel@tonic-gate 
2595*0Sstevel@tonic-gate 	/* Copy in the arguments */
2596*0Sstevel@tonic-gate 	switch (ddi_model_convert_from(mode)) {
2597*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
2598*0Sstevel@tonic-gate 	case DDI_MODEL_ILP32:
2599*0Sstevel@tonic-gate 		if (ddi_copyin((caddr_t)arg, (caddr_t)&fdr32,
2600*0Sstevel@tonic-gate 		    sizeof (fdr32), mode)) {
2601*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_RAWI,
2602*0Sstevel@tonic-gate 			(C, "fdrawioctl: copyin error, args32\n"));
2603*0Sstevel@tonic-gate 			return (EFAULT);
2604*0Sstevel@tonic-gate 		}
2605*0Sstevel@tonic-gate 		bcopy(fdr32.fdr_cmd, fdr.fdr_cmd, sizeof (fdr.fdr_cmd));
2606*0Sstevel@tonic-gate 		fdr.fdr_cnum = fdr32.fdr_cnum;
2607*0Sstevel@tonic-gate 		bcopy(fdr32.fdr_result, fdr.fdr_result,
2608*0Sstevel@tonic-gate 		    sizeof (fdr.fdr_result));
2609*0Sstevel@tonic-gate 		fdr.fdr_nbytes = fdr32.fdr_nbytes;
2610*0Sstevel@tonic-gate 		fdr.fdr_addr = (caddr_t)fdr32.fdr_addr;
2611*0Sstevel@tonic-gate 		break;
2612*0Sstevel@tonic-gate #endif
2613*0Sstevel@tonic-gate 	default:
2614*0Sstevel@tonic-gate 	case DDI_MODEL_NONE:
2615*0Sstevel@tonic-gate 		if (ddi_copyin((caddr_t)arg, (caddr_t)&fdr,
2616*0Sstevel@tonic-gate 		    sizeof (fdr), mode)) {
2617*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_RAWI,
2618*0Sstevel@tonic-gate 			(C, "fdrawioctl: copyin error, args\n"));
2619*0Sstevel@tonic-gate 			return (EFAULT);
2620*0Sstevel@tonic-gate 		}
2621*0Sstevel@tonic-gate 		break;
2622*0Sstevel@tonic-gate 	}
2623*0Sstevel@tonic-gate 
2624*0Sstevel@tonic-gate 	mutex_enter(&fdc->c_lolock);
2625*0Sstevel@tonic-gate 
2626*0Sstevel@tonic-gate 	CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc);
2627*0Sstevel@tonic-gate 
2628*0Sstevel@tonic-gate 	if (fdc->c_un->un_state == FD_STATE_STOPPED) {
2629*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
2630*0Sstevel@tonic-gate 		if ((pm_raise_power(fdc->c_dip, 0, PM_LEVEL_ON))
2631*0Sstevel@tonic-gate 					!= DDI_SUCCESS) {
2632*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "Power change \
2633*0Sstevel@tonic-gate failed. \n"));
2634*0Sstevel@tonic-gate 
2635*0Sstevel@tonic-gate 			(void) pm_idle_component(fdc->c_dip, 0);
2636*0Sstevel@tonic-gate 			return (EIO);
2637*0Sstevel@tonic-gate 		}
2638*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_lolock);
2639*0Sstevel@tonic-gate 	}
2640*0Sstevel@tonic-gate 
2641*0Sstevel@tonic-gate 	fdgetcsb(fdc);
2642*0Sstevel@tonic-gate 	csb = &fdc->c_csb;
2643*0Sstevel@tonic-gate 	csb->csb_unit = (uchar_t)unit;
2644*0Sstevel@tonic-gate 
2645*0Sstevel@tonic-gate 	/* copy cmd bytes into csb */
2646*0Sstevel@tonic-gate 	for (i = 0; i <= fdr.fdr_cnum; i++)
2647*0Sstevel@tonic-gate 		csb->csb_cmds[i] = fdr.fdr_cmd[i];
2648*0Sstevel@tonic-gate 	csb->csb_ncmds = (uchar_t)fdr.fdr_cnum;
2649*0Sstevel@tonic-gate 
2650*0Sstevel@tonic-gate 	csb->csb_maxretry = 0;	/* let the application deal with errors */
2651*0Sstevel@tonic-gate 	csb->csb_retrys = 0;
2652*0Sstevel@tonic-gate 
2653*0Sstevel@tonic-gate 	switch (fdr.fdr_cmd[0] & 0x0f) {
2654*0Sstevel@tonic-gate 
2655*0Sstevel@tonic-gate 	case FDRAW_SPECIFY:
2656*0Sstevel@tonic-gate 		/*
2657*0Sstevel@tonic-gate 		 * Ensure that the right DMA mode is selected.  There is
2658*0Sstevel@tonic-gate 		 * currently no way for the user to tell if DMA is
2659*0Sstevel@tonic-gate 		 * happening so set the value for the user.
2660*0Sstevel@tonic-gate 		 */
2661*0Sstevel@tonic-gate 
2662*0Sstevel@tonic-gate 		if (fdc->c_fdtype & FDCTYPE_DMA)
2663*0Sstevel@tonic-gate 			csb->csb_cmds[2] = csb->csb_cmds[2] & 0xFE;
2664*0Sstevel@tonic-gate 		else
2665*0Sstevel@tonic-gate 			csb->csb_cmds[2] = csb->csb_cmds[2] | 0x1;
2666*0Sstevel@tonic-gate 
2667*0Sstevel@tonic-gate 		csb->csb_opflags = CSB_OFNORESULTS;
2668*0Sstevel@tonic-gate 		csb->csb_nrslts = 0;
2669*0Sstevel@tonic-gate 		break;
2670*0Sstevel@tonic-gate 
2671*0Sstevel@tonic-gate 	case FDRAW_SENSE_DRV:
2672*0Sstevel@tonic-gate 		/* Insert the appropriate drive number */
2673*0Sstevel@tonic-gate 		csb->csb_cmds[1] = csb->csb_cmds[1] | (unit & DRV_MASK);
2674*0Sstevel@tonic-gate 		csb->csb_opflags = CSB_OFIMMEDIATE;
2675*0Sstevel@tonic-gate 		csb->csb_nrslts = 1;
2676*0Sstevel@tonic-gate 		break;
2677*0Sstevel@tonic-gate 
2678*0Sstevel@tonic-gate 	case FDRAW_REZERO:
2679*0Sstevel@tonic-gate 	case FDRAW_SEEK:
2680*0Sstevel@tonic-gate 		/* Insert the appropriate drive number */
2681*0Sstevel@tonic-gate 		csb->csb_cmds[1] = csb->csb_cmds[1] | (unit & DRV_MASK);
2682*0Sstevel@tonic-gate 		csb->csb_opflags = CSB_OFSEEKOPS + CSB_OFTIMEIT;
2683*0Sstevel@tonic-gate 		csb->csb_nrslts = 2;
2684*0Sstevel@tonic-gate 		break;
2685*0Sstevel@tonic-gate 
2686*0Sstevel@tonic-gate 	case FDRAW_FORMAT:
2687*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_RAWI,
2688*0Sstevel@tonic-gate 			(C, "fdrawioctl: cmd is fdfraw format\n"));
2689*0Sstevel@tonic-gate 
2690*0Sstevel@tonic-gate 		/* Insert the appropriate drive number */
2691*0Sstevel@tonic-gate 		csb->csb_cmds[1] = csb->csb_cmds[1] | (unit & DRV_MASK);
2692*0Sstevel@tonic-gate 		csb->csb_opflags = CSB_OFXFEROPS + CSB_OFTIMEIT;
2693*0Sstevel@tonic-gate 		csb->csb_nrslts = NRBRW;
2694*0Sstevel@tonic-gate 		flag = B_WRITE;
2695*0Sstevel@tonic-gate 
2696*0Sstevel@tonic-gate 		/*
2697*0Sstevel@tonic-gate 		 * Allocate memory for the command.
2698*0Sstevel@tonic-gate 		 * If PIO is being used, then add an extra 16 bytes
2699*0Sstevel@tonic-gate 		 */
2700*0Sstevel@tonic-gate 		if (fdc->c_fdtype & FDCTYPE_DMA) {
2701*0Sstevel@tonic-gate 
2702*0Sstevel@tonic-gate 			fc = (uint_t)(fdr.fdr_nbytes);
2703*0Sstevel@tonic-gate 			mutex_enter(&fdc->c_hilock);
2704*0Sstevel@tonic-gate 
2705*0Sstevel@tonic-gate 			res = ddi_dma_mem_alloc(fdc->c_dmahandle, fc,
2706*0Sstevel@tonic-gate 				&attr, DDI_DMA_STREAMING,
2707*0Sstevel@tonic-gate 				DDI_DMA_DONTWAIT, 0, &fa, &real_length,
2708*0Sstevel@tonic-gate 				&mem_handle);
2709*0Sstevel@tonic-gate 
2710*0Sstevel@tonic-gate 			if (res != DDI_SUCCESS) {
2711*0Sstevel@tonic-gate 				fdretcsb(fdc);
2712*0Sstevel@tonic-gate 				mutex_exit(&fdc->c_lolock);
2713*0Sstevel@tonic-gate 				mutex_exit(&fdc->c_hilock);
2714*0Sstevel@tonic-gate 				return (EIO);
2715*0Sstevel@tonic-gate 			}
2716*0Sstevel@tonic-gate 
2717*0Sstevel@tonic-gate 			fdc->c_csb.csb_read = CSB_WRITE;
2718*0Sstevel@tonic-gate 			if (fdstart_dma(fdc, fa, fc) != 0) {
2719*0Sstevel@tonic-gate 				ddi_dma_mem_free(&mem_handle);
2720*0Sstevel@tonic-gate 				fdretcsb(fdc);
2721*0Sstevel@tonic-gate 				mutex_exit(&fdc->c_lolock);
2722*0Sstevel@tonic-gate 				mutex_exit(&fdc->c_hilock);
2723*0Sstevel@tonic-gate 				return (EIO);
2724*0Sstevel@tonic-gate 			}
2725*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_hilock);
2726*0Sstevel@tonic-gate 
2727*0Sstevel@tonic-gate 		} else {
2728*0Sstevel@tonic-gate 			fc = (uint_t)(fdr.fdr_nbytes + 16);
2729*0Sstevel@tonic-gate 			fa = kmem_zalloc(fc, KM_SLEEP);
2730*0Sstevel@tonic-gate 		}
2731*0Sstevel@tonic-gate 
2732*0Sstevel@tonic-gate 		/* copy in the user's command bytes */
2733*0Sstevel@tonic-gate 		if (ddi_copyin(fdr.fdr_addr, fa,
2734*0Sstevel@tonic-gate 				(uint_t)fdr.fdr_nbytes, mode)) {
2735*0Sstevel@tonic-gate 			fdretcsb(fdc);
2736*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_lolock);
2737*0Sstevel@tonic-gate 
2738*0Sstevel@tonic-gate 			if (fdc->c_fdtype & FDCTYPE_DMA) {
2739*0Sstevel@tonic-gate 				ddi_dma_mem_free(&mem_handle);
2740*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_RAWI,
2741*0Sstevel@tonic-gate 				(C, "fdrawioctl: (err)free dma memory\n"));
2742*0Sstevel@tonic-gate 			} else {
2743*0Sstevel@tonic-gate 				kmem_free(fa, fc);
2744*0Sstevel@tonic-gate 			}
2745*0Sstevel@tonic-gate 
2746*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_RAWI,
2747*0Sstevel@tonic-gate 			(C, "fdrawioctl: ddi_copyin error\n"));
2748*0Sstevel@tonic-gate 			return (EFAULT);
2749*0Sstevel@tonic-gate 		}
2750*0Sstevel@tonic-gate 
2751*0Sstevel@tonic-gate 		break;
2752*0Sstevel@tonic-gate 	case FDRAW_WRCMD:
2753*0Sstevel@tonic-gate 	case FDRAW_WRITEDEL:
2754*0Sstevel@tonic-gate 		flag = B_WRITE;
2755*0Sstevel@tonic-gate 		/* FALLTHROUGH */
2756*0Sstevel@tonic-gate 	case FDRAW_RDCMD:
2757*0Sstevel@tonic-gate 	case FDRAW_READDEL:
2758*0Sstevel@tonic-gate 	case FDRAW_READTRACK:
2759*0Sstevel@tonic-gate 		/* Insert the appropriate drive number */
2760*0Sstevel@tonic-gate 		csb->csb_cmds[1] = csb->csb_cmds[1] | (unit & DRV_MASK);
2761*0Sstevel@tonic-gate 		if (fdc->c_fdtype & FDCTYPE_SB)
2762*0Sstevel@tonic-gate 			csb->csb_cmds[1] |= IPS;
2763*0Sstevel@tonic-gate 		csb->csb_opflags = CSB_OFXFEROPS + CSB_OFTIMEIT;
2764*0Sstevel@tonic-gate 		csb->csb_nrslts = NRBRW;
2765*0Sstevel@tonic-gate 		break;
2766*0Sstevel@tonic-gate 
2767*0Sstevel@tonic-gate 	default:
2768*0Sstevel@tonic-gate 		fdretcsb(fdc);
2769*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
2770*0Sstevel@tonic-gate 		return (EINVAL);
2771*0Sstevel@tonic-gate 	}
2772*0Sstevel@tonic-gate 
2773*0Sstevel@tonic-gate 	if ((csb->csb_opflags & CSB_OFXFEROPS) && (fdr.fdr_nbytes == 0)) {
2774*0Sstevel@tonic-gate 		fdretcsb(fdc);
2775*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
2776*0Sstevel@tonic-gate 		return (EINVAL);
2777*0Sstevel@tonic-gate 	}
2778*0Sstevel@tonic-gate 	csb->csb_opflags |= CSB_OFRAWIOCTL;
2779*0Sstevel@tonic-gate 
2780*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RAWI,
2781*0Sstevel@tonic-gate 		(C, "fdrawioctl: nbytes = %u\n", fdr.fdr_nbytes));
2782*0Sstevel@tonic-gate 
2783*0Sstevel@tonic-gate 	if ((fdr.fdr_cmd[0] & 0x0f) != FDRAW_FORMAT) {
2784*0Sstevel@tonic-gate 		if ((fc = (uint_t)fdr.fdr_nbytes) > 0) {
2785*0Sstevel@tonic-gate 			/*
2786*0Sstevel@tonic-gate 			 * In SunOS 4.X, we used to as_fault things in.
2787*0Sstevel@tonic-gate 			 * We really cannot do this in 5.0/SVr4. Unless
2788*0Sstevel@tonic-gate 			 * someone really believes that speed is of the
2789*0Sstevel@tonic-gate 			 * essence here, it is just much simpler to do
2790*0Sstevel@tonic-gate 			 * this in kernel space and use copyin/copyout.
2791*0Sstevel@tonic-gate 			 */
2792*0Sstevel@tonic-gate 			if (fdc->c_fdtype & FDCTYPE_DMA) {
2793*0Sstevel@tonic-gate 				mutex_enter(&fdc->c_hilock);
2794*0Sstevel@tonic-gate 				res = ddi_dma_mem_alloc(fdc->c_dmahandle, fc,
2795*0Sstevel@tonic-gate 					&attr, DDI_DMA_STREAMING,
2796*0Sstevel@tonic-gate 					DDI_DMA_DONTWAIT, 0, &fa, &real_length,
2797*0Sstevel@tonic-gate 					&mem_handle);
2798*0Sstevel@tonic-gate 
2799*0Sstevel@tonic-gate 				if (res != DDI_SUCCESS) {
2800*0Sstevel@tonic-gate 					fdretcsb(fdc);
2801*0Sstevel@tonic-gate 					mutex_exit(&fdc->c_lolock);
2802*0Sstevel@tonic-gate 					mutex_exit(&fdc->c_hilock);
2803*0Sstevel@tonic-gate 					return (EIO);
2804*0Sstevel@tonic-gate 				}
2805*0Sstevel@tonic-gate 
2806*0Sstevel@tonic-gate 				if (flag == B_WRITE)
2807*0Sstevel@tonic-gate 					fdc->c_csb.csb_read = CSB_WRITE;
2808*0Sstevel@tonic-gate 				else
2809*0Sstevel@tonic-gate 					fdc->c_csb.csb_read = CSB_READ;
2810*0Sstevel@tonic-gate 
2811*0Sstevel@tonic-gate 				if (fdstart_dma(fdc, fa, fc) != 0) {
2812*0Sstevel@tonic-gate 					ddi_dma_mem_free(&mem_handle);
2813*0Sstevel@tonic-gate 					fdretcsb(fdc);
2814*0Sstevel@tonic-gate 					mutex_exit(&fdc->c_lolock);
2815*0Sstevel@tonic-gate 					mutex_exit(&fdc->c_hilock);
2816*0Sstevel@tonic-gate 					return (EIO);
2817*0Sstevel@tonic-gate 				}
2818*0Sstevel@tonic-gate 				mutex_exit(&fdc->c_hilock);
2819*0Sstevel@tonic-gate 
2820*0Sstevel@tonic-gate 			} else {
2821*0Sstevel@tonic-gate 				fa = kmem_zalloc(fc, KM_SLEEP);
2822*0Sstevel@tonic-gate 			}
2823*0Sstevel@tonic-gate 
2824*0Sstevel@tonic-gate 			if (flag == B_WRITE) {
2825*0Sstevel@tonic-gate 				if (ddi_copyin(fdr.fdr_addr, fa, fc, mode)) {
2826*0Sstevel@tonic-gate 					if (fdc->c_fdtype & FDCTYPE_DMA)
2827*0Sstevel@tonic-gate 						ddi_dma_mem_free(&mem_handle);
2828*0Sstevel@tonic-gate 					else
2829*0Sstevel@tonic-gate 						kmem_free(fa, fc);
2830*0Sstevel@tonic-gate 					fdretcsb(fdc);
2831*0Sstevel@tonic-gate 					mutex_exit(&fdc->c_lolock);
2832*0Sstevel@tonic-gate 					FDERRPRINT(FDEP_L1, FDEM_RAWI,
2833*0Sstevel@tonic-gate 					(C, "fdrawioctl: can't copy data\n"));
2834*0Sstevel@tonic-gate 
2835*0Sstevel@tonic-gate 					return (EFAULT);
2836*0Sstevel@tonic-gate 				}
2837*0Sstevel@tonic-gate 			}
2838*0Sstevel@tonic-gate 			csb->csb_addr = fa;
2839*0Sstevel@tonic-gate 			csb->csb_len = fc;
2840*0Sstevel@tonic-gate 		} else {
2841*0Sstevel@tonic-gate 			csb->csb_addr = 0;
2842*0Sstevel@tonic-gate 			csb->csb_len = 0;
2843*0Sstevel@tonic-gate 		}
2844*0Sstevel@tonic-gate 	} else {
2845*0Sstevel@tonic-gate 		csb->csb_addr = fa;
2846*0Sstevel@tonic-gate 		csb->csb_len = fc;
2847*0Sstevel@tonic-gate 	}
2848*0Sstevel@tonic-gate 
2849*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RAWI,
2850*0Sstevel@tonic-gate 	    (C, "cmd: %x %x %x %x %x %x %x %x %x %x\n", csb->csb_cmds[0],
2851*0Sstevel@tonic-gate 	    csb->csb_cmds[1], csb->csb_cmds[2], csb->csb_cmds[3],
2852*0Sstevel@tonic-gate 	    csb->csb_cmds[4], csb->csb_cmds[5], csb->csb_cmds[6],
2853*0Sstevel@tonic-gate 	    csb->csb_cmds[7], csb->csb_cmds[8], csb->csb_cmds[9]));
2854*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RAWI,
2855*0Sstevel@tonic-gate 	    (C, "nbytes: %x, opflags: %x, addr: %p, len: %x\n",
2856*0Sstevel@tonic-gate 	    csb->csb_ncmds, csb->csb_opflags, (void *)csb->csb_addr,
2857*0Sstevel@tonic-gate 	    csb->csb_len));
2858*0Sstevel@tonic-gate 
2859*0Sstevel@tonic-gate 
2860*0Sstevel@tonic-gate 	/*
2861*0Sstevel@tonic-gate 	 * Note that we ignore any error return s from fdexec.
2862*0Sstevel@tonic-gate 	 * This is the way the driver has been, and it may be
2863*0Sstevel@tonic-gate 	 * that the raw ioctl senders simply don't want to
2864*0Sstevel@tonic-gate 	 * see any errors returned in this fashion.
2865*0Sstevel@tonic-gate 	 */
2866*0Sstevel@tonic-gate 
2867*0Sstevel@tonic-gate 	if ((csb->csb_opflags & CSB_OFNORESULTS) ||
2868*0Sstevel@tonic-gate 	    (csb->csb_opflags & CSB_OFIMMEDIATE)) {
2869*0Sstevel@tonic-gate 		(void) fdexec(fdc, 0); /* don't sleep, don't check change */
2870*0Sstevel@tonic-gate 	} else {
2871*0Sstevel@tonic-gate 		(void) fdexec(fdc, FDXC_SLEEP | FDXC_CHECKCHG);
2872*0Sstevel@tonic-gate 	}
2873*0Sstevel@tonic-gate 
2874*0Sstevel@tonic-gate 
2875*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RAWI,
2876*0Sstevel@tonic-gate 	    (C, "rslt: %x %x %x %x %x %x %x %x %x %x\n", csb->csb_rslt[0],
2877*0Sstevel@tonic-gate 	    csb->csb_rslt[1], csb->csb_rslt[2], csb->csb_rslt[3],
2878*0Sstevel@tonic-gate 	    csb->csb_rslt[4], csb->csb_rslt[5], csb->csb_rslt[6],
2879*0Sstevel@tonic-gate 	    csb->csb_rslt[7], csb->csb_rslt[8], csb->csb_rslt[9]));
2880*0Sstevel@tonic-gate 
2881*0Sstevel@tonic-gate 	if ((fdr.fdr_cmd[0] & 0x0f) != FDRAW_FORMAT && fc &&
2882*0Sstevel@tonic-gate 	    flag == B_READ && err == 0) {
2883*0Sstevel@tonic-gate 		if (ddi_copyout(fa, fdr.fdr_addr, fc, mode)) {
2884*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_RAWI,
2885*0Sstevel@tonic-gate 			(C, "fdrawioctl: can't copy read data\n"));
2886*0Sstevel@tonic-gate 
2887*0Sstevel@tonic-gate 			err = EFAULT;
2888*0Sstevel@tonic-gate 		}
2889*0Sstevel@tonic-gate 	}
2890*0Sstevel@tonic-gate 
2891*0Sstevel@tonic-gate 
2892*0Sstevel@tonic-gate 	if (fc) {
2893*0Sstevel@tonic-gate 		if (fdc->c_fdtype & FDCTYPE_DMA) {
2894*0Sstevel@tonic-gate 			ddi_dma_mem_free(&mem_handle);
2895*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_RAWI,
2896*0Sstevel@tonic-gate 				(C, "fdrawioctl: free dma memory\n"));
2897*0Sstevel@tonic-gate 		} else {
2898*0Sstevel@tonic-gate 			kmem_free(fa, fc);
2899*0Sstevel@tonic-gate 		}
2900*0Sstevel@tonic-gate 	}
2901*0Sstevel@tonic-gate 
2902*0Sstevel@tonic-gate 
2903*0Sstevel@tonic-gate 	/* copy cmd results into fdr */
2904*0Sstevel@tonic-gate 	for (i = 0; (int)i <= (int)csb->csb_nrslts; i++)
2905*0Sstevel@tonic-gate 		fdr.fdr_result[i] = csb->csb_rslt[i];
2906*0Sstevel@tonic-gate 	fdr.fdr_nbytes = fdc->c_csb.csb_rlen; /* return resid */
2907*0Sstevel@tonic-gate 
2908*0Sstevel@tonic-gate 	switch (ddi_model_convert_from(mode)) {
2909*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
2910*0Sstevel@tonic-gate 	case DDI_MODEL_ILP32:
2911*0Sstevel@tonic-gate 		bcopy(fdr.fdr_cmd, fdr32.fdr_cmd, sizeof (fdr32.fdr_cmd));
2912*0Sstevel@tonic-gate 		fdr32.fdr_cnum = fdr.fdr_cnum;
2913*0Sstevel@tonic-gate 		bcopy(fdr.fdr_result, fdr32.fdr_result,
2914*0Sstevel@tonic-gate 		    sizeof (fdr32.fdr_result));
2915*0Sstevel@tonic-gate 		fdr32.fdr_nbytes = fdr.fdr_nbytes;
2916*0Sstevel@tonic-gate 		fdr32.fdr_addr = (caddr32_t)fdr.fdr_addr;
2917*0Sstevel@tonic-gate 		if (ddi_copyout(&fdr32, (caddr_t)arg, sizeof (fdr32), mode)) {
2918*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_RAWI,
2919*0Sstevel@tonic-gate 			(C, "fdrawioctl: can't copy results32\n"));
2920*0Sstevel@tonic-gate 			err = EFAULT;
2921*0Sstevel@tonic-gate 		}
2922*0Sstevel@tonic-gate 		break;
2923*0Sstevel@tonic-gate #endif
2924*0Sstevel@tonic-gate 	case DDI_MODEL_NONE:
2925*0Sstevel@tonic-gate 	default:
2926*0Sstevel@tonic-gate 		if (ddi_copyout(&fdr, (caddr_t)arg, sizeof (fdr), mode)) {
2927*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_RAWI,
2928*0Sstevel@tonic-gate 			(C, "fdrawioctl: can't copy results\n"));
2929*0Sstevel@tonic-gate 			err = EFAULT;
2930*0Sstevel@tonic-gate 		}
2931*0Sstevel@tonic-gate 		break;
2932*0Sstevel@tonic-gate 	}
2933*0Sstevel@tonic-gate 
2934*0Sstevel@tonic-gate 	fdretcsb(fdc);
2935*0Sstevel@tonic-gate 	mutex_exit(&fdc->c_lolock);
2936*0Sstevel@tonic-gate 	return (0);
2937*0Sstevel@tonic-gate }
2938*0Sstevel@tonic-gate 
2939*0Sstevel@tonic-gate /*
2940*0Sstevel@tonic-gate  * fdformat
2941*0Sstevel@tonic-gate  *	format a track
2942*0Sstevel@tonic-gate  * For PIO, builds a table of sector data values with 16 bytes
2943*0Sstevel@tonic-gate  * (sizeof fdc's fifo) of dummy on end.	 This is so than when fdc->c_len
2944*0Sstevel@tonic-gate  * goes to 0 and fd_intr sends a TC that all the real formatting will
2945*0Sstevel@tonic-gate  * have already been done.
2946*0Sstevel@tonic-gate  *
2947*0Sstevel@tonic-gate  *	- called with the low level lock held
2948*0Sstevel@tonic-gate  */
2949*0Sstevel@tonic-gate static int
2950*0Sstevel@tonic-gate fdformat(struct fdctlr *fdc, int unit, int cyl, int hd)
2951*0Sstevel@tonic-gate {
2952*0Sstevel@tonic-gate 	struct fdcsb *csb;
2953*0Sstevel@tonic-gate 	struct fdunit *un;
2954*0Sstevel@tonic-gate 	struct fd_char *ch;
2955*0Sstevel@tonic-gate 	int	cmdresult;
2956*0Sstevel@tonic-gate 	uchar_t	*fmthdrs;
2957*0Sstevel@tonic-gate 	caddr_t fd;
2958*0Sstevel@tonic-gate 	int	i;
2959*0Sstevel@tonic-gate 	size_t	real_length;
2960*0Sstevel@tonic-gate 	ddi_device_acc_attr_t attr;
2961*0Sstevel@tonic-gate 	ddi_acc_handle_t mem_handle;
2962*0Sstevel@tonic-gate 
2963*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_FORM,
2964*0Sstevel@tonic-gate 	    (C, "fdformat cyl %d, hd %d\n", cyl, hd));
2965*0Sstevel@tonic-gate 	fdgetcsb(fdc);
2966*0Sstevel@tonic-gate 
2967*0Sstevel@tonic-gate 	ASSERT(fdc->c_un->un_unit_no == unit);
2968*0Sstevel@tonic-gate 
2969*0Sstevel@tonic-gate 	csb = &fdc->c_csb;
2970*0Sstevel@tonic-gate 	un = fdc->c_un;
2971*0Sstevel@tonic-gate 	ch = un->un_chars;
2972*0Sstevel@tonic-gate 
2973*0Sstevel@tonic-gate 	/* setup common things in csb */
2974*0Sstevel@tonic-gate 	csb->csb_unit = (uchar_t)unit;
2975*0Sstevel@tonic-gate 
2976*0Sstevel@tonic-gate 	/*
2977*0Sstevel@tonic-gate 	 * The controller needs to do a seek before
2978*0Sstevel@tonic-gate 	 * each format to get to right cylinder.
2979*0Sstevel@tonic-gate 	 */
2980*0Sstevel@tonic-gate 	if (fdrecalseek(fdc, unit, cyl, FDXC_CHECKCHG)) {
2981*0Sstevel@tonic-gate 		fdretcsb(fdc);
2982*0Sstevel@tonic-gate 		return (EIO);
2983*0Sstevel@tonic-gate 	}
2984*0Sstevel@tonic-gate 
2985*0Sstevel@tonic-gate 	/*
2986*0Sstevel@tonic-gate 	 * now do the format itself
2987*0Sstevel@tonic-gate 	 */
2988*0Sstevel@tonic-gate 	csb->csb_nrslts = NRBRW;
2989*0Sstevel@tonic-gate 	csb->csb_opflags = CSB_OFXFEROPS | CSB_OFTIMEIT;
2990*0Sstevel@tonic-gate 
2991*0Sstevel@tonic-gate 	csb->csb_cmds[0] = FDRAW_FORMAT;
2992*0Sstevel@tonic-gate 	/* always or in MFM bit */
2993*0Sstevel@tonic-gate 	csb->csb_cmds[0] |= MFM;
2994*0Sstevel@tonic-gate 	csb->csb_cmds[1] = (hd << 2) | (unit & 0x03);
2995*0Sstevel@tonic-gate 	csb->csb_cmds[2] = ch->fdc_medium ? 3 : 2;
2996*0Sstevel@tonic-gate 	csb->csb_cmds[3] = ch->fdc_secptrack;
2997*0Sstevel@tonic-gate 	csb->csb_cmds[4] = GPLF;
2998*0Sstevel@tonic-gate 	csb->csb_cmds[5] = FDATA;
2999*0Sstevel@tonic-gate 	csb->csb_ncmds = 6;
3000*0Sstevel@tonic-gate 	csb->csb_maxretry = rwretry;
3001*0Sstevel@tonic-gate 	csb->csb_retrys = 0;
3002*0Sstevel@tonic-gate 
3003*0Sstevel@tonic-gate 	/*
3004*0Sstevel@tonic-gate 	 * NOTE: have to add size of fifo also - for dummy format action
3005*0Sstevel@tonic-gate 	 * if PIO is being used.
3006*0Sstevel@tonic-gate 	 */
3007*0Sstevel@tonic-gate 
3008*0Sstevel@tonic-gate 
3009*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_DMA) {
3010*0Sstevel@tonic-gate 
3011*0Sstevel@tonic-gate 		csb->csb_len = (uint_t)4 * ch->fdc_secptrack;
3012*0Sstevel@tonic-gate 
3013*0Sstevel@tonic-gate 		attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
3014*0Sstevel@tonic-gate 		attr.devacc_attr_endian_flags  = DDI_STRUCTURE_BE_ACC;
3015*0Sstevel@tonic-gate 		attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
3016*0Sstevel@tonic-gate 
3017*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_hilock);
3018*0Sstevel@tonic-gate 
3019*0Sstevel@tonic-gate 		cmdresult = ddi_dma_mem_alloc(fdc->c_dmahandle, csb->csb_len,
3020*0Sstevel@tonic-gate 			&attr, DDI_DMA_STREAMING,
3021*0Sstevel@tonic-gate 			DDI_DMA_DONTWAIT, 0, &fd, &real_length,
3022*0Sstevel@tonic-gate 			&mem_handle);
3023*0Sstevel@tonic-gate 
3024*0Sstevel@tonic-gate 		if (cmdresult != DDI_SUCCESS) {
3025*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_hilock);
3026*0Sstevel@tonic-gate 			return (cmdresult);
3027*0Sstevel@tonic-gate 		}
3028*0Sstevel@tonic-gate 
3029*0Sstevel@tonic-gate 		fdc->c_csb.csb_read = CSB_WRITE;
3030*0Sstevel@tonic-gate 		if (fdstart_dma(fdc, fd,  csb->csb_len) != 0) {
3031*0Sstevel@tonic-gate 			ddi_dma_mem_free(&mem_handle);
3032*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_hilock);
3033*0Sstevel@tonic-gate 			return (-1);
3034*0Sstevel@tonic-gate 		}
3035*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_hilock);
3036*0Sstevel@tonic-gate 
3037*0Sstevel@tonic-gate 
3038*0Sstevel@tonic-gate 	} else {
3039*0Sstevel@tonic-gate 		csb->csb_len = (uint_t)4 * ch->fdc_secptrack + 16;
3040*0Sstevel@tonic-gate 		fd = kmem_zalloc(csb->csb_len, KM_SLEEP);
3041*0Sstevel@tonic-gate 		fmthdrs = (uchar_t *)fd;
3042*0Sstevel@tonic-gate 	}
3043*0Sstevel@tonic-gate 
3044*0Sstevel@tonic-gate 	csb->csb_addr = (caddr_t)fd;
3045*0Sstevel@tonic-gate 
3046*0Sstevel@tonic-gate 	for (i = 1; i <= ch->fdc_secptrack; i++) {
3047*0Sstevel@tonic-gate 		*fd++ = (uchar_t)cyl;		/* cylinder */
3048*0Sstevel@tonic-gate 		*fd++ = (uchar_t)hd;		/* head */
3049*0Sstevel@tonic-gate 		*fd++ = (uchar_t)i;	/* sector number */
3050*0Sstevel@tonic-gate 		*fd++ = ch->fdc_medium ? 3 : 2; /* sec_size code */
3051*0Sstevel@tonic-gate 	}
3052*0Sstevel@tonic-gate 
3053*0Sstevel@tonic-gate 	if ((cmdresult = fdexec(fdc, FDXC_SLEEP | FDXC_CHECKCHG)) == 0) {
3054*0Sstevel@tonic-gate 		if (csb->csb_cmdstat)
3055*0Sstevel@tonic-gate 			cmdresult = EIO;	/* XXX TBD NYD for now */
3056*0Sstevel@tonic-gate 	}
3057*0Sstevel@tonic-gate 
3058*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_DMA) {
3059*0Sstevel@tonic-gate 		ddi_dma_mem_free(&mem_handle);
3060*0Sstevel@tonic-gate 	} else {
3061*0Sstevel@tonic-gate 		kmem_free((caddr_t)fmthdrs, csb->csb_len);
3062*0Sstevel@tonic-gate 	}
3063*0Sstevel@tonic-gate 
3064*0Sstevel@tonic-gate 	fdretcsb(fdc);
3065*0Sstevel@tonic-gate 
3066*0Sstevel@tonic-gate 	return (cmdresult);
3067*0Sstevel@tonic-gate }
3068*0Sstevel@tonic-gate 
3069*0Sstevel@tonic-gate /*
3070*0Sstevel@tonic-gate  * fdstart
3071*0Sstevel@tonic-gate  *	called from fd_strategy() or from fdXXXX() to setup and
3072*0Sstevel@tonic-gate  *	start operations of read or write only (using buf structs).
3073*0Sstevel@tonic-gate  *	Because the chip doesn't handle crossing cylinder boundaries on
3074*0Sstevel@tonic-gate  *	the fly, this takes care of those boundary conditions.	Note that
3075*0Sstevel@tonic-gate  *	it sleeps until the operation is done *within fdstart* - so that
3076*0Sstevel@tonic-gate  *	when fdstart returns, the operation is already done.
3077*0Sstevel@tonic-gate  *
3078*0Sstevel@tonic-gate  *	- called with the low level lock held
3079*0Sstevel@tonic-gate  *
3080*0Sstevel@tonic-gate  */
3081*0Sstevel@tonic-gate 
3082*0Sstevel@tonic-gate static int slavio_index_pulse_work_around = 0;
3083*0Sstevel@tonic-gate 
3084*0Sstevel@tonic-gate static void
3085*0Sstevel@tonic-gate fdstart(struct fdctlr *fdc)
3086*0Sstevel@tonic-gate {
3087*0Sstevel@tonic-gate 	struct buf *bp;
3088*0Sstevel@tonic-gate 	struct fdcsb *csb;
3089*0Sstevel@tonic-gate 	struct fdunit *un;
3090*0Sstevel@tonic-gate 	struct fd_char *ch;
3091*0Sstevel@tonic-gate 	struct dk_map32 *dkm;
3092*0Sstevel@tonic-gate 	uint_t	part;		/* partition number for the transfer */
3093*0Sstevel@tonic-gate 	uint_t	start_part;	/* starting block of the partition */
3094*0Sstevel@tonic-gate 	uint_t	last_part;	/* last block of the partition */
3095*0Sstevel@tonic-gate 	uint_t	blk;		/* starting block of transfer on diskette */
3096*0Sstevel@tonic-gate 	uint_t	sect;		/* starting block's offset into track */
3097*0Sstevel@tonic-gate 	uint_t	cyl;		/* starting cylinder of the transfer */
3098*0Sstevel@tonic-gate 	uint_t	bincyl;		/* starting blocks's offset into cylinder */
3099*0Sstevel@tonic-gate 	uint_t	secpcyl;	/* number of sectors per cylinder */
3100*0Sstevel@tonic-gate 	uint_t	phys_blkno;	/* no. of blocks on the diskette */
3101*0Sstevel@tonic-gate 	uint_t	head;		/* one of two diskette heads */
3102*0Sstevel@tonic-gate 	uint_t	unit;
3103*0Sstevel@tonic-gate 	uint_t	len, tlen;
3104*0Sstevel@tonic-gate 	caddr_t addr;
3105*0Sstevel@tonic-gate 	caddr_t temp_addr;
3106*0Sstevel@tonic-gate 	uint_t	partial_read = 0;
3107*0Sstevel@tonic-gate 	int sb_temp_buf_used = 0;
3108*0Sstevel@tonic-gate 
3109*0Sstevel@tonic-gate 	bp = fdc->c_actf;
3110*0Sstevel@tonic-gate 
3111*0Sstevel@tonic-gate 	while (bp != NULL) {
3112*0Sstevel@tonic-gate 
3113*0Sstevel@tonic-gate 		fdc->c_actf = bp->av_forw;
3114*0Sstevel@tonic-gate 		fdc->c_current = bp;
3115*0Sstevel@tonic-gate 
3116*0Sstevel@tonic-gate 		/*
3117*0Sstevel@tonic-gate 		 * Initialize the buf structure.  The residual count is
3118*0Sstevel@tonic-gate 		 * initially the number of bytes to be read or written
3119*0Sstevel@tonic-gate 		 */
3120*0Sstevel@tonic-gate 		bp->b_flags &= ~B_ERROR;
3121*0Sstevel@tonic-gate 		bp->b_error = 0;
3122*0Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount;
3123*0Sstevel@tonic-gate 		bp_mapin(bp);			/* map in buffers */
3124*0Sstevel@tonic-gate 
3125*0Sstevel@tonic-gate 		addr = bp->b_un.b_addr;		/* assign buffer address */
3126*0Sstevel@tonic-gate 
3127*0Sstevel@tonic-gate 		/*
3128*0Sstevel@tonic-gate 		 * Find the unit and partition numbers.
3129*0Sstevel@tonic-gate 		 */
3130*0Sstevel@tonic-gate 		unit = fdc->c_un->un_unit_no;
3131*0Sstevel@tonic-gate 		un = fdc->c_un;
3132*0Sstevel@tonic-gate 		ch = un->un_chars;
3133*0Sstevel@tonic-gate 		part = FDPARTITION(bp->b_edev);
3134*0Sstevel@tonic-gate 		dkm = &un->un_label.dkl_map[part];
3135*0Sstevel@tonic-gate 
3136*0Sstevel@tonic-gate 		if (un->un_chars->fdc_medium) {
3137*0Sstevel@tonic-gate 			phys_blkno = bp->b_blkno >> 1;
3138*0Sstevel@tonic-gate 		} else {
3139*0Sstevel@tonic-gate 			phys_blkno = bp->b_blkno;
3140*0Sstevel@tonic-gate 		}
3141*0Sstevel@tonic-gate 
3142*0Sstevel@tonic-gate 		if (un->un_iostat) {
3143*0Sstevel@tonic-gate 			kstat_waitq_to_runq(KIOSP);
3144*0Sstevel@tonic-gate 		}
3145*0Sstevel@tonic-gate 
3146*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_STRT,
3147*0Sstevel@tonic-gate 		    (C, "fdstart: bp=0x%p blkno=0x%x bcount=0x%x\n",
3148*0Sstevel@tonic-gate 		    (void *)bp, (int)bp->b_blkno, (int)bp->b_bcount));
3149*0Sstevel@tonic-gate 
3150*0Sstevel@tonic-gate 		/*
3151*0Sstevel@tonic-gate 		 * Get the csb and initialize the values that are the same
3152*0Sstevel@tonic-gate 		 * for DMA and PIO.
3153*0Sstevel@tonic-gate 		 */
3154*0Sstevel@tonic-gate 		fdgetcsb(fdc);		/* get csb (maybe wait for it) */
3155*0Sstevel@tonic-gate 		csb = &fdc->c_csb;
3156*0Sstevel@tonic-gate 		csb->csb_unit = unit;		/* floppy unit number */
3157*0Sstevel@tonic-gate 
3158*0Sstevel@tonic-gate 
3159*0Sstevel@tonic-gate 		/*
3160*0Sstevel@tonic-gate 		 * bugID:4133425 : If the controller is SLAVIO, and
3161*0Sstevel@tonic-gate 		 * the read does not reach end of track, then modify
3162*0Sstevel@tonic-gate 		 * the tlen to read until the end of track to a temp
3163*0Sstevel@tonic-gate 		 * buffer and disable MT. After the read is over,
3164*0Sstevel@tonic-gate 		 * copy the useful portion of the data to 'addr'.
3165*0Sstevel@tonic-gate 		 * Enable this feature only when
3166*0Sstevel@tonic-gate 		 * slavio_index_pulse_work_aound variable is
3167*0Sstevel@tonic-gate 		 * set in /etc/system.
3168*0Sstevel@tonic-gate 		 */
3169*0Sstevel@tonic-gate 
3170*0Sstevel@tonic-gate 
3171*0Sstevel@tonic-gate 		if (bp->b_flags & B_READ) {
3172*0Sstevel@tonic-gate 			if (((fdc->c_fdtype & FDCTYPE_SLAVIO) &&
3173*0Sstevel@tonic-gate 				slavio_index_pulse_work_around) ||
3174*0Sstevel@tonic-gate 					(fdc->c_fdtype & FDCTYPE_TCBUG))
3175*0Sstevel@tonic-gate 				csb->csb_cmds[0] = SK | FDRAW_RDCMD | MFM;
3176*0Sstevel@tonic-gate 			else
3177*0Sstevel@tonic-gate 				csb->csb_cmds[0] = MT | SK | FDRAW_RDCMD | MFM;
3178*0Sstevel@tonic-gate 		} else {
3179*0Sstevel@tonic-gate 			if (fdc->c_fdtype & FDCTYPE_TCBUG)
3180*0Sstevel@tonic-gate 				csb->csb_cmds[0] = FDRAW_WRCMD | MFM;
3181*0Sstevel@tonic-gate 			else
3182*0Sstevel@tonic-gate 				csb->csb_cmds[0] = MT | FDRAW_WRCMD | MFM;
3183*0Sstevel@tonic-gate 		}
3184*0Sstevel@tonic-gate 
3185*0Sstevel@tonic-gate 
3186*0Sstevel@tonic-gate 		if (bp->b_flags & B_READ)
3187*0Sstevel@tonic-gate 			fdc->c_csb.csb_read = CSB_READ;
3188*0Sstevel@tonic-gate 		else
3189*0Sstevel@tonic-gate 			fdc->c_csb.csb_read = CSB_WRITE;
3190*0Sstevel@tonic-gate 
3191*0Sstevel@tonic-gate 
3192*0Sstevel@tonic-gate 		csb->csb_cmds[5] = ch->fdc_medium ? 3 : 2; /* sector size  */
3193*0Sstevel@tonic-gate 		csb->csb_cmds[6] = ch->fdc_secptrack; /* EOT-# of sectors/trk */
3194*0Sstevel@tonic-gate 		csb->csb_cmds[7] = GPLN;	/* GPL - gap 3 size code */
3195*0Sstevel@tonic-gate 		csb->csb_cmds[8] = SSSDTL;	/* DTL - be 0xFF if N != 0 */
3196*0Sstevel@tonic-gate 
3197*0Sstevel@tonic-gate 		csb->csb_ncmds = NCBRW;		/* number of command bytes */
3198*0Sstevel@tonic-gate 		csb->csb_nrslts = NRBRW;	/* number of result bytes */
3199*0Sstevel@tonic-gate 
3200*0Sstevel@tonic-gate 
3201*0Sstevel@tonic-gate 		/*
3202*0Sstevel@tonic-gate 		 * opflags for interrupt handler, et.al.
3203*0Sstevel@tonic-gate 		 */
3204*0Sstevel@tonic-gate 		csb->csb_opflags = CSB_OFXFEROPS | CSB_OFTIMEIT;
3205*0Sstevel@tonic-gate 
3206*0Sstevel@tonic-gate 
3207*0Sstevel@tonic-gate 		/*
3208*0Sstevel@tonic-gate 		 * Make sure the transfer does not go off the end
3209*0Sstevel@tonic-gate 		 * of the partition.  Limit the actual amount transferred
3210*0Sstevel@tonic-gate 		 * to fit the partition.
3211*0Sstevel@tonic-gate 		 */
3212*0Sstevel@tonic-gate 
3213*0Sstevel@tonic-gate 		blk = phys_blkno;
3214*0Sstevel@tonic-gate 		start_part = (dkm->dkl_cylno * ch->fdc_secptrack
3215*0Sstevel@tonic-gate 				* ch->fdc_nhead);
3216*0Sstevel@tonic-gate 		blk = blk + start_part;
3217*0Sstevel@tonic-gate 		last_part = start_part + dkm->dkl_nblk;
3218*0Sstevel@tonic-gate 
3219*0Sstevel@tonic-gate 		if ((blk + (bp->b_bcount / ch->fdc_sec_size)) > last_part)
3220*0Sstevel@tonic-gate 			len = (last_part - blk) * ch->fdc_sec_size;
3221*0Sstevel@tonic-gate 		else
3222*0Sstevel@tonic-gate 			len = (uint_t)bp->b_bcount;
3223*0Sstevel@tonic-gate 
3224*0Sstevel@tonic-gate 		/*
3225*0Sstevel@tonic-gate 		 * now we have the real start blk,
3226*0Sstevel@tonic-gate 		 * addr and len for xfer op
3227*0Sstevel@tonic-gate 		 * sectors per cylinder
3228*0Sstevel@tonic-gate 		 */
3229*0Sstevel@tonic-gate 		secpcyl = ch->fdc_nhead * ch->fdc_secptrack;
3230*0Sstevel@tonic-gate 
3231*0Sstevel@tonic-gate 		/*
3232*0Sstevel@tonic-gate 		 * The controller can transfer up to a cylinder at a time.
3233*0Sstevel@tonic-gate 		 * Early revs of the 82077 have a bug that causes the chip to
3234*0Sstevel@tonic-gate 		 * fail to respond to the Terminal Count signal.  Due to this
3235*0Sstevel@tonic-gate 		 * bug, controllers with type FDCTYPE_TCBUG, only transfer up
3236*0Sstevel@tonic-gate 		 * to a track at a time.
3237*0Sstevel@tonic-gate 		 * See earlier comment for bugID:4133425 for index pulse
3238*0Sstevel@tonic-gate 		 * work around.
3239*0Sstevel@tonic-gate 		 */
3240*0Sstevel@tonic-gate 
3241*0Sstevel@tonic-gate 		while (len != 0) {
3242*0Sstevel@tonic-gate 
3243*0Sstevel@tonic-gate 			cyl = blk / secpcyl;	/* cylinder of transfer */
3244*0Sstevel@tonic-gate 			bincyl = blk % secpcyl;	/* blk within cylinder */
3245*0Sstevel@tonic-gate 			head = bincyl / ch->fdc_secptrack;
3246*0Sstevel@tonic-gate 			sect = (bincyl % ch->fdc_secptrack) + 1;
3247*0Sstevel@tonic-gate 						/* sect w/in track */
3248*0Sstevel@tonic-gate 
3249*0Sstevel@tonic-gate 			/*
3250*0Sstevel@tonic-gate 			 * If the desired block and length will go beyond the
3251*0Sstevel@tonic-gate 			 * cylinder end, limit it to the cylinder end.
3252*0Sstevel@tonic-gate 			 */
3253*0Sstevel@tonic-gate 
3254*0Sstevel@tonic-gate 			if ((fdc->c_fdtype & FDCTYPE_SLAVIO) &&
3255*0Sstevel@tonic-gate 				slavio_index_pulse_work_around &&
3256*0Sstevel@tonic-gate 				(fdc->c_csb.csb_read == CSB_READ)) {
3257*0Sstevel@tonic-gate 
3258*0Sstevel@tonic-gate 				tlen = (ch->fdc_secptrack - sect + 1) *
3259*0Sstevel@tonic-gate 							ch->fdc_sec_size;
3260*0Sstevel@tonic-gate 				if (len < tlen) {
3261*0Sstevel@tonic-gate 					partial_read = 1;
3262*0Sstevel@tonic-gate 					temp_addr = (caddr_t)kmem_alloc(tlen,
3263*0Sstevel@tonic-gate 								KM_SLEEP);
3264*0Sstevel@tonic-gate 				}
3265*0Sstevel@tonic-gate 
3266*0Sstevel@tonic-gate 			} else if (fdc->c_fdtype & FDCTYPE_TCBUG) {
3267*0Sstevel@tonic-gate 				tlen = len;
3268*0Sstevel@tonic-gate 				if (len > ((ch->fdc_secptrack - sect + 1) *
3269*0Sstevel@tonic-gate 							ch->fdc_sec_size))
3270*0Sstevel@tonic-gate 					tlen = (ch->fdc_secptrack - sect + 1)
3271*0Sstevel@tonic-gate 							* ch->fdc_sec_size;
3272*0Sstevel@tonic-gate 			} else {
3273*0Sstevel@tonic-gate 				if (len > ((secpcyl - bincyl)
3274*0Sstevel@tonic-gate 							* ch->fdc_sec_size))
3275*0Sstevel@tonic-gate 					tlen = (secpcyl - bincyl)
3276*0Sstevel@tonic-gate 							* ch->fdc_sec_size;
3277*0Sstevel@tonic-gate 
3278*0Sstevel@tonic-gate 				else
3279*0Sstevel@tonic-gate 					tlen = len;
3280*0Sstevel@tonic-gate 			}
3281*0Sstevel@tonic-gate 			if (fdc->c_fdtype & FDCTYPE_SB) {
3282*0Sstevel@tonic-gate 				/*
3283*0Sstevel@tonic-gate 				 * To avoid underrun errors during IFB activity.
3284*0Sstevel@tonic-gate 				 */
3285*0Sstevel@tonic-gate 				if (tlen > max_fd_dma_len)
3286*0Sstevel@tonic-gate 					tlen = max_fd_dma_len;
3287*0Sstevel@tonic-gate 			}
3288*0Sstevel@tonic-gate 
3289*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_STRT,
3290*0Sstevel@tonic-gate 			    (C, "	blk 0x%x, addr 0x%p, len 0x%x\n",
3291*0Sstevel@tonic-gate 			    blk, (void *)addr, len));
3292*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_STRT,
3293*0Sstevel@tonic-gate 			    (C, "cyl:%x, head:%x, sec:%x\n",
3294*0Sstevel@tonic-gate 			    cyl, head, sect));
3295*0Sstevel@tonic-gate 
3296*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_STRT,
3297*0Sstevel@tonic-gate 			    (C, "	resid 0x%lx, tlen %d\n",
3298*0Sstevel@tonic-gate 			    bp->b_resid, tlen));
3299*0Sstevel@tonic-gate 
3300*0Sstevel@tonic-gate 			/*
3301*0Sstevel@tonic-gate 			 * Finish programming the command
3302*0Sstevel@tonic-gate 			 */
3303*0Sstevel@tonic-gate 			csb->csb_cmds[1] = (head << 2) | unit;
3304*0Sstevel@tonic-gate 			if (fdc->c_fdtype & FDCTYPE_SB)
3305*0Sstevel@tonic-gate 				csb->csb_cmds[1] |= IPS;
3306*0Sstevel@tonic-gate 
3307*0Sstevel@tonic-gate 			csb->csb_cmds[2] = cyl;	/* C - cylinder address */
3308*0Sstevel@tonic-gate 			csb->csb_cmds[3] = head;	/* H - head number */
3309*0Sstevel@tonic-gate 			csb->csb_cmds[4] = sect;	/* R - sector number */
3310*0Sstevel@tonic-gate 			if (fdc->c_fdtype & FDCTYPE_TCBUG)
3311*0Sstevel@tonic-gate 				csb->csb_cmds[6] = sect +
3312*0Sstevel@tonic-gate 						(tlen / ch->fdc_sec_size) - 1;
3313*0Sstevel@tonic-gate 
3314*0Sstevel@tonic-gate 			csb->csb_len = tlen;
3315*0Sstevel@tonic-gate 			if (partial_read)
3316*0Sstevel@tonic-gate 				csb->csb_addr = temp_addr;
3317*0Sstevel@tonic-gate 			else
3318*0Sstevel@tonic-gate 				csb->csb_addr = addr;
3319*0Sstevel@tonic-gate 
3320*0Sstevel@tonic-gate 			/* retry this many times max */
3321*0Sstevel@tonic-gate 			csb->csb_maxretry = rwretry;
3322*0Sstevel@tonic-gate 			csb->csb_retrys = 0;
3323*0Sstevel@tonic-gate 
3324*0Sstevel@tonic-gate 			/* If platform supports DMA, set up DMA resources */
3325*0Sstevel@tonic-gate 			if (fdc->c_fdtype & FDCTYPE_DMA) {
3326*0Sstevel@tonic-gate 				if ((fdc->c_fdtype & FDCTYPE_SB) &&
3327*0Sstevel@tonic-gate 				    (((uint32_t)addr & 0xFFFF0000) !=
3328*0Sstevel@tonic-gate 				    (((uint32_t)addr + tlen) & 0xFFFF0000))) {
3329*0Sstevel@tonic-gate 					csb->csb_addr = fdc->dma_buf;
3330*0Sstevel@tonic-gate 					sb_temp_buf_used = 1;
3331*0Sstevel@tonic-gate 					if (csb->csb_read != CSB_READ) {
3332*0Sstevel@tonic-gate 						bcopy(addr, fdc->dma_buf, tlen);
3333*0Sstevel@tonic-gate 				}
3334*0Sstevel@tonic-gate 			}
3335*0Sstevel@tonic-gate 				mutex_enter(&fdc->c_hilock);
3336*0Sstevel@tonic-gate 
3337*0Sstevel@tonic-gate 				if (fdstart_dma(fdc, csb->csb_addr,
3338*0Sstevel@tonic-gate 								tlen) != 0) {
3339*0Sstevel@tonic-gate 
3340*0Sstevel@tonic-gate 					bp->b_flags |= B_ERROR;
3341*0Sstevel@tonic-gate 					bp->b_error = EAGAIN;
3342*0Sstevel@tonic-gate 
3343*0Sstevel@tonic-gate 					mutex_exit(&fdc->c_hilock);
3344*0Sstevel@tonic-gate 					FDERRPRINT(FDEP_L1, FDEM_STRT,
3345*0Sstevel@tonic-gate 					(C, "fdstart: no dma resources\n"));
3346*0Sstevel@tonic-gate 
3347*0Sstevel@tonic-gate 					break;
3348*0Sstevel@tonic-gate 				}
3349*0Sstevel@tonic-gate 				mutex_exit(&fdc->c_hilock);
3350*0Sstevel@tonic-gate 
3351*0Sstevel@tonic-gate 			}
3352*0Sstevel@tonic-gate 
3353*0Sstevel@tonic-gate 			bp->b_error = fdexec(fdc, FDXC_SLEEP|FDXC_CHECKCHG);
3354*0Sstevel@tonic-gate 			if (bp->b_error != 0) {
3355*0Sstevel@tonic-gate 				/*
3356*0Sstevel@tonic-gate 				 * error in fdexec
3357*0Sstevel@tonic-gate 				 */
3358*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_STRT, (C,
3359*0Sstevel@tonic-gate 				    "fdstart: bad exec of bp: 0x%p, err %d\n",
3360*0Sstevel@tonic-gate 				    (void *)bp, bp->b_error));
3361*0Sstevel@tonic-gate 
3362*0Sstevel@tonic-gate 				bp->b_flags |= B_ERROR;
3363*0Sstevel@tonic-gate 				if (partial_read) {
3364*0Sstevel@tonic-gate 					partial_read = 0;
3365*0Sstevel@tonic-gate 					kmem_free(temp_addr, tlen);
3366*0Sstevel@tonic-gate 				}
3367*0Sstevel@tonic-gate 				break;
3368*0Sstevel@tonic-gate 			}
3369*0Sstevel@tonic-gate 
3370*0Sstevel@tonic-gate 			/*
3371*0Sstevel@tonic-gate 			 * If it was a partial read, copy the useful
3372*0Sstevel@tonic-gate 			 * portion of data to 'addr'.
3373*0Sstevel@tonic-gate 			 */
3374*0Sstevel@tonic-gate 			if (partial_read) {
3375*0Sstevel@tonic-gate 				partial_read = 0;
3376*0Sstevel@tonic-gate 				bcopy(temp_addr, addr, len);
3377*0Sstevel@tonic-gate 				kmem_free(temp_addr, tlen);
3378*0Sstevel@tonic-gate 				tlen = len;
3379*0Sstevel@tonic-gate 			}
3380*0Sstevel@tonic-gate 			if ((fdc->c_fdtype & FDCTYPE_SB) &&
3381*0Sstevel@tonic-gate 					(csb->csb_read == CSB_READ)) {
3382*0Sstevel@tonic-gate 				if (sb_temp_buf_used) {
3383*0Sstevel@tonic-gate 					bcopy(fdc->dma_buf, addr, tlen);
3384*0Sstevel@tonic-gate 					sb_temp_buf_used = 0;
3385*0Sstevel@tonic-gate 				}
3386*0Sstevel@tonic-gate 			}
3387*0Sstevel@tonic-gate 
3388*0Sstevel@tonic-gate 			blk += tlen / ch->fdc_sec_size;
3389*0Sstevel@tonic-gate 			len -= tlen;
3390*0Sstevel@tonic-gate 			addr += tlen;
3391*0Sstevel@tonic-gate 			bp->b_resid -= tlen;
3392*0Sstevel@tonic-gate 
3393*0Sstevel@tonic-gate 		}
3394*0Sstevel@tonic-gate 
3395*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_STRT,
3396*0Sstevel@tonic-gate 		    (C, "fdstart done: b_resid %lu, b_count %lu, csb_rlen %d\n",
3397*0Sstevel@tonic-gate 		    bp->b_resid, bp->b_bcount, fdc->c_csb.csb_rlen));
3398*0Sstevel@tonic-gate 
3399*0Sstevel@tonic-gate 		fdc->c_current = 0;
3400*0Sstevel@tonic-gate 		fdretcsb(fdc);
3401*0Sstevel@tonic-gate 		if (un->un_iostat) {
3402*0Sstevel@tonic-gate 			if (bp->b_flags & B_READ) {
3403*0Sstevel@tonic-gate 				KIOSP->reads++;
3404*0Sstevel@tonic-gate 				KIOSP->nread +=
3405*0Sstevel@tonic-gate 					(bp->b_bcount - bp->b_resid);
3406*0Sstevel@tonic-gate 			} else {
3407*0Sstevel@tonic-gate 				KIOSP->writes++;
3408*0Sstevel@tonic-gate 				KIOSP->nwritten += (bp->b_bcount - bp->b_resid);
3409*0Sstevel@tonic-gate 			}
3410*0Sstevel@tonic-gate 			kstat_runq_exit(KIOSP);
3411*0Sstevel@tonic-gate 		}
3412*0Sstevel@tonic-gate 		biodone(bp);
3413*0Sstevel@tonic-gate 
3414*0Sstevel@tonic-gate 		/*
3415*0Sstevel@tonic-gate 		 * Look at the next buffer
3416*0Sstevel@tonic-gate 		 */
3417*0Sstevel@tonic-gate 		bp = fdc->c_actf;
3418*0Sstevel@tonic-gate 
3419*0Sstevel@tonic-gate 	}
3420*0Sstevel@tonic-gate }
3421*0Sstevel@tonic-gate 
3422*0Sstevel@tonic-gate /*
3423*0Sstevel@tonic-gate  * Set up DMA resources
3424*0Sstevel@tonic-gate  * The DMA handle was initialized in fd_attach()
3425*0Sstevel@tonic-gate  * Assumes the handle has already been allocated by fd_attach()
3426*0Sstevel@tonic-gate  */
3427*0Sstevel@tonic-gate static int
3428*0Sstevel@tonic-gate fdstart_dma(struct fdctlr *fdc, caddr_t addr, uint_t len)
3429*0Sstevel@tonic-gate {
3430*0Sstevel@tonic-gate 	int		flags;		/* flags for setting up resources */
3431*0Sstevel@tonic-gate 	int		res;
3432*0Sstevel@tonic-gate 
3433*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_SDMA, (C, "fdstart_dma: start\n"));
3434*0Sstevel@tonic-gate 
3435*0Sstevel@tonic-gate 	if (fdc->c_csb.csb_read == CSB_READ) {
3436*0Sstevel@tonic-gate 		flags = DDI_DMA_READ;
3437*0Sstevel@tonic-gate 	} else {
3438*0Sstevel@tonic-gate 		flags = DDI_DMA_WRITE;
3439*0Sstevel@tonic-gate 	}
3440*0Sstevel@tonic-gate 
3441*0Sstevel@tonic-gate 
3442*0Sstevel@tonic-gate 	/* allow partial mapping to maximize the portability of the driver */
3443*0Sstevel@tonic-gate 	flags = flags | DDI_DMA_PARTIAL;
3444*0Sstevel@tonic-gate 
3445*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_SDMA, (C, "fdstart_dma: amt. asked for %d\n",
3446*0Sstevel@tonic-gate 					len));
3447*0Sstevel@tonic-gate 
3448*0Sstevel@tonic-gate 	/*
3449*0Sstevel@tonic-gate 	 * Zero out the current cookie.  This is done to ensure that
3450*0Sstevel@tonic-gate 	 * the previous transfers cookie information can in no way be
3451*0Sstevel@tonic-gate 	 * used.
3452*0Sstevel@tonic-gate 	 */
3453*0Sstevel@tonic-gate 	bzero((char *)&fdc->c_csb.csb_dmacookie,
3454*0Sstevel@tonic-gate 			sizeof (fdc->c_csb.csb_dmacookie));
3455*0Sstevel@tonic-gate 	fdc->c_csb.csb_nwin = 0;
3456*0Sstevel@tonic-gate 	fdc->c_csb.csb_windex = 0;
3457*0Sstevel@tonic-gate 	fdc->c_csb.csb_ccount = 0;
3458*0Sstevel@tonic-gate 
3459*0Sstevel@tonic-gate 	res = ddi_dma_addr_bind_handle(fdc->c_dmahandle, NULL, addr, len,
3460*0Sstevel@tonic-gate 	flags, DDI_DMA_DONTWAIT, 0,  &fdc->c_csb.csb_dmacookie,
3461*0Sstevel@tonic-gate 	&fdc->c_csb.csb_ccount);
3462*0Sstevel@tonic-gate 
3463*0Sstevel@tonic-gate 	switch (res) {
3464*0Sstevel@tonic-gate 		case DDI_DMA_MAPPED:
3465*0Sstevel@tonic-gate 			/*
3466*0Sstevel@tonic-gate 			 * There is one window. csb_windex is the index
3467*0Sstevel@tonic-gate 			 * into the array of windows. If there are n
3468*0Sstevel@tonic-gate 			 * windows then, (0 <= windex <= n-1).  csb_windex
3469*0Sstevel@tonic-gate 			 * represents the index of the next window
3470*0Sstevel@tonic-gate 			 * to be processed.
3471*0Sstevel@tonic-gate 			 */
3472*0Sstevel@tonic-gate 			fdc->c_csb.csb_nwin = 1;
3473*0Sstevel@tonic-gate 			fdc->c_csb.csb_windex = 1;
3474*0Sstevel@tonic-gate 
3475*0Sstevel@tonic-gate 
3476*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_SDMA,
3477*0Sstevel@tonic-gate 				(C, "fdstart_dma: DDI_DMA_MAPPED\n"));
3478*0Sstevel@tonic-gate 
3479*0Sstevel@tonic-gate 			break;
3480*0Sstevel@tonic-gate 		case DDI_DMA_PARTIAL_MAP:
3481*0Sstevel@tonic-gate 
3482*0Sstevel@tonic-gate 			/*
3483*0Sstevel@tonic-gate 			 * obtain the number of DMA windows
3484*0Sstevel@tonic-gate 			 */
3485*0Sstevel@tonic-gate 			if (ddi_dma_numwin(fdc->c_dmahandle,
3486*0Sstevel@tonic-gate 				&fdc->c_csb.csb_nwin) != DDI_SUCCESS) {
3487*0Sstevel@tonic-gate 				return (-1);
3488*0Sstevel@tonic-gate 			}
3489*0Sstevel@tonic-gate 
3490*0Sstevel@tonic-gate 
3491*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_SDMA,
3492*0Sstevel@tonic-gate 			(C, "fdstart_dma: partially mapped %d windows\n",
3493*0Sstevel@tonic-gate 			fdc->c_csb.csb_nwin));
3494*0Sstevel@tonic-gate 
3495*0Sstevel@tonic-gate 			/*
3496*0Sstevel@tonic-gate 			 * The DMA window currently in use is window number
3497*0Sstevel@tonic-gate 			 * one.
3498*0Sstevel@tonic-gate 			 */
3499*0Sstevel@tonic-gate 			fdc->c_csb.csb_windex = 1;
3500*0Sstevel@tonic-gate 
3501*0Sstevel@tonic-gate 			break;
3502*0Sstevel@tonic-gate 		case DDI_DMA_NORESOURCES:
3503*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_SDMA,
3504*0Sstevel@tonic-gate 				(C, "fdstart_dma: no resources\n"));
3505*0Sstevel@tonic-gate 			return (-1);
3506*0Sstevel@tonic-gate 		case DDI_DMA_NOMAPPING:
3507*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_SDMA,
3508*0Sstevel@tonic-gate 				(C, "fdstart_dma: no mapping\n"));
3509*0Sstevel@tonic-gate 			return (-1);
3510*0Sstevel@tonic-gate 		case DDI_DMA_TOOBIG:
3511*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_SDMA,
3512*0Sstevel@tonic-gate 				(C, "fdstart_dma: too big\n"));
3513*0Sstevel@tonic-gate 			return (-1);
3514*0Sstevel@tonic-gate 
3515*0Sstevel@tonic-gate 		case DDI_DMA_INUSE:
3516*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_SDMA,
3517*0Sstevel@tonic-gate 				(C, "fdstart_dma: dma inuse\n"));
3518*0Sstevel@tonic-gate 			return (-1);
3519*0Sstevel@tonic-gate 		default:
3520*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_SDMA,
3521*0Sstevel@tonic-gate 				(C, "fdstart_dma: result is 0x%x\n", res));
3522*0Sstevel@tonic-gate 			return (-1);
3523*0Sstevel@tonic-gate 
3524*0Sstevel@tonic-gate 	};
3525*0Sstevel@tonic-gate 
3526*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_SDMA,
3527*0Sstevel@tonic-gate 		(C, "fdstart_dma: bound the handle\n"));
3528*0Sstevel@tonic-gate 
3529*0Sstevel@tonic-gate 	ASSERT(fdc->c_csb.csb_dmacookie.dmac_size);
3530*0Sstevel@tonic-gate 
3531*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_SDMA, (C, "fdstart_dma: done\n"));
3532*0Sstevel@tonic-gate 	return (0);
3533*0Sstevel@tonic-gate }
3534*0Sstevel@tonic-gate 
3535*0Sstevel@tonic-gate 
3536*0Sstevel@tonic-gate /*
3537*0Sstevel@tonic-gate  * fd_unbind_handle: unbind a dma handle if one exists
3538*0Sstevel@tonic-gate  *		return EIO if unbind failes
3539*0Sstevel@tonic-gate  */
3540*0Sstevel@tonic-gate static int
3541*0Sstevel@tonic-gate fd_unbind_handle(struct fdctlr *fdc)
3542*0Sstevel@tonic-gate {
3543*0Sstevel@tonic-gate 	if ((fdc->c_fdtype & FDCTYPE_DMA) &&
3544*0Sstevel@tonic-gate 		((fdc->c_csb.csb_read == CSB_READ) ||
3545*0Sstevel@tonic-gate 		    (fdc->c_csb.csb_read == CSB_WRITE))) {
3546*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_hilock);
3547*0Sstevel@tonic-gate 
3548*0Sstevel@tonic-gate 		if (fdc->c_fdtype & FDCTYPE_SB) {
3549*0Sstevel@tonic-gate 			if (fdc->sb_dma_lock) {
3550*0Sstevel@tonic-gate 				release_sb_dma(fdc);
3551*0Sstevel@tonic-gate 			}
3552*0Sstevel@tonic-gate 		}
3553*0Sstevel@tonic-gate 
3554*0Sstevel@tonic-gate 		/*
3555*0Sstevel@tonic-gate 		 * If the byte count isn't zero, then the DMA engine is
3556*0Sstevel@tonic-gate 		 * still doing a transfer.  If the byte count is nonzero,
3557*0Sstevel@tonic-gate 		 * reset the DMA engine to cause it to drain.
3558*0Sstevel@tonic-gate 		 */
3559*0Sstevel@tonic-gate 
3560*0Sstevel@tonic-gate 		if (get_data_count_register(fdc) != 0) {
3561*0Sstevel@tonic-gate 			    FDERRPRINT(FDEP_L1, FDEM_EXEC,
3562*0Sstevel@tonic-gate 				(C, "unbind & byte count isn't zero\n"));
3563*0Sstevel@tonic-gate 
3564*0Sstevel@tonic-gate 				reset_dma_controller(fdc);
3565*0Sstevel@tonic-gate 				set_dma_control_register(fdc, DCSR_INIT_BITS);
3566*0Sstevel@tonic-gate 		}
3567*0Sstevel@tonic-gate 
3568*0Sstevel@tonic-gate 		if (ddi_dma_unbind_handle(fdc->c_dmahandle) != DDI_SUCCESS) {
3569*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_EXEC,
3570*0Sstevel@tonic-gate 				(C, "problem unbinding the handle\n"));
3571*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_hilock);
3572*0Sstevel@tonic-gate 			return (EIO);
3573*0Sstevel@tonic-gate 		}
3574*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_hilock);
3575*0Sstevel@tonic-gate 	}
3576*0Sstevel@tonic-gate 	return (0);
3577*0Sstevel@tonic-gate }
3578*0Sstevel@tonic-gate 
3579*0Sstevel@tonic-gate /*
3580*0Sstevel@tonic-gate  * fdexec
3581*0Sstevel@tonic-gate  *	all commands go through here.  Assumes the command block
3582*0Sstevel@tonic-gate  *	fdctlr.c_csb is filled in.  The bytes are sent to the
3583*0Sstevel@tonic-gate  *	controller and then we do whatever else the csb says -
3584*0Sstevel@tonic-gate  *	like wait for immediate results, etc.
3585*0Sstevel@tonic-gate  *
3586*0Sstevel@tonic-gate  *	All waiting for operations done is in here - to allow retrys
3587*0Sstevel@tonic-gate  *	and checking for disk changed - so we don't have to worry
3588*0Sstevel@tonic-gate  *	about sleeping at interrupt level.
3589*0Sstevel@tonic-gate  *
3590*0Sstevel@tonic-gate  * RETURNS: 0 if all ok,
3591*0Sstevel@tonic-gate  *	ENXIO - diskette not in drive
3592*0Sstevel@tonic-gate  *	EBUSY - if chip is locked or busy
3593*0Sstevel@tonic-gate  *	EIO - for timeout during sending cmds to chip
3594*0Sstevel@tonic-gate  *
3595*0Sstevel@tonic-gate  * to sleep: set FDXC_SLEEP, to check for disk
3596*0Sstevel@tonic-gate  * changed: set FDXC_CHECKCHG
3597*0Sstevel@tonic-gate  *
3598*0Sstevel@tonic-gate  *	- called with the lock held
3599*0Sstevel@tonic-gate  */
3600*0Sstevel@tonic-gate static int
3601*0Sstevel@tonic-gate fdexec(struct fdctlr *fdc, int flags)
3602*0Sstevel@tonic-gate {
3603*0Sstevel@tonic-gate 	struct fdcsb *csb;
3604*0Sstevel@tonic-gate 	int	i;
3605*0Sstevel@tonic-gate 	int	to, unit;
3606*0Sstevel@tonic-gate 	uchar_t	tmp;
3607*0Sstevel@tonic-gate 	caddr_t a = (caddr_t)fdc;
3608*0Sstevel@tonic-gate 
3609*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "fdexec: flags:%x\n", flags));
3610*0Sstevel@tonic-gate 
3611*0Sstevel@tonic-gate 	ASSERT(mutex_owned(&fdc->c_lolock));
3612*0Sstevel@tonic-gate 
3613*0Sstevel@tonic-gate 	csb = &fdc->c_csb;
3614*0Sstevel@tonic-gate 	unit = csb->csb_unit;
3615*0Sstevel@tonic-gate 
3616*0Sstevel@tonic-gate 
3617*0Sstevel@tonic-gate 	ASSERT(unit == fdc->c_un->un_unit_no);
3618*0Sstevel@tonic-gate 
3619*0Sstevel@tonic-gate retry:
3620*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "fdexec: cmd is %s\n",
3621*0Sstevel@tonic-gate 				fdcmds[csb->csb_cmds[0] & 0x1f].cmdname));
3622*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "fdexec: transfer rate = %d\n",
3623*0Sstevel@tonic-gate 	    fdc->c_un->un_chars->fdc_transfer_rate));
3624*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "fdexec: sec size = %d\n",
3625*0Sstevel@tonic-gate 	    fdc->c_un->un_chars->fdc_sec_size));
3626*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "fdexec: nblocks (512) = %d\n",
3627*0Sstevel@tonic-gate 	    fdc->c_un->un_label.dkl_map[2].dkl_nblk));
3628*0Sstevel@tonic-gate 
3629*0Sstevel@tonic-gate 	if ((fdc->c_fdtype & FDCTYPE_CTRLMASK) == FDCTYPE_82077) {
3630*0Sstevel@tonic-gate 		fdexec_turn_on_motor(fdc, flags, unit);
3631*0Sstevel@tonic-gate 	}
3632*0Sstevel@tonic-gate 
3633*0Sstevel@tonic-gate 
3634*0Sstevel@tonic-gate 	fdselect(fdc, unit, 1);	/* select drive */
3635*0Sstevel@tonic-gate 
3636*0Sstevel@tonic-gate 	/*
3637*0Sstevel@tonic-gate 	 * select data rate for this unit/command
3638*0Sstevel@tonic-gate 	 */
3639*0Sstevel@tonic-gate 	switch (fdc->c_un->un_chars->fdc_transfer_rate) {
3640*0Sstevel@tonic-gate 	case 500:
3641*0Sstevel@tonic-gate 		Dsr(fdc, 0);
3642*0Sstevel@tonic-gate 		break;
3643*0Sstevel@tonic-gate 	case 300:
3644*0Sstevel@tonic-gate 		Dsr(fdc, 1);
3645*0Sstevel@tonic-gate 		break;
3646*0Sstevel@tonic-gate 	case 250:
3647*0Sstevel@tonic-gate 		Dsr(fdc, 2);
3648*0Sstevel@tonic-gate 		break;
3649*0Sstevel@tonic-gate 	}
3650*0Sstevel@tonic-gate 	drv_usecwait(2);
3651*0Sstevel@tonic-gate 
3652*0Sstevel@tonic-gate 
3653*0Sstevel@tonic-gate 	/*
3654*0Sstevel@tonic-gate 	 * If checking for changed is enabled (i.e., not seeking in checkdisk),
3655*0Sstevel@tonic-gate 	 * we sample the DSKCHG line to see if the diskette has wandered away.
3656*0Sstevel@tonic-gate 	 */
3657*0Sstevel@tonic-gate 	if ((flags & FDXC_CHECKCHG) && fdsense_chng(fdc, unit)) {
3658*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "diskette changed\n"));
3659*0Sstevel@tonic-gate 		fdc->c_un->un_flags |= FDUNIT_CHANGED;
3660*0Sstevel@tonic-gate 
3661*0Sstevel@tonic-gate 		if (fdcheckdisk(fdc, unit)) {
3662*0Sstevel@tonic-gate 
3663*0Sstevel@tonic-gate 			(void) fd_unbind_handle(fdc);
3664*0Sstevel@tonic-gate 			return (ENXIO);
3665*0Sstevel@tonic-gate 
3666*0Sstevel@tonic-gate 		}
3667*0Sstevel@tonic-gate 	}
3668*0Sstevel@tonic-gate 
3669*0Sstevel@tonic-gate 	/*
3670*0Sstevel@tonic-gate 	 * gather some statistics
3671*0Sstevel@tonic-gate 	 */
3672*0Sstevel@tonic-gate 	switch (csb->csb_cmds[0] & 0x1f) {
3673*0Sstevel@tonic-gate 	case FDRAW_RDCMD:
3674*0Sstevel@tonic-gate 		fdc->fdstats.rd++;
3675*0Sstevel@tonic-gate 		break;
3676*0Sstevel@tonic-gate 	case FDRAW_WRCMD:
3677*0Sstevel@tonic-gate 		fdc->fdstats.wr++;
3678*0Sstevel@tonic-gate 		break;
3679*0Sstevel@tonic-gate 	case FDRAW_REZERO:
3680*0Sstevel@tonic-gate 		fdc->fdstats.recal++;
3681*0Sstevel@tonic-gate 		break;
3682*0Sstevel@tonic-gate 	case FDRAW_FORMAT:
3683*0Sstevel@tonic-gate 		fdc->fdstats.form++;
3684*0Sstevel@tonic-gate 		break;
3685*0Sstevel@tonic-gate 	default:
3686*0Sstevel@tonic-gate 		fdc->fdstats.other++;
3687*0Sstevel@tonic-gate 		break;
3688*0Sstevel@tonic-gate 	}
3689*0Sstevel@tonic-gate 
3690*0Sstevel@tonic-gate 	/*
3691*0Sstevel@tonic-gate 	 * Always set the opmode *prior* to poking the chip.
3692*0Sstevel@tonic-gate 	 * This way we don't have to do any locking at high level.
3693*0Sstevel@tonic-gate 	 */
3694*0Sstevel@tonic-gate 	csb->csb_raddr = 0;
3695*0Sstevel@tonic-gate 	csb->csb_rlen = 0;
3696*0Sstevel@tonic-gate 	if (csb->csb_opflags & CSB_OFSEEKOPS) {
3697*0Sstevel@tonic-gate 		csb->csb_opmode = 2;
3698*0Sstevel@tonic-gate 	} else if (csb->csb_opflags & CSB_OFIMMEDIATE) {
3699*0Sstevel@tonic-gate 		csb->csb_opmode = 0;
3700*0Sstevel@tonic-gate 	} else {
3701*0Sstevel@tonic-gate 		csb->csb_opmode = 1;	/* normal data xfer commands */
3702*0Sstevel@tonic-gate 		csb->csb_raddr = csb->csb_addr;
3703*0Sstevel@tonic-gate 		csb->csb_rlen = csb->csb_len;
3704*0Sstevel@tonic-gate 	}
3705*0Sstevel@tonic-gate 
3706*0Sstevel@tonic-gate 	bzero((caddr_t)csb->csb_rslt, 10);
3707*0Sstevel@tonic-gate 	csb->csb_status = 0;
3708*0Sstevel@tonic-gate 	csb->csb_cmdstat = 0;
3709*0Sstevel@tonic-gate 
3710*0Sstevel@tonic-gate 
3711*0Sstevel@tonic-gate 	/*
3712*0Sstevel@tonic-gate 	 * Program the DMA engine with the length and address of the transfer
3713*0Sstevel@tonic-gate 	 * (DMA is only used on a read or a write)
3714*0Sstevel@tonic-gate 	 */
3715*0Sstevel@tonic-gate 	if ((fdc->c_fdtype & FDCTYPE_DMA) &&
3716*0Sstevel@tonic-gate 			((fdc->c_csb.csb_read == CSB_READ) ||
3717*0Sstevel@tonic-gate 			    (fdc->c_csb.csb_read == CSB_WRITE)))  {
3718*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_hilock);
3719*0Sstevel@tonic-gate 
3720*0Sstevel@tonic-gate 		/* Reset the dcsr to clear it of all errors */
3721*0Sstevel@tonic-gate 
3722*0Sstevel@tonic-gate 		reset_dma_controller(fdc);
3723*0Sstevel@tonic-gate 
3724*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "cookie addr 0x%p\n",
3725*0Sstevel@tonic-gate 		    (void *)fdc->c_csb.csb_dmacookie.dmac_laddress));
3726*0Sstevel@tonic-gate 
3727*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "cookie length %ld\n",
3728*0Sstevel@tonic-gate 				fdc->c_csb.csb_dmacookie.dmac_size));
3729*0Sstevel@tonic-gate 		ASSERT(fdc->c_csb.csb_dmacookie.dmac_size);
3730*0Sstevel@tonic-gate 
3731*0Sstevel@tonic-gate 		set_data_count_register(fdc,
3732*0Sstevel@tonic-gate 			fdc->c_csb.csb_dmacookie.dmac_size);
3733*0Sstevel@tonic-gate 		set_data_address_register(fdc,
3734*0Sstevel@tonic-gate 			fdc->c_csb.csb_dmacookie.dmac_laddress);
3735*0Sstevel@tonic-gate 
3736*0Sstevel@tonic-gate 		/* Program the DCSR */
3737*0Sstevel@tonic-gate 
3738*0Sstevel@tonic-gate 		if (fdc->c_csb.csb_read == CSB_READ)
3739*0Sstevel@tonic-gate 			set_dma_mode(fdc, CSB_READ);
3740*0Sstevel@tonic-gate 		else
3741*0Sstevel@tonic-gate 			set_dma_mode(fdc, CSB_WRITE);
3742*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_hilock);
3743*0Sstevel@tonic-gate 	}
3744*0Sstevel@tonic-gate 
3745*0Sstevel@tonic-gate 	/*
3746*0Sstevel@tonic-gate 	 * I saw this (chip unexpectedly busy) happen when i shoved the
3747*0Sstevel@tonic-gate 	 * floppy into the drive while
3748*0Sstevel@tonic-gate 	 * running a dd if= /dev/rfd0c.	so it *is* possible for this to happen.
3749*0Sstevel@tonic-gate 	 * we need to do a ctlr reset ...
3750*0Sstevel@tonic-gate 	 */
3751*0Sstevel@tonic-gate 
3752*0Sstevel@tonic-gate 	if (Msr(fdc) & CB) {
3753*0Sstevel@tonic-gate 		/* tried to give command to chip when it is busy! */
3754*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L3, FDEM_EXEC,
3755*0Sstevel@tonic-gate 		    (C, "fdc: unexpectedly busy-stat 0x%x\n", Msr(fdc)));
3756*0Sstevel@tonic-gate 		csb->csb_cmdstat = 1;	/* XXX TBD ERRS NYD for now */
3757*0Sstevel@tonic-gate 
3758*0Sstevel@tonic-gate 		(void) fd_unbind_handle(fdc);
3759*0Sstevel@tonic-gate 		return (EBUSY);
3760*0Sstevel@tonic-gate 	}
3761*0Sstevel@tonic-gate 
3762*0Sstevel@tonic-gate 	/* Give command to the controller */
3763*0Sstevel@tonic-gate 	for (i = 0; i < (int)csb->csb_ncmds; i++) {
3764*0Sstevel@tonic-gate 
3765*0Sstevel@tonic-gate 		/* Test the readiness of the controller to receive the cmd */
3766*0Sstevel@tonic-gate 		for (to = FD_CRETRY; to; to--) {
3767*0Sstevel@tonic-gate 			if ((Msr(fdc) & (DIO|RQM)) == RQM)
3768*0Sstevel@tonic-gate 				break;
3769*0Sstevel@tonic-gate 		}
3770*0Sstevel@tonic-gate 		if (to == 0) {
3771*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L2, FDEM_EXEC,
3772*0Sstevel@tonic-gate 			    (C, "fdc: no RQM - stat 0x%x\n", Msr(fdc)));
3773*0Sstevel@tonic-gate 			csb->csb_cmdstat = 1;
3774*0Sstevel@tonic-gate 
3775*0Sstevel@tonic-gate 			(void) fd_unbind_handle(fdc);
3776*0Sstevel@tonic-gate 			return (EIO);
3777*0Sstevel@tonic-gate 		}
3778*0Sstevel@tonic-gate 
3779*0Sstevel@tonic-gate 		Set_Fifo(fdc, csb->csb_cmds[i]);
3780*0Sstevel@tonic-gate 
3781*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_EXEC,
3782*0Sstevel@tonic-gate 		    (C, "fdexec: sent 0x%x, Msr 0x%x\n", csb->csb_cmds[i],
3783*0Sstevel@tonic-gate 		    Msr(fdc)));
3784*0Sstevel@tonic-gate 
3785*0Sstevel@tonic-gate 	}
3786*0Sstevel@tonic-gate 
3787*0Sstevel@tonic-gate 
3788*0Sstevel@tonic-gate 	/*
3789*0Sstevel@tonic-gate 	 * Start watchdog timer on data transfer type commands - required
3790*0Sstevel@tonic-gate 	 * in case a diskette is not present or is unformatted
3791*0Sstevel@tonic-gate 	 */
3792*0Sstevel@tonic-gate 	if (csb->csb_opflags & CSB_OFTIMEIT) {
3793*0Sstevel@tonic-gate 		fdc->c_timeid = timeout(fdwatch, a,
3794*0Sstevel@tonic-gate 		    tosec * drv_usectohz(1000000));
3795*0Sstevel@tonic-gate 	}
3796*0Sstevel@tonic-gate 
3797*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_EXEC,
3798*0Sstevel@tonic-gate 	    (C, "fdexec: cmd sent, Msr 0x%x\n", Msr(fdc)));
3799*0Sstevel@tonic-gate 
3800*0Sstevel@tonic-gate 	/* If the operation has no results - then just return */
3801*0Sstevel@tonic-gate 	if (csb->csb_opflags & CSB_OFNORESULTS) {
3802*0Sstevel@tonic-gate 		if (fdc->c_fdtype & FDCTYPE_82077) {
3803*0Sstevel@tonic-gate 			if (fdc->c_mtimeid == 0) {
3804*0Sstevel@tonic-gate 				fdc->c_mtimeid = timeout(fdmotoff, a,
3805*0Sstevel@tonic-gate 					Motoff_delay);
3806*0Sstevel@tonic-gate 			}
3807*0Sstevel@tonic-gate 		}
3808*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "fdexec: O K ..\n"));
3809*0Sstevel@tonic-gate 
3810*0Sstevel@tonic-gate 		/*
3811*0Sstevel@tonic-gate 		 * Make sure the last byte is received well by the
3812*0Sstevel@tonic-gate 		 * controller. On faster CPU, it may still be busy
3813*0Sstevel@tonic-gate 		 * by the time another command comes here.
3814*0Sstevel@tonic-gate 		 */
3815*0Sstevel@tonic-gate 		for (to = FD_CRETRY; to; to--) {
3816*0Sstevel@tonic-gate 			if ((Msr(fdc) & (DIO|RQM)) == RQM)
3817*0Sstevel@tonic-gate 				break;
3818*0Sstevel@tonic-gate 			}
3819*0Sstevel@tonic-gate 		if (to == 0) {
3820*0Sstevel@tonic-gate 			csb->csb_cmdstat = 1;
3821*0Sstevel@tonic-gate 			return (EIO);
3822*0Sstevel@tonic-gate 		}
3823*0Sstevel@tonic-gate 
3824*0Sstevel@tonic-gate 		/*
3825*0Sstevel@tonic-gate 		 * An operation that has no results isn't doing DMA so,
3826*0Sstevel@tonic-gate 		 * there is no reason to try to unbind a handle
3827*0Sstevel@tonic-gate 		 */
3828*0Sstevel@tonic-gate 		return (0);
3829*0Sstevel@tonic-gate 	}
3830*0Sstevel@tonic-gate 
3831*0Sstevel@tonic-gate 	/*
3832*0Sstevel@tonic-gate 	 * If this operation has no interrupt AND an immediate result
3833*0Sstevel@tonic-gate 	 * then we just busy wait for the results and stuff them into
3834*0Sstevel@tonic-gate 	 * the csb
3835*0Sstevel@tonic-gate 	 */
3836*0Sstevel@tonic-gate 	if (csb->csb_opflags & CSB_OFIMMEDIATE) {
3837*0Sstevel@tonic-gate 		to = FD_RRETRY;
3838*0Sstevel@tonic-gate 		csb->csb_nrslts = 0;
3839*0Sstevel@tonic-gate 		/*
3840*0Sstevel@tonic-gate 		 * Wait while this command is still going on.
3841*0Sstevel@tonic-gate 		 */
3842*0Sstevel@tonic-gate 		while ((tmp = Msr(fdc)) & CB) {
3843*0Sstevel@tonic-gate 			/*
3844*0Sstevel@tonic-gate 			 * If RQM + DIO, then a result byte is at hand.
3845*0Sstevel@tonic-gate 			 */
3846*0Sstevel@tonic-gate 			if ((tmp & (RQM|DIO|CB)) == (RQM|DIO|CB)) {
3847*0Sstevel@tonic-gate 				csb->csb_rslt[csb->csb_nrslts++] =
3848*0Sstevel@tonic-gate 								Fifo(fdc);
3849*0Sstevel@tonic-gate 				/*
3850*0Sstevel@tonic-gate 				 * FDERRPRINT(FDEP_L4, FDEM_EXEC,
3851*0Sstevel@tonic-gate 				 *    (C, "fdexec: got result 0x%x\n",
3852*0Sstevel@tonic-gate 				 *    csb->csb_nrslts));
3853*0Sstevel@tonic-gate 				 */
3854*0Sstevel@tonic-gate 			} else if (--to == 0) {
3855*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L4, FDEM_EXEC,
3856*0Sstevel@tonic-gate 				    (C, "fdexec: timeout, Msr%x, nr%x\n",
3857*0Sstevel@tonic-gate 				    Msr(fdc), csb->csb_nrslts));
3858*0Sstevel@tonic-gate 
3859*0Sstevel@tonic-gate 				csb->csb_status = 2;
3860*0Sstevel@tonic-gate 				if (fdc->c_fdtype & FDCTYPE_82077) {
3861*0Sstevel@tonic-gate 					if (fdc->c_mtimeid == 0) {
3862*0Sstevel@tonic-gate 						fdc->c_mtimeid = timeout(
3863*0Sstevel@tonic-gate 						fdmotoff, a, Motoff_delay);
3864*0Sstevel@tonic-gate 					}
3865*0Sstevel@tonic-gate 				}
3866*0Sstevel@tonic-gate 				/*
3867*0Sstevel@tonic-gate 				 * There is no DMA happening.  No need to
3868*0Sstevel@tonic-gate 				 * try freeing a handle.
3869*0Sstevel@tonic-gate 				 */
3870*0Sstevel@tonic-gate 
3871*0Sstevel@tonic-gate 				return (EIO);
3872*0Sstevel@tonic-gate 			}
3873*0Sstevel@tonic-gate 		}
3874*0Sstevel@tonic-gate 	}
3875*0Sstevel@tonic-gate 
3876*0Sstevel@tonic-gate 	/*
3877*0Sstevel@tonic-gate 	 * If told to sleep here, well then sleep!
3878*0Sstevel@tonic-gate 	 */
3879*0Sstevel@tonic-gate 
3880*0Sstevel@tonic-gate 	if (flags & FDXC_SLEEP) {
3881*0Sstevel@tonic-gate 		fdc->c_flags |= FDCFLG_WAITING;
3882*0Sstevel@tonic-gate 		while (fdc->c_flags & FDCFLG_WAITING) {
3883*0Sstevel@tonic-gate 			cv_wait(&fdc->c_iocv, &fdc->c_lolock);
3884*0Sstevel@tonic-gate 		}
3885*0Sstevel@tonic-gate 	}
3886*0Sstevel@tonic-gate 
3887*0Sstevel@tonic-gate 	/*
3888*0Sstevel@tonic-gate 	 * kludge for end-of-cylinder error which must be ignored!!!
3889*0Sstevel@tonic-gate 	 */
3890*0Sstevel@tonic-gate 
3891*0Sstevel@tonic-gate 	if ((fdc->c_fdtype & FDCTYPE_TCBUG) &&
3892*0Sstevel@tonic-gate 	    ((csb->csb_rslt[0] & IC_SR0) == 0x40) &&
3893*0Sstevel@tonic-gate 	    (csb->csb_rslt[1] & EN_SR1))
3894*0Sstevel@tonic-gate 		csb->csb_rslt[0] &= ~IC_SR0;
3895*0Sstevel@tonic-gate 
3896*0Sstevel@tonic-gate 	/*
3897*0Sstevel@tonic-gate 	 * See if there was an error detected, if so, fdrecover()
3898*0Sstevel@tonic-gate 	 * will check it out and say what to do.
3899*0Sstevel@tonic-gate 	 *
3900*0Sstevel@tonic-gate 	 * Don't do this, though, if this was the Sense Drive Status
3901*0Sstevel@tonic-gate 	 * or the Dump Registers command.
3902*0Sstevel@tonic-gate 	 */
3903*0Sstevel@tonic-gate 	if (((csb->csb_rslt[0] & IC_SR0) || (fdc->c_csb.csb_dcsr_rslt) ||
3904*0Sstevel@tonic-gate 		(csb->csb_status)) &&
3905*0Sstevel@tonic-gate 		((csb->csb_cmds[0] != FDRAW_SENSE_DRV) &&
3906*0Sstevel@tonic-gate 		(csb->csb_cmds[0] != DUMPREG))) {
3907*0Sstevel@tonic-gate 		/* if it can restarted OK, then do so, else return error */
3908*0Sstevel@tonic-gate 		if (fdrecover(fdc) != 0) {
3909*0Sstevel@tonic-gate 			if (fdc->c_fdtype & FDCTYPE_82077) {
3910*0Sstevel@tonic-gate 				if (fdc->c_mtimeid == 0) {
3911*0Sstevel@tonic-gate 					fdc->c_mtimeid = timeout(fdmotoff,
3912*0Sstevel@tonic-gate 						a, Motoff_delay);
3913*0Sstevel@tonic-gate 				}
3914*0Sstevel@tonic-gate 			}
3915*0Sstevel@tonic-gate 
3916*0Sstevel@tonic-gate 			/*
3917*0Sstevel@tonic-gate 			 * If this was a dma transfer, unbind the handle so
3918*0Sstevel@tonic-gate 			 * that other transfers may use it.
3919*0Sstevel@tonic-gate 			 */
3920*0Sstevel@tonic-gate 
3921*0Sstevel@tonic-gate 			(void) fd_unbind_handle(fdc);
3922*0Sstevel@tonic-gate 			return (EIO);
3923*0Sstevel@tonic-gate 		} else {
3924*0Sstevel@tonic-gate 			/* ASSUMES that cmd is still intact in csb */
3925*0Sstevel@tonic-gate 			goto retry;
3926*0Sstevel@tonic-gate 		}
3927*0Sstevel@tonic-gate 	}
3928*0Sstevel@tonic-gate 
3929*0Sstevel@tonic-gate 	/* things went ok */
3930*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_82077) {
3931*0Sstevel@tonic-gate 		if (fdc->c_mtimeid == 0) {
3932*0Sstevel@tonic-gate 			fdc->c_mtimeid = timeout(fdmotoff, a, Motoff_delay);
3933*0Sstevel@tonic-gate 		}
3934*0Sstevel@tonic-gate 	}
3935*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "fdexec: O K ..........\n"));
3936*0Sstevel@tonic-gate 
3937*0Sstevel@tonic-gate 	if (fd_unbind_handle(fdc))
3938*0Sstevel@tonic-gate 		return (EIO);
3939*0Sstevel@tonic-gate 
3940*0Sstevel@tonic-gate 	return (0);
3941*0Sstevel@tonic-gate }
3942*0Sstevel@tonic-gate 
3943*0Sstevel@tonic-gate /*
3944*0Sstevel@tonic-gate  * Turn on the drive's motor
3945*0Sstevel@tonic-gate  *
3946*0Sstevel@tonic-gate  *	- called with the low level lock held
3947*0Sstevel@tonic-gate  */
3948*0Sstevel@tonic-gate static void
3949*0Sstevel@tonic-gate fdexec_turn_on_motor(struct fdctlr *fdc, int flags,  uint_t unit)
3950*0Sstevel@tonic-gate {
3951*0Sstevel@tonic-gate 	clock_t local_lbolt;
3952*0Sstevel@tonic-gate 	timeout_id_t timeid;
3953*0Sstevel@tonic-gate 
3954*0Sstevel@tonic-gate 	/*
3955*0Sstevel@tonic-gate 	 * The low level mutex may not be held over the call to
3956*0Sstevel@tonic-gate 	 * untimeout().  See the manpage for details.
3957*0Sstevel@tonic-gate 	 */
3958*0Sstevel@tonic-gate 	timeid = fdc->c_mtimeid;
3959*0Sstevel@tonic-gate 	fdc->c_mtimeid = 0;
3960*0Sstevel@tonic-gate 	if (timeid) {
3961*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
3962*0Sstevel@tonic-gate 		(void) untimeout(timeid);
3963*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_lolock);
3964*0Sstevel@tonic-gate 	}
3965*0Sstevel@tonic-gate 
3966*0Sstevel@tonic-gate 	ASSERT(fdc->c_un->un_unit_no == unit);
3967*0Sstevel@tonic-gate 
3968*0Sstevel@tonic-gate 
3969*0Sstevel@tonic-gate 	set_rotational_speed(fdc, unit);
3970*0Sstevel@tonic-gate 
3971*0Sstevel@tonic-gate 	if (!(Dor(fdc) & (MOTEN(unit)))) {
3972*0Sstevel@tonic-gate 		/*
3973*0Sstevel@tonic-gate 		 * Turn on the motor
3974*0Sstevel@tonic-gate 		 */
3975*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_EXEC,
3976*0Sstevel@tonic-gate 			(C, "fdexec: turning on motor\n"));
3977*0Sstevel@tonic-gate 
3978*0Sstevel@tonic-gate 		/* LINTED */
3979*0Sstevel@tonic-gate 		Set_dor(fdc, (MOTEN(unit)), 1);
3980*0Sstevel@tonic-gate 
3981*0Sstevel@tonic-gate 		if (flags & FDXC_SLEEP) {
3982*0Sstevel@tonic-gate 			local_lbolt = ddi_get_lbolt();
3983*0Sstevel@tonic-gate 			(void) cv_timedwait(&fdc->c_motoncv,
3984*0Sstevel@tonic-gate 			    &fdc->c_lolock, local_lbolt + Moton_delay);
3985*0Sstevel@tonic-gate 		} else {
3986*0Sstevel@tonic-gate 			drv_usecwait(1000000);
3987*0Sstevel@tonic-gate 		}
3988*0Sstevel@tonic-gate 	}
3989*0Sstevel@tonic-gate 
3990*0Sstevel@tonic-gate }
3991*0Sstevel@tonic-gate 
3992*0Sstevel@tonic-gate /*
3993*0Sstevel@tonic-gate  * fdrecover
3994*0Sstevel@tonic-gate  *	see if possible to retry an operation.
3995*0Sstevel@tonic-gate  *	All we can do is restart the operation.	 If we are out of allowed
3996*0Sstevel@tonic-gate  *	retries - return non-zero so that the higher levels will be notified.
3997*0Sstevel@tonic-gate  *
3998*0Sstevel@tonic-gate  * RETURNS: 0 if ok to restart, !0 if can't or out of retries
3999*0Sstevel@tonic-gate  *	- called with the low level lock held
4000*0Sstevel@tonic-gate  */
4001*0Sstevel@tonic-gate static int
4002*0Sstevel@tonic-gate fdrecover(struct fdctlr *fdc)
4003*0Sstevel@tonic-gate {
4004*0Sstevel@tonic-gate 	struct fdcsb *csb;
4005*0Sstevel@tonic-gate 
4006*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RECO, (C, "fdrecover\n"));
4007*0Sstevel@tonic-gate 	csb = &fdc->c_csb;
4008*0Sstevel@tonic-gate 
4009*0Sstevel@tonic-gate 	if (fdc->c_flags & FDCFLG_TIMEDOUT) {
4010*0Sstevel@tonic-gate 		struct fdcsb savecsb;
4011*0Sstevel@tonic-gate 
4012*0Sstevel@tonic-gate 		fdc->c_flags ^= FDCFLG_TIMEDOUT;
4013*0Sstevel@tonic-gate 		csb->csb_rslt[1] |= TO_SR1;
4014*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_RECO,
4015*0Sstevel@tonic-gate 		    (C, "fd%d: %s timed out\n", csb->csb_unit,
4016*0Sstevel@tonic-gate 		    fdcmds[csb->csb_cmds[0] & 0x1f].cmdname));
4017*0Sstevel@tonic-gate 
4018*0Sstevel@tonic-gate 		/* use private csb */
4019*0Sstevel@tonic-gate 		savecsb = fdc->c_csb;
4020*0Sstevel@tonic-gate 		bzero(&fdc->c_csb, sizeof (struct fdcsb));
4021*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_RECO, (C, "fdc: resetting\n"));
4022*0Sstevel@tonic-gate 
4023*0Sstevel@tonic-gate 		(void) fdreset(fdc);
4024*0Sstevel@tonic-gate 
4025*0Sstevel@tonic-gate 		if (fdc->c_fdtype & FDCTYPE_DMA) {
4026*0Sstevel@tonic-gate 			mutex_enter(&fdc->c_hilock);
4027*0Sstevel@tonic-gate 			/* Reset the DMA engine as well */
4028*0Sstevel@tonic-gate 			reset_dma_controller(fdc);
4029*0Sstevel@tonic-gate 			set_dma_control_register(fdc, DCSR_INIT_BITS);
4030*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_hilock);
4031*0Sstevel@tonic-gate 		}
4032*0Sstevel@tonic-gate 
4033*0Sstevel@tonic-gate 
4034*0Sstevel@tonic-gate 		/* check change first?? */
4035*0Sstevel@tonic-gate 		/* don't ckchg in fdexec, too convoluted */
4036*0Sstevel@tonic-gate 		(void) fdrecalseek(fdc, savecsb.csb_unit, -1, 0);
4037*0Sstevel@tonic-gate 		fdc->c_csb = savecsb; /* restore original csb */
4038*0Sstevel@tonic-gate 	}
4039*0Sstevel@tonic-gate 
4040*0Sstevel@tonic-gate 	/*
4041*0Sstevel@tonic-gate 	 * gather statistics on errors
4042*0Sstevel@tonic-gate 	 */
4043*0Sstevel@tonic-gate 	if (csb->csb_rslt[1] & DE_SR1) {
4044*0Sstevel@tonic-gate 		fdc->fdstats.de++;
4045*0Sstevel@tonic-gate 	}
4046*0Sstevel@tonic-gate 	if (csb->csb_rslt[1] & OR_SR1) {
4047*0Sstevel@tonic-gate 		fdc->fdstats.run++;
4048*0Sstevel@tonic-gate 	}
4049*0Sstevel@tonic-gate 	if (csb->csb_rslt[1] & (ND_SR1+MA_SR1)) {
4050*0Sstevel@tonic-gate 		fdc->fdstats.bfmt++;
4051*0Sstevel@tonic-gate 	}
4052*0Sstevel@tonic-gate 	if (csb->csb_rslt[1] & TO_SR1) {
4053*0Sstevel@tonic-gate 		fdc->fdstats.to++;
4054*0Sstevel@tonic-gate 	}
4055*0Sstevel@tonic-gate 
4056*0Sstevel@tonic-gate 	/*
4057*0Sstevel@tonic-gate 	 * If raw ioctl don't examine results just pass status
4058*0Sstevel@tonic-gate 	 * back via fdraw. Raw commands are timed too, so put this
4059*0Sstevel@tonic-gate 	 * after the above check.
4060*0Sstevel@tonic-gate 	 */
4061*0Sstevel@tonic-gate 	if (csb->csb_opflags & CSB_OFRAWIOCTL) {
4062*0Sstevel@tonic-gate 		return (1);
4063*0Sstevel@tonic-gate 	}
4064*0Sstevel@tonic-gate 
4065*0Sstevel@tonic-gate 
4066*0Sstevel@tonic-gate 	/*
4067*0Sstevel@tonic-gate 	 * if there was a pci bus error, do not retry
4068*0Sstevel@tonic-gate 	 */
4069*0Sstevel@tonic-gate 
4070*0Sstevel@tonic-gate 	    if (csb->csb_dcsr_rslt == 1) {
4071*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L3, FDEM_RECO,
4072*0Sstevel@tonic-gate 			    (C, "fd%d: host bus error\n", 0));
4073*0Sstevel@tonic-gate 		return (1);
4074*0Sstevel@tonic-gate 	    }
4075*0Sstevel@tonic-gate 
4076*0Sstevel@tonic-gate 	/*
4077*0Sstevel@tonic-gate 	 * If there was an error with the DMA functions, do not retry
4078*0Sstevel@tonic-gate 	 */
4079*0Sstevel@tonic-gate 	if (csb->csb_dma_rslt == 1) {
4080*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_RECO,
4081*0Sstevel@tonic-gate 			    (C, "fd%d: DMA interface error\n", csb->csb_unit));
4082*0Sstevel@tonic-gate 		return (1);
4083*0Sstevel@tonic-gate 	}
4084*0Sstevel@tonic-gate 
4085*0Sstevel@tonic-gate 
4086*0Sstevel@tonic-gate 	/*
4087*0Sstevel@tonic-gate 	 * if we have run out of retries, return an error
4088*0Sstevel@tonic-gate 	 * XXX need better status interp
4089*0Sstevel@tonic-gate 	 */
4090*0Sstevel@tonic-gate 
4091*0Sstevel@tonic-gate 	csb->csb_retrys++;
4092*0Sstevel@tonic-gate 	if (csb->csb_retrys > csb->csb_maxretry) {
4093*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L3, FDEM_RECO,
4094*0Sstevel@tonic-gate 		    (C, "fd%d: %s failed (%x %x %x)\n",
4095*0Sstevel@tonic-gate 		    0, fdcmds[csb->csb_cmds[0] & 0x1f].cmdname,
4096*0Sstevel@tonic-gate 		    csb->csb_rslt[0], csb->csb_rslt[1], csb->csb_rslt[2]));
4097*0Sstevel@tonic-gate 		if (csb->csb_rslt[1] & NW_SR1) {
4098*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L3, FDEM_RECO,
4099*0Sstevel@tonic-gate 			    (C, "fd%d: not writable\n", 0));
4100*0Sstevel@tonic-gate 		}
4101*0Sstevel@tonic-gate 		if (csb->csb_rslt[1] & DE_SR1) {
4102*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L3, FDEM_RECO,
4103*0Sstevel@tonic-gate 			    (C, "fd%d: crc error blk %d\n", 0,
4104*0Sstevel@tonic-gate 			    (int)fdc->c_current->b_blkno));
4105*0Sstevel@tonic-gate 		}
4106*0Sstevel@tonic-gate 		if (csb->csb_rslt[1] & OR_SR1) {
4107*0Sstevel@tonic-gate 			if (fdc->c_fdtype & FDCTYPE_SB) {
4108*0Sstevel@tonic-gate 				/*
4109*0Sstevel@tonic-gate 				 * When using southbridge chip we need to
4110*0Sstevel@tonic-gate 				 * retry atleast 10 times to shake off the
4111*0Sstevel@tonic-gate 				 * underrun err.
4112*0Sstevel@tonic-gate 				 */
4113*0Sstevel@tonic-gate 				if (csb->csb_retrys <= rwretry)
4114*0Sstevel@tonic-gate 					return (0);
4115*0Sstevel@tonic-gate 			}
4116*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L3, FDEM_RECO,
4117*0Sstevel@tonic-gate 			    (C, "fd%d: over/underrun\n", 0));
4118*0Sstevel@tonic-gate 		}
4119*0Sstevel@tonic-gate 
4120*0Sstevel@tonic-gate 		if (csb->csb_rslt[1] & (ND_SR1+MA_SR1)) {
4121*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L3, FDEM_RECO,
4122*0Sstevel@tonic-gate 			    (C, "fd%d: bad format\n", 0));
4123*0Sstevel@tonic-gate 		}
4124*0Sstevel@tonic-gate 
4125*0Sstevel@tonic-gate 		if (csb->csb_rslt[1] & TO_SR1) {
4126*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L3, FDEM_RECO,
4127*0Sstevel@tonic-gate 			    (C, "fd%d: timeout\n", 0));
4128*0Sstevel@tonic-gate 		}
4129*0Sstevel@tonic-gate 
4130*0Sstevel@tonic-gate 		csb->csb_cmdstat = 1; /* failed - give up */
4131*0Sstevel@tonic-gate 		return (1);
4132*0Sstevel@tonic-gate 	}
4133*0Sstevel@tonic-gate 
4134*0Sstevel@tonic-gate 	if (csb->csb_opflags & CSB_OFSEEKOPS) {
4135*0Sstevel@tonic-gate 		/* seek, recal type commands - just look at st0 */
4136*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L2, FDEM_RECO,
4137*0Sstevel@tonic-gate 		    (C, "fd%d: %s error : st0 0x%x\n", csb->csb_unit,
4138*0Sstevel@tonic-gate 		    fdcmds[csb->csb_cmds[0] & 0x1f].cmdname,
4139*0Sstevel@tonic-gate 		    csb->csb_rslt[0]));
4140*0Sstevel@tonic-gate 	}
4141*0Sstevel@tonic-gate 	if (csb->csb_opflags & CSB_OFXFEROPS) {
4142*0Sstevel@tonic-gate 		/* rd, wr, fmt type commands - look at st0, st1, st2 */
4143*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L2, FDEM_RECO,
4144*0Sstevel@tonic-gate 		    (C, "fd%d: %s error : st0=0x%x st1=0x%x st2=0x%x\n",
4145*0Sstevel@tonic-gate 		    csb->csb_unit, fdcmds[csb->csb_cmds[0] & 0x1f].cmdname,
4146*0Sstevel@tonic-gate 		    csb->csb_rslt[0], csb->csb_rslt[1], csb->csb_rslt[2]));
4147*0Sstevel@tonic-gate 	}
4148*0Sstevel@tonic-gate 
4149*0Sstevel@tonic-gate 	return (0);	/* tell fdexec to retry */
4150*0Sstevel@tonic-gate }
4151*0Sstevel@tonic-gate 
4152*0Sstevel@tonic-gate /*
4153*0Sstevel@tonic-gate  * Interrupt handle for DMA
4154*0Sstevel@tonic-gate  */
4155*0Sstevel@tonic-gate 
4156*0Sstevel@tonic-gate static uint_t
4157*0Sstevel@tonic-gate fdintr_dma()
4158*0Sstevel@tonic-gate {
4159*0Sstevel@tonic-gate 	struct fdctlr   *fdc;
4160*0Sstevel@tonic-gate 	off_t		off;
4161*0Sstevel@tonic-gate 	size_t		len;
4162*0Sstevel@tonic-gate 	uint_t		ccount;
4163*0Sstevel@tonic-gate 	uint_t		windex;
4164*0Sstevel@tonic-gate 	uint_t		done = 0;
4165*0Sstevel@tonic-gate 	int		tmp_dcsr;
4166*0Sstevel@tonic-gate 	int		to;
4167*0Sstevel@tonic-gate 	uchar_t		tmp;
4168*0Sstevel@tonic-gate 	int		i = 0;
4169*0Sstevel@tonic-gate 	int		res = DDI_INTR_UNCLAIMED;
4170*0Sstevel@tonic-gate 	int		not_cheerio = 1;
4171*0Sstevel@tonic-gate 
4172*0Sstevel@tonic-gate 	/* search for a controller that's expecting an interrupt */
4173*0Sstevel@tonic-gate 	fdc = fdctlrs;
4174*0Sstevel@tonic-gate 
4175*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
4176*0Sstevel@tonic-gate 		tmp_dcsr = get_dma_control_register(fdc);
4177*0Sstevel@tonic-gate 		if (!(tmp_dcsr & DCSR_INT_PEND) && !(DCSR_ERR_PEND & tmp_dcsr))
4178*0Sstevel@tonic-gate 			return (res);
4179*0Sstevel@tonic-gate 		not_cheerio = 0;
4180*0Sstevel@tonic-gate 	}
4181*0Sstevel@tonic-gate 
4182*0Sstevel@tonic-gate 	mutex_enter(&fdc->c_hilock);
4183*0Sstevel@tonic-gate 
4184*0Sstevel@tonic-gate 	if (fdc->c_csb.csb_opmode == 0x0) {
4185*0Sstevel@tonic-gate 		fdc->c_csb.csb_opmode = 2;
4186*0Sstevel@tonic-gate 	}
4187*0Sstevel@tonic-gate 	if (fdc->sb_dma_lock) {
4188*0Sstevel@tonic-gate 		release_sb_dma(fdc);
4189*0Sstevel@tonic-gate 	}
4190*0Sstevel@tonic-gate 
4191*0Sstevel@tonic-gate 	/*
4192*0Sstevel@tonic-gate 	 * An interrupt can come from either the floppy controller or
4193*0Sstevel@tonic-gate 	 * or the DMA engine.  The DMA engine will only issue an
4194*0Sstevel@tonic-gate 	 * interrupt if there was an error.
4195*0Sstevel@tonic-gate 	 */
4196*0Sstevel@tonic-gate 
4197*0Sstevel@tonic-gate 	switch (fdc->c_csb.csb_opmode) {
4198*0Sstevel@tonic-gate 		case 0x1:
4199*0Sstevel@tonic-gate 			/* read/write/format data-xfer case */
4200*0Sstevel@tonic-gate 
4201*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_INTR,
4202*0Sstevel@tonic-gate 				(C, "fdintr_dma: opmode 1\n"));
4203*0Sstevel@tonic-gate 
4204*0Sstevel@tonic-gate 			/*
4205*0Sstevel@tonic-gate 			 * See if the interrupt is from the floppy
4206*0Sstevel@tonic-gate 			 * controller.  If there is, take out the status bytes.
4207*0Sstevel@tonic-gate 			 */
4208*0Sstevel@tonic-gate 
4209*0Sstevel@tonic-gate 			if (not_cheerio || (tmp_dcsr & DCSR_INT_PEND)) {
4210*0Sstevel@tonic-gate 
4211*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_INTR,
4212*0Sstevel@tonic-gate 					(C, "fdintr_dma: INT_PEND \n"));
4213*0Sstevel@tonic-gate 
4214*0Sstevel@tonic-gate 				res = DDI_INTR_CLAIMED;
4215*0Sstevel@tonic-gate 
4216*0Sstevel@tonic-gate 				to = FD_RRETRY;
4217*0Sstevel@tonic-gate 				fdc->c_csb.csb_nrslts = 0;
4218*0Sstevel@tonic-gate 
4219*0Sstevel@tonic-gate 				/* check status */
4220*0Sstevel@tonic-gate 				i = 0;
4221*0Sstevel@tonic-gate 
4222*0Sstevel@tonic-gate 				/*
4223*0Sstevel@tonic-gate 				 * CB turns off once all the result bytes are
4224*0Sstevel@tonic-gate 				 *  read.
4225*0Sstevel@tonic-gate 				 *
4226*0Sstevel@tonic-gate 				 * NOTE: the counters are there so that the
4227*0Sstevel@tonic-gate 				 * handler will never get stuck in a loop.
4228*0Sstevel@tonic-gate 				 * If the counters do reach their maximum
4229*0Sstevel@tonic-gate 				 * values, then a catastrophic error has
4230*0Sstevel@tonic-gate 				 * occurred.  This should never be the case.
4231*0Sstevel@tonic-gate 				 * The counters only came into play during
4232*0Sstevel@tonic-gate 				 * development.
4233*0Sstevel@tonic-gate 				 */
4234*0Sstevel@tonic-gate 				while (((tmp = Msr(fdc)) & CB) &&
4235*0Sstevel@tonic-gate 					(i < 1000001)) {
4236*0Sstevel@tonic-gate 
4237*0Sstevel@tonic-gate 					/*
4238*0Sstevel@tonic-gate 					 * If RQM + DIO, then a result byte
4239*0Sstevel@tonic-gate 					 * is at hand.
4240*0Sstevel@tonic-gate 					 */
4241*0Sstevel@tonic-gate 					if ((tmp & (RQM|DIO|CB)) ==
4242*0Sstevel@tonic-gate 								(RQM|DIO|CB)) {
4243*0Sstevel@tonic-gate 						fdc->c_csb.csb_rslt
4244*0Sstevel@tonic-gate 						[fdc->c_csb.csb_nrslts++]
4245*0Sstevel@tonic-gate 							    = Fifo(fdc);
4246*0Sstevel@tonic-gate 
4247*0Sstevel@tonic-gate 						FDERRPRINT(FDEP_L1, FDEM_INTR,
4248*0Sstevel@tonic-gate 						(C, "fdintr_dma: res 0x%x\n",
4249*0Sstevel@tonic-gate 							fdc->c_csb.csb_rslt
4250*0Sstevel@tonic-gate 							[fdc->c_csb.csb_nrslts
4251*0Sstevel@tonic-gate 							- 1]));
4252*0Sstevel@tonic-gate 
4253*0Sstevel@tonic-gate 					} else if (--to == 0) {
4254*0Sstevel@tonic-gate 						/*
4255*0Sstevel@tonic-gate 						 * controller was never
4256*0Sstevel@tonic-gate 						 * ready to give results
4257*0Sstevel@tonic-gate 						 */
4258*0Sstevel@tonic-gate 						fdc->c_csb.csb_status = 2;
4259*0Sstevel@tonic-gate 						break;
4260*0Sstevel@tonic-gate 					}
4261*0Sstevel@tonic-gate 				i++;
4262*0Sstevel@tonic-gate 				}
4263*0Sstevel@tonic-gate 				if (i == 10000) {
4264*0Sstevel@tonic-gate 					FDERRPRINT(FDEP_L1, FDEM_INTR,
4265*0Sstevel@tonic-gate 						(C, "First loop overran\n"));
4266*0Sstevel@tonic-gate 				}
4267*0Sstevel@tonic-gate 			}
4268*0Sstevel@tonic-gate 
4269*0Sstevel@tonic-gate 			/*
4270*0Sstevel@tonic-gate 			 * See if the interrupt is from the DMA engine,
4271*0Sstevel@tonic-gate 			 * which will only interrupt on an error
4272*0Sstevel@tonic-gate 			 */
4273*0Sstevel@tonic-gate 			if ((!not_cheerio) && (tmp_dcsr & DCSR_ERR_PEND)) {
4274*0Sstevel@tonic-gate 
4275*0Sstevel@tonic-gate 				res = DDI_INTR_CLAIMED;
4276*0Sstevel@tonic-gate 
4277*0Sstevel@tonic-gate 				done = 1;
4278*0Sstevel@tonic-gate 				fdc->c_csb.csb_dcsr_rslt = 1;
4279*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_INTR,
4280*0Sstevel@tonic-gate 					(C, "fdintr_dma: Error pending\n"));
4281*0Sstevel@tonic-gate 				reset_dma_controller(fdc);
4282*0Sstevel@tonic-gate 				set_dma_control_register(fdc, DCSR_INIT_BITS);
4283*0Sstevel@tonic-gate 				break;
4284*0Sstevel@tonic-gate 			}
4285*0Sstevel@tonic-gate 
4286*0Sstevel@tonic-gate 			/* TCBUG kludge */
4287*0Sstevel@tonic-gate 			if ((fdc->c_fdtype & FDCTYPE_TCBUG) &&
4288*0Sstevel@tonic-gate 				((fdc->c_csb.csb_rslt[0] & IC_SR0) == 0x40) &&
4289*0Sstevel@tonic-gate 				(fdc->c_csb.csb_rslt[1] & EN_SR1)) {
4290*0Sstevel@tonic-gate 
4291*0Sstevel@tonic-gate 				fdc->c_csb.csb_rslt[0] &= ~IC_SR0;
4292*0Sstevel@tonic-gate 
4293*0Sstevel@tonic-gate 				fdc->c_csb.csb_rslt[1] &= ~EN_SR1;
4294*0Sstevel@tonic-gate 
4295*0Sstevel@tonic-gate 
4296*0Sstevel@tonic-gate 			}
4297*0Sstevel@tonic-gate 
4298*0Sstevel@tonic-gate 
4299*0Sstevel@tonic-gate 			/* Exit if there were errors in the DMA */
4300*0Sstevel@tonic-gate 			if (((fdc->c_csb.csb_rslt[0] & IC_SR0) != 0) ||
4301*0Sstevel@tonic-gate 			    (fdc->c_csb.csb_rslt[1] != 0) ||
4302*0Sstevel@tonic-gate 			    (fdc->c_csb.csb_rslt[2] != 0)) {
4303*0Sstevel@tonic-gate 				done = 1;
4304*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_INTR,
4305*0Sstevel@tonic-gate 					(C, "fdintr_dma: errors in command\n"));
4306*0Sstevel@tonic-gate 
4307*0Sstevel@tonic-gate 
4308*0Sstevel@tonic-gate 				break;
4309*0Sstevel@tonic-gate 			}
4310*0Sstevel@tonic-gate 
4311*0Sstevel@tonic-gate 
4312*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_INTR,
4313*0Sstevel@tonic-gate 				(C, "fdintr_dma: dbcr 0x%x\n",
4314*0Sstevel@tonic-gate 				get_data_count_register(fdc)));
4315*0Sstevel@tonic-gate 			/*
4316*0Sstevel@tonic-gate 			 * The csb_ccount is the number of cookies that still
4317*0Sstevel@tonic-gate 			 * need to be processed.  A cookie was just processed
4318*0Sstevel@tonic-gate 			 * so decrement the cookie counter.
4319*0Sstevel@tonic-gate 			 */
4320*0Sstevel@tonic-gate 			if (fdc->c_csb.csb_ccount == 0) {
4321*0Sstevel@tonic-gate 				done = 1;
4322*0Sstevel@tonic-gate 				break;
4323*0Sstevel@tonic-gate 			}
4324*0Sstevel@tonic-gate 			fdc->c_csb.csb_ccount--;
4325*0Sstevel@tonic-gate 			ccount = fdc->c_csb.csb_ccount;
4326*0Sstevel@tonic-gate 
4327*0Sstevel@tonic-gate 			windex = fdc->c_csb.csb_windex;
4328*0Sstevel@tonic-gate 
4329*0Sstevel@tonic-gate 			/*
4330*0Sstevel@tonic-gate 			 * If there are no more cookies and all the windows
4331*0Sstevel@tonic-gate 			 * have been DMA'd, then DMA is done.
4332*0Sstevel@tonic-gate 			 *
4333*0Sstevel@tonic-gate 			 */
4334*0Sstevel@tonic-gate 			if ((ccount == 0) && (windex == fdc->c_csb.csb_nwin)) {
4335*0Sstevel@tonic-gate 
4336*0Sstevel@tonic-gate 				done = 1;
4337*0Sstevel@tonic-gate 
4338*0Sstevel@tonic-gate 				/*
4339*0Sstevel@tonic-gate 				 * The handle is unbound in fdexec
4340*0Sstevel@tonic-gate 				 */
4341*0Sstevel@tonic-gate 
4342*0Sstevel@tonic-gate 				break;
4343*0Sstevel@tonic-gate 			}
4344*0Sstevel@tonic-gate 
4345*0Sstevel@tonic-gate 			if (ccount != 0) {
4346*0Sstevel@tonic-gate 				/* process the next cookie */
4347*0Sstevel@tonic-gate 				ddi_dma_nextcookie(fdc->c_dmahandle,
4348*0Sstevel@tonic-gate 				    &fdc->c_csb.csb_dmacookie);
4349*0Sstevel@tonic-gate 
4350*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_INTR,
4351*0Sstevel@tonic-gate 				    (C, "cookie addr 0x%" PRIx64 "\n",
4352*0Sstevel@tonic-gate 				    fdc->c_csb.csb_dmacookie.dmac_laddress));
4353*0Sstevel@tonic-gate 
4354*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_INTR,
4355*0Sstevel@tonic-gate 				    (C, "cookie length %lu\n",
4356*0Sstevel@tonic-gate 				    fdc->c_csb.csb_dmacookie.dmac_size));
4357*0Sstevel@tonic-gate 
4358*0Sstevel@tonic-gate 			} else {
4359*0Sstevel@tonic-gate 
4360*0Sstevel@tonic-gate 				(void) ddi_dma_getwin(fdc->c_dmahandle,
4361*0Sstevel@tonic-gate 				    fdc->c_csb.csb_windex,
4362*0Sstevel@tonic-gate 				    &off, &len,
4363*0Sstevel@tonic-gate 				    &fdc->c_csb.csb_dmacookie,
4364*0Sstevel@tonic-gate 				    &fdc->c_csb.csb_ccount);
4365*0Sstevel@tonic-gate 				fdc->c_csb.csb_windex++;
4366*0Sstevel@tonic-gate 
4367*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_INTR,
4368*0Sstevel@tonic-gate 				    (C, "fdintr_dma: process %d window\n",
4369*0Sstevel@tonic-gate 				    fdc->c_csb.csb_windex));
4370*0Sstevel@tonic-gate 
4371*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_INTR,
4372*0Sstevel@tonic-gate 				    (C, "fdintr_dma: process no. cookies %d\n",
4373*0Sstevel@tonic-gate 				    fdc->c_csb.csb_ccount));
4374*0Sstevel@tonic-gate 
4375*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_INTR,
4376*0Sstevel@tonic-gate 				    (C, "cookie addr 0x%" PRIx64 "\n",
4377*0Sstevel@tonic-gate 				    fdc->c_csb.csb_dmacookie.dmac_laddress));
4378*0Sstevel@tonic-gate 
4379*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_INTR,
4380*0Sstevel@tonic-gate 				    (C, "cookie length %lu\n",
4381*0Sstevel@tonic-gate 				    fdc->c_csb.csb_dmacookie.dmac_size));
4382*0Sstevel@tonic-gate 			}
4383*0Sstevel@tonic-gate 
4384*0Sstevel@tonic-gate 			/*
4385*0Sstevel@tonic-gate 			 * Program the DMA engine with the length and
4386*0Sstevel@tonic-gate 			 * the address of the transfer
4387*0Sstevel@tonic-gate 			 */
4388*0Sstevel@tonic-gate 
4389*0Sstevel@tonic-gate 			ASSERT(fdc->c_csb.csb_dmacookie.dmac_size);
4390*0Sstevel@tonic-gate 
4391*0Sstevel@tonic-gate 			set_data_count_register(fdc,
4392*0Sstevel@tonic-gate 				fdc->c_csb.csb_dmacookie.dmac_size);
4393*0Sstevel@tonic-gate 			set_data_address_register(fdc,
4394*0Sstevel@tonic-gate 				fdc->c_csb.csb_dmacookie.dmac_laddress);
4395*0Sstevel@tonic-gate 
4396*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_INTR, (C,
4397*0Sstevel@tonic-gate 			    "fdintr_dma: size 0x%lx\n",
4398*0Sstevel@tonic-gate 			    fdc->c_csb.csb_dmacookie.dmac_size));
4399*0Sstevel@tonic-gate 
4400*0Sstevel@tonic-gate 
4401*0Sstevel@tonic-gate 			/* reprogram the controller */
4402*0Sstevel@tonic-gate 			fdc->c_csb.csb_cmds[2] = fdc->c_csb.csb_rslt[3];
4403*0Sstevel@tonic-gate 			fdc->c_csb.csb_cmds[3] = fdc->c_csb.csb_rslt[4];
4404*0Sstevel@tonic-gate 			fdc->c_csb.csb_cmds[4] = fdc->c_csb.csb_rslt[5];
4405*0Sstevel@tonic-gate 			fdc->c_csb.csb_cmds[1] = (fdc->c_csb.csb_cmds[1]
4406*0Sstevel@tonic-gate 				& ~0x04) | (fdc->c_csb.csb_rslt[4] << 2);
4407*0Sstevel@tonic-gate 
4408*0Sstevel@tonic-gate 			for (i = 0; i < (int)fdc->c_csb.csb_ncmds; i++) {
4409*0Sstevel@tonic-gate 
4410*0Sstevel@tonic-gate 				/*
4411*0Sstevel@tonic-gate 				 * Test the readiness of the controller
4412*0Sstevel@tonic-gate 				 * to receive the cmd
4413*0Sstevel@tonic-gate 				 */
4414*0Sstevel@tonic-gate 				for (to = FD_CRETRY; to; to--) {
4415*0Sstevel@tonic-gate 					if ((Msr(fdc) & (DIO|RQM)) == RQM)
4416*0Sstevel@tonic-gate 						break;
4417*0Sstevel@tonic-gate 				}
4418*0Sstevel@tonic-gate 				if (to == 0) {
4419*0Sstevel@tonic-gate 					FDERRPRINT(FDEP_L2, FDEM_EXEC,
4420*0Sstevel@tonic-gate 					(C,
4421*0Sstevel@tonic-gate 					"fdc: no RQM - stat 0x%x\n", Msr(fdc)));
4422*0Sstevel@tonic-gate 					/* stop the DMA from happening */
4423*0Sstevel@tonic-gate 					fdc->c_csb.csb_status = 2;
4424*0Sstevel@tonic-gate 					done = 1;
4425*0Sstevel@tonic-gate 					break;
4426*0Sstevel@tonic-gate 				}
4427*0Sstevel@tonic-gate 
4428*0Sstevel@tonic-gate 				Set_Fifo(fdc, fdc->c_csb.csb_cmds[i]);
4429*0Sstevel@tonic-gate 
4430*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_INTR,
4431*0Sstevel@tonic-gate 					(C,
4432*0Sstevel@tonic-gate 					"fdintr_dma: sent 0x%x, Msr 0x%x\n",
4433*0Sstevel@tonic-gate 					fdc->c_csb.csb_cmds[i], Msr(fdc)));
4434*0Sstevel@tonic-gate 			}
4435*0Sstevel@tonic-gate 
4436*0Sstevel@tonic-gate 			/* reenable DMA */
4437*0Sstevel@tonic-gate 			if ((!not_cheerio) && (!done))
4438*0Sstevel@tonic-gate 				set_dma_control_register(fdc, tmp_dcsr |
4439*0Sstevel@tonic-gate 					DCSR_EN_DMA);
4440*0Sstevel@tonic-gate 			break;
4441*0Sstevel@tonic-gate 
4442*0Sstevel@tonic-gate 		case 0x2:
4443*0Sstevel@tonic-gate 		/* seek/recal type cmd */
4444*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_INTR,
4445*0Sstevel@tonic-gate 				(C, "fintr_dma: opmode 2\n"));
4446*0Sstevel@tonic-gate 
4447*0Sstevel@tonic-gate 			/*
4448*0Sstevel@tonic-gate 			 *  See if the interrupt is from the DMA engine,
4449*0Sstevel@tonic-gate 			 *  which will only interrupt if there was an error.
4450*0Sstevel@tonic-gate 			 */
4451*0Sstevel@tonic-gate 			if ((!not_cheerio) && (tmp_dcsr & DCSR_ERR_PEND)) {
4452*0Sstevel@tonic-gate 				res = DDI_INTR_CLAIMED;
4453*0Sstevel@tonic-gate 				done = 1;
4454*0Sstevel@tonic-gate 				fdc->c_csb.csb_dcsr_rslt = 1;
4455*0Sstevel@tonic-gate 				reset_dma_controller(fdc);
4456*0Sstevel@tonic-gate 				set_dma_control_register(fdc, DCSR_INIT_BITS);
4457*0Sstevel@tonic-gate 
4458*0Sstevel@tonic-gate 				break;
4459*0Sstevel@tonic-gate 			}
4460*0Sstevel@tonic-gate 
4461*0Sstevel@tonic-gate 
4462*0Sstevel@tonic-gate 			/* See if the interrupt is from the floppy controller */
4463*0Sstevel@tonic-gate 			if (not_cheerio || (tmp_dcsr & DCSR_INT_PEND)) {
4464*0Sstevel@tonic-gate 
4465*0Sstevel@tonic-gate 				res = DDI_INTR_CLAIMED;
4466*0Sstevel@tonic-gate 
4467*0Sstevel@tonic-gate 
4468*0Sstevel@tonic-gate 				/*
4469*0Sstevel@tonic-gate 				 * Wait until there's no longer a command
4470*0Sstevel@tonic-gate 				 * in progress
4471*0Sstevel@tonic-gate 				 */
4472*0Sstevel@tonic-gate 
4473*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L1, FDEM_INTR,
4474*0Sstevel@tonic-gate 					(C, "fdintr_dma: interrupt pending\n"));
4475*0Sstevel@tonic-gate 				i = 0;
4476*0Sstevel@tonic-gate 				while (((Msr(fdc) & CB)) && (i < 10000)) {
4477*0Sstevel@tonic-gate 					i++;
4478*0Sstevel@tonic-gate 				}
4479*0Sstevel@tonic-gate 
4480*0Sstevel@tonic-gate 				if (i == 10000)
4481*0Sstevel@tonic-gate 					FDERRPRINT(FDEP_L1, FDEM_INTR,
4482*0Sstevel@tonic-gate 						(C, "2nd loop overran !!!\n"));
4483*0Sstevel@tonic-gate 
4484*0Sstevel@tonic-gate 				/*
4485*0Sstevel@tonic-gate 				 * Check the RQM bit to see if the controller is
4486*0Sstevel@tonic-gate 				 * ready to transfer status of the command.
4487*0Sstevel@tonic-gate 				 */
4488*0Sstevel@tonic-gate 				i = 0;
4489*0Sstevel@tonic-gate 				while ((!(Msr(fdc) & RQM)) && (i < 10000)) {
4490*0Sstevel@tonic-gate 					i++;
4491*0Sstevel@tonic-gate 				}
4492*0Sstevel@tonic-gate 
4493*0Sstevel@tonic-gate 				if (i == 10000)
4494*0Sstevel@tonic-gate 					FDERRPRINT(FDEP_L1, FDEM_INTR,
4495*0Sstevel@tonic-gate 					    (C, "3rd loop overran !!!\n"));
4496*0Sstevel@tonic-gate 
4497*0Sstevel@tonic-gate 				/*
4498*0Sstevel@tonic-gate 				 * Issue the Sense Interrupt Status Command
4499*0Sstevel@tonic-gate 				 */
4500*0Sstevel@tonic-gate 				Set_Fifo(fdc, SNSISTAT);
4501*0Sstevel@tonic-gate 
4502*0Sstevel@tonic-gate 				i = 0;
4503*0Sstevel@tonic-gate 				while ((!(Msr(fdc) & RQM)) && (i < 10000)) {
4504*0Sstevel@tonic-gate 					i++;
4505*0Sstevel@tonic-gate 				}
4506*0Sstevel@tonic-gate 				if (i == 10000)
4507*0Sstevel@tonic-gate 					FDERRPRINT(FDEP_L1, FDEM_INTR,
4508*0Sstevel@tonic-gate 					(C, "4th loop overran !!!\n"));
4509*0Sstevel@tonic-gate 
4510*0Sstevel@tonic-gate 				/* Store the first result byte */
4511*0Sstevel@tonic-gate 				fdc->c_csb.csb_rslt[0] = Fifo(fdc);
4512*0Sstevel@tonic-gate 
4513*0Sstevel@tonic-gate 				i = 0;
4514*0Sstevel@tonic-gate 				while ((!(Msr(fdc) & RQM)) && (i < 10000)) {
4515*0Sstevel@tonic-gate 					i++;
4516*0Sstevel@tonic-gate 				}
4517*0Sstevel@tonic-gate 				if (i == 10000)
4518*0Sstevel@tonic-gate 					FDERRPRINT(FDEP_L1, FDEM_INTR,
4519*0Sstevel@tonic-gate 					(C, "5th loop overran !!!\n"));
4520*0Sstevel@tonic-gate 
4521*0Sstevel@tonic-gate 				/* Store the second  result byte */
4522*0Sstevel@tonic-gate 				fdc->c_csb.csb_rslt[1] = Fifo(fdc);
4523*0Sstevel@tonic-gate 
4524*0Sstevel@tonic-gate 				done = 1;
4525*0Sstevel@tonic-gate 			}
4526*0Sstevel@tonic-gate 
4527*0Sstevel@tonic-gate 		}
4528*0Sstevel@tonic-gate 
4529*0Sstevel@tonic-gate 	/*
4530*0Sstevel@tonic-gate 	 * We are done with the actual interrupt handling here.
4531*0Sstevel@tonic-gate 	 * The portion below should be actually be done by fd_lointr().
4532*0Sstevel@tonic-gate 	 * We should be triggering the fd_lointr here and exiting.
4533*0Sstevel@tonic-gate 	 * However for want of time this will be done in the next FIX.
4534*0Sstevel@tonic-gate 	 *
4535*0Sstevel@tonic-gate 	 * Hence for now we will release hilock only and keep the remaining
4536*0Sstevel@tonic-gate 	 * code as it is.
4537*0Sstevel@tonic-gate 	 * Releasing of hilock ensures that we don't hold on to the
4538*0Sstevel@tonic-gate 	 * lolock and hilock at the same time.
4539*0Sstevel@tonic-gate 	 * hilock is acquired each time dma related  registers are accessed.
4540*0Sstevel@tonic-gate 	 */
4541*0Sstevel@tonic-gate 	mutex_exit(&fdc->c_hilock);
4542*0Sstevel@tonic-gate 	/* Make signal and get out of interrupt handler */
4543*0Sstevel@tonic-gate 	if (done) {
4544*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_lolock);
4545*0Sstevel@tonic-gate 
4546*0Sstevel@tonic-gate 		fdc->c_csb.csb_opmode = 0;
4547*0Sstevel@tonic-gate 
4548*0Sstevel@tonic-gate 		/*  reset watchdog timer if armed and not already triggered */
4549*0Sstevel@tonic-gate 
4550*0Sstevel@tonic-gate 
4551*0Sstevel@tonic-gate 		if (fdc->c_timeid) {
4552*0Sstevel@tonic-gate 			timeout_id_t timeid = fdc->c_timeid;
4553*0Sstevel@tonic-gate 			fdc->c_timeid = 0;
4554*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_lolock);
4555*0Sstevel@tonic-gate 			(void) untimeout(timeid);
4556*0Sstevel@tonic-gate 			mutex_enter(&fdc->c_lolock);
4557*0Sstevel@tonic-gate 		}
4558*0Sstevel@tonic-gate 
4559*0Sstevel@tonic-gate 
4560*0Sstevel@tonic-gate 		if (fdc->c_flags & FDCFLG_WAITING) {
4561*0Sstevel@tonic-gate 			/*
4562*0Sstevel@tonic-gate 			 * somebody's waiting on finish of fdctlr/csb,
4563*0Sstevel@tonic-gate 			 * wake them
4564*0Sstevel@tonic-gate 			 */
4565*0Sstevel@tonic-gate 
4566*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_INTR,
4567*0Sstevel@tonic-gate 				(C, "fdintr_dma: signal the waiter\n"));
4568*0Sstevel@tonic-gate 
4569*0Sstevel@tonic-gate 			fdc->c_flags ^= FDCFLG_WAITING;
4570*0Sstevel@tonic-gate 			cv_signal(&fdc->c_iocv);
4571*0Sstevel@tonic-gate 
4572*0Sstevel@tonic-gate 			/*
4573*0Sstevel@tonic-gate 			 * FDCFLG_BUSY is NOT cleared, NOR is the csb given
4574*0Sstevel@tonic-gate 			 * back; the operation just finished can look at the csb
4575*0Sstevel@tonic-gate 			 */
4576*0Sstevel@tonic-gate 		} else {
4577*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_INTR,
4578*0Sstevel@tonic-gate 				(C, "fdintr_dma: nobody sleeping (%x %x %x)\n",
4579*0Sstevel@tonic-gate 			fdc->c_csb.csb_rslt[0], fdc->c_csb.csb_rslt[1],
4580*0Sstevel@tonic-gate 			fdc->c_csb.csb_rslt[2]));
4581*0Sstevel@tonic-gate 		}
4582*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
4583*0Sstevel@tonic-gate 	}
4584*0Sstevel@tonic-gate 	/* update high level interrupt counter */
4585*0Sstevel@tonic-gate 	if (fdc->c_intrstat)
4586*0Sstevel@tonic-gate 			KIOIP->intrs[KSTAT_INTR_HARD]++;
4587*0Sstevel@tonic-gate 
4588*0Sstevel@tonic-gate 
4589*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_INTR, (C, "fdintr_dma: done\n"));
4590*0Sstevel@tonic-gate 	return (res);
4591*0Sstevel@tonic-gate }
4592*0Sstevel@tonic-gate 
4593*0Sstevel@tonic-gate /*
4594*0Sstevel@tonic-gate  * fd_lointr
4595*0Sstevel@tonic-gate  *	This is the low level SW interrupt handler triggered by the high
4596*0Sstevel@tonic-gate  *	level interrupt handler (or by fdwatch).
4597*0Sstevel@tonic-gate  */
4598*0Sstevel@tonic-gate static uint_t
4599*0Sstevel@tonic-gate fd_lointr(caddr_t arg)
4600*0Sstevel@tonic-gate {
4601*0Sstevel@tonic-gate 	struct fdctlr *fdc = (struct fdctlr *)arg;
4602*0Sstevel@tonic-gate 	struct fdcsb *csb;
4603*0Sstevel@tonic-gate 
4604*0Sstevel@tonic-gate 	csb = &fdc->c_csb;
4605*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_INTR, (C, "fdintr: opmode %d\n",
4606*0Sstevel@tonic-gate 	    csb->csb_opmode));
4607*0Sstevel@tonic-gate 	/*
4608*0Sstevel@tonic-gate 	 * Check that lowlevel interrupt really meant to trigger us.
4609*0Sstevel@tonic-gate 	 */
4610*0Sstevel@tonic-gate 	if (csb->csb_opmode != 4) {
4611*0Sstevel@tonic-gate 		/*
4612*0Sstevel@tonic-gate 		 * This should probably be protected, but, what the
4613*0Sstevel@tonic-gate 		 * heck...the cost isn't worth the accuracy for this
4614*0Sstevel@tonic-gate 		 * statistic.
4615*0Sstevel@tonic-gate 		 */
4616*0Sstevel@tonic-gate 		if (fdc->c_intrstat)
4617*0Sstevel@tonic-gate 			KIOIP->intrs[KSTAT_INTR_SPURIOUS]++;
4618*0Sstevel@tonic-gate 		return (DDI_INTR_UNCLAIMED);
4619*0Sstevel@tonic-gate 	}
4620*0Sstevel@tonic-gate 
4621*0Sstevel@tonic-gate 	mutex_enter(&fdc->c_lolock);
4622*0Sstevel@tonic-gate 	csb->csb_opmode = 0;
4623*0Sstevel@tonic-gate 
4624*0Sstevel@tonic-gate 	/*  reset watchdog timer if armed and not already triggered */
4625*0Sstevel@tonic-gate 	if (fdc->c_timeid) {
4626*0Sstevel@tonic-gate 		timeout_id_t timeid = fdc->c_timeid;
4627*0Sstevel@tonic-gate 		fdc->c_timeid = 0;
4628*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
4629*0Sstevel@tonic-gate 		(void) untimeout(timeid);
4630*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_lolock);
4631*0Sstevel@tonic-gate 
4632*0Sstevel@tonic-gate 	}
4633*0Sstevel@tonic-gate 
4634*0Sstevel@tonic-gate 	if (fdc->c_flags & FDCFLG_WAITING) {
4635*0Sstevel@tonic-gate 		/*
4636*0Sstevel@tonic-gate 		 * somebody's waiting on finish of fdctlr/csb, wake them
4637*0Sstevel@tonic-gate 		 */
4638*0Sstevel@tonic-gate 		fdc->c_flags ^= FDCFLG_WAITING;
4639*0Sstevel@tonic-gate 		cv_signal(&fdc->c_iocv);
4640*0Sstevel@tonic-gate 
4641*0Sstevel@tonic-gate 		/*
4642*0Sstevel@tonic-gate 		 * FDCFLG_BUSY is NOT cleared, NOR is the csb given back; so
4643*0Sstevel@tonic-gate 		 * the operation just finished can look at the csb
4644*0Sstevel@tonic-gate 		 */
4645*0Sstevel@tonic-gate 	} else {
4646*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L3, FDEM_INTR,
4647*0Sstevel@tonic-gate 		    (C, "fdintr: nobody sleeping (%x %x %x)\n",
4648*0Sstevel@tonic-gate 		    csb->csb_rslt[0], csb->csb_rslt[1], csb->csb_rslt[2]));
4649*0Sstevel@tonic-gate 	}
4650*0Sstevel@tonic-gate 	if (fdc->c_intrstat)
4651*0Sstevel@tonic-gate 		KIOIP->intrs[KSTAT_INTR_SOFT]++;
4652*0Sstevel@tonic-gate 	mutex_exit(&fdc->c_lolock);
4653*0Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
4654*0Sstevel@tonic-gate }
4655*0Sstevel@tonic-gate 
4656*0Sstevel@tonic-gate /*
4657*0Sstevel@tonic-gate  * fdwatch
4658*0Sstevel@tonic-gate  *	is called from timein() when a floppy operation has expired.
4659*0Sstevel@tonic-gate  */
4660*0Sstevel@tonic-gate static void
4661*0Sstevel@tonic-gate fdwatch(void *arg)
4662*0Sstevel@tonic-gate {
4663*0Sstevel@tonic-gate 	struct fdctlr *fdc = arg;
4664*0Sstevel@tonic-gate 	int old_opmode;
4665*0Sstevel@tonic-gate 	struct fdcsb *csb;
4666*0Sstevel@tonic-gate 
4667*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_WATC, (C, "fdwatch\n"));
4668*0Sstevel@tonic-gate 
4669*0Sstevel@tonic-gate 	mutex_enter(&fdc->c_lolock);
4670*0Sstevel@tonic-gate 	if (fdc->c_timeid == 0) {
4671*0Sstevel@tonic-gate 		/*
4672*0Sstevel@tonic-gate 		 * fdintr got here first, ergo, no timeout condition..
4673*0Sstevel@tonic-gate 		 */
4674*0Sstevel@tonic-gate 
4675*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_WATC,
4676*0Sstevel@tonic-gate 				(C, "fdwatch: no timeout\n"));
4677*0Sstevel@tonic-gate 
4678*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
4679*0Sstevel@tonic-gate 		return;
4680*0Sstevel@tonic-gate 	}
4681*0Sstevel@tonic-gate 	fdc->c_timeid = 0;
4682*0Sstevel@tonic-gate 	csb = &fdc->c_csb;
4683*0Sstevel@tonic-gate 
4684*0Sstevel@tonic-gate 	mutex_enter(&fdc->c_hilock);
4685*0Sstevel@tonic-gate 	/*
4686*0Sstevel@tonic-gate 	 * XXXX: We should probably reset the bloody chip
4687*0Sstevel@tonic-gate 	 */
4688*0Sstevel@tonic-gate 	old_opmode = csb->csb_opmode;
4689*0Sstevel@tonic-gate 
4690*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_WATC,
4691*0Sstevel@tonic-gate 	    (C, "fd%d: timeout, opmode:%d\n", csb->csb_unit, old_opmode));
4692*0Sstevel@tonic-gate 
4693*0Sstevel@tonic-gate 	csb->csb_opmode = 4;
4694*0Sstevel@tonic-gate 	mutex_exit(&fdc->c_hilock);
4695*0Sstevel@tonic-gate 
4696*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_WATC, (C, "fdwatch: cmd %s timed out\n",
4697*0Sstevel@tonic-gate 				fdcmds[csb->csb_cmds[0] & 0x1f].cmdname));
4698*0Sstevel@tonic-gate 	fdc->c_flags |= FDCFLG_TIMEDOUT;
4699*0Sstevel@tonic-gate 	csb->csb_status = CSB_CMDTO;
4700*0Sstevel@tonic-gate 
4701*0Sstevel@tonic-gate 	if ((fdc->c_fdtype & FDCTYPE_DMA) == 0) {
4702*0Sstevel@tonic-gate 		ddi_trigger_softintr(fdc->c_softid);
4703*0Sstevel@tonic-gate 		KIOIP->intrs[KSTAT_INTR_WATCHDOG]++;
4704*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
4705*0Sstevel@tonic-gate 	} else {
4706*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
4707*0Sstevel@tonic-gate 		(void) fd_lointr((caddr_t)fdctlrs);
4708*0Sstevel@tonic-gate 	}
4709*0Sstevel@tonic-gate }
4710*0Sstevel@tonic-gate 
4711*0Sstevel@tonic-gate /*
4712*0Sstevel@tonic-gate  * fdgetcsb
4713*0Sstevel@tonic-gate  *	wait until the csb is free
4714*0Sstevel@tonic-gate  */
4715*0Sstevel@tonic-gate static void
4716*0Sstevel@tonic-gate fdgetcsb(struct fdctlr *fdc)
4717*0Sstevel@tonic-gate {
4718*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_GETC, (C, "fdgetcsb\n"));
4719*0Sstevel@tonic-gate 	ASSERT(mutex_owned(&fdc->c_lolock));
4720*0Sstevel@tonic-gate 	while (fdc->c_flags & FDCFLG_BUSY) {
4721*0Sstevel@tonic-gate 		fdc->c_flags |= FDCFLG_WANT;
4722*0Sstevel@tonic-gate 		cv_wait(&fdc->c_csbcv, &fdc->c_lolock);
4723*0Sstevel@tonic-gate 	}
4724*0Sstevel@tonic-gate 	fdc->c_flags |= FDCFLG_BUSY; /* got it! */
4725*0Sstevel@tonic-gate }
4726*0Sstevel@tonic-gate 
4727*0Sstevel@tonic-gate /*
4728*0Sstevel@tonic-gate  * fdretcsb
4729*0Sstevel@tonic-gate  *	return csb
4730*0Sstevel@tonic-gate  */
4731*0Sstevel@tonic-gate static void
4732*0Sstevel@tonic-gate fdretcsb(struct fdctlr *fdc)
4733*0Sstevel@tonic-gate {
4734*0Sstevel@tonic-gate 
4735*0Sstevel@tonic-gate 	ASSERT(mutex_owned(&fdc->c_lolock));
4736*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RETC, (C, "fdretcsb\n"));
4737*0Sstevel@tonic-gate 	fdc->c_flags &= ~FDCFLG_BUSY; /* let go */
4738*0Sstevel@tonic-gate 
4739*0Sstevel@tonic-gate 	fdc->c_csb.csb_read = 0;
4740*0Sstevel@tonic-gate 
4741*0Sstevel@tonic-gate 	if (fdc->c_flags & FDCFLG_WANT) {
4742*0Sstevel@tonic-gate 		fdc->c_flags ^= FDCFLG_WANT;
4743*0Sstevel@tonic-gate 		/*
4744*0Sstevel@tonic-gate 		 * broadcast the signal.  One thread will wake up and
4745*0Sstevel@tonic-gate 		 * set the flags to FDCFLG_BUSY.  If more than one thread is
4746*0Sstevel@tonic-gate 		 * waiting then each thread will wake up in turn.  The first
4747*0Sstevel@tonic-gate 		 * thread to wake-up will set the FDCFLG_BUSY flag and the
4748*0Sstevel@tonic-gate 		 * subsequent threads will will wake-up, but reset the
4749*0Sstevel@tonic-gate 		 * flag to FDCFLG_WANT because the FDCFLG_BUSY bit is set.
4750*0Sstevel@tonic-gate 		 */
4751*0Sstevel@tonic-gate 		cv_broadcast(&fdc->c_csbcv);
4752*0Sstevel@tonic-gate 	}
4753*0Sstevel@tonic-gate }
4754*0Sstevel@tonic-gate 
4755*0Sstevel@tonic-gate 
4756*0Sstevel@tonic-gate /*
4757*0Sstevel@tonic-gate  * fdreset
4758*0Sstevel@tonic-gate  *	reset THE controller, and configure it to be
4759*0Sstevel@tonic-gate  *	the way it ought to be
4760*0Sstevel@tonic-gate  * ASSUMES: that it already owns the csb/fdctlr!
4761*0Sstevel@tonic-gate  *
4762*0Sstevel@tonic-gate  *	- called with the low level lock held
4763*0Sstevel@tonic-gate  */
4764*0Sstevel@tonic-gate static int
4765*0Sstevel@tonic-gate fdreset(struct fdctlr *fdc)
4766*0Sstevel@tonic-gate {
4767*0Sstevel@tonic-gate 	struct fdcsb *csb;
4768*0Sstevel@tonic-gate 	clock_t local_lbolt = 0;
4769*0Sstevel@tonic-gate 	timeout_id_t timeid;
4770*0Sstevel@tonic-gate 
4771*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RESE, (C, "fdreset\n"));
4772*0Sstevel@tonic-gate 
4773*0Sstevel@tonic-gate 	ASSERT(mutex_owned(&fdc->c_lolock));
4774*0Sstevel@tonic-gate 
4775*0Sstevel@tonic-gate 	/* count resets */
4776*0Sstevel@tonic-gate 	fdc->fdstats.reset++;
4777*0Sstevel@tonic-gate 
4778*0Sstevel@tonic-gate 	/*
4779*0Sstevel@tonic-gate 	 * On the 82077, the DSR will clear itself after a reset.  Upon exiting
4780*0Sstevel@tonic-gate 	 * the reset, a polling interrupt will be generated.  If the floppy
4781*0Sstevel@tonic-gate 	 * interrupt is enabled, it's possible for cv_signal() to be called
4782*0Sstevel@tonic-gate 	 * before cv_wait().  This will cause the system to hang.  Turn off
4783*0Sstevel@tonic-gate 	 * the floppy interrupt to avoid this race condition
4784*0Sstevel@tonic-gate 	 */
4785*0Sstevel@tonic-gate 	if ((fdc->c_fdtype & FDCTYPE_CTRLMASK) == FDCTYPE_82077) {
4786*0Sstevel@tonic-gate 		/*
4787*0Sstevel@tonic-gate 		 * We need to perform any timeouts before we Reset the
4788*0Sstevel@tonic-gate 		 * controller. We cannot afford to drop the c_lolock mutex after
4789*0Sstevel@tonic-gate 		 * Resetting the controller. The reason is that we get a spate
4790*0Sstevel@tonic-gate 		 * of interrupts until we take the controller out of reset.
4791*0Sstevel@tonic-gate 		 * The way we avoid this spate of continuous interrupts is by
4792*0Sstevel@tonic-gate 		 * holding on to the c_lolock and forcing the fdintr_dma routine
4793*0Sstevel@tonic-gate 		 * to go to sleep waiting for this mutex.
4794*0Sstevel@tonic-gate 		 */
4795*0Sstevel@tonic-gate 		/* Do not hold the mutex across the untimeout call */
4796*0Sstevel@tonic-gate 		timeid = fdc->c_mtimeid;
4797*0Sstevel@tonic-gate 		fdc->c_mtimeid = 0;
4798*0Sstevel@tonic-gate 		if (timeid) {
4799*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_lolock);
4800*0Sstevel@tonic-gate 			(void) untimeout(timeid);
4801*0Sstevel@tonic-gate 			mutex_enter(&fdc->c_lolock);
4802*0Sstevel@tonic-gate 		}
4803*0Sstevel@tonic-gate 		/* LINTED */
4804*0Sstevel@tonic-gate 		Set_dor(fdc, DMAGATE, 0);
4805*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_RESE, (C, "fdreset: set dor\n"));
4806*0Sstevel@tonic-gate 	}
4807*0Sstevel@tonic-gate 
4808*0Sstevel@tonic-gate 	/* toggle software reset */
4809*0Sstevel@tonic-gate 	Dsr(fdc, SWR);
4810*0Sstevel@tonic-gate 
4811*0Sstevel@tonic-gate 	drv_usecwait(5);
4812*0Sstevel@tonic-gate 
4813*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RESE,
4814*0Sstevel@tonic-gate 			(C, "fdreset: toggled software reset\n"));
4815*0Sstevel@tonic-gate 
4816*0Sstevel@tonic-gate 	/*
4817*0Sstevel@tonic-gate 	 * This sets the data rate to 500Kbps (for high density)
4818*0Sstevel@tonic-gate 	 * XXX should use current characteristics instead XXX
4819*0Sstevel@tonic-gate 	 */
4820*0Sstevel@tonic-gate 	Dsr(fdc, 0);
4821*0Sstevel@tonic-gate 	drv_usecwait(5);
4822*0Sstevel@tonic-gate 	switch (fdc->c_fdtype & FDCTYPE_CTRLMASK) {
4823*0Sstevel@tonic-gate 	case FDCTYPE_82077:
4824*0Sstevel@tonic-gate 		/*
4825*0Sstevel@tonic-gate 		 * when we bring the controller out of reset it will generate
4826*0Sstevel@tonic-gate 		 * a polling interrupt. fdintr() will field it and schedule
4827*0Sstevel@tonic-gate 		 * fd_lointr(). There will be no one sleeping but we are
4828*0Sstevel@tonic-gate 		 * expecting an interrupt so....
4829*0Sstevel@tonic-gate 		 */
4830*0Sstevel@tonic-gate 		fdc->c_flags |= FDCFLG_WAITING;
4831*0Sstevel@tonic-gate 
4832*0Sstevel@tonic-gate 		/*
4833*0Sstevel@tonic-gate 		 * The reset bit must be cleared to take the 077 out of
4834*0Sstevel@tonic-gate 		 * reset state and the DMAGATE bit must be high to enable
4835*0Sstevel@tonic-gate 		 * interrupts.
4836*0Sstevel@tonic-gate 		 */
4837*0Sstevel@tonic-gate 		/* LINTED */
4838*0Sstevel@tonic-gate 		Set_dor(fdc, DMAGATE|RESET, 1);
4839*0Sstevel@tonic-gate 
4840*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA,
4841*0Sstevel@tonic-gate 			(C, "fdattach: Dor 0x%x\n", Dor(fdc)));
4842*0Sstevel@tonic-gate 
4843*0Sstevel@tonic-gate 		local_lbolt = ddi_get_lbolt();
4844*0Sstevel@tonic-gate 		if (cv_timedwait(&fdc->c_iocv, &fdc->c_lolock,
4845*0Sstevel@tonic-gate 			local_lbolt + drv_usectohz(1000000)) == -1) {
4846*0Sstevel@tonic-gate 			return (-1);
4847*0Sstevel@tonic-gate 		}
4848*0Sstevel@tonic-gate 		break;
4849*0Sstevel@tonic-gate 
4850*0Sstevel@tonic-gate 	default:
4851*0Sstevel@tonic-gate 		fdc->c_flags |= FDCFLG_WAITING;
4852*0Sstevel@tonic-gate 
4853*0Sstevel@tonic-gate 		/*
4854*0Sstevel@tonic-gate 		 * A timed wait is not used because it's possible for the timer
4855*0Sstevel@tonic-gate 		 * to go off before the controller has a chance to interrupt.
4856*0Sstevel@tonic-gate 		 */
4857*0Sstevel@tonic-gate 		cv_wait(&fdc->c_iocv, &fdc->c_lolock);
4858*0Sstevel@tonic-gate 		break;
4859*0Sstevel@tonic-gate 	}
4860*0Sstevel@tonic-gate 	csb = &fdc->c_csb;
4861*0Sstevel@tonic-gate 
4862*0Sstevel@tonic-gate 	/* setup common things in csb */
4863*0Sstevel@tonic-gate 	csb->csb_unit = fdc->c_un->un_unit_no;
4864*0Sstevel@tonic-gate 	csb->csb_nrslts = 0;
4865*0Sstevel@tonic-gate 	csb->csb_opflags = CSB_OFNORESULTS;
4866*0Sstevel@tonic-gate 	csb->csb_maxretry = 0;
4867*0Sstevel@tonic-gate 	csb->csb_retrys = 0;
4868*0Sstevel@tonic-gate 
4869*0Sstevel@tonic-gate 	csb->csb_read = CSB_NULL;
4870*0Sstevel@tonic-gate 
4871*0Sstevel@tonic-gate 	/* send SPECIFY command to fdc */
4872*0Sstevel@tonic-gate 	/* csb->unit is don't care */
4873*0Sstevel@tonic-gate 	csb->csb_cmds[0] = FDRAW_SPECIFY;
4874*0Sstevel@tonic-gate 	csb->csb_cmds[1] = fdspec[0]; /* step rate, head unload time */
4875*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_DMA)
4876*0Sstevel@tonic-gate 		csb->csb_cmds[2] =  SPEC_DMA_MODE;
4877*0Sstevel@tonic-gate 	else
4878*0Sstevel@tonic-gate 		csb->csb_cmds[2] = fdspec[1];  /* head load time, DMA mode */
4879*0Sstevel@tonic-gate 
4880*0Sstevel@tonic-gate 	csb->csb_ncmds = 3;
4881*0Sstevel@tonic-gate 
4882*0Sstevel@tonic-gate 	/* XXX for now ignore errors, they "CAN'T HAPPEN" */
4883*0Sstevel@tonic-gate 	(void) fdexec(fdc, 0);	/* no FDXC_CHECKCHG, ... */
4884*0Sstevel@tonic-gate 	/* no results */
4885*0Sstevel@tonic-gate 
4886*0Sstevel@tonic-gate 	/* send CONFIGURE command to fdc */
4887*0Sstevel@tonic-gate 	/* csb->unit is don't care */
4888*0Sstevel@tonic-gate 	csb->csb_cmds[0] = CONFIGURE;
4889*0Sstevel@tonic-gate 	csb->csb_cmds[1] = fdconf[0]; /* motor info, motor delays */
4890*0Sstevel@tonic-gate 	csb->csb_cmds[2] = fdconf[1]; /* enaimplsk, disapoll, fifothru */
4891*0Sstevel@tonic-gate 	csb->csb_cmds[3] = fdconf[2]; /* track precomp */
4892*0Sstevel@tonic-gate 	csb->csb_ncmds = 4;
4893*0Sstevel@tonic-gate 
4894*0Sstevel@tonic-gate 	csb->csb_read = CSB_NULL;
4895*0Sstevel@tonic-gate 
4896*0Sstevel@tonic-gate 	csb->csb_retrys = 0;
4897*0Sstevel@tonic-gate 
4898*0Sstevel@tonic-gate 	/* XXX for now ignore errors, they "CAN'T HAPPEN" */
4899*0Sstevel@tonic-gate 	(void) fdexec(fdc, 0); /* no FDXC_CHECKCHG, ... */
4900*0Sstevel@tonic-gate 	return (0);
4901*0Sstevel@tonic-gate }
4902*0Sstevel@tonic-gate 
4903*0Sstevel@tonic-gate /*
4904*0Sstevel@tonic-gate  * fdrecalseek
4905*0Sstevel@tonic-gate  *	performs recalibrates or seeks if the "arg" is -1 does a
4906*0Sstevel@tonic-gate  *	recalibrate on a drive, else it seeks to the cylinder of
4907*0Sstevel@tonic-gate  *	the drive.  The recalibrate is also used to find a drive,
4908*0Sstevel@tonic-gate  *	ie if the drive is not there, the controller says "error"
4909*0Sstevel@tonic-gate  *	on the operation
4910*0Sstevel@tonic-gate  * NOTE: that there is special handling of this operation in the hardware
4911*0Sstevel@tonic-gate  * interrupt routine - it causes the operation to appear to have results;
4912*0Sstevel@tonic-gate  * ie the results of the SENSE INTERRUPT STATUS that the hardware interrupt
4913*0Sstevel@tonic-gate  * function did for us.
4914*0Sstevel@tonic-gate  * NOTE: because it uses sleep/wakeup it must be protected in a critical
4915*0Sstevel@tonic-gate  * section so create one before calling it!
4916*0Sstevel@tonic-gate  *
4917*0Sstevel@tonic-gate  * RETURNS: 0 for ok,
4918*0Sstevel@tonic-gate  *	else	errno from fdexec,
4919*0Sstevel@tonic-gate  *	or	ENODEV if error (infers hardware type error)
4920*0Sstevel@tonic-gate  *
4921*0Sstevel@tonic-gate  *	- called with the low level lock held
4922*0Sstevel@tonic-gate  */
4923*0Sstevel@tonic-gate static int
4924*0Sstevel@tonic-gate fdrecalseek(struct fdctlr *fdc, int unit, int arg, int execflg)
4925*0Sstevel@tonic-gate {
4926*0Sstevel@tonic-gate 	struct fdcsb *csb;
4927*0Sstevel@tonic-gate 	int result;
4928*0Sstevel@tonic-gate 
4929*0Sstevel@tonic-gate 	ASSERT(fdc->c_un->un_unit_no == unit);
4930*0Sstevel@tonic-gate 
4931*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RECA, (C, "fdrecalseek to %d\n", arg));
4932*0Sstevel@tonic-gate 
4933*0Sstevel@tonic-gate 	/* XXX TODO: check see argument for <= num cyls OR < 256 */
4934*0Sstevel@tonic-gate 
4935*0Sstevel@tonic-gate 	csb = &fdc->c_csb;
4936*0Sstevel@tonic-gate 	csb->csb_unit = (uchar_t)unit;
4937*0Sstevel@tonic-gate 	csb->csb_cmds[1] = unit & 0x03;
4938*0Sstevel@tonic-gate 
4939*0Sstevel@tonic-gate 	if (arg == -1) {			/* is recal... */
4940*0Sstevel@tonic-gate 		csb->csb_cmds[0] = FDRAW_REZERO;
4941*0Sstevel@tonic-gate 		csb->csb_ncmds = 2;
4942*0Sstevel@tonic-gate 	} else {
4943*0Sstevel@tonic-gate 		csb->csb_cmds[0] = FDRAW_SEEK;
4944*0Sstevel@tonic-gate 		csb->csb_cmds[2] = (uchar_t)arg;
4945*0Sstevel@tonic-gate 		csb->csb_ncmds = 3;
4946*0Sstevel@tonic-gate 	}
4947*0Sstevel@tonic-gate 	csb->csb_nrslts = 2;	/* 2 for SENSE INTERRUPTS */
4948*0Sstevel@tonic-gate 	csb->csb_opflags = CSB_OFSEEKOPS | CSB_OFTIMEIT;
4949*0Sstevel@tonic-gate 	/*
4950*0Sstevel@tonic-gate 	 * MAYBE NYD need to set retries to different values? - depending on
4951*0Sstevel@tonic-gate 	 * drive characteristics - if we get to high capacity drives
4952*0Sstevel@tonic-gate 	 */
4953*0Sstevel@tonic-gate 	csb->csb_maxretry = skretry;
4954*0Sstevel@tonic-gate 	csb->csb_retrys = 0;
4955*0Sstevel@tonic-gate 
4956*0Sstevel@tonic-gate 	/* send cmd off to fdexec */
4957*0Sstevel@tonic-gate 	if (result = fdexec(fdc, FDXC_SLEEP | execflg)) {
4958*0Sstevel@tonic-gate 		goto out;
4959*0Sstevel@tonic-gate 	}
4960*0Sstevel@tonic-gate 
4961*0Sstevel@tonic-gate 	/*
4962*0Sstevel@tonic-gate 	 * if recal, test for equipment check error
4963*0Sstevel@tonic-gate 	 * ASSUMES result = 0 from above call
4964*0Sstevel@tonic-gate 	 */
4965*0Sstevel@tonic-gate 	if (arg == -1) {
4966*0Sstevel@tonic-gate 		result = 0;
4967*0Sstevel@tonic-gate 	} else {
4968*0Sstevel@tonic-gate 		/* for seeks, any old error will do */
4969*0Sstevel@tonic-gate 		if ((csb->csb_rslt[0] & IC_SR0) || csb->csb_cmdstat)
4970*0Sstevel@tonic-gate 			result = ENODEV;
4971*0Sstevel@tonic-gate 	}
4972*0Sstevel@tonic-gate 
4973*0Sstevel@tonic-gate out:
4974*0Sstevel@tonic-gate 	return (result);
4975*0Sstevel@tonic-gate }
4976*0Sstevel@tonic-gate 
4977*0Sstevel@tonic-gate /*
4978*0Sstevel@tonic-gate  * fdsensedrv
4979*0Sstevel@tonic-gate  *	do a sense_drive command.  used by fdopen and fdcheckdisk.
4980*0Sstevel@tonic-gate  *
4981*0Sstevel@tonic-gate  *	- called with the lock held
4982*0Sstevel@tonic-gate  */
4983*0Sstevel@tonic-gate static int
4984*0Sstevel@tonic-gate fdsensedrv(struct fdctlr *fdc, int unit)
4985*0Sstevel@tonic-gate {
4986*0Sstevel@tonic-gate 	struct fdcsb *csb;
4987*0Sstevel@tonic-gate 
4988*0Sstevel@tonic-gate 	ASSERT(fdc->c_un->un_unit_no == unit);
4989*0Sstevel@tonic-gate 
4990*0Sstevel@tonic-gate 	csb = &fdc->c_csb;
4991*0Sstevel@tonic-gate 
4992*0Sstevel@tonic-gate 	/* setup common things in csb */
4993*0Sstevel@tonic-gate 	csb->csb_unit = (uchar_t)unit;
4994*0Sstevel@tonic-gate 	csb->csb_opflags = CSB_OFIMMEDIATE;
4995*0Sstevel@tonic-gate 	csb->csb_cmds[0] = FDRAW_SENSE_DRV;
4996*0Sstevel@tonic-gate 	/* MOT bit set means don't delay */
4997*0Sstevel@tonic-gate 	csb->csb_cmds[1] = MOT | (unit & 0x03);
4998*0Sstevel@tonic-gate 	csb->csb_ncmds = 2;
4999*0Sstevel@tonic-gate 	csb->csb_nrslts = 1;
5000*0Sstevel@tonic-gate 	csb->csb_maxretry = skretry;
5001*0Sstevel@tonic-gate 	csb->csb_retrys = 0;
5002*0Sstevel@tonic-gate 
5003*0Sstevel@tonic-gate 	/* XXX for now ignore errors, they "CAN'T HAPPEN" */
5004*0Sstevel@tonic-gate 	(void) fdexec(fdc, 0);	/* DON't check changed!, no sleep */
5005*0Sstevel@tonic-gate 
5006*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_CHEK,
5007*0Sstevel@tonic-gate 		(C, "fdsensedrv: result 0x%x", csb->csb_rslt[0]));
5008*0Sstevel@tonic-gate 
5009*0Sstevel@tonic-gate 	return (csb->csb_rslt[0]); /* return status byte 3 */
5010*0Sstevel@tonic-gate }
5011*0Sstevel@tonic-gate 
5012*0Sstevel@tonic-gate /*
5013*0Sstevel@tonic-gate  * fdcheckdisk
5014*0Sstevel@tonic-gate  *	check to see if the disk is still there - do a recalibrate,
5015*0Sstevel@tonic-gate  *	then see if DSKCHG line went away, if so, diskette is in; else
5016*0Sstevel@tonic-gate  *	it's (still) out.
5017*0Sstevel@tonic-gate  */
5018*0Sstevel@tonic-gate 
5019*0Sstevel@tonic-gate static int
5020*0Sstevel@tonic-gate fdcheckdisk(struct fdctlr *fdc, int unit)
5021*0Sstevel@tonic-gate {
5022*0Sstevel@tonic-gate 	auto struct fdcsb savecsb;
5023*0Sstevel@tonic-gate 	struct fdcsb *csb;
5024*0Sstevel@tonic-gate 	int	err, st3;
5025*0Sstevel@tonic-gate 	int	seekto;			/* where to seek for reset of DSKCHG */
5026*0Sstevel@tonic-gate 
5027*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_CHEK,
5028*0Sstevel@tonic-gate 	    (C, "fdcheckdisk, unit %d\n", unit));
5029*0Sstevel@tonic-gate 
5030*0Sstevel@tonic-gate 	ASSERT(fdc->c_un->un_unit_no == unit);
5031*0Sstevel@tonic-gate 
5032*0Sstevel@tonic-gate 	/*
5033*0Sstevel@tonic-gate 	 * save old csb
5034*0Sstevel@tonic-gate 	 */
5035*0Sstevel@tonic-gate 
5036*0Sstevel@tonic-gate 	csb = &fdc->c_csb;
5037*0Sstevel@tonic-gate 	savecsb = fdc->c_csb;
5038*0Sstevel@tonic-gate 	bzero((caddr_t)csb, sizeof (*csb));
5039*0Sstevel@tonic-gate 
5040*0Sstevel@tonic-gate 	/*
5041*0Sstevel@tonic-gate 	 * Read drive status to see if at TRK0, if so, seek to cyl 1,
5042*0Sstevel@tonic-gate 	 * else seek to cyl 0.	We do this because the controller is
5043*0Sstevel@tonic-gate 	 * "smart" enough to not send any step pulses (which are how
5044*0Sstevel@tonic-gate 	 * the DSKCHG line gets reset) if it sees TRK0 'cause it
5045*0Sstevel@tonic-gate 	 * knows the drive is already recalibrated.
5046*0Sstevel@tonic-gate 	 */
5047*0Sstevel@tonic-gate 	st3 = fdsensedrv(fdc, unit);
5048*0Sstevel@tonic-gate 
5049*0Sstevel@tonic-gate 	/* check TRK0 bit in status */
5050*0Sstevel@tonic-gate 	if (st3 & T0_SR3)
5051*0Sstevel@tonic-gate 		seekto = 1;	/* at TRK0, seek out */
5052*0Sstevel@tonic-gate 	else
5053*0Sstevel@tonic-gate 		seekto = 0;
5054*0Sstevel@tonic-gate 
5055*0Sstevel@tonic-gate 	/*
5056*0Sstevel@tonic-gate 	 * DON'T recurse check changed
5057*0Sstevel@tonic-gate 	 */
5058*0Sstevel@tonic-gate 	err = fdrecalseek(fdc, unit, seekto, 0);
5059*0Sstevel@tonic-gate 
5060*0Sstevel@tonic-gate 	/* "restore" old csb, check change state */
5061*0Sstevel@tonic-gate 	fdc->c_csb = savecsb;
5062*0Sstevel@tonic-gate 
5063*0Sstevel@tonic-gate 	/* any recal/seek errors are too serious to attend to */
5064*0Sstevel@tonic-gate 	if (err) {
5065*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L2, FDEM_CHEK,
5066*0Sstevel@tonic-gate 		    (C, "fdcheckdisk err %d\n", err));
5067*0Sstevel@tonic-gate 		return (err);
5068*0Sstevel@tonic-gate 	}
5069*0Sstevel@tonic-gate 
5070*0Sstevel@tonic-gate 	/*
5071*0Sstevel@tonic-gate 	 * if disk change still asserted, no diskette in drive!
5072*0Sstevel@tonic-gate 	 */
5073*0Sstevel@tonic-gate 	if (fdsense_chng(fdc, csb->csb_unit)) {
5074*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L2, FDEM_CHEK,
5075*0Sstevel@tonic-gate 		    (C, "fdcheckdisk no disk\n"));
5076*0Sstevel@tonic-gate 		return (1);
5077*0Sstevel@tonic-gate 	}
5078*0Sstevel@tonic-gate 	return (0);
5079*0Sstevel@tonic-gate }
5080*0Sstevel@tonic-gate 
5081*0Sstevel@tonic-gate /*
5082*0Sstevel@tonic-gate  *	fdselect() - select drive, needed for external to chip select logic
5083*0Sstevel@tonic-gate  *	fdeject() - ejects drive, must be previously selected
5084*0Sstevel@tonic-gate  *	fdsense_chng() - sense disk changed line from previously selected drive
5085*0Sstevel@tonic-gate  *		return s 1 is signal asserted, else 0
5086*0Sstevel@tonic-gate  */
5087*0Sstevel@tonic-gate /* ARGSUSED */
5088*0Sstevel@tonic-gate static void
5089*0Sstevel@tonic-gate fdselect(struct fdctlr *fdc, int unit, int on)
5090*0Sstevel@tonic-gate {
5091*0Sstevel@tonic-gate 
5092*0Sstevel@tonic-gate 	ASSERT(fdc->c_un->un_unit_no == unit);
5093*0Sstevel@tonic-gate 
5094*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_DSEL,
5095*0Sstevel@tonic-gate 	    (C, "fdselect, unit %d, on = %d\n", unit, on));
5096*0Sstevel@tonic-gate 
5097*0Sstevel@tonic-gate 	switch (fdc->c_fdtype & FDCTYPE_AUXIOMASK) {
5098*0Sstevel@tonic-gate 	case FDCTYPE_MACHIO:
5099*0Sstevel@tonic-gate 		set_auxioreg(AUX_DRVSELECT, on);
5100*0Sstevel@tonic-gate 		break;
5101*0Sstevel@tonic-gate 
5102*0Sstevel@tonic-gate 	case FDCTYPE_SLAVIO:
5103*0Sstevel@tonic-gate 	case FDCTYPE_CHEERIO:
5104*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA,
5105*0Sstevel@tonic-gate 			(C, "fdselect: (before) Dor 0x%x\n", Dor(fdc)));
5106*0Sstevel@tonic-gate 
5107*0Sstevel@tonic-gate 		if (unit == 0) {
5108*0Sstevel@tonic-gate 			Set_dor(fdc, DRVSEL, !on);
5109*0Sstevel@tonic-gate 		} else {
5110*0Sstevel@tonic-gate 			Set_dor(fdc, DRVSEL, on);
5111*0Sstevel@tonic-gate 		}
5112*0Sstevel@tonic-gate 
5113*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_ATTA,
5114*0Sstevel@tonic-gate 			(C, "fdselect: Dor 0x%x\n", Dor(fdc)));
5115*0Sstevel@tonic-gate 
5116*0Sstevel@tonic-gate 		break;
5117*0Sstevel@tonic-gate 
5118*0Sstevel@tonic-gate 	default:
5119*0Sstevel@tonic-gate 		break;
5120*0Sstevel@tonic-gate 	}
5121*0Sstevel@tonic-gate }
5122*0Sstevel@tonic-gate 
5123*0Sstevel@tonic-gate /* ARGSUSED */
5124*0Sstevel@tonic-gate static void
5125*0Sstevel@tonic-gate fdeject(struct fdctlr *fdc, int unit)
5126*0Sstevel@tonic-gate {
5127*0Sstevel@tonic-gate 	struct fdunit *un;
5128*0Sstevel@tonic-gate 
5129*0Sstevel@tonic-gate 	ASSERT(fdc->c_un->un_unit_no == unit);
5130*0Sstevel@tonic-gate 
5131*0Sstevel@tonic-gate 	un = fdc->c_un;
5132*0Sstevel@tonic-gate 
5133*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_EJEC, (C, "fdeject\n"));
5134*0Sstevel@tonic-gate 	/*
5135*0Sstevel@tonic-gate 	 * assume delay of function calling sufficient settling time
5136*0Sstevel@tonic-gate 	 * eject line is NOT driven by inverter so it is true low
5137*0Sstevel@tonic-gate 	 */
5138*0Sstevel@tonic-gate 	switch (fdc->c_fdtype & FDCTYPE_AUXIOMASK) {
5139*0Sstevel@tonic-gate 	case FDCTYPE_MACHIO:
5140*0Sstevel@tonic-gate 		set_auxioreg(AUX_EJECT, 0);
5141*0Sstevel@tonic-gate 		drv_usecwait(2);
5142*0Sstevel@tonic-gate 		set_auxioreg(AUX_EJECT, 1);
5143*0Sstevel@tonic-gate 		break;
5144*0Sstevel@tonic-gate 
5145*0Sstevel@tonic-gate 	case FDCTYPE_SLAVIO:
5146*0Sstevel@tonic-gate 		if (!(Dor(fdc) & MOTEN(unit))) {
5147*0Sstevel@tonic-gate 			/* LINTED */
5148*0Sstevel@tonic-gate 			Set_dor(fdc, MOTEN(unit), 1);
5149*0Sstevel@tonic-gate 		}
5150*0Sstevel@tonic-gate 		drv_usecwait(2);	/* just to settle */
5151*0Sstevel@tonic-gate 		/* LINTED */
5152*0Sstevel@tonic-gate 		Set_dor(fdc, EJECT, 1);
5153*0Sstevel@tonic-gate 		drv_usecwait(2);
5154*0Sstevel@tonic-gate 		/* LINTED */
5155*0Sstevel@tonic-gate 		Set_dor(fdc, EJECT, 0);
5156*0Sstevel@tonic-gate 		break;
5157*0Sstevel@tonic-gate 	case FDCTYPE_CHEERIO:
5158*0Sstevel@tonic-gate 		if (!(Dor(fdc) & MOTEN(unit))) {
5159*0Sstevel@tonic-gate 			/* LINTED */
5160*0Sstevel@tonic-gate 			Set_dor(fdc, MOTEN(unit), 1);
5161*0Sstevel@tonic-gate 		}
5162*0Sstevel@tonic-gate 		drv_usecwait(2);	/* just to settle */
5163*0Sstevel@tonic-gate 		/* LINTED */
5164*0Sstevel@tonic-gate 		Set_dor(fdc, EJECT_DMA, 1);
5165*0Sstevel@tonic-gate 		drv_usecwait(2);
5166*0Sstevel@tonic-gate 		/* LINTED */
5167*0Sstevel@tonic-gate 		Set_dor(fdc, EJECT_DMA, 0);
5168*0Sstevel@tonic-gate 		break;
5169*0Sstevel@tonic-gate 	}
5170*0Sstevel@tonic-gate 	/*
5171*0Sstevel@tonic-gate 	 * XXX set ejected state?
5172*0Sstevel@tonic-gate 	 */
5173*0Sstevel@tonic-gate 	un->un_ejected = 1;
5174*0Sstevel@tonic-gate }
5175*0Sstevel@tonic-gate 
5176*0Sstevel@tonic-gate /* ARGSUSED */
5177*0Sstevel@tonic-gate static int
5178*0Sstevel@tonic-gate fdsense_chng(struct fdctlr *fdc, int unit)
5179*0Sstevel@tonic-gate {
5180*0Sstevel@tonic-gate 	int changed = 0;
5181*0Sstevel@tonic-gate 
5182*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_SCHG, (C, "fdsense_chng:start\n"));
5183*0Sstevel@tonic-gate 
5184*0Sstevel@tonic-gate 	ASSERT(fdc->c_un->un_unit_no == unit);
5185*0Sstevel@tonic-gate 
5186*0Sstevel@tonic-gate 	/*
5187*0Sstevel@tonic-gate 	 * Do not turn on the motor of a pollable drive
5188*0Sstevel@tonic-gate 	 */
5189*0Sstevel@tonic-gate 	if (fd_pollable) {
5190*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_SCHG, (C, "pollable: don't turn on motor\n"));
5191*0Sstevel@tonic-gate 		/*
5192*0Sstevel@tonic-gate 		 * Invert the sense of the DSKCHG for pollable drives
5193*0Sstevel@tonic-gate 		 */
5194*0Sstevel@tonic-gate 		if (Dir(fdc) & DSKCHG)
5195*0Sstevel@tonic-gate 			changed = 0;
5196*0Sstevel@tonic-gate 		else
5197*0Sstevel@tonic-gate 			changed = 1;
5198*0Sstevel@tonic-gate 
5199*0Sstevel@tonic-gate 		return (changed);
5200*0Sstevel@tonic-gate 	}
5201*0Sstevel@tonic-gate 
5202*0Sstevel@tonic-gate 	switch (fdc->c_fdtype & FDCTYPE_AUXIOMASK) {
5203*0Sstevel@tonic-gate 	case FDCTYPE_MACHIO:
5204*0Sstevel@tonic-gate 		if (*fdc->c_auxiova & AUX_DISKCHG)
5205*0Sstevel@tonic-gate 			changed = 1;
5206*0Sstevel@tonic-gate 		break;
5207*0Sstevel@tonic-gate 
5208*0Sstevel@tonic-gate 	case FDCTYPE_SB:
5209*0Sstevel@tonic-gate 	case FDCTYPE_SLAVIO:
5210*0Sstevel@tonic-gate 	case FDCTYPE_CHEERIO:
5211*0Sstevel@tonic-gate 		if (!(Dor(fdc) & MOTEN(unit))) {
5212*0Sstevel@tonic-gate 			/* LINTED */
5213*0Sstevel@tonic-gate 			Set_dor(fdc, MOTEN(unit), 1);
5214*0Sstevel@tonic-gate 		}
5215*0Sstevel@tonic-gate 		drv_usecwait(2);	/* just to settle */
5216*0Sstevel@tonic-gate 		if (Dir(fdc) & DSKCHG)
5217*0Sstevel@tonic-gate 			changed = 1;
5218*0Sstevel@tonic-gate 		break;
5219*0Sstevel@tonic-gate 	}
5220*0Sstevel@tonic-gate 
5221*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_SCHG, (C, "fdsense_chng:end\n"));
5222*0Sstevel@tonic-gate 
5223*0Sstevel@tonic-gate 	return (changed);
5224*0Sstevel@tonic-gate }
5225*0Sstevel@tonic-gate 
5226*0Sstevel@tonic-gate /*
5227*0Sstevel@tonic-gate  *	if it can read a valid label it does so, else it will use a
5228*0Sstevel@tonic-gate  *	default.  If it can`t read the diskette - that is an error.
5229*0Sstevel@tonic-gate  *
5230*0Sstevel@tonic-gate  * RETURNS: 0 for ok - meaning that it could at least read the device,
5231*0Sstevel@tonic-gate  *	!0 for error XXX TBD NYD error codes
5232*0Sstevel@tonic-gate  *
5233*0Sstevel@tonic-gate  *	- called with the low level lock held
5234*0Sstevel@tonic-gate  */
5235*0Sstevel@tonic-gate static int
5236*0Sstevel@tonic-gate fdgetlabel(struct fdctlr *fdc, int unit)
5237*0Sstevel@tonic-gate {
5238*0Sstevel@tonic-gate 	struct dk_label *label = NULL;
5239*0Sstevel@tonic-gate 	struct fdunit *un;
5240*0Sstevel@tonic-gate 	short *sp;
5241*0Sstevel@tonic-gate 	short count;
5242*0Sstevel@tonic-gate 	short xsum;			/* checksum */
5243*0Sstevel@tonic-gate 	int	i, tries;
5244*0Sstevel@tonic-gate 	int	err = 0;
5245*0Sstevel@tonic-gate 	short	oldlvl;
5246*0Sstevel@tonic-gate 
5247*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_GETL,
5248*0Sstevel@tonic-gate 	    (C, "fdgetlabel: unit %d\n", unit));
5249*0Sstevel@tonic-gate 
5250*0Sstevel@tonic-gate 	un = fdc->c_un;
5251*0Sstevel@tonic-gate 	un->un_flags &= ~(FDUNIT_UNLABELED);
5252*0Sstevel@tonic-gate 
5253*0Sstevel@tonic-gate 	ASSERT(fdc->c_un->un_unit_no == unit);
5254*0Sstevel@tonic-gate 
5255*0Sstevel@tonic-gate 	/* Do not print errors since this is a private cmd */
5256*0Sstevel@tonic-gate 
5257*0Sstevel@tonic-gate 	oldlvl = fderrlevel;
5258*0Sstevel@tonic-gate 
5259*0Sstevel@tonic-gate 
5260*0Sstevel@tonic-gate 	fderrlevel = FDEP_L4;
5261*0Sstevel@tonic-gate 
5262*0Sstevel@tonic-gate 	label = (struct dk_label *)
5263*0Sstevel@tonic-gate 				kmem_zalloc(sizeof (struct dk_label), KM_SLEEP);
5264*0Sstevel@tonic-gate 
5265*0Sstevel@tonic-gate 	/*
5266*0Sstevel@tonic-gate 	 * try different characteristics (ie densities) by attempting to read
5267*0Sstevel@tonic-gate 	 * from the diskette.  The diskette may not be present or
5268*0Sstevel@tonic-gate 	 * is unformatted.
5269*0Sstevel@tonic-gate 	 *
5270*0Sstevel@tonic-gate 	 * First, the last sector of the first track is read.  If this
5271*0Sstevel@tonic-gate 	 * passes, attempt to read the last sector + 1 of the first track.
5272*0Sstevel@tonic-gate 	 * For example, for a high density diskette, sector 18 is read.  If
5273*0Sstevel@tonic-gate 	 * the diskette is high density, this will pass.  Next, try to
5274*0Sstevel@tonic-gate 	 * read sector 19 of the first track.  This should fail.  If it
5275*0Sstevel@tonic-gate 	 * passes, this is not a high density diskette.  Finally, read
5276*0Sstevel@tonic-gate 	 * the first sector which should contain a label.
5277*0Sstevel@tonic-gate 	 *
5278*0Sstevel@tonic-gate 	 * if un->un_curfdtype is -1 then the current characteristics
5279*0Sstevel@tonic-gate 	 * were set by FDIOSCHAR and need to try it as well as everything
5280*0Sstevel@tonic-gate 	 * in the table
5281*0Sstevel@tonic-gate 	 */
5282*0Sstevel@tonic-gate 	if (un->un_curfdtype == -1) {
5283*0Sstevel@tonic-gate 		tries = nfdtypes+1;
5284*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_GETL,
5285*0Sstevel@tonic-gate 		    (C, "fdgetl: un_curfdtype is -1\n"));
5286*0Sstevel@tonic-gate 
5287*0Sstevel@tonic-gate 	    } else {
5288*0Sstevel@tonic-gate 		tries = nfdtypes;
5289*0Sstevel@tonic-gate 
5290*0Sstevel@tonic-gate 		/* Always start with the highest density (1.7MB) */
5291*0Sstevel@tonic-gate 		un->un_curfdtype = 0;
5292*0Sstevel@tonic-gate 		*(un->un_chars) = fdtypes[un->un_curfdtype];
5293*0Sstevel@tonic-gate 	}
5294*0Sstevel@tonic-gate 
5295*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_GETL,
5296*0Sstevel@tonic-gate 		    (C, "fdgetl: no. of tries %d\n", tries));
5297*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_GETL,
5298*0Sstevel@tonic-gate 		    (C, "fdgetl: no. of curfdtype %d\n", un->un_curfdtype));
5299*0Sstevel@tonic-gate 
5300*0Sstevel@tonic-gate 	for (i = 0; i < tries; i++) {
5301*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_GETL,
5302*0Sstevel@tonic-gate 		    (C, "fdgetl: trying %d\n", i));
5303*0Sstevel@tonic-gate 
5304*0Sstevel@tonic-gate 		if (!(err = fdrw(fdc, unit, FDREAD, 0, 0,
5305*0Sstevel@tonic-gate 			un->un_chars->fdc_secptrack, (caddr_t)label,
5306*0Sstevel@tonic-gate 			sizeof (struct dk_label))) &&
5307*0Sstevel@tonic-gate 
5308*0Sstevel@tonic-gate 		    fdrw(fdc, unit, FDREAD, 0, 0,
5309*0Sstevel@tonic-gate 			un->un_chars->fdc_secptrack + 1,
5310*0Sstevel@tonic-gate 			(caddr_t)label, sizeof (struct dk_label)) &&
5311*0Sstevel@tonic-gate 
5312*0Sstevel@tonic-gate 		    !(err = fdrw(fdc, unit, FDREAD, 0, 0, 1, (caddr_t)label,
5313*0Sstevel@tonic-gate 			sizeof (struct dk_label)))) {
5314*0Sstevel@tonic-gate 
5315*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_GETL,
5316*0Sstevel@tonic-gate 				(C, "fdgetl: succeeded\n"));
5317*0Sstevel@tonic-gate 
5318*0Sstevel@tonic-gate 			break;
5319*0Sstevel@tonic-gate 		}
5320*0Sstevel@tonic-gate 
5321*0Sstevel@tonic-gate 		/*
5322*0Sstevel@tonic-gate 		 * try the next entry in the characteristics tbl
5323*0Sstevel@tonic-gate 		 * If curfdtype is -1, the nxt entry in tbl is 0 (the first).
5324*0Sstevel@tonic-gate 		 */
5325*0Sstevel@tonic-gate 
5326*0Sstevel@tonic-gate 		un->un_curfdtype = (un->un_curfdtype + 1) % nfdtypes;
5327*0Sstevel@tonic-gate 		*(un->un_chars) = fdtypes[un->un_curfdtype];
5328*0Sstevel@tonic-gate 
5329*0Sstevel@tonic-gate 
5330*0Sstevel@tonic-gate 	}
5331*0Sstevel@tonic-gate 
5332*0Sstevel@tonic-gate 	/* print errors again */
5333*0Sstevel@tonic-gate 	fderrlevel = oldlvl;
5334*0Sstevel@tonic-gate 
5335*0Sstevel@tonic-gate 	/* Couldn't read anything */
5336*0Sstevel@tonic-gate 	if (err) {
5337*0Sstevel@tonic-gate 
5338*0Sstevel@tonic-gate 		/* The default characteristics are high density (1.4MB) */
5339*0Sstevel@tonic-gate 		un->un_curfdtype = 1;
5340*0Sstevel@tonic-gate 		*(un->un_chars) = fdtypes[un->un_curfdtype];
5341*0Sstevel@tonic-gate 
5342*0Sstevel@tonic-gate 		fdunpacklabel(&fdlbl_high_80, &un->un_label);
5343*0Sstevel@tonic-gate 
5344*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_GETL,
5345*0Sstevel@tonic-gate 			(C, "fdgetl: Can't autosense diskette\n"));
5346*0Sstevel@tonic-gate 
5347*0Sstevel@tonic-gate 		goto out;
5348*0Sstevel@tonic-gate 	}
5349*0Sstevel@tonic-gate 
5350*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_GETL,
5351*0Sstevel@tonic-gate 	    (C, "fdgetl: fdtype=%d !!!\n", un->un_curfdtype));
5352*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_GETL,
5353*0Sstevel@tonic-gate 	    (C, "fdgetl: rate=%d ssize=%d !!!\n",
5354*0Sstevel@tonic-gate 	    un->un_chars->fdc_transfer_rate, un->un_chars->fdc_sec_size));
5355*0Sstevel@tonic-gate 
5356*0Sstevel@tonic-gate 	/*
5357*0Sstevel@tonic-gate 	 * _something_ was read	 -  look for unixtype label
5358*0Sstevel@tonic-gate 	 */
5359*0Sstevel@tonic-gate 	if (label->dkl_magic != DKL_MAGIC) {
5360*0Sstevel@tonic-gate 
5361*0Sstevel@tonic-gate 		/*
5362*0Sstevel@tonic-gate 		 * The label isn't a unix label.  However, the diskette
5363*0Sstevel@tonic-gate 		 * is formatted because we were able to read the first
5364*0Sstevel@tonic-gate 		 * cylinder.
5365*0Sstevel@tonic-gate 		 */
5366*0Sstevel@tonic-gate 
5367*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_GETL,
5368*0Sstevel@tonic-gate 		    (C, "fdgetl: not unix label\n"));
5369*0Sstevel@tonic-gate 
5370*0Sstevel@tonic-gate 		goto nolabel;
5371*0Sstevel@tonic-gate 	}
5372*0Sstevel@tonic-gate 
5373*0Sstevel@tonic-gate 	/*
5374*0Sstevel@tonic-gate 	 * Checksum the label
5375*0Sstevel@tonic-gate 	 */
5376*0Sstevel@tonic-gate 	count = sizeof (struct dk_label)/sizeof (short);
5377*0Sstevel@tonic-gate 	sp = (short *)label;
5378*0Sstevel@tonic-gate 	xsum = 0;
5379*0Sstevel@tonic-gate 	while (count--)
5380*0Sstevel@tonic-gate 		xsum ^= *sp++;	/* should add up to 0 */
5381*0Sstevel@tonic-gate 	if (xsum) {
5382*0Sstevel@tonic-gate 
5383*0Sstevel@tonic-gate 		/*
5384*0Sstevel@tonic-gate 		 * The checksum fails.  However, the diskette is formatted
5385*0Sstevel@tonic-gate 		 * because we were able to read the first cylinder
5386*0Sstevel@tonic-gate 		 */
5387*0Sstevel@tonic-gate 
5388*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_GETL,
5389*0Sstevel@tonic-gate 		    (C, "fdgetl: bad cksum\n"));
5390*0Sstevel@tonic-gate 
5391*0Sstevel@tonic-gate 		goto nolabel;
5392*0Sstevel@tonic-gate 	}
5393*0Sstevel@tonic-gate 
5394*0Sstevel@tonic-gate 	/*
5395*0Sstevel@tonic-gate 	 * The diskette has a unix label with a correct checksum.
5396*0Sstevel@tonic-gate 	 * Copy the label into the unit structure
5397*0Sstevel@tonic-gate 	 */
5398*0Sstevel@tonic-gate 	un->un_label = *label;
5399*0Sstevel@tonic-gate 
5400*0Sstevel@tonic-gate 	goto out;
5401*0Sstevel@tonic-gate 
5402*0Sstevel@tonic-gate nolabel:
5403*0Sstevel@tonic-gate 	/*
5404*0Sstevel@tonic-gate 	 * The diskette doesn't have a correct unix label, but it is formatted.
5405*0Sstevel@tonic-gate 	 * Use a default label according to the diskette's density
5406*0Sstevel@tonic-gate 	 * (mark default used)
5407*0Sstevel@tonic-gate 	 */
5408*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_GETL,
5409*0Sstevel@tonic-gate 	    (C, "fdgetlabel: unit %d\n", unit));
5410*0Sstevel@tonic-gate 	un->un_flags |= FDUNIT_UNLABELED;
5411*0Sstevel@tonic-gate 	switch (un->un_chars->fdc_secptrack) {
5412*0Sstevel@tonic-gate 	case 9:
5413*0Sstevel@tonic-gate 		fdunpacklabel(&fdlbl_low_80, &un->un_label);
5414*0Sstevel@tonic-gate 		break;
5415*0Sstevel@tonic-gate 	case 8:
5416*0Sstevel@tonic-gate 		fdunpacklabel(&fdlbl_medium_80, &un->un_label);
5417*0Sstevel@tonic-gate 		break;
5418*0Sstevel@tonic-gate 	case 18:
5419*0Sstevel@tonic-gate 		fdunpacklabel(&fdlbl_high_80, &un->un_label);
5420*0Sstevel@tonic-gate 		break;
5421*0Sstevel@tonic-gate 	case 21:
5422*0Sstevel@tonic-gate 		fdunpacklabel(&fdlbl_high_21, &un->un_label);
5423*0Sstevel@tonic-gate 		break;
5424*0Sstevel@tonic-gate 	default:
5425*0Sstevel@tonic-gate 		fdunpacklabel(&fdlbl_high_80, &un->un_label);
5426*0Sstevel@tonic-gate 		break;
5427*0Sstevel@tonic-gate 	}
5428*0Sstevel@tonic-gate 
5429*0Sstevel@tonic-gate out:
5430*0Sstevel@tonic-gate 	if (label != NULL)
5431*0Sstevel@tonic-gate 		kmem_free((caddr_t)label, sizeof (struct dk_label));
5432*0Sstevel@tonic-gate 	return (err);
5433*0Sstevel@tonic-gate }
5434*0Sstevel@tonic-gate 
5435*0Sstevel@tonic-gate /*
5436*0Sstevel@tonic-gate  * fdrw- used only for reading labels  and for DKIOCSVTOC ioctl
5437*0Sstevel@tonic-gate  *	 which reads the 1 sector.
5438*0Sstevel@tonic-gate  */
5439*0Sstevel@tonic-gate static int
5440*0Sstevel@tonic-gate fdrw(struct fdctlr *fdc, int unit, int rw, int cyl, int head,
5441*0Sstevel@tonic-gate     int sector, caddr_t bufp, uint_t len)
5442*0Sstevel@tonic-gate {
5443*0Sstevel@tonic-gate 	struct fdcsb *csb;
5444*0Sstevel@tonic-gate 	struct	fd_char *ch;
5445*0Sstevel@tonic-gate 	int	cmdresult = 0;
5446*0Sstevel@tonic-gate 	caddr_t dma_addr;
5447*0Sstevel@tonic-gate 	size_t	real_length;
5448*0Sstevel@tonic-gate 	int	res;
5449*0Sstevel@tonic-gate 	ddi_device_acc_attr_t attr;
5450*0Sstevel@tonic-gate 	ddi_acc_handle_t	mem_handle = NULL;
5451*0Sstevel@tonic-gate 
5452*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RW, (C, "fdrw\n"));
5453*0Sstevel@tonic-gate 
5454*0Sstevel@tonic-gate 	ASSERT(fdc->c_un->un_unit_no == unit);
5455*0Sstevel@tonic-gate 
5456*0Sstevel@tonic-gate 	CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc);
5457*0Sstevel@tonic-gate 
5458*0Sstevel@tonic-gate 	if (fdc->c_un->un_state == FD_STATE_STOPPED) {
5459*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
5460*0Sstevel@tonic-gate 		if ((pm_raise_power(fdc->c_dip, 0, PM_LEVEL_ON))
5461*0Sstevel@tonic-gate 					!= DDI_SUCCESS) {
5462*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "Power change \
5463*0Sstevel@tonic-gate failed. \n"));
5464*0Sstevel@tonic-gate 			mutex_enter(&fdc->c_lolock);
5465*0Sstevel@tonic-gate 			return (EIO);
5466*0Sstevel@tonic-gate 		}
5467*0Sstevel@tonic-gate 
5468*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_lolock);
5469*0Sstevel@tonic-gate 	}
5470*0Sstevel@tonic-gate 
5471*0Sstevel@tonic-gate 	fdgetcsb(fdc);
5472*0Sstevel@tonic-gate 	csb = &fdc->c_csb;
5473*0Sstevel@tonic-gate 	ch = fdc->c_un->un_chars;
5474*0Sstevel@tonic-gate 	if (rw == FDREAD) {
5475*0Sstevel@tonic-gate 		if (fdc->c_fdtype & FDCTYPE_TCBUG) {
5476*0Sstevel@tonic-gate 			/*
5477*0Sstevel@tonic-gate 			 * kludge for lack of Multitrack functionality
5478*0Sstevel@tonic-gate 			 */
5479*0Sstevel@tonic-gate 			csb->csb_cmds[0] = SK + FDRAW_RDCMD;
5480*0Sstevel@tonic-gate 		} else
5481*0Sstevel@tonic-gate 			csb->csb_cmds[0] = MT + SK + FDRAW_RDCMD;
5482*0Sstevel@tonic-gate 	} else { /* write */
5483*0Sstevel@tonic-gate 		if (fdc->c_fdtype & FDCTYPE_TCBUG) {
5484*0Sstevel@tonic-gate 			/*
5485*0Sstevel@tonic-gate 			 * kludge for lack of Multitrack functionality
5486*0Sstevel@tonic-gate 			 */
5487*0Sstevel@tonic-gate 			csb->csb_cmds[0] = FDRAW_WRCMD;
5488*0Sstevel@tonic-gate 		} else
5489*0Sstevel@tonic-gate 			csb->csb_cmds[0] = MT + FDRAW_WRCMD;
5490*0Sstevel@tonic-gate 	}
5491*0Sstevel@tonic-gate 
5492*0Sstevel@tonic-gate 	if (rw == FDREAD)
5493*0Sstevel@tonic-gate 		fdc->c_csb.csb_read = CSB_READ;
5494*0Sstevel@tonic-gate 	else
5495*0Sstevel@tonic-gate 		fdc->c_csb.csb_read = CSB_WRITE;
5496*0Sstevel@tonic-gate 
5497*0Sstevel@tonic-gate 	/* always or in MFM bit */
5498*0Sstevel@tonic-gate 	csb->csb_cmds[0] |= MFM;
5499*0Sstevel@tonic-gate 	csb->csb_cmds[1] = (uchar_t)(unit | ((head & 0x1) << 2));
5500*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_SB)
5501*0Sstevel@tonic-gate 		csb->csb_cmds[1] |= IPS;
5502*0Sstevel@tonic-gate 	csb->csb_cmds[2] = (uchar_t)cyl;
5503*0Sstevel@tonic-gate 	csb->csb_cmds[3] = (uchar_t)head;
5504*0Sstevel@tonic-gate 	csb->csb_cmds[4] = (uchar_t)sector;
5505*0Sstevel@tonic-gate 	csb->csb_cmds[5] = ch->fdc_medium ? 3 : 2; /* sector size code */
5506*0Sstevel@tonic-gate 	/*
5507*0Sstevel@tonic-gate 	 * kludge for end-of-cylinder error.
5508*0Sstevel@tonic-gate 	 */
5509*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_TCBUG)
5510*0Sstevel@tonic-gate 		csb->csb_cmds[6] = sector + (len / ch->fdc_sec_size) - 1;
5511*0Sstevel@tonic-gate 	else
5512*0Sstevel@tonic-gate 		csb->csb_cmds[6] =
5513*0Sstevel@tonic-gate 		    (uchar_t)max(fdc->c_un->un_chars->fdc_secptrack, sector);
5514*0Sstevel@tonic-gate 	csb->csb_len = len;
5515*0Sstevel@tonic-gate 	csb->csb_cmds[7] = GPLN;
5516*0Sstevel@tonic-gate 	csb->csb_cmds[8] = SSSDTL;
5517*0Sstevel@tonic-gate 	csb->csb_ncmds = NCBRW;
5518*0Sstevel@tonic-gate 	csb->csb_len = len;
5519*0Sstevel@tonic-gate 	csb->csb_maxretry = 2;
5520*0Sstevel@tonic-gate 	csb->csb_retrys = 0;
5521*0Sstevel@tonic-gate 	bzero(csb->csb_rslt, NRBRW);
5522*0Sstevel@tonic-gate 	csb->csb_nrslts = NRBRW;
5523*0Sstevel@tonic-gate 	csb->csb_opflags = CSB_OFXFEROPS | CSB_OFTIMEIT;
5524*0Sstevel@tonic-gate 
5525*0Sstevel@tonic-gate 	/* If platform supports DMA, set up DMA resources */
5526*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_DMA) {
5527*0Sstevel@tonic-gate 
5528*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_hilock);
5529*0Sstevel@tonic-gate 
5530*0Sstevel@tonic-gate 		attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
5531*0Sstevel@tonic-gate 		attr.devacc_attr_endian_flags  = DDI_STRUCTURE_BE_ACC;
5532*0Sstevel@tonic-gate 		attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
5533*0Sstevel@tonic-gate 
5534*0Sstevel@tonic-gate 		res = ddi_dma_mem_alloc(fdc->c_dmahandle, len,
5535*0Sstevel@tonic-gate 			&attr, DDI_DMA_STREAMING,
5536*0Sstevel@tonic-gate 			DDI_DMA_DONTWAIT, 0, &dma_addr, &real_length,
5537*0Sstevel@tonic-gate 			&mem_handle);
5538*0Sstevel@tonic-gate 
5539*0Sstevel@tonic-gate 		if (res != DDI_SUCCESS) {
5540*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_RW,
5541*0Sstevel@tonic-gate 				(C, "fdrw: dma mem alloc failed\n"));
5542*0Sstevel@tonic-gate 
5543*0Sstevel@tonic-gate 			fdretcsb(fdc);
5544*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_hilock);
5545*0Sstevel@tonic-gate 			return (EIO);
5546*0Sstevel@tonic-gate 		}
5547*0Sstevel@tonic-gate 
5548*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_RW, (C, "fdrw: allocated memory"));
5549*0Sstevel@tonic-gate 
5550*0Sstevel@tonic-gate 		if (fdstart_dma(fdc, dma_addr, len) != 0) {
5551*0Sstevel@tonic-gate 			fdretcsb(fdc);
5552*0Sstevel@tonic-gate 			ddi_dma_mem_free(&mem_handle);
5553*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_hilock);
5554*0Sstevel@tonic-gate 			return (-1);
5555*0Sstevel@tonic-gate 
5556*0Sstevel@tonic-gate 		}
5557*0Sstevel@tonic-gate 
5558*0Sstevel@tonic-gate 		/*
5559*0Sstevel@tonic-gate 		 * If the command is a write, copy the data to be written to
5560*0Sstevel@tonic-gate 		 * dma_addr.
5561*0Sstevel@tonic-gate 		 */
5562*0Sstevel@tonic-gate 
5563*0Sstevel@tonic-gate 		if (fdc->c_csb.csb_read == CSB_WRITE) {
5564*0Sstevel@tonic-gate 			bcopy((char *)bufp, (char *)dma_addr, len);
5565*0Sstevel@tonic-gate 		}
5566*0Sstevel@tonic-gate 
5567*0Sstevel@tonic-gate 		csb->csb_addr = dma_addr;
5568*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_hilock);
5569*0Sstevel@tonic-gate 	} else {
5570*0Sstevel@tonic-gate 		csb->csb_addr = bufp;
5571*0Sstevel@tonic-gate 	}
5572*0Sstevel@tonic-gate 
5573*0Sstevel@tonic-gate 
5574*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RW, (C, "fdrw: call fdexec\n"));
5575*0Sstevel@tonic-gate 
5576*0Sstevel@tonic-gate 	if (fdexec(fdc, FDXC_SLEEP | FDXC_CHECKCHG) != 0) {
5577*0Sstevel@tonic-gate 		fdretcsb(fdc);
5578*0Sstevel@tonic-gate 
5579*0Sstevel@tonic-gate 		if (mem_handle)
5580*0Sstevel@tonic-gate 			ddi_dma_mem_free(&mem_handle);
5581*0Sstevel@tonic-gate 
5582*0Sstevel@tonic-gate 		return (EIO);
5583*0Sstevel@tonic-gate 
5584*0Sstevel@tonic-gate 	}
5585*0Sstevel@tonic-gate 
5586*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RW, (C, "fdrw: fdexec returned\n"));
5587*0Sstevel@tonic-gate 
5588*0Sstevel@tonic-gate 	/*
5589*0Sstevel@tonic-gate 	 * if DMA was used and the command was a read
5590*0Sstevel@tonic-gate 	 * copy the results into bufp
5591*0Sstevel@tonic-gate 	 */
5592*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_DMA) {
5593*0Sstevel@tonic-gate 		if (fdc->c_csb.csb_read == CSB_READ) {
5594*0Sstevel@tonic-gate 			bcopy((char *)dma_addr, (char *)bufp, len);
5595*0Sstevel@tonic-gate 		}
5596*0Sstevel@tonic-gate 		ddi_dma_mem_free(&mem_handle);
5597*0Sstevel@tonic-gate 	}
5598*0Sstevel@tonic-gate 
5599*0Sstevel@tonic-gate 	if (csb->csb_cmdstat)
5600*0Sstevel@tonic-gate 		cmdresult = EIO;	/* XXX TBD NYD for now */
5601*0Sstevel@tonic-gate 
5602*0Sstevel@tonic-gate 	fdretcsb(fdc);
5603*0Sstevel@tonic-gate 	return (cmdresult);
5604*0Sstevel@tonic-gate }
5605*0Sstevel@tonic-gate 
5606*0Sstevel@tonic-gate /*
5607*0Sstevel@tonic-gate  * fdunpacklabel
5608*0Sstevel@tonic-gate  *	this unpacks a (packed) struct dk_label into a standard dk_label.
5609*0Sstevel@tonic-gate  */
5610*0Sstevel@tonic-gate static void
5611*0Sstevel@tonic-gate fdunpacklabel(struct packed_label *from, struct dk_label *to)
5612*0Sstevel@tonic-gate {
5613*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_PACK, (C, "fdpacklabel\n"));
5614*0Sstevel@tonic-gate 	bzero((caddr_t)to, sizeof (*to));
5615*0Sstevel@tonic-gate 	bcopy((caddr_t)&from->dkl_vname, (caddr_t)to->dkl_asciilabel,
5616*0Sstevel@tonic-gate 	    sizeof (to->dkl_asciilabel));
5617*0Sstevel@tonic-gate 	to->dkl_rpm = from->dkl_rpm;	/* rotations per minute */
5618*0Sstevel@tonic-gate 	to->dkl_pcyl = from->dkl_pcyl;	/* # physical cylinders */
5619*0Sstevel@tonic-gate 	to->dkl_apc = from->dkl_apc;	/* alternates per cylinder */
5620*0Sstevel@tonic-gate 	to->dkl_intrlv = from->dkl_intrlv;	/* interleave factor */
5621*0Sstevel@tonic-gate 	to->dkl_ncyl = from->dkl_ncyl;	/* # of data cylinders */
5622*0Sstevel@tonic-gate 	to->dkl_acyl = from->dkl_acyl;	/* # of alternate cylinders */
5623*0Sstevel@tonic-gate 	to->dkl_nhead = from->dkl_nhead; /* # of heads in this partition */
5624*0Sstevel@tonic-gate 	to->dkl_nsect = from->dkl_nsect; /* # of 512 byte sectors per track */
5625*0Sstevel@tonic-gate 	/* logical partitions */
5626*0Sstevel@tonic-gate 	bcopy((caddr_t)from->dkl_map, (caddr_t)to->dkl_map,
5627*0Sstevel@tonic-gate 	    sizeof (struct dk_map32) * NDKMAP);
5628*0Sstevel@tonic-gate 	to->dkl_vtoc = from->dkl_vtoc;
5629*0Sstevel@tonic-gate }
5630*0Sstevel@tonic-gate 
5631*0Sstevel@tonic-gate static struct fdctlr *
5632*0Sstevel@tonic-gate fd_getctlr(dev_t dev)
5633*0Sstevel@tonic-gate {
5634*0Sstevel@tonic-gate 
5635*0Sstevel@tonic-gate 	struct fdctlr *fdc = fdctlrs;
5636*0Sstevel@tonic-gate 	int ctlr = FDCTLR(dev);
5637*0Sstevel@tonic-gate 
5638*0Sstevel@tonic-gate 	while (fdc) {
5639*0Sstevel@tonic-gate 		if (ddi_get_instance(fdc->c_dip) == ctlr)
5640*0Sstevel@tonic-gate 			return (fdc);
5641*0Sstevel@tonic-gate 		fdc = fdc->c_next;
5642*0Sstevel@tonic-gate 	}
5643*0Sstevel@tonic-gate 	return (fdc);
5644*0Sstevel@tonic-gate }
5645*0Sstevel@tonic-gate 
5646*0Sstevel@tonic-gate static int
5647*0Sstevel@tonic-gate fd_unit_is_open(struct fdunit *un)
5648*0Sstevel@tonic-gate {
5649*0Sstevel@tonic-gate 	int i;
5650*0Sstevel@tonic-gate 	for (i = 0; i < NDKMAP; i++)
5651*0Sstevel@tonic-gate 		if (un->un_lyropen[i])
5652*0Sstevel@tonic-gate 			return (1);
5653*0Sstevel@tonic-gate 	for (i = 0; i < OTYPCNT - 1; i++)
5654*0Sstevel@tonic-gate 		if (un->un_regopen[i])
5655*0Sstevel@tonic-gate 			return (1);
5656*0Sstevel@tonic-gate 	return (0);
5657*0Sstevel@tonic-gate }
5658*0Sstevel@tonic-gate 
5659*0Sstevel@tonic-gate /*
5660*0Sstevel@tonic-gate  * Return the a vtoc structure in *vtoc.
5661*0Sstevel@tonic-gate  * The vtoc is built from information in
5662*0Sstevel@tonic-gate  * the diskette's label.
5663*0Sstevel@tonic-gate  */
5664*0Sstevel@tonic-gate static void
5665*0Sstevel@tonic-gate fd_build_user_vtoc(struct fdunit *un, struct vtoc *vtoc)
5666*0Sstevel@tonic-gate {
5667*0Sstevel@tonic-gate 	int i;
5668*0Sstevel@tonic-gate 	int nblks;			/* DEV_BSIZE sectors per cylinder */
5669*0Sstevel@tonic-gate 	struct dk_map2 *lpart;
5670*0Sstevel@tonic-gate 	struct dk_map32	*lmap;
5671*0Sstevel@tonic-gate 	struct partition *vpart;
5672*0Sstevel@tonic-gate 
5673*0Sstevel@tonic-gate 	bzero(vtoc, sizeof (struct vtoc));
5674*0Sstevel@tonic-gate 
5675*0Sstevel@tonic-gate 	/* Initialize info. needed by mboot.  (unsupported) */
5676*0Sstevel@tonic-gate 	vtoc->v_bootinfo[0] = un->un_label.dkl_vtoc.v_bootinfo[0];
5677*0Sstevel@tonic-gate 	vtoc->v_bootinfo[1] = un->un_label.dkl_vtoc.v_bootinfo[1];
5678*0Sstevel@tonic-gate 	vtoc->v_bootinfo[2] = un->un_label.dkl_vtoc.v_bootinfo[2];
5679*0Sstevel@tonic-gate 
5680*0Sstevel@tonic-gate 	/* Fill in vtoc sanity and version information */
5681*0Sstevel@tonic-gate 	vtoc->v_sanity		= un->un_label.dkl_vtoc.v_sanity;
5682*0Sstevel@tonic-gate 	vtoc->v_version		= un->un_label.dkl_vtoc.v_version;
5683*0Sstevel@tonic-gate 
5684*0Sstevel@tonic-gate 	/* Copy the volume name */
5685*0Sstevel@tonic-gate 	bcopy(un->un_label.dkl_vtoc.v_volume,
5686*0Sstevel@tonic-gate 	    vtoc->v_volume, LEN_DKL_VVOL);
5687*0Sstevel@tonic-gate 
5688*0Sstevel@tonic-gate 	/*
5689*0Sstevel@tonic-gate 	 * The dk_map32 structure is based on DEV_BSIZE byte blocks.
5690*0Sstevel@tonic-gate 	 * However, medium density diskettes have 1024 byte blocks.
5691*0Sstevel@tonic-gate 	 * The number of sectors per partition listed in the dk_map32 structure
5692*0Sstevel@tonic-gate 	 * accounts for this by multiplying the number of 1024 byte
5693*0Sstevel@tonic-gate 	 * blocks by 2.  (See the packed_label initializations.)  The
5694*0Sstevel@tonic-gate 	 * 1024 byte block size can not be listed for medium density
5695*0Sstevel@tonic-gate 	 * diskettes because the kernel is hard coded for DEV_BSIZE
5696*0Sstevel@tonic-gate 	 * blocks.
5697*0Sstevel@tonic-gate 	 */
5698*0Sstevel@tonic-gate 	vtoc->v_sectorsz = DEV_BSIZE;
5699*0Sstevel@tonic-gate 	vtoc->v_nparts = un->un_label.dkl_vtoc.v_nparts;
5700*0Sstevel@tonic-gate 
5701*0Sstevel@tonic-gate 	/* Copy the reserved space */
5702*0Sstevel@tonic-gate 	bcopy(un->un_label.dkl_vtoc.v_reserved,
5703*0Sstevel@tonic-gate 	    vtoc->v_reserved, sizeof (un->un_label.dkl_vtoc.v_reserved));
5704*0Sstevel@tonic-gate 	/*
5705*0Sstevel@tonic-gate 	 * Convert partitioning information.
5706*0Sstevel@tonic-gate 	 *
5707*0Sstevel@tonic-gate 	 * Note the conversion from starting cylinder number
5708*0Sstevel@tonic-gate 	 * to starting sector number.
5709*0Sstevel@tonic-gate 	 */
5710*0Sstevel@tonic-gate 	lmap = un->un_label.dkl_map;
5711*0Sstevel@tonic-gate 	lpart = un->un_label.dkl_vtoc.v_part;
5712*0Sstevel@tonic-gate 	vpart = vtoc->v_part;
5713*0Sstevel@tonic-gate 
5714*0Sstevel@tonic-gate 	nblks = (un->un_chars->fdc_nhead * un->un_chars->fdc_secptrack *
5715*0Sstevel@tonic-gate 		un->un_chars->fdc_sec_size) / DEV_BSIZE;
5716*0Sstevel@tonic-gate 
5717*0Sstevel@tonic-gate 	for (i = 0; i < V_NUMPAR; i++) {
5718*0Sstevel@tonic-gate 		vpart->p_tag	= lpart->p_tag;
5719*0Sstevel@tonic-gate 		vpart->p_flag	= lpart->p_flag;
5720*0Sstevel@tonic-gate 		vpart->p_start	= lmap->dkl_cylno * nblks;
5721*0Sstevel@tonic-gate 		vpart->p_size	= lmap->dkl_nblk;
5722*0Sstevel@tonic-gate 
5723*0Sstevel@tonic-gate 		lmap++;
5724*0Sstevel@tonic-gate 		lpart++;
5725*0Sstevel@tonic-gate 		vpart++;
5726*0Sstevel@tonic-gate 	}
5727*0Sstevel@tonic-gate 
5728*0Sstevel@tonic-gate 	/* Initialize timestamp and label */
5729*0Sstevel@tonic-gate 	bcopy(un->un_label.dkl_vtoc.v_timestamp,
5730*0Sstevel@tonic-gate 	    vtoc->timestamp, sizeof (vtoc->timestamp));
5731*0Sstevel@tonic-gate 
5732*0Sstevel@tonic-gate 	bcopy(un->un_label.dkl_asciilabel,
5733*0Sstevel@tonic-gate 	    vtoc->v_asciilabel, LEN_DKL_ASCII);
5734*0Sstevel@tonic-gate }
5735*0Sstevel@tonic-gate 
5736*0Sstevel@tonic-gate /*
5737*0Sstevel@tonic-gate  * Build a label out of a vtoc structure.
5738*0Sstevel@tonic-gate  */
5739*0Sstevel@tonic-gate static int
5740*0Sstevel@tonic-gate fd_build_label_vtoc(struct fdunit *un, struct vtoc *vtoc)
5741*0Sstevel@tonic-gate {
5742*0Sstevel@tonic-gate 	struct dk_map32		*lmap;
5743*0Sstevel@tonic-gate 	struct dk_map2		*lpart;
5744*0Sstevel@tonic-gate 	struct partition	*vpart;
5745*0Sstevel@tonic-gate 	int			nblks;	/* no. blocks per cylinder */
5746*0Sstevel@tonic-gate 	int			ncyl;
5747*0Sstevel@tonic-gate 	int			i;
5748*0Sstevel@tonic-gate 	short	 sum, *sp;
5749*0Sstevel@tonic-gate 
5750*0Sstevel@tonic-gate 	/* Sanity-check the vtoc */
5751*0Sstevel@tonic-gate 	if ((vtoc->v_sanity != VTOC_SANE) ||
5752*0Sstevel@tonic-gate 			(vtoc->v_nparts > NDKMAP) || (vtoc->v_nparts <= 0)) {
5753*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_IOCT,
5754*0Sstevel@tonic-gate 		    (C, "fd_build_label:  sanity check on vtoc failed\n"));
5755*0Sstevel@tonic-gate 		return (EINVAL);
5756*0Sstevel@tonic-gate 	}
5757*0Sstevel@tonic-gate 
5758*0Sstevel@tonic-gate 	nblks = (un->un_chars->fdc_nhead * un->un_chars->fdc_secptrack *
5759*0Sstevel@tonic-gate 		un->un_chars->fdc_sec_size) / DEV_BSIZE;
5760*0Sstevel@tonic-gate 
5761*0Sstevel@tonic-gate 	vpart = vtoc->v_part;
5762*0Sstevel@tonic-gate 
5763*0Sstevel@tonic-gate 	/*
5764*0Sstevel@tonic-gate 	 * Check the partition information in the vtoc.  The starting sectors
5765*0Sstevel@tonic-gate 	 * must lie along partition boundaries. (NDKMAP entries are checked
5766*0Sstevel@tonic-gate 	 * to ensure that the unused entries are set to 0 if vtoc->v_nparts
5767*0Sstevel@tonic-gate 	 * is less than NDKMAP)
5768*0Sstevel@tonic-gate 	 */
5769*0Sstevel@tonic-gate 
5770*0Sstevel@tonic-gate 	for (i = 0; i < NDKMAP; i++) {
5771*0Sstevel@tonic-gate 		if ((vpart->p_start % nblks) != 0) {
5772*0Sstevel@tonic-gate 			return (EINVAL);
5773*0Sstevel@tonic-gate 		}
5774*0Sstevel@tonic-gate 		ncyl = vpart->p_start % nblks;
5775*0Sstevel@tonic-gate 		ncyl += vpart->p_size % nblks;
5776*0Sstevel@tonic-gate 		if ((vpart->p_size % nblks) != 0)
5777*0Sstevel@tonic-gate 			ncyl++;
5778*0Sstevel@tonic-gate 		if (ncyl > un->un_chars->fdc_ncyl) {
5779*0Sstevel@tonic-gate 			return (EINVAL);
5780*0Sstevel@tonic-gate 		}
5781*0Sstevel@tonic-gate 		vpart++;
5782*0Sstevel@tonic-gate 	}
5783*0Sstevel@tonic-gate 
5784*0Sstevel@tonic-gate 	/*
5785*0Sstevel@tonic-gate 	 * reinitialize the existing label
5786*0Sstevel@tonic-gate 	 */
5787*0Sstevel@tonic-gate 	bzero(&un->un_label, sizeof (un->un_label));
5788*0Sstevel@tonic-gate 
5789*0Sstevel@tonic-gate 	/* Put appropriate vtoc structure fields into the disk label */
5790*0Sstevel@tonic-gate 	un->un_label.dkl_vtoc.v_bootinfo[0] = (uint32_t)vtoc->v_bootinfo[0];
5791*0Sstevel@tonic-gate 	un->un_label.dkl_vtoc.v_bootinfo[1] = (uint32_t)vtoc->v_bootinfo[1];
5792*0Sstevel@tonic-gate 	un->un_label.dkl_vtoc.v_bootinfo[2] = (uint32_t)vtoc->v_bootinfo[2];
5793*0Sstevel@tonic-gate 
5794*0Sstevel@tonic-gate 	un->un_label.dkl_vtoc.v_sanity = vtoc->v_sanity;
5795*0Sstevel@tonic-gate 	un->un_label.dkl_vtoc.v_version = vtoc->v_version;
5796*0Sstevel@tonic-gate 
5797*0Sstevel@tonic-gate 	bcopy(vtoc->v_volume, un->un_label.dkl_vtoc.v_volume, LEN_DKL_VVOL);
5798*0Sstevel@tonic-gate 
5799*0Sstevel@tonic-gate 	un->un_label.dkl_vtoc.v_nparts = vtoc->v_nparts;
5800*0Sstevel@tonic-gate 
5801*0Sstevel@tonic-gate 	bcopy(vtoc->v_reserved, un->un_label.dkl_vtoc.v_reserved,
5802*0Sstevel@tonic-gate 	    sizeof (un->un_label.dkl_vtoc.v_reserved));
5803*0Sstevel@tonic-gate 
5804*0Sstevel@tonic-gate 	/*
5805*0Sstevel@tonic-gate 	 * Initialize cylinder information in the label.
5806*0Sstevel@tonic-gate 	 * Note the conversion from starting sector number
5807*0Sstevel@tonic-gate 	 * to starting cylinder number.
5808*0Sstevel@tonic-gate 	 * Return error if division results in a remainder.
5809*0Sstevel@tonic-gate 	 */
5810*0Sstevel@tonic-gate 	lmap = un->un_label.dkl_map;
5811*0Sstevel@tonic-gate 	lpart = un->un_label.dkl_vtoc.v_part;
5812*0Sstevel@tonic-gate 	vpart = vtoc->v_part;
5813*0Sstevel@tonic-gate 
5814*0Sstevel@tonic-gate 	for (i = 0; i < (int)vtoc->v_nparts; i++) {
5815*0Sstevel@tonic-gate 		lpart->p_tag  = vtoc->v_part[i].p_tag;
5816*0Sstevel@tonic-gate 		lpart->p_flag = vtoc->v_part[i].p_flag;
5817*0Sstevel@tonic-gate 		lmap->dkl_cylno = vpart->p_start / nblks;
5818*0Sstevel@tonic-gate 		lmap->dkl_nblk = vpart->p_size;
5819*0Sstevel@tonic-gate 
5820*0Sstevel@tonic-gate 		lmap++;
5821*0Sstevel@tonic-gate 		lpart++;
5822*0Sstevel@tonic-gate 		vpart++;
5823*0Sstevel@tonic-gate 	}
5824*0Sstevel@tonic-gate 
5825*0Sstevel@tonic-gate 	/* Copy the timestamp and ascii label */
5826*0Sstevel@tonic-gate 	for (i = 0; i < NDKMAP; i++) {
5827*0Sstevel@tonic-gate 		un->un_label.dkl_vtoc.v_timestamp[i] = vtoc->timestamp[i];
5828*0Sstevel@tonic-gate 	}
5829*0Sstevel@tonic-gate 
5830*0Sstevel@tonic-gate 
5831*0Sstevel@tonic-gate 	bcopy(vtoc->v_asciilabel, un->un_label.dkl_asciilabel, LEN_DKL_ASCII);
5832*0Sstevel@tonic-gate 
5833*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_IOCT,
5834*0Sstevel@tonic-gate 		    (C, "fd_build_label: asciilabel %s\n",
5835*0Sstevel@tonic-gate 			un->un_label.dkl_asciilabel));
5836*0Sstevel@tonic-gate 
5837*0Sstevel@tonic-gate 	/* Initialize the magic number */
5838*0Sstevel@tonic-gate 	un->un_label.dkl_magic = DKL_MAGIC;
5839*0Sstevel@tonic-gate 
5840*0Sstevel@tonic-gate 	un->un_label.dkl_pcyl = un->un_chars->fdc_ncyl;
5841*0Sstevel@tonic-gate 
5842*0Sstevel@tonic-gate 	/*
5843*0Sstevel@tonic-gate 	 * The fdc_secptrack filed of the fd_char structure is the number
5844*0Sstevel@tonic-gate 	 * of sectors per track where the sectors are fdc_sec_size.  The
5845*0Sstevel@tonic-gate 	 * dkl_nsect field of the dk_label structure is the number of
5846*0Sstevel@tonic-gate 	 * 512 (DEVBSIZE) byte sectors per track.
5847*0Sstevel@tonic-gate 	 */
5848*0Sstevel@tonic-gate 	un->un_label.dkl_nsect = (un->un_chars->fdc_secptrack *
5849*0Sstevel@tonic-gate 				un->un_chars->fdc_sec_size) / DEV_BSIZE;
5850*0Sstevel@tonic-gate 
5851*0Sstevel@tonic-gate 
5852*0Sstevel@tonic-gate 	un->un_label.dkl_ncyl = un->un_label.dkl_pcyl;
5853*0Sstevel@tonic-gate 	un->un_label.dkl_nhead = un->un_chars->fdc_nhead;
5854*0Sstevel@tonic-gate 	un->un_label.dkl_rpm = un->un_chars->fdc_medium ? 360 : 300;
5855*0Sstevel@tonic-gate 	un->un_label.dkl_intrlv = 1;
5856*0Sstevel@tonic-gate 
5857*0Sstevel@tonic-gate 	/* Create the checksum */
5858*0Sstevel@tonic-gate 	sum = 0;
5859*0Sstevel@tonic-gate 	un->un_label.dkl_cksum = 0;
5860*0Sstevel@tonic-gate 	sp = (short *)&un->un_label;
5861*0Sstevel@tonic-gate 	i = sizeof (struct dk_label)/sizeof (short);
5862*0Sstevel@tonic-gate 	while (i--) {
5863*0Sstevel@tonic-gate 		sum ^= *sp++;
5864*0Sstevel@tonic-gate 	}
5865*0Sstevel@tonic-gate 	un->un_label.dkl_cksum = sum;
5866*0Sstevel@tonic-gate 
5867*0Sstevel@tonic-gate 	return (0);
5868*0Sstevel@tonic-gate }
5869*0Sstevel@tonic-gate 
5870*0Sstevel@tonic-gate /*
5871*0Sstevel@tonic-gate  * Check for auxio register node
5872*0Sstevel@tonic-gate  */
5873*0Sstevel@tonic-gate 
5874*0Sstevel@tonic-gate int
5875*0Sstevel@tonic-gate fd_isauxiodip(dev_info_t *dip)
5876*0Sstevel@tonic-gate {
5877*0Sstevel@tonic-gate 	if (strcmp(ddi_get_name(dip), "auxio") == 0 ||
5878*0Sstevel@tonic-gate 	    strcmp(ddi_get_name(dip), "auxiliary-io") == 0) {
5879*0Sstevel@tonic-gate 		return (1);
5880*0Sstevel@tonic-gate 	}
5881*0Sstevel@tonic-gate 	return (0);
5882*0Sstevel@tonic-gate }
5883*0Sstevel@tonic-gate 
5884*0Sstevel@tonic-gate /*
5885*0Sstevel@tonic-gate  * Search for auxio register node, then for address property
5886*0Sstevel@tonic-gate  */
5887*0Sstevel@tonic-gate 
5888*0Sstevel@tonic-gate caddr_t
5889*0Sstevel@tonic-gate fd_getauxiova(dev_info_t *dip)
5890*0Sstevel@tonic-gate {
5891*0Sstevel@tonic-gate 	dev_info_t *auxdip;
5892*0Sstevel@tonic-gate 	caddr_t addr;
5893*0Sstevel@tonic-gate 
5894*0Sstevel@tonic-gate 	/*
5895*0Sstevel@tonic-gate 	 * Search sibling list, which happens to be safe inside attach
5896*0Sstevel@tonic-gate 	 */
5897*0Sstevel@tonic-gate 	auxdip = ddi_get_child(ddi_get_parent(dip));
5898*0Sstevel@tonic-gate 	while (auxdip) {
5899*0Sstevel@tonic-gate 		if (fd_isauxiodip(auxdip))
5900*0Sstevel@tonic-gate 			break;
5901*0Sstevel@tonic-gate 		auxdip = ddi_get_next_sibling(auxdip);
5902*0Sstevel@tonic-gate 	}
5903*0Sstevel@tonic-gate 
5904*0Sstevel@tonic-gate 	if (auxdip == NULL)
5905*0Sstevel@tonic-gate 		return (NULL);
5906*0Sstevel@tonic-gate 
5907*0Sstevel@tonic-gate 	addr = (caddr_t)(caddr32_t)ddi_getprop(DDI_DEV_T_ANY,
5908*0Sstevel@tonic-gate 		auxdip, DDI_PROP_DONTPASS, "address", 0);
5909*0Sstevel@tonic-gate 
5910*0Sstevel@tonic-gate 	/*
5911*0Sstevel@tonic-gate 	 * The device tree on some sun4c machines (SS1+) incorrectly
5912*0Sstevel@tonic-gate 	 * reports the "auxiliary-io" as being word wide at an
5913*0Sstevel@tonic-gate 	 * aligned address rather than byte wide at an offset of 3.
5914*0Sstevel@tonic-gate 	 * Here we correct for this ..
5915*0Sstevel@tonic-gate 	 */
5916*0Sstevel@tonic-gate 	if (strcmp(ddi_get_name(auxdip), "auxiliary-io") == 0 &&
5917*0Sstevel@tonic-gate 	    (((int)addr & 3) == 0))
5918*0Sstevel@tonic-gate 		addr += 3;
5919*0Sstevel@tonic-gate 
5920*0Sstevel@tonic-gate 	return (addr);
5921*0Sstevel@tonic-gate }
5922*0Sstevel@tonic-gate 
5923*0Sstevel@tonic-gate 
5924*0Sstevel@tonic-gate /*
5925*0Sstevel@tonic-gate  * set_rotational speed
5926*0Sstevel@tonic-gate  * 300 rpm for high and low density.
5927*0Sstevel@tonic-gate  * 360 rpm for medium density.
5928*0Sstevel@tonic-gate  * for now, we assume that 3rd density is supported only for Sun4M,
5929*0Sstevel@tonic-gate  * not for Clones. (else we would have to check for 82077, and do
5930*0Sstevel@tonic-gate  * specific things for the MEDIUM_DENSITY BIT for clones.
5931*0Sstevel@tonic-gate  * this code should not break CLONES.
5932*0Sstevel@tonic-gate  *
5933*0Sstevel@tonic-gate  * REMARK: there is a SOny requirement, to deselect the drive then
5934*0Sstevel@tonic-gate  * select it again after the medium density change, since the
5935*0Sstevel@tonic-gate  * leading edge of the select line latches the rotational Speed.
5936*0Sstevel@tonic-gate  * then after that, we have to wait 500 ms for the rotation to
5937*0Sstevel@tonic-gate  * stabilize.
5938*0Sstevel@tonic-gate  *
5939*0Sstevel@tonic-gate  */
5940*0Sstevel@tonic-gate static void
5941*0Sstevel@tonic-gate set_rotational_speed(struct fdctlr *fdc, int unit)
5942*0Sstevel@tonic-gate {
5943*0Sstevel@tonic-gate 	int check;
5944*0Sstevel@tonic-gate 	int is_medium;
5945*0Sstevel@tonic-gate 
5946*0Sstevel@tonic-gate 	ASSERT(fdc->c_un->un_unit_no == unit);
5947*0Sstevel@tonic-gate 
5948*0Sstevel@tonic-gate 	/*
5949*0Sstevel@tonic-gate 	 * if we do not have a Sun4m, medium density is not supported.
5950*0Sstevel@tonic-gate 	 */
5951*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_MACHIO)
5952*0Sstevel@tonic-gate 		return;
5953*0Sstevel@tonic-gate 
5954*0Sstevel@tonic-gate 	/*
5955*0Sstevel@tonic-gate 	 * if FDUNIT_SET_SPEED is set, set the speed.
5956*0Sstevel@tonic-gate 	 * else,
5957*0Sstevel@tonic-gate 	 *	if there is a change, do it, if not leave it alone.
5958*0Sstevel@tonic-gate 	 *	there is a change if un->un_chars->fdc_medium does not match
5959*0Sstevel@tonic-gate 	 *	un->un_flags & FDUNIT_MEDIUM
5960*0Sstevel@tonic-gate 	 *	un->un_flags & FDUNIT_MEDIUM specifies the last setting.
5961*0Sstevel@tonic-gate 	 *	un->un_chars->fdc_medium specifies next setting.
5962*0Sstevel@tonic-gate 	 *	if there is a change, wait 500ms according to Sony spec.
5963*0Sstevel@tonic-gate 	 */
5964*0Sstevel@tonic-gate 
5965*0Sstevel@tonic-gate 	is_medium = fdc->c_un->un_chars->fdc_medium;
5966*0Sstevel@tonic-gate 
5967*0Sstevel@tonic-gate 	if (fdc->c_un->un_flags & FDUNIT_SET_SPEED) {
5968*0Sstevel@tonic-gate 		check = 1;
5969*0Sstevel@tonic-gate 	} else {
5970*0Sstevel@tonic-gate 		check = is_medium ^
5971*0Sstevel@tonic-gate 			((fdc->c_un->un_flags & FDUNIT_MEDIUM) ? 1 : 0);
5972*0Sstevel@tonic-gate 
5973*0Sstevel@tonic-gate 		/* Set the un_flags if necessary */
5974*0Sstevel@tonic-gate 
5975*0Sstevel@tonic-gate 		if (check)
5976*0Sstevel@tonic-gate 			fdc->c_un->un_flags ^= FDUNIT_MEDIUM;
5977*0Sstevel@tonic-gate 	}
5978*0Sstevel@tonic-gate 
5979*0Sstevel@tonic-gate 	fdc->c_un->un_flags &= ~FDUNIT_SET_SPEED;
5980*0Sstevel@tonic-gate 
5981*0Sstevel@tonic-gate 
5982*0Sstevel@tonic-gate 	if (check) {
5983*0Sstevel@tonic-gate 
5984*0Sstevel@tonic-gate 		fdselect(fdc, unit, 0);
5985*0Sstevel@tonic-gate 		drv_usecwait(5);
5986*0Sstevel@tonic-gate 
5987*0Sstevel@tonic-gate 		if ((fdc->c_fdtype & FDCTYPE_AUXIOMASK) == FDCTYPE_SLAVIO) {
5988*0Sstevel@tonic-gate 			Set_dor(fdc, MEDIUM_DENSITY, is_medium);
5989*0Sstevel@tonic-gate 		}
5990*0Sstevel@tonic-gate 
5991*0Sstevel@tonic-gate 		if ((fdc->c_fdtype & FDCTYPE_AUXIOMASK) == FDCTYPE_CHEERIO) {
5992*0Sstevel@tonic-gate 			if (is_medium) {
5993*0Sstevel@tonic-gate 				Set_auxio(fdc, AUX_MEDIUM_DENSITY);
5994*0Sstevel@tonic-gate 			} else {
5995*0Sstevel@tonic-gate 				Set_auxio(fdc, AUX_HIGH_DENSITY);
5996*0Sstevel@tonic-gate 			}
5997*0Sstevel@tonic-gate 
5998*0Sstevel@tonic-gate 		}
5999*0Sstevel@tonic-gate 
6000*0Sstevel@tonic-gate 		if (is_medium) {
6001*0Sstevel@tonic-gate 			drv_usecwait(5);
6002*0Sstevel@tonic-gate 		}
6003*0Sstevel@tonic-gate 
6004*0Sstevel@tonic-gate 		fdselect(fdc, unit, 1);	/* Sony requirement */
6005*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "rotation:medium\n"));
6006*0Sstevel@tonic-gate 		drv_usecwait(500000);
6007*0Sstevel@tonic-gate 	}
6008*0Sstevel@tonic-gate }
6009*0Sstevel@tonic-gate 
6010*0Sstevel@tonic-gate static void
6011*0Sstevel@tonic-gate fd_media_watch(void *arg)
6012*0Sstevel@tonic-gate {
6013*0Sstevel@tonic-gate 	dev_t		dev;
6014*0Sstevel@tonic-gate 	struct fdunit *un;
6015*0Sstevel@tonic-gate 	struct fdctlr *fdc;
6016*0Sstevel@tonic-gate 	int		unit;
6017*0Sstevel@tonic-gate 
6018*0Sstevel@tonic-gate 	dev = (dev_t)arg;
6019*0Sstevel@tonic-gate 	fdc = fd_getctlr(dev);
6020*0Sstevel@tonic-gate 	unit = fdc->c_un->un_unit_no;
6021*0Sstevel@tonic-gate 	un = fdc->c_un;
6022*0Sstevel@tonic-gate 
6023*0Sstevel@tonic-gate 	mutex_enter(&fdc->c_lolock);
6024*0Sstevel@tonic-gate 
6025*0Sstevel@tonic-gate 	if (un->un_media_timeout_id == 0) {
6026*0Sstevel@tonic-gate 		/*
6027*0Sstevel@tonic-gate 		 * Untimeout is about to be called.
6028*0Sstevel@tonic-gate 		 * Don't call fd_get_media_state again
6029*0Sstevel@tonic-gate 		 */
6030*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
6031*0Sstevel@tonic-gate 		return;
6032*0Sstevel@tonic-gate 	}
6033*0Sstevel@tonic-gate 
6034*0Sstevel@tonic-gate 
6035*0Sstevel@tonic-gate 	un->un_media_state = fd_get_media_state(fdc, unit);
6036*0Sstevel@tonic-gate 	cv_broadcast(&fdc->c_statecv);
6037*0Sstevel@tonic-gate 
6038*0Sstevel@tonic-gate 	mutex_exit(&fdc->c_lolock);
6039*0Sstevel@tonic-gate 
6040*0Sstevel@tonic-gate 	if (un->un_media_timeout) {
6041*0Sstevel@tonic-gate 		un->un_media_timeout_id = timeout(fd_media_watch,
6042*0Sstevel@tonic-gate 			(void *)(ulong_t)dev, un->un_media_timeout);
6043*0Sstevel@tonic-gate 	}
6044*0Sstevel@tonic-gate }
6045*0Sstevel@tonic-gate 
6046*0Sstevel@tonic-gate enum dkio_state
6047*0Sstevel@tonic-gate fd_get_media_state(struct fdctlr *fdc, int unit)
6048*0Sstevel@tonic-gate {
6049*0Sstevel@tonic-gate 	enum dkio_state state;
6050*0Sstevel@tonic-gate 
6051*0Sstevel@tonic-gate 	ASSERT(fdc->c_un->un_unit_no == unit);
6052*0Sstevel@tonic-gate 
6053*0Sstevel@tonic-gate 	if (fdsense_chng(fdc, unit)) {
6054*0Sstevel@tonic-gate 		/* check disk only if DSKCHG "high" */
6055*0Sstevel@tonic-gate 		if (fdcheckdisk(fdc, unit)) {
6056*0Sstevel@tonic-gate 			state = DKIO_EJECTED;
6057*0Sstevel@tonic-gate 		} else {
6058*0Sstevel@tonic-gate 			state = DKIO_INSERTED;
6059*0Sstevel@tonic-gate 		}
6060*0Sstevel@tonic-gate 	} else {
6061*0Sstevel@tonic-gate 		state = DKIO_INSERTED;
6062*0Sstevel@tonic-gate 	}
6063*0Sstevel@tonic-gate 	return (state);
6064*0Sstevel@tonic-gate }
6065*0Sstevel@tonic-gate 
6066*0Sstevel@tonic-gate static int
6067*0Sstevel@tonic-gate fd_check_media(dev_t dev, enum dkio_state state)
6068*0Sstevel@tonic-gate {
6069*0Sstevel@tonic-gate 	struct fdunit *un;
6070*0Sstevel@tonic-gate 	struct fdctlr *fdc;
6071*0Sstevel@tonic-gate 	int		unit;
6072*0Sstevel@tonic-gate 
6073*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RW, (C, "fd_check_media: start\n"));
6074*0Sstevel@tonic-gate 
6075*0Sstevel@tonic-gate 	fdc = fd_getctlr(dev);
6076*0Sstevel@tonic-gate 	unit = fdc->c_un->un_unit_no;
6077*0Sstevel@tonic-gate 	un = fdc->c_un;
6078*0Sstevel@tonic-gate 
6079*0Sstevel@tonic-gate 	mutex_enter(&fdc->c_lolock);
6080*0Sstevel@tonic-gate 
6081*0Sstevel@tonic-gate 	CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc);
6082*0Sstevel@tonic-gate 
6083*0Sstevel@tonic-gate 	if (fdc->c_un->un_state == FD_STATE_STOPPED) {
6084*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
6085*0Sstevel@tonic-gate 		if ((pm_raise_power(fdc->c_dip, 0, PM_LEVEL_ON))
6086*0Sstevel@tonic-gate 					!= DDI_SUCCESS) {
6087*0Sstevel@tonic-gate 			FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "Power change \
6088*0Sstevel@tonic-gate failed. \n"));
6089*0Sstevel@tonic-gate 
6090*0Sstevel@tonic-gate 			(void) pm_idle_component(fdc->c_dip, 0);
6091*0Sstevel@tonic-gate 			return (EIO);
6092*0Sstevel@tonic-gate 		}
6093*0Sstevel@tonic-gate 
6094*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_lolock);
6095*0Sstevel@tonic-gate 	}
6096*0Sstevel@tonic-gate 
6097*0Sstevel@tonic-gate 	un->un_media_state = fd_get_media_state(fdc, unit);
6098*0Sstevel@tonic-gate 
6099*0Sstevel@tonic-gate 	/* turn on timeout */
6100*0Sstevel@tonic-gate 	un->un_media_timeout = drv_usectohz(fd_check_media_time);
6101*0Sstevel@tonic-gate 	un->un_media_timeout_id = timeout(fd_media_watch,
6102*0Sstevel@tonic-gate 			(void *)(ulong_t)dev, un->un_media_timeout);
6103*0Sstevel@tonic-gate 
6104*0Sstevel@tonic-gate 	while (un->un_media_state == state) {
6105*0Sstevel@tonic-gate 		if (cv_wait_sig(&fdc->c_statecv, &fdc->c_lolock) == 0) {
6106*0Sstevel@tonic-gate 			un->un_media_timeout = 0;
6107*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_lolock);
6108*0Sstevel@tonic-gate 			return (EINTR);
6109*0Sstevel@tonic-gate 		}
6110*0Sstevel@tonic-gate 	}
6111*0Sstevel@tonic-gate 
6112*0Sstevel@tonic-gate 	if (un->un_media_timeout_id) {
6113*0Sstevel@tonic-gate 		timeout_id_t timeid = un->un_media_timeout_id;
6114*0Sstevel@tonic-gate 		un->un_media_timeout_id = 0;
6115*0Sstevel@tonic-gate 
6116*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
6117*0Sstevel@tonic-gate 		(void) untimeout(timeid);
6118*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_lolock);
6119*0Sstevel@tonic-gate 	}
6120*0Sstevel@tonic-gate 
6121*0Sstevel@tonic-gate 	if (un->un_media_state == DKIO_INSERTED) {
6122*0Sstevel@tonic-gate 		if (fdgetlabel(fdc, unit)) {
6123*0Sstevel@tonic-gate 			mutex_exit(&fdc->c_lolock);
6124*0Sstevel@tonic-gate 			return (EIO);
6125*0Sstevel@tonic-gate 		}
6126*0Sstevel@tonic-gate 	}
6127*0Sstevel@tonic-gate 	mutex_exit(&fdc->c_lolock);
6128*0Sstevel@tonic-gate 
6129*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_RW, (C, "fd_check_media: end\n"));
6130*0Sstevel@tonic-gate 	return (0);
6131*0Sstevel@tonic-gate }
6132*0Sstevel@tonic-gate 
6133*0Sstevel@tonic-gate /*
6134*0Sstevel@tonic-gate  * fd_get_media_info :
6135*0Sstevel@tonic-gate  * 	Collects medium information for
6136*0Sstevel@tonic-gate  *	DKIOCGMEDIAINFO ioctl.
6137*0Sstevel@tonic-gate  */
6138*0Sstevel@tonic-gate 
6139*0Sstevel@tonic-gate static int
6140*0Sstevel@tonic-gate fd_get_media_info(struct fdunit *un, caddr_t buf, int flag)
6141*0Sstevel@tonic-gate {
6142*0Sstevel@tonic-gate 	struct dk_minfo media_info;
6143*0Sstevel@tonic-gate 	int err = 0;
6144*0Sstevel@tonic-gate 
6145*0Sstevel@tonic-gate 	media_info.dki_media_type = DK_FLOPPY;
6146*0Sstevel@tonic-gate 	media_info.dki_lbsize = un->un_chars->fdc_sec_size;
6147*0Sstevel@tonic-gate 	media_info.dki_capacity = un->un_chars->fdc_ncyl *
6148*0Sstevel@tonic-gate 		un->un_chars->fdc_secptrack * un->un_chars->fdc_nhead;
6149*0Sstevel@tonic-gate 
6150*0Sstevel@tonic-gate 	if (ddi_copyout((caddr_t)&media_info, buf,
6151*0Sstevel@tonic-gate 					sizeof (struct dk_minfo), flag))
6152*0Sstevel@tonic-gate 			err = EFAULT;
6153*0Sstevel@tonic-gate 	return (err);
6154*0Sstevel@tonic-gate }
6155*0Sstevel@tonic-gate 
6156*0Sstevel@tonic-gate /*
6157*0Sstevel@tonic-gate  * fd_power :
6158*0Sstevel@tonic-gate  *	Power entry point of fd driver.
6159*0Sstevel@tonic-gate  */
6160*0Sstevel@tonic-gate 
6161*0Sstevel@tonic-gate static int
6162*0Sstevel@tonic-gate fd_power(dev_info_t *dip, int component, int level)
6163*0Sstevel@tonic-gate {
6164*0Sstevel@tonic-gate 
6165*0Sstevel@tonic-gate 	struct fdctlr *fdc;
6166*0Sstevel@tonic-gate 	int instance;
6167*0Sstevel@tonic-gate 	int rval;
6168*0Sstevel@tonic-gate 
6169*0Sstevel@tonic-gate 	if ((level < PM_LEVEL_OFF) || (level > PM_LEVEL_ON) ||
6170*0Sstevel@tonic-gate 						(component != 0)) {
6171*0Sstevel@tonic-gate 		return (DDI_FAILURE);
6172*0Sstevel@tonic-gate 	}
6173*0Sstevel@tonic-gate 
6174*0Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
6175*0Sstevel@tonic-gate 	fdc = fd_getctlr(instance << FDINSTSHIFT);
6176*0Sstevel@tonic-gate 	if (fdc->c_un == NULL)
6177*0Sstevel@tonic-gate 		return (DDI_FAILURE);
6178*0Sstevel@tonic-gate 
6179*0Sstevel@tonic-gate 	if (level == PM_LEVEL_OFF) {
6180*0Sstevel@tonic-gate 		rval = fd_pm_lower_power(fdc);
6181*0Sstevel@tonic-gate 	}
6182*0Sstevel@tonic-gate 	if (level == PM_LEVEL_ON) {
6183*0Sstevel@tonic-gate 		rval = fd_pm_raise_power(fdc);
6184*0Sstevel@tonic-gate 	}
6185*0Sstevel@tonic-gate 	return (rval);
6186*0Sstevel@tonic-gate }
6187*0Sstevel@tonic-gate 
6188*0Sstevel@tonic-gate /*
6189*0Sstevel@tonic-gate  * fd_pm_lower_power :
6190*0Sstevel@tonic-gate  *	This function is called only during pm suspend. At this point,
6191*0Sstevel@tonic-gate  *	the power management framework thinks the device is idle for
6192*0Sstevel@tonic-gate  *	long enough to go to a low power mode. If the device is busy,
6193*0Sstevel@tonic-gate  *	then this function returns DDI_FAILURE.
6194*0Sstevel@tonic-gate  */
6195*0Sstevel@tonic-gate 
6196*0Sstevel@tonic-gate static int
6197*0Sstevel@tonic-gate fd_pm_lower_power(struct fdctlr *fdc)
6198*0Sstevel@tonic-gate {
6199*0Sstevel@tonic-gate 
6200*0Sstevel@tonic-gate 	mutex_enter(&fdc->c_lolock);
6201*0Sstevel@tonic-gate 
6202*0Sstevel@tonic-gate 	if ((fdc->c_un->un_state == FD_STATE_SUSPENDED) ||
6203*0Sstevel@tonic-gate 			(fdc->c_un->un_state == FD_STATE_STOPPED)) {
6204*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
6205*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
6206*0Sstevel@tonic-gate 	}
6207*0Sstevel@tonic-gate 
6208*0Sstevel@tonic-gate 
6209*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "fd_pm_lower_power called\n"));
6210*0Sstevel@tonic-gate 
6211*0Sstevel@tonic-gate 	/* if the device is busy then we fail the lower power request */
6212*0Sstevel@tonic-gate 	if (fdc->c_flags & FDCFLG_BUSY) {
6213*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L2, FDEM_PWR, (C, "fd_pm_lower_power : \
6214*0Sstevel@tonic-gate controller is busy.\n"));
6215*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
6216*0Sstevel@tonic-gate 		return (DDI_FAILURE);
6217*0Sstevel@tonic-gate 	}
6218*0Sstevel@tonic-gate 
6219*0Sstevel@tonic-gate 	fdc->c_un->un_state = FD_STATE_STOPPED;
6220*0Sstevel@tonic-gate 
6221*0Sstevel@tonic-gate 	mutex_exit(&fdc->c_lolock);
6222*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
6223*0Sstevel@tonic-gate }
6224*0Sstevel@tonic-gate 
6225*0Sstevel@tonic-gate /*
6226*0Sstevel@tonic-gate  * fd_pm_raise_power :
6227*0Sstevel@tonic-gate  *	This function performs the necessary steps for resuming a
6228*0Sstevel@tonic-gate  *	device, either from pm suspend or CPR. Here the controller
6229*0Sstevel@tonic-gate  *	is reset, initialized and the state is set to FD_STATE_NORMAL.
6230*0Sstevel@tonic-gate  */
6231*0Sstevel@tonic-gate 
6232*0Sstevel@tonic-gate static int
6233*0Sstevel@tonic-gate fd_pm_raise_power(struct fdctlr *fdc)
6234*0Sstevel@tonic-gate {
6235*0Sstevel@tonic-gate 
6236*0Sstevel@tonic-gate 	struct fdunit *un = fdc->c_un;
6237*0Sstevel@tonic-gate 	int unit;
6238*0Sstevel@tonic-gate 
6239*0Sstevel@tonic-gate 	FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "fd_pm_raise_power called\n"));
6240*0Sstevel@tonic-gate 	mutex_enter(&fdc->c_lolock);
6241*0Sstevel@tonic-gate 	fdgetcsb(fdc);
6242*0Sstevel@tonic-gate 
6243*0Sstevel@tonic-gate 	/* Reset the dma engine */
6244*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_DMA) {
6245*0Sstevel@tonic-gate 		mutex_enter(&fdc->c_hilock);
6246*0Sstevel@tonic-gate 		reset_dma_controller(fdc);
6247*0Sstevel@tonic-gate 		set_dma_control_register(fdc, DCSR_INIT_BITS);
6248*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_hilock);
6249*0Sstevel@tonic-gate 	}
6250*0Sstevel@tonic-gate 
6251*0Sstevel@tonic-gate 	/*
6252*0Sstevel@tonic-gate 	 * Force a rotational speed set in the next
6253*0Sstevel@tonic-gate 	 * call to set_rotational_speed().
6254*0Sstevel@tonic-gate 	 */
6255*0Sstevel@tonic-gate 
6256*0Sstevel@tonic-gate 	fdc->c_un->un_flags |= FDUNIT_SET_SPEED;
6257*0Sstevel@tonic-gate 
6258*0Sstevel@tonic-gate 	/* Reset and configure the controller */
6259*0Sstevel@tonic-gate 	(void) fdreset(fdc);
6260*0Sstevel@tonic-gate 
6261*0Sstevel@tonic-gate 	unit = fdc->c_un->un_unit_no;
6262*0Sstevel@tonic-gate 
6263*0Sstevel@tonic-gate 	/* Recalibrate the drive */
6264*0Sstevel@tonic-gate 	if (fdrecalseek(fdc, unit, -1, 0) != 0) {
6265*0Sstevel@tonic-gate 		FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "raise_power : recalibrate \
6266*0Sstevel@tonic-gate failed\n"));
6267*0Sstevel@tonic-gate 		fdretcsb(fdc);
6268*0Sstevel@tonic-gate 		mutex_exit(&fdc->c_lolock);
6269*0Sstevel@tonic-gate 		return (DDI_FAILURE);
6270*0Sstevel@tonic-gate 	}
6271*0Sstevel@tonic-gate 
6272*0Sstevel@tonic-gate 	/* Select the drive through the AUXIO registers */
6273*0Sstevel@tonic-gate 	fdselect(fdc, unit, 0);
6274*0Sstevel@tonic-gate 	un->un_state = FD_STATE_NORMAL;
6275*0Sstevel@tonic-gate 	fdretcsb(fdc);
6276*0Sstevel@tonic-gate 	mutex_exit(&fdc->c_lolock);
6277*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
6278*0Sstevel@tonic-gate }
6279*0Sstevel@tonic-gate 
6280*0Sstevel@tonic-gate /*
6281*0Sstevel@tonic-gate  * create_pm_components :
6282*0Sstevel@tonic-gate  *	creates the power management components for auto pm framework.
6283*0Sstevel@tonic-gate  */
6284*0Sstevel@tonic-gate 
6285*0Sstevel@tonic-gate static void
6286*0Sstevel@tonic-gate create_pm_components(dev_info_t *dip)
6287*0Sstevel@tonic-gate {
6288*0Sstevel@tonic-gate 	char	*un_pm_comp[] = { "NAME=spindle-motor", "0=off", "1=on"};
6289*0Sstevel@tonic-gate 
6290*0Sstevel@tonic-gate 	if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
6291*0Sstevel@tonic-gate 		"pm-components", un_pm_comp, 3) == DDI_PROP_SUCCESS) {
6292*0Sstevel@tonic-gate 
6293*0Sstevel@tonic-gate 		(void) pm_raise_power(dip, 0, PM_LEVEL_ON);
6294*0Sstevel@tonic-gate 	}
6295*0Sstevel@tonic-gate }
6296*0Sstevel@tonic-gate 
6297*0Sstevel@tonic-gate /*
6298*0Sstevel@tonic-gate  * set_data_count_register(struct fdctlr *fdc, uint32_t count)
6299*0Sstevel@tonic-gate  * 	Set the data count in appropriate dma register.
6300*0Sstevel@tonic-gate  */
6301*0Sstevel@tonic-gate 
6302*0Sstevel@tonic-gate static void
6303*0Sstevel@tonic-gate set_data_count_register(struct fdctlr *fdc, uint32_t count)
6304*0Sstevel@tonic-gate {
6305*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
6306*0Sstevel@tonic-gate 		struct cheerio_dma_reg *dma_reg;
6307*0Sstevel@tonic-gate 		dma_reg = (struct cheerio_dma_reg *)fdc->c_dma_regs;
6308*0Sstevel@tonic-gate 		ddi_put32(fdc->c_handlep_dma, &dma_reg->fdc_dbcr, count);
6309*0Sstevel@tonic-gate 	} else if (fdc->c_fdtype & FDCTYPE_SB) {
6310*0Sstevel@tonic-gate 		struct sb_dma_reg *dma_reg;
6311*0Sstevel@tonic-gate 		count = count - 1; /* 8237 needs it */
6312*0Sstevel@tonic-gate 		dma_reg = (struct sb_dma_reg *)fdc->c_dma_regs;
6313*0Sstevel@tonic-gate 		switch (fdc->sb_dma_channel) {
6314*0Sstevel@tonic-gate 			case 0 :
6315*0Sstevel@tonic-gate 				ddi_put16(fdc->c_handlep_dma,
6316*0Sstevel@tonic-gate 				(ushort_t *)&dma_reg->sb_dma_regs[DMA_0WCNT],
6317*0Sstevel@tonic-gate 				count & 0xFFFF);
6318*0Sstevel@tonic-gate 				break;
6319*0Sstevel@tonic-gate 			case 1 :
6320*0Sstevel@tonic-gate 				ddi_put16(fdc->c_handlep_dma,
6321*0Sstevel@tonic-gate 				(ushort_t *)&dma_reg->sb_dma_regs[DMA_1WCNT],
6322*0Sstevel@tonic-gate 				count & 0xFFFF);
6323*0Sstevel@tonic-gate 				break;
6324*0Sstevel@tonic-gate 			case 2 :
6325*0Sstevel@tonic-gate 				ddi_put16(fdc->c_handlep_dma,
6326*0Sstevel@tonic-gate 				(ushort_t *)&dma_reg->sb_dma_regs[DMA_2WCNT],
6327*0Sstevel@tonic-gate 				count & 0xFFFF);
6328*0Sstevel@tonic-gate 				break;
6329*0Sstevel@tonic-gate 			case 3 :
6330*0Sstevel@tonic-gate 				ddi_put16(fdc->c_handlep_dma,
6331*0Sstevel@tonic-gate 				(ushort_t *)&dma_reg->sb_dma_regs[DMA_3WCNT],
6332*0Sstevel@tonic-gate 				count & 0xFFFF);
6333*0Sstevel@tonic-gate 				break;
6334*0Sstevel@tonic-gate 			default :
6335*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L3, FDEM_SDMA,
6336*0Sstevel@tonic-gate 				(C, "set_data_count: wrong channel %x\n",
6337*0Sstevel@tonic-gate 				fdc->sb_dma_channel));
6338*0Sstevel@tonic-gate 				break;
6339*0Sstevel@tonic-gate 		}
6340*0Sstevel@tonic-gate 	}
6341*0Sstevel@tonic-gate }
6342*0Sstevel@tonic-gate 
6343*0Sstevel@tonic-gate /*
6344*0Sstevel@tonic-gate  * get_data_count_register(struct fdctlr *fdc)
6345*0Sstevel@tonic-gate  * 	Read the data count from appropriate dma register.
6346*0Sstevel@tonic-gate  */
6347*0Sstevel@tonic-gate 
6348*0Sstevel@tonic-gate static uint32_t
6349*0Sstevel@tonic-gate get_data_count_register(struct fdctlr *fdc)
6350*0Sstevel@tonic-gate {
6351*0Sstevel@tonic-gate 	uint32_t retval = 0;
6352*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
6353*0Sstevel@tonic-gate 		struct cheerio_dma_reg *dma_reg;
6354*0Sstevel@tonic-gate 		dma_reg = (struct cheerio_dma_reg *)fdc->c_dma_regs;
6355*0Sstevel@tonic-gate 		retval = ddi_get32(fdc->c_handlep_dma, &dma_reg->fdc_dbcr);
6356*0Sstevel@tonic-gate 	} else if (fdc->c_fdtype & FDCTYPE_SB) {
6357*0Sstevel@tonic-gate 		struct sb_dma_reg *dma_reg;
6358*0Sstevel@tonic-gate 		dma_reg = (struct sb_dma_reg *)fdc->c_dma_regs;
6359*0Sstevel@tonic-gate 		switch (fdc->sb_dma_channel) {
6360*0Sstevel@tonic-gate 			case 0 :
6361*0Sstevel@tonic-gate 				retval = ddi_get16(fdc->c_handlep_dma,
6362*0Sstevel@tonic-gate 				(ushort_t *)&dma_reg->sb_dma_regs[DMA_0WCNT]);
6363*0Sstevel@tonic-gate 				break;
6364*0Sstevel@tonic-gate 			case 1 :
6365*0Sstevel@tonic-gate 				retval = ddi_get16(fdc->c_handlep_dma,
6366*0Sstevel@tonic-gate 				(ushort_t *)&dma_reg->sb_dma_regs[DMA_1WCNT]);
6367*0Sstevel@tonic-gate 				break;
6368*0Sstevel@tonic-gate 			case 2 :
6369*0Sstevel@tonic-gate 				retval = ddi_get16(fdc->c_handlep_dma,
6370*0Sstevel@tonic-gate 				(ushort_t *)&dma_reg->sb_dma_regs[DMA_2WCNT]);
6371*0Sstevel@tonic-gate 				break;
6372*0Sstevel@tonic-gate 			case 3 :
6373*0Sstevel@tonic-gate 				retval = ddi_get16(fdc->c_handlep_dma,
6374*0Sstevel@tonic-gate 				(ushort_t *)&dma_reg->sb_dma_regs[DMA_3WCNT]);
6375*0Sstevel@tonic-gate 				break;
6376*0Sstevel@tonic-gate 			default :
6377*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L3, FDEM_SDMA,
6378*0Sstevel@tonic-gate 				(C, "get_data_count: wrong channel %x\n",
6379*0Sstevel@tonic-gate 				fdc->sb_dma_channel));
6380*0Sstevel@tonic-gate 				break;
6381*0Sstevel@tonic-gate 		}
6382*0Sstevel@tonic-gate 		retval = (uint32_t)((uint16_t)(retval +1));
6383*0Sstevel@tonic-gate 	}
6384*0Sstevel@tonic-gate 
6385*0Sstevel@tonic-gate 	return (retval);
6386*0Sstevel@tonic-gate 
6387*0Sstevel@tonic-gate }
6388*0Sstevel@tonic-gate 
6389*0Sstevel@tonic-gate /*
6390*0Sstevel@tonic-gate  * reset_dma_controller(struct fdctlr *fdc)
6391*0Sstevel@tonic-gate  * 	Reset and initialize the dma controller.
6392*0Sstevel@tonic-gate  */
6393*0Sstevel@tonic-gate 
6394*0Sstevel@tonic-gate static void
6395*0Sstevel@tonic-gate reset_dma_controller(struct fdctlr *fdc)
6396*0Sstevel@tonic-gate {
6397*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
6398*0Sstevel@tonic-gate 		struct cheerio_dma_reg *dma_reg;
6399*0Sstevel@tonic-gate 		dma_reg = (struct cheerio_dma_reg *)fdc->c_dma_regs;
6400*0Sstevel@tonic-gate 		ddi_put32(fdc->c_handlep_dma, &dma_reg->fdc_dcsr, DCSR_RESET);
6401*0Sstevel@tonic-gate 		while (get_dma_control_register(fdc) & DCSR_CYC_PEND);
6402*0Sstevel@tonic-gate 		ddi_put32(fdc->c_handlep_dma, &dma_reg->fdc_dcsr, 0);
6403*0Sstevel@tonic-gate 	} else if (fdc->c_fdtype & FDCTYPE_SB) {
6404*0Sstevel@tonic-gate 		struct sb_dma_reg *dma_reg;
6405*0Sstevel@tonic-gate 		dma_reg = (struct sb_dma_reg *)fdc->c_dma_regs;
6406*0Sstevel@tonic-gate 		ddi_put8(fdc->c_handlep_dma, &dma_reg->sb_dma_regs[DMAC1_MASK],
6407*0Sstevel@tonic-gate 		    (fdc->sb_dma_channel & 0x3));
6408*0Sstevel@tonic-gate 
6409*0Sstevel@tonic-gate 	}
6410*0Sstevel@tonic-gate }
6411*0Sstevel@tonic-gate 
6412*0Sstevel@tonic-gate /*
6413*0Sstevel@tonic-gate  * Get the DMA control register for CHEERIO.
6414*0Sstevel@tonic-gate  * For SouthBridge 8237 DMA controller, this register is not valid.
6415*0Sstevel@tonic-gate  * So, just return 0.
6416*0Sstevel@tonic-gate  */
6417*0Sstevel@tonic-gate static uint32_t
6418*0Sstevel@tonic-gate get_dma_control_register(struct fdctlr *fdc)
6419*0Sstevel@tonic-gate {
6420*0Sstevel@tonic-gate 	uint32_t retval = 0;
6421*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
6422*0Sstevel@tonic-gate 		struct cheerio_dma_reg *dma_reg;
6423*0Sstevel@tonic-gate 		dma_reg = (struct cheerio_dma_reg *)fdc->c_dma_regs;
6424*0Sstevel@tonic-gate 		retval = ddi_get32(fdc->c_handlep_dma, &dma_reg->fdc_dcsr);
6425*0Sstevel@tonic-gate 	}
6426*0Sstevel@tonic-gate 
6427*0Sstevel@tonic-gate 	return (retval);
6428*0Sstevel@tonic-gate }
6429*0Sstevel@tonic-gate 
6430*0Sstevel@tonic-gate 
6431*0Sstevel@tonic-gate /*
6432*0Sstevel@tonic-gate  * set_data_address_register(struct fdctlr *fdc)
6433*0Sstevel@tonic-gate  * 	Set the data address in appropriate dma register.
6434*0Sstevel@tonic-gate  */
6435*0Sstevel@tonic-gate static void
6436*0Sstevel@tonic-gate set_data_address_register(struct fdctlr *fdc, uint32_t address)
6437*0Sstevel@tonic-gate {
6438*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
6439*0Sstevel@tonic-gate 		struct cheerio_dma_reg *dma_reg;
6440*0Sstevel@tonic-gate 		dma_reg = (struct cheerio_dma_reg *)fdc->c_dma_regs;
6441*0Sstevel@tonic-gate 		ddi_put32(fdc->c_handlep_dma, &dma_reg->fdc_dacr, address);
6442*0Sstevel@tonic-gate 	} else if (fdc->c_fdtype & FDCTYPE_SB) {
6443*0Sstevel@tonic-gate 		struct sb_dma_reg *dma_reg;
6444*0Sstevel@tonic-gate 		dma_reg = (struct sb_dma_reg *)fdc->c_dma_regs;
6445*0Sstevel@tonic-gate 		switch (fdc->sb_dma_channel) {
6446*0Sstevel@tonic-gate 			case 0 :
6447*0Sstevel@tonic-gate 				ddi_put8(fdc->c_handlep_dma,
6448*0Sstevel@tonic-gate 				    &dma_reg->sb_dma_regs[DMA_0PAGE],
6449*0Sstevel@tonic-gate 				    (address & 0xFF0000) >>16);
6450*0Sstevel@tonic-gate 				ddi_put8(fdc->c_handlep_dma,
6451*0Sstevel@tonic-gate 				    &dma_reg->sb_dma_regs[DMA_0HPG],
6452*0Sstevel@tonic-gate 				    (address & 0xFF000000) >>24);
6453*0Sstevel@tonic-gate 				ddi_put16(fdc->c_handlep_dma,
6454*0Sstevel@tonic-gate 				    (ushort_t *)&dma_reg->sb_dma_regs[DMA_0ADR],
6455*0Sstevel@tonic-gate 				    address & 0xFFFF);
6456*0Sstevel@tonic-gate 				break;
6457*0Sstevel@tonic-gate 			case 1 :
6458*0Sstevel@tonic-gate 				ddi_put8(fdc->c_handlep_dma,
6459*0Sstevel@tonic-gate 				    &dma_reg->sb_dma_regs[DMA_1PAGE],
6460*0Sstevel@tonic-gate 				    (address & 0xFF0000) >>16);
6461*0Sstevel@tonic-gate 				ddi_put8(fdc->c_handlep_dma,
6462*0Sstevel@tonic-gate 				    &dma_reg->sb_dma_regs[DMA_1HPG],
6463*0Sstevel@tonic-gate 				    (address & 0xFF000000) >>24);
6464*0Sstevel@tonic-gate 				ddi_put16(fdc->c_handlep_dma,
6465*0Sstevel@tonic-gate 				    (ushort_t *)&dma_reg->sb_dma_regs[DMA_1ADR],
6466*0Sstevel@tonic-gate 				    address & 0xFFFF);
6467*0Sstevel@tonic-gate 				break;
6468*0Sstevel@tonic-gate 			case 2 :
6469*0Sstevel@tonic-gate 				ddi_put8(fdc->c_handlep_dma,
6470*0Sstevel@tonic-gate 				    &dma_reg->sb_dma_regs[DMA_2PAGE],
6471*0Sstevel@tonic-gate 				    (address & 0xFF0000) >>16);
6472*0Sstevel@tonic-gate 				ddi_put8(fdc->c_handlep_dma,
6473*0Sstevel@tonic-gate 				    &dma_reg->sb_dma_regs[DMA_2HPG],
6474*0Sstevel@tonic-gate 				    (address & 0xFF000000) >>24);
6475*0Sstevel@tonic-gate 				ddi_put16(fdc->c_handlep_dma,
6476*0Sstevel@tonic-gate 				    (ushort_t *)&dma_reg->sb_dma_regs[DMA_2ADR],
6477*0Sstevel@tonic-gate 				    address & 0xFFFF);
6478*0Sstevel@tonic-gate 				break;
6479*0Sstevel@tonic-gate 			case 3 :
6480*0Sstevel@tonic-gate 				ddi_put8(fdc->c_handlep_dma,
6481*0Sstevel@tonic-gate 				    &dma_reg->sb_dma_regs[DMA_3PAGE],
6482*0Sstevel@tonic-gate 				    (address & 0xFF0000) >>16);
6483*0Sstevel@tonic-gate 				ddi_put8(fdc->c_handlep_dma,
6484*0Sstevel@tonic-gate 				    &dma_reg->sb_dma_regs[DMA_3HPG],
6485*0Sstevel@tonic-gate 				    (address & 0xFF000000) >>24);
6486*0Sstevel@tonic-gate 				ddi_put16(fdc->c_handlep_dma,
6487*0Sstevel@tonic-gate 				    (ushort_t *)&dma_reg->sb_dma_regs[DMA_3ADR],
6488*0Sstevel@tonic-gate 				    address & 0xFFFF);
6489*0Sstevel@tonic-gate 				break;
6490*0Sstevel@tonic-gate 			default :
6491*0Sstevel@tonic-gate 				FDERRPRINT(FDEP_L3, FDEM_SDMA,
6492*0Sstevel@tonic-gate 				(C, "set_data_address: wrong channel %x\n",
6493*0Sstevel@tonic-gate 				fdc->sb_dma_channel));
6494*0Sstevel@tonic-gate 			break;
6495*0Sstevel@tonic-gate 		}
6496*0Sstevel@tonic-gate 	}
6497*0Sstevel@tonic-gate 
6498*0Sstevel@tonic-gate }
6499*0Sstevel@tonic-gate 
6500*0Sstevel@tonic-gate 
6501*0Sstevel@tonic-gate /*
6502*0Sstevel@tonic-gate  * set_dma_mode(struct fdctlr *fdc, int val)
6503*0Sstevel@tonic-gate  * 	Set the appropriate dma direction and registers.
6504*0Sstevel@tonic-gate  */
6505*0Sstevel@tonic-gate static void
6506*0Sstevel@tonic-gate set_dma_mode(struct fdctlr *fdc, int val)
6507*0Sstevel@tonic-gate {
6508*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
6509*0Sstevel@tonic-gate 		struct cheerio_dma_reg *dma_reg;
6510*0Sstevel@tonic-gate 		dma_reg = (struct cheerio_dma_reg *)fdc->c_dma_regs;
6511*0Sstevel@tonic-gate 		if (val == CSB_READ)
6512*0Sstevel@tonic-gate 			ddi_put32(fdc->c_handlep_dma, &dma_reg->fdc_dcsr,
6513*0Sstevel@tonic-gate 				DCSR_INIT_BITS|DCSR_WRITE);
6514*0Sstevel@tonic-gate 		else
6515*0Sstevel@tonic-gate 			ddi_put32(fdc->c_handlep_dma, &dma_reg->fdc_dcsr,
6516*0Sstevel@tonic-gate 				DCSR_INIT_BITS);
6517*0Sstevel@tonic-gate 
6518*0Sstevel@tonic-gate 	} else if (fdc->c_fdtype & FDCTYPE_SB) {
6519*0Sstevel@tonic-gate 		uint8_t mode_reg_val, chn_mask;
6520*0Sstevel@tonic-gate 		struct sb_dma_reg *dma_reg;
6521*0Sstevel@tonic-gate 		dma_reg = (struct sb_dma_reg *)fdc->c_dma_regs;
6522*0Sstevel@tonic-gate 
6523*0Sstevel@tonic-gate 		if (val == CSB_READ) {
6524*0Sstevel@tonic-gate 			mode_reg_val = fdc->sb_dma_channel | DMAMODE_READ
6525*0Sstevel@tonic-gate 					| DMAMODE_SINGLE;
6526*0Sstevel@tonic-gate 		} else { /* Read operation */
6527*0Sstevel@tonic-gate 			mode_reg_val = fdc->sb_dma_channel | DMAMODE_WRITE
6528*0Sstevel@tonic-gate 					| DMAMODE_SINGLE;
6529*0Sstevel@tonic-gate 		}
6530*0Sstevel@tonic-gate 		ddi_put8(fdc->c_handlep_dma, &dma_reg->sb_dma_regs[DMAC1_MODE],
6531*0Sstevel@tonic-gate 			mode_reg_val);
6532*0Sstevel@tonic-gate 		chn_mask = 1 << (fdc->sb_dma_channel & 0x3);
6533*0Sstevel@tonic-gate 		ddi_put8(fdc->c_handlep_dma,
6534*0Sstevel@tonic-gate 			&dma_reg->sb_dma_regs[DMAC1_ALLMASK], ~chn_mask);
6535*0Sstevel@tonic-gate 		fdc->sb_dma_lock = 1;
6536*0Sstevel@tonic-gate 	}
6537*0Sstevel@tonic-gate }
6538*0Sstevel@tonic-gate 
6539*0Sstevel@tonic-gate /*
6540*0Sstevel@tonic-gate  * This function is valid only for CHEERIO/RIO based
6541*0Sstevel@tonic-gate  * controllers. The control register for the dma channel
6542*0Sstevel@tonic-gate  * is initialized by this function.
6543*0Sstevel@tonic-gate  */
6544*0Sstevel@tonic-gate 
6545*0Sstevel@tonic-gate static void
6546*0Sstevel@tonic-gate set_dma_control_register(struct fdctlr *fdc, uint32_t val)
6547*0Sstevel@tonic-gate {
6548*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
6549*0Sstevel@tonic-gate 		struct cheerio_dma_reg *dma_reg;
6550*0Sstevel@tonic-gate 		dma_reg = (struct cheerio_dma_reg *)fdc->c_dma_regs;
6551*0Sstevel@tonic-gate 		ddi_put32(fdc->c_handlep_dma, &dma_reg->fdc_dcsr, val);
6552*0Sstevel@tonic-gate 	}
6553*0Sstevel@tonic-gate }
6554*0Sstevel@tonic-gate 
6555*0Sstevel@tonic-gate static void
6556*0Sstevel@tonic-gate release_sb_dma(struct fdctlr *fdc)
6557*0Sstevel@tonic-gate {
6558*0Sstevel@tonic-gate 	struct sb_dma_reg *dma_reg;
6559*0Sstevel@tonic-gate 	dma_reg = (struct sb_dma_reg *)fdc->c_dma_regs;
6560*0Sstevel@tonic-gate 	/* Unmask all the channels to release the DMA controller */
6561*0Sstevel@tonic-gate 	ddi_put8(fdc->c_handlep_dma,
6562*0Sstevel@tonic-gate 		&dma_reg->sb_dma_regs[DMAC1_ALLMASK], NULL);
6563*0Sstevel@tonic-gate 	fdc->sb_dma_lock = 0;
6564*0Sstevel@tonic-gate }
6565*0Sstevel@tonic-gate 
6566*0Sstevel@tonic-gate static void
6567*0Sstevel@tonic-gate quiesce_fd_interrupt(struct fdctlr *fdc)
6568*0Sstevel@tonic-gate {
6569*0Sstevel@tonic-gate 	/*
6570*0Sstevel@tonic-gate 	 * The following code is put here to take care of HW problem.
6571*0Sstevel@tonic-gate 	 * The HW problem is as follows:
6572*0Sstevel@tonic-gate 	 *
6573*0Sstevel@tonic-gate 	 *	After poweron the Southbridge floppy controller asserts the
6574*0Sstevel@tonic-gate 	 * interrupt in tristate. This causes continuous interrupts to
6575*0Sstevel@tonic-gate 	 * be generated.
6576*0Sstevel@tonic-gate 	 * Until the Hardware is FIXED we will have to use the following code
6577*0Sstevel@tonic-gate 	 * to set the interrupt line to proper state after poweron.
6578*0Sstevel@tonic-gate 	 */
6579*0Sstevel@tonic-gate 	if (fdc->c_fdtype & FDCTYPE_SB) {
6580*0Sstevel@tonic-gate 		ddi_put8(fdc->c_handlep_cont, ((uint8_t *)fdc->c_dor),
6581*0Sstevel@tonic-gate 			0x0);
6582*0Sstevel@tonic-gate 		drv_usecwait(200);
6583*0Sstevel@tonic-gate 		ddi_put8(fdc->c_handlep_cont, ((uint8_t *)fdc->c_dor),
6584*0Sstevel@tonic-gate 			0xC);
6585*0Sstevel@tonic-gate 		drv_usecwait(200);
6586*0Sstevel@tonic-gate 		Set_Fifo(fdc, 0xE6);
6587*0Sstevel@tonic-gate 		drv_usecwait(200);
6588*0Sstevel@tonic-gate 	}
6589*0Sstevel@tonic-gate }
6590