public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] OMAP: Add support for IrDA driver
@ 2006-08-07  9:12 Komal Shah
  2006-08-11  5:47 ` Samuel Ortiz
  0 siblings, 1 reply; 3+ messages in thread
From: Komal Shah @ 2006-08-07  9:12 UTC (permalink / raw)
  To: samuel, irda-users; +Cc:  , tony, r-woodruff2, akpm

[-- Attachment #1: Type: text/plain, Size: 476 bytes --]

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.

FYI...we are in process of submitting OMAP I2C driver to mainline,
and right now I am working on the 2nd round of review comments
from Jean.

---Komal Shah
http://komalshah.blogspot.com

-- 
http://www.fastmail.fm - Choose from over 50 domains or use your own


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-OMAP-Add-support-for-IrDA-driver.patch --]
[-- Type: text/x-patch; name="0001-OMAP-Add-support-for-IrDA-driver.patch", Size: 24186 bytes --]

From nobody Mon Sep 17 00:00:00 2001
From: Komal Shah <komal_shah802003@yahoo.com>
Date: Mon, 7 Aug 2006 18:38:45 +0530
Subject: [PATCH] OMAP: Add support for IrDA driver

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 <komal_shah802003@yahoo.com>

---

 drivers/net/irda/Kconfig   |   10 
 drivers/net/irda/Makefile  |    1 
 drivers/net/irda/omap-ir.c |  903 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 914 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/irda/omap-ir.c

e725e5afaf6b7f9903ee8b48faf1faeff8604ac7
diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig
index e9e6d99..8d29a0a 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 Teaxas 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..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
+
+static int rx_state = 0;	/* RX state for IOCTL */
+
+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 irda_device_setup(struct net_device *dev);
+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;
+
+	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);
+
+		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;
+
+	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;
+	}
+
+	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);
+
+	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


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] OMAP: Add support for IrDA driver
  2006-08-07  9:12 [PATCH] OMAP: Add support for IrDA driver Komal Shah
@ 2006-08-11  5:47 ` Samuel Ortiz
  2006-08-11  9:20   ` [irda-users] " Komal Shah
  0 siblings, 1 reply; 3+ messages in thread
From: Samuel Ortiz @ 2006-08-11  5:47 UTC (permalink / raw)
  To: Komal Shah; +Cc: samuel, irda-users, tony, akpm, r-woodruff2, linux-kernel

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.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [irda-users] [PATCH] OMAP: Add support for IrDA driver
  2006-08-11  5:47 ` Samuel Ortiz
@ 2006-08-11  9:20   ` Komal Shah
  0 siblings, 0 replies; 3+ messages in thread
From: Komal Shah @ 2006-08-11  9:20 UTC (permalink / raw)
  To: Samuel Ortiz; +Cc: akpm, samuel, tony, linux-kernel, irda-users, r-woodruff2

--- Samuel Ortiz <samuel@sortiz.org> wrote:

> 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:

Thanx for the review.
 
> > +
> > +#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.

I will update this and to all the places where FRAME_SIZE_LIMIT was
used as default ro rx and tx sizes.

> 
> 
> > +static int rx_state = 0;	/* RX state for IOCTL */
> rx_state could probably be part of omap_irda.

I think this can be removed all together, as it remains zero only.

> 
> 
> > +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.

Check include/asm-arm/arch-omap/irda.h

> > +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. 

Ok. I will update this.

> > +
> > +	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.

Ok. Done

> > +
> > +	case SIOCGRECEIVING:
> > +		rq->ifr_receiving = rx_state;
> > +		break;
> rx_state is always set to 0, so this looks a bit useless.

I can remove the "case SIOCGRECEIVING" altogether then.

> > +
> > +	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 ?

Correct. Then I have to think passing those information using either
device resources or revert back to #ifdefers in omap-ir.c. Any
suggestions?

> > +
> > +	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 ?


As H3 and H4 platforms uses gpio_expander (through I2C) for selecting
between IrDA and GPS Module, and that code is being called from IRQ
context, as you know that gpip_expander code ultimately leades to I2C
code and which can sleep.

See these links:

http://linux.omap.com/pipermail/linux-omap-open-source/2005-December/thread.html
http://linux.omap.com/pipermail/linux-omap-open-source/2005-December/005996.html
http://linux.omap.com/pipermail/linux-omap-open-source/2005-December/006019.html

---Komal Shah
http://komalshah.blogspot.com/

__________________________________________________
Do You Yahoo!?
Tired of spam?  Yahoo! Mail has the best spam protection around 
http://mail.yahoo.com 

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2006-08-11  9:20 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-08-07  9:12 [PATCH] OMAP: Add support for IrDA driver Komal Shah
2006-08-11  5:47 ` Samuel Ortiz
2006-08-11  9:20   ` [irda-users] " Komal Shah

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox