From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from NAM01-BY2-obe.outbound.protection.outlook.com (mail-by2nam01on0046.outbound.protection.outlook.com [104.47.34.46]) by dpdk.org (Postfix) with ESMTP id 4C0315F1B for ; Fri, 9 Mar 2018 09:43:11 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amdcloud.onmicrosoft.com; s=selector1-amd-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=H7TvAs9nGNy7zcI5A4rf55Y9hqyX8UYF42CxqjClVNE=; b=4waNsRiyx+opElSLcBusbnucWczsdTGKpfIidfoVGvJSRQGne0eFALVGf6gI2hIM46YREqo/NOJ62BdVTKeeCkPY1MfGYf5FFp6QnEaQBZeu3+dhabM9HDl6nIx6+UQ2zpbOotBqEpLHcFyJ5NI4RB9EmIMYRaMeORlbB49rl8w= Received: from wallaby-smavila.amd.com (202.56.249.162) by MWHPR12MB1517.namprd12.prod.outlook.com (2603:10b6:301:b::21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.567.12; Fri, 9 Mar 2018 08:43:08 +0000 From: Ravi Kumar To: dev@dpdk.org Cc: ferruh.yigit@intel.com Date: Fri, 9 Mar 2018 03:42:22 -0500 Message-Id: <1520584954-130575-6-git-send-email-Ravi1.kumar@amd.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1520584954-130575-1-git-send-email-Ravi1.kumar@amd.com> References: <1515145938-97474-1-git-send-email-Ravi1.kumar@amd.com> <1520584954-130575-1-git-send-email-Ravi1.kumar@amd.com> MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [202.56.249.162] X-ClientProxiedBy: BM1PR0101CA0057.INDPRD01.PROD.OUTLOOK.COM (2603:1096:b00:19::19) To MWHPR12MB1517.namprd12.prod.outlook.com (2603:10b6:301:b::21) X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: bbece5c5-1a42-4cb9-68bd-08d58599c91a X-Microsoft-Antispam: UriScan:; BCL:0; PCL:0; RULEID:(7020095)(4652020)(48565401081)(5600026)(4604075)(4534165)(4627221)(201703031133081)(201702281549075)(2017052603328)(7153060)(7193020); SRVR:MWHPR12MB1517; X-Microsoft-Exchange-Diagnostics: 1; MWHPR12MB1517; 3:ho4+PqLmgerQpNDqz9J7pB7k9lrUinypyrfgHMEMgGdOfZ6KxxOwuf+aryHVB9a84bgvTvh3OpcmorZcCKYn4X0S3td/xA+jrAU5vBXfkpyYm9Y1F1e4xiw9/OZsPEW9g/xXkDBjB+0W5DpHqoB1Y8L1wZ/9E373ExHjcoCK+mxtRM9Q8mEbE1Gl9Cg5yxVEH6smkZI0vfGfZh2ln3qz/X1xKDLDd8OEa7FxYCTmmSyHQfgVHR8gS6iz4IOhFqRy; 25:uBg3jiTQxHrNcIMarMPW40cwkio3ZM6J4SNSRCAPzKWJ27wxtyHRTsXgu0sk9LhIxuushMbLwaNVOc/Y/yZ1NrVQUAYrHJav2aFy9vAAKwpoqDyTXGEKjENSqiQzJePsr9KPSu3+RH4Z96QNKA/0t8kL4omS/wevci+q6uE5hV49mkxJvLZWxaxjoR/ydsrY6jZshfDvyKTH0Yjhpl+oY/AM7PX3vYgTzykSnEV/LKtOVWVklDHG9gaV85ps9cIRuM7jLT1GEQBFDs7wAbB2XpM5vx9xT4X2maF9+vQZDroRJQlqOk8zvbQS8ef/NXCI4HA3uLdc4zJLv22IWnsI0w==; 31:yflI4dUzQ+zkBtWsewzN/glG4vWT6VTagbvwiqJUn0/eAW2KgpgL/tgAJL8cqt9i9OC/Iql1Fe4QtZdUXexEQ/q5QEZiyOLJscINEtSFtymC9cCEE7gX0MTOEItOa5RvRV333XVUYLHlZyMCNAptlKqtI6oj+83aAsKHwKOXQTbNgwXRk3BNuL9XfDqo9tVGPmBen1iJkTLJGFcnXVMaDH8aQhpDfmU7GCOkb+OWSwM= X-MS-TrafficTypeDiagnostic: MWHPR12MB1517: X-Microsoft-Exchange-Diagnostics: 1; MWHPR12MB1517; 20:9LAqq2tjm8mwDjGfXMHOg3YzvkdhiIQJ0CHdS45UckDFOdi2sCCjaGv8UUhlXIOS1SV5brXIgVVjjwqh98XSUL9W1rUMtY1ANpw2IX4VwTBW33+lPTEX+f2WbRVffdDLQLiCcCpw3G5k2ORefJvXyU8WPXz7feGxObc6qw0y7eekFiBakIPCE3mBWfsoR9R9ONj3jHLKDHjVbuPxCx3TcdLuqvQzGqO2ffDdAl3UHsT/ozOfjOsHMAQK45L2RZaRYY/H8RNgfJY5eG3VtbhfG93fR1wLu3w5AQ0PQVVqG/jelll1abLURoW2g/xmnETjzBmFm6qdw/k0btYwPK4p9ovzWRsTkFdAOvpBBaNExj94Cs3EiJissiTLVqDjeYhQqPYtct1w3WJnTqeU/PQJp1B+9deNnOV11/Qoh6emXTQX0xgOe5hgD3h82FjwoTMqA+tBOmN8XMYg/MXFbDK1OqM/R7TasWc5j12WlBjiyZyLCcXroolHRRM6/78d46OE; 4:0dqCN31wq9xF0Leuet7pcuIhukizf2HE5DnYnoZp24t7SS5d8/fpV6sWmEDiDDtpaiY/GLXT3rrlVYyT8PP1MT3NTSAFCIKJ1GjWRwCvHASDjpQLun0/OSnjhzRSCM/wpShopuy2Tbru3itzjjBpfKYsGL2ytf+wfOaYli3Zc4PFfgonGc4zhJisO0XtcnpeyCsBgZxlLKnOFthiMPqtDQm3kJ1cMQe5jwfInXZSqxgYa/w+3Cd5XixWHRLQtbtNSGUnHMh3oYr9gaToWgcjdlYjiUVfT+j9lAxzDbt/v5Mjx8wKm9LnJW/t6EJQ2z5L X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(767451399110); X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(8211001083)(6040522)(2401047)(8121501046)(5005006)(3002001)(10201501046)(93006095)(93001095)(3231220)(944501244)(52105095)(6055026)(6041310)(201703131423095)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(20161123558120)(20161123562045)(20161123560045)(20161123564045)(6072148)(201708071742011); SRVR:MWHPR12MB1517; BCL:0; PCL:0; RULEID:; SRVR:MWHPR12MB1517; X-Forefront-PRVS: 0606BBEB39 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10009020)(346002)(39380400002)(396003)(39860400002)(366004)(376002)(199004)(189003)(305945005)(50226002)(386003)(6346003)(26005)(36756003)(97736004)(25786009)(7736002)(50466002)(8936002)(4326008)(16586007)(86362001)(81156014)(81166006)(6666003)(2950100002)(6916009)(16526019)(186003)(5660300001)(59450400001)(316002)(8676002)(76176011)(52116002)(51416003)(7696005)(53416004)(48376002)(478600001)(106356001)(6116002)(3846002)(68736007)(2351001)(2906002)(6486002)(66066001)(47776003)(105586002)(53946003)(16200700003)(72206003)(53936002)(2361001)(559001)(569006); DIR:OUT; SFP:1101; SCL:1; SRVR:MWHPR12MB1517; H:wallaby-smavila.amd.com; FPR:; SPF:None; PTR:InfoNoRecords; A:1; MX:1; LANG:en; Received-SPF: None (protection.outlook.com: amd.com does not designate permitted sender hosts) Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=Ravi1.Kumar@amd.com; X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; MWHPR12MB1517; 23:8I+0OOgkjUNeONEBnxC/AOZfnJYBLAtnTXSB4vesK?= =?us-ascii?Q?OJXF8R91SotptdJT2x70L4Ay1f+F2ZWZtbrSeavu9Ou/B/cy541gAbMKuOW+?= =?us-ascii?Q?Qgn04Tfh0Ru44Q/RbYbQTc2HQjBnLjcllbEcycmWqL8TYny2bS5K6TQwD38h?= =?us-ascii?Q?x+IInGtIXSoDVSTC13Uu7Mvf5bOK6A0btgaqOkivXH1sz/4EVdAE7xXXxaj8?= =?us-ascii?Q?5y3MKeCh5z/x/p3uy1Vab5P9iMkWlfng8qTb7qlo03tYg3+TzHUq2VSFyxhb?= =?us-ascii?Q?WH6dm/lfNRELkdIZXKCQgDiIjQ248eUnp161GHxp7Ku/ZaECLjbZ9HP2yGDX?= =?us-ascii?Q?BzYILc4Sdcag6AiupBQ98go5yFwyOAc2BWYhzQw3IvFuvDz8ky5jH6mHZhGY?= =?us-ascii?Q?nCnWhdVhxPKn4+YCXpjUkhyWqeg7fXV6GZb/06rgqkNxk3qeCK+u90Z2ids/?= =?us-ascii?Q?XtT3NviyzA0qdxyexUTmzs+XyQEUP7CV7WBqklTGUPu7ZpNdVKR8K+PQNgq/?= =?us-ascii?Q?dkpNZsiX1fIMNRxB+GiIzYLj9b1FYvO5nBp4un3wfNtvh8czqUIYneEerPu1?= =?us-ascii?Q?gsM8haDjXszmGXDlF26LKJ/RFuFZxrfxrJGvUWq/4pytI4rQvN94GzlAyzep?= =?us-ascii?Q?Iv+pFmGmTnFg3EGKir9FCJSC9bVt5JPt6CIuYhLCU8UAQbK0bAStbzNGtA+o?= =?us-ascii?Q?RNWTwyxhKlDWyWZ9W49qn652nIN5GU5k6wb7jaJ11rjRe3/1foNdgkbxu3vS?= =?us-ascii?Q?3Lj8MU09X5Eux8acR1M+IV6gGqDAT+cg32dndfcJ5kfigFBYX9Wwfz4Ovk44?= =?us-ascii?Q?CNJkNuUBVLu8KWGvtpSto2pV5cNcf7X9ZRh/14dk3j2ZvV9FZB8gaC07/0Ry?= =?us-ascii?Q?+CjMjhfM5gHEPrsxV7jiMLkh6jj5bVQNk08zny3jVlEPht5T2GPTI2akH43r?= =?us-ascii?Q?kL7zOa6M2Sn9FOh2jxchZucDQNfJfrPjTshYCuiCclpb3rzW2ss0huRzGiyR?= =?us-ascii?Q?KOC3qFBUCrhsbo86FptQ0ifXWpUbi39p8/Inn4QgmDbDtXN4E1+fD3mIMj7i?= =?us-ascii?Q?yQzAmaMiZoz2hWJstmU0kbJopZ/uSobQ3J/eXjFQPB30M7xQ4mr/dZ6UNtlE?= =?us-ascii?Q?Rw+/GJhC+209BIKksiNAcqEmU51KrR/LwwQx9PfxURTE5FjeKE7PqfoKaJje?= =?us-ascii?Q?hEhCk8D/GCX9LmzJbfb9PZmAnbAAkV718XN6SY+Ef0hDb492P1XL2w/NnSBI?= =?us-ascii?Q?L0yvyQA/z07JHHRY64xqUfVn0Y9KoXsCJXSFfIakfqu9H3OdzwHR2TTPZIep?= =?us-ascii?B?UT09?= X-Microsoft-Antispam-Message-Info: 6zZXPMJgyI5mLFDLKuGlNA6SVb527QdRGdsJjJlP2pfL72dfevGxqjWwUM8qy+c/JDSIdgK6Ox1wU0VyXV3HfvTaNKkxPC360iEh8+7rOpBq7rRrJl8jB5p0ZjhXjT/8wRNWehsMPvNbebNJlCdfF5wZj/HnYcgAPxUT+2DqVf6jn6ZjoPT74SGdQ0cLfdD6 X-Microsoft-Exchange-Diagnostics: 1; MWHPR12MB1517; 6:Z2u03gLoB6dxg9EP1CgC8N4kSPoM7osJPngRKGAQ8c/Z1QAp/VmHf+cmcrDqqW+VG9QFvUmvBXUmEosQnwkTQt1Eap8LMyD820sK+IzPfBG4FG8oHlspIDqqCEm4n5VDB2uMHa+WjpPhSjWSXUiPErP3vaBEo5mi2PzdOTFNKstZ5CF3eUOpybgZUvoIFzJZj28vVqcIiDzdH97EaLJV2Nj0XxznM7Eu83ylQCOcoDWe0YHuaeKIMYuXOuxH8Iosvt0WqLa71tVrPzmB1005IsF8Tq96H1FMtxx/qRIzUVBOLl73z4HX+1TxA6wtWN3dt7VAlrLoMHf4ylcaqUyPrsIyGb2yhhvw4rBmcFSw9u0=; 5:aBzz+mB+ZYpBKwqkrqaLhfiThKajMsE3GZIutiT1l09ejrAQtzbnSGH8KaSWSLgGPfE6/jj6v3mVdXx5gMGkypT27BtzUe4q/82YMc0RjqdW+ng2eL2OGXKe8vpytvxq8lyzN2Mw3lloMoKvIIw+W/jdn4OAHoIrw3uUMN45/L0=; 24:chWtn9fcBil8RV+5hlGYADEfrIeaOs/e5ocpXSP7oqx3ER9FrYjfNgc/E+smJDSlc/26EWCGBeoQS1P7N+h2ON/5SK/fDkYYF1eAUDTo4ls=; 7:wR4j191P/lHRw2OcLqqvEb0Bub64M8Tezxc0dz7XJIRPzfw4BXiP22KllEf1QeL23MrYpdFZqzz8j0hHJQjPlqdjZITeH+DgixPmRESfNoA9Kypoiku1yJJLuEwAGOZC97AOW2RclVcLqJhJI3ZQdjskUtW/CpFi4WYGInlqFRf7RlLnnNlw5ZAvCRNAhjBVAFudgJm0GUW3ertz+qegIQi49ygqamxdmPYdh8EX0afbUKfG3tzurgOhyY19Xf0b SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1; MWHPR12MB1517; 20:V5/OWyLS4Klnz+STIlhYBnY/WFCeRc927UR1t0Xt8fX5cfg2iIXHxWCiT2Zi7ZzgKpJmhA4vyEpB2RO/Y0oCvdghZ4XsDCPjQ8lKsymH31+Ow357e0pPiHLJTYyn+D++aVU6aVklHGcuOgyY57NwdnWDtXI8FhJRCDwAadulrf1qQ/77wa7F3WypK9KKw6BF4q1yCtaNtPMdATJKplB5EMs7icXtU3HwK86fvcVUOOFSqrXDxdfTpK9O0JIEH5+Q X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 09 Mar 2018 08:43:08.3391 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: bbece5c5-1a42-4cb9-68bd-08d58599c91a X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 3dd8961f-e488-4e60-8e11-a82d994e183d X-MS-Exchange-Transport-CrossTenantHeadersStamped: MWHPR12MB1517 Subject: [dpdk-dev] [PATCH v3 06/18] net/axgbe: add phy programming apis 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: , X-List-Received-Date: Fri, 09 Mar 2018 08:43:11 -0000 Signed-off-by: Ravi Kumar --- drivers/net/axgbe/axgbe_dev.c | 27 + drivers/net/axgbe/axgbe_mdio.c | 963 +++++++++++++++++++++++++ drivers/net/axgbe/axgbe_phy_impl.c | 1397 ++++++++++++++++++++++++++++++++++++ 3 files changed, 2387 insertions(+) diff --git a/drivers/net/axgbe/axgbe_dev.c b/drivers/net/axgbe/axgbe_dev.c index 90a99c4..528241e 100644 --- a/drivers/net/axgbe/axgbe_dev.c +++ b/drivers/net/axgbe/axgbe_dev.c @@ -310,6 +310,30 @@ static void axgbe_write_mmd_regs(struct axgbe_port *pdata, int prtad, } } +static int axgbe_set_speed(struct axgbe_port *pdata, int speed) +{ + unsigned int ss; + + switch (speed) { + case SPEED_1000: + ss = 0x03; + break; + case SPEED_2500: + ss = 0x02; + break; + case SPEED_10000: + ss = 0x00; + break; + default: + return -EINVAL; + } + + if (AXGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) != ss) + AXGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, ss); + + return 0; +} + static int __axgbe_exit(struct axgbe_port *pdata) { unsigned int count = 2000; @@ -346,9 +370,12 @@ void axgbe_init_function_ptrs_dev(struct axgbe_hw_if *hw_if) { hw_if->exit = axgbe_exit; + hw_if->read_mmd_regs = axgbe_read_mmd_regs; hw_if->write_mmd_regs = axgbe_write_mmd_regs; + hw_if->set_speed = axgbe_set_speed; + hw_if->set_ext_mii_mode = axgbe_set_ext_mii_mode; hw_if->read_ext_mii_regs = axgbe_read_ext_mii_regs; hw_if->write_ext_mii_regs = axgbe_write_ext_mii_regs; diff --git a/drivers/net/axgbe/axgbe_mdio.c b/drivers/net/axgbe/axgbe_mdio.c index 4fbf5c3..753dde9 100644 --- a/drivers/net/axgbe/axgbe_mdio.c +++ b/drivers/net/axgbe/axgbe_mdio.c @@ -129,6 +129,963 @@ #include "axgbe_common.h" #include "axgbe_phy.h" +static void axgbe_an37_clear_interrupts(struct axgbe_port *pdata) +{ + int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT); + reg &= ~AXGBE_AN_CL37_INT_MASK; + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT, reg); +} + +static void axgbe_an37_disable_interrupts(struct axgbe_port *pdata) +{ + int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL); + reg &= ~AXGBE_AN_CL37_INT_MASK; + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL, reg); + + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL); + reg &= ~AXGBE_PCS_CL37_BP; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL, reg); +} + +static void axgbe_an73_clear_interrupts(struct axgbe_port *pdata) +{ + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); +} + +static void axgbe_an73_disable_interrupts(struct axgbe_port *pdata) +{ + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0); +} + +static void axgbe_an73_enable_interrupts(struct axgbe_port *pdata) +{ + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, + AXGBE_AN_CL73_INT_MASK); +} + +static void axgbe_an_enable_interrupts(struct axgbe_port *pdata) +{ + switch (pdata->an_mode) { + case AXGBE_AN_MODE_CL73: + case AXGBE_AN_MODE_CL73_REDRV: + axgbe_an73_enable_interrupts(pdata); + break; + case AXGBE_AN_MODE_CL37: + case AXGBE_AN_MODE_CL37_SGMII: + PMD_DRV_LOG(ERR, "Unsupported AN_MOD_37"); + break; + default: + break; + } +} + +static void axgbe_an_clear_interrupts_all(struct axgbe_port *pdata) +{ + axgbe_an73_clear_interrupts(pdata); + axgbe_an37_clear_interrupts(pdata); +} + +static void axgbe_an73_enable_kr_training(struct axgbe_port *pdata) +{ + unsigned int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL); + + reg |= AXGBE_KR_TRAINING_ENABLE; + XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg); +} + +static void axgbe_an73_disable_kr_training(struct axgbe_port *pdata) +{ + unsigned int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL); + + reg &= ~AXGBE_KR_TRAINING_ENABLE; + XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg); +} + +static void axgbe_kr_mode(struct axgbe_port *pdata) +{ + /* Enable KR training */ + axgbe_an73_enable_kr_training(pdata); + + /* Set MAC to 10G speed */ + pdata->hw_if.set_speed(pdata, SPEED_10000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, AXGBE_MODE_KR); +} + +static void axgbe_kx_2500_mode(struct axgbe_port *pdata) +{ + /* Disable KR training */ + axgbe_an73_disable_kr_training(pdata); + + /* Set MAC to 2.5G speed */ + pdata->hw_if.set_speed(pdata, SPEED_2500); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, AXGBE_MODE_KX_2500); +} + +static void axgbe_kx_1000_mode(struct axgbe_port *pdata) +{ + /* Disable KR training */ + axgbe_an73_disable_kr_training(pdata); + + /* Set MAC to 1G speed */ + pdata->hw_if.set_speed(pdata, SPEED_1000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, AXGBE_MODE_KX_1000); +} + +static void axgbe_sfi_mode(struct axgbe_port *pdata) +{ + /* If a KR re-driver is present, change to KR mode instead */ + if (pdata->kr_redrv) + return axgbe_kr_mode(pdata); + + /* Disable KR training */ + axgbe_an73_disable_kr_training(pdata); + + /* Set MAC to 10G speed */ + pdata->hw_if.set_speed(pdata, SPEED_10000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, AXGBE_MODE_SFI); +} + +static void axgbe_x_mode(struct axgbe_port *pdata) +{ + /* Disable KR training */ + axgbe_an73_disable_kr_training(pdata); + + /* Set MAC to 1G speed */ + pdata->hw_if.set_speed(pdata, SPEED_1000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, AXGBE_MODE_X); +} + +static void axgbe_sgmii_1000_mode(struct axgbe_port *pdata) +{ + /* Disable KR training */ + axgbe_an73_disable_kr_training(pdata); + + /* Set MAC to 1G speed */ + pdata->hw_if.set_speed(pdata, SPEED_1000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, AXGBE_MODE_SGMII_1000); +} + +static void axgbe_sgmii_100_mode(struct axgbe_port *pdata) +{ + /* Disable KR training */ + axgbe_an73_disable_kr_training(pdata); + + /* Set MAC to 1G speed */ + pdata->hw_if.set_speed(pdata, SPEED_1000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, AXGBE_MODE_SGMII_100); +} + +static enum axgbe_mode axgbe_cur_mode(struct axgbe_port *pdata) +{ + return pdata->phy_if.phy_impl.cur_mode(pdata); +} + +static bool axgbe_in_kr_mode(struct axgbe_port *pdata) +{ + return axgbe_cur_mode(pdata) == AXGBE_MODE_KR; +} + +static void axgbe_change_mode(struct axgbe_port *pdata, + enum axgbe_mode mode) +{ + switch (mode) { + case AXGBE_MODE_KX_1000: + axgbe_kx_1000_mode(pdata); + break; + case AXGBE_MODE_KX_2500: + axgbe_kx_2500_mode(pdata); + break; + case AXGBE_MODE_KR: + axgbe_kr_mode(pdata); + break; + case AXGBE_MODE_SGMII_100: + axgbe_sgmii_100_mode(pdata); + break; + case AXGBE_MODE_SGMII_1000: + axgbe_sgmii_1000_mode(pdata); + break; + case AXGBE_MODE_X: + axgbe_x_mode(pdata); + break; + case AXGBE_MODE_SFI: + axgbe_sfi_mode(pdata); + break; + case AXGBE_MODE_UNKNOWN: + break; + default: + PMD_DRV_LOG(ERR, "invalid operation mode requested (%u)", mode); + } +} + +static void axgbe_switch_mode(struct axgbe_port *pdata) +{ + axgbe_change_mode(pdata, pdata->phy_if.phy_impl.switch_mode(pdata)); +} + +static void axgbe_set_mode(struct axgbe_port *pdata, + enum axgbe_mode mode) +{ + if (mode == axgbe_cur_mode(pdata)) + return; + + axgbe_change_mode(pdata, mode); +} + +static bool axgbe_use_mode(struct axgbe_port *pdata, + enum axgbe_mode mode) +{ + return pdata->phy_if.phy_impl.use_mode(pdata, mode); +} + +static void axgbe_an37_set(struct axgbe_port *pdata, bool enable, + bool restart) +{ + unsigned int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_CTRL1); + reg &= ~MDIO_VEND2_CTRL1_AN_ENABLE; + + if (enable) + reg |= MDIO_VEND2_CTRL1_AN_ENABLE; + + if (restart) + reg |= MDIO_VEND2_CTRL1_AN_RESTART; + + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_CTRL1, reg); +} + +static void axgbe_an37_disable(struct axgbe_port *pdata) +{ + axgbe_an37_set(pdata, false, false); + axgbe_an37_disable_interrupts(pdata); +} + +static void axgbe_an73_set(struct axgbe_port *pdata, bool enable, + bool restart) +{ + unsigned int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_CTRL1); + reg &= ~MDIO_AN_CTRL1_ENABLE; + + if (enable) + reg |= MDIO_AN_CTRL1_ENABLE; + + if (restart) + reg |= MDIO_AN_CTRL1_RESTART; + + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_CTRL1, reg); +} + +static void axgbe_an73_restart(struct axgbe_port *pdata) +{ + axgbe_an73_enable_interrupts(pdata); + axgbe_an73_set(pdata, true, true); +} + +static void axgbe_an73_disable(struct axgbe_port *pdata) +{ + axgbe_an73_set(pdata, false, false); + axgbe_an73_disable_interrupts(pdata); +} + +static void axgbe_an_restart(struct axgbe_port *pdata) +{ + switch (pdata->an_mode) { + case AXGBE_AN_MODE_CL73: + case AXGBE_AN_MODE_CL73_REDRV: + axgbe_an73_restart(pdata); + break; + case AXGBE_AN_MODE_CL37: + case AXGBE_AN_MODE_CL37_SGMII: + PMD_DRV_LOG(ERR, "Unsupported AN_MODE_CL37"); + break; + default: + break; + } +} + +static void axgbe_an_disable(struct axgbe_port *pdata) +{ + switch (pdata->an_mode) { + case AXGBE_AN_MODE_CL73: + case AXGBE_AN_MODE_CL73_REDRV: + axgbe_an73_disable(pdata); + break; + case AXGBE_AN_MODE_CL37: + case AXGBE_AN_MODE_CL37_SGMII: + PMD_DRV_LOG(ERR, "Unsupported AN_MODE_CL37"); + break; + default: + break; + } +} + +static void axgbe_an_disable_all(struct axgbe_port *pdata) +{ + axgbe_an73_disable(pdata); + axgbe_an37_disable(pdata); +} + +static enum axgbe_an axgbe_an73_tx_training(struct axgbe_port *pdata, + enum axgbe_rx *state) +{ + unsigned int ad_reg, lp_reg, reg; + + *state = AXGBE_RX_COMPLETE; + + /* If we're not in KR mode then we're done */ + if (!axgbe_in_kr_mode(pdata)) + return AXGBE_AN_PAGE_RECEIVED; + + /* Enable/Disable FEC */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); + + reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FECCTRL); + reg &= ~(MDIO_PMA_10GBR_FECABLE_ABLE | MDIO_PMA_10GBR_FECABLE_ERRABLE); + if ((ad_reg & 0xc000) && (lp_reg & 0xc000)) + reg |= pdata->fec_ability; + XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FECCTRL, reg); + + /* Start KR training */ + reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL); + if (reg & AXGBE_KR_TRAINING_ENABLE) { + if (pdata->phy_if.phy_impl.kr_training_pre) + pdata->phy_if.phy_impl.kr_training_pre(pdata); + + reg |= AXGBE_KR_TRAINING_START; + XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, + reg); + + if (pdata->phy_if.phy_impl.kr_training_post) + pdata->phy_if.phy_impl.kr_training_post(pdata); + } + + return AXGBE_AN_PAGE_RECEIVED; +} + +static enum axgbe_an axgbe_an73_tx_xnp(struct axgbe_port *pdata, + enum axgbe_rx *state) +{ + u16 msg; + + *state = AXGBE_RX_XNP; + + msg = AXGBE_XNP_MCF_NULL_MESSAGE; + msg |= AXGBE_XNP_MP_FORMATTED; + + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP + 2, 0); + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP + 1, 0); + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP, msg); + + return AXGBE_AN_PAGE_RECEIVED; +} + +static enum axgbe_an axgbe_an73_rx_bpa(struct axgbe_port *pdata, + enum axgbe_rx *state) +{ + unsigned int link_support; + unsigned int reg, ad_reg, lp_reg; + + /* Read Base Ability register 2 first */ + reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); + + /* Check for a supported mode, otherwise restart in a different one */ + link_support = axgbe_in_kr_mode(pdata) ? 0x80 : 0x20; + if (!(reg & link_support)) + return AXGBE_AN_INCOMPAT_LINK; + + /* Check Extended Next Page support */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); + + return ((ad_reg & AXGBE_XNP_NP_EXCHANGE) || + (lp_reg & AXGBE_XNP_NP_EXCHANGE)) + ? axgbe_an73_tx_xnp(pdata, state) + : axgbe_an73_tx_training(pdata, state); +} + +static enum axgbe_an axgbe_an73_rx_xnp(struct axgbe_port *pdata, + enum axgbe_rx *state) +{ + unsigned int ad_reg, lp_reg; + + /* Check Extended Next Page support */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_XNP); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPX); + + return ((ad_reg & AXGBE_XNP_NP_EXCHANGE) || + (lp_reg & AXGBE_XNP_NP_EXCHANGE)) + ? axgbe_an73_tx_xnp(pdata, state) + : axgbe_an73_tx_training(pdata, state); +} + +static enum axgbe_an axgbe_an73_page_received(struct axgbe_port *pdata) +{ + enum axgbe_rx *state; + unsigned long an_timeout; + enum axgbe_an ret; + unsigned long ticks; + + if (!pdata->an_start) { + pdata->an_start = rte_get_timer_cycles(); + } else { + an_timeout = pdata->an_start + + msecs_to_timer_cycles(AXGBE_AN_MS_TIMEOUT); + ticks = rte_get_timer_cycles(); + if (time_after(ticks, an_timeout)) { + /* Auto-negotiation timed out, reset state */ + pdata->kr_state = AXGBE_RX_BPA; + pdata->kx_state = AXGBE_RX_BPA; + + pdata->an_start = rte_get_timer_cycles(); + } + } + + state = axgbe_in_kr_mode(pdata) ? &pdata->kr_state + : &pdata->kx_state; + + switch (*state) { + case AXGBE_RX_BPA: + ret = axgbe_an73_rx_bpa(pdata, state); + break; + case AXGBE_RX_XNP: + ret = axgbe_an73_rx_xnp(pdata, state); + break; + default: + ret = AXGBE_AN_ERROR; + } + + return ret; +} + +static enum axgbe_an axgbe_an73_incompat_link(struct axgbe_port *pdata) +{ + /* Be sure we aren't looping trying to negotiate */ + if (axgbe_in_kr_mode(pdata)) { + pdata->kr_state = AXGBE_RX_ERROR; + + if (!(pdata->phy.advertising & ADVERTISED_1000baseKX_Full) && + !(pdata->phy.advertising & ADVERTISED_2500baseX_Full)) + return AXGBE_AN_NO_LINK; + + if (pdata->kx_state != AXGBE_RX_BPA) + return AXGBE_AN_NO_LINK; + } else { + pdata->kx_state = AXGBE_RX_ERROR; + + if (!(pdata->phy.advertising & ADVERTISED_10000baseKR_Full)) + return AXGBE_AN_NO_LINK; + + if (pdata->kr_state != AXGBE_RX_BPA) + return AXGBE_AN_NO_LINK; + } + + axgbe_an73_disable(pdata); + axgbe_switch_mode(pdata); + axgbe_an73_restart(pdata); + + return AXGBE_AN_INCOMPAT_LINK; +} + +static void axgbe_an73_state_machine(struct axgbe_port *pdata) +{ + enum axgbe_an cur_state = pdata->an_state; + + if (!pdata->an_int) + return; + +next_int: + if (pdata->an_int & AXGBE_AN_CL73_PG_RCV) { + pdata->an_state = AXGBE_AN_PAGE_RECEIVED; + pdata->an_int &= ~AXGBE_AN_CL73_PG_RCV; + } else if (pdata->an_int & AXGBE_AN_CL73_INC_LINK) { + pdata->an_state = AXGBE_AN_INCOMPAT_LINK; + pdata->an_int &= ~AXGBE_AN_CL73_INC_LINK; + } else if (pdata->an_int & AXGBE_AN_CL73_INT_CMPLT) { + pdata->an_state = AXGBE_AN_COMPLETE; + pdata->an_int &= ~AXGBE_AN_CL73_INT_CMPLT; + } else { + pdata->an_state = AXGBE_AN_ERROR; + } + +again: + cur_state = pdata->an_state; + + switch (pdata->an_state) { + case AXGBE_AN_READY: + pdata->an_supported = 0; + break; + case AXGBE_AN_PAGE_RECEIVED: + pdata->an_state = axgbe_an73_page_received(pdata); + pdata->an_supported++; + break; + case AXGBE_AN_INCOMPAT_LINK: + pdata->an_supported = 0; + pdata->parallel_detect = 0; + pdata->an_state = axgbe_an73_incompat_link(pdata); + break; + case AXGBE_AN_COMPLETE: + pdata->parallel_detect = pdata->an_supported ? 0 : 1; + break; + case AXGBE_AN_NO_LINK: + break; + default: + pdata->an_state = AXGBE_AN_ERROR; + } + + if (pdata->an_state == AXGBE_AN_NO_LINK) { + pdata->an_int = 0; + axgbe_an73_clear_interrupts(pdata); + pdata->eth_dev->data->dev_link.link_status = + ETH_LINK_DOWN; + } else if (pdata->an_state == AXGBE_AN_ERROR) { + PMD_DRV_LOG(ERR, "error during auto-negotiation, state=%u\n", + cur_state); + pdata->an_int = 0; + axgbe_an73_clear_interrupts(pdata); + } + + if (pdata->an_state >= AXGBE_AN_COMPLETE) { + pdata->an_result = pdata->an_state; + pdata->an_state = AXGBE_AN_READY; + pdata->kr_state = AXGBE_RX_BPA; + pdata->kx_state = AXGBE_RX_BPA; + pdata->an_start = 0; + } + + if (cur_state != pdata->an_state) + goto again; + + if (pdata->an_int) + goto next_int; + + axgbe_an73_enable_interrupts(pdata); +} + +static void axgbe_an73_isr(struct axgbe_port *pdata) +{ + /* Disable AN interrupts */ + axgbe_an73_disable_interrupts(pdata); + + /* Save the interrupt(s) that fired */ + pdata->an_int = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_INT); + + if (pdata->an_int) { + /* Clear the interrupt(s) that fired and process them */ + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, ~pdata->an_int); + pthread_mutex_lock(&pdata->an_mutex); + axgbe_an73_state_machine(pdata); + pthread_mutex_unlock(&pdata->an_mutex); + } else { + /* Enable AN interrupts */ + axgbe_an73_enable_interrupts(pdata); + } +} + +static void axgbe_an_isr(struct axgbe_port *pdata) +{ + switch (pdata->an_mode) { + case AXGBE_AN_MODE_CL73: + case AXGBE_AN_MODE_CL73_REDRV: + axgbe_an73_isr(pdata); + break; + case AXGBE_AN_MODE_CL37: + case AXGBE_AN_MODE_CL37_SGMII: + PMD_DRV_LOG(ERR, "AN_MODE_37 not supported"); + break; + default: + break; + } +} + +static void axgbe_an_combined_isr(struct axgbe_port *pdata) +{ + axgbe_an_isr(pdata); +} + +static void axgbe_an73_init(struct axgbe_port *pdata) +{ + unsigned int advertising, reg; + + advertising = pdata->phy_if.phy_impl.an_advertising(pdata); + + /* Set up Advertisement register 3 first */ + reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); + if (advertising & ADVERTISED_10000baseR_FEC) + reg |= 0xc000; + else + reg &= ~0xc000; + + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2, reg); + + /* Set up Advertisement register 2 next */ + reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); + if (advertising & ADVERTISED_10000baseKR_Full) + reg |= 0x80; + else + reg &= ~0x80; + + if ((advertising & ADVERTISED_1000baseKX_Full) || + (advertising & ADVERTISED_2500baseX_Full)) + reg |= 0x20; + else + reg &= ~0x20; + + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1, reg); + + /* Set up Advertisement register 1 last */ + reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); + if (advertising & ADVERTISED_Pause) + reg |= 0x400; + else + reg &= ~0x400; + + if (advertising & ADVERTISED_Asym_Pause) + reg |= 0x800; + else + reg &= ~0x800; + + /* We don't intend to perform XNP */ + reg &= ~AXGBE_XNP_NP_EXCHANGE; + + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg); +} + +static void axgbe_an_init(struct axgbe_port *pdata) +{ + /* Set up advertisement registers based on current settings */ + pdata->an_mode = pdata->phy_if.phy_impl.an_mode(pdata); + switch (pdata->an_mode) { + case AXGBE_AN_MODE_CL73: + case AXGBE_AN_MODE_CL73_REDRV: + axgbe_an73_init(pdata); + break; + case AXGBE_AN_MODE_CL37: + case AXGBE_AN_MODE_CL37_SGMII: + PMD_DRV_LOG(ERR, "Unsupported AN_CL37"); + break; + default: + break; + } +} + +static void axgbe_phy_adjust_link(struct axgbe_port *pdata) +{ + if (pdata->phy.link) { + /* Speed support */ + if (pdata->phy_speed != pdata->phy.speed) + pdata->phy_speed = pdata->phy.speed; + if (pdata->phy_link != pdata->phy.link) + pdata->phy_link = pdata->phy.link; + } else if (pdata->phy_link) { + pdata->phy_link = 0; + pdata->phy_speed = SPEED_UNKNOWN; + } +} + +static int axgbe_phy_config_fixed(struct axgbe_port *pdata) +{ + enum axgbe_mode mode; + + /* Disable auto-negotiation */ + axgbe_an_disable(pdata); + + /* Set specified mode for specified speed */ + mode = pdata->phy_if.phy_impl.get_mode(pdata, pdata->phy.speed); + switch (mode) { + case AXGBE_MODE_KX_1000: + case AXGBE_MODE_KX_2500: + case AXGBE_MODE_KR: + case AXGBE_MODE_SGMII_100: + case AXGBE_MODE_SGMII_1000: + case AXGBE_MODE_X: + case AXGBE_MODE_SFI: + break; + case AXGBE_MODE_UNKNOWN: + default: + return -EINVAL; + } + + /* Validate duplex mode */ + if (pdata->phy.duplex != DUPLEX_FULL) + return -EINVAL; + + axgbe_set_mode(pdata, mode); + + return 0; +} + +static int __axgbe_phy_config_aneg(struct axgbe_port *pdata) +{ + int ret; + + axgbe_set_bit(AXGBE_LINK_INIT, &pdata->dev_state); + pdata->link_check = rte_get_timer_cycles(); + + ret = pdata->phy_if.phy_impl.an_config(pdata); + if (ret) + return ret; + + if (pdata->phy.autoneg != AUTONEG_ENABLE) { + ret = axgbe_phy_config_fixed(pdata); + if (ret || !pdata->kr_redrv) + return ret; + } + + /* Disable auto-negotiation interrupt */ + rte_intr_disable(&pdata->pci_dev->intr_handle); + + /* Start auto-negotiation in a supported mode */ + if (axgbe_use_mode(pdata, AXGBE_MODE_KR)) { + axgbe_set_mode(pdata, AXGBE_MODE_KR); + } else if (axgbe_use_mode(pdata, AXGBE_MODE_KX_2500)) { + axgbe_set_mode(pdata, AXGBE_MODE_KX_2500); + } else if (axgbe_use_mode(pdata, AXGBE_MODE_KX_1000)) { + axgbe_set_mode(pdata, AXGBE_MODE_KX_1000); + } else if (axgbe_use_mode(pdata, AXGBE_MODE_SFI)) { + axgbe_set_mode(pdata, AXGBE_MODE_SFI); + } else if (axgbe_use_mode(pdata, AXGBE_MODE_X)) { + axgbe_set_mode(pdata, AXGBE_MODE_X); + } else if (axgbe_use_mode(pdata, AXGBE_MODE_SGMII_1000)) { + axgbe_set_mode(pdata, AXGBE_MODE_SGMII_1000); + } else if (axgbe_use_mode(pdata, AXGBE_MODE_SGMII_100)) { + axgbe_set_mode(pdata, AXGBE_MODE_SGMII_100); + } else { + rte_intr_enable(&pdata->pci_dev->intr_handle); + return -EINVAL; + } + + /* Disable and stop any in progress auto-negotiation */ + axgbe_an_disable_all(pdata); + + /* Clear any auto-negotitation interrupts */ + axgbe_an_clear_interrupts_all(pdata); + + pdata->an_result = AXGBE_AN_READY; + pdata->an_state = AXGBE_AN_READY; + pdata->kr_state = AXGBE_RX_BPA; + pdata->kx_state = AXGBE_RX_BPA; + + /* Re-enable auto-negotiation interrupt */ + rte_intr_enable(&pdata->pci_dev->intr_handle); + + axgbe_an_init(pdata); + axgbe_an_restart(pdata); + + return 0; +} + +static int axgbe_phy_config_aneg(struct axgbe_port *pdata) +{ + int ret; + + pthread_mutex_lock(&pdata->an_mutex); + + ret = __axgbe_phy_config_aneg(pdata); + if (ret) + axgbe_set_bit(AXGBE_LINK_ERR, &pdata->dev_state); + else + axgbe_clear_bit(AXGBE_LINK_ERR, &pdata->dev_state); + + pthread_mutex_unlock(&pdata->an_mutex); + + return ret; +} + +static bool axgbe_phy_aneg_done(struct axgbe_port *pdata) +{ + return pdata->an_result == AXGBE_AN_COMPLETE; +} + +static void axgbe_check_link_timeout(struct axgbe_port *pdata) +{ + unsigned long link_timeout; + unsigned long ticks; + + link_timeout = pdata->link_check + (AXGBE_LINK_TIMEOUT * + 2 * rte_get_timer_hz()); + ticks = rte_get_timer_cycles(); + if (time_after(ticks, link_timeout)) + axgbe_phy_config_aneg(pdata); +} + +static enum axgbe_mode axgbe_phy_status_aneg(struct axgbe_port *pdata) +{ + return pdata->phy_if.phy_impl.an_outcome(pdata); +} + +static void axgbe_phy_status_result(struct axgbe_port *pdata) +{ + enum axgbe_mode mode; + + pdata->phy.lp_advertising = 0; + + if ((pdata->phy.autoneg != AUTONEG_ENABLE) || pdata->parallel_detect) + mode = axgbe_cur_mode(pdata); + else + mode = axgbe_phy_status_aneg(pdata); + + switch (mode) { + case AXGBE_MODE_SGMII_100: + pdata->phy.speed = SPEED_100; + break; + case AXGBE_MODE_X: + case AXGBE_MODE_KX_1000: + case AXGBE_MODE_SGMII_1000: + pdata->phy.speed = SPEED_1000; + break; + case AXGBE_MODE_KX_2500: + pdata->phy.speed = SPEED_2500; + break; + case AXGBE_MODE_KR: + case AXGBE_MODE_SFI: + pdata->phy.speed = SPEED_10000; + break; + case AXGBE_MODE_UNKNOWN: + default: + pdata->phy.speed = SPEED_UNKNOWN; + } + + pdata->phy.duplex = DUPLEX_FULL; + + axgbe_set_mode(pdata, mode); +} + +static void axgbe_phy_status(struct axgbe_port *pdata) +{ + unsigned int link_aneg; + int an_restart; + + if (axgbe_test_bit(AXGBE_LINK_ERR, &pdata->dev_state)) { + pdata->phy.link = 0; + goto adjust_link; + } + + link_aneg = (pdata->phy.autoneg == AUTONEG_ENABLE); + + pdata->phy.link = pdata->phy_if.phy_impl.link_status(pdata, + &an_restart); + if (an_restart) { + axgbe_phy_config_aneg(pdata); + return; + } + + if (pdata->phy.link) { + if (link_aneg && !axgbe_phy_aneg_done(pdata)) { + axgbe_check_link_timeout(pdata); + return; + } + axgbe_phy_status_result(pdata); + if (axgbe_test_bit(AXGBE_LINK_INIT, &pdata->dev_state)) + axgbe_clear_bit(AXGBE_LINK_INIT, &pdata->dev_state); + } else { + if (axgbe_test_bit(AXGBE_LINK_INIT, &pdata->dev_state)) { + axgbe_check_link_timeout(pdata); + + if (link_aneg) + return; + } + axgbe_phy_status_result(pdata); + } + +adjust_link: + axgbe_phy_adjust_link(pdata); +} + +static void axgbe_phy_stop(struct axgbe_port *pdata) +{ + if (!pdata->phy_started) + return; + /* Indicate the PHY is down */ + pdata->phy_started = 0; + /* Disable auto-negotiation */ + axgbe_an_disable_all(pdata); + pdata->phy_if.phy_impl.stop(pdata); + pdata->phy.link = 0; + axgbe_phy_adjust_link(pdata); +} + +static int axgbe_phy_start(struct axgbe_port *pdata) +{ + int ret; + + ret = pdata->phy_if.phy_impl.start(pdata); + if (ret) + return ret; + /* Set initial mode - call the mode setting routines + * directly to insure we are properly configured + */ + if (axgbe_use_mode(pdata, AXGBE_MODE_KR)) { + axgbe_kr_mode(pdata); + } else if (axgbe_use_mode(pdata, AXGBE_MODE_KX_2500)) { + axgbe_kx_2500_mode(pdata); + } else if (axgbe_use_mode(pdata, AXGBE_MODE_KX_1000)) { + axgbe_kx_1000_mode(pdata); + } else if (axgbe_use_mode(pdata, AXGBE_MODE_SFI)) { + axgbe_sfi_mode(pdata); + } else if (axgbe_use_mode(pdata, AXGBE_MODE_X)) { + axgbe_x_mode(pdata); + } else if (axgbe_use_mode(pdata, AXGBE_MODE_SGMII_1000)) { + axgbe_sgmii_1000_mode(pdata); + } else if (axgbe_use_mode(pdata, AXGBE_MODE_SGMII_100)) { + axgbe_sgmii_100_mode(pdata); + } else { + ret = -EINVAL; + goto err_stop; + } + /* Indicate the PHY is up and running */ + pdata->phy_started = 1; + axgbe_an_init(pdata); + axgbe_an_enable_interrupts(pdata); + return axgbe_phy_config_aneg(pdata); + +err_stop: + pdata->phy_if.phy_impl.stop(pdata); + + return ret; +} + +static int axgbe_phy_reset(struct axgbe_port *pdata) +{ + int ret; + + ret = pdata->phy_if.phy_impl.reset(pdata); + if (ret) + return ret; + + /* Disable auto-negotiation for now */ + axgbe_an_disable_all(pdata); + + /* Clear auto-negotiation interrupts */ + axgbe_an_clear_interrupts_all(pdata); + + return 0; +} + static int axgbe_phy_best_advertised_speed(struct axgbe_port *pdata) { if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full) @@ -200,4 +1157,10 @@ static int axgbe_phy_init(struct axgbe_port *pdata) void axgbe_init_function_ptrs_phy(struct axgbe_phy_if *phy_if) { phy_if->phy_init = axgbe_phy_init; + phy_if->phy_reset = axgbe_phy_reset; + phy_if->phy_start = axgbe_phy_start; + phy_if->phy_stop = axgbe_phy_stop; + phy_if->phy_status = axgbe_phy_status; + phy_if->phy_config_aneg = axgbe_phy_config_aneg; + phy_if->an_isr = axgbe_an_combined_isr; } diff --git a/drivers/net/axgbe/axgbe_phy_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c index cea4266..5f69651 100644 --- a/drivers/net/axgbe/axgbe_phy_impl.c +++ b/drivers/net/axgbe/axgbe_phy_impl.c @@ -361,6 +361,1337 @@ struct axgbe_phy_data { unsigned int redrv_model; }; +static enum axgbe_an_mode axgbe_phy_an_mode(struct axgbe_port *pdata); + +static int axgbe_phy_i2c_xfer(struct axgbe_port *pdata, + struct axgbe_i2c_op *i2c_op) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + + /* Be sure we own the bus */ + if (!phy_data->comm_owned) + return -EIO; + + return pdata->i2c_if.i2c_xfer(pdata, i2c_op); +} + +static int axgbe_phy_redrv_write(struct axgbe_port *pdata, unsigned int reg, + unsigned int val) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + struct axgbe_i2c_op i2c_op; + uint16_t *redrv_val; + u8 redrv_data[5], csum; + unsigned int i, retry; + int ret; + + /* High byte of register contains read/write indicator */ + redrv_data[0] = ((reg >> 8) & 0xff) << 1; + redrv_data[1] = reg & 0xff; + redrv_val = (uint16_t *)&redrv_data[2]; + *redrv_val = rte_cpu_to_be_16(val); + + /* Calculate 1 byte checksum */ + csum = 0; + for (i = 0; i < 4; i++) { + csum += redrv_data[i]; + if (redrv_data[i] > csum) + csum++; + } + redrv_data[4] = ~csum; + + retry = 1; +again1: + i2c_op.cmd = AXGBE_I2C_CMD_WRITE; + i2c_op.target = phy_data->redrv_addr; + i2c_op.len = sizeof(redrv_data); + i2c_op.buf = redrv_data; + ret = axgbe_phy_i2c_xfer(pdata, &i2c_op); + if (ret) { + if ((ret == -EAGAIN) && retry--) + goto again1; + + return ret; + } + + retry = 1; +again2: + i2c_op.cmd = AXGBE_I2C_CMD_READ; + i2c_op.target = phy_data->redrv_addr; + i2c_op.len = 1; + i2c_op.buf = redrv_data; + ret = axgbe_phy_i2c_xfer(pdata, &i2c_op); + if (ret) { + if ((ret == -EAGAIN) && retry--) + goto again2; + + return ret; + } + + if (redrv_data[0] != 0xff) { + PMD_DRV_LOG(ERR, "Redriver write checksum error\n"); + ret = -EIO; + } + + return ret; +} + +static int axgbe_phy_i2c_read(struct axgbe_port *pdata, unsigned int target, + void *reg, unsigned int reg_len, + void *val, unsigned int val_len) +{ + struct axgbe_i2c_op i2c_op; + int retry, ret; + + retry = 1; +again1: + /* Set the specified register to read */ + i2c_op.cmd = AXGBE_I2C_CMD_WRITE; + i2c_op.target = target; + i2c_op.len = reg_len; + i2c_op.buf = reg; + ret = axgbe_phy_i2c_xfer(pdata, &i2c_op); + if (ret) { + if ((ret == -EAGAIN) && retry--) + goto again1; + + return ret; + } + + retry = 1; +again2: + /* Read the specfied register */ + i2c_op.cmd = AXGBE_I2C_CMD_READ; + i2c_op.target = target; + i2c_op.len = val_len; + i2c_op.buf = val; + ret = axgbe_phy_i2c_xfer(pdata, &i2c_op); + if ((ret == -EAGAIN) && retry--) + goto again2; + + return ret; +} + +static int axgbe_phy_sfp_put_mux(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + struct axgbe_i2c_op i2c_op; + uint8_t mux_channel; + + if (phy_data->sfp_comm == AXGBE_SFP_COMM_DIRECT) + return 0; + + /* Select no mux channels */ + mux_channel = 0; + i2c_op.cmd = AXGBE_I2C_CMD_WRITE; + i2c_op.target = phy_data->sfp_mux_address; + i2c_op.len = sizeof(mux_channel); + i2c_op.buf = &mux_channel; + + return axgbe_phy_i2c_xfer(pdata, &i2c_op); +} + +static int axgbe_phy_sfp_get_mux(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + struct axgbe_i2c_op i2c_op; + u8 mux_channel; + + if (phy_data->sfp_comm == AXGBE_SFP_COMM_DIRECT) + return 0; + + /* Select desired mux channel */ + mux_channel = 1 << phy_data->sfp_mux_channel; + i2c_op.cmd = AXGBE_I2C_CMD_WRITE; + i2c_op.target = phy_data->sfp_mux_address; + i2c_op.len = sizeof(mux_channel); + i2c_op.buf = &mux_channel; + + return axgbe_phy_i2c_xfer(pdata, &i2c_op); +} + +static void axgbe_phy_put_comm_ownership(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + + phy_data->comm_owned = 0; + + pthread_mutex_unlock(&pdata->phy_mutex); +} + +static int axgbe_phy_get_comm_ownership(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + uint64_t timeout; + unsigned int mutex_id; + + if (phy_data->comm_owned) + return 0; + + /* The I2C and MDIO/GPIO bus is multiplexed between multiple devices, + * the driver needs to take the software mutex and then the hardware + * mutexes before being able to use the busses. + */ + pthread_mutex_lock(&pdata->phy_mutex); + + /* Clear the mutexes */ + XP_IOWRITE(pdata, XP_I2C_MUTEX, AXGBE_MUTEX_RELEASE); + XP_IOWRITE(pdata, XP_MDIO_MUTEX, AXGBE_MUTEX_RELEASE); + + /* Mutex formats are the same for I2C and MDIO/GPIO */ + mutex_id = 0; + XP_SET_BITS(mutex_id, XP_I2C_MUTEX, ID, phy_data->port_id); + XP_SET_BITS(mutex_id, XP_I2C_MUTEX, ACTIVE, 1); + + timeout = rte_get_timer_cycles() + (rte_get_timer_hz() * 5); + while (time_before(rte_get_timer_cycles(), timeout)) { + /* Must be all zeroes in order to obtain the mutex */ + if (XP_IOREAD(pdata, XP_I2C_MUTEX) || + XP_IOREAD(pdata, XP_MDIO_MUTEX)) { + rte_delay_us(100); + continue; + } + + /* Obtain the mutex */ + XP_IOWRITE(pdata, XP_I2C_MUTEX, mutex_id); + XP_IOWRITE(pdata, XP_MDIO_MUTEX, mutex_id); + + phy_data->comm_owned = 1; + return 0; + } + + pthread_mutex_unlock(&pdata->phy_mutex); + + PMD_DRV_LOG(ERR, "unable to obtain hardware mutexes"); + + return -ETIMEDOUT; +} + +static void axgbe_phy_sfp_phy_settings(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + + if (phy_data->sfp_mod_absent) { + pdata->phy.speed = SPEED_UNKNOWN; + pdata->phy.duplex = DUPLEX_UNKNOWN; + pdata->phy.autoneg = AUTONEG_ENABLE; + pdata->phy.advertising = pdata->phy.supported; + } + + pdata->phy.advertising &= ~ADVERTISED_Autoneg; + pdata->phy.advertising &= ~ADVERTISED_TP; + pdata->phy.advertising &= ~ADVERTISED_FIBRE; + pdata->phy.advertising &= ~ADVERTISED_100baseT_Full; + pdata->phy.advertising &= ~ADVERTISED_1000baseT_Full; + pdata->phy.advertising &= ~ADVERTISED_10000baseT_Full; + pdata->phy.advertising &= ~ADVERTISED_10000baseR_FEC; + + switch (phy_data->sfp_base) { + case AXGBE_SFP_BASE_1000_T: + case AXGBE_SFP_BASE_1000_SX: + case AXGBE_SFP_BASE_1000_LX: + case AXGBE_SFP_BASE_1000_CX: + pdata->phy.speed = SPEED_UNKNOWN; + pdata->phy.duplex = DUPLEX_UNKNOWN; + pdata->phy.autoneg = AUTONEG_ENABLE; + pdata->phy.advertising |= ADVERTISED_Autoneg; + break; + case AXGBE_SFP_BASE_10000_SR: + case AXGBE_SFP_BASE_10000_LR: + case AXGBE_SFP_BASE_10000_LRM: + case AXGBE_SFP_BASE_10000_ER: + case AXGBE_SFP_BASE_10000_CR: + default: + pdata->phy.speed = SPEED_10000; + pdata->phy.duplex = DUPLEX_FULL; + pdata->phy.autoneg = AUTONEG_DISABLE; + break; + } + + switch (phy_data->sfp_base) { + case AXGBE_SFP_BASE_1000_T: + case AXGBE_SFP_BASE_1000_CX: + case AXGBE_SFP_BASE_10000_CR: + pdata->phy.advertising |= ADVERTISED_TP; + break; + default: + pdata->phy.advertising |= ADVERTISED_FIBRE; + } + + switch (phy_data->sfp_speed) { + case AXGBE_SFP_SPEED_100_1000: + if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_100) + pdata->phy.advertising |= ADVERTISED_100baseT_Full; + if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_1000) + pdata->phy.advertising |= ADVERTISED_1000baseT_Full; + break; + case AXGBE_SFP_SPEED_1000: + if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_1000) + pdata->phy.advertising |= ADVERTISED_1000baseT_Full; + break; + case AXGBE_SFP_SPEED_10000: + if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_10000) + pdata->phy.advertising |= ADVERTISED_10000baseT_Full; + break; + default: + /* Choose the fastest supported speed */ + if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_10000) + pdata->phy.advertising |= ADVERTISED_10000baseT_Full; + else if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_1000) + pdata->phy.advertising |= ADVERTISED_1000baseT_Full; + else if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_100) + pdata->phy.advertising |= ADVERTISED_100baseT_Full; + } +} + +static bool axgbe_phy_sfp_bit_rate(struct axgbe_sfp_eeprom *sfp_eeprom, + enum axgbe_sfp_speed sfp_speed) +{ + u8 *sfp_base, min, max; + + sfp_base = sfp_eeprom->base; + + switch (sfp_speed) { + case AXGBE_SFP_SPEED_1000: + min = AXGBE_SFP_BASE_BR_1GBE_MIN; + max = AXGBE_SFP_BASE_BR_1GBE_MAX; + break; + case AXGBE_SFP_SPEED_10000: + min = AXGBE_SFP_BASE_BR_10GBE_MIN; + max = AXGBE_SFP_BASE_BR_10GBE_MAX; + break; + default: + return false; + } + + return ((sfp_base[AXGBE_SFP_BASE_BR] >= min) && + (sfp_base[AXGBE_SFP_BASE_BR] <= max)); +} + +static void axgbe_phy_sfp_external_phy(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + + if (!phy_data->sfp_changed) + return; + + phy_data->sfp_phy_avail = 0; + + if (phy_data->sfp_base != AXGBE_SFP_BASE_1000_T) + return; +} + +static bool axgbe_phy_belfuse_parse_quirks(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + struct axgbe_sfp_eeprom *sfp_eeprom = &phy_data->sfp_eeprom; + + if (memcmp(&sfp_eeprom->base[AXGBE_SFP_BASE_VENDOR_NAME], + AXGBE_BEL_FUSE_VENDOR, AXGBE_SFP_BASE_VENDOR_NAME_LEN)) + return false; + + if (!memcmp(&sfp_eeprom->base[AXGBE_SFP_BASE_VENDOR_PN], + AXGBE_BEL_FUSE_PARTNO, AXGBE_SFP_BASE_VENDOR_PN_LEN)) { + phy_data->sfp_base = AXGBE_SFP_BASE_1000_SX; + phy_data->sfp_cable = AXGBE_SFP_CABLE_ACTIVE; + phy_data->sfp_speed = AXGBE_SFP_SPEED_1000; + return true; + } + + return false; +} + +static bool axgbe_phy_sfp_parse_quirks(struct axgbe_port *pdata) +{ + if (axgbe_phy_belfuse_parse_quirks(pdata)) + return true; + + return false; +} + +static void axgbe_phy_sfp_parse_eeprom(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + struct axgbe_sfp_eeprom *sfp_eeprom = &phy_data->sfp_eeprom; + uint8_t *sfp_base; + + sfp_base = sfp_eeprom->base; + + if (sfp_base[AXGBE_SFP_BASE_ID] != AXGBE_SFP_ID_SFP) + return; + + if (sfp_base[AXGBE_SFP_BASE_EXT_ID] != AXGBE_SFP_EXT_ID_SFP) + return; + + if (axgbe_phy_sfp_parse_quirks(pdata)) + return; + + /* Assume ACTIVE cable unless told it is PASSIVE */ + if (sfp_base[AXGBE_SFP_BASE_CABLE] & AXGBE_SFP_BASE_CABLE_PASSIVE) { + phy_data->sfp_cable = AXGBE_SFP_CABLE_PASSIVE; + phy_data->sfp_cable_len = sfp_base[AXGBE_SFP_BASE_CU_CABLE_LEN]; + } else { + phy_data->sfp_cable = AXGBE_SFP_CABLE_ACTIVE; + } + + /* Determine the type of SFP */ + if (sfp_base[AXGBE_SFP_BASE_10GBE_CC] & AXGBE_SFP_BASE_10GBE_CC_SR) + phy_data->sfp_base = AXGBE_SFP_BASE_10000_SR; + else if (sfp_base[AXGBE_SFP_BASE_10GBE_CC] & AXGBE_SFP_BASE_10GBE_CC_LR) + phy_data->sfp_base = AXGBE_SFP_BASE_10000_LR; + else if (sfp_base[AXGBE_SFP_BASE_10GBE_CC] & + AXGBE_SFP_BASE_10GBE_CC_LRM) + phy_data->sfp_base = AXGBE_SFP_BASE_10000_LRM; + else if (sfp_base[AXGBE_SFP_BASE_10GBE_CC] & AXGBE_SFP_BASE_10GBE_CC_ER) + phy_data->sfp_base = AXGBE_SFP_BASE_10000_ER; + else if (sfp_base[AXGBE_SFP_BASE_1GBE_CC] & AXGBE_SFP_BASE_1GBE_CC_SX) + phy_data->sfp_base = AXGBE_SFP_BASE_1000_SX; + else if (sfp_base[AXGBE_SFP_BASE_1GBE_CC] & AXGBE_SFP_BASE_1GBE_CC_LX) + phy_data->sfp_base = AXGBE_SFP_BASE_1000_LX; + else if (sfp_base[AXGBE_SFP_BASE_1GBE_CC] & AXGBE_SFP_BASE_1GBE_CC_CX) + phy_data->sfp_base = AXGBE_SFP_BASE_1000_CX; + else if (sfp_base[AXGBE_SFP_BASE_1GBE_CC] & AXGBE_SFP_BASE_1GBE_CC_T) + phy_data->sfp_base = AXGBE_SFP_BASE_1000_T; + else if ((phy_data->sfp_cable == AXGBE_SFP_CABLE_PASSIVE) && + axgbe_phy_sfp_bit_rate(sfp_eeprom, AXGBE_SFP_SPEED_10000)) + phy_data->sfp_base = AXGBE_SFP_BASE_10000_CR; + + switch (phy_data->sfp_base) { + case AXGBE_SFP_BASE_1000_T: + phy_data->sfp_speed = AXGBE_SFP_SPEED_100_1000; + break; + case AXGBE_SFP_BASE_1000_SX: + case AXGBE_SFP_BASE_1000_LX: + case AXGBE_SFP_BASE_1000_CX: + phy_data->sfp_speed = AXGBE_SFP_SPEED_1000; + break; + case AXGBE_SFP_BASE_10000_SR: + case AXGBE_SFP_BASE_10000_LR: + case AXGBE_SFP_BASE_10000_LRM: + case AXGBE_SFP_BASE_10000_ER: + case AXGBE_SFP_BASE_10000_CR: + phy_data->sfp_speed = AXGBE_SFP_SPEED_10000; + break; + default: + break; + } +} + +static bool axgbe_phy_sfp_verify_eeprom(uint8_t cc_in, uint8_t *buf, + unsigned int len) +{ + uint8_t cc; + + for (cc = 0; len; buf++, len--) + cc += *buf; + + return (cc == cc_in) ? true : false; +} + +static int axgbe_phy_sfp_read_eeprom(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + struct axgbe_sfp_eeprom sfp_eeprom; + uint8_t eeprom_addr; + int ret; + + ret = axgbe_phy_sfp_get_mux(pdata); + if (ret) { + PMD_DRV_LOG(ERR, "I2C error setting SFP MUX"); + return ret; + } + + /* Read the SFP serial ID eeprom */ + eeprom_addr = 0; + ret = axgbe_phy_i2c_read(pdata, AXGBE_SFP_SERIAL_ID_ADDRESS, + &eeprom_addr, sizeof(eeprom_addr), + &sfp_eeprom, sizeof(sfp_eeprom)); + if (ret) { + PMD_DRV_LOG(ERR, "I2C error reading SFP EEPROM"); + goto put; + } + + /* Validate the contents read */ + if (!axgbe_phy_sfp_verify_eeprom(sfp_eeprom.base[AXGBE_SFP_BASE_CC], + sfp_eeprom.base, + sizeof(sfp_eeprom.base) - 1)) { + ret = -EINVAL; + goto put; + } + + if (!axgbe_phy_sfp_verify_eeprom(sfp_eeprom.extd[AXGBE_SFP_EXTD_CC], + sfp_eeprom.extd, + sizeof(sfp_eeprom.extd) - 1)) { + ret = -EINVAL; + goto put; + } + + /* Check for an added or changed SFP */ + if (memcmp(&phy_data->sfp_eeprom, &sfp_eeprom, sizeof(sfp_eeprom))) { + phy_data->sfp_changed = 1; + memcpy(&phy_data->sfp_eeprom, &sfp_eeprom, sizeof(sfp_eeprom)); + + if (sfp_eeprom.extd[AXGBE_SFP_EXTD_SFF_8472]) { + uint8_t diag_type; + diag_type = sfp_eeprom.extd[AXGBE_SFP_EXTD_DIAG]; + + if (!(diag_type & AXGBE_SFP_EXTD_DIAG_ADDR_CHANGE)) + phy_data->sfp_diags = 1; + } + } else { + phy_data->sfp_changed = 0; + } + +put: + axgbe_phy_sfp_put_mux(pdata); + + return ret; +} + +static void axgbe_phy_sfp_signals(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + unsigned int gpio_input; + u8 gpio_reg, gpio_ports[2]; + int ret; + + /* Read the input port registers */ + gpio_reg = 0; + ret = axgbe_phy_i2c_read(pdata, phy_data->sfp_gpio_address, + &gpio_reg, sizeof(gpio_reg), + gpio_ports, sizeof(gpio_ports)); + if (ret) { + PMD_DRV_LOG(ERR, "I2C error reading SFP GPIOs"); + return; + } + + gpio_input = (gpio_ports[1] << 8) | gpio_ports[0]; + + if (phy_data->sfp_gpio_mask & AXGBE_GPIO_NO_MOD_ABSENT) { + /* No GPIO, just assume the module is present for now */ + phy_data->sfp_mod_absent = 0; + } else { + if (!(gpio_input & (1 << phy_data->sfp_gpio_mod_absent))) + phy_data->sfp_mod_absent = 0; + } + + if (!(phy_data->sfp_gpio_mask & AXGBE_GPIO_NO_RX_LOS) && + (gpio_input & (1 << phy_data->sfp_gpio_rx_los))) + phy_data->sfp_rx_los = 1; + + if (!(phy_data->sfp_gpio_mask & AXGBE_GPIO_NO_TX_FAULT) && + (gpio_input & (1 << phy_data->sfp_gpio_tx_fault))) + phy_data->sfp_tx_fault = 1; +} + +static void axgbe_phy_sfp_mod_absent(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + + phy_data->sfp_mod_absent = 1; + phy_data->sfp_phy_avail = 0; + memset(&phy_data->sfp_eeprom, 0, sizeof(phy_data->sfp_eeprom)); +} + +static void axgbe_phy_sfp_reset(struct axgbe_phy_data *phy_data) +{ + phy_data->sfp_rx_los = 0; + phy_data->sfp_tx_fault = 0; + phy_data->sfp_mod_absent = 1; + phy_data->sfp_diags = 0; + phy_data->sfp_base = AXGBE_SFP_BASE_UNKNOWN; + phy_data->sfp_cable = AXGBE_SFP_CABLE_UNKNOWN; + phy_data->sfp_speed = AXGBE_SFP_SPEED_UNKNOWN; +} + +static void axgbe_phy_sfp_detect(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + int ret; + + /* Reset the SFP signals and info */ + axgbe_phy_sfp_reset(phy_data); + + ret = axgbe_phy_get_comm_ownership(pdata); + if (ret) + return; + + /* Read the SFP signals and check for module presence */ + axgbe_phy_sfp_signals(pdata); + if (phy_data->sfp_mod_absent) { + axgbe_phy_sfp_mod_absent(pdata); + goto put; + } + + ret = axgbe_phy_sfp_read_eeprom(pdata); + if (ret) { + /* Treat any error as if there isn't an SFP plugged in */ + axgbe_phy_sfp_reset(phy_data); + axgbe_phy_sfp_mod_absent(pdata); + goto put; + } + + axgbe_phy_sfp_parse_eeprom(pdata); + axgbe_phy_sfp_external_phy(pdata); + +put: + axgbe_phy_sfp_phy_settings(pdata); + axgbe_phy_put_comm_ownership(pdata); +} + +static void axgbe_phy_phydev_flowctrl(struct axgbe_port *pdata) +{ + pdata->phy.tx_pause = 0; + pdata->phy.rx_pause = 0; +} + +static enum axgbe_mode axgbe_phy_an73_redrv_outcome(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + enum axgbe_mode mode; + unsigned int ad_reg, lp_reg; + + pdata->phy.lp_advertising |= ADVERTISED_Autoneg; + pdata->phy.lp_advertising |= ADVERTISED_Backplane; + + /* Use external PHY to determine flow control */ + if (pdata->phy.pause_autoneg) + axgbe_phy_phydev_flowctrl(pdata); + + /* Compare Advertisement and Link Partner register 2 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); + if (lp_reg & 0x80) + pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full; + if (lp_reg & 0x20) + pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full; + + ad_reg &= lp_reg; + if (ad_reg & 0x80) { + switch (phy_data->port_mode) { + case AXGBE_PORT_MODE_BACKPLANE: + mode = AXGBE_MODE_KR; + break; + default: + mode = AXGBE_MODE_SFI; + break; + } + } else if (ad_reg & 0x20) { + switch (phy_data->port_mode) { + case AXGBE_PORT_MODE_BACKPLANE: + mode = AXGBE_MODE_KX_1000; + break; + case AXGBE_PORT_MODE_1000BASE_X: + mode = AXGBE_MODE_X; + break; + case AXGBE_PORT_MODE_SFP: + switch (phy_data->sfp_base) { + case AXGBE_SFP_BASE_1000_T: + mode = AXGBE_MODE_SGMII_1000; + break; + case AXGBE_SFP_BASE_1000_SX: + case AXGBE_SFP_BASE_1000_LX: + case AXGBE_SFP_BASE_1000_CX: + default: + mode = AXGBE_MODE_X; + break; + } + break; + default: + mode = AXGBE_MODE_SGMII_1000; + break; + } + } else { + mode = AXGBE_MODE_UNKNOWN; + } + + /* Compare Advertisement and Link Partner register 3 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); + if (lp_reg & 0xc000) + pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC; + + return mode; +} + +static enum axgbe_mode axgbe_phy_an73_outcome(struct axgbe_port *pdata) +{ + enum axgbe_mode mode; + unsigned int ad_reg, lp_reg; + + pdata->phy.lp_advertising |= ADVERTISED_Autoneg; + pdata->phy.lp_advertising |= ADVERTISED_Backplane; + + /* Compare Advertisement and Link Partner register 1 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); + if (lp_reg & 0x400) + pdata->phy.lp_advertising |= ADVERTISED_Pause; + if (lp_reg & 0x800) + pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause; + + if (pdata->phy.pause_autoneg) { + /* Set flow control based on auto-negotiation result */ + pdata->phy.tx_pause = 0; + pdata->phy.rx_pause = 0; + + if (ad_reg & lp_reg & 0x400) { + pdata->phy.tx_pause = 1; + pdata->phy.rx_pause = 1; + } else if (ad_reg & lp_reg & 0x800) { + if (ad_reg & 0x400) + pdata->phy.rx_pause = 1; + else if (lp_reg & 0x400) + pdata->phy.tx_pause = 1; + } + } + + /* Compare Advertisement and Link Partner register 2 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); + if (lp_reg & 0x80) + pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full; + if (lp_reg & 0x20) + pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full; + + ad_reg &= lp_reg; + if (ad_reg & 0x80) + mode = AXGBE_MODE_KR; + else if (ad_reg & 0x20) + mode = AXGBE_MODE_KX_1000; + else + mode = AXGBE_MODE_UNKNOWN; + + /* Compare Advertisement and Link Partner register 3 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); + if (lp_reg & 0xc000) + pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC; + + return mode; +} + +static enum axgbe_mode axgbe_phy_an_outcome(struct axgbe_port *pdata) +{ + switch (pdata->an_mode) { + case AXGBE_AN_MODE_CL73: + return axgbe_phy_an73_outcome(pdata); + case AXGBE_AN_MODE_CL73_REDRV: + return axgbe_phy_an73_redrv_outcome(pdata); + case AXGBE_AN_MODE_CL37: + case AXGBE_AN_MODE_CL37_SGMII: + default: + return AXGBE_MODE_UNKNOWN; + } +} + +static unsigned int axgbe_phy_an_advertising(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + unsigned int advertising; + + /* Without a re-driver, just return current advertising */ + if (!phy_data->redrv) + return pdata->phy.advertising; + + /* With the KR re-driver we need to advertise a single speed */ + advertising = pdata->phy.advertising; + advertising &= ~ADVERTISED_1000baseKX_Full; + advertising &= ~ADVERTISED_10000baseKR_Full; + + switch (phy_data->port_mode) { + case AXGBE_PORT_MODE_BACKPLANE: + advertising |= ADVERTISED_10000baseKR_Full; + break; + case AXGBE_PORT_MODE_BACKPLANE_2500: + advertising |= ADVERTISED_1000baseKX_Full; + break; + case AXGBE_PORT_MODE_1000BASE_T: + case AXGBE_PORT_MODE_1000BASE_X: + case AXGBE_PORT_MODE_NBASE_T: + advertising |= ADVERTISED_1000baseKX_Full; + break; + case AXGBE_PORT_MODE_10GBASE_T: + PMD_DRV_LOG(ERR, "10GBASE_T mode is not supported"); + break; + case AXGBE_PORT_MODE_10GBASE_R: + advertising |= ADVERTISED_10000baseKR_Full; + break; + case AXGBE_PORT_MODE_SFP: + switch (phy_data->sfp_base) { + case AXGBE_SFP_BASE_1000_T: + case AXGBE_SFP_BASE_1000_SX: + case AXGBE_SFP_BASE_1000_LX: + case AXGBE_SFP_BASE_1000_CX: + advertising |= ADVERTISED_1000baseKX_Full; + break; + default: + advertising |= ADVERTISED_10000baseKR_Full; + break; + } + break; + default: + advertising |= ADVERTISED_10000baseKR_Full; + break; + } + + return advertising; +} + +static int axgbe_phy_an_config(struct axgbe_port *pdata __rte_unused) +{ + return 0; + /* Dummy API since there is no case to support + * external phy devices registred through kerenl apis + */ +} + +static enum axgbe_an_mode axgbe_phy_an_sfp_mode(struct axgbe_phy_data *phy_data) +{ + switch (phy_data->sfp_base) { + case AXGBE_SFP_BASE_1000_T: + return AXGBE_AN_MODE_CL37_SGMII; + case AXGBE_SFP_BASE_1000_SX: + case AXGBE_SFP_BASE_1000_LX: + case AXGBE_SFP_BASE_1000_CX: + return AXGBE_AN_MODE_CL37; + default: + return AXGBE_AN_MODE_NONE; + } +} + +static enum axgbe_an_mode axgbe_phy_an_mode(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + + /* A KR re-driver will always require CL73 AN */ + if (phy_data->redrv) + return AXGBE_AN_MODE_CL73_REDRV; + + switch (phy_data->port_mode) { + case AXGBE_PORT_MODE_BACKPLANE: + return AXGBE_AN_MODE_CL73; + case AXGBE_PORT_MODE_BACKPLANE_2500: + return AXGBE_AN_MODE_NONE; + case AXGBE_PORT_MODE_1000BASE_T: + return AXGBE_AN_MODE_CL37_SGMII; + case AXGBE_PORT_MODE_1000BASE_X: + return AXGBE_AN_MODE_CL37; + case AXGBE_PORT_MODE_NBASE_T: + return AXGBE_AN_MODE_CL37_SGMII; + case AXGBE_PORT_MODE_10GBASE_T: + return AXGBE_AN_MODE_CL73; + case AXGBE_PORT_MODE_10GBASE_R: + return AXGBE_AN_MODE_NONE; + case AXGBE_PORT_MODE_SFP: + return axgbe_phy_an_sfp_mode(phy_data); + default: + return AXGBE_AN_MODE_NONE; + } +} + +static int axgbe_phy_set_redrv_mode_mdio(struct axgbe_port *pdata, + enum axgbe_phy_redrv_mode mode) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + u16 redrv_reg, redrv_val; + + redrv_reg = AXGBE_PHY_REDRV_MODE_REG + (phy_data->redrv_lane * 0x1000); + redrv_val = (u16)mode; + + return pdata->hw_if.write_ext_mii_regs(pdata, phy_data->redrv_addr, + redrv_reg, redrv_val); +} + +static int axgbe_phy_set_redrv_mode_i2c(struct axgbe_port *pdata, + enum axgbe_phy_redrv_mode mode) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + unsigned int redrv_reg; + int ret; + + /* Calculate the register to write */ + redrv_reg = AXGBE_PHY_REDRV_MODE_REG + (phy_data->redrv_lane * 0x1000); + + ret = axgbe_phy_redrv_write(pdata, redrv_reg, mode); + + return ret; +} + +static void axgbe_phy_set_redrv_mode(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + enum axgbe_phy_redrv_mode mode; + int ret; + + if (!phy_data->redrv) + return; + + mode = AXGBE_PHY_REDRV_MODE_CX; + if ((phy_data->port_mode == AXGBE_PORT_MODE_SFP) && + (phy_data->sfp_base != AXGBE_SFP_BASE_1000_CX) && + (phy_data->sfp_base != AXGBE_SFP_BASE_10000_CR)) + mode = AXGBE_PHY_REDRV_MODE_SR; + + ret = axgbe_phy_get_comm_ownership(pdata); + if (ret) + return; + + if (phy_data->redrv_if) + axgbe_phy_set_redrv_mode_i2c(pdata, mode); + else + axgbe_phy_set_redrv_mode_mdio(pdata, mode); + + axgbe_phy_put_comm_ownership(pdata); +} + +static void axgbe_phy_start_ratechange(struct axgbe_port *pdata) +{ + if (!XP_IOREAD_BITS(pdata, XP_DRIVER_INT_RO, STATUS)) + return; +} + +static void axgbe_phy_complete_ratechange(struct axgbe_port *pdata) +{ + unsigned int wait; + + /* Wait for command to complete */ + wait = AXGBE_RATECHANGE_COUNT; + while (wait--) { + if (!XP_IOREAD_BITS(pdata, XP_DRIVER_INT_RO, STATUS)) + return; + + rte_delay_us(1500); + } +} + +static void axgbe_phy_rrc(struct axgbe_port *pdata) +{ + unsigned int s0; + + axgbe_phy_start_ratechange(pdata); + + /* Receiver Reset Cycle */ + s0 = 0; + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, COMMAND, 5); + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 0); + + /* Call FW to make the change */ + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, s0); + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0); + XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1); + + axgbe_phy_complete_ratechange(pdata); +} + +static void axgbe_phy_power_off(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + + axgbe_phy_start_ratechange(pdata); + + /* Call FW to make the change */ + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, 0); + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0); + XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1); + axgbe_phy_complete_ratechange(pdata); + phy_data->cur_mode = AXGBE_MODE_UNKNOWN; +} + +static void axgbe_phy_sfi_mode(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + unsigned int s0; + + axgbe_phy_set_redrv_mode(pdata); + + axgbe_phy_start_ratechange(pdata); + + /* 10G/SFI */ + s0 = 0; + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, COMMAND, 3); + if (phy_data->sfp_cable != AXGBE_SFP_CABLE_PASSIVE) { + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 0); + } else { + if (phy_data->sfp_cable_len <= 1) + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 1); + else if (phy_data->sfp_cable_len <= 3) + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 2); + else + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 3); + } + + /* Call FW to make the change */ + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, s0); + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0); + XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1); + axgbe_phy_complete_ratechange(pdata); + phy_data->cur_mode = AXGBE_MODE_SFI; +} + +static void axgbe_phy_kr_mode(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + unsigned int s0; + + axgbe_phy_set_redrv_mode(pdata); + + axgbe_phy_start_ratechange(pdata); + + /* 10G/KR */ + s0 = 0; + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, COMMAND, 4); + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 0); + + /* Call FW to make the change */ + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, s0); + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0); + XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1); + axgbe_phy_complete_ratechange(pdata); + phy_data->cur_mode = AXGBE_MODE_KR; +} + +static enum axgbe_mode axgbe_phy_cur_mode(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + + return phy_data->cur_mode; +} + +static enum axgbe_mode axgbe_phy_switch_baset_mode(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + + /* No switching if not 10GBase-T */ + if (phy_data->port_mode != AXGBE_PORT_MODE_10GBASE_T) + return axgbe_phy_cur_mode(pdata); + + switch (axgbe_phy_cur_mode(pdata)) { + case AXGBE_MODE_SGMII_100: + case AXGBE_MODE_SGMII_1000: + return AXGBE_MODE_KR; + case AXGBE_MODE_KR: + default: + return AXGBE_MODE_SGMII_1000; + } +} + +static enum axgbe_mode axgbe_phy_switch_bp_2500_mode(struct axgbe_port *pdata + __rte_unused) +{ + return AXGBE_MODE_KX_2500; +} + +static enum axgbe_mode axgbe_phy_switch_bp_mode(struct axgbe_port *pdata) +{ + /* If we are in KR switch to KX, and vice-versa */ + switch (axgbe_phy_cur_mode(pdata)) { + case AXGBE_MODE_KX_1000: + return AXGBE_MODE_KR; + case AXGBE_MODE_KR: + default: + return AXGBE_MODE_KX_1000; + } +} + +static enum axgbe_mode axgbe_phy_switch_mode(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + + switch (phy_data->port_mode) { + case AXGBE_PORT_MODE_BACKPLANE: + return axgbe_phy_switch_bp_mode(pdata); + case AXGBE_PORT_MODE_BACKPLANE_2500: + return axgbe_phy_switch_bp_2500_mode(pdata); + case AXGBE_PORT_MODE_1000BASE_T: + case AXGBE_PORT_MODE_NBASE_T: + case AXGBE_PORT_MODE_10GBASE_T: + return axgbe_phy_switch_baset_mode(pdata); + case AXGBE_PORT_MODE_1000BASE_X: + case AXGBE_PORT_MODE_10GBASE_R: + case AXGBE_PORT_MODE_SFP: + /* No switching, so just return current mode */ + return axgbe_phy_cur_mode(pdata); + default: + return AXGBE_MODE_UNKNOWN; + } +} + +static enum axgbe_mode axgbe_phy_get_basex_mode(struct axgbe_phy_data *phy_data + __rte_unused, + int speed) +{ + switch (speed) { + case SPEED_1000: + return AXGBE_MODE_X; + case SPEED_10000: + return AXGBE_MODE_KR; + default: + return AXGBE_MODE_UNKNOWN; + } +} + +static enum axgbe_mode axgbe_phy_get_baset_mode(struct axgbe_phy_data *phy_data + __rte_unused, + int speed) +{ + switch (speed) { + case SPEED_100: + return AXGBE_MODE_SGMII_100; + case SPEED_1000: + return AXGBE_MODE_SGMII_1000; + case SPEED_10000: + return AXGBE_MODE_KR; + default: + return AXGBE_MODE_UNKNOWN; + } +} + +static enum axgbe_mode axgbe_phy_get_sfp_mode(struct axgbe_phy_data *phy_data, + int speed) +{ + switch (speed) { + case SPEED_100: + return AXGBE_MODE_SGMII_100; + case SPEED_1000: + if (phy_data->sfp_base == AXGBE_SFP_BASE_1000_T) + return AXGBE_MODE_SGMII_1000; + else + return AXGBE_MODE_X; + case SPEED_10000: + case SPEED_UNKNOWN: + return AXGBE_MODE_SFI; + default: + return AXGBE_MODE_UNKNOWN; + } +} + +static enum axgbe_mode axgbe_phy_get_bp_2500_mode(int speed) +{ + switch (speed) { + case SPEED_2500: + return AXGBE_MODE_KX_2500; + default: + return AXGBE_MODE_UNKNOWN; + } +} + +static enum axgbe_mode axgbe_phy_get_bp_mode(int speed) +{ + switch (speed) { + case SPEED_1000: + return AXGBE_MODE_KX_1000; + case SPEED_10000: + return AXGBE_MODE_KR; + default: + return AXGBE_MODE_UNKNOWN; + } +} + +static enum axgbe_mode axgbe_phy_get_mode(struct axgbe_port *pdata, + int speed) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + + switch (phy_data->port_mode) { + case AXGBE_PORT_MODE_BACKPLANE: + return axgbe_phy_get_bp_mode(speed); + case AXGBE_PORT_MODE_BACKPLANE_2500: + return axgbe_phy_get_bp_2500_mode(speed); + case AXGBE_PORT_MODE_1000BASE_T: + case AXGBE_PORT_MODE_NBASE_T: + case AXGBE_PORT_MODE_10GBASE_T: + return axgbe_phy_get_baset_mode(phy_data, speed); + case AXGBE_PORT_MODE_1000BASE_X: + case AXGBE_PORT_MODE_10GBASE_R: + return axgbe_phy_get_basex_mode(phy_data, speed); + case AXGBE_PORT_MODE_SFP: + return axgbe_phy_get_sfp_mode(phy_data, speed); + default: + return AXGBE_MODE_UNKNOWN; + } +} + +static void axgbe_phy_set_mode(struct axgbe_port *pdata, enum axgbe_mode mode) +{ + switch (mode) { + case AXGBE_MODE_KR: + axgbe_phy_kr_mode(pdata); + break; + case AXGBE_MODE_SFI: + axgbe_phy_sfi_mode(pdata); + break; + default: + break; + } +} + +static bool axgbe_phy_check_mode(struct axgbe_port *pdata, + enum axgbe_mode mode, u32 advert) +{ + if (pdata->phy.autoneg == AUTONEG_ENABLE) { + if (pdata->phy.advertising & advert) + return true; + } else { + enum axgbe_mode cur_mode; + + cur_mode = axgbe_phy_get_mode(pdata, pdata->phy.speed); + if (cur_mode == mode) + return true; + } + + return false; +} + +static bool axgbe_phy_use_basex_mode(struct axgbe_port *pdata, + enum axgbe_mode mode) +{ + switch (mode) { + case AXGBE_MODE_X: + return axgbe_phy_check_mode(pdata, mode, + ADVERTISED_1000baseT_Full); + case AXGBE_MODE_KR: + return axgbe_phy_check_mode(pdata, mode, + ADVERTISED_10000baseT_Full); + default: + return false; + } +} + +static bool axgbe_phy_use_baset_mode(struct axgbe_port *pdata, + enum axgbe_mode mode) +{ + switch (mode) { + case AXGBE_MODE_SGMII_100: + return axgbe_phy_check_mode(pdata, mode, + ADVERTISED_100baseT_Full); + case AXGBE_MODE_SGMII_1000: + return axgbe_phy_check_mode(pdata, mode, + ADVERTISED_1000baseT_Full); + case AXGBE_MODE_KR: + return axgbe_phy_check_mode(pdata, mode, + ADVERTISED_10000baseT_Full); + default: + return false; + } +} + +static bool axgbe_phy_use_sfp_mode(struct axgbe_port *pdata, + enum axgbe_mode mode) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + + switch (mode) { + case AXGBE_MODE_X: + if (phy_data->sfp_base == AXGBE_SFP_BASE_1000_T) + return false; + return axgbe_phy_check_mode(pdata, mode, + ADVERTISED_1000baseT_Full); + case AXGBE_MODE_SGMII_100: + if (phy_data->sfp_base != AXGBE_SFP_BASE_1000_T) + return false; + return axgbe_phy_check_mode(pdata, mode, + ADVERTISED_100baseT_Full); + case AXGBE_MODE_SGMII_1000: + if (phy_data->sfp_base != AXGBE_SFP_BASE_1000_T) + return false; + return axgbe_phy_check_mode(pdata, mode, + ADVERTISED_1000baseT_Full); + case AXGBE_MODE_SFI: + return axgbe_phy_check_mode(pdata, mode, + ADVERTISED_10000baseT_Full); + default: + return false; + } +} + +static bool axgbe_phy_use_bp_2500_mode(struct axgbe_port *pdata, + enum axgbe_mode mode) +{ + switch (mode) { + case AXGBE_MODE_KX_2500: + return axgbe_phy_check_mode(pdata, mode, + ADVERTISED_2500baseX_Full); + default: + return false; + } +} + +static bool axgbe_phy_use_bp_mode(struct axgbe_port *pdata, + enum axgbe_mode mode) +{ + switch (mode) { + case AXGBE_MODE_KX_1000: + return axgbe_phy_check_mode(pdata, mode, + ADVERTISED_1000baseKX_Full); + case AXGBE_MODE_KR: + return axgbe_phy_check_mode(pdata, mode, + ADVERTISED_10000baseKR_Full); + default: + return false; + } +} + +static bool axgbe_phy_use_mode(struct axgbe_port *pdata, enum axgbe_mode mode) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + + switch (phy_data->port_mode) { + case AXGBE_PORT_MODE_BACKPLANE: + return axgbe_phy_use_bp_mode(pdata, mode); + case AXGBE_PORT_MODE_BACKPLANE_2500: + return axgbe_phy_use_bp_2500_mode(pdata, mode); + case AXGBE_PORT_MODE_1000BASE_T: + case AXGBE_PORT_MODE_NBASE_T: + case AXGBE_PORT_MODE_10GBASE_T: + return axgbe_phy_use_baset_mode(pdata, mode); + case AXGBE_PORT_MODE_1000BASE_X: + case AXGBE_PORT_MODE_10GBASE_R: + return axgbe_phy_use_basex_mode(pdata, mode); + case AXGBE_PORT_MODE_SFP: + return axgbe_phy_use_sfp_mode(pdata, mode); + default: + return false; + } +} + +static int axgbe_phy_link_status(struct axgbe_port *pdata, int *an_restart) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + unsigned int reg; + + *an_restart = 0; + + if (phy_data->port_mode == AXGBE_PORT_MODE_SFP) { + /* Check SFP signals */ + axgbe_phy_sfp_detect(pdata); + + if (phy_data->sfp_changed) { + *an_restart = 1; + return 0; + } + + if (phy_data->sfp_mod_absent || phy_data->sfp_rx_los) + return 0; + } + + /* Link status is latched low, so read once to clear + * and then read again to get current state + */ + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); + if (reg & MDIO_STAT1_LSTATUS) + return 1; + + /* No link, attempt a receiver reset cycle */ + if (phy_data->rrc_count++) { + phy_data->rrc_count = 0; + axgbe_phy_rrc(pdata); + } + + return 0; +} + static void axgbe_phy_sfp_gpio_setup(struct axgbe_port *pdata) { struct axgbe_phy_data *phy_data = pdata->phy_data; @@ -557,6 +1888,59 @@ static bool axgbe_phy_port_enabled(struct axgbe_port *pdata) return true; } +static void axgbe_phy_stop(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + + /* Reset SFP data */ + axgbe_phy_sfp_reset(phy_data); + axgbe_phy_sfp_mod_absent(pdata); + + /* Power off the PHY */ + axgbe_phy_power_off(pdata); + + /* Stop the I2C controller */ + pdata->i2c_if.i2c_stop(pdata); +} + +static int axgbe_phy_start(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + int ret; + + /* Start the I2C controller */ + ret = pdata->i2c_if.i2c_start(pdata); + if (ret) + return ret; + + /* Start in highest supported mode */ + axgbe_phy_set_mode(pdata, phy_data->start_mode); + + /* After starting the I2C controller, we can check for an SFP */ + switch (phy_data->port_mode) { + case AXGBE_PORT_MODE_SFP: + axgbe_phy_sfp_detect(pdata); + break; + default: + break; + } + + return ret; +} + +static int axgbe_phy_reset(struct axgbe_port *pdata) +{ + struct axgbe_phy_data *phy_data = pdata->phy_data; + enum axgbe_mode cur_mode; + + /* Reset by power cycling the PHY */ + cur_mode = phy_data->cur_mode; + axgbe_phy_power_off(pdata); + /* First time reset is done with passed unknown mode*/ + axgbe_phy_set_mode(pdata, cur_mode); + return 0; +} + static int axgbe_phy_init(struct axgbe_port *pdata) { struct axgbe_phy_data *phy_data; @@ -796,4 +2180,17 @@ void axgbe_init_function_ptrs_phy_v2(struct axgbe_phy_if *phy_if) struct axgbe_phy_impl_if *phy_impl = &phy_if->phy_impl; phy_impl->init = axgbe_phy_init; + phy_impl->reset = axgbe_phy_reset; + phy_impl->start = axgbe_phy_start; + phy_impl->stop = axgbe_phy_stop; + phy_impl->link_status = axgbe_phy_link_status; + phy_impl->use_mode = axgbe_phy_use_mode; + phy_impl->set_mode = axgbe_phy_set_mode; + phy_impl->get_mode = axgbe_phy_get_mode; + phy_impl->switch_mode = axgbe_phy_switch_mode; + phy_impl->cur_mode = axgbe_phy_cur_mode; + phy_impl->an_mode = axgbe_phy_an_mode; + phy_impl->an_config = axgbe_phy_an_config; + phy_impl->an_advertising = axgbe_phy_an_advertising; + phy_impl->an_outcome = axgbe_phy_an_outcome; } -- 2.7.4