diff options
author | Peter Hung <hpeter@gmail.com> | 2015-03-17 05:48:25 -0400 |
---|---|---|
committer | Johan Hovold <johan@kernel.org> | 2015-03-27 12:29:27 -0400 |
commit | 8bb4ca6b56bdf65944d0848561ea8dcf53a87507 (patch) | |
tree | 93b35959b2195dd53f964ce24e6235ac03d9f0fa /drivers/usb/serial | |
parent | 94f87309fb12e3d41dbc56df899f3b357277d0bf (diff) |
USB: f81232: implement set_termios()
The original driver had do not any h/w change in driver.
This patch implements with configure H/W for
baud/parity/word length/stop bits functional in f81232_set_termios().
This patch also implement DTR/RTS control when baudrate B0.
We drop DTR/RTS when B0, otherwise enable it.
We are checking baudrate in set_termios() too, If baudrate larger then 115200,
it will be changed to 115200 and use tty_encode_baud_rate() to encode into tty
Signed-off-by: Peter Hung <hpeter+linux_kernel@gmail.com>
Signed-off-by: Johan Hovold <johan@kernel.org>
Diffstat (limited to 'drivers/usb/serial')
-rw-r--r-- | drivers/usb/serial/f81232.c | 112 |
1 files changed, 108 insertions, 4 deletions
diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 960282304b50..c00ced1ed281 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c | |||
@@ -31,14 +31,19 @@ static const struct usb_device_id id_table[] = { | |||
31 | }; | 31 | }; |
32 | MODULE_DEVICE_TABLE(usb, id_table); | 32 | MODULE_DEVICE_TABLE(usb, id_table); |
33 | 33 | ||
34 | /* Maximum baudrate for F81232 */ | ||
35 | #define F81232_MAX_BAUDRATE 115200 | ||
36 | |||
34 | /* USB Control EP parameter */ | 37 | /* USB Control EP parameter */ |
35 | #define F81232_REGISTER_REQUEST 0xa0 | 38 | #define F81232_REGISTER_REQUEST 0xa0 |
36 | #define F81232_GET_REGISTER 0xc0 | 39 | #define F81232_GET_REGISTER 0xc0 |
37 | #define F81232_SET_REGISTER 0x40 | 40 | #define F81232_SET_REGISTER 0x40 |
38 | 41 | ||
39 | #define SERIAL_BASE_ADDRESS 0x0120 | 42 | #define SERIAL_BASE_ADDRESS 0x0120 |
43 | #define RECEIVE_BUFFER_REGISTER (0x00 + SERIAL_BASE_ADDRESS) | ||
40 | #define INTERRUPT_ENABLE_REGISTER (0x01 + SERIAL_BASE_ADDRESS) | 44 | #define INTERRUPT_ENABLE_REGISTER (0x01 + SERIAL_BASE_ADDRESS) |
41 | #define FIFO_CONTROL_REGISTER (0x02 + SERIAL_BASE_ADDRESS) | 45 | #define FIFO_CONTROL_REGISTER (0x02 + SERIAL_BASE_ADDRESS) |
46 | #define LINE_CONTROL_REGISTER (0x03 + SERIAL_BASE_ADDRESS) | ||
42 | #define MODEM_CONTROL_REGISTER (0x04 + SERIAL_BASE_ADDRESS) | 47 | #define MODEM_CONTROL_REGISTER (0x04 + SERIAL_BASE_ADDRESS) |
43 | #define MODEM_STATUS_REGISTER (0x06 + SERIAL_BASE_ADDRESS) | 48 | #define MODEM_STATUS_REGISTER (0x06 + SERIAL_BASE_ADDRESS) |
44 | 49 | ||
@@ -64,6 +69,11 @@ struct f81232_private { | |||
64 | struct usb_serial_port *port; | 69 | struct usb_serial_port *port; |
65 | }; | 70 | }; |
66 | 71 | ||
72 | static int calc_baud_divisor(speed_t baudrate) | ||
73 | { | ||
74 | return DIV_ROUND_CLOSEST(F81232_MAX_BAUDRATE, baudrate); | ||
75 | } | ||
76 | |||
67 | static int f81232_get_register(struct usb_serial_port *port, u16 reg, u8 *val) | 77 | static int f81232_get_register(struct usb_serial_port *port, u16 reg, u8 *val) |
68 | { | 78 | { |
69 | int status; | 79 | int status; |
@@ -350,6 +360,53 @@ static void f81232_break_ctl(struct tty_struct *tty, int break_state) | |||
350 | */ | 360 | */ |
351 | } | 361 | } |
352 | 362 | ||
363 | static void f81232_set_baudrate(struct usb_serial_port *port, speed_t baudrate) | ||
364 | { | ||
365 | u8 lcr; | ||
366 | int divisor, status = 0; | ||
367 | |||
368 | divisor = calc_baud_divisor(baudrate); | ||
369 | |||
370 | status = f81232_get_register(port, LINE_CONTROL_REGISTER, | ||
371 | &lcr); /* get LCR */ | ||
372 | if (status) { | ||
373 | dev_err(&port->dev, "%s failed to get LCR: %d\n", | ||
374 | __func__, status); | ||
375 | return; | ||
376 | } | ||
377 | |||
378 | status = f81232_set_register(port, LINE_CONTROL_REGISTER, | ||
379 | lcr | UART_LCR_DLAB); /* Enable DLAB */ | ||
380 | if (status) { | ||
381 | dev_err(&port->dev, "%s failed to set DLAB: %d\n", | ||
382 | __func__, status); | ||
383 | return; | ||
384 | } | ||
385 | |||
386 | status = f81232_set_register(port, RECEIVE_BUFFER_REGISTER, | ||
387 | divisor & 0x00ff); /* low */ | ||
388 | if (status) { | ||
389 | dev_err(&port->dev, "%s failed to set baudrate MSB: %d\n", | ||
390 | __func__, status); | ||
391 | goto reapply_lcr; | ||
392 | } | ||
393 | |||
394 | status = f81232_set_register(port, INTERRUPT_ENABLE_REGISTER, | ||
395 | (divisor & 0xff00) >> 8); /* high */ | ||
396 | if (status) { | ||
397 | dev_err(&port->dev, "%s failed to set baudrate LSB: %d\n", | ||
398 | __func__, status); | ||
399 | } | ||
400 | |||
401 | reapply_lcr: | ||
402 | status = f81232_set_register(port, LINE_CONTROL_REGISTER, | ||
403 | lcr & ~UART_LCR_DLAB); | ||
404 | if (status) { | ||
405 | dev_err(&port->dev, "%s failed to set DLAB: %d\n", | ||
406 | __func__, status); | ||
407 | } | ||
408 | } | ||
409 | |||
353 | static int f81232_port_enable(struct usb_serial_port *port) | 410 | static int f81232_port_enable(struct usb_serial_port *port) |
354 | { | 411 | { |
355 | u8 val; | 412 | u8 val; |
@@ -395,15 +452,62 @@ static int f81232_port_disable(struct usb_serial_port *port) | |||
395 | static void f81232_set_termios(struct tty_struct *tty, | 452 | static void f81232_set_termios(struct tty_struct *tty, |
396 | struct usb_serial_port *port, struct ktermios *old_termios) | 453 | struct usb_serial_port *port, struct ktermios *old_termios) |
397 | { | 454 | { |
398 | /* FIXME - Stubbed out for now */ | 455 | u8 new_lcr = 0; |
456 | int status = 0; | ||
457 | speed_t baudrate; | ||
399 | 458 | ||
400 | /* Don't change anything if nothing has changed */ | 459 | /* Don't change anything if nothing has changed */ |
401 | if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios)) | 460 | if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios)) |
402 | return; | 461 | return; |
403 | 462 | ||
404 | /* Do the real work here... */ | 463 | if (C_BAUD(tty) == B0) |
405 | if (old_termios) | 464 | f81232_set_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS); |
406 | tty_termios_copy_hw(&tty->termios, old_termios); | 465 | else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) |
466 | f81232_set_mctrl(port, TIOCM_DTR | TIOCM_RTS, 0); | ||
467 | |||
468 | baudrate = tty_get_baud_rate(tty); | ||
469 | if (baudrate > 0) { | ||
470 | if (baudrate > F81232_MAX_BAUDRATE) { | ||
471 | baudrate = F81232_MAX_BAUDRATE; | ||
472 | tty_encode_baud_rate(tty, baudrate, baudrate); | ||
473 | } | ||
474 | f81232_set_baudrate(port, baudrate); | ||
475 | } | ||
476 | |||
477 | if (C_PARENB(tty)) { | ||
478 | new_lcr |= UART_LCR_PARITY; | ||
479 | |||
480 | if (!C_PARODD(tty)) | ||
481 | new_lcr |= UART_LCR_EPAR; | ||
482 | |||
483 | if (C_CMSPAR(tty)) | ||
484 | new_lcr |= UART_LCR_SPAR; | ||
485 | } | ||
486 | |||
487 | if (C_CSTOPB(tty)) | ||
488 | new_lcr |= UART_LCR_STOP; | ||
489 | |||
490 | switch (C_CSIZE(tty)) { | ||
491 | case CS5: | ||
492 | new_lcr |= UART_LCR_WLEN5; | ||
493 | break; | ||
494 | case CS6: | ||
495 | new_lcr |= UART_LCR_WLEN6; | ||
496 | break; | ||
497 | case CS7: | ||
498 | new_lcr |= UART_LCR_WLEN7; | ||
499 | break; | ||
500 | default: | ||
501 | case CS8: | ||
502 | new_lcr |= UART_LCR_WLEN8; | ||
503 | break; | ||
504 | } | ||
505 | |||
506 | status = f81232_set_register(port, LINE_CONTROL_REGISTER, new_lcr); | ||
507 | if (status) { | ||
508 | dev_err(&port->dev, "%s failed to set LCR: %d\n", | ||
509 | __func__, status); | ||
510 | } | ||
407 | } | 511 | } |
408 | 512 | ||
409 | static int f81232_tiocmget(struct tty_struct *tty) | 513 | static int f81232_tiocmget(struct tty_struct *tty) |