📄 ioc4.c
字号:
* the connect points. * This code is slightly paranoid. */ rc = hwgraph_path_add(conn_vhdl, EDGE_LBL_IOC4, &ioc4_vhdl); ASSERT(rc == GRAPH_SUCCESS); /* * Allocate the soft structure, fill it in a bit, * and attach it to the ioc4 vertex. */ NEW(soft); spin_lock_init(&soft->is_ir_lock); soft->is_ioc4_vhdl = ioc4_vhdl; soft->is_conn_vhdl = conn_vhdl; soft->is_ioc4_mem = mem; soft->is_pci_dev = pci_handle; ioc4_soft_set(ioc4_vhdl, soft); /* Init the IOC4 */ /* SN boot PROMs allocate the PCI * space and set up the pci_addr fields. * Other systems need to set the base address. * This is handled automatically if the PCI infrastructure * is used. * * No need to set the latency timer since the PCI * infrastructure sets it to 1 us. */ pci_read_config_dword(pci_handle, IOC4_PCI_SCR, &tmp); pci_write_config_dword(pci_handle, IOC4_PCI_SCR, tmp | PCI_CMD_BUS_MASTER | PCI_CMD_MEM_SPACE | PCI_CMD_PAR_ERR_RESP | PCI_CMD_SERR_ENABLE); PCI_OUTW(&mem->sio_cr, (0xf << IOC4_SIO_CR_CMD_PULSE_SHIFT)); /* Enable serial port mode select generic PIO pins as outputs */ PCI_OUTW(&mem->gpcr_s, IOC4_GPCR_UART0_MODESEL | IOC4_GPCR_UART1_MODESEL); /* Clear and disable all interrupts */ IOC4_WRITE_IEC(soft, ~0, ioc4_sio_intr_type); PCI_OUTW(&mem->sio_ir, ~0); IOC4_WRITE_IEC(soft, ~0, ioc4_other_intr_type); PCI_OUTW(&mem->other_ir, ~0); /* * Alloc the IOC4 intr before attaching the subdevs, so the * cpu handling the IOC4 intr is known (for setmustrun on * the ioc4 ithreads). */ /* attach interrupt handler */ ioc4_ss_connect_interrupt(pci_handle->irq, (void *)ioc4_intr, (void *)soft); /* ============================================================= * Attach Sub-devices * * NB: As subdevs start calling pciio_driver_register(), * we can stop explicitly calling subdev drivers. * * The drivers attached here have not been converted * to stand on their own. However, they *do* know * to call ioc4_subdev_enabled() to decide whether * to actually attach themselves. * * It would be nice if we could convert these * few remaining drivers over so they would * register as proper PCI device drivers ... */ ioc4_serial_attach(conn_vhdl, (void *)soft->is_ioc4_mem); /* DMA serial ports */ /* Normally we'd return 0 - but we need to get the ide driver init'd too. * Returning an error will keep the IOC4 on the pci list */ return -1;}/* * ioc4_intr_connect: * Arrange for interrupts for a sub-device * to be delivered to the right bit of * code with the right parameter. * * XXX- returning an error instead of panicing * might be a good idea (think bugs in loadable * ioc4 sub-devices). */voidioc4_intr_connect(vertex_hdl_t conn_vhdl, ioc4_intr_type_t type, ioc4reg_t intrbits, ioc4_intr_func_f *intr, intr_arg_t info, vertex_hdl_t owner_vhdl, vertex_hdl_t intr_dev_vhdl){ graph_error_t rc; vertex_hdl_t ioc4_vhdl; ioc4_soft_t *soft; ioc4reg_t old, bits; int i; ASSERT((type == ioc4_sio_intr_type) || (type == ioc4_other_intr_type)); rc = hwgraph_traverse(conn_vhdl, EDGE_LBL_IOC4, &ioc4_vhdl); if (rc != GRAPH_SUCCESS) { printk(KERN_ALERT "ioc4_intr_connect(%p): ioc4_attach not yet called", (void *)owner_vhdl); return; } soft = ioc4_soft_get(ioc4_vhdl); ASSERT(soft != NULL); /* * Try to allocate a slot in the array * that has been marked free; if there * are none, extend the high water mark. */ while (1) { bits = atomic_read(&soft->is_intr_type[type].is_intr_ents_free); if (bits == 0) { i = atomic_inc(&soft->is_intr_type[type].is_num_intrs) - 1; ASSERT(i < MAX_IOC4_INTR_ENTS || (printk("i %d\n", i), 0)); break; } bits &= ~(bits - 1); /* keep only the ls bit */ old = atomicClearInt(&soft->is_intr_type[type].is_intr_ents_free, bits); if (bits & old) { ioc4reg_t shf; i = 31; if ((shf = (bits >> 16))) bits = shf; else i -= 16; if ((shf = (bits >> 8))) bits = shf; else i -= 8; if ((shf = (bits >> 4))) bits = shf; else i -= 4; if ((shf = (bits >> 2))) bits = shf; else i -= 2; if ((shf = (bits >> 1))) bits = shf; else i -= 1; ASSERT(i < MAX_IOC4_INTR_ENTS || (printk("i %d\n", i), 0)); break; } } soft->is_intr_type[type].is_intr_info[i].sd_bits = intrbits; soft->is_intr_type[type].is_intr_info[i].sd_intr = intr; soft->is_intr_type[type].is_intr_info[i].sd_info = info; soft->is_intr_type[type].is_intr_info[i].sd_vhdl = owner_vhdl; soft->is_intr_type[type].is_intr_info[i].sd_soft = soft; /* Make sure there are no bitmask overlaps */ { ioc4reg_t old; old = atomicSetInt(&soft->is_intr_type[type].is_intr_bits_busy, intrbits); if (old & intrbits) { printk("%p: trying to share ioc4 intr bits 0x%X\n", (void *)owner_vhdl, old & intrbits);#if DEBUG && IOC4_DEBUG { int x; for (x = 0; x < i; x++) if (intrbits & soft->is_intr_type[type].is_intr_info[x].sd_bits) { printk("%p: ioc4 intr bits 0x%X already call " "0x%X(0x%X, ...)\n", (void *)soft->is_intr_type[type].is_intr_info[x].sd_vhdl, soft->is_intr_type[type].is_intr_info[i].sd_bits, soft->is_intr_type[type].is_intr_info[i].sd_intr, soft->is_intr_type[type].is_intr_info[i].sd_info); } }#endif panic("ioc4_intr_connect: no IOC4 interrupt source sharing allowed"); } }}/* * ioc4_intr_disconnect: * Turn off interrupt request service for a * specific service function and argument. * Scans the array for connections to the specified * function with the specified info and owner; turns off * the bits specified in intrbits. If this results in * an empty entry, logs it in the free entry map. */voidioc4_intr_disconnect(vertex_hdl_t conn_vhdl, ioc4_intr_type_t type, ioc4reg_t intrbits, ioc4_intr_func_f *intr, intr_arg_t info, vertex_hdl_t owner_vhdl){ graph_error_t rc; vertex_hdl_t ioc4_vhdl; ioc4_soft_t *soft; ioc4reg_t bits; int i, num_intrs; ASSERT((type == ioc4_sio_intr_type) || (type == ioc4_other_intr_type)); rc = hwgraph_traverse(conn_vhdl, EDGE_LBL_IOC4, &ioc4_vhdl); if (rc != GRAPH_SUCCESS) { printk(KERN_ALERT "%p: ioc4_intr_disconnect: ioc4_attach not yet called", (void *)owner_vhdl); return; } soft = ioc4_soft_get(ioc4_vhdl); ASSERT(soft != NULL); num_intrs = (int)atomic_read(&soft->is_intr_type[type].is_num_intrs); for (i = 0; i < num_intrs; ++i) { if ((soft->is_intr_type[type].is_intr_info[i].sd_intr == intr) && (soft->is_intr_type[type].is_intr_info[i].sd_info == info) && (soft->is_intr_type[type].is_intr_info[i].sd_vhdl == owner_vhdl) && (bits = soft->is_intr_type[type].is_intr_info[i].sd_bits & intrbits)) { soft->is_intr_type[type].is_intr_info[i].sd_bits &= ~bits; atomicClearInt(&soft->is_intr_type[type].is_intr_bits_busy, bits); if (!(soft->is_intr_type[type].is_intr_info[i].sd_bits)) { soft->is_intr_type[type].is_intr_info[i].sd_intr = NULL; soft->is_intr_type[type].is_intr_info[i].sd_info = NULL; soft->is_intr_type[type].is_intr_info[i].sd_vhdl = GRAPH_VERTEX_NONE; atomicSetInt(&soft->is_intr_type[type].is_intr_ents_free, 1 << i); } } }}/* Top level IOC4 interrupt handler. Farms out the interrupt to * the various IOC4 device drivers. */voidioc4_intr(int irq, void *arg, struct pt_regs *regs){ ioc4_soft_t *soft; ioc4reg_t this_ir; ioc4reg_t this_mir; int x, num_intrs = 0; ioc4_intr_type_t t; soft = (ioc4_soft_t *)arg; if (!soft) return; /* Polled but no console ioc4 registered */ for (t = ioc4_first_intr_type; t < ioc4_num_intr_types; t++) { num_intrs = (int)atomic_read(&soft->is_intr_type[t].is_num_intrs); this_mir = this_ir = ioc4_pending_intrs(soft, t);#ifdef DEBUG_INTERRUPTS printk("%s : %d : this_mir 0x%x num_intrs %d\n", __FUNCTION__, __LINE__, this_mir, num_intrs);#endif /* Farm out the interrupt to the various drivers depending on * which interrupt bits are set. */ for (x = 0; x < num_intrs; x++) { struct ioc4_intr_info *ii = &soft->is_intr_type[t].is_intr_info[x]; if ((this_mir = this_ir & ii->sd_bits)) { /* Disable owned interrupts, and call the interrupt handler */ IOC4_WRITE_IEC(soft, ii->sd_bits, t); ii->sd_intr(ii->sd_info, this_mir); this_ir &= ~this_mir; } } if (this_ir) printk(KERN_ALERT "unknown IOC4 %s interrupt 0x%x, sio_ir = 0x%x, sio_ies = 0x%x, other_ir = 0x%x, other_ies = 0x%x\n", (t == ioc4_sio_intr_type) ? "sio" : "other", this_ir, soft->is_ioc4_mem->sio_ir, soft->is_ioc4_mem->sio_ies_ro, soft->is_ioc4_mem->other_ir, soft->is_ioc4_mem->other_ies_ro); }#ifdef DEBUG_INTERRUPTS { ioc4_mem_t *mem = soft->is_ioc4_mem; spinlock_t *lp = &soft->is_ir_lock; unsigned long s; spin_lock_irqsave(lp, s); printk("%s : %d : sio_ir 0x%x sio_ies_ro 0x%x other_ir 0x%x other_ies_ro 0x%x mask 0x%x\n", __FUNCTION__, __LINE__, mem->sio_ir, mem->sio_ies_ro, mem->other_ir, mem->other_ies_ro, IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR); spin_unlock_irqrestore(lp, s); }#endif}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -