[PATCH 2.1/7] Add QUICC Engine (QE) infrastructure

Kumar Gala galak at kernel.crashing.org
Tue Oct 3 13:43:37 EST 2006


General comments:
* error handling doesn't seem to free resources properly
* mark ioregs as __be32/__be16

Some other comments inline.

- k

> diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/ 
> qe_lib/qe.c
> new file mode 100644
> index 0000000..8c35f09
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/qe.c
> @@ -0,0 +1,177 @@
> +/*
> + * arch/powerpc/sysdev/qe_lib/qe.c
> + *
> + * FSL QE SOC setup.
> + *
> + * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights  
> reserved.
> + *
> + * Author: Li Yang <LeoLi at freescale.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/config.h>
> +#include <linux/stddef.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/errno.h>
> +#include <linux/major.h>
> +#include <linux/delay.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/fsl_devices.h>
> +
> +#include <asm/system.h>
> +#include <asm/atomic.h>
> +#include <asm/io.h>
> +#include <asm/irq.h>
> +#include <asm/prom.h>
> +#include <sysdev/fsl_soc.h>
> +#include <mm/mmu_decl.h>
> +
> +static phys_addr_t qebase = -1;
> +
> +phys_addr_t get_qe_base(void)
> +{
> +	struct device_node *qe;
> +
> +	if (qebase != -1)
> +		return qebase;
> +
> +	qe = of_find_node_by_type(NULL, "qe");
> +	if (qe) {
> +		unsigned int size;
> +		void *prop = get_property(qe, "reg", &size);
> +		qebase = of_translate_address(qe, prop);
> +		of_node_put(qe);
> +	};
> +
> +	return qebase;
> +}
> +EXPORT_SYMBOL(get_qe_base);
> +
> +static int __init ucc_geth_of_init(void)
> +{
> +	struct device_node *np;
> +	unsigned int i, ucc_num;
> +	struct platform_device *ugeth_dev;
> +	struct resource res;
> +	int ret;
> +
> +	for (np = NULL, i = 0;
> +	     (np = of_find_compatible_node(np, "network", "ucc_geth")) !=  
> NULL;
> +	     i++) {
> +		struct resource r[2];
> +		struct device_node *phy, *mdio;
> +		struct ucc_geth_platform_data ugeth_data;
> +		unsigned int *id;
> +		char *model;
> +		void *mac_addr;
> +		phandle *ph;
> +
> +		memset(r, 0, sizeof(r));
> +		memset(&ugeth_data, 0, sizeof(ugeth_data));
> +
> +		ret = of_address_to_resource(np, 0, &r[0]);
> +		if (ret)
> +			goto err;
> +
> +		ugeth_data.phy_reg_addr = r[0].start;
> +		r[1].start = r[1].end = irq_of_parse_and_map(np, 0);
> +		r[1].flags = IORESOURCE_IRQ;
> +
> +		model = get_property(np, "model", NULL);
> +		ucc_num = *((u32 *) get_property(np, "device-id", NULL));
> +		if ((strstr(model, "UCC") == NULL) ||
> +				(ucc_num < 1) || (ucc_num > 8)) {
> +			ret = -ENODEV;
> +			goto err;
> +		}
> +
> +		ugeth_dev =
> +		    platform_device_register_simple("ucc_geth", ucc_num - 1,
> +				    &r[0], 2);
> +
> +		if (IS_ERR(ugeth_dev)) {
> +			ret = PTR_ERR(ugeth_dev);
> +			goto err;
> +		}
> +
> +		mac_addr = get_property(np, "mac-address", NULL);
> +
> +		memcpy(ugeth_data.mac_addr, mac_addr, 6);
> +
> +		ugeth_data.rx_clock = *((u32 *) get_property(np, "rx-clock",
> +					NULL));
> +		ugeth_data.tx_clock = *((u32 *) get_property(np, "tx-clock",
> +					NULL));
> +
> +		ph = (phandle *) get_property(np, "phy-handle", NULL);
> +		phy = of_find_node_by_phandle(*ph);
> +
> +		if (phy == NULL) {
> +			ret = -ENODEV;
> +			goto unreg;
> +		}
> +
> +		mdio = of_get_parent(phy);
> +
> +		id = (u32 *) get_property(phy, "reg", NULL);
> +		ret = of_address_to_resource(mdio, 0, &res);
> +		if (ret) {
> +			of_node_put(phy);
> +			of_node_put(mdio);
> +			goto unreg;
> +		}
> +
> +		ugeth_data.phy_id = *id;
> +
> +		ugeth_data.phy_interrupt = irq_of_parse_and_map(phy, 0);
> +		ugeth_data.phy_interface = *((u32 *) get_property(phy,
> +					"interface", NULL));
> +
> +		/*
> +		 * FIXME: Work around for early chip rev
> +		 * There's a bug in initial chip rev(s) in the RGMII ac
> +		 * timing. The following compensates by writing to the reserved
> +		 * QE Port Output Hold Registers (CPOH1?)
> +		 */
> +		if ((ugeth_data.phy_interface == ENET_1000_RGMII) ||
> +				(ugeth_data.phy_interface == ENET_100_RGMII) ||
> +				(ugeth_data.phy_interface == ENET_10_RGMII)) {
> +			u32 *tmp_reg = (u32 *) ioremap(get_immrbase()
> +					+ 0x14A8, 0x4);
> +			u32 tmp_val = in_be32(tmp_reg);
> +			if (ucc_num == 1)
> +				out_be32(tmp_reg, tmp_val | 0x00003000);
> +			else if (ucc_num == 2)
> +				out_be32(tmp_reg, tmp_val | 0x0c000000);
> +			iounmap(tmp_reg);
> +		}
> +
> +		if (ugeth_data.phy_interrupt != 0)
> +			ugeth_data.board_flags |= FSL_UGETH_BRD_HAS_PHY_INTR;
> +
> +		of_node_put(phy);
> +		of_node_put(mdio);
> +
> +		ret = platform_device_add_data(ugeth_dev, &ugeth_data,
> +				sizeof(struct ucc_geth_platform_data));
> +		if (ret)
> +			goto unreg;
> +	}
> +
> +	return 0;
> +
> +unreg:
> +	platform_device_unregister(ugeth_dev);
> +err:
> +	return ret;
> +}

Is all this code needed now that we use an of_device?

> +
> +arch_initcall(ucc_geth_of_init);
> diff --git a/arch/powerpc/sysdev/qe_lib/qe_common.c b/arch/powerpc/ 
> sysdev/qe_lib/qe_common.c
> new file mode 100644
> index 0000000..666aa90
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/qe_common.c
> @@ -0,0 +1,353 @@
> +/*
> + * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights  
> reserved.
> + *
> + * Authors: 	Shlomi Gridish <gridish at freescale.com>
> + * 		Li Yang <leoli at freescale.com>
> + * Based on cpm2_common.c from Dan Malek (dmalek at jlc.net)
> + *
> + * Description:
> + * General Purpose functions for the global management of the
> + * QUICC Engine (QE).
> + *
> + * 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/errno.h>
> +#include <linux/sched.h>
> +#include <linux/kernel.h>
> +#include <linux/param.h>
> +#include <linux/string.h>
> +#include <linux/mm.h>
> +#include <linux/interrupt.h>
> +#include <linux/bootmem.h>
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/ioport.h>
> +#include <asm/irq.h>
> +#include <asm/page.h>
> +#include <asm/pgtable.h>
> +#include <asm/immap_qe.h>
> +#include <asm/qe.h>
> +#include <asm/prom.h>
> +#include <asm/rheap.h>
> +
> +static void qe_snums_init(void);
> +static void qe_muram_init(void);
> +static int qe_sdma_init(void);
> +
> +static DEFINE_SPINLOCK(qe_lock);
> +
> +/* QE snum state */
> +enum qe_snum_state {
> +	QE_SNUM_STATE_USED,	/* used */
> +	QE_SNUM_STATE_FREE	/* free */
> +};

Comments seem redundant.

> +
> +/* QE snum */
> +struct qe_snum {
> +	u8 num;				/* snum */
> +	enum qe_snum_state state;	/* state */
> +};
> +
> +/* We allocate this here because it is used almost exclusively for
> + * the communication processor devices.
> + */
> +struct qe_immap *qe_immr = NULL;
> +EXPORT_SYMBOL(qe_immr);
> +
> +static struct qe_snum snums[QE_NUM_OF_SNUM];	/* Dynamically  
> allocated SNUMs */
> +
> +static phys_addr_t qebase = -1;
> +
> +phys_addr_t get_qe_base(void)
> +{
> +	struct device_node *qe;
> +
> +	if (qebase != -1)
> +		return qebase;
> +
> +	qe = of_find_node_by_type(NULL, "qe");
> +	if (qe) {
> +		unsigned int size;
> +		void *prop = get_property(qe, "reg", &size);
> +		qebase = of_translate_address(qe, prop);
> +		of_node_put(qe);
> +	};
> +
> +	return qebase;
> +}
> +
> +EXPORT_SYMBOL(get_qe_base);
> +
> +void qe_reset(void)
> +{
> +	if (qe_immr == NULL)
> +		qe_immr = (struct qe_immap *) ioremap(get_qe_base(),  
> QE_IMMAP_SIZE);

Cast is not needed

> +	qe_snums_init();
> +
> +	qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID,
> +		     (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);

is this (u8) cast really needed? (if not remove all other cases)

> +
> +	/* Reclaim the MURAM memory for our use. */
> +	qe_muram_init();
> +
> +	if (qe_sdma_init())
> +		panic("sdma init failed!");
> +}
> +
> +int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input)
> +{
> +	unsigned long flags;
> +	u32 cecr;
> +	u8 mcn_shift = 0, dev_shift = 0;
> +
> +	spin_lock_irqsave(&qe_lock, flags);
> +	if (cmd == QE_RESET) {
> +		out_be32(&qe_immr->cp.cecr, (u32) (cmd | QE_CR_FLG));
> +	} else {
> +		if (cmd == QE_ASSIGN_PAGE) {
> +			/* Here device is the SNUM, not sub-block */
> +			dev_shift = QE_CR_SNUM_SHIFT;
> +		} else if (cmd == QE_ASSIGN_RISC) {
> +			/* Here device is the SNUM, and mcnProtocol is
> +			 * e_QeCmdRiscAssignment value */
> +			dev_shift = QE_CR_SNUM_SHIFT;
> +			mcn_shift = QE_CR_MCN_RISC_ASSIGN_SHIFT;
> +		} else {
> +			if (device == QE_CR_SUBBLOCK_USB)
> +				mcn_shift = QE_CR_MCN_USB_SHIFT;
> +			else
> +				mcn_shift = QE_CR_MCN_NORMAL_SHIFT;
> +		}
> +
> +		out_be32(&qe_immr->cp.cecdr,
> +			 immrbar_virt_to_phys((void *)cmd_input));

What is this immrbar_virt_to_phys() doing?

> +		out_be32(&qe_immr->cp.cecr,
> +			 (cmd | QE_CR_FLG | ((u32) device << dev_shift) | (u32)
> +			  mcn_protocol << mcn_shift));
> +	}
> +
> +	/* wait for the QE_CR_FLG to clear */
> +	do {
> +		cecr = in_be32(&qe_immr->cp.cecr);
> +	} while (cecr & QE_CR_FLG);

you should use cpu_relax().  Something like

while(in_be32(&qe_immr->cp.cecr) & QE_CR_FLG)
	cpu_relax();

> +	spin_unlock_irqrestore(&qe_lock, flags);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(qe_issue_cmd);
> +
> +/* Set a baud rate generator. This needs lots of work. There are
> + * 16 BRGs, which can be connected to the QE channels or output
> + * as clocks. The BRGs are in two different block of internal
> + * memory mapped space.
> + * The baud rate clock is the system clock divided by something.
> + * It was set up long ago during the initial boot phase and is
> + * is given to us.
> + * Baud rate clocks are zero-based in the driver code (as that maps
> + * to port numbers). Documentation uses 1-based numbering.
> + */
> +static unsigned int brg_clk = 0;
> +
> +unsigned int get_brg_clk(void)
> +{
> +	struct device_node *qe;
> +	if (brg_clk)
> +		return brg_clk;
> +
> +	qe = of_find_node_by_type(NULL, "qe");
> +	if (qe) {
> +		unsigned int size;
> +		u32 *prop = (u32 *) get_property(qe, "brg-frequency", &size);
> +		brg_clk = *prop;
> +		of_node_put(qe);
> +	};
> +	return brg_clk;
> +}
> +
> +/* This function is used by UARTS, or anything else that uses a 16x
> + * oversampled clock.
> + */
> +void qe_setbrg(u32 brg, u32 rate)
> +{
> +	volatile u32 *bp;
> +	u32 divisor;
> +	int div16 = 0;
> +
> +	bp = (u32 *) & qe_immr->brg.brgc1;
> +	bp += brg;
> +
> +	divisor = (get_brg_clk() / rate);
> +	if (divisor > QE_BRGC_DIVISOR_MAX + 1) {
> +		div16 = 1;
> +		divisor /= 16;
> +	}
> +
> +	*bp = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) | QE_BRGC_ENABLE;
> +	if (div16)
> +		*bp |= QE_BRGC_DIV16;

any reasons not to use out_be32?  Also, why not doing this in a  
single write after the if(div16)?

> +}
> +
> +/* Initialize SNUMs (thread serial numbers) according to
> + * QE Module Control chapter, SNUM table
> + */
> +static void qe_snums_init(void)
> +{
> +	int i;
> +	static const u8 snum_init[] = {
> +		0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D,
> +		0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89,
> +		0x98, 0x99, 0xA8, 0xA9, 0xB8, 0xB9, 0xC8, 0xC9,
> +		0xD8, 0xD9, 0xE8, 0xE9,
> +	};
> +
> +	for (i = 0; i < QE_NUM_OF_SNUM; i++) {
> +		snums[i].num = snum_init[i];
> +		snums[i].state = QE_SNUM_STATE_FREE;
> +	}
> +}

