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[] = {
MODULE_DEVICE_TABLE(usb, id_table);
/* Maximum baudrate for F81232 */
#define F81232_MAX_BAUDRATE 115200
#define F81232_MAX_BAUDRATE 1500000
#define F81232_DEF_BAUDRATE 9600
/* USB Control EP parameter */
#define F81232_REGISTER_REQUEST 0xa0
......@@ -44,18 +45,42 @@ MODULE_DEVICE_TABLE(usb, id_table);
#define LINE_STATUS_REGISTER (0x05 + 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 mutex lock;
u8 modem_control;
u8 modem_status;
speed_t baud_base;
struct work_struct lsr_work;
struct work_struct interrupt_work;
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)
......@@ -129,6 +154,21 @@ static int f81232_set_register(struct usb_serial_port *port, u16 reg, u8 val)
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)
{
int status;
......@@ -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;
int divisor;
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,
&lcr); /* get LCR */
......@@ -442,6 +522,7 @@ static void f81232_set_termios(struct tty_struct *tty,
u8 new_lcr = 0;
int status = 0;
speed_t baudrate;
speed_t old_baud;
/* Don't change anything if nothing has changed */
if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
......@@ -454,11 +535,12 @@ static void f81232_set_termios(struct tty_struct *tty,
baudrate = tty_get_baud_rate(tty);
if (baudrate > 0) {
if (baudrate > F81232_MAX_BAUDRATE) {
baudrate = F81232_MAX_BAUDRATE;
tty_encode_baud_rate(tty, baudrate, baudrate);
}
f81232_set_baudrate(port, baudrate);
if (old_termios)
old_baud = tty_termios_baud_rate(old_termios);
else
old_baud = F81232_DEF_BAUDRATE;
f81232_set_baudrate(tty, port, baudrate, old_baud);
}
if (C_PARENB(tty)) {
......@@ -595,11 +677,12 @@ static int f81232_get_serial_info(struct tty_struct *tty,
struct serial_struct *ss)
{
struct usb_serial_port *port = tty->driver_data;
struct f81232_private *priv = usb_get_serial_port_data(port);
ss->type = PORT_16550A;
ss->line = port->minor;
ss->port = port->port_number;
ss->baud_base = F81232_MAX_BAUDRATE;
ss->baud_base = priv->baud_base;
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