| /* | |
| * Copyright (c) 2006 QLogic, Inc. All rights reserved. | |
| * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. | |
| * | |
| * This software is available to you under a choice of one of two | |
| * licenses. You may choose to be licensed under the terms of the GNU | |
| * General Public License (GPL) Version 2, available from the file | |
| * COPYING in the main directory of this source tree, or the | |
| * OpenIB.org BSD license below: | |
| * | |
| * Redistribution and use in source and binary forms, with or | |
| * without modification, are permitted provided that the following | |
| * conditions are met: | |
| * | |
| * - Redistributions of source code must retain the above | |
| * copyright notice, this list of conditions and the following | |
| * disclaimer. | |
| * | |
| * - Redistributions in binary form must reproduce the above | |
| * copyright notice, this list of conditions and the following | |
| * disclaimer in the documentation and/or other materials | |
| * provided with the distribution. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| * SOFTWARE. | |
| */ | |
| /* | |
| * These are the routines used by layered drivers, currently just the | |
| * layered ethernet driver and verbs layer. | |
| */ | |
| #include <linux/io.h> | |
| #include <linux/pci.h> | |
| #include <asm/byteorder.h> | |
| #include "ipath_kernel.h" | |
| #include "ipath_layer.h" | |
| #include "ipath_verbs.h" | |
| #include "ipath_common.h" | |
| /* Acquire before ipath_devs_lock. */ | |
| static DEFINE_MUTEX(ipath_layer_mutex); | |
| u16 ipath_layer_rcv_opcode; | |
| static int (*layer_intr)(void *, u32); | |
| static int (*layer_rcv)(void *, void *, struct sk_buff *); | |
| static int (*layer_rcv_lid)(void *, void *); | |
| static void *(*layer_add_one)(int, struct ipath_devdata *); | |
| static void (*layer_remove_one)(void *); | |
| int __ipath_layer_intr(struct ipath_devdata *dd, u32 arg) | |
| { | |
| int ret = -ENODEV; | |
| if (dd->ipath_layer.l_arg && layer_intr) | |
| ret = layer_intr(dd->ipath_layer.l_arg, arg); | |
| return ret; | |
| } | |
| int ipath_layer_intr(struct ipath_devdata *dd, u32 arg) | |
| { | |
| int ret; | |
| mutex_lock(&ipath_layer_mutex); | |
| ret = __ipath_layer_intr(dd, arg); | |
| mutex_unlock(&ipath_layer_mutex); | |
| return ret; | |
| } | |
| int __ipath_layer_rcv(struct ipath_devdata *dd, void *hdr, | |
| struct sk_buff *skb) | |
| { | |
| int ret = -ENODEV; | |
| if (dd->ipath_layer.l_arg && layer_rcv) | |
| ret = layer_rcv(dd->ipath_layer.l_arg, hdr, skb); | |
| return ret; | |
| } | |
| int __ipath_layer_rcv_lid(struct ipath_devdata *dd, void *hdr) | |
| { | |
| int ret = -ENODEV; | |
| if (dd->ipath_layer.l_arg && layer_rcv_lid) | |
| ret = layer_rcv_lid(dd->ipath_layer.l_arg, hdr); | |
| return ret; | |
| } | |
| void ipath_layer_lid_changed(struct ipath_devdata *dd) | |
| { | |
| mutex_lock(&ipath_layer_mutex); | |
| if (dd->ipath_layer.l_arg && layer_intr) | |
| layer_intr(dd->ipath_layer.l_arg, IPATH_LAYER_INT_LID); | |
| mutex_unlock(&ipath_layer_mutex); | |
| } | |
| void ipath_layer_add(struct ipath_devdata *dd) | |
| { | |
| mutex_lock(&ipath_layer_mutex); | |
| if (layer_add_one) | |
| dd->ipath_layer.l_arg = | |
| layer_add_one(dd->ipath_unit, dd); | |
| mutex_unlock(&ipath_layer_mutex); | |
| } | |
| void ipath_layer_remove(struct ipath_devdata *dd) | |
| { | |
| mutex_lock(&ipath_layer_mutex); | |
| if (dd->ipath_layer.l_arg && layer_remove_one) { | |
| layer_remove_one(dd->ipath_layer.l_arg); | |
| dd->ipath_layer.l_arg = NULL; | |
| } | |
| mutex_unlock(&ipath_layer_mutex); | |
| } | |
| int ipath_layer_register(void *(*l_add)(int, struct ipath_devdata *), | |
| void (*l_remove)(void *), | |
| int (*l_intr)(void *, u32), | |
| int (*l_rcv)(void *, void *, struct sk_buff *), | |
| u16 l_rcv_opcode, | |
| int (*l_rcv_lid)(void *, void *)) | |
| { | |
| struct ipath_devdata *dd, *tmp; | |
| unsigned long flags; | |
| mutex_lock(&ipath_layer_mutex); | |
| layer_add_one = l_add; | |
| layer_remove_one = l_remove; | |
| layer_intr = l_intr; | |
| layer_rcv = l_rcv; | |
| layer_rcv_lid = l_rcv_lid; | |
| ipath_layer_rcv_opcode = l_rcv_opcode; | |
| spin_lock_irqsave(&ipath_devs_lock, flags); | |
| list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) { | |
| if (!(dd->ipath_flags & IPATH_INITTED)) | |
| continue; | |
| if (dd->ipath_layer.l_arg) | |
| continue; | |
| spin_unlock_irqrestore(&ipath_devs_lock, flags); | |
| dd->ipath_layer.l_arg = l_add(dd->ipath_unit, dd); | |
| spin_lock_irqsave(&ipath_devs_lock, flags); | |
| } | |
| spin_unlock_irqrestore(&ipath_devs_lock, flags); | |
| mutex_unlock(&ipath_layer_mutex); | |
| return 0; | |
| } | |
| EXPORT_SYMBOL_GPL(ipath_layer_register); | |
| void ipath_layer_unregister(void) | |
| { | |
| struct ipath_devdata *dd, *tmp; | |
| unsigned long flags; | |
| mutex_lock(&ipath_layer_mutex); | |
| spin_lock_irqsave(&ipath_devs_lock, flags); | |
| list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) { | |
| if (dd->ipath_layer.l_arg && layer_remove_one) { | |
| spin_unlock_irqrestore(&ipath_devs_lock, flags); | |
| layer_remove_one(dd->ipath_layer.l_arg); | |
| spin_lock_irqsave(&ipath_devs_lock, flags); | |
| dd->ipath_layer.l_arg = NULL; | |
| } | |
| } | |
| spin_unlock_irqrestore(&ipath_devs_lock, flags); | |
| layer_add_one = NULL; | |
| layer_remove_one = NULL; | |
| layer_intr = NULL; | |
| layer_rcv = NULL; | |
| layer_rcv_lid = NULL; | |
| mutex_unlock(&ipath_layer_mutex); | |
| } | |
| EXPORT_SYMBOL_GPL(ipath_layer_unregister); | |
| int ipath_layer_open(struct ipath_devdata *dd, u32 * pktmax) | |
| { | |
| int ret; | |
| u32 intval = 0; | |
| mutex_lock(&ipath_layer_mutex); | |
| if (!dd->ipath_layer.l_arg) { | |
| ret = -EINVAL; | |
| goto bail; | |
| } | |
| ret = ipath_setrcvhdrsize(dd, IPATH_HEADER_QUEUE_WORDS); | |
| if (ret < 0) | |
| goto bail; | |
| *pktmax = dd->ipath_ibmaxlen; | |
| if (*dd->ipath_statusp & IPATH_STATUS_IB_READY) | |
| intval |= IPATH_LAYER_INT_IF_UP; | |
| if (dd->ipath_lid) | |
| intval |= IPATH_LAYER_INT_LID; | |
| if (dd->ipath_mlid) | |
| intval |= IPATH_LAYER_INT_BCAST; | |
| /* | |
| * do this on open, in case low level is already up and | |
| * just layered driver was reloaded, etc. | |
| */ | |
| if (intval) | |
| layer_intr(dd->ipath_layer.l_arg, intval); | |
| ret = 0; | |
| bail: | |
| mutex_unlock(&ipath_layer_mutex); | |
| return ret; | |
| } | |
| EXPORT_SYMBOL_GPL(ipath_layer_open); | |
| u16 ipath_layer_get_lid(struct ipath_devdata *dd) | |
| { | |
| return dd->ipath_lid; | |
| } | |
| EXPORT_SYMBOL_GPL(ipath_layer_get_lid); | |
| /** | |
| * ipath_layer_get_mac - get the MAC address | |
| * @dd: the infinipath device | |
| * @mac: the MAC is put here | |
| * | |
| * This is the EUID-64 OUI octets (top 3), then | |
| * skip the next 2 (which should both be zero or 0xff). | |
| * The returned MAC is in network order | |
| * mac points to at least 6 bytes of buffer | |
| * We assume that by the time the LID is set, that the GUID is as valid | |
| * as it's ever going to be, rather than adding yet another status bit. | |
| */ | |
| int ipath_layer_get_mac(struct ipath_devdata *dd, u8 * mac) | |
| { | |
| u8 *guid; | |
| guid = (u8 *) &dd->ipath_guid; | |
| mac[0] = guid[0]; | |
| mac[1] = guid[1]; | |
| mac[2] = guid[2]; | |
| mac[3] = guid[5]; | |
| mac[4] = guid[6]; | |
| mac[5] = guid[7]; | |
| if ((guid[3] || guid[4]) && !(guid[3] == 0xff && guid[4] == 0xff)) | |
| ipath_dbg("Warning, guid bytes 3 and 4 not 0 or 0xffff: " | |
| "%x %x\n", guid[3], guid[4]); | |
| return 0; | |
| } | |
| EXPORT_SYMBOL_GPL(ipath_layer_get_mac); | |
| u16 ipath_layer_get_bcast(struct ipath_devdata *dd) | |
| { | |
| return dd->ipath_mlid; | |
| } | |
| EXPORT_SYMBOL_GPL(ipath_layer_get_bcast); | |
| int ipath_layer_send_hdr(struct ipath_devdata *dd, struct ether_header *hdr) | |
| { | |
| int ret = 0; | |
| u32 __iomem *piobuf; | |
| u32 plen, *uhdr; | |
| size_t count; | |
| __be16 vlsllnh; | |
| if (!(dd->ipath_flags & IPATH_RCVHDRSZ_SET)) { | |
| ipath_dbg("send while not open\n"); | |
| ret = -EINVAL; | |
| } else | |
| if ((dd->ipath_flags & (IPATH_LINKUNK | IPATH_LINKDOWN)) || | |
| dd->ipath_lid == 0) { | |
| /* | |
| * lid check is for when sma hasn't yet configured | |
| */ | |
| ret = -ENETDOWN; | |
| ipath_cdbg(VERBOSE, "send while not ready, " | |
| "mylid=%u, flags=0x%x\n", | |
| dd->ipath_lid, dd->ipath_flags); | |
| } | |
| vlsllnh = *((__be16 *) hdr); | |
| if (vlsllnh != htons(IPATH_LRH_BTH)) { | |
| ipath_dbg("Warning: lrh[0] wrong (%x, not %x); " | |
| "not sending\n", be16_to_cpu(vlsllnh), | |
| IPATH_LRH_BTH); | |
| ret = -EINVAL; | |
| } | |
| if (ret) | |
| goto done; | |
| /* Get a PIO buffer to use. */ | |
| piobuf = ipath_getpiobuf(dd, NULL); | |
| if (piobuf == NULL) { | |
| ret = -EBUSY; | |
| goto done; | |
| } | |
| plen = (sizeof(*hdr) >> 2); /* actual length */ | |
| ipath_cdbg(EPKT, "0x%x+1w pio %p\n", plen, piobuf); | |
| writeq(plen+1, piobuf); /* len (+1 for pad) to pbc, no flags */ | |
| ipath_flush_wc(); | |
| piobuf += 2; | |
| uhdr = (u32 *)hdr; | |
| count = plen-1; /* amount we can copy before trigger word */ | |
| __iowrite32_copy(piobuf, uhdr, count); | |
| ipath_flush_wc(); | |
| __raw_writel(uhdr[count], piobuf + count); | |
| ipath_flush_wc(); /* ensure it's sent, now */ | |
| ipath_stats.sps_ether_spkts++; /* ether packet sent */ | |
| done: | |
| return ret; | |
| } | |
| EXPORT_SYMBOL_GPL(ipath_layer_send_hdr); | |
| int ipath_layer_set_piointbufavail_int(struct ipath_devdata *dd) | |
| { | |
| set_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl); | |
| ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, | |
| dd->ipath_sendctrl); | |
| return 0; | |
| } | |
| EXPORT_SYMBOL_GPL(ipath_layer_set_piointbufavail_int); |