* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
- * Copyright (C) 2003-2005 Silicon Graphics, Inc. All Rights Reserved.
+ * Copyright (C) 2003-2006 Silicon Graphics, Inc. All Rights Reserved.
*/
#include <linux/serial_reg.h>
#include <linux/module.h>
#include <linux/pci.h>
-#include <linux/ioc4_common.h>
+#include <linux/ioc4.h>
#include <linux/serial_core.h>
/*
IOC4_SIO_IR_S3_TX_EXPLICIT)
/* Bitmasks for IOC4_OTHER_IR, IOC4_OTHER_IEC, and IOC4_OTHER_IES */
-#define IOC4_OTHER_IR_ATA_INT 0x00000001 /* ATAPI intr pass-thru */
-#define IOC4_OTHER_IR_ATA_MEMERR 0x00000002 /* ATAPI DMA PCI error */
-#define IOC4_OTHER_IR_S0_MEMERR 0x00000004 /* Port 0 PCI error */
-#define IOC4_OTHER_IR_S1_MEMERR 0x00000008 /* Port 1 PCI error */
-#define IOC4_OTHER_IR_S2_MEMERR 0x00000010 /* Port 2 PCI error */
-#define IOC4_OTHER_IR_S3_MEMERR 0x00000020 /* Port 3 PCI error */
+#define IOC4_OTHER_IR_ATA_INT 0x00000001 /* ATAPI intr pass-thru */
+#define IOC4_OTHER_IR_ATA_MEMERR 0x00000002 /* ATAPI DMA PCI error */
+#define IOC4_OTHER_IR_S0_MEMERR 0x00000004 /* Port 0 PCI error */
+#define IOC4_OTHER_IR_S1_MEMERR 0x00000008 /* Port 1 PCI error */
+#define IOC4_OTHER_IR_S2_MEMERR 0x00000010 /* Port 2 PCI error */
+#define IOC4_OTHER_IR_S3_MEMERR 0x00000020 /* Port 3 PCI error */
+#define IOC4_OTHER_IR_KBD_INT 0x00000040 /* Keyboard/mouse */
+#define IOC4_OTHER_IR_RESERVED 0x007fff80 /* Reserved */
+#define IOC4_OTHER_IR_RT_INT 0x00800000 /* INT_OUT section output */
+#define IOC4_OTHER_IR_GEN_INT 0xff000000 /* Generic pins */
+
+#define IOC4_OTHER_IR_SER_MEMERR (IOC4_OTHER_IR_S0_MEMERR | IOC4_OTHER_IR_S1_MEMERR | \
+ IOC4_OTHER_IR_S2_MEMERR | IOC4_OTHER_IR_S3_MEMERR)
/* Bitmasks for IOC4_SIO_CR */
#define IOC4_SIO_CR_CMD_PULSE_SHIFT 0 /* byte bus strobe shift */
#define i4u_dlm u2.dlm
#define i4u_fcr u3.fcr
-/* PCI memory space register map addressed using pci_bar0 */
-struct ioc4_memregs {
- struct ioc4_mem {
- /* Miscellaneous IOC4 registers */
- uint32_t pci_err_addr_l;
- uint32_t pci_err_addr_h;
- uint32_t sio_ir;
- uint32_t other_ir;
-
- /* These registers are read-only for general kernel code. */
- uint32_t sio_ies_ro;
- uint32_t other_ies_ro;
- uint32_t sio_iec_ro;
- uint32_t other_iec_ro;
- uint32_t sio_cr;
- uint32_t misc_fill1;
- uint32_t int_out;
- uint32_t misc_fill2;
- uint32_t gpcr_s;
- uint32_t gpcr_c;
- uint32_t gpdr;
- uint32_t misc_fill3;
- uint32_t gppr_0;
- uint32_t gppr_1;
- uint32_t gppr_2;
- uint32_t gppr_3;
- uint32_t gppr_4;
- uint32_t gppr_5;
- uint32_t gppr_6;
- uint32_t gppr_7;
- } ioc4_mem;
-
- char misc_fill4[0x100 - 0x5C - 4];
-
- /* ATA/ATAP registers */
- uint32_t ata_notused[9];
- char ata_fill1[0x140 - 0x120 - 4];
- uint32_t ata_notused1[8];
- char ata_fill2[0x200 - 0x15C - 4];
-
- /* Keyboard and mouse registers */
- uint32_t km_notused[5];;
- char km_fill1[0x300 - 0x210 - 4];
-
- /* Serial port registers used for DMA serial I/O */
- struct ioc4_serial {
- uint32_t sbbr01_l;
- uint32_t sbbr01_h;
- uint32_t sbbr23_l;
- uint32_t sbbr23_h;
-
- struct ioc4_serialregs port_0;
- struct ioc4_serialregs port_1;
- struct ioc4_serialregs port_2;
- struct ioc4_serialregs port_3;
- struct ioc4_uartregs uart_0;
- struct ioc4_uartregs uart_1;
- struct ioc4_uartregs uart_2;
- struct ioc4_uartregs uart_3;
- } ioc4_serial;
-};
+/* Serial port registers used for DMA serial I/O */
+struct ioc4_serial {
+ uint32_t sbbr01_l;
+ uint32_t sbbr01_h;
+ uint32_t sbbr23_l;
+ uint32_t sbbr23_h;
+
+ struct ioc4_serialregs port_0;
+ struct ioc4_serialregs port_1;
+ struct ioc4_serialregs port_2;
+ struct ioc4_serialregs port_3;
+ struct ioc4_uartregs uart_0;
+ struct ioc4_uartregs uart_1;
+ struct ioc4_uartregs uart_2;
+ struct ioc4_uartregs uart_3;
+} ioc4_serial;
/* UART clock speed */
-#define IOC4_SER_XIN_CLK IOC4_SER_XIN_CLK_66
#define IOC4_SER_XIN_CLK_66 66666667
#define IOC4_SER_XIN_CLK_33 33333333
typedef void ioc4_intr_func_f(void *, uint32_t);
typedef ioc4_intr_func_f *ioc4_intr_func_t;
+static unsigned int Num_of_ioc4_cards;
+
/* defining this will get you LOTS of great debug info */
//#define DEBUG_INTERRUPTS
#define DPRINT_CONFIG(_x...) ;
#define WAKEUP_CHARS 256
/* number of characters we want to transmit to the lower level at a time */
-#define IOC4_MAX_CHARS 128
+#define IOC4_MAX_CHARS 256
+#define IOC4_FIFO_CHARS 255
/* Device name we're using */
-#define DEVICE_NAME "ttyIOC"
-#define DEVICE_MAJOR 204
-#define DEVICE_MINOR 50
+#define DEVICE_NAME_RS232 "ttyIOC"
+#define DEVICE_NAME_RS422 "ttyAIOC"
+#define DEVICE_MAJOR 204
+#define DEVICE_MINOR_RS232 50
+#define DEVICE_MINOR_RS422 84
+
/* register offsets */
#define IOC4_SERIAL_OFFSET 0x300
#define MAX_BAUD_SUPPORTED 115200
/* protocol types supported */
-enum sio_proto {
- PROTO_RS232,
- PROTO_RS422
-};
+#define PROTO_RS232 3
+#define PROTO_RS422 7
/* Notification types */
#define N_DATA_READY 0x01
| UART_LCR_WLEN7 | UART_LCR_WLEN8)
#define LCR_MASK_STOP_BITS (UART_LCR_STOP)
-#define PENDING(_p) (readl(&(_p)->ip_mem->sio_ir) & _p->ip_ienb)
-#define READ_SIO_IR(_p) readl(&(_p)->ip_mem->sio_ir)
+#define PENDING(_p) (readl(&(_p)->ip_mem->sio_ir.raw) & _p->ip_ienb)
+#define READ_SIO_IR(_p) readl(&(_p)->ip_mem->sio_ir.raw)
/* Default to 4k buffers */
#ifdef IOC4_1K_BUFFERS
/*
* This is the entry saved by the driver - one per card
*/
+
+#define UART_PORT_MIN 0
+#define UART_PORT_RS232 UART_PORT_MIN
+#define UART_PORT_RS422 1
+#define UART_PORT_COUNT 2 /* one for each mode */
+
struct ioc4_control {
int ic_irq;
struct {
- /* uart ports are allocated here */
- struct uart_port icp_uart_port;
+ /* uart ports are allocated here - 1 for rs232, 1 for rs422 */
+ struct uart_port icp_uart_port[UART_PORT_COUNT];
/* Handy reference material */
struct ioc4_port *icp_port;
} ic_port[IOC4_NUM_SERIAL_PORTS];
*/
#define MAX_IOC4_INTR_ENTS (8 * sizeof(uint32_t))
struct ioc4_soft {
- struct ioc4_mem __iomem *is_ioc4_mem_addr;
+ struct ioc4_misc_regs __iomem *is_ioc4_misc_addr;
struct ioc4_serial __iomem *is_ioc4_serial_addr;
/* Each interrupt type has an entry in the array */
/* Local port info for each IOC4 serial ports */
struct ioc4_port {
- struct uart_port *ip_port;
+ struct uart_port *ip_port; /* current active port ptr */
+ /* Ptrs for all ports */
+ struct uart_port *ip_all_ports[UART_PORT_COUNT];
/* Back ptrs for this port */
struct ioc4_control *ip_control;
struct pci_dev *ip_pdev;
struct ioc4_soft *ip_ioc4_soft;
/* pci mem addresses */
- struct ioc4_mem __iomem *ip_mem;
+ struct ioc4_misc_regs __iomem *ip_mem;
struct ioc4_serial __iomem *ip_serial;
struct ioc4_serialregs __iomem *ip_serial_regs;
struct ioc4_uartregs __iomem *ip_uart_regs;
#define DCD_ON 0x02
#define LOWAT_WRITTEN 0x04
#define READ_ABORTED 0x08
+#define PORT_ACTIVE 0x10
+#define PORT_INACTIVE 0 /* This is the value when "off" */
+
/* Since each port has different register offsets and bitmasks
* for everything, we'll store those that we need in tables so we
uint32_t intr_dma_error;
uint32_t intr_clear;
uint32_t intr_all;
- char rs422_select_pin;
+ int rs422_select_pin;
};
static struct hooks hooks_array[IOC4_NUM_SERIAL_PORTS] = {
static void receive_chars(struct uart_port *);
static void handle_intr(void *arg, uint32_t sio_ir);
+/*
+ * port_is_active - determines if this port is currently active
+ * @port: ptr to soft struct for this port
+ * @uart_port: uart port to test for
+ */
+static inline int port_is_active(struct ioc4_port *port,
+ struct uart_port *uart_port)
+{
+ if (port) {
+ if ((port->ip_flags & PORT_ACTIVE)
+ && (port->ip_port == uart_port))
+ return 1;
+ }
+ return 0;
+}
+
+
/**
* write_ireg - write the interrupt regs
* @ioc4_soft: ptr to soft struct for this port
static inline void
write_ireg(struct ioc4_soft *ioc4_soft, uint32_t val, int which, int type)
{
- struct ioc4_mem __iomem *mem = ioc4_soft->is_ioc4_mem_addr;
+ struct ioc4_misc_regs __iomem *mem = ioc4_soft->is_ioc4_misc_addr;
unsigned long flags;
spin_lock_irqsave(&ioc4_soft->is_ir_lock, flags);
case IOC4_SIO_INTR_TYPE:
switch (which) {
case IOC4_W_IES:
- writel(val, &mem->sio_ies_ro);
+ writel(val, &mem->sio_ies.raw);
break;
case IOC4_W_IEC:
- writel(val, &mem->sio_iec_ro);
+ writel(val, &mem->sio_iec.raw);
break;
}
break;
case IOC4_OTHER_INTR_TYPE:
switch (which) {
case IOC4_W_IES:
- writel(val, &mem->other_ies_ro);
+ writel(val, &mem->other_ies.raw);
break;
case IOC4_W_IEC:
- writel(val, &mem->other_iec_ro);
+ writel(val, &mem->other_iec.raw);
break;
}
break;
/**
* get_ioc4_port - given a uart port, return the control structure
* @port: uart port
+ * @set: set this port as current
*/
-static struct ioc4_port *get_ioc4_port(struct uart_port *the_port)
+static struct ioc4_port *get_ioc4_port(struct uart_port *the_port, int set)
{
- struct ioc4_control *control = dev_get_drvdata(the_port->dev);
- int ii;
+ struct ioc4_driver_data *idd = dev_get_drvdata(the_port->dev);
+ struct ioc4_control *control = idd->idd_serial_data;
+ struct ioc4_port *port;
+ int port_num, port_type;
if (control) {
- for ( ii = 0; ii < IOC4_NUM_SERIAL_PORTS; ii++ ) {
- if (!control->ic_port[ii].icp_port)
+ for ( port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS;
+ port_num++ ) {
+ port = control->ic_port[port_num].icp_port;
+ if (!port)
continue;
- if (the_port == control->ic_port[ii].icp_port->ip_port)
- return control->ic_port[ii].icp_port;
+ for (port_type = UART_PORT_MIN;
+ port_type < UART_PORT_COUNT;
+ port_type++) {
+ if (the_port == port->ip_all_ports
+ [port_type]) {
+ /* set local copy */
+ if (set) {
+ port->ip_port = the_port;
+ }
+ return port;
+ }
+ }
}
}
return NULL;
static inline uint32_t
pending_intrs(struct ioc4_soft *soft, int type)
{
- struct ioc4_mem __iomem *mem = soft->is_ioc4_mem_addr;
+ struct ioc4_misc_regs __iomem *mem = soft->is_ioc4_misc_addr;
unsigned long flag;
uint32_t intrs = 0;
switch (type) {
case IOC4_SIO_INTR_TYPE:
- intrs = readl(&mem->sio_ir) & readl(&mem->sio_ies_ro);
+ intrs = readl(&mem->sio_ir.raw) & readl(&mem->sio_ies.raw);
break;
case IOC4_OTHER_INTR_TYPE:
- intrs = readl(&mem->other_ir) & readl(&mem->other_ies_ro);
+ intrs = readl(&mem->other_ir.raw) & readl(&mem->other_ies.raw);
/* Don't process any ATA interrupte */
intrs &= ~(IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR);
/* Wait until any pending bus activity for this port has ceased */
do
- sio_cr = readl(&port->ip_mem->sio_cr);
+ sio_cr = readl(&port->ip_mem->sio_cr.raw);
while (!(sio_cr & IOC4_SIO_CR_SIO_DIAG_IDLE));
/* Finish reset sequence */
port->ip_tx_prod = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK;
writel(port->ip_tx_prod, &port->ip_serial_regs->stpir);
port->ip_rx_cons = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK;
- writel(port->ip_rx_cons, &port->ip_serial_regs->srcir);
+ writel(port->ip_rx_cons | IOC4_SRCIR_ARM, &port->ip_serial_regs->srcir);
/* Disable interrupts for this 16550 */
uart = port->ip_uart_regs;
ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf;
DPRINT_CONFIG(("%s: ring_pci_addr 0x%lx\n",
- __FUNCTION__, ring_pci_addr));
+ __func__, ring_pci_addr));
writel((unsigned int)((uint64_t)ring_pci_addr >> 32), sbbr_h);
writel((unsigned int)ring_pci_addr | IOC4_BUF_SIZE_BIT, sbbr_l);
write_ireg(port->ip_ioc4_soft, hooks->intr_clear,
IOC4_W_IEC, IOC4_SIO_INTR_TYPE);
port->ip_ienb &= ~hooks->intr_clear;
- writel(hooks->intr_clear, &port->ip_mem->sio_ir);
+ writel(hooks->intr_clear, &port->ip_mem->sio_ir.raw);
return 0;
}
{
struct ioc4_port *port = (struct ioc4_port *)arg;
struct hooks *hooks = port->ip_hooks;
- unsigned int flags;
+ unsigned long flags;
spin_lock_irqsave(&port->ip_lock, flags);
/* ACK the interrupt */
- writel(hooks->intr_dma_error, &port->ip_mem->other_ir);
+ writel(hooks->intr_dma_error, &port->ip_mem->other_ir.raw);
- if (readl(&port->ip_mem->pci_err_addr_l) & IOC4_PCI_ERR_ADDR_VLD) {
+ if (readl(&port->ip_mem->pci_err_addr_l.raw) & IOC4_PCI_ERR_ADDR_VLD) {
printk(KERN_ERR
- "PCI error address is 0x%lx, "
+ "PCI error address is 0x%llx, "
"master is serial port %c %s\n",
(((uint64_t)readl(&port->ip_mem->pci_err_addr_h)
<< 32)
- | readl(&port->ip_mem->pci_err_addr_l))
+ | readl(&port->ip_mem->pci_err_addr_l.raw))
& IOC4_PCI_ERR_ADDR_ADDR_MSK, '1' +
- ((char)(readl(&port->ip_mem-> pci_err_addr_l) &
+ ((char)(readl(&port->ip_mem->pci_err_addr_l.raw) &
IOC4_PCI_ERR_ADDR_MST_NUM_MSK) >> 1),
- (readl(&port->ip_mem->pci_err_addr_l)
+ (readl(&port->ip_mem->pci_err_addr_l.raw)
& IOC4_PCI_ERR_ADDR_MST_TYP_MSK)
? "RX" : "TX");
- if (readl(&port->ip_mem->pci_err_addr_l)
+ if (readl(&port->ip_mem->pci_err_addr_l.raw)
& IOC4_PCI_ERR_ADDR_MUL_ERR) {
printk(KERN_ERR
"Multiple errors occurred\n");
* ioc4_intr - Top level IOC4 interrupt handler.
* @irq: irq value
* @arg: handler arg
- * @regs: registers
*/
-static irqreturn_t ioc4_intr(int irq, void *arg, struct pt_regs *regs)
+
+static irqreturn_t ioc4_intr(int irq, void *arg)
{
struct ioc4_soft *soft;
uint32_t this_ir, this_mir;
int xx, num_intrs = 0;
int intr_type;
int handled = 0;
- struct ioc4_intr_info *ii;
+ struct ioc4_intr_info *intr_info;
soft = arg;
for (intr_type = 0; intr_type < IOC4_NUM_INTR_TYPES; intr_type++) {
* which interrupt bits are set.
*/
for (xx = 0; xx < num_intrs; xx++) {
- ii = &soft->is_intr_type[intr_type].is_intr_info[xx];
- if ((this_mir = this_ir & ii->sd_bits)) {
+ intr_info = &soft->is_intr_type[intr_type].is_intr_info[xx];
+ if ((this_mir = this_ir & intr_info->sd_bits)) {
/* Disable owned interrupts, call handler */
handled++;
- write_ireg(soft, ii->sd_bits, IOC4_W_IEC,
+ write_ireg(soft, intr_info->sd_bits, IOC4_W_IEC,
intr_type);
- ii->sd_intr(ii->sd_info, this_mir);
+ intr_info->sd_intr(intr_info->sd_info, this_mir);
this_ir &= ~this_mir;
}
}
- if (this_ir) {
- printk(KERN_ERR
- "unknown IOC4 %s interrupt 0x%x, sio_ir = 0x%x,"
- " sio_ies = 0x%x, other_ir = 0x%x :"
- "other_ies = 0x%x\n",
- (intr_type == IOC4_SIO_INTR_TYPE) ? "sio" :
- "other", this_ir,
- readl(&soft->is_ioc4_mem_addr->sio_ir),
- readl(&soft->is_ioc4_mem_addr->sio_ies_ro),
- readl(&soft->is_ioc4_mem_addr->other_ir),
- readl(&soft->is_ioc4_mem_addr->other_ies_ro));
- }
}
#ifdef DEBUG_INTERRUPTS
{
- struct ioc4_mem __iomem *mem = soft->is_ioc4_mem_addr;
- spinlock_t *lp = &soft->is_ir_lock;
+ struct ioc4_misc_regs __iomem *mem = soft->is_ioc4_misc_addr;
unsigned long flag;
spin_lock_irqsave(&soft->is_ir_lock, flag);
- printk ("%s : %d : mem 0x%p sio_ir 0x%x sio_ies_ro 0x%x "
- "other_ir 0x%x other_ies_ro 0x%x mask 0x%x\n",
- __FUNCTION__, __LINE__,
- (void *)mem, readl(&mem->sio_ir),
- readl(&mem->sio_ies_ro),
- readl(&mem->other_ir),
- readl(&mem->other_ies_ro),
+ printk ("%s : %d : mem 0x%p sio_ir 0x%x sio_ies 0x%x "
+ "other_ir 0x%x other_ies 0x%x mask 0x%x\n",
+ __func__, __LINE__,
+ (void *)mem, readl(&mem->sio_ir.raw),
+ readl(&mem->sio_ies.raw),
+ readl(&mem->other_ir.raw),
+ readl(&mem->other_ies.raw),
IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR);
spin_unlock_irqrestore(&soft->is_ir_lock, flag);
}
* ioc4_attach_local - Device initialization.
* Called at *_attach() time for each
* IOC4 with serial ports in the system.
- * @control: ioc4_control ptr
- * @pdev: PCI handle for this device
- * @soft: soft struct for this device
- * @ioc4: ioc4 mem space
+ * @idd: Master module data for this IOC4
*/
-static int inline ioc4_attach_local(struct pci_dev *pdev,
- struct ioc4_control *control,
- struct ioc4_soft *soft, void __iomem *ioc4_mem,
- void __iomem *ioc4_serial)
+static int inline ioc4_attach_local(struct ioc4_driver_data *idd)
{
struct ioc4_port *port;
struct ioc4_port *ports[IOC4_NUM_SERIAL_PORTS];
int port_number;
uint16_t ioc4_revid_min = 62;
uint16_t ioc4_revid;
+ struct pci_dev *pdev = idd->idd_pdev;
+ struct ioc4_control* control = idd->idd_serial_data;
+ struct ioc4_soft *soft = control->ic_soft;
+ void __iomem *ioc4_misc = idd->idd_misc_regs;
+ void __iomem *ioc4_serial = soft->is_ioc4_serial_addr;
/* IOC4 firmware must be at least rev 62 */
pci_read_config_word(pdev, PCI_COMMAND_SPECIAL, &ioc4_revid);
ioc4_revid, ioc4_revid_min);
return -EPERM;
}
- BUG_ON(ioc4_mem == NULL);
+ BUG_ON(ioc4_misc == NULL);
BUG_ON(ioc4_serial == NULL);
/* Create port structures for each port */
for (port_number = 0; port_number < IOC4_NUM_SERIAL_PORTS;
port_number++) {
- port = kmalloc(sizeof(struct ioc4_port), GFP_KERNEL);
+ port = kzalloc(sizeof(struct ioc4_port), GFP_KERNEL);
if (!port) {
printk(KERN_WARNING
"IOC4 serial memory not available for port\n");
return -ENOMEM;
}
- memset(port, 0, sizeof(struct ioc4_port));
+ spin_lock_init(&port->ip_lock);
/* we need to remember the previous ones, to point back to
* them farther down - setting up the ring buffers.
port->ip_ioc4_soft = soft;
port->ip_pdev = pdev;
port->ip_ienb = 0;
- port->ip_pci_bus_speed = IOC4_SER_XIN_CLK;
+ /* Use baud rate calculations based on detected PCI
+ * bus speed. Simply test whether the PCI clock is
+ * running closer to 66MHz or 33MHz.
+ */
+ if (idd->count_period/IOC4_EXTINT_COUNT_DIVISOR < 20) {
+ port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_66;
+ } else {
+ port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_33;
+ }
port->ip_baud = 9600;
port->ip_control = control;
- port->ip_mem = ioc4_mem;
+ port->ip_mem = ioc4_misc;
port->ip_serial = ioc4_serial;
/* point to the right hook */
(TOTAL_RING_BUF_SIZE - 1)) == 0));
DPRINT_CONFIG(("%s : ip_cpu_ringbuf 0x%p "
"ip_dma_ringbuf 0x%p\n",
- __FUNCTION__,
+ __func__,
(void *)port->ip_cpu_ringbuf,
(void *)port->ip_dma_ringbuf));
port->ip_inring = RING(port, RX_0_OR_2);
port->ip_outring = RING(port, TX_0_OR_2);
}
DPRINT_CONFIG(("%s : port %d [addr 0x%p] control 0x%p",
- __FUNCTION__,
+ __func__,
port_number, (void *)port, (void *)control));
DPRINT_CONFIG((" ip_serial_regs 0x%p ip_uart_regs 0x%p\n",
(void *)port->ip_serial_regs,
DPRINT_CONFIG(("%s: port_number %d port 0x%p inring 0x%p "
"outring 0x%p\n",
- __FUNCTION__,
+ __func__,
port_number, (void *)port,
(void *)port->ip_inring,
(void *)port->ip_outring));
{
int spiniter = 0;
- port->ip_flags = 0;
+ port->ip_flags = PORT_ACTIVE;
/* Pause the DMA interface if necessary */
if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
& IOC4_SSCR_PAUSE_STATE) == 0) {
spiniter++;
if (spiniter > MAXITER) {
+ port->ip_flags = PORT_INACTIVE;
return -1;
}
}
int spiniter = 0;
DPRINT_CONFIG(("%s: baud %d byte_size %d stop %d parenb %d parodd %d\n",
- __FUNCTION__, baud, byte_size, stop_bits, parenb, parodd));
+ __func__, baud, byte_size, stop_bits, parenb, parodd));
if (set_baud(port, baud))
return 1;
/**
* set_mcr - set the master control reg
* @the_port: port to use
- * @set: set ?
* @mask1: mcr mask
* @mask2: shadow mask
*/
-static inline int set_mcr(struct uart_port *the_port, int set,
+static inline int set_mcr(struct uart_port *the_port,
int mask1, int mask2)
{
- struct ioc4_port *port = get_ioc4_port(the_port);
+ struct ioc4_port *port = get_ioc4_port(the_port, 0);
uint32_t shadow;
int spiniter = 0;
char mcr;
mcr = (shadow & 0xff000000) >> 24;
/* Set new value */
- if (set) {
- mcr |= mask1;
- shadow |= mask2;
- } else {
- mcr &= ~mask1;
- shadow &= ~mask2;
- }
+ mcr |= mask1;
+ shadow |= mask2;
+
writeb(mcr, &port->ip_uart_regs->i4u_mcr);
writel(shadow, &port->ip_serial_regs->shadow);
* @port: port to use
* @proto: protocol to use
*/
-static int ioc4_set_proto(struct ioc4_port *port, enum sio_proto proto)
+static int ioc4_set_proto(struct ioc4_port *port, int proto)
{
struct hooks *hooks = port->ip_hooks;
switch (proto) {
case PROTO_RS232:
/* Clear the appropriate GIO pin */
- writel(0, (&port->ip_mem->gppr_0 +
- hooks->rs422_select_pin));
+ writel(0, (&port->ip_mem->gppr[hooks->rs422_select_pin].raw));
break;
case PROTO_RS422:
/* Set the appropriate GIO pin */
- writel(1, (&port->ip_mem->gppr_0 +
- hooks->rs422_select_pin));
+ writel(1, (&port->ip_mem->gppr[hooks->rs422_select_pin].raw));
break;
default:
int result;
char *start;
struct tty_struct *tty;
- struct ioc4_port *port = get_ioc4_port(the_port);
- struct uart_info *info;
+ struct ioc4_port *port = get_ioc4_port(the_port, 0);
+ struct uart_state *state;
if (!the_port)
return;
if (!port)
return;
- info = the_port->info;
- tty = info->tty;
+ state = the_port->state;
+ tty = state->port.tty;
- if (uart_circ_empty(&info->xmit) || uart_tx_stopped(the_port)) {
+ if (uart_circ_empty(&state->xmit) || uart_tx_stopped(the_port)) {
/* Nothing to do or hw stopped */
set_notification(port, N_ALL_OUTPUT, 0);
return;
}
- head = info->xmit.head;
- tail = info->xmit.tail;
- start = (char *)&info->xmit.buf[tail];
+ head = state->xmit.head;
+ tail = state->xmit.tail;
+ start = (char *)&state->xmit.buf[tail];
/* write out all the data or until the end of the buffer */
xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail);
/* advance the pointers */
tail += result;
tail &= UART_XMIT_SIZE - 1;
- info->xmit.tail = tail;
- start = (char *)&info->xmit.buf[tail];
+ state->xmit.tail = tail;
+ start = (char *)&state->xmit.buf[tail];
}
}
- if (uart_circ_chars_pending(&info->xmit) < WAKEUP_CHARS)
+ if (uart_circ_chars_pending(&state->xmit) < WAKEUP_CHARS)
uart_write_wakeup(the_port);
- if (uart_circ_empty(&info->xmit)) {
+ if (uart_circ_empty(&state->xmit)) {
set_notification(port, N_OUTPUT_LOWAT, 0);
} else {
set_notification(port, N_OUTPUT_LOWAT, 1);
*/
static void
ioc4_change_speed(struct uart_port *the_port,
- struct termios *new_termios, struct termios *old_termios)
+ struct ktermios *new_termios, struct ktermios *old_termios)
{
- struct ioc4_port *port = get_ioc4_port(the_port);
+ struct ioc4_port *port = get_ioc4_port(the_port, 0);
int baud, bits;
- unsigned cflag, cval;
- int new_parity = 0, new_parity_enable = 0, new_stop = 1, new_data = 8;
- struct uart_info *info = the_port->info;
+ unsigned cflag;
+ int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8;
+ struct uart_state *state = the_port->state;
cflag = new_termios->c_cflag;
switch (cflag & CSIZE) {
case CS5:
new_data = 5;
- cval = 0x00;
bits = 7;
break;
case CS6:
new_data = 6;
- cval = 0x01;
bits = 8;
break;
case CS7:
new_data = 7;
- cval = 0x02;
bits = 9;
break;
case CS8:
new_data = 8;
- cval = 0x03;
bits = 10;
break;
default:
/* cuz we always need a default ... */
new_data = 5;
- cval = 0x00;
bits = 7;
break;
}
if (cflag & CSTOPB) {
- cval |= 0x04;
bits++;
new_stop = 1;
}
if (cflag & PARENB) {
- cval |= UART_LCR_PARITY;
bits++;
new_parity_enable = 1;
- }
- if (cflag & PARODD) {
- cval |= UART_LCR_EPAR;
- new_parity = 1;
- }
- if (cflag & IGNPAR) {
- cval &= ~UART_LCR_PARITY;
- new_parity_enable = 0;
+ if (cflag & PARODD)
+ new_parity = 1;
}
baud = uart_get_baud_rate(the_port, new_termios, old_termios,
MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED);
- DPRINT_CONFIG(("%s: returned baud %d\n", __FUNCTION__, baud));
+ DPRINT_CONFIG(("%s: returned baud %d\n", __func__, baud));
/* default is 9600 */
if (!baud)
baud = 9600;
if (!the_port->fifosize)
- the_port->fifosize = IOC4_MAX_CHARS;
+ the_port->fifosize = IOC4_FIFO_CHARS;
the_port->timeout = ((the_port->fifosize * HZ * bits) / (baud / 10));
the_port->timeout += HZ / 50; /* Add .02 seconds of slop */
the_port->ignore_status_mask = N_ALL_INPUT;
- if (I_IGNPAR(info->tty))
+ state->port.tty->low_latency = 1;
+
+ if (I_IGNPAR(state->port.tty))
the_port->ignore_status_mask &= ~(N_PARITY_ERROR
| N_FRAMING_ERROR);
- if (I_IGNBRK(info->tty)) {
+ if (I_IGNBRK(state->port.tty)) {
the_port->ignore_status_mask &= ~N_BREAK;
- if (I_IGNPAR(info->tty))
+ if (I_IGNPAR(state->port.tty))
the_port->ignore_status_mask &= ~N_OVERRUN_ERROR;
}
if (!(cflag & CREAD)) {
}
if (cflag & CRTSCTS) {
- info->flags |= ASYNC_CTS_FLOW;
port->ip_sscr |= IOC4_SSCR_HFC_EN;
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
}
- else
- info->flags &= ~ASYNC_CTS_FLOW;
+ else {
+ port->ip_sscr &= ~IOC4_SSCR_HFC_EN;
+ }
+ writel(port->ip_sscr, &port->ip_serial_regs->sscr);
/* Set the configuration and proper notification call */
DPRINT_CONFIG(("%s : port 0x%p cflag 0%o "
"config_port(baud %d data %d stop %d p enable %d parity %d),"
" notification 0x%x\n",
- __FUNCTION__, (void *)port, cflag, baud, new_data, new_stop,
+ __func__, (void *)port, cflag, baud, new_data, new_stop,
new_parity_enable, new_parity, the_port->ignore_status_mask));
if ((config_port(port, baud, /* baud */
*/
static inline int ic4_startup_local(struct uart_port *the_port)
{
- int retval = 0;
struct ioc4_port *port;
- struct uart_info *info;
+ struct uart_state *state;
if (!the_port)
return -1;
- port = get_ioc4_port(the_port);
+ port = get_ioc4_port(the_port, 0);
if (!port)
return -1;
- info = the_port->info;
- if (info->flags & UIF_INITIALIZED) {
- return retval;
- }
+ state = the_port->state;
- if (info->tty) {
- set_bit(TTY_IO_ERROR, &info->tty->flags);
- clear_bit(TTY_IO_ERROR, &info->tty->flags);
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- info->tty->alt_speed = 57600;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- info->tty->alt_speed = 115200;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
- info->tty->alt_speed = 230400;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
- info->tty->alt_speed = 460800;
- }
local_open(port);
+ /* set the protocol - mapbase has the port type */
+ ioc4_set_proto(port, the_port->mapbase);
+
/* set the speed of the serial port */
- ioc4_change_speed(the_port, info->tty->termios, (struct termios *)0);
+ ioc4_change_speed(the_port, state->port.tty->termios,
+ (struct ktermios *)0);
- info->flags |= UIF_INITIALIZED;
return 0;
}
/*
* ioc4_cb_output_lowat - called when the output low water mark is hit
- * @port: port to output
+ * @the_port: port to output
*/
-static void ioc4_cb_output_lowat(struct ioc4_port *port)
+static void ioc4_cb_output_lowat(struct uart_port *the_port)
{
+ unsigned long pflags;
+
/* ip_lock is set on the call here */
- if (port->ip_port) {
- transmit_chars(port->ip_port);
+ if (the_port) {
+ spin_lock_irqsave(&the_port->lock, pflags);
+ transmit_chars(the_port);
+ spin_unlock_irqrestore(&the_port->lock, pflags);
}
}
-
/**
* handle_intr - service any interrupts for the given port - 2nd level
* called via sd_intr
struct ioc4_port *port = (struct ioc4_port *)arg;
struct hooks *hooks = port->ip_hooks;
unsigned int rx_high_rd_aborted = 0;
- unsigned int flags;
+ unsigned long flags;
struct uart_port *the_port;
int loop_counter;
if (sio_ir & hooks->intr_delta_dcd) {
/* ACK the interrupt */
writel(hooks->intr_delta_dcd,
- &port->ip_mem->sio_ir);
+ &port->ip_mem->sio_ir.raw);
shadow = readl(&port->ip_serial_regs->shadow);
the_port = port->ip_port;
the_port->icount.dcd = 1;
wake_up_interruptible
- (&the_port-> info->delta_msr_wait);
+ (&the_port->state->port.delta_msr_wait);
} else if ((port->ip_notify & N_DDCD)
&& !(shadow & IOC4_SHADOW_DCD)) {
/* Flag delta DCD/no DCD */
if (sio_ir & hooks->intr_delta_cts) {
/* ACK the interrupt */
writel(hooks->intr_delta_cts,
- &port->ip_mem->sio_ir);
+ &port->ip_mem->sio_ir.raw);
shadow = readl(&port->ip_serial_regs->shadow);
the_port->icount.cts =
(shadow & IOC4_SHADOW_CTS) ? 1 : 0;
wake_up_interruptible
- (&the_port->info->delta_msr_wait);
+ (&the_port->state->port.delta_msr_wait);
}
}
if (sio_ir & hooks->intr_rx_timer) {
/* ACK the interrupt */
writel(hooks->intr_rx_timer,
- &port->ip_mem->sio_ir);
+ &port->ip_mem->sio_ir.raw);
if ((port->ip_notify & N_DATA_READY)
&& (port->ip_port)) {
/* ACK the interrupt */
writel(hooks->intr_tx_explicit,
- &port->ip_mem->sio_ir);
+ &port->ip_mem->sio_ir.raw);
if (port->ip_notify & N_OUTPUT_LOWAT)
- ioc4_cb_output_lowat(port);
+ ioc4_cb_output_lowat(port->ip_port);
}
/* Handle tx_mt. Must come after tx_explicit. */
* So send the notification now.
*/
if (port->ip_notify & N_OUTPUT_LOWAT) {
- ioc4_cb_output_lowat(port);
+ ioc4_cb_output_lowat(port->ip_port);
/* We need to reload the sio_ir since the lowat
* call may have caused another write to occur,
int len)
{
int prod_ptr, cons_ptr, total;
- struct ioc4_port *port = get_ioc4_port(the_port);
+ struct ioc4_port *port = get_ioc4_port(the_port, 0);
struct ring *inring;
struct ring_entry *entry;
struct hooks *hooks = port->ip_hooks;
* available data as long as it returns some.
*/
/* Re-arm the timer */
- writel(port->ip_rx_cons | IOC4_SRCIR_ARM,
- &port->ip_serial_regs->srcir);
+ writel(port->ip_rx_cons | IOC4_SRCIR_ARM, &port->ip_serial_regs->srcir);
prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK;
cons_ptr = port->ip_rx_cons;
&& port->ip_port) {
the_port->icount.dcd = 0;
wake_up_interruptible
- (&the_port->info->
- delta_msr_wait);
+ (&the_port->state->
+ port.delta_msr_wait);
}
/* If we had any data to return, we
}
return total;
}
+
/**
* receive_chars - upper level read. Called with ip_lock.
* @the_port: port to read from
{
struct tty_struct *tty;
unsigned char ch[IOC4_MAX_CHARS];
- int read_count, request_count;
+ int read_count, request_count = IOC4_MAX_CHARS;
struct uart_icount *icount;
- struct uart_info *info = the_port->info;
+ struct uart_state *state = the_port->state;
+ unsigned long pflags;
/* Make sure all the pointers are "good" ones */
- if (!info)
+ if (!state)
return;
- if (!info->tty)
+ if (!state->port.tty)
return;
- tty = info->tty;
+ spin_lock_irqsave(&the_port->lock, pflags);
+ tty = state->port.tty;
- request_count = TTY_FLIPBUF_SIZE - tty->flip.count - 1;
+ request_count = tty_buffer_request_room(tty, IOC4_MAX_CHARS);
if (request_count > 0) {
- if (request_count > IOC4_MAX_CHARS - 2)
- request_count = IOC4_MAX_CHARS - 2;
icount = &the_port->icount;
read_count = do_read(the_port, ch, request_count);
if (read_count > 0) {
- memcpy(tty->flip.char_buf_ptr, ch, read_count);
- memset(tty->flip.flag_buf_ptr, TTY_NORMAL, read_count);
- tty->flip.char_buf_ptr += read_count;
- tty->flip.flag_buf_ptr += read_count;
- tty->flip.count += read_count;
+ tty_insert_flip_string(tty, ch, read_count);
icount->rx += read_count;
}
}
+
+ spin_unlock_irqrestore(&the_port->lock, pflags);
+
tty_flip_buffer_push(tty);
}
*/
static const char *ic4_type(struct uart_port *the_port)
{
- return "SGI IOC4 Serial";
+ if (the_port->mapbase == PROTO_RS232)
+ return "SGI IOC4 Serial [rs232]";
+ else
+ return "SGI IOC4 Serial [rs422]";
}
/**
- * ic4_tx_empty - Is the transmitter empty? We pretend we're always empty
- * @port: Port to operate on (we ignore since we always return 1)
+ * ic4_tx_empty - Is the transmitter empty?
+ * @port: Port to operate on
*
*/
static unsigned int ic4_tx_empty(struct uart_port *the_port)
{
- return 1;
+ struct ioc4_port *port = get_ioc4_port(the_port, 0);
+ unsigned int ret = 0;
+
+ if (port_is_active(port, the_port)) {
+ if (readl(&port->ip_serial_regs->shadow) & IOC4_SHADOW_TEMT)
+ ret = TIOCSER_TEMT;
+ }
+ return ret;
}
/**
* ic4_stop_tx - stop the transmitter
* @port: Port to operate on
- * @tty_stop: Set to 1 if called via uart_stop
*
*/
-static void ic4_stop_tx(struct uart_port *the_port, unsigned int tty_stop)
+static void ic4_stop_tx(struct uart_port *the_port)
{
+ struct ioc4_port *port = get_ioc4_port(the_port, 0);
+
+ if (port_is_active(port, the_port))
+ set_notification(port, N_OUTPUT_LOWAT, 0);
}
/**
{
unsigned long port_flags;
struct ioc4_port *port;
- struct uart_info *info;
+ struct uart_state *state;
- port = get_ioc4_port(the_port);
+ port = get_ioc4_port(the_port, 0);
if (!port)
return;
- info = the_port->info;
-
- if (!(info->flags & UIF_INITIALIZED))
- return;
+ state = the_port->state;
+ port->ip_port = NULL;
- wake_up_interruptible(&info->delta_msr_wait);
+ wake_up_interruptible(&state->port.delta_msr_wait);
- if (info->tty)
- set_bit(TTY_IO_ERROR, &info->tty->flags);
+ if (state->port.tty)
+ set_bit(TTY_IO_ERROR, &state->port.tty->flags);
- spin_lock_irqsave(&port->ip_lock, port_flags);
+ spin_lock_irqsave(&the_port->lock, port_flags);
set_notification(port, N_ALL, 0);
- info->flags &= ~UIF_INITIALIZED;
- spin_unlock_irqrestore(&port->ip_lock, port_flags);
+ port->ip_flags = PORT_INACTIVE;
+ spin_unlock_irqrestore(&the_port->lock, port_flags);
}
/**
static void ic4_set_mctrl(struct uart_port *the_port, unsigned int mctrl)
{
unsigned char mcr = 0;
+ struct ioc4_port *port;
+
+ port = get_ioc4_port(the_port, 0);
+ if (!port_is_active(port, the_port))
+ return;
if (mctrl & TIOCM_RTS)
mcr |= UART_MCR_RTS;
if (mctrl & TIOCM_LOOP)
mcr |= UART_MCR_LOOP;
- set_mcr(the_port, 1, mcr, IOC4_SHADOW_DTR);
+ set_mcr(the_port, mcr, IOC4_SHADOW_DTR);
}
/**
*/
static unsigned int ic4_get_mctrl(struct uart_port *the_port)
{
- struct ioc4_port *port = get_ioc4_port(the_port);
+ struct ioc4_port *port = get_ioc4_port(the_port, 0);
uint32_t shadow;
unsigned int ret = 0;
- if (!port)
+ if (!port_is_active(port, the_port))
return 0;
shadow = readl(&port->ip_serial_regs->shadow);
/**
* ic4_start_tx - Start transmitter, flush any output
* @port: Port to operate on
- * @tty_stop: Set to 1 if called via uart_start
*
*/
-static void ic4_start_tx(struct uart_port *the_port, unsigned int tty_stop)
+static void ic4_start_tx(struct uart_port *the_port)
{
- struct ioc4_port *port = get_ioc4_port(the_port);
- unsigned long flags;
+ struct ioc4_port *port = get_ioc4_port(the_port, 0);
- if (port) {
- spin_lock_irqsave(&port->ip_lock, flags);
- transmit_chars(the_port);
- spin_unlock_irqrestore(&port->ip_lock, flags);
+ if (port_is_active(port, the_port)) {
+ set_notification(port, N_OUTPUT_LOWAT, 1);
+ enable_intrs(port, port->ip_hooks->intr_tx_mt);
}
}
}
/**
- * ic4_startup - Start up the serial port - always return 0 (We're always on)
+ * ic4_startup - Start up the serial port
* @port: Port to operate on
*
*/
int retval;
struct ioc4_port *port;
struct ioc4_control *control;
- struct uart_info *info;
+ struct uart_state *state;
unsigned long port_flags;
- if (!the_port) {
+ if (!the_port)
return -ENODEV;
- }
- port = get_ioc4_port(the_port);
- if (!port) {
+ port = get_ioc4_port(the_port, 1);
+ if (!port)
return -ENODEV;
- }
- info = the_port->info;
+ state = the_port->state;
control = port->ip_control;
if (!control) {
+ port->ip_port = NULL;
return -ENODEV;
}
/* Start up the serial port */
- spin_lock_irqsave(&port->ip_lock, port_flags);
+ spin_lock_irqsave(&the_port->lock, port_flags);
retval = ic4_startup_local(the_port);
- spin_unlock_irqrestore(&port->ip_lock, port_flags);
+ spin_unlock_irqrestore(&the_port->lock, port_flags);
return retval;
}
*/
static void
ic4_set_termios(struct uart_port *the_port,
- struct termios *termios, struct termios *old_termios)
+ struct ktermios *termios, struct ktermios *old_termios)
{
- struct ioc4_port *port = get_ioc4_port(the_port);
unsigned long port_flags;
- spin_lock_irqsave(&port->ip_lock, port_flags);
+ spin_lock_irqsave(&the_port->lock, port_flags);
ioc4_change_speed(the_port, termios, old_termios);
- spin_unlock_irqrestore(&port->ip_lock, port_flags);
+ spin_unlock_irqrestore(&the_port->lock, port_flags);
}
/**
* Boot-time initialization code
*/
-static struct uart_driver ioc4_uart = {
+static struct uart_driver ioc4_uart_rs232 = {
+ .owner = THIS_MODULE,
+ .driver_name = "ioc4_serial_rs232",
+ .dev_name = DEVICE_NAME_RS232,
+ .major = DEVICE_MAJOR,
+ .minor = DEVICE_MINOR_RS232,
+ .nr = IOC4_NUM_CARDS * IOC4_NUM_SERIAL_PORTS,
+};
+
+static struct uart_driver ioc4_uart_rs422 = {
.owner = THIS_MODULE,
- .driver_name = "ioc4_serial",
- .dev_name = DEVICE_NAME,
+ .driver_name = "ioc4_serial_rs422",
+ .dev_name = DEVICE_NAME_RS422,
.major = DEVICE_MAJOR,
- .minor = DEVICE_MINOR,
+ .minor = DEVICE_MINOR_RS422,
.nr = IOC4_NUM_CARDS * IOC4_NUM_SERIAL_PORTS,
};
+
/**
- * ioc4_serial_core_attach - register with serial core
+ * ioc4_serial_remove_one - detach function
+ *
+ * @idd: IOC4 master module data for this IOC4
+ */
+
+static int ioc4_serial_remove_one(struct ioc4_driver_data *idd)
+{
+ int port_num, port_type;
+ struct ioc4_control *control;
+ struct uart_port *the_port;
+ struct ioc4_port *port;
+ struct ioc4_soft *soft;
+
+ /* If serial driver did not attach, don't try to detach */
+ control = idd->idd_serial_data;
+ if (!control)
+ return 0;
+
+ for (port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; port_num++) {
+ for (port_type = UART_PORT_MIN;
+ port_type < UART_PORT_COUNT;
+ port_type++) {
+ the_port = &control->ic_port[port_num].icp_uart_port
+ [port_type];
+ if (the_port) {
+ switch (port_type) {
+ case UART_PORT_RS422:
+ uart_remove_one_port(&ioc4_uart_rs422,
+ the_port);
+ break;
+ default:
+ case UART_PORT_RS232:
+ uart_remove_one_port(&ioc4_uart_rs232,
+ the_port);
+ break;
+ }
+ }
+ }
+ port = control->ic_port[port_num].icp_port;
+ /* we allocate in pairs */
+ if (!(port_num & 1) && port) {
+ pci_free_consistent(port->ip_pdev,
+ TOTAL_RING_BUF_SIZE,
+ port->ip_cpu_ringbuf,
+ port->ip_dma_ringbuf);
+ kfree(port);
+ }
+ }
+ soft = control->ic_soft;
+ if (soft) {
+ free_irq(control->ic_irq, soft);
+ if (soft->is_ioc4_serial_addr) {
+ iounmap(soft->is_ioc4_serial_addr);
+ release_mem_region((unsigned long)
+ soft->is_ioc4_serial_addr,
+ sizeof(struct ioc4_serial));
+ }
+ kfree(soft);
+ }
+ kfree(control);
+ idd->idd_serial_data = NULL;
+
+ return 0;
+}
+
+
+/**
+ * ioc4_serial_core_attach_rs232 - register with serial core
* This is done during pci probing
* @pdev: handle for this card
*/
static inline int
-ioc4_serial_core_attach(struct pci_dev *pdev)
+ioc4_serial_core_attach(struct pci_dev *pdev, int port_type)
{
struct ioc4_port *port;
struct uart_port *the_port;
- struct ioc4_control *control = pci_get_drvdata(pdev);
- int ii;
+ struct ioc4_driver_data *idd = pci_get_drvdata(pdev);
+ struct ioc4_control *control = idd->idd_serial_data;
+ int port_num;
+ int port_type_idx;
+ struct uart_driver *u_driver;
+
DPRINT_CONFIG(("%s: attach pdev 0x%p - control 0x%p\n",
- __FUNCTION__, pdev, (void *)control));
+ __func__, pdev, (void *)control));
if (!control)
return -ENODEV;
+ port_type_idx = (port_type == PROTO_RS232) ? UART_PORT_RS232
+ : UART_PORT_RS422;
+
+ u_driver = (port_type == PROTO_RS232) ? &ioc4_uart_rs232
+ : &ioc4_uart_rs422;
+
/* once around for each port on this card */
- for (ii = 0; ii < IOC4_NUM_SERIAL_PORTS; ii++) {
- the_port = &control->ic_port[ii].icp_uart_port;
- port = control->ic_port[ii].icp_port;
- port->ip_port = the_port;
+ for (port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; port_num++) {
+ the_port = &control->ic_port[port_num].icp_uart_port
+ [port_type_idx];
+ port = control->ic_port[port_num].icp_port;
+ port->ip_all_ports[port_type_idx] = the_port;
- DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p\n",
- __FUNCTION__, (void *)the_port,
- (void *)port));
+ DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p : type %s\n",
+ __func__, (void *)the_port,
+ (void *)port,
+ port_type == PROTO_RS232 ? "rs232" : "rs422"));
- spin_lock_init(&the_port->lock);
/* membase, iobase and mapbase just need to be non-0 */
the_port->membase = (unsigned char __iomem *)1;
- the_port->line = the_port->iobase = ii;
- the_port->mapbase = 1;
+ the_port->iobase = (pdev->bus->number << 16) | port_num;
+ the_port->line = (Num_of_ioc4_cards << 2) | port_num;
+ the_port->mapbase = port_type;
the_port->type = PORT_16550A;
- the_port->fifosize = IOC4_MAX_CHARS;
+ the_port->fifosize = IOC4_FIFO_CHARS;
the_port->ops = &ioc4_ops;
the_port->irq = control->ic_irq;
the_port->dev = &pdev->dev;
- if (uart_add_one_port(&ioc4_uart, the_port) < 0) {
+ spin_lock_init(&the_port->lock);
+ if (uart_add_one_port(u_driver, the_port) < 0) {
printk(KERN_WARNING
- "%s: unable to add port %d\n",
- __FUNCTION__, the_port->line);
+ "%s: unable to add port %d bus %d\n",
+ __func__, the_port->line, pdev->bus->number);
} else {
DPRINT_CONFIG(
- ("IOC4 serial driver port %d irq = %d\n",
- the_port->line, the_port->irq));
+ ("IOC4 serial port %d irq = %d, bus %d\n",
+ the_port->line, the_port->irq, pdev->bus->number));
}
- /* all ports are rs232 for now */
- ioc4_set_proto(port, PROTO_RS232);
}
return 0;
}
/**
* ioc4_serial_attach_one - register attach function
- * called per card found from ioc4_serial_detect as part
- * of module_init().
- * @pdev: handle for this card
- * @pci_id: pci id for this card
+ * called per card found from IOC4 master module.
+ * @idd: Master module data for this IOC4
*/
int
-ioc4_serial_attach_one(struct pci_dev *pdev, const struct pci_device_id *pci_id)
+ioc4_serial_attach_one(struct ioc4_driver_data *idd)
{
- struct ioc4_mem __iomem *mem;
- unsigned long tmp_addr, tmp_addr1;
+ unsigned long tmp_addr1;
struct ioc4_serial __iomem *serial;
struct ioc4_soft *soft;
struct ioc4_control *control;
- int tmp, ret = 0;
+ int ret = 0;
- DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __FUNCTION__, pdev, pci_id));
+ DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __func__, idd->idd_pdev,
+ idd->idd_pci_id));
- /* Map in the ioc4 memory */
- tmp_addr = pci_resource_start(pdev, 0);
- if (!tmp_addr) {
- printk(KERN_WARNING
- "ioc4 (%p) : unable to get PIO mapping for "
- "MEM space\n", (void *)pdev);
- return -ENODEV;
- }
- if (!request_region(tmp_addr, sizeof(struct ioc4_mem), "sioc4_mem")) {
- printk(KERN_ALERT
- "ioc4 (%p): unable to get request region for "
- "MEM space\n", (void *)pdev);
- return -ENODEV;
- }
- mem = ioremap(tmp_addr, sizeof(struct ioc4_mem));
- if (!mem) {
- printk(KERN_WARNING
- "ioc4 (%p) : unable to remap ioc4 memory\n",
- (void *)pdev);
- ret = -ENODEV;
- goto out1;
- }
+ /* PCI-RT does not bring out serial connections.
+ * Do not attach to this particular IOC4.
+ */
+ if (idd->idd_variant == IOC4_VARIANT_PCI_RT)
+ return 0;
/* request serial registers */
- tmp_addr1 = pci_resource_start(pdev, 0) + IOC4_SERIAL_OFFSET;
+ tmp_addr1 = idd->idd_bar0 + IOC4_SERIAL_OFFSET;
- if (!request_region(tmp_addr1, sizeof(struct ioc4_serial),
+ if (!request_mem_region(tmp_addr1, sizeof(struct ioc4_serial),
"sioc4_uart")) {
printk(KERN_WARNING
"ioc4 (%p): unable to get request region for "
- "uart space\n", (void *)pdev);
+ "uart space\n", (void *)idd->idd_pdev);
ret = -ENODEV;
goto out1;
}
if (!serial) {
printk(KERN_WARNING
"ioc4 (%p) : unable to remap ioc4 serial register\n",
- (void *)pdev);
+ (void *)idd->idd_pdev);
ret = -ENODEV;
goto out2;
}
DPRINT_CONFIG(("%s : mem 0x%p, serial 0x%p\n",
- __FUNCTION__, (void *)mem, (void *)serial));
+ __func__, (void *)idd->idd_misc_regs,
+ (void *)serial));
/* Get memory for the new card */
- control = kmalloc(sizeof(struct ioc4_control) * IOC4_NUM_SERIAL_PORTS,
- GFP_KERNEL);
+ control = kzalloc(sizeof(struct ioc4_control), GFP_KERNEL);
if (!control) {
printk(KERN_WARNING "ioc4_attach_one"
ret = -ENOMEM;
goto out2;
}
- memset(control, 0, sizeof(struct ioc4_control));
- pci_set_drvdata(pdev, control);
+ idd->idd_serial_data = control;
/* Allocate the soft structure */
- soft = kmalloc(sizeof(struct ioc4_soft), GFP_KERNEL);
+ soft = kzalloc(sizeof(struct ioc4_soft), GFP_KERNEL);
if (!soft) {
printk(KERN_WARNING
"ioc4 (%p): unable to get memory for the soft struct\n",
- (void *)pdev);
+ (void *)idd->idd_pdev);
ret = -ENOMEM;
goto out3;
}
- memset(soft, 0, sizeof(struct ioc4_soft));
spin_lock_init(&soft->is_ir_lock);
- soft->is_ioc4_mem_addr = mem;
+ soft->is_ioc4_misc_addr = idd->idd_misc_regs;
soft->is_ioc4_serial_addr = serial;
/* Init the IOC4 */
- pci_read_config_dword(pdev, PCI_COMMAND, &tmp);
- pci_write_config_dword(pdev, PCI_COMMAND,
- tmp | PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
-
- writel(0xf << IOC4_SIO_CR_CMD_PULSE_SHIFT, &mem->sio_cr);
+ writel(0xf << IOC4_SIO_CR_CMD_PULSE_SHIFT,
+ &idd->idd_misc_regs->sio_cr.raw);
/* Enable serial port mode select generic PIO pins as outputs */
writel(IOC4_GPCR_UART0_MODESEL | IOC4_GPCR_UART1_MODESEL
| IOC4_GPCR_UART2_MODESEL | IOC4_GPCR_UART3_MODESEL,
- &mem->gpcr_s);
+ &idd->idd_misc_regs->gpcr_s.raw);
- /* Clear and disable all interrupts */
+ /* Clear and disable all serial interrupts */
write_ireg(soft, ~0, IOC4_W_IEC, IOC4_SIO_INTR_TYPE);
- writel(~0, &mem->sio_ir);
- write_ireg(soft, ~(IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR),
- IOC4_W_IEC, IOC4_OTHER_INTR_TYPE);
- writel(~(IOC4_OTHER_IR_ATA_MEMERR | IOC4_OTHER_IR_ATA_MEMERR),
- &mem->other_ir);
+ writel(~0, &idd->idd_misc_regs->sio_ir.raw);
+ write_ireg(soft, IOC4_OTHER_IR_SER_MEMERR, IOC4_W_IEC,
+ IOC4_OTHER_INTR_TYPE);
+ writel(IOC4_OTHER_IR_SER_MEMERR, &idd->idd_misc_regs->other_ir.raw);
control->ic_soft = soft;
- if (!request_irq(pdev->irq, ioc4_intr, SA_SHIRQ,
- "sgi-ioc4serial", (void *)soft)) {
- control->ic_irq = pdev->irq;
+
+ /* Hook up interrupt handler */
+ if (!request_irq(idd->idd_pdev->irq, ioc4_intr, IRQF_SHARED,
+ "sgi-ioc4serial", soft)) {
+ control->ic_irq = idd->idd_pdev->irq;
} else {
printk(KERN_WARNING
"%s : request_irq fails for IRQ 0x%x\n ",
- __FUNCTION__, pdev->irq);
+ __func__, idd->idd_pdev->irq);
}
- if ((ret = ioc4_attach_local(pdev, control, soft,
- soft->is_ioc4_mem_addr,
- soft->is_ioc4_serial_addr)))
+ ret = ioc4_attach_local(idd);
+ if (ret)
goto out4;
- /* register port with the serial core */
+ /* register port with the serial core - 1 rs232, 1 rs422 */
- if ((ret = ioc4_serial_core_attach(pdev)))
+ if ((ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS232)))
goto out4;
+ if ((ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS422)))
+ goto out5;
+
+ Num_of_ioc4_cards++;
+
return ret;
/* error exits that give back resources */
+out5:
+ ioc4_serial_remove_one(idd);
out4:
kfree(soft);
out3:
kfree(control);
out2:
- release_region(tmp_addr1, sizeof(struct ioc4_serial));
+ if (serial)
+ iounmap(serial);
+ release_mem_region(tmp_addr1, sizeof(struct ioc4_serial));
out1:
- release_region(tmp_addr, sizeof(struct ioc4_mem));
return ret;
}
-/**
- * ioc4_serial_remove_one - detach function
- *
- * @pdev: handle for this card
- */
-
-#if 0
-void ioc4_serial_remove_one(struct pci_dev *pdev)
-{
- int ii;
- struct ioc4_control *control;
- struct uart_port *the_port;
- struct ioc4_port *port;
- struct ioc4_soft *soft;
-
- control = pci_get_drvdata(pdev);
-
- for (ii = 0; ii < IOC4_NUM_SERIAL_PORTS; ii++) {
- the_port = &control->ic_port[ii].icp_uart_port;
- if (the_port) {
- uart_remove_one_port(&ioc4_uart, the_port);
- }
- port = control->ic_port[ii].icp_port;
- if (!(ii & 1) && port) {
- pci_free_consistent(port->ip_pdev,
- TOTAL_RING_BUF_SIZE,
- (void *)port->ip_cpu_ringbuf,
- port->ip_dma_ringbuf);
- kfree(port);
- }
- }
- soft = control->ic_soft;
- if (soft) {
- free_irq(control->ic_irq, (void *)soft);
- if (soft->is_ioc4_serial_addr) {
- release_region((unsigned long)
- soft->is_ioc4_serial_addr,
- sizeof(struct ioc4_serial));
- }
- kfree(soft);
- }
- kfree(control);
- pci_set_drvdata(pdev, NULL);
- uart_unregister_driver(&ioc4_uart);
-}
-#endif
+static struct ioc4_submodule ioc4_serial_submodule = {
+ .is_name = "IOC4_serial",
+ .is_owner = THIS_MODULE,
+ .is_probe = ioc4_serial_attach_one,
+ .is_remove = ioc4_serial_remove_one,
+};
/**
* ioc4_serial_init - module init
int ret;
/* register with serial core */
- if ((ret = uart_register_driver(&ioc4_uart)) < 0) {
+ if ((ret = uart_register_driver(&ioc4_uart_rs232)) < 0) {
printk(KERN_WARNING
- "%s: Couldn't register IOC4 serial driver\n",
- __FUNCTION__);
+ "%s: Couldn't register rs232 IOC4 serial driver\n",
+ __func__);
return ret;
}
- return 0;
+ if ((ret = uart_register_driver(&ioc4_uart_rs422)) < 0) {
+ printk(KERN_WARNING
+ "%s: Couldn't register rs422 IOC4 serial driver\n",
+ __func__);
+ return ret;
+ }
+
+ /* register with IOC4 main module */
+ return ioc4_register_submodule(&ioc4_serial_submodule);
}
+static void __devexit ioc4_serial_exit(void)
+{
+ ioc4_unregister_submodule(&ioc4_serial_submodule);
+ uart_unregister_driver(&ioc4_uart_rs232);
+ uart_unregister_driver(&ioc4_uart_rs422);
+}
+
+late_initcall(ioc4_serial_init); /* Call only after tty init is done */
+module_exit(ioc4_serial_exit);
+
MODULE_AUTHOR("Pat Gefre - Silicon Graphics Inc. (SGI) <pfg@sgi.com>");
MODULE_DESCRIPTION("Serial PCI driver module for SGI IOC4 Base-IO Card");
MODULE_LICENSE("GPL");
-
-EXPORT_SYMBOL(ioc4_serial_init);
-EXPORT_SYMBOL(ioc4_serial_attach_one);