[PATCH V2 7/7] USB: serial: f81232: Add gpiolib to GPIO device

From: Ji-Ze Hong (Peter Hong)
Date: Sun Sep 22 2019 - 22:25:26 EST


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@xxxxxxxxx>
---
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