// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2015-2016 Freescale Semiconductor, Inc. * Copyright 2017 NXP */ #include "pfe_mod.h" #include "pfe/pfe.h" /* A-010897: Jumbo frame is not supported */ extern bool pfe_errata_a010897; #define PFE_RCR_MAX_FL_MASK 0xC000FFFF 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; 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; 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; 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[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; #if !defined(CONFIG_FSL_PPFE_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); } len = (len & 0x3); if (len) { 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 endianness, 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 endianness, 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 endianness, 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++; } } #if !defined(CONFIG_FSL_PPFE_UTIL_DISABLED) /* 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 endianness, 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 endianness, 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); } #endif /* 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, struct 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_FSL_PPFE_UTIL_DISABLED) if (id == UTIL_ID) { pr_err("%s: unsupported pmem section for UTIL\n", __func__); return -EINVAL; } #endif if (((unsigned long)(data + offset) & 0x3) != (addr & 0x3)) { pr_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) { pr_err("%s: load address(%x) is not 16bit aligned\n", __func__, addr); return -EINVAL; } if (size & 0x1) { pr_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: pr_err("%s: unsupported section type(%x)\n", __func__, type); return -EINVAL; } 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, struct 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)) { pr_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) { pr_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: pr_err("%s: unsupported section type(%x)\n", __func__, type); return -EINVAL; } 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, struct elf32_shdr *shdr, struct device *dev) { 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) { pr_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)) { pr_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) { pr_err( "%s: load address(%x) is not 16bit aligned\n" , __func__, addr); return -EINVAL; } if (size & 0x1) { pr_err( "%s: load length(%x) is not 16bit aligned\n" , __func__, size); return -EINVAL; } memcpy(DDR_PHYS_TO_VIRT( DDR_PFE_TO_PHYS(addr)), data + offset, size); } #if !defined(CONFIG_FSL_PPFE_UTIL_DISABLED) } else if (id == UTIL_ID) { if (((unsigned long)(data + offset) & 0x3) != (addr & 0x3)) { pr_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) { pr_err( "%s: load address(%x) is not 16bit aligned\n" , __func__, addr); return -EINVAL; } if (size & 0x1) { pr_err( "%s: load length(%x) is not 16bit aligned\n" , __func__, size); return -EINVAL; } util_pmem_memcpy(DDR_PHYS_TO_VIRT( DDR_PFE_TO_PHYS(addr)), data + offset, size); } #endif } else { pr_err( "%s: unsupported ddr section type(%x) for PE(%d)\n" , __func__, type, id); return -EINVAL; } } else { memcpy(DDR_PHYS_TO_VIRT(DDR_PFE_TO_PHYS(addr)), data + offset, size); } break; case SHT_NOBITS: memset(DDR_PHYS_TO_VIRT(DDR_PFE_TO_PHYS(addr)), 0, size); break; default: pr_err("%s: unsupported section type(%x)\n", __func__, type); return -EINVAL; } 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, struct 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) { pr_err( "%s: unsupported pe-lmem section type(%x) for PE(%d)\n", __func__, type, id); return -EINVAL; } if (((unsigned long)(data + offset) & 0x3) != (addr & 0x3)) { pr_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) { pr_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: pr_err("%s: unsupported section type(%x)\n", __func__, type); return -EINVAL; } 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, struct elf32_shdr *shdr, struct device *dev) { 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; else if (IS_PHYS_DDR(addr, size)) return pe_load_ddr_section(id, data, shdr, dev); else if (IS_PE_LMEM(addr, size)) return pe_load_pe_lmem_section(id, data, shdr); pr_err("%s: unsupported memory range(%x)\n", __func__, addr); return 0; } /**************************** BMU ***************************/ /* Initializes a BMU block. * @param[in] base BMU block base address * @param[in] cfg BMU configuration */ void bmu_init(void *base, struct 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, struct 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); /* Interrupts are never used */ writel(cfg->low_watermark, base + BMU_LOW_WATERMARK); writel(cfg->high_watermark, base + BMU_HIGH_WATERMARK); writel(0x0, base + BMU_INT_ENABLE); } /**************************** MTIP 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) { /*Do not find configuration to do this */ } /* Disable Rx Checksum Engine. * * @param[in] base GEMAC base address. */ void gemac_disable_rx_checksum_offload(void *base) { /*Do not find configuration to do this */ } /* 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, enum mac_speed gem_speed) { u32 ecr = readl(base + EMAC_ECNTRL_REG) & ~EMAC_ECNTRL_SPEED; u32 rcr = readl(base + EMAC_RCNTRL_REG) & ~EMAC_RCNTRL_RMII_10T; switch (gem_speed) { case SPEED_10M: rcr |= EMAC_RCNTRL_RMII_10T; break; case SPEED_1000M: ecr |= EMAC_ECNTRL_SPEED; break; case SPEED_100M: default: /*It is in 100M mode */ break; } writel(ecr, (base + EMAC_ECNTRL_REG)); writel(rcr, (base + EMAC_RCNTRL_REG)); } /* 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) { if (duplex == DUPLEX_HALF) { writel(readl(base + EMAC_TCNTRL_REG) & ~EMAC_TCNTRL_FDEN, base + EMAC_TCNTRL_REG); writel(readl(base + EMAC_RCNTRL_REG) | EMAC_RCNTRL_DRT, (base + EMAC_RCNTRL_REG)); } else{ writel(readl(base + EMAC_TCNTRL_REG) | EMAC_TCNTRL_FDEN, base + EMAC_TCNTRL_REG); writel(readl(base + EMAC_RCNTRL_REG) & ~EMAC_RCNTRL_DRT, (base + EMAC_RCNTRL_REG)); } } /* GEMAC set mode. * @param[in] base GEMAC base address * @param[in] mode GEMAC operation mode (MII, RMII, RGMII, SGMII) */ void gemac_set_mode(void *base, int mode) { u32 val = readl(base + EMAC_RCNTRL_REG); /*Remove loopbank*/ val &= ~EMAC_RCNTRL_LOOP; /* Enable flow control and MII mode.PFE firmware always expects CRC should be forwarded by MAC to validate CRC in software.*/ val |= (EMAC_RCNTRL_FCE | EMAC_RCNTRL_MII_MODE); writel(val, base + EMAC_RCNTRL_REG); } /* GEMAC enable function. * @param[in] base GEMAC base address */ void gemac_enable(void *base) { writel(readl(base + EMAC_ECNTRL_REG) | EMAC_ECNTRL_ETHER_EN, base + EMAC_ECNTRL_REG); } /* GEMAC disable function. * @param[in] base GEMAC base address */ void gemac_disable(void *base) { writel(readl(base + EMAC_ECNTRL_REG) & ~EMAC_ECNTRL_ETHER_EN, base + EMAC_ECNTRL_REG); } /* GEMAC TX disable function. * @param[in] base GEMAC base address */ void gemac_tx_disable(void *base) { writel(readl(base + EMAC_TCNTRL_REG) | EMAC_TCNTRL_GTS, base + EMAC_TCNTRL_REG); } void gemac_tx_enable(void *base) { writel(readl(base + EMAC_TCNTRL_REG) & ~EMAC_TCNTRL_GTS, base + EMAC_TCNTRL_REG); } /* 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, struct pfe_mac_addr *hash) { writel(hash->bottom, base + EMAC_GALR); writel(hash->top, base + EMAC_GAUR); } void gemac_set_laddrN(void *base, struct pfe_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 < 1) { writel(htonl(address->bottom), base + EMAC_PHY_ADDR_LOW); writel((htonl(address->top) | 0x8808), base + EMAC_PHY_ADDR_HIGH); } else { writel(htonl(address->bottom), base + ((entry_index - 1) * 8) + EMAC_SMAC_0_0); writel((htonl(address->top) | 0x8808), base + ((entry_index - 1) * 8) + EMAC_SMAC_0_1); } } 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 < 1) { writel(0, base + EMAC_PHY_ADDR_LOW); writel(0, base + EMAC_PHY_ADDR_HIGH); } else { writel(0, base + ((entry_index - 1) * 8) + EMAC_SMAC_0_0); writel(0, base + ((entry_index - 1) * 8) + EMAC_SMAC_0_1); } } /* 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, enum mac_loop gem_loop) { pr_info("%s()\n", __func__); writel(readl(base + EMAC_RCNTRL_REG) | EMAC_RCNTRL_LOOP, (base + EMAC_RCNTRL_REG)); } /* GEMAC allow frames * @param[in] base GEMAC base address */ void gemac_enable_copy_all(void *base) { writel(readl(base + EMAC_RCNTRL_REG) | EMAC_RCNTRL_PROM, (base + EMAC_RCNTRL_REG)); } /* GEMAC do not allow frames * @param[in] base GEMAC base address */ void gemac_disable_copy_all(void *base) { writel(readl(base + EMAC_RCNTRL_REG) & ~EMAC_RCNTRL_PROM, (base + EMAC_RCNTRL_REG)); } /* GEMAC allow broadcast function. * @param[in] base GEMAC base address */ void gemac_allow_broadcast(void *base) { writel(readl(base + EMAC_RCNTRL_REG) & ~EMAC_RCNTRL_BC_REJ, base + EMAC_RCNTRL_REG); } /* GEMAC no broadcast function. * @param[in] base GEMAC base address */ void gemac_no_broadcast(void *base) { writel(readl(base + EMAC_RCNTRL_REG) | EMAC_RCNTRL_BC_REJ, base + EMAC_RCNTRL_REG); } /* GEMAC enable 1536 rx function. * @param[in] base GEMAC base address */ void gemac_enable_1536_rx(void *base) { /* Set 1536 as Maximum frame length */ writel((readl(base + EMAC_RCNTRL_REG) & PFE_RCR_MAX_FL_MASK) | (1536 << 16), base + EMAC_RCNTRL_REG); } /* GEMAC set rx Max frame length. * @param[in] base GEMAC base address * @param[in] mtu new mtu */ void gemac_set_rx_max_fl(void *base, int mtu) { /* Set mtu as Maximum frame length */ writel((readl(base + EMAC_RCNTRL_REG) & PFE_RCR_MAX_FL_MASK) | (mtu << 16), base + EMAC_RCNTRL_REG); } /* GEMAC enable stacked vlan function. * @param[in] base GEMAC base address */ void gemac_enable_stacked_vlan(void *base) { /* MTIP doesn't support stacked vlan */ } /* GEMAC enable pause rx function. * @param[in] base GEMAC base address */ void gemac_enable_pause_rx(void *base) { writel(readl(base + EMAC_RCNTRL_REG) | EMAC_RCNTRL_FCE, base + EMAC_RCNTRL_REG); } /* GEMAC disable pause rx function. * @param[in] base GEMAC base address */ void gemac_disable_pause_rx(void *base) { writel(readl(base + EMAC_RCNTRL_REG) & ~EMAC_RCNTRL_FCE, base + EMAC_RCNTRL_REG); } /* GEMAC enable pause tx function. * @param[in] base GEMAC base address */ void gemac_enable_pause_tx(void *base) { writel(EMAC_RX_SECTION_EMPTY_V, base + EMAC_RX_SECTION_EMPTY); } /* GEMAC disable pause tx function. * @param[in] base GEMAC base address */ void gemac_disable_pause_tx(void *base) { writel(0x0, base + EMAC_RX_SECTION_EMPTY); } /* GEMAC wol configuration * @param[in] base GEMAC base address * @param[in] wol_conf WoL register configuration */ void gemac_set_wol(void *base, u32 wol_conf) { u32 val = readl(base + EMAC_ECNTRL_REG); if (wol_conf) val |= (EMAC_ECNTRL_MAGIC_ENA | EMAC_ECNTRL_SLEEP); else val &= ~(EMAC_ECNTRL_MAGIC_ENA | EMAC_ECNTRL_SLEEP); writel(val, base + EMAC_ECNTRL_REG); } /* 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) { } /* Sets Gemac configuration. * @param[in] base GEMAC base address * @param[in] cfg GEMAC configuration */ void gemac_set_config(void *base, struct gemac_cfg *cfg) { /*GEMAC config taken from VLSI */ writel(0x00000004, base + EMAC_TFWR_STR_FWD); writel(0x00000005, base + EMAC_RX_SECTION_FULL); if (pfe_errata_a010897) writel(0x0000076c, base + EMAC_TRUNC_FL); else writel(0x00003fff, base + EMAC_TRUNC_FL); writel(0x00000030, base + EMAC_TX_SECTION_EMPTY); writel(0x00000000, base + EMAC_MIB_CTRL_STS_REG); 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, struct 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, struct 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); if (cfg->mtip_pause_reg) { writel(cfg->mtip_pause_reg, base + GPI_CSR_MTIP_PAUSE_REG); writel(EGPI_PAUSE_TIME, base + GPI_TX_PAUSE_TIME); } } /**************************** CLASSIFIER ***************************/ /* Initializes CLASSIFIER block. * @param[in] cfg CLASSIFIER configuration */ void class_init(struct 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(struct class_cfg *cfg) { u32 val; /* Initialize route table */ if (!cfg->resume) memset(DDR_PHYS_TO_VIRT(cfg->route_table_baseaddr), 0, (1 << cfg->route_table_hash_bits) * CLASS_ROUTE_SIZE); #if !defined(LS1012A_PFE_RESET_WA) writel(cfg->pe_sys_clk_ratio, CLASS_PE_SYS_CLK_RATIO); #endif 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 /* * Class PE packet steering will only work if TOE mode, bridge fetch or * route fetch are enabled (see class/qb_fet.v). Route fetch would * trigger additional memory copies (likely from DDR because of hash * table size, which cannot be reduced because PE software still * relies on hash value computed in HW), so when not in TOE mode we * simply enable HW bridge fetch even though we don't use it. */ if (cfg->toe_mode) val |= CLASS_TOE; else val |= HW_BRIDGE_FETCH; writel(val, CLASS_ROUTE_MULTI); writel(DDR_PHYS_TO_PFE(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(struct tmu_cfg *cfg) { int q, phyno; tmu_disable(0xF); mdelay(10); #if !defined(LS1012A_PFE_RESET_WA) /* keep in soft reset */ writel(SW_RESET, TMU_CTRL); #endif 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(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_TDQ3_SCH_CTRL); #if !defined(LS1012A_PFE_RESET_WA) writel(cfg->pe_sys_clk_ratio, TMU_PE_SYS_CLK_RATIO); #endif #if !defined(LS1012A_PFE_RESET_WA) writel(DDR_PHYS_TO_PFE(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 */ pr_info("%s: mem init\n", __func__); writel(MEM_INIT, TMU_CTRL); while (!(readl(TMU_CTRL) & MEM_INIT_DONE)) ; /* LLM init */ pr_info("%s: lmem init\n", __func__); writel(LLM_INIT, TMU_CTRL); while (!(readl(TMU_CTRL) & LLM_INIT_DONE)) ; #endif /* set up each queue for tail drop */ for (phyno = 0; phyno < 4; phyno++) { if (phyno == 2) continue; 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); } } #ifdef CFG_LRO /* Set TMU-3 queue 5 (LRO) in no-drop mode */ writel((3 << 8) | TMU_QUEUE_LRO, TMU_TEQ_CTRL); writel(0, TMU_TEQ_QCFG); #endif 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, TMU_DM_PESTATUS + offsetof(struct pe_status, tmu_qstatus), 4)); } u32 tmu_pkts_processed(u32 if_id) { return cpu_to_be32(pe_dmem_read(TMU0_ID + if_id, TMU_DM_PESTATUS + offsetof(struct 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(struct 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 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) { 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); }