From: Samuel Ortiz <samuel@sortiz.org>
To: Komal Shah <komal_shah802003@yahoo.com>
Cc: samuel@sortiz.org, irda-users@lists.sourceforge.net,
tony@atomide.com, akpm@osdl.org, r-woodruff2@ti.com,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH] OMAP: Add support for IrDA driver
Date: Fri, 11 Aug 2006 08:47:56 +0300 [thread overview]
Message-ID: <20060811054756.GA14621@sortiz.org> (raw)
In-Reply-To: <1154941953.26912.267785299@webmail.messagingengine.com>
Hi Komal,
On Mon, Aug 07, 2006 at 02:42:33PM +0530, Komal Shah wrote:
> Samuel/Tony,
>
> I have attached the 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.
Overall the patch looks good, I have some comments though. See below:
> +++ 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 Teaxas Instruments
s/Teaxas/Texas/ ;-)
> 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..c51fb48
> --- /dev/null
> +++ b/drivers/net/irda/omap-ir.c
> @@ -0,0 +1,903 @@
> +/*
> + * 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 <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 <linux/platform_device.h>
> +#include <linux/i2c.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/mux.h>
> +#include <asm/arch/gpio.h>
> +#include <asm/arch/irda.h>
> +
> +#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(si) ((si)->speed >= 4000000)
> +#define IRDA_FRAME_SIZE_LIMIT 4096
You should use IRDA_SKB_MAX_MTU and IRDA_SIR_MAX_FRAME for respectively your
max Rx and Tx sizes.
> +static int rx_state = 0; /* RX state for IOCTL */
rx_state could probably be part of omap_irda.
> +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;
You forgot to define omap_irda_config.
Same thing for IR_SEL, IR_SIRMODE, IR_FIRMODE and IR_MIRMODE.
> +};
> +
> +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 irda_device_setup(struct net_device *dev);
This is useless as you don't use this function anywhere in the code below.
> +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 *si)
> +{
> + /* Configure DMA */
> + omap_set_dma_src_params(si->rx_dma_channel, 0x3, 0x0,
> + si->pdata->src_start,
> + 0, 0);
> +
> + omap_enable_dma_irq(si->rx_dma_channel, 0x01);
> +
> + omap_set_dma_dest_params(si->rx_dma_channel, 0x0, 0x1,
> + si->rx_buf_dma_phys,
> + 0, 0);
> +
> + omap_set_dma_transfer_params(si->rx_dma_channel, 0x0,
> + IRDA_FRAME_SIZE_LIMIT, 0x1,
> + 0x0, si->pdata->rx_trigger, 0);
> + omap_start_dma(si->rx_dma_channel);
> +}
> +
> +static void omap_start_tx_dma(struct omap_irda *si, int size)
> +{
> + /* Configure DMA */
> + omap_set_dma_dest_params(si->tx_dma_channel, 0x03, 0x0,
> + si->pdata->dest_start, 0, 0);
> +
> + omap_enable_dma_irq(si->tx_dma_channel, 0x01);
> +
> + omap_set_dma_src_params(si->tx_dma_channel, 0x0, 0x1,
> + si->tx_buf_dma_phys,
> + 0, 0);
> +
> + omap_set_dma_transfer_params(si->tx_dma_channel, 0x0, size, 0x1,
> + 0x0, si->pdata->tx_trigger, 0);
> +
> + /* Start DMA */
> + omap_start_dma(si->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 *si = dev->priv;
A detail here, but I think that "si" for a variable name is a bit meaningless.
> + printk(KERN_ERR "RX Transfer error or very big frame\n");
> +
> + /* Clear interrupts */
> + uart_reg_in(UART3_IIR);
> +
> + si->stats.rx_frame_errors++;
> +
> + uart_reg_in(UART3_RESUME);
> +
> + /* Re-init RX DMA */
> + omap_irda_start_rx_dma(si);
> +}
> +
> +/* 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 *si = dev->priv;
> +
> + /*Stop DMA controller */
> + omap_stop_dma(si->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 *si = dev->priv;
> +
> +
> + /* 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 (si->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);
> + si->pdata->transceiver_mode(si->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 *si)
> +{
> + 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 *si = dev->priv;
> + 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");
> +
> + si->stats.tx_packets++;
> +
> + if (si->newspeed) {
> + omap_irda_set_speed(dev, si->newspeed);
> + si->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(si->rx_dma_channel);
> +
> + status = uart_reg_in(UART3_SFLSR); /* Take a frame status */
> +
> + if (status != 0) { /* Bad frame? */
> + si->stats.rx_frame_errors++;
> + uart_reg_in(UART3_RESUME);
> + } else {
> + /* We got a frame! */
> + skb = alloc_skb(IRDA_FRAME_SIZE_LIMIT, GFP_ATOMIC);
Use dev_alloc_skb() for allocating RX packets.
> +
> + 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(si->rx_dma_channel);
> +
> + if (cpu_is_omap16xx())
> + w -= OMAP1_DMA_CDSA_L_REG(si->rx_dma_channel);
> + if (cpu_is_omap24xx())
> + w -= OMAP2_DMA_CDSA_REG(si->rx_dma_channel);
> +
> + if (!IS_FIR(si)) {
> + /* Copy DMA buffer to skb */
> + memcpy(skb_put(skb, w - 2), si->rx_buf_dma_virt, w - 2);
> + } else {
> + /* Copy DMA buffer to skb */
> + memcpy(skb_put(skb, w - 4), si->rx_buf_dma_virt, w - 4);
> + }
> +
> + 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 */
> + omap_irda_start_rx_dma(si);
> +
> + dev->last_rx = jiffies;
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int omap_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> + struct omap_irda *si = dev->priv;
> + 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 != si->speed && speed != -1)
> + si->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 (si->newspeed) {
> + si->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(si->tx_buf_dma_virt, skb->data, skb->len);
> +
> + /* Copy skb data to DMA buffer */
> + si->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(si, 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 *si = dev->priv;
> + 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 (si->open)
> + ret = omap_irda_set_speed(dev,
> + rq->ifr_baudrate);
> + else {
> + printk(KERN_ERR "omap_irda_ioctl: 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 = rx_state;
> + break;
rx_state is always set to 0, so this looks a bit useless.
> + default:
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static struct net_device_stats *omap_irda_stats(struct net_device *dev)
> +{
> + struct omap_irda *si = dev->priv;
> + return &si->stats;
> +}
> +
> +static int omap_irda_start(struct net_device *dev)
> +{
> + struct omap_irda *si = dev->priv;
> + int err;
> +
> + si->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(si->pdata->rx_channel, "IrDA Rx DMA",
> + (void *)omap_irda_rx_dma_callback,
> + dev, &(si->rx_dma_channel))) {
> + printk(KERN_ERR "Failed to request IrDA Rx DMA\n");
> + goto err_irq;
> + }
> +
> + if (omap_request_dma(si->pdata->tx_channel, "IrDA Tx DMA",
> + (void *)omap_irda_tx_dma_callback,
> + dev, &(si->tx_dma_channel))) {
> + printk(KERN_ERR "Failed to request IrDA Tx DMA\n");
> + goto err_irq;
> + }
> +
> + /* Allocate TX and RX buffers for DMA channels */
> + si->rx_buf_dma_virt =
> + dma_alloc_coherent(NULL, IRDA_FRAME_SIZE_LIMIT,
> + &(si->rx_buf_dma_phys),
> + GFP_KERNEL);
> +
> + if (!si->rx_buf_dma_virt) {
> + printk(KERN_ERR "Unable to allocate memory for rx_buf_dma\n");
> + goto err_irq;
> + }
> +
> + si->tx_buf_dma_virt =
> + dma_alloc_coherent(NULL, IRDA_FRAME_SIZE_LIMIT,
> + &(si->tx_buf_dma_phys),
> + GFP_KERNEL);
> +
> + if (!si->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 (si->pdata->select_irda)
> + si->pdata->select_irda(si->dev, IR_SEL);
> +
> + err = omap_irda_startup(dev);
> +
> + if (err)
> + goto err_startup;
> +
> + omap_irda_set_speed(dev, si->speed = 9600);
> +
> + /*
> + * Open a new IrLAP layer instance.
> + */
> + si->irlap = irlap_open(dev, &si->qos, "omap_sir");
> +
> + err = -ENOMEM;
> + if (!si->irlap)
> + goto err_irlap;
> +
> + /* Now enable the interrupt and start the queue */
> + si->open = 1;
> +
> + /* Start RX DMA */
> + omap_irda_start_rx_dma(si);
> +
> + enable_irq(dev->irq);
> + netif_start_queue(dev);
> +
> + return 0;
> +
> +err_irlap:
> + si->open = 0;
> + omap_irda_shutdown(si);
> +err_startup:
> + dma_free_coherent(NULL, IRDA_FRAME_SIZE_LIMIT,
> + si->tx_buf_dma_virt, si->tx_buf_dma_phys);
> +err_mem1:
> + dma_free_coherent(NULL, IRDA_FRAME_SIZE_LIMIT,
> + si->rx_buf_dma_virt, si->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 *si = dev->priv;
> +
> + disable_irq(dev->irq);
> +
> + netif_stop_queue(dev);
> +
> + omap_free_dma(si->rx_dma_channel);
> + omap_free_dma(si->tx_dma_channel);
> +
> + if (si->rx_buf_dma_virt)
> + dma_free_coherent(NULL, IRDA_FRAME_SIZE_LIMIT,
> + si->rx_buf_dma_virt, si->rx_buf_dma_phys);
> + if (si->tx_buf_dma_virt)
> + dma_free_coherent(NULL, IRDA_FRAME_SIZE_LIMIT,
> + si->tx_buf_dma_virt, si->tx_buf_dma_phys);
> +
> + omap_irda_shutdown(si);
> +
> + /* Stop IrLAP */
> + if (si->irlap) {
> + irlap_close(si->irlap);
> + si->irlap = NULL;
> + }
> +
> + si->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 *si = dev->priv;
> + int divisor;
> + unsigned long flags;
> +
> + /* Set IrDA speed */
> + if (speed <= 115200) {
> +
> + local_irq_save(flags);
> +
> + /* SIR mode */
> + if (si->pdata->transceiver_mode)
> + si->pdata->transceiver_mode(si->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 (si->pdata->transceiver_mode)
> + si->pdata->transceiver_mode(si->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 (si->pdata->transceiver_mode)
> + si->pdata->transceiver_mode(si->dev, IR_FIRMODE);
> +
> + local_irq_restore(flags);
> + }
> +
> + si->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 *si = dev->priv;
> +
> + if (!dev)
> + return 0;
> +
> + if (si->open) {
> + /*
> + * Stop the transmit queue
> + */
> + netif_device_detach(dev);
> + disable_irq(dev->irq);
> + omap_irda_shutdown(si);
> + }
> + 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 *si= dev->priv;
> +
> + if (!dev)
> + return 0;
> +
> + if (si->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 (si->newspeed) {
> + si->speed = si->newspeed;
> + si->newspeed = 0;
> + }
> +
> + omap_irda_startup(dev);
> + omap_irda_set_speed(dev, si->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 *si;
> + 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;
> + }
I see that rx_channel and tx_channel are part of your omap_irda_config, which
seems to be a platform specific structure. However, DMA channels are
architecture specific and could be removed from this structure, isn't it ?
> +
> + 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;
> +
> +
> + si = dev->priv;
> + si->dev = &pdev->dev;
> + si->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(&si->qos);
> +
> + baudrate_mask = 0;
> + if (si->pdata->transceiver_cap & IR_SIRMODE)
> + baudrate_mask |= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
> + if (si->pdata->transceiver_cap & IR_MIRMODE)
> + baudrate_mask |= IR_57600 | IR_1152000;
> + if (si->pdata->transceiver_cap & IR_FIRMODE)
> + baudrate_mask |= IR_4000000 << 8;
> +
> + si->qos.baud_rate.bits &= baudrate_mask;
> + si->qos.min_turn_time.bits = 7;
> +
> + irda_qos_bits_to_value(&si->qos);
> +
> + /* Any better way to avoid this? No. */
> + if (machine_is_omap_h3() || machine_is_omap_h4())
> + INIT_WORK(&si->pdata->gpio_expa, NULL, NULL);
What is this for ?
> + 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
Cheers,
Samuel.
next prev parent reply other threads:[~2006-08-10 22:35 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-08-07 9:12 [PATCH] OMAP: Add support for IrDA driver Komal Shah
2006-08-11 5:47 ` Samuel Ortiz [this message]
2006-08-11 9:20 ` [irda-users] " Komal Shah
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20060811054756.GA14621@sortiz.org \
--to=samuel@sortiz.org \
--cc=akpm@osdl.org \
--cc=irda-users@lists.sourceforge.net \
--cc=komal_shah802003@yahoo.com \
--cc=linux-kernel@vger.kernel.org \
--cc=r-woodruff2@ti.com \
--cc=tony@atomide.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.