/*---------------------------------------------------------------------- * File Name: netip_mem_util.c *---------------------------------------------------------------------- Copyright (C) 2020-2021 MaxLinear, Inc. Copyright (C) 2005-2020 Intel Corporation This software is licensed under (a) a 3-clause BSD license; or alternatively (b) the GPL v2 license -- A. BSD-3-Clause ---------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of Intel Corporation, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- B. GPL-2.0 ---------------------------- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2, as published by the Free Software Foundation. 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, see . ------------------------------ SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */ #include #include #include #include #include #include #include #include #include #include "netip_subsystem_config.h" /*************************************************************************/ /* Define */ /*************************************************************************/ #define CACHE_LINE_SIZE 64 /* following address are for debug use will be removed in final solution */ /* After final solution following address range will go in exact map for descriptors in shared memory */ /* MMIO information for Atom only configuration */ #define NPCPU_DESCRIPTOR_MEM_BASE (0xFC00000) #define NPCPU_DESCRIPTOR_MEM_SIZE (0xBF00000 - 0x100000) //physical base address for npcpu descriptor static phys_addr_t npcpu_descriptor_physical_mem_base; //physical end address for npcpu descriptor static phys_addr_t npcpu_descriptor_physical_mem_end; //virtual base address for npcpu descriptor static void *npcpu_descriptor_virtual_mem_base; //virtual end address for npcpu descriptor static void *npcpu_descriptor_virtual_mem_end; #define NPCPU_DESCRIPTOR_MAX_PHYSICAL_MEM_SIZE (0x10000000) //Structure described memory region. struct netip_mem_rgion_device { netss_dev_t subdevice; //subdevice id. Used by netss_device_get_info() function size_t virt_base; //virtual base address size_t virt_end; //virtual end address volatile bool ioremapped; //flag which identify whether the region was initialized or not size_t phys_base; //physical base address size_t phys_end; //physical end address char* name; //region name. Used for debug only }; //memory regions array static struct netip_mem_rgion_device mem_util_dev[] = { {NETSS_DEV_PACKET_PROCESSOR1, 0, 0, false, 0xF3000000, 0, "NETSS_DEV_PACKET_PROCESSOR1"}, {NETSS_DEV_PACKET_PROCESSOR2, 0, 0, false, 0xF9000000, 0, "NETSS_DEV_PACKET_PROCESSOR2"}, {NETSS_DEV_GBE, 0, 0, false, 0xF0318000, 0, "NETSS_DEV_GBE_GENERAL"}, {NETSS_DEV_GBE5, 0, 0, false, 0xF0314000, 0, "NETSS_DEV_GBE5"}, {NETSS_DEV_GBE4, 0, 0, false, 0xF0310000, 0, "NETSS_DEV_GBE4"}, {NETSS_DEV_GBE3, 0, 0, false, 0xF030C000, 0, "NETSS_DEV_GBE3"}, {NETSS_DEV_GBE2, 0, 0, false, 0xF0308000, 0, "NETSS_DEV_GBE2"}, {NETSS_DEV_GBE1, 0, 0, false, 0xF0304000, 0, "NETSS_DEV_GBE1"}, {NETSS_DEV_GBE0, 0, 0, false, 0xF0300000, 0, "NETSS_DEV_GBE0"}, {NETSS_DEV_CLK, 0, 0, false, 0xF00D0000, 0, "NETSS_DEV_CLK"}, {NETSS_DEV_BOOTCFG, 0, 0, false, 0xF00C0000, 0, "NETSS_DEV_BOOTCFG"}, {NETSS_DEV_TDM, 0, 0, false, 0xF0100000, 0, "NETSS_DEV_TDM"}, {NETSS_DEV_TIMER0, 0, 0, false, 0xF0030000, 0, "NETSS_DEV_TIMER0"}, {NETSS_DEV_TIMER1, 0, 0, false, 0xF0110000, 0, "NETSS_DEV_TIMER1"}, {NETSS_DEV_TDM1, 0, 0, false, 0xF0120000, 0, "NETSS_DEV_TDM1"}, {NETSS_DEV_TIMER2, 0, 0, false, 0xF0130000, 0, "NETSS_DEV_TIMER2"}, {NETSS_DEV_TDM2, 0, 0, false, 0xF0180000, 0, "NETSS_DEV_TDM2"}, {NETSS_DEV_VCODEC, 0, 0, false, 0xF00F6000, 0, "NETSS_DEV_VCODEC"}, {NETSS_DEV_BOOT_RAM, 0, 0, false, 0xFFFF0000, 0, "NETSS_DEV_BOOT_RAM"}, }; /**************** Local functions Declarations*****************/ /*************************************************************/ /**************************************************************************/ /*! \fn netip_memmap_init ************************************************************************** * * \brief memory init function. Inits memory regions and npcpu descriptor * * \param[in] void * \return 0 in case of success. Negative in case of error **************************************************************************/ int netip_memmap_init(void) { int i, ret = 0, hw_mbox_ret = 0; netss_dev_info_t mmio_dev_info; u64 npcpu_rpc_phys_addr = 0; u64 npcpu_rpc_mem_size = 0; phys_addr_t max_phys_addr = 0; Cppi41HwMboxOffChipMemInfoReplyMsg_t off_chip_reply; size_t dataLen = sizeof(Cppi41HwMboxOffChipMemInfoReplyMsg_t); if(!netss_driver_ready()) { pr_err("netss driver is not ready!\n"); return -ENODEV; } for (i = 0; i < ARRAY_SIZE(mem_util_dev); i++) { if (mem_util_dev[i].ioremapped) { /* Already mapped */ continue; } if (netss_device_get_info(mem_util_dev[i].subdevice, &mmio_dev_info)) { pr_err("Get device info failed for region %s\n", mem_util_dev[i].name); ret = -EINVAL; continue; } mem_util_dev[i].phys_end = mem_util_dev[i].phys_base + mmio_dev_info.size; mem_util_dev[i].virt_base = (size_t)ioremap(mmio_dev_info.base, mmio_dev_info.size); mem_util_dev[i].virt_end = mem_util_dev[i].virt_base + mmio_dev_info.size; if(mem_util_dev[i].virt_base) { mem_util_dev[i].ioremapped = true; } else { pr_err("ioremap failed for region %s\n", mem_util_dev[i].name); ret = -ENOMEM; } } if (npcpu_descriptor_virtual_mem_base != NULL) { pr_info("netip_memmap_init: npcpu_descriptor_virtual_mem_base already mapped\n"); return 0; } #ifdef CONFIG_NPCPU_HARDCODED_OFFCHIP_INFO npcpu_descriptor_physical_mem_base = NPCPU_DESCRIPTOR_MEM_BASE; npcpu_descriptor_physical_mem_end = npcpu_descriptor_physical_mem_base + NPCPU_DESCRIPTOR_MEM_SIZE; #else pr_info("netip_memmap_init: Request offChip information from NPCPU\n"); /* Request offChip information from NPCPU */ off_chip_reply.cmd = cpu_to_be32(CPPI41_HWMBOX_CMD_GET_OFFCHIP_MEM_INFO_REPLY); off_chip_reply.length = 0; off_chip_reply.off_chip_phy_addr = 0; if(hwMbox_isReady()) { pr_err("HW mailbox isn't ready yet."); return -ENODEV; } hw_mbox_ret = hwMbox_sendOpcode(HW_MBOX_MASTER_NP_CPU, NPCPU_APPCPU_HW_MBOX_TAG_CPPI41_MBX, (uint8_t *)&off_chip_reply, dataLen, dataLen, &dataLen); if(hw_mbox_ret) { pr_err("HW mailbox hwMbox_sendOpcode failed (retCode =%d).", hw_mbox_ret); return -ECOMM; } npcpu_rpc_phys_addr = be32_to_cpu(off_chip_reply.off_chip_phy_addr); npcpu_rpc_mem_size = be32_to_cpu(off_chip_reply.length); max_phys_addr = sizeof(phys_addr_t) > 4 ? U64_MAX : U32_MAX; if (!npcpu_rpc_phys_addr || !npcpu_rpc_mem_size || (npcpu_rpc_mem_size > NPCPU_DESCRIPTOR_MAX_PHYSICAL_MEM_SIZE) || (npcpu_rpc_phys_addr > (max_phys_addr - npcpu_rpc_mem_size))) { pr_err("Offchip info is out of bounds."); return -EINVAL; } /* We can now absorb these addresses in their correct types */ npcpu_descriptor_physical_mem_base = npcpu_rpc_phys_addr; npcpu_descriptor_physical_mem_end = npcpu_descriptor_physical_mem_base + npcpu_rpc_mem_size; pr_info("netip_memmap_init: received offChip base addr [%p] end addr [%p] \n", (void*)npcpu_descriptor_physical_mem_base, (void*)npcpu_descriptor_physical_mem_end); #endif npcpu_descriptor_virtual_mem_base = ioremap( npcpu_descriptor_physical_mem_base, npcpu_rpc_mem_size); npcpu_descriptor_virtual_mem_end = npcpu_descriptor_virtual_mem_base + npcpu_rpc_mem_size; if(!npcpu_descriptor_virtual_mem_base) { pr_err("NPCPU_VIRTUAL_MEM_BASE IOREMAP error \n"); return -ENOMEM; } pr_info("netip_memmap_init: npcpu_descr virt_base [%p] virt_end [%p] \n", (void*)npcpu_descriptor_virtual_mem_base, (void*)npcpu_descriptor_virtual_mem_end); #ifdef CONFIG_NET_SUBSYSTEM_SNOOPED_MODE netss_enable_snooped_mode(); #endif return ret; } EXPORT_SYMBOL(netip_memmap_init); /**************************************************************************/ /*! \fn netip_memmap_cleanup ************************************************************************** * * \brief memory cleanup function * * \param[in] void * \return void **************************************************************************/ void netip_memmap_cleanup(void) { int i; pr_info("cleaningup mapped descriptor memory \n"); if(npcpu_descriptor_virtual_mem_base) { iounmap(npcpu_descriptor_virtual_mem_base); npcpu_descriptor_virtual_mem_base = NULL; } for (i=0; i < ARRAY_SIZE(mem_util_dev); i++) { if(mem_util_dev[i].ioremapped) { iounmap((void*)mem_util_dev[i].virt_base); mem_util_dev[i].ioremapped = false; mem_util_dev[i].virt_base = 0; } } } EXPORT_SYMBOL(netip_memmap_cleanup); /**************************************************************************/ /*! \fn netip_mmio_to_virtual ************************************************************************** * * \brief Converts physical addresses to virtual. Looks for correct region and * returns address from virtual region space with the same offset. * If lookup failed, returns standard linux function phys_to_virt() * * \param[in] unsigned long netip_phys_addr - physical address * \return void* - virtual address **************************************************************************/ void *netip_mmio_to_virtual(unsigned long netip_phys_addr) { unsigned long offset = 0; int i; if( (netip_phys_addr >= npcpu_descriptor_physical_mem_base) && (netip_phys_addr < npcpu_descriptor_physical_mem_end)) { offset = (netip_phys_addr - npcpu_descriptor_physical_mem_base); pr_debug("netip_phys_addr=%lx virt_addr=%p offset=%lx\n", netip_phys_addr, npcpu_descriptor_virtual_mem_base + offset, offset); return (void *)(npcpu_descriptor_virtual_mem_base + offset); } for (i=0; i < ARRAY_SIZE(mem_util_dev); i++) { if ( unlikely(!mem_util_dev[i].ioremapped) ) continue; if ((size_t)netip_phys_addr > mem_util_dev[i].phys_end) continue; if ((size_t)netip_phys_addr < mem_util_dev[i].phys_base) continue; offset = (size_t)netip_phys_addr - mem_util_dev[i].phys_base; pr_debug("Physical to virtual called netip_phys_addr=%p virt_addr=%p offset=%p\n", (void*)netip_phys_addr, (void*)(mem_util_dev[i].virt_base + offset), (void*)offset); return (void *)(mem_util_dev[i].virt_base + offset); } pr_debug("%s:%d: No region found for 0x%lx, return as Kernel map instead\n", __func__, __LINE__, netip_phys_addr); return (void *)phys_to_virt(netip_phys_addr); } EXPORT_SYMBOL(netip_mmio_to_virtual); /**************************************************************************/ /*! \fn netip_mmio_to_physical ************************************************************************** * * \brief Converts virtual addresses to physical. Looks for correct region and * returns address from physical region space with the same offset. * If lookup failed, returns standard linux function virt_to_phys() * * \param[in] void* virt_addr - virtual address * \return void* - physical address **************************************************************************/ void *netip_mmio_to_physical(void* virt_addr) { unsigned long offset = 0; int i; if( (virt_addr >= npcpu_descriptor_virtual_mem_base) && (virt_addr < npcpu_descriptor_virtual_mem_end)) { offset = virt_addr - npcpu_descriptor_virtual_mem_base; pr_debug("virt_addr=%p phys_addr=0x%lx offset=%lx\n", virt_addr, npcpu_descriptor_physical_mem_base + offset, offset); return (void*)(npcpu_descriptor_physical_mem_base + offset); } for (i=0; i < ARRAY_SIZE(mem_util_dev); i++) { if ( unlikely(!mem_util_dev[i].ioremapped) ) continue; if ((size_t)virt_addr > mem_util_dev[i].virt_end) continue; if ((size_t)virt_addr < mem_util_dev[i].virt_base) continue; offset = (size_t)virt_addr - mem_util_dev[i].virt_base; pr_debug("Virtual to physical called virtual address=%p phy_addr=%lu offset=%lx\n", virt_addr, (mem_util_dev[i].phys_base + offset) , offset); return (void*)(mem_util_dev[i].phys_base + offset); } pr_debug("%s:%d: No region found for 0x%p, return as Kernel map instead\n", __func__, __LINE__, virt_addr); return (void*)virt_to_phys(virt_addr); } EXPORT_SYMBOL(netip_mmio_to_physical); void cache_flush_buffer(void *bufptr, int size) { clflush_cache_range(bufptr, size); } EXPORT_SYMBOL(cache_flush_buffer);