summaryrefslogtreecommitdiffstats
path: root/drivers/ata/disk_ata_drive.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ata/disk_ata_drive.c')
-rw-r--r--drivers/ata/disk_ata_drive.c345
1 files changed, 36 insertions, 309 deletions
diff --git a/drivers/ata/disk_ata_drive.c b/drivers/ata/disk_ata_drive.c
index 6bb72a9756..923be9a26c 100644
--- a/drivers/ata/disk_ata_drive.c
+++ b/drivers/ata/disk_ata_drive.c
@@ -24,28 +24,7 @@
#include <block.h>
#include <ata_drive.h>
#include <disks.h>
-
-#define ATA_CMD_ID_DEVICE 0xEC
-#define ATA_CMD_RD_CONF 0x40
-#define ATA_CMD_RD 0x20
-#define ATA_CMD_WR 0x30
-
-#define DISK_MASTER 0
-#define DISK_SLAVE 1
-
-/* max timeout for a rotating disk in [ms] */
-#define MAX_TIMEOUT 5000
-
-/**
- * Collection of data we need to know about this drive
- */
-struct ata_drive_access {
- struct block_device blk; /**< the main device */
- struct ata_ioports *io; /**< register file */
- uint16_t id[(SECTOR_SIZE / sizeof(uint16_t))];
-};
-
-#define to_ata_drive_access(x) container_of((x), struct ata_drive_access, blk)
+#include <dma.h>
#define ata_id_u32(id,n) \
(((uint32_t) (id)[(n) + 1] << 16) | ((uint32_t) (id)[(n)]))
@@ -57,18 +36,6 @@ struct ata_drive_access {
#define ata_id_has_lba(id) ((id)[49] & (1 << 9))
-/* drive's status flags */
-#define ATA_STATUS_BUSY (1 << 7)
-#define ATA_STATUS_READY (1 << 6)
-#define ATA_STATUS_WR_FLT (1 << 5)
-#define ATA_STATUS_DRQ (1 << 4)
-#define ATA_STATUS_CORR (1 << 3)
-#define ATA_STATUS_ERROR (1 << 1)
-/* command flags */
-#define LBA_FLAG (1 << 6)
-#define ATA_DEVCTL_SOFT_RESET (1 << 2)
-#define ATA_DEVCTL_INTR_DISABLE (1 << 1)
-
enum {
ATA_ID_SERNO = 10,
#define ATA_ID_SERNO_LEN 20
@@ -240,225 +207,6 @@ static void ata_fix_endianess(uint16_t *buf, unsigned wds)
}
/**
- * Read the status register of the ATA drive
- * @param io Register file
- * @return Register's content
- */
-static uint8_t ata_rd_status(struct ata_ioports *io)
-{
- return readb(io->status_addr);
-}
-
-/**
- * Wait until the disk is busy or time out
- * @param io Register file
- * @param timeout Timeout in [ms]
- * @return 0 on success, -ETIMEDOUT else
- */
-static int ata_wait_busy(struct ata_ioports *io, unsigned timeout)
-{
- uint8_t status;
- uint64_t start = get_time_ns();
- uint64_t toffs = timeout * 1000 * 1000;
-
- do {
- status = ata_rd_status(io);
- if (status & ATA_STATUS_BUSY)
- return 0;
- } while (!is_timeout(start, toffs));
-
- return -ETIMEDOUT;
-}
-
-/**
- * Wait until the disk is ready again or time out
- * @param io Register file
- * @param timeout Timeout in [ms]
- * @return 0 on success, -ETIMEDOUT else
- *
- * This function is useful to check if the disk has accepted a command.
- */
-static int ata_wait_ready(struct ata_ioports *io, unsigned timeout)
-{
- uint8_t status;
- uint64_t start = get_time_ns();
- uint64_t toffs = timeout * 1000 * 1000;
-
- do {
- status = ata_rd_status(io);
- if (!(status & ATA_STATUS_BUSY)) {
- if (status & ATA_STATUS_READY)
- return 0;
- }
- } while (!is_timeout(start, toffs));
-
- return -ETIMEDOUT;
-}
-
-/**
- * Setup the sector number in LBA notation (LBA28)
- * @param io Register file
- * @param drive 0 master drive, 1 slave drive
- * @param num Sector number
- *
- * @todo LBA48 support
- */
-static int ata_set_lba_sector(struct ata_ioports *io, unsigned drive, uint64_t num)
-{
- if (num > 0x0FFFFFFF || drive > 1)
- return -EINVAL;
-
- writeb(0xA0 | LBA_FLAG | drive << 4 | num >> 24, io->device_addr);
- writeb(0x00, io->error_addr);
- writeb(0x01, io->nsect_addr);
- writeb(num, io->lbal_addr); /* 0 ... 7 */
- writeb(num >> 8, io->lbam_addr); /* 8 ... 15 */
- writeb(num >> 16, io->lbah_addr); /* 16 ... 23 */
-
- return 0;
-}
-
-/**
- * Write an ATA command into the disk
- * @param io Register file
- * @param cmd Command to write
- * @return 0 on success
- */
-static int ata_wr_cmd(struct ata_ioports *io, uint8_t cmd)
-{
- int rc;
-
- rc = ata_wait_ready(io, MAX_TIMEOUT);
- if (rc != 0)
- return rc;
-
- writeb(cmd, io->command_addr);
- return 0;
-}
-
-/**
- * Write a new value into the "device control register"
- * @param io Register file
- * @param val Value to write
- */
-static void ata_wr_dev_ctrl(struct ata_ioports *io, uint8_t val)
-{
- writeb(val, io->ctl_addr);
-}
-
-/**
- * Read one sector from the drive (always SECTOR_SIZE bytes at once)
- * @param io Register file
- * @param buf Buffer to read the data into
- */
-static void ata_rd_sector(struct ata_ioports *io, void *buf)
-{
- unsigned u = SECTOR_SIZE / sizeof(uint16_t);
- uint16_t *b = buf;
-
- if (io->dataif_be) {
- for (; u > 0; u--)
- *b++ = be16_to_cpu(readw(io->data_addr));
- } else {
- for (; u > 0; u--)
- *b++ = le16_to_cpu(readw(io->data_addr));
- }
-}
-
-/**
- * Write one sector into the drive
- * @param io Register file
- * @param buf Buffer to read the data from
- */
-static void ata_wr_sector(struct ata_ioports *io, const void *buf)
-{
- unsigned u = SECTOR_SIZE / sizeof(uint16_t);
- const uint16_t *b = buf;
-
- if (io->dataif_be) {
- for (; u > 0; u--)
- writew(cpu_to_be16(*b++), io->data_addr);
- } else {
- for (; u > 0; u--)
- writew(cpu_to_le16(*b++), io->data_addr);
- }
-}
-
-/**
- * Read the ATA disk's description info
- * @param d All we need to know about the disk
- * @return 0 on success
- */
-static int ata_get_id(struct ata_drive_access *d)
-{
- int rc;
-
- writeb(0xA0, d->io->device_addr); /* FIXME drive */
- writeb(0x00, d->io->lbal_addr);
- writeb(0x00, d->io->lbam_addr);
- writeb(0x00, d->io->lbah_addr);
-
- rc = ata_wr_cmd(d->io, ATA_CMD_ID_DEVICE);
- if (rc != 0)
- return rc;
-
- rc = ata_wait_ready(d->io, MAX_TIMEOUT);
- if (rc != 0)
- return rc;
-
- ata_rd_sector(d->io, &d->id);
-
- ata_fix_endianess(d->id, SECTOR_SIZE / sizeof(uint16_t));
-
- return ata_id_is_valid(d->id);
-}
-
-static int ata_reset(struct ata_ioports *io)
-{
- int rc;
- uint8_t reg;
-
- /* try a hard reset first (if available) */
- if (io->reset != NULL) {
- pr_debug("%s: Resetting drive...\n", __func__);
- io->reset(1);
- rc = ata_wait_busy(io, 500);
- io->reset(0);
- if (rc == 0) {
- rc = ata_wait_ready(io, MAX_TIMEOUT);
- if (rc != 0)
- return rc;
- } else {
- pr_debug("%s: Drive does not respond to RESET line. Ignored\n",
- __func__);
- }
- }
-
- /* try a soft reset */
- ata_wr_dev_ctrl(io, ATA_DEVCTL_SOFT_RESET | ATA_DEVCTL_INTR_DISABLE);
- rc = ata_wait_busy(io, MAX_TIMEOUT); /* does the drive accept the command? */
- if (rc != 0) {
- pr_debug("%s: Drive fails on soft reset\n", __func__);
- return rc;
- }
- ata_wr_dev_ctrl(io, ATA_DEVCTL_INTR_DISABLE);
- rc = ata_wait_ready(io, MAX_TIMEOUT);
- if (rc != 0) {
- pr_debug("%s: Drive fails after soft reset\n", __func__);
- return rc;
- }
-
- reg = ata_rd_status(io) & 0xf;
-
- if (reg == 0xf) {
- pr_debug("%s: Seems no drive connected!\n", __func__);
- return -ENODEV;
- }
-
- return 0;
-}
-
-/**
* Read a chunk of sectors from the drive
* @param blk All info about the block device we need
* @param buffer Buffer to read into
@@ -474,27 +222,9 @@ static int ata_reset(struct ata_ioports *io)
static int ata_read(struct block_device *blk, void *buffer, int block,
int num_blocks)
{
- int rc;
- uint64_t sector = block;
- struct ata_drive_access *drv = to_ata_drive_access(blk);
-
- while (num_blocks) {
- rc = ata_set_lba_sector(drv->io, DISK_MASTER, sector);
- if (rc != 0)
- return rc;
- rc = ata_wr_cmd(drv->io, ATA_CMD_RD);
- if (rc != 0)
- return rc;
- rc = ata_wait_ready(drv->io, MAX_TIMEOUT);
- if (rc != 0)
- return rc;
- ata_rd_sector(drv->io, buffer);
- num_blocks--;
- sector++;
- buffer += SECTOR_SIZE;
- }
+ struct ata_port *port = container_of(blk, struct ata_port, blk);
- return 0;
+ return port->ops->read(port, buffer, block, num_blocks);
}
/**
@@ -513,24 +243,9 @@ static int ata_read(struct block_device *blk, void *buffer, int block,
static int __maybe_unused ata_write(struct block_device *blk,
const void *buffer, int block, int num_blocks)
{
- int rc;
- uint64_t sector = block;
- struct ata_drive_access *drv = to_ata_drive_access(blk);
-
- while (num_blocks) {
- rc = ata_set_lba_sector(drv->io, DISK_MASTER, sector);
- if (rc != 0)
- return rc;
- rc = ata_wr_cmd(drv->io, ATA_CMD_WR);
- if (rc != 0)
- return rc;
- ata_wr_sector(drv->io, buffer);
- num_blocks--;
- sector++;
- buffer += SECTOR_SIZE;
- }
+ struct ata_port *port = container_of(blk, struct ata_port, blk);
- return 0;
+ return port->ops->write(port, buffer, block, num_blocks);
}
static struct block_device_ops ata_ops = {
@@ -546,55 +261,67 @@ static struct block_device_ops ata_ops = {
* @param io ATA register file description
* @return 0 on success
*/
-int register_ata_drive(struct device_d *dev, struct ata_ioports *io)
+int ata_port_register(struct ata_port *port)
{
int rc;
- struct ata_drive_access *drive;
+ struct ata_port_operations *ops = port->ops;
+ struct device_d *dev = port->dev;
- drive = xzalloc(sizeof(struct ata_drive_access));
+ port->id = dma_alloc(SECTOR_SIZE);
- drive->io = io;
- drive->blk.dev = dev;
- drive->blk.ops = &ata_ops;
+ port->blk.dev = dev;
+ port->blk.ops = &ata_ops;
- rc = ata_reset(io);
- if (rc) {
- dev_dbg(dev, "Resetting failed\n");
- goto on_error;
+ if (ops->reset) {
+ rc = ops->reset(port);
+ if (rc) {
+ dev_dbg(dev, "Resetting failed\n");
+ goto on_error;
+ }
}
- rc = ata_get_id(drive);
+ rc = ops->read_id(port, port->id);
if (rc != 0) {
dev_dbg(dev, "Reading ID failed\n");
goto on_error;
}
+ ata_fix_endianess(port->id, SECTOR_SIZE / sizeof(uint16_t));
+
+ rc = ata_id_is_valid(port->id);
+ if (rc) {
+ dev_err(dev, "ata id invalid\n");
+ free(port->id);
+ return rc;
+ }
+
#ifdef DEBUG
- ata_dump_id(drive->id);
+ ata_dump_id(port->id);
#endif
- rc = cdev_find_free_index("disk");
+ rc = cdev_find_free_index("ata");
if (rc == -1)
pr_err("Cannot find a free index for the disk node\n");
- drive->blk.num_blocks = ata_id_n_sectors(drive->id);
- drive->blk.cdev.name = asprintf("disk%d", rc);
- drive->blk.blockbits = SECTOR_SHIFT;
+ port->blk.num_blocks = ata_id_n_sectors(port->id);
+ port->blk.cdev.name = asprintf("ata%d", rc);
+ port->blk.blockbits = SECTOR_SHIFT;
- rc = blockdevice_register(&drive->blk);
+ rc = blockdevice_register(&port->blk);
if (rc != 0) {
dev_err(dev, "Failed to register blockdevice\n");
goto on_error;
}
+ dev_info(dev, "registered /dev/%s\n", port->blk.cdev.name);
+
/* create partitions on demand */
- rc = parse_partition_table(&drive->blk);
+ rc = parse_partition_table(&port->blk);
if (rc != 0)
dev_warn(dev, "No partition table found\n");
return 0;
on_error:
- free(drive);
return rc;
}