--- drivers/net/irda/sa1100_ir.c.orig	Sat Feb  3 21:27:34 2001
+++ drivers/net/irda/sa1100_ir.c	Mon Feb  5 01:11:18 2001
@@ -5,7 +5,11 @@
  *
  * (C) 2000 Russell King
  *
- * We currently only use HP-SIR mode.
+ *
+ * 04 Feb 2001	green@iXcelerator.com - FIR support.
+ *
+ * TODO: get rid of data copying between skb & rx buffer, when using HSSP
+ *
  */
 #include <linux/config.h>
 #include <linux/module.h>
@@ -24,8 +28,11 @@
 #include <net/irda/irda_device.h>
 
 #include <asm/irq.h>
+#include <asm/dma.h>
 #include <asm/hardware.h>
 #include <asm/mach-types.h>
+#include <asm/system.h>
+#include <linux/pci.h>
 
 MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
 MODULE_DESCRIPTION("StrongARM SA1100 IrDA driver");
@@ -54,8 +61,13 @@
 
 	iobuff_t		tx_buff;
 	iobuff_t		rx_buff;
+
 };
 
+int rxdma, txdma;
+dma_addr_t dmaposrx, dmapostx;
+static void sa1100_irda_rxdma_irq( void *buf_id, int size );
+static void sa1100_irda_dma_receive(struct net_device *dev);
 /*
  * Set the IrDA communications speed.
  */
@@ -79,11 +91,16 @@
 		Ser2UTCR2 = brd;
 
 		Ser2UTCR3 = si->utcr3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE;
+		Ser2HSCR2 = HSCR2_RcDataH | HSCR2_TrDataH;
 
 #ifdef CONFIG_SA1100_ASSABET
 		if (machine_is_assabet())
 			BCR_clear(BCR_IRDA_FSEL);
 #endif
+#ifdef CONFIG_SA1100_BITSY
+		if (machine_is_bitsy())
+			clr_bitsy_egpio(EGPIO_BITSY_IR_FSEL);
+#endif
 
 		si->speed = speed;
 		si->using_hssr = 0;
@@ -94,14 +111,16 @@
 
 	case 4000000:
 		/* not currently supported */
-#if 0
+		//printk("sa1100_ir: Enabling FIR\n");
+#if 1
 		save_flags(flags);
 		cli();
 
 		Ser2UTCR3 = si->utcr3 & ~(UTCR3_TXE | UTCR3_RXE);
 		Ser2HSCR0 = si->hscr0 & ~(HSCR0_TXE | HSCR0_RXE);
-
-		si->hscr0 = HSCR0_RIE | HSCR0_TXE | HSCR0_RXE | HSCR0_HSSP;
+		Ser2HSSR0 = 0xff; // clear all error conditions
+		
+		si->hscr0 = HSCR0_HSSP | HSCR0_TXE /*| HSCR0_LBM */;
 		si->utcr3 = 0;
 
 		Ser2HSCR0 = si->hscr0;
@@ -109,14 +128,21 @@
 
 		si->speed = speed;
 		si->using_hssr = 1;
+		Ser2HSCR2 = HSCR2_RcDataL | HSCR2_TrDataH;
 
 #ifdef CONFIG_SA1100_ASSABET
 		if (machine_is_assabet())
 			BCR_set(BCR_IRDA_FSEL);
 #endif
+#ifdef CONFIG_SA1100_BITSY
+		if (machine_is_bitsy())
+			set_bitsy_egpio(EGPIO_BITSY_IR_FSEL);
+#endif
 
 		restore_flags(flags);
 #endif
+		sa1100_irda_dma_receive(netdev);
+		ret = 0;
 		break;
 
 	default:
@@ -268,6 +294,148 @@
 }
 #endif
 
+static void sa1100_irda_dma_receive(struct net_device *dev)
+{
+	struct sa1100_irda *si = dev->priv;
+
+	//printk("sa1100_ir: Started FIR receive\n");
+	// First empty receive FIFO
+	Ser2HSCR0 = si->hscr0 & ~HSCR0_RXE;
+	si->hscr0 |= HSCR0_RXE;
+	Ser2HSCR0 = si->hscr0;
+	si->rx_buff.data = si->rx_buff.head;
+
+	// Here one would enable DMA and stuff
+	sa1100_dma_stop(rxdma);
+	sa1100_dma_flush_all(rxdma);
+	if (dmaposrx)
+		pci_unmap_single(NULL, dmaposrx, si->rx_buff.truesize, PCI_DMA_FROMDEVICE);
+	dmaposrx = pci_map_single(NULL, si->rx_buff.data, si->rx_buff.truesize, PCI_DMA_FROMDEVICE);
+	sa1100_dma_set_callback(rxdma, sa1100_irda_rxdma_irq);
+	sa1100_dma_queue_buffer(rxdma, NULL, dmaposrx, si->rx_buff.truesize);
+}
+
+static void sa1100_irda_rxdma_irq( void *buf_id, int size )
+{
+	struct sa1100_irda *si = netdev->priv;
+	// Strange, we should never get here.
+	printk("sa1100_irda_rxdma_irq: Should never get here\n");
+	si->stats.rx_over_errors++;
+	sa1100_irda_dma_receive(netdev);
+}
+
+static void sa1100_irda_txdma_irq( void *buf_id, int size )
+{
+	struct sa1100_irda *si = netdev->priv;
+
+	sa1100_irda_dma_receive(netdev);
+	si->stats.tx_packets++;
+	si->stats.tx_bytes+=size;
+	sa1100_dma_set_callback(txdma, NULL);
+	pci_unmap_single(NULL, dmapostx, size, PCI_DMA_TODEVICE);
+	dmapostx=0;
+	dev_kfree_skb_irq((struct sk_buff *)buf_id);
+	netif_wake_queue(netdev);
+}
+
+static void sa1100_irda_dma_receive_complete(struct net_device *dev)
+{
+	struct sa1100_irda *si = dev->priv;
+	struct sk_buff *skb;
+	dma_addr_t dma_addr;
+	int len,bad=0;
+	
+	//printk("sa1100_ir: sa1100_irda_dma_receive_complete\n");
+	sa1100_dma_set_callback(rxdma, NULL);
+	sa1100_dma_stop(rxdma);
+	sa1100_dma_get_current(rxdma, NULL, &dma_addr);
+	// STOP DMA and obtain DMA count
+	pci_unmap_single(NULL, dmaposrx, si->rx_buff.truesize, PCI_DMA_FROMDEVICE);
+	len = dma_addr - dmaposrx;
+	dmaposrx = 0;
+	si->rx_buff.data += len;
+
+	while (Ser2HSSR0 & HSSR0_EIF) {
+		int bstat;
+
+		bstat = Ser2HSSR1;
+
+		if (bstat & HSSR1_ROR)
+			si->stats.rx_fifo_errors++;
+		if (bstat & HSSR1_CRE)
+			si->stats.rx_crc_errors++;
+		if ( bstat & ( HSSR1_ROR | HSSR1_CRE) )
+			bad++;
+
+		*(si->rx_buff.data++) = Ser2HSDR;
+	}
+	if (bad)
+		return;
+	
+	Ser2HSCR0 = si->hscr0 = si->hscr0 & ~HSCR0_RXE;
+	len = si->rx_buff.data - si->rx_buff.head;
+//	printk("sa1100_irda_dma_receive_complete: %d\n",len);
+
+	if (len) {
+		skb = dev_alloc_skb(len+1);
+		if (skb == NULL) {
+			si->stats.rx_dropped++;
+			return;
+		}
+		skb_reserve(skb, 1);
+		skb_put(skb, len);
+		memcpy(skb->data, si->rx_buff.data, len);
+		skb->dev = dev;
+		skb->mac.raw = skb->data;
+		skb->protocol = htons(ETH_P_IRDA);
+		netif_rx(skb);
+		si->stats.rx_packets++;
+		si->stats.rx_bytes += len;
+	}
+
+	return;
+}
+
+
+/*
+ * HSSP format interrupt service routines.
+ */
+static void sa1100_irda_hssp_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = dev_id;
+	struct sa1100_irda *si = dev->priv;
+	int status;
+
+	status = Ser2HSSR0;
+	//printk("sa1100_irda_hssp_irq: %x %x\n",status,Ser2HSSR1);
+
+	/*
+	 * Deal with any receive errors first.  The bytes in error may be
+	 * the only bytes in the receive FIFO, so we do this first.
+	 */
+	while (status & HSSR0_EIF) {
+		sa1100_irda_dma_receive_complete(dev);
+		sa1100_irda_dma_receive(dev);
+		status = Ser2HSSR0;
+	}
+
+	if (status & HSSR0_TUR) {
+		// strange - why are we here?
+		Ser2HSSR0 = HSSR0_TUR;
+	}
+
+	if (status & HSSR0_RAB) {
+		Ser2HSSR0 = HSSR0_RAB;
+		sa1100_irda_dma_receive_complete(dev);
+		sa1100_irda_dma_receive(dev);
+	}
+
+	if (status & HSSR0_FRE) {
+		si->stats.rx_frame_errors++;
+		Ser2HSSR0 = HSSR0_FRE;
+	}
+}
+
 /*
  * HP-SIR format interrupt service routines.
  */
