Commit 8421a12c authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://bart.bkbits.net/ide-2.6

into ppc970.osdl.org:/home/torvalds/v2.6/linux
parents 67a2dad3 c31f6714
...@@ -656,15 +656,9 @@ static int e100_ide_build_dmatable (ide_drive_t *drive) ...@@ -656,15 +656,9 @@ static int e100_ide_build_dmatable (ide_drive_t *drive)
ata_tot_size = 0; ata_tot_size = 0;
if (HWGROUP(drive)->rq->flags & REQ_DRIVE_TASKFILE) { ide_map_sg(drive, rq);
sg_init_one(&sg[0], rq->buffer, rq->nr_sectors * SECTOR_SIZE);
hwif->sg_nents = i = 1;
}
else
{
hwif->sg_nents = i = blk_rq_map_sg(drive->queue, rq, hwif->sg_table);
}
i = hwif->sg_nents;
while(i) { while(i) {
/* /*
......
...@@ -212,33 +212,18 @@ static void icside_build_sglist(ide_drive_t *drive, struct request *rq) ...@@ -212,33 +212,18 @@ static void icside_build_sglist(ide_drive_t *drive, struct request *rq)
ide_hwif_t *hwif = drive->hwif; ide_hwif_t *hwif = drive->hwif;
struct icside_state *state = hwif->hwif_data; struct icside_state *state = hwif->hwif_data;
struct scatterlist *sg = hwif->sg_table; struct scatterlist *sg = hwif->sg_table;
int nents;
if (rq->flags & REQ_DRIVE_TASKFILE) { ide_map_sg(drive, rq);
ide_task_t *args = rq->special;
if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE)
hwif->sg_dma_direction = DMA_TO_DEVICE;
else
hwif->sg_dma_direction = DMA_FROM_DEVICE;
sg_init_one(sg, rq->buffer, rq->nr_sectors * SECTOR_SIZE);
nents = 1;
} else {
nents = blk_rq_map_sg(drive->queue, rq, sg);
if (rq_data_dir(rq) == READ) if (rq_data_dir(rq) == READ)
hwif->sg_dma_direction = DMA_FROM_DEVICE; hwif->sg_dma_direction = DMA_FROM_DEVICE;
else else
hwif->sg_dma_direction = DMA_TO_DEVICE; hwif->sg_dma_direction = DMA_TO_DEVICE;
}
nents = dma_map_sg(state->dev, sg, nents, hwif->sg_dma_direction); hwif->sg_nents = dma_map_sg(state->dev, sg, hwif->sg_nents,
hwif->sg_dma_direction);
hwif->sg_nents = nents;
} }
/* /*
* Configure the IOMD to give the appropriate timings for the transfer * Configure the IOMD to give the appropriate timings for the transfer
* mode being requested. We take the advice of the ATA standards, and * mode being requested. We take the advice of the ATA standards, and
...@@ -498,14 +483,6 @@ static int icside_dma_test_irq(ide_drive_t *drive) ...@@ -498,14 +483,6 @@ static int icside_dma_test_irq(ide_drive_t *drive)
ICS_ARCIN_V6_INTRSTAT_1)) & 1; ICS_ARCIN_V6_INTRSTAT_1)) & 1;
} }
static int icside_dma_verbose(ide_drive_t *drive)
{
printk(", %s (peak %dMB/s)",
ide_xfer_verbose(drive->current_speed),
2000 / drive->drive_data);
return 1;
}
static int icside_dma_timeout(ide_drive_t *drive) static int icside_dma_timeout(ide_drive_t *drive)
{ {
printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name); printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name);
...@@ -554,7 +531,6 @@ static void icside_dma_init(ide_hwif_t *hwif) ...@@ -554,7 +531,6 @@ static void icside_dma_init(ide_hwif_t *hwif)
hwif->dma_start = icside_dma_start; hwif->dma_start = icside_dma_start;
hwif->ide_dma_end = icside_dma_end; hwif->ide_dma_end = icside_dma_end;
hwif->ide_dma_test_irq = icside_dma_test_irq; hwif->ide_dma_test_irq = icside_dma_test_irq;
hwif->ide_dma_verbose = icside_dma_verbose;
hwif->ide_dma_timeout = icside_dma_timeout; hwif->ide_dma_timeout = icside_dma_timeout;
hwif->ide_dma_lostirq = icside_dma_lostirq; hwif->ide_dma_lostirq = icside_dma_lostirq;
......
...@@ -3039,10 +3039,9 @@ int ide_cdrom_probe_capabilities (ide_drive_t *drive) ...@@ -3039,10 +3039,9 @@ int ide_cdrom_probe_capabilities (ide_drive_t *drive)
printk(", %dkB Cache", be16_to_cpu(cap.buffer_size)); printk(", %dkB Cache", be16_to_cpu(cap.buffer_size));
#ifdef CONFIG_BLK_DEV_IDEDMA
if (drive->using_dma) if (drive->using_dma)
(void) HWIF(drive)->ide_dma_verbose(drive); ide_dma_verbose(drive);
#endif /* CONFIG_BLK_DEV_IDEDMA */
printk("\n"); printk("\n");
return nslots; return nslots;
......
...@@ -1244,7 +1244,7 @@ static void idedisk_setup (ide_drive_t *drive) ...@@ -1244,7 +1244,7 @@ static void idedisk_setup (ide_drive_t *drive)
printk(", CHS=%d/%d/%d", printk(", CHS=%d/%d/%d",
drive->bios_cyl, drive->bios_head, drive->bios_sect); drive->bios_cyl, drive->bios_head, drive->bios_sect);
if (drive->using_dma) if (drive->using_dma)
(void) HWIF(drive)->ide_dma_verbose(drive); ide_dma_verbose(drive);
printk("\n"); printk("\n");
drive->mult_count = 0; drive->mult_count = 0;
......
...@@ -207,66 +207,22 @@ int ide_build_sglist(ide_drive_t *drive, struct request *rq) ...@@ -207,66 +207,22 @@ int ide_build_sglist(ide_drive_t *drive, struct request *rq)
{ {
ide_hwif_t *hwif = HWIF(drive); ide_hwif_t *hwif = HWIF(drive);
struct scatterlist *sg = hwif->sg_table; struct scatterlist *sg = hwif->sg_table;
int nents;
nents = blk_rq_map_sg(drive->queue, rq, hwif->sg_table); if ((rq->flags & REQ_DRIVE_TASKFILE) && rq->nr_sectors > 256)
BUG();
ide_map_sg(drive, rq);
if (rq_data_dir(rq) == READ) if (rq_data_dir(rq) == READ)
hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; hwif->sg_dma_direction = PCI_DMA_FROMDEVICE;
else else
hwif->sg_dma_direction = PCI_DMA_TODEVICE; hwif->sg_dma_direction = PCI_DMA_TODEVICE;
return pci_map_sg(hwif->pci_dev, sg, nents, hwif->sg_dma_direction); return pci_map_sg(hwif->pci_dev, sg, hwif->sg_nents, hwif->sg_dma_direction);
} }
EXPORT_SYMBOL_GPL(ide_build_sglist); EXPORT_SYMBOL_GPL(ide_build_sglist);
/**
* ide_raw_build_sglist - map IDE scatter gather for DMA
* @drive: the drive to build the DMA table for
* @rq: the request holding the sg list
*
* Perform the PCI mapping magic necessary to access the source or
* target buffers of a taskfile request via PCI DMA. The lower layers
* of the kernel provide the necessary cache management so that we can
* operate in a portable fashion
*/
int ide_raw_build_sglist(ide_drive_t *drive, struct request *rq)
{
ide_hwif_t *hwif = HWIF(drive);
struct scatterlist *sg = hwif->sg_table;
int nents = 0;
ide_task_t *args = rq->special;
u8 *virt_addr = rq->buffer;
int sector_count = rq->nr_sectors;
if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE)
hwif->sg_dma_direction = PCI_DMA_TODEVICE;
else
hwif->sg_dma_direction = PCI_DMA_FROMDEVICE;
#if 1
if (sector_count > 256)
BUG();
if (sector_count > 128) {
#else
while (sector_count > 128) {
#endif
sg_init_one(&sg[nents], virt_addr, 128 * SECTOR_SIZE);
nents++;
virt_addr = virt_addr + (128 * SECTOR_SIZE);
sector_count -= 128;
}
sg_init_one(&sg[nents], virt_addr, sector_count * SECTOR_SIZE);
nents++;
return pci_map_sg(hwif->pci_dev, sg, nents, hwif->sg_dma_direction);
}
EXPORT_SYMBOL_GPL(ide_raw_build_sglist);
/** /**
* ide_build_dmatable - build IDE DMA table * ide_build_dmatable - build IDE DMA table
* *
...@@ -288,9 +244,6 @@ int ide_build_dmatable (ide_drive_t *drive, struct request *rq) ...@@ -288,9 +244,6 @@ int ide_build_dmatable (ide_drive_t *drive, struct request *rq)
int i; int i;
struct scatterlist *sg; struct scatterlist *sg;
if (HWGROUP(drive)->rq->flags & REQ_DRIVE_TASKFILE)
hwif->sg_nents = i = ide_raw_build_sglist(drive, rq);
else
hwif->sg_nents = i = ide_build_sglist(drive, rq); hwif->sg_nents = i = ide_build_sglist(drive, rq);
if (!i) if (!i)
...@@ -728,17 +681,14 @@ int __ide_dma_good_drive (ide_drive_t *drive) ...@@ -728,17 +681,14 @@ int __ide_dma_good_drive (ide_drive_t *drive)
EXPORT_SYMBOL(__ide_dma_good_drive); EXPORT_SYMBOL(__ide_dma_good_drive);
#ifdef CONFIG_BLK_DEV_IDEDMA_PCI void ide_dma_verbose(ide_drive_t *drive)
int __ide_dma_verbose (ide_drive_t *drive)
{ {
struct hd_driveid *id = drive->id; struct hd_driveid *id = drive->id;
ide_hwif_t *hwif = HWIF(drive); ide_hwif_t *hwif = HWIF(drive);
if (id->field_valid & 4) { if (id->field_valid & 4) {
if ((id->dma_ultra >> 8) && (id->dma_mword >> 8)) { if ((id->dma_ultra >> 8) && (id->dma_mword >> 8))
printk(", BUG DMA OFF"); goto bug_dma_off;
return hwif->ide_dma_off_quietly(drive);
}
if (id->dma_ultra & ((id->dma_ultra >> 8) & hwif->ultra_mask)) { if (id->dma_ultra & ((id->dma_ultra >> 8) & hwif->ultra_mask)) {
if (((id->dma_ultra >> 11) & 0x1F) && if (((id->dma_ultra >> 11) & 0x1F) &&
eighty_ninty_three(drive)) { eighty_ninty_three(drive)) {
...@@ -768,19 +718,22 @@ int __ide_dma_verbose (ide_drive_t *drive) ...@@ -768,19 +718,22 @@ int __ide_dma_verbose (ide_drive_t *drive)
printk(", (U)DMA"); /* Can be BIOS-enabled! */ printk(", (U)DMA"); /* Can be BIOS-enabled! */
} }
} else if (id->field_valid & 2) { } else if (id->field_valid & 2) {
if ((id->dma_mword >> 8) && (id->dma_1word >> 8)) { if ((id->dma_mword >> 8) && (id->dma_1word >> 8))
printk(", BUG DMA OFF"); goto bug_dma_off;
return hwif->ide_dma_off_quietly(drive);
}
printk(", DMA"); printk(", DMA");
} else if (id->field_valid & 1) { } else if (id->field_valid & 1) {
printk(", BUG"); printk(", BUG");
} }
return 1; return;
bug_dma_off:
printk(", BUG DMA OFF");
hwif->ide_dma_off_quietly(drive);
return;
} }
EXPORT_SYMBOL(__ide_dma_verbose); EXPORT_SYMBOL(ide_dma_verbose);
#ifdef CONFIG_BLK_DEV_IDEDMA_PCI
int __ide_dma_lostirq (ide_drive_t *drive) int __ide_dma_lostirq (ide_drive_t *drive)
{ {
printk("%s: DMA interrupt recovery\n", drive->name); printk("%s: DMA interrupt recovery\n", drive->name);
...@@ -955,8 +908,6 @@ void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_p ...@@ -955,8 +908,6 @@ void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_p
hwif->ide_dma_end = &__ide_dma_end; hwif->ide_dma_end = &__ide_dma_end;
if (!hwif->ide_dma_test_irq) if (!hwif->ide_dma_test_irq)
hwif->ide_dma_test_irq = &__ide_dma_test_irq; hwif->ide_dma_test_irq = &__ide_dma_test_irq;
if (!hwif->ide_dma_verbose)
hwif->ide_dma_verbose = &__ide_dma_verbose;
if (!hwif->ide_dma_timeout) if (!hwif->ide_dma_timeout)
hwif->ide_dma_timeout = &__ide_dma_timeout; hwif->ide_dma_timeout = &__ide_dma_timeout;
if (!hwif->ide_dma_lostirq) if (!hwif->ide_dma_lostirq)
......
...@@ -680,6 +680,9 @@ void ide_map_sg(ide_drive_t *drive, struct request *rq) ...@@ -680,6 +680,9 @@ void ide_map_sg(ide_drive_t *drive, struct request *rq)
ide_hwif_t *hwif = drive->hwif; ide_hwif_t *hwif = drive->hwif;
struct scatterlist *sg = hwif->sg_table; struct scatterlist *sg = hwif->sg_table;
if (hwif->sg_mapped) /* needed by ide-scsi */
return;
if ((rq->flags & REQ_DRIVE_TASKFILE) == 0) { if ((rq->flags & REQ_DRIVE_TASKFILE) == 0) {
hwif->sg_nents = blk_rq_map_sg(drive->queue, rq, sg); hwif->sg_nents = blk_rq_map_sg(drive->queue, rq, sg);
} else { } else {
...@@ -1219,12 +1222,15 @@ static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) ...@@ -1219,12 +1222,15 @@ static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error)
HWGROUP(drive)->rq = NULL; HWGROUP(drive)->rq = NULL;
rq->errors = 0; rq->errors = 0;
if (!rq->bio)
goto out;
rq->sector = rq->bio->bi_sector; rq->sector = rq->bio->bi_sector;
rq->current_nr_sectors = bio_iovec(rq->bio)->bv_len >> 9; rq->current_nr_sectors = bio_iovec(rq->bio)->bv_len >> 9;
rq->hard_cur_sectors = rq->current_nr_sectors; rq->hard_cur_sectors = rq->current_nr_sectors;
if (rq->bio)
rq->buffer = NULL; rq->buffer = NULL;
out:
return ret; return ret;
} }
......
...@@ -8,37 +8,6 @@ ...@@ -8,37 +8,6 @@
/* /*
* This is the /proc/ide/ filesystem implementation. * This is the /proc/ide/ filesystem implementation.
* *
* The major reason this exists is to provide sufficient access
* to driver and config data, such that user-mode programs can
* be developed to handle chipset tuning for most PCI interfaces.
* This should provide better utilities, and less kernel bloat.
*
* The entire pci config space for a PCI interface chipset can be
* retrieved by just reading it. e.g. "cat /proc/ide3/config"
*
* To modify registers *safely*, do something like:
* echo "P40:88" >/proc/ide/ide3/config
* That expression writes 0x88 to pci config register 0x40
* on the chip which controls ide3. Multiple tuples can be issued,
* and the writes will be completed as an atomic set:
* echo "P40:88 P41:35 P42:00 P43:00" >/proc/ide/ide3/config
*
* All numbers must be specified using pairs of ascii hex digits.
* It is important to note that these writes will be performed
* after waiting for the IDE controller (both interfaces)
* to be completely idle, to ensure no corruption of I/O in progress.
*
* Non-PCI registers can also be written, using "R" in place of "P"
* in the above examples. The size of the port transfer is determined
* by the number of pairs of hex digits given for the data. If a two
* digit value is given, the write will be a byte operation; if four
* digits are used, the write will be performed as a 16-bit operation;
* and if eight digits are specified, a 32-bit "dword" write will be
* performed. Odd numbers of digits are not permitted.
*
* If there is an error *anywhere* in the string of registers/data
* then *none* of the writes will be performed.
*
* Drive/Driver settings can be retrieved by reading the drive's * Drive/Driver settings can be retrieved by reading the drive's
* "settings" files. e.g. "cat /proc/ide0/hda/settings" * "settings" files. e.g. "cat /proc/ide0/hda/settings"
* To write a new value "val" into a specific setting "name", use: * To write a new value "val" into a specific setting "name", use:
...@@ -51,10 +20,6 @@ ...@@ -51,10 +20,6 @@
* returned data as 256 16-bit words. The "hdparm" utility will * returned data as 256 16-bit words. The "hdparm" utility will
* be updated someday soon to use this mechanism. * be updated someday soon to use this mechanism.
* *
* Feel free to develop and distribute fancy GUI configuration
* utilities for your favorite PCI chipsets. I'll be working on
* one for the Promise 20246 someday soon. -ml
*
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -74,227 +39,6 @@ ...@@ -74,227 +39,6 @@
#include <asm/io.h> #include <asm/io.h>
static int proc_ide_write_config(struct file *file, const char __user *buffer,
unsigned long count, void *data)
{
ide_hwif_t *hwif = (ide_hwif_t *)data;
ide_hwgroup_t *mygroup = (ide_hwgroup_t *)(hwif->hwgroup);
ide_hwgroup_t *mategroup = NULL;
unsigned long timeout;
unsigned long flags;
const char *start = NULL, *msg = NULL;
struct entry { u32 val; u16 reg; u8 size; u8 pci; } *prog, *q, *r;
int want_pci = 0;
char *buf, *s;
int err;
if (hwif->mate && hwif->mate->hwgroup)
mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup);
if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
return -EACCES;
if (count >= PAGE_SIZE)
return -EINVAL;
s = buf = (char *)__get_free_page(GFP_USER);
if (!buf)
return -ENOMEM;
err = -ENOMEM;
q = prog = (struct entry *)__get_free_page(GFP_USER);
if (!prog)
goto out;
err = -EFAULT;
if (copy_from_user(buf, buffer, count))
goto out1;
buf[count] = '\0';
while (isspace(*s))
s++;
while (*s) {
char *p;
int digits;
start = s;
if ((char *)(q + 1) > (char *)prog + PAGE_SIZE) {
msg = "too many entries";
goto parse_error;
}
switch (*s++) {
case 'R': q->pci = 0;
break;
case 'P': q->pci = 1;
want_pci = 1;
break;
default: msg = "expected 'R' or 'P'";
goto parse_error;
}
q->reg = simple_strtoul(s, &p, 16);
digits = p - s;
if (!digits || digits > 4 || (q->pci && q->reg > 0xff)) {
msg = "bad/missing register number";
goto parse_error;
}
if (*p++ != ':') {
msg = "missing ':'";
goto parse_error;
}
q->val = simple_strtoul(p, &s, 16);
digits = s - p;
if (digits != 2 && digits != 4 && digits != 8) {
msg = "bad data, 2/4/8 digits required";
goto parse_error;
}
q->size = digits / 2;
if (q->pci) {
#ifdef CONFIG_BLK_DEV_IDEPCI
if (q->reg & (q->size - 1)) {
msg = "misaligned access";
goto parse_error;
}
#else
msg = "not a PCI device";
goto parse_error;
#endif /* CONFIG_BLK_DEV_IDEPCI */
}
q++;
if (*s && !isspace(*s++)) {
msg = "expected whitespace after data";
goto parse_error;
}
while (isspace(*s))
s++;
}
/*
* What follows below is fucking insane, even for IDE people.
* For now I've dealt with the obvious problems on the parsing
* side, but IMNSHO we should simply remove the write access
* to /proc/ide/.../config, killing that FPOS completely.
*/
err = -EBUSY;
timeout = jiffies + (3 * HZ);
spin_lock_irqsave(&ide_lock, flags);
while (mygroup->busy ||
(mategroup && mategroup->busy)) {
spin_unlock_irqrestore(&ide_lock, flags);
if (time_after(jiffies, timeout)) {
printk("/proc/ide/%s/config: channel(s) busy, cannot write\n", hwif->name);
goto out1;
}
spin_lock_irqsave(&ide_lock, flags);
}
#ifdef CONFIG_BLK_DEV_IDEPCI
if (want_pci && (!hwif->pci_dev || hwif->pci_dev->vendor)) {
spin_unlock_irqrestore(&ide_lock, flags);
printk("proc_ide: PCI registers not accessible for %s\n",
hwif->name);
err = -EINVAL;
goto out1;
}
#endif /* CONFIG_BLK_DEV_IDEPCI */
for (r = prog; r < q; r++) {
unsigned int reg = r->reg, val = r->val;
if (r->pci) {
#ifdef CONFIG_BLK_DEV_IDEPCI
int rc = 0;
struct pci_dev *dev = hwif->pci_dev;
switch (q->size) {
case 1: msg = "byte";
rc = pci_write_config_byte(dev, reg, val);
break;
case 2: msg = "word";
rc = pci_write_config_word(dev, reg, val);
break;
case 4: msg = "dword";
rc = pci_write_config_dword(dev, reg, val);
break;
}
if (rc) {
spin_unlock_irqrestore(&ide_lock, flags);
printk("proc_ide_write_config: error writing %s at bus %02x dev %02x reg 0x%x value 0x%x\n",
msg, dev->bus->number, dev->devfn, reg, val);
printk("proc_ide_write_config: error %d\n", rc);
err = -EIO;
goto out1;
}
#endif /* CONFIG_BLK_DEV_IDEPCI */
} else { /* not pci */
switch (r->size) {
case 1: hwif->OUTB(val, reg);
break;
case 2: hwif->OUTW(val, reg);
break;
case 4: hwif->OUTL(val, reg);
break;
}
}
}
spin_unlock_irqrestore(&ide_lock, flags);
err = count;
out1:
free_page((unsigned long)prog);
out:
free_page((unsigned long)buf);
return err;
parse_error:
printk("parse error\n");
printk("proc_ide: error: %s: '%s'\n", msg, start);
err = -EINVAL;
goto out1;
}
static int proc_ide_read_config
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
char *out = page;
int len;
#ifdef CONFIG_BLK_DEV_IDEPCI
ide_hwif_t *hwif = (ide_hwif_t *)data;
struct pci_dev *dev = hwif->pci_dev;
if ((hwif->pci_dev && hwif->pci_dev->vendor) && dev && dev->bus) {
int reg = 0;
out += sprintf(out, "pci bus %02x device %02x vendor %04x "
"device %04x channel %d\n",
dev->bus->number, dev->devfn,
hwif->pci_dev->vendor, hwif->pci_dev->device,
hwif->channel);
do {
u8 val;
int rc = pci_read_config_byte(dev, reg, &val);
if (rc) {
printk("proc_ide_read_config: error %d reading"
" bus %02x dev %02x reg 0x%02x\n",
rc, dev->bus->number, dev->devfn, reg);
out += sprintf(out, "??%c",
(++reg & 0xf) ? ' ' : '\n');
} else
out += sprintf(out, "%02x%c",
val, (++reg & 0xf) ? ' ' : '\n');
} while (reg < 0x100);
} else
#endif /* CONFIG_BLK_DEV_IDEPCI */
out += sprintf(out, "(none)\n");
len = out - page;
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
}
static int proc_ide_read_imodel static int proc_ide_read_imodel
(char *page, char **start, off_t off, int count, int *eof, void *data) (char *page, char **start, off_t off, int count, int *eof, void *data)
{ {
...@@ -687,7 +431,6 @@ void destroy_proc_ide_drives(ide_hwif_t *hwif) ...@@ -687,7 +431,6 @@ void destroy_proc_ide_drives(ide_hwif_t *hwif)
static ide_proc_entry_t hwif_entries[] = { static ide_proc_entry_t hwif_entries[] = {
{ "channel", S_IFREG|S_IRUGO, proc_ide_read_channel, NULL }, { "channel", S_IFREG|S_IRUGO, proc_ide_read_channel, NULL },
{ "config", S_IFREG|S_IRUGO|S_IWUSR,proc_ide_read_config, proc_ide_write_config },
{ "mate", S_IFREG|S_IRUGO, proc_ide_read_mate, NULL }, { "mate", S_IFREG|S_IRUGO, proc_ide_read_mate, NULL },
{ "model", S_IFREG|S_IRUGO, proc_ide_read_imodel, NULL }, { "model", S_IFREG|S_IRUGO, proc_ide_read_imodel, NULL },
{ NULL, 0, NULL, NULL } { NULL, 0, NULL, NULL }
......
...@@ -694,7 +694,6 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif) ...@@ -694,7 +694,6 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif)
hwif->ide_dma_test_irq = tmp_hwif->ide_dma_test_irq; hwif->ide_dma_test_irq = tmp_hwif->ide_dma_test_irq;
hwif->ide_dma_host_on = tmp_hwif->ide_dma_host_on; hwif->ide_dma_host_on = tmp_hwif->ide_dma_host_on;
hwif->ide_dma_host_off = tmp_hwif->ide_dma_host_off; hwif->ide_dma_host_off = tmp_hwif->ide_dma_host_off;
hwif->ide_dma_verbose = tmp_hwif->ide_dma_verbose;
hwif->ide_dma_lostirq = tmp_hwif->ide_dma_lostirq; hwif->ide_dma_lostirq = tmp_hwif->ide_dma_lostirq;
hwif->ide_dma_timeout = tmp_hwif->ide_dma_timeout; hwif->ide_dma_timeout = tmp_hwif->ide_dma_timeout;
......
...@@ -331,17 +331,6 @@ sgiioc4_ide_dma_host_off(ide_drive_t * drive) ...@@ -331,17 +331,6 @@ sgiioc4_ide_dma_host_off(ide_drive_t * drive)
return 0; return 0;
} }
static int
sgiioc4_ide_dma_verbose(ide_drive_t * drive)
{
if (drive->using_dma == 1)
printk(", UDMA(16)");
else
printk(", PIO");
return 1;
}
static int static int
sgiioc4_ide_dma_lostirq(ide_drive_t * drive) sgiioc4_ide_dma_lostirq(ide_drive_t * drive)
{ {
...@@ -501,9 +490,6 @@ sgiioc4_build_dma_table(ide_drive_t * drive, struct request *rq, int ddir) ...@@ -501,9 +490,6 @@ sgiioc4_build_dma_table(ide_drive_t * drive, struct request *rq, int ddir)
unsigned int count = 0, i = 1; unsigned int count = 0, i = 1;
struct scatterlist *sg; struct scatterlist *sg;
if (HWGROUP(drive)->rq->flags & REQ_DRIVE_TASKFILE)
hwif->sg_nents = i = ide_raw_build_sglist(drive, rq);
else
hwif->sg_nents = i = ide_build_sglist(drive, rq); hwif->sg_nents = i = ide_build_sglist(drive, rq);
if (!i) if (!i)
...@@ -623,7 +609,6 @@ ide_init_sgiioc4(ide_hwif_t * hwif) ...@@ -623,7 +609,6 @@ ide_init_sgiioc4(ide_hwif_t * hwif)
hwif->ide_dma_test_irq = &sgiioc4_ide_dma_test_irq; hwif->ide_dma_test_irq = &sgiioc4_ide_dma_test_irq;
hwif->ide_dma_host_on = &sgiioc4_ide_dma_host_on; hwif->ide_dma_host_on = &sgiioc4_ide_dma_host_on;
hwif->ide_dma_host_off = &sgiioc4_ide_dma_host_off; hwif->ide_dma_host_off = &sgiioc4_ide_dma_host_off;
hwif->ide_dma_verbose = &sgiioc4_ide_dma_verbose;
hwif->ide_dma_lostirq = &sgiioc4_ide_dma_lostirq; hwif->ide_dma_lostirq = &sgiioc4_ide_dma_lostirq;
hwif->ide_dma_timeout = &__ide_dma_timeout; hwif->ide_dma_timeout = &__ide_dma_timeout;
hwif->INB = &sgiioc4_INB; hwif->INB = &sgiioc4_INB;
......
...@@ -554,12 +554,6 @@ static int siimage_mmio_ide_dma_test_irq (ide_drive_t *drive) ...@@ -554,12 +554,6 @@ static int siimage_mmio_ide_dma_test_irq (ide_drive_t *drive)
return 0; return 0;
} }
static int siimage_mmio_ide_dma_verbose (ide_drive_t *drive)
{
int temp = __ide_dma_verbose(drive);
return temp;
}
/** /**
* siimage_busproc - bus isolation ioctl * siimage_busproc - bus isolation ioctl
* @drive: drive to isolate/restore * @drive: drive to isolate/restore
...@@ -1077,7 +1071,6 @@ static void __devinit init_hwif_siimage(ide_hwif_t *hwif) ...@@ -1077,7 +1071,6 @@ static void __devinit init_hwif_siimage(ide_hwif_t *hwif)
if (hwif->mmio) { if (hwif->mmio) {
hwif->ide_dma_test_irq = &siimage_mmio_ide_dma_test_irq; hwif->ide_dma_test_irq = &siimage_mmio_ide_dma_test_irq;
hwif->ide_dma_verbose = &siimage_mmio_ide_dma_verbose;
} else { } else {
hwif->ide_dma_test_irq = & siimage_io_ide_dma_test_irq; hwif->ide_dma_test_irq = & siimage_io_ide_dma_test_irq;
} }
......
...@@ -1559,56 +1559,6 @@ pmac_ide_probe(void) ...@@ -1559,56 +1559,6 @@ pmac_ide_probe(void)
#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
/*
* We build & map the sglist for a given request.
*/
static int __pmac
pmac_ide_build_sglist(ide_drive_t *drive, struct request *rq)
{
ide_hwif_t *hwif = HWIF(drive);
struct scatterlist *sg = hwif->sg_table;
int nents;
nents = blk_rq_map_sg(drive->queue, rq, sg);
if (rq_data_dir(rq) == READ)
hwif->sg_dma_direction = PCI_DMA_FROMDEVICE;
else
hwif->sg_dma_direction = PCI_DMA_TODEVICE;
return pci_map_sg(hwif->pci_dev, sg, nents, hwif->sg_dma_direction);
}
/*
* Same as above but for a "raw" taskfile request
*/
static int __pmac
pmac_ide_raw_build_sglist(ide_drive_t *drive, struct request *rq)
{
ide_hwif_t *hwif = HWIF(drive);
struct scatterlist *sg = hwif->sg_table;
int nents = 0;
ide_task_t *args = rq->special;
unsigned char *virt_addr = rq->buffer;
int sector_count = rq->nr_sectors;
if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE)
hwif->sg_dma_direction = PCI_DMA_TODEVICE;
else
hwif->sg_dma_direction = PCI_DMA_FROMDEVICE;
if (sector_count > 128) {
sg_init_one(&sg[nents], virt_addr, 128 * SECTOR_SIZE);
nents++;
virt_addr = virt_addr + (128 * SECTOR_SIZE);
sector_count -= 128;
}
sg_init_one(&sg[nents], virt_addr, sector_count * SECTOR_SIZE);
nents++;
return pci_map_sg(hwif->pci_dev, sg, nents, hwif->sg_dma_direction);
}
/* /*
* pmac_ide_build_dmatable builds the DBDMA command list * pmac_ide_build_dmatable builds the DBDMA command list
* for a transfer and sets the DBDMA channel to point to it. * for a transfer and sets the DBDMA channel to point to it.
...@@ -1632,11 +1582,8 @@ pmac_ide_build_dmatable(ide_drive_t *drive, struct request *rq) ...@@ -1632,11 +1582,8 @@ pmac_ide_build_dmatable(ide_drive_t *drive, struct request *rq)
while (readl(&dma->status) & RUN) while (readl(&dma->status) & RUN)
udelay(1); udelay(1);
/* Build sglist */ hwif->sg_nents = i = ide_build_sglist(drive, rq);
if (HWGROUP(drive)->rq->flags & REQ_DRIVE_TASKFILE)
hwif->sg_nents = i = pmac_ide_raw_build_sglist(drive, rq);
else
hwif->sg_nents = i = pmac_ide_build_sglist(drive, rq);
if (!i) if (!i)
return 0; return 0;
...@@ -2078,7 +2025,6 @@ pmac_ide_setup_dma(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) ...@@ -2078,7 +2025,6 @@ pmac_ide_setup_dma(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif)
hwif->ide_dma_test_irq = &pmac_ide_dma_test_irq; hwif->ide_dma_test_irq = &pmac_ide_dma_test_irq;
hwif->ide_dma_host_off = &pmac_ide_dma_host_off; hwif->ide_dma_host_off = &pmac_ide_dma_host_off;
hwif->ide_dma_host_on = &pmac_ide_dma_host_on; hwif->ide_dma_host_on = &pmac_ide_dma_host_on;
hwif->ide_dma_verbose = &__ide_dma_verbose;
hwif->ide_dma_timeout = &__ide_dma_timeout; hwif->ide_dma_timeout = &__ide_dma_timeout;
hwif->ide_dma_lostirq = &pmac_ide_dma_lostirq; hwif->ide_dma_lostirq = &pmac_ide_dma_lostirq;
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#include <linux/hdreg.h> #include <linux/hdreg.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/ide.h> #include <linux/ide.h>
#include <linux/scatterlist.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/bitops.h> #include <asm/bitops.h>
...@@ -253,17 +254,6 @@ static inline void idescsi_transform_pc2 (ide_drive_t *drive, idescsi_pc_t *pc) ...@@ -253,17 +254,6 @@ static inline void idescsi_transform_pc2 (ide_drive_t *drive, idescsi_pc_t *pc)
kfree(atapi_buf); kfree(atapi_buf);
} }
static inline void idescsi_free_bio (struct bio *bio)
{
struct bio *bhp;
while (bio) {
bhp = bio;
bio = bio->bi_next;
bio_put(bhp);
}
}
static void hexdump(u8 *x, int len) static void hexdump(u8 *x, int len)
{ {
int i; int i;
...@@ -421,7 +411,6 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs) ...@@ -421,7 +411,6 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs)
spin_lock_irqsave(host->host_lock, flags); spin_lock_irqsave(host->host_lock, flags);
pc->done(pc->scsi_cmd); pc->done(pc->scsi_cmd);
spin_unlock_irqrestore(host->host_lock, flags); spin_unlock_irqrestore(host->host_lock, flags);
idescsi_free_bio(rq->bio);
kfree(pc); kfree(pc);
kfree(rq); kfree(rq);
scsi->pc = NULL; scsi->pc = NULL;
...@@ -585,6 +574,50 @@ static ide_startstop_t idescsi_transfer_pc(ide_drive_t *drive) ...@@ -585,6 +574,50 @@ static ide_startstop_t idescsi_transfer_pc(ide_drive_t *drive)
return ide_started; return ide_started;
} }
static inline int idescsi_set_direction(idescsi_pc_t *pc)
{
switch (pc->c[0]) {
case READ_6: case READ_10: case READ_12:
clear_bit(PC_WRITING, &pc->flags);
return 0;
case WRITE_6: case WRITE_10: case WRITE_12:
set_bit(PC_WRITING, &pc->flags);
return 0;
default:
return 1;
}
}
static int idescsi_map_sg(ide_drive_t *drive, idescsi_pc_t *pc)
{
ide_hwif_t *hwif = drive->hwif;
struct scatterlist *sg, *scsi_sg;
int segments;
if (!pc->request_transfer || pc->request_transfer % 1024)
return 1;
if (idescsi_set_direction(pc))
return 1;
sg = hwif->sg_table;
scsi_sg = pc->scsi_cmd->request_buffer;
segments = pc->scsi_cmd->use_sg;
if (segments > hwif->sg_max_nents)
return 1;
if (!segments) {
hwif->sg_nents = 1;
sg_init_one(sg, pc->scsi_cmd->request_buffer, pc->request_transfer);
} else {
hwif->sg_nents = segments;
memcpy(sg, scsi_sg, sizeof(*sg) * segments);
}
return 0;
}
/* /*
* Issue a packet command * Issue a packet command
*/ */
...@@ -594,7 +627,6 @@ static ide_startstop_t idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc) ...@@ -594,7 +627,6 @@ static ide_startstop_t idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc)
ide_hwif_t *hwif = drive->hwif; ide_hwif_t *hwif = drive->hwif;
atapi_feature_t feature; atapi_feature_t feature;
atapi_bcount_t bcount; atapi_bcount_t bcount;
struct request *rq = pc->rq;
scsi->pc=pc; /* Set the current packet command */ scsi->pc=pc; /* Set the current packet command */
pc->actually_transferred=0; /* We haven't transferred any data yet */ pc->actually_transferred=0; /* We haven't transferred any data yet */
...@@ -602,8 +634,11 @@ static ide_startstop_t idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc) ...@@ -602,8 +634,11 @@ static ide_startstop_t idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc)
bcount.all = min(pc->request_transfer, 63 * 1024); /* Request to transfer the entire buffer at once */ bcount.all = min(pc->request_transfer, 63 * 1024); /* Request to transfer the entire buffer at once */
feature.all = 0; feature.all = 0;
if (drive->using_dma && rq->bio) if (drive->using_dma && !idescsi_map_sg(drive, pc)) {
hwif->sg_mapped = 1;
feature.b.dma = !hwif->dma_setup(drive); feature.b.dma = !hwif->dma_setup(drive);
hwif->sg_mapped = 0;
}
SELECT_DRIVE(drive); SELECT_DRIVE(drive);
if (IDE_CONTROL_REG) if (IDE_CONTROL_REG)
...@@ -775,81 +810,6 @@ static int idescsi_ioctl (struct scsi_device *dev, int cmd, void __user *arg) ...@@ -775,81 +810,6 @@ static int idescsi_ioctl (struct scsi_device *dev, int cmd, void __user *arg)
return -EINVAL; return -EINVAL;
} }
static inline struct bio *idescsi_kmalloc_bio (int count)
{
struct bio *bh, *bhp, *first_bh;
if ((first_bh = bhp = bh = bio_alloc(GFP_ATOMIC, 1)) == NULL)
goto abort;
bio_init(bh);
bh->bi_vcnt = 1;
while (--count) {
if ((bh = bio_alloc(GFP_ATOMIC, 1)) == NULL)
goto abort;
bio_init(bh);
bh->bi_vcnt = 1;
bhp->bi_next = bh;
bhp = bh;
bh->bi_next = NULL;
}
return first_bh;
abort:
idescsi_free_bio (first_bh);
return NULL;
}
static inline int idescsi_set_direction (idescsi_pc_t *pc)
{
switch (pc->c[0]) {
case READ_6: case READ_10: case READ_12:
clear_bit (PC_WRITING, &pc->flags);
return 0;
case WRITE_6: case WRITE_10: case WRITE_12:
set_bit (PC_WRITING, &pc->flags);
return 0;
default:
return 1;
}
}
static inline struct bio *idescsi_dma_bio(ide_drive_t *drive, idescsi_pc_t *pc)
{
struct bio *bh = NULL, *first_bh = NULL;
int segments = pc->scsi_cmd->use_sg;
struct scatterlist *sg = pc->scsi_cmd->request_buffer;
if (!drive->using_dma || !pc->request_transfer || pc->request_transfer % 1024)
return NULL;
if (idescsi_set_direction(pc))
return NULL;
if (segments) {
if ((first_bh = bh = idescsi_kmalloc_bio (segments)) == NULL)
return NULL;
#if IDESCSI_DEBUG_LOG
printk ("ide-scsi: %s: building DMA table, %d segments, %dkB total\n", drive->name, segments, pc->request_transfer >> 10);
#endif /* IDESCSI_DEBUG_LOG */
while (segments--) {
bh->bi_io_vec[0].bv_page = sg->page;
bh->bi_io_vec[0].bv_len = sg->length;
bh->bi_io_vec[0].bv_offset = sg->offset;
bh->bi_size = sg->length;
bh = bh->bi_next;
sg++;
}
} else {
if ((first_bh = bh = idescsi_kmalloc_bio (1)) == NULL)
return NULL;
#if IDESCSI_DEBUG_LOG
printk ("ide-scsi: %s: building DMA table for a single buffer (%dkB)\n", drive->name, pc->request_transfer >> 10);
#endif /* IDESCSI_DEBUG_LOG */
bh->bi_io_vec[0].bv_page = virt_to_page(pc->scsi_cmd->request_buffer);
bh->bi_io_vec[0].bv_offset = offset_in_page(pc->scsi_cmd->request_buffer);
bh->bi_io_vec[0].bv_len = pc->request_transfer;
bh->bi_size = pc->request_transfer;
}
return first_bh;
}
static inline int should_transform(ide_drive_t *drive, struct scsi_cmnd *cmd) static inline int should_transform(ide_drive_t *drive, struct scsi_cmnd *cmd)
{ {
idescsi_scsi_t *scsi = drive_to_idescsi(drive); idescsi_scsi_t *scsi = drive_to_idescsi(drive);
...@@ -921,7 +881,6 @@ static int idescsi_queue (struct scsi_cmnd *cmd, ...@@ -921,7 +881,6 @@ static int idescsi_queue (struct scsi_cmnd *cmd,
ide_init_drive_cmd (rq); ide_init_drive_cmd (rq);
rq->special = (char *) pc; rq->special = (char *) pc;
rq->bio = idescsi_dma_bio (drive, pc);
rq->flags = REQ_SPECIAL; rq->flags = REQ_SPECIAL;
spin_unlock_irq(host->host_lock); spin_unlock_irq(host->host_lock);
(void) ide_do_drive_cmd (drive, rq, ide_end); (void) ide_do_drive_cmd (drive, rq, ide_end);
...@@ -975,7 +934,6 @@ static int idescsi_eh_abort (struct scsi_cmnd *cmd) ...@@ -975,7 +934,6 @@ static int idescsi_eh_abort (struct scsi_cmnd *cmd)
*/ */
printk (KERN_ERR "ide-scsi: cmd aborted!\n"); printk (KERN_ERR "ide-scsi: cmd aborted!\n");
idescsi_free_bio(scsi->pc->rq->bio);
if (scsi->pc->rq->flags & REQ_SENSE) if (scsi->pc->rq->flags & REQ_SENSE)
kfree(scsi->pc->buffer); kfree(scsi->pc->buffer);
kfree(scsi->pc->rq); kfree(scsi->pc->rq);
...@@ -1024,7 +982,6 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd) ...@@ -1024,7 +982,6 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd)
/* kill current request */ /* kill current request */
blkdev_dequeue_request(req); blkdev_dequeue_request(req);
end_that_request_last(req); end_that_request_last(req);
idescsi_free_bio(req->bio);
if (req->flags & REQ_SENSE) if (req->flags & REQ_SENSE)
kfree(scsi->pc->buffer); kfree(scsi->pc->buffer);
kfree(scsi->pc); kfree(scsi->pc);
......
...@@ -874,7 +874,6 @@ typedef struct hwif_s { ...@@ -874,7 +874,6 @@ typedef struct hwif_s {
int (*ide_dma_test_irq)(ide_drive_t *drive); int (*ide_dma_test_irq)(ide_drive_t *drive);
int (*ide_dma_host_on)(ide_drive_t *drive); int (*ide_dma_host_on)(ide_drive_t *drive);
int (*ide_dma_host_off)(ide_drive_t *drive); int (*ide_dma_host_off)(ide_drive_t *drive);
int (*ide_dma_verbose)(ide_drive_t *drive);
int (*ide_dma_lostirq)(ide_drive_t *drive); int (*ide_dma_lostirq)(ide_drive_t *drive);
int (*ide_dma_timeout)(ide_drive_t *drive); int (*ide_dma_timeout)(ide_drive_t *drive);
...@@ -938,6 +937,7 @@ typedef struct hwif_s { ...@@ -938,6 +937,7 @@ typedef struct hwif_s {
unsigned no_lba48_dma : 1; /* 1 = cannot do LBA48 DMA */ unsigned no_lba48_dma : 1; /* 1 = cannot do LBA48 DMA */
unsigned no_dsc : 1; /* 0 default, 1 dsc_overlap disabled */ unsigned no_dsc : 1; /* 0 default, 1 dsc_overlap disabled */
unsigned auto_poll : 1; /* supports nop auto-poll */ unsigned auto_poll : 1; /* supports nop auto-poll */
unsigned sg_mapped : 1; /* sg_table and sg_nents are ready */
struct device gendev; struct device gendev;
struct semaphore gendev_rel_sem; /* To deal with device release() */ struct semaphore gendev_rel_sem; /* To deal with device release() */
...@@ -1492,10 +1492,10 @@ void ide_init_sg_cmd(ide_drive_t *, struct request *); ...@@ -1492,10 +1492,10 @@ void ide_init_sg_cmd(ide_drive_t *, struct request *);
int __ide_dma_bad_drive(ide_drive_t *); int __ide_dma_bad_drive(ide_drive_t *);
int __ide_dma_good_drive(ide_drive_t *); int __ide_dma_good_drive(ide_drive_t *);
int __ide_dma_off(ide_drive_t *); int __ide_dma_off(ide_drive_t *);
void ide_dma_verbose(ide_drive_t *);
#ifdef CONFIG_BLK_DEV_IDEDMA_PCI #ifdef CONFIG_BLK_DEV_IDEDMA_PCI
extern int ide_build_sglist(ide_drive_t *, struct request *); extern int ide_build_sglist(ide_drive_t *, struct request *);
extern int ide_raw_build_sglist(ide_drive_t *, struct request *);
extern int ide_build_dmatable(ide_drive_t *, struct request *); extern int ide_build_dmatable(ide_drive_t *, struct request *);
extern void ide_destroy_dmatable(ide_drive_t *); extern void ide_destroy_dmatable(ide_drive_t *);
extern ide_startstop_t ide_dma_intr(ide_drive_t *); extern ide_startstop_t ide_dma_intr(ide_drive_t *);
...@@ -1511,13 +1511,13 @@ extern int ide_dma_setup(ide_drive_t *); ...@@ -1511,13 +1511,13 @@ extern int ide_dma_setup(ide_drive_t *);
extern void ide_dma_start(ide_drive_t *); extern void ide_dma_start(ide_drive_t *);
extern int __ide_dma_end(ide_drive_t *); extern int __ide_dma_end(ide_drive_t *);
extern int __ide_dma_test_irq(ide_drive_t *); extern int __ide_dma_test_irq(ide_drive_t *);
extern int __ide_dma_verbose(ide_drive_t *);
extern int __ide_dma_lostirq(ide_drive_t *); extern int __ide_dma_lostirq(ide_drive_t *);
extern int __ide_dma_timeout(ide_drive_t *); extern int __ide_dma_timeout(ide_drive_t *);
#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */ #endif /* CONFIG_BLK_DEV_IDEDMA_PCI */
#else #else
static inline int __ide_dma_off(ide_drive_t *drive) { return 0; } static inline int __ide_dma_off(ide_drive_t *drive) { return 0; }
static inline void ide_dma_verbose(ide_drive_t *drive) { ; }
#endif /* CONFIG_BLK_DEV_IDEDMA */ #endif /* CONFIG_BLK_DEV_IDEDMA */
#ifndef CONFIG_BLK_DEV_IDEDMA_PCI #ifndef CONFIG_BLK_DEV_IDEDMA_PCI
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment