[PATCH] irda: driver for Freescale FIRI controller
Anton Vorontsov
avorontsov at ru.mvista.com
Thu Jun 5 01:45:10 EST 2008
From: Zhang Wei <wei.zhang at freescale.com>
The driver supports SIR, MIR, FIR modes and maximum 4000000bps rate.
Signed-off-by: Zhang Wei <wei.zhang at freescale.com>
[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 <avorontsov at ru.mvista.com>
---
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, wei.zhang at freescale.com, 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 <wei.zhang at freescale.com>
+ * - 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 <wei.zhang at freescale.com>");
+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, wei.zhang at freescale.com, Oct. 2007
+ *
+ * Description:
+ * This file is the header file for Freescale IrDA driver.
+ *
+ * Changelog:
+ * Oct 2007 Zhang Wei <wei.zhang at freescale.com>
+ * - 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
More information about the Linuxppc-dev
mailing list