Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751140AbWH0Uk6 (ORCPT ); Sun, 27 Aug 2006 16:40:58 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1751151AbWH0Uk6 (ORCPT ); Sun, 27 Aug 2006 16:40:58 -0400 Received: from smtp4.pp.htv.fi ([213.243.153.38]:34256 "EHLO smtp4.pp.htv.fi") by vger.kernel.org with ESMTP id S1751140AbWH0Uk5 (ORCPT ); Sun, 27 Aug 2006 16:40:57 -0400 Date: Mon, 28 Aug 2006 06:54:26 +0300 From: Samuel Ortiz To: Komal Shah Cc: linux-kernel@vger.kernel.org, tony@atomide.com, irda-users@lists.sourceforge.net Subject: Re: [irda-users] [PATCH] OMAP: Add support for IrDA driver #2 Message-ID: <20060828035426.GB3819@sortiz.org> Reply-To: Samuel Ortiz References: <1155721390.31825.268493979@webmail.messagingengine.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <1155721390.31825.268493979@webmail.messagingengine.com> User-Agent: Mutt/1.5.12-2006-07-14 Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 28128 Lines: 1010 Hi Komal, On Wed, Aug 16, 2006 at 03:13:10PM +0530, Komal Shah wrote: > Samuel/Tony, > > I have attached the updated OMAP IrDA patch for OMAP1610/1710 and OMAP242x > for review. See that for IrDA driver to work on H3 and H4, we need > gpio-expander functions to go in mainline, but it can work on H2. > > I have addressed your review comments as per the following thread, This looks good to me. Acked-by: Samuel Ortiz Cheers, Samuel. > http://lkml.org/lkml/2006/8/10/556 > > except for dma channels. > > ---Komal Shah > http://komalshah.blogspot.com > > -- > http://www.fastmail.fm - Send your email first class > > From nobody Mon Sep 17 00:00:00 2001 > From: Komal Shah > Date: Wed, 16 Aug 2006 20:36:29 +0530 > Subject: [PATCH] OMAP: Add support for IrDA driver #2 > > Patch adds IrDA driver support for Texas Instruments OMAP > (http://www.ti.com/omap) based processors like OMAP1610/1710 and OMAP242x. > > Signed-off-by: Komal Shah > > --- > > drivers/net/irda/Kconfig | 10 > drivers/net/irda/Makefile | 1 > drivers/net/irda/omap-ir.c | 904 ++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 915 insertions(+), 0 deletions(-) > create mode 100644 drivers/net/irda/omap-ir.c > > 27da3dbdbd7eb4f22237e4e452d08df929010c7c > diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig > index e9e6d99..88f2c66 100644 > --- a/drivers/net/irda/Kconfig > +++ b/drivers/net/irda/Kconfig > @@ -432,5 +432,15 @@ config MCS_FIR > To compile it as a module, choose M here: the module will be called > mcs7780. > > +config OMAP_IR > + tristate "OMAP IrDA(SIR/MIR/FIR)" > + depends on IRDA && ARCH_OMAP > + select GPIOEXPANDER_OMAP if (MACH_OMAP_H3 || MACH_OMAP_H4) > + help > + Say Y here if you want to build support for the Texas Instruments > + OMAP IrDA device driver, which supports SIR/MIR/FIR. This driver > + relies on platform specific helper routines so available capabilities > + may vary from one OMAP target to another. > + > endmenu > > diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile > index 5be09f1..89c6389 100644 > --- a/drivers/net/irda/Makefile > +++ b/drivers/net/irda/Makefile > @@ -20,6 +20,7 @@ obj-$(CONFIG_VLSI_FIR) += vlsi_ir.o > obj-$(CONFIG_VIA_FIR) += via-ircc.o > obj-$(CONFIG_PXA_FICP) += pxaficp_ir.o > obj-$(CONFIG_MCS_FIR) += mcs7780.o > +obj-$(CONFIG_OMAP_IR) += omap-ir.o > # Old dongle drivers for old SIR drivers > obj-$(CONFIG_ESI_DONGLE_OLD) += esi.o > obj-$(CONFIG_TEKRAM_DONGLE_OLD) += tekram.o > diff --git a/drivers/net/irda/omap-ir.c b/drivers/net/irda/omap-ir.c > new file mode 100644 > index 0000000..e3dc0d6 > --- /dev/null > +++ b/drivers/net/irda/omap-ir.c > @@ -0,0 +1,904 @@ > +/* > + * BRIEF MODULE DESCRIPTION > + * > + * Infra-red driver for the OMAP1610-H2 and OMAP1710-H3 and H4 Platforms > + * (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). > + Nov 2004, Texas Instruments > + - Added support for Power Management. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define UART3_EFR_EN (1 << 4) > +#define UART3_MCR_EN_TCR_TLR (1 << 6) > + > +#define UART3_LCR_WL_8 (3 << 0) > +#define UART3_LCR_SP2 (1 << 2) > +#define UART3_LCR_DIVEN (1 << 7) > + > +#define UART3_FCR_FIFO_EN (1 << 0) > +#define UART3_FCR_FIFO_RX (1 << 1) > +#define UART3_FCR_FIFO_TX (1 << 2) > +#define UART3_FCR_FIFO_DMA1 (1 << 3) > +#define UART3_FCR_FIFO_TX_TRIG16 (1 << 4) > +#define UART3_FCR_FIFO_RX_TRIG16 (1 << 6) > +#define UART3_FCR_CONFIG (\ > + UART3_FCR_FIFO_EN | UART3_FCR_FIFO_RX |\ > + UART3_FCR_FIFO_TX | UART3_FCR_FIFO_DMA1 |\ > + UART3_FCR_FIFO_TX_TRIG16 |\ > + UART3_FCR_FIFO_RX_TRIG16) > + > +#define UART3_SCR_TX_TRIG1 (1 << 6) > +#define UART3_SCR_RX_TRIG1 (1 << 7) > + > +#define UART3_MDR1_RESET (0x07) > +#define UART3_MDR1_SIR (1 << 0) > +#define UART3_MDR1_MIR (4 << 0) > +#define UART3_MDR1_FIR (5 << 0) > +#define UART3_MDR1_SIP_AUTO (1 << 6) > + > +#define UART3_MDR2_TRIG1 (0 << 1) > +#define UART3_MDR2_IRTX_UNDERRUN (1 << 0) > + > +#define UART3_ACERG_TX_UNDERRUN_DIS (1 << 4) > +#define UART3_ACERG_SD_MODE_LOW (1 << 6) > +#define UART3_ACERG_DIS_IR_RX (1 << 5) > + > +#define UART3_IER_EOF (1 << 5) > +#define UART3_IER_CTS (1 << 7) > + > +#define UART3_IIR_TX_STATUS (1 << 5) > +#define UART3_IIR_EOF (0x80) > + > +#define IS_FIR(omap_ir) ((omap_ir)->speed >= 4000000) > + > +struct omap_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 address of RX DMA buffer */ > + dma_addr_t tx_buf_dma_phys; /* Physical address of TX DMA buffer */ > + > + void *rx_buf_dma_virt; /* Virtual address of RX DMA buffer */ > + void *tx_buf_dma_virt; /* Virtual address of TX DMA buffer */ > + > + struct device *dev; > + struct omap_irda_config *pdata; > +}; > + > +static void inline uart_reg_out(int idx, u8 val) > +{ > + omap_writeb(val, idx); > +} > + > +static u8 inline uart_reg_in(int idx) > +{ > + u8 b = omap_readb(idx); > + return b; > +} > + > +/* forward declarations */ > +extern void omap_stop_dma(int lch); > +static int omap_irda_set_speed(struct net_device *dev, int speed); > + > +static void omap_irda_start_rx_dma(struct omap_irda *omap_ir) > +{ > + /* Configure DMA */ > + omap_set_dma_src_params(omap_ir->rx_dma_channel, 0x3, 0x0, > + omap_ir->pdata->src_start, > + 0, 0); > + > + omap_enable_dma_irq(omap_ir->rx_dma_channel, 0x01); > + > + omap_set_dma_dest_params(omap_ir->rx_dma_channel, 0x0, 0x1, > + omap_ir->rx_buf_dma_phys, > + 0, 0); > + > + omap_set_dma_transfer_params(omap_ir->rx_dma_channel, 0x0, > + IRDA_SKB_MAX_MTU, 0x1, > + 0x0, omap_ir->pdata->rx_trigger, 0); > + > + omap_start_dma(omap_ir->rx_dma_channel); > +} > + > +static void omap_start_tx_dma(struct omap_irda *omap_ir, int size) > +{ > + /* Configure DMA */ > + omap_set_dma_dest_params(omap_ir->tx_dma_channel, 0x03, 0x0, > + omap_ir->pdata->dest_start, 0, 0); > + > + omap_enable_dma_irq(omap_ir->tx_dma_channel, 0x01); > + > + omap_set_dma_src_params(omap_ir->tx_dma_channel, 0x0, 0x1, > + omap_ir->tx_buf_dma_phys, > + 0, 0); > + > + omap_set_dma_transfer_params(omap_ir->tx_dma_channel, 0x0, size, 0x1, > + 0x0, omap_ir->pdata->tx_trigger, 0); > + > + /* Start DMA */ > + omap_start_dma(omap_ir->tx_dma_channel); > +} > + > +/* DMA RX callback - normally, we should not go here, > + * it calls only if something is going wrong > + */ > +static void omap_irda_rx_dma_callback(int lch, u16 ch_status, void *data) > +{ > + struct net_device *dev = data; > + struct omap_irda *omap_ir = netdev_priv(dev); > + > + printk(KERN_ERR "RX Transfer error or very big frame\n"); > + > + /* Clear interrupts */ > + uart_reg_in(UART3_IIR); > + > + omap_ir->stats.rx_frame_errors++; > + > + uart_reg_in(UART3_RESUME); > + > + /* Re-init RX DMA */ > + omap_irda_start_rx_dma(omap_ir); > +} > + > +/* DMA TX callback - calling when frame transfer has been finished */ > +static void omap_irda_tx_dma_callback(int lch, u16 ch_status, void *data) > +{ > + struct net_device *dev = data; > + struct omap_irda *omap_ir = netdev_priv(dev); > + > + /*Stop DMA controller */ > + omap_stop_dma(omap_ir->tx_dma_channel); > +} > + > +/* > + * Set the IrDA communications speed. > + * Interrupt have to be disabled here. > + */ > +static int omap_irda_startup(struct net_device *dev) > +{ > + struct omap_irda *omap_ir = netdev_priv(dev); > + > + /* FIXME: use clk_* apis for UART3 clock*/ > + /* Enable UART3 clock and set UART3 to IrDA mode */ > + if (machine_is_omap_h2() || machine_is_omap_h3()) > + omap_writel(omap_readl(MOD_CONF_CTRL_0) | (1 << 31) | (1 << 15), > + MOD_CONF_CTRL_0); > + > + /* Only for H2? > + */ > + if (omap_ir->pdata->transceiver_mode && machine_is_omap_h2()) { > + /* Is it select_irda on H2 ? */ > + omap_writel(omap_readl(FUNC_MUX_CTRL_A) | 7, > + FUNC_MUX_CTRL_A); > + omap_ir->pdata->transceiver_mode(omap_ir->dev, IR_SIRMODE); > + } > + > + uart_reg_out(UART3_MDR1, UART3_MDR1_RESET); /* Reset mode */ > + > + /* Clear DLH and DLL */ > + uart_reg_out(UART3_LCR, UART3_LCR_DIVEN); > + > + uart_reg_out(UART3_DLL, 0); > + uart_reg_out(UART3_DLH, 0); > + uart_reg_out(UART3_LCR, 0xbf); /* FIXME: Add #define */ > + > + uart_reg_out(UART3_EFR, UART3_EFR_EN); > + uart_reg_out(UART3_LCR, UART3_LCR_DIVEN); > + > + /* Enable access to UART3_TLR and UART3_TCR registers */ > + uart_reg_out(UART3_MCR, UART3_MCR_EN_TCR_TLR); > + > + uart_reg_out(UART3_SCR, 0); > + /* Set Rx trigger to 1 and Tx trigger to 1 */ > + uart_reg_out(UART3_TLR, 0); > + > + /* Set LCR to 8 bits and 1 stop bit */ > + uart_reg_out(UART3_LCR, 0x03); > + > + /* Clear RX and TX FIFO and enable FIFO */ > + /* Use DMA Req for transfers */ > + uart_reg_out(UART3_FCR, UART3_FCR_CONFIG); > + > + uart_reg_out(UART3_MCR, 0); > + > + uart_reg_out(UART3_SCR, UART3_SCR_TX_TRIG1 | > + UART3_SCR_RX_TRIG1); > + > + /* Enable UART3 SIR Mode,(Frame-length method to end frames) */ > + uart_reg_out(UART3_MDR1, UART3_MDR1_SIR); > + > + /* Set Status FIFO trig to 1 */ > + uart_reg_out(UART3_MDR2, 0); > + > + /* Enables RXIR input */ > + /* and disable TX underrun */ > + /* SEND_SIP pulse */ > + uart_reg_out(UART3_ACREG, UART3_ACERG_SD_MODE_LOW | > + UART3_ACERG_TX_UNDERRUN_DIS); > + > + /* Enable EOF Interrupt only */ > + uart_reg_out(UART3_IER, UART3_IER_CTS | UART3_IER_EOF); > + > + /* Set Maximum Received Frame size to 2048 bytes */ > + uart_reg_out(UART3_RXFLL, 0x00); > + uart_reg_out(UART3_RXFLH, 0x08); > + > + uart_reg_in(UART3_RESUME); > + > + return 0; > +} > + > +static int omap_irda_shutdown(struct omap_irda *omap_ir) > +{ > + unsigned long flags; > + > + local_irq_save(flags); > + > + /* Disable all UART3 Interrupts */ > + uart_reg_out(UART3_IER, 0); > + > + /* Disable UART3 and disable baud rate generator */ > + uart_reg_out(UART3_MDR1, UART3_MDR1_RESET); > + > + /* set SD_MODE pin to high and Disable RX IR */ > + uart_reg_out(UART3_ACREG, (UART3_ACERG_DIS_IR_RX | > + ~(UART3_ACERG_SD_MODE_LOW))); > + > + /* Clear DLH and DLL */ > + uart_reg_out(UART3_LCR, UART3_LCR_DIVEN); > + uart_reg_out(UART3_DLL, 0); > + uart_reg_out(UART3_DLH, 0); > + > + local_irq_restore(flags); > + > + return 0; > +} > + > +static irqreturn_t > +omap_irda_irq(int irq, void *dev_id, struct pt_regs *hw_regs) > +{ > + struct net_device *dev = dev_id; > + struct omap_irda *omap_ir = netdev_priv(dev); > + struct sk_buff *skb; > + > + u8 status; > + int w = 0; > + > + /* Clear EOF interrupt */ > + status = uart_reg_in(UART3_IIR); > + > + if (status & UART3_IIR_TX_STATUS) { > + u8 mdr2 = uart_reg_in(UART3_MDR2); > + if (mdr2 & UART3_MDR2_IRTX_UNDERRUN) > + printk(KERN_ERR "IrDA Buffer underrun error\n"); > + > + omap_ir->stats.tx_packets++; > + > + if (omap_ir->newspeed) { > + omap_irda_set_speed(dev, omap_ir->newspeed); > + omap_ir->newspeed = 0; > + } > + > + netif_wake_queue(dev); > + if (!(status & UART3_IIR_EOF)) > + return IRQ_HANDLED; > + } > + > + /* Stop DMA and if there are no errors, send frame to upper layer */ > + omap_stop_dma(omap_ir->rx_dma_channel); > + > + status = uart_reg_in(UART3_SFLSR); /* Take a frame status */ > + > + if (status != 0) { /* Bad frame? */ > + omap_ir->stats.rx_frame_errors++; > + uart_reg_in(UART3_RESUME); > + } else { > + /* We got a frame! */ > + skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); > + > + 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 = OMAP_DMA_CDAC_REG(omap_ir->rx_dma_channel); > + > + if (cpu_is_omap16xx()) > + w -= OMAP1_DMA_CDSA_L_REG(omap_ir->rx_dma_channel); > + if (cpu_is_omap24xx()) > + w -= OMAP2_DMA_CDSA_REG(omap_ir->rx_dma_channel); > + > + if (!IS_FIR(omap_ir)) > + /* Copy DMA buffer to skb */ > + memcpy(skb_put(skb, w - 2), omap_ir->rx_buf_dma_virt, > + w - 2); > + else > + /* Copy DMA buffer to skb */ > + memcpy(skb_put(skb, w - 4), omap_ir->rx_buf_dma_virt, > + w - 4); > + > + skb->dev = dev; > + skb->mac.raw = skb->data; > + skb->protocol = htons(ETH_P_IRDA); > + omap_ir->stats.rx_packets++; > + omap_ir->stats.rx_bytes += skb->len; > + netif_receive_skb(skb); /* Send data to upper level */ > + } > + > + /* Re-init RX DMA */ > + omap_irda_start_rx_dma(omap_ir); > + > + dev->last_rx = jiffies; > + > + return IRQ_HANDLED; > +} > + > +static int omap_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) > +{ > + struct omap_irda *omap_ir = netdev_priv(dev); > + int speed = irda_get_next_speed(skb); > + int mtt = irda_get_mtt(skb); > + int xbofs = irda_get_next_xbofs(skb); > + > + > + /* > + * 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 != omap_ir->speed && speed != -1) > + omap_ir->newspeed = speed; > + > + if (xbofs) /* Set number of addtional BOFS */ > + uart_reg_out(UART3_EBLR, xbofs + 1); > + > + /* > + * If this is an empty frame, we can bypass a lot. > + */ > + if (skb->len == 0) { > + if (omap_ir->newspeed) { > + omap_ir->newspeed = 0; > + omap_irda_set_speed(dev, speed); > + } > + dev_kfree_skb(skb); > + return 0; > + } > + > + netif_stop_queue(dev); > + > + /* Copy skb data to DMA buffer */ > + memcpy(omap_ir->tx_buf_dma_virt, skb->data, skb->len); > + > + /* Copy skb data to DMA buffer */ > + omap_ir->stats.tx_bytes += skb->len; > + > + /* Set frame length */ > + uart_reg_out(UART3_TXFLL, (skb->len & 0xff)); > + uart_reg_out(UART3_TXFLH, (skb->len >> 8)); > + > + if (mtt > 1000) > + mdelay(mtt / 1000); > + else > + udelay(mtt); > + > + /* Start TX DMA transfer */ > + omap_start_tx_dma(omap_ir, skb->len); > + > + /* We can free skb now because it's already in DMA buffer */ > + dev_kfree_skb(skb); > + > + dev->trans_start = jiffies; > + > + return 0; > +} > + > +static int > +omap_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) > +{ > + struct if_irda_req *rq = (struct if_irda_req *)ifreq; > + struct omap_irda *omap_ir = netdev_priv(dev); > + int ret = -EOPNOTSUPP; > + > + > + switch (cmd) { > + case SIOCSBANDWIDTH: > + if (capable(CAP_NET_ADMIN)) { > + /* > + * We are unable to set the speed if the > + * device is not running. > + */ > + if (omap_ir->open) > + ret = omap_irda_set_speed(dev, > + rq->ifr_baudrate); > + else { > + printk(KERN_ERR "omap_ir: SIOCSBANDWIDTH:" > + " !netif_running\n"); > + ret = 0; > + } > + } > + break; > + > + case SIOCSMEDIABUSY: > + ret = -EPERM; > + if (capable(CAP_NET_ADMIN)) { > + irda_device_set_media_busy(dev, TRUE); > + ret = 0; > + } > + break; > + > + case SIOCGRECEIVING: > + rq->ifr_receiving = 0; > + break; > + > + default: > + break; > + } > + > + return ret; > +} > + > +static struct net_device_stats *omap_irda_stats(struct net_device *dev) > +{ > + struct omap_irda *omap_ir = netdev_priv(dev); > + return &omap_ir->stats; > +} > + > +static int omap_irda_start(struct net_device *dev) > +{ > + struct omap_irda *omap_ir = netdev_priv(dev); > + int err; > + > + omap_ir->speed = 9600; > + > + err = request_irq(dev->irq, omap_irda_irq, 0, dev->name, dev); > + if (err) > + goto err_irq; > + > + /* > + * The interrupt must remain disabled for now. > + */ > + disable_irq(dev->irq); > + > + /* Request DMA channels for IrDA hardware */ > + if (omap_request_dma(omap_ir->pdata->rx_channel, "IrDA Rx DMA", > + (void *)omap_irda_rx_dma_callback, > + dev, &(omap_ir->rx_dma_channel))) { > + printk(KERN_ERR "Failed to request IrDA Rx DMA\n"); > + goto err_irq; > + } > + > + if (omap_request_dma(omap_ir->pdata->tx_channel, "IrDA Tx DMA", > + (void *)omap_irda_tx_dma_callback, > + dev, &(omap_ir->tx_dma_channel))) { > + printk(KERN_ERR "Failed to request IrDA Tx DMA\n"); > + goto err_irq; > + } > + > + /* Allocate TX and RX buffers for DMA channels */ > + omap_ir->rx_buf_dma_virt = > + dma_alloc_coherent(NULL, IRDA_SKB_MAX_MTU, > + &(omap_ir->rx_buf_dma_phys), > + GFP_KERNEL); > + > + if (!omap_ir->rx_buf_dma_virt) { > + printk(KERN_ERR "Unable to allocate memory for rx_buf_dma\n"); > + goto err_irq; > + } > + > + omap_ir->tx_buf_dma_virt = > + dma_alloc_coherent(NULL, IRDA_SIR_MAX_FRAME, > + &(omap_ir->tx_buf_dma_phys), > + GFP_KERNEL); > + > + if (!omap_ir->tx_buf_dma_virt) { > + printk(KERN_ERR "Unable to allocate memory for tx_buf_dma\n"); > + goto err_mem1; > + } > + > + /* > + * Setup the serial port for the specified config. > + */ > + if (omap_ir->pdata->select_irda) > + omap_ir->pdata->select_irda(omap_ir->dev, IR_SEL); > + > + err = omap_irda_startup(dev); > + > + if (err) > + goto err_startup; > + > + omap_irda_set_speed(dev, omap_ir->speed = 9600); > + > + /* > + * Open a new IrLAP layer instance. > + */ > + omap_ir->irlap = irlap_open(dev, &omap_ir->qos, "omap_sir"); > + > + err = -ENOMEM; > + if (!omap_ir->irlap) > + goto err_irlap; > + > + /* Now enable the interrupt and start the queue */ > + omap_ir->open = 1; > + > + /* Start RX DMA */ > + omap_irda_start_rx_dma(omap_ir); > + > + enable_irq(dev->irq); > + netif_start_queue(dev); > + > + return 0; > + > +err_irlap: > + omap_ir->open = 0; > + omap_irda_shutdown(omap_ir); > +err_startup: > + dma_free_coherent(NULL, IRDA_SIR_MAX_FRAME, > + omap_ir->tx_buf_dma_virt, omap_ir->tx_buf_dma_phys); > +err_mem1: > + dma_free_coherent(NULL, IRDA_SKB_MAX_MTU, > + omap_ir->rx_buf_dma_virt, omap_ir->rx_buf_dma_phys); > +err_irq: > + free_irq(dev->irq, dev); > + return err; > +} > + > +static int omap_irda_stop(struct net_device *dev) > +{ > + struct omap_irda *omap_ir = netdev_priv(dev); > + > + disable_irq(dev->irq); > + > + netif_stop_queue(dev); > + > + omap_free_dma(omap_ir->rx_dma_channel); > + omap_free_dma(omap_ir->tx_dma_channel); > + > + if (omap_ir->rx_buf_dma_virt) > + dma_free_coherent(NULL, IRDA_SKB_MAX_MTU, > + omap_ir->rx_buf_dma_virt, > + omap_ir->rx_buf_dma_phys); > + if (omap_ir->tx_buf_dma_virt) > + dma_free_coherent(NULL, IRDA_SIR_MAX_FRAME, > + omap_ir->tx_buf_dma_virt, > + omap_ir->tx_buf_dma_phys); > + > + omap_irda_shutdown(omap_ir); > + > + /* Stop IrLAP */ > + if (omap_ir->irlap) { > + irlap_close(omap_ir->irlap); > + omap_ir->irlap = NULL; > + } > + > + omap_ir->open = 0; > + > + /* > + * Free resources > + */ > + free_irq(dev->irq, dev); > + > + return 0; > +} > + > +static int omap_irda_set_speed(struct net_device *dev, int speed) > +{ > + struct omap_irda *omap_ir = netdev_priv(dev); > + int divisor; > + unsigned long flags; > + > + /* Set IrDA speed */ > + if (speed <= 115200) { > + > + local_irq_save(flags); > + > + /* SIR mode */ > + if (omap_ir->pdata->transceiver_mode) > + omap_ir->pdata->transceiver_mode(omap_ir->dev, > + IR_SIRMODE); > + > + /* Set SIR mode */ > + uart_reg_out(UART3_MDR1, 1); > + uart_reg_out(UART3_EBLR, 1); > + > + divisor = 48000000 / (16 * speed); /* Base clock 48 MHz */ > + > + uart_reg_out(UART3_LCR, UART3_LCR_DIVEN); > + uart_reg_out(UART3_DLL, (divisor & 0xff)); > + uart_reg_out(UART3_DLH, (divisor >> 8)); > + uart_reg_out(UART3_LCR, 0x03); > + > + uart_reg_out(UART3_MCR, 0); > + > + local_irq_restore(flags); > + } else if (speed <= 1152000) { > + > + local_irq_save(flags); > + > + /* Set MIR mode, auto SIP */ > + uart_reg_out(UART3_MDR1, UART3_MDR1_MIR | > + UART3_MDR1_SIP_AUTO); > + > + uart_reg_out(UART3_EBLR, 2); > + > + divisor = 48000000 / (41 * speed); /* Base clock 48 MHz */ > + > + uart_reg_out(UART3_LCR, UART3_LCR_DIVEN); > + uart_reg_out(UART3_DLL, (divisor & 0xff)); > + uart_reg_out(UART3_DLH, (divisor >> 8)); > + uart_reg_out(UART3_LCR, 0x03); > + > + if (omap_ir->pdata->transceiver_mode) > + omap_ir->pdata->transceiver_mode(omap_ir->dev, > + IR_MIRMODE); > + > + local_irq_restore(flags); > + } else { > + local_irq_save(flags); > + > + /* FIR mode */ > + uart_reg_out(UART3_MDR1, UART3_MDR1_FIR | > + UART3_MDR1_SIP_AUTO); > + > + if (omap_ir->pdata->transceiver_mode) > + omap_ir->pdata->transceiver_mode(omap_ir->dev, > + IR_FIRMODE); > + > + local_irq_restore(flags); > + } > + > + omap_ir->speed = speed; > + > + return 0; > +} > + > +#ifdef CONFIG_PM > +/* > + * Suspend the IrDA interface. > + */ > +static int omap_irda_suspend(struct platform_device *pdev, pm_message_t state) > +{ > + struct net_device *dev = platform_get_drvdata(pdev); > + struct omap_irda *omap_ir = netdev_priv(dev); > + > + if (!dev) > + return 0; > + > + if (omap_ir->open) { > + /* > + * Stop the transmit queue > + */ > + netif_device_detach(dev); > + disable_irq(dev->irq); > + omap_irda_shutdown(omap_ir); > + } > + return 0; > +} > + > +/* > + * Resume the IrDA interface. > + */ > +static int omap_irda_resume(struct platform_device *pdev) > +{ > + struct net_device *dev = platform_get_drvdata(pdev); > + struct omap_irda *omap_ir= netdev_priv(dev); > + > + if (!dev) > + return 0; > + > + if (omap_ir->open) { > + /* > + * If we missed a speed change, initialise at the new speed > + * directly. It is debatable whether this is actually > + * required, but in the interests of continuing from where > + * we left off it is desireable. The converse argument is > + * that we should re-negotiate at 9600 baud again. > + */ > + if (omap_ir->newspeed) { > + omap_ir->speed = omap_ir->newspeed; > + omap_ir->newspeed = 0; > + } > + > + omap_irda_startup(dev); > + omap_irda_set_speed(dev, omap_ir->speed); > + enable_irq(dev->irq); > + > + /* > + * This automatically wakes up the queue > + */ > + netif_device_attach(dev); > + } > + > + return 0; > +} > +#else > +#define omap_irda_suspend NULL > +#define omap_irda_resume NULL > +#endif > + > +static int omap_irda_probe(struct platform_device *pdev) > +{ > + struct net_device *dev; > + struct omap_irda *omap_ir; > + struct omap_irda_config *pdata = pdev->dev.platform_data; > + unsigned int baudrate_mask; > + int err = 0; > + int irq = NO_IRQ; > + > + if (!pdata) { > + printk(KERN_ERR "IrDA Platform data not supplied\n"); > + return -ENOENT; > + } > + > + if (!pdata->rx_channel || !pdata->tx_channel) { > + printk(KERN_ERR "IrDA invalid rx/tx channel value\n"); > + return -ENOENT; > + } > + > + irq = platform_get_irq(pdev, 0); > + if (irq <= 0) { > + printk(KERN_WARNING "no irq for IrDA\n"); > + return -ENOENT; > + } > + > + dev = alloc_irdadev(sizeof(struct omap_irda)); > + if (!dev) > + goto err_mem_1; > + > + > + omap_ir = netdev_priv(dev); > + omap_ir->dev = &pdev->dev; > + omap_ir->pdata = pdata; > + > + dev->hard_start_xmit = omap_irda_hard_xmit; > + dev->open = omap_irda_start; > + dev->stop = omap_irda_stop; > + dev->do_ioctl = omap_irda_ioctl; > + dev->get_stats = omap_irda_stats; > + dev->irq = irq; > + > + irda_init_max_qos_capabilies(&omap_ir->qos); > + > + baudrate_mask = 0; > + if (omap_ir->pdata->transceiver_cap & IR_SIRMODE) > + baudrate_mask |= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; > + if (omap_ir->pdata->transceiver_cap & IR_MIRMODE) > + baudrate_mask |= IR_57600 | IR_1152000; > + if (omap_ir->pdata->transceiver_cap & IR_FIRMODE) > + baudrate_mask |= IR_4000000 << 8; > + > + omap_ir->qos.baud_rate.bits &= baudrate_mask; > + omap_ir->qos.min_turn_time.bits = 7; > + > + irda_qos_bits_to_value(&omap_ir->qos); > + > + /* Any better way to avoid this? No. */ > + if (machine_is_omap_h3() || machine_is_omap_h4()) > + INIT_WORK(&omap_ir->pdata->gpio_expa, NULL, NULL); > + > + err = register_netdev(dev); > + if (!err) > + platform_set_drvdata(pdev, dev); > + else > + free_netdev(dev); > + > +err_mem_1: > + return err; > +} > + > +static int omap_irda_remove(struct platform_device *pdev) > +{ > + struct net_device *dev = platform_get_drvdata(pdev); > + > + if (pdev) { > + unregister_netdev(dev); > + free_netdev(dev); > + } > + return 0; > +} > + > +static struct platform_driver omapir_driver = { > + .probe = omap_irda_probe, > + .remove = omap_irda_remove, > + .suspend = omap_irda_suspend, > + .resume = omap_irda_resume, > + .driver = { > + .name = "omapirda", > + }, > +}; > + > +static char __initdata banner[] = KERN_INFO "OMAP IrDA driver initializing\n"; > + > +static int __init omap_irda_init(void) > +{ > + printk(banner); > + return platform_driver_register(&omapir_driver); > +} > + > +static void __exit omap_irda_exit(void) > +{ > + platform_driver_unregister(&omapir_driver); > +} > + > +module_init(omap_irda_init); > +module_exit(omap_irda_exit); > + > +MODULE_AUTHOR("MontaVista"); > +MODULE_DESCRIPTION("OMAP IrDA Driver"); > +MODULE_LICENSE("GPL"); > + > -- > 1.3.3 > > ------------------------------------------------------------------------- > Using Tomcat but need to do more? Need to support web services, security? > Get stuff done quickly with pre-integrated technology to make your job easier > Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo > http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642 > _______________________________________________ > irda-users mailing list > irda-users@lists.sourceforge.net > http://lists.sourceforge.net/lists/listinfo/irda-users - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/