Subject: [PATCH v2 15/16] misc: support for I-8042 in LP-8x4x

From: [email-masked] (Sergei Ianovich)
Date: Fri, 13 Dec 2013 06:27:24 +0400

Status of I-8042 16 digital output channels can be managed via
sysfs.

Status of I-8042 16 digital input channels can be read via sysfs.

http://www.icpdas.com/products/Remote_IO/i-8ke/i-8042w.htm

Signed-off-by: Sergei Ianovich <[email-masked]>
---
   v0..v2
   * no changes

 Documentation/misc-devices/lp8x4x_bus.txt |  4 ++
 drivers/misc/lp8x4x_bus.c                 | 76 ++++++++++++++++++++++++++++++-
 2 files changed, 78 insertions(+), 2 deletions(-)

diff --git a/Documentation/misc-devices/lp8x4x_bus.txt b/Documentation/misc-devices/lp8x4x_bus.txt
index 74df10b..e3a8bcf 100644
--- a/Documentation/misc-devices/lp8x4x_bus.txt
+++ b/Documentation/misc-devices/lp8x4x_bus.txt
@@ -50,6 +50,10 @@ active_slot
 model
 	RO - shows expansion module model number
 
+input_status
+	RO - get status of digital input channels on the module in
+	     the expansion slot.
+
 output_status
 	RW - set status of digital output channels on the module in
 	     the expansion slot. Value can be read back.
diff --git a/drivers/misc/lp8x4x_bus.c b/drivers/misc/lp8x4x_bus.c
index 1ef1726..684e640 100644
--- a/drivers/misc/lp8x4x_bus.c
+++ b/drivers/misc/lp8x4x_bus.c
@@ -27,6 +27,8 @@ struct lp8x4x_slot {
 	struct mutex		lock;
 	unsigned int		DO_len;
 	u32			DO;
+	unsigned int		DI_len;
+	u32			DI;
 	struct device		dev;
 };
 
@@ -89,6 +91,21 @@ static void lp8x4x_slot_release(struct device *dev)
 {
 }
 
+static void lp8x4x_slot_get_DI(struct lp8x4x_slot *s)
+{
+	int i;
+	u32 b;
+
+	mutex_lock(&s->lock);
+	s->DI = 0;
+	for (i = 0; i < s->DI_len; i++) {
+		b = ioread8(s->data_addr + 2 * (i + 1));
+		b ^= 0xff;
+		s->DI += b << (i * 8);
+	}
+	mutex_unlock(&s->lock);
+}
+
 static void lp8x4x_slot_set_DO(struct lp8x4x_slot *s)
 {
 	int i;
@@ -98,29 +115,70 @@ static void lp8x4x_slot_set_DO(struct lp8x4x_slot *s)
 	mutex_unlock(&s->lock);
 }
 
+static ssize_t input_status_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct lp8x4x_slot *s = container_of(dev, struct lp8x4x_slot, dev);
+
+	lp8x4x_slot_get_DI(s);
+	switch (s->DI_len) {
+	case 4:
+		return sprintf(buf, "0x%08x\n", s->DI);
+	case 2:
+		return sprintf(buf, "0x%04x\n", s->DI);
+	case 1:
+		return sprintf(buf, "0x%02x\n", s->DI);
+	default:
+		break;
+	}
+	return sprintf(buf, "0x%x\n", s->DI);
+}
+
+static DEVICE_ATTR_RO(input_status);
+
 static ssize_t output_status_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
 	struct lp8x4x_slot *s = container_of(dev, struct lp8x4x_slot, dev);
 
-	return sprintf(buf, "0x%08x\n", s->DO);
+	switch (s->DO_len) {
+	case 4:
+		return sprintf(buf, "0x%08x\n", s->DO);
+	case 2:
+		return sprintf(buf, "0x%04x\n", s->DO);
+	case 1:
+		return sprintf(buf, "0x%02x\n", s->DO);
+	default:
+		break;
+	}
+	return sprintf(buf, "0x%x\n", s->DO);
 }
 
 static ssize_t output_status_store(struct device *dev,
 		struct device_attribute *attr, const char *buf, size_t count)
 {
 	struct lp8x4x_slot *s = container_of(dev, struct lp8x4x_slot, dev);
+	u32 DO;
+	u8 *b = (void *) &DO;
+	int i;
 
 	if (!buf)
 		return count;
 	if (0 == count)
 		return count;
 
-	if (kstrtouint(buf, 16, &s->DO) != 0) {
+	if (kstrtouint(buf, 16, &DO) != 0) {
 		dev_err(dev, "bad input\n");
 		return count;
 	}
 
+	for (i = 4; i > s->DO_len; i--)
+		if (b[i - 1] != 0) {
+			dev_err(dev, "bad input\n");
+			return count;
+		}
+
+	s->DO = DO;
 	lp8x4x_slot_set_DO(s);
 
 	return count;
@@ -151,6 +209,14 @@ static struct attribute *do_slot_dev_attrs[] = {
 };
 ATTRIBUTE_GROUPS(do_slot_dev);
 
+static struct attribute *dio_slot_dev_attrs[] = {
+	&dev_attr_model.attr,
+	&dev_attr_output_status.attr,
+	&dev_attr_input_status.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(dio_slot_dev);
+
 static void lp8x4x_master_release(struct device *dev)
 {
 	struct lp8x4x_master *m = container_of(dev, struct lp8x4x_master, dev);
@@ -255,6 +321,12 @@ static void __init lp8x4x_bus_probe_slot(struct lp8x4x_master *m, int i,
 		lp8x4x_slot_set_DO(s);
 		s->dev.groups = do_slot_dev_groups;
 		break;
+	case 42:
+		s->DI_len = 2;
+		s->DO_len = 2;
+		lp8x4x_slot_set_DO(s);
+		s->dev.groups = dio_slot_dev_groups;
+		break;
 	default:
 		s->dev.groups = slot_dev_groups;
 		break;
-- 
1.8.4.3