summaryrefslogtreecommitdiff
path: root/linux/dev/drivers/block/ahci.c
diff options
context:
space:
mode:
authorSamuel Thibault <samuel.thibault@ens-lyon.org>2014-02-23 11:57:45 -0500
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2014-02-23 11:57:45 -0500
commitc3f3e29ecd36052bfa4b01cd5d782c04836ae37a (patch)
treef5a999b1819901ccc2f0a1b1905a8d10bed9b283 /linux/dev/drivers/block/ahci.c
parent8d03c760d386d656ab5c0d976fbce30206501eac (diff)
AHCI driver cleanups
* linux/dev/drivers/block/ahci.c (struct port): Add id and is_cd fields. (ahci_end_request): Only print error if quiet flag of the request is not set. (ahci_do_port_request): Upgrade sector parameter to 64 bit. Include those bits in the LBA48 request. (ahci_do_request): Upgrade sector count to 64bit. Only print errors if quiet flag of the request is not set. (ahci_probe_port): Move identify code... (ahci_identify): ... to new function. Handle WIN_PIDENTIFY case to recognize ATAPI devices. (ahci_probe_port): Also try WIN_PIDENTIFY command. (ahci_geninit): Avoid checking partition table on empty devices.
Diffstat (limited to 'linux/dev/drivers/block/ahci.c')
-rw-r--r--linux/dev/drivers/block/ahci.c260
1 files changed, 154 insertions, 106 deletions
diff --git a/linux/dev/drivers/block/ahci.c b/linux/dev/drivers/block/ahci.c
index 2c573ac..7df6b70 100644
--- a/linux/dev/drivers/block/ahci.c
+++ b/linux/dev/drivers/block/ahci.c
@@ -239,6 +239,8 @@ static struct port {
struct ahci_fis *fis;
struct ahci_cmd_tbl *prdtl;
+ struct hd_driveid id;
+ unsigned is_cd;
unsigned long long capacity; /* Nr of sectors */
u32 status; /* interrupt status */
unsigned cls; /* Command list maximum size.
@@ -264,9 +266,9 @@ static void ahci_end_request(int uptodate)
rq->errors = 0;
if (!uptodate) {
- printk("end_request: I/O error, dev %s, sector %lu\n",
- kdevname(rq->rq_dev), rq->sector);
- assert(0);
+ if (!rq->quiet)
+ printk("end_request: I/O error, dev %s, sector %lu\n",
+ kdevname(rq->rq_dev), rq->sector);
}
for (bh = rq->bh; bh; )
@@ -286,7 +288,7 @@ static void ahci_end_request(int uptodate)
}
/* Push the request to the controler port */
-static void ahci_do_port_request(struct port *port, unsigned sector, struct request *rq)
+static void ahci_do_port_request(struct port *port, unsigned long long sector, struct request *rq)
{
struct ahci_command *command = port->command;
struct ahci_cmd_tbl *prdtl = port->prdtl;
@@ -321,8 +323,8 @@ static void ahci_do_port_request(struct port *port, unsigned sector, struct requ
fis_h2d->lba2 = sector >> 16;
fis_h2d->lba3 = sector >> 24;
- fis_h2d->lba4 = 0;
- fis_h2d->lba5 = 0;
+ fis_h2d->lba4 = sector >> 32;
+ fis_h2d->lba5 = sector >> 40;
fis_h2d->countl = rq->nr_sectors;
fis_h2d->counth = rq->nr_sectors >> 8;
@@ -360,7 +362,7 @@ static void ahci_do_request() /* invoked with cli() */
{
struct request *rq;
unsigned minor, unit;
- unsigned long block, blockend;
+ unsigned long long block, blockend;
struct port *port;
rq = CURRENT;
@@ -393,12 +395,16 @@ static void ahci_do_request() /* invoked with cli() */
/* And check end */
blockend = block + rq->nr_sectors;
if (blockend < block) {
- printk("bad blockend %lu vs %lu\n", blockend, block);
+ if (!rq->quiet)
+ printk("bad blockend %lu vs %lu\n", (unsigned long) blockend, (unsigned long) block);
goto kill_rq;
}
if (blockend > port->capacity) {
- printk("offset for %u was %lu\n", minor, port->part[minor & PARTN_MASK].start_sect);
- printk("bad access: block %lu, count= %lu\n", blockend, (unsigned long) port->capacity);
+ if (!rq->quiet)
+ {
+ printk("offset for %u was %lu\n", minor, port->part[minor & PARTN_MASK].start_sect);
+ printk("bad access: block %lu, count= %lu\n", (unsigned long) blockend, (unsigned long) port->capacity);
+ }
goto kill_rq;
}
@@ -553,103 +559,17 @@ static void identify_timeout(unsigned long data)
static struct timer_list identify_timer = { .function = identify_timeout };
-/* Probe one AHCI port */
-static void ahci_probe_port(const volatile struct ahci_host *ahci_host, const volatile struct ahci_port *ahci_port)
+static int ahci_identify(const volatile struct ahci_host *ahci_host, const volatile struct ahci_port *ahci_port, struct port *port, unsigned cmd)
{
- struct port *port;
- void *mem;
struct hd_driveid id;
- unsigned cls = ((readl(&ahci_host->cap) >> 8) & 0x1f) + 1;
- struct ahci_command *command;
- struct ahci_fis *fis;
- struct ahci_cmd_tbl *prdtl;
struct ahci_fis_h2d *fis_h2d;
- vm_size_t size =
- cls * sizeof(*command)
- + sizeof(*fis)
- + cls * sizeof(*prdtl);
- unsigned i;
+ struct ahci_command *command = port->command;
+ struct ahci_cmd_tbl *prdtl = port->prdtl;
+ unsigned long flags;
unsigned slot;
unsigned long first_part;
unsigned long long timeout;
- unsigned long flags;
-
- for (i = 0; i < MAX_PORTS; i++) {
- if (!ports[i].ahci_port)
- break;
- }
- if (i == MAX_PORTS)
- return;
- port = &ports[i];
-
- /* Has to be 1K-aligned */
- mem = vmalloc (size);
- if (!mem)
- return;
- assert (!(((unsigned long) mem) & (1024-1)));
- memset (mem, 0, size);
-
- port->ahci_host = ahci_host;
- port->ahci_port = ahci_port;
- port->cls = cls;
-
- port->command = command = mem;
- port->fis = fis = (void*) command + cls * sizeof(*command);
- port->prdtl = prdtl = (void*) fis + sizeof(*fis);
-
- /* Stop commands */
- writel(readl(&ahci_port->cmd) & ~PORT_CMD_START, &ahci_port->cmd);
- timeout = jiffies + WAIT_MAX;
- while (readl(&ahci_port->cmd) & PORT_CMD_LIST_ON)
- if (jiffies > timeout) {
- printk("sd%u: timeout waiting for list completion\n", port-ports);
- port->ahci_host = NULL;
- port->ahci_port = NULL;
- return;
- }
-
- writel(readl(&ahci_port->cmd) & ~PORT_CMD_FIS_RX, &ahci_port->cmd);
- timeout = jiffies + WAIT_MAX;
- while (readl(&ahci_port->cmd) & PORT_CMD_FIS_ON)
- if (jiffies > timeout) {
- printk("sd%u: timeout waiting for FIS completion\n", port-ports);
- port->ahci_host = NULL;
- port->ahci_port = NULL;
- return;
- }
-
- /* We don't support 64bit */
- /* Point controller to our buffers */
- writel(0, &ahci_port->clbu);
- writel(vmtophys((void*) command), &ahci_port->clb);
- writel(0, &ahci_port->fbu);
- writel(vmtophys((void*) fis), &ahci_port->fb);
-
- /* Clear any previous interrupts */
- writel(readl(&ahci_port->is), &ahci_port->is);
- writel(1 << (ahci_port - ahci_host->ports), &ahci_host->is);
-
- /* And activate them */
- writel(DEF_PORT_IRQ, &ahci_port->ie);
- writel(readl(&ahci_host->ghc) | HOST_IRQ_EN, &ahci_host->ghc);
-
- for (i = 0; i < cls; i++)
- {
- command[i].ctbau = 0;
- command[i].ctba = vmtophys((void*) &prdtl[i]);
- }
-
- /* Start commands */
- timeout = jiffies + WAIT_MAX;
- while (readl(&ahci_port->cmd) & PORT_CMD_LIST_ON)
- if (jiffies > timeout) {
- printk("sd%u: timeout waiting for list completion\n", port-ports);
- port->ahci_host = NULL;
- port->ahci_port = NULL;
- return;
- }
-
- writel(readl(&ahci_port->cmd) | PORT_CMD_FIS_RX | PORT_CMD_START, &ahci_port->cmd);
+ int ret = 0;
/* Identify device */
/* TODO: make this a request */
@@ -658,7 +578,7 @@ static void ahci_probe_port(const volatile struct ahci_host *ahci_host, const vo
fis_h2d = (void*) &prdtl[slot].cfis;
fis_h2d->fis_type = FIS_TYPE_REG_H2D;
fis_h2d->flags = 128;
- fis_h2d->command = WIN_IDENTIFY;
+ fis_h2d->command = cmd;
fis_h2d->device = 0;
/* Fetch the 512 identify data */
@@ -695,7 +615,7 @@ static void ahci_probe_port(const volatile struct ahci_host *ahci_host, const vo
printk("sd%u: timeout waiting for ready\n", port-ports);
port->ahci_host = NULL;
port->ahci_port = NULL;
- return;
+ return 3;
}
save_flags(flags);
@@ -718,22 +638,48 @@ static void ahci_probe_port(const volatile struct ahci_host *ahci_host, const vo
port->ahci_host = NULL;
port->ahci_port = NULL;
del_timer(&identify_timer);
- return;
+ return 3;
}
sleep_on(&port->q);
}
del_timer(&identify_timer);
restore_flags(flags);
- if (readl(&ahci_port->is) & PORT_IRQ_TF_ERR)
+ if ((port->status & PORT_IRQ_TF_ERR) || readl(&ahci_port->is) & PORT_IRQ_TF_ERR)
{
- printk("sd%u: identify error\n", port-ports);
+ /* Identify error */
port->capacity = 0;
port->lba48 = 0;
+ ret = 2;
} else {
+ memcpy(&port->id, &id, sizeof(id));
+ port->is_cd = 0;
+
ide_fixstring(id.model, sizeof(id.model), 1);
ide_fixstring(id.fw_rev, sizeof(id.fw_rev), 1);
ide_fixstring(id.serial_no, sizeof(id.serial_no), 1);
+ if (cmd == WIN_PIDENTIFY)
+ {
+ unsigned char type = (id.config >> 8) & 0x1f;
+
+ printk("sd%u: %s, ATAPI ", port - ports, id.model);
+ if (type == 5)
+ {
+ printk("unsupported CDROM drive\n");
+ port->is_cd = 1;
+ port->lba48 = 0;
+ port->capacity = 0;
+ }
+ else
+ {
+ printk("unsupported type %d\n", type);
+ port->lba48 = 0;
+ port->capacity = 0;
+ return 2;
+ }
+ return 0;
+ }
+
if (id.command_set_2 & (1U<<10))
{
port->lba48 = 1;
@@ -760,6 +706,106 @@ static void ahci_probe_port(const volatile struct ahci_host *ahci_host, const vo
printk("sd%u: %s, %uMB w/%dkB Cache\n", port - ports, id.model, (unsigned) (port->capacity/2048), id.buf_size/2);
}
port->identify = 0;
+
+ return ret;
+}
+
+/* Probe one AHCI port */
+static void ahci_probe_port(const volatile struct ahci_host *ahci_host, const volatile struct ahci_port *ahci_port)
+{
+ struct port *port;
+ void *mem;
+ unsigned cls = ((readl(&ahci_host->cap) >> 8) & 0x1f) + 1;
+ struct ahci_command *command;
+ struct ahci_fis *fis;
+ struct ahci_cmd_tbl *prdtl;
+ vm_size_t size =
+ cls * sizeof(*command)
+ + sizeof(*fis)
+ + cls * sizeof(*prdtl);
+ unsigned i;
+ unsigned long long timeout;
+
+ for (i = 0; i < MAX_PORTS; i++) {
+ if (!ports[i].ahci_port)
+ break;
+ }
+ if (i == MAX_PORTS)
+ return;
+ port = &ports[i];
+
+ /* Has to be 1K-aligned */
+ mem = vmalloc (size);
+ if (!mem)
+ return;
+ assert (!(((unsigned long) mem) & (1024-1)));
+ memset (mem, 0, size);
+
+ port->ahci_host = ahci_host;
+ port->ahci_port = ahci_port;
+ port->cls = cls;
+
+ port->command = command = mem;
+ port->fis = fis = (void*) command + cls * sizeof(*command);
+ port->prdtl = prdtl = (void*) fis + sizeof(*fis);
+
+ /* Stop commands */
+ writel(readl(&ahci_port->cmd) & ~PORT_CMD_START, &ahci_port->cmd);
+ timeout = jiffies + WAIT_MAX;
+ while (readl(&ahci_port->cmd) & PORT_CMD_LIST_ON)
+ if (jiffies > timeout) {
+ printk("sd%u: timeout waiting for list completion\n", port-ports);
+ port->ahci_host = NULL;
+ port->ahci_port = NULL;
+ return;
+ }
+
+ writel(readl(&ahci_port->cmd) & ~PORT_CMD_FIS_RX, &ahci_port->cmd);
+ timeout = jiffies + WAIT_MAX;
+ while (readl(&ahci_port->cmd) & PORT_CMD_FIS_ON)
+ if (jiffies > timeout) {
+ printk("sd%u: timeout waiting for FIS completion\n", port-ports);
+ port->ahci_host = NULL;
+ port->ahci_port = NULL;
+ return;
+ }
+
+ /* We don't support 64bit */
+ /* Point controller to our buffers */
+ writel(0, &ahci_port->clbu);
+ writel(vmtophys((void*) command), &ahci_port->clb);
+ writel(0, &ahci_port->fbu);
+ writel(vmtophys((void*) fis), &ahci_port->fb);
+
+ /* Clear any previous interrupts */
+ writel(readl(&ahci_port->is), &ahci_port->is);
+ writel(1 << (ahci_port - ahci_host->ports), &ahci_host->is);
+
+ /* And activate them */
+ writel(DEF_PORT_IRQ, &ahci_port->ie);
+ writel(readl(&ahci_host->ghc) | HOST_IRQ_EN, &ahci_host->ghc);
+
+ for (i = 0; i < cls; i++)
+ {
+ command[i].ctbau = 0;
+ command[i].ctba = vmtophys((void*) &prdtl[i]);
+ }
+
+ /* Start commands */
+ timeout = jiffies + WAIT_MAX;
+ while (readl(&ahci_port->cmd) & PORT_CMD_LIST_ON)
+ if (jiffies > timeout) {
+ printk("sd%u: timeout waiting for list completion\n", port-ports);
+ port->ahci_host = NULL;
+ port->ahci_port = NULL;
+ return;
+ }
+
+ writel(readl(&ahci_port->cmd) | PORT_CMD_FIS_RX | PORT_CMD_START, &ahci_port->cmd);
+
+ if (ahci_identify(ahci_host, ahci_port, port, WIN_IDENTIFY) >= 2)
+ /* Try ATAPI */
+ ahci_identify(ahci_host, ahci_port, port, WIN_PIDENTIFY);
}
/* Probe one AHCI PCI device */
@@ -859,6 +905,8 @@ static void ahci_geninit(struct gendisk *gd)
for (unit = 0; unit < gd->nr_real; unit++) {
port = &ports[unit];
port->part[0].nr_sects = port->capacity;
+ if (!port->part[0].nr_sects)
+ port->part[0].nr_sects = -1;
}
}