18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2012 Intel Corporation. All rights reserved. 38c2ecf20Sopenharmony_ci * Copyright (c) 2006 - 2012 QLogic Corporation. All rights reserved. 48c2ecf20Sopenharmony_ci * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two 78c2ecf20Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 88c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 98c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the 108c2ecf20Sopenharmony_ci * OpenIB.org BSD license below: 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or 138c2ecf20Sopenharmony_ci * without modification, are permitted provided that the following 148c2ecf20Sopenharmony_ci * conditions are met: 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above 178c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 188c2ecf20Sopenharmony_ci * disclaimer. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above 218c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 228c2ecf20Sopenharmony_ci * disclaimer in the documentation and/or other materials 238c2ecf20Sopenharmony_ci * provided with the distribution. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 268c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 278c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 288c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 298c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 308c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 318c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 328c2ecf20Sopenharmony_ci * SOFTWARE. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * This file contains support for diagnostic functions. It is accessed by 378c2ecf20Sopenharmony_ci * opening the qib_diag device, normally minor number 129. Diagnostic use 388c2ecf20Sopenharmony_ci * of the QLogic_IB chip may render the chip or board unusable until the 398c2ecf20Sopenharmony_ci * driver is unloaded, or in some cases, until the system is rebooted. 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * Accesses to the chip through this interface are not similar to going 428c2ecf20Sopenharmony_ci * through the /sys/bus/pci resource mmap interface. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#include <linux/io.h> 468c2ecf20Sopenharmony_ci#include <linux/pci.h> 478c2ecf20Sopenharmony_ci#include <linux/poll.h> 488c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 498c2ecf20Sopenharmony_ci#include <linux/export.h> 508c2ecf20Sopenharmony_ci#include <linux/fs.h> 518c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#include "qib.h" 548c2ecf20Sopenharmony_ci#include "qib_common.h" 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#undef pr_fmt 578c2ecf20Sopenharmony_ci#define pr_fmt(fmt) QIB_DRV_NAME ": " fmt 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* 608c2ecf20Sopenharmony_ci * Each client that opens the diag device must read then write 618c2ecf20Sopenharmony_ci * offset 0, to prevent lossage from random cat or od. diag_state 628c2ecf20Sopenharmony_ci * sequences this "handshake". 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_cienum diag_state { UNUSED = 0, OPENED, INIT, READY }; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* State for an individual client. PID so children cannot abuse handshake */ 678c2ecf20Sopenharmony_cistatic struct qib_diag_client { 688c2ecf20Sopenharmony_ci struct qib_diag_client *next; 698c2ecf20Sopenharmony_ci struct qib_devdata *dd; 708c2ecf20Sopenharmony_ci pid_t pid; 718c2ecf20Sopenharmony_ci enum diag_state state; 728c2ecf20Sopenharmony_ci} *client_pool; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* 758c2ecf20Sopenharmony_ci * Get a client struct. Recycled if possible, else kmalloc. 768c2ecf20Sopenharmony_ci * Must be called with qib_mutex held 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_cistatic struct qib_diag_client *get_client(struct qib_devdata *dd) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct qib_diag_client *dc; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci dc = client_pool; 838c2ecf20Sopenharmony_ci if (dc) 848c2ecf20Sopenharmony_ci /* got from pool remove it and use */ 858c2ecf20Sopenharmony_ci client_pool = dc->next; 868c2ecf20Sopenharmony_ci else 878c2ecf20Sopenharmony_ci /* None in pool, alloc and init */ 888c2ecf20Sopenharmony_ci dc = kmalloc(sizeof(*dc), GFP_KERNEL); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (dc) { 918c2ecf20Sopenharmony_ci dc->next = NULL; 928c2ecf20Sopenharmony_ci dc->dd = dd; 938c2ecf20Sopenharmony_ci dc->pid = current->pid; 948c2ecf20Sopenharmony_ci dc->state = OPENED; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci return dc; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* 1008c2ecf20Sopenharmony_ci * Return to pool. Must be called with qib_mutex held 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_cistatic void return_client(struct qib_diag_client *dc) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct qib_devdata *dd = dc->dd; 1058c2ecf20Sopenharmony_ci struct qib_diag_client *tdc, *rdc; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci rdc = NULL; 1088c2ecf20Sopenharmony_ci if (dc == dd->diag_client) { 1098c2ecf20Sopenharmony_ci dd->diag_client = dc->next; 1108c2ecf20Sopenharmony_ci rdc = dc; 1118c2ecf20Sopenharmony_ci } else { 1128c2ecf20Sopenharmony_ci tdc = dc->dd->diag_client; 1138c2ecf20Sopenharmony_ci while (tdc) { 1148c2ecf20Sopenharmony_ci if (dc == tdc->next) { 1158c2ecf20Sopenharmony_ci tdc->next = dc->next; 1168c2ecf20Sopenharmony_ci rdc = dc; 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci tdc = tdc->next; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci if (rdc) { 1238c2ecf20Sopenharmony_ci rdc->state = UNUSED; 1248c2ecf20Sopenharmony_ci rdc->dd = NULL; 1258c2ecf20Sopenharmony_ci rdc->pid = 0; 1268c2ecf20Sopenharmony_ci rdc->next = client_pool; 1278c2ecf20Sopenharmony_ci client_pool = rdc; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int qib_diag_open(struct inode *in, struct file *fp); 1328c2ecf20Sopenharmony_cistatic int qib_diag_release(struct inode *in, struct file *fp); 1338c2ecf20Sopenharmony_cistatic ssize_t qib_diag_read(struct file *fp, char __user *data, 1348c2ecf20Sopenharmony_ci size_t count, loff_t *off); 1358c2ecf20Sopenharmony_cistatic ssize_t qib_diag_write(struct file *fp, const char __user *data, 1368c2ecf20Sopenharmony_ci size_t count, loff_t *off); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic const struct file_operations diag_file_ops = { 1398c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1408c2ecf20Sopenharmony_ci .write = qib_diag_write, 1418c2ecf20Sopenharmony_ci .read = qib_diag_read, 1428c2ecf20Sopenharmony_ci .open = qib_diag_open, 1438c2ecf20Sopenharmony_ci .release = qib_diag_release, 1448c2ecf20Sopenharmony_ci .llseek = default_llseek, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic atomic_t diagpkt_count = ATOMIC_INIT(0); 1488c2ecf20Sopenharmony_cistatic struct cdev *diagpkt_cdev; 1498c2ecf20Sopenharmony_cistatic struct device *diagpkt_device; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic ssize_t qib_diagpkt_write(struct file *fp, const char __user *data, 1528c2ecf20Sopenharmony_ci size_t count, loff_t *off); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic const struct file_operations diagpkt_file_ops = { 1558c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1568c2ecf20Sopenharmony_ci .write = qib_diagpkt_write, 1578c2ecf20Sopenharmony_ci .llseek = noop_llseek, 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ciint qib_diag_add(struct qib_devdata *dd) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci char name[16]; 1638c2ecf20Sopenharmony_ci int ret = 0; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (atomic_inc_return(&diagpkt_count) == 1) { 1668c2ecf20Sopenharmony_ci ret = qib_cdev_init(QIB_DIAGPKT_MINOR, "ipath_diagpkt", 1678c2ecf20Sopenharmony_ci &diagpkt_file_ops, &diagpkt_cdev, 1688c2ecf20Sopenharmony_ci &diagpkt_device); 1698c2ecf20Sopenharmony_ci if (ret) 1708c2ecf20Sopenharmony_ci goto done; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "ipath_diag%d", dd->unit); 1748c2ecf20Sopenharmony_ci ret = qib_cdev_init(QIB_DIAG_MINOR_BASE + dd->unit, name, 1758c2ecf20Sopenharmony_ci &diag_file_ops, &dd->diag_cdev, 1768c2ecf20Sopenharmony_ci &dd->diag_device); 1778c2ecf20Sopenharmony_cidone: 1788c2ecf20Sopenharmony_ci return ret; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void qib_unregister_observers(struct qib_devdata *dd); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_civoid qib_diag_remove(struct qib_devdata *dd) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct qib_diag_client *dc; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&diagpkt_count)) 1888c2ecf20Sopenharmony_ci qib_cdev_cleanup(&diagpkt_cdev, &diagpkt_device); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci qib_cdev_cleanup(&dd->diag_cdev, &dd->diag_device); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* 1938c2ecf20Sopenharmony_ci * Return all diag_clients of this device. There should be none, 1948c2ecf20Sopenharmony_ci * as we are "guaranteed" that no clients are still open 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci while (dd->diag_client) 1978c2ecf20Sopenharmony_ci return_client(dd->diag_client); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* Now clean up all unused client structs */ 2008c2ecf20Sopenharmony_ci while (client_pool) { 2018c2ecf20Sopenharmony_ci dc = client_pool; 2028c2ecf20Sopenharmony_ci client_pool = dc->next; 2038c2ecf20Sopenharmony_ci kfree(dc); 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci /* Clean up observer list */ 2068c2ecf20Sopenharmony_ci qib_unregister_observers(dd); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/* qib_remap_ioaddr32 - remap an offset into chip address space to __iomem * 2108c2ecf20Sopenharmony_ci * 2118c2ecf20Sopenharmony_ci * @dd: the qlogic_ib device 2128c2ecf20Sopenharmony_ci * @offs: the offset in chip-space 2138c2ecf20Sopenharmony_ci * @cntp: Pointer to max (byte) count for transfer starting at offset 2148c2ecf20Sopenharmony_ci * This returns a u32 __iomem * so it can be used for both 64 and 32-bit 2158c2ecf20Sopenharmony_ci * mapping. It is needed because with the use of PAT for control of 2168c2ecf20Sopenharmony_ci * write-combining, the logically contiguous address-space of the chip 2178c2ecf20Sopenharmony_ci * may be split into virtually non-contiguous spaces, with different 2188c2ecf20Sopenharmony_ci * attributes, which are them mapped to contiguous physical space 2198c2ecf20Sopenharmony_ci * based from the first BAR. 2208c2ecf20Sopenharmony_ci * 2218c2ecf20Sopenharmony_ci * The code below makes the same assumptions as were made in 2228c2ecf20Sopenharmony_ci * init_chip_wc_pat() (qib_init.c), copied here: 2238c2ecf20Sopenharmony_ci * Assumes chip address space looks like: 2248c2ecf20Sopenharmony_ci * - kregs + sregs + cregs + uregs (in any order) 2258c2ecf20Sopenharmony_ci * - piobufs (2K and 4K bufs in either order) 2268c2ecf20Sopenharmony_ci * or: 2278c2ecf20Sopenharmony_ci * - kregs + sregs + cregs (in any order) 2288c2ecf20Sopenharmony_ci * - piobufs (2K and 4K bufs in either order) 2298c2ecf20Sopenharmony_ci * - uregs 2308c2ecf20Sopenharmony_ci * 2318c2ecf20Sopenharmony_ci * If cntp is non-NULL, returns how many bytes from offset can be accessed 2328c2ecf20Sopenharmony_ci * Returns 0 if the offset is not mapped. 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_cistatic u32 __iomem *qib_remap_ioaddr32(struct qib_devdata *dd, u32 offset, 2358c2ecf20Sopenharmony_ci u32 *cntp) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci u32 kreglen; 2388c2ecf20Sopenharmony_ci u32 snd_bottom, snd_lim = 0; 2398c2ecf20Sopenharmony_ci u32 __iomem *krb32 = (u32 __iomem *)dd->kregbase; 2408c2ecf20Sopenharmony_ci u32 __iomem *map = NULL; 2418c2ecf20Sopenharmony_ci u32 cnt = 0; 2428c2ecf20Sopenharmony_ci u32 tot4k, offs4k; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* First, simplest case, offset is within the first map. */ 2458c2ecf20Sopenharmony_ci kreglen = (dd->kregend - dd->kregbase) * sizeof(u64); 2468c2ecf20Sopenharmony_ci if (offset < kreglen) { 2478c2ecf20Sopenharmony_ci map = krb32 + (offset / sizeof(u32)); 2488c2ecf20Sopenharmony_ci cnt = kreglen - offset; 2498c2ecf20Sopenharmony_ci goto mapped; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* 2538c2ecf20Sopenharmony_ci * Next check for user regs, the next most common case, 2548c2ecf20Sopenharmony_ci * and a cheap check because if they are not in the first map 2558c2ecf20Sopenharmony_ci * they are last in chip. 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_ci if (dd->userbase) { 2588c2ecf20Sopenharmony_ci /* If user regs mapped, they are after send, so set limit. */ 2598c2ecf20Sopenharmony_ci u32 ulim = (dd->cfgctxts * dd->ureg_align) + dd->uregbase; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (!dd->piovl15base) 2628c2ecf20Sopenharmony_ci snd_lim = dd->uregbase; 2638c2ecf20Sopenharmony_ci krb32 = (u32 __iomem *)dd->userbase; 2648c2ecf20Sopenharmony_ci if (offset >= dd->uregbase && offset < ulim) { 2658c2ecf20Sopenharmony_ci map = krb32 + (offset - dd->uregbase) / sizeof(u32); 2668c2ecf20Sopenharmony_ci cnt = ulim - offset; 2678c2ecf20Sopenharmony_ci goto mapped; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* 2728c2ecf20Sopenharmony_ci * Lastly, check for offset within Send Buffers. 2738c2ecf20Sopenharmony_ci * This is gnarly because struct devdata is deliberately vague 2748c2ecf20Sopenharmony_ci * about things like 7322 VL15 buffers, and we are not in 2758c2ecf20Sopenharmony_ci * chip-specific code here, so should not make many assumptions. 2768c2ecf20Sopenharmony_ci * The one we _do_ make is that the only chip that has more sndbufs 2778c2ecf20Sopenharmony_ci * than we admit is the 7322, and it has userregs above that, so 2788c2ecf20Sopenharmony_ci * we know the snd_lim. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ci /* Assume 2K buffers are first. */ 2818c2ecf20Sopenharmony_ci snd_bottom = dd->pio2k_bufbase; 2828c2ecf20Sopenharmony_ci if (snd_lim == 0) { 2838c2ecf20Sopenharmony_ci u32 tot2k = dd->piobcnt2k * ALIGN(dd->piosize2k, dd->palign); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci snd_lim = snd_bottom + tot2k; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci /* If 4k buffers exist, account for them by bumping 2888c2ecf20Sopenharmony_ci * appropriate limit. 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_ci tot4k = dd->piobcnt4k * dd->align4k; 2918c2ecf20Sopenharmony_ci offs4k = dd->piobufbase >> 32; 2928c2ecf20Sopenharmony_ci if (dd->piobcnt4k) { 2938c2ecf20Sopenharmony_ci if (snd_bottom > offs4k) 2948c2ecf20Sopenharmony_ci snd_bottom = offs4k; 2958c2ecf20Sopenharmony_ci else { 2968c2ecf20Sopenharmony_ci /* 4k above 2k. Bump snd_lim, if needed*/ 2978c2ecf20Sopenharmony_ci if (!dd->userbase || dd->piovl15base) 2988c2ecf20Sopenharmony_ci snd_lim = offs4k + tot4k; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci /* 3028c2ecf20Sopenharmony_ci * Judgement call: can we ignore the space between SendBuffs and 3038c2ecf20Sopenharmony_ci * UserRegs, where we would like to see vl15 buffs, but not more? 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ci if (offset >= snd_bottom && offset < snd_lim) { 3068c2ecf20Sopenharmony_ci offset -= snd_bottom; 3078c2ecf20Sopenharmony_ci map = (u32 __iomem *)dd->piobase + (offset / sizeof(u32)); 3088c2ecf20Sopenharmony_ci cnt = snd_lim - offset; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (!map && offs4k && dd->piovl15base) { 3128c2ecf20Sopenharmony_ci snd_lim = offs4k + tot4k + 2 * dd->align4k; 3138c2ecf20Sopenharmony_ci if (offset >= (offs4k + tot4k) && offset < snd_lim) { 3148c2ecf20Sopenharmony_ci map = (u32 __iomem *)dd->piovl15base + 3158c2ecf20Sopenharmony_ci ((offset - (offs4k + tot4k)) / sizeof(u32)); 3168c2ecf20Sopenharmony_ci cnt = snd_lim - offset; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cimapped: 3218c2ecf20Sopenharmony_ci if (cntp) 3228c2ecf20Sopenharmony_ci *cntp = cnt; 3238c2ecf20Sopenharmony_ci return map; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci/* 3278c2ecf20Sopenharmony_ci * qib_read_umem64 - read a 64-bit quantity from the chip into user space 3288c2ecf20Sopenharmony_ci * @dd: the qlogic_ib device 3298c2ecf20Sopenharmony_ci * @uaddr: the location to store the data in user memory 3308c2ecf20Sopenharmony_ci * @regoffs: the offset from BAR0 (_NOT_ full pointer, anymore) 3318c2ecf20Sopenharmony_ci * @count: number of bytes to copy (multiple of 32 bits) 3328c2ecf20Sopenharmony_ci * 3338c2ecf20Sopenharmony_ci * This function also localizes all chip memory accesses. 3348c2ecf20Sopenharmony_ci * The copy should be written such that we read full cacheline packets 3358c2ecf20Sopenharmony_ci * from the chip. This is usually used for a single qword 3368c2ecf20Sopenharmony_ci * 3378c2ecf20Sopenharmony_ci * NOTE: This assumes the chip address is 64-bit aligned. 3388c2ecf20Sopenharmony_ci */ 3398c2ecf20Sopenharmony_cistatic int qib_read_umem64(struct qib_devdata *dd, void __user *uaddr, 3408c2ecf20Sopenharmony_ci u32 regoffs, size_t count) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci const u64 __iomem *reg_addr; 3438c2ecf20Sopenharmony_ci const u64 __iomem *reg_end; 3448c2ecf20Sopenharmony_ci u32 limit; 3458c2ecf20Sopenharmony_ci int ret; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci reg_addr = (const u64 __iomem *)qib_remap_ioaddr32(dd, regoffs, &limit); 3488c2ecf20Sopenharmony_ci if (reg_addr == NULL || limit == 0 || !(dd->flags & QIB_PRESENT)) { 3498c2ecf20Sopenharmony_ci ret = -EINVAL; 3508c2ecf20Sopenharmony_ci goto bail; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci if (count >= limit) 3538c2ecf20Sopenharmony_ci count = limit; 3548c2ecf20Sopenharmony_ci reg_end = reg_addr + (count / sizeof(u64)); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* not very efficient, but it works for now */ 3578c2ecf20Sopenharmony_ci while (reg_addr < reg_end) { 3588c2ecf20Sopenharmony_ci u64 data = readq(reg_addr); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (copy_to_user(uaddr, &data, sizeof(u64))) { 3618c2ecf20Sopenharmony_ci ret = -EFAULT; 3628c2ecf20Sopenharmony_ci goto bail; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci reg_addr++; 3658c2ecf20Sopenharmony_ci uaddr += sizeof(u64); 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci ret = 0; 3688c2ecf20Sopenharmony_cibail: 3698c2ecf20Sopenharmony_ci return ret; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci/* 3738c2ecf20Sopenharmony_ci * qib_write_umem64 - write a 64-bit quantity to the chip from user space 3748c2ecf20Sopenharmony_ci * @dd: the qlogic_ib device 3758c2ecf20Sopenharmony_ci * @regoffs: the offset from BAR0 (_NOT_ full pointer, anymore) 3768c2ecf20Sopenharmony_ci * @uaddr: the source of the data in user memory 3778c2ecf20Sopenharmony_ci * @count: the number of bytes to copy (multiple of 32 bits) 3788c2ecf20Sopenharmony_ci * 3798c2ecf20Sopenharmony_ci * This is usually used for a single qword 3808c2ecf20Sopenharmony_ci * NOTE: This assumes the chip address is 64-bit aligned. 3818c2ecf20Sopenharmony_ci */ 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int qib_write_umem64(struct qib_devdata *dd, u32 regoffs, 3848c2ecf20Sopenharmony_ci const void __user *uaddr, size_t count) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci u64 __iomem *reg_addr; 3878c2ecf20Sopenharmony_ci const u64 __iomem *reg_end; 3888c2ecf20Sopenharmony_ci u32 limit; 3898c2ecf20Sopenharmony_ci int ret; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci reg_addr = (u64 __iomem *)qib_remap_ioaddr32(dd, regoffs, &limit); 3928c2ecf20Sopenharmony_ci if (reg_addr == NULL || limit == 0 || !(dd->flags & QIB_PRESENT)) { 3938c2ecf20Sopenharmony_ci ret = -EINVAL; 3948c2ecf20Sopenharmony_ci goto bail; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci if (count >= limit) 3978c2ecf20Sopenharmony_ci count = limit; 3988c2ecf20Sopenharmony_ci reg_end = reg_addr + (count / sizeof(u64)); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* not very efficient, but it works for now */ 4018c2ecf20Sopenharmony_ci while (reg_addr < reg_end) { 4028c2ecf20Sopenharmony_ci u64 data; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (copy_from_user(&data, uaddr, sizeof(data))) { 4058c2ecf20Sopenharmony_ci ret = -EFAULT; 4068c2ecf20Sopenharmony_ci goto bail; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci writeq(data, reg_addr); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci reg_addr++; 4118c2ecf20Sopenharmony_ci uaddr += sizeof(u64); 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci ret = 0; 4148c2ecf20Sopenharmony_cibail: 4158c2ecf20Sopenharmony_ci return ret; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci/* 4198c2ecf20Sopenharmony_ci * qib_read_umem32 - read a 32-bit quantity from the chip into user space 4208c2ecf20Sopenharmony_ci * @dd: the qlogic_ib device 4218c2ecf20Sopenharmony_ci * @uaddr: the location to store the data in user memory 4228c2ecf20Sopenharmony_ci * @regoffs: the offset from BAR0 (_NOT_ full pointer, anymore) 4238c2ecf20Sopenharmony_ci * @count: number of bytes to copy 4248c2ecf20Sopenharmony_ci * 4258c2ecf20Sopenharmony_ci * read 32 bit values, not 64 bit; for memories that only 4268c2ecf20Sopenharmony_ci * support 32 bit reads; usually a single dword. 4278c2ecf20Sopenharmony_ci */ 4288c2ecf20Sopenharmony_cistatic int qib_read_umem32(struct qib_devdata *dd, void __user *uaddr, 4298c2ecf20Sopenharmony_ci u32 regoffs, size_t count) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci const u32 __iomem *reg_addr; 4328c2ecf20Sopenharmony_ci const u32 __iomem *reg_end; 4338c2ecf20Sopenharmony_ci u32 limit; 4348c2ecf20Sopenharmony_ci int ret; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci reg_addr = qib_remap_ioaddr32(dd, regoffs, &limit); 4378c2ecf20Sopenharmony_ci if (reg_addr == NULL || limit == 0 || !(dd->flags & QIB_PRESENT)) { 4388c2ecf20Sopenharmony_ci ret = -EINVAL; 4398c2ecf20Sopenharmony_ci goto bail; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci if (count >= limit) 4428c2ecf20Sopenharmony_ci count = limit; 4438c2ecf20Sopenharmony_ci reg_end = reg_addr + (count / sizeof(u32)); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* not very efficient, but it works for now */ 4468c2ecf20Sopenharmony_ci while (reg_addr < reg_end) { 4478c2ecf20Sopenharmony_ci u32 data = readl(reg_addr); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (copy_to_user(uaddr, &data, sizeof(data))) { 4508c2ecf20Sopenharmony_ci ret = -EFAULT; 4518c2ecf20Sopenharmony_ci goto bail; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci reg_addr++; 4558c2ecf20Sopenharmony_ci uaddr += sizeof(u32); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci ret = 0; 4598c2ecf20Sopenharmony_cibail: 4608c2ecf20Sopenharmony_ci return ret; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci/* 4648c2ecf20Sopenharmony_ci * qib_write_umem32 - write a 32-bit quantity to the chip from user space 4658c2ecf20Sopenharmony_ci * @dd: the qlogic_ib device 4668c2ecf20Sopenharmony_ci * @regoffs: the offset from BAR0 (_NOT_ full pointer, anymore) 4678c2ecf20Sopenharmony_ci * @uaddr: the source of the data in user memory 4688c2ecf20Sopenharmony_ci * @count: number of bytes to copy 4698c2ecf20Sopenharmony_ci * 4708c2ecf20Sopenharmony_ci * write 32 bit values, not 64 bit; for memories that only 4718c2ecf20Sopenharmony_ci * support 32 bit write; usually a single dword. 4728c2ecf20Sopenharmony_ci */ 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic int qib_write_umem32(struct qib_devdata *dd, u32 regoffs, 4758c2ecf20Sopenharmony_ci const void __user *uaddr, size_t count) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci u32 __iomem *reg_addr; 4788c2ecf20Sopenharmony_ci const u32 __iomem *reg_end; 4798c2ecf20Sopenharmony_ci u32 limit; 4808c2ecf20Sopenharmony_ci int ret; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci reg_addr = qib_remap_ioaddr32(dd, regoffs, &limit); 4838c2ecf20Sopenharmony_ci if (reg_addr == NULL || limit == 0 || !(dd->flags & QIB_PRESENT)) { 4848c2ecf20Sopenharmony_ci ret = -EINVAL; 4858c2ecf20Sopenharmony_ci goto bail; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci if (count >= limit) 4888c2ecf20Sopenharmony_ci count = limit; 4898c2ecf20Sopenharmony_ci reg_end = reg_addr + (count / sizeof(u32)); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci while (reg_addr < reg_end) { 4928c2ecf20Sopenharmony_ci u32 data; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (copy_from_user(&data, uaddr, sizeof(data))) { 4958c2ecf20Sopenharmony_ci ret = -EFAULT; 4968c2ecf20Sopenharmony_ci goto bail; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci writel(data, reg_addr); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci reg_addr++; 5018c2ecf20Sopenharmony_ci uaddr += sizeof(u32); 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci ret = 0; 5048c2ecf20Sopenharmony_cibail: 5058c2ecf20Sopenharmony_ci return ret; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic int qib_diag_open(struct inode *in, struct file *fp) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci int unit = iminor(in) - QIB_DIAG_MINOR_BASE; 5118c2ecf20Sopenharmony_ci struct qib_devdata *dd; 5128c2ecf20Sopenharmony_ci struct qib_diag_client *dc; 5138c2ecf20Sopenharmony_ci int ret; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci mutex_lock(&qib_mutex); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci dd = qib_lookup(unit); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci if (dd == NULL || !(dd->flags & QIB_PRESENT) || 5208c2ecf20Sopenharmony_ci !dd->kregbase) { 5218c2ecf20Sopenharmony_ci ret = -ENODEV; 5228c2ecf20Sopenharmony_ci goto bail; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci dc = get_client(dd); 5268c2ecf20Sopenharmony_ci if (!dc) { 5278c2ecf20Sopenharmony_ci ret = -ENOMEM; 5288c2ecf20Sopenharmony_ci goto bail; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci dc->next = dd->diag_client; 5318c2ecf20Sopenharmony_ci dd->diag_client = dc; 5328c2ecf20Sopenharmony_ci fp->private_data = dc; 5338c2ecf20Sopenharmony_ci ret = 0; 5348c2ecf20Sopenharmony_cibail: 5358c2ecf20Sopenharmony_ci mutex_unlock(&qib_mutex); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci return ret; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci/** 5418c2ecf20Sopenharmony_ci * qib_diagpkt_write - write an IB packet 5428c2ecf20Sopenharmony_ci * @fp: the diag data device file pointer 5438c2ecf20Sopenharmony_ci * @data: qib_diag_pkt structure saying where to get the packet 5448c2ecf20Sopenharmony_ci * @count: size of data to write 5458c2ecf20Sopenharmony_ci * @off: unused by this code 5468c2ecf20Sopenharmony_ci */ 5478c2ecf20Sopenharmony_cistatic ssize_t qib_diagpkt_write(struct file *fp, 5488c2ecf20Sopenharmony_ci const char __user *data, 5498c2ecf20Sopenharmony_ci size_t count, loff_t *off) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci u32 __iomem *piobuf; 5528c2ecf20Sopenharmony_ci u32 plen, pbufn, maxlen_reserve; 5538c2ecf20Sopenharmony_ci struct qib_diag_xpkt dp; 5548c2ecf20Sopenharmony_ci u32 *tmpbuf = NULL; 5558c2ecf20Sopenharmony_ci struct qib_devdata *dd; 5568c2ecf20Sopenharmony_ci struct qib_pportdata *ppd; 5578c2ecf20Sopenharmony_ci ssize_t ret = 0; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (count != sizeof(dp)) { 5608c2ecf20Sopenharmony_ci ret = -EINVAL; 5618c2ecf20Sopenharmony_ci goto bail; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci if (copy_from_user(&dp, data, sizeof(dp))) { 5648c2ecf20Sopenharmony_ci ret = -EFAULT; 5658c2ecf20Sopenharmony_ci goto bail; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci dd = qib_lookup(dp.unit); 5698c2ecf20Sopenharmony_ci if (!dd || !(dd->flags & QIB_PRESENT) || !dd->kregbase) { 5708c2ecf20Sopenharmony_ci ret = -ENODEV; 5718c2ecf20Sopenharmony_ci goto bail; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci if (!(dd->flags & QIB_INITTED)) { 5748c2ecf20Sopenharmony_ci /* no hardware, freeze, etc. */ 5758c2ecf20Sopenharmony_ci ret = -ENODEV; 5768c2ecf20Sopenharmony_ci goto bail; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (dp.version != _DIAG_XPKT_VERS) { 5808c2ecf20Sopenharmony_ci qib_dev_err(dd, "Invalid version %u for diagpkt_write\n", 5818c2ecf20Sopenharmony_ci dp.version); 5828c2ecf20Sopenharmony_ci ret = -EINVAL; 5838c2ecf20Sopenharmony_ci goto bail; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci /* send count must be an exact number of dwords */ 5868c2ecf20Sopenharmony_ci if (dp.len & 3) { 5878c2ecf20Sopenharmony_ci ret = -EINVAL; 5888c2ecf20Sopenharmony_ci goto bail; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci if (!dp.port || dp.port > dd->num_pports) { 5918c2ecf20Sopenharmony_ci ret = -EINVAL; 5928c2ecf20Sopenharmony_ci goto bail; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci ppd = &dd->pport[dp.port - 1]; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* 5978c2ecf20Sopenharmony_ci * need total length before first word written, plus 2 Dwords. One Dword 5988c2ecf20Sopenharmony_ci * is for padding so we get the full user data when not aligned on 5998c2ecf20Sopenharmony_ci * a word boundary. The other Dword is to make sure we have room for the 6008c2ecf20Sopenharmony_ci * ICRC which gets tacked on later. 6018c2ecf20Sopenharmony_ci */ 6028c2ecf20Sopenharmony_ci maxlen_reserve = 2 * sizeof(u32); 6038c2ecf20Sopenharmony_ci if (dp.len > ppd->ibmaxlen - maxlen_reserve) { 6048c2ecf20Sopenharmony_ci ret = -EINVAL; 6058c2ecf20Sopenharmony_ci goto bail; 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci plen = sizeof(u32) + dp.len; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci tmpbuf = vmalloc(plen); 6118c2ecf20Sopenharmony_ci if (!tmpbuf) { 6128c2ecf20Sopenharmony_ci ret = -ENOMEM; 6138c2ecf20Sopenharmony_ci goto bail; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (copy_from_user(tmpbuf, 6178c2ecf20Sopenharmony_ci u64_to_user_ptr(dp.data), 6188c2ecf20Sopenharmony_ci dp.len)) { 6198c2ecf20Sopenharmony_ci ret = -EFAULT; 6208c2ecf20Sopenharmony_ci goto bail; 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci plen >>= 2; /* in dwords */ 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci if (dp.pbc_wd == 0) 6268c2ecf20Sopenharmony_ci dp.pbc_wd = plen; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci piobuf = dd->f_getsendbuf(ppd, dp.pbc_wd, &pbufn); 6298c2ecf20Sopenharmony_ci if (!piobuf) { 6308c2ecf20Sopenharmony_ci ret = -EBUSY; 6318c2ecf20Sopenharmony_ci goto bail; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci /* disarm it just to be extra sure */ 6348c2ecf20Sopenharmony_ci dd->f_sendctrl(dd->pport, QIB_SENDCTRL_DISARM_BUF(pbufn)); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* disable header check on pbufn for this packet */ 6378c2ecf20Sopenharmony_ci dd->f_txchk_change(dd, pbufn, 1, TXCHK_CHG_TYPE_DIS1, NULL); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci writeq(dp.pbc_wd, piobuf); 6408c2ecf20Sopenharmony_ci /* 6418c2ecf20Sopenharmony_ci * Copy all but the trigger word, then flush, so it's written 6428c2ecf20Sopenharmony_ci * to chip before trigger word, then write trigger word, then 6438c2ecf20Sopenharmony_ci * flush again, so packet is sent. 6448c2ecf20Sopenharmony_ci */ 6458c2ecf20Sopenharmony_ci if (dd->flags & QIB_PIO_FLUSH_WC) { 6468c2ecf20Sopenharmony_ci qib_flush_wc(); 6478c2ecf20Sopenharmony_ci qib_pio_copy(piobuf + 2, tmpbuf, plen - 1); 6488c2ecf20Sopenharmony_ci qib_flush_wc(); 6498c2ecf20Sopenharmony_ci __raw_writel(tmpbuf[plen - 1], piobuf + plen + 1); 6508c2ecf20Sopenharmony_ci } else 6518c2ecf20Sopenharmony_ci qib_pio_copy(piobuf + 2, tmpbuf, plen); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (dd->flags & QIB_USE_SPCL_TRIG) { 6548c2ecf20Sopenharmony_ci u32 spcl_off = (pbufn >= dd->piobcnt2k) ? 2047 : 1023; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci qib_flush_wc(); 6578c2ecf20Sopenharmony_ci __raw_writel(0xaebecede, piobuf + spcl_off); 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* 6618c2ecf20Sopenharmony_ci * Ensure buffer is written to the chip, then re-enable 6628c2ecf20Sopenharmony_ci * header checks (if supported by chip). The txchk 6638c2ecf20Sopenharmony_ci * code will ensure seen by chip before returning. 6648c2ecf20Sopenharmony_ci */ 6658c2ecf20Sopenharmony_ci qib_flush_wc(); 6668c2ecf20Sopenharmony_ci qib_sendbuf_done(dd, pbufn); 6678c2ecf20Sopenharmony_ci dd->f_txchk_change(dd, pbufn, 1, TXCHK_CHG_TYPE_ENAB1, NULL); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci ret = sizeof(dp); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cibail: 6728c2ecf20Sopenharmony_ci vfree(tmpbuf); 6738c2ecf20Sopenharmony_ci return ret; 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic int qib_diag_release(struct inode *in, struct file *fp) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci mutex_lock(&qib_mutex); 6798c2ecf20Sopenharmony_ci return_client(fp->private_data); 6808c2ecf20Sopenharmony_ci fp->private_data = NULL; 6818c2ecf20Sopenharmony_ci mutex_unlock(&qib_mutex); 6828c2ecf20Sopenharmony_ci return 0; 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci/* 6868c2ecf20Sopenharmony_ci * Chip-specific code calls to register its interest in 6878c2ecf20Sopenharmony_ci * a specific range. 6888c2ecf20Sopenharmony_ci */ 6898c2ecf20Sopenharmony_cistruct diag_observer_list_elt { 6908c2ecf20Sopenharmony_ci struct diag_observer_list_elt *next; 6918c2ecf20Sopenharmony_ci const struct diag_observer *op; 6928c2ecf20Sopenharmony_ci}; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ciint qib_register_observer(struct qib_devdata *dd, 6958c2ecf20Sopenharmony_ci const struct diag_observer *op) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci struct diag_observer_list_elt *olp; 6988c2ecf20Sopenharmony_ci unsigned long flags; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (!dd || !op) 7018c2ecf20Sopenharmony_ci return -EINVAL; 7028c2ecf20Sopenharmony_ci olp = vmalloc(sizeof(*olp)); 7038c2ecf20Sopenharmony_ci if (!olp) 7048c2ecf20Sopenharmony_ci return -ENOMEM; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci spin_lock_irqsave(&dd->qib_diag_trans_lock, flags); 7078c2ecf20Sopenharmony_ci olp->op = op; 7088c2ecf20Sopenharmony_ci olp->next = dd->diag_observer_list; 7098c2ecf20Sopenharmony_ci dd->diag_observer_list = olp; 7108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci return 0; 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci/* Remove all registered observers when device is closed */ 7168c2ecf20Sopenharmony_cistatic void qib_unregister_observers(struct qib_devdata *dd) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci struct diag_observer_list_elt *olp; 7198c2ecf20Sopenharmony_ci unsigned long flags; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci spin_lock_irqsave(&dd->qib_diag_trans_lock, flags); 7228c2ecf20Sopenharmony_ci olp = dd->diag_observer_list; 7238c2ecf20Sopenharmony_ci while (olp) { 7248c2ecf20Sopenharmony_ci /* Pop one observer, let go of lock */ 7258c2ecf20Sopenharmony_ci dd->diag_observer_list = olp->next; 7268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags); 7278c2ecf20Sopenharmony_ci vfree(olp); 7288c2ecf20Sopenharmony_ci /* try again. */ 7298c2ecf20Sopenharmony_ci spin_lock_irqsave(&dd->qib_diag_trans_lock, flags); 7308c2ecf20Sopenharmony_ci olp = dd->diag_observer_list; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags); 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci/* 7368c2ecf20Sopenharmony_ci * Find the observer, if any, for the specified address. Initial implementation 7378c2ecf20Sopenharmony_ci * is simple stack of observers. This must be called with diag transaction 7388c2ecf20Sopenharmony_ci * lock held. 7398c2ecf20Sopenharmony_ci */ 7408c2ecf20Sopenharmony_cistatic const struct diag_observer *diag_get_observer(struct qib_devdata *dd, 7418c2ecf20Sopenharmony_ci u32 addr) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci struct diag_observer_list_elt *olp; 7448c2ecf20Sopenharmony_ci const struct diag_observer *op = NULL; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci olp = dd->diag_observer_list; 7478c2ecf20Sopenharmony_ci while (olp) { 7488c2ecf20Sopenharmony_ci op = olp->op; 7498c2ecf20Sopenharmony_ci if (addr >= op->bottom && addr <= op->top) 7508c2ecf20Sopenharmony_ci break; 7518c2ecf20Sopenharmony_ci olp = olp->next; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci if (!olp) 7548c2ecf20Sopenharmony_ci op = NULL; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci return op; 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic ssize_t qib_diag_read(struct file *fp, char __user *data, 7608c2ecf20Sopenharmony_ci size_t count, loff_t *off) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci struct qib_diag_client *dc = fp->private_data; 7638c2ecf20Sopenharmony_ci struct qib_devdata *dd = dc->dd; 7648c2ecf20Sopenharmony_ci ssize_t ret; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (dc->pid != current->pid) { 7678c2ecf20Sopenharmony_ci ret = -EPERM; 7688c2ecf20Sopenharmony_ci goto bail; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci if (count == 0) 7728c2ecf20Sopenharmony_ci ret = 0; 7738c2ecf20Sopenharmony_ci else if ((count % 4) || (*off % 4)) 7748c2ecf20Sopenharmony_ci /* address or length is not 32-bit aligned, hence invalid */ 7758c2ecf20Sopenharmony_ci ret = -EINVAL; 7768c2ecf20Sopenharmony_ci else if (dc->state < READY && (*off || count != 8)) 7778c2ecf20Sopenharmony_ci ret = -EINVAL; /* prevent cat /dev/qib_diag* */ 7788c2ecf20Sopenharmony_ci else { 7798c2ecf20Sopenharmony_ci unsigned long flags; 7808c2ecf20Sopenharmony_ci u64 data64 = 0; 7818c2ecf20Sopenharmony_ci int use_32; 7828c2ecf20Sopenharmony_ci const struct diag_observer *op; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci use_32 = (count % 8) || (*off % 8); 7858c2ecf20Sopenharmony_ci ret = -1; 7868c2ecf20Sopenharmony_ci spin_lock_irqsave(&dd->qib_diag_trans_lock, flags); 7878c2ecf20Sopenharmony_ci /* 7888c2ecf20Sopenharmony_ci * Check for observer on this address range. 7898c2ecf20Sopenharmony_ci * we only support a single 32 or 64-bit read 7908c2ecf20Sopenharmony_ci * via observer, currently. 7918c2ecf20Sopenharmony_ci */ 7928c2ecf20Sopenharmony_ci op = diag_get_observer(dd, *off); 7938c2ecf20Sopenharmony_ci if (op) { 7948c2ecf20Sopenharmony_ci u32 offset = *off; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci ret = op->hook(dd, op, offset, &data64, 0, use_32); 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci /* 7998c2ecf20Sopenharmony_ci * We need to release lock before any copy_to_user(), 8008c2ecf20Sopenharmony_ci * whether implicit in qib_read_umem* or explicit below. 8018c2ecf20Sopenharmony_ci */ 8028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags); 8038c2ecf20Sopenharmony_ci if (!op) { 8048c2ecf20Sopenharmony_ci if (use_32) 8058c2ecf20Sopenharmony_ci /* 8068c2ecf20Sopenharmony_ci * Address or length is not 64-bit aligned; 8078c2ecf20Sopenharmony_ci * do 32-bit rd 8088c2ecf20Sopenharmony_ci */ 8098c2ecf20Sopenharmony_ci ret = qib_read_umem32(dd, data, (u32) *off, 8108c2ecf20Sopenharmony_ci count); 8118c2ecf20Sopenharmony_ci else 8128c2ecf20Sopenharmony_ci ret = qib_read_umem64(dd, data, (u32) *off, 8138c2ecf20Sopenharmony_ci count); 8148c2ecf20Sopenharmony_ci } else if (ret == count) { 8158c2ecf20Sopenharmony_ci /* Below finishes case where observer existed */ 8168c2ecf20Sopenharmony_ci ret = copy_to_user(data, &data64, use_32 ? 8178c2ecf20Sopenharmony_ci sizeof(u32) : sizeof(u64)); 8188c2ecf20Sopenharmony_ci if (ret) 8198c2ecf20Sopenharmony_ci ret = -EFAULT; 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (ret >= 0) { 8248c2ecf20Sopenharmony_ci *off += count; 8258c2ecf20Sopenharmony_ci ret = count; 8268c2ecf20Sopenharmony_ci if (dc->state == OPENED) 8278c2ecf20Sopenharmony_ci dc->state = INIT; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_cibail: 8308c2ecf20Sopenharmony_ci return ret; 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic ssize_t qib_diag_write(struct file *fp, const char __user *data, 8348c2ecf20Sopenharmony_ci size_t count, loff_t *off) 8358c2ecf20Sopenharmony_ci{ 8368c2ecf20Sopenharmony_ci struct qib_diag_client *dc = fp->private_data; 8378c2ecf20Sopenharmony_ci struct qib_devdata *dd = dc->dd; 8388c2ecf20Sopenharmony_ci ssize_t ret; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci if (dc->pid != current->pid) { 8418c2ecf20Sopenharmony_ci ret = -EPERM; 8428c2ecf20Sopenharmony_ci goto bail; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci if (count == 0) 8468c2ecf20Sopenharmony_ci ret = 0; 8478c2ecf20Sopenharmony_ci else if ((count % 4) || (*off % 4)) 8488c2ecf20Sopenharmony_ci /* address or length is not 32-bit aligned, hence invalid */ 8498c2ecf20Sopenharmony_ci ret = -EINVAL; 8508c2ecf20Sopenharmony_ci else if (dc->state < READY && 8518c2ecf20Sopenharmony_ci ((*off || count != 8) || dc->state != INIT)) 8528c2ecf20Sopenharmony_ci /* No writes except second-step of init seq */ 8538c2ecf20Sopenharmony_ci ret = -EINVAL; /* before any other write allowed */ 8548c2ecf20Sopenharmony_ci else { 8558c2ecf20Sopenharmony_ci unsigned long flags; 8568c2ecf20Sopenharmony_ci const struct diag_observer *op = NULL; 8578c2ecf20Sopenharmony_ci int use_32 = (count % 8) || (*off % 8); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci /* 8608c2ecf20Sopenharmony_ci * Check for observer on this address range. 8618c2ecf20Sopenharmony_ci * We only support a single 32 or 64-bit write 8628c2ecf20Sopenharmony_ci * via observer, currently. This helps, because 8638c2ecf20Sopenharmony_ci * we would otherwise have to jump through hoops 8648c2ecf20Sopenharmony_ci * to make "diag transaction" meaningful when we 8658c2ecf20Sopenharmony_ci * cannot do a copy_from_user while holding the lock. 8668c2ecf20Sopenharmony_ci */ 8678c2ecf20Sopenharmony_ci if (count == 4 || count == 8) { 8688c2ecf20Sopenharmony_ci u64 data64; 8698c2ecf20Sopenharmony_ci u32 offset = *off; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci ret = copy_from_user(&data64, data, count); 8728c2ecf20Sopenharmony_ci if (ret) { 8738c2ecf20Sopenharmony_ci ret = -EFAULT; 8748c2ecf20Sopenharmony_ci goto bail; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci spin_lock_irqsave(&dd->qib_diag_trans_lock, flags); 8778c2ecf20Sopenharmony_ci op = diag_get_observer(dd, *off); 8788c2ecf20Sopenharmony_ci if (op) 8798c2ecf20Sopenharmony_ci ret = op->hook(dd, op, offset, &data64, ~0Ull, 8808c2ecf20Sopenharmony_ci use_32); 8818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags); 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci if (!op) { 8858c2ecf20Sopenharmony_ci if (use_32) 8868c2ecf20Sopenharmony_ci /* 8878c2ecf20Sopenharmony_ci * Address or length is not 64-bit aligned; 8888c2ecf20Sopenharmony_ci * do 32-bit write 8898c2ecf20Sopenharmony_ci */ 8908c2ecf20Sopenharmony_ci ret = qib_write_umem32(dd, (u32) *off, data, 8918c2ecf20Sopenharmony_ci count); 8928c2ecf20Sopenharmony_ci else 8938c2ecf20Sopenharmony_ci ret = qib_write_umem64(dd, (u32) *off, data, 8948c2ecf20Sopenharmony_ci count); 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci if (ret >= 0) { 8998c2ecf20Sopenharmony_ci *off += count; 9008c2ecf20Sopenharmony_ci ret = count; 9018c2ecf20Sopenharmony_ci if (dc->state == INIT) 9028c2ecf20Sopenharmony_ci dc->state = READY; /* all read/write OK now */ 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_cibail: 9058c2ecf20Sopenharmony_ci return ret; 9068c2ecf20Sopenharmony_ci} 907