blob: e5ef6856c68dc7bf737b635d67905c30daa5bdfc [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'Connor2d2fa312013-09-14 21:55:26 -04008#include "ata.h" // ATA_CB_STAT
9#include "biosvar.h" // GET_GLOBAL
Kevin O'Connor135f3f62013-09-14 23:57:26 -040010#include "block.h" // struct drive_s
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040011#include "blockcmd.h" // CDB_CMD_READ_10
Kevin O'Connorb3064592012-08-14 21:20:10 -040012#include "byteorder.h" // be16_to_cpu
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040013#include "malloc.h" // malloc_fseg
14#include "output.h" // dprintf
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'Connor2d2fa312013-09-14 21:55:26 -040018#include "pic.h" // enable_hwirq
Kevin O'Connor3df600b2013-09-14 19:28:55 -040019#include "stacks.h" // yield
Kevin O'Connor135f3f62013-09-14 23:57:26 -040020#include "std/disk.h" // DISK_RET_SUCCESS
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040021#include "string.h" // memset
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040022#include "util.h" // timer_calc
Kevin O'Connor4ade5232013-09-18 21:41:48 -040023#include "x86.h" // inb
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050024
Kevin O'Connor425f2122009-04-18 12:23:00 -040025#define IDE_TIMEOUT 32000 //32 seconds max for IDE ops
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050026
Kevin O'Connor15aee2e2008-03-01 13:34:04 -050027
Kevin O'Connora6b9f712008-03-29 12:53:57 -040028/****************************************************************
29 * Helper functions
30 ****************************************************************/
31
32// Wait for the specified ide state
Kevin O'Connor580e3322009-02-06 22:36:53 -050033static inline int
34await_ide(u8 mask, u8 flags, u16 base, u16 timeout)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050035{
Kevin O'Connor018bdd72013-07-20 18:22:57 -040036 u32 end = timer_calc(timeout);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050037 for (;;) {
Kevin O'Connora6b9f712008-03-29 12:53:57 -040038 u8 status = inb(base+ATA_CB_STAT);
Kevin O'Connor580e3322009-02-06 22:36:53 -050039 if ((status & mask) == flags)
Kevin O'Connora6b9f712008-03-29 12:53:57 -040040 return status;
Kevin O'Connor018bdd72013-07-20 18:22:57 -040041 if (timer_check(end)) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -050042 warn_timeout();
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050043 return -1;
44 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -040045 yield();
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050046 }
Kevin O'Connor580e3322009-02-06 22:36:53 -050047}
48
49// Wait for the device to be not-busy.
50static int
51await_not_bsy(u16 base)
52{
53 return await_ide(ATA_CB_STAT_BSY, 0, base, IDE_TIMEOUT);
54}
55
56// Wait for the device to be ready.
57static int
58await_rdy(u16 base)
59{
60 return await_ide(ATA_CB_STAT_RDY, ATA_CB_STAT_RDY, base, IDE_TIMEOUT);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050061}
62
Kevin O'Connora6b9f712008-03-29 12:53:57 -040063// Wait for ide state - pauses for one ata cycle first.
Kevin O'Connora9caeae2009-03-07 00:09:52 -050064static inline int
Kevin O'Connor580e3322009-02-06 22:36:53 -050065pause_await_not_bsy(u16 iobase1, u16 iobase2)
Kevin O'Connora6b9f712008-03-29 12:53:57 -040066{
67 // Wait one PIO transfer cycle.
68 inb(iobase2 + ATA_CB_ASTAT);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050069
Kevin O'Connor580e3322009-02-06 22:36:53 -050070 return await_not_bsy(iobase1);
Kevin O'Connora6b9f712008-03-29 12:53:57 -040071}
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050072
Kevin O'Connoref2822a2008-06-07 15:23:11 -040073// Wait for ide state - pause for 400ns first.
Kevin O'Connora9caeae2009-03-07 00:09:52 -050074static inline int
Kevin O'Connor580e3322009-02-06 22:36:53 -050075ndelay_await_not_bsy(u16 iobase1)
Kevin O'Connoref2822a2008-06-07 15:23:11 -040076{
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -050077 ndelay(400);
Kevin O'Connor580e3322009-02-06 22:36:53 -050078 return await_not_bsy(iobase1);
Kevin O'Connoref2822a2008-06-07 15:23:11 -040079}
80
Kevin O'Connora6b9f712008-03-29 12:53:57 -040081// Reset a drive
Kevin O'Connorb1144362009-08-11 20:43:38 -040082static void
Kevin O'Connor8f469b92010-02-28 01:28:11 -050083ata_reset(struct atadrive_s *adrive_g)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050084{
Kevin O'Connor8f469b92010-02-28 01:28:11 -050085 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
86 u8 slave = GET_GLOBAL(adrive_g->slave);
87 u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
88 u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050089
Kevin O'Connor8f469b92010-02-28 01:28:11 -050090 dprintf(6, "ata_reset drive=%p\n", &adrive_g->drive);
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.
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400103 u32 end = timer_calc(IDE_TIMEOUT);
Kevin O'Connor580e3322009-02-06 22:36:53 -0500104 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'Connor018bdd72013-07-20 18:22:57 -0400112 if (timer_check(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'Connor8f469b92010-02-28 01:28:11 -0500123 u8 type=GET_GLOBAL(adrive_g->drive.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'Connor8f469b92010-02-28 01:28:11 -0500136isready(struct atadrive_s *adrive_g)
Kevin O'Connor42337662009-08-10 00:06:37 -0400137{
138 // Read the status from controller
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500139 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
140 u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
Kevin O'Connor42337662009-08-10 00:06:37 -0400141 u8 status = inb(iobase1 + ATA_CB_STAT);
Kevin O'Connor126eac62009-08-16 13:32:24 -0400142 if ((status & (ATA_CB_STAT_BSY|ATA_CB_STAT_RDY)) == ATA_CB_STAT_RDY)
143 return DISK_RET_SUCCESS;
144 return DISK_RET_ENOTREADY;
Kevin O'Connor42337662009-08-10 00:06:37 -0400145}
146
Kevin O'Connoree55c762008-05-13 00:18:20 -0400147
148/****************************************************************
149 * ATA send command
150 ****************************************************************/
151
Kevin O'Connorf888f8c2008-03-23 00:04:54 -0400152struct ata_pio_command {
Kevin O'Connorf888f8c2008-03-23 00:04:54 -0400153 u8 feature;
154 u8 sector_count;
155 u8 lba_low;
156 u8 lba_mid;
157 u8 lba_high;
158 u8 device;
159 u8 command;
160
Kevin O'Connor14021f22009-12-26 23:21:38 -0500161 u8 feature2;
Kevin O'Connorf888f8c2008-03-23 00:04:54 -0400162 u8 sector_count2;
163 u8 lba_low2;
164 u8 lba_mid2;
165 u8 lba_high2;
166};
167
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400168// Send an ata command to the drive.
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400169static int
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500170send_cmd(struct atadrive_s *adrive_g, struct ata_pio_command *cmd)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500171{
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500172 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
173 u8 slave = GET_GLOBAL(adrive_g->slave);
174 u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
Kevin O'Connoref2822a2008-06-07 15:23:11 -0400175
176 // Select device
Kevin O'Connor580e3322009-02-06 22:36:53 -0500177 int status = await_not_bsy(iobase1);
178 if (status < 0)
179 return status;
180 u8 newdh = ((cmd->device & ~ATA_CB_DH_DEV1)
181 | (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0));
182 u8 olddh = inb(iobase1 + ATA_CB_DH);
183 outb(newdh, iobase1 + ATA_CB_DH);
184 if ((olddh ^ newdh) & (1<<4)) {
185 // Was a device change - wait for device to become not busy.
Kevin O'Connorc946eca2009-05-24 13:55:01 -0400186 status = ndelay_await_not_bsy(iobase1);
Kevin O'Connor580e3322009-02-06 22:36:53 -0500187 if (status < 0)
188 return status;
189 }
Kevin O'Connoref2822a2008-06-07 15:23:11 -0400190
Kevin O'Connor14021f22009-12-26 23:21:38 -0500191 // Check for ATA_CMD_(READ|WRITE)_(SECTORS|DMA)_EXT commands.
192 if ((cmd->command & ~0x11) == ATA_CMD_READ_SECTORS_EXT) {
193 outb(cmd->feature2, iobase1 + ATA_CB_FR);
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400194 outb(cmd->sector_count2, iobase1 + ATA_CB_SC);
195 outb(cmd->lba_low2, iobase1 + ATA_CB_SN);
196 outb(cmd->lba_mid2, iobase1 + ATA_CB_CL);
197 outb(cmd->lba_high2, iobase1 + ATA_CB_CH);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500198 }
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400199 outb(cmd->feature, iobase1 + ATA_CB_FR);
200 outb(cmd->sector_count, iobase1 + ATA_CB_SC);
201 outb(cmd->lba_low, iobase1 + ATA_CB_SN);
202 outb(cmd->lba_mid, iobase1 + ATA_CB_CL);
203 outb(cmd->lba_high, iobase1 + ATA_CB_CH);
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400204 outb(cmd->command, iobase1 + ATA_CB_CMD);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500205
Kevin O'Connor14021f22009-12-26 23:21:38 -0500206 return 0;
207}
208
209// Wait for data after calling 'send_cmd'.
210static int
211ata_wait_data(u16 iobase1)
212{
213 int status = ndelay_await_not_bsy(iobase1);
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400214 if (status < 0)
215 return status;
216
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500217 if (status & ATA_CB_STAT_ERR) {
Kevin O'Connor580e3322009-02-06 22:36:53 -0500218 dprintf(6, "send_cmd : read error (status=%02x err=%02x)\n"
219 , status, inb(iobase1 + ATA_CB_ERR));
Kevin O'Connora05223c2008-06-28 12:15:57 -0400220 return -4;
Kevin O'Connor3a049632008-03-11 11:48:04 -0400221 }
222 if (!(status & ATA_CB_STAT_DRQ)) {
Kevin O'Connor580e3322009-02-06 22:36:53 -0500223 dprintf(6, "send_cmd : DRQ not set (status %02x)\n", status);
Kevin O'Connora05223c2008-06-28 12:15:57 -0400224 return -5;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500225 }
226
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400227 return 0;
228}
229
Kevin O'Connor14021f22009-12-26 23:21:38 -0500230// Send an ata command that does not transfer any further data.
231int
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500232ata_cmd_nondata(struct atadrive_s *adrive_g, struct ata_pio_command *cmd)
Kevin O'Connor14021f22009-12-26 23:21:38 -0500233{
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500234 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
235 u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
236 u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500237
238 // Disable interrupts
239 outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC);
240
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500241 int ret = send_cmd(adrive_g, cmd);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500242 if (ret)
243 goto fail;
244 ret = ndelay_await_not_bsy(iobase1);
245 if (ret < 0)
246 goto fail;
247
248 if (ret & ATA_CB_STAT_ERR) {
249 dprintf(6, "nondata cmd : read error (status=%02x err=%02x)\n"
250 , ret, inb(iobase1 + ATA_CB_ERR));
251 ret = -4;
252 goto fail;
253 }
254 if (ret & ATA_CB_STAT_DRQ) {
255 dprintf(6, "nondata cmd : DRQ set (status %02x)\n", ret);
256 ret = -5;
257 goto fail;
258 }
259
260fail:
261 // Enable interrupts
262 outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);
263
264 return ret;
265}
266
Kevin O'Connoree55c762008-05-13 00:18:20 -0400267
268/****************************************************************
Kevin O'Connor14021f22009-12-26 23:21:38 -0500269 * ATA PIO transfers
Kevin O'Connoree55c762008-05-13 00:18:20 -0400270 ****************************************************************/
271
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400272// Transfer 'op->count' blocks (of 'blocksize' bytes) to/from drive
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400273// 'op->drive_g'.
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400274static int
Kevin O'Connor14021f22009-12-26 23:21:38 -0500275ata_pio_transfer(struct disk_op_s *op, int iswrite, int blocksize)
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400276{
Kevin O'Connor14021f22009-12-26 23:21:38 -0500277 dprintf(16, "ata_pio_transfer id=%p write=%d count=%d bs=%d buf=%p\n"
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400278 , op->drive_g, iswrite, op->count, blocksize, op->buf_fl);
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400279
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500280 struct atadrive_s *adrive_g = container_of(
281 op->drive_g, struct atadrive_s, drive);
282 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
283 u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
284 u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400285 int count = op->count;
286 void *buf_fl = op->buf_fl;
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400287 int status;
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400288 for (;;) {
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400289 if (iswrite) {
Kevin O'Connor3a049632008-03-11 11:48:04 -0400290 // Write data to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400291 dprintf(16, "Write sector id=%p dest=%p\n", op->drive_g, buf_fl);
Kevin O'Connor32945af2009-02-27 21:23:01 -0500292 if (CONFIG_ATA_PIO32)
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400293 outsl_fl(iobase1, buf_fl, blocksize / 4);
Kevin O'Connor3a049632008-03-11 11:48:04 -0400294 else
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400295 outsw_fl(iobase1, buf_fl, blocksize / 2);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500296 } else {
Kevin O'Connor3a049632008-03-11 11:48:04 -0400297 // Read data from controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400298 dprintf(16, "Read sector id=%p dest=%p\n", op->drive_g, buf_fl);
Kevin O'Connor32945af2009-02-27 21:23:01 -0500299 if (CONFIG_ATA_PIO32)
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400300 insl_fl(iobase1, buf_fl, blocksize / 4);
Kevin O'Connor3a049632008-03-11 11:48:04 -0400301 else
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400302 insw_fl(iobase1, buf_fl, blocksize / 2);
Kevin O'Connor3a049632008-03-11 11:48:04 -0400303 }
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400304 buf_fl += blocksize;
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400305
Kevin O'Connor580e3322009-02-06 22:36:53 -0500306 status = pause_await_not_bsy(iobase1, iobase2);
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400307 if (status < 0) {
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400308 // Error
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400309 op->count -= count;
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400310 return status;
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400311 }
Kevin O'Connor3a049632008-03-11 11:48:04 -0400312
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400313 count--;
314 if (!count)
Kevin O'Connor3a049632008-03-11 11:48:04 -0400315 break;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500316 status &= (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR);
317 if (status != ATA_CB_STAT_DRQ) {
Kevin O'Connor14021f22009-12-26 23:21:38 -0500318 dprintf(6, "ata_pio_transfer : more sectors left (status %02x)\n"
Kevin O'Connor580e3322009-02-06 22:36:53 -0500319 , status);
Kevin O'Connor9ae1e9b2009-08-09 18:06:40 -0400320 op->count -= count;
Kevin O'Connora05223c2008-06-28 12:15:57 -0400321 return -6;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500322 }
323 }
Kevin O'Connor3a049632008-03-11 11:48:04 -0400324
Kevin O'Connor580e3322009-02-06 22:36:53 -0500325 status &= (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ
326 | ATA_CB_STAT_ERR);
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400327 if (!iswrite)
Kevin O'Connor3a049632008-03-11 11:48:04 -0400328 status &= ~ATA_CB_STAT_DF;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500329 if (status != 0) {
Kevin O'Connor14021f22009-12-26 23:21:38 -0500330 dprintf(6, "ata_pio_transfer : no sectors left (status %02x)\n", status);
Kevin O'Connora05223c2008-06-28 12:15:57 -0400331 return -7;
Kevin O'Connor3a049632008-03-11 11:48:04 -0400332 }
333
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500334 return 0;
335}
336
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400337
338/****************************************************************
Kevin O'Connor14021f22009-12-26 23:21:38 -0500339 * ATA DMA transfers
340 ****************************************************************/
341
342#define BM_CMD 0
343#define BM_CMD_MEMWRITE 0x08
344#define BM_CMD_START 0x01
345#define BM_STATUS 2
346#define BM_STATUS_IRQ 0x04
347#define BM_STATUS_ERROR 0x02
348#define BM_STATUS_ACTIVE 0x01
349#define BM_TABLE 4
350
351struct sff_dma_prd {
352 u32 buf_fl;
353 u32 count;
354};
355
356// Check if DMA available and setup transfer if so.
357static int
358ata_try_dma(struct disk_op_s *op, int iswrite, int blocksize)
359{
Kevin O'Connor46b82622012-05-13 12:10:30 -0400360 ASSERT16();
Kevin O'Connor4d079022010-01-17 12:58:47 -0500361 if (! CONFIG_ATA_DMA)
362 return -1;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500363 u32 dest = (u32)op->buf_fl;
364 if (dest & 1)
365 // Need minimum alignment of 1.
366 return -1;
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500367 struct atadrive_s *adrive_g = container_of(
368 op->drive_g, struct atadrive_s, drive);
369 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
370 u16 iomaster = GET_GLOBALFLAT(chan_gf->iomaster);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500371 if (! iomaster)
372 return -1;
373 u32 bytes = op->count * blocksize;
374 if (! bytes)
375 return -1;
376
377 // Build PRD dma structure.
Kevin O'Connor46b82622012-05-13 12:10:30 -0400378 struct sff_dma_prd *dma = MAKE_FLATPTR(SEG_LOW, ExtraStack);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500379 struct sff_dma_prd *origdma = dma;
380 while (bytes) {
381 if (dma >= &origdma[16])
382 // Too many descriptors..
383 return -1;
384 u32 count = bytes;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500385 u32 max = 0x10000 - (dest & 0xffff);
386 if (count > max)
387 count = max;
388
Kevin O'Connor1e3bd4f2012-05-24 23:56:19 -0400389 SET_LOWFLAT(dma->buf_fl, dest);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500390 bytes -= count;
391 if (!bytes)
392 // Last descriptor.
393 count |= 1<<31;
394 dprintf(16, "dma@%p: %08x %08x\n", dma, dest, count);
395 dest += count;
Kevin O'Connor1e3bd4f2012-05-24 23:56:19 -0400396 SET_LOWFLAT(dma->count, count);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500397 dma++;
398 }
399
400 // Program bus-master controller.
401 outl((u32)origdma, iomaster + BM_TABLE);
402 u8 oldcmd = inb(iomaster + BM_CMD) & ~(BM_CMD_MEMWRITE|BM_CMD_START);
403 outb(oldcmd | (iswrite ? 0x00 : BM_CMD_MEMWRITE), iomaster + BM_CMD);
404 outb(BM_STATUS_ERROR|BM_STATUS_IRQ, iomaster + BM_STATUS);
405
406 return 0;
407}
408
409// Transfer data using DMA.
410static int
411ata_dma_transfer(struct disk_op_s *op)
412{
Kevin O'Connor4d079022010-01-17 12:58:47 -0500413 if (! CONFIG_ATA_DMA)
414 return -1;
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500415 dprintf(16, "ata_dma_transfer id=%p buf=%p\n", op->drive_g, op->buf_fl);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500416
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500417 struct atadrive_s *adrive_g = container_of(
418 op->drive_g, struct atadrive_s, drive);
419 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
420 u16 iomaster = GET_GLOBALFLAT(chan_gf->iomaster);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500421
422 // Start bus-master controller.
423 u8 oldcmd = inb(iomaster + BM_CMD);
424 outb(oldcmd | BM_CMD_START, iomaster + BM_CMD);
425
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400426 u32 end = timer_calc(IDE_TIMEOUT);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500427 u8 status;
428 for (;;) {
429 status = inb(iomaster + BM_STATUS);
430 if (status & BM_STATUS_IRQ)
431 break;
432 // Transfer in progress
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400433 if (timer_check(end)) {
Kevin O'Connor14021f22009-12-26 23:21:38 -0500434 // Timeout.
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500435 warn_timeout();
Kevin O'Connor14021f22009-12-26 23:21:38 -0500436 break;
437 }
438 yield();
439 }
440 outb(oldcmd & ~BM_CMD_START, iomaster + BM_CMD);
441
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500442 u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
443 u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500444 int idestatus = pause_await_not_bsy(iobase1, iobase2);
445
446 if ((status & (BM_STATUS_IRQ|BM_STATUS_ACTIVE)) == BM_STATUS_IRQ
447 && idestatus >= 0x00
448 && (idestatus & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ
449 | ATA_CB_STAT_ERR)) == 0x00)
450 // Success.
451 return 0;
452
453 dprintf(6, "IDE DMA error (dma=%x ide=%x/%x/%x)\n", status, idestatus
454 , inb(iobase2 + ATA_CB_ASTAT), inb(iobase1 + ATA_CB_ERR));
455 op->count = 0;
456 return -1;
457}
458
459
460/****************************************************************
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400461 * ATA hard drive functions
462 ****************************************************************/
463
Kevin O'Connor14021f22009-12-26 23:21:38 -0500464// Transfer data to harddrive using PIO protocol.
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400465static int
Kevin O'Connor14021f22009-12-26 23:21:38 -0500466ata_pio_cmd_data(struct disk_op_s *op, int iswrite, struct ata_pio_command *cmd)
Kevin O'Connorf888f8c2008-03-23 00:04:54 -0400467{
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500468 struct atadrive_s *adrive_g = container_of(
469 op->drive_g, struct atadrive_s, drive);
470 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
471 u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
472 u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400473
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500474 // Disable interrupts
475 outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC);
476
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500477 int ret = send_cmd(adrive_g, cmd);
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400478 if (ret)
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500479 goto fail;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500480 ret = ata_wait_data(iobase1);
481 if (ret)
482 goto fail;
483 ret = ata_pio_transfer(op, iswrite, DISK_SECTOR_SIZE);
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500484
485fail:
486 // Enable interrupts
487 outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);
488 return ret;
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400489}
490
Kevin O'Connor14021f22009-12-26 23:21:38 -0500491// Transfer data to harddrive using DMA protocol.
492static int
493ata_dma_cmd_data(struct disk_op_s *op, struct ata_pio_command *cmd)
494{
Kevin O'Connor4d079022010-01-17 12:58:47 -0500495 if (! CONFIG_ATA_DMA)
496 return -1;
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500497 struct atadrive_s *adrive_g = container_of(
498 op->drive_g, struct atadrive_s, drive);
499 int ret = send_cmd(adrive_g, cmd);
Kevin O'Connor14021f22009-12-26 23:21:38 -0500500 if (ret)
501 return ret;
502 return ata_dma_transfer(op);
503}
504
505// Read/write count blocks from a harddrive.
506static int
507ata_readwrite(struct disk_op_s *op, int iswrite)
508{
509 u64 lba = op->lba;
510
511 int usepio = ata_try_dma(op, iswrite, DISK_SECTOR_SIZE);
512
513 struct ata_pio_command cmd;
514 memset(&cmd, 0, sizeof(cmd));
515
516 if (op->count >= (1<<8) || lba + op->count >= (1<<28)) {
517 cmd.sector_count2 = op->count >> 8;
518 cmd.lba_low2 = lba >> 24;
519 cmd.lba_mid2 = lba >> 32;
520 cmd.lba_high2 = lba >> 40;
521 lba &= 0xffffff;
522
523 if (usepio)
524 cmd.command = (iswrite ? ATA_CMD_WRITE_SECTORS_EXT
525 : ATA_CMD_READ_SECTORS_EXT);
526 else
527 cmd.command = (iswrite ? ATA_CMD_WRITE_DMA_EXT
528 : ATA_CMD_READ_DMA_EXT);
529 } else {
530 if (usepio)
531 cmd.command = (iswrite ? ATA_CMD_WRITE_SECTORS
532 : ATA_CMD_READ_SECTORS);
533 else
534 cmd.command = (iswrite ? ATA_CMD_WRITE_DMA
535 : ATA_CMD_READ_DMA);
536 }
537
538 cmd.sector_count = op->count;
539 cmd.lba_low = lba;
540 cmd.lba_mid = lba >> 8;
541 cmd.lba_high = lba >> 16;
542 cmd.device = ((lba >> 24) & 0xf) | ATA_CB_DH_LBA;
543
544 int ret;
545 if (usepio)
546 ret = ata_pio_cmd_data(op, iswrite, &cmd);
547 else
548 ret = ata_dma_cmd_data(op, &cmd);
549 if (ret)
550 return DISK_RET_EBADTRACK;
551 return DISK_RET_SUCCESS;
552}
553
554// 16bit command demuxer for ATA harddrives.
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400555int
556process_ata_op(struct disk_op_s *op)
557{
Kevin O'Connorc892b132009-08-11 21:59:37 -0400558 if (!CONFIG_ATA)
559 return 0;
560
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400561 struct atadrive_s *adrive_g = container_of(
562 op->drive_g, struct atadrive_s, drive);
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400563 switch (op->command) {
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400564 case CMD_READ:
Kevin O'Connor14021f22009-12-26 23:21:38 -0500565 return ata_readwrite(op, 0);
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400566 case CMD_WRITE:
Kevin O'Connor14021f22009-12-26 23:21:38 -0500567 return ata_readwrite(op, 1);
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400568 case CMD_RESET:
569 ata_reset(adrive_g);
570 return DISK_RET_SUCCESS;
571 case CMD_ISREADY:
572 return isready(adrive_g);
573 case CMD_FORMAT:
574 case CMD_VERIFY:
575 case CMD_SEEK:
576 return DISK_RET_SUCCESS;
Kevin O'Connor42337662009-08-10 00:06:37 -0400577 default:
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400578 op->count = 0;
579 return DISK_RET_EPARAM;
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400580 }
Kevin O'Connorf888f8c2008-03-23 00:04:54 -0400581}
582
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400583
584/****************************************************************
585 * ATAPI functions
586 ****************************************************************/
587
Kevin O'Connor7d700252010-02-15 11:56:07 -0500588#define CDROM_CDB_SIZE 12
589
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400590// Low-level atapi command transmit function.
Kevin O'Connor7d700252010-02-15 11:56:07 -0500591int
592atapi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500593{
Kevin O'Connor80c2b6e2010-12-05 12:52:02 -0500594 if (! CONFIG_ATA)
595 return 0;
596
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500597 struct atadrive_s *adrive_g = container_of(
598 op->drive_g, struct atadrive_s, drive);
599 struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
600 u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
601 u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500602
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400603 struct ata_pio_command cmd;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500604 memset(&cmd, 0, sizeof(cmd));
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400605 cmd.lba_mid = blocksize;
606 cmd.lba_high = blocksize >> 8;
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400607 cmd.command = ATA_CMD_PACKET;
608
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500609 // Disable interrupts
610 outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC);
611
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500612 int ret = send_cmd(adrive_g, &cmd);
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400613 if (ret)
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500614 goto fail;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500615 ret = ata_wait_data(iobase1);
616 if (ret)
617 goto fail;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500618
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400619 // Send command to device
Kevin O'Connor7d700252010-02-15 11:56:07 -0500620 outsw_fl(iobase1, MAKE_FLATPTR(GET_SEG(SS), cdbcmd), CDROM_CDB_SIZE / 2);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500621
Kevin O'Connor580e3322009-02-06 22:36:53 -0500622 int status = pause_await_not_bsy(iobase1, iobase2);
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500623 if (status < 0) {
624 ret = status;
625 goto fail;
626 }
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400627
Kevin O'Connor580e3322009-02-06 22:36:53 -0500628 if (status & ATA_CB_STAT_ERR) {
Kevin O'Connorb30c4002009-05-05 21:47:20 -0400629 u8 err = inb(iobase1 + ATA_CB_ERR);
630 // skip "Not Ready"
631 if (err != 0x20)
632 dprintf(6, "send_atapi_cmd : read error (status=%02x err=%02x)\n"
633 , status, err);
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500634 ret = -2;
635 goto fail;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500636 }
Paolo Bonzini39d59892012-03-19 11:41:09 +0100637 if (blocksize) {
638 if (!(status & ATA_CB_STAT_DRQ)) {
639 dprintf(6, "send_atapi_cmd : DRQ not set (status %02x)\n", status);
640 ret = -3;
641 goto fail;
642 }
Kevin O'Connor580e3322009-02-06 22:36:53 -0500643
Paolo Bonzini39d59892012-03-19 11:41:09 +0100644 ret = ata_pio_transfer(op, 0, blocksize);
645 }
Kevin O'Connor42bc3942009-11-20 17:28:19 -0500646
647fail:
648 // Enable interrupts
649 outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);
Kevin O'Connor76977b22010-02-17 01:01:32 -0500650 if (ret)
651 return DISK_RET_EBADTRACK;
652 return DISK_RET_SUCCESS;
Kevin O'Connoraa2590c2008-03-22 23:13:24 -0400653}
654
Kevin O'Connora6b9f712008-03-29 12:53:57 -0400655
656/****************************************************************
Kevin O'Connor0a924122009-02-08 19:43:47 -0500657 * ATA detect and init
658 ****************************************************************/
659
Kevin O'Connor14021f22009-12-26 23:21:38 -0500660// Send an identify device or identify device packet command.
661static int
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500662send_ata_identity(struct atadrive_s *adrive_g, u16 *buffer, int command)
Kevin O'Connor14021f22009-12-26 23:21:38 -0500663{
664 memset(buffer, 0, DISK_SECTOR_SIZE);
665
666 struct disk_op_s dop;
667 memset(&dop, 0, sizeof(dop));
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500668 dop.drive_g = &adrive_g->drive;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500669 dop.count = 1;
670 dop.lba = 1;
671 dop.buf_fl = MAKE_FLATPTR(GET_SEG(SS), buffer);
672
673 struct ata_pio_command cmd;
674 memset(&cmd, 0, sizeof(cmd));
675 cmd.command = command;
676
677 return ata_pio_cmd_data(&dop, 0, &cmd);
678}
679
Kevin O'Connorf2d48a32009-08-11 20:58:11 -0400680// Extract the ATA/ATAPI version info.
Gerd Hoffmann54fa8ec2010-11-29 09:42:12 +0100681int
682ata_extract_version(u16 *buffer)
Kevin O'Connorf2d48a32009-08-11 20:58:11 -0400683{
684 // Extract ATA/ATAPI version.
685 u16 ataversion = buffer[80];
686 u8 version;
687 for (version=15; version>0; version--)
688 if (ataversion & (1<<version))
689 break;
690 return version;
691}
692
Kevin O'Connor575ffc82010-02-21 23:20:10 -0500693#define MAXMODEL 40
Kevin O'Connor0a924122009-02-08 19:43:47 -0500694
Kevin O'Connor575ffc82010-02-21 23:20:10 -0500695// Extract the ATA/ATAPI model info.
Gerd Hoffmann54fa8ec2010-11-29 09:42:12 +0100696char *
697ata_extract_model(char *model, u32 size, u16 *buffer)
Kevin O'Connor575ffc82010-02-21 23:20:10 -0500698{
Kevin O'Connor0a924122009-02-08 19:43:47 -0500699 // Read model name
700 int i;
Gerd Hoffmann54fa8ec2010-11-29 09:42:12 +0100701 for (i=0; i<size/2; i++)
Kevin O'Connorb3064592012-08-14 21:20:10 -0400702 *(u16*)&model[i*2] = be16_to_cpu(buffer[27+i]);
Gerd Hoffmann54fa8ec2010-11-29 09:42:12 +0100703 model[size] = 0x00;
Kevin O'Connor9e881a32011-01-08 12:06:54 -0500704 nullTrailingSpace(model);
Kevin O'Connor575ffc82010-02-21 23:20:10 -0500705 return model;
Kevin O'Connor0a924122009-02-08 19:43:47 -0500706}
707
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500708// Common init code between ata and atapi
709static struct atadrive_s *
710init_atadrive(struct atadrive_s *dummy, u16 *buffer)
711{
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500712 struct atadrive_s *adrive_g = malloc_fseg(sizeof(*adrive_g));
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500713 if (!adrive_g) {
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500714 warn_noalloc();
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500715 return NULL;
716 }
717 memset(adrive_g, 0, sizeof(*adrive_g));
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500718 adrive_g->chan_gf = dummy->chan_gf;
719 adrive_g->slave = dummy->slave;
720 adrive_g->drive.cntl_id = adrive_g->chan_gf->chanid * 2 + dummy->slave;
721 adrive_g->drive.removable = (buffer[0] & 0x80) ? 1 : 0;
722 return adrive_g;
723}
724
Kevin O'Connor14021f22009-12-26 23:21:38 -0500725// Detect if the given drive is an atapi - initialize it if so.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500726static struct atadrive_s *
727init_drive_atapi(struct atadrive_s *dummy, u16 *buffer)
Kevin O'Connor0a924122009-02-08 19:43:47 -0500728{
729 // Send an IDENTIFY_DEVICE_PACKET command to device
Kevin O'Connor14021f22009-12-26 23:21:38 -0500730 int ret = send_ata_identity(dummy, buffer, ATA_CMD_IDENTIFY_PACKET_DEVICE);
Kevin O'Connor0a924122009-02-08 19:43:47 -0500731 if (ret)
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400732 return NULL;
Kevin O'Connor0a924122009-02-08 19:43:47 -0500733
734 // Success - setup as ATAPI.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500735 struct atadrive_s *adrive_g = init_atadrive(dummy, buffer);
736 if (!adrive_g)
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400737 return NULL;
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400738 adrive_g->drive.type = DTYPE_ATA_ATAPI;
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500739 adrive_g->drive.blksize = CDROM_SECTOR_SIZE;
740 adrive_g->drive.sectors = (u64)-1;
Kevin O'Connor42337662009-08-10 00:06:37 -0400741 u8 iscd = ((buffer[0] >> 8) & 0x1f) == 0x05;
Kevin O'Connor575ffc82010-02-21 23:20:10 -0500742 char model[MAXMODEL+1];
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500743 char *desc = znprintf(MAXDESCSIZE
744 , "DVD/CD [ata%d-%d: %s ATAPI-%d %s]"
745 , adrive_g->chan_gf->chanid, adrive_g->slave
746 , ata_extract_model(model, MAXMODEL, buffer)
747 , ata_extract_version(buffer)
748 , (iscd ? "DVD/CD" : "Device"));
749 dprintf(1, "%s\n", desc);
Kevin O'Connor42337662009-08-10 00:06:37 -0400750
751 // fill cdidmap
Kevin O'Connor031ef552010-12-27 19:26:57 -0500752 if (iscd) {
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -0400753 int prio = bootprio_find_ata_device(adrive_g->chan_gf->pci_tmp,
Kevin O'Connor031ef552010-12-27 19:26:57 -0500754 adrive_g->chan_gf->chanid,
755 adrive_g->slave);
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500756 boot_add_cd(&adrive_g->drive, desc, prio);
Kevin O'Connor031ef552010-12-27 19:26:57 -0500757 }
Kevin O'Connor0a924122009-02-08 19:43:47 -0500758
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500759 return adrive_g;
Kevin O'Connor0a924122009-02-08 19:43:47 -0500760}
761
Kevin O'Connor14021f22009-12-26 23:21:38 -0500762// Detect if the given drive is a regular ata drive - initialize it if so.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500763static struct atadrive_s *
764init_drive_ata(struct atadrive_s *dummy, u16 *buffer)
Kevin O'Connorc1437612008-05-18 01:43:07 -0400765{
Kevin O'Connor580e3322009-02-06 22:36:53 -0500766 // Send an IDENTIFY_DEVICE command to device
Kevin O'Connor14021f22009-12-26 23:21:38 -0500767 int ret = send_ata_identity(dummy, buffer, ATA_CMD_IDENTIFY_DEVICE);
Kevin O'Connorc1437612008-05-18 01:43:07 -0400768 if (ret)
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400769 return NULL;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500770
771 // Success - setup as ATA.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500772 struct atadrive_s *adrive_g = init_atadrive(dummy, buffer);
773 if (!adrive_g)
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400774 return NULL;
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500775 adrive_g->drive.type = DTYPE_ATA;
776 adrive_g->drive.blksize = DISK_SECTOR_SIZE;
Kevin O'Connorc1437612008-05-18 01:43:07 -0400777
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400778 adrive_g->drive.pchs.cylinder = buffer[1];
779 adrive_g->drive.pchs.head = buffer[3];
780 adrive_g->drive.pchs.sector = buffer[6];
Kevin O'Connorc1437612008-05-18 01:43:07 -0400781
782 u64 sectors;
Kevin O'Connorab515602009-02-11 22:22:15 -0500783 if (buffer[83] & (1 << 10)) // word 83 - lba48 support
784 sectors = *(u64*)&buffer[100]; // word 100-103
Kevin O'Connorc1437612008-05-18 01:43:07 -0400785 else
Kevin O'Connorab515602009-02-11 22:22:15 -0500786 sectors = *(u32*)&buffer[60]; // word 60 and word 61
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500787 adrive_g->drive.sectors = sectors;
Kevin O'Connor575ffc82010-02-21 23:20:10 -0500788 u64 adjsize = sectors >> 11;
789 char adjprefix = 'M';
790 if (adjsize >= (1 << 16)) {
791 adjsize >>= 10;
792 adjprefix = 'G';
793 }
794 char model[MAXMODEL+1];
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500795 char *desc = znprintf(MAXDESCSIZE
796 , "ata%d-%d: %s ATA-%d Hard-Disk (%u %ciBytes)"
797 , adrive_g->chan_gf->chanid, adrive_g->slave
798 , ata_extract_model(model, MAXMODEL, buffer)
799 , ata_extract_version(buffer)
800 , (u32)adjsize, adjprefix);
801 dprintf(1, "%s\n", desc);
Kevin O'Connorc1437612008-05-18 01:43:07 -0400802
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -0400803 int prio = bootprio_find_ata_device(adrive_g->chan_gf->pci_tmp,
Kevin O'Connor031ef552010-12-27 19:26:57 -0500804 adrive_g->chan_gf->chanid,
805 adrive_g->slave);
Kevin O'Connor0a924122009-02-08 19:43:47 -0500806 // Register with bcv system.
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500807 boot_add_hd(&adrive_g->drive, desc, prio);
Kevin O'Connoraafa6572008-03-13 19:57:49 -0400808
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500809 return adrive_g;
Kevin O'Connoraafa6572008-03-13 19:57:49 -0400810}
811
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400812static u32 SpinupEnd;
Kevin O'Connora5826b52009-10-24 17:57:29 -0400813
Kevin O'Connor14021f22009-12-26 23:21:38 -0500814// Wait for non-busy status and check for "floating bus" condition.
Kevin O'Connor425f2122009-04-18 12:23:00 -0400815static int
Kevin O'Connora5826b52009-10-24 17:57:29 -0400816powerup_await_non_bsy(u16 base)
Kevin O'Connor425f2122009-04-18 12:23:00 -0400817{
818 u8 orstatus = 0;
819 u8 status;
820 for (;;) {
821 status = inb(base+ATA_CB_STAT);
822 if (!(status & ATA_CB_STAT_BSY))
823 break;
824 orstatus |= status;
825 if (orstatus == 0xff) {
Kevin O'Connor9dc243e2010-03-20 17:53:03 -0400826 dprintf(4, "powerup IDE floating\n");
Kevin O'Connor425f2122009-04-18 12:23:00 -0400827 return orstatus;
828 }
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400829 if (timer_check(SpinupEnd)) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500830 warn_timeout();
Kevin O'Connor425f2122009-04-18 12:23:00 -0400831 return -1;
832 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400833 yield();
Kevin O'Connor425f2122009-04-18 12:23:00 -0400834 }
835 dprintf(6, "powerup iobase=%x st=%x\n", base, status);
836 return status;
837}
838
Kevin O'Connor14021f22009-12-26 23:21:38 -0500839// Detect any drives attached to a given controller.
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -0400840static void
Kevin O'Connora5826b52009-10-24 17:57:29 -0400841ata_detect(void *data)
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500842{
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500843 struct ata_channel_s *chan_gf = data;
844 struct atadrive_s dummy;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400845 memset(&dummy, 0, sizeof(dummy));
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500846 dummy.chan_gf = chan_gf;
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500847 // Device detection
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500848 int didreset = 0;
849 u8 slave;
850 for (slave=0; slave<=1; slave++) {
Kevin O'Connor425f2122009-04-18 12:23:00 -0400851 // Wait for not-bsy.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500852 u16 iobase1 = chan_gf->iobase1;
Kevin O'Connora5826b52009-10-24 17:57:29 -0400853 int status = powerup_await_non_bsy(iobase1);
Kevin O'Connor425f2122009-04-18 12:23:00 -0400854 if (status < 0)
855 continue;
856 u8 newdh = slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0;
857 outb(newdh, iobase1+ATA_CB_DH);
Kevin O'Connorc946eca2009-05-24 13:55:01 -0400858 ndelay(400);
Kevin O'Connora5826b52009-10-24 17:57:29 -0400859 status = powerup_await_non_bsy(iobase1);
Kevin O'Connor425f2122009-04-18 12:23:00 -0400860 if (status < 0)
861 continue;
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500862
Kevin O'Connor580e3322009-02-06 22:36:53 -0500863 // Check if ioport registers look valid.
Kevin O'Connor425f2122009-04-18 12:23:00 -0400864 outb(newdh, iobase1+ATA_CB_DH);
865 u8 dh = inb(iobase1+ATA_CB_DH);
866 outb(0x55, iobase1+ATA_CB_SC);
867 outb(0xaa, iobase1+ATA_CB_SN);
Kevin O'Connoraafa6572008-03-13 19:57:49 -0400868 u8 sc = inb(iobase1+ATA_CB_SC);
869 u8 sn = inb(iobase1+ATA_CB_SN);
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500870 dprintf(6, "ata_detect ata%d-%d: sc=%x sn=%x dh=%x\n"
871 , chan_gf->chanid, slave, sc, sn, dh);
Kevin O'Connor425f2122009-04-18 12:23:00 -0400872 if (sc != 0x55 || sn != 0xaa || dh != newdh)
Kevin O'Connoraafa6572008-03-13 19:57:49 -0400873 continue;
874
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400875 // Prepare new drive.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500876 dummy.slave = slave;
Kevin O'Connorb1144362009-08-11 20:43:38 -0400877
Kevin O'Connoraafa6572008-03-13 19:57:49 -0400878 // reset the channel
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500879 if (!didreset) {
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400880 ata_reset(&dummy);
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500881 didreset = 1;
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500882 }
883
Kevin O'Connor580e3322009-02-06 22:36:53 -0500884 // check for ATAPI
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400885 u16 buffer[256];
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500886 struct atadrive_s *adrive_g = init_drive_atapi(&dummy, buffer);
887 if (!adrive_g) {
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400888 // Didn't find an ATAPI drive - look for ATA drive.
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400889 u8 st = inb(iobase1+ATA_CB_STAT);
890 if (!st)
891 // Status not set - can't be a valid drive.
892 continue;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500893
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400894 // Wait for RDY.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400895 int ret = await_rdy(iobase1);
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400896 if (ret < 0)
897 continue;
Kevin O'Connor580e3322009-02-06 22:36:53 -0500898
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400899 // check for ATA.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500900 adrive_g = init_drive_ata(&dummy, buffer);
901 if (!adrive_g)
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400902 // No ATA drive found
903 continue;
904 }
Kevin O'Connor580e3322009-02-06 22:36:53 -0500905
Kevin O'Connorc79637b2009-06-10 20:33:57 -0400906 u16 resetresult = buffer[93];
907 dprintf(6, "ata_detect resetresult=%04x\n", resetresult);
908 if (!slave && (resetresult & 0xdf61) == 0x4041)
909 // resetresult looks valid and device 0 is responding to
910 // device 1 requests - device 1 must not be present - skip
911 // detection.
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500912 break;
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500913 }
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500914}
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -0400915
Kevin O'Connor14021f22009-12-26 23:21:38 -0500916// Initialize an ata controller and detect its drives.
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -0400917static void
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -0400918init_controller(struct pci_device *pci, int irq
919 , u32 port1, u32 port2, u32 master)
Kevin O'Connor4ccb2312009-12-05 11:25:09 -0500920{
Kevin O'Connor927d16e2011-06-19 09:43:20 -0400921 static int chanid = 0;
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500922 struct ata_channel_s *chan_gf = malloc_fseg(sizeof(*chan_gf));
923 if (!chan_gf) {
924 warn_noalloc();
925 return;
926 }
Kevin O'Connor927d16e2011-06-19 09:43:20 -0400927 chan_gf->chanid = chanid++;
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500928 chan_gf->irq = irq;
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -0400929 chan_gf->pci_bdf = pci ? pci->bdf : -1;
930 chan_gf->pci_tmp = pci;
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500931 chan_gf->iobase1 = port1;
932 chan_gf->iobase2 = port2;
933 chan_gf->iomaster = master;
Kevin O'Connor14021f22009-12-26 23:21:38 -0500934 dprintf(1, "ATA controller %d at %x/%x/%x (irq %d dev %x)\n"
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -0400935 , chanid, port1, port2, master, irq, chan_gf->pci_bdf);
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500936 run_thread(ata_detect, chan_gf);
Kevin O'Connor4ccb2312009-12-05 11:25:09 -0500937}
938
Kevin O'Connor525219b2009-12-05 13:36:18 -0500939#define IRQ_ATA1 14
940#define IRQ_ATA2 15
941
Kevin O'Connor927d16e2011-06-19 09:43:20 -0400942// Handle controllers on an ATA PCI device.
943static void
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400944init_pciata(struct pci_device *pci, u8 prog_if)
Kevin O'Connor927d16e2011-06-19 09:43:20 -0400945{
Kevin O'Connor76b5e712011-06-21 22:52:51 -0400946 pci->have_driver = 1;
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400947 u16 bdf = pci->bdf;
Kevin O'Connor927d16e2011-06-19 09:43:20 -0400948 u8 pciirq = pci_config_readb(bdf, PCI_INTERRUPT_LINE);
Kevin O'Connor927d16e2011-06-19 09:43:20 -0400949 int master = 0;
950 if (CONFIG_ATA_DMA && prog_if & 0x80) {
951 // Check for bus-mastering.
952 u32 bar = pci_config_readl(bdf, PCI_BASE_ADDRESS_4);
953 if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
954 master = bar & PCI_BASE_ADDRESS_IO_MASK;
955 pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER);
956 }
957 }
958
959 u32 port1, port2, irq;
960 if (prog_if & 1) {
961 port1 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_0)
962 & PCI_BASE_ADDRESS_IO_MASK);
963 port2 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_1)
964 & PCI_BASE_ADDRESS_IO_MASK);
965 irq = pciirq;
966 } else {
967 port1 = PORT_ATA1_CMD_BASE;
968 port2 = PORT_ATA1_CTRL_BASE;
969 irq = IRQ_ATA1;
970 }
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -0400971 init_controller(pci, irq, port1, port2, master);
Kevin O'Connor927d16e2011-06-19 09:43:20 -0400972
973 if (prog_if & 4) {
974 port1 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_2)
975 & PCI_BASE_ADDRESS_IO_MASK);
976 port2 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_3)
977 & PCI_BASE_ADDRESS_IO_MASK);
978 irq = pciirq;
979 } else {
980 port1 = PORT_ATA2_CMD_BASE;
981 port2 = PORT_ATA2_CTRL_BASE;
982 irq = IRQ_ATA2;
983 }
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -0400984 init_controller(pci, irq, port1, port2, master ? master + 8 : 0);
Kevin O'Connor927d16e2011-06-19 09:43:20 -0400985}
986
Kevin O'Connorb9457ec2011-06-19 10:03:08 -0400987static void
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400988found_genericata(struct pci_device *pci, void *arg)
Kevin O'Connorb9457ec2011-06-19 10:03:08 -0400989{
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400990 init_pciata(pci, pci->prog_if);
Kevin O'Connorb9457ec2011-06-19 10:03:08 -0400991}
992
993static void
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400994found_compatibleahci(struct pci_device *pci, void *arg)
Kevin O'Connorb9457ec2011-06-19 10:03:08 -0400995{
996 if (CONFIG_AHCI)
997 // Already handled directly via native ahci interface.
998 return;
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400999 init_pciata(pci, 0x8f);
Kevin O'Connorb9457ec2011-06-19 10:03:08 -04001000}
1001
Kevin O'Connor927d16e2011-06-19 09:43:20 -04001002static const struct pci_device_id pci_ata_tbl[] = {
Kevin O'Connorb9457ec2011-06-19 10:03:08 -04001003 PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE
1004 , found_genericata),
1005 PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4391, found_compatibleahci),
Kevin O'Connor927d16e2011-06-19 09:43:20 -04001006 PCI_DEVICE_END,
1007};
1008
Kevin O'Connor14021f22009-12-26 23:21:38 -05001009// Locate and init ata controllers.
Kevin O'Connor4ccb2312009-12-05 11:25:09 -05001010static void
Kevin O'Connord83c87b2013-01-21 01:14:12 -05001011ata_scan(void)
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -04001012{
Kevin O'Connorb98a4b12013-06-08 21:53:36 -04001013 if (CONFIG_QEMU && hlist_empty(&PCIDevices)) {
Kevin O'Connor4ccb2312009-12-05 11:25:09 -05001014 // No PCI devices found - probably a QEMU "-M isapc" machine.
1015 // Try using ISA ports for ATA controllers.
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -04001016 init_controller(NULL, IRQ_ATA1
Kevin O'Connor14021f22009-12-26 23:21:38 -05001017 , PORT_ATA1_CMD_BASE, PORT_ATA1_CTRL_BASE, 0);
Kevin O'Connor95b2e0c2011-07-09 14:42:11 -04001018 init_controller(NULL, IRQ_ATA2
Kevin O'Connor14021f22009-12-26 23:21:38 -05001019 , PORT_ATA2_CMD_BASE, PORT_ATA2_CTRL_BASE, 0);
Kevin O'Connor3f3e58d2011-06-20 22:20:43 -04001020 return;
1021 }
1022
1023 // Scan PCI bus for ATA adapters
1024 struct pci_device *pci;
1025 foreachpci(pci) {
Kevin O'Connor278b19f2011-06-21 22:41:15 -04001026 pci_init_device(pci_ata_tbl, pci, NULL);
Kevin O'Connor4ccb2312009-12-05 11:25:09 -05001027 }
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -04001028}
1029
1030void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -05001031ata_setup(void)
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -04001032{
Kevin O'Connor59c75742010-02-13 18:49:24 -05001033 ASSERT32FLAT();
Kevin O'Connorc1437612008-05-18 01:43:07 -04001034 if (!CONFIG_ATA)
1035 return;
1036
Kevin O'Connor35192dd2008-06-08 19:18:33 -04001037 dprintf(3, "init hard drives\n");
Kevin O'Connora5826b52009-10-24 17:57:29 -04001038
Kevin O'Connor018bdd72013-07-20 18:22:57 -04001039 SpinupEnd = timer_calc(IDE_TIMEOUT);
Kevin O'Connord83c87b2013-01-21 01:14:12 -05001040 ata_scan();
Kevin O'Connorc1437612008-05-18 01:43:07 -04001041
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -04001042 SET_BDA(disk_control_byte, 0xc0);
Kevin O'Connorf54c1502008-06-14 15:56:16 -04001043
Kevin O'Connorcc9e1bf2010-07-28 21:31:38 -04001044 enable_hwirq(14, FUNC16(entry_76));
Kevin O'Connor9f0d94d2008-04-13 17:25:30 -04001045}