public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Driver for GE PIO2 VME Card
@ 2011-11-04 17:33 Martyn Welch
  2011-11-04 17:38 ` Martyn Welch
  2011-11-04 19:55 ` Paul Bolle
  0 siblings, 2 replies; 9+ messages in thread
From: Martyn Welch @ 2011-11-04 17:33 UTC (permalink / raw)
  To: gregkh; +Cc: Grant Likely, devel, manohar.vanga, cota, linux-kernel

From: Martyn Welch <martyn.welch@gefanuc.com>

This patch implements a driver for the GE PIO2 VME Parallel I/O Card.  This
card is a 6U VME Card, implementing 32 solid-state relay switched IO lines,
in 4 groups of 8. The IO lines are provided as input, output or both as a
build time option.

Signed-off-by: Martyn Welch <martyn.welch@ge.com>
---

This patch provides basic support for the GPIO on the PIO-2 at this point it
does not provide support for IRQs generated by GPIO or the 6 counters also
provided by the board. However, it does provide a good example of how to write
a VME driver for real hardware.

 drivers/staging/vme/devices/Kconfig         |   10 +
 drivers/staging/vme/devices/Makefile        |    3 
 drivers/staging/vme/devices/vme_pio2.h      |  249 +++++++++++++
 drivers/staging/vme/devices/vme_pio2_cntr.c |   71 ++++
 drivers/staging/vme/devices/vme_pio2_core.c |  524 +++++++++++++++++++++++++++
 drivers/staging/vme/devices/vme_pio2_gpio.c |  227 ++++++++++++
 6 files changed, 1084 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/vme/devices/vme_pio2.h
 create mode 100644 drivers/staging/vme/devices/vme_pio2_cntr.c
 create mode 100644 drivers/staging/vme/devices/vme_pio2_core.c
 create mode 100644 drivers/staging/vme/devices/vme_pio2_gpio.c


diff --git a/drivers/staging/vme/devices/Kconfig b/drivers/staging/vme/devices/Kconfig
index ca5ba89..99f5414 100644
--- a/drivers/staging/vme/devices/Kconfig
+++ b/drivers/staging/vme/devices/Kconfig
@@ -6,3 +6,13 @@ config VME_USER
 	  If you say Y here you want to be able to access a limited number of
 	  VME windows in a manner at least semi-compatible with the interface
 	  provided with the original driver at http://vmelinux.org/.
+config VME_PIO2
+	tristate "GE PIO2 VME"
+	help
+	  If you say Y here you have a GE PIO2. The PIO2 is a 6U VME Card,
+	  implementing 32 solid-state relay switched IO lines, in 4 groups of 8.
+	  The IO lines are provided as input, output or both as a build time
+	  option.
+
+	  Otherwise it is safe to say N here.
+
diff --git a/drivers/staging/vme/devices/Makefile b/drivers/staging/vme/devices/Makefile
index 459742a..172512c 100644
--- a/drivers/staging/vme/devices/Makefile
+++ b/drivers/staging/vme/devices/Makefile
@@ -3,3 +3,6 @@
 #
 
 obj-$(CONFIG_VME_USER)		+= vme_user.o
+
+vme_pio2-objs	:= vme_pio2_cntr.o vme_pio2_gpio.o vme_pio2_core.o
+obj-$(CONFIG_VME_PIO2)         += vme_pio2.o
diff --git a/drivers/staging/vme/devices/vme_pio2.h b/drivers/staging/vme/devices/vme_pio2.h
new file mode 100644
index 0000000..3c59313
--- /dev/null
+++ b/drivers/staging/vme/devices/vme_pio2.h
@@ -0,0 +1,249 @@
+#ifndef _VME_PIO2_H_
+#define _VME_PIO2_H_
+
+#define PIO2_CARDS_MAX			32
+
+#define PIO2_VARIANT_LENGTH		5
+
+#define PIO2_NUM_CHANNELS		32
+#define PIO2_NUM_IRQS			11
+#define PIO2_NUM_CNTRS			6
+
+#define PIO2_REGS_SIZE			0x40
+
+#define PIO2_REGS_DATA0			0x0
+#define PIO2_REGS_DATA1			0x1
+#define PIO2_REGS_DATA2			0x2
+#define PIO2_REGS_DATA3			0x3
+
+static const int PIO2_REGS_DATA[4] = { PIO2_REGS_DATA0, PIO2_REGS_DATA1,
+					PIO2_REGS_DATA2, PIO2_REGS_DATA3 };
+
+#define PIO2_REGS_INT_STAT0		0x8
+#define PIO2_REGS_INT_STAT1		0x9
+#define PIO2_REGS_INT_STAT2		0xa
+#define PIO2_REGS_INT_STAT3		0xb
+
+static const int PIO2_REGS_INT_STAT[4] = { PIO2_REGS_INT_STAT0,
+					PIO2_REGS_INT_STAT1,
+					PIO2_REGS_INT_STAT2,
+					PIO2_REGS_INT_STAT3 };
+
+#define PIO2_REGS_INT_STAT_CNTR		0xc
+#define PIO2_REGS_INT_MASK0		0x10
+#define PIO2_REGS_INT_MASK1		0x11
+#define PIO2_REGS_INT_MASK2		0x12
+#define PIO2_REGS_INT_MASK3		0x13
+#define PIO2_REGS_INT_MASK4		0x14
+#define PIO2_REGS_INT_MASK5		0x15
+#define PIO2_REGS_INT_MASK6		0x16
+#define PIO2_REGS_INT_MASK7		0x17
+
+static const int PIO2_REGS_INT_MASK[8] = { PIO2_REGS_INT_MASK0,
+					PIO2_REGS_INT_MASK1,
+					PIO2_REGS_INT_MASK2,
+					PIO2_REGS_INT_MASK3,
+					PIO2_REGS_INT_MASK4,
+					PIO2_REGS_INT_MASK5,
+					PIO2_REGS_INT_MASK6,
+					PIO2_REGS_INT_MASK7 };
+
+
+
+#define PIO2_REGS_CTRL			0x18
+#define PIO2_REGS_VME_VECTOR		0x19
+#define PIO2_REGS_CNTR0			0x20
+#define PIO2_REGS_CNTR1			0x22
+#define PIO2_REGS_CNTR2			0x24
+#define PIO2_REGS_CTRL_WRD0		0x26
+#define PIO2_REGS_CNTR3			0x28
+#define PIO2_REGS_CNTR4			0x2a
+#define PIO2_REGS_CNTR5			0x2c
+#define PIO2_REGS_CTRL_WRD1		0x2e
+
+#define PIO2_REGS_ID			0x30
+
+
+/* PIO2_REGS_DATAx (0x0 - 0x3) */
+
+static const int PIO2_CHANNEL_BANK[32] = { 0, 0, 0, 0, 0, 0, 0, 0,
+					1, 1, 1, 1, 1, 1, 1, 1,
+					2, 2, 2, 2, 2, 2, 2, 2,
+					3, 3, 3, 3, 3, 3, 3, 3 };
+
+#define PIO2_CHANNEL0_BIT		(1 << 0)
+#define PIO2_CHANNEL1_BIT		(1 << 1)
+#define PIO2_CHANNEL2_BIT		(1 << 2)
+#define PIO2_CHANNEL3_BIT		(1 << 3)
+#define PIO2_CHANNEL4_BIT		(1 << 4)
+#define PIO2_CHANNEL5_BIT		(1 << 5)
+#define PIO2_CHANNEL6_BIT		(1 << 6)
+#define PIO2_CHANNEL7_BIT		(1 << 7)
+#define PIO2_CHANNEL8_BIT		(1 << 0)
+#define PIO2_CHANNEL9_BIT		(1 << 1)
+#define PIO2_CHANNEL10_BIT		(1 << 2)
+#define PIO2_CHANNEL11_BIT		(1 << 3)
+#define PIO2_CHANNEL12_BIT		(1 << 4)
+#define PIO2_CHANNEL13_BIT		(1 << 5)
+#define PIO2_CHANNEL14_BIT		(1 << 6)
+#define PIO2_CHANNEL15_BIT		(1 << 7)
+#define PIO2_CHANNEL16_BIT		(1 << 0)
+#define PIO2_CHANNEL17_BIT		(1 << 1)
+#define PIO2_CHANNEL18_BIT		(1 << 2)
+#define PIO2_CHANNEL19_BIT		(1 << 3)
+#define PIO2_CHANNEL20_BIT		(1 << 4)
+#define PIO2_CHANNEL21_BIT		(1 << 5)
+#define PIO2_CHANNEL22_BIT		(1 << 6)
+#define PIO2_CHANNEL23_BIT		(1 << 7)
+#define PIO2_CHANNEL24_BIT		(1 << 0)
+#define PIO2_CHANNEL25_BIT		(1 << 1)
+#define PIO2_CHANNEL26_BIT		(1 << 2)
+#define PIO2_CHANNEL27_BIT		(1 << 3)
+#define PIO2_CHANNEL28_BIT		(1 << 4)
+#define PIO2_CHANNEL29_BIT		(1 << 5)
+#define PIO2_CHANNEL30_BIT		(1 << 6)
+#define PIO2_CHANNEL31_BIT		(1 << 7)
+
+static const int PIO2_CHANNEL_BIT[32] = { PIO2_CHANNEL0_BIT, PIO2_CHANNEL1_BIT,
+					PIO2_CHANNEL2_BIT, PIO2_CHANNEL3_BIT,
+					PIO2_CHANNEL4_BIT, PIO2_CHANNEL5_BIT,
+					PIO2_CHANNEL6_BIT, PIO2_CHANNEL7_BIT,
+					PIO2_CHANNEL8_BIT, PIO2_CHANNEL9_BIT,
+					PIO2_CHANNEL10_BIT, PIO2_CHANNEL11_BIT,
+					PIO2_CHANNEL12_BIT, PIO2_CHANNEL13_BIT,
+					PIO2_CHANNEL14_BIT, PIO2_CHANNEL15_BIT,
+					PIO2_CHANNEL16_BIT, PIO2_CHANNEL17_BIT,
+					PIO2_CHANNEL18_BIT, PIO2_CHANNEL19_BIT,
+					PIO2_CHANNEL20_BIT, PIO2_CHANNEL21_BIT,
+					PIO2_CHANNEL22_BIT, PIO2_CHANNEL23_BIT,
+					PIO2_CHANNEL24_BIT, PIO2_CHANNEL25_BIT,
+					PIO2_CHANNEL26_BIT, PIO2_CHANNEL27_BIT,
+					PIO2_CHANNEL28_BIT, PIO2_CHANNEL29_BIT,
+					PIO2_CHANNEL30_BIT, PIO2_CHANNEL31_BIT
+					};
+
+/* PIO2_REGS_INT_STAT_CNTR (0xc) */
+#define PIO2_COUNTER0			(1 << 0)
+#define PIO2_COUNTER1			(1 << 1)
+#define PIO2_COUNTER2			(1 << 2)
+#define PIO2_COUNTER3			(1 << 3)
+#define PIO2_COUNTER4			(1 << 4)
+#define PIO2_COUNTER5			(1 << 5)
+
+static const int PIO2_COUNTER[6] = { PIO2_COUNTER0, PIO2_COUNTER1,
+					PIO2_COUNTER2, PIO2_COUNTER3,
+					PIO2_COUNTER4, PIO2_COUNTER5 };
+
+/* PIO2_REGS_CTRL (0x18) */
+#define PIO2_VME_INT_MASK		0x7
+#define PIO2_LED			(1 << 6)
+#define PIO2_LOOP			(1 << 7)
+
+/* PIO2_REGS_VME_VECTOR (0x19) */
+#define PIO2_VME_VECTOR_SPUR		0x0
+#define PIO2_VME_VECTOR_BANK0		0x1
+#define PIO2_VME_VECTOR_BANK1		0x2
+#define PIO2_VME_VECTOR_BANK2		0x3
+#define PIO2_VME_VECTOR_BANK3		0x4
+#define PIO2_VME_VECTOR_CNTR0		0x5
+#define PIO2_VME_VECTOR_CNTR1		0x6
+#define PIO2_VME_VECTOR_CNTR2		0x7
+#define PIO2_VME_VECTOR_CNTR3		0x8
+#define PIO2_VME_VECTOR_CNTR4		0x9
+#define PIO2_VME_VECTOR_CNTR5		0xa
+
+#define PIO2_VME_VECTOR_MASK		0xf0
+
+static const int PIO2_VECTOR_BANK[4] = { PIO2_VME_VECTOR_BANK0,
+					PIO2_VME_VECTOR_BANK1,
+					PIO2_VME_VECTOR_BANK2,
+					PIO2_VME_VECTOR_BANK3 };
+
+static const int PIO2_VECTOR_CNTR[6] = { PIO2_VME_VECTOR_CNTR0,
+					PIO2_VME_VECTOR_CNTR1,
+					PIO2_VME_VECTOR_CNTR2,
+					PIO2_VME_VECTOR_CNTR3,
+					PIO2_VME_VECTOR_CNTR4,
+					PIO2_VME_VECTOR_CNTR5 };
+
+/* PIO2_REGS_CNTRx (0x20 - 0x24 & 0x28 - 0x2c) */
+
+static const int PIO2_CNTR_DATA[6] = { PIO2_REGS_CNTR0, PIO2_REGS_CNTR1,
+					PIO2_REGS_CNTR2, PIO2_REGS_CNTR3,
+					PIO2_REGS_CNTR4, PIO2_REGS_CNTR5 };
+
+/* PIO2_REGS_CTRL_WRDx (0x26 & 0x2e) */
+
+static const int PIO2_CNTR_CTRL[6] = { PIO2_REGS_CTRL_WRD0,
+					PIO2_REGS_CTRL_WRD0,
+					PIO2_REGS_CTRL_WRD0,
+					PIO2_REGS_CTRL_WRD1,
+					PIO2_REGS_CTRL_WRD1,
+					PIO2_REGS_CTRL_WRD1 };
+
+#define PIO2_CNTR_SC_DEV0		0
+#define PIO2_CNTR_SC_DEV1		(1 << 6)
+#define PIO2_CNTR_SC_DEV2		(2 << 6)
+#define PIO2_CNTR_SC_RDBACK		(3 << 6)
+
+static const int PIO2_CNTR_SC_DEV[6] = { PIO2_CNTR_SC_DEV0, PIO2_CNTR_SC_DEV1,
+					PIO2_CNTR_SC_DEV2, PIO2_CNTR_SC_DEV0,
+					PIO2_CNTR_SC_DEV1, PIO2_CNTR_SC_DEV2 };
+
+#define PIO2_CNTR_RW_LATCH		0
+#define PIO2_CNTR_RW_LSB		(1 << 4)
+#define PIO2_CNTR_RW_MSB		(2 << 4)
+#define PIO2_CNTR_RW_BOTH		(3 << 4)
+
+#define PIO2_CNTR_MODE0			0
+#define PIO2_CNTR_MODE1			(1 << 1)
+#define PIO2_CNTR_MODE2			(2 << 1)
+#define PIO2_CNTR_MODE3			(3 << 1)
+#define PIO2_CNTR_MODE4			(4 << 1)
+#define PIO2_CNTR_MODE5			(5 << 1)
+
+#define PIO2_CNTR_BCD			1
+
+
+
+enum pio2_bank_config { NOFIT, INPUT, OUTPUT, BOTH };
+enum pio2_int_config { NONE = 0, LOW2HIGH = 1, HIGH2LOW = 2, EITHER = 4 };
+
+/* Bank configuration structure */
+struct pio2_io_bank {
+	enum pio2_bank_config config;
+	u8 value;
+	enum pio2_int_config irq[8];
+};
+
+/* Counter configuration structure */
+struct pio2_cntr {
+	int mode;
+	int count;
+};
+
+struct pio2_card {
+	int id;
+	int bus;
+	long base;
+	int irq_vector;
+	int irq_level;
+	char variant[6];
+	int led;
+
+	struct vme_dev *vdev;
+	struct vme_resource *window;
+
+	struct gpio_chip gc;
+	struct pio2_io_bank bank[4];
+
+	struct pio2_cntr cntr[6];
+};
+
+int pio2_cntr_reset(struct pio2_card *);
+
+int pio2_gpio_reset(struct pio2_card *);
+int __init pio2_gpio_init(struct pio2_card *);
+void __exit pio2_gpio_exit(struct pio2_card *);
+
+#endif /* _VME_PIO2_H_ */
diff --git a/drivers/staging/vme/devices/vme_pio2_cntr.c b/drivers/staging/vme/devices/vme_pio2_cntr.c
new file mode 100644
index 0000000..08e0d59
--- /dev/null
+++ b/drivers/staging/vme/devices/vme_pio2_cntr.c
@@ -0,0 +1,71 @@
+/*
+ * GE PIO2 Counter Driver
+ *
+ * Author: Martyn Welch <martyn.welch@ge.com>
+ * Copyright 2009 GE Intelligent Platforms Embedded Systems, Inc.
+ *
+ * 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.
+ *
+ * The PIO-2 has 6 counters, currently this code just disables the interrupts
+ * and leaves them alone.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/gpio.h>
+
+#include "../vme.h"
+#include "vme_pio2.h"
+
+static int pio2_cntr_irq_set(struct pio2_card *card, int id)
+{
+	int retval;
+	u8 data;
+
+	data = PIO2_CNTR_SC_DEV[id] | PIO2_CNTR_RW_BOTH | card->cntr[id].mode;
+	retval = vme_master_write(card->window, &data, 1, PIO2_CNTR_CTRL[id]);
+	if (retval < 0)
+		return retval;
+
+	data = card->cntr[id].count & 0xFF;
+	retval = vme_master_write(card->window, &data, 1, PIO2_CNTR_DATA[id]);
+	if (retval < 0)
+		return retval;
+
+	data = (card->cntr[id].count >> 8) & 0xFF;
+	retval = vme_master_write(card->window, &data, 1, PIO2_CNTR_DATA[id]);
+	if (retval < 0)
+		return retval;
+
+	return 0;
+}
+
+int pio2_cntr_reset(struct pio2_card *card)
+{
+	int i, retval = 0;
+	u8 reg;
+
+	/* Clear down all timers */
+	for (i = 0; i < 6; i++) {
+		card->cntr[i].mode = PIO2_CNTR_MODE5;
+		card->cntr[i].count = 0;
+		retval = pio2_cntr_irq_set(card, i);
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Ensure all counter interrupts are cleared */
+	do {
+		retval = vme_master_read(card->window, &reg, 1,
+			PIO2_REGS_INT_STAT_CNTR);
+		if (retval < 0)
+			return retval;
+	} while (reg != 0);
+
+	return retval;
+}
+
diff --git a/drivers/staging/vme/devices/vme_pio2_core.c b/drivers/staging/vme/devices/vme_pio2_core.c
new file mode 100644
index 0000000..0b2db6a
--- /dev/null
+++ b/drivers/staging/vme/devices/vme_pio2_core.c
@@ -0,0 +1,524 @@
+/*
+ * GE PIO2 6U VME I/O Driver
+ *
+ * Author: Martyn Welch <martyn.welch@ge.com>
+ * Copyright 2009 GE Intelligent Platforms Embedded Systems, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/ctype.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#include "../vme.h"
+#include "vme_pio2.h"
+
+
+static const char driver_name[] = "pio2";
+
+static int bus[PIO2_CARDS_MAX];
+static int bus_num;
+static long base[PIO2_CARDS_MAX];
+static int base_num;
+static int vector[PIO2_CARDS_MAX];
+static int vector_num;
+static int level[PIO2_CARDS_MAX];
+static int level_num;
+static const char *variant[PIO2_CARDS_MAX];
+static int variant_num;
+
+static int loopback;
+
+static int pio2_match(struct vme_dev *);
+static int __devinit pio2_probe(struct vme_dev *);
+static int __devexit pio2_remove(struct vme_dev *);
+
+static int pio2_get_led(struct pio2_card *card)
+{
+	/* Can't read hardware, state saved in structure */
+	return card->led;
+}
+
+static int pio2_set_led(struct pio2_card *card, int state)
+{
+	u8 reg;
+	int retval;
+
+	reg = card->irq_level;
+
+	/* Register state inverse of led state */
+	if (!state)
+		reg |= PIO2_LED;
+
+	if (loopback)
+		reg |= PIO2_LOOP;
+
+	retval = vme_master_write(card->window, &reg, 1, PIO2_REGS_CTRL);
+	if (retval < 0)
+		return retval;
+
+	card->led = state ? 1 : 0;
+
+	return 0;
+}
+
+static void pio2_int(int level, int vector, void *ptr)
+{
+	int vec, i, channel, retval;
+	u8 reg;
+	struct pio2_card *card  = ptr;
+
+	vec = vector & ~PIO2_VME_VECTOR_MASK;
+
+	switch (vec) {
+	case 0:
+		dev_warn(&card->vdev->dev, "Spurious Interrupt\n");
+		break;
+	case 1:
+	case 2:
+	case 3:
+	case 4:
+		/* Channels 0 to 7 */
+		retval = vme_master_read(card->window, &reg, 1,
+			PIO2_REGS_INT_STAT[vec - 1]);
+		if (retval < 0) {
+			dev_err(&card->vdev->dev,
+				"Unable to read IRQ status register\n");
+			return;
+		}
+		for (i = 0; i < 8; i++) {
+			channel = ((vec - 1) * 8) + i;
+			if (reg & PIO2_CHANNEL_BIT[channel])
+				dev_info(&card->vdev->dev,
+					"Interrupt on I/O channel %d\n",
+					channel);
+		}
+		break;
+	case 5:
+	case 6:
+	case 7:
+	case 8:
+	case 9:
+	case 10:
+		/* Counters are dealt with by their own handler */
+		dev_err(&card->vdev->dev,
+			"Counter interrupt\n");
+		break;
+	}
+}
+
+
+/*
+ * We return whether this has been successful - this is used in the probe to
+ * ensure we have a valid card.
+ */
+static int pio2_reset_card(struct pio2_card *card)
+{
+	int retval = 0;
+	u8 data = 0;
+
+	/* Clear main register*/
+	retval = vme_master_write(card->window, &data, 1, PIO2_REGS_CTRL);
+	if (retval < 0)
+		return retval;
+
+	/* Clear VME vector */
+	retval = vme_master_write(card->window, &data, 1, PIO2_REGS_VME_VECTOR);
+	if (retval < 0)
+		return retval;
+
+	/* Reset GPIO */
+	retval = pio2_gpio_reset(card);
+	if (retval < 0)
+		return retval;
+
+	/* Reset counters */
+	retval = pio2_cntr_reset(card);
+	if (retval < 0)
+		return retval;
+
+	return 0;
+}
+
+static struct vme_driver pio2_driver = {
+	.name = driver_name,
+	.match = pio2_match,
+	.probe = pio2_probe,
+	.remove = __devexit_p(pio2_remove),
+};
+
+
+static int __init pio2_init(void)
+{
+	int retval = 0;
+
+	if (bus_num == 0) {
+		printk(KERN_ERR "%s: No cards, skipping registration\n",
+			driver_name);
+		goto err_nocard;
+	}
+
+	if (bus_num > PIO2_CARDS_MAX) {
+		printk(KERN_ERR
+			"%s: Driver only able to handle %d PIO2 Cards\n",
+			driver_name, PIO2_CARDS_MAX);
+		bus_num = PIO2_CARDS_MAX;
+	}
+
+	/* Register the PIO2 driver */
+	retval = vme_register_driver(&pio2_driver, bus_num);
+	if (retval != 0)
+		goto err_reg;
+
+	return retval;
+
+err_reg:
+err_nocard:
+	return retval;
+}
+
+static int pio2_match(struct vme_dev *vdev)
+{
+
+	if (vdev->num >= bus_num) {
+		dev_err(&vdev->dev,
+			"The enumeration of the VMEbus to which the board is connected must be specified");
+		return 0;
+	}
+
+	if (vdev->num >= base_num) {
+		dev_err(&vdev->dev,
+			"The VME address for the cards registers must be specified");
+		return 0;
+	}
+
+	if (vdev->num >= vector_num) {
+		dev_err(&vdev->dev,
+			"The IRQ vector used by the card must be specified");
+		return 0;
+	}
+
+	if (vdev->num >= level_num) {
+		dev_err(&vdev->dev,
+			"The IRQ level used by the card must be specified");
+		return 0;
+	}
+
+	if (vdev->num >= variant_num) {
+		dev_err(&vdev->dev, "The variant of the card must be specified");
+		return 0;
+	}
+
+	return 1;
+}
+
+static int __devinit pio2_probe(struct vme_dev *vdev)
+{
+	struct pio2_card *card;
+	int retval;
+	int i;
+	u8 reg;
+	int vec;
+
+	card = kzalloc(sizeof(struct pio2_card), GFP_KERNEL);
+	if (card == NULL) {
+		dev_err(&vdev->dev, "Unable to allocate card structure\n");
+		retval = -ENOMEM;
+		goto err_struct;
+	}
+
+	card->id = vdev->num;
+	card->bus = bus[card->id];
+	card->base = base[card->id];
+	card->irq_vector = vector[card->id];
+	card->irq_level = level[card->id] & PIO2_VME_INT_MASK;
+	strncpy(card->variant, variant[card->id], PIO2_VARIANT_LENGTH);
+	card->vdev = vdev;
+
+	for (i = 0; i < PIO2_VARIANT_LENGTH; i++) {
+
+		if (isdigit(card->variant[i]) == 0) {
+			dev_err(&card->vdev->dev, "Variant invalid\n");
+			retval = -EINVAL;
+			goto err_variant;
+		}
+	}
+
+	/*
+	 * Bottom 4 bits of VME interrupt vector used to determine source,
+	 * provided vector should only use upper 4 bits.
+	 */
+	if (card->irq_vector & ~PIO2_VME_VECTOR_MASK) {
+		dev_err(&card->vdev->dev,
+			"Invalid VME IRQ Vector, vector must not use lower 4 bits\n");
+		retval = -EINVAL;
+		goto err_vector;
+	}
+
+	/*
+	 * There is no way to determine the build variant or whether each bank
+	 * is input, output or both at run time. The inputs are also inverted
+	 * if configured as both.
+	 *
+	 * We pass in the board variant and use that to determine the
+	 * configuration of the banks.
+	 */
+	for (i = 1; i < PIO2_VARIANT_LENGTH; i++) {
+		switch (card->variant[i]) {
+		case '0':
+			card->bank[i-1].config = NOFIT;
+			break;
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+			card->bank[i-1].config = INPUT;
+			break;
+		case '5':
+			card->bank[i-1].config = OUTPUT;
+			break;
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+			card->bank[i-1].config = BOTH;
+			break;
+		}
+	}
+
+	/* Get a master window and position over regs */
+	card->window = vme_master_request(vdev, VME_A24, VME_SCT, VME_D16);
+	if (card->window == NULL) {
+		dev_err(&card->vdev->dev,
+			"Unable to assign VME master resource\n");
+		retval = -EIO;
+		goto err_window;
+	}
+
+	retval = vme_master_set(card->window, 1, card->base, 0x10000, VME_A24,
+		(VME_SCT | VME_USER | VME_DATA), VME_D16);
+	if (retval) {
+		dev_err(&card->vdev->dev,
+			"Unable to configure VME master resource\n");
+		goto err_set;
+	}
+
+	/*
+	 * There is also no obvious register which we can probe to determine
+	 * whether the provided base is valid. If we can read the "ID Register"
+	 * offset and the reset function doesn't error, assume we have a valid
+	 * location.
+	 */
+	retval = vme_master_read(card->window, &reg, 1, PIO2_REGS_ID);
+	if (retval < 0) {
+		dev_err(&card->vdev->dev, "Unable to read from device\n");
+		goto err_read;
+	}
+
+	dev_dbg(&card->vdev->dev, "ID Register:%x\n", reg);
+
+	/*
+	 * Ensure all the I/O is cleared. We can't read back the states, so
+	 * this is the only method we have to ensure that the I/O is in a known
+	 * state.
+	 */
+	retval = pio2_reset_card(card);
+	if (retval) {
+		dev_err(&card->vdev->dev,
+			"Failed to reset card, is location valid?");
+		retval = -ENODEV;
+		goto err_reset;
+	}
+
+	/* Configure VME Interrupts */
+	reg = card->irq_level;
+	if (pio2_get_led(card))
+		reg |= PIO2_LED;
+	if (loopback)
+		reg |= PIO2_LOOP;
+	retval = vme_master_write(card->window, &reg, 1, PIO2_REGS_CTRL);
+	if (retval < 0)
+		return retval;
+
+	/* Set VME vector */
+	retval = vme_master_write(card->window, &card->irq_vector, 1,
+		PIO2_REGS_VME_VECTOR);
+	if (retval < 0)
+		return retval;
+
+	/* Attach spurious interrupt handler. */
+	vec = card->irq_vector | PIO2_VME_VECTOR_SPUR;
+
+	retval = vme_irq_request(vdev, card->irq_level, vec,
+		&pio2_int, (void *)card);
+	if (retval < 0) {
+		dev_err(&card->vdev->dev,
+			"Unable to attach VME interrupt vector0x%x, level 0x%x\n",
+			 vec, card->irq_level);
+		goto err_irq;
+	}
+
+	/* Attach GPIO interrupt handlers. */
+	for (i = 0; i < 4; i++) {
+		vec = card->irq_vector | PIO2_VECTOR_BANK[i];
+
+		retval = vme_irq_request(vdev, card->irq_level, vec,
+			&pio2_int, (void *)card);
+		if (retval < 0) {
+			dev_err(&card->vdev->dev,
+				"Unable to attach VME interrupt vector0x%x, level 0x%x\n",
+				 vec, card->irq_level);
+			goto err_gpio_irq;
+		}
+	}
+
+	/* Attach counter interrupt handlers. */
+	for (i = 0; i < 6; i++) {
+		vec = card->irq_vector | PIO2_VECTOR_CNTR[i];
+
+		retval = vme_irq_request(vdev, card->irq_level, vec,
+			&pio2_int, (void *)card);
+		if (retval < 0) {
+			dev_err(&card->vdev->dev,
+				"Unable to attach VME interrupt vector0x%x, level 0x%x\n",
+				vec, card->irq_level);
+			goto err_cntr_irq;
+		}
+	}
+
+	/* Register IO */
+	retval = pio2_gpio_init(card);
+	if (retval < 0) {
+		dev_err(&card->vdev->dev,
+			"Unable to register with GPIO framework\n");
+		goto err_gpio;
+	}
+
+	/* Set LED - This also sets interrupt level */
+	retval = pio2_set_led(card, 0);
+	if (retval < 0) {
+		dev_err(&card->vdev->dev, "Unable to set LED\n");
+		goto err_led;
+	}
+
+	dev_set_drvdata(&card->vdev->dev, card);
+
+	dev_info(&card->vdev->dev,
+		"PIO2 (variant %s) configured at 0x%lx\n", card->variant,
+		card->base);
+
+	return 0;
+
+err_led:
+	pio2_gpio_exit(card);
+err_gpio:
+	i = 6;
+err_cntr_irq:
+	while (i > 0) {
+		i--;
+		vec = card->irq_vector | PIO2_VECTOR_CNTR[i];
+		vme_irq_free(vdev, card->irq_level, vec);
+	}
+
+	i = 4;
+err_gpio_irq:
+	while (i > 0) {
+		i--;
+		vec = card->irq_vector | PIO2_VECTOR_BANK[i];
+		vme_irq_free(vdev, card->irq_level, vec);
+	}
+
+	vec = (card->irq_vector & PIO2_VME_VECTOR_MASK) | PIO2_VME_VECTOR_SPUR;
+	vme_irq_free(vdev, card->irq_level, vec);
+err_irq:
+	 pio2_reset_card(card);
+err_reset:
+err_read:
+	vme_master_set(card->window, 0, 0, 0, VME_A16, 0, VME_D16);
+err_set:
+	vme_master_free(card->window);
+err_window:
+err_vector:
+err_variant:
+	kfree(card);
+err_struct:
+	return retval;
+}
+
+static int __devexit pio2_remove(struct vme_dev *vdev)
+{
+	int vec;
+	int i;
+
+	struct pio2_card *card = dev_get_drvdata(&vdev->dev);
+
+	pio2_gpio_exit(card);
+
+	for (i = 0; i < 6; i++) {
+		vec = card->irq_vector | PIO2_VECTOR_CNTR[i];
+		vme_irq_free(vdev, card->irq_level, vec);
+	}
+
+	for (i = 0; i < 4; i++) {
+		vec = card->irq_vector | PIO2_VECTOR_BANK[i];
+		vme_irq_free(vdev, card->irq_level, vec);
+	}
+
+	vec = (card->irq_vector & PIO2_VME_VECTOR_MASK) | PIO2_VME_VECTOR_SPUR;
+	vme_irq_free(vdev, card->irq_level, vec);
+
+	pio2_reset_card(card);
+
+	vme_master_set(card->window, 0, 0, 0, VME_A16, 0, VME_D16);
+
+	vme_master_free(card->window);
+
+	kfree(card);
+
+	return 0;
+}
+
+static void __exit pio2_exit(void)
+{
+	vme_unregister_driver(&pio2_driver);
+}
+
+
+/* These are required for each board */
+MODULE_PARM_DESC(bus, "Enumeration of VMEbus to which the board is connected");
+module_param_array(bus, int, &bus_num, 0);
+
+MODULE_PARM_DESC(base, "Base VME address for PIO2 Registers");
+module_param_array(base, long, &base_num, 0);
+
+MODULE_PARM_DESC(vector, "VME IRQ Vector (Lower 4 bits masked)");
+module_param_array(vector, int, &vector_num, 0);
+
+MODULE_PARM_DESC(level, "VME IRQ Level");
+module_param_array(level, int, &level_num, 0);
+
+MODULE_PARM_DESC(variant, "Last 4 characters of PIO2 board variant");
+module_param_array(variant, charp, &variant_num, 0);
+
+/* This is for debugging */
+MODULE_PARM_DESC(loopback, "Enable loopback mode on all cards");
+module_param(loopback, bool, 0);
+
+MODULE_DESCRIPTION("GE PIO2 6U VME I/O Driver");
+MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com");
+MODULE_LICENSE("GPL");
+
+module_init(pio2_init);
+module_exit(pio2_exit);
+
diff --git a/drivers/staging/vme/devices/vme_pio2_gpio.c b/drivers/staging/vme/devices/vme_pio2_gpio.c
new file mode 100644
index 0000000..2ac88ad
--- /dev/null
+++ b/drivers/staging/vme/devices/vme_pio2_gpio.c
@@ -0,0 +1,227 @@
+/*
+ * GE PIO2 GPIO Driver
+ *
+ * Author: Martyn Welch <martyn.welch@ge.com>
+ * Copyright 2009 GE Intelligent Platforms Embedded Systems, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/ctype.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#include "../vme.h"
+#include "vme_pio2.h"
+
+static const char driver_name[] = "pio2_gpio";
+
+static struct pio2_card *gpio_to_pio2_card(struct gpio_chip *chip)
+{
+	return container_of(chip, struct pio2_card, gc);
+}
+
+static int pio2_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+	u8 reg;
+	int retval;
+	struct pio2_card *card = gpio_to_pio2_card(chip);
+
+	if ((card->bank[PIO2_CHANNEL_BANK[offset]].config == OUTPUT) |
+		(card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
+
+		dev_err(&card->vdev->dev, "Channel not available as input\n");
+		return 0;
+	}
+
+	retval = vme_master_read(card->window, &reg, 1,
+		PIO2_REGS_DATA[PIO2_CHANNEL_BANK[offset]]);
+	if (retval < 0) {
+		dev_err(&card->vdev->dev, "Unable to read from GPIO\n");
+		return 0;
+	}
+
+	/*
+	 * Remember, input on channels configured as both input and output
+	 * are inverted!
+	 */
+	if (reg & PIO2_CHANNEL_BIT[offset]) {
+		if (card->bank[PIO2_CHANNEL_BANK[offset]].config != BOTH)
+			return 0;
+		else
+			return 1;
+	} else {
+		if (card->bank[PIO2_CHANNEL_BANK[offset]].config != BOTH)
+			return 1;
+		else
+			return 0;
+	}
+}
+
+static void pio2_gpio_set(struct gpio_chip *chip, unsigned int offset,
+	int value)
+{
+	u8 reg;
+	int retval;
+	struct pio2_card *card = gpio_to_pio2_card(chip);
+
+	if ((card->bank[PIO2_CHANNEL_BANK[offset]].config == INPUT) |
+		(card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
+
+		dev_err(&card->vdev->dev, "Channel not availabe as output\n");
+		return;
+	}
+
+	if (value)
+		reg = card->bank[PIO2_CHANNEL_BANK[offset]].value |
+			PIO2_CHANNEL_BIT[offset];
+	else
+		reg = card->bank[PIO2_CHANNEL_BANK[offset]].value &
+			~PIO2_CHANNEL_BIT[offset];
+
+	retval = vme_master_write(card->window, &reg, 1,
+		PIO2_REGS_DATA[PIO2_CHANNEL_BANK[offset]]);
+	if (retval < 0) {
+		dev_err(&card->vdev->dev, "Unable to write to GPIO\n");
+		return;
+	}
+
+	card->bank[PIO2_CHANNEL_BANK[offset]].value = reg;
+}
+
+/* Directionality configured at board build - send appropriate response */
+static int pio2_gpio_dir_in(struct gpio_chip *chip, unsigned offset)
+{
+	int data;
+	struct pio2_card *card = gpio_to_pio2_card(chip);
+
+	if ((card->bank[PIO2_CHANNEL_BANK[offset]].config == OUTPUT) |
+		(card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
+		dev_err(&card->vdev->dev,
+			"Channel directionality not configurable at runtine\n");
+
+		data = -EINVAL;
+	} else {
+		data = 0;
+	}
+
+	return data;
+}
+
+/* Directionality configured at board build - send appropriate response */
+static int pio2_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int value)
+{
+	int data;
+	struct pio2_card *card = gpio_to_pio2_card(chip);
+
+	if ((card->bank[PIO2_CHANNEL_BANK[offset]].config == INPUT) |
+		(card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
+		dev_err(&card->vdev->dev,
+			"Channel directionality not configurable at runtine\n");
+
+		data = -EINVAL;
+	} else {
+		data = 0;
+	}
+
+	return data;
+}
+
+/*
+ * We return whether this has been successful - this is used in the probe to
+ * ensure we have a valid card.
+ */
+int pio2_gpio_reset(struct pio2_card *card)
+{
+	int retval = 0;
+	int i, j;
+
+	u8 data = 0;
+
+	/* Zero output registers */
+	for (i = 0; i < 4; i++) {
+		retval = vme_master_write(card->window, &data, 1,
+			PIO2_REGS_DATA[i]);
+		if (retval < 0)
+			return retval;
+		card->bank[i].value = 0;
+	}
+
+	/* Set input interrupt masks */
+	for (i = 0; i < 8; i++) {
+		retval = vme_master_write(card->window, &data, 1,
+			PIO2_REGS_INT_MASK[i]);
+		if (retval < 0)
+			return retval;
+
+		for (j = 0; j < 8; j++)
+			card->bank[i].irq[j] = NONE;
+	}
+
+	/* Ensure all I/O interrupts are cleared */
+	for (i = 0; i < 4; i++) {
+		do {
+			retval = vme_master_read(card->window, &data, 1,
+				PIO2_REGS_INT_STAT[i]);
+			if (retval < 0)
+				return retval;
+		} while (data != 0);
+	}
+
+	return 0;
+}
+
+int __init pio2_gpio_init(struct pio2_card *card)
+{
+	int retval = 0;
+	char *label;
+
+	label = kmalloc(PIO2_NUM_CHANNELS, GFP_KERNEL);
+	if (label == NULL) {
+		dev_err(&card->vdev->dev, "Unable to allocate GPIO label\n");
+		return -ENOMEM;
+	}
+
+	sprintf(label, "%s@%s", driver_name, dev_name(&card->vdev->dev));
+	card->gc.label = label;
+
+	card->gc.ngpio = PIO2_NUM_CHANNELS;
+	/* Dynamic allocation of base */
+	card->gc.base = -1;
+	/* Setup pointers to chip functions */
+	card->gc.direction_input = pio2_gpio_dir_in;
+	card->gc.direction_output = pio2_gpio_dir_out;
+	card->gc.get = pio2_gpio_get;
+	card->gc.set = pio2_gpio_set;
+
+	/* This function adds a memory mapped GPIO chip */
+	retval = gpiochip_add(&(card->gc));
+	if (retval) {
+		dev_err(&card->vdev->dev, "Unable to register GPIO\n");
+		kfree(card->gc.label);
+	}
+
+	return retval;
+};
+
+void __exit pio2_gpio_exit(struct pio2_card *card)
+{
+	const char *label = card->gc.label;
+
+	if (gpiochip_remove(&(card->gc)))
+		dev_err(&card->vdev->dev, "Failed to remove GPIO");
+
+	kfree(label);
+}
+


--
Martyn Welch (Principal Software Engineer)   |   Registered in England and
GE Intelligent Platforms                     |   Wales (3828642) at 100
T +44(0)127322748                            |   Barbirolli Square, Manchester,
E martyn.welch@ge.com                        |   M2 3AB  VAT:GB 927559189

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH] Driver for GE PIO2 VME Card
  2011-11-04 17:33 [PATCH] Driver for GE PIO2 VME Card Martyn Welch
@ 2011-11-04 17:38 ` Martyn Welch
  2011-11-04 19:55 ` Paul Bolle
  1 sibling, 0 replies; 9+ messages in thread
From: Martyn Welch @ 2011-11-04 17:38 UTC (permalink / raw)
  To: gregkh; +Cc: Grant Likely, devel, manohar.vanga, cota, linux-kernel

On 04/11/11 17:33, Martyn Welch wrote:
> From: Martyn Welch <martyn.welch@gefanuc.com>
> 

Agh, how did that get in there. As in the "signed-off-by" line my email
address is (and has been for a while now) "martyn.welch@ge.com".

Martyn

> This patch implements a driver for the GE PIO2 VME Parallel I/O Card.  This
> card is a 6U VME Card, implementing 32 solid-state relay switched IO lines,
> in 4 groups of 8. The IO lines are provided as input, output or both as a
> build time option.
> 
> Signed-off-by: Martyn Welch <martyn.welch@ge.com>
> ---
> 
> This patch provides basic support for the GPIO on the PIO-2 at this point it
> does not provide support for IRQs generated by GPIO or the 6 counters also
> provided by the board. However, it does provide a good example of how to write
> a VME driver for real hardware.
> 
>  drivers/staging/vme/devices/Kconfig         |   10 +
>  drivers/staging/vme/devices/Makefile        |    3 
>  drivers/staging/vme/devices/vme_pio2.h      |  249 +++++++++++++
>  drivers/staging/vme/devices/vme_pio2_cntr.c |   71 ++++
>  drivers/staging/vme/devices/vme_pio2_core.c |  524 +++++++++++++++++++++++++++
>  drivers/staging/vme/devices/vme_pio2_gpio.c |  227 ++++++++++++
>  6 files changed, 1084 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/vme/devices/vme_pio2.h
>  create mode 100644 drivers/staging/vme/devices/vme_pio2_cntr.c
>  create mode 100644 drivers/staging/vme/devices/vme_pio2_core.c
>  create mode 100644 drivers/staging/vme/devices/vme_pio2_gpio.c
> 
> 
> diff --git a/drivers/staging/vme/devices/Kconfig b/drivers/staging/vme/devices/Kconfig
> index ca5ba89..99f5414 100644
> --- a/drivers/staging/vme/devices/Kconfig
> +++ b/drivers/staging/vme/devices/Kconfig
> @@ -6,3 +6,13 @@ config VME_USER
>  	  If you say Y here you want to be able to access a limited number of
>  	  VME windows in a manner at least semi-compatible with the interface
>  	  provided with the original driver at http://vmelinux.org/.
> +config VME_PIO2
> +	tristate "GE PIO2 VME"
> +	help
> +	  If you say Y here you have a GE PIO2. The PIO2 is a 6U VME Card,
> +	  implementing 32 solid-state relay switched IO lines, in 4 groups of 8.
> +	  The IO lines are provided as input, output or both as a build time
> +	  option.
> +
> +	  Otherwise it is safe to say N here.
> +
> diff --git a/drivers/staging/vme/devices/Makefile b/drivers/staging/vme/devices/Makefile
> index 459742a..172512c 100644
> --- a/drivers/staging/vme/devices/Makefile
> +++ b/drivers/staging/vme/devices/Makefile
> @@ -3,3 +3,6 @@
>  #
>  
>  obj-$(CONFIG_VME_USER)		+= vme_user.o
> +
> +vme_pio2-objs	:= vme_pio2_cntr.o vme_pio2_gpio.o vme_pio2_core.o
> +obj-$(CONFIG_VME_PIO2)         += vme_pio2.o
> diff --git a/drivers/staging/vme/devices/vme_pio2.h b/drivers/staging/vme/devices/vme_pio2.h
> new file mode 100644
> index 0000000..3c59313
> --- /dev/null
> +++ b/drivers/staging/vme/devices/vme_pio2.h
> @@ -0,0 +1,249 @@
> +#ifndef _VME_PIO2_H_
> +#define _VME_PIO2_H_
> +
> +#define PIO2_CARDS_MAX			32
> +
> +#define PIO2_VARIANT_LENGTH		5
> +
> +#define PIO2_NUM_CHANNELS		32
> +#define PIO2_NUM_IRQS			11
> +#define PIO2_NUM_CNTRS			6
> +
> +#define PIO2_REGS_SIZE			0x40
> +
> +#define PIO2_REGS_DATA0			0x0
> +#define PIO2_REGS_DATA1			0x1
> +#define PIO2_REGS_DATA2			0x2
> +#define PIO2_REGS_DATA3			0x3
> +
> +static const int PIO2_REGS_DATA[4] = { PIO2_REGS_DATA0, PIO2_REGS_DATA1,
> +					PIO2_REGS_DATA2, PIO2_REGS_DATA3 };
> +
> +#define PIO2_REGS_INT_STAT0		0x8
> +#define PIO2_REGS_INT_STAT1		0x9
> +#define PIO2_REGS_INT_STAT2		0xa
> +#define PIO2_REGS_INT_STAT3		0xb
> +
> +static const int PIO2_REGS_INT_STAT[4] = { PIO2_REGS_INT_STAT0,
> +					PIO2_REGS_INT_STAT1,
> +					PIO2_REGS_INT_STAT2,
> +					PIO2_REGS_INT_STAT3 };
> +
> +#define PIO2_REGS_INT_STAT_CNTR		0xc
> +#define PIO2_REGS_INT_MASK0		0x10
> +#define PIO2_REGS_INT_MASK1		0x11
> +#define PIO2_REGS_INT_MASK2		0x12
> +#define PIO2_REGS_INT_MASK3		0x13
> +#define PIO2_REGS_INT_MASK4		0x14
> +#define PIO2_REGS_INT_MASK5		0x15
> +#define PIO2_REGS_INT_MASK6		0x16
> +#define PIO2_REGS_INT_MASK7		0x17
> +
> +static const int PIO2_REGS_INT_MASK[8] = { PIO2_REGS_INT_MASK0,
> +					PIO2_REGS_INT_MASK1,
> +					PIO2_REGS_INT_MASK2,
> +					PIO2_REGS_INT_MASK3,
> +					PIO2_REGS_INT_MASK4,
> +					PIO2_REGS_INT_MASK5,
> +					PIO2_REGS_INT_MASK6,
> +					PIO2_REGS_INT_MASK7 };
> +
> +
> +
> +#define PIO2_REGS_CTRL			0x18
> +#define PIO2_REGS_VME_VECTOR		0x19
> +#define PIO2_REGS_CNTR0			0x20
> +#define PIO2_REGS_CNTR1			0x22
> +#define PIO2_REGS_CNTR2			0x24
> +#define PIO2_REGS_CTRL_WRD0		0x26
> +#define PIO2_REGS_CNTR3			0x28
> +#define PIO2_REGS_CNTR4			0x2a
> +#define PIO2_REGS_CNTR5			0x2c
> +#define PIO2_REGS_CTRL_WRD1		0x2e
> +
> +#define PIO2_REGS_ID			0x30
> +
> +
> +/* PIO2_REGS_DATAx (0x0 - 0x3) */
> +
> +static const int PIO2_CHANNEL_BANK[32] = { 0, 0, 0, 0, 0, 0, 0, 0,
> +					1, 1, 1, 1, 1, 1, 1, 1,
> +					2, 2, 2, 2, 2, 2, 2, 2,
> +					3, 3, 3, 3, 3, 3, 3, 3 };
> +
> +#define PIO2_CHANNEL0_BIT		(1 << 0)
> +#define PIO2_CHANNEL1_BIT		(1 << 1)
> +#define PIO2_CHANNEL2_BIT		(1 << 2)
> +#define PIO2_CHANNEL3_BIT		(1 << 3)
> +#define PIO2_CHANNEL4_BIT		(1 << 4)
> +#define PIO2_CHANNEL5_BIT		(1 << 5)
> +#define PIO2_CHANNEL6_BIT		(1 << 6)
> +#define PIO2_CHANNEL7_BIT		(1 << 7)
> +#define PIO2_CHANNEL8_BIT		(1 << 0)
> +#define PIO2_CHANNEL9_BIT		(1 << 1)
> +#define PIO2_CHANNEL10_BIT		(1 << 2)
> +#define PIO2_CHANNEL11_BIT		(1 << 3)
> +#define PIO2_CHANNEL12_BIT		(1 << 4)
> +#define PIO2_CHANNEL13_BIT		(1 << 5)
> +#define PIO2_CHANNEL14_BIT		(1 << 6)
> +#define PIO2_CHANNEL15_BIT		(1 << 7)
> +#define PIO2_CHANNEL16_BIT		(1 << 0)
> +#define PIO2_CHANNEL17_BIT		(1 << 1)
> +#define PIO2_CHANNEL18_BIT		(1 << 2)
> +#define PIO2_CHANNEL19_BIT		(1 << 3)
> +#define PIO2_CHANNEL20_BIT		(1 << 4)
> +#define PIO2_CHANNEL21_BIT		(1 << 5)
> +#define PIO2_CHANNEL22_BIT		(1 << 6)
> +#define PIO2_CHANNEL23_BIT		(1 << 7)
> +#define PIO2_CHANNEL24_BIT		(1 << 0)
> +#define PIO2_CHANNEL25_BIT		(1 << 1)
> +#define PIO2_CHANNEL26_BIT		(1 << 2)
> +#define PIO2_CHANNEL27_BIT		(1 << 3)
> +#define PIO2_CHANNEL28_BIT		(1 << 4)
> +#define PIO2_CHANNEL29_BIT		(1 << 5)
> +#define PIO2_CHANNEL30_BIT		(1 << 6)
> +#define PIO2_CHANNEL31_BIT		(1 << 7)
> +
> +static const int PIO2_CHANNEL_BIT[32] = { PIO2_CHANNEL0_BIT, PIO2_CHANNEL1_BIT,
> +					PIO2_CHANNEL2_BIT, PIO2_CHANNEL3_BIT,
> +					PIO2_CHANNEL4_BIT, PIO2_CHANNEL5_BIT,
> +					PIO2_CHANNEL6_BIT, PIO2_CHANNEL7_BIT,
> +					PIO2_CHANNEL8_BIT, PIO2_CHANNEL9_BIT,
> +					PIO2_CHANNEL10_BIT, PIO2_CHANNEL11_BIT,
> +					PIO2_CHANNEL12_BIT, PIO2_CHANNEL13_BIT,
> +					PIO2_CHANNEL14_BIT, PIO2_CHANNEL15_BIT,
> +					PIO2_CHANNEL16_BIT, PIO2_CHANNEL17_BIT,
> +					PIO2_CHANNEL18_BIT, PIO2_CHANNEL19_BIT,
> +					PIO2_CHANNEL20_BIT, PIO2_CHANNEL21_BIT,
> +					PIO2_CHANNEL22_BIT, PIO2_CHANNEL23_BIT,
> +					PIO2_CHANNEL24_BIT, PIO2_CHANNEL25_BIT,
> +					PIO2_CHANNEL26_BIT, PIO2_CHANNEL27_BIT,
> +					PIO2_CHANNEL28_BIT, PIO2_CHANNEL29_BIT,
> +					PIO2_CHANNEL30_BIT, PIO2_CHANNEL31_BIT
> +					};
> +
> +/* PIO2_REGS_INT_STAT_CNTR (0xc) */
> +#define PIO2_COUNTER0			(1 << 0)
> +#define PIO2_COUNTER1			(1 << 1)
> +#define PIO2_COUNTER2			(1 << 2)
> +#define PIO2_COUNTER3			(1 << 3)
> +#define PIO2_COUNTER4			(1 << 4)
> +#define PIO2_COUNTER5			(1 << 5)
> +
> +static const int PIO2_COUNTER[6] = { PIO2_COUNTER0, PIO2_COUNTER1,
> +					PIO2_COUNTER2, PIO2_COUNTER3,
> +					PIO2_COUNTER4, PIO2_COUNTER5 };
> +
> +/* PIO2_REGS_CTRL (0x18) */
> +#define PIO2_VME_INT_MASK		0x7
> +#define PIO2_LED			(1 << 6)
> +#define PIO2_LOOP			(1 << 7)
> +
> +/* PIO2_REGS_VME_VECTOR (0x19) */
> +#define PIO2_VME_VECTOR_SPUR		0x0
> +#define PIO2_VME_VECTOR_BANK0		0x1
> +#define PIO2_VME_VECTOR_BANK1		0x2
> +#define PIO2_VME_VECTOR_BANK2		0x3
> +#define PIO2_VME_VECTOR_BANK3		0x4
> +#define PIO2_VME_VECTOR_CNTR0		0x5
> +#define PIO2_VME_VECTOR_CNTR1		0x6
> +#define PIO2_VME_VECTOR_CNTR2		0x7
> +#define PIO2_VME_VECTOR_CNTR3		0x8
> +#define PIO2_VME_VECTOR_CNTR4		0x9
> +#define PIO2_VME_VECTOR_CNTR5		0xa
> +
> +#define PIO2_VME_VECTOR_MASK		0xf0
> +
> +static const int PIO2_VECTOR_BANK[4] = { PIO2_VME_VECTOR_BANK0,
> +					PIO2_VME_VECTOR_BANK1,
> +					PIO2_VME_VECTOR_BANK2,
> +					PIO2_VME_VECTOR_BANK3 };
> +
> +static const int PIO2_VECTOR_CNTR[6] = { PIO2_VME_VECTOR_CNTR0,
> +					PIO2_VME_VECTOR_CNTR1,
> +					PIO2_VME_VECTOR_CNTR2,
> +					PIO2_VME_VECTOR_CNTR3,
> +					PIO2_VME_VECTOR_CNTR4,
> +					PIO2_VME_VECTOR_CNTR5 };
> +
> +/* PIO2_REGS_CNTRx (0x20 - 0x24 & 0x28 - 0x2c) */
> +
> +static const int PIO2_CNTR_DATA[6] = { PIO2_REGS_CNTR0, PIO2_REGS_CNTR1,
> +					PIO2_REGS_CNTR2, PIO2_REGS_CNTR3,
> +					PIO2_REGS_CNTR4, PIO2_REGS_CNTR5 };
> +
> +/* PIO2_REGS_CTRL_WRDx (0x26 & 0x2e) */
> +
> +static const int PIO2_CNTR_CTRL[6] = { PIO2_REGS_CTRL_WRD0,
> +					PIO2_REGS_CTRL_WRD0,
> +					PIO2_REGS_CTRL_WRD0,
> +					PIO2_REGS_CTRL_WRD1,
> +					PIO2_REGS_CTRL_WRD1,
> +					PIO2_REGS_CTRL_WRD1 };
> +
> +#define PIO2_CNTR_SC_DEV0		0
> +#define PIO2_CNTR_SC_DEV1		(1 << 6)
> +#define PIO2_CNTR_SC_DEV2		(2 << 6)
> +#define PIO2_CNTR_SC_RDBACK		(3 << 6)
> +
> +static const int PIO2_CNTR_SC_DEV[6] = { PIO2_CNTR_SC_DEV0, PIO2_CNTR_SC_DEV1,
> +					PIO2_CNTR_SC_DEV2, PIO2_CNTR_SC_DEV0,
> +					PIO2_CNTR_SC_DEV1, PIO2_CNTR_SC_DEV2 };
> +
> +#define PIO2_CNTR_RW_LATCH		0
> +#define PIO2_CNTR_RW_LSB		(1 << 4)
> +#define PIO2_CNTR_RW_MSB		(2 << 4)
> +#define PIO2_CNTR_RW_BOTH		(3 << 4)
> +
> +#define PIO2_CNTR_MODE0			0
> +#define PIO2_CNTR_MODE1			(1 << 1)
> +#define PIO2_CNTR_MODE2			(2 << 1)
> +#define PIO2_CNTR_MODE3			(3 << 1)
> +#define PIO2_CNTR_MODE4			(4 << 1)
> +#define PIO2_CNTR_MODE5			(5 << 1)
> +
> +#define PIO2_CNTR_BCD			1
> +
> +
> +
> +enum pio2_bank_config { NOFIT, INPUT, OUTPUT, BOTH };
> +enum pio2_int_config { NONE = 0, LOW2HIGH = 1, HIGH2LOW = 2, EITHER = 4 };
> +
> +/* Bank configuration structure */
> +struct pio2_io_bank {
> +	enum pio2_bank_config config;
> +	u8 value;
> +	enum pio2_int_config irq[8];
> +};
> +
> +/* Counter configuration structure */
> +struct pio2_cntr {
> +	int mode;
> +	int count;
> +};
> +
> +struct pio2_card {
> +	int id;
> +	int bus;
> +	long base;
> +	int irq_vector;
> +	int irq_level;
> +	char variant[6];
> +	int led;
> +
> +	struct vme_dev *vdev;
> +	struct vme_resource *window;
> +
> +	struct gpio_chip gc;
> +	struct pio2_io_bank bank[4];
> +
> +	struct pio2_cntr cntr[6];
> +};
> +
> +int pio2_cntr_reset(struct pio2_card *);
> +
> +int pio2_gpio_reset(struct pio2_card *);
> +int __init pio2_gpio_init(struct pio2_card *);
> +void __exit pio2_gpio_exit(struct pio2_card *);
> +
> +#endif /* _VME_PIO2_H_ */
> diff --git a/drivers/staging/vme/devices/vme_pio2_cntr.c b/drivers/staging/vme/devices/vme_pio2_cntr.c
> new file mode 100644
> index 0000000..08e0d59
> --- /dev/null
> +++ b/drivers/staging/vme/devices/vme_pio2_cntr.c
> @@ -0,0 +1,71 @@
> +/*
> + * GE PIO2 Counter Driver
> + *
> + * Author: Martyn Welch <martyn.welch@ge.com>
> + * Copyright 2009 GE Intelligent Platforms Embedded Systems, Inc.
> + *
> + * 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.
> + *
> + * The PIO-2 has 6 counters, currently this code just disables the interrupts
> + * and leaves them alone.
> + *
> + */
> +
> +#include <linux/device.h>
> +#include <linux/types.h>
> +#include <linux/gpio.h>
> +
> +#include "../vme.h"
> +#include "vme_pio2.h"
> +
> +static int pio2_cntr_irq_set(struct pio2_card *card, int id)
> +{
> +	int retval;
> +	u8 data;
> +
> +	data = PIO2_CNTR_SC_DEV[id] | PIO2_CNTR_RW_BOTH | card->cntr[id].mode;
> +	retval = vme_master_write(card->window, &data, 1, PIO2_CNTR_CTRL[id]);
> +	if (retval < 0)
> +		return retval;
> +
> +	data = card->cntr[id].count & 0xFF;
> +	retval = vme_master_write(card->window, &data, 1, PIO2_CNTR_DATA[id]);
> +	if (retval < 0)
> +		return retval;
> +
> +	data = (card->cntr[id].count >> 8) & 0xFF;
> +	retval = vme_master_write(card->window, &data, 1, PIO2_CNTR_DATA[id]);
> +	if (retval < 0)
> +		return retval;
> +
> +	return 0;
> +}
> +
> +int pio2_cntr_reset(struct pio2_card *card)
> +{
> +	int i, retval = 0;
> +	u8 reg;
> +
> +	/* Clear down all timers */
> +	for (i = 0; i < 6; i++) {
> +		card->cntr[i].mode = PIO2_CNTR_MODE5;
> +		card->cntr[i].count = 0;
> +		retval = pio2_cntr_irq_set(card, i);
> +		if (retval < 0)
> +			return retval;
> +	}
> +
> +	/* Ensure all counter interrupts are cleared */
> +	do {
> +		retval = vme_master_read(card->window, &reg, 1,
> +			PIO2_REGS_INT_STAT_CNTR);
> +		if (retval < 0)
> +			return retval;
> +	} while (reg != 0);
> +
> +	return retval;
> +}
> +
> diff --git a/drivers/staging/vme/devices/vme_pio2_core.c b/drivers/staging/vme/devices/vme_pio2_core.c
> new file mode 100644
> index 0000000..0b2db6a
> --- /dev/null
> +++ b/drivers/staging/vme/devices/vme_pio2_core.c
> @@ -0,0 +1,524 @@
> +/*
> + * GE PIO2 6U VME I/O Driver
> + *
> + * Author: Martyn Welch <martyn.welch@ge.com>
> + * Copyright 2009 GE Intelligent Platforms Embedded Systems, Inc.
> + *
> + * 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.
> + */
> +
> +#include <linux/version.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/device.h>
> +#include <linux/ctype.h>
> +#include <linux/gpio.h>
> +#include <linux/slab.h>
> +
> +#include "../vme.h"
> +#include "vme_pio2.h"
> +
> +
> +static const char driver_name[] = "pio2";
> +
> +static int bus[PIO2_CARDS_MAX];
> +static int bus_num;
> +static long base[PIO2_CARDS_MAX];
> +static int base_num;
> +static int vector[PIO2_CARDS_MAX];
> +static int vector_num;
> +static int level[PIO2_CARDS_MAX];
> +static int level_num;
> +static const char *variant[PIO2_CARDS_MAX];
> +static int variant_num;
> +
> +static int loopback;
> +
> +static int pio2_match(struct vme_dev *);
> +static int __devinit pio2_probe(struct vme_dev *);
> +static int __devexit pio2_remove(struct vme_dev *);
> +
> +static int pio2_get_led(struct pio2_card *card)
> +{
> +	/* Can't read hardware, state saved in structure */
> +	return card->led;
> +}
> +
> +static int pio2_set_led(struct pio2_card *card, int state)
> +{
> +	u8 reg;
> +	int retval;
> +
> +	reg = card->irq_level;
> +
> +	/* Register state inverse of led state */
> +	if (!state)
> +		reg |= PIO2_LED;
> +
> +	if (loopback)
> +		reg |= PIO2_LOOP;
> +
> +	retval = vme_master_write(card->window, &reg, 1, PIO2_REGS_CTRL);
> +	if (retval < 0)
> +		return retval;
> +
> +	card->led = state ? 1 : 0;
> +
> +	return 0;
> +}
> +
> +static void pio2_int(int level, int vector, void *ptr)
> +{
> +	int vec, i, channel, retval;
> +	u8 reg;
> +	struct pio2_card *card  = ptr;
> +
> +	vec = vector & ~PIO2_VME_VECTOR_MASK;
> +
> +	switch (vec) {
> +	case 0:
> +		dev_warn(&card->vdev->dev, "Spurious Interrupt\n");
> +		break;
> +	case 1:
> +	case 2:
> +	case 3:
> +	case 4:
> +		/* Channels 0 to 7 */
> +		retval = vme_master_read(card->window, &reg, 1,
> +			PIO2_REGS_INT_STAT[vec - 1]);
> +		if (retval < 0) {
> +			dev_err(&card->vdev->dev,
> +				"Unable to read IRQ status register\n");
> +			return;
> +		}
> +		for (i = 0; i < 8; i++) {
> +			channel = ((vec - 1) * 8) + i;
> +			if (reg & PIO2_CHANNEL_BIT[channel])
> +				dev_info(&card->vdev->dev,
> +					"Interrupt on I/O channel %d\n",
> +					channel);
> +		}
> +		break;
> +	case 5:
> +	case 6:
> +	case 7:
> +	case 8:
> +	case 9:
> +	case 10:
> +		/* Counters are dealt with by their own handler */
> +		dev_err(&card->vdev->dev,
> +			"Counter interrupt\n");
> +		break;
> +	}
> +}
> +
> +
> +/*
> + * We return whether this has been successful - this is used in the probe to
> + * ensure we have a valid card.
> + */
> +static int pio2_reset_card(struct pio2_card *card)
> +{
> +	int retval = 0;
> +	u8 data = 0;
> +
> +	/* Clear main register*/
> +	retval = vme_master_write(card->window, &data, 1, PIO2_REGS_CTRL);
> +	if (retval < 0)
> +		return retval;
> +
> +	/* Clear VME vector */
> +	retval = vme_master_write(card->window, &data, 1, PIO2_REGS_VME_VECTOR);
> +	if (retval < 0)
> +		return retval;
> +
> +	/* Reset GPIO */
> +	retval = pio2_gpio_reset(card);
> +	if (retval < 0)
> +		return retval;
> +
> +	/* Reset counters */
> +	retval = pio2_cntr_reset(card);
> +	if (retval < 0)
> +		return retval;
> +
> +	return 0;
> +}
> +
> +static struct vme_driver pio2_driver = {
> +	.name = driver_name,
> +	.match = pio2_match,
> +	.probe = pio2_probe,
> +	.remove = __devexit_p(pio2_remove),
> +};
> +
> +
> +static int __init pio2_init(void)
> +{
> +	int retval = 0;
> +
> +	if (bus_num == 0) {
> +		printk(KERN_ERR "%s: No cards, skipping registration\n",
> +			driver_name);
> +		goto err_nocard;
> +	}
> +
> +	if (bus_num > PIO2_CARDS_MAX) {
> +		printk(KERN_ERR
> +			"%s: Driver only able to handle %d PIO2 Cards\n",
> +			driver_name, PIO2_CARDS_MAX);
> +		bus_num = PIO2_CARDS_MAX;
> +	}
> +
> +	/* Register the PIO2 driver */
> +	retval = vme_register_driver(&pio2_driver, bus_num);
> +	if (retval != 0)
> +		goto err_reg;
> +
> +	return retval;
> +
> +err_reg:
> +err_nocard:
> +	return retval;
> +}
> +
> +static int pio2_match(struct vme_dev *vdev)
> +{
> +
> +	if (vdev->num >= bus_num) {
> +		dev_err(&vdev->dev,
> +			"The enumeration of the VMEbus to which the board is connected must be specified");
> +		return 0;
> +	}
> +
> +	if (vdev->num >= base_num) {
> +		dev_err(&vdev->dev,
> +			"The VME address for the cards registers must be specified");
> +		return 0;
> +	}
> +
> +	if (vdev->num >= vector_num) {
> +		dev_err(&vdev->dev,
> +			"The IRQ vector used by the card must be specified");
> +		return 0;
> +	}
> +
> +	if (vdev->num >= level_num) {
> +		dev_err(&vdev->dev,
> +			"The IRQ level used by the card must be specified");
> +		return 0;
> +	}
> +
> +	if (vdev->num >= variant_num) {
> +		dev_err(&vdev->dev, "The variant of the card must be specified");
> +		return 0;
> +	}
> +
> +	return 1;
> +}
> +
> +static int __devinit pio2_probe(struct vme_dev *vdev)
> +{
> +	struct pio2_card *card;
> +	int retval;
> +	int i;
> +	u8 reg;
> +	int vec;
> +
> +	card = kzalloc(sizeof(struct pio2_card), GFP_KERNEL);
> +	if (card == NULL) {
> +		dev_err(&vdev->dev, "Unable to allocate card structure\n");
> +		retval = -ENOMEM;
> +		goto err_struct;
> +	}
> +
> +	card->id = vdev->num;
> +	card->bus = bus[card->id];
> +	card->base = base[card->id];
> +	card->irq_vector = vector[card->id];
> +	card->irq_level = level[card->id] & PIO2_VME_INT_MASK;
> +	strncpy(card->variant, variant[card->id], PIO2_VARIANT_LENGTH);
> +	card->vdev = vdev;
> +
> +	for (i = 0; i < PIO2_VARIANT_LENGTH; i++) {
> +
> +		if (isdigit(card->variant[i]) == 0) {
> +			dev_err(&card->vdev->dev, "Variant invalid\n");
> +			retval = -EINVAL;
> +			goto err_variant;
> +		}
> +	}
> +
> +	/*
> +	 * Bottom 4 bits of VME interrupt vector used to determine source,
> +	 * provided vector should only use upper 4 bits.
> +	 */
> +	if (card->irq_vector & ~PIO2_VME_VECTOR_MASK) {
> +		dev_err(&card->vdev->dev,
> +			"Invalid VME IRQ Vector, vector must not use lower 4 bits\n");
> +		retval = -EINVAL;
> +		goto err_vector;
> +	}
> +
> +	/*
> +	 * There is no way to determine the build variant or whether each bank
> +	 * is input, output or both at run time. The inputs are also inverted
> +	 * if configured as both.
> +	 *
> +	 * We pass in the board variant and use that to determine the
> +	 * configuration of the banks.
> +	 */
> +	for (i = 1; i < PIO2_VARIANT_LENGTH; i++) {
> +		switch (card->variant[i]) {
> +		case '0':
> +			card->bank[i-1].config = NOFIT;
> +			break;
> +		case '1':
> +		case '2':
> +		case '3':
> +		case '4':
> +			card->bank[i-1].config = INPUT;
> +			break;
> +		case '5':
> +			card->bank[i-1].config = OUTPUT;
> +			break;
> +		case '6':
> +		case '7':
> +		case '8':
> +		case '9':
> +			card->bank[i-1].config = BOTH;
> +			break;
> +		}
> +	}
> +
> +	/* Get a master window and position over regs */
> +	card->window = vme_master_request(vdev, VME_A24, VME_SCT, VME_D16);
> +	if (card->window == NULL) {
> +		dev_err(&card->vdev->dev,
> +			"Unable to assign VME master resource\n");
> +		retval = -EIO;
> +		goto err_window;
> +	}
> +
> +	retval = vme_master_set(card->window, 1, card->base, 0x10000, VME_A24,
> +		(VME_SCT | VME_USER | VME_DATA), VME_D16);
> +	if (retval) {
> +		dev_err(&card->vdev->dev,
> +			"Unable to configure VME master resource\n");
> +		goto err_set;
> +	}
> +
> +	/*
> +	 * There is also no obvious register which we can probe to determine
> +	 * whether the provided base is valid. If we can read the "ID Register"
> +	 * offset and the reset function doesn't error, assume we have a valid
> +	 * location.
> +	 */
> +	retval = vme_master_read(card->window, &reg, 1, PIO2_REGS_ID);
> +	if (retval < 0) {
> +		dev_err(&card->vdev->dev, "Unable to read from device\n");
> +		goto err_read;
> +	}
> +
> +	dev_dbg(&card->vdev->dev, "ID Register:%x\n", reg);
> +
> +	/*
> +	 * Ensure all the I/O is cleared. We can't read back the states, so
> +	 * this is the only method we have to ensure that the I/O is in a known
> +	 * state.
> +	 */
> +	retval = pio2_reset_card(card);
> +	if (retval) {
> +		dev_err(&card->vdev->dev,
> +			"Failed to reset card, is location valid?");
> +		retval = -ENODEV;
> +		goto err_reset;
> +	}
> +
> +	/* Configure VME Interrupts */
> +	reg = card->irq_level;
> +	if (pio2_get_led(card))
> +		reg |= PIO2_LED;
> +	if (loopback)
> +		reg |= PIO2_LOOP;
> +	retval = vme_master_write(card->window, &reg, 1, PIO2_REGS_CTRL);
> +	if (retval < 0)
> +		return retval;
> +
> +	/* Set VME vector */
> +	retval = vme_master_write(card->window, &card->irq_vector, 1,
> +		PIO2_REGS_VME_VECTOR);
> +	if (retval < 0)
> +		return retval;
> +
> +	/* Attach spurious interrupt handler. */
> +	vec = card->irq_vector | PIO2_VME_VECTOR_SPUR;
> +
> +	retval = vme_irq_request(vdev, card->irq_level, vec,
> +		&pio2_int, (void *)card);
> +	if (retval < 0) {
> +		dev_err(&card->vdev->dev,
> +			"Unable to attach VME interrupt vector0x%x, level 0x%x\n",
> +			 vec, card->irq_level);
> +		goto err_irq;
> +	}
> +
> +	/* Attach GPIO interrupt handlers. */
> +	for (i = 0; i < 4; i++) {
> +		vec = card->irq_vector | PIO2_VECTOR_BANK[i];
> +
> +		retval = vme_irq_request(vdev, card->irq_level, vec,
> +			&pio2_int, (void *)card);
> +		if (retval < 0) {
> +			dev_err(&card->vdev->dev,
> +				"Unable to attach VME interrupt vector0x%x, level 0x%x\n",
> +				 vec, card->irq_level);
> +			goto err_gpio_irq;
> +		}
> +	}
> +
> +	/* Attach counter interrupt handlers. */
> +	for (i = 0; i < 6; i++) {
> +		vec = card->irq_vector | PIO2_VECTOR_CNTR[i];
> +
> +		retval = vme_irq_request(vdev, card->irq_level, vec,
> +			&pio2_int, (void *)card);
> +		if (retval < 0) {
> +			dev_err(&card->vdev->dev,
> +				"Unable to attach VME interrupt vector0x%x, level 0x%x\n",
> +				vec, card->irq_level);
> +			goto err_cntr_irq;
> +		}
> +	}
> +
> +	/* Register IO */
> +	retval = pio2_gpio_init(card);
> +	if (retval < 0) {
> +		dev_err(&card->vdev->dev,
> +			"Unable to register with GPIO framework\n");
> +		goto err_gpio;
> +	}
> +
> +	/* Set LED - This also sets interrupt level */
> +	retval = pio2_set_led(card, 0);
> +	if (retval < 0) {
> +		dev_err(&card->vdev->dev, "Unable to set LED\n");
> +		goto err_led;
> +	}
> +
> +	dev_set_drvdata(&card->vdev->dev, card);
> +
> +	dev_info(&card->vdev->dev,
> +		"PIO2 (variant %s) configured at 0x%lx\n", card->variant,
> +		card->base);
> +
> +	return 0;
> +
> +err_led:
> +	pio2_gpio_exit(card);
> +err_gpio:
> +	i = 6;
> +err_cntr_irq:
> +	while (i > 0) {
> +		i--;
> +		vec = card->irq_vector | PIO2_VECTOR_CNTR[i];
> +		vme_irq_free(vdev, card->irq_level, vec);
> +	}
> +
> +	i = 4;
> +err_gpio_irq:
> +	while (i > 0) {
> +		i--;
> +		vec = card->irq_vector | PIO2_VECTOR_BANK[i];
> +		vme_irq_free(vdev, card->irq_level, vec);
> +	}
> +
> +	vec = (card->irq_vector & PIO2_VME_VECTOR_MASK) | PIO2_VME_VECTOR_SPUR;
> +	vme_irq_free(vdev, card->irq_level, vec);
> +err_irq:
> +	 pio2_reset_card(card);
> +err_reset:
> +err_read:
> +	vme_master_set(card->window, 0, 0, 0, VME_A16, 0, VME_D16);
> +err_set:
> +	vme_master_free(card->window);
> +err_window:
> +err_vector:
> +err_variant:
> +	kfree(card);
> +err_struct:
> +	return retval;
> +}
> +
> +static int __devexit pio2_remove(struct vme_dev *vdev)
> +{
> +	int vec;
> +	int i;
> +
> +	struct pio2_card *card = dev_get_drvdata(&vdev->dev);
> +
> +	pio2_gpio_exit(card);
> +
> +	for (i = 0; i < 6; i++) {
> +		vec = card->irq_vector | PIO2_VECTOR_CNTR[i];
> +		vme_irq_free(vdev, card->irq_level, vec);
> +	}
> +
> +	for (i = 0; i < 4; i++) {
> +		vec = card->irq_vector | PIO2_VECTOR_BANK[i];
> +		vme_irq_free(vdev, card->irq_level, vec);
> +	}
> +
> +	vec = (card->irq_vector & PIO2_VME_VECTOR_MASK) | PIO2_VME_VECTOR_SPUR;
> +	vme_irq_free(vdev, card->irq_level, vec);
> +
> +	pio2_reset_card(card);
> +
> +	vme_master_set(card->window, 0, 0, 0, VME_A16, 0, VME_D16);
> +
> +	vme_master_free(card->window);
> +
> +	kfree(card);
> +
> +	return 0;
> +}
> +
> +static void __exit pio2_exit(void)
> +{
> +	vme_unregister_driver(&pio2_driver);
> +}
> +
> +
> +/* These are required for each board */
> +MODULE_PARM_DESC(bus, "Enumeration of VMEbus to which the board is connected");
> +module_param_array(bus, int, &bus_num, 0);
> +
> +MODULE_PARM_DESC(base, "Base VME address for PIO2 Registers");
> +module_param_array(base, long, &base_num, 0);
> +
> +MODULE_PARM_DESC(vector, "VME IRQ Vector (Lower 4 bits masked)");
> +module_param_array(vector, int, &vector_num, 0);
> +
> +MODULE_PARM_DESC(level, "VME IRQ Level");
> +module_param_array(level, int, &level_num, 0);
> +
> +MODULE_PARM_DESC(variant, "Last 4 characters of PIO2 board variant");
> +module_param_array(variant, charp, &variant_num, 0);
> +
> +/* This is for debugging */
> +MODULE_PARM_DESC(loopback, "Enable loopback mode on all cards");
> +module_param(loopback, bool, 0);
> +
> +MODULE_DESCRIPTION("GE PIO2 6U VME I/O Driver");
> +MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com");
> +MODULE_LICENSE("GPL");
> +
> +module_init(pio2_init);
> +module_exit(pio2_exit);
> +
> diff --git a/drivers/staging/vme/devices/vme_pio2_gpio.c b/drivers/staging/vme/devices/vme_pio2_gpio.c
> new file mode 100644
> index 0000000..2ac88ad
> --- /dev/null
> +++ b/drivers/staging/vme/devices/vme_pio2_gpio.c
> @@ -0,0 +1,227 @@
> +/*
> + * GE PIO2 GPIO Driver
> + *
> + * Author: Martyn Welch <martyn.welch@ge.com>
> + * Copyright 2009 GE Intelligent Platforms Embedded Systems, Inc.
> + *
> + * 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.
> + */
> +
> +#include <linux/version.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/ctype.h>
> +#include <linux/gpio.h>
> +#include <linux/slab.h>
> +
> +#include "../vme.h"
> +#include "vme_pio2.h"
> +
> +static const char driver_name[] = "pio2_gpio";
> +
> +static struct pio2_card *gpio_to_pio2_card(struct gpio_chip *chip)
> +{
> +	return container_of(chip, struct pio2_card, gc);
> +}
> +
> +static int pio2_gpio_get(struct gpio_chip *chip, unsigned int offset)
> +{
> +	u8 reg;
> +	int retval;
> +	struct pio2_card *card = gpio_to_pio2_card(chip);
> +
> +	if ((card->bank[PIO2_CHANNEL_BANK[offset]].config == OUTPUT) |
> +		(card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
> +
> +		dev_err(&card->vdev->dev, "Channel not available as input\n");
> +		return 0;
> +	}
> +
> +	retval = vme_master_read(card->window, &reg, 1,
> +		PIO2_REGS_DATA[PIO2_CHANNEL_BANK[offset]]);
> +	if (retval < 0) {
> +		dev_err(&card->vdev->dev, "Unable to read from GPIO\n");
> +		return 0;
> +	}
> +
> +	/*
> +	 * Remember, input on channels configured as both input and output
> +	 * are inverted!
> +	 */
> +	if (reg & PIO2_CHANNEL_BIT[offset]) {
> +		if (card->bank[PIO2_CHANNEL_BANK[offset]].config != BOTH)
> +			return 0;
> +		else
> +			return 1;
> +	} else {
> +		if (card->bank[PIO2_CHANNEL_BANK[offset]].config != BOTH)
> +			return 1;
> +		else
> +			return 0;
> +	}
> +}
> +
> +static void pio2_gpio_set(struct gpio_chip *chip, unsigned int offset,
> +	int value)
> +{
> +	u8 reg;
> +	int retval;
> +	struct pio2_card *card = gpio_to_pio2_card(chip);
> +
> +	if ((card->bank[PIO2_CHANNEL_BANK[offset]].config == INPUT) |
> +		(card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
> +
> +		dev_err(&card->vdev->dev, "Channel not availabe as output\n");
> +		return;
> +	}
> +
> +	if (value)
> +		reg = card->bank[PIO2_CHANNEL_BANK[offset]].value |
> +			PIO2_CHANNEL_BIT[offset];
> +	else
> +		reg = card->bank[PIO2_CHANNEL_BANK[offset]].value &
> +			~PIO2_CHANNEL_BIT[offset];
> +
> +	retval = vme_master_write(card->window, &reg, 1,
> +		PIO2_REGS_DATA[PIO2_CHANNEL_BANK[offset]]);
> +	if (retval < 0) {
> +		dev_err(&card->vdev->dev, "Unable to write to GPIO\n");
> +		return;
> +	}
> +
> +	card->bank[PIO2_CHANNEL_BANK[offset]].value = reg;
> +}
> +
> +/* Directionality configured at board build - send appropriate response */
> +static int pio2_gpio_dir_in(struct gpio_chip *chip, unsigned offset)
> +{
> +	int data;
> +	struct pio2_card *card = gpio_to_pio2_card(chip);
> +
> +	if ((card->bank[PIO2_CHANNEL_BANK[offset]].config == OUTPUT) |
> +		(card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
> +		dev_err(&card->vdev->dev,
> +			"Channel directionality not configurable at runtine\n");
> +
> +		data = -EINVAL;
> +	} else {
> +		data = 0;
> +	}
> +
> +	return data;
> +}
> +
> +/* Directionality configured at board build - send appropriate response */
> +static int pio2_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int value)
> +{
> +	int data;
> +	struct pio2_card *card = gpio_to_pio2_card(chip);
> +
> +	if ((card->bank[PIO2_CHANNEL_BANK[offset]].config == INPUT) |
> +		(card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
> +		dev_err(&card->vdev->dev,
> +			"Channel directionality not configurable at runtine\n");
> +
> +		data = -EINVAL;
> +	} else {
> +		data = 0;
> +	}
> +
> +	return data;
> +}
> +
> +/*
> + * We return whether this has been successful - this is used in the probe to
> + * ensure we have a valid card.
> + */
> +int pio2_gpio_reset(struct pio2_card *card)
> +{
> +	int retval = 0;
> +	int i, j;
> +
> +	u8 data = 0;
> +
> +	/* Zero output registers */
> +	for (i = 0; i < 4; i++) {
> +		retval = vme_master_write(card->window, &data, 1,
> +			PIO2_REGS_DATA[i]);
> +		if (retval < 0)
> +			return retval;
> +		card->bank[i].value = 0;
> +	}
> +
> +	/* Set input interrupt masks */
> +	for (i = 0; i < 8; i++) {
> +		retval = vme_master_write(card->window, &data, 1,
> +			PIO2_REGS_INT_MASK[i]);
> +		if (retval < 0)
> +			return retval;
> +
> +		for (j = 0; j < 8; j++)
> +			card->bank[i].irq[j] = NONE;
> +	}
> +
> +	/* Ensure all I/O interrupts are cleared */
> +	for (i = 0; i < 4; i++) {
> +		do {
> +			retval = vme_master_read(card->window, &data, 1,
> +				PIO2_REGS_INT_STAT[i]);
> +			if (retval < 0)
> +				return retval;
> +		} while (data != 0);
> +	}
> +
> +	return 0;
> +}
> +
> +int __init pio2_gpio_init(struct pio2_card *card)
> +{
> +	int retval = 0;
> +	char *label;
> +
> +	label = kmalloc(PIO2_NUM_CHANNELS, GFP_KERNEL);
> +	if (label == NULL) {
> +		dev_err(&card->vdev->dev, "Unable to allocate GPIO label\n");
> +		return -ENOMEM;
> +	}
> +
> +	sprintf(label, "%s@%s", driver_name, dev_name(&card->vdev->dev));
> +	card->gc.label = label;
> +
> +	card->gc.ngpio = PIO2_NUM_CHANNELS;
> +	/* Dynamic allocation of base */
> +	card->gc.base = -1;
> +	/* Setup pointers to chip functions */
> +	card->gc.direction_input = pio2_gpio_dir_in;
> +	card->gc.direction_output = pio2_gpio_dir_out;
> +	card->gc.get = pio2_gpio_get;
> +	card->gc.set = pio2_gpio_set;
> +
> +	/* This function adds a memory mapped GPIO chip */
> +	retval = gpiochip_add(&(card->gc));
> +	if (retval) {
> +		dev_err(&card->vdev->dev, "Unable to register GPIO\n");
> +		kfree(card->gc.label);
> +	}
> +
> +	return retval;
> +};
> +
> +void __exit pio2_gpio_exit(struct pio2_card *card)
> +{
> +	const char *label = card->gc.label;
> +
> +	if (gpiochip_remove(&(card->gc)))
> +		dev_err(&card->vdev->dev, "Failed to remove GPIO");
> +
> +	kfree(label);
> +}
> +
> 
> 
> --
> Martyn Welch (Principal Software Engineer)   |   Registered in England and
> GE Intelligent Platforms                     |   Wales (3828642) at 100
> T +44(0)127322748                            |   Barbirolli Square, Manchester,
> E martyn.welch@ge.com                        |   M2 3AB  VAT:GB 927559189
> _______________________________________________
> devel mailing list
> devel@linuxdriverproject.org
> http://driverdev.linuxdriverproject.org/mailman/listinfo/devel


-- 
Martyn Welch (Principal Software Engineer) | Registered in England and
GE Intelligent Platforms                   | Wales (3828642) at 100
T +44(0)1327322748                         | Barbirolli Square, Manchester,
E martyn.welch@ge.com                      | M2 3AB  VAT:GB 927559189

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Driver for GE PIO2 VME Card
  2011-11-04 17:33 [PATCH] Driver for GE PIO2 VME Card Martyn Welch
  2011-11-04 17:38 ` Martyn Welch
@ 2011-11-04 19:55 ` Paul Bolle
  2011-11-04 20:00   ` Greg KH
  2011-11-07  9:34   ` Martyn Welch
  1 sibling, 2 replies; 9+ messages in thread
From: Paul Bolle @ 2011-11-04 19:55 UTC (permalink / raw)
  To: Martyn Welch
  Cc: gregkh, Grant Likely, devel, manohar.vanga, cota, linux-kernel

A few random remarks follow. Ie, things that came up while quickly
scanning this.

On Fri, 2011-11-04 at 17:33 +0000, Martyn Welch wrote:
> diff --git a/drivers/staging/vme/devices/Kconfig b/drivers/staging/vme/devices/Kconfig
> index ca5ba89..99f5414 100644
> --- a/drivers/staging/vme/devices/Kconfig
> +++ b/drivers/staging/vme/devices/Kconfig
> @@ -6,3 +6,13 @@ config VME_USER
>  	  If you say Y here you want to be able to access a limited number of
>  	  VME windows in a manner at least semi-compatible with the interface
>  	  provided with the original driver at http://vmelinux.org/.

Blank line here.

> +config VME_PIO2
> +	tristate "GE PIO2 VME"
> +	help
> +	  If you say Y here you have a GE PIO2. The PIO2 is a 6U VME Card,

Maybe something like: "Say Y here if you have a GE PIO2."?

> +	  implementing 32 solid-state relay switched IO lines, in 4 groups of 8.
> +	  The IO lines are provided as input, output or both as a build time
> +	  option.

What option would that be?

> +	  Otherwise it is safe to say N here.
> +

A lot of modules have the "if you say M here the module will be called"
boilerplate here (which actually doesn't match the line I just made up).

> +/* These are required for each board */
> +MODULE_PARM_DESC(bus, "Enumeration of VMEbus to which the board is connected");
> +module_param_array(bus, int, &bus_num, 0);
> +
> +MODULE_PARM_DESC(base, "Base VME address for PIO2 Registers");
> +module_param_array(base, long, &base_num, 0);
> +
> +MODULE_PARM_DESC(vector, "VME IRQ Vector (Lower 4 bits masked)");
> +module_param_array(vector, int, &vector_num, 0);
> +
> +MODULE_PARM_DESC(level, "VME IRQ Level");
> +module_param_array(level, int, &level_num, 0);
> +
> +MODULE_PARM_DESC(variant, "Last 4 characters of PIO2 board variant");
> +module_param_array(variant, charp, &variant_num, 0);
> +
> +/* This is for debugging */
> +MODULE_PARM_DESC(loopback, "Enable loopback mode on all cards");
> +module_param(loopback, bool, 0);

All module parameters have a sysfs visibility (or permission) of zero.
Why is that? (This might very well be a naive question. But I often
wonder why a certain parameter's permission isn't at least 400, just to
allow a quick check of that parameter.) Are arrays tricky in sysfs?

I can't remember ever having seen guidelines for this. It's possible
they exist and my question is answered after reading those.


Paul Bolle


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Driver for GE PIO2 VME Card
  2011-11-04 19:55 ` Paul Bolle
@ 2011-11-04 20:00   ` Greg KH
  2011-11-07 10:37     ` Martyn Welch
  2011-11-07  9:34   ` Martyn Welch
  1 sibling, 1 reply; 9+ messages in thread
From: Greg KH @ 2011-11-04 20:00 UTC (permalink / raw)
  To: Paul Bolle
  Cc: Martyn Welch, Grant Likely, devel, manohar.vanga, cota,
	linux-kernel

On Fri, Nov 04, 2011 at 08:55:14PM +0100, Paul Bolle wrote:
> A few random remarks follow. Ie, things that came up while quickly
> scanning this.
> 
> On Fri, 2011-11-04 at 17:33 +0000, Martyn Welch wrote:
> > diff --git a/drivers/staging/vme/devices/Kconfig b/drivers/staging/vme/devices/Kconfig
> > index ca5ba89..99f5414 100644
> > --- a/drivers/staging/vme/devices/Kconfig
> > +++ b/drivers/staging/vme/devices/Kconfig
> > @@ -6,3 +6,13 @@ config VME_USER
> >  	  If you say Y here you want to be able to access a limited number of
> >  	  VME windows in a manner at least semi-compatible with the interface
> >  	  provided with the original driver at http://vmelinux.org/.
> 
> Blank line here.
> 
> > +config VME_PIO2
> > +	tristate "GE PIO2 VME"
> > +	help
> > +	  If you say Y here you have a GE PIO2. The PIO2 is a 6U VME Card,
> 
> Maybe something like: "Say Y here if you have a GE PIO2."?
> 
> > +	  implementing 32 solid-state relay switched IO lines, in 4 groups of 8.
> > +	  The IO lines are provided as input, output or both as a build time
> > +	  option.
> 
> What option would that be?
> 
> > +	  Otherwise it is safe to say N here.
> > +
> 
> A lot of modules have the "if you say M here the module will be called"
> boilerplate here (which actually doesn't match the line I just made up).
> 
> > +/* These are required for each board */
> > +MODULE_PARM_DESC(bus, "Enumeration of VMEbus to which the board is connected");
> > +module_param_array(bus, int, &bus_num, 0);
> > +
> > +MODULE_PARM_DESC(base, "Base VME address for PIO2 Registers");
> > +module_param_array(base, long, &base_num, 0);
> > +
> > +MODULE_PARM_DESC(vector, "VME IRQ Vector (Lower 4 bits masked)");
> > +module_param_array(vector, int, &vector_num, 0);
> > +
> > +MODULE_PARM_DESC(level, "VME IRQ Level");
> > +module_param_array(level, int, &level_num, 0);
> > +
> > +MODULE_PARM_DESC(variant, "Last 4 characters of PIO2 board variant");
> > +module_param_array(variant, charp, &variant_num, 0);
> > +
> > +/* This is for debugging */
> > +MODULE_PARM_DESC(loopback, "Enable loopback mode on all cards");
> > +module_param(loopback, bool, 0);
> 
> All module parameters have a sysfs visibility (or permission) of zero.
> Why is that? (This might very well be a naive question. But I often
> wonder why a certain parameter's permission isn't at least 400, just to
> allow a quick check of that parameter.) Are arrays tricky in sysfs?

Yes, arrays are tricky, and in the end, all of these need to be "real"
sysfs files, not module parameters, but for now, it's ok to leave it as
is until the code is cleaned up.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Driver for GE PIO2 VME Card
  2011-11-04 19:55 ` Paul Bolle
  2011-11-04 20:00   ` Greg KH
@ 2011-11-07  9:34   ` Martyn Welch
  2011-11-07  9:55     ` Paul Bolle
  1 sibling, 1 reply; 9+ messages in thread
From: Martyn Welch @ 2011-11-07  9:34 UTC (permalink / raw)
  To: Paul Bolle; +Cc: gregkh, Grant Likely, devel, manohar.vanga, cota, linux-kernel

On 04/11/11 19:55, Paul Bolle wrote:
> A few random remarks follow. Ie, things that came up while quickly
> scanning this.
> 
> On Fri, 2011-11-04 at 17:33 +0000, Martyn Welch wrote:
>> diff --git a/drivers/staging/vme/devices/Kconfig b/drivers/staging/vme/devices/Kconfig
>> index ca5ba89..99f5414 100644
>> --- a/drivers/staging/vme/devices/Kconfig
>> +++ b/drivers/staging/vme/devices/Kconfig
>> @@ -6,3 +6,13 @@ config VME_USER
>>  	  If you say Y here you want to be able to access a limited number of
>>  	  VME windows in a manner at least semi-compatible with the interface
>>  	  provided with the original driver at http://vmelinux.org/.
> 
> Blank line here.
> 

ok

>> +config VME_PIO2
>> +	tristate "GE PIO2 VME"
>> +	help
>> +	  If you say Y here you have a GE PIO2. The PIO2 is a 6U VME Card,
> 
> Maybe something like: "Say Y here if you have a GE PIO2."?
> 

Yup, thats better :-)

>> +	  implementing 32 solid-state relay switched IO lines, in 4 groups of 8.
>> +	  The IO lines are provided as input, output or both as a build time
>> +	  option.
> 
> What option would that be?
> 

I think "Each bank of IO lines is pre-configured as input, output or both
depending on the variant of the card" may be a bit clearer?

>> +	  Otherwise it is safe to say N here.
>> +
> 
> A lot of modules have the "if you say M here the module will be called"
> boilerplate here (which actually doesn't match the line I just made up).
> 

ok

>> +/* These are required for each board */
>> +MODULE_PARM_DESC(bus, "Enumeration of VMEbus to which the board is connected");
>> +module_param_array(bus, int, &bus_num, 0);
>> +
>> +MODULE_PARM_DESC(base, "Base VME address for PIO2 Registers");
>> +module_param_array(base, long, &base_num, 0);
>> +
>> +MODULE_PARM_DESC(vector, "VME IRQ Vector (Lower 4 bits masked)");
>> +module_param_array(vector, int, &vector_num, 0);
>> +
>> +MODULE_PARM_DESC(level, "VME IRQ Level");
>> +module_param_array(level, int, &level_num, 0);
>> +
>> +MODULE_PARM_DESC(variant, "Last 4 characters of PIO2 board variant");
>> +module_param_array(variant, charp, &variant_num, 0);
>> +
>> +/* This is for debugging */
>> +MODULE_PARM_DESC(loopback, "Enable loopback mode on all cards");
>> +module_param(loopback, bool, 0);
> 
> All module parameters have a sysfs visibility (or permission) of zero.
> Why is that? (This might very well be a naive question. But I often
> wonder why a certain parameter's permission isn't at least 400, just to
> allow a quick check of that parameter.) Are arrays tricky in sysfs?
> 

Hadn't really thought about it to be honest. There seems to be plenty of
examples where it's set to non-zero.

Martyn

> I can't remember ever having seen guidelines for this. It's possible
> they exist and my question is answered after reading those.
> 
> 
> Paul Bolle
> 


-- 
Martyn Welch (Principal Software Engineer) | Registered in England and
GE Intelligent Platforms                   | Wales (3828642) at 100
T +44(0)1327322748                         | Barbirolli Square, Manchester,
E martyn.welch@ge.com                      | M2 3AB  VAT:GB 927559189

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Driver for GE PIO2 VME Card
  2011-11-07  9:34   ` Martyn Welch
@ 2011-11-07  9:55     ` Paul Bolle
  2011-11-07 11:10       ` Martyn Welch
  0 siblings, 1 reply; 9+ messages in thread
From: Paul Bolle @ 2011-11-07  9:55 UTC (permalink / raw)
  To: Martyn Welch
  Cc: gregkh, Grant Likely, devel, manohar.vanga, cota, linux-kernel

On Mon, 2011-11-07 at 09:34 +0000, Martyn Welch wrote:
> On 04/11/11 19:55, Paul Bolle wrote:
> >> +	  implementing 32 solid-state relay switched IO lines, in 4 groups of 8.
> >> +	  The IO lines are provided as input, output or both as a build time
> >> +	  option.
> > 
> > What option would that be? 
> 
> I think "Each bank of IO lines is pre-configured as input, output or both
> depending on the variant of the card" may be a bit clearer?

So this concerns a physical build option! Anything that makes that clear
(like your alternative does) is fine with me. (Perhaps "is
pre-configured" could be "functions", as that's less ambiguous in a
Kconfig file, but that's debatable.)

> > All module parameters have a sysfs visibility (or permission) of zero.
> > Why is that? (This might very well be a naive question. But I often
> > wonder why a certain parameter's permission isn't at least 400, just to
> > allow a quick check of that parameter.) Are arrays tricky in sysfs? 
> 
> Hadn't really thought about it to be honest. There seems to be plenty of
> examples where it's set to non-zero.

(Perhaps you meant to write "set to zero" here.)

Greg already explained that it's OK to leave the code as is for now
(since all these control knobs need to be provided via "real" sysfs
files in the long run anyhow).


Paul Bolle


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Driver for GE PIO2 VME Card
  2011-11-04 20:00   ` Greg KH
@ 2011-11-07 10:37     ` Martyn Welch
  2011-11-07 23:05       ` Greg KH
  0 siblings, 1 reply; 9+ messages in thread
From: Martyn Welch @ 2011-11-07 10:37 UTC (permalink / raw)
  To: Greg KH; +Cc: Paul Bolle, Grant Likely, devel, manohar.vanga, cota,
	linux-kernel

On 04/11/11 20:00, Greg KH wrote:
> On Fri, Nov 04, 2011 at 08:55:14PM +0100, Paul Bolle wrote:
>> A few random remarks follow. Ie, things that came up while quickly
>> scanning this.
>>
>> On Fri, 2011-11-04 at 17:33 +0000, Martyn Welch wrote:
>>> diff --git a/drivers/staging/vme/devices/Kconfig b/drivers/staging/vme/devices/Kconfig
>>> index ca5ba89..99f5414 100644
>>> --- a/drivers/staging/vme/devices/Kconfig
>>> +++ b/drivers/staging/vme/devices/Kconfig
>>> @@ -6,3 +6,13 @@ config VME_USER
>>>  	  If you say Y here you want to be able to access a limited number of
>>>  	  VME windows in a manner at least semi-compatible with the interface
>>>  	  provided with the original driver at http://vmelinux.org/.
>>
>> Blank line here.
>>
>>> +config VME_PIO2
>>> +	tristate "GE PIO2 VME"
>>> +	help
>>> +	  If you say Y here you have a GE PIO2. The PIO2 is a 6U VME Card,
>>
>> Maybe something like: "Say Y here if you have a GE PIO2."?
>>
>>> +	  implementing 32 solid-state relay switched IO lines, in 4 groups of 8.
>>> +	  The IO lines are provided as input, output or both as a build time
>>> +	  option.
>>
>> What option would that be?
>>
>>> +	  Otherwise it is safe to say N here.
>>> +
>>
>> A lot of modules have the "if you say M here the module will be called"
>> boilerplate here (which actually doesn't match the line I just made up).
>>
>>> +/* These are required for each board */
>>> +MODULE_PARM_DESC(bus, "Enumeration of VMEbus to which the board is connected");
>>> +module_param_array(bus, int, &bus_num, 0);
>>> +
>>> +MODULE_PARM_DESC(base, "Base VME address for PIO2 Registers");
>>> +module_param_array(base, long, &base_num, 0);
>>> +
>>> +MODULE_PARM_DESC(vector, "VME IRQ Vector (Lower 4 bits masked)");
>>> +module_param_array(vector, int, &vector_num, 0);
>>> +
>>> +MODULE_PARM_DESC(level, "VME IRQ Level");
>>> +module_param_array(level, int, &level_num, 0);
>>> +
>>> +MODULE_PARM_DESC(variant, "Last 4 characters of PIO2 board variant");
>>> +module_param_array(variant, charp, &variant_num, 0);
>>> +
>>> +/* This is for debugging */
>>> +MODULE_PARM_DESC(loopback, "Enable loopback mode on all cards");
>>> +module_param(loopback, bool, 0);
>>
>> All module parameters have a sysfs visibility (or permission) of zero.
>> Why is that? (This might very well be a naive question. But I often
>> wonder why a certain parameter's permission isn't at least 400, just to
>> allow a quick check of that parameter.) Are arrays tricky in sysfs?
> 
> Yes, arrays are tricky, and in the end, all of these need to be "real"
> sysfs files, not module parameters, but for now, it's ok to leave it as
> is until the code is cleaned up.
> 

Hi Greg,

Am I right in thinking we wouldn't then be able to pass the parameters in the
boot args?

Martyn

> thanks,
> 
> greg k-h


-- 
Martyn Welch (Principal Software Engineer) | Registered in England and
GE Intelligent Platforms                   | Wales (3828642) at 100
T +44(0)1327322748                         | Barbirolli Square, Manchester,
E martyn.welch@ge.com                      | M2 3AB  VAT:GB 927559189

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Driver for GE PIO2 VME Card
  2011-11-07  9:55     ` Paul Bolle
@ 2011-11-07 11:10       ` Martyn Welch
  0 siblings, 0 replies; 9+ messages in thread
From: Martyn Welch @ 2011-11-07 11:10 UTC (permalink / raw)
  To: Paul Bolle; +Cc: gregkh, Grant Likely, devel, manohar.vanga, cota, linux-kernel

On 07/11/11 09:55, Paul Bolle wrote:
> On Mon, 2011-11-07 at 09:34 +0000, Martyn Welch wrote:
>> On 04/11/11 19:55, Paul Bolle wrote:
>>>> +	  implementing 32 solid-state relay switched IO lines, in 4 groups of 8.
>>>> +	  The IO lines are provided as input, output or both as a build time
>>>> +	  option.
>>>
>>> What option would that be? 
>>
>> I think "Each bank of IO lines is pre-configured as input, output or both
>> depending on the variant of the card" may be a bit clearer?
> 
> So this concerns a physical build option! Anything that makes that clear
> (like your alternative does) is fine with me. (Perhaps "is
> pre-configured" could be "functions", as that's less ambiguous in a
> Kconfig file, but that's debatable.)
> 

Yes, with this card it is a physical build option.

>>> All module parameters have a sysfs visibility (or permission) of zero.
>>> Why is that? (This might very well be a naive question. But I often
>>> wonder why a certain parameter's permission isn't at least 400, just to
>>> allow a quick check of that parameter.) Are arrays tricky in sysfs? 
>>
>> Hadn't really thought about it to be honest. There seems to be plenty of
>> examples where it's set to non-zero.
> 
> (Perhaps you meant to write "set to zero" here.)
> 

I had a look to see what other drivers did before I saw Greg's email. I was
planning to at least change these to S_IRUGO for the next revision of the
patch as it would probably make sense for now I think.

> Greg already explained that it's OK to leave the code as is for now
> (since all these control knobs need to be provided via "real" sysfs
> files in the long run anyhow).
> 
>
> Paul Bolle
>

Not quite sure how that would work, would the driver be loaded then the values
written into sysfs to bind to each instance? A set of parameters are required
for each card (bus, base, vector, level and variant), I can't think of an
example of this, any ideas?

Martyn

-- 
Martyn Welch (Principal Software Engineer) | Registered in England and
GE Intelligent Platforms                   | Wales (3828642) at 100
T +44(0)1327322748                         | Barbirolli Square, Manchester,
E martyn.welch@ge.com                      | M2 3AB  VAT:GB 927559189

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Driver for GE PIO2 VME Card
  2011-11-07 10:37     ` Martyn Welch
@ 2011-11-07 23:05       ` Greg KH
  0 siblings, 0 replies; 9+ messages in thread
From: Greg KH @ 2011-11-07 23:05 UTC (permalink / raw)
  To: Martyn Welch
  Cc: Paul Bolle, Grant Likely, devel, manohar.vanga, cota,
	linux-kernel

On Mon, Nov 07, 2011 at 10:37:51AM +0000, Martyn Welch wrote:
> On 04/11/11 20:00, Greg KH wrote:
> > On Fri, Nov 04, 2011 at 08:55:14PM +0100, Paul Bolle wrote:
> >> A few random remarks follow. Ie, things that came up while quickly
> >> scanning this.
> >>
> >> On Fri, 2011-11-04 at 17:33 +0000, Martyn Welch wrote:
> >>> diff --git a/drivers/staging/vme/devices/Kconfig b/drivers/staging/vme/devices/Kconfig
> >>> index ca5ba89..99f5414 100644
> >>> --- a/drivers/staging/vme/devices/Kconfig
> >>> +++ b/drivers/staging/vme/devices/Kconfig
> >>> @@ -6,3 +6,13 @@ config VME_USER
> >>>  	  If you say Y here you want to be able to access a limited number of
> >>>  	  VME windows in a manner at least semi-compatible with the interface
> >>>  	  provided with the original driver at http://vmelinux.org/.
> >>
> >> Blank line here.
> >>
> >>> +config VME_PIO2
> >>> +	tristate "GE PIO2 VME"
> >>> +	help
> >>> +	  If you say Y here you have a GE PIO2. The PIO2 is a 6U VME Card,
> >>
> >> Maybe something like: "Say Y here if you have a GE PIO2."?
> >>
> >>> +	  implementing 32 solid-state relay switched IO lines, in 4 groups of 8.
> >>> +	  The IO lines are provided as input, output or both as a build time
> >>> +	  option.
> >>
> >> What option would that be?
> >>
> >>> +	  Otherwise it is safe to say N here.
> >>> +
> >>
> >> A lot of modules have the "if you say M here the module will be called"
> >> boilerplate here (which actually doesn't match the line I just made up).
> >>
> >>> +/* These are required for each board */
> >>> +MODULE_PARM_DESC(bus, "Enumeration of VMEbus to which the board is connected");
> >>> +module_param_array(bus, int, &bus_num, 0);
> >>> +
> >>> +MODULE_PARM_DESC(base, "Base VME address for PIO2 Registers");
> >>> +module_param_array(base, long, &base_num, 0);
> >>> +
> >>> +MODULE_PARM_DESC(vector, "VME IRQ Vector (Lower 4 bits masked)");
> >>> +module_param_array(vector, int, &vector_num, 0);
> >>> +
> >>> +MODULE_PARM_DESC(level, "VME IRQ Level");
> >>> +module_param_array(level, int, &level_num, 0);
> >>> +
> >>> +MODULE_PARM_DESC(variant, "Last 4 characters of PIO2 board variant");
> >>> +module_param_array(variant, charp, &variant_num, 0);
> >>> +
> >>> +/* This is for debugging */
> >>> +MODULE_PARM_DESC(loopback, "Enable loopback mode on all cards");
> >>> +module_param(loopback, bool, 0);
> >>
> >> All module parameters have a sysfs visibility (or permission) of zero.
> >> Why is that? (This might very well be a naive question. But I often
> >> wonder why a certain parameter's permission isn't at least 400, just to
> >> allow a quick check of that parameter.) Are arrays tricky in sysfs?
> > 
> > Yes, arrays are tricky, and in the end, all of these need to be "real"
> > sysfs files, not module parameters, but for now, it's ok to leave it as
> > is until the code is cleaned up.
> > 
> 
> Hi Greg,
> 
> Am I right in thinking we wouldn't then be able to pass the parameters in the
> boot args?

If they are sysfs files and not module paramaters, then, no, you wouldn't.

greg k-h

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2011-11-07 23:06 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-11-04 17:33 [PATCH] Driver for GE PIO2 VME Card Martyn Welch
2011-11-04 17:38 ` Martyn Welch
2011-11-04 19:55 ` Paul Bolle
2011-11-04 20:00   ` Greg KH
2011-11-07 10:37     ` Martyn Welch
2011-11-07 23:05       ` Greg KH
2011-11-07  9:34   ` Martyn Welch
2011-11-07  9:55     ` Paul Bolle
2011-11-07 11:10       ` Martyn Welch

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox