Path: blob/master/drivers/infiniband/hw/ipath/ipath_diag.c
15112 views
/*1* Copyright (c) 2006, 2007, 2008 QLogic Corporation. All rights reserved.2* Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.3*4* This software is available to you under a choice of one of two5* licenses. You may choose to be licensed under the terms of the GNU6* General Public License (GPL) Version 2, available from the file7* COPYING in the main directory of this source tree, or the8* OpenIB.org BSD license below:9*10* Redistribution and use in source and binary forms, with or11* without modification, are permitted provided that the following12* conditions are met:13*14* - Redistributions of source code must retain the above15* copyright notice, this list of conditions and the following16* disclaimer.17*18* - Redistributions in binary form must reproduce the above19* copyright notice, this list of conditions and the following20* disclaimer in the documentation and/or other materials21* provided with the distribution.22*23* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,24* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF25* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND26* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS27* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN28* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN29* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE30* SOFTWARE.31*/3233/*34* This file contains support for diagnostic functions. It is accessed by35* opening the ipath_diag device, normally minor number 129. Diagnostic use36* of the InfiniPath chip may render the chip or board unusable until the37* driver is unloaded, or in some cases, until the system is rebooted.38*39* Accesses to the chip through this interface are not similar to going40* through the /sys/bus/pci resource mmap interface.41*/4243#include <linux/io.h>44#include <linux/pci.h>45#include <linux/vmalloc.h>46#include <linux/fs.h>47#include <asm/uaccess.h>4849#include "ipath_kernel.h"50#include "ipath_common.h"5152int ipath_diag_inuse;53static int diag_set_link;5455static int ipath_diag_open(struct inode *in, struct file *fp);56static int ipath_diag_release(struct inode *in, struct file *fp);57static ssize_t ipath_diag_read(struct file *fp, char __user *data,58size_t count, loff_t *off);59static ssize_t ipath_diag_write(struct file *fp, const char __user *data,60size_t count, loff_t *off);6162static const struct file_operations diag_file_ops = {63.owner = THIS_MODULE,64.write = ipath_diag_write,65.read = ipath_diag_read,66.open = ipath_diag_open,67.release = ipath_diag_release,68.llseek = default_llseek,69};7071static ssize_t ipath_diagpkt_write(struct file *fp,72const char __user *data,73size_t count, loff_t *off);7475static const struct file_operations diagpkt_file_ops = {76.owner = THIS_MODULE,77.write = ipath_diagpkt_write,78.llseek = noop_llseek,79};8081static atomic_t diagpkt_count = ATOMIC_INIT(0);82static struct cdev *diagpkt_cdev;83static struct device *diagpkt_dev;8485int ipath_diag_add(struct ipath_devdata *dd)86{87char name[16];88int ret = 0;8990if (atomic_inc_return(&diagpkt_count) == 1) {91ret = ipath_cdev_init(IPATH_DIAGPKT_MINOR,92"ipath_diagpkt", &diagpkt_file_ops,93&diagpkt_cdev, &diagpkt_dev);9495if (ret) {96ipath_dev_err(dd, "Couldn't create ipath_diagpkt "97"device: %d", ret);98goto done;99}100}101102snprintf(name, sizeof(name), "ipath_diag%d", dd->ipath_unit);103104ret = ipath_cdev_init(IPATH_DIAG_MINOR_BASE + dd->ipath_unit, name,105&diag_file_ops, &dd->diag_cdev,106&dd->diag_dev);107if (ret)108ipath_dev_err(dd, "Couldn't create %s device: %d",109name, ret);110111done:112return ret;113}114115void ipath_diag_remove(struct ipath_devdata *dd)116{117if (atomic_dec_and_test(&diagpkt_count))118ipath_cdev_cleanup(&diagpkt_cdev, &diagpkt_dev);119120ipath_cdev_cleanup(&dd->diag_cdev, &dd->diag_dev);121}122123/**124* ipath_read_umem64 - read a 64-bit quantity from the chip into user space125* @dd: the infinipath device126* @uaddr: the location to store the data in user memory127* @caddr: the source chip address (full pointer, not offset)128* @count: number of bytes to copy (multiple of 32 bits)129*130* This function also localizes all chip memory accesses.131* The copy should be written such that we read full cacheline packets132* from the chip. This is usually used for a single qword133*134* NOTE: This assumes the chip address is 64-bit aligned.135*/136static int ipath_read_umem64(struct ipath_devdata *dd, void __user *uaddr,137const void __iomem *caddr, size_t count)138{139const u64 __iomem *reg_addr = caddr;140const u64 __iomem *reg_end = reg_addr + (count / sizeof(u64));141int ret;142143/* not very efficient, but it works for now */144if (reg_addr < dd->ipath_kregbase || reg_end > dd->ipath_kregend) {145ret = -EINVAL;146goto bail;147}148while (reg_addr < reg_end) {149u64 data = readq(reg_addr);150if (copy_to_user(uaddr, &data, sizeof(u64))) {151ret = -EFAULT;152goto bail;153}154reg_addr++;155uaddr += sizeof(u64);156}157ret = 0;158bail:159return ret;160}161162/**163* ipath_write_umem64 - write a 64-bit quantity to the chip from user space164* @dd: the infinipath device165* @caddr: the destination chip address (full pointer, not offset)166* @uaddr: the source of the data in user memory167* @count: the number of bytes to copy (multiple of 32 bits)168*169* This is usually used for a single qword170* NOTE: This assumes the chip address is 64-bit aligned.171*/172173static int ipath_write_umem64(struct ipath_devdata *dd, void __iomem *caddr,174const void __user *uaddr, size_t count)175{176u64 __iomem *reg_addr = caddr;177const u64 __iomem *reg_end = reg_addr + (count / sizeof(u64));178int ret;179180/* not very efficient, but it works for now */181if (reg_addr < dd->ipath_kregbase || reg_end > dd->ipath_kregend) {182ret = -EINVAL;183goto bail;184}185while (reg_addr < reg_end) {186u64 data;187if (copy_from_user(&data, uaddr, sizeof(data))) {188ret = -EFAULT;189goto bail;190}191writeq(data, reg_addr);192193reg_addr++;194uaddr += sizeof(u64);195}196ret = 0;197bail:198return ret;199}200201/**202* ipath_read_umem32 - read a 32-bit quantity from the chip into user space203* @dd: the infinipath device204* @uaddr: the location to store the data in user memory205* @caddr: the source chip address (full pointer, not offset)206* @count: number of bytes to copy207*208* read 32 bit values, not 64 bit; for memories that only209* support 32 bit reads; usually a single dword.210*/211static int ipath_read_umem32(struct ipath_devdata *dd, void __user *uaddr,212const void __iomem *caddr, size_t count)213{214const u32 __iomem *reg_addr = caddr;215const u32 __iomem *reg_end = reg_addr + (count / sizeof(u32));216int ret;217218if (reg_addr < (u32 __iomem *) dd->ipath_kregbase ||219reg_end > (u32 __iomem *) dd->ipath_kregend) {220ret = -EINVAL;221goto bail;222}223/* not very efficient, but it works for now */224while (reg_addr < reg_end) {225u32 data = readl(reg_addr);226if (copy_to_user(uaddr, &data, sizeof(data))) {227ret = -EFAULT;228goto bail;229}230231reg_addr++;232uaddr += sizeof(u32);233234}235ret = 0;236bail:237return ret;238}239240/**241* ipath_write_umem32 - write a 32-bit quantity to the chip from user space242* @dd: the infinipath device243* @caddr: the destination chip address (full pointer, not offset)244* @uaddr: the source of the data in user memory245* @count: number of bytes to copy246*247* write 32 bit values, not 64 bit; for memories that only248* support 32 bit write; usually a single dword.249*/250251static int ipath_write_umem32(struct ipath_devdata *dd, void __iomem *caddr,252const void __user *uaddr, size_t count)253{254u32 __iomem *reg_addr = caddr;255const u32 __iomem *reg_end = reg_addr + (count / sizeof(u32));256int ret;257258if (reg_addr < (u32 __iomem *) dd->ipath_kregbase ||259reg_end > (u32 __iomem *) dd->ipath_kregend) {260ret = -EINVAL;261goto bail;262}263while (reg_addr < reg_end) {264u32 data;265if (copy_from_user(&data, uaddr, sizeof(data))) {266ret = -EFAULT;267goto bail;268}269writel(data, reg_addr);270271reg_addr++;272uaddr += sizeof(u32);273}274ret = 0;275bail:276return ret;277}278279static int ipath_diag_open(struct inode *in, struct file *fp)280{281int unit = iminor(in) - IPATH_DIAG_MINOR_BASE;282struct ipath_devdata *dd;283int ret;284285mutex_lock(&ipath_mutex);286287if (ipath_diag_inuse) {288ret = -EBUSY;289goto bail;290}291292dd = ipath_lookup(unit);293294if (dd == NULL || !(dd->ipath_flags & IPATH_PRESENT) ||295!dd->ipath_kregbase) {296ret = -ENODEV;297goto bail;298}299300fp->private_data = dd;301ipath_diag_inuse = -2;302diag_set_link = 0;303ret = 0;304305/* Only expose a way to reset the device if we306make it into diag mode. */307ipath_expose_reset(&dd->pcidev->dev);308309bail:310mutex_unlock(&ipath_mutex);311312return ret;313}314315/**316* ipath_diagpkt_write - write an IB packet317* @fp: the diag data device file pointer318* @data: ipath_diag_pkt structure saying where to get the packet319* @count: size of data to write320* @off: unused by this code321*/322static ssize_t ipath_diagpkt_write(struct file *fp,323const char __user *data,324size_t count, loff_t *off)325{326u32 __iomem *piobuf;327u32 plen, clen, pbufn;328struct ipath_diag_pkt odp;329struct ipath_diag_xpkt dp;330u32 *tmpbuf = NULL;331struct ipath_devdata *dd;332ssize_t ret = 0;333u64 val;334u32 l_state, lt_state; /* LinkState, LinkTrainingState */335336if (count < sizeof(odp)) {337ret = -EINVAL;338goto bail;339}340341if (count == sizeof(dp)) {342if (copy_from_user(&dp, data, sizeof(dp))) {343ret = -EFAULT;344goto bail;345}346} else if (copy_from_user(&odp, data, sizeof(odp))) {347ret = -EFAULT;348goto bail;349}350351/*352* Due to padding/alignment issues (lessened with new struct)353* the old and new structs are the same length. We need to354* disambiguate them, which we can do because odp.len has never355* been less than the total of LRH+BTH+DETH so far, while356* dp.unit (same offset) unit is unlikely to get that high.357* Similarly, dp.data, the pointer to user at the same offset358* as odp.unit, is almost certainly at least one (512byte)page359* "above" NULL. The if-block below can be omitted if compatibility360* between a new driver and older diagnostic code is unimportant.361* compatibility the other direction (new diags, old driver) is362* handled in the diagnostic code, with a warning.363*/364if (dp.unit >= 20 && dp.data < 512) {365/* very probable version mismatch. Fix it up */366memcpy(&odp, &dp, sizeof(odp));367/* We got a legacy dp, copy elements to dp */368dp.unit = odp.unit;369dp.data = odp.data;370dp.len = odp.len;371dp.pbc_wd = 0; /* Indicate we need to compute PBC wd */372}373374/* send count must be an exact number of dwords */375if (dp.len & 3) {376ret = -EINVAL;377goto bail;378}379380clen = dp.len >> 2;381382dd = ipath_lookup(dp.unit);383if (!dd || !(dd->ipath_flags & IPATH_PRESENT) ||384!dd->ipath_kregbase) {385ipath_cdbg(VERBOSE, "illegal unit %u for diag data send\n",386dp.unit);387ret = -ENODEV;388goto bail;389}390391if (ipath_diag_inuse && !diag_set_link &&392!(dd->ipath_flags & IPATH_LINKACTIVE)) {393diag_set_link = 1;394ipath_cdbg(VERBOSE, "Trying to set to set link active for "395"diag pkt\n");396ipath_set_linkstate(dd, IPATH_IB_LINKARM);397ipath_set_linkstate(dd, IPATH_IB_LINKACTIVE);398}399400if (!(dd->ipath_flags & IPATH_INITTED)) {401/* no hardware, freeze, etc. */402ipath_cdbg(VERBOSE, "unit %u not usable\n", dd->ipath_unit);403ret = -ENODEV;404goto bail;405}406/*407* Want to skip check for l_state if using custom PBC,408* because we might be trying to force an SM packet out.409* first-cut, skip _all_ state checking in that case.410*/411val = ipath_ib_state(dd, dd->ipath_lastibcstat);412lt_state = ipath_ib_linktrstate(dd, dd->ipath_lastibcstat);413l_state = ipath_ib_linkstate(dd, dd->ipath_lastibcstat);414if (!dp.pbc_wd && (lt_state != INFINIPATH_IBCS_LT_STATE_LINKUP ||415(val != dd->ib_init && val != dd->ib_arm &&416val != dd->ib_active))) {417ipath_cdbg(VERBOSE, "unit %u not ready (state %llx)\n",418dd->ipath_unit, (unsigned long long) val);419ret = -EINVAL;420goto bail;421}422423/* need total length before first word written */424/* +1 word is for the qword padding */425plen = sizeof(u32) + dp.len;426427if ((plen + 4) > dd->ipath_ibmaxlen) {428ipath_dbg("Pkt len 0x%x > ibmaxlen %x\n",429plen - 4, dd->ipath_ibmaxlen);430ret = -EINVAL;431goto bail; /* before writing pbc */432}433tmpbuf = vmalloc(plen);434if (!tmpbuf) {435dev_info(&dd->pcidev->dev, "Unable to allocate tmp buffer, "436"failing\n");437ret = -ENOMEM;438goto bail;439}440441if (copy_from_user(tmpbuf,442(const void __user *) (unsigned long) dp.data,443dp.len)) {444ret = -EFAULT;445goto bail;446}447448plen >>= 2; /* in dwords */449450piobuf = ipath_getpiobuf(dd, plen, &pbufn);451if (!piobuf) {452ipath_cdbg(VERBOSE, "No PIO buffers avail unit for %u\n",453dd->ipath_unit);454ret = -EBUSY;455goto bail;456}457/* disarm it just to be extra sure */458ipath_disarm_piobufs(dd, pbufn, 1);459460if (ipath_debug & __IPATH_PKTDBG)461ipath_cdbg(VERBOSE, "unit %u 0x%x+1w pio%d\n",462dd->ipath_unit, plen - 1, pbufn);463464if (dp.pbc_wd == 0)465dp.pbc_wd = plen;466writeq(dp.pbc_wd, piobuf);467/*468* Copy all by the trigger word, then flush, so it's written469* to chip before trigger word, then write trigger word, then470* flush again, so packet is sent.471*/472if (dd->ipath_flags & IPATH_PIO_FLUSH_WC) {473ipath_flush_wc();474__iowrite32_copy(piobuf + 2, tmpbuf, clen - 1);475ipath_flush_wc();476__raw_writel(tmpbuf[clen - 1], piobuf + clen + 1);477} else478__iowrite32_copy(piobuf + 2, tmpbuf, clen);479480ipath_flush_wc();481482ret = sizeof(dp);483484bail:485vfree(tmpbuf);486return ret;487}488489static int ipath_diag_release(struct inode *in, struct file *fp)490{491mutex_lock(&ipath_mutex);492ipath_diag_inuse = 0;493fp->private_data = NULL;494mutex_unlock(&ipath_mutex);495return 0;496}497498static ssize_t ipath_diag_read(struct file *fp, char __user *data,499size_t count, loff_t *off)500{501struct ipath_devdata *dd = fp->private_data;502void __iomem *kreg_base;503ssize_t ret;504505kreg_base = dd->ipath_kregbase;506507if (count == 0)508ret = 0;509else if ((count % 4) || (*off % 4))510/* address or length is not 32-bit aligned, hence invalid */511ret = -EINVAL;512else if (ipath_diag_inuse < 1 && (*off || count != 8))513ret = -EINVAL; /* prevent cat /dev/ipath_diag* */514else if ((count % 8) || (*off % 8))515/* address or length not 64-bit aligned; do 32-bit reads */516ret = ipath_read_umem32(dd, data, kreg_base + *off, count);517else518ret = ipath_read_umem64(dd, data, kreg_base + *off, count);519520if (ret >= 0) {521*off += count;522ret = count;523if (ipath_diag_inuse == -2)524ipath_diag_inuse++;525}526527return ret;528}529530static ssize_t ipath_diag_write(struct file *fp, const char __user *data,531size_t count, loff_t *off)532{533struct ipath_devdata *dd = fp->private_data;534void __iomem *kreg_base;535ssize_t ret;536537kreg_base = dd->ipath_kregbase;538539if (count == 0)540ret = 0;541else if ((count % 4) || (*off % 4))542/* address or length is not 32-bit aligned, hence invalid */543ret = -EINVAL;544else if ((ipath_diag_inuse == -1 && (*off || count != 8)) ||545ipath_diag_inuse == -2) /* read qw off 0, write qw off 0 */546ret = -EINVAL; /* before any other write allowed */547else if ((count % 8) || (*off % 8))548/* address or length not 64-bit aligned; do 32-bit writes */549ret = ipath_write_umem32(dd, kreg_base + *off, data, count);550else551ret = ipath_write_umem64(dd, kreg_base + *off, data, count);552553if (ret >= 0) {554*off += count;555ret = count;556if (ipath_diag_inuse == -1)557ipath_diag_inuse = 1; /* all read/write OK now */558}559560return ret;561}562563564