diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/acorn |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/acorn')
-rw-r--r-- | drivers/acorn/README | 1 | ||||
-rw-r--r-- | drivers/acorn/block/Kconfig | 36 | ||||
-rw-r--r-- | drivers/acorn/block/Makefile | 9 | ||||
-rw-r--r-- | drivers/acorn/block/fd1772.c | 1609 | ||||
-rw-r--r-- | drivers/acorn/block/fd1772dma.S | 100 | ||||
-rw-r--r-- | drivers/acorn/block/mfm.S | 162 | ||||
-rw-r--r-- | drivers/acorn/block/mfmhd.c | 1416 | ||||
-rw-r--r-- | drivers/acorn/char/Makefile | 6 | ||||
-rw-r--r-- | drivers/acorn/char/defkeymap-l7200.c | 386 | ||||
-rw-r--r-- | drivers/acorn/char/i2c.c | 369 | ||||
-rw-r--r-- | drivers/acorn/char/pcf8583.c | 239 | ||||
-rw-r--r-- | drivers/acorn/char/pcf8583.h | 41 |
12 files changed, 4374 insertions, 0 deletions
diff --git a/drivers/acorn/README b/drivers/acorn/README new file mode 100644 index 00000000000..d399c09ca61 --- /dev/null +++ b/drivers/acorn/README | |||
@@ -0,0 +1 @@ | |||
Drivers for the ACORN "podule" ARM specific bus. | |||
diff --git a/drivers/acorn/block/Kconfig b/drivers/acorn/block/Kconfig new file mode 100644 index 00000000000..073add35e66 --- /dev/null +++ b/drivers/acorn/block/Kconfig | |||
@@ -0,0 +1,36 @@ | |||
1 | # | ||
2 | # Block device driver configuration | ||
3 | # | ||
4 | |||
5 | menu "Acorn-specific block devices" | ||
6 | depends on ARCH_ACORN | ||
7 | |||
8 | config BLK_DEV_FD1772 | ||
9 | tristate "Old Archimedes floppy (1772) support" | ||
10 | depends on ARCH_ARC || ARCH_A5K | ||
11 | help | ||
12 | Support the floppy drive on the Acorn Archimedes (A300, A4x0, A540, | ||
13 | R140 and R260) series of computers; it supports only 720K floppies | ||
14 | at the moment. If you don't have one of these machines just answer | ||
15 | N. | ||
16 | |||
17 | config BLK_DEV_MFM | ||
18 | tristate "MFM harddisk support" | ||
19 | depends on ARCH_ARC || ARCH_A5K | ||
20 | help | ||
21 | Support the MFM hard drives on the Acorn Archimedes both | ||
22 | on-board the A4x0 motherboards and via the Acorn MFM podules. | ||
23 | Drives up to 64MB are supported. If you haven't got one of these | ||
24 | machines or drives just say N. | ||
25 | |||
26 | config BLK_DEV_MFM_AUTODETECT | ||
27 | bool "Autodetect hard drive geometry" | ||
28 | depends on BLK_DEV_MFM | ||
29 | help | ||
30 | If you answer Y, the MFM code will attempt to automatically detect | ||
31 | the cylinders/heads/sectors count on your hard drive. WARNING: This | ||
32 | sometimes doesn't work and it also does some dodgy stuff which | ||
33 | potentially might damage your drive. | ||
34 | |||
35 | endmenu | ||
36 | |||
diff --git a/drivers/acorn/block/Makefile b/drivers/acorn/block/Makefile new file mode 100644 index 00000000000..38a9afe8e03 --- /dev/null +++ b/drivers/acorn/block/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | # | ||
2 | # Makefile for the Acorn block device drivers. | ||
3 | # | ||
4 | |||
5 | fd1772_mod-objs := fd1772.o fd1772dma.o | ||
6 | mfmhd_mod-objs := mfmhd.o mfm.o | ||
7 | |||
8 | obj-$(CONFIG_BLK_DEV_FD1772) += fd1772_mod.o | ||
9 | obj-$(CONFIG_BLK_DEV_MFM) += mfmhd_mod.o | ||
diff --git a/drivers/acorn/block/fd1772.c b/drivers/acorn/block/fd1772.c new file mode 100644 index 00000000000..3cd2e968e96 --- /dev/null +++ b/drivers/acorn/block/fd1772.c | |||
@@ -0,0 +1,1609 @@ | |||
1 | /* | ||
2 | * linux/kernel/arch/arm/drivers/block/fd1772.c | ||
3 | * Based on ataflop.c in the m68k Linux | ||
4 | * Copyright (C) 1993 Greg Harp | ||
5 | * Atari Support by Bjoern Brauel, Roman Hodek | ||
6 | * Archimedes Support by Dave Gilbert (linux@treblig.org) | ||
7 | * | ||
8 | * Big cleanup Sep 11..14 1994 Roman Hodek: | ||
9 | * - Driver now works interrupt driven | ||
10 | * - Support for two drives; should work, but I cannot test that :-( | ||
11 | * - Reading is done in whole tracks and buffered to speed up things | ||
12 | * - Disk change detection and drive deselecting after motor-off | ||
13 | * similar to TOS | ||
14 | * - Autodetection of disk format (DD/HD); untested yet, because I | ||
15 | * don't have an HD drive :-( | ||
16 | * | ||
17 | * Fixes Nov 13 1994 Martin Schaller: | ||
18 | * - Autodetection works now | ||
19 | * - Support for 5 1/4" disks | ||
20 | * - Removed drive type (unknown on atari) | ||
21 | * - Do seeks with 8 Mhz | ||
22 | * | ||
23 | * Changes by Andreas Schwab: | ||
24 | * - After errors in multiple read mode try again reading single sectors | ||
25 | * (Feb 1995): | ||
26 | * - Clean up error handling | ||
27 | * - Set blk_size for proper size checking | ||
28 | * - Initialize track register when testing presence of floppy | ||
29 | * - Implement some ioctl's | ||
30 | * | ||
31 | * Changes by Torsten Lang: | ||
32 | * - When probing the floppies we should add the FDC1772CMDADD_H flag since | ||
33 | * the FDC1772 will otherwise wait forever when no disk is inserted... | ||
34 | * | ||
35 | * Things left to do: | ||
36 | * - Formatting | ||
37 | * - Maybe a better strategy for disk change detection (does anyone | ||
38 | * know one?) | ||
39 | * - There are some strange problems left: The strangest one is | ||
40 | * that, at least on my TT (4+4MB), the first 2 Bytes of the last | ||
41 | * page of the TT-Ram (!) change their contents (some bits get | ||
42 | * set) while a floppy DMA is going on. But there are no accesses | ||
43 | * to these memory locations from the kernel... (I tested that by | ||
44 | * making the page read-only). I cannot explain what's going on... | ||
45 | * - Sometimes the drive-change-detection stops to work. The | ||
46 | * function is still called, but the WP bit always reads as 0... | ||
47 | * Maybe a problem with the status reg mode or a timing problem. | ||
48 | * Note 10/12/94: The change detection now seems to work reliably. | ||
49 | * There is no proof, but I've seen no hang for a long time... | ||
50 | * | ||
51 | * ARCHIMEDES changes: (gilbertd@cs.man.ac.uk) | ||
52 | * 26/12/95 - Changed all names starting with FDC to FDC1772 | ||
53 | * Removed all references to clock speed of FDC - we're stuck with 8MHz | ||
54 | * Modified disk_type structure to remove HD formats | ||
55 | * | ||
56 | * 7/ 1/96 - Wrote FIQ code, removed most remaining atariisms | ||
57 | * | ||
58 | * 13/ 1/96 - Well I think its read a single sector; but there is a problem | ||
59 | * fd_rwsec_done which is called in FIQ mode starts another transfer | ||
60 | * off (in fd_rwsec) while still in FIQ mode. Because its still in | ||
61 | * FIQ mode it can't service the DMA and loses data. So need to | ||
62 | * heavily restructure. | ||
63 | * 14/ 1/96 - Found that the definitions of the register numbers of the | ||
64 | * FDC were multiplied by 2 in the header for the 16bit words | ||
65 | * of the atari so half the writes were going in the wrong place. | ||
66 | * Also realised that the FIQ entry didn't make any attempt to | ||
67 | * preserve registers or return correctly; now in assembler. | ||
68 | * | ||
69 | * 11/ 2/96 - Hmm - doesn't work on real machine. Auto detect doesn't | ||
70 | * and hacking that past seems to wait forever - check motor | ||
71 | * being turned on. | ||
72 | * | ||
73 | * 17/ 2/96 - still having problems - forcing track to -1 when selecting | ||
74 | * new drives seems to allow it to read first few sectors | ||
75 | * but then we get solid hangs at apparently random places | ||
76 | * which change depending what is happening. | ||
77 | * | ||
78 | * 9/ 3/96 - Fiddled a lot of stuff around to move to kernel 1.3.35 | ||
79 | * A lot of fiddling in DMA stuff. Having problems with it | ||
80 | * constnatly thinking its timeing out. Ah - its timeout | ||
81 | * was set to (6*HZ) rather than jiffies+(6*HZ). Now giving | ||
82 | * duff data! | ||
83 | * | ||
84 | * 5/ 4/96 - Made it use the new IOC_ macros rather than *ioc | ||
85 | * Hmm - giving unexpected FIQ and then timeouts | ||
86 | * 18/ 8/96 - Ran through indent -kr -i8 | ||
87 | * Some changes to disc change detect; don't know how well it | ||
88 | * works. | ||
89 | * 24/ 8/96 - Put all the track buffering code back in from the atari | ||
90 | * code - I wonder if it will still work... No :-) | ||
91 | * Still works if I turn off track buffering. | ||
92 | * 25/ 8/96 - Changed the timer expires that I'd added back to be | ||
93 | * jiffies + ....; and it all sprang to life! Got 2.8K/sec | ||
94 | * off a cp -r of a 679K disc (showed 94% cpu usage!) | ||
95 | * (PC gets 14.3K/sec - 0% CPU!) Hmm - hard drive corrupt! | ||
96 | * Also perhaps that compile was with cache off. | ||
97 | * changed cli in fd_readtrack_check to cliIF | ||
98 | * changed vmallocs to kmalloc (whats the difference!!) | ||
99 | * Removed the busy wait loop in do_fd_request and replaced | ||
100 | * by a routine on tq_immediate; only 11% cpu on a dd off the | ||
101 | * raw disc - but the speed is the same. | ||
102 | * 1/ 9/96 - Idea (failed!) - set the 'disable spin-up sequence' | ||
103 | * when we read the track if we know the motor is on; didn't | ||
104 | * help - perhaps we have to do it in stepping as well. | ||
105 | * Nope. Still doesn't help. | ||
106 | * Hmm - what seems to be happening is that fd_readtrack_check | ||
107 | * is never getting called. Its job is to terminate the read | ||
108 | * just after we think we should have got the data; otherwise | ||
109 | * the fdc takes 1 second to timeout; which is what's happening | ||
110 | * Now I can see 'readtrack_timer' being set (which should do the | ||
111 | * call); but it never seems to be called - hmm! | ||
112 | * OK - I've moved the check to my tq_immediate code - | ||
113 | * and it WORKS! 13.95K/second at 19% CPU. | ||
114 | * I wish I knew why that timer didn't work..... | ||
115 | * | ||
116 | * 16/11/96 - Fiddled and frigged for 2.0.18 | ||
117 | * | ||
118 | * DAG 30/01/99 - Started frobbing for 2.2.1 | ||
119 | * DAG 20/06/99 - A little more frobbing: | ||
120 | * Included include/asm/uaccess.h for get_user/put_user | ||
121 | * | ||
122 | * DAG 1/09/00 - Dusted off for 2.4.0-test7 | ||
123 | * MAX_SECTORS was name clashing so it is now FD1772_... | ||
124 | * Minor parameter, name layouts for 2.4.x differences | ||
125 | */ | ||
126 | |||
127 | #include <linux/sched.h> | ||
128 | #include <linux/fs.h> | ||
129 | #include <linux/fcntl.h> | ||
130 | #include <linux/slab.h> | ||
131 | #include <linux/kernel.h> | ||
132 | #include <linux/interrupt.h> | ||
133 | #include <linux/timer.h> | ||
134 | #include <linux/workqueue.h> | ||
135 | #include <linux/fd.h> | ||
136 | #include <linux/fd1772.h> | ||
137 | #include <linux/errno.h> | ||
138 | #include <linux/types.h> | ||
139 | #include <linux/delay.h> | ||
140 | #include <linux/mm.h> | ||
141 | #include <linux/bitops.h> | ||
142 | |||
143 | #include <asm/arch/oldlatches.h> | ||
144 | #include <asm/dma.h> | ||
145 | #include <asm/hardware.h> | ||
146 | #include <asm/hardware/ioc.h> | ||
147 | #include <asm/io.h> | ||
148 | #include <asm/irq.h> | ||
149 | #include <asm/mach-types.h> | ||
150 | #include <asm/pgtable.h> | ||
151 | #include <asm/system.h> | ||
152 | #include <asm/uaccess.h> | ||
153 | |||
154 | |||
155 | /* Note: FD_MAX_UNITS could be redefined to 2 for the Atari (with | ||
156 | * little additional rework in this file). But I'm not yet sure if | ||
157 | * some other code depends on the number of floppies... (It is defined | ||
158 | * in a public header!) | ||
159 | */ | ||
160 | #if 0 | ||
161 | #undef FD_MAX_UNITS | ||
162 | #define FD_MAX_UNITS 2 | ||
163 | #endif | ||
164 | |||
165 | /* Ditto worries for Arc - DAG */ | ||
166 | #define FD_MAX_UNITS 4 | ||
167 | #define TRACKBUFFER 0 | ||
168 | /*#define DEBUG*/ | ||
169 | |||
170 | #ifdef DEBUG | ||
171 | #define DPRINT(a) printk a | ||
172 | #else | ||
173 | #define DPRINT(a) | ||
174 | #endif | ||
175 | |||
176 | static struct request_queue *floppy_queue; | ||
177 | |||
178 | #define MAJOR_NR FLOPPY_MAJOR | ||
179 | #define FLOPPY_DMA 0 | ||
180 | #define DEVICE_NAME "floppy" | ||
181 | #define QUEUE (floppy_queue) | ||
182 | #define CURRENT elv_next_request(floppy_queue) | ||
183 | |||
184 | /* Disk types: DD */ | ||
185 | static struct archy_disk_type { | ||
186 | const char *name; | ||
187 | unsigned spt; /* sectors per track */ | ||
188 | unsigned blocks; /* total number of blocks */ | ||
189 | unsigned stretch; /* track doubling ? */ | ||
190 | } disk_type[] = { | ||
191 | |||
192 | { "d360", 9, 720, 0 }, /* 360kB diskette */ | ||
193 | { "D360", 9, 720, 1 }, /* 360kb in 720kb drive */ | ||
194 | { "D720", 9, 1440, 0 }, /* 720kb diskette (DD) */ | ||
195 | /*{ "D820", 10,1640, 0}, *//* DD disk with 82 tracks/10 sectors | ||
196 | - DAG - can't see how type detect can distinguish this | ||
197 | from 720K until it reads block 4 by which time its too late! */ | ||
198 | }; | ||
199 | |||
200 | #define NUM_DISK_TYPES (sizeof(disk_type)/sizeof(*disk_type)) | ||
201 | |||
202 | /* | ||
203 | * Maximum disk size (in kilobytes). This default is used whenever the | ||
204 | * current disk size is unknown. | ||
205 | */ | ||
206 | #define MAX_DISK_SIZE 720 | ||
207 | |||
208 | static struct gendisk *disks[FD_MAX_UNIT]; | ||
209 | |||
210 | /* current info on each unit */ | ||
211 | static struct archy_floppy_struct { | ||
212 | int connected; /* !=0 : drive is connected */ | ||
213 | int autoprobe; /* !=0 : do autoprobe */ | ||
214 | |||
215 | struct archy_disk_type *disktype; /* current type of disk */ | ||
216 | |||
217 | int track; /* current head position or -1 | ||
218 | * if unknown */ | ||
219 | unsigned int steprate; /* steprate setting */ | ||
220 | unsigned int wpstat; /* current state of WP signal | ||
221 | * (for disk change detection) */ | ||
222 | } unit[FD_MAX_UNITS]; | ||
223 | |||
224 | /* DAG: On Arc we spin on a flag being cleared by fdc1772_comendhandler which | ||
225 | is an assembler routine */ | ||
226 | extern void fdc1772_comendhandler(void); /* Actually doens't have these parameters - see fd1772.S */ | ||
227 | extern volatile int fdc1772_comendstatus; | ||
228 | extern volatile int fdc1772_fdc_int_done; | ||
229 | |||
230 | #define FDC1772BASE ((0x210000>>2)|0x80000000) | ||
231 | |||
232 | #define FDC1772_READ(reg) inb(FDC1772BASE+(reg/2)) | ||
233 | |||
234 | /* DAG: You wouldn't be silly to ask why FDC1772_WRITE is a function rather | ||
235 | than the #def below - well simple - the #def won't compile - and I | ||
236 | don't understand why (__outwc not defined) */ | ||
237 | /* NOTE: Reg is 0,2,4,6 as opposed to 0,1,2,3 or 0,4,8,12 to keep compatibility | ||
238 | with the ST version of fd1772.h */ | ||
239 | /*#define FDC1772_WRITE(reg,val) outw(val,(reg+FDC1772BASE)); */ | ||
240 | void FDC1772_WRITE(int reg, unsigned char val) | ||
241 | { | ||
242 | if (reg == FDC1772REG_CMD) { | ||
243 | DPRINT(("FDC1772_WRITE new command 0x%x @ %d\n", val,jiffies)); | ||
244 | if (fdc1772_fdc_int_done) { | ||
245 | DPRINT(("FDC1772_WRITE: Hmm fdc1772_fdc_int_done true - resetting\n")); | ||
246 | fdc1772_fdc_int_done = 0; | ||
247 | }; | ||
248 | }; | ||
249 | outb(val, (reg / 2) + FDC1772BASE); | ||
250 | }; | ||
251 | |||
252 | #define FD1772_MAX_SECTORS 22 | ||
253 | |||
254 | unsigned char *DMABuffer; /* buffer for writes */ | ||
255 | /*static unsigned long PhysDMABuffer; *//* physical address */ | ||
256 | /* DAG: On Arc we just go straight for the DMA buffer */ | ||
257 | #define PhysDMABuffer DMABuffer | ||
258 | |||
259 | #ifdef TRACKBUFFER | ||
260 | unsigned char *TrackBuffer; /* buffer for reads */ | ||
261 | #define PhysTrackBuffer TrackBuffer /* physical address */ | ||
262 | static int BufferDrive, BufferSide, BufferTrack; | ||
263 | static int read_track; /* non-zero if we are reading whole tracks */ | ||
264 | |||
265 | #define SECTOR_BUFFER(sec) (TrackBuffer + ((sec)-1)*512) | ||
266 | #define IS_BUFFERED(drive,side,track) \ | ||
267 | (BufferDrive == (drive) && BufferSide == (side) && BufferTrack == (track)) | ||
268 | #endif | ||
269 | |||
270 | /* | ||
271 | * These are global variables, as that's the easiest way to give | ||
272 | * information to interrupts. They are the data used for the current | ||
273 | * request. | ||
274 | */ | ||
275 | static int SelectedDrive = 0; | ||
276 | static int ReqCmd, ReqBlock; | ||
277 | static int ReqSide, ReqTrack, ReqSector, ReqCnt; | ||
278 | static int HeadSettleFlag = 0; | ||
279 | static unsigned char *ReqData, *ReqBuffer; | ||
280 | static int MotorOn = 0, MotorOffTrys; | ||
281 | |||
282 | /* Synchronization of FDC1772 access. */ | ||
283 | static volatile int fdc_busy = 0; | ||
284 | static DECLARE_WAIT_QUEUE_HEAD(fdc_wait); | ||
285 | |||
286 | |||
287 | /* long req'd for set_bit --RR */ | ||
288 | static unsigned long changed_floppies = 0xff, fake_change = 0; | ||
289 | #define CHECK_CHANGE_DELAY HZ/2 | ||
290 | |||
291 | /* DAG - increased to 30*HZ - not sure if this is the correct thing to do */ | ||
292 | #define FD_MOTOR_OFF_DELAY (10*HZ) | ||
293 | #define FD_MOTOR_OFF_MAXTRY (10*20) | ||
294 | |||
295 | #define FLOPPY_TIMEOUT (6*HZ) | ||
296 | #define RECALIBRATE_ERRORS 4 /* After this many errors the drive | ||
297 | * will be recalibrated. */ | ||
298 | #define MAX_ERRORS 8 /* After this many errors the driver | ||
299 | * will give up. */ | ||
300 | |||
301 | #define START_MOTOR_OFF_TIMER(delay) \ | ||
302 | do { \ | ||
303 | motor_off_timer.expires = jiffies + (delay); \ | ||
304 | add_timer( &motor_off_timer ); \ | ||
305 | MotorOffTrys = 0; \ | ||
306 | } while(0) | ||
307 | |||
308 | #define START_CHECK_CHANGE_TIMER(delay) \ | ||
309 | do { \ | ||
310 | mod_timer(&fd_timer, jiffies + (delay)); \ | ||
311 | } while(0) | ||
312 | |||
313 | #define START_TIMEOUT() \ | ||
314 | do { \ | ||
315 | mod_timer(&timeout_timer, jiffies+FLOPPY_TIMEOUT); \ | ||
316 | } while(0) | ||
317 | |||
318 | #define STOP_TIMEOUT() \ | ||
319 | do { \ | ||
320 | del_timer( &timeout_timer ); \ | ||
321 | } while(0) | ||
322 | |||
323 | #define ENABLE_IRQ() enable_irq(FIQ_FD1772+64); | ||
324 | |||
325 | #define DISABLE_IRQ() disable_irq(FIQ_FD1772+64); | ||
326 | |||
327 | static void fd1772_checkint(void); | ||
328 | |||
329 | DECLARE_WORK(fd1772_tq, (void *)fd1772_checkint, NULL); | ||
330 | /* | ||
331 | * The driver is trying to determine the correct media format | ||
332 | * while Probing is set. fd_rwsec_done() clears it after a | ||
333 | * successful access. | ||
334 | */ | ||
335 | static int Probing = 0; | ||
336 | |||
337 | /* This flag is set when a dummy seek is necessary to make the WP | ||
338 | * status bit accessible. | ||
339 | */ | ||
340 | static int NeedSeek = 0; | ||
341 | |||
342 | |||
343 | /***************************** Prototypes *****************************/ | ||
344 | |||
345 | static void fd_select_side(int side); | ||
346 | static void fd_select_drive(int drive); | ||
347 | static void fd_deselect(void); | ||
348 | static void fd_motor_off_timer(unsigned long dummy); | ||
349 | static void check_change(unsigned long dummy); | ||
350 | static void floppy_irqconsequencehandler(void); | ||
351 | static void fd_error(void); | ||
352 | static void do_fd_action(int drive); | ||
353 | static void fd_calibrate(void); | ||
354 | static void fd_calibrate_done(int status); | ||
355 | static void fd_seek(void); | ||
356 | static void fd_seek_done(int status); | ||
357 | static void fd_rwsec(void); | ||
358 | #ifdef TRACKBUFFER | ||
359 | static void fd_readtrack_check( unsigned long dummy ); | ||
360 | #endif | ||
361 | static void fd_rwsec_done(int status); | ||
362 | static void fd_times_out(unsigned long dummy); | ||
363 | static void finish_fdc(void); | ||
364 | static void finish_fdc_done(int dummy); | ||
365 | static void floppy_off(unsigned int nr); | ||
366 | static void setup_req_params(int drive); | ||
367 | static void redo_fd_request(void); | ||
368 | static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int | ||
369 | cmd, unsigned long param); | ||
370 | static void fd_probe(int drive); | ||
371 | static int fd_test_drive_present(int drive); | ||
372 | static void config_types(void); | ||
373 | static int floppy_open(struct inode *inode, struct file *filp); | ||
374 | static int floppy_release(struct inode *inode, struct file *filp); | ||
375 | static void do_fd_request(request_queue_t *); | ||
376 | |||
377 | /************************* End of Prototypes **************************/ | ||
378 | |||
379 | static struct timer_list motor_off_timer = | ||
380 | TIMER_INITIALIZER(fd_motor_off_timer, 0, 0); | ||
381 | |||
382 | #ifdef TRACKBUFFER | ||
383 | static struct timer_list readtrack_timer = | ||
384 | TIMER_INITIALIZER(fd_readtrack_check, 0, 0); | ||
385 | #endif | ||
386 | |||
387 | static struct timer_list timeout_timer = | ||
388 | TIMER_INITIALIZER(fd_times_out, 0, 0); | ||
389 | |||
390 | static struct timer_list fd_timer = | ||
391 | TIMER_INITIALIZER(check_change, 0, 0); | ||
392 | |||
393 | /* DAG: Haven't got a clue what this is? */ | ||
394 | int stdma_islocked(void) | ||
395 | { | ||
396 | return 0; | ||
397 | }; | ||
398 | |||
399 | /* Select the side to use. */ | ||
400 | |||
401 | static void fd_select_side(int side) | ||
402 | { | ||
403 | oldlatch_aupdate(LATCHA_SIDESEL, side ? 0 : LATCHA_SIDESEL); | ||
404 | } | ||
405 | |||
406 | |||
407 | /* Select a drive, update the FDC1772's track register | ||
408 | */ | ||
409 | |||
410 | static void fd_select_drive(int drive) | ||
411 | { | ||
412 | #ifdef DEBUG | ||
413 | printk("fd_select_drive:%d\n", drive); | ||
414 | #endif | ||
415 | /* Hmm - nowhere do we seem to turn the motor on - I'm going to do it here! */ | ||
416 | oldlatch_aupdate(LATCHA_MOTOR | LATCHA_INUSE, 0); | ||
417 | |||
418 | if (drive == SelectedDrive) | ||
419 | return; | ||
420 | |||
421 | oldlatch_aupdate(LATCHA_FDSELALL, 0xf - (1 << drive)); | ||
422 | |||
423 | /* restore track register to saved value */ | ||
424 | FDC1772_WRITE(FDC1772REG_TRACK, unit[drive].track); | ||
425 | udelay(25); | ||
426 | |||
427 | SelectedDrive = drive; | ||
428 | } | ||
429 | |||
430 | |||
431 | /* Deselect both drives. */ | ||
432 | |||
433 | static void fd_deselect(void) | ||
434 | { | ||
435 | unsigned long flags; | ||
436 | |||
437 | DPRINT(("fd_deselect\n")); | ||
438 | |||
439 | oldlatch_aupdate(LATCHA_FDSELALL | LATCHA_MOTOR | LATCHA_INUSE, 0xf | LATCHA_MOTOR | LATCHA_INUSE); | ||
440 | |||
441 | SelectedDrive = -1; | ||
442 | } | ||
443 | |||
444 | |||
445 | /* This timer function deselects the drives when the FDC1772 switched the | ||
446 | * motor off. The deselection cannot happen earlier because the FDC1772 | ||
447 | * counts the index signals, which arrive only if one drive is selected. | ||
448 | */ | ||
449 | |||
450 | static void fd_motor_off_timer(unsigned long dummy) | ||
451 | { | ||
452 | unsigned long flags; | ||
453 | unsigned char status; | ||
454 | int delay; | ||
455 | |||
456 | del_timer(&motor_off_timer); | ||
457 | |||
458 | if (SelectedDrive < 0) | ||
459 | /* no drive selected, needn't deselect anyone */ | ||
460 | return; | ||
461 | |||
462 | save_flags(flags); | ||
463 | cli(); | ||
464 | |||
465 | if (fdc_busy) /* was stdma_islocked */ | ||
466 | goto retry; | ||
467 | |||
468 | status = FDC1772_READ(FDC1772REG_STATUS); | ||
469 | |||
470 | if (!(status & 0x80)) { | ||
471 | /* | ||
472 | * motor already turned off by FDC1772 -> deselect drives | ||
473 | * In actual fact its this deselection which turns the motor | ||
474 | * off on the Arc, since the motor control is actually on | ||
475 | * Latch A | ||
476 | */ | ||
477 | DPRINT(("fdc1772: deselecting in fd_motor_off_timer\n")); | ||
478 | fd_deselect(); | ||
479 | MotorOn = 0; | ||
480 | restore_flags(flags); | ||
481 | return; | ||
482 | } | ||
483 | /* not yet off, try again */ | ||
484 | |||
485 | retry: | ||
486 | restore_flags(flags); | ||
487 | /* Test again later; if tested too often, it seems there is no disk | ||
488 | * in the drive and the FDC1772 will leave the motor on forever (or, | ||
489 | * at least until a disk is inserted). So we'll test only twice | ||
490 | * per second from then on... | ||
491 | */ | ||
492 | delay = (MotorOffTrys < FD_MOTOR_OFF_MAXTRY) ? | ||
493 | (++MotorOffTrys, HZ / 20) : HZ / 2; | ||
494 | START_MOTOR_OFF_TIMER(delay); | ||
495 | } | ||
496 | |||
497 | |||
498 | /* This function is repeatedly called to detect disk changes (as good | ||
499 | * as possible) and keep track of the current state of the write protection. | ||
500 | */ | ||
501 | |||
502 | static void check_change(unsigned long dummy) | ||
503 | { | ||
504 | static int drive = 0; | ||
505 | |||
506 | unsigned long flags; | ||
507 | int stat; | ||
508 | |||
509 | if (fdc_busy) | ||
510 | return; /* Don't start poking about if the fdc is busy */ | ||
511 | |||
512 | return; /* let's just forget it for the mo DAG */ | ||
513 | |||
514 | if (++drive > 1 || !unit[drive].connected) | ||
515 | drive = 0; | ||
516 | |||
517 | save_flags(flags); | ||
518 | cli(); | ||
519 | |||
520 | if (!stdma_islocked()) { | ||
521 | stat = !!(FDC1772_READ(FDC1772REG_STATUS) & FDC1772STAT_WPROT); | ||
522 | |||
523 | /* The idea here is that if the write protect line has changed then | ||
524 | the disc must have changed */ | ||
525 | if (stat != unit[drive].wpstat) { | ||
526 | DPRINT(("wpstat[%d] = %d\n", drive, stat)); | ||
527 | unit[drive].wpstat = stat; | ||
528 | set_bit(drive, &changed_floppies); | ||
529 | } | ||
530 | } | ||
531 | restore_flags(flags); | ||
532 | |||
533 | START_CHECK_CHANGE_TIMER(CHECK_CHANGE_DELAY); | ||
534 | } | ||
535 | |||
536 | |||
537 | /* Handling of the Head Settling Flag: This flag should be set after each | ||
538 | * seek operation, because we don't use seeks with verify. | ||
539 | */ | ||
540 | |||
541 | static inline void set_head_settle_flag(void) | ||
542 | { | ||
543 | HeadSettleFlag = FDC1772CMDADD_E; | ||
544 | } | ||
545 | |||
546 | static inline int get_head_settle_flag(void) | ||
547 | { | ||
548 | int tmp = HeadSettleFlag; | ||
549 | HeadSettleFlag = 0; | ||
550 | return (tmp); | ||
551 | } | ||
552 | |||
553 | |||
554 | |||
555 | |||
556 | /* General Interrupt Handling */ | ||
557 | |||
558 | static inline void copy_buffer(void *from, void *to) | ||
559 | { | ||
560 | ulong *p1 = (ulong *) from, *p2 = (ulong *) to; | ||
561 | int cnt; | ||
562 | |||
563 | for (cnt = 512 / 4; cnt; cnt--) | ||
564 | *p2++ = *p1++; | ||
565 | } | ||
566 | |||
567 | static void (*FloppyIRQHandler) (int status) = NULL; | ||
568 | |||
569 | static void floppy_irqconsequencehandler(void) | ||
570 | { | ||
571 | unsigned char status; | ||
572 | void (*handler) (int); | ||
573 | |||
574 | fdc1772_fdc_int_done = 0; | ||
575 | |||
576 | handler = FloppyIRQHandler; | ||
577 | FloppyIRQHandler = NULL; | ||
578 | |||
579 | if (handler) { | ||
580 | nop(); | ||
581 | status = (unsigned char) fdc1772_comendstatus; | ||
582 | DPRINT(("FDC1772 irq, status = %02x handler = %08lx\n", (unsigned int) status, (unsigned long) handler)); | ||
583 | handler(status); | ||
584 | } else { | ||
585 | DPRINT(("FDC1772 irq, no handler status=%02x\n", fdc1772_comendstatus)); | ||
586 | } | ||
587 | DPRINT(("FDC1772 irq: end of floppy_irq\n")); | ||
588 | } | ||
589 | |||
590 | |||
591 | /* Error handling: If some error happened, retry some times, then | ||
592 | * recalibrate, then try again, and fail after MAX_ERRORS. | ||
593 | */ | ||
594 | |||
595 | static void fd_error(void) | ||
596 | { | ||
597 | printk("FDC1772: fd_error\n"); | ||
598 | /*panic("fd1772: fd_error"); *//* DAG tmp */ | ||
599 | if (!CURRENT) | ||
600 | return; | ||
601 | CURRENT->errors++; | ||
602 | if (CURRENT->errors >= MAX_ERRORS) { | ||
603 | printk("fd%d: too many errors.\n", SelectedDrive); | ||
604 | end_request(CURRENT, 0); | ||
605 | } else if (CURRENT->errors == RECALIBRATE_ERRORS) { | ||
606 | printk("fd%d: recalibrating\n", SelectedDrive); | ||
607 | if (SelectedDrive != -1) | ||
608 | unit[SelectedDrive].track = -1; | ||
609 | } | ||
610 | redo_fd_request(); | ||
611 | } | ||
612 | |||
613 | |||
614 | |||
615 | #define SET_IRQ_HANDLER(proc) do { FloppyIRQHandler = (proc); } while(0) | ||
616 | |||
617 | |||
618 | /* do_fd_action() is the general procedure for a fd request: All | ||
619 | * required parameter settings (drive select, side select, track | ||
620 | * position) are checked and set if needed. For each of these | ||
621 | * parameters and the actual reading or writing exist two functions: | ||
622 | * one that starts the setting (or skips it if possible) and one | ||
623 | * callback for the "done" interrupt. Each done func calls the next | ||
624 | * set function to propagate the request down to fd_rwsec_done(). | ||
625 | */ | ||
626 | |||
627 | static void do_fd_action(int drive) | ||
628 | { | ||
629 | struct request *req; | ||
630 | DPRINT(("do_fd_action unit[drive].track=%d\n", unit[drive].track)); | ||
631 | |||
632 | #ifdef TRACKBUFFER | ||
633 | repeat: | ||
634 | |||
635 | if (IS_BUFFERED( drive, ReqSide, ReqTrack )) { | ||
636 | req = CURRENT; | ||
637 | if (ReqCmd == READ) { | ||
638 | copy_buffer( SECTOR_BUFFER(ReqSector), ReqData ); | ||
639 | if (++ReqCnt < req->current_nr_sectors) { | ||
640 | /* read next sector */ | ||
641 | setup_req_params( drive ); | ||
642 | goto repeat; | ||
643 | } else { | ||
644 | /* all sectors finished */ | ||
645 | req->nr_sectors -= req->current_nr_sectors; | ||
646 | req->sector += req->current_nr_sectors; | ||
647 | end_request(req, 1); | ||
648 | redo_fd_request(); | ||
649 | return; | ||
650 | } | ||
651 | } else { | ||
652 | /* cmd == WRITE, pay attention to track buffer | ||
653 | * consistency! */ | ||
654 | copy_buffer( ReqData, SECTOR_BUFFER(ReqSector) ); | ||
655 | } | ||
656 | } | ||
657 | #endif | ||
658 | |||
659 | if (SelectedDrive != drive) { | ||
660 | /*unit[drive].track = -1; DAG */ | ||
661 | fd_select_drive(drive); | ||
662 | }; | ||
663 | |||
664 | |||
665 | if (unit[drive].track == -1) | ||
666 | fd_calibrate(); | ||
667 | else if (unit[drive].track != ReqTrack << unit[drive].disktype->stretch) | ||
668 | fd_seek(); | ||
669 | else | ||
670 | fd_rwsec(); | ||
671 | } | ||
672 | |||
673 | |||
674 | /* Seek to track 0 if the current track is unknown */ | ||
675 | |||
676 | static void fd_calibrate(void) | ||
677 | { | ||
678 | DPRINT(("fd_calibrate\n")); | ||
679 | if (unit[SelectedDrive].track >= 0) { | ||
680 | fd_calibrate_done(0); | ||
681 | return; | ||
682 | } | ||
683 | DPRINT(("fd_calibrate (after track compare)\n")); | ||
684 | SET_IRQ_HANDLER(fd_calibrate_done); | ||
685 | /* we can't verify, since the speed may be incorrect */ | ||
686 | FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_RESTORE | unit[SelectedDrive].steprate); | ||
687 | |||
688 | NeedSeek = 1; | ||
689 | MotorOn = 1; | ||
690 | START_TIMEOUT(); | ||
691 | /* wait for IRQ */ | ||
692 | } | ||
693 | |||
694 | |||
695 | static void fd_calibrate_done(int status) | ||
696 | { | ||
697 | DPRINT(("fd_calibrate_done()\n")); | ||
698 | STOP_TIMEOUT(); | ||
699 | |||
700 | /* set the correct speed now */ | ||
701 | if (status & FDC1772STAT_RECNF) { | ||
702 | printk("fd%d: restore failed\n", SelectedDrive); | ||
703 | fd_error(); | ||
704 | } else { | ||
705 | unit[SelectedDrive].track = 0; | ||
706 | fd_seek(); | ||
707 | } | ||
708 | } | ||
709 | |||
710 | |||
711 | /* Seek the drive to the requested track. The drive must have been | ||
712 | * calibrated at some point before this. | ||
713 | */ | ||
714 | |||
715 | static void fd_seek(void) | ||
716 | { | ||
717 | unsigned long flags; | ||
718 | DPRINT(("fd_seek() to track %d (unit[SelectedDrive].track=%d)\n", ReqTrack, | ||
719 | unit[SelectedDrive].track)); | ||
720 | if (unit[SelectedDrive].track == ReqTrack << | ||
721 | unit[SelectedDrive].disktype->stretch) { | ||
722 | fd_seek_done(0); | ||
723 | return; | ||
724 | } | ||
725 | FDC1772_WRITE(FDC1772REG_DATA, ReqTrack << | ||
726 | unit[SelectedDrive].disktype->stretch); | ||
727 | udelay(25); | ||
728 | save_flags(flags); | ||
729 | clf(); | ||
730 | SET_IRQ_HANDLER(fd_seek_done); | ||
731 | FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK | unit[SelectedDrive].steprate | | ||
732 | /* DAG */ | ||
733 | (MotorOn?FDC1772CMDADD_H:0)); | ||
734 | |||
735 | restore_flags(flags); | ||
736 | MotorOn = 1; | ||
737 | set_head_settle_flag(); | ||
738 | START_TIMEOUT(); | ||
739 | /* wait for IRQ */ | ||
740 | } | ||
741 | |||
742 | |||
743 | static void fd_seek_done(int status) | ||
744 | { | ||
745 | DPRINT(("fd_seek_done()\n")); | ||
746 | STOP_TIMEOUT(); | ||
747 | |||
748 | /* set the correct speed */ | ||
749 | if (status & FDC1772STAT_RECNF) { | ||
750 | printk("fd%d: seek error (to track %d)\n", | ||
751 | SelectedDrive, ReqTrack); | ||
752 | /* we don't know exactly which track we are on now! */ | ||
753 | unit[SelectedDrive].track = -1; | ||
754 | fd_error(); | ||
755 | } else { | ||
756 | unit[SelectedDrive].track = ReqTrack << | ||
757 | unit[SelectedDrive].disktype->stretch; | ||
758 | NeedSeek = 0; | ||
759 | fd_rwsec(); | ||
760 | } | ||
761 | } | ||
762 | |||
763 | |||
764 | /* This does the actual reading/writing after positioning the head | ||
765 | * over the correct track. | ||
766 | */ | ||
767 | |||
768 | #ifdef TRACKBUFFER | ||
769 | static int MultReadInProgress = 0; | ||
770 | #endif | ||
771 | |||
772 | |||
773 | static void fd_rwsec(void) | ||
774 | { | ||
775 | unsigned long paddr, flags; | ||
776 | unsigned int rwflag, old_motoron; | ||
777 | unsigned int track; | ||
778 | |||
779 | DPRINT(("fd_rwsec(), Sec=%d, Access=%c\n", ReqSector, ReqCmd == WRITE ? 'w' : 'r')); | ||
780 | if (ReqCmd == WRITE) { | ||
781 | /*cache_push( (unsigned long)ReqData, 512 ); */ | ||
782 | paddr = (unsigned long) ReqData; | ||
783 | rwflag = 0x100; | ||
784 | } else { | ||
785 | paddr = (unsigned long) PhysDMABuffer; | ||
786 | #ifdef TRACKBUFFER | ||
787 | if (read_track) | ||
788 | paddr = (unsigned long)PhysTrackBuffer; | ||
789 | #endif | ||
790 | rwflag = 0; | ||
791 | } | ||
792 | |||
793 | DPRINT(("fd_rwsec() before sidesel rwflag=%d sec=%d trk=%d\n", rwflag, | ||
794 | ReqSector, FDC1772_READ(FDC1772REG_TRACK))); | ||
795 | fd_select_side(ReqSide); | ||
796 | |||
797 | /*DPRINT(("fd_rwsec() before start sector \n")); */ | ||
798 | /* Start sector of this operation */ | ||
799 | #ifdef TRACKBUFFER | ||
800 | FDC1772_WRITE( FDC1772REG_SECTOR, !read_track ? ReqSector : 1 ); | ||
801 | #else | ||
802 | FDC1772_WRITE( FDC1772REG_SECTOR, ReqSector ); | ||
803 | #endif | ||
804 | |||
805 | /* Cheat for track if stretch != 0 */ | ||
806 | if (unit[SelectedDrive].disktype->stretch) { | ||
807 | track = FDC1772_READ(FDC1772REG_TRACK); | ||
808 | FDC1772_WRITE(FDC1772REG_TRACK, track >> | ||
809 | unit[SelectedDrive].disktype->stretch); | ||
810 | } | ||
811 | udelay(25); | ||
812 | |||
813 | DPRINT(("fd_rwsec() before setup DMA \n")); | ||
814 | /* Setup DMA - Heavily modified by DAG */ | ||
815 | save_flags(flags); | ||
816 | clf(); | ||
817 | disable_dma(FLOPPY_DMA); | ||
818 | set_dma_mode(FLOPPY_DMA, rwflag ? DMA_MODE_WRITE : DMA_MODE_READ); | ||
819 | set_dma_addr(FLOPPY_DMA, (long) paddr); /* DAG - changed from Atari specific */ | ||
820 | #ifdef TRACKBUFFER | ||
821 | set_dma_count(FLOPPY_DMA,(!read_track ? 1 : unit[SelectedDrive].disktype->spt)*512); | ||
822 | #else | ||
823 | set_dma_count(FLOPPY_DMA, 512); /* Block/sector size - going to have to change */ | ||
824 | #endif | ||
825 | SET_IRQ_HANDLER(fd_rwsec_done); | ||
826 | /* Turn on dma int */ | ||
827 | enable_dma(FLOPPY_DMA); | ||
828 | /* Now give it something to do */ | ||
829 | FDC1772_WRITE(FDC1772REG_CMD, (rwflag ? (FDC1772CMD_WRSEC | FDC1772CMDADD_P) : | ||
830 | #ifdef TRACKBUFFER | ||
831 | (FDC1772CMD_RDSEC | (read_track ? FDC1772CMDADD_M : 0) | | ||
832 | /* Hmm - the idea here is to stop the FDC spinning the disc | ||
833 | up when we know that we already still have it spinning */ | ||
834 | (MotorOn?FDC1772CMDADD_H:0)) | ||
835 | #else | ||
836 | FDC1772CMD_RDSEC | ||
837 | #endif | ||
838 | )); | ||
839 | |||
840 | restore_flags(flags); | ||
841 | DPRINT(("fd_rwsec() after DMA setup flags=0x%08x\n", flags)); | ||
842 | /*sti(); *//* DAG - Hmm */ | ||
843 | /* Hmm - should do something DAG */ | ||
844 | old_motoron = MotorOn; | ||
845 | MotorOn = 1; | ||
846 | NeedSeek = 1; | ||
847 | |||
848 | /* wait for interrupt */ | ||
849 | |||
850 | #ifdef TRACKBUFFER | ||
851 | if (read_track) { | ||
852 | /* | ||
853 | * If reading a whole track, wait about one disk rotation and | ||
854 | * then check if all sectors are read. The FDC will even | ||
855 | * search for the first non-existant sector and need 1 sec to | ||
856 | * recognise that it isn't present :-( | ||
857 | */ | ||
858 | /* 1 rot. + 5 rot.s if motor was off */ | ||
859 | mod_timer(&readtrack_timer, jiffies + HZ/5 + (old_motoron ? 0 : HZ)); | ||
860 | DPRINT(("Setting readtrack_timer to %d @ %d\n", | ||
861 | readtrack_timer.expires,jiffies)); | ||
862 | MultReadInProgress = 1; | ||
863 | } | ||
864 | #endif | ||
865 | |||
866 | /*DPRINT(("fd_rwsec() before START_TIMEOUT \n")); */ | ||
867 | START_TIMEOUT(); | ||
868 | /*DPRINT(("fd_rwsec() after START_TIMEOUT \n")); */ | ||
869 | } | ||
870 | |||
871 | |||
872 | #ifdef TRACKBUFFER | ||
873 | |||
874 | static void fd_readtrack_check(unsigned long dummy) | ||
875 | { | ||
876 | unsigned long flags, addr; | ||
877 | extern unsigned char *fdc1772_dataaddr; | ||
878 | |||
879 | DPRINT(("fd_readtrack_check @ %d\n",jiffies)); | ||
880 | |||
881 | save_flags(flags); | ||
882 | clf(); | ||
883 | |||
884 | del_timer( &readtrack_timer ); | ||
885 | |||
886 | if (!MultReadInProgress) { | ||
887 | /* This prevents a race condition that could arise if the | ||
888 | * interrupt is triggered while the calling of this timer | ||
889 | * callback function takes place. The IRQ function then has | ||
890 | * already cleared 'MultReadInProgress' when control flow | ||
891 | * gets here. | ||
892 | */ | ||
893 | restore_flags(flags); | ||
894 | return; | ||
895 | } | ||
896 | |||
897 | /* get the current DMA address */ | ||
898 | addr=(unsigned long)fdc1772_dataaddr; /* DAG - ? */ | ||
899 | DPRINT(("fd_readtrack_check: addr=%x PhysTrackBuffer=%x\n",addr,PhysTrackBuffer)); | ||
900 | |||
901 | if (addr >= (unsigned int)PhysTrackBuffer + unit[SelectedDrive].disktype->spt*512) { | ||
902 | /* already read enough data, force an FDC interrupt to stop | ||
903 | * the read operation | ||
904 | */ | ||
905 | SET_IRQ_HANDLER( NULL ); | ||
906 | restore_flags(flags); | ||
907 | DPRINT(("fd_readtrack_check(): done\n")); | ||
908 | FDC1772_WRITE( FDC1772REG_CMD, FDC1772CMD_FORCI ); | ||
909 | udelay(25); | ||
910 | |||
911 | /* No error until now -- the FDC would have interrupted | ||
912 | * otherwise! | ||
913 | */ | ||
914 | fd_rwsec_done( 0 ); | ||
915 | } else { | ||
916 | /* not yet finished, wait another tenth rotation */ | ||
917 | restore_flags(flags); | ||
918 | DPRINT(("fd_readtrack_check(): not yet finished\n")); | ||
919 | readtrack_timer.expires = jiffies + HZ/5/10; | ||
920 | add_timer( &readtrack_timer ); | ||
921 | } | ||
922 | } | ||
923 | |||
924 | #endif | ||
925 | |||
926 | static void fd_rwsec_done(int status) | ||
927 | { | ||
928 | unsigned int track; | ||
929 | |||
930 | DPRINT(("fd_rwsec_done() status=%d @ %d\n", status,jiffies)); | ||
931 | |||
932 | #ifdef TRACKBUFFER | ||
933 | if (read_track && !MultReadInProgress) | ||
934 | return; | ||
935 | |||
936 | MultReadInProgress = 0; | ||
937 | |||
938 | STOP_TIMEOUT(); | ||
939 | |||
940 | if (read_track) | ||
941 | del_timer( &readtrack_timer ); | ||
942 | #endif | ||
943 | |||
944 | |||
945 | /* Correct the track if stretch != 0 */ | ||
946 | if (unit[SelectedDrive].disktype->stretch) { | ||
947 | track = FDC1772_READ(FDC1772REG_TRACK); | ||
948 | FDC1772_WRITE(FDC1772REG_TRACK, track << | ||
949 | unit[SelectedDrive].disktype->stretch); | ||
950 | } | ||
951 | if (ReqCmd == WRITE && (status & FDC1772STAT_WPROT)) { | ||
952 | printk("fd%d: is write protected\n", SelectedDrive); | ||
953 | goto err_end; | ||
954 | } | ||
955 | if ((status & FDC1772STAT_RECNF) | ||
956 | #ifdef TRACKBUFFER | ||
957 | /* RECNF is no error after a multiple read when the FDC | ||
958 | * searched for a non-existant sector! | ||
959 | */ | ||
960 | && !(read_track && | ||
961 | FDC1772_READ(FDC1772REG_SECTOR) > unit[SelectedDrive].disktype->spt) | ||
962 | #endif | ||
963 | ) { | ||
964 | if (Probing) { | ||
965 | if (unit[SelectedDrive].disktype > disk_type) { | ||
966 | /* try another disk type */ | ||
967 | unit[SelectedDrive].disktype--; | ||
968 | set_capacity(disks[SelectedDrive], | ||
969 | unit[SelectedDrive].disktype->blocks); | ||
970 | } else | ||
971 | Probing = 0; | ||
972 | } else { | ||
973 | /* record not found, but not probing. Maybe stretch wrong ? Restart probing */ | ||
974 | if (unit[SelectedDrive].autoprobe) { | ||
975 | unit[SelectedDrive].disktype = disk_type + NUM_DISK_TYPES - 1; | ||
976 | set_capacity(disks[SelectedDrive], | ||
977 | unit[SelectedDrive].disktype->blocks); | ||
978 | Probing = 1; | ||
979 | } | ||
980 | } | ||
981 | if (Probing) { | ||
982 | setup_req_params(SelectedDrive); | ||
983 | #ifdef TRACKBUFFER | ||
984 | BufferDrive = -1; | ||
985 | #endif | ||
986 | do_fd_action(SelectedDrive); | ||
987 | return; | ||
988 | } | ||
989 | printk("fd%d: sector %d not found (side %d, track %d)\n", | ||
990 | SelectedDrive, FDC1772_READ(FDC1772REG_SECTOR), ReqSide, ReqTrack); | ||
991 | goto err_end; | ||
992 | } | ||
993 | if (status & FDC1772STAT_CRC) { | ||
994 | printk("fd%d: CRC error (side %d, track %d, sector %d)\n", | ||
995 | SelectedDrive, ReqSide, ReqTrack, FDC1772_READ(FDC1772REG_SECTOR)); | ||
996 | goto err_end; | ||
997 | } | ||
998 | if (status & FDC1772STAT_LOST) { | ||
999 | printk("fd%d: lost data (side %d, track %d, sector %d)\n", | ||
1000 | SelectedDrive, ReqSide, ReqTrack, FDC1772_READ(FDC1772REG_SECTOR)); | ||
1001 | goto err_end; | ||
1002 | } | ||
1003 | Probing = 0; | ||
1004 | |||
1005 | if (ReqCmd == READ) { | ||
1006 | #ifdef TRACKBUFFER | ||
1007 | if (!read_track) { | ||
1008 | /*cache_clear (PhysDMABuffer, 512);*/ | ||
1009 | copy_buffer (DMABuffer, ReqData); | ||
1010 | } else { | ||
1011 | /*cache_clear (PhysTrackBuffer, FD1772_MAX_SECTORS * 512);*/ | ||
1012 | BufferDrive = SelectedDrive; | ||
1013 | BufferSide = ReqSide; | ||
1014 | BufferTrack = ReqTrack; | ||
1015 | copy_buffer (SECTOR_BUFFER (ReqSector), ReqData); | ||
1016 | } | ||
1017 | #else | ||
1018 | /*cache_clear( PhysDMABuffer, 512 ); */ | ||
1019 | copy_buffer(DMABuffer, ReqData); | ||
1020 | #endif | ||
1021 | } | ||
1022 | if (++ReqCnt < CURRENT->current_nr_sectors) { | ||
1023 | /* read next sector */ | ||
1024 | setup_req_params(SelectedDrive); | ||
1025 | do_fd_action(SelectedDrive); | ||
1026 | } else { | ||
1027 | /* all sectors finished */ | ||
1028 | CURRENT->nr_sectors -= CURRENT->current_nr_sectors; | ||
1029 | CURRENT->sector += CURRENT->current_nr_sectors; | ||
1030 | end_request(CURRENT, 1); | ||
1031 | redo_fd_request(); | ||
1032 | } | ||
1033 | return; | ||
1034 | |||
1035 | err_end: | ||
1036 | #ifdef TRACKBUFFER | ||
1037 | BufferDrive = -1; | ||
1038 | #endif | ||
1039 | |||
1040 | fd_error(); | ||
1041 | } | ||
1042 | |||
1043 | |||
1044 | static void fd_times_out(unsigned long dummy) | ||
1045 | { | ||
1046 | SET_IRQ_HANDLER(NULL); | ||
1047 | /* If the timeout occurred while the readtrack_check timer was | ||
1048 | * active, we need to cancel it, else bad things will happen */ | ||
1049 | del_timer( &readtrack_timer ); | ||
1050 | FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI); | ||
1051 | udelay(25); | ||
1052 | |||
1053 | printk("floppy timeout\n"); | ||
1054 | STOP_TIMEOUT(); /* hmm - should we do this ? */ | ||
1055 | fd_error(); | ||
1056 | } | ||
1057 | |||
1058 | |||
1059 | /* The (noop) seek operation here is needed to make the WP bit in the | ||
1060 | * FDC1772 status register accessible for check_change. If the last disk | ||
1061 | * operation would have been a RDSEC, this bit would always read as 0 | ||
1062 | * no matter what :-( To save time, the seek goes to the track we're | ||
1063 | * already on. | ||
1064 | */ | ||
1065 | |||
1066 | static void finish_fdc(void) | ||
1067 | { | ||
1068 | /* DAG - just try without this dummy seek! */ | ||
1069 | finish_fdc_done(0); | ||
1070 | return; | ||
1071 | |||
1072 | if (!NeedSeek) { | ||
1073 | finish_fdc_done(0); | ||
1074 | } else { | ||
1075 | DPRINT(("finish_fdc: dummy seek started\n")); | ||
1076 | FDC1772_WRITE(FDC1772REG_DATA, unit[SelectedDrive].track); | ||
1077 | SET_IRQ_HANDLER(finish_fdc_done); | ||
1078 | FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK); | ||
1079 | MotorOn = 1; | ||
1080 | START_TIMEOUT(); | ||
1081 | /* we must wait for the IRQ here, because the ST-DMA is | ||
1082 | * released immediately afterwards and the interrupt may be | ||
1083 | * delivered to the wrong driver. | ||
1084 | */ | ||
1085 | } | ||
1086 | } | ||
1087 | |||
1088 | |||
1089 | static void finish_fdc_done(int dummy) | ||
1090 | { | ||
1091 | unsigned long flags; | ||
1092 | |||
1093 | DPRINT(("finish_fdc_done entered\n")); | ||
1094 | STOP_TIMEOUT(); | ||
1095 | NeedSeek = 0; | ||
1096 | |||
1097 | if (timer_pending(&fd_timer) && | ||
1098 | time_after(jiffies + 5, fd_timer.expires)) | ||
1099 | /* If the check for a disk change is done too early after this | ||
1100 | * last seek command, the WP bit still reads wrong :-(( | ||
1101 | */ | ||
1102 | mod_timer(&fd_timer, jiffies + 5); | ||
1103 | else { | ||
1104 | /* START_CHECK_CHANGE_TIMER( CHECK_CHANGE_DELAY ); */ | ||
1105 | }; | ||
1106 | del_timer(&motor_off_timer); | ||
1107 | START_MOTOR_OFF_TIMER(FD_MOTOR_OFF_DELAY); | ||
1108 | |||
1109 | save_flags(flags); | ||
1110 | cli(); | ||
1111 | /* stdma_release(); - not sure if I should do something DAG */ | ||
1112 | fdc_busy = 0; | ||
1113 | wake_up(&fdc_wait); | ||
1114 | restore_flags(flags); | ||
1115 | |||
1116 | DPRINT(("finish_fdc() finished\n")); | ||
1117 | } | ||
1118 | |||
1119 | |||
1120 | /* Prevent "aliased" accesses. */ | ||
1121 | static int fd_ref[4]; | ||
1122 | static int fd_device[4]; | ||
1123 | |||
1124 | /* dummy for blk.h */ | ||
1125 | static void floppy_off(unsigned int nr) | ||
1126 | { | ||
1127 | } | ||
1128 | |||
1129 | |||
1130 | /* On the old arcs write protect depends on the particular model | ||
1131 | of machine. On the A310, R140, and A440 there is a disc changed | ||
1132 | detect, however on the A4x0/1 range there is not. There | ||
1133 | is nothing to tell you which machine your on. | ||
1134 | At the moment I'm just marking changed always. I've | ||
1135 | left the Atari's 'change on write protect change' code in this | ||
1136 | part (but nothing sets it). | ||
1137 | RiscOS apparently checks the disc serial number etc. to detect changes | ||
1138 | - but if it sees a disc change line go high (?) it flips to using | ||
1139 | it. Well maybe I'll add that in the future (!?) | ||
1140 | */ | ||
1141 | static int check_floppy_change(struct gendisk *disk) | ||
1142 | { | ||
1143 | struct archy_floppy_struct *p = disk->private_data; | ||
1144 | unsigned int drive = p - unit; | ||
1145 | |||
1146 | if (test_bit(drive, &fake_change)) { | ||
1147 | /* simulated change (e.g. after formatting) */ | ||
1148 | return 1; | ||
1149 | } | ||
1150 | if (test_bit(drive, &changed_floppies)) { | ||
1151 | /* surely changed (the WP signal changed at least once) */ | ||
1152 | return 1; | ||
1153 | } | ||
1154 | if (p->wpstat) { | ||
1155 | /* WP is on -> could be changed: to be sure, buffers should be | ||
1156 | * invalidated... | ||
1157 | */ | ||
1158 | return 1; | ||
1159 | } | ||
1160 | return 1; /* DAG - was 0 */ | ||
1161 | } | ||
1162 | |||
1163 | static int floppy_revalidate(struct gendisk *disk) | ||
1164 | { | ||
1165 | struct archy_floppy_struct *p = disk->private_data; | ||
1166 | unsigned int drive = p - unit; | ||
1167 | |||
1168 | if (test_bit(drive, &changed_floppies) || test_bit(drive, &fake_change) | ||
1169 | || unit[drive].disktype == 0) { | ||
1170 | #ifdef TRACKBUFFER | ||
1171 | BufferDrive = -1; | ||
1172 | #endif | ||
1173 | clear_bit(drive, &fake_change); | ||
1174 | clear_bit(drive, &changed_floppies); | ||
1175 | p->disktype = 0; | ||
1176 | } | ||
1177 | return 0; | ||
1178 | } | ||
1179 | |||
1180 | /* This sets up the global variables describing the current request. */ | ||
1181 | |||
1182 | static void setup_req_params(int drive) | ||
1183 | { | ||
1184 | int block = ReqBlock + ReqCnt; | ||
1185 | |||
1186 | ReqTrack = block / unit[drive].disktype->spt; | ||
1187 | ReqSector = block - ReqTrack * unit[drive].disktype->spt + 1; | ||
1188 | ReqSide = ReqTrack & 1; | ||
1189 | ReqTrack >>= 1; | ||
1190 | ReqData = ReqBuffer + 512 * ReqCnt; | ||
1191 | |||
1192 | #ifdef TRACKBUFFER | ||
1193 | read_track = (ReqCmd == READ && CURRENT->errors == 0); | ||
1194 | #endif | ||
1195 | |||
1196 | DPRINT(("Request params: Si=%d Tr=%d Se=%d Data=%08lx\n", ReqSide, | ||
1197 | ReqTrack, ReqSector, (unsigned long) ReqData)); | ||
1198 | } | ||
1199 | |||
1200 | |||
1201 | static void redo_fd_request(void) | ||
1202 | { | ||
1203 | int drive, type; | ||
1204 | struct archy_floppy_struct *floppy; | ||
1205 | |||
1206 | DPRINT(("redo_fd_request: CURRENT=%p dev=%s CURRENT->sector=%ld\n", | ||
1207 | CURRENT, CURRENT ? CURRENT->rq_disk->disk_name : "", | ||
1208 | CURRENT ? CURRENT->sector : 0)); | ||
1209 | |||
1210 | repeat: | ||
1211 | |||
1212 | if (!CURRENT) | ||
1213 | goto the_end; | ||
1214 | |||
1215 | floppy = CURRENT->rq_disk->private_data; | ||
1216 | drive = floppy - unit; | ||
1217 | type = fd_device[drive]; | ||
1218 | |||
1219 | if (!floppy->connected) { | ||
1220 | /* drive not connected */ | ||
1221 | printk("Unknown Device: fd%d\n", drive); | ||
1222 | end_request(CURRENT, 0); | ||
1223 | goto repeat; | ||
1224 | } | ||
1225 | if (type == 0) { | ||
1226 | if (!floppy->disktype) { | ||
1227 | Probing = 1; | ||
1228 | floppy->disktype = disk_type + NUM_DISK_TYPES - 1; | ||
1229 | set_capacity(disks[drive], floppy->disktype->blocks); | ||
1230 | floppy->autoprobe = 1; | ||
1231 | } | ||
1232 | } else { | ||
1233 | /* user supplied disk type */ | ||
1234 | --type; | ||
1235 | if (type >= NUM_DISK_TYPES) { | ||
1236 | printk("fd%d: invalid disk format", drive); | ||
1237 | end_request(CURRENT, 0); | ||
1238 | goto repeat; | ||
1239 | } | ||
1240 | floppy->disktype = &disk_type[type]; | ||
1241 | set_capacity(disks[drive], floppy->disktype->blocks); | ||
1242 | floppy->autoprobe = 0; | ||
1243 | } | ||
1244 | |||
1245 | if (CURRENT->sector + 1 > floppy->disktype->blocks) { | ||
1246 | end_request(CURRENT, 0); | ||
1247 | goto repeat; | ||
1248 | } | ||
1249 | /* stop deselect timer */ | ||
1250 | del_timer(&motor_off_timer); | ||
1251 | |||
1252 | ReqCnt = 0; | ||
1253 | ReqCmd = CURRENT->cmd; | ||
1254 | ReqBlock = CURRENT->sector; | ||
1255 | ReqBuffer = CURRENT->buffer; | ||
1256 | setup_req_params(drive); | ||
1257 | do_fd_action(drive); | ||
1258 | |||
1259 | return; | ||
1260 | |||
1261 | the_end: | ||
1262 | finish_fdc(); | ||
1263 | } | ||
1264 | |||
1265 | static void fd1772_checkint(void) | ||
1266 | { | ||
1267 | extern int fdc1772_bytestogo; | ||
1268 | |||
1269 | /*printk("fd1772_checkint %d\n",fdc1772_fdc_int_done);*/ | ||
1270 | if (fdc1772_fdc_int_done) | ||
1271 | floppy_irqconsequencehandler(); | ||
1272 | if ((MultReadInProgress) && (fdc1772_bytestogo==0)) fd_readtrack_check(0); | ||
1273 | if (fdc_busy) { | ||
1274 | schedule_work(&fd1772_tq); | ||
1275 | } | ||
1276 | } | ||
1277 | |||
1278 | static void do_fd_request(request_queue_t* q) | ||
1279 | { | ||
1280 | unsigned long flags; | ||
1281 | |||
1282 | DPRINT(("do_fd_request for pid %d\n", current->pid)); | ||
1283 | if (fdc_busy) return; | ||
1284 | save_flags(flags); | ||
1285 | cli(); | ||
1286 | while (fdc_busy) | ||
1287 | sleep_on(&fdc_wait); | ||
1288 | fdc_busy = 1; | ||
1289 | ENABLE_IRQ(); | ||
1290 | restore_flags(flags); | ||
1291 | |||
1292 | fdc1772_fdc_int_done = 0; | ||
1293 | |||
1294 | redo_fd_request(); | ||
1295 | |||
1296 | schedule_work(&fd1772_tq); | ||
1297 | } | ||
1298 | |||
1299 | |||
1300 | static int invalidate_drive(struct block_device *bdev) | ||
1301 | { | ||
1302 | struct archy_floppy_struct *p = bdev->bd_disk->private_data; | ||
1303 | /* invalidate the buffer track to force a reread */ | ||
1304 | #ifdef TRACKBUFFER | ||
1305 | BufferDrive = -1; | ||
1306 | #endif | ||
1307 | |||
1308 | set_bit(p - unit, &fake_change); | ||
1309 | return 0; | ||
1310 | } | ||
1311 | |||
1312 | static int fd_ioctl(struct inode *inode, struct file *filp, | ||
1313 | unsigned int cmd, unsigned long param) | ||
1314 | { | ||
1315 | struct block_device *bdev = inode->i_bdev; | ||
1316 | |||
1317 | switch (cmd) { | ||
1318 | case FDFMTEND: | ||
1319 | case FDFLUSH: | ||
1320 | invalidate_drive(bdev); | ||
1321 | check_disk_change(bdev); | ||
1322 | case FDFMTBEG: | ||
1323 | return 0; | ||
1324 | default: | ||
1325 | return -EINVAL; | ||
1326 | } | ||
1327 | } | ||
1328 | |||
1329 | |||
1330 | /* Initialize the 'unit' variable for drive 'drive' */ | ||
1331 | |||
1332 | static void fd_probe(int drive) | ||
1333 | { | ||
1334 | unit[drive].connected = 0; | ||
1335 | unit[drive].disktype = NULL; | ||
1336 | |||
1337 | if (!fd_test_drive_present(drive)) | ||
1338 | return; | ||
1339 | |||
1340 | unit[drive].connected = 1; | ||
1341 | unit[drive].track = -1; /* If we put the auto detect back in this can go to 0 */ | ||
1342 | unit[drive].steprate = FDC1772STEP_6; | ||
1343 | MotorOn = 1; /* from probe restore operation! */ | ||
1344 | } | ||
1345 | |||
1346 | |||
1347 | /* This function tests the physical presence of a floppy drive (not | ||
1348 | * whether a disk is inserted). This is done by issuing a restore | ||
1349 | * command, waiting max. 2 seconds (that should be enough to move the | ||
1350 | * head across the whole disk) and looking at the state of the "TR00" | ||
1351 | * signal. This should now be raised if there is a drive connected | ||
1352 | * (and there is no hardware failure :-) Otherwise, the drive is | ||
1353 | * declared absent. | ||
1354 | */ | ||
1355 | |||
1356 | static int fd_test_drive_present(int drive) | ||
1357 | { | ||
1358 | unsigned long timeout; | ||
1359 | unsigned char status; | ||
1360 | int ok; | ||
1361 | |||
1362 | printk("fd_test_drive_present %d\n", drive); | ||
1363 | if (drive > 1) | ||
1364 | return (0); | ||
1365 | return (1); /* Simple hack for the moment - the autodetect doesn't seem to work on arc */ | ||
1366 | fd_select_drive(drive); | ||
1367 | |||
1368 | /* disable interrupt temporarily */ | ||
1369 | DISABLE_IRQ(); | ||
1370 | FDC1772_WRITE(FDC1772REG_TRACK, 0x00); /* was ff00 why? */ | ||
1371 | FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_RESTORE | FDC1772CMDADD_H | FDC1772STEP_6); | ||
1372 | |||
1373 | /*printk("fd_test_drive_present: Going into timeout loop\n"); */ | ||
1374 | for (ok = 0, timeout = jiffies + 2 * HZ + HZ / 2; time_before(jiffies, timeout);) { | ||
1375 | /* What does this piece of atariism do? - query for an interrupt? */ | ||
1376 | /* if (!(mfp.par_dt_reg & 0x20)) | ||
1377 | break; */ | ||
1378 | /* Well this is my nearest guess - quit when we get an FDC interrupt */ | ||
1379 | if (ioc_readb(IOC_FIQSTAT) & 2) | ||
1380 | break; | ||
1381 | } | ||
1382 | |||
1383 | /*printk("fd_test_drive_present: Coming out of timeout loop\n"); */ | ||
1384 | status = FDC1772_READ(FDC1772REG_STATUS); | ||
1385 | ok = (status & FDC1772STAT_TR00) != 0; | ||
1386 | |||
1387 | /*printk("fd_test_drive_present: ok=%d\n",ok); */ | ||
1388 | /* force interrupt to abort restore operation (FDC1772 would try | ||
1389 | * about 50 seconds!) */ | ||
1390 | FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI); | ||
1391 | udelay(500); | ||
1392 | status = FDC1772_READ(FDC1772REG_STATUS); | ||
1393 | udelay(20); | ||
1394 | /*printk("fd_test_drive_present: just before OK code %d\n",ok); */ | ||
1395 | |||
1396 | if (ok) { | ||
1397 | /* dummy seek command to make WP bit accessible */ | ||
1398 | FDC1772_WRITE(FDC1772REG_DATA, 0); | ||
1399 | FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK); | ||
1400 | printk("fd_test_drive_present: just before wait for int\n"); | ||
1401 | /* DAG: Guess means wait for interrupt */ | ||
1402 | while (!(ioc_readb(IOC_FIQSTAT) & 2)); | ||
1403 | printk("fd_test_drive_present: just after wait for int\n"); | ||
1404 | status = FDC1772_READ(FDC1772REG_STATUS); | ||
1405 | } | ||
1406 | printk("fd_test_drive_present: just before ENABLE_IRQ\n"); | ||
1407 | ENABLE_IRQ(); | ||
1408 | printk("fd_test_drive_present: about to return\n"); | ||
1409 | return (ok); | ||
1410 | } | ||
1411 | |||
1412 | |||
1413 | /* Look how many and which kind of drives are connected. If there are | ||
1414 | * floppies, additionally start the disk-change and motor-off timers. | ||
1415 | */ | ||
1416 | |||
1417 | static void config_types(void) | ||
1418 | { | ||
1419 | int drive, cnt = 0; | ||
1420 | |||
1421 | printk("Probing floppy drive(s):\n"); | ||
1422 | for (drive = 0; drive < FD_MAX_UNITS; drive++) { | ||
1423 | fd_probe(drive); | ||
1424 | if (unit[drive].connected) { | ||
1425 | printk("fd%d\n", drive); | ||
1426 | ++cnt; | ||
1427 | } | ||
1428 | } | ||
1429 | |||
1430 | if (FDC1772_READ(FDC1772REG_STATUS) & FDC1772STAT_BUSY) { | ||
1431 | /* If FDC1772 is still busy from probing, give it another FORCI | ||
1432 | * command to abort the operation. If this isn't done, the FDC1772 | ||
1433 | * will interrupt later and its IRQ line stays low, because | ||
1434 | * the status register isn't read. And this will block any | ||
1435 | * interrupts on this IRQ line :-( | ||
1436 | */ | ||
1437 | FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI); | ||
1438 | udelay(500); | ||
1439 | FDC1772_READ(FDC1772REG_STATUS); | ||
1440 | udelay(20); | ||
1441 | } | ||
1442 | if (cnt > 0) { | ||
1443 | START_MOTOR_OFF_TIMER(FD_MOTOR_OFF_DELAY); | ||
1444 | if (cnt == 1) | ||
1445 | fd_select_drive(0); | ||
1446 | /*START_CHECK_CHANGE_TIMER( CHECK_CHANGE_DELAY ); */ | ||
1447 | } | ||
1448 | } | ||
1449 | |||
1450 | /* | ||
1451 | * floppy_open check for aliasing (/dev/fd0 can be the same as | ||
1452 | * /dev/PS0 etc), and disallows simultaneous access to the same | ||
1453 | * drive with different device numbers. | ||
1454 | */ | ||
1455 | |||
1456 | static int floppy_open(struct inode *inode, struct file *filp) | ||
1457 | { | ||
1458 | int drive = iminor(inode) & 3; | ||
1459 | int type = iminor(inode) >> 2; | ||
1460 | int old_dev = fd_device[drive]; | ||
1461 | |||
1462 | if (fd_ref[drive] && old_dev != type) | ||
1463 | return -EBUSY; | ||
1464 | |||
1465 | if (fd_ref[drive] == -1 || (fd_ref[drive] && filp->f_flags & O_EXCL)) | ||
1466 | return -EBUSY; | ||
1467 | |||
1468 | if (filp->f_flags & O_EXCL) | ||
1469 | fd_ref[drive] = -1; | ||
1470 | else | ||
1471 | fd_ref[drive]++; | ||
1472 | |||
1473 | fd_device[drive] = type; | ||
1474 | |||
1475 | if (filp->f_flags & O_NDELAY) | ||
1476 | return 0; | ||
1477 | |||
1478 | if (filp->f_mode & 3) { | ||
1479 | check_disk_change(inode->i_bdev); | ||
1480 | if (filp->f_mode & 2) { | ||
1481 | if (unit[drive].wpstat) { | ||
1482 | floppy_release(inode, filp); | ||
1483 | return -EROFS; | ||
1484 | } | ||
1485 | } | ||
1486 | } | ||
1487 | return 0; | ||
1488 | } | ||
1489 | |||
1490 | |||
1491 | static int floppy_release(struct inode *inode, struct file *filp) | ||
1492 | { | ||
1493 | int drive = iminor(inode) & 3; | ||
1494 | |||
1495 | if (fd_ref[drive] < 0) | ||
1496 | fd_ref[drive] = 0; | ||
1497 | else if (!fd_ref[drive]--) { | ||
1498 | printk("floppy_release with fd_ref == 0"); | ||
1499 | fd_ref[drive] = 0; | ||
1500 | } | ||
1501 | |||
1502 | return 0; | ||
1503 | } | ||
1504 | |||
1505 | static struct block_device_operations floppy_fops = | ||
1506 | { | ||
1507 | .open = floppy_open, | ||
1508 | .release = floppy_release, | ||
1509 | .ioctl = fd_ioctl, | ||
1510 | .media_changed = check_floppy_change, | ||
1511 | .revalidate_disk= floppy_revalidate, | ||
1512 | }; | ||
1513 | |||
1514 | static struct kobject *floppy_find(dev_t dev, int *part, void *data) | ||
1515 | { | ||
1516 | int drive = *part & 3; | ||
1517 | if ((*part >> 2) > NUM_DISK_TYPES || drive >= FD_MAX_UNITS) | ||
1518 | return NULL; | ||
1519 | *part = 0; | ||
1520 | return get_disk(disks[drive]); | ||
1521 | } | ||
1522 | |||
1523 | int fd1772_init(void) | ||
1524 | { | ||
1525 | static DEFINE_SPINLOCK(lock); | ||
1526 | int i, err = -ENOMEM; | ||
1527 | |||
1528 | if (!machine_is_archimedes()) | ||
1529 | return 0; | ||
1530 | |||
1531 | for (i = 0; i < FD_MAX_UNITS; i++) { | ||
1532 | disks[i] = alloc_disk(1); | ||
1533 | if (!disks[i]) | ||
1534 | goto err_disk; | ||
1535 | } | ||
1536 | |||
1537 | err = register_blkdev(MAJOR_NR, "fd"); | ||
1538 | if (err) | ||
1539 | goto err_disk; | ||
1540 | |||
1541 | err = -EBUSY; | ||
1542 | if (request_dma(FLOPPY_DMA, "fd1772")) { | ||
1543 | printk("Unable to grab DMA%d for the floppy (1772) driver\n", FLOPPY_DMA); | ||
1544 | goto err_blkdev; | ||
1545 | }; | ||
1546 | |||
1547 | if (request_dma(FIQ_FD1772, "fd1772 end")) { | ||
1548 | printk("Unable to grab DMA%d for the floppy (1772) driver\n", FIQ_FD1772); | ||
1549 | goto err_dma1; | ||
1550 | }; | ||
1551 | |||
1552 | /* initialize variables */ | ||
1553 | SelectedDrive = -1; | ||
1554 | #ifdef TRACKBUFFER | ||
1555 | BufferDrive = BufferSide = BufferTrack = -1; | ||
1556 | /* Atari uses 512 - I want to eventually cope with 1K sectors */ | ||
1557 | DMABuffer = (char *)kmalloc((FD1772_MAX_SECTORS+1)*512,GFP_KERNEL); | ||
1558 | TrackBuffer = DMABuffer + 512; | ||
1559 | #else | ||
1560 | /* Allocate memory for the DMAbuffer - on the Atari this takes it | ||
1561 | out of some special memory... */ | ||
1562 | DMABuffer = (char *) kmalloc(2048); /* Copes with pretty large sectors */ | ||
1563 | #endif | ||
1564 | err = -ENOMEM; | ||
1565 | if (!DMAbuffer) | ||
1566 | goto err_dma2; | ||
1567 | |||
1568 | enable_dma(FIQ_FD1772); /* This inserts a call to our command end routine */ | ||
1569 | |||
1570 | floppy_queue = blk_init_queue(do_fd_request, &lock); | ||
1571 | if (!floppy_queue) | ||
1572 | goto err_queue; | ||
1573 | |||
1574 | for (i = 0; i < FD_MAX_UNITS; i++) { | ||
1575 | unit[i].track = -1; | ||
1576 | disks[i]->major = MAJOR_NR; | ||
1577 | disks[i]->first_minor = 0; | ||
1578 | disks[i]->fops = &floppy_fops; | ||
1579 | sprintf(disks[i]->disk_name, "fd%d", i); | ||
1580 | disks[i]->private_data = &unit[i]; | ||
1581 | disks[i]->queue = floppy_queue; | ||
1582 | set_capacity(disks[i], MAX_DISK_SIZE * 2); | ||
1583 | } | ||
1584 | blk_register_region(MKDEV(MAJOR_NR, 0), 256, THIS_MODULE, | ||
1585 | floppy_find, NULL, NULL); | ||
1586 | |||
1587 | for (i = 0; i < FD_MAX_UNITS; i++) | ||
1588 | add_disk(disks[i]); | ||
1589 | |||
1590 | config_types(); | ||
1591 | |||
1592 | return 0; | ||
1593 | |||
1594 | err_queue: | ||
1595 | kfree(DMAbuffer); | ||
1596 | err_dma2: | ||
1597 | free_dma(FIQ_FD1772); | ||
1598 | |||
1599 | err_dma1: | ||
1600 | free_dma(FLOPPY_DMA); | ||
1601 | |||
1602 | err_blkdev: | ||
1603 | unregister_blkdev(MAJOR_NR, "fd"); | ||
1604 | |||
1605 | err_disk: | ||
1606 | while (i--) | ||
1607 | put_disk(disks[i]); | ||
1608 | return err; | ||
1609 | } | ||
diff --git a/drivers/acorn/block/fd1772dma.S b/drivers/acorn/block/fd1772dma.S new file mode 100644 index 00000000000..7964435443e --- /dev/null +++ b/drivers/acorn/block/fd1772dma.S | |||
@@ -0,0 +1,100 @@ | |||
1 | #include <asm/hardware.h> | ||
2 | |||
3 | @ Code for DMA with the 1772 fdc | ||
4 | .text | ||
5 | |||
6 | |||
7 | .global fdc1772_dataaddr | ||
8 | fdc1772_fiqdata: | ||
9 | @ Number of bytes left to DMA | ||
10 | .global fdc1772_bytestogo | ||
11 | fdc1772_bytestogo: | ||
12 | .word 0 | ||
13 | @ Place to put/get data from in DMA | ||
14 | .global fdc1772_dataaddr | ||
15 | fdc1772_dataaddr: | ||
16 | .word 0 | ||
17 | |||
18 | .global fdc1772_fdc_int_done | ||
19 | fdc1772_fdc_int_done: | ||
20 | .word 0 | ||
21 | .global fdc1772_comendstatus | ||
22 | fdc1772_comendstatus: | ||
23 | .word 0 | ||
24 | |||
25 | @ We hang this off DMA channel 1 | ||
26 | .global fdc1772_comendhandler | ||
27 | fdc1772_comendhandler: | ||
28 | mov r8,#IOC_BASE | ||
29 | ldrb r9,[r8,#0x34] @ IOC FIQ status | ||
30 | tst r9,#2 | ||
31 | subeqs pc,r14,#4 @ should I leave a space here | ||
32 | orr r9,r8,#0x10000 @ FDC base | ||
33 | adr r8,fdc1772_fdc_int_done | ||
34 | ldrb r10,[r9,#0] @ FDC status | ||
35 | mov r9,#1 @ Got a FIQ flag | ||
36 | stmia r8,{r9,r10} | ||
37 | subs pc,r14,#4 | ||
38 | |||
39 | |||
40 | .global fdc1772_dma_read | ||
41 | fdc1772_dma_read: | ||
42 | mov r8,#IOC_BASE | ||
43 | ldrb r9,[r8,#0x34] @ IOC FIQ status | ||
44 | tst r9,#1 | ||
45 | beq fdc1772_dma_read_notours | ||
46 | orr r8,r8,#0x10000 @ FDC base | ||
47 | ldrb r10,[r8,#0xc] @ Read from FDC data reg (also clears interrupt) | ||
48 | ldmia r11,{r8,r9} | ||
49 | subs r8,r8,#1 @ One less byte to go | ||
50 | @ If there was somewhere for this data to go then store it and update pointers | ||
51 | strplb r10,[r9],#1 @ Store the data and increment the pointer | ||
52 | stmplia r11,{r8,r9} @ Update count/pointers | ||
53 | @ Handle any other interrupts if there are any | ||
54 | fdc1772_dma_read_notours: | ||
55 | @ Cant branch because this code has been copied down to the FIQ vector | ||
56 | ldr pc,[pc,#-4] | ||
57 | .word fdc1772_comendhandler | ||
58 | .global fdc1772_dma_read_end | ||
59 | fdc1772_dma_read_end: | ||
60 | |||
61 | .global fdc1772_dma_write | ||
62 | fdc1772_dma_write: | ||
63 | mov r8,#IOC_BASE | ||
64 | ldrb r9,[r8,#0x34] @ IOC FIQ status | ||
65 | tst r9,#1 | ||
66 | beq fdc1772_dma_write_notours | ||
67 | orr r8,r8,#0x10000 @ FDC base | ||
68 | ldmia r11,{r9,r10} | ||
69 | subs r9,r9,#1 @ One less byte to go | ||
70 | @ If there really is some data then get it, store it and update count | ||
71 | ldrplb r12,[r10],#1 | ||
72 | strplb r12,[r8,#0xc] @ write it to FDC data reg | ||
73 | stmplia r11,{r9,r10} @ Update count and pointer - should clear interrupt | ||
74 | @ Handle any other interrupts | ||
75 | fdc1772_dma_write_notours: | ||
76 | @ Cant branch because this code has been copied down to the FIQ vector | ||
77 | ldr pc,[pc,#-4] | ||
78 | .word fdc1772_comendhandler | ||
79 | |||
80 | .global fdc1772_dma_write_end | ||
81 | fdc1772_dma_write_end: | ||
82 | |||
83 | |||
84 | @ Setup the FIQ R11 to point to the data and store the count, address | ||
85 | @ for this dma | ||
86 | @ R0=count | ||
87 | @ R1=address | ||
88 | .global fdc1772_setupdma | ||
89 | fdc1772_setupdma: | ||
90 | @ The big job is flipping in and out of FIQ mode | ||
91 | adr r2,fdc1772_fiqdata @ This is what we really came here for | ||
92 | stmia r2,{r0,r1} | ||
93 | mov r3, pc | ||
94 | teqp pc,#0x0c000001 @ Disable FIQs, IRQs and switch to FIQ mode | ||
95 | mov r0,r0 @ NOP | ||
96 | mov r11,r2 | ||
97 | teqp r3,#0 @ Normal mode | ||
98 | mov r0,r0 @ NOP | ||
99 | mov pc,r14 | ||
100 | |||
diff --git a/drivers/acorn/block/mfm.S b/drivers/acorn/block/mfm.S new file mode 100644 index 00000000000..c90cbd41ce2 --- /dev/null +++ b/drivers/acorn/block/mfm.S | |||
@@ -0,0 +1,162 @@ | |||
1 | @ Read/Write DMA code for the ST506/MFM hard drive controllers on the A400 Acorn Archimedes | ||
2 | @ motherboard and on ST506 expansion podules. | ||
3 | @ (c) David Alan Gilbert (linux@treblig.org) 1996-1999 | ||
4 | |||
5 | #include <asm/assembler.h> | ||
6 | |||
7 | hdc63463_irqdata: | ||
8 | @ Controller base address | ||
9 | .global hdc63463_baseaddress | ||
10 | hdc63463_baseaddress: | ||
11 | .word 0 | ||
12 | |||
13 | .global hdc63463_irqpolladdress | ||
14 | hdc63463_irqpolladdress: | ||
15 | .word 0 | ||
16 | |||
17 | .global hdc63463_irqpollmask | ||
18 | hdc63463_irqpollmask: | ||
19 | .word 0 | ||
20 | |||
21 | @ where to read/write data from the kernel data space | ||
22 | .global hdc63463_dataptr | ||
23 | hdc63463_dataptr: | ||
24 | .word 0 | ||
25 | |||
26 | @ Number of bytes left to transfer | ||
27 | .global hdc63463_dataleft | ||
28 | hdc63463_dataleft: | ||
29 | .word 0 | ||
30 | |||
31 | @ ------------------------------------------------------------------------- | ||
32 | @ hdc63463_writedma: DMA from host to controller | ||
33 | @ internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask | ||
34 | @ r3=data ptr, r4=data left, r5,r6=temporary | ||
35 | .global hdc63463_writedma | ||
36 | hdc63463_writedma: | ||
37 | stmfd sp!,{r4-r7} | ||
38 | adr r5,hdc63463_irqdata | ||
39 | ldmia r5,{r0,r1,r2,r3,r4} | ||
40 | |||
41 | writedma_again: | ||
42 | |||
43 | @ test number of remaining bytes to transfer | ||
44 | cmp r4,#0 | ||
45 | beq writedma_end | ||
46 | bmi writedma_end | ||
47 | |||
48 | @ Check the hdc is interrupting | ||
49 | ldrb r5,[r1,#0] | ||
50 | tst r5,r2 | ||
51 | beq writedma_end | ||
52 | |||
53 | @ Transfer a block of upto 256 bytes | ||
54 | cmp r4,#256 | ||
55 | movlt r7,r4 | ||
56 | movge r7,#256 | ||
57 | |||
58 | @ Check the hdc is still busy and command has not ended and no errors | ||
59 | ldr r5,[r0,#32] @ Status reg - 16 bit - its the top few bits which are status | ||
60 | @ think we should continue DMA until it drops busy - perhaps this was | ||
61 | @ the main problem with corrected errors causing a hang | ||
62 | @tst r5,#0x3c00 @ Test for things which should be off | ||
63 | @bne writedma_end | ||
64 | and r5,r5,#0x8000 @ This is test for things which should be on: Busy | ||
65 | cmp r5,#0x8000 | ||
66 | bne writedma_end | ||
67 | |||
68 | @ Bytes remaining at end | ||
69 | sub r4,r4,r7 | ||
70 | |||
71 | @ HDC Write register location | ||
72 | add r0,r0,#32+8 | ||
73 | |||
74 | writedma_loop: | ||
75 | @ OK - pretty sure we should be doing this | ||
76 | |||
77 | ldr r5,[r3],#4 @ Get a word to be written | ||
78 | @ get bottom half to be sent first | ||
79 | mov r6,r5,lsl#16 @ Separate the first 2 bytes | ||
80 | orr r2,r6,r6,lsr #16 @ Duplicate them in the bottom half of the word | ||
81 | @ now the top half | ||
82 | mov r6,r5,lsr#16 @ Get 2nd 2 bytes | ||
83 | orr r6,r6,r6,lsl#16 @ Duplicate | ||
84 | @str r6,[r0] @ to hdc | ||
85 | stmia r0,{r2,r6} | ||
86 | subs r7,r7,#4 @ Dec. number of bytes left | ||
87 | bne writedma_loop | ||
88 | |||
89 | @ If we were too slow we had better go through again - DAG - took out with new interrupt routine | ||
90 | @ sub r0,r0,#32+8 | ||
91 | @ adr r2,hdc63463_irqdata | ||
92 | @ ldr r2,[r2,#8] | ||
93 | @ b writedma_again | ||
94 | |||
95 | writedma_end: | ||
96 | adr r5,hdc63463_irqdata+12 | ||
97 | stmia r5,{r3,r4} | ||
98 | ldmfd sp!,{r4-r7} | ||
99 | RETINSTR(mov,pc,lr) | ||
100 | |||
101 | @ ------------------------------------------------------------------------- | ||
102 | @ hdc63463_readdma: DMA from controller to host | ||
103 | @ internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask | ||
104 | @ r3=data ptr, r4=data left, r5,r6=temporary | ||
105 | .global hdc63463_readdma | ||
106 | hdc63463_readdma: | ||
107 | stmfd sp!,{r4-r7} | ||
108 | adr r5,hdc63463_irqdata | ||
109 | ldmia r5,{r0,r1,r2,r3,r4} | ||
110 | |||
111 | readdma_again: | ||
112 | @ test number of remaining bytes to transfer | ||
113 | cmp r4,#0 | ||
114 | beq readdma_end | ||
115 | bmi readdma_end | ||
116 | |||
117 | @ Check the hdc is interrupting | ||
118 | ldrb r5,[r1,#0] | ||
119 | tst r5,r2 | ||
120 | beq readdma_end | ||
121 | |||
122 | @ Check the hdc is still busy and command has not ended and no errors | ||
123 | ldr r5,[r0,#32] @ Status reg - 16 bit - its the top few bits which are status | ||
124 | @ think we should continue DMA until it drops busy - perhaps this was | ||
125 | @ the main problem with corrected errors causing a hang | ||
126 | @tst r5,#0x3c00 @ Test for things which should be off | ||
127 | @bne readdma_end | ||
128 | and r5,r5,#0x8000 @ This is test for things which should be on: Busy | ||
129 | cmp r5,#0x8000 | ||
130 | bne readdma_end | ||
131 | |||
132 | @ Transfer a block of upto 256 bytes | ||
133 | cmp r4,#256 | ||
134 | movlt r7,r4 | ||
135 | movge r7,#256 | ||
136 | |||
137 | @ Bytes remaining at end | ||
138 | sub r4,r4,r7 | ||
139 | |||
140 | @ Set a pointer to the data register in the HDC | ||
141 | add r0,r0,#8 | ||
142 | readdma_loop: | ||
143 | @ OK - pretty sure we should be doing this | ||
144 | ldmia r0,{r5,r6} | ||
145 | mov r5,r5,lsl#16 | ||
146 | mov r6,r6,lsl#16 | ||
147 | orr r6,r6,r5,lsr #16 | ||
148 | str r6,[r3],#4 | ||
149 | subs r7,r7,#4 @ Decrement bytes to go | ||
150 | bne readdma_loop | ||
151 | |||
152 | @ Try reading multiple blocks - if this was fast enough then I do not think | ||
153 | @ this should help - NO taken out DAG - new interrupt handler has | ||
154 | @ non-consecutive memory blocks | ||
155 | @ sub r0,r0,#8 | ||
156 | @ b readdma_again | ||
157 | |||
158 | readdma_end: | ||
159 | adr r5,hdc63463_irqdata+12 | ||
160 | stmia r5,{r3,r4} | ||
161 | ldmfd sp!,{r4-r7} | ||
162 | RETINSTR(mov,pc,lr) | ||
diff --git a/drivers/acorn/block/mfmhd.c b/drivers/acorn/block/mfmhd.c new file mode 100644 index 00000000000..4b65f74d66b --- /dev/null +++ b/drivers/acorn/block/mfmhd.c | |||
@@ -0,0 +1,1416 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/drivers/block/mfmhd.c | ||
3 | * | ||
4 | * Copyright (C) 1995, 1996 Russell King, Dave Alan Gilbert (gilbertd@cs.man.ac.uk) | ||
5 | * | ||
6 | * MFM hard drive code [experimental] | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * Change list: | ||
11 | * | ||
12 | * 3/2/96:DAG: Started a change list :-) | ||
13 | * Set the hardsect_size pointers up since we are running 256 byte | ||
14 | * sectors | ||
15 | * Added DMA code, put it into the rw_intr | ||
16 | * Moved RCAL out of generic interrupt code - don't want to do it | ||
17 | * while DMA'ing - its now in individual handlers. | ||
18 | * Took interrupt handlers off task queue lists and called | ||
19 | * directly - not sure of implications. | ||
20 | * | ||
21 | * 18/2/96:DAG: Well its reading OK I think, well enough for image file code | ||
22 | * to find the image file; but now I've discovered that I actually | ||
23 | * have to put some code in for image files. | ||
24 | * | ||
25 | * Added stuff for image files; seems to work, but I've not | ||
26 | * got a multisegment image file (I don't think!). | ||
27 | * Put in a hack (yep a real hack) for multiple cylinder reads. | ||
28 | * Not convinced its working. | ||
29 | * | ||
30 | * 5/4/96:DAG: Added asm/hardware.h and use IOC_ macros | ||
31 | * Rewrote dma code in mfm.S (again!) - now takes a word at a time | ||
32 | * from main RAM for speed; still doesn't feel speedy! | ||
33 | * | ||
34 | * 20/4/96:DAG: After rewriting mfm.S a heck of a lot of times and speeding | ||
35 | * things up, I've finally figured out why its so damn slow. | ||
36 | * Linux is only reading a block at a time, and so you never | ||
37 | * get more than 1K per disc revoloution ~=60K/second. | ||
38 | * | ||
39 | * 27/4/96:DAG: On Russell's advice I change ll_rw_blk.c to ask it to | ||
40 | * join adjacent blocks together. Everything falls flat on its | ||
41 | * face. | ||
42 | * Four hours of debugging later; I hadn't realised that | ||
43 | * ll_rw_blk would be so generous as to join blocks whose | ||
44 | * results aren't going into consecutive buffers. | ||
45 | * | ||
46 | * OK; severe rehacking of mfm_rw_interrupt; now end_request's | ||
47 | * as soon as its DMA'd each request. Odd thing is that | ||
48 | * we are sometimes getting interrupts where we are not transferring | ||
49 | * any data; why? Is that what happens when you miss? I doubt | ||
50 | * it; are we too fast? No - its just at command ends. Got 240K/s | ||
51 | * better than before, but RiscOS hits 480K/s | ||
52 | * | ||
53 | * 25/6/96:RMK: Fixed init code to allow the MFM podule to work. Increased the | ||
54 | * number of errors for my Miniscribe drive (8425). | ||
55 | * | ||
56 | * 30/6/96:DAG: Russell suggested that a check drive 0 might turn the LEDs off | ||
57 | * - so in request_done just before it clears Busy it sends a | ||
58 | * check drive 0 - and the LEDs go off!!!! | ||
59 | * | ||
60 | * Added test for mainboard controller. - Removes need for separate | ||
61 | * define. | ||
62 | * | ||
63 | * 13/7/96:DAG: Changed hardware sectore size to 512 in attempt to make | ||
64 | * IM drivers work. | ||
65 | * 21/7/96:DAG: Took out old image file stuff (accessing it now produces an IO | ||
66 | * error.) | ||
67 | * | ||
68 | * 17/8/96:DAG: Ran through indent -kr -i8; evil - all my nice 2 character indents | ||
69 | * gone :-( Hand modified afterwards. | ||
70 | * Took out last remains of the older image map system. | ||
71 | * | ||
72 | * 22/9/96:DAG: Changed mfm.S so it will carry on DMA'ing til; BSY is dropped | ||
73 | * Changed mfm_rw_intr so that it doesn't follow the error | ||
74 | * code until BSY is dropped. Nope - still broke. Problem | ||
75 | * may revolve around when it reads the results for the error | ||
76 | * number? | ||
77 | * | ||
78 | *16/11/96:DAG: Modified for 2.0.18; request_irq changed | ||
79 | * | ||
80 | *17/12/96:RMK: Various cleanups, reorganisation, and the changes for new IO system. | ||
81 | * Improved probe for onboard MFM chip - it was hanging on my A5k. | ||
82 | * Added autodetect CHS code such that we don't rely on the presence | ||
83 | * of an ADFS boot block. Added ioport resource manager calls so | ||
84 | * that we don't clash with already-running hardware (eg. RiscPC Ether | ||
85 | * card slots if someone tries this)! | ||
86 | * | ||
87 | * 17/1/97:RMK: Upgraded to 2.1 kernels. | ||
88 | * | ||
89 | * 4/3/98:RMK: Changed major number to 21. | ||
90 | * | ||
91 | * 27/6/98:RMK: Changed asm/delay.h to linux/delay.h for mdelay(). | ||
92 | */ | ||
93 | |||
94 | /* | ||
95 | * Possible enhancements: | ||
96 | * Multi-thread the code so that it is possible that while one drive | ||
97 | * is seeking, the other one can be reading data/seeking as well. | ||
98 | * This would be a performance boost with dual drive systems. | ||
99 | */ | ||
100 | |||
101 | #include <linux/module.h> | ||
102 | #include <linux/config.h> | ||
103 | #include <linux/sched.h> | ||
104 | #include <linux/fs.h> | ||
105 | #include <linux/interrupt.h> | ||
106 | #include <linux/kernel.h> | ||
107 | #include <linux/timer.h> | ||
108 | #include <linux/mm.h> | ||
109 | #include <linux/errno.h> | ||
110 | #include <linux/genhd.h> | ||
111 | #include <linux/major.h> | ||
112 | #include <linux/ioport.h> | ||
113 | #include <linux/delay.h> | ||
114 | #include <linux/blkpg.h> | ||
115 | |||
116 | #include <asm/system.h> | ||
117 | #include <asm/io.h> | ||
118 | #include <asm/irq.h> | ||
119 | #include <asm/uaccess.h> | ||
120 | #include <asm/dma.h> | ||
121 | #include <asm/hardware.h> | ||
122 | #include <asm/ecard.h> | ||
123 | #include <asm/hardware/ioc.h> | ||
124 | |||
125 | static void (*do_mfm)(void) = NULL; | ||
126 | static struct request_queue *mfm_queue; | ||
127 | static DEFINE_SPINLOCK(mfm_lock); | ||
128 | |||
129 | #define MAJOR_NR MFM_ACORN_MAJOR | ||
130 | #define QUEUE (mfm_queue) | ||
131 | #define CURRENT elv_next_request(mfm_queue) | ||
132 | /* | ||
133 | * This sort of stuff should be in a header file shared with ide.c, hd.c, xd.c etc | ||
134 | */ | ||
135 | #ifndef HDIO_GETGEO | ||
136 | #define HDIO_GETGEO 0x301 | ||
137 | struct hd_geometry { | ||
138 | unsigned char heads; | ||
139 | unsigned char sectors; | ||
140 | unsigned short cylinders; | ||
141 | unsigned long start; | ||
142 | }; | ||
143 | #endif | ||
144 | |||
145 | |||
146 | /* | ||
147 | * Configuration section | ||
148 | * | ||
149 | * This is the maximum number of drives that we accept | ||
150 | */ | ||
151 | #define MFM_MAXDRIVES 2 | ||
152 | /* | ||
153 | * Linux I/O address of onboard MFM controller or 0 to disable this | ||
154 | */ | ||
155 | #define ONBOARD_MFM_ADDRESS ((0x002d0000 >> 2) | 0x80000000) | ||
156 | /* | ||
157 | * Uncomment this to enable debugging in the MFM driver... | ||
158 | */ | ||
159 | #ifndef DEBUG | ||
160 | /*#define DEBUG */ | ||
161 | #endif | ||
162 | /* | ||
163 | * End of configuration | ||
164 | */ | ||
165 | |||
166 | |||
167 | /* | ||
168 | * This structure contains all information to do with a particular physical | ||
169 | * device. | ||
170 | */ | ||
171 | struct mfm_info { | ||
172 | unsigned char sectors; | ||
173 | unsigned char heads; | ||
174 | unsigned short cylinders; | ||
175 | unsigned short lowcurrent; | ||
176 | unsigned short precomp; | ||
177 | #define NO_TRACK -1 | ||
178 | #define NEED_1_RECAL -2 | ||
179 | #define NEED_2_RECAL -3 | ||
180 | int cylinder; | ||
181 | struct { | ||
182 | char recal; | ||
183 | char report; | ||
184 | char abort; | ||
185 | } errors; | ||
186 | } mfm_info[MFM_MAXDRIVES]; | ||
187 | |||
188 | #define MFM_DRV_INFO mfm_info[raw_cmd.dev] | ||
189 | |||
190 | /* Stuff from the assembly routines */ | ||
191 | extern unsigned int hdc63463_baseaddress; /* Controller base address */ | ||
192 | extern unsigned int hdc63463_irqpolladdress; /* Address to read to test for int */ | ||
193 | extern unsigned int hdc63463_irqpollmask; /* Mask for irq register */ | ||
194 | extern unsigned int hdc63463_dataptr; /* Pointer to kernel data space to DMA */ | ||
195 | extern int hdc63463_dataleft; /* Number of bytes left to transfer */ | ||
196 | |||
197 | |||
198 | |||
199 | |||
200 | static int lastspecifieddrive; | ||
201 | static unsigned Busy; | ||
202 | |||
203 | static unsigned int PartFragRead; /* The number of sectors which have been read | ||
204 | during a partial read split over two | ||
205 | cylinders. If 0 it means a partial | ||
206 | read did not occur. */ | ||
207 | |||
208 | static unsigned int PartFragRead_RestartBlock; /* Where to restart on a split access */ | ||
209 | static unsigned int PartFragRead_SectorsLeft; /* Where to restart on a split access */ | ||
210 | |||
211 | static int Sectors256LeftInCurrent; /* i.e. 256 byte sectors left in current */ | ||
212 | static int SectorsLeftInRequest; /* i.e. blocks left in the thing mfm_request was called for */ | ||
213 | static int Copy_Sector; /* The 256 byte sector we are currently at - fragments need to know | ||
214 | where to take over */ | ||
215 | static char *Copy_buffer; | ||
216 | |||
217 | |||
218 | static void mfm_seek(void); | ||
219 | static void mfm_rerequest(void); | ||
220 | static void mfm_request(void); | ||
221 | static void mfm_specify (void); | ||
222 | static void issue_request(unsigned int block, unsigned int nsect, | ||
223 | struct request *req); | ||
224 | |||
225 | static unsigned int mfm_addr; /* Controller address */ | ||
226 | static unsigned int mfm_IRQPollLoc; /* Address to read for IRQ information */ | ||
227 | static unsigned int mfm_irqenable; /* Podule IRQ enable location */ | ||
228 | static unsigned char mfm_irq; /* Interrupt number */ | ||
229 | static int mfm_drives = 0; /* drives available */ | ||
230 | static int mfm_status = 0; /* interrupt status */ | ||
231 | static int *errors; | ||
232 | |||
233 | static struct rawcmd { | ||
234 | unsigned int dev; | ||
235 | unsigned int cylinder; | ||
236 | unsigned int head; | ||
237 | unsigned int sector; | ||
238 | unsigned int cmdtype; | ||
239 | unsigned int cmdcode; | ||
240 | unsigned char cmddata[16]; | ||
241 | unsigned int cmdlen; | ||
242 | } raw_cmd; | ||
243 | |||
244 | static unsigned char result[16]; | ||
245 | |||
246 | static struct cont { | ||
247 | void (*interrupt) (void); /* interrupt handler */ | ||
248 | void (*error) (void); /* error handler */ | ||
249 | void (*redo) (void); /* redo handler */ | ||
250 | void (*done) (int st); /* done handler */ | ||
251 | } *cont = NULL; | ||
252 | |||
253 | #if 0 | ||
254 | static struct tq_struct mfm_tq = {0, 0, (void (*)(void *)) NULL, 0}; | ||
255 | #endif | ||
256 | |||
257 | int number_mfm_drives = 1; | ||
258 | |||
259 | /* ------------------------------------------------------------------------------------------ */ | ||
260 | /* | ||
261 | * From the HD63463 data sheet from Hitachi Ltd. | ||
262 | */ | ||
263 | |||
264 | #define MFM_COMMAND (mfm_addr + 0) | ||
265 | #define MFM_DATAOUT (mfm_addr + 1) | ||
266 | #define MFM_STATUS (mfm_addr + 8) | ||
267 | #define MFM_DATAIN (mfm_addr + 9) | ||
268 | |||
269 | #define CMD_ABT 0xF0 /* Abort */ | ||
270 | #define CMD_SPC 0xE8 /* Specify */ | ||
271 | #define CMD_TST 0xE0 /* Test */ | ||
272 | #define CMD_RCLB 0xC8 /* Recalibrate */ | ||
273 | #define CMD_SEK 0xC0 /* Seek */ | ||
274 | #define CMD_WFS 0xAB /* Write Format Skew */ | ||
275 | #define CMD_WFM 0xA3 /* Write Format */ | ||
276 | #define CMD_MTB 0x90 /* Memory to buffer */ | ||
277 | #define CMD_CMPD 0x88 /* Compare data */ | ||
278 | #define CMD_WD 0x87 /* Write data */ | ||
279 | #define CMD_RED 0x70 /* Read erroneous data */ | ||
280 | #define CMD_RIS 0x68 /* Read ID skew */ | ||
281 | #define CMD_FID 0x61 /* Find ID */ | ||
282 | #define CMD_RID 0x60 /* Read ID */ | ||
283 | #define CMD_BTM 0x50 /* Buffer to memory */ | ||
284 | #define CMD_CKD 0x48 /* Check data */ | ||
285 | #define CMD_RD 0x40 /* Read data */ | ||
286 | #define CMD_OPBW 0x38 /* Open buffer write */ | ||
287 | #define CMD_OPBR 0x30 /* Open buffer read */ | ||
288 | #define CMD_CKV 0x28 /* Check drive */ | ||
289 | #define CMD_CKE 0x20 /* Check ECC */ | ||
290 | #define CMD_POD 0x18 /* Polling disable */ | ||
291 | #define CMD_POL 0x10 /* Polling enable */ | ||
292 | #define CMD_RCAL 0x08 /* Recall */ | ||
293 | |||
294 | #define STAT_BSY 0x8000 /* Busy */ | ||
295 | #define STAT_CPR 0x4000 /* Command Parameter Rejection */ | ||
296 | #define STAT_CED 0x2000 /* Command end */ | ||
297 | #define STAT_SED 0x1000 /* Seek end */ | ||
298 | #define STAT_DER 0x0800 /* Drive error */ | ||
299 | #define STAT_ABN 0x0400 /* Abnormal end */ | ||
300 | #define STAT_POL 0x0200 /* Polling */ | ||
301 | |||
302 | /* ------------------------------------------------------------------------------------------ */ | ||
303 | #ifdef DEBUG | ||
304 | static void console_printf(const char *fmt,...) | ||
305 | { | ||
306 | static char buffer[2048]; /* Arbitary! */ | ||
307 | extern void console_print(const char *); | ||
308 | unsigned long flags; | ||
309 | va_list ap; | ||
310 | |||
311 | local_irq_save(flags); | ||
312 | |||
313 | va_start(ap, fmt); | ||
314 | vsprintf(buffer, fmt, ap); | ||
315 | console_print(buffer); | ||
316 | va_end(fmt); | ||
317 | |||
318 | local_irq_restore(flags); | ||
319 | }; /* console_printf */ | ||
320 | |||
321 | #define DBG(x...) console_printf(x) | ||
322 | #else | ||
323 | #define DBG(x...) | ||
324 | #endif | ||
325 | |||
326 | static void print_status(void) | ||
327 | { | ||
328 | char *error; | ||
329 | static char *errors[] = { | ||
330 | "no error", | ||
331 | "command aborted", | ||
332 | "invalid command", | ||
333 | "parameter error", | ||
334 | "not initialised", | ||
335 | "rejected TEST", | ||
336 | "no useld", | ||
337 | "write fault", | ||
338 | "not ready", | ||
339 | "no scp", | ||
340 | "in seek", | ||
341 | "invalid NCA", | ||
342 | "invalid step rate", | ||
343 | "seek error", | ||
344 | "over run", | ||
345 | "invalid PHA", | ||
346 | "data field EEC error", | ||
347 | "data field CRC error", | ||
348 | "error corrected", | ||
349 | "data field fatal error", | ||
350 | "no data am", | ||
351 | "not hit", | ||
352 | "ID field CRC error", | ||
353 | "time over", | ||
354 | "no ID am", | ||
355 | "not writable" | ||
356 | }; | ||
357 | if (result[1] < 0x65) | ||
358 | error = errors[result[1] >> 2]; | ||
359 | else | ||
360 | error = "unknown"; | ||
361 | printk("("); | ||
362 | if (mfm_status & STAT_BSY) printk("BSY "); | ||
363 | if (mfm_status & STAT_CPR) printk("CPR "); | ||
364 | if (mfm_status & STAT_CED) printk("CED "); | ||
365 | if (mfm_status & STAT_SED) printk("SED "); | ||
366 | if (mfm_status & STAT_DER) printk("DER "); | ||
367 | if (mfm_status & STAT_ABN) printk("ABN "); | ||
368 | if (mfm_status & STAT_POL) printk("POL "); | ||
369 | printk(") SSB = %X (%s)\n", result[1], error); | ||
370 | |||
371 | } | ||
372 | |||
373 | /* ------------------------------------------------------------------------------------- */ | ||
374 | |||
375 | static void issue_command(int command, unsigned char *cmdb, int len) | ||
376 | { | ||
377 | int status; | ||
378 | #ifdef DEBUG | ||
379 | int i; | ||
380 | console_printf("issue_command: %02X: ", command); | ||
381 | for (i = 0; i < len; i++) | ||
382 | console_printf("%02X ", cmdb[i]); | ||
383 | console_printf("\n"); | ||
384 | #endif | ||
385 | |||
386 | do { | ||
387 | status = inw(MFM_STATUS); | ||
388 | } while (status & (STAT_BSY | STAT_POL)); | ||
389 | DBG("issue_command: status after pol/bsy loop: %02X:\n ", status >> 8); | ||
390 | |||
391 | if (status & (STAT_CPR | STAT_CED | STAT_SED | STAT_DER | STAT_ABN)) { | ||
392 | outw(CMD_RCAL, MFM_COMMAND); | ||
393 | while (inw(MFM_STATUS) & STAT_BSY); | ||
394 | } | ||
395 | status = inw(MFM_STATUS); | ||
396 | DBG("issue_command: status before parameter issue: %02X:\n ", status >> 8); | ||
397 | |||
398 | while (len > 0) { | ||
399 | outw(cmdb[1] | (cmdb[0] << 8), MFM_DATAOUT); | ||
400 | len -= 2; | ||
401 | cmdb += 2; | ||
402 | } | ||
403 | status = inw(MFM_STATUS); | ||
404 | DBG("issue_command: status before command issue: %02X:\n ", status >> 8); | ||
405 | |||
406 | outw(command, MFM_COMMAND); | ||
407 | status = inw(MFM_STATUS); | ||
408 | DBG("issue_command: status immediately after command issue: %02X:\n ", status >> 8); | ||
409 | } | ||
410 | |||
411 | static void wait_for_completion(void) | ||
412 | { | ||
413 | while ((mfm_status = inw(MFM_STATUS)) & STAT_BSY); | ||
414 | } | ||
415 | |||
416 | static void wait_for_command_end(void) | ||
417 | { | ||
418 | int i; | ||
419 | |||
420 | while (!((mfm_status = inw(MFM_STATUS)) & STAT_CED)); | ||
421 | |||
422 | for (i = 0; i < 16;) { | ||
423 | int in; | ||
424 | in = inw(MFM_DATAIN); | ||
425 | result[i++] = in >> 8; | ||
426 | result[i++] = in; | ||
427 | } | ||
428 | outw (CMD_RCAL, MFM_COMMAND); | ||
429 | } | ||
430 | |||
431 | /* ------------------------------------------------------------------------------------- */ | ||
432 | |||
433 | static void mfm_rw_intr(void) | ||
434 | { | ||
435 | int old_status; /* Holds status on entry, we read to see if the command just finished */ | ||
436 | #ifdef DEBUG | ||
437 | console_printf("mfm_rw_intr...dataleft=%d\n", hdc63463_dataleft); | ||
438 | print_status(); | ||
439 | #endif | ||
440 | |||
441 | /* Now don't handle the error until BSY drops */ | ||
442 | if ((mfm_status & (STAT_DER | STAT_ABN)) && ((mfm_status&STAT_BSY)==0)) { | ||
443 | /* Something has gone wrong - let's try that again */ | ||
444 | outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ | ||
445 | if (cont) { | ||
446 | DBG("mfm_rw_intr: DER/ABN err\n"); | ||
447 | cont->error(); | ||
448 | cont->redo(); | ||
449 | }; | ||
450 | return; | ||
451 | }; | ||
452 | |||
453 | /* OK so what ever happened it's not an error, now I reckon we are left between | ||
454 | a choice of command end or some data which is ready to be collected */ | ||
455 | /* I think we have to transfer data while the interrupt line is on and its | ||
456 | not any other type of interrupt */ | ||
457 | if (CURRENT->cmd == WRITE) { | ||
458 | extern void hdc63463_writedma(void); | ||
459 | if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) { | ||
460 | printk("mfm_rw_intr: Apparent DMA write request when no more to DMA\n"); | ||
461 | if (cont) { | ||
462 | cont->error(); | ||
463 | cont->redo(); | ||
464 | }; | ||
465 | return; | ||
466 | }; | ||
467 | hdc63463_writedma(); | ||
468 | } else { | ||
469 | extern void hdc63463_readdma(void); | ||
470 | if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) { | ||
471 | printk("mfm_rw_intr: Apparent DMA read request when no more to DMA\n"); | ||
472 | if (cont) { | ||
473 | cont->error(); | ||
474 | cont->redo(); | ||
475 | }; | ||
476 | return; | ||
477 | }; | ||
478 | DBG("Going to try read dma..............status=0x%x, buffer=%p\n", mfm_status, hdc63463_dataptr); | ||
479 | hdc63463_readdma(); | ||
480 | }; /* Read */ | ||
481 | |||
482 | if (hdc63463_dataptr != ((unsigned int) Copy_buffer + 256)) { | ||
483 | /* If we didn't actually manage to get any data on this interrupt - but why? We got the interrupt */ | ||
484 | /* Ah - well looking at the status its just when we get command end; so no problem */ | ||
485 | /*console_printf("mfm: dataptr mismatch. dataptr=0x%08x Copy_buffer+256=0x%08p\n", | ||
486 | hdc63463_dataptr,Copy_buffer+256); | ||
487 | print_status(); */ | ||
488 | } else { | ||
489 | Sectors256LeftInCurrent--; | ||
490 | Copy_buffer += 256; | ||
491 | Copy_Sector++; | ||
492 | |||
493 | /* We have come to the end of this request */ | ||
494 | if (!Sectors256LeftInCurrent) { | ||
495 | DBG("mfm: end_request for CURRENT=0x%p CURRENT(sector=%d current_nr_sectors=%d nr_sectors=%d)\n", | ||
496 | CURRENT, CURRENT->sector, CURRENT->current_nr_sectors, CURRENT->nr_sectors); | ||
497 | |||
498 | CURRENT->nr_sectors -= CURRENT->current_nr_sectors; | ||
499 | CURRENT->sector += CURRENT->current_nr_sectors; | ||
500 | SectorsLeftInRequest -= CURRENT->current_nr_sectors; | ||
501 | |||
502 | end_request(CURRENT, 1); | ||
503 | if (SectorsLeftInRequest) { | ||
504 | hdc63463_dataptr = (unsigned int) CURRENT->buffer; | ||
505 | Copy_buffer = CURRENT->buffer; | ||
506 | Sectors256LeftInCurrent = CURRENT->current_nr_sectors * 2; | ||
507 | errors = &(CURRENT->errors); | ||
508 | /* These should match the present calculations of the next logical sector | ||
509 | on the device | ||
510 | Copy_Sector=CURRENT->sector*2; */ | ||
511 | |||
512 | if (Copy_Sector != CURRENT->sector * 2) | ||
513 | #ifdef DEBUG | ||
514 | /*console_printf*/printk("mfm: Copy_Sector mismatch. Copy_Sector=%d CURRENT->sector*2=%d\n", | ||
515 | Copy_Sector, CURRENT->sector * 2); | ||
516 | #else | ||
517 | printk("mfm: Copy_Sector mismatch! Eek!\n"); | ||
518 | #endif | ||
519 | }; /* CURRENT */ | ||
520 | }; /* Sectors256LeftInCurrent */ | ||
521 | }; | ||
522 | |||
523 | old_status = mfm_status; | ||
524 | mfm_status = inw(MFM_STATUS); | ||
525 | if (mfm_status & (STAT_DER | STAT_ABN)) { | ||
526 | /* Something has gone wrong - let's try that again */ | ||
527 | if (cont) { | ||
528 | DBG("mfm_rw_intr: DER/ABN error\n"); | ||
529 | cont->error(); | ||
530 | cont->redo(); | ||
531 | }; | ||
532 | return; | ||
533 | }; | ||
534 | |||
535 | /* If this code wasn't entered due to command_end but there is | ||
536 | now a command end we must read the command results out. If it was | ||
537 | entered like this then mfm_interrupt_handler would have done the | ||
538 | job. */ | ||
539 | if ((!((old_status & (STAT_CPR | STAT_BSY)) == STAT_CPR)) && | ||
540 | ((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR)) { | ||
541 | int len = 0; | ||
542 | while (len < 16) { | ||
543 | int in; | ||
544 | in = inw(MFM_DATAIN); | ||
545 | result[len++] = in >> 8; | ||
546 | result[len++] = in; | ||
547 | }; | ||
548 | }; /* Result read */ | ||
549 | |||
550 | /*console_printf ("mfm_rw_intr nearexit [%02X]\n", __raw_readb(mfm_IRQPollLoc)); */ | ||
551 | |||
552 | /* If end of command move on */ | ||
553 | if (mfm_status & (STAT_CED)) { | ||
554 | outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ | ||
555 | /* End of command - trigger the next command */ | ||
556 | if (cont) { | ||
557 | cont->done(1); | ||
558 | } | ||
559 | DBG("mfm_rw_intr: returned from cont->done\n"); | ||
560 | } else { | ||
561 | /* Its going to generate another interrupt */ | ||
562 | do_mfm = mfm_rw_intr; | ||
563 | }; | ||
564 | } | ||
565 | |||
566 | static void mfm_setup_rw(void) | ||
567 | { | ||
568 | DBG("setting up for rw...\n"); | ||
569 | |||
570 | do_mfm = mfm_rw_intr; | ||
571 | issue_command(raw_cmd.cmdcode, raw_cmd.cmddata, raw_cmd.cmdlen); | ||
572 | } | ||
573 | |||
574 | static void mfm_recal_intr(void) | ||
575 | { | ||
576 | #ifdef DEBUG | ||
577 | console_printf("recal intr - status = "); | ||
578 | print_status(); | ||
579 | #endif | ||
580 | outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ | ||
581 | if (mfm_status & (STAT_DER | STAT_ABN)) { | ||
582 | printk("recal failed\n"); | ||
583 | MFM_DRV_INFO.cylinder = NEED_2_RECAL; | ||
584 | if (cont) { | ||
585 | cont->error(); | ||
586 | cont->redo(); | ||
587 | } | ||
588 | return; | ||
589 | } | ||
590 | /* Thats seek end - we are finished */ | ||
591 | if (mfm_status & STAT_SED) { | ||
592 | issue_command(CMD_POD, NULL, 0); | ||
593 | MFM_DRV_INFO.cylinder = 0; | ||
594 | mfm_seek(); | ||
595 | return; | ||
596 | } | ||
597 | /* Command end without seek end (see data sheet p.20) for parallel seek | ||
598 | - we have to send a POL command to wait for the seek */ | ||
599 | if (mfm_status & STAT_CED) { | ||
600 | do_mfm = mfm_recal_intr; | ||
601 | issue_command(CMD_POL, NULL, 0); | ||
602 | return; | ||
603 | } | ||
604 | printk("recal: unknown status\n"); | ||
605 | } | ||
606 | |||
607 | static void mfm_seek_intr(void) | ||
608 | { | ||
609 | #ifdef DEBUG | ||
610 | console_printf("seek intr - status = "); | ||
611 | print_status(); | ||
612 | #endif | ||
613 | outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ | ||
614 | if (mfm_status & (STAT_DER | STAT_ABN)) { | ||
615 | printk("seek failed\n"); | ||
616 | MFM_DRV_INFO.cylinder = NEED_2_RECAL; | ||
617 | if (cont) { | ||
618 | cont->error(); | ||
619 | cont->redo(); | ||
620 | } | ||
621 | return; | ||
622 | } | ||
623 | if (mfm_status & STAT_SED) { | ||
624 | issue_command(CMD_POD, NULL, 0); | ||
625 | MFM_DRV_INFO.cylinder = raw_cmd.cylinder; | ||
626 | mfm_seek(); | ||
627 | return; | ||
628 | } | ||
629 | if (mfm_status & STAT_CED) { | ||
630 | do_mfm = mfm_seek_intr; | ||
631 | issue_command(CMD_POL, NULL, 0); | ||
632 | return; | ||
633 | } | ||
634 | printk("seek: unknown status\n"); | ||
635 | } | ||
636 | |||
637 | /* IDEA2 seems to work better - its what RiscOS sets my | ||
638 | * disc to - on its SECOND call to specify! | ||
639 | */ | ||
640 | #define IDEA2 | ||
641 | #ifndef IDEA2 | ||
642 | #define SPEC_SL 0x16 | ||
643 | #define SPEC_SH 0xa9 /* Step pulse high=21, Record Length=001 (256 bytes) */ | ||
644 | #else | ||
645 | #define SPEC_SL 0x00 /* OM2 - SL - step pulse low */ | ||
646 | #define SPEC_SH 0x21 /* Step pulse high=4, Record Length=001 (256 bytes) */ | ||
647 | #endif | ||
648 | |||
649 | static void mfm_setupspecify (int drive, unsigned char *cmdb) | ||
650 | { | ||
651 | cmdb[0] = 0x1f; /* OM0 - !SECT,!MOD,!DIF,PADP,ECD,CRCP,CRCI,ACOR */ | ||
652 | cmdb[1] = 0xc3; /* OM1 - DTM,BRST,!CEDM,!SEDM,!DERM,0,AMEX,PSK */ | ||
653 | cmdb[2] = SPEC_SL; /* OM2 - SL - step pulse low */ | ||
654 | cmdb[3] = (number_mfm_drives == 1) ? 0x02 : 0x06; /* 1 or 2 drives */ | ||
655 | cmdb[4] = 0xfc | ((mfm_info[drive].cylinders - 1) >> 8);/* RW time over/high part of number of cylinders */ | ||
656 | cmdb[5] = mfm_info[drive].cylinders - 1; /* low part of number of cylinders */ | ||
657 | cmdb[6] = mfm_info[drive].heads - 1; /* Number of heads */ | ||
658 | cmdb[7] = mfm_info[drive].sectors - 1; /* Number of sectors */ | ||
659 | cmdb[8] = SPEC_SH; | ||
660 | cmdb[9] = 0x0a; /* gap length 1 */ | ||
661 | cmdb[10] = 0x0d; /* gap length 2 */ | ||
662 | cmdb[11] = 0x0c; /* gap length 3 */ | ||
663 | cmdb[12] = (mfm_info[drive].precomp - 1) >> 8; /* pre comp cylinder */ | ||
664 | cmdb[13] = mfm_info[drive].precomp - 1; | ||
665 | cmdb[14] = (mfm_info[drive].lowcurrent - 1) >> 8; /* Low current cylinder */ | ||
666 | cmdb[15] = mfm_info[drive].lowcurrent - 1; | ||
667 | } | ||
668 | |||
669 | static void mfm_specify (void) | ||
670 | { | ||
671 | unsigned char cmdb[16]; | ||
672 | |||
673 | DBG("specify...dev=%d lastspecified=%d\n", raw_cmd.dev, lastspecifieddrive); | ||
674 | mfm_setupspecify (raw_cmd.dev, cmdb); | ||
675 | |||
676 | issue_command (CMD_SPC, cmdb, 16); | ||
677 | /* Ensure that we will do another specify if we move to the other drive */ | ||
678 | lastspecifieddrive = raw_cmd.dev; | ||
679 | wait_for_completion(); | ||
680 | } | ||
681 | |||
682 | static void mfm_seek(void) | ||
683 | { | ||
684 | unsigned char cmdb[4]; | ||
685 | |||
686 | DBG("seeking...\n"); | ||
687 | if (MFM_DRV_INFO.cylinder < 0) { | ||
688 | do_mfm = mfm_recal_intr; | ||
689 | DBG("mfm_seek: about to call specify\n"); | ||
690 | mfm_specify (); /* DAG added this */ | ||
691 | |||
692 | cmdb[0] = raw_cmd.dev + 1; | ||
693 | cmdb[1] = 0; | ||
694 | |||
695 | issue_command(CMD_RCLB, cmdb, 2); | ||
696 | return; | ||
697 | } | ||
698 | if (MFM_DRV_INFO.cylinder != raw_cmd.cylinder) { | ||
699 | cmdb[0] = raw_cmd.dev + 1; | ||
700 | cmdb[1] = 0; /* raw_cmd.head; DAG: My data sheet says this should be 0 */ | ||
701 | cmdb[2] = raw_cmd.cylinder >> 8; | ||
702 | cmdb[3] = raw_cmd.cylinder; | ||
703 | |||
704 | do_mfm = mfm_seek_intr; | ||
705 | issue_command(CMD_SEK, cmdb, 4); | ||
706 | } else | ||
707 | mfm_setup_rw(); | ||
708 | } | ||
709 | |||
710 | static void mfm_initialise(void) | ||
711 | { | ||
712 | DBG("init...\n"); | ||
713 | mfm_seek(); | ||
714 | } | ||
715 | |||
716 | static void request_done(int uptodate) | ||
717 | { | ||
718 | DBG("mfm:request_done\n"); | ||
719 | if (uptodate) { | ||
720 | unsigned char block[2] = {0, 0}; | ||
721 | |||
722 | /* Apparently worked - let's check bytes left to DMA */ | ||
723 | if (hdc63463_dataleft != (PartFragRead_SectorsLeft * 256)) { | ||
724 | printk("mfm: request_done - dataleft=%d - should be %d - Eek!\n", hdc63463_dataleft, PartFragRead_SectorsLeft * 256); | ||
725 | end_request(CURRENT, 0); | ||
726 | Busy = 0; | ||
727 | }; | ||
728 | /* Potentially this means that we've done; but we might be doing | ||
729 | a partial access, (over two cylinders) or we may have a number | ||
730 | of fragments in an image file. First let's deal with partial accesss | ||
731 | */ | ||
732 | if (PartFragRead) { | ||
733 | /* Yep - a partial access */ | ||
734 | |||
735 | /* and issue the remainder */ | ||
736 | issue_request(PartFragRead_RestartBlock, PartFragRead_SectorsLeft, CURRENT); | ||
737 | return; | ||
738 | } | ||
739 | |||
740 | /* ah well - perhaps there is another fragment to go */ | ||
741 | |||
742 | /* Increment pointers/counts to start of next fragment */ | ||
743 | if (SectorsLeftInRequest > 0) printk("mfm: SectorsLeftInRequest>0 - Eek! Shouldn't happen!\n"); | ||
744 | |||
745 | /* No - its the end of the line */ | ||
746 | /* end_request's should have happened at the end of sector DMAs */ | ||
747 | /* Turns Drive LEDs off - may slow it down? */ | ||
748 | if (!elv_next_request(QUEUE)) | ||
749 | issue_command(CMD_CKV, block, 2); | ||
750 | |||
751 | Busy = 0; | ||
752 | DBG("request_done: About to mfm_request\n"); | ||
753 | /* Next one please */ | ||
754 | mfm_request(); /* Moved from mfm_rw_intr */ | ||
755 | DBG("request_done: returned from mfm_request\n"); | ||
756 | } else { | ||
757 | printk("mfm:request_done: update=0\n"); | ||
758 | end_request(CURRENT, 0); | ||
759 | Busy = 0; | ||
760 | } | ||
761 | } | ||
762 | |||
763 | static void error_handler(void) | ||
764 | { | ||
765 | printk("error detected... status = "); | ||
766 | print_status(); | ||
767 | (*errors)++; | ||
768 | if (*errors > MFM_DRV_INFO.errors.abort) | ||
769 | cont->done(0); | ||
770 | if (*errors > MFM_DRV_INFO.errors.recal) | ||
771 | MFM_DRV_INFO.cylinder = NEED_2_RECAL; | ||
772 | } | ||
773 | |||
774 | static void rw_interrupt(void) | ||
775 | { | ||
776 | printk("rw_interrupt\n"); | ||
777 | } | ||
778 | |||
779 | static struct cont rw_cont = | ||
780 | { | ||
781 | rw_interrupt, | ||
782 | error_handler, | ||
783 | mfm_rerequest, | ||
784 | request_done | ||
785 | }; | ||
786 | |||
787 | /* | ||
788 | * Actually gets round to issuing the request - note everything at this | ||
789 | * point is in 256 byte sectors not Linux 512 byte blocks | ||
790 | */ | ||
791 | static void issue_request(unsigned int block, unsigned int nsect, | ||
792 | struct request *req) | ||
793 | { | ||
794 | struct gendisk *disk = req->rq_disk; | ||
795 | struct mfm_info *p = disk->private_data; | ||
796 | int track, start_head, start_sector; | ||
797 | int sectors_to_next_cyl; | ||
798 | dev = p - mfm_info; | ||
799 | |||
800 | track = block / p->sectors; | ||
801 | start_sector = block % p->sectors; | ||
802 | start_head = track % p->heads; | ||
803 | |||
804 | /* First get the number of whole tracks which are free before the next | ||
805 | track */ | ||
806 | sectors_to_next_cyl = (p->heads - (start_head + 1)) * p->sectors; | ||
807 | /* Then add in the number of sectors left on this track */ | ||
808 | sectors_to_next_cyl += (p->sectors - start_sector); | ||
809 | |||
810 | DBG("issue_request: mfm_info[dev].sectors=%d track=%d\n", p->sectors, track); | ||
811 | |||
812 | raw_cmd.dev = dev; | ||
813 | raw_cmd.sector = start_sector; | ||
814 | raw_cmd.head = start_head; | ||
815 | raw_cmd.cylinder = track / p->heads; | ||
816 | raw_cmd.cmdtype = CURRENT->cmd; | ||
817 | raw_cmd.cmdcode = CURRENT->cmd == WRITE ? CMD_WD : CMD_RD; | ||
818 | raw_cmd.cmddata[0] = dev + 1; /* DAG: +1 to get US */ | ||
819 | raw_cmd.cmddata[1] = raw_cmd.head; | ||
820 | raw_cmd.cmddata[2] = raw_cmd.cylinder >> 8; | ||
821 | raw_cmd.cmddata[3] = raw_cmd.cylinder; | ||
822 | raw_cmd.cmddata[4] = raw_cmd.head; | ||
823 | raw_cmd.cmddata[5] = raw_cmd.sector; | ||
824 | |||
825 | /* Was == and worked - how the heck??? */ | ||
826 | if (lastspecifieddrive != raw_cmd.dev) | ||
827 | mfm_specify (); | ||
828 | |||
829 | if (nsect <= sectors_to_next_cyl) { | ||
830 | raw_cmd.cmddata[6] = nsect >> 8; | ||
831 | raw_cmd.cmddata[7] = nsect; | ||
832 | PartFragRead = 0; /* All in one */ | ||
833 | PartFragRead_SectorsLeft = 0; /* Must set this - used in DMA calcs */ | ||
834 | } else { | ||
835 | raw_cmd.cmddata[6] = sectors_to_next_cyl >> 8; | ||
836 | raw_cmd.cmddata[7] = sectors_to_next_cyl; | ||
837 | PartFragRead = sectors_to_next_cyl; /* only do this many this time */ | ||
838 | PartFragRead_RestartBlock = block + sectors_to_next_cyl; /* Where to restart from */ | ||
839 | PartFragRead_SectorsLeft = nsect - sectors_to_next_cyl; | ||
840 | } | ||
841 | raw_cmd.cmdlen = 8; | ||
842 | |||
843 | /* Setup DMA pointers */ | ||
844 | hdc63463_dataptr = (unsigned int) Copy_buffer; | ||
845 | hdc63463_dataleft = nsect * 256; /* Better way? */ | ||
846 | |||
847 | DBG("mfm%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx (%p)\n", | ||
848 | raw_cmd.dev + 'a', (CURRENT->cmd == READ) ? "read" : "writ", | ||
849 | raw_cmd.cylinder, | ||
850 | raw_cmd.head, | ||
851 | raw_cmd.sector, nsect, (unsigned long) Copy_buffer, CURRENT); | ||
852 | |||
853 | cont = &rw_cont; | ||
854 | errors = &(CURRENT->errors); | ||
855 | #if 0 | ||
856 | mfm_tq.routine = (void (*)(void *)) mfm_initialise; | ||
857 | queue_task(&mfm_tq, &tq_immediate); | ||
858 | mark_bh(IMMEDIATE_BH); | ||
859 | #else | ||
860 | mfm_initialise(); | ||
861 | #endif | ||
862 | } /* issue_request */ | ||
863 | |||
864 | /* | ||
865 | * Called when an error has just happened - need to trick mfm_request | ||
866 | * into thinking we weren't busy | ||
867 | * | ||
868 | * Turn off ints - mfm_request expects them this way | ||
869 | */ | ||
870 | static void mfm_rerequest(void) | ||
871 | { | ||
872 | DBG("mfm_rerequest\n"); | ||
873 | cli(); | ||
874 | Busy = 0; | ||
875 | mfm_request(); | ||
876 | } | ||
877 | |||
878 | static struct gendisk *mfm_gendisk[2]; | ||
879 | |||
880 | static void mfm_request(void) | ||
881 | { | ||
882 | DBG("mfm_request CURRENT=%p Busy=%d\n", CURRENT, Busy); | ||
883 | |||
884 | /* If we are still processing then return; we will get called again */ | ||
885 | if (Busy) { | ||
886 | /* Again seems to be common in 1.3.45 */ | ||
887 | /*DBG*/printk("mfm_request: Exiting due to busy\n"); | ||
888 | return; | ||
889 | } | ||
890 | Busy = 1; | ||
891 | |||
892 | while (1) { | ||
893 | unsigned int block, nsect; | ||
894 | struct gendisk *disk; | ||
895 | |||
896 | DBG("mfm_request: loop start\n"); | ||
897 | sti(); | ||
898 | |||
899 | DBG("mfm_request: before !CURRENT\n"); | ||
900 | |||
901 | if (!CURRENT) { | ||
902 | printk("mfm_request: Exiting due to empty queue (pre)\n"); | ||
903 | do_mfm = NULL; | ||
904 | Busy = 0; | ||
905 | return; | ||
906 | } | ||
907 | |||
908 | DBG("mfm_request: before arg extraction\n"); | ||
909 | |||
910 | disk = CURRENT->rq_disk; | ||
911 | block = CURRENT->sector; | ||
912 | nsect = CURRENT->nr_sectors; | ||
913 | if (block >= get_capacity(disk) || | ||
914 | block+nsect > get_capacity(disk)) { | ||
915 | printk("%s: bad access: block=%d, count=%d, nr_sects=%ld\n", | ||
916 | disk->disk_name, block, nsect, get_capacity(disk)); | ||
917 | printk("mfm: continue 1\n"); | ||
918 | end_request(CURRENT, 0); | ||
919 | Busy = 0; | ||
920 | continue; | ||
921 | } | ||
922 | |||
923 | /* DAG: Linux doesn't cope with this - even though it has an array telling | ||
924 | it the hardware block size - silly */ | ||
925 | block <<= 1; /* Now in 256 byte sectors */ | ||
926 | nsect <<= 1; /* Ditto */ | ||
927 | |||
928 | SectorsLeftInRequest = nsect >> 1; | ||
929 | Sectors256LeftInCurrent = CURRENT->current_nr_sectors * 2; | ||
930 | Copy_buffer = CURRENT->buffer; | ||
931 | Copy_Sector = CURRENT->sector << 1; | ||
932 | |||
933 | DBG("mfm_request: block after offset=%d\n", block); | ||
934 | |||
935 | if (CURRENT->cmd != READ && CURRENT->cmd != WRITE) { | ||
936 | printk("unknown mfm-command %d\n", CURRENT->cmd); | ||
937 | end_request(CURRENT, 0); | ||
938 | Busy = 0; | ||
939 | printk("mfm: continue 4\n"); | ||
940 | continue; | ||
941 | } | ||
942 | issue_request(block, nsect, CURRENT); | ||
943 | |||
944 | break; | ||
945 | } | ||
946 | DBG("mfm_request: Dropping out bottom\n"); | ||
947 | } | ||
948 | |||
949 | static void do_mfm_request(request_queue_t *q) | ||
950 | { | ||
951 | DBG("do_mfm_request: about to mfm_request\n"); | ||
952 | mfm_request(); | ||
953 | } | ||
954 | |||
955 | static void mfm_interrupt_handler(int unused, void *dev_id, struct pt_regs *regs) | ||
956 | { | ||
957 | void (*handler) (void) = do_mfm; | ||
958 | |||
959 | do_mfm = NULL; | ||
960 | |||
961 | DBG("mfm_interrupt_handler (handler=0x%p)\n", handler); | ||
962 | |||
963 | mfm_status = inw(MFM_STATUS); | ||
964 | |||
965 | /* If CPR (Command Parameter Reject) and not busy it means that the command | ||
966 | has some return message to give us */ | ||
967 | if ((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR) { | ||
968 | int len = 0; | ||
969 | while (len < 16) { | ||
970 | int in; | ||
971 | in = inw(MFM_DATAIN); | ||
972 | result[len++] = in >> 8; | ||
973 | result[len++] = in; | ||
974 | } | ||
975 | } | ||
976 | if (handler) { | ||
977 | handler(); | ||
978 | return; | ||
979 | } | ||
980 | outw (CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ | ||
981 | printk ("mfm: unexpected interrupt - status = "); | ||
982 | print_status (); | ||
983 | while (1); | ||
984 | } | ||
985 | |||
986 | |||
987 | |||
988 | |||
989 | |||
990 | /* | ||
991 | * Tell the user about the drive if we decided it exists. | ||
992 | */ | ||
993 | static void mfm_geometry(int drive) | ||
994 | { | ||
995 | struct mfm_info *p = mfm_info + drive; | ||
996 | struct gendisk *disk = mfm_gendisk[drive]; | ||
997 | disk->private_data = p; | ||
998 | if (p->cylinders) | ||
999 | printk ("%s: %dMB CHS=%d/%d/%d LCC=%d RECOMP=%d\n", | ||
1000 | disk->disk_name, | ||
1001 | p->cylinders * p->heads * p->sectors / 4096, | ||
1002 | p->cylinders, p->heads, p->sectors, | ||
1003 | p->lowcurrent, p->precomp); | ||
1004 | set_capacity(disk, p->cylinders * p->heads * p->sectors / 2); | ||
1005 | } | ||
1006 | |||
1007 | #ifdef CONFIG_BLK_DEV_MFM_AUTODETECT | ||
1008 | /* | ||
1009 | * Attempt to detect a drive and find its geometry. The drive has already been | ||
1010 | * specified... | ||
1011 | * | ||
1012 | * We first recalibrate the disk, then try to probe sectors, heads and then | ||
1013 | * cylinders. NOTE! the cylinder probe may break drives. The xd disk driver | ||
1014 | * does something along these lines, so I assume that most drives are up to | ||
1015 | * this mistreatment... | ||
1016 | */ | ||
1017 | static int mfm_detectdrive (int drive) | ||
1018 | { | ||
1019 | unsigned int mingeo[3], maxgeo[3]; | ||
1020 | unsigned int attribute, need_recal = 1; | ||
1021 | unsigned char cmdb[8]; | ||
1022 | |||
1023 | memset (mingeo, 0, sizeof (mingeo)); | ||
1024 | maxgeo[0] = mfm_info[drive].sectors; | ||
1025 | maxgeo[1] = mfm_info[drive].heads; | ||
1026 | maxgeo[2] = mfm_info[drive].cylinders; | ||
1027 | |||
1028 | cmdb[0] = drive + 1; | ||
1029 | cmdb[6] = 0; | ||
1030 | cmdb[7] = 1; | ||
1031 | for (attribute = 0; attribute < 3; attribute++) { | ||
1032 | while (mingeo[attribute] != maxgeo[attribute]) { | ||
1033 | unsigned int variable; | ||
1034 | |||
1035 | variable = (maxgeo[attribute] + mingeo[attribute]) >> 1; | ||
1036 | cmdb[1] = cmdb[2] = cmdb[3] = cmdb[4] = cmdb[5] = 0; | ||
1037 | |||
1038 | if (need_recal) { | ||
1039 | int tries = 5; | ||
1040 | |||
1041 | do { | ||
1042 | issue_command (CMD_RCLB, cmdb, 2); | ||
1043 | wait_for_completion (); | ||
1044 | wait_for_command_end (); | ||
1045 | if (result[1] == 0x20) | ||
1046 | break; | ||
1047 | } while (result[1] && --tries); | ||
1048 | if (result[1]) { | ||
1049 | outw (CMD_RCAL, MFM_COMMAND); | ||
1050 | return 0; | ||
1051 | } | ||
1052 | need_recal = 0; | ||
1053 | } | ||
1054 | |||
1055 | switch (attribute) { | ||
1056 | case 0: | ||
1057 | cmdb[5] = variable; | ||
1058 | issue_command (CMD_CMPD, cmdb, 8); | ||
1059 | break; | ||
1060 | case 1: | ||
1061 | cmdb[1] = variable; | ||
1062 | cmdb[4] = variable; | ||
1063 | issue_command (CMD_CMPD, cmdb, 8); | ||
1064 | break; | ||
1065 | case 2: | ||
1066 | cmdb[2] = variable >> 8; | ||
1067 | cmdb[3] = variable; | ||
1068 | issue_command (CMD_SEK, cmdb, 4); | ||
1069 | break; | ||
1070 | } | ||
1071 | wait_for_completion (); | ||
1072 | wait_for_command_end (); | ||
1073 | |||
1074 | switch (result[1]) { | ||
1075 | case 0x00: | ||
1076 | case 0x50: | ||
1077 | mingeo[attribute] = variable + 1; | ||
1078 | break; | ||
1079 | |||
1080 | case 0x20: | ||
1081 | outw (CMD_RCAL, MFM_COMMAND); | ||
1082 | return 0; | ||
1083 | |||
1084 | case 0x24: | ||
1085 | need_recal = 1; | ||
1086 | default: | ||
1087 | maxgeo[attribute] = variable; | ||
1088 | break; | ||
1089 | } | ||
1090 | } | ||
1091 | } | ||
1092 | mfm_info[drive].cylinders = mingeo[2]; | ||
1093 | mfm_info[drive].lowcurrent = mingeo[2]; | ||
1094 | mfm_info[drive].precomp = mingeo[2] / 2; | ||
1095 | mfm_info[drive].heads = mingeo[1]; | ||
1096 | mfm_info[drive].sectors = mingeo[0]; | ||
1097 | outw (CMD_RCAL, MFM_COMMAND); | ||
1098 | return 1; | ||
1099 | } | ||
1100 | #endif | ||
1101 | |||
1102 | /* | ||
1103 | * Initialise all drive information for this controller. | ||
1104 | */ | ||
1105 | static int mfm_initdrives(void) | ||
1106 | { | ||
1107 | int drive; | ||
1108 | |||
1109 | if (number_mfm_drives > MFM_MAXDRIVES) { | ||
1110 | number_mfm_drives = MFM_MAXDRIVES; | ||
1111 | printk("No. of ADFS MFM drives is greater than MFM_MAXDRIVES - you can't have that many!\n"); | ||
1112 | } | ||
1113 | |||
1114 | for (drive = 0; drive < number_mfm_drives; drive++) { | ||
1115 | mfm_info[drive].lowcurrent = 1; | ||
1116 | mfm_info[drive].precomp = 1; | ||
1117 | mfm_info[drive].cylinder = -1; | ||
1118 | mfm_info[drive].errors.recal = 0; | ||
1119 | mfm_info[drive].errors.report = 0; | ||
1120 | mfm_info[drive].errors.abort = 4; | ||
1121 | |||
1122 | #ifdef CONFIG_BLK_DEV_MFM_AUTODETECT | ||
1123 | mfm_info[drive].cylinders = 1024; | ||
1124 | mfm_info[drive].heads = 8; | ||
1125 | mfm_info[drive].sectors = 64; | ||
1126 | { | ||
1127 | unsigned char cmdb[16]; | ||
1128 | |||
1129 | mfm_setupspecify (drive, cmdb); | ||
1130 | cmdb[1] &= ~0x81; | ||
1131 | issue_command (CMD_SPC, cmdb, 16); | ||
1132 | wait_for_completion (); | ||
1133 | if (!mfm_detectdrive (drive)) { | ||
1134 | mfm_info[drive].cylinders = 0; | ||
1135 | mfm_info[drive].heads = 0; | ||
1136 | mfm_info[drive].sectors = 0; | ||
1137 | } | ||
1138 | cmdb[0] = cmdb[1] = 0; | ||
1139 | issue_command (CMD_CKV, cmdb, 2); | ||
1140 | } | ||
1141 | #else | ||
1142 | mfm_info[drive].cylinders = 1; /* its going to have to figure it out from the partition info */ | ||
1143 | mfm_info[drive].heads = 4; | ||
1144 | mfm_info[drive].sectors = 32; | ||
1145 | #endif | ||
1146 | } | ||
1147 | return number_mfm_drives; | ||
1148 | } | ||
1149 | |||
1150 | |||
1151 | |||
1152 | /* | ||
1153 | * The 'front' end of the mfm driver follows... | ||
1154 | */ | ||
1155 | |||
1156 | static int mfm_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) | ||
1157 | { | ||
1158 | struct mfm_info *p = inode->i_bdev->bd_disk->private_data; | ||
1159 | struct hd_geometry *geo = (struct hd_geometry *) arg; | ||
1160 | if (cmd != HDIO_GETGEO) | ||
1161 | return -EINVAL; | ||
1162 | if (!arg) | ||
1163 | return -EINVAL; | ||
1164 | if (put_user (p->heads, &geo->heads)) | ||
1165 | return -EFAULT; | ||
1166 | if (put_user (p->sectors, &geo->sectors)) | ||
1167 | return -EFAULT; | ||
1168 | if (put_user (p->cylinders, &geo->cylinders)) | ||
1169 | return -EFAULT; | ||
1170 | if (put_user (get_start_sect(inode->i_bdev), &geo->start)) | ||
1171 | return -EFAULT; | ||
1172 | return 0; | ||
1173 | } | ||
1174 | |||
1175 | /* | ||
1176 | * This is to handle various kernel command line parameters | ||
1177 | * specific to this driver. | ||
1178 | */ | ||
1179 | void mfm_setup(char *str, int *ints) | ||
1180 | { | ||
1181 | return; | ||
1182 | } | ||
1183 | |||
1184 | /* | ||
1185 | * Set the CHS from the ADFS boot block if it is present. This is not ideal | ||
1186 | * since if there are any non-ADFS partitions on the disk, this won't work! | ||
1187 | * Hence, I want to get rid of this... | ||
1188 | */ | ||
1189 | void xd_set_geometry(struct block_device *bdev, unsigned char secsptrack, | ||
1190 | unsigned char heads, unsigned int secsize) | ||
1191 | { | ||
1192 | struct mfm_info *p = bdev->bd_disk->private_data; | ||
1193 | int drive = p - mfm_info; | ||
1194 | unsigned long disksize = bdev->bd_inode->i_size; | ||
1195 | |||
1196 | if (p->cylinders == 1) { | ||
1197 | p->sectors = secsptrack; | ||
1198 | p->heads = heads; | ||
1199 | p->cylinders = discsize / (secsptrack * heads * secsize); | ||
1200 | |||
1201 | if ((heads < 1) || (p->cylinders > 1024)) { | ||
1202 | printk("%s: Insane disc shape! Setting to 512/4/32\n", | ||
1203 | bdev->bd_disk->disk_name); | ||
1204 | |||
1205 | /* These values are fairly arbitary, but are there so that if your | ||
1206 | * lucky you can pick apart your disc to find out what is going on - | ||
1207 | * I reckon these figures won't hurt MOST drives | ||
1208 | */ | ||
1209 | p->sectors = 32; | ||
1210 | p->heads = 4; | ||
1211 | p->cylinders = 512; | ||
1212 | } | ||
1213 | if (raw_cmd.dev == drive) | ||
1214 | mfm_specify (); | ||
1215 | mfm_geometry (drive); | ||
1216 | } | ||
1217 | } | ||
1218 | |||
1219 | static struct block_device_operations mfm_fops = | ||
1220 | { | ||
1221 | .owner = THIS_MODULE, | ||
1222 | .ioctl = mfm_ioctl, | ||
1223 | }; | ||
1224 | |||
1225 | /* | ||
1226 | * See if there is a controller at the address presently at mfm_addr | ||
1227 | * | ||
1228 | * We check to see if the controller is busy - if it is, we abort it first, | ||
1229 | * and check that the chip is no longer busy after at least 180 clock cycles. | ||
1230 | * We then issue a command and check that the BSY or CPR bits are set. | ||
1231 | */ | ||
1232 | static int mfm_probecontroller (unsigned int mfm_addr) | ||
1233 | { | ||
1234 | if (inw (MFM_STATUS) & STAT_BSY) { | ||
1235 | outw (CMD_ABT, MFM_COMMAND); | ||
1236 | udelay (50); | ||
1237 | if (inw (MFM_STATUS) & STAT_BSY) | ||
1238 | return 0; | ||
1239 | } | ||
1240 | |||
1241 | if (inw (MFM_STATUS) & STAT_CED) | ||
1242 | outw (CMD_RCAL, MFM_COMMAND); | ||
1243 | |||
1244 | outw (CMD_SEK, MFM_COMMAND); | ||
1245 | |||
1246 | if (inw (MFM_STATUS) & (STAT_BSY | STAT_CPR)) { | ||
1247 | unsigned int count = 2000; | ||
1248 | while (inw (MFM_STATUS) & STAT_BSY) { | ||
1249 | udelay (500); | ||
1250 | if (!--count) | ||
1251 | return 0; | ||
1252 | } | ||
1253 | |||
1254 | outw (CMD_RCAL, MFM_COMMAND); | ||
1255 | } | ||
1256 | return 1; | ||
1257 | } | ||
1258 | |||
1259 | static int mfm_do_init(unsigned char irqmask) | ||
1260 | { | ||
1261 | int i, ret; | ||
1262 | |||
1263 | printk("mfm: found at address %08X, interrupt %d\n", mfm_addr, mfm_irq); | ||
1264 | |||
1265 | ret = -EBUSY; | ||
1266 | if (!request_region (mfm_addr, 10, "mfm")) | ||
1267 | goto out1; | ||
1268 | |||
1269 | ret = register_blkdev(MAJOR_NR, "mfm"); | ||
1270 | if (ret) | ||
1271 | goto out2; | ||
1272 | |||
1273 | /* Stuff for the assembler routines to get to */ | ||
1274 | hdc63463_baseaddress = ioaddr(mfm_addr); | ||
1275 | hdc63463_irqpolladdress = mfm_IRQPollLoc; | ||
1276 | hdc63463_irqpollmask = irqmask; | ||
1277 | |||
1278 | mfm_queue = blk_init_queue(do_mfm_request, &mfm_lock); | ||
1279 | if (!mfm_queue) | ||
1280 | goto out2a; | ||
1281 | |||
1282 | Busy = 0; | ||
1283 | lastspecifieddrive = -1; | ||
1284 | |||
1285 | mfm_drives = mfm_initdrives(); | ||
1286 | if (!mfm_drives) { | ||
1287 | ret = -ENODEV; | ||
1288 | goto out3; | ||
1289 | } | ||
1290 | |||
1291 | for (i = 0; i < mfm_drives; i++) { | ||
1292 | struct gendisk *disk = alloc_disk(64); | ||
1293 | if (!disk) | ||
1294 | goto Enomem; | ||
1295 | disk->major = MAJOR_NR; | ||
1296 | disk->first_minor = i << 6; | ||
1297 | disk->fops = &mfm_fops; | ||
1298 | sprintf(disk->disk_name, "mfm%c", 'a'+i); | ||
1299 | mfm_gendisk[i] = disk; | ||
1300 | } | ||
1301 | |||
1302 | printk("mfm: detected %d hard drive%s\n", mfm_drives, | ||
1303 | mfm_drives == 1 ? "" : "s"); | ||
1304 | ret = request_irq(mfm_irq, mfm_interrupt_handler, SA_INTERRUPT, "MFM harddisk", NULL); | ||
1305 | if (ret) { | ||
1306 | printk("mfm: unable to get IRQ%d\n", mfm_irq); | ||
1307 | goto out4; | ||
1308 | } | ||
1309 | |||
1310 | if (mfm_irqenable) | ||
1311 | outw(0x80, mfm_irqenable); /* Required to enable IRQs from MFM podule */ | ||
1312 | |||
1313 | for (i = 0; i < mfm_drives; i++) { | ||
1314 | mfm_geometry(i); | ||
1315 | mfm_gendisk[i]->queue = mfm_queue; | ||
1316 | add_disk(mfm_gendisk[i]); | ||
1317 | } | ||
1318 | return 0; | ||
1319 | |||
1320 | out4: | ||
1321 | for (i = 0; i < mfm_drives; i++) | ||
1322 | put_disk(mfm_gendisk[i]); | ||
1323 | out3: | ||
1324 | blk_cleanup_queue(mfm_queue); | ||
1325 | out2a: | ||
1326 | unregister_blkdev(MAJOR_NR, "mfm"); | ||
1327 | out2: | ||
1328 | release_region(mfm_addr, 10); | ||
1329 | out1: | ||
1330 | return ret; | ||
1331 | Enomem: | ||
1332 | while (i--) | ||
1333 | put_disk(mfm_gendisk[i]); | ||
1334 | goto out3; | ||
1335 | } | ||
1336 | |||
1337 | static void mfm_do_exit(void) | ||
1338 | { | ||
1339 | int i; | ||
1340 | |||
1341 | free_irq(mfm_irq, NULL); | ||
1342 | for (i = 0; i < mfm_drives; i++) { | ||
1343 | del_gendisk(mfm_gendisk[i]); | ||
1344 | put_disk(mfm_gendisk[i]); | ||
1345 | } | ||
1346 | blk_cleanup_queue(mfm_queue); | ||
1347 | unregister_blkdev(MAJOR_NR, "mfm"); | ||
1348 | if (mfm_addr) | ||
1349 | release_region(mfm_addr, 10); | ||
1350 | } | ||
1351 | |||
1352 | static int __devinit mfm_probe(struct expansion_card *ec, struct ecard_id *id) | ||
1353 | { | ||
1354 | if (mfm_addr) | ||
1355 | return -EBUSY; | ||
1356 | |||
1357 | mfm_addr = ecard_address(ec, ECARD_IOC, ECARD_MEDIUM) + 0x800; | ||
1358 | mfm_IRQPollLoc = ioaddr(mfm_addr + 0x400); | ||
1359 | mfm_irqenable = mfm_IRQPollLoc; | ||
1360 | mfm_irq = ec->irq; | ||
1361 | |||
1362 | return mfm_do_init(0x08); | ||
1363 | } | ||
1364 | |||
1365 | static void __devexit mfm_remove(struct expansion_card *ec) | ||
1366 | { | ||
1367 | outw (0, mfm_irqenable); /* Required to enable IRQs from MFM podule */ | ||
1368 | mfm_do_exit(); | ||
1369 | } | ||
1370 | |||
1371 | static const struct ecard_id mfm_cids[] = { | ||
1372 | { MANU_ACORN, PROD_ACORN_MFM }, | ||
1373 | { 0xffff, 0xffff }, | ||
1374 | }; | ||
1375 | |||
1376 | static struct ecard_driver mfm_driver = { | ||
1377 | .probe = mfm_probe, | ||
1378 | .remove = __devexit(mfm_remove), | ||
1379 | .id_table = mfm_cids, | ||
1380 | .drv = { | ||
1381 | .name = "mfm", | ||
1382 | }, | ||
1383 | }; | ||
1384 | |||
1385 | /* | ||
1386 | * Look for a MFM controller - first check the motherboard, then the podules | ||
1387 | * The podules have an extra interrupt enable that needs to be played with | ||
1388 | * | ||
1389 | * The HDC is accessed at MEDIUM IOC speeds. | ||
1390 | */ | ||
1391 | static int __init mfm_init (void) | ||
1392 | { | ||
1393 | unsigned char irqmask; | ||
1394 | |||
1395 | if (mfm_probecontroller(ONBOARD_MFM_ADDRESS)) { | ||
1396 | mfm_addr = ONBOARD_MFM_ADDRESS; | ||
1397 | mfm_IRQPollLoc = IOC_IRQSTATB; | ||
1398 | mfm_irqenable = 0; | ||
1399 | mfm_irq = IRQ_HARDDISK; | ||
1400 | return mfm_do_init(0x08); /* IL3 pin */ | ||
1401 | } else { | ||
1402 | return ecard_register_driver(&mfm_driver); | ||
1403 | } | ||
1404 | } | ||
1405 | |||
1406 | static void __exit mfm_exit(void) | ||
1407 | { | ||
1408 | if (mfm_addr == ONBOARD_MFM_ADDRESS) | ||
1409 | mfm_do_exit(); | ||
1410 | else | ||
1411 | ecard_unregister_driver(&mfm_driver); | ||
1412 | } | ||
1413 | |||
1414 | module_init(mfm_init) | ||
1415 | module_exit(mfm_exit) | ||
1416 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/acorn/char/Makefile b/drivers/acorn/char/Makefile new file mode 100644 index 00000000000..2fa9a8bf48a --- /dev/null +++ b/drivers/acorn/char/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | # | ||
2 | # Makefile for the acorn character device drivers. | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_ARCH_ACORN) += i2c.o pcf8583.o | ||
6 | obj-$(CONFIG_L7200_KEYB) += defkeymap-l7200.o keyb_l7200.o | ||
diff --git a/drivers/acorn/char/defkeymap-l7200.c b/drivers/acorn/char/defkeymap-l7200.c new file mode 100644 index 00000000000..9e18ce742e3 --- /dev/null +++ b/drivers/acorn/char/defkeymap-l7200.c | |||
@@ -0,0 +1,386 @@ | |||
1 | /* | ||
2 | * linux/drivers/acorn/char/defkeymap-l7200.c | ||
3 | * | ||
4 | * Default keyboard maps for LinkUp Systems L7200 board | ||
5 | * | ||
6 | * Copyright (C) 2000 Steve Hill (sjhill@cotw.com) | ||
7 | * | ||
8 | * Changelog: | ||
9 | * 08-04-2000 SJH Created file | ||
10 | */ | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | #include <linux/keyboard.h> | ||
14 | #include <linux/kd.h> | ||
15 | |||
16 | /* Normal (maps 1:1 with no processing) */ | ||
17 | #define KTn 0xF0 | ||
18 | /* Function keys */ | ||
19 | #define KTf 0xF1 | ||
20 | /* Special (Performs special house-keeping funcs) */ | ||
21 | #define KTs 0xF2 | ||
22 | #define KIGNORE K(KTs, 0) /* Ignore */ | ||
23 | #define KENTER K(KTs, 1) /* Enter */ | ||
24 | #define KREGS K(KTs, 2) /* Regs */ | ||
25 | #define KMEM K(KTs, 3) /* Mem */ | ||
26 | #define KSTAT K(KTs, 4) /* State */ | ||
27 | #define KINTR K(KTs, 5) /* Intr */ | ||
28 | #define Ksl 6 /* Last console */ | ||
29 | #define KCAPSLK K(KTs, 7) /* Caps lock */ | ||
30 | #define KNUMLK K(KTs, 8) /* Num-lock */ | ||
31 | #define KSCRLLK K(KTs, 9) /* Scroll-lock */ | ||
32 | #define KSCRLFOR K(KTs,10) /* Scroll forward */ | ||
33 | #define KSCRLBAK K(KTs,11) /* Scroll back */ | ||
34 | #define KREBOOT K(KTs,12) /* Reboot */ | ||
35 | #define KCAPSON K(KTs,13) /* Caps on */ | ||
36 | #define KCOMPOSE K(KTs,14) /* Compose */ | ||
37 | #define KSAK K(KTs,15) /* SAK */ | ||
38 | #define CONS_DEC K(KTs,16) /* Dec console */ | ||
39 | #define CONS_INC K(KTs,17) /* Incr console */ | ||
40 | #define KFLOPPY K(KTs,18) /* Floppy */ | ||
41 | /* Key pad (0-9 = digits, 10=+, 11=-, 12=*, 13=/, 14=enter, 16=., 17=# */ | ||
42 | #define KTp 0xF3 | ||
43 | #define KPAD_0 K(KTp, 0 ) | ||
44 | #define KPAD_1 K(KTp, 1 ) | ||
45 | #define KPAD_2 K(KTp, 2 ) | ||
46 | #define KPAD_3 K(KTp, 3 ) | ||
47 | #define KPAD_4 K(KTp, 4 ) | ||
48 | #define KPAD_5 K(KTp, 5 ) | ||
49 | #define KPAD_6 K(KTp, 6 ) | ||
50 | #define KPAD_7 K(KTp, 7 ) | ||
51 | #define KPAD_8 K(KTp, 8 ) | ||
52 | #define KPAD_9 K(KTp, 9 ) | ||
53 | #define KPAD_PL K(KTp,10 ) | ||
54 | #define KPAD_MI K(KTp,11 ) | ||
55 | #define KPAD_ML K(KTp,12 ) | ||
56 | #define KPAD_DV K(KTp,13 ) | ||
57 | #define KPAD_EN K(KTp,14 ) | ||
58 | #define KPAD_DT K(KTp,16 ) | ||
59 | #define KPAD_HS K(KTp,20 ) | ||
60 | /* Console switching */ | ||
61 | #define KCn 0xF5 | ||
62 | /* Cursor */ | ||
63 | #define KTc 0xF6 | ||
64 | #define Kcd 0 /* Cursor down */ | ||
65 | #define Kcl 1 /* Cursor left */ | ||
66 | #define Kcr 2 /* Cursor right */ | ||
67 | #define Kcu 3 /* Cursor up */ | ||
68 | /* Shift/alt modifiers etc */ | ||
69 | #define KMd 0xF7 | ||
70 | #define KSHIFT K(KMd, 0 ) | ||
71 | #define KALTGR K(KMd, 1 ) | ||
72 | #define KCTRL K(KMd, 2 ) | ||
73 | #define KALT K(KMd, 3 ) | ||
74 | /* Meta */ | ||
75 | #define KMt 0xF8 | ||
76 | #define KAs 0xF9 | ||
77 | #define KPADA_0 K(KAs, 0 ) | ||
78 | #define KPADA_1 K(KAs, 1 ) | ||
79 | #define KPADA_2 K(KAs, 2 ) | ||
80 | #define KPADA_3 K(KAs, 3 ) | ||
81 | #define KPADA_4 K(KAs, 4 ) | ||
82 | #define KPADA_5 K(KAs, 5 ) | ||
83 | #define KPADA_6 K(KAs, 6 ) | ||
84 | #define KPADA_7 K(KAs, 7 ) | ||
85 | #define KPADA_8 K(KAs, 8 ) | ||
86 | #define KPADA_9 K(KAs, 9 ) | ||
87 | #define KPADB_0 K(KAs,10 ) | ||
88 | #define KPADB_1 K(KAs,11 ) | ||
89 | #define KPADB_2 K(KAs,12 ) | ||
90 | #define KPADB_3 K(KAs,13 ) | ||
91 | #define KPADB_4 K(KAs,14 ) | ||
92 | #define KPADB_5 K(KAs,15 ) | ||
93 | #define KPADB_6 K(KAs,16 ) | ||
94 | #define KPADB_7 K(KAs,17 ) | ||
95 | #define KPADB_8 K(KAs,18 ) | ||
96 | #define KPADB_9 K(KAs,19 ) | ||
97 | /* Locking keys */ | ||
98 | #define KLk 0xFA | ||
99 | /* Letters */ | ||
100 | #define KTl 0xFB | ||
101 | |||
102 | /* | ||
103 | * Here is the layout of the keys for the Fujitsu QWERTY | ||
104 | * style keyboard: | ||
105 | * | ||
106 | * static char Fujitsu_Key_Table[] = | ||
107 | * { | ||
108 | * KALT, '`' , KNUL, KCTL, KFUN, KESC, '1' , '2' , | ||
109 | * '9' , '0' , '-' , '=' , KNUL, KBSP, KNUL, KNUL, | ||
110 | * KNUL, KBSL, KSHF, KNUL, KNUL, KDEL, KNUL, 't' , | ||
111 | * 'y' , 'u' , 'i' , KRET, KSHF, KPGD, KNUL, KNUL, | ||
112 | * KNUL, KTAB, KNUL, KNUL, KNUL, 'q' , 'w' , 'e' , | ||
113 | * 'r' , 'o' , 'p' , '[' , KNUL, ']' , KNUL, KNUL, | ||
114 | * KNUL, 'z' , KNUL, KNUL, KNUL, KSHL, KNUL, KNUL, | ||
115 | * 'k' , 'l' , ';' , KSQT, KNUL, KPGU, KNUL, KNUL, | ||
116 | * KNUL, 'a' , KNUL, KNUL, KNUL, 's' , 'd' , 'f' , | ||
117 | * 'g' , 'h' , 'j' , '/' , KNUL, KHME, KNUL, KNUL, | ||
118 | * KNUL, 'x' , KNUL, KNUL, KNUL, 'c' , 'v' , 'b' , | ||
119 | * 'n' , 'm' , ',' , '.' , KNUL, ' ' , KNUL, KNUL, | ||
120 | * KNUL, KNUL, KNUL, KNUL, KNUL, '3' , '4' , '5' , | ||
121 | * '6' , '7' , '8' , KNUL, KPRG, KNUL, KEND, KNUL, | ||
122 | * }; | ||
123 | */ | ||
124 | |||
125 | u_short plain_map[NR_KEYS]= | ||
126 | { | ||
127 | 0xf703, 0xf060, 0xf200, 0xf702, 0xf200, 0xf01b, 0xf031, 0xf032, | ||
128 | 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf200, 0xf07f, 0xf200, 0xf200, | ||
129 | 0xf200, 0xf05c, 0xf700, 0xf200, 0xf200, 0xf116, 0xf000, 0xfb74, | ||
130 | 0xfb79, 0xfb75, 0xfb69, 0xf201, 0xf700, 0xf600, 0xf200, 0xf200, | ||
131 | 0xf200, 0xf009, 0xf200, 0xf200, 0xf200, 0xfb71, 0xfb77, 0xfb65, | ||
132 | 0xfb72, 0xfb6f, 0xfb70, 0xf05b, 0xf200, 0xf05d, 0xf200, 0xf200, | ||
133 | 0xf200, 0xfb7a, 0xf200, 0xf200, 0xf200, 0xf207, 0xf200, 0xf200, | ||
134 | 0xfb6b, 0xfb6c, 0xf03b, 0xf027, 0xf200, 0xf603, 0xf200, 0xf200, | ||
135 | 0xf200, 0xfb61, 0xf200, 0xf200, 0xf200, 0xfb73, 0xfb64, 0xfb66, | ||
136 | 0xfb67, 0xfb68, 0xfb6a, 0xf02f, 0xf200, 0xf601, 0xf200, 0xf200, | ||
137 | 0xf200, 0xfb78, 0xf200, 0xf200, 0xf200, 0xfb63, 0xfb76, 0xfb62, | ||
138 | 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf200, 0xf020, 0xf200, 0xf200, | ||
139 | 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf033, 0xf034, 0xf035, | ||
140 | 0xf036, 0xf037, 0xf038, 0xf200, 0xf200, 0xf200, 0xf602, 0xf200, | ||
141 | 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, | ||
142 | 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, | ||
143 | }; | ||
144 | |||
145 | u_short shift_map[NR_KEYS]= | ||
146 | { | ||
147 | 0xf703, 0xf07e, 0xf200, 0xf702, 0xf200, 0xf01b, 0xf021, 0xf040, | ||
148 | 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf200, 0xf07f, 0xf200, 0xf200, | ||
149 | 0xf200, 0xf07c, 0xf700, 0xf200, 0xf200, 0xf116, 0xf000, 0xfb54, | ||
150 | 0xfb59, 0xfb55, 0xfb49, 0xf201, 0xf700, 0xf600, 0xf200, 0xf200, | ||
151 | 0xf200, 0xf009, 0xf200, 0xf200, 0xf200, 0xfb51, 0xfb57, 0xfb45, | ||
152 | 0xfb52, 0xfb4f, 0xfb50, 0xf07b, 0xf200, 0xf07d, 0xf200, 0xf200, | ||
153 | 0xf200, 0xfb5a, 0xf200, 0xf200, 0xf200, 0xf207, 0xf200, 0xf200, | ||
154 | 0xfb4b, 0xfb4c, 0xf03a, 0xf022, 0xf200, 0xf603, 0xf200, 0xf200, | ||
155 | 0xf200, 0xfb41, 0xf200, 0xf200, 0xf200, 0xfb53, 0xfb44, 0xfb46, | ||
156 | 0xfb47, 0xfb48, 0xfb4a, 0xf03f, 0xf200, 0xf601, 0xf200, 0xf200, | ||
157 | 0xf200, 0xfb58, 0xf200, 0xf200, 0xf200, 0xfb43, 0xfb56, 0xfb42, | ||
158 | 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf200, 0xf020, 0xf200, 0xf200, | ||
159 | 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf023, 0xf024, 0xf025, | ||
160 | 0xf05e, 0xf026, 0xf02a, 0xf200, 0xf200, 0xf200, 0xf602, 0xf200, | ||
161 | 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, | ||
162 | 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, | ||
163 | }; | ||
164 | |||
165 | u_short altgr_map[NR_KEYS]= | ||
166 | { | ||
167 | KIGNORE ,K(KCn,12 ),K(KCn,13 ),K(KCn,14 ),K(KCn,15 ),K(KCn,16 ),K(KCn,17 ),K(KCn, 18), | ||
168 | K(KCn, 19),K(KCn,20 ),K(KCn,21 ),K(KCn,22 ),K(KCn,23 ),KIGNORE ,KREGS ,KINTR , | ||
169 | KIGNORE ,KIGNORE ,K(KTn,'@'),KIGNORE ,K(KTn,'$'),KIGNORE ,KIGNORE ,K(KTn,'{'), | ||
170 | K(KTn,'['),K(KTn,']'),K(KTn,'}'),K(KTn,'\\'),KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,21 ), | ||
171 | K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KTl,'q'), | ||
172 | K(KTl,'w'),K(KTl,'e'),K(KTl,'r'),K(KTl,'t' ),K(KTl,'y'),K(KTl,'u'),K(KTl,'i' ),K(KTl,'o'), | ||
173 | K(KTl,'p'),KIGNORE ,K(KTn,'~'),KIGNORE ,K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPADB_7 , | ||
174 | KPADB_8 ,KPADB_9 ,KPAD_MI ,KCTRL ,K(KAs,20 ),K(KTl,'s'),K(KAs,23 ),K(KAs,25 ), | ||
175 | K(KTl,'g'),K(KTl,'h'),K(KTl,'j'),K(KTl,'k' ),K(KTl,'l'),KIGNORE ,KIGNORE ,KENTER , | ||
176 | KPADB_4 ,KPADB_5 ,KPADB_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTl,'z' ),K(KTl,'x'), | ||
177 | K(KAs,22 ),K(KTl,'v'),K(KTl,21 ),K(KTl,'n' ),K(KTl,'m'),KIGNORE ,KIGNORE ,KIGNORE , | ||
178 | KSHIFT ,K(KTc,Kcu),KPADB_1 ,KPADB_2 ,KPADB_3 ,KCAPSLK ,KALT ,KIGNORE , | ||
179 | KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPADB_0 ,KPAD_DT ,KPAD_EN , | ||
180 | KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , | ||
181 | KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , | ||
182 | KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , | ||
183 | }; | ||
184 | |||
185 | u_short ctrl_map[NR_KEYS]= | ||
186 | { | ||
187 | 0xf703, 0xf200, 0xf200, 0xf702, 0xf200, 0xf200, 0xf001, 0xf002, | ||
188 | 0xf009, 0xf000, 0xf031, 0xf200, 0xf200, 0xf07f, 0xf200, 0xf200, | ||
189 | 0xf200, 0xf01c, 0xf700, 0xf200, 0xf200, 0xf116, 0xf000, 0xf020, | ||
190 | 0xf019, 0xf015, 0xf009, 0xf201, 0xf700, 0xf600, 0xf200, 0xf200, | ||
191 | 0xf200, 0xf009, 0xf200, 0xf200, 0xf200, 0xf011, 0xf017, 0xf005, | ||
192 | 0xf012, 0xf00f, 0xf010, 0xf01b, 0xf200, 0xf01d, 0xf200, 0xf200, | ||
193 | 0xf200, 0xf01a, 0xf200, 0xf200, 0xf200, 0xf207, 0xf200, 0xf200, | ||
194 | 0xf00b, 0xf00c, 0xf200, 0xf007, 0xf200, 0xf603, 0xf200, 0xf200, | ||
195 | 0xf200, 0xf001, 0xf200, 0xf200, 0xf200, 0xf001, 0xf013, 0xf006, | ||
196 | 0xf007, 0xf008, 0xf00a, 0xf07f, 0xf200, 0xf601, 0xf200, 0xf200, | ||
197 | 0xf200, 0xf018, 0xf200, 0xf200, 0xf200, 0xf003, 0xf016, 0xf002, | ||
198 | 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf200, 0xf000, 0xf200, 0xf200, | ||
199 | 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf01b, 0xf01c, 0xf01d, | ||
200 | 0xf036, 0xf037, 0xf038, 0xf200, 0xf200, 0xf200, 0xf602, 0xf200, | ||
201 | 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, | ||
202 | 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf602, 0xf200, | ||
203 | }; | ||
204 | |||
205 | u_short shift_ctrl_map[NR_KEYS]= | ||
206 | { | ||
207 | KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , | ||
208 | KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KFLOPPY ,KINTR , | ||
209 | KIGNORE ,KIGNORE ,K(KTn, 0 ),KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , | ||
210 | KIGNORE ,KIGNORE ,KIGNORE ,K(KTn,31 ),KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,21 ), | ||
211 | K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KTn,17 ), | ||
212 | K(KTn,23 ),K(KTn, 5 ),K(KTn,18 ),K(KTn,20 ),K(KTn,25 ),K(KTn,21 ),K(KTn, 9 ),K(KTn,15 ), | ||
213 | K(KTn,16 ),KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPAD_7 , | ||
214 | KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTn, 1 ),K(KTn,19 ),K(KTn, 4 ),K(KTn, 6 ), | ||
215 | K(KTn, 7 ),K(KTn, 8 ),K(KTn,10 ),K(KTn,11 ),K(KTn,12 ),KIGNORE ,K(KTn, 7 ),KENTER , | ||
216 | KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTn,26 ),K(KTn,24 ), | ||
217 | K(KTn, 3 ),K(KTn,22 ),K(KTn, 2 ),K(KTn,14 ),K(KTn,13 ),KIGNORE ,KIGNORE ,KIGNORE , | ||
218 | KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn, 0 ), | ||
219 | KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN , | ||
220 | KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , | ||
221 | KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , | ||
222 | KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , | ||
223 | }; | ||
224 | |||
225 | u_short alt_map[NR_KEYS]= | ||
226 | { | ||
227 | K(KMt,27 ),K(KCn, 0 ),K(KCn, 1 ),K(KCn, 2 ),K(KCn, 3 ),K(KCn, 4 ),K(KCn, 5 ),K(KCn, 6 ), | ||
228 | K(KCn, 7 ),K(KCn, 8 ),K(KCn, 9 ),K(KCn,10 ),K(KCn,11 ),KIGNORE ,KSCRLLK ,KINTR , | ||
229 | K(KMt,'`'),K(KMt,'1'),K(KMt,'2'),K(KMt,'3' ),K(KMt,'4'),K(KMt,'5'),K(KMt,'6' ),K(KMt,'7'), | ||
230 | K(KMt,'8'),K(KMt,'9'),K(KMt,'0'),K(KMt,'-' ),K(KMt,'='),K(KMt,'£'),K(KMt,127 ),K(KTf,21 ), | ||
231 | K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,K(KMt, 9 ),K(KMt,'q'), | ||
232 | K(KMt,'w'),K(KMt,'e'),K(KMt,'r'),K(KMt,'t' ),K(KMt,'y'),K(KMt,'u'),K(KMt,'i' ),K(KMt,'o'), | ||
233 | K(KMt,'p'),K(KMt,'['),K(KMt,']'),K(KMt,'\\'),K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPADA_7 , | ||
234 | KPADA_8 ,KPADA_9 ,KPAD_MI ,KCTRL ,K(KMt,'a'),K(KMt,'s'),K(KMt,'d' ),K(KMt,'f'), | ||
235 | K(KMt,'g'),K(KMt,'h'),K(KMt,'j'),K(KMt,'k' ),K(KMt,'l'),K(KMt,';'),K(KMt,'\''),K(KMt,13 ), | ||
236 | KPADA_4 ,KPADA_5 ,KPADA_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KMt,'z' ),K(KMt,'x'), | ||
237 | K(KMt,'c'),K(KMt,'v'),K(KMt,'b'),K(KMt,'n' ),K(KMt,'m'),K(KMt,','),K(KMt,'.' ),KIGNORE , | ||
238 | KSHIFT ,K(KTc,Kcu),KPADA_1 ,KPADA_2 ,KPADA_3 ,KCAPSLK ,KALT ,K(KMt,' '), | ||
239 | KALTGR ,KCTRL ,CONS_DEC ,K(KTc,Kcd ),CONS_INC ,KPADA_0 ,KPAD_DT ,KPAD_EN , | ||
240 | KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , | ||
241 | KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , | ||
242 | KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , | ||
243 | }; | ||
244 | |||
245 | u_short ctrl_alt_map[NR_KEYS]= | ||
246 | { | ||
247 | KIGNORE ,K(KCn, 0 ),K(KCn, 1 ),K(KCn, 2 ),K(KCn, 3 ),K(KCn, 4 ),K(KCn, 5 ),K(KCn, 6 ), | ||
248 | K(KCn, 7 ),K(KCn, 8 ),K(KCn, 9 ),K(KCn,10 ),K(KCn,11 ),KIGNORE ,KIGNORE ,KINTR , | ||
249 | KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , | ||
250 | KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,21 ), | ||
251 | K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KMt,17 ), | ||
252 | K(KMt,23 ),K(KMt, 5 ),K(KMt,18 ),K(KMt,20 ),K(KMt,25 ),K(KMt,21 ),K(KMt, 9 ),K(KMt,15 ), | ||
253 | K(KMt,16 ),KIGNORE ,KIGNORE ,KIGNORE ,KREBOOT ,K(KTf,23 ),K(KTf,25 ),KPAD_7 , | ||
254 | KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KMt, 1 ),K(KMt,19 ),K(KMt, 4 ),K(KMt, 6 ), | ||
255 | K(KMt, 7 ),K(KMt, 8 ),K(KMt,10 ),K(KMt,11 ),K(KMt,12 ),KIGNORE ,KIGNORE ,KENTER , | ||
256 | KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KMt,26 ),K(KMt,24 ), | ||
257 | K(KMt, 3 ),K(KMt,22 ),K(KMt, 2 ),K(KMt,14 ),K(KMt,13 ),KIGNORE ,KIGNORE ,KIGNORE , | ||
258 | KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,KIGNORE , | ||
259 | KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KREBOOT ,KPAD_EN , | ||
260 | KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , | ||
261 | KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , | ||
262 | KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , | ||
263 | }; | ||
264 | |||
265 | ushort *key_maps[MAX_NR_KEYMAPS] = { | ||
266 | plain_map, shift_map, altgr_map, 0, | ||
267 | ctrl_map, shift_ctrl_map, 0, 0, | ||
268 | alt_map, 0, 0, 0, | ||
269 | ctrl_alt_map, 0 | ||
270 | }; | ||
271 | |||
272 | unsigned int keymap_count = 7; | ||
273 | |||
274 | /* | ||
275 | * Philosophy: most people do not define more strings, but they who do | ||
276 | * often want quite a lot of string space. So, we statically allocate | ||
277 | * the default and allocate dynamically in chunks of 512 bytes. | ||
278 | */ | ||
279 | |||
280 | char func_buf[] = { | ||
281 | '\033', '[', '[', 'A', 0, | ||
282 | '\033', '[', '[', 'B', 0, | ||
283 | '\033', '[', '[', 'C', 0, | ||
284 | '\033', '[', '[', 'D', 0, | ||
285 | '\033', '[', '[', 'E', 0, | ||
286 | '\033', '[', '1', '7', '~', 0, | ||
287 | '\033', '[', '1', '8', '~', 0, | ||
288 | '\033', '[', '1', '9', '~', 0, | ||
289 | '\033', '[', '2', '0', '~', 0, | ||
290 | '\033', '[', '2', '1', '~', 0, | ||
291 | '\033', '[', '2', '3', '~', 0, | ||
292 | '\033', '[', '2', '4', '~', 0, | ||
293 | '\033', '[', '2', '5', '~', 0, | ||
294 | '\033', '[', '2', '6', '~', 0, | ||
295 | '\033', '[', '2', '8', '~', 0, | ||
296 | '\033', '[', '2', '9', '~', 0, | ||
297 | '\033', '[', '3', '1', '~', 0, | ||
298 | '\033', '[', '3', '2', '~', 0, | ||
299 | '\033', '[', '3', '3', '~', 0, | ||
300 | '\033', '[', '3', '4', '~', 0, | ||
301 | '\033', '[', '1', '~', 0, | ||
302 | '\033', '[', '2', '~', 0, | ||
303 | '\033', '[', '3', '~', 0, | ||
304 | '\033', '[', '4', '~', 0, | ||
305 | '\033', '[', '5', '~', 0, | ||
306 | '\033', '[', '6', '~', 0, | ||
307 | '\033', '[', 'M', 0, | ||
308 | '\033', '[', 'P', 0, | ||
309 | }; | ||
310 | |||
311 | char *funcbufptr = func_buf; | ||
312 | int funcbufsize = sizeof(func_buf); | ||
313 | int funcbufleft = 0; /* space left */ | ||
314 | |||
315 | char *func_table[MAX_NR_FUNC] = { | ||
316 | func_buf + 0, | ||
317 | func_buf + 5, | ||
318 | func_buf + 10, | ||
319 | func_buf + 15, | ||
320 | func_buf + 20, | ||
321 | func_buf + 25, | ||
322 | func_buf + 31, | ||
323 | func_buf + 37, | ||
324 | func_buf + 43, | ||
325 | func_buf + 49, | ||
326 | func_buf + 55, | ||
327 | func_buf + 61, | ||
328 | func_buf + 67, | ||
329 | func_buf + 73, | ||
330 | func_buf + 79, | ||
331 | func_buf + 85, | ||
332 | func_buf + 91, | ||
333 | func_buf + 97, | ||
334 | func_buf + 103, | ||
335 | func_buf + 109, | ||
336 | func_buf + 115, | ||
337 | func_buf + 120, | ||
338 | func_buf + 125, | ||
339 | func_buf + 130, | ||
340 | func_buf + 135, | ||
341 | func_buf + 140, | ||
342 | func_buf + 145, | ||
343 | 0, | ||
344 | 0, | ||
345 | func_buf + 149, | ||
346 | 0, | ||
347 | }; | ||
348 | |||
349 | struct kbdiacr accent_table[MAX_DIACR] = { | ||
350 | {'`', 'A', '\300'}, {'`', 'a', '\340'}, | ||
351 | {'\'', 'A', '\301'}, {'\'', 'a', '\341'}, | ||
352 | {'^', 'A', '\302'}, {'^', 'a', '\342'}, | ||
353 | {'~', 'A', '\303'}, {'~', 'a', '\343'}, | ||
354 | {'"', 'A', '\304'}, {'"', 'a', '\344'}, | ||
355 | {'O', 'A', '\305'}, {'o', 'a', '\345'}, | ||
356 | {'0', 'A', '\305'}, {'0', 'a', '\345'}, | ||
357 | {'A', 'A', '\305'}, {'a', 'a', '\345'}, | ||
358 | {'A', 'E', '\306'}, {'a', 'e', '\346'}, | ||
359 | {',', 'C', '\307'}, {',', 'c', '\347'}, | ||
360 | {'`', 'E', '\310'}, {'`', 'e', '\350'}, | ||
361 | {'\'', 'E', '\311'}, {'\'', 'e', '\351'}, | ||
362 | {'^', 'E', '\312'}, {'^', 'e', '\352'}, | ||
363 | {'"', 'E', '\313'}, {'"', 'e', '\353'}, | ||
364 | {'`', 'I', '\314'}, {'`', 'i', '\354'}, | ||
365 | {'\'', 'I', '\315'}, {'\'', 'i', '\355'}, | ||
366 | {'^', 'I', '\316'}, {'^', 'i', '\356'}, | ||
367 | {'"', 'I', '\317'}, {'"', 'i', '\357'}, | ||
368 | {'-', 'D', '\320'}, {'-', 'd', '\360'}, | ||
369 | {'~', 'N', '\321'}, {'~', 'n', '\361'}, | ||
370 | {'`', 'O', '\322'}, {'`', 'o', '\362'}, | ||
371 | {'\'', 'O', '\323'}, {'\'', 'o', '\363'}, | ||
372 | {'^', 'O', '\324'}, {'^', 'o', '\364'}, | ||
373 | {'~', 'O', '\325'}, {'~', 'o', '\365'}, | ||
374 | {'"', 'O', '\326'}, {'"', 'o', '\366'}, | ||
375 | {'/', 'O', '\330'}, {'/', 'o', '\370'}, | ||
376 | {'`', 'U', '\331'}, {'`', 'u', '\371'}, | ||
377 | {'\'', 'U', '\332'}, {'\'', 'u', '\372'}, | ||
378 | {'^', 'U', '\333'}, {'^', 'u', '\373'}, | ||
379 | {'"', 'U', '\334'}, {'"', 'u', '\374'}, | ||
380 | {'\'', 'Y', '\335'}, {'\'', 'y', '\375'}, | ||
381 | {'T', 'H', '\336'}, {'t', 'h', '\376'}, | ||
382 | {'s', 's', '\337'}, {'"', 'y', '\377'}, | ||
383 | {'s', 'z', '\337'}, {'i', 'j', '\377'}, | ||
384 | }; | ||
385 | |||
386 | unsigned int accent_table_size = 68; | ||
diff --git a/drivers/acorn/char/i2c.c b/drivers/acorn/char/i2c.c new file mode 100644 index 00000000000..c22bb9dca1e --- /dev/null +++ b/drivers/acorn/char/i2c.c | |||
@@ -0,0 +1,369 @@ | |||
1 | /* | ||
2 | * linux/drivers/acorn/char/i2c.c | ||
3 | * | ||
4 | * Copyright (C) 2000 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * ARM IOC/IOMD i2c driver. | ||
11 | * | ||
12 | * On Acorn machines, the following i2c devices are on the bus: | ||
13 | * - PCF8583 real time clock & static RAM | ||
14 | */ | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/time.h> | ||
18 | #include <linux/miscdevice.h> | ||
19 | #include <linux/rtc.h> | ||
20 | #include <linux/i2c.h> | ||
21 | #include <linux/i2c-algo-bit.h> | ||
22 | #include <linux/fs.h> | ||
23 | |||
24 | #include <asm/hardware.h> | ||
25 | #include <asm/io.h> | ||
26 | #include <asm/hardware/ioc.h> | ||
27 | #include <asm/system.h> | ||
28 | #include <asm/uaccess.h> | ||
29 | |||
30 | #include "pcf8583.h" | ||
31 | |||
32 | extern int (*set_rtc)(void); | ||
33 | |||
34 | static struct i2c_client *rtc_client; | ||
35 | static const unsigned char days_in_mon[] = | ||
36 | { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | ||
37 | |||
38 | #define CMOS_CHECKSUM (63) | ||
39 | |||
40 | /* | ||
41 | * Acorn machines store the year in the static RAM at | ||
42 | * location 128. | ||
43 | */ | ||
44 | #define CMOS_YEAR (64 + 128) | ||
45 | |||
46 | static inline int rtc_command(int cmd, void *data) | ||
47 | { | ||
48 | int ret = -EIO; | ||
49 | |||
50 | if (rtc_client) | ||
51 | ret = rtc_client->driver->command(rtc_client, cmd, data); | ||
52 | |||
53 | return ret; | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * Update the century + year bytes in the CMOS RAM, ensuring | ||
58 | * that the check byte is correctly adjusted for the change. | ||
59 | */ | ||
60 | static int rtc_update_year(unsigned int new_year) | ||
61 | { | ||
62 | unsigned char yr[2], chk; | ||
63 | struct mem cmos_year = { CMOS_YEAR, sizeof(yr), yr }; | ||
64 | struct mem cmos_check = { CMOS_CHECKSUM, 1, &chk }; | ||
65 | int ret; | ||
66 | |||
67 | ret = rtc_command(MEM_READ, &cmos_check); | ||
68 | if (ret) | ||
69 | goto out; | ||
70 | ret = rtc_command(MEM_READ, &cmos_year); | ||
71 | if (ret) | ||
72 | goto out; | ||
73 | |||
74 | chk -= yr[1] + yr[0]; | ||
75 | |||
76 | yr[1] = new_year / 100; | ||
77 | yr[0] = new_year % 100; | ||
78 | |||
79 | chk += yr[1] + yr[0]; | ||
80 | |||
81 | ret = rtc_command(MEM_WRITE, &cmos_year); | ||
82 | if (ret == 0) | ||
83 | ret = rtc_command(MEM_WRITE, &cmos_check); | ||
84 | out: | ||
85 | return ret; | ||
86 | } | ||
87 | |||
88 | /* | ||
89 | * Read the current RTC time and date, and update xtime. | ||
90 | */ | ||
91 | static void get_rtc_time(struct rtc_tm *rtctm, unsigned int *year) | ||
92 | { | ||
93 | unsigned char ctrl, yr[2]; | ||
94 | struct mem rtcmem = { CMOS_YEAR, sizeof(yr), yr }; | ||
95 | int real_year, year_offset; | ||
96 | |||
97 | /* | ||
98 | * Ensure that the RTC is running. | ||
99 | */ | ||
100 | rtc_command(RTC_GETCTRL, &ctrl); | ||
101 | if (ctrl & 0xc0) { | ||
102 | unsigned char new_ctrl = ctrl & ~0xc0; | ||
103 | |||
104 | printk(KERN_WARNING "RTC: resetting control %02x -> %02x\n", | ||
105 | ctrl, new_ctrl); | ||
106 | |||
107 | rtc_command(RTC_SETCTRL, &new_ctrl); | ||
108 | } | ||
109 | |||
110 | if (rtc_command(RTC_GETDATETIME, rtctm) || | ||
111 | rtc_command(MEM_READ, &rtcmem)) | ||
112 | return; | ||
113 | |||
114 | real_year = yr[0]; | ||
115 | |||
116 | /* | ||
117 | * The RTC year holds the LSB two bits of the current | ||
118 | * year, which should reflect the LSB two bits of the | ||
119 | * CMOS copy of the year. Any difference indicates | ||
120 | * that we have to correct the CMOS version. | ||
121 | */ | ||
122 | year_offset = rtctm->year_off - (real_year & 3); | ||
123 | if (year_offset < 0) | ||
124 | /* | ||
125 | * RTC year wrapped. Adjust it appropriately. | ||
126 | */ | ||
127 | year_offset += 4; | ||
128 | |||
129 | *year = real_year + year_offset + yr[1] * 100; | ||
130 | } | ||
131 | |||
132 | static int set_rtc_time(struct rtc_tm *rtctm, unsigned int year) | ||
133 | { | ||
134 | unsigned char leap; | ||
135 | int ret; | ||
136 | |||
137 | leap = (!(year % 4) && (year % 100)) || !(year % 400); | ||
138 | |||
139 | if (rtctm->mon > 12 || rtctm->mon == 0 || rtctm->mday == 0) | ||
140 | return -EINVAL; | ||
141 | |||
142 | if (rtctm->mday > (days_in_mon[rtctm->mon] + (rtctm->mon == 2 && leap))) | ||
143 | return -EINVAL; | ||
144 | |||
145 | if (rtctm->hours >= 24 || rtctm->mins >= 60 || rtctm->secs >= 60) | ||
146 | return -EINVAL; | ||
147 | |||
148 | /* | ||
149 | * The RTC's own 2-bit year must reflect the least | ||
150 | * significant two bits of the CMOS year. | ||
151 | */ | ||
152 | rtctm->year_off = (year % 100) & 3; | ||
153 | |||
154 | ret = rtc_command(RTC_SETDATETIME, rtctm); | ||
155 | if (ret == 0) | ||
156 | ret = rtc_update_year(year); | ||
157 | |||
158 | return ret; | ||
159 | } | ||
160 | |||
161 | /* | ||
162 | * Set the RTC time only. Note that | ||
163 | * we do not touch the date. | ||
164 | */ | ||
165 | static int k_set_rtc_time(void) | ||
166 | { | ||
167 | struct rtc_tm new_rtctm, old_rtctm; | ||
168 | unsigned long nowtime = xtime.tv_sec; | ||
169 | |||
170 | if (rtc_command(RTC_GETDATETIME, &old_rtctm)) | ||
171 | return 0; | ||
172 | |||
173 | new_rtctm.cs = xtime.tv_nsec / 10000000; | ||
174 | new_rtctm.secs = nowtime % 60; nowtime /= 60; | ||
175 | new_rtctm.mins = nowtime % 60; nowtime /= 60; | ||
176 | new_rtctm.hours = nowtime % 24; | ||
177 | |||
178 | /* | ||
179 | * avoid writing when we're going to change the day | ||
180 | * of the month. We will retry in the next minute. | ||
181 | * This basically means that if the RTC must not drift | ||
182 | * by more than 1 minute in 11 minutes. | ||
183 | * | ||
184 | * [ rtc: 1/1/2000 23:58:00, real 2/1/2000 00:01:00, | ||
185 | * rtc gets set to 1/1/2000 00:01:00 ] | ||
186 | */ | ||
187 | if ((old_rtctm.hours == 23 && old_rtctm.mins == 59) || | ||
188 | (new_rtctm.hours == 23 && new_rtctm.mins == 59)) | ||
189 | return 1; | ||
190 | |||
191 | return rtc_command(RTC_SETTIME, &new_rtctm); | ||
192 | } | ||
193 | |||
194 | static int rtc_ioctl(struct inode *inode, struct file *file, | ||
195 | unsigned int cmd, unsigned long arg) | ||
196 | { | ||
197 | unsigned int year; | ||
198 | struct rtc_time rtctm; | ||
199 | struct rtc_tm rtc_raw; | ||
200 | |||
201 | switch (cmd) { | ||
202 | case RTC_ALM_READ: | ||
203 | case RTC_ALM_SET: | ||
204 | break; | ||
205 | |||
206 | case RTC_RD_TIME: | ||
207 | memset(&rtctm, 0, sizeof(struct rtc_time)); | ||
208 | get_rtc_time(&rtc_raw, &year); | ||
209 | rtctm.tm_sec = rtc_raw.secs; | ||
210 | rtctm.tm_min = rtc_raw.mins; | ||
211 | rtctm.tm_hour = rtc_raw.hours; | ||
212 | rtctm.tm_mday = rtc_raw.mday; | ||
213 | rtctm.tm_mon = rtc_raw.mon - 1; /* month starts at 0 */ | ||
214 | rtctm.tm_year = year - 1900; /* starts at 1900 */ | ||
215 | return copy_to_user((void *)arg, &rtctm, sizeof(rtctm)) | ||
216 | ? -EFAULT : 0; | ||
217 | |||
218 | case RTC_SET_TIME: | ||
219 | if (!capable(CAP_SYS_TIME)) | ||
220 | return -EACCES; | ||
221 | |||
222 | if (copy_from_user(&rtctm, (void *)arg, sizeof(rtctm))) | ||
223 | return -EFAULT; | ||
224 | rtc_raw.secs = rtctm.tm_sec; | ||
225 | rtc_raw.mins = rtctm.tm_min; | ||
226 | rtc_raw.hours = rtctm.tm_hour; | ||
227 | rtc_raw.mday = rtctm.tm_mday; | ||
228 | rtc_raw.mon = rtctm.tm_mon + 1; | ||
229 | year = rtctm.tm_year + 1900; | ||
230 | return set_rtc_time(&rtc_raw, year); | ||
231 | break; | ||
232 | |||
233 | case RTC_EPOCH_READ: | ||
234 | return put_user(1900, (unsigned long *)arg); | ||
235 | |||
236 | } | ||
237 | return -EINVAL; | ||
238 | } | ||
239 | |||
240 | static struct file_operations rtc_fops = { | ||
241 | .ioctl = rtc_ioctl, | ||
242 | }; | ||
243 | |||
244 | static struct miscdevice rtc_dev = { | ||
245 | .minor = RTC_MINOR, | ||
246 | .name = "rtc", | ||
247 | .fops = &rtc_fops, | ||
248 | }; | ||
249 | |||
250 | /* IOC / IOMD i2c driver */ | ||
251 | |||
252 | #define FORCE_ONES 0xdc | ||
253 | #define SCL 0x02 | ||
254 | #define SDA 0x01 | ||
255 | |||
256 | /* | ||
257 | * We must preserve all non-i2c output bits in IOC_CONTROL. | ||
258 | * Note also that we need to preserve the value of SCL and | ||
259 | * SDA outputs as well (which may be different from the | ||
260 | * values read back from IOC_CONTROL). | ||
261 | */ | ||
262 | static u_int force_ones; | ||
263 | |||
264 | static void ioc_setscl(void *data, int state) | ||
265 | { | ||
266 | u_int ioc_control = ioc_readb(IOC_CONTROL) & ~(SCL | SDA); | ||
267 | u_int ones = force_ones; | ||
268 | |||
269 | if (state) | ||
270 | ones |= SCL; | ||
271 | else | ||
272 | ones &= ~SCL; | ||
273 | |||
274 | force_ones = ones; | ||
275 | |||
276 | ioc_writeb(ioc_control | ones, IOC_CONTROL); | ||
277 | } | ||
278 | |||
279 | static void ioc_setsda(void *data, int state) | ||
280 | { | ||
281 | u_int ioc_control = ioc_readb(IOC_CONTROL) & ~(SCL | SDA); | ||
282 | u_int ones = force_ones; | ||
283 | |||
284 | if (state) | ||
285 | ones |= SDA; | ||
286 | else | ||
287 | ones &= ~SDA; | ||
288 | |||
289 | force_ones = ones; | ||
290 | |||
291 | ioc_writeb(ioc_control | ones, IOC_CONTROL); | ||
292 | } | ||
293 | |||
294 | static int ioc_getscl(void *data) | ||
295 | { | ||
296 | return (ioc_readb(IOC_CONTROL) & SCL) != 0; | ||
297 | } | ||
298 | |||
299 | static int ioc_getsda(void *data) | ||
300 | { | ||
301 | return (ioc_readb(IOC_CONTROL) & SDA) != 0; | ||
302 | } | ||
303 | |||
304 | static struct i2c_algo_bit_data ioc_data = { | ||
305 | .setsda = ioc_setsda, | ||
306 | .setscl = ioc_setscl, | ||
307 | .getsda = ioc_getsda, | ||
308 | .getscl = ioc_getscl, | ||
309 | .udelay = 80, | ||
310 | .mdelay = 80, | ||
311 | .timeout = 100 | ||
312 | }; | ||
313 | |||
314 | static int ioc_client_reg(struct i2c_client *client) | ||
315 | { | ||
316 | if (client->driver->id == I2C_DRIVERID_PCF8583 && | ||
317 | client->addr == 0x50) { | ||
318 | struct rtc_tm rtctm; | ||
319 | unsigned int year; | ||
320 | struct timespec tv; | ||
321 | |||
322 | rtc_client = client; | ||
323 | get_rtc_time(&rtctm, &year); | ||
324 | |||
325 | tv.tv_nsec = rtctm.cs * 10000000; | ||
326 | tv.tv_sec = mktime(year, rtctm.mon, rtctm.mday, | ||
327 | rtctm.hours, rtctm.mins, rtctm.secs); | ||
328 | do_settimeofday(&tv); | ||
329 | set_rtc = k_set_rtc_time; | ||
330 | } | ||
331 | |||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | static int ioc_client_unreg(struct i2c_client *client) | ||
336 | { | ||
337 | if (client == rtc_client) { | ||
338 | set_rtc = NULL; | ||
339 | rtc_client = NULL; | ||
340 | } | ||
341 | |||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | static struct i2c_adapter ioc_ops = { | ||
346 | .id = I2C_HW_B_IOC, | ||
347 | .algo_data = &ioc_data, | ||
348 | .client_register = ioc_client_reg, | ||
349 | .client_unregister = ioc_client_unreg, | ||
350 | }; | ||
351 | |||
352 | static int __init i2c_ioc_init(void) | ||
353 | { | ||
354 | int ret; | ||
355 | |||
356 | force_ones = FORCE_ONES | SCL | SDA; | ||
357 | |||
358 | ret = i2c_bit_add_bus(&ioc_ops); | ||
359 | |||
360 | if (ret >= 0){ | ||
361 | ret = misc_register(&rtc_dev); | ||
362 | if(ret < 0) | ||
363 | i2c_bit_del_bus(&ioc_ops); | ||
364 | } | ||
365 | |||
366 | return ret; | ||
367 | } | ||
368 | |||
369 | __initcall(i2c_ioc_init); | ||
diff --git a/drivers/acorn/char/pcf8583.c b/drivers/acorn/char/pcf8583.c new file mode 100644 index 00000000000..ad7ae7ab892 --- /dev/null +++ b/drivers/acorn/char/pcf8583.c | |||
@@ -0,0 +1,239 @@ | |||
1 | /* | ||
2 | * linux/drivers/acorn/char/pcf8583.c | ||
3 | * | ||
4 | * Copyright (C) 2000 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * Driver for PCF8583 RTC & RAM chip | ||
11 | */ | ||
12 | #include <linux/i2c.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/string.h> | ||
15 | #include <linux/mc146818rtc.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/bcd.h> | ||
19 | |||
20 | #include "pcf8583.h" | ||
21 | |||
22 | static struct i2c_driver pcf8583_driver; | ||
23 | |||
24 | static unsigned short ignore[] = { I2C_CLIENT_END }; | ||
25 | static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; | ||
26 | |||
27 | static struct i2c_client_address_data addr_data = { | ||
28 | .normal_i2c = normal_addr, | ||
29 | .normal_i2c_range = ignore, | ||
30 | .probe = ignore, | ||
31 | .probe_range = ignore, | ||
32 | .ignore = ignore, | ||
33 | .ignore_range = ignore, | ||
34 | .force = ignore, | ||
35 | }; | ||
36 | |||
37 | #define DAT(x) ((unsigned int)(x->dev.driver_data)) | ||
38 | |||
39 | static int | ||
40 | pcf8583_attach(struct i2c_adapter *adap, int addr, int kind) | ||
41 | { | ||
42 | struct i2c_client *c; | ||
43 | unsigned char buf[1], ad[1] = { 0 }; | ||
44 | struct i2c_msg msgs[2] = { | ||
45 | { addr, 0, 1, ad }, | ||
46 | { addr, I2C_M_RD, 1, buf } | ||
47 | }; | ||
48 | |||
49 | c = kmalloc(sizeof(*c), GFP_KERNEL); | ||
50 | if (!c) | ||
51 | return -ENOMEM; | ||
52 | |||
53 | memset(c, 0, sizeof(*c)); | ||
54 | c->addr = addr; | ||
55 | c->adapter = adap; | ||
56 | c->driver = &pcf8583_driver; | ||
57 | |||
58 | if (i2c_transfer(c->adapter, msgs, 2) == 2) | ||
59 | DAT(c) = buf[0]; | ||
60 | |||
61 | return i2c_attach_client(c); | ||
62 | } | ||
63 | |||
64 | static int | ||
65 | pcf8583_probe(struct i2c_adapter *adap) | ||
66 | { | ||
67 | return i2c_probe(adap, &addr_data, pcf8583_attach); | ||
68 | } | ||
69 | |||
70 | static int | ||
71 | pcf8583_detach(struct i2c_client *client) | ||
72 | { | ||
73 | i2c_detach_client(client); | ||
74 | kfree(client); | ||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | static int | ||
79 | pcf8583_get_datetime(struct i2c_client *client, struct rtc_tm *dt) | ||
80 | { | ||
81 | unsigned char buf[8], addr[1] = { 1 }; | ||
82 | struct i2c_msg msgs[2] = { | ||
83 | { client->addr, 0, 1, addr }, | ||
84 | { client->addr, I2C_M_RD, 6, buf } | ||
85 | }; | ||
86 | int ret = -EIO; | ||
87 | |||
88 | memset(buf, 0, sizeof(buf)); | ||
89 | |||
90 | ret = i2c_transfer(client->adapter, msgs, 2); | ||
91 | if (ret == 2) { | ||
92 | dt->year_off = buf[4] >> 6; | ||
93 | dt->wday = buf[5] >> 5; | ||
94 | |||
95 | buf[4] &= 0x3f; | ||
96 | buf[5] &= 0x1f; | ||
97 | |||
98 | dt->cs = BCD_TO_BIN(buf[0]); | ||
99 | dt->secs = BCD_TO_BIN(buf[1]); | ||
100 | dt->mins = BCD_TO_BIN(buf[2]); | ||
101 | dt->hours = BCD_TO_BIN(buf[3]); | ||
102 | dt->mday = BCD_TO_BIN(buf[4]); | ||
103 | dt->mon = BCD_TO_BIN(buf[5]); | ||
104 | |||
105 | ret = 0; | ||
106 | } | ||
107 | |||
108 | return ret; | ||
109 | } | ||
110 | |||
111 | static int | ||
112 | pcf8583_set_datetime(struct i2c_client *client, struct rtc_tm *dt, int datetoo) | ||
113 | { | ||
114 | unsigned char buf[8]; | ||
115 | int ret, len = 6; | ||
116 | |||
117 | buf[0] = 0; | ||
118 | buf[1] = DAT(client) | 0x80; | ||
119 | buf[2] = BIN_TO_BCD(dt->cs); | ||
120 | buf[3] = BIN_TO_BCD(dt->secs); | ||
121 | buf[4] = BIN_TO_BCD(dt->mins); | ||
122 | buf[5] = BIN_TO_BCD(dt->hours); | ||
123 | |||
124 | if (datetoo) { | ||
125 | len = 8; | ||
126 | buf[6] = BIN_TO_BCD(dt->mday) | (dt->year_off << 6); | ||
127 | buf[7] = BIN_TO_BCD(dt->mon) | (dt->wday << 5); | ||
128 | } | ||
129 | |||
130 | ret = i2c_master_send(client, (char *)buf, len); | ||
131 | if (ret == len) | ||
132 | ret = 0; | ||
133 | |||
134 | buf[1] = DAT(client); | ||
135 | i2c_master_send(client, (char *)buf, 2); | ||
136 | |||
137 | return ret; | ||
138 | } | ||
139 | |||
140 | static int | ||
141 | pcf8583_get_ctrl(struct i2c_client *client, unsigned char *ctrl) | ||
142 | { | ||
143 | *ctrl = DAT(client); | ||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | static int | ||
148 | pcf8583_set_ctrl(struct i2c_client *client, unsigned char *ctrl) | ||
149 | { | ||
150 | unsigned char buf[2]; | ||
151 | |||
152 | buf[0] = 0; | ||
153 | buf[1] = *ctrl; | ||
154 | DAT(client) = *ctrl; | ||
155 | |||
156 | return i2c_master_send(client, (char *)buf, 2); | ||
157 | } | ||
158 | |||
159 | static int | ||
160 | pcf8583_read_mem(struct i2c_client *client, struct mem *mem) | ||
161 | { | ||
162 | unsigned char addr[1]; | ||
163 | struct i2c_msg msgs[2] = { | ||
164 | { client->addr, 0, 1, addr }, | ||
165 | { client->addr, I2C_M_RD, 0, mem->data } | ||
166 | }; | ||
167 | |||
168 | if (mem->loc < 8) | ||
169 | return -EINVAL; | ||
170 | |||
171 | addr[0] = mem->loc; | ||
172 | msgs[1].len = mem->nr; | ||
173 | |||
174 | return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO; | ||
175 | } | ||
176 | |||
177 | static int | ||
178 | pcf8583_write_mem(struct i2c_client *client, struct mem *mem) | ||
179 | { | ||
180 | unsigned char addr[1]; | ||
181 | struct i2c_msg msgs[2] = { | ||
182 | { client->addr, 0, 1, addr }, | ||
183 | { client->addr, 0, 0, mem->data } | ||
184 | }; | ||
185 | |||
186 | if (mem->loc < 8) | ||
187 | return -EINVAL; | ||
188 | |||
189 | addr[0] = mem->loc; | ||
190 | msgs[1].len = mem->nr; | ||
191 | |||
192 | return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO; | ||
193 | } | ||
194 | |||
195 | static int | ||
196 | pcf8583_command(struct i2c_client *client, unsigned int cmd, void *arg) | ||
197 | { | ||
198 | switch (cmd) { | ||
199 | case RTC_GETDATETIME: | ||
200 | return pcf8583_get_datetime(client, arg); | ||
201 | |||
202 | case RTC_SETTIME: | ||
203 | return pcf8583_set_datetime(client, arg, 0); | ||
204 | |||
205 | case RTC_SETDATETIME: | ||
206 | return pcf8583_set_datetime(client, arg, 1); | ||
207 | |||
208 | case RTC_GETCTRL: | ||
209 | return pcf8583_get_ctrl(client, arg); | ||
210 | |||
211 | case RTC_SETCTRL: | ||
212 | return pcf8583_set_ctrl(client, arg); | ||
213 | |||
214 | case MEM_READ: | ||
215 | return pcf8583_read_mem(client, arg); | ||
216 | |||
217 | case MEM_WRITE: | ||
218 | return pcf8583_write_mem(client, arg); | ||
219 | |||
220 | default: | ||
221 | return -EINVAL; | ||
222 | } | ||
223 | } | ||
224 | |||
225 | static struct i2c_driver pcf8583_driver = { | ||
226 | .name = "PCF8583", | ||
227 | .id = I2C_DRIVERID_PCF8583, | ||
228 | .flags = I2C_DF_NOTIFY, | ||
229 | .attach_adapter = pcf8583_probe, | ||
230 | .detach_client = pcf8583_detach, | ||
231 | .command = pcf8583_command | ||
232 | }; | ||
233 | |||
234 | static __init int pcf8583_init(void) | ||
235 | { | ||
236 | return i2c_add_driver(&pcf8583_driver); | ||
237 | } | ||
238 | |||
239 | __initcall(pcf8583_init); | ||
diff --git a/drivers/acorn/char/pcf8583.h b/drivers/acorn/char/pcf8583.h new file mode 100644 index 00000000000..847f7fdb876 --- /dev/null +++ b/drivers/acorn/char/pcf8583.h | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | * linux/drivers/acorn/char/pcf8583.h | ||
3 | * | ||
4 | * Copyright (C) 2000 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | struct rtc_tm { | ||
11 | unsigned char cs; | ||
12 | unsigned char secs; | ||
13 | unsigned char mins; | ||
14 | unsigned char hours; | ||
15 | unsigned char mday; | ||
16 | unsigned char mon; | ||
17 | unsigned char year_off; | ||
18 | unsigned char wday; | ||
19 | }; | ||
20 | |||
21 | struct mem { | ||
22 | unsigned int loc; | ||
23 | unsigned int nr; | ||
24 | unsigned char *data; | ||
25 | }; | ||
26 | |||
27 | #define RTC_GETDATETIME 0 | ||
28 | #define RTC_SETTIME 1 | ||
29 | #define RTC_SETDATETIME 2 | ||
30 | #define RTC_GETCTRL 3 | ||
31 | #define RTC_SETCTRL 4 | ||
32 | #define MEM_READ 5 | ||
33 | #define MEM_WRITE 6 | ||
34 | |||
35 | #define CTRL_STOP 0x80 | ||
36 | #define CTRL_HOLD 0x40 | ||
37 | #define CTRL_32KHZ 0x00 | ||
38 | #define CTRL_MASK 0x08 | ||
39 | #define CTRL_ALARMEN 0x04 | ||
40 | #define CTRL_ALARM 0x02 | ||
41 | #define CTRL_TIMER 0x01 | ||