Commit 268ddb5e authored by Ji-Ze Hong (Peter Hong)'s avatar Ji-Ze Hong (Peter Hong) Committed by Johan Hovold

USB: serial: f81232: add high baud rate support

The F81232 had 4 clocksource 1.846/18.46/14.77/24MHz and baud rates
can be up to 1.5Mbits with 24MHz.

F81232 Clock registers (106h)

Bit1-0:     Clock source selector
                    00: 1.846MHz.
                    01: 18.46MHz.
                    10: 24MHz.
                    11: 14.77MHz.
Signed-off-by: default avatarJi-Ze Hong (Peter Hong) <hpeter+linux_kernel@gmail.com>
Signed-off-by: default avatarJohan Hovold <johan@kernel.org>
parent 1c6b7ab2
...@@ -28,7 +28,8 @@ static const struct usb_device_id id_table[] = { ...@@ -28,7 +28,8 @@ static const struct usb_device_id id_table[] = {
MODULE_DEVICE_TABLE(usb, id_table); MODULE_DEVICE_TABLE(usb, id_table);
/* Maximum baudrate for F81232 */ /* Maximum baudrate for F81232 */
#define F81232_MAX_BAUDRATE 115200 #define F81232_MAX_BAUDRATE 1500000
#define F81232_DEF_BAUDRATE 9600
/* USB Control EP parameter */ /* USB Control EP parameter */
#define F81232_REGISTER_REQUEST 0xa0 #define F81232_REGISTER_REQUEST 0xa0
...@@ -44,18 +45,42 @@ MODULE_DEVICE_TABLE(usb, id_table); ...@@ -44,18 +45,42 @@ MODULE_DEVICE_TABLE(usb, id_table);
#define LINE_STATUS_REGISTER (0x05 + SERIAL_BASE_ADDRESS) #define LINE_STATUS_REGISTER (0x05 + SERIAL_BASE_ADDRESS)
#define MODEM_STATUS_REGISTER (0x06 + SERIAL_BASE_ADDRESS) #define MODEM_STATUS_REGISTER (0x06 + SERIAL_BASE_ADDRESS)
/*
* F81232 Clock registers (106h)
*
* Bit1-0: Clock source selector
* 00: 1.846MHz.
* 01: 18.46MHz.
* 10: 24MHz.
* 11: 14.77MHz.
*/
#define F81232_CLK_REGISTER 0x106
#define F81232_CLK_1_846_MHZ 0
#define F81232_CLK_18_46_MHZ BIT(0)
#define F81232_CLK_24_MHZ BIT(1)
#define F81232_CLK_14_77_MHZ (BIT(1) | BIT(0))
#define F81232_CLK_MASK GENMASK(1, 0)
struct f81232_private { struct f81232_private {
struct mutex lock; struct mutex lock;
u8 modem_control; u8 modem_control;
u8 modem_status; u8 modem_status;
speed_t baud_base;
struct work_struct lsr_work; struct work_struct lsr_work;
struct work_struct interrupt_work; struct work_struct interrupt_work;
struct usb_serial_port *port; struct usb_serial_port *port;
}; };
static int calc_baud_divisor(speed_t baudrate) 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 };
static int calc_baud_divisor(speed_t baudrate, speed_t clockrate)
{ {
return DIV_ROUND_CLOSEST(F81232_MAX_BAUDRATE, baudrate); if (!baudrate)
return 0;
return DIV_ROUND_CLOSEST(clockrate, baudrate);
} }
static int f81232_get_register(struct usb_serial_port *port, u16 reg, u8 *val) static int f81232_get_register(struct usb_serial_port *port, u16 reg, u8 *val)
...@@ -129,6 +154,21 @@ static int f81232_set_register(struct usb_serial_port *port, u16 reg, u8 val) ...@@ -129,6 +154,21 @@ static int f81232_set_register(struct usb_serial_port *port, u16 reg, u8 val)
return status; return status;
} }
static int f81232_set_mask_register(struct usb_serial_port *port, u16 reg,
u8 mask, u8 val)
{
int status;
u8 tmp;
status = f81232_get_register(port, reg, &tmp);
if (status)
return status;
tmp = (tmp & ~mask) | (val & mask);
return f81232_set_register(port, reg, tmp);
}
static void f81232_read_msr(struct usb_serial_port *port) static void f81232_read_msr(struct usb_serial_port *port)
{ {
int status; int status;
...@@ -346,13 +386,53 @@ static void f81232_break_ctl(struct tty_struct *tty, int break_state) ...@@ -346,13 +386,53 @@ static void f81232_break_ctl(struct tty_struct *tty, int break_state)
*/ */
} }
static void f81232_set_baudrate(struct usb_serial_port *port, speed_t baudrate) static int f81232_find_clk(speed_t baudrate)
{
int idx;
for (idx = 0; idx < ARRAY_SIZE(baudrate_table); ++idx) {
if (baudrate <= baudrate_table[idx] &&
baudrate_table[idx] % baudrate == 0)
return idx;
}
return -EINVAL;
}
static void f81232_set_baudrate(struct tty_struct *tty,
struct usb_serial_port *port, speed_t baudrate,
speed_t old_baudrate)
{ {
struct f81232_private *priv = usb_get_serial_port_data(port);
u8 lcr; u8 lcr;
int divisor; int divisor;
int status = 0; int status = 0;
int i;
int idx;
speed_t baud_list[] = { baudrate, old_baudrate, F81232_DEF_BAUDRATE };
for (i = 0; i < ARRAY_SIZE(baud_list); ++i) {
idx = f81232_find_clk(baud_list[i]);
if (idx >= 0) {
baudrate = baud_list[i];
tty_encode_baud_rate(tty, baudrate, baudrate);
break;
}
}
if (idx < 0)
return;
divisor = calc_baud_divisor(baudrate); priv->baud_base = baudrate_table[idx];
divisor = calc_baud_divisor(baudrate, priv->baud_base);
status = f81232_set_mask_register(port, F81232_CLK_REGISTER,
F81232_CLK_MASK, clock_table[idx]);
if (status) {
dev_err(&port->dev, "%s failed to set CLK_REG: %d\n",
__func__, status);
return;
}
status = f81232_get_register(port, LINE_CONTROL_REGISTER, status = f81232_get_register(port, LINE_CONTROL_REGISTER,
&lcr); /* get LCR */ &lcr); /* get LCR */
...@@ -442,6 +522,7 @@ static void f81232_set_termios(struct tty_struct *tty, ...@@ -442,6 +522,7 @@ static void f81232_set_termios(struct tty_struct *tty,
u8 new_lcr = 0; u8 new_lcr = 0;
int status = 0; int status = 0;
speed_t baudrate; speed_t baudrate;
speed_t old_baud;
/* Don't change anything if nothing has changed */ /* Don't change anything if nothing has changed */
if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios)) if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
...@@ -454,11 +535,12 @@ static void f81232_set_termios(struct tty_struct *tty, ...@@ -454,11 +535,12 @@ static void f81232_set_termios(struct tty_struct *tty,
baudrate = tty_get_baud_rate(tty); baudrate = tty_get_baud_rate(tty);
if (baudrate > 0) { if (baudrate > 0) {
if (baudrate > F81232_MAX_BAUDRATE) { if (old_termios)
baudrate = F81232_MAX_BAUDRATE; old_baud = tty_termios_baud_rate(old_termios);
tty_encode_baud_rate(tty, baudrate, baudrate); else
} old_baud = F81232_DEF_BAUDRATE;
f81232_set_baudrate(port, baudrate);
f81232_set_baudrate(tty, port, baudrate, old_baud);
} }
if (C_PARENB(tty)) { if (C_PARENB(tty)) {
...@@ -595,11 +677,12 @@ static int f81232_get_serial_info(struct tty_struct *tty, ...@@ -595,11 +677,12 @@ static int f81232_get_serial_info(struct tty_struct *tty,
struct serial_struct *ss) struct serial_struct *ss)
{ {
struct usb_serial_port *port = tty->driver_data; struct usb_serial_port *port = tty->driver_data;
struct f81232_private *priv = usb_get_serial_port_data(port);
ss->type = PORT_16550A; ss->type = PORT_16550A;
ss->line = port->minor; ss->line = port->minor;
ss->port = port->port_number; ss->port = port->port_number;
ss->baud_base = F81232_MAX_BAUDRATE; ss->baud_base = priv->baud_base;
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment