diff options
Diffstat (limited to 'linux/dev')
-rw-r--r-- | linux/dev/arch/i386/kernel/irq.c | 2 | ||||
-rw-r--r-- | linux/dev/drivers/block/ahci.c | 1009 | ||||
-rw-r--r-- | linux/dev/drivers/block/floppy.c | 2 | ||||
-rw-r--r-- | linux/dev/drivers/block/genhd.c | 26 | ||||
-rw-r--r-- | linux/dev/glue/block.c | 20 | ||||
-rw-r--r-- | linux/dev/glue/kmem.c | 6 | ||||
-rw-r--r-- | linux/dev/include/ahci.h | 268 | ||||
-rw-r--r-- | linux/dev/include/asm-i386/string.h | 36 | ||||
-rw-r--r-- | linux/dev/include/linux/blk.h | 5 | ||||
-rw-r--r-- | linux/dev/include/linux/blkdev.h | 1 | ||||
-rw-r--r-- | linux/dev/include/linux/fs.h | 4 | ||||
-rw-r--r-- | linux/dev/include/linux/mm.h | 1 | ||||
-rw-r--r-- | linux/dev/include/linux/types.h | 9 |
13 files changed, 1356 insertions, 33 deletions
diff --git a/linux/dev/arch/i386/kernel/irq.c b/linux/dev/arch/i386/kernel/irq.c index 68bf0c4b..7753814b 100644 --- a/linux/dev/arch/i386/kernel/irq.c +++ b/linux/dev/arch/i386/kernel/irq.c @@ -695,7 +695,7 @@ init_IRQ (void) * Program counter 0 of 8253 to interrupt hz times per second. */ outb_p (PIT_C0 | PIT_SQUAREMODE | PIT_READMODE, PITCTL_PORT); - outb_p (latch && 0xff, PITCTR0_PORT); + outb_p (latch & 0xff, PITCTR0_PORT); outb (latch >> 8, PITCTR0_PORT); /* diff --git a/linux/dev/drivers/block/ahci.c b/linux/dev/drivers/block/ahci.c new file mode 100644 index 00000000..9bb2e6b4 --- /dev/null +++ b/linux/dev/drivers/block/ahci.c @@ -0,0 +1,1009 @@ +/* + * Copyright (C) 2013 Free Software Foundation + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the program ; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <ahci.h> +#include <kern/assert.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/fs.h> +#include <linux/bios32.h> +#include <linux/major.h> +#include <linux/hdreg.h> +#include <linux/genhd.h> +#include <asm/io.h> + +#define MAJOR_NR SCSI_DISK_MAJOR +#include <linux/blk.h> + +/* Standard AHCI BAR for mmio */ +#define AHCI_PCI_BAR 5 + +/* minor: 2 bits for device number, 6 bits for partition number. */ + +#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. + * ll_rw_block creates at most 8 buffer heads */ +/* See MAX_BUF */ +#define PRDTL_SIZE 8 + +#define WAIT_MAX (1*HZ) /* Wait at most 1s for requests completion */ + +/* AHCI standard structures */ + +struct ahci_prdt { + u32 dba; /* Data base address */ + u32 dbau; /* upper 32bit */ + u32 rsv0; /* Reserved */ + + u32 dbc; /* Byte count bits 0-21, + * bit31 interrupt on completion. */ +}; + +struct ahci_cmd_tbl { + u8 cfis[64]; + u8 acmd[16]; + u8 rsv[48]; + + struct ahci_prdt prdtl[PRDTL_SIZE]; +}; + +struct ahci_command { + u32 opts; /* Command options */ + + u32 prdbc; /* Physical Region Descriptor byte count */ + + u32 ctba; /* Command Table Descriptor Base Address */ + u32 ctbau; /* upper 32bit */ + + u32 rsv1[4]; /* Reserved */ +}; + +struct ahci_fis_dma { + u8 fis_type; + u8 flags; + u8 rsved[2]; + u64 id; + u32 rsvd; + u32 offset; + u32 count; + u32 resvd; +}; + +struct ahci_fis_pio { + u8 fis_type; + u8 flags; + u8 status; + u8 error; + + u8 lba0; + u8 lba1; + u8 lba2; + u8 device; + + u8 lba3; + u8 lba4; + u8 lba5; + u8 rsv2; + + u8 countl; + u8 counth; + u8 rsv3; + u8 e_status; + + u16 tc; /* Transfer Count */ + u8 rsv4[2]; +}; + +struct ahci_fis_d2h { + u8 fis_type; + u8 flags; + u8 status; + u8 error; + + u8 lba0; + u8 lba1; + u8 lba2; + u8 device; + + u8 lba3; + u8 lba4; + u8 lba5; + u8 rsv2; + + u8 countl; + u8 counth; + u8 rsv3[2]; + + u8 rsv4[4]; +}; + +struct ahci_fis_dev { + u8 rsvd[8]; +}; + +struct ahci_fis_h2d { + u8 fis_type; + u8 flags; + u8 command; + u8 featurel; + + u8 lba0; + u8 lba1; + u8 lba2; + u8 device; + + u8 lba3; + u8 lba4; + u8 lba5; + u8 featureh; + + u8 countl; + u8 counth; + u8 icc; + u8 control; + + u8 rsv1[4]; +}; + +struct ahci_fis_data { + u8 fis_type; + u8 flags; + u8 rsv1[2]; + u32 data1[]; +}; + +struct ahci_fis { + struct ahci_fis_dma dma_fis; + u8 pad0[4]; + + struct ahci_fis_pio pio_fis; + u8 pad1[12]; + + struct ahci_fis_d2h d2h_fis; + u8 pad2[4]; + + struct ahci_fis_dev dev_fis; + + u8 ufis[64]; + + u8 rsv[0x100 - 0xa0]; +}; + +struct ahci_port { + u32 clb; /* Command List Base address */ + u32 clbu; /* upper 32bit */ + u32 fb; /* FIS Base */ + u32 fbu; /* upper 32bit */ + u32 is; /* Interrupt Status */ + u32 ie; /* Interrupt Enable */ + u32 cmd; /* Command and Status */ + u32 rsv0; /* Reserved */ + u32 tfd; /* Task File Data */ + u32 sig; /* Signature */ + u32 ssts; /* SATA Status */ + u32 sctl; /* SATA Control */ + u32 serr; /* SATA Error */ + u32 sact; /* SATA Active */ + u32 ci; /* Command Issue */ + u32 sntf; /* SATA Notification */ + u32 fbs; /* FIS-based switch control */ + u8 rsv1[0x70 - 0x44]; /* Reserved */ + u8 vendor[0x80 - 0x70]; /* Vendor-specific */ +}; + +struct ahci_host { + u32 cap; /* Host capabilities */ + u32 ghc; /* Global Host Control */ + u32 is; /* Interrupt Status */ + u32 pi; /* Port Implemented */ + u32 v; /* Version */ + u32 ccc_ctl; /* Command Completion Coalescing control */ + u32 ccc_pts; /* Command Completion Coalescing ports */ + u32 em_loc; /* Enclosure Management location */ + u32 em_ctrl; /* Enclosure Management control */ + u32 cap2; /* Host capabilities extended */ + u32 bohc; /* BIOS/OS Handoff Control and status */ + u8 rsv[0xa0 - 0x2c]; /* Reserved */ + u8 vendor[0x100 - 0xa0]; /* Vendor-specific */ + struct ahci_port ports[]; /* Up to 32 ports */ +}; + +/* Our own data */ + +static struct port { + /* memory-mapped regions */ + const volatile struct ahci_host *ahci_host; + const volatile struct ahci_port *ahci_port; + + /* host-memory buffers */ + struct ahci_command *command; + 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. + We currently only use 1. */ + struct wait_queue *q; /* IRQ wait queue */ + struct hd_struct *part; /* drive partition table */ + unsigned lba48; /* Whether LBA48 is supported */ + unsigned identify; /* Whether we are just identifying + at boot */ + struct gendisk *gd; +} ports[MAX_PORTS]; + + +/* do_request() gets called by the block layer to push a request to the disk. + We just push one, and when an interrupt tells it's over, we call do_request() + ourself again to push the next request, etc. */ + +/* Request completed, either successfully or with an error */ +static void ahci_end_request(int uptodate) +{ + struct request *rq = CURRENT; + struct buffer_head *bh; + + rq->errors = 0; + if (!uptodate) { + 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; ) + { + struct buffer_head *next = bh->b_reqnext; + bh->b_reqnext = NULL; + mark_buffer_uptodate (bh, uptodate); + unlock_buffer (bh); + bh = next; + } + + CURRENT = rq->next; + if (rq->sem != NULL) + up(rq->sem); + rq->rq_status = RQ_INACTIVE; + wake_up(&wait_for_request); +} + +/* Push the request to the controler port */ +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; + struct ahci_fis_h2d *fis_h2d; + unsigned slot = 0; + struct buffer_head *bh; + unsigned i; + + rq->rq_status = RQ_SCSI_BUSY; + + /* Shouldn't ever happen: the block glue is limited at 8 blocks */ + assert(rq->nr_sectors < 0x10000); + + fis_h2d = (void*) &prdtl[slot].cfis; + fis_h2d->fis_type = FIS_TYPE_REG_H2D; + fis_h2d->flags = 128; + if (port->lba48) + if (rq->cmd == READ) + fis_h2d->command = WIN_READDMA_EXT; + else + fis_h2d->command = WIN_WRITEDMA_EXT; + else + if (rq->cmd == READ) + fis_h2d->command = WIN_READDMA; + else + fis_h2d->command = WIN_WRITEDMA; + + fis_h2d->device = 1<<6; /* LBA */ + + fis_h2d->lba0 = sector; + fis_h2d->lba1 = sector >> 8; + fis_h2d->lba2 = sector >> 16; + + fis_h2d->lba3 = sector >> 24; + fis_h2d->lba4 = sector >> 32; + fis_h2d->lba5 = sector >> 40; + + fis_h2d->countl = rq->nr_sectors; + fis_h2d->counth = rq->nr_sectors >> 8; + + command[slot].opts = sizeof(*fis_h2d) / sizeof(u32); + + if (rq->cmd == WRITE) + command[slot].opts |= AHCI_CMD_WRITE; + + for (i = 0, bh = rq->bh; bh; i++, bh = bh->b_reqnext) + { + assert(i < PRDTL_SIZE); + assert((((unsigned long) bh->b_data) & ~PAGE_MASK) == + (((unsigned long) bh->b_data + bh->b_size - 1) & ~PAGE_MASK)); + prdtl[slot].prdtl[i].dbau = 0; + prdtl[slot].prdtl[i].dba = vmtophys(bh->b_data); + prdtl[slot].prdtl[i].dbc = bh->b_size - 1; + } + + command[slot].opts |= i << 16; + + /* Make sure main memory buffers are up to date */ + mb(); + + /* Issue command */ + writel(1 << slot, &port->ahci_port->ci); + + /* TODO: IRQ timeout handler */ +} + +/* Called by block core to push a request */ +/* TODO: ideally, would have one request queue per port */ +/* TODO: ideally, would use tags to process several requests at a time */ +static void ahci_do_request() /* invoked with cli() */ +{ + struct request *rq; + unsigned minor, unit; + unsigned long long block, blockend; + struct port *port; + + rq = CURRENT; + if (!rq) + return; + + if (rq->rq_status != RQ_ACTIVE) + /* Current one is already ongoing, let the interrupt handler + * push the new one when the current one is finished. */ + return; + + if (MAJOR(rq->rq_dev) != MAJOR_NR) { + printk("bad ahci major %u\n", MAJOR(rq->rq_dev)); + goto kill_rq; + } + + minor = MINOR(rq->rq_dev); + unit = minor >> PARTN_BITS; + if (unit > MAX_PORTS) { + printk("bad ahci unit %u\n", unit); + goto kill_rq; + } + + port = &ports[unit]; + + /* Compute start sector */ + block = rq->sector; + block += port->part[minor & PARTN_MASK].start_sect; + + /* And check end */ + blockend = block + rq->nr_sectors; + if (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) { + 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; + } + + /* Push this to the port */ + ahci_do_port_request(port, block, rq); + return; + +kill_rq: + ahci_end_request(0); +} + +/* The given port got an interrupt, terminate the current request if any */ +static void ahci_port_interrupt(struct port *port, u32 status) +{ + unsigned slot = 0; + + if (readl(&port->ahci_port->ci) & (1 << slot)) { + /* Command still pending */ + return; + } + + if (port->identify) { + port->status = status; + wake_up(&port->q); + return; + } + + if (!CURRENT || CURRENT->rq_status != RQ_SCSI_BUSY) { + /* No request currently running */ + return; + } + + if (status & (PORT_IRQ_TF_ERR | PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR | PORT_IRQ_IF_ERR | PORT_IRQ_IF_NONFATAL)) { + printk("ahci error %x %x\n", status, readl(&port->ahci_port->tfd)); + ahci_end_request(0); + return; + } + + ahci_end_request(1); +} + +/* Start of IRQ handler. Iterate over all ports for this host */ +static void ahci_interrupt (int irq, void *host, struct pt_regs *regs) +{ + struct port *port; + struct ahci_host *ahci_host = host; + u32 irq_mask; + u32 status; + + irq_mask = readl(&ahci_host->is); + + if (!irq_mask) + return; + + for (port = &ports[0]; port < &ports[MAX_PORTS]; port++) { + if (port->ahci_host == ahci_host && (irq_mask & (1 << (port->ahci_port - ahci_host->ports)))) { + status = readl(&port->ahci_port->is); + /* Clear interrupt before possibly triggering others */ + writel(status, &port->ahci_port->is); + ahci_port_interrupt (port, status); + } + } + + if (CURRENT) + /* Still some requests, queue another one */ + ahci_do_request(); + + /* Clear host after clearing ports */ + writel(irq_mask, &ahci_host->is); + + /* unlock */ +} + +static int ahci_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int major, unit; + + if (!inode || !inode->i_rdev) + return -EINVAL; + + major = MAJOR(inode->i_rdev); + if (major != MAJOR_NR) + return -ENOTTY; + + unit = DEVICE_NR(inode->i_rdev); + if (unit >= MAX_PORTS) + return -EINVAL; + + switch (cmd) { + case BLKRRPART: + if (!suser()) return -EACCES; + if (!ports[unit].gd) + return -EINVAL; + resetup_one_dev(ports[unit].gd, unit); + return 0; + default: + return -EPERM; + } +} + +static int ahci_open (struct inode *inode, struct file *file) +{ + int target; + + if (MAJOR(inode->i_rdev) != MAJOR_NR) + return -ENXIO; + + target = MINOR(inode->i_rdev) >> PARTN_BITS; + if (target >= MAX_PORTS) + return -ENXIO; + + if (!ports[target].ahci_port) + return -ENXIO; + + return 0; +} + +static void ahci_release (struct inode *inode, struct file *file) +{ +} + +static int ahci_fsync (struct inode *inode, struct file *file) +{ + printk("fsync\n"); + return -ENOSYS; +} + +static struct file_operations ahci_fops = { + .lseek = NULL, + .read = block_read, + .write = block_write, + .readdir = NULL, + .select = NULL, + .ioctl = ahci_ioctl, + .mmap = NULL, + .open = ahci_open, + .release = ahci_release, + .fsync = ahci_fsync, + .fasync = NULL, + .check_media_change = NULL, + .revalidate = NULL, +}; + +/* Disk timed out while processing identify, interrupt ahci_probe_port */ +static void identify_timeout(unsigned long data) +{ + struct port *port = (void*) data; + + wake_up(&port->q); +} + +static struct timer_list identify_timer = { .function = identify_timeout }; + +static int ahci_identify(const volatile struct ahci_host *ahci_host, const volatile struct ahci_port *ahci_port, struct port *port, unsigned cmd) +{ + struct hd_driveid id; + struct ahci_fis_h2d *fis_h2d; + 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; + int ret = 0; + + /* Identify device */ + /* TODO: make this a request */ + slot = 0; + + fis_h2d = (void*) &prdtl[slot].cfis; + fis_h2d->fis_type = FIS_TYPE_REG_H2D; + fis_h2d->flags = 128; + fis_h2d->command = cmd; + fis_h2d->device = 0; + + /* Fetch the 512 identify data */ + memset(&id, 0, sizeof(id)); + + command[slot].opts = sizeof(*fis_h2d) / sizeof(u32); + + first_part = PAGE_ALIGN((unsigned long) &id) - (unsigned long) &id; + + if (first_part && first_part < sizeof(id)) { + /* split over two pages */ + + command[slot].opts |= (2 << 16); + + prdtl[slot].prdtl[0].dbau = 0; + prdtl[slot].prdtl[0].dba = vmtophys((void*) &id); + prdtl[slot].prdtl[0].dbc = first_part - 1; + prdtl[slot].prdtl[1].dbau = 0; + prdtl[slot].prdtl[1].dba = vmtophys((void*) &id + first_part); + prdtl[slot].prdtl[1].dbc = sizeof(id) - first_part - 1; + } + else + { + command[slot].opts |= (1 << 16); + + prdtl[slot].prdtl[0].dbau = 0; + prdtl[slot].prdtl[0].dba = vmtophys((void*) &id); + prdtl[slot].prdtl[0].dbc = sizeof(id) - 1; + } + + timeout = jiffies + WAIT_MAX; + while (readl(&ahci_port->tfd) & (BUSY_STAT | DRQ_STAT)) + if (jiffies > timeout) { + printk("sd%u: timeout waiting for ready\n", port-ports); + port->ahci_host = NULL; + port->ahci_port = NULL; + return 3; + } + + save_flags(flags); + cli(); + + port->identify = 1; + port->status = 0; + + /* Issue command */ + mb(); + writel(1 << slot, &ahci_port->ci); + + timeout = jiffies + WAIT_MAX; + identify_timer.expires = timeout; + identify_timer.data = (unsigned long) port; + add_timer(&identify_timer); + while (!port->status) { + if (jiffies >= timeout) { + printk("sd%u: timeout waiting for ready\n", port-ports); + port->ahci_host = NULL; + port->ahci_port = NULL; + del_timer(&identify_timer); + restore_flags(flags); + return 3; + } + sleep_on(&port->q); + } + del_timer(&identify_timer); + restore_flags(flags); + + if ((port->status & PORT_IRQ_TF_ERR) || readl(&ahci_port->is) & PORT_IRQ_TF_ERR) + { + /* 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; + port->capacity = id.lba_capacity_2; + if (port->capacity >= (1ULL << 32)) + { + port->capacity = (1ULL << 32) - 1; + printk("Warning: truncating disk size to 2TiB\n"); + } + } + else + { + port->lba48 = 0; + port->capacity = id.lba_capacity; + if (port->capacity > (1ULL << 24)) + { + port->capacity = (1ULL << 24); + printk("Warning: truncating disk size to 128GiB\n"); + } + } + if (port->capacity/2048 >= 10240) + printk("sd%u: %s, %uGB w/%dkB Cache\n", port - ports, id.model, (unsigned) (port->capacity/(2048*1024)), id.buf_size/2); + else + 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 */ +static void ahci_probe_dev(unsigned char bus, unsigned char device) +{ + unsigned char hdrtype; + unsigned char dev, fun; + const volatile struct ahci_host *ahci_host; + const volatile struct ahci_port *ahci_port; + unsigned nports, n, i; + unsigned port_map; + unsigned bar; + unsigned char irq; + + dev = PCI_SLOT(device); + fun = PCI_FUNC(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); + return; + } + + if (hdrtype != 0) { + printk("ahci: %02u:%02u.%u: 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); + return; + } + if (bar & 0x01) { + printk("ahci: %02u:%02u.%u: BAR 5 is I/O?!", bus, dev, fun); + return; + } + bar &= ~0x0f; + + 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); + return; + } + + printk("AHCI SATA %02u:%02u.%u 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); + return; + } + + nports = (readl(&ahci_host->cap) & 0x1f) + 1; + port_map = readl(&ahci_host->pi); + + for (n = 0, i = 0; i < AHCI_MAX_PORTS; i++) + if (port_map & (1U << i)) + n++; + + if (nports != n) { + printk("ahci: %02u:%02u.%u: Odd number of ports %u, assuming %u is correct\n", bus, dev, fun, n, nports); + port_map = 0; + } + if (!port_map) { + port_map = (1U << nports) - 1; + } + + for (i = 0; i < AHCI_MAX_PORTS; i++) { + u32 ssts; + u8 spd, ipm; + + if (!(port_map & (1U << i))) + continue; + + ahci_port = &ahci_host->ports[i]; + + ssts = readl(&ahci_port->ssts); + spd = ssts & 0xf; + switch (spd) + { + case 0x0: + /* Device not present */ + continue; + case 0x1: + printk("ahci: %02u:%02u.%u: 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: %02u:%02u.%u: Phy offline?!\n", bus, dev, fun, i); + continue; + default: + printk("ahci: %02u:%02u.%u: 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: %02u:%02u.%u: Port %u in Partial power management. TODO: power on device\n", bus, dev, fun, i); + continue; + case 0x6: + printk("ahci: %02u:%02u.%u: Port %u in Slumber power management. TODO: power on device\n", bus, dev, fun, i); + continue; + default: + printk("ahci: %02u:%02u.%u: Unknown port %u IPM %x\n", bus, dev, fun, i, ipm); + continue; + } + + /* OK! Probe this port */ + ahci_probe_port(ahci_host, ahci_port); + } +} + +/* genhd callback to set size of disks */ +static void ahci_geninit(struct gendisk *gd) +{ + unsigned unit; + struct port *port; + + 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; + } +} + +/* Probe all AHCI PCI devices */ +void ahci_probe_pci(void) +{ + unsigned char bus, device; + unsigned short index; + int ret; + unsigned nports, unit, nminors; + struct port *port; + struct gendisk *gd, **gdp; + int *bs; + + for (index = 0; + (ret = pcibios_find_class(PCI_CLASS_STORAGE_SATA_AHCI, index, &bus, &device)) == PCIBIOS_SUCCESSFUL; + index++) + { + /* Note: this prevents from also having a SCSI controler. + * It shouldn't harm too much until we have proper hardware + * enumeration. + */ + if (register_blkdev(MAJOR_NR, "sd", &ahci_fops) < 0) + printk("could not register ahci\n"); + ahci_probe_dev(bus, device); + } + + for (nports = 0, port = &ports[0]; port < &ports[MAX_PORTS]; port++) + if (port->ahci_port) + nports++; + + nminors = nports * (1<<PARTN_BITS); + + gd = kmalloc(sizeof(*gd), GFP_KERNEL); + gd->sizes = kmalloc(nminors * sizeof(*gd->sizes), GFP_KERNEL); + gd->part = kmalloc(nminors * sizeof(*gd->part), GFP_KERNEL); + bs = kmalloc(nminors * sizeof(*bs), GFP_KERNEL); + + blksize_size[MAJOR_NR] = bs; + for (unit = 0; unit < nminors; unit++) + /* We prefer to transfer whole pages */ + *bs++ = PAGE_SIZE; + + memset(gd->part, 0, nminors * sizeof(*gd->part)); + + for (unit = 0; unit < nports; unit++) { + ports[unit].gd = gd; + ports[unit].part = &gd->part[unit << PARTN_BITS]; + } + + gd->major = MAJOR_NR; + gd->major_name = "sd"; + gd->minor_shift = PARTN_BITS; + gd->max_p = 1<<PARTN_BITS; + gd->max_nr = nports; + gd->nr_real = nports; + gd->init = ahci_geninit; + gd->next = NULL; + + for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) + ; + *gdp = gd; + + blk_dev[MAJOR_NR].request_fn = ahci_do_request; +} 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 diff --git a/linux/dev/glue/block.c b/linux/dev/glue/block.c index 8c41f088..da4ef38e 100644 --- a/linux/dev/glue/block.c +++ b/linux/dev/glue/block.c @@ -384,7 +384,7 @@ bread (kdev_t dev, int block, int size) bh = getblk (dev, block, size); if (bh) { - ll_rw_block (READ, 1, &bh); + ll_rw_block (READ, 1, &bh, 0); wait_on_buffer (bh); if (! buffer_uptodate (bh)) { @@ -444,7 +444,7 @@ enqueue_request (struct request *req) /* Perform the I/O operation RW on the buffer list BH containing NR buffers. */ void -ll_rw_block (int rw, int nr, struct buffer_head **bh) +ll_rw_block (int rw, int nr, struct buffer_head **bh, int quiet) { int i, bshift, bsize; unsigned major; @@ -476,6 +476,7 @@ ll_rw_block (int rw, int nr, struct buffer_head **bh) r->rq_dev = bh[0]->b_dev; r->cmd = rw; r->errors = 0; + r->quiet = quiet; r->sector = bh[0]->b_blocknr << (bshift - 9); r->current_nr_sectors = bh[0]->b_size >> 9; r->buffer = bh[0]->b_data; @@ -528,7 +529,7 @@ rdwr_partial (int rw, kdev_t dev, loff_t *off, bh->b_data = alloc_buffer (bh->b_size); if (! bh->b_data) return -ENOMEM; - ll_rw_block (READ, 1, &bh); + ll_rw_block (READ, 1, &bh, 0); wait_on_buffer (bh); if (buffer_uptodate (bh)) { @@ -542,7 +543,7 @@ rdwr_partial (int rw, kdev_t dev, loff_t *off, { memcpy (bh->b_data + o, *buf, c); bh->b_state = (1 << BH_Dirty) | (1 << BH_Lock); - ll_rw_block (WRITE, 1, &bh); + ll_rw_block (WRITE, 1, &bh, 0); wait_on_buffer (bh); if (! buffer_uptodate (bh)) { @@ -589,8 +590,8 @@ rdwr_full (int rw, kdev_t dev, loff_t *off, char **buf, int *resid, int bshift) set_bit (BH_Lock, &bh->b_state); if (rw == WRITE) set_bit (BH_Dirty, &bh->b_state); - cc = PAGE_SIZE - (((int) *buf) & PAGE_MASK); - if (cc >= BSIZE && ((int) *buf & 511) == 0) + cc = PAGE_SIZE - (((int) *buf + (nb << bshift)) & PAGE_MASK); + if (cc >= BSIZE && (((int) *buf + (nb << bshift)) & 511) == 0) cc &= ~BMASK; else { @@ -623,7 +624,8 @@ rdwr_full (int rw, kdev_t dev, loff_t *off, char **buf, int *resid, int bshift) } if (! err) { - ll_rw_block (rw, i, bhp); + assert (i > 0); + ll_rw_block (rw, i, bhp, 0); wait_on_buffer (bhp[i - 1]); } for (bh = bhead, cc = 0, j = 0; j < i; cc += bh->b_size, bh++, j++) @@ -1650,7 +1652,7 @@ device_get_status (void *d, dev_flavor_t flavor, dev_status_t status, /* It would be nice to return the block size as reported by the driver, but a lot of user level code assumes the sector size to be 512. */ - status[DEV_GET_SIZE_RECORD_SIZE] = 512; + status[DEV_GET_RECORDS_RECORD_SIZE] = 512; /* Always return DEV_GET_RECORDS_COUNT. This is what all native Mach drivers do, and makes it possible to detect the absence of the call by setting it to a different value on input. MiG @@ -1703,7 +1705,7 @@ device_get_status (void *d, dev_flavor_t flavor, dev_status_t status, static io_return_t device_set_status (void *d, dev_flavor_t flavor, dev_status_t status, - mach_msg_type_number_t *status_count) + mach_msg_type_number_t status_count) { struct block_data *bd = d; diff --git a/linux/dev/glue/kmem.c b/linux/dev/glue/kmem.c index 28321711..ff052ffc 100644 --- a/linux/dev/glue/kmem.c +++ b/linux/dev/glue/kmem.c @@ -560,6 +560,12 @@ vfree (void *addr) vmalloc_list_remove (p); } +unsigned long +vmtophys (void *addr) +{ + return kvtophys((vm_offset_t) addr); +} + /* XXX: Quick hacking. */ /* Remap physical address into virtual address. */ void * diff --git a/linux/dev/include/ahci.h b/linux/dev/include/ahci.h new file mode 100644 index 00000000..31977b63 --- /dev/null +++ b/linux/dev/include/ahci.h @@ -0,0 +1,268 @@ +#ifndef _GNUMACH_AHCI_H +#define _GNUMACH_AHCI_H +extern void ahci_probe_pci(void); + +/* From linux 3.9's drivers/ata/ahci.h */ + +/* + * ahci.h - Common AHCI SATA definitions and declarations + * + * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Please ALWAYS copy linux-ide@vger.kernel.org + * on emails. + * + * Copyright 2004-2005 Red Hat, Inc. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * AHCI hardware documentation: + * http://www.intel.com/technology/serialata/pdf/rev1_0.pdf + * http://www.intel.com/technology/serialata/pdf/rev1_1.pdf + * + */ + +enum { + AHCI_MAX_PORTS = 32, + AHCI_MAX_SG = 168, /* hardware max is 64K */ + AHCI_DMA_BOUNDARY = 0xffffffff, + AHCI_MAX_CMDS = 32, + AHCI_CMD_SZ = 32, + AHCI_CMD_SLOT_SZ = AHCI_MAX_CMDS * AHCI_CMD_SZ, + AHCI_RX_FIS_SZ = 256, + AHCI_CMD_TBL_CDB = 0x40, + AHCI_CMD_TBL_HDR_SZ = 0x80, + AHCI_CMD_TBL_SZ = AHCI_CMD_TBL_HDR_SZ + (AHCI_MAX_SG * 16), + AHCI_CMD_TBL_AR_SZ = AHCI_CMD_TBL_SZ * AHCI_MAX_CMDS, + AHCI_PORT_PRIV_DMA_SZ = AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_AR_SZ + + AHCI_RX_FIS_SZ, + AHCI_PORT_PRIV_FBS_DMA_SZ = AHCI_CMD_SLOT_SZ + + AHCI_CMD_TBL_AR_SZ + + (AHCI_RX_FIS_SZ * 16), + AHCI_IRQ_ON_SG = (1 << 31), + AHCI_CMD_ATAPI = (1 << 5), + AHCI_CMD_WRITE = (1 << 6), + AHCI_CMD_PREFETCH = (1 << 7), + AHCI_CMD_RESET = (1 << 8), + AHCI_CMD_CLR_BUSY = (1 << 10), + + RX_FIS_PIO_SETUP = 0x20, /* offset of PIO Setup FIS data */ + RX_FIS_D2H_REG = 0x40, /* offset of D2H Register FIS data */ + RX_FIS_SDB = 0x58, /* offset of SDB FIS data */ + RX_FIS_UNK = 0x60, /* offset of Unknown FIS data */ + + /* global controller registers */ + HOST_CAP = 0x00, /* host capabilities */ + HOST_CTL = 0x04, /* global host control */ + HOST_IRQ_STAT = 0x08, /* interrupt status */ + HOST_PORTS_IMPL = 0x0c, /* bitmap of implemented ports */ + HOST_VERSION = 0x10, /* AHCI spec. version compliancy */ + HOST_EM_LOC = 0x1c, /* Enclosure Management location */ + HOST_EM_CTL = 0x20, /* Enclosure Management Control */ + HOST_CAP2 = 0x24, /* host capabilities, extended */ + + /* HOST_CTL bits */ + HOST_RESET = (1 << 0), /* reset controller; self-clear */ + HOST_IRQ_EN = (1 << 1), /* global IRQ enable */ + HOST_AHCI_EN = (1 << 31), /* AHCI enabled */ + + /* HOST_CAP bits */ + HOST_CAP_SXS = (1 << 5), /* Supports External SATA */ + HOST_CAP_EMS = (1 << 6), /* Enclosure Management support */ + HOST_CAP_CCC = (1 << 7), /* Command Completion Coalescing */ + HOST_CAP_PART = (1 << 13), /* Partial state capable */ + HOST_CAP_SSC = (1 << 14), /* Slumber state capable */ + HOST_CAP_PIO_MULTI = (1 << 15), /* PIO multiple DRQ support */ + HOST_CAP_FBS = (1 << 16), /* FIS-based switching support */ + HOST_CAP_PMP = (1 << 17), /* Port Multiplier support */ + HOST_CAP_ONLY = (1 << 18), /* Supports AHCI mode only */ + HOST_CAP_CLO = (1 << 24), /* Command List Override support */ + HOST_CAP_LED = (1 << 25), /* Supports activity LED */ + HOST_CAP_ALPM = (1 << 26), /* Aggressive Link PM support */ + HOST_CAP_SSS = (1 << 27), /* Staggered Spin-up */ + HOST_CAP_MPS = (1 << 28), /* Mechanical presence switch */ + HOST_CAP_SNTF = (1 << 29), /* SNotification register */ + HOST_CAP_NCQ = (1 << 30), /* Native Command Queueing */ + HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */ + + /* HOST_CAP2 bits */ + HOST_CAP2_BOH = (1 << 0), /* BIOS/OS handoff supported */ + HOST_CAP2_NVMHCI = (1 << 1), /* NVMHCI supported */ + HOST_CAP2_APST = (1 << 2), /* Automatic partial to slumber */ + HOST_CAP2_SDS = (1 << 3), /* Support device sleep */ + HOST_CAP2_SADM = (1 << 4), /* Support aggressive DevSlp */ + HOST_CAP2_DESO = (1 << 5), /* DevSlp from slumber only */ + + /* registers for each SATA port */ + PORT_LST_ADDR = 0x00, /* command list DMA addr */ + PORT_LST_ADDR_HI = 0x04, /* command list DMA addr hi */ + PORT_FIS_ADDR = 0x08, /* FIS rx buf addr */ + PORT_FIS_ADDR_HI = 0x0c, /* FIS rx buf addr hi */ + PORT_IRQ_STAT = 0x10, /* interrupt status */ + PORT_IRQ_MASK = 0x14, /* interrupt enable/disable mask */ + PORT_CMD = 0x18, /* port command */ + PORT_TFDATA = 0x20, /* taskfile data */ + PORT_SIG = 0x24, /* device TF signature */ + PORT_CMD_ISSUE = 0x38, /* command issue */ + PORT_SCR_STAT = 0x28, /* SATA phy register: SStatus */ + PORT_SCR_CTL = 0x2c, /* SATA phy register: SControl */ + PORT_SCR_ERR = 0x30, /* SATA phy register: SError */ + PORT_SCR_ACT = 0x34, /* SATA phy register: SActive */ + PORT_SCR_NTF = 0x3c, /* SATA phy register: SNotification */ + PORT_FBS = 0x40, /* FIS-based Switching */ + PORT_DEVSLP = 0x44, /* device sleep */ + + /* PORT_IRQ_{STAT,MASK} bits */ + PORT_IRQ_COLD_PRES = (1 << 31), /* cold presence detect */ + PORT_IRQ_TF_ERR = (1 << 30), /* task file error */ + PORT_IRQ_HBUS_ERR = (1 << 29), /* host bus fatal error */ + PORT_IRQ_HBUS_DATA_ERR = (1 << 28), /* host bus data error */ + PORT_IRQ_IF_ERR = (1 << 27), /* interface fatal error */ + PORT_IRQ_IF_NONFATAL = (1 << 26), /* interface non-fatal error */ + PORT_IRQ_OVERFLOW = (1 << 24), /* xfer exhausted available S/G */ + PORT_IRQ_BAD_PMP = (1 << 23), /* incorrect port multiplier */ + + PORT_IRQ_PHYRDY = (1 << 22), /* PhyRdy changed */ + PORT_IRQ_DEV_ILCK = (1 << 7), /* device interlock */ + PORT_IRQ_CONNECT = (1 << 6), /* port connect change status */ + PORT_IRQ_SG_DONE = (1 << 5), /* descriptor processed */ + PORT_IRQ_UNK_FIS = (1 << 4), /* unknown FIS rx'd */ + PORT_IRQ_SDB_FIS = (1 << 3), /* Set Device Bits FIS rx'd */ + PORT_IRQ_DMAS_FIS = (1 << 2), /* DMA Setup FIS rx'd */ + PORT_IRQ_PIOS_FIS = (1 << 1), /* PIO Setup FIS rx'd */ + PORT_IRQ_D2H_REG_FIS = (1 << 0), /* D2H Register FIS rx'd */ + + PORT_IRQ_FREEZE = PORT_IRQ_HBUS_ERR | + PORT_IRQ_IF_ERR | + PORT_IRQ_CONNECT | + PORT_IRQ_PHYRDY | + PORT_IRQ_UNK_FIS | + PORT_IRQ_BAD_PMP, + PORT_IRQ_ERROR = PORT_IRQ_FREEZE | + PORT_IRQ_TF_ERR | + PORT_IRQ_HBUS_DATA_ERR, + DEF_PORT_IRQ = PORT_IRQ_ERROR | PORT_IRQ_SG_DONE | + PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | + PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS, + + /* PORT_CMD bits */ + PORT_CMD_ASP = (1 << 27), /* Aggressive Slumber/Partial */ + PORT_CMD_ALPE = (1 << 26), /* Aggressive Link PM enable */ + PORT_CMD_ATAPI = (1 << 24), /* Device is ATAPI */ + PORT_CMD_FBSCP = (1 << 22), /* FBS Capable Port */ + PORT_CMD_PMP = (1 << 17), /* PMP attached */ + PORT_CMD_LIST_ON = (1 << 15), /* cmd list DMA engine running */ + PORT_CMD_FIS_ON = (1 << 14), /* FIS DMA engine running */ + PORT_CMD_FIS_RX = (1 << 4), /* Enable FIS receive DMA engine */ + PORT_CMD_CLO = (1 << 3), /* Command list override */ + PORT_CMD_POWER_ON = (1 << 2), /* Power up device */ + PORT_CMD_SPIN_UP = (1 << 1), /* Spin up device */ + PORT_CMD_START = (1 << 0), /* Enable port DMA engine */ + + PORT_CMD_ICC_MASK = (0xf << 28), /* i/f ICC state mask */ + PORT_CMD_ICC_ACTIVE = (0x1 << 28), /* Put i/f in active state */ + PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */ + PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */ + + /* PORT_FBS bits */ + PORT_FBS_DWE_OFFSET = 16, /* FBS device with error offset */ + PORT_FBS_ADO_OFFSET = 12, /* FBS active dev optimization offset */ + PORT_FBS_DEV_OFFSET = 8, /* FBS device to issue offset */ + PORT_FBS_DEV_MASK = (0xf << PORT_FBS_DEV_OFFSET), /* FBS.DEV */ + PORT_FBS_SDE = (1 << 2), /* FBS single device error */ + PORT_FBS_DEC = (1 << 1), /* FBS device error clear */ + PORT_FBS_EN = (1 << 0), /* Enable FBS */ + + /* PORT_DEVSLP bits */ + PORT_DEVSLP_DM_OFFSET = 25, /* DITO multiplier offset */ + PORT_DEVSLP_DM_MASK = (0xf << 25), /* DITO multiplier mask */ + PORT_DEVSLP_DITO_OFFSET = 15, /* DITO offset */ + PORT_DEVSLP_MDAT_OFFSET = 10, /* Minimum assertion time */ + PORT_DEVSLP_DETO_OFFSET = 2, /* DevSlp exit timeout */ + PORT_DEVSLP_DSP = (1 << 1), /* DevSlp present */ + PORT_DEVSLP_ADSE = (1 << 0), /* Aggressive DevSlp enable */ + + /* hpriv->flags bits */ + +#define AHCI_HFLAGS(flags) .private_data = (void *)(flags) + + AHCI_HFLAG_NO_NCQ = (1 << 0), + AHCI_HFLAG_IGN_IRQ_IF_ERR = (1 << 1), /* ignore IRQ_IF_ERR */ + AHCI_HFLAG_IGN_SERR_INTERNAL = (1 << 2), /* ignore SERR_INTERNAL */ + AHCI_HFLAG_32BIT_ONLY = (1 << 3), /* force 32bit */ + AHCI_HFLAG_MV_PATA = (1 << 4), /* PATA port */ + AHCI_HFLAG_NO_MSI = (1 << 5), /* no PCI MSI */ + AHCI_HFLAG_NO_PMP = (1 << 6), /* no PMP */ + AHCI_HFLAG_SECT255 = (1 << 8), /* max 255 sectors */ + AHCI_HFLAG_YES_NCQ = (1 << 9), /* force NCQ cap on */ + AHCI_HFLAG_NO_SUSPEND = (1 << 10), /* don't suspend */ + AHCI_HFLAG_SRST_TOUT_IS_OFFLINE = (1 << 11), /* treat SRST timeout as + link offline */ + AHCI_HFLAG_NO_SNTF = (1 << 12), /* no sntf */ + AHCI_HFLAG_NO_FPDMA_AA = (1 << 13), /* no FPDMA AA */ + AHCI_HFLAG_YES_FBS = (1 << 14), /* force FBS cap on */ + AHCI_HFLAG_DELAY_ENGINE = (1 << 15), /* do not start engine on + port start (wait until + error-handling stage) */ + AHCI_HFLAG_MULTI_MSI = (1 << 16), /* multiple PCI MSIs */ + + /* ap->flags bits */ + + /* + AHCI_FLAG_COMMON = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | + ATA_FLAG_ACPI_SATA | ATA_FLAG_AN, + */ + + ICH_MAP = 0x90, /* ICH MAP register */ + + /* em constants */ + EM_MAX_SLOTS = 8, + EM_MAX_RETRY = 5, + + /* em_ctl bits */ + EM_CTL_RST = (1 << 9), /* Reset */ + EM_CTL_TM = (1 << 8), /* Transmit Message */ + EM_CTL_MR = (1 << 0), /* Message Received */ + EM_CTL_ALHD = (1 << 26), /* Activity LED */ + EM_CTL_XMT = (1 << 25), /* Transmit Only */ + EM_CTL_SMB = (1 << 24), /* Single Message Buffer */ + EM_CTL_SGPIO = (1 << 19), /* SGPIO messages supported */ + EM_CTL_SES = (1 << 18), /* SES-2 messages supported */ + EM_CTL_SAFTE = (1 << 17), /* SAF-TE messages supported */ + EM_CTL_LED = (1 << 16), /* LED messages supported */ + + /* em message type */ + EM_MSG_TYPE_LED = (1 << 0), /* LED */ + EM_MSG_TYPE_SAFTE = (1 << 1), /* SAF-TE */ + EM_MSG_TYPE_SES2 = (1 << 2), /* SES-2 */ + EM_MSG_TYPE_SGPIO = (1 << 3), /* SGPIO */ + + FIS_TYPE_REG_H2D = 0x27, + FIS_TYPE_REG_D2H = 0x34, + FIS_TYPE_DMA_ACT = 0x39, + FIS_TYPE_DMA_SETUP = 0x41, + FIS_TYPE_DATA = 0x46, + FIS_TYPE_BIST = 0x58, + FIS_TYPE_PIO_SETUP = 0x5F, + FIS_TYPE_DEV_BITS = 0xA1, +}; + +/* End from linux 3.9 */ + +#endif /* _GNUMACH_AHCI_H */ diff --git a/linux/dev/include/asm-i386/string.h b/linux/dev/include/asm-i386/string.h index bdb75455..f41ca5c0 100644 --- a/linux/dev/include/asm-i386/string.h +++ b/linux/dev/include/asm-i386/string.h @@ -28,7 +28,7 @@ */ #define __HAVE_ARCH_STRCPY -extern inline char * strcpy(char * dest,const char *src) +static inline char * strcpy(char * dest,const char *src) { int d0, d1, d2; __asm__ __volatile__( @@ -43,7 +43,7 @@ return dest; } #define __HAVE_ARCH_STRNCPY -extern inline char * strncpy(char * dest,const char *src,size_t count) +static inline char * strncpy(char * dest,const char *src,size_t count) { int d0, d1, d2, d3; __asm__ __volatile__( @@ -63,7 +63,7 @@ return dest; } #define __HAVE_ARCH_STRCAT -extern inline char * strcat(char * dest,const char * src) +static inline char * strcat(char * dest,const char * src) { int d0, d1, d2, d3; __asm__ __volatile__( @@ -81,7 +81,7 @@ return dest; } #define __HAVE_ARCH_STRNCAT -extern inline char * strncat(char * dest,const char * src,size_t count) +static inline char * strncat(char * dest,const char * src,size_t count) { int d0, d1, d2, d3; __asm__ __volatile__( @@ -105,7 +105,7 @@ return dest; } #define __HAVE_ARCH_STRCMP -extern inline int strcmp(const char * cs,const char * ct) +static inline int strcmp(const char * cs,const char * ct) { int d0, d1; register int __res; @@ -127,7 +127,7 @@ return __res; } #define __HAVE_ARCH_STRNCMP -extern inline int strncmp(const char * cs,const char * ct,size_t count) +static inline int strncmp(const char * cs,const char * ct,size_t count) { register int __res; int d0, d1, d2; @@ -151,7 +151,7 @@ return __res; } #define __HAVE_ARCH_STRCHR -extern inline char * strchr(const char * s, int c) +static inline char * strchr(const char * s, int c) { int d0; register char * __res; @@ -171,7 +171,7 @@ return __res; } #define __HAVE_ARCH_STRRCHR -extern inline char * strrchr(const char * s, int c) +static inline char * strrchr(const char * s, int c) { int d0, d1; register char * __res; @@ -189,7 +189,7 @@ return __res; } #define __HAVE_ARCH_STRLEN -extern inline size_t strlen(const char * s) +static inline size_t strlen(const char * s) { int d0; register int __res; @@ -203,7 +203,7 @@ __asm__ __volatile__( return __res; } -extern inline void * __memcpy(void * to, const void * from, size_t n) +static inline void * __memcpy(void * to, const void * from, size_t n) { int d0, d1, d2; __asm__ __volatile__( @@ -226,7 +226,7 @@ return (to); * This looks horribly ugly, but the compiler can optimize it totally, * as the count is constant. */ -extern inline void * __constant_memcpy(void * to, const void * from, size_t n) +static inline void * __constant_memcpy(void * to, const void * from, size_t n) { switch (n) { case 0: @@ -299,7 +299,7 @@ __asm__ __volatile__( \ __memcpy((t),(f),(n))) #define __HAVE_ARCH_MEMMOVE -extern inline void * memmove(void * dest,const void * src, size_t n) +static inline void * memmove(void * dest,const void * src, size_t n) { int d0, d1, d2; if (dest<src) @@ -327,7 +327,7 @@ return dest; #define memcmp __builtin_memcmp #define __HAVE_ARCH_MEMCHR -extern inline void * memchr(const void * cs,int c,size_t count) +static inline void * memchr(const void * cs,int c,size_t count) { int d0; register void * __res; @@ -344,7 +344,7 @@ __asm__ __volatile__( return __res; } -extern inline void * __memset_generic(void * s, char c,size_t count) +static inline void * __memset_generic(void * s, char c,size_t count) { int d0, d1; __asm__ __volatile__( @@ -365,7 +365,7 @@ return s; * things 32 bits at a time even when we don't know the size of the * area at compile-time.. */ -extern inline void * __constant_c_memset(void * s, unsigned long c, size_t count) +static inline void * __constant_c_memset(void * s, unsigned long c, size_t count) { int d0, d1; __asm__ __volatile__( @@ -386,7 +386,7 @@ return (s); /* Added by Gertjan van Wingerde to make minix and sysv module work */ #define __HAVE_ARCH_STRNLEN -extern inline size_t strnlen(const char * s, size_t count) +static inline size_t strnlen(const char * s, size_t count) { int d0; register int __res; @@ -410,7 +410,7 @@ return __res; * This looks horribly ugly, but the compiler can optimize it totally, * as we by now know that both pattern and count is constant.. */ -extern inline void * __constant_c_and_count_memset(void * s, unsigned long pattern, size_t count) +static inline void * __constant_c_and_count_memset(void * s, unsigned long pattern, size_t count) { switch (count) { case 0: @@ -469,7 +469,7 @@ __asm__ __volatile__("cld\n\t" \ * find the first occurrence of byte 'c', or 1 past the area if none */ #define __HAVE_ARCH_MEMSCAN -extern inline void * memscan(void * addr, int c, size_t size) +static inline void * memscan(void * addr, int c, size_t size) { if (!size) return addr; diff --git a/linux/dev/include/linux/blk.h b/linux/dev/include/linux/blk.h index 412b8641..156d91c4 100644 --- a/linux/dev/include/linux/blk.h +++ b/linux/dev/include/linux/blk.h @@ -391,8 +391,9 @@ static void end_request(int uptodate) { req->errors = 0; if (!uptodate) { - printk("end_request: I/O error, dev %s, sector %lu\n", - kdevname(req->rq_dev), req->sector); + if (!req->quiet) + printk("end_request: I/O error, dev %s, sector %lu\n", + kdevname(req->rq_dev), req->sector); #ifdef MACH for (bh = req->bh; bh; ) { diff --git a/linux/dev/include/linux/blkdev.h b/linux/dev/include/linux/blkdev.h index e9a40d7e..5bf0a288 100644 --- a/linux/dev/include/linux/blkdev.h +++ b/linux/dev/include/linux/blkdev.h @@ -23,6 +23,7 @@ struct request { kdev_t rq_dev; int cmd; /* READ or WRITE */ int errors; + int quiet; unsigned long sector; unsigned long nr_sectors; unsigned long current_nr_sectors; diff --git a/linux/dev/include/linux/fs.h b/linux/dev/include/linux/fs.h index 740ebb54..a2f9383e 100644 --- a/linux/dev/include/linux/fs.h +++ b/linux/dev/include/linux/fs.h @@ -638,7 +638,7 @@ extern int nr_buffer_heads; #define NR_LIST 4 #ifdef MACH -extern inline void +static inline void mark_buffer_uptodate (struct buffer_head *bh, int on) { if (on) @@ -733,7 +733,7 @@ extern struct file * get_empty_filp(void); extern int close_fp(struct file *filp); extern struct buffer_head * get_hash_table(kdev_t dev, int block, int size); extern struct buffer_head * getblk(kdev_t dev, int block, int size); -extern void ll_rw_block(int rw, int nr, struct buffer_head * bh[]); +extern void ll_rw_block(int rw, int nr, struct buffer_head * bh[], int quiet); extern void ll_rw_page(int rw, kdev_t dev, unsigned long nr, char * buffer); extern void ll_rw_swap_file(int rw, kdev_t dev, unsigned int *b, int nb, char *buffer); extern int is_read_only(kdev_t dev); diff --git a/linux/dev/include/linux/mm.h b/linux/dev/include/linux/mm.h index 0500e0cf..cd061378 100644 --- a/linux/dev/include/linux/mm.h +++ b/linux/dev/include/linux/mm.h @@ -281,6 +281,7 @@ extern void * vmalloc(unsigned long size); extern void * vremap(unsigned long offset, unsigned long size); extern void vfree(void * addr); extern int vread(char *buf, char *addr, int count); +extern unsigned long vmtophys (void *); /* mmap.c */ extern unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len, diff --git a/linux/dev/include/linux/types.h b/linux/dev/include/linux/types.h index 57bb25f4..b697d9ec 100644 --- a/linux/dev/include/linux/types.h +++ b/linux/dev/include/linux/types.h @@ -109,6 +109,15 @@ struct ustat { char f_fpack[6]; }; +/* stdint.h */ +typedef s8 int8_t; +typedef u8 uint8_t; +typedef s16 int16_t; +typedef u16 uint16_t; +typedef s32 int32_t; +typedef u32 uint32_t; +typedef s64 int64_t; +typedef u64 uint64_t; /* Yes, this is ugly. But that's why it is called glue code. */ |