📄 smsc-ircc2.c
字号:
IRDA_DEBUG(3, "%s\n", __FUNCTION__); iobase = self->io.sir_base; /* Reset UART */ outb(0, iobase+UART_MCR); /* Turn off interrupts */ outb(0, iobase+UART_IER);}#endif/* * Function smsc_sir_write_wakeup (self) * * Called by the SIR interrupt handler when there's room for more data. * If we have more packets to send, we send them here. * */static void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self){ int actual = 0; int iobase; int fcr; ASSERT(self != NULL, return;); IRDA_DEBUG(4, "%s\n", __FUNCTION__); iobase = self->io.sir_base; /* Finished with frame? */ if (self->tx_buff.len > 0) { /* Write data left in transmit buffer */ actual = smsc_ircc_sir_write(iobase, self->io.fifo_size, self->tx_buff.data, self->tx_buff.len); self->tx_buff.data += actual; self->tx_buff.len -= actual; } else { /*if (self->tx_buff.len ==0) {*/ /* * Now serial buffer is almost free & we can start * transmission of another packet. But first we must check * if we need to change the speed of the hardware */ if (self->new_speed) { IRDA_DEBUG(5, "%s(), Changing speed to %d.\n", __FUNCTION__, self->new_speed); smsc_ircc_sir_wait_hw_transmitter_finish(self); smsc_ircc_change_speed(self, self->new_speed); self->new_speed = 0; } else { /* Tell network layer that we want more frames */ netif_wake_queue(self->netdev); } self->stats.tx_packets++; if(self->io.speed <= 115200) { /* * Reset Rx FIFO to make sure that all reflected transmit data * is discarded. This is needed for half duplex operation */ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR; if (self->io.speed < 38400) fcr |= UART_FCR_TRIGGER_1; else fcr |= UART_FCR_TRIGGER_14; outb(fcr, iobase+UART_FCR); /* Turn on receive interrupts */ outb(UART_IER_RDI, iobase+UART_IER); } }}/* * Function smsc_ircc_sir_write (iobase, fifo_size, buf, len) * * Fill Tx FIFO with transmit data * */static int smsc_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len){ int actual = 0; /* Tx FIFO should be empty! */ if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) { WARNING("%s(), failed, fifo not empty!\n", __FUNCTION__); return 0; } /* Fill FIFO with current frame */ while ((fifo_size-- > 0) && (actual < len)) { /* Transmit next byte */ outb(buf[actual], iobase+UART_TX); actual++; } return actual;}/* * Function smsc_ircc_is_receiving (self) * * Returns true is we are currently receiving data * */static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self){ return (self->rx_buff.state != OUTSIDE_FRAME);}/* * Function smsc_ircc_probe_transceiver(self) * * Tries to find the used Transceiver * */static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self){ unsigned int i; ASSERT(self != NULL, return;); for(i=0; smsc_transceivers[i].name!=NULL; i++) if((*smsc_transceivers[i].probe)(self->io.fir_base)) { MESSAGE(" %s transceiver found\n", smsc_transceivers[i].name); self->transceiver= i+1; return; } MESSAGE("No transceiver found. Defaulting to %s\n", smsc_transceivers[SMSC_IRCC2_C_DEFAULT_TRANSCEIVER].name); self->transceiver= SMSC_IRCC2_C_DEFAULT_TRANSCEIVER;}/* * Function smsc_ircc_set_transceiver_for_speed(self, speed) * * Set the transceiver according to the speed * */static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed){ unsigned int trx; trx = self->transceiver; if(trx>0) (*smsc_transceivers[trx-1].set_for_speed)(self->io.fir_base, speed);}/* * Function smsc_ircc_wait_hw_transmitter_finish () * * Wait for the real end of HW transmission * * The UART is a strict FIFO, and we get called only when we have finished * pushing data to the FIFO, so the maximum amount of time we must wait * is only for the FIFO to drain out. * * We use a simple calibrated loop. We may need to adjust the loop * delay (udelay) to balance I/O traffic and latency. And we also need to * adjust the maximum timeout. * It would probably be better to wait for the proper interrupt, * but it doesn't seem to be available. * * We can't use jiffies or kernel timers because : * 1) We are called from the interrupt handler, which disable softirqs, * so jiffies won't be increased * 2) Jiffies granularity is usually very coarse (10ms), and we don't * want to wait that long to detect stuck hardware. * Jean II */static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self){ int iobase; int count = SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US; iobase = self->io.sir_base; /* Calibrated busy loop */ while((count-- > 0) && !(inb(iobase+UART_LSR) & UART_LSR_TEMT)) udelay(1); if(count == 0) IRDA_DEBUG(0, "%s(): stuck transmitter\n", __FUNCTION__);}/* PROBING * * */static int __init smsc_ircc_look_for_chips(void){ smsc_chip_address_t *address; char *type; unsigned int cfg_base, found; found = 0; address = possible_addresses; while(address->cfg_base){ cfg_base = address->cfg_base; /*printk(KERN_WARNING "%s(): probing: 0x%02x for: 0x%02x\n", __FUNCTION__, cfg_base, address->type);*/ if( address->type & SMSCSIO_TYPE_FDC){ type = "FDC"; if((address->type) & SMSCSIO_TYPE_FLAT) { if(!smsc_superio_flat(fdc_chips_flat,cfg_base, type)) found++; } if((address->type) & SMSCSIO_TYPE_PAGED) { if(!smsc_superio_paged(fdc_chips_paged,cfg_base, type)) found++; } } if( address->type & SMSCSIO_TYPE_LPC){ type = "LPC"; if((address->type) & SMSCSIO_TYPE_FLAT) { if(!smsc_superio_flat(lpc_chips_flat,cfg_base,type)) found++; } if((address->type) & SMSCSIO_TYPE_PAGED) { if(!smsc_superio_paged(lpc_chips_paged,cfg_base,"LPC")) found++; } } address++; } return found;} /* * Function smsc_superio_flat (chip, base, type) * * Try to get configuration of a smc SuperIO chip with flat register model * */static int __init smsc_superio_flat(const smsc_chip_t *chips, unsigned short cfgbase, char *type){ unsigned short firbase, sirbase; u8 mode, dma, irq; int ret = -ENODEV; IRDA_DEBUG(1, "%s\n", __FUNCTION__); if (smsc_ircc_probe(cfgbase, SMSCSIOFLAT_DEVICEID_REG, chips, type)==NULL) return ret; outb(SMSCSIOFLAT_UARTMODE0C_REG, cfgbase); mode = inb(cfgbase+1); /*printk(KERN_WARNING "%s(): mode: 0x%02x\n", __FUNCTION__, mode);*/ if(!(mode & SMSCSIOFLAT_UART2MODE_VAL_IRDA)) WARNING("%s(): IrDA not enabled\n", __FUNCTION__); outb(SMSCSIOFLAT_UART2BASEADDR_REG, cfgbase); sirbase = inb(cfgbase+1) << 2; /* FIR iobase */ outb(SMSCSIOFLAT_FIRBASEADDR_REG, cfgbase); firbase = inb(cfgbase+1) << 3; /* DMA */ outb(SMSCSIOFLAT_FIRDMASELECT_REG, cfgbase); dma = inb(cfgbase+1) & SMSCSIOFLAT_FIRDMASELECT_MASK; /* IRQ */ outb(SMSCSIOFLAT_UARTIRQSELECT_REG, cfgbase); irq = inb(cfgbase+1) & SMSCSIOFLAT_UART2IRQSELECT_MASK; MESSAGE("%s(): fir: 0x%02x, sir: 0x%02x, dma: %02d, irq: %d, mode: 0x%02x\n", __FUNCTION__, firbase, sirbase, dma, irq, mode); if (firbase) { if (smsc_ircc_open(firbase, sirbase, dma, irq) == 0) ret=0; } /* Exit configuration */ outb(SMSCSIO_CFGEXITKEY, cfgbase); return ret;}/* * Function smsc_superio_paged (chip, base, type) * * Try to get configuration of a smc SuperIO chip with paged register model * */static int __init smsc_superio_paged(const smsc_chip_t *chips, unsigned short cfg_base, char *type){ unsigned short fir_io, sir_io; int ret = -ENODEV; IRDA_DEBUG(1, "%s\n", __FUNCTION__); if (smsc_ircc_probe(cfg_base,0x20,chips,type)==NULL) return ret; /* Select logical device (UART2) */ outb(0x07, cfg_base); outb(0x05, cfg_base + 1); /* SIR iobase */ outb(0x60, cfg_base); sir_io = inb(cfg_base + 1) << 8; outb(0x61, cfg_base); sir_io |= inb(cfg_base + 1); /* Read FIR base */ outb(0x62, cfg_base); fir_io = inb(cfg_base + 1) << 8; outb(0x63, cfg_base); fir_io |= inb(cfg_base + 1); outb(0x2b, cfg_base); /* ??? */ if (fir_io) { if (smsc_ircc_open(fir_io, sir_io, ircc_dma, ircc_irq) == 0) ret=0; } /* Exit configuration */ outb(SMSCSIO_CFGEXITKEY, cfg_base); return ret;}static int __init smsc_access(unsigned short cfg_base,unsigned char reg){ IRDA_DEBUG(1, "%s\n", __FUNCTION__); outb(reg, cfg_base); if (inb(cfg_base)!=reg) return -1; return 0;}static const smsc_chip_t * __init smsc_ircc_probe(unsigned short cfg_base,u8 reg,const smsc_chip_t *chip,char *type){ u8 devid,xdevid,rev; IRDA_DEBUG(1, "%s\n", __FUNCTION__); /* Leave configuration */ outb(SMSCSIO_CFGEXITKEY, cfg_base); if (inb(cfg_base) == SMSCSIO_CFGEXITKEY) /* not a smc superio chip */ return NULL; outb(reg, cfg_base); xdevid=inb(cfg_base+1); /* Enter configuration */ outb(SMSCSIO_CFGACCESSKEY, cfg_base); #if 0 if (smsc_access(cfg_base,0x55)) /* send second key and check */ return NULL; #endif /* probe device ID */ if (smsc_access(cfg_base,reg)) return NULL; devid=inb(cfg_base+1); if (devid==0) /* typical value for unused port */ return NULL; if (devid==0xff) /* typical value for unused port */ return NULL; /* probe revision ID */ if (smsc_access(cfg_base,reg+1)) return NULL; rev=inb(cfg_base+1); if (rev>=128) /* i think this will make no sense */ return NULL; if (devid==xdevid) /* protection against false positives */ return NULL; /* Check for expected device ID; are there others? */ while(chip->devid!=devid) { chip++; if (chip->name==NULL) return NULL; } MESSAGE("found SMC SuperIO Chip (devid=0x%02x rev=%02X base=0x%04x): %s%s\n",devid,rev,cfg_base,type,chip->name); if (chip->rev>rev){ MESSAGE("Revision higher than expected\n"); return NULL; } if (chip->flags&NoIRDA) MESSAGE("chipset does not support IRDA\n"); return chip;}static int __init smsc_superio_fdc(unsigned short cfg_base){ int ret = -1; if (!request_region(cfg_base, 2, driver_name)) { WARNING("%s: can't get cfg_base of 0x%03x\n", __FUNCTION__, cfg_base); } else { if (!smsc_superio_flat(fdc_chips_flat,cfg_base,"FDC") ||!smsc_superio_paged(fdc_chips_paged,cfg_base,"FDC")) ret = 0; release_region(cfg_base, 2); } return ret;}static int __init smsc_superio_lpc(unsigned short cfg_base){ int ret = -1; if (!request_region(cfg_base, 2, driver_name)) { WARNING("%s: can't get cfg_base of 0x%03x\n", __FUNCTION__, cfg_base); } else { if (!smsc_superio_flat(lpc_chips_flat,cfg_base,"LPC") ||!smsc_superio_paged(lpc_chips_paged,cfg_base,"LPC")) ret = 0; release_region(cfg_base, 2); } return ret;}/************************************************ * * Transceivers specific functions * ************************************************//* * Function smsc_ircc_set_transceiver_smsc_ircc_atc(fir_base, speed) * * Program transceiver through smsc-ircc ATC circuitry * */static void smsc_ircc_set_transceiver_smsc_ircc_atc(int fir_base, u32 speed){ unsigned long jiffies_now, jiffies_timeout; u8 val; jiffies_now= jiffies; jiffies_timeout= jiffies+SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES; /* ATC */ register_bank(fir_base, 4); outb((inb(fir_base+IRCC_ATC) & IRCC_ATC_MASK) |IRCC_ATC_nPROGREADY|IRCC_ATC_ENABLE, fir_base+IRCC_ATC); while((val=(inb(fir_base+IRCC_ATC) & IRCC_ATC_nPROGREADY)) && !time_after(jiffies, jiffies_timeout)); if(val) WARNING("%s(): ATC: 0x%02x\n", __FUNCTION__, inb(fir_base+IRCC_ATC));}/* * Function smsc_ircc_probe_transceiver_smsc_ircc_atc(fir_base) * * Probe transceiver smsc-ircc ATC circuitry * */static int smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base){ return 0;}/* * Function smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(self, speed) * * Set transceiver * */static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed){ u8 fast_mode; switch(speed) { default: case 576000 : fast_mode = 0; break; case 1152000 : case 4000000 : fast_mode = IRCC_LCR_A_FAST; break; } register_bank(fir_base, 0); outb((inb(fir_base+IRCC_LCR_A) & 0xbf) | fast_mode, fir_base+IRCC_LCR_A);}/* * Function smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(fir_base) * * Probe transceiver * */static int smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base){ return 0;}/* * Function smsc_ircc_set_transceiver_toshiba_sat1800(fir_base, speed) * * Set transceiver * */static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed){ u8 fast_mode; switch(speed) { default: case 576000 : fast_mode = 0; break; case 1152000 : case 4000000 : fast_mode = /*IRCC_LCR_A_FAST |*/ IRCC_LCR_A_GP_DATA; break; } /* This causes an interrupt */ register_bank(fir_base, 0); outb((inb(fir_base+IRCC_LCR_A) & 0xbf) | fast_mode, fir_base+IRCC_LCR_A);}/* * Function smsc_ircc_probe_transceiver_toshiba_sat1800(fir_base) * * Probe transceiver * */static int smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base){ return 0;}module_init(smsc_ircc_init);module_exit(smsc_ircc_cleanup);MODULE_AUTHOR("Daniele Peri <peri@csai.unipa.it>");MODULE_DESCRIPTION("SMC IrCC SIR/FIR controller driver");MODULE_LICENSE("GPL");module_param(ircc_dma, int, 0);MODULE_PARM_DESC(ircc_dma, "DMA channel");module_param(ircc_irq, int, 0);MODULE_PARM_DESC(ircc_irq, "IRQ line");module_param(ircc_fir, int, 0);MODULE_PARM_DESC(ircc_fir, "FIR Base Address");module_param(ircc_sir, int, 0);MODULE_PARM_DESC(ircc_sir, "SIR Base Address");module_param(ircc_cfg, int, 0);MODULE_PARM_DESC(ircc_cfg, "Configuration register base address");module_param(ircc_transceiver, int, 0);MODULE_PARM_DESC(ircc_transceiver, "Transceiver type");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -