[PATCH] [POWERPC] 8xx: PQ SoC IRDA support

Subsystems: linux for powerpc (32-bit and 64-bit), linux for powerpc embedded ppc8xx and ppc83xx, networking drivers, the rest

STALE7039d

3 messages, 2 authors, 2007-05-08 · open the first message on its own page

[PATCH] [POWERPC] 8xx: PQ SoC IRDA support

From: Vitaly Bordug <hidden>
Date: 2007-05-08 05:30:34

Adds support of IRDA transceiver residing on PowerQUICC processors and
enabling such on mpc885ads reference board. The driver is implemented 
using of_device concept, hereby implies arch/powerpc support of the target.

Signed-off-by: Vitaly Bordug <redacted>

---

 arch/powerpc/Kconfig                         |    1 
 arch/powerpc/boot/dts/mpc885ads.dts          |   10 
 arch/powerpc/platforms/8xx/mpc885ads_setup.c |   39 +
 arch/powerpc/sysdev/fsl_soc.c                |   58 ++
 drivers/net/irda/Kconfig                     |    4 
 drivers/net/irda/Makefile                    |    1 
 drivers/net/irda/m8xx-sir.c                  |  715 ++++++++++++++++++++++++++
 include/asm-ppc/commproc.h                   |   67 ++
 8 files changed, 895 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index a8e08f4..c283907 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -605,6 +605,7 @@ endmenu
 config ISA_DMA_API
 	bool
 	default y
+	depends on !PPC_8xx
 
 menu "Bus options"
 
diff --git a/arch/powerpc/boot/dts/mpc885ads.dts b/arch/powerpc/boot/dts/mpc885ads.dts
index 110bf61..95b280c 100644
--- a/arch/powerpc/boot/dts/mpc885ads.dts
+++ b/arch/powerpc/boot/dts/mpc885ads.dts
@@ -178,6 +178,16 @@
 				interrupt-parent = <930>;
 				phy-handle = <e8002>;
 			};
+
+			scc@a20 {
+				device_type = "network";
+				compatible = "fsl,irda";
+				model = "SCC";
+				device-id = <2>;
+				reg = <a20 18 3d00 80>;
+				interrupts = <1d 3>;
+				interrupt-parent = <930>;
+			};
 		};
 	};
 };
diff --git a/arch/powerpc/platforms/8xx/mpc885ads_setup.c b/arch/powerpc/platforms/8xx/mpc885ads_setup.c
index a57b577..8611318 100644
--- a/arch/powerpc/platforms/8xx/mpc885ads_setup.c
+++ b/arch/powerpc/platforms/8xx/mpc885ads_setup.c
@@ -50,6 +50,7 @@ extern unsigned int mpc8xx_get_irq(void);
 static void init_smc1_uart_ioports(struct fs_uart_platform_info* fpi);
 static void init_smc2_uart_ioports(struct fs_uart_platform_info* fpi);
 static void init_scc3_ioports(struct fs_platform_info* ptr);
+static void init_irda_ioports(void);
 
 void __init mpc885ads_board_setup(void)
 {
@@ -115,6 +116,10 @@ void __init mpc885ads_board_setup(void)
 	immr_unmap(io_port);
 
 #endif
+
+#ifdef CONFIG_8XX_SIR
+	init_irda_ioports();
+#endif
 }
 
 
@@ -322,6 +327,40 @@ void init_smc_ioports(struct fs_uart_platform_info *data)
 	}
 }
 
