From: "Ji-Ze Hong (Peter Hong)" <hpeter@gmail.com>
To: johan@kernel.org
Cc: gregkh@linuxfoundation.org, linux-usb@vger.kernel.org,
linux-kernel@vger.kernel.org, peter_hong@fintek.com.tw,
"Ji-Ze Hong (Peter Hong)" <hpeter+linux_kernel@gmail.com>
Subject: [PATCH V2 7/7] USB: serial: f81232: Add gpiolib to GPIO device
Date: Mon, 23 Sep 2019 10:24:49 +0800 [thread overview]
Message-ID: <20190923022449.10952-8-hpeter+linux_kernel@gmail.com> (raw)
In-Reply-To: <20190923022449.10952-1-hpeter+linux_kernel@gmail.com>
The Fintek F81534A series contains 3 GPIOs per UART and The max GPIOs
is 12x3 = 36 GPIOs and this patch will implements GPIO device as a
gpiochip to control all GPIO pins even transforms to transceiver pins.
Signed-off-by: Ji-Ze Hong (Peter Hong) <hpeter+linux_kernel@gmail.com>
---
drivers/usb/serial/f81232.c | 249 ++++++++++++++++++++++++++++++++++++
1 file changed, 249 insertions(+)
diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c
index 82cc1e6cff62..dc9b28738b80 100644
--- a/drivers/usb/serial/f81232.c
+++ b/drivers/usb/serial/f81232.c
@@ -18,6 +18,7 @@
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
+#include <linux/gpio.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/serial_reg.h>
@@ -104,6 +105,8 @@ MODULE_DEVICE_TABLE(usb, all_serial_id_table);
#define F81534A_TRIGGER_MULTPILE_4X BIT(3)
#define F81534A_FIFO_128BYTE (BIT(1) | BIT(0))
+#define F81534A_MAX_PORT 12
+
/* Serial port self GPIO control, 2bytes [control&output data][input data] */
#define F81534A_GPIO_REG 0x10e
#define F81534A_GPIO_MODE2_DIR BIT(6) /* 1: input, 0: output */
@@ -115,6 +118,13 @@ MODULE_DEVICE_TABLE(usb, all_serial_id_table);
#define F81534A_CMD_ENABLE_PORT 0x116
+/*
+ * Control device global GPIO control,
+ * 2bytes [control&output data][input data]
+ */
+#define F81534A_CTRL_GPIO_REG 0x1601
+#define F81534A_CTRL_GPIO_MAX_PIN 3
+
struct f81232_private {
struct mutex lock;
u8 modem_control;
@@ -126,6 +136,12 @@ struct f81232_private {
struct usb_serial_port *port;
};
+struct f81534a_ctrl_private {
+ struct usb_interface *intf;
+ struct gpio_chip chip;
+ struct mutex lock;
+};
+
static u32 const baudrate_table[] = { 115200, 921600, 1152000, 1500000 };
static u8 const clock_table[] = { F81232_CLK_1_846_MHZ, F81232_CLK_14_77_MHZ,
F81232_CLK_18_46_MHZ, F81232_CLK_24_MHZ };
@@ -863,6 +879,50 @@ static void f81232_lsr_worker(struct work_struct *work)
dev_warn(&port->dev, "read LSR failed: %d\n", status);
}
+static int f81534a_ctrl_get_register(struct usb_device *dev, u16 reg, u16 size,
+ void *val)
+{
+ int retry = F81534A_ACCESS_REG_RETRY;
+ int status;
+ u8 *tmp;
+
+ tmp = kmalloc(size, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ while (retry--) {
+ status = usb_control_msg(dev,
+ usb_rcvctrlpipe(dev, 0),
+ F81534A_REGISTER_REQUEST,
+ F81534A_GET_REGISTER,
+ reg,
+ 0,
+ tmp,
+ size,
+ USB_CTRL_GET_TIMEOUT);
+ if (status != size) {
+ status = usb_translate_errors(status);
+ if (status == -EIO)
+ continue;
+
+ status = -EIO;
+ } else {
+ status = 0;
+ memcpy(val, tmp, size);
+ }
+
+ break;
+ }
+
+ if (status) {
+ dev_err(&dev->dev, "get reg: %x, failed status: %d\n", reg,
+ status);
+ }
+
+ kfree(tmp);
+ return status;
+}
+
static int f81534a_ctrl_set_register(struct usb_device *dev, u16 reg, u16 size,
void *val)
{
@@ -908,6 +968,182 @@ static int f81534a_ctrl_set_register(struct usb_device *dev, u16 reg, u16 size,
return status;
}
+#ifdef CONFIG_GPIOLIB
+static int f81534a_ctrl_set_mask_register(struct usb_device *dev, u16 reg,
+ u8 mask, u8 val)
+{
+ int status;
+ u8 tmp;
+
+ status = f81534a_ctrl_get_register(dev, reg, 1, &tmp);
+ if (status)
+ return status;
+
+
+ tmp = (tmp & ~mask) | (val & mask);
+
+ status = f81534a_ctrl_set_register(dev, reg, 1, &tmp);
+ if (status)
+ return status;
+
+ return 0;
+}
+
+static int f81534a_gpio_get(struct gpio_chip *chip, unsigned int gpio_num)
+{
+ struct f81534a_ctrl_private *priv = gpiochip_get_data(chip);
+ struct usb_interface *intf = priv->intf;
+ struct usb_device *dev = interface_to_usbdev(intf);
+ int status;
+ u8 tmp[2];
+ int set;
+ int idx;
+ int reg;
+
+ set = gpio_num / F81534A_CTRL_GPIO_MAX_PIN;
+ idx = gpio_num % F81534A_CTRL_GPIO_MAX_PIN;
+ reg = F81534A_CTRL_GPIO_REG + set;
+
+ mutex_lock(&priv->lock);
+
+ status = f81534a_ctrl_get_register(dev, reg, sizeof(tmp), tmp);
+ if (status) {
+ mutex_unlock(&priv->lock);
+ return status;
+ }
+
+ mutex_unlock(&priv->lock);
+
+ return !!(tmp[1] & BIT(idx));
+}
+
+static int f81534a_gpio_direction_in(struct gpio_chip *chip,
+ unsigned int gpio_num)
+{
+ struct f81534a_ctrl_private *priv = gpiochip_get_data(chip);
+ struct usb_interface *intf = priv->intf;
+ struct usb_device *dev = interface_to_usbdev(intf);
+ int status;
+ int set;
+ int idx;
+ int reg;
+ u8 mask;
+
+ set = gpio_num / F81534A_CTRL_GPIO_MAX_PIN;
+ idx = gpio_num % F81534A_CTRL_GPIO_MAX_PIN;
+ mask = F81534A_GPIO_MODE0_DIR << idx;
+ reg = F81534A_CTRL_GPIO_REG + set;
+
+ mutex_lock(&priv->lock);
+ status = f81534a_ctrl_set_mask_register(dev, reg, mask, mask);
+ mutex_unlock(&priv->lock);
+
+ return status;
+}
+
+static int f81534a_gpio_direction_out(struct gpio_chip *chip,
+ unsigned int gpio_num, int val)
+{
+ struct f81534a_ctrl_private *priv = gpiochip_get_data(chip);
+ struct usb_interface *intf = priv->intf;
+ struct usb_device *dev = interface_to_usbdev(intf);
+ int status;
+ int set;
+ int idx;
+ int reg;
+ u8 mask;
+ u8 data;
+
+ set = gpio_num / F81534A_CTRL_GPIO_MAX_PIN;
+ idx = gpio_num % F81534A_CTRL_GPIO_MAX_PIN;
+ mask = (F81534A_GPIO_MODE0_DIR << idx) | BIT(idx);
+ reg = F81534A_CTRL_GPIO_REG + set;
+ data = val ? BIT(idx) : 0;
+
+ mutex_lock(&priv->lock);
+ status = f81534a_ctrl_set_mask_register(dev, reg, mask, data);
+ mutex_unlock(&priv->lock);
+
+ return status;
+}
+
+static void f81534a_gpio_set(struct gpio_chip *chip, unsigned int gpio_num,
+ int val)
+{
+ f81534a_gpio_direction_out(chip, gpio_num, val);
+}
+
+static int f81534a_gpio_get_direction(struct gpio_chip *chip,
+ unsigned int gpio_num)
+{
+ struct f81534a_ctrl_private *priv = gpiochip_get_data(chip);
+ struct usb_interface *intf = priv->intf;
+ struct usb_device *dev = interface_to_usbdev(intf);
+ int status;
+ u8 tmp[2];
+ int set;
+ int idx;
+ int reg;
+ u8 mask;
+
+ set = gpio_num / F81534A_CTRL_GPIO_MAX_PIN;
+ idx = gpio_num % F81534A_CTRL_GPIO_MAX_PIN;
+ mask = F81534A_GPIO_MODE0_DIR << idx;
+ reg = F81534A_CTRL_GPIO_REG + set;
+
+ mutex_lock(&priv->lock);
+
+ status = f81534a_ctrl_get_register(dev, reg, sizeof(tmp), tmp);
+ if (status) {
+ mutex_unlock(&priv->lock);
+ return status;
+ }
+
+ mutex_unlock(&priv->lock);
+
+ if (tmp[0] & mask)
+ return GPIOF_DIR_IN;
+
+ return GPIOF_DIR_OUT;
+}
+
+static int f81534a_prepare_gpio(struct usb_interface *intf)
+{
+ struct f81534a_ctrl_private *priv = usb_get_intfdata(intf);
+ int status;
+
+ priv->chip.parent = &intf->dev;
+ priv->chip.owner = THIS_MODULE;
+ priv->chip.get_direction = f81534a_gpio_get_direction,
+ priv->chip.get = f81534a_gpio_get;
+ priv->chip.direction_input = f81534a_gpio_direction_in;
+ priv->chip.set = f81534a_gpio_set;
+ priv->chip.direction_output = f81534a_gpio_direction_out;
+ priv->chip.label = "f81534a";
+ priv->chip.can_sleep = true;
+ /* M0(SD)/M1/M2 */
+ priv->chip.ngpio = F81534A_CTRL_GPIO_MAX_PIN * F81534A_MAX_PORT;
+ priv->chip.base = -1;
+
+ priv->intf = intf;
+ mutex_init(&priv->lock);
+
+ status = devm_gpiochip_add_data(&intf->dev, &priv->chip, priv);
+ if (status) {
+ dev_err(&intf->dev, "failed to register gpiochip: %d\n",
+ status);
+ return status;
+ }
+
+ return 0;
+}
+#else
+static int f81534a_prepare_gpio(struct usb_interface *intf)
+{
+ return 0;
+}
+#endif
+
static int f81534a_ctrl_enable_all_ports(struct usb_interface *intf)
{
struct usb_device *dev = interface_to_usbdev(intf);
@@ -930,8 +1166,21 @@ static int f81534a_ctrl_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
+ struct f81534a_ctrl_private *priv;
int status;
+ priv = devm_kzalloc(&intf->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->lock);
+ usb_set_intfdata(intf, priv);
+ priv->intf = intf;
+
+ status = f81534a_prepare_gpio(intf);
+ if (status)
+ return status;
+
status = f81534a_ctrl_enable_all_ports(intf);
if (status)
return status;
--
2.17.1
next prev parent reply other threads:[~2019-09-23 2:25 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-09-23 2:24 [PATCH V2 0/7] Add Fintek F81534A series usb-to-serial driver Ji-Ze Hong (Peter Hong)
2019-09-23 2:24 ` [PATCH V2 1/7] USB: serial: f81232: Extract LSR handler Ji-Ze Hong (Peter Hong)
2019-10-23 9:23 ` Johan Hovold
2019-09-23 2:24 ` [PATCH V2 2/7] USB: serial: f81232: Add tx_empty function Ji-Ze Hong (Peter Hong)
2019-10-23 9:15 ` Johan Hovold
2019-09-23 2:24 ` [PATCH V2 3/7] USB: serial: f81232: Use devm_kzalloc Ji-Ze Hong (Peter Hong)
2019-09-23 2:24 ` [PATCH V2 4/7] USB: serial: f81232: Add F81534A support Ji-Ze Hong (Peter Hong)
2019-10-23 9:59 ` Johan Hovold
2019-09-23 2:24 ` [PATCH V2 5/7] USB: serial: f81232: Set F81534A serial port with RS232 mode Ji-Ze Hong (Peter Hong)
2019-10-23 11:53 ` Johan Hovold
2019-10-24 8:52 ` Ji-Ze Hong (Peter Hong)
2020-01-08 14:35 ` Johan Hovold
[not found] ` <3c79f786-de34-550e-3964-d7fb334f6d56@gmail.com>
2020-01-13 15:17 ` Johan Hovold
2020-01-14 1:08 ` Ji-Ze Hong (Peter Hong)
2019-09-23 2:24 ` [PATCH V2 6/7] USB: serial: f81232: Add generator for F81534A Ji-Ze Hong (Peter Hong)
2019-10-23 12:05 ` Johan Hovold
2019-10-23 12:25 ` Johan Hovold
2019-09-23 2:24 ` Ji-Ze Hong (Peter Hong) [this message]
2019-10-23 12:22 ` [PATCH V2 7/7] USB: serial: f81232: Add gpiolib to GPIO device Johan Hovold
2019-10-30 2:00 ` Ji-Ze Hong (Peter Hong)
2020-01-08 14:46 ` Johan Hovold
2020-01-09 2:43 ` Ji-Ze Hong (Peter Hong)
2020-01-13 15:24 ` Johan Hovold
2019-10-23 9:21 ` [PATCH V2 0/7] Add Fintek F81534A series usb-to-serial driver Johan Hovold
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=20190923022449.10952-8-hpeter+linux_kernel@gmail.com \
--to=hpeter@gmail.com \
--cc=gregkh@linuxfoundation.org \
--cc=hpeter+linux_kernel@gmail.com \
--cc=johan@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=peter_hong@fintek.com.tw \
/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.