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

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@freescale.com + * + * History: + * 20060601 tanya jiang (tanya.jiang@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@freescale.com + * + * History: + * 20060601 tanya jiang (tanya.jiang@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@freescale.com + * + * History: + * 20060601 tanya jiang (tanya.jiang@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@freescale.com + * + * History: + * 20060601 tanya jiang (tanya.jiang@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]; +}
participants (1)
-
Jiang Bo-r61859