Are the # of threads the same on 8360 & 832x?

> +
> +int qe_get_snum(void)
> +{
> +	unsigned long flags;
> +	int snum = -EBUSY;
> +	int i;
> +
> +	spin_lock_irqsave(&qe_lock, flags);
> +	for (i = 0; i < QE_NUM_OF_SNUM; i++) {
> +		if (snums[i].state == QE_SNUM_STATE_FREE) {
> +			snums[i].state = QE_SNUM_STATE_USED;
> +			snum = snums[i].num;
> +			break;
> +		}
> +	}
> +	spin_unlock_irqrestore(&qe_lock, flags);
> +
> +	return snum;
> +}
> +EXPORT_SYMBOL(qe_get_snum);
> +
> +void qe_put_snum(u8 snum)
> +{
> +	int i;
> +
> +	for (i = 0; i < QE_NUM_OF_SNUM; i++) {
> +		if (snums[i].num == snum) {
> +			snums[i].state = QE_SNUM_STATE_FREE;
> +			break;
> +		}
> +	}
> +}
> +EXPORT_SYMBOL(qe_put_snum);
> +
> +static int qe_sdma_init(void)
> +{
> +	struct sdma *sdma = &qe_immr->sdma;
> +	u32 sdma_buf_offset;
> +
> +	if (!sdma)
> +		return -ENODEV;
> +
> +	/* allocate 2 internal temporary buffers (512 bytes size each) for
> +	 * the SDMA */
> +	sdma_buf_offset = qe_muram_alloc(512 * 2, 64);
> +	if (IS_MURAM_ERR(sdma_buf_offset))
> +		return -ENOMEM;
> +
> +	out_be32(&sdma->sdebcr, sdma_buf_offset & QE_SDEBCR_BA_MASK);
> +	out_be32(&sdma->sdmr, (QE_SDMR_GLB_1_MSK | (0x1 >>
> +					QE_SDMR_CEN_SHIFT)));
> +
> +	return 0;
> +}
> +
> +/*
> + * muram_alloc / muram_free bits.
> + */
> +static DEFINE_SPINLOCK(qe_muram_lock);
> +
> +/* 16 blocks should be enough to satisfy all requests
> + * until the memory subsystem goes up... */
> +static rh_block_t qe_boot_muram_rh_block[16];
> +static rh_info_t qe_muram_info;
> +
> +static void qe_muram_init(void)
> +{
> +	struct device_node *np;
> +	u32 address;
> +	u64 size;
> +	unsigned int flags;
> +
> +	/* initialize the info header */
> +	rh_init(&qe_muram_info, 1,
> +		sizeof(qe_boot_muram_rh_block) /
> +		sizeof(qe_boot_muram_rh_block[0]), qe_boot_muram_rh_block);
> +
> +	/* Attach the usable muram area */
> +	/* XXX: This is a subset of the available muram. It
> +	 * varies with the processor and the microcode patches activated.
> +	 */
> +	if ((np = of_find_node_by_name(NULL, "data-only")) != NULL) {
> +		address = *of_get_address(np, 0, &size, &flags);
> +		of_node_put(np);
> +		rh_attach_region(&qe_muram_info,
> +			(void *)address, (int)size);
> +	}
> +}
> +
> +/* This function returns an index into the MURAM area.
> + */
> +u32 qe_muram_alloc(u32 size, u32 align)
> +{
> +	void *start;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&qe_muram_lock, flags);
> +	start = rh_alloc_align(&qe_muram_info, size, align, "QE");
> +	spin_unlock_irqrestore(&qe_muram_lock, flags);
> +
> +	return (u32) start;
> +}
> +EXPORT_SYMBOL(qe_muram_alloc);
> +
> +int qe_muram_free(u32 offset)
> +{
> +	int ret;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&qe_muram_lock, flags);
> +	ret = rh_free(&qe_muram_info, (void *)offset);
> +	spin_unlock_irqrestore(&qe_muram_lock, flags);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(qe_muram_free);
> +
> +/* not sure if this is ever needed */
> +u32 qe_muram_alloc_fixed(u32 offset, u32 size)
> +{
> +	void *start;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&qe_muram_lock, flags);
> +	start = rh_alloc_fixed(&qe_muram_info, (void *)offset, size,  
> "commproc");
> +	spin_unlock_irqrestore(&qe_muram_lock, flags);
> +
> +	return (u32) start;
> +}
> +EXPORT_SYMBOL(qe_muram_alloc_fixed);
> +
> +void qe_muram_dump(void)
> +{
> +	rh_dump(&qe_muram_info);
> +}
> +EXPORT_SYMBOL(qe_muram_dump);
> +
> +void *qe_muram_addr(u32 offset)
> +{
> +	return (void *)&qe_immr->muram[offset];
> +}
> +EXPORT_SYMBOL(qe_muram_addr);
> diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.c b/arch/powerpc/ 
> sysdev/qe_lib/qe_ic.c
> new file mode 100644
> index 0000000..f12af2d
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/qe_ic.c
> @@ -0,0 +1,555 @@
> +/*
> + * arch/powerpc/sysdev/qe_lib/qe_ic.c
> + *
> + * Copyright (C) 2006 Freescale Semicondutor, Inc.  All rights  
> reserved.
> + *
> + * Author: Li Yang <leoli at freescale.com>
> + * Based on code from Shlomi Gridish <gridish at freescale.com>
> + *
> + * QUICC ENGINE Interrupt Controller
> + *
> + * 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/kernel.h>
> +#include <linux/init.h>
> +#include <linux/errno.h>
> +#include <linux/reboot.h>
> +#include <linux/slab.h>
> +#include <linux/stddef.h>
> +#include <linux/sched.h>
> +#include <linux/signal.h>
> +#include <linux/sysdev.h>
> +#include <linux/device.h>
> +#include <linux/bootmem.h>
> +#include <linux/spinlock.h>
> +#include <asm/irq.h>
> +#include <asm/io.h>
> +#include <asm/prom.h>
> +#include <asm/qe_ic.h>
> +
> +#include "qe_ic.h"
> +
> +static DEFINE_SPINLOCK(qe_ic_lock);
> +
> +static struct qe_ic_info qe_ic_info[] = {
> +	[1] = {
> +	       .mask = 0x00008000,
> +	       .mask_reg = QEIC_CIMR,
> +	       .pri_code = 0,
> +	       .pri_reg = QEIC_CIPWCC,
> +	       },
> +	[2] = {
> +	       .mask = 0x00004000,
> +	       .mask_reg = QEIC_CIMR,
> +	       .pri_code = 1,
> +	       .pri_reg = QEIC_CIPWCC,
> +	       },
> +	[3] = {
> +	       .mask = 0x00002000,
> +	       .mask_reg = QEIC_CIMR,
> +	       .pri_code = 2,
> +	       .pri_reg = QEIC_CIPWCC,
> +	       },
> +	[10] = {
> +		.mask = 0x00000040,
> +		.mask_reg = QEIC_CIMR,
> +		.pri_code = 1,
> +		.pri_reg = QEIC_CIPZCC,
> +		},
> +	[11] = {
> +		.mask = 0x00000020,
> +		.mask_reg = QEIC_CIMR,
> +		.pri_code = 2,
> +		.pri_reg = QEIC_CIPZCC,
> +		},
> +	[12] = {
> +		.mask = 0x00000010,
> +		.mask_reg = QEIC_CIMR,
> +		.pri_code = 3,
> +		.pri_reg = QEIC_CIPZCC,
> +		},
> +	[13] = {
> +		.mask = 0x00000008,
> +		.mask_reg = QEIC_CIMR,
> +		.pri_code = 4,
> +		.pri_reg = QEIC_CIPZCC,
> +		},
> +	[14] = {
> +		.mask = 0x00000004,
> +		.mask_reg = QEIC_CIMR,
> +		.pri_code = 5,
> +		.pri_reg = QEIC_CIPZCC,
> +		},
> +	[15] = {
> +		.mask = 0x00000002,
> +		.mask_reg = QEIC_CIMR,
> +		.pri_code = 6,
> +		.pri_reg = QEIC_CIPZCC,
> +		},
> +	[20] = {
> +		.mask = 0x10000000,
> +		.mask_reg = QEIC_CRIMR,
> +		.pri_code = 3,
> +		.pri_reg = QEIC_CIPRTA,
> +		},
> +	[25] = {
> +		.mask = 0x00800000,
> +		.mask_reg = QEIC_CRIMR,
> +		.pri_code = 0,
> +		.pri_reg = QEIC_CIPRTB,
> +		},
> +	[26] = {
> +		.mask = 0x00400000,
> +		.mask_reg = QEIC_CRIMR,
> +		.pri_code = 1,
> +		.pri_reg = QEIC_CIPRTB,
> +		},
> +	[27] = {
> +		.mask = 0x00200000,
> +		.mask_reg = QEIC_CRIMR,
> +		.pri_code = 2,
> +		.pri_reg = QEIC_CIPRTB,
> +		},
> +	[28] = {
> +		.mask = 0x00100000,
> +		.mask_reg = QEIC_CRIMR,
> +		.pri_code = 3,
> +		.pri_reg = QEIC_CIPRTB,
> +		},
> +	[32] = {
> +		.mask = 0x80000000,
> +		.mask_reg = QEIC_CIMR,
> +		.pri_code = 0,
> +		.pri_reg = QEIC_CIPXCC,
> +		},
> +	[33] = {
> +		.mask = 0x40000000,
> +		.mask_reg = QEIC_CIMR,
> +		.pri_code = 1,
> +		.pri_reg = QEIC_CIPXCC,
> +		},
> +	[34] = {
> +		.mask = 0x20000000,
> +		.mask_reg = QEIC_CIMR,
> +		.pri_code = 2,
> +		.pri_reg = QEIC_CIPXCC,
> +		},
> +	[35] = {
> +		.mask = 0x10000000,
> +		.mask_reg = QEIC_CIMR,
> +		.pri_code = 3,
> +		.pri_reg = QEIC_CIPXCC,
> +		},
> +	[36] = {
> +		.mask = 0x08000000,
> +		.mask_reg = QEIC_CIMR,
> +		.pri_code = 4,
> +		.pri_reg = QEIC_CIPXCC,
> +		},
> +	[40] = {
> +		.mask = 0x00800000,
> +		.mask_reg = QEIC_CIMR,
> +		.pri_code = 0,
> +		.pri_reg = QEIC_CIPYCC,
> +		},
> +	[41] = {
> +		.mask = 0x00400000,
> +		.mask_reg = QEIC_CIMR,
> +		.pri_code = 1,
> +		.pri_reg = QEIC_CIPYCC,
> +		},
> +	[42] = {
> +		.mask = 0x00200000,
> +		.mask_reg = QEIC_CIMR,
> +		.pri_code = 2,
> +		.pri_reg = QEIC_CIPYCC,
> +		},
> +	[43] = {
> +		.mask = 0x00100000,
> +		.mask_reg = QEIC_CIMR,
> +		.pri_code = 3,
> +		.pri_reg = QEIC_CIPYCC,
> +		},
> +};
> +
> +static inline u32 qe_ic_read(volatile u32 __iomem * base, unsigned  
> int reg)
> +{
> +	return in_be32(base + (reg >> 2));
> +}
> +
> +static inline void qe_ic_write(volatile u32 __iomem * base,  
> unsigned int reg,
> +			       u32 value)
> +{
> +	out_be32(base + (reg >> 2), value);
> +}
> +
> +static inline struct qe_ic *qe_ic_from_irq(unsigned int virq)
> +{
> +	return irq_desc[virq].chip_data;
> +}
> +
> +#define virq_to_hw(virq)	((unsigned int)irq_map[virq].hwirq)
> +
> +static void qe_ic_unmask_irq(unsigned int virq)
> +{
> +	struct qe_ic *qe_ic = qe_ic_from_irq(virq);
> +	unsigned int src = virq_to_hw(virq);
> +	unsigned long flags;
> +	u32 temp;
> +
> +	spin_lock_irqsave(&qe_ic_lock, flags);
> +
> +	temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg);
> +	qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg,
> +		    temp | qe_ic_info[src].mask);
> +
> +	spin_unlock_irqrestore(&qe_ic_lock, flags);
> +}
> +
> +static void qe_ic_mask_irq(unsigned int virq)
> +{
> +	struct qe_ic *qe_ic = qe_ic_from_irq(virq);
> +	unsigned int src = virq_to_hw(virq);
> +	unsigned long flags;
> +	u32 temp;
> +
> +	spin_lock_irqsave(&qe_ic_lock, flags);
> +
> +	temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg);
> +	qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg,
> +		    temp & ~qe_ic_info[src].mask);
> +
> +	spin_unlock_irqrestore(&qe_ic_lock, flags);
> +}
> +
> +static void qe_ic_mask_irq_and_ack(unsigned int virq)
> +{
> +	struct qe_ic *qe_ic = qe_ic_from_irq(virq);
> +	unsigned int src = virq_to_hw(virq);
> +	unsigned long flags;
> +	u32 temp;
> +
> +	spin_lock_irqsave(&qe_ic_lock, flags);
> +
> +	temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg);
> +	qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg,
> +		    temp & ~qe_ic_info[src].mask);
> +
> +	/* There is nothing to do for ack here, ack is handled in ISR */
> +
> +	spin_unlock_irqrestore(&qe_ic_lock, flags);
> +}
> +
> +static struct irq_chip qe_ic_irq_chip = {
> +	.typename = " QEIC  ",
> +	.unmask = qe_ic_unmask_irq,
> +	.mask = qe_ic_mask_irq,
> +	.mask_ack = qe_ic_mask_irq_and_ack,
> +};
> +
> +static int qe_ic_host_match(struct irq_host *h, struct device_node  
> *node)
> +{
> +	struct qe_ic *qe_ic = h->host_data;
> +
> +	/* Exact match, unless qe_ic node is NULL */
> +	return qe_ic->of_node == NULL || qe_ic->of_node == node;
> +}
> +
> +static int qe_ic_host_map(struct irq_host *h, unsigned int virq,
> +			  irq_hw_number_t hw)
> +{
> +	struct qe_ic *qe_ic = h->host_data;
> +	struct irq_chip *chip;
> +
> +	if (qe_ic_info[hw].mask == 0) {
> +		printk(KERN_ERR "Can't map reserved IRQ \n");
> +		return -EINVAL;
> +	}
> +	/* Default chip */
> +	chip = &qe_ic->hc_irq;
> +
> +	set_irq_chip_data(virq, qe_ic);
> +	get_irq_desc(virq)->status |= IRQ_LEVEL;
> +
> +	set_irq_chip_and_handler(virq, chip, handle_level_irq);
> +
> +	return 0;
> +}
> +
> +static int qe_ic_host_xlate(struct irq_host *h, struct device_node  
> *ct,
> +			    u32 * intspec, unsigned int intsize,
> +			    irq_hw_number_t * out_hwirq,
> +			    unsigned int *out_flags)
> +{
> +	*out_hwirq = intspec[0];
> +	if (intsize > 1)
> +		*out_flags = intspec[1];
> +	else
> +		*out_flags = IRQ_TYPE_NONE;
> +	return 0;
> +}
> +
> +static struct irq_host_ops qe_ic_host_ops = {
> +	.match = qe_ic_host_match,
> +	.map = qe_ic_host_map,
> +	.xlate = qe_ic_host_xlate,
> +};
> +
> +/* Return an interrupt vector or NO_IRQ if no interrupt is  
> pending. */
> +unsigned int qe_ic_get_low_irq(struct qe_ic *qe_ic, struct pt_regs  
> *regs)
> +{
> +	int irq;
> +
> +	BUG_ON(qe_ic == NULL);
> +
> +	/* get the interrupt source vector. */
> +	irq = qe_ic_read(qe_ic->regs, QEIC_CIVEC) >> 26;
> +
> +	if (irq == 0)
> +		return NO_IRQ;
> +
> +	return irq_linear_revmap(qe_ic->irqhost, irq);
> +}
> +
> +/* Return an interrupt vector or NO_IRQ if no interrupt is  
> pending. */
> +unsigned int qe_ic_get_high_irq(struct qe_ic *qe_ic, struct  
> pt_regs *regs)
> +{
> +	int irq;
> +
> +	BUG_ON(qe_ic == NULL);
> +
> +	/* get the interrupt source vector. */
> +	irq = qe_ic_read(qe_ic->regs, QEIC_CHIVEC) >> 26;
> +
> +	if (irq == 0)
> +		return NO_IRQ;
> +
> +	return irq_linear_revmap(qe_ic->irqhost, irq);
> +}
> +
> +/* FIXME: We mask all the QE Low interrupts while handling.  We  
> should
> + * let other interrupt come in, but BAD interrupts are generated */
> +void fastcall qe_ic_cascade_low(unsigned int irq, struct irq_desc  
> *desc,
> +				struct pt_regs *regs)
> +{
> +	struct qe_ic *qe_ic = desc->handler_data;
> +	struct irq_chip *chip = irq_desc[irq].chip;
> +
> +	unsigned int cascade_irq = qe_ic_get_low_irq(qe_ic, regs);
> +
> +	chip->mask_ack(irq);
> +	if (cascade_irq != NO_IRQ)
> +		generic_handle_irq(cascade_irq, regs);
> +	chip->unmask(irq);
> +}
> +
> +/* FIXME: We mask all the QE High interrupts while handling.  We  
> should
> + * let other interrupt come in, but BAD interrupts are generated */
> +void fastcall qe_ic_cascade_high(unsigned int irq, struct irq_desc  
> *desc,
> +				 struct pt_regs *regs)
> +{
> +	struct qe_ic *qe_ic = desc->handler_data;
> +	struct irq_chip *chip = irq_desc[irq].chip;
> +
> +	unsigned int cascade_irq = qe_ic_get_high_irq(qe_ic, regs);
> +
> +	chip->mask_ack(irq);
> +	if (cascade_irq != NO_IRQ)
> +		generic_handle_irq(cascade_irq, regs);
> +	chip->unmask(irq);
> +}
> +
> +void __init qe_ic_init(struct device_node *node, unsigned int flags)
> +{
> +	struct qe_ic *qe_ic;
> +	struct resource res;
> +	u32 temp = 0, ret, high_active = 0;
> +
> +	qe_ic = alloc_bootmem(sizeof(struct qe_ic));
> +	if (qe_ic == NULL)
> +		return;
> +
> +	memset(qe_ic, 0, sizeof(struct qe_ic));
> +	qe_ic->of_node = node ? of_node_get(node) : NULL;
> +
> +	qe_ic->irqhost = irq_alloc_host(IRQ_HOST_MAP_LINEAR,
> +					NR_QE_IC_INTS, &qe_ic_host_ops, 0);
> +	if (qe_ic->irqhost == NULL) {
> +		of_node_put(node);
> +		return;
> +	}
> +
> +	ret = of_address_to_resource(node, 0, &res);
> +	if (ret)
> +		return;
> +
> +	qe_ic->regs = ioremap(res.start, res.end - res.start + 1);
> +
> +	qe_ic->irqhost->host_data = qe_ic;
> +	qe_ic->hc_irq = qe_ic_irq_chip;
> +
> +	qe_ic->virq_high = irq_of_parse_and_map(node, 0);
> +	qe_ic->virq_low = irq_of_parse_and_map(node, 1);
> +
> +	if (qe_ic->virq_low == NO_IRQ) {
> +		printk(KERN_ERR "Failed to map QE_IC low IRQ\n");
> +		return;
> +	}
> +
> +	/* default priority scheme is grouped. If spread mode is    */
> +	/* required, configure cicr accordingly.                    */
> +	if (flags & QE_IC_SPREADMODE_GRP_W)
> +		temp |= CICR_GWCC;
> +	if (flags & QE_IC_SPREADMODE_GRP_X)
> +		temp |= CICR_GXCC;
> +	if (flags & QE_IC_SPREADMODE_GRP_Y)
> +		temp |= CICR_GYCC;
> +	if (flags & QE_IC_SPREADMODE_GRP_Z)
> +		temp |= CICR_GZCC;
> +	if (flags & QE_IC_SPREADMODE_GRP_RISCA)
> +		temp |= CICR_GRTA;
> +	if (flags & QE_IC_SPREADMODE_GRP_RISCB)
> +		temp |= CICR_GRTB;
> +
> +	/* choose destination signal for highest priority interrupt */
> +	if (flags & QE_IC_HIGH_SIGNAL) {
> +		temp |= (SIGNAL_HIGH << CICR_HPIT_SHIFT);
> +		high_active = 1;
> +	}
> +
> +	qe_ic_write(qe_ic->regs, QEIC_CICR, temp);
> +
> +	set_irq_data(qe_ic->virq_low, qe_ic);
> +	set_irq_chained_handler(qe_ic->virq_low, qe_ic_cascade_low);
> +
> +	if (qe_ic->virq_high != NO_IRQ) {
> +		set_irq_data(qe_ic->virq_high, qe_ic);
> +		set_irq_chained_handler(qe_ic->virq_high, qe_ic_cascade_high);
> +	}
> +
> +	printk("QEIC (%d IRQ sources) at %p\n", NR_QE_IC_INTS, qe_ic->regs);
> +}
> +
> +void qe_ic_set_highest_priority(unsigned int virq, int high)
> +{
> +	struct qe_ic *qe_ic = qe_ic_from_irq(virq);
> +	unsigned int src = virq_to_hw(virq);
> +	u32 temp = 0;
> +
> +	temp = qe_ic_read(qe_ic->regs, QEIC_CICR);
> +
> +	temp &= ~CICR_HP_MASK;
> +	temp |= src << CICR_HP_SHIFT;
> +
> +	temp &= ~CICR_HPIT_MASK;
> +	temp |= (high ? SIGNAL_HIGH : SIGNAL_LOW) << CICR_HPIT_SHIFT;
> +
> +	qe_ic_write(qe_ic->regs, QEIC_CICR, temp);
> +}
> +
> +/* Set Priority level within its group, from 1 to 8 */
> +int qe_ic_set_priority(unsigned int virq, unsigned int priority)
> +{
> +	struct qe_ic *qe_ic = qe_ic_from_irq(virq);
> +	unsigned int src = virq_to_hw(virq);
> +	u32 temp;
> +
> +	if (priority > 8 || priority == 0)
> +		return -EINVAL;
> +	if (src > 127)
> +		return -EINVAL;
> +	if (qe_ic_info[src].pri_reg == 0)
> +		return -EINVAL;
> +
> +	temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].pri_reg);
> +
> +	if (priority < 4) {
> +		temp &= ~(0x7 << (32 - priority * 3));
> +		temp |= qe_ic_info[src].pri_code << (32 - priority * 3);
> +	} else {
> +		temp &= ~(0x7 << (24 - priority * 3));
> +		temp |= qe_ic_info[src].pri_code << (24 - priority * 3);
> +	}
> +
> +	qe_ic_write(qe_ic->regs, qe_ic_info[src].pri_reg, temp);
> +
> +	return 0;
> +}
> +
> +/* Set a QE priority to use high irq, only priority 1~2 can use  
> high irq */
> +int qe_ic_set_high_priority(unsigned int virq, unsigned int  
> priority, int high)
> +{
> +	struct qe_ic *qe_ic = qe_ic_from_irq(virq);
> +	unsigned int src = virq_to_hw(virq);
> +	u32 temp, control_reg = QEIC_CICNR, shift = 0;
> +
> +	if (priority > 2 || priority == 0)
> +		return -EINVAL;
> +
> +	switch (qe_ic_info[src].pri_reg) {
> +	case QEIC_CIPZCC:
> +		shift = CICNR_ZCC1T_SHIFT;
> +		break;
> +	case QEIC_CIPWCC:
> +		shift = CICNR_WCC1T_SHIFT;
> +		break;
> +	case QEIC_CIPYCC:
> +		shift = CICNR_YCC1T_SHIFT;
> +		break;
> +	case QEIC_CIPXCC:
> +		shift = CICNR_XCC1T_SHIFT;
> +		break;
> +	case QEIC_CIPRTA:
> +		shift = CRICR_RTA1T_SHIFT;
> +		control_reg = QEIC_CRICR;
> +		break;
> +	case QEIC_CIPRTB:
> +		shift = CRICR_RTB1T_SHIFT;
> +		control_reg = QEIC_CRICR;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	shift += (2 - priority) * 2;
> +	temp = qe_ic_read(qe_ic->regs, control_reg);
> +	temp &= ~(SIGNAL_MASK << shift);
> +	temp |= (high ? SIGNAL_HIGH : SIGNAL_LOW) << shift;
> +	qe_ic_write(qe_ic->regs, control_reg, temp);
> +
> +	return 0;
> +}
> +
> +static struct sysdev_class qe_ic_sysclass = {
> +	set_kset_name("qe_ic"),
> +};
> +
> +static struct sys_device device_qe_ic = {
> +	.id = 0,
> +	.cls = &qe_ic_sysclass,
> +};
> +
> +static int __init init_qe_ic_sysfs(void)
> +{
> +	int rc;
> +
> +	printk(KERN_DEBUG "Registering qe_ic with sysfs...\n");
> +
> +	rc = sysdev_class_register(&qe_ic_sysclass);
> +	if (rc) {
> +		printk(KERN_ERR "Failed registering qe_ic sys class\n");
> +		return -ENODEV;
> +	}
> +	rc = sysdev_register(&device_qe_ic);
> +	if (rc) {
> +		printk(KERN_ERR "Failed registering qe_ic sys device\n");
> +		return -ENODEV;
> +	}
> +	return 0;
> +}
> +
> +subsys_initcall(init_qe_ic_sysfs);
> diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.h b/arch/powerpc/ 
> sysdev/qe_lib/qe_ic.h
> new file mode 100644
> index 0000000..74b1595
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/qe_ic.h
> @@ -0,0 +1,106 @@
> +/*
> + * arch/powerpc/sysdev/qe_lib/qe_ic.h
> + *
> + * QUICC ENGINE Interrupt Controller Header
> + *
> + * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights  
> reserved.
> + *
> + * Author: Li Yang <leoli at freescale.com>
> + * Based on code from Shlomi Gridish <gridish at freescale.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.
> + */
> +#ifndef _POWERPC_SYSDEV_QE_IC_H
> +#define _POWERPC_SYSDEV_QE_IC_H
> +
> +#include <asm/qe_ic.h>
> +
> +#define NR_QE_IC_INTS		64
> +
> +/* QE IC registers offset */
> +#define QEIC_CICR		0x00
> +#define QEIC_CIVEC		0x04
> +#define QEIC_CRIPNR		0x08
> +#define QEIC_CIPNR		0x0c
> +#define QEIC_CIPXCC		0x10
> +#define QEIC_CIPYCC		0x14
> +#define QEIC_CIPWCC		0x18
> +#define QEIC_CIPZCC		0x1c
> +#define QEIC_CIMR		0x20
> +#define QEIC_CRIMR		0x24
> +#define QEIC_CICNR		0x28
> +#define QEIC_CIPRTA		0x30
> +#define QEIC_CIPRTB		0x34
> +#define QEIC_CRICR		0x3c
> +#define QEIC_CHIVEC		0x60
> +
> +/* Interrupt priority registers */
> +#define CIPCC_SHIFT_PRI0	29
> +#define CIPCC_SHIFT_PRI1	26
> +#define CIPCC_SHIFT_PRI2	23
> +#define CIPCC_SHIFT_PRI3	20
> +#define CIPCC_SHIFT_PRI4	13
> +#define CIPCC_SHIFT_PRI5	10
> +#define CIPCC_SHIFT_PRI6	7
> +#define CIPCC_SHIFT_PRI7	4
> +
> +/* CICR priority modes */
> +#define CICR_GWCC		0x00040000
> +#define CICR_GXCC		0x00020000
> +#define CICR_GYCC		0x00010000
> +#define CICR_GZCC		0x00080000
> +#define CICR_GRTA		0x00200000
> +#define CICR_GRTB		0x00400000
> +#define CICR_HPIT_SHIFT		8
> +#define CICR_HPIT_MASK		0x00000300
> +#define CICR_HP_SHIFT		24
> +#define CICR_HP_MASK		0x3f000000
> +
> +/* CICNR */
> +#define CICNR_WCC1T_SHIFT	20
> +#define CICNR_ZCC1T_SHIFT	28
> +#define CICNR_YCC1T_SHIFT	12
> +#define CICNR_XCC1T_SHIFT	4
> +
> +/* CRICR */
> +#define CRICR_RTA1T_SHIFT	20
> +#define CRICR_RTB1T_SHIFT	28
> +
> +/* Signal indicator */
> +#define SIGNAL_MASK		3
> +#define SIGNAL_HIGH		2
> +#define SIGNAL_LOW		0
> +
> +struct qe_ic {
> +	/* Control registers offset */
> +	volatile u32 __iomem *regs;
> +
> +	/* The remapper for this QEIC */
> +	struct irq_host *irqhost;
> +
> +	/* The "linux" controller struct */
> +	struct irq_chip hc_irq;
> +
> +	/* The device node of the interrupt controller */
> +	struct device_node *of_node;
> +
> +	/* VIRQ numbers of QE high/low irqs */
> +	unsigned int virq_high;
> +	unsigned int virq_low;
> +};
> +
> +/*
> + * QE interrupt controller internal structure
> + */
> +struct qe_ic_info {
> +	u32 mask;	/* location of this source at the QIMR register. */
> +	u32 mask_reg;	/* Mask register offset */
> +	u8 pri_code;	/* for grouped interrupts sources - the interrupt
> +			   code as appears at the group priority register. */
> +	u32 pri_reg;		/* Group priority register offset */
> +};
> +
> +#endif /* _POWERPC_SYSDEV_QE_IC_H */
> diff --git a/arch/powerpc/sysdev/qe_lib/qe_io.c b/arch/powerpc/ 
> sysdev/qe_lib/qe_io.c
> new file mode 100644
> index 0000000..56828f0
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/qe_io.c
> @@ -0,0 +1,227 @@
> +/*
> + * arch/powerpc/sysdev/qe_lib/qe_io.c
> + *
> + * QE Parallel I/O ports configuration routines
> + *
> + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights  
> reserved.
> + *
> + * Author: Li Yang <LeoLi at freescale.com>
> + * Based on code from Shlomi Gridish <gridish at freescale.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/config.h>
> +#include <linux/stddef.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/errno.h>
> +#include <linux/module.h>
> +#include <linux/ioport.h>
> +
> +#include <asm/io.h>
> +#include <asm/prom.h>
> +#include <sysdev/fsl_soc.h>
> +
> +#undef DEBUG
> +
> +#define NUM_OF_PINS	32
> +
> +struct port_regs {
> +		u32 cpodr;	/* Open drain register */
> +		u32 cpdata;	/* Data register */
> +		u32 cpdir1;	/* Direction register */
> +		u32 cpdir2;	/* Direction register */
> +		u32 cppar1;	/* Pin assignment register */
> +		u32 cppar2;	/* Pin assignment register */
> +};