@@ -372,10 +540,48 @@
 		Ser2UTSR0 = status;
 }
 
+/* Generic irq routinue that chooses actual one on basis of format */
+static void sa1100_irda_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct sa1100_irda *si = ((struct net_device *)dev_id)->priv;
+	if ( si->using_hssr) {
+		sa1100_irda_hssp_irq(irq, dev_id, regs);
+	} else {
+		sa1100_irda_hpsir_irq(irq, dev_id, regs);
+	}
+}
+
+static int sa1100_irda_dma_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct sa1100_irda *si = dev->priv;
+
+	if (dmapostx) {
+		netif_stop_queue(dev);
+		return 1;
+	}
+		
+	si->tx_buff.data = si->tx_buff.head;
+
+	if (skb->len > 2047) {
+		printk("sa1100_ir: Trying to send too big packet, size=%d\n", skb->len);
+		si->stats.tx_dropped++;
+		return 1;
+	}
+	si->tx_buff.data=skb->data;
+	dmapostx = pci_map_single(NULL, si->tx_buff.data, skb->len, PCI_DMA_TODEVICE);
+	Ser2HSCR0 = si->hscr0 & ~HSCR0_RXE;
+	sa1100_dma_set_callback(txdma, sa1100_irda_txdma_irq);
+	sa1100_dma_queue_buffer(txdma, skb, dmapostx, skb->len);
+	dev->trans_start = jiffies;
+
+	return 0;
+}
+
 static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct sa1100_irda *si = dev->priv;
 	int speed = irda_get_speed(skb);
+	int ret = 0;
 
 	/*
 	 * If the speed has changed, schedule an update just
@@ -401,13 +607,16 @@
 		Ser2UTCR3 = si->utcr3;
 
 		restore_flags(flags);
-
 		dev->trans_start = jiffies;
+		kfree_skb(skb);
+
+	} else {
+		ret = sa1100_irda_dma_xmit(skb, dev);
 	}
 
-	kfree_skb(skb);
+	//printk("sa1100_ir: Queued TX packet for transmission %d\n",ret);
 
-	return 0;
+	return ret;
 }
 
 static int sa1100_irda_ioctl(struct net_device *dev, struct ifreq *ifreq,
@@ -496,6 +705,11 @@
 {
 	si->open = 0;
 
+#ifdef CONFIG_SA1100_BITSY
+	if (machine_is_bitsy())
+		clr_bitsy_egpio(EGPIO_BITSY_IR_ON);
+#endif
+
 	/* Disable the port. */
 	si->utcr3 = 0;
 	si->hscr0 = 0;
