18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2006, 2007, 2008, 2009 QLogic Corporation. All rights reserved. 38c2ecf20Sopenharmony_ci * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two 68c2ecf20Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 78c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 88c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the 98c2ecf20Sopenharmony_ci * OpenIB.org BSD license below: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or 128c2ecf20Sopenharmony_ci * without modification, are permitted provided that the following 138c2ecf20Sopenharmony_ci * conditions are met: 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above 168c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 178c2ecf20Sopenharmony_ci * disclaimer. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above 208c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 218c2ecf20Sopenharmony_ci * disclaimer in the documentation and/or other materials 228c2ecf20Sopenharmony_ci * provided with the distribution. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 258c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 268c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 278c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 288c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 298c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 308c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 318c2ecf20Sopenharmony_ci * SOFTWARE. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/delay.h> 358c2ecf20Sopenharmony_ci#include <linux/pci.h> 368c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include "qib.h" 398c2ecf20Sopenharmony_ci#include "qib_qsfp.h" 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * QSFP support for ib_qib driver, using "Two Wire Serial Interface" driver 438c2ecf20Sopenharmony_ci * in qib_twsi.c 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci#define QSFP_MAX_RETRY 4 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int qsfp_read(struct qib_pportdata *ppd, int addr, void *bp, int len) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct qib_devdata *dd = ppd->dd; 508c2ecf20Sopenharmony_ci u32 out, mask; 518c2ecf20Sopenharmony_ci int ret, cnt, pass = 0; 528c2ecf20Sopenharmony_ci int stuck = 0; 538c2ecf20Sopenharmony_ci u8 *buff = bp; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&dd->eep_lock); 568c2ecf20Sopenharmony_ci if (ret) 578c2ecf20Sopenharmony_ci goto no_unlock; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (dd->twsi_eeprom_dev == QIB_TWSI_NO_DEV) { 608c2ecf20Sopenharmony_ci ret = -ENXIO; 618c2ecf20Sopenharmony_ci goto bail; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* 658c2ecf20Sopenharmony_ci * We presume, if we are called at all, that this board has 668c2ecf20Sopenharmony_ci * QSFP. This is on the same i2c chain as the legacy parts, 678c2ecf20Sopenharmony_ci * but only responds if the module is selected via GPIO pins. 688c2ecf20Sopenharmony_ci * Further, there are very long setup and hold requirements 698c2ecf20Sopenharmony_ci * on MODSEL. 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_ci mask = QSFP_GPIO_MOD_SEL_N | QSFP_GPIO_MOD_RST_N | QSFP_GPIO_LP_MODE; 728c2ecf20Sopenharmony_ci out = QSFP_GPIO_MOD_RST_N | QSFP_GPIO_LP_MODE; 738c2ecf20Sopenharmony_ci if (ppd->hw_pidx) { 748c2ecf20Sopenharmony_ci mask <<= QSFP_GPIO_PORT2_SHIFT; 758c2ecf20Sopenharmony_ci out <<= QSFP_GPIO_PORT2_SHIFT; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci dd->f_gpio_mod(dd, out, mask, mask); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* 818c2ecf20Sopenharmony_ci * Module could take up to 2 Msec to respond to MOD_SEL, and there 828c2ecf20Sopenharmony_ci * is no way to tell if it is ready, so we must wait. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci msleep(20); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* Make sure TWSI bus is in sane state. */ 878c2ecf20Sopenharmony_ci ret = qib_twsi_reset(dd); 888c2ecf20Sopenharmony_ci if (ret) { 898c2ecf20Sopenharmony_ci qib_dev_porterr(dd, ppd->port, 908c2ecf20Sopenharmony_ci "QSFP interface Reset for read failed\n"); 918c2ecf20Sopenharmony_ci ret = -EIO; 928c2ecf20Sopenharmony_ci stuck = 1; 938c2ecf20Sopenharmony_ci goto deselect; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* All QSFP modules are at A0 */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci cnt = 0; 998c2ecf20Sopenharmony_ci while (cnt < len) { 1008c2ecf20Sopenharmony_ci unsigned in_page; 1018c2ecf20Sopenharmony_ci int wlen = len - cnt; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci in_page = addr % QSFP_PAGESIZE; 1048c2ecf20Sopenharmony_ci if ((in_page + wlen) > QSFP_PAGESIZE) 1058c2ecf20Sopenharmony_ci wlen = QSFP_PAGESIZE - in_page; 1068c2ecf20Sopenharmony_ci ret = qib_twsi_blk_rd(dd, QSFP_DEV, addr, buff + cnt, wlen); 1078c2ecf20Sopenharmony_ci /* Some QSFP's fail first try. Retry as experiment */ 1088c2ecf20Sopenharmony_ci if (ret && cnt == 0 && ++pass < QSFP_MAX_RETRY) 1098c2ecf20Sopenharmony_ci continue; 1108c2ecf20Sopenharmony_ci if (ret) { 1118c2ecf20Sopenharmony_ci /* qib_twsi_blk_rd() 1 for error, else 0 */ 1128c2ecf20Sopenharmony_ci ret = -EIO; 1138c2ecf20Sopenharmony_ci goto deselect; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci addr += wlen; 1168c2ecf20Sopenharmony_ci cnt += wlen; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci ret = cnt; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cideselect: 1218c2ecf20Sopenharmony_ci /* 1228c2ecf20Sopenharmony_ci * Module could take up to 10 uSec after transfer before 1238c2ecf20Sopenharmony_ci * ready to respond to MOD_SEL negation, and there is no way 1248c2ecf20Sopenharmony_ci * to tell if it is ready, so we must wait. 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ci udelay(10); 1278c2ecf20Sopenharmony_ci /* set QSFP MODSEL, RST. LP all high */ 1288c2ecf20Sopenharmony_ci dd->f_gpio_mod(dd, mask, mask, mask); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* 1318c2ecf20Sopenharmony_ci * Module could take up to 2 Msec to respond to MOD_SEL 1328c2ecf20Sopenharmony_ci * going away, and there is no way to tell if it is ready. 1338c2ecf20Sopenharmony_ci * so we must wait. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ci if (stuck) 1368c2ecf20Sopenharmony_ci qib_dev_err(dd, "QSFP interface bus stuck non-idle\n"); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (pass >= QSFP_MAX_RETRY && ret) 1398c2ecf20Sopenharmony_ci qib_dev_porterr(dd, ppd->port, "QSFP failed even retrying\n"); 1408c2ecf20Sopenharmony_ci else if (pass) 1418c2ecf20Sopenharmony_ci qib_dev_porterr(dd, ppd->port, "QSFP retries: %d\n", pass); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci msleep(20); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cibail: 1468c2ecf20Sopenharmony_ci mutex_unlock(&dd->eep_lock); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cino_unlock: 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* 1538c2ecf20Sopenharmony_ci * qsfp_write 1548c2ecf20Sopenharmony_ci * We do not ordinarily write the QSFP, but this is needed to select 1558c2ecf20Sopenharmony_ci * the page on non-flat QSFPs, and possibly later unusual cases 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_cistatic int qib_qsfp_write(struct qib_pportdata *ppd, int addr, void *bp, 1588c2ecf20Sopenharmony_ci int len) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct qib_devdata *dd = ppd->dd; 1618c2ecf20Sopenharmony_ci u32 out, mask; 1628c2ecf20Sopenharmony_ci int ret, cnt; 1638c2ecf20Sopenharmony_ci u8 *buff = bp; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&dd->eep_lock); 1668c2ecf20Sopenharmony_ci if (ret) 1678c2ecf20Sopenharmony_ci goto no_unlock; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (dd->twsi_eeprom_dev == QIB_TWSI_NO_DEV) { 1708c2ecf20Sopenharmony_ci ret = -ENXIO; 1718c2ecf20Sopenharmony_ci goto bail; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* 1758c2ecf20Sopenharmony_ci * We presume, if we are called at all, that this board has 1768c2ecf20Sopenharmony_ci * QSFP. This is on the same i2c chain as the legacy parts, 1778c2ecf20Sopenharmony_ci * but only responds if the module is selected via GPIO pins. 1788c2ecf20Sopenharmony_ci * Further, there are very long setup and hold requirements 1798c2ecf20Sopenharmony_ci * on MODSEL. 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci mask = QSFP_GPIO_MOD_SEL_N | QSFP_GPIO_MOD_RST_N | QSFP_GPIO_LP_MODE; 1828c2ecf20Sopenharmony_ci out = QSFP_GPIO_MOD_RST_N | QSFP_GPIO_LP_MODE; 1838c2ecf20Sopenharmony_ci if (ppd->hw_pidx) { 1848c2ecf20Sopenharmony_ci mask <<= QSFP_GPIO_PORT2_SHIFT; 1858c2ecf20Sopenharmony_ci out <<= QSFP_GPIO_PORT2_SHIFT; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci dd->f_gpio_mod(dd, out, mask, mask); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * Module could take up to 2 Msec to respond to MOD_SEL, 1918c2ecf20Sopenharmony_ci * and there is no way to tell if it is ready, so we must wait. 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci msleep(20); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* Make sure TWSI bus is in sane state. */ 1968c2ecf20Sopenharmony_ci ret = qib_twsi_reset(dd); 1978c2ecf20Sopenharmony_ci if (ret) { 1988c2ecf20Sopenharmony_ci qib_dev_porterr(dd, ppd->port, 1998c2ecf20Sopenharmony_ci "QSFP interface Reset for write failed\n"); 2008c2ecf20Sopenharmony_ci ret = -EIO; 2018c2ecf20Sopenharmony_ci goto deselect; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* All QSFP modules are at A0 */ 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci cnt = 0; 2078c2ecf20Sopenharmony_ci while (cnt < len) { 2088c2ecf20Sopenharmony_ci unsigned in_page; 2098c2ecf20Sopenharmony_ci int wlen = len - cnt; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci in_page = addr % QSFP_PAGESIZE; 2128c2ecf20Sopenharmony_ci if ((in_page + wlen) > QSFP_PAGESIZE) 2138c2ecf20Sopenharmony_ci wlen = QSFP_PAGESIZE - in_page; 2148c2ecf20Sopenharmony_ci ret = qib_twsi_blk_wr(dd, QSFP_DEV, addr, buff + cnt, wlen); 2158c2ecf20Sopenharmony_ci if (ret) { 2168c2ecf20Sopenharmony_ci /* qib_twsi_blk_wr() 1 for error, else 0 */ 2178c2ecf20Sopenharmony_ci ret = -EIO; 2188c2ecf20Sopenharmony_ci goto deselect; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci addr += wlen; 2218c2ecf20Sopenharmony_ci cnt += wlen; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci ret = cnt; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cideselect: 2268c2ecf20Sopenharmony_ci /* 2278c2ecf20Sopenharmony_ci * Module could take up to 10 uSec after transfer before 2288c2ecf20Sopenharmony_ci * ready to respond to MOD_SEL negation, and there is no way 2298c2ecf20Sopenharmony_ci * to tell if it is ready, so we must wait. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci udelay(10); 2328c2ecf20Sopenharmony_ci /* set QSFP MODSEL, RST, LP high */ 2338c2ecf20Sopenharmony_ci dd->f_gpio_mod(dd, mask, mask, mask); 2348c2ecf20Sopenharmony_ci /* 2358c2ecf20Sopenharmony_ci * Module could take up to 2 Msec to respond to MOD_SEL 2368c2ecf20Sopenharmony_ci * going away, and there is no way to tell if it is ready. 2378c2ecf20Sopenharmony_ci * so we must wait. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci msleep(20); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cibail: 2428c2ecf20Sopenharmony_ci mutex_unlock(&dd->eep_lock); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cino_unlock: 2458c2ecf20Sopenharmony_ci return ret; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* 2498c2ecf20Sopenharmony_ci * For validation, we want to check the checksums, even of the 2508c2ecf20Sopenharmony_ci * fields we do not otherwise use. This function reads the bytes from 2518c2ecf20Sopenharmony_ci * <first> to <next-1> and returns the 8lsbs of the sum, or <0 for errors 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_cistatic int qsfp_cks(struct qib_pportdata *ppd, int first, int next) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci int ret; 2568c2ecf20Sopenharmony_ci u16 cks; 2578c2ecf20Sopenharmony_ci u8 bval; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci cks = 0; 2608c2ecf20Sopenharmony_ci while (first < next) { 2618c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, first, &bval, 1); 2628c2ecf20Sopenharmony_ci if (ret < 0) 2638c2ecf20Sopenharmony_ci goto bail; 2648c2ecf20Sopenharmony_ci cks += bval; 2658c2ecf20Sopenharmony_ci ++first; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci ret = cks & 0xFF; 2688c2ecf20Sopenharmony_cibail: 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ciint qib_refresh_qsfp_cache(struct qib_pportdata *ppd, struct qib_qsfp_cache *cp) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci int ret; 2768c2ecf20Sopenharmony_ci int idx; 2778c2ecf20Sopenharmony_ci u16 cks; 2788c2ecf20Sopenharmony_ci u8 peek[4]; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* ensure sane contents on invalid reads, for cable swaps */ 2818c2ecf20Sopenharmony_ci memset(cp, 0, sizeof(*cp)); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (!qib_qsfp_mod_present(ppd)) { 2848c2ecf20Sopenharmony_ci ret = -ENODEV; 2858c2ecf20Sopenharmony_ci goto bail; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, 0, peek, 3); 2898c2ecf20Sopenharmony_ci if (ret < 0) 2908c2ecf20Sopenharmony_ci goto bail; 2918c2ecf20Sopenharmony_ci if ((peek[0] & 0xFE) != 0x0C) 2928c2ecf20Sopenharmony_ci qib_dev_porterr(ppd->dd, ppd->port, 2938c2ecf20Sopenharmony_ci "QSFP byte0 is 0x%02X, S/B 0x0C/D\n", peek[0]); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if ((peek[2] & 4) == 0) { 2968c2ecf20Sopenharmony_ci /* 2978c2ecf20Sopenharmony_ci * If cable is paged, rather than "flat memory", we need to 2988c2ecf20Sopenharmony_ci * set the page to zero, Even if it already appears to be zero. 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_ci u8 poke = 0; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci ret = qib_qsfp_write(ppd, 127, &poke, 1); 3038c2ecf20Sopenharmony_ci udelay(50); 3048c2ecf20Sopenharmony_ci if (ret != 1) { 3058c2ecf20Sopenharmony_ci qib_dev_porterr(ppd->dd, ppd->port, 3068c2ecf20Sopenharmony_ci "Failed QSFP Page set\n"); 3078c2ecf20Sopenharmony_ci goto bail; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, QSFP_MOD_ID_OFFS, &cp->id, 1); 3128c2ecf20Sopenharmony_ci if (ret < 0) 3138c2ecf20Sopenharmony_ci goto bail; 3148c2ecf20Sopenharmony_ci if ((cp->id & 0xFE) != 0x0C) 3158c2ecf20Sopenharmony_ci qib_dev_porterr(ppd->dd, ppd->port, 3168c2ecf20Sopenharmony_ci "QSFP ID byte is 0x%02X, S/B 0x0C/D\n", cp->id); 3178c2ecf20Sopenharmony_ci cks = cp->id; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, QSFP_MOD_PWR_OFFS, &cp->pwr, 1); 3208c2ecf20Sopenharmony_ci if (ret < 0) 3218c2ecf20Sopenharmony_ci goto bail; 3228c2ecf20Sopenharmony_ci cks += cp->pwr; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci ret = qsfp_cks(ppd, QSFP_MOD_PWR_OFFS + 1, QSFP_MOD_LEN_OFFS); 3258c2ecf20Sopenharmony_ci if (ret < 0) 3268c2ecf20Sopenharmony_ci goto bail; 3278c2ecf20Sopenharmony_ci cks += ret; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, QSFP_MOD_LEN_OFFS, &cp->len, 1); 3308c2ecf20Sopenharmony_ci if (ret < 0) 3318c2ecf20Sopenharmony_ci goto bail; 3328c2ecf20Sopenharmony_ci cks += cp->len; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, QSFP_MOD_TECH_OFFS, &cp->tech, 1); 3358c2ecf20Sopenharmony_ci if (ret < 0) 3368c2ecf20Sopenharmony_ci goto bail; 3378c2ecf20Sopenharmony_ci cks += cp->tech; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, QSFP_VEND_OFFS, &cp->vendor, QSFP_VEND_LEN); 3408c2ecf20Sopenharmony_ci if (ret < 0) 3418c2ecf20Sopenharmony_ci goto bail; 3428c2ecf20Sopenharmony_ci for (idx = 0; idx < QSFP_VEND_LEN; ++idx) 3438c2ecf20Sopenharmony_ci cks += cp->vendor[idx]; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, QSFP_IBXCV_OFFS, &cp->xt_xcv, 1); 3468c2ecf20Sopenharmony_ci if (ret < 0) 3478c2ecf20Sopenharmony_ci goto bail; 3488c2ecf20Sopenharmony_ci cks += cp->xt_xcv; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, QSFP_VOUI_OFFS, &cp->oui, QSFP_VOUI_LEN); 3518c2ecf20Sopenharmony_ci if (ret < 0) 3528c2ecf20Sopenharmony_ci goto bail; 3538c2ecf20Sopenharmony_ci for (idx = 0; idx < QSFP_VOUI_LEN; ++idx) 3548c2ecf20Sopenharmony_ci cks += cp->oui[idx]; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, QSFP_PN_OFFS, &cp->partnum, QSFP_PN_LEN); 3578c2ecf20Sopenharmony_ci if (ret < 0) 3588c2ecf20Sopenharmony_ci goto bail; 3598c2ecf20Sopenharmony_ci for (idx = 0; idx < QSFP_PN_LEN; ++idx) 3608c2ecf20Sopenharmony_ci cks += cp->partnum[idx]; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, QSFP_REV_OFFS, &cp->rev, QSFP_REV_LEN); 3638c2ecf20Sopenharmony_ci if (ret < 0) 3648c2ecf20Sopenharmony_ci goto bail; 3658c2ecf20Sopenharmony_ci for (idx = 0; idx < QSFP_REV_LEN; ++idx) 3668c2ecf20Sopenharmony_ci cks += cp->rev[idx]; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, QSFP_ATTEN_OFFS, &cp->atten, QSFP_ATTEN_LEN); 3698c2ecf20Sopenharmony_ci if (ret < 0) 3708c2ecf20Sopenharmony_ci goto bail; 3718c2ecf20Sopenharmony_ci for (idx = 0; idx < QSFP_ATTEN_LEN; ++idx) 3728c2ecf20Sopenharmony_ci cks += cp->atten[idx]; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci ret = qsfp_cks(ppd, QSFP_ATTEN_OFFS + QSFP_ATTEN_LEN, QSFP_CC_OFFS); 3758c2ecf20Sopenharmony_ci if (ret < 0) 3768c2ecf20Sopenharmony_ci goto bail; 3778c2ecf20Sopenharmony_ci cks += ret; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci cks &= 0xFF; 3808c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, QSFP_CC_OFFS, &cp->cks1, 1); 3818c2ecf20Sopenharmony_ci if (ret < 0) 3828c2ecf20Sopenharmony_ci goto bail; 3838c2ecf20Sopenharmony_ci if (cks != cp->cks1) 3848c2ecf20Sopenharmony_ci qib_dev_porterr(ppd->dd, ppd->port, 3858c2ecf20Sopenharmony_ci "QSFP cks1 is %02X, computed %02X\n", cp->cks1, 3868c2ecf20Sopenharmony_ci cks); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* Second checksum covers 192 to (serial, date, lot) */ 3898c2ecf20Sopenharmony_ci ret = qsfp_cks(ppd, QSFP_CC_OFFS + 1, QSFP_SN_OFFS); 3908c2ecf20Sopenharmony_ci if (ret < 0) 3918c2ecf20Sopenharmony_ci goto bail; 3928c2ecf20Sopenharmony_ci cks = ret; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, QSFP_SN_OFFS, &cp->serial, QSFP_SN_LEN); 3958c2ecf20Sopenharmony_ci if (ret < 0) 3968c2ecf20Sopenharmony_ci goto bail; 3978c2ecf20Sopenharmony_ci for (idx = 0; idx < QSFP_SN_LEN; ++idx) 3988c2ecf20Sopenharmony_ci cks += cp->serial[idx]; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, QSFP_DATE_OFFS, &cp->date, QSFP_DATE_LEN); 4018c2ecf20Sopenharmony_ci if (ret < 0) 4028c2ecf20Sopenharmony_ci goto bail; 4038c2ecf20Sopenharmony_ci for (idx = 0; idx < QSFP_DATE_LEN; ++idx) 4048c2ecf20Sopenharmony_ci cks += cp->date[idx]; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, QSFP_LOT_OFFS, &cp->lot, QSFP_LOT_LEN); 4078c2ecf20Sopenharmony_ci if (ret < 0) 4088c2ecf20Sopenharmony_ci goto bail; 4098c2ecf20Sopenharmony_ci for (idx = 0; idx < QSFP_LOT_LEN; ++idx) 4108c2ecf20Sopenharmony_ci cks += cp->lot[idx]; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci ret = qsfp_cks(ppd, QSFP_LOT_OFFS + QSFP_LOT_LEN, QSFP_CC_EXT_OFFS); 4138c2ecf20Sopenharmony_ci if (ret < 0) 4148c2ecf20Sopenharmony_ci goto bail; 4158c2ecf20Sopenharmony_ci cks += ret; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, QSFP_CC_EXT_OFFS, &cp->cks2, 1); 4188c2ecf20Sopenharmony_ci if (ret < 0) 4198c2ecf20Sopenharmony_ci goto bail; 4208c2ecf20Sopenharmony_ci cks &= 0xFF; 4218c2ecf20Sopenharmony_ci if (cks != cp->cks2) 4228c2ecf20Sopenharmony_ci qib_dev_porterr(ppd->dd, ppd->port, 4238c2ecf20Sopenharmony_ci "QSFP cks2 is %02X, computed %02X\n", cp->cks2, 4248c2ecf20Sopenharmony_ci cks); 4258c2ecf20Sopenharmony_ci return 0; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cibail: 4288c2ecf20Sopenharmony_ci cp->id = 0; 4298c2ecf20Sopenharmony_ci return ret; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ciconst char * const qib_qsfp_devtech[16] = { 4338c2ecf20Sopenharmony_ci "850nm VCSEL", "1310nm VCSEL", "1550nm VCSEL", "1310nm FP", 4348c2ecf20Sopenharmony_ci "1310nm DFB", "1550nm DFB", "1310nm EML", "1550nm EML", 4358c2ecf20Sopenharmony_ci "Cu Misc", "1490nm DFB", "Cu NoEq", "Cu Eq", 4368c2ecf20Sopenharmony_ci "Undef", "Cu Active BothEq", "Cu FarEq", "Cu NearEq" 4378c2ecf20Sopenharmony_ci}; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci#define QSFP_DUMP_CHUNK 16 /* Holds longest string */ 4408c2ecf20Sopenharmony_ci#define QSFP_DEFAULT_HDR_CNT 224 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic const char *pwr_codes = "1.5W2.0W2.5W3.5W"; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ciint qib_qsfp_mod_present(struct qib_pportdata *ppd) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci u32 mask; 4478c2ecf20Sopenharmony_ci int ret; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci mask = QSFP_GPIO_MOD_PRS_N << 4508c2ecf20Sopenharmony_ci (ppd->hw_pidx * QSFP_GPIO_PORT2_SHIFT); 4518c2ecf20Sopenharmony_ci ret = ppd->dd->f_gpio_mod(ppd->dd, 0, 0, 0); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return !((ret & mask) >> 4548c2ecf20Sopenharmony_ci ((ppd->hw_pidx * QSFP_GPIO_PORT2_SHIFT) + 3)); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci/* 4588c2ecf20Sopenharmony_ci * Initialize structures that control access to QSFP. Called once per port 4598c2ecf20Sopenharmony_ci * on cards that support QSFP. 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_civoid qib_qsfp_init(struct qib_qsfp_data *qd, 4628c2ecf20Sopenharmony_ci void (*fevent)(struct work_struct *)) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci u32 mask, highs; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci struct qib_devdata *dd = qd->ppd->dd; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* Initialize work struct for later QSFP events */ 4698c2ecf20Sopenharmony_ci INIT_WORK(&qd->work, fevent); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* 4728c2ecf20Sopenharmony_ci * Later, we may want more validation. For now, just set up pins and 4738c2ecf20Sopenharmony_ci * blip reset. If module is present, call qib_refresh_qsfp_cache(), 4748c2ecf20Sopenharmony_ci * to do further init. 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_ci mask = QSFP_GPIO_MOD_SEL_N | QSFP_GPIO_MOD_RST_N | QSFP_GPIO_LP_MODE; 4778c2ecf20Sopenharmony_ci highs = mask - QSFP_GPIO_MOD_RST_N; 4788c2ecf20Sopenharmony_ci if (qd->ppd->hw_pidx) { 4798c2ecf20Sopenharmony_ci mask <<= QSFP_GPIO_PORT2_SHIFT; 4808c2ecf20Sopenharmony_ci highs <<= QSFP_GPIO_PORT2_SHIFT; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci dd->f_gpio_mod(dd, highs, mask, mask); 4838c2ecf20Sopenharmony_ci udelay(20); /* Generous RST dwell */ 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci dd->f_gpio_mod(dd, mask, mask, mask); 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ciint qib_qsfp_dump(struct qib_pportdata *ppd, char *buf, int len) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct qib_qsfp_cache cd; 4918c2ecf20Sopenharmony_ci u8 bin_buff[QSFP_DUMP_CHUNK]; 4928c2ecf20Sopenharmony_ci char lenstr[6]; 4938c2ecf20Sopenharmony_ci int sofar, ret; 4948c2ecf20Sopenharmony_ci int bidx = 0; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci sofar = 0; 4978c2ecf20Sopenharmony_ci ret = qib_refresh_qsfp_cache(ppd, &cd); 4988c2ecf20Sopenharmony_ci if (ret < 0) 4998c2ecf20Sopenharmony_ci goto bail; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci lenstr[0] = ' '; 5028c2ecf20Sopenharmony_ci lenstr[1] = '\0'; 5038c2ecf20Sopenharmony_ci if (QSFP_IS_CU(cd.tech)) 5048c2ecf20Sopenharmony_ci sprintf(lenstr, "%dM ", cd.len); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "PWR:%.3sW\n", pwr_codes + 5078c2ecf20Sopenharmony_ci (QSFP_PWR(cd.pwr) * 4)); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "TECH:%s%s\n", lenstr, 5108c2ecf20Sopenharmony_ci qib_qsfp_devtech[cd.tech >> 4]); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Vendor:%.*s\n", 5138c2ecf20Sopenharmony_ci QSFP_VEND_LEN, cd.vendor); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "OUI:%06X\n", 5168c2ecf20Sopenharmony_ci QSFP_OUI(cd.oui)); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Part#:%.*s\n", 5198c2ecf20Sopenharmony_ci QSFP_PN_LEN, cd.partnum); 5208c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Rev:%.*s\n", 5218c2ecf20Sopenharmony_ci QSFP_REV_LEN, cd.rev); 5228c2ecf20Sopenharmony_ci if (QSFP_IS_CU(cd.tech)) 5238c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Atten:%d, %d\n", 5248c2ecf20Sopenharmony_ci QSFP_ATTEN_SDR(cd.atten), 5258c2ecf20Sopenharmony_ci QSFP_ATTEN_DDR(cd.atten)); 5268c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Serial:%.*s\n", 5278c2ecf20Sopenharmony_ci QSFP_SN_LEN, cd.serial); 5288c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Date:%.*s\n", 5298c2ecf20Sopenharmony_ci QSFP_DATE_LEN, cd.date); 5308c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Lot:%.*s\n", 5318c2ecf20Sopenharmony_ci QSFP_LOT_LEN, cd.lot); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci while (bidx < QSFP_DEFAULT_HDR_CNT) { 5348c2ecf20Sopenharmony_ci int iidx; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, bidx, bin_buff, QSFP_DUMP_CHUNK); 5378c2ecf20Sopenharmony_ci if (ret < 0) 5388c2ecf20Sopenharmony_ci goto bail; 5398c2ecf20Sopenharmony_ci for (iidx = 0; iidx < ret; ++iidx) { 5408c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len-sofar, " %02X", 5418c2ecf20Sopenharmony_ci bin_buff[iidx]); 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "\n"); 5448c2ecf20Sopenharmony_ci bidx += QSFP_DUMP_CHUNK; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci ret = sofar; 5478c2ecf20Sopenharmony_cibail: 5488c2ecf20Sopenharmony_ci return ret; 5498c2ecf20Sopenharmony_ci} 550