[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