1f931551bSRalph Campbell /*
27fac3301SMike Marciniszyn  * Copyright (c) 2012 Intel Corporation. All rights reserved.
37fac3301SMike Marciniszyn  * Copyright (c) 2006 - 2012 QLogic Corporation. All rights reserved.
4f931551bSRalph Campbell  * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
5f931551bSRalph Campbell  *
6f931551bSRalph Campbell  * This software is available to you under a choice of one of two
7f931551bSRalph Campbell  * licenses.  You may choose to be licensed under the terms of the GNU
8f931551bSRalph Campbell  * General Public License (GPL) Version 2, available from the file
9f931551bSRalph Campbell  * COPYING in the main directory of this source tree, or the
10f931551bSRalph Campbell  * OpenIB.org BSD license below:
11f931551bSRalph Campbell  *
12f931551bSRalph Campbell  *     Redistribution and use in source and binary forms, with or
13f931551bSRalph Campbell  *     without modification, are permitted provided that the following
14f931551bSRalph Campbell  *     conditions are met:
15f931551bSRalph Campbell  *
16f931551bSRalph Campbell  *      - Redistributions of source code must retain the above
17f931551bSRalph Campbell  *        copyright notice, this list of conditions and the following
18f931551bSRalph Campbell  *        disclaimer.
19f931551bSRalph Campbell  *
20f931551bSRalph Campbell  *      - Redistributions in binary form must reproduce the above
21f931551bSRalph Campbell  *        copyright notice, this list of conditions and the following
22f931551bSRalph Campbell  *        disclaimer in the documentation and/or other materials
23f931551bSRalph Campbell  *        provided with the distribution.
24f931551bSRalph Campbell  *
25f931551bSRalph Campbell  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26f931551bSRalph Campbell  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27f931551bSRalph Campbell  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28f931551bSRalph Campbell  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29f931551bSRalph Campbell  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30f931551bSRalph Campbell  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31f931551bSRalph Campbell  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32f931551bSRalph Campbell  * SOFTWARE.
33f931551bSRalph Campbell  */
34f931551bSRalph Campbell 
35f931551bSRalph Campbell /*
36f931551bSRalph Campbell  * This file contains support for diagnostic functions.  It is accessed by
37f931551bSRalph Campbell  * opening the qib_diag device, normally minor number 129.  Diagnostic use
38f931551bSRalph Campbell  * of the QLogic_IB chip may render the chip or board unusable until the
39f931551bSRalph Campbell  * driver is unloaded, or in some cases, until the system is rebooted.
40f931551bSRalph Campbell  *
41f931551bSRalph Campbell  * Accesses to the chip through this interface are not similar to going
42f931551bSRalph Campbell  * through the /sys/bus/pci resource mmap interface.
43f931551bSRalph Campbell  */
44f931551bSRalph Campbell 
45f931551bSRalph Campbell #include <linux/io.h>
46f931551bSRalph Campbell #include <linux/pci.h>
47f931551bSRalph Campbell #include <linux/poll.h>
48f931551bSRalph Campbell #include <linux/vmalloc.h>
49b108d976SPaul Gortmaker #include <linux/export.h>
50f931551bSRalph Campbell #include <linux/fs.h>
51f931551bSRalph Campbell #include <linux/uaccess.h>
52f931551bSRalph Campbell 
53f931551bSRalph Campbell #include "qib.h"
54f931551bSRalph Campbell #include "qib_common.h"
55f931551bSRalph Campbell 
567fac3301SMike Marciniszyn #undef pr_fmt
577fac3301SMike Marciniszyn #define pr_fmt(fmt) QIB_DRV_NAME ": " fmt
587fac3301SMike Marciniszyn 
59f931551bSRalph Campbell /*
60f931551bSRalph Campbell  * Each client that opens the diag device must read then write
61f931551bSRalph Campbell  * offset 0, to prevent lossage from random cat or od. diag_state
62f931551bSRalph Campbell  * sequences this "handshake".
63f931551bSRalph Campbell  */
64f931551bSRalph Campbell enum diag_state { UNUSED = 0, OPENED, INIT, READY };
65f931551bSRalph Campbell 
66f931551bSRalph Campbell /* State for an individual client. PID so children cannot abuse handshake */
67f931551bSRalph Campbell static struct qib_diag_client {
68f931551bSRalph Campbell 	struct qib_diag_client *next;
69f931551bSRalph Campbell 	struct qib_devdata *dd;
70f931551bSRalph Campbell 	pid_t pid;
71f931551bSRalph Campbell 	enum diag_state state;
72f931551bSRalph Campbell } *client_pool;
73f931551bSRalph Campbell 
74f931551bSRalph Campbell /*
75f931551bSRalph Campbell  * Get a client struct. Recycled if possible, else kmalloc.
76f931551bSRalph Campbell  * Must be called with qib_mutex held
77f931551bSRalph Campbell  */
get_client(struct qib_devdata * dd)78f931551bSRalph Campbell static struct qib_diag_client *get_client(struct qib_devdata *dd)
79f931551bSRalph Campbell {
80f931551bSRalph Campbell 	struct qib_diag_client *dc;
81f931551bSRalph Campbell 
82f931551bSRalph Campbell 	dc = client_pool;
83f931551bSRalph Campbell 	if (dc)
84f931551bSRalph Campbell 		/* got from pool remove it and use */
85f931551bSRalph Campbell 		client_pool = dc->next;
86f931551bSRalph Campbell 	else
87f931551bSRalph Campbell 		/* None in pool, alloc and init */
88041af0bbSMike Marciniszyn 		dc = kmalloc(sizeof(*dc), GFP_KERNEL);
89f931551bSRalph Campbell 
90f931551bSRalph Campbell 	if (dc) {
91f931551bSRalph Campbell 		dc->next = NULL;
92f931551bSRalph Campbell 		dc->dd = dd;
93f931551bSRalph Campbell 		dc->pid = current->pid;
94f931551bSRalph Campbell 		dc->state = OPENED;
95f931551bSRalph Campbell 	}
96f931551bSRalph Campbell 	return dc;
97f931551bSRalph Campbell }
98f931551bSRalph Campbell 
99f931551bSRalph Campbell /*
100f931551bSRalph Campbell  * Return to pool. Must be called with qib_mutex held
101f931551bSRalph Campbell  */
return_client(struct qib_diag_client * dc)102f931551bSRalph Campbell static void return_client(struct qib_diag_client *dc)
103f931551bSRalph Campbell {
104f931551bSRalph Campbell 	struct qib_devdata *dd = dc->dd;
105f931551bSRalph Campbell 	struct qib_diag_client *tdc, *rdc;
106f931551bSRalph Campbell 
107f931551bSRalph Campbell 	rdc = NULL;
108f931551bSRalph Campbell 	if (dc == dd->diag_client) {
109f931551bSRalph Campbell 		dd->diag_client = dc->next;
110f931551bSRalph Campbell 		rdc = dc;
111f931551bSRalph Campbell 	} else {
112f931551bSRalph Campbell 		tdc = dc->dd->diag_client;
113f931551bSRalph Campbell 		while (tdc) {
114f931551bSRalph Campbell 			if (dc == tdc->next) {
115f931551bSRalph Campbell 				tdc->next = dc->next;
116f931551bSRalph Campbell 				rdc = dc;
117f931551bSRalph Campbell 				break;
118f931551bSRalph Campbell 			}
119f931551bSRalph Campbell 			tdc = tdc->next;
120f931551bSRalph Campbell 		}
121f931551bSRalph Campbell 	}
122f931551bSRalph Campbell 	if (rdc) {
123f931551bSRalph Campbell 		rdc->state = UNUSED;
124f931551bSRalph Campbell 		rdc->dd = NULL;
125f931551bSRalph Campbell 		rdc->pid = 0;
126f931551bSRalph Campbell 		rdc->next = client_pool;
127f931551bSRalph Campbell 		client_pool = rdc;
128f931551bSRalph Campbell 	}
129f931551bSRalph Campbell }
130f931551bSRalph Campbell 
131f931551bSRalph Campbell static int qib_diag_open(struct inode *in, struct file *fp);
132f931551bSRalph Campbell static int qib_diag_release(struct inode *in, struct file *fp);
133f931551bSRalph Campbell static ssize_t qib_diag_read(struct file *fp, char __user *data,
134f931551bSRalph Campbell 			     size_t count, loff_t *off);
135f931551bSRalph Campbell static ssize_t qib_diag_write(struct file *fp, const char __user *data,
136f931551bSRalph Campbell 			      size_t count, loff_t *off);
137f931551bSRalph Campbell 
138f931551bSRalph Campbell static const struct file_operations diag_file_ops = {
139f931551bSRalph Campbell 	.owner = THIS_MODULE,
140f931551bSRalph Campbell 	.write = qib_diag_write,
141f931551bSRalph Campbell 	.read = qib_diag_read,
142f931551bSRalph Campbell 	.open = qib_diag_open,
1436038f373SArnd Bergmann 	.release = qib_diag_release,
1446038f373SArnd Bergmann 	.llseek = default_llseek,
145f931551bSRalph Campbell };
146f931551bSRalph Campbell 
147f931551bSRalph Campbell static atomic_t diagpkt_count = ATOMIC_INIT(0);
148f931551bSRalph Campbell static struct cdev *diagpkt_cdev;
149f931551bSRalph Campbell static struct device *diagpkt_device;
150f931551bSRalph Campbell 
151f931551bSRalph Campbell static ssize_t qib_diagpkt_write(struct file *fp, const char __user *data,
152f931551bSRalph Campbell 				 size_t count, loff_t *off);
153f931551bSRalph Campbell 
154f931551bSRalph Campbell static const struct file_operations diagpkt_file_ops = {
155f931551bSRalph Campbell 	.owner = THIS_MODULE,
156f931551bSRalph Campbell 	.write = qib_diagpkt_write,
1576038f373SArnd Bergmann 	.llseek = noop_llseek,
158f931551bSRalph Campbell };
159f931551bSRalph Campbell 
qib_diag_add(struct qib_devdata * dd)160f931551bSRalph Campbell int qib_diag_add(struct qib_devdata *dd)
161f931551bSRalph Campbell {
162f931551bSRalph Campbell 	char name[16];
163f931551bSRalph Campbell 	int ret = 0;
164f931551bSRalph Campbell 
165f931551bSRalph Campbell 	if (atomic_inc_return(&diagpkt_count) == 1) {
166f931551bSRalph Campbell 		ret = qib_cdev_init(QIB_DIAGPKT_MINOR, "ipath_diagpkt",
167f931551bSRalph Campbell 				    &diagpkt_file_ops, &diagpkt_cdev,
168f931551bSRalph Campbell 				    &diagpkt_device);
169f931551bSRalph Campbell 		if (ret)
170f931551bSRalph Campbell 			goto done;
171f931551bSRalph Campbell 	}
172f931551bSRalph Campbell 
173f931551bSRalph Campbell 	snprintf(name, sizeof(name), "ipath_diag%d", dd->unit);
174f931551bSRalph Campbell 	ret = qib_cdev_init(QIB_DIAG_MINOR_BASE + dd->unit, name,
175f931551bSRalph Campbell 			    &diag_file_ops, &dd->diag_cdev,
176f931551bSRalph Campbell 			    &dd->diag_device);
177f931551bSRalph Campbell done:
178f931551bSRalph Campbell 	return ret;
179f931551bSRalph Campbell }
180f931551bSRalph Campbell 
181f931551bSRalph Campbell static void qib_unregister_observers(struct qib_devdata *dd);
182f931551bSRalph Campbell 
qib_diag_remove(struct qib_devdata * dd)183f931551bSRalph Campbell void qib_diag_remove(struct qib_devdata *dd)
184f931551bSRalph Campbell {
185f931551bSRalph Campbell 	struct qib_diag_client *dc;
186f931551bSRalph Campbell 
187f931551bSRalph Campbell 	if (atomic_dec_and_test(&diagpkt_count))
188f931551bSRalph Campbell 		qib_cdev_cleanup(&diagpkt_cdev, &diagpkt_device);
189f931551bSRalph Campbell 
190f931551bSRalph Campbell 	qib_cdev_cleanup(&dd->diag_cdev, &dd->diag_device);
191f931551bSRalph Campbell 
192f931551bSRalph Campbell 	/*
193f931551bSRalph Campbell 	 * Return all diag_clients of this device. There should be none,
194f931551bSRalph Campbell 	 * as we are "guaranteed" that no clients are still open
195f931551bSRalph Campbell 	 */
196f931551bSRalph Campbell 	while (dd->diag_client)
197f931551bSRalph Campbell 		return_client(dd->diag_client);
198f931551bSRalph Campbell 
199f931551bSRalph Campbell 	/* Now clean up all unused client structs */
200f931551bSRalph Campbell 	while (client_pool) {
201f931551bSRalph Campbell 		dc = client_pool;
202f931551bSRalph Campbell 		client_pool = dc->next;
203f931551bSRalph Campbell 		kfree(dc);
204f931551bSRalph Campbell 	}
205f931551bSRalph Campbell 	/* Clean up observer list */
206f931551bSRalph Campbell 	qib_unregister_observers(dd);
207f931551bSRalph Campbell }
208f931551bSRalph Campbell 
209f931551bSRalph Campbell /* qib_remap_ioaddr32 - remap an offset into chip address space to __iomem *
210f931551bSRalph Campbell  *
211f931551bSRalph Campbell  * @dd: the qlogic_ib device
212f931551bSRalph Campbell  * @offs: the offset in chip-space
213f931551bSRalph Campbell  * @cntp: Pointer to max (byte) count for transfer starting at offset
214f931551bSRalph Campbell  * This returns a u32 __iomem * so it can be used for both 64 and 32-bit
215f931551bSRalph Campbell  * mapping. It is needed because with the use of PAT for control of
216f931551bSRalph Campbell  * write-combining, the logically contiguous address-space of the chip
217f931551bSRalph Campbell  * may be split into virtually non-contiguous spaces, with different
218f931551bSRalph Campbell  * attributes, which are them mapped to contiguous physical space
219f931551bSRalph Campbell  * based from the first BAR.
220f931551bSRalph Campbell  *
221f931551bSRalph Campbell  * The code below makes the same assumptions as were made in
222f931551bSRalph Campbell  * init_chip_wc_pat() (qib_init.c), copied here:
223f931551bSRalph Campbell  * Assumes chip address space looks like:
224f931551bSRalph Campbell  *		- kregs + sregs + cregs + uregs (in any order)
225f931551bSRalph Campbell  *		- piobufs (2K and 4K bufs in either order)
226f931551bSRalph Campbell  *	or:
227f931551bSRalph Campbell  *		- kregs + sregs + cregs (in any order)
228f931551bSRalph Campbell  *		- piobufs (2K and 4K bufs in either order)
229f931551bSRalph Campbell  *		- uregs
230f931551bSRalph Campbell  *
231f931551bSRalph Campbell  * If cntp is non-NULL, returns how many bytes from offset can be accessed
232f931551bSRalph Campbell  * Returns 0 if the offset is not mapped.
233f931551bSRalph Campbell  */
qib_remap_ioaddr32(struct qib_devdata * dd,u32 offset,u32 * cntp)234f931551bSRalph Campbell static u32 __iomem *qib_remap_ioaddr32(struct qib_devdata *dd, u32 offset,
235f931551bSRalph Campbell 				       u32 *cntp)
236f931551bSRalph Campbell {
237f931551bSRalph Campbell 	u32 kreglen;
238f931551bSRalph Campbell 	u32 snd_bottom, snd_lim = 0;
239f931551bSRalph Campbell 	u32 __iomem *krb32 = (u32 __iomem *)dd->kregbase;
240f931551bSRalph Campbell 	u32 __iomem *map = NULL;
241f931551bSRalph Campbell 	u32 cnt = 0;
242fce24a9dSDave Olson 	u32 tot4k, offs4k;
243f931551bSRalph Campbell 
244f931551bSRalph Campbell 	/* First, simplest case, offset is within the first map. */
245f931551bSRalph Campbell 	kreglen = (dd->kregend - dd->kregbase) * sizeof(u64);
246f931551bSRalph Campbell 	if (offset < kreglen) {
247f931551bSRalph Campbell 		map = krb32 + (offset / sizeof(u32));
248f931551bSRalph Campbell 		cnt = kreglen - offset;
249f931551bSRalph Campbell 		goto mapped;
250f931551bSRalph Campbell 	}
251f931551bSRalph Campbell 
252f931551bSRalph Campbell 	/*
253f931551bSRalph Campbell 	 * Next check for user regs, the next most common case,
254f931551bSRalph Campbell 	 * and a cheap check because if they are not in the first map
255f931551bSRalph Campbell 	 * they are last in chip.
256f931551bSRalph Campbell 	 */
257f931551bSRalph Campbell 	if (dd->userbase) {
258f931551bSRalph Campbell 		/* If user regs mapped, they are after send, so set limit. */
259f931551bSRalph Campbell 		u32 ulim = (dd->cfgctxts * dd->ureg_align) + dd->uregbase;
260da12c1f6SMike Marciniszyn 
261fce24a9dSDave Olson 		if (!dd->piovl15base)
262f931551bSRalph Campbell 			snd_lim = dd->uregbase;
263f931551bSRalph Campbell 		krb32 = (u32 __iomem *)dd->userbase;
264f931551bSRalph Campbell 		if (offset >= dd->uregbase && offset < ulim) {
265f931551bSRalph Campbell 			map = krb32 + (offset - dd->uregbase) / sizeof(u32);
266f931551bSRalph Campbell 			cnt = ulim - offset;
267f931551bSRalph Campbell 			goto mapped;
268f931551bSRalph Campbell 		}
269f931551bSRalph Campbell 	}
270f931551bSRalph Campbell 
271f931551bSRalph Campbell 	/*
272f931551bSRalph Campbell 	 * Lastly, check for offset within Send Buffers.
273f931551bSRalph Campbell 	 * This is gnarly because struct devdata is deliberately vague
274f931551bSRalph Campbell 	 * about things like 7322 VL15 buffers, and we are not in
275f931551bSRalph Campbell 	 * chip-specific code here, so should not make many assumptions.
276f931551bSRalph Campbell 	 * The one we _do_ make is that the only chip that has more sndbufs
277f931551bSRalph Campbell 	 * than we admit is the 7322, and it has userregs above that, so
278f931551bSRalph Campbell 	 * we know the snd_lim.
279f931551bSRalph Campbell 	 */
280f931551bSRalph Campbell 	/* Assume 2K buffers are first. */
281f931551bSRalph Campbell 	snd_bottom = dd->pio2k_bufbase;
282f931551bSRalph Campbell 	if (snd_lim == 0) {
283f931551bSRalph Campbell 		u32 tot2k = dd->piobcnt2k * ALIGN(dd->piosize2k, dd->palign);
284da12c1f6SMike Marciniszyn 
285f931551bSRalph Campbell 		snd_lim = snd_bottom + tot2k;
286f931551bSRalph Campbell 	}
287f931551bSRalph Campbell 	/* If 4k buffers exist, account for them by bumping
288f931551bSRalph Campbell 	 * appropriate limit.
289f931551bSRalph Campbell 	 */
290fce24a9dSDave Olson 	tot4k = dd->piobcnt4k * dd->align4k;
291fce24a9dSDave Olson 	offs4k = dd->piobufbase >> 32;
292f931551bSRalph Campbell 	if (dd->piobcnt4k) {
293f931551bSRalph Campbell 		if (snd_bottom > offs4k)
294f931551bSRalph Campbell 			snd_bottom = offs4k;
295f931551bSRalph Campbell 		else {
296f931551bSRalph Campbell 			/* 4k above 2k. Bump snd_lim, if needed*/
297fce24a9dSDave Olson 			if (!dd->userbase || dd->piovl15base)
298f931551bSRalph Campbell 				snd_lim = offs4k + tot4k;
299f931551bSRalph Campbell 		}
300f931551bSRalph Campbell 	}
301f931551bSRalph Campbell 	/*
302f931551bSRalph Campbell 	 * Judgement call: can we ignore the space between SendBuffs and
303f931551bSRalph Campbell 	 * UserRegs, where we would like to see vl15 buffs, but not more?
304f931551bSRalph Campbell 	 */
305f931551bSRalph Campbell 	if (offset >= snd_bottom && offset < snd_lim) {
306f931551bSRalph Campbell 		offset -= snd_bottom;
307f931551bSRalph Campbell 		map = (u32 __iomem *)dd->piobase + (offset / sizeof(u32));
308f931551bSRalph Campbell 		cnt = snd_lim - offset;
309f931551bSRalph Campbell 	}
310f931551bSRalph Campbell 
311fce24a9dSDave Olson 	if (!map && offs4k && dd->piovl15base) {
312fce24a9dSDave Olson 		snd_lim = offs4k + tot4k + 2 * dd->align4k;
313fce24a9dSDave Olson 		if (offset >= (offs4k + tot4k) && offset < snd_lim) {
314fce24a9dSDave Olson 			map = (u32 __iomem *)dd->piovl15base +
315fce24a9dSDave Olson 				((offset - (offs4k + tot4k)) / sizeof(u32));
316fce24a9dSDave Olson 			cnt = snd_lim - offset;
317fce24a9dSDave Olson 		}
318fce24a9dSDave Olson 	}
319fce24a9dSDave Olson 
320f931551bSRalph Campbell mapped:
321f931551bSRalph Campbell 	if (cntp)
322f931551bSRalph Campbell 		*cntp = cnt;
323f931551bSRalph Campbell 	return map;
324f931551bSRalph Campbell }
325f931551bSRalph Campbell 
326f931551bSRalph Campbell /*
327f931551bSRalph Campbell  * qib_read_umem64 - read a 64-bit quantity from the chip into user space
328f931551bSRalph Campbell  * @dd: the qlogic_ib device
329f931551bSRalph Campbell  * @uaddr: the location to store the data in user memory
330f931551bSRalph Campbell  * @regoffs: the offset from BAR0 (_NOT_ full pointer, anymore)
331f931551bSRalph Campbell  * @count: number of bytes to copy (multiple of 32 bits)
332f931551bSRalph Campbell  *
333f931551bSRalph Campbell  * This function also localizes all chip memory accesses.
334f931551bSRalph Campbell  * The copy should be written such that we read full cacheline packets
335f931551bSRalph Campbell  * from the chip.  This is usually used for a single qword
336f931551bSRalph Campbell  *
337f931551bSRalph Campbell  * NOTE:  This assumes the chip address is 64-bit aligned.
338f931551bSRalph Campbell  */
qib_read_umem64(struct qib_devdata * dd,void __user * uaddr,u32 regoffs,size_t count)339f931551bSRalph Campbell static int qib_read_umem64(struct qib_devdata *dd, void __user *uaddr,
340f931551bSRalph Campbell 			   u32 regoffs, size_t count)
341f931551bSRalph Campbell {
342f931551bSRalph Campbell 	const u64 __iomem *reg_addr;
343f931551bSRalph Campbell 	const u64 __iomem *reg_end;
344f931551bSRalph Campbell 	u32 limit;
345f931551bSRalph Campbell 	int ret;
346f931551bSRalph Campbell 
347f931551bSRalph Campbell 	reg_addr = (const u64 __iomem *)qib_remap_ioaddr32(dd, regoffs, &limit);
348f931551bSRalph Campbell 	if (reg_addr == NULL || limit == 0 || !(dd->flags & QIB_PRESENT)) {
349f931551bSRalph Campbell 		ret = -EINVAL;
350f931551bSRalph Campbell 		goto bail;
351f931551bSRalph Campbell 	}
352f931551bSRalph Campbell 	if (count >= limit)
353f931551bSRalph Campbell 		count = limit;
354f931551bSRalph Campbell 	reg_end = reg_addr + (count / sizeof(u64));
355f931551bSRalph Campbell 
356f931551bSRalph Campbell 	/* not very efficient, but it works for now */
357f931551bSRalph Campbell 	while (reg_addr < reg_end) {
358f931551bSRalph Campbell 		u64 data = readq(reg_addr);
359f931551bSRalph Campbell 
360f931551bSRalph Campbell 		if (copy_to_user(uaddr, &data, sizeof(u64))) {
361f931551bSRalph Campbell 			ret = -EFAULT;
362f931551bSRalph Campbell 			goto bail;
363f931551bSRalph Campbell 		}
364f931551bSRalph Campbell 		reg_addr++;
365f931551bSRalph Campbell 		uaddr += sizeof(u64);
366f931551bSRalph Campbell 	}
367f931551bSRalph Campbell 	ret = 0;
368f931551bSRalph Campbell bail:
369f931551bSRalph Campbell 	return ret;
370f931551bSRalph Campbell }
371f931551bSRalph Campbell 
372f931551bSRalph Campbell /*
373f931551bSRalph Campbell  * qib_write_umem64 - write a 64-bit quantity to the chip from user space
374f931551bSRalph Campbell  * @dd: the qlogic_ib device
375f931551bSRalph Campbell  * @regoffs: the offset from BAR0 (_NOT_ full pointer, anymore)
376f931551bSRalph Campbell  * @uaddr: the source of the data in user memory
377f931551bSRalph Campbell  * @count: the number of bytes to copy (multiple of 32 bits)
378f931551bSRalph Campbell  *
379f931551bSRalph Campbell  * This is usually used for a single qword
380f931551bSRalph Campbell  * NOTE:  This assumes the chip address is 64-bit aligned.
381f931551bSRalph Campbell  */
382f931551bSRalph Campbell 
qib_write_umem64(struct qib_devdata * dd,u32 regoffs,const void __user * uaddr,size_t count)383f931551bSRalph Campbell static int qib_write_umem64(struct qib_devdata *dd, u32 regoffs,
384f931551bSRalph Campbell 			    const void __user *uaddr, size_t count)
385f931551bSRalph Campbell {
386f931551bSRalph Campbell 	u64 __iomem *reg_addr;
387f931551bSRalph Campbell 	const u64 __iomem *reg_end;
388f931551bSRalph Campbell 	u32 limit;
389f931551bSRalph Campbell 	int ret;
390f931551bSRalph Campbell 
391f931551bSRalph Campbell 	reg_addr = (u64 __iomem *)qib_remap_ioaddr32(dd, regoffs, &limit);
392f931551bSRalph Campbell 	if (reg_addr == NULL || limit == 0 || !(dd->flags & QIB_PRESENT)) {
393f931551bSRalph Campbell 		ret = -EINVAL;
394f931551bSRalph Campbell 		goto bail;
395f931551bSRalph Campbell 	}
396f931551bSRalph Campbell 	if (count >= limit)
397f931551bSRalph Campbell 		count = limit;
398f931551bSRalph Campbell 	reg_end = reg_addr + (count / sizeof(u64));
399f931551bSRalph Campbell 
400f931551bSRalph Campbell 	/* not very efficient, but it works for now */
401f931551bSRalph Campbell 	while (reg_addr < reg_end) {
402f931551bSRalph Campbell 		u64 data;
403da12c1f6SMike Marciniszyn 
404f931551bSRalph Campbell 		if (copy_from_user(&data, uaddr, sizeof(data))) {
405f931551bSRalph Campbell 			ret = -EFAULT;
406f931551bSRalph Campbell 			goto bail;
407f931551bSRalph Campbell 		}
408f931551bSRalph Campbell 		writeq(data, reg_addr);
409f931551bSRalph Campbell 
410f931551bSRalph Campbell 		reg_addr++;
411f931551bSRalph Campbell 		uaddr += sizeof(u64);
412f931551bSRalph Campbell 	}
413f931551bSRalph Campbell 	ret = 0;
414f931551bSRalph Campbell bail:
415f931551bSRalph Campbell 	return ret;
416f931551bSRalph Campbell }
417f931551bSRalph Campbell 
418f931551bSRalph Campbell /*
419f931551bSRalph Campbell  * qib_read_umem32 - read a 32-bit quantity from the chip into user space
420f931551bSRalph Campbell  * @dd: the qlogic_ib device
421f931551bSRalph Campbell  * @uaddr: the location to store the data in user memory
422f931551bSRalph Campbell  * @regoffs: the offset from BAR0 (_NOT_ full pointer, anymore)
423f931551bSRalph Campbell  * @count: number of bytes to copy
424f931551bSRalph Campbell  *
425f931551bSRalph Campbell  * read 32 bit values, not 64 bit; for memories that only
426f931551bSRalph Campbell  * support 32 bit reads; usually a single dword.
427f931551bSRalph Campbell  */
qib_read_umem32(struct qib_devdata * dd,void __user * uaddr,u32 regoffs,size_t count)428f931551bSRalph Campbell static int qib_read_umem32(struct qib_devdata *dd, void __user *uaddr,
429f931551bSRalph Campbell 			   u32 regoffs, size_t count)
430f931551bSRalph Campbell {
431f931551bSRalph Campbell 	const u32 __iomem *reg_addr;
432f931551bSRalph Campbell 	const u32 __iomem *reg_end;
433f931551bSRalph Campbell 	u32 limit;
434f931551bSRalph Campbell 	int ret;
435f931551bSRalph Campbell 
436f931551bSRalph Campbell 	reg_addr = qib_remap_ioaddr32(dd, regoffs, &limit);
437f931551bSRalph Campbell 	if (reg_addr == NULL || limit == 0 || !(dd->flags & QIB_PRESENT)) {
438f931551bSRalph Campbell 		ret = -EINVAL;
439f931551bSRalph Campbell 		goto bail;
440f931551bSRalph Campbell 	}
441f931551bSRalph Campbell 	if (count >= limit)
442f931551bSRalph Campbell 		count = limit;
443f931551bSRalph Campbell 	reg_end = reg_addr + (count / sizeof(u32));
444f931551bSRalph Campbell 
445f931551bSRalph Campbell 	/* not very efficient, but it works for now */
446f931551bSRalph Campbell 	while (reg_addr < reg_end) {
447f931551bSRalph Campbell 		u32 data = readl(reg_addr);
448f931551bSRalph Campbell 
449f931551bSRalph Campbell 		if (copy_to_user(uaddr, &data, sizeof(data))) {
450f931551bSRalph Campbell 			ret = -EFAULT;
451f931551bSRalph Campbell 			goto bail;
452f931551bSRalph Campbell 		}
453f931551bSRalph Campbell 
454f931551bSRalph Campbell 		reg_addr++;
455f931551bSRalph Campbell 		uaddr += sizeof(u32);
456f931551bSRalph Campbell 
457f931551bSRalph Campbell 	}
458f931551bSRalph Campbell 	ret = 0;
459f931551bSRalph Campbell bail:
460f931551bSRalph Campbell 	return ret;
461f931551bSRalph Campbell }
462f931551bSRalph Campbell 
463f931551bSRalph Campbell /*
464f931551bSRalph Campbell  * qib_write_umem32 - write a 32-bit quantity to the chip from user space
465f931551bSRalph Campbell  * @dd: the qlogic_ib device
466f931551bSRalph Campbell  * @regoffs: the offset from BAR0 (_NOT_ full pointer, anymore)
467f931551bSRalph Campbell  * @uaddr: the source of the data in user memory
468f931551bSRalph Campbell  * @count: number of bytes to copy
469f931551bSRalph Campbell  *
470f931551bSRalph Campbell  * write 32 bit values, not 64 bit; for memories that only
471f931551bSRalph Campbell  * support 32 bit write; usually a single dword.
472f931551bSRalph Campbell  */
473f931551bSRalph Campbell 
qib_write_umem32(struct qib_devdata * dd,u32 regoffs,const void __user * uaddr,size_t count)474f931551bSRalph Campbell static int qib_write_umem32(struct qib_devdata *dd, u32 regoffs,
475f931551bSRalph Campbell 			    const void __user *uaddr, size_t count)
476f931551bSRalph Campbell {
477f931551bSRalph Campbell 	u32 __iomem *reg_addr;
478f931551bSRalph Campbell 	const u32 __iomem *reg_end;
479f931551bSRalph Campbell 	u32 limit;
480f931551bSRalph Campbell 	int ret;
481f931551bSRalph Campbell 
482f931551bSRalph Campbell 	reg_addr = qib_remap_ioaddr32(dd, regoffs, &limit);
483f931551bSRalph Campbell 	if (reg_addr == NULL || limit == 0 || !(dd->flags & QIB_PRESENT)) {
484f931551bSRalph Campbell 		ret = -EINVAL;
485f931551bSRalph Campbell 		goto bail;
486f931551bSRalph Campbell 	}
487f931551bSRalph Campbell 	if (count >= limit)
488f931551bSRalph Campbell 		count = limit;
489f931551bSRalph Campbell 	reg_end = reg_addr + (count / sizeof(u32));
490f931551bSRalph Campbell 
491f931551bSRalph Campbell 	while (reg_addr < reg_end) {
492f931551bSRalph Campbell 		u32 data;
493f931551bSRalph Campbell 
494f931551bSRalph Campbell 		if (copy_from_user(&data, uaddr, sizeof(data))) {
495f931551bSRalph Campbell 			ret = -EFAULT;
496f931551bSRalph Campbell 			goto bail;
497f931551bSRalph Campbell 		}
498f931551bSRalph Campbell 		writel(data, reg_addr);
499f931551bSRalph Campbell 
500f931551bSRalph Campbell 		reg_addr++;
501f931551bSRalph Campbell 		uaddr += sizeof(u32);
502f931551bSRalph Campbell 	}
503f931551bSRalph Campbell 	ret = 0;
504f931551bSRalph Campbell bail:
505f931551bSRalph Campbell 	return ret;
506f931551bSRalph Campbell }
507f931551bSRalph Campbell 
qib_diag_open(struct inode * in,struct file * fp)508f931551bSRalph Campbell static int qib_diag_open(struct inode *in, struct file *fp)
509f931551bSRalph Campbell {
510f931551bSRalph Campbell 	int unit = iminor(in) - QIB_DIAG_MINOR_BASE;
511f931551bSRalph Campbell 	struct qib_devdata *dd;
512f931551bSRalph Campbell 	struct qib_diag_client *dc;
513f931551bSRalph Campbell 	int ret;
514f931551bSRalph Campbell 
515f931551bSRalph Campbell 	mutex_lock(&qib_mutex);
516f931551bSRalph Campbell 
517f931551bSRalph Campbell 	dd = qib_lookup(unit);
518f931551bSRalph Campbell 
519f931551bSRalph Campbell 	if (dd == NULL || !(dd->flags & QIB_PRESENT) ||
520f931551bSRalph Campbell 	    !dd->kregbase) {
521f931551bSRalph Campbell 		ret = -ENODEV;
522f931551bSRalph Campbell 		goto bail;
523f931551bSRalph Campbell 	}
524f931551bSRalph Campbell 
525f931551bSRalph Campbell 	dc = get_client(dd);
526f931551bSRalph Campbell 	if (!dc) {
527f931551bSRalph Campbell 		ret = -ENOMEM;
528f931551bSRalph Campbell 		goto bail;
529f931551bSRalph Campbell 	}
530f931551bSRalph Campbell 	dc->next = dd->diag_client;
531f931551bSRalph Campbell 	dd->diag_client = dc;
532f931551bSRalph Campbell 	fp->private_data = dc;
533f931551bSRalph Campbell 	ret = 0;
534f931551bSRalph Campbell bail:
535f931551bSRalph Campbell 	mutex_unlock(&qib_mutex);
536f931551bSRalph Campbell 
537f931551bSRalph Campbell 	return ret;
538f931551bSRalph Campbell }
539f931551bSRalph Campbell 
540f931551bSRalph Campbell /**
541f931551bSRalph Campbell  * qib_diagpkt_write - write an IB packet
542f931551bSRalph Campbell  * @fp: the diag data device file pointer
543f931551bSRalph Campbell  * @data: qib_diag_pkt structure saying where to get the packet
544f931551bSRalph Campbell  * @count: size of data to write
545f931551bSRalph Campbell  * @off: unused by this code
546f931551bSRalph Campbell  */
qib_diagpkt_write(struct file * fp,const char __user * data,size_t count,loff_t * off)547f931551bSRalph Campbell static ssize_t qib_diagpkt_write(struct file *fp,
548f931551bSRalph Campbell 				 const char __user *data,
549f931551bSRalph Campbell 				 size_t count, loff_t *off)
550f931551bSRalph Campbell {
551f931551bSRalph Campbell 	u32 __iomem *piobuf;
5521c20c819SDennis Dalessandro 	u32 plen, pbufn, maxlen_reserve;
553f931551bSRalph Campbell 	struct qib_diag_xpkt dp;
554f931551bSRalph Campbell 	u32 *tmpbuf = NULL;
555f931551bSRalph Campbell 	struct qib_devdata *dd;
556f931551bSRalph Campbell 	struct qib_pportdata *ppd;
557f931551bSRalph Campbell 	ssize_t ret = 0;
558f931551bSRalph Campbell 
559f931551bSRalph Campbell 	if (count != sizeof(dp)) {
560f931551bSRalph Campbell 		ret = -EINVAL;
561f931551bSRalph Campbell 		goto bail;
562f931551bSRalph Campbell 	}
563f931551bSRalph Campbell 	if (copy_from_user(&dp, data, sizeof(dp))) {
564f931551bSRalph Campbell 		ret = -EFAULT;
565f931551bSRalph Campbell 		goto bail;
566f931551bSRalph Campbell 	}
567f931551bSRalph Campbell 
568f931551bSRalph Campbell 	dd = qib_lookup(dp.unit);
569f931551bSRalph Campbell 	if (!dd || !(dd->flags & QIB_PRESENT) || !dd->kregbase) {
570f931551bSRalph Campbell 		ret = -ENODEV;
571f931551bSRalph Campbell 		goto bail;
572f931551bSRalph Campbell 	}
573f931551bSRalph Campbell 	if (!(dd->flags & QIB_INITTED)) {
574f931551bSRalph Campbell 		/* no hardware, freeze, etc. */
575f931551bSRalph Campbell 		ret = -ENODEV;
576f931551bSRalph Campbell 		goto bail;
577f931551bSRalph Campbell 	}
578f931551bSRalph Campbell 
579f931551bSRalph Campbell 	if (dp.version != _DIAG_XPKT_VERS) {
580f931551bSRalph Campbell 		qib_dev_err(dd, "Invalid version %u for diagpkt_write\n",
581f931551bSRalph Campbell 			    dp.version);
582f931551bSRalph Campbell 		ret = -EINVAL;
583f931551bSRalph Campbell 		goto bail;
584f931551bSRalph Campbell 	}
585f931551bSRalph Campbell 	/* send count must be an exact number of dwords */
586f931551bSRalph Campbell 	if (dp.len & 3) {
587f931551bSRalph Campbell 		ret = -EINVAL;
588f931551bSRalph Campbell 		goto bail;
589f931551bSRalph Campbell 	}
590f931551bSRalph Campbell 	if (!dp.port || dp.port > dd->num_pports) {
591f931551bSRalph Campbell 		ret = -EINVAL;
592f931551bSRalph Campbell 		goto bail;
593f931551bSRalph Campbell 	}
594f931551bSRalph Campbell 	ppd = &dd->pport[dp.port - 1];
595f931551bSRalph Campbell 
5961c20c819SDennis Dalessandro 	/*
5971c20c819SDennis Dalessandro 	 * need total length before first word written, plus 2 Dwords. One Dword
5981c20c819SDennis Dalessandro 	 * is for padding so we get the full user data when not aligned on
5991c20c819SDennis Dalessandro 	 * a word boundary. The other Dword is to make sure we have room for the
6001c20c819SDennis Dalessandro 	 * ICRC which gets tacked on later.
6011c20c819SDennis Dalessandro 	 */
6021c20c819SDennis Dalessandro 	maxlen_reserve = 2 * sizeof(u32);
6031c20c819SDennis Dalessandro 	if (dp.len > ppd->ibmaxlen - maxlen_reserve) {
604f931551bSRalph Campbell 		ret = -EINVAL;
6051c20c819SDennis Dalessandro 		goto bail;
606f931551bSRalph Campbell 	}
6071c20c819SDennis Dalessandro 
6081c20c819SDennis Dalessandro 	plen = sizeof(u32) + dp.len;
6091c20c819SDennis Dalessandro 
610f931551bSRalph Campbell 	tmpbuf = vmalloc(plen);
611f931551bSRalph Campbell 	if (!tmpbuf) {
612f931551bSRalph Campbell 		ret = -ENOMEM;
613f931551bSRalph Campbell 		goto bail;
614f931551bSRalph Campbell 	}
615f931551bSRalph Campbell 
616f931551bSRalph Campbell 	if (copy_from_user(tmpbuf,
6176f57c933SJason Gunthorpe 			   u64_to_user_ptr(dp.data),
618f931551bSRalph Campbell 			   dp.len)) {
619f931551bSRalph Campbell 		ret = -EFAULT;
620f931551bSRalph Campbell 		goto bail;
621f931551bSRalph Campbell 	}
622f931551bSRalph Campbell 
623f931551bSRalph Campbell 	plen >>= 2;             /* in dwords */
624f931551bSRalph Campbell 
625f931551bSRalph Campbell 	if (dp.pbc_wd == 0)
626f931551bSRalph Campbell 		dp.pbc_wd = plen;
627f931551bSRalph Campbell 
628f931551bSRalph Campbell 	piobuf = dd->f_getsendbuf(ppd, dp.pbc_wd, &pbufn);
629f931551bSRalph Campbell 	if (!piobuf) {
630f931551bSRalph Campbell 		ret = -EBUSY;
631f931551bSRalph Campbell 		goto bail;
632f931551bSRalph Campbell 	}
633f931551bSRalph Campbell 	/* disarm it just to be extra sure */
634f931551bSRalph Campbell 	dd->f_sendctrl(dd->pport, QIB_SENDCTRL_DISARM_BUF(pbufn));
635f931551bSRalph Campbell 
636f931551bSRalph Campbell 	/* disable header check on pbufn for this packet */
637f931551bSRalph Campbell 	dd->f_txchk_change(dd, pbufn, 1, TXCHK_CHG_TYPE_DIS1, NULL);
638f931551bSRalph Campbell 
639f931551bSRalph Campbell 	writeq(dp.pbc_wd, piobuf);
640f931551bSRalph Campbell 	/*
641f931551bSRalph Campbell 	 * Copy all but the trigger word, then flush, so it's written
642f931551bSRalph Campbell 	 * to chip before trigger word, then write trigger word, then
643f931551bSRalph Campbell 	 * flush again, so packet is sent.
644f931551bSRalph Campbell 	 */
645f931551bSRalph Campbell 	if (dd->flags & QIB_PIO_FLUSH_WC) {
646f931551bSRalph Campbell 		qib_flush_wc();
6471c20c819SDennis Dalessandro 		qib_pio_copy(piobuf + 2, tmpbuf, plen - 1);
648f931551bSRalph Campbell 		qib_flush_wc();
6491c20c819SDennis Dalessandro 		__raw_writel(tmpbuf[plen - 1], piobuf + plen + 1);
650f931551bSRalph Campbell 	} else
6511c20c819SDennis Dalessandro 		qib_pio_copy(piobuf + 2, tmpbuf, plen);
652f931551bSRalph Campbell 
653f931551bSRalph Campbell 	if (dd->flags & QIB_USE_SPCL_TRIG) {
654f931551bSRalph Campbell 		u32 spcl_off = (pbufn >= dd->piobcnt2k) ? 2047 : 1023;
655f931551bSRalph Campbell 
656f931551bSRalph Campbell 		qib_flush_wc();
657f931551bSRalph Campbell 		__raw_writel(0xaebecede, piobuf + spcl_off);
658f931551bSRalph Campbell 	}
659f931551bSRalph Campbell 
660f931551bSRalph Campbell 	/*
661f931551bSRalph Campbell 	 * Ensure buffer is written to the chip, then re-enable
662f931551bSRalph Campbell 	 * header checks (if supported by chip).  The txchk
663f931551bSRalph Campbell 	 * code will ensure seen by chip before returning.
664f931551bSRalph Campbell 	 */
665f931551bSRalph Campbell 	qib_flush_wc();
666f931551bSRalph Campbell 	qib_sendbuf_done(dd, pbufn);
667f931551bSRalph Campbell 	dd->f_txchk_change(dd, pbufn, 1, TXCHK_CHG_TYPE_ENAB1, NULL);
668f931551bSRalph Campbell 
669f931551bSRalph Campbell 	ret = sizeof(dp);
670f931551bSRalph Campbell 
671f931551bSRalph Campbell bail:
672f931551bSRalph Campbell 	vfree(tmpbuf);
673f931551bSRalph Campbell 	return ret;
674f931551bSRalph Campbell }
675f931551bSRalph Campbell 
qib_diag_release(struct inode * in,struct file * fp)676f931551bSRalph Campbell static int qib_diag_release(struct inode *in, struct file *fp)
677f931551bSRalph Campbell {
678f931551bSRalph Campbell 	mutex_lock(&qib_mutex);
679f931551bSRalph Campbell 	return_client(fp->private_data);
680f931551bSRalph Campbell 	fp->private_data = NULL;
681f931551bSRalph Campbell 	mutex_unlock(&qib_mutex);
682f931551bSRalph Campbell 	return 0;
683f931551bSRalph Campbell }
684f931551bSRalph Campbell 
685f931551bSRalph Campbell /*
686f931551bSRalph Campbell  * Chip-specific code calls to register its interest in
687f931551bSRalph Campbell  * a specific range.
688f931551bSRalph Campbell  */
689f931551bSRalph Campbell struct diag_observer_list_elt {
690f931551bSRalph Campbell 	struct diag_observer_list_elt *next;
691f931551bSRalph Campbell 	const struct diag_observer *op;
692f931551bSRalph Campbell };
693f931551bSRalph Campbell 
qib_register_observer(struct qib_devdata * dd,const struct diag_observer * op)694f931551bSRalph Campbell int qib_register_observer(struct qib_devdata *dd,
695f931551bSRalph Campbell 			  const struct diag_observer *op)
696f931551bSRalph Campbell {
697f931551bSRalph Campbell 	struct diag_observer_list_elt *olp;
698186f8ba0SDan Carpenter 	unsigned long flags;
699f931551bSRalph Campbell 
700f931551bSRalph Campbell 	if (!dd || !op)
701186f8ba0SDan Carpenter 		return -EINVAL;
702041af0bbSMike Marciniszyn 	olp = vmalloc(sizeof(*olp));
703c40a83b9SLeon Romanovsky 	if (!olp)
704186f8ba0SDan Carpenter 		return -ENOMEM;
705f931551bSRalph Campbell 
706f931551bSRalph Campbell 	spin_lock_irqsave(&dd->qib_diag_trans_lock, flags);
707f931551bSRalph Campbell 	olp->op = op;
708f931551bSRalph Campbell 	olp->next = dd->diag_observer_list;
709f931551bSRalph Campbell 	dd->diag_observer_list = olp;
710f931551bSRalph Campbell 	spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags);
711186f8ba0SDan Carpenter 
712186f8ba0SDan Carpenter 	return 0;
713f931551bSRalph Campbell }
714f931551bSRalph Campbell 
715f931551bSRalph Campbell /* Remove all registered observers when device is closed */
qib_unregister_observers(struct qib_devdata * dd)716f931551bSRalph Campbell static void qib_unregister_observers(struct qib_devdata *dd)
717f931551bSRalph Campbell {
718f931551bSRalph Campbell 	struct diag_observer_list_elt *olp;
719f931551bSRalph Campbell 	unsigned long flags;
720f931551bSRalph Campbell 
721f931551bSRalph Campbell 	spin_lock_irqsave(&dd->qib_diag_trans_lock, flags);
722f931551bSRalph Campbell 	olp = dd->diag_observer_list;
723f931551bSRalph Campbell 	while (olp) {
724f931551bSRalph Campbell 		/* Pop one observer, let go of lock */
725f931551bSRalph Campbell 		dd->diag_observer_list = olp->next;
726f931551bSRalph Campbell 		spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags);
727f931551bSRalph Campbell 		vfree(olp);
728f931551bSRalph Campbell 		/* try again. */
729f931551bSRalph Campbell 		spin_lock_irqsave(&dd->qib_diag_trans_lock, flags);
730f931551bSRalph Campbell 		olp = dd->diag_observer_list;
731f931551bSRalph Campbell 	}
732f931551bSRalph Campbell 	spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags);
733f931551bSRalph Campbell }
734f931551bSRalph Campbell 
735f931551bSRalph Campbell /*
736f931551bSRalph Campbell  * Find the observer, if any, for the specified address. Initial implementation
737f931551bSRalph Campbell  * is simple stack of observers. This must be called with diag transaction
738f931551bSRalph Campbell  * lock held.
739f931551bSRalph Campbell  */
diag_get_observer(struct qib_devdata * dd,u32 addr)740f931551bSRalph Campbell static const struct diag_observer *diag_get_observer(struct qib_devdata *dd,
741f931551bSRalph Campbell 						     u32 addr)
742f931551bSRalph Campbell {
743f931551bSRalph Campbell 	struct diag_observer_list_elt *olp;
744f931551bSRalph Campbell 	const struct diag_observer *op = NULL;
745f931551bSRalph Campbell 
746f931551bSRalph Campbell 	olp = dd->diag_observer_list;
747f931551bSRalph Campbell 	while (olp) {
748f931551bSRalph Campbell 		op = olp->op;
749f931551bSRalph Campbell 		if (addr >= op->bottom && addr <= op->top)
750f931551bSRalph Campbell 			break;
751f931551bSRalph Campbell 		olp = olp->next;
752f931551bSRalph Campbell 	}
753f931551bSRalph Campbell 	if (!olp)
754f931551bSRalph Campbell 		op = NULL;
755f931551bSRalph Campbell 
756f931551bSRalph Campbell 	return op;
757f931551bSRalph Campbell }
758f931551bSRalph Campbell 
qib_diag_read(struct file * fp,char __user * data,size_t count,loff_t * off)759f931551bSRalph Campbell static ssize_t qib_diag_read(struct file *fp, char __user *data,
760f931551bSRalph Campbell 			     size_t count, loff_t *off)
761f931551bSRalph Campbell {
762f931551bSRalph Campbell 	struct qib_diag_client *dc = fp->private_data;
763f931551bSRalph Campbell 	struct qib_devdata *dd = dc->dd;
764f931551bSRalph Campbell 	ssize_t ret;
765f931551bSRalph Campbell 
766f931551bSRalph Campbell 	if (dc->pid != current->pid) {
767f931551bSRalph Campbell 		ret = -EPERM;
768f931551bSRalph Campbell 		goto bail;
769f931551bSRalph Campbell 	}
770f931551bSRalph Campbell 
771f931551bSRalph Campbell 	if (count == 0)
772f931551bSRalph Campbell 		ret = 0;
773f931551bSRalph Campbell 	else if ((count % 4) || (*off % 4))
774f931551bSRalph Campbell 		/* address or length is not 32-bit aligned, hence invalid */
775f931551bSRalph Campbell 		ret = -EINVAL;
776f931551bSRalph Campbell 	else if (dc->state < READY && (*off || count != 8))
777f931551bSRalph Campbell 		ret = -EINVAL;  /* prevent cat /dev/qib_diag* */
778f931551bSRalph Campbell 	else {
779f931551bSRalph Campbell 		unsigned long flags;
780f931551bSRalph Campbell 		u64 data64 = 0;
781f931551bSRalph Campbell 		int use_32;
782f931551bSRalph Campbell 		const struct diag_observer *op;
783f931551bSRalph Campbell 
784f931551bSRalph Campbell 		use_32 = (count % 8) || (*off % 8);
785f931551bSRalph Campbell 		ret = -1;
786f931551bSRalph Campbell 		spin_lock_irqsave(&dd->qib_diag_trans_lock, flags);
787f931551bSRalph Campbell 		/*
788f931551bSRalph Campbell 		 * Check for observer on this address range.
789f931551bSRalph Campbell 		 * we only support a single 32 or 64-bit read
790f931551bSRalph Campbell 		 * via observer, currently.
791f931551bSRalph Campbell 		 */
792f931551bSRalph Campbell 		op = diag_get_observer(dd, *off);
793f931551bSRalph Campbell 		if (op) {
794f931551bSRalph Campbell 			u32 offset = *off;
795da12c1f6SMike Marciniszyn 
796f931551bSRalph Campbell 			ret = op->hook(dd, op, offset, &data64, 0, use_32);
797f931551bSRalph Campbell 		}
798f931551bSRalph Campbell 		/*
799f931551bSRalph Campbell 		 * We need to release lock before any copy_to_user(),
800f931551bSRalph Campbell 		 * whether implicit in qib_read_umem* or explicit below.
801f931551bSRalph Campbell 		 */
802f931551bSRalph Campbell 		spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags);
803f931551bSRalph Campbell 		if (!op) {
804f931551bSRalph Campbell 			if (use_32)
805f931551bSRalph Campbell 				/*
806f931551bSRalph Campbell 				 * Address or length is not 64-bit aligned;
807f931551bSRalph Campbell 				 * do 32-bit rd
808f931551bSRalph Campbell 				 */
809f931551bSRalph Campbell 				ret = qib_read_umem32(dd, data, (u32) *off,
810f931551bSRalph Campbell 						      count);
811f931551bSRalph Campbell 			else
812f931551bSRalph Campbell 				ret = qib_read_umem64(dd, data, (u32) *off,
813f931551bSRalph Campbell 						      count);
814f931551bSRalph Campbell 		} else if (ret == count) {
815f931551bSRalph Campbell 			/* Below finishes case where observer existed */
816f931551bSRalph Campbell 			ret = copy_to_user(data, &data64, use_32 ?
817f931551bSRalph Campbell 					   sizeof(u32) : sizeof(u64));
818f931551bSRalph Campbell 			if (ret)
819f931551bSRalph Campbell 				ret = -EFAULT;
820f931551bSRalph Campbell 		}
821f931551bSRalph Campbell 	}
822f931551bSRalph Campbell 
823f931551bSRalph Campbell 	if (ret >= 0) {
824f931551bSRalph Campbell 		*off += count;
825f931551bSRalph Campbell 		ret = count;
826f931551bSRalph Campbell 		if (dc->state == OPENED)
827f931551bSRalph Campbell 			dc->state = INIT;
828f931551bSRalph Campbell 	}
829f931551bSRalph Campbell bail:
830f931551bSRalph Campbell 	return ret;
831f931551bSRalph Campbell }
832f931551bSRalph Campbell 
qib_diag_write(struct file * fp,const char __user * data,size_t count,loff_t * off)833f931551bSRalph Campbell static ssize_t qib_diag_write(struct file *fp, const char __user *data,
834f931551bSRalph Campbell 			      size_t count, loff_t *off)
835f931551bSRalph Campbell {
836f931551bSRalph Campbell 	struct qib_diag_client *dc = fp->private_data;
837f931551bSRalph Campbell 	struct qib_devdata *dd = dc->dd;
838f931551bSRalph Campbell 	ssize_t ret;
839f931551bSRalph Campbell 
840f931551bSRalph Campbell 	if (dc->pid != current->pid) {
841f931551bSRalph Campbell 		ret = -EPERM;
842f931551bSRalph Campbell 		goto bail;
843f931551bSRalph Campbell 	}
844f931551bSRalph Campbell 
845f931551bSRalph Campbell 	if (count == 0)
846f931551bSRalph Campbell 		ret = 0;
847f931551bSRalph Campbell 	else if ((count % 4) || (*off % 4))
848f931551bSRalph Campbell 		/* address or length is not 32-bit aligned, hence invalid */
849f931551bSRalph Campbell 		ret = -EINVAL;
850f931551bSRalph Campbell 	else if (dc->state < READY &&
851f931551bSRalph Campbell 		((*off || count != 8) || dc->state != INIT))
852f931551bSRalph Campbell 		/* No writes except second-step of init seq */
853f931551bSRalph Campbell 		ret = -EINVAL;  /* before any other write allowed */
854f931551bSRalph Campbell 	else {
855f931551bSRalph Campbell 		unsigned long flags;
856f931551bSRalph Campbell 		const struct diag_observer *op = NULL;
857f931551bSRalph Campbell 		int use_32 =  (count % 8) || (*off % 8);
858f931551bSRalph Campbell 
859f931551bSRalph Campbell 		/*
860f931551bSRalph Campbell 		 * Check for observer on this address range.
861f931551bSRalph Campbell 		 * We only support a single 32 or 64-bit write
862f931551bSRalph Campbell 		 * via observer, currently. This helps, because
863f931551bSRalph Campbell 		 * we would otherwise have to jump through hoops
864f931551bSRalph Campbell 		 * to make "diag transaction" meaningful when we
865f931551bSRalph Campbell 		 * cannot do a copy_from_user while holding the lock.
866f931551bSRalph Campbell 		 */
867f931551bSRalph Campbell 		if (count == 4 || count == 8) {
868f931551bSRalph Campbell 			u64 data64;
869f931551bSRalph Campbell 			u32 offset = *off;
870da12c1f6SMike Marciniszyn 
871f931551bSRalph Campbell 			ret = copy_from_user(&data64, data, count);
872f931551bSRalph Campbell 			if (ret) {
873f931551bSRalph Campbell 				ret = -EFAULT;
874f931551bSRalph Campbell 				goto bail;
875f931551bSRalph Campbell 			}
876f931551bSRalph Campbell 			spin_lock_irqsave(&dd->qib_diag_trans_lock, flags);
877f931551bSRalph Campbell 			op = diag_get_observer(dd, *off);
878f931551bSRalph Campbell 			if (op)
879f931551bSRalph Campbell 				ret = op->hook(dd, op, offset, &data64, ~0Ull,
880f931551bSRalph Campbell 					       use_32);
881f931551bSRalph Campbell 			spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags);
882f931551bSRalph Campbell 		}
883f931551bSRalph Campbell 
884f931551bSRalph Campbell 		if (!op) {
885f931551bSRalph Campbell 			if (use_32)
886f931551bSRalph Campbell 				/*
887f931551bSRalph Campbell 				 * Address or length is not 64-bit aligned;
888f931551bSRalph Campbell 				 * do 32-bit write
889f931551bSRalph Campbell 				 */
890f931551bSRalph Campbell 				ret = qib_write_umem32(dd, (u32) *off, data,
891f931551bSRalph Campbell 						       count);
892f931551bSRalph Campbell 			else
893f931551bSRalph Campbell 				ret = qib_write_umem64(dd, (u32) *off, data,
894f931551bSRalph Campbell 						       count);
895f931551bSRalph Campbell 		}
896f931551bSRalph Campbell 	}
897f931551bSRalph Campbell 
898f931551bSRalph Campbell 	if (ret >= 0) {
899f931551bSRalph Campbell 		*off += count;
900f931551bSRalph Campbell 		ret = count;
901f931551bSRalph Campbell 		if (dc->state == INIT)
902f931551bSRalph Campbell 			dc->state = READY; /* all read/write OK now */
903f931551bSRalph Campbell 	}
904f931551bSRalph Campbell bail:
905f931551bSRalph Campbell 	return ret;
906f931551bSRalph Campbell }
907