diff options
author | Donald <donald@asix.com.tw> | 2012-04-19 03:00:45 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-04-19 22:08:10 -0400 |
commit | 0eafe4de1a689b69d3f0ce0b5d4aa5333208fd4a (patch) | |
tree | 55a74ab881fef9320d3c4f0861993febd1158c12 /drivers/usb/serial/mos7840.c | |
parent | c256667f0468ebb353c9b11b7feed5c5cba1bd9a (diff) |
USB: serial: mos7840: add support for MCS7810 devices
This patch added the support of MCS7810 device for the mos7840 driver.
The MCS7810 device supports single USB2.0-to-Serial port with
a LED indicator for reflecting transmission or reception activity.
Signed-off-by: Donald Lee <donald@asix.com.tw>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/serial/mos7840.c')
-rw-r--r-- | drivers/usb/serial/mos7840.c | 202 |
1 files changed, 188 insertions, 14 deletions
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index c526550694a0..aaef523955e0 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c | |||
@@ -114,6 +114,7 @@ | |||
114 | #define USB_VENDOR_ID_MOSCHIP 0x9710 | 114 | #define USB_VENDOR_ID_MOSCHIP 0x9710 |
115 | #define MOSCHIP_DEVICE_ID_7840 0x7840 | 115 | #define MOSCHIP_DEVICE_ID_7840 0x7840 |
116 | #define MOSCHIP_DEVICE_ID_7820 0x7820 | 116 | #define MOSCHIP_DEVICE_ID_7820 0x7820 |
117 | #define MOSCHIP_DEVICE_ID_7810 0x7810 | ||
117 | /* The native component can have its vendor/device id's overridden | 118 | /* The native component can have its vendor/device id's overridden |
118 | * in vendor-specific implementations. Such devices can be handled | 119 | * in vendor-specific implementations. Such devices can be handled |
119 | * by making a change here, in moschip_port_id_table, and in | 120 | * by making a change here, in moschip_port_id_table, and in |
@@ -184,10 +185,16 @@ | |||
184 | #define NUM_URBS 16 /* URB Count */ | 185 | #define NUM_URBS 16 /* URB Count */ |
185 | #define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */ | 186 | #define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */ |
186 | 187 | ||
188 | /* LED on/off milliseconds*/ | ||
189 | #define LED_ON_MS 500 | ||
190 | #define LED_OFF_MS 500 | ||
191 | |||
192 | static int device_type; | ||
187 | 193 | ||
188 | static const struct usb_device_id moschip_port_id_table[] = { | 194 | static const struct usb_device_id moschip_port_id_table[] = { |
189 | {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)}, | 195 | {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)}, |
190 | {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)}, | 196 | {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)}, |
197 | {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7810)}, | ||
191 | {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2)}, | 198 | {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2)}, |
192 | {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P)}, | 199 | {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P)}, |
193 | {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4)}, | 200 | {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4)}, |
@@ -209,6 +216,7 @@ static const struct usb_device_id moschip_port_id_table[] = { | |||
209 | static const struct usb_device_id moschip_id_table_combined[] __devinitconst = { | 216 | static const struct usb_device_id moschip_id_table_combined[] __devinitconst = { |
210 | {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)}, | 217 | {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)}, |
211 | {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)}, | 218 | {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)}, |
219 | {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7810)}, | ||
212 | {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2)}, | 220 | {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2)}, |
213 | {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P)}, | 221 | {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P)}, |
214 | {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4)}, | 222 | {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4)}, |
@@ -261,8 +269,13 @@ struct moschip_port { | |||
261 | struct urb *write_urb_pool[NUM_URBS]; | 269 | struct urb *write_urb_pool[NUM_URBS]; |
262 | char busy[NUM_URBS]; | 270 | char busy[NUM_URBS]; |
263 | bool read_urb_busy; | 271 | bool read_urb_busy; |
264 | }; | ||
265 | 272 | ||
273 | /* For device(s) with LED indicator */ | ||
274 | bool has_led; | ||
275 | bool led_flag; | ||
276 | struct timer_list led_timer1; /* Timer for LED on */ | ||
277 | struct timer_list led_timer2; /* Timer for LED off */ | ||
278 | }; | ||
266 | 279 | ||
267 | static bool debug; | 280 | static bool debug; |
268 | 281 | ||
@@ -572,6 +585,69 @@ static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg, | |||
572 | return ret; | 585 | return ret; |
573 | } | 586 | } |
574 | 587 | ||
588 | static void mos7840_set_led_callback(struct urb *urb) | ||
589 | { | ||
590 | switch (urb->status) { | ||
591 | case 0: | ||
592 | /* Success */ | ||
593 | break; | ||
594 | case -ECONNRESET: | ||
595 | case -ENOENT: | ||
596 | case -ESHUTDOWN: | ||
597 | /* This urb is terminated, clean up */ | ||
598 | dbg("%s - urb shutting down with status: %d", __func__, | ||
599 | urb->status); | ||
600 | break; | ||
601 | default: | ||
602 | dbg("%s - nonzero urb status received: %d", __func__, | ||
603 | urb->status); | ||
604 | } | ||
605 | } | ||
606 | |||
607 | static void mos7840_set_led_async(struct moschip_port *mcs, __u16 wval, | ||
608 | __u16 reg) | ||
609 | { | ||
610 | struct usb_device *dev = mcs->port->serial->dev; | ||
611 | struct usb_ctrlrequest *dr = mcs->dr; | ||
612 | |||
613 | dr->bRequestType = MCS_WR_RTYPE; | ||
614 | dr->bRequest = MCS_WRREQ; | ||
615 | dr->wValue = cpu_to_le16(wval); | ||
616 | dr->wIndex = cpu_to_le16(reg); | ||
617 | dr->wLength = cpu_to_le16(0); | ||
618 | |||
619 | usb_fill_control_urb(mcs->control_urb, dev, usb_sndctrlpipe(dev, 0), | ||
620 | (unsigned char *)dr, NULL, 0, mos7840_set_led_callback, NULL); | ||
621 | |||
622 | usb_submit_urb(mcs->control_urb, GFP_ATOMIC); | ||
623 | } | ||
624 | |||
625 | static void mos7840_set_led_sync(struct usb_serial_port *port, __u16 reg, | ||
626 | __u16 val) | ||
627 | { | ||
628 | struct usb_device *dev = port->serial->dev; | ||
629 | |||
630 | usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, MCS_WR_RTYPE, | ||
631 | val, reg, NULL, 0, MOS_WDR_TIMEOUT); | ||
632 | } | ||
633 | |||
634 | static void mos7840_led_off(unsigned long arg) | ||
635 | { | ||
636 | struct moschip_port *mcs = (struct moschip_port *) arg; | ||
637 | |||
638 | /* Turn off LED */ | ||
639 | mos7840_set_led_async(mcs, 0x0300, MODEM_CONTROL_REGISTER); | ||
640 | mod_timer(&mcs->led_timer2, | ||
641 | jiffies + msecs_to_jiffies(LED_OFF_MS)); | ||
642 | } | ||
643 | |||
644 | static void mos7840_led_flag_off(unsigned long arg) | ||
645 | { | ||
646 | struct moschip_port *mcs = (struct moschip_port *) arg; | ||
647 | |||
648 | mcs->led_flag = false; | ||
649 | } | ||
650 | |||
575 | /***************************************************************************** | 651 | /***************************************************************************** |
576 | * mos7840_interrupt_callback | 652 | * mos7840_interrupt_callback |
577 | * this is the callback function for when we have received data on the | 653 | * this is the callback function for when we have received data on the |
@@ -792,6 +868,14 @@ static void mos7840_bulk_in_callback(struct urb *urb) | |||
792 | return; | 868 | return; |
793 | } | 869 | } |
794 | 870 | ||
871 | /* Turn on LED */ | ||
872 | if (mos7840_port->has_led && !mos7840_port->led_flag) { | ||
873 | mos7840_port->led_flag = true; | ||
874 | mos7840_set_led_async(mos7840_port, 0x0301, | ||
875 | MODEM_CONTROL_REGISTER); | ||
876 | mod_timer(&mos7840_port->led_timer1, | ||
877 | jiffies + msecs_to_jiffies(LED_ON_MS)); | ||
878 | } | ||
795 | 879 | ||
796 | mos7840_port->read_urb_busy = true; | 880 | mos7840_port->read_urb_busy = true; |
797 | retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC); | 881 | retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC); |
@@ -1554,6 +1638,14 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, | |||
1554 | data1 = urb->transfer_buffer; | 1638 | data1 = urb->transfer_buffer; |
1555 | dbg("bulkout endpoint is %d", port->bulk_out_endpointAddress); | 1639 | dbg("bulkout endpoint is %d", port->bulk_out_endpointAddress); |
1556 | 1640 | ||
1641 | /* Turn on LED */ | ||
1642 | if (mos7840_port->has_led && !mos7840_port->led_flag) { | ||
1643 | mos7840_port->led_flag = true; | ||
1644 | mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0301); | ||
1645 | mod_timer(&mos7840_port->led_timer1, | ||
1646 | jiffies + msecs_to_jiffies(LED_ON_MS)); | ||
1647 | } | ||
1648 | |||
1557 | /* send it down the pipe */ | 1649 | /* send it down the pipe */ |
1558 | status = usb_submit_urb(urb, GFP_ATOMIC); | 1650 | status = usb_submit_urb(urb, GFP_ATOMIC); |
1559 | 1651 | ||
@@ -2327,28 +2419,74 @@ static int mos7840_ioctl(struct tty_struct *tty, | |||
2327 | return -ENOIOCTLCMD; | 2419 | return -ENOIOCTLCMD; |
2328 | } | 2420 | } |
2329 | 2421 | ||
2422 | static int mos7810_check(struct usb_serial *serial) | ||
2423 | { | ||
2424 | int i, pass_count = 0; | ||
2425 | __u16 data = 0, mcr_data = 0; | ||
2426 | __u16 test_pattern = 0x55AA; | ||
2427 | |||
2428 | /* Store MCR setting */ | ||
2429 | usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), | ||
2430 | MCS_RDREQ, MCS_RD_RTYPE, 0x0300, MODEM_CONTROL_REGISTER, | ||
2431 | &mcr_data, VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT); | ||
2432 | |||
2433 | for (i = 0; i < 16; i++) { | ||
2434 | /* Send the 1-bit test pattern out to MCS7810 test pin */ | ||
2435 | usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), | ||
2436 | MCS_WRREQ, MCS_WR_RTYPE, | ||
2437 | (0x0300 | (((test_pattern >> i) & 0x0001) << 1)), | ||
2438 | MODEM_CONTROL_REGISTER, NULL, 0, MOS_WDR_TIMEOUT); | ||
2439 | |||
2440 | /* Read the test pattern back */ | ||
2441 | usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), | ||
2442 | MCS_RDREQ, MCS_RD_RTYPE, 0, GPIO_REGISTER, &data, | ||
2443 | VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT); | ||
2444 | |||
2445 | /* If this is a MCS7810 device, both test patterns must match */ | ||
2446 | if (((test_pattern >> i) ^ (~data >> 1)) & 0x0001) | ||
2447 | break; | ||
2448 | |||
2449 | pass_count++; | ||
2450 | } | ||
2451 | |||
2452 | /* Restore MCR setting */ | ||
2453 | usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), MCS_WRREQ, | ||
2454 | MCS_WR_RTYPE, 0x0300 | mcr_data, MODEM_CONTROL_REGISTER, NULL, | ||
2455 | 0, MOS_WDR_TIMEOUT); | ||
2456 | |||
2457 | if (pass_count == 16) | ||
2458 | return 1; | ||
2459 | |||
2460 | return 0; | ||
2461 | } | ||
2462 | |||
2330 | static int mos7840_calc_num_ports(struct usb_serial *serial) | 2463 | static int mos7840_calc_num_ports(struct usb_serial *serial) |
2331 | { | 2464 | { |
2332 | __u16 Data = 0x00; | 2465 | __u16 data = 0x00; |
2333 | int ret = 0; | ||
2334 | int mos7840_num_ports; | 2466 | int mos7840_num_ports; |
2335 | 2467 | ||
2336 | ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), | 2468 | usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), |
2337 | MCS_RDREQ, MCS_RD_RTYPE, 0, GPIO_REGISTER, &Data, | 2469 | MCS_RDREQ, MCS_RD_RTYPE, 0, GPIO_REGISTER, &data, |
2338 | VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT); | 2470 | VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT); |
2339 | 2471 | ||
2340 | if ((Data & 0x01) == 0) { | 2472 | if (serial->dev->descriptor.idProduct == MOSCHIP_DEVICE_ID_7810 || |
2341 | mos7840_num_ports = 2; | 2473 | serial->dev->descriptor.idProduct == MOSCHIP_DEVICE_ID_7820) { |
2342 | serial->num_bulk_in = 2; | 2474 | device_type = serial->dev->descriptor.idProduct; |
2343 | serial->num_bulk_out = 2; | ||
2344 | serial->num_ports = 2; | ||
2345 | } else { | 2475 | } else { |
2346 | mos7840_num_ports = 4; | 2476 | /* For a MCS7840 device GPIO0 must be set to 1 */ |
2347 | serial->num_bulk_in = 4; | 2477 | if ((data & 0x01) == 1) |
2348 | serial->num_bulk_out = 4; | 2478 | device_type = MOSCHIP_DEVICE_ID_7840; |
2349 | serial->num_ports = 4; | 2479 | else if (mos7810_check(serial)) |
2480 | device_type = MOSCHIP_DEVICE_ID_7810; | ||
2481 | else | ||
2482 | device_type = MOSCHIP_DEVICE_ID_7820; | ||
2350 | } | 2483 | } |
2351 | 2484 | ||
2485 | mos7840_num_ports = (device_type >> 4) & 0x000F; | ||
2486 | serial->num_bulk_in = mos7840_num_ports; | ||
2487 | serial->num_bulk_out = mos7840_num_ports; | ||
2488 | serial->num_ports = mos7840_num_ports; | ||
2489 | |||
2352 | return mos7840_num_ports; | 2490 | return mos7840_num_ports; |
2353 | } | 2491 | } |
2354 | 2492 | ||
@@ -2563,6 +2701,34 @@ static int mos7840_startup(struct usb_serial *serial) | |||
2563 | status = -ENOMEM; | 2701 | status = -ENOMEM; |
2564 | goto error; | 2702 | goto error; |
2565 | } | 2703 | } |
2704 | |||
2705 | mos7840_port->has_led = false; | ||
2706 | |||
2707 | /* Initialize LED timers */ | ||
2708 | if (device_type == MOSCHIP_DEVICE_ID_7810) { | ||
2709 | mos7840_port->has_led = true; | ||
2710 | |||
2711 | init_timer(&mos7840_port->led_timer1); | ||
2712 | mos7840_port->led_timer1.function = mos7840_led_off; | ||
2713 | mos7840_port->led_timer1.expires = | ||
2714 | jiffies + msecs_to_jiffies(LED_ON_MS); | ||
2715 | mos7840_port->led_timer1.data = | ||
2716 | (unsigned long)mos7840_port; | ||
2717 | |||
2718 | init_timer(&mos7840_port->led_timer2); | ||
2719 | mos7840_port->led_timer2.function = | ||
2720 | mos7840_led_flag_off; | ||
2721 | mos7840_port->led_timer2.expires = | ||
2722 | jiffies + msecs_to_jiffies(LED_OFF_MS); | ||
2723 | mos7840_port->led_timer2.data = | ||
2724 | (unsigned long)mos7840_port; | ||
2725 | |||
2726 | mos7840_port->led_flag = false; | ||
2727 | |||
2728 | /* Turn off LED */ | ||
2729 | mos7840_set_led_sync(serial->port[i], | ||
2730 | MODEM_CONTROL_REGISTER, 0x0300); | ||
2731 | } | ||
2566 | } | 2732 | } |
2567 | dbg ("mos7840_startup: all ports configured..........."); | 2733 | dbg ("mos7840_startup: all ports configured..........."); |
2568 | 2734 | ||
@@ -2654,6 +2820,14 @@ static void mos7840_release(struct usb_serial *serial) | |||
2654 | mos7840_port = mos7840_get_port_private(serial->port[i]); | 2820 | mos7840_port = mos7840_get_port_private(serial->port[i]); |
2655 | dbg("mos7840_port %d = %p", i, mos7840_port); | 2821 | dbg("mos7840_port %d = %p", i, mos7840_port); |
2656 | if (mos7840_port) { | 2822 | if (mos7840_port) { |
2823 | if (mos7840_port->has_led) { | ||
2824 | /* Turn off LED */ | ||
2825 | mos7840_set_led_sync(mos7840_port->port, | ||
2826 | MODEM_CONTROL_REGISTER, 0x0300); | ||
2827 | |||
2828 | del_timer_sync(&mos7840_port->led_timer1); | ||
2829 | del_timer_sync(&mos7840_port->led_timer2); | ||
2830 | } | ||
2657 | kfree(mos7840_port->ctrl_buf); | 2831 | kfree(mos7840_port->ctrl_buf); |
2658 | kfree(mos7840_port->dr); | 2832 | kfree(mos7840_port->dr); |
2659 | kfree(mos7840_port); | 2833 | kfree(mos7840_port); |