//___________________________________________________________________________________________ long EvtUnlockedIoDeviceControl(struct file* filep, unsigned int cmd, unsigned long arg) { int iTimeoutMs, iSize; //used by interrupt and record DeviceContext *d = filep->private_data; switch(cmd) { case 'r': //get record, arg contains pointer to buffer containing timeout and size, and arg returns pointer to record if( copy_from_user(&iSize, (int*)arg, sizeof(iSize)) ) //get data from user space in arg (e.g. 1000) and copy to kernel buffer (iTimeoutMs) return -EFAULT; if( copy_from_user(&iTimeoutMs, (int*) (arg + sizeof(iSize)), sizeof(iTimeoutMs)) ) //get data from user space in arg (e.g. 1000) and copy to kernel buffer (iTimeoutMs) return -EFAULT; if (iTimeoutMs > 0) //positive timeout wait_event_interruptible_timeout(d->recordWaitQ, d->recordQ.in != d->recordQ.out, iTimeoutMs * HZ / 1000); //wait up to the timeout for the IRQ byte to be set by the IRQ handler (wait for the circular buffer to be non-empty) else if(iTimeoutMs < 0) //negative timeout (timeout disabled) wait_event_interruptible(d->recordWaitQ, d->recordQ.in != d->recordQ.out); //wait forever if(d->recordQ.in != d->recordQ.out) //q non empty { if(copy_to_user((char*)arg, d->recordQ.q[ d->recordQ.out ], iSize)) //return the record retrieved in the interrupt handler to users space in arg return -EFAULT; d->recordQ.out = (d->recordQ.out + 1) % SIZE; //increment the read pointer } else //wait_event was interrupted by a signal (e.g. ctrl-c) < 0, or timeout = 0 return -ERESTARTSYS; //user mode ioctl returns -1 break; case 'i': //interrupt (get EI/UI status byte). arg contains pointer to timeout, and arg returns pointer to status byte if( copy_from_user(&iTimeoutMs, (int*)arg, sizeof(iTimeoutMs)) ) //get data from user space in arg (e.g. 1000) and copy to kernel buffer (iTimeoutMs) return -EFAULT; if (iTimeoutMs > 0) //positive timeout wait_event_interruptible_timeout(d->interruptWaitQ, d->interruptQ.in != d->interruptQ.out, iTimeoutMs * HZ / 1000); //wait up to the timeout for the IRQ byte to be set by the IRQ handler (wait for the circular buffer to be non-empty) else if(iTimeoutMs < 0) //negative timeout (timeout disabled) wait_event_interruptible(d->interruptWaitQ, d->interruptQ.in != d->interruptQ.out); //wait forever if(d->interruptQ.in != d->interruptQ.out) //q non empty { if(copy_to_user((int*)arg, &(d->interruptQ.q[ d->interruptQ.out ]), sizeof(unsigned char))) //return the status byte retrieved in the interrupt handler to users space in arg return -EFAULT; d->interruptQ.out = (d->interruptQ.out + 1) % SIZE; //increment the read pointer } else //wait_event was interrupted by a signal (e.g. ctrl-c) < 0, or timeout = 0 return -ERESTARTSYS; //user mode ioctl returns -1 break; case 'R': //reset DMC-18x0/6 if(d->pciInfo.m_fSubSystem == DMC18x6) //18x6 (do nothing on 18x2) outb(0x80, d->baseAddress + 8); //reset 18x6 break; case 'v': printk(KERN_INFO "GalilIoctl IOCTL_GALIL_VERSION\n"); if (copy_to_user((char*)arg, "galiltools", strlen("galiltools"))) return -EACCES; break; default: return -ENOTTY; //invalid ioctl code (switch fell thru). POSIX says we should return -ENOTTY instead of EINVAL } return 0; //normal }
static inline void fWrite_hfc8(hfc4s8s_hw *a, u_char c) { outb(c, a->iobase); }
void pic_send_eoi(unsigned char irq) { outb(PIC1_COMMAND, PIC_EOI); if (irq >= 32 + 8) outb(PIC2_COMMAND, PIC_EOI); }
static void ide_outb (u8 val, unsigned long port) { outb(val, port); }
/* inline functions io mapped */ static inline void SetRegAddr(hfc4s8s_hw *a, u_char b) { outb(b, (a->iobase) + 4); }
PUBLIC void irq_8259_mask(const int irq) { const unsigned ctl_mask = irq < 8 ? INT_CTLMASK : INT2_CTLMASK; outb(ctl_mask, inb(ctl_mask) | (1 << (irq & 0x7))); }
static uchar cgaregr(int index) { outb(0x3D4, index); return inb(0x3D4+1) & 0xFF; }
static void quirk_wakeup_oxsemi(struct pcmcia_device *link) { struct serial_info *info = link->priv; outb(12, info->c950ctrl + 1); }
/** * host_control_smi: generate host control SMI * * Caller must set up the host control command in smi_data_buf. */ static int host_control_smi(void) { struct apm_cmd *apm_cmd; u8 *data; unsigned long flags; u32 num_ticks; s8 cmd_status; u8 index; apm_cmd = (struct apm_cmd *)smi_data_buf; apm_cmd->status = ESM_STATUS_CMD_UNSUCCESSFUL; switch (host_control_smi_type) { case HC_SMITYPE_TYPE1: spin_lock_irqsave(&rtc_lock, flags); /* write SMI data buffer physical address */ data = (u8 *)&smi_data_buf_phys_addr; for (index = PE1300_CMOS_CMD_STRUCT_PTR; index < (PE1300_CMOS_CMD_STRUCT_PTR + 4); index++, data++) { outb(index, (CMOS_BASE_PORT + CMOS_PAGE2_INDEX_PORT_PIIX4)); outb(*data, (CMOS_BASE_PORT + CMOS_PAGE2_DATA_PORT_PIIX4)); } /* first set status to -1 as called by spec */ cmd_status = ESM_STATUS_CMD_UNSUCCESSFUL; outb((u8) cmd_status, PCAT_APM_STATUS_PORT); /* generate SMM call */ outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT); spin_unlock_irqrestore(&rtc_lock, flags); /* wait a few to see if it executed */ num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING; while ((cmd_status = inb(PCAT_APM_STATUS_PORT)) == ESM_STATUS_CMD_UNSUCCESSFUL) { num_ticks--; if (num_ticks == EXPIRED_TIMER) return -ETIME; } break; case HC_SMITYPE_TYPE2: case HC_SMITYPE_TYPE3: spin_lock_irqsave(&rtc_lock, flags); /* write SMI data buffer physical address */ data = (u8 *)&smi_data_buf_phys_addr; for (index = PE1400_CMOS_CMD_STRUCT_PTR; index < (PE1400_CMOS_CMD_STRUCT_PTR + 4); index++, data++) { outb(index, (CMOS_BASE_PORT + CMOS_PAGE1_INDEX_PORT)); outb(*data, (CMOS_BASE_PORT + CMOS_PAGE1_DATA_PORT)); } /* generate SMM call */ if (host_control_smi_type == HC_SMITYPE_TYPE3) outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT); else outb(ESM_APM_CMD, PE1400_APM_CONTROL_PORT); /* restore RTC index pointer since it was written to above */ CMOS_READ(RTC_REG_C); spin_unlock_irqrestore(&rtc_lock, flags); /* read control port back to serialize write */ cmd_status = inb(PE1400_APM_CONTROL_PORT); /* wait a few to see if it executed */ num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING; while (apm_cmd->status == ESM_STATUS_CMD_UNSUCCESSFUL) { num_ticks--; if (num_ticks == EXPIRED_TIMER) return -ETIME; } break; default: dev_dbg(&dcdbas_pdev->dev, "%s: invalid SMI type %u\n", __func__, host_control_smi_type); return -ENOSYS; } return 0; }
int __devinit setup_hfcs(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; char tmp[64]; strcpy(tmp, hfcs_revision); printk(KERN_INFO "HiSax: HFC-S driver Rev. %s\n", HiSax_getrev(tmp)); #ifdef __ISAPNP__ if (!card->para[1] && isapnp_present()) { struct pnp_dev *pnp_d; while(ipid->card_vendor) { if ((pnp_c = pnp_find_card(ipid->card_vendor, ipid->card_device, pnp_c))) { pnp_d = NULL; if ((pnp_d = pnp_find_dev(pnp_c, ipid->vendor, ipid->function, pnp_d))) { int err; printk(KERN_INFO "HiSax: %s detected\n", (char *)ipid->driver_data); pnp_disable_dev(pnp_d); err = pnp_activate_dev(pnp_d); if (err<0) { printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", __func__, err); return(0); } card->para[1] = pnp_port_start(pnp_d, 0); card->para[0] = pnp_irq(pnp_d, 0); if (!card->para[0] || !card->para[1]) { printk(KERN_ERR "HFC PnP:some resources are missing %ld/%lx\n", card->para[0], card->para[1]); pnp_disable_dev(pnp_d); return(0); } break; } else { printk(KERN_ERR "HFC PnP: PnP error card found, no device\n"); } } ipid++; pnp_c = NULL; } if (!ipid->card_vendor) { printk(KERN_INFO "HFC PnP: no ISAPnP card found\n"); return(0); } } #endif cs->hw.hfcD.addr = card->para[1] & 0xfffe; cs->irq = card->para[0]; cs->hw.hfcD.cip = 0; cs->hw.hfcD.int_s1 = 0; cs->hw.hfcD.send = NULL; cs->bcs[0].hw.hfc.send = NULL; cs->bcs[1].hw.hfc.send = NULL; cs->hw.hfcD.dfifosize = 512; cs->dc.hfcd.ph_state = 0; cs->hw.hfcD.fifo = 255; if (cs->typ == ISDN_CTYPE_TELES3C) { cs->hw.hfcD.bfifosize = 1024 + 512; } else if (cs->typ == ISDN_CTYPE_ACERP10) { cs->hw.hfcD.bfifosize = 7*1024 + 512; } else return (0); if (!request_region(cs->hw.hfcD.addr, 2, "HFCS isdn")) { printk(KERN_WARNING "HiSax: %s config port %x-%x already in use\n", CardType[card->typ], cs->hw.hfcD.addr, cs->hw.hfcD.addr + 2); return (0); } printk(KERN_INFO "HFCS: defined at 0x%x IRQ %d HZ %d\n", cs->hw.hfcD.addr, cs->irq, HZ); if (cs->typ == ISDN_CTYPE_TELES3C) { /* Teles 16.3c IO ADR is 0x200 | YY0U (YY Bit 15/14 address) */ outb(0x00, cs->hw.hfcD.addr); outb(0x56, cs->hw.hfcD.addr | 1); } else if (cs->typ == ISDN_CTYPE_ACERP10) { /* Acer P10 IO ADR is 0x300 */ outb(0x00, cs->hw.hfcD.addr); outb(0x57, cs->hw.hfcD.addr | 1); } set_cs_func(cs); cs->hw.hfcD.timer.function = (void *) hfcs_Timer; cs->hw.hfcD.timer.data = (long) cs; init_timer(&cs->hw.hfcD.timer); cs->cardmsg = &hfcs_card_msg; cs->irq_func = &hfcs_interrupt; return (1); }
void pci_config_write8(uint32_t pci_addr, uint8_t reg, uint8_t data) { pci_addr |= reg & ~3; outl(PCI_ADDR_REG, pci_addr); outb(PCI_DATA_REG + (reg & 3), data); }
unsigned short w_EepromReadWord(unsigned short w_PCIBoardEepromAddress, char *pc_PCIChipInformation, unsigned short w_EepromStartAddress) { unsigned char b_Counter = 0; unsigned char b_ReadByte = 0; unsigned char b_ReadLowByte = 0; unsigned char b_ReadHighByte = 0; unsigned char b_SelectedAddressLow = 0; unsigned char b_SelectedAddressHigh = 0; unsigned short w_ReadWord = 0; /**************************/ /* Test the PCI chip type */ /**************************/ if ((!strcmp(pc_PCIChipInformation, "S5920")) || (!strcmp(pc_PCIChipInformation, "S5933"))) { for (b_Counter = 0; b_Counter < 2; b_Counter++) { b_SelectedAddressLow = (w_EepromStartAddress + b_Counter) % 256; /* Read the low 8 bit part */ b_SelectedAddressHigh = (w_EepromStartAddress + b_Counter) / 256; /* Read the high 8 bit part */ /************************************/ /* Select the load low address mode */ /************************************/ outb(NVCMD_LOAD_LOW, w_PCIBoardEepromAddress + 0x3F); /****************/ /* Wait on busy */ /****************/ v_EepromWaitBusy(w_PCIBoardEepromAddress); /************************/ /* Load the low address */ /************************/ outb(b_SelectedAddressLow, w_PCIBoardEepromAddress + 0x3E); /****************/ /* Wait on busy */ /****************/ v_EepromWaitBusy(w_PCIBoardEepromAddress); /*************************************/ /* Select the load high address mode */ /*************************************/ outb(NVCMD_LOAD_HIGH, w_PCIBoardEepromAddress + 0x3F); /****************/ /* Wait on busy */ /****************/ v_EepromWaitBusy(w_PCIBoardEepromAddress); /*************************/ /* Load the high address */ /*************************/ outb(b_SelectedAddressHigh, w_PCIBoardEepromAddress + 0x3E); /****************/ /* Wait on busy */ /****************/ v_EepromWaitBusy(w_PCIBoardEepromAddress); /************************/ /* Select the READ mode */ /************************/ outb(NVCMD_BEGIN_READ, w_PCIBoardEepromAddress + 0x3F); /****************/ /* Wait on busy */ /****************/ v_EepromWaitBusy(w_PCIBoardEepromAddress); /*****************************/ /* Read data into the EEPROM */ /*****************************/ b_ReadByte = inb(w_PCIBoardEepromAddress + 0x3E); /****************/ /* Wait on busy */ /****************/ v_EepromWaitBusy(w_PCIBoardEepromAddress); /*********************************/ /* Select the upper address part */ /*********************************/ if (b_Counter == 0) { b_ReadLowByte = b_ReadByte; } /* if(b_Counter==0) */ else { b_ReadHighByte = b_ReadByte; } /* if(b_Counter==0) */ } /* for (b_Counter=0; b_Counter<2; b_Counter++) */ w_ReadWord = (b_ReadLowByte | (((unsigned short) b_ReadHighByte) * 256)); } /* end of if ((!strcmp(pc_PCIChipInformation, "S5920")) || (!strcmp(pc_PCIChipInformation, "S5933"))) */ if (!strcmp(pc_PCIChipInformation, "93C76")) { /*************************************/ /* Read 16 bit from the EEPROM 93C76 */ /*************************************/ v_EepromCs76Read(w_PCIBoardEepromAddress, w_EepromStartAddress, &w_ReadWord); } return w_ReadWord; }
static int __devinit EvtDevicePrepareHardware(struct pci_dev *pdev, const struct pci_device_id *ent) { int ret = -EIO; u16 subVender; u16 uModel; //will contain galil model number (e.g. 0x1802) long resourceStart, resourceEnd, resourceLen; char name[10]; //will contain name in /dev/ (e.g. galilpci0) device++; //incremented each time be find a galil board if (device >= MAX_DEVICES) { printk("EvtDevicePrepareHardware This driver only supports %i devices\n", MAX_DEVICES); device--; //update the device count return -ENODEV; } if (pci_enable_device(pdev)) { //wake up the device printk("EvtDevicePrepareHardware Not possible to enable PCI Device\n"); device--; //update the device count return -ENODEV; } //Mark all PCI regions associated with PCI device pdev as being reserved by owner res_name. //Do not access any address inside the PCI regions unless this call returns successfully. //This will reverve both i/o ports and memory regions, and shows up in /proc/ioports and /proc/iomem if (pci_request_regions(pdev, GALILNAME)) { printk("EvtDevicePrepareHardware I/O address (0x%04x) already in use\n", (unsigned int) d[device].baseAddress); device--; //update the device count return -ENODEV; } //I/O ADDRESSES d[device].baseAddress = pci_resource_start(pdev, /*BAR*/ 2); //get base address N from BAR2 d[device].irqAddress = pci_resource_start(pdev, /*BAR*/ 1); //get base address N from BAR2. 0 on 18x0/2. Non zero on 18x2. resourceStart = pci_resource_start(pdev, /*BAR*/ 0); if ( resourceStart == 0x0000 ) { printk("No data record channel\n"); resourceEnd = 0; resourceLen = 0; } else { resourceEnd = pci_resource_end(pdev, /*BAR*/ 0); resourceLen = resourceEnd-resourceStart+1; } printk("Bar 0 address: 0x%X to 0x%X, len=%i\n", resourceStart, resourceEnd, resourceLen); if ( resourceLen == 128 ) { // d[device].recordAddress = pci_iomap(pdev, /*BAR*/ 0, 128 /*bytes*/); d[device].pciInfo.m_fRevsion = REV1; // PLX based device d[device].recordAddress = resourceStart; printk("PLX based controller - not supported\n"); device--; //update the device count return -ENODEV; } else if ( resourceLen == 512 ) { d[device].pciInfo.m_fRevsion = REV2; // XININX based device d[device].recordAddress = pci_iomap(pdev, 0, 512); printk("XINUNX based controller\n"); } if (d[device].baseAddress == 0x0000) { //check base address. printk("EvtDevicePrepareHardware No Main I/O-Address for device detected\n"); ret = -ENODEV; goto release_regions; } printk("Base Address: 0x%X\n", d[device].baseAddress); printk("Record Address: 0x%X\n", d[device].recordAddress); // Identify PCI device pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &subVender); //read galil model (e.g. 0x1806) pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &uModel); //read galil model (e.g. 0x1806) d[device].pciInfo.m_fSubVendor = subVender; d[device].pciInfo.m_fSubSystem = uModel; //register the device under major number 10. Minor number will show up in /proc/misc d[device].galilpci_miscdev.minor = MISC_DYNAMIC_MINOR, //assign the minor number dynamically (ask for a free one). This field will eventually contain the actual minor number. sprintf(name, GALILNAME "%i", device); d[device].galilpci_miscdev.name = name, //the name for this device, meant for human consumption: users will find the name in the /proc/misc file. d[device].galilpci_miscdev.fops = &galilpci_fops, //the file operations d[device].bOpen = 0; //file not open ret = misc_register(&(d[device].galilpci_miscdev)); if (ret) { printk ("EvtDevicePrepareHardware cannot register miscdev (err=%d)\n", ret); goto release_regions; } init_waitqueue_head(&d[device].sleepQ); //INTERRUPTS d[device].interrupt = pdev->irq; //store the IRQ number so we can call free-irq in the cleanup function when the module us removed with rmmod if (request_irq(d[device].interrupt, EvtInterruptIsr, IRQF_SHARED, GALILNAME, &d[device])) //register the interrupt handler. This should happen before we enable interrupts on the controller. Kernel 2.6.18 2006Sep changed SA_SHIRQ to IRQF_SHARED http://lwn.net/Articles/2.6-kernel-api/ { printk("EvtDevicePrepareHardware IRQ %x is not free\n", d[device].interrupt); goto misc_deregister; } init_waitqueue_head(&d[device].interruptWaitQ); //initialize the list of processes waiting for EI/UI interrupts d[device].interruptQ.in = d[device].interruptQ.out = 0; //initialize circular buffer pointers init_waitqueue_head(&d[device].recordWaitQ); //initialize the list of processes waiting for record interrupts d[device].recordQ.in = d[device].recordQ.out = 0; //initialize circular buffer pointers //ENABLE INTERRUPTS ON CONTROLLER #if 1 switch (d[device].pciInfo.m_fSubSystem) { case DMC18x2: inb( d[device].baseAddress + 1); //reset 4701 ("clear FIFOs"). Interrupts won't enable on 18x2 rev F without this (rev D1 doesn't require it) outb(0x01, d[device].baseAddress + 1); outb(0x80, d[device].baseAddress + 1); outb(0x01, d[device].baseAddress + 1); outb(0x80, d[device].baseAddress + 1); inb( d[device].baseAddress + 1); outb(0x02, d[device].baseAddress + 1); //set 4701 pointer register to point to the interrupt mask register (register 2) outb(0x04, d[device].baseAddress + 1); //interrupt when the controller writes to its mailbox (don't interrupt on 6 full, 5 empty, 4 almost full, 3 almost empty, 1 byte detect, nor 0 parity/frame error //below is necessary so 18x2 doesn't keep interrupting outb(0x06, d[device].baseAddress + 1); //set 4701 pointer register to point to the other port's mailbox inb ( d[device].baseAddress + 1); //read other port's mailbox (clears any pending mailbox interrupt) d[device].irqAddress += 0x4c; //76 byte offset from the address in BAR1 outl( 0x40 | inl(d[device].irqAddress), d[device].irqAddress); //enable interrupts on the controller. They will stay enabled until the module is unloaded (or power is shut off) break; case DMC18x6: case DMC18x0: outb(0x40 | inb(d[device].baseAddress + 4), d[device].baseAddress + 4); //enable interrupts on the controller. They will stay enabled until the module is unloaded (or power is shut off) break; case DMC1417: d[device].irqAddress += 0x4c; outb(0x40 | inb(d[device].irqAddress), d[device].irqAddress); //enable interrupts on the controller. They will stay enabled until the module is unloaded (or power is shut off) break; case DMC1640: default: ; }; #else if(d[device].irqAddress) //18x2 { inb( d[device].baseAddress + 1); //reset 4701 ("clear FIFOs"). Interrupts won't enable on 18x2 rev F without this (rev D1 doesn't require it) outb(0x01, d[device].baseAddress + 1); outb(0x80, d[device].baseAddress + 1); outb(0x01, d[device].baseAddress + 1); outb(0x80, d[device].baseAddress + 1); inb( d[device].baseAddress + 1); outb(0x02, d[device].baseAddress + 1); //set 4701 pointer register to point to the interrupt mask register (register 2) outb(0x04, d[device].baseAddress + 1); //interrupt when the controller writes to its mailbox (don't interrupt on 6 full, 5 empty, 4 almost full, 3 almost empty, 1 byte detect, nor 0 parity/frame error //below is necessary so 18x2 doesn't keep interrupting outb(0x06, d[device].baseAddress + 1); //set 4701 pointer register to point to the other port's mailbox inb ( d[device].baseAddress + 1); //read other port's mailbox (clears any pending mailbox interrupt) d[device].irqAddress += 0x4c; //76 byte offset from the address in BAR1 outl( 0x40 | inl(d[device].irqAddress), d[device].irqAddress); //enable interrupts on the controller. They will stay enabled until the module is unloaded (or power is shut off) } else //18x6 outb(0x40 | inb(d[device].baseAddress + 4), d[device].baseAddress + 4); //enable interrupts on the controller. They will stay enabled until the module is unloaded (or power is shut off) #endif printk("EvtDevicePrepareHardware I/O address (0x%lx), Model %x, minor number %d Rev(%i)\n", d[device].baseAddress, uModel, d[device].galilpci_miscdev.minor, d[device].pciInfo.m_fRevsion); return 0; misc_deregister: misc_deregister(&(d[device].galilpci_miscdev)); //unregister the device with major number 10 release_regions: pci_release_regions(pdev); disable_device: pci_disable_device(pdev); device--; //update the device count return ret; }
//___________________________________________________________________________________________ static irqreturn_t EvtInterruptIsr(int irq, void *dev_id) // struct pt_regs *regs) //Kernel 2.6.19 removed regs argument http://lwn.net/Articles/202449/ { unsigned char flagByte; unsigned char statusByte; DeviceContext *d = dev_id; //get a pointer to the device. This was passed to request_irq() int dataRecordSize; #if 1 switch(d->pciInfo.m_fSubSystem) { case DMC18x2: if(0x45 == (inl(d->irqAddress) & 0x45)) //this is our interrupt (bit 0 local interrupts enabled, bit 2 status (our)s, bit 6 enable (set below)) { unsigned char status = inb(d->baseAddress + 1); //read 4701 status register (clear 4701 IRQ flag) if(status & 0x04) //bit 2 means mailbox interrupt (controller sending status byte to PC) { outb(0x06, d->baseAddress + 1); //write 6 to N+1 (set 4701 pointer register to point to other port's mailbox) statusByte = inb(d->baseAddress + 1); //read status byte (4701 other port's mailbox) } else //controller probably set MR bit (bit 7) of 4701 (by hard resetting controller) { outb(0x02, d->baseAddress + 1); //set 4701 pointer register to point to the interrupt mask register (register 2) outb(0x04, d->baseAddress + 1); //interrupt when the controller writes to its mailbox (don't interrupt on 6 full, 5 empty, 4 almost full, 3 almost empty, 1 byte detect, nor 0 parity/frame error //below is necessary so 18x2 doesn't keep interrupting outb(0x06, d->baseAddress + 1); //set 4701 pointer register to point to the other port's mailbox inb(d->baseAddress + 1); //read other port's mailbox (clears any pending mailbox interrupt) return IRQ_HANDLED; //Tell WDF, and hence Windows, that there WAS an interrupt outstanding for us to process } } else return IRQ_NONE; break; case DMC18x6: if(0x60 == (inb(d->baseAddress + 4) & 0x60)) //if the Galil controller generated an interrupt and interrupts were enabled on the controller { statusByte = inb(d->baseAddress + 8); //read status byte (must happen before acknowledging the interrupt below) outb(0x20 | inb(d->baseAddress + 4), d->baseAddress + 4); //acknowledge the interrupt (else the controller will keep interrupting) } else return IRQ_NONE; break; case DMC1640: case DMC18x0: flagByte = inb(d->baseAddress + 4); if ( d->pciInfo.m_fRevsion == REV1 ) { if(0x60 == (flagByte & 0x60)) //if the Galil controller generated an interrupt and interrupts were enabled on the controller { statusByte = inb(d->baseAddress + 8); //read status byte (must happen before acknowledging the interrupt below) outb(flagByte, d->baseAddress + 4); //acknowledge the interrupt (else the controller will keep interrupting) } else return IRQ_NONE; } else { if(0x60 == (flagByte & 0x60)) //if the Galil controller generated an interrupt and interrupts were enabled on the controller { statusByte = inb(d->baseAddress + 8); //read status byte (must happen before acknowledging the interrupt below) outb(0x20 | flagByte, d->baseAddress + 4); //acknowledge the interrupt (else the controller will keep interrupting) } else return IRQ_NONE; } break; case DMC1417: if(0x45 == (inb(d->irqAddress) & 0x45)) { statusByte = 0xFF; // 1417 interrupt need to be read via IV command outb(0x05, d->irqAddress + 1); // stop Interrupt } else return IRQ_NONE; break; default: ; }; #else if(d->irqAddress) //18x2 { if(0x45 == (inl(d->irqAddress) & 0x45)) //this is our interrupt (bit 0 local interrupts enabled, bit 2 status (our)s, bit 6 enable (set below)) { unsigned char status = inb(d->baseAddress + 1); //read 4701 status register (clear 4701 IRQ flag) if(status & 0x04) //bit 2 means mailbox interrupt (controller sending status byte to PC) { outb(0x06, d->baseAddress + 1); //write 6 to N+1 (set 4701 pointer register to point to other port's mailbox) statusByte = inb(d->baseAddress + 1); //read status byte (4701 other port's mailbox) goto DPC; } else //controller probably set MR bit (bit 7) of 4701 (by hard resetting controller) { outb(0x02, d->baseAddress + 1); //set 4701 pointer register to point to the interrupt mask register (register 2) outb(0x04, d->baseAddress + 1); //interrupt when the controller writes to its mailbox (don't interrupt on 6 full, 5 empty, 4 almost full, 3 almost empty, 1 byte detect, nor 0 parity/frame error //below is necessary so 18x2 doesn't keep interrupting outb(0x06, d->baseAddress + 1); //set 4701 pointer register to point to the other port's mailbox inb(d->baseAddress + 1); //read other port's mailbox (clears any pending mailbox interrupt) } return IRQ_HANDLED; //Tell WDF, and hence Windows, that there WAS an interrupt outstanding for us to process } } else //18x6 { if(0x60 == (inb(d->baseAddress + 4) & 0x60)) //if the Galil controller generated an interrupt and interrupts were enabled on the controller { statusByte = inb(d->baseAddress + 8); //read status byte (must happen before acknowledging the interrupt below) outb(0x20 | inb(d->baseAddress + 4), d->baseAddress + 4); //acknowledge the interrupt (else the controller will keep interrupting) goto DPC; } } return IRQ_NONE; //not our interrupt, another device sharing the IRQ generated the interrupt DPC: #endif // printk("EvtInterruptIsr 0x%X\n", statusByte); switch(statusByte) { case 0xBA: break; //command done (colon sent) case 0xBB: break; //MG case 0xBC: //record if( (d->recordAddress != 0) && (d->recordQ.out != (d->recordQ.in + 1) % SIZE) ) //circular buffer NOT FULL (will lose new status bytes if full--old ones are retained) { if ( d->pciInfo.m_fRevsion == REV2 ) memcpy_fromio(d->recordQ.q[ d->recordQ.in ], (void __iomem *)d->recordAddress, 512); //copy 512 bytes from the dual port RAM else { dataRecordSize = 0; memset(d->recordQ.q[ d->recordQ.in ], 0, sizeof(d->recordQ.q[ d->recordQ.in ])); while ( (0x00 == (inb(d->baseAddress+4) & 0x80)) && dataRecordSize < 512 ) { // printk(KERN_INFO "DR read flag[%X]\n", inb(d->baseAddress+4)); d->recordQ.q[ d->recordQ.in ][dataRecordSize++] = inb(d->baseAddress+0xC); } printk("PLX data record size = %i\n", dataRecordSize); } d->recordQ.in = (d->recordQ.in + 1) % SIZE; //increment in (write) pointer wake_up_interruptible(&(d->recordWaitQ)); //wake up ioctl } //else FULL break; default: //EI (0xCx, Dx, Ex) and UI (0xf0 to ff) //CACHE the status byte for later retreival the next time ioctl is called from user mode if( d->interruptQ.out != (d->interruptQ.in + 1) % SIZE ) //circular buffer NOT FULL (will lose new status bytes if full--old ones are retained) { d->interruptQ.q[ d->interruptQ.in ] = statusByte; //store status byte read above d->interruptQ.in = (d->interruptQ.in + 1) % SIZE; //increment in (write) pointer wake_up_interruptible(&(d->interruptWaitQ)); //wake up ioctl } //else FULL break; }//switch return IRQ_HANDLED; }
//----------------------------------------------------------------------------- void pic_eoi(void) { outb(PIC1_CMD, PIC_EOI); outb(PIC2_CMD, PIC_EOI); }
static long sp5100_tco_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int new_options, retval = -EINVAL; int new_heartbeat; void __user *argp = (void __user *)arg; int __user *p = argp; static const struct watchdog_info ident = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .firmware_version = 0, .identity = TCO_MODULE_NAME, }; switch (cmd) { case WDIOC_GETSUPPORT: return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: return put_user(0, p); case WDIOC_SETOPTIONS: if (get_user(new_options, p)) return -EFAULT; if (new_options & WDIOS_DISABLECARD) { tco_timer_stop(); retval = 0; } if (new_options & WDIOS_ENABLECARD) { tco_timer_start(); tco_timer_keepalive(); retval = 0; } return retval; case WDIOC_KEEPALIVE: tco_timer_keepalive(); return 0; case WDIOC_SETTIMEOUT: if (get_user(new_heartbeat, p)) return -EFAULT; if (tco_timer_set_heartbeat(new_heartbeat)) return -EINVAL; tco_timer_keepalive(); /* Fall through */ case WDIOC_GETTIMEOUT: return put_user(heartbeat, p); default: return -ENOTTY; } } /* * Kernel Interfaces */ static const struct file_operations sp5100_tco_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .write = sp5100_tco_write, .unlocked_ioctl = sp5100_tco_ioctl, .open = sp5100_tco_open, .release = sp5100_tco_release, }; static struct miscdevice sp5100_tco_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &sp5100_tco_fops, }; /* * Data for PCI driver interface * * This data only exists for exporting the supported * PCI ids via MODULE_DEVICE_TABLE. We do not actually * register a pci_driver, because someone else might * want to register another driver on the same PCI id. */ static DEFINE_PCI_DEVICE_TABLE(sp5100_tco_pci_tbl) = { { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID, PCI_ANY_ID, }, { 0, }, /* End of list */ }; MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl); /* * Init & exit routines */ static unsigned char __devinit sp5100_tco_setupdevice(void) { struct pci_dev *dev = NULL; u32 val; /* Match the PCI device */ for_each_pci_dev(dev) { if (pci_match_id(sp5100_tco_pci_tbl, dev) != NULL) { sp5100_tco_pci = dev; break; } } if (!sp5100_tco_pci) return 0; /* Request the IO ports used by this driver */ pm_iobase = SP5100_IO_PM_INDEX_REG; if (!request_region(pm_iobase, SP5100_PM_IOPORTS_SIZE, "SP5100 TCO")) { printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", pm_iobase); goto exit; } /* Find the watchdog base address. */ outb(SP5100_PM_WATCHDOG_BASE3, SP5100_IO_PM_INDEX_REG); val = inb(SP5100_IO_PM_DATA_REG); outb(SP5100_PM_WATCHDOG_BASE2, SP5100_IO_PM_INDEX_REG); val = val << 8 | inb(SP5100_IO_PM_DATA_REG); outb(SP5100_PM_WATCHDOG_BASE1, SP5100_IO_PM_INDEX_REG); val = val << 8 | inb(SP5100_IO_PM_DATA_REG); outb(SP5100_PM_WATCHDOG_BASE0, SP5100_IO_PM_INDEX_REG); /* Low three bits of BASE0 are reserved. */ val = val << 8 | (inb(SP5100_IO_PM_DATA_REG) & 0xf8); if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, "SP5100 TCO")) { printk(KERN_ERR PFX "mmio address 0x%04x already in use\n", val); goto unreg_region; } tcobase_phys = val; tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE); if (tcobase == 0) { printk(KERN_ERR PFX "failed to get tcobase address\n"); goto unreg_mem_region; } /* Enable watchdog decode bit */ pci_read_config_dword(sp5100_tco_pci, SP5100_PCI_WATCHDOG_MISC_REG, &val); val |= SP5100_PCI_WATCHDOG_DECODE_EN; pci_write_config_dword(sp5100_tco_pci, SP5100_PCI_WATCHDOG_MISC_REG, val); /* Enable Watchdog timer and set the resolution to 1 sec. */ outb(SP5100_PM_WATCHDOG_CONTROL, SP5100_IO_PM_INDEX_REG); val = inb(SP5100_IO_PM_DATA_REG); val |= SP5100_PM_WATCHDOG_SECOND_RES; val &= ~SP5100_PM_WATCHDOG_DISABLE; outb(val, SP5100_IO_PM_DATA_REG); /* Check that the watchdog action is set to reset the system. */ val = readl(SP5100_WDT_CONTROL(tcobase)); val &= ~SP5100_PM_WATCHDOG_ACTION_RESET; writel(val, SP5100_WDT_CONTROL(tcobase)); /* Set a reasonable heartbeat before we stop the timer */ tco_timer_set_heartbeat(heartbeat); /* * Stop the TCO before we change anything so we don't race with * a zeroed timer. */ tco_timer_stop(); /* Done */ return 1; unreg_mem_region: release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); unreg_region: release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); exit: return 0; } static int __devinit sp5100_tco_init(struct platform_device *dev) { int ret; u32 val; /* Check whether or not the hardware watchdog is there. If found, then * set it up. */ if (!sp5100_tco_setupdevice()) return -ENODEV; /* Check to see if last reboot was due to watchdog timeout */ printk(KERN_INFO PFX "Watchdog reboot %sdetected.\n", readl(SP5100_WDT_CONTROL(tcobase)) & SP5100_PM_WATCHDOG_FIRED ? "" : "not "); /* Clear out the old status */ val = readl(SP5100_WDT_CONTROL(tcobase)); val &= ~SP5100_PM_WATCHDOG_FIRED; writel(val, SP5100_WDT_CONTROL(tcobase)); /* * Check that the heartbeat value is within it's range. * If not, reset to the default. */ if (tco_timer_set_heartbeat(heartbeat)) { heartbeat = WATCHDOG_HEARTBEAT; tco_timer_set_heartbeat(heartbeat); } ret = misc_register(&sp5100_tco_miscdev); if (ret != 0) { printk(KERN_ERR PFX "cannot register miscdev on minor=" "%d (err=%d)\n", WATCHDOG_MINOR, ret); goto exit; } clear_bit(0, &timer_alive); printk(KERN_INFO PFX "initialized (0x%p). heartbeat=%d sec" " (nowayout=%d)\n", tcobase, heartbeat, nowayout); return 0; exit: iounmap(tcobase); release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); return ret; } static void __devexit sp5100_tco_cleanup(void) { /* Stop the timer before we leave */ if (!nowayout) tco_timer_stop(); /* Deregister */ misc_deregister(&sp5100_tco_miscdev); iounmap(tcobase); release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); } static int __devexit sp5100_tco_remove(struct platform_device *dev) { if (tcobase) sp5100_tco_cleanup(); return 0; } static void sp5100_tco_shutdown(struct platform_device *dev) { tco_timer_stop(); } static struct platform_driver sp5100_tco_driver = { .probe = sp5100_tco_init, .remove = __devexit_p(sp5100_tco_remove), .shutdown = sp5100_tco_shutdown, .driver = { .owner = THIS_MODULE, .name = TCO_MODULE_NAME, }, }; static int __init sp5100_tco_init_module(void) { int err; printk(KERN_INFO PFX "SP5100 TCO WatchDog Timer Driver v%s\n", TCO_VERSION); err = platform_driver_register(&sp5100_tco_driver); if (err) return err; sp5100_tco_platform_device = platform_device_register_simple( TCO_MODULE_NAME, -1, NULL, 0); if (IS_ERR(sp5100_tco_platform_device)) { err = PTR_ERR(sp5100_tco_platform_device); goto unreg_platform_driver; } return 0; unreg_platform_driver: platform_driver_unregister(&sp5100_tco_driver); return err; }
/*===========================================================================* * intr_init * *===========================================================================*/ PUBLIC int intr_init(const int mine, const int auto_eoi) { /* Initialize the 8259s, finishing with all interrupts disabled. This is * only done in protected mode, in real mode we don't touch the 8259s, but * use the BIOS locations instead. The flag "mine" is set if the 8259s are * to be programmed for MINIX, or to be reset to what the BIOS expects. */ /* The AT and newer PS/2 have two interrupt controllers, one master, * one slaved at IRQ 2. (We don't have to deal with the PC that * has just one controller, because it must run in real mode.) */ outb( INT_CTL, machine.ps_mca ? ICW1_PS : ICW1_AT); outb( INT_CTLMASK, mine == INTS_MINIX ? IRQ0_VECTOR : BIOS_IRQ0_VEC); /* ICW2 for master */ outb( INT_CTLMASK, (1 << CASCADE_IRQ)); /* ICW3 tells slaves */ if (auto_eoi) outb( INT_CTLMASK, ICW4_AT_AEOI_MASTER); else outb( INT_CTLMASK, ICW4_AT_MASTER); outb( INT_CTLMASK, ~(1 << CASCADE_IRQ)); /* IRQ 0-7 mask */ outb( INT2_CTL, machine.ps_mca ? ICW1_PS : ICW1_AT); outb( INT2_CTLMASK, mine == INTS_MINIX ? IRQ8_VECTOR : BIOS_IRQ8_VEC); /* ICW2 for slave */ outb( INT2_CTLMASK, CASCADE_IRQ); /* ICW3 is slave nr */ if (auto_eoi) outb( INT2_CTLMASK, ICW4_AT_AEOI_SLAVE); else outb( INT2_CTLMASK, ICW4_AT_SLAVE); outb( INT2_CTLMASK, ~0); /* IRQ 8-15 mask */ /* Copy the BIOS vectors from the BIOS to the Minix location, so we * can still make BIOS calls without reprogramming the i8259s. */ #if IRQ0_VECTOR != BIOS_IRQ0_VEC phys_copy(BIOS_VECTOR(0) * 4L, VECTOR(0) * 4L, 8 * 4L); #endif #if IRQ8_VECTOR != BIOS_IRQ8_VEC phys_copy(BIOS_VECTOR(8) * 4L, VECTOR(8) * 4L, 8 * 4L); #endif return OK; }
static inline void xoutb(unsigned char val, unsigned short port) { pr_debug("outb(val=%.2x,port=%.4x)\n", val, port); outb(val, port); }
/* Disable 8259 - write 0xFF in OCW1 master and slave. */ PUBLIC void i8259_disable(void) { outb(INT2_CTLMASK, 0xFF); outb(INT_CTLMASK, 0xFF); inb(INT_CTLMASK); }
static void fdc_init_dma_read() { outb(0x0a, 0x06); outb(0x0b, 0x56); outb(0x0a, 0x02); }
static void cgaregw(int index, int data) { outb(0x3D4, index); outb(0x3D4+1, data); }
static void fdc_init_dma_write() { outb(0x0a, 0x06); outb(0x0b, 0x5a); outb(0x0a, 0x02); }
static void tcic_setb(u_char reg, u_char data) { printk(KERN_DEBUG "tcic_setb(%#lx, %#x)\n", tcic_base+reg, data); outb(data, tcic_base+reg); }
static void fdc_start_motor0() { outb(FDC_DOR, FDC_DOR_MASK_DRIVE0_MOTOR | FDC_DOR_MASK_RESET); }
static inline void Write_hfc8(hfc4s8s_hw *a, u_char b, u_char c) { SetRegAddr(a, b); outb(c, a->iobase); }
static void fdc_stop_motor0() { outb(FDC_DOR, FDC_DOR_MASK_RESET); }
void smm_init() { if (CONFIG_COREBOOT) // SMM only supported on emulators. return; if (!CONFIG_USE_SMM) return; dprintf(3, "init smm\n"); // This code is hardcoded for PIIX4 Power Management device. int bdf = pci_find_device(PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_82371AB_3); if (bdf < 0) // Device not found return; int i440_bdf = pci_find_device(PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_82441); if (i440_bdf < 0) return; /* check if SMM init is already done */ u32 value = pci_config_readl(bdf, 0x58); if (value & (1 << 25)) return; /* enable the SMM memory window */ pci_config_writeb(i440_bdf, 0x72, 0x02 | 0x48); /* save original memory content */ memcpy((void *)BUILD_SMM_ADDR, (void *)BUILD_SMM_INIT_ADDR, BUILD_SMM_SIZE); /* copy the SMM relocation code */ memcpy((void *)BUILD_SMM_INIT_ADDR, &smm_relocation_start, &smm_relocation_end - &smm_relocation_start); /* enable SMI generation when writing to the APMC register */ pci_config_writel(bdf, 0x58, value | (1 << 25)); /* init APM status port */ outb(0x01, PORT_SMI_STATUS); /* raise an SMI interrupt */ outb(0x00, PORT_SMI_CMD); /* wait until SMM code executed */ while (inb(PORT_SMI_STATUS) != 0x00) ; /* restore original memory content */ memcpy((void *)BUILD_SMM_INIT_ADDR, (void *)BUILD_SMM_ADDR, BUILD_SMM_SIZE); /* copy the SMM code */ memcpy((void *)BUILD_SMM_ADDR, &smm_code_start , &smm_code_end - &smm_code_start); wbinvd(); /* close the SMM memory window and enable normal SMM */ pci_config_writeb(i440_bdf, 0x72, 0x02 | 0x08); }
//----------------------------------------------------------------------------- void pic_init(u16 off1, u16 off2) { /* master PIC */ outb(PIC1_CMD, 0x11); outb(PIC1_DAT, off1); outb(PIC1_DAT, 0x4); outb(PIC1_DAT, 0x1); outb(PIC1_CMD, 0xB); /* slave PIC */ outb(PIC2_CMD, 0x11); outb(PIC2_DAT, off2); outb(PIC2_DAT, 0x2); outb(PIC2_DAT, 0xB); outb(PIC2_CMD, 0xB); /* mask all interrupts */ outb(PIC2_DAT, 0xFF); outb(PIC1_DAT, 0xFF); }
void irq_mask_all() { outb(PIC1_DATA, 0xFF); outb(PIC2_DATA, 0xFF); }
status_t ps2_ioctl( void* pNode, void* pCookie, uint32 nCommand, void* pArgs, bool bFromKernel ) { PS2_Port_s* psPort = (PS2_Port_s*)pNode; uint32 nFlg; if( psPort->bIsAux ) return( 0 ); switch( nCommand ) { case IOCTL_KBD_LEDRST: g_nKbdLedStatus=0; break; case IOCTL_KBD_SCRLOC: g_nKbdLedStatus ^= 0x01; break; case IOCTL_KBD_NUMLOC: g_nKbdLedStatus ^= 0x02; break; case IOCTL_KBD_CAPLOC: g_nKbdLedStatus ^= 0x04; break; default: printk( "PS2: Unknown IOCTL %x\n",(int)nCommand ); break; } /* Write command */ psPort->nAckReceived = 0; nFlg = spinlock_disable( &g_sLock ); ps2_wait_write(); outb( PS2_CMD_KBD_SETLEDS, PS2_DATA_REG ); spinunlock_enable( &g_sLock, nFlg ); int i = 0; while( psPort->nAckReceived == 0 && i < 200 ) { snooze( 1000 ); i++; } if( psPort->nAckReceived == -1 ) { printk( "Could not set LED status: Hardware reported an error for the command!\n" ); return( 0 ); } if( i == 200 ) { printk( "Could not set LED status: Timeout!\n" ); return( 0 ); } psPort->nAckReceived = 0; nFlg = spinlock_disable( &g_sLock ); ps2_wait_write(); outb( g_nKbdLedStatus, PS2_DATA_REG ); spinunlock_enable( &g_sLock, nFlg ); i = 0; while( psPort->nAckReceived == 0 && i < 200 ) { snooze( 1000 ); i++; } if( psPort->nAckReceived == -1 ) { printk( "Could not set LED status: Hardware reported an error for the data!\n" ); return( 0 ); } if( i == 200 ) { printk( "Could not set LED status: Timeout!\n" ); return( 0 ); } return( 0 ); }