[PATCH v2] usb:serial:pl2303: add GPIOs interface on PL2303

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



PL2303HX has two GPIOs, this patch add interface for it.

Signed-off-by: Wang YanQing <udknight@xxxxxxxxx>
---
 Changes v1-v2:
 1:drop gpio-pl2303.c and relation stuff
 2:hang gpio stuff off of pl2303.c

 drivers/usb/serial/Kconfig  |   7 ++
 drivers/usb/serial/pl2303.c | 153 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 160 insertions(+)

diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 3ce5c74..099ff05 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -516,6 +516,13 @@ config USB_SERIAL_PL2303
 	  To compile this driver as a module, choose M here: the
 	  module will be called pl2303.
 
+config USB_SERIAL_PL2303_GPIO
+	bool "USB Prolific 2303 Single Port GPIOs support"
+	depends on USB_SERIAL_PL2303
+	help
+	  Say Y here if you want to use the GPIOs on PL2303 USB Serial single port
+	  adapter from Prolific.
+
 config USB_SERIAL_OTI6858
 	tristate "USB Ours Technology Inc. OTi-6858 USB To RS232 Bridge Controller"
 	help
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index b3d5a35..1fbd338 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -28,6 +28,9 @@
 #include <linux/usb.h>
 #include <linux/usb/serial.h>
 #include <asm/unaligned.h>
+#ifdef CONFIG_USB_SERIAL_PL2303_GPIO
+#include <linux/gpio.h>
+#endif
 #include "pl2303.h"
 
 
@@ -143,9 +146,27 @@ struct pl2303_type_data {
 	unsigned long quirks;
 };
 
+#ifdef CONFIG_USB_SERIAL_PL2303_GPIO
+struct pl2303_gpio {
+	/*
+	 * 0..3: unknown (zero)
+	 * 4: gp0 output enable (1: gp0 pin is output, 0: gp0 pin is input)
+	 * 5: gp1 output enable (1: gp1 pin is output, 0: gp1 pin is input)
+	 * 6: gp0 pin value
+	 * 7: gp1 pin value
+	 */
+	u8 index;
+	struct usb_serial *serial;
+	struct gpio_chip gpio_chip;
+};
+#endif
+
 struct pl2303_serial_private {
 	const struct pl2303_type_data *type;
 	unsigned long quirks;
+#ifdef CONFIG_USB_SERIAL_PL2303_GPIO
+	struct pl2303_gpio *gpio;
+#endif
 };
 
 struct pl2303_private {
@@ -213,6 +234,100 @@ static int pl2303_probe(struct usb_serial *serial,
 	return 0;
 }
 
+#ifdef CONFIG_USB_SERIAL_PL2303_GPIO
+static inline struct pl2303_gpio *to_pl2303_gpio(struct gpio_chip *chip)
+{
+	return container_of(chip, struct pl2303_gpio, gpio_chip);
+}
+
+static int pl2303_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+	struct pl2303_gpio *gpio = to_pl2303_gpio(chip);
+
+	if (offset == 0)
+		gpio->index &= ~0x10;
+	else if (offset == 1)
+		gpio->index &= ~0x20;
+	else
+		return -EINVAL;
+
+	pl2303_vendor_write(gpio->serial, 1, gpio->index);
+	return 0;
+}
+
+static int pl2303_gpio_direction_out(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct pl2303_gpio *gpio = to_pl2303_gpio(chip);
+
+	if (offset == 0) {
+		gpio->index |= 0x10;
+		if (value)
+			gpio->index |= 0x40;
+		else
+			gpio->index &= ~0x40;
+	} else if (offset == 1) {
+		gpio->index |= 0x20;
+		if (value)
+			gpio->index |= 0x80;
+		else
+			gpio->index &= ~0x80;
+	} else {
+		return -EINVAL;
+	}
+
+	pl2303_vendor_write(gpio->serial, 1, gpio->index);
+	return 0;
+}
+
+static void pl2303_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct pl2303_gpio *gpio = to_pl2303_gpio(chip);
+
+	if (offset == 0) {
+		if (value)
+			gpio->index |= 0x40;
+		else
+			gpio->index &= ~0x40;
+	} else if (offset == 1) {
+		if (value)
+			gpio->index |= 0x80;
+		else
+			gpio->index &= ~0x80;
+	} else {
+		return;
+	}
+
+	pl2303_vendor_write(gpio->serial, 1, gpio->index);
+}
+
+static int pl2303_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct pl2303_gpio *gpio = to_pl2303_gpio(chip);
+	unsigned char buf[1];
+	int value = 0;
+
+	if(pl2303_vendor_read(gpio->serial, 0x0081, buf) < 1)
+		return -EIO;
+	if (offset == 0)
+		value = buf[0] & 0x40;
+	else if (offset == 1)
+		value = buf[0] & 0x80;
+	else
+		value = -EINVAL;
+	return value;
+}
+
+static struct gpio_chip template_chip = {
+	.label                  = "pl2303-gpio",
+	.owner                  = THIS_MODULE,
+	.direction_input        = pl2303_gpio_direction_in,
+	.get                    = pl2303_gpio_get,
+	.direction_output       = pl2303_gpio_direction_out,
+	.set                    = pl2303_gpio_set,
+	.can_sleep              = 1,
+};
+#endif
+
 static int pl2303_startup(struct usb_serial *serial)
 {
 	struct pl2303_serial_private *spriv;
@@ -262,6 +377,37 @@ static int pl2303_startup(struct usb_serial *serial)
 
 	kfree(buf);
 
+#ifdef CONFIG_USB_SERIAL_PL2303_GPIO
+	if (type == TYPE_HX)
+	{
+		struct pl2303_gpio *gpio;
+
+		gpio = kzalloc(sizeof(struct pl2303_gpio), GFP_KERNEL);
+		if (gpio == NULL) {
+			dev_err(&serial->interface->dev, "Failed to allocate pl2303_gpio\n");
+		} else {
+			int ret;
+
+			gpio->index = 0x00;
+			gpio->serial = serial;
+			gpio->gpio_chip = template_chip;
+			gpio->gpio_chip.ngpio = 2;
+			gpio->gpio_chip.base = -1;
+			gpio->gpio_chip.dev = &serial->interface->dev;
+			/* initialize GPIOs, input mode as default */
+			pl2303_vendor_write(gpio->serial, 1, gpio->index);
+
+			spriv->gpio = gpio;
+			ret = gpiochip_add(&spriv->gpio->gpio_chip);
+			if (ret < 0) {
+				dev_err(&serial->interface->dev,
+					"Could not register gpiochip, %d\n", ret);
+				kfree(spriv->gpio);
+				spriv->gpio = NULL;
+			}
+		}
+	}
+#endif
 	return 0;
 }
 
@@ -269,6 +415,13 @@ static void pl2303_release(struct usb_serial *serial)
 {
 	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
 
+#ifdef CONFIG_USB_SERIAL_PL2303_GPIO
+	if (spriv && spriv->gpio) {
+		if (gpiochip_remove(&spriv->gpio->gpio_chip))
+			dev_err(&serial->interface->dev, "unable to remove gpio_chip?\n");
+		kfree(spriv->gpio);
+	}
+#endif
 	kfree(spriv);
 }
 
-- 
1.8.5.5.dirty
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux