📄 smc91111.c
字号:
{ int offset; offset = (base + reg) & 0x3; return inl((base + reg) & ~3) >> 8 * offset;}static inline u16SMC_inw(u_int base, u_int reg){ int offset; offset = (base + reg) & 0x3; if (offset % 2) printk(KERN_CRIT "%s: read not word aligned!\n", __FUNCTION__); return inl((base + reg) & ~3) >> 8 * offset;}static inline u32SMC_inl(u_int base, u_int reg){ if ((base + reg) & 3) printk(KERN_CRIT "%s: read not dword aligned!\n", __FUNCTION__); return inl(base + reg);}/* Only one release command at a time, please */static inline void smc_wait_mmu_release_complete( unsigned int ioaddr){ int count = 0; /* assume bank 2 selected */ while ( SMC_inw( ioaddr, MMU_CMD_REG ) & MC_BUSY ) { udelay(1); // Wait until not busy if( ++count > 200) break; }}/* Only one allocate command at a time, please */static inline void smc_check_mmu_allocate_complete( unsigned int ioaddr){ if ( SMC_inb( ioaddr , AR_REG) & AR_FAILED) { printk("Memory alloc result: FAILED. \n"); }}static inline voidSMC_dma_inl(struct net_device *dev, unsigned char *data, u_int len){#if 0 struct smc_local *lp = (struct smc_local *) dev->priv; dma_addr_t dma_handle; unsigned int dcmd_width = DCMD_WIDTH2; if( lp->use_32bit) dcmd_width = DCMD_WIDTH4; //get sk_buff phys addr for dma engine: dma_handle = pci_map_single(NULL, data, (size_t) len, PCI_DMA_FROMDEVICE); if (dma_handle == 0) { printk(KERN_CRIT "%s: Couldn't acquire phys addr of dma target buffer!\n", __FUNCTION__); return; } PRINTK("%s: using %0#10x for phys addr for target buffer\n", __FUNCTION__, dma_handle); PRINTK("%s: using %0#10lx for phys addr of smc rx buffer\n", __FUNCTION__, lp->dev_dma_phys); PRINTK("%s: using dma channel %d\n", __FUNCTION__, dev->dma); PRINTK("%s: attempting to xfer %d bytes of data...\n", __FUNCTION__, len); DCSR(dev->dma) = DCSR_NODESC; DTADR(dev->dma) = dma_handle; DSADR(dev->dma) = lp->dev_dma_phys; DCMD(dev->dma) = DCMD_INCTRGADDR | DCMD_BURST32 | dcmd_width | (DCMD_LENGTH & len); DCSR(dev->dma) = DCSR_NODESC | DCSR_RUN; /* Interrupt context, folks- no sleeping allowed... */ while (!(DCSR(dev->dma) & DCSR_STOPSTATE)); DCSR(dev->dma) = 0; pci_unmap_single(NULL, dma_handle, (size_t) len, PCI_DMA_FROMDEVICE);#endif}static inline voidSMC_ins(u_int base, u_int reg, unsigned char *data, u_int len, struct net_device *dev){ struct smc_local *lp = (struct smc_local *) dev->priv; if( lp->dev_dma_phys) { SMC_dma_inl(dev, data, len); } else { u_int port = base + reg; if( lp->use_32bit) { int i; unsigned char *tail; unsigned long leftover; PRINTK ("SMC_ins: got %x for base, %x for reg, %p for data, %d for len\n", base, reg, data, len); PRINTK(" Reading %d dwords (and %d bytes) \n", len >> 2, len & 3); insl(port, data, len >> 2); /* read the left over bytes (this is where things go bad * on PXA. Don't even THINK of using insb... */ if (len & 3) { tail = data + (len & ~3); leftover = SMC_inl(base, reg); for (i = 0; i < (len & 3); i++) *tail++ = (char) (leftover >> (8 * i)) & 0xff; PRINTK("Leftover (last) fifo read: %0#10lx saving to %p\n", leftover, tail); } } else { PRINTK(" Reading %d words and %d byte(s) \n", len >> 1, len & 1); insw(port, data, len >> 1); if (len & 1) { data += len & ~1; *data = inb(port); } } }}void lan91c111Delay()
{
int i;
for(i=0; i<80; i++);
}/* byte, word writes function normally on PXA */static inline voidSMC_outb(u8 val, u_int base, u_int reg){ outb(val, base + reg); lan91c111Delay();}static inline voidSMC_outw(u16 val, u_int base, u_int reg){ outw(val, base + reg); lan91c111Delay();}static inline voidSMC_outl(u32 val, u_int base, u_int reg){ u_int port = base + reg; outl(val, port); lan91c111Delay();}/*static inline voidSMC_outb(u8 val, u_int base, u_int reg){ int offset; u32 offset2=val; offset = (base + reg) & 0x3; outl((offset2<< 8 * offset),((base + reg) & ~3)) ; }static inline voidSMC_outw(u16 val, u_int base, u_int reg){ int offset; u32 offset2=val; offset = (base + reg) & 0x3; if (offset % 2) printk(KERN_CRIT "%s: read not word aligned!\n", __FUNCTION__); outl((offset2<< 8 * offset),((base + reg) & ~3)) ;}static inline voidSMC_outl(u32 val, u_int base, u_int reg){ u_int port = base + reg; outl(val, port);}*/static inline voidSMC_outsl(u_int base, u_int reg, unsigned char *data, u_int len){ u_int port = base + reg; outsl(port, data, len >> 2); lan91c111Delay();}static inline voidSMC_outsw(u_int base, u_int reg, unsigned char *data, u_int len){ u_int port = base + reg; outsw(port, data, len >> 1); lan91c111Delay();}/*------------------------------------------------------------------------- . I define some macros to make it easier to do somewhat common . or slightly complicated, repeated tasks. --------------------------------------------------------------------------*//* select a register bank, 0 to 3 */#define SMC_SELECT_BANK(x) \ { \ SMC_outw(x, ioaddr, BANK_SELECT); \ }/* define a small delay for the reset */#define SMC_DELAY() \ { \ SMC_inw(ioaddr, RCR); \ SMC_inw(ioaddr, RCR); \ SMC_inw(ioaddr, RCR); \ }/* this enables an interrupt in the interrupt mask register */#define SMC_ENABLE_INT(x) \ { \ u8 mask; \ SMC_SELECT_BANK(2) \ mask = SMC_inb(ioaddr, IM_REG); \ mask |= (x); \ SMC_outb(mask, ioaddr, IM_REG); \ }/* this disables an interrupt from the interrupt mask register */#define SMC_DISABLE_INT(x) { \ u8 mask; \ SMC_SELECT_BANK(2); \ mask = SMC_inb( ioaddr, IM_REG ); \ mask &= ~(x); \ SMC_outb( mask, ioaddr, IM_REG ); \}#endif/* CONFIG_ARCH_S3C2410*//* . Function: smc_reset( struct device* dev ) . Purpose: . This sets the SMC91111 chip to its normal state, hopefully from whatever . mess that any other DOS driver has put it in. . . Maybe I should reset more registers to defaults in here? SOFTRST should . do that for me. . . Method: . 1. send a SOFT RESET . 2. wait for it to finish . 3. enable autorelease mode . 4. reset the memory management unit . 5. clear all interrupts .*/static voidsmc_reset(struct net_device *dev){ //struct smc_local *lp = (struct smc_local *)dev->priv; unsigned int ioaddr = dev->base_addr; PRINTK2("%s:smc_reset\n", dev->name); /* This resets the registers mostly to defaults, but doesn't affect EEPROM. That seems unnecessary */ SMC_SELECT_BANK(0); SMC_outw(RCR_SOFTRST, ioaddr, RCR_REG); /* Setup the Configuration Register */ /* This is necessary because the CONFIG_REG is not affected */ /* by a soft reset */ SMC_SELECT_BANK(1); SMC_outw(CONFIG_DEFAULT, ioaddr, CONFIG_REG); /* Setup for fast accesses if requested */ /* If the card/system can't handle it then there will */ /* be no recovery except for a hard reset or power cycle */ if (wait_mode[(int)(dev->name[3] - 48)]) //an ascii digit, don't forget SMC_outw(((SMC_inw(ioaddr, CONFIG_REG)) | CONFIG_NO_WAIT), ioaddr, CONFIG_REG);#ifdef SMC_POWER_DOWN /* Release from possible power-down state */ /* Configuration register is not affected by Soft Reset */ SMC_SELECT_BANK(1); SMC_outw((SMC_inw(ioaddr, CONFIG_REG) | CONFIG_EPH_POWER_EN), ioaddr, CONFIG_REG);#endif SMC_SELECT_BANK(0); /* this should pause enough for the chip to be happy */ mdelay(10); /* Disable transmit and receive functionality */ SMC_outw(RCR_CLEAR, ioaddr, RCR_REG); SMC_outw(TCR_CLEAR, ioaddr, TCR_REG); /* set the control register to automatically release successfully transmitted packets, to make the best use out of our limited memory */ SMC_SELECT_BANK(1);#ifdef USE_AUTO_RELEASE SMC_outw((SMC_inw(ioaddr, CTL_REG) | CTL_AUTO_RELEASE), ioaddr, CTL_REG);#else SMC_outw((SMC_inw(ioaddr, CTL_REG) & ~CTL_AUTO_RELEASE), ioaddr, CTL_REG);#endif /* Reset the MMU */ SMC_SELECT_BANK(2); SMC_outw(MC_RESET, ioaddr, MMU_CMD_REG); /* Note: It doesn't seem that waiting for the MMU busy is needed here, but this is a place where future chipsets _COULD_ break. Be wary of issuing another MMU command right after this */ /* Well, then, how about doing this? [jws] */ while (SMC_inw(ioaddr, MMU_CMD_REG) & MC_BUSY) udelay(1); // Wait until not busy /* Disable all interrupts */ SMC_outb(0, ioaddr, IM_REG);}/* . Function: smc_enable . Purpose: let the chip talk to the outside work . Method: . 1. Enable the transmitter . 2. Enable the receiver . 3. Enable interrupts*/static voidsmc_enable(struct net_device *dev){ unsigned int ioaddr = dev->base_addr; struct smc_local *lp = (struct smc_local *) dev->priv; PRINTK2("%s:smc_enable\n", dev->name); SMC_SELECT_BANK(0); /* see the header file for options in TCR/RCR DEFAULT */ SMC_outw(lp->tcr_cur_mode, ioaddr, TCR_REG); SMC_outw(lp->rcr_cur_mode, ioaddr, RCR_REG); /* now, enable interrupts */ SMC_SELECT_BANK(2); SMC_outb(SMC_INTERRUPT_MASK, ioaddr, IM_REG);}/* . Function: smc_shutdown . Purpose: closes down the SMC91xxx chip. . Method: . 1. zero the interrupt mask . 2. clear the enable receive flag . 3. clear the enable xmit flags . . TODO: . (1) maybe utilize power down mode. . Why not yet? Because while the chip will go into power down mode, . the manual says that it will wake up in response to any I/O requests . in the register space. Empirical results do not show this working.*/static voidsmc_shutdown(unsigned int ioaddr){ PRINTK2("%s:smc_shutdown\n", CARDNAME); /* no more interrupts for me */ SMC_SELECT_BANK(2); SMC_outb(0, ioaddr, IM_REG); /* and tell the card to stay away from that nasty outside world */ SMC_SELECT_BANK(0); SMC_outb(RCR_CLEAR, ioaddr, RCR_REG); SMC_outb(TCR_CLEAR, ioaddr, TCR_REG);#ifdef SMC_POWER_DOWN /* finally, shut the chip down */ SMC_SELECT_BANK(1); SMC_outw(SMC_inw(ioaddr, CONFIG_REG) & ~CONFIG_EPH_POWER_EN, ioaddr, CONFIG_REG);#endif}/* . Function: smc_setmulticast( int ioaddr, int count, dev_mc_list * adds ) . Purpose: . This sets the internal hardware table to filter out unwanted multicast . packets before they take up memory. . . The SMC chip uses a hash table where the high 6 bits of the CRC of . address are the offset into the table. If that bit is 1, then the . multicast packet is accepted. Otherwise, it's dropped silently. . . To use the 6 bits as an offset into the table, the high 3 bits are the . number of the 8 bit register, while the low 3 bits are the bit within . that register. . . This routine is based very heavily on the one provided by Peter Cammaert.*/static voidsmc_setmulticast(unsigned int ioaddr, int count, struct dev_mc_list *addrs){ int i; unsigned char multicast_table[8]; struct dev_mc_list *cur_addr; /* table for flipping the order of 3 bits */ unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; PRINTK2("%s:smc_setmulticast\n", CARDNAME); /* start with a table of all zeros: reject all */ memset(multicast_table, 0, sizeof (multicast_table)); cur_addr = addrs; for (i = 0; i < count; i++, cur_addr = cur_addr->next) { int position; /* do we have a pointer here? */ if (!cur_addr) break; /* make sure this is a multicast address - shouldn't this be a given if we have it here ? */ if (!(*cur_addr->dmi_addr & 1)) continue; /* only use the low order bits */ position = crc32(cur_addr->dmi_addr, 6) & 0x3f; /* do some messy swapping to put the bit in the right spot */ multicast_table[invert3[position & 7]] |= (1 << invert3[(position >> 3) & 7]); } /* now, the table can be loaded into the chipset */ SMC_SELECT_BANK(3); for (i = 0; i < 8; i++) { SMC_outb(multicast_table[i], ioaddr, MCAST_REG1 + i); }}/* Finds the CRC32 of a set of bytes. Again, from Peter Cammaert's code.*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -