blob: f604b3726d3e85d7a8e77ff5369a6f96d47a3570 [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'Connorb3064592012-08-14 21:20:10 -040011#include "byteorder.h" // be16_to_cpu
Kevin O'Connor15aee2e2008-03-01 13:34:04 -050012#include "cmos.h" // inb_cmos
Kevin O'Connord21c0892008-11-26 17:02:43 -050013#include "pic.h" // enable_hwirq
Kevin O'Connor46b82622012-05-13 12:10:30 -040014#include "biosvar.h" // GET_GLOBAL
Kevin O'Connor3f3e58d2011-06-20 22:20:43 -040015#include "pci.h" // foreachpci
Kevin O'Connor2ed2f582008-11-08 15:53:36 -050016#include "pci_ids.h" // PCI_CLASS_STORAGE_OTHER
17#include "pci_regs.h" // PCI_INTERRUPT_LINE
Kevin O'Connor72eee3e2010-12-27 19:07:49 -050018#include "boot.h" // boot_add_hd
Kevin O'Connor609da232008-12-28 23:18:57 -050019#include "disk.h" // struct ata_s
Kevin O'Connorc892b132009-08-11 21:59:37 -040020#include "ata.h" // ATA_CB_STAT
Kevin O'Connor7d700252010-02-15 11:56:07 -050021#include "blockcmd.h" // CDB_CMD_READ_10
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050022
Kevin O'Connor425f2122009-04-18 12:23:00 -040023#define IDE_TIMEOUT 32000 //32 seconds max for IDE ops
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050024
Kevin O'Connor15aee2e2008-03-01 13:34:04 -050025
Kevin O'Connora6b9f712008-03-29 12:53:57 -040026/****************************************************************
27 * Helper functions
28 ****************************************************************/
29
30// Wait for the specified ide state
Kevin O'Connor580e3322009-02-06 22:36:53 -050031static inline int
32await_ide(u8 mask, u8 flags, u16 base, u16 timeout)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050033{
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050034 u64 end = calc_future_tsc(timeout);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050035 for (;;) {
Kevin O'Connora6b9f712008-03-29 12:53:57 -040036 u8 status = inb(base+ATA_CB_STAT);
Kevin O'Connor580e3322009-02-06 22:36:53 -050037 if ((status & mask) == flags)
Kevin O'Connora6b9f712008-03-29 12:53:57 -040038 return status;
Kevin O'Connor144817b2010-05-23 10:46:49 -040039 if (check_tsc(end)) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -050040 warn_timeout();
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050041 return -1;
42 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -040043 yield();
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050044 }
Kevin O'Connor580e3322009-02-06 22:36:53 -050045}
46
47// Wait for the device to be not-busy.
48static int
49await_not_bsy(u16 base)
50{
51 return await_ide(ATA_CB_STAT_BSY, 0, base, IDE_TIMEOUT);
52}
53
54// Wait for the device to be ready.
55static int
56await_rdy(u16 base)
57{
58 return await_ide(ATA_CB_STAT_RDY, ATA_CB_STAT_RDY, base, IDE_TIMEOUT);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050059}
60
Kevin O'Connora6b9f712008-03-29 12:53:57 -040061// Wait for ide state - pauses for one ata cycle first.
Kevin O'Connora9caeae2009-03-07 00:09:52 -050062static inline int
Kevin O'Connor580e3322009-02-06 22:36:53 -050063pause_await_not_bsy(u16 iobase1, u16 iobase2)
Kevin O'Connora6b9f712008-03-29 12:53:57 -040064{
65 // Wait one PIO transfer cycle.
66 inb(iobase2 + ATA_CB_ASTAT);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050067
Kevin O'Connor580e3322009-02-06 22:36:53 -050068 return await_not_bsy(iobase1);
Kevin O'Connora6b9f712008-03-29 12:53:57 -040069}
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050070
Kevin O'Connoref2822a2008-06-07 15:23:11 -040071// Wait for ide state - pause for 400ns first.
Kevin O'Connora9caeae2009-03-07 00:09:52 -050072static inline int
Kevin O'Connor580e3322009-02-06 22:36:53 -050073ndelay_await_not_bsy(u16 iobase1)
Kevin O'Connoref2822a2008-06-07 15:23:11 -040074{
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -050075 ndelay(400);
Kevin O'Connor580e3322009-02-06 22:36:53 -050076 return await_not_bsy(iobase1);
Kevin O'Connoref2822a2008-06-07 15:23:11 -040077}
78
Kevin O'Connora6b9f712008-03-29 12:53:57 -040079// Reset a drive
Kevin O'Connorb1144362009-08-11 20:43:38 -040080static void
Kevin O'Connor8f469b92010-02-28 01:28:11 -050081ata_reset(struct atadrive_s *adrive_g)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050082{
Kevin O'Connor8f469b92010-02-28 01:28:11 -050083 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
84 u8 slave = GET_GLOBAL(adrive_g->slave);
85 u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
86 u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050087
Kevin O'Connor8f469b92010-02-28 01:28:11 -050088 dprintf(6, "ata_reset drive=%p\n", &adrive_g->drive);
Kevin O'Connor580e3322009-02-06 22:36:53 -050089 // Pulse SRST
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050090 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 -050091 udelay(5);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050092 outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2+ATA_CB_DC);
Kevin O'Connor10ad7992009-10-24 11:06:08 -040093 msleep(2);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050094
Kevin O'Connor580e3322009-02-06 22:36:53 -050095 // wait for device to become not busy.
96 int status = await_not_bsy(iobase1);
97 if (status < 0)
98 goto done;
99 if (slave) {
100 // Change device.
101 u64 end = calc_future_tsc(IDE_TIMEOUT);
102 for (;;) {
103 outb(ATA_CB_DH_DEV1, iobase1 + ATA_CB_DH);
Kevin O'Connorc946eca2009-05-24 13:55:01 -0400104 status = ndelay_await_not_bsy(iobase1);
Kevin O'Connor580e3322009-02-06 22:36:53 -0500105 if (status < 0)
106 goto done;
107 if (inb(iobase1 + ATA_CB_DH) == ATA_CB_DH_DEV1)
108 break;
109 // Change drive request failed to take effect - retry.
Kevin O'Connor144817b2010-05-23 10:46:49 -0400110 if (check_tsc(end)) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500111 warn_timeout();
Kevin O'Connor580e3322009-02-06 22:36:53 -0500112 goto done;
113 }
114 }
Kevin O'Connorf5624d22009-08-18 22:17:57 -0400115 } else {
116 // QEMU doesn't reset dh on reset, so set it explicitly.
117 outb(ATA_CB_DH_DEV0, iobase1 + ATA_CB_DH);
Kevin O'Connor2e3eeeb2008-06-12 22:29:30 -0400118 }
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500119
Kevin O'Connor580e3322009-02-06 22:36:53 -0500120 // On a user-reset request, wait for RDY if it is an ATA device.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500121 u8 type=GET_GLOBAL(adrive_g->drive.type);
Kevin O'Connor42337662009-08-10 00:06:37 -0400122 if (type == DTYPE_ATA)
Kevin O'Connor580e3322009-02-06 22:36:53 -0500123 status = await_rdy(iobase1);
Kevin O'Connor3e1b6492008-05-26 15:06:40 -0400124
Kevin O'Connor580e3322009-02-06 22:36:53 -0500125done:
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500126 // Enable interrupts
127 outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);
Kevin O'Connor580e3322009-02-06 22:36:53 -0500128
129 dprintf(6, "ata_reset exit status=%x\n", status);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500130}
131
Kevin O'Connor14021f22009-12-26 23:21:38 -0500132// Check for drive RDY for 16bit interface command.
Kevin O'Connor42337662009-08-10 00:06:37 -0400133static int
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500134isready(struct atadrive_s *adrive_g)
Kevin O'Connor42337662009-08-10 00:06:37 -0400135{
136 // Read the status from controller
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500137 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
138 u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
Kevin O'Connor42337662009-08-10 00:06:37 -0400139 u8 status = inb(iobase1 + ATA_CB_STAT);
Kevin O'Connor126eac62009-08-16 13:32:24 -0400140 if ((status & (ATA_CB_STAT_BSY|ATA_CB_STAT_RDY)) == ATA_CB_STAT_RDY)
141 return DISK_RET_SUCCESS;
142 return DISK_RET_ENOTREADY;
Kevin O'Connor42337662009-08-10 00:06:37 -0400143}
144
Kevin O'Connoree55c762008-05-13 00:18:20 -0400145
146/****************************************************************
147 * ATA send command
148 ****************************************************************/
149
Kevin O'Connorf888f8c2008-03-23 00:04:54 -0400150struct ata_pio_command {
Kevin O'Connorf888f8c2008-03-23 00:04:54 -0400151 u8 feature;
152 u8 sector_count;
153 u8 lba_low;
154 u8 lba_mid;
155 u8 lba_high;
156 u8 device;
157 u8 command;
158
Kevin O'Connor14021f22009-12-26 23:21:38 -0500159 u8 feature2;
Kevin O'Connorf888f8c2008-03-23 00:04:54 -0400160 u8 sector_count2;
161 u8 lba_low2;
162 u8 lba_mid2;
163 u8 lba_high2;
164};
165
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400166// Send an ata command to the drive.
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400167static int
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500168send_cmd(struct atadrive_s *adrive_g, struct ata_pio_command *cmd)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500169{
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500170 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
171 u8 slave = GET_GLOBAL(adrive_g->slave);
172 u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
Kevin O'Connoref2822a2008-06-07 15:23:11 -0400173
174 // Select device
Kevin O'Connor580e3322009-02-06 22:36:53 -0500175 int status = await_not_bsy(iobase1);
176 if (status < 0)
177 return status;
178 u8 newdh = ((cmd->device & ~ATA_CB_DH_DEV1)
179 | (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0));
180 u8 olddh = inb(iobase1 + ATA_CB_DH);
181 outb(newdh, iobase1 + ATA_CB_DH);
182 if ((olddh ^ newdh) & (1<<4)) {
183 // Was a device change - wait for device to become not busy.
Kevin O'Connorc946eca2009-05-24 13:55:01 -0400184 status = ndelay_await_not_bsy(iobase1);
Kevin O'Connor580e3322009-02-06 22:36:53 -0500185 if (status < 0)
186 return status;
187 }
Kevin O'Connoref2822a2008-06-07 15:23:11 -0400188
Kevin O'Connor14021f22009-12-26 23:21:38 -0500189 // Check for ATA_CMD_(READ|WRITE)_(SECTORS|DMA)_EXT commands.
190 if ((cmd->command & ~0x11) == ATA_CMD_READ_SECTORS_EXT) {
191 outb(cmd->feature2, iobase1 + ATA_CB_FR);
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400192 outb(cmd->sector_count2, iobase1 + ATA_CB_SC);
193 outb(cmd->lba_low2, iobase1 + ATA_CB_SN);
194 outb(cmd->lba_mid2, iobase1 + ATA_CB_CL);
195 outb(cmd->lba_high2, iobase1 + ATA_CB_CH);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500196 }
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400197 outb(cmd->feature, iobase1 + ATA_CB_FR);
198 outb(cmd->sector_count, iobase1 + ATA_CB_SC);
199 outb(cmd->lba_low, iobase1 + ATA_CB_SN);
200 outb(cmd->lba_mid, iobase1 + ATA_CB_CL);
201 outb(cmd->lba_high, iobase1 + ATA_CB_CH);
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400202 outb(cmd->command, iobase1 + ATA_CB_CMD);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500203
Kevin O'Connor14021f22009-12-26 23:21:38 -0500204 return 0;
205}
206
207// Wait for data after calling 'send_cmd'.
208static int
209ata_wait_data(u16 iobase1)
210{
211 int status = ndelay_await_not_bsy(iobase1);
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400212 if (status < 0)
213 return status;
214
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500215 if (status & ATA_CB_STAT_ERR) {
Kevin O'Connor580e3322009-02-06 22:36:53 -0500216 dprintf(6, "send_cmd : read error (status=%02x err=%02x)\n"
217 , status, inb(iobase1 + ATA_CB_ERR));
Kevin O'Connora05223c2008-06-28 12:15:57 -0400218 return -4;
Kevin O'Connor3a049632008-03-11 11:48:04 -0400219 }
220 if (!(status & ATA_CB_STAT_DRQ)) {
Kevin O'Connor580e3322009-02-06 22:36:53 -0500221 dprintf(6, "send_cmd : DRQ not set (status %02x)\n", status);
Kevin O'Connora05223c2008-06-28 12:15:57 -0400222 return -5;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500223 }
224
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400225 return 0;
226}
227
Kevin O'Connor14021f22009-12-26 23:21:38 -0500228// Send an ata command that does not transfer any further data.
229int
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500230ata_cmd_nondata(struct atadrive_s *adrive_g, struct ata_pio_command *cmd)
Kevin O'Connor14021f22009-12-26 23:21:38 -0500231{
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500232 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
233 u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
234 u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500235
236 // Disable interrupts
237 outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC);
238
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500239 int ret = send_cmd(adrive_g, cmd);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500240 if (ret)
241 goto fail;
242 ret = ndelay_await_not_bsy(iobase1);
243 if (ret < 0)
244 goto fail;
245
246 if (ret & ATA_CB_STAT_ERR) {
247 dprintf(6, "nondata cmd : read error (status=%02x err=%02x)\n"
248 , ret, inb(iobase1 + ATA_CB_ERR));
249 ret = -4;
250 goto fail;
251 }
252 if (ret & ATA_CB_STAT_DRQ) {
253 dprintf(6, "nondata cmd : DRQ set (status %02x)\n", ret);
254 ret = -5;
255 goto fail;
256 }
257
258fail:
259 // Enable interrupts
260 outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);
261
262 return ret;
263}
264
Kevin O'Connoree55c762008-05-13 00:18:20 -0400265
266/****************************************************************
Kevin O'Connor14021f22009-12-26 23:21:38 -0500267 * ATA PIO transfers
Kevin O'Connoree55c762008-05-13 00:18:20 -0400268 ****************************************************************/
269
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400270// Transfer 'op->count' blocks (of 'blocksize' bytes) to/from drive
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400271// 'op->drive_g'.
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400272static int
Kevin O'Connor14021f22009-12-26 23:21:38 -0500273ata_pio_transfer(struct disk_op_s *op, int iswrite, int blocksize)
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400274{
Kevin O'Connor14021f22009-12-26 23:21:38 -0500275 dprintf(16, "ata_pio_transfer id=%p write=%d count=%d bs=%d buf=%p\n"
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400276 , op->drive_g, iswrite, op->count, blocksize, op->buf_fl);
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400277
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500278 struct atadrive_s *adrive_g = container_of(
279 op->drive_g, struct atadrive_s, drive);
280 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
281 u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
282 u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400283 int count = op->count;
284 void *buf_fl = op->buf_fl;
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400285 int status;
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400286 for (;;) {
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400287 if (iswrite) {
Kevin O'Connor3a049632008-03-11 11:48:04 -0400288 // Write data to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400289 dprintf(16, "Write sector id=%p dest=%p\n", op->drive_g, buf_fl);
Kevin O'Connor32945af2009-02-27 21:23:01 -0500290 if (CONFIG_ATA_PIO32)
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400291 outsl_fl(iobase1, buf_fl, blocksize / 4);
Kevin O'Connor3a049632008-03-11 11:48:04 -0400292 else
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400293 outsw_fl(iobase1, buf_fl, blocksize / 2);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500294 } else {
Kevin O'Connor3a049632008-03-11 11:48:04 -0400295 // Read data from controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400296 dprintf(16, "Read sector id=%p dest=%p\n", op->drive_g, buf_fl);
Kevin O'Connor32945af2009-02-27 21:23:01 -0500297 if (CONFIG_ATA_PIO32)
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400298 insl_fl(iobase1, buf_fl, blocksize / 4);
Kevin O'Connor3a049632008-03-11 11:48:04 -0400299 else
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400300 insw_fl(iobase1, buf_fl, blocksize / 2);
Kevin O'Connor3a049632008-03-11 11:48:04 -0400301 }
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400302 buf_fl += blocksize;
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400303
Kevin O'Connor580e3322009-02-06 22:36:53 -0500304 status = pause_await_not_bsy(iobase1, iobase2);
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400305 if (status < 0) {
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400306 // Error
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400307 op->count -= count;
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400308 return status;
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400309 }
Kevin O'Connor3a049632008-03-11 11:48:04 -0400310
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400311 count--;
312 if (!count)
Kevin O'Connor3a049632008-03-11 11:48:04 -0400313 break;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500314 status &= (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR);
315 if (status != ATA_CB_STAT_DRQ) {
Kevin O'Connor14021f22009-12-26 23:21:38 -0500316 dprintf(6, "ata_pio_transfer : more sectors left (status %02x)\n"
Kevin O'Connor580e3322009-02-06 22:36:53 -0500317 , status);
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400318 op->count -= count;
Kevin O'Connora05223c2008-06-28 12:15:57 -0400319 return -6;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500320 }
321 }
Kevin O'Connor3a049632008-03-11 11:48:04 -0400322
Kevin O'Connor580e3322009-02-06 22:36:53 -0500323 status &= (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ
324 | ATA_CB_STAT_ERR);
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400325 if (!iswrite)
Kevin O'Connor3a049632008-03-11 11:48:04 -0400326 status &= ~ATA_CB_STAT_DF;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500327 if (status != 0) {
Kevin O'Connor14021f22009-12-26 23:21:38 -0500328 dprintf(6, "ata_pio_transfer : no sectors left (status %02x)\n", status);
Kevin O'Connora05223c2008-06-28 12:15:57 -0400329 return -7;
Kevin O'Connor3a049632008-03-11 11:48:04 -0400330 }
331
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500332 return 0;
333}
334
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400335
336/****************************************************************
Kevin O'Connor14021f22009-12-26 23:21:38 -0500337 * ATA DMA transfers
338 ****************************************************************/
339
340#define BM_CMD 0
341#define BM_CMD_MEMWRITE 0x08
342#define BM_CMD_START 0x01
343#define BM_STATUS 2
344#define BM_STATUS_IRQ 0x04
345#define BM_STATUS_ERROR 0x02
346#define BM_STATUS_ACTIVE 0x01
347#define BM_TABLE 4
348
349struct sff_dma_prd {
350 u32 buf_fl;
351 u32 count;
352};
353
354// Check if DMA available and setup transfer if so.
355static int
356ata_try_dma(struct disk_op_s *op, int iswrite, int blocksize)
357{
Kevin O'Connor46b82622012-05-13 12:10:30 -0400358 ASSERT16();
Kevin O'Connor4d079022010-01-17 12:58:47 -0500359 if (! CONFIG_ATA_DMA)
360 return -1;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500361 u32 dest = (u32)op->buf_fl;
362 if (dest & 1)
363 // Need minimum alignment of 1.
364 return -1;
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500365 struct atadrive_s *adrive_g = container_of(
366 op->drive_g, struct atadrive_s, drive);
367 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
368 u16 iomaster = GET_GLOBALFLAT(chan_gf->iomaster);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500369 if (! iomaster)
370 return -1;
371 u32 bytes = op->count * blocksize;
372 if (! bytes)
373 return -1;
374
375 // Build PRD dma structure.
Kevin O'Connor46b82622012-05-13 12:10:30 -0400376 struct sff_dma_prd *dma = MAKE_FLATPTR(SEG_LOW, ExtraStack);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500377 struct sff_dma_prd *origdma = dma;
378 while (bytes) {
379 if (dma >= &origdma[16])
380 // Too many descriptors..
381 return -1;
382 u32 count = bytes;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500383 u32 max = 0x10000 - (dest & 0xffff);
384 if (count > max)
385 count = max;
386
Kevin O'Connor1e3bd4f2012-05-24 23:56:19 -0400387 SET_LOWFLAT(dma->buf_fl, dest);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500388 bytes -= count;
389 if (!bytes)
390 // Last descriptor.
391 count |= 1<<31;
392 dprintf(16, "dma@%p: %08x %08x\n", dma, dest, count);
393 dest += count;
Kevin O'Connor1e3bd4f2012-05-24 23:56:19 -0400394 SET_LOWFLAT(dma->count, count);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500395 dma++;
396 }
397
398 // Program bus-master controller.
399 outl((u32)origdma, iomaster + BM_TABLE);
400 u8 oldcmd = inb(iomaster + BM_CMD) & ~(BM_CMD_MEMWRITE|BM_CMD_START);
401 outb(oldcmd | (iswrite ? 0x00 : BM_CMD_MEMWRITE), iomaster + BM_CMD);
402 outb(BM_STATUS_ERROR|BM_STATUS_IRQ, iomaster + BM_STATUS);
403
404 return 0;
405}
406
407// Transfer data using DMA.
408static int
409ata_dma_transfer(struct disk_op_s *op)
410{
Kevin O'Connor4d079022010-01-17 12:58:47 -0500411 if (! CONFIG_ATA_DMA)
412 return -1;
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500413 dprintf(16, "ata_dma_transfer id=%p buf=%p\n", op->drive_g, op->buf_fl);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500414
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500415 struct atadrive_s *adrive_g = container_of(
416 op->drive_g, struct atadrive_s, drive);
417 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
418 u16 iomaster = GET_GLOBALFLAT(chan_gf->iomaster);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500419
420 // Start bus-master controller.
421 u8 oldcmd = inb(iomaster + BM_CMD);
422 outb(oldcmd | BM_CMD_START, iomaster + BM_CMD);
423
424 u64 end = calc_future_tsc(IDE_TIMEOUT);
425 u8 status;
426 for (;;) {
427 status = inb(iomaster + BM_STATUS);
428 if (status & BM_STATUS_IRQ)
429 break;
430 // Transfer in progress
Kevin O'Connor144817b2010-05-23 10:46:49 -0400431 if (check_tsc(end)) {
Kevin O'Connor14021f22009-12-26 23:21:38 -0500432 // Timeout.
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500433 warn_timeout();
Kevin O'Connor14021f22009-12-26 23:21:38 -0500434 break;
435 }
436 yield();
437 }
438 outb(oldcmd & ~BM_CMD_START, iomaster + BM_CMD);
439
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500440 u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
441 u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500442 int idestatus = pause_await_not_bsy(iobase1, iobase2);
443
444 if ((status & (BM_STATUS_IRQ|BM_STATUS_ACTIVE)) == BM_STATUS_IRQ
445 && idestatus >= 0x00
446 && (idestatus & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ
447 | ATA_CB_STAT_ERR)) == 0x00)
448 // Success.
449 return 0;
450
451 dprintf(6, "IDE DMA error (dma=%x ide=%x/%x/%x)\n", status, idestatus
452 , inb(iobase2 + ATA_CB_ASTAT), inb(iobase1 + ATA_CB_ERR));
453 op->count = 0;
454 return -1;
455}
456
457
458/****************************************************************
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400459 * ATA hard drive functions
460 ****************************************************************/
461
Kevin O'Connor14021f22009-12-26 23:21:38 -0500462// Transfer data to harddrive using PIO protocol.
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400463static int
Kevin O'Connor14021f22009-12-26 23:21:38 -0500464ata_pio_cmd_data(struct disk_op_s *op, int iswrite, struct ata_pio_command *cmd)
Kevin O'Connorf888f8c2008-03-23 00:04:54 -0400465{
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500466 struct atadrive_s *adrive_g = container_of(
467 op->drive_g, struct atadrive_s, drive);
468 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
469 u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
470 u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400471
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500472 // Disable interrupts
473 outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC);
474
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500475 int ret = send_cmd(adrive_g, cmd);
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400476 if (ret)
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500477 goto fail;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500478 ret = ata_wait_data(iobase1);
479 if (ret)
480 goto fail;
481 ret = ata_pio_transfer(op, iswrite, DISK_SECTOR_SIZE);
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500482
483fail:
484 // Enable interrupts
485 outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);
486 return ret;
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400487}
488
Kevin O'Connor14021f22009-12-26 23:21:38 -0500489// Transfer data to harddrive using DMA protocol.
490static int
491ata_dma_cmd_data(struct disk_op_s *op, struct ata_pio_command *cmd)
492{
Kevin O'Connor4d079022010-01-17 12:58:47 -0500493 if (! CONFIG_ATA_DMA)
494 return -1;
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500495 struct atadrive_s *adrive_g = container_of(
496 op->drive_g, struct atadrive_s, drive);
497 int ret = send_cmd(adrive_g, cmd);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500498 if (ret)
499 return ret;
500 return ata_dma_transfer(op);
501}
502
503// Read/write count blocks from a harddrive.
504static int
505ata_readwrite(struct disk_op_s *op, int iswrite)
506{
507 u64 lba = op->lba;
508
509 int usepio = ata_try_dma(op, iswrite, DISK_SECTOR_SIZE);
510
511 struct ata_pio_command cmd;
512 memset(&cmd, 0, sizeof(cmd));
513
514 if (op->count >= (1<<8) || lba + op->count >= (1<<28)) {
515 cmd.sector_count2 = op->count >> 8;
516 cmd.lba_low2 = lba >> 24;
517 cmd.lba_mid2 = lba >> 32;
518 cmd.lba_high2 = lba >> 40;
519 lba &= 0xffffff;
520
521 if (usepio)
522 cmd.command = (iswrite ? ATA_CMD_WRITE_SECTORS_EXT
523 : ATA_CMD_READ_SECTORS_EXT);
524 else
525 cmd.command = (iswrite ? ATA_CMD_WRITE_DMA_EXT
526 : ATA_CMD_READ_DMA_EXT);
527 } else {
528 if (usepio)
529 cmd.command = (iswrite ? ATA_CMD_WRITE_SECTORS
530 : ATA_CMD_READ_SECTORS);
531 else
532 cmd.command = (iswrite ? ATA_CMD_WRITE_DMA
533 : ATA_CMD_READ_DMA);
534 }
535
536 cmd.sector_count = op->count;
537 cmd.lba_low = lba;
538 cmd.lba_mid = lba >> 8;
539 cmd.lba_high = lba >> 16;
540 cmd.device = ((lba >> 24) & 0xf) | ATA_CB_DH_LBA;
541
542 int ret;
543 if (usepio)
544 ret = ata_pio_cmd_data(op, iswrite, &cmd);
545 else
546 ret = ata_dma_cmd_data(op, &cmd);
547 if (ret)
548 return DISK_RET_EBADTRACK;
549 return DISK_RET_SUCCESS;
550}
551
552// 16bit command demuxer for ATA harddrives.
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400553int
554process_ata_op(struct disk_op_s *op)
555{
Kevin O'Connorc892b132009-08-11 21:59:37 -0400556 if (!CONFIG_ATA)
557 return 0;
558
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400559 struct atadrive_s *adrive_g = container_of(
560 op->drive_g, struct atadrive_s, drive);
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400561 switch (op->command) {
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400562 case CMD_READ:
Kevin O'Connor14021f22009-12-26 23:21:38 -0500563 return ata_readwrite(op, 0);
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400564 case CMD_WRITE:
Kevin O'Connor14021f22009-12-26 23:21:38 -0500565 return ata_readwrite(op, 1);
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400566 case CMD_RESET:
567 ata_reset(adrive_g);
568 return DISK_RET_SUCCESS;
569 case CMD_ISREADY:
570 return isready(adrive_g);
571 case CMD_FORMAT:
572 case CMD_VERIFY:
573 case CMD_SEEK:
574 return DISK_RET_SUCCESS;
Kevin O'Connor42337662009-08-10 00:06:37 -0400575 default:
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400576 op->count = 0;
577 return DISK_RET_EPARAM;
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400578 }
Kevin O'Connorf888f8c2008-03-23 00:04:54 -0400579}
580
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400581
582/****************************************************************
583 * ATAPI functions
584 ****************************************************************/
585
Kevin O'Connor7d700252010-02-15 11:56:07 -0500586#define CDROM_CDB_SIZE 12
587
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400588// Low-level atapi command transmit function.
Kevin O'Connor7d700252010-02-15 11:56:07 -0500589int
590atapi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500591{
Kevin O'Connor80c2b6e2010-12-05 12:52:02 -0500592 if (! CONFIG_ATA)
593 return 0;
594
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500595 struct atadrive_s *adrive_g = container_of(
596 op->drive_g, struct atadrive_s, drive);
597 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
598 u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
599 u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500600
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400601 struct ata_pio_command cmd;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500602 memset(&cmd, 0, sizeof(cmd));
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400603 cmd.lba_mid = blocksize;
604 cmd.lba_high = blocksize >> 8;
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400605 cmd.command = ATA_CMD_PACKET;
606
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500607 // Disable interrupts
608 outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC);
609
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500610 int ret = send_cmd(adrive_g, &cmd);
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400611 if (ret)
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500612 goto fail;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500613 ret = ata_wait_data(iobase1);
614 if (ret)
615 goto fail;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500616
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400617 // Send command to device
Kevin O'Connor7d700252010-02-15 11:56:07 -0500618 outsw_fl(iobase1, MAKE_FLATPTR(GET_SEG(SS), cdbcmd), CDROM_CDB_SIZE / 2);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500619
Kevin O'Connor580e3322009-02-06 22:36:53 -0500620 int status = pause_await_not_bsy(iobase1, iobase2);
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500621 if (status < 0) {
622 ret = status;
623 goto fail;
624 }
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400625
Kevin O'Connor580e3322009-02-06 22:36:53 -0500626 if (status & ATA_CB_STAT_ERR) {
Kevin O'Connorb30c4002009-05-05 21:47:20 -0400627 u8 err = inb(iobase1 + ATA_CB_ERR);
628 // skip "Not Ready"
629 if (err != 0x20)
630 dprintf(6, "send_atapi_cmd : read error (status=%02x err=%02x)\n"
631 , status, err);
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500632 ret = -2;
633 goto fail;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500634 }
Paolo Bonzini39d59892012-03-19 11:41:09 +0100635 if (blocksize) {
636 if (!(status & ATA_CB_STAT_DRQ)) {
637 dprintf(6, "send_atapi_cmd : DRQ not set (status %02x)\n", status);
638 ret = -3;
639 goto fail;
640 }
Kevin O'Connor580e3322009-02-06 22:36:53 -0500641
Paolo Bonzini39d59892012-03-19 11:41:09 +0100642 ret = ata_pio_transfer(op, 0, blocksize);
643 }
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500644
645fail:
646 // Enable interrupts
647 outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);
Kevin O'Connor76977b22010-02-17 01:01:32 -0500648 if (ret)
649 return DISK_RET_EBADTRACK;
650 return DISK_RET_SUCCESS;
Kevin O'Connoraa2590c2008-03-22 23:13:24 -0400651}
652
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400653
654/****************************************************************
Kevin O'Connor0a924122009-02-08 19:43:47 -0500655 * ATA detect and init
656 ****************************************************************/
657
Kevin O'Connor14021f22009-12-26 23:21:38 -0500658// Send an identify device or identify device packet command.
659static int
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500660send_ata_identity(struct atadrive_s *adrive_g, u16 *buffer, int command)
Kevin O'Connor14021f22009-12-26 23:21:38 -0500661{
662 memset(buffer, 0, DISK_SECTOR_SIZE);
663
664 struct disk_op_s dop;
665 memset(&dop, 0, sizeof(dop));
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500666 dop.drive_g = &adrive_g->drive;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500667 dop.count = 1;
668 dop.lba = 1;
669 dop.buf_fl = MAKE_FLATPTR(GET_SEG(SS), buffer);
670
671 struct ata_pio_command cmd;
672 memset(&cmd, 0, sizeof(cmd));
673 cmd.command = command;
674
675 return ata_pio_cmd_data(&dop, 0, &cmd);
676}
677
Kevin O'Connorf2d48a32009-08-11 20:58:11 -0400678// Extract the ATA/ATAPI version info.
Gerd Hoffmann54fa8ec2010-11-29 09:42:12 +0100679int
680ata_extract_version(u16 *buffer)
Kevin O'Connorf2d48a32009-08-11 20:58:11 -0400681{
682 // Extract ATA/ATAPI version.
683 u16 ataversion = buffer[80];
684 u8 version;
685 for (version=15; version>0; version--)
686 if (ataversion & (1<<version))
687 break;
688 return version;
689}
690
Kevin O'Connor575ffc82010-02-21 23:20:10 -0500691#define MAXMODEL 40
Kevin O'Connor0a924122009-02-08 19:43:47 -0500692
Kevin O'Connor575ffc82010-02-21 23:20:10 -0500693// Extract the ATA/ATAPI model info.
Gerd Hoffmann54fa8ec2010-11-29 09:42:12 +0100694char *
695ata_extract_model(char *model, u32 size, u16 *buffer)
Kevin O'Connor575ffc82010-02-21 23:20:10 -0500696{
Kevin O'Connor0a924122009-02-08 19:43:47 -0500697 // Read model name
698 int i;
Gerd Hoffmann54fa8ec2010-11-29 09:42:12 +0100699 for (i=0; i<size/2; i++)
Kevin O'Connorb3064592012-08-14 21:20:10 -0400700 *(u16*)&model[i*2] = be16_to_cpu(buffer[27+i]);
Gerd Hoffmann54fa8ec2010-11-29 09:42:12 +0100701 model[size] = 0x00;
Kevin O'Connor9e881a32011-01-08 12:06:54 -0500702 nullTrailingSpace(model);
Kevin O'Connor575ffc82010-02-21 23:20:10 -0500703 return model;
Kevin O'Connor0a924122009-02-08 19:43:47 -0500704}
705
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500706// Common init code between ata and atapi
707static struct atadrive_s *
708init_atadrive(struct atadrive_s *dummy, u16 *buffer)
709{
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500710 struct atadrive_s *adrive_g = malloc_fseg(sizeof(*adrive_g));
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500711 if (!adrive_g) {
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500712 warn_noalloc();
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500713 return NULL;
714 }
715 memset(adrive_g, 0, sizeof(*adrive_g));
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500716 adrive_g->chan_gf = dummy->chan_gf;
717 adrive_g->slave = dummy->slave;
718 adrive_g->drive.cntl_id = adrive_g->chan_gf->chanid * 2 + dummy->slave;
719 adrive_g->drive.removable = (buffer[0] & 0x80) ? 1 : 0;
720 return adrive_g;
721}
722
Kevin O'Connor14021f22009-12-26 23:21:38 -0500723// Detect if the given drive is an atapi - initialize it if so.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500724static struct atadrive_s *
725init_drive_atapi(struct atadrive_s *dummy, u16 *buffer)
Kevin O'Connor0a924122009-02-08 19:43:47 -0500726{
727 // Send an IDENTIFY_DEVICE_PACKET command to device
Kevin O'Connor14021f22009-12-26 23:21:38 -0500728 int ret = send_ata_identity(dummy, buffer, ATA_CMD_IDENTIFY_PACKET_DEVICE);
Kevin O'Connor0a924122009-02-08 19:43:47 -0500729 if (ret)
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400730 return NULL;
Kevin O'Connor0a924122009-02-08 19:43:47 -0500731
732 // Success - setup as ATAPI.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500733 struct atadrive_s *adrive_g = init_atadrive(dummy, buffer);
734 if (!adrive_g)
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400735 return NULL;
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400736 adrive_g->drive.type = DTYPE_ATA_ATAPI;
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500737 adrive_g->drive.blksize = CDROM_SECTOR_SIZE;
738 adrive_g->drive.sectors = (u64)-1;
Kevin O'Connor42337662009-08-10 00:06:37 -0400739 u8 iscd = ((buffer[0] >> 8) & 0x1f) == 0x05;
Kevin O'Connor575ffc82010-02-21 23:20:10 -0500740 char model[MAXMODEL+1];
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500741 char *desc = znprintf(MAXDESCSIZE
742 , "DVD/CD [ata%d-%d: %s ATAPI-%d %s]"
743 , adrive_g->chan_gf->chanid, adrive_g->slave
744 , ata_extract_model(model, MAXMODEL, buffer)
745 , ata_extract_version(buffer)
746 , (iscd ? "DVD/CD" : "Device"));
747 dprintf(1, "%s\n", desc);
Kevin O'Connor42337662009-08-10 00:06:37 -0400748
749 // fill cdidmap
Kevin O'Connor031ef552010-12-27 19:26:57 -0500750 if (iscd) {
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -0400751 int prio = bootprio_find_ata_device(adrive_g->chan_gf->pci_tmp,
Kevin O'Connor031ef552010-12-27 19:26:57 -0500752 adrive_g->chan_gf->chanid,
753 adrive_g->slave);
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500754 boot_add_cd(&adrive_g->drive, desc, prio);
Kevin O'Connor031ef552010-12-27 19:26:57 -0500755 }
Kevin O'Connor0a924122009-02-08 19:43:47 -0500756
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500757 return adrive_g;
Kevin O'Connor0a924122009-02-08 19:43:47 -0500758}
759
Kevin O'Connor14021f22009-12-26 23:21:38 -0500760// Detect if the given drive is a regular ata drive - initialize it if so.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500761static struct atadrive_s *
762init_drive_ata(struct atadrive_s *dummy, u16 *buffer)
Kevin O'Connorc1437612008-05-18 01:43:07 -0400763{
Kevin O'Connor580e3322009-02-06 22:36:53 -0500764 // Send an IDENTIFY_DEVICE command to device
Kevin O'Connor14021f22009-12-26 23:21:38 -0500765 int ret = send_ata_identity(dummy, buffer, ATA_CMD_IDENTIFY_DEVICE);
Kevin O'Connorc1437612008-05-18 01:43:07 -0400766 if (ret)
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400767 return NULL;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500768
769 // Success - setup as ATA.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500770 struct atadrive_s *adrive_g = init_atadrive(dummy, buffer);
771 if (!adrive_g)
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400772 return NULL;
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500773 adrive_g->drive.type = DTYPE_ATA;
774 adrive_g->drive.blksize = DISK_SECTOR_SIZE;
Kevin O'Connorc1437612008-05-18 01:43:07 -0400775
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500776 adrive_g->drive.pchs.cylinders = buffer[1];
777 adrive_g->drive.pchs.heads = buffer[3];
778 adrive_g->drive.pchs.spt = buffer[6];
Kevin O'Connorc1437612008-05-18 01:43:07 -0400779
780 u64 sectors;
Kevin O'Connorab515602009-02-11 22:22:15 -0500781 if (buffer[83] & (1 << 10)) // word 83 - lba48 support
782 sectors = *(u64*)&buffer[100]; // word 100-103
Kevin O'Connorc1437612008-05-18 01:43:07 -0400783 else
Kevin O'Connorab515602009-02-11 22:22:15 -0500784 sectors = *(u32*)&buffer[60]; // word 60 and word 61
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500785 adrive_g->drive.sectors = sectors;
Kevin O'Connor575ffc82010-02-21 23:20:10 -0500786 u64 adjsize = sectors >> 11;
787 char adjprefix = 'M';
788 if (adjsize >= (1 << 16)) {
789 adjsize >>= 10;
790 adjprefix = 'G';
791 }
792 char model[MAXMODEL+1];
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500793 char *desc = znprintf(MAXDESCSIZE
794 , "ata%d-%d: %s ATA-%d Hard-Disk (%u %ciBytes)"
795 , adrive_g->chan_gf->chanid, adrive_g->slave
796 , ata_extract_model(model, MAXMODEL, buffer)
797 , ata_extract_version(buffer)
798 , (u32)adjsize, adjprefix);
799 dprintf(1, "%s\n", desc);
Kevin O'Connorc1437612008-05-18 01:43:07 -0400800
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -0400801 int prio = bootprio_find_ata_device(adrive_g->chan_gf->pci_tmp,
Kevin O'Connor031ef552010-12-27 19:26:57 -0500802 adrive_g->chan_gf->chanid,
803 adrive_g->slave);
Kevin O'Connor0a924122009-02-08 19:43:47 -0500804 // Register with bcv system.
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500805 boot_add_hd(&adrive_g->drive, desc, prio);
Kevin O'Connoraafa6572008-03-13 19:57:49 -0400806
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500807 return adrive_g;
Kevin O'Connoraafa6572008-03-13 19:57:49 -0400808}
809
Kevin O'Connora5826b52009-10-24 17:57:29 -0400810static u64 SpinupEnd;
811
Kevin O'Connor14021f22009-12-26 23:21:38 -0500812// Wait for non-busy status and check for "floating bus" condition.
Kevin O'Connor425f2122009-04-18 12:23:00 -0400813static int
Kevin O'Connora5826b52009-10-24 17:57:29 -0400814powerup_await_non_bsy(u16 base)
Kevin O'Connor425f2122009-04-18 12:23:00 -0400815{
816 u8 orstatus = 0;
817 u8 status;
818 for (;;) {
819 status = inb(base+ATA_CB_STAT);
820 if (!(status & ATA_CB_STAT_BSY))
821 break;
822 orstatus |= status;
823 if (orstatus == 0xff) {
Kevin O'Connor9dc243e2010-03-20 17:53:03 -0400824 dprintf(4, "powerup IDE floating\n");
Kevin O'Connor425f2122009-04-18 12:23:00 -0400825 return orstatus;
826 }
Kevin O'Connor144817b2010-05-23 10:46:49 -0400827 if (check_tsc(SpinupEnd)) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500828 warn_timeout();
Kevin O'Connor425f2122009-04-18 12:23:00 -0400829 return -1;
830 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400831 yield();
Kevin O'Connor425f2122009-04-18 12:23:00 -0400832 }
833 dprintf(6, "powerup iobase=%x st=%x\n", base, status);
834 return status;
835}
836
Kevin O'Connor14021f22009-12-26 23:21:38 -0500837// Detect any drives attached to a given controller.
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -0400838static void
Kevin O'Connora5826b52009-10-24 17:57:29 -0400839ata_detect(void *data)
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500840{
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500841 struct ata_channel_s *chan_gf = data;
842 struct atadrive_s dummy;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400843 memset(&dummy, 0, sizeof(dummy));
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500844 dummy.chan_gf = chan_gf;
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500845 // Device detection
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500846 int didreset = 0;
847 u8 slave;
848 for (slave=0; slave<=1; slave++) {
Kevin O'Connor425f2122009-04-18 12:23:00 -0400849 // Wait for not-bsy.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500850 u16 iobase1 = chan_gf->iobase1;
Kevin O'Connora5826b52009-10-24 17:57:29 -0400851 int status = powerup_await_non_bsy(iobase1);
Kevin O'Connor425f2122009-04-18 12:23:00 -0400852 if (status < 0)
853 continue;
854 u8 newdh = slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0;
855 outb(newdh, iobase1+ATA_CB_DH);
Kevin O'Connorc946eca2009-05-24 13:55:01 -0400856 ndelay(400);
Kevin O'Connora5826b52009-10-24 17:57:29 -0400857 status = powerup_await_non_bsy(iobase1);
Kevin O'Connor425f2122009-04-18 12:23:00 -0400858 if (status < 0)
859 continue;
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500860
Kevin O'Connor580e3322009-02-06 22:36:53 -0500861 // Check if ioport registers look valid.
Kevin O'Connor425f2122009-04-18 12:23:00 -0400862 outb(newdh, iobase1+ATA_CB_DH);
863 u8 dh = inb(iobase1+ATA_CB_DH);
864 outb(0x55, iobase1+ATA_CB_SC);
865 outb(0xaa, iobase1+ATA_CB_SN);
Kevin O'Connoraafa6572008-03-13 19:57:49 -0400866 u8 sc = inb(iobase1+ATA_CB_SC);
867 u8 sn = inb(iobase1+ATA_CB_SN);
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500868 dprintf(6, "ata_detect ata%d-%d: sc=%x sn=%x dh=%x\n"
869 , chan_gf->chanid, slave, sc, sn, dh);
Kevin O'Connor425f2122009-04-18 12:23:00 -0400870 if (sc != 0x55 || sn != 0xaa || dh != newdh)
Kevin O'Connoraafa6572008-03-13 19:57:49 -0400871 continue;
872
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400873 // Prepare new drive.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500874 dummy.slave = slave;
Kevin O'Connorb1144362009-08-11 20:43:38 -0400875
Kevin O'Connoraafa6572008-03-13 19:57:49 -0400876 // reset the channel
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500877 if (!didreset) {
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400878 ata_reset(&dummy);
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500879 didreset = 1;
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500880 }
881
Kevin O'Connor580e3322009-02-06 22:36:53 -0500882 // check for ATAPI
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400883 u16 buffer[256];
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500884 struct atadrive_s *adrive_g = init_drive_atapi(&dummy, buffer);
885 if (!adrive_g) {
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400886 // Didn't find an ATAPI drive - look for ATA drive.
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400887 u8 st = inb(iobase1+ATA_CB_STAT);
888 if (!st)
889 // Status not set - can't be a valid drive.
890 continue;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500891
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400892 // Wait for RDY.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400893 int ret = await_rdy(iobase1);
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400894 if (ret < 0)
895 continue;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500896
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400897 // check for ATA.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500898 adrive_g = init_drive_ata(&dummy, buffer);
899 if (!adrive_g)
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400900 // No ATA drive found
901 continue;
902 }
Kevin O'Connor580e3322009-02-06 22:36:53 -0500903
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400904 u16 resetresult = buffer[93];
905 dprintf(6, "ata_detect resetresult=%04x\n", resetresult);
906 if (!slave && (resetresult & 0xdf61) == 0x4041)
907 // resetresult looks valid and device 0 is responding to
908 // device 1 requests - device 1 must not be present - skip
909 // detection.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500910 break;
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500911 }
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500912}
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -0400913
Kevin O'Connor14021f22009-12-26 23:21:38 -0500914// Initialize an ata controller and detect its drives.
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -0400915static void
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -0400916init_controller(struct pci_device *pci, int irq
917 , u32 port1, u32 port2, u32 master)
Kevin O'Connor4ccb2312009-12-05 11:25:09 -0500918{
Kevin O'Connor927d16e2011-06-19 09:43:20 -0400919 static int chanid = 0;
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500920 struct ata_channel_s *chan_gf = malloc_fseg(sizeof(*chan_gf));
921 if (!chan_gf) {
922 warn_noalloc();
923 return;
924 }
Kevin O'Connor927d16e2011-06-19 09:43:20 -0400925 chan_gf->chanid = chanid++;
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500926 chan_gf->irq = irq;
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -0400927 chan_gf->pci_bdf = pci ? pci->bdf : -1;
928 chan_gf->pci_tmp = pci;
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500929 chan_gf->iobase1 = port1;
930 chan_gf->iobase2 = port2;
931 chan_gf->iomaster = master;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500932 dprintf(1, "ATA controller %d at %x/%x/%x (irq %d dev %x)\n"
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -0400933 , chanid, port1, port2, master, irq, chan_gf->pci_bdf);
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500934 run_thread(ata_detect, chan_gf);
Kevin O'Connor4ccb2312009-12-05 11:25:09 -0500935}
936
Kevin O'Connor525219b2009-12-05 13:36:18 -0500937#define IRQ_ATA1 14
938#define IRQ_ATA2 15
939
Kevin O'Connor927d16e2011-06-19 09:43:20 -0400940// Handle controllers on an ATA PCI device.
941static void
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400942init_pciata(struct pci_device *pci, u8 prog_if)
Kevin O'Connor927d16e2011-06-19 09:43:20 -0400943{
Kevin O'Connor76b5e712011-06-21 22:52:51 -0400944 pci->have_driver = 1;
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400945 u16 bdf = pci->bdf;
Kevin O'Connor927d16e2011-06-19 09:43:20 -0400946 u8 pciirq = pci_config_readb(bdf, PCI_INTERRUPT_LINE);
Kevin O'Connor927d16e2011-06-19 09:43:20 -0400947 int master = 0;
948 if (CONFIG_ATA_DMA && prog_if & 0x80) {
949 // Check for bus-mastering.
950 u32 bar = pci_config_readl(bdf, PCI_BASE_ADDRESS_4);
951 if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
952 master = bar & PCI_BASE_ADDRESS_IO_MASK;
953 pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER);
954 }
955 }
956
957 u32 port1, port2, irq;
958 if (prog_if & 1) {
959 port1 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_0)
960 & PCI_BASE_ADDRESS_IO_MASK);
961 port2 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_1)
962 & PCI_BASE_ADDRESS_IO_MASK);
963 irq = pciirq;
964 } else {
965 port1 = PORT_ATA1_CMD_BASE;
966 port2 = PORT_ATA1_CTRL_BASE;
967 irq = IRQ_ATA1;
968 }
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -0400969 init_controller(pci, irq, port1, port2, master);
Kevin O'Connor927d16e2011-06-19 09:43:20 -0400970
971 if (prog_if & 4) {
972 port1 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_2)
973 & PCI_BASE_ADDRESS_IO_MASK);
974 port2 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_3)
975 & PCI_BASE_ADDRESS_IO_MASK);
976 irq = pciirq;
977 } else {
978 port1 = PORT_ATA2_CMD_BASE;
979 port2 = PORT_ATA2_CTRL_BASE;
980 irq = IRQ_ATA2;
981 }
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -0400982 init_controller(pci, irq, port1, port2, master ? master + 8 : 0);
Kevin O'Connor927d16e2011-06-19 09:43:20 -0400983}
984
Kevin O'Connorb9457ec2011-06-19 10:03:08 -0400985static void
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400986found_genericata(struct pci_device *pci, void *arg)
Kevin O'Connorb9457ec2011-06-19 10:03:08 -0400987{
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400988 init_pciata(pci, pci->prog_if);
Kevin O'Connorb9457ec2011-06-19 10:03:08 -0400989}
990
991static void
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400992found_compatibleahci(struct pci_device *pci, void *arg)
Kevin O'Connorb9457ec2011-06-19 10:03:08 -0400993{
994 if (CONFIG_AHCI)
995 // Already handled directly via native ahci interface.
996 return;
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400997 init_pciata(pci, 0x8f);
Kevin O'Connorb9457ec2011-06-19 10:03:08 -0400998}
999
Kevin O'Connor927d16e2011-06-19 09:43:20 -04001000static const struct pci_device_id pci_ata_tbl[] = {
Kevin O'Connorb9457ec2011-06-19 10:03:08 -04001001 PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE
1002 , found_genericata),
1003 PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4391, found_compatibleahci),
Kevin O'Connor927d16e2011-06-19 09:43:20 -04001004 PCI_DEVICE_END,
1005};
1006
Kevin O'Connor14021f22009-12-26 23:21:38 -05001007// Locate and init ata controllers.
Kevin O'Connor4ccb2312009-12-05 11:25:09 -05001008static void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -05001009ata_init(void)
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -04001010{
Kevin O'Connor3f3e58d2011-06-20 22:20:43 -04001011 if (!CONFIG_COREBOOT && !PCIDevices) {
Kevin O'Connor4ccb2312009-12-05 11:25:09 -05001012 // No PCI devices found - probably a QEMU "-M isapc" machine.
1013 // Try using ISA ports for ATA controllers.
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -04001014 init_controller(NULL, IRQ_ATA1
Kevin O'Connor14021f22009-12-26 23:21:38 -05001015 , PORT_ATA1_CMD_BASE, PORT_ATA1_CTRL_BASE, 0);
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -04001016 init_controller(NULL, IRQ_ATA2
Kevin O'Connor14021f22009-12-26 23:21:38 -05001017 , PORT_ATA2_CMD_BASE, PORT_ATA2_CTRL_BASE, 0);
Kevin O'Connor3f3e58d2011-06-20 22:20:43 -04001018 return;
1019 }
1020
1021 // Scan PCI bus for ATA adapters
1022 struct pci_device *pci;
1023 foreachpci(pci) {
Kevin O'Connor278b19f2011-06-21 22:41:15 -04001024 pci_init_device(pci_ata_tbl, pci, NULL);
Kevin O'Connor4ccb2312009-12-05 11:25:09 -05001025 }
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -04001026}
1027
1028void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -05001029ata_setup(void)
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -04001030{
Kevin O'Connor59c75742010-02-13 18:49:24 -05001031 ASSERT32FLAT();
Kevin O'Connorc1437612008-05-18 01:43:07 -04001032 if (!CONFIG_ATA)
1033 return;
1034
Kevin O'Connor35192dd2008-06-08 19:18:33 -04001035 dprintf(3, "init hard drives\n");
Kevin O'Connora5826b52009-10-24 17:57:29 -04001036
1037 SpinupEnd = calc_future_tsc(IDE_TIMEOUT);
Kevin O'Connorc1437612008-05-18 01:43:07 -04001038 ata_init();
Kevin O'Connorc1437612008-05-18 01:43:07 -04001039
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -04001040 SET_BDA(disk_control_byte, 0xc0);
Kevin O'Connorf54c1502008-06-14 15:56:16 -04001041
Kevin O'Connorcc9e1bf2010-07-28 21:31:38 -04001042 enable_hwirq(14, FUNC16(entry_76));
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -04001043}