+static void init_irda_ioports()
+{
+	iop8xx_t *io_port;
+	cpm8xx_t *cp;
+	unsigned *bcsr_io;
+
+	bcsr_io = ioremap(BCSR1, sizeof(unsigned long));
+
+	if (bcsr_io == NULL) {
+		printk(KERN_CRIT "Could not remap BCSR1\n");
+		return;
+	}
+
+	/* Enable the IRDA. */
+	clrbits32(bcsr_io,BCSR1_IRDAEN);
+	iounmap(bcsr_io);
+
+	io_port = (iop8xx_t *)immr_map(im_ioport);
+	cp = (cpm8xx_t *)immr_map(im_cpm);
+
+	/* Configure port A pins. */
+	setbits16(&io_port->iop_papar, 0x000c);
+	clrbits16(&io_port->iop_padir, 0x000c);
+
+	/* Configure Serial Interface clock routing.
+	 * First, clear all SCC bits to zero, then set the ones we want.
+	 */
+	clrbits32(&cp->cp_sicr, 0x0000ff00);
+	setbits32(&cp->cp_sicr, 0x00001200);
+
+	immr_unmap(io_port);
+	immr_unmap(cp);
+}
+
 int platform_device_skip(const char *model, int id)
 {
 #ifdef CONFIG_MPC8xx_SECOND_ETH_SCC3
diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c
index 8a123c7..f827147 100644
--- a/arch/powerpc/sysdev/fsl_soc.c
+++ b/arch/powerpc/sysdev/fsl_soc.c
@@ -1102,4 +1102,62 @@ err:
 
 arch_initcall(cpm_smc_uart_of_init);
 
+static const char *irda_regs = "regs";
+static const char *irda_pram = "pram";
+static const char *irda_irq = "interrupt";
+static char bus_id[9][BUS_ID_SIZE];
+
+static int __init fs_irda_of_init(void)
+{
+	struct device_node *np;
+	unsigned int i;
+	struct platform_device *fs_irda_dev = NULL;
+	int ret;
+
+	for (np = NULL, i = 0;
+	     (np = of_find_compatible_node(np, "network", "fsl,irda")) != NULL;
+	     i++) {
+		struct resource r[4];
+		unsigned int *id;
+		char *model;
+
+		memset(r, 0, sizeof(r));
+
+		model = (char *)get_property(np, "model", NULL);
+		if (model == NULL)
+			return -ENODEV;
+
+		id = (u32 *) get_property(np, "device-id", NULL);
+		if (platform_device_skip(model, *id))
+			continue;
+
+		ret = of_address_to_resource(np, 0, &r[0]);
+		if (ret)
+			return ret;
+		r[0].name = irda_regs;
+
+		if (strstr(model, "SCC")) {
+			ret = of_address_to_resource(np, 1, &r[1]);
+			if (ret)
+				return ret;
+			r[1].name = irda_pram;
+
+			r[2].start = r[2].end = irq_of_parse_and_map(np, 0);
+			r[2].flags = IORESOURCE_IRQ;
+			r[2].name = irda_irq;
+
+			fs_irda_dev =
+				    platform_device_register_simple("fsl-cpm-scc:irda", i, &r[0], 3);
+
+			if (IS_ERR(fs_irda_dev)) {
+				ret = PTR_ERR(fs_irda_dev);
+				return ret;
+			}
+		}
+	}
+	return 0;
+}
+
+arch_initcall(fs_irda_of_init);
+
 #endif /* CONFIG_8xx */
diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig
index 7c8ccc0..b3681e7 100644
--- a/drivers/net/irda/Kconfig
+++ b/drivers/net/irda/Kconfig
@@ -17,6 +17,10 @@ config IRTTY_SIR
 
 	  If unsure, say Y.
 
+config 8XX_SIR
+	  tristate "mpc8xx SIR"
+	  depends on 8xx && IRDA
+
 comment "Dongle support"
 
 config DONGLE
diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile
index 5be09f1..fc7c0fd 100644
--- a/drivers/net/irda/Makefile
+++ b/drivers/net/irda/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_EP7211_IR)		+= ep7211_ir.o
 obj-$(CONFIG_AU1000_FIR)	+= au1k_ir.o
 # New SIR drivers
 obj-$(CONFIG_IRTTY_SIR)		+= irtty-sir.o	sir-dev.o
+obj-$(CONFIG_8XX_SIR)		+= m8xx-sir.o
 # New dongles drivers for new SIR drivers
 obj-$(CONFIG_ESI_DONGLE)	+= esi-sir.o
 obj-$(CONFIG_TEKRAM_DONGLE)	+= tekram-sir.o
