162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2006, 2007, 2008, 2009 QLogic Corporation. All rights reserved.
362306a36Sopenharmony_ci * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This software is available to you under a choice of one of two
662306a36Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
762306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
862306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the
962306a36Sopenharmony_ci * OpenIB.org BSD license below:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
1262306a36Sopenharmony_ci *     without modification, are permitted provided that the following
1362306a36Sopenharmony_ci *     conditions are met:
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci *      - Redistributions of source code must retain the above
1662306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
1762306a36Sopenharmony_ci *        disclaimer.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
2062306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2162306a36Sopenharmony_ci *        disclaimer in the documentation and/or other materials
2262306a36Sopenharmony_ci *        provided with the distribution.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2562306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2662306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2762306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2862306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2962306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
3062306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3162306a36Sopenharmony_ci * SOFTWARE.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include <linux/delay.h>
3562306a36Sopenharmony_ci#include <linux/pci.h>
3662306a36Sopenharmony_ci#include <linux/vmalloc.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include "qib.h"
3962306a36Sopenharmony_ci#include "qib_qsfp.h"
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/*
4262306a36Sopenharmony_ci * QSFP support for ib_qib driver, using "Two Wire Serial Interface" driver
4362306a36Sopenharmony_ci * in qib_twsi.c
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_ci#define QSFP_MAX_RETRY 4
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int qsfp_read(struct qib_pportdata *ppd, int addr, void *bp, int len)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct qib_devdata *dd = ppd->dd;
5062306a36Sopenharmony_ci	u32 out, mask;
5162306a36Sopenharmony_ci	int ret, cnt, pass = 0;
5262306a36Sopenharmony_ci	int stuck = 0;
5362306a36Sopenharmony_ci	u8 *buff = bp;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&dd->eep_lock);
5662306a36Sopenharmony_ci	if (ret)
5762306a36Sopenharmony_ci		goto no_unlock;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (dd->twsi_eeprom_dev == QIB_TWSI_NO_DEV) {
6062306a36Sopenharmony_ci		ret = -ENXIO;
6162306a36Sopenharmony_ci		goto bail;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/*
6562306a36Sopenharmony_ci	 * We presume, if we are called at all, that this board has
6662306a36Sopenharmony_ci	 * QSFP. This is on the same i2c chain as the legacy parts,
6762306a36Sopenharmony_ci	 * but only responds if the module is selected via GPIO pins.
6862306a36Sopenharmony_ci	 * Further, there are very long setup and hold requirements
6962306a36Sopenharmony_ci	 * on MODSEL.
7062306a36Sopenharmony_ci	 */
7162306a36Sopenharmony_ci	mask = QSFP_GPIO_MOD_SEL_N | QSFP_GPIO_MOD_RST_N | QSFP_GPIO_LP_MODE;
7262306a36Sopenharmony_ci	out = QSFP_GPIO_MOD_RST_N | QSFP_GPIO_LP_MODE;
7362306a36Sopenharmony_ci	if (ppd->hw_pidx) {
7462306a36Sopenharmony_ci		mask <<= QSFP_GPIO_PORT2_SHIFT;
7562306a36Sopenharmony_ci		out <<= QSFP_GPIO_PORT2_SHIFT;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	dd->f_gpio_mod(dd, out, mask, mask);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/*
8162306a36Sopenharmony_ci	 * Module could take up to 2 Msec to respond to MOD_SEL, and there
8262306a36Sopenharmony_ci	 * is no way to tell if it is ready, so we must wait.
8362306a36Sopenharmony_ci	 */
8462306a36Sopenharmony_ci	msleep(20);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* Make sure TWSI bus is in sane state. */
8762306a36Sopenharmony_ci	ret = qib_twsi_reset(dd);
8862306a36Sopenharmony_ci	if (ret) {
8962306a36Sopenharmony_ci		qib_dev_porterr(dd, ppd->port,
9062306a36Sopenharmony_ci				"QSFP interface Reset for read failed\n");
9162306a36Sopenharmony_ci		ret = -EIO;
9262306a36Sopenharmony_ci		stuck = 1;
9362306a36Sopenharmony_ci		goto deselect;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/* All QSFP modules are at A0 */
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	cnt = 0;
9962306a36Sopenharmony_ci	while (cnt < len) {
10062306a36Sopenharmony_ci		unsigned in_page;
10162306a36Sopenharmony_ci		int wlen = len - cnt;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		in_page = addr % QSFP_PAGESIZE;
10462306a36Sopenharmony_ci		if ((in_page + wlen) > QSFP_PAGESIZE)
10562306a36Sopenharmony_ci			wlen = QSFP_PAGESIZE - in_page;
10662306a36Sopenharmony_ci		ret = qib_twsi_blk_rd(dd, QSFP_DEV, addr, buff + cnt, wlen);
10762306a36Sopenharmony_ci		/* Some QSFP's fail first try. Retry as experiment */
10862306a36Sopenharmony_ci		if (ret && cnt == 0 && ++pass < QSFP_MAX_RETRY)
10962306a36Sopenharmony_ci			continue;
11062306a36Sopenharmony_ci		if (ret) {
11162306a36Sopenharmony_ci			/* qib_twsi_blk_rd() 1 for error, else 0 */
11262306a36Sopenharmony_ci			ret = -EIO;
11362306a36Sopenharmony_ci			goto deselect;
11462306a36Sopenharmony_ci		}
11562306a36Sopenharmony_ci		addr += wlen;
11662306a36Sopenharmony_ci		cnt += wlen;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci	ret = cnt;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cideselect:
12162306a36Sopenharmony_ci	/*
12262306a36Sopenharmony_ci	 * Module could take up to 10 uSec after transfer before
12362306a36Sopenharmony_ci	 * ready to respond to MOD_SEL negation, and there is no way
12462306a36Sopenharmony_ci	 * to tell if it is ready, so we must wait.
12562306a36Sopenharmony_ci	 */
12662306a36Sopenharmony_ci	udelay(10);
12762306a36Sopenharmony_ci	/* set QSFP MODSEL, RST. LP all high */
12862306a36Sopenharmony_ci	dd->f_gpio_mod(dd, mask, mask, mask);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/*
13162306a36Sopenharmony_ci	 * Module could take up to 2 Msec to respond to MOD_SEL
13262306a36Sopenharmony_ci	 * going away, and there is no way to tell if it is ready.
13362306a36Sopenharmony_ci	 * so we must wait.
13462306a36Sopenharmony_ci	 */
13562306a36Sopenharmony_ci	if (stuck)
13662306a36Sopenharmony_ci		qib_dev_err(dd, "QSFP interface bus stuck non-idle\n");
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (pass >= QSFP_MAX_RETRY && ret)
13962306a36Sopenharmony_ci		qib_dev_porterr(dd, ppd->port, "QSFP failed even retrying\n");
14062306a36Sopenharmony_ci	else if (pass)
14162306a36Sopenharmony_ci		qib_dev_porterr(dd, ppd->port, "QSFP retries: %d\n", pass);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	msleep(20);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cibail:
14662306a36Sopenharmony_ci	mutex_unlock(&dd->eep_lock);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cino_unlock:
14962306a36Sopenharmony_ci	return ret;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci/*
15362306a36Sopenharmony_ci * qsfp_write
15462306a36Sopenharmony_ci * We do not ordinarily write the QSFP, but this is needed to select
15562306a36Sopenharmony_ci * the page on non-flat QSFPs, and possibly later unusual cases
15662306a36Sopenharmony_ci */
15762306a36Sopenharmony_cistatic int qib_qsfp_write(struct qib_pportdata *ppd, int addr, void *bp,
15862306a36Sopenharmony_ci			  int len)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct qib_devdata *dd = ppd->dd;
16162306a36Sopenharmony_ci	u32 out, mask;
16262306a36Sopenharmony_ci	int ret, cnt;
16362306a36Sopenharmony_ci	u8 *buff = bp;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&dd->eep_lock);
16662306a36Sopenharmony_ci	if (ret)
16762306a36Sopenharmony_ci		goto no_unlock;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (dd->twsi_eeprom_dev == QIB_TWSI_NO_DEV) {
17062306a36Sopenharmony_ci		ret = -ENXIO;
17162306a36Sopenharmony_ci		goto bail;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/*
17562306a36Sopenharmony_ci	 * We presume, if we are called at all, that this board has
17662306a36Sopenharmony_ci	 * QSFP. This is on the same i2c chain as the legacy parts,
17762306a36Sopenharmony_ci	 * but only responds if the module is selected via GPIO pins.
17862306a36Sopenharmony_ci	 * Further, there are very long setup and hold requirements
17962306a36Sopenharmony_ci	 * on MODSEL.
18062306a36Sopenharmony_ci	 */
18162306a36Sopenharmony_ci	mask = QSFP_GPIO_MOD_SEL_N | QSFP_GPIO_MOD_RST_N | QSFP_GPIO_LP_MODE;
18262306a36Sopenharmony_ci	out = QSFP_GPIO_MOD_RST_N | QSFP_GPIO_LP_MODE;
18362306a36Sopenharmony_ci	if (ppd->hw_pidx) {
18462306a36Sopenharmony_ci		mask <<= QSFP_GPIO_PORT2_SHIFT;
18562306a36Sopenharmony_ci		out <<= QSFP_GPIO_PORT2_SHIFT;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci	dd->f_gpio_mod(dd, out, mask, mask);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/*
19062306a36Sopenharmony_ci	 * Module could take up to 2 Msec to respond to MOD_SEL,
19162306a36Sopenharmony_ci	 * and there is no way to tell if it is ready, so we must wait.
19262306a36Sopenharmony_ci	 */
19362306a36Sopenharmony_ci	msleep(20);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* Make sure TWSI bus is in sane state. */
19662306a36Sopenharmony_ci	ret = qib_twsi_reset(dd);
19762306a36Sopenharmony_ci	if (ret) {
19862306a36Sopenharmony_ci		qib_dev_porterr(dd, ppd->port,
19962306a36Sopenharmony_ci				"QSFP interface Reset for write failed\n");
20062306a36Sopenharmony_ci		ret = -EIO;
20162306a36Sopenharmony_ci		goto deselect;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/* All QSFP modules are at A0 */
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	cnt = 0;
20762306a36Sopenharmony_ci	while (cnt < len) {
20862306a36Sopenharmony_ci		unsigned in_page;
20962306a36Sopenharmony_ci		int wlen = len - cnt;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci		in_page = addr % QSFP_PAGESIZE;
21262306a36Sopenharmony_ci		if ((in_page + wlen) > QSFP_PAGESIZE)
21362306a36Sopenharmony_ci			wlen = QSFP_PAGESIZE - in_page;
21462306a36Sopenharmony_ci		ret = qib_twsi_blk_wr(dd, QSFP_DEV, addr, buff + cnt, wlen);
21562306a36Sopenharmony_ci		if (ret) {
21662306a36Sopenharmony_ci			/* qib_twsi_blk_wr() 1 for error, else 0 */
21762306a36Sopenharmony_ci			ret = -EIO;
21862306a36Sopenharmony_ci			goto deselect;
21962306a36Sopenharmony_ci		}
22062306a36Sopenharmony_ci		addr += wlen;
22162306a36Sopenharmony_ci		cnt += wlen;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci	ret = cnt;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cideselect:
22662306a36Sopenharmony_ci	/*
22762306a36Sopenharmony_ci	 * Module could take up to 10 uSec after transfer before
22862306a36Sopenharmony_ci	 * ready to respond to MOD_SEL negation, and there is no way
22962306a36Sopenharmony_ci	 * to tell if it is ready, so we must wait.
23062306a36Sopenharmony_ci	 */
23162306a36Sopenharmony_ci	udelay(10);
23262306a36Sopenharmony_ci	/* set QSFP MODSEL, RST, LP high */
23362306a36Sopenharmony_ci	dd->f_gpio_mod(dd, mask, mask, mask);
23462306a36Sopenharmony_ci	/*
23562306a36Sopenharmony_ci	 * Module could take up to 2 Msec to respond to MOD_SEL
23662306a36Sopenharmony_ci	 * going away, and there is no way to tell if it is ready.
23762306a36Sopenharmony_ci	 * so we must wait.
23862306a36Sopenharmony_ci	 */
23962306a36Sopenharmony_ci	msleep(20);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cibail:
24262306a36Sopenharmony_ci	mutex_unlock(&dd->eep_lock);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cino_unlock:
24562306a36Sopenharmony_ci	return ret;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci/*
24962306a36Sopenharmony_ci * For validation, we want to check the checksums, even of the
25062306a36Sopenharmony_ci * fields we do not otherwise use. This function reads the bytes from
25162306a36Sopenharmony_ci * <first> to <next-1> and returns the 8lsbs of the sum, or <0 for errors
25262306a36Sopenharmony_ci */
25362306a36Sopenharmony_cistatic int qsfp_cks(struct qib_pportdata *ppd, int first, int next)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	int ret;
25662306a36Sopenharmony_ci	u16 cks;
25762306a36Sopenharmony_ci	u8 bval;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	cks = 0;
26062306a36Sopenharmony_ci	while (first < next) {
26162306a36Sopenharmony_ci		ret = qsfp_read(ppd, first, &bval, 1);
26262306a36Sopenharmony_ci		if (ret < 0)
26362306a36Sopenharmony_ci			goto bail;
26462306a36Sopenharmony_ci		cks += bval;
26562306a36Sopenharmony_ci		++first;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci	ret = cks & 0xFF;
26862306a36Sopenharmony_cibail:
26962306a36Sopenharmony_ci	return ret;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ciint qib_refresh_qsfp_cache(struct qib_pportdata *ppd, struct qib_qsfp_cache *cp)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	int ret;
27662306a36Sopenharmony_ci	int idx;
27762306a36Sopenharmony_ci	u16 cks;
27862306a36Sopenharmony_ci	u8 peek[4];
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* ensure sane contents on invalid reads, for cable swaps */
28162306a36Sopenharmony_ci	memset(cp, 0, sizeof(*cp));
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (!qib_qsfp_mod_present(ppd)) {
28462306a36Sopenharmony_ci		ret = -ENODEV;
28562306a36Sopenharmony_ci		goto bail;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	ret = qsfp_read(ppd, 0, peek, 3);
28962306a36Sopenharmony_ci	if (ret < 0)
29062306a36Sopenharmony_ci		goto bail;
29162306a36Sopenharmony_ci	if ((peek[0] & 0xFE) != 0x0C)
29262306a36Sopenharmony_ci		qib_dev_porterr(ppd->dd, ppd->port,
29362306a36Sopenharmony_ci				"QSFP byte0 is 0x%02X, S/B 0x0C/D\n", peek[0]);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if ((peek[2] & 4) == 0) {
29662306a36Sopenharmony_ci		/*
29762306a36Sopenharmony_ci		 * If cable is paged, rather than "flat memory", we need to
29862306a36Sopenharmony_ci		 * set the page to zero, Even if it already appears to be zero.
29962306a36Sopenharmony_ci		 */
30062306a36Sopenharmony_ci		u8 poke = 0;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		ret = qib_qsfp_write(ppd, 127, &poke, 1);
30362306a36Sopenharmony_ci		udelay(50);
30462306a36Sopenharmony_ci		if (ret != 1) {
30562306a36Sopenharmony_ci			qib_dev_porterr(ppd->dd, ppd->port,
30662306a36Sopenharmony_ci					"Failed QSFP Page set\n");
30762306a36Sopenharmony_ci			goto bail;
30862306a36Sopenharmony_ci		}
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	ret = qsfp_read(ppd, QSFP_MOD_ID_OFFS, &cp->id, 1);
31262306a36Sopenharmony_ci	if (ret < 0)
31362306a36Sopenharmony_ci		goto bail;
31462306a36Sopenharmony_ci	if ((cp->id & 0xFE) != 0x0C)
31562306a36Sopenharmony_ci		qib_dev_porterr(ppd->dd, ppd->port,
31662306a36Sopenharmony_ci				"QSFP ID byte is 0x%02X, S/B 0x0C/D\n", cp->id);
31762306a36Sopenharmony_ci	cks = cp->id;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	ret = qsfp_read(ppd, QSFP_MOD_PWR_OFFS, &cp->pwr, 1);
32062306a36Sopenharmony_ci	if (ret < 0)
32162306a36Sopenharmony_ci		goto bail;
32262306a36Sopenharmony_ci	cks += cp->pwr;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	ret = qsfp_cks(ppd, QSFP_MOD_PWR_OFFS + 1, QSFP_MOD_LEN_OFFS);
32562306a36Sopenharmony_ci	if (ret < 0)
32662306a36Sopenharmony_ci		goto bail;
32762306a36Sopenharmony_ci	cks += ret;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	ret = qsfp_read(ppd, QSFP_MOD_LEN_OFFS, &cp->len, 1);
33062306a36Sopenharmony_ci	if (ret < 0)
33162306a36Sopenharmony_ci		goto bail;
33262306a36Sopenharmony_ci	cks += cp->len;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	ret = qsfp_read(ppd, QSFP_MOD_TECH_OFFS, &cp->tech, 1);
33562306a36Sopenharmony_ci	if (ret < 0)
33662306a36Sopenharmony_ci		goto bail;
33762306a36Sopenharmony_ci	cks += cp->tech;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	ret = qsfp_read(ppd, QSFP_VEND_OFFS, &cp->vendor, QSFP_VEND_LEN);
34062306a36Sopenharmony_ci	if (ret < 0)
34162306a36Sopenharmony_ci		goto bail;
34262306a36Sopenharmony_ci	for (idx = 0; idx < QSFP_VEND_LEN; ++idx)
34362306a36Sopenharmony_ci		cks += cp->vendor[idx];
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	ret = qsfp_read(ppd, QSFP_IBXCV_OFFS, &cp->xt_xcv, 1);
34662306a36Sopenharmony_ci	if (ret < 0)
34762306a36Sopenharmony_ci		goto bail;
34862306a36Sopenharmony_ci	cks += cp->xt_xcv;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	ret = qsfp_read(ppd, QSFP_VOUI_OFFS, &cp->oui, QSFP_VOUI_LEN);
35162306a36Sopenharmony_ci	if (ret < 0)
35262306a36Sopenharmony_ci		goto bail;
35362306a36Sopenharmony_ci	for (idx = 0; idx < QSFP_VOUI_LEN; ++idx)
35462306a36Sopenharmony_ci		cks += cp->oui[idx];
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	ret = qsfp_read(ppd, QSFP_PN_OFFS, &cp->partnum, QSFP_PN_LEN);
35762306a36Sopenharmony_ci	if (ret < 0)
35862306a36Sopenharmony_ci		goto bail;
35962306a36Sopenharmony_ci	for (idx = 0; idx < QSFP_PN_LEN; ++idx)
36062306a36Sopenharmony_ci		cks += cp->partnum[idx];
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	ret = qsfp_read(ppd, QSFP_REV_OFFS, &cp->rev, QSFP_REV_LEN);
36362306a36Sopenharmony_ci	if (ret < 0)
36462306a36Sopenharmony_ci		goto bail;
36562306a36Sopenharmony_ci	for (idx = 0; idx < QSFP_REV_LEN; ++idx)
36662306a36Sopenharmony_ci		cks += cp->rev[idx];
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	ret = qsfp_read(ppd, QSFP_ATTEN_OFFS, &cp->atten, QSFP_ATTEN_LEN);
36962306a36Sopenharmony_ci	if (ret < 0)
37062306a36Sopenharmony_ci		goto bail;
37162306a36Sopenharmony_ci	for (idx = 0; idx < QSFP_ATTEN_LEN; ++idx)
37262306a36Sopenharmony_ci		cks += cp->atten[idx];
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	ret = qsfp_cks(ppd, QSFP_ATTEN_OFFS + QSFP_ATTEN_LEN, QSFP_CC_OFFS);
37562306a36Sopenharmony_ci	if (ret < 0)
37662306a36Sopenharmony_ci		goto bail;
37762306a36Sopenharmony_ci	cks += ret;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	cks &= 0xFF;
38062306a36Sopenharmony_ci	ret = qsfp_read(ppd, QSFP_CC_OFFS, &cp->cks1, 1);
38162306a36Sopenharmony_ci	if (ret < 0)
38262306a36Sopenharmony_ci		goto bail;
38362306a36Sopenharmony_ci	if (cks != cp->cks1)
38462306a36Sopenharmony_ci		qib_dev_porterr(ppd->dd, ppd->port,
38562306a36Sopenharmony_ci				"QSFP cks1 is %02X, computed %02X\n", cp->cks1,
38662306a36Sopenharmony_ci				cks);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* Second checksum covers 192 to (serial, date, lot) */
38962306a36Sopenharmony_ci	ret = qsfp_cks(ppd, QSFP_CC_OFFS + 1, QSFP_SN_OFFS);
39062306a36Sopenharmony_ci	if (ret < 0)
39162306a36Sopenharmony_ci		goto bail;
39262306a36Sopenharmony_ci	cks = ret;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	ret = qsfp_read(ppd, QSFP_SN_OFFS, &cp->serial, QSFP_SN_LEN);
39562306a36Sopenharmony_ci	if (ret < 0)
39662306a36Sopenharmony_ci		goto bail;
39762306a36Sopenharmony_ci	for (idx = 0; idx < QSFP_SN_LEN; ++idx)
39862306a36Sopenharmony_ci		cks += cp->serial[idx];
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	ret = qsfp_read(ppd, QSFP_DATE_OFFS, &cp->date, QSFP_DATE_LEN);
40162306a36Sopenharmony_ci	if (ret < 0)
40262306a36Sopenharmony_ci		goto bail;
40362306a36Sopenharmony_ci	for (idx = 0; idx < QSFP_DATE_LEN; ++idx)
40462306a36Sopenharmony_ci		cks += cp->date[idx];
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	ret = qsfp_read(ppd, QSFP_LOT_OFFS, &cp->lot, QSFP_LOT_LEN);
40762306a36Sopenharmony_ci	if (ret < 0)
40862306a36Sopenharmony_ci		goto bail;
40962306a36Sopenharmony_ci	for (idx = 0; idx < QSFP_LOT_LEN; ++idx)
41062306a36Sopenharmony_ci		cks += cp->lot[idx];
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	ret = qsfp_cks(ppd, QSFP_LOT_OFFS + QSFP_LOT_LEN, QSFP_CC_EXT_OFFS);
41362306a36Sopenharmony_ci	if (ret < 0)
41462306a36Sopenharmony_ci		goto bail;
41562306a36Sopenharmony_ci	cks += ret;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	ret = qsfp_read(ppd, QSFP_CC_EXT_OFFS, &cp->cks2, 1);
41862306a36Sopenharmony_ci	if (ret < 0)
41962306a36Sopenharmony_ci		goto bail;
42062306a36Sopenharmony_ci	cks &= 0xFF;
42162306a36Sopenharmony_ci	if (cks != cp->cks2)
42262306a36Sopenharmony_ci		qib_dev_porterr(ppd->dd, ppd->port,
42362306a36Sopenharmony_ci				"QSFP cks2 is %02X, computed %02X\n", cp->cks2,
42462306a36Sopenharmony_ci				cks);
42562306a36Sopenharmony_ci	return 0;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cibail:
42862306a36Sopenharmony_ci	cp->id = 0;
42962306a36Sopenharmony_ci	return ret;
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ciconst char * const qib_qsfp_devtech[16] = {
43362306a36Sopenharmony_ci	"850nm VCSEL", "1310nm VCSEL", "1550nm VCSEL", "1310nm FP",
43462306a36Sopenharmony_ci	"1310nm DFB", "1550nm DFB", "1310nm EML", "1550nm EML",
43562306a36Sopenharmony_ci	"Cu Misc", "1490nm DFB", "Cu NoEq", "Cu Eq",
43662306a36Sopenharmony_ci	"Undef", "Cu Active BothEq", "Cu FarEq", "Cu NearEq"
43762306a36Sopenharmony_ci};
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci#define QSFP_DUMP_CHUNK 16 /* Holds longest string */
44062306a36Sopenharmony_ci#define QSFP_DEFAULT_HDR_CNT 224
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic const char *pwr_codes = "1.5W2.0W2.5W3.5W";
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ciint qib_qsfp_mod_present(struct qib_pportdata *ppd)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	u32 mask;
44762306a36Sopenharmony_ci	int ret;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	mask = QSFP_GPIO_MOD_PRS_N <<
45062306a36Sopenharmony_ci		(ppd->hw_pidx * QSFP_GPIO_PORT2_SHIFT);
45162306a36Sopenharmony_ci	ret = ppd->dd->f_gpio_mod(ppd->dd, 0, 0, 0);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	return !((ret & mask) >>
45462306a36Sopenharmony_ci		 ((ppd->hw_pidx * QSFP_GPIO_PORT2_SHIFT) + 3));
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci/*
45862306a36Sopenharmony_ci * Initialize structures that control access to QSFP. Called once per port
45962306a36Sopenharmony_ci * on cards that support QSFP.
46062306a36Sopenharmony_ci */
46162306a36Sopenharmony_civoid qib_qsfp_init(struct qib_qsfp_data *qd,
46262306a36Sopenharmony_ci		   void (*fevent)(struct work_struct *))
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	u32 mask, highs;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	struct qib_devdata *dd = qd->ppd->dd;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	/* Initialize work struct for later QSFP events */
46962306a36Sopenharmony_ci	INIT_WORK(&qd->work, fevent);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/*
47262306a36Sopenharmony_ci	 * Later, we may want more validation. For now, just set up pins and
47362306a36Sopenharmony_ci	 * blip reset. If module is present, call qib_refresh_qsfp_cache(),
47462306a36Sopenharmony_ci	 * to do further init.
47562306a36Sopenharmony_ci	 */
47662306a36Sopenharmony_ci	mask = QSFP_GPIO_MOD_SEL_N | QSFP_GPIO_MOD_RST_N | QSFP_GPIO_LP_MODE;
47762306a36Sopenharmony_ci	highs = mask - QSFP_GPIO_MOD_RST_N;
47862306a36Sopenharmony_ci	if (qd->ppd->hw_pidx) {
47962306a36Sopenharmony_ci		mask <<= QSFP_GPIO_PORT2_SHIFT;
48062306a36Sopenharmony_ci		highs <<= QSFP_GPIO_PORT2_SHIFT;
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci	dd->f_gpio_mod(dd, highs, mask, mask);
48362306a36Sopenharmony_ci	udelay(20); /* Generous RST dwell */
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	dd->f_gpio_mod(dd, mask, mask, mask);
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ciint qib_qsfp_dump(struct qib_pportdata *ppd, char *buf, int len)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct qib_qsfp_cache cd;
49162306a36Sopenharmony_ci	u8 bin_buff[QSFP_DUMP_CHUNK];
49262306a36Sopenharmony_ci	char lenstr[6];
49362306a36Sopenharmony_ci	int sofar, ret;
49462306a36Sopenharmony_ci	int bidx = 0;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	sofar = 0;
49762306a36Sopenharmony_ci	ret = qib_refresh_qsfp_cache(ppd, &cd);
49862306a36Sopenharmony_ci	if (ret < 0)
49962306a36Sopenharmony_ci		goto bail;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	lenstr[0] = ' ';
50262306a36Sopenharmony_ci	lenstr[1] = '\0';
50362306a36Sopenharmony_ci	if (QSFP_IS_CU(cd.tech))
50462306a36Sopenharmony_ci		sprintf(lenstr, "%dM ", cd.len);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	sofar += scnprintf(buf + sofar, len - sofar, "PWR:%.3sW\n", pwr_codes +
50762306a36Sopenharmony_ci			   (QSFP_PWR(cd.pwr) * 4));
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	sofar += scnprintf(buf + sofar, len - sofar, "TECH:%s%s\n", lenstr,
51062306a36Sopenharmony_ci			   qib_qsfp_devtech[cd.tech >> 4]);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	sofar += scnprintf(buf + sofar, len - sofar, "Vendor:%.*s\n",
51362306a36Sopenharmony_ci			   QSFP_VEND_LEN, cd.vendor);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	sofar += scnprintf(buf + sofar, len - sofar, "OUI:%06X\n",
51662306a36Sopenharmony_ci			   QSFP_OUI(cd.oui));
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	sofar += scnprintf(buf + sofar, len - sofar, "Part#:%.*s\n",
51962306a36Sopenharmony_ci			   QSFP_PN_LEN, cd.partnum);
52062306a36Sopenharmony_ci	sofar += scnprintf(buf + sofar, len - sofar, "Rev:%.*s\n",
52162306a36Sopenharmony_ci			   QSFP_REV_LEN, cd.rev);
52262306a36Sopenharmony_ci	if (QSFP_IS_CU(cd.tech))
52362306a36Sopenharmony_ci		sofar += scnprintf(buf + sofar, len - sofar, "Atten:%d, %d\n",
52462306a36Sopenharmony_ci				   QSFP_ATTEN_SDR(cd.atten),
52562306a36Sopenharmony_ci				   QSFP_ATTEN_DDR(cd.atten));
52662306a36Sopenharmony_ci	sofar += scnprintf(buf + sofar, len - sofar, "Serial:%.*s\n",
52762306a36Sopenharmony_ci			   QSFP_SN_LEN, cd.serial);
52862306a36Sopenharmony_ci	sofar += scnprintf(buf + sofar, len - sofar, "Date:%.*s\n",
52962306a36Sopenharmony_ci			   QSFP_DATE_LEN, cd.date);
53062306a36Sopenharmony_ci	sofar += scnprintf(buf + sofar, len - sofar, "Lot:%.*s\n",
53162306a36Sopenharmony_ci			   QSFP_LOT_LEN, cd.lot);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	while (bidx < QSFP_DEFAULT_HDR_CNT) {
53462306a36Sopenharmony_ci		int iidx;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		ret = qsfp_read(ppd, bidx, bin_buff, QSFP_DUMP_CHUNK);
53762306a36Sopenharmony_ci		if (ret < 0)
53862306a36Sopenharmony_ci			goto bail;
53962306a36Sopenharmony_ci		for (iidx = 0; iidx < ret; ++iidx) {
54062306a36Sopenharmony_ci			sofar += scnprintf(buf + sofar, len-sofar, " %02X",
54162306a36Sopenharmony_ci				bin_buff[iidx]);
54262306a36Sopenharmony_ci		}
54362306a36Sopenharmony_ci		sofar += scnprintf(buf + sofar, len - sofar, "\n");
54462306a36Sopenharmony_ci		bidx += QSFP_DUMP_CHUNK;
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci	ret = sofar;
54762306a36Sopenharmony_cibail:
54862306a36Sopenharmony_ci	return ret;
54962306a36Sopenharmony_ci}
550