162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2012 Intel Corporation. All rights reserved.
362306a36Sopenharmony_ci * Copyright (c) 2006 - 2012 QLogic Corporation. All rights reserved.
462306a36Sopenharmony_ci * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This software is available to you under a choice of one of two
762306a36Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
862306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
962306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the
1062306a36Sopenharmony_ci * OpenIB.org BSD license below:
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
1362306a36Sopenharmony_ci *     without modification, are permitted provided that the following
1462306a36Sopenharmony_ci *     conditions are met:
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci *      - Redistributions of source code must retain the above
1762306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
1862306a36Sopenharmony_ci *        disclaimer.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
2162306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2262306a36Sopenharmony_ci *        disclaimer in the documentation and/or other materials
2362306a36Sopenharmony_ci *        provided with the distribution.
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2662306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2762306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2862306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2962306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
3062306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
3162306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3262306a36Sopenharmony_ci * SOFTWARE.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/*
3662306a36Sopenharmony_ci * This file contains support for diagnostic functions.  It is accessed by
3762306a36Sopenharmony_ci * opening the qib_diag device, normally minor number 129.  Diagnostic use
3862306a36Sopenharmony_ci * of the QLogic_IB chip may render the chip or board unusable until the
3962306a36Sopenharmony_ci * driver is unloaded, or in some cases, until the system is rebooted.
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci * Accesses to the chip through this interface are not similar to going
4262306a36Sopenharmony_ci * through the /sys/bus/pci resource mmap interface.
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include <linux/io.h>
4662306a36Sopenharmony_ci#include <linux/pci.h>
4762306a36Sopenharmony_ci#include <linux/poll.h>
4862306a36Sopenharmony_ci#include <linux/vmalloc.h>
4962306a36Sopenharmony_ci#include <linux/export.h>
5062306a36Sopenharmony_ci#include <linux/fs.h>
5162306a36Sopenharmony_ci#include <linux/uaccess.h>
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#include "qib.h"
5462306a36Sopenharmony_ci#include "qib_common.h"
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#undef pr_fmt
5762306a36Sopenharmony_ci#define pr_fmt(fmt) QIB_DRV_NAME ": " fmt
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/*
6062306a36Sopenharmony_ci * Each client that opens the diag device must read then write
6162306a36Sopenharmony_ci * offset 0, to prevent lossage from random cat or od. diag_state
6262306a36Sopenharmony_ci * sequences this "handshake".
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_cienum diag_state { UNUSED = 0, OPENED, INIT, READY };
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* State for an individual client. PID so children cannot abuse handshake */
6762306a36Sopenharmony_cistatic struct qib_diag_client {
6862306a36Sopenharmony_ci	struct qib_diag_client *next;
6962306a36Sopenharmony_ci	struct qib_devdata *dd;
7062306a36Sopenharmony_ci	pid_t pid;
7162306a36Sopenharmony_ci	enum diag_state state;
7262306a36Sopenharmony_ci} *client_pool;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/*
7562306a36Sopenharmony_ci * Get a client struct. Recycled if possible, else kmalloc.
7662306a36Sopenharmony_ci * Must be called with qib_mutex held
7762306a36Sopenharmony_ci */
7862306a36Sopenharmony_cistatic struct qib_diag_client *get_client(struct qib_devdata *dd)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct qib_diag_client *dc;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	dc = client_pool;
8362306a36Sopenharmony_ci	if (dc)
8462306a36Sopenharmony_ci		/* got from pool remove it and use */
8562306a36Sopenharmony_ci		client_pool = dc->next;
8662306a36Sopenharmony_ci	else
8762306a36Sopenharmony_ci		/* None in pool, alloc and init */
8862306a36Sopenharmony_ci		dc = kmalloc(sizeof(*dc), GFP_KERNEL);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (dc) {
9162306a36Sopenharmony_ci		dc->next = NULL;
9262306a36Sopenharmony_ci		dc->dd = dd;
9362306a36Sopenharmony_ci		dc->pid = current->pid;
9462306a36Sopenharmony_ci		dc->state = OPENED;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci	return dc;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * Return to pool. Must be called with qib_mutex held
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_cistatic void return_client(struct qib_diag_client *dc)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct qib_devdata *dd = dc->dd;
10562306a36Sopenharmony_ci	struct qib_diag_client *tdc, *rdc;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	rdc = NULL;
10862306a36Sopenharmony_ci	if (dc == dd->diag_client) {
10962306a36Sopenharmony_ci		dd->diag_client = dc->next;
11062306a36Sopenharmony_ci		rdc = dc;
11162306a36Sopenharmony_ci	} else {
11262306a36Sopenharmony_ci		tdc = dc->dd->diag_client;
11362306a36Sopenharmony_ci		while (tdc) {
11462306a36Sopenharmony_ci			if (dc == tdc->next) {
11562306a36Sopenharmony_ci				tdc->next = dc->next;
11662306a36Sopenharmony_ci				rdc = dc;
11762306a36Sopenharmony_ci				break;
11862306a36Sopenharmony_ci			}
11962306a36Sopenharmony_ci			tdc = tdc->next;
12062306a36Sopenharmony_ci		}
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci	if (rdc) {
12362306a36Sopenharmony_ci		rdc->state = UNUSED;
12462306a36Sopenharmony_ci		rdc->dd = NULL;
12562306a36Sopenharmony_ci		rdc->pid = 0;
12662306a36Sopenharmony_ci		rdc->next = client_pool;
12762306a36Sopenharmony_ci		client_pool = rdc;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic int qib_diag_open(struct inode *in, struct file *fp);
13262306a36Sopenharmony_cistatic int qib_diag_release(struct inode *in, struct file *fp);
13362306a36Sopenharmony_cistatic ssize_t qib_diag_read(struct file *fp, char __user *data,
13462306a36Sopenharmony_ci			     size_t count, loff_t *off);
13562306a36Sopenharmony_cistatic ssize_t qib_diag_write(struct file *fp, const char __user *data,
13662306a36Sopenharmony_ci			      size_t count, loff_t *off);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic const struct file_operations diag_file_ops = {
13962306a36Sopenharmony_ci	.owner = THIS_MODULE,
14062306a36Sopenharmony_ci	.write = qib_diag_write,
14162306a36Sopenharmony_ci	.read = qib_diag_read,
14262306a36Sopenharmony_ci	.open = qib_diag_open,
14362306a36Sopenharmony_ci	.release = qib_diag_release,
14462306a36Sopenharmony_ci	.llseek = default_llseek,
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic atomic_t diagpkt_count = ATOMIC_INIT(0);
14862306a36Sopenharmony_cistatic struct cdev *diagpkt_cdev;
14962306a36Sopenharmony_cistatic struct device *diagpkt_device;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic ssize_t qib_diagpkt_write(struct file *fp, const char __user *data,
15262306a36Sopenharmony_ci				 size_t count, loff_t *off);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic const struct file_operations diagpkt_file_ops = {
15562306a36Sopenharmony_ci	.owner = THIS_MODULE,
15662306a36Sopenharmony_ci	.write = qib_diagpkt_write,
15762306a36Sopenharmony_ci	.llseek = noop_llseek,
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ciint qib_diag_add(struct qib_devdata *dd)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	char name[16];
16362306a36Sopenharmony_ci	int ret = 0;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (atomic_inc_return(&diagpkt_count) == 1) {
16662306a36Sopenharmony_ci		ret = qib_cdev_init(QIB_DIAGPKT_MINOR, "ipath_diagpkt",
16762306a36Sopenharmony_ci				    &diagpkt_file_ops, &diagpkt_cdev,
16862306a36Sopenharmony_ci				    &diagpkt_device);
16962306a36Sopenharmony_ci		if (ret)
17062306a36Sopenharmony_ci			goto done;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	snprintf(name, sizeof(name), "ipath_diag%d", dd->unit);
17462306a36Sopenharmony_ci	ret = qib_cdev_init(QIB_DIAG_MINOR_BASE + dd->unit, name,
17562306a36Sopenharmony_ci			    &diag_file_ops, &dd->diag_cdev,
17662306a36Sopenharmony_ci			    &dd->diag_device);
17762306a36Sopenharmony_cidone:
17862306a36Sopenharmony_ci	return ret;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void qib_unregister_observers(struct qib_devdata *dd);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_civoid qib_diag_remove(struct qib_devdata *dd)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct qib_diag_client *dc;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (atomic_dec_and_test(&diagpkt_count))
18862306a36Sopenharmony_ci		qib_cdev_cleanup(&diagpkt_cdev, &diagpkt_device);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	qib_cdev_cleanup(&dd->diag_cdev, &dd->diag_device);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/*
19362306a36Sopenharmony_ci	 * Return all diag_clients of this device. There should be none,
19462306a36Sopenharmony_ci	 * as we are "guaranteed" that no clients are still open
19562306a36Sopenharmony_ci	 */
19662306a36Sopenharmony_ci	while (dd->diag_client)
19762306a36Sopenharmony_ci		return_client(dd->diag_client);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* Now clean up all unused client structs */
20062306a36Sopenharmony_ci	while (client_pool) {
20162306a36Sopenharmony_ci		dc = client_pool;
20262306a36Sopenharmony_ci		client_pool = dc->next;
20362306a36Sopenharmony_ci		kfree(dc);
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci	/* Clean up observer list */
20662306a36Sopenharmony_ci	qib_unregister_observers(dd);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci/* qib_remap_ioaddr32 - remap an offset into chip address space to __iomem *
21062306a36Sopenharmony_ci *
21162306a36Sopenharmony_ci * @dd: the qlogic_ib device
21262306a36Sopenharmony_ci * @offs: the offset in chip-space
21362306a36Sopenharmony_ci * @cntp: Pointer to max (byte) count for transfer starting at offset
21462306a36Sopenharmony_ci * This returns a u32 __iomem * so it can be used for both 64 and 32-bit
21562306a36Sopenharmony_ci * mapping. It is needed because with the use of PAT for control of
21662306a36Sopenharmony_ci * write-combining, the logically contiguous address-space of the chip
21762306a36Sopenharmony_ci * may be split into virtually non-contiguous spaces, with different
21862306a36Sopenharmony_ci * attributes, which are them mapped to contiguous physical space
21962306a36Sopenharmony_ci * based from the first BAR.
22062306a36Sopenharmony_ci *
22162306a36Sopenharmony_ci * The code below makes the same assumptions as were made in
22262306a36Sopenharmony_ci * init_chip_wc_pat() (qib_init.c), copied here:
22362306a36Sopenharmony_ci * Assumes chip address space looks like:
22462306a36Sopenharmony_ci *		- kregs + sregs + cregs + uregs (in any order)
22562306a36Sopenharmony_ci *		- piobufs (2K and 4K bufs in either order)
22662306a36Sopenharmony_ci *	or:
22762306a36Sopenharmony_ci *		- kregs + sregs + cregs (in any order)
22862306a36Sopenharmony_ci *		- piobufs (2K and 4K bufs in either order)
22962306a36Sopenharmony_ci *		- uregs
23062306a36Sopenharmony_ci *
23162306a36Sopenharmony_ci * If cntp is non-NULL, returns how many bytes from offset can be accessed
23262306a36Sopenharmony_ci * Returns 0 if the offset is not mapped.
23362306a36Sopenharmony_ci */
23462306a36Sopenharmony_cistatic u32 __iomem *qib_remap_ioaddr32(struct qib_devdata *dd, u32 offset,
23562306a36Sopenharmony_ci				       u32 *cntp)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	u32 kreglen;
23862306a36Sopenharmony_ci	u32 snd_bottom, snd_lim = 0;
23962306a36Sopenharmony_ci	u32 __iomem *krb32 = (u32 __iomem *)dd->kregbase;
24062306a36Sopenharmony_ci	u32 __iomem *map = NULL;
24162306a36Sopenharmony_ci	u32 cnt = 0;
24262306a36Sopenharmony_ci	u32 tot4k, offs4k;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* First, simplest case, offset is within the first map. */
24562306a36Sopenharmony_ci	kreglen = (dd->kregend - dd->kregbase) * sizeof(u64);
24662306a36Sopenharmony_ci	if (offset < kreglen) {
24762306a36Sopenharmony_ci		map = krb32 + (offset / sizeof(u32));
24862306a36Sopenharmony_ci		cnt = kreglen - offset;
24962306a36Sopenharmony_ci		goto mapped;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/*
25362306a36Sopenharmony_ci	 * Next check for user regs, the next most common case,
25462306a36Sopenharmony_ci	 * and a cheap check because if they are not in the first map
25562306a36Sopenharmony_ci	 * they are last in chip.
25662306a36Sopenharmony_ci	 */
25762306a36Sopenharmony_ci	if (dd->userbase) {
25862306a36Sopenharmony_ci		/* If user regs mapped, they are after send, so set limit. */
25962306a36Sopenharmony_ci		u32 ulim = (dd->cfgctxts * dd->ureg_align) + dd->uregbase;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci		if (!dd->piovl15base)
26262306a36Sopenharmony_ci			snd_lim = dd->uregbase;
26362306a36Sopenharmony_ci		krb32 = (u32 __iomem *)dd->userbase;
26462306a36Sopenharmony_ci		if (offset >= dd->uregbase && offset < ulim) {
26562306a36Sopenharmony_ci			map = krb32 + (offset - dd->uregbase) / sizeof(u32);
26662306a36Sopenharmony_ci			cnt = ulim - offset;
26762306a36Sopenharmony_ci			goto mapped;
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/*
27262306a36Sopenharmony_ci	 * Lastly, check for offset within Send Buffers.
27362306a36Sopenharmony_ci	 * This is gnarly because struct devdata is deliberately vague
27462306a36Sopenharmony_ci	 * about things like 7322 VL15 buffers, and we are not in
27562306a36Sopenharmony_ci	 * chip-specific code here, so should not make many assumptions.
27662306a36Sopenharmony_ci	 * The one we _do_ make is that the only chip that has more sndbufs
27762306a36Sopenharmony_ci	 * than we admit is the 7322, and it has userregs above that, so
27862306a36Sopenharmony_ci	 * we know the snd_lim.
27962306a36Sopenharmony_ci	 */
28062306a36Sopenharmony_ci	/* Assume 2K buffers are first. */
28162306a36Sopenharmony_ci	snd_bottom = dd->pio2k_bufbase;
28262306a36Sopenharmony_ci	if (snd_lim == 0) {
28362306a36Sopenharmony_ci		u32 tot2k = dd->piobcnt2k * ALIGN(dd->piosize2k, dd->palign);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		snd_lim = snd_bottom + tot2k;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci	/* If 4k buffers exist, account for them by bumping
28862306a36Sopenharmony_ci	 * appropriate limit.
28962306a36Sopenharmony_ci	 */
29062306a36Sopenharmony_ci	tot4k = dd->piobcnt4k * dd->align4k;
29162306a36Sopenharmony_ci	offs4k = dd->piobufbase >> 32;
29262306a36Sopenharmony_ci	if (dd->piobcnt4k) {
29362306a36Sopenharmony_ci		if (snd_bottom > offs4k)
29462306a36Sopenharmony_ci			snd_bottom = offs4k;
29562306a36Sopenharmony_ci		else {
29662306a36Sopenharmony_ci			/* 4k above 2k. Bump snd_lim, if needed*/
29762306a36Sopenharmony_ci			if (!dd->userbase || dd->piovl15base)
29862306a36Sopenharmony_ci				snd_lim = offs4k + tot4k;
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci	/*
30262306a36Sopenharmony_ci	 * Judgement call: can we ignore the space between SendBuffs and
30362306a36Sopenharmony_ci	 * UserRegs, where we would like to see vl15 buffs, but not more?
30462306a36Sopenharmony_ci	 */
30562306a36Sopenharmony_ci	if (offset >= snd_bottom && offset < snd_lim) {
30662306a36Sopenharmony_ci		offset -= snd_bottom;
30762306a36Sopenharmony_ci		map = (u32 __iomem *)dd->piobase + (offset / sizeof(u32));
30862306a36Sopenharmony_ci		cnt = snd_lim - offset;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (!map && offs4k && dd->piovl15base) {
31262306a36Sopenharmony_ci		snd_lim = offs4k + tot4k + 2 * dd->align4k;
31362306a36Sopenharmony_ci		if (offset >= (offs4k + tot4k) && offset < snd_lim) {
31462306a36Sopenharmony_ci			map = (u32 __iomem *)dd->piovl15base +
31562306a36Sopenharmony_ci				((offset - (offs4k + tot4k)) / sizeof(u32));
31662306a36Sopenharmony_ci			cnt = snd_lim - offset;
31762306a36Sopenharmony_ci		}
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cimapped:
32162306a36Sopenharmony_ci	if (cntp)
32262306a36Sopenharmony_ci		*cntp = cnt;
32362306a36Sopenharmony_ci	return map;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci/*
32762306a36Sopenharmony_ci * qib_read_umem64 - read a 64-bit quantity from the chip into user space
32862306a36Sopenharmony_ci * @dd: the qlogic_ib device
32962306a36Sopenharmony_ci * @uaddr: the location to store the data in user memory
33062306a36Sopenharmony_ci * @regoffs: the offset from BAR0 (_NOT_ full pointer, anymore)
33162306a36Sopenharmony_ci * @count: number of bytes to copy (multiple of 32 bits)
33262306a36Sopenharmony_ci *
33362306a36Sopenharmony_ci * This function also localizes all chip memory accesses.
33462306a36Sopenharmony_ci * The copy should be written such that we read full cacheline packets
33562306a36Sopenharmony_ci * from the chip.  This is usually used for a single qword
33662306a36Sopenharmony_ci *
33762306a36Sopenharmony_ci * NOTE:  This assumes the chip address is 64-bit aligned.
33862306a36Sopenharmony_ci */
33962306a36Sopenharmony_cistatic int qib_read_umem64(struct qib_devdata *dd, void __user *uaddr,
34062306a36Sopenharmony_ci			   u32 regoffs, size_t count)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	const u64 __iomem *reg_addr;
34362306a36Sopenharmony_ci	const u64 __iomem *reg_end;
34462306a36Sopenharmony_ci	u32 limit;
34562306a36Sopenharmony_ci	int ret;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	reg_addr = (const u64 __iomem *)qib_remap_ioaddr32(dd, regoffs, &limit);
34862306a36Sopenharmony_ci	if (reg_addr == NULL || limit == 0 || !(dd->flags & QIB_PRESENT)) {
34962306a36Sopenharmony_ci		ret = -EINVAL;
35062306a36Sopenharmony_ci		goto bail;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci	if (count >= limit)
35362306a36Sopenharmony_ci		count = limit;
35462306a36Sopenharmony_ci	reg_end = reg_addr + (count / sizeof(u64));
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/* not very efficient, but it works for now */
35762306a36Sopenharmony_ci	while (reg_addr < reg_end) {
35862306a36Sopenharmony_ci		u64 data = readq(reg_addr);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		if (copy_to_user(uaddr, &data, sizeof(u64))) {
36162306a36Sopenharmony_ci			ret = -EFAULT;
36262306a36Sopenharmony_ci			goto bail;
36362306a36Sopenharmony_ci		}
36462306a36Sopenharmony_ci		reg_addr++;
36562306a36Sopenharmony_ci		uaddr += sizeof(u64);
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci	ret = 0;
36862306a36Sopenharmony_cibail:
36962306a36Sopenharmony_ci	return ret;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci/*
37362306a36Sopenharmony_ci * qib_write_umem64 - write a 64-bit quantity to the chip from user space
37462306a36Sopenharmony_ci * @dd: the qlogic_ib device
37562306a36Sopenharmony_ci * @regoffs: the offset from BAR0 (_NOT_ full pointer, anymore)
37662306a36Sopenharmony_ci * @uaddr: the source of the data in user memory
37762306a36Sopenharmony_ci * @count: the number of bytes to copy (multiple of 32 bits)
37862306a36Sopenharmony_ci *
37962306a36Sopenharmony_ci * This is usually used for a single qword
38062306a36Sopenharmony_ci * NOTE:  This assumes the chip address is 64-bit aligned.
38162306a36Sopenharmony_ci */
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic int qib_write_umem64(struct qib_devdata *dd, u32 regoffs,
38462306a36Sopenharmony_ci			    const void __user *uaddr, size_t count)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	u64 __iomem *reg_addr;
38762306a36Sopenharmony_ci	const u64 __iomem *reg_end;
38862306a36Sopenharmony_ci	u32 limit;
38962306a36Sopenharmony_ci	int ret;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	reg_addr = (u64 __iomem *)qib_remap_ioaddr32(dd, regoffs, &limit);
39262306a36Sopenharmony_ci	if (reg_addr == NULL || limit == 0 || !(dd->flags & QIB_PRESENT)) {
39362306a36Sopenharmony_ci		ret = -EINVAL;
39462306a36Sopenharmony_ci		goto bail;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci	if (count >= limit)
39762306a36Sopenharmony_ci		count = limit;
39862306a36Sopenharmony_ci	reg_end = reg_addr + (count / sizeof(u64));
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	/* not very efficient, but it works for now */
40162306a36Sopenharmony_ci	while (reg_addr < reg_end) {
40262306a36Sopenharmony_ci		u64 data;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		if (copy_from_user(&data, uaddr, sizeof(data))) {
40562306a36Sopenharmony_ci			ret = -EFAULT;
40662306a36Sopenharmony_ci			goto bail;
40762306a36Sopenharmony_ci		}
40862306a36Sopenharmony_ci		writeq(data, reg_addr);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		reg_addr++;
41162306a36Sopenharmony_ci		uaddr += sizeof(u64);
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci	ret = 0;
41462306a36Sopenharmony_cibail:
41562306a36Sopenharmony_ci	return ret;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci/*
41962306a36Sopenharmony_ci * qib_read_umem32 - read a 32-bit quantity from the chip into user space
42062306a36Sopenharmony_ci * @dd: the qlogic_ib device
42162306a36Sopenharmony_ci * @uaddr: the location to store the data in user memory
42262306a36Sopenharmony_ci * @regoffs: the offset from BAR0 (_NOT_ full pointer, anymore)
42362306a36Sopenharmony_ci * @count: number of bytes to copy
42462306a36Sopenharmony_ci *
42562306a36Sopenharmony_ci * read 32 bit values, not 64 bit; for memories that only
42662306a36Sopenharmony_ci * support 32 bit reads; usually a single dword.
42762306a36Sopenharmony_ci */
42862306a36Sopenharmony_cistatic int qib_read_umem32(struct qib_devdata *dd, void __user *uaddr,
42962306a36Sopenharmony_ci			   u32 regoffs, size_t count)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	const u32 __iomem *reg_addr;
43262306a36Sopenharmony_ci	const u32 __iomem *reg_end;
43362306a36Sopenharmony_ci	u32 limit;
43462306a36Sopenharmony_ci	int ret;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	reg_addr = qib_remap_ioaddr32(dd, regoffs, &limit);
43762306a36Sopenharmony_ci	if (reg_addr == NULL || limit == 0 || !(dd->flags & QIB_PRESENT)) {
43862306a36Sopenharmony_ci		ret = -EINVAL;
43962306a36Sopenharmony_ci		goto bail;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci	if (count >= limit)
44262306a36Sopenharmony_ci		count = limit;
44362306a36Sopenharmony_ci	reg_end = reg_addr + (count / sizeof(u32));
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* not very efficient, but it works for now */
44662306a36Sopenharmony_ci	while (reg_addr < reg_end) {
44762306a36Sopenharmony_ci		u32 data = readl(reg_addr);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci		if (copy_to_user(uaddr, &data, sizeof(data))) {
45062306a36Sopenharmony_ci			ret = -EFAULT;
45162306a36Sopenharmony_ci			goto bail;
45262306a36Sopenharmony_ci		}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		reg_addr++;
45562306a36Sopenharmony_ci		uaddr += sizeof(u32);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci	ret = 0;
45962306a36Sopenharmony_cibail:
46062306a36Sopenharmony_ci	return ret;
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci/*
46462306a36Sopenharmony_ci * qib_write_umem32 - write a 32-bit quantity to the chip from user space
46562306a36Sopenharmony_ci * @dd: the qlogic_ib device
46662306a36Sopenharmony_ci * @regoffs: the offset from BAR0 (_NOT_ full pointer, anymore)
46762306a36Sopenharmony_ci * @uaddr: the source of the data in user memory
46862306a36Sopenharmony_ci * @count: number of bytes to copy
46962306a36Sopenharmony_ci *
47062306a36Sopenharmony_ci * write 32 bit values, not 64 bit; for memories that only
47162306a36Sopenharmony_ci * support 32 bit write; usually a single dword.
47262306a36Sopenharmony_ci */
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic int qib_write_umem32(struct qib_devdata *dd, u32 regoffs,
47562306a36Sopenharmony_ci			    const void __user *uaddr, size_t count)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	u32 __iomem *reg_addr;
47862306a36Sopenharmony_ci	const u32 __iomem *reg_end;
47962306a36Sopenharmony_ci	u32 limit;
48062306a36Sopenharmony_ci	int ret;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	reg_addr = qib_remap_ioaddr32(dd, regoffs, &limit);
48362306a36Sopenharmony_ci	if (reg_addr == NULL || limit == 0 || !(dd->flags & QIB_PRESENT)) {
48462306a36Sopenharmony_ci		ret = -EINVAL;
48562306a36Sopenharmony_ci		goto bail;
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci	if (count >= limit)
48862306a36Sopenharmony_ci		count = limit;
48962306a36Sopenharmony_ci	reg_end = reg_addr + (count / sizeof(u32));
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	while (reg_addr < reg_end) {
49262306a36Sopenharmony_ci		u32 data;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci		if (copy_from_user(&data, uaddr, sizeof(data))) {
49562306a36Sopenharmony_ci			ret = -EFAULT;
49662306a36Sopenharmony_ci			goto bail;
49762306a36Sopenharmony_ci		}
49862306a36Sopenharmony_ci		writel(data, reg_addr);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		reg_addr++;
50162306a36Sopenharmony_ci		uaddr += sizeof(u32);
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci	ret = 0;
50462306a36Sopenharmony_cibail:
50562306a36Sopenharmony_ci	return ret;
50662306a36Sopenharmony_ci}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_cistatic int qib_diag_open(struct inode *in, struct file *fp)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	int unit = iminor(in) - QIB_DIAG_MINOR_BASE;
51162306a36Sopenharmony_ci	struct qib_devdata *dd;
51262306a36Sopenharmony_ci	struct qib_diag_client *dc;
51362306a36Sopenharmony_ci	int ret;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	mutex_lock(&qib_mutex);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	dd = qib_lookup(unit);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	if (dd == NULL || !(dd->flags & QIB_PRESENT) ||
52062306a36Sopenharmony_ci	    !dd->kregbase) {
52162306a36Sopenharmony_ci		ret = -ENODEV;
52262306a36Sopenharmony_ci		goto bail;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	dc = get_client(dd);
52662306a36Sopenharmony_ci	if (!dc) {
52762306a36Sopenharmony_ci		ret = -ENOMEM;
52862306a36Sopenharmony_ci		goto bail;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci	dc->next = dd->diag_client;
53162306a36Sopenharmony_ci	dd->diag_client = dc;
53262306a36Sopenharmony_ci	fp->private_data = dc;
53362306a36Sopenharmony_ci	ret = 0;
53462306a36Sopenharmony_cibail:
53562306a36Sopenharmony_ci	mutex_unlock(&qib_mutex);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	return ret;
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci/**
54162306a36Sopenharmony_ci * qib_diagpkt_write - write an IB packet
54262306a36Sopenharmony_ci * @fp: the diag data device file pointer
54362306a36Sopenharmony_ci * @data: qib_diag_pkt structure saying where to get the packet
54462306a36Sopenharmony_ci * @count: size of data to write
54562306a36Sopenharmony_ci * @off: unused by this code
54662306a36Sopenharmony_ci */
54762306a36Sopenharmony_cistatic ssize_t qib_diagpkt_write(struct file *fp,
54862306a36Sopenharmony_ci				 const char __user *data,
54962306a36Sopenharmony_ci				 size_t count, loff_t *off)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	u32 __iomem *piobuf;
55262306a36Sopenharmony_ci	u32 plen, pbufn, maxlen_reserve;
55362306a36Sopenharmony_ci	struct qib_diag_xpkt dp;
55462306a36Sopenharmony_ci	u32 *tmpbuf = NULL;
55562306a36Sopenharmony_ci	struct qib_devdata *dd;
55662306a36Sopenharmony_ci	struct qib_pportdata *ppd;
55762306a36Sopenharmony_ci	ssize_t ret = 0;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if (count != sizeof(dp)) {
56062306a36Sopenharmony_ci		ret = -EINVAL;
56162306a36Sopenharmony_ci		goto bail;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci	if (copy_from_user(&dp, data, sizeof(dp))) {
56462306a36Sopenharmony_ci		ret = -EFAULT;
56562306a36Sopenharmony_ci		goto bail;
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	dd = qib_lookup(dp.unit);
56962306a36Sopenharmony_ci	if (!dd || !(dd->flags & QIB_PRESENT) || !dd->kregbase) {
57062306a36Sopenharmony_ci		ret = -ENODEV;
57162306a36Sopenharmony_ci		goto bail;
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci	if (!(dd->flags & QIB_INITTED)) {
57462306a36Sopenharmony_ci		/* no hardware, freeze, etc. */
57562306a36Sopenharmony_ci		ret = -ENODEV;
57662306a36Sopenharmony_ci		goto bail;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	if (dp.version != _DIAG_XPKT_VERS) {
58062306a36Sopenharmony_ci		qib_dev_err(dd, "Invalid version %u for diagpkt_write\n",
58162306a36Sopenharmony_ci			    dp.version);
58262306a36Sopenharmony_ci		ret = -EINVAL;
58362306a36Sopenharmony_ci		goto bail;
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci	/* send count must be an exact number of dwords */
58662306a36Sopenharmony_ci	if (dp.len & 3) {
58762306a36Sopenharmony_ci		ret = -EINVAL;
58862306a36Sopenharmony_ci		goto bail;
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci	if (!dp.port || dp.port > dd->num_pports) {
59162306a36Sopenharmony_ci		ret = -EINVAL;
59262306a36Sopenharmony_ci		goto bail;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci	ppd = &dd->pport[dp.port - 1];
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	/*
59762306a36Sopenharmony_ci	 * need total length before first word written, plus 2 Dwords. One Dword
59862306a36Sopenharmony_ci	 * is for padding so we get the full user data when not aligned on
59962306a36Sopenharmony_ci	 * a word boundary. The other Dword is to make sure we have room for the
60062306a36Sopenharmony_ci	 * ICRC which gets tacked on later.
60162306a36Sopenharmony_ci	 */
60262306a36Sopenharmony_ci	maxlen_reserve = 2 * sizeof(u32);
60362306a36Sopenharmony_ci	if (dp.len > ppd->ibmaxlen - maxlen_reserve) {
60462306a36Sopenharmony_ci		ret = -EINVAL;
60562306a36Sopenharmony_ci		goto bail;
60662306a36Sopenharmony_ci	}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	plen = sizeof(u32) + dp.len;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	tmpbuf = vmalloc(plen);
61162306a36Sopenharmony_ci	if (!tmpbuf) {
61262306a36Sopenharmony_ci		ret = -ENOMEM;
61362306a36Sopenharmony_ci		goto bail;
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (copy_from_user(tmpbuf,
61762306a36Sopenharmony_ci			   u64_to_user_ptr(dp.data),
61862306a36Sopenharmony_ci			   dp.len)) {
61962306a36Sopenharmony_ci		ret = -EFAULT;
62062306a36Sopenharmony_ci		goto bail;
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	plen >>= 2;             /* in dwords */
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	if (dp.pbc_wd == 0)
62662306a36Sopenharmony_ci		dp.pbc_wd = plen;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	piobuf = dd->f_getsendbuf(ppd, dp.pbc_wd, &pbufn);
62962306a36Sopenharmony_ci	if (!piobuf) {
63062306a36Sopenharmony_ci		ret = -EBUSY;
63162306a36Sopenharmony_ci		goto bail;
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci	/* disarm it just to be extra sure */
63462306a36Sopenharmony_ci	dd->f_sendctrl(dd->pport, QIB_SENDCTRL_DISARM_BUF(pbufn));
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	/* disable header check on pbufn for this packet */
63762306a36Sopenharmony_ci	dd->f_txchk_change(dd, pbufn, 1, TXCHK_CHG_TYPE_DIS1, NULL);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	writeq(dp.pbc_wd, piobuf);
64062306a36Sopenharmony_ci	/*
64162306a36Sopenharmony_ci	 * Copy all but the trigger word, then flush, so it's written
64262306a36Sopenharmony_ci	 * to chip before trigger word, then write trigger word, then
64362306a36Sopenharmony_ci	 * flush again, so packet is sent.
64462306a36Sopenharmony_ci	 */
64562306a36Sopenharmony_ci	if (dd->flags & QIB_PIO_FLUSH_WC) {
64662306a36Sopenharmony_ci		qib_flush_wc();
64762306a36Sopenharmony_ci		qib_pio_copy(piobuf + 2, tmpbuf, plen - 1);
64862306a36Sopenharmony_ci		qib_flush_wc();
64962306a36Sopenharmony_ci		__raw_writel(tmpbuf[plen - 1], piobuf + plen + 1);
65062306a36Sopenharmony_ci	} else
65162306a36Sopenharmony_ci		qib_pio_copy(piobuf + 2, tmpbuf, plen);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (dd->flags & QIB_USE_SPCL_TRIG) {
65462306a36Sopenharmony_ci		u32 spcl_off = (pbufn >= dd->piobcnt2k) ? 2047 : 1023;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci		qib_flush_wc();
65762306a36Sopenharmony_ci		__raw_writel(0xaebecede, piobuf + spcl_off);
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	/*
66162306a36Sopenharmony_ci	 * Ensure buffer is written to the chip, then re-enable
66262306a36Sopenharmony_ci	 * header checks (if supported by chip).  The txchk
66362306a36Sopenharmony_ci	 * code will ensure seen by chip before returning.
66462306a36Sopenharmony_ci	 */
66562306a36Sopenharmony_ci	qib_flush_wc();
66662306a36Sopenharmony_ci	qib_sendbuf_done(dd, pbufn);
66762306a36Sopenharmony_ci	dd->f_txchk_change(dd, pbufn, 1, TXCHK_CHG_TYPE_ENAB1, NULL);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	ret = sizeof(dp);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cibail:
67262306a36Sopenharmony_ci	vfree(tmpbuf);
67362306a36Sopenharmony_ci	return ret;
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_cistatic int qib_diag_release(struct inode *in, struct file *fp)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	mutex_lock(&qib_mutex);
67962306a36Sopenharmony_ci	return_client(fp->private_data);
68062306a36Sopenharmony_ci	fp->private_data = NULL;
68162306a36Sopenharmony_ci	mutex_unlock(&qib_mutex);
68262306a36Sopenharmony_ci	return 0;
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci/*
68662306a36Sopenharmony_ci * Chip-specific code calls to register its interest in
68762306a36Sopenharmony_ci * a specific range.
68862306a36Sopenharmony_ci */
68962306a36Sopenharmony_cistruct diag_observer_list_elt {
69062306a36Sopenharmony_ci	struct diag_observer_list_elt *next;
69162306a36Sopenharmony_ci	const struct diag_observer *op;
69262306a36Sopenharmony_ci};
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ciint qib_register_observer(struct qib_devdata *dd,
69562306a36Sopenharmony_ci			  const struct diag_observer *op)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	struct diag_observer_list_elt *olp;
69862306a36Sopenharmony_ci	unsigned long flags;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	if (!dd || !op)
70162306a36Sopenharmony_ci		return -EINVAL;
70262306a36Sopenharmony_ci	olp = vmalloc(sizeof(*olp));
70362306a36Sopenharmony_ci	if (!olp)
70462306a36Sopenharmony_ci		return -ENOMEM;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	spin_lock_irqsave(&dd->qib_diag_trans_lock, flags);
70762306a36Sopenharmony_ci	olp->op = op;
70862306a36Sopenharmony_ci	olp->next = dd->diag_observer_list;
70962306a36Sopenharmony_ci	dd->diag_observer_list = olp;
71062306a36Sopenharmony_ci	spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	return 0;
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci/* Remove all registered observers when device is closed */
71662306a36Sopenharmony_cistatic void qib_unregister_observers(struct qib_devdata *dd)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	struct diag_observer_list_elt *olp;
71962306a36Sopenharmony_ci	unsigned long flags;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	spin_lock_irqsave(&dd->qib_diag_trans_lock, flags);
72262306a36Sopenharmony_ci	olp = dd->diag_observer_list;
72362306a36Sopenharmony_ci	while (olp) {
72462306a36Sopenharmony_ci		/* Pop one observer, let go of lock */
72562306a36Sopenharmony_ci		dd->diag_observer_list = olp->next;
72662306a36Sopenharmony_ci		spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags);
72762306a36Sopenharmony_ci		vfree(olp);
72862306a36Sopenharmony_ci		/* try again. */
72962306a36Sopenharmony_ci		spin_lock_irqsave(&dd->qib_diag_trans_lock, flags);
73062306a36Sopenharmony_ci		olp = dd->diag_observer_list;
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci	spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags);
73362306a36Sopenharmony_ci}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci/*
73662306a36Sopenharmony_ci * Find the observer, if any, for the specified address. Initial implementation
73762306a36Sopenharmony_ci * is simple stack of observers. This must be called with diag transaction
73862306a36Sopenharmony_ci * lock held.
73962306a36Sopenharmony_ci */
74062306a36Sopenharmony_cistatic const struct diag_observer *diag_get_observer(struct qib_devdata *dd,
74162306a36Sopenharmony_ci						     u32 addr)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	struct diag_observer_list_elt *olp;
74462306a36Sopenharmony_ci	const struct diag_observer *op = NULL;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	olp = dd->diag_observer_list;
74762306a36Sopenharmony_ci	while (olp) {
74862306a36Sopenharmony_ci		op = olp->op;
74962306a36Sopenharmony_ci		if (addr >= op->bottom && addr <= op->top)
75062306a36Sopenharmony_ci			break;
75162306a36Sopenharmony_ci		olp = olp->next;
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci	if (!olp)
75462306a36Sopenharmony_ci		op = NULL;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	return op;
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cistatic ssize_t qib_diag_read(struct file *fp, char __user *data,
76062306a36Sopenharmony_ci			     size_t count, loff_t *off)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	struct qib_diag_client *dc = fp->private_data;
76362306a36Sopenharmony_ci	struct qib_devdata *dd = dc->dd;
76462306a36Sopenharmony_ci	ssize_t ret;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	if (dc->pid != current->pid) {
76762306a36Sopenharmony_ci		ret = -EPERM;
76862306a36Sopenharmony_ci		goto bail;
76962306a36Sopenharmony_ci	}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	if (count == 0)
77262306a36Sopenharmony_ci		ret = 0;
77362306a36Sopenharmony_ci	else if ((count % 4) || (*off % 4))
77462306a36Sopenharmony_ci		/* address or length is not 32-bit aligned, hence invalid */
77562306a36Sopenharmony_ci		ret = -EINVAL;
77662306a36Sopenharmony_ci	else if (dc->state < READY && (*off || count != 8))
77762306a36Sopenharmony_ci		ret = -EINVAL;  /* prevent cat /dev/qib_diag* */
77862306a36Sopenharmony_ci	else {
77962306a36Sopenharmony_ci		unsigned long flags;
78062306a36Sopenharmony_ci		u64 data64 = 0;
78162306a36Sopenharmony_ci		int use_32;
78262306a36Sopenharmony_ci		const struct diag_observer *op;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		use_32 = (count % 8) || (*off % 8);
78562306a36Sopenharmony_ci		ret = -1;
78662306a36Sopenharmony_ci		spin_lock_irqsave(&dd->qib_diag_trans_lock, flags);
78762306a36Sopenharmony_ci		/*
78862306a36Sopenharmony_ci		 * Check for observer on this address range.
78962306a36Sopenharmony_ci		 * we only support a single 32 or 64-bit read
79062306a36Sopenharmony_ci		 * via observer, currently.
79162306a36Sopenharmony_ci		 */
79262306a36Sopenharmony_ci		op = diag_get_observer(dd, *off);
79362306a36Sopenharmony_ci		if (op) {
79462306a36Sopenharmony_ci			u32 offset = *off;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci			ret = op->hook(dd, op, offset, &data64, 0, use_32);
79762306a36Sopenharmony_ci		}
79862306a36Sopenharmony_ci		/*
79962306a36Sopenharmony_ci		 * We need to release lock before any copy_to_user(),
80062306a36Sopenharmony_ci		 * whether implicit in qib_read_umem* or explicit below.
80162306a36Sopenharmony_ci		 */
80262306a36Sopenharmony_ci		spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags);
80362306a36Sopenharmony_ci		if (!op) {
80462306a36Sopenharmony_ci			if (use_32)
80562306a36Sopenharmony_ci				/*
80662306a36Sopenharmony_ci				 * Address or length is not 64-bit aligned;
80762306a36Sopenharmony_ci				 * do 32-bit rd
80862306a36Sopenharmony_ci				 */
80962306a36Sopenharmony_ci				ret = qib_read_umem32(dd, data, (u32) *off,
81062306a36Sopenharmony_ci						      count);
81162306a36Sopenharmony_ci			else
81262306a36Sopenharmony_ci				ret = qib_read_umem64(dd, data, (u32) *off,
81362306a36Sopenharmony_ci						      count);
81462306a36Sopenharmony_ci		} else if (ret == count) {
81562306a36Sopenharmony_ci			/* Below finishes case where observer existed */
81662306a36Sopenharmony_ci			ret = copy_to_user(data, &data64, use_32 ?
81762306a36Sopenharmony_ci					   sizeof(u32) : sizeof(u64));
81862306a36Sopenharmony_ci			if (ret)
81962306a36Sopenharmony_ci				ret = -EFAULT;
82062306a36Sopenharmony_ci		}
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	if (ret >= 0) {
82462306a36Sopenharmony_ci		*off += count;
82562306a36Sopenharmony_ci		ret = count;
82662306a36Sopenharmony_ci		if (dc->state == OPENED)
82762306a36Sopenharmony_ci			dc->state = INIT;
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_cibail:
83062306a36Sopenharmony_ci	return ret;
83162306a36Sopenharmony_ci}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_cistatic ssize_t qib_diag_write(struct file *fp, const char __user *data,
83462306a36Sopenharmony_ci			      size_t count, loff_t *off)
83562306a36Sopenharmony_ci{
83662306a36Sopenharmony_ci	struct qib_diag_client *dc = fp->private_data;
83762306a36Sopenharmony_ci	struct qib_devdata *dd = dc->dd;
83862306a36Sopenharmony_ci	ssize_t ret;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	if (dc->pid != current->pid) {
84162306a36Sopenharmony_ci		ret = -EPERM;
84262306a36Sopenharmony_ci		goto bail;
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	if (count == 0)
84662306a36Sopenharmony_ci		ret = 0;
84762306a36Sopenharmony_ci	else if ((count % 4) || (*off % 4))
84862306a36Sopenharmony_ci		/* address or length is not 32-bit aligned, hence invalid */
84962306a36Sopenharmony_ci		ret = -EINVAL;
85062306a36Sopenharmony_ci	else if (dc->state < READY &&
85162306a36Sopenharmony_ci		((*off || count != 8) || dc->state != INIT))
85262306a36Sopenharmony_ci		/* No writes except second-step of init seq */
85362306a36Sopenharmony_ci		ret = -EINVAL;  /* before any other write allowed */
85462306a36Sopenharmony_ci	else {
85562306a36Sopenharmony_ci		unsigned long flags;
85662306a36Sopenharmony_ci		const struct diag_observer *op = NULL;
85762306a36Sopenharmony_ci		int use_32 =  (count % 8) || (*off % 8);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci		/*
86062306a36Sopenharmony_ci		 * Check for observer on this address range.
86162306a36Sopenharmony_ci		 * We only support a single 32 or 64-bit write
86262306a36Sopenharmony_ci		 * via observer, currently. This helps, because
86362306a36Sopenharmony_ci		 * we would otherwise have to jump through hoops
86462306a36Sopenharmony_ci		 * to make "diag transaction" meaningful when we
86562306a36Sopenharmony_ci		 * cannot do a copy_from_user while holding the lock.
86662306a36Sopenharmony_ci		 */
86762306a36Sopenharmony_ci		if (count == 4 || count == 8) {
86862306a36Sopenharmony_ci			u64 data64;
86962306a36Sopenharmony_ci			u32 offset = *off;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci			ret = copy_from_user(&data64, data, count);
87262306a36Sopenharmony_ci			if (ret) {
87362306a36Sopenharmony_ci				ret = -EFAULT;
87462306a36Sopenharmony_ci				goto bail;
87562306a36Sopenharmony_ci			}
87662306a36Sopenharmony_ci			spin_lock_irqsave(&dd->qib_diag_trans_lock, flags);
87762306a36Sopenharmony_ci			op = diag_get_observer(dd, *off);
87862306a36Sopenharmony_ci			if (op)
87962306a36Sopenharmony_ci				ret = op->hook(dd, op, offset, &data64, ~0Ull,
88062306a36Sopenharmony_ci					       use_32);
88162306a36Sopenharmony_ci			spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags);
88262306a36Sopenharmony_ci		}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci		if (!op) {
88562306a36Sopenharmony_ci			if (use_32)
88662306a36Sopenharmony_ci				/*
88762306a36Sopenharmony_ci				 * Address or length is not 64-bit aligned;
88862306a36Sopenharmony_ci				 * do 32-bit write
88962306a36Sopenharmony_ci				 */
89062306a36Sopenharmony_ci				ret = qib_write_umem32(dd, (u32) *off, data,
89162306a36Sopenharmony_ci						       count);
89262306a36Sopenharmony_ci			else
89362306a36Sopenharmony_ci				ret = qib_write_umem64(dd, (u32) *off, data,
89462306a36Sopenharmony_ci						       count);
89562306a36Sopenharmony_ci		}
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	if (ret >= 0) {
89962306a36Sopenharmony_ci		*off += count;
90062306a36Sopenharmony_ci		ret = count;
90162306a36Sopenharmony_ci		if (dc->state == INIT)
90262306a36Sopenharmony_ci			dc->state = READY; /* all read/write OK now */
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_cibail:
90562306a36Sopenharmony_ci	return ret;
90662306a36Sopenharmony_ci}
907