blob: c1f5ee25e5722c7d421e8c7ef68a4e49cc75d6f0 [file] [log] [blame]
/*
* Copyright (c) 2009 Mindspeed Technologies, Inc.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "hal.h"
#include "pfe/pfe.h"
void *cbus_base_addr;
void *ddr_base_addr;
unsigned long ddr_phys_base_addr;
unsigned int ddr_size;
static struct pe_info pe[MAX_PE];
/** Initializes the PFE library.
* Must be called before using any of the library functions.
*
* @param[in] cbus_base CBUS virtual base address (as mapped in the host CPU address space)
* @param[in] ddr_base PFE DDR range virtual base address (as mapped in the host CPU address space)
* @param[in] ddr_phys_base PFE DDR range physical base address (as mapped in platform)
* @param[in] size PFE DDR range size (as defined by the host software)
*/
void pfe_lib_init(void *cbus_base, void *ddr_base, unsigned long ddr_phys_base, unsigned int size)
{
cbus_base_addr = cbus_base;
ddr_base_addr = ddr_base;
ddr_phys_base_addr = ddr_phys_base;
ddr_size = size;
pe[CLASS0_ID].dmem_base_addr = CLASS_DMEM_BASE_ADDR(0);
pe[CLASS0_ID].pmem_base_addr = CLASS_IMEM_BASE_ADDR(0);
pe[CLASS0_ID].pmem_size = CLASS_IMEM_SIZE;
pe[CLASS0_ID].mem_access_wdata = CLASS_MEM_ACCESS_WDATA;
pe[CLASS0_ID].mem_access_addr = CLASS_MEM_ACCESS_ADDR;
pe[CLASS0_ID].mem_access_rdata = CLASS_MEM_ACCESS_RDATA;
pe[CLASS1_ID].dmem_base_addr = CLASS_DMEM_BASE_ADDR(1);
pe[CLASS1_ID].pmem_base_addr = CLASS_IMEM_BASE_ADDR(1);
pe[CLASS1_ID].pmem_size = CLASS_IMEM_SIZE;
pe[CLASS1_ID].mem_access_wdata = CLASS_MEM_ACCESS_WDATA;
pe[CLASS1_ID].mem_access_addr = CLASS_MEM_ACCESS_ADDR;
pe[CLASS1_ID].mem_access_rdata = CLASS_MEM_ACCESS_RDATA;
pe[CLASS2_ID].dmem_base_addr = CLASS_DMEM_BASE_ADDR(2);
pe[CLASS2_ID].pmem_base_addr = CLASS_IMEM_BASE_ADDR(2);
pe[CLASS2_ID].pmem_size = CLASS_IMEM_SIZE;
pe[CLASS2_ID].mem_access_wdata = CLASS_MEM_ACCESS_WDATA;
pe[CLASS2_ID].mem_access_addr = CLASS_MEM_ACCESS_ADDR;
pe[CLASS2_ID].mem_access_rdata = CLASS_MEM_ACCESS_RDATA;
pe[CLASS3_ID].dmem_base_addr = CLASS_DMEM_BASE_ADDR(3);
pe[CLASS3_ID].pmem_base_addr = CLASS_IMEM_BASE_ADDR(3);
pe[CLASS3_ID].pmem_size = CLASS_IMEM_SIZE;
pe[CLASS3_ID].mem_access_wdata = CLASS_MEM_ACCESS_WDATA;
pe[CLASS3_ID].mem_access_addr = CLASS_MEM_ACCESS_ADDR;
pe[CLASS3_ID].mem_access_rdata = CLASS_MEM_ACCESS_RDATA;
#if !defined(CONFIG_PLATFORM_PCI)
pe[CLASS4_ID].dmem_base_addr = CLASS_DMEM_BASE_ADDR(4);
pe[CLASS4_ID].pmem_base_addr = CLASS_IMEM_BASE_ADDR(4);
pe[CLASS4_ID].pmem_size = CLASS_IMEM_SIZE;
pe[CLASS4_ID].mem_access_wdata = CLASS_MEM_ACCESS_WDATA;
pe[CLASS4_ID].mem_access_addr = CLASS_MEM_ACCESS_ADDR;
pe[CLASS4_ID].mem_access_rdata = CLASS_MEM_ACCESS_RDATA;
pe[CLASS5_ID].dmem_base_addr = CLASS_DMEM_BASE_ADDR(5);
pe[CLASS5_ID].pmem_base_addr = CLASS_IMEM_BASE_ADDR(5);
pe[CLASS5_ID].pmem_size = CLASS_IMEM_SIZE;
pe[CLASS5_ID].mem_access_wdata = CLASS_MEM_ACCESS_WDATA;
pe[CLASS5_ID].mem_access_addr = CLASS_MEM_ACCESS_ADDR;
pe[CLASS5_ID].mem_access_rdata = CLASS_MEM_ACCESS_RDATA;
#endif
pe[TMU0_ID].dmem_base_addr = TMU_DMEM_BASE_ADDR(0);
pe[TMU0_ID].pmem_base_addr = TMU_IMEM_BASE_ADDR(0);
pe[TMU0_ID].pmem_size = TMU_IMEM_SIZE;
pe[TMU0_ID].mem_access_wdata = TMU_MEM_ACCESS_WDATA;
pe[TMU0_ID].mem_access_addr = TMU_MEM_ACCESS_ADDR;
pe[TMU0_ID].mem_access_rdata = TMU_MEM_ACCESS_RDATA;
#if !defined(CONFIG_TMU_DUMMY)
pe[TMU1_ID].dmem_base_addr = TMU_DMEM_BASE_ADDR(1);
pe[TMU1_ID].pmem_base_addr = TMU_IMEM_BASE_ADDR(1);
pe[TMU1_ID].pmem_size = TMU_IMEM_SIZE;
pe[TMU1_ID].mem_access_wdata = TMU_MEM_ACCESS_WDATA;
pe[TMU1_ID].mem_access_addr = TMU_MEM_ACCESS_ADDR;
pe[TMU1_ID].mem_access_rdata = TMU_MEM_ACCESS_RDATA;
pe[TMU2_ID].dmem_base_addr = TMU_DMEM_BASE_ADDR(2);
pe[TMU2_ID].pmem_base_addr = TMU_IMEM_BASE_ADDR(2);
pe[TMU2_ID].pmem_size = TMU_IMEM_SIZE;
pe[TMU2_ID].mem_access_wdata = TMU_MEM_ACCESS_WDATA;
pe[TMU2_ID].mem_access_addr = TMU_MEM_ACCESS_ADDR;
pe[TMU2_ID].mem_access_rdata = TMU_MEM_ACCESS_RDATA;
pe[TMU3_ID].dmem_base_addr = TMU_DMEM_BASE_ADDR(3);
pe[TMU3_ID].pmem_base_addr = TMU_IMEM_BASE_ADDR(3);
pe[TMU3_ID].pmem_size = TMU_IMEM_SIZE;
pe[TMU3_ID].mem_access_wdata = TMU_MEM_ACCESS_WDATA;
pe[TMU3_ID].mem_access_addr = TMU_MEM_ACCESS_ADDR;
pe[TMU3_ID].mem_access_rdata = TMU_MEM_ACCESS_RDATA;
#endif
#if !defined(CONFIG_UTIL_DISABLED)
pe[UTIL_ID].dmem_base_addr = UTIL_DMEM_BASE_ADDR;
pe[UTIL_ID].mem_access_wdata = UTIL_MEM_ACCESS_WDATA;
pe[UTIL_ID].mem_access_addr = UTIL_MEM_ACCESS_ADDR;
pe[UTIL_ID].mem_access_rdata = UTIL_MEM_ACCESS_RDATA;
#endif
}
/** Writes a buffer to PE internal memory from the host
* through indirect access registers.
*
* @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, ..., UTIL_ID)
* @param[in] src Buffer source address
* @param[in] mem_access_addr DMEM destination address (must be 32bit aligned)
* @param[in] len Number of bytes to copy
*/
void pe_mem_memcpy_to32(int id, u32 mem_access_addr, const void *src, unsigned int len)
{
u32 offset = 0, val, addr;
unsigned int len32 = len >> 2;
int i;
addr = mem_access_addr | PE_MEM_ACCESS_WRITE | PE_MEM_ACCESS_BYTE_ENABLE(0, 4);
for (i = 0; i < len32; i++, offset += 4, src += 4) {
val = *(u32 *)src;
writel(cpu_to_be32(val), pe[id].mem_access_wdata);
writel(addr + offset, pe[id].mem_access_addr);
}
if ((len = (len & 0x3))) {
val = 0;
addr = (mem_access_addr | PE_MEM_ACCESS_WRITE | PE_MEM_ACCESS_BYTE_ENABLE(0, len)) + offset;
for (i = 0; i < len; i++, src++)
val |= (*(u8 *)src) << (8 * i);
writel(cpu_to_be32(val), pe[id].mem_access_wdata);
writel(addr, pe[id].mem_access_addr);
}
}
/** Writes a buffer to PE internal data memory (DMEM) from the host
* through indirect access registers.
* @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, ..., UTIL_ID)
* @param[in] src Buffer source address
* @param[in] dst DMEM destination address (must be 32bit aligned)
* @param[in] len Number of bytes to copy
*/
void pe_dmem_memcpy_to32(int id, u32 dst, const void *src, unsigned int len)
{
pe_mem_memcpy_to32(id, pe[id].dmem_base_addr | dst | PE_MEM_ACCESS_DMEM, src, len);
}
/** Writes a buffer to PE internal program memory (PMEM) from the host
* through indirect access registers.
* @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, ..., TMU3_ID)
* @param[in] src Buffer source address
* @param[in] dst PMEM destination address (must be 32bit aligned)
* @param[in] len Number of bytes to copy
*/
void pe_pmem_memcpy_to32(int id, u32 dst, const void *src, unsigned int len)
{
pe_mem_memcpy_to32(id, pe[id].pmem_base_addr | (dst & (pe[id].pmem_size - 1)) | PE_MEM_ACCESS_IMEM, src, len);
}
/** Reads PE internal program memory (IMEM) from the host
* through indirect access registers.
* @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, ..., TMU3_ID)
* @param[in] addr PMEM read address (must be aligned on size)
* @param[in] size Number of bytes to read (maximum 4, must not cross 32bit boundaries)
* @return the data read (in PE endianess, i.e BE).
*/
u32 pe_pmem_read(int id, u32 addr, u8 size)
{
u32 offset = addr & 0x3;
u32 mask = 0xffffffff >> ((4 - size) << 3);
u32 val;
addr = pe[id].pmem_base_addr | ((addr & ~0x3) & (pe[id].pmem_size - 1)) | PE_MEM_ACCESS_IMEM | PE_MEM_ACCESS_BYTE_ENABLE(offset, size);
writel(addr, pe[id].mem_access_addr);
val = be32_to_cpu(readl(pe[id].mem_access_rdata));
return (val >> (offset << 3)) & mask;
}
/** Writes PE internal data memory (DMEM) from the host
* through indirect access registers.
* @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, ..., UTIL_ID)
* @param[in] addr DMEM write address (must be aligned on size)
* @param[in] val Value to write (in PE endianess, i.e BE)
* @param[in] size Number of bytes to write (maximum 4, must not cross 32bit boundaries)
*/
void pe_dmem_write(int id, u32 val, u32 addr, u8 size)
{
u32 offset = addr & 0x3;
addr = pe[id].dmem_base_addr | (addr & ~0x3) | PE_MEM_ACCESS_WRITE | PE_MEM_ACCESS_DMEM | PE_MEM_ACCESS_BYTE_ENABLE(offset, size);
/* Indirect access interface is byte swapping data being written */
writel(cpu_to_be32(val << (offset << 3)), pe[id].mem_access_wdata);
writel(addr, pe[id].mem_access_addr);
}
/** Reads PE internal data memory (DMEM) from the host
* through indirect access registers.
* @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, ..., UTIL_ID)
* @param[in] addr DMEM read address (must be aligned on size)
* @param[in] size Number of bytes to read (maximum 4, must not cross 32bit boundaries)
* @return the data read (in PE endianess, i.e BE).
*/
u32 pe_dmem_read(int id, u32 addr, u8 size)
{
u32 offset = addr & 0x3;
u32 mask = 0xffffffff >> ((4 - size) << 3);
u32 val;
addr = pe[id].dmem_base_addr | (addr & ~0x3) | PE_MEM_ACCESS_DMEM | PE_MEM_ACCESS_BYTE_ENABLE(offset, size);
writel(addr, pe[id].mem_access_addr);
/* Indirect access interface is byte swapping data being read */
val = be32_to_cpu(readl(pe[id].mem_access_rdata));
return (val >> (offset << 3)) & mask;
}
/** This function is used to write to CLASS internal bus peripherals (ccu, pe-lem) from the host
* through indirect access registers.
* @param[in] val value to write
* @param[in] addr Address to write to (must be aligned on size)
* @param[in] size Number of bytes to write (1, 2 or 4)
*
*/
void class_bus_write(u32 val, u32 addr, u8 size)
{
u32 offset = addr & 0x3;
writel((addr & CLASS_BUS_ACCESS_BASE_MASK), CLASS_BUS_ACCESS_BASE);
addr = (addr & ~CLASS_BUS_ACCESS_BASE_MASK) | PE_MEM_ACCESS_WRITE | (size << 24);
writel(cpu_to_be32(val << (offset << 3)), CLASS_BUS_ACCESS_WDATA);
writel(addr, CLASS_BUS_ACCESS_ADDR);
}
/** Reads from CLASS internal bus peripherals (ccu, pe-lem) from the host
* through indirect access registers.
* @param[in] addr Address to read from (must be aligned on size)
* @param[in] size Number of bytes to read (1, 2 or 4)
* @return the read data
*
*/
u32 class_bus_read(u32 addr, u8 size)
{
u32 offset = addr & 0x3;
u32 mask = 0xffffffff >> ((4 - size) << 3);
u32 val;
writel((addr & CLASS_BUS_ACCESS_BASE_MASK), CLASS_BUS_ACCESS_BASE);
addr = (addr & ~CLASS_BUS_ACCESS_BASE_MASK) | (size << 24);
writel(addr, CLASS_BUS_ACCESS_ADDR);
val = be32_to_cpu(readl(CLASS_BUS_ACCESS_RDATA));
return (val >> (offset << 3)) & mask;
}
/** Writes data to the cluster memory (PE_LMEM)
* @param[in] dst PE LMEM destination address (must be 32bit aligned)
* @param[in] src Buffer source address
* @param[in] len Number of bytes to copy
*/
void class_pe_lmem_memcpy_to32(u32 dst, const void *src, unsigned int len)
{
u32 len32 = len >> 2;
int i;
for (i = 0; i < len32; i++, src += 4, dst += 4)
class_bus_write(*(u32 *)src, dst, 4);
if (len & 0x2)
{
class_bus_write(*(u16 *)src, dst, 2);
src += 2;
dst += 2;
}
if (len & 0x1)
{
class_bus_write(*(u8 *)src, dst, 1);
src++;
dst++;
}
}
/** Writes value to the cluster memory (PE_LMEM)
* @param[in] dst PE LMEM destination address (must be 32bit aligned)
* @param[in] val Value to write
* @param[in] len Number of bytes to write
*/
void class_pe_lmem_memset(u32 dst, int val, unsigned int len)
{
u32 len32 = len >> 2;
int i;
val = val | (val << 8) | (val << 16) | (val << 24);
for (i = 0; i < len32; i++, dst += 4)
class_bus_write(val, dst, 4);
if (len & 0x2)
{
class_bus_write(val, dst, 2);
dst += 2;
}
if (len & 0x1)
{
class_bus_write(val, dst, 1);
dst++;
}
}
/** Writes UTIL program memory (DDR) from the host.
*
* @param[in] addr Address to write (virtual, must be aligned on size)
* @param[in] val Value to write (in PE endianess, i.e BE)
* @param[in] size Number of bytes to write (2 or 4)
*/
static void util_pmem_write(u32 val, void *addr, u8 size)
{
void *addr64 = (void *)((unsigned long)addr & ~0x7);
unsigned long off = 8 - ((unsigned long)addr & 0x7) - size;
//IMEM should be loaded as a 64bit swapped value in a 64bit aligned location
if (size == 4)
writel(be32_to_cpu(val), addr64 + off);
else
writew(be16_to_cpu((u16)val), addr64 + off);
}
/** Writes a buffer to UTIL program memory (DDR) from the host.
*
* @param[in] dst Address to write (virtual, must be at least 16bit aligned)
* @param[in] src Buffer to write (in PE endianess, i.e BE, must have same alignment as dst)
* @param[in] len Number of bytes to write (must be at least 16bit aligned)
*/
static void util_pmem_memcpy(void *dst, const void *src, unsigned int len)
{
unsigned int len32;
int i;
if ((unsigned long)src & 0x2) {
util_pmem_write(*(u16 *)src, dst, 2);
src += 2;
dst += 2;
len -= 2;
}
len32 = len >> 2;
for (i = 0; i < len32; i++, dst += 4, src += 4)
util_pmem_write(*(u32 *)src, dst, 4);
if (len & 0x2)
util_pmem_write(*(u16 *)src, dst, len & 0x2);
}
/** Loads an elf section into pmem
* Code needs to be at least 16bit aligned and only PROGBITS sections are supported
*
* @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, ..., TMU3_ID)
* @param[in] data pointer to the elf firmware
* @param[in] shdr pointer to the elf section header
*
*/
static int pe_load_pmem_section(int id, const void *data, Elf32_Shdr *shdr)
{
u32 offset = be32_to_cpu(shdr->sh_offset);
u32 addr = be32_to_cpu(shdr->sh_addr);
u32 size = be32_to_cpu(shdr->sh_size);
u32 type = be32_to_cpu(shdr->sh_type);
#if !defined(CONFIG_UTIL_DISABLED)
if (id == UTIL_ID)
{
printk(KERN_ERR "%s: unsuported pmem section for UTIL\n", __func__);
return -EINVAL;
}
#endif
if (((unsigned long)(data + offset) & 0x3) != (addr & 0x3))
{
printk(KERN_ERR "%s: load address(%x) and elf file address(%lx) don't have the same alignment\n",
__func__, addr, (unsigned long) data + offset);
return -EINVAL;
}
if (addr & 0x1)
{
printk(KERN_ERR "%s: load address(%x) is not 16bit aligned\n", __func__, addr);
return -EINVAL;
}
if (size & 0x1)
{
printk(KERN_ERR "%s: load size(%x) is not 16bit aligned\n", __func__, size);
return -EINVAL;
}
switch (type)
{
case SHT_PROGBITS:
pe_pmem_memcpy_to32(id, addr, data + offset, size);
break;
default:
printk(KERN_ERR "%s: unsuported section type(%x)\n", __func__, type);
return -EINVAL;
break;
}
return 0;
}
/** Loads an elf section into dmem
* Data needs to be at least 32bit aligned, NOBITS sections are correctly initialized to 0
*
* @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, ..., UTIL_ID)
* @param[in] data pointer to the elf firmware
* @param[in] shdr pointer to the elf section header
*
*/
static int pe_load_dmem_section(int id, const void *data, Elf32_Shdr *shdr)
{
u32 offset = be32_to_cpu(shdr->sh_offset);
u32 addr = be32_to_cpu(shdr->sh_addr);
u32 size = be32_to_cpu(shdr->sh_size);
u32 type = be32_to_cpu(shdr->sh_type);
u32 size32 = size >> 2;
int i;
if (((unsigned long)(data + offset) & 0x3) != (addr & 0x3))
{
printk(KERN_ERR "%s: load address(%x) and elf file address(%lx) don't have the same alignment\n",
__func__, addr, (unsigned long)data + offset);
return -EINVAL;
}
if (addr & 0x3)
{
printk(KERN_ERR "%s: load address(%x) is not 32bit aligned\n", __func__, addr);
return -EINVAL;
}
switch (type)
{
case SHT_PROGBITS:
pe_dmem_memcpy_to32(id, addr, data + offset, size);
break;
case SHT_NOBITS:
for (i = 0; i < size32; i++, addr += 4)
pe_dmem_write(id, 0, addr, 4);
if (size & 0x3)
pe_dmem_write(id, 0, addr, size & 0x3);
break;
default:
printk(KERN_ERR "%s: unsuported section type(%x)\n", __func__, type);
return -EINVAL;
break;
}
return 0;
}
/** Loads an elf section into DDR
* Data needs to be at least 32bit aligned, NOBITS sections are correctly initialized to 0
*
* @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, ..., UTIL_ID)
* @param[in] data pointer to the elf firmware
* @param[in] shdr pointer to the elf section header
*
*/
static int pe_load_ddr_section(int id, const void *data, Elf32_Shdr *shdr)
{
u32 offset = be32_to_cpu(shdr->sh_offset);
u32 addr = be32_to_cpu(shdr->sh_addr);
u32 size = be32_to_cpu(shdr->sh_size);
u32 type = be32_to_cpu(shdr->sh_type);
u32 flags = be32_to_cpu(shdr->sh_flags);
switch (type)
{
case SHT_PROGBITS:
if (flags & SHF_EXECINSTR)
{
if (id <= CLASS_MAX_ID)
{
/* DO the loading only once in DDR */
if (id == CLASS0_ID)
{
printk(KERN_ERR "%s: load address(%x) and elf file address(%lx) rcvd\n", __func__, addr, (unsigned long)data + offset);
if (((unsigned long)(data + offset) & 0x3) != (addr & 0x3))
{
printk(KERN_ERR "%s: load address(%x) and elf file address(%lx) don't have the same alignment\n",
__func__, addr, (unsigned long)data + offset);
return -EINVAL;
}
if (addr & 0x1)
{
printk(KERN_ERR "%s: load address(%x) is not 16bit aligned\n", __func__, addr);
return -EINVAL;
}
if (size & 0x1)
{
printk(KERN_ERR "%s: load length(%x) is not 16bit aligned\n", __func__, size);
return -EINVAL;
}
memcpy(DDR_PHYS_TO_VIRT(addr), data + offset, size);
}
}
#if !defined(CONFIG_UTIL_DISABLED)
else if (id == UTIL_ID)
{
if (((unsigned long)(data + offset) & 0x3) != (addr & 0x3))
{
printk(KERN_ERR "%s: load address(%x) and elf file address(%lx) don't have the same alignment\n",
__func__, addr, (unsigned long)data + offset);
return -EINVAL;
}
if (addr & 0x1)
{
printk(KERN_ERR "%s: load address(%x) is not 16bit aligned\n", __func__, addr);
return -EINVAL;
}
if (size & 0x1)
{
printk(KERN_ERR "%s: load length(%x) is not 16bit aligned\n", __func__, size);
return -EINVAL;
}
util_pmem_memcpy(DDR_PHYS_TO_VIRT(addr), data + offset, size);
}
#endif
else
{
printk(KERN_ERR "%s: unsuported ddr section type(%x) for PE(%d)\n", __func__, type, id);
return -EINVAL;
}
}
else
{
memcpy(DDR_PHYS_TO_VIRT(addr), data + offset, size);
}
break;
case SHT_NOBITS:
memset(DDR_PHYS_TO_VIRT(addr), 0, size);
break;
default:
printk(KERN_ERR "%s: unsuported section type(%x)\n", __func__, type);
return -EINVAL;
break;
}
return 0;
}
/** Loads an elf section into pe lmem
* Data needs to be at least 32bit aligned, NOBITS sections are correctly initialized to 0
*
* @param[in] id PE identification (CLASS0_ID,..., CLASS5_ID)
* @param[in] data pointer to the elf firmware
* @param[in] shdr pointer to the elf section header
*
*/
static int pe_load_pe_lmem_section(int id, const void *data, Elf32_Shdr *shdr)
{
u32 offset = be32_to_cpu(shdr->sh_offset);
u32 addr = be32_to_cpu(shdr->sh_addr);
u32 size = be32_to_cpu(shdr->sh_size);
u32 type = be32_to_cpu(shdr->sh_type);
if (id > CLASS_MAX_ID)
{
printk(KERN_ERR "%s: unsuported pe-lmem section type(%x) for PE(%d)\n", __func__, type, id);
return -EINVAL;
}
if (((unsigned long)(data + offset) & 0x3) != (addr & 0x3))
{
printk(KERN_ERR "%s: load address(%x) and elf file address(%lx) don't have the same alignment\n",
__func__, addr, (unsigned long)data + offset);
return -EINVAL;
}
if (addr & 0x3)
{
printk(KERN_ERR "%s: load address(%x) is not 32bit aligned\n", __func__, addr);
return -EINVAL;
}
switch (type)
{
case SHT_PROGBITS:
class_pe_lmem_memcpy_to32(addr, data + offset, size);
break;
case SHT_NOBITS:
class_pe_lmem_memset(addr, 0, size);
break;
default:
printk(KERN_ERR "%s: unsuported section type(%x)\n", __func__, type);
return -EINVAL;
break;
}
return 0;
}
/** Loads an elf section into a PE
* For now only supports loading a section to dmem (all PE's), pmem (class and tmu PE's),
* DDDR (util PE code)
*
* @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, ..., UTIL_ID)
* @param[in] data pointer to the elf firmware
* @param[in] shdr pointer to the elf section header
*
*/
int pe_load_elf_section(int id, const void *data, Elf32_Shdr *shdr)
{
u32 addr = be32_to_cpu(shdr->sh_addr);
u32 size = be32_to_cpu(shdr->sh_size);
if (IS_DMEM(addr, size))
return pe_load_dmem_section(id, data, shdr);
else if (IS_PMEM(addr, size))
return pe_load_pmem_section(id, data, shdr);
else if (IS_PFE_LMEM(addr, size))
return 0; /* FIXME */
else if (IS_PHYS_DDR(addr, size))
return pe_load_ddr_section(id, data, shdr);
else if (IS_PE_LMEM(addr, size))
return pe_load_pe_lmem_section(id, data, shdr);
else {
printk(KERN_ERR "%s: unsuported memory range(%x)\n", __func__, addr);
// return -EINVAL;
}
return 0;
}
/**************************** BMU ***************************/
/** Initializes a BMU block.
* @param[in] base BMU block base address
* @param[in] cfg BMU configuration
*/
void bmu_init(void *base, BMU_CFG *cfg)
{
bmu_disable(base);
bmu_set_config(base, cfg);
bmu_reset(base);
}
/** Resets a BMU block.
* @param[in] base BMU block base address
*/
void bmu_reset(void *base)
{
writel(CORE_SW_RESET, base + BMU_CTRL);
/* Wait for self clear */
while (readl(base + BMU_CTRL) & CORE_SW_RESET) ;
}
/** Enabled a BMU block.
* @param[in] base BMU block base address
*/
void bmu_enable(void *base)
{
writel (CORE_ENABLE, base + BMU_CTRL);
}
/** Disables a BMU block.
* @param[in] base BMU block base address
*/
void bmu_disable(void *base)
{
writel (CORE_DISABLE, base + BMU_CTRL);
}
/** Sets the configuration of a BMU block.
* @param[in] base BMU block base address
* @param[in] cfg BMU configuration
*/
void bmu_set_config(void *base, BMU_CFG *cfg)
{
writel (cfg->baseaddr, base + BMU_UCAST_BASE_ADDR);
writel (cfg->count & 0xffff, base + BMU_UCAST_CONFIG);
writel (cfg->size & 0xffff, base + BMU_BUF_SIZE);
// writel (BMU1_THRES_CNT, base + BMU_THRES);
/* Interrupts are never used */
// writel (0x0, base + BMU_INT_SRC);
writel (0x0, base + BMU_INT_ENABLE);
}
/**************************** GEMAC ***************************/
/** Enable Rx Checksum Engine. With this enabled, Frame with bad IP,
* TCP or UDP checksums are discarded
*
* @param[in] base GEMAC base address.
*/
void gemac_enable_rx_checksum_offload(void *base)
{
writel(readl(base + EMAC_NETWORK_CONFIG) | EMAC_ENABLE_CHKSUM_RX, base + EMAC_NETWORK_CONFIG);
writel(readl(CLASS_L4_CHKSUM_ADDR) | IPV4_CHKSUM_DROP, CLASS_L4_CHKSUM_ADDR);
}
/** Disable Rx Checksum Engine.
*
* @param[in] base GEMAC base address.
*/
void gemac_disable_rx_checksum_offload(void *base)
{
writel(readl(base + EMAC_NETWORK_CONFIG) & ~EMAC_ENABLE_CHKSUM_RX, base + EMAC_NETWORK_CONFIG);
writel(readl(CLASS_L4_CHKSUM_ADDR) & ~IPV4_CHKSUM_DROP, CLASS_L4_CHKSUM_ADDR);
}
/** Setup the MII Mgmt clock speed.
* @param[in] base GEMAC base address (GEMAC0, GEMAC1, GEMAC2)
* @param[in] mdc_div MII clock dividor
*/
void gemac_set_mdc_div(void *base, int mdc_div)
{
u32 val = readl(base + EMAC_NETWORK_CONFIG) & ~EMAC_MDC_DIV_MASK;
u32 div;
switch (mdc_div) {
case 8:
div = 0;
break;
case 16:
div = 1;
break;
case 32:
div = 2;
break;
case 48:
div = 3;
break;
default:
case 64:
div = 4;
break;
case 96:
div = 5;
break;
case 128:
div = 6;
break;
case 224:
div = 7;
break;
}
val |= div << 18;
writel(val, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC set speed.
* @param[in] base GEMAC base address
* @param[in] speed GEMAC speed (10, 100 or 1000 Mbps)
*/
void gemac_set_speed(void *base, MAC_SPEED gem_speed)
{
u32 val = readl(base + EMAC_NETWORK_CONFIG);
val = val & ~EMAC_SPEED_MASK;
switch (gem_speed)
{
case SPEED_10M:
val &= (~EMAC_PCS_ENABLE);
break;
case SPEED_100M:
val = val | EMAC_SPEED_100;
val &= (~EMAC_PCS_ENABLE);
break;
case SPEED_1000M:
val = val | EMAC_SPEED_1000;
val &= (~EMAC_PCS_ENABLE);
break;
case SPEED_1000M_PCS:
val = val | EMAC_SPEED_1000;
val |= EMAC_PCS_ENABLE;
break;
default:
val = val | EMAC_SPEED_100;
val &= (~EMAC_PCS_ENABLE);
break;
}
writel (val, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC set duplex.
* @param[in] base GEMAC base address
* @param[in] duplex GEMAC duplex mode (Full, Half)
*/
void gemac_set_duplex(void *base, int duplex)
{
u32 val = readl(base + EMAC_NETWORK_CONFIG);
if (duplex == DUPLEX_HALF)
val = (val & ~EMAC_DUPLEX_MASK) | EMAC_HALF_DUP;
else
val = (val & ~EMAC_DUPLEX_MASK) | EMAC_FULL_DUP;
writel (val, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC set mode.
* @param[in] base GEMAC base address
* @param[in] mode GEMAC operation mode (MII, RMII, RGMII, SGMII)
*/
#if defined(CONFIG_IP_ALIGNED)
#define IP_ALIGNED_BITVAL EMAC_TWO_BYTES_IP_ALIGN
#else
#define IP_ALIGNED_BITVAL 0
#endif
void gemac_set_mode(void *base, int mode)
{
switch (mode)
{
case GMII:
writel ((readl(base + EMAC_CONTROL) & ~EMAC_MODE_MASK) | EMAC_GMII_MODE_ENABLE | IP_ALIGNED_BITVAL, base + EMAC_CONTROL);
writel (readl(base + EMAC_NETWORK_CONFIG) & (~EMAC_SGMII_MODE_ENABLE), base + EMAC_NETWORK_CONFIG);
break;
case RGMII:
writel ((readl(base + EMAC_CONTROL) & ~EMAC_MODE_MASK) | EMAC_RGMII_MODE_ENABLE | IP_ALIGNED_BITVAL, base + EMAC_CONTROL);
writel (readl(base + EMAC_NETWORK_CONFIG) & (~EMAC_SGMII_MODE_ENABLE), base + EMAC_NETWORK_CONFIG);
break;
case RMII:
writel ((readl(base + EMAC_CONTROL) & ~EMAC_MODE_MASK) | EMAC_RMII_MODE_ENABLE | IP_ALIGNED_BITVAL, base + EMAC_CONTROL);
writel (readl(base + EMAC_NETWORK_CONFIG) & (~EMAC_SGMII_MODE_ENABLE), base + EMAC_NETWORK_CONFIG);
break;
case MII:
writel ((readl(base + EMAC_CONTROL) & ~EMAC_MODE_MASK) | EMAC_MII_MODE_ENABLE | IP_ALIGNED_BITVAL, base + EMAC_CONTROL);
writel (readl(base + EMAC_NETWORK_CONFIG) & (~EMAC_SGMII_MODE_ENABLE), base + EMAC_NETWORK_CONFIG);
break;
case SGMII:
writel ((readl(base + EMAC_CONTROL) & ~EMAC_MODE_MASK) | (EMAC_RMII_MODE_DISABLE | EMAC_RGMII_MODE_DISABLE) | IP_ALIGNED_BITVAL, base + EMAC_CONTROL);
writel (readl(base + EMAC_NETWORK_CONFIG) | EMAC_SGMII_MODE_ENABLE, base + EMAC_NETWORK_CONFIG);
break;
default:
writel ((readl(base + EMAC_CONTROL) & ~EMAC_MODE_MASK) | EMAC_MII_MODE_ENABLE | IP_ALIGNED_BITVAL, base + EMAC_CONTROL);
writel (readl(base + EMAC_NETWORK_CONFIG) & (~EMAC_SGMII_MODE_ENABLE), base + EMAC_NETWORK_CONFIG);
break;
}
}
/** GEMAC Enable MDIO: Activate the Management interface. This is required to program the PHY
* @param[in] base GEMAC base address
*/
void gemac_enable_mdio(void *base)
{
u32 data;
data = readl(base + EMAC_NETWORK_CONTROL);
data |= EMAC_MDIO_EN;
writel(data, base + EMAC_NETWORK_CONTROL);
}
/** GEMAC Disable MDIO: Disable the Management interface.
* @param[in] base GEMAC base address
*/
void gemac_disable_mdio(void *base)
{
u32 data;
data = readl(base + EMAC_NETWORK_CONTROL);
data &= ~EMAC_MDIO_EN;
writel(data, base + EMAC_NETWORK_CONTROL);
}
/** GEMAC reset function.
* @param[in] base GEMAC base address
*/
void gemac_reset(void *base)
{
}
/** GEMAC enable function.
* @param[in] base GEMAC base address
*/
void gemac_enable(void *base)
{
writel (readl(base + EMAC_NETWORK_CONTROL) | EMAC_TX_ENABLE | EMAC_RX_ENABLE, base + EMAC_NETWORK_CONTROL);
}
/** GEMAC disable function.
* @param[in] base GEMAC base address
*/
void gemac_disable(void *base)
{
writel (readl(base + EMAC_NETWORK_CONTROL) & ~(EMAC_TX_ENABLE | EMAC_RX_ENABLE), base + EMAC_NETWORK_CONTROL);
}
/** GEMAC set mac address configuration.
* @param[in] base GEMAC base address
* @param[in] addr MAC address to be configured
*/
void gemac_set_address(void *base, SPEC_ADDR *addr)
{
writel(addr->one.bottom, base + EMAC_SPEC1_ADD_BOT);
writel(addr->one.top, base + EMAC_SPEC1_ADD_TOP);
writel(addr->two.bottom, base + EMAC_SPEC2_ADD_BOT);
writel(addr->two.top, base + EMAC_SPEC2_ADD_TOP);
writel(addr->three.bottom, base + EMAC_SPEC3_ADD_BOT);
writel(addr->three.top, base + EMAC_SPEC3_ADD_TOP);
writel(addr->four.bottom, base + EMAC_SPEC4_ADD_BOT);
writel(addr->four.top, base + EMAC_SPEC4_ADD_TOP);
}
/** GEMAC get mac address configuration.
* @param[in] base GEMAC base address
*
* @return MAC addresses configured
*/
SPEC_ADDR gemac_get_address(void *base)
{
SPEC_ADDR addr;
addr.one.bottom = readl(base + EMAC_SPEC1_ADD_BOT);
addr.one.top = readl(base + EMAC_SPEC1_ADD_TOP);
addr.two.bottom = readl(base + EMAC_SPEC2_ADD_BOT);
addr.two.top = readl(base + EMAC_SPEC2_ADD_TOP);
addr.three.bottom = readl(base + EMAC_SPEC3_ADD_BOT);
addr.three.top = readl(base + EMAC_SPEC3_ADD_TOP);
addr.four.bottom = readl(base + EMAC_SPEC4_ADD_BOT);
addr.four.top = readl(base + EMAC_SPEC4_ADD_TOP);
return addr;
}
/** Sets the hash register of the MAC.
* This register is used for matching unicast and multicast frames.
*
* @param[in] base GEMAC base address.
* @param[in] hash 64-bit hash to be configured.
*/
void gemac_set_hash( void *base, MAC_ADDR *hash )
{
writel(hash->bottom, base + EMAC_HASH_BOT);
writel(hash->top, base + EMAC_HASH_TOP);
}
/** Get the current value hash register of the MAC.
* This register is used for matching unicast and multicast frames.
*
* @param[in] base GEMAC base address
* @returns 64-bit hash.
*/
MAC_ADDR gemac_get_hash( void *base )
{
MAC_ADDR hash;
hash.bottom = readl(base + EMAC_HASH_BOT);
hash.top = readl(base + EMAC_HASH_TOP);
return hash;
}
/** GEMAC set specific local addresses of the MAC.
* Rather than setting up all four specific addresses, this function sets them up individually.
*
* @param[in] base GEMAC base address
* @param[in] addr MAC address to be configured
*/
void gemac_set_laddr1(void *base, MAC_ADDR *address)
{
writel(address->bottom, base + EMAC_SPEC1_ADD_BOT);
writel(address->top, base + EMAC_SPEC1_ADD_TOP);
}
void gemac_set_laddr2(void *base, MAC_ADDR *address)
{
writel(address->bottom, base + EMAC_SPEC2_ADD_BOT);
writel(address->top, base + EMAC_SPEC2_ADD_TOP);
}
void gemac_set_laddr3(void *base, MAC_ADDR *address)
{
writel(address->bottom, base + EMAC_SPEC3_ADD_BOT);
writel(address->top, base + EMAC_SPEC3_ADD_TOP);
}
void gemac_set_laddr4(void *base, MAC_ADDR *address)
{
writel(address->bottom, base + EMAC_SPEC4_ADD_BOT);
writel(address->top, base + EMAC_SPEC4_ADD_TOP);
}
void gemac_set_laddrN(void *base, MAC_ADDR *address, unsigned int entry_index)
{
if( (entry_index < 1) || (entry_index > EMAC_SPEC_ADDR_MAX) )
return;
entry_index = entry_index - 1;
if (entry_index < 4)
{
writel(address->bottom, base + (entry_index * 8) + EMAC_SPEC1_ADD_BOT);
writel(address->top, base + (entry_index * 8) + EMAC_SPEC1_ADD_TOP);
}
else
{
writel(address->bottom, base + ((entry_index - 4) * 8) + EMAC_SPEC5_ADD_BOT);
writel(address->top, base + ((entry_index - 4) * 8) + EMAC_SPEC5_ADD_TOP);
}
}
/** Get specific local addresses of the MAC.
* This allows returning of a single specific address stored in the MAC.
* @param[in] base GEMAC base address
*
* @return Specific MAC address 1
*
*/
MAC_ADDR gem_get_laddr1(void *base)
{
MAC_ADDR addr;
addr.bottom = readl(base + EMAC_SPEC1_ADD_BOT);
addr.top = readl(base + EMAC_SPEC1_ADD_TOP);
return addr;
}
MAC_ADDR gem_get_laddr2(void *base)
{
MAC_ADDR addr;
addr.bottom = readl(base + EMAC_SPEC2_ADD_BOT);
addr.top = readl(base + EMAC_SPEC2_ADD_TOP);
return addr;
}
MAC_ADDR gem_get_laddr3(void *base)
{
MAC_ADDR addr;
addr.bottom = readl(base + EMAC_SPEC3_ADD_BOT);
addr.top = readl(base + EMAC_SPEC3_ADD_TOP);
return addr;
}
MAC_ADDR gem_get_laddr4(void *base)
{
MAC_ADDR addr;
addr.bottom = readl(base + EMAC_SPEC4_ADD_BOT);
addr.top = readl(base + EMAC_SPEC4_ADD_TOP);
return addr;
}
MAC_ADDR gem_get_laddrN(void *base, unsigned int entry_index)
{
MAC_ADDR addr = {0xffffffff, 0xffffffff};
if( (entry_index < 1) || (entry_index > EMAC_SPEC_ADDR_MAX) )
return addr;
entry_index = entry_index - 1;
if (entry_index < 4)
{
addr.bottom = readl(base + (entry_index * 8) + EMAC_SPEC1_ADD_BOT);
addr.top = readl(base + (entry_index * 8) + EMAC_SPEC1_ADD_TOP);
}
else
{
addr.bottom = readl(base + ((entry_index - 4) * 8) + EMAC_SPEC5_ADD_BOT);
addr.top = readl(base + ((entry_index - 4) * 8) + EMAC_SPEC5_ADD_TOP);
}
return addr;
}
/** Clear specific local addresses of the MAC.
* @param[in] base GEMAC base address
*/
void gemac_clear_laddr1(void *base)
{
writel(0, base + EMAC_SPEC1_ADD_BOT);
}
void gemac_clear_laddr2(void *base)
{
writel(0, base + EMAC_SPEC2_ADD_BOT);
}
void gemac_clear_laddr3(void *base)
{
writel(0, base + EMAC_SPEC3_ADD_BOT);
}
void gemac_clear_laddr4(void *base)
{
writel(0, base + EMAC_SPEC4_ADD_BOT);
}
void gemac_clear_laddrN(void *base, unsigned int entry_index)
{
if( (entry_index < 1) || (entry_index > EMAC_SPEC_ADDR_MAX) )
return;
entry_index = entry_index - 1;
if ( entry_index < 4 )
writel(0, base + (entry_index * 8) + EMAC_SPEC1_ADD_BOT);
else
writel(0, base + ((entry_index - 4) * 8) + EMAC_SPEC5_ADD_BOT);
}
/** Set the loopback mode of the MAC. This can be either no loopback for normal
* operation, local loopback through MAC internal loopback module or PHY
* loopback for external loopback through a PHY. This asserts the external loop
* pin.
*
* @param[in] base GEMAC base address.
* @param[in] gem_loop Loopback mode to be enabled. LB_LOCAL - MAC Loopback,
* LB_EXT - PHY Loopback.
*/
void gemac_set_loop( void *base, MAC_LOOP gem_loop )
{
switch (gem_loop) {
case LB_LOCAL:
writel(readl(base + EMAC_NETWORK_CONTROL) & (~EMAC_LB_PHY),
base + EMAC_NETWORK_CONTROL);
writel(readl(base + EMAC_NETWORK_CONTROL) | (EMAC_LB_MAC),
base + EMAC_NETWORK_CONTROL);
break;
case LB_EXT:
writel(readl(base + EMAC_NETWORK_CONTROL) & (~EMAC_LB_MAC),
base + EMAC_NETWORK_CONTROL);
writel(readl(base + EMAC_NETWORK_CONTROL) | (EMAC_LB_PHY),
base + EMAC_NETWORK_CONTROL);
break;
default:
writel(readl(base + EMAC_NETWORK_CONTROL) & (~(EMAC_LB_MAC | EMAC_LB_PHY)),
base + EMAC_NETWORK_CONTROL);
}
}
/** GEMAC allow frames
* @param[in] base GEMAC base address
*/
void gemac_enable_copy_all(void *base)
{
writel (readl(base + EMAC_NETWORK_CONFIG) | EMAC_ENABLE_COPY_ALL, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC do not allow frames
* @param[in] base GEMAC base address
*/
void gemac_disable_copy_all(void *base)
{
writel (readl(base + EMAC_NETWORK_CONFIG) & ~EMAC_ENABLE_COPY_ALL, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC allow broadcast function.
* @param[in] base GEMAC base address
*/
void gemac_allow_broadcast(void *base)
{
writel (readl(base + EMAC_NETWORK_CONFIG) & ~EMAC_NO_BROADCAST, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC no broadcast function.
* @param[in] base GEMAC base address
*/
void gemac_no_broadcast(void *base)
{
writel (readl(base + EMAC_NETWORK_CONFIG) | EMAC_NO_BROADCAST, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC enable unicast function.
* @param[in] base GEMAC base address
*/
void gemac_enable_unicast(void *base)
{
writel (readl(base + EMAC_NETWORK_CONFIG) | EMAC_ENABLE_UNICAST, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC disable unicast function.
* @param[in] base GEMAC base address
*/
void gemac_disable_unicast(void *base)
{
writel (readl(base + EMAC_NETWORK_CONFIG) & ~EMAC_ENABLE_UNICAST, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC enable multicast function.
* @param[in] base GEMAC base address
*/
void gemac_enable_multicast(void *base)
{
writel (readl(base + EMAC_NETWORK_CONFIG) | EMAC_ENABLE_MULTICAST, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC disable multicast function.
* @param[in] base GEMAC base address
*/
void gemac_disable_multicast(void *base)
{
writel (readl(base + EMAC_NETWORK_CONFIG) & ~EMAC_ENABLE_MULTICAST, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC enable fcs rx function.
* @param[in] base GEMAC base address
*/
void gemac_enable_fcs_rx(void *base)
{
writel (readl(base + EMAC_NETWORK_CONFIG) | EMAC_ENABLE_FCS_RX, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC disable fcs rx function.
* @param[in] base GEMAC base address
*/
void gemac_disable_fcs_rx(void *base)
{
writel (readl(base + EMAC_NETWORK_CONFIG) & ~EMAC_ENABLE_FCS_RX, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC enable 1536 rx function.
* @param[in] base GEMAC base address
*/
void gemac_enable_1536_rx(void *base)
{
writel (readl(base + EMAC_NETWORK_CONFIG) | EMAC_ENABLE_1536_RX, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC disable 1536 rx function.
* @param[in] base GEMAC base address
*/
void gemac_disable_1536_rx(void *base)
{
writel (readl(base + EMAC_NETWORK_CONFIG) & ~EMAC_ENABLE_1536_RX, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC enable jumbo function.
* @param[in] base GEMAC base address
*/
void gemac_enable_rx_jmb(void *base)
{
writel (readl(base + EMAC_NETWORK_CONFIG) | EMAC_ENABLE_JUMBO_FRAME, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC disable jumbo function.
* @param[in] base GEMAC base address
*/
void gemac_disable_rx_jmb(void *base)
{
writel (readl(base + EMAC_NETWORK_CONFIG) & ~EMAC_ENABLE_JUMBO_FRAME, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC enable pause rx function.
* @param[in] base GEMAC base address
*/
void gemac_enable_pause_rx(void *base)
{
writel (readl(base + EMAC_NETWORK_CONFIG) | EMAC_ENABLE_PAUSE_RX, base + EMAC_NETWORK_CONFIG);
}
/** GEMAC disable pause rx function.
* @param[in] base GEMAC base address
*/
void gemac_disable_pause_rx(void *base)
{
writel (readl(base + EMAC_NETWORK_CONFIG) & ~EMAC_ENABLE_PAUSE_RX, base + EMAC_NETWORK_CONFIG);
}
/** Sets Gemac bus width to 64bit
* @param[in] base GEMAC base address
* @param[in] width gemac bus width to be set possible values are 32/64/128
* */
void gemac_set_bus_width(void *base, int width)
{
u32 val = readl(base + EMAC_NETWORK_CONFIG);
switch(width)
{
case 32:
val = (val & ~EMAC_DATA_BUS_WIDTH_MASK) | EMAC_DATA_BUS_WIDTH_32;
case 128:
val = (val & ~EMAC_DATA_BUS_WIDTH_MASK) | EMAC_DATA_BUS_WIDTH_128;
case 64:
default:
val = (val & ~EMAC_DATA_BUS_WIDTH_MASK) | EMAC_DATA_BUS_WIDTH_64;
}
writel (val, base + EMAC_NETWORK_CONFIG);
}
/** Sets Gemac configuration.
* @param[in] base GEMAC base address
* @param[in] cfg GEMAC configuration
*/
void gemac_set_config(void *base, GEMAC_CFG *cfg)
{
gemac_set_mode(base, cfg->mode);
gemac_set_speed(base, cfg->speed);
gemac_set_duplex(base,cfg->duplex);
}
/**************************** GPI ***************************/
/** Initializes a GPI block.
* @param[in] base GPI base address
* @param[in] cfg GPI configuration
*/
void gpi_init(void *base, GPI_CFG *cfg)
{
gpi_reset(base);
gpi_disable(base);
gpi_set_config(base, cfg);
}
/** Resets a GPI block.
* @param[in] base GPI base address
*/
void gpi_reset(void *base)
{
writel (CORE_SW_RESET, base + GPI_CTRL);
}
/** Enables a GPI block.
* @param[in] base GPI base address
*/
void gpi_enable(void *base)
{
writel (CORE_ENABLE, base + GPI_CTRL);
}
/** Disables a GPI block.
* @param[in] base GPI base address
*/
void gpi_disable(void *base)
{
writel (CORE_DISABLE, base + GPI_CTRL);
}
/** Sets the configuration of a GPI block.
* @param[in] base GPI base address
* @param[in] cfg GPI configuration
*/
void gpi_set_config(void *base, GPI_CFG *cfg)
{
writel (CBUS_VIRT_TO_PFE(BMU1_BASE_ADDR + BMU_ALLOC_CTRL), base + GPI_LMEM_ALLOC_ADDR);
writel (CBUS_VIRT_TO_PFE(BMU1_BASE_ADDR + BMU_FREE_CTRL), base + GPI_LMEM_FREE_ADDR);
writel (CBUS_VIRT_TO_PFE(BMU2_BASE_ADDR + BMU_ALLOC_CTRL), base + GPI_DDR_ALLOC_ADDR);
writel (CBUS_VIRT_TO_PFE(BMU2_BASE_ADDR + BMU_FREE_CTRL), base + GPI_DDR_FREE_ADDR);
writel (CBUS_VIRT_TO_PFE(CLASS_INQ_PKTPTR), base + GPI_CLASS_ADDR);
writel (DDR_HDR_SIZE, base + GPI_DDR_DATA_OFFSET);
writel (LMEM_HDR_SIZE, base + GPI_LMEM_DATA_OFFSET);
writel (0, base + GPI_LMEM_SEC_BUF_DATA_OFFSET);
writel (0, base + GPI_DDR_SEC_BUF_DATA_OFFSET);
writel ((DDR_HDR_SIZE << 16) | LMEM_HDR_SIZE, base + GPI_HDR_SIZE);
writel ((DDR_BUF_SIZE << 16) | LMEM_BUF_SIZE, base + GPI_BUF_SIZE);
writel (((cfg->lmem_rtry_cnt << 16) | (GPI_DDR_BUF_EN << 1) | GPI_LMEM_BUF_EN), base + GPI_RX_CONFIG);
writel (cfg->tmlf_txthres, base + GPI_TMLF_TX);
writel (cfg->aseq_len, base + GPI_DTX_ASEQ);
writel (1, base + GPI_TOE_CHKSUM_EN);
}
/**************************** CLASSIFIER ***************************/
/** Initializes CLASSIFIER block.
* @param[in] cfg CLASSIFIER configuration
*/
void class_init(CLASS_CFG *cfg)
{
class_reset();
class_disable();
class_set_config(cfg);
}
/** Resets CLASSIFIER block.
*
*/
void class_reset(void)
{
writel(CORE_SW_RESET, CLASS_TX_CTRL);
}
/** Enables all CLASS-PE's cores.
*
*/
void class_enable(void)
{
writel(CORE_ENABLE, CLASS_TX_CTRL);
}
/** Disables all CLASS-PE's cores.
*
*/
void class_disable(void)
{
writel(CORE_DISABLE, CLASS_TX_CTRL);
}
/** Sets the configuration of the CLASSIFIER block.
* @param[in] cfg CLASSIFIER configuration
*/
void class_set_config(CLASS_CFG *cfg)
{
u32 val;
/* Initialize route table */
memset(DDR_PHYS_TO_VIRT(cfg->route_table_baseaddr), 0, (1 << cfg->route_table_hash_bits) * CLASS_ROUTE_SIZE);
writel(cfg->pe_sys_clk_ratio, CLASS_PE_SYS_CLK_RATIO);
writel((DDR_HDR_SIZE << 16) | LMEM_HDR_SIZE, CLASS_HDR_SIZE);
writel(LMEM_BUF_SIZE, CLASS_LMEM_BUF_SIZE);
writel(CLASS_ROUTE_ENTRY_SIZE(CLASS_ROUTE_SIZE) | CLASS_ROUTE_HASH_SIZE(cfg->route_table_hash_bits), CLASS_ROUTE_HASH_ENTRY_SIZE);
writel(HIF_PKT_CLASS_EN| HIF_PKT_OFFSET(sizeof(struct hif_hdr)), CLASS_HIF_PARSE);
val = HASH_CRC_PORT_IP | QB2BUS_LE;
#if defined(CONFIG_IP_ALIGNED)
val |= IP_ALIGNED;
#endif
if (cfg->toe_mode)
val |= CLASS_TOE;
writel(val, CLASS_ROUTE_MULTI);
writel(cfg->route_table_baseaddr, CLASS_ROUTE_TABLE_BASE);
writel(CLASS_PE0_RO_DM_ADDR0_VAL, CLASS_PE0_RO_DM_ADDR0);
writel(CLASS_PE0_RO_DM_ADDR1_VAL, CLASS_PE0_RO_DM_ADDR1);
writel(CLASS_PE0_QB_DM_ADDR0_VAL, CLASS_PE0_QB_DM_ADDR0);
writel(CLASS_PE0_QB_DM_ADDR1_VAL, CLASS_PE0_QB_DM_ADDR1);
writel(CBUS_VIRT_TO_PFE(TMU_PHY_INQ_PKTPTR), CLASS_TM_INQ_ADDR);
writel(23, CLASS_AFULL_THRES);
writel(23, CLASS_TSQ_FIFO_THRES);
writel(24, CLASS_MAX_BUF_CNT);
writel(24, CLASS_TSQ_MAX_CNT);
}
/**************************** TMU ***************************/
void tmu_reset(void)
{
writel(SW_RESET, TMU_CTRL);
}
/** Initializes TMU block.
* @param[in] cfg TMU configuration
*/
void tmu_init(TMU_CFG *cfg)
{
int q, phyno;
/* keep in soft reset */
writel(SW_RESET, TMU_CTRL);
writel(0x3, TMU_SYS_GENERIC_CONTROL);
writel(750, TMU_INQ_WATERMARK);
writel(CBUS_VIRT_TO_PFE(EGPI1_BASE_ADDR + GPI_INQ_PKTPTR), TMU_PHY0_INQ_ADDR);
writel(CBUS_VIRT_TO_PFE(EGPI2_BASE_ADDR + GPI_INQ_PKTPTR), TMU_PHY1_INQ_ADDR);
writel(CBUS_VIRT_TO_PFE(EGPI3_BASE_ADDR + GPI_INQ_PKTPTR), TMU_PHY2_INQ_ADDR);
writel(CBUS_VIRT_TO_PFE(HGPI_BASE_ADDR + GPI_INQ_PKTPTR), TMU_PHY3_INQ_ADDR);
writel(CBUS_VIRT_TO_PFE(HIF_NOCPY_RX_INQ0_PKTPTR), TMU_PHY4_INQ_ADDR);
writel(CBUS_VIRT_TO_PFE(UTIL_INQ_PKTPTR), TMU_PHY5_INQ_ADDR);
writel(CBUS_VIRT_TO_PFE(BMU2_BASE_ADDR + BMU_FREE_CTRL), TMU_BMU_INQ_ADDR);
writel(0x3FF, TMU_TDQ0_SCH_CTRL); // enabling all 10 schedulers [9:0] of each TDQ
writel(0x3FF, TMU_TDQ1_SCH_CTRL);
writel(0x3FF, TMU_TDQ2_SCH_CTRL);
writel(0x3FF, TMU_TDQ3_SCH_CTRL);
writel(cfg->pe_sys_clk_ratio, TMU_PE_SYS_CLK_RATIO);
writel(cfg->llm_base_addr, TMU_LLM_BASE_ADDR); // Extra packet pointers will be stored from this address onwards
writel(cfg->llm_queue_len, TMU_LLM_QUE_LEN);
writel(5, TMU_TDQ_IIFG_CFG);
writel(DDR_BUF_SIZE, TMU_BMU_BUF_SIZE);
writel(0x0, TMU_CTRL);
/* MEM init */
printk(KERN_INFO "%s: mem init\n", __func__);
writel(MEM_INIT, TMU_CTRL);
while(!(readl(TMU_CTRL) & MEM_INIT_DONE)) ;
/* LLM init */
printk(KERN_INFO "%s: lmem init\n", __func__);
writel(LLM_INIT, TMU_CTRL);
while(!(readl(TMU_CTRL) & LLM_INIT_DONE)) ;
// set up each queue for tail drop
for (phyno = 0; phyno < 4; phyno++)
{
for (q = 0; q < 16; q++)
{
u32 qdepth;
writel((phyno << 8) | q, TMU_TEQ_CTRL);
writel(1 << 22, TMU_TEQ_QCFG); //Enable tail drop
if (phyno == 3)
qdepth = DEFAULT_TMU3_QDEPTH;
else
qdepth = (q == 0) ? DEFAULT_Q0_QDEPTH : DEFAULT_MAX_QDEPTH;
// LOG: 68855
// The following is a workaround for the reordered packet and BMU2 buffer leakage issue.
if (CHIP_REVISION() == 0)
qdepth = 31;
writel(qdepth << 18, TMU_TEQ_HW_PROB_CFG2);
writel(qdepth >> 14, TMU_TEQ_HW_PROB_CFG3);
}
}
/* Set TMU-3 queue 5 (LRO) in no-drop mode */
writel((3 << 8) | 5, TMU_TEQ_CTRL);
writel(0, TMU_TEQ_QCFG);
writel(0x05, TMU_TEQ_DISABLE_DROPCHK);
writel(0x0, TMU_CTRL);
}
/** Enables TMU-PE cores.
* @param[in] pe_mask TMU PE mask
*/
void tmu_enable(u32 pe_mask)
{
writel(readl(TMU_TX_CTRL) | (pe_mask & 0xF), TMU_TX_CTRL);
}
/** Disables TMU cores.
* @param[in] pe_mask TMU PE mask
*/
void tmu_disable(u32 pe_mask)
{
writel(readl(TMU_TX_CTRL) & ~(pe_mask & 0xF), TMU_TX_CTRL);
}
/** This will return the tmu queue status
* @param[in] if_id gem interface id or TMU index
* @return returns the bit mask of busy queues, zero means all queues are empty
*/
u32 tmu_qstatus(u32 if_id)
{
return cpu_to_be32(pe_dmem_read(TMU0_ID+if_id, PESTATUS_ADDR_TMU + offsetof(PE_STATUS, tmu_qstatus), 4));
}
u32 tmu_pkts_processed(u32 if_id)
{
return cpu_to_be32(pe_dmem_read(TMU0_ID+if_id, PESTATUS_ADDR_TMU + offsetof(PE_STATUS, rx), 4));
}
/**************************** UTIL ***************************/
/** Resets UTIL block.
*/
void util_reset(void)
{
writel(CORE_SW_RESET, UTIL_TX_CTRL);
}
/** Initializes UTIL block.
* @param[in] cfg UTIL configuration
*/
void util_init(UTIL_CFG *cfg)
{
writel(cfg->pe_sys_clk_ratio, UTIL_PE_SYS_CLK_RATIO);
}
/** Enables UTIL-PE core.
*
*/
void util_enable(void)
{
writel(CORE_ENABLE, UTIL_TX_CTRL);
}
/** Disables UTIL-PE core.
*
*/
void util_disable(void)
{
writel(CORE_DISABLE, UTIL_TX_CTRL);
}
/**************************** HIF ***************************/
/** Initializes HIF no copy block.
*
*/
void hif_nocpy_init(void)
{
writel(4, HIF_NOCPY_TX_PORT_NO);
writel(CBUS_VIRT_TO_PFE(BMU1_BASE_ADDR + BMU_ALLOC_CTRL), HIF_NOCPY_LMEM_ALLOC_ADDR);
writel(CBUS_VIRT_TO_PFE(CLASS_INQ_PKTPTR), HIF_NOCPY_CLASS_ADDR);
writel(CBUS_VIRT_TO_PFE(TMU_PHY_INQ_PKTPTR), HIF_NOCPY_TMU_PORT0_ADDR);
writel(HIF_RX_POLL_CTRL_CYCLE<<16|HIF_TX_POLL_CTRL_CYCLE, HIF_NOCPY_POLL_CTRL);
}
/** Enable hif_nocpy tx DMA and interrupt
*
*/
void hif_nocpy_tx_enable(void)
{
/*TODO not sure poll_cntrl_en is required or not */
writel( HIF_CTRL_DMA_EN, HIF_NOCPY_TX_CTRL);
//writel((readl(HIF_NOCPY_INT_ENABLE) | HIF_INT_EN | HIF_TXPKT_INT_EN), HIF_NOCPY_INT_ENABLE);
}
/** Disable hif_nocpy tx DMA and interrupt
*
*/
void hif_nocpy_tx_disable(void)
{
u32 hif_int;
writel(0, HIF_NOCPY_TX_CTRL);
hif_int = readl(HIF_NOCPY_INT_ENABLE);
hif_int &= HIF_TXPKT_INT_EN;
writel(hif_int, HIF_NOCPY_INT_ENABLE);
}
/** Enable hif rx DMA and interrupt
*
*/
void hif_nocpy_rx_enable(void)
{
hif_nocpy_rx_dma_start();
writel((readl(HIF_NOCPY_INT_ENABLE) | HIF_INT_EN | HIF_RXPKT_INT_EN), HIF_NOCPY_INT_ENABLE);
}
/** Disable hif_nocpy rx DMA and interrupt
*
*/
void hif_nocpy_rx_disable(void)
{
u32 hif_int;
writel(0, HIF_NOCPY_RX_CTRL);
hif_int = readl(HIF_NOCPY_INT_ENABLE);
hif_int &= HIF_RXPKT_INT_EN;
writel(hif_int, HIF_NOCPY_INT_ENABLE);
}
/** Initializes HIF copy block.
*
*/
void hif_init(void)
{
/*Initialize HIF registers*/
writel((HIF_RX_POLL_CTRL_CYCLE << 16) | HIF_TX_POLL_CTRL_CYCLE, HIF_POLL_CTRL);
}
/** Enable hif tx DMA and interrupt
*
*/
void hif_tx_enable(void)
{
/*TODO not sure poll_cntrl_en is required or not */
writel(HIF_CTRL_DMA_EN, HIF_TX_CTRL);
writel((readl(HIF_INT_ENABLE) | HIF_INT_EN | HIF_TXPKT_INT_EN), HIF_INT_ENABLE);
}
/** Disable hif tx DMA and interrupt
*
*/
void hif_tx_disable(void)
{
u32 hif_int;
writel(0, HIF_TX_CTRL);
hif_int = readl(HIF_INT_ENABLE);
hif_int &= HIF_TXPKT_INT_EN;
writel(hif_int, HIF_INT_ENABLE);
}
/** Enable hif rx DMA and interrupt
*
*/
void hif_rx_enable(void)
{
hif_rx_dma_start();
writel((readl(HIF_INT_ENABLE) | HIF_INT_EN | HIF_RXPKT_INT_EN), HIF_INT_ENABLE);
}
/** Disable hif rx DMA and interrupt
*
*/
void hif_rx_disable(void)
{
u32 hif_int;
writel(0, HIF_RX_CTRL);
hif_int = readl(HIF_INT_ENABLE);
hif_int &= HIF_RXPKT_INT_EN;
writel(hif_int, HIF_INT_ENABLE);
}