Commit 55c7c0fd authored by Alan Cox's avatar Alan Cox Committed by Greg Kroah-Hartman

serial: quatech: add the other serial identifiers and preliminary control code

Jonathan Woithe posted an out of tree enabler/control module for these
cards.  Lift the relevant identifiers and put them in the 8250_pci driver
along with code used to control custom registers on these cards.
Signed-off-by: default avatarAlan Cox <alan@linux.intel.com>
Signed-off-by: default avatarJonathan Woithe <jwoithe@just42.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9931faca
......@@ -1040,6 +1040,253 @@ static int pci_asix_setup(struct serial_private *priv,
return pci_default_setup(priv, board, port, idx);
}
/* Quatech devices have their own extra interface features */
struct quatech_feature {
u16 devid;
bool amcc;
};
#define QPCR_TEST_FOR1 0x3F
#define QPCR_TEST_GET1 0x00
#define QPCR_TEST_FOR2 0x40
#define QPCR_TEST_GET2 0x40
#define QPCR_TEST_FOR3 0x80
#define QPCR_TEST_GET3 0x40
#define QPCR_TEST_FOR4 0xC0
#define QPCR_TEST_GET4 0x80
#define QOPR_CLOCK_X1 0x0000
#define QOPR_CLOCK_X2 0x0001
#define QOPR_CLOCK_X4 0x0002
#define QOPR_CLOCK_X8 0x0003
#define QOPR_CLOCK_RATE_MASK 0x0003
static struct quatech_feature quatech_cards[] = {
{ PCI_DEVICE_ID_QUATECH_QSC100, 1 },
{ PCI_DEVICE_ID_QUATECH_DSC100, 1 },
{ PCI_DEVICE_ID_QUATECH_DSC100E, 0 },
{ PCI_DEVICE_ID_QUATECH_DSC200, 1 },
{ PCI_DEVICE_ID_QUATECH_DSC200E, 0 },
{ PCI_DEVICE_ID_QUATECH_ESC100D, 1 },
{ PCI_DEVICE_ID_QUATECH_ESC100M, 1 },
{ PCI_DEVICE_ID_QUATECH_QSCP100, 1 },
{ PCI_DEVICE_ID_QUATECH_DSCP100, 1 },
{ PCI_DEVICE_ID_QUATECH_QSCP200, 1 },
{ PCI_DEVICE_ID_QUATECH_DSCP200, 1 },
{ PCI_DEVICE_ID_QUATECH_ESCLP100, 0 },
{ PCI_DEVICE_ID_QUATECH_QSCLP100, 0 },
{ PCI_DEVICE_ID_QUATECH_DSCLP100, 0 },
{ PCI_DEVICE_ID_QUATECH_SSCLP100, 0 },
{ PCI_DEVICE_ID_QUATECH_QSCLP200, 0 },
{ PCI_DEVICE_ID_QUATECH_DSCLP200, 0 },
{ PCI_DEVICE_ID_QUATECH_SSCLP200, 0 },
{ PCI_DEVICE_ID_QUATECH_SPPXP_100, 0 },
{ 0, }
};
static int pci_quatech_amcc(u16 devid)
{
struct quatech_feature *qf = &quatech_cards[0];
while (qf->devid) {
if (qf->devid == devid)
return qf->amcc;
qf++;
}
pr_err("quatech: unknown port type '0x%04X'.\n", devid);
return 0;
};
static int pci_quatech_rqopr(struct uart_8250_port *port)
{
unsigned long base = port->port.iobase;
u8 LCR, val;
LCR = inb(base + UART_LCR);
outb(0xBF, base + UART_LCR);
val = inb(base + UART_SCR);
outb(LCR, base + UART_LCR);
return val;
}
static void pci_quatech_wqopr(struct uart_8250_port *port, u8 qopr)
{
unsigned long base = port->port.iobase;
u8 LCR, val;
LCR = inb(base + UART_LCR);
outb(0xBF, base + UART_LCR);
val = inb(base + UART_SCR);
outb(qopr, base + UART_SCR);
outb(LCR, base + UART_LCR);
}
static int pci_quatech_rqmcr(struct uart_8250_port *port)
{
unsigned long base = port->port.iobase;
u8 LCR, val, qmcr;
LCR = inb(base + UART_LCR);
outb(0xBF, base + UART_LCR);
val = inb(base + UART_SCR);
outb(val | 0x10, base + UART_SCR);
qmcr = inb(base + UART_MCR);
outb(val, base + UART_SCR);
outb(LCR, base + UART_LCR);
return qmcr;
}
static void pci_quatech_wqmcr(struct uart_8250_port *port, u8 qmcr)
{
unsigned long base = port->port.iobase;
u8 LCR, val;
LCR = inb(base + UART_LCR);
outb(0xBF, base + UART_LCR);
val = inb(base + UART_SCR);
outb(val | 0x10, base + UART_SCR);
outb(qmcr, base + UART_MCR);
outb(val, base + UART_SCR);
outb(LCR, base + UART_LCR);
}
static int pci_quatech_has_qmcr(struct uart_8250_port *port)
{
unsigned long base = port->port.iobase;
u8 LCR, val;
LCR = inb(base + UART_LCR);
outb(0xBF, base + UART_LCR);
val = inb(base + UART_SCR);
if (val & 0x20) {
outb(0x80, UART_LCR);
if (!(inb(UART_SCR) & 0x20)) {
outb(LCR, base + UART_LCR);
return 1;
}
}
return 0;
}
static int pci_quatech_test(struct uart_8250_port *port)
{
u8 reg;
u8 qopr = pci_quatech_rqopr(port);
pci_quatech_wqopr(port, qopr & QPCR_TEST_FOR1);
reg = pci_quatech_rqopr(port) & 0xC0;
if (reg != QPCR_TEST_GET1)
return -EINVAL;
pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR2);
reg = pci_quatech_rqopr(port) & 0xC0;
if (reg != QPCR_TEST_GET2)
return -EINVAL;
pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR3);
reg = pci_quatech_rqopr(port) & 0xC0;
if (reg != QPCR_TEST_GET3)
return -EINVAL;
pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR4);
reg = pci_quatech_rqopr(port) & 0xC0;
if (reg != QPCR_TEST_GET4)
return -EINVAL;
pci_quatech_wqopr(port, qopr);
return 0;
}
static int pci_quatech_clock(struct uart_8250_port *port)
{
u8 qopr, reg, set;
unsigned long clock;
if (pci_quatech_test(port) < 0)
return 1843200;
qopr = pci_quatech_rqopr(port);
pci_quatech_wqopr(port, qopr & ~QOPR_CLOCK_X8);
reg = pci_quatech_rqopr(port);
if (reg & QOPR_CLOCK_X8) {
clock = 1843200;
goto out;
}
pci_quatech_wqopr(port, qopr | QOPR_CLOCK_X8);
reg = pci_quatech_rqopr(port);
if (!(reg & QOPR_CLOCK_X8)) {
clock = 1843200;
goto out;
}
reg &= QOPR_CLOCK_X8;
if (reg == QOPR_CLOCK_X2) {
clock = 3685400;
set = QOPR_CLOCK_X2;
} else if (reg == QOPR_CLOCK_X4) {
clock = 7372800;
set = QOPR_CLOCK_X4;
} else if (reg == QOPR_CLOCK_X8) {
clock = 14745600;
set = QOPR_CLOCK_X8;
} else {
clock = 1843200;
set = QOPR_CLOCK_X1;
}
qopr &= ~QOPR_CLOCK_RATE_MASK;
qopr |= set;
out:
pci_quatech_wqopr(port, qopr);
return clock;
}
static int pci_quatech_rs422(struct uart_8250_port *port)
{
u8 qmcr;
int rs422 = 0;
if (!pci_quatech_has_qmcr(port))
return 0;
qmcr = pci_quatech_rqmcr(port);
pci_quatech_wqmcr(port, 0xFF);
if (pci_quatech_rqmcr(port))
rs422 = 1;
pci_quatech_wqmcr(port, qmcr);
return rs422;
}
static int pci_quatech_init(struct pci_dev *dev)
{
if (pci_quatech_amcc(dev->device)) {
unsigned long base = pci_resource_start(dev, 0);
if (base) {
u32 tmp;
outl(inl(base + 0x38), base + 0x38);
tmp = inl(base + 0x3c);
outl(tmp | 0x01000000, base + 0x3c);
outl(tmp, base + 0x3c);
}
}
return 0;
}
static int pci_quatech_setup(struct serial_private *priv,
const struct pciserial_board *board,
struct uart_8250_port *port, int idx)
{
/* Needed by pci_quatech calls below */
port->port.iobase = pci_resource_start(priv->dev, FL_GET_BASE(board->flags));
/* Set up the clocking */
port->port.uartclk = pci_quatech_clock(port);
/* For now just warn about RS422 */
if (pci_quatech_rs422(port))
pr_warn("quatech: software control of RS422 features not currently supported.\n");
return pci_default_setup(priv, board, port, idx);
}
static void __devexit pci_quatech_exit(struct pci_dev *dev)
{
}
static int pci_default_setup(struct serial_private *priv,
const struct pciserial_board *board,
struct uart_8250_port *port, int idx)
......@@ -1528,6 +1775,16 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.setup = pci_ni8430_setup,
.exit = pci_ni8430_exit,
},
/* Quatech */
{
.vendor = PCI_VENDOR_ID_QUATECH,
.device = PCI_ANY_ID,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.init = pci_quatech_init,
.setup = pci_quatech_setup,
.exit = __devexit_p(pci_quatech_exit),
},
/*
* Panacom
*/
......@@ -3475,18 +3732,70 @@ static struct pci_device_id serial_pci_tbl[] = {
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS,
0x10b5, 0x106a, 0, 0,
pbn_plx_romulus },
/*
* Quatech cards. These actually have configurable clocks but for
* now we just use the default.
*
* 100 series are RS232, 200 series RS422,
*/
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_4_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_2_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100E,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_2_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC200,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_2_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC200E,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_2_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC200,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_4_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_8_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_8_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCP100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_4_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCP100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_2_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCP200,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_4_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCP200,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_2_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_4_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_2_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_1_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP200,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_4_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP200,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_2_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP200,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_1_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESCLP100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_8_115200 },
{ PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954,
PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4,
0, 0,
......
......@@ -1868,8 +1868,23 @@
#define PCI_VENDOR_ID_QUATECH 0x135C
#define PCI_DEVICE_ID_QUATECH_QSC100 0x0010
#define PCI_DEVICE_ID_QUATECH_DSC100 0x0020
#define PCI_DEVICE_ID_QUATECH_DSC200 0x0030
#define PCI_DEVICE_ID_QUATECH_QSC200 0x0040
#define PCI_DEVICE_ID_QUATECH_ESC100D 0x0050
#define PCI_DEVICE_ID_QUATECH_ESC100M 0x0060
#define PCI_DEVICE_ID_QUATECH_QSCP100 0x0120
#define PCI_DEVICE_ID_QUATECH_DSCP100 0x0130
#define PCI_DEVICE_ID_QUATECH_QSCP200 0x0140
#define PCI_DEVICE_ID_QUATECH_DSCP200 0x0150
#define PCI_DEVICE_ID_QUATECH_QSCLP100 0x0170
#define PCI_DEVICE_ID_QUATECH_DSCLP100 0x0180
#define PCI_DEVICE_ID_QUATECH_DSC100E 0x0181
#define PCI_DEVICE_ID_QUATECH_SSCLP100 0x0190
#define PCI_DEVICE_ID_QUATECH_QSCLP200 0x01A0
#define PCI_DEVICE_ID_QUATECH_DSCLP200 0x01B0
#define PCI_DEVICE_ID_QUATECH_DSC200E 0x01B1
#define PCI_DEVICE_ID_QUATECH_SSCLP200 0x01C0
#define PCI_DEVICE_ID_QUATECH_ESCLP100 0x01E0
#define PCI_DEVICE_ID_QUATECH_SPPXP_100 0x0278
#define PCI_VENDOR_ID_SEALEVEL 0x135e
......
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