diff options
Diffstat (limited to 'drivers/tty/serial/8250/8250_fintek.c')
| -rw-r--r-- | drivers/tty/serial/8250/8250_fintek.c | 166 |
1 files changed, 132 insertions, 34 deletions
diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index e500f7dd2470..b4461a89b8d0 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Probe for F81216A LPC to 4 UART * * Copyright (C) 2014-2016 Ricardo Ribalda, Qtechnology A/S - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. */ #include <linux/module.h> #include <linux/pci.h> @@ -23,7 +19,9 @@ #define CHIP_ID2 0x21 #define CHIP_ID_F81865 0x0407 #define CHIP_ID_F81866 0x1010 +#define CHIP_ID_F81966 0x0215 #define CHIP_ID_F81216AD 0x1602 +#define CHIP_ID_F81216E 0x1617 #define CHIP_ID_F81216H 0x0501 #define CHIP_ID_F81216 0x0802 #define VENDOR_ID1 0x23 @@ -40,6 +38,16 @@ #define IRQ_LEVEL_LOW 0 #define IRQ_EDGE_HIGH BIT(5) +/* + * F81216H clock source register, the value and mask is the same with F81866, + * but it's on F0h. + * + * Clock speeds for UART (register F0h) + * 00: 1.8432MHz. + * 01: 18.432MHz. + * 10: 24MHz. + * 11: 14.769MHz. + */ #define RS485 0xF0 #define RTS_INVERT BIT(5) #define RS485_URA BIT(4) @@ -56,9 +64,9 @@ #define F81216_LDN_HIGH 0x4 /* - * F81866 registers + * F81866/966 registers * - * The IRQ setting mode of F81866 is not the same with F81216 series. + * The IRQ setting mode of F81866/966 is not the same with F81216 series. * Level/Low: IRQ_MODE0:0, IRQ_MODE1:0 * Edge/High: IRQ_MODE0:1, IRQ_MODE1:0 * @@ -118,6 +126,9 @@ static int fintek_8250_enter_key(u16 base_port, u8 key) if (!request_muxed_region(base_port, 2, "8250_fintek")) return -EBUSY; + /* Force to deactivate all SuperIO in this base_port */ + outb(EXIT_KEY, base_port + ADDR_PORT); + outb(key, base_port + ADDR_PORT); outb(key, base_port + ADDR_PORT); return 0; @@ -146,7 +157,9 @@ static int fintek_8250_check_id(struct fintek_8250 *pdata) switch (chip) { case CHIP_ID_F81865: case CHIP_ID_F81866: + case CHIP_ID_F81966: case CHIP_ID_F81216AD: + case CHIP_ID_F81216E: case CHIP_ID_F81216H: case CHIP_ID_F81216: break; @@ -162,6 +175,7 @@ static int fintek_8250_get_ldn_range(struct fintek_8250 *pdata, int *min, int *max) { switch (pdata->pid) { + case CHIP_ID_F81966: case CHIP_ID_F81865: case CHIP_ID_F81866: *min = F81866_LDN_LOW; @@ -169,6 +183,7 @@ static int fintek_8250_get_ldn_range(struct fintek_8250 *pdata, int *min, return 0; case CHIP_ID_F81216AD: + case CHIP_ID_F81216E: case CHIP_ID_F81216H: case CHIP_ID_F81216: *min = F81216_LDN_LOW; @@ -179,7 +194,7 @@ static int fintek_8250_get_ldn_range(struct fintek_8250 *pdata, int *min, return -ENODEV; } -static int fintek_8250_rs485_config(struct uart_port *port, +static int fintek_8250_rs485_config(struct uart_port *port, struct ktermios *termios, struct serial_rs485 *rs485) { uint8_t config = 0; @@ -188,13 +203,14 @@ static int fintek_8250_rs485_config(struct uart_port *port, if (!pdata) return -EINVAL; - if (rs485->flags & SER_RS485_ENABLED) - memset(rs485->padding, 0, sizeof(rs485->padding)); - else - memset(rs485, 0, sizeof(*rs485)); - rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | - SER_RS485_RTS_AFTER_SEND; + if (rs485->flags & SER_RS485_ENABLED) { + /* Hardware do not support same RTS level on send and receive */ + if (!(rs485->flags & SER_RS485_RTS_ON_SEND) == + !(rs485->flags & SER_RS485_RTS_AFTER_SEND)) + return -EINVAL; + config |= RS485_URA; + } if (rs485->delay_rts_before_send) { rs485->delay_rts_before_send = 1; @@ -206,12 +222,6 @@ static int fintek_8250_rs485_config(struct uart_port *port, config |= RXW4C_IRA; } - if ((!!(rs485->flags & SER_RS485_RTS_ON_SEND)) == - (!!(rs485->flags & SER_RS485_RTS_AFTER_SEND))) - rs485->flags &= SER_RS485_ENABLED; - else - config |= RS485_URA; - if (rs485->flags & SER_RS485_RTS_ON_SEND) config |= RTS_INVERT; @@ -222,8 +232,6 @@ static int fintek_8250_rs485_config(struct uart_port *port, sio_write_reg(pdata, RS485, config); fintek_8250_exit_key(pdata->base_port); - port->rs485 = *rs485; - return 0; } @@ -232,10 +240,11 @@ static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) sio_write_reg(pdata, LDN, pdata->index); switch (pdata->pid) { + case CHIP_ID_F81966: case CHIP_ID_F81866: sio_write_mask_reg(pdata, F81866_FIFO_CTRL, F81866_IRQ_MODE1, 0); - /* fall through */ + fallthrough; case CHIP_ID_F81865: sio_write_mask_reg(pdata, F81866_IRQ_MODE, F81866_IRQ_SHARE, F81866_IRQ_SHARE); @@ -244,6 +253,7 @@ static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) break; case CHIP_ID_F81216AD: + case CHIP_ID_F81216E: case CHIP_ID_F81216H: case CHIP_ID_F81216: sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_SHARE, @@ -257,7 +267,9 @@ static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) static void fintek_8250_set_max_fifo(struct fintek_8250 *pdata) { switch (pdata->pid) { - case CHIP_ID_F81216H: /* 128Bytes FIFO */ + case CHIP_ID_F81216E: /* 128Bytes FIFO */ + case CHIP_ID_F81216H: + case CHIP_ID_F81966: case CHIP_ID_F81866: sio_write_mask_reg(pdata, FIFO_CTRL, FIFO_MODE_MASK | RXFTHR_MODE_MASK, @@ -269,20 +281,85 @@ static void fintek_8250_set_max_fifo(struct fintek_8250 *pdata) } } -static void fintek_8250_goto_highspeed(struct uart_8250_port *uart, - struct fintek_8250 *pdata) +static void fintek_8250_set_termios(struct uart_port *port, + struct ktermios *termios, + const struct ktermios *old) { - sio_write_reg(pdata, LDN, pdata->index); + struct fintek_8250 *pdata = port->private_data; + unsigned int baud = tty_termios_baud_rate(termios); + int i; + u8 reg; + static u32 baudrate_table[] = {115200, 921600, 1152000, 1500000}; + static u8 clock_table[] = { F81866_UART_CLK_1_8432MHZ, + F81866_UART_CLK_14_769MHZ, F81866_UART_CLK_18_432MHZ, + F81866_UART_CLK_24MHZ }; + + /* + * We'll use serial8250_do_set_termios() for baud = 0, otherwise It'll + * crash on baudrate_table[i] % baud with "division by zero". + */ + if (!baud) + goto exit; switch (pdata->pid) { - case CHIP_ID_F81866: /* set uart clock for high speed serial mode */ - sio_write_mask_reg(pdata, F81866_UART_CLK, - F81866_UART_CLK_MASK, - F81866_UART_CLK_14_769MHZ); + case CHIP_ID_F81216E: + case CHIP_ID_F81216H: + reg = RS485; + break; + case CHIP_ID_F81966: + case CHIP_ID_F81866: + reg = F81866_UART_CLK; + break; + default: + /* Don't change clocksource with unknown PID */ + dev_warn(port->dev, + "%s: pid: %x Not support. use default set_termios.\n", + __func__, pdata->pid); + goto exit; + } + + for (i = 0; i < ARRAY_SIZE(baudrate_table); ++i) { + if (baud > baudrate_table[i] || baudrate_table[i] % baud != 0) + continue; - uart->port.uartclk = 921600 * 16; + if (port->uartclk == baudrate_table[i] * 16) + break; + + if (fintek_8250_enter_key(pdata->base_port, pdata->key)) + continue; + + port->uartclk = baudrate_table[i] * 16; + + sio_write_reg(pdata, LDN, pdata->index); + sio_write_mask_reg(pdata, reg, F81866_UART_CLK_MASK, + clock_table[i]); + + fintek_8250_exit_key(pdata->base_port); + break; + } + + if (i == ARRAY_SIZE(baudrate_table)) { + baud = tty_termios_baud_rate(old); + tty_termios_encode_baud_rate(termios, baud, baud); + } + +exit: + serial8250_do_set_termios(port, termios, old); +} + +static void fintek_8250_set_termios_handler(struct uart_8250_port *uart) +{ + struct fintek_8250 *pdata = uart->port.private_data; + + switch (pdata->pid) { + case CHIP_ID_F81216E: + case CHIP_ID_F81216H: + case CHIP_ID_F81966: + case CHIP_ID_F81866: + uart->port.set_termios = fintek_8250_set_termios; break; - default: /* leave clock speed untouched */ + + default: break; } } @@ -327,7 +404,6 @@ static int probe_setup_port(struct fintek_8250 *pdata, fintek_8250_set_irq_mode(pdata, level_mode); fintek_8250_set_max_fifo(pdata); - fintek_8250_goto_highspeed(uart, pdata); fintek_8250_exit_key(addr[i]); @@ -341,6 +417,17 @@ static int probe_setup_port(struct fintek_8250 *pdata, return -ENODEV; } +/* Only the first port supports delays */ +static const struct serial_rs485 fintek_8250_rs485_supported_port0 = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, +}; + +static const struct serial_rs485 fintek_8250_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, +}; + static void fintek_8250_set_rs485_handler(struct uart_8250_port *uart) { struct fintek_8250 *pdata = uart->port.private_data; @@ -348,9 +435,19 @@ static void fintek_8250_set_rs485_handler(struct uart_8250_port *uart) switch (pdata->pid) { case CHIP_ID_F81216AD: case CHIP_ID_F81216H: + case CHIP_ID_F81966: case CHIP_ID_F81866: case CHIP_ID_F81865: uart->port.rs485_config = fintek_8250_rs485_config; + if (!pdata->index) + uart->port.rs485_supported = fintek_8250_rs485_supported_port0; + else + uart->port.rs485_supported = fintek_8250_rs485_supported; + break; + + case CHIP_ID_F81216E: /* F81216E does not support RS485 delays */ + uart->port.rs485_config = fintek_8250_rs485_config; + uart->port.rs485_supported = fintek_8250_rs485_supported; break; default: /* No RS485 Auto direction functional */ @@ -373,6 +470,7 @@ int fintek_8250_probe(struct uart_8250_port *uart) memcpy(pdata, &probe_data, sizeof(probe_data)); uart->port.private_data = pdata; fintek_8250_set_rs485_handler(uart); + fintek_8250_set_termios_handler(uart); return 0; } |
