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