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,
617*6f57c933SJason 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