On Wed, Apr 17, 2019 at 05:55:06PM -0700, jay.dolan@xxxxxxxxxxx wrote: > From: Jay Dolan <jay.dolan@xxxxxxxxxxx> > > The Pericom chips can achieve additional baud rates by programming the > sample clock register. The baud rates can be described as > 921600 * 16 / (16 - scr) for scr values 5 to 15. The divisor is set to 1 > for these baud rates. > > Adds new quirk for Pericom chips other than the four port chips to use > the > > Signed-off-by: Jay Dolan <jay.dolan@xxxxxxxxxxx> > --- > drivers/tty/serial/8250/8250_pci.c | 66 +++++++++++++++++++++-------- > drivers/tty/serial/8250/8250_port.c | 31 ++++++++++++++ > include/linux/serial_8250.h | 3 ++ > include/uapi/linux/serial_reg.h | 5 +++ > 4 files changed, 88 insertions(+), 17 deletions(-) > > diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c > index df41397de478..8f86f5e578ff 100644 > --- a/drivers/tty/serial/8250/8250_pci.c > +++ b/drivers/tty/serial/8250/8250_pci.c > @@ -1333,6 +1333,30 @@ static int pci_pericom_setup(struct serial_private *priv, > { > unsigned int bar, offset = board->first_offset, maxnr; > > + bar = FL_GET_BASE(board->flags); > + if (board->flags & FL_BASE_BARS) > + bar += idx; > + else > + offset += idx * board->uart_offset; > + > + > + maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >> > + (board->reg_shift + 3); > + > + if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) > + return 1; > + > + port->port.set_divisor = pericom_do_set_divisor; > + > + return setup_port(priv, port, bar, offset, board->reg_shift); > +} > + > +static int pci_pericom_setup_four_at_eight(struct serial_private *priv, > + const struct pciserial_board *board, > + struct uart_8250_port *port, int idx) > +{ > + unsigned int bar, offset = board->first_offset, maxnr; > + > bar = FL_GET_BASE(board->flags); > if (board->flags & FL_BASE_BARS) > bar += idx; > @@ -1348,6 +1372,8 @@ static int pci_pericom_setup(struct serial_private *priv, > if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) > return 1; > > + port->port.set_divisor = pericom_do_set_divisor; > + > return setup_port(priv, port, bar, offset, board->reg_shift); > } > > @@ -1995,7 +2021,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { > .device = PCI_DEVICE_ID_PERICOM_PI7C9X7954, > .subvendor = PCI_ANY_ID, > .subdevice = PCI_ANY_ID, > - .setup = pci_pericom_setup, > + .setup = pci_pericom_setup_four_at_eight, > }, > /* > * PLX > @@ -2032,107 +2058,113 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { > .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SDB, > .subvendor = PCI_ANY_ID, > .subdevice = PCI_ANY_ID, > - .setup = pci_pericom_setup, > + .setup = pci_pericom_setup_four_at_eight, > }, > { > .vendor = PCI_VENDOR_ID_ACCESIO, > .device = PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4S, > .subvendor = PCI_ANY_ID, > .subdevice = PCI_ANY_ID, > - .setup = pci_pericom_setup, > + .setup = pci_pericom_setup_four_at_eight, > }, > { > .vendor = PCI_VENDOR_ID_ACCESIO, > .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4DB, > .subvendor = PCI_ANY_ID, > .subdevice = PCI_ANY_ID, > - .setup = pci_pericom_setup, > + .setup = pci_pericom_setup_four_at_eight, > }, > { > .vendor = PCI_VENDOR_ID_ACCESIO, > .device = PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_4, > .subvendor = PCI_ANY_ID, > .subdevice = PCI_ANY_ID, > - .setup = pci_pericom_setup, > + .setup = pci_pericom_setup_four_at_eight, > }, > { > .vendor = PCI_VENDOR_ID_ACCESIO, > .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SMDB, > .subvendor = PCI_ANY_ID, > .subdevice = PCI_ANY_ID, > - .setup = pci_pericom_setup, > + .setup = pci_pericom_setup_four_at_eight, > }, > { > .vendor = PCI_VENDOR_ID_ACCESIO, > .device = PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4SM, > .subvendor = PCI_ANY_ID, > .subdevice = PCI_ANY_ID, > - .setup = pci_pericom_setup, > + .setup = pci_pericom_setup_four_at_eight, > }, > { > .vendor = PCI_VENDOR_ID_ACCESIO, > .device = PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_4, > .subvendor = PCI_ANY_ID, > .subdevice = PCI_ANY_ID, > - .setup = pci_pericom_setup, > + .setup = pci_pericom_setup_four_at_eight, > }, > { > .vendor = PCI_VENDOR_ID_ACCESIO, > .device = PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_4, > .subvendor = PCI_ANY_ID, > .subdevice = PCI_ANY_ID, > - .setup = pci_pericom_setup, > + .setup = pci_pericom_setup_four_at_eight, > }, > { > .vendor = PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4S, > .device = PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_4, > .subvendor = PCI_ANY_ID, > .subdevice = PCI_ANY_ID, > - .setup = pci_pericom_setup, > + .setup = pci_pericom_setup_four_at_eight, > }, > { > .vendor = PCI_VENDOR_ID_ACCESIO, > .device = PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_4, > .subvendor = PCI_ANY_ID, > .subdevice = PCI_ANY_ID, > - .setup = pci_pericom_setup, > + .setup = pci_pericom_setup_four_at_eight, > }, > { > .vendor = PCI_VENDOR_ID_ACCESIO, > .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM422_4, > .subvendor = PCI_ANY_ID, > .subdevice = PCI_ANY_ID, > - .setup = pci_pericom_setup, > + .setup = pci_pericom_setup_four_at_eight, > }, > { > .vendor = PCI_VENDOR_ID_ACCESIO, > .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM485_4, > .subvendor = PCI_ANY_ID, > .subdevice = PCI_ANY_ID, > - .setup = pci_pericom_setup, > + .setup = pci_pericom_setup_four_at_eight, > }, > { > .vendor = PCI_VENDOR_ID_ACCESIO, > .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4, > .subvendor = PCI_ANY_ID, > .subdevice = PCI_ANY_ID, > - .setup = pci_pericom_setup, > + .setup = pci_pericom_setup_four_at_eight, > }, > { > .vendor = PCI_VENDOR_ID_ACCESIO, > .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SM, > .subvendor = PCI_ANY_ID, > .subdevice = PCI_ANY_ID, > - .setup = pci_pericom_setup, > + .setup = pci_pericom_setup_four_at_eight, > }, > { > .vendor = PCI_VENDOR_ID_ACCESIO, > .device = PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM, > .subvendor = PCI_ANY_ID, > .subdevice = PCI_ANY_ID, > - .setup = pci_pericom_setup, > + .setup = pci_pericom_setup_four_at_eight, > }, > - /* > + { > + .vendor = PCI_VENDOR_ID_ACCESIO, > + .device = PCI_ANY_ID, > + .subvendor = PCI_ANY_ID, > + .subdevice = PCI_ANY_ID, > + .setup = pci_pericom_setup, > + }, /* > * SBS Technologies, Inc., PMC-OCTALPRO 232 > */ > { > diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c > index d2f3310abe54..53da396e2f52 100644 > --- a/drivers/tty/serial/8250/8250_port.c > +++ b/drivers/tty/serial/8250/8250_port.c > @@ -2577,6 +2577,37 @@ static unsigned char serial8250_compute_lcr(struct uart_8250_port *up, > return cval; > } > > +void > +pericom_do_set_divisor(struct uart_port *port, unsigned int baud, > + unsigned int quot, unsigned int quot_frac) > +{ > + int scr; > + int lcr; > + int actual_baud; > + int tolerance; > + > + for (scr = 5 ; scr <= 15 ; scr++) { > + actual_baud = 921600 * 16 / scr; > + tolerance = actual_baud / 50; > + > + if ((baud < actual_baud + tolerance) && > + (baud > actual_baud - tolerance)) { > + > + lcr = serial_port_in(port, UART_LCR); > + serial_port_out(port, UART_LCR, lcr | 0x80); > + > + serial_port_out(port, UART_DLL, 1); > + serial_port_out(port, UART_DLM, 0); > + serial_port_out(port, UART_PCM_SCR, 16 - scr); > + serial_port_out(port, UART_LCR, lcr); > + return; > + } else if (baud > actual_baud) { > + break; > + } > + } > + serial8250_do_set_divisor(port, baud, quot, quot_frac); > +} > + > void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud, > unsigned int quot, unsigned int quot_frac) > { > diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h > index 5a655ba8d273..b5469563271f 100644 > --- a/include/linux/serial_8250.h > +++ b/include/linux/serial_8250.h > @@ -167,6 +167,9 @@ extern void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl); > extern void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud, > unsigned int quot, > unsigned int quot_frac); > +extern void pericom_do_set_divisor(struct uart_port *port, unsigned int baud, > + unsigned int quot, > + unsigned int quot_frac); > extern int fsl8250_handle_irq(struct uart_port *port); > int serial8250_handle_irq(struct uart_port *port, unsigned int iir); > unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr); > diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h > index be07b5470f4b..5e01e9ca7dfc 100644 > --- a/include/uapi/linux/serial_reg.h > +++ b/include/uapi/linux/serial_reg.h > @@ -376,5 +376,10 @@ > #define UART_ALTR_EN_TXFIFO_LW 0x01 /* Enable the TX FIFO Low Watermark */ > #define UART_ALTR_TX_LOW 0x41 /* Tx FIFO Low Watermark */ > > +/* > + * Definition for Pericom sample clock register > + */ > +#define UART_PCM_SCR 0x02 /* Sample Clock Register */ I still do not understand why you need this #define in a public userspace .h file. Why? thanks, greg k-h