From: Wang YanQing <udknight@gmail.com>
To: linus.walleij@linaro.org
Cc: gregkh@linuxfoundation.org, jhovold@gmail.com,
linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH] gpio:gpio-pl2303: add gpio driver for GPIOs on PL2303
Date: Sun, 20 Jul 2014 08:01:31 +0800 [thread overview]
Message-ID: <20140720000131.GA2158@udknight> (raw)
PL2303HX has two GPIOs, this patch add driver for it.
Signed-off-by: Wang YanQing <udknight@gmail.com>
---
MAINTAINERS | 5 +
drivers/gpio/Kconfig | 7 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-pl2303.c | 238 ++++++++++++++++++++++++++++++++++++++++++++
drivers/usb/serial/pl2303.c | 19 ++++
5 files changed, 270 insertions(+)
create mode 100644 drivers/gpio/gpio-pl2303.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 53feaaf..4a9d764 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6903,6 +6903,11 @@ F: drivers/i2c/busses/i2c-puv3.c
F: drivers/video/fb-puv3.c
F: drivers/rtc/rtc-puv3.c
+PL2303 GPIO DRIVER
+M: Wang YanQing <udknight@gmail.com>
+S: Maintained
+F: drivers/gpio/gpio-pl2303.c
+
PMBUS HARDWARE MONITORING DRIVERS
M: Guenter Roeck <linux@roeck-us.net>
L: lm-sensors@lm-sensors.org
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 4a1b511..0f90950 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -110,6 +110,13 @@ config GPIO_DA9055
config GPIO_MAX730X
tristate
+config GPIO_PL2303
+ tristate "USB Prolific 2303 gpio support"
+ depends on USB_SERIAL_PL2303
+ help
+ Enable support for GPIOs on USB Prolific 2303
+ It support two GPIOs on PL2303HX currently.
+
comment "Memory mapped GPIO drivers:"
config GPIO_CLPS711X
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index d10f6a9..4ff59f6 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -101,3 +101,4 @@ obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o
obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o
obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o
obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
+obj-$(CONFIG_GPIO_PL2303) += gpio-pl2303.o
diff --git a/drivers/gpio/gpio-pl2303.c b/drivers/gpio/gpio-pl2303.c
new file mode 100644
index 0000000..a703440
--- /dev/null
+++ b/drivers/gpio/gpio-pl2303.c
@@ -0,0 +1,238 @@
+/*
+ * PL2303 GPIO driver
+ *
+ * Copyright (C) 2014 Wang YanQing <udknight@gmail.com>
+ *
+ * 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.
+ *
+ * Check pl2303.c for further details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+
+#define VENDOR_READ_REQUEST_TYPE 0xc0
+#define VENDOR_READ_REQUEST 0x01
+
+#define VENDOR_WRITE_REQUEST_TYPE 0x40
+#define VENDOR_WRITE_REQUEST 0x01
+
+struct pl2303_gpio_data {
+ struct usb_device *pl2303;
+ /*
+ * 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 gpio_chip gpio_chip;
+};
+
+static inline struct pl2303_gpio_data *to_pl2303_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct pl2303_gpio_data, gpio_chip);
+}
+
+static int pl2303_gpio_read(struct gpio_chip *chip, unsigned char buf[1])
+{
+ struct pl2303_gpio_data *pl2303_gpio = to_pl2303_gpio(chip);
+ struct usb_device *pl2303 = pl2303_gpio->pl2303;
+ struct device *dev = chip->dev;
+ int res;
+
+ res = usb_control_msg(pl2303, usb_rcvctrlpipe(pl2303, 0),
+ VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE,
+ 0x0081, 0, buf, 1, 100);
+ if (res != 1) {
+ dev_err(dev, "%s - failed to read [%04x]: %d\n", __func__,
+ 0x0081, res);
+ if (res >= 0)
+ res = -EIO;
+
+ return res;
+ }
+
+ dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, 0x0081, buf[0]);
+
+ return 0;
+}
+
+static int pl2303_gpio_write(struct gpio_chip *chip)
+{
+ struct pl2303_gpio_data *pl2303_gpio = to_pl2303_gpio(chip);
+ struct usb_device *pl2303 = pl2303_gpio->pl2303;
+ struct device *dev = chip->dev;
+ int res;
+
+ dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, 1, pl2303_gpio->index);
+
+ res = usb_control_msg(pl2303, usb_sndctrlpipe(pl2303, 0),
+ VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE,
+ 1, pl2303_gpio->index, NULL, 0, 100);
+ if (res) {
+ dev_err(dev, "%s - failed to write [%04x] = %02x: %d\n", __func__,
+ 1, pl2303_gpio->index, res);
+ return res;
+ }
+
+ return 0;
+}
+
+static int pl2303_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct pl2303_gpio_data *pl2303_gpio = to_pl2303_gpio(chip);
+
+ if (offset == 0)
+ pl2303_gpio->index &= ~0x10;
+ else if (offset == 1)
+ pl2303_gpio->index &= ~0x20;
+ else
+ return -EINVAL;
+
+ pl2303_gpio_write(chip);
+ return 0;
+}
+
+static int pl2303_gpio_direction_out(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct pl2303_gpio_data *pl2303_gpio = to_pl2303_gpio(chip);
+
+ if (offset == 0) {
+ pl2303_gpio->index |= 0x10;
+ if (value)
+ pl2303_gpio->index |= 0x40;
+ else
+ pl2303_gpio->index &= ~0x40;
+ } else if (offset == 1) {
+ pl2303_gpio->index |= 0x20;
+ if (value)
+ pl2303_gpio->index |= 0x80;
+ else
+ pl2303_gpio->index &= ~0x80;
+ } else {
+ return -EINVAL;
+ }
+
+ pl2303_gpio_write(chip);
+ return 0;
+}
+
+static void pl2303_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct pl2303_gpio_data *pl2303_gpio = to_pl2303_gpio(chip);
+
+ if (offset == 0) {
+ if (value)
+ pl2303_gpio->index |= 0x40;
+ else
+ pl2303_gpio->index &= ~0x40;
+ } else if (offset == 1) {
+ if (value)
+ pl2303_gpio->index |= 0x80;
+ else
+ pl2303_gpio->index &= ~0x80;
+ } else {
+ return;
+ }
+
+ pl2303_gpio_write(chip);
+}
+
+static int pl2303_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ unsigned char buf[1];
+ int value = 0;
+
+ if(pl2303_gpio_read(chip, buf) < 1)
+ return -EIO;
+ if (offset == 0)
+ value = buf[0] & 0x40;
+ else if (offset == 1)
+ value = buf[0] & 0x80;
+ else
+ return -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,
+};
+
+static int pl2303_gpio_probe(struct platform_device *pdev)
+{
+ struct usb_device *pl2303 = platform_get_drvdata(pdev);
+ struct pl2303_gpio_data *pl2303_gpio;
+ int ret;
+
+ pl2303_gpio = kzalloc(sizeof(*pl2303_gpio), GFP_KERNEL);
+ if (pl2303_gpio == NULL)
+ return -ENOMEM;
+
+ /* initialize gpio0,gpio1 for input as default*/
+ pl2303_gpio->index = 0x00;
+ pl2303_gpio->pl2303 = pl2303;
+ pl2303_gpio->gpio_chip = template_chip;
+ pl2303_gpio->gpio_chip.ngpio = 2;
+ pl2303_gpio->gpio_chip.base = -1;
+ pl2303_gpio->gpio_chip.dev = &pdev->dev;
+ pl2303_gpio_write(&pl2303_gpio->gpio_chip);
+
+ ret = gpiochip_add(&pl2303_gpio->gpio_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ return ret;
+ }
+ platform_set_drvdata(pdev, pl2303_gpio);
+
+ return ret;
+}
+
+static int pl2303_gpio_remove(struct platform_device *pdev)
+{
+ struct pl2303_gpio_data *pl2303_gpio = platform_get_drvdata(pdev);
+
+ if (gpiochip_remove(&pl2303_gpio->gpio_chip))
+ dev_err(&pdev->dev, "unable to remove gpio_chip?\n");
+ platform_set_drvdata(pdev, NULL);
+ kfree(pl2303_gpio);
+ return 0;
+}
+
+static struct platform_driver pl2303_gpio_driver = {
+ .driver.name = "pl2303-gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = pl2303_gpio_probe,
+ .remove = pl2303_gpio_remove,
+};
+
+static int __init pl2303_gpio_init(void)
+{
+ return platform_driver_register(&pl2303_gpio_driver);
+}
+module_init(pl2303_gpio_init);
+
+static void __exit pl2303_gpio_exit(void)
+{
+ platform_driver_unregister(&pl2303_gpio_driver);
+}
+module_exit(pl2303_gpio_exit);
+
+MODULE_AUTHOR("Wang YanQing <udknight@gmail.com>");
+MODULE_DESCRIPTION("GPIO driver for PL2303");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("platform:pl2303-gpio");
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index b3d5a35..1bb8928 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -28,6 +28,7 @@
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <asm/unaligned.h>
+#include <linux/platform_device.h>
#include "pl2303.h"
@@ -146,6 +147,7 @@ struct pl2303_type_data {
struct pl2303_serial_private {
const struct pl2303_type_data *type;
unsigned long quirks;
+ struct platform_device *pdev;
};
struct pl2303_private {
@@ -261,7 +263,22 @@ static int pl2303_startup(struct usb_serial *serial)
pl2303_vendor_write(serial, 2, 0x44);
kfree(buf);
+ if (type != TYPE_HX)
+ return 0;
+ spriv->pdev = platform_device_alloc("pl2303-gpio", PLATFORM_DEVID_AUTO);
+ if (spriv->pdev == NULL) {
+ dev_err(&serial->interface->dev, "Failed to allocate %s\n", "pl2303-gpio");
+ } else {
+ spriv->pdev->dev.parent = &serial->interface->dev;
+ platform_set_drvdata(spriv->pdev, serial->dev);
+ platform_device_add(spriv->pdev);
+ if (platform_device_add(spriv->pdev) != 0) {
+ dev_err(&serial->interface->dev, "Failed to register %s\n", "pl2303-gpio");
+ platform_device_put(spriv->pdev);
+ spriv->pdev = NULL;
+ }
+ }
return 0;
}
@@ -269,6 +286,8 @@ static void pl2303_release(struct usb_serial *serial)
{
struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
+ if (spriv && spriv->pdev)
+ platform_device_unregister(spriv->pdev);
kfree(spriv);
}
--
1.8.5.5.dirty
next reply other threads:[~2014-07-20 0:02 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-07-20 0:01 Wang YanQing [this message]
2014-07-20 0:10 ` [PATCH] gpio:gpio-pl2303: add gpio driver for GPIOs on PL2303 Greg KH
2014-07-20 20:11 ` Andreas Mohr
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20140720000131.GA2158@udknight \
--to=udknight@gmail.com \
--cc=gregkh@linuxfoundation.org \
--cc=jhovold@gmail.com \
--cc=linus.walleij@linaro.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.