Want to mark these as __be32?

> +
> +static struct port_regs *par_io = NULL;
> +static int num_par_io_ports = 0;
> +
> +int par_io_init(struct device_node *np)
> +{
> +	struct resource res;
> +	int ret;
> +	u32 *num_ports;
> +
> +	/* Map Parallel I/O ports registers */
> +	ret = of_address_to_resource(np, 0, &res);
> +	if (ret)
> +		return ret;
> +	par_io = (struct port_regs *)ioremap(res.start, res.end -  
> res.start + 1);
> +
> +	num_ports = get_property(np, "num-ports", NULL);
> +	if (num_ports)
> +		num_par_io_ports = *num_ports;
> +
> +	return 0;
> +}
> +
> +int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain,
> +		      int assignment, int has_irq)
> +{
> +	u32 pin_mask1bit, pin_mask2bits, new_mask2bits, tmp_val;
> +
> +	if (!par_io)
> +		return -1;
> +
> +	/* calculate pin location for single and 2 bits information */
> +	pin_mask1bit = (u32) (1 << (NUM_OF_PINS - (pin + 1)));
> +
> +	/* Set open drain, if required */
> +	tmp_val = in_be32(&par_io[port].cpodr);
> +	if (open_drain)
> +		out_be32(&par_io[port].cpodr, pin_mask1bit | tmp_val);
> +	else
> +		out_be32(&par_io[port].cpodr, ~pin_mask1bit & tmp_val);
> +
> +	/* define direction */
> +	tmp_val = (pin > (NUM_OF_PINS / 2) - 1) ?
> +		in_be32(&par_io[port].cpdir2) :
> +		in_be32(&par_io[port].cpdir1);
> +
> +	/* get all bits mask for 2 bit per port */
> +	pin_mask2bits = (u32) (0x3 << (NUM_OF_PINS -
> +				(pin % (NUM_OF_PINS / 2) + 1) * 2));
> +
> +	/* Get the final mask we need for the right definition */
> +	new_mask2bits = (u32) (dir << (NUM_OF_PINS -
> +				(pin % (NUM_OF_PINS / 2) + 1) * 2));
> +
> +	/* clear and set 2 bits mask */
> +	if (pin > (NUM_OF_PINS / 2) - 1) {
> +		out_be32(&par_io[port].cpdir2,
> +			 ~pin_mask2bits & tmp_val);
> +		tmp_val &= ~pin_mask2bits;
> +		out_be32(&par_io[port].cpdir2, new_mask2bits | tmp_val);
> +	} else {
> +		out_be32(&par_io[port].cpdir1,
> +			 ~pin_mask2bits & tmp_val);
> +		tmp_val &= ~pin_mask2bits;
> +		out_be32(&par_io[port].cpdir1, new_mask2bits | tmp_val);
> +	}
> +	/* define pin assignment */
> +	tmp_val = (pin > (NUM_OF_PINS / 2) - 1) ?
> +		in_be32(&par_io[port].cppar2) :
> +		in_be32(&par_io[port].cppar1);
> +
> +	new_mask2bits = (u32) (assignment << (NUM_OF_PINS -
> +			(pin % (NUM_OF_PINS / 2) + 1) * 2));
> +	/* clear and set 2 bits mask */
> +	if (pin > (NUM_OF_PINS / 2) - 1) {
> +		out_be32(&par_io[port].cppar2,
> +			 ~pin_mask2bits & tmp_val);
> +		tmp_val &= ~pin_mask2bits;
> +		out_be32(&par_io[port].cppar2, new_mask2bits | tmp_val);
> +	} else {
> +		out_be32(&par_io[port].cppar1,
> +			 ~pin_mask2bits & tmp_val);
> +		tmp_val &= ~pin_mask2bits;
> +		out_be32(&par_io[port].cppar1, new_mask2bits | tmp_val);
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(par_io_config_pin);
> +
> +int par_io_data_set(u8 port, u8 pin, u8 val)
> +{
> +	u32 pin_mask, tmp_val;
> +
> +	if (port >= num_par_io_ports)
> +		return -EINVAL;
> +	if (pin >= NUM_OF_PINS)
> +		return -EINVAL;
> +	/* calculate pin location */
> +	pin_mask = (u32) (1 << (NUM_OF_PINS - 1 - pin));
> +
> +	tmp_val = in_be32(&par_io[port].cpdata);
> +
> +	if (val == 0)		/* clear */
> +		out_be32(&par_io[port].cpdata, ~pin_mask & tmp_val);
> +	else			/* set */
> +		out_be32(&par_io[port].cpdata, pin_mask | tmp_val);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(par_io_data_set);
> +
> +int par_io_of_config(struct device_node *np)
> +{
> +	struct device_node *pio;
> +	phandle *ph;
> +	int pio_map_len;
> +	unsigned int *pio_map;
> +
> +	if (par_io == NULL) {
> +		printk(KERN_ERR "par_io not initialized \n");
> +		return -1;
> +	}
> +
> +	ph = (phandle *) get_property(np, "pio-handle", NULL);
> +	if (ph == 0) {
> +		printk(KERN_ERR "pio-handle not available \n");
> +		return -1;
> +	}
> +
> +	pio = of_find_node_by_phandle(*ph);
> +
> +	pio_map = (unsigned int *)
> +		get_property(pio, "pio-map", &pio_map_len);
> +	if (pio_map == NULL) {
> +		printk(KERN_ERR "pio-map is not set! \n");
> +		return -1;
> +	}
> +	pio_map_len /= sizeof(unsigned int);
> +	if ((pio_map_len % 6) != 0) {
> +		printk(KERN_ERR "pio-map format wrong! \n");
> +		return -1;
> +	}
> +
> +	while (pio_map_len > 0) {
> +		par_io_config_pin((u8) pio_map[0], (u8) pio_map[1],
> +				(int) pio_map[2], (int) pio_map[3],
> +				(int) pio_map[4], (int) pio_map[5]);
> +		pio_map += 6;
> +		pio_map_len -= 6;
> +	}
> +	of_node_put(pio);
> +	return 0;
> +}
> +EXPORT_SYMBOL(par_io_of_config);
> +
> +#ifdef DEBUG
> +static void dump_par_io(void)
> +{
> +	int i;
> +
> +	printk(KERN_INFO "PAR IO registars:\n");
> +	printk(KERN_INFO "Base address: 0x%08x\n", (u32) par_io);
> +	for (i = 0; i < num_par_io_ports; i++) {
> +		printk(KERN_INFO "cpodr[%d] : addr - 0x%08x, val - 0x%08x\n",
> +		       i, (u32) & par_io[i].cpodr,
> +		       in_be32(&par_io[i].cpodr));
> +		printk(KERN_INFO "cpdata[%d]: addr - 0x%08x, val - 0x%08x\n",
> +		       i, (u32) & par_io[i].cpdata,
> +		       in_be32(&par_io[i].cpdata));
> +		printk(KERN_INFO "cpdir1[%d]: addr - 0x%08x, val - 0x%08x\n",
> +		       i, (u32) & par_io[i].cpdir1,
> +		       in_be32(&par_io[i].cpdir1));
> +		printk(KERN_INFO "cpdir2[%d]: addr - 0x%08x, val - 0x%08x\n",
> +		       i, (u32) & par_io[i].cpdir2,
> +		       in_be32(&par_io[i].cpdir2));
> +		printk(KERN_INFO "cppar1[%d]: addr - 0x%08x, val - 0x%08x\n",
> +		       i, (u32) & par_io[i].cppar1,
> +		       in_be32(&par_io[i].cppar1));
> +		printk(KERN_INFO "cppar2[%d]: addr - 0x%08x, val - 0x%08x\n",
> +		       i, (u32) & par_io[i].cppar2,
> +		       in_be32(&par_io[i].cppar2));
> +	}
> +
> +}
> +EXPORT_SYMBOL(dump_par_io);
> +#endif /* DEBUG */
> diff --git a/arch/powerpc/sysdev/qe_lib/ucc.c b/arch/powerpc/sysdev/ 
> qe_lib/ucc.c
> new file mode 100644
> index 0000000..916c9e5
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/ucc.c
> @@ -0,0 +1,251 @@
> +/*
> + * arch/powerpc/sysdev/qe_lib/ucc.c
> + *
> + * QE UCC API Set - UCC specific routines implementations.
> + *
> + * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights  
> reserved.
> + *
> + * Authors: 	Shlomi Gridish <gridish at freescale.com>
> + * 		Li Yang <leoli at freescale.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/kernel.h>
> +#include <linux/init.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/stddef.h>
> +
> +#include <asm/irq.h>
> +#include <asm/io.h>
> +#include <asm/immap_qe.h>
> +#include <asm/qe.h>
> +#include <asm/ucc.h>
> +
> +static DEFINE_SPINLOCK(ucc_lock);
> +
> +int ucc_set_qe_mux_mii_mng(int ucc_num)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&ucc_lock, flags);
> +	out_be32(&qe_immr->qmx.cmxgcr,
> +		 ((in_be32(&qe_immr->qmx.cmxgcr) &
> +		   ~QE_CMXGCR_MII_ENET_MNG) |
> +		  (ucc_num << QE_CMXGCR_MII_ENET_MNG_SHIFT)));
> +	spin_unlock_irqrestore(&ucc_lock, flags);
> +
> +	return 0;
> +}
> +
> +int ucc_set_type(int ucc_num, struct ucc_common *regs,
> +		 enum ucc_speed_type speed)
> +{
> +	u8 guemr = 0;
> +
> +	/* check if the UCC number is in range. */
> +	if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0))
> +		return -EINVAL;
> +
> +	guemr = regs->guemr;
> +	guemr &= ~(UCC_GUEMR_MODE_MASK_RX | UCC_GUEMR_MODE_MASK_TX);
> +	switch (speed) {
> +	case UCC_SPEED_TYPE_SLOW:
> +		guemr |= (UCC_GUEMR_MODE_SLOW_RX | UCC_GUEMR_MODE_SLOW_TX);
> +		break;
> +	case UCC_SPEED_TYPE_FAST:
> +		guemr |= (UCC_GUEMR_MODE_FAST_RX | UCC_GUEMR_MODE_FAST_TX);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	regs->guemr = guemr;
> +
> +	return 0;
> +}
> +
> +int ucc_init_guemr(struct ucc_common *regs)
> +{
> +	u8 guemr = 0;
> +
> +	if (!regs)
> +		return -EINVAL;
> +
> +	/* Set bit 3 (which is reserved in the GUEMR register) to 1 */
> +	guemr = UCC_GUEMR_SET_RESERVED3;
> +
> +	regs->guemr = guemr;
> +
> +	return 0;
> +}
> +
> +static void get_cmxucr_reg(int ucc_num, volatile u32 ** p_cmxucr,  
> u8 * reg_num,
> +			   u8 * shift)
> +{
> +	switch (ucc_num) {
> +	case 0: *p_cmxucr = &(qe_immr->qmx.cmxucr1);
> +		*reg_num = 1;
> +		*shift = 16;
> +		break;
> +	case 2: *p_cmxucr = &(qe_immr->qmx.cmxucr1);
> +		*reg_num = 1;
> +		*shift = 0;
> +		break;
> +	case 4: *p_cmxucr = &(qe_immr->qmx.cmxucr2);
> +		*reg_num = 2;
> +		*shift = 16;
> +		break;
> +	case 6: *p_cmxucr = &(qe_immr->qmx.cmxucr2);
> +		*reg_num = 2;
> +		*shift = 0;
> +		break;
> +	case 1: *p_cmxucr = &(qe_immr->qmx.cmxucr3);
> +		*reg_num = 3;
> +		*shift = 16;
> +		break;
> +	case 3: *p_cmxucr = &(qe_immr->qmx.cmxucr3);
> +		*reg_num = 3;
> +		*shift = 0;
> +		break;
> +	case 5: *p_cmxucr = &(qe_immr->qmx.cmxucr4);
> +		*reg_num = 4;
> +		*shift = 16;
> +		break;
> +	case 7: *p_cmxucr = &(qe_immr->qmx.cmxucr4);
> +		*reg_num = 4;
> +		*shift = 0;
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +int ucc_mux_set_grant_tsa_bkpt(int ucc_num, int set, u32 mask)
> +{
> +	volatile u32 *p_cmxucr;
> +	u8 reg_num;
> +	u8 shift;
> +
> +	/* check if the UCC number is in range. */
> +	if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0))
> +		return -EINVAL;
> +
> +	get_cmxucr_reg(ucc_num, &p_cmxucr, &reg_num, &shift);
> +
> +	if (set)
> +		out_be32(p_cmxucr, in_be32(p_cmxucr) | (mask << shift));
> +	else
> +		out_be32(p_cmxucr, in_be32(p_cmxucr) & ~(mask << shift));
> +
> +	return 0;
> +}
> +
> +int ucc_set_qe_mux_rxtx(int ucc_num, enum qe_clock clock, enum  
> comm_dir mode)
> +{
> +	volatile u32 *p_cmxucr;
> +	u8 reg_num;
> +	u8 shift;
> +	u32 clock_bits;
> +	u32 clock_mask;
> +	int source = -1;
> +
> +	/* check if the UCC number is in range. */
> +	if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0))
> +		return -EINVAL;
> +
> +	if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX))) {
> +		printk(KERN_ERR
> +		       "ucc_set_qe_mux_rxtx: bad comm mode type passed.");
> +		return -EINVAL;
> +	}
> +
> +	get_cmxucr_reg(ucc_num, &p_cmxucr, &reg_num, &shift);
> +
> +	switch (reg_num) {
> +	case 1:
> +		switch (clock) {
> +		case QE_BRG1:	source = 1; break;
> +		case QE_BRG2:	source = 2; break;
> +		case QE_BRG7:	source = 3; break;
> +		case QE_BRG8:	source = 4; break;
> +		case QE_CLK9:	source = 5; break;
> +		case QE_CLK10:	source = 6; break;
> +		case QE_CLK11:	source = 7; break;
> +		case QE_CLK12:	source = 8; break;
> +		case QE_CLK15:	source = 9; break;
> +		case QE_CLK16:	source = 10; break;
> +		default: 	source = -1; break;
> +		}
> +		break;
> +	case 2:
> +		switch (clock) {
> +		case QE_BRG5:	source = 1; break;
> +		case QE_BRG6:	source = 2; break;
> +		case QE_BRG7:	source = 3; break;
> +		case QE_BRG8:	source = 4; break;
> +		case QE_CLK13:	source = 5; break;
> +		case QE_CLK14:	source = 6; break;
> +		case QE_CLK19:	source = 7; break;
> +		case QE_CLK20:	source = 8; break;
> +		case QE_CLK15:	source = 9; break;
> +		case QE_CLK16:	source = 10; break;
> +		default: 	source = -1; break;
> +		}
> +		break;
> +	case 3:
> +		switch (clock) {
> +		case QE_BRG9:	source = 1; break;
> +		case QE_BRG10:	source = 2; break;
> +		case QE_BRG15:	source = 3; break;
> +		case QE_BRG16:	source = 4; break;
> +		case QE_CLK3:	source = 5; break;
> +		case QE_CLK4:	source = 6; break;
> +		case QE_CLK17:	source = 7; break;
> +		case QE_CLK18:	source = 8; break;
> +		case QE_CLK7:	source = 9; break;
> +		case QE_CLK8:	source = 10; break;
> +		default:	source = -1; break;
> +		}
> +		break;
> +	case 4:
> +		switch (clock) {
> +		case QE_BRG13:	source = 1; break;
> +		case QE_BRG14:	source = 2; break;
> +		case QE_BRG15:	source = 3; break;
> +		case QE_BRG16:	source = 4; break;
> +		case QE_CLK5:	source = 5; break;
> +		case QE_CLK6:	source = 6; break;
> +		case QE_CLK21:	source = 7; break;
> +		case QE_CLK22:	source = 8; break;
> +		case QE_CLK7:	source = 9; break;
> +		case QE_CLK8:	source = 10; break;
> +		default: 	source = -1; break;
> +		}
> +		break;
> +	default:
> +		source = -1;
> +		break;
> +	}
> +
> +	if (source == -1) {
> +		printk(KERN_ERR
> +		     "ucc_set_qe_mux_rxtx: Bad combination of clock and UCC.");
> +		return -ENOENT;
> +	}
> +
> +	clock_bits = (u32) source;
> +	clock_mask = QE_CMXUCR_TX_CLK_SRC_MASK;
> +	if (mode == COMM_DIR_RX) {
> +		clock_bits <<= 4;  /* Rx field is 4 bits to left of Tx field */
> +		clock_mask <<= 4;  /* Rx field is 4 bits to left of Tx field */
> +	}
> +	clock_bits <<= shift;
> +	clock_mask <<= shift;
> +
> +	out_be32(p_cmxucr, (in_be32(p_cmxucr) & ~clock_mask) | clock_bits);
> +
> +	return 0;
> +}
> diff --git a/arch/powerpc/sysdev/qe_lib/ucc_fast.c b/arch/powerpc/ 
> sysdev/qe_lib/ucc_fast.c
> new file mode 100644
> index 0000000..d635738
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/ucc_fast.c
> @@ -0,0 +1,397 @@
> +/*
> + * arch/powerpc/sysdev/qe_lib/ucc_fast.c
> + *
> + * QE UCC Fast API Set - UCC Fast specific routines implementations.
> + *
> + * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights  
> reserved.
> + *
> + * Authors: 	Shlomi Gridish <gridish at freescale.com>
> + * 		Li Yang <leoli at freescale.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/kernel.h>
> +#include <linux/init.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/stddef.h>
> +#include <linux/interrupt.h>
> +
> +#include <asm/io.h>
> +#include <asm/immap_qe.h>
> +#include <asm/qe.h>
> +
> +#include <asm/ucc.h>
> +#include <asm/ucc_fast.h>
> +
> +#define uccf_printk(level, format, arg...) \
> +	printk(level format "\n", ## arg)
> +
> +#define uccf_dbg(format, arg...) \
> +	uccf_printk(KERN_DEBUG , format , ## arg)
> +#define uccf_err(format, arg...) \
> +	uccf_printk(KERN_ERR , format , ## arg)
> +#define uccf_info(format, arg...) \
> +	uccf_printk(KERN_INFO , format , ## arg)
> +#define uccf_warn(format, arg...) \
> +	uccf_printk(KERN_WARNING , format , ## arg)
> +
> +#ifdef UCCF_VERBOSE_DEBUG
> +#define uccf_vdbg uccf_dbg
> +#else
> +#define uccf_vdbg(fmt, args...) do { } while (0)
> +#endif				/* UCCF_VERBOSE_DEBUG */
> +
> +void ucc_fast_dump_regs(struct ucc_fast_private * uccf)
> +{
> +	uccf_info("UCC%d Fast registers:", uccf->uf_info->ucc_num);
> +	uccf_info("Base address: 0x%08x", (u32) uccf->uf_regs);
> +
> +	uccf_info("gumr  : addr - 0x%08x, val - 0x%08x",
> +		  (u32) & uccf->uf_regs->gumr, in_be32(&uccf->uf_regs->gumr));
> +	uccf_info("upsmr : addr - 0x%08x, val - 0x%08x",
> +		  (u32) & uccf->uf_regs->upsmr, in_be32(&uccf->uf_regs->upsmr));
> +	uccf_info("utodr : addr - 0x%08x, val - 0x%04x",
> +		  (u32) & uccf->uf_regs->utodr, in_be16(&uccf->uf_regs->utodr));
> +	uccf_info("udsr  : addr - 0x%08x, val - 0x%04x",
> +		  (u32) & uccf->uf_regs->udsr, in_be16(&uccf->uf_regs->udsr));
> +	uccf_info("ucce  : addr - 0x%08x, val - 0x%08x",
> +		  (u32) & uccf->uf_regs->ucce, in_be32(&uccf->uf_regs->ucce));
> +	uccf_info("uccm  : addr - 0x%08x, val - 0x%08x",
> +		  (u32) & uccf->uf_regs->uccm, in_be32(&uccf->uf_regs->uccm));
> +	uccf_info("uccs  : addr - 0x%08x, val - 0x%02x",
> +		  (u32) & uccf->uf_regs->uccs, uccf->uf_regs->uccs);
> +	uccf_info("urfb  : addr - 0x%08x, val - 0x%08x",
> +		  (u32) & uccf->uf_regs->urfb, in_be32(&uccf->uf_regs->urfb));
> +	uccf_info("urfs  : addr - 0x%08x, val - 0x%04x",
> +		  (u32) & uccf->uf_regs->urfs, in_be16(&uccf->uf_regs->urfs));
> +	uccf_info("urfet : addr - 0x%08x, val - 0x%04x",
> +		  (u32) & uccf->uf_regs->urfet, in_be16(&uccf->uf_regs->urfet));
> +	uccf_info("urfset: addr - 0x%08x, val - 0x%04x",
> +		  (u32) & uccf->uf_regs->urfset,
> +		  in_be16(&uccf->uf_regs->urfset));
> +	uccf_info("utfb  : addr - 0x%08x, val - 0x%08x",
> +		  (u32) & uccf->uf_regs->utfb, in_be32(&uccf->uf_regs->utfb));
> +	uccf_info("utfs  : addr - 0x%08x, val - 0x%04x",
> +		  (u32) & uccf->uf_regs->utfs, in_be16(&uccf->uf_regs->utfs));
> +	uccf_info("utfet : addr - 0x%08x, val - 0x%04x",
> +		  (u32) & uccf->uf_regs->utfet, in_be16(&uccf->uf_regs->utfet));
> +	uccf_info("utftt : addr - 0x%08x, val - 0x%04x",
> +		  (u32) & uccf->uf_regs->utftt, in_be16(&uccf->uf_regs->utftt));
> +	uccf_info("utpt  : addr - 0x%08x, val - 0x%04x",
> +		  (u32) & uccf->uf_regs->utpt, in_be16(&uccf->uf_regs->utpt));
> +	uccf_info("urtry : addr - 0x%08x, val - 0x%08x",
> +		  (u32) & uccf->uf_regs->urtry, in_be32(&uccf->uf_regs->urtry));
> +	uccf_info("guemr : addr - 0x%08x, val - 0x%02x",
> +		  (u32) & uccf->uf_regs->guemr, uccf->uf_regs->guemr);
> +}
> +
> +u32 ucc_fast_get_qe_cr_subblock(int uccf_num)
> +{
> +	switch (uccf_num) {
> +	case 0:	return QE_CR_SUBBLOCK_UCCFAST1;
> +	case 1: return QE_CR_SUBBLOCK_UCCFAST2;
> +	case 2: return QE_CR_SUBBLOCK_UCCFAST3;
> +	case 3: return QE_CR_SUBBLOCK_UCCFAST4;
> +	case 4: return QE_CR_SUBBLOCK_UCCFAST5;
> +	case 5: return QE_CR_SUBBLOCK_UCCFAST6;
> +	case 6: return QE_CR_SUBBLOCK_UCCFAST7;
> +	case 7:	return QE_CR_SUBBLOCK_UCCFAST8;
> +	default: return QE_CR_SUBBLOCK_INVALID;
> +	}
> +}
> +
> +void ucc_fast_transmit_on_demand(struct ucc_fast_private * uccf)
> +{
> +	out_be16(&uccf->uf_regs->utodr, UCC_FAST_TOD);
> +}
> +
> +void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir  
> mode)
> +{
> +	struct ucc_fast *uf_regs;
> +	u32 gumr;
> +
> +	uf_regs = uccf->uf_regs;
> +
> +	/* Enable reception and/or transmission on this UCC. */
> +	gumr = in_be32(&uf_regs->gumr);
> +	if (mode & COMM_DIR_TX) {
> +		gumr |= UCC_FAST_GUMR_ENT;
> +		uccf->enabled_tx = 1;
> +	}
> +	if (mode & COMM_DIR_RX) {
> +		gumr |= UCC_FAST_GUMR_ENR;
> +		uccf->enabled_rx = 1;
> +	}
> +	out_be32(&uf_regs->gumr, gumr);
> +}
> +
> +void ucc_fast_disable(struct ucc_fast_private * uccf, enum  
> comm_dir mode)
> +{
> +	struct ucc_fast *uf_regs;
> +	u32 gumr;
> +
> +	uf_regs = uccf->uf_regs;
> +
> +	/* Disable reception and/or transmission on this UCC. */
> +	gumr = in_be32(&uf_regs->gumr);
> +	if (mode & COMM_DIR_TX) {
> +		gumr &= ~UCC_FAST_GUMR_ENT;
> +		uccf->enabled_tx = 0;
> +	}
> +	if (mode & COMM_DIR_RX) {
> +		gumr &= ~UCC_FAST_GUMR_ENR;
> +		uccf->enabled_rx = 0;
> +	}
> +	out_be32(&uf_regs->gumr, gumr);
> +}
> +
> +int ucc_fast_init(struct ucc_fast_info * uf_info, struct  
> ucc_fast_private ** uccf_ret)
> +{
> +	struct ucc_fast_private *uccf;
> +	struct ucc_fast *uf_regs;
> +	u32 gumr = 0;
> +	int ret;
> +
> +	uccf_vdbg("%s: IN", __FUNCTION__);
> +
> +	if (!uf_info)
> +		return -EINVAL;
> +
> +	/* check if the UCC port number is in range. */
> +	if ((uf_info->ucc_num < 0) || (uf_info->ucc_num > UCC_MAX_NUM -  
> 1)) {
> +		uccf_err("ucc_fast_init: Illagal UCC number!");
> +		return -EINVAL;
> +	}
> +
> +	/* Check that 'max_rx_buf_length' is properly aligned (4). */
> +	if (uf_info->max_rx_buf_length & (UCC_FAST_MRBLR_ALIGNMENT - 1)) {
> +		uccf_err("ucc_fast_init: max_rx_buf_length not aligned.");
> +		return -EINVAL;
> +	}
> +
> +	/* Validate Virtual Fifo register values */
> +	if (uf_info->urfs < UCC_FAST_URFS_MIN_VAL) {
> +		uccf_err
> +		    ("ucc_fast_init: Virtual Fifo register urfs too small.");
> +		return -EINVAL;
> +	}
> +
> +	if (uf_info->urfs & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) {
> +		uccf_err
> +		    ("ucc_fast_init: Virtual Fifo register urfs not aligned.");
> +		return -EINVAL;
> +	}
> +
> +	if (uf_info->urfet & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) {
> +		uccf_err
> +		    ("ucc_fast_init: Virtual Fifo register urfet not aligned.");
> +		return -EINVAL;
> +	}
> +
> +	if (uf_info->urfset & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) {
> +		uccf_err
> +		   ("ucc_fast_init: Virtual Fifo register urfset not aligned.");
> +		return -EINVAL;
> +	}
> +
> +	if (uf_info->utfs & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) {
> +		uccf_err
> +		    ("ucc_fast_init: Virtual Fifo register utfs not aligned.");
> +		return -EINVAL;
> +	}
> +
> +	if (uf_info->utfet & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) {
> +		uccf_err
> +		    ("ucc_fast_init: Virtual Fifo register utfet not aligned.");
> +		return -EINVAL;
> +	}
> +
> +	if (uf_info->utftt & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) {
> +		uccf_err
> +		    ("ucc_fast_init: Virtual Fifo register utftt not aligned.");
> +		return -EINVAL;
> +	}
> +
> +	uccf =
> +	    (struct ucc_fast_private *) kmalloc(sizeof(struct  
> ucc_fast_private),
> +					   GFP_KERNEL);
> +	if (!uccf) {
> +		uccf_err
> +		    ("ucc_fast_init: No memory for UCC slow data structure!");
> +		return -ENOMEM;
> +	}
> +	memset(uccf, 0, sizeof(struct ucc_fast_private));
> +
> +	/* Fill fast UCC structure */
> +	uccf->uf_info = uf_info;
> +	/* Set the PHY base address */
> +	uccf->uf_regs =
> +	    (struct ucc_fast *) ioremap(uf_info->regs, sizeof(struct  
> ucc_fast));
> +	if (uccf->uf_regs == NULL) {
> +		uccf_err
> +		    ("ucc_fast_init: No memory map for UCC slow controller!");
> +		return -ENOMEM;
> +	}
> +
> +	uccf->enabled_tx = 0;
> +	uccf->enabled_rx = 0;
> +	uccf->stopped_tx = 0;
> +	uccf->stopped_rx = 0;
> +	uf_regs = uccf->uf_regs;
> +	uccf->p_ucce = (u32 *) & (uf_regs->ucce);
> +	uccf->p_uccm = (u32 *) & (uf_regs->uccm);
> +#ifdef STATISTICS
> +	uccf->tx_frames = 0;
> +	uccf->rx_frames = 0;
> +	uccf->rx_discarded = 0;
> +#endif				/* STATISTICS */
> +
> +	/* Init Guemr register */
> +	if ((ret = ucc_init_guemr((struct ucc_common *) (uf_regs)))) {
> +		uccf_err("ucc_fast_init: Could not init the guemr register.");
> +		ucc_fast_free(uccf);
> +		return ret;
> +	}
> +
> +	/* Set UCC to fast type */
> +	if ((ret = ucc_set_type(uf_info->ucc_num,
> +				(struct ucc_common *) (uf_regs),
> +				UCC_SPEED_TYPE_FAST))) {
> +		uccf_err("ucc_fast_init: Could not set type to fast.");
> +		ucc_fast_free(uccf);
> +		return ret;
> +	}
> +
> +	uccf->mrblr = uf_info->max_rx_buf_length;
> +
> +	/* Set GUMR */
> +	/* For more details see the hardware spec. */
> +	/* gumr starts as zero. */
> +	if (uf_info->tci)
> +		gumr |= UCC_FAST_GUMR_TCI;
> +	gumr |= uf_info->ttx_trx;
> +	if (uf_info->cdp)
> +		gumr |= UCC_FAST_GUMR_CDP;
> +	if (uf_info->ctsp)
> +		gumr |= UCC_FAST_GUMR_CTSP;
> +	if (uf_info->cds)
> +		gumr |= UCC_FAST_GUMR_CDS;
> +	if (uf_info->ctss)
> +		gumr |= UCC_FAST_GUMR_CTSS;
> +	if (uf_info->txsy)
> +		gumr |= UCC_FAST_GUMR_TXSY;
> +	if (uf_info->rsyn)
> +		gumr |= UCC_FAST_GUMR_RSYN;
> +	gumr |= uf_info->synl;
> +	if (uf_info->rtsm)
> +		gumr |= UCC_FAST_GUMR_RTSM;
> +	gumr |= uf_info->renc;
> +	if (uf_info->revd)
> +		gumr |= UCC_FAST_GUMR_REVD;
> +	gumr |= uf_info->tenc;
> +	gumr |= uf_info->tcrc;
> +	gumr |= uf_info->mode;
> +	out_be32(&uf_regs->gumr, gumr);
> +
> +	/* Allocate memory for Tx Virtual Fifo */
> +	uccf->ucc_fast_tx_virtual_fifo_base_offset =
> +	    qe_muram_alloc(uf_info->utfs,  
> UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT);
> +	if (IS_MURAM_ERR(uccf->ucc_fast_tx_virtual_fifo_base_offset)) {
> +		uccf_err
> +		    ("ucc_fast_init: Can not allocate MURAM memory for "
> +			"struct ucc_fastx_virtual_fifo_base_offset.");
> +		uccf->ucc_fast_tx_virtual_fifo_base_offset = 0;
> +		ucc_fast_free(uccf);
> +		return -ENOMEM;
> +	}
> +
> +	/* Allocate memory for Rx Virtual Fifo */
> +	uccf->ucc_fast_rx_virtual_fifo_base_offset =
> +	    qe_muram_alloc(uf_info->urfs +
> +			   (u32)
> +			   UCC_FAST_RECEIVE_VIRTUAL_FIFO_SIZE_FUDGE_FACTOR,
> +			   UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT);
> +	if (IS_MURAM_ERR(uccf->ucc_fast_rx_virtual_fifo_base_offset)) {
> +		uccf_err
> +		    ("ucc_fast_init: Can not allocate MURAM memory for "
> +			"ucc_fast_rx_virtual_fifo_base_offset.");
> +		uccf->ucc_fast_rx_virtual_fifo_base_offset = 0;
> +		ucc_fast_free(uccf);
> +		return -ENOMEM;
> +	}
> +
> +	/* Set Virtual Fifo registers */
> +	out_be16(&uf_regs->urfs, uf_info->urfs);
> +	out_be16(&uf_regs->urfet, uf_info->urfet);
> +	out_be16(&uf_regs->urfset, uf_info->urfset);
> +	out_be16(&uf_regs->utfs, uf_info->utfs);
> +	out_be16(&uf_regs->utfet, uf_info->utfet);
> +	out_be16(&uf_regs->utftt, uf_info->utftt);
> +	/* utfb, urfb are offsets from MURAM base */
> +	out_be32(&uf_regs->utfb, uccf- 
> >ucc_fast_tx_virtual_fifo_base_offset);
> +	out_be32(&uf_regs->urfb, uccf- 
> >ucc_fast_rx_virtual_fifo_base_offset);
> +
> +	/* Mux clocking */
> +	/* Grant Support */
> +	ucc_set_qe_mux_grant(uf_info->ucc_num, uf_info->grant_support);
> +	/* Breakpoint Support */
> +	ucc_set_qe_mux_bkpt(uf_info->ucc_num, uf_info->brkpt_support);
> +	/* Set Tsa or NMSI mode. */
> +	ucc_set_qe_mux_tsa(uf_info->ucc_num, uf_info->tsa);
> +	/* If NMSI (not Tsa), set Tx and Rx clock. */
> +	if (!uf_info->tsa) {
> +		/* Rx clock routing */
> +		if (uf_info->rx_clock != QE_CLK_NONE) {
> +			if (ucc_set_qe_mux_rxtx
> +			    (uf_info->ucc_num, uf_info->rx_clock,
> +			     COMM_DIR_RX)) {
> +				uccf_err
> +		("ucc_fast_init: Illegal value for parameter 'RxClock'.");
> +				ucc_fast_free(uccf);
> +				return -EINVAL;
> +			}
> +		}
> +		/* Tx clock routing */
> +		if (uf_info->tx_clock != QE_CLK_NONE) {
> +			if (ucc_set_qe_mux_rxtx
> +			    (uf_info->ucc_num, uf_info->tx_clock,
> +			     COMM_DIR_TX)) {
> +				uccf_err
> +		("ucc_fast_init: Illegal value for parameter 'TxClock'.");
> +				ucc_fast_free(uccf);
> +				return -EINVAL;
> +			}
> +		}
> +	}
> +
> +	/* Set interrupt mask register at UCC level. */
> +	out_be32(&uf_regs->uccm, uf_info->uccm_mask);
> +
> +	/* First, clear anything pending at UCC level,
> +	 * otherwise, old garbage may come through
> +	 * as soon as the dam is opened
> +	 * Writing '1' clears
> +	 */
> +	out_be32(&uf_regs->ucce, 0xffffffff);
> +
> +	*uccf_ret = uccf;
> +	return 0;
> +}
> +
> +void ucc_fast_free(struct ucc_fast_private * uccf)
> +{
> +	if (!uccf)
> +		return;
> +
> +	if (uccf->ucc_fast_tx_virtual_fifo_base_offset)
> +		qe_muram_free(uccf->ucc_fast_tx_virtual_fifo_base_offset);
> +
> +	if (uccf->ucc_fast_rx_virtual_fifo_base_offset)
> +		qe_muram_free(uccf->ucc_fast_rx_virtual_fifo_base_offset);
> +
> +	kfree(uccf);
> +}
> diff --git a/arch/powerpc/sysdev/qe_lib/ucc_slow.c b/arch/powerpc/ 
> sysdev/qe_lib/ucc_slow.c
> new file mode 100644
> index 0000000..de86985
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/ucc_slow.c
> @@ -0,0 +1,404 @@
> +/*
> + * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights  
> reserved.
> + *
> + * Authors: 	Shlomi Gridish <gridish at freescale.com>
> + * 		Li Yang <leoli at freescale.com>
> + *
> + * Description:
> + * QE UCC Slow API Set - UCC Slow specific routines implementations.
> + *
> + * 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/kernel.h>
> +#include <linux/init.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/stddef.h>
> +#include <linux/interrupt.h>
> +
> +#include <asm/irq.h>
> +#include <asm/io.h>
> +#include <asm/immap_qe.h>
> +#include <asm/qe.h>
> +
> +#include <asm/ucc.h>
> +#include <asm/ucc_slow.h>
> +
> +#define uccs_printk(level, format, arg...) \
> +        printk(level format "\n", ## arg)
> +
> +#define uccs_dbg(format, arg...) \
> +	uccs_printk(KERN_DEBUG , format , ## arg)
> +#define uccs_err(format, arg...) \
> +	uccs_printk(KERN_ERR , format , ## arg)
> +#define uccs_info(format, arg...) \
> +	uccs_printk(KERN_INFO , format , ## arg)
> +#define uccs_warn(format, arg...) \
> +	uccs_printk(KERN_WARNING , format , ## arg)
> +
> +#ifdef UCCS_VERBOSE_DEBUG
> +#define uccs_vdbg uccs_dbg
> +#else
> +#define uccs_vdbg(fmt, args...) do { } while (0)
> +#endif				/* UCCS_VERBOSE_DEBUG */
> +
> +u32 ucc_slow_get_qe_cr_subblock(int uccs_num)
> +{
> +	switch (uccs_num) {
> +	case 0: return QE_CR_SUBBLOCK_UCCSLOW1;
> +	case 1: return QE_CR_SUBBLOCK_UCCSLOW2;
> +	case 2: return QE_CR_SUBBLOCK_UCCSLOW3;
> +	case 3: return QE_CR_SUBBLOCK_UCCSLOW4;
> +	case 4: return QE_CR_SUBBLOCK_UCCSLOW5;
> +	case 5: return QE_CR_SUBBLOCK_UCCSLOW6;
> +	case 6: return QE_CR_SUBBLOCK_UCCSLOW7;
> +	case 7: return QE_CR_SUBBLOCK_UCCSLOW8;
> +	default: return QE_CR_SUBBLOCK_INVALID;
> +	}
> +}
> +
> +void ucc_slow_poll_transmitter_now(struct ucc_slow_private * uccs)
> +{
> +	out_be16(&uccs->us_regs->utodr, UCC_SLOW_TOD);
> +}
> +
> +void ucc_slow_graceful_stop_tx(struct ucc_slow_private * uccs)
> +{
> +	struct ucc_slow_info *us_info = uccs->us_info;
> +	u32 id;
> +
> +	id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num);
> +	qe_issue_cmd(QE_GRACEFUL_STOP_TX, id,
> +			 (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
> +}
> +
> +void ucc_slow_stop_tx(struct ucc_slow_private * uccs)
> +{
> +	struct ucc_slow_info *us_info = uccs->us_info;
> +	u32 id;
> +
> +	id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num);
> +	qe_issue_cmd(QE_STOP_TX, id, (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
> +}
> +
> +void ucc_slow_restart_tx(struct ucc_slow_private * uccs)
> +{
> +	struct ucc_slow_info *us_info = uccs->us_info;
> +	u32 id;
> +
> +	id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num);
> +	qe_issue_cmd(QE_RESTART_TX, id, (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
> +}
> +
> +void ucc_slow_enable(struct ucc_slow_private * uccs, enum comm_dir  
> mode)
> +{
> +	struct ucc_slow *us_regs;
> +	u32 gumr_l;
> +
> +	us_regs = uccs->us_regs;
> +
> +	/* Enable reception and/or transmission on this UCC. */
> +	gumr_l = in_be32(&us_regs->gumr_l);
> +	if (mode & COMM_DIR_TX) {
> +		gumr_l |= UCC_SLOW_GUMR_L_ENT;
> +		uccs->enabled_tx = 1;
> +	}
> +	if (mode & COMM_DIR_RX) {
> +		gumr_l |= UCC_SLOW_GUMR_L_ENR;
> +		uccs->enabled_rx = 1;
> +	}
> +	out_be32(&us_regs->gumr_l, gumr_l);
> +}
> +
> +void ucc_slow_disable(struct ucc_slow_private * uccs, enum  
> comm_dir mode)
> +{
> +	struct ucc_slow *us_regs;
> +	u32 gumr_l;
> +
> +	us_regs = uccs->us_regs;
> +
> +	/* Disable reception and/or transmission on this UCC. */
> +	gumr_l = in_be32(&us_regs->gumr_l);
> +	if (mode & COMM_DIR_TX) {
> +		gumr_l &= ~UCC_SLOW_GUMR_L_ENT;
> +		uccs->enabled_tx = 0;
> +	}
> +	if (mode & COMM_DIR_RX) {
> +		gumr_l &= ~UCC_SLOW_GUMR_L_ENR;
> +		uccs->enabled_rx = 0;
> +	}
> +	out_be32(&us_regs->gumr_l, gumr_l);
> +}
> +
> +int ucc_slow_init(struct ucc_slow_info * us_info, struct  
> ucc_slow_private ** uccs_ret)
> +{
> +	u32 i;
> +	struct ucc_slow *us_regs;
> +	u32 gumr;
> +	u8 function_code = 0;
> +	u8 *bd;
> +	struct ucc_slow_private *uccs;
> +	u32 id;
> +	u32 command;
> +	int ret;
> +
> +	uccs_vdbg("%s: IN", __FUNCTION__);
> +
> +	if (!us_info)
> +		return -EINVAL;
> +
> +	/* check if the UCC port number is in range. */
> +	if ((us_info->ucc_num < 0) || (us_info->ucc_num > UCC_MAX_NUM -  
> 1)) {
> +		uccs_err("ucc_slow_init: Illagal UCC number!");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Set mrblr
> +	 * Check that 'max_rx_buf_length' is properly aligned (4), unless
> + 	 * rfw is 1, meaning that QE accepts one byte at a time, unlike  
> normal
> +	 * case when QE accepts 32 bits at a time.
> +	 */
> +	if ((!us_info->rfw) &&
> +		(us_info->max_rx_buf_length & (UCC_SLOW_MRBLR_ALIGNMENT - 1))) {
> +		uccs_err("max_rx_buf_length not aligned.");
> +		return -EINVAL;
> +	}
> +
> +	uccs = (struct ucc_slow_private *)
> +		kmalloc(sizeof(struct ucc_slow_private), GFP_KERNEL);
> +	if (!uccs) {
> +		uccs_err
> +		    ("ucc_slow_init: No memory for UCC slow data structure!");
> +		return -ENOMEM;
> +	}
> +	memset(uccs, 0, sizeof(struct ucc_slow_private));
> +
> +	/* Fill slow UCC structure */
> +	uccs->us_info = us_info;
> +	uccs->saved_uccm = 0;
> +	uccs->p_rx_frame = 0;
> +	uccs->us_regs = us_info->us_regs;
> +	us_regs = uccs->us_regs;
> +	uccs->p_ucce = (u16 *) & (us_regs->ucce);
> +	uccs->p_uccm = (u16 *) & (us_regs->uccm);
> +#ifdef STATISTICS
> +	uccs->rx_frames = 0;
> +	uccs->tx_frames = 0;
> +	uccs->rx_discarded = 0;
> +#endif				/* STATISTICS */
> +
> +	/* Get PRAM base */
> +	uccs->us_pram_offset = qe_muram_alloc(UCC_SLOW_PRAM_SIZE,
> +						 ALIGNMENT_OF_UCC_SLOW_PRAM);
> +	if (IS_MURAM_ERR(uccs->us_pram_offset)) {
> +		uccs_err
> +		    ("ucc_slow_init: Can not allocate MURAM memory "
> +			"for Slow UCC.");
> +		ucc_slow_free(uccs);
> +		return -ENOMEM;
> +	}
> +	id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num);
> +	qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, id,  
> QE_CR_PROTOCOL_UNSPECIFIED,
> +			(u32) uccs->us_pram_offset);
> +
> +	uccs->us_pram = qe_muram_addr(uccs->us_pram_offset);
> +
> +	/* Init Guemr register */
> +	if ((ret = ucc_init_guemr((struct ucc_common *) (us_info- 
> >us_regs)))) {
> +		uccs_err("ucc_slow_init: Could not init the guemr register.");
> +		ucc_slow_free(uccs);
> +		return ret;
> +	}
> +
> +	/* Set UCC to slow type */
> +	if ((ret = ucc_set_type(us_info->ucc_num,
> +				(struct ucc_common *) (us_info->us_regs),
> +				UCC_SPEED_TYPE_SLOW))) {
> +		uccs_err("ucc_slow_init: Could not init the guemr register.");
> +		ucc_slow_free(uccs);
> +		return ret;
> +	}
> +
> +	out_be16(&uccs->us_pram->mrblr, us_info->max_rx_buf_length);
> +
> +	INIT_LIST_HEAD(&uccs->confQ);
> +
> +	/* Allocate BDs. */
> +	uccs->rx_base_offset =
> +		qe_muram_alloc(us_info->rx_bd_ring_len * sizeof(struct qe_bd),
> +				QE_ALIGNMENT_OF_BD);
> +	if (IS_MURAM_ERR(uccs->rx_base_offset)) {
> +		uccs_err("ucc_slow_init: No memory for Rx BD's.");
> +		uccs->rx_base_offset = 0;
> +		ucc_slow_free(uccs);
> +		return -ENOMEM;
> +	}
> +
> +	uccs->tx_base_offset =
> +		qe_muram_alloc(us_info->tx_bd_ring_len * sizeof(struct qe_bd),
> +			QE_ALIGNMENT_OF_BD);
> +	if (IS_MURAM_ERR(uccs->tx_base_offset)) {
> +		uccs_err("ucc_slow_init: No memory for Tx BD's.");
> +		uccs->tx_base_offset = 0;
> +		ucc_slow_free(uccs);
> +		return -ENOMEM;
> +	}
> +
> +	/* Init Tx bds */
> +	bd = uccs->confBd = uccs->tx_bd = qe_muram_addr(uccs- 
> >tx_base_offset);
> +	for (i = 0; i < us_info->tx_bd_ring_len; i++) {
> +		/* clear bd buffer */
> +		out_be32(&(((struct qe_bd *)bd)->buf), 0);
> +		/* set bd status and length */
> +		out_be32((u32*)bd, 0);
> +		bd += sizeof(struct qe_bd);
> +	}
> +	bd -= sizeof(struct qe_bd);
> +	/* set bd status and length */
> +	out_be32((u32*)bd, T_W);	/* for last BD set Wrap bit */
> +
> +	/* Init Rx bds */
> +	bd = uccs->rx_bd = qe_muram_addr(uccs->rx_base_offset);
> +	for (i = 0; i < us_info->rx_bd_ring_len; i++) {
> +		/* set bd status and length */
> +		out_be32((u32*)bd, 0);
> +		/* clear bd buffer */
> +		out_be32(&(((struct qe_bd *)bd)->buf), 0);
> +		bd += sizeof(struct qe_bd);
> +	}
> +	bd -= sizeof(struct qe_bd);
> +	/* set bd status and length */
> +	out_be32((u32*)bd, R_W);	/* for last BD set Wrap bit */
> +
> +	/* Set GUMR (For more details see the hardware spec.). */
> +	/* gumr_h */
> +	gumr = 0;
> +	gumr |= us_info->tcrc;
> +	if (us_info->cdp)
> +		gumr |= UCC_SLOW_GUMR_H_CDP;
> +	if (us_info->ctsp)
> +		gumr |= UCC_SLOW_GUMR_H_CTSP;
> +	if (us_info->cds)
> +		gumr |= UCC_SLOW_GUMR_H_CDS;
> +	if (us_info->ctss)
> +		gumr |= UCC_SLOW_GUMR_H_CTSS;
> +	if (us_info->tfl)
> +		gumr |= UCC_SLOW_GUMR_H_TFL;
> +	if (us_info->rfw)
> +		gumr |= UCC_SLOW_GUMR_H_RFW;
> +	if (us_info->txsy)
> +		gumr |= UCC_SLOW_GUMR_H_TXSY;
> +	if (us_info->rtsm)
> +		gumr |= UCC_SLOW_GUMR_H_RTSM;
> +	out_be32(&us_regs->gumr_h, gumr);
> +
> +	/* gumr_l */
> +	gumr = 0;
> +	if (us_info->tci)
> +		gumr |= UCC_SLOW_GUMR_L_TCI;
> +	if (us_info->rinv)
> +		gumr |= UCC_SLOW_GUMR_L_RINV;
> +	if (us_info->tinv)
> +		gumr |= UCC_SLOW_GUMR_L_TINV;
> +	if (us_info->tend)
> +		gumr |= UCC_SLOW_GUMR_L_TEND;
> +	gumr |= us_info->tdcr;
> +	gumr |= us_info->rdcr;
> +	gumr |= us_info->tenc;
> +	gumr |= us_info->renc;
> +	gumr |= us_info->diag;
> +	gumr |= us_info->mode;
> +	out_be32(&us_regs->gumr_l, gumr);
> +
> +	/* Function code registers */
> +	/* function_code has initial value 0 */
> +
> +	/* if the data is in cachable memory, the 'global' */
> +	/* in the function code should be set. */
> +	function_code |= us_info->data_mem_part;
> +	function_code |= QE_BMR_BYTE_ORDER_BO_MOT;	/* Required for QE */
> +	uccs->us_pram->tfcr = function_code;
> +	uccs->us_pram->rfcr = function_code;
> +
> +	/* rbase, tbase are offsets from MURAM base */
> +	out_be16(&uccs->us_pram->rbase, uccs->us_pram_offset);
> +	out_be16(&uccs->us_pram->tbase, uccs->us_pram_offset);
> +
> +	/* Mux clocking */
> +	/* Grant Support */
> +	ucc_set_qe_mux_grant(us_info->ucc_num, us_info->grant_support);
> +	/* Breakpoint Support */
> +	ucc_set_qe_mux_bkpt(us_info->ucc_num, us_info->brkpt_support);
> +	/* Set Tsa or NMSI mode. */
> +	ucc_set_qe_mux_tsa(us_info->ucc_num, us_info->tsa);
> +	/* If NMSI (not Tsa), set Tx and Rx clock. */
> +	if (!us_info->tsa) {
> +		/* Rx clock routing */
> +		if (ucc_set_qe_mux_rxtx
> +		    (us_info->ucc_num, us_info->rx_clock, COMM_DIR_RX)) {
> +			uccs_err
> +			    ("ucc_slow_init: Illegal value for parameter"
> +				" 'RxClock'.");
> +			ucc_slow_free(uccs);
> +			return -EINVAL;
> +		}
> +		/* Tx clock routing */
> +		if (ucc_set_qe_mux_rxtx(us_info->ucc_num,
> +				 us_info->tx_clock, COMM_DIR_TX)) {
> +			uccs_err
> +			    ("ucc_slow_init: Illegal value for parameter "
> +				"'TxClock'.");
> +			ucc_slow_free(uccs);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/*
> +	 * INTERRUPTS
> +	 */
> +	/* Set interrupt mask register at UCC level. */
> +	out_be16(&us_regs->uccm, us_info->uccm_mask);
> +
> +	/* First, clear anything pending at UCC level, */
> +	/* otherwise, old garbage may come through */
> +	/* as soon as the dam is opened. */
> +
> +	/* Writing '1' clears */
> +	out_be16(&us_regs->ucce, 0xffff);
> +
> +	/* Issue QE Init command */
> +	if (us_info->init_tx && us_info->init_rx)
> +		command = QE_INIT_TX_RX;
> +	else if (us_info->init_tx)
> +		command = QE_INIT_TX;
> +	else
> +		command = QE_INIT_RX;	/* We know at least one is TRUE */
> +	id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num);
> +	qe_issue_cmd(command, id, (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
> +
> +	*uccs_ret = uccs;
> +	return 0;
> +}
> +
> +void ucc_slow_free(struct ucc_slow_private * uccs)
> +{
> +	if (!uccs)
> +		return;
> +
> +	if (uccs->rx_base_offset)
> +		qe_muram_free(uccs->rx_base_offset);
> +
> +	if (uccs->tx_base_offset)
> +		qe_muram_free(uccs->tx_base_offset);
> +
> +	if (uccs->us_pram) {
> +		qe_muram_free(uccs->us_pram_offset);
> +		uccs->us_pram = NULL;
> +	}
> +
> +	kfree(uccs);
> +}
> -- 
> 1.4.2.3




More information about the Linuxppc-dev mailing list