diff --git a/drivers/net/irda/m8xx-sir.c b/drivers/net/irda/m8xx-sir.c
new file mode 100644
index 0000000..6fb215f
--- /dev/null
+++ b/drivers/net/irda/m8xx-sir.c
@@ -0,0 +1,715 @@
+/*
+ * Infra-red SIR driver for the MPC8xx processors.
+ *
+ * Copyright (c) 2005-2007 MontaVista Software Inc.
+ * Author: Yuri Shpilevsky <source@mvista.com>
+ *
+ *     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.
+ */
+
+#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/ioport.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/irda_device.h>
+
+#include <asm/io.h>
+#include <asm/cacheflush.h>
+#include <asm/irq.h>
+#include <asm/8xx_immap.h>
+#include <asm/pgtable.h>
+#include <asm/mpc8xx.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/commproc.h>
+#include <asm/fs_pd.h>
+
+/*
+ * Our netdevice.  There is only ever one of these.
+ */
+
+#define CPM_IRDA_RX_PAGES       4
+#define CPM_IRDA_RX_FRSIZE      2048
+#define CPM_IRDA_RX_FRPPG       (PAGE_SIZE / CPM_IRDA_RX_FRSIZE)
+#define RX_RING_SIZE            (CPM_IRDA_RX_FRPPG * CPM_IRDA_RX_PAGES)
+#define TX_RING_SIZE            8	/* Must be power of two */
+#define TX_RING_MOD_MASK        7
+
+struct mpc8xx_irda {
+	unsigned char open;
+
+	int speed;
+	int newspeed;
+
+	/* The saved address of a sent-in-place packet/buffer, for skfree().
+	 */
+	struct sk_buff *tx_skbuff[TX_RING_SIZE];
+	ushort skb_cur;
+	ushort skb_dirty;
+
+	struct net_device_stats stats;
+	struct device *dev;
+	struct irda_platform_data *pdata;
+	struct irlap_cb *irlap;
+	struct qos_info qos;
+
+	scc_t *sccp;
+	scc_ahdlc_t *ahp;
+	int irq;
+	cbd_t *rx_bd_base;
+	cbd_t *tx_bd_base;
+	cbd_t *dirty_tx;
+	cbd_t *cur_rx, *cur_tx;	/* The next free ring entry */
+	unsigned char *rx_vaddr[RX_RING_SIZE];
+	int tx_free;
+	spinlock_t lock;
+};
+
+#define HPSIR_MAX_RXLEN		2050
+#define HPSIR_MAX_TXLEN		2050
+#define TXBUFF_MAX_SIZE		HPSIR_MAX_TXLEN
+
+static void mpc8xx_irda_set_speed(struct net_device *dev, int speed);
+
+/************************************************************************************/
+
+/* Low level init/uninstall function IrDA protocol stack registration
+ */
+
+static void mpc8xx_irda_rx(struct net_device *dev)
+{
+	struct mpc8xx_irda *si = dev->priv;
+	cbd_t *bdp;
+	struct sk_buff *skb;
+	int len;
+	ushort status;
+
+	bdp = si->cur_rx;
+
+	for (;;) {
+		if (bdp->cbd_sc & BD_AHDLC_RX_EMPTY)
+			break;
+		status = bdp->cbd_sc;
+
+		if (status & BD_AHDLC_RX_STATS) {
+			/* process errors
+			 */
+			if (bdp->cbd_sc & BD_AHDLC_RX_AB)
+				si->stats.rx_length_errors++;
+			if (bdp->cbd_sc & BD_AHDLC_RX_CR)	/* CRC Error */
+				si->stats.rx_crc_errors++;
+			if (bdp->cbd_sc & BD_AHDLC_RX_OV)	/* FIFO overrun */
+				si->stats.rx_over_errors++;
+		} else {
+			/* Process the incoming frame.
+			 */
+			len = bdp->cbd_datlen;
+
+			skb = dev_alloc_skb(len + 1);
+			if (skb == NULL) {
+				printk(KERN_INFO
+				       "%s: Memory squeeze, dropping packet.\n",
+				       dev->name);
+				si->stats.rx_dropped++;
+			} else {
+				skb->dev = dev;
+				skb_reserve(skb, 1);
+				memcpy(skb_put(skb, len),
+				       si->rx_vaddr[bdp - si->rx_bd_base], len);
+				skb_trim(skb, skb->len - 2);
+
+				si->stats.rx_packets++;
+				si->stats.rx_bytes += len;
+
+				skb->mac.raw = skb->data;
+				skb->protocol = htons(ETH_P_IRDA);
+				netif_rx(skb);
+			}
+		}
+
+		/* Clear the status flags for this buffer.
+		 */
+		bdp->cbd_sc &= ~BD_AHDLC_RX_STATS;
+
+		/* Mark the buffer empty.
+		 */
+		bdp->cbd_sc |= BD_AHDLC_RX_EMPTY;
+
+		/* Update BD pointer to next entry.
+		 */
+		if (bdp->cbd_sc & BD_AHDLC_RX_WRAP)
+			bdp = si->rx_bd_base;
+		else
+			bdp++;
+	}
+	si->cur_rx = (cbd_t *) bdp;
+}
+
+static irqreturn_t mpc8xx_irda_irq(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	struct mpc8xx_irda *si = dev->priv;
+	scc_t *sccp = si->sccp;
+	cbd_t *bdp;
+	ushort int_events;
+	int must_restart = 0;
+
+	/* Get the interrupt events that caused us to be here.
+	 */
+	int_events = in_be16(&sccp->scc_scce);
+	out_be16(&sccp->scc_scce, int_events);
+
+	/* Handle receive event in its own function.
+	 */
+	if (int_events & SCC_AHDLC_RXF)
+		mpc8xx_irda_rx(dev);
+
+	spin_lock(&si->lock);
+
+	/* Transmit OK, or non-fatal error.  Update the buffer descriptors.
+	 */
+	if (int_events & (SCC_AHDLC_TXE | SCC_AHDLC_TXB)) {
+		bdp = si->dirty_tx;
+
+		while ((bdp->cbd_sc & BD_AHDLC_TX_READY) == 0) {
+			if (si->tx_free == TX_RING_SIZE)
+				break;
+
+			if (bdp->cbd_sc & BD_AHDLC_TX_CTS)
+				must_restart = 1;
+
+			si->stats.tx_packets++;
+
+			/* Free the sk buffer associated with this last transmit.
+			 */
+			dev_kfree_skb_irq(si->tx_skbuff[si->skb_dirty]);
+			si->skb_dirty = (si->skb_dirty + 1) & TX_RING_MOD_MASK;
+
+			/* Update pointer to next buffer descriptor to be transmitted.
+			 */
+			if (bdp->cbd_sc & BD_AHDLC_TX_WRAP)
+				bdp = si->tx_bd_base;
+			else
+				bdp++;
+
+			/* Since we have freed up a buffer, the ring is no longer full.
+			 */
+			if (!si->tx_free++) {
+				if (netif_queue_stopped(dev))
+					netif_wake_queue(dev);
+			}
+
+			si->dirty_tx = (cbd_t *) bdp;
+
+			if (si->newspeed) {
+				mpc8xx_irda_set_speed(dev, si->newspeed);
+				si->speed = si->newspeed;
+				si->newspeed = 0;
+			}
+		}
+
+		if (must_restart) {
+			cpm8xx_t *cp = immr_map(im_cpm);
+			printk(KERN_INFO "restart TX\n");
+
+			/* Some transmit errors cause the transmitter to shut
+			 * down.  We now issue a restart transmit.  Since the
+			 * errors close the BD and update the pointers, the restart
+			 * _should_ pick up without having to reset any of our
+			 * pointers either.
+			 */
+			out_be16(&cp->cp_cpcr,
+			    mk_cr_cmd(CPM_CR_CH_SCC2,
+				      CPM_CR_RESTART_TX) | CPM_CR_FLG);
+			while (in_be16(&cp->cp_cpcr) & CPM_CR_FLG) ;
+		}
+	}
+
+	spin_unlock(&si->lock);
+
+	return IRQ_HANDLED;
+}
+
+static int mpc8xx_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct mpc8xx_irda *si = dev->priv;
+	volatile scc_ahdlc_t *ahp = si->ahp;
+	int speed = irda_get_next_speed(skb);
+	cbd_t *bdp;
+	int mtt;
+	u16 xbofs;
+	unsigned long flags;
+
+	/*
+	 * 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;
+
+	mtt = irda_get_mtt(skb);
+
+	spin_lock_irqsave(&si->lock, flags);
+
+	/*
+	 * If this is an empty frame, we can bypass a lot.
+	 */
+	if (skb->len == 0) {
+		if (si->newspeed) {
+			si->newspeed = 0;
+			mpc8xx_irda_set_speed(dev, speed);
+		}
+		spin_unlock_irqrestore(&si->lock, flags);
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	/* Get a Tx ring entry */
+	bdp = si->cur_tx;
+
+	/* Clear all of the status flags.
+	 */
+	bdp->cbd_sc &= ~BD_AHDLC_TX_STATS;
+
+	/* Set xbofs
+	 */
+	{
+		struct irda_skb_cb *cb = (struct irda_skb_cb *)skb->cb;
+		xbofs =
+		    (cb->magic != LAP_MAGIC) ? 10 : cb->xbofs + cb->xbofs_delay;
+		xbofs = (xbofs > 163) ? 163 : xbofs;
+		out_be16(&ahp->ahdlc_nof, xbofs);
+	}
+
+	/* Set buffer length and buffer pointer.
+	 */
+	bdp->cbd_datlen = skb->len;
+	bdp->cbd_bufaddr = __pa(skb->data);
+
+	/* Push the data cache so the CPM does not get stale memory data.
+	 */
+	flush_dcache_range((unsigned long)(skb->data),
+			   (unsigned long)(skb->data + skb->len));
+
+	/* Save skb pointer.
+	 */
+	si->tx_skbuff[si->skb_cur] = skb;
+
+	si->stats.tx_bytes += skb->len;
+	si->skb_cur = (si->skb_cur + 1) & TX_RING_MOD_MASK;
+
+	/* 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 > 0)
+		udelay(mtt);
+
+	/* Send it on its way.  Tell CPM its ready, interrupt when done,
+	 * its the last BD of the frame, and to put the CRC on the end.
+	 */
+	bdp->cbd_sc |=
+	    (BD_AHDLC_TX_READY | BD_AHDLC_TX_INTR | BD_AHDLC_TX_LAST);
+
+	dev->trans_start = jiffies;
+
+	/* If this was the last BD in the ring, start at the beginning again.
+	 */
+	if (bdp->cbd_sc & BD_AHDLC_TX_WRAP)
+		bdp = si->tx_bd_base;
+	else
+		bdp++;
+
+	spin_unlock_irqrestore(&si->lock, flags);
+
+	if (!--si->tx_free)
+		netif_stop_queue(dev);
+
+	si->cur_tx = (cbd_t *) bdp;
+
+	return 0;
+}
+
+static int
+mpc8xx_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
+{
+	struct if_irda_req *rq = (struct if_irda_req *)ifreq;
+	struct mpc8xx_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)
+				mpc8xx_irda_set_speed(dev, rq->ifr_baudrate);
+			else
+				printk(KERN_INFO
+				       "mpc8xx_irda_ioctl: SIOCSBANDWIDTH: !netif_running\n");
+			ret = 0;
+		}
+		break;
+
+	case SIOCSMEDIABUSY:
+		ret = -EPERM;
+		if (capable(CAP_NET_ADMIN)) {
+			irda_device_set_media_busy(dev, TRUE);
+			ret = 0;
+		}
+		break;
+
+	case SIOCGRECEIVING:
+		rq->ifr_receiving = 0;
+		break;
+
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static struct net_device_stats *mpc8xx_irda_stats(struct net_device *ndev)
+{
+	struct mpc8xx_irda *si = ndev->priv;
+	return &si->stats;
+}
+
+static int mpc8xx_irda_pd_setup(struct mpc8xx_irda *si)
+{
+	struct platform_device *pdev = to_platform_device(si->dev);
+	struct resource *r;
+
+	/* Fill out IRQ field */
+	si->irq = platform_get_irq_byname(pdev, "interrupt");
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+	si->sccp = ioremap(r->start, r->end - r->start + 1);
+
+	if (si->sccp == NULL)
+		return -EINVAL;
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pram");
+	si->ahp = ioremap(r->start, r->end - r->start + 1);
+
+	if (si->ahp == NULL)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int mpc8xx_irda_startup(struct net_device *dev)
+{
+	struct mpc8xx_irda *si = dev->priv;
+	cbd_t *bdp;
+	volatile cpm8xx_t *cp = immr_map(im_cpm);
+	scc_t *sccp;
+	volatile scc_ahdlc_t *ahp;
+	dma_addr_t mem_addr;
+	int i, j, k;
+	unsigned char *ba;
+	int err = -ENOMEM;
+
+	spin_lock_init(&si->lock);
+
+	if ((err = mpc8xx_irda_pd_setup(si)))
+		return err;
+
+	sccp = si->sccp;
+	ahp = si->ahp;
+
+	/* Disable receive and transmit.
+	 */
+	clrbits32(&sccp->scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT);
+
+	/* Allocate space for the buffer descriptors in the DP ram.
+	 * These are relative offsets in the DP ram address space.
+	 * Initialize base addresses for the buffer descriptors.
+	 */
+	i = cpm_dpalloc(sizeof(cbd_t) * RX_RING_SIZE, 8);
+	out_be16(&ahp->scc_genscc.scc_rbase, i);
+	si->rx_bd_base = cpm_dpram_addr(i);
+	si->cur_rx = si->rx_bd_base;
+
+	i = cpm_dpalloc(sizeof(cbd_t) * TX_RING_SIZE, 8);
+	out_be16(&ahp->scc_genscc.scc_tbase, i);
+	si->tx_bd_base = cpm_dpram_addr(i);
+	si->dirty_tx = si->cur_tx = si->tx_bd_base;
+	si->tx_free = TX_RING_SIZE;
+
+	/* Issue init Tx and Rx BD command for SCC2.
+	 */
+	out_be16(&cp->cp_cpcr, mk_cr_cmd(CPM_CR_CH_SCC2, CPM_CR_INIT_TRX) | CPM_CR_FLG);
+	while (in_be16(&cp->cp_cpcr) & CPM_CR_FLG) ;
+
+	si->skb_cur = si->skb_dirty = 0;
+
+	/* Initialize function code registers for big-endian.
+	 */
+	out_8(&ahp->scc_genscc.scc_rfcr, SCC_EB);
+	out_8(&ahp->scc_genscc.scc_tfcr, SCC_EB);
+
+	/* Set maximum bytes per receive buffer.
+	 * This appears to be an Ethernet frame size, not the buffer
+	 * fragment size.  It must be a multiple of four.
+	 */
+	out_be16(&ahp->scc_genscc.scc_mrblr, 14384);
+
+	/* Set CRC preset and mask.
+	 */
+	out_be32(&ahp->ahdlc_cpres, 0x0000ffff);
+	out_be32(&ahp->ahdlc_cmask, 0x0000f0b8);
+
+	/* Clear zero register.
+	 */
+	out_be16(&ahp->ahdlc_zero, 0);
+
+	/* Program RFTHR to the number of frames to be received
+	 * before generating an interrupt.
+	 */
+	out_be16(&ahp->ahdlc_rfthr, 1);
+
+	/* Program the control character tables, TXCTL_TBL and RXCTL_TBL.
+	 */
+	out_be32(&ahp->ahdlc_txctl_tbl, 0);	// initialized to zero for IrLAP.
+	out_be32(&ahp->ahdlc_rxctl_tbl, 0);	// initialized to zero for IrLAP.
+
+	out_be16(&ahp->ahdlc_bof, 0xC0);	//IRLAP_BOF; /* Begin. of Flag Char */
+	out_be16(&ahp->ahdlc_eof, 0xC1);	//IRLAP_EOF; /* End of Flag Char */
+	out_be16(&ahp->ahdlc_esc, 0x7D);	//IRLAP_ESC; /* Control Escape Char */
+	out_be16(&ahp->ahdlc_nof, 12);
+
+	/* Now allocate the host memory pages and initialize the
+	 * buffer descriptors.
+	 */
+	bdp = si->tx_bd_base;
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		/* Initialize the BD for every fragment in the page.
+		 */
+		bdp->cbd_sc = 0;
+		bdp->cbd_bufaddr = 0;
+		bdp++;
+	}
+
+	/* Set the last buffer to wrap.
+	 */
+	bdp--;
+	bdp->cbd_sc |= BD_SC_WRAP;
+
+	bdp = si->rx_bd_base;
+	k = 0;
+	for (i = 0; i < CPM_IRDA_RX_PAGES; i++) {
+
+		/* Allocate a page.
+		 */
+		ba = (unsigned char *)dma_alloc_coherent(NULL, PAGE_SIZE,
+							 &mem_addr, GFP_KERNEL);
+
+		/* Initialize the BD for every fragment in the page.
+		 */
+		for (j = 0; j < CPM_IRDA_RX_FRPPG; j++) {
+			bdp->cbd_sc = BD_AHDLC_RX_EMPTY | BD_AHDLC_RX_INTR;
+			bdp->cbd_bufaddr = mem_addr;
+			si->rx_vaddr[k++] = ba;
+			mem_addr += CPM_IRDA_RX_FRSIZE;
+			ba += CPM_IRDA_RX_FRSIZE;
+			bdp++;
+		}
+	}
+
+	/* Set the last buffer to wrap.
+	 */
+	bdp--;
+	bdp->cbd_sc |= BD_SC_WRAP;
+
+	out_be16(&sccp->scc_scce, 0xffff);	/* Clear any pending events */
+	out_be16(&sccp->scc_sccm, SCC_AHDLC_TXE | SCC_AHDLC_RXF | SCC_AHDLC_TXB);
+
+	err = request_irq(si->irq, mpc8xx_irda_irq, 0, dev->name, dev);
+	if (err) {
+		kfree(si);
+		return err;
+	}
+
+	/* Set GSMR_H to enable all normal operating modes.
+	 * Set GSMR_L to enable Ethernet to MC68160.
+	 */
+	out_be32(&sccp->scc_gsmrh, SCC_GSMRH_RFW | SCC_GSMRH_IRP);
+	out_be32(&sccp->scc_gsmrl, SCC_GSMRL_SIR | SCC_GSMRL_MODE_AHDLC | SCC_GSMRL_TDCR_16 |
+				   SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16);
+
+	out_be16(&sccp->scc_dsr, 0x7e7e);
+
+	/* Set processing mode.
+	 */
+	out_be16(&sccp->scc_psmr, SCC_PMSR_CHLN);
+
+	/* Enable the transmit and receive processing.
+	 */
+	setbits32(&sccp->scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT);
+
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+static int mpc8xx_irda_start(struct net_device *dev)
+{
+	struct mpc8xx_irda *si = dev->priv;
+	int err = 0;
+
+	if ((err = mpc8xx_irda_startup(dev)))
+		return err;
+
+	mpc8xx_irda_set_speed(dev, si->speed = 9600);
+
+	/* Open new IrLAP layer instance, now that everything should be
+	 * initialized properly
+	 */
+	si->irlap = irlap_open(dev, &si->qos, "MPC8xx SIR");
+	err = -ENOMEM;
+	if (!si->irlap) {
+		si->open = 0;
+		return err;
+	}
+
+	/* Now start the queue
+	 */
+	si->open = 1;
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+static int mpc8xx_irda_stop(struct net_device *dev)
+{
+	struct mpc8xx_irda *si = dev->priv;
+
+	disable_irq(dev->irq);
+
+	/* Stop IrLAP */
+	if (si->irlap) {
+		irlap_close(si->irlap);
+		si->irlap = NULL;
+	}
+
+	netif_stop_queue(dev);
+	si->open = 0;
+
+	/* Free resources
+	 */
+	free_irq(dev->irq, dev);
+
+	return 0;
+}
+
+static void mpc8xx_irda_set_speed(struct net_device *dev, int speed)
+{
+	cpm_setbrg(2, speed);
+}
+
+static int mpc8xx_irda_probe(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct net_device *ndev;
+	struct mpc8xx_irda *si;
+	int err;
+
+	if (!(ndev = alloc_irdadev(sizeof(struct mpc8xx_irda))))
+		return -ENOMEM;
+
+	si = ndev->priv;
+	si->dev = dev;
+	si->pdata = pdev->dev.platform_data;
+
+	ndev->hard_start_xmit = mpc8xx_irda_hard_xmit;
+	ndev->open = mpc8xx_irda_start;
+	ndev->stop = mpc8xx_irda_stop;
+	ndev->do_ioctl = mpc8xx_irda_ioctl;
+	ndev->get_stats = mpc8xx_irda_stats;
+
+	irda_init_max_qos_capabilies(&si->qos);
+
+	/* We support original IRDA up to 115k2.
+	 * Min Turn Time set to 1ms or greater.
+	 */
+
+	si->qos.baud_rate.bits =
+	    IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200;
+	si->qos.min_turn_time.bits = 7;
+	irda_qos_bits_to_value(&si->qos);
+
+	err = register_netdev(ndev);
+
+	if (!err) {
+		dev_set_drvdata(&pdev->dev, ndev);
+		printk(KERN_INFO "IrDA: Registered device %s\n", ndev->name);
+	} else
+		free_netdev(ndev);
+
+	return err;
+}
+
+static int mpc8xx_irda_remove(struct device *_dev)
+{
+	struct net_device *dev = dev_get_drvdata(_dev);
+
+	if (dev) {
+		rtnl_lock();
+		unregister_netdevice(dev);
+		free_netdev(dev);
+		rtnl_unlock();
+	}
+
+	/*
+	 * We now know that the netdevice is no longer in use, and all
+	 * references to our driver have been removed.  The only structure
+	 * which may still be present is the netdevice, which will get
+	 * cleaned up by net/core/dev.c
+	 */
+
+	return 0;
+}
+
+static struct device_driver m8xxir_driver = {
+	.name = "fsl-cpm-scc:irda",
+	.bus = &platform_bus_type,
+	.probe = mpc8xx_irda_probe,
+	.remove = mpc8xx_irda_remove,
+};
+
+static int mpc8xx_irda_init(void)
+{
+	return driver_register(&m8xxir_driver);
+}
+
+static void __exit mpc8xx_irda_exit(void)
+{
+	driver_unregister(&m8xxir_driver);
+}
+
+module_init(mpc8xx_irda_init);
+module_exit(mpc8xx_irda_exit);
+
+MODULE_AUTHOR("source@mvista.com");
+MODULE_DESCRIPTION("MPC8XX SIR");
+MODULE_LICENSE("GPL");
diff --git a/include/asm-ppc/commproc.h b/include/asm-ppc/commproc.h
index 4f99df1..9e251c3 100644
--- a/include/asm-ppc/commproc.h
+++ b/include/asm-ppc/commproc.h
@@ -616,6 +616,73 @@ typedef struct spi {
 #define SPIE_TXB	0x02
 #define SPIE_RXB	0x01
 
+/* Asynchronous HDLC Mode Parameter RAM.
+*/
+typedef struct scc_ahdlc {
+	sccp_t  scc_genscc;
+	uint    ahdlc_res1;	/* Reserved */
+	uint    ahdlc_cmask;	/* Constant mask for CRC */
+	uint    ahdlc_cpres;	/* Preset CRC */
+	ushort  ahdlc_bof;	/* Beginning of flag character */
+	ushort  ahdlc_eof;	/* End of flag character */
+	ushort  ahdlc_esc;	/* Control escape character. */
+	ushort  ahdlc_res2;	/* Reserved */
+	ushort  ahdlc_res3;	/* Reserved */
+	ushort  ahdlc_zero;	/* Clear this field */
+	ushort  ahdlc_res4;	/* Reserved */
+	ushort  ahdlc_rfthr;	/* Received frames threshold */
+	uint    ahdlc_res5;	/* Reserved */
+	uint    ahdlc_txctl_tbl;/* Control Tx character tables */
+	uint    ahdlc_rxctl_tbl;/* Control Rx character tables */
+	ushort  ahdlc_nof;	/* Number of opening flags to be sent at the beginning of a frame */
+	ushort  ahdlc_res6;	/* Reserved */
+} scc_ahdlc_t;
+
+/* SCC Mode Register (PMSR) as used by Ethernet.
+*/
+#define SCC_PMSR_CHLN		((ushort)0x3000)        /* 8 bits character length */
+#define SICR_IRDA_MASK		((uint)0x0000ff00)
+#define SICR_IRDA_CLKRT		((uint)0x00001200)
+
+/* SCC Event register as used by Asynchronous HDLC.
+*/
+#define SCC_AHDLC_GLR		((ushort)0x1000)
+#define SCC_AHDLC_GLT		((ushort)0x0800)
+#define SCC_AHDLC_IDL		((ushort)0x0100)
+#define SCC_AHDLC_BRKE		((ushort)0x0040)
+#define SCC_AHDLC_BRKS		((ushort)0x0020)
+#define SCC_AHDLC_TXE		((ushort)0x0010)
+#define SCC_AHDLC_RXF		((ushort)0x0008)
+#define SCC_AHDLC_BSY		((ushort)0x0004)
+#define SCC_AHDLC_TXB		((ushort)0x0002)
+#define SCC_AHDLC_RXB		((ushort)0x0001)
+
+/* Buffer descriptor control/status used by AHDLC receive.
+*/
+#define BD_AHDLC_RX_EMPTY	((ushort)0x8000)
+#define BD_AHDLC_RX_WRAP	((ushort)0x2000)
+#define BD_AHDLC_RX_INTR	((ushort)0x1000)
+#define BD_AHDLC_RX_LAST	((ushort)0x0800)
+#define BD_AHDLC_RX_FIRST	((ushort)0x0400)
+#define BD_AHDLC_RX_CM		((ushort)0x0200)
+#define BD_AHDLC_RX_BRK		((ushort)0x0080)
+#define BD_AHDLC_RX_BOF		((ushort)0x0040)
+#define BD_AHDLC_RX_AB		((ushort)0x0008)
+#define BD_AHDLC_RX_CR		((ushort)0x0004)
+#define BD_AHDLC_RX_OV		((ushort)0x0002)
+#define BD_AHDLC_RX_CD		((ushort)0x0001)
+#define BD_AHDLC_RX_STATS	((ushort)0x008f)        /* All status bits */
+
+/* Buffer descriptor control/status used by AHDLC transmit.
+*/
+#define BD_AHDLC_TX_READY	((ushort)0x8000)
+#define BD_AHDLC_TX_WRAP	((ushort)0x2000)
+#define BD_AHDLC_TX_INTR	((ushort)0x1000)
+#define BD_AHDLC_TX_LAST	((ushort)0x0800)
+#define BD_AHDLC_TX_CM		((ushort)0x0200)
+#define BD_AHDLC_TX_CTS		((ushort)0x0001)
+#define BD_AHDLC_TX_STATS	((ushort)0x03ff)        /* All status bits */
+
 /*
  * RISC Controller Configuration Register definitons
  */

RE: [PATCH] [POWERPC] 8xx: PQ SoC IRDA support

From: Loeliger Jon-LOELIGER <hidden>
Date: 2007-05-08 14:36:57

Adds support of IRDA transceiver residing on PowerQUICC processors and
enabling such on mpc885ads reference board. The driver is implemented=20
using of_device concept, hereby implies arch/powerpc support=20
of the target.
=20
Signed-off-by: Vitaly Bordug <redacted>
quoted hunk
diff --git a/arch/powerpc/boot/dts/mpc885ads.dts=20
b/arch/powerpc/boot/dts/mpc885ads.dts
index 110bf61..95b280c 100644
--- a/arch/powerpc/boot/dts/mpc885ads.dts
+++ b/arch/powerpc/boot/dts/mpc885ads.dts
@@ -178,6 +178,16 @@
 				interrupt-parent =3D <930>;
 				phy-handle =3D <e8002>;
 			};
+
+			scc@a20 {
+				device_type =3D "network";
+				compatible =3D "fsl,irda";
+				model =3D "SCC";
+				device-id =3D <2>;
+				reg =3D <a20 18 3d00 80>;
+				interrupts =3D <1d 3>;
+				interrupt-parent =3D <930>;
+			};
 		};
 	};
 };

