USB: serial: f81232: add control driver for F81534A
The Fintek F81534A series contains 1 HUB, 1 GPIO device and n UARTs. The UARTs are disabled by default and need to be enabled by the GPIO device (2c42:16F8). When F81534A plug to host, we can only see 1 HUB and 1 GPIO device and we write 0x8fff to GPIO device register F81534A_CTRL_CMD_ENABLE_PORT (116h) to enable all available serial ports. Signed-off-by: Ji-Ze Hong (Peter Hong) <hpeter+linux_kernel@gmail.com> [johan: reword commit message and an error message slightly] Signed-off-by: Johan Hovold <johan@kernel.org>
This commit is contained in:
parent
615e58ccfb
commit
33a6b48a57
|
@ -36,6 +36,9 @@
|
|||
{ USB_DEVICE(0x2c42, 0x1635) }, /* 8 port UART device */ \
|
||||
{ USB_DEVICE(0x2c42, 0x1636) } /* 12 port UART device */
|
||||
|
||||
#define F81534A_CTRL_ID \
|
||||
{ USB_DEVICE(0x2c42, 0x16f8) } /* Global control device */
|
||||
|
||||
static const struct usb_device_id f81232_id_table[] = {
|
||||
F81232_ID,
|
||||
{ } /* Terminating entry */
|
||||
|
@ -46,9 +49,15 @@ static const struct usb_device_id f81534a_id_table[] = {
|
|||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
static const struct usb_device_id f81534a_ctrl_id_table[] = {
|
||||
F81534A_CTRL_ID,
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
static const struct usb_device_id combined_id_table[] = {
|
||||
F81232_ID,
|
||||
F81534A_SERIES_ID,
|
||||
F81534A_CTRL_ID,
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, combined_id_table);
|
||||
|
@ -61,6 +70,7 @@ MODULE_DEVICE_TABLE(usb, combined_id_table);
|
|||
#define F81232_REGISTER_REQUEST 0xa0
|
||||
#define F81232_GET_REGISTER 0xc0
|
||||
#define F81232_SET_REGISTER 0x40
|
||||
#define F81534A_ACCESS_REG_RETRY 2
|
||||
|
||||
#define SERIAL_BASE_ADDRESS 0x0120
|
||||
#define RECEIVE_BUFFER_REGISTER (0x00 + SERIAL_BASE_ADDRESS)
|
||||
|
@ -101,6 +111,8 @@ MODULE_DEVICE_TABLE(usb, combined_id_table);
|
|||
#define F81534A_GPIO_MODE1_OUTPUT BIT(1)
|
||||
#define F81534A_GPIO_MODE0_OUTPUT BIT(0)
|
||||
|
||||
#define F81534A_CTRL_CMD_ENABLE_PORT 0x116
|
||||
|
||||
struct f81232_private {
|
||||
struct mutex lock;
|
||||
u8 modem_control;
|
||||
|
@ -848,6 +860,93 @@ static void f81232_lsr_worker(struct work_struct *work)
|
|||
dev_warn(&port->dev, "read LSR failed: %d\n", status);
|
||||
}
|
||||
|
||||
static int f81534a_ctrl_set_register(struct usb_interface *intf, u16 reg,
|
||||
u16 size, void *val)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
int retry = F81534A_ACCESS_REG_RETRY;
|
||||
int status;
|
||||
u8 *tmp;
|
||||
|
||||
tmp = kmemdup(val, size, GFP_KERNEL);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
|
||||
while (retry--) {
|
||||
status = usb_control_msg(dev,
|
||||
usb_sndctrlpipe(dev, 0),
|
||||
F81232_REGISTER_REQUEST,
|
||||
F81232_SET_REGISTER,
|
||||
reg,
|
||||
0,
|
||||
tmp,
|
||||
size,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
if (status < 0) {
|
||||
status = usb_translate_errors(status);
|
||||
if (status == -EIO)
|
||||
continue;
|
||||
} else if (status != size) {
|
||||
/* Retry on short transfers */
|
||||
status = -EIO;
|
||||
continue;
|
||||
} else {
|
||||
status = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (status) {
|
||||
dev_err(&intf->dev, "failed to set register 0x%x: %d\n",
|
||||
reg, status);
|
||||
}
|
||||
|
||||
kfree(tmp);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int f81534a_ctrl_enable_all_ports(struct usb_interface *intf, bool en)
|
||||
{
|
||||
unsigned char enable[2] = {0};
|
||||
int status;
|
||||
|
||||
/*
|
||||
* Enable all available serial ports, define as following:
|
||||
* bit 15 : Reset behavior (when HUB got soft reset)
|
||||
* 0: maintain all serial port enabled state.
|
||||
* 1: disable all serial port.
|
||||
* bit 0~11 : Serial port enable bit.
|
||||
*/
|
||||
if (en) {
|
||||
enable[0] = 0xff;
|
||||
enable[1] = 0x8f;
|
||||
}
|
||||
|
||||
status = f81534a_ctrl_set_register(intf, F81534A_CTRL_CMD_ENABLE_PORT,
|
||||
sizeof(enable), enable);
|
||||
if (status)
|
||||
dev_err(&intf->dev, "failed to enable ports: %d\n", status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int f81534a_ctrl_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
return f81534a_ctrl_enable_all_ports(intf, true);
|
||||
}
|
||||
|
||||
static void f81534a_ctrl_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
f81534a_ctrl_enable_all_ports(intf, false);
|
||||
}
|
||||
|
||||
static int f81534a_ctrl_resume(struct usb_interface *intf)
|
||||
{
|
||||
return f81534a_ctrl_enable_all_ports(intf, true);
|
||||
}
|
||||
|
||||
static int f81232_port_probe(struct usb_serial_port *port)
|
||||
{
|
||||
struct f81232_private *priv;
|
||||
|
@ -975,7 +1074,41 @@ static struct usb_serial_driver * const serial_drivers[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
module_usb_serial_driver(serial_drivers, combined_id_table);
|
||||
static struct usb_driver f81534a_ctrl_driver = {
|
||||
.name = "f81534a_ctrl",
|
||||
.id_table = f81534a_ctrl_id_table,
|
||||
.probe = f81534a_ctrl_probe,
|
||||
.disconnect = f81534a_ctrl_disconnect,
|
||||
.resume = f81534a_ctrl_resume,
|
||||
};
|
||||
|
||||
static int __init f81232_init(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = usb_register_driver(&f81534a_ctrl_driver, THIS_MODULE,
|
||||
KBUILD_MODNAME);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
status = usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME,
|
||||
combined_id_table);
|
||||
if (status) {
|
||||
usb_deregister(&f81534a_ctrl_driver);
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit f81232_exit(void)
|
||||
{
|
||||
usb_serial_deregister_drivers(serial_drivers);
|
||||
usb_deregister(&f81534a_ctrl_driver);
|
||||
}
|
||||
|
||||
module_init(f81232_init);
|
||||
module_exit(f81232_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Fintek F81232/532A/534A/535/536 USB to serial driver");
|
||||
MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");
|
||||
|
|
Loading…
Reference in New Issue