📄 au1k_ir.c
字号:
/* * Alchemy Semi Au1000 IrDA driver * * Copyright 2001 MontaVista Software Inc. * Author: MontaVista Software, Inc. * ppopov@mvista.com or source@mvista.com * * This program is free software; you can distribute it and/or modify it * under the terms of the GNU General Public License (Version 2) as * published by the Free Software Foundation. * * This program is distributed in the hope 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/types.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/netdevice.h>#include <linux/slab.h>#include <linux/rtnetlink.h>#include <linux/interrupt.h>#include <linux/pm.h>#include <linux/bitops.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/au1000.h>#if defined(CONFIG_MIPS_PB1000) || defined(CONFIG_MIPS_PB1100)#include <asm/pb1000.h>#elif defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100)#include <asm/db1x00.h>#else #error au1k_ir: unsupported board#endif#include <net/irda/irda.h>#include <net/irda/irmod.h>#include <net/irda/wrapper.h>#include <net/irda/irda_device.h>#include "au1000_ircc.h"static int au1k_irda_net_init(struct net_device *);static int au1k_irda_start(struct net_device *);static int au1k_irda_stop(struct net_device *dev);static int au1k_irda_hard_xmit(struct sk_buff *, struct net_device *);static int au1k_irda_rx(struct net_device *);static void au1k_irda_interrupt(int, void *);static void au1k_tx_timeout(struct net_device *);static struct net_device_stats *au1k_irda_stats(struct net_device *);static int au1k_irda_ioctl(struct net_device *, struct ifreq *, int);static int au1k_irda_set_speed(struct net_device *dev, int speed);static void *dma_alloc(size_t, dma_addr_t *);static void dma_free(void *, size_t);static int qos_mtt_bits = 0x07; /* 1 ms or more */static struct net_device *ir_devs[NUM_IR_IFF];static char version[] __devinitdata = "au1k_ircc:1.2 ppopov@mvista.com\n";#define RUN_AT(x) (jiffies + (x))#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100)static BCSR * const bcsr = (BCSR *)0xAE000000;#endifstatic DEFINE_SPINLOCK(ir_lock);/* * IrDA peripheral bug. You have to read the register * twice to get the right value. */u32 read_ir_reg(u32 addr) { readl(addr); return readl(addr);}/* * Buffer allocation/deallocation routines. The buffer descriptor returned * has the virtual and dma address of a buffer suitable for * both, receive and transmit operations. */static db_dest_t *GetFreeDB(struct au1k_private *aup){ db_dest_t *pDB; pDB = aup->pDBfree; if (pDB) { aup->pDBfree = pDB->pnext; } return pDB;}static void ReleaseDB(struct au1k_private *aup, db_dest_t *pDB){ db_dest_t *pDBfree = aup->pDBfree; if (pDBfree) pDBfree->pnext = pDB; aup->pDBfree = pDB;}/* DMA memory allocation, derived from pci_alloc_consistent. However, the Au1000 data cache is coherent (when programmed so), therefore we return KSEG0 address, not KSEG1.*/static void *dma_alloc(size_t size, dma_addr_t * dma_handle){ void *ret; int gfp = GFP_ATOMIC | GFP_DMA; ret = (void *) __get_free_pages(gfp, get_order(size)); if (ret != NULL) { memset(ret, 0, size); *dma_handle = virt_to_bus(ret); ret = (void *)KSEG0ADDR(ret); } return ret;}static void dma_free(void *vaddr, size_t size){ vaddr = (void *)KSEG0ADDR(vaddr); free_pages((unsigned long) vaddr, get_order(size));}static void setup_hw_rings(struct au1k_private *aup, u32 rx_base, u32 tx_base){ int i; for (i=0; i<NUM_IR_DESC; i++) { aup->rx_ring[i] = (volatile ring_dest_t *) (rx_base + sizeof(ring_dest_t)*i); } for (i=0; i<NUM_IR_DESC; i++) { aup->tx_ring[i] = (volatile ring_dest_t *) (tx_base + sizeof(ring_dest_t)*i); }}static int au1k_irda_init(void){ static unsigned version_printed = 0; struct au1k_private *aup; struct net_device *dev; int err; if (version_printed++ == 0) printk(version); dev = alloc_irdadev(sizeof(struct au1k_private)); if (!dev) return -ENOMEM; dev->irq = AU1000_IRDA_RX_INT; /* TX has its own interrupt */ err = au1k_irda_net_init(dev); if (err) goto out; err = register_netdev(dev); if (err) goto out1; ir_devs[0] = dev; printk(KERN_INFO "IrDA: Registered device %s\n", dev->name); return 0;out1: aup = netdev_priv(dev); dma_free((void *)aup->db[0].vaddr, MAX_BUF_SIZE * 2*NUM_IR_DESC); dma_free((void *)aup->rx_ring[0], 2 * MAX_NUM_IR_DESC*(sizeof(ring_dest_t))); kfree(aup->rx_buff.head);out: free_netdev(dev); return err;}static int au1k_irda_init_iobuf(iobuff_t *io, int size){ io->head = kmalloc(size, GFP_KERNEL); if (io->head != NULL) { io->truesize = size; io->in_frame = FALSE; io->state = OUTSIDE_FRAME; io->data = io->head; } return io->head ? 0 : -ENOMEM;}static int au1k_irda_net_init(struct net_device *dev){ struct au1k_private *aup = netdev_priv(dev); int i, retval = 0, err; db_dest_t *pDB, *pDBfree; dma_addr_t temp; err = au1k_irda_init_iobuf(&aup->rx_buff, 14384); if (err) goto out1; dev->open = au1k_irda_start; dev->hard_start_xmit = au1k_irda_hard_xmit; dev->stop = au1k_irda_stop; dev->get_stats = au1k_irda_stats; dev->do_ioctl = au1k_irda_ioctl; dev->tx_timeout = au1k_tx_timeout; irda_init_max_qos_capabilies(&aup->qos); /* The only value we must override it the baudrate */ aup->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| IR_115200|IR_576000 |(IR_4000000 << 8); aup->qos.min_turn_time.bits = qos_mtt_bits; irda_qos_bits_to_value(&aup->qos); retval = -ENOMEM; /* Tx ring follows rx ring + 512 bytes */ /* we need a 1k aligned buffer */ aup->rx_ring[0] = (ring_dest_t *) dma_alloc(2*MAX_NUM_IR_DESC*(sizeof(ring_dest_t)), &temp); if (!aup->rx_ring[0]) goto out2; /* allocate the data buffers */ aup->db[0].vaddr = (void *)dma_alloc(MAX_BUF_SIZE * 2*NUM_IR_DESC, &temp); if (!aup->db[0].vaddr) goto out3; setup_hw_rings(aup, (u32)aup->rx_ring[0], (u32)aup->rx_ring[0] + 512); pDBfree = NULL; pDB = aup->db; for (i=0; i<(2*NUM_IR_DESC); i++) { pDB->pnext = pDBfree; pDBfree = pDB; pDB->vaddr = (u32 *)((unsigned)aup->db[0].vaddr + MAX_BUF_SIZE*i); pDB->dma_addr = (dma_addr_t)virt_to_bus(pDB->vaddr); pDB++; } aup->pDBfree = pDBfree; /* attach a data buffer to each descriptor */ for (i=0; i<NUM_IR_DESC; i++) { pDB = GetFreeDB(aup); if (!pDB) goto out; aup->rx_ring[i]->addr_0 = (u8)(pDB->dma_addr & 0xff); aup->rx_ring[i]->addr_1 = (u8)((pDB->dma_addr>>8) & 0xff); aup->rx_ring[i]->addr_2 = (u8)((pDB->dma_addr>>16) & 0xff); aup->rx_ring[i]->addr_3 = (u8)((pDB->dma_addr>>24) & 0xff); aup->rx_db_inuse[i] = pDB; } for (i=0; i<NUM_IR_DESC; i++) { pDB = GetFreeDB(aup); if (!pDB) goto out; aup->tx_ring[i]->addr_0 = (u8)(pDB->dma_addr & 0xff); aup->tx_ring[i]->addr_1 = (u8)((pDB->dma_addr>>8) & 0xff); aup->tx_ring[i]->addr_2 = (u8)((pDB->dma_addr>>16) & 0xff); aup->tx_ring[i]->addr_3 = (u8)((pDB->dma_addr>>24) & 0xff); aup->tx_ring[i]->count_0 = 0; aup->tx_ring[i]->count_1 = 0; aup->tx_ring[i]->flags = 0; aup->tx_db_inuse[i] = pDB; }#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) /* power on */ bcsr->resets &= ~BCSR_RESETS_IRDA_MODE_MASK; bcsr->resets |= BCSR_RESETS_IRDA_MODE_FULL; au_sync();#endif return 0;out3: dma_free((void *)aup->rx_ring[0], 2 * MAX_NUM_IR_DESC*(sizeof(ring_dest_t)));out2: kfree(aup->rx_buff.head);out1: printk(KERN_ERR "au1k_init_module failed. Returns %d\n", retval); return retval;}static int au1k_init(struct net_device *dev){ struct au1k_private *aup = netdev_priv(dev); int i; u32 control; u32 ring_address; /* bring the device out of reset */ control = 0xe; /* coherent, clock enable, one half system clock */ #ifndef CONFIG_CPU_LITTLE_ENDIAN control |= 1;#endif aup->tx_head = 0; aup->tx_tail = 0; aup->rx_head = 0; for (i=0; i<NUM_IR_DESC; i++) { aup->rx_ring[i]->flags = AU_OWN; } writel(control, IR_INTERFACE_CONFIG); au_sync_delay(10); writel(read_ir_reg(IR_ENABLE) & ~0x8000, IR_ENABLE); /* disable PHY */ au_sync_delay(1); writel(MAX_BUF_SIZE, IR_MAX_PKT_LEN); ring_address = (u32)virt_to_phys((void *)aup->rx_ring[0]); writel(ring_address >> 26, IR_RING_BASE_ADDR_H); writel((ring_address >> 10) & 0xffff, IR_RING_BASE_ADDR_L); writel(RING_SIZE_64<<8 | RING_SIZE_64<<12, IR_RING_SIZE); writel(1<<2 | IR_ONE_PIN, IR_CONFIG_2); /* 48MHz */ writel(0, IR_RING_ADDR_CMPR); au1k_irda_set_speed(dev, 9600); return 0;}static int au1k_irda_start(struct net_device *dev){ int retval; char hwname[32]; struct au1k_private *aup = netdev_priv(dev); if ((retval = au1k_init(dev))) { printk(KERN_ERR "%s: error in au1k_init\n", dev->name); return retval; } if ((retval = request_irq(AU1000_IRDA_TX_INT, &au1k_irda_interrupt, 0, dev->name, dev))) { printk(KERN_ERR "%s: unable to get IRQ %d\n", dev->name, dev->irq); return retval; } if ((retval = request_irq(AU1000_IRDA_RX_INT, &au1k_irda_interrupt, 0, dev->name, dev))) { free_irq(AU1000_IRDA_TX_INT, dev); printk(KERN_ERR "%s: unable to get IRQ %d\n", dev->name, dev->irq); return retval; } /* Give self a hardware name */ sprintf(hwname, "Au1000 SIR/FIR"); aup->irlap = irlap_open(dev, &aup->qos, hwname); netif_start_queue(dev); writel(read_ir_reg(IR_CONFIG_2) | 1<<8, IR_CONFIG_2); /* int enable */ aup->timer.expires = RUN_AT((3*HZ)); aup->timer.data = (unsigned long)dev; return 0;}static int au1k_irda_stop(struct net_device *dev){ struct au1k_private *aup = netdev_priv(dev); /* disable interrupts */ writel(read_ir_reg(IR_CONFIG_2) & ~(1<<8), IR_CONFIG_2); writel(0, IR_CONFIG_1); writel(0, IR_INTERFACE_CONFIG); /* disable clock */ au_sync(); if (aup->irlap) { irlap_close(aup->irlap); aup->irlap = NULL; } netif_stop_queue(dev); del_timer(&aup->timer); /* disable the interrupt */ free_irq(AU1000_IRDA_TX_INT, dev); free_irq(AU1000_IRDA_RX_INT, dev); return 0;}static void __exit au1k_irda_exit(void){ struct net_device *dev = ir_devs[0]; struct au1k_private *aup = netdev_priv(dev); unregister_netdev(dev); dma_free((void *)aup->db[0].vaddr, MAX_BUF_SIZE * 2*NUM_IR_DESC); dma_free((void *)aup->rx_ring[0], 2 * MAX_NUM_IR_DESC*(sizeof(ring_dest_t))); kfree(aup->rx_buff.head); free_netdev(dev);}static inline void update_tx_stats(struct net_device *dev, u32 status, u32 pkt_len){ struct au1k_private *aup = netdev_priv(dev); struct net_device_stats *ps = &aup->stats; ps->tx_packets++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -