From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from NAM02-SN1-obe.outbound.protection.outlook.com (mail-sn1nam02on0047.outbound.protection.outlook.com [104.47.36.47]) by dpdk.org (Postfix) with ESMTP id CB9881C950 for ; Thu, 5 Apr 2018 08:40:21 +0200 (CEST) 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=FcuOPHVxISHQEwSXSqGQnDawb0S4y23fSWj2yqDYzQU=; b=r9sUTQ9lYart48Hdg3n2F8iF/yJUpuX3NVUYLH4M8lsdDGfQcfd0N1St6f4lz4sWvMcREKXqHJRQNfjcetsbNDCBzE7xTZYi61uDA13j+tSN2g3EXiMk7jgj/nzFDqQzj0l9QZGH9SdapKdI8iaRCbjKLzM79LzPec7l8yQRd1Y= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=Ravi1.Kumar@amd.com; Received: from wallaby-smavila.amd.com (202.56.249.162) by BN6PR12MB1505.namprd12.prod.outlook.com (2603:10b6:405:11::18) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.653.12; Thu, 5 Apr 2018 06:40:19 +0000 From: Ravi Kumar To: dev@dpdk.org Cc: ferruh.yigit@intel.com Date: Thu, 5 Apr 2018 02:39:38 -0400 Message-Id: <1522910389-35530-6-git-send-email-Ravi1.kumar@amd.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1522910389-35530-1-git-send-email-Ravi1.kumar@amd.com> References: <1520584954-130575-1-git-send-email-Ravi1.kumar@amd.com> <1522910389-35530-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: BMXPR01CA0040.INDPRD01.PROD.OUTLOOK.COM (2603:1096:b00:c::26) To BN6PR12MB1505.namprd12.prod.outlook.com (2603:10b6:405:11::18) X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: ca902997-a3c6-4f2d-6492-08d59ac01a09 X-MS-Office365-Filtering-HT: Tenant X-Microsoft-Antispam: UriScan:; BCL:0; PCL:0; RULEID:(7020095)(4652020)(5600026)(4604075)(48565401081)(4534165)(4627221)(201703031133081)(201702281549075)(2017052603328)(7153060)(7193020); SRVR:BN6PR12MB1505; X-Microsoft-Exchange-Diagnostics: 1; BN6PR12MB1505; 3:Gto0uLuzlQ6vX50ubGPMl+tmDHjcDkMpO0qxCwlCKit5qNXSge3vLFyFWSSTNBd6PXhhPEZuWl+k0ME87NE1V5nfWTfuUUFYlu1JhV43mm+/ou3Aq+bBu7Xn3QL/9B956iPjVT8H5yTynTRvfZ4xNo6tAgcDDDe+RfvI924HP7+kH+RBm97UceWow8fN+85WQq7PIXvDksAUyedBb8QiXvhZAAD8JH0WZQz/7daDwiBPIhWLPKjhdutqvJWGS0tk; 25:EqYrQQFARtdRff8y6zIclj0otSPrnohh5lakUr8bmXu3kAJuxiR3HxCm9bpzW12lqwYglZnd5+V27pNGeV/tQKlppLjFfwSyOriDHogFyH4kjWeo2Ofnn18YIikn4oHT2+a/pHxL79Jj+Pm8A8q0FSLFgfl5LyBCsgVhkRLgTtFxIMXDZHWa4yrqeo20F7PmY6To3DIvlAUSnOvR7kh0DuS9znA68EE5l3HwCQW4eoQ1FD2wn23MGkfyPzUPJgasP47fGhAl200x43IgGXia8ZwXckF8nP8tggH5mkc47cevQP5PkrCF7ZYks+PCkN2lF67I5U/ZboV1lYhzRfvYAw==; 31:O7g+fe7w9S8Z76aQ7oUCsXoJYHUAeiQni3I1MTCK5zWbplOegj7sR1JEpUVpKPf1Ul5oGmd7EMZ70URJaS11n9F3DHMiZBWgVg2lTTYzldE4fjlACtJ9a/wrMPFNK5Pk6KYDLEqdlRL0EKOnCPlASOKaQyEDkYrNr1FNI17kd5OvnN6B4sPcJ+NjPtiTzyICFJ+Xipc/cDvznvs72H+uhvagW6Y/Vch1Ap/d3r1YRQ0= X-MS-TrafficTypeDiagnostic: BN6PR12MB1505: X-Microsoft-Exchange-Diagnostics: 1; BN6PR12MB1505; 20:ExXQ0EeJMzNcOMBgVwbhBT9gX0jZYKn6WwjrMtt9coGhQvRJ3MvSlvrKBzLW+t6L+ZX2dspuw6aIyq9IVL/uvpuvIYp59xCxQ67Eu02E88s7IWA8/NO8+ublCZJiDSFZJqi9TlC1wWgyewZGPDXpuVuJRYL2M6kmYPkXUgiHqjMXR09n4dREQVIMWkqxGTuAyUGOWkJipsqwfxo6Pc2NZ0V3oNotvmXbenF3P6wf7kDT8FbGIQRvaz/kE3Q3KfFfnTSLwsMN4todfCgP7AKzIT6GTavkXh+tLFwCLIkFzWJ4UIIfiLSen36axdX/HIxUDBfs1YU5LOa6bRpbqRzuPjM/dbCUS4asgRSmvxj85WpnsrIZ+Dzquvw10/9JcMaiDlqslPqmtSNokPDEnV/le8yOEJ+Cy6SuAOGrqM2gMUCaUK0b5v3svBrAJ2OgAT29TKvMLF480SCcQnDzNENRTLGrBQH2MUAMURolaQK0NyitHz58Cgd+bfRLz19xGb2f; 4:xD+e9Bx2l9lkEM+3rHmOduWLB51KQVc8nc8LTat8Wkv/YlK47Ll3UxYCDmLorJKVLf1owXD6nUvV/cS7YfCnP7QfUNDN4mzI+9g6GGUzfeZOep2Fw9KbALpUz4d1O+iYrPSuyOOPs+8jmhb1uhi/OAvt83VsQNTSg7psPWvySSB51vjBilZr4setsXwJFP14ywrPhV7RKkPT/2Sqv21VQAy1JOqTKJ6nXeqEA0Bh3qsGCxZny411sXKKSsS0zIkRG/eYvSslnT9Jvaz7F/MCATBpxN8Tl0jNXpyr4urBJ17qfZh0EBtC9i7ZMdaIRzHD 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)(3231221)(944501327)(52105095)(93006095)(93001095)(10201501046)(6055026)(6041310)(20161123564045)(20161123562045)(20161123558120)(201703131423095)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(20161123560045)(6072148)(201708071742011); SRVR:BN6PR12MB1505; BCL:0; PCL:0; RULEID:; SRVR:BN6PR12MB1505; X-Forefront-PRVS: 06339BAE63 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10009020)(366004)(346002)(376002)(396003)(39860400002)(39380400002)(199004)(189003)(3846002)(476003)(2616005)(316002)(446003)(956004)(305945005)(7736002)(478600001)(16586007)(16526019)(6486002)(186003)(76176011)(5660300001)(59450400001)(97736004)(2906002)(6116002)(486006)(386003)(26005)(50466002)(11346002)(25786009)(8936002)(86362001)(50226002)(66066001)(47776003)(16200700003)(48376002)(2351001)(6666003)(2361001)(6916009)(4326008)(81166006)(36756003)(53416004)(51416003)(68736007)(53946003)(7696005)(8676002)(106356001)(105586002)(52116002)(81156014)(53936002)(72206003)(559001)(579004)(569006); DIR:OUT; SFP:1101; SCL:1; SRVR:BN6PR12MB1505; H:wallaby-smavila.amd.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; A:1; MX:1; Received-SPF: None (protection.outlook.com: amd.com does not designate permitted sender hosts) X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; BN6PR12MB1505; 23:jZS2J0SCYFxo971DlRCHNfy71VdixIQrD960R1HSF?= =?us-ascii?Q?rn9SN1jwS7X0312hLon3yZbxtqHOeLmkc/yqFhmoLy9gSXSFzkSObucDLTx8?= =?us-ascii?Q?hgU33ZlLSJaHPj9doHaIHNrEyAxYGXUN+FKq5kKox8C93TUjIdzeWx0LVL/o?= =?us-ascii?Q?RHrW89ZizGif324AGg5Z6WQ5cXPkaQ6qhA56hjRsnLzoM+UFRg5OkdPDnO6H?= =?us-ascii?Q?CKUsws8PG75ABENvHNb/7d6+VUiYWVGLlZlHyjj9SCYrn1vmXKSyhIXgvI11?= =?us-ascii?Q?U7cycDR8iusFFwq8M840TAz21Vn9uvP6ri21uOjEkU6MIqb3JZasRzxbSf6G?= =?us-ascii?Q?cXywRb2MpzZ9+90HohyepQac2AaW7sRhtkVnOFIW7WvsKZtkVN9AarvRXO2v?= =?us-ascii?Q?FbgY4iB6ZHbtj7QpMqfLQDPkXalFu6QkpCN6OHdq6ZZWvUZkjLc07Kg6PMEz?= =?us-ascii?Q?q6bImiwOp/kl9ZZ/C1d0TaEx4WSb6IfnJXI9WxdwdKN/9s0Qpv1HD27ZA8ow?= =?us-ascii?Q?Z3Kt+VCbF2aRfCs1CAeheXCjBosywavR6Ehs3+lkEXzm2s9QGFYTk2hmPBOY?= =?us-ascii?Q?ZE4Bb03sQWKYh+QBCjhGBPAmMmncsbp7caHOOVJ9p2r7asY0c3flN67eWQEM?= =?us-ascii?Q?3Zn/fItUDhlq6NBA7BxxWHKZv8f0rmXOul5uMF9l4w92DljRScfw7VoJNfER?= =?us-ascii?Q?Q/NndRz+BzH9AEdAHjySnZZKPLsPphBZDyvoplxpMpJPhffrCXJZWNkWQCUu?= =?us-ascii?Q?DzdaepuLKuiaxBCoQVesQ9/kwM/70/FqTaQoLK+JAgU/74R8rN9ah21RhT5y?= =?us-ascii?Q?O+CjRjAngXOOiK7fFu+3oYEJZ7NqjpfyVfL91mYMiZiNTQvKKSk+CflRSnxf?= =?us-ascii?Q?hPcY/4kyTzCS5sJRfjOHtmpkR+VFDG2radB/WHIBY+S9SceBWbGXW2BL/PcW?= =?us-ascii?Q?VXfox6XrYqMCb0+fKwKJ3xjLK3V87pLgBoEBYEK4S5je+uvGDXJN7ChQt6gB?= =?us-ascii?Q?eOipnQ6CcsFbIGl7FWYGej9zA8klx80Uw2GjmCGcrXc6fmS8bPzYbNnbCCZq?= =?us-ascii?Q?W0HvEsUwtU0c0J5WxLVH4dmjJ3ZaAgJY80DUXpwoRq9va2y3WSNM/6kxhw1G?= =?us-ascii?Q?QN57Fj3aX9wJiHWx6SmcYjKHo1jK7oNu56tnSH0xXCWV0pylS3uoV2cqzLXJ?= =?us-ascii?Q?OtzGHQHQlczydcbnulgXGtv3BAywJOFCDCrIsJT5Htei9lwmEFgkG0UPyf69?= =?us-ascii?Q?xBKmoiCuGHmHyW5hO+Fbb/5VqxrgPmiieteL5P/97sbM00cq5UISSBRf2Et7?= =?us-ascii?Q?B5SQ2eQ1DylMVU2CEfvv3S9gIY0/ZNmBq4yfVpYjW3u+5lat4hbmN4rf70PH?= =?us-ascii?Q?CDABQ=3D=3D?= X-Microsoft-Antispam-Message-Info: Ysf5x3oTb77uT3J9RFRo5aCbY8Bmgp7rVEn47+LyCNFhQZG8x0nLyWWPoAoetjo0vaiFdjwfC7epyc0E5zrWseomqjZuapMflRNdyHHDjT4IEgCLzNA4aiu9CFfEOpsF1jq9bkcyvmRRM2gBfMs8X/uvc8jcedG28tEgOTjzfwNRk43TbqRGuekbvxoYPxwz X-Microsoft-Exchange-Diagnostics: 1; BN6PR12MB1505; 6:FjGpI9sA3LXpTAIuJACLYNP0su7GLeszzS9UMf4yh4j7VUDpElLIvu673zRsCWb56S2lh0EzCkRSMQAniYeE3tNrf3BuZ/E4BWJUloVC1uaTOa1tMwt8GYQalSshJkrqXBzKNcyyXu+ljlOE72XVSo+mQ1nKvPlzWbNDLA3dtDNddNZ+ZKjY769jeM8EpuMXhCibZYesVKz3cv1aXUXp4cfu5A+gFlokim2gcKZVnnK27W7xtGnYFPR/gGsnzwTomzCarHtm2nIzp+NqxqsAEtua6OVE6XsWzArOOKzLQZN9J/3z2QwESdIfqFuYmq64IfV3EqKV+09Ds8RjbM3vCo2TWLRDHi04JEqJr5wmO+PZk71B6VnVMyO0cOT10/03tRTPdzAZ7cZV7aJRUGOFbum32hoG2S+GG42QnRjfyhOmBaWayeOY3w0hXnrx3PuUuob8wbzPQEZwg8JImUDIjA==; 5:6GoqBAhMT0K7jPcl0d5YMW+X02PHro0awqn9BVpFQVR6XWsgn6MStC3BCQchHdKnXi7tuX7i9s6jl0xIn74lnlCptWloPE7Px7igiaFNWVdPlTknO1Eaxgp3IoVBWYqUD/BySDLQQXS3LMZ/NWV81d2g94kJpajcCVYpohSzoRI=; 24:LqCslRz/EYPfsPpeOgdiYkIuTqf5T55klGhvG/qFtrt1V8SGEWMSsm9XPinQC9UMJXYGz0Uuwnh7mCnejyThXBZ0zaKNd02/O8rOTxEP/KM= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1; BN6PR12MB1505; 7:0IFN+u2tBzNQoX11DI05W5SvfpVQc45Ze42jVOez9J0nqzkqtjU8isT2Fb7WtZNzf63uIq4ad7TzJSkyYYkOKR2o7+E1qmPWMogIgeuXGs5bcmuEQ3iJwwddovdhnyUmtudKBGWjj5yhw/sHK0N+tTmNa9CcxRlQ1bc6BJYf2rQGZAfNxMX2S52aqlcLwTCxMxuP+KwfsGO0lkVXznKH/sNZ+Ymdg8QZW3vB7WEl5WsQFM0eQr25BlMR9HBPVmxF; 20:kdovjIjjylHxJT1CP/PqtuYucN9KjAXq4EsqRNtYZt/3NuMeQcJcGYg0pMtX9z0eg0DFBCXmxUIbxqKLE01UlxL3xjyK0m6d2mc2uKKBM5y0oUbf6oYNON5Dfe8FP2sx2LhJfDYsOaCMos9oABopjmLeC5c7Cb08TZWGum69FgCEpTHszPwOf4fBUPVNUxTHNcr4K9EEj180dqCogaTe6E/OaEkiFWVOBM4DA7p018D2HH6gDE/d/+4IBRxbW9x4 X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 05 Apr 2018 06:40:19.5007 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: ca902997-a3c6-4f2d-6492-08d59ac01a09 X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 3dd8961f-e488-4e60-8e11-a82d994e183d X-MS-Exchange-Transport-CrossTenantHeadersStamped: BN6PR12MB1505 Subject: [dpdk-dev] [PATCH v4 06/17] 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: Thu, 05 Apr 2018 06:40:22 -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 4a45ee6..82fe7bf 100644 --- a/drivers/net/axgbe/axgbe_dev.c +++ b/drivers/net/axgbe/axgbe_dev.c @@ -188,6 +188,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; @@ -224,9 +248,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 691ff79..914f34f 100644 --- a/drivers/net/axgbe/axgbe_mdio.c +++ b/drivers/net/axgbe/axgbe_mdio.c @@ -7,6 +7,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\n"); + 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)\n", 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\n"); + 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\n"); + 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\n"); + 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\n"); + 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) @@ -78,4 +1035,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 047b619..19bd4be 100644 --- a/drivers/net/axgbe/axgbe_phy_impl.c +++ b/drivers/net/axgbe/axgbe_phy_impl.c @@ -239,6 +239,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\n"); + + 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\n"); + 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\n"); + 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\n"); + 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\n"); + 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; @@ -435,6 +1766,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; @@ -674,4 +2058,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