1 |
1275 |
phoenix |
/*
|
2 |
|
|
* Copyright (C) 1993-1996 Bas Laarhoven,
|
3 |
|
|
* (C) 1996-1997 Claus-Justus Heine.
|
4 |
|
|
|
5 |
|
|
This program is free software; you can redistribute it and/or modify
|
6 |
|
|
it under the terms of the GNU General Public License as published by
|
7 |
|
|
the Free Software Foundation; either version 2, or (at your option)
|
8 |
|
|
any later version.
|
9 |
|
|
|
10 |
|
|
This program is distributed in the hope that it will be useful,
|
11 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13 |
|
|
GNU General Public License for more details.
|
14 |
|
|
|
15 |
|
|
You should have received a copy of the GNU General Public License
|
16 |
|
|
along with this program; see the file COPYING. If not, write to
|
17 |
|
|
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
18 |
|
|
|
19 |
|
|
*
|
20 |
|
|
* $Source: /home/marcus/revision_ctrl_test/oc_cvs/cvs/or1k/linux/linux-2.4/drivers/char/ftape/lowlevel/fdc-io.c,v $
|
21 |
|
|
* $Revision: 1.1.1.1 $
|
22 |
|
|
* $Date: 2004-04-15 02:02:35 $
|
23 |
|
|
*
|
24 |
|
|
* This file contains the low-level floppy disk interface code
|
25 |
|
|
* for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for
|
26 |
|
|
* Linux.
|
27 |
|
|
*/
|
28 |
|
|
|
29 |
|
|
#include <linux/config.h> /* for CONFIG_FT_* */
|
30 |
|
|
#include <linux/errno.h>
|
31 |
|
|
#include <linux/sched.h>
|
32 |
|
|
#include <linux/ioport.h>
|
33 |
|
|
#include <linux/version.h>
|
34 |
|
|
#include <linux/interrupt.h>
|
35 |
|
|
#include <asm/system.h>
|
36 |
|
|
#include <asm/io.h>
|
37 |
|
|
#include <asm/dma.h>
|
38 |
|
|
#include <asm/irq.h>
|
39 |
|
|
|
40 |
|
|
#include <linux/ftape.h>
|
41 |
|
|
#include <linux/qic117.h>
|
42 |
|
|
#include "../lowlevel/ftape-tracing.h"
|
43 |
|
|
#include "../lowlevel/fdc-io.h"
|
44 |
|
|
#include "../lowlevel/fdc-isr.h"
|
45 |
|
|
#include "../lowlevel/ftape-io.h"
|
46 |
|
|
#include "../lowlevel/ftape-rw.h"
|
47 |
|
|
#include "../lowlevel/ftape-ctl.h"
|
48 |
|
|
#include "../lowlevel/ftape-calibr.h"
|
49 |
|
|
#include "../lowlevel/fc-10.h"
|
50 |
|
|
|
51 |
|
|
/* Global vars.
|
52 |
|
|
*/
|
53 |
|
|
int ftape_motor;
|
54 |
|
|
volatile int ftape_current_cylinder = -1;
|
55 |
|
|
volatile fdc_mode_enum fdc_mode = fdc_idle;
|
56 |
|
|
fdc_config_info fdc;
|
57 |
|
|
DECLARE_WAIT_QUEUE_HEAD(ftape_wait_intr);
|
58 |
|
|
|
59 |
|
|
unsigned int ft_fdc_base = CONFIG_FT_FDC_BASE;
|
60 |
|
|
unsigned int ft_fdc_irq = CONFIG_FT_FDC_IRQ;
|
61 |
|
|
unsigned int ft_fdc_dma = CONFIG_FT_FDC_DMA;
|
62 |
|
|
unsigned int ft_fdc_threshold = CONFIG_FT_FDC_THR; /* bytes */
|
63 |
|
|
unsigned int ft_fdc_rate_limit = CONFIG_FT_FDC_MAX_RATE; /* bits/sec */
|
64 |
|
|
int ft_probe_fc10 = CONFIG_FT_PROBE_FC10;
|
65 |
|
|
int ft_mach2 = CONFIG_FT_MACH2;
|
66 |
|
|
|
67 |
|
|
/* Local vars.
|
68 |
|
|
*/
|
69 |
|
|
static unsigned int fdc_calibr_count;
|
70 |
|
|
static unsigned int fdc_calibr_time;
|
71 |
|
|
static int fdc_status;
|
72 |
|
|
volatile __u8 fdc_head; /* FDC head from sector id */
|
73 |
|
|
volatile __u8 fdc_cyl; /* FDC track from sector id */
|
74 |
|
|
volatile __u8 fdc_sect; /* FDC sector from sector id */
|
75 |
|
|
static int fdc_data_rate = 500; /* data rate (Kbps) */
|
76 |
|
|
static int fdc_rate_code; /* data rate code (0 == 500 Kbps) */
|
77 |
|
|
static int fdc_seek_rate = 2; /* step rate (msec) */
|
78 |
|
|
static void (*do_ftape) (void);
|
79 |
|
|
static int fdc_fifo_state; /* original fifo setting - fifo enabled */
|
80 |
|
|
static int fdc_fifo_thr; /* original fifo setting - threshold */
|
81 |
|
|
static int fdc_lock_state; /* original lock setting - locked */
|
82 |
|
|
static int fdc_fifo_locked; /* has fifo && lock set ? */
|
83 |
|
|
static __u8 fdc_precomp; /* default precomp. value (nsec) */
|
84 |
|
|
static __u8 fdc_prec_code; /* fdc precomp. select code */
|
85 |
|
|
|
86 |
|
|
static char ftape_id[] = "ftape"; /* used by request irq and free irq */
|
87 |
|
|
|
88 |
|
|
void fdc_catch_stray_interrupts(int count)
|
89 |
|
|
{
|
90 |
|
|
unsigned long flags;
|
91 |
|
|
|
92 |
|
|
save_flags(flags);
|
93 |
|
|
cli();
|
94 |
|
|
if (count == 0) {
|
95 |
|
|
ft_expected_stray_interrupts = 0;
|
96 |
|
|
} else {
|
97 |
|
|
ft_expected_stray_interrupts += count;
|
98 |
|
|
}
|
99 |
|
|
restore_flags(flags);
|
100 |
|
|
}
|
101 |
|
|
|
102 |
|
|
/* Wait during a timeout period for a given FDC status.
|
103 |
|
|
* If usecs == 0 then just test status, else wait at least for usecs.
|
104 |
|
|
* Returns -ETIME on timeout. Function must be calibrated first !
|
105 |
|
|
*/
|
106 |
|
|
int fdc_wait(unsigned int usecs, __u8 mask, __u8 state)
|
107 |
|
|
{
|
108 |
|
|
int count_1 = (fdc_calibr_count * usecs +
|
109 |
|
|
fdc_calibr_count - 1) / fdc_calibr_time;
|
110 |
|
|
|
111 |
|
|
do {
|
112 |
|
|
fdc_status = inb_p(fdc.msr);
|
113 |
|
|
if ((fdc_status & mask) == state) {
|
114 |
|
|
return 0;
|
115 |
|
|
}
|
116 |
|
|
} while (count_1-- >= 0);
|
117 |
|
|
return -ETIME;
|
118 |
|
|
}
|
119 |
|
|
|
120 |
|
|
int fdc_ready_wait(unsigned int usecs)
|
121 |
|
|
{
|
122 |
|
|
return fdc_wait(usecs, FDC_DATA_READY | FDC_BUSY, FDC_DATA_READY);
|
123 |
|
|
}
|
124 |
|
|
|
125 |
|
|
/* Why can't we just use udelay()?
|
126 |
|
|
*/
|
127 |
|
|
static void fdc_usec_wait(unsigned int usecs)
|
128 |
|
|
{
|
129 |
|
|
fdc_wait(usecs, 0, 1); /* will always timeout ! */
|
130 |
|
|
}
|
131 |
|
|
|
132 |
|
|
int fdc_ready_out_wait(unsigned int usecs)
|
133 |
|
|
{
|
134 |
|
|
fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
|
135 |
|
|
return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_OUT_READY);
|
136 |
|
|
}
|
137 |
|
|
|
138 |
|
|
int fdc_ready_in_wait(unsigned int usecs)
|
139 |
|
|
{
|
140 |
|
|
fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
|
141 |
|
|
return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_IN_READY);
|
142 |
|
|
}
|
143 |
|
|
|
144 |
|
|
void fdc_wait_calibrate(void)
|
145 |
|
|
{
|
146 |
|
|
ftape_calibrate("fdc_wait",
|
147 |
|
|
fdc_usec_wait, &fdc_calibr_count, &fdc_calibr_time);
|
148 |
|
|
}
|
149 |
|
|
|
150 |
|
|
/* Wait for a (short) while for the FDC to become ready
|
151 |
|
|
* and transfer the next command byte.
|
152 |
|
|
* Return -ETIME on timeout on getting ready (depends on hardware!).
|
153 |
|
|
*/
|
154 |
|
|
static int fdc_write(const __u8 data)
|
155 |
|
|
{
|
156 |
|
|
fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
|
157 |
|
|
if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_IN_READY) < 0) {
|
158 |
|
|
return -ETIME;
|
159 |
|
|
} else {
|
160 |
|
|
outb(data, fdc.fifo);
|
161 |
|
|
return 0;
|
162 |
|
|
}
|
163 |
|
|
}
|
164 |
|
|
|
165 |
|
|
/* Wait for a (short) while for the FDC to become ready
|
166 |
|
|
* and transfer the next result byte.
|
167 |
|
|
* Return -ETIME if timeout on getting ready (depends on hardware!).
|
168 |
|
|
*/
|
169 |
|
|
static int fdc_read(__u8 * data)
|
170 |
|
|
{
|
171 |
|
|
fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
|
172 |
|
|
if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_OUT_READY) < 0) {
|
173 |
|
|
return -ETIME;
|
174 |
|
|
} else {
|
175 |
|
|
*data = inb(fdc.fifo);
|
176 |
|
|
return 0;
|
177 |
|
|
}
|
178 |
|
|
}
|
179 |
|
|
|
180 |
|
|
/* Output a cmd_len long command string to the FDC.
|
181 |
|
|
* The FDC should be ready to receive a new command or
|
182 |
|
|
* an error (EBUSY or ETIME) will occur.
|
183 |
|
|
*/
|
184 |
|
|
int fdc_command(const __u8 * cmd_data, int cmd_len)
|
185 |
|
|
{
|
186 |
|
|
int result = 0;
|
187 |
|
|
unsigned long flags;
|
188 |
|
|
int count = cmd_len;
|
189 |
|
|
int retry = 0;
|
190 |
|
|
#ifdef TESTING
|
191 |
|
|
static unsigned int last_time;
|
192 |
|
|
unsigned int time;
|
193 |
|
|
#endif
|
194 |
|
|
TRACE_FUN(ft_t_any);
|
195 |
|
|
|
196 |
|
|
fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
|
197 |
|
|
save_flags(flags);
|
198 |
|
|
cli();
|
199 |
|
|
#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,30)
|
200 |
|
|
if (!in_interrupt())
|
201 |
|
|
#else
|
202 |
|
|
if (!intr_count)
|
203 |
|
|
#endif
|
204 |
|
|
/* Yes, I know, too much comments inside this function
|
205 |
|
|
* ...
|
206 |
|
|
*
|
207 |
|
|
* Yet another bug in the original driver. All that
|
208 |
|
|
* havoc is caused by the fact that the isr() sends
|
209 |
|
|
* itself a command to the floppy tape driver (pause,
|
210 |
|
|
* micro step pause). Now, the problem is that
|
211 |
|
|
* commands are transmitted via the fdc_seek
|
212 |
|
|
* command. But: the fdc performs seeks in the
|
213 |
|
|
* background i.e. it doesn't signal busy while
|
214 |
|
|
* sending the step pulses to the drive. Therefore the
|
215 |
|
|
* non-interrupt level driver has no chance to tell
|
216 |
|
|
* whether the isr() just has issued a seek. Therefore
|
217 |
|
|
* we HAVE TO have a look at the ft_hide_interrupt
|
218 |
|
|
* flag: it signals the non-interrupt level part of
|
219 |
|
|
* the driver that it has to wait for the fdc until it
|
220 |
|
|
* has completet seeking.
|
221 |
|
|
*
|
222 |
|
|
* THIS WAS PRESUMABLY THE REASON FOR ALL THAT
|
223 |
|
|
* "fdc_read timeout" errors, I HOPE :-)
|
224 |
|
|
*/
|
225 |
|
|
if (ft_hide_interrupt) {
|
226 |
|
|
restore_flags(flags);
|
227 |
|
|
TRACE(ft_t_info,
|
228 |
|
|
"Waiting for the isr() completing fdc_seek()");
|
229 |
|
|
if (fdc_interrupt_wait(2 * FT_SECOND) < 0) {
|
230 |
|
|
TRACE(ft_t_warn,
|
231 |
|
|
"Warning: timeout waiting for isr() seek to complete");
|
232 |
|
|
}
|
233 |
|
|
if (ft_hide_interrupt || !ft_seek_completed) {
|
234 |
|
|
/* There cannot be another
|
235 |
|
|
* interrupt. The isr() only stops
|
236 |
|
|
* the tape and the next interrupt
|
237 |
|
|
* won't come until we have send our
|
238 |
|
|
* command to the drive.
|
239 |
|
|
*/
|
240 |
|
|
TRACE_ABORT(-EIO, ft_t_bug,
|
241 |
|
|
"BUG? isr() is still seeking?\n"
|
242 |
|
|
KERN_INFO "hide: %d\n"
|
243 |
|
|
KERN_INFO "seek: %d",
|
244 |
|
|
ft_hide_interrupt,
|
245 |
|
|
ft_seek_completed);
|
246 |
|
|
|
247 |
|
|
}
|
248 |
|
|
fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
|
249 |
|
|
save_flags(flags);
|
250 |
|
|
cli();
|
251 |
|
|
}
|
252 |
|
|
fdc_status = inb(fdc.msr);
|
253 |
|
|
if ((fdc_status & FDC_DATA_READY_MASK) != FDC_DATA_IN_READY) {
|
254 |
|
|
restore_flags(flags);
|
255 |
|
|
TRACE_ABORT(-EBUSY, ft_t_err, "fdc not ready");
|
256 |
|
|
}
|
257 |
|
|
fdc_mode = *cmd_data; /* used by isr */
|
258 |
|
|
#ifdef TESTING
|
259 |
|
|
if (fdc_mode == FDC_SEEK) {
|
260 |
|
|
time = ftape_timediff(last_time, ftape_timestamp());
|
261 |
|
|
if (time < 6000) {
|
262 |
|
|
TRACE(ft_t_bug,"Warning: short timeout between seek commands: %d",
|
263 |
|
|
time);
|
264 |
|
|
}
|
265 |
|
|
}
|
266 |
|
|
#endif
|
267 |
|
|
#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,30)
|
268 |
|
|
if (!in_interrupt()) {
|
269 |
|
|
/* shouldn't be cleared if called from isr
|
270 |
|
|
*/
|
271 |
|
|
ft_interrupt_seen = 0;
|
272 |
|
|
}
|
273 |
|
|
#else
|
274 |
|
|
if (!intr_count) {
|
275 |
|
|
/* shouldn't be cleared if called from isr
|
276 |
|
|
*/
|
277 |
|
|
ft_interrupt_seen = 0;
|
278 |
|
|
}
|
279 |
|
|
#endif
|
280 |
|
|
while (count) {
|
281 |
|
|
result = fdc_write(*cmd_data);
|
282 |
|
|
if (result < 0) {
|
283 |
|
|
TRACE(ft_t_fdc_dma,
|
284 |
|
|
"fdc_mode = %02x, status = %02x at index %d",
|
285 |
|
|
(int) fdc_mode, (int) fdc_status,
|
286 |
|
|
cmd_len - count);
|
287 |
|
|
if (++retry <= 3) {
|
288 |
|
|
TRACE(ft_t_warn, "fdc_write timeout, retry");
|
289 |
|
|
} else {
|
290 |
|
|
TRACE(ft_t_err, "fdc_write timeout, fatal");
|
291 |
|
|
/* recover ??? */
|
292 |
|
|
break;
|
293 |
|
|
}
|
294 |
|
|
} else {
|
295 |
|
|
--count;
|
296 |
|
|
++cmd_data;
|
297 |
|
|
}
|
298 |
|
|
}
|
299 |
|
|
#ifdef TESTING
|
300 |
|
|
if (fdc_mode == FDC_SEEK) {
|
301 |
|
|
last_time = ftape_timestamp();
|
302 |
|
|
}
|
303 |
|
|
#endif
|
304 |
|
|
restore_flags(flags);
|
305 |
|
|
TRACE_EXIT result;
|
306 |
|
|
}
|
307 |
|
|
|
308 |
|
|
/* Input a res_len long result string from the FDC.
|
309 |
|
|
* The FDC should be ready to send the result or an error
|
310 |
|
|
* (EBUSY or ETIME) will occur.
|
311 |
|
|
*/
|
312 |
|
|
int fdc_result(__u8 * res_data, int res_len)
|
313 |
|
|
{
|
314 |
|
|
int result = 0;
|
315 |
|
|
unsigned long flags;
|
316 |
|
|
int count = res_len;
|
317 |
|
|
int retry = 0;
|
318 |
|
|
TRACE_FUN(ft_t_any);
|
319 |
|
|
|
320 |
|
|
save_flags(flags);
|
321 |
|
|
cli();
|
322 |
|
|
fdc_status = inb(fdc.msr);
|
323 |
|
|
if ((fdc_status & FDC_DATA_READY_MASK) != FDC_DATA_OUT_READY) {
|
324 |
|
|
TRACE(ft_t_err, "fdc not ready");
|
325 |
|
|
result = -EBUSY;
|
326 |
|
|
} else while (count) {
|
327 |
|
|
if (!(fdc_status & FDC_BUSY)) {
|
328 |
|
|
restore_flags(flags);
|
329 |
|
|
TRACE_ABORT(-EIO, ft_t_err, "premature end of result phase");
|
330 |
|
|
}
|
331 |
|
|
result = fdc_read(res_data);
|
332 |
|
|
if (result < 0) {
|
333 |
|
|
TRACE(ft_t_fdc_dma,
|
334 |
|
|
"fdc_mode = %02x, status = %02x at index %d",
|
335 |
|
|
(int) fdc_mode,
|
336 |
|
|
(int) fdc_status,
|
337 |
|
|
res_len - count);
|
338 |
|
|
if (++retry <= 3) {
|
339 |
|
|
TRACE(ft_t_warn, "fdc_read timeout, retry");
|
340 |
|
|
} else {
|
341 |
|
|
TRACE(ft_t_err, "fdc_read timeout, fatal");
|
342 |
|
|
/* recover ??? */
|
343 |
|
|
break;
|
344 |
|
|
++retry;
|
345 |
|
|
}
|
346 |
|
|
} else {
|
347 |
|
|
--count;
|
348 |
|
|
++res_data;
|
349 |
|
|
}
|
350 |
|
|
}
|
351 |
|
|
restore_flags(flags);
|
352 |
|
|
fdc_usec_wait(FT_RQM_DELAY); /* allow FDC to negate BSY */
|
353 |
|
|
TRACE_EXIT result;
|
354 |
|
|
}
|
355 |
|
|
|
356 |
|
|
/* Handle command and result phases for
|
357 |
|
|
* commands without data phase.
|
358 |
|
|
*/
|
359 |
|
|
int fdc_issue_command(const __u8 * out_data, int out_count,
|
360 |
|
|
__u8 * in_data, int in_count)
|
361 |
|
|
{
|
362 |
|
|
TRACE_FUN(ft_t_any);
|
363 |
|
|
|
364 |
|
|
if (out_count > 0) {
|
365 |
|
|
TRACE_CATCH(fdc_command(out_data, out_count),);
|
366 |
|
|
}
|
367 |
|
|
/* will take 24 - 30 usec for fdc_sense_drive_status and
|
368 |
|
|
* fdc_sense_interrupt_status commands.
|
369 |
|
|
* 35 fails sometimes (5/9/93 SJL)
|
370 |
|
|
* On a loaded system it incidentally takes longer than
|
371 |
|
|
* this for the fdc to get ready ! ?????? WHY ??????
|
372 |
|
|
* So until we know what's going on use a very long timeout.
|
373 |
|
|
*/
|
374 |
|
|
TRACE_CATCH(fdc_ready_out_wait(500 /* usec */),);
|
375 |
|
|
if (in_count > 0) {
|
376 |
|
|
TRACE_CATCH(fdc_result(in_data, in_count),
|
377 |
|
|
TRACE(ft_t_err, "result phase aborted"));
|
378 |
|
|
}
|
379 |
|
|
TRACE_EXIT 0;
|
380 |
|
|
}
|
381 |
|
|
|
382 |
|
|
/* Wait for FDC interrupt with timeout (in milliseconds).
|
383 |
|
|
* Signals are blocked so the wait will not be aborted.
|
384 |
|
|
* Note: interrupts must be enabled ! (23/05/93 SJL)
|
385 |
|
|
*/
|
386 |
|
|
int fdc_interrupt_wait(unsigned int time)
|
387 |
|
|
{
|
388 |
|
|
DECLARE_WAITQUEUE(wait,current);
|
389 |
|
|
sigset_t old_sigmask;
|
390 |
|
|
static int resetting;
|
391 |
|
|
long timeout;
|
392 |
|
|
|
393 |
|
|
TRACE_FUN(ft_t_fdc_dma);
|
394 |
|
|
|
395 |
|
|
#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16)
|
396 |
|
|
if (waitqueue_active(&ftape_wait_intr)) {
|
397 |
|
|
TRACE_ABORT(-EIO, ft_t_err, "error: nested call");
|
398 |
|
|
}
|
399 |
|
|
#else
|
400 |
|
|
if (ftape_wait_intr) {
|
401 |
|
|
TRACE_ABORT(-EIO, ft_t_err, "error: nested call");
|
402 |
|
|
}
|
403 |
|
|
#endif
|
404 |
|
|
/* timeout time will be up to USPT microseconds too long ! */
|
405 |
|
|
timeout = (1000 * time + FT_USPT - 1) / FT_USPT;
|
406 |
|
|
|
407 |
|
|
spin_lock_irq(¤t->sigmask_lock);
|
408 |
|
|
old_sigmask = current->blocked;
|
409 |
|
|
sigfillset(¤t->blocked);
|
410 |
|
|
recalc_sigpending(current);
|
411 |
|
|
spin_unlock_irq(¤t->sigmask_lock);
|
412 |
|
|
|
413 |
|
|
current->state = TASK_INTERRUPTIBLE;
|
414 |
|
|
add_wait_queue(&ftape_wait_intr, &wait);
|
415 |
|
|
while (!ft_interrupt_seen && (current->state == TASK_INTERRUPTIBLE)) {
|
416 |
|
|
timeout = schedule_timeout(timeout);
|
417 |
|
|
}
|
418 |
|
|
|
419 |
|
|
spin_lock_irq(¤t->sigmask_lock);
|
420 |
|
|
current->blocked = old_sigmask;
|
421 |
|
|
recalc_sigpending(current);
|
422 |
|
|
spin_unlock_irq(¤t->sigmask_lock);
|
423 |
|
|
|
424 |
|
|
remove_wait_queue(&ftape_wait_intr, &wait);
|
425 |
|
|
/* the following IS necessary. True: as well
|
426 |
|
|
* wake_up_interruptible() as the schedule() set TASK_RUNNING
|
427 |
|
|
* when they wakeup a task, BUT: it may very well be that
|
428 |
|
|
* ft_interrupt_seen is already set to 1 when we enter here
|
429 |
|
|
* in which case schedule() gets never called, and
|
430 |
|
|
* TASK_RUNNING never set. This has the funny effect that we
|
431 |
|
|
* execute all the code until we leave kernel space, but then
|
432 |
|
|
* the task is stopped (a task CANNOT be preempted while in
|
433 |
|
|
* kernel mode. Sending a pair of SIGSTOP/SIGCONT to the
|
434 |
|
|
* tasks wakes it up again. Funny! :-)
|
435 |
|
|
*/
|
436 |
|
|
current->state = TASK_RUNNING;
|
437 |
|
|
if (ft_interrupt_seen) { /* woken up by interrupt */
|
438 |
|
|
ft_interrupt_seen = 0;
|
439 |
|
|
TRACE_EXIT 0;
|
440 |
|
|
}
|
441 |
|
|
/* Original comment:
|
442 |
|
|
* In first instance, next statement seems unnecessary since
|
443 |
|
|
* it will be cleared in fdc_command. However, a small part of
|
444 |
|
|
* the software seems to rely on this being cleared here
|
445 |
|
|
* (ftape_close might fail) so stick to it until things get fixed !
|
446 |
|
|
*/
|
447 |
|
|
/* My deeply sought of knowledge:
|
448 |
|
|
* Behold NO! It is obvious. fdc_reset() doesn't call fdc_command()
|
449 |
|
|
* but nevertheless uses fdc_interrupt_wait(). OF COURSE this needs to
|
450 |
|
|
* be reset here.
|
451 |
|
|
*/
|
452 |
|
|
ft_interrupt_seen = 0; /* clear for next call */
|
453 |
|
|
if (!resetting) {
|
454 |
|
|
resetting = 1; /* break infinite recursion if reset fails */
|
455 |
|
|
TRACE(ft_t_any, "cleanup reset");
|
456 |
|
|
fdc_reset();
|
457 |
|
|
resetting = 0;
|
458 |
|
|
}
|
459 |
|
|
TRACE_EXIT (signal_pending(current)) ? -EINTR : -ETIME;
|
460 |
|
|
}
|
461 |
|
|
|
462 |
|
|
/* Start/stop drive motor. Enable DMA mode.
|
463 |
|
|
*/
|
464 |
|
|
void fdc_motor(int motor)
|
465 |
|
|
{
|
466 |
|
|
int unit = ft_drive_sel;
|
467 |
|
|
int data = unit | FDC_RESET_NOT | FDC_DMA_MODE;
|
468 |
|
|
TRACE_FUN(ft_t_any);
|
469 |
|
|
|
470 |
|
|
ftape_motor = motor;
|
471 |
|
|
if (ftape_motor) {
|
472 |
|
|
data |= FDC_MOTOR_0 << unit;
|
473 |
|
|
TRACE(ft_t_noise, "turning motor %d on", unit);
|
474 |
|
|
} else {
|
475 |
|
|
TRACE(ft_t_noise, "turning motor %d off", unit);
|
476 |
|
|
}
|
477 |
|
|
if (ft_mach2) {
|
478 |
|
|
outb_p(data, fdc.dor2);
|
479 |
|
|
} else {
|
480 |
|
|
outb_p(data, fdc.dor);
|
481 |
|
|
}
|
482 |
|
|
ftape_sleep(10 * FT_MILLISECOND);
|
483 |
|
|
TRACE_EXIT;
|
484 |
|
|
}
|
485 |
|
|
|
486 |
|
|
static void fdc_update_dsr(void)
|
487 |
|
|
{
|
488 |
|
|
TRACE_FUN(ft_t_any);
|
489 |
|
|
|
490 |
|
|
TRACE(ft_t_flow, "rate = %d Kbps, precomp = %d ns",
|
491 |
|
|
fdc_data_rate, fdc_precomp);
|
492 |
|
|
if (fdc.type >= i82077) {
|
493 |
|
|
outb_p((fdc_rate_code & 0x03) | fdc_prec_code, fdc.dsr);
|
494 |
|
|
} else {
|
495 |
|
|
outb_p(fdc_rate_code & 0x03, fdc.ccr);
|
496 |
|
|
}
|
497 |
|
|
TRACE_EXIT;
|
498 |
|
|
}
|
499 |
|
|
|
500 |
|
|
void fdc_set_write_precomp(int precomp)
|
501 |
|
|
{
|
502 |
|
|
TRACE_FUN(ft_t_any);
|
503 |
|
|
|
504 |
|
|
TRACE(ft_t_noise, "New precomp: %d nsec", precomp);
|
505 |
|
|
fdc_precomp = precomp;
|
506 |
|
|
/* write precompensation can be set in multiples of 41.67 nsec.
|
507 |
|
|
* round the parameter to the nearest multiple and convert it
|
508 |
|
|
* into a fdc setting. Note that 0 means default to the fdc,
|
509 |
|
|
* 7 is used instead of that.
|
510 |
|
|
*/
|
511 |
|
|
fdc_prec_code = ((fdc_precomp + 21) / 42) << 2;
|
512 |
|
|
if (fdc_prec_code == 0 || fdc_prec_code > (6 << 2)) {
|
513 |
|
|
fdc_prec_code = 7 << 2;
|
514 |
|
|
}
|
515 |
|
|
fdc_update_dsr();
|
516 |
|
|
TRACE_EXIT;
|
517 |
|
|
}
|
518 |
|
|
|
519 |
|
|
/* Reprogram the 82078 registers to use Data Rate Table 1 on all drives.
|
520 |
|
|
*/
|
521 |
|
|
void fdc_set_drive_specs(void)
|
522 |
|
|
{
|
523 |
|
|
__u8 cmd[] = { FDC_DRIVE_SPEC, 0x00, 0x00, 0x00, 0x00, 0xc0};
|
524 |
|
|
int result;
|
525 |
|
|
TRACE_FUN(ft_t_any);
|
526 |
|
|
|
527 |
|
|
TRACE(ft_t_flow, "Setting of drive specs called");
|
528 |
|
|
if (fdc.type >= i82078_1) {
|
529 |
|
|
cmd[1] = (0 << 5) | (2 << 2);
|
530 |
|
|
cmd[2] = (1 << 5) | (2 << 2);
|
531 |
|
|
cmd[3] = (2 << 5) | (2 << 2);
|
532 |
|
|
cmd[4] = (3 << 5) | (2 << 2);
|
533 |
|
|
result = fdc_command(cmd, NR_ITEMS(cmd));
|
534 |
|
|
if (result < 0) {
|
535 |
|
|
TRACE(ft_t_err, "Setting of drive specs failed");
|
536 |
|
|
}
|
537 |
|
|
}
|
538 |
|
|
TRACE_EXIT;
|
539 |
|
|
}
|
540 |
|
|
|
541 |
|
|
/* Select clock for fdc, must correspond with tape drive setting !
|
542 |
|
|
* This also influences the fdc timing so we must adjust some values.
|
543 |
|
|
*/
|
544 |
|
|
int fdc_set_data_rate(int rate)
|
545 |
|
|
{
|
546 |
|
|
int bad_rate = 0;
|
547 |
|
|
TRACE_FUN(ft_t_any);
|
548 |
|
|
|
549 |
|
|
/* Select clock for fdc, must correspond with tape drive setting !
|
550 |
|
|
* This also influences the fdc timing so we must adjust some values.
|
551 |
|
|
*/
|
552 |
|
|
TRACE(ft_t_fdc_dma, "new rate = %d", rate);
|
553 |
|
|
switch (rate) {
|
554 |
|
|
case 250:
|
555 |
|
|
fdc_rate_code = fdc_data_rate_250;
|
556 |
|
|
break;
|
557 |
|
|
case 500:
|
558 |
|
|
fdc_rate_code = fdc_data_rate_500;
|
559 |
|
|
break;
|
560 |
|
|
case 1000:
|
561 |
|
|
if (fdc.type < i82077) {
|
562 |
|
|
bad_rate = 1;
|
563 |
|
|
} else {
|
564 |
|
|
fdc_rate_code = fdc_data_rate_1000;
|
565 |
|
|
}
|
566 |
|
|
break;
|
567 |
|
|
case 2000:
|
568 |
|
|
if (fdc.type < i82078_1) {
|
569 |
|
|
bad_rate = 1;
|
570 |
|
|
} else {
|
571 |
|
|
fdc_rate_code = fdc_data_rate_2000;
|
572 |
|
|
}
|
573 |
|
|
break;
|
574 |
|
|
default:
|
575 |
|
|
bad_rate = 1;
|
576 |
|
|
}
|
577 |
|
|
if (bad_rate) {
|
578 |
|
|
TRACE_ABORT(-EIO,
|
579 |
|
|
ft_t_fdc_dma, "%d is not a valid data rate", rate);
|
580 |
|
|
}
|
581 |
|
|
fdc_data_rate = rate;
|
582 |
|
|
fdc_update_dsr();
|
583 |
|
|
fdc_set_seek_rate(fdc_seek_rate); /* clock changed! */
|
584 |
|
|
ftape_udelay(1000);
|
585 |
|
|
TRACE_EXIT 0;
|
586 |
|
|
}
|
587 |
|
|
|
588 |
|
|
/* keep the unit select if keep_select is != 0,
|
589 |
|
|
*/
|
590 |
|
|
static void fdc_dor_reset(int keep_select)
|
591 |
|
|
{
|
592 |
|
|
__u8 fdc_ctl = ft_drive_sel;
|
593 |
|
|
|
594 |
|
|
if (keep_select != 0) {
|
595 |
|
|
fdc_ctl |= FDC_DMA_MODE;
|
596 |
|
|
if (ftape_motor) {
|
597 |
|
|
fdc_ctl |= FDC_MOTOR_0 << ft_drive_sel;
|
598 |
|
|
}
|
599 |
|
|
}
|
600 |
|
|
ftape_udelay(10); /* ??? but seems to be necessary */
|
601 |
|
|
if (ft_mach2) {
|
602 |
|
|
outb_p(fdc_ctl & 0x0f, fdc.dor);
|
603 |
|
|
outb_p(fdc_ctl, fdc.dor2);
|
604 |
|
|
} else {
|
605 |
|
|
outb_p(fdc_ctl, fdc.dor);
|
606 |
|
|
}
|
607 |
|
|
fdc_usec_wait(10); /* delay >= 14 fdc clocks */
|
608 |
|
|
if (keep_select == 0) {
|
609 |
|
|
fdc_ctl = 0;
|
610 |
|
|
}
|
611 |
|
|
fdc_ctl |= FDC_RESET_NOT;
|
612 |
|
|
if (ft_mach2) {
|
613 |
|
|
outb_p(fdc_ctl & 0x0f, fdc.dor);
|
614 |
|
|
outb_p(fdc_ctl, fdc.dor2);
|
615 |
|
|
} else {
|
616 |
|
|
outb_p(fdc_ctl, fdc.dor);
|
617 |
|
|
}
|
618 |
|
|
}
|
619 |
|
|
|
620 |
|
|
/* Reset the floppy disk controller. Leave the ftape_unit selected.
|
621 |
|
|
*/
|
622 |
|
|
void fdc_reset(void)
|
623 |
|
|
{
|
624 |
|
|
int st0;
|
625 |
|
|
int i;
|
626 |
|
|
int dummy;
|
627 |
|
|
unsigned long flags;
|
628 |
|
|
TRACE_FUN(ft_t_any);
|
629 |
|
|
|
630 |
|
|
save_flags(flags);
|
631 |
|
|
cli();
|
632 |
|
|
|
633 |
|
|
fdc_dor_reset(1); /* keep unit selected */
|
634 |
|
|
|
635 |
|
|
fdc_mode = fdc_idle;
|
636 |
|
|
|
637 |
|
|
/* maybe the cli()/sti() pair is not necessary, BUT:
|
638 |
|
|
* the following line MUST be here. Otherwise fdc_interrupt_wait()
|
639 |
|
|
* won't wait. Note that fdc_reset() is called from
|
640 |
|
|
* ftape_dumb_stop() when the fdc is busy transferring data. In this
|
641 |
|
|
* case fdc_isr() MOST PROBABLY sets ft_interrupt_seen, and tries
|
642 |
|
|
* to get the result bytes from the fdc etc. CLASH.
|
643 |
|
|
*/
|
644 |
|
|
ft_interrupt_seen = 0;
|
645 |
|
|
|
646 |
|
|
/* Program data rate
|
647 |
|
|
*/
|
648 |
|
|
fdc_update_dsr(); /* restore data rate and precomp */
|
649 |
|
|
|
650 |
|
|
restore_flags(flags);
|
651 |
|
|
|
652 |
|
|
/*
|
653 |
|
|
* Wait for first polling cycle to complete
|
654 |
|
|
*/
|
655 |
|
|
if (fdc_interrupt_wait(1 * FT_SECOND) < 0) {
|
656 |
|
|
TRACE(ft_t_err, "no drive polling interrupt!");
|
657 |
|
|
} else { /* clear all disk-changed statuses */
|
658 |
|
|
for (i = 0; i < 4; ++i) {
|
659 |
|
|
if(fdc_sense_interrupt_status(&st0, &dummy) != 0) {
|
660 |
|
|
TRACE(ft_t_err, "sense failed for %d", i);
|
661 |
|
|
}
|
662 |
|
|
if (i == ft_drive_sel) {
|
663 |
|
|
ftape_current_cylinder = dummy;
|
664 |
|
|
}
|
665 |
|
|
}
|
666 |
|
|
TRACE(ft_t_noise, "drive polling completed");
|
667 |
|
|
}
|
668 |
|
|
/*
|
669 |
|
|
* SPECIFY COMMAND
|
670 |
|
|
*/
|
671 |
|
|
fdc_set_seek_rate(fdc_seek_rate);
|
672 |
|
|
/*
|
673 |
|
|
* DRIVE SPECIFICATION COMMAND (if fdc type known)
|
674 |
|
|
*/
|
675 |
|
|
if (fdc.type >= i82078_1) {
|
676 |
|
|
fdc_set_drive_specs();
|
677 |
|
|
}
|
678 |
|
|
TRACE_EXIT;
|
679 |
|
|
}
|
680 |
|
|
|
681 |
|
|
#if !defined(CLK_48MHZ)
|
682 |
|
|
# define CLK_48MHZ 1
|
683 |
|
|
#endif
|
684 |
|
|
|
685 |
|
|
/* When we're done, put the fdc into reset mode so that the regular
|
686 |
|
|
* floppy disk driver will figure out that something is wrong and
|
687 |
|
|
* initialize the controller the way it wants.
|
688 |
|
|
*/
|
689 |
|
|
void fdc_disable(void)
|
690 |
|
|
{
|
691 |
|
|
__u8 cmd1[] = {FDC_CONFIGURE, 0x00, 0x00, 0x00};
|
692 |
|
|
__u8 cmd2[] = {FDC_LOCK};
|
693 |
|
|
__u8 cmd3[] = {FDC_UNLOCK};
|
694 |
|
|
__u8 stat[1];
|
695 |
|
|
TRACE_FUN(ft_t_flow);
|
696 |
|
|
|
697 |
|
|
if (!fdc_fifo_locked) {
|
698 |
|
|
fdc_reset();
|
699 |
|
|
TRACE_EXIT;
|
700 |
|
|
}
|
701 |
|
|
if (fdc_issue_command(cmd3, 1, stat, 1) < 0 || stat[0] != 0x00) {
|
702 |
|
|
fdc_dor_reset(0);
|
703 |
|
|
TRACE_ABORT(/**/, ft_t_bug,
|
704 |
|
|
"couldn't unlock fifo, configuration remains changed");
|
705 |
|
|
}
|
706 |
|
|
fdc_fifo_locked = 0;
|
707 |
|
|
if (CLK_48MHZ && fdc.type >= i82078) {
|
708 |
|
|
cmd1[0] |= FDC_CLK48_BIT;
|
709 |
|
|
}
|
710 |
|
|
cmd1[2] = ((fdc_fifo_state) ? 0 : 0x20) + (fdc_fifo_thr - 1);
|
711 |
|
|
if (fdc_command(cmd1, NR_ITEMS(cmd1)) < 0) {
|
712 |
|
|
fdc_dor_reset(0);
|
713 |
|
|
TRACE_ABORT(/**/, ft_t_bug,
|
714 |
|
|
"couldn't reconfigure fifo to old state");
|
715 |
|
|
}
|
716 |
|
|
if (fdc_lock_state &&
|
717 |
|
|
fdc_issue_command(cmd2, 1, stat, 1) < 0) {
|
718 |
|
|
fdc_dor_reset(0);
|
719 |
|
|
TRACE_ABORT(/**/, ft_t_bug, "couldn't lock old state again");
|
720 |
|
|
}
|
721 |
|
|
TRACE(ft_t_noise, "fifo restored: %sabled, thr. %d, %slocked",
|
722 |
|
|
fdc_fifo_state ? "en" : "dis",
|
723 |
|
|
fdc_fifo_thr, (fdc_lock_state) ? "" : "not ");
|
724 |
|
|
fdc_dor_reset(0);
|
725 |
|
|
TRACE_EXIT;
|
726 |
|
|
}
|
727 |
|
|
|
728 |
|
|
/* Specify FDC seek-rate (milliseconds)
|
729 |
|
|
*/
|
730 |
|
|
int fdc_set_seek_rate(int seek_rate)
|
731 |
|
|
{
|
732 |
|
|
/* set step rate, dma mode, and minimal head load and unload times
|
733 |
|
|
*/
|
734 |
|
|
__u8 in[3] = { FDC_SPECIFY, 1, (1 << 1)};
|
735 |
|
|
|
736 |
|
|
fdc_seek_rate = seek_rate;
|
737 |
|
|
in[1] |= (16 - (fdc_data_rate * fdc_seek_rate) / 500) << 4;
|
738 |
|
|
|
739 |
|
|
return fdc_command(in, 3);
|
740 |
|
|
}
|
741 |
|
|
|
742 |
|
|
/* Sense drive status: get unit's drive status (ST3)
|
743 |
|
|
*/
|
744 |
|
|
int fdc_sense_drive_status(int *st3)
|
745 |
|
|
{
|
746 |
|
|
__u8 out[2];
|
747 |
|
|
__u8 in[1];
|
748 |
|
|
TRACE_FUN(ft_t_any);
|
749 |
|
|
|
750 |
|
|
out[0] = FDC_SENSED;
|
751 |
|
|
out[1] = ft_drive_sel;
|
752 |
|
|
TRACE_CATCH(fdc_issue_command(out, 2, in, 1),);
|
753 |
|
|
*st3 = in[0];
|
754 |
|
|
TRACE_EXIT 0;
|
755 |
|
|
}
|
756 |
|
|
|
757 |
|
|
/* Sense Interrupt Status command:
|
758 |
|
|
* should be issued at the end of each seek.
|
759 |
|
|
* get ST0 and current cylinder.
|
760 |
|
|
*/
|
761 |
|
|
int fdc_sense_interrupt_status(int *st0, int *current_cylinder)
|
762 |
|
|
{
|
763 |
|
|
__u8 out[1];
|
764 |
|
|
__u8 in[2];
|
765 |
|
|
TRACE_FUN(ft_t_any);
|
766 |
|
|
|
767 |
|
|
out[0] = FDC_SENSEI;
|
768 |
|
|
TRACE_CATCH(fdc_issue_command(out, 1, in, 2),);
|
769 |
|
|
*st0 = in[0];
|
770 |
|
|
*current_cylinder = in[1];
|
771 |
|
|
TRACE_EXIT 0;
|
772 |
|
|
}
|
773 |
|
|
|
774 |
|
|
/* step to track
|
775 |
|
|
*/
|
776 |
|
|
int fdc_seek(int track)
|
777 |
|
|
{
|
778 |
|
|
__u8 out[3];
|
779 |
|
|
int st0, pcn;
|
780 |
|
|
#ifdef TESTING
|
781 |
|
|
unsigned int time;
|
782 |
|
|
#endif
|
783 |
|
|
TRACE_FUN(ft_t_any);
|
784 |
|
|
|
785 |
|
|
out[0] = FDC_SEEK;
|
786 |
|
|
out[1] = ft_drive_sel;
|
787 |
|
|
out[2] = track;
|
788 |
|
|
#ifdef TESTING
|
789 |
|
|
time = ftape_timestamp();
|
790 |
|
|
#endif
|
791 |
|
|
/* We really need this command to work !
|
792 |
|
|
*/
|
793 |
|
|
ft_seek_completed = 0;
|
794 |
|
|
TRACE_CATCH(fdc_command(out, 3),
|
795 |
|
|
fdc_reset();
|
796 |
|
|
TRACE(ft_t_noise, "destination was: %d, resetting FDC...",
|
797 |
|
|
track));
|
798 |
|
|
/* Handle interrupts until ft_seek_completed or timeout.
|
799 |
|
|
*/
|
800 |
|
|
for (;;) {
|
801 |
|
|
TRACE_CATCH(fdc_interrupt_wait(2 * FT_SECOND),);
|
802 |
|
|
if (ft_seek_completed) {
|
803 |
|
|
TRACE_CATCH(fdc_sense_interrupt_status(&st0, &pcn),);
|
804 |
|
|
if ((st0 & ST0_SEEK_END) == 0) {
|
805 |
|
|
TRACE_ABORT(-EIO, ft_t_err,
|
806 |
|
|
"no seek-end after seek completion !??");
|
807 |
|
|
}
|
808 |
|
|
break;
|
809 |
|
|
}
|
810 |
|
|
}
|
811 |
|
|
#ifdef TESTING
|
812 |
|
|
time = ftape_timediff(time, ftape_timestamp()) / ABS(track - ftape_current_cylinder);
|
813 |
|
|
if ((time < 900 || time > 3100) && ABS(track - ftape_current_cylinder) > 5) {
|
814 |
|
|
TRACE(ft_t_warn, "Wrong FDC STEP interval: %d usecs (%d)",
|
815 |
|
|
time, track - ftape_current_cylinder);
|
816 |
|
|
}
|
817 |
|
|
#endif
|
818 |
|
|
/* Verify whether we issued the right tape command.
|
819 |
|
|
*/
|
820 |
|
|
/* Verify that we seek to the proper track. */
|
821 |
|
|
if (pcn != track) {
|
822 |
|
|
TRACE_ABORT(-EIO, ft_t_err, "bad seek..");
|
823 |
|
|
}
|
824 |
|
|
ftape_current_cylinder = track;
|
825 |
|
|
TRACE_EXIT 0;
|
826 |
|
|
}
|
827 |
|
|
|
828 |
|
|
/* Recalibrate and wait until home.
|
829 |
|
|
*/
|
830 |
|
|
int fdc_recalibrate(void)
|
831 |
|
|
{
|
832 |
|
|
__u8 out[2];
|
833 |
|
|
int st0;
|
834 |
|
|
int pcn;
|
835 |
|
|
int retry;
|
836 |
|
|
int old_seek_rate = fdc_seek_rate;
|
837 |
|
|
TRACE_FUN(ft_t_any);
|
838 |
|
|
|
839 |
|
|
TRACE_CATCH(fdc_set_seek_rate(6),);
|
840 |
|
|
out[0] = FDC_RECAL;
|
841 |
|
|
out[1] = ft_drive_sel;
|
842 |
|
|
ft_seek_completed = 0;
|
843 |
|
|
TRACE_CATCH(fdc_command(out, 2),);
|
844 |
|
|
/* Handle interrupts until ft_seek_completed or timeout.
|
845 |
|
|
*/
|
846 |
|
|
for (retry = 0;; ++retry) {
|
847 |
|
|
TRACE_CATCH(fdc_interrupt_wait(2 * FT_SECOND),);
|
848 |
|
|
if (ft_seek_completed) {
|
849 |
|
|
TRACE_CATCH(fdc_sense_interrupt_status(&st0, &pcn),);
|
850 |
|
|
if ((st0 & ST0_SEEK_END) == 0) {
|
851 |
|
|
if (retry < 1) {
|
852 |
|
|
continue; /* some drives/fdc's
|
853 |
|
|
* give an extra interrupt
|
854 |
|
|
*/
|
855 |
|
|
} else {
|
856 |
|
|
TRACE_ABORT(-EIO, ft_t_err,
|
857 |
|
|
"no seek-end after seek completion !??");
|
858 |
|
|
}
|
859 |
|
|
}
|
860 |
|
|
break;
|
861 |
|
|
}
|
862 |
|
|
}
|
863 |
|
|
ftape_current_cylinder = pcn;
|
864 |
|
|
if (pcn != 0) {
|
865 |
|
|
TRACE(ft_t_err, "failed: resulting track = %d", pcn);
|
866 |
|
|
}
|
867 |
|
|
TRACE_CATCH(fdc_set_seek_rate(old_seek_rate),);
|
868 |
|
|
TRACE_EXIT 0;
|
869 |
|
|
}
|
870 |
|
|
|
871 |
|
|
static int perpend_mode; /* set if fdc is in perpendicular mode */
|
872 |
|
|
|
873 |
|
|
static int perpend_off(void)
|
874 |
|
|
{
|
875 |
|
|
__u8 perpend[] = {FDC_PERPEND, 0x00};
|
876 |
|
|
TRACE_FUN(ft_t_any);
|
877 |
|
|
|
878 |
|
|
if (perpend_mode) {
|
879 |
|
|
/* Turn off perpendicular mode */
|
880 |
|
|
perpend[1] = 0x80;
|
881 |
|
|
TRACE_CATCH(fdc_command(perpend, 2),
|
882 |
|
|
TRACE(ft_t_err,"Perpendicular mode exit failed!"));
|
883 |
|
|
perpend_mode = 0;
|
884 |
|
|
}
|
885 |
|
|
TRACE_EXIT 0;
|
886 |
|
|
}
|
887 |
|
|
|
888 |
|
|
static int handle_perpend(int segment_id)
|
889 |
|
|
{
|
890 |
|
|
__u8 perpend[] = {FDC_PERPEND, 0x00};
|
891 |
|
|
TRACE_FUN(ft_t_any);
|
892 |
|
|
|
893 |
|
|
/* When writing QIC-3020 tapes, turn on perpendicular mode
|
894 |
|
|
* if tape is moving in forward direction (even tracks).
|
895 |
|
|
*/
|
896 |
|
|
if (ft_qic_std == QIC_TAPE_QIC3020 &&
|
897 |
|
|
((segment_id / ft_segments_per_track) & 1) == 0) {
|
898 |
|
|
/* FIXME: some i82077 seem to support perpendicular mode as
|
899 |
|
|
* well.
|
900 |
|
|
*/
|
901 |
|
|
#if 0
|
902 |
|
|
if (fdc.type < i82077AA) {}
|
903 |
|
|
#else
|
904 |
|
|
if (fdc.type < i82077 && ft_data_rate < 1000) {
|
905 |
|
|
#endif
|
906 |
|
|
/* fdc does not support perpendicular mode: complain
|
907 |
|
|
*/
|
908 |
|
|
TRACE_ABORT(-EIO, ft_t_err,
|
909 |
|
|
"Your FDC does not support QIC-3020.");
|
910 |
|
|
}
|
911 |
|
|
perpend[1] = 0x03 /* 0x83 + (0x4 << ft_drive_sel) */ ;
|
912 |
|
|
TRACE_CATCH(fdc_command(perpend, 2),
|
913 |
|
|
TRACE(ft_t_err,"Perpendicular mode entry failed!"));
|
914 |
|
|
TRACE(ft_t_flow, "Perpendicular mode set");
|
915 |
|
|
perpend_mode = 1;
|
916 |
|
|
TRACE_EXIT 0;
|
917 |
|
|
}
|
918 |
|
|
TRACE_EXIT perpend_off();
|
919 |
|
|
}
|
920 |
|
|
|
921 |
|
|
static inline void fdc_setup_dma(char mode,
|
922 |
|
|
volatile void *addr, unsigned int count)
|
923 |
|
|
{
|
924 |
|
|
/* Program the DMA controller.
|
925 |
|
|
*/
|
926 |
|
|
disable_dma(fdc.dma);
|
927 |
|
|
clear_dma_ff(fdc.dma);
|
928 |
|
|
set_dma_mode(fdc.dma, mode);
|
929 |
|
|
set_dma_addr(fdc.dma, virt_to_bus((void*)addr));
|
930 |
|
|
set_dma_count(fdc.dma, count);
|
931 |
|
|
#ifdef GCC_2_4_5_BUG
|
932 |
|
|
/* This seemingly stupid construction confuses the gcc-2.4.5
|
933 |
|
|
* code generator enough to create correct code.
|
934 |
|
|
*/
|
935 |
|
|
if (1) {
|
936 |
|
|
int i;
|
937 |
|
|
|
938 |
|
|
for (i = 0; i < 1; ++i) {
|
939 |
|
|
ftape_udelay(1);
|
940 |
|
|
}
|
941 |
|
|
}
|
942 |
|
|
#endif
|
943 |
|
|
enable_dma(fdc.dma);
|
944 |
|
|
}
|
945 |
|
|
|
946 |
|
|
/* Setup fdc and dma for formatting the next segment
|
947 |
|
|
*/
|
948 |
|
|
int fdc_setup_formatting(buffer_struct * buff)
|
949 |
|
|
{
|
950 |
|
|
unsigned long flags;
|
951 |
|
|
__u8 out[6] = {
|
952 |
|
|
FDC_FORMAT, 0x00, 3, 4 * FT_SECTORS_PER_SEGMENT, 0x00, 0x6b
|
953 |
|
|
};
|
954 |
|
|
TRACE_FUN(ft_t_any);
|
955 |
|
|
|
956 |
|
|
TRACE_CATCH(handle_perpend(buff->segment_id),);
|
957 |
|
|
/* Program the DMA controller.
|
958 |
|
|
*/
|
959 |
|
|
TRACE(ft_t_fdc_dma,
|
960 |
|
|
"phys. addr. = %lx", virt_to_bus((void*) buff->ptr));
|
961 |
|
|
save_flags(flags);
|
962 |
|
|
cli(); /* could be called from ISR ! */
|
963 |
|
|
fdc_setup_dma(DMA_MODE_WRITE, buff->ptr, FT_SECTORS_PER_SEGMENT * 4);
|
964 |
|
|
/* Issue FDC command to start reading/writing.
|
965 |
|
|
*/
|
966 |
|
|
out[1] = ft_drive_sel;
|
967 |
|
|
out[4] = buff->gap3;
|
968 |
|
|
TRACE_CATCH(fdc_setup_error = fdc_command(out, sizeof(out)),
|
969 |
|
|
restore_flags(flags); fdc_mode = fdc_idle);
|
970 |
|
|
restore_flags(flags);
|
971 |
|
|
TRACE_EXIT 0;
|
972 |
|
|
}
|
973 |
|
|
|
974 |
|
|
|
975 |
|
|
/* Setup Floppy Disk Controller and DMA to read or write the next cluster
|
976 |
|
|
* of good sectors from or to the current segment.
|
977 |
|
|
*/
|
978 |
|
|
int fdc_setup_read_write(buffer_struct * buff, __u8 operation)
|
979 |
|
|
{
|
980 |
|
|
unsigned long flags;
|
981 |
|
|
__u8 out[9];
|
982 |
|
|
int dma_mode;
|
983 |
|
|
TRACE_FUN(ft_t_any);
|
984 |
|
|
|
985 |
|
|
switch(operation) {
|
986 |
|
|
case FDC_VERIFY:
|
987 |
|
|
if (fdc.type < i82077) {
|
988 |
|
|
operation = FDC_READ;
|
989 |
|
|
}
|
990 |
|
|
case FDC_READ:
|
991 |
|
|
case FDC_READ_DELETED:
|
992 |
|
|
dma_mode = DMA_MODE_READ;
|
993 |
|
|
TRACE(ft_t_fdc_dma, "xfer %d sectors to 0x%p",
|
994 |
|
|
buff->sector_count, buff->ptr);
|
995 |
|
|
TRACE_CATCH(perpend_off(),);
|
996 |
|
|
break;
|
997 |
|
|
case FDC_WRITE_DELETED:
|
998 |
|
|
TRACE(ft_t_noise, "deleting segment %d", buff->segment_id);
|
999 |
|
|
case FDC_WRITE:
|
1000 |
|
|
dma_mode = DMA_MODE_WRITE;
|
1001 |
|
|
/* When writing QIC-3020 tapes, turn on perpendicular mode
|
1002 |
|
|
* if tape is moving in forward direction (even tracks).
|
1003 |
|
|
*/
|
1004 |
|
|
TRACE_CATCH(handle_perpend(buff->segment_id),);
|
1005 |
|
|
TRACE(ft_t_fdc_dma, "xfer %d sectors from 0x%p",
|
1006 |
|
|
buff->sector_count, buff->ptr);
|
1007 |
|
|
break;
|
1008 |
|
|
default:
|
1009 |
|
|
TRACE_ABORT(-EIO,
|
1010 |
|
|
ft_t_bug, "bug: illegal operation parameter");
|
1011 |
|
|
}
|
1012 |
|
|
TRACE(ft_t_fdc_dma, "phys. addr. = %lx",virt_to_bus((void*)buff->ptr));
|
1013 |
|
|
save_flags(flags);
|
1014 |
|
|
cli(); /* could be called from ISR ! */
|
1015 |
|
|
if (operation != FDC_VERIFY) {
|
1016 |
|
|
fdc_setup_dma(dma_mode, buff->ptr,
|
1017 |
|
|
FT_SECTOR_SIZE * buff->sector_count);
|
1018 |
|
|
}
|
1019 |
|
|
/* Issue FDC command to start reading/writing.
|
1020 |
|
|
*/
|
1021 |
|
|
out[0] = operation;
|
1022 |
|
|
out[1] = ft_drive_sel;
|
1023 |
|
|
out[2] = buff->cyl;
|
1024 |
|
|
out[3] = buff->head;
|
1025 |
|
|
out[4] = buff->sect + buff->sector_offset;
|
1026 |
|
|
out[5] = 3; /* Sector size of 1K. */
|
1027 |
|
|
out[6] = out[4] + buff->sector_count - 1; /* last sector */
|
1028 |
|
|
out[7] = 109; /* Gap length. */
|
1029 |
|
|
out[8] = 0xff; /* No limit to transfer size. */
|
1030 |
|
|
TRACE(ft_t_fdc_dma, "C: 0x%02x, H: 0x%02x, R: 0x%02x, cnt: 0x%02x",
|
1031 |
|
|
out[2], out[3], out[4], out[6] - out[4] + 1);
|
1032 |
|
|
restore_flags(flags);
|
1033 |
|
|
TRACE_CATCH(fdc_setup_error = fdc_command(out, 9),fdc_mode = fdc_idle);
|
1034 |
|
|
TRACE_EXIT 0;
|
1035 |
|
|
}
|
1036 |
|
|
|
1037 |
|
|
int fdc_fifo_threshold(__u8 threshold,
|
1038 |
|
|
int *fifo_state, int *lock_state, int *fifo_thr)
|
1039 |
|
|
{
|
1040 |
|
|
const __u8 cmd0[] = {FDC_DUMPREGS};
|
1041 |
|
|
__u8 cmd1[] = {FDC_CONFIGURE, 0, (0x0f & (threshold - 1)), 0};
|
1042 |
|
|
const __u8 cmd2[] = {FDC_LOCK};
|
1043 |
|
|
const __u8 cmd3[] = {FDC_UNLOCK};
|
1044 |
|
|
__u8 reg[10];
|
1045 |
|
|
__u8 stat;
|
1046 |
|
|
int i;
|
1047 |
|
|
int result;
|
1048 |
|
|
TRACE_FUN(ft_t_any);
|
1049 |
|
|
|
1050 |
|
|
if (CLK_48MHZ && fdc.type >= i82078) {
|
1051 |
|
|
cmd1[0] |= FDC_CLK48_BIT;
|
1052 |
|
|
}
|
1053 |
|
|
/* Dump fdc internal registers for examination
|
1054 |
|
|
*/
|
1055 |
|
|
TRACE_CATCH(fdc_command(cmd0, NR_ITEMS(cmd0)),
|
1056 |
|
|
TRACE(ft_t_warn, "dumpreg cmd failed, fifo unchanged"));
|
1057 |
|
|
/* Now read fdc internal registers from fifo
|
1058 |
|
|
*/
|
1059 |
|
|
for (i = 0; i < (int)NR_ITEMS(reg); ++i) {
|
1060 |
|
|
fdc_read(®[i]);
|
1061 |
|
|
TRACE(ft_t_fdc_dma, "Register %d = 0x%02x", i, reg[i]);
|
1062 |
|
|
}
|
1063 |
|
|
if (fifo_state && lock_state && fifo_thr) {
|
1064 |
|
|
*fifo_state = (reg[8] & 0x20) == 0;
|
1065 |
|
|
*lock_state = reg[7] & 0x80;
|
1066 |
|
|
*fifo_thr = 1 + (reg[8] & 0x0f);
|
1067 |
|
|
}
|
1068 |
|
|
TRACE(ft_t_noise,
|
1069 |
|
|
"original fifo state: %sabled, threshold %d, %slocked",
|
1070 |
|
|
((reg[8] & 0x20) == 0) ? "en" : "dis",
|
1071 |
|
|
1 + (reg[8] & 0x0f), (reg[7] & 0x80) ? "" : "not ");
|
1072 |
|
|
/* If fdc is already locked, unlock it first ! */
|
1073 |
|
|
if (reg[7] & 0x80) {
|
1074 |
|
|
fdc_ready_wait(100);
|
1075 |
|
|
TRACE_CATCH(fdc_issue_command(cmd3, NR_ITEMS(cmd3), &stat, 1),
|
1076 |
|
|
TRACE(ft_t_bug, "FDC unlock command failed, "
|
1077 |
|
|
"configuration unchanged"));
|
1078 |
|
|
}
|
1079 |
|
|
fdc_fifo_locked = 0;
|
1080 |
|
|
/* Enable fifo and set threshold at xx bytes to allow a
|
1081 |
|
|
* reasonably large latency and reduce number of dma bursts.
|
1082 |
|
|
*/
|
1083 |
|
|
fdc_ready_wait(100);
|
1084 |
|
|
if ((result = fdc_command(cmd1, NR_ITEMS(cmd1))) < 0) {
|
1085 |
|
|
TRACE(ft_t_bug, "configure cmd failed, fifo unchanged");
|
1086 |
|
|
}
|
1087 |
|
|
/* Now lock configuration so reset will not change it
|
1088 |
|
|
*/
|
1089 |
|
|
if(fdc_issue_command(cmd2, NR_ITEMS(cmd2), &stat, 1) < 0 ||
|
1090 |
|
|
stat != 0x10) {
|
1091 |
|
|
TRACE_ABORT(-EIO, ft_t_bug,
|
1092 |
|
|
"FDC lock command failed, stat = 0x%02x", stat);
|
1093 |
|
|
}
|
1094 |
|
|
fdc_fifo_locked = 1;
|
1095 |
|
|
TRACE_EXIT result;
|
1096 |
|
|
}
|
1097 |
|
|
|
1098 |
|
|
static int fdc_fifo_enable(void)
|
1099 |
|
|
{
|
1100 |
|
|
TRACE_FUN(ft_t_any);
|
1101 |
|
|
|
1102 |
|
|
if (fdc_fifo_locked) {
|
1103 |
|
|
TRACE_ABORT(0, ft_t_warn, "Fifo not enabled because locked");
|
1104 |
|
|
}
|
1105 |
|
|
TRACE_CATCH(fdc_fifo_threshold(ft_fdc_threshold /* bytes */,
|
1106 |
|
|
&fdc_fifo_state,
|
1107 |
|
|
&fdc_lock_state,
|
1108 |
|
|
&fdc_fifo_thr),);
|
1109 |
|
|
TRACE_CATCH(fdc_fifo_threshold(ft_fdc_threshold /* bytes */,
|
1110 |
|
|
NULL, NULL, NULL),);
|
1111 |
|
|
TRACE_EXIT 0;
|
1112 |
|
|
}
|
1113 |
|
|
|
1114 |
|
|
/* Determine fd controller type
|
1115 |
|
|
*/
|
1116 |
|
|
static __u8 fdc_save_state[2];
|
1117 |
|
|
|
1118 |
|
|
int fdc_probe(void)
|
1119 |
|
|
{
|
1120 |
|
|
__u8 cmd[1];
|
1121 |
|
|
__u8 stat[16]; /* must be able to hold dumpregs & save results */
|
1122 |
|
|
int i;
|
1123 |
|
|
TRACE_FUN(ft_t_any);
|
1124 |
|
|
|
1125 |
|
|
/* Try to find out what kind of fd controller we have to deal with
|
1126 |
|
|
* Scheme borrowed from floppy driver:
|
1127 |
|
|
* first try if FDC_DUMPREGS command works
|
1128 |
|
|
* (this indicates that we have a 82072 or better)
|
1129 |
|
|
* then try the FDC_VERSION command (82072 doesn't support this)
|
1130 |
|
|
* then try the FDC_UNLOCK command (some older 82077's don't support this)
|
1131 |
|
|
* then try the FDC_PARTID command (82078's support this)
|
1132 |
|
|
*/
|
1133 |
|
|
cmd[0] = FDC_DUMPREGS;
|
1134 |
|
|
if (fdc_issue_command(cmd, 1, stat, 1) != 0) {
|
1135 |
|
|
TRACE_ABORT(no_fdc, ft_t_bug, "No FDC found");
|
1136 |
|
|
}
|
1137 |
|
|
if (stat[0] == 0x80) {
|
1138 |
|
|
/* invalid command: must be pre 82072 */
|
1139 |
|
|
TRACE_ABORT(i8272,
|
1140 |
|
|
ft_t_warn, "Type 8272A/765A compatible FDC found");
|
1141 |
|
|
}
|
1142 |
|
|
fdc_result(&stat[1], 9);
|
1143 |
|
|
fdc_save_state[0] = stat[7];
|
1144 |
|
|
fdc_save_state[1] = stat[8];
|
1145 |
|
|
cmd[0] = FDC_VERSION;
|
1146 |
|
|
if (fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] == 0x80) {
|
1147 |
|
|
TRACE_ABORT(i8272, ft_t_warn, "Type 82072 FDC found");
|
1148 |
|
|
}
|
1149 |
|
|
if (*stat != 0x90) {
|
1150 |
|
|
TRACE_ABORT(i8272, ft_t_warn, "Unknown FDC found");
|
1151 |
|
|
}
|
1152 |
|
|
cmd[0] = FDC_UNLOCK;
|
1153 |
|
|
if(fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] != 0x00) {
|
1154 |
|
|
TRACE_ABORT(i8272, ft_t_warn,
|
1155 |
|
|
"Type pre-1991 82077 FDC found, "
|
1156 |
|
|
"treating it like a 82072");
|
1157 |
|
|
}
|
1158 |
|
|
if (fdc_save_state[0] & 0x80) { /* was locked */
|
1159 |
|
|
cmd[0] = FDC_LOCK; /* restore lock */
|
1160 |
|
|
(void)fdc_issue_command(cmd, 1, stat, 1);
|
1161 |
|
|
TRACE(ft_t_warn, "FDC is already locked");
|
1162 |
|
|
}
|
1163 |
|
|
/* Test for a i82078 FDC */
|
1164 |
|
|
cmd[0] = FDC_PARTID;
|
1165 |
|
|
if (fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] == 0x80) {
|
1166 |
|
|
/* invalid command: not a i82078xx type FDC */
|
1167 |
|
|
for (i = 0; i < 4; ++i) {
|
1168 |
|
|
outb_p(i, fdc.tdr);
|
1169 |
|
|
if ((inb_p(fdc.tdr) & 0x03) != i) {
|
1170 |
|
|
TRACE_ABORT(i82077,
|
1171 |
|
|
ft_t_warn, "Type 82077 FDC found");
|
1172 |
|
|
}
|
1173 |
|
|
}
|
1174 |
|
|
TRACE_ABORT(i82077AA, ft_t_warn, "Type 82077AA FDC found");
|
1175 |
|
|
}
|
1176 |
|
|
/* FDC_PARTID cmd succeeded */
|
1177 |
|
|
switch (stat[0] >> 5) {
|
1178 |
|
|
case 0x0:
|
1179 |
|
|
/* i82078SL or i82078-1. The SL part cannot run at
|
1180 |
|
|
* 2Mbps (the SL and -1 dies are identical; they are
|
1181 |
|
|
* speed graded after production, according to Intel).
|
1182 |
|
|
* Some SL's can be detected by doing a SAVE cmd and
|
1183 |
|
|
* look at bit 7 of the first byte (the SEL3V# bit).
|
1184 |
|
|
* If it is 0, the part runs off 3Volts, and hence it
|
1185 |
|
|
* is a SL.
|
1186 |
|
|
*/
|
1187 |
|
|
cmd[0] = FDC_SAVE;
|
1188 |
|
|
if(fdc_issue_command(cmd, 1, stat, 16) < 0) {
|
1189 |
|
|
TRACE(ft_t_err, "FDC_SAVE failed. Dunno why");
|
1190 |
|
|
/* guess we better claim the fdc to be a i82078 */
|
1191 |
|
|
TRACE_ABORT(i82078,
|
1192 |
|
|
ft_t_warn,
|
1193 |
|
|
"Type i82078 FDC (i suppose) found");
|
1194 |
|
|
}
|
1195 |
|
|
if ((stat[0] & FDC_SEL3V_BIT)) {
|
1196 |
|
|
/* fdc running off 5Volts; Pray that it's a i82078-1
|
1197 |
|
|
*/
|
1198 |
|
|
TRACE_ABORT(i82078_1, ft_t_warn,
|
1199 |
|
|
"Type i82078-1 or 5Volt i82078SL FDC found");
|
1200 |
|
|
}
|
1201 |
|
|
TRACE_ABORT(i82078, ft_t_warn,
|
1202 |
|
|
"Type 3Volt i82078SL FDC (1Mbps) found");
|
1203 |
|
|
case 0x1:
|
1204 |
|
|
case 0x2: /* S82078B */
|
1205 |
|
|
/* The '78B isn't '78 compatible. Detect it as a '77AA */
|
1206 |
|
|
TRACE_ABORT(i82077AA, ft_t_warn, "Type i82077AA FDC found");
|
1207 |
|
|
case 0x3: /* NSC PC8744 core; used in several super-IO chips */
|
1208 |
|
|
TRACE_ABORT(i82077AA,
|
1209 |
|
|
ft_t_warn, "Type 82077AA compatible FDC found");
|
1210 |
|
|
default:
|
1211 |
|
|
TRACE(ft_t_warn, "A previously undetected FDC found");
|
1212 |
|
|
TRACE_ABORT(i82077AA, ft_t_warn,
|
1213 |
|
|
"Treating it as a 82077AA. Please report partid= %d",
|
1214 |
|
|
stat[0]);
|
1215 |
|
|
} /* switch(stat[ 0] >> 5) */
|
1216 |
|
|
TRACE_EXIT no_fdc;
|
1217 |
|
|
}
|
1218 |
|
|
|
1219 |
|
|
static int fdc_request_regions(void)
|
1220 |
|
|
{
|
1221 |
|
|
TRACE_FUN(ft_t_flow);
|
1222 |
|
|
|
1223 |
|
|
if (ft_mach2 || ft_probe_fc10) {
|
1224 |
|
|
if (check_region(fdc.sra, 8) < 0) {
|
1225 |
|
|
#ifndef BROKEN_FLOPPY_DRIVER
|
1226 |
|
|
TRACE_EXIT -EBUSY;
|
1227 |
|
|
#else
|
1228 |
|
|
TRACE(ft_t_warn,
|
1229 |
|
|
"address 0x%03x occupied (by floppy driver?), using it anyway", fdc.sra);
|
1230 |
|
|
#endif
|
1231 |
|
|
}
|
1232 |
|
|
request_region(fdc.sra, 8, "fdc (ft)");
|
1233 |
|
|
} else {
|
1234 |
|
|
if (check_region(fdc.sra, 6) < 0 ||
|
1235 |
|
|
check_region(fdc.dir, 1) < 0) {
|
1236 |
|
|
#ifndef BROKEN_FLOPPY_DRIVER
|
1237 |
|
|
TRACE_EXIT -EBUSY;
|
1238 |
|
|
#else
|
1239 |
|
|
TRACE(ft_t_warn,
|
1240 |
|
|
"address 0x%03x occupied (by floppy driver?), using it anyway", fdc.sra);
|
1241 |
|
|
#endif
|
1242 |
|
|
}
|
1243 |
|
|
request_region(fdc.sra, 6, "fdc (ft)");
|
1244 |
|
|
request_region(fdc.sra + 7, 1, "fdc (ft)");
|
1245 |
|
|
}
|
1246 |
|
|
TRACE_EXIT 0;
|
1247 |
|
|
}
|
1248 |
|
|
|
1249 |
|
|
void fdc_release_regions(void)
|
1250 |
|
|
{
|
1251 |
|
|
TRACE_FUN(ft_t_flow);
|
1252 |
|
|
|
1253 |
|
|
if (fdc.sra != 0) {
|
1254 |
|
|
if (fdc.dor2 != 0) {
|
1255 |
|
|
release_region(fdc.sra, 8);
|
1256 |
|
|
} else {
|
1257 |
|
|
release_region(fdc.sra, 6);
|
1258 |
|
|
release_region(fdc.dir, 1);
|
1259 |
|
|
}
|
1260 |
|
|
}
|
1261 |
|
|
TRACE_EXIT;
|
1262 |
|
|
}
|
1263 |
|
|
|
1264 |
|
|
static int fdc_config_regs(unsigned int fdc_base,
|
1265 |
|
|
unsigned int fdc_irq,
|
1266 |
|
|
unsigned int fdc_dma)
|
1267 |
|
|
{
|
1268 |
|
|
TRACE_FUN(ft_t_flow);
|
1269 |
|
|
|
1270 |
|
|
fdc.irq = fdc_irq;
|
1271 |
|
|
fdc.dma = fdc_dma;
|
1272 |
|
|
fdc.sra = fdc_base;
|
1273 |
|
|
fdc.srb = fdc_base + 1;
|
1274 |
|
|
fdc.dor = fdc_base + 2;
|
1275 |
|
|
fdc.tdr = fdc_base + 3;
|
1276 |
|
|
fdc.msr = fdc.dsr = fdc_base + 4;
|
1277 |
|
|
fdc.fifo = fdc_base + 5;
|
1278 |
|
|
fdc.dir = fdc.ccr = fdc_base + 7;
|
1279 |
|
|
fdc.dor2 = (ft_mach2 || ft_probe_fc10) ? fdc_base + 6 : 0;
|
1280 |
|
|
TRACE_CATCH(fdc_request_regions(), fdc.sra = 0);
|
1281 |
|
|
TRACE_EXIT 0;
|
1282 |
|
|
}
|
1283 |
|
|
|
1284 |
|
|
static int fdc_config(void)
|
1285 |
|
|
{
|
1286 |
|
|
static int already_done;
|
1287 |
|
|
TRACE_FUN(ft_t_any);
|
1288 |
|
|
|
1289 |
|
|
if (already_done) {
|
1290 |
|
|
TRACE_CATCH(fdc_request_regions(),);
|
1291 |
|
|
*(fdc.hook) = fdc_isr; /* hook our handler in */
|
1292 |
|
|
TRACE_EXIT 0;
|
1293 |
|
|
}
|
1294 |
|
|
if (ft_probe_fc10) {
|
1295 |
|
|
int fc_type;
|
1296 |
|
|
|
1297 |
|
|
TRACE_CATCH(fdc_config_regs(ft_fdc_base,
|
1298 |
|
|
ft_fdc_irq, ft_fdc_dma),);
|
1299 |
|
|
fc_type = fc10_enable();
|
1300 |
|
|
if (fc_type != 0) {
|
1301 |
|
|
TRACE(ft_t_warn, "FC-%c0 controller found", '0' + fc_type);
|
1302 |
|
|
fdc.type = fc10;
|
1303 |
|
|
fdc.hook = &do_ftape;
|
1304 |
|
|
*(fdc.hook) = fdc_isr; /* hook our handler in */
|
1305 |
|
|
already_done = 1;
|
1306 |
|
|
TRACE_EXIT 0;
|
1307 |
|
|
} else {
|
1308 |
|
|
TRACE(ft_t_warn, "FC-10/20 controller not found");
|
1309 |
|
|
fdc_release_regions();
|
1310 |
|
|
fdc.type = no_fdc;
|
1311 |
|
|
ft_probe_fc10 = 0;
|
1312 |
|
|
ft_fdc_base = 0x3f0;
|
1313 |
|
|
ft_fdc_irq = 6;
|
1314 |
|
|
ft_fdc_dma = 2;
|
1315 |
|
|
}
|
1316 |
|
|
}
|
1317 |
|
|
TRACE(ft_t_warn, "fdc base: 0x%x, irq: %d, dma: %d",
|
1318 |
|
|
ft_fdc_base, ft_fdc_irq, ft_fdc_dma);
|
1319 |
|
|
TRACE_CATCH(fdc_config_regs(ft_fdc_base, ft_fdc_irq, ft_fdc_dma),);
|
1320 |
|
|
fdc.hook = &do_ftape;
|
1321 |
|
|
*(fdc.hook) = fdc_isr; /* hook our handler in */
|
1322 |
|
|
already_done = 1;
|
1323 |
|
|
TRACE_EXIT 0;
|
1324 |
|
|
}
|
1325 |
|
|
|
1326 |
|
|
static void ftape_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
1327 |
|
|
{
|
1328 |
|
|
void (*handler) (void) = *fdc.hook;
|
1329 |
|
|
TRACE_FUN(ft_t_any);
|
1330 |
|
|
|
1331 |
|
|
*fdc.hook = NULL;
|
1332 |
|
|
if (handler) {
|
1333 |
|
|
handler();
|
1334 |
|
|
} else {
|
1335 |
|
|
TRACE(ft_t_bug, "Unexpected ftape interrupt");
|
1336 |
|
|
}
|
1337 |
|
|
TRACE_EXIT;
|
1338 |
|
|
}
|
1339 |
|
|
|
1340 |
|
|
int fdc_grab_irq_and_dma(void)
|
1341 |
|
|
{
|
1342 |
|
|
TRACE_FUN(ft_t_any);
|
1343 |
|
|
|
1344 |
|
|
if (fdc.hook == &do_ftape) {
|
1345 |
|
|
/* Get fast interrupt handler.
|
1346 |
|
|
*/
|
1347 |
|
|
if (request_irq(fdc.irq, ftape_interrupt,
|
1348 |
|
|
SA_INTERRUPT, "ft", ftape_id)) {
|
1349 |
|
|
TRACE_ABORT(-EIO, ft_t_bug,
|
1350 |
|
|
"Unable to grab IRQ%d for ftape driver",
|
1351 |
|
|
fdc.irq);
|
1352 |
|
|
}
|
1353 |
|
|
if (request_dma(fdc.dma, ftape_id)) {
|
1354 |
|
|
free_irq(fdc.irq, ftape_id);
|
1355 |
|
|
TRACE_ABORT(-EIO, ft_t_bug,
|
1356 |
|
|
"Unable to grab DMA%d for ftape driver",
|
1357 |
|
|
fdc.dma);
|
1358 |
|
|
}
|
1359 |
|
|
}
|
1360 |
|
|
if (ft_fdc_base != 0x3f0 && (ft_fdc_dma == 2 || ft_fdc_irq == 6)) {
|
1361 |
|
|
/* Using same dma channel or irq as standard fdc, need
|
1362 |
|
|
* to disable the dma-gate on the std fdc. This
|
1363 |
|
|
* couldn't be done in the floppy driver as some
|
1364 |
|
|
* laptops are using the dma-gate to enter a low power
|
1365 |
|
|
* or even suspended state :-(
|
1366 |
|
|
*/
|
1367 |
|
|
outb_p(FDC_RESET_NOT, 0x3f2);
|
1368 |
|
|
TRACE(ft_t_noise, "DMA-gate on standard fdc disabled");
|
1369 |
|
|
}
|
1370 |
|
|
TRACE_EXIT 0;
|
1371 |
|
|
}
|
1372 |
|
|
|
1373 |
|
|
int fdc_release_irq_and_dma(void)
|
1374 |
|
|
{
|
1375 |
|
|
TRACE_FUN(ft_t_any);
|
1376 |
|
|
|
1377 |
|
|
if (fdc.hook == &do_ftape) {
|
1378 |
|
|
disable_dma(fdc.dma); /* just in case... */
|
1379 |
|
|
free_dma(fdc.dma);
|
1380 |
|
|
free_irq(fdc.irq, ftape_id);
|
1381 |
|
|
}
|
1382 |
|
|
if (ft_fdc_base != 0x3f0 && (ft_fdc_dma == 2 || ft_fdc_irq == 6)) {
|
1383 |
|
|
/* Using same dma channel as standard fdc, need to
|
1384 |
|
|
* disable the dma-gate on the std fdc. This couldn't
|
1385 |
|
|
* be done in the floppy driver as some laptops are
|
1386 |
|
|
* using the dma-gate to enter a low power or even
|
1387 |
|
|
* suspended state :-(
|
1388 |
|
|
*/
|
1389 |
|
|
outb_p(FDC_RESET_NOT | FDC_DMA_MODE, 0x3f2);
|
1390 |
|
|
TRACE(ft_t_noise, "DMA-gate on standard fdc enabled again");
|
1391 |
|
|
}
|
1392 |
|
|
TRACE_EXIT 0;
|
1393 |
|
|
}
|
1394 |
|
|
|
1395 |
|
|
int fdc_init(void)
|
1396 |
|
|
{
|
1397 |
|
|
TRACE_FUN(ft_t_any);
|
1398 |
|
|
|
1399 |
|
|
/* find a FDC to use */
|
1400 |
|
|
TRACE_CATCH(fdc_config(),);
|
1401 |
|
|
TRACE_CATCH(fdc_grab_irq_and_dma(), fdc_release_regions());
|
1402 |
|
|
ftape_motor = 0;
|
1403 |
|
|
fdc_catch_stray_interrupts(0); /* clear number of awainted
|
1404 |
|
|
* stray interrupte
|
1405 |
|
|
*/
|
1406 |
|
|
fdc_catch_stray_interrupts(1); /* one always comes (?) */
|
1407 |
|
|
TRACE(ft_t_flow, "resetting fdc");
|
1408 |
|
|
fdc_set_seek_rate(2); /* use nominal QIC step rate */
|
1409 |
|
|
fdc_reset(); /* init fdc & clear track counters */
|
1410 |
|
|
if (fdc.type == no_fdc) { /* no FC-10 or FC-20 found */
|
1411 |
|
|
fdc.type = fdc_probe();
|
1412 |
|
|
fdc_reset(); /* update with new knowledge */
|
1413 |
|
|
}
|
1414 |
|
|
if (fdc.type == no_fdc) {
|
1415 |
|
|
fdc_release_irq_and_dma();
|
1416 |
|
|
fdc_release_regions();
|
1417 |
|
|
TRACE_EXIT -ENXIO;
|
1418 |
|
|
}
|
1419 |
|
|
if (fdc.type >= i82077) {
|
1420 |
|
|
if (fdc_fifo_enable() < 0) {
|
1421 |
|
|
TRACE(ft_t_warn, "couldn't enable fdc fifo !");
|
1422 |
|
|
} else {
|
1423 |
|
|
TRACE(ft_t_flow, "fdc fifo enabled and locked");
|
1424 |
|
|
}
|
1425 |
|
|
}
|
1426 |
|
|
TRACE_EXIT 0;
|
1427 |
|
|
}
|