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