18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two
58c2ecf20Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
68c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
78c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the
88c2ecf20Sopenharmony_ci * OpenIB.org BSD license below:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
118c2ecf20Sopenharmony_ci *     without modification, are permitted provided that the following
128c2ecf20Sopenharmony_ci *     conditions are met:
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci *      - Redistributions of source code must retain the above
158c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
168c2ecf20Sopenharmony_ci *        disclaimer.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
198c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
208c2ecf20Sopenharmony_ci *        disclaimer in the documentation and/or other materials
218c2ecf20Sopenharmony_ci *        provided with the distribution.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
248c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
258c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
268c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
278c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
288c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
298c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
308c2ecf20Sopenharmony_ci * SOFTWARE.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci#include "common.h"
338c2ecf20Sopenharmony_ci#include "regs.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cienum {
368c2ecf20Sopenharmony_ci	AEL100X_TX_CONFIG1 = 0xc002,
378c2ecf20Sopenharmony_ci	AEL1002_PWR_DOWN_HI = 0xc011,
388c2ecf20Sopenharmony_ci	AEL1002_PWR_DOWN_LO = 0xc012,
398c2ecf20Sopenharmony_ci	AEL1002_XFI_EQL = 0xc015,
408c2ecf20Sopenharmony_ci	AEL1002_LB_EN = 0xc017,
418c2ecf20Sopenharmony_ci	AEL_OPT_SETTINGS = 0xc017,
428c2ecf20Sopenharmony_ci	AEL_I2C_CTRL = 0xc30a,
438c2ecf20Sopenharmony_ci	AEL_I2C_DATA = 0xc30b,
448c2ecf20Sopenharmony_ci	AEL_I2C_STAT = 0xc30c,
458c2ecf20Sopenharmony_ci	AEL2005_GPIO_CTRL = 0xc214,
468c2ecf20Sopenharmony_ci	AEL2005_GPIO_STAT = 0xc215,
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	AEL2020_GPIO_INTR   = 0xc103,	/* Latch High (LH) */
498c2ecf20Sopenharmony_ci	AEL2020_GPIO_CTRL   = 0xc108,	/* Store Clear (SC) */
508c2ecf20Sopenharmony_ci	AEL2020_GPIO_STAT   = 0xc10c,	/* Read Only (RO) */
518c2ecf20Sopenharmony_ci	AEL2020_GPIO_CFG    = 0xc110,	/* Read Write (RW) */
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	AEL2020_GPIO_SDA    = 0,	/* IN: i2c serial data */
548c2ecf20Sopenharmony_ci	AEL2020_GPIO_MODDET = 1,	/* IN: Module Detect */
558c2ecf20Sopenharmony_ci	AEL2020_GPIO_0      = 3,	/* IN: unassigned */
568c2ecf20Sopenharmony_ci	AEL2020_GPIO_1      = 2,	/* OUT: unassigned */
578c2ecf20Sopenharmony_ci	AEL2020_GPIO_LSTAT  = AEL2020_GPIO_1, /* wired to link status LED */
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cienum { edc_none, edc_sr, edc_twinax };
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* PHY module I2C device address */
638c2ecf20Sopenharmony_cienum {
648c2ecf20Sopenharmony_ci	MODULE_DEV_ADDR	= 0xa0,
658c2ecf20Sopenharmony_ci	SFF_DEV_ADDR	= 0xa2,
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* PHY transceiver type */
698c2ecf20Sopenharmony_cienum {
708c2ecf20Sopenharmony_ci	phy_transtype_unknown = 0,
718c2ecf20Sopenharmony_ci	phy_transtype_sfp     = 3,
728c2ecf20Sopenharmony_ci	phy_transtype_xfp     = 6,
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define AEL2005_MODDET_IRQ 4
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistruct reg_val {
788c2ecf20Sopenharmony_ci	unsigned short mmd_addr;
798c2ecf20Sopenharmony_ci	unsigned short reg_addr;
808c2ecf20Sopenharmony_ci	unsigned short clear_bits;
818c2ecf20Sopenharmony_ci	unsigned short set_bits;
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int set_phy_regs(struct cphy *phy, const struct reg_val *rv)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	int err;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	for (err = 0; rv->mmd_addr && !err; rv++) {
898c2ecf20Sopenharmony_ci		if (rv->clear_bits == 0xffff)
908c2ecf20Sopenharmony_ci			err = t3_mdio_write(phy, rv->mmd_addr, rv->reg_addr,
918c2ecf20Sopenharmony_ci					    rv->set_bits);
928c2ecf20Sopenharmony_ci		else
938c2ecf20Sopenharmony_ci			err = t3_mdio_change_bits(phy, rv->mmd_addr,
948c2ecf20Sopenharmony_ci						  rv->reg_addr, rv->clear_bits,
958c2ecf20Sopenharmony_ci						  rv->set_bits);
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci	return err;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic void ael100x_txon(struct cphy *phy)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	int tx_on_gpio =
1038c2ecf20Sopenharmony_ci		phy->mdio.prtad == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	msleep(100);
1068c2ecf20Sopenharmony_ci	t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio);
1078c2ecf20Sopenharmony_ci	msleep(30);
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/*
1118c2ecf20Sopenharmony_ci * Read an 8-bit word from a device attached to the PHY's i2c bus.
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_cistatic int ael_i2c_rd(struct cphy *phy, int dev_addr, int word_addr)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	int i, err;
1168c2ecf20Sopenharmony_ci	unsigned int stat, data;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL_I2C_CTRL,
1198c2ecf20Sopenharmony_ci			    (dev_addr << 8) | (1 << 8) | word_addr);
1208c2ecf20Sopenharmony_ci	if (err)
1218c2ecf20Sopenharmony_ci		return err;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	for (i = 0; i < 200; i++) {
1248c2ecf20Sopenharmony_ci		msleep(1);
1258c2ecf20Sopenharmony_ci		err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL_I2C_STAT, &stat);
1268c2ecf20Sopenharmony_ci		if (err)
1278c2ecf20Sopenharmony_ci			return err;
1288c2ecf20Sopenharmony_ci		if ((stat & 3) == 1) {
1298c2ecf20Sopenharmony_ci			err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL_I2C_DATA,
1308c2ecf20Sopenharmony_ci					   &data);
1318c2ecf20Sopenharmony_ci			if (err)
1328c2ecf20Sopenharmony_ci				return err;
1338c2ecf20Sopenharmony_ci			return data >> 8;
1348c2ecf20Sopenharmony_ci		}
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci	CH_WARN(phy->adapter, "PHY %u i2c read of dev.addr %#x.%#x timed out\n",
1378c2ecf20Sopenharmony_ci		phy->mdio.prtad, dev_addr, word_addr);
1388c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic int ael1002_power_down(struct cphy *phy, int enable)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	int err;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, !!enable);
1468c2ecf20Sopenharmony_ci	if (!err)
1478c2ecf20Sopenharmony_ci		err = mdio_set_flag(&phy->mdio, phy->mdio.prtad,
1488c2ecf20Sopenharmony_ci				    MDIO_MMD_PMAPMD, MDIO_CTRL1,
1498c2ecf20Sopenharmony_ci				    MDIO_CTRL1_LPOWER, enable);
1508c2ecf20Sopenharmony_ci	return err;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic int ael1002_reset(struct cphy *phy, int wait)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	int err;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if ((err = ael1002_power_down(phy, 0)) ||
1588c2ecf20Sopenharmony_ci	    (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL100X_TX_CONFIG1, 1)) ||
1598c2ecf20Sopenharmony_ci	    (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_PWR_DOWN_HI, 0)) ||
1608c2ecf20Sopenharmony_ci	    (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_PWR_DOWN_LO, 0)) ||
1618c2ecf20Sopenharmony_ci	    (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_XFI_EQL, 0x18)) ||
1628c2ecf20Sopenharmony_ci	    (err = t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, AEL1002_LB_EN,
1638c2ecf20Sopenharmony_ci				       0, 1 << 5)))
1648c2ecf20Sopenharmony_ci		return err;
1658c2ecf20Sopenharmony_ci	return 0;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic int ael1002_intr_noop(struct cphy *phy)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	return 0;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci/*
1748c2ecf20Sopenharmony_ci * Get link status for a 10GBASE-R device.
1758c2ecf20Sopenharmony_ci */
1768c2ecf20Sopenharmony_cistatic int get_link_status_r(struct cphy *phy, int *link_ok, int *speed,
1778c2ecf20Sopenharmony_ci			     int *duplex, int *fc)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	if (link_ok) {
1808c2ecf20Sopenharmony_ci		unsigned int stat0, stat1, stat2;
1818c2ecf20Sopenharmony_ci		int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD,
1828c2ecf20Sopenharmony_ci				       MDIO_PMA_RXDET, &stat0);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		if (!err)
1858c2ecf20Sopenharmony_ci			err = t3_mdio_read(phy, MDIO_MMD_PCS,
1868c2ecf20Sopenharmony_ci					   MDIO_PCS_10GBRT_STAT1, &stat1);
1878c2ecf20Sopenharmony_ci		if (!err)
1888c2ecf20Sopenharmony_ci			err = t3_mdio_read(phy, MDIO_MMD_PHYXS,
1898c2ecf20Sopenharmony_ci					   MDIO_PHYXS_LNSTAT, &stat2);
1908c2ecf20Sopenharmony_ci		if (err)
1918c2ecf20Sopenharmony_ci			return err;
1928c2ecf20Sopenharmony_ci		*link_ok = (stat0 & stat1 & (stat2 >> 12)) & 1;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci	if (speed)
1958c2ecf20Sopenharmony_ci		*speed = SPEED_10000;
1968c2ecf20Sopenharmony_ci	if (duplex)
1978c2ecf20Sopenharmony_ci		*duplex = DUPLEX_FULL;
1988c2ecf20Sopenharmony_ci	return 0;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic const struct cphy_ops ael1002_ops = {
2028c2ecf20Sopenharmony_ci	.reset = ael1002_reset,
2038c2ecf20Sopenharmony_ci	.intr_enable = ael1002_intr_noop,
2048c2ecf20Sopenharmony_ci	.intr_disable = ael1002_intr_noop,
2058c2ecf20Sopenharmony_ci	.intr_clear = ael1002_intr_noop,
2068c2ecf20Sopenharmony_ci	.intr_handler = ael1002_intr_noop,
2078c2ecf20Sopenharmony_ci	.get_link_status = get_link_status_r,
2088c2ecf20Sopenharmony_ci	.power_down = ael1002_power_down,
2098c2ecf20Sopenharmony_ci	.mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,
2108c2ecf20Sopenharmony_ci};
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ciint t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter,
2138c2ecf20Sopenharmony_ci			int phy_addr, const struct mdio_ops *mdio_ops)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops,
2168c2ecf20Sopenharmony_ci		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE,
2178c2ecf20Sopenharmony_ci		   "10GBASE-R");
2188c2ecf20Sopenharmony_ci	ael100x_txon(phy);
2198c2ecf20Sopenharmony_ci	return 0;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic int ael1006_reset(struct cphy *phy, int wait)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	return t3_phy_reset(phy, MDIO_MMD_PMAPMD, wait);
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic const struct cphy_ops ael1006_ops = {
2288c2ecf20Sopenharmony_ci	.reset = ael1006_reset,
2298c2ecf20Sopenharmony_ci	.intr_enable = t3_phy_lasi_intr_enable,
2308c2ecf20Sopenharmony_ci	.intr_disable = t3_phy_lasi_intr_disable,
2318c2ecf20Sopenharmony_ci	.intr_clear = t3_phy_lasi_intr_clear,
2328c2ecf20Sopenharmony_ci	.intr_handler = t3_phy_lasi_intr_handler,
2338c2ecf20Sopenharmony_ci	.get_link_status = get_link_status_r,
2348c2ecf20Sopenharmony_ci	.power_down = ael1002_power_down,
2358c2ecf20Sopenharmony_ci	.mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,
2368c2ecf20Sopenharmony_ci};
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ciint t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter,
2398c2ecf20Sopenharmony_ci			     int phy_addr, const struct mdio_ops *mdio_ops)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops,
2428c2ecf20Sopenharmony_ci		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE,
2438c2ecf20Sopenharmony_ci		   "10GBASE-SR");
2448c2ecf20Sopenharmony_ci	ael100x_txon(phy);
2458c2ecf20Sopenharmony_ci	return 0;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci/*
2498c2ecf20Sopenharmony_ci * Decode our module type.
2508c2ecf20Sopenharmony_ci */
2518c2ecf20Sopenharmony_cistatic int ael2xxx_get_module_type(struct cphy *phy, int delay_ms)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	int v;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (delay_ms)
2568c2ecf20Sopenharmony_ci		msleep(delay_ms);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	/* see SFF-8472 for below */
2598c2ecf20Sopenharmony_ci	v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 3);
2608c2ecf20Sopenharmony_ci	if (v < 0)
2618c2ecf20Sopenharmony_ci		return v;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	if (v == 0x10)
2648c2ecf20Sopenharmony_ci		return phy_modtype_sr;
2658c2ecf20Sopenharmony_ci	if (v == 0x20)
2668c2ecf20Sopenharmony_ci		return phy_modtype_lr;
2678c2ecf20Sopenharmony_ci	if (v == 0x40)
2688c2ecf20Sopenharmony_ci		return phy_modtype_lrm;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 6);
2718c2ecf20Sopenharmony_ci	if (v < 0)
2728c2ecf20Sopenharmony_ci		return v;
2738c2ecf20Sopenharmony_ci	if (v != 4)
2748c2ecf20Sopenharmony_ci		goto unknown;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 10);
2778c2ecf20Sopenharmony_ci	if (v < 0)
2788c2ecf20Sopenharmony_ci		return v;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (v & 0x80) {
2818c2ecf20Sopenharmony_ci		v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 0x12);
2828c2ecf20Sopenharmony_ci		if (v < 0)
2838c2ecf20Sopenharmony_ci			return v;
2848c2ecf20Sopenharmony_ci		return v > 10 ? phy_modtype_twinax_long : phy_modtype_twinax;
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ciunknown:
2878c2ecf20Sopenharmony_ci	return phy_modtype_unknown;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci/*
2918c2ecf20Sopenharmony_ci * Code to support the Aeluros/NetLogic 2005 10Gb PHY.
2928c2ecf20Sopenharmony_ci */
2938c2ecf20Sopenharmony_cistatic int ael2005_setup_sr_edc(struct cphy *phy)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	static const struct reg_val regs[] = {
2968c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x181 },
2978c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xc010, 0xffff, 0x448a },
2988c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5200 },
2998c2ecf20Sopenharmony_ci		{ 0, 0, 0, 0 }
3008c2ecf20Sopenharmony_ci	};
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	int i, err;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	err = set_phy_regs(phy, regs);
3058c2ecf20Sopenharmony_ci	if (err)
3068c2ecf20Sopenharmony_ci		return err;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	msleep(50);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (phy->priv != edc_sr)
3118c2ecf20Sopenharmony_ci		err = t3_get_edc_fw(phy, EDC_OPT_AEL2005,
3128c2ecf20Sopenharmony_ci				    EDC_OPT_AEL2005_SIZE);
3138c2ecf20Sopenharmony_ci	if (err)
3148c2ecf20Sopenharmony_ci		return err;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	for (i = 0; i <  EDC_OPT_AEL2005_SIZE / sizeof(u16) && !err; i += 2)
3178c2ecf20Sopenharmony_ci		err = t3_mdio_write(phy, MDIO_MMD_PMAPMD,
3188c2ecf20Sopenharmony_ci				    phy->phy_cache[i],
3198c2ecf20Sopenharmony_ci				    phy->phy_cache[i + 1]);
3208c2ecf20Sopenharmony_ci	if (!err)
3218c2ecf20Sopenharmony_ci		phy->priv = edc_sr;
3228c2ecf20Sopenharmony_ci	return err;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic int ael2005_setup_twinax_edc(struct cphy *phy, int modtype)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	static const struct reg_val regs[] = {
3288c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5a00 },
3298c2ecf20Sopenharmony_ci		{ 0, 0, 0, 0 }
3308c2ecf20Sopenharmony_ci	};
3318c2ecf20Sopenharmony_ci	static const struct reg_val preemphasis[] = {
3328c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xc014, 0xffff, 0xfe16 },
3338c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xc015, 0xffff, 0xa000 },
3348c2ecf20Sopenharmony_ci		{ 0, 0, 0, 0 }
3358c2ecf20Sopenharmony_ci	};
3368c2ecf20Sopenharmony_ci	int i, err;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	err = set_phy_regs(phy, regs);
3398c2ecf20Sopenharmony_ci	if (!err && modtype == phy_modtype_twinax_long)
3408c2ecf20Sopenharmony_ci		err = set_phy_regs(phy, preemphasis);
3418c2ecf20Sopenharmony_ci	if (err)
3428c2ecf20Sopenharmony_ci		return err;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	msleep(50);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (phy->priv != edc_twinax)
3478c2ecf20Sopenharmony_ci		err = t3_get_edc_fw(phy, EDC_TWX_AEL2005,
3488c2ecf20Sopenharmony_ci				    EDC_TWX_AEL2005_SIZE);
3498c2ecf20Sopenharmony_ci	if (err)
3508c2ecf20Sopenharmony_ci		return err;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	for (i = 0; i <  EDC_TWX_AEL2005_SIZE / sizeof(u16) && !err; i += 2)
3538c2ecf20Sopenharmony_ci		err = t3_mdio_write(phy, MDIO_MMD_PMAPMD,
3548c2ecf20Sopenharmony_ci				    phy->phy_cache[i],
3558c2ecf20Sopenharmony_ci				    phy->phy_cache[i + 1]);
3568c2ecf20Sopenharmony_ci	if (!err)
3578c2ecf20Sopenharmony_ci		phy->priv = edc_twinax;
3588c2ecf20Sopenharmony_ci	return err;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic int ael2005_get_module_type(struct cphy *phy, int delay_ms)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	int v;
3648c2ecf20Sopenharmony_ci	unsigned int stat;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	v = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, &stat);
3678c2ecf20Sopenharmony_ci	if (v)
3688c2ecf20Sopenharmony_ci		return v;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (stat & (1 << 8))			/* module absent */
3718c2ecf20Sopenharmony_ci		return phy_modtype_none;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	return ael2xxx_get_module_type(phy, delay_ms);
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic int ael2005_intr_enable(struct cphy *phy)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0x200);
3798c2ecf20Sopenharmony_ci	return err ? err : t3_phy_lasi_intr_enable(phy);
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic int ael2005_intr_disable(struct cphy *phy)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0x100);
3858c2ecf20Sopenharmony_ci	return err ? err : t3_phy_lasi_intr_disable(phy);
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic int ael2005_intr_clear(struct cphy *phy)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0xd00);
3918c2ecf20Sopenharmony_ci	return err ? err : t3_phy_lasi_intr_clear(phy);
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic int ael2005_reset(struct cphy *phy, int wait)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	static const struct reg_val regs0[] = {
3978c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xc001, 0, 1 << 5 },
3988c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xc017, 0, 1 << 5 },
3998c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xc013, 0xffff, 0xf341 },
4008c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8000 },
4018c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8100 },
4028c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8000 },
4038c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0 },
4048c2ecf20Sopenharmony_ci		{ 0, 0, 0, 0 }
4058c2ecf20Sopenharmony_ci	};
4068c2ecf20Sopenharmony_ci	static const struct reg_val regs1[] = {
4078c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xca00, 0xffff, 0x0080 },
4088c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xca12, 0xffff, 0 },
4098c2ecf20Sopenharmony_ci		{ 0, 0, 0, 0 }
4108c2ecf20Sopenharmony_ci	};
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	int err;
4138c2ecf20Sopenharmony_ci	unsigned int lasi_ctrl;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL,
4168c2ecf20Sopenharmony_ci			   &lasi_ctrl);
4178c2ecf20Sopenharmony_ci	if (err)
4188c2ecf20Sopenharmony_ci		return err;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, 0);
4218c2ecf20Sopenharmony_ci	if (err)
4228c2ecf20Sopenharmony_ci		return err;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	msleep(125);
4258c2ecf20Sopenharmony_ci	phy->priv = edc_none;
4268c2ecf20Sopenharmony_ci	err = set_phy_regs(phy, regs0);
4278c2ecf20Sopenharmony_ci	if (err)
4288c2ecf20Sopenharmony_ci		return err;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	msleep(50);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	err = ael2005_get_module_type(phy, 0);
4338c2ecf20Sopenharmony_ci	if (err < 0)
4348c2ecf20Sopenharmony_ci		return err;
4358c2ecf20Sopenharmony_ci	phy->modtype = err;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (err == phy_modtype_twinax || err == phy_modtype_twinax_long)
4388c2ecf20Sopenharmony_ci		err = ael2005_setup_twinax_edc(phy, err);
4398c2ecf20Sopenharmony_ci	else
4408c2ecf20Sopenharmony_ci		err = ael2005_setup_sr_edc(phy);
4418c2ecf20Sopenharmony_ci	if (err)
4428c2ecf20Sopenharmony_ci		return err;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	err = set_phy_regs(phy, regs1);
4458c2ecf20Sopenharmony_ci	if (err)
4468c2ecf20Sopenharmony_ci		return err;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	/* reset wipes out interrupts, reenable them if they were on */
4498c2ecf20Sopenharmony_ci	if (lasi_ctrl & 1)
4508c2ecf20Sopenharmony_ci		err = ael2005_intr_enable(phy);
4518c2ecf20Sopenharmony_ci	return err;
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic int ael2005_intr_handler(struct cphy *phy)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	unsigned int stat;
4578c2ecf20Sopenharmony_ci	int ret, edc_needed, cause = 0;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_STAT, &stat);
4608c2ecf20Sopenharmony_ci	if (ret)
4618c2ecf20Sopenharmony_ci		return ret;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	if (stat & AEL2005_MODDET_IRQ) {
4648c2ecf20Sopenharmony_ci		ret = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL,
4658c2ecf20Sopenharmony_ci				    0xd00);
4668c2ecf20Sopenharmony_ci		if (ret)
4678c2ecf20Sopenharmony_ci			return ret;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci		/* modules have max 300 ms init time after hot plug */
4708c2ecf20Sopenharmony_ci		ret = ael2005_get_module_type(phy, 300);
4718c2ecf20Sopenharmony_ci		if (ret < 0)
4728c2ecf20Sopenharmony_ci			return ret;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		phy->modtype = ret;
4758c2ecf20Sopenharmony_ci		if (ret == phy_modtype_none)
4768c2ecf20Sopenharmony_ci			edc_needed = phy->priv;       /* on unplug retain EDC */
4778c2ecf20Sopenharmony_ci		else if (ret == phy_modtype_twinax ||
4788c2ecf20Sopenharmony_ci			 ret == phy_modtype_twinax_long)
4798c2ecf20Sopenharmony_ci			edc_needed = edc_twinax;
4808c2ecf20Sopenharmony_ci		else
4818c2ecf20Sopenharmony_ci			edc_needed = edc_sr;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci		if (edc_needed != phy->priv) {
4848c2ecf20Sopenharmony_ci			ret = ael2005_reset(phy, 0);
4858c2ecf20Sopenharmony_ci			return ret ? ret : cphy_cause_module_change;
4868c2ecf20Sopenharmony_ci		}
4878c2ecf20Sopenharmony_ci		cause = cphy_cause_module_change;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	ret = t3_phy_lasi_intr_handler(phy);
4918c2ecf20Sopenharmony_ci	if (ret < 0)
4928c2ecf20Sopenharmony_ci		return ret;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	ret |= cause;
4958c2ecf20Sopenharmony_ci	return ret ? ret : cphy_cause_link_change;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic const struct cphy_ops ael2005_ops = {
4998c2ecf20Sopenharmony_ci	.reset           = ael2005_reset,
5008c2ecf20Sopenharmony_ci	.intr_enable     = ael2005_intr_enable,
5018c2ecf20Sopenharmony_ci	.intr_disable    = ael2005_intr_disable,
5028c2ecf20Sopenharmony_ci	.intr_clear      = ael2005_intr_clear,
5038c2ecf20Sopenharmony_ci	.intr_handler    = ael2005_intr_handler,
5048c2ecf20Sopenharmony_ci	.get_link_status = get_link_status_r,
5058c2ecf20Sopenharmony_ci	.power_down      = ael1002_power_down,
5068c2ecf20Sopenharmony_ci	.mmds            = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,
5078c2ecf20Sopenharmony_ci};
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ciint t3_ael2005_phy_prep(struct cphy *phy, struct adapter *adapter,
5108c2ecf20Sopenharmony_ci			int phy_addr, const struct mdio_ops *mdio_ops)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	cphy_init(phy, adapter, phy_addr, &ael2005_ops, mdio_ops,
5138c2ecf20Sopenharmony_ci		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE |
5148c2ecf20Sopenharmony_ci		  SUPPORTED_IRQ, "10GBASE-R");
5158c2ecf20Sopenharmony_ci	msleep(125);
5168c2ecf20Sopenharmony_ci	return t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, AEL_OPT_SETTINGS, 0,
5178c2ecf20Sopenharmony_ci				   1 << 5);
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci/*
5218c2ecf20Sopenharmony_ci * Setup EDC and other parameters for operation with an optical module.
5228c2ecf20Sopenharmony_ci */
5238c2ecf20Sopenharmony_cistatic int ael2020_setup_sr_edc(struct cphy *phy)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	static const struct reg_val regs[] = {
5268c2ecf20Sopenharmony_ci		/* set CDR offset to 10 */
5278c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xcc01, 0xffff, 0x488a },
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci		/* adjust 10G RX bias current */
5308c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xcb1b, 0xffff, 0x0200 },
5318c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xcb1c, 0xffff, 0x00f0 },
5328c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xcc06, 0xffff, 0x00e0 },
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci		/* end */
5358c2ecf20Sopenharmony_ci		{ 0, 0, 0, 0 }
5368c2ecf20Sopenharmony_ci	};
5378c2ecf20Sopenharmony_ci	int err;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	err = set_phy_regs(phy, regs);
5408c2ecf20Sopenharmony_ci	msleep(50);
5418c2ecf20Sopenharmony_ci	if (err)
5428c2ecf20Sopenharmony_ci		return err;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	phy->priv = edc_sr;
5458c2ecf20Sopenharmony_ci	return 0;
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci/*
5498c2ecf20Sopenharmony_ci * Setup EDC and other parameters for operation with an TWINAX module.
5508c2ecf20Sopenharmony_ci */
5518c2ecf20Sopenharmony_cistatic int ael2020_setup_twinax_edc(struct cphy *phy, int modtype)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	/* set uC to 40MHz */
5548c2ecf20Sopenharmony_ci	static const struct reg_val uCclock40MHz[] = {
5558c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xff28, 0xffff, 0x4001 },
5568c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xff2a, 0xffff, 0x0002 },
5578c2ecf20Sopenharmony_ci		{ 0, 0, 0, 0 }
5588c2ecf20Sopenharmony_ci	};
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	/* activate uC clock */
5618c2ecf20Sopenharmony_ci	static const struct reg_val uCclockActivate[] = {
5628c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xd000, 0xffff, 0x5200 },
5638c2ecf20Sopenharmony_ci		{ 0, 0, 0, 0 }
5648c2ecf20Sopenharmony_ci	};
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/* set PC to start of SRAM and activate uC */
5678c2ecf20Sopenharmony_ci	static const struct reg_val uCactivate[] = {
5688c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xd080, 0xffff, 0x0100 },
5698c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, 0xd092, 0xffff, 0x0000 },
5708c2ecf20Sopenharmony_ci		{ 0, 0, 0, 0 }
5718c2ecf20Sopenharmony_ci	};
5728c2ecf20Sopenharmony_ci	int i, err;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	/* set uC clock and activate it */
5758c2ecf20Sopenharmony_ci	err = set_phy_regs(phy, uCclock40MHz);
5768c2ecf20Sopenharmony_ci	msleep(500);
5778c2ecf20Sopenharmony_ci	if (err)
5788c2ecf20Sopenharmony_ci		return err;
5798c2ecf20Sopenharmony_ci	err = set_phy_regs(phy, uCclockActivate);
5808c2ecf20Sopenharmony_ci	msleep(500);
5818c2ecf20Sopenharmony_ci	if (err)
5828c2ecf20Sopenharmony_ci		return err;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	if (phy->priv != edc_twinax)
5858c2ecf20Sopenharmony_ci		err = t3_get_edc_fw(phy, EDC_TWX_AEL2020,
5868c2ecf20Sopenharmony_ci				    EDC_TWX_AEL2020_SIZE);
5878c2ecf20Sopenharmony_ci	if (err)
5888c2ecf20Sopenharmony_ci		return err;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	for (i = 0; i <  EDC_TWX_AEL2020_SIZE / sizeof(u16) && !err; i += 2)
5918c2ecf20Sopenharmony_ci		err = t3_mdio_write(phy, MDIO_MMD_PMAPMD,
5928c2ecf20Sopenharmony_ci				    phy->phy_cache[i],
5938c2ecf20Sopenharmony_ci				    phy->phy_cache[i + 1]);
5948c2ecf20Sopenharmony_ci	/* activate uC */
5958c2ecf20Sopenharmony_ci	err = set_phy_regs(phy, uCactivate);
5968c2ecf20Sopenharmony_ci	if (!err)
5978c2ecf20Sopenharmony_ci		phy->priv = edc_twinax;
5988c2ecf20Sopenharmony_ci	return err;
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci/*
6028c2ecf20Sopenharmony_ci * Return Module Type.
6038c2ecf20Sopenharmony_ci */
6048c2ecf20Sopenharmony_cistatic int ael2020_get_module_type(struct cphy *phy, int delay_ms)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	int v;
6078c2ecf20Sopenharmony_ci	unsigned int stat;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	v = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_STAT, &stat);
6108c2ecf20Sopenharmony_ci	if (v)
6118c2ecf20Sopenharmony_ci		return v;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	if (stat & (0x1 << (AEL2020_GPIO_MODDET*4))) {
6148c2ecf20Sopenharmony_ci		/* module absent */
6158c2ecf20Sopenharmony_ci		return phy_modtype_none;
6168c2ecf20Sopenharmony_ci	}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	return ael2xxx_get_module_type(phy, delay_ms);
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci/*
6228c2ecf20Sopenharmony_ci * Enable PHY interrupts.  We enable "Module Detection" interrupts (on any
6238c2ecf20Sopenharmony_ci * state transition) and then generic Link Alarm Status Interrupt (LASI).
6248c2ecf20Sopenharmony_ci */
6258c2ecf20Sopenharmony_cistatic int ael2020_intr_enable(struct cphy *phy)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	static const struct reg_val regs[] = {
6288c2ecf20Sopenharmony_ci		/* output Module's Loss Of Signal (LOS) to LED */
6298c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CFG+AEL2020_GPIO_LSTAT,
6308c2ecf20Sopenharmony_ci			0xffff, 0x4 },
6318c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,
6328c2ecf20Sopenharmony_ci			0xffff, 0x8 << (AEL2020_GPIO_LSTAT*4) },
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci		 /* enable module detect status change interrupts */
6358c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,
6368c2ecf20Sopenharmony_ci			0xffff, 0x2 << (AEL2020_GPIO_MODDET*4) },
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci		/* end */
6398c2ecf20Sopenharmony_ci		{ 0, 0, 0, 0 }
6408c2ecf20Sopenharmony_ci	};
6418c2ecf20Sopenharmony_ci	int err, link_ok = 0;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	/* set up "link status" LED and enable module change interrupts */
6448c2ecf20Sopenharmony_ci	err = set_phy_regs(phy, regs);
6458c2ecf20Sopenharmony_ci	if (err)
6468c2ecf20Sopenharmony_ci		return err;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	err = get_link_status_r(phy, &link_ok, NULL, NULL, NULL);
6498c2ecf20Sopenharmony_ci	if (err)
6508c2ecf20Sopenharmony_ci		return err;
6518c2ecf20Sopenharmony_ci	if (link_ok)
6528c2ecf20Sopenharmony_ci		t3_link_changed(phy->adapter,
6538c2ecf20Sopenharmony_ci				phy2portid(phy));
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	err = t3_phy_lasi_intr_enable(phy);
6568c2ecf20Sopenharmony_ci	if (err)
6578c2ecf20Sopenharmony_ci		return err;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	return 0;
6608c2ecf20Sopenharmony_ci}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci/*
6638c2ecf20Sopenharmony_ci * Disable PHY interrupts.  The mirror of the above ...
6648c2ecf20Sopenharmony_ci */
6658c2ecf20Sopenharmony_cistatic int ael2020_intr_disable(struct cphy *phy)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	static const struct reg_val regs[] = {
6688c2ecf20Sopenharmony_ci		/* reset "link status" LED to "off" */
6698c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,
6708c2ecf20Sopenharmony_ci			0xffff, 0xb << (AEL2020_GPIO_LSTAT*4) },
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci		/* disable module detect status change interrupts */
6738c2ecf20Sopenharmony_ci		{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,
6748c2ecf20Sopenharmony_ci			0xffff, 0x1 << (AEL2020_GPIO_MODDET*4) },
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci		/* end */
6778c2ecf20Sopenharmony_ci		{ 0, 0, 0, 0 }
6788c2ecf20Sopenharmony_ci	};
6798c2ecf20Sopenharmony_ci	int err;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	/* turn off "link status" LED and disable module change interrupts */
6828c2ecf20Sopenharmony_ci	err = set_phy_regs(phy, regs);
6838c2ecf20Sopenharmony_ci	if (err)
6848c2ecf20Sopenharmony_ci		return err;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	return t3_phy_lasi_intr_disable(phy);
6878c2ecf20Sopenharmony_ci}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci/*
6908c2ecf20Sopenharmony_ci * Clear PHY interrupt state.
6918c2ecf20Sopenharmony_ci */
6928c2ecf20Sopenharmony_cistatic int ael2020_intr_clear(struct cphy *phy)
6938c2ecf20Sopenharmony_ci{
6948c2ecf20Sopenharmony_ci	/*
6958c2ecf20Sopenharmony_ci	 * The GPIO Interrupt register on the AEL2020 is a "Latching High"
6968c2ecf20Sopenharmony_ci	 * (LH) register which is cleared to the current state when it's read.
6978c2ecf20Sopenharmony_ci	 * Thus, we simply read the register and discard the result.
6988c2ecf20Sopenharmony_ci	 */
6998c2ecf20Sopenharmony_ci	unsigned int stat;
7008c2ecf20Sopenharmony_ci	int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_INTR, &stat);
7018c2ecf20Sopenharmony_ci	return err ? err : t3_phy_lasi_intr_clear(phy);
7028c2ecf20Sopenharmony_ci}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_cistatic const struct reg_val ael2020_reset_regs[] = {
7058c2ecf20Sopenharmony_ci	/* Erratum #2: CDRLOL asserted, causing PMA link down status */
7068c2ecf20Sopenharmony_ci	{ MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x3101 },
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	/* force XAUI to send LF when RX_LOS is asserted */
7098c2ecf20Sopenharmony_ci	{ MDIO_MMD_PMAPMD, 0xcd40, 0xffff, 0x0001 },
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	/* allow writes to transceiver module EEPROM on i2c bus */
7128c2ecf20Sopenharmony_ci	{ MDIO_MMD_PMAPMD, 0xff02, 0xffff, 0x0023 },
7138c2ecf20Sopenharmony_ci	{ MDIO_MMD_PMAPMD, 0xff03, 0xffff, 0x0000 },
7148c2ecf20Sopenharmony_ci	{ MDIO_MMD_PMAPMD, 0xff04, 0xffff, 0x0000 },
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	/* end */
7178c2ecf20Sopenharmony_ci	{ 0, 0, 0, 0 }
7188c2ecf20Sopenharmony_ci};
7198c2ecf20Sopenharmony_ci/*
7208c2ecf20Sopenharmony_ci * Reset the PHY and put it into a canonical operating state.
7218c2ecf20Sopenharmony_ci */
7228c2ecf20Sopenharmony_cistatic int ael2020_reset(struct cphy *phy, int wait)
7238c2ecf20Sopenharmony_ci{
7248c2ecf20Sopenharmony_ci	int err;
7258c2ecf20Sopenharmony_ci	unsigned int lasi_ctrl;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	/* grab current interrupt state */
7288c2ecf20Sopenharmony_ci	err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL,
7298c2ecf20Sopenharmony_ci			   &lasi_ctrl);
7308c2ecf20Sopenharmony_ci	if (err)
7318c2ecf20Sopenharmony_ci		return err;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, 125);
7348c2ecf20Sopenharmony_ci	if (err)
7358c2ecf20Sopenharmony_ci		return err;
7368c2ecf20Sopenharmony_ci	msleep(100);
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	/* basic initialization for all module types */
7398c2ecf20Sopenharmony_ci	phy->priv = edc_none;
7408c2ecf20Sopenharmony_ci	err = set_phy_regs(phy, ael2020_reset_regs);
7418c2ecf20Sopenharmony_ci	if (err)
7428c2ecf20Sopenharmony_ci		return err;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	/* determine module type and perform appropriate initialization */
7458c2ecf20Sopenharmony_ci	err = ael2020_get_module_type(phy, 0);
7468c2ecf20Sopenharmony_ci	if (err < 0)
7478c2ecf20Sopenharmony_ci		return err;
7488c2ecf20Sopenharmony_ci	phy->modtype = (u8)err;
7498c2ecf20Sopenharmony_ci	if (err == phy_modtype_twinax || err == phy_modtype_twinax_long)
7508c2ecf20Sopenharmony_ci		err = ael2020_setup_twinax_edc(phy, err);
7518c2ecf20Sopenharmony_ci	else
7528c2ecf20Sopenharmony_ci		err = ael2020_setup_sr_edc(phy);
7538c2ecf20Sopenharmony_ci	if (err)
7548c2ecf20Sopenharmony_ci		return err;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	/* reset wipes out interrupts, reenable them if they were on */
7578c2ecf20Sopenharmony_ci	if (lasi_ctrl & 1)
7588c2ecf20Sopenharmony_ci		err = ael2005_intr_enable(phy);
7598c2ecf20Sopenharmony_ci	return err;
7608c2ecf20Sopenharmony_ci}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci/*
7638c2ecf20Sopenharmony_ci * Handle a PHY interrupt.
7648c2ecf20Sopenharmony_ci */
7658c2ecf20Sopenharmony_cistatic int ael2020_intr_handler(struct cphy *phy)
7668c2ecf20Sopenharmony_ci{
7678c2ecf20Sopenharmony_ci	unsigned int stat;
7688c2ecf20Sopenharmony_ci	int ret, edc_needed, cause = 0;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_INTR, &stat);
7718c2ecf20Sopenharmony_ci	if (ret)
7728c2ecf20Sopenharmony_ci		return ret;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	if (stat & (0x1 << AEL2020_GPIO_MODDET)) {
7758c2ecf20Sopenharmony_ci		/* modules have max 300 ms init time after hot plug */
7768c2ecf20Sopenharmony_ci		ret = ael2020_get_module_type(phy, 300);
7778c2ecf20Sopenharmony_ci		if (ret < 0)
7788c2ecf20Sopenharmony_ci			return ret;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci		phy->modtype = (u8)ret;
7818c2ecf20Sopenharmony_ci		if (ret == phy_modtype_none)
7828c2ecf20Sopenharmony_ci			edc_needed = phy->priv;       /* on unplug retain EDC */
7838c2ecf20Sopenharmony_ci		else if (ret == phy_modtype_twinax ||
7848c2ecf20Sopenharmony_ci			 ret == phy_modtype_twinax_long)
7858c2ecf20Sopenharmony_ci			edc_needed = edc_twinax;
7868c2ecf20Sopenharmony_ci		else
7878c2ecf20Sopenharmony_ci			edc_needed = edc_sr;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci		if (edc_needed != phy->priv) {
7908c2ecf20Sopenharmony_ci			ret = ael2020_reset(phy, 0);
7918c2ecf20Sopenharmony_ci			return ret ? ret : cphy_cause_module_change;
7928c2ecf20Sopenharmony_ci		}
7938c2ecf20Sopenharmony_ci		cause = cphy_cause_module_change;
7948c2ecf20Sopenharmony_ci	}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	ret = t3_phy_lasi_intr_handler(phy);
7978c2ecf20Sopenharmony_ci	if (ret < 0)
7988c2ecf20Sopenharmony_ci		return ret;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	ret |= cause;
8018c2ecf20Sopenharmony_ci	return ret ? ret : cphy_cause_link_change;
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cistatic const struct cphy_ops ael2020_ops = {
8058c2ecf20Sopenharmony_ci	.reset           = ael2020_reset,
8068c2ecf20Sopenharmony_ci	.intr_enable     = ael2020_intr_enable,
8078c2ecf20Sopenharmony_ci	.intr_disable    = ael2020_intr_disable,
8088c2ecf20Sopenharmony_ci	.intr_clear      = ael2020_intr_clear,
8098c2ecf20Sopenharmony_ci	.intr_handler    = ael2020_intr_handler,
8108c2ecf20Sopenharmony_ci	.get_link_status = get_link_status_r,
8118c2ecf20Sopenharmony_ci	.power_down      = ael1002_power_down,
8128c2ecf20Sopenharmony_ci	.mmds		 = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,
8138c2ecf20Sopenharmony_ci};
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ciint t3_ael2020_phy_prep(struct cphy *phy, struct adapter *adapter, int phy_addr,
8168c2ecf20Sopenharmony_ci			const struct mdio_ops *mdio_ops)
8178c2ecf20Sopenharmony_ci{
8188c2ecf20Sopenharmony_ci	cphy_init(phy, adapter, phy_addr, &ael2020_ops, mdio_ops,
8198c2ecf20Sopenharmony_ci		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE |
8208c2ecf20Sopenharmony_ci		  SUPPORTED_IRQ, "10GBASE-R");
8218c2ecf20Sopenharmony_ci	msleep(125);
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	return set_phy_regs(phy, ael2020_reset_regs);
8248c2ecf20Sopenharmony_ci}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci/*
8278c2ecf20Sopenharmony_ci * Get link status for a 10GBASE-X device.
8288c2ecf20Sopenharmony_ci */
8298c2ecf20Sopenharmony_cistatic int get_link_status_x(struct cphy *phy, int *link_ok, int *speed,
8308c2ecf20Sopenharmony_ci			     int *duplex, int *fc)
8318c2ecf20Sopenharmony_ci{
8328c2ecf20Sopenharmony_ci	if (link_ok) {
8338c2ecf20Sopenharmony_ci		unsigned int stat0, stat1, stat2;
8348c2ecf20Sopenharmony_ci		int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD,
8358c2ecf20Sopenharmony_ci				       MDIO_PMA_RXDET, &stat0);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci		if (!err)
8388c2ecf20Sopenharmony_ci			err = t3_mdio_read(phy, MDIO_MMD_PCS,
8398c2ecf20Sopenharmony_ci					   MDIO_PCS_10GBX_STAT1, &stat1);
8408c2ecf20Sopenharmony_ci		if (!err)
8418c2ecf20Sopenharmony_ci			err = t3_mdio_read(phy, MDIO_MMD_PHYXS,
8428c2ecf20Sopenharmony_ci					   MDIO_PHYXS_LNSTAT, &stat2);
8438c2ecf20Sopenharmony_ci		if (err)
8448c2ecf20Sopenharmony_ci			return err;
8458c2ecf20Sopenharmony_ci		*link_ok = (stat0 & (stat1 >> 12) & (stat2 >> 12)) & 1;
8468c2ecf20Sopenharmony_ci	}
8478c2ecf20Sopenharmony_ci	if (speed)
8488c2ecf20Sopenharmony_ci		*speed = SPEED_10000;
8498c2ecf20Sopenharmony_ci	if (duplex)
8508c2ecf20Sopenharmony_ci		*duplex = DUPLEX_FULL;
8518c2ecf20Sopenharmony_ci	return 0;
8528c2ecf20Sopenharmony_ci}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_cistatic const struct cphy_ops qt2045_ops = {
8558c2ecf20Sopenharmony_ci	.reset = ael1006_reset,
8568c2ecf20Sopenharmony_ci	.intr_enable = t3_phy_lasi_intr_enable,
8578c2ecf20Sopenharmony_ci	.intr_disable = t3_phy_lasi_intr_disable,
8588c2ecf20Sopenharmony_ci	.intr_clear = t3_phy_lasi_intr_clear,
8598c2ecf20Sopenharmony_ci	.intr_handler = t3_phy_lasi_intr_handler,
8608c2ecf20Sopenharmony_ci	.get_link_status = get_link_status_x,
8618c2ecf20Sopenharmony_ci	.power_down = ael1002_power_down,
8628c2ecf20Sopenharmony_ci	.mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,
8638c2ecf20Sopenharmony_ci};
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ciint t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter,
8668c2ecf20Sopenharmony_ci		       int phy_addr, const struct mdio_ops *mdio_ops)
8678c2ecf20Sopenharmony_ci{
8688c2ecf20Sopenharmony_ci	unsigned int stat;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops,
8718c2ecf20Sopenharmony_ci		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP,
8728c2ecf20Sopenharmony_ci		  "10GBASE-CX4");
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	/*
8758c2ecf20Sopenharmony_ci	 * Some cards where the PHY is supposed to be at address 0 actually
8768c2ecf20Sopenharmony_ci	 * have it at 1.
8778c2ecf20Sopenharmony_ci	 */
8788c2ecf20Sopenharmony_ci	if (!phy_addr &&
8798c2ecf20Sopenharmony_ci	    !t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_STAT1, &stat) &&
8808c2ecf20Sopenharmony_ci	    stat == 0xffff)
8818c2ecf20Sopenharmony_ci		phy->mdio.prtad = 1;
8828c2ecf20Sopenharmony_ci	return 0;
8838c2ecf20Sopenharmony_ci}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_cistatic int xaui_direct_reset(struct cphy *phy, int wait)
8868c2ecf20Sopenharmony_ci{
8878c2ecf20Sopenharmony_ci	return 0;
8888c2ecf20Sopenharmony_ci}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_cistatic int xaui_direct_get_link_status(struct cphy *phy, int *link_ok,
8918c2ecf20Sopenharmony_ci				       int *speed, int *duplex, int *fc)
8928c2ecf20Sopenharmony_ci{
8938c2ecf20Sopenharmony_ci	if (link_ok) {
8948c2ecf20Sopenharmony_ci		unsigned int status;
8958c2ecf20Sopenharmony_ci		int prtad = phy->mdio.prtad;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci		status = t3_read_reg(phy->adapter,
8988c2ecf20Sopenharmony_ci				     XGM_REG(A_XGM_SERDES_STAT0, prtad)) |
8998c2ecf20Sopenharmony_ci		    t3_read_reg(phy->adapter,
9008c2ecf20Sopenharmony_ci				    XGM_REG(A_XGM_SERDES_STAT1, prtad)) |
9018c2ecf20Sopenharmony_ci		    t3_read_reg(phy->adapter,
9028c2ecf20Sopenharmony_ci				XGM_REG(A_XGM_SERDES_STAT2, prtad)) |
9038c2ecf20Sopenharmony_ci		    t3_read_reg(phy->adapter,
9048c2ecf20Sopenharmony_ci				XGM_REG(A_XGM_SERDES_STAT3, prtad));
9058c2ecf20Sopenharmony_ci		*link_ok = !(status & F_LOWSIG0);
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci	if (speed)
9088c2ecf20Sopenharmony_ci		*speed = SPEED_10000;
9098c2ecf20Sopenharmony_ci	if (duplex)
9108c2ecf20Sopenharmony_ci		*duplex = DUPLEX_FULL;
9118c2ecf20Sopenharmony_ci	return 0;
9128c2ecf20Sopenharmony_ci}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_cistatic int xaui_direct_power_down(struct cphy *phy, int enable)
9158c2ecf20Sopenharmony_ci{
9168c2ecf20Sopenharmony_ci	return 0;
9178c2ecf20Sopenharmony_ci}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_cistatic const struct cphy_ops xaui_direct_ops = {
9208c2ecf20Sopenharmony_ci	.reset = xaui_direct_reset,
9218c2ecf20Sopenharmony_ci	.intr_enable = ael1002_intr_noop,
9228c2ecf20Sopenharmony_ci	.intr_disable = ael1002_intr_noop,
9238c2ecf20Sopenharmony_ci	.intr_clear = ael1002_intr_noop,
9248c2ecf20Sopenharmony_ci	.intr_handler = ael1002_intr_noop,
9258c2ecf20Sopenharmony_ci	.get_link_status = xaui_direct_get_link_status,
9268c2ecf20Sopenharmony_ci	.power_down = xaui_direct_power_down,
9278c2ecf20Sopenharmony_ci};
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ciint t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter,
9308c2ecf20Sopenharmony_ci			    int phy_addr, const struct mdio_ops *mdio_ops)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	cphy_init(phy, adapter, phy_addr, &xaui_direct_ops, mdio_ops,
9338c2ecf20Sopenharmony_ci		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP,
9348c2ecf20Sopenharmony_ci		  "10GBASE-CX4");
9358c2ecf20Sopenharmony_ci	return 0;
9368c2ecf20Sopenharmony_ci}
937