From patchwork Wed Dec 11 09:52:10 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Xiaojun Liu X-Patchwork-Id: 63744 X-Patchwork-Delegate: xiaolong.ye@intel.com Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id 1562AA04F1; Wed, 11 Dec 2019 10:52:47 +0100 (CET) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id E58071BF7E; Wed, 11 Dec 2019 10:52:13 +0100 (CET) Received: from EUR01-VE1-obe.outbound.protection.outlook.com (mail-eopbgr140132.outbound.protection.outlook.com [40.107.14.132]) by dpdk.org (Postfix) with ESMTP id 42C371BF73 for ; Wed, 11 Dec 2019 10:52:12 +0100 (CET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=igMZ8WxrRxF6b+3kHmhBo4XuIzfGvkNE1GM8Bm/G7/zhbeqGlNrAtE5ZdskVHsLwd/Quub8xKxvwQtJTbuBtQtbsYMZc9U0ZFL+W58DcR38kOYJTbVh357VLYARv9bFodSrNoqAByc72yiSvUMzCFayfCULw3kxhAvQyVH5sqM2643zJ17oJJ+kWIl0lB2ewa4fmbQQTJwiXYcUfHZXgmSEr30K5JOrBTwdffooTy03Yq8i2Ap4KMS4cVj1V9W+ePSXf9K9PeLShHpOXzjhhjoCSDgu7SD+L97nsXNuHr/MAbSzF9wRtExGSXl33O+9qe575kPUkdCO3+D5YEOFeVg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=6QA/aBMlgA+lf74+ky5gcYh7KpWcdtfJi98kzJwlNns=; b=Ltw/+Uv8DqVeAvRpIqvIZ6TZlaECDRoBzuPSS2QO0eYM05CI/ESsPcorRm9t8HCnE0JfjrGGtUeAWIOaO173K+CnEkR+Mckul1O3Yw5APqlqfNF6iMz10Qt79ERAfTt8E5VDNVn+EKNci8fZqAG8+fYAF+xNDuAPlGo20tkqNYdd9i0wu/ohA9qgCV7cd/74VKq99j85Vsb2U16iUUE0ANl3DCPz6tPO++OeX8xY0v0fWV0GpHBX50pKqByvVNTukUw5DefxqzGuWcIUK8CM/dZMx8qukmtdCXUPBUbHjoX9aosOlNJM7iYuuZL0KU1DQ0+PhX20Bq8NxjciUKDAvQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silicom.co.il; dmarc=pass action=none header.from=silicom.co.il; dkim=pass header.d=silicom.co.il; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=SILICOMLTD.onmicrosoft.com; s=selector2-SILICOMLTD-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=6QA/aBMlgA+lf74+ky5gcYh7KpWcdtfJi98kzJwlNns=; b=f7LoRsncJZwdNPzevEGU1nFt+Q+x9fNAe1Zqesg7wTLCdNKjV+qr4WCpbnIYIx622XkKiLR0MaKK3H0S9heSgxnLgVWqsO7aClWhA3QEXEiP2S9WUSO5D6Csbyz6f/spO0FtUJTkWAnC78uoEUYjZiAqDJsg0EbZGzcqsNlQJHQ= Received: from DB7PR04MB5196.eurprd04.prod.outlook.com (20.176.234.140) by DB7PR04MB4203.eurprd04.prod.outlook.com (52.135.131.12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2538.14; Wed, 11 Dec 2019 09:52:10 +0000 Received: from DB7PR04MB5196.eurprd04.prod.outlook.com ([fe80::cdaa:fcae:322b:59ed]) by DB7PR04MB5196.eurprd04.prod.outlook.com ([fe80::cdaa:fcae:322b:59ed%7]) with mapi id 15.20.2516.018; Wed, 11 Dec 2019 09:52:10 +0000 From: Xiaojun Liu To: "xiao.w.wang@intel.com" , "qi.z.zhang@intel.com" , "ngai-mint.kwan@intel.com" , "jakub.fornal@intel.co" , "jacob.e.keller@intel.com" CC: "dev@dpdk.org" , Xiaojun Liu Thread-Topic: [PATCH v2 4/7] net/fm10k: add flow and switch management Thread-Index: AQHVsAio5Q7GPKA61UezUT6MLAT9Zg== Date: Wed, 11 Dec 2019 09:52:10 +0000 Message-ID: <1576057875-7677-5-git-send-email-xiaojun.liu@silicom.co.il> References: <1576057875-7677-1-git-send-email-xiaojun.liu@silicom.co.il> In-Reply-To: <1576057875-7677-1-git-send-email-xiaojun.liu@silicom.co.il> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-clientproxiedby: HK2PR02CA0180.apcprd02.prod.outlook.com (2603:1096:201:21::16) To DB7PR04MB5196.eurprd04.prod.outlook.com (2603:10a6:10:1a::12) authentication-results: spf=none (sender IP is ) smtp.mailfrom=xiaojun.liu@silicom.co.il; x-ms-exchange-messagesentrepresentingtype: 1 x-mailer: git-send-email 1.8.3.1 x-originating-ip: [113.110.226.253] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: aa8b3655-c9ac-4428-01aa-08d77e1fca62 x-ms-traffictypediagnostic: DB7PR04MB4203: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:205; x-forefront-prvs: 024847EE92 x-forefront-antispam-report: SFV:NSPM; SFS:(10019020)(346002)(366004)(136003)(39850400004)(376002)(396003)(199004)(189003)(2906002)(71200400001)(81156014)(64756008)(316002)(66556008)(6506007)(44832011)(8676002)(52116002)(81166006)(26005)(66476007)(54906003)(110136005)(66946007)(186003)(36756003)(30864003)(6512007)(4326008)(86362001)(2616005)(107886003)(6486002)(66446008)(8936002)(5660300002)(478600001)(21314003)(579004)(569006); DIR:OUT; SFP:1102; SCL:1; SRVR:DB7PR04MB4203; H:DB7PR04MB5196.eurprd04.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; A:1; MX:1; received-spf: None (protection.outlook.com: silicom.co.il does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: 3NFg5P5n8AwFp7IolM93c04yXGRtKkaHpsqJr7r2ga5srkJee5u/AmPLGMztv3Yis3EFziWUoEB/MWbQEc2GaU4nhbz+AN8uHlV+tVxu3Gye5RdOGy++tcSyw1prdPC608wj1gxYOJfl93Y/qD/l8eaeDT+lpRxsJTH+Qq/duCIoSIxUwcyTDco5+oj1hL3R/tJEl9cr9rzaEvTpioScAqGdxZMtGVKa2EWbmwrU45r/ynF/Y4sbZaJqvuK0SY6ED9H1OskCyA3j704YBcfs4Bgiuj5kDLrrZ6DKV6SEwCXpK98kDxld2eOyl+7oHgoFI9+lMKfRsiKzu8UhbXSnlgQYa+RboK+F0LxyMCfj93yRBL/+tc8sVD5oNiih40MfW5q5Pmw1RPmLXJqzqN7NxdeDuEmfPKAk7RXBC9ShvQP5aP/MH2AxtWQu8C9NDi2rIV2JK/MumSco3eOdZbmnc/u8uqXtrI4Zg8gN7UvtPepqk4fMAcub/4azmY4YgNSa MIME-Version: 1.0 X-OriginatorOrg: silicom.co.il X-MS-Exchange-CrossTenant-Network-Message-Id: aa8b3655-c9ac-4428-01aa-08d77e1fca62 X-MS-Exchange-CrossTenant-originalarrivaltime: 11 Dec 2019 09:52:10.2669 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: c9e326d8-ce47-4930-8612-cc99d3c87ad1 X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: 5GImiDm4GmbzeTIf+dFePKBxYbDMiCcaM3/hgd/UcgJrgpEJ301cc6x5nRbkgB+B1lUcvKZnhf40xhxh6rGnKrZYnSy8sbhLEr0Uoa+bdxU= X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB7PR04MB4203 Subject: [dpdk-dev] [PATCH v2 4/7] net/fm10k: add flow and switch management X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" To support switch management, add the following new files: Add fm10k/switch/fm10k_flow.h. Add fm10k/switch/fm10k_flow.c(support dpdk flow operations) Add fm10k/switch/fm10k_switch.c(support switch management) Modify fm10k/Makefile(add fm10k_flow.c and fm10k_switch.c). To avoid configuration for both kernel driver and userspace SDK outside DPDK, we add switch management in FM10K DPDK PMD driver. To enable switch management, you need add CONFIG_RTE_FM10K_MANAGEMENT=y in config/common_linux when building. Signed-off-by: Xiaojun Liu --- drivers/net/fm10k/Makefile | 2 + drivers/net/fm10k/switch/fm10k_flow.c | 872 +++++++++++ drivers/net/fm10k/switch/fm10k_flow.h | 26 + drivers/net/fm10k/switch/fm10k_switch.c | 2562 +++++++++++++++++++++++++++++++ 4 files changed, 3462 insertions(+) create mode 100644 drivers/net/fm10k/switch/fm10k_flow.c create mode 100644 drivers/net/fm10k/switch/fm10k_flow.h create mode 100644 drivers/net/fm10k/switch/fm10k_switch.c diff --git a/drivers/net/fm10k/Makefile b/drivers/net/fm10k/Makefile index ed73251..ab263c5 100644 --- a/drivers/net/fm10k/Makefile +++ b/drivers/net/fm10k/Makefile @@ -93,6 +93,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_spico_code.c SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_stats.c SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_ffu.c SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_config.c +SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_switch.c +SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_flow.c endif SRCS-$(CONFIG_RTE_LIBRTE_FM10K_INC_VECTOR) += fm10k_rxtx_vec.c diff --git a/drivers/net/fm10k/switch/fm10k_flow.c b/drivers/net/fm10k/switch/fm10k_flow.c new file mode 100644 index 0000000..353f021 --- /dev/null +++ b/drivers/net/fm10k/switch/fm10k_flow.c @@ -0,0 +1,872 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2019 Silicom Ltd. Connectivity Solutions + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "fm10k_flow.h" +#include "fm10k_switch.h" +#include "fm10k_ffu.h" +#include "fm10k_config.h" + + +static int fm10k_flow_validate(struct rte_eth_dev *dev, + const struct rte_flow_attr *attr, + const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], + struct rte_flow_error *error); +static struct rte_flow *fm10k_flow_create(struct rte_eth_dev *dev, + const struct rte_flow_attr *attr, + const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], + struct rte_flow_error *error); +static int fm10k_flow_destroy(struct rte_eth_dev *dev, + struct rte_flow *flow, + struct rte_flow_error *error); +static int fm10k_flow_flush(struct rte_eth_dev *dev, + struct rte_flow_error *error); +static int fm10k_flow_parse_attr(const struct rte_flow_attr *attr, + struct rte_flow_error *error); + +const struct rte_flow_ops fm10k_flow_ops = { + .validate = fm10k_flow_validate, + .create = fm10k_flow_create, + .destroy = fm10k_flow_destroy, + .flush = fm10k_flow_flush, +}; + +union fm10k_filter_t cons_filter; +enum rte_filter_type fm10k_cons_filter_type = RTE_ETH_FILTER_NONE; + +/** + * MPLS filter configuration. + */ +enum fm10k_mpls_type { + FM10K_MPLS_TYPE_UNI, + FM10K_MPLS_TYPE_MULTI, +}; + +enum fm10k_mpls_action { + FM10K_MPLS_ACTION_DROP, + FM10K_MPLS_ACTION_QUEUE, +}; + +struct fm10k_mpls_filter_conf { + enum fm10k_mpls_type mpls_type; /**< mandatory for MPLS */ + uint32_t mpls_header; /**< MPLS header */ + uint32_t mpls_header_mask; /**< MPLS header mask */ + enum fm10k_mpls_action mpls_action; + uint16_t queue; + uint8_t ffu_id; + uint8_t ffu_prio; +}; + +/** + * VLAN filter configuration. + */ +struct fm10k_vlan_filter_conf { + int ffu_id; + uint8_t ffu_prio; + uint8_t is_ingress; + uint8_t port; + uint8_t in_ext_port; + uint8_t out_ext_port; + uint16_t in_vlan; + uint16_t out_vlan; +}; + + +union fm10k_filter_t { + struct fm10k_mpls_filter_conf mpls_filter; + struct fm10k_vlan_filter_conf vlan_filter; +}; + +typedef int (*parse_filter_t)(struct rte_eth_dev *dev, + const struct rte_flow_attr *attr, + const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], + struct rte_flow_error *error, + union fm10k_filter_t *filter); + +struct fm10k_valid_pattern { + enum rte_flow_item_type *items; + parse_filter_t parse_filter; +}; + +static enum rte_flow_item_type pattern_mpls_1[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_MPLS, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_vlan_1[] = { + RTE_FLOW_ITEM_TYPE_VLAN, + RTE_FLOW_ITEM_TYPE_PHY_PORT, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_vlan_2[] = { + RTE_FLOW_ITEM_TYPE_VLAN, + RTE_FLOW_ITEM_TYPE_PHY_PORT, + RTE_FLOW_ITEM_TYPE_PHY_PORT, + RTE_FLOW_ITEM_TYPE_END, +}; + +static int fm10k_flow_parse_mpls_filter(struct rte_eth_dev *dev, + const struct rte_flow_attr *attr, + const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], + struct rte_flow_error *error, + union fm10k_filter_t *filter); +static int +fm10k_flow_parse_vlan_filter(struct rte_eth_dev *dev, + const struct rte_flow_attr *attr, + const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], + struct rte_flow_error *error, + union fm10k_filter_t *filter); + +static struct fm10k_valid_pattern fm10k_supported_patterns[] = { + /* MPLS */ + { pattern_mpls_1, fm10k_flow_parse_mpls_filter }, + /* VLAN */ + { pattern_vlan_1, fm10k_flow_parse_vlan_filter }, + /* VLAN */ + { pattern_vlan_2, fm10k_flow_parse_vlan_filter }, +}; + +static const struct rte_flow_action * +fm10k_next_item_of_action(const struct rte_flow_action *actions, + uint32_t *index) +{ + static const struct rte_flow_action *act; + + act = actions + *index; + while (act->type == RTE_FLOW_ACTION_TYPE_VOID) { + (*index)++; + act = actions + *index; + } + return act; +} + +/* Find the first VOID or non-VOID item pointer */ +static const struct rte_flow_item * +fm10k_find_first_item(const struct rte_flow_item *item, bool is_void) +{ + bool is_find; + + while (item->type != RTE_FLOW_ITEM_TYPE_END) { + if (is_void) + is_find = item->type == RTE_FLOW_ITEM_TYPE_VOID; + else + is_find = item->type != RTE_FLOW_ITEM_TYPE_VOID; + if (is_find) + break; + item++; + } + return item; +} + +/* Skip all VOID items of the pattern */ +static void +fm10k_pattern_skip_void_item(struct rte_flow_item *items, + const struct rte_flow_item *pattern) +{ + uint32_t cpy_count = 0; + const struct rte_flow_item *pb = pattern, *pe = pattern; + + for (;;) { + /* Find a non-void item first */ + pb = fm10k_find_first_item(pb, false); + if (pb->type == RTE_FLOW_ITEM_TYPE_END) { + pe = pb; + break; + } + + /* Find a void item */ + pe = fm10k_find_first_item(pb + 1, true); + + cpy_count = pe - pb; + rte_memcpy(items, pb, sizeof(struct rte_flow_item) * cpy_count); + + items += cpy_count; + + if (pe->type == RTE_FLOW_ITEM_TYPE_END) { + pb = pe; + break; + } + + pb = pe + 1; + } + /* Copy the END item. */ + rte_memcpy(items, pe, sizeof(struct rte_flow_item)); +} + +/* Check if the pattern matches a supported item type array */ +static bool +fm10k_match_pattern(enum rte_flow_item_type *item_array, + struct rte_flow_item *pattern) +{ + struct rte_flow_item *item = pattern; + + while ((*item_array == item->type) && + (*item_array != RTE_FLOW_ITEM_TYPE_END)) { + item_array++; + item++; + } + + return (*item_array == RTE_FLOW_ITEM_TYPE_END && + item->type == RTE_FLOW_ITEM_TYPE_END); +} + +/* Find if there's parse filter function matched */ +static parse_filter_t +fm10k_find_parse_filter_func(struct rte_flow_item *pattern, uint32_t *idx) +{ + parse_filter_t parse_filter = NULL; + uint8_t i = *idx; + + for (; i < RTE_DIM(fm10k_supported_patterns); i++) { + if (fm10k_match_pattern(fm10k_supported_patterns[i].items, + pattern)) { + parse_filter = fm10k_supported_patterns[i].parse_filter; + break; + } + } + + *idx = ++i; + + return parse_filter; +} + +/* Parse attributes */ +static int +fm10k_flow_parse_attr(const struct rte_flow_attr *attr, + struct rte_flow_error *error) +{ + /* Not supported */ + if (attr->group) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ATTR_GROUP, + attr, "Not support group."); + return -rte_errno; + } + + return 0; +} + +/* + * MPLS + */ +/* 1. Last in item should be NULL as range is not supported. + * 2. Supported filter types: MPLS label. + * 3. Mask of fields which need to be matched should be + * filled with 1. + * 4. Mask of fields which needn't to be matched should be + * filled with 0. + */ +static int +fm10k_flow_parse_mpls_pattern(__rte_unused struct rte_eth_dev *dev, + const struct rte_flow_item *pattern, + struct rte_flow_error *error, + struct fm10k_mpls_filter_conf *filter) +{ + const struct rte_flow_item *item = pattern; + const struct rte_flow_item_mpls *mpls_spec; + const struct rte_flow_item_mpls *mpls_mask; + const struct rte_flow_item_eth *eth_spec; + enum rte_flow_item_type item_type; + const uint8_t label_mask[3] = {0xFF, 0xFF, 0xF0}; + uint32_t label_be = 0; + uint32_t be_mask = 0; + + for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) { + if (item->last) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Not support range"); + return -rte_errno; + } + item_type = item->type; + switch (item_type) { + case RTE_FLOW_ITEM_TYPE_ETH: + if (!item->spec || item->mask) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Invalid ETH item"); + return -rte_errno; + } + eth_spec = + (const struct rte_flow_item_eth *)item->spec; + + if (rte_be_to_cpu_16(eth_spec->type) == 0x8847) { + filter->mpls_type = FM10K_MPLS_TYPE_UNI; + } else if (rte_be_to_cpu_16(eth_spec->type) == 0x8848) { + filter->mpls_type = FM10K_MPLS_TYPE_MULTI; + } else { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, "Invalid ETH item"); + return -rte_errno; + } + break; + case RTE_FLOW_ITEM_TYPE_MPLS: + mpls_spec = + (const struct rte_flow_item_mpls *)item->spec; + mpls_mask = + (const struct rte_flow_item_mpls *)item->mask; + + if (!mpls_spec || !mpls_mask) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Invalid MPLS item"); + return -rte_errno; + } + + if (memcmp(mpls_mask->label_tc_s, label_mask, 3)) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Invalid MPLS label mask"); + return -rte_errno; + } + rte_memcpy(((uint8_t *)&label_be + 1), + mpls_spec->label_tc_s, 3); + rte_memcpy(((uint8_t *)&be_mask + 1), + mpls_mask->label_tc_s, 3); + filter->mpls_header = + rte_be_to_cpu_32(label_be) >> 4; + filter->mpls_header_mask = + rte_be_to_cpu_32(be_mask) >> 4; + + fm10k_cons_filter_type = RTE_ETH_FILTER_TUNNEL; + break; + default: + break; + } + } + + return 0; +} + +/* MPLS action only supports QUEUE or DROP. */ +static int +fm10k_flow_parse_mpls_action(const struct rte_flow_action *actions, + struct rte_flow_error *error, + struct fm10k_mpls_filter_conf *filter) +{ + const struct rte_flow_action *act; + const struct rte_flow_action_queue *act_q; + uint32_t index = 0; + + /* Check if the first non-void action is QUEUE or DROP. */ + act = fm10k_next_item_of_action(actions, &index); + if (act->type != RTE_FLOW_ACTION_TYPE_QUEUE && + act->type != RTE_FLOW_ACTION_TYPE_DROP) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, + act, "Not supported action."); + return -rte_errno; + } + + if (act->type == RTE_FLOW_ACTION_TYPE_QUEUE) { + act_q = (const struct rte_flow_action_queue *)act->conf; + filter->mpls_action = FM10K_MPLS_ACTION_QUEUE; + filter->queue = act_q->index; + } else { + filter->mpls_action = FM10K_MPLS_ACTION_DROP; + } + + /* Check if the next non-void item is END */ + index++; + act = fm10k_next_item_of_action(actions, &index); + if (act->type != RTE_FLOW_ACTION_TYPE_END) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, + act, "Not supported action."); + return -rte_errno; + } + + return 0; +} + +static int +fm10k_flow_parse_mpls_filter(struct rte_eth_dev *dev, + const struct rte_flow_attr *attr, + const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], + struct rte_flow_error *error, + union fm10k_filter_t *filter) +{ + struct fm10k_mpls_filter_conf *mpls_filter = + &filter->mpls_filter; + int ret; + + ret = fm10k_flow_parse_mpls_pattern(dev, pattern, + error, mpls_filter); + if (ret) + return ret; + + ret = fm10k_flow_parse_mpls_action(actions, error, mpls_filter); + if (ret) + return ret; + + ret = fm10k_flow_parse_attr(attr, error); + return ret; +} + + +/* + * VLAN + */ +static int +fm10k_flow_parse_vlan_pattern(__rte_unused struct rte_eth_dev *dev, + const struct rte_flow_item *pattern, + struct rte_flow_error *error, + struct fm10k_vlan_filter_conf *filter) +{ + const struct rte_flow_item *item = pattern; + const struct rte_flow_item_vlan *vlan_spec; + const struct rte_flow_item_phy_port *pp_spec; + enum rte_flow_item_type item_type; + + PMD_INIT_LOG(DEBUG, "Parse vlan pattern ffu id %d", filter->ffu_id); + + for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) { + if (item->last) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Not support range"); + return -rte_errno; + } + item_type = item->type; + switch (item_type) { + case RTE_FLOW_ITEM_TYPE_VLAN: + vlan_spec = + (const struct rte_flow_item_vlan *)item->spec; + + if (!vlan_spec) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Invalid VLAN item"); + return -rte_errno; + } + break; + + case RTE_FLOW_ITEM_TYPE_PHY_PORT: + pp_spec = + (const struct rte_flow_item_phy_port *)item->spec; + if (!pp_spec) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Invalid PHY PORT item"); + return -rte_errno; + } + break; + + default: + break; + } + } + + return 0; +} + + +static int +fm10k_flow_parse_vlan_action(struct rte_eth_dev *dev, + const struct rte_flow_action *actions, + struct rte_flow_error *error, + struct fm10k_vlan_filter_conf *filter) +{ + const struct rte_flow_action *act; + uint32_t index = 0; + + PMD_INIT_LOG(DEBUG, "Parse vlan action name %s ffu id %d", + dev->device->name, filter->ffu_id); + + /* Check if the first non-void action is QUEUE or DROP. */ + act = fm10k_next_item_of_action(actions, &index); + if (act->type != RTE_FLOW_ACTION_TYPE_MARK) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, + act, "Not supported action."); + return -rte_errno; + } + + index++; + act = fm10k_next_item_of_action(actions, &index); + if (act->type != RTE_FLOW_ACTION_TYPE_MARK && + act->type != RTE_FLOW_ACTION_TYPE_END) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, + act, "Not supported action."); + return -rte_errno; + } + if (act->type == RTE_FLOW_ACTION_TYPE_END) + return 0; + + index++; + /* Check if the next non-void item is END */ + act = fm10k_next_item_of_action(actions, &index); + if (act->type != RTE_FLOW_ACTION_TYPE_END) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, + act, "Not supported action."); + return -rte_errno; + } + + return 0; +} + +static int +fm10k_flow_parse_vlan_filter(struct rte_eth_dev *dev, + const struct rte_flow_attr *attr, + const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], + struct rte_flow_error *error, + union fm10k_filter_t *filter) +{ + int ret; + struct fm10k_vlan_filter_conf *vlan_filter = + &filter->vlan_filter; + + ret = fm10k_flow_parse_vlan_pattern(dev, pattern, + error, vlan_filter); + if (ret) + return ret; + + ret = fm10k_flow_parse_vlan_action(dev, actions, error, vlan_filter); + if (ret) + return ret; + + if (attr->ingress) + vlan_filter->is_ingress = 1; + else if (attr->egress) + vlan_filter->is_ingress = 0; + vlan_filter->ffu_prio = attr->priority; + + ret = fm10k_flow_parse_attr(attr, error); + return ret; +} + +/* + * + */ +static int +fm10k_flow_validate(struct rte_eth_dev *dev, + const struct rte_flow_attr *attr, + const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], + struct rte_flow_error *error) +{ + struct rte_flow_item *items; /* internal pattern w/o VOID items */ + parse_filter_t parse_filter; + uint32_t item_num = 0; /* non-void item number of pattern*/ + uint32_t i = 0; + bool flag = false; + int ret = -1; + + if (!pattern) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_NUM, + NULL, "NULL pattern."); + return -rte_errno; + } + + if (!actions) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION_NUM, + NULL, "NULL action."); + return -rte_errno; + } + + if (!attr) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ATTR, + NULL, "NULL attribute."); + return -rte_errno; + } + + /* Get the non-void item number of pattern */ + while ((pattern + i)->type != RTE_FLOW_ITEM_TYPE_END) { + if ((pattern + i)->type != RTE_FLOW_ITEM_TYPE_VOID) + item_num++; + i++; + } + item_num++; + + items = rte_zmalloc("fm10k_pattern", + item_num * sizeof(struct rte_flow_item), 0); + if (!items) { + rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_ITEM_NUM, + NULL, "No memory for PMD internal items."); + return -ENOMEM; + } + + fm10k_pattern_skip_void_item(items, pattern); + + i = 0; + do { + parse_filter = fm10k_find_parse_filter_func(items, &i); + if (!parse_filter && !flag) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + pattern, "Unsupported pattern"); + rte_free(items); + return -rte_errno; + } + if (parse_filter) + ret = parse_filter(dev, attr, items, actions, + error, &cons_filter); + flag = true; + } while ((ret < 0) && (i < RTE_DIM(fm10k_supported_patterns))); + + rte_free(items); + + return ret; +} + +static struct fm10k_cfg_flow * +fm10k_flow_cfg_transfer(struct rte_eth_dev *dev, + const struct rte_flow_attr *attr, + const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], + struct rte_flow_error *error) +{ + int i; + u8 port_id; + int set_port_num = 0, set_vlan_num = 0; + u16 fw_port_id = 0, bp_port_id = 0; + u16 filter_vlan_id = 0, fw_vlan_id = 0, bp_vlan_id = 0; + struct fm10k_hw *hw = FM10K_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct fm10k_cfg_flow *cf; + + cf = rte_zmalloc("fm10k_rule", sizeof(struct fm10k_cfg_flow), 0); + if (!cf) { + rte_flow_error_set(error, ENOMEM, + RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "Failed to allocate memory"); + return NULL; + } + memset(cf, 0, sizeof(struct fm10k_cfg_flow)); + + port_id = fm10k_switch_dpdk_port_no_get(hw); + for (i = 0; i < 4; i++) { + if (pattern[i].type == RTE_FLOW_ITEM_TYPE_VLAN) { + filter_vlan_id = + rte_be_to_cpu_16 + (((const struct rte_flow_item_vlan *) + pattern[i].spec)->tci); + } else if (pattern[i].type == RTE_FLOW_ITEM_TYPE_PHY_PORT) { + if (set_port_num) + bp_port_id = + ((const struct rte_flow_item_phy_port *) + pattern[i].spec)->index; + else + fw_port_id = + ((const struct rte_flow_item_phy_port *) + pattern[i].spec)->index; + set_port_num++; + } else if (pattern[i].type == RTE_FLOW_ITEM_TYPE_END) { + break; + } + } + + for (i = 0; i < 3; i++) { + if (actions[i].type == RTE_FLOW_ACTION_TYPE_MARK) { + if (set_vlan_num) + bp_vlan_id = + ((const struct rte_flow_action_mark *) + actions[i].conf)->id; + else + fw_vlan_id = + ((const struct rte_flow_action_mark *) + actions[i].conf)->id; + set_vlan_num++; + } else if (actions[i].type == RTE_FLOW_ACTION_TYPE_END) { + break; + } + } + + if (attr->ingress && !attr->egress) { + /* this port is DPDK port and it is destination port */ + cf->src_port.port_type = FM10K_CONFIG_FLOW_EXT_PORT; + cf->src_port.port_no = fw_port_id; + cf->src_port.vlan_id = filter_vlan_id; + cf->fw_port[0].port_type = FM10K_CONFIG_FLOW_DPDK_PORT; + cf->fw_port[0].port_no = port_id; + cf->fw_port[0].vlan_id = fw_vlan_id; + } else if (!attr->ingress && attr->egress) { + /* this port is DPDK port and it is source port */ + cf->src_port.port_type = FM10K_CONFIG_FLOW_DPDK_PORT; + cf->src_port.port_no = port_id; + cf->src_port.vlan_id = filter_vlan_id; + cf->fw_port[0].port_type = FM10K_CONFIG_FLOW_EXT_PORT; + cf->fw_port[0].port_no = fw_port_id; + cf->fw_port[0].vlan_id = fw_vlan_id; + } else if (!attr->ingress && !attr->egress) { + /* two ports are external port */ + cf->src_port.port_type = FM10K_CONFIG_FLOW_EXT_PORT; + cf->src_port.port_no = port_id; + cf->src_port.vlan_id = filter_vlan_id; + cf->fw_port[0].port_type = FM10K_CONFIG_FLOW_EXT_PORT; + cf->fw_port[0].port_no = fw_port_id; + cf->fw_port[0].vlan_id = fw_vlan_id; + } else { + /* two ports are DPDK port */ + cf->src_port.port_type = FM10K_CONFIG_FLOW_DPDK_PORT; + cf->src_port.port_no = port_id; + cf->src_port.vlan_id = filter_vlan_id; + cf->fw_port[0].port_type = FM10K_CONFIG_FLOW_DPDK_PORT; + cf->fw_port[0].port_no = fw_port_id; + cf->fw_port[0].vlan_id = fw_vlan_id; + } + + if (set_port_num == 2 && set_vlan_num == 2) { + cf->fw_port[1].port_type = FM10K_CONFIG_FLOW_EXT_PORT; + cf->fw_port[1].port_no = bp_port_id; + cf->fw_port[1].vlan_id = bp_vlan_id; + } + + return cf; +} + + +static struct rte_flow * +fm10k_flow_create(struct rte_eth_dev *dev, + const struct rte_flow_attr *attr, + const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], + struct rte_flow_error *error) +{ + struct fm10k_hw *hw = FM10K_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct rte_flow *flow; + struct fm10k_switch *sw = fm10k_switch_get(); + struct fm10k_cfg_flow *cf; + int ret; + + flow = rte_zmalloc("fm10k_flow", sizeof(struct rte_flow), 0); + if (!flow) { + rte_flow_error_set(error, ENOMEM, + RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "Failed to allocate memory"); + return flow; + } + + ret = fm10k_flow_validate(dev, attr, pattern, actions, error); + if (ret < 0) + return NULL; + + cf = fm10k_flow_cfg_transfer(dev, attr, pattern, actions, error); + if (!cf) + goto free_flow; + + flow->rule = cf; + fm10k_ffu_flow_enable(sw, cf); + fm10k_config_flow_list_add_tail(fm10k_config_flowset_current_get(), cf); + + TAILQ_INSERT_TAIL((struct fm10k_flow_list *) + fm10k_switch_dpdk_port_flow_list_get(hw), flow, node); + return flow; + +free_flow: + rte_flow_error_set(error, -ret, + RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "Failed to create flow."); + rte_free(flow); + return NULL; +} + +static int +fm10k_flow_destroy(struct rte_eth_dev *dev, + struct rte_flow *flow, + struct rte_flow_error *error) +{ + struct fm10k_hw *hw = FM10K_DEV_PRIVATE_TO_HW(dev->data->dev_private); + int ret = 0; + struct fm10k_switch *sw = fm10k_switch_get(); + + if (flow->rule) { + fm10k_config_flow_list_delete + ((struct fm10k_cfg_flow *)flow->rule); + fm10k_ffu_flow_disable(sw, + (struct fm10k_cfg_flow *)flow->rule); + } + + if (!ret) { + TAILQ_REMOVE((struct fm10k_flow_list *) + fm10k_switch_dpdk_port_flow_list_get(hw), + flow, node); + rte_free(flow->rule); + rte_free(flow); + } else { + rte_flow_error_set(error, -ret, + RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "Failed to destroy flow."); + } + return ret; +} + + +static int +fm10k_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error) +{ + struct fm10k_hw *hw = FM10K_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct rte_flow *flow; + void *temp; + int ret = 0; + struct fm10k_cfg_flow *cf; + struct fm10k_switch *sw = fm10k_switch_get(); + + /* Delete flows in flow list. */ + TAILQ_FOREACH_SAFE(flow, + (struct fm10k_flow_list *) + fm10k_switch_dpdk_port_flow_list_get(hw), + node, temp) { + cf = flow->rule; + if (cf) { + fm10k_config_flow_list_delete(cf); + fm10k_ffu_flow_disable(sw, cf); + } else { + rte_flow_error_set(error, -ret, + RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "No such rule in flow."); + } + TAILQ_REMOVE((struct fm10k_flow_list *) + fm10k_switch_dpdk_port_flow_list_get(hw), + flow, node); + rte_free(flow); + } + + return ret; +} + +void +fm10k_flow_list_init(void *flow_list) +{ + TAILQ_INIT((struct fm10k_flow_list *)flow_list); +} + +/* Flow operations */ +const struct rte_flow_ops * +fm10k_flow_ops_get(void) +{ + return &fm10k_flow_ops; +} + + diff --git a/drivers/net/fm10k/switch/fm10k_flow.h b/drivers/net/fm10k/switch/fm10k_flow.h new file mode 100644 index 0000000..c538544 --- /dev/null +++ b/drivers/net/fm10k/switch/fm10k_flow.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2019 Silicom Ltd. Connectivity Solutions + */ + +#ifndef _FM10K_SW_FLOW_H_ +#define _FM10K_SW_FLOW_H_ + +#include +#include +#include +#include +#include +#include + +/* + * Struct to store flow created. + */ +struct rte_flow { + TAILQ_ENTRY(rte_flow) node; + enum rte_filter_type filter_type; + void *rule; +}; + +TAILQ_HEAD(fm10k_flow_list, rte_flow); + +#endif /* _FM10K_SW_FLOW_H_ */ diff --git a/drivers/net/fm10k/switch/fm10k_switch.c b/drivers/net/fm10k/switch/fm10k_switch.c new file mode 100644 index 0000000..c3887d0 --- /dev/null +++ b/drivers/net/fm10k/switch/fm10k_switch.c @@ -0,0 +1,2562 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2019 Silicom Ltd. Connectivity Solutions + */ + +#include +#include + +#include + +#include "../base/fm10k_type.h" +#include "../base/fm10k_osdep.h" + +#include "../fm10k.h" +#include "../fm10k_logs.h" +#include "fm10k_debug.h" +#include "fm10k_regs.h" +#include "fm10k_switch.h" +#include "fm10k_ext_port.h" +#include "fm10k_serdes.h" +#include "fm10k_i2c.h" +#include "fm10k_ffu.h" +#include "fm10k_stats.h" +#include "fm10k_config.h" + +static struct fm10k_device_info fm10k_device_table[] = { + { FM10K_SW_VENDOR_ID_SILICOM, FM10K_SW_DEV_ID_PE3100G2DQIR_QXSL4, + "Silicom PE3100G2DQiR-QX4/QS4/QL4", 2, 100, 2, 2 }, + { FM10K_SW_VENDOR_ID_SILICOM, FM10K_SW_DEV_ID_PE3100G2DQIRL_QXSL4, + "Silicom PE3100G2DQiRL-QX4/QS4/QL4", 2, 100, 2, 2 }, + { FM10K_SW_VENDOR_ID_SILICOM, FM10K_SW_DEV_ID_PE3100G2DQIRM_QXSL4, + "Silicom PE3100G2DQiRM-QX4/QS4/QL4", 2, 100, 2, 4 }, +}; + + +static struct fm10k_switch fm10k_sw; + +#define FM10K_AM_TIMEOUT 16384 +#define FM10K_COMP_PPM_SCALE 1000000 + +#define FM10K_LED_POLL_INTERVAL_MS 500 +#define FM10K_LED_BLINKS_PER_SECOND 2 + +/* + * GLORT MAP + */ +#define FM10K_SW_EPLA_GLORT 0x10 +#define FM10K_SW_EPLB_GLORT 0x20 +#define FM10K_SW_PEP01_GLORT 0x30 +#define FM10K_SW_PEP23_GLORT 0x40 +#define FM10K_SW_PEP45_GLORT 0x50 +#define FM10K_SW_PEP67_GLORT 0x60 + +/* + * logical port number + */ +#define FM10K_SW_EPLA_LOGICAL_PORT 1 +#define FM10K_SW_EPLB_LOGICAL_PORT 2 + +#define FM10K_SW_PEP01_LOGICAL_PORT 3 +#define FM10K_SW_PEP23_LOGICAL_PORT 4 +#define FM10K_SW_PEP45_LOGICAL_PORT 5 +#define FM10K_SW_PEP67_LOGICAL_PORT 6 + +/* + * physical port number + */ +#define FM10K_SW_EPLA_PHYSICAL_PORT 0 +#define FM10K_SW_EPLB_PHYSICAL_PORT 4 + +#define FM10K_SW_PEP01_PHYSICAL_PORT 36 +#define FM10K_SW_PEP23_PHYSICAL_PORT 40 +#define FM10K_SW_PEP45_PHYSICAL_PORT 44 +#define FM10K_SW_PEP67_PHYSICAL_PORT 48 + +static struct fm10k_sw_port_map fm10k_pep_port_map[FM10K_SW_PEPS_SUPPORTED] = { + { + FM10K_SW_PEP01_GLORT, + FM10K_SW_PEP01_LOGICAL_PORT, + FM10K_SW_PEP01_PHYSICAL_PORT + }, + { + FM10K_SW_PEP23_GLORT, + FM10K_SW_PEP23_LOGICAL_PORT, + FM10K_SW_PEP23_PHYSICAL_PORT + }, + { + FM10K_SW_PEP45_GLORT, + FM10K_SW_PEP45_LOGICAL_PORT, + FM10K_SW_PEP45_PHYSICAL_PORT + }, + { + FM10K_SW_PEP67_GLORT, + FM10K_SW_PEP67_LOGICAL_PORT, + FM10K_SW_PEP67_PHYSICAL_PORT + }, +}; + +static struct fm10k_sw_port_map fm10k_epl_port_map[FM10K_SW_EPLS_SUPPORTED] = { + { + FM10K_SW_EPLA_GLORT, + FM10K_SW_EPLA_LOGICAL_PORT, + FM10K_SW_EPLA_PHYSICAL_PORT + }, + { + FM10K_SW_EPLB_GLORT, + FM10K_SW_EPLB_LOGICAL_PORT, + FM10K_SW_EPLB_PHYSICAL_PORT + }, +}; + +/* + * use epl as external port map, only support QUAD_ON + */ +struct fm10k_sched_prog { + uint8_t idle; + uint8_t phys; /* physical port */ + uint8_t log; /* logical port */ + uint8_t quad; /* now, only support QUAD_ON */ +/* convert entry to idle if EPL in quad port mode */ +#define FM10K_SW_QUAD_OFF 0 +/* always use quad port mode */ +#define FM10K_SW_QUAD_ON 1 +/* use quad port mode if link speed is 40/100G */ +#define FM10K_SW_QUAD_40_100 2 +}; + +static struct fm10k_sched_prog + fm10k_sched_prog[FM10K_SW_PEPS_SUPPORTED + FM10K_SW_EPLS_SUPPORTED + 1]; + +uint32_t +fm10k_switch_pf_logical_get(uint8_t pf_no) +{ + return fm10k_pep_port_map[pf_no].logical_port; +} + +uint32_t +fm10k_switch_epl_logical_get(uint8_t epl_no) +{ + return fm10k_epl_port_map[epl_no].logical_port; +} + +uint32_t +fm10k_switch_vf_glort_get(uint8_t vf_no) +{ + return FM10K_SW_VF_GLORT_START + vf_no; +} + +uint32_t +fm10k_switch_pf_glort_get(uint8_t pf_no) +{ + return fm10k_pep_port_map[pf_no].glort; +} + +uint32_t +fm10k_switch_epl_glort_get(uint8_t epl_no) +{ + return fm10k_epl_port_map[epl_no].glort; +} + +uint32_t +fm10k_switch_pfs_glort_get(uint8_t pf1, uint8_t pf2) +{ + uint8_t idx; + if (pf1 > pf2) + idx = (pf2 & 0xf) | (pf1 << 4 & 0xf0); + else + idx = (pf1 & 0xf) | (pf2 << 4 & 0xf0); + return FM10K_SW_PFS_GLORT_START + idx; +} + +struct fm10k_multi_glort { + uint8_t lport1; + uint8_t lport2; + uint16_t vlan1; + uint16_t vlan2; +} fm10k_multi_glorts[FM10K_SW_FFU_RULE_MAX]; + +uint32_t +fm10k_switch_multi_glort_get(uint8_t lport1, uint8_t lport2, + uint16_t vlan1, uint16_t vlan2, bool *p_new) +{ + int i; + + for (i = 0; i < FM10K_SW_FFU_RULE_MAX; i++) { + if (lport1 == fm10k_multi_glorts[i].lport1 && + lport2 == fm10k_multi_glorts[i].lport2 && + vlan1 == fm10k_multi_glorts[i].vlan1 && + vlan2 == fm10k_multi_glorts[i].vlan2) { + if (p_new != NULL) + *p_new = false; + return FM10K_SW_MULTI_GLORT_START + i; + } + } + + for (i = 0; i < FM10K_SW_FFU_RULE_MAX; i++) { + if (fm10k_multi_glorts[i].lport1 == 0 && + fm10k_multi_glorts[i].lport2 == 0 && + fm10k_multi_glorts[i].vlan1 == 0 && + fm10k_multi_glorts[i].vlan2 == 0) { + fm10k_multi_glorts[i].lport1 = lport1; + fm10k_multi_glorts[i].lport2 = lport2; + fm10k_multi_glorts[i].vlan1 = vlan1; + fm10k_multi_glorts[i].vlan2 = vlan2; + if (p_new != NULL) + *p_new = true; + return FM10K_SW_MULTI_GLORT_START + i; + } + } + + return 0; +} + + +/* + * Note that for physical port numbers, the initial values used for + * the EPL entries assume EPL[A] is EPL[0] and EPL[B] is EPL[1]. + * fm10k_switch_determine_epls() will update these physical port + * numbers based on the actual A and B indices. + */ +static void +fm10k_switch_set_sched_prog(void) +{ + int i; + int start = 0; + + for (i = 0; i < FM10K_SW_EPLS_SUPPORTED; i++) { + fm10k_sched_prog[i].idle = 0; + fm10k_sched_prog[i].phys = fm10k_epl_port_map[i].physical_port; + fm10k_sched_prog[i].log = fm10k_epl_port_map[i].logical_port; + fm10k_sched_prog[i].quad = FM10K_SW_QUAD_ON; + } + start += FM10K_SW_EPLS_SUPPORTED; + for (i = 0; i < FM10K_SW_PEPS_SUPPORTED; i++) { + fm10k_sched_prog[start + i].idle = 0; + fm10k_sched_prog[start + i].phys = + fm10k_pep_port_map[i].physical_port; + fm10k_sched_prog[start + i].log = + fm10k_pep_port_map[i].logical_port; + fm10k_sched_prog[start + i].quad = FM10K_SW_QUAD_40_100; + } + start += FM10K_SW_PEPS_SUPPORTED; + fm10k_sched_prog[start].idle = 1; +} + +struct fm10k_device_info* +fm10k_get_device_info(struct fm10k_hw *hw) +{ + unsigned int i; + struct fm10k_device_info *info; + uint16_t pci_vendor = hw->vendor_id; + uint16_t pci_device = hw->device_id; + uint16_t pci_subvendor = hw->subsystem_vendor_id; + uint16_t pci_subdevice = hw->subsystem_device_id; + + if (pci_vendor != FM10K_SW_VENDOR_ID_INTEL || + pci_device != FM10K_SW_DEV_ID_FM10K) + return (NULL); + + for (i = 0; + i < sizeof(fm10k_device_table) / + sizeof(fm10k_device_table[0]); + i++) { + info = &fm10k_device_table[i]; + if (pci_subvendor == info->subvendor && + pci_subdevice == info->subdevice) { + return info; + } + } + + return NULL; +} + + +static void +fm10k_switch_determine_epls(struct fm10k_switch *sw) +{ + struct fm10k_device_info *cfg = sw->info; + unsigned int i; + uint8_t phys; + + sw->epla_no = 0; + sw->eplb_no = 6; + + switch (FM10K_SW_CARD_ID(cfg->subvendor, cfg->subdevice)) { + case FM10K_SW_CARD(SILICOM, PE3100G2DQIR_QXSL4): + case FM10K_SW_CARD(SILICOM, PE3100G2DQIR_QXSL4_REV_2): + sw->epla_no = 1; + sw->eplb_no = 6; + break; + case FM10K_SW_CARD(SILICOM, PE3100G2DQIRL_QXSL4): + case FM10K_SW_CARD(SILICOM, PE3100G2DQIRM_QXSL4): + case FM10K_SW_CARD(SILICOM, PE325G2DSIR): + sw->epla_no = 1; + sw->eplb_no = 7; + break; + } + + for (i = 0; + i < sizeof(fm10k_sched_prog) / + sizeof(fm10k_sched_prog[0]); + i++) { + if (fm10k_sched_prog[i].idle) + continue; + + phys = fm10k_sched_prog[i].phys; + if (phys <= 3) { + /* Substitute actual epla phys port number */ + fm10k_sched_prog[i].phys = sw->epla_no * 4 + phys; + } else if (phys >= 4 && phys <= 7) { + /* Substitute actual eplb phys port number */ + fm10k_sched_prog[i].phys = sw->eplb_no * 4 + (phys - 4); + } + } +} + +static void +fm10k_hw_eicr_disable_source(struct fm10k_switch *sw, unsigned int source) +{ + unsigned int shift; + + switch (source) { + case FM10K_SW_EICR_PCA_FAULT: + shift = FM10K_SW_EICR_PCA_FAULT_SHIFT; + break; + case FM10K_SW_EICR_THI_FAULT: + shift = FM10K_SW_EICR_THI_FAULT_SHIFT; + break; + case FM10K_SW_EICR_FUM_FAULT: + shift = FM10K_SW_EICR_FUM_FAULT_SHIFT; + break; + case FM10K_SW_EICR_MAILBOX: + shift = FM10K_SW_EICR_MAILBOX_SHIFT; + break; + case FM10K_SW_EICR_SWITCH_READY: + shift = FM10K_SW_EICR_SWITCH_READY_SHIFT; + break; + case FM10K_SW_EICR_SWITCH_NREADY: + shift = FM10K_SW_EICR_SWITCH_NREADY_SHIFT; + break; + case FM10K_SW_EICR_SWITCH_INT: + shift = FM10K_SW_EICR_SWITCH_INT_SHIFT; + break; + case FM10K_SW_EICR_SRAM_ERROR: + shift = FM10K_SW_EICR_SRAM_ERROR_SHIFT; + break; + case FM10K_SW_EICR_VFLR: + shift = FM10K_SW_EICR_VFLR_SHIFT; + break; + case FM10K_SW_EICR_MAX_HOLD_TIME: + shift = FM10K_SW_EICR_MAX_HOLD_TIME_SHIFT; + break; + default: + return; + } + + fm10k_write_switch_reg(sw, + FM10K_SW_EIMR, FM10K_SW_EIMR_DISABLE << (shift * 2)); + fm10k_write_flush(sw); +} + + +unsigned int +fm10k_switch_eplidx_to_eplno(struct fm10k_switch *sw, unsigned int eplidx) +{ + return eplidx ? sw->eplb_no : sw->epla_no; +} + + +static uint16_t +fm10k_switch_ppm_to_tx_clk_compensation_timeout(uint32_t pcs, uint32_t num_ppm) +{ + unsigned int scale; + unsigned int ppm; + unsigned int timeout; + unsigned int am_ppm; + + if (num_ppm == 0 || pcs == FM10K_SW_EPL_PCS_SEL_DISABLE) { + if (pcs == FM10K_SW_EPL_PCS_SEL_40GBASER || + pcs == FM10K_SW_EPL_PCS_SEL_100GBASER) + return (FM10K_AM_TIMEOUT / 2); + else + return (0); + } + + if (pcs == FM10K_SW_EPL_PCS_SEL_40GBASER || + pcs == FM10K_SW_EPL_PCS_SEL_100GBASER) { + am_ppm = 1000000 / FM10K_AM_TIMEOUT; + scale = FM10K_COMP_PPM_SCALE / 2; + } else { + am_ppm = 0; + scale = FM10K_COMP_PPM_SCALE; + } + + ppm = num_ppm + am_ppm; + timeout = scale / ppm; + + if (timeout >= 0xffff) + return 0xffff; + else + return timeout; +} + + +static int +fm10k_switch_configure_epls(struct fm10k_hw *hw, struct fm10k_switch *sw) +{ + struct fm10k_ext_ports *ext_ports = sw->ext_ports; + struct fm10k_ext_port *port; + struct fm10k_device_info *cfg = sw->info; + u32 mac_cfg[FM10K_SW_MAC_CFG_ARRAY_SIZE]; + u32 data, pcs, qpl; + u32 pcstmp; + unsigned int i, j; + unsigned int dic_enable; + unsigned int anti_bubble_wm; + unsigned int rate_fifo_wm; + unsigned int rate_fifo_slow_inc, rate_fifo_fast_inc; + int error; + u16 timeout; + + cfg = fm10k_get_device_info(hw); + if (cfg == NULL) + return -1; + + /* + * Assumptions: + * - All external interfaces are the same speed + * - 1G/10G ports are packed into the minimum number of EPLs + * - quad-mode EPLs use lane 0 as master + * - The lowest numbered PEPs are used + * - PEPs are always used in x8 mode. + */ + switch (cfg->ext_port_speed) { + case 10: + pcs = FM10K_SW_EPL_PCS_SEL_10GBASER; + qpl = FM10K_SW_EPL_QPL_MODE_L1_L1_L1_L1; + dic_enable = 1; + anti_bubble_wm = 5; + rate_fifo_wm = 3; + rate_fifo_slow_inc = 12; + rate_fifo_fast_inc = 13; + break; + /* XXX what is the physical config for 25G? */ + case 25: + pcs = FM10K_SW_EPL_PCS_SEL_100GBASER; + qpl = FM10K_SW_EPL_QPL_MODE_L1_L1_L1_L1; + dic_enable = 0; + anti_bubble_wm = 6; + rate_fifo_wm = 3; + rate_fifo_slow_inc = 32; + rate_fifo_fast_inc = 33; + break; + case 40: + pcs = FM10K_SW_EPL_PCS_SEL_40GBASER; + qpl = FM10K_SW_EPL_QPL_MODE_L4_XX_XX_XX; + dic_enable = 1; + anti_bubble_wm = 4; + rate_fifo_wm = 5; + rate_fifo_slow_inc = 51; + rate_fifo_fast_inc = 52; + break; + case 100: + pcs = FM10K_SW_EPL_PCS_SEL_100GBASER; + qpl = FM10K_SW_EPL_QPL_MODE_L4_XX_XX_XX; + dic_enable = 1; + anti_bubble_wm = 4; + rate_fifo_wm = 3; + rate_fifo_slow_inc = 129; + rate_fifo_fast_inc = 130; + break; + default: + error = -1; + goto done; + } + + /* + * EPL_CFG_A + * + * Mark all used lanes as active and all unused lanes as + * inactive. Adjust timeout and skew tolerance values for EPLs that + * are used. + */ + for (i = 0; i < FM10K_SW_EPLS_MAX; i++) { + data = fm10k_read_switch_reg(sw, FM10K_SW_EPL_CFG_A(i)); + data &= ~FM10K_SW_EPL_CFG_A_ACTIVE_QUAD; + if (FM10K_SW_EXT_PORTS_EPL_USED(ext_ports, i)) { + for (j = 0; j < ext_ports->num_ports; j++) { + port = &ext_ports->ports[j]; + if (port->eplno == i) + data |= port->is_quad ? + FM10K_SW_EPL_CFG_A_ACTIVE_QUAD : + FM10K_SW_EPL_CFG_A_ACTIVE + (port->first_lane); + } + FM10K_SW_REPLACE_REG_FIELD(data, + EPL_CFG_A_TIMEOUT, 19, data); + FM10K_SW_REPLACE_REG_FIELD(data, + EPL_CFG_A_SKEW_TOLERANCE, 38, data); + } + fm10k_write_switch_reg(sw, FM10K_SW_EPL_CFG_A(i), data); + } + + /* + * EPL_CFG_B + * + * Disable all unused lanes and configure all used ones + * appropriately. For EPLs used in quad port mode, the master lane + * is the only one that gets configured. + */ + data = 0; + for (i = 0; i < FM10K_SW_EPLS_MAX; i++) { + if (FM10K_SW_EXT_PORTS_EPL_USED(ext_ports, i)) { + for (j = 0; j < FM10K_SW_EPL_LANES; j++) { + if (j < ext_ports->ports_per_epl) + pcstmp = pcs; + else + pcstmp = FM10K_SW_EPL_PCS_SEL_DISABLE; + data |= + FM10K_SW_MAKE_REG_FIELD_IDX + (EPL_CFG_B_PCS_SEL, j, pcstmp, j, j); + } + data |= + FM10K_SW_MAKE_REG_FIELD + (EPL_CFG_B_QPL_MODE, qpl); + } else { + for (j = 0; j < FM10K_SW_EPL_LANES; j++) + data |= + FM10K_SW_MAKE_REG_FIELD_IDX + (EPL_CFG_B_PCS_SEL, j, + FM10K_SW_EPL_PCS_SEL_DISABLE, j, j); + data |= + FM10K_SW_MAKE_REG_FIELD + (EPL_CFG_B_QPL_MODE, + FM10K_SW_EPL_QPL_MODE_XX_XX_XX_XX); + } + fm10k_write_switch_reg(sw, FM10K_SW_EPL_CFG_B(i), data); + } + + /* + * MAC_CFG, LINK_RULES and PCS_ML_BASER_CFG + * + * Only EPLs/lanes that are being used are initialized. All others + * are left at their defaults. + */ + for (i = 0; i < FM10K_SW_EPLS_MAX; i++) { + if (!FM10K_SW_EXT_PORTS_EPL_USED(ext_ports, i)) + continue; + for (j = 0; j < ext_ports->ports_per_epl; j++) { + fm10k_read_switch_array(sw, FM10K_SW_MAC_CFG(i, j), + mac_cfg, FM10K_SW_MAC_CFG_ARRAY_SIZE); + + /* dic enable */ + FM10K_SW_SET_ARRAY_BIT(mac_cfg, + MAC_CFG_TX_IDLE_ENABLE_DIC, + dic_enable, mac_cfg); + + /* ifg */ + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_TX_IDLE_MIN_IFG_BYTES, + 12, mac_cfg); + + /* tx pad size: (64 bytes + 8 preamble) / 4 */ + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_TX_MIN_COLUMNS, + 18, mac_cfg); + + /* tx clock compensation */ + timeout = + fm10k_switch_ppm_to_tx_clk_compensation_timeout(pcs, + 100); + FM10K_SW_SET_ARRAY_BIT(mac_cfg, + MAC_CFG_TX_CLOCK_COMPENSATION_ENABLE, + (timeout > 0), mac_cfg); + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_TX_CLOCK_COMPENSATION_TIMEOUT, + timeout, mac_cfg); + + FM10K_SW_SET_ARRAY_BIT(mac_cfg, + MAC_CFG_PREAMBLE_MODE, + 0, mac_cfg); + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_RX_MIN_FRAME_LENGTH, + 64, mac_cfg); + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_RX_MAX_FRAME_LENGTH, + FM10K_SW_PACKET_SIZE_MAX, mac_cfg); + FM10K_SW_SET_ARRAY_BIT(mac_cfg, + MAC_CFG_RX_IGNORE_IFG_ERRORS, + 0, mac_cfg); + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_TX_FCS_MODE, + FM10K_SW_TX_MAX_FCS_MODE_REPLACE_NORMAL, mac_cfg); + + FM10K_SW_SET_ARRAY_BIT(mac_cfg, + MAC_CFG_IEEE_1588_ENABLE, + 0, mac_cfg); + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_TX_DRAIN_MODE, + FM10K_SW_TX_MAC_DRAIN_MODE_DRAIN_NORMAL, mac_cfg); + + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_TX_PC_ACT_TIMEOUT, + 100, mac_cfg); + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_TX_PC_ACT_TIME_SCALE, + 3, mac_cfg); + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_TX_LPI_TIMEOUT, + 180, mac_cfg); + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_TX_LPI_TIME_SCALE, + 1, mac_cfg); + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_TX_LPI_HOLD_TIMEOUT, + 20, mac_cfg); + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_TX_LPI_HOLD_TIME_SCALE, + 1, mac_cfg); + + FM10K_SW_SET_ARRAY_BIT(mac_cfg, + MAC_CFG_TX_LPI_AUTOMATIC, + 1, mac_cfg); + FM10K_SW_SET_ARRAY_BIT(mac_cfg, + MAC_CFG_TX_LP_IDLE_REQUEST, + 0, mac_cfg); + + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_TX_FAULT_MODE, + FM10K_SW_TX_MAC_FAULT_MODE_NORMAL, mac_cfg); + + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_TX_ANTI_BUBBLE_WATERMARK, + anti_bubble_wm, mac_cfg); + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_TX_RATE_FIFO_WATERMARK, + rate_fifo_wm, mac_cfg); + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_TX_RATE_FIFO_FAST_INC, + rate_fifo_fast_inc, mac_cfg); + FM10K_SW_REPLACE_ARRAY_FIELD(mac_cfg, + MAC_CFG_TX_RATE_FIFO_SLOW_INC, + rate_fifo_slow_inc, mac_cfg); + + fm10k_write_switch_array(sw, FM10K_SW_MAC_CFG(i, j), + mac_cfg, FM10K_SW_MAC_CFG_ARRAY_SIZE); + + data = + fm10k_read_switch_reg(sw, + FM10K_SW_LINK_RULES(i, j)); + + /* link-up debounce params */ + FM10K_SW_REPLACE_REG_FIELD(data, + LINK_RULES_FAULT_TIME_SCALE_UP, + 4, data); + FM10K_SW_REPLACE_REG_FIELD(data, + LINK_RULES_FAULT_TICKS_UP, + 30, data); + + /* link-down debounce params */ + FM10K_SW_REPLACE_REG_FIELD(data, + LINK_RULES_FAULT_TIME_SCALE_DOWN, + 4, data); + FM10K_SW_REPLACE_REG_FIELD(data, + LINK_RULES_FAULT_TICKS_DOWN, + 5, data); + + FM10K_SW_REPLACE_REG_FIELD(data, + LINK_RULES_HEARTBEAT_TIME_SCALE, + cfg->ext_port_speed == 10 ? 4 : 0, data); + fm10k_write_switch_reg(sw, + FM10K_SW_LINK_RULES(i, j), data); + + /* XXX add 10GBASER config */ + } + + if (cfg->ext_port_speed != 10) + fm10k_write_switch_reg(sw, + FM10K_SW_PCS_ML_BASER_CFG(i), 0x00003fff); + } + + + /* + * LANE_CFG, LANE_SERDES_CFG, LANE_ENERGY_DETECT_CFG, + * LANE_SIGNAL_DETECT_CFG, and EPL_FIFO_ERROR_STATUS + * + * Only EPLs/lanes that are being used are initialized. All others + * are left at their defaults. + */ + for (i = 0; i < FM10K_SW_EPLS_MAX; i++) { + if (!FM10K_SW_EXT_PORTS_EPL_USED(ext_ports, i)) + continue; + for (j = 0; j < ext_ports->ports_per_epl; j++) { + fm10k_write_switch_reg(sw, + FM10K_SW_LANE_CFG(i, j), 0); + fm10k_write_switch_reg(sw, + FM10K_SW_LANE_SERDES_CFG(i, j), 0x1106); + + data = fm10k_read_switch_reg(sw, + FM10K_SW_LANE_ENERGY_DETECT_CFG(i, j)); + + data |= + FM10K_SW_LANE_ENERGY_DETECT_CFG_ED_MASK_RX_SIGNAL_OK | + FM10K_SW_LANE_ENERGY_DETECT_CFG_ED_MASK_RX_RDY | + FM10K_SW_LANE_ENERGY_DETECT_CFG_ED_MASK_RX_ACTIVITY; + + data &= + ~FM10K_SW_LANE_ENERGY_DETECT_CFG_ED_MASK_ENERGY_DETECT; + fm10k_write_switch_reg(sw, + FM10K_SW_LANE_ENERGY_DETECT_CFG(i, j), data); + + data = fm10k_read_switch_reg(sw, + FM10K_SW_LANE_SIGNAL_DETECT_CFG(i, j)); + + data &= + ~(FM10K_SW_LANE_SIGNAL_DETECT_CFG_SD_MASK_RX_SIGNAL_OK | + FM10K_SW_LANE_SIGNAL_DETECT_CFG_SD_MASK_RX_RDY); + + data |= + FM10K_SW_LANE_SIGNAL_DETECT_CFG_SD_MASK_RX_ACTIVITY | + FM10K_SW_LANE_SIGNAL_DETECT_CFG_SD_MASK_ENERGY_DETECT; + + fm10k_write_switch_reg(sw, + FM10K_SW_LANE_SIGNAL_DETECT_CFG(i, j), data); + + data = 0; + data |= ((1 << j) << 4) | (1 << j); + fm10k_write_switch_reg(sw, + FM10K_SW_EPL_FIFO_ERROR_STATUS(i), data); + } + } + + error = fm10k_epl_serdes_reset_and_load_all(sw); + if (error) + goto done; + + /* + * EPL_FIFO_ERROR_STATUS LINK_IP + */ + for (i = 0; i < FM10K_SW_EPLS_MAX; i++) { + if (!FM10K_SW_EXT_PORTS_EPL_USED(ext_ports, i)) + continue; + for (j = 0; j < ext_ports->ports_per_epl; j++) { + fm10k_write_switch_reg(sw, + FM10K_SW_LINK_IP(i, j), FM10K_SW_MASK32(31, 0)); + } + + data = 0; + data |= ((1 << j) << 4) | (1 << j); + fm10k_write_switch_reg(sw, + FM10K_SW_EPL_FIFO_ERROR_STATUS(i), data); + } +done: + return (error); +} + +int fm10k_switch_dpdk_port_no_get(struct fm10k_hw *hw); + +int +fm10k_switch_mirror_set(struct fm10k_hw *hw, u16 dest_port, u16 vlan) +{ + struct fm10k_switch *sw = &fm10k_sw; + int src_port = fm10k_switch_dpdk_port_no_get(hw); + + /* source port is external port number */ + if (src_port < 0 || src_port == dest_port) + return -1; + + return fm10k_ffu_mirror_set(sw, src_port, dest_port, vlan); +} + +int fm10k_switch_mirror_reset(struct fm10k_hw *hw) +{ + struct fm10k_switch *sw = &fm10k_sw; + int src_port = fm10k_switch_dpdk_port_no_get(hw); + + if (src_port < 0) + return -1; + + return fm10k_ffu_mirror_reset(sw, src_port); +} + + +typedef struct { + u8 lport; + u8 has_ftag; +} fm10k_sw_lport; + +static int +fm10k_switch_init(struct fm10k_hw *hw, struct fm10k_switch *sw) +{ + u32 data; + unsigned int num_lports = sw->info->num_peps + FM10K_SW_EPLS_SUPPORTED; + fm10k_sw_lport all_lports[num_lports]; + struct fm10k_device_info *cfg = sw->info; + unsigned int i; + unsigned int is_quad; + unsigned int table_idx; + int error; + u32 watermark; + u64 data64, data64_2; + + /* + * Build list of all logical ports that might appear in the + * scheduler program. Note that for any particular card + * configuration, not all of these logical ports may be used. + */ + table_idx = 0; + for (i = 0; i < sw->info->num_peps; i++, table_idx++) { + all_lports[table_idx].lport = sw->pep_map[i].logical_port; + all_lports[table_idx].has_ftag = 1; + } + for (i = 0; i < FM10K_SW_EPLS_SUPPORTED; i++, table_idx++) { + all_lports[table_idx].lport = sw->epl_map[i].logical_port; + all_lports[table_idx].has_ftag = 0; + } + + if (table_idx != num_lports) { + FM10K_SW_ERR("fm10k switch lport table construction error"); + return -1; + } + + /* + * Reset the switch to get to the default state + */ + data = fm10k_read_switch_reg(sw, FM10K_SW_SOFT_RESET); + data &= ~FM10K_SW_SOFT_RESET_SWITCH_READY; + fm10k_write_switch_reg(sw, FM10K_SW_SOFT_RESET, data); + fm10k_write_flush(sw); + + usec_delay(100); + data = fm10k_read_switch_reg(sw, FM10K_SW_SOFT_RESET); + data |= FM10K_SW_SOFT_RESET_SWITCH_RESET | + FM10K_SW_SOFT_RESET_EPL_RESET; + fm10k_write_switch_reg(sw, FM10K_SW_SOFT_RESET, data); + fm10k_write_flush(sw); + usec_delay(1000); + + /* Clear memories */ + fm10k_write_switch_reg64(sw, FM10K_SW_BIST_CTRL, + FM10K_SW_BIST_CTRL_BIST_MODE_FABRIC | + FM10K_SW_BIST_CTRL_BIST_MODE_TUNNEL | + FM10K_SW_BIST_CTRL_BIST_MODE_EPL); + fm10k_write_flush(sw); + + fm10k_write_switch_reg64(sw, FM10K_SW_BIST_CTRL, + FM10K_SW_BIST_CTRL_BIST_MODE_FABRIC | + FM10K_SW_BIST_CTRL_BIST_MODE_TUNNEL | + FM10K_SW_BIST_CTRL_BIST_MODE_EPL | + FM10K_SW_BIST_CTRL_BIST_RUN_FABRIC | + FM10K_SW_BIST_CTRL_BIST_RUN_TUNNEL | + FM10K_SW_BIST_CTRL_BIST_RUN_EPL); + fm10k_write_flush(sw); + usec_delay(800); + + fm10k_write_switch_reg64(sw, FM10K_SW_BIST_CTRL, 0); + fm10k_write_flush(sw); + + data = fm10k_read_switch_reg(sw, FM10K_SW_SOFT_RESET); + data &= ~(FM10K_SW_SOFT_RESET_SWITCH_RESET | + FM10K_SW_SOFT_RESET_EPL_RESET); + fm10k_write_switch_reg(sw, FM10K_SW_SOFT_RESET, data); + fm10k_write_flush(sw); + /* ensure switch reset is deasserted for at least 100ns */ + usec_delay(1); + + sw->epl_sbus = fm10k_sbus_attach(sw, "EPL", FM10K_SW_SBUS_EPL_CFG); + if (sw->epl_sbus == NULL) { + error = -1; + goto done; + } + + /* Clear non-BIST accessible pause state memories */ + for (i = 0; i < FM10K_SW_LOGICAL_PORTS_MAX; i++) { + fm10k_write_switch_reg64(sw, + FM10K_SW_CM_EGRESS_PAUSE_COUNT(i, 0), 0); + fm10k_write_switch_reg64(sw, + FM10K_SW_CM_EGRESS_PAUSE_COUNT(i, 1), 0); + } + + /* Initialize RXQ_MCAST list */ + for (i = 0; i < FM10K_SW_SCHED_RXQ_STORAGE_POINTERS_ENTRIES; i++) { + fm10k_write_switch_reg64(sw, + FM10K_SW_SCHED_RXQ_STORAGE_POINTERS(i), + FM10K_SW_MAKE_REG_FIELD + (SCHED_RXQ_STORAGE_POINTERS_HEAD_PAGE, i) | + FM10K_SW_MAKE_REG_FIELD + (SCHED_RXQ_STORAGE_POINTERS_TAIL_PAGE, i)); + } + for (i = 0; + i < FM10K_SW_SCHED_RXQ_FREELIST_INIT_ENTRIES - + FM10K_SW_SCHED_RXQ_STORAGE_POINTERS_ENTRIES; i++) { + fm10k_write_switch_reg(sw, + FM10K_SW_SCHED_RXQ_FREELIST_INIT, + FM10K_SW_MAKE_REG_FIELD + (SCHED_RXQ_FREELIST_INIT_ADDRESS, + i + FM10K_SW_SCHED_RXQ_STORAGE_POINTERS_ENTRIES)); + } + /* Initialize TXQ list */ + for (i = 0; i < FM10K_SW_SCHED_TXQ_HEAD_PERQ_ENTRIES; i++) { + fm10k_write_switch_reg(sw, + FM10K_SW_SCHED_TXQ_HEAD_PERQ(i), + FM10K_SW_MAKE_REG_FIELD(SCHED_TXQ_HEAD_PERQ_HEAD, i)); + fm10k_write_switch_reg(sw, + FM10K_SW_SCHED_TXQ_TAIL0_PERQ(i), + FM10K_SW_MAKE_REG_FIELD(SCHED_TXQ_TAIL0_PERQ_TAIL, i)); + fm10k_write_switch_reg(sw, + FM10K_SW_SCHED_TXQ_TAIL1_PERQ(i), + FM10K_SW_MAKE_REG_FIELD(SCHED_TXQ_TAIL1_PERQ_TAIL, i)); + } + for (i = 0; + i < FM10K_SW_SCHED_TXQ_FREELIST_INIT_ENTRIES - + FM10K_SW_SCHED_TXQ_HEAD_PERQ_ENTRIES; i++) { + fm10k_write_switch_reg(sw, + FM10K_SW_SCHED_TXQ_FREELIST_INIT, + FM10K_SW_MAKE_REG_FIELD + (SCHED_TXQ_FREELIST_INIT_ADDRESS, + i + FM10K_SW_SCHED_TXQ_HEAD_PERQ_ENTRIES)); + } + /* Initialize free segment list */ + for (i = 0; i < FM10K_SW_LOGICAL_PORTS_MAX; i++) { + fm10k_write_switch_reg(sw, + FM10K_SW_SCHED_SSCHED_RX_PERPORT(i), + FM10K_SW_MAKE_REG_FIELD(SCHED_SSCHED_RX_PERPORT_NEXT, i)); + } + for (i = 0; + i < FM10K_SW_SCHED_FREELIST_INIT_ENTRIES - + FM10K_SW_LOGICAL_PORTS_MAX; i++) { + fm10k_write_switch_reg(sw, + FM10K_SW_SCHED_FREELIST_INIT, + FM10K_SW_MAKE_REG_FIELD + (SCHED_FREELIST_INIT_ADDRESS, + i + FM10K_SW_LOGICAL_PORTS_MAX)); + } + /* Disable switch scan chain */ + fm10k_write_switch_reg(sw, + FM10K_SW_SCAN_DATA_IN, + FM10K_SW_SCAN_DATA_IN_UPDATE_NODES | + FM10K_SW_SCAN_DATA_IN_PASSTHRU); + + error = fm10k_switch_configure_epls(hw, sw); + if (error) + goto done; + + /* + * XXX for now configure store-and-forward between PEPs and external + * ports regardless of relative speeds + */ + for (i = 0; i < num_lports; i++) { + fm10k_write_switch_reg64(sw, + FM10K_SW_SAF_MATRIX(all_lports[i].lport), + FM10K_SW_SAF_MATRIX_ENABLE_SNF_ALL_PORTS); + } + + /* Disable MTU violation trap */ + data = fm10k_read_switch_reg(sw, FM10K_SW_SYS_CFG_1); + data &= ~FM10K_SW_SYS_CFG_1_TRAP_MTU_VIOLATIONS; + fm10k_write_switch_reg(sw, FM10K_SW_SYS_CFG_1, data); + + /* Disable ingress VLAN filtering and learning */ + for (i = 0; i < num_lports; i++) { + fm10k_write_switch_reg(sw, + FM10K_SW_PORT_CFG_3(all_lports[i].lport), 0); + } + /* + * Make all ports members of every VLAN, and configure every VLAN to + * use MST instance 0. + */ + data64 = 0; + for (i = 0; i < num_lports; i++) + data64 |= + FM10K_SW_INGRESS_VID_TABLE_MEMBERSHIP + (all_lports[i].lport); + for (i = 0; i < FM10K_SW_INGRESS_VID_TABLE_ENTRIES; i++) { + fm10k_write_switch_reg128(sw, FM10K_SW_INGRESS_VID_TABLE(i), + FM10K_SW_INGRESS_VID_TABLE_REFLECT, data64); + } + data64 = 0; + for (i = 0; i < num_lports; i++) + data64 |= + FM10K_SW_EGRESS_VID_TABLE_MEMBERSHIP + (all_lports[i].lport); + for (i = 0; i < FM10K_SW_EGRESS_VID_TABLE_ENTRIES; i++) + fm10k_write_switch_reg128(sw, + FM10K_SW_EGRESS_VID_TABLE(i), 0, data64); + + // Init MOD_VLAN_TAG_VID1_MAP + for (i = 0; i < FM10K_SW_INGRESS_VID_TABLE_ENTRIES; i++) { + data64 = i; + data64 = data64 << 48; + fm10k_write_switch_reg64(sw, + 0xE80000 + 0x2 * i + 0x20000, data64); + } + + /* Configure MST instance 0 to forward for all ports */ + data64 = 0; + for (i = 0; i < num_lports; i++) + data64 |= + FM10K_SW_EGRESS_MST_TABLE_FORWARDING + (all_lports[i].lport); + fm10k_write_switch_reg64(sw, + FM10K_SW_EGRESS_MST_TABLE(0), data64); + + data64 = 0; + data64_2 = 0; + for (i = 0; i < num_lports; i++) { + if (all_lports[i].lport < + FM10K_SW_INGRESS_MST_TABLE_PORTS_PER_TABLE) { + data64 |= + FM10K_SW_MAKE_REG_FIELD_IDX64 + (INGRESS_MST_TABLE_STP_STATE, + all_lports[i].lport, + FM10K_SW_INGRESS_MST_TABLE_STP_STATE_FORWARD, + all_lports[i].lport, all_lports[i].lport); + } else { + data64_2 |= + FM10K_SW_MAKE_REG_FIELD_IDX64 + (INGRESS_MST_TABLE_STP_STATE, + all_lports[i].lport, + FM10K_SW_INGRESS_MST_TABLE_STP_STATE_FORWARD, + all_lports[i].lport, all_lports[i].lport); + } + } + fm10k_write_switch_reg64(sw, + FM10K_SW_INGRESS_MST_TABLE(0, 0), data64); + fm10k_write_switch_reg64(sw, + FM10K_SW_INGRESS_MST_TABLE(1, 0), data64_2); + + for (i = 0; i < num_lports; i++) { + data64 = fm10k_read_switch_reg64(sw, + FM10K_SW_PARSER_PORT_CFG_1(all_lports[i].lport)); + data64 |= FM10K_SW_PARSER_PORT_CFG_1_VLAN1_TAG(0) | + FM10K_SW_PARSER_PORT_CFG_1_VLAN2_TAG(0); + fm10k_write_switch_reg64(sw, + FM10K_SW_PARSER_PORT_CFG_1(all_lports[i].lport), data64); + + /* + * Configure tags for f-tagged lports + */ + if (all_lports[i].has_ftag) { + data64 = fm10k_read_switch_reg64(sw, + FM10K_SW_PARSER_PORT_CFG_1(all_lports[i].lport)); + data64 |= FM10K_SW_PARSER_PORT_CFG_1_FTAG; + fm10k_write_switch_reg64(sw, + FM10K_SW_PARSER_PORT_CFG_1(all_lports[i].lport), + data64); + + data64 = fm10k_read_switch_reg64(sw, + FM10K_SW_MOD_PER_PORT_CFG_2(all_lports[i].lport)); + data64 |= FM10K_SW_MOD_PER_PORT_CFG_2_FTAG; + fm10k_write_switch_reg64(sw, + FM10K_SW_MOD_PER_PORT_CFG_2(all_lports[i].lport), + data64); + } + data64 = fm10k_read_switch_reg64(sw, + FM10K_SW_PARSER_PORT_CFG_2(all_lports[i].lport)); + data64 |= FM10K_SW_PARSER_PORT_CFG_2_PARSE_L3; + data64 |= FM10K_SW_PARSER_PORT_CFG_2_PARSE_L4; + fm10k_write_switch_reg64(sw, + FM10K_SW_PARSER_PORT_CFG_2(all_lports[i].lport), data64); + } + + /* + * Assign default SGLORTs for the EPL ports in the PARSER config. + * This isn't necessary for the PEPs as for those, as all frames + * ingressing from PEPs are tagged with an SGLORT that is derived + * from a per-tx-queue setting. + * + * The PORT_CFG_ISL register offset is determined by logical port + * number and the register contents determined by the corresponding + * SGLORT. + */ + for (i = 0; i < FM10K_SW_EPLS_SUPPORTED; i++) { + fm10k_write_switch_reg(sw, + FM10K_SW_PORT_CFG_ISL + (fm10k_sw.epl_map[i].logical_port), + FM10K_SW_MAKE_REG_FIELD + (PORT_CFG_ISL_SGLORT, + fm10k_sw.epl_map[i].glort)); + } + + /* + * FFU counter, 11.10.3.5 POLICER_CFG[0..3] + */ + fm10k_write_switch_reg64(sw, + 0xE40000 + 0x2 * FM10K_SW_FFU_CNT_BANK + 0x11000, + FM10K_SW_POLICER_LAST); + + /* + * 11.10.3.1 POLICER_CFG_4K[0..1][0..4095] + * 11.10.3.2 POLICER_CFG_512[0..1][0..511] + */ + for (i = 0; i < FM10K_SW_FFU_CNT_MAX; i++) { + if (FM10K_SW_FFU_CNT_BANK < 2) { + fm10k_read_switch_reg64(sw, + 0xE40000 + 0x2000 * FM10K_SW_FFU_CNT_BANK + + 0x2 * (i + FM10K_SW_FFU_CNT_START) + 0x0); + } else { + fm10k_read_switch_reg64(sw, + 0xE40000 + 0x400 * (FM10K_SW_FFU_CNT_BANK - 2) + + 0x2 * (i + FM10K_SW_FFU_CNT_START) + 0x4000); + } + } + + /* + * FFU + * + * The FFU is programmed to match on SGLORT and to set the DGLORT to + * a unique value corresponding to the given SGLORT. One TCAM entry + * is required per host port per PEP and one per external interface. + * Only slice 0 is used. + */ + if (fm10k_ffu_init(sw, sw->dpdk_cfg) < 0) + return -1; + + /* + * Program the segment scheduler (see tables at top) + * + * The TX and RX schedules are the same. Page 0 is used. + */ + if (cfg->ext_port_speed == 40 || cfg->ext_port_speed == 100) + is_quad = 1; + else + is_quad = 0; + for (i = 0; + i < sizeof(fm10k_sched_prog) / + sizeof(fm10k_sched_prog[0]); + i++) { + /* + * In addition to explicit idle cycles, non-quad port + * entries are converted to idle cycles if the interfaces + * are configured in quad-port mode. + */ + if (fm10k_sched_prog[i].idle || + (!fm10k_sched_prog[i].quad && is_quad)) + data = FM10K_SW_SCHED_SCHEDULE_IDLE; + else + data = FM10K_SW_SCHED_SCHEDULE_ENTRY + (fm10k_sched_prog[i].phys, + fm10k_sched_prog[i].log, + (fm10k_sched_prog[i].quad == FM10K_SW_QUAD_40_100) ? + is_quad : fm10k_sched_prog[i].quad); + fm10k_write_switch_reg(sw, + FM10K_SW_SCHED_RX_SCHEDULE(0, i), data); + fm10k_write_switch_reg(sw, + FM10K_SW_SCHED_TX_SCHEDULE(0, i), data); + } + + fm10k_write_switch_reg(sw, FM10K_SW_SCHED_SCHEDULE_CTRL, + FM10K_SW_SCHED_SCHEDULE_CTRL_RX_ENABLE | + FM10K_SW_MAKE_REG_FIELD + (SCHED_SCHEDULE_CTRL_RX_MAX_INDEX, + (sizeof(fm10k_sched_prog) / + sizeof(fm10k_sched_prog[0])) - 1) | + FM10K_SW_SCHED_SCHEDULE_CTRL_TX_ENABLE | + FM10K_SW_MAKE_REG_FIELD + (SCHED_SCHEDULE_CTRL_TX_MAX_INDEX, + (sizeof(fm10k_sched_prog) / + sizeof(fm10k_sched_prog[0])) - 1)); + + /* Per 5.7.10.4 */ + watermark = FM10K_SW_MEM_POOL_SEGS_MAX - + ((sw->info->num_peps + + FM10K_SW_EPLS_SUPPORTED * FM10K_SW_EPL_LANES) * + FM10K_SW_HOWMANY(FM10K_SW_PACKET_SIZE_MAX, + FM10K_SW_MEM_POOL_SEG_SIZE, FM10K_SW_MEM_POOL_SEG_SIZE)) - + FM10K_SW_MEM_POOL_SEGS_RSVD; + fm10k_write_switch_reg(sw, FM10K_SW_CM_GLOBAL_WM, + FM10K_SW_MAKE_REG_FIELD(CM_GLOBAL_WM_WATERMARK, watermark)); + fm10k_write_switch_reg(sw, FM10K_SW_CM_GLOBAL_CFG, + FM10K_SW_CM_GLOBAL_CFG_WM_SWEEP_EN | + FM10K_SW_CM_GLOBAL_CFG_PAUSE_GEN_SWEEP_EN | + FM10K_SW_CM_GLOBAL_CFG_PAUSE_REC_SWEEP_EN | + FM10K_SW_MAKE_REG_FIELD + (CM_GLOBAL_CFG_NUM_SWEEPER_PORTS, + FM10K_SW_LOGICAL_PORTS_MAX)); + + /* Configure stats counters */ + data = FM10K_SW_RX_STATS_CFG_ENABLE_ALL_BANKS; + data64 = + FM10K_SW_MOD_STATS_CFG_ENABLE_GROUP_7 | + FM10K_SW_MOD_STATS_CFG_ENABLE_GROUP_8; + for (i = 0; i < num_lports; i++) { + fm10k_write_switch_reg(sw, + FM10K_SW_RX_STATS_CFG(all_lports[i].lport), + data | + ((all_lports[i].has_ftag) ? + FM10K_SW_MAKE_REG_FIELD + (RX_STATS_CFG_PER_FRAME_ADJUSTMENT, + FM10K_SW_FTAG_SIZE) : + 0)); + fm10k_write_switch_reg64(sw, + FM10K_SW_MOD_STATS_CFG(all_lports[i].lport), + data64); + } + + /* Transition switch to ready */ + data = fm10k_read_switch_reg(sw, FM10K_SW_SOFT_RESET); + data |= FM10K_SW_SOFT_RESET_SWITCH_READY; + fm10k_write_switch_reg(sw, FM10K_SW_SOFT_RESET, data); + + done: + return (error); +} + + +static void +fm10k_switch_leds_init(struct fm10k_switch *sw) +{ + struct fm10k_device_info *cfg = sw->info; + unsigned int i; + uint8_t addr; + uint32_t data; + int max; + + switch (FM10K_SW_CARD_ID(cfg->subvendor, cfg->subdevice)) { + case FM10K_SW_CARD(SILICOM, PE340G2DBIR_QS41): + case FM10K_SW_CARD(SILICOM_RB, PE340G2DBIR_QS41): + case FM10K_SW_CARD(SILICOM, PE340G2DBIR_QS43): + case FM10K_SW_CARD(SILICOM_RB, PE340G2DBIR_QS43): + case FM10K_SW_CARD(SILICOM, PE340G2DBIR_QL4): + case FM10K_SW_CARD(SILICOM_RB, PE340G2DBIR_QL4): + FM10K_SW_I2C_REQ_LOCK(sw->i2c); + /* + * Set up the first PCA9545 mux so we can get at the PCA9635 + * that the LED control lines are connected to. + */ + fm10k_i2c_write8(sw->i2c, 0x70, 0x04); + + /* + * PCA9635 initialization + * only differences from defaults are noted + */ + + /* MODE1 - put into sleep mode to ensure it is brought out + * of sleep without violating the oscillator startup wait + */ + fm10k_i2c_write16(sw->i2c, 0x6a, 0x00, 0x10); + fm10k_udelay(10); + + /* MODE1 - normal mode, disable all call */ + fm10k_i2c_write16(sw->i2c, 0x6a, 0x00, 0x00); + + /* Wait for oscillator to stabilize after coming out of sleep */ + fm10k_udelay(500); + + /* MODE2 - group control is blinking, open drain outputs, + * OE high -> LEDn = 0 + */ + fm10k_i2c_write16(sw->i2c, 0x6a, 0x01, 0x20); + + /* PWM0 - 100% duty cycle */ + fm10k_i2c_write16(sw->i2c, 0x6a, 0x02, 0xff); + + /* PWM1 - 100% duty cycle */ + fm10k_i2c_write16(sw->i2c, 0x6a, 0x03, 0xff); + + /* GRPPWM - 50% blink duty cycle */ + fm10k_i2c_write16(sw->i2c, 0x6a, 0x12, 0x80); + + /* GRPFREQ - FM10K_LED_BLINKS_PER_SECOND */ + if (24 / FM10K_LED_BLINKS_PER_SECOND > 1) + max = 24 / FM10K_LED_BLINKS_PER_SECOND; + else + max = 1; + fm10k_i2c_write16(sw->i2c, 0x6a, 0x13, max - 1); + + FM10K_SW_I2C_REQ_UNLOCK(sw->i2c); + break; + case FM10K_SW_CARD(SILICOM, PE3100G2DQIR_QXSL4): + case FM10K_SW_CARD(SILICOM, PE3100G2DQIR_QXSL4_REV_2): + FM10K_SW_I2C_REQ_LOCK(sw->i2c); + /* + * Set up the first PCA9545 mux so we can get at the PCA9635s + * that the LED control lines are connected to. + */ + fm10k_i2c_write8(sw->i2c, 0x70, 0x01); + + /* + * PCA9635 initialization + * only differences from defaults are noted + */ + + addr = 0x6a; + for (i = 0; i < 2; i++) { + /* MODE1 - put into sleep mode to ensure it is + * brought out of sleep without violating the + * oscillator startup wait + */ + fm10k_i2c_write16(sw->i2c, addr, 0x00, 0x10); + fm10k_udelay(10); + + /* MODE1 - normal mode, disable all call */ + fm10k_i2c_write16(sw->i2c, addr, 0x00, 0x00); + + /* MODE2 - group control is blinking, open drain + * outputs, OE high -> LEDn = 0 + */ + fm10k_i2c_write16(sw->i2c, addr, 0x01, 0x20); + + /* Wait for oscillator to stabilize + * after coming out of sleep + */ + fm10k_udelay(500); + + /* PWM0 - 100% duty cycle */ + fm10k_i2c_write16(sw->i2c, addr, 0x02, 0xff); + + /* PWM3 - 100% duty cycle */ + fm10k_i2c_write16(sw->i2c, addr, 0x05, 0xff); + + /* GRPPWM - 50% blink duty cycle */ + fm10k_i2c_write16(sw->i2c, addr, 0x12, 0x80); + + /* GRPFREQ - FM10K_LED_BLINKS_PER_SECOND */ + if (24 / FM10K_LED_BLINKS_PER_SECOND > 1) + max = 24 / FM10K_LED_BLINKS_PER_SECOND; + else + max = 1; + fm10k_i2c_write16(sw->i2c, addr, 0x13, max - 1); + + addr = 0x69; + } + FM10K_SW_I2C_REQ_UNLOCK(sw->i2c); + break; + + case FM10K_SW_CARD(SILICOM, PE3100G2DQIRL_QXSL4): + FM10K_SW_I2C_REQ_LOCK(sw->i2c); + addr = 0x62; + fm10k_i2c_write16(sw->i2c, addr, 0x03, 0x88); + fm10k_i2c_write16(sw->i2c, addr, 0x01, 0x0); + + data = fm10k_read_switch_reg(sw, 0xc2b); + data |= 1 << 24; + fm10k_write_switch_reg(sw, 0xc2b, data); + FM10K_SW_I2C_REQ_UNLOCK(sw->i2c); + break; + + case FM10K_SW_CARD(SILICOM, PE3100G2DQIRM_QXSL4): + FM10K_SW_I2C_REQ_LOCK(sw->i2c); + /* + * PCA9538 initialization + * only differences from defaults are noted + */ + /* + * 00000000: No link + * 00000001: 10G Port 0 + * 00000010: 40G Port 0 + * 00000100: 25G Port 0 + * 00001000: 100G Port 0 + */ + addr = 0x62; + fm10k_i2c_write16(sw->i2c, addr, 0x03, 0x0); + fm10k_i2c_write16(sw->i2c, addr, 0x01, 0x0); + + addr = 0x65; + fm10k_i2c_write16(sw->i2c, addr, 0x03, 0xf0); + fm10k_i2c_write16(sw->i2c, addr, 0x01, 0x0); + + addr = 0x66; + fm10k_i2c_write16(sw->i2c, addr, 0x03, 0x0); + fm10k_i2c_write16(sw->i2c, addr, 0x01, 0x0); + + /* set LEC_CFG */ + data = fm10k_read_switch_reg(sw, 0xc2b); + data |= 1 << 24; + fm10k_write_switch_reg(sw, 0xc2b, data); + + /* port from rdifd, LED */ + fm10k_gpio_output_set(sw, 4, 0); + FM10K_SW_I2C_REQ_UNLOCK(sw->i2c); + break; + + default: + FM10K_SW_ERR("don't know how to operate LEDs for this card " + "(subvendor=0x%04x subdevice=0x%04x)", + cfg->subvendor, cfg->subdevice); + break; + } +} + + +static unsigned int +fm10k_switch_pca9635_led_bits(uint8_t led_flags) +{ + unsigned int bits; + + if (led_flags & FM10K_SW_EXT_PORT_LED_FLAG_UP) { + if (led_flags & FM10K_SW_EXT_PORT_LED_FLAG_ACTIVE) + bits = 0x3; /* group blink */ + else + bits = 0x1; /* full on */ + } else { + bits = 0; /* off */ + } + return (bits); +} + +static void +fm10k_switch_process_leds(void *ctx) +{ + struct fm10k_switch *sw = ctx; + struct fm10k_device_info *cfg = sw->info; + struct fm10k_ext_ports *ports = sw->ext_ports; + struct fm10k_ext_port *port; + unsigned int i; + unsigned int num_ports = ports->num_ports; + uint32_t data; + uint8_t update_port[num_ports]; + uint8_t led_flags = 0, read; + uint8_t addr; + + FM10K_SW_SWITCH_LOCK(sw); + + if (sw->master_hw->sw_addr == NULL) { + FM10K_SW_SWITCH_UNLOCK(sw); + return; + } + + for (i = 0; i < num_ports; i++) { + port = &ports->ports[i]; + data = fm10k_read_switch_reg(sw, + FM10K_SW_EPL_LED_STATUS(port->eplno)); + led_flags = + ((data & FM10K_SW_EPL_LED_STATUS_PORT_LINK_UP + (port->first_lane)) ? + FM10K_SW_EXT_PORT_LED_FLAG_UP : 0) | + ((data & + (FM10K_SW_EPL_LED_STATUS_PORT_TRANSMITTING + (port->first_lane) | + FM10K_SW_EPL_LED_STATUS_PORT_RECEIVING + (port->first_lane))) ? + FM10K_SW_EXT_PORT_LED_FLAG_ACTIVE : 0); + update_port[i] = (led_flags != port->last_led_flags); + port->last_led_flags = led_flags; + } + FM10K_SW_SWITCH_UNLOCK(sw); + + switch (FM10K_SW_CARD_ID(cfg->subvendor, cfg->subdevice)) { + case FM10K_SW_CARD(SILICOM, PE340G2DBIR_QS41): + case FM10K_SW_CARD(SILICOM_RB, PE340G2DBIR_QS41): + case FM10K_SW_CARD(SILICOM, PE340G2DBIR_QS43): + case FM10K_SW_CARD(SILICOM_RB, PE340G2DBIR_QS43): + case FM10K_SW_CARD(SILICOM, PE340G2DBIR_QL4): + case FM10K_SW_CARD(SILICOM_RB, PE340G2DBIR_QL4): + FM10K_SW_I2C_REQ_LOCK(sw->i2c); + /* + * Set up the first PCA9545 mux so we can get at the PCA9635 + * that the LED control lines are connected to. + */ + fm10k_i2c_write8(sw->i2c, 0x70, 0x04); + + if (!(update_port[0] || update_port[1])) { + FM10K_SW_I2C_REQ_UNLOCK(sw->i2c); + break; + } + + led_flags = + fm10k_switch_pca9635_led_bits + (ports->ports[0].last_led_flags) | + (fm10k_switch_pca9635_led_bits + (ports->ports[1].last_led_flags) << 2); + + fm10k_i2c_write16(sw->i2c, 0x6a, 0x14, led_flags); + FM10K_SW_I2C_REQ_UNLOCK(sw->i2c); + break; + + case FM10K_SW_CARD(SILICOM, PE3100G2DQIR_QXSL4): + case FM10K_SW_CARD(SILICOM, PE3100G2DQIR_QXSL4_REV_2): + FM10K_SW_I2C_REQ_LOCK(sw->i2c); + /* + * Set up the first PCA9545 mux so we can get at the PCA9635s + * that the LED control lines are connected to. + */ + fm10k_i2c_write8(sw->i2c, 0x70, 0x01); + + /* XXX will need to update for QSFPs operating + * as four independent lanes/ports + */ + for (i = 0; i < 2; i++) { + if (update_port[i] == 0) + continue; + + addr = (i == 0) ? 0x6a : 0x69; + + port = &ports->ports[i]; + led_flags = + fm10k_switch_pca9635_led_bits + (port->last_led_flags); + + switch (port->lane_speed * port->num_lanes) { + case 100: + fm10k_i2c_write16(sw->i2c, + addr, 0x14, led_flags); + break; + case 40: + fm10k_i2c_write16(sw->i2c, + addr, 0x14, led_flags << 6); + break; + } + } + FM10K_SW_I2C_REQ_UNLOCK(sw->i2c); + break; + + case FM10K_SW_CARD(SILICOM, PE3100G2DQIRL_QXSL4): + FM10K_SW_I2C_REQ_LOCK(sw->i2c); + led_flags = 0; + addr = 0x62; + for (i = 0; i < 2; i++) { + if (!update_port[i]) + continue; + port = &ports->ports[i]; + if (port->last_led_flags & + FM10K_SW_EXT_PORT_LED_FLAG_UP) { + switch (port->lane_speed * port->num_lanes) { + case 100: + case 25: + led_flags |= 0x6 << (4 * i); /* 100G */ + break; + case 40: + case 10: + led_flags |= 0x4 << (4 * i); /* 40G */ + break; + default: + led_flags = 0; + } + } else { + led_flags = 0; /* off */ + } + } + + if (update_port[0] || update_port[1]) { + fm10k_i2c_read8_ext(sw->i2c, addr, 0x1, &read); + if (update_port[0]) + led_flags |= read & 0xf0; + else + led_flags |= read & 0xf; + fm10k_i2c_write16(sw->i2c, addr, 0x1, led_flags); + fm10k_i2c_read8_ext(sw->i2c, addr, 0x1, &read); + } + FM10K_SW_I2C_REQ_UNLOCK(sw->i2c); + break; + + case FM10K_SW_CARD(SILICOM, PE3100G2DQIRM_QXSL4): + FM10K_SW_I2C_REQ_LOCK(sw->i2c); + /* XXX will need to update for QSFPs + * operating as four independent lanes/ports + */ + for (i = 0; i < 2; i++) { + if (!update_port[i]) + continue; + /* + * 00000000: No link + * 00000001: 10G Port 0 + * 00000010: 40G Port 0 + * 00000100: 25G Port 0 + * 00001000: 100G Port 0 + */ + addr = (i == 0) ? 0x62 : 0x66; + port = &ports->ports[i]; + if (port->last_led_flags & + FM10K_SW_EXT_PORT_LED_FLAG_UP) { + switch (port->lane_speed * port->num_lanes) { + case 100: + led_flags = 0x08; /* 100G */ + break; + case 40: + led_flags = 0x02; /* 40G */ + break; + } + } else { + led_flags = 0; /* off */ + } + fm10k_i2c_write16(sw->i2c, + addr, 0x1, led_flags); + } + FM10K_SW_I2C_REQ_UNLOCK(sw->i2c); + break; + } +} + +static void * +fm10k_switch_leds_update(void *ctx) +{ + struct fm10k_switch *sw = ctx; + + while (sw->detaching == 0) { + fm10k_switch_process_leds(ctx); + usec_delay(FM10K_LED_POLL_INTERVAL_MS * 1000); + } + return NULL; +} + +static void +fm10k_switch_leds_off(struct fm10k_switch *sw, struct fm10k_ext_ports *ports) +{ + struct fm10k_device_info *cfg = sw->info; + struct fm10k_ext_port *port; + unsigned int i; + uint8_t led_flags; + uint8_t addr; + + switch (FM10K_SW_CARD_ID(cfg->subvendor, cfg->subdevice)) { + case FM10K_SW_CARD(SILICOM, PE340G2DBIR_QS41): + case FM10K_SW_CARD(SILICOM_RB, PE340G2DBIR_QS41): + case FM10K_SW_CARD(SILICOM, PE340G2DBIR_QS43): + case FM10K_SW_CARD(SILICOM_RB, PE340G2DBIR_QS43): + case FM10K_SW_CARD(SILICOM, PE340G2DBIR_QL4): + case FM10K_SW_CARD(SILICOM_RB, PE340G2DBIR_QL4): + FM10K_SW_I2C_REQ_LOCK(sw->i2c); + /* + * Set up the first PCA9545 mux so we can get at the PCA9635 + * that the LED control lines are connected to. + */ + fm10k_i2c_write8(sw->i2c, 0x70, 0x04); + + led_flags = + fm10k_switch_pca9635_led_bits(0) | + (fm10k_switch_pca9635_led_bits(0) << 2); + + fm10k_i2c_write16(sw->i2c, 0x6a, 0x14, led_flags); + FM10K_SW_I2C_REQ_UNLOCK(sw->i2c); + break; + + case FM10K_SW_CARD(SILICOM, PE3100G2DQIR_QXSL4): + case FM10K_SW_CARD(SILICOM, PE3100G2DQIR_QXSL4_REV_2): + case FM10K_SW_CARD(SILICOM, PE3100G2DQIRL_QXSL4): + case FM10K_SW_CARD(SILICOM, PE3100G2DQIRM_QXSL4): + FM10K_SW_I2C_REQ_LOCK(sw->i2c); + /* + * Set up the first PCA9545 mux so we can get at the PCA9635s + * that the LED control lines are connected to. + */ + fm10k_i2c_write8(sw->i2c, 0x70, 0x01); + + /* XXX will need to update for QSFPs + * operating as four independent lanes/ports + */ + addr = 0x6a; + led_flags = fm10k_switch_pca9635_led_bits(0); + for (i = 0; i < 2; i++) { + port = &ports->ports[i]; + switch (port->lane_speed * port->num_lanes) { + case 100: + fm10k_i2c_write16(sw->i2c, + addr, 0x14, led_flags); + break; + case 40: + fm10k_i2c_write16(sw->i2c, + addr, 0x14, led_flags << 6); + break; + } + + addr = 0x69; + } + FM10K_SW_I2C_REQ_UNLOCK(sw->i2c); + break; + } +} + + +static void * +fm10k_switch_process_intr(void *ctx) +{ + struct fm10k_switch *sw = ctx; + struct fm10k_ext_ports *ports; + uint64_t gid; + uint64_t reenable_mask; + uint16_t epl_mask; + + while (1) { + sem_wait(&sw->intr_tq); + /* + * Mask off all global interrupt detect interrupts + * toward PCIe to ensure that the PEP sees the + * interrupt condition clear so that all subsequent + * events will be recognized. The same result could + * be achieved by masking off all block-internal + * interrupt sources in all blocks prior to handling + * any global interrupt detect interrupts, but the + * approach taken here makes the interrupt processing + * easier to decompose and increases the likelihood that + * more work will be coalesced into fewer interrupts. + */ + FM10K_SW_TRACE("masking off PCIe gid interrupts"); + FM10K_SW_SWITCH_LOCK(sw); + /* + * sw->ext_ports will be set to NULL during detach + * to indicate that we are to no longer process EPL + * interrupts. + */ + ports = sw->ext_ports; + /* + * sw->sm_mailbox_enabled will be set to 0 during + * detach to indicate that we are to no longer process + * PEP interrupts. + */ + fm10k_write_switch_reg64(sw, + FM10K_SW_INTERRUPT_MASK_PCIE(sw->pepno), + FM10K_SW_INTERRUPT_MASK_PCIE_ALL); + gid = fm10k_read_switch_reg64(sw, + FM10K_SW_GLOBAL_INTERRUPT_DETECT); + FM10K_SW_SWITCH_UNLOCK(sw); + + reenable_mask = 0; + if (ports) { + epl_mask = fm10k_ext_ports_epl_intrs(ports, gid); + reenable_mask |= + FM10K_SW_MAKE_REG_FIELD64 + (INTERRUPT_MASK_PCIE_EPL, epl_mask); + } + + if (gid & FM10K_SW_GLOBAL_INTERRUPT_DETECT_I2C) + fm10k_i2c_intr(sw->i2c); + + reenable_mask |= FM10K_SW_INTERRUPT_MASK_PCIE_I2C; + + FM10K_SW_TRACE("re-enabling PCIe gid interrupts"); + FM10K_SW_SWITCH_LOCK(sw); + fm10k_write_switch_reg64(sw, + FM10K_SW_INTERRUPT_MASK_PCIE(sw->pepno), + ~reenable_mask); + FM10K_SW_SWITCH_UNLOCK(sw); + } + return NULL; +} + +void +fm10k_switch_intr(struct fm10k_hw *hw) +{ + if (hw) + sem_post(&fm10k_sw.intr_tq); +} + + +static void +fm10k_switch_enable_interrupts(struct fm10k_switch *sw) +{ + uint64_t data64; + unsigned int i; + + data64 = fm10k_read_switch_reg64(sw, + FM10K_SW_INTERRUPT_MASK_PCIE(sw->pepno)); + + /* enable interrupts from all EPLs in use */ + FM10K_SW_REPLACE_REG_FIELD64(data64, INTERRUPT_MASK_PCIE_EPL, + ~sw->ext_ports->epl_mask, data64); + + /* enable interrupts from all non-master PEPs in use */ + FM10K_SW_REPLACE_REG_FIELD64(data64, INTERRUPT_MASK_PCIE_PCIE, + ~sw->pep_mask, data64); + + /* enable I2C interrupt */ + data64 &= ~FM10K_SW_INTERRUPT_MASK_PCIE_I2C; + fm10k_write_switch_reg64(sw, + FM10K_SW_INTERRUPT_MASK_PCIE(sw->pepno), data64); + + /* enable outbound mailbox interrupts from all non-master PEPs */ + for (i = 0; i < FM10K_SW_PEPS_MAX; i++) { + if (sw->pep_mask & (1 << i)) + fm10k_write_switch_reg(sw, FM10K_SW_PCIE_GLOBAL(i, IM), + ~FM10K_SW_IM_MAILBOX); + } + FM10K_WRITE_REG(sw->master_hw, + FM10K_SW_EIMR, FM10K_SW_EIMR_FIELD(SWITCH_INT, ENABLE)); +} + + +static void +fm10k_switch_detach(struct fm10k_hw *hw) +{ + struct fm10k_ext_ports *ports; + struct fm10k_switch *sw = &fm10k_sw; + + if (hw == NULL || sw == NULL) + return; + + FM10K_SW_SWITCH_LOCK(sw); + sw->detaching = 1; + FM10K_SW_SWITCH_UNLOCK(sw); + + if (sw->ext_ports) { + ports = sw->ext_ports; + /* + * Ensure that any further switch interrupts will not + * process sw->ext_ports before detaching them. + */ + FM10K_SW_SWITCH_LOCK(sw); + sw->ext_ports = NULL; + FM10K_SW_SWITCH_UNLOCK(sw); + fm10k_switch_leds_off(sw, ports); + fm10k_ext_ports_detach(ports); + } + + if (sw->epl_sbus) + fm10k_sbus_detach(sw->epl_sbus); + + /* + * Detach i2c after ext_ports so ext_ports can use i2c to disable + * phys. + */ + if (sw->i2c) + fm10k_i2c_detach(sw->i2c); + + fm10k_hw_eicr_disable_source(sw, FM10K_SW_EICR_SWITCH_INT); +} + + +static unsigned int +fm10k_switch_get_fabric_clock(struct fm10k_switch *sw) +{ + uint32_t pll_fabric; + unsigned int freq; + + pll_fabric = fm10k_read_switch_reg(sw, FM10K_SW_PLL_FABRIC_LOCK); + + switch (FM10K_SW_REG_FIELD(pll_fabric, PLL_FABRIC_FREQSEL)) { + case FM10K_SW_PLL_FABRIC_FREQSEL_CTRL: { + uint32_t ctrl; + unsigned int refdiv, fbdiv4, fbdiv255; + + ctrl = fm10k_read_switch_reg(sw, FM10K_SW_PLL_FABRIC_CTRL); + refdiv = FM10K_SW_REG_FIELD(ctrl, PLL_FABRIC_REFDIV); + fbdiv4 = FM10K_SW_REG_FIELD(ctrl, PLL_FABRIC_FBDIV4); + fbdiv255 = FM10K_SW_REG_FIELD(ctrl, PLL_FABRIC_FBDIV255); + + freq = (15625 * 4 * fbdiv255 * (1 + fbdiv4)) / (refdiv * 100); + break; + } + case FM10K_SW_PLL_FABRIC_FREQSEL_F600: + freq = 600; + break; + case FM10K_SW_PLL_FABRIC_FREQSEL_F500: + freq = 500; + break; + case FM10K_SW_PLL_FABRIC_FREQSEL_F400: + freq = 400; + break; + case FM10K_SW_PLL_FABRIC_FREQSEL_F300: + freq = 300; + break; + default: + freq = 0; + break; + } + + return freq; +} + +static unsigned int +fm10k_switch_get_sku(struct fm10k_switch *sw) +{ + uint32_t fuse_data; + + fuse_data = fm10k_read_switch_reg(sw, FM10K_SW_FUSE_DATA_0); + return FM10K_SW_REG_FIELD(fuse_data, FUSE_SKU); +} + +static const char * +fm10k_switch_get_sku_name(unsigned int sku) +{ + const char *name; + + switch (sku) { + case FM10K_SW_FUSE_SKU_FM10840: + name = "FM10840"; + break; + case FM10K_SW_FUSE_SKU_FM10420: + name = "FM10420"; + break; + case FM10K_SW_FUSE_SKU_FM10064: + name = "FM10064"; + break; + default: + name = "FM10xxx-unknown"; + break; + } + + return name; +} + + +static void +fm10k_switch_describe(struct fm10k_switch *sw) +{ + unsigned int i, pair; + unsigned int reset_state, active_state; + uint32_t device_cfg, resets; + + if (!fm10k_config_check_debug(sw->dpdk_cfg, FM10K_CONFIG_DEBUG_CONFIG)) + return; + + FM10K_SW_INFO("switch: device is %s, fabric clock %u MHz", + fm10k_switch_get_sku_name(fm10k_switch_get_sku(sw)), + fm10k_switch_get_fabric_clock(sw)); + + device_cfg = fm10k_read_switch_reg(sw, FM10K_SW_DEVICE_CFG); + FM10K_SW_INFO("switch: 100G Ethernet is %s", + (device_cfg & FM10K_SW_DEVICE_CFG_PCIE_100G_DIS) ? + "disabled" : "enabled"); + switch (FM10K_SW_REG_FIELD(device_cfg, DEVICE_CFG_FEATURE)) { + case FM10K_SW_DEVICE_CFG_PCIE_FULL: + FM10K_SW_INFO("switch: PEPs 0-7 support 2x4 and 1x8 " + "modes, PEP 8 is x1"); + break; + case FM10K_SW_DEVICE_CFG_PCIE_HALF: + FM10K_SW_INFO("switch: PEPs 0-3 support 2x4 and 1x8 " + "modes, PEPs 4 and 6 are 1x4, PEP 8 is x1"); + break; + case FM10K_SW_DEVICE_CFG_PCIE_BASIC: + FM10K_SW_INFO("switch: PEP 0 supports, PEP 8 is x1, " + "only one can be used"); + break; + } + + resets = fm10k_read_switch_reg(sw, FM10K_SW_SOFT_RESET); + for (i = 0; i < FM10K_SW_PEPS_MAX; i++) { + if (!(device_cfg & FM10K_SW_DEVICE_CFG_PCIE_EN(i))) + continue; + + pair = i / 2; + reset_state = !!(resets & FM10K_SW_SOFT_RESET_PCIE_RESET(i)); + active_state = !!(resets & FM10K_SW_SOFT_RESET_PCIE_ACTIVE(i)); + if (pair < FM10K_SW_DEVICE_CFG_PCIE_MODE_PAIRS && + !(device_cfg & FM10K_SW_DEVICE_CFG_PCIE_MODE_2X4(pair))) { + if (i % 2 == 0) + FM10K_SW_INFO("switch: PEP[%u,%u] is enabled in 1x8 " + "mode (Reset=%u, Active=%u)", i, i + 1, + reset_state, active_state); + else + FM10K_SW_INFO("switch: PEP[%u] unexpectedly enabled when " + "PEP[%u,%u] is in 1x8 mode (Reset=%u, " + "Active=%u)", i, i - 1, i, reset_state, + active_state); + } else { + FM10K_SW_INFO("switch: PEP[%u] is enabled in 1x4 " + "mode (Reset=%u, Active=%u)", i, reset_state, + active_state); + } + } +} + + +static struct fm10k_switch * +fm10k_switch_attach(struct fm10k_hw *hw, struct fm10k_switch *sw) +{ + int i; + int error = 0; + + /* + * XXX apparently no way to determine one's own PEP number. + */ + switch (sw->info->num_peps) { + case 1: + sw->pepno = 0; + break; + + case 2: + /* + * XXX assumption is using even numbered PEPs + * starting with 0, and the highest numbered PEP + * is the master + */ + sw->pepno = 2; + for (i = 0; i < sw->info->num_peps - 1; i++) + sw->pep_mask |= (1 << (i * 2)); + break; + + case 4: + sw->pepno = 6; + sw->pep_mask = 0x7f; + break; + + default: + sw->pepno = 0; + } + + /* When not zero, initialize serdes in 'near loopback' mode */ + sw->serdes_loopback = 0; + + pthread_mutex_init(&sw->lock, NULL); + + fm10k_switch_determine_epls(sw); + + sw->ext_ports = fm10k_ext_ports_attach(sw); + if (sw->ext_ports == NULL) + goto fail; + + /* label all external ports with their logical port numbers */ + for (i = 0; i < sw->ext_ports->num_ports; i++) + sw->ext_ports->ports[i].lport = sw->epl_map[i].logical_port; + + /* interrupt */ + sem_init(&sw->intr_tq, 0, 0); + pthread_create(&sw->intr_task, NULL, fm10k_switch_process_intr, sw); + + fm10k_switch_enable_interrupts(sw); + fm10k_switch_describe(sw); + + sw->i2c = fm10k_i2c_attach(sw); + if (sw->i2c == NULL) + goto fail; + + error = fm10k_switch_init(hw, sw); + if (error) + goto fail; + + for (i = 0; i < sw->ext_ports->num_ports; i++) + fm10k_ext_port_up(&sw->ext_ports->ports[i]); + + usec_delay(10000); + fm10k_switch_leds_init(sw); + + pthread_create(&sw->led_task, NULL, &fm10k_switch_leds_update, sw); + pthread_create(&sw->stats_task, NULL, &fm10k_switch_process_stats, sw); + sw->inited = 1; + return sw; +fail: + if (sw != NULL) + fm10k_switch_detach(hw); + return NULL; +} + +struct fm10k_switch* +fm10k_switch_get(void) +{ + return &fm10k_sw; +} + +static int +fm10k_switch_dpdk_port_get(struct fm10k_switch *sw, int pf_no) +{ + int i; + + for (i = 0; i < FM10K_SW_LOGICAL_PORTS_MAX; i++) { + if (sw->dpdk_cfg->ports[i].pf_no == pf_no) + return i; + } + return -1; +} + +static int +fm10k_switch_mapped_ext_port_get(struct fm10k_switch *sw, int pf_no) +{ + int i; + struct fm10k_cfg_port_pf_map *map; + + for (i = 0; i < FM10K_SW_EXT_PORTS_MAX; i++) { + map = &sw->dpdk_cfg->ext_port_map[i]; + if (map->type == FM10K_CONFIG_PORT_MAP_PF) { + if (map->map_no[0] == pf_no) + return i; + } else if (map->type == FM10K_CONFIG_PORT_MAP_PFS) { + if (map->map_no[0] == pf_no || map->map_no[1] == pf_no) + return i; + } + } + return -1; +} + +static int +fm10k_switch_start(struct fm10k_switch *sw, + struct fm10k_hw *master_hw, eth_fm10k_dev_init_half_func *func) +{ + int i, j; + struct fm10k_dpdk_port *port; + struct fm10k_cfg_port_pf_map *map; + struct fm10k_cfg_port_pf_map *dpdk_map; + int dpdk_port_no, ext_port_no = 0; + int pf_no; + + sw->info = fm10k_get_device_info(master_hw); + sw->master_hw = master_hw; + sw->pep_map = fm10k_pep_port_map; + sw->epl_map = fm10k_epl_port_map; + + sw->info->ext_port_speed = sw->dpdk_cfg->ext_port_speed; + fm10k_switch_set_sched_prog(); + fm10k_switch_attach(master_hw, &fm10k_sw); + + for (i = 0; i < FM10K_SW_LOGICAL_PORTS_MAX; i++) { + port = &sw->dpdk_cfg->ports[i]; + map = &sw->dpdk_cfg->dpdk_port_map[i]; + if (port->type != FM10K_CONFIG_DPDK_PF || + map->type != FM10K_CONFIG_PORT_MAP_PFS) + continue; + + for (j = 0; j < 2; j++) { + pf_no = map->map_no[j]; + dpdk_port_no = + fm10k_switch_dpdk_port_get(sw, pf_no); + dpdk_map = + &sw->dpdk_cfg->dpdk_port_map[dpdk_port_no]; + if (map->type == FM10K_CONFIG_PORT_MAP_PF) { + dpdk_map->type = FM10K_CONFIG_PORT_MAP_PFSS; + dpdk_map->map_no[0] = + sw->dpdk_cfg->dpdk_port_map[i].map_no[0]; + dpdk_map->map_no[1] = + sw->dpdk_cfg->dpdk_port_map[i].map_no[1]; + } + } + } + + /* do initialize all ports, after switch is ready */ + for (i = 0; i < FM10K_SW_LOGICAL_PORTS_MAX; i++) { + uint32_t sglort; + struct fm10k_dpdk_port *port = &sw->dpdk_cfg->ports[i]; + struct fm10k_hw *hw = port->hw; + + if (hw == NULL) + break; + + map = &sw->dpdk_cfg->ext_port_map[ext_port_no]; + if (port->type == FM10K_CONFIG_DPDK_PF) { + pf_no = fm10k_switch_dpdk_pf_no_get(hw); + if (map->type == FM10K_CONFIG_PORT_MAP_PF) { + sglort = fm10k_switch_pf_glort_get(pf_no); + } else if (map->type == FM10K_CONFIG_PORT_MAP_PFS) { + ext_port_no = + fm10k_switch_mapped_ext_port_get + (sw, pf_no); + sglort = + fm10k_switch_pfs_glort_get + (map->map_no[0], map->map_no[1]); + } else { + FM10K_SW_ERR("Unknown mapped port type %d!", + port->type); + return -1; + } + hw->mac.dglort_map = sglort | 0xffff0000; + } + func(hw); + } + return 0; +} + + +static int +fm10k_switch_dpdk_port_reg(struct fm10k_switch *sw, + struct fm10k_hw *hw, void *rte_dev, uint8_t is_pf, bool master) +{ + int i; + struct fm10k_dpdk_port *port; + struct fm10k_cfg_port_pf_map *map; + + if (sw->dpdk_cfg == NULL && is_pf) { + if (fm10k_config_init(sw, hw) < 0) + return -1; + } + + for (i = 0; i < FM10K_SW_LOGICAL_PORTS_MAX; i++) { + port = &sw->dpdk_cfg->ports[i]; + if (master && port->hw && + port->type == FM10K_CONFIG_DPDK_PF) { + port->pf_no = + sw->dpdk_cfg->pf_max - 1 - + (hw->mac.addr[5] - port->hw->mac.addr[5]); + sw->dpdk_cfg->pf_hw[port->pf_no] = port->hw; + } + + if (port->hw == NULL) { + port->hw = hw; + port->rte_dev = rte_dev; + fm10k_flow_list_init(&port->flow_list); + map = &sw->dpdk_cfg->dpdk_port_map[i]; + if (is_pf) { + sw->dpdk_cfg->pf_bind++; + port->type = FM10K_CONFIG_DPDK_PF; + if (map->type == FM10K_CONFIG_PORT_MAP_NULL) { + map->type = FM10K_CONFIG_PORT_MAP_PF; + map->map_no[0] = i; + } + } else { + port->type = FM10K_CONFIG_DPDK_VF; + } + if (master) + sw->dpdk_cfg->master_hw = hw; + if (sw->dpdk_cfg->master_hw) { + port->pf_no = + sw->dpdk_cfg->pf_max - 1 - + (sw->dpdk_cfg->master_hw->mac.addr[5] - + hw->mac.addr[5]); + sw->dpdk_cfg->pf_hw[port->pf_no] = port->hw; + } + + return 0; + } + } + return -1; +} + +int +fm10k_switch_dpdk_port_no_get(struct fm10k_hw *hw) +{ + int i; + + for (i = 0; i < FM10K_SW_LOGICAL_PORTS_MAX; i++) { + if (fm10k_sw.dpdk_cfg->ports[i].hw == NULL) + break; + + if (fm10k_sw.dpdk_cfg->ports[i].hw == hw) + return i; + } + return -1; +} + +void * +fm10k_switch_dpdk_port_rte_dev_get(struct fm10k_hw *hw) +{ + int port_no = fm10k_switch_dpdk_port_no_get(hw); + + if (port_no < 0) + return NULL; + + return fm10k_switch_get()->dpdk_cfg->ports[port_no].rte_dev; +} + +struct fm10k_flow_list * +fm10k_switch_dpdk_port_flow_list_get(struct fm10k_hw *hw) +{ + int port_no = fm10k_switch_dpdk_port_no_get(hw); + + if (port_no < 0) + return NULL; + + return fm10k_switch_get()->dpdk_cfg->ports[port_no].flow_list; +} + +static int +fm10k_switch_dpdk_cfg_check(struct fm10k_switch *sw) +{ + int i; + bool need_default = true; + struct fm10k_dpdk_port *port; + struct fm10k_cfg_port_pf_map *map; + + if (sw->dpdk_cfg->master_hw == NULL) { + FM10K_SW_ERR("Master PF is not bound!!!"); + return -1; + } + + for (i = 0; i < FM10K_SW_LOGICAL_PORTS_MAX; i++) { + port = &sw->dpdk_cfg->ports[i]; + map = &sw->dpdk_cfg->dpdk_port_map[i]; + if (port->type == FM10K_CONFIG_DPDK_PF && + map->type != FM10K_CONFIG_PORT_MAP_NULL) { + need_default = false; + break; + } + } + + if (need_default) { + for (i = 0; i < FM10K_SW_LOGICAL_PORTS_MAX; i++) { + map = &sw->dpdk_cfg->dpdk_port_map[i]; + if (port->type == FM10K_CONFIG_DPDK_PF) { + map->type = FM10K_CONFIG_PORT_MAP_PF; + map->map_no[0] = i; + } + } + } + + return 0; +} + + +static void +fm10k_switch_dpdk_cfg_describe(struct fm10k_switch *sw) +{ + int i; + struct fm10k_cfg_port_pf_map *map; + + if (!fm10k_config_check_debug(sw->dpdk_cfg, FM10K_CONFIG_DEBUG_CONFIG)) + return; + + printf("--- FM10K DYNAMIC CONFIG ---\n"); + printf(" PF Bind : %d\n", sw->dpdk_cfg->pf_bind); + + printf("--- PF ---\n"); + for (i = 0; i < FM10K_SW_PEP_PORTS_MAX; i++) { + uint8_t *mac; + struct rte_eth_dev *dev; + struct rte_pci_device *pdev; + + if (sw->dpdk_cfg->pf_hw[i] == NULL) + continue; + dev = (struct rte_eth_dev *) + fm10k_switch_dpdk_port_rte_dev_get + (sw->dpdk_cfg->pf_hw[i]); + pdev = RTE_ETH_DEV_TO_PCI(dev); + mac = sw->dpdk_cfg->pf_hw[i]->mac.addr; + printf(" PF%d : Logical %d Glort %#x " + "PCI Addr %02x:%02x.%x " + "MAC Addr %02x:%02x:%02x:%02x:%02x:%02x\n", + i, fm10k_pep_port_map[i].logical_port, + fm10k_switch_pf_glort_get(i), pdev->addr.bus, + pdev->addr.devid, + pdev->addr.function, + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + } + + printf("--- DPDK PORT ---\n"); + for (i = 0; i < FM10K_SW_LOGICAL_PORTS_MAX; i++) { + if (sw->dpdk_cfg->ports[i].type == FM10K_CONFIG_DPDK_NULL) + break; + map = &sw->dpdk_cfg->dpdk_port_map[i]; + if (map->type == FM10K_CONFIG_PORT_MAP_NULL) + break; + if (map->type == FM10K_CONFIG_PORT_MAP_PF) { + printf(" DPDK PORT%d : Map to PF%d\n", + i, map->map_no[0]); + } else { + printf(" DPDK PORT%d : Map to PF%d&PF%d Glort %#x\n", + i, + map->map_no[0], map->map_no[1], + fm10k_switch_pfs_glort_get + (map->map_no[0], map->map_no[1])); + } + } + + printf("--- EXT PORT ---\n"); + for (i = 0; i < sw->dpdk_cfg->ext_port_num; i++) { + map = &sw->dpdk_cfg->ext_port_map[i]; + printf(" EXT PORT%d : Logical %d Glort %#x", i, + fm10k_epl_port_map[i].logical_port, + fm10k_switch_epl_glort_get(i)); + + if (map->type == FM10K_CONFIG_PORT_MAP_NULL) + printf("\n"); + else if (map->type == FM10K_CONFIG_PORT_MAP_PF) + printf(" Map to PF%d\n", map->map_no[0]); + else + printf(" Map to PF%d&PF%d Glort %#x\n", + map->map_no[0], map->map_no[1], + fm10k_switch_pfs_glort_get + (map->map_no[0], map->map_no[1])); + } +} + + +int +fm10k_switch_dpdk_port_start(struct fm10k_hw *hw, void *rte_dev, + uint8_t is_pf, bool master, eth_fm10k_dev_init_half_func *func) +{ + int ret; + struct fm10k_switch *sw = fm10k_switch_get(); + + if (fm10k_switch_dpdk_port_reg(sw, hw, rte_dev, is_pf, master) != 0) { + FM10K_SW_ERR("Register ports failed!!!"); + return -1; + } + + /* + * After all pfs are started + * start switch here + */ + if (fm10k_sw.dpdk_cfg->pf_max != 0 && + fm10k_sw.dpdk_cfg->pf_bind == fm10k_sw.dpdk_cfg->pf_num) { + if (fm10k_switch_dpdk_cfg_check(&fm10k_sw) != 0) + return -1; + + ret = fm10k_switch_start(&fm10k_sw, + fm10k_sw.dpdk_cfg->master_hw, func); + fm10k_switch_dpdk_cfg_describe(&fm10k_sw); + return ret; + } + return 0; +} + +void fm10k_switch_dpdk_port_stop(struct fm10k_hw *hw) +{ + if (hw) + return; +} + + +/* + * for multi-host, only dpdk port 0 and 1 are real rx/tx packets + * so we need init/setup/start dpdk port queue for them. + * for dpdk port 2/3, we need init/setup except start. + * we map the pf queue to 0/1 dpdk queue first, then map + * the other pf queue to 2/3 dpdk queue. + */ +int +fm10k_switch_dpdk_hw_queue_map(struct fm10k_hw *hw, + uint16_t queue, uint16_t max_queue, + struct fm10k_hw **map_hw, uint16_t *map_queue) +{ + int idx, pf_no; + int dpdk_port_no = fm10k_switch_dpdk_port_no_get(hw); + struct fm10k_dpdk_port *port; + struct fm10k_switch *sw = fm10k_switch_get(); + + if (dpdk_port_no < 0) { + FM10K_SW_ERR("Can not find the dpdk port!!!"); + return -1; + } + + port = &sw->dpdk_cfg->ports[dpdk_port_no]; + + if (port->type == FM10K_CONFIG_DPDK_VF) { + FM10K_SW_ERR("Not support yet!!!"); + return -1; + } + + if (port->type != FM10K_CONFIG_DPDK_PF) { + FM10K_SW_ERR("Can not be here!!!"); + return -1; + } + + if (sw->dpdk_cfg->dpdk_port_map[dpdk_port_no].type == + FM10K_CONFIG_PORT_MAP_PF) { + pf_no = sw->dpdk_cfg->dpdk_port_map[dpdk_port_no].map_no[0]; + *map_hw = sw->dpdk_cfg->pf_hw[pf_no]; + *map_queue = queue; + return 1; + } else if (sw->dpdk_cfg->dpdk_port_map[dpdk_port_no].type == + FM10K_CONFIG_PORT_MAP_PFS) { + idx = queue % 2; + pf_no = sw->dpdk_cfg->dpdk_port_map[dpdk_port_no].map_no[idx]; + *map_hw = sw->dpdk_cfg->pf_hw[pf_no]; + *map_queue = queue / 2; + return 1; + } else if (sw->dpdk_cfg->dpdk_port_map[dpdk_port_no].type == + FM10K_CONFIG_PORT_MAP_PFSS) { + idx = queue % 2; + pf_no = sw->dpdk_cfg->dpdk_port_map[dpdk_port_no].map_no[idx]; + *map_hw = sw->dpdk_cfg->pf_hw[pf_no]; + *map_queue = (max_queue + 1) / 2 + queue / 2; + return 0; + } else if (sw->dpdk_cfg->dpdk_port_map[dpdk_port_no].type == + FM10K_CONFIG_PORT_MAP_NULL) { + FM10K_SW_ERR("Unmapped dpdk port %d!!!", dpdk_port_no); + return -1; + } + FM10K_SW_ERR("Unknown mapped type!!!"); + return -1; +} + +int +fm10k_switch_dpdk_mapped_hw_get(struct fm10k_hw *hw, struct fm10k_hw *hw_list[]) +{ + int pf_no, pf_no_ext; + int dpdk_port_no = fm10k_switch_dpdk_port_no_get(hw); + struct fm10k_dpdk_port *port; + struct fm10k_cfg_port_pf_map *map; + struct fm10k_switch *sw = fm10k_switch_get(); + + if (dpdk_port_no < 0) { + FM10K_SW_ERR("Can not find the dpdk port!!!"); + return -1; + } + + port = &sw->dpdk_cfg->ports[dpdk_port_no]; + map = &sw->dpdk_cfg->dpdk_port_map[dpdk_port_no]; + + if (port->type == FM10K_CONFIG_DPDK_VF) { + hw_list[0] = hw; + hw_list[1] = NULL; + return 1; + } + + if (port->type != FM10K_CONFIG_DPDK_PF) { + FM10K_SW_ERR("Can not be here!!!"); + return -1; + } else if (map->type == FM10K_CONFIG_PORT_MAP_PF) { + pf_no = map->map_no[0]; + hw_list[0] = sw->dpdk_cfg->pf_hw[pf_no]; + hw_list[1] = NULL; + return 1; + } else if (map->type == FM10K_CONFIG_PORT_MAP_PFS) { + pf_no = map->map_no[0]; + hw_list[0] = sw->dpdk_cfg->pf_hw[pf_no]; + pf_no_ext = map->map_no[1]; + hw_list[1] = sw->dpdk_cfg->pf_hw[pf_no_ext]; + return 2; + } else if (map->type == FM10K_CONFIG_PORT_MAP_PFSS) { + pf_no = map->map_no[0]; + hw_list[0] = sw->dpdk_cfg->pf_hw[pf_no]; + pf_no_ext = map->map_no[1]; + hw_list[1] = sw->dpdk_cfg->pf_hw[pf_no_ext]; + return 0; + } + + hw_list[0] = NULL; + hw_list[1] = NULL; + return 0; +} + +void +fm10k_switch_dpdk_tx_queue_num_set(struct fm10k_hw *hw, uint8_t num) +{ + int port_no = fm10k_switch_dpdk_port_no_get(hw); + struct fm10k_switch *sw = fm10k_switch_get(); + + if (port_no < 0) { + FM10K_SW_ERR("Can not find the dpdk port!!!"); + return; + } + + sw->dpdk_cfg->ports[port_no].tx_queue_num = num; +} + +void +fm10k_switch_dpdk_rx_queue_num_set(struct fm10k_hw *hw, uint8_t num) +{ + int port_no = fm10k_switch_dpdk_port_no_get(hw); + struct fm10k_switch *sw = fm10k_switch_get(); + + if (port_no < 0) { + FM10K_SW_ERR("Can not find the dpdk port!!!"); + return; + } + + sw->dpdk_cfg->ports[port_no].rx_queue_num = num; +} + +int +fm10k_switch_dpdk_pf_no_get(struct fm10k_hw *hw) +{ + int port_no = fm10k_switch_dpdk_port_no_get(hw); + struct fm10k_switch *sw = fm10k_switch_get(); + + if (port_no < 0) { + FM10K_SW_ERR("Can not find the dpdk port!!!"); + return -1; + } + + return sw->dpdk_cfg->ports[port_no].pf_no; +} + + +void +fm10k_switch_flowset_switchto(const char *name) +{ + struct fm10k_switch *sw = fm10k_switch_get(); + + fm10k_ffu_flowset_switch(sw, name); +} + +void +fm10k_switch_show_port(void) +{ + struct fm10k_switch *sw = fm10k_switch_get(); + + fm10k_stats_epl_port_print(sw); + fm10k_stats_dpdk_port_print(sw); +} + +void +fm10k_switch_show_ffu(void) +{ + struct fm10k_switch *sw = fm10k_switch_get(); + + fm10k_stats_ffu_count_print(sw); +} + +void +fm10k_switch_show_bank(void) +{ + struct fm10k_switch *sw = fm10k_switch_get(); + + fm10k_stats_port_bank_print(sw); +}