/* * CTI Serial port line types. These match the values stored in the first * nibble of the CTI EEPROM port_flags word.
*/ enum cti_port_type {
CTI_PORT_TYPE_NONE = 0,
CTI_PORT_TYPE_RS232, // RS232 ONLY
CTI_PORT_TYPE_RS422_485, // RS422/RS485 ONLY
CTI_PORT_TYPE_RS232_422_485_HW, // RS232/422/485 HW ONLY Switchable
CTI_PORT_TYPE_RS232_422_485_SW, // RS232/422/485 SW ONLY Switchable
CTI_PORT_TYPE_RS232_422_485_4B, // RS232/422/485 HW/SW (4bit ex. BCG004)
CTI_PORT_TYPE_RS232_422_485_2B, // RS232/422/485 HW/SW (2bit ex. BBG008)
CTI_PORT_TYPE_MAX,
};
/** * struct exar8250_board - board information * @num_ports: number of serial ports * @reg_shift: describes UART register mapping in PCI memory * @setup: quirk run at ->probe() stage for each port * @exit: quirk run at ->remove() stage
*/ struct exar8250_board { unsignedint num_ports; unsignedint reg_shift; int (*setup)(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, int idx); void (*exit)(struct pci_dev *pcidev);
};
if (eeprom->reg_data_in)
regb |= UART_EXAR_REGB_EEDI; if (eeprom->reg_data_clock)
regb |= UART_EXAR_REGB_EECK; if (eeprom->reg_chip_select)
regb |= UART_EXAR_REGB_EECS;
/** * exar_mpio_config_output() - Configure an Exar MPIO as an output * @priv: Device's private structure * @mpio_num: MPIO number/offset to configure * * Configure a single MPIO as an output and disable tristate. It is recommended * to set the level with exar_mpio_set_high()/exar_mpio_set_low() prior to * calling this function to ensure default MPIO pin state. * * Return: 0 on success, negative error code on failure
*/ staticint exar_mpio_config_output(struct exar8250 *priv, unsignedint mpio_num)
{ unsignedint mpio_offset;
u8 sel_reg; // MPIO Select register (input/output)
u8 tri_reg; // MPIO Tristate register
u8 value;
// Disable MPIO pin tri-state
value = exar_read_reg(priv, tri_reg);
value &= ~BIT(mpio_offset);
exar_write_reg(priv, tri_reg, value);
value = exar_read_reg(priv, sel_reg);
value &= ~BIT(mpio_offset);
exar_write_reg(priv, sel_reg, value);
return 0;
}
/** * _exar_mpio_set() - Set an Exar MPIO output high or low * @priv: Device's private structure * @mpio_num: MPIO number/offset to set * @high: Set MPIO high if true, low if false * * Set a single MPIO high or low. exar_mpio_config_output() must also be called * to configure the pin as an output. * * Return: 0 on success, negative error code on failure
*/ staticint _exar_mpio_set(struct exar8250 *priv, unsignedint mpio_num, bool high)
{ unsignedint mpio_offset;
u8 lvl_reg;
u8 value;
staticvoid exar_pm(struct uart_port *port, unsignedint state, unsignedint old)
{ /* * Exar UARTs have a SLEEP register that enables or disables each UART * to enter sleep mode separately. On the XR17V35x the register * is accessible to each UART at the UART_EXAR_SLEEP offset, but * the UART channel may only write to the corresponding bit.
*/
serial_port_out(port, UART_EXAR_SLEEP, state ? 0xff : 0);
}
/* * XR17V35x UARTs have an extra fractional divisor register (DLD) * Calculate divisor with extra 4-bit fractional portion
*/ staticunsignedint xr17v35x_get_divisor(struct uart_port *p, unsignedint baud, unsignedint *frac)
{ unsignedint quot_16;
/* Preserve bits not related to baudrate; DLD[7:4]. */
quot_frac |= serial_port_in(p, 0x2) & 0xf0;
serial_port_out(p, 0x2, quot_frac);
}
staticint xr17v35x_startup(struct uart_port *port)
{ /* * First enable access to IER [7:5], ISR [5:4], FCR [5:4], * MCR [7:5] and MSR [7:0]
*/
serial_port_out(port, UART_XR_EFR, UART_EFR_ECB);
/* * Make sure all interrupts are masked until initialization is * complete and the FIFOs are cleared * * Synchronize UART_IER access against the console.
*/
uart_port_lock_irq(port);
serial_port_out(port, UART_IER, 0);
uart_port_unlock_irq(port);
/* * XR17V35x UARTs have an extra divisor register, DLD that gets enabled * with when DLAB is set which will cause the device to incorrectly match * and assign port type to PORT_16650. The EFR for this UART is found * at offset 0x09. Instead check the Deice ID (DVID) register * for a 2, 4 or 8 port UART.
*/
status = readb(port->port.membase + UART_EXAR_DVID); if (status == 0x82 || status == 0x84 || status == 0x88) {
port->port.type = PORT_XR17V35X;
writeb(0x00, p + UART_EXAR_8XMODE);
writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR);
writeb(32, p + UART_EXAR_TXTRG);
writeb(32, p + UART_EXAR_RXTRG);
/* Skip the initial (per device) setup */ if (idx) return 0;
/* * Setup Multipurpose Input/Output pins.
*/ switch (pcidev->device) { case PCI_DEVICE_ID_COMMTECH_4222PCI335: case PCI_DEVICE_ID_COMMTECH_4224PCI335:
writeb(0x78, p + UART_EXAR_MPIOLVL_7_0);
writeb(0x00, p + UART_EXAR_MPIOINV_7_0);
writeb(0x00, p + UART_EXAR_MPIOSEL_7_0); break; case PCI_DEVICE_ID_COMMTECH_2324PCI335: case PCI_DEVICE_ID_COMMTECH_2328PCI335:
writeb(0x00, p + UART_EXAR_MPIOLVL_7_0);
writeb(0xc0, p + UART_EXAR_MPIOINV_7_0);
writeb(0xc0, p + UART_EXAR_MPIOSEL_7_0); break; default: break;
}
writeb(0x00, p + UART_EXAR_MPIOINT_7_0);
writeb(0x00, p + UART_EXAR_MPIO3T_7_0);
writeb(0x00, p + UART_EXAR_MPIOOD_7_0);
return 0;
}
/** * cti_tristate_disable() - Disable RS485 transciever tristate * @priv: Device's private structure * @port_num: Port number to set tristate off * * Most RS485 capable cards have a power on tristate jumper/switch that ensures * the RS422/RS485 transceiver does not drive a multi-drop RS485 bus when it is * not the master. When this jumper is installed the user must set the RS485 * mode to Full or Half duplex to disable tristate prior to using the port. * * Some Exar UARTs have an auto-tristate feature while others require setting * an MPIO to disable the tristate. * * Return: 0 on success, negative error code on failure
*/ staticint cti_tristate_disable(struct exar8250 *priv, unsignedint port_num)
{ int ret;
ret = exar_mpio_set_high(priv, port_num); if (ret) return ret;
return exar_mpio_config_output(priv, port_num);
}
/** * cti_plx_int_enable() - Enable UART interrupts to PLX bridge * @priv: Device's private structure * * Some older CTI cards require MPIO_0 to be set low to enable the * interrupts from the UART to the PLX PCI->PCIe bridge. * * Return: 0 on success, negative error code on failure
*/ staticint cti_plx_int_enable(struct exar8250 *priv)
{ int ret;
ret = exar_mpio_set_low(priv, 0); if (ret) return ret;
return exar_mpio_config_output(priv, 0);
}
/** * cti_read_osc_freq() - Read the UART oscillator frequency from EEPROM * @priv: Device's private structure * @eeprom_offset: Offset where the oscillator frequency is stored * * CTI XR17x15X and XR17V25X cards have the serial boards oscillator frequency * stored in the EEPROM. FPGA and XR17V35X based cards use the PCI/PCIe clock. * * Return: frequency on success, negative error code on failure
*/ staticint cti_read_osc_freq(struct exar8250 *priv, u8 eeprom_offset)
{
__le16 ee_words[2];
u32 osc_freq;
/** * cti_get_port_type_xr17c15x_xr17v25x() - Get port type of xr17c15x/xr17v25x * @priv: Device's private structure * @pcidev: Pointer to the PCI device for this port * @port_num: Port to get type of * * CTI xr17c15x and xr17v25x based cards port types are based on PCI IDs. * * Return: port type on success, CTI_PORT_TYPE_NONE on failure
*/ staticenum cti_port_type cti_get_port_type_xr17c15x_xr17v25x(struct exar8250 *priv, struct pci_dev *pcidev, unsignedint port_num)
{ switch (pcidev->subsystem_device) { // RS232 only cards case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232_NS: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232_NS: return CTI_PORT_TYPE_RS232; // 1x RS232, 1x RS422/RS485 case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1: return (port_num == 0) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; // 2x RS232, 2x RS422/RS485 case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2: return (port_num < 2) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; // 4x RS232, 4x RS422/RS485 case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP: return (port_num < 4) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; // RS232/RS422/RS485 HW (jumper) selectable case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_SP_OPTO: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_A: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_B: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP: return CTI_PORT_TYPE_RS232_422_485_HW; // RS422/RS485 HW (jumper) selectable case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485: return CTI_PORT_TYPE_RS422_485; // 6x RS232, 2x RS422/RS485 case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP: return (port_num < 6) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; // 2x RS232, 6x RS422/RS485 case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP: return (port_num < 2) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; default:
dev_err(&pcidev->dev, "unknown/unsupported device\n"); return CTI_PORT_TYPE_NONE;
}
}
/** * cti_get_port_type_fpga() - Get the port type of a CTI FPGA card * @priv: Device's private structure * @pcidev: Pointer to the PCI device for this port * @port_num: Port to get type of * * FPGA based cards port types are based on PCI IDs. * * Return: port type on success, CTI_PORT_TYPE_NONE on failure
*/ staticenum cti_port_type cti_get_port_type_fpga(struct exar8250 *priv, struct pci_dev *pcidev, unsignedint port_num)
{ switch (pcidev->device) { case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG00X: case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG01X: case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_16: return CTI_PORT_TYPE_RS232_422_485_HW; default:
dev_err(&pcidev->dev, "unknown/unsupported device\n"); return CTI_PORT_TYPE_NONE;
}
}
/** * cti_get_port_type_xr17v35x() - Read port type from the EEPROM * @priv: Device's private structure * @pcidev: Pointer to the PCI device for this port * @port_num: port offset * * CTI XR17V35X based cards have the port types stored in the EEPROM. * This function reads the port type for a single port. * * Return: port type on success, CTI_PORT_TYPE_NONE on failure
*/ staticenum cti_port_type cti_get_port_type_xr17v35x(struct exar8250 *priv, struct pci_dev *pcidev, unsignedint port_num)
{ enum cti_port_type port_type;
u16 port_flags;
u8 offset;
port_type = FIELD_GET(CTI_EE_MASK_PORT_FLAGS_TYPE, port_flags); if (CTI_PORT_TYPE_VALID(port_type)) return port_type;
/* * If the port type is missing the card assume it is a * RS232/RS422/RS485 card to be safe. * * There is one known board (BEG013) that only has 3 of 4 port types * written to the EEPROM so this acts as a work around.
*/
dev_warn(&pcidev->dev, "failed to get port %d type from EEPROM\n", port_num);
// FPGA OSC is fixed to the 33MHz PCI clock
priv->osc_freq = CTI_DEFAULT_FPGA_OSC_FREQ;
// Enable external interrupts in special cfg space register
ret = pci_read_config_word(pcidev, CTI_FPGA_CFG_INT_EN_REG, &cfg_val); if (ret) return pcibios_err_to_errno(ret);
cfg_val |= CTI_FPGA_CFG_INT_EN_EXT_BIT;
ret = pci_write_config_word(pcidev, CTI_FPGA_CFG_INT_EN_REG, cfg_val); if (ret) return pcibios_err_to_errno(ret);
// RS485 gate needs to be enabled; otherwise RTS/CTS will not work
exar_write_reg(priv, CTI_FPGA_RS485_IO_REG, 0x01);
return 0;
}
staticint cti_port_setup_fpga(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, int idx)
{ enum cti_port_type port_type; unsignedint offset; int ret;
if (idx == 0) {
ret = cti_board_init_fpga(priv, pcidev); if (ret) return ret;
}
/* enable interrupts on cards that need the "PLX fix" */ switch (pcidev->subsystem_device) { case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B:
cti_plx_int_enable(priv); break; default: break;
}
}
staticint cti_port_setup_xr17v25x(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, int idx)
{ enum cti_port_type port_type; unsignedint offset; int ret;
if (idx == 0)
cti_board_init_xr17v25x(priv, pcidev);
if (CTI_PORT_TYPE_RS485(port_type)) { switch (pcidev->subsystem_device) { // These cards support power on 485 tri-state via MPIO case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485:
port->port.rs485_config = cti_rs485_config_mpio_tristate; break; // Otherwise auto or no power on 485 tri-state support default:
port->port.rs485_config = generic_rs485_config; break;
}
/* enable interrupts on cards that need the "PLX fix" */ switch (pcidev->subsystem_device) { case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B:
cti_plx_int_enable(priv); break; default: break;
}
}
if (CTI_PORT_TYPE_RS485(port_type)) { switch (pcidev->subsystem_device) { // These cards support power on 485 tri-state via MPIO case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485:
port->port.rs485_config = cti_rs485_config_mpio_tristate; break; // Otherwise auto or no power on 485 tri-state support default:
port->port.rs485_config = generic_rs485_config; break;
}
staticvoid setup_gpio(struct pci_dev *pcidev, u8 __iomem *p)
{ /* * The Commtech adapters required the MPIOs to be driven low. The Exar * devices will export them as GPIOs, so we pre-configure them safely * as inputs.
*/
u8 dir = 0x00;
if ((pcidev->vendor == PCI_VENDOR_ID_EXAR) &&
(pcidev->subsystem_vendor != PCI_VENDOR_ID_SEALEVEL)) { // Configure GPIO as inputs for Commtech adapters
dir = 0xff;
} else { // Configure GPIO as outputs for SeaLevel adapters
dir = 0x00;
}
writeb(0x00, p + UART_EXAR_MPIOINT_7_0);
writeb(0x00, p + UART_EXAR_MPIOLVL_7_0);
writeb(0x00, p + UART_EXAR_MPIO3T_7_0);
writeb(0x00, p + UART_EXAR_MPIOINV_7_0);
writeb(dir, p + UART_EXAR_MPIOSEL_7_0);
writeb(0x00, p + UART_EXAR_MPIOOD_7_0);
writeb(0x00, p + UART_EXAR_MPIOINT_15_8);
writeb(0x00, p + UART_EXAR_MPIOLVL_15_8);
writeb(0x00, p + UART_EXAR_MPIO3T_15_8);
writeb(0x00, p + UART_EXAR_MPIOINV_15_8);
writeb(dir, p + UART_EXAR_MPIOSEL_15_8);
writeb(0x00, p + UART_EXAR_MPIOOD_15_8);
}
writeb(IOT2040_UARTS_DEFAULT_MODE, p + UART_EXAR_MPIOLVL_7_0);
writeb(IOT2040_UARTS_GPIO_LO_MODE, p + UART_EXAR_MPIOSEL_7_0);
writeb(IOT2040_UARTS_ENABLE, p + UART_EXAR_MPIOLVL_15_8);
writeb(IOT2040_UARTS_GPIO_HI_MODE, p + UART_EXAR_MPIOSEL_15_8);
/* * For SIMATIC IOT2000, only IOT2040 and its variants have the Exar device, * IOT2020 doesn't have. Therefore it is sufficient to match on the common * board name after the device was found.
*/ staticconststruct dmi_system_id exar_platforms[] = {
{
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"),
},
.driver_data = (void *)&iot2040_platform,
},
{}
};
if (pcidev->subsystem_vendor == PCI_VENDOR_ID_SEALEVEL)
port->port.rs485_config = sealevel_rs485_config;
/* * Setup the UART clock for the devices on expansion slot to * half the clock speed of the main chip (which is 125MHz)
*/ if (idx >= 8)
port->port.uartclk /= 2;
ret = default_setup(priv, pcidev, idx, offset, port); if (ret) return ret;
p = port->port.membase;
writeb(0x00, p + UART_EXAR_8XMODE);
writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR);
writeb(128, p + UART_EXAR_TXTRG);
writeb(128, p + UART_EXAR_RXTRG);
staticinlinevoid exar_misc_clear(struct exar8250 *priv)
{ /* Clear all PCI interrupts by reading INT0. No effect on IIR */
readb(priv->virt + UART_EXAR_INT0);
/* Clear INT0 for Expansion Interface slave ports, too */ if (priv->board->num_ports > 8)
readb(priv->virt + 0x2000 + UART_EXAR_INT0);
}
/* * These Exar UARTs have an extra interrupt indicator that could fire for a * few interrupts that are not presented/cleared through IIR. One of which is * a wakeup interrupt when coming out of sleep. These interrupts are only * cleared by reading global INT0 or INT1 registers as interrupts are * associated with channel 0. The INT[3:0] registers _are_ accessible from each * channel's address space, but for the sake of bus efficiency we register a * dedicated handler at the PCI device level to handle them.
*/ static irqreturn_t exar_misc_handler(int irq, void *data)
{
exar_misc_clear(data);
for (i = 0; i < nr_ports && i < maxnr; i++) {
rc = board->setup(priv, pcidev, &uart, i); if (rc) {
dev_err_probe(&pcidev->dev, rc, "Failed to setup port %u\n", i); break;
}
dev_dbg(&pcidev->dev, "Setup PCI port: port %lx, irq %d, type %d\n",
uart.port.iobase, uart.port.irq, uart.port.iotype);
priv->line[i] = serial8250_register_8250_port(&uart); if (priv->line[i] < 0) {
dev_err_probe(&pcidev->dev, priv->line[i], "Couldn't register serial port %lx, type %d, irq %d\n",
uart.port.iobase, uart.port.iotype, uart.port.irq); break;
}
}
priv->nr = i;
pci_set_drvdata(pcidev, priv); return 0;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.