* [PATCH] [POWERPC] 8xx: PQ SoC IRDA support
@ 2007-05-08 22:42 Vitaly Bordug
2007-05-09 8:42 ` Samuel Ortiz
2007-05-09 23:53 ` Andrew Morton
0 siblings, 2 replies; 12+ messages in thread
From: Vitaly Bordug @ 2007-05-08 22:42 UTC (permalink / raw)
To: Samuel Ortiz; +Cc: Andrew Morton, linuxppc-dev, linux-kernel
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 <vitb@kernel.crashing.org>
---
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 808d2ef..9e48c80 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -629,6 +629,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..0d4286a 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,pq-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
*/
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH] [POWERPC] 8xx: PQ SoC IRDA support
2007-05-08 22:42 [PATCH] [POWERPC] 8xx: PQ SoC IRDA support Vitaly Bordug
@ 2007-05-09 8:42 ` Samuel Ortiz
2007-05-09 10:53 ` Vitaly Bordug
2007-05-09 23:53 ` Andrew Morton
1 sibling, 1 reply; 12+ messages in thread
From: Samuel Ortiz @ 2007-05-09 8:42 UTC (permalink / raw)
To: vitb; +Cc: linuxppc-dev@ozlabs.org
Hi Vitaly,
On 5/8/2007, "Vitaly Bordug" <vitb@kernel.crashing.org> wrote:
>
>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 <vitb@kernel.crashing.org>
I'd really prefer this patch to be splitted in 2 parts: The PPC specific
bits, and the IrDA driver that would apply on top of it.
Meanwhile, I can comment on the IrDA driver code:
>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
>
> =09 If unsure, say Y.
>
>+config 8XX_SIR
>+=09 tristate "mpc8xx SIR"
>+=09 depends on 8xx && IRDA
>+
Some "help" field here wouldn't hurt.
>+static void mpc8xx_irda_rx(struct net_device *dev)
>+{
>+=09struct mpc8xx_irda *si =3D dev->priv;
>+=09cbd_t *bdp;
>+=09struct sk_buff *skb;
>+=09int len;
>+=09ushort status;
>+
>+=09bdp =3D si->cur_rx;
>+
>+=09for (;;) {
>+=09=09if (bdp->cbd_sc & BD_AHDLC_RX_EMPTY)
>+=09=09=09break;
>+=09=09status =3D bdp->cbd_sc;
>+
>+=09=09if (status & BD_AHDLC_RX_STATS) {
>+=09=09=09/* process errors
>+=09=09=09 */
>+=09=09=09if (bdp->cbd_sc & BD_AHDLC_RX_AB)
>+=09=09=09=09si->stats.rx_length_errors++;
>+=09=09=09if (bdp->cbd_sc & BD_AHDLC_RX_CR)=09/* CRC Error */
>+=09=09=09=09si->stats.rx_crc_errors++;
>+=09=09=09if (bdp->cbd_sc & BD_AHDLC_RX_OV)=09/* FIFO overrun */
>+=09=09=09=09si->stats.rx_over_errors++;
>+=09=09} else {
>+=09=09=09/* Process the incoming frame.
>+=09=09=09 */
>+=09=09=09len =3D bdp->cbd_datlen;
>+
>+=09=09=09skb =3D dev_alloc_skb(len + 1);
>+=09=09=09if (skb =3D=3D NULL) {
>+=09=09=09=09printk(KERN_INFO
>+=09=09=09=09 "%s: Memory squeeze, dropping packet.\n",
>+=09=09=09=09 dev->name);
>+=09=09=09=09si->stats.rx_dropped++;
>+=09=09=09} else {
>+=09=09=09=09skb->dev =3D dev;
>+=09=09=09=09skb_reserve(skb, 1);
>+=09=09=09=09memcpy(skb_put(skb, len),
>+=09=09=09=09 si->rx_vaddr[bdp - si->rx_bd_base], len);
>+=09=09=09=09skb_trim(skb, skb->len - 2);
>+
>+=09=09=09=09si->stats.rx_packets++;
>+=09=09=09=09si->stats.rx_bytes +=3D len;
>+
>+=09=09=09=09skb->mac.raw =3D skb->data;
This should be
+=09=09=09=09skb_reset_mac_header(skb);
>+static irqreturn_t mpc8xx_irda_irq(int irq, void *dev_id)
>+{
>+=09struct net_device *dev =3D dev_id;
>+=09struct mpc8xx_irda *si =3D dev->priv;
>+=09scc_t *sccp =3D si->sccp;
>+=09cbd_t *bdp;
>+=09ushort int_events;
>+=09int must_restart =3D 0;
>+
>+=09/* Get the interrupt events that caused us to be here.
>+=09 */
>+=09int_events =3D in_be16(&sccp->scc_scce);
>+=09out_be16(&sccp->scc_scce, int_events);
>+
>+=09/* Handle receive event in its own function.
>+=09 */
>+=09if (int_events & SCC_AHDLC_RXF)
>+=09=09mpc8xx_irda_rx(dev);
>+
>+=09spin_lock(&si->lock);
>+
>+=09/* Transmit OK, or non-fatal error. Update the buffer descriptors.
>+=09 */
>+=09if (int_events & (SCC_AHDLC_TXE | SCC_AHDLC_TXB)) {
>+=09=09bdp =3D si->dirty_tx;
>+
>+=09=09while ((bdp->cbd_sc & BD_AHDLC_TX_READY) =3D=3D 0) {
>+=09=09=09if (si->tx_free =3D=3D TX_RING_SIZE)
>+=09=09=09=09break;
>+
>+=09=09=09if (bdp->cbd_sc & BD_AHDLC_TX_CTS)
>+=09=09=09=09must_restart =3D 1;
>+
>+=09=09=09si->stats.tx_packets++;
>+
>+=09=09=09/* Free the sk buffer associated with this last transmit.
>+=09=09=09 */
>+=09=09=09dev_kfree_skb_irq(si->tx_skbuff[si->skb_dirty]);
>+=09=09=09si->skb_dirty =3D (si->skb_dirty + 1) & TX_RING_MOD_MASK;
>+
>+=09=09=09/* Update pointer to next buffer descriptor to be transmitted.
>+=09=09=09 */
>+=09=09=09if (bdp->cbd_sc & BD_AHDLC_TX_WRAP)
>+=09=09=09=09bdp =3D si->tx_bd_base;
>+=09=09=09else
>+=09=09=09=09bdp++;
>+
>+=09=09=09/* Since we have freed up a buffer, the ring is no longer full.
>+=09=09=09 */
>+=09=09=09if (!si->tx_free++) {
>+=09=09=09=09if (netif_queue_stopped(dev))
>+=09=09=09=09=09netif_wake_queue(dev);
>+=09=09=09}
>+
>+=09=09=09si->dirty_tx =3D (cbd_t *) bdp;
>+
>+=09=09=09if (si->newspeed) {
>+=09=09=09=09mpc8xx_irda_set_speed(dev, si->newspeed);
>+=09=09=09=09si->speed =3D si->newspeed;
>+=09=09=09=09si->newspeed =3D 0;
>+=09=09=09}
>+=09=09}
>+
>+=09=09if (must_restart) {
>+=09=09=09cpm8xx_t *cp =3D immr_map(im_cpm);
>+=09=09=09printk(KERN_INFO "restart TX\n");
>+
>+=09=09=09/* Some transmit errors cause the transmitter to shut
>+=09=09=09 * down. We now issue a restart transmit. Since the
>+=09=09=09 * errors close the BD and update the pointers, the restart
>+=09=09=09 * _should_ pick up without having to reset any of our
>+=09=09=09 * pointers either.
>+=09=09=09 */
>+=09=09=09out_be16(&cp->cp_cpcr,
>+=09=09=09 mk_cr_cmd(CPM_CR_CH_SCC2,
>+=09=09=09=09 CPM_CR_RESTART_TX) | CPM_CR_FLG);
>+=09=09=09while (in_be16(&cp->cp_cpcr) & CPM_CR_FLG) ;
You're busy looping in an interrupt handler, that's not really nice.
In general, I think this interrupt handler would deserve a bottom half
split since it looks quite busy.
>+static struct device_driver m8xxir_driver =3D {
>+=09.name =3D "fsl-cpm-scc:irda",
>+=09.bus =3D &platform_bus_type,
>+=09.probe =3D mpc8xx_irda_probe,
>+=09.remove =3D mpc8xx_irda_remove,
>+};
Why not a platform driver ?
Cheers,
Samuel.
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH] [POWERPC] 8xx: PQ SoC IRDA support
2007-05-09 8:42 ` Samuel Ortiz
@ 2007-05-09 10:53 ` Vitaly Bordug
2007-05-09 13:46 ` Samuel Ortiz
0 siblings, 1 reply; 12+ messages in thread
From: Vitaly Bordug @ 2007-05-09 10:53 UTC (permalink / raw)
To: Samuel Ortiz; +Cc: linuxppc-dev@ozlabs.org
On 9 May 2007 08:42:44 -0000
Samuel Ortiz wrote:
>
> Hi Vitaly,
>
> On 5/8/2007, "Vitaly Bordug" <vitb@kernel.crashing.org> wrote:
>
> >
> >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 <vitb@kernel.crashing.org>
> I'd really prefer this patch to be splitted in 2 parts: The PPC
> specific bits, and the IrDA driver that would apply on top of it.
> Meanwhile, I can comment on the IrDA driver code:
>
I used to do such before with network drivers but it may result in badly breakage since stuff is heading through different routes. The most productive way seems to acquire ACK from both driver-wise and arch-wise areas,
and have Andrew to merge it. IOW, separating stuff that cannot live one without other is error-prone.
Of course, there's np do do that for your convenience - I'm just against pushing it through different trees.
>
> >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
> >+
> Some "help" field here wouldn't hurt.
>
OK
>
>
> >+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;
> This should be
> + skb_reset_mac_header(skb);
>
OK
>
> >+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) ;
> You're busy looping in an interrupt handler, that's not really nice.
That's correct. In fact, I included protection stuff for SCC Ethernet in fs_enet driver to
get rid of indefinite loop in case something really bad happens in CPM microcode.
I'll move it here.
> In general, I think this interrupt handler would deserve a bottom half
> split since it looks quite busy.
>
I don't think it worths bh stuff - scc/FCC/etc ethernet (drivers/net/fs_enet/) use more busy int handler and is for
the similar (SCC) SoC device on the same target board (with higher throughout speed) without performance affected.
It is doable but I guess we'll lose more than win. Also we have to keep in mind, that 8xx PQ SoC is not speed-blasting and having IRQ path between schedule() ticks would not be great either.
So to recap, similar handling path for similar SoC device existing in current kernel for a while now, and working good.
You are right that we have to provide some protection in case of CPM lockup though.
>
> >+static struct device_driver m8xxir_driver = {
> >+ .name = "fsl-cpm-scc:irda",
> >+ .bus = &platform_bus_type,
> >+ .probe = mpc8xx_irda_probe,
> >+ .remove = mpc8xx_irda_remove,
> >+};
> Why not a platform driver ?
>
Now I have full of_platform_driver implementation so I better drop platform_devices here.
Thanks for review, will follow-up with fixes shortly.
--
Sincerely, Vitaly
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH] [POWERPC] 8xx: PQ SoC IRDA support
2007-05-09 10:53 ` Vitaly Bordug
@ 2007-05-09 13:46 ` Samuel Ortiz
2007-05-09 22:36 ` Vitaly Bordug
0 siblings, 1 reply; 12+ messages in thread
From: Samuel Ortiz @ 2007-05-09 13:46 UTC (permalink / raw)
To: vitb; +Cc: linuxppc-dev@ozlabs.org
Hi Vitaly,
On 5/9/2007, "Vitaly Bordug" <vitb@kernel.crashing.org> wrote:
>> I'd really prefer this patch to be splitted in 2 parts: The PPC
>> specific bits, and the IrDA driver that would apply on top of it.
>> Meanwhile, I can comment on the IrDA driver code:
>>
>I used to do such before with network drivers but it may result in badly bre=
akage since stuff is heading through different routes. The most productive wa=
y seems to acquire ACK from both driver-wise and arch-wise areas,
>and have Andrew to merge it. IOW, separating stuff that cannot live one with=
out other is error-prone.
>
>Of course, there's np do do that for your convenience - I'm just against pus=
hing it through different trees.
>>
Fine, but please CC netdev and irda-users@lists.sourceforge.net for your
next patch iteration.
>> >+static irqreturn_t mpc8xx_irda_irq(int irq, void *dev_id)
>> >+{
>> >+=09struct net_device *dev =3D dev_id;
>> >+=09struct mpc8xx_irda *si =3D dev->priv;
>> >+=09scc_t *sccp =3D si->sccp;
>> >+=09cbd_t *bdp;
>> >+=09ushort int_events;
>> >+=09int must_restart =3D 0;
>> >+
>> >+=09/* Get the interrupt events that caused us to be here.
>> >+=09 */
>> >+=09int_events =3D in_be16(&sccp->scc_scce);
>> >+=09out_be16(&sccp->scc_scce, int_events);
>> >+
>> >+=09/* Handle receive event in its own function.
>> >+=09 */
>> >+=09if (int_events & SCC_AHDLC_RXF)
>> >+=09=09mpc8xx_irda_rx(dev);
>> >+
>> >+=09spin_lock(&si->lock);
>> >+
>> >+=09/* Transmit OK, or non-fatal error. Update the buffer
>> >descriptors.
>> >+=09 */
>> >+=09if (int_events & (SCC_AHDLC_TXE | SCC_AHDLC_TXB)) {
>> >+=09=09bdp =3D si->dirty_tx;
>> >+
>> >+=09=09while ((bdp->cbd_sc & BD_AHDLC_TX_READY) =3D=3D 0) {
>> >+=09=09=09if (si->tx_free =3D=3D TX_RING_SIZE)
>> >+=09=09=09=09break;
>> >+
>> >+=09=09=09if (bdp->cbd_sc & BD_AHDLC_TX_CTS)
>> >+=09=09=09=09must_restart =3D 1;
>> >+
>> >+=09=09=09si->stats.tx_packets++;
>> >+
>> >+=09=09=09/* Free the sk buffer associated with this
>> >last transmit.
>> >+=09=09=09 */
>> >+
>> >dev_kfree_skb_irq(si->tx_skbuff[si->skb_dirty]);
>> >+=09=09=09si->skb_dirty =3D (si->skb_dirty + 1) &
>> >TX_RING_MOD_MASK; +
>> >+=09=09=09/* Update pointer to next buffer descriptor
>> >to be transmitted.
>> >+=09=09=09 */
>> >+=09=09=09if (bdp->cbd_sc & BD_AHDLC_TX_WRAP)
>> >+=09=09=09=09bdp =3D si->tx_bd_base;
>> >+=09=09=09else
>> >+=09=09=09=09bdp++;
>> >+
>> >+=09=09=09/* Since we have freed up a buffer, the
>> >ring is no longer full.
>> >+=09=09=09 */
>> >+=09=09=09if (!si->tx_free++) {
>> >+=09=09=09=09if (netif_queue_stopped(dev))
>> >+=09=09=09=09=09netif_wake_queue(dev);
>> >+=09=09=09}
>> >+
>> >+=09=09=09si->dirty_tx =3D (cbd_t *) bdp;
>> >+
>> >+=09=09=09if (si->newspeed) {
>> >+=09=09=09=09mpc8xx_irda_set_speed(dev,
>> >si->newspeed);
>> >+=09=09=09=09si->speed =3D si->newspeed;
>> >+=09=09=09=09si->newspeed =3D 0;
>> >+=09=09=09}
>> >+=09=09}
>> >+
>> >+=09=09if (must_restart) {
>> >+=09=09=09cpm8xx_t *cp =3D immr_map(im_cpm);
>> >+=09=09=09printk(KERN_INFO "restart TX\n");
>> >+
>> >+=09=09=09/* Some transmit errors cause the
>> >transmitter to shut
>> >+=09=09=09 * down. We now issue a restart transmit.
>> >Since the
>> >+=09=09=09 * errors close the BD and update the
>> >pointers, the restart
>> >+=09=09=09 * _should_ pick up without having to reset
>> >any of our
>> >+=09=09=09 * pointers either.
>> >+=09=09=09 */
>> >+=09=09=09out_be16(&cp->cp_cpcr,
>> >+=09=09=09 mk_cr_cmd(CPM_CR_CH_SCC2,
>> >+=09=09=09=09 CPM_CR_RESTART_TX) |
>> >CPM_CR_FLG);
>> >+=09=09=09while (in_be16(&cp->cp_cpcr) & CPM_CR_FLG) ;
>> You're busy looping in an interrupt handler, that's not really nice.
>That's correct. In fact, I included protection stuff for SCC Ethernet in fs_=
enet driver to
>get rid of indefinite loop in case something really bad happens in CPM micro=
code.
>
>I'll move it here.
Good.
>> In general, I think this interrupt handler would deserve a bottom half
>> split since it looks quite busy.
>>
>I don't think it worths bh stuff - scc/FCC/etc ethernet (drivers/net/fs_enet=
/) use more busy int handler and is for
>the similar (SCC) SoC device on the same target board (with higher throughou=
t speed) without performance affected.
>
>It is doable but I guess we'll lose more than win. Also we have to keep in m=
ind, that 8xx PQ SoC is not speed-blasting and having IRQ path between schedu=
le() ticks would not be great either.
>
>So to recap, similar handling path for similar SoC device existing in curren=
t kernel for a while now, and working good.
I'm sure it works good, but doing the whole RX path from an interrupt
handler doesn't sound like the best thing to do, at least to me. And
with a SIR IrDA device, you can afford to offload your interrupt
processing to a BH, nobody will notice the difference in terms of
performance.
I'm not against your proposed implementation, but I just think there's
a nicer way to achieve the same result.
>Thanks for review, will follow-up with fixes shortly.
Thanks for the patch.
Cheers,
Samuel.
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH] [POWERPC] 8xx: PQ SoC IRDA support
2007-05-09 13:46 ` Samuel Ortiz
@ 2007-05-09 22:36 ` Vitaly Bordug
0 siblings, 0 replies; 12+ messages in thread
From: Vitaly Bordug @ 2007-05-09 22:36 UTC (permalink / raw)
To: Samuel Ortiz; +Cc: linuxppc-dev@ozlabs.org
On 9 May 2007 13:46:11 -0000
Samuel Ortiz wrote:
> >So to recap, similar handling path for similar SoC device existing
> >in current kernel for a while now, and working good.
>
> I'm sure it works good, but doing the whole RX path from an interrupt
> handler doesn't sound like the best thing to do, at least to me. And
> with a SIR IrDA device, you can afford to offload your interrupt
> processing to a BH, nobody will notice the difference in terms of
> performance.
> I'm not against your proposed implementation, but I just think there's
> a nicer way to achieve the same result.
May I ask to point to the code you're keeping in mind here?
I'm not going to touch this path or else the code will miss this merge window as well,
but I'm going to keep improving it, and this rework might be useful not only for this particular driver,
but for similar kernel pieces too.
--
Sincerely, Vitaly
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] [POWERPC] 8xx: PQ SoC IRDA support
2007-05-08 22:42 [PATCH] [POWERPC] 8xx: PQ SoC IRDA support Vitaly Bordug
2007-05-09 8:42 ` Samuel Ortiz
@ 2007-05-09 23:53 ` Andrew Morton
1 sibling, 0 replies; 12+ messages in thread
From: Andrew Morton @ 2007-05-09 23:53 UTC (permalink / raw)
To: Vitaly Bordug; +Cc: linuxppc-dev, Samuel Ortiz, linux-kernel
On Wed, 09 May 2007 02:42:07 +0400
Vitaly Bordug <vitb@kernel.crashing.org> wrote:
> + model = (char *)get_property(np, "model", NULL);
> + if (model == NULL)
> + return -ENODEV;
> +
> + id = (u32 *) get_property(np, "device-id", NULL);
get_property() got renamed to of_get_property().
You have two coding-styles in the typecasting here. The former (no space)
is more common and makes more sense, IMO.
However of_get_property() returns const void* so really you shouldn't be
doing any casting at all. `model' should have type `const char *' and then
you can do
model = get_property(np, "model", NULL);
which has nice type-safety and const-correctness.
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH] [POWERPC] 8xx: PQ SoC IRDA support
@ 2007-05-08 5:27 Vitaly Bordug
2007-05-08 14:36 ` Loeliger Jon-LOELIGER
0 siblings, 1 reply; 12+ messages in thread
From: Vitaly Bordug @ 2007-05-08 5:27 UTC (permalink / raw)
To: Samuel Ortiz; +Cc: Andrew Morton, linuxppc-dev, linux-kernel
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 <vitb@kernel.crashing.org>
---
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
*/
^ permalink raw reply related [flat|nested] 12+ messages in thread* RE: [PATCH] [POWERPC] 8xx: PQ SoC IRDA support
2007-05-08 5:27 Vitaly Bordug
@ 2007-05-08 14:36 ` Loeliger Jon-LOELIGER
2007-05-08 16:18 ` Vitaly Bordug
0 siblings, 1 reply; 12+ messages in thread
From: Loeliger Jon-LOELIGER @ 2007-05-08 14:36 UTC (permalink / raw)
To: Vitaly Bordug, Samuel Ortiz; +Cc: Andrew Morton, linuxppc-dev, linux-kernel
> 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 <vitb@kernel.crashing.org>
> 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
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH] [POWERPC] 8xx: PQ SoC IRDA support
2007-05-08 14:36 ` Loeliger Jon-LOELIGER
@ 2007-05-08 16:18 ` Vitaly Bordug
0 siblings, 0 replies; 12+ messages in thread
From: Vitaly Bordug @ 2007-05-08 16:18 UTC (permalink / raw)
To: Loeliger Jon-LOELIGER; +Cc: linuxppc-dev, Samuel Ortiz, linux-kernel
On Tue, 8 May 2007 07:36:48 -0700
"Loeliger Jon-LOELIGER" <jdl@freescale.com> wrote:
> > 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 <vitb@kernel.crashing.org>
>
> > 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
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH] [POWERPC] 8xx: PQ SoC IRDA support
@ 2007-05-08 1:30 Vitaly Bordug
2007-05-08 13:33 ` Kumar Gala
0 siblings, 1 reply; 12+ messages in thread
From: Vitaly Bordug @ 2007-05-08 1:30 UTC (permalink / raw)
To: Samuel Ortiz; +Cc: Andrew Morton, linuxppc-dev, linux-kernel
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 <vitb@kernel.crashing.org>
Signed-off-by: Vitaly Bordug <vitb@kernel.crashing.org>
---
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
*/
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH] [POWERPC] 8xx: PQ SoC IRDA support
2007-05-08 1:30 Vitaly Bordug
@ 2007-05-08 13:33 ` Kumar Gala
2007-05-08 16:19 ` Vitaly Bordug
0 siblings, 1 reply; 12+ messages in thread
From: Kumar Gala @ 2007-05-08 13:33 UTC (permalink / raw)
To: Vitaly Bordug; +Cc: Andrew Morton, linuxppc-dev, Samuel Ortiz, linux-kernel
On May 7, 2007, at 8:30 PM, Vitaly Bordug wrote:
>
> 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 <vitb@kernel.crashing.org>
>
> Signed-off-by: Vitaly Bordug <vitb@kernel.crashing.org>
> ---
>
> 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";
is this specific enough? 52xx also has irda and I'm guessing they'd
require different drivers (haven't looked).
> + model = "SCC";
> + device-id = <2>;
> + reg = <a20 18 3d00 80>;
> + interrupts = <1d 3>;
> + interrupt-parent = <930>;
> + };
> };
> };
> };
[snip]
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2007-05-09 23:53 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-05-08 22:42 [PATCH] [POWERPC] 8xx: PQ SoC IRDA support Vitaly Bordug
2007-05-09 8:42 ` Samuel Ortiz
2007-05-09 10:53 ` Vitaly Bordug
2007-05-09 13:46 ` Samuel Ortiz
2007-05-09 22:36 ` Vitaly Bordug
2007-05-09 23:53 ` Andrew Morton
-- strict thread matches above, loose matches on Subject: below --
2007-05-08 5:27 Vitaly Bordug
2007-05-08 14:36 ` Loeliger Jon-LOELIGER
2007-05-08 16:18 ` Vitaly Bordug
2007-05-08 1:30 Vitaly Bordug
2007-05-08 13:33 ` Kumar Gala
2007-05-08 16:19 ` Vitaly Bordug
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).