diff options
author | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2014-02-23 11:57:45 -0500 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2014-02-23 11:57:45 -0500 |
commit | c3f3e29ecd36052bfa4b01cd5d782c04836ae37a (patch) | |
tree | f5a999b1819901ccc2f0a1b1905a8d10bed9b283 /linux/dev/drivers | |
parent | 8d03c760d386d656ab5c0d976fbce30206501eac (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')
-rw-r--r-- | linux/dev/drivers/block/ahci.c | 260 |
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; } } |