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: Ji-Ze Hong (Peter Hong) <hpeter+linux_kernel@gmail.com> Signed-off-by: Johan Hovold <johan@kernel.org>
This commit is contained in:
Родитель
1c6b7ab2dd
Коммит
268ddb5e9b
|
@ -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 };
|
||||
|
||||
divisor = calc_baud_divisor(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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче