?? ioc3.c
字號:
/* * SGI IOC3 master driver and IRQ demuxer * * Copyright (c) 2005 Stanislaw Skowronek <skylark@linux-mips.org> * Heavily based on similar work by: * Brent Casavant <bcasavan@sgi.com> - IOC4 master driver * Pat Gefre <pfg@sgi.com> - IOC3 serial port IRQ demuxer */#include <linux/errno.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/dma-mapping.h>#include <linux/interrupt.h>#include <linux/spinlock.h>#include <linux/delay.h>#include <linux/ioc3.h>#include <linux/rwsem.h>#define IOC3_PCI_SIZE 0x100000static LIST_HEAD(ioc3_devices);static int ioc3_counter;static DECLARE_RWSEM(ioc3_devices_rwsem);static struct ioc3_submodule *ioc3_submodules[IOC3_MAX_SUBMODULES];static struct ioc3_submodule *ioc3_ethernet;static DEFINE_RWLOCK(ioc3_submodules_lock);/* NIC probing code */#define GPCR_MLAN_EN 0x00200000 /* enable MCR to pin 8 */static inline unsigned mcr_pack(unsigned pulse, unsigned sample){ return (pulse << 10) | (sample << 2);}static int nic_wait(struct ioc3_driver_data *idd){ unsigned mcr; do { mcr = readl(&idd->vma->mcr); } while (!(mcr & 2)); return mcr & 1;}static int nic_reset(struct ioc3_driver_data *idd){ int presence; unsigned long flags; local_irq_save(flags); writel(mcr_pack(500, 65), &idd->vma->mcr); presence = nic_wait(idd); local_irq_restore(flags); udelay(500); return presence;}static int nic_read_bit(struct ioc3_driver_data *idd){ int result; unsigned long flags; local_irq_save(flags); writel(mcr_pack(6, 13), &idd->vma->mcr); result = nic_wait(idd); local_irq_restore(flags); udelay(500); return result;}static void nic_write_bit(struct ioc3_driver_data *idd, int bit){ if (bit) writel(mcr_pack(6, 110), &idd->vma->mcr); else writel(mcr_pack(80, 30), &idd->vma->mcr); nic_wait(idd);}static unsigned nic_read_byte(struct ioc3_driver_data *idd){ unsigned result = 0; int i; for (i = 0; i < 8; i++) result = (result >> 1) | (nic_read_bit(idd) << 7); return result;}static void nic_write_byte(struct ioc3_driver_data *idd, int byte){ int i, bit; for (i = 8; i; i--) { bit = byte & 1; byte >>= 1; nic_write_bit(idd, bit); }}static unsigned longnic_find(struct ioc3_driver_data *idd, int *last, unsigned long addr){ int a, b, index, disc; nic_reset(idd); /* Search ROM. */ nic_write_byte(idd, 0xF0); /* Algorithm from ``Book of iButton Standards''. */ for (index = 0, disc = 0; index < 64; index++) { a = nic_read_bit(idd); b = nic_read_bit(idd); if (a && b) { printk(KERN_WARNING "IOC3 NIC search failed.\n"); *last = 0; return 0; } if (!a && !b) { if (index == *last) { addr |= 1UL << index; } else if (index > *last) { addr &= ~(1UL << index); disc = index; } else if ((addr & (1UL << index)) == 0) disc = index; nic_write_bit(idd, (addr>>index)&1); continue; } else { if (a) addr |= 1UL << index; else addr &= ~(1UL << index); nic_write_bit(idd, a); continue; } } *last = disc; return addr;}static void nic_addr(struct ioc3_driver_data *idd, unsigned long addr){ int index; nic_reset(idd); nic_write_byte(idd, 0xF0); for (index = 0; index < 64; index++) { nic_read_bit(idd); nic_read_bit(idd); nic_write_bit(idd, (addr>>index)&1); }}static void crc16_byte(unsigned int *crc, unsigned char db){ int i; for(i=0;i<8;i++) { *crc <<= 1; if((db^(*crc>>16)) & 1) *crc ^= 0x8005; db >>= 1; } *crc &= 0xFFFF;}static unsigned int crc16_area(unsigned char *dbs, int size, unsigned int crc){ while(size--) crc16_byte(&crc, *(dbs++)); return crc;}static void crc8_byte(unsigned int *crc, unsigned char db){ int i,f; for(i=0;i<8;i++) { f = (*crc ^ db) & 1; *crc >>= 1; db >>= 1; if(f) *crc ^= 0x8c; } *crc &= 0xff;}static unsigned int crc8_addr(unsigned long addr){ int i; unsigned int crc = 0x00; for(i=0;i<8;i++) crc8_byte(&crc, addr>>(i<<3)); return crc;}static voidread_redir_page(struct ioc3_driver_data *idd, unsigned long addr, int page, unsigned char *redir, unsigned char *data){ int loops = 16, i; while(redir[page] != 0xFF) { page = redir[page]^0xFF; loops--; if(loops<0) { printk(KERN_ERR "IOC3: NIC circular redirection\n"); return; } } loops = 3; while(loops>0) { nic_addr(idd, addr); nic_write_byte(idd, 0xF0); nic_write_byte(idd, (page << 5) & 0xE0); nic_write_byte(idd, (page >> 3) & 0x1F); for(i=0;i<0x20;i++) data[i] = nic_read_byte(idd); if(crc16_area(data, 0x20, 0x0000) == 0x800d) return; loops--; } printk(KERN_ERR "IOC3: CRC error in data page\n"); for(i=0;i<0x20;i++) data[i] = 0x00;}static voidread_redir_map(struct ioc3_driver_data *idd, unsigned long addr, unsigned char *redir){ int i,j,loops = 3,crc_ok; unsigned int crc; while(loops>0) { crc_ok = 1; nic_addr(idd, addr); nic_write_byte(idd, 0xAA); nic_write_byte(idd, 0x00); nic_write_byte(idd, 0x01); for(i=0;i<64;i+=8) { for(j=0;j<8;j++) redir[i+j] = nic_read_byte(idd); crc = crc16_area(redir+i, 8, (i==0)?0x8707:0x0000); crc16_byte(&crc, nic_read_byte(idd)); crc16_byte(&crc, nic_read_byte(idd)); if(crc != 0x800d) crc_ok = 0; } if(crc_ok) return; loops--; } printk(KERN_ERR "IOC3: CRC error in redirection page\n"); for(i=0;i<64;i++) redir[i] = 0xFF;}static void read_nic(struct ioc3_driver_data *idd, unsigned long addr){ unsigned char redir[64]; unsigned char data[64],part[32]; int i,j; /* read redirections */ read_redir_map(idd, addr, redir); /* read data pages */ read_redir_page(idd, addr, 0, redir, data); read_redir_page(idd, addr, 1, redir, data+32); /* assemble the part # */ j=0; for(i=0;i<19;i++) if(data[i+11] != ' ') part[j++] = data[i+11]; for(i=0;i<6;i++) if(data[i+32] != ' ') part[j++] = data[i+32]; part[j] = 0; /* skip Octane power supplies */ if(!strncmp(part, "060-0035-", 9)) return; if(!strncmp(part, "060-0038-", 9)) return; strcpy(idd->nic_part, part); /* assemble the serial # */ j=0; for(i=0;i<10;i++) if(data[i+1] != ' ') idd->nic_serial[j++] = data[i+1]; idd->nic_serial[j] = 0;}static void read_mac(struct ioc3_driver_data *idd, unsigned long addr){ int i, loops = 3; unsigned char data[13]; while(loops>0) { nic_addr(idd, addr); nic_write_byte(idd, 0xF0); nic_write_byte(idd, 0x00); nic_write_byte(idd, 0x00); nic_read_byte(idd); for(i=0;i<13;i++) data[i] = nic_read_byte(idd); if(crc16_area(data, 13, 0x0000) == 0x800d) { for(i=10;i>4;i--) idd->nic_mac[10-i] = data[i]; return; } loops--; } printk(KERN_ERR "IOC3: CRC error in MAC address\n"); for(i=0;i<6;i++) idd->nic_mac[i] = 0x00;}static void probe_nic(struct ioc3_driver_data *idd){ int save = 0, loops = 3; unsigned long first, addr; writel(GPCR_MLAN_EN, &idd->vma->gpcr_s); while(loops>0) { idd->nic_part[0] = 0; idd->nic_serial[0] = 0; addr = first = nic_find(idd, &save, 0); if(!first) return; while(1) { if(crc8_addr(addr)) break; else { switch(addr & 0xFF) { case 0x0B: read_nic(idd, addr); break; case 0x09: case 0x89: case 0x91: read_mac(idd, addr); break; } } addr = nic_find(idd, &save, addr); if(addr == first) return; } loops--; } printk(KERN_ERR "IOC3: CRC error in NIC address\n");}/* Interrupts */static void write_ireg(struct ioc3_driver_data *idd, uint32_t val, int which){ unsigned long flags; spin_lock_irqsave(&idd->ir_lock, flags); switch (which) { case IOC3_W_IES: writel(val, &idd->vma->sio_ies); break; case IOC3_W_IEC: writel(val, &idd->vma->sio_iec); break; } spin_unlock_irqrestore(&idd->ir_lock, flags);}static inline uint32_t get_pending_intrs(struct ioc3_driver_data *idd){ unsigned long flag; uint32_t intrs = 0; spin_lock_irqsave(&idd->ir_lock, flag); intrs = readl(&idd->vma->sio_ir); intrs &= readl(&idd->vma->sio_ies); spin_unlock_irqrestore(&idd->ir_lock, flag); return intrs;}static irqreturn_t ioc3_intr_io(int irq, void *arg){ unsigned long flags; struct ioc3_driver_data *idd = arg; int handled = 1, id; unsigned int pending; read_lock_irqsave(&ioc3_submodules_lock, flags); if(idd->dual_irq && readb(&idd->vma->eisr)) { /* send Ethernet IRQ to the driver */ if(ioc3_ethernet && idd->active[ioc3_ethernet->id] && ioc3_ethernet->intr) { handled = handled && !ioc3_ethernet->intr(ioc3_ethernet, idd, 0); } } pending = get_pending_intrs(idd); /* look at the IO IRQs */ for(id=0;id<IOC3_MAX_SUBMODULES;id++) { if(idd->active[id] && ioc3_submodules[id] && (pending & ioc3_submodules[id]->irq_mask)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -