[PATCH 3/10] VIOC: New Network Device Driver

From: Misha Tomushev
Date: Thu Oct 05 2006 - 14:24:17 EST


Adding VIOC device driver. Out-of-band provisioning protocol support code.

Signed-off-by: Misha Tomushev <misha@xxxxxxxxxxx>

diff -uprN linux-2.6.17/drivers/net/vioc/f7/spp.h
linux-2.6.17.vioc/drivers/net/vioc/f7/spp.h
--- linux-2.6.17/drivers/net/vioc/f7/spp.h 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.17.vioc/drivers/net/vioc/f7/spp.h 2006-09-06 16:22:59.000000000
-0700
@@ -0,0 +1,68 @@
+/*
+ * Fabric7 Systems Virtual IO Controller Driver
+ * Copyright (C) 2003-2005 Fabric7 Systems. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * http://www.fabric7.com/
+ *
+ * Maintainers:
+ * driver-support@xxxxxxxxxxx
+ *
+ *
+ */
+#ifndef _SPP_H_
+#define _SPP_H_
+
+#include "vnic_hw_registers.h"
+
+#define SPP_MODULE VIOC_BMC
+
+#define SPP_CMD_REG_BANK 15
+#define SPP_SIM_PMM_BANK 14
+#define SPP_PMM_BMC_BANK 13
+
+/* communications COMMAND REGISTERS */
+#define SPP_SIM_PMM_CMDREG GETRELADDR(SPP_MODULE, SPP_CMD_REG_BANK,
VREG_BMC_REG_R1)
+#define VIOCCP_SPP_SIM_PMM_CMDREG \
+ VIOCCP_GETRELADDR(SPP_MODULE, SPP_CMD_REG_BANK,
VREG_BMC_REG_R1)
+#define SPP_PMM_SIM_CMDREG GETRELADDR(SPP_MODULE, SPP_CMD_REG_BANK,
VREG_BMC_REG_R2)
+#define VIOCCP_SPP_PMM_SIM_CMDREG \
+ VIOCCP_GETRELADDR(SPP_MODULE, SPP_CMD_REG_BANK,
VREG_BMC_REG_R2)
+#define SPP_PMM_BMC_HB_CMDREG GETRELADDR(SPP_MODULE, SPP_CMD_REG_BANK,
VREG_BMC_REG_R3)
+#define SPP_PMM_BMC_SIG_CMDREG GETRELADDR(SPP_MODULE, SPP_CMD_REG_BANK,
VREG_BMC_REG_R4)
+#define SPP_PMM_BMC_CMDREG GETRELADDR(SPP_MODULE, SPP_CMD_REG_BANK,
VREG_BMC_REG_R5)
+
+#define SPP_BANK_ADDR(bank) GETRELADDR(SPP_MODULE, bank, VREG_BMC_REG_R0)
+
+#define SPP_SIM_PMM_DATA GETRELADDR(SPP_MODULE, SPP_SIM_PMM_BANK,
VREG_BMC_REG_R0)
+#define VIOCCP_SPP_SIM_PMM_DATA \
+ VIOCCP_GETRELADDR(SPP_MODULE, SPP_SIM_PMM_BANK,
VREG_BMC_REG_R0)
+
+/* PMM-BMC Sensor register bits */
+#define SPP_PMM_BMC_HB_SENREG GETRELADDR(SPP_MODULE, 0, VREG_BMC_SENSOR0)
+#define SPP_PMM_BMC_CTL_SENREG GETRELADDR(SPP_MODULE, 0, VREG_BMC_SENSOR1)
+#define SPP_PMM_BMC_SENREG GETRELADDR(SPP_MODULE, 0, VREG_BMC_SENSOR2)
+
+/* BMC Interrupt number used to alert PMM that message has been sent */
+#define SPP_SIM_PMM_INTR 1
+#define SPP_BANK_REGS 32
+
+
+#define SPP_OK 0
+#define SPP_CHKSUM_ERR 1
+#endif /* _SPP_H_ */
+
diff -uprN linux-2.6.17/drivers/net/vioc/f7/spp_msgdata.h
linux-2.6.17.vioc/drivers/net/vioc/f7/spp_msgdata.h
--- linux-2.6.17/drivers/net/vioc/f7/spp_msgdata.h 1969-12-31
16:00:00.000000000 -0800
+++ linux-2.6.17.vioc/drivers/net/vioc/f7/spp_msgdata.h 2006-09-06
16:22:59.000000000 -0700
@@ -0,0 +1,54 @@
+/*
+ * Fabric7 Systems Virtual IO Controller Driver
+ * Copyright (C) 2003-2005 Fabric7 Systems. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * http://www.fabric7.com/
+ *
+ * Maintainers:
+ * driver-support@xxxxxxxxxxx
+ *
+ *
+ */
+#ifndef _SPPMSGDATA_H_
+#define _SPPMSGDATA_H_
+
+#include "spp.h"
+
+/* KEYs For SPP_FACILITY_VNIC */
+#define SPP_KEY_VNIC_CTL 1
+#define SPP_KEY_SET_PROV 2
+
+/* Data Register Offset for VIOC ID parameter */
+#define SPP_VIOC_ID_IDX 0
+#define SPP_VIOC_ID_OFFSET GETRELADDR(SPP_MODULE, SPP_SIM_PMM_BANK,
(VREG_BMC_REG_R0 + (SPP_VIOC_ID_IDX << 2)))
+#define VIOCCP_SPP_VIOC_ID_OFFSET VIOCCP_GETRELADDR(SPP_MODULE,
SPP_SIM_PMM_BANK, (VREG_BMC_REG_R0 + (SPP_VIOC_ID_IDX << 2)))
+
+/* KEYs for SPP_FACILITY_SYS */
+#define SPP_KEY_REQUEST_SIGNAL 1
+
+/* Data Register Offset for RESET_TYPE parameter */
+#define SPP_UOS_RESET_TYPE_IDX 0
+#define SPP_UOS_RESET_TYPE_OFFSET GETRELADDR(SPP_MODULE, SPP_SIM_PMM_BANK,
(VREG_BMC_REG_R0 + (SPP_UOS_RESET_TYPE_IDX << 2)))
+#define VIOCCP_SPP_UOS_RESET_TYPE_OFFSET VIOCCP_GETRELADDR(SPP_MODULE,
SPP_SIM_PMM_BANK, (VREG_BMC_REG_R0 + (SPP_UOS_RESET_TYPE_IDX << 2)))
+
+#define SPP_RESET_TYPE_REBOOT 1
+#define SPP_RESET_TYPE_SHUTDOWN 2
+
+#endif /* _SPPMSGDATA_H_ */
+
+
diff -uprN linux-2.6.17/drivers/net/vioc/f7/sppapi.h
linux-2.6.17.vioc/drivers/net/vioc/f7/sppapi.h
--- linux-2.6.17/drivers/net/vioc/f7/sppapi.h 1969-12-31 16:00:00.000000000
-0800
+++ linux-2.6.17.vioc/drivers/net/vioc/f7/sppapi.h 2006-09-06
16:22:59.000000000 -0700
@@ -0,0 +1,240 @@
+/*
+ * Fabric7 Systems Virtual IO Controller Driver
+ * Copyright (C) 2003-2005 Fabric7 Systems. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * http://www.fabric7.com/
+ *
+ * Maintainers:
+ * driver-support@xxxxxxxxxxx
+ *
+ *
+ */
+#ifndef _SPPAPI_H_
+#define _SPPAPI_H_
+
+/*
+ * COMMAND REGISTER is encoded as follows:
+ * |-------------|-------------|--------|-----|----|------------|---------|
+ * | chksum: | unique id: | spare: | RC: | D: | facility: | key: |
+ * | 31 30 29 28 | 27 26 25 24 | 23 22 | 21 | 20 | 19 18 17 16| 15 - 0 |
+ * |-------------|-------------|--------|-----|----|------------|---------|
+ */
+
+#define SPP_KEY_MASK 0x0000ffff
+#define SPP_KEY_SHIFT 0
+#define SPP_KEY_MASK_SHIFTED (SPP_KEY_MASK >> SPP_KEY_SHIFT)
+#define SPP_FACILITY_MASK 0x000f0000
+#define SPP_FACILITY_SHIFT 16
+#define SPP_FACILITY_MASK_SHIFTED (SPP_FACILITY_MASK >>
SPP_FACILITY_SHIFT)
+#define SPP_D_MASK 0x00100000
+#define SPP_D_SHIFT 20
+#define SPP_D_MASK_SHIFTED (SPP_D_MASK >> SPP_D_SHIFT)
+#define SPP_RC_MASK 0x00200000
+#define SPP_RC_SHIFT 21
+#define SPP_RC_MASK_SHIFTED (SPP_RC_MASK >> SPP_RC_SHIFT)
+#define SPP_UNIQID_MASK 0x0f000000
+#define SPP_UNIQID_SHIFT 24
+#define SPP_UNIQID_MASK_SHIFTED (SPP_UNIQID_MASK >> SPP_UNIQID_SHIFT)
+#define SPP_CHKSUM_MASK 0xf0000000
+#define SPP_CHKSUM_SHIFT 28
+#define SPP_CHKSUM_MASK_SHIFTED (SPP_CHKSUM_MASK >> SPP_CHKSUM_SHIFT)
+
+#define SPP_GET_KEY(m) \
+ ((m & SPP_KEY_MASK) >> SPP_KEY_SHIFT)
+
+#define SPP_GET_FACILITY(m) \
+ ((m & SPP_FACILITY_MASK) >> SPP_FACILITY_SHIFT)
+
+#define SPP_GET_D(m) \
+ ((m & SPP_D_MASK) >> SPP_D_SHIFT)
+
+#define SPP_GET_RC(m) \
+ ((m & SPP_D_MASK) >> SPP_D_SHIFT)
+
+#define SPP_GET_UNIQID(m) \
+ ((m & SPP_UNIQID_MASK) >> SPP_UNIQID_SHIFT)
+
+#define SPP_GET_CHKSUM(m) \
+ ((m & SPP_CHKSUM_MASK) >> SPP_CHKSUM_SHIFT)
+
+#define SPP_SET_KEY(m, key) do { \
+ m = ((m & ~SPP_KEY_MASK) | ((key << SPP_KEY_SHIFT) & SPP_KEY_MASK)) ; \
+} while (0)
+
+#define SPP_SET_FACILITY(m, facility) do { \
+ m = ((m & ~SPP_FACILITY_MASK) | ((facility << SPP_FACILITY_SHIFT) &
SPP_FACILITY_MASK)) ; \
+} while (0)
+
+#define SPP_MBOX_FREE 0
+#define SPP_MBOX_OCCUPIED 1
+
+#define SPP_MBOX_EMPTY(m) (SPP_GET_D(m) == SPP_MBOX_FREE)
+#define SPP_MBOX_FULL(m) (SPP_GET_D(m) == SPP_MBOX_OCCUPIED)
+
+#define SPP_SET_D(m, D) do { \
+ m = ((m & ~SPP_D_MASK) | ((D << SPP_D_SHIFT) & SPP_D_MASK)) ; \
+} while (0)
+
+#define SPP_CMD_OK 0
+#define SPP_CMD_FAIL 1
+
+#define SPP_SET_RC(m, rc) do { \
+ m = ((m & ~SPP_RC_MASK) | ((rc << SPP_RC_SHIFT) & SPP_RC_MASK)) ; \
+} while (0)
+
+#define SPP_SET_UNIQID(m, uniqid) do { \
+ m = ((m & ~SPP_UNIQID_MASK) | ((uniqid << SPP_UNIQID_SHIFT) &
SPP_UNIQID_MASK)) ; \
+} while (0)
+
+
+#define SPP_SET_CHKSUM(m, chksum) do { \
+ m = ((m & ~SPP_CHKSUM_MASK) | ((chksum << SPP_CHKSUM_SHIFT) &
SPP_CHKSUM_MASK)) ; \
+} while (0)
+
+
+static inline u32 spp_calc_u32_4bit_chksum(u32 w, int n)
+{
+ int len;
+ int nibbles = (n > 8) ? 8:n;
+ u32 cs = 0;
+
+ for (len = 0; len < nibbles; len++) {
+ w = (w >> len);
+ cs += w & SPP_CHKSUM_MASK_SHIFTED;
+ }
+
+ while (cs >> 4)
+ cs = (cs & SPP_CHKSUM_MASK_SHIFTED) + (cs >> 4);
+
+ return (~cs);
+}
+
+static inline u32 spp_calc_u32_chksum(u32 w)
+{
+ return (spp_calc_u32_4bit_chksum(w, 7));
+}
+
+static inline u32
+spp_validate_u32_chksum(u32 w)
+{
+ return (spp_calc_u32_4bit_chksum(w, 8) & SPP_CHKSUM_MASK_SHIFTED);
+}
+
+static inline u32
+spp_mbox_build_cmd(u32 key, u32 facility, u32 uniqid)
+{
+ u32 m = 0;
+ u32 cs;
+
+ SPP_SET_KEY(m, key);
+ SPP_SET_FACILITY(m, facility);
+ SPP_SET_UNIQID(m, uniqid);
+ SPP_SET_D(m, SPP_MBOX_OCCUPIED);
+ cs = spp_calc_u32_4bit_chksum(m, 7);
+ cs = (cs)?cs:~cs;
+ SPP_SET_CHKSUM(m, cs);
+
+ return (m);
+}
+
+static inline u32
+spp_build_key_facility(u32 key, u32 facility)
+{
+ u32 m = 0;
+
+ SPP_SET_KEY(m, key);
+ SPP_SET_FACILITY(m, facility);
+
+ return (m);
+}
+
+
+static inline u32
+spp_mbox_build_reply(u32 cmd, int success)
+{
+ u32 reply = cmd;
+ u32 cs;
+
+ SPP_SET_D(reply, SPP_MBOX_FREE);
+ if (success == SPP_CMD_OK)
+ SPP_SET_RC(reply, SPP_CMD_OK);
+ else
+ SPP_SET_RC(reply, SPP_CMD_FAIL);
+
+ cs = spp_calc_u32_4bit_chksum(reply, 7);
+ cs = (cs)?cs:~cs;
+ SPP_SET_CHKSUM(reply, cs);
+
+ return (reply);
+}
+
+static inline int
+spp_mbox_empty(u32 m)
+{
+ return SPP_MBOX_EMPTY(m);
+}
+
+static inline int
+spp_mbox_full(u32 m)
+{
+ return SPP_MBOX_FULL(m);
+}
+
+/*
+ * SPP Facilities 0 - 15
+ */
+
+#define SPP_FACILITY_VNIC 0 /* VNIC Provisioning */
+#define SPP_FACILITY_SYS 1 /* UOS Control */
+#define SPP_FACILITY_ISCSI_VNIC 2 /* iSCSI Initiator
Provisioning */
+
+
+/*
+ * spp_msg_register() is use to install a callback - cb_fn() function, that
would
+ * be invoked once the message with specific handle has beed received.
+ * The unique "handle" that associates the callback with the messages is
+ * key_facility parameter.
+ * Typically the endpoints (sender on SIM and receiver on PMM) would agree on
+ * the "handle" that consists of FACILITY (4 bits) and KEY (16 bits).
+ * The inlude spp_build_key_facility(key, facility) builds such handle.
+ * The parameters of callback:
+ * cb_nf(u32 cmd, void *data_buf, int data_buf_size, u32 timestamp), are
+ * cmd - an actual command value that was sent by SIM
+ * data_buf - pointer to up to 128 bytes (32 u32s) of message data from SIM
+ * data_buf_size - actual number of bytes in the message
+ * timestamp - a relative time/sequence number indicating when message,
+ * that caused the callback, was received.
+ *
+ * IMPORTANT: If the message was already waiting, at the time of
spp_msg_register()
+ * call, the cb_fn() will be invoked in the context of spp_msg_register().
+ *
+ */
+extern int spp_msg_register(u32 key_facility, void (*cb_fn)(u32, void *, int,
u32));
+
+/*
+ * spp_msg_unregirster() will remove the callback function, so that
+ * the imcoming messages with the key_facility handle will basically be
+ * overwriting command and data buffers.
+ */
+
+extern void spp_msg_unregister(u32 key_facility);
+
+extern int read_spp_regbank32(int vioc, int bank, char *buffer);
+
+#endif /* _SPPAPI_H_ */
+
diff -uprN linux-2.6.17/drivers/net/vioc/khash.h
linux-2.6.17.vioc/drivers/net/vioc/khash.h
--- linux-2.6.17/drivers/net/vioc/khash.h 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.17.vioc/drivers/net/vioc/khash.h 2006-09-06 16:22:59.000000000
-0700
@@ -0,0 +1,63 @@
+/*
+ * Fabric7 Systems Virtual IO Controller Driver
+ * Copyright (C) 2003-2005 Fabric7 Systems. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * http://www.fabric7.com/
+ *
+ * Maintainers:
+ * driver-support@xxxxxxxxxxx
+ *
+ *
+ */
+#ifndef __HASHIT__
+#define __HASHIT__
+
+#define CHAIN_H 0U
+#define OADDRESS_H 1U
+#define OVERFLOW_H 2U
+
+#include <linux/stddef.h>
+#include <linux/errno.h>
+#include <linux/vmalloc.h>
+#include <linux/string.h>
+
+struct shash_t;
+
+/* Elem is used both for chain and overflow hash */
+struct hash_elem_t {
+ struct hash_elem_t *next;
+ void *key;
+ void *data;
+ void (*cb_fn)(u32, void *, int, u32);
+ u32 command;
+ u32 timestamp;
+ spinlock_t lock;
+};
+
+
+struct shash_t *hashT_create(u32, size_t, size_t, u32(*)(unsigned char *,
unsigned long), int(*)(void *, void *), unsigned int);
+int hashT_delete(struct shash_t * , void *);
+struct hash_elem_t *hashT_lookup(struct shash_t * , void *);
+struct hash_elem_t *hashT_add(struct shash_t *, void *);
+void hashT_destroy(struct shash_t *);
+/* Accesors */
+void **hashT_getkeys(struct shash_t *);
+size_t hashT_tablesize(struct shash_t *);
+size_t hashT_size(struct shash_t *);
+
+#endif

diff -uprN linux-2.6.17/drivers/net/vioc/spp.c
linux-2.6.17.vioc/drivers/net/vioc/spp.c
--- linux-2.6.17/drivers/net/vioc/spp.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.17.vioc/drivers/net/vioc/spp.c 2006-10-04 10:16:15.000000000
-0700
@@ -0,0 +1,624 @@
+/*
+ * Fabric7 Systems Virtual IO Controller Driver
+ * Copyright (C) 2003-2005 Fabric7 Systems. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * http://www.fabric7.com/
+ *
+ * Maintainers:
+ * driver-support@xxxxxxxxxxx
+ *
+ *
+ */
+#include <linux/module.h>
+#include "f7/vnic_hw_registers.h"
+#include "f7/spp.h"
+#include "f7/sppapi.h"
+
+#include "vioc_vnic.h"
+#include "vioc_api.h"
+#include "khash.h"
+
+#define FACILITY_CNT 16
+
+#define mix(a,b,c) \
+{ \
+ a -= b; a -= c; a ^= (c >> 13); \
+ b -= c; b -= a; b ^= (a << 8); \
+ c -= a; c -= b; c ^= (b >> 13); \
+ a -= b; a -= c; a ^= (c >> 12); \
+ b -= c; b -= a; b ^= (a << 16); \
+ c -= a; c -= b; c ^= (b >> 5); \
+ a -= b; a -= c; a ^= (c >> 3); \
+ b -= c; b -= a; b ^= (a << 10); \
+ c -= a; c -= b; c ^= (b >> 15); \
+}
+
+
+struct shash_t {
+ /* Common fields for all hash tables types */
+ size_t tsize; /* Table size */
+ size_t ksize; /* Key size is fixed at creation time */
+ size_t dsize; /* Data size is fixed at creation time */
+ size_t nelems; /* Number of elements in the hash table */
+ struct hash_ops *h_ops;
+ u32(*hash_fn) (unsigned char *, unsigned long);
+ void (*copy_fn) (void *, int, void *);
+ int (*compare_fn) (void *, void *);
+ struct hash_elem_t **chtable; /* Data for the collision */
+};
+
+struct hash_ops {
+ int (*delete) (struct shash_t *, void *);
+ struct hash_elem_t *(*lookup) (struct shash_t *, void *);
+ void (*destroy) (struct shash_t *);
+ struct hash_elem_t *(*add) (struct shash_t *, void *);
+ void **(*getkeys) (struct shash_t *);
+};
+
+struct facility {
+ struct shash_t *hashT;
+ spinlock_t lock;
+};
+
+static u32 hash_func(unsigned char *k, unsigned long length)
+{
+ unsigned long a, b, c, len;
+
+ /* Set up the internal state */
+ len = length;
+ a = b = c = 0x9e3779b9; /* the golden ratio; an arbitrary value */
+
+ /* Handle most of the key */
+ while (len >= 12) {
+ a += (k[0] + ((unsigned long)k[1] << 8) +
+ ((unsigned long)k[2] << 16) +
+ ((unsigned long)k[3] << 24));
+ b += (k[4] + ((unsigned long)k[5] << 8) +
+ ((unsigned long)k[6] << 16) +
+ ((unsigned long)k[7] << 24));
+ c += (k[8] + ((unsigned long)k[9] << 8) +
+ ((unsigned long)k[10] << 16) +
+ ((unsigned long)k[11] << 24));
+ mix(a, b, c);
+ k += 12;
+ len -= 12;
+ }
+
+ /* Handle the last 11 bytes */
+ c += length;
+ switch (len) { /* all the case statements fall through */
+ case 11:
+ c += ((unsigned long)k[10] << 24);
+ case 10:
+ c += ((unsigned long)k[9] << 16);
+ case 9:
+ c += ((unsigned long)k[8] << 8);
+ /* the first byte of c is reserved for the length */
+ case 8:
+ b += ((unsigned long)k[7] << 24);
+ case 7:
+ b += ((unsigned long)k[6] << 16);
+ case 6:
+ b += ((unsigned long)k[5] << 8);
+ case 5:
+ b += k[4];
+ case 4:
+ a += ((unsigned long)k[3] << 24);
+ case 3:
+ a += ((unsigned long)k[2] << 16);
+ case 2:
+ a += ((unsigned long)k[1] << 8);
+ case 1:
+ a += k[0];
+ }
+ mix(a, b, c);
+
+ return c;
+}
+
+static u32 gethash(struct shash_t *htable, void *key)
+{
+ size_t len;
+
+ len = htable->ksize;
+
+ /* Hash the key and get the meaningful bits for our table */
+ return ((htable->hash_fn(key, len)) & (htable->tsize - 1));
+}
+
+/* Data associated to this key MUST be freed by the caller */
+static int ch_delete(struct shash_t *htable, void *key)
+{
+ u32 idx;
+ struct hash_elem_t *cursor; /* Cursor for the linked list */
+ struct hash_elem_t *tmp; /* Pointer to the element to be deleted */
+
+ idx = gethash(htable, key);
+
+ if (!htable->chtable[idx])
+ return -1;
+
+ /* Delete asked element */
+ /* Element to delete is the first in the chain */
+ if (htable->compare_fn(htable->chtable[idx]->key, key) == 0) {
+ tmp = htable->chtable[idx];
+ htable->chtable[idx] = tmp->next;
+ vfree(tmp);
+ htable->nelems--;
+ return 0;
+ }
+ /* Search thru the chain for the element */
+ else {
+ cursor = htable->chtable[idx];
+ while (cursor->next != NULL) {
+ if (htable->compare_fn(cursor->next->key, key) == 0) {
+ tmp = cursor->next;
+ cursor->next = tmp->next;
+ vfree(tmp);
+ htable->nelems--;
+ return 0;
+ }
+ cursor = cursor->next;
+ }
+ }
+
+ return -1;
+}
+
+static void ch_destroy(struct shash_t *htable)
+{
+ u32 idx;
+ struct hash_elem_t *cursor;
+
+ for (idx = 0; idx < htable->tsize; idx++) {
+ cursor = htable->chtable[idx];
+
+ while (cursor != NULL) {
+ struct hash_elem_t *tmp_cursor = cursor;
+
+ cursor = cursor->next;
+ vfree(tmp_cursor);
+ }
+ }
+ vfree(htable->chtable);
+}
+
+/* Return a NULL terminated string of pointers to all hash table keys */
+static void **ch_getkeys(struct shash_t *htable)
+{
+ u32 idx;
+ struct hash_elem_t *cursor;
+ void **keys;
+ u32 kidx;
+
+ keys = vmalloc((htable->nelems + 1) * sizeof(void *));
+ if (!keys) {
+ return NULL;
+ }
+ keys[htable->nelems] = NULL;
+ kidx = 0;
+
+ for (idx = 0; idx < htable->tsize; idx++) {
+
+ cursor = htable->chtable[idx];
+ while (cursor != NULL) {
+ printk(KERN_INFO "Element %d in bucket %d, key %p value %px\n",
+ kidx, idx, cursor->key, cursor->data);
+ keys[kidx] = cursor->key;
+ kidx++;
+
+ cursor = cursor->next;
+ }
+ }
+
+ return keys;
+}
+
+/* Accesors
******************************************************************/
+inline size_t hashT_tablesize(struct shash_t * htable)
+{
+ return htable->tsize;
+}
+
+inline size_t hashT_size(struct shash_t * htable)
+{
+ return htable->nelems;
+}
+
+static struct hash_elem_t *ch_lookup(struct shash_t *htable, void *key)
+{
+ u32 idx;
+ struct hash_elem_t *cursor;
+
+ idx = gethash(htable, key);
+
+ cursor = htable->chtable[idx];
+
+ /* Search thru the chain for the asked key */
+ while (cursor != NULL) {
+ if (htable->compare_fn(cursor->key, key) == 0)
+ return cursor;
+ cursor = cursor->next;
+ }
+
+ return NULL;
+}
+
+static struct hash_elem_t *ch_add(struct shash_t *htable, void *key)
+{
+ u32 idx;
+ struct hash_elem_t *cursor;
+ struct hash_elem_t *oneelem;
+
+ idx = gethash(htable, key);
+
+ cursor = htable->chtable[idx];
+
+ /* Search thru the chain for the asked key */
+ while (cursor != NULL) {
+ if (htable->compare_fn(cursor->key, key) == 0)
+ break;
+ cursor = cursor->next;
+ }
+
+ if (!cursor) {
+ /* cursor == NULL, means, that no element with this key was found,
+ * need to insert one
+ */
+ oneelem =
+ (struct hash_elem_t *)vmalloc(sizeof(struct hash_elem_t) +
+ htable->ksize +
+ htable->dsize);
+ if (!oneelem)
+ return (struct hash_elem_t *)NULL;
+ memset((void *)oneelem, 0,
+ sizeof(struct hash_elem_t) + htable->ksize +
+ htable->dsize);
+
+ oneelem->command = 0;
+ oneelem->key = (void *)((char *)oneelem + sizeof(struct hash_elem_t));
+ oneelem->data = (void *)((char *)oneelem->key + htable->ksize);
+ memcpy((void *)oneelem->key, (void *)key, htable->ksize);
+
+ oneelem->next = NULL;
+
+ if (htable->chtable[idx] == NULL)
+ /* No collision ;), first element in this bucket */
+ htable->chtable[idx] = oneelem;
+ else {
+ /* Collision, insert at the end of the chain */
+ cursor = htable->chtable[idx];
+ while (cursor->next != NULL)
+ cursor = cursor->next;
+
+ /* Insert element at the end of the chain */
+ cursor->next = oneelem;
+ }
+
+ htable->nelems++;
+ spin_lock_init(&oneelem->lock);
+ } else {
+ /* Found element with the key */
+ oneelem = cursor;
+ }
+
+ return oneelem;
+}
+
+static int key_compare(void *key_in, void *key_out)
+{
+ if ((u16) * ((u16 *) key_in) == (u16) * ((u16 *) key_out))
+ return 0;
+ else
+ return 1;
+}
+
+struct hash_ops ch_ops = {
+ .delete = ch_delete,
+ .lookup = ch_lookup,
+ .destroy = ch_destroy,
+ .getkeys = ch_getkeys,
+ .add = ch_add
+};
+
+struct facility fTable[FACILITY_CNT];
+
+int spp_init(void)
+{
+ int i;
+
+ for (i = 0; i < FACILITY_CNT; i++) {
+ fTable[i].hashT = NULL;
+ spin_lock_init(&fTable[i].lock);
+ }
+
+ spp_vnic_init();
+
+ return 0;
+}
+
+void spp_terminate(void)
+{
+ int i;
+
+ spp_vnic_exit();
+
+ for (i = 0; i < FACILITY_CNT; i++) {
+ if (fTable[i].hashT) {
+ spin_lock(&fTable[i].lock);
+ hashT_destroy(fTable[i].hashT);
+ fTable[i].hashT = NULL;
+ spin_unlock(&fTable[i].lock);
+ }
+ }
+}
+
+void spp_msg_from_sim(int vioc_idx)
+{
+ struct vioc_device *viocdev = vioc_viocdev(vioc_idx);
+ u32 command_reg, pmm_reply;
+ u32 key, facility, uniqid;
+ u32 *data_p;
+ struct hash_elem_t *elem;
+ int i;
+
+ vioc_reg_rd(viocdev->ba.virt, SPP_SIM_PMM_CMDREG, &command_reg);
+
+ if (spp_mbox_empty(command_reg)) {
+ return;
+ }
+
+ /* Validate checksum */
+ if (spp_validate_u32_chksum(command_reg) != 0) {
+ return;
+ }
+
+ /* Build reply message to SIM */
+ pmm_reply = spp_mbox_build_reply(command_reg, SPP_CMD_OK);
+
+ key = SPP_GET_KEY(command_reg);
+ facility = SPP_GET_FACILITY(command_reg);
+ uniqid = SPP_GET_UNIQID(command_reg);
+
+ /* Check-and-create hash table */
+ spin_lock(&fTable[facility].lock);
+
+ if (fTable[facility].hashT == NULL) {
+ fTable[facility].hashT =
+ hashT_create(1024, 2, 128, NULL, key_compare, 0);
+ if (fTable[facility].hashT == NULL) {
+ goto error_exit;
+ }
+ }
+
+ /* Add the hash table element */
+ elem = hashT_add(fTable[facility].hashT, (void *)&key);
+ if (!elem) {
+ goto error_exit;
+ }
+ spin_unlock(&fTable[facility].lock);
+
+ /* Copy data from SPP Registers to the key buffer */
+ spin_lock(&elem->lock);
+
+ /* Copy data from SPP Register Bank */
+ for (i = 0, data_p = (u32 *) elem->data; i < SPP_BANK_REGS;
+ i++, (u32 *) data_p++) {
+ vioc_reg_rd(viocdev->ba.virt, SPP_SIM_PMM_DATA + (i << 2), (u32 *) data_p);
+ }
+
+ elem->command = command_reg;
+ elem->timestamp++;
+ elem->timestamp = (elem->timestamp & 0x0fffffff) | (vioc_idx << 28);
+
+ spin_unlock(&elem->lock);
+
+ vioc_reg_wr(pmm_reply, viocdev->ba.virt, SPP_SIM_PMM_CMDREG);
+ viocdev->last_msg_to_sim = pmm_reply;
+
+ /* If there was registered callback, execute it */
+ if (elem->cb_fn) {
+ spin_lock(&elem->lock);
+ if (spp_validate_u32_chksum(elem->command) == 0) {
+ /* If there is a valid message waiting, call callback */
+ elem->cb_fn(elem->command,
+ elem->data, 128, elem->timestamp);
+ elem->command = 0;
+ }
+ spin_unlock(&elem->lock);
+ }
+ return;
+
+ error_exit:
+ spin_unlock(&fTable[facility].lock);
+ pmm_reply = spp_mbox_build_reply(command_reg, SPP_CMD_FAIL);
+ vioc_reg_wr(pmm_reply, viocdev->ba.virt, SPP_SIM_PMM_CMDREG);
+ return;
+
+}
+
+int spp_msg_register(u32 key_facility, void (*cb_fn) (u32, void *, int, u32))
+{
+ u32 facility = SPP_GET_FACILITY(key_facility);
+ u32 key = SPP_GET_KEY(key_facility);
+ struct hash_elem_t *elem;
+
+ /* Check-and-create hash table */
+ spin_lock(&fTable[facility].lock);
+
+ if (fTable[facility].hashT == NULL) {
+ fTable[facility].hashT =
+ hashT_create(1024, 2, 128, NULL, key_compare, 0);
+ if (fTable[facility].hashT == NULL) {
+ goto error_exit;
+ }
+ }
+
+ /* Add the hash table element */
+ elem = hashT_add(fTable[facility].hashT, (void *)&key);
+ if (!elem) {
+ goto error_exit;
+ }
+ spin_unlock(&fTable[facility].lock);
+
+ spin_lock(&elem->lock);
+ elem->cb_fn = cb_fn;
+ if (spp_validate_u32_chksum(elem->command) == 0) {
+ /* If there is a valid message waiting, call callback */
+ elem->cb_fn(elem->command, elem->data, 128, elem->timestamp);
+ elem->command = 0;
+ }
+ spin_unlock(&elem->lock);
+ return SPP_CMD_OK;
+
+ error_exit:
+ spin_unlock(&fTable[facility].lock);
+ return SPP_CMD_FAIL;
+}
+
+void spp_msg_unregister(u32 key_facility)
+{
+ u32 key = SPP_GET_KEY(key_facility);
+ u32 facility = SPP_GET_FACILITY(key_facility);
+ struct hash_elem_t *elem;
+
+ if (fTable[facility].hashT == NULL)
+ return;
+
+ elem = hashT_lookup(fTable[facility].hashT, (void *)&key);
+ if (!elem)
+ return;
+
+ spin_lock(&elem->lock);
+ elem->cb_fn = NULL;
+ spin_unlock(&elem->lock);
+}
+
+
+int read_spp_regbank32(int vioc_idx, int bank, char *buffer)
+{
+ struct vioc_device *viocdev = vioc_viocdev(vioc_idx);
+ int i;
+ u32 *data_p;
+ u32 reg;
+
+ if (!viocdev)
+ return 0;
+
+ /* Copy data from SPP Register Bank */
+ for (i = 0, data_p = (u32 *) buffer; i < SPP_BANK_REGS;
+ i++, (u32 *) data_p++) {
+ reg = SPP_BANK_ADDR(bank) + (i << 2);
+ vioc_reg_rd(viocdev->ba.virt, reg, (u32 *) data_p);
+ }
+
+ return i;
+}
+
+struct shash_t *hashT_create(u32 sizehint,
+ size_t keybuf_size,
+ size_t databuf_size,
+ u32(*hfunc) (unsigned char *,
+ unsigned long),
+ int (*cfunc) (void *, void *),
+ unsigned int flags)
+{
+ struct shash_t *htable;
+ u32 size = 0; /* Table size */
+ int i = 1;
+
+ /* Take the size hint and round it to the next higher power of two */
+ while (size < sizehint) {
+ size = 1 << i++;
+ if (size == 0) {
+ size = 1 << (i - 2);
+ break;
+ }
+ }
+
+ if (cfunc == NULL)
+ return NULL;
+
+ /* Create hash table */
+ htable = vmalloc(sizeof(struct shash_t) + keybuf_size + databuf_size);
+ if (!htable)
+ return NULL;
+
+ /* And create structs for hash table */
+
+ htable->h_ops = &ch_ops;
+ htable->chtable = vmalloc(size * (sizeof(struct hash_elem_t)
+ + keybuf_size
+ + databuf_size));
+ if (!htable->chtable) {
+ vfree(htable);
+ return NULL;
+ }
+
+ memset(htable->chtable, '\0',
+ size * (sizeof(struct hash_elem_t) + keybuf_size + databuf_size));
+
+ /* Initialize hash table common fields */
+ htable->tsize = size;
+ htable->ksize = keybuf_size;
+ htable->dsize = databuf_size;
+ htable->nelems = 0;
+
+ if (hfunc)
+ htable->hash_fn = hfunc;
+ else
+ htable->hash_fn = hash_func;
+
+ htable->compare_fn = cfunc;
+
+ return htable;
+}
+
+int hashT_delete(struct shash_t *htable, void *key)
+{
+ return htable->h_ops->delete(htable, key);
+}
+
+struct hash_elem_t *hashT_add(struct shash_t *htable, void *key)
+{
+ return htable->h_ops->add(htable, key);
+}
+
+struct hash_elem_t *hashT_lookup(struct shash_t *htable, void *key)
+{
+ return htable->h_ops->lookup(htable, key);
+}
+
+void hashT_destroy(struct shash_t *htable)
+{
+
+ htable->h_ops->destroy(htable);
+
+ vfree(htable);
+}
+
+void **hashT_getkeys(struct shash_t *htable)
+{
+ return htable->h_ops->getkeys(htable);
+}
+
+#ifdef EXPORT_SYMTAB
+EXPORT_SYMBOL(spp_msg_register);
+EXPORT_SYMBOL(spp_msg_unregister);
+EXPORT_SYMBOL(read_spp_regbank32);
+#endif
diff -uprN linux-2.6.17/drivers/net/vioc/spp_vnic.c
linux-2.6.17.vioc/drivers/net/vioc/spp_vnic.c
--- linux-2.6.17/drivers/net/vioc/spp_vnic.c 1969-12-31 16:00:00.000000000
-0800
+++ linux-2.6.17.vioc/drivers/net/vioc/spp_vnic.c 2006-10-04
10:18:35.000000000 -0700
@@ -0,0 +1,131 @@
+/*
+ * Fabric7 Systems Virtual IO Controller Driver
+ * Copyright (C) 2003-2005 Fabric7 Systems. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * http://www.fabric7.com/
+ *
+ * Maintainers:
+ * driver-support@xxxxxxxxxxx
+ *
+ *
+ */
+#include <stdarg.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include "vioc_vnic.h"
+#include "vioc_api.h"
+#include "f7/sppapi.h"
+#include "f7/spp_msgdata.h"
+#include "f7/vnic_hw_registers.h"
+
+
+#define VIOC_ID_FROM_STAMP(stamp) ((stamp >> 28) & 0xf)
+
+static u32 relative_time;
+
+static void spp_vnic_cb(u32 cmd, void *msg_buf, int buf_size, u32 viocstamp)
+{
+ u32 param, param2;
+ int viocdev_idx;
+ u32 timestamp = viocstamp & 0x0fffffff;
+ int vioc_id = VIOC_ID_FROM_STAMP(viocstamp);
+
+ relative_time = timestamp;
+
+ viocdev_idx = ((int *)msg_buf)[SPP_VIOC_ID_IDX];
+ if (viocdev_idx != vioc_id) {
+ printk(KERN_ERR "%s: MSG to VIOC=%d, but param VIOC=%d\n",
+ __FUNCTION__, vioc_id, viocdev_idx);
+ }
+ param = vioc_rrd(viocdev_idx, VIOC_BMC, 0, VREG_BMC_VNIC_EN);
+ param2 = vioc_rrd(viocdev_idx, VIOC_BMC, 0, VREG_BMC_PORT_EN);
+#ifdef VNIC_UNREGISTER_PATCH
+ vioc_vnic_prov(viocdev_idx, param, param2, 1);
+#else
+ vioc_vnic_prov(viocdev_idx, param, param2, 0);
+#endif
+}
+
+static void spp_sys_cb(u32 cmd, void *msg_buf, int buf_size, u32 viocstamp)
+{
+ u32 reset_type;
+ u32 timestamp = viocstamp & 0x0fffffff;
+
+ relative_time = timestamp;
+
+ reset_type = ((u32 *) msg_buf)[SPP_UOS_RESET_TYPE_IDX];
+ if (vioc_handle_reset_request(reset_type) < 0) {
+ /* Invalid reset type ..ignore the message */
+ printk(KERN_ERR "Invalid reset request %d\n", reset_type);
+ }
+}
+static void spp_prov_cb(u32 cmd, void *msg_buf, int buf_size, u32 viocstamp)
+{
+ u32 timestamp = viocstamp & 0x0fffffff;
+
+ relative_time = timestamp;
+}
+
+struct kf_handler {
+ u32 key;
+ u32 facility;
+ void (*cb) (u32, void *, int, u32);
+};
+
+static
+struct kf_handler local_cb[] = {
+ {SPP_KEY_VNIC_CTL, SPP_FACILITY_VNIC, spp_vnic_cb},
+ {SPP_KEY_REQUEST_SIGNAL, SPP_FACILITY_SYS, spp_sys_cb},
+ {SPP_KEY_SET_PROV, SPP_FACILITY_VNIC, spp_prov_cb},
+ {0, 0, NULL}
+};
+
+int spp_vnic_init(void)
+{
+ u32 key_facility = 0;
+ int i;
+
+ for (i = 0; local_cb[i].cb; i++) {
+
+ SPP_SET_KEY(key_facility, local_cb[i].key);
+ SPP_SET_FACILITY(key_facility, local_cb[i].facility);
+
+ spp_msg_register(key_facility, local_cb[i].cb);
+ }
+ return 0;
+}
+
+void spp_vnic_exit(void)
+{
+ u32 key_facility = 0;
+ int i;
+
+ for (i = 0; local_cb[i].cb; i++) {
+
+ SPP_SET_KEY(key_facility, local_cb[i].key);
+ SPP_SET_FACILITY(key_facility, local_cb[i].facility);
+
+ spp_msg_unregister(key_facility);
+ }
+ return;
+}
+
diff -uprN linux-2.6.17/drivers/net/vioc/vioc_spp.c
linux-2.6.17.vioc/drivers/net/vioc/vioc_spp.c
--- linux-2.6.17/drivers/net/vioc/vioc_spp.c 1969-12-31 16:00:00.000000000
-0800
+++ linux-2.6.17.vioc/drivers/net/vioc/vioc_spp.c 2006-10-04
10:39:45.000000000 -0700
@@ -0,0 +1,388 @@
+/*
+ * Fabric7 Systems Virtual IO Controller Driver
+ * Copyright (C) 2003-2005 Fabric7 Systems. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * http://www.fabric7.com/
+ *
+ * Maintainers:
+ * driver-support@xxxxxxxxxxx
+ *
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/if_vlan.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/reboot.h>
+#include <linux/kallsyms.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <net/genetlink.h>
+
+#include "f7/vnic_hw_registers.h"
+#include "f7/vnic_defs.h"
+#include "f7/spp_msgdata.h"
+#include "vioc_vnic.h"
+#include "vioc_api.h"
+
+/* Register definitions for communication between VIOC and BMC */
+
+#define SPP_VIOC_MODULE VIOC_BMC
+
+#define SPP_BMC_VNIC 14
+#define SPP_SIM_VNIC 15
+
+/* PMM-BMC communications messages */
+#define SPP_BMC_RST_RQ 0x01
+#define SPP_BMC_TUN_MSG 0x02
+#define SPP_BMC_HB 0x01
+
+/* PMM-BMC Sensor register bits */
+#define SPP_HB_SENSOR_REG GETRELADDR(SPP_VIOC_MODULE, 0,
VREG_BMC_SENSOR0)
+#define SPP_SHUTDWN_SENSOR_REG GETRELADDR(SPP_VIOC_MODULE, 0,
VREG_BMC_SENSOR1)
+#define SPP_PANIC_SENSOR_REG GETRELADDR(SPP_VIOC_MODULE, 0,
VREG_BMC_SENSOR2)
+#define SPP_RST_SENSOR_REG GETRELADDR(SPP_VIOC_MODULE, 0,
VREG_BMC_SENSOR3)
+
+/* PMM-BMC communications registers */
+#define SPP_BMC_HB_REG GETRELADDR(SPP_VIOC_MODULE, SPP_BMC_VNIC,
VREG_BMC_REG_R31)
+#define SPP_BMC_CMD_REG GETRELADDR(SPP_VIOC_MODULE,
SPP_BMC_VNIC, VREG_BMC_REG_R30)
+
+static DECLARE_MUTEX(vnic_prov_sem);
+
+static inline void vnic_prov_get_sema(void)
+{
+ down(&vnic_prov_sem);
+}
+
+static inline void vnic_prov_put_sema(void)
+{
+ up(&vnic_prov_sem);
+}
+
+/* VIOC must be write-locked */
+static int vioc_vnic_enable(int vioc_id, int vnic_id)
+{
+ struct vioc_device *viocdev = vioc_viocdev(vioc_id);
+ struct net_device *netdev;
+ int rc = E_VIOCOK;
+
+ netdev = viocdev->vnic_netdev[vnic_id];
+ if (netdev == NULL) {
+ netdev = vioc_alloc_vnicdev(viocdev, vnic_id);
+ if (netdev == NULL) {
+ rc = -ENOMEM;
+ goto vioc_vnic_enable_exit;
+ } else {
+ viocdev->vnic_netdev[vnic_id] = netdev;
+
+ if ((rc = register_netdev(netdev))) {
+ free_netdev(netdev);
+ viocdev->vnic_netdev[vnic_id] = NULL;
+ goto vioc_vnic_enable_exit;
+ }
+ }
+ }
+
+ viocdev->vnics_map |= (1 << vnic_id);
+
+ rc = vioc_vnic_resources_set(vioc_id, vnic_id);
+
+ vioc_vnic_enable_exit:
+
+ return rc;
+}
+
+/* VIOC must be write-locked */
+static int vioc_vnic_disable(int vioc_id, int vnic_id, int unreg_flag)
+{
+ struct vioc_device *viocdev = vioc_viocdev(vioc_id);
+
+ /* Remove VNIC from the map BEFORE releasing resources */
+ viocdev->vnics_map &= ~(1 << vnic_id);
+
+ if (unreg_flag) {
+ /* Unregister netdev */
+ if (viocdev->vnic_netdev[vnic_id]) {
+ unregister_netdev(viocdev->vnic_netdev[vnic_id]);
+ dev_err(&viocdev->pdev->dev, "%s: %s\n", __FUNCTION__,
+ (viocdev->vnic_netdev[vnic_id])->name);
+ free_netdev(viocdev->vnic_netdev[vnic_id]);
+ viocdev->vnic_netdev[vnic_id] = NULL;
+ } else
+ BUG_ON(viocdev->vnic_netdev[vnic_id] == NULL);
+ }
+
+ return E_VIOCOK;
+}
+
+void vioc_vnic_prov(int vioc_id, u32 vnic_en, u32 port_en, int free_flag)
+{
+ u32 change_map;
+ u32 up_map;
+ u32 down_map;
+ int vnic_id;
+ int rc = E_VIOCOK;
+ struct vioc_device *viocdev = vioc_viocdev(vioc_id);
+
+ change_map = vnic_en ^ viocdev->vnics_map;
+ up_map = vnic_en & change_map;
+ down_map = viocdev->vnics_map & change_map;
+
+ /* Enable from 0 to max */
+ for (vnic_id = 0; vnic_id < VIOC_MAX_VNICS; vnic_id++) {
+ if (up_map & (1 << vnic_id)) {
+ rc = vioc_vnic_enable(vioc_id, vnic_id);
+ if (rc) {
+ dev_err(&viocdev->pdev->dev, "%s: Enable VNIC %d FAILED\n",
+ __FUNCTION__, vnic_id);
+ }
+ }
+ }
+
+ /* Disable from max to 0 */
+ for (vnic_id = VIOC_MAX_VNICS - 1; vnic_id >= 0; vnic_id--) {
+ if (down_map & (1 << vnic_id)) {
+ rc = vioc_vnic_disable(vioc_id, vnic_id, free_flag);
+ if (rc) {
+ dev_err(&viocdev->pdev->dev, "%s: Disable VNIC %d FAILED\n",
+ __FUNCTION__, vnic_id);
+ }
+ }
+ }
+
+ /*
+ * Now, after all VNIC enable-disable changes are in place,
+ * viocdev->vnics_map contains the current state of VNIC map.
+ * Use only ENABLED VNICs to process PORT_EN register, aka
+ * LINK state register.
+ */
+ for (vnic_id = VIOC_MAX_VNICS - 1; vnic_id >= 0; vnic_id--) {
+ if (viocdev->vnics_map & (1 << vnic_id)) {
+ struct net_device *netdev =
+ viocdev->vnic_netdev[vnic_id];
+ if (port_en & (1 << vnic_id)) {
+ /* PORT ENABLED - LINK UP */
+ if (!netif_carrier_ok(netdev)) {
+ netif_carrier_on(netdev);
+ netif_wake_queue(netdev);
+ dev_err(&viocdev->pdev->dev, "idx %d, %s: Link UP\n",
+ vioc_id, netdev->name);
+ }
+ } else {
+ /* PORT DISABLED - LINK DOWN */
+ if (netif_carrier_ok(netdev)) {
+ netif_stop_queue(netdev);
+ netif_carrier_off(netdev);
+ dev_err(&viocdev->pdev->dev,
+ "idx %d, %s: Link DOWN\n",
+ vioc_id, netdev->name);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Called from interrupt or task context
+ */
+void vioc_bmc_interrupt(void *input_param)
+{
+ int vioc_id = VIOC_IRQ_PARAM_VIOC_ID(input_param);
+ int rc = 0;
+ u32 intr_source_map, intr_source;
+ struct vioc_device *viocdev = vioc_viocdev(vioc_id);
+
+ if (viocdev->vioc_state != VIOC_STATE_UP) {
+ dev_err(&viocdev->pdev->dev, "VIOC %d is not UP yet\n",
+ viocdev->viocdev_idx);
+ return;
+ }
+
+ /* Get the Interrupt Source Register */
+ intr_source_map = vioc_rrd(viocdev->viocdev_idx, VIOC_BMC, 0,
+ VREG_BMC_INTRSTATUS);
+
+ vnic_prov_get_sema();
+
+ /*
+ * Clear all pending interrupt bits, we will service all interrupts
+ * based on the copy of 0x144 register in intr_source.
+ */
+ rc = vioc_rwr(viocdev->viocdev_idx, VIOC_BMC, 0,
+ VREG_BMC_INTRSTATUS, intr_source_map);
+ if (rc) {
+ dev_err(&viocdev->pdev->dev, "%s: vioc_rwr() -> %d\n", __FUNCTION__, rc);
+ goto vioc_bmc_interrupt_exit;
+ }
+
+ for (intr_source = VIOC_BMC_INTR0; intr_source_map; intr_source <<= 1) {
+ switch (intr_source_map & intr_source) {
+ case VIOC_BMC_INTR0:
+ dev_err(&viocdev->pdev->dev,
+ "*** OLD SPP commands (BMC intr %08x) are no longer supported\n",
+ intr_source_map);
+ break;
+ case VIOC_BMC_INTR1:
+ spp_msg_from_sim(viocdev->viocdev_idx);
+ break;
+
+ default:
+ break;
+ }
+ intr_source_map &= ~intr_source;
+ }
+
+ vioc_bmc_interrupt_exit:
+
+ vnic_prov_put_sema();
+
+ return;
+
+}
+
+/* SPP Messages originated by VIOC driver */
+void vioc_hb_to_bmc(int vioc_id)
+{
+ struct vioc_device *viocdev = vioc_viocdev(0);
+
+ if (!viocdev)
+ return;
+
+ /* Signal BMC that command was written by setting bit 0 of
+ * SENSOR Register */
+ vioc_reg_wr(1, viocdev->ba.virt, SPP_HB_SENSOR_REG);
+}
+
+void vioc_reset_rq_to_bmc(int vioc_id, u32 command)
+{
+ struct vioc_device *viocdev = vioc_viocdev(0);
+
+ if (!viocdev)
+ return;
+
+ switch (command) {
+ case SYS_RESTART:
+ vioc_reg_wr(1, viocdev->ba.virt, SPP_RST_SENSOR_REG);
+ break;
+ case SYS_HALT:
+ case SYS_POWER_OFF:
+ vioc_reg_wr(1, viocdev->ba.virt, SPP_SHUTDWN_SENSOR_REG);
+ break;
+ default:
+ dev_err(&viocdev->pdev->dev, "%s: Received invalid command %d\n",
+ __FUNCTION__, command);
+ }
+}
+
+/*-------------------------------------------------------------------*/
+
+/* Shutdown/Reboot handler definitions */
+static int vioc_shutdown_notify(struct notifier_block *thisblock,
+ unsigned long code, void *unused);
+
+static struct notifier_block vioc_shutdown_notifier = {
+ .notifier_call = vioc_shutdown_notify
+};
+
+void vioc_os_reset_notifier_init(void)
+{
+ /* We want to know get a callback when the system is going down */
+ if (register_reboot_notifier(&vioc_shutdown_notifier)) {
+ printk(KERN_ERR "%s: register_reboot_notifier() returned error\n",
+ __FUNCTION__);
+ }
+}
+
+void vioc_os_reset_notifier_exit(void)
+{
+ if (unregister_reboot_notifier(&vioc_shutdown_notifier)) {
+ printk (KERN_ERR "%s: unregister_reboot_notifier() returned error\n",
+ __FUNCTION__);
+ }
+}
+
+ /*
+ * vioc_shutdown_notify - Called when the OS is going down.
+ */
+static int vioc_shutdown_notify(struct notifier_block *thisblock,
+ unsigned long code, void *unused)
+{
+ vioc_reset_rq_to_bmc(0, code);
+ printk(KERN_ERR "%s: sent %s UOS state to BMC\n",
+ __FUNCTION__,
+ ((code == SYS_RESTART) ? "REBOOT" :
+ ((code == SYS_HALT) ? "HALT" :
+ ((code == SYS_POWER_OFF) ? "POWER_OFF" : "Unknown"))));
+
+ return 0;
+}
+
+/*
+ * FUNCTION: vioc_handle_reset_request
+ * INPUT: Reset type : Shutdown, Reboot, Poweroff
+ * OUTPUT: Returns 0 on Success -EINVAL on failure
+ *
+ */
+int vioc_handle_reset_request(int reset_type)
+{
+ struct sk_buff *msg = NULL;
+ void *hdr;
+
+ printk(KERN_ERR "%s: received reset request %d via SPP\n",
+ __FUNCTION__, reset_type);
+
+ /* Disable VIOC interrupts */
+ // vioc_irq_exit();
+
+ msg = nlmsg_new(NLMSG_GOODSIZE);
+ if (msg == NULL) {
+ printk(KERN_ERR "%s: nlmsg_new() FAILS\n", __FUNCTION__);
+ return (-ENOBUFS);
+ }
+
+ hdr = nlmsg_put(msg, 0, 0, 0, 0, 0);
+
+ if (reset_type == SPP_RESET_TYPE_REBOOT)
+ nla_put_string(msg, reset_type, "Reboot");
+ else
+ nla_put_string(msg, reset_type, "Shutdown");
+
+ nlmsg_end(msg, hdr);
+
+ return nlmsg_multicast(genl_sock, msg, 0, VIOC_LISTEN_GROUP);
+}


--
Misha Tomushev
misha@xxxxxxxxxxx


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/