OMAP: Add support for IrDA driver
PATCH 1/1 OMAP: Add support for IrDA driver
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
Signature · 1 line
http://www.fastmail.fm - Choose from over 50 domains or use your own
Hi Komal,
Quoted 5 lines · Komal Shah
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:
Review config MCS_FIR · line 440
Quoted 11 lines
+++ b/drivers/net/irda/Kconfig
(quoted diff) +6
-0@@ -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/ ;-)
Quoted 124 lines
drivers/net/irda/Makefile +1
-0@@ -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.odrivers/net/irda/omap-ir.c new file +106
-0@@ -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. +* [email protected] +* +* 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)
drivers/net/irda/omap-ir.c line 116
Quoted 10 lines
drivers/net/irda/omap-ir.c new file +10
-0+#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.
drivers/net/irda/omap-ir.c line 117
Quoted 1 line
drivers/net/irda/omap-ir.c new file +1
-0+static int rx_state =0 ;/* RX state for IOCTL */
rx_state could probably be part of omap_irda.
Quoted 10 lines
drivers/net/irda/omap-ir.c new file +10
-0+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;
drivers/net/irda/omap-ir.c line 137
Quoted 10 lines
drivers/net/irda/omap-ir.c new file +10
-0+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.
Quoted 5 lines
drivers/net/irda/omap-ir.c new file +5
-0+}; + +static void inline uart_reg_out (int idx,u8 val) +{ +omap_writeb (val, idx);
drivers/net/irda/omap-ir.c line 152
Quoted 10 lines
drivers/net/irda/omap-ir.c new file +10
-0+} + +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.
Quoted 38 lines
drivers/net/irda/omap-ir.c new file +38
-0+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 */
drivers/net/irda/omap-ir.c line 200
Quoted 10 lines
drivers/net/irda/omap-ir.c new file +10
-0+omap_start_dma (si->tx_dma_channel); +} + +/* DMA RX callback - normally, we should not go here, +* it calls only if something is going wrong +*/ +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.
Quoted 159 lines
drivers/net/irda/omap-ir.c new file +159
-0+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 */
drivers/net/irda/omap-ir.c line 369
Quoted 10 lines
drivers/net/irda/omap-ir.c new file +10
-0+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.
Quoted 128 lines
drivers/net/irda/omap-ir.c new file +128
-0+ +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 :
drivers/net/irda/omap-ir.c line 507
Quoted 10 lines
drivers/net/irda/omap-ir.c new file +10
-0+ 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.
Quoted 288 lines
drivers/net/irda/omap-ir.c new file +288
-0+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 ;
drivers/net/irda/omap-ir.c line 805
Quoted 10 lines
drivers/net/irda/omap-ir.c new file +10
-0+ +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 ?
Quoted 31 lines
drivers/net/irda/omap-ir.c new file +31
-0+ + 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 )
drivers/net/irda/omap-ir.c line 846
Quoted 10 lines
drivers/net/irda/omap-ir.c new file +10
-0+ 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 ?
Quoted 53 lines
drivers/net/irda/omap-ir.c new file +51
-0+ 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" ); +Signature · 1 line
1.3.3
Cheers,
Samuel.
Quoted 9 lines · --- Samuel Ortiz
Hi Komal,
Quoted 5 lines · Komal Shah
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.
Quoted 9 lines
Review
Quoted 6 lines
(quoted diff) +6
-0+ +#define UART3_IIR_TX_STATUS (1 << 5) +#define UART3_IIR_EOF (0x80) + +#define IS_FIR(si) ((si)->speed >= 4000000) +#define IRDA_FRAME_SIZE_LIMIT 4096You should use
IRDA_SKB_MAX_MTUandIRDA_SIR_MAX_FRAMEfor 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.
Quoted 4 lines
Quoted 3 lines
+static int
rx_state= 0; /* RX state for IOCTL */
rx_statecould probably be part ofomap_irda.
I think this can be removed all together, as it remains zero only.
Quoted 18 lines
Quoted 5 lines
(quoted diff) +3
-0+struct omap_irda { + unsigned char open; + int speed; /* Current IrDA speed */Review
Quoted 10 lines
(quoted diff) +10
-0+ 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*/
Review
Quoted 1 line
(quoted diff) +1
-0+ dma_addr_t tx_buf_dma_phys; /* Physical address of TX DMA buffer*/
Quoted 8 lines
Review
Quoted 6 lines
(quoted diff) +6
-0+ + 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 forIR_SEL,IR_SIRMODE,IR_FIRMODEandIR_MIRMODE.
Check include/asm-arm/arch-omap/irda.h
Quoted 7 lines
Quoted 1 line
+static void omap_irda_rx_dma_callback(int lch, u16
ch_status, void*data)
Review
Quoted 3 lines
(quoted diff) +3
-0+{ + 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.
Quoted 10 lines
Review
Quoted 9 lines
(quoted diff) +9
-0+ + 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
Quoted 5 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+ + case SIOCGRECEIVING: + rq->ifr_receiving = rx_state; + break;
rx_stateis always set to 0, so this looks a bit useless.
I can remove the "case SIOCGRECEIVING" altogether then.
Quoted 10 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ + if (!pdata->rx_channel || !pdata->tx_channel) { + printk(KERN_ERR "IrDA invalid rx/tx channel value\n"); + return -ENOENT; + }I see that
rx_channelandtx_channelare part of youromap_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?
Quoted 7 lines
Review
Quoted 6 lines
(quoted diff) +6
-0+ + 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