blob: a27da56b0337589f61cac17c0ad2709384e89321 [file] [log] [blame]
Kevin O'Connorc09492e2008-03-01 13:38:07 -05001// Low level ATA disk access
2//
Kevin O'Connorc892b132009-08-11 21:59:37 -04003// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connorc09492e2008-03-01 13:38:07 -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'Connorc09492e2008-03-01 13:38:07 -05007
Kevin O'Connor3491e8b2008-02-29 00:22:27 -05008#include "types.h" // u8
9#include "ioport.h" // inb
Kevin O'Connorac8df8c2008-05-24 23:46:33 -040010#include "util.h" // dprintf
Kevin O'Connor15aee2e2008-03-01 13:34:04 -050011#include "cmos.h" // inb_cmos
Kevin O'Connord21c0892008-11-26 17:02:43 -050012#include "pic.h" // enable_hwirq
Kevin O'Connor9521e262008-07-04 13:04:29 -040013#include "biosvar.h" // GET_EBDA
Kevin O'Connor51fd0a12009-09-12 13:20:14 -040014#include "pci.h" // foreachpci
Kevin O'Connor2ed2f582008-11-08 15:53:36 -050015#include "pci_ids.h" // PCI_CLASS_STORAGE_OTHER
16#include "pci_regs.h" // PCI_INTERRUPT_LINE
Kevin O'Connor0a924122009-02-08 19:43:47 -050017#include "boot.h" // add_bcv_hd
Kevin O'Connor609da232008-12-28 23:18:57 -050018#include "disk.h" // struct ata_s
Kevin O'Connorc892b132009-08-11 21:59:37 -040019#include "ata.h" // ATA_CB_STAT
Kevin O'Connor7d700252010-02-15 11:56:07 -050020#include "blockcmd.h" // CDB_CMD_READ_10
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050021
Kevin O'Connor425f2122009-04-18 12:23:00 -040022#define IDE_TIMEOUT 32000 //32 seconds max for IDE ops
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050023
Kevin O'Connor372e0712009-09-09 09:51:31 -040024struct ata_channel_s ATA_channels[CONFIG_MAX_ATA_INTERFACES] VAR16VISIBLE;
Kevin O'Connor609da232008-12-28 23:18:57 -050025
Kevin O'Connor15aee2e2008-03-01 13:34:04 -050026
Kevin O'Connora6b9f712008-03-29 12:53:57 -040027/****************************************************************
28 * Helper functions
29 ****************************************************************/
30
31// Wait for the specified ide state
Kevin O'Connor580e3322009-02-06 22:36:53 -050032static inline int
33await_ide(u8 mask, u8 flags, u16 base, u16 timeout)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050034{
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050035 u64 end = calc_future_tsc(timeout);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050036 for (;;) {
Kevin O'Connora6b9f712008-03-29 12:53:57 -040037 u8 status = inb(base+ATA_CB_STAT);
Kevin O'Connor580e3322009-02-06 22:36:53 -050038 if ((status & mask) == flags)
Kevin O'Connora6b9f712008-03-29 12:53:57 -040039 return status;
Kevin O'Connor89eb6242009-10-22 22:30:37 -040040 if (check_time(end)) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -050041 warn_timeout();
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050042 return -1;
43 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -040044 yield();
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050045 }
Kevin O'Connor580e3322009-02-06 22:36:53 -050046}
47
48// Wait for the device to be not-busy.
49static int
50await_not_bsy(u16 base)
51{
52 return await_ide(ATA_CB_STAT_BSY, 0, base, IDE_TIMEOUT);
53}
54
55// Wait for the device to be ready.
56static int
57await_rdy(u16 base)
58{
59 return await_ide(ATA_CB_STAT_RDY, ATA_CB_STAT_RDY, base, IDE_TIMEOUT);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050060}
61
Kevin O'Connora6b9f712008-03-29 12:53:57 -040062// Wait for ide state - pauses for one ata cycle first.
Kevin O'Connora9caeae2009-03-07 00:09:52 -050063static inline int
Kevin O'Connor580e3322009-02-06 22:36:53 -050064pause_await_not_bsy(u16 iobase1, u16 iobase2)
Kevin O'Connora6b9f712008-03-29 12:53:57 -040065{
66 // Wait one PIO transfer cycle.
67 inb(iobase2 + ATA_CB_ASTAT);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050068
Kevin O'Connor580e3322009-02-06 22:36:53 -050069 return await_not_bsy(iobase1);
Kevin O'Connora6b9f712008-03-29 12:53:57 -040070}
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050071
Kevin O'Connoref2822a2008-06-07 15:23:11 -040072// Wait for ide state - pause for 400ns first.
Kevin O'Connora9caeae2009-03-07 00:09:52 -050073static inline int
Kevin O'Connor580e3322009-02-06 22:36:53 -050074ndelay_await_not_bsy(u16 iobase1)
Kevin O'Connoref2822a2008-06-07 15:23:11 -040075{
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -050076 ndelay(400);
Kevin O'Connor580e3322009-02-06 22:36:53 -050077 return await_not_bsy(iobase1);
Kevin O'Connoref2822a2008-06-07 15:23:11 -040078}
79
Kevin O'Connora6b9f712008-03-29 12:53:57 -040080// Reset a drive
Kevin O'Connorb1144362009-08-11 20:43:38 -040081static void
Kevin O'Connor77d227b2009-10-22 21:48:39 -040082ata_reset(struct drive_s *drive_g)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050083{
Kevin O'Connor77d227b2009-10-22 21:48:39 -040084 u8 ataid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connorb1144362009-08-11 20:43:38 -040085 u8 channel = ataid / 2;
86 u8 slave = ataid % 2;
Kevin O'Connorc892b132009-08-11 21:59:37 -040087 u16 iobase1 = GET_GLOBAL(ATA_channels[channel].iobase1);
88 u16 iobase2 = GET_GLOBAL(ATA_channels[channel].iobase2);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050089
Kevin O'Connor77d227b2009-10-22 21:48:39 -040090 dprintf(6, "ata_reset drive=%p\n", drive_g);
Kevin O'Connor580e3322009-02-06 22:36:53 -050091 // Pulse SRST
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050092 outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST, iobase2+ATA_CB_DC);
Kevin O'Connor580e3322009-02-06 22:36:53 -050093 udelay(5);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050094 outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2+ATA_CB_DC);
Kevin O'Connor10ad7992009-10-24 11:06:08 -040095 msleep(2);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050096
Kevin O'Connor580e3322009-02-06 22:36:53 -050097 // wait for device to become not busy.
98 int status = await_not_bsy(iobase1);
99 if (status < 0)
100 goto done;
101 if (slave) {
102 // Change device.
103 u64 end = calc_future_tsc(IDE_TIMEOUT);
104 for (;;) {
105 outb(ATA_CB_DH_DEV1, iobase1 + ATA_CB_DH);
Kevin O'Connorc946eca2009-05-24 13:55:01 -0400106 status = ndelay_await_not_bsy(iobase1);
Kevin O'Connor580e3322009-02-06 22:36:53 -0500107 if (status < 0)
108 goto done;
109 if (inb(iobase1 + ATA_CB_DH) == ATA_CB_DH_DEV1)
110 break;
111 // Change drive request failed to take effect - retry.
Kevin O'Connor89eb6242009-10-22 22:30:37 -0400112 if (check_time(end)) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500113 warn_timeout();
Kevin O'Connor580e3322009-02-06 22:36:53 -0500114 goto done;
115 }
116 }
Kevin O'Connorf5624d22009-08-18 22:17:57 -0400117 } else {
118 // QEMU doesn't reset dh on reset, so set it explicitly.
119 outb(ATA_CB_DH_DEV0, iobase1 + ATA_CB_DH);
Kevin O'Connor2e3eeeb2008-06-12 22:29:30 -0400120 }
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500121
Kevin O'Connor580e3322009-02-06 22:36:53 -0500122 // On a user-reset request, wait for RDY if it is an ATA device.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400123 u8 type=GET_GLOBAL(drive_g->type);
Kevin O'Connor42337662009-08-10 00:06:37 -0400124 if (type == DTYPE_ATA)
Kevin O'Connor580e3322009-02-06 22:36:53 -0500125 status = await_rdy(iobase1);
Kevin O'Connor3e1b6492008-05-26 15:06:40 -0400126
Kevin O'Connor580e3322009-02-06 22:36:53 -0500127done:
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500128 // Enable interrupts
129 outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);
Kevin O'Connor580e3322009-02-06 22:36:53 -0500130
131 dprintf(6, "ata_reset exit status=%x\n", status);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500132}
133
Kevin O'Connor14021f22009-12-26 23:21:38 -0500134// Check for drive RDY for 16bit interface command.
Kevin O'Connor42337662009-08-10 00:06:37 -0400135static int
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400136isready(struct drive_s *drive_g)
Kevin O'Connor42337662009-08-10 00:06:37 -0400137{
138 // Read the status from controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400139 u8 ataid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connorb1144362009-08-11 20:43:38 -0400140 u8 channel = ataid / 2;
Kevin O'Connorc892b132009-08-11 21:59:37 -0400141 u16 iobase1 = GET_GLOBAL(ATA_channels[channel].iobase1);
Kevin O'Connor42337662009-08-10 00:06:37 -0400142 u8 status = inb(iobase1 + ATA_CB_STAT);
Kevin O'Connor126eac62009-08-16 13:32:24 -0400143 if ((status & (ATA_CB_STAT_BSY|ATA_CB_STAT_RDY)) == ATA_CB_STAT_RDY)
144 return DISK_RET_SUCCESS;
145 return DISK_RET_ENOTREADY;
Kevin O'Connor42337662009-08-10 00:06:37 -0400146}
147
Kevin O'Connor14021f22009-12-26 23:21:38 -0500148// Default 16bit command demuxer for ATA and ATAPI devices.
Kevin O'Connor42337662009-08-10 00:06:37 -0400149static int
150process_ata_misc_op(struct disk_op_s *op)
151{
Kevin O'Connorc892b132009-08-11 21:59:37 -0400152 if (!CONFIG_ATA)
153 return 0;
154
Kevin O'Connor42337662009-08-10 00:06:37 -0400155 switch (op->command) {
Kevin O'Connor42337662009-08-10 00:06:37 -0400156 case CMD_RESET:
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400157 ata_reset(op->drive_g);
Kevin O'Connor126eac62009-08-16 13:32:24 -0400158 return DISK_RET_SUCCESS;
Kevin O'Connor42337662009-08-10 00:06:37 -0400159 case CMD_ISREADY:
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400160 return isready(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400161 case CMD_FORMAT:
162 case CMD_VERIFY:
163 case CMD_SEEK:
164 return DISK_RET_SUCCESS;
165 default:
166 op->count = 0;
167 return DISK_RET_EPARAM;
Kevin O'Connor42337662009-08-10 00:06:37 -0400168 }
169}
170
Kevin O'Connoree55c762008-05-13 00:18:20 -0400171
172/****************************************************************
173 * ATA send command
174 ****************************************************************/
175
Kevin O'Connorf888f8c2008-03-23 00:04:54 -0400176struct ata_pio_command {
Kevin O'Connorf888f8c2008-03-23 00:04:54 -0400177 u8 feature;
178 u8 sector_count;
179 u8 lba_low;
180 u8 lba_mid;
181 u8 lba_high;
182 u8 device;
183 u8 command;
184
Kevin O'Connor14021f22009-12-26 23:21:38 -0500185 u8 feature2;
Kevin O'Connorf888f8c2008-03-23 00:04:54 -0400186 u8 sector_count2;
187 u8 lba_low2;
188 u8 lba_mid2;
189 u8 lba_high2;
190};
191
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400192// Send an ata command to the drive.
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400193static int
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400194send_cmd(struct drive_s *drive_g, struct ata_pio_command *cmd)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500195{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400196 u8 ataid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connorb1144362009-08-11 20:43:38 -0400197 u8 channel = ataid / 2;
198 u8 slave = ataid % 2;
Kevin O'Connorc892b132009-08-11 21:59:37 -0400199 u16 iobase1 = GET_GLOBAL(ATA_channels[channel].iobase1);
Kevin O'Connoref2822a2008-06-07 15:23:11 -0400200
201 // Select device
Kevin O'Connor580e3322009-02-06 22:36:53 -0500202 int status = await_not_bsy(iobase1);
203 if (status < 0)
204 return status;
205 u8 newdh = ((cmd->device & ~ATA_CB_DH_DEV1)
206 | (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0));
207 u8 olddh = inb(iobase1 + ATA_CB_DH);
208 outb(newdh, iobase1 + ATA_CB_DH);
209 if ((olddh ^ newdh) & (1<<4)) {
210 // Was a device change - wait for device to become not busy.
Kevin O'Connorc946eca2009-05-24 13:55:01 -0400211 status = ndelay_await_not_bsy(iobase1);
Kevin O'Connor580e3322009-02-06 22:36:53 -0500212 if (status < 0)
213 return status;
214 }
Kevin O'Connoref2822a2008-06-07 15:23:11 -0400215
Kevin O'Connor14021f22009-12-26 23:21:38 -0500216 // Check for ATA_CMD_(READ|WRITE)_(SECTORS|DMA)_EXT commands.
217 if ((cmd->command & ~0x11) == ATA_CMD_READ_SECTORS_EXT) {
218 outb(cmd->feature2, iobase1 + ATA_CB_FR);
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400219 outb(cmd->sector_count2, iobase1 + ATA_CB_SC);
220 outb(cmd->lba_low2, iobase1 + ATA_CB_SN);
221 outb(cmd->lba_mid2, iobase1 + ATA_CB_CL);
222 outb(cmd->lba_high2, iobase1 + ATA_CB_CH);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500223 }
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400224 outb(cmd->feature, iobase1 + ATA_CB_FR);
225 outb(cmd->sector_count, iobase1 + ATA_CB_SC);
226 outb(cmd->lba_low, iobase1 + ATA_CB_SN);
227 outb(cmd->lba_mid, iobase1 + ATA_CB_CL);
228 outb(cmd->lba_high, iobase1 + ATA_CB_CH);
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400229 outb(cmd->command, iobase1 + ATA_CB_CMD);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500230
Kevin O'Connor14021f22009-12-26 23:21:38 -0500231 return 0;
232}
233
234// Wait for data after calling 'send_cmd'.
235static int
236ata_wait_data(u16 iobase1)
237{
238 int status = ndelay_await_not_bsy(iobase1);
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400239 if (status < 0)
240 return status;
241
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500242 if (status & ATA_CB_STAT_ERR) {
Kevin O'Connor580e3322009-02-06 22:36:53 -0500243 dprintf(6, "send_cmd : read error (status=%02x err=%02x)\n"
244 , status, inb(iobase1 + ATA_CB_ERR));
Kevin O'Connora05223c2008-06-28 12:15:57 -0400245 return -4;
Kevin O'Connor3a049632008-03-11 11:48:04 -0400246 }
247 if (!(status & ATA_CB_STAT_DRQ)) {
Kevin O'Connor580e3322009-02-06 22:36:53 -0500248 dprintf(6, "send_cmd : DRQ not set (status %02x)\n", status);
Kevin O'Connora05223c2008-06-28 12:15:57 -0400249 return -5;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500250 }
251
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400252 return 0;
253}
254
Kevin O'Connor14021f22009-12-26 23:21:38 -0500255// Send an ata command that does not transfer any further data.
256int
257ata_cmd_nondata(struct drive_s *drive_g, struct ata_pio_command *cmd)
258{
259 u8 ataid = GET_GLOBAL(drive_g->cntl_id);
260 u8 channel = ataid / 2;
261 u16 iobase1 = GET_GLOBAL(ATA_channels[channel].iobase1);
262 u16 iobase2 = GET_GLOBAL(ATA_channels[channel].iobase2);
263
264 // Disable interrupts
265 outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC);
266
267 int ret = send_cmd(drive_g, cmd);
268 if (ret)
269 goto fail;
270 ret = ndelay_await_not_bsy(iobase1);
271 if (ret < 0)
272 goto fail;
273
274 if (ret & ATA_CB_STAT_ERR) {
275 dprintf(6, "nondata cmd : read error (status=%02x err=%02x)\n"
276 , ret, inb(iobase1 + ATA_CB_ERR));
277 ret = -4;
278 goto fail;
279 }
280 if (ret & ATA_CB_STAT_DRQ) {
281 dprintf(6, "nondata cmd : DRQ set (status %02x)\n", ret);
282 ret = -5;
283 goto fail;
284 }
285
286fail:
287 // Enable interrupts
288 outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);
289
290 return ret;
291}
292
Kevin O'Connoree55c762008-05-13 00:18:20 -0400293
294/****************************************************************
Kevin O'Connor14021f22009-12-26 23:21:38 -0500295 * ATA PIO transfers
Kevin O'Connoree55c762008-05-13 00:18:20 -0400296 ****************************************************************/
297
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400298// Transfer 'op->count' blocks (of 'blocksize' bytes) to/from drive
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400299// 'op->drive_g'.
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400300static int
Kevin O'Connor14021f22009-12-26 23:21:38 -0500301ata_pio_transfer(struct disk_op_s *op, int iswrite, int blocksize)
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400302{
Kevin O'Connor14021f22009-12-26 23:21:38 -0500303 dprintf(16, "ata_pio_transfer id=%p write=%d count=%d bs=%d buf=%p\n"
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400304 , op->drive_g, iswrite, op->count, blocksize, op->buf_fl);
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400305
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400306 u8 ataid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connorb1144362009-08-11 20:43:38 -0400307 u8 channel = ataid / 2;
Kevin O'Connorc892b132009-08-11 21:59:37 -0400308 u16 iobase1 = GET_GLOBAL(ATA_channels[channel].iobase1);
309 u16 iobase2 = GET_GLOBAL(ATA_channels[channel].iobase2);
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400310 int count = op->count;
311 void *buf_fl = op->buf_fl;
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400312 int status;
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400313 for (;;) {
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400314 if (iswrite) {
Kevin O'Connor3a049632008-03-11 11:48:04 -0400315 // Write data to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400316 dprintf(16, "Write sector id=%p dest=%p\n", op->drive_g, buf_fl);
Kevin O'Connor32945af2009-02-27 21:23:01 -0500317 if (CONFIG_ATA_PIO32)
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400318 outsl_fl(iobase1, buf_fl, blocksize / 4);
Kevin O'Connor3a049632008-03-11 11:48:04 -0400319 else
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400320 outsw_fl(iobase1, buf_fl, blocksize / 2);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500321 } else {
Kevin O'Connor3a049632008-03-11 11:48:04 -0400322 // Read data from controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400323 dprintf(16, "Read sector id=%p dest=%p\n", op->drive_g, buf_fl);
Kevin O'Connor32945af2009-02-27 21:23:01 -0500324 if (CONFIG_ATA_PIO32)
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400325 insl_fl(iobase1, buf_fl, blocksize / 4);
Kevin O'Connor3a049632008-03-11 11:48:04 -0400326 else
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400327 insw_fl(iobase1, buf_fl, blocksize / 2);
Kevin O'Connor3a049632008-03-11 11:48:04 -0400328 }
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400329 buf_fl += blocksize;
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400330
Kevin O'Connor580e3322009-02-06 22:36:53 -0500331 status = pause_await_not_bsy(iobase1, iobase2);
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400332 if (status < 0) {
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400333 // Error
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400334 op->count -= count;
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400335 return status;
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400336 }
Kevin O'Connor3a049632008-03-11 11:48:04 -0400337
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400338 count--;
339 if (!count)
Kevin O'Connor3a049632008-03-11 11:48:04 -0400340 break;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500341 status &= (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR);
342 if (status != ATA_CB_STAT_DRQ) {
Kevin O'Connor14021f22009-12-26 23:21:38 -0500343 dprintf(6, "ata_pio_transfer : more sectors left (status %02x)\n"
Kevin O'Connor580e3322009-02-06 22:36:53 -0500344 , status);
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400345 op->count -= count;
Kevin O'Connora05223c2008-06-28 12:15:57 -0400346 return -6;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500347 }
348 }
Kevin O'Connor3a049632008-03-11 11:48:04 -0400349
Kevin O'Connor580e3322009-02-06 22:36:53 -0500350 status &= (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ
351 | ATA_CB_STAT_ERR);
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400352 if (!iswrite)
Kevin O'Connor3a049632008-03-11 11:48:04 -0400353 status &= ~ATA_CB_STAT_DF;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500354 if (status != 0) {
Kevin O'Connor14021f22009-12-26 23:21:38 -0500355 dprintf(6, "ata_pio_transfer : no sectors left (status %02x)\n", status);
Kevin O'Connora05223c2008-06-28 12:15:57 -0400356 return -7;
Kevin O'Connor3a049632008-03-11 11:48:04 -0400357 }
358
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500359 return 0;
360}
361
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400362
363/****************************************************************
Kevin O'Connor14021f22009-12-26 23:21:38 -0500364 * ATA DMA transfers
365 ****************************************************************/
366
367#define BM_CMD 0
368#define BM_CMD_MEMWRITE 0x08
369#define BM_CMD_START 0x01
370#define BM_STATUS 2
371#define BM_STATUS_IRQ 0x04
372#define BM_STATUS_ERROR 0x02
373#define BM_STATUS_ACTIVE 0x01
374#define BM_TABLE 4
375
376struct sff_dma_prd {
377 u32 buf_fl;
378 u32 count;
379};
380
381// Check if DMA available and setup transfer if so.
382static int
383ata_try_dma(struct disk_op_s *op, int iswrite, int blocksize)
384{
Kevin O'Connor4d079022010-01-17 12:58:47 -0500385 if (! CONFIG_ATA_DMA)
386 return -1;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500387 u32 dest = (u32)op->buf_fl;
388 if (dest & 1)
389 // Need minimum alignment of 1.
390 return -1;
391 u8 ataid = GET_GLOBAL(op->drive_g->cntl_id);
392 u8 channel = ataid / 2;
393 u16 iomaster = GET_GLOBAL(ATA_channels[channel].iomaster);
394 if (! iomaster)
395 return -1;
396 u32 bytes = op->count * blocksize;
397 if (! bytes)
398 return -1;
399
400 // Build PRD dma structure.
401 struct sff_dma_prd *dma = MAKE_FLATPTR(
402 get_ebda_seg()
403 , (void*)offsetof(struct extended_bios_data_area_s, extra_stack));
404 struct sff_dma_prd *origdma = dma;
405 while (bytes) {
406 if (dma >= &origdma[16])
407 // Too many descriptors..
408 return -1;
409 u32 count = bytes;
410 if (count > 0x10000)
411 count = 0x10000;
412 u32 max = 0x10000 - (dest & 0xffff);
413 if (count > max)
414 count = max;
415
416 SET_FLATPTR(dma->buf_fl, dest);
417 bytes -= count;
418 if (!bytes)
419 // Last descriptor.
420 count |= 1<<31;
421 dprintf(16, "dma@%p: %08x %08x\n", dma, dest, count);
422 dest += count;
423 SET_FLATPTR(dma->count, count);
424 dma++;
425 }
426
427 // Program bus-master controller.
428 outl((u32)origdma, iomaster + BM_TABLE);
429 u8 oldcmd = inb(iomaster + BM_CMD) & ~(BM_CMD_MEMWRITE|BM_CMD_START);
430 outb(oldcmd | (iswrite ? 0x00 : BM_CMD_MEMWRITE), iomaster + BM_CMD);
431 outb(BM_STATUS_ERROR|BM_STATUS_IRQ, iomaster + BM_STATUS);
432
433 return 0;
434}
435
436// Transfer data using DMA.
437static int
438ata_dma_transfer(struct disk_op_s *op)
439{
Kevin O'Connor4d079022010-01-17 12:58:47 -0500440 if (! CONFIG_ATA_DMA)
441 return -1;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500442 dprintf(16, "ata_dma_transfer id=%p buf=%p\n"
443 , op->drive_g, op->buf_fl);
444
445 u8 ataid = GET_GLOBAL(op->drive_g->cntl_id);
446 u8 channel = ataid / 2;
447 u16 iomaster = GET_GLOBAL(ATA_channels[channel].iomaster);
448
449 // Start bus-master controller.
450 u8 oldcmd = inb(iomaster + BM_CMD);
451 outb(oldcmd | BM_CMD_START, iomaster + BM_CMD);
452
453 u64 end = calc_future_tsc(IDE_TIMEOUT);
454 u8 status;
455 for (;;) {
456 status = inb(iomaster + BM_STATUS);
457 if (status & BM_STATUS_IRQ)
458 break;
459 // Transfer in progress
460 if (check_time(end)) {
461 // Timeout.
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500462 warn_timeout();
Kevin O'Connor14021f22009-12-26 23:21:38 -0500463 break;
464 }
465 yield();
466 }
467 outb(oldcmd & ~BM_CMD_START, iomaster + BM_CMD);
468
469 u16 iobase1 = GET_GLOBAL(ATA_channels[channel].iobase1);
470 u16 iobase2 = GET_GLOBAL(ATA_channels[channel].iobase2);
471 int idestatus = pause_await_not_bsy(iobase1, iobase2);
472
473 if ((status & (BM_STATUS_IRQ|BM_STATUS_ACTIVE)) == BM_STATUS_IRQ
474 && idestatus >= 0x00
475 && (idestatus & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ
476 | ATA_CB_STAT_ERR)) == 0x00)
477 // Success.
478 return 0;
479
480 dprintf(6, "IDE DMA error (dma=%x ide=%x/%x/%x)\n", status, idestatus
481 , inb(iobase2 + ATA_CB_ASTAT), inb(iobase1 + ATA_CB_ERR));
482 op->count = 0;
483 return -1;
484}
485
486
487/****************************************************************
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400488 * ATA hard drive functions
489 ****************************************************************/
490
Kevin O'Connor14021f22009-12-26 23:21:38 -0500491// Transfer data to harddrive using PIO protocol.
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400492static int
Kevin O'Connor14021f22009-12-26 23:21:38 -0500493ata_pio_cmd_data(struct disk_op_s *op, int iswrite, struct ata_pio_command *cmd)
Kevin O'Connorf888f8c2008-03-23 00:04:54 -0400494{
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500495 u8 ataid = GET_GLOBAL(op->drive_g->cntl_id);
496 u8 channel = ataid / 2;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500497 u16 iobase1 = GET_GLOBAL(ATA_channels[channel].iobase1);
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500498 u16 iobase2 = GET_GLOBAL(ATA_channels[channel].iobase2);
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400499
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500500 // Disable interrupts
501 outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC);
502
Kevin O'Connor14021f22009-12-26 23:21:38 -0500503 int ret = send_cmd(op->drive_g, cmd);
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400504 if (ret)
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500505 goto fail;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500506 ret = ata_wait_data(iobase1);
507 if (ret)
508 goto fail;
509 ret = ata_pio_transfer(op, iswrite, DISK_SECTOR_SIZE);
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500510
511fail:
512 // Enable interrupts
513 outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);
514 return ret;
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400515}
516
Kevin O'Connor14021f22009-12-26 23:21:38 -0500517// Transfer data to harddrive using DMA protocol.
518static int
519ata_dma_cmd_data(struct disk_op_s *op, struct ata_pio_command *cmd)
520{
Kevin O'Connor4d079022010-01-17 12:58:47 -0500521 if (! CONFIG_ATA_DMA)
522 return -1;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500523 int ret = send_cmd(op->drive_g, cmd);
524 if (ret)
525 return ret;
526 return ata_dma_transfer(op);
527}
528
529// Read/write count blocks from a harddrive.
530static int
531ata_readwrite(struct disk_op_s *op, int iswrite)
532{
533 u64 lba = op->lba;
534
535 int usepio = ata_try_dma(op, iswrite, DISK_SECTOR_SIZE);
536
537 struct ata_pio_command cmd;
538 memset(&cmd, 0, sizeof(cmd));
539
540 if (op->count >= (1<<8) || lba + op->count >= (1<<28)) {
541 cmd.sector_count2 = op->count >> 8;
542 cmd.lba_low2 = lba >> 24;
543 cmd.lba_mid2 = lba >> 32;
544 cmd.lba_high2 = lba >> 40;
545 lba &= 0xffffff;
546
547 if (usepio)
548 cmd.command = (iswrite ? ATA_CMD_WRITE_SECTORS_EXT
549 : ATA_CMD_READ_SECTORS_EXT);
550 else
551 cmd.command = (iswrite ? ATA_CMD_WRITE_DMA_EXT
552 : ATA_CMD_READ_DMA_EXT);
553 } else {
554 if (usepio)
555 cmd.command = (iswrite ? ATA_CMD_WRITE_SECTORS
556 : ATA_CMD_READ_SECTORS);
557 else
558 cmd.command = (iswrite ? ATA_CMD_WRITE_DMA
559 : ATA_CMD_READ_DMA);
560 }
561
562 cmd.sector_count = op->count;
563 cmd.lba_low = lba;
564 cmd.lba_mid = lba >> 8;
565 cmd.lba_high = lba >> 16;
566 cmd.device = ((lba >> 24) & 0xf) | ATA_CB_DH_LBA;
567
568 int ret;
569 if (usepio)
570 ret = ata_pio_cmd_data(op, iswrite, &cmd);
571 else
572 ret = ata_dma_cmd_data(op, &cmd);
573 if (ret)
574 return DISK_RET_EBADTRACK;
575 return DISK_RET_SUCCESS;
576}
577
578// 16bit command demuxer for ATA harddrives.
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400579int
580process_ata_op(struct disk_op_s *op)
581{
Kevin O'Connorc892b132009-08-11 21:59:37 -0400582 if (!CONFIG_ATA)
583 return 0;
584
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400585 switch (op->command) {
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400586 case CMD_READ:
Kevin O'Connor14021f22009-12-26 23:21:38 -0500587 return ata_readwrite(op, 0);
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400588 case CMD_WRITE:
Kevin O'Connor14021f22009-12-26 23:21:38 -0500589 return ata_readwrite(op, 1);
Kevin O'Connor42337662009-08-10 00:06:37 -0400590 default:
591 return process_ata_misc_op(op);
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400592 }
Kevin O'Connorf888f8c2008-03-23 00:04:54 -0400593}
594
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400595
596/****************************************************************
597 * ATAPI functions
598 ****************************************************************/
599
Kevin O'Connor7d700252010-02-15 11:56:07 -0500600#define CDROM_CDB_SIZE 12
601
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400602// Low-level atapi command transmit function.
Kevin O'Connor7d700252010-02-15 11:56:07 -0500603int
604atapi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500605{
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500606 u8 ataid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connorb1144362009-08-11 20:43:38 -0400607 u8 channel = ataid / 2;
Kevin O'Connorc892b132009-08-11 21:59:37 -0400608 u16 iobase1 = GET_GLOBAL(ATA_channels[channel].iobase1);
609 u16 iobase2 = GET_GLOBAL(ATA_channels[channel].iobase2);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500610
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400611 struct ata_pio_command cmd;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500612 memset(&cmd, 0, sizeof(cmd));
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400613 cmd.lba_mid = blocksize;
614 cmd.lba_high = blocksize >> 8;
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400615 cmd.command = ATA_CMD_PACKET;
616
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500617 // Disable interrupts
618 outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC);
619
620 int ret = send_cmd(op->drive_g, &cmd);
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400621 if (ret)
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500622 goto fail;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500623 ret = ata_wait_data(iobase1);
624 if (ret)
625 goto fail;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500626
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400627 // Send command to device
Kevin O'Connor7d700252010-02-15 11:56:07 -0500628 outsw_fl(iobase1, MAKE_FLATPTR(GET_SEG(SS), cdbcmd), CDROM_CDB_SIZE / 2);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500629
Kevin O'Connor580e3322009-02-06 22:36:53 -0500630 int status = pause_await_not_bsy(iobase1, iobase2);
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500631 if (status < 0) {
632 ret = status;
633 goto fail;
634 }
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400635
Kevin O'Connor580e3322009-02-06 22:36:53 -0500636 if (status & ATA_CB_STAT_ERR) {
Kevin O'Connorb30c4002009-05-05 21:47:20 -0400637 u8 err = inb(iobase1 + ATA_CB_ERR);
638 // skip "Not Ready"
639 if (err != 0x20)
640 dprintf(6, "send_atapi_cmd : read error (status=%02x err=%02x)\n"
641 , status, err);
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500642 ret = -2;
643 goto fail;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500644 }
645 if (!(status & ATA_CB_STAT_DRQ)) {
646 dprintf(6, "send_atapi_cmd : DRQ not set (status %02x)\n", status);
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500647 ret = -3;
648 goto fail;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500649 }
650
Kevin O'Connor14021f22009-12-26 23:21:38 -0500651 ret = ata_pio_transfer(op, 0, blocksize);
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500652
653fail:
654 // Enable interrupts
655 outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);
656 return ret;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500657}
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500658
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400659// Read sectors from the cdrom.
660int
661cdrom_read(struct disk_op_s *op)
Kevin O'Connor180a9592008-03-04 22:50:53 -0500662{
Kevin O'Connor7d700252010-02-15 11:56:07 -0500663 struct cdb_rwdata_10 cmd;
664 memset(&cmd, 0, sizeof(cmd));
665 cmd.command = CDB_CMD_READ_10;
666 cmd.lba = htonl(op->lba);
667 cmd.count = htons(op->count);
668 return atapi_cmd_data(op, &cmd, CDROM_SECTOR_SIZE);
Kevin O'Connoraa2590c2008-03-22 23:13:24 -0400669}
670
Kevin O'Connor14021f22009-12-26 23:21:38 -0500671// 16bit command demuxer for ATAPI cdroms.
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400672int
673process_atapi_op(struct disk_op_s *op)
674{
Kevin O'Connor126eac62009-08-16 13:32:24 -0400675 int ret;
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400676 switch (op->command) {
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400677 case CMD_READ:
Kevin O'Connor126eac62009-08-16 13:32:24 -0400678 ret = cdrom_read(op);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500679 if (ret)
680 return DISK_RET_EBADTRACK;
681 return DISK_RET_SUCCESS;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400682 case CMD_FORMAT:
683 case CMD_WRITE:
684 return DISK_RET_EWRITEPROTECT;
Kevin O'Connor42337662009-08-10 00:06:37 -0400685 default:
686 return process_ata_misc_op(op);
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400687 }
688}
689
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400690
691/****************************************************************
Kevin O'Connor0a924122009-02-08 19:43:47 -0500692 * ATA detect and init
693 ****************************************************************/
694
Kevin O'Connor14021f22009-12-26 23:21:38 -0500695// Send an identify device or identify device packet command.
696static int
697send_ata_identity(struct drive_s *drive_g, u16 *buffer, int command)
698{
699 memset(buffer, 0, DISK_SECTOR_SIZE);
700
701 struct disk_op_s dop;
702 memset(&dop, 0, sizeof(dop));
703 dop.drive_g = drive_g;
704 dop.count = 1;
705 dop.lba = 1;
706 dop.buf_fl = MAKE_FLATPTR(GET_SEG(SS), buffer);
707
708 struct ata_pio_command cmd;
709 memset(&cmd, 0, sizeof(cmd));
710 cmd.command = command;
711
712 return ata_pio_cmd_data(&dop, 0, &cmd);
713}
714
Kevin O'Connorf2d48a32009-08-11 20:58:11 -0400715// Extract the ATA/ATAPI version info.
716static int
717extract_version(u16 *buffer)
718{
719 // Extract ATA/ATAPI version.
720 u16 ataversion = buffer[80];
721 u8 version;
722 for (version=15; version>0; version--)
723 if (ataversion & (1<<version))
724 break;
725 return version;
726}
727
Kevin O'Connor160f71c2009-02-11 22:46:29 -0500728// Extract common information from IDENTIFY commands.
Kevin O'Connor0a924122009-02-08 19:43:47 -0500729static void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400730extract_identify(struct drive_s *drive_g, u16 *buffer)
Kevin O'Connor0a924122009-02-08 19:43:47 -0500731{
Kevin O'Connor160f71c2009-02-11 22:46:29 -0500732 dprintf(3, "Identify w0=%x w2=%x\n", buffer[0], buffer[2]);
Kevin O'Connor0a924122009-02-08 19:43:47 -0500733
734 // Read model name
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400735 char *model = drive_g->model;
736 int maxsize = ARRAY_SIZE(drive_g->model);
Kevin O'Connor0a924122009-02-08 19:43:47 -0500737 int i;
Kevin O'Connorab515602009-02-11 22:22:15 -0500738 for (i=0; i<maxsize/2; i++) {
739 u16 v = buffer[27+i];
740 model[i*2] = v >> 8;
741 model[i*2+1] = v & 0xff;
Kevin O'Connor0a924122009-02-08 19:43:47 -0500742 }
743 model[maxsize-1] = 0x00;
744
Kevin O'Connor160f71c2009-02-11 22:46:29 -0500745 // Trim trailing spaces from model name.
Kevin O'Connor0a924122009-02-08 19:43:47 -0500746 for (i=maxsize-2; i>0 && model[i] == 0x20; i--)
747 model[i] = 0x00;
Kevin O'Connor0a924122009-02-08 19:43:47 -0500748
Kevin O'Connor160f71c2009-02-11 22:46:29 -0500749 // Common flags.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400750 SET_GLOBAL(drive_g->removable, (buffer[0] & 0x80) ? 1 : 0);
751 SET_GLOBAL(drive_g->cntl_info, extract_version(buffer));
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400752}
753
Kevin O'Connor14021f22009-12-26 23:21:38 -0500754// Print out a description of the given atapi drive.
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400755void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400756describe_atapi(struct drive_s *drive_g)
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400757{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400758 u8 ataid = drive_g->cntl_id;
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400759 u8 channel = ataid / 2;
760 u8 slave = ataid % 2;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400761 u8 version = drive_g->cntl_info;
762 int iscd = drive_g->floppy_type;
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400763 printf("ata%d-%d: %s ATAPI-%d %s", channel, slave
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400764 , drive_g->model, version
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400765 , (iscd ? "CD-Rom/DVD-Rom" : "Device"));
Kevin O'Connor0a924122009-02-08 19:43:47 -0500766}
767
Kevin O'Connor14021f22009-12-26 23:21:38 -0500768// Detect if the given drive is an atapi - initialize it if so.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400769static struct drive_s *
770init_drive_atapi(struct drive_s *dummy, u16 *buffer)
Kevin O'Connor0a924122009-02-08 19:43:47 -0500771{
772 // Send an IDENTIFY_DEVICE_PACKET command to device
Kevin O'Connor14021f22009-12-26 23:21:38 -0500773 int ret = send_ata_identity(dummy, buffer, ATA_CMD_IDENTIFY_PACKET_DEVICE);
Kevin O'Connor0a924122009-02-08 19:43:47 -0500774 if (ret)
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400775 return NULL;
Kevin O'Connor0a924122009-02-08 19:43:47 -0500776
777 // Success - setup as ATAPI.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400778 struct drive_s *drive_g = allocDrive();
779 if (! drive_g)
780 return NULL;
781 SET_GLOBAL(drive_g->cntl_id, dummy->cntl_id);
782 extract_identify(drive_g, buffer);
783 SET_GLOBAL(drive_g->type, DTYPE_ATAPI);
784 SET_GLOBAL(drive_g->blksize, CDROM_SECTOR_SIZE);
785 SET_GLOBAL(drive_g->sectors, (u64)-1);
Kevin O'Connor42337662009-08-10 00:06:37 -0400786 u8 iscd = ((buffer[0] >> 8) & 0x1f) == 0x05;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400787 SET_GLOBAL(drive_g->floppy_type, iscd);
Kevin O'Connor42337662009-08-10 00:06:37 -0400788
789 // fill cdidmap
Kevin O'Connorc892b132009-08-11 21:59:37 -0400790 if (iscd)
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400791 map_cd_drive(drive_g);
Kevin O'Connor0a924122009-02-08 19:43:47 -0500792
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400793 return drive_g;
Kevin O'Connor0a924122009-02-08 19:43:47 -0500794}
795
Kevin O'Connor14021f22009-12-26 23:21:38 -0500796// Print out a description of the given ata drive.
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400797void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400798describe_ata(struct drive_s *drive_g)
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400799{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400800 u8 ataid = drive_g->cntl_id;
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400801 u8 channel = ataid / 2;
802 u8 slave = ataid % 2;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400803 u64 sectors = drive_g->sectors;
804 u8 version = drive_g->cntl_info;
805 char *model = drive_g->model;
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400806 printf("ata%d-%d: %s ATA-%d Hard-Disk", channel, slave, model, version);
807 u64 sizeinmb = sectors >> 11;
808 if (sizeinmb < (1 << 16))
809 printf(" (%u MiBytes)", (u32)sizeinmb);
810 else
811 printf(" (%u GiBytes)", (u32)(sizeinmb >> 10));
812}
813
Kevin O'Connor14021f22009-12-26 23:21:38 -0500814// Detect if the given drive is a regular ata drive - initialize it if so.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400815static struct drive_s *
816init_drive_ata(struct drive_s *dummy, u16 *buffer)
Kevin O'Connorc1437612008-05-18 01:43:07 -0400817{
Kevin O'Connor580e3322009-02-06 22:36:53 -0500818 // Send an IDENTIFY_DEVICE command to device
Kevin O'Connor14021f22009-12-26 23:21:38 -0500819 int ret = send_ata_identity(dummy, buffer, ATA_CMD_IDENTIFY_DEVICE);
Kevin O'Connorc1437612008-05-18 01:43:07 -0400820 if (ret)
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400821 return NULL;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500822
823 // Success - setup as ATA.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400824 struct drive_s *drive_g = allocDrive();
825 if (! drive_g)
826 return NULL;
827 SET_GLOBAL(drive_g->cntl_id, dummy->cntl_id);
828 extract_identify(drive_g, buffer);
829 SET_GLOBAL(drive_g->type, DTYPE_ATA);
830 SET_GLOBAL(drive_g->blksize, DISK_SECTOR_SIZE);
Kevin O'Connorc1437612008-05-18 01:43:07 -0400831
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400832 SET_GLOBAL(drive_g->pchs.cylinders, buffer[1]);
833 SET_GLOBAL(drive_g->pchs.heads, buffer[3]);
834 SET_GLOBAL(drive_g->pchs.spt, buffer[6]);
Kevin O'Connorc1437612008-05-18 01:43:07 -0400835
836 u64 sectors;
Kevin O'Connorab515602009-02-11 22:22:15 -0500837 if (buffer[83] & (1 << 10)) // word 83 - lba48 support
838 sectors = *(u64*)&buffer[100]; // word 100-103
Kevin O'Connorc1437612008-05-18 01:43:07 -0400839 else
Kevin O'Connorab515602009-02-11 22:22:15 -0500840 sectors = *(u32*)&buffer[60]; // word 60 and word 61
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400841 SET_GLOBAL(drive_g->sectors, sectors);
Kevin O'Connorc1437612008-05-18 01:43:07 -0400842
843 // Setup disk geometry translation.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400844 setup_translation(drive_g);
Kevin O'Connoraafa6572008-03-13 19:57:49 -0400845
Kevin O'Connor0a924122009-02-08 19:43:47 -0500846 // Register with bcv system.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400847 add_bcv_internal(drive_g);
Kevin O'Connoraafa6572008-03-13 19:57:49 -0400848
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400849 return drive_g;
Kevin O'Connoraafa6572008-03-13 19:57:49 -0400850}
851
Kevin O'Connora5826b52009-10-24 17:57:29 -0400852static u64 SpinupEnd;
853
Kevin O'Connor14021f22009-12-26 23:21:38 -0500854// Wait for non-busy status and check for "floating bus" condition.
Kevin O'Connor425f2122009-04-18 12:23:00 -0400855static int
Kevin O'Connora5826b52009-10-24 17:57:29 -0400856powerup_await_non_bsy(u16 base)
Kevin O'Connor425f2122009-04-18 12:23:00 -0400857{
858 u8 orstatus = 0;
859 u8 status;
860 for (;;) {
861 status = inb(base+ATA_CB_STAT);
862 if (!(status & ATA_CB_STAT_BSY))
863 break;
864 orstatus |= status;
865 if (orstatus == 0xff) {
866 dprintf(1, "powerup IDE floating\n");
867 return orstatus;
868 }
Kevin O'Connora5826b52009-10-24 17:57:29 -0400869 if (check_time(SpinupEnd)) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500870 warn_timeout();
Kevin O'Connor425f2122009-04-18 12:23:00 -0400871 return -1;
872 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400873 yield();
Kevin O'Connor425f2122009-04-18 12:23:00 -0400874 }
875 dprintf(6, "powerup iobase=%x st=%x\n", base, status);
876 return status;
877}
878
Kevin O'Connor14021f22009-12-26 23:21:38 -0500879// Detect any drives attached to a given controller.
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -0400880static void
Kevin O'Connora5826b52009-10-24 17:57:29 -0400881ata_detect(void *data)
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500882{
Kevin O'Connora5826b52009-10-24 17:57:29 -0400883 struct ata_channel_s *atachannel = data;
884 int startid = (atachannel - ATA_channels) * 2;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400885 struct drive_s dummy;
886 memset(&dummy, 0, sizeof(dummy));
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500887 // Device detection
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400888 int ataid, last_reset_ataid=-1;
Kevin O'Connora5826b52009-10-24 17:57:29 -0400889 for (ataid=startid; ataid<startid+2; ataid++) {
Kevin O'Connorb1144362009-08-11 20:43:38 -0400890 u8 channel = ataid / 2;
891 u8 slave = ataid % 2;
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500892
Kevin O'Connorc892b132009-08-11 21:59:37 -0400893 u16 iobase1 = GET_GLOBAL(ATA_channels[channel].iobase1);
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400894 if (!iobase1)
895 break;
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500896
Kevin O'Connor425f2122009-04-18 12:23:00 -0400897 // Wait for not-bsy.
Kevin O'Connora5826b52009-10-24 17:57:29 -0400898 int status = powerup_await_non_bsy(iobase1);
Kevin O'Connor425f2122009-04-18 12:23:00 -0400899 if (status < 0)
900 continue;
901 u8 newdh = slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0;
902 outb(newdh, iobase1+ATA_CB_DH);
Kevin O'Connorc946eca2009-05-24 13:55:01 -0400903 ndelay(400);
Kevin O'Connora5826b52009-10-24 17:57:29 -0400904 status = powerup_await_non_bsy(iobase1);
Kevin O'Connor425f2122009-04-18 12:23:00 -0400905 if (status < 0)
906 continue;
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500907
Kevin O'Connor580e3322009-02-06 22:36:53 -0500908 // Check if ioport registers look valid.
Kevin O'Connor425f2122009-04-18 12:23:00 -0400909 outb(newdh, iobase1+ATA_CB_DH);
910 u8 dh = inb(iobase1+ATA_CB_DH);
911 outb(0x55, iobase1+ATA_CB_SC);
912 outb(0xaa, iobase1+ATA_CB_SN);
Kevin O'Connoraafa6572008-03-13 19:57:49 -0400913 u8 sc = inb(iobase1+ATA_CB_SC);
914 u8 sn = inb(iobase1+ATA_CB_SN);
Kevin O'Connorb1144362009-08-11 20:43:38 -0400915 dprintf(6, "ata_detect ataid=%d sc=%x sn=%x dh=%x\n"
916 , ataid, sc, sn, dh);
Kevin O'Connor425f2122009-04-18 12:23:00 -0400917 if (sc != 0x55 || sn != 0xaa || dh != newdh)
Kevin O'Connoraafa6572008-03-13 19:57:49 -0400918 continue;
919
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400920 // Prepare new drive.
921 dummy.cntl_id = ataid;
Kevin O'Connorb1144362009-08-11 20:43:38 -0400922
Kevin O'Connoraafa6572008-03-13 19:57:49 -0400923 // reset the channel
Kevin O'Connorb1144362009-08-11 20:43:38 -0400924 if (slave && ataid == last_reset_ataid + 1) {
Kevin O'Connor580e3322009-02-06 22:36:53 -0500925 // The drive was just reset - no need to reset it again.
926 } else {
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400927 ata_reset(&dummy);
Kevin O'Connorb1144362009-08-11 20:43:38 -0400928 last_reset_ataid = ataid;
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500929 }
930
Kevin O'Connor580e3322009-02-06 22:36:53 -0500931 // check for ATAPI
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400932 u16 buffer[256];
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400933 struct drive_s *drive_g = init_drive_atapi(&dummy, buffer);
934 if (!drive_g) {
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400935 // Didn't find an ATAPI drive - look for ATA drive.
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400936 u8 st = inb(iobase1+ATA_CB_STAT);
937 if (!st)
938 // Status not set - can't be a valid drive.
939 continue;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500940
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400941 // Wait for RDY.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400942 int ret = await_rdy(iobase1);
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400943 if (ret < 0)
944 continue;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500945
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400946 // check for ATA.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400947 drive_g = init_drive_ata(&dummy, buffer);
948 if (!drive_g)
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400949 // No ATA drive found
950 continue;
951 }
Kevin O'Connor580e3322009-02-06 22:36:53 -0500952
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400953 u16 resetresult = buffer[93];
954 dprintf(6, "ata_detect resetresult=%04x\n", resetresult);
955 if (!slave && (resetresult & 0xdf61) == 0x4041)
956 // resetresult looks valid and device 0 is responding to
957 // device 1 requests - device 1 must not be present - skip
958 // detection.
Kevin O'Connorb1144362009-08-11 20:43:38 -0400959 ataid++;
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500960 }
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500961}
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -0400962
Kevin O'Connor14021f22009-12-26 23:21:38 -0500963// Initialize an ata controller and detect its drives.
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -0400964static void
Kevin O'Connor4ccb2312009-12-05 11:25:09 -0500965init_controller(struct ata_channel_s *atachannel
Kevin O'Connor14021f22009-12-26 23:21:38 -0500966 , int bdf, int irq, u32 port1, u32 port2, u32 master)
Kevin O'Connor4ccb2312009-12-05 11:25:09 -0500967{
968 SET_GLOBAL(atachannel->irq, irq);
969 SET_GLOBAL(atachannel->pci_bdf, bdf);
970 SET_GLOBAL(atachannel->iobase1, port1);
971 SET_GLOBAL(atachannel->iobase2, port2);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500972 SET_GLOBAL(atachannel->iomaster, master);
973 dprintf(1, "ATA controller %d at %x/%x/%x (irq %d dev %x)\n"
974 , atachannel - ATA_channels, port1, port2, master, irq, bdf);
Kevin O'Connor4ccb2312009-12-05 11:25:09 -0500975 run_thread(ata_detect, atachannel);
976}
977
Kevin O'Connor525219b2009-12-05 13:36:18 -0500978#define IRQ_ATA1 14
979#define IRQ_ATA2 15
980
Kevin O'Connor14021f22009-12-26 23:21:38 -0500981// Locate and init ata controllers.
Kevin O'Connor4ccb2312009-12-05 11:25:09 -0500982static void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500983ata_init(void)
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -0400984{
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400985 // Scan PCI bus for ATA adapters
Kevin O'Connor4ccb2312009-12-05 11:25:09 -0500986 int count=0, pcicount=0;
Kevin O'Connor53ab0b62008-12-04 19:22:49 -0500987 int bdf, max;
Kevin O'Connor4132e022008-12-04 19:39:10 -0500988 foreachpci(bdf, max) {
Kevin O'Connor4ccb2312009-12-05 11:25:09 -0500989 pcicount++;
Kevin O'Connor53ab0b62008-12-04 19:22:49 -0500990 if (pci_config_readw(bdf, PCI_CLASS_DEVICE) != PCI_CLASS_STORAGE_IDE)
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400991 continue;
Kevin O'Connorc892b132009-08-11 21:59:37 -0400992 if (count >= ARRAY_SIZE(ATA_channels))
Kevin O'Connor89f67632009-02-11 20:16:49 -0500993 break;
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400994
Kevin O'Connor525219b2009-12-05 13:36:18 -0500995 u8 pciirq = pci_config_readb(bdf, PCI_INTERRUPT_LINE);
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500996 u8 prog_if = pci_config_readb(bdf, PCI_CLASS_PROG);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500997 int master = 0;
Kevin O'Connor4d079022010-01-17 12:58:47 -0500998 if (CONFIG_ATA_DMA && prog_if & 0x80) {
Kevin O'Connor14021f22009-12-26 23:21:38 -0500999 // Check for bus-mastering.
1000 u32 bar = pci_config_readl(bdf, PCI_BASE_ADDRESS_4);
1001 if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
1002 master = bar & PCI_BASE_ADDRESS_IO_MASK;
1003 pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER);
1004 }
1005 }
1006
Kevin O'Connor525219b2009-12-05 13:36:18 -05001007 u32 port1, port2, irq;
Kevin O'Connor53ab0b62008-12-04 19:22:49 -05001008 if (prog_if & 1) {
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -05001009 port1 = pci_config_readl(bdf, PCI_BASE_ADDRESS_0) & ~3;
1010 port2 = pci_config_readl(bdf, PCI_BASE_ADDRESS_1) & ~3;
Kevin O'Connor525219b2009-12-05 13:36:18 -05001011 irq = pciirq;
Kevin O'Connor53236cc2008-08-31 11:16:33 -04001012 } else {
Kevin O'Connor4ccb2312009-12-05 11:25:09 -05001013 port1 = PORT_ATA1_CMD_BASE;
1014 port2 = PORT_ATA1_CTRL_BASE;
Kevin O'Connor525219b2009-12-05 13:36:18 -05001015 irq = IRQ_ATA1;
Kevin O'Connor53236cc2008-08-31 11:16:33 -04001016 }
Kevin O'Connor14021f22009-12-26 23:21:38 -05001017 init_controller(&ATA_channels[count], bdf, irq, port1, port2, master);
Kevin O'Connor53236cc2008-08-31 11:16:33 -04001018 count++;
1019
Kevin O'Connor53ab0b62008-12-04 19:22:49 -05001020 if (prog_if & 4) {
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -05001021 port1 = pci_config_readl(bdf, PCI_BASE_ADDRESS_2) & ~3;
1022 port2 = pci_config_readl(bdf, PCI_BASE_ADDRESS_3) & ~3;
Kevin O'Connor525219b2009-12-05 13:36:18 -05001023 irq = pciirq;
Kevin O'Connor53236cc2008-08-31 11:16:33 -04001024 } else {
Kevin O'Connor4ccb2312009-12-05 11:25:09 -05001025 port1 = PORT_ATA2_CMD_BASE;
1026 port2 = PORT_ATA2_CTRL_BASE;
Kevin O'Connor525219b2009-12-05 13:36:18 -05001027 irq = IRQ_ATA2;
Kevin O'Connor53236cc2008-08-31 11:16:33 -04001028 }
Kevin O'Connor14021f22009-12-26 23:21:38 -05001029 init_controller(&ATA_channels[count], bdf, irq, port1, port2
1030 , master ? master + 8 : 0);
Kevin O'Connor53236cc2008-08-31 11:16:33 -04001031 count++;
1032 }
Kevin O'Connor4ccb2312009-12-05 11:25:09 -05001033
1034 if (!CONFIG_COREBOOT && !pcicount && ARRAY_SIZE(ATA_channels) >= 2) {
1035 // No PCI devices found - probably a QEMU "-M isapc" machine.
1036 // Try using ISA ports for ATA controllers.
Kevin O'Connor14021f22009-12-26 23:21:38 -05001037 init_controller(&ATA_channels[0], -1, IRQ_ATA1
1038 , PORT_ATA1_CMD_BASE, PORT_ATA1_CTRL_BASE, 0);
1039 init_controller(&ATA_channels[1], -1, IRQ_ATA2
1040 , PORT_ATA2_CMD_BASE, PORT_ATA2_CTRL_BASE, 0);
Kevin O'Connor4ccb2312009-12-05 11:25:09 -05001041 }
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -04001042}
1043
1044void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -05001045ata_setup(void)
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -04001046{
Kevin O'Connor59c75742010-02-13 18:49:24 -05001047 ASSERT32FLAT();
Kevin O'Connorc1437612008-05-18 01:43:07 -04001048 if (!CONFIG_ATA)
1049 return;
1050
Kevin O'Connor35192dd2008-06-08 19:18:33 -04001051 dprintf(3, "init hard drives\n");
Kevin O'Connora5826b52009-10-24 17:57:29 -04001052
1053 SpinupEnd = calc_future_tsc(IDE_TIMEOUT);
Kevin O'Connorc1437612008-05-18 01:43:07 -04001054 ata_init();
Kevin O'Connorc1437612008-05-18 01:43:07 -04001055
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -04001056 SET_BDA(disk_control_byte, 0xc0);
Kevin O'Connorf54c1502008-06-14 15:56:16 -04001057
Kevin O'Connord21c0892008-11-26 17:02:43 -05001058 enable_hwirq(14, entry_76);
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -04001059}