From: Zhang Wei <[EMAIL PROTECTED]>

The driver supports SIR, MIR, FIR modes and maximum 4000000bps rate.

Signed-off-by: Zhang Wei <[EMAIL PROTECTED]>
[AV: few small fixes, plus had made platform ops passing via node->data
     to avoid #ifdef stuff in the fsl_soc (think DIU). ]
Signed-off-by: Anton Vorontsov <[EMAIL PROTECTED]>
---

If anyone from the Freescale is already working on this patch for the
mainline, I'll readily step back.

 drivers/net/irda/Kconfig  |    5 +
 drivers/net/irda/Makefile |    1 +
 drivers/net/irda/fsl_ir.c |  792 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/fsl_ir.h    |  207 ++++++++++++
 4 files changed, 1005 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/irda/fsl_ir.c
 create mode 100644 include/linux/fsl_ir.h

diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig
index ce816ba..da24f57 100644
--- a/drivers/net/irda/Kconfig
+++ b/drivers/net/irda/Kconfig
@@ -341,5 +341,10 @@ config MCS_FIR
          To compile it as a module, choose M here: the module will be called
          mcs7780.
 
+config FSL_FIR
+       tristate "Freescale Irda driver"
+       depends on IRDA && FSL_SOC
+       help
+
 endmenu
 
diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile
index 5d20fde..724218c 100644
--- a/drivers/net/irda/Makefile
+++ b/drivers/net/irda/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_VIA_FIR)         += via-ircc.o
 obj-$(CONFIG_PXA_FICP)         += pxaficp_ir.o
 obj-$(CONFIG_MCS_FIR)          += mcs7780.o
 obj-$(CONFIG_AU1000_FIR)       += au1k_ir.o
+obj-$(CONFIG_FSL_FIR)          += fsl_ir.o
 # SIR drivers
 obj-$(CONFIG_IRTTY_SIR)                += irtty-sir.o  sir-dev.o
 # dongle drivers for SIR drivers
diff --git a/drivers/net/irda/fsl_ir.c b/drivers/net/irda/fsl_ir.c
new file mode 100644
index 0000000..d38d309
--- /dev/null
+++ b/drivers/net/irda/fsl_ir.c
@@ -0,0 +1,792 @@
+/*
+ * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Zhang Wei, [EMAIL PROTECTED], Oct. 2007
+ *
+ * Description:
+ * The IrDA driver for Freescale PowerPC MPC8610 processor. The driver
+ * support SIR and FIR mode. The maximum speed is 4Mbps.
+ *
+ * Changelog:
+ * Oct 2007 Zhang Wei <[EMAIL PROTECTED]>
+ * - Initial version.
+ *
+ * This file is part of the Linux kernel
+ *
+ * This 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/of_platform.h>
+#include <linux/fsl_ir.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/irda_device.h>
+#include <net/irda/irlap_frame.h>
+#include <net/irda/irlap.h>
+
+#include <sysdev/fsl_soc.h>
+
+#define is_sir_speed(speed) ((speed <= 115200) ? 1 : 0)
+#define is_sir(ir) (is_sir_speed(ir->speed))
+
+static void init_iobuf(iobuff_t *io, void *buff, int size)
+{
+       io->head = buff;
+       io->truesize = size;
+       io->in_frame = FALSE;
+       io->state = OUTSIDE_FRAME;
+       io->data = io->head;
+       io->skb = NULL;
+}
+
+static void ir_switch_mode(struct fsl_ir *ir, u32 speed)
+{
+
+       if (ir->speed && (ir->speed < 115200))  /* Switch from SIR to FIR */
+               /* Disable SIRI */
+               clrbits32(&ir->reg_base->scr1, FSL_IR_SCR1_IREN |
+                                               FSL_IR_SCR1_SIRIEN);
+       else {                                  /* Switch from FIR to SIR */
+               /* Disable FIRI */
+               out_be32(&ir->reg_base->firitcr, 0);
+               out_be32(&ir->reg_base->firircr, 0);
+       }
+
+       /* Switch the IrDA mode on board */
+       if (ir->plat_op && ir->plat_op->set_ir_mode)
+               ir->plat_op->set_ir_mode(ir, speed);
+}
+
+static int ir_crc_len(struct fsl_ir *ir)
+{
+       int crc_len;
+
+       switch (ir->speed) {
+       case 576000:
+       case 1152000:
+               crc_len = 2;    /* CRC16, 16 bits */
+               break;
+       case 4000000:
+               crc_len = 4;    /* CRC32, 32 bits */
+               break;
+       default:
+               crc_len = 0;
+               break;
+       }
+       return crc_len;
+}
+
+static void fir_rx(struct fsl_ir *ir, int len)
+{
+       struct net_device *ndev = dev_get_drvdata(ir->dev);
+       struct sk_buff *skb;
+       int i;
+
+       if (len <= ir_crc_len(ir))
+               return;
+
+       do_gettimeofday(&ir->stamp);
+       /* Now, for new packet arriving */
+       skb = alloc_skb(len + 1, GFP_ATOMIC);
+       if (!skb) {
+               ir->stats.rx_dropped++;
+               return;
+       }
+       skb_reserve(skb, 1);
+
+       for (i = 0; i < len; i++)
+               skb->data[i] = in_8((u8 *)&ir->reg_base->rfifo);
+
+       len -= ir_crc_len(ir);
+       skb_put(skb, len);
+
+       ir->stats.rx_packets++;
+       ir->stats.rx_bytes += len;
+
+       skb->dev = ndev;
+       skb_reset_mac_header(skb);
+       skb->protocol = htons(ETH_P_IRDA);
+       netif_rx(skb);
+}
+
+static void fir_tx(struct fsl_ir *ir)
+{
+       int free_bytes;
+       struct sk_buff *skb = ir->tx_buff.skb;
+       size_t len;
+
+       if (!skb)
+               return;
+
+       spin_lock(&ir->tx_lock);
+       do {
+               free_bytes = 128 -
+                       ((in_be32(&ir->reg_base->firitsr) >> 8) & 0xff);
+               for (len = min(free_bytes, ir->tx_buff.len); len > 0;
+                       len--, ir->tx_buff.len--)
+                       out_8((u8 *)&ir->reg_base->tfifo,
+                               skb->data[skb->len - ir->tx_buff.len]);
+       } while (ir->tx_buff.len > 0);
+       spin_unlock(&ir->tx_lock);
+
+       dev_kfree_skb_any(ir->tx_buff.skb);
+       ir->stats.tx_packets++;
+       ir->stats.tx_bytes += skb->len;
+}
+
+static void sir_set_data(struct fsl_ir *ir, u8 data_size)
+{
+       switch (data_size) {
+       case 7:
+               clrbits32(&ir->reg_base->scr2, FSL_IR_SCR2_WS);
+               break;
+       case 8:
+               setbits32(&ir->reg_base->scr2, FSL_IR_SCR2_WS);
+               break;
+       }
+}
+
+static void sir_set_parity(struct fsl_ir *ir, enum sir_parity parity)
+{
+       switch (parity) {
+       case SIR_PARITY_NONE:
+               clrbits32(&ir->reg_base->scr2, FSL_IR_SCR2_PREN);
+               break;
+       case SIR_PARITY_EVEN:
+               setbits32(&ir->reg_base->scr2, FSL_IR_SCR2_PREN);
+               clrbits32(&ir->reg_base->scr2, FSL_IR_SCR2_PROE);
+               break;
+       case SIR_PARITY_ODD:
+               setbits32(&ir->reg_base->scr2, FSL_IR_SCR2_PREN
+                               | FSL_IR_SCR2_PROE);
+               break;
+       }
+}
+
+static void sir_set_stop(struct fsl_ir *ir, u8 stop_bit)
+{
+       switch (stop_bit) {
+       case 1:
+               clrbits32(&ir->reg_base->scr2, FSL_IR_SCR2_STPB);
+               break;
+       case 2:
+               setbits32(&ir->reg_base->scr2, FSL_IR_SCR2_STPB);
+               break;
+       }
+}
+
+static void sir_put(struct fsl_ir *ir, u8 ch)
+{
+       out_be32(&ir->reg_base->stxd, ch);
+}
+
+static u8 sir_get(struct fsl_ir *ir)
+{
+       u32 rxd = in_be32(&ir->reg_base->srxd);
+
+       if (rxd & FSL_IR_SRXD_ERR)
+               ir->stats.rx_errors++;
+
+       if (rxd & FSL_IR_SRXD_OVRRUN)
+               ir->stats.rx_fifo_errors++;
+
+       if (rxd & FSL_IR_SRXD_FRMERR)
+               ir->stats.rx_frame_errors++;
+
+       if (rxd & FSL_IR_SRXD_PRERR)
+               ir->stats.rx_crc_errors++;
+
+       return (u8)rxd;
+}
+
+static int fsl_ir_set_speed(struct net_device *ndev, u32 speed)
+{
+       u32 sbir;
+       u32 sbmr;
+       struct fsl_ir *ir = netdev_priv(ndev);
+       struct irlap_cb *self;
+
+       if (is_sir_speed(ir->speed) != is_sir_speed(speed))
+               ir_switch_mode(ir, speed);
+
+       ir->speed = speed;
+       if (is_sir_speed(speed)) {
+               /* SIR */
+               sbir = 89;
+               sbmr = ir->clock_in / ir->div / 16 * (sbir + 1) / speed - 1;
+               out_be32(&ir->reg_base->sbir, sbir);
+               out_be32(&ir->reg_base->sbmr, sbmr);
+
+               /* Prepare SIR buff */
+               init_iobuf(&ir->tx_buff, ir->txb, FSL_SIR_TXBUFF_SIZE);
+               init_iobuf(&ir->rx_buff, ir->rxb, FSL_SIR_RXBUFF_SIZE);
+
+               /* Enable SIR */
+               setbits32(&ir->reg_base->scr1, FSL_IR_SCR1_IREN |
+                                               FSL_IR_SCR1_SIRIEN);
+       } else {
+               /* MIR and FIR are the same mode in FSL IR controller */
+
+               /* Clean the TCR and RCR register */
+               out_be32(&ir->reg_base->firitcr, 0);
+               out_be32(&ir->reg_base->firircr, 0);
+
+               /* Prepare FIR buff */
+               ir->tx_buff.skb = NULL;
+               ir->rx_buff.skb = NULL;
+
+               /* Set FIRI Transmitter Control
+                * Transmit complete interrupt: Enabled
+                * Send packet abort sysmbol when FIFO underrun or transfer
+                * abort: Enabled
+                */
+               setbits32(&ir->reg_base->firitcr,
+                       FSL_FIRITCR_TCIE | FSL_FIRITCR_PCF | FSL_FIRITCR_PC);
+               out_be32(&ir->reg_base->firicr, (32 << 5) | 5);
+
+               /* Set FIRI Receive control */
+               self = ndev->atalk_ptr;
+               setbits32(&ir->reg_base->firircr,
+                       FSL_FIRIRCR_RAM | ((((u32)self->caddr |
+                                               ((self->state == LAP_NRM_S) ?
+                                                CMD_FRAME : 0)) << 16)
+                                               & 0x00ff0000)|
+                       FSL_FIRIRCR_RDT_16 | FSL_FIRIRCR_RPA | FSL_FIRIRCR_RPP |
+                       FSL_FIRIRCR_RPEIE);
+
+               switch (speed) {
+               case 576000:
+                       setbits32(&ir->reg_base->firitcr,
+                                               FSL_FIRITCR_TM_MIR_576);
+                       setbits32(&ir->reg_base->firircr,
+                                               FSL_FIRIRCR_RM_MIR_576);
+                       break;
+               case 1152000:
+                       setbits32(&ir->reg_base->firitcr,
+                                               FSL_FIRITCR_TM_MIR_1152);
+                       setbits32(&ir->reg_base->firircr,
+                                               FSL_FIRIRCR_RM_MIR_1152);
+                       break;
+               case 4000000:
+                       setbits32(&ir->reg_base->firitcr, FSL_FIRITCR_TM_FIR);
+                       setbits32(&ir->reg_base->firircr, FSL_FIRIRCR_RM_FIR);
+                       break;
+               default:
+                       dev_err(ir->dev, "speed %d is not supported!\n",
+                                       speed);
+                       return -ENOTSUPP;
+               }
+
+               out_be32(&ir->reg_base->firitsr, 0xffff);
+               /* Enable FIR Rx */
+               setbits32(&ir->reg_base->firircr, FSL_FIRIRCR_RE);
+               out_be32(&ir->reg_base->firirsr, 0xffff);
+       }
+       return 0;
+}
+
+/* Start IR from 9600 speed */
+static void fsl_ir_start(struct net_device *ndev)
+{
+       struct fsl_ir *ir = netdev_priv(ndev);
+
+       /* Start IrDA speed from 9600 with SIRI mode */
+       ir->div = ir->clock_in / 0x10000 / 1000 + 1;
+
+       if (ir->div > 7) {
+               dev_err(ir->dev, "Unsupported SIR clock in frequency!\n");
+               return;
+       }
+
+       clrbits32(&ir->reg_base->sfcr, FSL_IR_SFCR_RFDIV_MASK);
+       setbits32(&ir->reg_base->sfcr, (ir->div < 7) ?
+                               ((6 - ir->div) << 7) : FSL_IR_SFCR_RFDIV_7);
+
+       /* Set Tx and Rx watermarks,
+        * Tx watermarks = 2
+        * Rx watermarks = 30
+        */
+       setbits32(&ir->reg_base->sfcr, (2 << 10) | 30);
+
+       /* Set One Millisecond Register */
+       out_be32(&ir->reg_base->sonems, 0xffff & (ir->clock_in / ir->div
+                                                               / 1000));
+
+       /* Select interrupts */
+
+       /* SCR3:
+        * Invert TX
+        * If the Tx need invert, enable below:
+        * out_be32(&ir->reg_base->scr3, 0x0700 | FSL_IR_SCR3_INVT);
+        */
+
+
+       /* SCR1:
+        * SIR initialization */
+       setbits32(&ir->reg_base->scr1, FSL_IR_SCR1_IREN | FSL_IR_SCR1_RRDYEN |
+                               FSL_IR_SCR1_ATDMAEN | FSL_IR_SCR1_SIRIEN);
+
+       /* 9600 8-N-1 */
+       ir_switch_mode(ir, 9600);
+       fsl_ir_set_speed(ndev, 9600);
+       sir_set_data(ir, 8);
+       sir_set_parity(ir, SIR_PARITY_NONE);
+       sir_set_stop(ir, 1);
+
+       out_be32(&ir->reg_base->sts, 0x60);
+
+       /* SCR2:
+        * Enable Tx/Rx */
+       setbits32(&ir->reg_base->scr2, 0x4000 | FSL_IR_SCR2_TXEN |
+                               FSL_IR_SCR2_ATEN | FSL_IR_SCR2_RXEN);
+
+       /* SCR4:
+        * If the Rx need invert, enable FSL_IR_SCR4_INVR bit.
+        */
+       out_be32(&ir->reg_base->scr4, 0x8000 | FSL_IR_SCR4_IRSC);
+
+
+       dev_dbg(ir->dev, "scr1 %08x\n", in_be32(&ir->reg_base->scr1));
+       dev_dbg(ir->dev, "scr2 %08x\n", in_be32(&ir->reg_base->scr2));
+       dev_dbg(ir->dev, "scr3 %08x\n", in_be32(&ir->reg_base->scr3));
+       dev_dbg(ir->dev, "scr4 %08x\n", in_be32(&ir->reg_base->scr4));
+}
+
+static void fsl_ir_halt(struct fsl_ir *ir)
+{
+       ir_switch_mode(ir, 0);
+
+       /* Clean SIR registers */
+       out_be32(&ir->reg_base->scr1, 0);
+       out_be32(&ir->reg_base->scr2, 0);
+       out_be32(&ir->reg_base->scr3, 0);
+       out_be32(&ir->reg_base->scr4, 0);
+
+       /* Clean FIR TCR and RCR register */
+       out_be32(&ir->reg_base->firitcr, 0);
+       out_be32(&ir->reg_base->firircr, 0);
+}
+
+static irqreturn_t fsl_ir_sir_irq(struct net_device *ndev)
+{
+       struct fsl_ir *ir = netdev_priv(ndev);
+       u32 ssr1, ssr2;
+       ssr1 = in_be32(&ir->reg_base->ssr1);
+       ssr2 = in_be32(&ir->reg_base->ssr2);
+
+       /* Tx is ready */
+       if ((ssr1 & FSL_IR_SSR1_TRDY) && ir->tx_buff.len) {
+               clrbits32(&ir->reg_base->scr1, FSL_IR_SCR1_TRDYEN);
+               tasklet_schedule(&ir->tx_tasklet);
+       }
+
+       /* Last Tx transfer is finished */
+       if (ssr2 & FSL_IR_SSR2_TXDC && !ir->tx_buff.len) {
+               if (ir->new_speed) {
+                       fsl_ir_set_speed(ndev, ir->new_speed);
+                       ir->new_speed = 0;
+               }
+               clrbits32(&ir->reg_base->scr4, FSL_IR_SCR4_TCEN);
+
+               ir->stats.tx_packets++;
+               ir->stats.tx_bytes += ir->tx_buff.data
+                                               - ir->tx_buff.head;
+
+               netif_wake_queue(ndev);
+       }
+
+       /* Rx is ready */
+       if (ssr1 & FSL_IR_SSR1_RRDY) {
+               int i;
+               int rxchars = in_be32(&ir->reg_base->sfcr) & 0x3f;
+
+               for (i = 0; i < rxchars; i++)
+                       async_unwrap_char(ndev, &ir->stats, &ir->rx_buff,
+                                                               sir_get(ir));
+               ndev->last_rx = jiffies;
+       }
+
+       /* There are some Rx datas in FIFO less than watermark */
+       if (ssr1 & FSL_IR_SSR1_AGTIM) {
+               while (in_be32(&ir->reg_base->ssr2) & 0x1)
+                       async_unwrap_char(ndev, &ir->stats, &ir->rx_buff,
+                                                               sir_get(ir));
+               ndev->last_rx = jiffies;
+       }
+
+       out_be32(&ir->reg_base->ssr1, ssr1);
+       out_be32(&ir->reg_base->ssr2, ssr2);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t fsl_ir_fir_irq(struct net_device *ndev)
+{
+       struct fsl_ir *ir = netdev_priv(ndev);
+       u32 tsr, rsr;
+
+       tsr = in_be32(&ir->reg_base->firitsr);
+       rsr = in_be32(&ir->reg_base->firirsr);
+
+       out_be32(&ir->reg_base->firitsr, tsr);
+       out_be32(&ir->reg_base->firirsr, rsr);
+
+       /* Tx completed */
+       if ((tsr & FSL_FIRITSR_TC) && (!ir->tx_buff.len)) {
+               clrbits32(&ir->reg_base->firitcr, FSL_FIRITCR_TE);
+               if (ir->new_speed) {
+                       fsl_ir_set_speed(ndev, ir->new_speed);
+                       ir->new_speed = 0;
+               }
+               ir->tx_buff.skb = NULL;
+               /* Tx finish */
+               netif_wake_queue(ndev);
+               goto out;
+       }
+
+       /* Rx errors */
+       if (rsr & (FSL_FIRIRSR_CRCE | FSL_FIRIRSR_DDE | FSL_FIRIRSR_RFO)) {
+               ir->stats.rx_errors++;
+               if (rsr & FSL_FIRIRSR_CRCE)
+                       ir->stats.rx_crc_errors++;
+               if (rsr & FSL_FIRIRSR_DDE)
+                       ir->stats.rx_frame_errors++;
+               if (rsr & FSL_FIRIRSR_RFO) {
+                       ir->stats.rx_over_errors++;
+                       /* Clean FIFO, and restart Rx */
+                       clrbits32(&ir->reg_base->firircr, FSL_FIRIRCR_RE);
+                       setbits32(&ir->reg_base->firircr, FSL_FIRIRCR_RE);
+               }
+               goto out;
+       }
+
+       /* Rx */
+       if (rsr & FSL_FIRIRSR_RPE)
+               fir_rx(ir, (rsr >> 8) & 0xff);
+
+out:
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t fsl_ir_do_interrupt(int irq, void *data)
+{
+       struct net_device *ndev = data;
+       struct fsl_ir *ir = netdev_priv(ndev);
+
+       return is_sir(ir) ? fsl_ir_sir_irq(ndev) : fsl_ir_fir_irq(ndev);
+}
+
+static void fsl_ir_tx_do_tasklet(unsigned long data)
+{
+       struct fsl_ir *ir = (struct fsl_ir *)data;
+
+       if (is_sir(ir)) {
+               int i;
+               i = FSL_SIR_TX_MAX
+                       - ((in_be32(&ir->reg_base->sfcr) >> 10) & 0x3f);
+
+               spin_lock(&ir->tx_lock);
+               while (ir->tx_buff.len && i--) {
+                       sir_put(ir, *ir->tx_buff.data++);
+                       ir->tx_buff.len--;
+               }
+
+               if (ir->tx_buff.len)
+                       setbits32(&ir->reg_base->scr1, FSL_IR_SCR1_TRDYEN);
+               else
+                       setbits32(&ir->reg_base->scr4, FSL_IR_SCR4_TCEN);
+               spin_unlock(&ir->tx_lock);
+       }
+}
+
+static struct net_device_stats *fsl_ir_stats(struct net_device *ndev)
+{
+       struct fsl_ir *ir = netdev_priv(ndev);
+       return &ir->stats;
+}
+
+static int fsl_ir_ioctl(struct net_device *ndev, struct ifreq *ifreq, int cmd)
+{
+       struct if_irda_req *rq = (struct if_irda_req *)ifreq;
+       struct fsl_ir *ir = netdev_priv(ndev);
+       int err = 0;
+
+       switch (cmd) {
+       case SIOCSBANDWIDTH:
+               if (capable(CAP_NET_ADMIN)) {
+                       if (ir->tx_buff.len)
+                               ir->new_speed = rq->ifr_baudrate;
+                       else
+                               err = fsl_ir_set_speed(ndev, rq->ifr_baudrate);
+               }
+               break;
+       case SIOCSMEDIABUSY:
+               if (capable(CAP_NET_ADMIN))
+                       irda_device_set_media_busy(ndev, TRUE);
+               else
+                       err = -EPERM;
+               break;
+       case SIOCGRECEIVING:
+               rq->ifr_receiving = is_sir(ir) ?
+                               ir->rx_buff.state != OUTSIDE_FRAME : 0;
+               break;
+       default:
+               err = -EOPNOTSUPP;
+               break;
+       }
+
+       return err;
+}
+
+static int fsl_ir_hard_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct fsl_ir *ir = netdev_priv(ndev);
+       int speed = irda_get_next_speed(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 != ir->speed && speed != -1)
+               ir->new_speed = speed;
+
+       /*
+        * If this is an empty frame, we can bypass a lot.
+        */
+       if (skb->len == 0) {
+               if (ir->new_speed) {
+                       ir->new_speed = 0;
+                       fsl_ir_set_speed(ndev, speed);
+               }
+               dev_kfree_skb(skb);
+               return 0;
+       }
+
+       /* Tx buff is one */
+       netif_stop_queue(ndev);
+       if (is_sir(ir)) {
+               ir->tx_buff.data = ir->tx_buff.head;
+               ir->tx_buff.len = async_wrap_skb(skb, ir->tx_buff.data,
+                                                ir->tx_buff.truesize);
+               dev_kfree_skb(skb);
+               setbits32(&ir->reg_base->scr1, FSL_IR_SCR1_TRDYEN);
+       } else {
+               unsigned int mtt = irda_get_mtt(skb);
+               struct timeval now;
+               int pending;
+
+               ir->tx_buff.skb = skb;
+               ir->tx_buff.len = skb->len;
+
+               /*
+                * If we have a mean turn-around time, impose the specified
+                * specified delay.  We could shorten this by timing from
+                * the point we received the packet.
+                */
+               if (mtt) {
+                       do_gettimeofday(&now);
+                       pending = now.tv_usec - ir->stamp.tv_usec;
+
+                       if (pending < 0)
+                               pending += 1000000;
+
+                       if (mtt > pending)
+                               mtt -= pending;
+                       else if (pending < mtt * 2)
+                               mtt = 0;
+                       udelay(mtt);
+               }
+
+               /* Enable Tx */
+               out_be32(&ir->reg_base->firitctr, skb->len - 1);
+               setbits32(&ir->reg_base->firitcr, FSL_FIRITCR_TE);
+               fir_tx(ir);
+       }
+
+       ndev->trans_start = jiffies;
+       return 0;
+}
+
+static int fsl_ir_open(struct net_device *ndev)
+{
+       struct fsl_ir *ir = netdev_priv(ndev);
+       int err = 0;
+       unsigned int baudrate_mask;
+
+       dev_info(ir->dev, "%s open\n", ndev->name);
+
+       irda_init_max_qos_capabilies(&ir->qos);
+
+       /*
+        * We support SIR, MIR and FIR rate.
+        */
+       baudrate_mask = IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200
+                       | IR_576000 | IR_1152000        /* MIR rates */
+                       | (IR_4000000 << 8);            /* FIR rates */
+       ir->qos.baud_rate.bits &= baudrate_mask;
+       ir->qos.min_turn_time.bits = 7; /* Min turn around time is 1ms */
+
+       /* 64 bytes receive size, since FIFO buffer size is 128 bytes. */
+       ir->qos.data_size.bits = 0x01;
+
+       irda_qos_bits_to_value(&ir->qos);
+
+       ir->speed = 0;
+       fsl_ir_start(ndev);
+
+       /*http://www.kernel.org/pub/scm/linux/kernel/git/avi/kvm.git
+        * Open a new IrLAP layer instance.
+        */
+       ir->irlap = irlap_open(ndev, &ir->qos, "fsl-irda");
+       if (!ir->irlap) {
+               err = -ENOMEM;
+               goto err;
+       }
+
+       if (ir->irq != NO_IRQ) {
+               err = request_irq(ir->irq, &fsl_ir_do_interrupt, IRQF_SHARED,
+                                       "fsl-irda", ndev);
+               if (err)
+                       dev_err(&ndev->dev, "fsl irda request_irq error "
+                               "with return %d\n", err);
+       }
+
+       netif_start_queue(ndev);
+       return 0;
+
+err:
+       free_irq(ir->irq, ndev);
+       irlap_close(ir->irlap);
+       fsl_ir_halt(ir);
+       return err;
+}
+
+static int fsl_ir_stop(struct net_device *ndev)
+{
+       struct fsl_ir *ir = netdev_priv(ndev);
+
+       irlap_close(ir->irlap);
+       ir->irlap = NULL;
+
+       netif_stop_queue(ndev);
+       free_irq(ir->irq, ndev);
+       fsl_ir_halt(ir);
+       return 0;
+}
+
+static int of_fsl_ir_probe(struct of_device *ofdev,
+                       const struct of_device_id *match)
+{
+       struct net_device *ndev;
+       struct fsl_ir *ir = NULL;
+       int err;
+       const void *of_val;
+       int len;
+       struct device_node *cpu_node;
+
+       ndev = alloc_irdadev(sizeof(struct fsl_ir));
+       if (!ndev) {
+               err = -ENOMEM;
+               goto err;
+       }
+
+       ir = ndev->priv;
+       ir->dev = &ofdev->dev;
+       ir->plat_op = ofdev->node->data;
+
+       spin_lock_init(&ir->tx_lock);
+       spin_lock_init(&ir->rx_lock);
+
+       /* get register base from of-node */
+       err = of_address_to_resource(ofdev->node, 0, &ir->res);
+       if (err) {
+               dev_err(&ofdev->dev, "Can't get %s property 'reg'\n",
+                               ofdev->node->full_name);
+               goto err;
+       }
+
+       ir->reg_base = ioremap(ir->res.start, ir->res.end - ir->res.start + 1);
+       tasklet_init(&ir->tx_tasklet, fsl_ir_tx_do_tasklet, (unsigned long)ir);
+
+       cpu_node = of_find_node_by_type(NULL, "cpu");
+       if (!cpu_node) {
+               err = -ENODEV;
+               goto err;
+       }
+
+       of_val = of_get_property(cpu_node, "bus-frequency", &len);
+       if (of_val)
+               ir->clock_in = *(u32 *)of_val / 2;
+
+       /* Get irq from of-node */
+       ir->irq = irq_of_parse_and_map(ofdev->node, 0);
+
+       ndev->hard_start_xmit   = fsl_ir_hard_xmit;
+       ndev->open              = fsl_ir_open;
+       ndev->stop              = fsl_ir_stop;
+       ndev->do_ioctl          = fsl_ir_ioctl;
+       ndev->get_stats         = fsl_ir_stats;
+       ndev->irq               = ir->irq;
+
+       err = register_netdev(ndev);
+       if (err)
+               goto err;
+
+       dev_set_drvdata(&ofdev->dev, ndev);
+
+       dev_info(&ofdev->dev, "Found fsl-irda controller\n");
+       dev_info(&ofdev->dev, "Clock-in frequency %dHz, irq %d, %s\n",
+                               ir->clock_in, ir->irq, ndev->name);
+
+err:
+       return err;
+}
+
+static int of_fsl_ir_remove(struct of_device *ofdev)
+{
+       struct net_device *ndev = dev_get_drvdata(&ofdev->dev);
+
+       if (ndev) {
+               unregister_netdev(ndev);
+               free_netdev(ndev);
+       }
+       return 0;
+}
+
+static struct of_device_id of_fsl_ir_ids[] = {
+       { .compatible = "fsl,mpc8610-irda", },
+       {}
+};
+
+static struct of_platform_driver of_fsl_ir_drv = {
+       .owner          = THIS_MODULE,
+       .name           = "of-fsl-irda",
+       .match_table    = of_fsl_ir_ids,
+       .probe          = of_fsl_ir_probe,
+       .remove         = of_fsl_ir_remove,
+};
+
+static __init int of_fsl_ir_init(void)
+{
+       return of_register_platform_driver(&of_fsl_ir_drv);
+}
+
+static void __exit of_fsl_ir_exit(void)
+{
+       of_unregister_platform_driver(&of_fsl_ir_drv);
+}
+
+device_initcall(of_fsl_ir_init);
+module_exit(of_fsl_ir_exit);
+
+MODULE_AUTHOR("Zhang Wei <[EMAIL PROTECTED]>");
+MODULE_DESCRIPTION("Freescale MPC8610 IrDA driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/fsl_ir.h b/include/linux/fsl_ir.h
new file mode 100644
index 0000000..2ee623a
--- /dev/null
+++ b/include/linux/fsl_ir.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Zhang Wei, [EMAIL PROTECTED], Oct. 2007
+ *
+ * Description:
+ * This file is the header file for Freescale IrDA driver.
+ *
+ * Changelog:
+ * Oct 2007 Zhang Wei <[EMAIL PROTECTED]>
+ * - Initial version.
+ *
+ * This file is part of the Linux kernel
+ *
+ * This 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.
+ *
+ */
+#ifndef __LINUX_FSL_IR_H
+#define __LINUX_FSL_IR_H
+
+#include <linux/device.h>
+#include <net/irda/irda.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/irda_device.h>
+
+enum fsl_ir_mode {
+       FSL_IR_CLOSE,
+       FSL_IR_SIR,
+       FSL_IR_MIR,
+       FSL_IR_FIR
+};
+
+#define FSL_IR_SRXD_CHARRDY    0x00008000 /* Character ready */
+#define FSL_IR_SRXD_ERR                0x00004000 /* Error detect */
+#define FSL_IR_SRXD_OVRRUN     0x00002000 /* Receiver overrun */
+#define FSL_IR_SRXD_FRMERR     0x00001000 /* Frame error */
+#define FSL_IR_SRXD_PRERR      0x00000400 /* Parity error */
+
+#define FSL_IR_SCR1_TRDYEN     0x00002000 /* Transmitter ready enable */
+#define FSL_IR_SCR1_RRDYEN     0x00000200 /* Receive ready interrupt enable */
+#define FSL_IR_SCR1_RXDMAEN    0x00000100 /* Receive ready DMA enable */
+#define FSL_IR_SCR1_IREN       0x00000080 /* Infrared interface enable */
+#define FSL_IR_SCR1_TXDMAEN    0x00000008 /* Transmitter ready DMA enable */
+#define FSL_IR_SCR1_ATDMAEN    0x00000004 /* Aging DMA timer enable */
+#define FSL_IR_SCR1_SIRIEN     0x00000001 /* SIRI Enable */
+
+#define FSL_IR_SCR2_PREN       0x00000100 /* Parity enable */
+#define FSL_IR_SCR2_PROE       0x00000080 /* Parity odd/even */
+#define FSL_IR_SCR2_STPB       0x00000040 /* Stop bit */
+#define FSL_IR_SCR2_WS         0x00000020 /* Word size */
+#define FSL_IR_SCR2_ATEN       0x00000008 /* Aging timer enable */
+#define FSL_IR_SCR2_TXEN       0x00000004 /* Transmitter enable */
+#define FSL_IR_SCR2_RXEN       0x00000002 /* Receiver enable */
+#define FSL_IR_SCR2_SRST       0x00000001 /* Software reset */
+
+#define FSL_IR_SCR3_RXDSEN     0x00000040 /* Receive status interrupt enable */
+#define FSL_IR_SCR3_AIRINTEN   0x00000020 /* Asynchronous IR Wake int enable */
+#define FSL_IR_SCR3_AWAKEN     0x00000010 /* Asynchronous WAKE int enable */
+#define FSL_IR_SCR3_INVT       0x00000002 /* Inverted infrared transmission */
+
+#define FSL_IR_SCR4_INVR       0x00000200 /* Inverted infrared reception */
+#define FSL_IR_SCR4_ENIRI      0x00000100 /* Serial irda interrupt enable*/
+#define FSL_IR_SCR4_IRSC       0x00000020 /* IR special case */
+#define FSL_IR_SCR4_LPBYP      0x00000010 /* Low power bypass */
+#define FSL_IR_SCR4_TCEN       0x00000008 /* Transmit complete int enable */
+#define FSL_IR_SCR4_DREN       0x00000001 /* Receive data ready int enable */
+
+#define FSL_IR_SFCR_RFDIV_MASK 0x00000380
+#define FSL_IR_SFCR_RFDIV_6    0x00000000
+#define FSL_IR_SFCR_RFDIV_5    0x00000080
+#define FSL_IR_SFCR_RFDIV_4    0x00000100
+#define FSL_IR_SFCR_RFDIV_3    0x00000180
+#define FSL_IR_SFCR_RFDIV_2    0x00000200
+#define FSL_IR_SFCR_RFDIV_1    0x00000210
+#define FSL_IR_SFCR_RFDIV_7    0x00000300
+
+#define FSL_IR_STS_LOOP                0x00001000      /* Loop TX and RX for 
test */
+#define FSL_IR_STS_LOOPIR      0x00000400      /* Loop TX and RX for test */
+#define FSL_IR_STS_TXFULL      0x00000010      /* TX FIFO full */
+
+#define FSL_IR_SSR1_TRDY       0x00002000      /* Transmitter ready */
+#define FSL_IR_SSR1_RRDY       0x00000200      /* Receiver ready */
+#define FSL_IR_SSR1_AGTIM      0x00000100      /* Aging timer interrupt flag */
+
+#define FSL_IR_SSR2_TXDC       0x00000008      /* Transmitter is complete */
+#define FSL_IR_SSR2_RDR                0x00000001      /* Receive data is 
ready */
+
+#define FSL_FIRITCR_TDT_32     0x00000800      /* TDT trigger leverl = 32 */
+#define FSL_FIRITCR_TDT_16     0x00000400      /* TDT trigger leverl = 16 */
+#define FSL_FIRITCR_TCIE       0x00000200      /* Transmit complete INT en */
+#define FSL_FIRITCR_PCF                0x00000040      /* Send abort if 
underrun */
+#define FSL_FIRITCR_PC         0x00000020      /* Send abort is sip == 1 */
+#define FSL_FIRITCR_TM_FIR     0x00000000
+#define FSL_FIRITCR_TM_MIR_576 0x00000002
+#define FSL_FIRITCR_TM_MIR_1152        0x00000004
+#define FSL_FIRITCR_TE         0x00000001
+
+#define FSL_FIRIRCR_RAM                0x03000000      /* Match RA and 
broadcast */
+#define FSL_FIRIRCR_RPEDE      0x00000800      /* Enable Rx DMA request */
+#define FSL_FIRIRCR_RDT_96     0x00000600      /* Rx DMA trigger level is 96 */
+#define FSL_FIRIRCR_RDT_32     0x00000200      /* Rx DMA trigger level is 32 */
+#define FSL_FIRIRCR_RDT_16     0x00000100      /* Rx DMA trigger level is 32 */
+#define FSL_FIRIRCR_RPA                0x00000080      /* Clear FIFO is 
illegal recv */
+#define FSL_FIRIRCR_RPEIE      0x00000040      /* Recv packet end INT enable */
+#define FSL_FIRIRCR_PAIE       0x00000020      /* Recv abort INT enable */
+#define FSL_FIRIRCR_RFOIE      0x00000010      /* Recv overrun INT enable */
+#define FSL_FIRIRCR_RPP                0x00000008      /* Recv signal is 
inverted*/
+#define FSL_FIRIRCR_RM_FIR     0x00000000
+#define FSL_FIRIRCR_RM_MIR_576 0x00000002
+#define FSL_FIRIRCR_RM_MIR_1152        0x00000004
+#define FSL_FIRIRCR_RE         0x00000001
+
+#define FSL_FIRITSR_TC         0x00000008      /* Transmit complete */
+#define FSL_FIRITSR_TPE                0x00000002      /* Transmit Packet 
complete */
+
+#define FSL_FIRIRSR_RFP_mask   0x0000ff00      /* Receiver FIFO point mask */
+#define FSL_FIRIRSR_PAS                0x00000020      /* Preamble search */
+#define FSL_FIRIRSR_RPE                0x00000010      /* Receiver packet end 
*/
+#define FSL_FIRIRSR_RFO                0x00000008      /* Receiver FIFO 
overrun */
+#define FSL_FIRIRSR_BAM                0x00000006      /* Broadcast address 
match */
+#define FSL_FIRIRSR_CRCE       0x00000002      /* Receiver CRC error */
+#define FSL_FIRIRSR_DDE                0x00000001      /* Receiver DD error */
+
+struct fsl_ir_reg {
+       __be32  srxd;           /* 0x00: SIRI receiver register */
+       u8      res1[0x3c];
+       __be32  stxd;           /* 0x40: SIRI transmitter register */
+       u8      res2[0x3c];
+       __be32  scr1;           /* 0x80: SIRI control register 1 */
+       __be32  scr2;           /* 0x84: SIRI control register 2 */
+       __be32  scr3;           /* 0x88: SIRI control register 3 */
+       __be32  scr4;           /* 0x8c: SIRI control register 4 */
+       __be32  sfcr;           /* 0x90: SIRI FIFO control register */
+       __be32  ssr1;           /* 0x94: SIRI status register 1 */
+       __be32  ssr2;           /* 0x98: SIRI status register 2 */
+       __be32  sesc;           /* 0x9c: SIRI escape character register */
+       __be32  stim;           /* 0xa0: SIRI escape timer register */
+       __be32  sbir;           /* 0xa4: SIRI BRM incremental register */
+       __be32  sbmr;           /* 0xa8: SIRI BRM modulator register */
+       __be32  sbrc;           /* 0xac: SIRI baud rate count register */
+       __be32  sonems;         /* 0xb0: SIRI one millisecond register */
+       __be32  sts;            /* 0xb4: SIRI test register */
+       u8      res3[0x48];
+       __be32  firitcr;        /* 0x100: FIRI transmit control register */
+       __be32  firitctr;       /* 0x104: FIRI transmit count register */
+       __be32  firircr;        /* 0x108: FIRI receive control register */
+       __be32  firitsr;        /* 0x10c: FIRI transmit status register */
+       __be32  firirsr;        /* 0x110: FIRI receive status register */
+       __be32  tfifo;          /* 0x114: Transmiter FIFO */
+       __be32  rfifo;          /* 0x118: Receiver FIFO */
+       __be32  firicr;         /* 0x11c: FIRI control register */
+       u8      res4[0xee0];
+};
+
+#define FSL_SIR_TXBUFF_SIZE    14384
+#define FSL_SIR_RXBUFF_SIZE    4000
+
+enum sir_parity {
+       SIR_PARITY_NONE,
+       SIR_PARITY_EVEN,
+       SIR_PARITY_ODD
+};
+
+#define FSL_SIR_TX_LEVEL       2
+#define FSL_SIR_TX_MAX         32
+
+#define FIR_PKG_SIZE           2048
+
+struct fsl_ir;
+
+struct fsl_ir_op {
+       void (*set_ir_mode)(struct fsl_ir *, u32 speed);
+};
+
+struct fsl_ir {
+       struct fsl_ir_reg __iomem       *reg_base;
+       struct resource                 res;
+       struct device                   *dev;
+       int                             irq;
+       struct fsl_ir_op                *plat_op;
+
+       struct irlap_cb                 *irlap;
+       struct qos_info                 qos;
+       struct net_device_stats         stats;
+
+       int                             speed;
+       int                             new_speed;
+       u32                             clock_in;
+       int                             div;
+
+       u8                              txb[FSL_SIR_TXBUFF_SIZE];
+       u8                              rxb[FSL_SIR_RXBUFF_SIZE];
+       iobuff_t                        tx_buff;
+       iobuff_t                        rx_buff;
+
+       spinlock_t                      tx_lock;  /* Lock for Tx */
+       spinlock_t                      rx_lock;  /* Lock for Rx */
+
+       struct tasklet_struct           tx_tasklet;
+       struct delayed_work             rx_work;
+       struct timeval                  stamp;
+};
+
+#endif /* __LINUX_FSL_IR_H */
-- 
1.5.5.1
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-dev

Reply via email to