aboutsummaryrefslogtreecommitdiff
path: root/linux/dev/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'linux/dev/drivers')
-rw-r--r--linux/dev/drivers/block/ahci.c348
-rw-r--r--linux/dev/drivers/block/floppy.c2
-rw-r--r--linux/dev/drivers/block/genhd.c26
3 files changed, 250 insertions, 126 deletions
diff --git a/linux/dev/drivers/block/ahci.c b/linux/dev/drivers/block/ahci.c
index 2c573acb..b60f1a19 100644
--- a/linux/dev/drivers/block/ahci.c
+++ b/linux/dev/drivers/block/ahci.c
@@ -36,8 +36,8 @@
/* minor: 2 bits for device number, 6 bits for partition number. */
-#define MAX_PORTS 4
-#define PARTN_BITS 6
+#define MAX_PORTS 8
+#define PARTN_BITS 5
#define PARTN_MASK ((1<<PARTN_BITS)-1)
/* We need to use one DMA scatter element per physical page.
@@ -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;
@@ -379,7 +381,7 @@ static void ahci_do_request() /* invoked with cli() */
minor = MINOR(rq->rq_dev);
unit = minor >> PARTN_BITS;
- if (unit > MAX_PORTS) {
+ if (unit >= MAX_PORTS) {
printk("bad ahci unit %u\n", unit);
goto kill_rq;
}
@@ -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,49 @@ 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;
+ restore_flags(flags);
+ 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 +707,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 */
@@ -779,41 +826,56 @@ static void ahci_probe_dev(unsigned char bus, unsigned char device)
/* Get configuration */
if (pcibios_read_config_byte(bus, device, PCI_HEADER_TYPE, &hdrtype) != PCIBIOS_SUCCESSFUL) {
- printk("ahci: %02u:%02u.%u: Can not read configuration", bus, dev, fun);
+ printk("ahci: %02x:%02x.%x: Can not read configuration", bus, dev, fun);
return;
}
if (hdrtype != 0) {
- printk("ahci: %02u:%02u.%u: Unknown hdrtype %d\n", bus, dev, fun, hdrtype);
+ printk("ahci: %02x:%02x.%x: Unknown hdrtype %d\n", bus, dev, fun, hdrtype);
return;
}
if (pcibios_read_config_dword(bus, device, PCI_BASE_ADDRESS_5, &bar) != PCIBIOS_SUCCESSFUL) {
- printk("ahci: %02u:%02u.%u: Can not read BAR 5", bus, dev, fun);
+ printk("ahci: %02x:%02x.%x: Can not read BAR 5", bus, dev, fun);
return;
}
- if (bar & 0x01) {
- printk("ahci: %02u:%02u.%u: BAR 5 is I/O?!", bus, dev, fun);
+ if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
+ printk("ahci: %02x:%02x.%x: BAR 5 is I/O?!", bus, dev, fun);
return;
}
- bar &= ~0x0f;
+ bar &= PCI_BASE_ADDRESS_MEM_MASK;
if (pcibios_read_config_byte(bus, device, PCI_INTERRUPT_LINE, &irq) != PCIBIOS_SUCCESSFUL) {
- printk("ahci: %02u:%02u.%u: Can not read IRQ", bus, dev, fun);
+ printk("ahci: %02x:%02x.%x: Can not read IRQ", bus, dev, fun);
return;
}
- printk("AHCI SATA %02u:%02u.%u BAR 0x%x IRQ %u\n", bus, dev, fun, bar, irq);
+ printk("AHCI SATA %02x:%02x.%x BAR 0x%x IRQ %u\n", bus, dev, fun, bar, irq);
/* Map mmio */
ahci_host = vremap(bar, 0x2000);
/* Request IRQ */
if (request_irq(irq, &ahci_interrupt, SA_SHIRQ, "ahci", (void*) ahci_host)) {
- printk("ahci: %02u:%02u.%u: Can not get irq %u\n", bus, dev, fun, irq);
+ printk("ahci: %02x:%02x.%x: Can not get irq %u\n", bus, dev, fun, irq);
return;
}
+#ifdef CONFIG_BLK_DEV_IDE
+ /* OK, we will handle it. Disable probing on legacy IDE ports it may have. */
+ for (i = 0; i < 6; i++)
+ {
+ unsigned mybar;
+ if (pcibios_read_config_dword(bus, device, PCI_BASE_ADDRESS_0 + i*4, &mybar) == PCIBIOS_SUCCESSFUL) {
+ if (!(bar & PCI_BASE_ADDRESS_SPACE_IO))
+ /* Memory, don't care */
+ continue;
+ /* printk("ahci: %02x:%02x.%x: BAR %d is %x\n", bus, dev, fun, i, mybar); */
+ ide_disable_base(bar & PCI_BASE_ADDRESS_IO_MASK);
+ }
+ }
+#endif
+
nports = (readl(&ahci_host->cap) & 0x1f) + 1;
port_map = readl(&ahci_host->pi);
@@ -822,7 +884,7 @@ static void ahci_probe_dev(unsigned char bus, unsigned char device)
n++;
if (nports != n) {
- printk("ahci: %02u:%02u.%u: Odd number of ports, assuming %d is correct\n", bus, dev, fun, nports);
+ printk("ahci: %02x:%02x.%x: Odd number of ports %u, assuming %u is correct\n", bus, dev, fun, n, nports);
port_map = 0;
}
if (!port_map) {
@@ -831,6 +893,7 @@ static void ahci_probe_dev(unsigned char bus, unsigned char device)
for (i = 0; i < AHCI_MAX_PORTS; i++) {
u32 ssts;
+ u8 spd, ipm;
if (!(port_map & (1U << i)))
continue;
@@ -838,12 +901,45 @@ static void ahci_probe_dev(unsigned char bus, unsigned char device)
ahci_port = &ahci_host->ports[i];
ssts = readl(&ahci_port->ssts);
- if ((ssts & 0xf) != 0x3)
- /* Device not present */
- continue;
- if (((ssts >> 8) & 0xf) != 0x1)
- /* Device down */
- continue;
+ spd = ssts & 0xf;
+ switch (spd)
+ {
+ case 0x0:
+ /* Device not present */
+ continue;
+ case 0x1:
+ printk("ahci: %02x:%02x.%x: Port %u communication not established. TODO: power on device\n", bus, dev, fun, i);
+ continue;
+ case 0x3:
+ /* Present and communication established */
+ break;
+ case 0x4:
+ printk("ahci: %02x:%02x.%x: Port %u phy offline?!\n", bus, dev, fun, i);
+ continue;
+ default:
+ printk("ahci: %02x:%02x.%x: Unknown port %u SPD %x\n", bus, dev, fun, i, spd);
+ continue;
+ }
+
+ ipm = (ssts >> 8) & 0xf;
+ switch (ipm)
+ {
+ case 0x0:
+ /* Device not present */
+ continue;
+ case 0x1:
+ /* Active */
+ break;
+ case 0x2:
+ printk("ahci: %02x:%02x.%x: Port %u in Partial power management. TODO: power on device\n", bus, dev, fun, i);
+ continue;
+ case 0x6:
+ printk("ahci: %02x:%02x.%x: Port %u in Slumber power management. TODO: power on device\n", bus, dev, fun, i);
+ continue;
+ default:
+ printk("ahci: %02x:%02x.%x: Unknown port %u IPM %x\n", bus, dev, fun, i, ipm);
+ continue;
+ }
/* OK! Probe this port */
ahci_probe_port(ahci_host, ahci_port);
@@ -859,6 +955,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;
}
}
diff --git a/linux/dev/drivers/block/floppy.c b/linux/dev/drivers/block/floppy.c
index 4c0977a3..83d66f05 100644
--- a/linux/dev/drivers/block/floppy.c
+++ b/linux/dev/drivers/block/floppy.c
@@ -3723,7 +3723,7 @@ static int floppy_revalidate(kdev_t dev)
return 1;
}
if (bh && !buffer_uptodate(bh))
- ll_rw_block(READ, 1, &bh);
+ ll_rw_block(READ, 1, &bh, 1);
process_fd_request();
wait_on_buffer(bh);
brelse(bh);
diff --git a/linux/dev/drivers/block/genhd.c b/linux/dev/drivers/block/genhd.c
index 95b499b1..3a861386 100644
--- a/linux/dev/drivers/block/genhd.c
+++ b/linux/dev/drivers/block/genhd.c
@@ -27,6 +27,8 @@
#ifdef CONFIG_BLK_DEV_INITRD
#include <linux/blk.h>
#endif
+#include <linux/hdreg.h>
+#include <alloca.h>
#include <asm/system.h>
@@ -768,12 +770,36 @@ static void setup_dev(struct gendisk *dev)
void device_setup(void)
{
extern void console_map_init(void);
+ extern char *kernel_cmdline;
+ char *c, *param, *white;
struct gendisk *p;
int nr=0;
#ifdef MACH
linux_intr_pri = SPL6;
#endif
+ for (c = kernel_cmdline; c; )
+ {
+ param = strstr(c, " ide");
+ if (!param)
+ param = strstr(c, " hd");
+ if (!param)
+ break;
+ if (param) {
+ param++;
+ white = strchr(param, ' ');
+ if (!white) {
+ ide_setup(param);
+ c = NULL;
+ } else {
+ char *word = alloca(white - param + 1);
+ strncpy(word, param, white - param);
+ word[white-param] = '\0';
+ ide_setup(word);
+ c = white + 1;
+ }
+ }
+ }
#ifndef MACH
chr_dev_init();
#endif