[U-Boot-Users] [PATCH 4/6 part 2] QUICC Engine lib

Jiang Bo-r61859 tanya.jiang at freescale.com
Thu Aug 17 14:17:05 CEST 2006


Subject: [PATCH] QUICC Engine lib

---

 drivers/sysdev/qe_lib/Makefile    |   36 ++
 drivers/sysdev/qe_lib/board.c     |  230 ++++++++++++
 drivers/sysdev/qe_lib/mm.c        |  728
+++++++++++++++++++++++++++++++++++++
 drivers/sysdev/qe_lib/mm.h        |   33 ++
 drivers/sysdev/qe_lib/qe_common.c |  252 +++++++++++++
 5 files changed, 1279 insertions(+), 0 deletions(-)
 create mode 100644 drivers/sysdev/qe_lib/Makefile
 create mode 100644 drivers/sysdev/qe_lib/board.c
 create mode 100644 drivers/sysdev/qe_lib/mm.c
 create mode 100644 drivers/sysdev/qe_lib/mm.h
 create mode 100644 drivers/sysdev/qe_lib/qe_common.c

3278ada1f08a1f31954537269503ccf845cc3e08
diff --git a/drivers/sysdev/qe_lib/Makefile
b/drivers/sysdev/qe_lib/Makefile
new file mode 100644
index 0000000..d0bf511
--- /dev/null
+++ b/drivers/sysdev/qe_lib/Makefile
@@ -0,0 +1,36 @@
+#
+# Copyright (C) 2006 Freescale Semiconductor, Inc.
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# 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 $(TOPDIR)/config.mk
+
+LIB	= libqe.a
+
+OBJS	:= qe_common.o mm.o board.o
+
+#CFLAGS +=
+
+$(LIB):	$(OBJS)
+	$(AR) crv $@ $(OBJS)
+
+clean:
+	rm -f $(OBJS)
+
+distclean:	clean
+	rm -f $(LIB) core *.bak .depend *.flc
+
+#######################################################################
##
+
+.depend:	Makefile $(OBJS:.o=.c)
+		$(CC) -M $(CPPFLAGS) $(CFLAGS) $(OBJS:.o=.c) > $@
+
+-include .depend
+#######################################################################
##
diff --git a/drivers/sysdev/qe_lib/board.c
b/drivers/sysdev/qe_lib/board.c
new file mode 100644
index 0000000..a9dad32
--- /dev/null
+++ b/drivers/sysdev/qe_lib/board.c
@@ -0,0 +1,230 @@
+/*
+ * drivers/sysdev/qe_lib/board.c
+ * GETH io map function at here for MPC83XX QE
+ *
+ * (C) Copyright 2006 Freescale Semiconductor, Inc
+ * Author: Shlomi Gridih <gridish at freescale.com>
+ *
+ * History:
+ * 20060601 tanya jiang (tanya.jiang at freescale.com)
+ *	    Code style fixed; move from cpu/mpc83xx to drivers/sysdev
+ *
+ * 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 "common.h"
+#include "asm/io.h"
+#include "asm/errno.h"
+
+extern void qe_reset(void);
+
+#define NUM_OF_PINS		32
+#if defined(CONFIG_MPC8360)
+#define NUM_OF_PAR_IOS		7
+#endif
+
+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},
+#if defined (CONFIG_MPC8360)
+    {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}
+#endif
+};
+
+static u8 get_irq_num(u8 port, u8 pin)
+{
+	int i, j;
+	u8 num = 0;
+
+	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;
+
+static 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 *) (CFG_IMMRBAR + 0x1400);
+		qe_par_io = (qe_par_io_t *) (CFG_IMMRBAR + 0xC00);
+
+		/* 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);
+		out_be32(&par_io->io_regs[port].cpdir2,
+				 newMask2bits | tmp_val);
+	} else {
+		out_be32(&par_io->io_regs[port].cpdir1,
+				 ~pinMask2bits & tmp_val);
+		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);
+		out_be32(&par_io->io_regs[port].cppar2,
+				 newMask2bits | tmp_val);
+	} else {
+		out_be32(&par_io->io_regs[port].cppar1,
+				 ~pinMask2bits & tmp_val);
+		out_be32(&par_io->io_regs[port].cppar1,
+				 newMask2bits | tmp_val);
+	}
+
+	/* If this pin should not generate interrupt clear event mask
bit */
+	if (has_irq) {
+		int i, j, k = 0;
+		int irq = get_irq_num(port, pin);
+		u32 mask = 0;
+
+		for (j = 0; j < NUM_OF_PAR_IOS; j++)
+			for (i = 0; i < NUM_OF_PINS; i++) {
+				if (qe_irq_ports[j][i]) {
+					if (k == irq) {
+						mask = 0x80000000 >> k;
+						break;
+					}
+					k++;
+				}
+			}
+
+		if (!mask)
+			return -EINVAL;
+
+		tmp_val = in_be32(&qe_par_io->cepimr);
+		out_be32(&qe_par_io->cepimr, ~mask & tmp_val);
+	}
+	return 0;
+}
+
+#if defined (CONFIG_MPC8360EPB)
+void board_init(int rgmii)
+{
+	qe_reset();
+
+	par_io_config_pin(0, 1, 3, 0, 2, 0);	/* MDIO */
+	par_io_config_pin(0, 2, 1, 0, 1, 0);	/* MDC */
+
+	if (rgmii) {
+		/* 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?).
*/
+		u32 *tmp_reg = (u32 *) (CFG_IMMRBAR + 0x14A8);
+		u32 tmp_val = in_be32(tmp_reg);
+
+		out_be32(tmp_reg, tmp_val | 0x00003000);
+
+		par_io_config_pin(0, 3, 1, 0, 1, 0);	/* TxD0orTxD4 */
+		par_io_config_pin(0, 4, 1, 0, 1, 0);	/* TxD1orTxD5 */
+		par_io_config_pin(0, 5, 1, 0, 1, 0);	/* TxD2orTxD6 */
+		par_io_config_pin(0, 6, 1, 0, 1, 0);	/* TxD2orTxD7 */
+		par_io_config_pin(0, 7, 1, 0, 1, 0);	/* TX_ENorTX_ER
*/
+		par_io_config_pin(0, 9, 2, 0, 1, 0);	/* RxD0orRxD4 */
+		par_io_config_pin(0, 10, 2, 0, 1, 0);	/* RxD1orRxD5 */
+		par_io_config_pin(0, 11, 2, 0, 1, 0);	/* RxD2orRxD6 */
+		par_io_config_pin(0, 12, 2, 0, 1, 0);	/* RxD3orRxD7 */
+		par_io_config_pin(0, 15, 2, 0, 1, 0);	/* RX_DVorRX_ER
*/
+		par_io_config_pin(0, 0, 2, 0, 1, 0);	/* RX_CLK */
+		par_io_config_pin(2, 9, 1, 0, 3, 0);	/* GTX_CLK -
CLK10 */
+		par_io_config_pin(2, 8, 2, 0, 1, 0);	/* GTX125 - CLK9
*/
+	} else {
+		par_io_config_pin(0, 3, 1, 0, 1, 0);	/* TxD0 */
+		par_io_config_pin(0, 4, 1, 0, 1, 0);	/* TxD1 */
+		par_io_config_pin(0, 5, 1, 0, 1, 0);	/* TxD2 */
+		par_io_config_pin(0, 6, 1, 0, 1, 0);	/* TxD3 */
+		par_io_config_pin(1, 6, 1, 0, 3, 0);	/* TxD4 */
+		par_io_config_pin(1, 7, 1, 0, 1, 0);	/* TxD5 */
+		par_io_config_pin(1, 9, 1, 0, 2, 0);	/* TxD6 */
+		par_io_config_pin(1, 10, 1, 0, 2, 0);	/* TxD7 */
+		par_io_config_pin(0, 9, 2, 0, 1, 0);	/* RxD0 */
+		par_io_config_pin(0, 10, 2, 0, 1, 0);	/* RxD1 */
+		par_io_config_pin(0, 11, 2, 0, 1, 0);	/* RxD2 */
+		par_io_config_pin(0, 12, 2, 0, 1, 0);	/* RxD3 */
+		par_io_config_pin(0, 13, 2, 0, 1, 0);	/* RxD4 */
+		par_io_config_pin(1, 1, 2, 0, 2, 0);	/* RxD5 */
+		par_io_config_pin(1, 0, 2, 0, 2, 0);	/* RxD6 */
+		par_io_config_pin(1, 4, 2, 0, 2, 0);	/* RxD7 */
+		par_io_config_pin(0, 7, 1, 0, 1, 0);	/* TX_EN */
+		par_io_config_pin(0, 8, 1, 0, 1, 0);	/* TX_ER */
+		par_io_config_pin(0, 15, 2, 0, 1, 0);	/* RX_DV */
+		par_io_config_pin(0, 16, 2, 0, 1, 0);	/* RX_ER */
+		par_io_config_pin(0, 0, 2, 0, 1, 0);	/* RX_CLK */
+		par_io_config_pin(2, 9, 1, 0, 3, 0);	/* GTX_CLK -
CLK10 */
+		par_io_config_pin(2, 8, 2, 0, 1, 0);	/* GTX125 - CLK9
*/
+	}
+}
+#else
+#error need ucc io for geth!
+#endif
diff --git a/drivers/sysdev/qe_lib/mm.c b/drivers/sysdev/qe_lib/mm.c
new file mode 100644
index 0000000..aaf3aba
--- /dev/null
+++ b/drivers/sysdev/qe_lib/mm.c
@@ -0,0 +1,728 @@
+/*
+ * drivers/sysdev/qe_lib/mm.c
+ * Memory Manager.
+ *
+ * (C) Copyright 2006 Freescale Semiconductor, Inc
+ * Author: Shlomi Gridish <gridish at freescale.com>
+ *
+ * History:
+ * 20060601 tanya jiang (tanya.jiang at freescale.com)
+ *	    Code style fixed; move from cpu/mpc83xx to drivers/sysdev
+ *
+ * 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 "common.h"
+#include "asm/errno.h"
+#include "linux/string.h"
+#include "malloc.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 *) malloc(sizeof(mem_block_t));
+	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 *) malloc(sizeof(free_block_t));
+	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 *) malloc(sizeof(busy_block_t));
+	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;
+						free(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;
+						free(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;
+					free(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;
+					free(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;
+					free(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 *) malloc(sizeof(mm_t));
+	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);
+}
+
+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;
+		free(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;
+			free(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;
+		free(p_blk);
+	}
+
+	/* release memory allocated for MM object itself */
+	free(p_mm);
+}
+
+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);
+}
+
+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);
+}
+
+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;
+
+	free(p_busy_blk);
+
+	return (0);
+}
+
+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;
+	printf("List of busy blocks:\n");
+	while (p_busy_blk) {
+		printf("\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;
+	}
+
+	printf("\nLists of free blocks according to alignment:\n");
+	for (i = 0; i <= MAX_ALIGNMENT; i++) {
+		printf("%d alignment:\n", (0x1 << i));
+		p_free_blk = p_mm->free_blocks[i];
+		while (p_free_blk) {
+			printf("\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;
+		}
+		printf("\n");
+	}
+}
diff --git a/drivers/sysdev/qe_lib/mm.h b/drivers/sysdev/qe_lib/mm.h
new file mode 100644
index 0000000..0dca3a0
--- /dev/null
+++ b/drivers/sysdev/qe_lib/mm.h
@@ -0,0 +1,33 @@
+/*
+ * drivers/sysdev/qe_lib/mm.h
+ * MURAM operation.
+ *
+ * Copyright (C) 2006 Freescale Semiconductor, Inc
+ *
+ * Author: Shlomi Gridish <gridish at freescale.com>
+ *
+ * History:
+ * 20060601 tanya jiang (tanya.jiang at freescale.com)
+ *	    Code style fixed; move from cpu/mpc83xx to drivers/sysdev
+ *
+ * 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 __MM_H__
+#define __MM_H__
+
+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);
+
+static inline void *ERR_PTR(long error)
+{
+	return (void *)error;
+}
+
+#endif	/* __MM_H__ */
diff --git a/drivers/sysdev/qe_lib/qe_common.c
b/drivers/sysdev/qe_lib/qe_common.c
new file mode 100644
index 0000000..94abe24
--- /dev/null
+++ b/drivers/sysdev/qe_lib/qe_common.c
@@ -0,0 +1,252 @@
+/*
+ * drivers/sysdev/qe_lib/qe_common.c
+ *
+ * General Purpose functions for the global management of the
+ * QUICC Engine (QE).
+ *
+ * (C) Copyright 2006 Freescale Semiconductor, Inc
+ * Author: Shlomi Gridish <gridish at freescale.com>
+ *
+ * History:
+ * 20060601 tanya jiang (tanya.jiang at freescale.com)
+ *	    Code style fixed; move from cpu/mpc83xx to drivers/sysdev
+ *
+ * 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 "common.h"
+#include "asm/errno.h"
+#include "asm/io.h"
+
+#include "immap_qe.h"
+#include "qe.h"
+#include "mm.h"
+#include "qe_common.h"
+
+#define QE_MAP_SIZE    (0x100000)	/* 1MB */
+
+/* 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.
+ */
+qe_map_t *qe_immr;
+static qe_snum_t snums[QE_NUM_OF_SNUM];	/* Dynamically allocated
SNUMs  */
+
+static void qe_snums_init(void);
+static void qe_muram_init(void);
+
+void qe_reset(void)
+{
+	qe_immr = (qe_map_t *) (CFG_IMMRBAR + 0x00100000);
+
+	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();
+
+	qe_muram_alloc(sizeof(qe_timer_tables_t), 1);
+}
+
+int qe_issue_cmd(uint cmd, uint device, u8 TypeId, u32 cmd_input)
+{
+	u32 cecr;
+	u8 shift;
+
+	if (cmd == QE_RESET) {
+		out_be32(&qe_immr->cp.cecr, (u32) (cmd | QE_CR_FLG));
+	} else {
+		if (device == QE_CR_SUBBLOCK_USB)
+			shift = 4;
+		else
+			shift = 6;
+		out_be32(&qe_immr->cp.cecdr, cmd_input);
+		out_be32(&qe_immr->cp.cecr,
+			 (cmd | QE_CR_FLG | device | (u32) TypeId <<
shift));
+	}
+
+	/* wait for the QE_CR_FLG to clear */
+	do {
+		cecr = in_be32(&qe_immr->cp.cecr);
+	} while (cecr & QE_CR_FLG);
+
+	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.
+ */
+#define BRG_CLK    (gd->bd->bi_brgfreq)
+
+/* This function is used by UARTS, or anything else that uses a 16x
+ * oversampled clock.
+ */
+void qe_setbrg(uint brg, uint rate)
+{
+	DECLARE_GLOBAL_DATA_PTR;
+	volatile uint *bp;
+	u32 divisor;
+	int div16 = 0;
+
+	bp = (uint *) & qe_immr->brg.brgc1;
+	bp += brg;
+
+	divisor = (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)
+{
+	int snum = -EBUSY;
+	int i;
+
+	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;
+		}
+	}
+
+	return 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;
+		}
+	}
+}
+
+/*
+ * muram_alloc / muram_free bits.
+ */
+static void *mm = NULL;
+
+static void qe_muram_init(void)
+{
+	mm_init(&mm, (u32) qe_muram_addr(QE_MURAM_DATAONLY_BASE),
+		QE_MURAM_DATAONLY_SIZE);
+}
+
+/* This function returns an index into the MURAM area.
+ */
+uint qe_muram_alloc(uint size, uint align)
+{
+	void *start;
+
+	start = mm_get(mm, (u32) size, (int)align, "QE");
+	if (!IS_MURAM_ERR((u32) start))
+		start = (void *)((u32) start - (u32) qe_immr->muram);
+
+	return (uint) start;
+}
+
+int qe_muram_free(uint offset)
+{
+	int ret;
+
+	ret = mm_put(mm, (u32) qe_muram_addr(offset));
+
+	return ret;
+}
+
+/* not sure if this is ever needed */
+uint qe_muram_alloc_fixed(uint offset, uint size, uint align)
+{
+	void *start;
+
+	start = mm_get_force(mm, (u32) offset, (u32) size, "QE");
+	if (!IS_MURAM_ERR((u32) start))
+		start = (void *)((u32) start - (u32) qe_immr->muram);
+
+	return (uint) start;
+}
+
+void qe_muram_dump(void)
+{
+	mm_dump(mm);
+}
+
+void *qe_muram_addr(uint offset)
+{
+	return (void *)&qe_immr->muram[offset];
+}
-- 
1.3.GIT




More information about the U-Boot mailing list