diff mbox series

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

Message ID 20190923022449.10952-8-hpeter+linux_kernel@gmail.com (mailing list archive)
State New, archived
Headers show
Series Add Fintek F81534A series usb-to-serial driver | expand

Commit Message

Ji-Ze Hong (Peter Hong) Sept. 23, 2019, 2:24 a.m. UTC
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(+)

Comments

Johan Hovold Oct. 23, 2019, 12:22 p.m. UTC | #1
On Mon, Sep 23, 2019 at 10:24:49AM +0800, Ji-Ze Hong (Peter Hong) wrote:
> 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.

Depending to your answer to my question whether these pins are truly
general purpose or not, this may not be the right interface.

> 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)

Looks like this one needs to go under CONFIG_GPIOLIB as well.

> +{
> +	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)

Could you rename this f81534a_gpiochip_register or similar?

> +{
> +	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;

Maybe store the parent struct usb_device instead since that's what you
end up using?

> +	mutex_init(&priv->lock);

Already initialised in probe() below. Same with priv->intf by the way.

> +
> +	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;
> +	}

Have you tried disconnecting the device with gpios requested? This used
to break gpiolib, but was fixed. Just want to make sure it hasn't
regressed.

> +
> +	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;

Overall this all look much better now.

Thanks,
Johan
Ji-Ze Hong (Peter Hong) Oct. 30, 2019, 2 a.m. UTC | #2
Hi Johan,

Johan Hovold 於 2019/10/23 下午 08:22 寫道:
> On Mon, Sep 23, 2019 at 10:24:49AM +0800, Ji-Ze Hong (Peter Hong) wrote:
>> 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.
> 
> Depending to your answer to my question whether these pins are truly
> general purpose or not, this may not be the right interface.

Our F81534A series contains F81532A/534A/535/536. For the following link
of F81534A pin-out:
	https://imgur.com/a/AZHqQ1N

We had 2 type about GPIO pins, MODEx_y & GPIOxx. All MODEx_y & GPIOxx
are GPIOs and can be controlled by GPIO device, but they had some
difference about usage.
	MODEx_y:
		1. 3 pins(x: 0/1/2) can be access by UART port y.
		2. Used to control UART's transceiver normally, but it
		   also can be configure as GPIO when UART disabled by
		   H/W (DTR strap to GND).
	GPIOxx:
		1. Access only by GPIO device.

The series patch only support RS233 mode for all serial port, So we'll
direct set all MODEx_y to (0/0/1) for our demo board for default. If
user really want to use the pin, we had provide the gpiolib with GPIO
device, but we'll recommend user to use GPIOxy first.

Is any suggest about this ? Could I maintain this for this series patch?

>> +
>> +	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;
>> +	}
> 
> Have you tried disconnecting the device with gpios requested? This used
> to break gpiolib, but was fixed. Just want to make sure it hasn't
> regressed.

I had try export GPIOs and detach the F81534A in kernel 5.0.0, it seems
no problem. Is any link about this issue for me to do more test ?

Thanks
Johan Hovold Jan. 8, 2020, 2:46 p.m. UTC | #3
On Wed, Oct 30, 2019 at 10:00:12AM +0800, Ji-Ze Hong (Peter Hong) wrote:
> Hi Johan,
> 
> Johan Hovold 於 2019/10/23 下午 08:22 寫道:
> > On Mon, Sep 23, 2019 at 10:24:49AM +0800, Ji-Ze Hong (Peter Hong) wrote:
> >> 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.
> > 
> > Depending to your answer to my question whether these pins are truly
> > general purpose or not, this may not be the right interface.
> 
> Our F81534A series contains F81532A/534A/535/536. For the following link
> of F81534A pin-out:
> 	https://imgur.com/a/AZHqQ1N
> 
> We had 2 type about GPIO pins, MODEx_y & GPIOxx. All MODEx_y & GPIOxx
> are GPIOs and can be controlled by GPIO device, but they had some
> difference about usage.
> 	MODEx_y:
> 		1. 3 pins(x: 0/1/2) can be access by UART port y.
> 		2. Used to control UART's transceiver normally, but it
> 		   also can be configure as GPIO when UART disabled by
> 		   H/W (DTR strap to GND).
> 	GPIOxx:
> 		1. Access only by GPIO device.
> 
> The series patch only support RS233 mode for all serial port, So we'll
> direct set all MODEx_y to (0/0/1) for our demo board for default. If
> user really want to use the pin, we had provide the gpiolib with GPIO
> device, but we'll recommend user to use GPIOxy first.

Do you mean that you'd need to register a separate gpio chip per port in
order to expose an interface for changing the MODEx_y pins for an
enabled UART? Or can you do that through the "global" gpio device?

> Is any suggest about this ? Could I maintain this for this series patch?

I understood from your other mail that the gpio device would not be able
to control the pins of an enabled port. In either case, I think you need
to refuse a request for a pin that's already in use by the corresponding
port.

Also is there a way to determine the number of available pins by
detecting the chip/package type? I'm assuming not all 36 pins are always
accessible?

> >> +	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;
> >> +	}
> > 
> > Have you tried disconnecting the device with gpios requested? This used
> > to break gpiolib, but was fixed. Just want to make sure it hasn't
> > regressed.
> 
> I had try export GPIOs and detach the F81534A in kernel 5.0.0, it seems
> no problem. Is any link about this issue for me to do more test ?

Then it's hopefully fine. Thanks for verifying.

Johan
Ji-Ze Hong (Peter Hong) Jan. 9, 2020, 2:43 a.m. UTC | #4
Hi Johan,

Johan Hovold 於 2020/1/8 下午 10:46 寫道:
> On Wed, Oct 30, 2019 at 10:00:12AM +0800, Ji-Ze Hong (Peter Hong) wrote:

>> We had 2 type about GPIO pins, MODEx_y & GPIOxx. All MODEx_y & GPIOxx
>> are GPIOs and can be controlled by GPIO device, but they had some
>> difference about usage.
>> 	MODEx_y:
>> 		1. 3 pins(x: 0/1/2) can be access by UART port y.
>> 		2. Used to control UART's transceiver normally, but it
>> 		   also can be configure as GPIO when UART disabled by
>> 		   H/W (DTR strap to GND).
>> 	GPIOxx:
>> 		1. Access only by GPIO device.
>>
>> The series patch only support RS233 mode for all serial port, So we'll
>> direct set all MODEx_y to (0/0/1) for our demo board for default. If
>> user really want to use the pin, we had provide the gpiolib with GPIO
>> device, but we'll recommend user to use GPIOxy first.
> 
> Do you mean that you'd need to register a separate gpio chip per port in
> order to expose an interface for changing the MODEx_y pins for an
> enabled UART? Or can you do that through the "global" gpio device?

No, I still implement the gpiolib in GPIO Device. Sorry for bad
describe.

>> Is any suggest about this ? Could I maintain this for this series patch?
> 
> I understood from your other mail that the gpio device would not be able
> to control the pins of an enabled port. In either case, I think you need
> to refuse a request for a pin that's already in use by the corresponding
> port.

OK, I'll change the code as previous mail as following:

I can read the UART enable state from GPIO Device, so I can do when the
GPIO is associated with UART enabled, change it as output only otherwise
can be set to input/output.

> Also is there a way to determine the number of available pins by
> detecting the chip/package type? I'm assuming not all 36 pins are always
> accessible?

Yes, we had register to get package type, I'll add UART enable & package
type to determinate final GPIO pin out.
Johan Hovold Jan. 13, 2020, 3:24 p.m. UTC | #5
On Thu, Jan 09, 2020 at 10:43:48AM +0800, Ji-Ze Hong (Peter Hong) wrote:

> Johan Hovold 於 2020/1/8 下午 10:46 寫道:

> > I understood from your other mail that the gpio device would not be able
> > to control the pins of an enabled port. In either case, I think you need
> > to refuse a request for a pin that's already in use by the corresponding
> > port.
> 
> OK, I'll change the code as previous mail as following:
> 
> I can read the UART enable state from GPIO Device, so I can do when the
> GPIO is associated with UART enabled, change it as output only otherwise
> can be set to input/output.
> 
> > Also is there a way to determine the number of available pins by
> > detecting the chip/package type? I'm assuming not all 36 pins are always
> > accessible?
> 
> Yes, we had register to get package type, I'll add UART enable & package
> type to determinate final GPIO pin out.

I suggest you start without any gpiochip, just add a simple control
driver which enables each UART (only the ones available in the package
and which have not been hardware disabled perhaps).

We don't want a user to be able to change the tranceiver mode behind the
serial driver's back so to speak.

Exposing GPIO pins (not MODE pins) in packages which have those enabled
should be fine, but you can add that later.

Johan
diff mbox series

Patch

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;