Any chance we could get an update to the booting-without-of.txt
for any of this?

Thanks,
jdl

Re: [PATCH] [POWERPC] 8xx: PQ SoC IRDA support

From: Vitaly Bordug <hidden>
Date: 2007-05-08 16:21:33

On Tue, 8 May 2007 07:36:48 -0700
"Loeliger Jon-LOELIGER" [off-list ref] wrote:
quoted
Adds support of IRDA transceiver residing on PowerQUICC processors and
enabling such on mpc885ads reference board. The driver is implemented 
using of_device concept, hereby implies arch/powerpc support 
of the target.

Signed-off-by: Vitaly Bordug <redacted>
quoted
diff --git a/arch/powerpc/boot/dts/mpc885ads.dts 
b/arch/powerpc/boot/dts/mpc885ads.dts
index 110bf61..95b280c 100644
--- a/arch/powerpc/boot/dts/mpc885ads.dts
+++ b/arch/powerpc/boot/dts/mpc885ads.dts
@@ -178,6 +178,16 @@
 				interrupt-parent = <930>;
 				phy-handle = <e8002>;
 			};
+
+			scc@a20 {
+				device_type = "network";
+				compatible = "fsl,irda";
+				model = "SCC";
+				device-id = <2>;
+				reg = <a20 18 3d00 80>;
+				interrupts = <1d 3>;
+				interrupt-parent = <930>;
+			};
 		};
 	};
 };

Any chance we could get an update to the booting-without-of.txt
for any of this?
Yes of course. my note is that it makes sense once defines would settle down
stuff that looks OK in docs might not look such in driver...

-- 
Sincerely, 
Vitaly
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help