linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* RE: [PATCH 3/7] powerpc: Add QE library qe_lib--common files
@ 2006-06-29 16:06 Gridish Shlomi-RM96313
  2006-06-29 16:33 ` Kumar Gala
  0 siblings, 1 reply; 6+ messages in thread
From: Gridish Shlomi-RM96313 @ 2006-06-29 16:06 UTC (permalink / raw)
  To: Kumar Gala, Li Yang-r58472
  Cc: linuxppc-dev, Phillips Kim-R1AAHA, Paul Mackerras, linux-kernel,
	Chu hanjin-r52514

Hi all,

The reason why I chose  to work with MM and not with RHEAP is that,
apparently, there is a BUG in the RHEAP module,=20
it is not allocating correctly blocks that require alignments grater
than 8.
The usage of MM is only a temporary solution till RHEAP is fixed.

Regards,
Shlomi


-----Original Message-----
From: Kumar Gala [mailto:galak@kernel.crashing.org]=20
Sent: Wednesday, June 28, 2006 22:20
To: Li Yang-r58472
Cc: 'Paul Mackerras'; linuxppc-dev@ozlabs.org;
'linux-kernel@vger.kernel.org'; Chu hanjin-r52514; Gridish
Shlomi-RM96313; Phillips Kim-R1AAHA
Subject: Re: [PATCH 3/7] powerpc: Add QE library qe_lib--common files

Nack, remove the mm allocation code and just use rheap.

- k

On Jun 28, 2006, at 9:23 AM, Li Yang-r58472 wrote:

>
> Signed-off-by: Shlomi Gridish <gridish@freescale.com>
> Signed-off-by: Li Yang <leoli@freescale.com>
> Signed-off-by: Kim Phillips <kim.phillips@freescale.com>
>
> ---
>  arch/powerpc/Kconfig                   |   12
>  arch/powerpc/sysdev/Makefile           |    1
>  arch/powerpc/sysdev/ipic.c             |    2
>  arch/powerpc/sysdev/qe_lib/Kconfig     |  315 +++++++++++++
>  arch/powerpc/sysdev/qe_lib/Makefile    |    8
>  arch/powerpc/sysdev/qe_lib/mm.c        |  770 +++++++++++++++++++++=20
> +++++++++++
>  arch/powerpc/sysdev/qe_lib/mm.h        |    6
>  arch/powerpc/sysdev/qe_lib/qe.c        |  181 ++++++++
>  arch/powerpc/sysdev/qe_lib/qe_common.c |  401 +++++++++++++++++
>  arch/powerpc/sysdev/qe_lib/qe_ic.c     |  487 ++++++++++++++++++++
>  arch/powerpc/sysdev/qe_lib/qe_ic.h     |   83 +++
>  arch/powerpc/sysdev/qe_lib/qe_io.c     |  275 +++++++++++
>  12 files changed, 2541 insertions(+), 0 deletions(-)
>
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 6729c98..6d4fc0b 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -334,6 +334,16 @@ config APUS
>  	  <http://linux-apus.sourceforge.net/>.
>  endchoice
>
> +config QUICC_ENGINE
> +	bool
> +	depends on MPC836x
> +	default y
> +	help
> +	  The QE(QUICC Engine) is a new generation of coprocessor on
> +	  Freescale embedded CPUs(like CPM  in older chips).  Selecting
> +	  this option means that you wish to build a kernel for a
machine
> +	  with QE coprocessor on it.
> +
>  config PPC_PSERIES
>  	depends on PPC_MULTIPLATFORM && PPC64
>  	bool "  IBM pSeries & new (POWER5-based) iSeries"
> @@ -993,6 +1003,8 @@ # XXX source "arch/ppc/8xx_io/Kconfig"
>
>  # XXX source "arch/ppc/8260_io/Kconfig"
>
> +source "arch/powerpc/sysdev/qe_lib/Kconfig"
> +
>  source "arch/powerpc/platforms/iseries/Kconfig"
>
>  source "lib/Kconfig"
> diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/=20
> Makefile
> index 4c2b356..cd1d5cc 100644
> --- a/arch/powerpc/sysdev/Makefile
> +++ b/arch/powerpc/sysdev/Makefile
> @@ -8,3 +8,4 @@ obj-$(CONFIG_U3_DART)		+=3D dart_iommu.o
>  obj-$(CONFIG_MMIO_NVRAM)	+=3D mmio_nvram.o
>  obj-$(CONFIG_PPC_83xx)		+=3D ipic.o
>  obj-$(CONFIG_FSL_SOC)		+=3D fsl_soc.o
> +obj-$(CONFIG_QUICC_ENGINE)	+=3D qe_lib/
> diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c
> index 8f01e0f..dbeccba 100644
> --- a/arch/powerpc/sysdev/ipic.c
> +++ b/arch/powerpc/sysdev/ipic.c
> @@ -537,12 +537,14 @@ void ipic_set_highest_priority(unsigned
>
>  void ipic_set_default_priority(void)
>  {
> +#ifdef CONFIG_MPC834x
>  	ipic_set_priority(MPC83xx_IRQ_TSEC1_TX, 0);
>  	ipic_set_priority(MPC83xx_IRQ_TSEC1_RX, 1);
>  	ipic_set_priority(MPC83xx_IRQ_TSEC1_ERROR, 2);
>  	ipic_set_priority(MPC83xx_IRQ_TSEC2_TX, 3);
>  	ipic_set_priority(MPC83xx_IRQ_TSEC2_RX, 4);
>  	ipic_set_priority(MPC83xx_IRQ_TSEC2_ERROR, 5);
> +#endif
>  	ipic_set_priority(MPC83xx_IRQ_USB2_DR, 6);
>  	ipic_set_priority(MPC83xx_IRQ_USB2_MPH, 7);
>
> diff --git a/arch/powerpc/sysdev/qe_lib/Kconfig b/arch/powerpc/=20
> sysdev/qe_lib/Kconfig
> new file mode 100644
> index 0000000..6105237
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/Kconfig
> @@ -0,0 +1,315 @@
> +#
> +# QE Communication options
> +#
> +
> +menu "QE Options"
> +	depends on QUICC_ENGINE
> +=09
> +config UCC1
> +	bool "Enable QE UCC1"
> +
> +choice
> +	prompt "UCC1 speed selection"
> +	depends on UCC1
> +	default UCC1_SLOW
> +
> +	config UCC1_SLOW
> +		bool "UCC1 is slow"
> +	config UCC1_FAST
> +		bool "UCC1 is fast"
> +endchoice
> +
> +menu "UCC1 Protocols options"
> +	depends on UCC1
> +
> +	choice
> +		prompt "UCC1 Slow Protocols selection"
> +		depends on UCC1_SLOW
> +		default UCC1_UART
> +
> +		config UCC1_UART
> +			bool "UCC1 is UART"
> +	endchoice
> +
> +	choice
> +		prompt "UCC1 Fast Protocols selection"
> +		depends on UCC1_FAST
> +		default UCC1_GETH
> +
> +		config UCC1_GETH
> +			bool "UCC1 is GETH"
> +	endchoice
> +endmenu
> +
> +config UCC2
> +	bool "Enable QE UCC2"
> +
> +choice
> +	prompt "UCC2 speed selection"
> +	depends on UCC2
> +	default UCC2_SLOW
> +
> +	config UCC2_SLOW
> +		bool "UCC2 is slow"
> +	config UCC2_FAST
> +		bool "UCC2 is fast"
> +endchoice
> +
> +menu "UCC2 Protocols options"
> +	depends on UCC2
> +
> +	choice
> +		prompt "UCC2 Slow Protocols selection"
> +		depends on UCC2_SLOW
> +		default UCC2_UART
> +
> +		config UCC2_UART
> +			bool "UCC2 is UART"
> +	endchoice
> +
> +	choice
> +		prompt "UCC2 Fast Protocols selection"
> +		depends on UCC2_FAST
> +		default UCC2_GETH
> +
> +		config UCC2_GETH
> +			bool "UCC2 is GETH"
> +	endchoice
> +endmenu
> +
> +config UCC3
> +	bool "Enable QE UCC3"
> +
> +choice
> +	prompt "UCC3 speed selection"
> +	depends on UCC3
> +	default UCC3_SLOW
> +
> +	config UCC3_SLOW
> +		bool "UCC3 is slow"
> +	config UCC3_FAST
> +		bool "UCC3 is fast"
> +endchoice
> +
> +menu "UCC3 Protocols options"
> +	depends on UCC3
> +
> +	choice
> +		prompt "UCC3 Slow Protocols selection"
> +		depends on UCC3_SLOW
> +		default UCC3_UART
> +
> +		config UCC3_UART
> +			bool "UCC3 is UART"
> +        endchoice
> +
> +	config UCC3_GETH
> +		depends on UCC3_FAST
> +		bool "UCC3 is GETH"
> +
> +	config UCC3_ATM
> +		depends on UCC3_FAST && !UCC3_GETH
> +		tristate "UCC3 is ATM"
> +endmenu
> +
> +config UCC4
> +	bool "Enable QE UCC4"
> +
> +choice
> +	prompt "UCC4 speed selection"
> +	depends on UCC4
> +	default UCC4_SLOW
> +
> +	config UCC4_SLOW
> +		bool "UCC4 is slow"
> +	config UCC4_FAST
> +		bool "UCC4 is fast"
> +endchoice
> +
> +menu "UCC4 Protocols options"
> +	depends on UCC4
> +
> +	choice
> +		prompt "UCC4 Slow Protocols selection"
> +		depends on UCC4_SLOW
> +		default UCC4_UART
> +
> +		config UCC4_UART
> +			bool "UCC4 is UART"
> +	endchoice
> +
> +	choice
> +		prompt "UCC4 Fast Protocols selection"
> +		depends on UCC4_FAST
> +		default UCC4_GETH
> +
> +		config UCC4_GETH
> +			bool "UCC4 is GETH"
> +	endchoice
> +endmenu
> +
> +config UCC5
> +	bool "Enable QE UCC5"
> +
> +choice
> +	prompt "UCC5 speed selection"
> +	depends on UCC5
> +	default UCC5_SLOW
> +
> +	config UCC5_SLOW
> +		bool "UCC5 is slow"
> +	config UCC5_FAST
> +		bool "UCC5 is fast"
> +endchoice
> +
> +menu "UCC5 Protocols options"
> +	depends on UCC5
> +
> +	choice
> +		prompt "UCC5 Slow Protocols selection"
> +		depends on UCC5_SLOW
> +		default UCC5_UART
> +
> +		config UCC5_UART
> +			bool "UCC5 is UART"
> +	endchoice
> +
> +	choice
> +		prompt "UCC5 Fast Protocols selection"
> +		depends on UCC5_FAST
> +		default UCC5_GETH
> +
> +		config UCC5_GETH
> +			bool "UCC5 is GETH"
> +	endchoice
> +endmenu
> +
> +config UCC6
> +	bool "Enable QE UCC6"
> +
> +choice
> +	prompt "UCC6 speed selection"
> +	depends on UCC6
> +	default UCC6_SLOW
> +
> +	config UCC6_SLOW
> +		bool "UCC6 is slow"
> +	config UCC6_FAST
> +		bool "UCC6 is fast"
> +endchoice
> +
> +menu "UCC6 Protocols options"
> +	depends on UCC6
> +
> +	choice
> +		prompt "UCC6 Slow Protocols selection"
> +		depends on UCC6_SLOW
> +		default UCC6_UART
> +
> +		config UCC6_UART
> +			bool "UCC6 is UART"
> +	endchoice
> +
> +	choice
> +		prompt "UCC6 Fast Protocols selection"
> +		depends on UCC6_FAST
> +		default UCC6_GETH
> +
> +		config UCC6_GETH
> +			bool "UCC6 is GETH"
> +	endchoice
> +endmenu
> +
> +config UCC7
> +	bool "Enable QE UCC7"
> +
> +choice
> +	prompt "UCC7 speed selection"
> +	depends on UCC7
> +	default UCC7_SLOW
> +
> +	config UCC7_SLOW
> +		bool "UCC7 is slow"
> +	config UCC7_FAST
> +		bool "UCC7 is fast"
> +endchoice
> +
> +menu "UCC7 Protocols options"
> +	depends on UCC7
> +
> +	choice
> +		prompt "UCC7 Slow Protocols selection"
> +		depends on UCC7_SLOW
> +		default UCC7_UART
> +
> +		config UCC7_UART
> +			bool "UCC7 is UART"
> +	endchoice
> +
> +	choice
> +		prompt "UCC7 Fast Protocols selection"
> +		depends on UCC7_FAST
> +		default UCC7_GETH
> +
> +		config UCC7_GETH
> +			bool "UCC7 is GETH"
> +	endchoice
> +endmenu
> +
> +config UCC8
> +	bool "Enable QE UCC8"
> +
> +choice
> +	prompt "UCC8 speed selection"
> +	depends on UCC8
> +	default UCC8_SLOW
> +
> +	config UCC8_SLOW
> +		bool "UCC8 is slow"
> +	config UCC8_FAST
> +		bool "UCC8 is fast"
> +endchoice
> +
> +menu "UCC8 Protocols options"
> +	depends on UCC8
> +
> +	choice
> +		prompt "UCC8 Slow Protocols selection"
> +		depends on UCC8_SLOW
> +		default UCC8_UART
> +
> +		config UCC8_UART
> +			bool "UCC8 is UART"
> +	endchoice
> +
> +	choice
> +		prompt "UCC8 Fast Protocols selection"
> +		depends on UCC8_FAST
> +		default UCC8_GETH
> +
> +		config UCC8_GETH
> +			bool "UCC8 is GETH"
> +	endchoice
> +endmenu
> +
> +config UCC
> +	depends on UCC1 || UCC2 || UCC3 || UCC4 || UCC5 || UCC6 || UCC7

> || UCC8
> +	default y
> +	bool
> +
> +config UCC_SLOW
> +	depends on UCC1_SLOW || UCC2_SLOW || UCC3_SLOW || UCC4_SLOW || =20
> UCC5_SLOW || UCC6_SLOW || UCC7_SLOW || UCC8_SLOW
> +	default y
> +	bool
> +
> +config UCC_FAST
> +	depends on UCC1_FAST || UCC2_FAST || UCC3_FAST || UCC4_FAST || =20
> UCC5_FAST || UCC6_FAST || UCC7_FAST || UCC8_FAST
> +	default y
> +	bool
> +
> +config UCC_GETH_CONF
> +	depends on UCC1_GETH || UCC2_GETH || UCC3_GETH || UCC4_GETH || =20
> UCC5_GETH || UCC6_GETH || UCC7_GETH || UCC8_GETH
> +	default y
> +	bool
> +endmenu
> +
> diff --git a/arch/powerpc/sysdev/qe_lib/Makefile b/arch/powerpc/=20
> sysdev/qe_lib/Makefile
> new file mode 100644
> index 0000000..c04a70c
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/Makefile
> @@ -0,0 +1,8 @@
> +#
> +# Makefile for the linux ppc-specific parts of QE
> +#
> +obj-$(CONFIG_QUICC_ENGINE)+=3D qe_common.o mm.o qe.o qe_ic.o qe_io.o
> +
> +obj-$(CONFIG_UCC)	+=3D ucc.o
> +obj-$(CONFIG_UCC_SLOW)	+=3D ucc_slow.o
> +obj-$(CONFIG_UCC_FAST)	+=3D ucc_fast.o ucc_slow.o
> diff --git a/arch/powerpc/sysdev/qe_lib/mm.c b/arch/powerpc/sysdev/=20
> qe_lib/mm.c
> new file mode 100644
> index 0000000..58984ba
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/mm.c
> @@ -0,0 +1,770 @@
> +/*
> + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights =20
> reserved.
> + *
> + * Author: Shlomi Gridish <gridish@freescale.com>
> + *
> + * Description:
> + * QE Memory Manager.
> + *
> + * Changelog:
> + * Jun 28, 2006 Li Yang <LeoLi@freescale.com>
> + * - Reorganized as qe_lib
> + * - Merged to powerpc arch; add device tree support
> + * - Style fixes
> + *
> + * This program is free software; you can redistribute  it and/or =20
> modify it
> + * under  the terms of  the GNU General  Public License as =20
> published by the
> + * Free Software Foundation;  either version 2 of the  License, or =20
> (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 <asm/irq.h>
> +#include <asm/page.h>
> +#include <asm/pgtable.h>
> +
> +#include "mm.h"
> +
> +#define MAX_ALIGNMENT   20
> +#define MAX_NAME_LEN    50
> +
> +#define MAKE_ALIGNED(adr, align) ( ((u32)adr + (align - 1)) & (~=20
> (align - 1)) )
> +
> +/* mem_block_t data stucutre defines parameters of the Memory =20
> Block */
> +typedef struct mem_block {
> +	struct mem_block *next;	/* Pointer to the next memory block */
> +
> +	u32 base;		/* base address of the memory block */
> +	u32 end;		/* end address of the memory block */
> +} mem_block_t;
> +
> +/* free_block_t data stucutre defines parameters of the Free Block */
> +typedef struct free_block {
> +	struct free_block *next;	/* Pointer to the next free
block */
> +
> +	u32 base;		/* base address of the block */
> +	u32 end;		/* end address of the block */
> +} free_block_t;
> +
> +/* busy_block_t data stucutre defines parameters of the Busy =20
> Block  */
> +typedef struct busy_block {
> +	struct busy_block *next;	/* Pointer to the next free
block */
> +
> +	u32 base;		/* base address of the block */
> +	u32 end;		/* end address of the block */
> +	char name[MAX_NAME_LEN];
> +} busy_block_t;
> +
> +/* mm_t data structure defines parameters of the MM object */
> +typedef struct mm {
> +	mem_block_t *mem_blocks;    /* List of memory blocks (Memory =20
> list) */
> +	busy_block_t *busy_blocks;	/* List of busy blocks (Busy
list) */
> +	free_block_t *free_blocks[MAX_ALIGNMENT + 1];/* align lists of
free
> +							blocks (Free
lists) */
> +} mm_t;
> +
> +/=20
> **********************************************************************
> + *                     MM internal routines =20
> set                       *
> + =20
> **********************************************************************

> /
> +
> +/****************************************************************
> + *  Routine:   mem_block_init
> + *
> + *  Description:
> + *      Initializes a new memory block of "size" bytes and started
> + *      from "base" address.
> + *
> + *  Arguments:
> + *      mem_blk- handle to the mem_blk object
> + *      base    - base address of the memory block
> + *      size    - size of the memory block
> + *
> + *  Return value:
> + *      0 is returned on success. E_NOMEMORY is returned
> + *      if can't allocate memory for mem_blk object.
> + ****************************************************************/
> +static int mem_block_init(void **mem_blk, u32 base, u32 size)
> +{
> +	mem_block_t *p_mem_blk;
> +
> +	p_mem_blk =3D (mem_block_t *) kmalloc(sizeof(mem_block_t), =20
> GFP_KERNEL);
> +	if (!p_mem_blk)
> +		return -ENOMEM;
> +
> +	p_mem_blk->base =3D base;
> +	p_mem_blk->end =3D base + size;
> +	p_mem_blk->next =3D 0;
> +
> +	*mem_blk =3D p_mem_blk;
> +
> +	return (0);
> +}
> +
> +/****************************************************************
> + *  Routine:   free_block_init
> + *
> + *  Description:
> + *      Initializes a new free block of of "size" bytes and
> + *      started from "base" address.
> + *
> + *  Arguments:
> + *      FreeBlock - handle to the FreeBlock object
> + *      base      - base address of the free block
> + *      size      - size of the free block
> + *
> + *  Return value:
> + *      0 is returned on success. E_NOMEMORY is returned
> + *      if can't allocate memory for a free block.
> + ****************************************************************/
> +static int free_block_init(void **FreeBlock, u32 base, u32 size)
> +{
> +	free_block_t *p_free_blk;
> +
> +	p_free_blk =3D (free_block_t *) kmalloc(sizeof(free_block_t), =20
> GFP_KERNEL);
> +	if (!p_free_blk)
> +		return -ENOMEM;
> +
> +	p_free_blk->base =3D base;
> +	p_free_blk->end =3D base + size;
> +	p_free_blk->next =3D 0;
> +
> +	*FreeBlock =3D p_free_blk;
> +
> +	return (0);
> +}
> +
> +/****************************************************************
> + *  Routine:   busy_block_init
> + *
> + *  Description:
> + *      Initializes a new busy block of "size" bytes and started
> + *      rom "base" address. Each busy block has a name that
> + *      specified the purpose of the memory allocation.
> + *
> + *  Arguments:
> + *      BusyBlock - handle to the BusyBlock object
> + *      base      - base address of the busy block
> + *      size      - size of the busy block
> + *      name      - name that specified the busy block
> + *
> + *  Return value:
> + *      0 is returned on success. E_NOMEMORY is returned
> + *      if can't allocate memory for busy block.
> + ****************************************************************/
> +static int busy_block_init(void **BusyBlock, u32 base, u32 size, =20
> char *name)
> +{
> +	busy_block_t *p_busy_blk;
> +	int n, NameLen;
> +
> +	p_busy_blk =3D (busy_block_t *) kmalloc(sizeof(busy_block_t), =20
> GFP_KERNEL);
> +	if (!p_busy_blk)
> +		return -ENOMEM;
> +
> +	p_busy_blk->base =3D base;
> +	p_busy_blk->end =3D base + size;
> +	NameLen =3D (int)strlen(name);
> +	n =3D (NameLen > MAX_NAME_LEN - 1) ? MAX_NAME_LEN - 1 : NameLen;
> +	strncpy(p_busy_blk->name, name, (u32) n);
> +	p_busy_blk->name[n] =3D '\0';
> +	p_busy_blk->next =3D 0;
> +
> +	*BusyBlock =3D p_busy_blk;
> +
> +	return (0);
> +}
> +
> +/****************************************************************
> + *  Routine:    add_free
> + *
> + *  Description:
> + *      Adds a new free block to the free lists. It updates each
> + *      free list to include a new free block.
> + *      Note, that all free block in each free list are ordered
> + *      by their base address.
> + *
> + *  Arguments:
> + *      p_mm  - pointer to the MM object
> + *      base  - base address of a given free block
> + *      end   - end address of a given free block
> + *
> + *  Return value:
> + *
> + *
> + ****************************************************************/
> +static int add_free(mm_t * p_mm, u32 base, u32 end)
> +{
> +	free_block_t *p_prev_blk, *p_curr_blk, *p_new_blk;
> +	u32 align;
> +	int i;
> +	u32 align_base;
> +
> +	/* Updates free lists to include  a just released block */
> +	for (i =3D 0; i <=3D MAX_ALIGNMENT; i++) {
> +		p_prev_blk =3D p_new_blk =3D 0;
> +		p_curr_blk =3D p_mm->free_blocks[i];
> +
> +		align =3D (u32) (0x1 << i);
> +		align_base =3D MAKE_ALIGNED(base, align);
> +
> +		/* Goes to the next free list if there is no block to
free */
> +		if (align_base >=3D end)
> +			continue;
> +
> +		/* Looks for a free block that should be updated */
> +		while (p_curr_blk) {
> +			if (align_base <=3D p_curr_blk->end) {
> +				if (end > p_curr_blk->end) {
> +					free_block_t *p_NextB;
> +					while (p_curr_blk->next
> +					       && end >
p_curr_blk->next->end) {
> +						p_NextB =3D
p_curr_blk->next;
> +						p_curr_blk->next =3D
> +
p_curr_blk->next->next;
> +						kfree(p_NextB);
> +					}
> +
> +					p_NextB =3D p_curr_blk->next;
> +					if (!p_NextB
> +					    || (p_NextB
> +						&& end < p_NextB->base))
{
> +						p_curr_blk->end =3D end;
> +					} else {
> +						p_curr_blk->end =3D
p_NextB->end;
> +						p_curr_blk->next =3D
> +						    p_NextB->next;
> +						kfree(p_NextB);
> +					}
> +				} else if (end < p_curr_blk->base
> +					   && ((end - align_base) >=3D
align)) {
> +					if (free_block_init
> +					    ((void *)&p_new_blk,
align_base,
> +					     end - align_base) !=3D 0)
> +						return -ENOMEM;
> +					p_new_blk->next =3D p_curr_blk;
> +					if (p_prev_blk)
> +						p_prev_blk->next =3D
p_new_blk;
> +					else
> +						p_mm->free_blocks[i] =3D
> +						    p_new_blk;
> +					break;
> +				}
> +
> +				if (align_base < p_curr_blk->base
> +				    && end >=3D p_curr_blk->base)
> +					p_curr_blk->base =3D align_base;
> +
> +				/* if size of the free block is less
then
> +				 * alignment deletes that free block
from
> +				 * the free list. */
> +				if ((p_curr_blk->end - p_curr_blk->base)
<
> +				    align) {
> +					if (p_prev_blk)
> +						p_prev_blk->next =3D
> +						    p_curr_blk->next;
> +					else
> +						p_mm->free_blocks[i] =3D
> +						    p_curr_blk->next;
> +					kfree(p_curr_blk);
> +				}
> +				break;
> +			} else {
> +				p_prev_blk =3D p_curr_blk;
> +				p_curr_blk =3D p_curr_blk->next;
> +			}
> +		}
> +
> +		/* If no free block found to be updated, insert a new
free block
> +		 * to the end of the free list. */
> +		if (!p_curr_blk && ((end - base) % align =3D=3D 0)) {
> +			if (free_block_init
> +			    ((void *)&p_new_blk, align_base, end - base)
!=3D 0)
> +				return -ENOMEM;
> +			if (p_prev_blk)
> +				p_prev_blk->next =3D p_new_blk;
> +			else
> +				p_mm->free_blocks[i] =3D p_new_blk;
> +		}
> +
> +		/* Update boundaries of the new free block */
> +		if (align =3D=3D 1 && !p_new_blk) {
> +			if (p_curr_blk && base > p_curr_blk->base)
> +				base =3D p_curr_blk->base;
> +			if (p_curr_blk && end < p_curr_blk->end)
> +				end =3D p_curr_blk->end;
> +		}
> +	}
> +
> +	return (0);
> +}
> +
> +/****************************************************************
> + *  Routine:      cut_free
> + *
> + *  Description:
> + *      Cuts a free block from hold_base to hold_end from the free =20
> lists.
> + *      That is, it updates all free lists of the MM object do
> + *      not include a block of memory from hold_base to hold_end.
> + *      For each free lists it seek for a free block that holds
> + *      either hold_base or hold_end. If such block is found it =20
> updates it.
> + *
> + *  Arguments:
> + *      p_mm            - pointer to the MM object
> + *      hold_base        - base address of the allocated block
> + *      hold_end         - end address of the allocated block
> + *
> + *  Return value:
> + *      0 is returned on success,
> + *      otherwise returns an error code.
> + *
> + ****************************************************************/
> +static int cut_free(mm_t * p_mm, u32 hold_base, u32 hold_end)
> +{
> +	free_block_t *p_prev_blk, *p_curr_blk, *p_new_blk;
> +	u32 align_base, base, end, align;
> +	int i;
> +
> +	for (i =3D 0; i <=3D MAX_ALIGNMENT; i++) {
> +		p_prev_blk =3D p_new_blk =3D 0;
> +		p_curr_blk =3D p_mm->free_blocks[i];
> +
> +		align =3D (u32) 0x1 << i;
> +		align_base =3D MAKE_ALIGNED(hold_end, align);
> +
> +		while (p_curr_blk) {
> +			base =3D p_curr_blk->base;
> +			end =3D p_curr_blk->end;
> +
> +			if (hold_base <=3D base && hold_end <=3D end
> +			    && hold_end > base) {
> +				if (align_base >=3D end
> +				    || (align_base < end
> +					&& (end - align_base) < align))
{
> +					if (p_prev_blk)
> +						p_prev_blk->next =3D
> +						    p_curr_blk->next;
> +					else
> +						p_mm->free_blocks[i] =3D
> +						    p_curr_blk->next;
> +					kfree(p_curr_blk);
> +				} else {
> +					p_curr_blk->base =3D align_base;
> +				}
> +				break;
> +			} else if (hold_base > base && hold_end <=3D end)
{
> +				if ((hold_base - base) >=3D align) {
> +					if (align_base < end
> +					    && (end - align_base) >=3D
align) {
> +						if (free_block_init
> +						    ((void *)&p_new_blk,
> +						     align_base,
> +						     (end - align_base))
!=3D 0)
> +							return -ENOMEM;
> +						p_new_blk->next =3D
> +						    p_curr_blk->next;
> +						p_curr_blk->next =3D
p_new_blk;
> +					}
> +					p_curr_blk->end =3D hold_base;
> +				} else if (align_base < end
> +					   && (end - align_base) >=3D
align) {
> +					p_curr_blk->base =3D align_base;
> +				} else {
> +					if (p_prev_blk)
> +						p_prev_blk->next =3D
> +						    p_curr_blk->next;
> +					else
> +						p_mm->free_blocks[i] =3D
> +						    p_curr_blk->next;
> +					kfree(p_curr_blk);
> +				}
> +				break;
> +			} else {
> +				p_prev_blk =3D p_curr_blk;
> +				p_curr_blk =3D p_curr_blk->next;
> +			}
> +		}
> +	}
> +
> +	return (0);
> +}
> +
> +/****************************************************************
> + *  Routine:     add_busy
> + *
> + *  Description:
> + *      Adds a new busy block to the list of busy blocks. Note,
> + *      that all busy blocks are ordered by their base address in
> + *      the busy list.
> + *
> + *  Arguments:
> + *      MM              - handler to the MM object
> + *      p_new_busy_blk      - pointer to the a busy block
> + *
> + *  Return value:
> + *      None.
> + *
> + ****************************************************************/
> +static void add_busy(mm_t * p_mm, busy_block_t * p_new_busy_blk)
> +{
> +	busy_block_t *p_cur_busy_blk, *p_prev_busy_blk;
> +
> +	/* finds a place of a new busy block in the list of busy blocks
*/
> +	p_prev_busy_blk =3D 0;
> +	p_cur_busy_blk =3D p_mm->busy_blocks;
> +
> +	while (p_cur_busy_blk && p_new_busy_blk->base > p_cur_busy_blk-=20
> >base) {
> +		p_prev_busy_blk =3D p_cur_busy_blk;
> +		p_cur_busy_blk =3D p_cur_busy_blk->next;
> +	}
> +
> +	/* insert the new busy block into the list of busy blocks */
> +	if (p_cur_busy_blk)
> +		p_new_busy_blk->next =3D p_cur_busy_blk;
> +	if (p_prev_busy_blk)
> +		p_prev_busy_blk->next =3D p_new_busy_blk;
> +	else
> +		p_mm->busy_blocks =3D p_new_busy_blk;
> +
> +}
> +
> +/****************************************************************
> + *  Routine:     get_greater_align
> + *
> + *  Description:
> + *      Allocates a block of memory according to the given size
> + *      and the alignment. That routine is called from the mm_get
> + *      routine if the required alignment is grater then =20
> MAX_ALIGNMENT.
> + *      In that case, it goes over free blocks of 64 byte align list
> + *      and checks if it has the required size of bytes of the =20
> required
> + *      alignment. If no blocks found returns ILLEGAL_BASE.
> + *      After the block is found and data is allocated, it calls
> + *      the internal cut_free routine to update all free lists
> + *      do not include a just allocated block. Of course, each
> + *      free list contains a free blocks with the same alignment.
> + *      It is also creates a busy block that holds
> + *      information about an allocated block.
> + *
> + *  Arguments:
> + *      MM              - handle to the MM object
> + *      size            - size of the MM
> + *      align       - index as a power of two defines
> + *                        a required alignment that is grater then =20
> 64.
> + *      name            - the name that specifies an allocated block.
> + *
> + *  Return value:
> + *      base address of an allocated block.
> + *      ILLEGAL_BASE if can't allocate a block
> + *
> + ****************************************************************/
> +static int get_greater_align(void *MM, u32 size, int align, char =20
> *name)
> +{
> +	mm_t *p_mm =3D (mm_t *) MM;
> +	free_block_t *p_free_blk;
> +	busy_block_t *p_new_busy_blk;
> +	u32 hold_base, hold_end, align_base =3D 0;
> +	u32 ret;
> +
> +	/* goes over free blocks of the 64 byte alignment list
> +	 * and look for a block of the suitable size and
> +	 * base address according to the alignment.
> +	 */
> +	p_free_blk =3D p_mm->free_blocks[MAX_ALIGNMENT];
> +
> +	while (p_free_blk) {
> +		align_base =3D MAKE_ALIGNED(p_free_blk->base, align);
> +
> +		/* the block is found if the aligned base inside the
block
> +		 * and has the anough size.
> +		 */
> +		if (align_base >=3D p_free_blk->base &&
> +		    align_base < p_free_blk->end &&
> +		    size <=3D (p_free_blk->end - align_base))
> +			break;
> +		else
> +			p_free_blk =3D p_free_blk->next;
> +	}
> +
> +	/* If such block isn't found */
> +	if (!p_free_blk)
> +		return -EBUSY;
> +
> +	hold_base =3D align_base;
> +	hold_end =3D align_base + size;
> +
> +	/* init a new busy block */
> +	if ((ret =3D
> +	     busy_block_init((void *)&p_new_busy_blk, hold_base, size,
> +			     name)) !=3D 0)
> +		return ret;
> +
> +	/* calls Update routine to update a lists of free blocks */
> +	if ((ret =3D cut_free(MM, hold_base, hold_end)) !=3D 0)
> +		return ret;
> +
> +	/* insert the new busy block into the list of busy blocks */
> +	add_busy(p_mm, p_new_busy_blk);
> +
> +	return (hold_base);
> +}
> +
> +/=20
> **********************************************************************
> + *                     MM API routines =20
> set                            *
> + =20
> **********************************************************************

> /
> +int mm_init(void **MM, u32 base, u32 size)
> +{
> +	mm_t *p_mm;
> +	int i;
> +	u32 new_base, new_size;
> +
> +	/* Initializes a new MM object */
> +	p_mm =3D (mm_t *) kmalloc(sizeof(mm_t), GFP_KERNEL);
> +	if (p_mm =3D=3D 0)
> +		return -ENOMEM;
> +
> +	/* initializes a new memory block */
> +	if (mem_block_init((void *)&p_mm->mem_blocks, base, size) !=3D 0)
> +		return -ENOMEM;
> +
> +	/* A busy list is empty */
> +	p_mm->busy_blocks =3D 0;
> +
> +	/*Initializes a new free block for each free list */
> +	for (i =3D 0; i <=3D MAX_ALIGNMENT; i++) {
> +		new_base =3D MAKE_ALIGNED(base, (0x1 << i));
> +		new_size =3D size - (new_base - base);
> +		if (free_block_init((void *)&p_mm->free_blocks[i],
> +				    new_base, new_size) !=3D 0)
> +			return -ENOMEM;
> +	}
> +
> +	*MM =3D p_mm;
> +
> +	return (0);
> +}
> +
> +EXPORT_SYMBOL(mm_init);
> +
> +void mm_free(void *MM)
> +{
> +	mm_t *p_mm =3D (mm_t *) MM;
> +	mem_block_t *p_mem_blk;
> +	busy_block_t *p_busy_blk;
> +	free_block_t *p_free_blk;
> +	void *p_blk;
> +	int i;
> +
> +	if (!p_mm)
> +		return;
> +
> +	/* release memory allocated for busy blocks */
> +	p_busy_blk =3D p_mm->busy_blocks;
> +	while (p_busy_blk) {
> +		p_blk =3D p_busy_blk;
> +		p_busy_blk =3D p_busy_blk->next;
> +		kfree(p_blk);
> +	}
> +
> +	/* release memory allocated for free blocks */
> +	for (i =3D 0; i <=3D MAX_ALIGNMENT; i++) {
> +		p_free_blk =3D p_mm->free_blocks[i];
> +		while (p_free_blk) {
> +			p_blk =3D p_free_blk;
> +			p_free_blk =3D p_free_blk->next;
> +			kfree(p_blk);
> +		}
> +	}
> +
> +	/* release memory allocated for memory blocks */
> +	p_mem_blk =3D p_mm->mem_blocks;
> +	while (p_mem_blk) {
> +		p_blk =3D p_mem_blk;
> +		p_mem_blk =3D p_mem_blk->next;
> +		kfree(p_blk);
> +	}
> +
> +	/* release memory allocated for MM object itself */
> +	kfree(p_mm);
> +}
> +
> +EXPORT_SYMBOL(mm_free);
> +
> +void *mm_get(void *MM, u32 size, int align, char *name)
> +{
> +	mm_t *p_mm =3D (mm_t *) MM;
> +	free_block_t *p_free_blk;
> +	busy_block_t *p_new_busy_blk;
> +	u32 hold_base, hold_end;
> +	u32 i =3D 0, j =3D (u32) align;
> +	u32 ret;
> +
> +	if (!p_mm)
> +		return ERR_PTR(-EINVAL);
> +
> +	/* checks that align value is grater then zero */
> +	if (align =3D=3D 0)
> +		return ERR_PTR(-EINVAL);
> +
> +	/* checks if alignment is a power of two,
> +	 * if it correct and if the required size
> +	 * is multiple of the given alignment.
> +	 */
> +	while ((j & 0x1) =3D=3D 0) {
> +		i++;
> +		j =3D j >> 1;
> +	}
> +
> +	/* if the given alignment isn't power of two, returns an error
*/
> +	if (j !=3D 1)
> +		return ERR_PTR(-EINVAL);
> +
> +	if (i > MAX_ALIGNMENT)
> +		return ERR_PTR(get_greater_align(MM, size, align,
name));
> +
> +	/* look for a block of the size grater or equal to the
> +	 * required size.
> +	 */
> +	p_free_blk =3D p_mm->free_blocks[i];
> +	while (p_free_blk && (p_free_blk->end - p_free_blk->base) <
size)
> +		p_free_blk =3D p_free_blk->next;
> +
> +	/* If such block is found */
> +	if (!p_free_blk)
> +		return ERR_PTR(-ENOMEM);
> +
> +	hold_base =3D p_free_blk->base;
> +	hold_end =3D hold_base + size;
> +
> +	/* init a new busy block */
> +	if ((ret =3D
> +	     busy_block_init((void *)&p_new_busy_blk, hold_base, size,
> +			     name)) !=3D 0)
> +		return ERR_PTR(ret);
> +
> +	/* calls Update routine to update a lists of free blocks */
> +	if ((ret =3D cut_free(MM, hold_base, hold_end)) !=3D 0)
> +		return ERR_PTR(ret);
> +
> +	/* insert the new busy block into the list of busy blocks */
> +	add_busy(p_mm, p_new_busy_blk);
> +
> +	return (void *)(hold_base);
> +}
> +
> +EXPORT_SYMBOL(mm_get);
> +
> +void *mm_get_force(void *MM, u32 base, u32 size, char *name)
> +{
> +	mm_t *p_mm =3D (mm_t *) MM;
> +	free_block_t *p_free_blk;
> +	busy_block_t *p_new_busy_blk;
> +	int blk_is_free =3D 0;
> +	u32 ret;
> +
> +	p_free_blk =3D p_mm->free_blocks[0];/* The biggest free blocks are
in
> +					     the free list with
alignment 1 */
> +	while (p_free_blk) {
> +		if (base >=3D p_free_blk->base
> +		    && (base + size) <=3D p_free_blk->end) {
> +			blk_is_free =3D 1;
> +			break;
> +		} else
> +			p_free_blk =3D p_free_blk->next;
> +	}
> +
> +	if (!blk_is_free)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* init a new busy block */
> +	if ((ret =3D
> +	     busy_block_init((void *)&p_new_busy_blk, base, size, name))
!=20
> =3D 0)
> +		return ERR_PTR(ret);
> +
> +	/* calls Update routine to update a lists of free blocks */
> +	if ((ret =3D cut_free(MM, base, base + size)) !=3D 0)
> +		return ERR_PTR(ret);
> +
> +	/* insert the new busy block into the list of busy blocks */
> +	add_busy(p_mm, p_new_busy_blk);
> +	return (void *)(base);
> +}
> +
> +EXPORT_SYMBOL(mm_get_force);
> +
> +int mm_put(void *MM, u32 base)
> +{
> +	mm_t *p_mm =3D (mm_t *) MM;
> +	busy_block_t *p_busy_blk, *p_prev_busy_blk;
> +	u32 size;
> +	u32 ret;
> +
> +	if (!p_mm)
> +		return -EINVAL;
> +
> +	/* Look for a busy block that have the given base value.
> +	 * That block will be returned back to the memory.
> +	 */
> +	p_prev_busy_blk =3D 0;
> +	p_busy_blk =3D p_mm->busy_blocks;
> +	while (p_busy_blk && base !=3D p_busy_blk->base) {
> +		p_prev_busy_blk =3D p_busy_blk;
> +		p_busy_blk =3D p_busy_blk->next;
> +	}
> +
> +	if (!p_busy_blk)
> +		return -EINVAL;
> +
> +	if ((ret =3D add_free(p_mm, p_busy_blk->base, p_busy_blk->end)) !=3D
0)
> +		return ret;
> +
> +	/* removes a busy block form the list of busy blocks */
> +	if (p_prev_busy_blk)
> +		p_prev_busy_blk->next =3D p_busy_blk->next;
> +	else
> +		p_mm->busy_blocks =3D p_busy_blk->next;
> +
> +	size =3D p_busy_blk->end - p_busy_blk->base;
> +
> +	kfree(p_busy_blk);
> +
> +	return (0);
> +}
> +
> +EXPORT_SYMBOL(mm_put);
> +
> +void mm_dump(void *MM)
> +{
> +	mm_t *p_mm =3D (mm_t *) MM;
> +	free_block_t *p_free_blk;
> +	busy_block_t *p_busy_blk;
> +	int i;
> +
> +	p_busy_blk =3D p_mm->busy_blocks;
> +	printk(KERN_INFO "List of busy blocks:\n");
> +	while (p_busy_blk) {
> +		printk(KERN_INFO "\t0x%08x: (%s: b=3D0x%08x, e=3D0x%08x)\n",
> +		       (u32) p_busy_blk, p_busy_blk->name,
p_busy_blk->base,
> +		       p_busy_blk->end);
> +		p_busy_blk =3D p_busy_blk->next;
> +	}
> +
> +	printk(KERN_INFO "Lists of free blocks according to
alignment:\n");
> +	for (i =3D 0; i <=3D MAX_ALIGNMENT; i++) {
> +		printk(KERN_INFO "%d alignment:\n", (0x1 << i));
> +		p_free_blk =3D p_mm->free_blocks[i];
> +		while (p_free_blk) {
> +			printk(KERN_INFO "\t0x%08x: (b=3D0x%08x,
e=3D0x%08x)\n",
> +			       (u32) p_free_blk, p_free_blk->base,
> +			       p_free_blk->end);
> +			p_free_blk =3D p_free_blk->next;
> +		}
> +		printk(KERN_INFO "\n");
> +	}
> +}
> +
> +EXPORT_SYMBOL(mm_dump);
> diff --git a/arch/powerpc/sysdev/qe_lib/mm.h b/arch/powerpc/sysdev/=20
> qe_lib/mm.h
> new file mode 100644
> index 0000000..ab30080
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/mm.h
> @@ -0,0 +1,6 @@
> +int mm_init ( void **MM, u32 base, u32 size );
> +void mm_free (void * MM);
> +void *mm_get ( void * MM, u32 size, int align, char* name );
> +void *mm_get_force ( void * MM, u32 base, u32 size, char* name );
> +int mm_put ( void * MM, u32 base );
> +void mm_dump ( void * MM );
> diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/=20
> qe_lib/qe.c
> new file mode 100644
> index 0000000..0fbb54c
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/qe.c
> @@ -0,0 +1,181 @@
> +/*
> + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights =20
> reserved.
> + *
> + * Author: Li Yang <LeoLi@freescale.com>
> + *
> + * Description:
> + * FSL QE SOC setup.
> + *
> + * Changelog:
> + * Jun 21, 2006	Initial version
> + *
> + * This program is free software; you can redistribute  it and/or =20
> modify it
> + * under  the terms of  the GNU General  Public License as =20
> published by the
> + * Free Software Foundation;  either version 2 of the  License, or =20
> (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 =3D -1;
> +
> +phys_addr_t get_qe_base(void)
> +{
> +	struct device_node *qe;
> +
> +	if (qebase !=3D -1)
> +		return qebase;
> +
> +	qe =3D of_find_node_by_type(NULL, "qe");
> +	if (qe) {
> +		unsigned int size;
> +		void *prop =3D get_property(qe, "reg", &size);
> +		qebase =3D 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 =3D NULL, i =3D 0;
> +	     (np =3D of_find_compatible_node(np, "network", "ucc_geth"))
!=3D =20
> 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 =3D of_address_to_resource(np, 0, &r[0]);
> +		if (ret)
> +			goto err;
> +
> +		ugeth_data.phy_reg_addr =3D r[0].start;
> +		r[1].start =3D np->intrs[0].line;
> +		r[1].end =3D np->intrs[0].line;
> +		r[1].flags =3D IORESOURCE_IRQ;
> +
> +		model =3D get_property(np, "model", NULL);
> +		ucc_num =3D *((u32 *) get_property(np, "device-id",
NULL));
> +		if ((strstr(model, "UCC") =3D=3D NULL) ||
> +				(ucc_num < 1) || (ucc_num > 8)) {
> +			ret =3D -ENODEV;
> +			goto err;
> +		}
> +	=09
> +		ugeth_dev =3D
> +		    platform_device_register_simple("ucc_geth", ucc_num
- 1,
> +				    &r[0], np->n_intrs + 1);
> +
> +		if (IS_ERR(ugeth_dev)) {
> +			ret =3D PTR_ERR(ugeth_dev);
> +			goto err;
> +		}
> +
> +		mac_addr =3D get_property(np, "mac-address", NULL);
> +	=09
> +		memcpy(ugeth_data.mac_addr, mac_addr, 6);
> +
> +		ugeth_data.rx_clock =3D *((u32 *) get_property(np,
"rx-clock",
> +					NULL));
> +		ugeth_data.tx_clock =3D *((u32 *) get_property(np,
"tx-clock",
> +					NULL));
> +
> +		ph =3D (phandle *) get_property(np, "phy-handle", NULL);
> +		phy =3D of_find_node_by_phandle(*ph);
> +
> +		if (phy =3D=3D NULL) {
> +			ret =3D -ENODEV;
> +			goto unreg;
> +		}
> +
> +		mdio =3D of_get_parent(phy);
> +
> +		id =3D (u32 *) get_property(phy, "reg", NULL);
> +		ret =3D of_address_to_resource(mdio, 0, &res);
> +		if (ret) {
> +			of_node_put(phy);
> +			of_node_put(mdio);
> +			goto unreg;
> +		}
> +	=09
> +		ugeth_data.phy_id =3D *id;
> +		ugeth_data.phy_interrupt =3D phy->intrs[0].line;
> +		ugeth_data.phy_interface =3D *((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?).
*/=09
> +		if ((ugeth_data.phy_interface =3D=3D ENET_1000_RGMII) ||
> +				(ugeth_data.phy_interface =3D=3D
ENET_100_RGMII) ||
> +				(ugeth_data.phy_interface =3D=3D
ENET_10_RGMII)) {
> +			u32 *tmp_reg =3D (u32 *) ioremap(get_immrbase()
> +					+ 0x14A8, 0x4);
> +			u32 tmp_val =3D in_be32(tmp_reg);
> +			if (ucc_num =3D=3D 1)
> +				out_be32(tmp_reg, tmp_val | 0x00003000);
> +			else if (ucc_num =3D=3D 2)
> +				out_be32(tmp_reg, tmp_val | 0x0c000000);
> +			iounmap(tmp_reg);
> +		}
> +	=09
> +		if (phy->intrs[0].line !=3D 0)
> +			ugeth_data.board_flags |=3D
FSL_UGETH_BRD_HAS_PHY_INTR;
> +
> +		of_node_put(phy);
> +		of_node_put(mdio);
> +
> +		ret =3D
> +		    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;
> +}
> +
> +arch_initcall(ucc_geth_of_init);
> diff --git a/arch/powerpc/sysdev/qe_lib/qe_common.c b/arch/powerpc/=20
> sysdev/qe_lib/qe_common.c
> new file mode 100644
> index 0000000..cd0aca9
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/qe_common.c
> @@ -0,0 +1,401 @@
> +/*
> + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights =20
> reserved.
> + *
> + * Author: Shlomi Gridish <gridish@freescale.com>
> + *
> + * Description:
> + * General Purpose functions for the global management of the
> + * QUICC Engine (QE).
> + *
> + * Changelog:
> + * Jun 28, 2006	Li Yang <LeoLi@freescale.com>
> + * - Reorganized as qe_lib
> + * - Merged to powerpc arch; add device tree support
> + * - Style fixes
> + *
> + * This program is free software; you can redistribute  it and/or =20
> modify it
> + * under  the terms of  the GNU General  Public License as =20
> published by the
> + * Free Software Foundation;  either version 2 of the  License, or =20
> (at your
> + * option) any later version.
> + */
> +#undef USE_RHEAP
> +#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 <asm/irq.h>
> +#include <asm/page.h>
> +#include <asm/pgtable.h>
> +#include <asm/immap_qe.h>
> +#include <asm/qe.h>
> +#include <asm/prom.h>
> +#ifdef USE_RHEAP
> +#include <asm/rheap.h>
> +#else
> +#include "mm.h"
> +#endif				/* USE_RHEAP */
> +
> +/* QE snum state
> +*/
> +typedef enum qe_snum_state {
> +	QE_SNUM_STATE_USED,	/* used */
> +	QE_SNUM_STATE_FREE	/* free */
> +} qe_snum_state_e;
> +
> +/* QE snum
> +*/
> +typedef struct qe_snum {
> +	u8 num;			/* snum  */
> +	qe_snum_state_e state;	/* state */
> +} qe_snum_t;
> +
> +/* We allocate this here because it is used almost exclusively for
> + * the communication processor devices.
> + */
> +EXPORT_SYMBOL(qe_immr);
> +qe_map_t *qe_immr =3D NULL;
> +static qe_snum_t snums[QE_NUM_OF_SNUM];	/* Dynamically allocated

> SNUMs  */
> +
> +static void qe_snums_init(void);
> +static void qe_muram_init(void);
> +static int qe_sdma_init(void);
> +
> +void qe_reset(void)
> +{
> +	if (qe_immr =3D=3D NULL)
> +		qe_immr =3D (qe_map_t *) ioremap(get_qe_base(),
QE_IMMAP_SIZE);
> +
> +	qe_snums_init();
> +
> +	qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID,
> +		     (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
> +
> +	/* Reclaim the MURAM memory for our use. */
> +	qe_muram_init();
> +
> +#ifdef USE_RHEAP
> +	if (qe_sdma_init())
> +		panic("sdma init failed!");
> +#endif				/* USE_RHEAP */
> +}
> +
> +EXPORT_SYMBOL(qe_issue_cmd);
> +int qe_issue_cmd(uint cmd, uint device, u8 mcn_protocol, u32 =20
> cmd_input)
> +{
> +	unsigned long flags;
> +	u32 cecr;
> +	u8 mcn_shift =3D 0, dev_shift =3D 0;
> +
> +	local_irq_save(flags);
> +	if (cmd =3D=3D QE_RESET) {
> +		out_be32(&qe_immr->cp.cecr, (u32) (cmd | QE_CR_FLG));
> +	} else {
> +		if (cmd =3D=3D QE_ASSIGN_PAGE) {
> +			/* Here device is the SNUM, not sub-block */
> +			dev_shift =3D QE_CR_SNUM_SHIFT;
> +		} else if (cmd =3D=3D QE_ASSIGN_RISC) {
> +			/* Here device is the SNUM, and mcnProtocol is
> +			 * e_QeCmdRiscAssignment value */
> +			dev_shift =3D QE_CR_SNUM_SHIFT;
> +			mcn_shift =3D QE_CR_MCN_RISC_ASSIGN_SHIFT;
> +		} else {
> +			if (device =3D=3D QE_CR_SUBBLOCK_USB)
> +				mcn_shift =3D QE_CR_MCN_USB_SHIFT;
> +			else
> +				mcn_shift =3D QE_CR_MCN_NORMAL_SHIFT;
> +		}
> +
> +		out_be32(&qe_immr->cp.cecdr,
> +			 immrbar_virt_to_phys((void *)cmd_input));
> +		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 =3D in_be32(&qe_immr->cp.cecr);
> +	} while (cecr & QE_CR_FLG);
> +	local_irq_restore(flags);
> +
> +	return 0;
> +}
> +
> +/* 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 =3D 0;
> +
> +unsigned int get_brg_clk(void)
> +{
> +	struct device_node *qe;
> +	if (brg_clk)
> +		return brg_clk;
> +
> +	qe =3D of_find_node_by_type(NULL, "qe");
> +	if (qe) {
> +		unsigned int size;
> +		u32 *prop =3D (u32 *) get_property(qe, "brg-frequency",
&size);
> +		brg_clk =3D *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(uint brg, uint rate)
> +{
> +	volatile uint *bp;
> +	u32 divisor;
> +	int div16 =3D 0;
> +
> +	bp =3D (uint *) & qe_immr->brg.brgc1;
> +	bp +=3D brg;
> +
> +	divisor =3D (get_brg_clk() / rate);
> +	if (divisor > QE_BRGC_DIVISOR_MAX + 1) {
> +		div16 =3D 1;
> +		divisor /=3D 16;
> +	}
> +
> +	*bp =3D ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) | QE_BRGC_ENABLE;
> +	if (div16)
> +		*bp |=3D QE_BRGC_DIV16;
> +}
> +
> +static void qe_snums_init(void)
> +{
> +	int i;
> +
> +	/* Initialize the SNUMs array. */
> +	for (i =3D 0; i < QE_NUM_OF_SNUM; i++)
> +		snums[i].state =3D QE_SNUM_STATE_FREE;
> +
> +	/* Initialize SNUMs (thread serial numbers) according to QE
> +	 * spec chapter 4, SNUM table */
> +	i =3D 0;
> +	snums[i++].num =3D 0x04;
> +	snums[i++].num =3D 0x05;
> +	snums[i++].num =3D 0x0C;
> +	snums[i++].num =3D 0x0D;
> +	snums[i++].num =3D 0x14;
> +	snums[i++].num =3D 0x15;
> +	snums[i++].num =3D 0x1C;
> +	snums[i++].num =3D 0x1D;
> +	snums[i++].num =3D 0x24;
> +	snums[i++].num =3D 0x25;
> +	snums[i++].num =3D 0x2C;
> +	snums[i++].num =3D 0x2D;
> +	snums[i++].num =3D 0x34;
> +	snums[i++].num =3D 0x35;
> +	snums[i++].num =3D 0x88;
> +	snums[i++].num =3D 0x89;
> +	snums[i++].num =3D 0x98;
> +	snums[i++].num =3D 0x99;
> +	snums[i++].num =3D 0xA8;
> +	snums[i++].num =3D 0xA9;
> +	snums[i++].num =3D 0xB8;
> +	snums[i++].num =3D 0xB9;
> +	snums[i++].num =3D 0xC8;
> +	snums[i++].num =3D 0xC9;
> +	snums[i++].num =3D 0xD8;
> +	snums[i++].num =3D 0xD9;
> +	snums[i++].num =3D 0xE8;
> +	snums[i++].num =3D 0xE9;
> +}
> +
> +int qe_get_snum(void)
> +{
> +	unsigned long flags;
> +	int snum =3D -EBUSY;
> +	int i;
> +
> +	local_irq_save(flags);
> +	for (i =3D 0; i < QE_NUM_OF_SNUM; i++) {
> +		if (snums[i].state =3D=3D QE_SNUM_STATE_FREE) {
> +			snums[i].state =3D QE_SNUM_STATE_USED;
> +			snum =3D snums[i].num;
> +			break;
> +		}
> +	}
> +	local_irq_restore(flags);
> +
> +	return snum;
> +}
> +
> +EXPORT_SYMBOL(qe_get_snum);
> +
> +void qe_put_snum(u8 snum)
> +{
> +	int i;
> +
> +	for (i =3D 0; i < QE_NUM_OF_SNUM; i++) {
> +		if (snums[i].num =3D=3D snum) {
> +			snums[i].state =3D QE_SNUM_STATE_FREE;
> +			break;
> +		}
> +	}
> +}
> +
> +EXPORT_SYMBOL(qe_put_snum);
> +
> +static int qe_sdma_init(void)
> +{
> +	sdma_t *sdma =3D &qe_immr->sdma;
> +	uint sdma_buf_offset;
> +
> +	if (!sdma)
> +		return -ENODEV;
> +
> +	/* allocate 2 internal temporary buffers (512 bytes size each)
for
> +	 * the SDMA */
> +	sdma_buf_offset =3D 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 spinlock_t qe_muram_lock;
> +#ifdef USE_RHEAP
> +/* 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;
> +#else
> +static void *mm =3D NULL;
> +#endif				/* USE_RHEAP */
> +
> +static void qe_muram_init(void)
> +{
> +	spin_lock_init(&qe_muram_lock);
> +
> +#ifdef USE_RHEAP
> +	/* 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 actually crap. QE_DATAONLY_BASE and
> +	 * QE_DATAONLY_SIZE is only a subset of the available muram. It
> +	 * varies with the processor and the microcode patches
activated.
> +	 * But the following should be at least safe.
> +	 */
> +	rh_attach_region(&qe_muram_info,
> +			 (void *)QE_MURAM_DATAONLY_BASE,
> +			 QE_MURAM_DATAONLY_SIZE);
> +#else
> +#endif				/* USE_RHEAP */
> +}
> +
> +/* This function returns an index into the MURAM area.
> + */
> +uint qe_muram_alloc(uint size, uint align)
> +{
> +	void *start;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&qe_muram_lock, flags);
> +#ifdef USE_RHEAP
> +	qe_muram_info.alignment =3D align;
> +	start =3D rh_alloc(&qe_muram_info, size, "QE");
> +#else
> +	if (!mm) {
> +		mm_init(&mm, (u32)
qe_muram_addr(QE_MURAM_DATAONLY_BASE),
> +			QE_MURAM_DATAONLY_SIZE);
> +		if (qe_sdma_init())
> +			panic("sdma init failed!");
> +
> +	}
> +	start =3D mm_get(mm, (u32) size, (int)align, "QE");
> +	if (!IS_MURAM_ERR((u32) start))
> +		start =3D (void *)((u32) start - (u32) qe_immr->muram);
> +#endif				/* USE_RHEAP */
> +	spin_unlock_irqrestore(&qe_muram_lock, flags);
> +
> +	return (uint) start;
> +}
> +
> +EXPORT_SYMBOL(qe_muram_alloc);
> +
> +int qe_muram_free(uint offset)
> +{
> +	int ret;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&qe_muram_lock, flags);
> +#ifdef USE_RHEAP
> +	ret =3D rh_free(&qe_muram_info, (void *)offset);
> +#else
> +	ret =3D mm_put(mm, (u32) qe_muram_addr(offset));
> +#endif				/* USE_RHEAP */
> +	spin_unlock_irqrestore(&qe_muram_lock, flags);
> +
> +	return ret;
> +}
> +
> +EXPORT_SYMBOL(qe_muram_free);
> +
> +/* not sure if this is ever needed */
> +uint qe_muram_alloc_fixed(uint offset, uint size, uint align)
> +{
> +	void *start;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&qe_muram_lock, flags);
> +#ifdef USE_RHEAP
> +	qe_muram_info.alignment =3D align;
> +	start =3D
> +	    rh_alloc_fixed(&qe_muram_info, (void *)offset, size, =20
> "commproc");
> +#else
> +	start =3D mm_get_force(mm, (u32) offset, (u32) size, "QE");
> +	if (!IS_MURAM_ERR((u32) start))
> +		start =3D (void *)((u32) start - (u32) qe_immr->muram);
> +#endif				/* USE_RHEAP */
> +	spin_unlock_irqrestore(&qe_muram_lock, flags);
> +
> +	return (uint) start;
> +}
> +
> +EXPORT_SYMBOL(qe_muram_alloc_fixed);
> +
> +void qe_muram_dump(void)
> +{
> +#ifdef USE_RHEAP
> +	rh_dump(&qe_muram_info);
> +#else
> +	mm_dump(mm);
> +#endif				/* USE_RHEAP */
> +}
> +
> +EXPORT_SYMBOL(qe_muram_dump);
> +
> +void *qe_muram_addr(uint 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/=20
> sysdev/qe_lib/qe_ic.c
> new file mode 100644
> index 0000000..465630e
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/qe_ic.c
> @@ -0,0 +1,487 @@
> +/*
> + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights =20
> reserved.
> + *
> + * Author: Shlomi Gridish <gridish@freescale.com>
> + *
> + * Description:
> + * QE Interrupt Controller routines implementations.
> + *
> + * Changelog:
> + * Jun 28, 2006 Li Yang <LeoLi@freescale.com>
> + * - Reorganized as qe_lib
> + * - Merged to powerpc arch; add device tree support
> + * - Style fixes
> + *
> + * This program is free software; you can redistribute  it and/or =20
> modify it
> + * under  the terms of  the GNU General  Public License as =20
> published by the
> + * Free Software Foundation;  either version 2 of the  License, or =20
> (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/interrupt.h>
> +
> +#include <asm/irq.h>
> +#include <asm/io.h>
> +#include <asm/qe_ic.h>
> +
> +#include "qe_ic.h"
> +
> +static struct qe_ic_private p_qe_ic;
> +static struct qe_ic_private *primary_qe_ic;
> +
> +static struct qe_ic_info qe_ic_info[] =3D {
> +	[1] =3D {
> +	       .mask =3D 0x00008000,
> +	       .qimr =3D 1,
> +	       .pri_code =3D 0},
> +	[2] =3D {
> +	       .mask =3D 0x00004000,
> +	       .qimr =3D 1,
> +	       .pri_code =3D 1},
> +	[3] =3D {
> +	       .mask =3D 0x00002000,
> +	       .qimr =3D 1,
> +	       .pri_code =3D 2},
> +	[10] =3D {
> +		.mask =3D 0x00000040,
> +		.qimr =3D 1,
> +		.pri_code =3D 1},
> +	[11] =3D {
> +		.mask =3D 0x00000020,
> +		.qimr =3D 1,
> +		.pri_code =3D 2},
> +	[12] =3D {
> +		.mask =3D 0x00000010,
> +		.qimr =3D 1,
> +		.pri_code =3D 3},
> +	[13] =3D {
> +		.mask =3D 0x00000008,
> +		.qimr =3D 1,
> +		.pri_code =3D 4},
> +	[14] =3D {
> +		.mask =3D 0x00000004,
> +		.qimr =3D 1,
> +		.pri_code =3D 5},
> +	[15] =3D {
> +		.mask =3D 0x00000002,
> +		.qimr =3D 1,
> +		.pri_code =3D 6},
> +	[20] =3D {
> +		.mask =3D 0x10000000,
> +		.qimr =3D 0,
> +		.pri_code =3D 3},
> +	[25] =3D {
> +		.mask =3D 0x00800000,
> +		.qimr =3D 0,
> +		.pri_code =3D 0},
> +	[26] =3D {
> +		.mask =3D 0x00400000,
> +		.qimr =3D 0,
> +		.pri_code =3D 1},
> +	[27] =3D {
> +		.mask =3D 0x00200000,
> +		.qimr =3D 0,
> +		.pri_code =3D 2},
> +	[28] =3D {
> +		.mask =3D 0x00100000,
> +		.qimr =3D 0,
> +		.pri_code =3D 3},
> +	[32] =3D {
> +		.mask =3D 0x80000000,
> +		.qimr =3D 1,
> +		.pri_code =3D 0},
> +	[33] =3D {
> +		.mask =3D 0x40000000,
> +		.qimr =3D 1,
> +		.pri_code =3D 1},
> +	[34] =3D {
> +		.mask =3D 0x20000000,
> +		.qimr =3D 1,
> +		.pri_code =3D 2},
> +	[35] =3D {
> +		.mask =3D 0x10000000,
> +		.qimr =3D 1,
> +		.pri_code =3D 3},
> +	[36] =3D {
> +		.mask =3D 0x08000000,
> +		.qimr =3D 1,
> +		.pri_code =3D 4},
> +	[40] =3D {
> +		.mask =3D 0x00800000,
> +		.qimr =3D 1,
> +		.pri_code =3D 0},
> +	[41] =3D {
> +		.mask =3D 0x00400000,
> +		.qimr =3D 1,
> +		.pri_code =3D 1},
> +	[42] =3D {
> +		.mask =3D 0x00200000,
> +		.qimr =3D 1,
> +		.pri_code =3D 2},
> +	[43] =3D {
> +		.mask =3D 0x00100000,
> +		.qimr =3D 1,
> +		.pri_code =3D 3},
> +};
> +
> +struct hw_interrupt_type qe_ic =3D {
> +	.typename =3D "QE IC",
> +	.enable =3D qe_ic_enable_irq,
> +	.disable =3D qe_ic_disable_irq,
> +	.ack =3D qe_ic_disable_irq_and_ack,
> +	.end =3D qe_ic_end_irq,
> +};
> +
> +static int qe_ic_get_low_irq(struct pt_regs *regs)
> +{
> +	struct qe_ic_private *p_qe_ic =3D primary_qe_ic;
> +	int irq =3D -1;
> +
> +	/* get the low byte of SIVEC to get the interrupt source vector.
*/
> +	irq =3D (in_be32(&p_qe_ic->regs->qivec) >> 24) >> 2;
> +
> +	if (irq =3D=3D 0)		/* 0 --> no irq is pending */
> +		return -1;
> +
> +	return irq + p_qe_ic->irq_offset;
> +}
> +
> +static int qe_ic_get_high_irq(struct pt_regs *regs)
> +{
> +	struct qe_ic_private *p_qe_ic =3D primary_qe_ic;
> +	int irq =3D -1;
> +
> +	/* get the high byte of SIVEC to get the interrupt source
vector. */
> +	irq =3D (in_be32(&p_qe_ic->regs->qhivec) >> 24) >> 2;
> +
> +	if (irq =3D=3D 0)		/* 0 --> no irq is pending */
> +		return -1;
> +
> +	return irq + p_qe_ic->irq_offset;
> +}
> +
> +static irqreturn_t qe_ic_cascade_low(int irq, void *dev_id,
> +				     struct pt_regs *regs)
> +{
> +	while ((irq =3D qe_ic_get_low_irq(regs)) >=3D 0)
> +		__do_IRQ(irq, regs);
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t qe_ic_cascade_high(int irq, void *dev_id,
> +				      struct pt_regs *regs)
> +{
> +	while ((irq =3D qe_ic_get_high_irq(regs)) >=3D 0)
> +		__do_IRQ(irq, regs);
> +	return IRQ_HANDLED;
> +}
> +
> +static struct irqaction qe_ic_low_irqaction =3D {
> +	.handler =3D qe_ic_cascade_low,
> +	.flags =3D SA_INTERRUPT,
> +	.mask =3D CPU_MASK_NONE,
> +	.name =3D "qe_ic_cascade_low",
> +};
> +
> +static struct irqaction qe_ic_high_irqaction =3D {
> +	.handler =3D qe_ic_cascade_high,
> +	.flags =3D SA_INTERRUPT,
> +	.mask =3D CPU_MASK_NONE,
> +	.name =3D "qe_ic_cascade_high",
> +};
> +
> +int qe_ic_init(phys_addr_t phys_addr,
> +	       unsigned int flags, unsigned int irq_offset)
> +{
> +	struct qe_ic_map *regs;
> +	u8 grp, pri, shift =3D 0;
> +	u32 tmp_qicr =3D 0, tmp_qricr =3D 0, tmp_qicnr =3D 0, tmp_mask;
> +	int i, high_hctive =3D 0;
> +	const u32 high_signal =3D 2;
> +
> +	primary_qe_ic =3D &p_qe_ic;
> +	memset(primary_qe_ic, 0, sizeof(struct qe_ic_private));
> +
> +	/* initialize QE interrupt controller registers */
> +	primary_qe_ic->regs =3D regs =3D
> +	    (struct qe_ic_map *)ioremap(phys_addr, QE_IC_SIZE);
> +	primary_qe_ic->irq_offset =3D irq_offset;
> +
> +	/* default priority scheme is grouped. If spread mode is    */
> +	/* required, configure sicr accordingly.                    */
> +	if (flags & QE_IC_SPREADMODE_GRP_W)
> +		tmp_qicr |=3D QICR_GWCC;
> +	if (flags & QE_IC_SPREADMODE_GRP_X)
> +		tmp_qicr |=3D QICR_GXCC;
> +	if (flags & QE_IC_SPREADMODE_GRP_Y)
> +		tmp_qicr |=3D QICR_GYCC;
> +	if (flags & QE_IC_SPREADMODE_GRP_Z)
> +		tmp_qicr |=3D QICR_GZCC;
> +	if (flags & QE_IC_SPREADMODE_GRP_RISCA)
> +		tmp_qicr |=3D QICR_GRTA;
> +	if (flags & QE_IC_SPREADMODE_GRP_RISCB)
> +		tmp_qicr |=3D QICR_GRTB;
> +
> +	/* choose destination signal for highest priority interrupt */
> +	if (flags & QE_IC_HIGH_SIGNAL) {
> +		tmp_qicr |=3D (high_signal << QICR_HPIT_SHIFT);
> +		high_hctive =3D 1;
> +	}
> +
> +	out_be32(&regs->qicr, tmp_qicr);
> +
> +	tmp_mask =3D (1 << QE_IC_GRP_W_DEST_SIGNAL_SHIFT);
> +	/* choose destination signal for highest priority interrupt in
each
> +	 * group */
> +	for (grp =3D 0; grp < NUM_OF_QE_IC_GROUPS; grp++) {
> +		/* the first 2 priorities in each group have a choice of
> +		 * destination signal */
> +		for (pri =3D 0; pri <=3D 1; pri++) {
> +			if (flags & ((tmp_mask << (grp << 1)) << pri)) {
> +				/* indicate whether QE High signal is
> +				 * required */
> +				if (!high_hctive)
> +					high_hctive =3D 1;
> +
> +				/* The location of the bits relevant to
> +				 * priority 0 in the   */
> +				/* registers is always 2 bits left
comparing
> +				 * to priority 1. */
> +				if (pri =3D=3D 0)
> +					shift =3D 2;
> +
> +				switch (grp) {
> +				case (QE_IC_GRP_W):
> +					shift +=3D QICNR_WCC1T_SHIFT;
> +					tmp_qicnr |=3D high_signal <<
shift;
> +					break;
> +				case (QE_IC_GRP_X):
> +					shift +=3D QICNR_XCC1T_SHIFT;
> +					tmp_qicnr |=3D high_signal <<
shift;
> +					break;
> +				case (QE_IC_GRP_Y):
> +					shift +=3D QICNR_YCC1T_SHIFT;
> +					tmp_qicnr |=3D high_signal <<
shift;
> +					break;
> +				case (QE_IC_GRP_Z):
> +					shift +=3D QICNR_ZCC1T_SHIFT;
> +					tmp_qicnr |=3D high_signal <<
shift;
> +					break;
> +				case (QE_IC_GRP_RISCA):
> +					shift +=3D QRICR_RTA1T_SHIFT;
> +					tmp_qricr |=3D high_signal <<
shift;
> +					break;
> +				case (QE_IC_GRP_RISCB):
> +					shift +=3D QRICR_RTB1T_SHIFT;
> +					tmp_qricr |=3D high_signal <<
shift;
> +					break;
> +				default:
> +					break;
> +				}
> +			}
> +		}
> +	}
> +
> +	if (tmp_qicnr)
> +		out_be32(&regs->qicnr, tmp_qicnr);
> +	if (tmp_qricr)
> +		out_be32(&regs->qricr, tmp_qricr);
> +
> +	for (i =3D primary_qe_ic->irq_offset;
> +	     i < (NR_QE_IC_INTS + primary_qe_ic->irq_offset); i++) {
> +		irq_desc[i].handler =3D &qe_ic;
> +		irq_desc[i].status =3D IRQ_LEVEL;
> +	}
> +
> +	/* register QE_IC interrupt controller in the a higher hirarchy
> +	 * controller */
> +	setup_irq(IRQ_QE_LOW, &qe_ic_low_irqaction);
> +
> +	if (high_hctive)
> +		/* register QE_IC high interrupt source in the higher
> +		 * hirarchy controller */
> +		setup_irq(IRQ_QE_HIGH, &qe_ic_high_irqaction);
> +
> +	printk("QE IC (%d IRQ sources) at %p\n", NR_QE_IC_INTS,
> +	       primary_qe_ic->regs);
> +	return 0;
> +}
> +
> +void qe_ic_free(void)
> +{
> +}
> +
> +void qe_ic_enable_irq(unsigned int irq)
> +{
> +	struct qe_ic_private *p_qe_ic =3D primary_qe_ic;
> +	unsigned int src =3D irq - p_qe_ic->irq_offset;
> +	u32 qimr;
> +
> +	if (qe_ic_info[src].qimr) {
> +		qimr =3D in_be32(&((struct qe_ic_private
*)p_qe_ic)->regs->qimr);
> +		out_be32(&((struct qe_ic_private *)p_qe_ic)->regs->qimr,
> +			 qimr | (qe_ic_info[src].mask));
> +	} else {
> +		qimr =3D in_be32(&((struct qe_ic_private
*)p_qe_ic)->regs->qrimr);
> +		out_be32(&((struct qe_ic_private
*)p_qe_ic)->regs->qrimr,
> +			 qimr | (qe_ic_info[src].mask));
> +	}
> +}
> +
> +void qe_ic_disable_irq(unsigned int irq)
> +{
> +	struct qe_ic_private *p_qe_ic =3D primary_qe_ic;
> +	unsigned int src =3D irq - p_qe_ic->irq_offset;
> +	u32 qimr;
> +
> +	if (qe_ic_info[src].qimr) {
> +		qimr =3D in_be32(&((struct qe_ic_private
*)p_qe_ic)->regs->qimr);
> +		out_be32(&((struct qe_ic_private *)p_qe_ic)->regs->qimr,
> +			 qimr & ~(qe_ic_info[src].mask));
> +	} else {
> +		qimr =3D in_be32(&((struct qe_ic_private
*)p_qe_ic)->regs->qrimr);
> +		out_be32(&((struct qe_ic_private
*)p_qe_ic)->regs->qrimr,
> +			 qimr & ~(qe_ic_info[src].mask));
> +	}
> +}
> +
> +void qe_ic_disable_irq_and_ack(unsigned int irq)
> +{
> +	qe_ic_disable_irq(irq);
> +}
> +
> +void qe_ic_end_irq(unsigned int irq)
> +{
> +
> +	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))
> +	    && irq_desc[irq].action)
> +		qe_ic_enable_irq(irq);
> +}
> +
> +void qe_ic_modify_highest_priority(unsigned int irq)
> +{
> +	struct qe_ic_private *p_qe_ic =3D primary_qe_ic;
> +	unsigned int src =3D irq - p_qe_ic->irq_offset;
> +	u32 tmp_qicr =3D 0;
> +
> +	tmp_qicr =3D in_be32(&p_qe_ic->regs->qicr);
> +	out_be32(&p_qe_ic->regs->qicr, (u32) (tmp_qicr | ((u8) src <<
24)));
> +}
> +
> +void qe_ic_modify_priority(enum qe_ic_grp_id grp,
> +			   unsigned int pri0,
> +			   unsigned int pri1,
> +			   unsigned int pri2,
> +			   unsigned int pri3,
> +			   unsigned int pri4,
> +			   unsigned int pri5,
> +			   unsigned int pri6, unsigned int pri7)
> +{
> +	struct qe_ic_private *p_qe_ic =3D primary_qe_ic;
> +	volatile u32 *p_qip =3D 0;
> +	u32 tmp_qip =3D 0;
> +	u8 tmp_array[8] =3D { 0, 0, 0, 0, 0, 0, 0, 0 };
> +	signed char code_array[8], i =3D 0, j =3D 0;
> +
> +	code_array[0] =3D (signed char)(pri0 ? qe_ic_info[pri0].pri_code :

> -1);
> +	code_array[1] =3D (signed char)(pri1 ? qe_ic_info[pri1].pri_code :

> -1);
> +	code_array[2] =3D (signed char)(pri2 ? qe_ic_info[pri2].pri_code :

> -1);
> +	code_array[3] =3D (signed char)(pri3 ? qe_ic_info[pri3].pri_code :

> -1);
> +	code_array[4] =3D (signed char)(pri4 ? qe_ic_info[pri4].pri_code :

> -1);
> +	code_array[5] =3D (signed char)(pri5 ? qe_ic_info[pri5].pri_code :

> -1);
> +	code_array[6] =3D (signed char)(pri6 ? qe_ic_info[pri6].pri_code :

> -1);
> +	code_array[7] =3D (signed char)(pri7 ? qe_ic_info[pri7].pri_code :

> -1);
> +
> +	for (i =3D 0; i < 8; i++) {
> +		if (code_array[i] =3D=3D -1)
> +			break;
> +		tmp_array[code_array[i]] =3D 1;
> +	}
> +
> +	for (; i < 8; i++) {
> +		while (tmp_array[j] && j < 8)
> +			j++;
> +		code_array[i] =3D j;
> +		tmp_array[j] =3D 1;
> +	}
> +
> +	tmp_qip =3D (u32) (code_array[0] << QIPCC_SHIFT_PRI0 |
> +			 code_array[1] << QIPCC_SHIFT_PRI1 |
> +			 code_array[2] << QIPCC_SHIFT_PRI2 |
> +			 code_array[3] << QIPCC_SHIFT_PRI3 |
> +			 code_array[4] << QIPCC_SHIFT_PRI4 |
> +			 code_array[5] << QIPCC_SHIFT_PRI5 |
> +			 code_array[6] << QIPCC_SHIFT_PRI6 |
> +			 code_array[7] << QIPCC_SHIFT_PRI7);
> +
> +	switch (grp) {
> +	case (QE_IC_GRP_W):
> +		p_qip =3D &(p_qe_ic->regs->qipwcc);
> +		break;
> +	case (QE_IC_GRP_X):
> +		p_qip =3D &(p_qe_ic->regs->qipxcc);
> +		break;
> +	case (QE_IC_GRP_Y):
> +		p_qip =3D &(p_qe_ic->regs->qipycc);
> +		break;
> +	case (QE_IC_GRP_Z):
> +		p_qip =3D &(p_qe_ic->regs->qipzcc);
> +		break;
> +	case (QE_IC_GRP_RISCA):
> +		p_qip =3D &(p_qe_ic->regs->qiprta);
> +		break;
> +	case (QE_IC_GRP_RISCB):
> +		p_qip =3D &(p_qe_ic->regs->qiprtb);
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	out_be32(p_qip, tmp_qip);
> +}
> +
> +void qe_ic_dump_regs(void)
> +{
> +	struct qe_ic_private *p_qe_ic =3D primary_qe_ic;
> +
> +	printk(KERN_INFO "QE IC registars:\n");
> +	printk(KERN_INFO "Base address: 0x%08x\n", (u32) p_qe_ic->regs);
> +	printk(KERN_INFO "qicr  : addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & p_qe_ic->regs->qicr,
in_be32(&p_qe_ic->regs->qicr));
> +	printk(KERN_INFO "qivec : addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & p_qe_ic->regs->qivec, in_be32(&p_qe_ic->regs-=20
> >qivec));
> +	printk(KERN_INFO "qripnr: addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & p_qe_ic->regs->qripnr, in_be32(&p_qe_ic->regs-=20
> >qripnr));
> +	printk(KERN_INFO "qipnr : addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & p_qe_ic->regs->qipnr, in_be32(&p_qe_ic->regs-=20
> >qipnr));
> +	printk(KERN_INFO "qipxcc: addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & p_qe_ic->regs->qipxcc, in_be32(&p_qe_ic->regs-=20
> >qipxcc));
> +	printk(KERN_INFO "qipycc: addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & p_qe_ic->regs->qipycc, in_be32(&p_qe_ic->regs-=20
> >qipycc));
> +	printk(KERN_INFO "qipwcc: addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & p_qe_ic->regs->qipwcc, in_be32(&p_qe_ic->regs-=20
> >qipwcc));
> +	printk(KERN_INFO "qipzcc: addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & p_qe_ic->regs->qipzcc, in_be32(&p_qe_ic->regs-=20
> >qipzcc));
> +	printk(KERN_INFO "qimr  : addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & p_qe_ic->regs->qimr,
in_be32(&p_qe_ic->regs->qimr));
> +	printk(KERN_INFO "qrimr : addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & p_qe_ic->regs->qrimr, in_be32(&p_qe_ic->regs-=20
> >qrimr));
> +	printk(KERN_INFO "qicnr : addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & p_qe_ic->regs->qicnr, in_be32(&p_qe_ic->regs-=20
> >qicnr));
> +	printk(KERN_INFO "qiprta: addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & p_qe_ic->regs->qiprta, in_be32(&p_qe_ic->regs-=20
> >qiprta));
> +	printk(KERN_INFO "qiprtb: addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & p_qe_ic->regs->qiprtb, in_be32(&p_qe_ic->regs-=20
> >qiprtb));
> +	printk(KERN_INFO "qricr : addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & p_qe_ic->regs->qricr, in_be32(&p_qe_ic->regs-=20
> >qricr));
> +	printk(KERN_INFO "qhivec: addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & p_qe_ic->regs->qhivec, in_be32(&p_qe_ic->regs-=20
> >qhivec));
> +}
> diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.h b/arch/powerpc/=20
> sysdev/qe_lib/qe_ic.h
> new file mode 100644
> index 0000000..6662ad2
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/qe_ic.h
> @@ -0,0 +1,83 @@
> +/*
> + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights =20
> reserved.
> + *
> + * Author: Shlomi Gridish <gridish@freescale.com>
> + *
> + * Description:
> + * QE IC private definitions and structure.
> + *
> + * Changelog:
> + * Jun 21, 2006 Li Yang <LeoLi@freescale.com>
> + * - Style fix; port to powerpc arch
> + *
> + * This program is free software; you can redistribute  it and/or =20
> modify it
> + * under  the terms of  the GNU General  Public License as =20
> published by the
> + * Free Software Foundation;  either version 2 of the  License, or =20
> (at your
> + * option) any later version.
> + */
> +#ifndef __QE_IC_H__
> +#define __QE_IC_H__
> +
> +typedef struct qe_ic_map {
> +	volatile u32 qicr;
> +	volatile u32 qivec;
> +	volatile u32 qripnr;
> +	volatile u32 qipnr;
> +	volatile u32 qipxcc;
> +	volatile u32 qipycc;
> +	volatile u32 qipwcc;
> +	volatile u32 qipzcc;
> +	volatile u32 qimr;
> +	volatile u32 qrimr;
> +	volatile u32 qicnr;
> +	volatile u8 res0[0x4];
> +	volatile u32 qiprta;
> +	volatile u32 qiprtb;
> +	volatile u8 res1[0x4];
> +	volatile u32 qricr;
> +	volatile u8 res2[0x20];
> +	volatile u32 qhivec;
> +	volatile u8 res3[0x1C];
> +} __attribute__ ((packed)) qe_ic_map_t;
> +
> +
> +#define QE_IC_SIZE sizeof(struct qe_ic_map)
> +
> +/* Interrupt priority registers */
> +#define QIPCC_SHIFT_PRI0        29
> +#define QIPCC_SHIFT_PRI1        26
> +#define QIPCC_SHIFT_PRI2        23
> +#define QIPCC_SHIFT_PRI3        20
> +#define QIPCC_SHIFT_PRI4        13
> +#define QIPCC_SHIFT_PRI5        10
> +#define QIPCC_SHIFT_PRI6        7
> +#define QIPCC_SHIFT_PRI7        4
> +
> +/* QICR priority modes */
> +#define QICR_GWCC               0x00040000
> +#define QICR_GXCC               0x00020000
> +#define QICR_GYCC               0x00010000
> +#define QICR_GZCC               0x00080000
> +#define QICR_GRTA               0x00200000
> +#define QICR_GRTB               0x00400000
> +#define QICR_HPIT_SHIFT         8
> +
> +/* QICNR */
> +#define QICNR_WCC1T_SHIFT       20
> +#define QICNR_ZCC1T_SHIFT       28
> +#define QICNR_YCC1T_SHIFT       12
> +#define QICNR_XCC1T_SHIFT       4
> +
> +/* QRICR */
> +#define QRICR_RTA1T_SHIFT       20
> +#define QRICR_RTB1T_SHIFT       28
> +
> +struct qe_ic_private {
> +	struct qe_ic_map *regs;
> +	unsigned int irq_offset;
> +} qe_ic_private_t;
> +
> +extern struct hw_interrupt_type qe_ic;
> +extern int qe_ic_get_irq(struct pt_regs *regs);
> +
> +#endif				/* __QE_IC_H__ */
> diff --git a/arch/powerpc/sysdev/qe_lib/qe_io.c b/arch/powerpc/=20
> sysdev/qe_lib/qe_io.c
> new file mode 100644
> index 0000000..a943c27
> --- /dev/null
> +++ b/arch/powerpc/sysdev/qe_lib/qe_io.c
> @@ -0,0 +1,275 @@
> +/*
> + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights =20
> reserved.
> + *
> + * Author: Li Yang <LeoLi@freescale.com>
> + *
> + * Description:
> + * QE Parallel I/O ports configuration routines.  Based on code from
> + * Shlomi Gridish <gridish@freescale.com>
> + *
> + * Changelog:
> + * Jun 21, 2006	Initial version
> + *
> + * This program is free software; you can redistribute  it and/or =20
> modify it
> + * under  the terms of  the GNU General  Public License as =20
> published by the
> + * Free Software Foundation;  either version 2 of the  License, or =20
> (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 <asm/io.h>
> +#include <asm/prom.h>
> +#include <sysdev/fsl_soc.h>
> +#undef DEBUG
> +
> +#define NUM_OF_PINS     32
> +#define NUM_OF_PAR_IOS  7
> +
> +typedef struct par_io {
> +	struct {
> +		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 */
> +	} io_regs[NUM_OF_PAR_IOS];
> +} par_io_t;
> +
> +typedef struct qe_par_io {
> +	u8 res[0xc];
> +	u32 cepier;		/* QE ports interrupt event register */
> +	u32 cepimr;		/* QE ports mask event register */
> +	u32 cepicr;		/* QE ports control event register */
> +} qe_par_io_t;
> +
> +static int qe_irq_ports[NUM_OF_PAR_IOS][NUM_OF_PINS] =3D {
> +	/* 0-7 */          /* 8-15 */      /* 16 - 23 */     /* 24 - 31
*/
> +	{0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, =20
> 0,0,0,0,0,1,1,0},
> +	{0,0,0,1,0,1,0,0, 0,0,0,0,1,1,0,0, 0,0,0,0,0,0,0,0, =20
> 0,0,1,1,0,0,0,0},
> +	{0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, =20
> 0,0,0,1,1,1,0,0},
> +	{0,0,0,0,0,0,0,0, 0,0,0,0,1,1,0,0, 1,1,0,0,0,0,0,0, =20
> 0,0,1,1,0,0,0,0},
> +	{0,0,0,0,0,0,0,0, 0,0,0,0,1,1,0,0, 0,0,0,0,0,0,0,0, =20
> 1,1,1,1,0,0,0,1},
> +	{0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,1,0,0,0, =20
> 0,0,0,0,0,0,0,0},
> +	{0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,1}
> +};
> +
> +
> +static u8 get_irq_num(u8 port, u8 pin)
> +{
> +	int i, j;
> +	u8 num =3D 0;
> +
> +	if (qe_irq_ports[port][pin] =3D=3D 0)
> +		return -1;
> +	for (j =3D 0; j <=3D port; j++)
> +		for (i =3D 0; i < pin; i++)
> +			if (qe_irq_ports[j][i])
> +				num++;
> +	return num;
> +}
> +
> +static par_io_t *par_io =3D NULL;
> +static qe_par_io_t *qe_par_io =3D NULL;
> +
> +int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain,
> +		      int assignment, int has_irq)
> +{
> +	u32 pinMask1bit, pinMask2bits, newMask2bits, tmp_val;
> +
> +	if (!par_io) {
> +		par_io =3D (par_io_t *) ioremap(get_immrbase() + 0x1400,
> +					      sizeof(par_io_t));
> +		qe_par_io =3D (qe_par_io_t *) ioremap(get_immrbase() +
0xC00,
> +
sizeof(qe_par_io_t));
> +
> +		/* clear event bits in the event register of the QE
ports */
> +		out_be32(&qe_par_io->cepier, 0xFFFFFFFF);
> +	}
> +
> +	/* calculate pin location for single and 2 bits  information */
> +	pinMask1bit =3D (u32) (1 << (NUM_OF_PINS - (pin + 1)));
> +
> +	/* Set open drain, if required */
> +	tmp_val =3D in_be32(&par_io->io_regs[port].cpodr);
> +	if (open_drain)
> +		out_be32(&par_io->io_regs[port].cpodr, pinMask1bit |
tmp_val);
> +	else
> +		out_be32(&par_io->io_regs[port].cpodr, ~pinMask1bit &
tmp_val);
> +
> +	/* define direction */
> +	tmp_val =3D (pin > (NUM_OF_PINS / 2) - 1) ?
> +	    in_be32(&par_io->io_regs[port].cpdir2) :
> +	    in_be32(&par_io->io_regs[port].cpdir1);
> +
> +	/* get all bits mask for 2 bit per port */
> +	pinMask2bits =3D (u32) (0x3 <<
> +			      (NUM_OF_PINS -
> +			       (pin % (NUM_OF_PINS / 2) + 1) * 2));
> +
> +	/* Get the final mask we need for the right definition */
> +	newMask2bits =3D (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->io_regs[port].cpdir2,
> +			 ~pinMask2bits & tmp_val);
> +		tmp_val &=3D ~pinMask2bits;
> +		out_be32(&par_io->io_regs[port].cpdir2, newMask2bits |
tmp_val);
> +	} else {
> +		out_be32(&par_io->io_regs[port].cpdir1,
> +			 ~pinMask2bits & tmp_val);
> +		tmp_val &=3D ~pinMask2bits;
> +		out_be32(&par_io->io_regs[port].cpdir1, newMask2bits |
tmp_val);
> +	}
> +	/* define pin assignment */
> +	tmp_val =3D (pin > (NUM_OF_PINS / 2) - 1) ?
> +	    in_be32(&par_io->io_regs[port].cppar2) :
> +	    in_be32(&par_io->io_regs[port].cppar1);
> +
> +	newMask2bits =3D (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->io_regs[port].cppar2,
> +			 ~pinMask2bits & tmp_val);
> +		tmp_val &=3D ~pinMask2bits;
> +		out_be32(&par_io->io_regs[port].cppar2, newMask2bits |
tmp_val);
> +	} else {
> +		out_be32(&par_io->io_regs[port].cppar1,
> +			 ~pinMask2bits & tmp_val);
> +		tmp_val &=3D ~pinMask2bits;
> +		out_be32(&par_io->io_regs[port].cppar1, newMask2bits |
tmp_val);
> +	}
> +
> +	/* Set interrupt mask if the pin generates interrupt */
> +	if (has_irq) {
> +		int irq =3D get_irq_num(port, pin);
> +		u32 mask =3D 0;
> +
> +		if (irq =3D=3D -1) {
> +			printk(KERN_WARNING "Port %d, pin %d is can't be
"
> +					"interrupt\n", port, pin);
> +			return -EINVAL;
> +		}
> +		mask =3D 0x80000000 >> irq;
> +
> +		tmp_val =3D in_be32(&qe_par_io->cepimr);
> +		out_be32(&qe_par_io->cepimr, mask | 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 >=3D NUM_OF_PAR_IOS)
> +		return -EINVAL;
> +	if (pin >=3D NUM_OF_PINS)
> +		return -EINVAL;
> +	/* calculate pin location */
> +	pin_mask =3D (u32) (1 << (NUM_OF_PINS - 1 - pin));
> +
> +	tmp_val =3D in_be32(&par_io->io_regs[port].cpdata);
> +
> +	if (val =3D=3D 0)		/* clear  */
> +		out_be32(&par_io->io_regs[port].cpdata, ~pin_mask &
tmp_val);
> +	else			/* set */
> +		out_be32(&par_io->io_regs[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;
> +=09
> +	ph =3D (phandle *) get_property(np, "pio-handle", NULL);
> +	if (ph =3D=3D 0) {
> +		printk(KERN_ERR "pio-handle not available \n");
> +		return -1;
> +	}
> +	=09
> +	pio =3D of_find_node_by_phandle(*ph);
> +
> +	pio_map =3D (unsigned int *)
> +		get_property(pio, "pio-map", &pio_map_len);
> +	if (pio_map =3D=3D NULL) {
> +		printk(KERN_ERR "pio-map is not set! \n");
> +		return -1;
> +	}
> +	pio_map_len /=3D sizeof(unsigned int);
> +	if ((pio_map_len % 6) !=3D 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 +=3D 6;
> +		pio_map_len -=3D 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 =3D 0; i < NUM_OF_PAR_IOS; i++) {
> +		printk(KERN_INFO "cpodr[%d] : addr - 0x%08x, val -
0x%08x\n",
> +		       i, (u32) & par_io->io_regs[i].cpodr,
> +		       in_be32(&par_io->io_regs[i].cpodr));
> +		printk(KERN_INFO "cpdata[%d]: addr - 0x%08x, val -
0x%08x\n",
> +		       i, (u32) & par_io->io_regs[i].cpdata,
> +		       in_be32(&par_io->io_regs[i].cpdata));
> +		printk(KERN_INFO "cpdir1[%d]: addr - 0x%08x, val -
0x%08x\n",
> +		       i, (u32) & par_io->io_regs[i].cpdir1,
> +		       in_be32(&par_io->io_regs[i].cpdir1));
> +		printk(KERN_INFO "cpdir2[%d]: addr - 0x%08x, val -
0x%08x\n",
> +		       i, (u32) & par_io->io_regs[i].cpdir2,
> +		       in_be32(&par_io->io_regs[i].cpdir2));
> +		printk(KERN_INFO "cppar1[%d]: addr - 0x%08x, val -
0x%08x\n",
> +		       i, (u32) & par_io->io_regs[i].cppar1,
> +		       in_be32(&par_io->io_regs[i].cppar1));
> +		printk(KERN_INFO "cppar2[%d]: addr - 0x%08x, val -
0x%08x\n",
> +		       i, (u32) & par_io->io_regs[i].cppar2,
> +		       in_be32(&par_io->io_regs[i].cppar2));
> +	}
> +
> +	printk(KERN_INFO "QE PAR IO registars:\n");
> +	printk(KERN_INFO "Base address: 0x%08x\n", (u32) qe_par_io);
> +	printk(KERN_INFO "cepier : addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & qe_par_io->cepier, in_be32(&qe_par_io->cepier));
> +	printk(KERN_INFO "cepimr : addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & qe_par_io->cepimr, in_be32(&qe_par_io->cepimr));
> +	printk(KERN_INFO "cepicr : addr - 0x%08x, val - 0x%08x\n",
> +	       (u32) & qe_par_io->cepicr, in_be32(&qe_par_io->cepicr));
> +}
> +
> +EXPORT_SYMBOL(dump_par_io);
> +#endif
> -
> To unsubscribe from this list: send the line "unsubscribe linux-=20
> kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

^ permalink raw reply	[flat|nested] 6+ messages in thread
* RE: [PATCH 3/7] powerpc: Add QE library qe_lib--common files
@ 2006-06-29 17:14 Gridish Shlomi-RM96313
  0 siblings, 0 replies; 6+ messages in thread
From: Gridish Shlomi-RM96313 @ 2006-06-29 17:14 UTC (permalink / raw)
  To: Kumar Gala
  Cc: Phillips Kim-R1AAHA, linux-kernel, linuxppc-dev, Paul Mackerras,
	Chu hanjin-r52514

Yes, I agree.
Note that the code is already has 'USE_RHEAP' flag so it can be used
instead of the MM when the RHEAP will be fixed.
Shlomi
=20

-----Original Message-----
From: Kumar Gala [mailto:galak@kernel.crashing.org]=20
Sent: Thursday, June 29, 2006 19:33
To: Gridish Shlomi-RM96313
Cc: Li Yang-r58472; Paul Mackerras; linuxppc-dev@ozlabs.org;
linux-kernel@vger.kernel.org; Chu hanjin-r52514; Phillips Kim-R1AAHA
Subject: Re: [PATCH 3/7] powerpc: Add QE library qe_lib--common files


On Jun 29, 2006, at 11:06 AM, Gridish Shlomi-RM96313 wrote:

> Hi all,
>
> The reason why I chose  to work with MM and not with RHEAP is that,
> apparently, there is a BUG in the RHEAP module,
> it is not allocating correctly blocks that require alignments grater
> than 8.
> The usage of MM is only a temporary solution till RHEAP is fixed.
>
> Regards,
> Shlomi

Will fix rheap than.  We aren't going to merge in QE support with a =20
new memory allocator.

- kumar

> -----Original Message-----
> From: Kumar Gala [mailto:galak@kernel.crashing.org]
> Sent: Wednesday, June 28, 2006 22:20
> To: Li Yang-r58472
> Cc: 'Paul Mackerras'; linuxppc-dev@ozlabs.org;
> 'linux-kernel@vger.kernel.org'; Chu hanjin-r52514; Gridish
> Shlomi-RM96313; Phillips Kim-R1AAHA
> Subject: Re: [PATCH 3/7] powerpc: Add QE library qe_lib--common files
>
> Nack, remove the mm allocation code and just use rheap.
>
> - k
>
> On Jun 28, 2006, at 9:23 AM, Li Yang-r58472 wrote:
>
>>
>> Signed-off-by: Shlomi Gridish <gridish@freescale.com>
>> Signed-off-by: Li Yang <leoli@freescale.com>
>> Signed-off-by: Kim Phillips <kim.phillips@freescale.com>
>>
>> ---
>>  arch/powerpc/Kconfig                   |   12
>>  arch/powerpc/sysdev/Makefile           |    1
>>  arch/powerpc/sysdev/ipic.c             |    2
>>  arch/powerpc/sysdev/qe_lib/Kconfig     |  315 +++++++++++++
>>  arch/powerpc/sysdev/qe_lib/Makefile    |    8
>>  arch/powerpc/sysdev/qe_lib/mm.c        |  770 +++++++++++++++++++++
>> +++++++++++
>>  arch/powerpc/sysdev/qe_lib/mm.h        |    6
>>  arch/powerpc/sysdev/qe_lib/qe.c        |  181 ++++++++
>>  arch/powerpc/sysdev/qe_lib/qe_common.c |  401 +++++++++++++++++
>>  arch/powerpc/sysdev/qe_lib/qe_ic.c     |  487 ++++++++++++++++++++
>>  arch/powerpc/sysdev/qe_lib/qe_ic.h     |   83 +++
>>  arch/powerpc/sysdev/qe_lib/qe_io.c     |  275 +++++++++++
>>  12 files changed, 2541 insertions(+), 0 deletions(-)
>>
>> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
>> index 6729c98..6d4fc0b 100644
>> --- a/arch/powerpc/Kconfig
>> +++ b/arch/powerpc/Kconfig
>> @@ -334,6 +334,16 @@ config APUS
>>  	  <http://linux-apus.sourceforge.net/>.
>>  endchoice
>>
>> +config QUICC_ENGINE
>> +	bool
>> +	depends on MPC836x
>> +	default y
>> +	help
>> +	  The QE(QUICC Engine) is a new generation of coprocessor on
>> +	  Freescale embedded CPUs(like CPM  in older chips).  Selecting
>> +	  this option means that you wish to build a kernel for a
> machine
>> +	  with QE coprocessor on it.
>> +
>>  config PPC_PSERIES
>>  	depends on PPC_MULTIPLATFORM && PPC64
>>  	bool "  IBM pSeries & new (POWER5-based) iSeries"
>> @@ -993,6 +1003,8 @@ # XXX source "arch/ppc/8xx_io/Kconfig"
>>
>>  # XXX source "arch/ppc/8260_io/Kconfig"
>>
>> +source "arch/powerpc/sysdev/qe_lib/Kconfig"
>> +
>>  source "arch/powerpc/platforms/iseries/Kconfig"
>>
>>  source "lib/Kconfig"
>> diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/
>> Makefile
>> index 4c2b356..cd1d5cc 100644
>> --- a/arch/powerpc/sysdev/Makefile
>> +++ b/arch/powerpc/sysdev/Makefile
>> @@ -8,3 +8,4 @@ obj-$(CONFIG_U3_DART)		+=3D dart_iommu.o
>>  obj-$(CONFIG_MMIO_NVRAM)	+=3D mmio_nvram.o
>>  obj-$(CONFIG_PPC_83xx)		+=3D ipic.o
>>  obj-$(CONFIG_FSL_SOC)		+=3D fsl_soc.o
>> +obj-$(CONFIG_QUICC_ENGINE)	+=3D qe_lib/
>> diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c
>> index 8f01e0f..dbeccba 100644
>> --- a/arch/powerpc/sysdev/ipic.c
>> +++ b/arch/powerpc/sysdev/ipic.c
>> @@ -537,12 +537,14 @@ void ipic_set_highest_priority(unsigned
>>
>>  void ipic_set_default_priority(void)
>>  {
>> +#ifdef CONFIG_MPC834x
>>  	ipic_set_priority(MPC83xx_IRQ_TSEC1_TX, 0);
>>  	ipic_set_priority(MPC83xx_IRQ_TSEC1_RX, 1);
>>  	ipic_set_priority(MPC83xx_IRQ_TSEC1_ERROR, 2);
>>  	ipic_set_priority(MPC83xx_IRQ_TSEC2_TX, 3);
>>  	ipic_set_priority(MPC83xx_IRQ_TSEC2_RX, 4);
>>  	ipic_set_priority(MPC83xx_IRQ_TSEC2_ERROR, 5);
>> +#endif
>>  	ipic_set_priority(MPC83xx_IRQ_USB2_DR, 6);
>>  	ipic_set_priority(MPC83xx_IRQ_USB2_MPH, 7);
>>
>> diff --git a/arch/powerpc/sysdev/qe_lib/Kconfig b/arch/powerpc/
>> sysdev/qe_lib/Kconfig
>> new file mode 100644
>> index 0000000..6105237
>> --- /dev/null
>> +++ b/arch/powerpc/sysdev/qe_lib/Kconfig
>> @@ -0,0 +1,315 @@
>> +#
>> +# QE Communication options
>> +#
>> +
>> +menu "QE Options"
>> +	depends on QUICC_ENGINE
>> +=09
>> +config UCC1
>> +	bool "Enable QE UCC1"
>> +
>> +choice
>> +	prompt "UCC1 speed selection"
>> +	depends on UCC1
>> +	default UCC1_SLOW
>> +
>> +	config UCC1_SLOW
>> +		bool "UCC1 is slow"
>> +	config UCC1_FAST
>> +		bool "UCC1 is fast"
>> +endchoice
>> +
>> +menu "UCC1 Protocols options"
>> +	depends on UCC1
>> +
>> +	choice
>> +		prompt "UCC1 Slow Protocols selection"
>> +		depends on UCC1_SLOW
>> +		default UCC1_UART
>> +
>> +		config UCC1_UART
>> +			bool "UCC1 is UART"
>> +	endchoice
>> +
>> +	choice
>> +		prompt "UCC1 Fast Protocols selection"
>> +		depends on UCC1_FAST
>> +		default UCC1_GETH
>> +
>> +		config UCC1_GETH
>> +			bool "UCC1 is GETH"
>> +	endchoice
>> +endmenu
>> +
>> +config UCC2
>> +	bool "Enable QE UCC2"
>> +
>> +choice
>> +	prompt "UCC2 speed selection"
>> +	depends on UCC2
>> +	default UCC2_SLOW
>> +
>> +	config UCC2_SLOW
>> +		bool "UCC2 is slow"
>> +	config UCC2_FAST
>> +		bool "UCC2 is fast"
>> +endchoice
>> +
>> +menu "UCC2 Protocols options"
>> +	depends on UCC2
>> +
>> +	choice
>> +		prompt "UCC2 Slow Protocols selection"
>> +		depends on UCC2_SLOW
>> +		default UCC2_UART
>> +
>> +		config UCC2_UART
>> +			bool "UCC2 is UART"
>> +	endchoice
>> +
>> +	choice
>> +		prompt "UCC2 Fast Protocols selection"
>> +		depends on UCC2_FAST
>> +		default UCC2_GETH
>> +
>> +		config UCC2_GETH
>> +			bool "UCC2 is GETH"
>> +	endchoice
>> +endmenu
>> +
>> +config UCC3
>> +	bool "Enable QE UCC3"
>> +
>> +choice
>> +	prompt "UCC3 speed selection"
>> +	depends on UCC3
>> +	default UCC3_SLOW
>> +
>> +	config UCC3_SLOW
>> +		bool "UCC3 is slow"
>> +	config UCC3_FAST
>> +		bool "UCC3 is fast"
>> +endchoice
>> +
>> +menu "UCC3 Protocols options"
>> +	depends on UCC3
>> +
>> +	choice
>> +		prompt "UCC3 Slow Protocols selection"
>> +		depends on UCC3_SLOW
>> +		default UCC3_UART
>> +
>> +		config UCC3_UART
>> +			bool "UCC3 is UART"
>> +        endchoice
>> +
>> +	config UCC3_GETH
>> +		depends on UCC3_FAST
>> +		bool "UCC3 is GETH"
>> +
>> +	config UCC3_ATM
>> +		depends on UCC3_FAST && !UCC3_GETH
>> +		tristate "UCC3 is ATM"
>> +endmenu
>> +
>> +config UCC4
>> +	bool "Enable QE UCC4"
>> +
>> +choice
>> +	prompt "UCC4 speed selection"
>> +	depends on UCC4
>> +	default UCC4_SLOW
>> +
>> +	config UCC4_SLOW
>> +		bool "UCC4 is slow"
>> +	config UCC4_FAST
>> +		bool "UCC4 is fast"
>> +endchoice
>> +
>> +menu "UCC4 Protocols options"
>> +	depends on UCC4
>> +
>> +	choice
>> +		prompt "UCC4 Slow Protocols selection"
>> +		depends on UCC4_SLOW
>> +		default UCC4_UART
>> +
>> +		config UCC4_UART
>> +			bool "UCC4 is UART"
>> +	endchoice
>> +
>> +	choice
>> +		prompt "UCC4 Fast Protocols selection"
>> +		depends on UCC4_FAST
>> +		default UCC4_GETH
>> +
>> +		config UCC4_GETH
>> +			bool "UCC4 is GETH"
>> +	endchoice
>> +endmenu
>> +
>> +config UCC5
>> +	bool "Enable QE UCC5"
>> +
>> +choice
>> +	prompt "UCC5 speed selection"
>> +	depends on UCC5
>> +	default UCC5_SLOW
>> +
>> +	config UCC5_SLOW
>> +		bool "UCC5 is slow"
>> +	config UCC5_FAST
>> +		bool "UCC5 is fast"
>> +endchoice
>> +
>> +menu "UCC5 Protocols options"
>> +	depends on UCC5
>> +
>> +	choice
>> +		prompt "UCC5 Slow Protocols selection"
>> +		depends on UCC5_SLOW
>> +		default UCC5_UART
>> +
>> +		config UCC5_UART
>> +			bool "UCC5 is UART"
>> +	endchoice
>> +
>> +	choice
>> +		prompt "UCC5 Fast Protocols selection"
>> +		depends on UCC5_FAST
>> +		default UCC5_GETH
>> +
>> +		config UCC5_GETH
>> +			bool "UCC5 is GETH"
>> +	endchoice
>> +endmenu
>> +
>> +config UCC6
>> +	bool "Enable QE UCC6"
>> +
>> +choice
>> +	prompt "UCC6 speed selection"
>> +	depends on UCC6
>> +	default UCC6_SLOW
>> +
>> +	config UCC6_SLOW
>> +		bool "UCC6 is slow"
>> +	config UCC6_FAST
>> +		bool "UCC6 is fast"
>> +endchoice
>> +
>> +menu "UCC6 Protocols options"
>> +	depends on UCC6
>> +
>> +	choice
>> +		prompt "UCC6 Slow Protocols selection"
>> +		depends on UCC6_SLOW
>> +		default UCC6_UART
>> +
>> +		config UCC6_UART
>> +			bool "UCC6 is UART"
>> +	endchoice
>> +
>> +	choice
>> +		prompt "UCC6 Fast Protocols selection"
>> +		depends on UCC6_FAST
>> +		default UCC6_GETH
>> +
>> +		config UCC6_GETH
>> +			bool "UCC6 is GETH"
>> +	endchoice
>> +endmenu
>> +
>> +config UCC7
>> +	bool "Enable QE UCC7"
>> +
>> +choice
>> +	prompt "UCC7 speed selection"
>> +	depends on UCC7
>> +	default UCC7_SLOW
>> +
>> +	config UCC7_SLOW
>> +		bool "UCC7 is slow"
>> +	config UCC7_FAST
>> +		bool "UCC7 is fast"
>> +endchoice
>> +
>> +menu "UCC7 Protocols options"
>> +	depends on UCC7
>> +
>> +	choice
>> +		prompt "UCC7 Slow Protocols selection"
>> +		depends on UCC7_SLOW
>> +		default UCC7_UART
>> +
>> +		config UCC7_UART
>> +			bool "UCC7 is UART"
>> +	endchoice
>> +
>> +	choice
>> +		prompt "UCC7 Fast Protocols selection"
>> +		depends on UCC7_FAST
>> +		default UCC7_GETH
>> +
>> +		config UCC7_GETH
>> +			bool "UCC7 is GETH"
>> +	endchoice
>> +endmenu
>> +
>> +config UCC8
>> +	bool "Enable QE UCC8"
>> +
>> +choice
>> +	prompt "UCC8 speed selection"
>> +	depends on UCC8
>> +	default UCC8_SLOW
>> +
>> +	config UCC8_SLOW
>> +		bool "UCC8 is slow"
>> +	config UCC8_FAST
>> +		bool "UCC8 is fast"
>> +endchoice
>> +
>> +menu "UCC8 Protocols options"
>> +	depends on UCC8
>> +
>> +	choice
>> +		prompt "UCC8 Slow Protocols selection"
>> +		depends on UCC8_SLOW
>> +		default UCC8_UART
>> +
>> +		config UCC8_UART
>> +			bool "UCC8 is UART"
>> +	endchoice
>> +
>> +	choice
>> +		prompt "UCC8 Fast Protocols selection"
>> +		depends on UCC8_FAST
>> +		default UCC8_GETH
>> +
>> +		config UCC8_GETH
>> +			bool "UCC8 is GETH"
>> +	endchoice
>> +endmenu
>> +
>> +config UCC
>> +	depends on UCC1 || UCC2 || UCC3 || UCC4 || UCC5 || UCC6 || UCC7
>
>> || UCC8
>> +	default y
>> +	bool
>> +
>> +config UCC_SLOW
>> +	depends on UCC1_SLOW || UCC2_SLOW || UCC3_SLOW || UCC4_SLOW ||
>> UCC5_SLOW || UCC6_SLOW || UCC7_SLOW || UCC8_SLOW
>> +	default y
>> +	bool
>> +
>> +config UCC_FAST
>> +	depends on UCC1_FAST || UCC2_FAST || UCC3_FAST || UCC4_FAST ||
>> UCC5_FAST || UCC6_FAST || UCC7_FAST || UCC8_FAST
>> +	default y
>> +	bool
>> +
>> +config UCC_GETH_CONF
>> +	depends on UCC1_GETH || UCC2_GETH || UCC3_GETH || UCC4_GETH ||
>> UCC5_GETH || UCC6_GETH || UCC7_GETH || UCC8_GETH
>> +	default y
>> +	bool
>> +endmenu
>> +
>> diff --git a/arch/powerpc/sysdev/qe_lib/Makefile b/arch/powerpc/
>> sysdev/qe_lib/Makefile
>> new file mode 100644
>> index 0000000..c04a70c
>> --- /dev/null
>> +++ b/arch/powerpc/sysdev/qe_lib/Makefile
>> @@ -0,0 +1,8 @@
>> +#
>> +# Makefile for the linux ppc-specific parts of QE
>> +#
>> +obj-$(CONFIG_QUICC_ENGINE)+=3D qe_common.o mm.o qe.o qe_ic.o qe_io.o
>> +
>> +obj-$(CONFIG_UCC)	+=3D ucc.o
>> +obj-$(CONFIG_UCC_SLOW)	+=3D ucc_slow.o
>> +obj-$(CONFIG_UCC_FAST)	+=3D ucc_fast.o ucc_slow.o
>> diff --git a/arch/powerpc/sysdev/qe_lib/mm.c b/arch/powerpc/sysdev/
>> qe_lib/mm.c
>> new file mode 100644
>> index 0000000..58984ba
>> --- /dev/null
>> +++ b/arch/powerpc/sysdev/qe_lib/mm.c
>> @@ -0,0 +1,770 @@
>> +/*
>> + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights
>> reserved.
>> + *
>> + * Author: Shlomi Gridish <gridish@freescale.com>
>> + *
>> + * Description:
>> + * QE Memory Manager.
>> + *
>> + * Changelog:
>> + * Jun 28, 2006 Li Yang <LeoLi@freescale.com>
>> + * - Reorganized as qe_lib
>> + * - Merged to powerpc arch; add device tree support
>> + * - Style fixes
>> + *
>> + * 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 <asm/irq.h>
>> +#include <asm/page.h>
>> +#include <asm/pgtable.h>
>> +
>> +#include "mm.h"
>> +
>> +#define MAX_ALIGNMENT   20
>> +#define MAX_NAME_LEN    50
>> +
>> +#define MAKE_ALIGNED(adr, align) ( ((u32)adr + (align - 1)) & (~
>> (align - 1)) )
>> +
>> +/* mem_block_t data stucutre defines parameters of the Memory
>> Block */
>> +typedef struct mem_block {
>> +	struct mem_block *next;	/* Pointer to the next memory block */
>> +
>> +	u32 base;		/* base address of the memory block */
>> +	u32 end;		/* end address of the memory block */
>> +} mem_block_t;
>> +
>> +/* free_block_t data stucutre defines parameters of the Free =20
>> Block */
>> +typedef struct free_block {
>> +	struct free_block *next;	/* Pointer to the next free
> block */
>> +
>> +	u32 base;		/* base address of the block */
>> +	u32 end;		/* end address of the block */
>> +} free_block_t;
>> +
>> +/* busy_block_t data stucutre defines parameters of the Busy
>> Block  */
>> +typedef struct busy_block {
>> +	struct busy_block *next;	/* Pointer to the next free
> block */
>> +
>> +	u32 base;		/* base address of the block */
>> +	u32 end;		/* end address of the block */
>> +	char name[MAX_NAME_LEN];
>> +} busy_block_t;
>> +
>> +/* mm_t data structure defines parameters of the MM object */
>> +typedef struct mm {
>> +	mem_block_t *mem_blocks;    /* List of memory blocks (Memory
>> list) */
>> +	busy_block_t *busy_blocks;	/* List of busy blocks (Busy
> list) */
>> +	free_block_t *free_blocks[MAX_ALIGNMENT + 1];/* align lists of
> free
>> +							blocks (Free
> lists) */
>> +} mm_t;
>> +
>> +/
>> *********************************************************************

>> *
>> + *                     MM internal routines
>> set                       *
>> +
>> *********************************************************************

>> *
>
>> /
>> +
>> +/****************************************************************
>> + *  Routine:   mem_block_init
>> + *
>> + *  Description:
>> + *      Initializes a new memory block of "size" bytes and started
>> + *      from "base" address.
>> + *
>> + *  Arguments:
>> + *      mem_blk- handle to the mem_blk object
>> + *      base    - base address of the memory block
>> + *      size    - size of the memory block
>> + *
>> + *  Return value:
>> + *      0 is returned on success. E_NOMEMORY is returned
>> + *      if can't allocate memory for mem_blk object.
>> + ****************************************************************/
>> +static int mem_block_init(void **mem_blk, u32 base, u32 size)
>> +{
>> +	mem_block_t *p_mem_blk;
>> +
>> +	p_mem_blk =3D (mem_block_t *) kmalloc(sizeof(mem_block_t),
>> GFP_KERNEL);
>> +	if (!p_mem_blk)
>> +		return -ENOMEM;
>> +
>> +	p_mem_blk->base =3D base;
>> +	p_mem_blk->end =3D base + size;
>> +	p_mem_blk->next =3D 0;
>> +
>> +	*mem_blk =3D p_mem_blk;
>> +
>> +	return (0);
>> +}
>> +
>> +/****************************************************************
>> + *  Routine:   free_block_init
>> + *
>> + *  Description:
>> + *      Initializes a new free block of of "size" bytes and
>> + *      started from "base" address.
>> + *
>> + *  Arguments:
>> + *      FreeBlock - handle to the FreeBlock object
>> + *      base      - base address of the free block
>> + *      size      - size of the free block
>> + *
>> + *  Return value:
>> + *      0 is returned on success. E_NOMEMORY is returned
>> + *      if can't allocate memory for a free block.
>> + ****************************************************************/
>> +static int free_block_init(void **FreeBlock, u32 base, u32 size)
>> +{
>> +	free_block_t *p_free_blk;
>> +
>> +	p_free_blk =3D (free_block_t *) kmalloc(sizeof(free_block_t),
>> GFP_KERNEL);
>> +	if (!p_free_blk)
>> +		return -ENOMEM;
>> +
>> +	p_free_blk->base =3D base;
>> +	p_free_blk->end =3D base + size;
>> +	p_free_blk->next =3D 0;
>> +
>> +	*FreeBlock =3D p_free_blk;
>> +
>> +	return (0);
>> +}
>> +
>> +/****************************************************************
>> + *  Routine:   busy_block_init
>> + *
>> + *  Description:
>> + *      Initializes a new busy block of "size" bytes and started
>> + *      rom "base" address. Each busy block has a name that
>> + *      specified the purpose of the memory allocation.
>> + *
>> + *  Arguments:
>> + *      BusyBlock - handle to the BusyBlock object
>> + *      base      - base address of the busy block
>> + *      size      - size of the busy block
>> + *      name      - name that specified the busy block
>> + *
>> + *  Return value:
>> + *      0 is returned on success. E_NOMEMORY is returned
>> + *      if can't allocate memory for busy block.
>> + ****************************************************************/
>> +static int busy_block_init(void **BusyBlock, u32 base, u32 size,
>> char *name)
>> +{
>> +	busy_block_t *p_busy_blk;
>> +	int n, NameLen;
>> +
>> +	p_busy_blk =3D (busy_block_t *) kmalloc(sizeof(busy_block_t),
>> GFP_KERNEL);
>> +	if (!p_busy_blk)
>> +		return -ENOMEM;
>> +
>> +	p_busy_blk->base =3D base;
>> +	p_busy_blk->end =3D base + size;
>> +	NameLen =3D (int)strlen(name);
>> +	n =3D (NameLen > MAX_NAME_LEN - 1) ? MAX_NAME_LEN - 1 : NameLen;
>> +	strncpy(p_busy_blk->name, name, (u32) n);
>> +	p_busy_blk->name[n] =3D '\0';
>> +	p_busy_blk->next =3D 0;
>> +
>> +	*BusyBlock =3D p_busy_blk;
>> +
>> +	return (0);
>> +}
>> +
>> +/****************************************************************
>> + *  Routine:    add_free
>> + *
>> + *  Description:
>> + *      Adds a new free block to the free lists. It updates each
>> + *      free list to include a new free block.
>> + *      Note, that all free block in each free list are ordered
>> + *      by their base address.
>> + *
>> + *  Arguments:
>> + *      p_mm  - pointer to the MM object
>> + *      base  - base address of a given free block
>> + *      end   - end address of a given free block
>> + *
>> + *  Return value:
>> + *
>> + *
>> + ****************************************************************/
>> +static int add_free(mm_t * p_mm, u32 base, u32 end)
>> +{
>> +	free_block_t *p_prev_blk, *p_curr_blk, *p_new_blk;
>> +	u32 align;
>> +	int i;
>> +	u32 align_base;
>> +
>> +	/* Updates free lists to include  a just released block */
>> +	for (i =3D 0; i <=3D MAX_ALIGNMENT; i++) {
>> +		p_prev_blk =3D p_new_blk =3D 0;
>> +		p_curr_blk =3D p_mm->free_blocks[i];
>> +
>> +		align =3D (u32) (0x1 << i);
>> +		align_base =3D MAKE_ALIGNED(base, align);
>> +
>> +		/* Goes to the next free list if there is no block to
> free */
>> +		if (align_base >=3D end)
>> +			continue;
>> +
>> +		/* Looks for a free block that should be updated */
>> +		while (p_curr_blk) {
>> +			if (align_base <=3D p_curr_blk->end) {
>> +				if (end > p_curr_blk->end) {
>> +					free_block_t *p_NextB;
>> +					while (p_curr_blk->next
>> +					       && end >
> p_curr_blk->next->end) {
>> +						p_NextB =3D
> p_curr_blk->next;
>> +						p_curr_blk->next =3D
>> +
> p_curr_blk->next->next;
>> +						kfree(p_NextB);
>> +					}
>> +
>> +					p_NextB =3D p_curr_blk->next;
>> +					if (!p_NextB
>> +					    || (p_NextB
>> +						&& end < p_NextB->base))
> {
>> +						p_curr_blk->end =3D end;
>> +					} else {
>> +						p_curr_blk->end =3D
> p_NextB->end;
>> +						p_curr_blk->next =3D
>> +						    p_NextB->next;
>> +						kfree(p_NextB);
>> +					}
>> +				} else if (end < p_curr_blk->base
>> +					   && ((end - align_base) >=3D
> align)) {
>> +					if (free_block_init
>> +					    ((void *)&p_new_blk,
> align_base,
>> +					     end - align_base) !=3D 0)
>> +						return -ENOMEM;
>> +					p_new_blk->next =3D p_curr_blk;
>> +					if (p_prev_blk)
>> +						p_prev_blk->next =3D
> p_new_blk;
>> +					else
>> +						p_mm->free_blocks[i] =3D
>> +						    p_new_blk;
>> +					break;
>> +				}
>> +
>> +				if (align_base < p_curr_blk->base
>> +				    && end >=3D p_curr_blk->base)
>> +					p_curr_blk->base =3D align_base;
>> +
>> +				/* if size of the free block is less
> then
>> +				 * alignment deletes that free block
> from
>> +				 * the free list. */
>> +				if ((p_curr_blk->end - p_curr_blk->base)
> <
>> +				    align) {
>> +					if (p_prev_blk)
>> +						p_prev_blk->next =3D
>> +						    p_curr_blk->next;
>> +					else
>> +						p_mm->free_blocks[i] =3D
>> +						    p_curr_blk->next;
>> +					kfree(p_curr_blk);
>> +				}
>> +				break;
>> +			} else {
>> +				p_prev_blk =3D p_curr_blk;
>> +				p_curr_blk =3D p_curr_blk->next;
>> +			}
>> +		}
>> +
>> +		/* If no free block found to be updated, insert a new
> free block
>> +		 * to the end of the free list. */
>> +		if (!p_curr_blk && ((end - base) % align =3D=3D 0)) {
>> +			if (free_block_init
>> +			    ((void *)&p_new_blk, align_base, end - base)
> !=3D 0)
>> +				return -ENOMEM;
>> +			if (p_prev_blk)
>> +				p_prev_blk->next =3D p_new_blk;
>> +			else
>> +				p_mm->free_blocks[i] =3D p_new_blk;
>> +		}
>> +
>> +		/* Update boundaries of the new free block */
>> +		if (align =3D=3D 1 && !p_new_blk) {
>> +			if (p_curr_blk && base > p_curr_blk->base)
>> +				base =3D p_curr_blk->base;
>> +			if (p_curr_blk && end < p_curr_blk->end)
>> +				end =3D p_curr_blk->end;
>> +		}
>> +	}
>> +
>> +	return (0);
>> +}
>> +
>> +/****************************************************************
>> + *  Routine:      cut_free
>> + *
>> + *  Description:
>> + *      Cuts a free block from hold_base to hold_end from the free
>> lists.
>> + *      That is, it updates all free lists of the MM object do
>> + *      not include a block of memory from hold_base to hold_end.
>> + *      For each free lists it seek for a free block that holds
>> + *      either hold_base or hold_end. If such block is found it
>> updates it.
>> + *
>> + *  Arguments:
>> + *      p_mm            - pointer to the MM object
>> + *      hold_base        - base address of the allocated block
>> + *      hold_end         - end address of the allocated block
>> + *
>> + *  Return value:
>> + *      0 is returned on success,
>> + *      otherwise returns an error code.
>> + *
>> + ****************************************************************/
>> +static int cut_free(mm_t * p_mm, u32 hold_base, u32 hold_end)
>> +{
>> +	free_block_t *p_prev_blk, *p_curr_blk, *p_new_blk;
>> +	u32 align_base, base, end, align;
>> +	int i;
>> +
>> +	for (i =3D 0; i <=3D MAX_ALIGNMENT; i++) {
>> +		p_prev_blk =3D p_new_blk =3D 0;
>> +		p_curr_blk =3D p_mm->free_blocks[i];
>> +
>> +		align =3D (u32) 0x1 << i;
>> +		align_base =3D MAKE_ALIGNED(hold_end, align);
>> +
>> +		while (p_curr_blk) {
>> +			base =3D p_curr_blk->base;
>> +			end =3D p_curr_blk->end;
>> +
>> +			if (hold_base <=3D base && hold_end <=3D end
>> +			    && hold_end > base) {
>> +				if (align_base >=3D end
>> +				    || (align_base < end
>> +					&& (end - align_base) < align))
> {
>> +					if (p_prev_blk)
>> +						p_prev_blk->next =3D
>> +						    p_curr_blk->next;
>> +					else
>> +						p_mm->free_blocks[i] =3D
>> +						    p_curr_blk->next;
>> +					kfree(p_curr_blk);
>> +				} else {
>> +					p_curr_blk->base =3D align_base;
>> +				}
>> +				break;
>> +			} else if (hold_base > base && hold_end <=3D end)
> {
>> +				if ((hold_base - base) >=3D align) {
>> +					if (align_base < end
>> +					    && (end - align_base) >=3D
> align) {
>> +						if (free_block_init
>> +						    ((void *)&p_new_blk,
>> +						     align_base,
>> +						     (end - align_base))
> !=3D 0)
>> +							return -ENOMEM;
>> +						p_new_blk->next =3D
>> +						    p_curr_blk->next;
>> +						p_curr_blk->next =3D
> p_new_blk;
>> +					}
>> +					p_curr_blk->end =3D hold_base;
>> +				} else if (align_base < end
>> +					   && (end - align_base) >=3D
> align) {
>> +					p_curr_blk->base =3D align_base;
>> +				} else {
>> +					if (p_prev_blk)
>> +						p_prev_blk->next =3D
>> +						    p_curr_blk->next;
>> +					else
>> +						p_mm->free_blocks[i] =3D
>> +						    p_curr_blk->next;
>> +					kfree(p_curr_blk);
>> +				}
>> +				break;
>> +			} else {
>> +				p_prev_blk =3D p_curr_blk;
>> +				p_curr_blk =3D p_curr_blk->next;
>> +			}
>> +		}
>> +	}
>> +
>> +	return (0);
>> +}
>> +
>> +/****************************************************************
>> + *  Routine:     add_busy
>> + *
>> + *  Description:
>> + *      Adds a new busy block to the list of busy blocks. Note,
>> + *      that all busy blocks are ordered by their base address in
>> + *      the busy list.
>> + *
>> + *  Arguments:
>> + *      MM              - handler to the MM object
>> + *      p_new_busy_blk      - pointer to the a busy block
>> + *
>> + *  Return value:
>> + *      None.
>> + *
>> + ****************************************************************/
>> +static void add_busy(mm_t * p_mm, busy_block_t * p_new_busy_blk)
>> +{
>> +	busy_block_t *p_cur_busy_blk, *p_prev_busy_blk;
>> +
>> +	/* finds a place of a new busy block in the list of busy blocks
> */
>> +	p_prev_busy_blk =3D 0;
>> +	p_cur_busy_blk =3D p_mm->busy_blocks;
>> +
>> +	while (p_cur_busy_blk && p_new_busy_blk->base > p_cur_busy_blk-
>>> base) {
>> +		p_prev_busy_blk =3D p_cur_busy_blk;
>> +		p_cur_busy_blk =3D p_cur_busy_blk->next;
>> +	}
>> +
>> +	/* insert the new busy block into the list of busy blocks */
>> +	if (p_cur_busy_blk)
>> +		p_new_busy_blk->next =3D p_cur_busy_blk;
>> +	if (p_prev_busy_blk)
>> +		p_prev_busy_blk->next =3D p_new_busy_blk;
>> +	else
>> +		p_mm->busy_blocks =3D p_new_busy_blk;
>> +
>> +}
>> +
>> +/****************************************************************
>> + *  Routine:     get_greater_align
>> + *
>> + *  Description:
>> + *      Allocates a block of memory according to the given size
>> + *      and the alignment. That routine is called from the mm_get
>> + *      routine if the required alignment is grater then
>> MAX_ALIGNMENT.
>> + *      In that case, it goes over free blocks of 64 byte align list
>> + *      and checks if it has the required size of bytes of the
>> required
>> + *      alignment. If no blocks found returns ILLEGAL_BASE.
>> + *      After the block is found and data is allocated, it calls
>> + *      the internal cut_free routine to update all free lists
>> + *      do not include a just allocated block. Of course, each
>> + *      free list contains a free blocks with the same alignment.
>> + *      It is also creates a busy block that holds
>> + *      information about an allocated block.
>> + *
>> + *  Arguments:
>> + *      MM              - handle to the MM object
>> + *      size            - size of the MM
>> + *      align       - index as a power of two defines
>> + *                        a required alignment that is grater then
>> 64.
>> + *      name            - the name that specifies an allocated =20
>> block.
>> + *
>> + *  Return value:
>> + *      base address of an allocated block.
>> + *      ILLEGAL_BASE if can't allocate a block
>> + *
>> + ****************************************************************/
>> +static int get_greater_align(void *MM, u32 size, int align, char
>> *name)
>> +{
>> +	mm_t *p_mm =3D (mm_t *) MM;
>> +	free_block_t *p_free_blk;
>> +	busy_block_t *p_new_busy_blk;
>> +	u32 hold_base, hold_end, align_base =3D 0;
>> +	u32 ret;
>> +
>> +	/* goes over free blocks of the 64 byte alignment list
>> +	 * and look for a block of the suitable size and
>> +	 * base address according to the alignment.
>> +	 */
>> +	p_free_blk =3D p_mm->free_blocks[MAX_ALIGNMENT];
>> +
>> +	while (p_free_blk) {
>> +		align_base =3D MAKE_ALIGNED(p_free_blk->base, align);
>> +
>> +		/* the block is found if the aligned base inside the
> block
>> +		 * and has the anough size.
>> +		 */
>> +		if (align_base >=3D p_free_blk->base &&
>> +		    align_base < p_free_blk->end &&
>> +		    size <=3D (p_free_blk->end - align_base))
>> +			break;
>> +		else
>> +			p_free_blk =3D p_free_blk->next;
>> +	}
>> +
>> +	/* If such block isn't found */
>> +	if (!p_free_blk)
>> +		return -EBUSY;
>> +
>> +	hold_base =3D align_base;
>> +	hold_end =3D align_base + size;
>> +
>> +	/* init a new busy block */
>> +	if ((ret =3D
>> +	     busy_block_init((void *)&p_new_busy_blk, hold_base, size,
>> +			     name)) !=3D 0)
>> +		return ret;
>> +
>> +	/* calls Update routine to update a lists of free blocks */
>> +	if ((ret =3D cut_free(MM, hold_base, hold_end)) !=3D 0)
>> +		return ret;
>> +
>> +	/* insert the new busy block into the list of busy blocks */
>> +	add_busy(p_mm, p_new_busy_blk);
>> +
>> +	return (hold_base);
>> +}
>> +
>> +/
>> *********************************************************************

>> *
>> + *                     MM API routines
>> set                            *
>> +
>> *********************************************************************

>> *
>
>> /
>> +int mm_init(void **MM, u32 base, u32 size)
>> +{
>> +	mm_t *p_mm;
>> +	int i;
>> +	u32 new_base, new_size;
>> +
>> +	/* Initializes a new MM object */
>> +	p_mm =3D (mm_t *) kmalloc(sizeof(mm_t), GFP_KERNEL);
>> +	if (p_mm =3D=3D 0)
>> +		return -ENOMEM;
>> +
>> +	/* initializes a new memory block */
>> +	if (mem_block_init((void *)&p_mm->mem_blocks, base, size) !=3D 0)
>> +		return -ENOMEM;
>> +
>> +	/* A busy list is empty */
>> +	p_mm->busy_blocks =3D 0;
>> +
>> +	/*Initializes a new free block for each free list */
>> +	for (i =3D 0; i <=3D MAX_ALIGNMENT; i++) {
>> +		new_base =3D MAKE_ALIGNED(base, (0x1 << i));
>> +		new_size =3D size - (new_base - base);
>> +		if (free_block_init((void *)&p_mm->free_blocks[i],
>> +				    new_base, new_size) !=3D 0)
>> +			return -ENOMEM;
>> +	}
>> +
>> +	*MM =3D p_mm;
>> +
>> +	return (0);
>> +}
>> +
>> +EXPORT_SYMBOL(mm_init);
>> +
>> +void mm_free(void *MM)
>> +{
>> +	mm_t *p_mm =3D (mm_t *) MM;
>> +	mem_block_t *p_mem_blk;
>> +	busy_block_t *p_busy_blk;
>> +	free_block_t *p_free_blk;
>> +	void *p_blk;
>> +	int i;
>> +
>> +	if (!p_mm)
>> +		return;
>> +
>> +	/* release memory allocated for busy blocks */
>> +	p_busy_blk =3D p_mm->busy_blocks;
>> +	while (p_busy_blk) {
>> +		p_blk =3D p_busy_blk;
>> +		p_busy_blk =3D p_busy_blk->next;
>> +		kfree(p_blk);
>> +	}
>> +
>> +	/* release memory allocated for free blocks */
>> +	for (i =3D 0; i <=3D MAX_ALIGNMENT; i++) {
>> +		p_free_blk =3D p_mm->free_blocks[i];
>> +		while (p_free_blk) {
>> +			p_blk =3D p_free_blk;
>> +			p_free_blk =3D p_free_blk->next;
>> +			kfree(p_blk);
>> +		}
>> +	}
>> +
>> +	/* release memory allocated for memory blocks */
>> +	p_mem_blk =3D p_mm->mem_blocks;
>> +	while (p_mem_blk) {
>> +		p_blk =3D p_mem_blk;
>> +		p_mem_blk =3D p_mem_blk->next;
>> +		kfree(p_blk);
>> +	}
>> +
>> +	/* release memory allocated for MM object itself */
>> +	kfree(p_mm);
>> +}
>> +
>> +EXPORT_SYMBOL(mm_free);
>> +
>> +void *mm_get(void *MM, u32 size, int align, char *name)
>> +{
>> +	mm_t *p_mm =3D (mm_t *) MM;
>> +	free_block_t *p_free_blk;
>> +	busy_block_t *p_new_busy_blk;
>> +	u32 hold_base, hold_end;
>> +	u32 i =3D 0, j =3D (u32) align;
>> +	u32 ret;
>> +
>> +	if (!p_mm)
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	/* checks that align value is grater then zero */
>> +	if (align =3D=3D 0)
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	/* checks if alignment is a power of two,
>> +	 * if it correct and if the required size
>> +	 * is multiple of the given alignment.
>> +	 */
>> +	while ((j & 0x1) =3D=3D 0) {
>> +		i++;
>> +		j =3D j >> 1;
>> +	}
>> +
>> +	/* if the given alignment isn't power of two, returns an error
> */
>> +	if (j !=3D 1)
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	if (i > MAX_ALIGNMENT)
>> +		return ERR_PTR(get_greater_align(MM, size, align,
> name));
>> +
>> +	/* look for a block of the size grater or equal to the
>> +	 * required size.
>> +	 */
>> +	p_free_blk =3D p_mm->free_blocks[i];
>> +	while (p_free_blk && (p_free_blk->end - p_free_blk->base) <
> size)
>> +		p_free_blk =3D p_free_blk->next;
>> +
>> +	/* If such block is found */
>> +	if (!p_free_blk)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	hold_base =3D p_free_blk->base;
>> +	hold_end =3D hold_base + size;
>> +
>> +	/* init a new busy block */
>> +	if ((ret =3D
>> +	     busy_block_init((void *)&p_new_busy_blk, hold_base, size,
>> +			     name)) !=3D 0)
>> +		return ERR_PTR(ret);
>> +
>> +	/* calls Update routine to update a lists of free blocks */
>> +	if ((ret =3D cut_free(MM, hold_base, hold_end)) !=3D 0)
>> +		return ERR_PTR(ret);
>> +
>> +	/* insert the new busy block into the list of busy blocks */
>> +	add_busy(p_mm, p_new_busy_blk);
>> +
>> +	return (void *)(hold_base);
>> +}
>> +
>> +EXPORT_SYMBOL(mm_get);
>> +
>> +void *mm_get_force(void *MM, u32 base, u32 size, char *name)
>> +{
>> +	mm_t *p_mm =3D (mm_t *) MM;
>> +	free_block_t *p_free_blk;
>> +	busy_block_t *p_new_busy_blk;
>> +	int blk_is_free =3D 0;
>> +	u32 ret;
>> +
>> +	p_free_blk =3D p_mm->free_blocks[0];/* The biggest free blocks are
> in
>> +					     the free list with
> alignment 1 */
>> +	while (p_free_blk) {
>> +		if (base >=3D p_free_blk->base
>> +		    && (base + size) <=3D p_free_blk->end) {
>> +			blk_is_free =3D 1;
>> +			break;
>> +		} else
>> +			p_free_blk =3D p_free_blk->next;
>> +	}
>> +
>> +	if (!blk_is_free)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	/* init a new busy block */
>> +	if ((ret =3D
>> +	     busy_block_init((void *)&p_new_busy_blk, base, size, name))
> !
>> =3D 0)
>> +		return ERR_PTR(ret);
>> +
>> +	/* calls Update routine to update a lists of free blocks */
>> +	if ((ret =3D cut_free(MM, base, base + size)) !=3D 0)
>> +		return ERR_PTR(ret);
>> +
>> +	/* insert the new busy block into the list of busy blocks */
>> +	add_busy(p_mm, p_new_busy_blk);
>> +	return (void *)(base);
>> +}
>> +
>> +EXPORT_SYMBOL(mm_get_force);
>> +
>> +int mm_put(void *MM, u32 base)
>> +{
>> +	mm_t *p_mm =3D (mm_t *) MM;
>> +	busy_block_t *p_busy_blk, *p_prev_busy_blk;
>> +	u32 size;
>> +	u32 ret;
>> +
>> +	if (!p_mm)
>> +		return -EINVAL;
>> +
>> +	/* Look for a busy block that have the given base value.
>> +	 * That block will be returned back to the memory.
>> +	 */
>> +	p_prev_busy_blk =3D 0;
>> +	p_busy_blk =3D p_mm->busy_blocks;
>> +	while (p_busy_blk && base !=3D p_busy_blk->base) {
>> +		p_prev_busy_blk =3D p_busy_blk;
>> +		p_busy_blk =3D p_busy_blk->next;
>> +	}
>> +
>> +	if (!p_busy_blk)
>> +		return -EINVAL;
>> +
>> +	if ((ret =3D add_free(p_mm, p_busy_blk->base, p_busy_blk->end)) =
!=3D
> 0)
>> +		return ret;
>> +
>> +	/* removes a busy block form the list of busy blocks */
>> +	if (p_prev_busy_blk)
>> +		p_prev_busy_blk->next =3D p_busy_blk->next;
>> +	else
>> +		p_mm->busy_blocks =3D p_busy_blk->next;
>> +
>> +	size =3D p_busy_blk->end - p_busy_blk->base;
>> +
>> +	kfree(p_busy_blk);
>> +
>> +	return (0);
>> +}
>> +
>> +EXPORT_SYMBOL(mm_put);
>> +
>> +void mm_dump(void *MM)
>> +{
>> +	mm_t *p_mm =3D (mm_t *) MM;
>> +	free_block_t *p_free_blk;
>> +	busy_block_t *p_busy_blk;
>> +	int i;
>> +
>> +	p_busy_blk =3D p_mm->busy_blocks;
>> +	printk(KERN_INFO "List of busy blocks:\n");
>> +	while (p_busy_blk) {
>> +		printk(KERN_INFO "\t0x%08x: (%s: b=3D0x%08x, e=3D0x%08x)\n",
>> +		       (u32) p_busy_blk, p_busy_blk->name,
> p_busy_blk->base,
>> +		       p_busy_blk->end);
>> +		p_busy_blk =3D p_busy_blk->next;
>> +	}
>> +
>> +	printk(KERN_INFO "Lists of free blocks according to
> alignment:\n");
>> +	for (i =3D 0; i <=3D MAX_ALIGNMENT; i++) {
>> +		printk(KERN_INFO "%d alignment:\n", (0x1 << i));
>> +		p_free_blk =3D p_mm->free_blocks[i];
>> +		while (p_free_blk) {
>> +			printk(KERN_INFO "\t0x%08x: (b=3D0x%08x,
> e=3D0x%08x)\n",
>> +			       (u32) p_free_blk, p_free_blk->base,
>> +			       p_free_blk->end);
>> +			p_free_blk =3D p_free_blk->next;
>> +		}
>> +		printk(KERN_INFO "\n");
>> +	}
>> +}
>> +
>> +EXPORT_SYMBOL(mm_dump);
>> diff --git a/arch/powerpc/sysdev/qe_lib/mm.h b/arch/powerpc/sysdev/
>> qe_lib/mm.h
>> new file mode 100644
>> index 0000000..ab30080
>> --- /dev/null
>> +++ b/arch/powerpc/sysdev/qe_lib/mm.h
>> @@ -0,0 +1,6 @@
>> +int mm_init ( void **MM, u32 base, u32 size );
>> +void mm_free (void * MM);
>> +void *mm_get ( void * MM, u32 size, int align, char* name );
>> +void *mm_get_force ( void * MM, u32 base, u32 size, char* name );
>> +int mm_put ( void * MM, u32 base );
>> +void mm_dump ( void * MM );
>> diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/
>> qe_lib/qe.c
>> new file mode 100644
>> index 0000000..0fbb54c
>> --- /dev/null
>> +++ b/arch/powerpc/sysdev/qe_lib/qe.c
>> @@ -0,0 +1,181 @@
>> +/*
>> + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights
>> reserved.
>> + *
>> + * Author: Li Yang <LeoLi@freescale.com>
>> + *
>> + * Description:
>> + * FSL QE SOC setup.
>> + *
>> + * Changelog:
>> + * Jun 21, 2006	Initial version
>> + *
>> + * 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 =3D -1;
>> +
>> +phys_addr_t get_qe_base(void)
>> +{
>> +	struct device_node *qe;
>> +
>> +	if (qebase !=3D -1)
>> +		return qebase;
>> +
>> +	qe =3D of_find_node_by_type(NULL, "qe");
>> +	if (qe) {
>> +		unsigned int size;
>> +		void *prop =3D get_property(qe, "reg", &size);
>> +		qebase =3D 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 =3D NULL, i =3D 0;
>> +	     (np =3D of_find_compatible_node(np, "network", "ucc_geth"))
> !=3D
>> 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 =3D of_address_to_resource(np, 0, &r[0]);
>> +		if (ret)
>> +			goto err;
>> +
>> +		ugeth_data.phy_reg_addr =3D r[0].start;
>> +		r[1].start =3D np->intrs[0].line;
>> +		r[1].end =3D np->intrs[0].line;
>> +		r[1].flags =3D IORESOURCE_IRQ;
>> +
>> +		model =3D get_property(np, "model", NULL);
>> +		ucc_num =3D *((u32 *) get_property(np, "device-id",
> NULL));
>> +		if ((strstr(model, "UCC") =3D=3D NULL) ||
>> +				(ucc_num < 1) || (ucc_num > 8)) {
>> +			ret =3D -ENODEV;
>> +			goto err;
>> +		}
>> +	=09
>> +		ugeth_dev =3D
>> +		    platform_device_register_simple("ucc_geth", ucc_num
> - 1,
>> +				    &r[0], np->n_intrs + 1);
>> +
>> +		if (IS_ERR(ugeth_dev)) {
>> +			ret =3D PTR_ERR(ugeth_dev);
>> +			goto err;
>> +		}
>> +
>> +		mac_addr =3D get_property(np, "mac-address", NULL);
>> +	=09
>> +		memcpy(ugeth_data.mac_addr, mac_addr, 6);
>> +
>> +		ugeth_data.rx_clock =3D *((u32 *) get_property(np,
> "rx-clock",
>> +					NULL));
>> +		ugeth_data.tx_clock =3D *((u32 *) get_property(np,
> "tx-clock",
>> +					NULL));
>> +
>> +		ph =3D (phandle *) get_property(np, "phy-handle", NULL);
>> +		phy =3D of_find_node_by_phandle(*ph);
>> +
>> +		if (phy =3D=3D NULL) {
>> +			ret =3D -ENODEV;
>> +			goto unreg;
>> +		}
>> +
>> +		mdio =3D of_get_parent(phy);
>> +
>> +		id =3D (u32 *) get_property(phy, "reg", NULL);
>> +		ret =3D of_address_to_resource(mdio, 0, &res);
>> +		if (ret) {
>> +			of_node_put(phy);
>> +			of_node_put(mdio);
>> +			goto unreg;
>> +		}
>> +	=09
>> +		ugeth_data.phy_id =3D *id;
>> +		ugeth_data.phy_interrupt =3D phy->intrs[0].line;
>> +		ugeth_data.phy_interface =3D *((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?).
> */=09
>> +		if ((ugeth_data.phy_interface =3D=3D ENET_1000_RGMII) ||
>> +				(ugeth_data.phy_interface =3D=3D
> ENET_100_RGMII) ||
>> +				(ugeth_data.phy_interface =3D=3D
> ENET_10_RGMII)) {
>> +			u32 *tmp_reg =3D (u32 *) ioremap(get_immrbase()
>> +					+ 0x14A8, 0x4);
>> +			u32 tmp_val =3D in_be32(tmp_reg);
>> +			if (ucc_num =3D=3D 1)
>> +				out_be32(tmp_reg, tmp_val | 0x00003000);
>> +			else if (ucc_num =3D=3D 2)
>> +				out_be32(tmp_reg, tmp_val | 0x0c000000);
>> +			iounmap(tmp_reg);
>> +		}
>> +	=09
>> +		if (phy->intrs[0].line !=3D 0)
>> +			ugeth_data.board_flags |=3D
> FSL_UGETH_BRD_HAS_PHY_INTR;
>> +
>> +		of_node_put(phy);
>> +		of_node_put(mdio);
>> +
>> +		ret =3D
>> +		    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;
>> +}
>> +
>> +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..cd0aca9
>> --- /dev/null
>> +++ b/arch/powerpc/sysdev/qe_lib/qe_common.c
>> @@ -0,0 +1,401 @@
>> +/*
>> + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights
>> reserved.
>> + *
>> + * Author: Shlomi Gridish <gridish@freescale.com>
>> + *
>> + * Description:
>> + * General Purpose functions for the global management of the
>> + * QUICC Engine (QE).
>> + *
>> + * Changelog:
>> + * Jun 28, 2006	Li Yang <LeoLi@freescale.com>
>> + * - Reorganized as qe_lib
>> + * - Merged to powerpc arch; add device tree support
>> + * - Style fixes
>> + *
>> + * 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.
>> + */
>> +#undef USE_RHEAP
>> +#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 <asm/irq.h>
>> +#include <asm/page.h>
>> +#include <asm/pgtable.h>
>> +#include <asm/immap_qe.h>
>> +#include <asm/qe.h>
>> +#include <asm/prom.h>
>> +#ifdef USE_RHEAP
>> +#include <asm/rheap.h>
>> +#else
>> +#include "mm.h"
>> +#endif				/* USE_RHEAP */
>> +
>> +/* QE snum state
>> +*/
>> +typedef enum qe_snum_state {
>> +	QE_SNUM_STATE_USED,	/* used */
>> +	QE_SNUM_STATE_FREE	/* free */
>> +} qe_snum_state_e;
>> +
>> +/* QE snum
>> +*/
>> +typedef struct qe_snum {
>> +	u8 num;			/* snum  */
>> +	qe_snum_state_e state;	/* state */
>> +} qe_snum_t;
>> +
>> +/* We allocate this here because it is used almost exclusively for
>> + * the communication processor devices.
>> + */
>> +EXPORT_SYMBOL(qe_immr);
>> +qe_map_t *qe_immr =3D NULL;
>> +static qe_snum_t snums[QE_NUM_OF_SNUM];	/* Dynamically allocated
>
>> SNUMs  */
>> +
>> +static void qe_snums_init(void);
>> +static void qe_muram_init(void);
>> +static int qe_sdma_init(void);
>> +
>> +void qe_reset(void)
>> +{
>> +	if (qe_immr =3D=3D NULL)
>> +		qe_immr =3D (qe_map_t *) ioremap(get_qe_base(),
> QE_IMMAP_SIZE);
>> +
>> +	qe_snums_init();
>> +
>> +	qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID,
>> +		     (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
>> +
>> +	/* Reclaim the MURAM memory for our use. */
>> +	qe_muram_init();
>> +
>> +#ifdef USE_RHEAP
>> +	if (qe_sdma_init())
>> +		panic("sdma init failed!");
>> +#endif				/* USE_RHEAP */
>> +}
>> +
>> +EXPORT_SYMBOL(qe_issue_cmd);
>> +int qe_issue_cmd(uint cmd, uint device, u8 mcn_protocol, u32
>> cmd_input)
>> +{
>> +	unsigned long flags;
>> +	u32 cecr;
>> +	u8 mcn_shift =3D 0, dev_shift =3D 0;
>> +
>> +	local_irq_save(flags);
>> +	if (cmd =3D=3D QE_RESET) {
>> +		out_be32(&qe_immr->cp.cecr, (u32) (cmd | QE_CR_FLG));
>> +	} else {
>> +		if (cmd =3D=3D QE_ASSIGN_PAGE) {
>> +			/* Here device is the SNUM, not sub-block */
>> +			dev_shift =3D QE_CR_SNUM_SHIFT;
>> +		} else if (cmd =3D=3D QE_ASSIGN_RISC) {
>> +			/* Here device is the SNUM, and mcnProtocol is
>> +			 * e_QeCmdRiscAssignment value */
>> +			dev_shift =3D QE_CR_SNUM_SHIFT;
>> +			mcn_shift =3D QE_CR_MCN_RISC_ASSIGN_SHIFT;
>> +		} else {
>> +			if (device =3D=3D QE_CR_SUBBLOCK_USB)
>> +				mcn_shift =3D QE_CR_MCN_USB_SHIFT;
>> +			else
>> +				mcn_shift =3D QE_CR_MCN_NORMAL_SHIFT;
>> +		}
>> +
>> +		out_be32(&qe_immr->cp.cecdr,
>> +			 immrbar_virt_to_phys((void *)cmd_input));
>> +		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 =3D in_be32(&qe_immr->cp.cecr);
>> +	} while (cecr & QE_CR_FLG);
>> +	local_irq_restore(flags);
>> +
>> +	return 0;
>> +}
>> +
>> +/* 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 =3D 0;
>> +
>> +unsigned int get_brg_clk(void)
>> +{
>> +	struct device_node *qe;
>> +	if (brg_clk)
>> +		return brg_clk;
>> +
>> +	qe =3D of_find_node_by_type(NULL, "qe");
>> +	if (qe) {
>> +		unsigned int size;
>> +		u32 *prop =3D (u32 *) get_property(qe, "brg-frequency",
> &size);
>> +		brg_clk =3D *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(uint brg, uint rate)
>> +{
>> +	volatile uint *bp;
>> +	u32 divisor;
>> +	int div16 =3D 0;
>> +
>> +	bp =3D (uint *) & qe_immr->brg.brgc1;
>> +	bp +=3D brg;
>> +
>> +	divisor =3D (get_brg_clk() / rate);
>> +	if (divisor > QE_BRGC_DIVISOR_MAX + 1) {
>> +		div16 =3D 1;
>> +		divisor /=3D 16;
>> +	}
>> +
>> +	*bp =3D ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) | QE_BRGC_ENABLE;
>> +	if (div16)
>> +		*bp |=3D QE_BRGC_DIV16;
>> +}
>> +
>> +static void qe_snums_init(void)
>> +{
>> +	int i;
>> +
>> +	/* Initialize the SNUMs array. */
>> +	for (i =3D 0; i < QE_NUM_OF_SNUM; i++)
>> +		snums[i].state =3D QE_SNUM_STATE_FREE;
>> +
>> +	/* Initialize SNUMs (thread serial numbers) according to QE
>> +	 * spec chapter 4, SNUM table */
>> +	i =3D 0;
>> +	snums[i++].num =3D 0x04;
>> +	snums[i++].num =3D 0x05;
>> +	snums[i++].num =3D 0x0C;
>> +	snums[i++].num =3D 0x0D;
>> +	snums[i++].num =3D 0x14;
>> +	snums[i++].num =3D 0x15;
>> +	snums[i++].num =3D 0x1C;
>> +	snums[i++].num =3D 0x1D;
>> +	snums[i++].num =3D 0x24;
>> +	snums[i++].num =3D 0x25;
>> +	snums[i++].num =3D 0x2C;
>> +	snums[i++].num =3D 0x2D;
>> +	snums[i++].num =3D 0x34;
>> +	snums[i++].num =3D 0x35;
>> +	snums[i++].num =3D 0x88;
>> +	snums[i++].num =3D 0x89;
>> +	snums[i++].num =3D 0x98;
>> +	snums[i++].num =3D 0x99;
>> +	snums[i++].num =3D 0xA8;
>> +	snums[i++].num =3D 0xA9;
>> +	snums[i++].num =3D 0xB8;
>> +	snums[i++].num =3D 0xB9;
>> +	snums[i++].num =3D 0xC8;
>> +	snums[i++].num =3D 0xC9;
>> +	snums[i++].num =3D 0xD8;
>> +	snums[i++].num =3D 0xD9;
>> +	snums[i++].num =3D 0xE8;
>> +	snums[i++].num =3D 0xE9;
>> +}
>> +
>> +int qe_get_snum(void)
>> +{
>> +	unsigned long flags;
>> +	int snum =3D -EBUSY;
>> +	int i;
>> +
>> +	local_irq_save(flags);
>> +	for (i =3D 0; i < QE_NUM_OF_SNUM; i++) {
>> +		if (snums[i].state =3D=3D QE_SNUM_STATE_FREE) {
>> +			snums[i].state =3D QE_SNUM_STATE_USED;
>> +			snum =3D snums[i].num;
>> +			break;
>> +		}
>> +	}
>> +	local_irq_restore(flags);
>> +
>> +	return snum;
>> +}
>> +
>> +EXPORT_SYMBOL(qe_get_snum);
>> +
>> +void qe_put_snum(u8 snum)
>> +{
>> +	int i;
>> +
>> +	for (i =3D 0; i < QE_NUM_OF_SNUM; i++) {
>> +		if (snums[i].num =3D=3D snum) {
>> +			snums[i].state =3D QE_SNUM_STATE_FREE;
>> +			break;
>> +		}
>> +	}
>> +}
>> +
>> +EXPORT_SYMBOL(qe_put_snum);
>> +
>> +static int qe_sdma_init(void)
>> +{
>> +	sdma_t *sdma =3D &qe_immr->sdma;
>> +	uint sdma_buf_offset;
>> +
>> +	if (!sdma)
>> +		return -ENODEV;
>> +
>> +	/* allocate 2 internal temporary buffers (512 bytes size each)
> for
>> +	 * the SDMA */
>> +	sdma_buf_offset =3D 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 spinlock_t qe_muram_lock;
>> +#ifdef USE_RHEAP
>> +/* 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;
>> +#else
>> +static void *mm =3D NULL;
>> +#endif				/* USE_RHEAP */
>> +
>> +static void qe_muram_init(void)
>> +{
>> +	spin_lock_init(&qe_muram_lock);
>> +
>> +#ifdef USE_RHEAP
>> +	/* 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 actually crap. QE_DATAONLY_BASE and
>> +	 * QE_DATAONLY_SIZE is only a subset of the available muram. It
>> +	 * varies with the processor and the microcode patches
> activated.
>> +	 * But the following should be at least safe.
>> +	 */
>> +	rh_attach_region(&qe_muram_info,
>> +			 (void *)QE_MURAM_DATAONLY_BASE,
>> +			 QE_MURAM_DATAONLY_SIZE);
>> +#else
>> +#endif				/* USE_RHEAP */
>> +}
>> +
>> +/* This function returns an index into the MURAM area.
>> + */
>> +uint qe_muram_alloc(uint size, uint align)
>> +{
>> +	void *start;
>> +	unsigned long flags;
>> +
>> +	spin_lock_irqsave(&qe_muram_lock, flags);
>> +#ifdef USE_RHEAP
>> +	qe_muram_info.alignment =3D align;
>> +	start =3D rh_alloc(&qe_muram_info, size, "QE");
>> +#else
>> +	if (!mm) {
>> +		mm_init(&mm, (u32)
> qe_muram_addr(QE_MURAM_DATAONLY_BASE),
>> +			QE_MURAM_DATAONLY_SIZE);
>> +		if (qe_sdma_init())
>> +			panic("sdma init failed!");
>> +
>> +	}
>> +	start =3D mm_get(mm, (u32) size, (int)align, "QE");
>> +	if (!IS_MURAM_ERR((u32) start))
>> +		start =3D (void *)((u32) start - (u32) qe_immr->muram);
>> +#endif				/* USE_RHEAP */
>> +	spin_unlock_irqrestore(&qe_muram_lock, flags);
>> +
>> +	return (uint) start;
>> +}
>> +
>> +EXPORT_SYMBOL(qe_muram_alloc);
>> +
>> +int qe_muram_free(uint offset)
>> +{
>> +	int ret;
>> +	unsigned long flags;
>> +
>> +	spin_lock_irqsave(&qe_muram_lock, flags);
>> +#ifdef USE_RHEAP
>> +	ret =3D rh_free(&qe_muram_info, (void *)offset);
>> +#else
>> +	ret =3D mm_put(mm, (u32) qe_muram_addr(offset));
>> +#endif				/* USE_RHEAP */
>> +	spin_unlock_irqrestore(&qe_muram_lock, flags);
>> +
>> +	return ret;
>> +}
>> +
>> +EXPORT_SYMBOL(qe_muram_free);
>> +
>> +/* not sure if this is ever needed */
>> +uint qe_muram_alloc_fixed(uint offset, uint size, uint align)
>> +{
>> +	void *start;
>> +	unsigned long flags;
>> +
>> +	spin_lock_irqsave(&qe_muram_lock, flags);
>> +#ifdef USE_RHEAP
>> +	qe_muram_info.alignment =3D align;
>> +	start =3D
>> +	    rh_alloc_fixed(&qe_muram_info, (void *)offset, size,
>> "commproc");
>> +#else
>> +	start =3D mm_get_force(mm, (u32) offset, (u32) size, "QE");
>> +	if (!IS_MURAM_ERR((u32) start))
>> +		start =3D (void *)((u32) start - (u32) qe_immr->muram);
>> +#endif				/* USE_RHEAP */
>> +	spin_unlock_irqrestore(&qe_muram_lock, flags);
>> +
>> +	return (uint) start;
>> +}
>> +
>> +EXPORT_SYMBOL(qe_muram_alloc_fixed);
>> +
>> +void qe_muram_dump(void)
>> +{
>> +#ifdef USE_RHEAP
>> +	rh_dump(&qe_muram_info);
>> +#else
>> +	mm_dump(mm);
>> +#endif				/* USE_RHEAP */
>> +}
>> +
>> +EXPORT_SYMBOL(qe_muram_dump);
>> +
>> +void *qe_muram_addr(uint 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..465630e
>> --- /dev/null
>> +++ b/arch/powerpc/sysdev/qe_lib/qe_ic.c
>> @@ -0,0 +1,487 @@
>> +/*
>> + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights
>> reserved.
>> + *
>> + * Author: Shlomi Gridish <gridish@freescale.com>
>> + *
>> + * Description:
>> + * QE Interrupt Controller routines implementations.
>> + *
>> + * Changelog:
>> + * Jun 28, 2006 Li Yang <LeoLi@freescale.com>
>> + * - Reorganized as qe_lib
>> + * - Merged to powerpc arch; add device tree support
>> + * - Style fixes
>> + *
>> + * 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/interrupt.h>
>> +
>> +#include <asm/irq.h>
>> +#include <asm/io.h>
>> +#include <asm/qe_ic.h>
>> +
>> +#include "qe_ic.h"
>> +
>> +static struct qe_ic_private p_qe_ic;
>> +static struct qe_ic_private *primary_qe_ic;
>> +
>> +static struct qe_ic_info qe_ic_info[] =3D {
>> +	[1] =3D {
>> +	       .mask =3D 0x00008000,
>> +	       .qimr =3D 1,
>> +	       .pri_code =3D 0},
>> +	[2] =3D {
>> +	       .mask =3D 0x00004000,
>> +	       .qimr =3D 1,
>> +	       .pri_code =3D 1},
>> +	[3] =3D {
>> +	       .mask =3D 0x00002000,
>> +	       .qimr =3D 1,
>> +	       .pri_code =3D 2},
>> +	[10] =3D {
>> +		.mask =3D 0x00000040,
>> +		.qimr =3D 1,
>> +		.pri_code =3D 1},
>> +	[11] =3D {
>> +		.mask =3D 0x00000020,
>> +		.qimr =3D 1,
>> +		.pri_code =3D 2},
>> +	[12] =3D {
>> +		.mask =3D 0x00000010,
>> +		.qimr =3D 1,
>> +		.pri_code =3D 3},
>> +	[13] =3D {
>> +		.mask =3D 0x00000008,
>> +		.qimr =3D 1,
>> +		.pri_code =3D 4},
>> +	[14] =3D {
>> +		.mask =3D 0x00000004,
>> +		.qimr =3D 1,
>> +		.pri_code =3D 5},
>> +	[15] =3D {
>> +		.mask =3D 0x00000002,
>> +		.qimr =3D 1,
>> +		.pri_code =3D 6},
>> +	[20] =3D {
>> +		.mask =3D 0x10000000,
>> +		.qimr =3D 0,
>> +		.pri_code =3D 3},
>> +	[25] =3D {
>> +		.mask =3D 0x00800000,
>> +		.qimr =3D 0,
>> +		.pri_code =3D 0},
>> +	[26] =3D {
>> +		.mask =3D 0x00400000,
>> +		.qimr =3D 0,
>> +		.pri_code =3D 1},
>> +	[27] =3D {
>> +		.mask =3D 0x00200000,
>> +		.qimr =3D 0,
>> +		.pri_code =3D 2},
>> +	[28] =3D {
>> +		.mask =3D 0x00100000,
>> +		.qimr =3D 0,
>> +		.pri_code =3D 3},
>> +	[32] =3D {
>> +		.mask =3D 0x80000000,
>> +		.qimr =3D 1,
>> +		.pri_code =3D 0},
>> +	[33] =3D {
>> +		.mask =3D 0x40000000,
>> +		.qimr =3D 1,
>> +		.pri_code =3D 1},
>> +	[34] =3D {
>> +		.mask =3D 0x20000000,
>> +		.qimr =3D 1,
>> +		.pri_code =3D 2},
>> +	[35] =3D {
>> +		.mask =3D 0x10000000,
>> +		.qimr =3D 1,
>> +		.pri_code =3D 3},
>> +	[36] =3D {
>> +		.mask =3D 0x08000000,
>> +		.qimr =3D 1,
>> +		.pri_code =3D 4},
>> +	[40] =3D {
>> +		.mask =3D 0x00800000,
>> +		.qimr =3D 1,
>> +		.pri_code =3D 0},
>> +	[41] =3D {
>> +		.mask =3D 0x00400000,
>> +		.qimr =3D 1,
>> +		.pri_code =3D 1},
>> +	[42] =3D {
>> +		.mask =3D 0x00200000,
>> +		.qimr =3D 1,
>> +		.pri_code =3D 2},
>> +	[43] =3D {
>> +		.mask =3D 0x00100000,
>> +		.qimr =3D 1,
>> +		.pri_code =3D 3},
>> +};
>> +
>> +struct hw_interrupt_type qe_ic =3D {
>> +	.typename =3D "QE IC",
>> +	.enable =3D qe_ic_enable_irq,
>> +	.disable =3D qe_ic_disable_irq,
>> +	.ack =3D qe_ic_disable_irq_and_ack,
>> +	.end =3D qe_ic_end_irq,
>> +};
>> +
>> +static int qe_ic_get_low_irq(struct pt_regs *regs)
>> +{
>> +	struct qe_ic_private *p_qe_ic =3D primary_qe_ic;
>> +	int irq =3D -1;
>> +
>> +	/* get the low byte of SIVEC to get the interrupt source vector.
> */
>> +	irq =3D (in_be32(&p_qe_ic->regs->qivec) >> 24) >> 2;
>> +
>> +	if (irq =3D=3D 0)		/* 0 --> no irq is pending */
>> +		return -1;
>> +
>> +	return irq + p_qe_ic->irq_offset;
>> +}
>> +
>> +static int qe_ic_get_high_irq(struct pt_regs *regs)
>> +{
>> +	struct qe_ic_private *p_qe_ic =3D primary_qe_ic;
>> +	int irq =3D -1;
>> +
>> +	/* get the high byte of SIVEC to get the interrupt source
> vector. */
>> +	irq =3D (in_be32(&p_qe_ic->regs->qhivec) >> 24) >> 2;
>> +
>> +	if (irq =3D=3D 0)		/* 0 --> no irq is pending */
>> +		return -1;
>> +
>> +	return irq + p_qe_ic->irq_offset;
>> +}
>> +
>> +static irqreturn_t qe_ic_cascade_low(int irq, void *dev_id,
>> +				     struct pt_regs *regs)
>> +{
>> +	while ((irq =3D qe_ic_get_low_irq(regs)) >=3D 0)
>> +		__do_IRQ(irq, regs);
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static irqreturn_t qe_ic_cascade_high(int irq, void *dev_id,
>> +				      struct pt_regs *regs)
>> +{
>> +	while ((irq =3D qe_ic_get_high_irq(regs)) >=3D 0)
>> +		__do_IRQ(irq, regs);
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static struct irqaction qe_ic_low_irqaction =3D {
>> +	.handler =3D qe_ic_cascade_low,
>> +	.flags =3D SA_INTERRUPT,
>> +	.mask =3D CPU_MASK_NONE,
>> +	.name =3D "qe_ic_cascade_low",
>> +};
>> +
>> +static struct irqaction qe_ic_high_irqaction =3D {
>> +	.handler =3D qe_ic_cascade_high,
>> +	.flags =3D SA_INTERRUPT,
>> +	.mask =3D CPU_MASK_NONE,
>> +	.name =3D "qe_ic_cascade_high",
>> +};
>> +
>> +int qe_ic_init(phys_addr_t phys_addr,
>> +	       unsigned int flags, unsigned int irq_offset)
>> +{
>> +	struct qe_ic_map *regs;
>> +	u8 grp, pri, shift =3D 0;
>> +	u32 tmp_qicr =3D 0, tmp_qricr =3D 0, tmp_qicnr =3D 0, tmp_mask;
>> +	int i, high_hctive =3D 0;
>> +	const u32 high_signal =3D 2;
>> +
>> +	primary_qe_ic =3D &p_qe_ic;
>> +	memset(primary_qe_ic, 0, sizeof(struct qe_ic_private));
>> +
>> +	/* initialize QE interrupt controller registers */
>> +	primary_qe_ic->regs =3D regs =3D
>> +	    (struct qe_ic_map *)ioremap(phys_addr, QE_IC_SIZE);
>> +	primary_qe_ic->irq_offset =3D irq_offset;
>> +
>> +	/* default priority scheme is grouped. If spread mode is    */
>> +	/* required, configure sicr accordingly.                    */
>> +	if (flags & QE_IC_SPREADMODE_GRP_W)
>> +		tmp_qicr |=3D QICR_GWCC;
>> +	if (flags & QE_IC_SPREADMODE_GRP_X)
>> +		tmp_qicr |=3D QICR_GXCC;
>> +	if (flags & QE_IC_SPREADMODE_GRP_Y)
>> +		tmp_qicr |=3D QICR_GYCC;
>> +	if (flags & QE_IC_SPREADMODE_GRP_Z)
>> +		tmp_qicr |=3D QICR_GZCC;
>> +	if (flags & QE_IC_SPREADMODE_GRP_RISCA)
>> +		tmp_qicr |=3D QICR_GRTA;
>> +	if (flags & QE_IC_SPREADMODE_GRP_RISCB)
>> +		tmp_qicr |=3D QICR_GRTB;
>> +
>> +	/* choose destination signal for highest priority interrupt */
>> +	if (flags & QE_IC_HIGH_SIGNAL) {
>> +		tmp_qicr |=3D (high_signal << QICR_HPIT_SHIFT);
>> +		high_hctive =3D 1;
>> +	}
>> +
>> +	out_be32(&regs->qicr, tmp_qicr);
>> +
>> +	tmp_mask =3D (1 << QE_IC_GRP_W_DEST_SIGNAL_SHIFT);
>> +	/* choose destination signal for highest priority interrupt in
> each
>> +	 * group */
>> +	for (grp =3D 0; grp < NUM_OF_QE_IC_GROUPS; grp++) {
>> +		/* the first 2 priorities in each group have a choice of
>> +		 * destination signal */
>> +		for (pri =3D 0; pri <=3D 1; pri++) {
>> +			if (flags & ((tmp_mask << (grp << 1)) << pri)) {
>> +				/* indicate whether QE High signal is
>> +				 * required */
>> +				if (!high_hctive)
>> +					high_hctive =3D 1;
>> +
>> +				/* The location of the bits relevant to
>> +				 * priority 0 in the   */
>> +				/* registers is always 2 bits left
> comparing
>> +				 * to priority 1. */
>> +				if (pri =3D=3D 0)
>> +					shift =3D 2;
>> +
>> +				switch (grp) {
>> +				case (QE_IC_GRP_W):
>> +					shift +=3D QICNR_WCC1T_SHIFT;
>> +					tmp_qicnr |=3D high_signal <<
> shift;
>> +					break;
>> +				case (QE_IC_GRP_X):
>> +					shift +=3D QICNR_XCC1T_SHIFT;
>> +					tmp_qicnr |=3D high_signal <<
> shift;
>> +					break;
>> +				case (QE_IC_GRP_Y):
>> +					shift +=3D QICNR_YCC1T_SHIFT;
>> +					tmp_qicnr |=3D high_signal <<
> shift;
>> +					break;
>> +				case (QE_IC_GRP_Z):
>> +					shift +=3D QICNR_ZCC1T_SHIFT;
>> +					tmp_qicnr |=3D high_signal <<
> shift;
>> +					break;
>> +				case (QE_IC_GRP_RISCA):
>> +					shift +=3D QRICR_RTA1T_SHIFT;
>> +					tmp_qricr |=3D high_signal <<
> shift;
>> +					break;
>> +				case (QE_IC_GRP_RISCB):
>> +					shift +=3D QRICR_RTB1T_SHIFT;
>> +					tmp_qricr |=3D high_signal <<
> shift;
>> +					break;
>> +				default:
>> +					break;
>> +				}
>> +			}
>> +		}
>> +	}
>> +
>> +	if (tmp_qicnr)
>> +		out_be32(&regs->qicnr, tmp_qicnr);
>> +	if (tmp_qricr)
>> +		out_be32(&regs->qricr, tmp_qricr);
>> +
>> +	for (i =3D primary_qe_ic->irq_offset;
>> +	     i < (NR_QE_IC_INTS + primary_qe_ic->irq_offset); i++) {
>> +		irq_desc[i].handler =3D &qe_ic;
>> +		irq_desc[i].status =3D IRQ_LEVEL;
>> +	}
>> +
>> +	/* register QE_IC interrupt controller in the a higher hirarchy
>> +	 * controller */
>> +	setup_irq(IRQ_QE_LOW, &qe_ic_low_irqaction);
>> +
>> +	if (high_hctive)
>> +		/* register QE_IC high interrupt source in the higher
>> +		 * hirarchy controller */
>> +		setup_irq(IRQ_QE_HIGH, &qe_ic_high_irqaction);
>> +
>> +	printk("QE IC (%d IRQ sources) at %p\n", NR_QE_IC_INTS,
>> +	       primary_qe_ic->regs);
>> +	return 0;
>> +}
>> +
>> +void qe_ic_free(void)
>> +{
>> +}
>> +
>> +void qe_ic_enable_irq(unsigned int irq)
>> +{
>> +	struct qe_ic_private *p_qe_ic =3D primary_qe_ic;
>> +	unsigned int src =3D irq - p_qe_ic->irq_offset;
>> +	u32 qimr;
>> +
>> +	if (qe_ic_info[src].qimr) {
>> +		qimr =3D in_be32(&((struct qe_ic_private
> *)p_qe_ic)->regs->qimr);
>> +		out_be32(&((struct qe_ic_private *)p_qe_ic)->regs->qimr,
>> +			 qimr | (qe_ic_info[src].mask));
>> +	} else {
>> +		qimr =3D in_be32(&((struct qe_ic_private
> *)p_qe_ic)->regs->qrimr);
>> +		out_be32(&((struct qe_ic_private
> *)p_qe_ic)->regs->qrimr,
>> +			 qimr | (qe_ic_info[src].mask));
>> +	}
>> +}
>> +
>> +void qe_ic_disable_irq(unsigned int irq)
>> +{
>> +	struct qe_ic_private *p_qe_ic =3D primary_qe_ic;
>> +	unsigned int src =3D irq - p_qe_ic->irq_offset;
>> +	u32 qimr;
>> +
>> +	if (qe_ic_info[src].qimr) {
>> +		qimr =3D in_be32(&((struct qe_ic_private
> *)p_qe_ic)->regs->qimr);
>> +		out_be32(&((struct qe_ic_private *)p_qe_ic)->regs->qimr,
>> +			 qimr & ~(qe_ic_info[src].mask));
>> +	} else {
>> +		qimr =3D in_be32(&((struct qe_ic_private
> *)p_qe_ic)->regs->qrimr);
>> +		out_be32(&((struct qe_ic_private
> *)p_qe_ic)->regs->qrimr,
>> +			 qimr & ~(qe_ic_info[src].mask));
>> +	}
>> +}
>> +
>> +void qe_ic_disable_irq_and_ack(unsigned int irq)
>> +{
>> +	qe_ic_disable_irq(irq);
>> +}
>> +
>> +void qe_ic_end_irq(unsigned int irq)
>> +{
>> +
>> +	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))
>> +	    && irq_desc[irq].action)
>> +		qe_ic_enable_irq(irq);
>> +}
>> +
>> +void qe_ic_modify_highest_priority(unsigned int irq)
>> +{
>> +	struct qe_ic_private *p_qe_ic =3D primary_qe_ic;
>> +	unsigned int src =3D irq - p_qe_ic->irq_offset;
>> +	u32 tmp_qicr =3D 0;
>> +
>> +	tmp_qicr =3D in_be32(&p_qe_ic->regs->qicr);
>> +	out_be32(&p_qe_ic->regs->qicr, (u32) (tmp_qicr | ((u8) src <<
> 24)));
>> +}
>> +
>> +void qe_ic_modify_priority(enum qe_ic_grp_id grp,
>> +			   unsigned int pri0,
>> +			   unsigned int pri1,
>> +			   unsigned int pri2,
>> +			   unsigned int pri3,
>> +			   unsigned int pri4,
>> +			   unsigned int pri5,
>> +			   unsigned int pri6, unsigned int pri7)
>> +{
>> +	struct qe_ic_private *p_qe_ic =3D primary_qe_ic;
>> +	volatile u32 *p_qip =3D 0;
>> +	u32 tmp_qip =3D 0;
>> +	u8 tmp_array[8] =3D { 0, 0, 0, 0, 0, 0, 0, 0 };
>> +	signed char code_array[8], i =3D 0, j =3D 0;
>> +
>> +	code_array[0] =3D (signed char)(pri0 ? qe_ic_info[pri0].pri_code :
>
>> -1);
>> +	code_array[1] =3D (signed char)(pri1 ? qe_ic_info[pri1].pri_code :
>
>> -1);
>> +	code_array[2] =3D (signed char)(pri2 ? qe_ic_info[pri2].pri_code :
>
>> -1);
>> +	code_array[3] =3D (signed char)(pri3 ? qe_ic_info[pri3].pri_code :
>
>> -1);
>> +	code_array[4] =3D (signed char)(pri4 ? qe_ic_info[pri4].pri_code :
>
>> -1);
>> +	code_array[5] =3D (signed char)(pri5 ? qe_ic_info[pri5].pri_code :
>
>> -1);
>> +	code_array[6] =3D (signed char)(pri6 ? qe_ic_info[pri6].pri_code :
>
>> -1);
>> +	code_array[7] =3D (signed char)(pri7 ? qe_ic_info[pri7].pri_code :
>
>> -1);
>> +
>> +	for (i =3D 0; i < 8; i++) {
>> +		if (code_array[i] =3D=3D -1)
>> +			break;
>> +		tmp_array[code_array[i]] =3D 1;
>> +	}
>> +
>> +	for (; i < 8; i++) {
>> +		while (tmp_array[j] && j < 8)
>> +			j++;
>> +		code_array[i] =3D j;
>> +		tmp_array[j] =3D 1;
>> +	}
>> +
>> +	tmp_qip =3D (u32) (code_array[0] << QIPCC_SHIFT_PRI0 |
>> +			 code_array[1] << QIPCC_SHIFT_PRI1 |
>> +			 code_array[2] << QIPCC_SHIFT_PRI2 |
>> +			 code_array[3] << QIPCC_SHIFT_PRI3 |
>> +			 code_array[4] << QIPCC_SHIFT_PRI4 |
>> +			 code_array[5] << QIPCC_SHIFT_PRI5 |
>> +			 code_array[6] << QIPCC_SHIFT_PRI6 |
>> +			 code_array[7] << QIPCC_SHIFT_PRI7);
>> +
>> +	switch (grp) {
>> +	case (QE_IC_GRP_W):
>> +		p_qip =3D &(p_qe_ic->regs->qipwcc);
>> +		break;
>> +	case (QE_IC_GRP_X):
>> +		p_qip =3D &(p_qe_ic->regs->qipxcc);
>> +		break;
>> +	case (QE_IC_GRP_Y):
>> +		p_qip =3D &(p_qe_ic->regs->qipycc);
>> +		break;
>> +	case (QE_IC_GRP_Z):
>> +		p_qip =3D &(p_qe_ic->regs->qipzcc);
>> +		break;
>> +	case (QE_IC_GRP_RISCA):
>> +		p_qip =3D &(p_qe_ic->regs->qiprta);
>> +		break;
>> +	case (QE_IC_GRP_RISCB):
>> +		p_qip =3D &(p_qe_ic->regs->qiprtb);
>> +		break;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	out_be32(p_qip, tmp_qip);
>> +}
>> +
>> +void qe_ic_dump_regs(void)
>> +{
>> +	struct qe_ic_private *p_qe_ic =3D primary_qe_ic;
>> +
>> +	printk(KERN_INFO "QE IC registars:\n");
>> +	printk(KERN_INFO "Base address: 0x%08x\n", (u32) p_qe_ic->regs);
>> +	printk(KERN_INFO "qicr  : addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & p_qe_ic->regs->qicr,
> in_be32(&p_qe_ic->regs->qicr));
>> +	printk(KERN_INFO "qivec : addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & p_qe_ic->regs->qivec, in_be32(&p_qe_ic->regs-
>>> qivec));
>> +	printk(KERN_INFO "qripnr: addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & p_qe_ic->regs->qripnr, in_be32(&p_qe_ic->regs-
>>> qripnr));
>> +	printk(KERN_INFO "qipnr : addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & p_qe_ic->regs->qipnr, in_be32(&p_qe_ic->regs-
>>> qipnr));
>> +	printk(KERN_INFO "qipxcc: addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & p_qe_ic->regs->qipxcc, in_be32(&p_qe_ic->regs-
>>> qipxcc));
>> +	printk(KERN_INFO "qipycc: addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & p_qe_ic->regs->qipycc, in_be32(&p_qe_ic->regs-
>>> qipycc));
>> +	printk(KERN_INFO "qipwcc: addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & p_qe_ic->regs->qipwcc, in_be32(&p_qe_ic->regs-
>>> qipwcc));
>> +	printk(KERN_INFO "qipzcc: addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & p_qe_ic->regs->qipzcc, in_be32(&p_qe_ic->regs-
>>> qipzcc));
>> +	printk(KERN_INFO "qimr  : addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & p_qe_ic->regs->qimr,
> in_be32(&p_qe_ic->regs->qimr));
>> +	printk(KERN_INFO "qrimr : addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & p_qe_ic->regs->qrimr, in_be32(&p_qe_ic->regs-
>>> qrimr));
>> +	printk(KERN_INFO "qicnr : addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & p_qe_ic->regs->qicnr, in_be32(&p_qe_ic->regs-
>>> qicnr));
>> +	printk(KERN_INFO "qiprta: addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & p_qe_ic->regs->qiprta, in_be32(&p_qe_ic->regs-
>>> qiprta));
>> +	printk(KERN_INFO "qiprtb: addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & p_qe_ic->regs->qiprtb, in_be32(&p_qe_ic->regs-
>>> qiprtb));
>> +	printk(KERN_INFO "qricr : addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & p_qe_ic->regs->qricr, in_be32(&p_qe_ic->regs-
>>> qricr));
>> +	printk(KERN_INFO "qhivec: addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & p_qe_ic->regs->qhivec, in_be32(&p_qe_ic->regs-
>>> qhivec));
>> +}
>> 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..6662ad2
>> --- /dev/null
>> +++ b/arch/powerpc/sysdev/qe_lib/qe_ic.h
>> @@ -0,0 +1,83 @@
>> +/*
>> + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights
>> reserved.
>> + *
>> + * Author: Shlomi Gridish <gridish@freescale.com>
>> + *
>> + * Description:
>> + * QE IC private definitions and structure.
>> + *
>> + * Changelog:
>> + * Jun 21, 2006 Li Yang <LeoLi@freescale.com>
>> + * - Style fix; port to powerpc arch
>> + *
>> + * 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 __QE_IC_H__
>> +#define __QE_IC_H__
>> +
>> +typedef struct qe_ic_map {
>> +	volatile u32 qicr;
>> +	volatile u32 qivec;
>> +	volatile u32 qripnr;
>> +	volatile u32 qipnr;
>> +	volatile u32 qipxcc;
>> +	volatile u32 qipycc;
>> +	volatile u32 qipwcc;
>> +	volatile u32 qipzcc;
>> +	volatile u32 qimr;
>> +	volatile u32 qrimr;
>> +	volatile u32 qicnr;
>> +	volatile u8 res0[0x4];
>> +	volatile u32 qiprta;
>> +	volatile u32 qiprtb;
>> +	volatile u8 res1[0x4];
>> +	volatile u32 qricr;
>> +	volatile u8 res2[0x20];
>> +	volatile u32 qhivec;
>> +	volatile u8 res3[0x1C];
>> +} __attribute__ ((packed)) qe_ic_map_t;
>> +
>> +
>> +#define QE_IC_SIZE sizeof(struct qe_ic_map)
>> +
>> +/* Interrupt priority registers */
>> +#define QIPCC_SHIFT_PRI0        29
>> +#define QIPCC_SHIFT_PRI1        26
>> +#define QIPCC_SHIFT_PRI2        23
>> +#define QIPCC_SHIFT_PRI3        20
>> +#define QIPCC_SHIFT_PRI4        13
>> +#define QIPCC_SHIFT_PRI5        10
>> +#define QIPCC_SHIFT_PRI6        7
>> +#define QIPCC_SHIFT_PRI7        4
>> +
>> +/* QICR priority modes */
>> +#define QICR_GWCC               0x00040000
>> +#define QICR_GXCC               0x00020000
>> +#define QICR_GYCC               0x00010000
>> +#define QICR_GZCC               0x00080000
>> +#define QICR_GRTA               0x00200000
>> +#define QICR_GRTB               0x00400000
>> +#define QICR_HPIT_SHIFT         8
>> +
>> +/* QICNR */
>> +#define QICNR_WCC1T_SHIFT       20
>> +#define QICNR_ZCC1T_SHIFT       28
>> +#define QICNR_YCC1T_SHIFT       12
>> +#define QICNR_XCC1T_SHIFT       4
>> +
>> +/* QRICR */
>> +#define QRICR_RTA1T_SHIFT       20
>> +#define QRICR_RTB1T_SHIFT       28
>> +
>> +struct qe_ic_private {
>> +	struct qe_ic_map *regs;
>> +	unsigned int irq_offset;
>> +} qe_ic_private_t;
>> +
>> +extern struct hw_interrupt_type qe_ic;
>> +extern int qe_ic_get_irq(struct pt_regs *regs);
>> +
>> +#endif				/* __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..a943c27
>> --- /dev/null
>> +++ b/arch/powerpc/sysdev/qe_lib/qe_io.c
>> @@ -0,0 +1,275 @@
>> +/*
>> + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights
>> reserved.
>> + *
>> + * Author: Li Yang <LeoLi@freescale.com>
>> + *
>> + * Description:
>> + * QE Parallel I/O ports configuration routines.  Based on code from
>> + * Shlomi Gridish <gridish@freescale.com>
>> + *
>> + * Changelog:
>> + * Jun 21, 2006	Initial version
>> + *
>> + * 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 <asm/io.h>
>> +#include <asm/prom.h>
>> +#include <sysdev/fsl_soc.h>
>> +#undef DEBUG
>> +
>> +#define NUM_OF_PINS     32
>> +#define NUM_OF_PAR_IOS  7
>> +
>> +typedef struct par_io {
>> +	struct {
>> +		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 */
>> +	} io_regs[NUM_OF_PAR_IOS];
>> +} par_io_t;
>> +
>> +typedef struct qe_par_io {
>> +	u8 res[0xc];
>> +	u32 cepier;		/* QE ports interrupt event register */
>> +	u32 cepimr;		/* QE ports mask event register */
>> +	u32 cepicr;		/* QE ports control event register */
>> +} qe_par_io_t;
>> +
>> +static int qe_irq_ports[NUM_OF_PAR_IOS][NUM_OF_PINS] =3D {
>> +	/* 0-7 */          /* 8-15 */      /* 16 - 23 */     /* 24 - 31
> */
>> +	{0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0,
>> 0,0,0,0,0,1,1,0},
>> +	{0,0,0,1,0,1,0,0, 0,0,0,0,1,1,0,0, 0,0,0,0,0,0,0,0,
>> 0,0,1,1,0,0,0,0},
>> +	{0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
>> 0,0,0,1,1,1,0,0},
>> +	{0,0,0,0,0,0,0,0, 0,0,0,0,1,1,0,0, 1,1,0,0,0,0,0,0,
>> 0,0,1,1,0,0,0,0},
>> +	{0,0,0,0,0,0,0,0, 0,0,0,0,1,1,0,0, 0,0,0,0,0,0,0,0,
>> 1,1,1,1,0,0,0,1},
>> +	{0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,1,0,0,0,
>> 0,0,0,0,0,0,0,0},
>> +	{0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
> 0,0,0,0,0,0,0,1}
>> +};
>> +
>> +
>> +static u8 get_irq_num(u8 port, u8 pin)
>> +{
>> +	int i, j;
>> +	u8 num =3D 0;
>> +
>> +	if (qe_irq_ports[port][pin] =3D=3D 0)
>> +		return -1;
>> +	for (j =3D 0; j <=3D port; j++)
>> +		for (i =3D 0; i < pin; i++)
>> +			if (qe_irq_ports[j][i])
>> +				num++;
>> +	return num;
>> +}
>> +
>> +static par_io_t *par_io =3D NULL;
>> +static qe_par_io_t *qe_par_io =3D NULL;
>> +
>> +int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain,
>> +		      int assignment, int has_irq)
>> +{
>> +	u32 pinMask1bit, pinMask2bits, newMask2bits, tmp_val;
>> +
>> +	if (!par_io) {
>> +		par_io =3D (par_io_t *) ioremap(get_immrbase() + 0x1400,
>> +					      sizeof(par_io_t));
>> +		qe_par_io =3D (qe_par_io_t *) ioremap(get_immrbase() +
> 0xC00,
>> +
> sizeof(qe_par_io_t));
>> +
>> +		/* clear event bits in the event register of the QE
> ports */
>> +		out_be32(&qe_par_io->cepier, 0xFFFFFFFF);
>> +	}
>> +
>> +	/* calculate pin location for single and 2 bits  information */
>> +	pinMask1bit =3D (u32) (1 << (NUM_OF_PINS - (pin + 1)));
>> +
>> +	/* Set open drain, if required */
>> +	tmp_val =3D in_be32(&par_io->io_regs[port].cpodr);
>> +	if (open_drain)
>> +		out_be32(&par_io->io_regs[port].cpodr, pinMask1bit |
> tmp_val);
>> +	else
>> +		out_be32(&par_io->io_regs[port].cpodr, ~pinMask1bit &
> tmp_val);
>> +
>> +	/* define direction */
>> +	tmp_val =3D (pin > (NUM_OF_PINS / 2) - 1) ?
>> +	    in_be32(&par_io->io_regs[port].cpdir2) :
>> +	    in_be32(&par_io->io_regs[port].cpdir1);
>> +
>> +	/* get all bits mask for 2 bit per port */
>> +	pinMask2bits =3D (u32) (0x3 <<
>> +			      (NUM_OF_PINS -
>> +			       (pin % (NUM_OF_PINS / 2) + 1) * 2));
>> +
>> +	/* Get the final mask we need for the right definition */
>> +	newMask2bits =3D (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->io_regs[port].cpdir2,
>> +			 ~pinMask2bits & tmp_val);
>> +		tmp_val &=3D ~pinMask2bits;
>> +		out_be32(&par_io->io_regs[port].cpdir2, newMask2bits |
> tmp_val);
>> +	} else {
>> +		out_be32(&par_io->io_regs[port].cpdir1,
>> +			 ~pinMask2bits & tmp_val);
>> +		tmp_val &=3D ~pinMask2bits;
>> +		out_be32(&par_io->io_regs[port].cpdir1, newMask2bits |
> tmp_val);
>> +	}
>> +	/* define pin assignment */
>> +	tmp_val =3D (pin > (NUM_OF_PINS / 2) - 1) ?
>> +	    in_be32(&par_io->io_regs[port].cppar2) :
>> +	    in_be32(&par_io->io_regs[port].cppar1);
>> +
>> +	newMask2bits =3D (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->io_regs[port].cppar2,
>> +			 ~pinMask2bits & tmp_val);
>> +		tmp_val &=3D ~pinMask2bits;
>> +		out_be32(&par_io->io_regs[port].cppar2, newMask2bits |
> tmp_val);
>> +	} else {
>> +		out_be32(&par_io->io_regs[port].cppar1,
>> +			 ~pinMask2bits & tmp_val);
>> +		tmp_val &=3D ~pinMask2bits;
>> +		out_be32(&par_io->io_regs[port].cppar1, newMask2bits |
> tmp_val);
>> +	}
>> +
>> +	/* Set interrupt mask if the pin generates interrupt */
>> +	if (has_irq) {
>> +		int irq =3D get_irq_num(port, pin);
>> +		u32 mask =3D 0;
>> +
>> +		if (irq =3D=3D -1) {
>> +			printk(KERN_WARNING "Port %d, pin %d is can't be
> "
>> +					"interrupt\n", port, pin);
>> +			return -EINVAL;
>> +		}
>> +		mask =3D 0x80000000 >> irq;
>> +
>> +		tmp_val =3D in_be32(&qe_par_io->cepimr);
>> +		out_be32(&qe_par_io->cepimr, mask | 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 >=3D NUM_OF_PAR_IOS)
>> +		return -EINVAL;
>> +	if (pin >=3D NUM_OF_PINS)
>> +		return -EINVAL;
>> +	/* calculate pin location */
>> +	pin_mask =3D (u32) (1 << (NUM_OF_PINS - 1 - pin));
>> +
>> +	tmp_val =3D in_be32(&par_io->io_regs[port].cpdata);
>> +
>> +	if (val =3D=3D 0)		/* clear  */
>> +		out_be32(&par_io->io_regs[port].cpdata, ~pin_mask &
> tmp_val);
>> +	else			/* set */
>> +		out_be32(&par_io->io_regs[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;
>> +=09
>> +	ph =3D (phandle *) get_property(np, "pio-handle", NULL);
>> +	if (ph =3D=3D 0) {
>> +		printk(KERN_ERR "pio-handle not available \n");
>> +		return -1;
>> +	}
>> +	=09
>> +	pio =3D of_find_node_by_phandle(*ph);
>> +
>> +	pio_map =3D (unsigned int *)
>> +		get_property(pio, "pio-map", &pio_map_len);
>> +	if (pio_map =3D=3D NULL) {
>> +		printk(KERN_ERR "pio-map is not set! \n");
>> +		return -1;
>> +	}
>> +	pio_map_len /=3D sizeof(unsigned int);
>> +	if ((pio_map_len % 6) !=3D 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 +=3D 6;
>> +		pio_map_len -=3D 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 =3D 0; i < NUM_OF_PAR_IOS; i++) {
>> +		printk(KERN_INFO "cpodr[%d] : addr - 0x%08x, val -
> 0x%08x\n",
>> +		       i, (u32) & par_io->io_regs[i].cpodr,
>> +		       in_be32(&par_io->io_regs[i].cpodr));
>> +		printk(KERN_INFO "cpdata[%d]: addr - 0x%08x, val -
> 0x%08x\n",
>> +		       i, (u32) & par_io->io_regs[i].cpdata,
>> +		       in_be32(&par_io->io_regs[i].cpdata));
>> +		printk(KERN_INFO "cpdir1[%d]: addr - 0x%08x, val -
> 0x%08x\n",
>> +		       i, (u32) & par_io->io_regs[i].cpdir1,
>> +		       in_be32(&par_io->io_regs[i].cpdir1));
>> +		printk(KERN_INFO "cpdir2[%d]: addr - 0x%08x, val -
> 0x%08x\n",
>> +		       i, (u32) & par_io->io_regs[i].cpdir2,
>> +		       in_be32(&par_io->io_regs[i].cpdir2));
>> +		printk(KERN_INFO "cppar1[%d]: addr - 0x%08x, val -
> 0x%08x\n",
>> +		       i, (u32) & par_io->io_regs[i].cppar1,
>> +		       in_be32(&par_io->io_regs[i].cppar1));
>> +		printk(KERN_INFO "cppar2[%d]: addr - 0x%08x, val -
> 0x%08x\n",
>> +		       i, (u32) & par_io->io_regs[i].cppar2,
>> +		       in_be32(&par_io->io_regs[i].cppar2));
>> +	}
>> +
>> +	printk(KERN_INFO "QE PAR IO registars:\n");
>> +	printk(KERN_INFO "Base address: 0x%08x\n", (u32) qe_par_io);
>> +	printk(KERN_INFO "cepier : addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & qe_par_io->cepier, in_be32(&qe_par_io->cepier));
>> +	printk(KERN_INFO "cepimr : addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & qe_par_io->cepimr, in_be32(&qe_par_io->cepimr));
>> +	printk(KERN_INFO "cepicr : addr - 0x%08x, val - 0x%08x\n",
>> +	       (u32) & qe_par_io->cepicr, in_be32(&qe_par_io->cepicr));
>> +}
>> +
>> +EXPORT_SYMBOL(dump_par_io);
>> +#endif
>> -
>> To unsubscribe from this list: send the line "unsubscribe linux-
>> kernel" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>> Please read the FAQ at  http://www.tux.org/lkml/
>

^ permalink raw reply	[flat|nested] 6+ messages in thread
* [PATCH 3/7] powerpc: Add QE library qe_lib--common files
@ 2006-06-28 14:23 Li Yang-r58472
  2006-06-28 17:09 ` Christoph Hellwig
  2006-06-28 19:19 ` Kumar Gala
  0 siblings, 2 replies; 6+ messages in thread
From: Li Yang-r58472 @ 2006-06-28 14:23 UTC (permalink / raw)
  To: 'Paul Mackerras'
  Cc: Gridish Shlomi-RM96313, linuxppc-dev, Phillips Kim-R1AAHA,
	'linux-kernel@vger.kernel.org', Chu hanjin-r52514


Signed-off-by: Shlomi Gridish <gridish@freescale.com>
Signed-off-by: Li Yang <leoli@freescale.com>
Signed-off-by: Kim Phillips <kim.phillips@freescale.com>

---
 arch/powerpc/Kconfig                   |   12 
 arch/powerpc/sysdev/Makefile           |    1 
 arch/powerpc/sysdev/ipic.c             |    2 
 arch/powerpc/sysdev/qe_lib/Kconfig     |  315 +++++++++++++
 arch/powerpc/sysdev/qe_lib/Makefile    |    8 
 arch/powerpc/sysdev/qe_lib/mm.c        |  770 ++++++++++++++++++++++++++++++++
 arch/powerpc/sysdev/qe_lib/mm.h        |    6 
 arch/powerpc/sysdev/qe_lib/qe.c        |  181 ++++++++
 arch/powerpc/sysdev/qe_lib/qe_common.c |  401 +++++++++++++++++
 arch/powerpc/sysdev/qe_lib/qe_ic.c     |  487 ++++++++++++++++++++
 arch/powerpc/sysdev/qe_lib/qe_ic.h     |   83 +++
 arch/powerpc/sysdev/qe_lib/qe_io.c     |  275 +++++++++++
 12 files changed, 2541 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 6729c98..6d4fc0b 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -334,6 +334,16 @@ config APUS
 	  <http://linux-apus.sourceforge.net/>.
 endchoice
 
+config QUICC_ENGINE
+	bool
+	depends on MPC836x
+	default y
+	help
+	  The QE(QUICC Engine) is a new generation of coprocessor on 
+	  Freescale embedded CPUs(like CPM  in older chips).  Selecting
+	  this option means that you wish to build a kernel for a machine
+	  with QE coprocessor on it.
+
 config PPC_PSERIES
 	depends on PPC_MULTIPLATFORM && PPC64
 	bool "  IBM pSeries & new (POWER5-based) iSeries"
@@ -993,6 +1003,8 @@ # XXX source "arch/ppc/8xx_io/Kconfig"
 
 # XXX source "arch/ppc/8260_io/Kconfig"
 
+source "arch/powerpc/sysdev/qe_lib/Kconfig"
+
 source "arch/powerpc/platforms/iseries/Kconfig"
 
 source "lib/Kconfig"
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 4c2b356..cd1d5cc 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_U3_DART)		+= dart_iommu.o
 obj-$(CONFIG_MMIO_NVRAM)	+= mmio_nvram.o
 obj-$(CONFIG_PPC_83xx)		+= ipic.o
 obj-$(CONFIG_FSL_SOC)		+= fsl_soc.o
+obj-$(CONFIG_QUICC_ENGINE)	+= qe_lib/
diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c
index 8f01e0f..dbeccba 100644
--- a/arch/powerpc/sysdev/ipic.c
+++ b/arch/powerpc/sysdev/ipic.c
@@ -537,12 +537,14 @@ void ipic_set_highest_priority(unsigned 
 
 void ipic_set_default_priority(void)
 {
+#ifdef CONFIG_MPC834x
 	ipic_set_priority(MPC83xx_IRQ_TSEC1_TX, 0);
 	ipic_set_priority(MPC83xx_IRQ_TSEC1_RX, 1);
 	ipic_set_priority(MPC83xx_IRQ_TSEC1_ERROR, 2);
 	ipic_set_priority(MPC83xx_IRQ_TSEC2_TX, 3);
 	ipic_set_priority(MPC83xx_IRQ_TSEC2_RX, 4);
 	ipic_set_priority(MPC83xx_IRQ_TSEC2_ERROR, 5);
+#endif
 	ipic_set_priority(MPC83xx_IRQ_USB2_DR, 6);
 	ipic_set_priority(MPC83xx_IRQ_USB2_MPH, 7);
 
diff --git a/arch/powerpc/sysdev/qe_lib/Kconfig b/arch/powerpc/sysdev/qe_lib/Kconfig
new file mode 100644
index 0000000..6105237
--- /dev/null
+++ b/arch/powerpc/sysdev/qe_lib/Kconfig
@@ -0,0 +1,315 @@
+#
+# QE Communication options
+#
+
+menu "QE Options"
+	depends on QUICC_ENGINE
+	
+config UCC1
+	bool "Enable QE UCC1"
+
+choice
+	prompt "UCC1 speed selection"
+	depends on UCC1
+	default UCC1_SLOW
+
+	config UCC1_SLOW
+		bool "UCC1 is slow"
+	config UCC1_FAST
+		bool "UCC1 is fast"
+endchoice
+
+menu "UCC1 Protocols options"
+	depends on UCC1
+
+	choice
+		prompt "UCC1 Slow Protocols selection"
+		depends on UCC1_SLOW
+		default UCC1_UART
+
+		config UCC1_UART
+			bool "UCC1 is UART"
+	endchoice
+
+	choice
+		prompt "UCC1 Fast Protocols selection"
+		depends on UCC1_FAST
+		default UCC1_GETH
+
+		config UCC1_GETH
+			bool "UCC1 is GETH"
+	endchoice
+endmenu
+
+config UCC2
+	bool "Enable QE UCC2"
+
+choice
+	prompt "UCC2 speed selection"
+	depends on UCC2
+	default UCC2_SLOW
+
+	config UCC2_SLOW
+		bool "UCC2 is slow"
+	config UCC2_FAST
+		bool "UCC2 is fast"
+endchoice
+
+menu "UCC2 Protocols options"
+	depends on UCC2
+
+	choice
+		prompt "UCC2 Slow Protocols selection"
+		depends on UCC2_SLOW
+		default UCC2_UART
+
+		config UCC2_UART
+			bool "UCC2 is UART"
+	endchoice
+
+	choice
+		prompt "UCC2 Fast Protocols selection"
+		depends on UCC2_FAST
+		default UCC2_GETH
+
+		config UCC2_GETH
+			bool "UCC2 is GETH"
+	endchoice
+endmenu
+
+config UCC3
+	bool "Enable QE UCC3"
+
+choice
+	prompt "UCC3 speed selection"
+	depends on UCC3
+	default UCC3_SLOW
+
+	config UCC3_SLOW
+		bool "UCC3 is slow"
+	config UCC3_FAST
+		bool "UCC3 is fast"
+endchoice
+
+menu "UCC3 Protocols options"
+	depends on UCC3
+
+	choice
+		prompt "UCC3 Slow Protocols selection"
+		depends on UCC3_SLOW
+		default UCC3_UART
+
+		config UCC3_UART
+			bool "UCC3 is UART"
+        endchoice
+
+	config UCC3_GETH
+		depends on UCC3_FAST
+		bool "UCC3 is GETH"
+
+	config UCC3_ATM
+		depends on UCC3_FAST && !UCC3_GETH
+		tristate "UCC3 is ATM"
+endmenu
+
+config UCC4
+	bool "Enable QE UCC4"
+
+choice
+	prompt "UCC4 speed selection"
+	depends on UCC4
+	default UCC4_SLOW
+
+	config UCC4_SLOW
+		bool "UCC4 is slow"
+	config UCC4_FAST
+		bool "UCC4 is fast"
+endchoice
+
+menu "UCC4 Protocols options"
+	depends on UCC4
+
+	choice
+		prompt "UCC4 Slow Protocols selection"
+		depends on UCC4_SLOW
+		default UCC4_UART
+
+		config UCC4_UART
+			bool "UCC4 is UART"
+	endchoice
+
+	choice
+		prompt "UCC4 Fast Protocols selection"
+		depends on UCC4_FAST
+		default UCC4_GETH
+
+		config UCC4_GETH
+			bool "UCC4 is GETH"
+	endchoice
+endmenu
+
+config UCC5
+	bool "Enable QE UCC5"
+
+choice
+	prompt "UCC5 speed selection"
+	depends on UCC5
+	default UCC5_SLOW
+
+	config UCC5_SLOW
+		bool "UCC5 is slow"
+	config UCC5_FAST
+		bool "UCC5 is fast"
+endchoice
+
+menu "UCC5 Protocols options"
+	depends on UCC5
+
+	choice
+		prompt "UCC5 Slow Protocols selection"
+		depends on UCC5_SLOW
+		default UCC5_UART
+
+		config UCC5_UART
+			bool "UCC5 is UART"
+	endchoice
+
+	choice
+		prompt "UCC5 Fast Protocols selection"
+		depends on UCC5_FAST
+		default UCC5_GETH
+
+		config UCC5_GETH
+			bool "UCC5 is GETH"
+	endchoice
+endmenu
+
+config UCC6
+	bool "Enable QE UCC6"
+
+choice
+	prompt "UCC6 speed selection"
+	depends on UCC6
+	default UCC6_SLOW
+
+	config UCC6_SLOW
+		bool "UCC6 is slow"
+	config UCC6_FAST
+		bool "UCC6 is fast"
+endchoice
+
+menu "UCC6 Protocols options"
+	depends on UCC6
+
+	choice
+		prompt "UCC6 Slow Protocols selection"
+		depends on UCC6_SLOW
+		default UCC6_UART
+
+		config UCC6_UART
+			bool "UCC6 is UART"
+	endchoice
+
+	choice
+		prompt "UCC6 Fast Protocols selection"
+		depends on UCC6_FAST
+		default UCC6_GETH
+
+		config UCC6_GETH
+			bool "UCC6 is GETH"
+	endchoice
+endmenu
+
+config UCC7
+	bool "Enable QE UCC7"
+
+choice
+	prompt "UCC7 speed selection"
+	depends on UCC7
+	default UCC7_SLOW
+
+	config UCC7_SLOW
+		bool "UCC7 is slow"
+	config UCC7_FAST
+		bool "UCC7 is fast"
+endchoice
+
+menu "UCC7 Protocols options"
+	depends on UCC7
+
+	choice
+		prompt "UCC7 Slow Protocols selection"
+		depends on UCC7_SLOW
+		default UCC7_UART
+
+		config UCC7_UART
+			bool "UCC7 is UART"
+	endchoice
+
+	choice
+		prompt "UCC7 Fast Protocols selection"
+		depends on UCC7_FAST
+		default UCC7_GETH
+
+		config UCC7_GETH
+			bool "UCC7 is GETH"
+	endchoice
+endmenu
+
+config UCC8
+	bool "Enable QE UCC8"
+
+choice
+	prompt "UCC8 speed selection"
+	depends on UCC8
+	default UCC8_SLOW
+
+	config UCC8_SLOW
+		bool "UCC8 is slow"
+	config UCC8_FAST
+		bool "UCC8 is fast"
+endchoice
+
+menu "UCC8 Protocols options"
+	depends on UCC8
+
+	choice
+		prompt "UCC8 Slow Protocols selection"
+		depends on UCC8_SLOW
+		default UCC8_UART
+
+		config UCC8_UART
+			bool "UCC8 is UART"
+	endchoice
+
+	choice
+		prompt "UCC8 Fast Protocols selection"
+		depends on UCC8_FAST
+		default UCC8_GETH
+
+		config UCC8_GETH
+			bool "UCC8 is GETH"
+	endchoice
+endmenu
+
+config UCC
+	depends on UCC1 || UCC2 || UCC3 || UCC4 || UCC5 || UCC6 || UCC7 || UCC8
+	default y
+	bool
+
+config UCC_SLOW
+	depends on UCC1_SLOW || UCC2_SLOW || UCC3_SLOW || UCC4_SLOW || UCC5_SLOW || UCC6_SLOW || UCC7_SLOW || UCC8_SLOW
+	default y
+	bool
+
+config UCC_FAST
+	depends on UCC1_FAST || UCC2_FAST || UCC3_FAST || UCC4_FAST || UCC5_FAST || UCC6_FAST || UCC7_FAST || UCC8_FAST
+	default y
+	bool
+
+config UCC_GETH_CONF
+	depends on UCC1_GETH || UCC2_GETH || UCC3_GETH || UCC4_GETH || UCC5_GETH || UCC6_GETH || UCC7_GETH || UCC8_GETH
+	default y
+	bool
+endmenu
+
diff --git a/arch/powerpc/sysdev/qe_lib/Makefile b/arch/powerpc/sysdev/qe_lib/Makefile
new file mode 100644
index 0000000..c04a70c
--- /dev/null
+++ b/arch/powerpc/sysdev/qe_lib/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the linux ppc-specific parts of QE
+#
+obj-$(CONFIG_QUICC_ENGINE)+= qe_common.o mm.o qe.o qe_ic.o qe_io.o
+
+obj-$(CONFIG_UCC)	+= ucc.o
+obj-$(CONFIG_UCC_SLOW)	+= ucc_slow.o
+obj-$(CONFIG_UCC_FAST)	+= ucc_fast.o ucc_slow.o
diff --git a/arch/powerpc/sysdev/qe_lib/mm.c b/arch/powerpc/sysdev/qe_lib/mm.c
new file mode 100644
index 0000000..58984ba
--- /dev/null
+++ b/arch/powerpc/sysdev/qe_lib/mm.c
@@ -0,0 +1,770 @@
+/*
+ * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved.
+ *
+ * Author: Shlomi Gridish <gridish@freescale.com>
+ *
+ * Description:
+ * QE Memory Manager.
+ *
+ * Changelog: 
+ * Jun 28, 2006 Li Yang <LeoLi@freescale.com>
+ * - Reorganized as qe_lib
+ * - Merged to powerpc arch; add device tree support
+ * - Style fixes
+ *
+ * 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 <asm/irq.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#include "mm.h"
+
+#define MAX_ALIGNMENT   20
+#define MAX_NAME_LEN    50
+
+#define MAKE_ALIGNED(adr, align) ( ((u32)adr + (align - 1)) & (~(align - 1)) )
+
+/* mem_block_t data stucutre defines parameters of the Memory Block */
+typedef struct mem_block {
+	struct mem_block *next;	/* Pointer to the next memory block */
+
+	u32 base;		/* base address of the memory block */
+	u32 end;		/* end address of the memory block */
+} mem_block_t;
+
+/* free_block_t data stucutre defines parameters of the Free Block */
+typedef struct free_block {
+	struct free_block *next;	/* Pointer to the next free block */
+
+	u32 base;		/* base address of the block */
+	u32 end;		/* end address of the block */
+} free_block_t;
+
+/* busy_block_t data stucutre defines parameters of the Busy Block  */
+typedef struct busy_block {
+	struct busy_block *next;	/* Pointer to the next free block */
+
+	u32 base;		/* base address of the block */
+	u32 end;		/* end address of the block */
+	char name[MAX_NAME_LEN];
+} busy_block_t;
+
+/* mm_t data structure defines parameters of the MM object */
+typedef struct mm {
+	mem_block_t *mem_blocks;    /* List of memory blocks (Memory list) */
+	busy_block_t *busy_blocks;	/* List of busy blocks (Busy list) */
+	free_block_t *free_blocks[MAX_ALIGNMENT + 1];/* align lists of free 
+							blocks (Free lists) */
+} mm_t;
+
+/**********************************************************************
+ *                     MM internal routines set                       *
+ **********************************************************************/
+
+/****************************************************************
+ *  Routine:   mem_block_init  
+ *
+ *  Description:
+ *      Initializes a new memory block of "size" bytes and started
+ *      from "base" address.
+ *
+ *  Arguments:
+ *      mem_blk- handle to the mem_blk object
+ *      base    - base address of the memory block
+ *      size    - size of the memory block
+ *
+ *  Return value:
+ *      0 is returned on success. E_NOMEMORY is returned
+ *      if can't allocate memory for mem_blk object.
+ ****************************************************************/
+static int mem_block_init(void **mem_blk, u32 base, u32 size)
+{
+	mem_block_t *p_mem_blk;
+
+	p_mem_blk = (mem_block_t *) kmalloc(sizeof(mem_block_t), GFP_KERNEL);
+	if (!p_mem_blk)
+		return -ENOMEM;
+
+	p_mem_blk->base = base;
+	p_mem_blk->end = base + size;
+	p_mem_blk->next = 0;
+
+	*mem_blk = p_mem_blk;
+
+	return (0);
+}
+
+/****************************************************************
+ *  Routine:   free_block_init  
+ *
+ *  Description:
+ *      Initializes a new free block of of "size" bytes and
+ *      started from "base" address.
+ *
+ *  Arguments:
+ *      FreeBlock - handle to the FreeBlock object
+ *      base      - base address of the free block
+ *      size      - size of the free block
+ *
+ *  Return value:
+ *      0 is returned on success. E_NOMEMORY is returned
+ *      if can't allocate memory for a free block.
+ ****************************************************************/
+static int free_block_init(void **FreeBlock, u32 base, u32 size)
+{
+	free_block_t *p_free_blk;
+
+	p_free_blk = (free_block_t *) kmalloc(sizeof(free_block_t), GFP_KERNEL);
+	if (!p_free_blk)
+		return -ENOMEM;
+
+	p_free_blk->base = base;
+	p_free_blk->end = base + size;
+	p_free_blk->next = 0;
+
+	*FreeBlock = p_free_blk;
+
+	return (0);
+}
+
+/****************************************************************
+ *  Routine:   busy_block_init  
+ *
+ *  Description:
+ *      Initializes a new busy block of "size" bytes and started
+ *      rom "base" address. Each busy block has a name that
+ *      specified the purpose of the memory allocation.
+ *
+ *  Arguments:
+ *      BusyBlock - handle to the BusyBlock object
+ *      base      - base address of the busy block
+ *      size      - size of the busy block
+ *      name      - name that specified the busy block
+ *
+ *  Return value:
+ *      0 is returned on success. E_NOMEMORY is returned
+ *      if can't allocate memory for busy block.
+ ****************************************************************/
+static int busy_block_init(void **BusyBlock, u32 base, u32 size, char *name)
+{
+	busy_block_t *p_busy_blk;
+	int n, NameLen;
+
+	p_busy_blk = (busy_block_t *) kmalloc(sizeof(busy_block_t), GFP_KERNEL);
+	if (!p_busy_blk)
+		return -ENOMEM;
+
+	p_busy_blk->base = base;
+	p_busy_blk->end = base + size;
+	NameLen = (int)strlen(name);
+	n = (NameLen > MAX_NAME_LEN - 1) ? MAX_NAME_LEN - 1 : NameLen;
+	strncpy(p_busy_blk->name, name, (u32) n);
+	p_busy_blk->name[n] = '\0';
+	p_busy_blk->next = 0;
+
+	*BusyBlock = p_busy_blk;
+
+	return (0);
+}
+
+/****************************************************************
+ *  Routine:    add_free 
+ *
+ *  Description:
+ *      Adds a new free block to the free lists. It updates each
+ *      free list to include a new free block.
+ *      Note, that all free block in each free list are ordered
+ *      by their base address.
+ *
+ *  Arguments:
+ *      p_mm  - pointer to the MM object
+ *      base  - base address of a given free block
+ *      end   - end address of a given free block
+ *
+ *  Return value:
+ *   
+ *
+ ****************************************************************/
+static int add_free(mm_t * p_mm, u32 base, u32 end)
+{
+	free_block_t *p_prev_blk, *p_curr_blk, *p_new_blk;
+	u32 align;
+	int i;
+	u32 align_base;
+
+	/* Updates free lists to include  a just released block */
+	for (i = 0; i <= MAX_ALIGNMENT; i++) {
+		p_prev_blk = p_new_blk = 0;
+		p_curr_blk = p_mm->free_blocks[i];
+
+		align = (u32) (0x1 << i);
+		align_base = MAKE_ALIGNED(base, align);
+
+		/* Goes to the next free list if there is no block to free */
+		if (align_base >= end)
+			continue;
+
+		/* Looks for a free block that should be updated */
+		while (p_curr_blk) {
+			if (align_base <= p_curr_blk->end) {
+				if (end > p_curr_blk->end) {
+					free_block_t *p_NextB;
+					while (p_curr_blk->next
+					       && end > p_curr_blk->next->end) {
+						p_NextB = p_curr_blk->next;
+						p_curr_blk->next =
+						    p_curr_blk->next->next;
+						kfree(p_NextB);
+					}
+
+					p_NextB = p_curr_blk->next;
+					if (!p_NextB
+					    || (p_NextB
+						&& end < p_NextB->base)) {
+						p_curr_blk->end = end;
+					} else {
+						p_curr_blk->end = p_NextB->end;
+						p_curr_blk->next =
+						    p_NextB->next;
+						kfree(p_NextB);
+					}
+				} else if (end < p_curr_blk->base
+					   && ((end - align_base) >= align)) {
+					if (free_block_init
+					    ((void *)&p_new_blk, align_base,
+					     end - align_base) != 0)
+						return -ENOMEM;
+					p_new_blk->next = p_curr_blk;
+					if (p_prev_blk)
+						p_prev_blk->next = p_new_blk;
+					else
+						p_mm->free_blocks[i] =
+						    p_new_blk;
+					break;
+				}
+
+				if (align_base < p_curr_blk->base
+				    && end >= p_curr_blk->base)
+					p_curr_blk->base = align_base;
+
+				/* if size of the free block is less then
+				 * alignment deletes that free block from
+				 * the free list. */
+				if ((p_curr_blk->end - p_curr_blk->base) <
+				    align) {
+					if (p_prev_blk)
+						p_prev_blk->next =
+						    p_curr_blk->next;
+					else
+						p_mm->free_blocks[i] =
+						    p_curr_blk->next;
+					kfree(p_curr_blk);
+				}
+				break;
+			} else {
+				p_prev_blk = p_curr_blk;
+				p_curr_blk = p_curr_blk->next;
+			}
+		}
+
+		/* If no free block found to be updated, insert a new free block
+		 * to the end of the free list. */
+		if (!p_curr_blk && ((end - base) % align == 0)) {
+			if (free_block_init
+			    ((void *)&p_new_blk, align_base, end - base) != 0)
+				return -ENOMEM;
+			if (p_prev_blk)
+				p_prev_blk->next = p_new_blk;
+			else
+				p_mm->free_blocks[i] = p_new_blk;
+		}
+
+		/* Update boundaries of the new free block */
+		if (align == 1 && !p_new_blk) {
+			if (p_curr_blk && base > p_curr_blk->base)
+				base = p_curr_blk->base;
+			if (p_curr_blk && end < p_curr_blk->end)
+				end = p_curr_blk->end;
+		}
+	}
+
+	return (0);
+}
+
+/****************************************************************
+ *  Routine:      cut_free
+ *
+ *  Description:
+ *      Cuts a free block from hold_base to hold_end from the free lists.
+ *      That is, it updates all free lists of the MM object do
+ *      not include a block of memory from hold_base to hold_end.
+ *      For each free lists it seek for a free block that holds
+ *      either hold_base or hold_end. If such block is found it updates it.
+ *
+ *  Arguments:
+ *      p_mm            - pointer to the MM object
+ *      hold_base        - base address of the allocated block
+ *      hold_end         - end address of the allocated block
+ *
+ *  Return value:
+ *      0 is returned on success,
+ *      otherwise returns an error code.
+ *
+ ****************************************************************/
+static int cut_free(mm_t * p_mm, u32 hold_base, u32 hold_end)
+{
+	free_block_t *p_prev_blk, *p_curr_blk, *p_new_blk;
+	u32 align_base, base, end, align;
+	int i;
+
+	for (i = 0; i <= MAX_ALIGNMENT; i++) {
+		p_prev_blk = p_new_blk = 0;
+		p_curr_blk = p_mm->free_blocks[i];
+
+		align = (u32) 0x1 << i;
+		align_base = MAKE_ALIGNED(hold_end, align);
+
+		while (p_curr_blk) {
+			base = p_curr_blk->base;
+			end = p_curr_blk->end;
+
+			if (hold_base <= base && hold_end <= end
+			    && hold_end > base) {
+				if (align_base >= end
+				    || (align_base < end
+					&& (end - align_base) < align)) {
+					if (p_prev_blk)
+						p_prev_blk->next =
+						    p_curr_blk->next;
+					else
+						p_mm->free_blocks[i] =
+						    p_curr_blk->next;
+					kfree(p_curr_blk);
+				} else {
+					p_curr_blk->base = align_base;
+				}
+				break;
+			} else if (hold_base > base && hold_end <= end) {
+				if ((hold_base - base) >= align) {
+					if (align_base < end
+					    && (end - align_base) >= align) {
+						if (free_block_init
+						    ((void *)&p_new_blk,
+						     align_base,
+						     (end - align_base)) != 0)
+							return -ENOMEM;
+						p_new_blk->next =
+						    p_curr_blk->next;
+						p_curr_blk->next = p_new_blk;
+					}
+					p_curr_blk->end = hold_base;
+				} else if (align_base < end
+					   && (end - align_base) >= align) {
+					p_curr_blk->base = align_base;
+				} else {
+					if (p_prev_blk)
+						p_prev_blk->next =
+						    p_curr_blk->next;
+					else
+						p_mm->free_blocks[i] =
+						    p_curr_blk->next;
+					kfree(p_curr_blk);
+				}
+				break;
+			} else {
+				p_prev_blk = p_curr_blk;
+				p_curr_blk = p_curr_blk->next;
+			}
+		}
+	}
+
+	return (0);
+}
+
+/****************************************************************
+ *  Routine:     add_busy
+ *
+ *  Description:
+ *      Adds a new busy block to the list of busy blocks. Note,
+ *      that all busy blocks are ordered by their base address in
+ *      the busy list.
+ *
+ *  Arguments:
+ *      MM              - handler to the MM object
+ *      p_new_busy_blk      - pointer to the a busy block
+ *
+ *  Return value:
+ *      None.
+ *
+ ****************************************************************/
+static void add_busy(mm_t * p_mm, busy_block_t * p_new_busy_blk)
+{
+	busy_block_t *p_cur_busy_blk, *p_prev_busy_blk;
+
+	/* finds a place of a new busy block in the list of busy blocks */
+	p_prev_busy_blk = 0;
+	p_cur_busy_blk = p_mm->busy_blocks;
+
+	while (p_cur_busy_blk && p_new_busy_blk->base > p_cur_busy_blk->base) {
+		p_prev_busy_blk = p_cur_busy_blk;
+		p_cur_busy_blk = p_cur_busy_blk->next;
+	}
+
+	/* insert the new busy block into the list of busy blocks */
+	if (p_cur_busy_blk)
+		p_new_busy_blk->next = p_cur_busy_blk;
+	if (p_prev_busy_blk)
+		p_prev_busy_blk->next = p_new_busy_blk;
+	else
+		p_mm->busy_blocks = p_new_busy_blk;
+
+}
+
+/****************************************************************
+ *  Routine:     get_greater_align
+ *
+ *  Description:
+ *      Allocates a block of memory according to the given size
+ *      and the alignment. That routine is called from the mm_get
+ *      routine if the required alignment is grater then MAX_ALIGNMENT.
+ *      In that case, it goes over free blocks of 64 byte align list
+ *      and checks if it has the required size of bytes of the required
+ *      alignment. If no blocks found returns ILLEGAL_BASE.
+ *      After the block is found and data is allocated, it calls
+ *      the internal cut_free routine to update all free lists
+ *      do not include a just allocated block. Of course, each
+ *      free list contains a free blocks with the same alignment.             
+ *      It is also creates a busy block that holds
+ *      information about an allocated block.
+ *
+ *  Arguments:
+ *      MM              - handle to the MM object
+ *      size            - size of the MM
+ *      align       - index as a power of two defines
+ *                        a required alignment that is grater then 64.
+ *      name            - the name that specifies an allocated block.
+ *
+ *  Return value:
+ *      base address of an allocated block.
+ *      ILLEGAL_BASE if can't allocate a block
+ *
+ ****************************************************************/
+static int get_greater_align(void *MM, u32 size, int align, char *name)
+{
+	mm_t *p_mm = (mm_t *) MM;
+	free_block_t *p_free_blk;
+	busy_block_t *p_new_busy_blk;
+	u32 hold_base, hold_end, align_base = 0;
+	u32 ret;
+
+	/* goes over free blocks of the 64 byte alignment list
+	 * and look for a block of the suitable size and
+	 * base address according to the alignment.
+	 */
+	p_free_blk = p_mm->free_blocks[MAX_ALIGNMENT];
+
+	while (p_free_blk) {
+		align_base = MAKE_ALIGNED(p_free_blk->base, align);
+
+		/* the block is found if the aligned base inside the block
+		 * and has the anough size.
+		 */
+		if (align_base >= p_free_blk->base &&
+		    align_base < p_free_blk->end &&
+		    size <= (p_free_blk->end - align_base))
+			break;
+		else
+			p_free_blk = p_free_blk->next;
+	}
+
+	/* If such block isn't found */
+	if (!p_free_blk)
+		return -EBUSY;
+
+	hold_base = align_base;
+	hold_end = align_base + size;
+
+	/* init a new busy block */
+	if ((ret =
+	     busy_block_init((void *)&p_new_busy_blk, hold_base, size,
+			     name)) != 0)
+		return ret;
+
+	/* calls Update routine to update a lists of free blocks */
+	if ((ret = cut_free(MM, hold_base, hold_end)) != 0)
+		return ret;
+
+	/* insert the new busy block into the list of busy blocks */
+	add_busy(p_mm, p_new_busy_blk);
+
+	return (hold_base);
+}
+
+/**********************************************************************
+ *                     MM API routines set                            *
+ **********************************************************************/
+int mm_init(void **MM, u32 base, u32 size)
+{
+	mm_t *p_mm;
+	int i;
+	u32 new_base, new_size;
+
+	/* Initializes a new MM object */
+	p_mm = (mm_t *) kmalloc(sizeof(mm_t), GFP_KERNEL);
+	if (p_mm == 0)
+		return -ENOMEM;
+
+	/* initializes a new memory block */
+	if (mem_block_init((void *)&p_mm->mem_blocks, base, size) != 0)
+		return -ENOMEM;
+
+	/* A busy list is empty */
+	p_mm->busy_blocks = 0;
+
+	/*Initializes a new free block for each free list */
+	for (i = 0; i <= MAX_ALIGNMENT; i++) {
+		new_base = MAKE_ALIGNED(base, (0x1 << i));
+		new_size = size - (new_base - base);
+		if (free_block_init((void *)&p_mm->free_blocks[i],
+				    new_base, new_size) != 0)
+			return -ENOMEM;
+	}
+
+	*MM = p_mm;
+
+	return (0);
+}
+
+EXPORT_SYMBOL(mm_init);
+
+void mm_free(void *MM)
+{
+	mm_t *p_mm = (mm_t *) MM;
+	mem_block_t *p_mem_blk;
+	busy_block_t *p_busy_blk;
+	free_block_t *p_free_blk;
+	void *p_blk;
+	int i;
+
+	if (!p_mm)
+		return;
+
+	/* release memory allocated for busy blocks */
+	p_busy_blk = p_mm->busy_blocks;
+	while (p_busy_blk) {
+		p_blk = p_busy_blk;
+		p_busy_blk = p_busy_blk->next;
+		kfree(p_blk);
+	}
+
+	/* release memory allocated for free blocks */
+	for (i = 0; i <= MAX_ALIGNMENT; i++) {
+		p_free_blk = p_mm->free_blocks[i];
+		while (p_free_blk) {
+			p_blk = p_free_blk;
+			p_free_blk = p_free_blk->next;
+			kfree(p_blk);
+		}
+	}
+
+	/* release memory allocated for memory blocks */
+	p_mem_blk = p_mm->mem_blocks;
+	while (p_mem_blk) {
+		p_blk = p_mem_blk;
+		p_mem_blk = p_mem_blk->next;
+		kfree(p_blk);
+	}
+
+	/* release memory allocated for MM object itself */
+	kfree(p_mm);
+}
+
+EXPORT_SYMBOL(mm_free);
+
+void *mm_get(void *MM, u32 size, int align, char *name)
+{
+	mm_t *p_mm = (mm_t *) MM;
+	free_block_t *p_free_blk;
+	busy_block_t *p_new_busy_blk;
+	u32 hold_base, hold_end;
+	u32 i = 0, j = (u32) align;
+	u32 ret;
+
+	if (!p_mm)
+		return ERR_PTR(-EINVAL);
+
+	/* checks that align value is grater then zero */
+	if (align == 0)
+		return ERR_PTR(-EINVAL);
+
+	/* checks if alignment is a power of two,
+	 * if it correct and if the required size
+	 * is multiple of the given alignment.
+	 */
+	while ((j & 0x1) == 0) {
+		i++;
+		j = j >> 1;
+	}
+
+	/* if the given alignment isn't power of two, returns an error */
+	if (j != 1)
+		return ERR_PTR(-EINVAL);
+
+	if (i > MAX_ALIGNMENT)
+		return ERR_PTR(get_greater_align(MM, size, align, name));
+
+	/* look for a block of the size grater or equal to the
+	 * required size.
+	 */
+	p_free_blk = p_mm->free_blocks[i];
+	while (p_free_blk && (p_free_blk->end - p_free_blk->base) < size)
+		p_free_blk = p_free_blk->next;
+
+	/* If such block is found */
+	if (!p_free_blk)
+		return ERR_PTR(-ENOMEM);
+
+	hold_base = p_free_blk->base;
+	hold_end = hold_base + size;
+
+	/* init a new busy block */
+	if ((ret =
+	     busy_block_init((void *)&p_new_busy_blk, hold_base, size,
+			     name)) != 0)
+		return ERR_PTR(ret);
+
+	/* calls Update routine to update a lists of free blocks */
+	if ((ret = cut_free(MM, hold_base, hold_end)) != 0)
+		return ERR_PTR(ret);
+
+	/* insert the new busy block into the list of busy blocks */
+	add_busy(p_mm, p_new_busy_blk);
+
+	return (void *)(hold_base);
+}
+
+EXPORT_SYMBOL(mm_get);
+
+void *mm_get_force(void *MM, u32 base, u32 size, char *name)
+{
+	mm_t *p_mm = (mm_t *) MM;
+	free_block_t *p_free_blk;
+	busy_block_t *p_new_busy_blk;
+	int blk_is_free = 0;
+	u32 ret;
+
+	p_free_blk = p_mm->free_blocks[0];/* The biggest free blocks are in 
+					     the free list with alignment 1 */
+	while (p_free_blk) {
+		if (base >= p_free_blk->base
+		    && (base + size) <= p_free_blk->end) {
+			blk_is_free = 1;
+			break;
+		} else
+			p_free_blk = p_free_blk->next;
+	}
+
+	if (!blk_is_free)
+		return ERR_PTR(-ENOMEM);
+
+	/* init a new busy block */
+	if ((ret =
+	     busy_block_init((void *)&p_new_busy_blk, base, size, name)) != 0)
+		return ERR_PTR(ret);
+
+	/* calls Update routine to update a lists of free blocks */
+	if ((ret = cut_free(MM, base, base + size)) != 0)
+		return ERR_PTR(ret);
+
+	/* insert the new busy block into the list of busy blocks */
+	add_busy(p_mm, p_new_busy_blk);
+	return (void *)(base);
+}
+
+EXPORT_SYMBOL(mm_get_force);
+
+int mm_put(void *MM, u32 base)
+{
+	mm_t *p_mm = (mm_t *) MM;
+	busy_block_t *p_busy_blk, *p_prev_busy_blk;
+	u32 size;
+	u32 ret;
+
+	if (!p_mm)
+		return -EINVAL;
+
+	/* Look for a busy block that have the given base value.
+	 * That block will be returned back to the memory.
+	 */
+	p_prev_busy_blk = 0;
+	p_busy_blk = p_mm->busy_blocks;
+	while (p_busy_blk && base != p_busy_blk->base) {
+		p_prev_busy_blk = p_busy_blk;
+		p_busy_blk = p_busy_blk->next;
+	}
+
+	if (!p_busy_blk)
+		return -EINVAL;
+
+	if ((ret = add_free(p_mm, p_busy_blk->base, p_busy_blk->end)) != 0)
+		return ret;
+
+	/* removes a busy block form the list of busy blocks */
+	if (p_prev_busy_blk)
+		p_prev_busy_blk->next = p_busy_blk->next;
+	else
+		p_mm->busy_blocks = p_busy_blk->next;
+
+	size = p_busy_blk->end - p_busy_blk->base;
+
+	kfree(p_busy_blk);
+
+	return (0);
+}
+
+EXPORT_SYMBOL(mm_put);
+
+void mm_dump(void *MM)
+{
+	mm_t *p_mm = (mm_t *) MM;
+	free_block_t *p_free_blk;
+	busy_block_t *p_busy_blk;
+	int i;
+
+	p_busy_blk = p_mm->busy_blocks;
+	printk(KERN_INFO "List of busy blocks:\n");
+	while (p_busy_blk) {
+		printk(KERN_INFO "\t0x%08x: (%s: b=0x%08x, e=0x%08x)\n",
+		       (u32) p_busy_blk, p_busy_blk->name, p_busy_blk->base,
+		       p_busy_blk->end);
+		p_busy_blk = p_busy_blk->next;
+	}
+
+	printk(KERN_INFO "Lists of free blocks according to alignment:\n");
+	for (i = 0; i <= MAX_ALIGNMENT; i++) {
+		printk(KERN_INFO "%d alignment:\n", (0x1 << i));
+		p_free_blk = p_mm->free_blocks[i];
+		while (p_free_blk) {
+			printk(KERN_INFO "\t0x%08x: (b=0x%08x, e=0x%08x)\n",
+			       (u32) p_free_blk, p_free_blk->base,
+			       p_free_blk->end);
+			p_free_blk = p_free_blk->next;
+		}
+		printk(KERN_INFO "\n");
+	}
+}
+
+EXPORT_SYMBOL(mm_dump);
diff --git a/arch/powerpc/sysdev/qe_lib/mm.h b/arch/powerpc/sysdev/qe_lib/mm.h
new file mode 100644
index 0000000..ab30080
--- /dev/null
+++ b/arch/powerpc/sysdev/qe_lib/mm.h
@@ -0,0 +1,6 @@
+int mm_init ( void **MM, u32 base, u32 size );
+void mm_free (void * MM);
+void *mm_get ( void * MM, u32 size, int align, char* name );
+void *mm_get_force ( void * MM, u32 base, u32 size, char* name );
+int mm_put ( void * MM, u32 base );
+void mm_dump ( void * MM );
diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c
new file mode 100644
index 0000000..0fbb54c
--- /dev/null
+++ b/arch/powerpc/sysdev/qe_lib/qe.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved.
+ *
+ * Author: Li Yang <LeoLi@freescale.com>
+ *
+ * Description:
+ * FSL QE SOC setup.
+ *
+ * Changelog: 
+ * Jun 21, 2006	Initial version
+ *
+ * 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 = np->intrs[0].line;
+		r[1].end = np->intrs[0].line;
+		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], np->n_intrs + 1);
+
+		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 = phy->intrs[0].line; 
+		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 (phy->intrs[0].line != 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;
+}
+
+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..cd0aca9
--- /dev/null
+++ b/arch/powerpc/sysdev/qe_lib/qe_common.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved.
+ *
+ * Author: Shlomi Gridish <gridish@freescale.com>
+ *
+ * Description:
+ * General Purpose functions for the global management of the
+ * QUICC Engine (QE).
+ *
+ * Changelog:
+ * Jun 28, 2006	Li Yang <LeoLi@freescale.com>
+ * - Reorganized as qe_lib
+ * - Merged to powerpc arch; add device tree support
+ * - Style fixes
+ *
+ * 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.
+ */
+#undef USE_RHEAP
+#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 <asm/irq.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/immap_qe.h>
+#include <asm/qe.h>
+#include <asm/prom.h>
+#ifdef USE_RHEAP
+#include <asm/rheap.h>
+#else
+#include "mm.h"
+#endif				/* USE_RHEAP */
+
+/* QE snum state
+*/
+typedef enum qe_snum_state {
+	QE_SNUM_STATE_USED,	/* used */
+	QE_SNUM_STATE_FREE	/* free */
+} qe_snum_state_e;
+
+/* QE snum
+*/
+typedef struct qe_snum {
+	u8 num;			/* snum  */
+	qe_snum_state_e state;	/* state */
+} qe_snum_t;
+
+/* We allocate this here because it is used almost exclusively for
+ * the communication processor devices.
+ */
+EXPORT_SYMBOL(qe_immr);
+qe_map_t *qe_immr = NULL;
+static qe_snum_t snums[QE_NUM_OF_SNUM];	/* Dynamically allocated SNUMs  */
+
+static void qe_snums_init(void);
+static void qe_muram_init(void);
+static int qe_sdma_init(void);
+
+void qe_reset(void)
+{
+	if (qe_immr == NULL)
+		qe_immr = (qe_map_t *) ioremap(get_qe_base(), QE_IMMAP_SIZE);
+
+	qe_snums_init();
+
+	qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID,
+		     (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
+
+	/* Reclaim the MURAM memory for our use. */
+	qe_muram_init();
+
+#ifdef USE_RHEAP
+	if (qe_sdma_init())
+		panic("sdma init failed!");
+#endif				/* USE_RHEAP */
+}
+
+EXPORT_SYMBOL(qe_issue_cmd);
+int qe_issue_cmd(uint cmd, uint device, u8 mcn_protocol, u32 cmd_input)
+{
+	unsigned long flags;
+	u32 cecr;
+	u8 mcn_shift = 0, dev_shift = 0;
+
+	local_irq_save(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));
+		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);
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+/* 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(uint brg, uint rate)
+{
+	volatile uint *bp;
+	u32 divisor;
+	int div16 = 0;
+
+	bp = (uint *) & 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;
+}
+
+static void qe_snums_init(void)
+{
+	int i;
+
+	/* Initialize the SNUMs array. */
+	for (i = 0; i < QE_NUM_OF_SNUM; i++)
+		snums[i].state = QE_SNUM_STATE_FREE;
+
+	/* Initialize SNUMs (thread serial numbers) according to QE
+	 * spec chapter 4, SNUM table */
+	i = 0;
+	snums[i++].num = 0x04;
+	snums[i++].num = 0x05;
+	snums[i++].num = 0x0C;
+	snums[i++].num = 0x0D;
+	snums[i++].num = 0x14;
+	snums[i++].num = 0x15;
+	snums[i++].num = 0x1C;
+	snums[i++].num = 0x1D;
+	snums[i++].num = 0x24;
+	snums[i++].num = 0x25;
+	snums[i++].num = 0x2C;
+	snums[i++].num = 0x2D;
+	snums[i++].num = 0x34;
+	snums[i++].num = 0x35;
+	snums[i++].num = 0x88;
+	snums[i++].num = 0x89;
+	snums[i++].num = 0x98;
+	snums[i++].num = 0x99;
+	snums[i++].num = 0xA8;
+	snums[i++].num = 0xA9;
+	snums[i++].num = 0xB8;
+	snums[i++].num = 0xB9;
+	snums[i++].num = 0xC8;
+	snums[i++].num = 0xC9;
+	snums[i++].num = 0xD8;
+	snums[i++].num = 0xD9;
+	snums[i++].num = 0xE8;
+	snums[i++].num = 0xE9;
+}
+
+int qe_get_snum(void)
+{
+	unsigned long flags;
+	int snum = -EBUSY;
+	int i;
+
+	local_irq_save(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;
+		}
+	}
+	local_irq_restore(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)
+{
+	sdma_t *sdma = &qe_immr->sdma;
+	uint 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 spinlock_t qe_muram_lock;
+#ifdef USE_RHEAP
+/* 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;
+#else
+static void *mm = NULL;
+#endif				/* USE_RHEAP */
+
+static void qe_muram_init(void)
+{
+	spin_lock_init(&qe_muram_lock);
+
+#ifdef USE_RHEAP
+	/* 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 actually crap. QE_DATAONLY_BASE and
+	 * QE_DATAONLY_SIZE is only a subset of the available muram. It
+	 * varies with the processor and the microcode patches activated.
+	 * But the following should be at least safe.
+	 */
+	rh_attach_region(&qe_muram_info,
+			 (void *)QE_MURAM_DATAONLY_BASE,
+			 QE_MURAM_DATAONLY_SIZE);
+#else
+#endif				/* USE_RHEAP */
+}
+
+/* This function returns an index into the MURAM area.
+ */
+uint qe_muram_alloc(uint size, uint align)
+{
+	void *start;
+	unsigned long flags;
+
+	spin_lock_irqsave(&qe_muram_lock, flags);
+#ifdef USE_RHEAP
+	qe_muram_info.alignment = align;
+	start = rh_alloc(&qe_muram_info, size, "QE");
+#else
+	if (!mm) {
+		mm_init(&mm, (u32) qe_muram_addr(QE_MURAM_DATAONLY_BASE),
+			QE_MURAM_DATAONLY_SIZE);
+		if (qe_sdma_init())
+			panic("sdma init failed!");
+
+	}
+	start = mm_get(mm, (u32) size, (int)align, "QE");
+	if (!IS_MURAM_ERR((u32) start))
+		start = (void *)((u32) start - (u32) qe_immr->muram);
+#endif				/* USE_RHEAP */
+	spin_unlock_irqrestore(&qe_muram_lock, flags);
+
+	return (uint) start;
+}
+
+EXPORT_SYMBOL(qe_muram_alloc);
+
+int qe_muram_free(uint offset)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&qe_muram_lock, flags);
+#ifdef USE_RHEAP
+	ret = rh_free(&qe_muram_info, (void *)offset);
+#else
+	ret = mm_put(mm, (u32) qe_muram_addr(offset));
+#endif				/* USE_RHEAP */
+	spin_unlock_irqrestore(&qe_muram_lock, flags);
+
+	return ret;
+}
+
+EXPORT_SYMBOL(qe_muram_free);
+
+/* not sure if this is ever needed */
+uint qe_muram_alloc_fixed(uint offset, uint size, uint align)
+{
+	void *start;
+	unsigned long flags;
+
+	spin_lock_irqsave(&qe_muram_lock, flags);
+#ifdef USE_RHEAP
+	qe_muram_info.alignment = align;
+	start =
+	    rh_alloc_fixed(&qe_muram_info, (void *)offset, size, "commproc");
+#else
+	start = mm_get_force(mm, (u32) offset, (u32) size, "QE");
+	if (!IS_MURAM_ERR((u32) start))
+		start = (void *)((u32) start - (u32) qe_immr->muram);
+#endif				/* USE_RHEAP */
+	spin_unlock_irqrestore(&qe_muram_lock, flags);
+
+	return (uint) start;
+}
+
+EXPORT_SYMBOL(qe_muram_alloc_fixed);
+
+void qe_muram_dump(void)
+{
+#ifdef USE_RHEAP
+	rh_dump(&qe_muram_info);
+#else
+	mm_dump(mm);
+#endif				/* USE_RHEAP */
+}
+
+EXPORT_SYMBOL(qe_muram_dump);
+
+void *qe_muram_addr(uint 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..465630e
--- /dev/null
+++ b/arch/powerpc/sysdev/qe_lib/qe_ic.c
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved.
+ *
+ * Author: Shlomi Gridish <gridish@freescale.com>
+ *
+ * Description:
+ * QE Interrupt Controller routines implementations.
+ *
+ * Changelog: 
+ * Jun 28, 2006 Li Yang <LeoLi@freescale.com>
+ * - Reorganized as qe_lib
+ * - Merged to powerpc arch; add device tree support
+ * - Style fixes
+ *
+ * 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/interrupt.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/qe_ic.h>
+
+#include "qe_ic.h"
+
+static struct qe_ic_private p_qe_ic;
+static struct qe_ic_private *primary_qe_ic;
+
+static struct qe_ic_info qe_ic_info[] = {
+	[1] = {
+	       .mask = 0x00008000,
+	       .qimr = 1,
+	       .pri_code = 0},
+	[2] = {
+	       .mask = 0x00004000,
+	       .qimr = 1,
+	       .pri_code = 1},
+	[3] = {
+	       .mask = 0x00002000,
+	       .qimr = 1,
+	       .pri_code = 2},
+	[10] = {
+		.mask = 0x00000040,
+		.qimr = 1,
+		.pri_code = 1},
+	[11] = {
+		.mask = 0x00000020,
+		.qimr = 1,
+		.pri_code = 2},
+	[12] = {
+		.mask = 0x00000010,
+		.qimr = 1,
+		.pri_code = 3},
+	[13] = {
+		.mask = 0x00000008,
+		.qimr = 1,
+		.pri_code = 4},
+	[14] = {
+		.mask = 0x00000004,
+		.qimr = 1,
+		.pri_code = 5},
+	[15] = {
+		.mask = 0x00000002,
+		.qimr = 1,
+		.pri_code = 6},
+	[20] = {
+		.mask = 0x10000000,
+		.qimr = 0,
+		.pri_code = 3},
+	[25] = {
+		.mask = 0x00800000,
+		.qimr = 0,
+		.pri_code = 0},
+	[26] = {
+		.mask = 0x00400000,
+		.qimr = 0,
+		.pri_code = 1},
+	[27] = {
+		.mask = 0x00200000,
+		.qimr = 0,
+		.pri_code = 2},
+	[28] = {
+		.mask = 0x00100000,
+		.qimr = 0,
+		.pri_code = 3},
+	[32] = {
+		.mask = 0x80000000,
+		.qimr = 1,
+		.pri_code = 0},
+	[33] = {
+		.mask = 0x40000000,
+		.qimr = 1,
+		.pri_code = 1},
+	[34] = {
+		.mask = 0x20000000,
+		.qimr = 1,
+		.pri_code = 2},
+	[35] = {
+		.mask = 0x10000000,
+		.qimr = 1,
+		.pri_code = 3},
+	[36] = {
+		.mask = 0x08000000,
+		.qimr = 1,
+		.pri_code = 4},
+	[40] = {
+		.mask = 0x00800000,
+		.qimr = 1,
+		.pri_code = 0},
+	[41] = {
+		.mask = 0x00400000,
+		.qimr = 1,
+		.pri_code = 1},
+	[42] = {
+		.mask = 0x00200000,
+		.qimr = 1,
+		.pri_code = 2},
+	[43] = {
+		.mask = 0x00100000,
+		.qimr = 1,
+		.pri_code = 3},
+};
+
+struct hw_interrupt_type qe_ic = {
+	.typename = "QE IC",
+	.enable = qe_ic_enable_irq,
+	.disable = qe_ic_disable_irq,
+	.ack = qe_ic_disable_irq_and_ack,
+	.end = qe_ic_end_irq,
+};
+
+static int qe_ic_get_low_irq(struct pt_regs *regs)
+{
+	struct qe_ic_private *p_qe_ic = primary_qe_ic;
+	int irq = -1;
+
+	/* get the low byte of SIVEC to get the interrupt source vector. */
+	irq = (in_be32(&p_qe_ic->regs->qivec) >> 24) >> 2;
+
+	if (irq == 0)		/* 0 --> no irq is pending */
+		return -1;
+
+	return irq + p_qe_ic->irq_offset;
+}
+
+static int qe_ic_get_high_irq(struct pt_regs *regs)
+{
+	struct qe_ic_private *p_qe_ic = primary_qe_ic;
+	int irq = -1;
+
+	/* get the high byte of SIVEC to get the interrupt source vector. */
+	irq = (in_be32(&p_qe_ic->regs->qhivec) >> 24) >> 2;
+
+	if (irq == 0)		/* 0 --> no irq is pending */
+		return -1;
+
+	return irq + p_qe_ic->irq_offset;
+}
+
+static irqreturn_t qe_ic_cascade_low(int irq, void *dev_id,
+				     struct pt_regs *regs)
+{
+	while ((irq = qe_ic_get_low_irq(regs)) >= 0)
+		__do_IRQ(irq, regs);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t qe_ic_cascade_high(int irq, void *dev_id,
+				      struct pt_regs *regs)
+{
+	while ((irq = qe_ic_get_high_irq(regs)) >= 0)
+		__do_IRQ(irq, regs);
+	return IRQ_HANDLED;
+}
+
+static struct irqaction qe_ic_low_irqaction = {
+	.handler = qe_ic_cascade_low,
+	.flags = SA_INTERRUPT,
+	.mask = CPU_MASK_NONE,
+	.name = "qe_ic_cascade_low",
+};
+
+static struct irqaction qe_ic_high_irqaction = {
+	.handler = qe_ic_cascade_high,
+	.flags = SA_INTERRUPT,
+	.mask = CPU_MASK_NONE,
+	.name = "qe_ic_cascade_high",
+};
+
+int qe_ic_init(phys_addr_t phys_addr,
+	       unsigned int flags, unsigned int irq_offset)
+{
+	struct qe_ic_map *regs;
+	u8 grp, pri, shift = 0;
+	u32 tmp_qicr = 0, tmp_qricr = 0, tmp_qicnr = 0, tmp_mask;
+	int i, high_hctive = 0;
+	const u32 high_signal = 2;
+
+	primary_qe_ic = &p_qe_ic;
+	memset(primary_qe_ic, 0, sizeof(struct qe_ic_private));
+
+	/* initialize QE interrupt controller registers */
+	primary_qe_ic->regs = regs =
+	    (struct qe_ic_map *)ioremap(phys_addr, QE_IC_SIZE);
+	primary_qe_ic->irq_offset = irq_offset;
+
+	/* default priority scheme is grouped. If spread mode is    */
+	/* required, configure sicr accordingly.                    */
+	if (flags & QE_IC_SPREADMODE_GRP_W)
+		tmp_qicr |= QICR_GWCC;
+	if (flags & QE_IC_SPREADMODE_GRP_X)
+		tmp_qicr |= QICR_GXCC;
+	if (flags & QE_IC_SPREADMODE_GRP_Y)
+		tmp_qicr |= QICR_GYCC;
+	if (flags & QE_IC_SPREADMODE_GRP_Z)
+		tmp_qicr |= QICR_GZCC;
+	if (flags & QE_IC_SPREADMODE_GRP_RISCA)
+		tmp_qicr |= QICR_GRTA;
+	if (flags & QE_IC_SPREADMODE_GRP_RISCB)
+		tmp_qicr |= QICR_GRTB;
+
+	/* choose destination signal for highest priority interrupt */
+	if (flags & QE_IC_HIGH_SIGNAL) {
+		tmp_qicr |= (high_signal << QICR_HPIT_SHIFT);
+		high_hctive = 1;
+	}
+
+	out_be32(&regs->qicr, tmp_qicr);
+
+	tmp_mask = (1 << QE_IC_GRP_W_DEST_SIGNAL_SHIFT);
+	/* choose destination signal for highest priority interrupt in each
+	 * group */
+	for (grp = 0; grp < NUM_OF_QE_IC_GROUPS; grp++) {
+		/* the first 2 priorities in each group have a choice of
+		 * destination signal */
+		for (pri = 0; pri <= 1; pri++) {
+			if (flags & ((tmp_mask << (grp << 1)) << pri)) {
+				/* indicate whether QE High signal is
+				 * required */
+				if (!high_hctive)
+					high_hctive = 1;
+
+				/* The location of the bits relevant to
+				 * priority 0 in the   */
+				/* registers is always 2 bits left comparing
+				 * to priority 1. */
+				if (pri == 0)
+					shift = 2;
+
+				switch (grp) {
+				case (QE_IC_GRP_W):
+					shift += QICNR_WCC1T_SHIFT;
+					tmp_qicnr |= high_signal << shift;
+					break;
+				case (QE_IC_GRP_X):
+					shift += QICNR_XCC1T_SHIFT;
+					tmp_qicnr |= high_signal << shift;
+					break;
+				case (QE_IC_GRP_Y):
+					shift += QICNR_YCC1T_SHIFT;
+					tmp_qicnr |= high_signal << shift;
+					break;
+				case (QE_IC_GRP_Z):
+					shift += QICNR_ZCC1T_SHIFT;
+					tmp_qicnr |= high_signal << shift;
+					break;
+				case (QE_IC_GRP_RISCA):
+					shift += QRICR_RTA1T_SHIFT;
+					tmp_qricr |= high_signal << shift;
+					break;
+				case (QE_IC_GRP_RISCB):
+					shift += QRICR_RTB1T_SHIFT;
+					tmp_qricr |= high_signal << shift;
+					break;
+				default:
+					break;
+				}
+			}
+		}
+	}
+
+	if (tmp_qicnr)
+		out_be32(&regs->qicnr, tmp_qicnr);
+	if (tmp_qricr)
+		out_be32(&regs->qricr, tmp_qricr);
+
+	for (i = primary_qe_ic->irq_offset;
+	     i < (NR_QE_IC_INTS + primary_qe_ic->irq_offset); i++) {
+		irq_desc[i].handler = &qe_ic;
+		irq_desc[i].status = IRQ_LEVEL;
+	}
+
+	/* register QE_IC interrupt controller in the a higher hirarchy
+	 * controller */
+	setup_irq(IRQ_QE_LOW, &qe_ic_low_irqaction);
+
+	if (high_hctive)
+		/* register QE_IC high interrupt source in the higher
+		 * hirarchy controller */
+		setup_irq(IRQ_QE_HIGH, &qe_ic_high_irqaction);
+
+	printk("QE IC (%d IRQ sources) at %p\n", NR_QE_IC_INTS,
+	       primary_qe_ic->regs);
+	return 0;
+}
+
+void qe_ic_free(void)
+{
+}
+
+void qe_ic_enable_irq(unsigned int irq)
+{
+	struct qe_ic_private *p_qe_ic = primary_qe_ic;
+	unsigned int src = irq - p_qe_ic->irq_offset;
+	u32 qimr;
+
+	if (qe_ic_info[src].qimr) {
+		qimr = in_be32(&((struct qe_ic_private *)p_qe_ic)->regs->qimr);
+		out_be32(&((struct qe_ic_private *)p_qe_ic)->regs->qimr,
+			 qimr | (qe_ic_info[src].mask));
+	} else {
+		qimr = in_be32(&((struct qe_ic_private *)p_qe_ic)->regs->qrimr);
+		out_be32(&((struct qe_ic_private *)p_qe_ic)->regs->qrimr,
+			 qimr | (qe_ic_info[src].mask));
+	}
+}
+
+void qe_ic_disable_irq(unsigned int irq)
+{
+	struct qe_ic_private *p_qe_ic = primary_qe_ic;
+	unsigned int src = irq - p_qe_ic->irq_offset;
+	u32 qimr;
+
+	if (qe_ic_info[src].qimr) {
+		qimr = in_be32(&((struct qe_ic_private *)p_qe_ic)->regs->qimr);
+		out_be32(&((struct qe_ic_private *)p_qe_ic)->regs->qimr,
+			 qimr & ~(qe_ic_info[src].mask));
+	} else {
+		qimr = in_be32(&((struct qe_ic_private *)p_qe_ic)->regs->qrimr);
+		out_be32(&((struct qe_ic_private *)p_qe_ic)->regs->qrimr,
+			 qimr & ~(qe_ic_info[src].mask));
+	}
+}
+
+void qe_ic_disable_irq_and_ack(unsigned int irq)
+{
+	qe_ic_disable_irq(irq);
+}
+
+void qe_ic_end_irq(unsigned int irq)
+{
+
+	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))
+	    && irq_desc[irq].action)
+		qe_ic_enable_irq(irq);
+}
+
+void qe_ic_modify_highest_priority(unsigned int irq)
+{
+	struct qe_ic_private *p_qe_ic = primary_qe_ic;
+	unsigned int src = irq - p_qe_ic->irq_offset;
+	u32 tmp_qicr = 0;
+
+	tmp_qicr = in_be32(&p_qe_ic->regs->qicr);
+	out_be32(&p_qe_ic->regs->qicr, (u32) (tmp_qicr | ((u8) src << 24)));
+}
+
+void qe_ic_modify_priority(enum qe_ic_grp_id grp,
+			   unsigned int pri0,
+			   unsigned int pri1,
+			   unsigned int pri2,
+			   unsigned int pri3,
+			   unsigned int pri4,
+			   unsigned int pri5,
+			   unsigned int pri6, unsigned int pri7)
+{
+	struct qe_ic_private *p_qe_ic = primary_qe_ic;
+	volatile u32 *p_qip = 0;
+	u32 tmp_qip = 0;
+	u8 tmp_array[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+	signed char code_array[8], i = 0, j = 0;
+
+	code_array[0] = (signed char)(pri0 ? qe_ic_info[pri0].pri_code : -1);
+	code_array[1] = (signed char)(pri1 ? qe_ic_info[pri1].pri_code : -1);
+	code_array[2] = (signed char)(pri2 ? qe_ic_info[pri2].pri_code : -1);
+	code_array[3] = (signed char)(pri3 ? qe_ic_info[pri3].pri_code : -1);
+	code_array[4] = (signed char)(pri4 ? qe_ic_info[pri4].pri_code : -1);
+	code_array[5] = (signed char)(pri5 ? qe_ic_info[pri5].pri_code : -1);
+	code_array[6] = (signed char)(pri6 ? qe_ic_info[pri6].pri_code : -1);
+	code_array[7] = (signed char)(pri7 ? qe_ic_info[pri7].pri_code : -1);
+
+	for (i = 0; i < 8; i++) {
+		if (code_array[i] == -1)
+			break;
+		tmp_array[code_array[i]] = 1;
+	}
+
+	for (; i < 8; i++) {
+		while (tmp_array[j] && j < 8)
+			j++;
+		code_array[i] = j;
+		tmp_array[j] = 1;
+	}
+
+	tmp_qip = (u32) (code_array[0] << QIPCC_SHIFT_PRI0 |
+			 code_array[1] << QIPCC_SHIFT_PRI1 |
+			 code_array[2] << QIPCC_SHIFT_PRI2 |
+			 code_array[3] << QIPCC_SHIFT_PRI3 |
+			 code_array[4] << QIPCC_SHIFT_PRI4 |
+			 code_array[5] << QIPCC_SHIFT_PRI5 |
+			 code_array[6] << QIPCC_SHIFT_PRI6 |
+			 code_array[7] << QIPCC_SHIFT_PRI7);
+
+	switch (grp) {
+	case (QE_IC_GRP_W):
+		p_qip = &(p_qe_ic->regs->qipwcc);
+		break;
+	case (QE_IC_GRP_X):
+		p_qip = &(p_qe_ic->regs->qipxcc);
+		break;
+	case (QE_IC_GRP_Y):
+		p_qip = &(p_qe_ic->regs->qipycc);
+		break;
+	case (QE_IC_GRP_Z):
+		p_qip = &(p_qe_ic->regs->qipzcc);
+		break;
+	case (QE_IC_GRP_RISCA):
+		p_qip = &(p_qe_ic->regs->qiprta);
+		break;
+	case (QE_IC_GRP_RISCB):
+		p_qip = &(p_qe_ic->regs->qiprtb);
+		break;
+	default:
+		break;
+	}
+
+	out_be32(p_qip, tmp_qip);
+}
+
+void qe_ic_dump_regs(void)
+{
+	struct qe_ic_private *p_qe_ic = primary_qe_ic;
+
+	printk(KERN_INFO "QE IC registars:\n");
+	printk(KERN_INFO "Base address: 0x%08x\n", (u32) p_qe_ic->regs);
+	printk(KERN_INFO "qicr  : addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & p_qe_ic->regs->qicr, in_be32(&p_qe_ic->regs->qicr));
+	printk(KERN_INFO "qivec : addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & p_qe_ic->regs->qivec, in_be32(&p_qe_ic->regs->qivec));
+	printk(KERN_INFO "qripnr: addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & p_qe_ic->regs->qripnr, in_be32(&p_qe_ic->regs->qripnr));
+	printk(KERN_INFO "qipnr : addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & p_qe_ic->regs->qipnr, in_be32(&p_qe_ic->regs->qipnr));
+	printk(KERN_INFO "qipxcc: addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & p_qe_ic->regs->qipxcc, in_be32(&p_qe_ic->regs->qipxcc));
+	printk(KERN_INFO "qipycc: addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & p_qe_ic->regs->qipycc, in_be32(&p_qe_ic->regs->qipycc));
+	printk(KERN_INFO "qipwcc: addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & p_qe_ic->regs->qipwcc, in_be32(&p_qe_ic->regs->qipwcc));
+	printk(KERN_INFO "qipzcc: addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & p_qe_ic->regs->qipzcc, in_be32(&p_qe_ic->regs->qipzcc));
+	printk(KERN_INFO "qimr  : addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & p_qe_ic->regs->qimr, in_be32(&p_qe_ic->regs->qimr));
+	printk(KERN_INFO "qrimr : addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & p_qe_ic->regs->qrimr, in_be32(&p_qe_ic->regs->qrimr));
+	printk(KERN_INFO "qicnr : addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & p_qe_ic->regs->qicnr, in_be32(&p_qe_ic->regs->qicnr));
+	printk(KERN_INFO "qiprta: addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & p_qe_ic->regs->qiprta, in_be32(&p_qe_ic->regs->qiprta));
+	printk(KERN_INFO "qiprtb: addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & p_qe_ic->regs->qiprtb, in_be32(&p_qe_ic->regs->qiprtb));
+	printk(KERN_INFO "qricr : addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & p_qe_ic->regs->qricr, in_be32(&p_qe_ic->regs->qricr));
+	printk(KERN_INFO "qhivec: addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & p_qe_ic->regs->qhivec, in_be32(&p_qe_ic->regs->qhivec));
+}
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..6662ad2
--- /dev/null
+++ b/arch/powerpc/sysdev/qe_lib/qe_ic.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved.
+ *
+ * Author: Shlomi Gridish <gridish@freescale.com>
+ *
+ * Description:
+ * QE IC private definitions and structure.
+ *
+ * Changelog:
+ * Jun 21, 2006 Li Yang <LeoLi@freescale.com>
+ * - Style fix; port to powerpc arch
+ *
+ * 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 __QE_IC_H__
+#define __QE_IC_H__
+
+typedef struct qe_ic_map {
+	volatile u32 qicr;
+	volatile u32 qivec;
+	volatile u32 qripnr;
+	volatile u32 qipnr;
+	volatile u32 qipxcc;
+	volatile u32 qipycc;
+	volatile u32 qipwcc;
+	volatile u32 qipzcc;
+	volatile u32 qimr;
+	volatile u32 qrimr;
+	volatile u32 qicnr;
+	volatile u8 res0[0x4];
+	volatile u32 qiprta;
+	volatile u32 qiprtb;
+	volatile u8 res1[0x4];
+	volatile u32 qricr;
+	volatile u8 res2[0x20];
+	volatile u32 qhivec;
+	volatile u8 res3[0x1C];
+} __attribute__ ((packed)) qe_ic_map_t;
+
+
+#define QE_IC_SIZE sizeof(struct qe_ic_map)
+
+/* Interrupt priority registers */
+#define QIPCC_SHIFT_PRI0        29
+#define QIPCC_SHIFT_PRI1        26
+#define QIPCC_SHIFT_PRI2        23
+#define QIPCC_SHIFT_PRI3        20
+#define QIPCC_SHIFT_PRI4        13
+#define QIPCC_SHIFT_PRI5        10
+#define QIPCC_SHIFT_PRI6        7
+#define QIPCC_SHIFT_PRI7        4
+
+/* QICR priority modes */
+#define QICR_GWCC               0x00040000
+#define QICR_GXCC               0x00020000
+#define QICR_GYCC               0x00010000
+#define QICR_GZCC               0x00080000
+#define QICR_GRTA               0x00200000
+#define QICR_GRTB               0x00400000
+#define QICR_HPIT_SHIFT         8
+
+/* QICNR */
+#define QICNR_WCC1T_SHIFT       20
+#define QICNR_ZCC1T_SHIFT       28
+#define QICNR_YCC1T_SHIFT       12
+#define QICNR_XCC1T_SHIFT       4
+
+/* QRICR */
+#define QRICR_RTA1T_SHIFT       20
+#define QRICR_RTB1T_SHIFT       28
+
+struct qe_ic_private {
+	struct qe_ic_map *regs;
+	unsigned int irq_offset;
+} qe_ic_private_t;
+
+extern struct hw_interrupt_type qe_ic;
+extern int qe_ic_get_irq(struct pt_regs *regs);
+
+#endif				/* __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..a943c27
--- /dev/null
+++ b/arch/powerpc/sysdev/qe_lib/qe_io.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved.
+ *
+ * Author: Li Yang <LeoLi@freescale.com>
+ *
+ * Description:
+ * QE Parallel I/O ports configuration routines.  Based on code from
+ * Shlomi Gridish <gridish@freescale.com>
+ *
+ * Changelog:
+ * Jun 21, 2006	Initial version
+ *
+ * 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 <asm/io.h>
+#include <asm/prom.h>
+#include <sysdev/fsl_soc.h>
+#undef DEBUG
+
+#define NUM_OF_PINS     32
+#define NUM_OF_PAR_IOS  7
+
+typedef struct par_io {
+	struct {
+		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 */
+	} io_regs[NUM_OF_PAR_IOS];
+} par_io_t;
+
+typedef struct qe_par_io {
+	u8 res[0xc];
+	u32 cepier;		/* QE ports interrupt event register */
+	u32 cepimr;		/* QE ports mask event register */
+	u32 cepicr;		/* QE ports control event register */
+} qe_par_io_t;
+
+static int qe_irq_ports[NUM_OF_PAR_IOS][NUM_OF_PINS] = {
+	/* 0-7 */          /* 8-15 */      /* 16 - 23 */     /* 24 - 31 */
+	{0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,1,1,0},
+	{0,0,0,1,0,1,0,0, 0,0,0,0,1,1,0,0, 0,0,0,0,0,0,0,0, 0,0,1,1,0,0,0,0},
+	{0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,1,1,1,0,0},
+	{0,0,0,0,0,0,0,0, 0,0,0,0,1,1,0,0, 1,1,0,0,0,0,0,0, 0,0,1,1,0,0,0,0},
+	{0,0,0,0,0,0,0,0, 0,0,0,0,1,1,0,0, 0,0,0,0,0,0,0,0, 1,1,1,1,0,0,0,1},
+	{0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,1,0,0,0, 0,0,0,0,0,0,0,0},
+	{0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1}
+};
+
+
+static u8 get_irq_num(u8 port, u8 pin)
+{
+	int i, j;
+	u8 num = 0;
+
+	if (qe_irq_ports[port][pin] == 0) 
+		return -1;
+	for (j = 0; j <= port; j++)
+		for (i = 0; i < pin; i++)
+			if (qe_irq_ports[j][i])
+				num++;
+	return num;
+}
+
+static par_io_t *par_io = NULL;
+static qe_par_io_t *qe_par_io = NULL;
+
+int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain,
+		      int assignment, int has_irq)
+{
+	u32 pinMask1bit, pinMask2bits, newMask2bits, tmp_val;
+
+	if (!par_io) {
+		par_io = (par_io_t *) ioremap(get_immrbase() + 0x1400,
+					      sizeof(par_io_t));
+		qe_par_io = (qe_par_io_t *) ioremap(get_immrbase() + 0xC00,
+						    sizeof(qe_par_io_t));
+
+		/* clear event bits in the event register of the QE ports */
+		out_be32(&qe_par_io->cepier, 0xFFFFFFFF);
+	}
+
+	/* calculate pin location for single and 2 bits  information */
+	pinMask1bit = (u32) (1 << (NUM_OF_PINS - (pin + 1)));
+
+	/* Set open drain, if required */
+	tmp_val = in_be32(&par_io->io_regs[port].cpodr);
+	if (open_drain)
+		out_be32(&par_io->io_regs[port].cpodr, pinMask1bit | tmp_val);
+	else
+		out_be32(&par_io->io_regs[port].cpodr, ~pinMask1bit & tmp_val);
+
+	/* define direction */
+	tmp_val = (pin > (NUM_OF_PINS / 2) - 1) ?
+	    in_be32(&par_io->io_regs[port].cpdir2) :
+	    in_be32(&par_io->io_regs[port].cpdir1);
+
+	/* get all bits mask for 2 bit per port */
+	pinMask2bits = (u32) (0x3 <<
+			      (NUM_OF_PINS -
+			       (pin % (NUM_OF_PINS / 2) + 1) * 2));
+
+	/* Get the final mask we need for the right definition */
+	newMask2bits = (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->io_regs[port].cpdir2,
+			 ~pinMask2bits & tmp_val);
+		tmp_val &= ~pinMask2bits;
+		out_be32(&par_io->io_regs[port].cpdir2, newMask2bits | tmp_val);
+	} else {
+		out_be32(&par_io->io_regs[port].cpdir1,
+			 ~pinMask2bits & tmp_val);
+		tmp_val &= ~pinMask2bits;
+		out_be32(&par_io->io_regs[port].cpdir1, newMask2bits | tmp_val);
+	}
+	/* define pin assignment */
+	tmp_val = (pin > (NUM_OF_PINS / 2) - 1) ?
+	    in_be32(&par_io->io_regs[port].cppar2) :
+	    in_be32(&par_io->io_regs[port].cppar1);
+
+	newMask2bits = (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->io_regs[port].cppar2,
+			 ~pinMask2bits & tmp_val);
+		tmp_val &= ~pinMask2bits;
+		out_be32(&par_io->io_regs[port].cppar2, newMask2bits | tmp_val);
+	} else {
+		out_be32(&par_io->io_regs[port].cppar1,
+			 ~pinMask2bits & tmp_val);
+		tmp_val &= ~pinMask2bits;
+		out_be32(&par_io->io_regs[port].cppar1, newMask2bits | tmp_val);
+	}
+
+	/* Set interrupt mask if the pin generates interrupt */
+	if (has_irq) {
+		int irq = get_irq_num(port, pin);
+		u32 mask = 0;
+
+		if (irq == -1) {
+			printk(KERN_WARNING "Port %d, pin %d is can't be "
+					"interrupt\n", port, pin);
+			return -EINVAL;
+		}
+		mask = 0x80000000 >> irq;
+
+		tmp_val = in_be32(&qe_par_io->cepimr);
+		out_be32(&qe_par_io->cepimr, mask | 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_OF_PAR_IOS)
+		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->io_regs[port].cpdata);
+
+	if (val == 0)		/* clear  */
+		out_be32(&par_io->io_regs[port].cpdata, ~pin_mask & tmp_val);
+	else			/* set */
+		out_be32(&par_io->io_regs[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;
+	
+	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_OF_PAR_IOS; i++) {
+		printk(KERN_INFO "cpodr[%d] : addr - 0x%08x, val - 0x%08x\n",
+		       i, (u32) & par_io->io_regs[i].cpodr,
+		       in_be32(&par_io->io_regs[i].cpodr));
+		printk(KERN_INFO "cpdata[%d]: addr - 0x%08x, val - 0x%08x\n",
+		       i, (u32) & par_io->io_regs[i].cpdata,
+		       in_be32(&par_io->io_regs[i].cpdata));
+		printk(KERN_INFO "cpdir1[%d]: addr - 0x%08x, val - 0x%08x\n",
+		       i, (u32) & par_io->io_regs[i].cpdir1,
+		       in_be32(&par_io->io_regs[i].cpdir1));
+		printk(KERN_INFO "cpdir2[%d]: addr - 0x%08x, val - 0x%08x\n",
+		       i, (u32) & par_io->io_regs[i].cpdir2,
+		       in_be32(&par_io->io_regs[i].cpdir2));
+		printk(KERN_INFO "cppar1[%d]: addr - 0x%08x, val - 0x%08x\n",
+		       i, (u32) & par_io->io_regs[i].cppar1,
+		       in_be32(&par_io->io_regs[i].cppar1));
+		printk(KERN_INFO "cppar2[%d]: addr - 0x%08x, val - 0x%08x\n",
+		       i, (u32) & par_io->io_regs[i].cppar2,
+		       in_be32(&par_io->io_regs[i].cppar2));
+	}
+
+	printk(KERN_INFO "QE PAR IO registars:\n");
+	printk(KERN_INFO "Base address: 0x%08x\n", (u32) qe_par_io);
+	printk(KERN_INFO "cepier : addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & qe_par_io->cepier, in_be32(&qe_par_io->cepier));
+	printk(KERN_INFO "cepimr : addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & qe_par_io->cepimr, in_be32(&qe_par_io->cepimr));
+	printk(KERN_INFO "cepicr : addr - 0x%08x, val - 0x%08x\n",
+	       (u32) & qe_par_io->cepicr, in_be32(&qe_par_io->cepicr));
+}
+
+EXPORT_SYMBOL(dump_par_io);
+#endif

^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2006-06-29 17:14 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-06-29 16:06 [PATCH 3/7] powerpc: Add QE library qe_lib--common files Gridish Shlomi-RM96313
2006-06-29 16:33 ` Kumar Gala
  -- strict thread matches above, loose matches on Subject: below --
2006-06-29 17:14 Gridish Shlomi-RM96313
2006-06-28 14:23 Li Yang-r58472
2006-06-28 17:09 ` Christoph Hellwig
2006-06-28 19:19 ` Kumar Gala

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).