X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Finfiniband%2Fhw%2Fipath%2Fipath_diag.c;h=28c087b824c2846c7d6e971e59359295618587b0;hb=12cbbd95e12e911b43afae7f6f4850f616223642;hp=0fcd507e7c7dd78faabc76553e1ea46b7a7ec7cf;hpb=759d57686dab8169ca68bbf938ce8e965d1e107a;p=linux-2.6-omap-h63xx.git diff --git a/drivers/infiniband/hw/ipath/ipath_diag.c b/drivers/infiniband/hw/ipath/ipath_diag.c index 0fcd507e7c7..28c087b824c 100644 --- a/drivers/infiniband/hw/ipath/ipath_diag.c +++ b/drivers/infiniband/hw/ipath/ipath_diag.c @@ -41,13 +41,13 @@ * through the /sys/bus/pci resource mmap interface. */ +#include #include +#include #include -#include "ipath_common.h" #include "ipath_kernel.h" -#include "ips_common.h" -#include "ipath_layer.h" +#include "ipath_common.h" int ipath_diag_inuse; static int diag_set_link; @@ -67,18 +67,55 @@ static struct file_operations diag_file_ops = { .release = ipath_diag_release }; -static struct cdev *diag_cdev; -static struct class_device *diag_class_dev; +static ssize_t ipath_diagpkt_write(struct file *fp, + const char __user *data, + size_t count, loff_t *off); + +static struct file_operations diagpkt_file_ops = { + .owner = THIS_MODULE, + .write = ipath_diagpkt_write, +}; + +static atomic_t diagpkt_count = ATOMIC_INIT(0); +static struct cdev *diagpkt_cdev; +static struct class_device *diagpkt_class_dev; -int ipath_diag_init(void) +int ipath_diag_add(struct ipath_devdata *dd) { - return ipath_cdev_init(IPATH_DIAG_MINOR, "ipath_diag", - &diag_file_ops, &diag_cdev, &diag_class_dev); + char name[16]; + int ret = 0; + + if (atomic_inc_return(&diagpkt_count) == 1) { + ret = ipath_cdev_init(IPATH_DIAGPKT_MINOR, + "ipath_diagpkt", &diagpkt_file_ops, + &diagpkt_cdev, &diagpkt_class_dev); + + if (ret) { + ipath_dev_err(dd, "Couldn't create ipath_diagpkt " + "device: %d", ret); + goto done; + } + } + + snprintf(name, sizeof(name), "ipath_diag%d", dd->ipath_unit); + + ret = ipath_cdev_init(IPATH_DIAG_MINOR_BASE + dd->ipath_unit, name, + &diag_file_ops, &dd->diag_cdev, + &dd->diag_class_dev); + if (ret) + ipath_dev_err(dd, "Couldn't create %s device: %d", + name, ret); + +done: + return ret; } -void ipath_diag_cleanup(void) +void ipath_diag_remove(struct ipath_devdata *dd) { - ipath_cdev_cleanup(&diag_cdev, &diag_class_dev); + if (atomic_dec_and_test(&diagpkt_count)) + ipath_cdev_cleanup(&diagpkt_cdev, &diagpkt_class_dev); + + ipath_cdev_cleanup(&dd->diag_cdev, &dd->diag_class_dev); } /** @@ -102,8 +139,7 @@ static int ipath_read_umem64(struct ipath_devdata *dd, void __user *uaddr, int ret; /* not very efficient, but it works for now */ - if (reg_addr < dd->ipath_kregbase || - reg_end > dd->ipath_kregend) { + if (reg_addr < dd->ipath_kregbase || reg_end > dd->ipath_kregend) { ret = -EINVAL; goto bail; } @@ -114,7 +150,7 @@ static int ipath_read_umem64(struct ipath_devdata *dd, void __user *uaddr, goto bail; } reg_addr++; - uaddr++; + uaddr += sizeof(u64); } ret = 0; bail: @@ -140,8 +176,7 @@ static int ipath_write_umem64(struct ipath_devdata *dd, void __iomem *caddr, int ret; /* not very efficient, but it works for now */ - if (reg_addr < dd->ipath_kregbase || - reg_end > dd->ipath_kregend) { + if (reg_addr < dd->ipath_kregbase || reg_end > dd->ipath_kregend) { ret = -EINVAL; goto bail; } @@ -154,7 +189,7 @@ static int ipath_write_umem64(struct ipath_devdata *dd, void __iomem *caddr, writeq(data, reg_addr); reg_addr++; - uaddr++; + uaddr += sizeof(u64); } ret = 0; bail: @@ -192,7 +227,8 @@ static int ipath_read_umem32(struct ipath_devdata *dd, void __user *uaddr, } reg_addr++; - uaddr++; + uaddr += sizeof(u32); + } ret = 0; bail: @@ -231,7 +267,7 @@ static int ipath_write_umem32(struct ipath_devdata *dd, void __iomem *caddr, writel(data, reg_addr); reg_addr++; - uaddr++; + uaddr += sizeof(u32); } ret = 0; bail: @@ -240,59 +276,173 @@ bail: static int ipath_diag_open(struct inode *in, struct file *fp) { + int unit = iminor(in) - IPATH_DIAG_MINOR_BASE; struct ipath_devdata *dd; - int unit = 0; /* XXX this is bogus */ - unsigned long flags; int ret; - dd = ipath_lookup(unit); - mutex_lock(&ipath_mutex); - spin_lock_irqsave(&ipath_devs_lock, flags); if (ipath_diag_inuse) { ret = -EBUSY; goto bail; } - list_for_each_entry(dd, &ipath_dev_list, ipath_list) { - /* - * we need at least one infinipath device to be present - * (don't use INITTED, because we want to be able to open - * even if device is in freeze mode, which cleared INITTED). - * There is a small amount of risk to this, which is why we - * also verify kregbase is set. - */ - - if (!(dd->ipath_flags & IPATH_PRESENT) || - !dd->ipath_kregbase) - continue; - - ipath_diag_inuse = 1; - diag_set_link = 0; - ret = 0; + dd = ipath_lookup(unit); + + if (dd == NULL || !(dd->ipath_flags & IPATH_PRESENT) || + !dd->ipath_kregbase) { + ret = -ENODEV; goto bail; } - ret = -ENODEV; - -bail: - spin_unlock_irqrestore(&ipath_devs_lock, flags); + fp->private_data = dd; + ipath_diag_inuse = 1; + diag_set_link = 0; + ret = 0; /* Only expose a way to reset the device if we make it into diag mode. */ - if (ret == 0) - ipath_expose_reset(&dd->pcidev->dev); + ipath_expose_reset(&dd->pcidev->dev); +bail: mutex_unlock(&ipath_mutex); return ret; } -static int ipath_diag_release(struct inode *i, struct file *f) +/** + * ipath_diagpkt_write - write an IB packet + * @fp: the diag data device file pointer + * @data: ipath_diag_pkt structure saying where to get the packet + * @count: size of data to write + * @off: unused by this code + */ +static ssize_t ipath_diagpkt_write(struct file *fp, + const char __user *data, + size_t count, loff_t *off) +{ + u32 __iomem *piobuf; + u32 plen, clen, pbufn; + struct ipath_diag_pkt dp; + u32 *tmpbuf = NULL; + struct ipath_devdata *dd; + ssize_t ret = 0; + u64 val; + + if (count < sizeof(dp)) { + ret = -EINVAL; + goto bail; + } + + if (copy_from_user(&dp, data, sizeof(dp))) { + ret = -EFAULT; + goto bail; + } + + /* send count must be an exact number of dwords */ + if (dp.len & 3) { + ret = -EINVAL; + goto bail; + } + + clen = dp.len >> 2; + + dd = ipath_lookup(dp.unit); + if (!dd || !(dd->ipath_flags & IPATH_PRESENT) || + !dd->ipath_kregbase) { + ipath_cdbg(VERBOSE, "illegal unit %u for diag data send\n", + dp.unit); + ret = -ENODEV; + goto bail; + } + + if (ipath_diag_inuse && !diag_set_link && + !(dd->ipath_flags & IPATH_LINKACTIVE)) { + diag_set_link = 1; + ipath_cdbg(VERBOSE, "Trying to set to set link active for " + "diag pkt\n"); + ipath_set_linkstate(dd, IPATH_IB_LINKARM); + ipath_set_linkstate(dd, IPATH_IB_LINKACTIVE); + } + + if (!(dd->ipath_flags & IPATH_INITTED)) { + /* no hardware, freeze, etc. */ + ipath_cdbg(VERBOSE, "unit %u not usable\n", dd->ipath_unit); + ret = -ENODEV; + goto bail; + } + val = dd->ipath_lastibcstat & IPATH_IBSTATE_MASK; + if (val != IPATH_IBSTATE_INIT && val != IPATH_IBSTATE_ARM && + val != IPATH_IBSTATE_ACTIVE) { + ipath_cdbg(VERBOSE, "unit %u not ready (state %llx)\n", + dd->ipath_unit, (unsigned long long) val); + ret = -EINVAL; + goto bail; + } + + /* need total length before first word written */ + /* +1 word is for the qword padding */ + plen = sizeof(u32) + dp.len; + + if ((plen + 4) > dd->ipath_ibmaxlen) { + ipath_dbg("Pkt len 0x%x > ibmaxlen %x\n", + plen - 4, dd->ipath_ibmaxlen); + ret = -EINVAL; + goto bail; /* before writing pbc */ + } + tmpbuf = vmalloc(plen); + if (!tmpbuf) { + dev_info(&dd->pcidev->dev, "Unable to allocate tmp buffer, " + "failing\n"); + ret = -ENOMEM; + goto bail; + } + + if (copy_from_user(tmpbuf, + (const void __user *) (unsigned long) dp.data, + dp.len)) { + ret = -EFAULT; + goto bail; + } + + piobuf = ipath_getpiobuf(dd, &pbufn); + if (!piobuf) { + ipath_cdbg(VERBOSE, "No PIO buffers avail unit for %u\n", + dd->ipath_unit); + ret = -EBUSY; + goto bail; + } + + plen >>= 2; /* in dwords */ + + if (ipath_debug & __IPATH_PKTDBG) + ipath_cdbg(VERBOSE, "unit %u 0x%x+1w pio%d\n", + dd->ipath_unit, plen - 1, pbufn); + + /* we have to flush after the PBC for correctness on some cpus + * or WC buffer can be written out of order */ + writeq(plen, piobuf); + ipath_flush_wc(); + /* copy all by the trigger word, then flush, so it's written + * to chip before trigger word, then write trigger word, then + * flush again, so packet is sent. */ + __iowrite32_copy(piobuf + 2, tmpbuf, clen - 1); + ipath_flush_wc(); + __raw_writel(tmpbuf[clen - 1], piobuf + clen + 1); + ipath_flush_wc(); + + ret = sizeof(dp); + +bail: + vfree(tmpbuf); + return ret; +} + +static int ipath_diag_release(struct inode *in, struct file *fp) { mutex_lock(&ipath_mutex); ipath_diag_inuse = 0; + fp->private_data = NULL; mutex_unlock(&ipath_mutex); return 0; } @@ -300,17 +450,10 @@ static int ipath_diag_release(struct inode *i, struct file *f) static ssize_t ipath_diag_read(struct file *fp, char __user *data, size_t count, loff_t *off) { - int unit = 0; /* XXX provide for reads on other units some day */ - struct ipath_devdata *dd; + struct ipath_devdata *dd = fp->private_data; void __iomem *kreg_base; ssize_t ret; - dd = ipath_lookup(unit); - if (!dd) { - ret = -ENODEV; - goto bail; - } - kreg_base = dd->ipath_kregbase; if (count == 0) @@ -329,23 +472,16 @@ static ssize_t ipath_diag_read(struct file *fp, char __user *data, ret = count; } -bail: return ret; } static ssize_t ipath_diag_write(struct file *fp, const char __user *data, size_t count, loff_t *off) { - int unit = 0; /* XXX this is bogus */ - struct ipath_devdata *dd; + struct ipath_devdata *dd = fp->private_data; void __iomem *kreg_base; ssize_t ret; - dd = ipath_lookup(unit); - if (!dd) { - ret = -ENODEV; - goto bail; - } kreg_base = dd->ipath_kregbase; if (count == 0) @@ -364,6 +500,5 @@ static ssize_t ipath_diag_write(struct file *fp, const char __user *data, ret = count; } -bail: return ret; }