Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934819AbbGVPnH (ORCPT ); Wed, 22 Jul 2015 11:43:07 -0400 Received: from mail-bn1on0119.outbound.protection.outlook.com ([157.56.110.119]:15079 "EHLO na01-bn1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S934436AbbGVPnD (ORCPT ); Wed, 22 Jul 2015 11:43:03 -0400 Authentication-Results: spf=fail (sender IP is 192.88.168.50) smtp.mailfrom=freescale.com; freescale.mail.onmicrosoft.com; dkim=none (message not signed) header.d=none; From: To: CC: , , , , , , , Igal Liberman Subject: [v3, 8/9] fsl/fman: Add FMan Port Support Date: Wed, 22 Jul 2015 14:22:49 +0300 Message-ID: <1437564169-16695-1-git-send-email-igal.liberman@freescale.com> X-Mailer: git-send-email 1.7.9.5 Reply-To: X-EOPAttributedMessage: 0 X-Microsoft-Exchange-Diagnostics: 1;BL2FFO11FD013;1:qG/qXSz0okm+/kEp20vqWQnXb6CL8fX2Y6MX4WURTJQQIShRjuTD87yCb5uW9wlWGf2qIfdDEV+zFyq1OkUHovyF9skTxIZNpR5d2Iq4sYPfojmBiO3s0w6R3QFiuyw7y+QmsavH06kAu1RTBm0h8VAU8uJKtZlsGUozg0e/3zyQir7qd19Q+UcFvM9LbhiOA/Yt3xp198GvPXVVePtmeUqsI//iEJfOyZUeEfNBYdY2ZYfkJ9eXlOig7K5q+rt7v4CMtA9r/+jVB+IyGu/CJ+8qHXiJS4M+Avaoqha207DYa7SDp342lIQmOmvy5ogeobqRJuWaBFDAxFk+3rlPXBRxLG50gBp/w/qqrU6PBS+QCJzDMI8oRHUDWJCW4p9lOzqhExYNLU0jcbElUwIIzkrkLKjlkJIA5g9mMY3WxuS/AMGO18irjLK+C8eYVYrs X-Forefront-Antispam-Report: CIP:192.88.168.50;CTRY:US;IPV:NLI;EFV:NLI;SFV:NSPM;SFS:(10019020)(6009001)(2980300002)(339900001)(199003)(189002)(48376002)(6806004)(19580405001)(50466002)(19580395003)(87936001)(50986999)(50226001)(86152002)(86362001)(575784001)(53806999)(47776003)(62966003)(77156002)(77096005)(46102003)(5001960100002)(189998001)(110136002)(107886002)(33646002)(43066003)(2351001)(104016003)(105606002)(5003940100001)(85426001)(106466001)(229853001)(36756003)(4001430100001)(579004)(559001);DIR:OUT;SFP:1102;SCL:1;SRVR:BLUPR03MB373;H:tx30smr01.am.freescale.net;FPR:;SPF:Fail;MLV:sfv;A:1;MX:1;LANG:; MIME-Version: 1.0 Content-Type: text/plain X-Microsoft-Exchange-Diagnostics: 1;BLUPR03MB373;2:zFheTfe29/zgv0P7gTbqy3ExlHwSeocdGjdUQrvwsMYme0QSMPsi5+zGU5cxgGqCar+m8BkSEqegs8EQPocD/ndXXq10vf0nZCE3FBc2P7Dc37eLInlwzKy8LsCkGQXxWwEN1NFmyWqflI6Jow/+5ex8W4x96eVTvd0sx4fsDkk=;3:W+xPxocRU14PQkfZxzED3h8ykcq/PN/GjPyOuoVaAXAOxWm1NgIa1RaMVH0WtHSe6kt2+BnQLyBKpfB7+VcKRxV1eDCqjOXQEUcq03ymTnH5Za5FX4fTYqXem/fzaiwW9Eh0bduQ6UenOkjg/VPfLtWpualvr02T+qjWzCLZCTZqATssf5CNhOtLEajAp9p8lZ+K001k27T0URev5XfAwAxoybdAq5AKv1d39xKCyO4=;25:TjpXCq7s4R+UEFaJ1kbyOdP09RHRU/6iU5CKWVlmZqMT4QRxDikhE6kjTW1RHbw028pvJ0fihZQXm/q14+hebFNFe+Hf5hKV72q4AwBe9XFg3IvIM4Dlf9lfQZN5d5IqUSQB1mSQ77z6jHN+2SywsjSdOSIoGVXn456bx6kdR6szH1QbE39ILjKipR1QkAkj3/rbKtGyOh8xLlldwbNY/y8KSXVp6FORucOV3TtjzkiP7Q9PjA3eRRWgWgFq1d0xra6px/Jjcihayp+n7bnL6g== X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:BLUPR03MB373; X-Microsoft-Exchange-Diagnostics: 1;BLUPR03MB373;20:hg4qKRecStvxwzJn5Fm7p/vLMfdvJE31RgT7pYUZhYZGlV/puDmoldy7HcQbA4ES+PntR20MPEwcSWRAxI/q353m7GG9PbD9iTaaOP0pVVi2oPY7iCQJQ7Y72XgCxqo+GEFhJWNzCRR5e7Kzpo11pyLKq4xjGHW/vLpwTyHGouAN8YB4Zr8Bvvf96ip/PdJXKEe75RH+ovidtd3th6x+Kic7i3TspDwEaN3I2dEgvbDNbr0ZbBFUztSg91cASUzh+G5Xfn0kwcK1VD477pjPCgm52BnWEzYMqB24s6XrFvZZrxNmfpU0fHkKWbvl/2AaArZK4cREeIdExYegZ67xMRfIUiN09uH3O+X1zzVLbQg=;4:Wn0WvAOSE8KhKLoDeEVC69ohsPO5JFxCSKyquWgZvvG56iVaU8cNex1HSMSoEj4Lep+S/2wSYyjvSNSV4CYSDsnrTtumX+mD99lXJNDsRz3XkKDOkbB9zG0NmFnC5o7pwA5fNoOaQLPH0xf+qZ/iWXfftbvS51ybvrMUkWPqGZBinwvhojc23e6UKdMoUoAwno6n7zF5mv/C1+M6JVQ/qK5AfrevXMiQzcUiubKx8mCp3FNLzGPcrxOoWMvXFxn6Zn2ZwUuXq/cckorg5DHaNWCw2WETyXRXFddgYf5xQ00= BLUPR03MB373: X-MS-Exchange-Organization-RulesExecuted X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(601004)(5005006)(3002001);SRVR:BLUPR03MB373;BCL:0;PCL:0;RULEID:;SRVR:BLUPR03MB373; X-Forefront-PRVS: 0645BEB7AA X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;BLUPR03MB373;23:1RPjN18qKqGf+FkYnAQuskUXd84ammFmtRiaQmWada?= =?us-ascii?Q?CnP/RmhTM078Y9KHGLo9NPGTczzmq5zIQ/EAp+ZRHJhznBGaKfpmsHf+CMoo?= =?us-ascii?Q?T/A6nIxDruF3+6dzp97xOySyz0oFL2w+Bzt9QgSyBSrxOS6mAOUPeNG8ZJLP?= =?us-ascii?Q?NksUZ077kJ82bjcd7emRHDzkSP/r6dYR9ID+oKE3Dvadkq0m2Ef57R8K7yVZ?= =?us-ascii?Q?OrsXIIdz3Mbc2dmW2qbUkew4J334GmIxVO7zSK3pOWAfQW6HwH6WrrJYkgS6?= =?us-ascii?Q?KabqaFc6Z0vsZS3wwXSak7u3CQ1A0RtFlzvKZGaD+Kk6f9zX884Tld6kpEIF?= =?us-ascii?Q?ND+xVfnsBFx1LefeVbxMW+1wStaf1xjojtZaQPtu0hdu/KkD4ANwRvjv006n?= =?us-ascii?Q?XWCKC+TJCQfNB+GFjAyESbMaTGyEIqsAfqASmGR3XO5cEmR1DZmyC6TG/OIl?= =?us-ascii?Q?zC+znAgGoGhMEaCecYf6f+xhW9keipx45odJZPoFBNncV3gg4L9yFmZsrNhi?= =?us-ascii?Q?fChSMeaom8LzAg3lUamWuS0CefMFia+NAn7RTJ3lXNSs3QROYcHoJLR4KMNr?= =?us-ascii?Q?KWfeDyIOx6ndN7XswPNFNkK4c6LS0ozgmg0oZMrJ+5iUD5czD0nTUBKgL6DK?= =?us-ascii?Q?UCZAfvSX+34iZ/Cc8Vbdgc6S6JymZTb7n4f//n8zLajsRgXpj6YoYrVT5lWg?= =?us-ascii?Q?VaqAEzk+Z23WouKQ0QlsVHcowVIJf3xlcv/tePqBNOfODBDrImflGkPbXFUe?= =?us-ascii?Q?/LZdggWAcY3btDQupyJtwcsGMoHKQQGYh6lpsEAEohkd3f14hoj+f2lxdl8h?= =?us-ascii?Q?SAnUV7HIOUrgV0d05zzpLKM/nF4wl79kHQKIuo5/kMjsdUhpMAMExmFitVXd?= =?us-ascii?Q?06djxzmB1V9qN/1Ujdakukw//cqNWpXSRf+ToO+wMArbxzkpg/IOMVbZ9z9F?= =?us-ascii?Q?mPxOtVaFrBYi0lXqX/xR/NTzM/q7i3yWJspaH2KGumRkTdkKlS0sMn424S/R?= =?us-ascii?Q?Ff+GqvI47r+VM+1OvdbRwS?= X-Microsoft-Exchange-Diagnostics: 1;BLUPR03MB373;5:C08CbapxP9rxvBOvFr0Zrx1hmTkVCJyRbISFk/mtM9hbu8AgHs+1X/uz4i2MjKZ0t3gE/2qiwTsKi/Ec6CFEFNwjayhg94j1q+m2UozTxjTgIkEP8/YqHdawUttbQw/GGOcuzzebO4pJ/kIIC4HvXg==;24:wU800XKWtSb2IUBhrxbDFIlceq3bFVB38VBj3AE6RxI7O4JCvp4jvIjDFFOSvoo2lxdx5CBWptRrJN/SnQvChQR1xyhNOqg3hRAIrhQnU8U=;20:X+vXbGuuxP89AttNnBg3h4E8oGUdA3eOGnj1G+vAM/MXHbgq2rBwHEYXu8iZM+MGZxTcFxuaw8EdZdx2vZ60UA== X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Jul 2015 15:27:28.7902 (UTC) X-MS-Exchange-CrossTenant-Id: 710a03f5-10f6-4d38-9ff4-a80b81da590d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=710a03f5-10f6-4d38-9ff4-a80b81da590d;Ip=[192.88.168.50];Helo=[tx30smr01.am.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BLUPR03MB373 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 99131 Lines: 2966 From: Igal Liberman This patch adds The FMan Port configuration, initialization and runtime control routines. Signed-off-by: Igal Liberman --- drivers/net/ethernet/freescale/fman/Makefile | 2 +- drivers/net/ethernet/freescale/fman/fm.c | 251 ++++- drivers/net/ethernet/freescale/fman/fm_common.h | 35 + drivers/net/ethernet/freescale/fman/fm_drv.c | 72 +- drivers/net/ethernet/freescale/fman/fm_drv.h | 2 + drivers/net/ethernet/freescale/fman/fm_port_drv.c | 386 +++++++ .../net/ethernet/freescale/fman/inc/fm_port_ext.h | 340 ++++++ .../net/ethernet/freescale/fman/inc/fsl_fman_drv.h | 104 ++ drivers/net/ethernet/freescale/fman/port/Makefile | 2 +- drivers/net/ethernet/freescale/fman/port/fm_port.c | 1081 ++++++++++++++++++++ drivers/net/ethernet/freescale/fman/port/fm_port.h | 502 +++++++++ 11 files changed, 2773 insertions(+), 4 deletions(-) create mode 100644 drivers/net/ethernet/freescale/fman/fm_port_drv.c create mode 100644 drivers/net/ethernet/freescale/fman/inc/fm_port_ext.h create mode 100644 drivers/net/ethernet/freescale/fman/port/fm_port.c create mode 100644 drivers/net/ethernet/freescale/fman/port/fm_port.h diff --git a/drivers/net/ethernet/freescale/fman/Makefile b/drivers/net/ethernet/freescale/fman/Makefile index c6c3e24..8d637e2 100644 --- a/drivers/net/ethernet/freescale/fman/Makefile +++ b/drivers/net/ethernet/freescale/fman/Makefile @@ -4,7 +4,7 @@ subdir-ccflags-y += -I$(srctree)/drivers/net/ethernet/freescale/fman/flib \ obj-y += fsl_fman.o -fsl_fman-objs := fman.o fm_muram.o fm.o fm_drv.o +fsl_fman-objs := fman.o fm_muram.o fm.o fm_drv.o fm_port_drv.o obj-y += port/ obj-y += mac/ diff --git a/drivers/net/ethernet/freescale/fman/fm.c b/drivers/net/ethernet/freescale/fman/fm.c index 450ee6b..a8ecf0b 100644 --- a/drivers/net/ethernet/freescale/fman/fm.c +++ b/drivers/net/ethernet/freescale/fman/fm.c @@ -376,11 +376,29 @@ static void qmi_err_event(struct fm_t *fm) static void dma_err_event(struct fm_t *fm) { - u32 status; + u32 status, com_id; + u8 tnum, port_id, relative_port_id; + u16 liodn; struct fman_dma_regs __iomem *dma_rg = fm->dma_regs; status = fman_get_dma_err_event(dma_rg); + if (status & DMA_STATUS_BUS_ERR) { + com_id = fman_get_dma_com_id(dma_rg); + port_id = (u8)(((com_id & DMA_TRANSFER_PORTID_MASK) >> + DMA_TRANSFER_PORTID_SHIFT)); + relative_port_id = + hw_port_id_to_sw_port_id(fm->fm_state->rev_info.major_rev, + port_id); + tnum = (u8)((com_id & DMA_TRANSFER_TNUM_MASK) >> + DMA_TRANSFER_TNUM_SHIFT); + liodn = (u16)(com_id & DMA_TRANSFER_LIODN_MASK); + WARN_ON(fm->fm_state->ports_types[port_id] == + FM_PORT_TYPE_DUMMY); + fm->bus_error_cb(fm->dev_id, fm->fm_state->ports_types[port_id], + relative_port_id, + fman_get_dma_addr(dma_rg), tnum, liodn); + } if (status & DMA_STATUS_FM_SPDAT_ECC) fm->exception_cb(fm->dev_id, FM_EX_DMA_SINGLE_PORT_ECC); if (status & DMA_STATUS_READ_ECC) @@ -587,6 +605,233 @@ u8 fm_get_id(struct fm_t *fm) return fm->fm_state->fm_id; } +int fm_get_set_port_params(struct fm_t *fm, + struct fm_inter_module_port_init_params_t + *port_params) +{ + int err; + unsigned long int_flags; + u8 port_id = port_params->port_id, mac_id; + struct fman_rg fman_rg; + + fman_rg.bmi_rg = fm->bmi_regs; + fman_rg.qmi_rg = fm->qmi_regs; + fman_rg.fpm_rg = fm->fpm_regs; + fman_rg.dma_rg = fm->dma_regs; + + spin_lock_irqsave(&fm->spinlock, int_flags); + + fm->fm_state->ports_types[port_id] = port_params->port_type; + + err = fm_set_num_of_tasks(fm, port_params->port_id, + &port_params->num_of_tasks, + &port_params->num_of_extra_tasks); + if (err) { + spin_unlock_irqrestore(&fm->spinlock, int_flags); + return err; + } + + /* TX Ports */ + if (port_params->port_type != FM_PORT_TYPE_RX) { + u32 enq_th; + u32 deq_th; + + /* update qmi ENQ/DEQ threshold */ + fm->fm_state->accumulated_num_of_deq_tnums += + port_params->deq_pipeline_depth; + enq_th = fman_get_qmi_enq_th(fman_rg.qmi_rg); + /* if enq_th is too big, we reduce it to the max value + * that is still 0 + */ + if (enq_th >= (fm->intg->qmi_max_num_of_tnums - + fm->fm_state->accumulated_num_of_deq_tnums)) { + enq_th = + fm->intg->qmi_max_num_of_tnums - + fm->fm_state->accumulated_num_of_deq_tnums - 1; + fman_set_qmi_enq_th(fman_rg.qmi_rg, enq_th); + } + + deq_th = fman_get_qmi_deq_th(fman_rg.qmi_rg); + /* if deq_th is too small, we enlarge it to the min + * value that is still 0. + * depTh may not be larger than 63 + * (fm->intg->qmi_max_num_of_tnums-1). + */ + if ((deq_th <= fm->fm_state->accumulated_num_of_deq_tnums) && + (deq_th < fm->intg->qmi_max_num_of_tnums - 1)) { + deq_th = + fm->fm_state->accumulated_num_of_deq_tnums + 1; + fman_set_qmi_deq_th(fman_rg.qmi_rg, deq_th); + } + } + + err = fm_set_size_of_fifo(fm, port_params->port_id, + &port_params->size_of_fifo, + &port_params->extra_size_of_fifo); + if (err) { + spin_unlock_irqrestore(&fm->spinlock, int_flags); + return err; + } + + err = fm_set_num_of_open_dmas(fm, port_params->port_id, + &port_params->num_of_open_dmas, + &port_params->num_of_extra_open_dmas); + if (err) { + spin_unlock_irqrestore(&fm->spinlock, int_flags); + return err; + } + + fman_set_liodn_per_port(&fman_rg, port_id, port_params->liodn_base, + port_params->liodn_offset); + + if (fm->fm_state->rev_info.major_rev < 6) + fman_set_order_restoration_per_port(fman_rg.fpm_rg, + port_id, + !!((port_params-> + port_type == + FM_PORT_TYPE_RX))); + + mac_id = hw_port_id_to_sw_port_id(fm->fm_state->rev_info. + major_rev, + port_id); + + if (port_params->max_frame_length >= fm->fm_state->mac_mfl[mac_id]) { + fm->fm_state->port_mfl[mac_id] = port_params->max_frame_length; + } else { + pr_warn("Port max_frame_length is smaller than MAC current MTU\n"); + spin_unlock_irqrestore(&fm->spinlock, int_flags); + return -EINVAL; + } + + spin_unlock_irqrestore(&fm->spinlock, int_flags); + + return 0; +} + +int fm_set_size_of_fifo(struct fm_t *fm, u8 port_id, u32 *size_of_fifo, + u32 *extra_size_of_fifo) +{ + struct fman_bmi_regs __iomem *bmi_rg = fm->bmi_regs; + u32 fifo = *size_of_fifo; + u32 extra_fifo = *extra_size_of_fifo; + + /* if this is the first time a port requires extra_fifo_pool_size, + * the total extra_fifo_pool_size must be initialized to 1 buffer per + * port + */ + if (extra_fifo && !fm->fm_state->extra_fifo_pool_size) + fm->fm_state->extra_fifo_pool_size = + fm->intg->num_of_rx_ports * BMI_FIFO_UNITS; + + fm->fm_state->extra_fifo_pool_size = + max(fm->fm_state->extra_fifo_pool_size, extra_fifo); + + /* check that there are enough uncommitted fifo size */ + if ((fm->fm_state->accumulated_fifo_size + fifo) > + (fm->fm_state->total_fifo_size - + fm->fm_state->extra_fifo_pool_size)) { + pr_err("Requested fifo size and extra size exceed total FIFO size.\n"); + return -EAGAIN; + } + /* update accumulated */ + fm->fm_state->accumulated_fifo_size += fifo; + fman_set_size_of_fifo(bmi_rg, port_id, fifo, extra_fifo); + + return 0; +} + +int fm_set_num_of_tasks(struct fm_t *fm, u8 port_id, u8 *num_of_tasks, + u8 *num_of_extra_tasks) +{ + struct fman_bmi_regs __iomem *bmi_rg = fm->bmi_regs; + u8 tasks = *num_of_tasks; + u8 extra_tasks = *num_of_extra_tasks; + + if (extra_tasks) + fm->fm_state->extra_tasks_pool_size = + (u8)max(fm->fm_state->extra_tasks_pool_size, extra_tasks); + + /* check that there are enough uncommitted tasks */ + if ((fm->fm_state->accumulated_num_of_tasks + tasks) > + (fm->fm_state->total_num_of_tasks - + fm->fm_state->extra_tasks_pool_size)) { + pr_err("Requested num_of_tasks and extra tasks pool for fm%d exceed total num_of_tasks.\n", + fm->fm_state->fm_id); + return -EAGAIN; + } + /* update accumulated */ + fm->fm_state->accumulated_num_of_tasks += tasks; + fman_set_num_of_tasks(bmi_rg, port_id, tasks, extra_tasks); + + return 0; +} + +int fm_set_num_of_open_dmas(struct fm_t *fm, u8 port_id, u8 *num_of_open_dmas, + u8 *num_of_extra_open_dmas) +{ + struct fman_bmi_regs __iomem *bmi_rg = fm->bmi_regs; + u8 open_dmas = *num_of_open_dmas; + u8 extra_open_dmas = *num_of_extra_open_dmas; + u8 total_num_dmas = 0, current_val = 0, current_extra_val = 0; + + if (!open_dmas) { + /* !open_dmas - first configuration according + * to values in regs.- read the current number of + * open Dma's + */ + current_extra_val = fman_get_num_extra_dmas(bmi_rg, port_id); + current_val = fman_get_num_of_dmas(bmi_rg, port_id); + /* This is the first configuration and user did not + * specify value (!open_dmas), reset values will be used + * and we just save these values for resource management + */ + fm->fm_state->extra_open_dmas_pool_size = + (u8)max(fm->fm_state->extra_open_dmas_pool_size, + current_extra_val); + fm->fm_state->accumulated_num_of_open_dmas += current_val; + *num_of_open_dmas = current_val; + *num_of_extra_open_dmas = current_extra_val; + return 0; + } + + if (extra_open_dmas > current_extra_val) + fm->fm_state->extra_open_dmas_pool_size = + (u8)max(fm->fm_state->extra_open_dmas_pool_size, + extra_open_dmas); + + if ((fm->fm_state->rev_info.major_rev < 6) && + (fm->fm_state->accumulated_num_of_open_dmas - current_val + + open_dmas > fm->fm_state->max_num_of_open_dmas)) { + pr_err("Requested num_of_open_dmas for fm%d exceeds total num_of_open_dmas.\n", + fm->fm_state->fm_id); + return -EAGAIN; + } else if ((fm->fm_state->rev_info.major_rev >= 6) && + !((fm->fm_state->rev_info.major_rev == 6) && + (fm->fm_state->rev_info.minor_rev == 0)) && + (fm->fm_state->accumulated_num_of_open_dmas - + current_val + open_dmas > + fm->intg->dma_thresh_max_commq + 1)) { + pr_err("Requested num_of_open_dmas for fm%d exceeds DMA Command queue (%d)\n", + fm->fm_state->fm_id, fm->intg->dma_thresh_max_commq + 1); + return -EAGAIN; + } + + WARN_ON(fm->fm_state->accumulated_num_of_open_dmas < current_val); + /* update acummulated */ + fm->fm_state->accumulated_num_of_open_dmas -= current_val; + fm->fm_state->accumulated_num_of_open_dmas += open_dmas; + + if (fm->fm_state->rev_info.major_rev < 6) + total_num_dmas = + (u8)(fm->fm_state->accumulated_num_of_open_dmas + + fm->fm_state->extra_open_dmas_pool_size); + + fman_set_num_of_open_dmas(bmi_rg, port_id, open_dmas, + extra_open_dmas, total_num_dmas); + + return 0; +} + int fm_reset_mac(struct fm_t *fm, u8 mac_id) { int err; @@ -690,6 +935,7 @@ static int init_fm_dma(struct fm_t *fm) void *fm_config(struct fm_params_t *fm_param) { struct fm_t *fm; + u8 i; void __iomem *base_addr; base_addr = fm_param->base_addr; @@ -706,6 +952,9 @@ void *fm_config(struct fm_params_t *fm_param) /* Initialize FM parameters which will be kept by the driver */ fm->fm_state->fm_id = fm_param->fm_id; + for (i = 0; i < FM_MAX_NUM_OF_HW_PORT_IDS; i++) + fm->fm_state->ports_types[i] = FM_PORT_TYPE_DUMMY; + /* Allocate the FM driver's parameters structure */ fm->fm_drv_param = kzalloc(sizeof(*fm->fm_drv_param), GFP_KERNEL); if (!fm->fm_drv_param) diff --git a/drivers/net/ethernet/freescale/fman/fm_common.h b/drivers/net/ethernet/freescale/fman/fm_common.h index abe89a7..ae51553 100644 --- a/drivers/net/ethernet/freescale/fman/fm_common.h +++ b/drivers/net/ethernet/freescale/fman/fm_common.h @@ -120,6 +120,30 @@ enum fm_mac_type { FM_MAC_1G /* 1G MAC */ }; +/* Structure for port-FM communication + * during fm_port_init. Fields commented 'IN' are passed + * by the port module to be used by the FM module. + * Fields commented 'OUT' will be filled by FM before returning to port. + * Some fields are optional (depending on configuration) and + * will be analized by the port and FM modules accordingly. + */ +struct fm_inter_module_port_init_params_t { + u8 port_id; /* IN. port Id */ + enum fm_port_type port_type; /* IN. Port type */ + enum fm_port_speed port_speed; /* IN. Port speed */ + u16 liodn_offset; /* IN. Port's requested resource */ + u8 num_of_tasks; /* IN. Port's requested resource */ + u8 num_of_extra_tasks; /* IN. Port's requested resource */ + u8 num_of_open_dmas; /* IN. Port's requested resource */ + u8 num_of_extra_open_dmas; /* IN. Port's requested resource */ + u32 size_of_fifo; /* IN. Port's requested resource */ + u32 extra_size_of_fifo; /* IN. Port's requested resource */ + u8 deq_pipeline_depth; /* IN. Port's requested resource */ + u16 max_frame_length; /* IN. Port's max frame length. */ + u16 liodn_base; + /* LIODN base for this port, to be used together with LIODN offset. */ +}; + void fm_register_intr(struct fm_t *fm, enum fm_event_modules mod, u8 mod_id, enum fm_intr_type intr_type, void (*f_isr)(void *h_src_arg), void *h_src_arg); @@ -135,6 +159,17 @@ u16 fm_get_clock_freq(struct fm_t *fm); u8 fm_get_id(struct fm_t *fm); +int fm_get_set_port_params(struct fm_t *fm, + struct fm_inter_module_port_init_params_t + *port_params); + +int fm_set_num_of_open_dmas(struct fm_t *fm, u8 port_id, u8 *num_of_open_dmas, + u8 *num_of_extra_open_dmas); +int fm_set_num_of_tasks(struct fm_t *fm, u8 port_id, u8 *num_of_tasks, + u8 *num_of_extra_tasks); +int fm_set_size_of_fifo(struct fm_t *fm, u8 port_id, u32 *size_of_fifo, + u32 *extra_size_of_fifo); + u32 fm_get_bmi_max_fifo_size(struct fm_t *fm); int fm_set_mac_max_frame(struct fm_t *fm, enum fm_mac_type type, u8 mac_id, diff --git a/drivers/net/ethernet/freescale/fman/fm_drv.c b/drivers/net/ethernet/freescale/fman/fm_drv.c index f2bee7b..7e9dc28 100644 --- a/drivers/net/ethernet/freescale/fman/fm_drv.c +++ b/drivers/net/ethernet/freescale/fman/fm_drv.c @@ -144,6 +144,51 @@ static irqreturn_t fm_err_irq(int irq, void *fm_dev) return IRQ_NONE; } +static int fill_rest_fm_info(struct fm_drv_t *fm_drv) +{ +#define FM_BMI_PPIDS_OFFSET 0x00080304 +#define FM_DMA_PLR_OFFSET 0x000c2060 +#define DMA_HIGH_LIODN_MASK 0x0FFF0000 +#define DMA_LOW_LIODN_MASK 0x00000FFF +#define DMA_LIODN_SHIFT 16 + + struct plr_t { + u32 plr[32]; + }; + + struct ppids_t { + u32 fmbm_ppid[63]; + }; + + struct plr_t *p_plr; + struct ppids_t __iomem *p_ppids; + int i; + + p_plr = (struct plr_t *)(fm_drv->fm_base_addr + FM_DMA_PLR_OFFSET); + + for (i = 0; i < FM_MAX_NUM_OF_PARTITIONS; i++) { + u16 liodn_base; + + liodn_base = (u16)((i % 2) ? + (p_plr->plr[i / 2] & DMA_LOW_LIODN_MASK) : + ((p_plr->plr[i / 2] & DMA_HIGH_LIODN_MASK) >> + DMA_LIODN_SHIFT)); + + if (((i >= FIRST_RX_PORT) && (i <= LAST_RX_PORT)) || + ((i >= FIRST_TX_PORT) && (i <= LAST_TX_PORT))) + fm_drv->ports[i].port_params.liodn_base = liodn_base; + } + + p_ppids = (struct ppids_t __iomem *) + (fm_drv->fm_base_addr + FM_BMI_PPIDS_OFFSET); + + for (i = FIRST_RX_PORT; i <= LAST_RX_PORT; i++) + fm_drv->ports[i].port_params.specific_params.rx_params. + liodn_offset = (u16)ioread32be(&p_ppids->fmbm_ppid[i - 1]); + + return 0; +} + static int fill_qman_channels_info(struct fm_drv_t *fm_drv) { fm_drv->qman_channels = kcalloc(fm_drv->num_of_qman_channels, @@ -418,7 +463,7 @@ static int configure_fm_dev(struct fm_drv_t *fm_drv) fm_drv->params.bus_error_cb = fm_drv_bus_error_cb; fm_drv->params.dev_id = fm_drv; - return 0; + return fill_rest_fm_info(fm_drv); } static int init_fm_dev(struct fm_drv_t *fm_drv) @@ -540,6 +585,31 @@ void *fm_get_handle(struct fm *fm) return (void *)fm_drv->fm_dev; } +struct fm_port_drv_t *fm_port_bind(struct device *fm_port_dev) +{ + return (struct fm_port_drv_t *) + (dev_get_drvdata(get_device(fm_port_dev))); +} + +struct fm_port_t *fm_port_drv_handle(const struct fm_port_drv_t *port) +{ + return port->fm_port; +} +EXPORT_SYMBOL(fm_port_drv_handle); + +void fm_port_get_buff_layout_ext_params(struct fm_port_drv_t *port, + struct fm_port_params *params) +{ + params->data_align = 0; +} +EXPORT_SYMBOL(fm_port_get_buff_layout_ext_params); + +int fm_get_tx_port_channel(struct fm_port_drv_t *port) +{ + return port->tx_ch; +} +EXPORT_SYMBOL(fm_get_tx_port_channel); + static const struct of_device_id fm_match[] = { { .compatible = "fsl,fman"}, diff --git a/drivers/net/ethernet/freescale/fman/fm_drv.h b/drivers/net/ethernet/freescale/fman/fm_drv.h index 73d9eff..298a9b96 100644 --- a/drivers/net/ethernet/freescale/fman/fm_drv.h +++ b/drivers/net/ethernet/freescale/fman/fm_drv.h @@ -36,6 +36,7 @@ #include #include "fsl_fman_drv.h" +#include "fm_port_ext.h" #ifndef CONFIG_FSL_FM_MAX_FRAME_SIZE #define CONFIG_FSL_FM_MAX_FRAME_SIZE 0 @@ -69,6 +70,7 @@ struct fm_port_drv_t { phys_addr_t phys_base_addr; void __iomem *base_addr; /* Port's *virtual* address */ resource_size_t mem_size; + struct fm_port_params_t port_params; struct fm_buffer_prefix_content_t buff_prefix_content; struct fm_port_t *fm_port; struct fm_drv_t *fm; diff --git a/drivers/net/ethernet/freescale/fman/fm_port_drv.c b/drivers/net/ethernet/freescale/fman/fm_port_drv.c new file mode 100644 index 0000000..3807f5f --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fm_port_drv.c @@ -0,0 +1,386 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "fm_common.h" +#include "fsl_fman_drv.h" +#include "fm_port_ext.h" +#include "fm_drv.h" + +static struct fm_port_drv_t +*read_fm_port_dev_tree_node(struct platform_device *of_dev) +{ + struct fm_drv_t *fm_drv; + struct fm_port_drv_t *port; + struct device_node *fm_node, *port_node; + struct resource res; + const u32 *u32_prop; + int err = 0, lenp; + enum fm_port_type port_type; + enum fm_port_speed port_speed = FM_PORT_SPEED_DUMMY; + u8 cell_index; + + port_node = of_node_get(of_dev->dev.of_node); + + /* Get the FM node */ + fm_node = of_get_parent(port_node); + if (!fm_node) { + pr_err("of_get_parent() = %d\n", err); + return NULL; + } + + fm_drv = dev_get_drvdata(&of_find_device_by_node(fm_node)->dev); + of_node_put(fm_node); + + /* if fm_probe() failed, no point in going further with port probing */ + if (!fm_drv) + return NULL; + + u32_prop = (u32 *)of_get_property(port_node, "cell-index", &lenp); + if (!u32_prop) { + pr_err("of_get_property(%s, cell-index) failed\n", + port_node->full_name); + return NULL; + } + if (WARN_ON(lenp != sizeof(u32))) + return NULL; + cell_index = (u8)*u32_prop; + + port = &fm_drv->ports[cell_index]; + port->id = cell_index; + port->port_params.port_id = port->id; + + if (of_device_is_compatible(port_node, "fsl,fman-v3-port-tx")) { + port_type = FM_PORT_TYPE_TX; + port_speed = FM_PORT_SPEED_1G; + u32_prop = (u32 *)of_get_property(port_node, + "fsl,fman-10g-port", &lenp); + if (u32_prop) + port_speed = FM_PORT_SPEED_10G; + + } else if (of_device_is_compatible(port_node, "fsl,fman-v2-port-tx")) { + if (cell_index >= TX_10G_PORT_BASE) + port_speed = FM_PORT_SPEED_10G; + else + port_speed = FM_PORT_SPEED_1G; + port_type = FM_PORT_TYPE_TX; + + } else if (of_device_is_compatible(port_node, "fsl,fman-v3-port-rx")) { + port_type = FM_PORT_TYPE_RX; + port_speed = FM_PORT_SPEED_1G; + u32_prop = (u32 *)of_get_property(port_node, + "fsl,fman-10g-port", &lenp); + if (u32_prop) + port_speed = FM_PORT_SPEED_10G; + + } else if (of_device_is_compatible(port_node, "fsl,fman-v2-port-rx")) { + if (cell_index >= RX_10G_PORT_BASE) + port_speed = FM_PORT_SPEED_10G; + else + port_speed = FM_PORT_SPEED_1G; + port_type = FM_PORT_TYPE_RX; + + } else { + pr_err("Illegal port type\n"); + return NULL; + } + + port->port_params.port_type = port_type; + port->port_params.port_speed = port_speed; + + if (port_type == FM_PORT_TYPE_TX) { + u32 qman_channel_id; + + qman_channel_id = get_qman_channel_id(fm_drv, cell_index); + + if (qman_channel_id == 0) { + pr_err("incorrect qman-channel-id\n"); + return NULL; + } + port->tx_ch = qman_channel_id; + port->port_params.specific_params.non_rx_params.qm_channel = + qman_channel_id; + } + + err = of_address_to_resource(port_node, 0, &res); + if (err < 0) { + pr_err("of_address_to_resource() = %d\n", err); + return NULL; + } + + port->dev = &of_dev->dev; + port->base_addr = 0; + port->phys_base_addr = res.start; + port->mem_size = res.end + 1 - res.start; + port->port_params.fm = fm_drv->fm_dev; + port->fm = (void *)fm_drv; + + of_node_put(port_node); + + port->active = true; + + return port; +} + +static int configure_fm_port_dev(struct fm_port_drv_t *port) +{ + struct fm_drv_t *fm_drv = (struct fm_drv_t *)port->fm; + struct resource *dev_res; + + if (!port->active) { + pr_err("FM port not configured!!!\n"); + return -EINVAL; + } + + dev_res = __devm_request_region(fm_drv->dev, fm_drv->res, + port->phys_base_addr, + port->mem_size, + "fman-port-hc"); + if (!dev_res) { + pr_err("__devm_request_region() failed\n"); + return -EINVAL; + } + port->base_addr = devm_ioremap(fm_drv->dev, port->phys_base_addr, + port->mem_size); + if (port->base_addr == 0) + pr_err("devm_ioremap() failed\n"); + + port->port_params.base_addr = port->base_addr; + + return 0; +} + +static int init_fm_port_dev(struct fm_port_drv_t *port) +{ + struct fm_drv_t *fm_drv = (struct fm_drv_t *)port->fm; + + if (!port->active || port->fm_port) + return -EINVAL; + + port->fm_port = fm_port_config(&port->port_params); + if (!port->fm_port) { + pr_err("fm_port_config() failed\n"); + return -EINVAL; + } + + fm_get_revision(fm_drv->fm_dev, &port->fm_rev_info); + + /* FM_QMI_NO_DEQ_OPTIONS_SUPPORT Errata workaround */ + if (port->port_params.port_type == FM_PORT_TYPE_TX) { + int err = 0; + + err = fm_port_cfg_deq_high_priority(port->fm_port, true); + if (err) + return err; + err = + fm_port_cfg_deq_prefetch_option(port->fm_port, + FM_PORT_DEQ_FULL_PREFETCH); + if (err) + return err; + } + + /* Configure BCB workaround on Rx ports, only for B4860 rev1 */ + if ((port->fm_rev_info.major_rev >= 6) && + (port->port_params.port_type == FM_PORT_TYPE_RX)) { + unsigned int svr; + + svr = mfspr(SPRN_SVR); + if ((SVR_SOC_VER(svr) == SVR_B4860) && (SVR_MAJ(svr) == 1)) + fm_port_cfg_bcb_wa(port->fm_port); + } + + fm_port_cfg_buf_prefix_content(port->fm_port, + &port->buff_prefix_content); + + if (fm_port_init(port->fm_port) != 0) { + pr_err("fm_port_init() failed\n"); + return -EINVAL; + } + +/* FMan Fifo sizes "behind the scene": + * Using the following formulae (*), under a set of simplifying assumptions (.): + * . all ports are configured in Normal Mode (rather than Independent Mode) + * . the DPAA Eth driver allocates buffers of size: + * . MAXFRM + NET_IP_ALIGN + DPA_PRIV_DATA_SIZE + DPA_PARSE_RESULTS_SIZE + * + DPA_HASH_RESULTS_SIZE, i.e.: + * MAXFRM + 2 + 16 + sizeof(fm_prs_result_t) + 16, i.e.: + * MAXFRM + 66 + * . excessive buffer pools not accounted for + * + * for Rx ports on P4080: + * . IFSZ = ceil(max(FMBM_EBMPI[PBS]) / 256)*256 + 7*256 + * . no internal frame offset (FMBM_RIM[FOF] == 0) - otherwise, + * add up to 256 to the above + * + * for Tx ports: + * . IFSZ = ceil(frame_size / 256)*256 + 3*256 + * + FMBM_TFP[DPDE]*256, i.e.: + * IFSZ = ceil(MAXFRM / 256)*256 + 3 x 256 + FMBM_TFP[DPDE]*256 + * + * for P4080: + * . (conservative decisions, assuming that BMI must bring the entire + * frame, not only the frame header) + * . no internal frame offset (FMBM_OIM[FOF] == 0) - otherwise, + * add up to 256 to the above + * + * . for P4080/P5020/P3041/P2040, DPDE is: + * > 0 or 1, for 1Gb ports, HW default: 0 + * > 2..7 (recommended: 3..7) for 10Gb ports, HW default: 3 + * + * . for P4080, MXT is in range (0..63) + * + */ + return 0; +} + +void fm_set_rx_port_params(struct fm_port_drv_t *port, + struct fm_port_params *params) +{ + int i; + + port->port_params.specific_params.rx_params.err_fqid = params->errq; + port->port_params.specific_params.rx_params.dflt_fqid = params->defq; + port->port_params.specific_params.rx_params. + ext_buf_pools.num_of_pools_used = params->num_pools; + for (i = 0; i < params->num_pools; i++) { + port->port_params.specific_params.rx_params.ext_buf_pools. + ext_buf_pool[i].id = params->pool_param[i].id; + port->port_params.specific_params.rx_params.ext_buf_pools. + ext_buf_pool[i].size = params->pool_param[i].size; + } + + port->buff_prefix_content.priv_data_size = params->priv_data_size; + port->buff_prefix_content.pass_prs_result = params->parse_results; + port->buff_prefix_content.pass_hash_result = params->hash_results; + port->buff_prefix_content.pass_time_stamp = params->time_stamp; + port->buff_prefix_content.data_align = params->data_align; + + init_fm_port_dev(port); +} +EXPORT_SYMBOL(fm_set_rx_port_params); + +void fm_set_tx_port_params(struct fm_port_drv_t *port, + struct fm_port_params *params) +{ + port->port_params.specific_params.non_rx_params.err_fqid = + params->errq; + port->port_params.specific_params.non_rx_params.dflt_fqid = + params->defq; + + port->buff_prefix_content.priv_data_size = params->priv_data_size; + port->buff_prefix_content.pass_prs_result = params->parse_results; + port->buff_prefix_content.pass_hash_result = params->hash_results; + port->buff_prefix_content.pass_time_stamp = params->time_stamp; + port->buff_prefix_content.data_align = params->data_align; + + init_fm_port_dev(port); +} +EXPORT_SYMBOL(fm_set_tx_port_params); + +static int fm_port_probe(struct platform_device *of_dev) +{ + struct fm_port_drv_t *port; + struct fm_drv_t *fm_drv; + struct device *dev; + + dev = &of_dev->dev; + + port = read_fm_port_dev_tree_node(of_dev); + if (!port) + return -EIO; + + if (!port->active) + return 0; + + if (configure_fm_port_dev(port) != 0) + return -EIO; + + dev_set_drvdata(dev, port); + + fm_drv = (struct fm_drv_t *)port->fm; + + if (port->port_params.port_type == FM_PORT_TYPE_RX) { + snprintf(port->name, sizeof(port->name), + "%s-port-rx%d", fm_drv->name, port->id); + } else if (port->port_params.port_type == FM_PORT_TYPE_TX) { + snprintf(port->name, sizeof(port->name), + "%s-port-tx%d", fm_drv->name, port->id); + } + + /* FM_TX_INVALID_ECC_ERRATA_10GMAC_A009 Errata workaround */ + if (port->fm_rev_info.major_rev < 6) + fm_disable_rams_ecc(fm_drv->fm_dev); + + pr_debug("%s probed\n", port->name); + + return 0; +} + +static const struct of_device_id fm_port_match[] = { + {.compatible = "fsl,fman-v3-port-rx"}, + {.compatible = "fsl,fman-v2-port-rx"}, + {.compatible = "fsl,fman-v3-port-tx"}, + {.compatible = "fsl,fman-v2-port-tx"}, + {} +}; + +MODULE_DEVICE_TABLE(of, fm_port_match); + +static struct platform_driver fm_port_driver = { + .driver = { + .name = "fsl-fman-port", + .of_match_table = fm_port_match, + }, + .probe = fm_port_probe, +}; + +builtin_platform_driver(fm_port_driver); + +static int __init __cold fm_port_load(void) +{ + if (platform_driver_register(&fm_port_driver)) { + pr_crit("platform_driver_register() failed\n"); + return -ENODEV; + } + + pr_info("Freescale FMan Ports module\n"); + + return 0; +} + +device_initcall(fm_port_load); diff --git a/drivers/net/ethernet/freescale/fman/inc/fm_port_ext.h b/drivers/net/ethernet/freescale/fman/inc/fm_port_ext.h new file mode 100644 index 0000000..56c83b5 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/inc/fm_port_ext.h @@ -0,0 +1,340 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. + */ +#ifndef __FM_PORT_EXT +#define __FM_PORT_EXT + +#include "fm_ext.h" + +/* FM Port API + * The FM uses a general module called "port" to represent a Tx port (MAC), + * an Rx port (MAC) or Offline Parsing port. + * The number of ports in an FM varies between SOCs. + * The SW driver manages these ports as sub-modules of the FM,i.e. after an + * FM is initialized, its ports may be initialized and operated upon. + * The port is initialized aware of its type, but other functions on a port + * may be indifferent to its type. When necessary, the driver verifies + * coherence and returns error if applicable. + * On initialization, user specifies the port type and it's index (relative + * to the port's type) - always starting at 0. + */ + +/* General FM Port defines */ +/* Number of 4 bytes words in parser result */ +#define FM_PORT_PRS_RESULT_NUM_OF_WORDS 8 + +/* FM Frame error */ +/* Frame Descriptor errors */ +/* Not for Rx-Port! Unsupported Format */ +#define FM_PORT_FRM_ERR_UNSUPPORTED_FORMAT FM_FD_ERR_UNSUPPORTED_FORMAT +/* Not for Rx-Port! Length Error */ +#define FM_PORT_FRM_ERR_LENGTH FM_FD_ERR_LENGTH +/* DMA Data error */ +#define FM_PORT_FRM_ERR_DMA FM_FD_ERR_DMA +/* non Frame-Manager error; probably come from SEC that was chained to FM */ +#define FM_PORT_FRM_ERR_NON_FM FM_FD_RX_STATUS_ERR_NON_FM + /* IPR error */ +#define FM_PORT_FRM_ERR_IPRE (FM_FD_ERR_IPR & ~FM_FD_IPR) +/* IPR non-consistent-sp */ +#define FM_PORT_FRM_ERR_IPR_NCSP (FM_FD_ERR_IPR_NCSP & \ + ~FM_FD_IPR) + +/* Rx FIFO overflow, FCS error, code error, running disparity + * error (SGMII and TBI modes), FIFO parity error. + * PHY Sequence error, PHY error control character detected. + */ +#define FM_PORT_FRM_ERR_PHYSICAL FM_FD_ERR_PHYSICAL +/* Frame too long OR Frame size exceeds max_length_frame */ +#define FM_PORT_FRM_ERR_SIZE FM_FD_ERR_SIZE +/* indicates a classifier "drop" operation */ +#define FM_PORT_FRM_ERR_CLS_DISCARD FM_FD_ERR_CLS_DISCARD +/* Extract Out of Frame */ +#define FM_PORT_FRM_ERR_EXTRACTION FM_FD_ERR_EXTRACTION +/* No Scheme Selected */ +#define FM_PORT_FRM_ERR_NO_SCHEME FM_FD_ERR_NO_SCHEME +/* Keysize Overflow */ +#define FM_PORT_FRM_ERR_KEYSIZE_OVERFLOW FM_FD_ERR_KEYSIZE_OVERFLOW +/* Frame color is red */ +#define FM_PORT_FRM_ERR_COLOR_RED FM_FD_ERR_COLOR_RED +/* Frame color is yellow */ +#define FM_PORT_FRM_ERR_COLOR_YELLOW FM_FD_ERR_COLOR_YELLOW +/* Parser Time out Exceed */ +#define FM_PORT_FRM_ERR_PRS_TIMEOUT FM_FD_ERR_PRS_TIMEOUT +/* Invalid Soft Parser instruction */ +#define FM_PORT_FRM_ERR_PRS_ILL_INSTRUCT FM_FD_ERR_PRS_ILL_INSTRUCT +/* Header error was identified during parsing */ +#define FM_PORT_FRM_ERR_PRS_HDR_ERR FM_FD_ERR_PRS_HDR_ERR +/* Frame parsed beyind 256 first bytes */ +#define FM_PORT_FRM_ERR_BLOCK_LIMIT_EXCEEDED FM_FD_ERR_BLOCK_LIMIT_EXCEEDED +/* FPM Frame Processing Timeout Exceeded */ +#define FM_PORT_FRM_ERR_PROCESS_TIMEOUT 0x00000001 + +/* FM Port Initialization Unit */ +struct fm_port_t; + +/* A structure for additional Rx port parameters */ +struct fm_port_rx_params_t { + u32 err_fqid; /* Error Queue Id. */ + u32 dflt_fqid; /* Default Queue Id. */ + u16 liodn_offset; /* Port's LIODN offset. */ + /* Which external buffer pools are used + * (up to FM_PORT_MAX_NUM_OF_EXT_POOLS), and their sizes. + */ + struct fm_ext_pools_t ext_buf_pools; +}; + +/* A structure for additional non-Rx port parameters */ +struct fm_port_non_rx_params_t { + /* Error Queue Id. */ + u32 err_fqid; + /* For Tx - Default Confirmation queue, 0 means no Tx confirmation + * for processed frames. For OP port - default Rx queue. + */ + u32 dflt_fqid; + /* QM-channel dedicated to this port; will be used + * by the FM for dequeue. + */ + u32 qm_channel; +}; + +/* A union for additional parameters depending on port type */ +union fm_port_specific_params_u { + /* Rx port parameters structure */ + struct fm_port_rx_params_t rx_params; + /* Non-Rx port parameters structure */ + struct fm_port_non_rx_params_t non_rx_params; +}; + +/* A structure representing FM initialization parameters */ +struct fm_port_params_t { + void __iomem *base_addr; + /* Virtual Address of memory mapped FM Port registers. */ + void *fm; + /* A handle to the FM object this port related to */ + enum fm_port_type port_type; + /* Port type */ + enum fm_port_speed port_speed; + /* Port speed */ + u8 port_id; + /* Port Id - relative to type; + * NOTE: When configuring Offline Parsing port for FMANv3 devices, + * it is highly recommended NOT to use port_id=0 due to lack of HW + * resources on port_id=0. + */ + u16 liodn_base; + /* Irrelevant for P4080 rev 1. LIODN base for this port, to be + * used together with LIODN offset. + */ + union fm_port_specific_params_u specific_params; + /* Additional parameters depending on port type. */ +}; + +struct fm_port; + +/** + * fm_port_config + * @fm_port_params: Pointer to data structure of parameters + * + * Creates a descriptor for the FM PORT module. + * The routine returns a handle (descriptor) to the FM PORT object. + * This descriptor must be passed as first parameter to all other FM PORT + * function calls. + * No actual initialization or configuration of FM hardware is done by this + * routine. + * + * Return: Pointer to FM Port object, or NULL for Failure. + */ +struct fm_port_t *fm_port_config(struct fm_port_params_t *fm_port_params); + +/** + * fm_port_init + * fm_port: A pointer to a FM Port module. + * Initializes the FM PORT module by defining the software structure and + * configuring the hardware registers. + * + * Return: 0 on success; Error code otherwise. + */ +int fm_port_init(struct fm_port_t *fm_port); + +/* enum for defining QM frame dequeue */ +enum fm_port_deq_type { + /* Dequeue from the SP channel - with priority precedence, + * and Intra-Class Scheduling respected. + */ + FM_PORT_DEQ_TYPE1, + /* Dequeue from the SP channel - with active FQ precedence, + * and Intra-Class Scheduling respected. + */ + FM_PORT_DEQ_TYPE2, + /* Dequeue from the SP channel - with active FQ precedence, + * and override Intra-Class Scheduling + */ + FM_PORT_DEQ_TYPE3 +}; + +/* enum for defining QM frame dequeue */ +enum fm_port_deq_prefetch_option { + /* QMI preforms a dequeue action for a single frame only + * when a dedicated portID Tnum is waiting. + */ + FM_PORT_DEQ_NO_PREFETCH, + /* QMI preforms a dequeue action for 3 frames when one + * dedicated port_id tnum is waiting. + */ + FM_PORT_DEQ_PARTIAL_PREFETCH, + /* QMI preforms a dequeue action for 3 frames when + * no dedicated port_id tnums are waiting. + */ + FM_PORT_DEQ_FULL_PREFETCH +}; + +/* enum for defining port default color */ +enum fm_port_color { + FM_PORT_COLOR_GREEN, /* Default port color is green */ + FM_PORT_COLOR_YELLOW, /* Default port color is yellow */ + FM_PORT_COLOR_RED, /* Default port color is red */ + FM_PORT_COLOR_OVERRIDE /* Ignore color */ +}; + +/* A structure for defining FM port resources */ +struct fm_port_rsrc_t { + u32 num; /* Committed required resource */ + u32 extra; /* Extra (not committed) required resource */ +}; + +/** + * fm_port_cfg_deq_high_priority + * fm_port: A pointer to a FM Port module. + * @high_pri: True to select high priority, false for normal operation. + * + * Calling this routine changes the dequeue priority in the internal driver + * data base from its default configuration (DFLT_PORT_DEQ_HIGH_PRIORITY) + * May be used for Non-Rx ports only + * + * Allowed only following fm_port_config() and before fm_port_init(). + * + * Return: 0 on success; Error code otherwise. + */ +int fm_port_cfg_deq_high_priority(struct fm_port_t *fm_port, bool high_pri); + +/** + * fm_port_cfg_deq_prefetch_option + * fm_port: A pointer to a FM Port module. + * @deq_prefetch_option: New option + * Calling this routine changes the dequeue prefetch option parameter in the + * internal driver data base from its default configuration: + * [DEFAULT_PORT_DEQ_PREFETCH_OPT] + * Note: Available for some chips only May be used for Non-Rx ports only + * + * Allowed only following fm_port_config() and before fm_port_init(). + * + * Return: 0 on success; Error code otherwise. + */ +int fm_port_cfg_deq_prefetch_option(struct fm_port_t *fm_port, + enum fm_port_deq_prefetch_option + deq_prefetch_option); + +/** + * fm_port_cfg_buf_prefix_content + * fm_port A pointer to a FM Port module. + * @fm_buffer_prefix_content A structure of parameters describing + * the structure of the buffer. + * Out parameter: + * Start margin - offset of data from + * start of external buffer. + * Defines the structure, size and content of the application buffer. + * The prefix, in Tx ports, if 'pass_prs_result', the application should set + * a value to their offsets in the prefix of the FM will save the first + * 'priv_data_size', than, depending on 'pass_prs_result' and + * 'pass_time_stamp', copy parse result and timeStamp, and the packet itself + * (in this order), to the application buffer, and to offset. + * Calling this routine changes the buffer margins definitions in the internal + * driver data base from its default configuration: + * Data size: [DEFAULT_PORT_BUFFER_PREFIX_CONTENT_PRIV_DATA_SIZE] + * Pass Parser result: [DEFAULT_PORT_BUFFER_PREFIX_CONTENT_PASS_PRS_RESULT]. + * Pass timestamp: [DEFAULT_PORT_BUFFER_PREFIX_CONTENT_PASS_TIME_STAMP]. + * May be used for all ports + * + * Allowed only following fm_port_config() and before fm_port_init(). + * + * Return: 0 on success; Error code otherwise. + */ +int fm_port_cfg_buf_prefix_content(struct fm_port_t *fm_port, + struct fm_buffer_prefix_content_t + *fm_buffer_prefix_content); + +/** + * fm_port_cfg_bcb_wa + * fm_port A pointer to a FM Port module. + * + * BCB errata workaround. + * When BCB errata is applicable, the workaround is always performed by FM + * Controller. Thus, this functions doesn't actually enable errata workaround + * but rather allows driver to perform adjustments required due to errata + * workaround execution in FM controller. + * Applying BCB workaround also configures FM_PORT_FRM_ERR_PHYSICAL errors + * to be discarded. + * + * Allowed only following fm_port_config() and before fm_port_init(). + * + * Return: 0 on success; Error code otherwise. + */ +int fm_port_cfg_bcb_wa(struct fm_port_t *fm_port); + +/** + * fm_port_disable + * fm_port A pointer to a FM Port module. + * + * Gracefully disable an FM port. The port will not start new tasks after all + * tasks associated with the port are terminated. + * + * This is a blocking routine, it returns after port is gracefully stopped, + * i.e. the port will not except new frames, but it will finish all frames + * or tasks which were already began. + * Allowed only following fm_port_init(). + * + * Return: 0 on success; Error code otherwise. + */ +int fm_port_disable(struct fm_port_t *fm_port); + +/** + * fm_port_enable + * fm_port: A pointer to a FM Port module. + * + * A runtime routine provided to allow disable/enable of port. + * + * Allowed only following fm_port_init(). + * + * Return: 0 on success; Error code otherwise. + */ +int fm_port_enable(struct fm_port_t *fm_port); + +#endif /* __FM_PORT_EXT */ diff --git a/drivers/net/ethernet/freescale/fman/inc/fsl_fman_drv.h b/drivers/net/ethernet/freescale/fman/inc/fsl_fman_drv.h index c96cfd1..d849bc2 100644 --- a/drivers/net/ethernet/freescale/fman/inc/fsl_fman_drv.h +++ b/drivers/net/ethernet/freescale/fman/inc/fsl_fman_drv.h @@ -40,6 +40,40 @@ /* FM device opaque structure used for type checking */ struct fm; +/* FMan Port structure .., */ +struct fm_port_t; + +/* A structure of information about each of the external + * buffer pools used by the port, + */ +struct fm_port_pool_param { + u8 id; /* External buffer pool id */ + u16 size; /* External buffer pool buffer size */ +}; + +/* structure for additional port parameters */ +struct fm_port_params { + u32 errq; /* Error Queue Id. */ + u32 defq; /* For Tx and HC - Default Confirmation queue, + * 0 means no Tx conf for processed frames. + * For Rx and OP - default Rx queue. + */ + u8 num_pools; /* Number of pools use by this port */ + struct fm_port_pool_param pool_param[FM_PORT_MAX_NUM_OF_EXT_POOLS]; + /* Parameters for each pool */ + u16 priv_data_size; + /* Area that user may save for his own + * need (E.g. save the SKB) + */ + bool parse_results; /* Put the parser-results in the Rx/Tx buffer */ + bool hash_results; /* Put the hash-results in the Rx/Tx buffer */ + bool time_stamp; /* Put the time-stamp in the Rx/Tx buffer */ + u16 data_align; + /* value for selecting a data alignment (must be a power of 2); + * if write optimization is used, must be >= 16. + */ +}; + /** * fm_bind * @fm_dev: the OF handle of the FM device. @@ -82,6 +116,76 @@ void *fm_get_handle(struct fm *fm); */ struct resource *fm_get_mem_region(struct fm *fm); + +/** + * fm_port_bind + * @fm_port_dev: The OF handle of the FM port device. + * + * Bind to a specific FM-port device (may be Rx or Tx port). + * + * Allowed only after the port was created. + * + * Return: A handle of the FM port device. + */ +struct fm_port_drv_t *fm_port_bind(struct device *fm_port_dev); + +/** + * fm_set_rx_port_params + * @port: A handle of the FM port device. + * @params: Rx port parameters + * + * Configure parameters for a specific Rx FM-port device. + * + * Allowed only after the port is binded. + */ +void fm_set_rx_port_params(struct fm_port_drv_t *port, + struct fm_port_params *params); + +/** + * fm_port_get_buff_layout_ext_params + * @port: A handle of the FM port device. + * @params: PCD port parameters + * + * Get data_align from the device tree chosen node if applied. + * This function will only update these two parameters. + * When this port has no such parameters in the device tree + * values will be set to 0. + * + * Allowed only after the port is binded. + */ +void fm_port_get_buff_layout_ext_params(struct fm_port_drv_t *port, + struct fm_port_params *params); + +/** + * fm_get_tx_port_channel + * @port: A handle of the FM port device. + * + * Get qman-channel number for this Tx port. + * Allowed only after the port is binded. + * + * Return: qman-channel number for this Tx port. + */ +int fm_get_tx_port_channel(struct fm_port_drv_t *port); + +/** + * fm_set_tx_port_params + * @port: A handle of the FM port device. + * @params: Tx port parameters + * + * Configure parameters for a specific Tx FM-port device + * + * Allowed only after the port is binded. + */ +void fm_set_tx_port_params(struct fm_port_drv_t *port, + struct fm_port_params *params); +/** + * fm_port_drv_handle + * @port: A handle of the FM port device. + * + * Return: A pointer to the internal FM Port structure + */ +struct fm_port_t *fm_port_drv_handle(const struct fm_port_drv_t *port); + /** * fm_get_max_frm * diff --git a/drivers/net/ethernet/freescale/fman/port/Makefile b/drivers/net/ethernet/freescale/fman/port/Makefile index 54b1fa4..55825e3 100644 --- a/drivers/net/ethernet/freescale/fman/port/Makefile +++ b/drivers/net/ethernet/freescale/fman/port/Makefile @@ -1,3 +1,3 @@ obj-y += fsl_fman_port.o -fsl_fman_port-objs := fman_port.o +fsl_fman_port-objs := fman_port.o fm_port.o diff --git a/drivers/net/ethernet/freescale/fman/port/fm_port.c b/drivers/net/ethernet/freescale/fman/port/fm_port.c new file mode 100644 index 0000000..ff32cd9 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/port/fm_port.c @@ -0,0 +1,1081 @@ +/* + * Copyright 2008 - 2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "fm_muram_ext.h" + +#include "fm_port.h" + +#include +#include + +static int check_init_parameters(struct fm_port_t *fm_port) +{ + struct fm_port_drv_param_t *params = fm_port->fm_port_drv_param; + struct fman_port_cfg *dflt_config = ¶ms->dflt_cfg; + u32 unused_mask; + + /* Rx only */ + if (fm_port->port_type == FM_PORT_TYPE_RX) { + /* external buffer pools */ + if (!params->ext_buf_pools.num_of_pools_used) { + pr_err("ext_buf_pools.num_of_pools_used=0. At least one buffer pool must be defined\n"); + return -EINVAL; + } + + if (fm_sp_check_buf_pools_params(¶ms->ext_buf_pools, + params->backup_bm_pools, + ¶ms->buf_pool_depletion, + fm_port->port_intg->max_num_of_ext_pools, + fm_port->port_intg->bm_max_num_of_pools) != 0) + return -EINVAL; + /* Check that part of IC that needs copying is small enough + * to enter start margin + */ + if (params->int_context.size && + (params->int_context.size + + params->int_context.ext_buf_offset > + params->buf_margins.start_margins)) { + pr_err("int_context.size is larger than start margins\n"); + return -EINVAL; + } + + if ((params->liodn_offset != LIODN_DONT_OVERRIDE) && + (params->liodn_offset & ~FM_LIODN_OFFSET_MASK)) { + pr_err("liodn_offset is larger than %d\n", + FM_LIODN_OFFSET_MASK + 1); + } + + if (fm_port->fm_rev_info.major_rev < 6) + if (fm_port->fm_port_drv_param->backup_bm_pools) { + pr_err("Backup Bm Pools\n"); + return -EINVAL; + } + } else { + /* Non Rx ports */ + if (params->deq_sub_portal >= + fm_port->port_intg->fm_max_num_of_sub_portals) { + pr_err("deq_sub_portal has to be in the range of 0 - %d\n", + fm_port->port_intg->fm_max_num_of_sub_portals); + return -EINVAL; + } + + /* to protect HW internal-context from overwrite */ + if ((params->int_context.size) && + (params->int_context.int_context_offset < + MIN_TX_INT_OFFSET)) { + pr_err("non-Rx int_context.int_context_offset can't be smaller than %d\n", + MIN_TX_INT_OFFSET); + return -EINVAL; + } + + if ((fm_port->port_type == FM_PORT_TYPE_TX) || + /* in O/H DFLT_NOT_SUPPORTED indicates that + * it is not supported and should not be checked + */ + (fm_port->fm_port_drv_param->dflt_cfg. + tx_fifo_deq_pipeline_depth != DFLT_NOT_SUPPORTED)) { + /* Check that not larger than 8 */ + if ((!fm_port->fm_port_drv_param->dflt_cfg. + tx_fifo_deq_pipeline_depth) || + (fm_port->fm_port_drv_param->dflt_cfg. + tx_fifo_deq_pipeline_depth > + MAX_FIFO_PIPELINE_DEPTH)) { + pr_err("fifo_deq_pipeline_depth can't be larger than %d\n", + MAX_FIFO_PIPELINE_DEPTH); + return -EINVAL; + } + } + } + + /* Rx */ + if (fm_port->port_type == FM_PORT_TYPE_RX) { + if (!params->dflt_fqid) { + pr_err("dflt_fqid must be between 1 and 2^24-1\n"); + return -EINVAL; + } + } + + /* All ports */ + /* common BMI registers values */ + /* Check that Queue Id is not larger than 2^24, and is not 0 */ + if ((params->err_fqid & ~0x00FFFFFF) || !params->err_fqid) { + pr_err("err_fqid must be between 1 and 2^24-1\n"); + return -EINVAL; + } + if (params->dflt_fqid & ~0x00FFFFFF) { + pr_err("dflt_fqid must be between 1 and 2^24-1\n"); + return -EINVAL; + } + + /* Rx only */ + if (fm_port->port_type == FM_PORT_TYPE_RX) { + if (dflt_config->rx_pri_elevation % BMI_FIFO_UNITS) { + pr_err("rx_fifo_pri_elevation_level has to be divisible by %d\n", + BMI_FIFO_UNITS); + return -EINVAL; + } + if ((dflt_config->rx_pri_elevation < BMI_FIFO_UNITS) || + (dflt_config->rx_pri_elevation > + fm_port->port_intg->max_port_fifo_size)) { + pr_err("rx_fifo_pri_elevation_level not in range of 256 - %d\n", + fm_port->port_intg->max_port_fifo_size); + return -EINVAL; + } + if (dflt_config->rx_fifo_thr % BMI_FIFO_UNITS) { + pr_err("rx_fifo_threshold must be div by %d\n", + BMI_FIFO_UNITS); + return -EINVAL; + } + if ((dflt_config->rx_fifo_thr < BMI_FIFO_UNITS) || + (dflt_config->rx_fifo_thr > + fm_port->port_intg->max_port_fifo_size)) { + pr_err("rx_fifo_threshold has to be in the range of 256 - %d\n", + fm_port->port_intg->max_port_fifo_size); + return -EINVAL; + } + + /* Check that not larger than 16 */ + if (dflt_config->rx_cut_end_bytes > FRAME_END_DATA_SIZE) { + pr_err("cut_bytes_from_end can't be larger than %d\n", + FRAME_END_DATA_SIZE); + return -EINVAL; + } + + if (fm_sp_check_buf_margins(¶ms->buf_margins) != 0) + return -EINVAL; + + /* extra FIFO size (allowed only to Rx ports) */ + if (params->set_size_of_fifo && + (fm_port->fifo_bufs.extra % BMI_FIFO_UNITS)) { + pr_err("fifo_bufs.extra has to be divisible by %d\n", + BMI_FIFO_UNITS); + return -EINVAL; + } + + if (params->buf_pool_depletion.pools_grp_mode_enable && + !params->buf_pool_depletion.num_of_pools) { + pr_err("buf_pool_depletion.num_of_pools can not be 0 when pools_grp_mode_enable=true\n"); + return -EINVAL; + } + } + + /* Non Rx ports */ + /* extra FIFO size (allowed only to Rx ports) */ + else if (fm_port->fifo_bufs.extra) { + pr_err(" No fifo_bufs.extra for non Rx ports\n"); + return -EINVAL; + } + + /* Tx only */ + if (fm_port->port_type == FM_PORT_TYPE_TX) { + if (dflt_config->tx_fifo_min_level % BMI_FIFO_UNITS) { + pr_err("tx_fifo_min_fill_level has to be divisible by %d\n", + BMI_FIFO_UNITS); + return -EINVAL; + } + if (dflt_config->tx_fifo_min_level > + (fm_port->port_intg->max_port_fifo_size - 256)) { + pr_err("tx_fifo_min_fill_level has to be in the range of 0 - %d\n", + (fm_port->port_intg->max_port_fifo_size - 256)); + return -EINVAL; + } + if (dflt_config->tx_fifo_low_comf_level % BMI_FIFO_UNITS) { + pr_err("tx_fifo_low_comf_level has to be divisible by %d\n", + BMI_FIFO_UNITS); + return -EINVAL; + } + if ((dflt_config->tx_fifo_low_comf_level < BMI_FIFO_UNITS) || + (dflt_config->tx_fifo_low_comf_level > + fm_port->port_intg->max_port_fifo_size)) { + pr_err("tx_fifo_low_comf_level has to be in the range of 256 - %d\n", + fm_port->port_intg->max_port_fifo_size); + return -EINVAL; + } + if (fm_port->port_speed == FM_PORT_SPEED_1G) + if (fm_port->fm_port_drv_param->dflt_cfg. + tx_fifo_deq_pipeline_depth > 2) { + pr_err("fifoDeqPipelineDepth for 1G can't be larger than 2\n"); + return -EINVAL; + } + } + + /* Non Tx Ports */ + /* If discard override was selected , no frames may be discarded. */ + else if (dflt_config->discard_override && params->errors_to_discard) { + pr_err("errors_to_discard is not empty, but frm_discard_override selected (all discarded frames to be enqueued to error queue).\n"); + return -EINVAL; + } + + /* Rx */ + if (fm_port->port_type == FM_PORT_TYPE_RX) { + unused_mask = BMI_STATUS_RX_MASK_UNUSED; + + /* Check that no common bits with BMI_STATUS_MASK_UNUSED */ + if (params->errors_to_discard & unused_mask) { + pr_err("errors_to_discard contains undefined bits\n"); + return -EINVAL; + } + } + + /* All ports */ + /* Check that not larger than 16 */ + if ((params->cheksum_last_bytes_ignore > FRAME_END_DATA_SIZE) && + ((params->cheksum_last_bytes_ignore != DFLT_NOT_SUPPORTED))) { + pr_err("cheksum_last_bytes_ignore can't be larger than %d\n", + FRAME_END_DATA_SIZE); + return -EINVAL; + } + + if (fm_sp_check_int_context_params(¶ms->int_context) != 0) + return -EINVAL; + + /* common BMI registers values */ + if (params->set_num_of_tasks && + ((!fm_port->tasks.num) || + (fm_port->tasks.num > MAX_NUM_OF_TASKS))) { + pr_err("tasks.num can't be larger than %d\n", + MAX_NUM_OF_TASKS); + return -EINVAL; + } + if (params->set_num_of_tasks && + (fm_port->tasks.extra > MAX_NUM_OF_EXTRA_TASKS)) { + pr_err("tasks.extra can't be larger than %d\n", + MAX_NUM_OF_EXTRA_TASKS); + return -EINVAL; + } + if (params->set_num_of_open_dmas && + ((!fm_port->open_dmas.num) || + (fm_port->open_dmas.num > MAX_NUM_OF_DMAS))) { + pr_err("open_dmas.num can't be larger than %d\n", + MAX_NUM_OF_DMAS); + return -EINVAL; + } + if (params->set_num_of_open_dmas && + (fm_port->open_dmas.extra > MAX_NUM_OF_EXTRA_DMAS)) { + pr_err("open_dmas.extra can't be larger than %d\n", + MAX_NUM_OF_EXTRA_DMAS); + return -EINVAL; + } + if (params->set_size_of_fifo && + (!fm_port->fifo_bufs.num || (fm_port->fifo_bufs.num > + fm_port->port_intg->max_port_fifo_size))) { + pr_err("fifo_bufs.num has to be in the range of 256 - %d\n", + fm_port->port_intg->max_port_fifo_size); + return -EINVAL; + } + if (params->set_size_of_fifo && + (fm_port->fifo_bufs.num % BMI_FIFO_UNITS)) { + pr_err("fifo_bufs.num has to be divisible by %d\n", + BMI_FIFO_UNITS); + return -EINVAL; + } + + return 0; +} + +static bool is_init_done(struct fm_port_drv_param_t *fm_port_drv_params) +{ + /* Checks if FMan port driver parameters were initialized */ + if (!fm_port_drv_params) + return true; + + return false; +} + +static int verify_size_of_fifo(struct fm_port_t *fm_port) +{ + u32 min_fifo_size_required = 0, opt_fifo_size_for_b2b = 0; + + /* TX Ports */ + if (fm_port->port_type == FM_PORT_TYPE_TX) { + min_fifo_size_required = (u32) + (roundup(fm_port->max_frame_length, + BMI_FIFO_UNITS) + (3 * BMI_FIFO_UNITS)); + + min_fifo_size_required += + fm_port->fm_port_drv_param-> + dflt_cfg.tx_fifo_deq_pipeline_depth * BMI_FIFO_UNITS; + + opt_fifo_size_for_b2b = min_fifo_size_required; + + /* Add some margin for back-to-back capability to improve + * performance, allows the hardware to pipeline new frame dma + * while the previous frame not yet transmitted. + */ + if (fm_port->port_speed == FM_PORT_SPEED_10G) + opt_fifo_size_for_b2b += 3 * BMI_FIFO_UNITS; + else + opt_fifo_size_for_b2b += 2 * BMI_FIFO_UNITS; + } + + /* RX Ports */ + else if (fm_port->port_type == FM_PORT_TYPE_RX) { + if (fm_port->fm_rev_info.major_rev >= 6) + min_fifo_size_required = (u32) + (roundup(fm_port->max_frame_length, + BMI_FIFO_UNITS) + (5 * BMI_FIFO_UNITS)); + /* 4 according to spec + 1 for FOF>0 */ + else + min_fifo_size_required = (u32) + (roundup(min(fm_port->max_frame_length, + fm_port->rx_pools_params.largest_buf_size), + BMI_FIFO_UNITS) + (7 * BMI_FIFO_UNITS)); + + opt_fifo_size_for_b2b = min_fifo_size_required; + + /* Add some margin for back-to-back capability to improve + * performance,allows the hardware to pipeline new frame dma + * while the previous frame not yet transmitted. + */ + if (fm_port->port_speed == FM_PORT_SPEED_10G) + opt_fifo_size_for_b2b += 8 * BMI_FIFO_UNITS; + else + opt_fifo_size_for_b2b += 3 * BMI_FIFO_UNITS; + } + + BUG_ON(min_fifo_size_required <= 0); + BUG_ON(opt_fifo_size_for_b2b < min_fifo_size_required); + + /* Verify the size */ + if (fm_port->fifo_bufs.num < min_fifo_size_required) + pr_debug("FIFO size should be enlarged to %d bytes\n", + min_fifo_size_required); + else if (fm_port->fifo_bufs.num < opt_fifo_size_for_b2b) + pr_debug("For b2b processing,FIFO may be enlarged to %d bytes\n", + opt_fifo_size_for_b2b); + + return 0; +} + +static void fm_port_drv_param_free(struct fm_port_t *fm_port) +{ + kfree(fm_port->fm_port_drv_param); + fm_port->fm_port_drv_param = NULL; +} + +static int set_ext_buffer_pools(struct fm_port_t *fm_port) +{ + struct fm_ext_pools_t *ext_buf_pools = + &fm_port->fm_port_drv_param->ext_buf_pools; + struct fm_buf_pool_depletion_t *buf_pool_depletion = + &fm_port->fm_port_drv_param->buf_pool_depletion; + u8 ordered_array[FM_PORT_MAX_NUM_OF_EXT_POOLS]; + u16 sizes_array[BM_MAX_NUM_OF_POOLS]; + int i = 0, j = 0, err; + struct fman_port_bpools bpools; + + memset(&ordered_array, 0, sizeof(u8) * FM_PORT_MAX_NUM_OF_EXT_POOLS); + memset(&sizes_array, 0, sizeof(u16) * BM_MAX_NUM_OF_POOLS); + memcpy(&fm_port->ext_buf_pools, ext_buf_pools, + sizeof(struct fm_ext_pools_t)); + + fm_sp_set_buf_pools_in_asc_order_of_buf_sizes(ext_buf_pools, + ordered_array, + sizes_array); + + /* Prepare flibs bpools structure */ + memset(&bpools, 0, sizeof(struct fman_port_bpools)); + bpools.count = ext_buf_pools->num_of_pools_used; + bpools.counters_enable = true; + for (i = 0; i < ext_buf_pools->num_of_pools_used; i++) { + bpools.bpool[i].bpid = ordered_array[i]; + bpools.bpool[i].size = sizes_array[ordered_array[i]]; + /* functionality available only for some derivatives + * (limited by config) + */ + if (fm_port->fm_port_drv_param->backup_bm_pools) + for (j = 0; j < fm_port->fm_port_drv_param-> + backup_bm_pools->num_of_backup_pools; j++) + if (ordered_array[i] == + fm_port->fm_port_drv_param-> + backup_bm_pools->pool_ids[j]) { + bpools.bpool[i].is_backup = true; + break; + } + } + + /* save pools parameters for later use */ + fm_port->rx_pools_params.num_of_pools = + ext_buf_pools->num_of_pools_used; + fm_port->rx_pools_params.largest_buf_size = + sizes_array[ordered_array[ext_buf_pools->num_of_pools_used - 1]]; + fm_port->rx_pools_params.second_largest_buf_size = + sizes_array[ordered_array[ext_buf_pools->num_of_pools_used - 2]]; + + /* FMBM_RMPD reg. - pool depletion */ + if (buf_pool_depletion->pools_grp_mode_enable) { + bpools.grp_bp_depleted_num = buf_pool_depletion->num_of_pools; + for (i = 0; i < fm_port->port_intg->bm_max_num_of_pools; + i++) { + if (buf_pool_depletion->pools_to_consider[i]) { + for (j = 0; j < ext_buf_pools-> + num_of_pools_used; j++) { + if (i == ordered_array[j]) { + bpools.bpool[j]. + grp_bp_depleted = true; + break; + } + } + } + } + } + + if (buf_pool_depletion->single_pool_mode_enable) { + for (i = 0; i < fm_port->port_intg->bm_max_num_of_pools; i++) { + if (buf_pool_depletion-> + pools_to_consider_for_single_mode[i]) { + for (j = 0; j < ext_buf_pools-> + num_of_pools_used; j++) { + if (i == ordered_array[j]) { + bpools.bpool[j]. + single_bp_depleted = true; + break; + } + } + } + } + } + + /* Issue flibs function */ + err = fman_port_set_bpools(&fm_port->port, &bpools); + if (err != 0) { + pr_err("fman_port_set_bpools\n"); + return -EINVAL; + } + + kfree(fm_port->fm_port_drv_param->backup_bm_pools); + + return 0; +} + +static int init_low_level_driver(struct fm_port_t *fm_port) +{ + struct fm_port_drv_param_t *drv_params = fm_port->fm_port_drv_param; + struct fman_port_params port_params; + u32 tmp_val; + + /* Set up flibs parameters and issue init function */ + memset(&port_params, 0, sizeof(struct fman_port_params)); + port_params.discard_mask = drv_params->errors_to_discard; + port_params.dflt_fqid = drv_params->dflt_fqid; + port_params.err_fqid = drv_params->err_fqid; + port_params.deq_sp = drv_params->deq_sub_portal; + port_params.dont_release_buf = drv_params->dont_release_buf; + switch (fm_port->port_type) { + case FM_PORT_TYPE_RX: + port_params.err_mask = + (RX_ERRS_TO_ENQ & ~port_params.discard_mask); + if (drv_params->forward_reuse_int_context) + drv_params->dflt_cfg.rx_fd_bits = + (u8)(BMI_PORT_RFNE_FRWD_RPD >> 24); + break; + default: + break; + } + + tmp_val = (u32)((fm_port->internal_buf_offset % OFFSET_UNITS) ? + (fm_port->internal_buf_offset / OFFSET_UNITS + 1) : + (fm_port->internal_buf_offset / OFFSET_UNITS)); + fm_port->internal_buf_offset = (u8)(tmp_val * OFFSET_UNITS); + drv_params->dflt_cfg.int_buf_start_margin = + fm_port->internal_buf_offset; + drv_params->dflt_cfg.ext_buf_start_margin = + drv_params->buf_margins.start_margins; + drv_params->dflt_cfg.ext_buf_end_margin = + drv_params->buf_margins.end_margins; + + drv_params->dflt_cfg.ic_ext_offset = + drv_params->int_context.ext_buf_offset; + drv_params->dflt_cfg.ic_int_offset = + drv_params->int_context.int_context_offset; + drv_params->dflt_cfg.ic_size = drv_params->int_context.size; + + if (0 != + fman_port_init(&fm_port->port, &drv_params->dflt_cfg, + &port_params)) { + pr_err("fman_port_init\n"); + return -ENODEV; + } + + /* The code bellow is a trick so the FM will not release the buffer + * to BM nor will try to enqueue the frame to QM + */ + if (fm_port->port_type == FM_PORT_TYPE_TX) { + if (!drv_params->dflt_fqid && drv_params->dont_release_buf) { + /* override fmbm_tcfqid 0 with a false non-0 value. + * This will force FM to act according to tfene. + * Otherwise, if fmbm_tcfqid is 0 the FM will release + * buffers to BM regardless of fmbm_tfene + */ + out_be32(&fm_port->port.bmi_regs->tx.fmbm_tcfqid, + 0xFFFFFF); + out_be32(&fm_port->port.bmi_regs->tx.fmbm_tfene, + NIA_ENG_BMI | NIA_BMI_AC_TX_RELEASE); + } + } + + return 0; +} + +static struct fm_port_intg_t *set_port_intg_params(struct fm_port_t *fm_port) +{ + struct fm_port_intg_t *intg; + u32 bmi_max_fifo_size; + + intg = kzalloc(sizeof(*intg), GFP_KERNEL); + if (!intg) + return NULL; + + bmi_max_fifo_size = fm_get_bmi_max_fifo_size(fm_port->fm); + + intg->max_port_fifo_size = + MAX_PORT_FIFO_SIZE(bmi_max_fifo_size); + + switch (fm_port->fm_rev_info.major_rev) { + case FM_IP_BLOCK_P2_P3_P5: + case FM_IP_BLOCK_P4: + intg->max_num_of_ext_pools = 4; + intg->fm_max_num_of_sub_portals = 12; + intg->bm_max_num_of_pools = 64; + break; + + case FM_IP_BLOCK_B_T: + intg->max_num_of_ext_pools = 8; + intg->fm_max_num_of_sub_portals = 16; + intg->bm_max_num_of_pools = 64; + break; + + default: + pr_err("Unsupported FMan version\n"); + kfree(intg); + return NULL; + } + + return intg; +} + +struct fm_port_t *fm_port_config(struct fm_port_params_t *fm_port_params) +{ + struct fm_port_t *fm_port; + void __iomem *base_addr = fm_port_params->base_addr; + enum fman_port_type fman_port_type = E_FMAN_PORT_TYPE_DUMMY; + u32 tmp_reg; + + /* Allocate FM structure */ + fm_port = kzalloc(sizeof(*fm_port), GFP_KERNEL); + if (!fm_port) + return NULL; + + /* Allocate the FM driver's parameters structure */ + fm_port->fm_port_drv_param = + kzalloc(sizeof(*fm_port->fm_port_drv_param), GFP_KERNEL); + if (!fm_port->fm_port_drv_param) + goto err_fm_port_params; + + /* Initialize FM port parameters which will be kept by the driver */ + fm_port->port_type = fm_port_params->port_type; + fm_port->port_speed = fm_port_params->port_speed; + fm_port->port_id = fm_port_params->port_id; + fm_port->fm = fm_port_params->fm; + + /* get FM revision */ + fm_get_revision(fm_port->fm, &fm_port->fm_rev_info); + + fm_port->port_intg = set_port_intg_params(fm_port); + if (!fm_port->port_intg) + goto err_fm_port_intg; + + /* Set up FM port parameters for initialization phase only */ + + /* In order to be aligned with flib port types, we need to translate + * the port type and speed to fman_port_type + */ + if (fm_port->port_type == FM_PORT_TYPE_TX) { + if (fm_port->port_speed == FM_PORT_SPEED_10G) + fman_port_type = E_FMAN_PORT_TYPE_TX_10G; + else + fman_port_type = E_FMAN_PORT_TYPE_TX; + } else if (fm_port->port_type == FM_PORT_TYPE_RX) { + if (fm_port->port_speed == FM_PORT_SPEED_10G) + fman_port_type = E_FMAN_PORT_TYPE_RX_10G; + else + fman_port_type = E_FMAN_PORT_TYPE_RX; + } + fman_port_defconfig(&fm_port->fm_port_drv_param->dflt_cfg, + fman_port_type); + /* Overwrite some integration specific parameters */ + fm_port->fm_port_drv_param->dflt_cfg.rx_pri_elevation = + DFLT_PORT_RX_FIFO_PRI_ELEVATION_LEV( + fm_port->port_intg->max_port_fifo_size); + fm_port->fm_port_drv_param->dflt_cfg.rx_fifo_thr = + fm_port->fm_port_drv_param->rx_fifo_threshold = + DFLT_PORT_RX_FIFO_THRESHOLD(fm_port->fm_rev_info.major_rev, + fm_port->port_intg->max_port_fifo_size); + + fm_port->fm_port_drv_param->dflt_cfg.errata_A006675 = false; + + if ((fm_port->fm_rev_info.major_rev == 6) && + ((fm_port->fm_rev_info.minor_rev == 0) || + (fm_port->fm_rev_info.minor_rev == 3))) + fm_port->fm_port_drv_param->dflt_cfg.errata_A006320 = true; + else + fm_port->fm_port_drv_param->dflt_cfg.errata_A006320 = false; + + /* Excessive Threshold register - exists for pre-FMv3 chips only */ + if (fm_port->fm_rev_info.major_rev < 6) { + fm_port->fm_port_drv_param->dflt_cfg. + excessive_threshold_register = true; + + fm_port->fm_port_drv_param->dflt_cfg.fmbm_rebm_has_sgd = + false; + fm_port->fm_port_drv_param->dflt_cfg.fmbm_tfne_has_features = + false; + } else { + fm_port->fm_port_drv_param->dflt_cfg. + excessive_threshold_register = false; + fm_port->fm_port_drv_param->dflt_cfg.fmbm_rebm_has_sgd = + true; + fm_port->fm_port_drv_param->dflt_cfg.fmbm_tfne_has_features = + true; + } + + fm_port->fm_port_drv_param->dflt_cfg.qmi_deq_options_support = true; + + /* Continue with other parameters */ + fm_port->fm_port_drv_param->base_addr = base_addr; + /* set memory map pointers */ + fm_port->fm_port_bmi_regs = (union fm_port_bmi_regs_u __iomem *) + (base_addr + BMI_PORT_REGS_OFFSET); + + fm_port->fm_port_drv_param->buffer_prefix_content.priv_data_size = + DFLT_PORT_BUFFER_PREFIX_CONTENT_PRIV_DATA_SIZE; + fm_port->fm_port_drv_param->buffer_prefix_content.pass_prs_result = + DFLT_PORT_BUFFER_PREFIX_CONTENT_PASS_PRS_RESULT; + fm_port->fm_port_drv_param->buffer_prefix_content.pass_time_stamp = + DFLT_PORT_BUFFER_PREFIX_CONTENT_PASS_TIME_STAMP; + fm_port->fm_port_drv_param->buffer_prefix_content.data_align = + DFLT_PORT_BUFFER_PREFIX_CONTEXT_DATA_ALIGN; + fm_port->fm_port_drv_param->liodn_base = fm_port_params->liodn_base; + fm_port->fm_port_drv_param->cheksum_last_bytes_ignore = + DFLT_PORT_CHECKSUM_LAST_BYTES_IGNORE; + + fm_port->max_frame_length = DFLT_PORT_MAX_FRAME_LENGTH; + /* resource distribution. */ + + fm_port->fifo_bufs.num = + fm_port_dflt_num_of_fifo_bufs(fm_port->fm_rev_info.major_rev, + fm_port->port_type, + fm_port->port_speed) * BMI_FIFO_UNITS; + fm_port->fifo_bufs.extra = + DFLT_PORT_EXTRA_NUM_OF_FIFO_BUFS * BMI_FIFO_UNITS; + + fm_port->open_dmas.num = + fm_port_dflt_num_of_open_dmas(fm_port->fm_rev_info.major_rev, + fm_port->port_type, + fm_port->port_speed); + fm_port->open_dmas.extra = + fm_port_dflt_extra_num_of_open_dmas(fm_port->fm_rev_info.major_rev, + fm_port->port_type, + fm_port->port_speed); + fm_port->tasks.num = + fm_port_dflt_num_of_tasks(fm_port->fm_rev_info.major_rev, + fm_port->port_type, + fm_port->port_speed); + fm_port->tasks.extra = + fm_port_dflt_extra_num_of_tasks(fm_port->fm_rev_info.major_rev, + fm_port->port_type, + fm_port->port_speed); + + /* FM_HEAVY_TRAFFIC_SEQUENCER_HANG_ERRATA_FMAN_A006981 errata + * workaround + */ + if ((fm_port->fm_rev_info.major_rev == 6) && + (fm_port->fm_rev_info.minor_rev == 0) && + (((fm_port->port_type == FM_PORT_TYPE_TX) && + (fm_port->port_speed == FM_PORT_SPEED_1G)))) { + fm_port->open_dmas.num = 16; + fm_port->open_dmas.extra = 0; + } + + /* Port type specific initialization: */ + switch (fm_port->port_type) { + case FM_PORT_TYPE_RX: + /* Initialize FM port parameters for initialization + * phase only + */ + fm_port->fm_port_drv_param->cut_bytes_from_end = + DFLT_PORT_CUT_BYTES_FROM_END; + fm_port->fm_port_drv_param->en_buf_pool_depletion = false; + fm_port->fm_port_drv_param->frm_discard_override = + DFLT_PORT_FRM_DISCARD_OVERRIDE; + + fm_port->fm_port_drv_param->rx_fifo_pri_elevation_level = + DFLT_PORT_RX_FIFO_PRI_ELEVATION_LEV(fm_port->port_intg-> + max_port_fifo_size); + fm_port->fm_port_drv_param->rx_fifo_threshold = + DFLT_PORT_RX_FIFO_THRESHOLD(fm_port->fm_rev_info.major_rev, + fm_port->port_intg-> + max_port_fifo_size); + + fm_port->fm_port_drv_param->buf_margins.end_margins = + DFLT_PORT_BUF_MARGINS_END_MAARGINS; + fm_port->fm_port_drv_param->errors_to_discard = + DFLT_PORT_ERRORS_TO_DISCARD; + fm_port->fm_port_drv_param->forward_reuse_int_context = + DFLT_PORT_FORWARD_INT_CONTENT_REUSE; + break; + + case FM_PORT_TYPE_TX: + if (fm_port->port_speed == FM_PORT_SPEED_1G) { + fm_port->fm_port_drv_param->dont_release_buf = false; + /* FM_WRONG_RESET_VALUES_ERRATA_FMAN_A005127 Errata + * workaround + */ + if (fm_port->fm_rev_info.major_rev >= 6) { + tmp_reg = 0x00001013; + out_be32(&fm_port->fm_port_bmi_regs-> + tx_port_bmi_regs.fmbm_tfp, + tmp_reg); + } + } + if (fm_port->port_speed == FM_PORT_SPEED_10G) { + fm_port->fm_port_drv_param->tx_fifo_min_fill_level = + DFLT_PORT_TX_FIFO_MIN_FILL_LEVEL; + fm_port->fm_port_drv_param->tx_fifo_low_comf_level = + DFLT_PORT_TX_FIFO_LOW_COMF_LEVEL; + + fm_port->fm_port_drv_param->deq_type = + DFLT_PORT_DEQ_TYPE; + fm_port->fm_port_drv_param->deq_prefetch_option = + DFLT_PORT_DEQ_PREFETCH_OPT; + } + fm_port->fm_port_drv_param->deq_high_priority = + DFLT_PORT_DEQ_HIGH_PRIORITY(fm_port->port_speed); + fm_port->fm_port_drv_param->deq_byte_cnt = + DFLT_PORT_DEQ_BYTE_CNT(fm_port->port_speed); + fm_port->fm_port_drv_param->dflt_cfg. + tx_fifo_deq_pipeline_depth = + fm_port_dflt_fifo_deq_pipeline_depth( + fm_port->fm_rev_info.major_rev, + fm_port->port_type, + fm_port->port_speed); + break; + default: + pr_err("Invalid port type\n"); + goto err_fm_port; + } + + switch (fm_port->port_type) { + case FM_PORT_TYPE_RX: + /* Initialize FM port parameters for initialization + * phase only + */ + memcpy(&fm_port->fm_port_drv_param->ext_buf_pools, + &fm_port_params-> + specific_params.rx_params.ext_buf_pools, + sizeof(struct fm_ext_pools_t)); + fm_port->fm_port_drv_param->err_fqid = + fm_port_params->specific_params.rx_params.err_fqid; + fm_port->fm_port_drv_param->dflt_fqid = + fm_port_params->specific_params.rx_params.dflt_fqid; + fm_port->fm_port_drv_param->liodn_offset = + fm_port_params->specific_params.rx_params.liodn_offset; + break; + case FM_PORT_TYPE_TX: + fm_port->fm_port_drv_param->err_fqid = + fm_port_params->specific_params.non_rx_params.err_fqid; + fm_port->fm_port_drv_param->deq_sub_portal = + (u8)(fm_port_params->specific_params.non_rx_params. + qm_channel & QMI_DEQ_CFG_SUBPORTAL_MASK); + fm_port->fm_port_drv_param->dflt_fqid = + fm_port_params->specific_params.non_rx_params.dflt_fqid; + break; + default: + pr_err("Invalid port type\n"); + goto err_fm_port; + } + + memset(fm_port->name, 0, (sizeof(char)) * MODULE_NAME_SIZE); + if (snprintf(fm_port->name, MODULE_NAME_SIZE, "FM-%d-port-%s-%d", + fm_get_id(fm_port->fm), + ((fm_port->port_type == FM_PORT_TYPE_TX) ? + ((fm_port->port_speed == FM_PORT_SPEED_10G) ? "10g-TX" : + "1g-TX") : + ((fm_port->port_speed == FM_PORT_SPEED_10G) ? "10g-RX" : + "1g-RX")), + fm_port->port_id) == 0) { + pr_err("sprintf failed\n"); + goto err_fm_port; + } + + return fm_port; + +err_fm_port: + kfree(fm_port->port_intg); +err_fm_port_intg: + kfree(fm_port->fm_port_drv_param); +err_fm_port_params: + kfree(fm_port); + return NULL; +} + +int fm_port_init(struct fm_port_t *fm_port) +{ + struct fm_port_drv_param_t *drv_params; + int err; + struct fm_inter_module_port_init_params_t fm_params; + struct fm_revision_info_t rev_info; + enum fman_port_type fman_port_type = E_FMAN_PORT_TYPE_DUMMY; + + if (is_init_done(fm_port->fm_port_drv_param)) + return -EINVAL; + + err = fm_sp_build_buffer_structure(&fm_port->fm_port_drv_param-> + int_context, + &fm_port->fm_port_drv_param-> + buffer_prefix_content, + &fm_port->fm_port_drv_param-> + buf_margins, + &fm_port->buffer_offsets, + &fm_port->internal_buf_offset); + if (err) + return err; + + /* FM_HEAVY_TRAFFIC_HANG_ERRATA_FMAN_A005669 Errata workaround */ + if (fm_port->fm_rev_info.major_rev >= 6 && + (fm_port->fm_port_drv_param->bcb_workaround) && + ((fm_port->port_type == FM_PORT_TYPE_RX) && + (fm_port->port_speed == FM_PORT_SPEED_1G))) { + fm_port->fm_port_drv_param->errors_to_discard |= + FM_PORT_FRM_ERR_PHYSICAL; + if (!fm_port->fifo_bufs.num) + fm_port->fifo_bufs.num = + fm_port_dflt_num_of_fifo_bufs( + fm_port->fm_rev_info.major_rev, + fm_port->port_type, + fm_port->port_speed) * + BMI_FIFO_UNITS; + fm_port->fifo_bufs.num += 4 * 1024; + } + + err = check_init_parameters(fm_port); + if (err) + return err; + + drv_params = fm_port->fm_port_drv_param; + + memset(&fm_port->port, 0, sizeof(struct fman_port)); + /* In order to be aligned with flib port types, we need to translate + * the port type and speed to fman_port_type + */ + if (fm_port->port_type == FM_PORT_TYPE_TX) { + if (fm_port->port_speed == FM_PORT_SPEED_10G) + fman_port_type = E_FMAN_PORT_TYPE_TX_10G; + else + fman_port_type = E_FMAN_PORT_TYPE_TX; + } else if (fm_port->port_type == FM_PORT_TYPE_RX) { + if (fm_port->port_speed == FM_PORT_SPEED_10G) + fman_port_type = E_FMAN_PORT_TYPE_RX_10G; + else + fman_port_type = E_FMAN_PORT_TYPE_RX; + } + fm_port->port.type = fman_port_type; + fm_get_revision(fm_port->fm, &rev_info); + fm_port->port.fm_rev_maj = rev_info.major_rev; + fm_port->port.fm_rev_min = rev_info.minor_rev; + fm_port->port.bmi_regs = (union fman_port_bmi_regs __iomem *) + (drv_params->base_addr + BMI_PORT_REGS_OFFSET); + fm_port->port.qmi_regs = (struct fman_port_qmi_regs __iomem *) + (drv_params->base_addr + QMI_PORT_REGS_OFFSET); + fm_port->port.ext_pools_num = (u8)8; + + if (fm_port->port_type == FM_PORT_TYPE_RX) { + /* Call the external Buffer routine which also checks fifo + * size and updates it if necessary + */ + /* define external buffer pools and pool depletion */ + err = set_ext_buffer_pools(fm_port); + if (err) + return err; + /* check if the largest external buffer pool is large enough */ + if (drv_params->buf_margins.start_margins + MIN_EXT_BUF_SIZE + + drv_params->buf_margins.end_margins > + fm_port->rx_pools_params.largest_buf_size) { + pr_err("buf_margins.start_margins (%d) + minimum buf size (64) + buf_margins.end_margins (%d) is larger than maximum external buffer size (%d)\n", + drv_params->buf_margins.start_margins, + drv_params->buf_margins.end_margins, + fm_port->rx_pools_params.largest_buf_size); + return -EINVAL; + } + } + + /* Call FM module routine for communicating parameters */ + memset(&fm_params, 0, sizeof(fm_params)); + fm_params.port_id = fm_port->port_id; + fm_params.port_type = (enum fm_port_type)fm_port->port_type; + fm_params.port_speed = (enum fm_port_speed)fm_port->port_speed; + fm_params.num_of_tasks = (u8)fm_port->tasks.num; + fm_params.num_of_extra_tasks = (u8)fm_port->tasks.extra; + fm_params.num_of_open_dmas = (u8)fm_port->open_dmas.num; + fm_params.num_of_extra_open_dmas = (u8)fm_port->open_dmas.extra; + + if (fm_port->fifo_bufs.num) { + err = verify_size_of_fifo(fm_port); + if (err != 0) + return -err; + } + fm_params.size_of_fifo = fm_port->fifo_bufs.num; + fm_params.extra_size_of_fifo = fm_port->fifo_bufs.extra; + fm_params.liodn_offset = drv_params->liodn_offset; + fm_params.liodn_base = drv_params->liodn_base; + fm_params.deq_pipeline_depth = + fm_port->fm_port_drv_param->dflt_cfg.tx_fifo_deq_pipeline_depth; + fm_params.max_frame_length = fm_port->max_frame_length; + + err = fm_get_set_port_params(fm_port->fm, &fm_params); + if (err) + return -err; + + err = init_low_level_driver(fm_port); + if (err != 0) + return -err; + + fm_port_drv_param_free(fm_port); + + return 0; +} + +int fm_port_cfg_deq_high_priority(struct fm_port_t *fm_port, bool high_pri) +{ + if (is_init_done(fm_port->fm_port_drv_param)) + return -EINVAL; + + if (fm_port->port_type == FM_PORT_TYPE_RX) { + pr_err("cfg_deq_high_priority() not available for Rx ports\n"); + return -ENOMEM; + } + + fm_port->fm_port_drv_param->dflt_cfg.deq_high_pri = high_pri; + + return 0; +} + +int fm_port_cfg_deq_prefetch_option(struct fm_port_t *fm_port, + enum fm_port_deq_prefetch_option + deq_prefetch_option) +{ + if (is_init_done(fm_port->fm_port_drv_param)) + return -EINVAL; + + if (fm_port->port_type == FM_PORT_TYPE_RX) { + pr_err("fm_port_cfg_deq_prefetch_option not available for Rx ports\n"); + return -ENODEV; + } + fm_port->fm_port_drv_param->dflt_cfg.deq_prefetch_opt = + (enum fman_port_deq_prefetch)deq_prefetch_option; + + return 0; +} + +int fm_port_cfg_buf_prefix_content(struct fm_port_t *fm_port, + struct fm_buffer_prefix_content_t * + fm_buffer_prefix_content) +{ + if (is_init_done(fm_port->fm_port_drv_param)) + return -EINVAL; + + memcpy(&fm_port->fm_port_drv_param->buffer_prefix_content, + fm_buffer_prefix_content, + sizeof(struct fm_buffer_prefix_content_t)); + /* if data_align was not initialized by user, + * we return to driver's default + */ + if (!fm_port->fm_port_drv_param->buffer_prefix_content.data_align) + fm_port->fm_port_drv_param->buffer_prefix_content.data_align = + DFLT_PORT_BUFFER_PREFIX_CONTEXT_DATA_ALIGN; + + return 0; +} + +int fm_port_cfg_bcb_wa(struct fm_port_t *fm_port) +{ + if (is_init_done(fm_port->fm_port_drv_param)) + return -EINVAL; + + fm_port->fm_port_drv_param->bcb_workaround = true; + + return 0; +} + +int fm_port_disable(struct fm_port_t *fm_port) +{ + int err; + + if (!is_init_done(fm_port->fm_port_drv_param)) + return -EINVAL; + + err = fman_port_disable(&fm_port->port); + if (err == -EBUSY) { + pr_debug("%s: BMI or QMI is Busy. Port forced down\n", + fm_port->name); + err = 0; + } + + fm_port->enabled = false; + + return err; +} +EXPORT_SYMBOL(fm_port_disable); + +int fm_port_enable(struct fm_port_t *fm_port) +{ + int err; + + if (!is_init_done(fm_port->fm_port_drv_param)) + return -EINVAL; + + /* Used by fm_port_free routine as indicationif to disable port. + * Thus set it to true prior to enabling itself. + * This way if part of enable process fails there will be still + * things to disable during Free. + * For example, if BMI enable succeeded but QMI failed, still BMI + * needs to be disabled by Free. + */ + fm_port->enabled = true; + + err = fman_port_enable(&fm_port->port); + + return err; +} +EXPORT_SYMBOL(fm_port_enable); diff --git a/drivers/net/ethernet/freescale/fman/port/fm_port.h b/drivers/net/ethernet/freescale/fman/port/fm_port.h new file mode 100644 index 0000000..4ac24c1 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/port/fm_port.h @@ -0,0 +1,502 @@ +/* + * Copyright 2008 - 2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. + */ + +#ifndef __FM_PORT_H +#define __FM_PORT_H + +#include "fm_common.h" +#include "fm_sp_common.h" +#include "fsl_fman_sp.h" +#include "fm_port_ext.h" +#include "fsl_fman_port.h" + +#define LIODN_DONT_OVERRIDE (-1) + +#define MIN_EXT_BUF_SIZE 64 + +#define MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) \ + min((u32)bmi_max_fifo_size, (u32)1024 * BMI_FIFO_UNITS) + +/* Memory Map defines */ +#define BMI_PORT_REGS_OFFSET 0 +#define QMI_PORT_REGS_OFFSET 0x400 + +/* defaults */ +#define DFLT_PORT_DEQ_HIGH_PRIORITY(speed) \ + ((speed == FM_PORT_SPEED_10G) ? true : false) +#define DFLT_PORT_DEQ_TYPE FM_PORT_DEQ_TYPE1 +#define DFLT_PORT_DEQ_PREFETCH_OPT FM_PORT_DEQ_FULL_PREFETCH +#define DFLT_PORT_DEQ_BYTE_CNT(speed) \ + ((speed == FM_PORT_SPEED_10G) ? 0x1400 : 0x400) +#define DFLT_PORT_BUFFER_PREFIX_CONTENT_PRIV_DATA_SIZE \ + DEFAULT_FM_SP_BUFFER_PREFIX_CONTENT_PRIV_DATA_SIZE +#define DFLT_PORT_BUFFER_PREFIX_CONTENT_PASS_PRS_RESULT \ + DEFAULT_FM_SP_BUFFER_PREFIX_CONTENT_PRIV_PASS_PRS_RESULT +#define DFLT_PORT_BUFFER_PREFIX_CONTENT_PASS_TIME_STAMP \ + DEFAULT_FM_SP_BUFFER_PREFIX_CONTEXT_PASS_TIME_STAMP +#define DFLT_PORT_BUFFER_PREFIX_CONTEXT_DATA_ALIGN \ + DEFAULT_FM_SP_BUFFER_PREFIX_CONTEXT_DATA_ALIGN +#define DFLT_PORT_CHECKSUM_LAST_BYTES_IGNORE 0 +#define DFLT_PORT_CUT_BYTES_FROM_END 4 + +#define DFLT_PORT_FRM_DISCARD_OVERRIDE false + +#define DFLT_PORT_FORWARD_INT_CONTENT_REUSE false +#define DFLT_PORT_BUF_MARGINS_END_MAARGINS 0 +#define DFLT_PORT_ERRORS_TO_DISCARD FM_PORT_FRM_ERR_CLS_DISCARD +#define DFLT_PORT_MAX_FRAME_LENGTH 9600 + +#define DFLT_NOT_SUPPORTED 0xff + +#define DFLT_PORT_RX_FIFO_PRI_ELEVATION_LEV(bmi_max_fifo_size) \ + MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) + +#define DFLT_PORT_RX_FIFO_THRESHOLD(major, bmi_max_fifo_size) \ + (major == 6 ? \ + MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) : \ + (MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) * 3 / 4)) \ + +#define DFLT_PORT_TX_FIFO_MIN_FILL_LEVEL 0 +#define DFLT_PORT_TX_FIFO_LOW_COMF_LEVEL (5 * 1024) + +#define DFLT_PORT_EXTRA_NUM_OF_FIFO_BUFS 0 + +#define FM_PORT_MAX_NUM_OF_CONGESTION_GRPS_ALL_INTEGRATIONS 256 + +/* Memory Mapped Registers */ +struct fm_port_rx_bmi_regs_t { + u32 fmbm_rcfg; /* Rx Configuration */ + u32 fmbm_rst; /* Rx Status */ + u32 fmbm_rda; /* Rx DMA attributes */ + u32 fmbm_rfp; /* Rx FIFO Parameters */ + u32 fmbm_rfed; /* Rx Frame End Data */ + u32 fmbm_ricp; /* Rx Internal Context Parameters */ + u32 fmbm_rim; /* Rx Internal Buffer Margins */ + u32 fmbm_rebm; /* Rx External Buffer Margins */ + u32 fmbm_rfne; /* Rx Frame Next Engine */ + u32 fmbm_rfca; /* Rx Frame Command Attributes. */ + u32 fmbm_rfpne; /* Rx Frame Parser Next Engine */ + u32 fmbm_rpso; /* Rx Parse Start Offset */ + u32 fmbm_rpp; /* Rx Policer Profile */ + u32 fmbm_rccb; /* Rx Coarse Classification Base */ + u32 fmbm_reth; /* Rx Excessive Threshold */ + u32 reserved1[0x01]; /* (0x03C) */ + u32 fmbm_rprai[FM_PORT_PRS_RESULT_NUM_OF_WORDS]; + /* Rx Parse Results Array Initialization */ + u32 fmbm_rfqid; /* Rx Frame Queue ID */ + u32 fmbm_refqid; /* Rx Error Frame Queue ID */ + u32 fmbm_rfsdm; /* Rx Frame Status Discard Mask */ + u32 fmbm_rfsem; /* Rx Frame Status Error Mask */ + u32 fmbm_rfene; /* Rx Frame Enqueue Next Engine */ + u32 reserved2[0x02]; /* (0x074-0x078) */ + /* Rx Frame Continuous Mode Next Engine */ + u32 fmbm_rcmne; + u32 reserved3[0x20]; /* (0x080 0x0FF) */ + u32 fmbm_ebmpi[FM_PORT_MAX_NUM_OF_EXT_POOLS]; + /* Buffer Manager pool Information- */ + u32 fmbm_acnt[FM_PORT_MAX_NUM_OF_EXT_POOLS]; /* Allocate Counter- */ + u32 reserved4[0x08]; /* 0x130/0x140 - 0x15F reserved - */ + u32 fmbm_rcgm[FM_PORT_MAX_NUM_OF_CONGESTION_GRPS_ALL_INTEGRATIONS / 32]; + /* Congestion Group Map */ + u32 fmbm_rmpd; /* BM Pool Depletion */ + u32 reserved5[0x1F]; /* (0x184 0x1FF) */ + u32 fmbm_rstc; /* Rx Statistics Counters */ + u32 fmbm_rfrc; /* Rx Frame Counter */ + u32 fmbm_rfbc; /* Rx Bad Frames Counter */ + u32 fmbm_rlfc; /* Rx Large Frames Counter */ + u32 fmbm_rffc; /* Rx Filter Frames Counter */ + u32 fmbm_rfcd; /* Rx Frame Discard Counter */ + u32 fmbm_rfldec; /* Rx Frames List DMA Error Counter */ + u32 fmbm_rodc;/* Rx Out of Buffers Discard Counter- */ + u32 fmbm_rbdc; /* Rx Buffers Deallocate Counter- */ + u32 fmbm_rpec; /* Rx RX Prepare to enqueue Counter- */ + u32 reserved6[0x16]; /* (0x228 0x27F) */ + u32 fmbm_rpc; /* Rx Performance Counters */ + u32 fmbm_rpcp; /* Rx Performance Count Parameters */ + u32 fmbm_rccn; /* Rx Cycle Counter */ + u32 fmbm_rtuc; /* Rx Tasks Utilization Counter */ + u32 fmbm_rrquc;/* Rx Receive Queue Utilization Counter */ + u32 fmbm_rduc; /* Rx DMA Utilization Counter */ + u32 fmbm_rfuc; /* Rx FIFO Utilization Counter */ + u32 fmbm_rpac; /* Rx Pause Activation Counter */ + u32 reserved7[0x18]; /* (0x2A0-0x2FF) */ + u32 fmbm_rdcfg[0x3]; /* Rx Debug- */ + u32 fmbm_rgpr; /* Rx General Purpose Register. */ + u32 reserved8[0x3a]; + /* (0x310-0x3FF) */ +}; + +struct fm_port_tx_bmi_regs_t { + u32 fmbm_tcfg; /* Tx Configuration */ + u32 fmbm_tst; /* Tx Status */ + u32 fmbm_tda; /* Tx DMA attributes */ + u32 fmbm_tfp; /* Tx FIFO Parameters */ + u32 fmbm_tfed; /* Tx Frame End Data */ + u32 fmbm_ticp; /* Tx Internal Context Parameters */ + u32 fmbm_tfdne; /* Tx Frame Dequeue Next Engine. */ + u32 fmbm_tfca; /* Tx Frame Command attribute. */ + u32 fmbm_tcfqid; /* Tx Confirmation Frame Queue ID. */ + u32 fmbm_tfeqid; /* Tx Frame Error Queue ID */ + u32 fmbm_tfene; /* Tx Frame Enqueue Next Engine */ + u32 fmbm_trlmts; /* Tx Rate Limiter Scale */ + u32 fmbm_trlmt; /* Tx Rate Limiter */ + u32 fmbm_tccb; /* Tx Coarse Classification Base */ + u32 reserved0[0x0e]; /* (0x038-0x070) */ + u32 fmbm_tfne; /* Tx Frame Next Engine */ + u32 fmbm_tpfcm[0x02]; + /* Tx Priority based Flow Control (PFC) Mapping */ + u32 fmbm_tcmne; /* Tx Frame Continuous Mode Next Engine */ + u32 reserved2[0x60]; /* (0x080-0x200) */ + u32 fmbm_tstc; /* Tx Statistics Counters */ + u32 fmbm_tfrc; /* Tx Frame Counter */ + u32 fmbm_tfdc; /* Tx Frames Discard Counter */ + u32 fmbm_tfledc; /* Tx Frame Length error discard counter */ + /* Tx Frame unsupported format discard Counter */ + u32 fmbm_tfufdc; + u32 fmbm_tbdc; /* Tx Buffers Deallocate Counter */ + u32 reserved3[0x1A]; /* (0x218-0x280) */ + u32 fmbm_tpc; /* Tx Performance Counters */ + u32 fmbm_tpcp; /* Tx Performance Count Parameters */ + u32 fmbm_tccn; /* Tx Cycle Counter */ + u32 fmbm_ttuc; /* Tx Tasks Utilization Counter */ + u32 fmbm_ttcquc; /* Tx Transmit Confirm Queue Utilization Counter */ + u32 fmbm_tduc; /* Tx DMA Utilization Counter */ + u32 fmbm_tfuc; /* Tx FIFO Utilization Counter */ + u32 reserved4[16];/* (0x29C-0x2FF) */ + u32 fmbm_tdcfg[0x3]; + /* Tx Debug- */ + u32 fmbm_tgpr; /* O/H General Purpose Register */ + u32 reserved5[0x3a]; /* (0x310-0x3FF) */ +}; + +union fm_port_bmi_regs_u { + struct fm_port_rx_bmi_regs_t rx_port_bmi_regs; + struct fm_port_tx_bmi_regs_t tx_port_bmi_regs; +}; + +/* BMI defines */ +#define BMI_PORT_RFNE_FRWD_RPD 0x40000000 + +#define BMI_STATUS_RX_MASK_UNUSED \ +(u32)(~(FM_PORT_FRM_ERR_DMA | \ +FM_PORT_FRM_ERR_PHYSICAL | \ +FM_PORT_FRM_ERR_SIZE | \ +FM_PORT_FRM_ERR_CLS_DISCARD | \ +FM_PORT_FRM_ERR_EXTRACTION | \ +FM_PORT_FRM_ERR_NO_SCHEME | \ +FM_PORT_FRM_ERR_COLOR_RED | \ +FM_PORT_FRM_ERR_COLOR_YELLOW | \ +FM_PORT_FRM_ERR_PRS_TIMEOUT | \ +FM_PORT_FRM_ERR_PRS_ILL_INSTRUCT | \ +FM_PORT_FRM_ERR_BLOCK_LIMIT_EXCEEDED | \ +FM_PORT_FRM_ERR_PRS_HDR_ERR | \ +FM_PORT_FRM_ERR_IPRE | \ +FM_PORT_FRM_ERR_IPR_NCSP | \ +FM_PORT_FRM_ERR_KEYSIZE_OVERFLOW)) + +#define RX_ERRS_TO_ENQ \ +(FM_PORT_FRM_ERR_DMA | \ +FM_PORT_FRM_ERR_PHYSICAL | \ +FM_PORT_FRM_ERR_SIZE | \ +FM_PORT_FRM_ERR_EXTRACTION | \ +FM_PORT_FRM_ERR_NO_SCHEME | \ +FM_PORT_FRM_ERR_PRS_TIMEOUT | \ +FM_PORT_FRM_ERR_PRS_ILL_INSTRUCT | \ +FM_PORT_FRM_ERR_BLOCK_LIMIT_EXCEEDED | \ +FM_PORT_FRM_ERR_PRS_HDR_ERR | \ +FM_PORT_FRM_ERR_KEYSIZE_OVERFLOW | \ +FM_PORT_FRM_ERR_IPRE) + +/* sizes */ +#define FRAME_END_DATA_SIZE 16 +#define MIN_TX_INT_OFFSET 16 +#define MAX_FIFO_PIPELINE_DEPTH 8 +#define MAX_NUM_OF_TASKS 64 +#define MAX_NUM_OF_EXTRA_TASKS 8 +#define MAX_NUM_OF_DMAS 16 +#define MAX_NUM_OF_EXTRA_DMAS 8 + +/* QMI defines */ +#define QMI_DEQ_CFG_SUBPORTAL_MASK 0x1f + +struct fm_port_drv_param_t { + struct fman_port_cfg dflt_cfg; + u32 dflt_fqid; + u32 err_fqid; + void __iomem *base_addr; + u8 deq_sub_portal; + bool deq_high_priority; + enum fm_port_deq_type deq_type; + enum fm_port_deq_prefetch_option deq_prefetch_option; + u16 deq_byte_cnt; + u8 cheksum_last_bytes_ignore; + u8 cut_bytes_from_end; + struct fm_buf_pool_depletion_t buf_pool_depletion; + bool frm_discard_override; + bool en_buf_pool_depletion; + u16 liodn_offset; + u16 liodn_base; + struct fm_ext_pools_t ext_buf_pools; + u32 tx_fifo_min_fill_level; + u32 tx_fifo_low_comf_level; + u32 rx_fifo_pri_elevation_level; + u32 rx_fifo_threshold; + struct fm_sp_buf_margins_t buf_margins; + struct fm_sp_int_context_data_copy_t int_context; + u32 errors_to_discard; + bool forward_reuse_int_context; + struct fm_buffer_prefix_content_t buffer_prefix_content; + struct fm_backup_bm_pools_t *backup_bm_pools; + bool dont_release_buf; + bool set_num_of_tasks; + bool set_num_of_open_dmas; + bool set_size_of_fifo; + bool bcb_workaround; +}; + +struct fm_port_rx_pools_params_t { + u8 num_of_pools; + u16 second_largest_buf_size; + u16 largest_buf_size; +}; + +struct fm_port_intg_t { + u32 max_port_fifo_size; + u32 max_num_of_ext_pools; + u32 fm_max_num_of_sub_portals; + u32 bm_max_num_of_pools; +}; + +/* No PCD Engine indicated */ +#define FM_PCD_NONE 0 + +struct fm_port_t { + struct fman_port port; + void *fm; + struct fm_revision_info_t fm_rev_info; + u8 port_id; + enum fm_port_type port_type; + enum fm_port_speed port_speed; + int enabled; + char name[MODULE_NAME_SIZE]; + + union fm_port_bmi_regs_u __iomem *fm_port_bmi_regs; + /* The optional engines are devined avobe */ + struct fm_sp_buffer_offsets_t buffer_offsets; + + u8 internal_buf_offset; + struct fm_ext_pools_t ext_buf_pools; + + u16 max_frame_length; + struct fm_port_rsrc_t open_dmas; + struct fm_port_rsrc_t tasks; + struct fm_port_rsrc_t fifo_bufs; + struct fm_port_rx_pools_params_t rx_pools_params; + + struct fm_port_drv_param_t *fm_port_drv_param; + struct fm_port_intg_t *port_intg; +}; + +static inline int fm_port_dflt_fifo_deq_pipeline_depth(u8 major, + enum fm_port_type type, + enum fm_port_speed speed) +{ + switch (type) { + case FM_PORT_TYPE_RX: + case FM_PORT_TYPE_TX: + switch (speed) { + case FM_PORT_SPEED_10G: + return 4; + case FM_PORT_SPEED_1G: + if (major >= 6) + return 2; + else + return 1; + default: + return 0; + } + default: + return 0; + } +} + +static inline int fm_port_dflt_num_of_tasks(u8 major, + enum fm_port_type type, + enum fm_port_speed speed) +{ + switch (type) { + case FM_PORT_TYPE_RX: + case FM_PORT_TYPE_TX: + switch (speed) { + case FM_PORT_SPEED_10G: + return 16; + case FM_PORT_SPEED_1G: + if (major >= 6) + return 4; + else + return 3; + default: + return 0; + } + default: + return 0; + } +} + +static inline int fm_port_dflt_extra_num_of_tasks(u8 major, + enum fm_port_type type, + enum fm_port_speed speed) +{ + switch (type) { + case FM_PORT_TYPE_RX: + /* FMan V3 */ + if (major >= 6) + return 0; + + /* FMan V2 */ + if (speed == FM_PORT_SPEED_10G) + return 8; + else + return 2; + case FM_PORT_TYPE_TX: + default: + return 0; + } +} + +static inline int fm_port_dflt_num_of_open_dmas(u8 major, + enum fm_port_type type, + enum fm_port_speed speed) +{ + int val; + + if (major >= 6) { + switch (type) { + case FM_PORT_TYPE_TX: + if (speed == FM_PORT_SPEED_10G) + val = 12; + else + val = 3; + break; + case FM_PORT_TYPE_RX: + if (speed == FM_PORT_SPEED_10G) + val = 8; + else + val = 2; + break; + default: + return 0; + } + } else { + switch (type) { + case FM_PORT_TYPE_TX: + case FM_PORT_TYPE_RX: + if (speed == FM_PORT_SPEED_10G) + val = 8; + else + val = 1; + break; + default: + val = 0; + } + } + + return val; +} + +static inline int fm_port_dflt_extra_num_of_open_dmas(u8 major, + enum fm_port_type type, + enum fm_port_speed speed) +{ + /* FMan V3 */ + if (major >= 6) + return 0; + + /* FMan V2 */ + switch (type) { + case FM_PORT_TYPE_RX: + case FM_PORT_TYPE_TX: + if (speed == FM_PORT_SPEED_10G) + return 8; + else + return 1; + default: + return 0; + } +} + +static inline int fm_port_dflt_num_of_fifo_bufs(u8 major, + enum fm_port_type type, + enum fm_port_speed speed) +{ + int val; + + if (major >= 6) { + switch (type) { + case FM_PORT_TYPE_TX: + if (speed == FM_PORT_SPEED_10G) + val = 64; + else + val = 50; + break; + case FM_PORT_TYPE_RX: + if (speed == FM_PORT_SPEED_10G) + val = 96; + else + val = 50; + break; + default: + val = 0; + } + } else { + switch (type) { + case FM_PORT_TYPE_TX: + if (speed == FM_PORT_SPEED_10G) + val = 48; + else + val = 44; + break; + case FM_PORT_TYPE_RX: + if (speed == FM_PORT_SPEED_10G) + val = 48; + else + val = 45; + break; + default: + val = 0; + } + } + + return val; +} + +#endif /* __FM_PORT_H */ -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/