📄 lanai.c
字号:
* Returns buf->order<0 if no memory * Note that the size will be rounded up to an "order" of pages, and * if we can't allocate that we'll settle for something smaller * until minbytes * * NOTE: buffer must be 32-bit DMA capable - when linux can * make distinction, this will need tweaking for this * to work on BIG memory machines. */static void lanai_buf_allocate(struct lanai_buffer *buf, int bytes, int minbytes){ unsigned long address; int order = bytes_to_order(bytes); do { address = __get_free_pages(GFP_KERNEL, order); if (address != 0) { /* Success */ bytes = PAGE_SIZE << order; buf->start = buf->ptr = (u32 *) address; buf->end = (u32 *) (address + bytes); memset((void *) address, 0, bytes); break; } if ((PAGE_SIZE << --order) < minbytes) order = -1; /* Too small - give up */ } while (order >= 0); buf->order = order;}static inline void lanai_buf_deallocate(struct lanai_buffer *buf){ if (buf->order >= 0) { APRINTK(buf->start != 0, "lanai_buf_deallocate: start==0!\n"); free_pages((unsigned long) buf->start, buf->order); buf->start = buf->end = buf->ptr = 0; }}/* size of buffer in bytes */static inline int lanai_buf_size(const struct lanai_buffer *buf){ return ((unsigned long) buf->end) - ((unsigned long) buf->start);}/* size of buffer as "card order" (0=1k .. 7=128k) */static inline int lanai_buf_size_cardorder(const struct lanai_buffer *buf){ return buf->order + PAGE_SHIFT - 10;}/* DMA-able address for this buffer */static unsigned long lanai_buf_dmaaddr(const struct lanai_buffer *buf){ unsigned long r = virt_to_bus(buf->start); APRINTK((r & ~0xFFFFFF00) == 0, "bad dmaaddr: 0x%lx\n", (long) r); return r;}/* -------------------- HANDLE BACKLOG_VCCS BITFIELD: */static inline void vcc_mark_backlogged(struct lanai_dev *lanai, const struct lanai_vcc *lvcc){ APRINTK(lvcc->vbase != 0, "vcc_mark_backlogged: zero vbase!\n"); vci_bitfield_set(&lanai->backlog_vccs, lvcc->vci);}static inline void vcc_unmark_backlogged(struct lanai_dev *lanai, const struct lanai_vcc *lvcc){ APRINTK(lvcc->vbase != 0, "vcc_unmark_backlogged: zero vbase!\n"); vci_bitfield_clear(&lanai->backlog_vccs, lvcc->vci);}static inline void vcc_backlog_init(struct lanai_dev *lanai){ vci_bitfield_init(&lanai->backlog_vccs);}static inline int vcc_is_backlogged(/*const*/ struct lanai_vcc *lvcc){ return lvcc->tx.inprogress != NULL || !skb_queue_empty(&lvcc->tx.backlog);}/* -------------------- PORT I/O UTILITIES: *//* Registers (and their bit-fields) */enum lanai_register { Reset_Reg = 0x00, /* Reset; read for chip type; bits: */#define RESET_GET_BOARD_REV(x) (((x)>> 0)&0x03) /* Board revision */#define RESET_GET_BOARD_ID(x) (((x)>> 2)&0x03) /* Board ID */#define BOARD_ID_LANAI256 (0) /* 25.6M adaptor card */ Endian_Reg = 0x04, /* Endian setting */ IntStatus_Reg = 0x08, /* Interrupt status */ IntStatusMasked_Reg = 0x0C, /* Interrupt status (masked) */ IntAck_Reg = 0x10, /* Interrupt acknowledge */ IntAckMasked_Reg = 0x14, /* Interrupt acknowledge (masked) */ IntStatusSet_Reg = 0x18, /* Get status + enable/disable */ IntStatusSetMasked_Reg = 0x1C, /* Get status + en/di (masked) */ IntControlEna_Reg = 0x20, /* Interrupt control enable */ IntControlDis_Reg = 0x24, /* Interrupt control disable */ Status_Reg = 0x28, /* Status */#define STATUS_PROMDATA (0x00000001) /* PROM_DATA pin */#define STATUS_WAITING (0x00000002) /* Interrupt being delayed */#define STATUS_SOOL (0x00000004) /* SOOL alarm */#define STATUS_LOCD (0x00000008) /* LOCD alarm */#define STATUS_LED (0x00000010) /* LED (HAPPI) output */#define STATUS_GPIN (0x00000020) /* GPIN pin */#define STATUS_BUTTBUSY (0x00000040) /* Butt register is pending */ Config1_Reg = 0x2C, /* Config word 1; bits: */#define CONFIG1_PROMDATA (0x00000001) /* PROM_DATA pin */#define CONFIG1_PROMCLK (0x00000002) /* PROM_CLK pin */#define CONFIG1_SET_READMODE(x) ((x)*0x004) /* PCI BM reads; values: */#define READMODE_PLAIN (0) /* Plain memory read */#define READMODE_LINE (2) /* Memory read line */#define READMODE_MULTIPLE (3) /* Memory read multiple */#define CONFIG1_DMA_ENABLE (0x00000010) /* Turn on DMA */#define CONFIG1_POWERDOWN (0x00000020) /* Turn off clocks */#define CONFIG1_SET_LOOPMODE(x) ((x)*0x080) /* Clock&loop mode; values: */#define LOOPMODE_NORMAL (0) /* Normal - no loop */#define LOOPMODE_TIME (1)#define LOOPMODE_DIAG (2)#define LOOPMODE_LINE (3)#define CONFIG1_MASK_LOOPMODE (0x00000180)#define CONFIG1_SET_LEDMODE(x) ((x)*0x0200) /* Mode of LED; values: */#define LEDMODE_NOT_SOOL (0) /* !SOOL */#define LEDMODE_OFF (1) /* 0 */#define LEDMODE_ON (2) /* 1 */#define LEDMODE_NOT_LOCD (3) /* !LOCD */#define LEDMORE_GPIN (4) /* GPIN */#define LEDMODE_NOT_GPIN (7) /* !GPIN */#define CONFIG1_MASK_LEDMODE (0x00000E00)#define CONFIG1_GPOUT1 (0x00001000) /* Toggle for reset */#define CONFIG1_GPOUT2 (0x00002000) /* Loopback PHY */#define CONFIG1_GPOUT3 (0x00004000) /* Loopback lanai */ Config2_Reg = 0x30, /* Config word 2; bits: */#define CONFIG2_HOWMANY (0x00000001) /* >512 VCIs? */#define CONFIG2_PTI7_MODE (0x00000002) /* Make PTI=7 RM, not OAM */#define CONFIG2_VPI_CHK_DIS (0x00000004) /* Ignore RX VPI value */#define CONFIG2_HEC_DROP (0x00000008) /* Drop cells w/ HEC errors */#define CONFIG2_VCI0_NORMAL (0x00000010) /* Treat VCI=0 normally */#define CONFIG2_CBR_ENABLE (0x00000020) /* Deal with CBR traffic */#define CONFIG2_TRASH_ALL (0x00000040) /* Trashing incoming cells */#define CONFIG2_TX_DISABLE (0x00000080) /* Trashing outgoing cells */#define CONFIG2_SET_TRASH (0x00000100) /* Turn trashing on */ Statistics_Reg = 0x34, /* Statistics; bits: */#define STATS_GET_FIFO_OVFL(x) (((x)>> 0)&0xFF) /* FIFO overflowed */#define STATS_GET_HEC_ERR(x) (((x)>> 8)&0xFF) /* HEC was bad */#define STATS_GET_BAD_VCI(x) (((x)>>16)&0xFF) /* VCI not open */#define STATS_GET_BUF_OVFL(x) (((x)>>24)&0xFF) /* VCC buffer full */ ServiceStuff_Reg = 0x38, /* Service stuff; bits: */#define SSTUFF_SET_SIZE(x) ((x)*0x20000000) /* size of service buffer */#define SSTUFF_SET_ADDR(x) ((x)>>8) /* set address of buffer */ ServWrite_Reg = 0x3C, /* ServWrite Pointer */ ServRead_Reg = 0x40, /* ServRead Pointer */ TxDepth_Reg = 0x44, /* FIFO Transmit Depth */ Butt_Reg = 0x48, /* Butt register */ CBR_ICG_Reg = 0x50, CBR_PTR_Reg = 0x54, PingCount_Reg = 0x58, /* Ping count */ DMA_Addr_Reg = 0x5C /* DMA address */};static inline bus_addr_t reg_addr(const struct lanai_dev *lanai, enum lanai_register reg){ return lanai->base + (bus_addr_t) reg;}static inline u32 reg_read(const struct lanai_dev *lanai, enum lanai_register reg){ u32 t; t = readl(reg_addr(lanai, reg)); RWDEBUG("R [0x%08X] 0x%02X = 0x%08X\n", (unsigned int) lanai->base, (int) reg, t); return t;}static inline void reg_write(const struct lanai_dev *lanai, u32 val, enum lanai_register reg){ RWDEBUG("W [0x%08X] 0x%02X < 0x%08X\n", (unsigned int) lanai->base, (int) reg, val); writel(val, reg_addr(lanai, reg)); mdelay(1);}static inline void conf1_write(const struct lanai_dev *lanai){ reg_write(lanai, lanai->conf1, Config1_Reg);}static inline void conf2_write(const struct lanai_dev *lanai){ reg_write(lanai, lanai->conf2, Config2_Reg);}static inline void reset_board(const struct lanai_dev *lanai){ DPRINTK("about to reset board\n"); reg_write(lanai, 0, Reset_Reg); /* * If we don't delay a little while here then we can end up * leaving the card in a VERY weird state and lock up the * PCI bus. This isn't documented anywhere but I've convinced * myself after a lot of painful experimentation */ udelay(5);}/* -------------------- VCC LIST LOCK: *//* * The linux-atm code disables local IRQs while managing the list of * VCCs on a card. This is good, but it doesn't save us against * SMP. Unfortunately, fixing this will require changes in the * API which will have to wait a little bit. It's a hard race to * trigger accidentally, so it isn't TOO horrible so far. * * One possible solution would be to have an rwlock which is * always grabbed _irq-style on writing. This would automatically * be grabbed (for writing) by the higher layers on things that * would result in a change in the vcc list (_open, _close, * probably _change_qos) - thus it would also protect the * higher-level list of vccs on each device (atm_dev->vccs). * The driver would be responsible for grabbing it as a read_lock * anytime it wants to consult its table of vccs - for instance * when handling an incoming PDU. This also explains why we would * probably want the write_lock while in _change_qos - to prevent * handling of PDUs while possibly in an inconsistant state. * Also, _send would grab the lock for reading. * * One problem with this is that _open and _close could no longer * do anything that might provoke a schedule. First, it would * force us to use GFP_ATOMIC memory (which is bad), but also * some devices pretty much require scheduling due to long * delays (see lanai_close for an example). So in this case * we need a way to schedule without losing the spinlock. * The cleanest way to do this is probably have a way to mark a * VCC as "in progress" so that the interrupt handler can * still disregard any traffic for it while _open or _close * are sleeping on it. Then it will need to be _open and * _close's job to relinquish the write_lock. Thus, the * lock could be dropped around the times that scheduling * might occur. Perhaps the _READY flag can be used for * this purpose. * * One short note about this "upper layer grabs, driver * relinquishes" write lock - since this needs to be * an _irq lock we're going to have problem saving * and restoring flags (_irqsave/_irqrestore). This * shouldn't be a problem, however - we must just * require that those syscalls are never called with * interrupts disabled so we can use the non-flags-saving * versions. * * Anyway, all of the above is vaporware currently - fixing * this right will require changes in the API and all of * the drivers - this will wait until 2.5.x most likely. * The following NOP macros are just here to mark where * the locks will be needed in the future. */#define vcclist_read_lock() do {} while (0)#define vcclist_read_unlock() do {} while (0)#define vcclist_write_lock() do {} while (0)#define vcclist_write_unlock() do {} while (0)/* -------------------- CARD SRAM UTILITIES: *//* The SRAM is mapped into normal PCI memory space - the only catch is * that it is only 16-bits wide but must be accessed as 32-bit. The * 16 high bits will be zero. We don't hide this, since they get * programmed mostly like discrete registers anyway */#define SRAM_START (0x20000)#define SRAM_BYTES (0x20000) /* Again, half don't really exist */static inline bus_addr_t sram_addr(const struct lanai_dev *lanai, int offset){ return lanai->base + SRAM_START + offset;}static inline u32 sram_read(const struct lanai_dev *lanai, int offset){ return readl(sram_addr(lanai, offset));}static inline void sram_write(const struct lanai_dev *lanai, u32 val, int offset){ writel(val, sram_addr(lanai, offset));}static int __init sram_test_word( const struct lanai_dev *lanai, int offset, u32 pattern){ u32 readback; sram_write(lanai, pattern, offset); readback = sram_read(lanai, offset); if (readback == pattern) return 0; printk(KERN_ERR DEV_LABEL "(itf %d): SRAM word at %d bad: wrote 0x%X, read 0x%X\n", lanai->number, offset, pattern, readback); return -EIO;}static int __init sram_test_pass(const struct lanai_dev *lanai, u32 pattern){ int offset, result = 0; for (offset = 0; offset < SRAM_BYTES && result == 0; offset += 4) result = sram_test_word(lanai, offset, pattern); return result;}static int __init sram_test_and_clear(const struct lanai_dev *lanai){#ifdef FULL_MEMORY_TEST int result; DPRINTK("testing SRAM\n"); if ((result = sram_test_pass(lanai, 0x5555)) != 0) return result; if ((result = sram_test_pass(lanai, 0xAAAA)) != 0) return result;#endif DPRINTK("clearing SRAM\n"); return sram_test_pass(lanai, 0x0000);}/* -------------------- CARD-BASED VCC TABLE UTILITIES: *//* vcc table */enum lanai_vcc_offset { vcc_rxaddr1 = 0x00, /* Location1, plus bits: */#define RXADDR1_SET_SIZE(x) ((x)*0x0000100) /* size of RX buffer */#define RXADDR1_SET_RMMODE(x) ((x)*0x00800) /* RM cell action; values: */#define RMMODE_TRASH (0) /* discard */#define RMMODE_PRESERVE (1) /* input as AAL0 */#define RMMODE_PIPE (2) /* pipe to coscheduler */#define RMMODE_PIPEALL (3) /* pipe non-RM too */#define RXADDR1_OAM_PRESERVE (0x00002000) /* Input OAM cells as AAL0 */#define RXADDR1_SET_MODE(x) ((x)*0x0004000) /* Reassembly mode */#define RXMODE_TRASH (0) /* discard */#define RXMODE_AAL0 (1) /* non-AAL5 mode */#define RXMODE_AAL5 (2) /* AAL5, intr. each PDU */#define RXMODE_AAL5_STREAM (3) /* AAL5 w/o per-PDU intr */ vcc_rxaddr2 = 0x04, /* Location2 */ vcc_rxcrc1 = 0x08, /* RX CRC claculation space */ vcc_rxcrc2 = 0x0C, vcc_rxwriteptr = 0x10, /* RX writeptr, plus bits: */#define RXWRITEPTR_LASTEFCI (0x00002000) /* Last PDU had EFCI bit */#define RXWRITEPTR_DROPPING (0x00004000) /* Had error, dropping */#define RXWRITEPTR_TRASHING (0x00008000) /* Trashing */ vcc_rxbufstart = 0x14, /* RX bufstart, plus bits: */#define RXBUFSTART_CLP (0x00004000)#define RXBUFSTART_CI (0x00008000) vcc_rxreadptr = 0x18, /* RX readptr */ vcc_txicg = 0x1C, /* TX ICG */ vcc_txaddr1 = 0x20, /* Location1, plus bits: */#define TXADDR1_SET_SIZE(x) ((x)*0x0000100) /* size of TX buffer */#define TXADDR1_ABR (0x00008000) /* use ABR (doesn't work) */ vcc_txaddr2 = 0x24, /* Location2 */ vcc_txcrc1 = 0x28, /* TX CRC claculation space */ vcc_txcrc2 = 0x2C, vcc_txreadptr = 0x30, /* TX Readptr, plus bits: */#define TXREADPTR_GET_PTR(x) ((x)&0x01FFF)#define TXREADPTR_MASK_DELTA (0x0000E000) /* ? */ vcc_txendptr = 0x34, /* TX Endptr, plus bits: */#define TXENDPTR_CLP (0x00002000)#define TXENDPTR_MASK_PDUMODE (0x0000C000) /* PDU mode; values: */#define PDUMODE_AAL0 (0*0x04000)#define PDUMODE_AAL5 (2*0x04000)#define PDUMODE_AAL5STREAM (3*0x04000) vcc_txwriteptr = 0x38, /* TX Writeptr */#define TXWRITEPTR_GET_PTR(x) ((x)&0x1FFF) vcc_txcbr_next = 0x3C /* # of next CBR VCI in ring */#define TXCBR_NEXT_BOZO (0x00008000) /* "bozo bit" */};#define CARDVCC_SIZE (0x40)static inline bus_addr_t cardvcc_addr(const struct lanai_dev *lanai, vci_t vci){ return sram_addr(lanai, vci * CARDVCC_SIZE);}static inline u32 cardvcc_read(const struct lanai_vcc *lvcc, enum lanai_vcc_offset offset){ u32 val; APRINTK(lvcc->vbase != 0, "cardvcc_read: unbound vcc!\n"); val= readl(lvcc->vbase + (bus_addr_t) offset); RWDEBUG("VR vci=%04d 0x%02X = 0x%08X\n", lvcc->vci, (int) offset, val); return val;}static inline void cardvcc_write(const struct lanai_vcc *lvcc,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -