📄 vlsi_ir.c
字号:
/********************************************************************* * * vlsi_ir.c: VLSI82C147 PCI IrDA controller driver for Linux * * Version: 0.3a, Nov 10, 2001 * * Copyright (c) 2001 Martin Diehl * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * ********************************************************************/#include <linux/module.h> #include <linux/kernel.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/netdevice.h>#include <linux/skbuff.h>#include <linux/delay.h>#include <linux/time.h>#include <net/irda/irda.h>#include <net/irda/irda_device.h>#include <net/irda/wrapper.h>#include <net/irda/irlap.h>#include <net/irda/vlsi_ir.h>/********************************************************/MODULE_DESCRIPTION("IrDA SIR/MIR/FIR driver for VLSI 82C147");MODULE_AUTHOR("Martin Diehl <info@mdiehl.de>");MODULE_LICENSE("GPL");static /* const */ char drivername[] = "vlsi_ir";#define PCI_CLASS_WIRELESS_IRDA 0x0d00static struct pci_device_id vlsi_irda_table [] __devinitdata = { { class: PCI_CLASS_WIRELESS_IRDA << 8, vendor: PCI_VENDOR_ID_VLSI, device: PCI_DEVICE_ID_VLSI_82C147, }, { /* all zeroes */ }};MODULE_DEVICE_TABLE(pci, vlsi_irda_table);/********************************************************/MODULE_PARM(clksrc, "i");MODULE_PARM_DESC(clksrc, "clock input source selection");/* clksrc: which clock source to be used * 0: auto - try PLL, fallback to 40MHz XCLK * 1: on-chip 48MHz PLL * 2: external 48MHz XCLK * 3: external 40MHz XCLK (HP OB-800) */static int clksrc = 0; /* default is 0(auto) */MODULE_PARM(ringsize, "1-2i");MODULE_PARM_DESC(ringsize, "TX, RX ring descriptor size");/* ringsize: size of the tx and rx descriptor rings * independent for tx and rx * specify as ringsize=tx[,rx] * allowed values: 4, 8, 16, 32, 64 * Due to the IrDA 1.x max. allowed window size=7, * there should be no gain when using rings larger than 8 */static int ringsize[] = {8,8}; /* default is tx=rx=8 */MODULE_PARM(sirpulse, "i");MODULE_PARM_DESC(sirpulse, "SIR pulse width tuning");/* sirpulse: tuning of the SIR pulse width within IrPHY 1.3 limits * 0: very short, 1.5us (exception: 6us at 2.4 kbaud) * 1: nominal 3/16 bittime width * note: IrDA compliant peer devices should be happy regardless * which one is used. Primary goal is to save some power * on the sender's side - at 9.6kbaud for example the short * pulse width saves more than 90% of the transmitted IR power. */static int sirpulse = 1; /* default is 3/16 bittime */MODULE_PARM(qos_mtt_bits, "i");MODULE_PARM_DESC(qos_mtt_bits, "IrLAP bitfield representing min-turn-time");/* qos_mtt_bits: encoded min-turn-time value we require the peer device * to use before transmitting to us. "Type 1" (per-station) * bitfield according to IrLAP definition (section 6.6.8) * The HP HDLS-1100 requires 1 msec - don't even know * if this is the one which is used by my OB800 */static int qos_mtt_bits = 0x04; /* default is 1 ms *//********************************************************//* some helpers for operations on ring descriptors */static inline int rd_is_active(struct vlsi_ring *r, unsigned i){ return ((r->hw[i].rd_status & RD_STAT_ACTIVE) != 0);}static inline void rd_activate(struct vlsi_ring *r, unsigned i){ r->hw[i].rd_status |= RD_STAT_ACTIVE;}static inline void rd_set_addr_status(struct vlsi_ring *r, unsigned i, dma_addr_t a, u8 s){ struct ring_descr *rd = r->hw +i; /* ordering is important for two reasons: * - overlayed: writing addr overwrites status * - we want to write status last so we have valid address in * case status has RD_STAT_ACTIVE set */ if ((a & ~DMA_MASK_MSTRPAGE) != MSTRPAGE_VALUE) BUG(); a &= DMA_MASK_MSTRPAGE; /* clear highbyte to make sure we won't write * to status - just in case MSTRPAGE_VALUE!=0 */ rd->rd_addr = a; wmb(); rd->rd_status = s; /* potentially passes ownership to the hardware */}static inline void rd_set_status(struct vlsi_ring *r, unsigned i, u8 s){ r->hw[i].rd_status = s;}static inline void rd_set_count(struct vlsi_ring *r, unsigned i, u16 c){ r->hw[i].rd_count = c;}static inline u8 rd_get_status(struct vlsi_ring *r, unsigned i){ return r->hw[i].rd_status;}static inline dma_addr_t rd_get_addr(struct vlsi_ring *r, unsigned i){ dma_addr_t a; a = (r->hw[i].rd_addr & DMA_MASK_MSTRPAGE) | (MSTRPAGE_VALUE << 24); return a;}static inline u16 rd_get_count(struct vlsi_ring *r, unsigned i){ return r->hw[i].rd_count;}/* producer advances r->head when descriptor was added for processing by hw */static inline void ring_put(struct vlsi_ring *r){ r->head = (r->head + 1) & r->mask;}/* consumer advances r->tail when descriptor was removed after getting processed by hw */static inline void ring_get(struct vlsi_ring *r){ r->tail = (r->tail + 1) & r->mask;}/********************************************************//* the memory required to hold the 2 descriptor rings */#define RING_AREA_SIZE (2 * MAX_RING_DESCR * sizeof(struct ring_descr))/* the memory required to hold the rings' buffer entries */#define RING_ENTRY_SIZE (2 * MAX_RING_DESCR * sizeof(struct ring_entry))/********************************************************//* just dump all registers */static void vlsi_reg_debug(unsigned iobase, const char *s){ int i; mb(); printk(KERN_DEBUG "%s: ", s); for (i = 0; i < 0x20; i++) printk("%02x", (unsigned)inb((iobase+i))); printk("\n");}/********************************************************/static int vlsi_set_clock(struct pci_dev *pdev){ u8 clkctl, lock; int i, count; if (clksrc < 2) { /* auto or PLL: try PLL */ clkctl = CLKCTL_NO_PD | CLKCTL_CLKSTP; pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); /* procedure to detect PLL lock synchronisation: * after 0.5 msec initial delay we expect to find 3 PLL lock * indications within 10 msec for successful PLL detection. */ udelay(500); count = 0; for (i = 500; i <= 10000; i += 50) { /* max 10 msec */ pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &lock); if (lock&CLKCTL_LOCK) { if (++count >= 3) break; } udelay(50); } if (count < 3) { if (clksrc == 1) { /* explicitly asked for PLL hence bail out */ printk(KERN_ERR "%s: no PLL or failed to lock!\n", __FUNCTION__); clkctl = CLKCTL_CLKSTP; pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); return -1; } else /* was: clksrc=0(auto) */ clksrc = 3; /* fallback to 40MHz XCLK (OB800) */ printk(KERN_INFO "%s: PLL not locked, fallback to clksrc=%d\n", __FUNCTION__, clksrc); } else { /* got successful PLL lock */ clksrc = 1; return 0; } } /* we get here if either no PLL detected in auto-mode or the external clock source was explicitly specified */ clkctl = CLKCTL_EXTCLK | CLKCTL_CLKSTP; if (clksrc == 3) clkctl |= CLKCTL_XCKSEL; pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); /* no way to test for working XCLK */ return 0;}static void vlsi_start_clock(struct pci_dev *pdev){ u8 clkctl; printk(KERN_INFO "%s: start clock using %s as input\n", __FUNCTION__, (clksrc&2)?((clksrc&1)?"40MHz XCLK":"48MHz XCLK"):"48MHz PLL"); pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &clkctl); clkctl &= ~CLKCTL_CLKSTP; pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl);} static void vlsi_stop_clock(struct pci_dev *pdev){ u8 clkctl; pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &clkctl); clkctl |= CLKCTL_CLKSTP; pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl);} static void vlsi_unset_clock(struct pci_dev *pdev){ u8 clkctl; pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &clkctl); if (!(clkctl&CLKCTL_CLKSTP)) /* make sure clock is already stopped */ vlsi_stop_clock(pdev); clkctl &= ~(CLKCTL_EXTCLK | CLKCTL_NO_PD); pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl);}/********************************************************//* ### FIXME: don't use old virt_to_bus() anymore! */static void vlsi_arm_rx(struct vlsi_ring *r){ unsigned i; dma_addr_t ba; for (i = 0; i < r->size; i++) { if (r->buf[i].data == NULL) BUG(); ba = virt_to_bus(r->buf[i].data); rd_set_addr_status(r, i, ba, RD_STAT_ACTIVE); }}static int vlsi_alloc_ringbuf(struct vlsi_ring *r){ unsigned i, j; r->head = r->tail = 0; r->mask = r->size - 1; for (i = 0; i < r->size; i++) { r->buf[i].skb = NULL; r->buf[i].data = kmalloc(XFER_BUF_SIZE, GFP_KERNEL|GFP_DMA); if (r->buf[i].data == NULL) { for (j = 0; j < i; j++) { kfree(r->buf[j].data); r->buf[j].data = NULL; } return -ENOMEM; } } return 0;}static void vlsi_free_ringbuf(struct vlsi_ring *r){ unsigned i; for (i = 0; i < r->size; i++) { if (r->buf[i].data == NULL) continue; if (r->buf[i].skb) { dev_kfree_skb(r->buf[i].skb); r->buf[i].skb = NULL; } else kfree(r->buf[i].data); r->buf[i].data = NULL; }}static int vlsi_init_ring(vlsi_irda_dev_t *idev){ char *ringarea; ringarea = pci_alloc_consistent(idev->pdev, RING_AREA_SIZE, &idev->busaddr); if (!ringarea) { printk(KERN_ERR "%s: insufficient memory for descriptor rings\n", __FUNCTION__); return -ENOMEM; } memset(ringarea, 0, RING_AREA_SIZE);#if 0 printk(KERN_DEBUG "%s: (%d,%d)-ring %p / %p\n", __FUNCTION__, ringsize[0], ringsize[1], ringarea, (void *)(unsigned)idev->busaddr);#endif idev->rx_ring.size = ringsize[1]; idev->rx_ring.hw = (struct ring_descr *)ringarea; if (!vlsi_alloc_ringbuf(&idev->rx_ring)) { idev->tx_ring.size = ringsize[0]; idev->tx_ring.hw = idev->rx_ring.hw + MAX_RING_DESCR; if (!vlsi_alloc_ringbuf(&idev->tx_ring)) { idev->virtaddr = ringarea; return 0; } vlsi_free_ringbuf(&idev->rx_ring); } pci_free_consistent(idev->pdev, RING_AREA_SIZE, ringarea, idev->busaddr); printk(KERN_ERR "%s: insufficient memory for ring buffers\n", __FUNCTION__); return -1;}/********************************************************/static int vlsi_set_baud(struct net_device *ndev){ vlsi_irda_dev_t *idev = ndev->priv; unsigned long flags; u16 nphyctl; unsigned iobase; u16 config; unsigned mode; int ret; int baudrate; baudrate = idev->new_baud; iobase = ndev->base_addr; printk(KERN_DEBUG "%s: %d -> %d\n", __FUNCTION__, idev->baud, idev->new_baud); spin_lock_irqsave(&idev->lock, flags); outw(0, iobase+VLSI_PIO_IRENABLE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -