Commit | Line | Data |
---|---|---|
984263bc MD |
1 | /* |
2 | * Copyright (c) 1990 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Don Ahn. | |
7 | * | |
8 | * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu) | |
9 | * aided by the Linux floppy driver modifications from David Bateman | |
10 | * (dbateman@eng.uts.edu.au). | |
11 | * | |
12 | * Copyright (c) 1993, 1994 by | |
13 | * jc@irbs.UUCP (John Capo) | |
14 | * vak@zebub.msk.su (Serge Vakulenko) | |
15 | * ache@astral.msk.su (Andrew A. Chernov) | |
16 | * | |
17 | * Copyright (c) 1993, 1994, 1995 by | |
18 | * joerg_wunsch@uriah.sax.de (Joerg Wunsch) | |
19 | * dufault@hda.com (Peter Dufault) | |
20 | * | |
21 | * Copyright (c) 2001 Joerg Wunsch, | |
22 | * joerg_wunsch@uriah.sax.de (Joerg Wunsch) | |
23 | * | |
24 | * Redistribution and use in source and binary forms, with or without | |
25 | * modification, are permitted provided that the following conditions | |
26 | * are met: | |
27 | * 1. Redistributions of source code must retain the above copyright | |
28 | * notice, this list of conditions and the following disclaimer. | |
29 | * 2. Redistributions in binary form must reproduce the above copyright | |
30 | * notice, this list of conditions and the following disclaimer in the | |
31 | * documentation and/or other materials provided with the distribution. | |
32 | * 3. All advertising materials mentioning features or use of this software | |
33 | * must display the following acknowledgement: | |
34 | * This product includes software developed by the University of | |
35 | * California, Berkeley and its contributors. | |
36 | * 4. Neither the name of the University nor the names of its contributors | |
37 | * may be used to endorse or promote products derived from this software | |
38 | * without specific prior written permission. | |
39 | * | |
40 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
41 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
42 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
43 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
44 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
45 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
46 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
47 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
48 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
49 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
50 | * SUCH DAMAGE. | |
51 | * | |
52 | * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 | |
53 | * $FreeBSD: src/sys/isa/fd.c,v 1.176.2.8 2002/05/15 21:56:14 joerg Exp $ | |
54 | * | |
55 | */ | |
56 | ||
57 | #include "opt_fdc.h" | |
984263bc MD |
58 | |
59 | #include <sys/param.h> | |
60 | #include <sys/systm.h> | |
61 | #include <sys/kernel.h> | |
62 | #include <sys/buf.h> | |
63 | #include <sys/bus.h> | |
64 | #include <sys/conf.h> | |
983105c1 MD |
65 | #include <sys/diskslice.h> |
66 | #include <sys/disk.h> | |
984263bc MD |
67 | #include <sys/devicestat.h> |
68 | #include <sys/fcntl.h> | |
69 | #include <sys/malloc.h> | |
70 | #include <sys/module.h> | |
71 | #include <sys/proc.h> | |
2b3f93ea | 72 | #include <sys/caps.h> |
984263bc | 73 | #include <sys/syslog.h> |
335dda38 | 74 | #include <sys/device.h> |
984263bc | 75 | #include <sys/rman.h> |
3020e3be | 76 | #include <sys/buf2.h> |
fbe276c7 | 77 | #include <sys/thread2.h> |
3020e3be | 78 | |
984263bc | 79 | #include <machine/clock.h> |
664f5072 | 80 | #include <machine/inttypes.h> |
984263bc | 81 | #include <machine/ioctl_fd.h> |
984263bc MD |
82 | #include <machine/stdarg.h> |
83 | ||
1f2de5d4 MD |
84 | #include <bus/isa/isavar.h> |
85 | #include <bus/isa/isareg.h> | |
86 | #include "fdreg.h" | |
87 | #include "fdc.h" | |
88 | #include <bus/isa/rtc.h> | |
984263bc | 89 | |
984263bc MD |
90 | /* configuration flags */ |
91 | #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ | |
92 | #define FDC_NO_FIFO (1 << 2) /* do not enable FIFO */ | |
93 | ||
94 | /* internally used only, not really from CMOS: */ | |
95 | #define RTCFDT_144M_PRETENDED 0x1000 | |
96 | ||
97 | /* error returns for fd_cmd() */ | |
98 | #define FD_FAILED -1 | |
99 | #define FD_NOT_VALID -2 | |
100 | #define FDC_ERRMAX 100 /* do not log more */ | |
101 | /* | |
102 | * Stop retrying after this many DMA overruns. Since each retry takes | |
103 | * one revolution, with 300 rpm., 25 retries take approximately 10 | |
104 | * seconds which the read attempt will block in case the DMA overrun | |
105 | * is persistent. | |
106 | */ | |
107 | #define FDC_DMAOV_MAX 25 | |
108 | ||
109 | /* | |
110 | * Timeout value for the PIO loops to wait until the FDC main status | |
111 | * register matches our expectations (request for master, direction | |
112 | * bit). This is supposed to be a number of microseconds, although | |
113 | * timing might actually not be very accurate. | |
114 | * | |
115 | * Timeouts of 100 msec are believed to be required for some broken | |
116 | * (old) hardware. | |
117 | */ | |
118 | #define FDSTS_TIMEOUT 100000 | |
119 | ||
120 | #define NUMTYPES 17 | |
121 | #define NUMDENS (NUMTYPES - 7) | |
122 | ||
123 | /* These defines (-1) must match index for fd_types */ | |
124 | #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ | |
125 | #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ | |
126 | #define FD_1720 1 | |
127 | #define FD_1480 2 | |
128 | #define FD_1440 3 | |
129 | #define FD_1200 4 | |
130 | #define FD_820 5 | |
131 | #define FD_800 6 | |
132 | #define FD_720 7 | |
133 | #define FD_360 8 | |
134 | #define FD_640 9 | |
135 | #define FD_1232 10 | |
136 | ||
137 | #define FD_1480in5_25 11 | |
138 | #define FD_1440in5_25 12 | |
139 | #define FD_820in5_25 13 | |
140 | #define FD_800in5_25 14 | |
141 | #define FD_720in5_25 15 | |
142 | #define FD_360in5_25 16 | |
143 | #define FD_640in5_25 17 | |
144 | ||
145 | ||
146 | static struct fd_type fd_types[NUMTYPES] = | |
147 | { | |
148 | { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ | |
149 | { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ | |
150 | { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ | |
151 | { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ | |
152 | { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ | |
153 | { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ | |
154 | { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ | |
155 | { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ | |
156 | { 8,2,0xFF,0x2A,80,1280,1,FDC_250KBPS,2,0x50,1 }, /* 640K in DD 5.25in */ | |
157 | { 8,3,0xFF,0x35,77,1232,1,FDC_500KBPS,2,0x74,1 }, /* 1.23M in HD 5.25in */ | |
158 | ||
159 | { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ | |
160 | { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ | |
161 | { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ | |
162 | { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ | |
163 | { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ | |
164 | { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ | |
165 | { 8,2,0xFF,0x2A,80,1280,1,FDC_300KBPS,2,0x50,1 }, /* 640K in HD 5.25in */ | |
166 | }; | |
167 | ||
168 | #define DRVS_PER_CTLR 2 /* 2 floppies */ | |
169 | ||
170 | /***********************************************************************\ | |
171 | * Per controller structure. * | |
172 | \***********************************************************************/ | |
8c5c0470 | 173 | devclass_t fdc_devclass; |
984263bc MD |
174 | |
175 | /***********************************************************************\ | |
176 | * Per drive structure. * | |
177 | * N per controller (DRVS_PER_CTLR) * | |
178 | \***********************************************************************/ | |
179 | struct fd_data { | |
180 | struct fdc_data *fdc; /* pointer to controller structure */ | |
181 | int fdsu; /* this units number on this controller */ | |
182 | int type; /* Drive type (FD_1440...) */ | |
983105c1 | 183 | struct fd_type ft; /* the type descriptor */ |
984263bc MD |
184 | int flags; |
185 | #define FD_OPEN 0x01 /* it's open */ | |
186 | #define FD_ACTIVE 0x02 /* it's active */ | |
187 | #define FD_MOTOR 0x04 /* motor should be on */ | |
188 | #define FD_MOTOR_WAIT 0x08 /* motor coming up */ | |
189 | int skip; | |
190 | int hddrv; | |
191 | #define FD_NO_TRACK -2 | |
192 | int track; /* where we think the head is */ | |
193 | int options; /* user configurable options, see ioctl_fd.h */ | |
e8b273ff MD |
194 | struct callout toffhandle; |
195 | struct callout tohandle; | |
4fc46503 | 196 | struct callout motor; |
983105c1 | 197 | struct disk disk; |
984263bc MD |
198 | struct devstat device_stats; |
199 | device_t dev; | |
200 | fdu_t fdu; | |
201 | }; | |
202 | ||
203 | struct fdc_ivars { | |
204 | int fdunit; | |
205 | }; | |
206 | static devclass_t fd_devclass; | |
207 | ||
208 | /***********************************************************************\ | |
209 | * Throughout this file the following conventions will be used: * | |
210 | * fd is a pointer to the fd_data struct for the drive in question * | |
211 | * fdc is a pointer to the fdc_data struct for the controller * | |
212 | * fdu is the floppy drive unit number * | |
213 | * fdcu is the floppy controller unit number * | |
214 | * fdsu is the floppy drive unit number on that controller. (sub-unit) * | |
215 | \***********************************************************************/ | |
216 | ||
217 | /* internal functions */ | |
218 | static void fdc_intr(void *); | |
219 | static void set_motor(struct fdc_data *, int, int); | |
220 | # define TURNON 1 | |
221 | # define TURNOFF 0 | |
222 | static timeout_t fd_turnoff; | |
223 | static timeout_t fd_motor_on; | |
224 | static void fd_turnon(struct fd_data *); | |
225 | static void fdc_reset(fdc_p); | |
226 | static int fd_in(struct fdc_data *, int *); | |
227 | static int out_fdc(struct fdc_data *, int); | |
228 | static void fdstart(struct fdc_data *); | |
229 | static timeout_t fd_iotimeout; | |
230 | static timeout_t fd_pseudointr; | |
231 | static int fdstate(struct fdc_data *); | |
232 | static int retrier(struct fdc_data *); | |
b13267a5 | 233 | static int fdformat(cdev_t, struct fd_formb *, struct ucred *); |
984263bc MD |
234 | |
235 | static int enable_fifo(fdc_p fdc); | |
236 | ||
237 | static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ | |
238 | ||
239 | ||
240 | #define DEVIDLE 0 | |
241 | #define FINDWORK 1 | |
242 | #define DOSEEK 2 | |
243 | #define SEEKCOMPLETE 3 | |
244 | #define IOCOMPLETE 4 | |
245 | #define RECALCOMPLETE 5 | |
246 | #define STARTRECAL 6 | |
247 | #define RESETCTLR 7 | |
248 | #define SEEKWAIT 8 | |
249 | #define RECALWAIT 9 | |
250 | #define MOTORWAIT 10 | |
251 | #define IOTIMEDOUT 11 | |
252 | #define RESETCOMPLETE 12 | |
253 | #define PIOREAD 13 | |
254 | ||
255 | #ifdef FDC_DEBUG | |
256 | static char const * const fdstates[] = | |
257 | { | |
258 | "DEVIDLE", | |
259 | "FINDWORK", | |
260 | "DOSEEK", | |
261 | "SEEKCOMPLETE", | |
262 | "IOCOMPLETE", | |
263 | "RECALCOMPLETE", | |
264 | "STARTRECAL", | |
265 | "RESETCTLR", | |
266 | "SEEKWAIT", | |
267 | "RECALWAIT", | |
268 | "MOTORWAIT", | |
269 | "IOTIMEDOUT", | |
270 | "RESETCOMPLETE", | |
271 | "PIOREAD", | |
272 | }; | |
273 | ||
274 | /* CAUTION: fd_debug causes huge amounts of logging output */ | |
275 | static int volatile fd_debug = 0; | |
e3869ec7 SW |
276 | #define TRACE0(arg) if(fd_debug) kprintf(arg) |
277 | #define TRACE1(arg1, arg2) if(fd_debug) kprintf(arg1, arg2) | |
984263bc MD |
278 | #else /* FDC_DEBUG */ |
279 | #define TRACE0(arg) | |
280 | #define TRACE1(arg1, arg2) | |
281 | #endif /* FDC_DEBUG */ | |
282 | ||
8c5c0470 | 283 | void |
984263bc MD |
284 | fdout_wr(fdc_p fdc, u_int8_t v) |
285 | { | |
286 | bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v); | |
287 | } | |
288 | ||
289 | static u_int8_t | |
290 | fdsts_rd(fdc_p fdc) | |
291 | { | |
292 | return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off); | |
293 | } | |
294 | ||
295 | static void | |
296 | fddata_wr(fdc_p fdc, u_int8_t v) | |
297 | { | |
298 | bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v); | |
299 | } | |
300 | ||
301 | static u_int8_t | |
302 | fddata_rd(fdc_p fdc) | |
303 | { | |
304 | return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off); | |
305 | } | |
306 | ||
307 | static void | |
308 | fdctl_wr_isa(fdc_p fdc, u_int8_t v) | |
309 | { | |
310 | bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v); | |
311 | } | |
312 | ||
984263bc MD |
313 | #if 0 |
314 | ||
315 | static u_int8_t | |
316 | fdin_rd(fdc_p fdc) | |
317 | { | |
318 | return bus_space_read_1(fdc->portt, fdc->porth, FDIN); | |
319 | } | |
320 | ||
321 | #endif | |
322 | ||
323 | static d_open_t Fdopen; /* NOTE, not fdopen */ | |
324 | static d_close_t fdclose; | |
325 | static d_ioctl_t fdioctl; | |
326 | static d_strategy_t fdstrategy; | |
327 | ||
fef8985e | 328 | static struct dev_ops fd_ops = { |
311467df | 329 | { "fd", 0, D_DISK }, |
fef8985e MD |
330 | .d_open = Fdopen, |
331 | .d_close = fdclose, | |
332 | .d_read = physread, | |
333 | .d_write = physwrite, | |
334 | .d_ioctl = fdioctl, | |
335 | .d_strategy = fdstrategy, | |
984263bc MD |
336 | }; |
337 | ||
338 | static int | |
339 | fdc_err(struct fdc_data *fdc, const char *s) | |
340 | { | |
341 | fdc->fdc_errs++; | |
342 | if (s) { | |
343 | if (fdc->fdc_errs < FDC_ERRMAX) | |
344 | device_printf(fdc->fdc_dev, "%s", s); | |
345 | else if (fdc->fdc_errs == FDC_ERRMAX) | |
346 | device_printf(fdc->fdc_dev, "too many errors, not " | |
347 | "logging any more\n"); | |
348 | } | |
349 | ||
350 | return FD_FAILED; | |
351 | } | |
352 | ||
353 | /* | |
354 | * fd_cmd: Send a command to the chip. Takes a varargs with this structure: | |
355 | * Unit number, | |
356 | * # of output bytes, output bytes as ints ..., | |
357 | * # of input bytes, input bytes as ints ... | |
358 | */ | |
8c5c0470 | 359 | int |
984263bc MD |
360 | fd_cmd(struct fdc_data *fdc, int n_out, ...) |
361 | { | |
362 | u_char cmd; | |
363 | int n_in; | |
364 | int n; | |
e2565a42 | 365 | __va_list ap; |
984263bc | 366 | |
e2565a42 MD |
367 | __va_start(ap, n_out); |
368 | cmd = (u_char)(__va_arg(ap, int)); | |
369 | __va_end(ap); | |
370 | __va_start(ap, n_out); | |
984263bc MD |
371 | for (n = 0; n < n_out; n++) |
372 | { | |
e2565a42 | 373 | if (out_fdc(fdc, __va_arg(ap, int)) < 0) |
984263bc MD |
374 | { |
375 | char msg[50]; | |
f8c7a42d | 376 | ksnprintf(msg, sizeof(msg), |
984263bc MD |
377 | "cmd %x failed at out byte %d of %d\n", |
378 | cmd, n + 1, n_out); | |
379 | return fdc_err(fdc, msg); | |
380 | } | |
381 | } | |
e2565a42 | 382 | n_in = __va_arg(ap, int); |
984263bc MD |
383 | for (n = 0; n < n_in; n++) |
384 | { | |
e2565a42 | 385 | int *ptr = __va_arg(ap, int *); |
984263bc MD |
386 | if (fd_in(fdc, ptr) < 0) |
387 | { | |
388 | char msg[50]; | |
f8c7a42d | 389 | ksnprintf(msg, sizeof(msg), |
984263bc MD |
390 | "cmd %02x failed at in byte %d of %d\n", |
391 | cmd, n + 1, n_in); | |
392 | return fdc_err(fdc, msg); | |
393 | } | |
394 | } | |
395 | ||
396 | return 0; | |
397 | } | |
398 | ||
399 | static int | |
400 | enable_fifo(fdc_p fdc) | |
401 | { | |
402 | int i, j; | |
403 | ||
404 | if ((fdc->flags & FDC_HAS_FIFO) == 0) { | |
405 | ||
406 | /* | |
407 | * XXX: | |
408 | * Cannot use fd_cmd the normal way here, since | |
409 | * this might be an invalid command. Thus we send the | |
410 | * first byte, and check for an early turn of data directon. | |
411 | */ | |
412 | ||
413 | if (out_fdc(fdc, I8207X_CONFIGURE) < 0) | |
414 | return fdc_err(fdc, "Enable FIFO failed\n"); | |
415 | ||
416 | /* If command is invalid, return */ | |
417 | j = FDSTS_TIMEOUT; | |
418 | while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM)) | |
419 | != NE7_RQM && j-- > 0) { | |
420 | if (i == (NE7_DIO | NE7_RQM)) { | |
421 | fdc_reset(fdc); | |
422 | return FD_FAILED; | |
423 | } | |
424 | DELAY(1); | |
425 | } | |
426 | if (j<0 || | |
427 | fd_cmd(fdc, 3, | |
428 | 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { | |
429 | fdc_reset(fdc); | |
430 | return fdc_err(fdc, "Enable FIFO failed\n"); | |
431 | } | |
432 | fdc->flags |= FDC_HAS_FIFO; | |
433 | return 0; | |
434 | } | |
435 | if (fd_cmd(fdc, 4, | |
436 | I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) | |
437 | return fdc_err(fdc, "Re-enable FIFO failed\n"); | |
438 | return 0; | |
439 | } | |
440 | ||
441 | static int | |
442 | fd_sense_drive_status(fdc_p fdc, int *st3p) | |
443 | { | |
444 | int st3; | |
445 | ||
446 | if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) | |
447 | { | |
448 | return fdc_err(fdc, "Sense Drive Status failed\n"); | |
449 | } | |
450 | if (st3p) | |
451 | *st3p = st3; | |
452 | ||
453 | return 0; | |
454 | } | |
455 | ||
456 | static int | |
457 | fd_sense_int(fdc_p fdc, int *st0p, int *cylp) | |
458 | { | |
459 | int cyl, st0, ret; | |
460 | ||
461 | ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); | |
462 | if (ret) { | |
463 | (void)fdc_err(fdc, | |
464 | "sense intr err reading stat reg 0\n"); | |
465 | return ret; | |
466 | } | |
467 | ||
468 | if (st0p) | |
469 | *st0p = st0; | |
470 | ||
471 | if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { | |
472 | /* | |
473 | * There doesn't seem to have been an interrupt. | |
474 | */ | |
475 | return FD_NOT_VALID; | |
476 | } | |
477 | ||
478 | if (fd_in(fdc, &cyl) < 0) { | |
479 | return fdc_err(fdc, "can't get cyl num\n"); | |
480 | } | |
481 | ||
482 | if (cylp) | |
483 | *cylp = cyl; | |
484 | ||
485 | return 0; | |
486 | } | |
487 | ||
488 | ||
489 | static int | |
490 | fd_read_status(fdc_p fdc, int fdsu) | |
491 | { | |
492 | int i, ret; | |
493 | ||
494 | for (i = 0; i < 7; i++) { | |
495 | /* | |
496 | * XXX types are poorly chosen. Only bytes can by read | |
497 | * from the hardware, but fdc->status[] wants u_ints and | |
498 | * fd_in() gives ints. | |
499 | */ | |
500 | int status; | |
501 | ||
502 | ret = fd_in(fdc, &status); | |
503 | fdc->status[i] = status; | |
504 | if (ret != 0) | |
505 | break; | |
506 | } | |
507 | ||
508 | if (ret == 0) | |
509 | fdc->flags |= FDC_STAT_VALID; | |
510 | else | |
511 | fdc->flags &= ~FDC_STAT_VALID; | |
512 | ||
513 | return ret; | |
514 | } | |
515 | ||
516 | /****************************************************************************/ | |
517 | /* autoconfiguration stuff */ | |
518 | /****************************************************************************/ | |
519 | ||
8c5c0470 | 520 | int |
984263bc MD |
521 | fdc_alloc_resources(struct fdc_data *fdc) |
522 | { | |
523 | device_t dev; | |
524 | int ispnp, ispcmcia; | |
525 | ||
526 | dev = fdc->fdc_dev; | |
527 | ispnp = (fdc->flags & FDC_ISPNP) != 0; | |
528 | ispcmcia = (fdc->flags & FDC_ISPCMCIA) != 0; | |
529 | fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; | |
530 | fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; | |
531 | ||
532 | /* | |
533 | * On standard ISA, we don't just use an 8 port range | |
534 | * (e.g. 0x3f0-0x3f7) since that covers an IDE control | |
535 | * register at 0x3f6. | |
536 | * | |
537 | * Isn't PC hardware wonderful. | |
538 | * | |
539 | * The Y-E Data PCMCIA FDC doesn't have this problem, it | |
540 | * uses the register with offset 6 for pseudo-DMA, and the | |
541 | * one with offset 7 as control register. | |
542 | */ | |
543 | fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, | |
544 | &fdc->rid_ioport, 0ul, ~0ul, | |
545 | ispcmcia ? 8 : (ispnp ? 1 : 6), | |
546 | RF_ACTIVE); | |
914b334c | 547 | if (fdc->res_ioport == NULL) { |
984263bc MD |
548 | device_printf(dev, "cannot reserve I/O port range\n"); |
549 | return ENXIO; | |
550 | } | |
551 | fdc->portt = rman_get_bustag(fdc->res_ioport); | |
552 | fdc->porth = rman_get_bushandle(fdc->res_ioport); | |
553 | ||
554 | if (!ispcmcia) { | |
555 | /* | |
556 | * Some BIOSen report the device at 0x3f2-0x3f5,0x3f7 | |
557 | * and some at 0x3f0-0x3f5,0x3f7. We detect the former | |
558 | * by checking the size and adjust the port address | |
559 | * accordingly. | |
560 | */ | |
561 | if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4) | |
562 | fdc->port_off = -2; | |
563 | ||
564 | /* | |
565 | * Register the control port range as rid 1 if it | |
566 | * isn't there already. Most PnP BIOSen will have | |
567 | * already done this but non-PnP configurations don't. | |
568 | * | |
569 | * And some (!!) report 0x3f2-0x3f5 and completely | |
570 | * leave out the control register! It seems that some | |
571 | * non-antique controller chips have a different | |
572 | * method of programming the transfer speed which | |
573 | * doesn't require the control register, but it's | |
574 | * mighty bogus as the chip still responds to the | |
575 | * address for the control register. | |
576 | */ | |
577 | if (bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) { | |
578 | u_long ctlstart; | |
579 | ||
580 | /* Find the control port, usually 0x3f7 */ | |
581 | ctlstart = rman_get_start(fdc->res_ioport) + | |
582 | fdc->port_off + 7; | |
583 | ||
b47b3275 SZ |
584 | bus_set_resource(dev, SYS_RES_IOPORT, 1, ctlstart, 1, |
585 | -1); | |
984263bc MD |
586 | } |
587 | ||
588 | /* | |
589 | * Now (finally!) allocate the control port. | |
590 | */ | |
591 | fdc->rid_ctl = 1; | |
592 | fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT, | |
593 | &fdc->rid_ctl, | |
594 | 0ul, ~0ul, 1, RF_ACTIVE); | |
914b334c | 595 | if (fdc->res_ctl == NULL) { |
984263bc MD |
596 | device_printf(dev, |
597 | "cannot reserve control I/O port range\n"); | |
598 | return ENXIO; | |
599 | } | |
600 | fdc->ctlt = rman_get_bustag(fdc->res_ctl); | |
601 | fdc->ctlh = rman_get_bushandle(fdc->res_ctl); | |
602 | } | |
603 | ||
604 | fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, | |
605 | &fdc->rid_irq, 0ul, ~0ul, 1, | |
606 | RF_ACTIVE); | |
914b334c | 607 | if (fdc->res_irq == NULL) { |
984263bc MD |
608 | device_printf(dev, "cannot reserve interrupt line\n"); |
609 | return ENXIO; | |
610 | } | |
611 | ||
612 | if ((fdc->flags & FDC_NODMA) == 0) { | |
613 | fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, | |
614 | &fdc->rid_drq, 0ul, ~0ul, 1, | |
615 | RF_ACTIVE); | |
914b334c | 616 | if (fdc->res_drq == NULL) { |
984263bc MD |
617 | device_printf(dev, "cannot reserve DMA request line\n"); |
618 | return ENXIO; | |
619 | } | |
620 | fdc->dmachan = fdc->res_drq->r_start; | |
621 | } | |
622 | ||
623 | return 0; | |
624 | } | |
625 | ||
8c5c0470 | 626 | void |
984263bc MD |
627 | fdc_release_resources(struct fdc_data *fdc) |
628 | { | |
629 | device_t dev; | |
630 | ||
631 | dev = fdc->fdc_dev; | |
914b334c | 632 | if (fdc->res_irq != NULL) { |
984263bc MD |
633 | bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, |
634 | fdc->res_irq); | |
635 | bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, | |
636 | fdc->res_irq); | |
637 | } | |
914b334c | 638 | if (fdc->res_ctl != NULL) { |
984263bc MD |
639 | bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, |
640 | fdc->res_ctl); | |
641 | bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, | |
642 | fdc->res_ctl); | |
643 | } | |
914b334c | 644 | if (fdc->res_ioport != NULL) { |
984263bc MD |
645 | bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, |
646 | fdc->res_ioport); | |
647 | bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, | |
648 | fdc->res_ioport); | |
649 | } | |
914b334c | 650 | if (fdc->res_drq != NULL) { |
984263bc MD |
651 | bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, |
652 | fdc->res_drq); | |
653 | bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, | |
654 | fdc->res_drq); | |
655 | } | |
656 | } | |
657 | ||
658 | /****************************************************************************/ | |
659 | /* autoconfiguration stuff */ | |
660 | /****************************************************************************/ | |
661 | ||
662 | static struct isa_pnp_id fdc_ids[] = { | |
663 | {0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */ | |
664 | {0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */ | |
665 | {0} | |
666 | }; | |
667 | ||
8c5c0470 | 668 | int |
984263bc MD |
669 | fdc_read_ivar(device_t dev, device_t child, int which, u_long *result) |
670 | { | |
671 | struct fdc_ivars *ivars = device_get_ivars(child); | |
672 | ||
673 | switch (which) { | |
674 | case FDC_IVAR_FDUNIT: | |
675 | *result = ivars->fdunit; | |
676 | break; | |
677 | default: | |
678 | return ENOENT; | |
679 | } | |
680 | return 0; | |
681 | } | |
682 | ||
683 | /* | |
684 | * fdc controller section. | |
685 | */ | |
686 | static int | |
687 | fdc_probe(device_t dev) | |
688 | { | |
689 | int error, ic_type; | |
690 | struct fdc_data *fdc; | |
691 | ||
692 | fdc = device_get_softc(dev); | |
693 | bzero(fdc, sizeof *fdc); | |
694 | fdc->fdc_dev = dev; | |
695 | fdc->fdctl_wr = fdctl_wr_isa; | |
696 | ||
697 | /* Check pnp ids */ | |
698 | error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); | |
699 | if (error == ENXIO) | |
700 | return ENXIO; | |
701 | if (error == 0) | |
702 | fdc->flags |= FDC_ISPNP; | |
703 | ||
704 | /* Attempt to allocate our resources for the duration of the probe */ | |
705 | error = fdc_alloc_resources(fdc); | |
706 | if (error) | |
707 | goto out; | |
708 | ||
709 | /* First - lets reset the floppy controller */ | |
710 | fdout_wr(fdc, 0); | |
711 | DELAY(100); | |
712 | fdout_wr(fdc, FDO_FRST); | |
713 | ||
714 | /* see if it can handle a command */ | |
715 | if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), | |
716 | NE7_SPEC_2(2, 0), 0)) { | |
717 | error = ENXIO; | |
718 | goto out; | |
719 | } | |
720 | ||
721 | if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { | |
722 | ic_type = (u_char)ic_type; | |
723 | switch (ic_type) { | |
724 | case 0x80: | |
725 | device_set_desc(dev, "NEC 765 or clone"); | |
726 | fdc->fdct = FDC_NE765; | |
727 | break; | |
728 | case 0x81: | |
729 | device_set_desc(dev, "Intel 82077 or clone"); | |
730 | fdc->fdct = FDC_I82077; | |
731 | break; | |
732 | case 0x90: | |
733 | device_set_desc(dev, "NEC 72065B or clone"); | |
734 | fdc->fdct = FDC_NE72065; | |
735 | break; | |
736 | default: | |
737 | device_set_desc(dev, "generic floppy controller"); | |
738 | fdc->fdct = FDC_UNKNOWN; | |
739 | break; | |
740 | } | |
741 | } | |
742 | ||
743 | out: | |
744 | fdc_release_resources(fdc); | |
745 | return (error); | |
746 | } | |
747 | ||
984263bc MD |
748 | /* |
749 | * Add a child device to the fdc controller. It will then be probed etc. | |
750 | */ | |
751 | static void | |
752 | fdc_add_child(device_t dev, const char *name, int unit) | |
753 | { | |
754 | int disabled; | |
755 | struct fdc_ivars *ivar; | |
756 | device_t child; | |
757 | ||
efda3bd0 | 758 | ivar = kmalloc(sizeof *ivar, M_DEVBUF /* XXX */, M_WAITOK | M_ZERO); |
984263bc MD |
759 | if (resource_int_value(name, unit, "drive", &ivar->fdunit) != 0) |
760 | ivar->fdunit = 0; | |
761 | child = device_add_child(dev, name, unit); | |
762 | if (child == NULL) | |
763 | return; | |
764 | device_set_ivars(child, ivar); | |
765 | if (resource_int_value(name, unit, "disabled", &disabled) == 0 | |
766 | && disabled != 0) | |
767 | device_disable(child); | |
768 | } | |
769 | ||
8c5c0470 | 770 | int |
984263bc MD |
771 | fdc_attach(device_t dev) |
772 | { | |
773 | struct fdc_data *fdc; | |
774 | int i, error; | |
775 | ||
776 | fdc = device_get_softc(dev); | |
8b97cf2a JS |
777 | |
778 | callout_init(&fdc->pseudointr_ch); | |
779 | ||
984263bc MD |
780 | error = fdc_alloc_resources(fdc); |
781 | if (error) { | |
0ca0cd25 | 782 | device_printf(dev, "cannot reacquire resources\n"); |
984263bc MD |
783 | return error; |
784 | } | |
785 | error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, | |
ee61f228 | 786 | 0, fdc_intr, fdc, |
2c5c4bb9 | 787 | &fdc->fdc_intr, NULL, NULL); |
984263bc MD |
788 | if (error) { |
789 | device_printf(dev, "cannot setup interrupt\n"); | |
790 | return error; | |
791 | } | |
792 | fdc->fdcu = device_get_unit(dev); | |
793 | fdc->flags |= FDC_ATTACHED; | |
794 | ||
795 | if ((fdc->flags & FDC_NODMA) == 0) { | |
796 | /* Acquire the DMA channel forever, The driver will do the rest */ | |
797 | /* XXX should integrate with rman */ | |
0bc821c6 | 798 | error = isa_dma_acquire(fdc->dmachan); |
799 | if (!error) { | |
a8a4cdd3 | 800 | error = isa_dma_init(fdc->dmachan, 128 << 3 /* XXX max secsize */, |
801 | M_WAITOK); | |
0bc821c6 | 802 | if (error) { |
803 | isa_dma_release(fdc->dmachan); | |
804 | device_printf(dev, "disabling dma\n"); | |
805 | fdc->flags |= FDC_NODMA; | |
806 | } | |
807 | } | |
984263bc MD |
808 | } |
809 | fdc->state = DEVIDLE; | |
810 | ||
811 | /* reset controller, turn motor off, clear fdout mirror reg */ | |
812 | fdout_wr(fdc, ((fdc->fdout = 0))); | |
81b5c339 | 813 | bioq_init(&fdc->bio_queue); |
984263bc MD |
814 | |
815 | /* | |
816 | * Probe and attach any children. We should probably detect | |
817 | * devices from the BIOS unless overridden. | |
818 | */ | |
819 | for (i = resource_query_string(-1, "at", device_get_nameunit(dev)); | |
820 | i != -1; | |
821 | i = resource_query_string(i, "at", device_get_nameunit(dev))) | |
822 | fdc_add_child(dev, resource_query_name(i), | |
823 | resource_query_unit(i)); | |
824 | ||
825 | return (bus_generic_attach(dev)); | |
826 | } | |
827 | ||
8c5c0470 | 828 | int |
984263bc MD |
829 | fdc_print_child(device_t me, device_t child) |
830 | { | |
831 | int retval = 0; | |
832 | ||
833 | retval += bus_print_child_header(me, child); | |
e3869ec7 | 834 | retval += kprintf(" on %s drive %d\n", device_get_nameunit(me), |
984263bc MD |
835 | fdc_get_fdunit(child)); |
836 | ||
837 | return (retval); | |
838 | } | |
839 | ||
840 | static device_method_t fdc_methods[] = { | |
841 | /* Device interface */ | |
842 | DEVMETHOD(device_probe, fdc_probe), | |
843 | DEVMETHOD(device_attach, fdc_attach), | |
844 | DEVMETHOD(device_detach, bus_generic_detach), | |
845 | DEVMETHOD(device_shutdown, bus_generic_shutdown), | |
846 | DEVMETHOD(device_suspend, bus_generic_suspend), | |
847 | DEVMETHOD(device_resume, bus_generic_resume), | |
848 | ||
849 | /* Bus interface */ | |
850 | DEVMETHOD(bus_print_child, fdc_print_child), | |
851 | DEVMETHOD(bus_read_ivar, fdc_read_ivar), | |
852 | /* Our children never use any other bus interface methods. */ | |
853 | ||
d3c9c58e | 854 | DEVMETHOD_END |
984263bc MD |
855 | }; |
856 | ||
857 | static driver_t fdc_driver = { | |
858 | "fdc", | |
859 | fdc_methods, | |
860 | sizeof(struct fdc_data) | |
861 | }; | |
862 | ||
aa2b9d05 | 863 | DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, NULL, NULL); |
0bc821c6 | 864 | #if 0 |
aa2b9d05 | 865 | DRIVER_MODULE(fdc, acpi, fdc_driver, fdc_devclass, NULL, NULL); |
0bc821c6 | 866 | #endif |
984263bc | 867 | |
984263bc MD |
868 | /******************************************************************/ |
869 | /* | |
870 | * devices attached to the controller section. | |
871 | */ | |
872 | static int | |
873 | fd_probe(device_t dev) | |
874 | { | |
875 | int i; | |
876 | u_int fdt, st0, st3; | |
877 | struct fd_data *fd; | |
878 | struct fdc_data *fdc; | |
879 | fdsu_t fdsu; | |
880 | static int fd_fifo = 0; | |
881 | ||
882 | fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ | |
883 | fd = device_get_softc(dev); | |
884 | fdc = device_get_softc(device_get_parent(dev)); | |
885 | ||
886 | bzero(fd, sizeof *fd); | |
887 | fd->dev = dev; | |
888 | fd->fdc = fdc; | |
889 | fd->fdsu = fdsu; | |
890 | fd->fdu = device_get_unit(dev); | |
891 | ||
0bc821c6 | 892 | #ifdef __x86_64__ |
984263bc MD |
893 | /* look up what bios thinks we have */ |
894 | switch (fd->fdu) { | |
895 | case 0: | |
896 | if ((fdc->flags & FDC_ISPCMCIA)) | |
897 | fdt = RTCFDT_144M; | |
898 | else if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) | |
899 | fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; | |
900 | else | |
901 | fdt = (rtcin(RTC_FDISKETTE) & 0xf0); | |
902 | break; | |
903 | case 1: | |
904 | fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); | |
905 | break; | |
906 | default: | |
907 | fdt = RTCFDT_NONE; | |
908 | break; | |
909 | } | |
910 | #else | |
911 | fdt = RTCFDT_144M; /* XXX probably */ | |
912 | #endif | |
913 | ||
914 | /* is there a unit? */ | |
915 | if (fdt == RTCFDT_NONE) | |
916 | return (ENXIO); | |
917 | ||
918 | /* select it */ | |
919 | set_motor(fdc, fdsu, TURNON); | |
920 | DELAY(1000000); /* 1 sec */ | |
921 | ||
922 | /* XXX This doesn't work before the first set_motor() */ | |
923 | if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN | |
924 | && (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0 | |
925 | && enable_fifo(fdc) == 0) { | |
926 | device_printf(device_get_parent(dev), | |
927 | "FIFO enabled, %d bytes threshold\n", fifo_threshold); | |
928 | } | |
929 | fd_fifo = 1; | |
930 | ||
931 | if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) | |
932 | && (st3 & NE7_ST3_T0)) { | |
933 | /* if at track 0, first seek inwards */ | |
934 | /* seek some steps: */ | |
935 | fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); | |
936 | DELAY(300000); /* ...wait a moment... */ | |
937 | fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ | |
938 | } | |
939 | ||
940 | /* If we're at track 0 first seek inwards. */ | |
941 | if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { | |
942 | /* Seek some steps... */ | |
943 | if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { | |
944 | /* ...wait a moment... */ | |
945 | DELAY(300000); | |
946 | /* make ctrlr happy: */ | |
947 | fd_sense_int(fdc, 0, 0); | |
948 | } | |
949 | } | |
950 | ||
951 | for (i = 0; i < 2; i++) { | |
952 | /* | |
953 | * we must recalibrate twice, just in case the | |
954 | * heads have been beyond cylinder 76, since most | |
955 | * FDCs still barf when attempting to recalibrate | |
956 | * more than 77 steps | |
957 | */ | |
958 | /* go back to 0: */ | |
959 | if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { | |
960 | /* a second being enough for full stroke seek*/ | |
961 | DELAY(i == 0 ? 1000000 : 300000); | |
962 | ||
963 | /* anything responding? */ | |
964 | if (fd_sense_int(fdc, &st0, 0) == 0 && | |
965 | (st0 & NE7_ST0_EC) == 0) | |
966 | break; /* already probed succesfully */ | |
967 | } | |
968 | } | |
969 | ||
970 | set_motor(fdc, fdsu, TURNOFF); | |
971 | ||
972 | if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ | |
973 | return (ENXIO); | |
974 | ||
975 | fd->track = FD_NO_TRACK; | |
976 | fd->fdc = fdc; | |
977 | fd->fdsu = fdsu; | |
978 | fd->options = 0; | |
e8b273ff MD |
979 | callout_init(&fd->toffhandle); |
980 | callout_init(&fd->tohandle); | |
4fc46503 | 981 | callout_init(&fd->motor); |
984263bc MD |
982 | |
983 | switch (fdt) { | |
984 | case RTCFDT_12M: | |
985 | device_set_desc(dev, "1200-KB 5.25\" drive"); | |
986 | fd->type = FD_1200; | |
987 | break; | |
988 | case RTCFDT_144M | RTCFDT_144M_PRETENDED: | |
989 | device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); | |
984263bc | 990 | fd->type = FD_1440; |
c755a6ff | 991 | break; |
984263bc MD |
992 | case RTCFDT_144M: |
993 | device_set_desc(dev, "1440-KB 3.5\" drive"); | |
994 | fd->type = FD_1440; | |
995 | break; | |
996 | case RTCFDT_288M: | |
997 | case RTCFDT_288M_1: | |
998 | device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); | |
999 | fd->type = FD_1440; | |
1000 | break; | |
1001 | case RTCFDT_360K: | |
1002 | device_set_desc(dev, "360-KB 5.25\" drive"); | |
1003 | fd->type = FD_360; | |
1004 | break; | |
1005 | case RTCFDT_720K: | |
e3869ec7 | 1006 | kprintf("720-KB 3.5\" drive"); |
984263bc MD |
1007 | fd->type = FD_720; |
1008 | break; | |
1009 | default: | |
1010 | return (ENXIO); | |
1011 | } | |
983105c1 | 1012 | fd->ft = fd_types[fd->type - 1]; |
984263bc MD |
1013 | return (0); |
1014 | } | |
1015 | ||
1016 | static int | |
1017 | fd_attach(device_t dev) | |
1018 | { | |
7ba1363d MD |
1019 | struct disk_info info; |
1020 | struct fd_data *fd; | |
1021 | struct fd_type *ft; | |
984263bc MD |
1022 | |
1023 | fd = device_get_softc(dev); | |
1024 | ||
983105c1 | 1025 | disk_create(fd->fdu, &fd->disk, &fd_ops); |
f5d8307c | 1026 | disk_setdisktype(&fd->disk, "floppy"); |
984263bc MD |
1027 | |
1028 | /* | |
983105c1 MD |
1029 | * Make special raw floppy devices with preset types to |
1030 | * make formatting easier. These override the disk management | |
1031 | * layer for the whole-slice-disk for partitions 128-191. Note | |
1032 | * that we do not override partition 255, which is the | |
1033 | * whole-slice-part. If we did we would have to provide our | |
1034 | * own DIOCGPART ioctl. | |
984263bc | 1035 | */ |
983105c1 MD |
1036 | make_dev(&fd_ops, dkmakeminor(fd->fdu, WHOLE_DISK_SLICE, 128 + 1), |
1037 | UID_ROOT, GID_WHEEL, 0600, "fd%d.1720", fd->fdu); | |
1038 | make_dev(&fd_ops, dkmakeminor(fd->fdu, WHOLE_DISK_SLICE, 128 + 2), | |
1039 | UID_ROOT, GID_WHEEL, 0600, "fd%d.1480", fd->fdu); | |
1040 | make_dev(&fd_ops, dkmakeminor(fd->fdu, WHOLE_DISK_SLICE, 128 + 3), | |
1041 | UID_ROOT, GID_WHEEL, 0600, "fd%d.1440", fd->fdu); | |
1042 | make_dev(&fd_ops, dkmakeminor(fd->fdu, WHOLE_DISK_SLICE, 128 + 4), | |
1043 | UID_ROOT, GID_WHEEL, 0600, "fd%d.1200", fd->fdu); | |
1044 | make_dev(&fd_ops, dkmakeminor(fd->fdu, WHOLE_DISK_SLICE, 128 + 5), | |
1045 | UID_ROOT, GID_WHEEL, 0600, "fd%d.820", fd->fdu); | |
1046 | make_dev(&fd_ops, dkmakeminor(fd->fdu, WHOLE_DISK_SLICE, 128 + 6), | |
1047 | UID_ROOT, GID_WHEEL, 0600, "fd%d.800", fd->fdu); | |
1048 | make_dev(&fd_ops, dkmakeminor(fd->fdu, WHOLE_DISK_SLICE, 128 + 7), | |
1049 | UID_ROOT, GID_WHEEL, 0600, "fd%d.720", fd->fdu); | |
1050 | make_dev(&fd_ops, dkmakeminor(fd->fdu, WHOLE_DISK_SLICE, 128 + 8), | |
1051 | UID_ROOT, GID_WHEEL, 0600, "fd%d.360", fd->fdu); | |
1052 | make_dev(&fd_ops, dkmakeminor(fd->fdu, WHOLE_DISK_SLICE, 128 + 9), | |
1053 | UID_ROOT, GID_WHEEL, 0600, "fd%d.640", fd->fdu); | |
1054 | make_dev(&fd_ops, dkmakeminor(fd->fdu, WHOLE_DISK_SLICE, 128 + 10), | |
1055 | UID_ROOT, GID_WHEEL, 0600, "fd%d.1232", fd->fdu); | |
1056 | ||
984263bc MD |
1057 | devstat_add_entry(&fd->device_stats, device_get_name(dev), |
1058 | device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS, | |
1059 | DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, | |
1060 | DEVSTAT_PRIORITY_FD); | |
7ba1363d MD |
1061 | |
1062 | if (fd->type != NO_TYPE) { | |
1063 | bzero(&info, sizeof(info)); | |
1064 | ft = &fd_types[fd->type - 1]; | |
1065 | info.d_media_blksize = 128 << ft->secsize; | |
1066 | info.d_media_blocks = ft->size; | |
1067 | info.d_dsflags = DSO_COMPATPARTA | DSO_COMPATMBR; | |
1068 | info.d_nheads = ft->heads; | |
1069 | info.d_secpertrack = ft->sectrac; | |
1070 | info.d_secpercyl = ft->sectrac * ft->heads; | |
1071 | info.d_ncylinders = ft->size / info.d_secpercyl; | |
1072 | disk_setdiskinfo(&fd->disk, &info); | |
1073 | } | |
984263bc MD |
1074 | return (0); |
1075 | } | |
1076 | ||
1077 | static int | |
1078 | fd_detach(device_t dev) | |
1079 | { | |
1080 | struct fd_data *fd; | |
1081 | ||
1082 | fd = device_get_softc(dev); | |
cd29885a MD |
1083 | kprintf("devfs: Please make sure that only the right fd device was removed!!!\n"); |
1084 | dev_ops_remove_minor(&fd_ops, | |
1085 | /*dkunitmask() | dkmakeslice(-1) | dkmakepart(128|64),*/ | |
983105c1 MD |
1086 | dkmakeminor(fd->fdu, WHOLE_DISK_SLICE, 128)); |
1087 | disk_invalidate(&fd->disk); | |
1088 | disk_destroy(&fd->disk); | |
1089 | devstat_remove_entry(&fd->device_stats); | |
e8b273ff | 1090 | callout_stop(&fd->toffhandle); |
4fc46503 | 1091 | callout_stop(&fd->motor); |
984263bc MD |
1092 | |
1093 | return (0); | |
1094 | } | |
1095 | ||
1096 | static device_method_t fd_methods[] = { | |
1097 | /* Device interface */ | |
1098 | DEVMETHOD(device_probe, fd_probe), | |
1099 | DEVMETHOD(device_attach, fd_attach), | |
1100 | DEVMETHOD(device_detach, fd_detach), | |
1101 | DEVMETHOD(device_shutdown, bus_generic_shutdown), | |
1102 | DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ | |
1103 | DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ | |
1104 | ||
d3c9c58e | 1105 | DEVMETHOD_END |
984263bc MD |
1106 | }; |
1107 | ||
1108 | static driver_t fd_driver = { | |
1109 | "fd", | |
1110 | fd_methods, | |
1111 | sizeof(struct fd_data) | |
1112 | }; | |
1113 | ||
aa2b9d05 | 1114 | DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, NULL, NULL); |
984263bc MD |
1115 | |
1116 | /****************************************************************************/ | |
1117 | /* motor control stuff */ | |
1118 | /* remember to not deselect the drive we're working on */ | |
1119 | /****************************************************************************/ | |
1120 | static void | |
1121 | set_motor(struct fdc_data *fdc, int fdsu, int turnon) | |
1122 | { | |
1123 | int fdout = fdc->fdout; | |
1124 | int needspecify = 0; | |
1125 | ||
1126 | if(turnon) { | |
1127 | fdout &= ~FDO_FDSEL; | |
1128 | fdout |= (FDO_MOEN0 << fdsu) + fdsu; | |
1129 | } else | |
1130 | fdout &= ~(FDO_MOEN0 << fdsu); | |
1131 | ||
1132 | if(!turnon | |
1133 | && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) | |
1134 | /* gonna turn off the last drive, put FDC to bed */ | |
1135 | fdout &= ~ (FDO_FRST|FDO_FDMAEN); | |
1136 | else { | |
1137 | /* make sure controller is selected and specified */ | |
1138 | if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) | |
1139 | needspecify = 1; | |
1140 | fdout |= (FDO_FRST|FDO_FDMAEN); | |
1141 | } | |
1142 | ||
1143 | fdout_wr(fdc, fdout); | |
1144 | fdc->fdout = fdout; | |
1145 | TRACE1("[0x%x->FDOUT]", fdout); | |
1146 | ||
1147 | if (needspecify) { | |
1148 | /* | |
1149 | * XXX | |
1150 | * special case: since we have just woken up the FDC | |
1151 | * from its sleep, we silently assume the command will | |
1152 | * be accepted, and do not test for a timeout | |
1153 | */ | |
1154 | (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, | |
1155 | NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), | |
1156 | 0); | |
1157 | if (fdc->flags & FDC_HAS_FIFO) | |
1158 | (void) enable_fifo(fdc); | |
1159 | } | |
1160 | } | |
1161 | ||
1162 | static void | |
1163 | fd_turnoff(void *xfd) | |
1164 | { | |
984263bc MD |
1165 | fd_p fd = xfd; |
1166 | ||
1167 | TRACE1("[fd%d: turnoff]", fd->fdu); | |
1168 | ||
fbe276c7 | 1169 | crit_enter(); |
984263bc MD |
1170 | /* |
1171 | * Don't turn off the motor yet if the drive is active. | |
1172 | * | |
1173 | * If we got here, this could only mean we missed an interrupt. | |
1174 | * This can e. g. happen on the Y-E Date PCMCIA floppy controller | |
1175 | * after a controller reset. Just schedule a pseudo-interrupt | |
1176 | * so the state machine gets re-entered. | |
1177 | */ | |
1178 | if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { | |
1179 | fdc_intr(fd->fdc); | |
fbe276c7 | 1180 | crit_exit(); |
984263bc MD |
1181 | return; |
1182 | } | |
1183 | ||
1184 | fd->flags &= ~FD_MOTOR; | |
1185 | set_motor(fd->fdc, fd->fdsu, TURNOFF); | |
fbe276c7 | 1186 | crit_exit(); |
984263bc MD |
1187 | } |
1188 | ||
1189 | static void | |
1190 | fd_motor_on(void *xfd) | |
1191 | { | |
984263bc MD |
1192 | fd_p fd = xfd; |
1193 | ||
fbe276c7 | 1194 | crit_enter(); |
984263bc MD |
1195 | fd->flags &= ~FD_MOTOR_WAIT; |
1196 | if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) | |
1197 | { | |
1198 | fdc_intr(fd->fdc); | |
1199 | } | |
fbe276c7 | 1200 | crit_exit(); |
984263bc MD |
1201 | } |
1202 | ||
1203 | static void | |
1204 | fd_turnon(fd_p fd) | |
1205 | { | |
1206 | if(!(fd->flags & FD_MOTOR)) | |
1207 | { | |
1208 | fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); | |
1209 | set_motor(fd->fdc, fd->fdsu, TURNON); | |
4fc46503 | 1210 | callout_reset(&fd->motor, hz, fd_motor_on, fd); |
984263bc MD |
1211 | } |
1212 | } | |
1213 | ||
1214 | static void | |
1215 | fdc_reset(fdc_p fdc) | |
1216 | { | |
1217 | /* Try a reset, keep motor on */ | |
1218 | fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); | |
1219 | TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); | |
1220 | DELAY(100); | |
1221 | /* enable FDC, but defer interrupts a moment */ | |
1222 | fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN); | |
1223 | TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); | |
1224 | DELAY(100); | |
1225 | fdout_wr(fdc, fdc->fdout); | |
1226 | TRACE1("[0x%x->FDOUT]", fdc->fdout); | |
1227 | ||
1228 | /* XXX after a reset, silently believe the FDC will accept commands */ | |
1229 | (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, | |
1230 | NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), | |
1231 | 0); | |
1232 | if (fdc->flags & FDC_HAS_FIFO) | |
1233 | (void) enable_fifo(fdc); | |
1234 | } | |
1235 | ||
1236 | /****************************************************************************/ | |
1237 | /* fdc in/out */ | |
1238 | /****************************************************************************/ | |
1239 | /* | |
1240 | * FDC IO functions, take care of the main status register, timeout | |
1241 | * in case the desired status bits are never set. | |
1242 | * | |
1243 | * These PIO loops initially start out with short delays between | |
1244 | * each iteration in the expectation that the required condition | |
1245 | * is usually met quickly, so it can be handled immediately. After | |
1246 | * about 1 ms, stepping is increased to achieve a better timing | |
1247 | * accuracy in the calls to DELAY(). | |
1248 | */ | |
1249 | static int | |
1250 | fd_in(struct fdc_data *fdc, int *ptr) | |
1251 | { | |
1252 | int i, j, step; | |
1253 | ||
1254 | for (j = 0, step = 1; | |
1255 | (i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && | |
1256 | j < FDSTS_TIMEOUT; | |
1257 | j += step) { | |
1258 | if (i == NE7_RQM) | |
1259 | return (fdc_err(fdc, "ready for output in input\n")); | |
1260 | if (j == 1000) | |
1261 | step = 1000; | |
1262 | DELAY(step); | |
1263 | } | |
1264 | if (j >= FDSTS_TIMEOUT) | |
1265 | return (fdc_err(fdc, bootverbose? "input ready timeout\n": 0)); | |
1266 | #ifdef FDC_DEBUG | |
1267 | i = fddata_rd(fdc); | |
1268 | TRACE1("[FDDATA->0x%x]", (unsigned char)i); | |
1269 | *ptr = i; | |
1270 | return (0); | |
1271 | #else /* !FDC_DEBUG */ | |
1272 | i = fddata_rd(fdc); | |
1273 | if (ptr) | |
1274 | *ptr = i; | |
1275 | return (0); | |
1276 | #endif /* FDC_DEBUG */ | |
1277 | } | |
1278 | ||
1279 | static int | |
1280 | out_fdc(struct fdc_data *fdc, int x) | |
1281 | { | |
1282 | int i, j, step; | |
1283 | ||
1284 | for (j = 0, step = 1; | |
1285 | (i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != NE7_RQM && | |
1286 | j < FDSTS_TIMEOUT; | |
1287 | j += step) { | |
1288 | if (i == (NE7_DIO|NE7_RQM)) | |
1289 | return (fdc_err(fdc, "ready for input in output\n")); | |
1290 | if (j == 1000) | |
1291 | step = 1000; | |
1292 | DELAY(step); | |
1293 | } | |
1294 | if (j >= FDSTS_TIMEOUT) | |
1295 | return (fdc_err(fdc, bootverbose? "output ready timeout\n": 0)); | |
1296 | ||
1297 | /* Send the command and return */ | |
1298 | fddata_wr(fdc, x); | |
1299 | TRACE1("[0x%x->FDDATA]", x); | |
1300 | return (0); | |
1301 | } | |
1302 | ||
1303 | /****************************************************************************/ | |
1304 | /* fdopen/fdclose */ | |
1305 | /****************************************************************************/ | |
1306 | int | |
fef8985e | 1307 | Fdopen(struct dev_open_args *ap) |
984263bc | 1308 | { |
b13267a5 | 1309 | cdev_t dev = ap->a_head.a_dev; |
983105c1 MD |
1310 | fdu_t fdu = dkunit(dev); |
1311 | struct disk_info info; | |
1312 | struct fd_type *ft; | |
1313 | int type; | |
1314 | int changetype; | |
984263bc MD |
1315 | fd_p fd; |
1316 | fdc_p fdc; | |
1317 | ||
1318 | /* check bounds */ | |
914b334c | 1319 | if ((fd = devclass_get_softc(fd_devclass, fdu)) == NULL) |
984263bc MD |
1320 | return (ENXIO); |
1321 | fdc = fd->fdc; | |
1322 | if ((fdc == NULL) || (fd->type == NO_TYPE)) | |
1323 | return (ENXIO); | |
983105c1 MD |
1324 | |
1325 | /* | |
1326 | * Figure out the type of floppy. There are special whole-disk-device | |
1327 | * overrides that will override the current type. | |
1328 | */ | |
1329 | type = dkpart(dev); | |
1330 | if (type == WHOLE_SLICE_PART) { | |
1331 | type = fd->type; /* do not change selected type data */ | |
1332 | changetype = 0; | |
1333 | } else if (type > 128) { | |
1334 | type -= 128; /* set to specific format */ | |
1335 | changetype = 1; | |
1336 | } else { | |
1337 | type = fd->type; /* reset to default */ | |
1338 | changetype = 1; | |
1339 | } | |
984263bc MD |
1340 | if (type > NUMDENS) |
1341 | return (ENXIO); | |
983105c1 | 1342 | if (type != fd->type) { |
984263bc MD |
1343 | /* |
1344 | * For each type of basic drive, make sure we are trying | |
1345 | * to open a type it can do, | |
1346 | */ | |
983105c1 MD |
1347 | switch (fd->type) { |
1348 | case FD_360: | |
1349 | return (ENXIO); | |
1350 | case FD_720: | |
1351 | if ( type != FD_820 | |
1352 | && type != FD_800 | |
1353 | && type != FD_640 | |
1354 | ) | |
984263bc | 1355 | return (ENXIO); |
983105c1 MD |
1356 | break; |
1357 | case FD_1200: | |
1358 | switch (type) { | |
1359 | case FD_1480: | |
1360 | type = FD_1480in5_25; | |
1361 | break; | |
1362 | case FD_1440: | |
1363 | type = FD_1440in5_25; | |
1364 | break; | |
1365 | case FD_1232: | |
1366 | break; | |
1367 | case FD_820: | |
1368 | type = FD_820in5_25; | |
1369 | break; | |
1370 | case FD_800: | |
1371 | type = FD_800in5_25; | |
1372 | break; | |
984263bc | 1373 | case FD_720: |
983105c1 | 1374 | type = FD_720in5_25; |
984263bc | 1375 | break; |
983105c1 MD |
1376 | case FD_640: |
1377 | type = FD_640in5_25; | |
984263bc | 1378 | break; |
983105c1 MD |
1379 | case FD_360: |
1380 | type = FD_360in5_25; | |
984263bc | 1381 | break; |
983105c1 MD |
1382 | default: |
1383 | return(ENXIO); | |
984263bc | 1384 | } |
983105c1 MD |
1385 | break; |
1386 | case FD_1440: | |
1387 | if ( type != FD_1720 | |
1388 | && type != FD_1480 | |
1389 | && type != FD_1200 | |
1390 | && type != FD_820 | |
1391 | && type != FD_800 | |
1392 | && type != FD_720 | |
1393 | && type != FD_640 | |
1394 | ) | |
1395 | return(ENXIO); | |
1396 | break; | |
984263bc MD |
1397 | } |
1398 | } | |
983105c1 MD |
1399 | |
1400 | /* | |
1401 | * fd->type is the basic drive type, not the current format | |
1402 | * we are reading. We only change the type when opening the | |
1403 | * whole-slice-partition | |
1404 | */ | |
1405 | if (changetype) | |
1406 | fd->ft = fd_types[type - 1]; | |
984263bc | 1407 | fd->flags |= FD_OPEN; |
983105c1 | 1408 | |
984263bc MD |
1409 | /* |
1410 | * Clearing the DMA overrun counter at open time is a bit messy. | |
1411 | * Since we're only managing one counter per controller, opening | |
1412 | * the second drive could mess it up. Anyway, if the DMA overrun | |
1413 | * condition is really persistent, it will eventually time out | |
1414 | * still. OTOH, clearing it here will ensure we'll at least start | |
1415 | * trying again after a previous (maybe even long ago) failure. | |
1416 | * Also, this is merely a stop-gap measure only that should not | |
1417 | * happen during normal operation, so we can tolerate it to be a | |
1418 | * bit sloppy about this. | |
1419 | */ | |
1420 | fdc->dma_overruns = 0; | |
1421 | ||
983105c1 MD |
1422 | /* |
1423 | * Set disk parameters for the disk management layer. | |
1424 | * | |
1425 | * Note that we do not set RAWEXTENSIONS here. We override | |
1426 | * the minor numbers in the raw-extension range and handle them | |
1427 | * directly. | |
1428 | */ | |
1429 | bzero(&info, sizeof(info)); | |
1430 | ft = &fd->ft; | |
1431 | info.d_media_blksize = 128 << ft->secsize; | |
1432 | info.d_media_blocks = ft->size; | |
1433 | info.d_dsflags = DSO_COMPATPARTA | DSO_COMPATMBR; | |
1434 | info.d_nheads = ft->heads; | |
1435 | info.d_secpertrack = ft->sectrac; | |
1436 | info.d_secpercyl = ft->sectrac * ft->heads; | |
1437 | info.d_ncylinders = ft->size / info.d_secpercyl; | |
1438 | disk_setdiskinfo(&fd->disk, &info); | |
1439 | ||
984263bc MD |
1440 | return 0; |
1441 | } | |
1442 | ||
1443 | int | |
fef8985e | 1444 | fdclose(struct dev_close_args *ap) |
984263bc | 1445 | { |
b13267a5 | 1446 | cdev_t dev = ap->a_head.a_dev; |
983105c1 | 1447 | fdu_t fdu = dkunit(dev); |
984263bc MD |
1448 | struct fd_data *fd; |
1449 | ||
1450 | fd = devclass_get_softc(fd_devclass, fdu); | |
1451 | fd->flags &= ~FD_OPEN; | |
1452 | fd->options &= ~(FDOPT_NORETRY | FDOPT_NOERRLOG); | |
1453 | ||
1454 | return (0); | |
1455 | } | |
1456 | ||
1457 | /****************************************************************************/ | |
1458 | /* fdstrategy */ | |
1459 | /****************************************************************************/ | |
fef8985e MD |
1460 | int |
1461 | fdstrategy(struct dev_strategy_args *ap) | |
984263bc | 1462 | { |
b13267a5 | 1463 | cdev_t dev = ap->a_head.a_dev; |
fef8985e | 1464 | struct bio *bio = ap->a_bio; |
81b5c339 | 1465 | struct buf *bp = bio->bio_buf; |
984263bc | 1466 | unsigned nblocks, blknum, cando; |
984263bc MD |
1467 | fdu_t fdu; |
1468 | fdc_p fdc; | |
1469 | fd_p fd; | |
1470 | size_t fdblk; | |
1471 | ||
983105c1 | 1472 | fdu = dkunit(dev); |
984263bc | 1473 | fd = devclass_get_softc(fd_devclass, fdu); |
914b334c | 1474 | if (fd == NULL) |
984263bc | 1475 | panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", |
81b5c339 | 1476 | (u_long)major(dev), (u_long)minor(dev)); |
984263bc MD |
1477 | fdc = fd->fdc; |
1478 | if (fd->type == NO_TYPE) { | |
1479 | bp->b_error = ENXIO; | |
1480 | bp->b_flags |= B_ERROR; | |
1481 | goto bad; | |
0fdb7d01 | 1482 | } |
984263bc | 1483 | |
983105c1 | 1484 | fdblk = 128 << (fd->ft.secsize); |
10f3fee5 | 1485 | if (bp->b_cmd != BUF_CMD_FORMAT) { |
54078292 | 1486 | if (bio->bio_offset < 0) { |
e3869ec7 | 1487 | kprintf( |
664f5072 | 1488 | "fd%d: fdstrat: bad request offset = %"PRId64", bcount = %d\n", |
54078292 | 1489 | fdu, bio->bio_offset, bp->b_bcount); |
984263bc MD |
1490 | bp->b_error = EINVAL; |
1491 | bp->b_flags |= B_ERROR; | |
1492 | goto bad; | |
1493 | } | |
1494 | if ((bp->b_bcount % fdblk) != 0) { | |
1495 | bp->b_error = EINVAL; | |
1496 | bp->b_flags |= B_ERROR; | |
1497 | goto bad; | |
1498 | } | |
1499 | } | |
1500 | ||
1501 | /* | |
1502 | * Set up block calculations. | |
1503 | */ | |
54078292 | 1504 | if (bio->bio_offset > 20000000LL * fdblk) { |
984263bc MD |
1505 | /* |
1506 | * Reject unreasonably high block number, prevent the | |
1507 | * multiplication below from overflowing. | |
1508 | */ | |
1509 | bp->b_error = EINVAL; | |
1510 | bp->b_flags |= B_ERROR; | |
1511 | goto bad; | |
1512 | } | |
54078292 | 1513 | blknum = (unsigned)(bio->bio_offset / fdblk); |
983105c1 | 1514 | nblocks = fd->ft.size; |
984263bc MD |
1515 | bp->b_resid = 0; |
1516 | if (blknum + (bp->b_bcount / fdblk) > nblocks) { | |
1517 | if (blknum <= nblocks) { | |
1518 | cando = (nblocks - blknum) * fdblk; | |
1519 | bp->b_resid = bp->b_bcount - cando; | |
1520 | if (cando == 0) | |
1521 | goto bad; /* not actually bad but EOF */ | |
1522 | } else { | |
1523 | bp->b_error = EINVAL; | |
1524 | bp->b_flags |= B_ERROR; | |
1525 | goto bad; | |
1526 | } | |
1527 | } | |
fbe276c7 | 1528 | crit_enter(); |
81b5c339 MD |
1529 | bio->bio_driver_info = dev; |
1530 | bioqdisksort(&fdc->bio_queue, bio); | |
e8b273ff | 1531 | callout_stop(&fd->toffhandle); |
984263bc MD |
1532 | |
1533 | /* Tell devstat we are starting on the transaction */ | |
1534 | devstat_start_transaction(&fd->device_stats); | |
cfa47be2 | 1535 | #if 0 |
984263bc | 1536 | device_busy(fd->dev); |
cfa47be2 | 1537 | #endif |
984263bc | 1538 | fdstart(fdc); |
fbe276c7 | 1539 | crit_exit(); |
fef8985e | 1540 | return(0); |
984263bc MD |
1541 | |
1542 | bad: | |
81b5c339 | 1543 | biodone(bio); |
fef8985e | 1544 | return(0); |
984263bc MD |
1545 | } |
1546 | ||
1547 | /***************************************************************\ | |
1548 | * fdstart * | |
1549 | * We have just queued something.. if the controller is not busy * | |
1550 | * then simulate the case where it has just finished a command * | |
1551 | * So that it (the interrupt routine) looks on the queue for more* | |
1552 | * work to do and picks up what we just added. * | |
1553 | * If the controller is already busy, we need do nothing, as it * | |
1554 | * will pick up our work when the present work completes * | |
1555 | \***************************************************************/ | |
1556 | static void | |
1557 | fdstart(struct fdc_data *fdc) | |
1558 | { | |
fbe276c7 | 1559 | crit_enter(); |
984263bc MD |
1560 | if(fdc->state == DEVIDLE) |
1561 | { | |
1562 | fdc_intr(fdc); | |
1563 | } | |
fbe276c7 | 1564 | crit_exit(); |
984263bc MD |
1565 | } |
1566 | ||
1567 | static void | |
1568 | fd_iotimeout(void *xfdc) | |
1569 | { | |
1570 | fdc_p fdc; | |
984263bc MD |
1571 | |
1572 | fdc = xfdc; | |
1573 | TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); | |
1574 | ||
1575 | /* | |
1576 | * Due to IBM's brain-dead design, the FDC has a faked ready | |
1577 | * signal, hardwired to ready == true. Thus, any command | |
1578 | * issued if there's no diskette in the drive will _never_ | |
1579 | * complete, and must be aborted by resetting the FDC. | |
1580 | * Many thanks, Big Blue! | |
1581 | * The FDC must not be reset directly, since that would | |
1582 | * interfere with the state machine. Instead, pretend that | |
1583 | * the command completed but was invalid. The state machine | |
1584 | * will reset the FDC and retry once. | |
1585 | */ | |
fbe276c7 | 1586 | crit_enter(); |
984263bc MD |
1587 | fdc->status[0] = NE7_ST0_IC_IV; |
1588 | fdc->flags &= ~FDC_STAT_VALID; | |
1589 | fdc->state = IOTIMEDOUT; | |
1590 | fdc_intr(fdc); | |
fbe276c7 | 1591 | crit_exit(); |
984263bc MD |
1592 | } |
1593 | ||
fbe276c7 | 1594 | /* just ensure it is running in a critical section */ |
984263bc MD |
1595 | static void |
1596 | fd_pseudointr(void *xfdc) | |
1597 | { | |
fbe276c7 | 1598 | crit_enter(); |
984263bc | 1599 | fdc_intr(xfdc); |
fbe276c7 | 1600 | crit_exit(); |
984263bc MD |
1601 | } |
1602 | ||
1603 | /***********************************************************************\ | |
1604 | * fdintr * | |
1605 | * keep calling the state machine until it returns a 0 * | |
1606 | * ALWAYS called at SPLBIO * | |
1607 | \***********************************************************************/ | |
1608 | static void | |
1609 | fdc_intr(void *xfdc) | |
1610 | { | |
1611 | fdc_p fdc = xfdc; | |
1612 | while(fdstate(fdc)) | |
1613 | ; | |
1614 | } | |
1615 | ||
1616 | /* | |
1617 | * magic pseudo-DMA initialization for YE FDC. Sets count and | |
1618 | * direction | |
1619 | */ | |
1620 | #define SET_BCDR(fdc,wr,cnt,port) \ | |
1621 | bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port, \ | |
1622 | ((cnt)-1) & 0xff); \ | |
1623 | bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port + 1, \ | |
1624 | ((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))); | |
1625 | ||
1626 | /* | |
1627 | * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy | |
1628 | */ | |
10f3fee5 | 1629 | static int fdcpio(fdc_p fdc, buf_cmd_t cmd, caddr_t addr, u_int count) |
984263bc MD |
1630 | { |
1631 | u_char *cptr = (u_char *)addr; | |
1632 | ||
10f3fee5 | 1633 | if (cmd == BUF_CMD_READ) { |
984263bc MD |
1634 | if (fdc->state != PIOREAD) { |
1635 | fdc->state = PIOREAD; | |
1636 | return(0); | |
0fdb7d01 | 1637 | } |
984263bc MD |
1638 | SET_BCDR(fdc, 0, count, 0); |
1639 | bus_space_read_multi_1(fdc->portt, fdc->porth, fdc->port_off + | |
1640 | FDC_YE_DATAPORT, cptr, count); | |
1641 | } else { | |
1642 | bus_space_write_multi_1(fdc->portt, fdc->porth, fdc->port_off + | |
1643 | FDC_YE_DATAPORT, cptr, count); | |
1644 | SET_BCDR(fdc, 0, count, 0); | |
0fdb7d01 | 1645 | } |
984263bc MD |
1646 | return(1); |
1647 | } | |
1648 | ||
1649 | /***********************************************************************\ | |
1650 | * The controller state machine. * | |
1651 | * if it returns a non zero value, it should be called again immediatly * | |
1652 | \***********************************************************************/ | |
1653 | static int | |
1654 | fdstate(fdc_p fdc) | |
1655 | { | |
1656 | int read, format, head, i, sec = 0, sectrac, st0, cyl, st3; | |
1657 | unsigned blknum = 0, b_cylinder = 0; | |
c755a6ff | 1658 | fdu_t fdu; |
984263bc | 1659 | fd_p fd; |
81b5c339 | 1660 | struct bio *bio; |
3a1b0fdc | 1661 | struct buf *bp; |
984263bc MD |
1662 | struct fd_formb *finfo = NULL; |
1663 | size_t fdblk; | |
b13267a5 | 1664 | cdev_t dev; |
81b5c339 MD |
1665 | |
1666 | bio = fdc->bio; | |
1667 | if (bio == NULL) { | |
1668 | bio = bioq_first(&fdc->bio_queue); | |
1669 | if (bio != NULL) { | |
1670 | bioq_remove(&fdc->bio_queue, bio); | |
1671 | fdc->bio = bio; | |
984263bc MD |
1672 | } |
1673 | } | |
81b5c339 | 1674 | if (bio == NULL) { |
984263bc MD |
1675 | /***********************************************\ |
1676 | * nothing left for this controller to do * | |
1677 | * Force into the IDLE state, * | |
1678 | \***********************************************/ | |
1679 | fdc->state = DEVIDLE; | |
1680 | if (fdc->fd) { | |
1681 | device_printf(fdc->fdc_dev, | |
1682 | "unexpected valid fd pointer\n"); | |
1683 | fdc->fd = (fd_p) 0; | |
1684 | fdc->fdu = -1; | |
1685 | } | |
1686 | TRACE1("[fdc%d IDLE]", fdc->fdcu); | |
1687 | return (0); | |
1688 | } | |
81b5c339 MD |
1689 | bp = bio->bio_buf; |
1690 | dev = bio->bio_driver_info; | |
1691 | ||
983105c1 | 1692 | fdu = dkunit(dev); |
984263bc | 1693 | fd = devclass_get_softc(fd_devclass, fdu); |
983105c1 | 1694 | fdblk = 128 << fd->ft.secsize; |
984263bc MD |
1695 | if (fdc->fd && (fd != fdc->fd)) |
1696 | device_printf(fd->dev, "confused fd pointers\n"); | |
10f3fee5 MD |
1697 | read = (bp->b_cmd == BUF_CMD_READ); |
1698 | format = (bp->b_cmd == BUF_CMD_FORMAT); | |
984263bc MD |
1699 | if (format) { |
1700 | finfo = (struct fd_formb *)bp->b_data; | |
1701 | fd->skip = (char *)&(finfo->fd_formb_cylno(0)) | |
1702 | - (char *)finfo; | |
1703 | } | |
1704 | if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { | |
54078292 MD |
1705 | blknum = (unsigned)(bio->bio_offset / fdblk) + |
1706 | fd->skip /fdblk; | |
983105c1 | 1707 | b_cylinder = blknum / (fd->ft.sectrac * fd->ft.heads); |
984263bc MD |
1708 | } |
1709 | TRACE1("fd%d", fdu); | |
1710 | TRACE1("[%s]", fdstates[fdc->state]); | |
1711 | TRACE1("(0x%x)", fd->flags); | |
e8b273ff | 1712 | callout_reset(&fd->toffhandle, 4 * hz, fd_turnoff, fd); |
984263bc MD |
1713 | switch (fdc->state) |
1714 | { | |
1715 | case DEVIDLE: | |
1716 | case FINDWORK: /* we have found new work */ | |
1717 | fdc->retry = 0; | |
1718 | fd->skip = 0; | |
1719 | fdc->fd = fd; | |
1720 | fdc->fdu = fdu; | |
983105c1 MD |
1721 | fdc->fdctl_wr(fdc, fd->ft.trans); |
1722 | TRACE1("[0x%x->FDCTL]", fd->ft.trans); | |
984263bc MD |
1723 | /*******************************************************\ |
1724 | * If the next drive has a motor startup pending, then * | |
1725 | * it will start up in its own good time * | |
1726 | \*******************************************************/ | |
1727 | if(fd->flags & FD_MOTOR_WAIT) { | |
1728 | fdc->state = MOTORWAIT; | |
1729 | return (0); /* come back later */ | |
1730 | } | |
1731 | /*******************************************************\ | |
1732 | * Maybe if it's not starting, it SHOULD be starting * | |
1733 | \*******************************************************/ | |
1734 | if (!(fd->flags & FD_MOTOR)) | |
1735 | { | |
1736 | fdc->state = MOTORWAIT; | |
1737 | fd_turnon(fd); | |
1738 | return (0); | |
1739 | } | |
1740 | else /* at least make sure we are selected */ | |
1741 | { | |
1742 | set_motor(fdc, fd->fdsu, TURNON); | |
1743 | } | |
1744 | if (fdc->flags & FDC_NEEDS_RESET) { | |
1745 | fdc->state = RESETCTLR; | |
1746 | fdc->flags &= ~FDC_NEEDS_RESET; | |
1747 | } else | |
1748 | fdc->state = DOSEEK; | |
1749 | break; | |
1750 | case DOSEEK: | |
1751 | if (b_cylinder == (unsigned)fd->track) | |
1752 | { | |
1753 | fdc->state = SEEKCOMPLETE; | |
1754 | break; | |
1755 | } | |
1756 | if (fd_cmd(fdc, 3, NE7CMD_SEEK, | |
983105c1 | 1757 | fd->fdsu, b_cylinder * fd->ft.steptrac, |
984263bc MD |
1758 | 0)) |
1759 | { | |
1760 | /* | |
1761 | * seek command not accepted, looks like | |
1762 | * the FDC went off to the Saints... | |
1763 | */ | |
1764 | fdc->retry = 6; /* try a reset */ | |
1765 | return(retrier(fdc)); | |
1766 | } | |
1767 | fd->track = FD_NO_TRACK; | |
1768 | fdc->state = SEEKWAIT; | |
1769 | return(0); /* will return later */ | |
1770 | case SEEKWAIT: | |
1771 | /* allow heads to settle */ | |
4fc46503 JS |
1772 | callout_reset(&fdc->pseudointr_ch, hz / 16, |
1773 | fd_pseudointr, fdc); | |
984263bc MD |
1774 | fdc->state = SEEKCOMPLETE; |
1775 | return(0); /* will return later */ | |
1776 | case SEEKCOMPLETE : /* SEEK DONE, START DMA */ | |
1777 | /* Make sure seek really happened*/ | |
1778 | if(fd->track == FD_NO_TRACK) { | |
983105c1 | 1779 | int descyl = b_cylinder * fd->ft.steptrac; |
984263bc MD |
1780 | do { |
1781 | /* | |
1782 | * This might be a "ready changed" interrupt, | |
1783 | * which cannot really happen since the | |
1784 | * RDY pin is hardwired to + 5 volts. This | |
1785 | * generally indicates a "bouncing" intr | |
1786 | * line, so do one of the following: | |
1787 | * | |
1788 | * When running on an enhanced FDC that is | |
1789 | * known to not go stuck after responding | |
1790 | * with INVALID, fetch all interrupt states | |
1791 | * until seeing either an INVALID or a | |
1792 | * real interrupt condition. | |
1793 | * | |
1794 | * When running on a dumb old NE765, give | |
1795 | * up immediately. The controller will | |
1796 | * provide up to four dummy RC interrupt | |
1797 | * conditions right after reset (for the | |
1798 | * corresponding four drives), so this is | |
1799 | * our only chance to get notice that it | |
1800 | * was not the FDC that caused the interrupt. | |
1801 | */ | |
1802 | if (fd_sense_int(fdc, &st0, &cyl) | |
1803 | == FD_NOT_VALID) | |
1804 | return 0; | |
1805 | if(fdc->fdct == FDC_NE765 | |
1806 | && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) | |
1807 | return 0; /* hope for a real intr */ | |
1808 | } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); | |
1809 | ||
1810 | if (0 == descyl) { | |
1811 | int failed = 0; | |
1812 | /* | |
1813 | * seek to cyl 0 requested; make sure we are | |
1814 | * really there | |
1815 | */ | |
1816 | if (fd_sense_drive_status(fdc, &st3)) | |
1817 | failed = 1; | |
1818 | if ((st3 & NE7_ST3_T0) == 0) { | |
e3869ec7 | 1819 | kprintf( |
c3783d8f | 1820 | "fd%d: Seek to cyl 0, but not really there (ST3 = %pb%i)\n", |
1821 | fdu, NE7_ST3BITS, st3); | |
984263bc MD |
1822 | failed = 1; |
1823 | } | |
1824 | ||
1825 | if (failed) { | |
1826 | if(fdc->retry < 3) | |
1827 | fdc->retry = 3; | |
1828 | return (retrier(fdc)); | |
1829 | } | |
1830 | } | |
1831 | ||
1832 | if (cyl != descyl) { | |
e3869ec7 | 1833 | kprintf( |
984263bc MD |
1834 | "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", |
1835 | fdu, descyl, cyl, st0); | |
1836 | if (fdc->retry < 3) | |
1837 | fdc->retry = 3; | |
1838 | return (retrier(fdc)); | |
1839 | } | |
1840 | } | |
1841 | ||
1842 | fd->track = b_cylinder; | |
10f3fee5 MD |
1843 | if (!(fdc->flags & FDC_NODMA)) { |
1844 | isa_dmastart(isa_dmabp(bp), | |
1845 | bp->b_data+fd->skip, | |
984263bc | 1846 | format ? bp->b_bcount : fdblk, fdc->dmachan); |
10f3fee5 | 1847 | } |
983105c1 MD |
1848 | sectrac = fd->ft.sectrac; |
1849 | sec = blknum % (sectrac * fd->ft.heads); | |
984263bc MD |
1850 | head = sec / sectrac; |
1851 | sec = sec % sectrac + 1; | |
1852 | fd->hddrv = ((head&1)<<2)+fdu; | |
1853 | ||
1854 | if(format || !read) | |
1855 | { | |
1856 | /* make sure the drive is writable */ | |
1857 | if(fd_sense_drive_status(fdc, &st3) != 0) | |
1858 | { | |
1859 | /* stuck controller? */ | |
1860 | if (!(fdc->flags & FDC_NODMA)) | |
10f3fee5 | 1861 | isa_dmadone(isa_dmabp(bp), |
984263bc MD |
1862 | bp->b_data + fd->skip, |
1863 | format ? bp->b_bcount : fdblk, | |
1864 | fdc->dmachan); | |
1865 | fdc->retry = 6; /* reset the beast */ | |
1866 | return (retrier(fdc)); | |
1867 | } | |
1868 | if(st3 & NE7_ST3_WP) | |
1869 | { | |
1870 | /* | |
1871 | * XXX YES! this is ugly. | |
1872 | * in order to force the current operation | |
1873 | * to fail, we will have to fake an FDC | |
1874 | * error - all error handling is done | |
1875 | * by the retrier() | |
1876 | */ | |
1877 | fdc->status[0] = NE7_ST0_IC_AT; | |
1878 | fdc->status[1] = NE7_ST1_NW; | |
1879 | fdc->status[2] = 0; | |
1880 | fdc->status[3] = fd->track; | |
1881 | fdc->status[4] = head; | |
1882 | fdc->status[5] = sec; | |
1883 | fdc->retry = 8; /* break out immediately */ | |
1884 | fdc->state = IOTIMEDOUT; /* not really... */ | |
1885 | return (1); | |
1886 | } | |
1887 | } | |
1888 | ||
1889 | if (format) { | |
1890 | if (fdc->flags & FDC_NODMA) { | |
1891 | /* | |
1892 | * This seems to be necessary for | |
1893 | * whatever obscure reason; if we omit | |
1894 | * it, we end up filling the sector ID | |
1895 | * fields of the newly formatted track | |
1896 | * entirely with garbage, causing | |
1897 | * `wrong cylinder' errors all over | |
1898 | * the place when trying to read them | |
1899 | * back. | |
1900 | * | |
1901 | * Umpf. | |
1902 | */ | |
1903 | SET_BCDR(fdc, 1, bp->b_bcount, 0); | |
1904 | ||
10f3fee5 | 1905 | (void)fdcpio(fdc,bp->b_cmd, |
984263bc MD |
1906 | bp->b_data+fd->skip, |
1907 | bp->b_bcount); | |
1908 | ||
1909 | } | |
1910 | /* formatting */ | |
1911 | if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, | |
1912 | finfo->fd_formb_secshift, | |
1913 | finfo->fd_formb_nsecs, | |
1914 | finfo->fd_formb_gaplen, | |
1915 | finfo->fd_formb_fillbyte, 0)) { | |
1916 | /* controller fell over */ | |
1917 | if (!(fdc->flags & FDC_NODMA)) | |
10f3fee5 | 1918 | isa_dmadone(isa_dmabp(bp), |
984263bc MD |
1919 | bp->b_data + fd->skip, |
1920 | format ? bp->b_bcount : fdblk, | |
1921 | fdc->dmachan); | |
1922 | fdc->retry = 6; | |
1923 | return (retrier(fdc)); | |
1924 | } | |
1925 | } else { | |
1926 | if (fdc->flags & FDC_NODMA) { | |
1927 | /* | |
1928 | * this seems to be necessary even when | |
1929 | * reading data | |
1930 | */ | |
1931 | SET_BCDR(fdc, 1, fdblk, 0); | |
1932 | ||
1933 | /* | |
1934 | * perform the write pseudo-DMA before | |
1935 | * the WRITE command is sent | |
1936 | */ | |
1937 | if (!read) | |
10f3fee5 | 1938 | (void)fdcpio(fdc,bp->b_cmd, |
984263bc MD |
1939 | bp->b_data+fd->skip, |
1940 | fdblk); | |
1941 | } | |
1942 | if (fd_cmd(fdc, 9, | |
1943 | (read ? NE7CMD_READ : NE7CMD_WRITE), | |
1944 | head << 2 | fdu, /* head & unit */ | |
1945 | fd->track, /* track */ | |
1946 | head, | |
1947 | sec, /* sector + 1 */ | |
983105c1 | 1948 | fd->ft.secsize, /* sector size */ |
984263bc | 1949 | sectrac, /* sectors/track */ |
983105c1 MD |
1950 | fd->ft.gap, /* gap size */ |
1951 | fd->ft.datalen, /* data length */ | |
984263bc MD |
1952 | 0)) { |
1953 | /* the beast is sleeping again */ | |
1954 | if (!(fdc->flags & FDC_NODMA)) | |
10f3fee5 | 1955 | isa_dmadone(isa_dmabp(bp), |
984263bc MD |
1956 | bp->b_data + fd->skip, |
1957 | format ? bp->b_bcount : fdblk, | |
1958 | fdc->dmachan); | |
1959 | fdc->retry = 6; | |
1960 | return (retrier(fdc)); | |
1961 | } | |
1962 | } | |
1963 | if (fdc->flags & FDC_NODMA) | |
1964 | /* | |
1965 | * if this is a read, then simply await interrupt | |
1966 | * before performing PIO | |
1967 | */ | |
10f3fee5 | 1968 | if (read && !fdcpio(fdc,bp->b_cmd, |
984263bc | 1969 | bp->b_data+fd->skip,fdblk)) { |
e8b273ff MD |
1970 | callout_reset(&fd->tohandle, hz, |
1971 | fd_iotimeout, fdc); | |
984263bc | 1972 | return(0); /* will return later */ |
0fdb7d01 | 1973 | } |
984263bc MD |
1974 | |
1975 | /* | |
1976 | * write (or format) operation will fall through and | |
1977 | * await completion interrupt | |
1978 | */ | |
1979 | fdc->state = IOCOMPLETE; | |
e8b273ff | 1980 | callout_reset(&fd->tohandle, hz, fd_iotimeout, fdc); |
984263bc MD |
1981 | return (0); /* will return later */ |
1982 | case PIOREAD: | |
1983 | /* | |
1984 | * actually perform the PIO read. The IOCOMPLETE case | |
1985 | * removes the timeout for us. | |
1986 | */ | |
10f3fee5 | 1987 | (void)fdcpio(fdc,bp->b_cmd,bp->b_data+fd->skip,fdblk); |
984263bc MD |
1988 | fdc->state = IOCOMPLETE; |
1989 | /* FALLTHROUGH */ | |
1990 | case IOCOMPLETE: /* IO DONE, post-analyze */ | |
e8b273ff | 1991 | callout_stop(&fd->tohandle); |
984263bc MD |
1992 | |
1993 | if (fd_read_status(fdc, fd->fdsu)) { | |
10f3fee5 MD |
1994 | if (!(fdc->flags & FDC_NODMA)) { |
1995 | isa_dmadone(isa_dmabp(bp), | |
1996 | bp->b_data + fd->skip, | |
984263bc MD |
1997 | format ? bp->b_bcount : fdblk, |
1998 | fdc->dmachan); | |
10f3fee5 | 1999 | } |
984263bc MD |
2000 | if (fdc->retry < 6) |
2001 | fdc->retry = 6; /* force a reset */ | |
2002 | return (retrier(fdc)); | |
2003 | } | |
2004 | ||
2005 | fdc->state = IOTIMEDOUT; | |
2006 | ||
2007 | /* FALLTHROUGH */ | |
2008 | ||
2009 | case IOTIMEDOUT: | |
10f3fee5 MD |
2010 | if (!(fdc->flags & FDC_NODMA)) { |
2011 | isa_dmadone(isa_dmabp(bp), | |
2012 | bp->b_data + fd->skip, | |
984263bc | 2013 | format ? bp->b_bcount : fdblk, fdc->dmachan); |
10f3fee5 | 2014 | } |
984263bc MD |
2015 | if (fdc->status[0] & NE7_ST0_IC) { |
2016 | if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT | |
2017 | && fdc->status[1] & NE7_ST1_OR) { | |
2018 | /* | |
2019 | * DMA overrun. Someone hogged the bus and | |
2020 | * didn't release it in time for the next | |
2021 | * FDC transfer. | |
2022 | * | |
2023 | * We normally restart this without bumping | |
2024 | * the retry counter. However, in case | |
2025 | * something is seriously messed up (like | |
2026 | * broken hardware), we rather limit the | |
2027 | * number of retries so the IO operation | |
2028 | * doesn't block indefinately. | |
2029 | */ | |
2030 | if (fdc->dma_overruns++ < FDC_DMAOV_MAX) { | |
2031 | fdc->state = SEEKCOMPLETE; | |
2032 | return (1); | |
2033 | } /* else fall through */ | |
2034 | } | |
2035 | if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV | |
2036 | && fdc->retry < 6) | |
2037 | fdc->retry = 6; /* force a reset */ | |
2038 | else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT | |
2039 | && fdc->status[2] & NE7_ST2_WC | |
2040 | && fdc->retry < 3) | |
2041 | fdc->retry = 3; /* force recalibrate */ | |
2042 | return (retrier(fdc)); | |
2043 | } | |
2044 | /* All OK */ | |
2045 | /* Operation successful, retry DMA overruns again next time. */ | |
2046 | fdc->dma_overruns = 0; | |
2047 | fd->skip += fdblk; | |
2048 | if (!format && fd->skip < bp->b_bcount - bp->b_resid) { | |
2049 | /* set up next transfer */ | |
2050 | fdc->state = DOSEEK; | |
2051 | } else { | |
2052 | /* ALL DONE */ | |
2053 | fd->skip = 0; | |
81b5c339 | 2054 | fdc->bio = NULL; |
cfa47be2 | 2055 | #if 0 |
984263bc | 2056 | device_unbusy(fd->dev); |
cfa47be2 | 2057 | #endif |
984263bc | 2058 | devstat_end_transaction_buf(&fd->device_stats, bp); |
81b5c339 | 2059 | biodone(bio); |
984263bc MD |
2060 | fdc->fd = (fd_p) 0; |
2061 | fdc->fdu = -1; | |
2062 | fdc->state = FINDWORK; | |
2063 | } | |
2064 | return (1); | |
2065 | case RESETCTLR: | |
2066 | fdc_reset(fdc); | |
2067 | fdc->retry++; | |
2068 | fdc->state = RESETCOMPLETE; | |
2069 | return (0); | |
2070 | case RESETCOMPLETE: | |
2071 | /* | |
2072 | * Discard all the results from the reset so that they | |
2073 | * can't cause an unexpected interrupt later. | |
2074 | */ | |
2075 | for (i = 0; i < 4; i++) | |
2076 | (void)fd_sense_int(fdc, &st0, &cyl); | |
2077 | fdc->state = STARTRECAL; | |
2078 | /* Fall through. */ | |
2079 | case STARTRECAL: | |
2080 | if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { | |
2081 | /* arrgl */ | |
2082 | fdc->retry = 6; | |
2083 | return (retrier(fdc)); | |
2084 | } | |
2085 | fdc->state = RECALWAIT; | |
2086 | return (0); /* will return later */ | |
2087 | case RECALWAIT: | |
2088 | /* allow heads to settle */ | |
4fc46503 | 2089 | callout_reset(&fdc->pseudointr_ch, hz / 8, fd_pseudointr, fdc); |
984263bc MD |
2090 | fdc->state = RECALCOMPLETE; |
2091 | return (0); /* will return later */ | |
2092 | case RECALCOMPLETE: | |
2093 | do { | |
2094 | /* | |
2095 | * See SEEKCOMPLETE for a comment on this: | |
2096 | */ | |
2097 | if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) | |
2098 | return 0; | |
2099 | if(fdc->fdct == FDC_NE765 | |
2100 | && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) | |
2101 | return 0; /* hope for a real intr */ | |
2102 | } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); | |
2103 | if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) | |
2104 | { | |
2105 | if(fdc->retry > 3) | |
2106 | /* | |
2107 | * a recalibrate from beyond cylinder 77 | |
2108 | * will "fail" due to the FDC limitations; | |
2109 | * since people used to complain much about | |
2110 | * the failure message, try not logging | |
2111 | * this one if it seems to be the first | |
2112 | * time in a line | |
2113 | */ | |
c3783d8f | 2114 | kprintf("fd%d: recal failed ST0 %pb%i cyl %d\n", |
2115 | fdu, NE7_ST0BITS, st0, cyl); | |
984263bc MD |
2116 | if(fdc->retry < 3) fdc->retry = 3; |
2117 | return (retrier(fdc)); | |
2118 | } | |
2119 | fd->track = 0; | |
2120 | /* Seek (probably) necessary */ | |
2121 | fdc->state = DOSEEK; | |
2122 | return (1); /* will return immediatly */ | |
2123 | case MOTORWAIT: | |
2124 | if(fd->flags & FD_MOTOR_WAIT) | |
2125 | { | |
2126 | return (0); /* time's not up yet */ | |
2127 | } | |
2128 | if (fdc->flags & FDC_NEEDS_RESET) { | |
2129 | fdc->state = RESETCTLR; | |
2130 | fdc->flags &= ~FDC_NEEDS_RESET; | |
2131 | } else { | |
2132 | /* | |
2133 | * If all motors were off, then the controller was | |
2134 | * reset, so it has lost track of the current | |
2135 | * cylinder. Recalibrate to handle this case. | |
2136 | * But first, discard the results of the reset. | |
2137 | */ | |
2138 | fdc->state = RESETCOMPLETE; | |
2139 | } | |
2140 | return (1); /* will return immediatly */ | |
2141 | default: | |
2142 | device_printf(fdc->fdc_dev, "unexpected FD int->"); | |
2143 | if (fd_read_status(fdc, fd->fdsu) == 0) | |
e3869ec7 | 2144 | kprintf("FDC status :%x %x %x %x %x %x %x ", |
984263bc MD |
2145 | fdc->status[0], |
2146 | fdc->status[1], | |
2147 | fdc->status[2], | |
2148 | fdc->status[3], | |
2149 | fdc->status[4], | |
2150 | fdc->status[5], | |
2151 | fdc->status[6] ); | |
2152 | else | |
e3869ec7 | 2153 | kprintf("No status available "); |
984263bc MD |
2154 | if (fd_sense_int(fdc, &st0, &cyl) != 0) |
2155 | { | |
e3869ec7 | 2156 | kprintf("[controller is dead now]\n"); |
984263bc MD |
2157 | return (0); |
2158 | } | |
e3869ec7 | 2159 | kprintf("ST0 = %x, PCN = %x\n", st0, cyl); |
984263bc MD |
2160 | return (0); |
2161 | } | |
2162 | /*XXX confusing: some branches return immediately, others end up here*/ | |
2163 | return (1); /* Come back immediatly to new state */ | |
2164 | } | |
2165 | ||
2166 | static int | |
2167 | retrier(struct fdc_data *fdc) | |
2168 | { | |
81b5c339 | 2169 | struct bio *bio; |
3a1b0fdc | 2170 | struct buf *bp; |
984263bc | 2171 | struct fd_data *fd; |
b13267a5 | 2172 | cdev_t dev; |
984263bc MD |
2173 | int fdu; |
2174 | ||
81b5c339 MD |
2175 | bio = fdc->bio; |
2176 | bp = bio->bio_buf; | |
2177 | dev = bio->bio_driver_info; | |
984263bc MD |
2178 | |
2179 | /* XXX shouldn't this be cached somewhere? */ | |
983105c1 | 2180 | fdu = dkunit(dev); |
984263bc MD |
2181 | fd = devclass_get_softc(fd_devclass, fdu); |
2182 | if (fd->options & FDOPT_NORETRY) | |
2183 | goto fail; | |
2184 | ||
2185 | switch (fdc->retry) { | |
2186 | case 0: case 1: case 2: | |
2187 | fdc->state = SEEKCOMPLETE; | |
2188 | break; | |
2189 | case 3: case 4: case 5: | |
2190 | fdc->state = STARTRECAL; | |
2191 | break; | |
2192 | case 6: | |
2193 | fdc->state = RESETCTLR; | |
2194 | break; | |
2195 | case 7: | |
2196 | break; | |
2197 | default: | |
2198 | fail: | |
2199 | { | |
2200 | int printerror = (fd->options & FDOPT_NOERRLOG) == 0; | |
984263bc | 2201 | |
e4c9c0c8 | 2202 | if (printerror) { |
ee3ad2c9 MD |
2203 | /* |
2204 | * note: use the correct device for more | |
2205 | * verbose error reporting. | |
2206 | */ | |
983105c1 | 2207 | diskerr(bio, dev, |
ee3ad2c9 | 2208 | "hard error", LOG_PRINTF, |
a688b15c | 2209 | fdc->fd->skip); |
e4c9c0c8 | 2210 | } |
984263bc MD |
2211 | if (printerror) { |
2212 | if (fdc->flags & FDC_STAT_VALID) | |
e3869ec7 | 2213 | kprintf( |
c3783d8f | 2214 | " (ST0 %pb%i ST1 %pb%i ST2 %pb%i cyl %u hd %u sec %u)\n", |
2215 | NE7_ST0BITS, fdc->status[0], | |
2216 | NE7_ST1BITS, fdc->status[1], | |
2217 | NE7_ST2BITS, fdc->status[2], | |
984263bc MD |
2218 | fdc->status[3], fdc->status[4], |
2219 | fdc->status[5]); | |
2220 | else | |
e3869ec7 | 2221 | kprintf(" (No status)\n"); |
984263bc MD |
2222 | } |
2223 | } | |
2224 | bp->b_flags |= B_ERROR; | |
2225 | bp->b_error = EIO; | |
2226 | bp->b_resid += bp->b_bcount - fdc->fd->skip; | |
81b5c339 | 2227 | fdc->bio = NULL; |
984263bc | 2228 | fdc->fd->skip = 0; |
cfa47be2 | 2229 | #if 0 |
984263bc | 2230 | device_unbusy(fd->dev); |
cfa47be2 | 2231 | #endif |
984263bc | 2232 | devstat_end_transaction_buf(&fdc->fd->device_stats, bp); |
81b5c339 | 2233 | biodone(bio); |
984263bc MD |
2234 | fdc->state = FINDWORK; |
2235 | fdc->flags |= FDC_NEEDS_RESET; | |
2236 | fdc->fd = (fd_p) 0; | |
2237 | fdc->fdu = -1; | |
2238 | return (1); | |
2239 | } | |
2240 | fdc->retry++; | |
2241 | return (1); | |
2242 | } | |
2243 | ||
2244 | static int | |
b13267a5 | 2245 | fdformat(cdev_t dev, struct fd_formb *finfo, struct ucred *cred) |
984263bc MD |
2246 | { |
2247 | fdu_t fdu; | |
2248 | fd_p fd; | |
984263bc | 2249 | struct buf *bp; |
fbe276c7 | 2250 | int rv = 0; |
984263bc MD |
2251 | size_t fdblk; |
2252 | ||
983105c1 | 2253 | fdu = dkunit(dev); |
984263bc | 2254 | fd = devclass_get_softc(fd_devclass, fdu); |
983105c1 | 2255 | fdblk = 128 << fd->ft.secsize; |
984263bc MD |
2256 | |
2257 | /* set up a buffer header for fdstrategy() */ | |
014958fd | 2258 | bp = getpbuf(NULL); |
10f3fee5 | 2259 | bp->b_cmd = BUF_CMD_FORMAT; |
984263bc MD |
2260 | |
2261 | /* | |
2262 | * calculate a fake blkno, so fdstrategy() would initiate a | |
2263 | * seek to the requested cylinder | |
2264 | */ | |
54078292 | 2265 | bp->b_bio1.bio_offset = (off_t)(finfo->cyl * |
983105c1 MD |
2266 | (fd->ft.sectrac * fd->ft.heads) |
2267 | + finfo->head * fd->ft.sectrac) * fdblk; | |
81b5c339 | 2268 | bp->b_bio1.bio_driver_info = dev; |
ae8e83e6 MD |
2269 | bp->b_bio1.bio_flags |= BIO_SYNC; |
2270 | bp->b_bio1.bio_done = biodone_sync; | |
984263bc MD |
2271 | |
2272 | bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; | |
2273 | bp->b_data = (caddr_t)finfo; | |
2274 | ||
2275 | /* now do the format */ | |
81b5c339 | 2276 | dev_dstrategy(dev, &bp->b_bio1); |
984263bc MD |
2277 | |
2278 | /* ...and wait for it to complete */ | |
ae8e83e6 | 2279 | rv = biowait_timeout(&bp->b_bio1, "fdform", 20 * hz); |
984263bc MD |
2280 | if (rv == EWOULDBLOCK) { |
2281 | /* timed out */ | |
2282 | rv = EIO; | |
cfa47be2 | 2283 | #if 0 |
984263bc | 2284 | device_unbusy(fd->dev); |
cfa47be2 | 2285 | #endif |
81b5c339 | 2286 | biodone(&bp->b_bio1); |
984263bc MD |
2287 | } |
2288 | if (bp->b_flags & B_ERROR) | |
2289 | rv = bp->b_error; | |
2290 | /* | |
2291 | * allow the process to be swapped | |
2292 | */ | |
014958fd | 2293 | relpbuf(bp, NULL); |
984263bc MD |
2294 | return rv; |
2295 | } | |
2296 | ||
2297 | /* | |
2298 | * TODO: don't allocate buffer on stack. | |
2299 | */ | |
2300 | ||
2301 | static int | |
fef8985e | 2302 | fdioctl(struct dev_ioctl_args *ap) |
984263bc | 2303 | { |
b13267a5 | 2304 | cdev_t dev = ap->a_head.a_dev; |
983105c1 | 2305 | fdu_t fdu = dkunit(dev); |
984263bc | 2306 | fd_p fd = devclass_get_softc(fd_devclass, fdu); |
984263bc | 2307 | struct fdc_status *fsp; |
984263bc MD |
2308 | int error = 0; |
2309 | ||
fef8985e | 2310 | switch (ap->a_cmd) { |
984263bc | 2311 | case FD_FORM: |
fef8985e | 2312 | if ((ap->a_fflag & FWRITE) == 0) |
984263bc | 2313 | error = EBADF; /* must be opened for writing */ |
fef8985e | 2314 | else if (((struct fd_formb *)ap->a_data)->format_version != |
984263bc MD |
2315 | FD_FORMAT_VERSION) |
2316 | error = EINVAL; /* wrong version of formatting prog */ | |
2317 | else | |
fef8985e | 2318 | error = fdformat(dev, (struct fd_formb *)ap->a_data, ap->a_cred); |
984263bc MD |
2319 | break; |
2320 | ||
2321 | case FD_GTYPE: /* get drive type */ | |
983105c1 | 2322 | *(struct fd_type *)ap->a_data = fd->ft; |
984263bc MD |
2323 | break; |
2324 | ||
2325 | case FD_STYPE: /* set drive type */ | |
2326 | /* this is considered harmful; only allow for superuser */ | |
2b3f93ea | 2327 | if (caps_priv_check(ap->a_cred, SYSCAP_RESTRICTEDROOT)) |
984263bc | 2328 | return EPERM; |
983105c1 | 2329 | fd->ft = *(struct fd_type *)ap->a_data; |
984263bc MD |
2330 | break; |
2331 | ||
2332 | case FD_GOPTS: /* get drive options */ | |
fef8985e | 2333 | *(int *)ap->a_data = fd->options; |
984263bc MD |
2334 | break; |
2335 | ||
2336 | case FD_SOPTS: /* set drive options */ | |
fef8985e | 2337 | fd->options = *(int *)ap->a_data; |
984263bc MD |
2338 | break; |
2339 | ||
2340 | case FD_GSTAT: | |
fef8985e | 2341 | fsp = (struct fdc_status *)ap->a_data; |
984263bc MD |
2342 | if ((fd->fdc->flags & FDC_STAT_VALID) == 0) |
2343 | return EINVAL; | |
2344 | memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int)); | |
2345 | break; | |
2346 | ||
2347 | default: | |
2348 | error = ENOTTY; | |
2349 | break; | |
2350 | } | |
2351 | return (error); | |
2352 | } |