📄 omap24xx-ir.c
字号:
/* * BRIEF MODULE DESCRIPTION * * Infra-red driver for the OMAP24XX Platform * (SIR/MIR/FIR modes) * (based on omap-sir.c) * * Copyright 2003 MontaVista Software Inc. * Author: MontaVista Software, Inc. * source@mvista.com * * Copyright 2004 Texas Instruments. * * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * 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., * 675 Mass Ave, Cambridge, MA 02139, USA. * Modifications: Feb 2004, Texas Instruments - Ported to 2.6 kernel (Feb 2004). * Apr 2004, Texas Instruments - Added support for H3 (Apr 2004). * May 2004, Texas Instruments - Added support for H4 (May 2004). */#include <linux/config.h>#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/delay.h>#include <linux/ioport.h>#include <linux/dma-mapping.h>#include <net/irda/irda.h>#include <net/irda/irmod.h>#include <net/irda/wrapper.h>#include <net/irda/irda_device.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/hardware.h>#include <asm/serial.h>#include <asm/mach-types.h>#include <asm/dma.h>#include <asm/arch/gpio.h>#include <linux/i2c.h>#include <asm/arch/mux.h>#include "omap24xx-ir.h"#define SIR_MODE 0#define MIR_MODE 1#define FIR_MODE 2/* * Our netdevice. There is only ever one of these. */unsigned long uart3_base;static struct net_device *netdev; /* Our net device */static int rx_state = 0; /* RX state for IOCTL */struct omap24xx_irda { unsigned char open; int speed; /* Current IrDA speed */ int newspeed; struct net_device_stats stats; struct irlap_cb *irlap; struct qos_info qos; int rx_dma_channel; int tx_dma_channel; dma_addr_t rx_buf_dma_phys; /* Physical adress of RX DMA buffer */ dma_addr_t tx_buf_dma_phys; /* Physical adress of TX DMA buffer */ void *rx_buf_dma_virt; /* Virtual adress of RX DMA buffer */ void *tx_buf_dma_virt; /* Virtual adress of TX DMA buffer */ struct device *dev;};/* Functions to read and write to the I/O expanders */int read_gpio_expa(u8 * val, int addr);int write_gpio_expa(u8 val, int addr);/* * If you want to disable debug information * please uncomment line bellow *///#define OMAP24XX_IR_DEBUG_ENABLE #undef OMAP24XX_IR_DEBUG_ENABLE#ifdef OMAP24XX_IR_DEBUG_ENABLE#define __ECHO_IN printk(KERN_ERR "%s: enter\n",__FUNCTION__);#define __ECHO_OUT printk(KERN_ERR "%s: exit\n",__FUNCTION__);#define DBG(args...) printk(KERN_ERR __FUNCTION__"():"args);#else#define __ECHO_IN#define __ECHO_OUT#define DBG(args...)#endif#define DBG_IRQ(args...) printk(KERN_ERR __FUNCTION__"():"args);/* forward declarations */#define BASE_CLK 48000000static int omap24xx_irda_set_speed(struct net_device *dev, int speed);static void omap24xx_irda_start_rx_dma(struct omap24xx_irda *si){ /* Configure DMA */ omap_set_dma_src_params(si->rx_dma_channel, 0x00, OMAP2420_UART3_BASE, 0, 0); omap_set_dma_dest_params(si->rx_dma_channel, 0x01, si->rx_buf_dma_phys, 0, 0); omap_set_dma_transfer_params(si->rx_dma_channel, OMAP_DMA_DATA_TYPE_S8, 4096, 1, 0x00, OMAP24XX_TRIGGER_RX, 0); omap_start_dma(si->rx_dma_channel);}static void omap24xx_start_tx_dma(struct omap24xx_irda *si, int size){ omap_set_dma_dest_params(si->tx_dma_channel, 0x0, OMAP2420_UART3_BASE, 0, 0); omap_set_dma_src_params(si->tx_dma_channel, 0x01, si->tx_buf_dma_phys, 0, 0); omap_set_dma_transfer_params(si->tx_dma_channel, OMAP_DMA_DATA_TYPE_S8, size, 1, 0x00, OMAP24XX_TRIGGER_TX, 0); omap_start_dma(si->tx_dma_channel);}/* DMA RX callback - normally, we should not go here, it calls only if something is going wrong */staticvoid omap24xx_irda_rx_dma_callback(int lch, u16 ch_status, void *data){ struct net_device *dev = data; struct omap24xx_irda *si = dev->priv; printk(KERN_ERR "RX Transfer error or very big frame \n"); /* Clear interrupts */ readb(UART3_IIR); si->stats.rx_frame_errors++; readb(UART3_RESUME); /* Re-init RX DMA */ omap24xx_irda_start_rx_dma(si);}/* DMA TX callback - calling when frame transfer has been finished */staticvoid omap24xx_irda_tx_dma_callback(int lch, u16 ch_status, void *data){ struct net_device *dev = data; struct omap24xx_irda *si = dev->priv; /*Stop DMA controller */ omap_stop_dma(si->tx_dma_channel);}/* * Set the IrDA communications speed. * Interrupt have to be disabled here. */staticint omap24xx_irda_startup(struct net_device *dev){ __ECHO_IN; writeb(0x07, UART3_MDR1); /* Put UART3 in reset mode */ /* Clear DLH and DLL */ writeb(1 << 7, UART3_LCR); writeb(0, UART3_DLL); writeb(0, UART3_DLH); writeb(0xbf, UART3_LCR); writeb(1 << 4, UART3_EFR); writeb(1 << 7, UART3_LCR); /* Enable access to UART3_TLR and UART3_TCR registers */ writeb(1 << 6, UART3_MCR); writeb(0, UART3_SCR); /* Set Rx trigger to 1 and Tx trigger to 1 */ writeb(0, UART3_TLR); /* Set LCR to 8 bits and 1 stop bit */ writeb(0x03, UART3_LCR); /* Clear RX and TX FIFO and enable FIFO */ /* Use DMA Req for transfers */ writeb((1 << 2) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 6) | 1, UART3_FCR); writeb(0, UART3_MCR); writeb((1 << 7) | (1 << 6), UART3_SCR); /* Enable UART3 SIR Mode,(Frame-length method to end frames) */ writeb(1, UART3_MDR1); /* Set Status FIFO trig to 1 */ writeb(0, UART3_MDR2); /* Enables RXIR input */ /* and disable TX underrun */ /* SEND_SIP pulse */ // writeb((1 << 7) | (1 << 6) | (1 << 4), UART3_ACREG); writeb((1 << 6) | (1 << 4), UART3_ACREG); /* Enable EOF Interrupt only */ writeb((1 << 7) | (1 << 5), UART3_IER); /* Set Maximum Received Frame size to 2048 bytes */ writeb(0x00, UART3_RXFLL); writeb(0x08, UART3_RXFLH); readb(UART3_RESUME); __ECHO_OUT; return 0;}staticint omap24xx_irda_shutdown(struct omap24xx_irda *si){ /* Disable all UART3 Interrupts */ writeb(0, UART3_IER); /* Disable UART3 and disable baud rate generator */ writeb(0x07, UART3_MDR1); /* Put UART3 in reset mode */ writeb((1 << 5), UART3_ACREG); /* set SD_MODE pin to high and Disable RX IR */ /* Clear DLH and DLL */ writeb(1 << 7, UART3_LCR); writeb(0, UART3_DLL); writeb(0, UART3_DLH); return 0;}static irqreturn_tomap24xx_irda_irq(int irq, void *dev_id, struct pt_regs *hw_regs){ struct net_device *dev = dev_id; struct omap24xx_irda *si = dev->priv; struct sk_buff *skb; u8 status; int w = 0; __ECHO_IN; /* Clear EOF interrupt */ status = readb(UART3_IIR); if (status & (1 << 5)) { u8 mdr2 = readb(UART3_MDR2); if (mdr2 & 1) printk(KERN_ERR "IRDA Buffer underrun error\n"); si->stats.tx_packets++; if (si->newspeed) { omap24xx_irda_set_speed(dev, si->newspeed); si->newspeed = 0; } netif_wake_queue(dev); if (!(status & 0x80)) return IRQ_HANDLED; } /* Stop DMA and if there are no errors, send frame to upper layer */ omap_stop_dma(si->rx_dma_channel); status = readb(UART3_SFLSR); /* Take a frame status */ if (status != 0) { /* Bad frame? */ si->stats.rx_frame_errors++; readb(UART3_RESUME); } else { /* We got a frame! */ skb = alloc_skb(4096, GFP_ATOMIC); if (!skb) { printk(KERN_ERR "omap_sir: out of memory for RX SKB\n"); return IRQ_HANDLED; } /* * Align any IP headers that may be contained * within the frame. */ skb_reserve(skb, 1); w = readw(OMAP_DMA4_CDAC_REG(si->rx_dma_channel)); w -= readw(OMAP_DMA4_CDSA_REG(si->rx_dma_channel)); if (si->speed != 4000000) { memcpy(skb_put(skb, w - 2), si->rx_buf_dma_virt, w - 2); /* Copy DMA buffer to skb */ } else { memcpy(skb_put(skb, w - 4), si->rx_buf_dma_virt, w - 4); /* Copy DMA buffer to skb */ } skb->dev = dev; skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IRDA); si->stats.rx_packets++; si->stats.rx_bytes += skb->len; netif_receive_skb(skb); /* Send data to upper level */ } /* Re-init RX DMA */ omap24xx_irda_start_rx_dma(si); dev->last_rx = jiffies; __ECHO_OUT; return IRQ_HANDLED;}staticint omap24xx_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev){ struct omap24xx_irda *si = dev->priv; int speed = irda_get_next_speed(skb); int mtt = irda_get_mtt(skb); int xbofs = irda_get_next_xbofs(skb); __ECHO_IN; /* * Does this packet contain a request to change the interface * speed? If so, remember it until we complete the transmission * of this frame. */ if (speed != si->speed && speed != -1) si->newspeed = speed; if (xbofs) { /* Set number of addtional BOFS */ writeb(xbofs + 1, UART3_EBLR); } /* * If this is an empty frame, we can bypass a lot. */ if (skb->len == 0) { if (si->newspeed) { si->newspeed = 0; omap24xx_irda_set_speed(dev, speed); } dev_kfree_skb(skb); return 0; } netif_stop_queue(dev); /* Copy skb data to DMA buffer */ memcpy(si->tx_buf_dma_virt, skb->data, skb->len); si->stats.tx_bytes += skb->len; /* Set frame length */ writeb((skb->len & 0xff), UART3_TXFLL); writeb((skb->len >> 8), UART3_TXFLH); if (mtt > 1000) mdelay(mtt / 1000); else udelay(mtt); /* Start TX DMA transfer */ omap24xx_start_tx_dma(si, skb->len); /* We can free skb now because it's already in DMA buffer */ dev_kfree_skb(skb); dev->trans_start = jiffies; __ECHO_OUT; return 0;}static intomap24xx_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd){ struct if_irda_req *rq = (struct if_irda_req *)ifreq; struct omap24xx_irda *si = dev->priv; int ret = -EOPNOTSUPP; __ECHO_IN; switch (cmd) { case SIOCSBANDWIDTH: if (capable(CAP_NET_ADMIN)) { /* * We are unable to set the speed if the * device is not running. */ if (si->open) { ret = omap24xx_irda_set_speed(dev, rq->ifr_baudrate); } else { printk (KERN_ERR "omap_irda_ioctl: SIOCSBANDWIDTH: !netif_running\n"); ret = 0; } } break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -