18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Driver for Marvell NETA network controller Buffer Manager. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2015 Marvell 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Marcin Wojtas <mw@semihalf.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 98c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 108c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <linux/genalloc.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/mbus.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 208c2ecf20Sopenharmony_ci#include <linux/of.h> 218c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 248c2ecf20Sopenharmony_ci#include <net/hwbm.h> 258c2ecf20Sopenharmony_ci#include "mvneta_bm.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define MVNETA_BM_DRIVER_NAME "mvneta_bm" 288c2ecf20Sopenharmony_ci#define MVNETA_BM_DRIVER_VERSION "1.0" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic void mvneta_bm_write(struct mvneta_bm *priv, u32 offset, u32 data) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci writel(data, priv->reg_base + offset); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic u32 mvneta_bm_read(struct mvneta_bm *priv, u32 offset) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci return readl(priv->reg_base + offset); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic void mvneta_bm_pool_enable(struct mvneta_bm *priv, int pool_id) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci u32 val; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci val = mvneta_bm_read(priv, MVNETA_BM_POOL_BASE_REG(pool_id)); 458c2ecf20Sopenharmony_ci val |= MVNETA_BM_POOL_ENABLE_MASK; 468c2ecf20Sopenharmony_ci mvneta_bm_write(priv, MVNETA_BM_POOL_BASE_REG(pool_id), val); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* Clear BM cause register */ 498c2ecf20Sopenharmony_ci mvneta_bm_write(priv, MVNETA_BM_INTR_CAUSE_REG, 0); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void mvneta_bm_pool_disable(struct mvneta_bm *priv, int pool_id) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci u32 val; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci val = mvneta_bm_read(priv, MVNETA_BM_POOL_BASE_REG(pool_id)); 578c2ecf20Sopenharmony_ci val &= ~MVNETA_BM_POOL_ENABLE_MASK; 588c2ecf20Sopenharmony_ci mvneta_bm_write(priv, MVNETA_BM_POOL_BASE_REG(pool_id), val); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic inline void mvneta_bm_config_set(struct mvneta_bm *priv, u32 mask) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci u32 val; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci val = mvneta_bm_read(priv, MVNETA_BM_CONFIG_REG); 668c2ecf20Sopenharmony_ci val |= mask; 678c2ecf20Sopenharmony_ci mvneta_bm_write(priv, MVNETA_BM_CONFIG_REG, val); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic inline void mvneta_bm_config_clear(struct mvneta_bm *priv, u32 mask) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci u32 val; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci val = mvneta_bm_read(priv, MVNETA_BM_CONFIG_REG); 758c2ecf20Sopenharmony_ci val &= ~mask; 768c2ecf20Sopenharmony_ci mvneta_bm_write(priv, MVNETA_BM_CONFIG_REG, val); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void mvneta_bm_pool_target_set(struct mvneta_bm *priv, int pool_id, 808c2ecf20Sopenharmony_ci u8 target_id, u8 attr) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci u32 val; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci val = mvneta_bm_read(priv, MVNETA_BM_XBAR_POOL_REG(pool_id)); 858c2ecf20Sopenharmony_ci val &= ~MVNETA_BM_TARGET_ID_MASK(pool_id); 868c2ecf20Sopenharmony_ci val &= ~MVNETA_BM_XBAR_ATTR_MASK(pool_id); 878c2ecf20Sopenharmony_ci val |= MVNETA_BM_TARGET_ID_VAL(pool_id, target_id); 888c2ecf20Sopenharmony_ci val |= MVNETA_BM_XBAR_ATTR_VAL(pool_id, attr); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci mvneta_bm_write(priv, MVNETA_BM_XBAR_POOL_REG(pool_id), val); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ciint mvneta_bm_construct(struct hwbm_pool *hwbm_pool, void *buf) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct mvneta_bm_pool *bm_pool = 968c2ecf20Sopenharmony_ci (struct mvneta_bm_pool *)hwbm_pool->priv; 978c2ecf20Sopenharmony_ci struct mvneta_bm *priv = bm_pool->priv; 988c2ecf20Sopenharmony_ci dma_addr_t phys_addr; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* In order to update buf_cookie field of RX descriptor properly, 1018c2ecf20Sopenharmony_ci * BM hardware expects buf virtual address to be placed in the 1028c2ecf20Sopenharmony_ci * first four bytes of mapped buffer. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_ci *(u32 *)buf = (u32)buf; 1058c2ecf20Sopenharmony_ci phys_addr = dma_map_single(&priv->pdev->dev, buf, bm_pool->buf_size, 1068c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 1078c2ecf20Sopenharmony_ci if (unlikely(dma_mapping_error(&priv->pdev->dev, phys_addr))) 1088c2ecf20Sopenharmony_ci return -ENOMEM; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci mvneta_bm_pool_put_bp(priv, bm_pool, phys_addr); 1118c2ecf20Sopenharmony_ci return 0; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mvneta_bm_construct); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* Create pool */ 1168c2ecf20Sopenharmony_cistatic int mvneta_bm_pool_create(struct mvneta_bm *priv, 1178c2ecf20Sopenharmony_ci struct mvneta_bm_pool *bm_pool) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct platform_device *pdev = priv->pdev; 1208c2ecf20Sopenharmony_ci u8 target_id, attr; 1218c2ecf20Sopenharmony_ci int size_bytes, err; 1228c2ecf20Sopenharmony_ci size_bytes = sizeof(u32) * bm_pool->hwbm_pool.size; 1238c2ecf20Sopenharmony_ci bm_pool->virt_addr = dma_alloc_coherent(&pdev->dev, size_bytes, 1248c2ecf20Sopenharmony_ci &bm_pool->phys_addr, 1258c2ecf20Sopenharmony_ci GFP_KERNEL); 1268c2ecf20Sopenharmony_ci if (!bm_pool->virt_addr) 1278c2ecf20Sopenharmony_ci return -ENOMEM; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (!IS_ALIGNED((u32)bm_pool->virt_addr, MVNETA_BM_POOL_PTR_ALIGN)) { 1308c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, size_bytes, bm_pool->virt_addr, 1318c2ecf20Sopenharmony_ci bm_pool->phys_addr); 1328c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "BM pool %d is not %d bytes aligned\n", 1338c2ecf20Sopenharmony_ci bm_pool->id, MVNETA_BM_POOL_PTR_ALIGN); 1348c2ecf20Sopenharmony_ci return -ENOMEM; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci err = mvebu_mbus_get_dram_win_info(bm_pool->phys_addr, &target_id, 1388c2ecf20Sopenharmony_ci &attr); 1398c2ecf20Sopenharmony_ci if (err < 0) { 1408c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, size_bytes, bm_pool->virt_addr, 1418c2ecf20Sopenharmony_ci bm_pool->phys_addr); 1428c2ecf20Sopenharmony_ci return err; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* Set pool address */ 1468c2ecf20Sopenharmony_ci mvneta_bm_write(priv, MVNETA_BM_POOL_BASE_REG(bm_pool->id), 1478c2ecf20Sopenharmony_ci bm_pool->phys_addr); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci mvneta_bm_pool_target_set(priv, bm_pool->id, target_id, attr); 1508c2ecf20Sopenharmony_ci mvneta_bm_pool_enable(priv, bm_pool->id); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* Notify the driver that BM pool is being used as specific type and return the 1568c2ecf20Sopenharmony_ci * pool pointer on success 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_cistruct mvneta_bm_pool *mvneta_bm_pool_use(struct mvneta_bm *priv, u8 pool_id, 1598c2ecf20Sopenharmony_ci enum mvneta_bm_type type, u8 port_id, 1608c2ecf20Sopenharmony_ci int pkt_size) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct mvneta_bm_pool *new_pool = &priv->bm_pools[pool_id]; 1638c2ecf20Sopenharmony_ci int num, err; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (new_pool->type == MVNETA_BM_LONG && 1668c2ecf20Sopenharmony_ci new_pool->port_map != 1 << port_id) { 1678c2ecf20Sopenharmony_ci dev_err(&priv->pdev->dev, 1688c2ecf20Sopenharmony_ci "long pool cannot be shared by the ports\n"); 1698c2ecf20Sopenharmony_ci return NULL; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (new_pool->type == MVNETA_BM_SHORT && new_pool->type != type) { 1738c2ecf20Sopenharmony_ci dev_err(&priv->pdev->dev, 1748c2ecf20Sopenharmony_ci "mixing pools' types between the ports is forbidden\n"); 1758c2ecf20Sopenharmony_ci return NULL; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (new_pool->pkt_size == 0 || type != MVNETA_BM_SHORT) 1798c2ecf20Sopenharmony_ci new_pool->pkt_size = pkt_size; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* Allocate buffers in case BM pool hasn't been used yet */ 1828c2ecf20Sopenharmony_ci if (new_pool->type == MVNETA_BM_FREE) { 1838c2ecf20Sopenharmony_ci struct hwbm_pool *hwbm_pool = &new_pool->hwbm_pool; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci new_pool->priv = priv; 1868c2ecf20Sopenharmony_ci new_pool->type = type; 1878c2ecf20Sopenharmony_ci new_pool->buf_size = MVNETA_RX_BUF_SIZE(new_pool->pkt_size); 1888c2ecf20Sopenharmony_ci hwbm_pool->frag_size = 1898c2ecf20Sopenharmony_ci SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(new_pool->pkt_size)) + 1908c2ecf20Sopenharmony_ci SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); 1918c2ecf20Sopenharmony_ci hwbm_pool->construct = mvneta_bm_construct; 1928c2ecf20Sopenharmony_ci hwbm_pool->priv = new_pool; 1938c2ecf20Sopenharmony_ci mutex_init(&hwbm_pool->buf_lock); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* Create new pool */ 1968c2ecf20Sopenharmony_ci err = mvneta_bm_pool_create(priv, new_pool); 1978c2ecf20Sopenharmony_ci if (err) { 1988c2ecf20Sopenharmony_ci dev_err(&priv->pdev->dev, "fail to create pool %d\n", 1998c2ecf20Sopenharmony_ci new_pool->id); 2008c2ecf20Sopenharmony_ci return NULL; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* Allocate buffers for this pool */ 2048c2ecf20Sopenharmony_ci num = hwbm_pool_add(hwbm_pool, hwbm_pool->size); 2058c2ecf20Sopenharmony_ci if (num != hwbm_pool->size) { 2068c2ecf20Sopenharmony_ci WARN(1, "pool %d: %d of %d allocated\n", 2078c2ecf20Sopenharmony_ci new_pool->id, num, hwbm_pool->size); 2088c2ecf20Sopenharmony_ci return NULL; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return new_pool; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mvneta_bm_pool_use); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* Free all buffers from the pool */ 2178c2ecf20Sopenharmony_civoid mvneta_bm_bufs_free(struct mvneta_bm *priv, struct mvneta_bm_pool *bm_pool, 2188c2ecf20Sopenharmony_ci u8 port_map) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci int i; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci bm_pool->port_map &= ~port_map; 2238c2ecf20Sopenharmony_ci if (bm_pool->port_map) 2248c2ecf20Sopenharmony_ci return; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci mvneta_bm_config_set(priv, MVNETA_BM_EMPTY_LIMIT_MASK); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci for (i = 0; i < bm_pool->hwbm_pool.buf_num; i++) { 2298c2ecf20Sopenharmony_ci dma_addr_t buf_phys_addr; 2308c2ecf20Sopenharmony_ci u32 *vaddr; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* Get buffer physical address (indirect access) */ 2338c2ecf20Sopenharmony_ci buf_phys_addr = mvneta_bm_pool_get_bp(priv, bm_pool); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* Work-around to the problems when destroying the pool, 2368c2ecf20Sopenharmony_ci * when it occurs that a read access to BPPI returns 0. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci if (buf_phys_addr == 0) 2398c2ecf20Sopenharmony_ci continue; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci vaddr = phys_to_virt(buf_phys_addr); 2428c2ecf20Sopenharmony_ci if (!vaddr) 2438c2ecf20Sopenharmony_ci break; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, buf_phys_addr, 2468c2ecf20Sopenharmony_ci bm_pool->buf_size, DMA_FROM_DEVICE); 2478c2ecf20Sopenharmony_ci hwbm_buf_free(&bm_pool->hwbm_pool, vaddr); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci mvneta_bm_config_clear(priv, MVNETA_BM_EMPTY_LIMIT_MASK); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* Update BM driver with number of buffers removed from pool */ 2538c2ecf20Sopenharmony_ci bm_pool->hwbm_pool.buf_num -= i; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mvneta_bm_bufs_free); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/* Cleanup pool */ 2588c2ecf20Sopenharmony_civoid mvneta_bm_pool_destroy(struct mvneta_bm *priv, 2598c2ecf20Sopenharmony_ci struct mvneta_bm_pool *bm_pool, u8 port_map) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct hwbm_pool *hwbm_pool = &bm_pool->hwbm_pool; 2628c2ecf20Sopenharmony_ci bm_pool->port_map &= ~port_map; 2638c2ecf20Sopenharmony_ci if (bm_pool->port_map) 2648c2ecf20Sopenharmony_ci return; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci bm_pool->type = MVNETA_BM_FREE; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci mvneta_bm_bufs_free(priv, bm_pool, port_map); 2698c2ecf20Sopenharmony_ci if (hwbm_pool->buf_num) 2708c2ecf20Sopenharmony_ci WARN(1, "cannot free all buffers in pool %d\n", bm_pool->id); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (bm_pool->virt_addr) { 2738c2ecf20Sopenharmony_ci dma_free_coherent(&priv->pdev->dev, 2748c2ecf20Sopenharmony_ci sizeof(u32) * hwbm_pool->size, 2758c2ecf20Sopenharmony_ci bm_pool->virt_addr, bm_pool->phys_addr); 2768c2ecf20Sopenharmony_ci bm_pool->virt_addr = NULL; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci mvneta_bm_pool_disable(priv, bm_pool->id); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mvneta_bm_pool_destroy); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic void mvneta_bm_pools_init(struct mvneta_bm *priv) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct device_node *dn = priv->pdev->dev.of_node; 2868c2ecf20Sopenharmony_ci struct mvneta_bm_pool *bm_pool; 2878c2ecf20Sopenharmony_ci char prop[15]; 2888c2ecf20Sopenharmony_ci u32 size; 2898c2ecf20Sopenharmony_ci int i; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* Activate BM unit */ 2928c2ecf20Sopenharmony_ci mvneta_bm_write(priv, MVNETA_BM_COMMAND_REG, MVNETA_BM_START_MASK); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Create all pools with maximum size */ 2958c2ecf20Sopenharmony_ci for (i = 0; i < MVNETA_BM_POOLS_NUM; i++) { 2968c2ecf20Sopenharmony_ci bm_pool = &priv->bm_pools[i]; 2978c2ecf20Sopenharmony_ci bm_pool->id = i; 2988c2ecf20Sopenharmony_ci bm_pool->type = MVNETA_BM_FREE; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* Reset read pointer */ 3018c2ecf20Sopenharmony_ci mvneta_bm_write(priv, MVNETA_BM_POOL_READ_PTR_REG(i), 0); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* Reset write pointer */ 3048c2ecf20Sopenharmony_ci mvneta_bm_write(priv, MVNETA_BM_POOL_WRITE_PTR_REG(i), 0); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* Configure pool size according to DT or use default value */ 3078c2ecf20Sopenharmony_ci sprintf(prop, "pool%d,capacity", i); 3088c2ecf20Sopenharmony_ci if (of_property_read_u32(dn, prop, &size)) { 3098c2ecf20Sopenharmony_ci size = MVNETA_BM_POOL_CAP_DEF; 3108c2ecf20Sopenharmony_ci } else if (size > MVNETA_BM_POOL_CAP_MAX) { 3118c2ecf20Sopenharmony_ci dev_warn(&priv->pdev->dev, 3128c2ecf20Sopenharmony_ci "Illegal pool %d capacity %d, set to %d\n", 3138c2ecf20Sopenharmony_ci i, size, MVNETA_BM_POOL_CAP_MAX); 3148c2ecf20Sopenharmony_ci size = MVNETA_BM_POOL_CAP_MAX; 3158c2ecf20Sopenharmony_ci } else if (size < MVNETA_BM_POOL_CAP_MIN) { 3168c2ecf20Sopenharmony_ci dev_warn(&priv->pdev->dev, 3178c2ecf20Sopenharmony_ci "Illegal pool %d capacity %d, set to %d\n", 3188c2ecf20Sopenharmony_ci i, size, MVNETA_BM_POOL_CAP_MIN); 3198c2ecf20Sopenharmony_ci size = MVNETA_BM_POOL_CAP_MIN; 3208c2ecf20Sopenharmony_ci } else if (!IS_ALIGNED(size, MVNETA_BM_POOL_CAP_ALIGN)) { 3218c2ecf20Sopenharmony_ci dev_warn(&priv->pdev->dev, 3228c2ecf20Sopenharmony_ci "Illegal pool %d capacity %d, round to %d\n", 3238c2ecf20Sopenharmony_ci i, size, ALIGN(size, 3248c2ecf20Sopenharmony_ci MVNETA_BM_POOL_CAP_ALIGN)); 3258c2ecf20Sopenharmony_ci size = ALIGN(size, MVNETA_BM_POOL_CAP_ALIGN); 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci bm_pool->hwbm_pool.size = size; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci mvneta_bm_write(priv, MVNETA_BM_POOL_SIZE_REG(i), 3308c2ecf20Sopenharmony_ci bm_pool->hwbm_pool.size); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* Obtain custom pkt_size from DT */ 3338c2ecf20Sopenharmony_ci sprintf(prop, "pool%d,pkt-size", i); 3348c2ecf20Sopenharmony_ci if (of_property_read_u32(dn, prop, &bm_pool->pkt_size)) 3358c2ecf20Sopenharmony_ci bm_pool->pkt_size = 0; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic void mvneta_bm_default_set(struct mvneta_bm *priv) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci u32 val; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* Mask BM all interrupts */ 3448c2ecf20Sopenharmony_ci mvneta_bm_write(priv, MVNETA_BM_INTR_MASK_REG, 0); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* Clear BM cause register */ 3478c2ecf20Sopenharmony_ci mvneta_bm_write(priv, MVNETA_BM_INTR_CAUSE_REG, 0); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Set BM configuration register */ 3508c2ecf20Sopenharmony_ci val = mvneta_bm_read(priv, MVNETA_BM_CONFIG_REG); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* Reduce MaxInBurstSize from 32 BPs to 16 BPs */ 3538c2ecf20Sopenharmony_ci val &= ~MVNETA_BM_MAX_IN_BURST_SIZE_MASK; 3548c2ecf20Sopenharmony_ci val |= MVNETA_BM_MAX_IN_BURST_SIZE_16BP; 3558c2ecf20Sopenharmony_ci mvneta_bm_write(priv, MVNETA_BM_CONFIG_REG, val); 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic int mvneta_bm_init(struct mvneta_bm *priv) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci mvneta_bm_default_set(priv); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* Allocate and initialize BM pools structures */ 3638c2ecf20Sopenharmony_ci priv->bm_pools = devm_kcalloc(&priv->pdev->dev, MVNETA_BM_POOLS_NUM, 3648c2ecf20Sopenharmony_ci sizeof(struct mvneta_bm_pool), 3658c2ecf20Sopenharmony_ci GFP_KERNEL); 3668c2ecf20Sopenharmony_ci if (!priv->bm_pools) 3678c2ecf20Sopenharmony_ci return -ENOMEM; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci mvneta_bm_pools_init(priv); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return 0; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int mvneta_bm_get_sram(struct device_node *dn, 3758c2ecf20Sopenharmony_ci struct mvneta_bm *priv) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci priv->bppi_pool = of_gen_pool_get(dn, "internal-mem", 0); 3788c2ecf20Sopenharmony_ci if (!priv->bppi_pool) 3798c2ecf20Sopenharmony_ci return -ENOMEM; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci priv->bppi_virt_addr = gen_pool_dma_alloc(priv->bppi_pool, 3828c2ecf20Sopenharmony_ci MVNETA_BM_BPPI_SIZE, 3838c2ecf20Sopenharmony_ci &priv->bppi_phys_addr); 3848c2ecf20Sopenharmony_ci if (!priv->bppi_virt_addr) 3858c2ecf20Sopenharmony_ci return -ENOMEM; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic void mvneta_bm_put_sram(struct mvneta_bm *priv) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci gen_pool_free(priv->bppi_pool, priv->bppi_phys_addr, 3938c2ecf20Sopenharmony_ci MVNETA_BM_BPPI_SIZE); 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistruct mvneta_bm *mvneta_bm_get(struct device_node *node) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct platform_device *pdev = of_find_device_by_node(node); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci return pdev ? platform_get_drvdata(pdev) : NULL; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mvneta_bm_get); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_civoid mvneta_bm_put(struct mvneta_bm *priv) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci platform_device_put(priv->pdev); 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mvneta_bm_put); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int mvneta_bm_probe(struct platform_device *pdev) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct device_node *dn = pdev->dev.of_node; 4138c2ecf20Sopenharmony_ci struct mvneta_bm *priv; 4148c2ecf20Sopenharmony_ci int err; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(struct mvneta_bm), GFP_KERNEL); 4178c2ecf20Sopenharmony_ci if (!priv) 4188c2ecf20Sopenharmony_ci return -ENOMEM; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci priv->reg_base = devm_platform_ioremap_resource(pdev, 0); 4218c2ecf20Sopenharmony_ci if (IS_ERR(priv->reg_base)) 4228c2ecf20Sopenharmony_ci return PTR_ERR(priv->reg_base); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci priv->clk = devm_clk_get(&pdev->dev, NULL); 4258c2ecf20Sopenharmony_ci if (IS_ERR(priv->clk)) 4268c2ecf20Sopenharmony_ci return PTR_ERR(priv->clk); 4278c2ecf20Sopenharmony_ci err = clk_prepare_enable(priv->clk); 4288c2ecf20Sopenharmony_ci if (err < 0) 4298c2ecf20Sopenharmony_ci return err; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci err = mvneta_bm_get_sram(dn, priv); 4328c2ecf20Sopenharmony_ci if (err < 0) { 4338c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate internal memory\n"); 4348c2ecf20Sopenharmony_ci goto err_clk; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci priv->pdev = pdev; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* Initialize buffer manager internals */ 4408c2ecf20Sopenharmony_ci err = mvneta_bm_init(priv); 4418c2ecf20Sopenharmony_ci if (err < 0) { 4428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to initialize controller\n"); 4438c2ecf20Sopenharmony_ci goto err_sram; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci dn->data = priv; 4478c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Buffer Manager for network controller enabled\n"); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return 0; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cierr_sram: 4548c2ecf20Sopenharmony_ci mvneta_bm_put_sram(priv); 4558c2ecf20Sopenharmony_cierr_clk: 4568c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 4578c2ecf20Sopenharmony_ci return err; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic int mvneta_bm_remove(struct platform_device *pdev) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct mvneta_bm *priv = platform_get_drvdata(pdev); 4638c2ecf20Sopenharmony_ci u8 all_ports_map = 0xff; 4648c2ecf20Sopenharmony_ci int i = 0; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci for (i = 0; i < MVNETA_BM_POOLS_NUM; i++) { 4678c2ecf20Sopenharmony_ci struct mvneta_bm_pool *bm_pool = &priv->bm_pools[i]; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci mvneta_bm_pool_destroy(priv, bm_pool, all_ports_map); 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci mvneta_bm_put_sram(priv); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* Dectivate BM unit */ 4758c2ecf20Sopenharmony_ci mvneta_bm_write(priv, MVNETA_BM_COMMAND_REG, MVNETA_BM_STOP_MASK); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci return 0; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic const struct of_device_id mvneta_bm_match[] = { 4838c2ecf20Sopenharmony_ci { .compatible = "marvell,armada-380-neta-bm" }, 4848c2ecf20Sopenharmony_ci { } 4858c2ecf20Sopenharmony_ci}; 4868c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mvneta_bm_match); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic struct platform_driver mvneta_bm_driver = { 4898c2ecf20Sopenharmony_ci .probe = mvneta_bm_probe, 4908c2ecf20Sopenharmony_ci .remove = mvneta_bm_remove, 4918c2ecf20Sopenharmony_ci .driver = { 4928c2ecf20Sopenharmony_ci .name = MVNETA_BM_DRIVER_NAME, 4938c2ecf20Sopenharmony_ci .of_match_table = mvneta_bm_match, 4948c2ecf20Sopenharmony_ci }, 4958c2ecf20Sopenharmony_ci}; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cimodule_platform_driver(mvneta_bm_driver); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell NETA Buffer Manager Driver - www.marvell.com"); 5008c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marcin Wojtas <mw@semihalf.com>"); 5018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 502