blob: e9f891678fba8367a34607f05902d4a2f1a3885b [file] [log] [blame]
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05001// 16bit code to access floppy drives.
2//
Kevin O'Connorc892b132009-08-11 21:59:37 -04003// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05004// Copyright (C) 2002 MandrakeSoft S.A.
5//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05007
8#include "types.h" // u8
9#include "disk.h" // DISK_RET_SUCCESS
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040010#include "config.h" // CONFIG_FLOPPY
Kevin O'Connor9521e262008-07-04 13:04:29 -040011#include "biosvar.h" // SET_BDA
Kevin O'Connor94c749c2012-05-28 11:44:02 -040012#include "util.h" // dprintf
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050013#include "cmos.h" // inb_cmos
Kevin O'Connord21c0892008-11-26 17:02:43 -050014#include "pic.h" // eoi_pic1
Kevin O'Connor9521e262008-07-04 13:04:29 -040015#include "bregs.h" // struct bregs
Kevin O'Connor72eee3e2010-12-27 19:07:49 -050016#include "boot.h" // boot_add_floppy
Kevin O'Connor031ef552010-12-27 19:26:57 -050017#include "pci.h" // pci_to_bdf
18#include "pci_ids.h" // PCI_CLASS_BRIDGE_ISA
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050019
Kevin O'Connor7b184d82009-08-23 11:56:55 -040020#define FLOPPY_SIZE_CODE 0x02 // 512 byte sectors
21#define FLOPPY_DATALEN 0xff // Not used - because size code is 0x02
22#define FLOPPY_MOTOR_TICKS 37 // ~2 seconds
23#define FLOPPY_FILLBYTE 0xf6
24#define FLOPPY_GAPLEN 0x1B
25#define FLOPPY_FORMAT_GAPLEN 0x6c
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050026
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050027// New diskette parameter table adding 3 parameters from IBM
28// Since no provisions are made for multiple drive types, most
29// values in this table are ignored. I set parameters for 1.44M
30// floppy here
Kevin O'Connor372e0712009-09-09 09:51:31 -040031struct floppy_ext_dbt_s diskette_param_table2 VAR16VISIBLE = {
Kevin O'Connor44c631d2008-03-02 11:24:36 -050032 .dbt = {
Kevin O'Connor7b184d82009-08-23 11:56:55 -040033 .specify1 = 0xAF, // step rate 12ms, head unload 240ms
34 .specify2 = 0x02, // head load time 4ms, DMA used
35 .shutoff_ticks = FLOPPY_MOTOR_TICKS, // ~2 seconds
36 .bps_code = FLOPPY_SIZE_CODE,
Kevin O'Connor44c631d2008-03-02 11:24:36 -050037 .sectors = 18,
Kevin O'Connor7b184d82009-08-23 11:56:55 -040038 .interblock_len = FLOPPY_GAPLEN,
39 .data_len = FLOPPY_DATALEN,
40 .gap_len = FLOPPY_FORMAT_GAPLEN,
41 .fill_byte = FLOPPY_FILLBYTE,
42 .settle_time = 0x0F, // 15ms
43 .startup_time = 0x08, // 1 second
Kevin O'Connor44c631d2008-03-02 11:24:36 -050044 },
45 .max_track = 79, // maximum track
46 .data_rate = 0, // data transfer rate
47 .drive_type = 4, // drive type in cmos
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050048};
49
Kevin O'Connoref719892012-11-26 11:14:00 -050050struct floppy_dbt_s diskette_param_table VAR16FIXED(0xefc7);
Kevin O'Connor30853762009-01-17 18:49:20 -050051
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040052struct floppyinfo_s {
53 struct chs_s chs;
54 u8 config_data;
55 u8 media_state;
56};
57
Kevin O'Connor372e0712009-09-09 09:51:31 -040058struct floppyinfo_s FloppyInfo[] VAR16VISIBLE = {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040059 // Unknown
60 { {0, 0, 0}, 0x00, 0x00},
61 // 1 - 360KB, 5.25" - 2 heads, 40 tracks, 9 sectors
62 { {2, 40, 9}, 0x00, 0x25},
63 // 2 - 1.2MB, 5.25" - 2 heads, 80 tracks, 15 sectors
64 { {2, 80, 15}, 0x00, 0x25},
65 // 3 - 720KB, 3.5" - 2 heads, 80 tracks, 9 sectors
66 { {2, 80, 9}, 0x00, 0x17},
67 // 4 - 1.44MB, 3.5" - 2 heads, 80 tracks, 18 sectors
68 { {2, 80, 18}, 0x00, 0x17},
69 // 5 - 2.88MB, 3.5" - 2 heads, 80 tracks, 36 sectors
70 { {2, 80, 36}, 0xCC, 0xD7},
71 // 6 - 160k, 5.25" - 1 heads, 40 tracks, 8 sectors
72 { {1, 40, 8}, 0x00, 0x27},
73 // 7 - 180k, 5.25" - 1 heads, 40 tracks, 9 sectors
74 { {1, 40, 9}, 0x00, 0x27},
75 // 8 - 320k, 5.25" - 2 heads, 40 tracks, 8 sectors
76 { {2, 40, 8}, 0x00, 0x27},
77};
78
Kevin O'Connor77d227b2009-10-22 21:48:39 -040079struct drive_s *
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -050080init_floppy(int floppyid, int ftype)
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040081{
82 if (ftype <= 0 || ftype >= ARRAY_SIZE(FloppyInfo)) {
83 dprintf(1, "Bad floppy type %d\n", ftype);
Kevin O'Connor77d227b2009-10-22 21:48:39 -040084 return NULL;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040085 }
86
Kevin O'Connord7e998f2010-02-15 22:48:28 -050087 struct drive_s *drive_g = malloc_fseg(sizeof(*drive_g));
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -050088 if (!drive_g) {
Kevin O'Connord7e998f2010-02-15 22:48:28 -050089 warn_noalloc();
Kevin O'Connor77d227b2009-10-22 21:48:39 -040090 return NULL;
Kevin O'Connord7e998f2010-02-15 22:48:28 -050091 }
92 memset(drive_g, 0, sizeof(*drive_g));
Kevin O'Connor77d227b2009-10-22 21:48:39 -040093 drive_g->cntl_id = floppyid;
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -050094 drive_g->type = DTYPE_FLOPPY;
Kevin O'Connor77d227b2009-10-22 21:48:39 -040095 drive_g->blksize = DISK_SECTOR_SIZE;
96 drive_g->floppy_type = ftype;
97 drive_g->sectors = (u64)-1;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040098
Kevin O'Connor77d227b2009-10-22 21:48:39 -040099 memcpy(&drive_g->lchs, &FloppyInfo[ftype].chs
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400100 , sizeof(FloppyInfo[ftype].chs));
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500101 return drive_g;
102}
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400103
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500104static void
105addFloppy(int floppyid, int ftype)
106{
107 struct drive_s *drive_g = init_floppy(floppyid, ftype);
108 if (!drive_g)
109 return;
110 char *desc = znprintf(MAXDESCSIZE, "Floppy [drive %c]", 'A' + floppyid);
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400111 struct pci_device *pci = pci_find_class(PCI_CLASS_BRIDGE_ISA); /* isa-to-pci bridge */
Kevin O'Connor03e589c2011-07-09 14:35:37 -0400112 int prio = bootprio_find_fdc_device(pci, PORT_FD_BASE, floppyid);
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500113 boot_add_floppy(drive_g, desc, prio);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400114}
115
116void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500117floppy_setup(void)
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400118{
Kevin O'Connoref719892012-11-26 11:14:00 -0500119 memcpy(&diskette_param_table, &diskette_param_table2
120 , sizeof(diskette_param_table));
121 SET_IVT(0x1E, SEGOFF(SEG_BIOS
122 , (u32)&diskette_param_table2 - BUILD_BIOS_ADDR));
123
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400124 if (! CONFIG_FLOPPY)
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400125 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -0400126 dprintf(3, "init floppy drives\n");
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400127
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500128 if (CONFIG_COREBOOT) {
Kevin O'Connor8b73b832012-11-26 11:18:11 -0500129 u8 type = romfile_loadint("etc/floppy0", 0);
130 if (type)
131 addFloppy(0, type);
132 type = romfile_loadint("etc/floppy1", 0);
133 if (type)
134 addFloppy(1, type);
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500135 } else {
136 u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400137 if (type & 0xf0)
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500138 addFloppy(0, type >> 4);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400139 if (type & 0x0f)
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500140 addFloppy(1, type & 0x0f);
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400141 }
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400142
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400143 outb(0x02, PORT_DMA1_MASK_REG);
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400144
Kevin O'Connorcc9e1bf2010-07-28 21:31:38 -0400145 enable_hwirq(6, FUNC16(entry_0e));
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400146}
147
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400148// Find a floppy type that matches a given image size.
149int
150find_floppy_type(u32 size)
151{
152 int i;
153 for (i=1; i<ARRAY_SIZE(FloppyInfo); i++) {
154 struct chs_s *c = &FloppyInfo[i].chs;
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400155 if (c->cylinders * c->heads * c->spt * DISK_SECTOR_SIZE == size)
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400156 return i;
157 }
158 return -1;
159}
160
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500161
162/****************************************************************
163 * Low-level floppy IO
164 ****************************************************************/
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500165
166static void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500167floppy_reset_controller(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500168{
169 // Reset controller
170 u8 val8 = inb(PORT_FD_DOR);
171 outb(val8 & ~0x04, PORT_FD_DOR);
172 outb(val8 | 0x04, PORT_FD_DOR);
173
174 // Wait for controller to come out of reset
175 while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
176 ;
177}
178
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500179static int
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500180wait_floppy_irq(void)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500181{
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400182 ASSERT16();
Kevin O'Connore51316d2012-06-10 09:09:22 -0400183 u8 frs;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500184 for (;;) {
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400185 if (!GET_BDA(floppy_motor_counter))
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500186 return -1;
Kevin O'Connore51316d2012-06-10 09:09:22 -0400187 frs = GET_BDA(floppy_recalibration_status);
188 if (frs & FRS_TIMEOUT)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500189 break;
Kevin O'Connor94c749c2012-05-28 11:44:02 -0400190 // Could use yield_toirq() here, but that causes issues on
Kevin O'Connor7a98fd02010-01-17 13:00:49 -0500191 // bochs, so use yield() instead.
192 yield();
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500193 }
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500194
Kevin O'Connore51316d2012-06-10 09:09:22 -0400195 frs &= ~FRS_TIMEOUT;
196 SET_BDA(floppy_recalibration_status, frs);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500197 return 0;
198}
199
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500200static void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400201floppy_prepare_controller(u8 floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500202{
Kevin O'Connore51316d2012-06-10 09:09:22 -0400203 u8 frs = GET_BDA(floppy_recalibration_status);
204 SET_BDA(floppy_recalibration_status, frs & ~FRS_TIMEOUT);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500205
206 // turn on motor of selected drive, DMA & int enabled, normal operation
207 u8 prev_reset = inb(PORT_FD_DOR) & 0x04;
208 u8 dor = 0x10;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400209 if (floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500210 dor = 0x20;
211 dor |= 0x0c;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400212 dor |= floppyid;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500213 outb(dor, PORT_FD_DOR);
214
215 // reset the disk motor timeout value of INT 08
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400216 SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500217
218 // wait for drive readiness
219 while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
220 ;
221
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500222 if (!prev_reset)
223 wait_floppy_irq();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500224}
225
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400226static int
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500227floppy_pio(u8 *cmd, u8 cmdlen)
228{
229 floppy_prepare_controller(cmd[1] & 1);
230
231 // send command to controller
232 u8 i;
233 for (i=0; i<cmdlen; i++)
234 outb(cmd[i], PORT_FD_DATA);
235
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500236 int ret = wait_floppy_irq();
237 if (ret) {
238 floppy_reset_controller();
239 return -1;
Kevin O'Connor06ad44e2008-04-05 19:30:02 -0400240 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500241
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500242 return 0;
243}
244
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400245static int
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400246floppy_cmd(struct disk_op_s *op, u16 count, u8 *cmd, u8 cmdlen)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500247{
248 // es:bx = pointer to where to place information from diskette
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400249 u32 addr = (u32)op->buf_fl;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500250
251 // check for 64K boundary overrun
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400252 u16 end = count - 1;
253 u32 last_addr = addr + end;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400254 if ((addr >> 16) != (last_addr >> 16))
255 return DISK_RET_EBOUNDARY;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500256
257 u8 mode_register = 0x4a; // single mode, increment, autoinit disable,
258 if (cmd[0] == 0xe6)
259 // read
260 mode_register = 0x46;
261
Kevin O'Connor44c631d2008-03-02 11:24:36 -0500262 //DEBUGF("floppy dma c2\n");
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500263 outb(0x06, PORT_DMA1_MASK_REG);
264 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
Kevin O'Connore10d3452008-07-09 22:45:12 -0400265 outb(addr, PORT_DMA_ADDR_2);
266 outb(addr>>8, PORT_DMA_ADDR_2);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500267 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400268 outb(end, PORT_DMA_CNT_2);
269 outb(end>>8, PORT_DMA_CNT_2);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500270
271 // port 0b: DMA-1 Mode Register
272 // transfer type=write, channel 2
273 outb(mode_register, PORT_DMA1_MODE_REG);
274
275 // port 81: DMA-1 Page Register, channel 2
Kevin O'Connore10d3452008-07-09 22:45:12 -0400276 outb(addr>>16, PORT_DMA_PAGE_2);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500277
278 outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2
279
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400280 int ret = floppy_pio(cmd, cmdlen);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400281 if (ret)
282 return DISK_RET_ETIMEOUT;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500283
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500284 // check port 3f4 for accessibility to status bytes
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400285 if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
286 return DISK_RET_ECONTROLLER;
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500287
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500288 // read 7 return status bytes from controller
289 u8 i;
290 for (i=0; i<7; i++) {
291 u8 v = inb(PORT_FD_DATA);
292 cmd[i] = v;
293 SET_BDA(floppy_return_status[i], v);
294 }
295
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400296 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500297}
298
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500299
300/****************************************************************
301 * Floppy media sense
302 ****************************************************************/
303
304static inline void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400305set_diskette_current_cyl(u8 floppyid, u8 cyl)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500306{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400307 SET_BDA(floppy_track[floppyid], cyl);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500308}
309
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500310static void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400311floppy_drive_recal(u8 floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500312{
313 // send Recalibrate command (2 bytes) to controller
314 u8 data[12];
315 data[0] = 0x07; // 07: Recalibrate
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400316 data[1] = floppyid; // 0=drive0, 1=drive1
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500317 floppy_pio(data, 2);
318
Kevin O'Connore51316d2012-06-10 09:09:22 -0400319 u8 frs = GET_BDA(floppy_recalibration_status);
320 SET_BDA(floppy_recalibration_status, frs | (1<<floppyid));
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400321 set_diskette_current_cyl(floppyid, 0);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500322}
323
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500324static int
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400325floppy_media_sense(struct drive_s *drive_g)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500326{
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500327 // for now cheat and get drive type from CMOS,
328 // assume media is same as drive type
329
330 // ** config_data **
331 // Bitfields for diskette media control:
332 // Bit(s) Description (Table M0028)
333 // 7-6 last data rate set by controller
334 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
335 // 5-4 last diskette drive step rate selected
336 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
337 // 3-2 {data rate at start of operation}
338 // 1-0 reserved
339
340 // ** media_state **
341 // Bitfields for diskette drive media state:
342 // Bit(s) Description (Table M0030)
343 // 7-6 data rate
344 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
345 // 5 double stepping required (e.g. 360kB in 1.2MB)
346 // 4 media type established
347 // 3 drive capable of supporting 4MB media
348 // 2-0 on exit from BIOS, contains
349 // 000 trying 360kB in 360kB
350 // 001 trying 360kB in 1.2MB
351 // 010 trying 1.2MB in 1.2MB
352 // 011 360kB in 360kB established
353 // 100 360kB in 1.2MB established
354 // 101 1.2MB in 1.2MB established
355 // 110 reserved
356 // 111 all other formats/drives
357
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400358 u8 ftype = GET_GLOBAL(drive_g->floppy_type);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400359 SET_BDA(floppy_last_data_rate, GET_GLOBAL(FloppyInfo[ftype].config_data));
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400360 u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400361 SET_BDA(floppy_media_state[floppyid]
362 , GET_GLOBAL(FloppyInfo[ftype].media_state));
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400363 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500364}
365
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400366static int
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400367check_recal_drive(struct drive_s *drive_g)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500368{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400369 u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400370 if ((GET_BDA(floppy_recalibration_status) & (1<<floppyid))
371 && (GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED))
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500372 // Media is known.
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400373 return DISK_RET_SUCCESS;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500374
375 // Recalibrate drive.
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400376 floppy_drive_recal(floppyid);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500377
378 // Sense media.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400379 return floppy_media_sense(drive_g);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500380}
381
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500382
383/****************************************************************
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400384 * Floppy handlers
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500385 ****************************************************************/
386
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500387static void
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400388lba2chs(struct disk_op_s *op, u8 *track, u8 *sector, u8 *head)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500389{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400390 u32 lba = op->lba;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400391
392 u32 tmp = lba + 1;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400393 u16 nlspt = GET_GLOBAL(op->drive_g->lchs.spt);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400394 *sector = tmp % nlspt;
395
396 tmp /= nlspt;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400397 u16 nlh = GET_GLOBAL(op->drive_g->lchs.heads);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400398 *head = tmp % nlh;
399
400 tmp /= nlh;
401 *track = tmp;
402}
403
404// diskette controller reset
405static int
406floppy_reset(struct disk_op_s *op)
407{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400408 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400409 set_diskette_current_cyl(floppyid, 0); // current cylinder
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400410 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500411}
412
413// Read Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400414static int
415floppy_read(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500416{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400417 int res = check_recal_drive(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400418 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400419 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500420
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400421 u8 track, sector, head;
422 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500423
424 // send read-normal-data command (9 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400425 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500426 u8 data[12];
427 data[0] = 0xe6; // e6: read normal data
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400428 data[1] = (head << 2) | floppyid; // HD DR1 DR2
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500429 data[2] = track;
430 data[3] = head;
431 data[4] = sector;
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400432 data[5] = FLOPPY_SIZE_CODE;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400433 data[6] = sector + op->count - 1; // last sector to read on track
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400434 data[7] = FLOPPY_GAPLEN;
435 data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500436
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400437 res = floppy_cmd(op, op->count * DISK_SECTOR_SIZE, data, 9);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400438 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400439 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500440
441 if (data[0] & 0xc0) {
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400442 res = DISK_RET_ECONTROLLER;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400443 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500444 }
445
446 // ??? should track be new val from return_status[3] ?
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400447 set_diskette_current_cyl(floppyid, track);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400448 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400449fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400450 op->count = 0; // no sectors read
451 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500452}
453
454// Write Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400455static int
456floppy_write(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500457{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400458 int res = check_recal_drive(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400459 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400460 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500461
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400462 u8 track, sector, head;
463 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500464
465 // send write-normal-data command (9 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400466 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500467 u8 data[12];
468 data[0] = 0xc5; // c5: write normal data
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400469 data[1] = (head << 2) | floppyid; // HD DR1 DR2
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500470 data[2] = track;
471 data[3] = head;
472 data[4] = sector;
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400473 data[5] = FLOPPY_SIZE_CODE;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400474 data[6] = sector + op->count - 1; // last sector to write on track
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400475 data[7] = FLOPPY_GAPLEN;
476 data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500477
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400478 res = floppy_cmd(op, op->count * DISK_SECTOR_SIZE, data, 9);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400479 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400480 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500481
482 if (data[0] & 0xc0) {
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500483 if (data[1] & 0x02)
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400484 res = DISK_RET_EWRITEPROTECT;
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500485 else
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400486 res = DISK_RET_ECONTROLLER;
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500487 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500488 }
489
490 // ??? should track be new val from return_status[3] ?
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400491 set_diskette_current_cyl(floppyid, track);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400492 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400493fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400494 op->count = 0; // no sectors read
495 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500496}
497
498// Verify Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400499static int
500floppy_verify(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500501{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400502 int res = check_recal_drive(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400503 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400504 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500505
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400506 u8 track, sector, head;
507 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500508
509 // ??? should track be new val from return_status[3] ?
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400510 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400511 set_diskette_current_cyl(floppyid, track);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400512 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400513fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400514 op->count = 0; // no sectors read
515 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500516}
517
518// format diskette track
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400519static int
520floppy_format(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500521{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400522 int ret = check_recal_drive(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400523 if (ret)
524 return ret;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500525
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400526 u8 head = op->lba;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500527
528 // send format-track command (6 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400529 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500530 u8 data[12];
531 data[0] = 0x4d; // 4d: format track
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400532 data[1] = (head << 2) | floppyid; // HD DR1 DR2
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400533 data[2] = FLOPPY_SIZE_CODE;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400534 data[3] = op->count; // number of sectors per track
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400535 data[4] = FLOPPY_FORMAT_GAPLEN;
536 data[5] = FLOPPY_FILLBYTE;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500537
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400538 ret = floppy_cmd(op, op->count * 4, data, 6);
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400539 if (ret)
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400540 return ret;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500541
542 if (data[0] & 0xc0) {
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500543 if (data[1] & 0x02)
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400544 return DISK_RET_EWRITEPROTECT;
545 return DISK_RET_ECONTROLLER;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500546 }
547
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400548 set_diskette_current_cyl(floppyid, 0);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400549 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500550}
551
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400552int
553process_floppy_op(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500554{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400555 if (!CONFIG_FLOPPY)
556 return 0;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500557
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400558 switch (op->command) {
559 case CMD_RESET:
560 return floppy_reset(op);
561 case CMD_READ:
562 return floppy_read(op);
563 case CMD_WRITE:
564 return floppy_write(op);
565 case CMD_VERIFY:
566 return floppy_verify(op);
567 case CMD_FORMAT:
568 return floppy_format(op);
569 default:
570 op->count = 0;
571 return DISK_RET_EPARAM;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500572 }
573}
574
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500575
576/****************************************************************
577 * HW irqs
578 ****************************************************************/
579
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500580// INT 0Eh Diskette Hardware ISR Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500581void VISIBLE16
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400582handle_0e(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500583{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400584 if (! CONFIG_FLOPPY)
Kevin O'Connorff5e0052012-12-07 11:25:58 -0500585 return;
586 debug_isr(DEBUG_ISR_0e);
Kevin O'Connor40967022008-07-21 22:23:05 -0400587
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500588 if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0) {
589 outb(0x08, PORT_FD_DATA); // sense interrupt status
590 while ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
591 ;
592 do {
593 inb(PORT_FD_DATA);
594 } while ((inb(PORT_FD_STATUS) & 0xc0) == 0xc0);
595 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500596 // diskette interrupt has occurred
Kevin O'Connore51316d2012-06-10 09:09:22 -0400597 u8 frs = GET_BDA(floppy_recalibration_status);
598 SET_BDA(floppy_recalibration_status, frs | FRS_TIMEOUT);
Kevin O'Connor40967022008-07-21 22:23:05 -0400599
Kevin O'Connor40967022008-07-21 22:23:05 -0400600 eoi_pic1();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500601}
602
603// Called from int08 handler.
604void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500605floppy_tick(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500606{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400607 if (! CONFIG_FLOPPY)
Kevin O'Connor40967022008-07-21 22:23:05 -0400608 return;
609
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500610 // time to turn off drive(s)?
611 u8 fcount = GET_BDA(floppy_motor_counter);
612 if (fcount) {
613 fcount--;
614 SET_BDA(floppy_motor_counter, fcount);
615 if (fcount == 0)
616 // turn motor(s) off
617 outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
618 }
619}