RE: [PATCH 3/7] powerpc: Add QE library qe_lib--common files
From: Gridish Shlomi-RM96313 <hidden>
Date: 2006-06-29 16:06:35
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:
quoted hunk
Signed-off-by: Shlomi Gridish <redacted> Signed-off-by: Li Yang <redacted> Signed-off-by: Kim Phillips <redacted> --- 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
quoted hunk
+ 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/=20Makefile 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/=20sysdev/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
quoted hunk
|| 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/=20sysdev/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.odiff --git a/arch/powerpc/sysdev/qe_lib/mm.c b/arch/powerpc/sysdev/=20qe_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 =20reserved. + * + * Author: Shlomi Gridish [off-list ref] + * + * Description: + * QE Memory Manager. + * + * Changelog: + * Jun 28, 2006 Li Yang [off-list ref] + * - 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 freeblock */
+
+ 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 (Busylist) */
+ 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 tofree */
+ 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 =3Dp_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 newfree 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) >=3Dalign) {+ 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-=20quoted
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 arein
+ 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)) !=3D0)
+ 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",
quoted hunk
+ (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/=20qe_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/=20qe_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 =20reserved. + * + * Author: Li Yang [off-list ref] + * + * 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));
quoted hunk
+ 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/=20sysdev/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 =20reserved. + * + * Author: Shlomi Gridish [off-list ref] + * + * Description: + * General Purpose functions for the global management of the + * QUICC Engine (QE). + * + * Changelog: + * Jun 28, 2006 Li Yang [off-list ref] + * - 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),
quoted hunk
+ 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/=20sysdev/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 =20reserved. + * + * Author: Shlomi Gridish [off-list ref] + * + * Description: + * QE Interrupt Controller routines implementations. + * + * Changelog: + * Jun 28, 2006 Li Yang [off-list ref] + * - 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 sourcevector. */
+ 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(®s->qicr, tmp_qicr);
+
+ tmp_mask =3D (1 << QE_IC_GRP_W_DEST_SIGNAL_SHIFT);
+ /* choose destination signal for highest priority interrupt ineach
+ * 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 leftcomparing
+ * 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(®s->qicnr, tmp_qicnr);
+ if (tmp_qricr)
+ out_be32(®s->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-=20quoted
qivec));+ printk(KERN_INFO "qripnr: addr - 0x%08x, val - 0x%08x\n", + (u32) & p_qe_ic->regs->qripnr, in_be32(&p_qe_ic->regs-=20quoted
qripnr));+ printk(KERN_INFO "qipnr : addr - 0x%08x, val - 0x%08x\n", + (u32) & p_qe_ic->regs->qipnr, in_be32(&p_qe_ic->regs-=20quoted
qipnr));+ printk(KERN_INFO "qipxcc: addr - 0x%08x, val - 0x%08x\n", + (u32) & p_qe_ic->regs->qipxcc, in_be32(&p_qe_ic->regs-=20quoted
qipxcc));+ printk(KERN_INFO "qipycc: addr - 0x%08x, val - 0x%08x\n", + (u32) & p_qe_ic->regs->qipycc, in_be32(&p_qe_ic->regs-=20quoted
qipycc));+ printk(KERN_INFO "qipwcc: addr - 0x%08x, val - 0x%08x\n", + (u32) & p_qe_ic->regs->qipwcc, in_be32(&p_qe_ic->regs-=20quoted
qipwcc));+ printk(KERN_INFO "qipzcc: addr - 0x%08x, val - 0x%08x\n", + (u32) & p_qe_ic->regs->qipzcc, in_be32(&p_qe_ic->regs-=20quoted
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));
quoted hunk
+ printk(KERN_INFO "qrimr : addr - 0x%08x, val - 0x%08x\n", + (u32) & p_qe_ic->regs->qrimr, in_be32(&p_qe_ic->regs-=20quoted
qrimr));+ printk(KERN_INFO "qicnr : addr - 0x%08x, val - 0x%08x\n", + (u32) & p_qe_ic->regs->qicnr, in_be32(&p_qe_ic->regs-=20quoted
qicnr));+ printk(KERN_INFO "qiprta: addr - 0x%08x, val - 0x%08x\n", + (u32) & p_qe_ic->regs->qiprta, in_be32(&p_qe_ic->regs-=20quoted
qiprta));+ printk(KERN_INFO "qiprtb: addr - 0x%08x, val - 0x%08x\n", + (u32) & p_qe_ic->regs->qiprtb, in_be32(&p_qe_ic->regs-=20quoted
qiprtb));+ printk(KERN_INFO "qricr : addr - 0x%08x, val - 0x%08x\n", + (u32) & p_qe_ic->regs->qricr, in_be32(&p_qe_ic->regs-=20quoted
qricr));+ printk(KERN_INFO "qhivec: addr - 0x%08x, val - 0x%08x\n", + (u32) & p_qe_ic->regs->qhivec, in_be32(&p_qe_ic->regs-=20quoted
qhivec));+}diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.h b/arch/powerpc/=20sysdev/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 =20reserved. + * + * Author: Shlomi Gridish [off-list ref] + * + * Description: + * QE IC private definitions and structure. + * + * Changelog: + * Jun 21, 2006 Li Yang [off-list ref] + * - 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/=20sysdev/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 =20reserved. + * + * Author: Li Yang [off-list ref] + * + * Description: + * QE Parallel I/O ports configuration routines. Based on code from + * Shlomi Gridish [off-list ref] + * + * 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/