@@ -510,9 +724,11 @@
 
 	MOD_INC_USE_COUNT;
 
-	si->speed = 9600;
+	// Why not to start with higher speed? If people wish so, they can
+	// reduce it later (or even use ir_tty)
+	si->speed = 4000000;
 
-	err = request_irq(dev->irq, sa1100_irda_hpsir_irq, 0, dev->name, dev);
+	err = request_irq(dev->irq, sa1100_irda_irq, 0, dev->name, dev);
 	if (err)
 		goto out;
 
@@ -521,6 +737,10 @@
 	 */
 	disable_irq(dev->irq);
 
+#ifdef CONFIG_SA1100_BITSY
+	if (machine_is_bitsy())
+		set_bitsy_egpio(EGPIO_BITSY_IR_ON);
+#endif
 	/*
 	 * Setup the serial port for the specified speed.
 	 */
@@ -616,17 +836,17 @@
 	irda_init_max_qos_capabilies(&si->qos);
 
 	/*
-	 * We support original IRDA up to 115k2. (we don't currently
-	 * support 4Mbps).  Min Turn Time set to 1ms or greater.
+	 * We support original IRDA up to 115k2 and IrDA 1.2 FIR at 4 Mbps
+	 * Min Turn Time set to 1ms or greater.
 	 */
 	si->qos.baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|
-				  IR_115200/*|IR_4000000*/;
+				  IR_115200|IR_4000000;
 	si->qos.min_turn_time.bits = 7;
 
 	irda_qos_bits_to_value(&si->qos);
 
 	/*
-	 * Enable HP-SIR modulation, and ensure that the port is disabled.
+	 * Enable HP-SIR modulation for the beginning, and ensure that the port is disabled.
 	 */
 	Ser2UTCR3 = si->utcr3 = 0;
 	Ser2UTCR4 = si->utcr4 = UTCR4_HPSIR | UTCR4_Z3_16Bit;
@@ -669,6 +889,9 @@
 
 	pm_unregister(si->pmdev);
 
+	if (dmaposrx)
+		pci_unmap_single(NULL, dmaposrx, si->rx_buff.truesize, PCI_DMA_FROMDEVICE);
+
 	kfree(si->tx_buff.head);
 	kfree(si->rx_buff.head);
 	kfree(si);
@@ -682,6 +905,17 @@
 	struct net_device *dev;
 	int err;
 
+	err = sa1100_request_dma(&rxdma, "IrDA receive");
+	if (err) {
+		printk("sa1100_ir: unable to register rx dma\n");
+		goto err_rx_dma;
+	}
+	err = sa1100_request_dma(&txdma, "IrDA transmit");
+	if (err) {
+		printk("sa1100_ir: unable to register tx dma\n");
+		goto err_tx_dma;
+	}
+
 	rtnl_lock();
 	dev = dev_alloc("irda%d", &err);
 	if (dev) {
@@ -692,11 +926,21 @@
 		err = register_netdevice(dev);
 
 		if (err)
-			kfree(dev);
+			goto err_net_dev;
 		else
 			netdev = dev;
 	}
 	rtnl_unlock();
+	sa1100_dma_set_device(rxdma, DMA_Ser2HSSPRd);
+	sa1100_dma_set_device(txdma, DMA_Ser2HSSPWr);
+	return 0;
+
+err_net_dev:
+	kfree(dev);
+        sa1100_free_dma(txdma);
+err_tx_dma:
+        sa1100_free_dma(rxdma);
+err_rx_dma:
 	return err;
 }
 
@@ -717,6 +961,9 @@
 	 * which may still be present is the netdevice, which will get
 	 * cleaned up by net/core/dev.c
 	 */
+
+        sa1100_free_dma(txdma);
+        sa1100_free_dma(rxdma);
 }
 
 #ifdef MODULE

