brintos

brintos / linux-shallow public Read only

0
0
Text · 23.2 KiB · 9d591f0 Raw
937 lines · c
1/*2 * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved.3 *4 * This software is available to you under a choice of one of two5 * licenses.  You may choose to be licensed under the terms of the GNU6 * General Public License (GPL) Version 2, available from the file7 * COPYING in the main directory of this source tree, or the8 * OpenIB.org BSD license below:9 *10 *     Redistribution and use in source and binary forms, with or11 *     without modification, are permitted provided that the following12 *     conditions are met:13 *14 *      - Redistributions of source code must retain the above15 *        copyright notice, this list of conditions and the following16 *        disclaimer.17 *18 *      - Redistributions in binary form must reproduce the above19 *        copyright notice, this list of conditions and the following20 *        disclaimer in the documentation and/or other materials21 *        provided with the distribution.22 *23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE30 * SOFTWARE.31 */32#include "common.h"33#include "regs.h"34 35enum {36	AEL100X_TX_CONFIG1 = 0xc002,37	AEL1002_PWR_DOWN_HI = 0xc011,38	AEL1002_PWR_DOWN_LO = 0xc012,39	AEL1002_XFI_EQL = 0xc015,40	AEL1002_LB_EN = 0xc017,41	AEL_OPT_SETTINGS = 0xc017,42	AEL_I2C_CTRL = 0xc30a,43	AEL_I2C_DATA = 0xc30b,44	AEL_I2C_STAT = 0xc30c,45	AEL2005_GPIO_CTRL = 0xc214,46	AEL2005_GPIO_STAT = 0xc215,47 48	AEL2020_GPIO_INTR   = 0xc103,	/* Latch High (LH) */49	AEL2020_GPIO_CTRL   = 0xc108,	/* Store Clear (SC) */50	AEL2020_GPIO_STAT   = 0xc10c,	/* Read Only (RO) */51	AEL2020_GPIO_CFG    = 0xc110,	/* Read Write (RW) */52 53	AEL2020_GPIO_SDA    = 0,	/* IN: i2c serial data */54	AEL2020_GPIO_MODDET = 1,	/* IN: Module Detect */55	AEL2020_GPIO_0      = 3,	/* IN: unassigned */56	AEL2020_GPIO_1      = 2,	/* OUT: unassigned */57	AEL2020_GPIO_LSTAT  = AEL2020_GPIO_1, /* wired to link status LED */58};59 60enum { edc_none, edc_sr, edc_twinax };61 62/* PHY module I2C device address */63enum {64	MODULE_DEV_ADDR	= 0xa0,65	SFF_DEV_ADDR	= 0xa2,66};67 68/* PHY transceiver type */69enum {70	phy_transtype_unknown = 0,71	phy_transtype_sfp     = 3,72	phy_transtype_xfp     = 6,73};74 75#define AEL2005_MODDET_IRQ 476 77struct reg_val {78	unsigned short mmd_addr;79	unsigned short reg_addr;80	unsigned short clear_bits;81	unsigned short set_bits;82};83 84static int set_phy_regs(struct cphy *phy, const struct reg_val *rv)85{86	int err;87 88	for (err = 0; rv->mmd_addr && !err; rv++) {89		if (rv->clear_bits == 0xffff)90			err = t3_mdio_write(phy, rv->mmd_addr, rv->reg_addr,91					    rv->set_bits);92		else93			err = t3_mdio_change_bits(phy, rv->mmd_addr,94						  rv->reg_addr, rv->clear_bits,95						  rv->set_bits);96	}97	return err;98}99 100static void ael100x_txon(struct cphy *phy)101{102	int tx_on_gpio =103		phy->mdio.prtad == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL;104 105	msleep(100);106	t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio);107	msleep(30);108}109 110/*111 * Read an 8-bit word from a device attached to the PHY's i2c bus.112 */113static int ael_i2c_rd(struct cphy *phy, int dev_addr, int word_addr)114{115	int i, err;116	unsigned int stat, data;117 118	err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL_I2C_CTRL,119			    (dev_addr << 8) | (1 << 8) | word_addr);120	if (err)121		return err;122 123	for (i = 0; i < 200; i++) {124		msleep(1);125		err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL_I2C_STAT, &stat);126		if (err)127			return err;128		if ((stat & 3) == 1) {129			err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL_I2C_DATA,130					   &data);131			if (err)132				return err;133			return data >> 8;134		}135	}136	CH_WARN(phy->adapter, "PHY %u i2c read of dev.addr %#x.%#x timed out\n",137		phy->mdio.prtad, dev_addr, word_addr);138	return -ETIMEDOUT;139}140 141static int ael1002_power_down(struct cphy *phy, int enable)142{143	int err;144 145	err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, !!enable);146	if (!err)147		err = mdio_set_flag(&phy->mdio, phy->mdio.prtad,148				    MDIO_MMD_PMAPMD, MDIO_CTRL1,149				    MDIO_CTRL1_LPOWER, enable);150	return err;151}152 153static int ael1002_reset(struct cphy *phy, int wait)154{155	int err;156 157	if ((err = ael1002_power_down(phy, 0)) ||158	    (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL100X_TX_CONFIG1, 1)) ||159	    (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_PWR_DOWN_HI, 0)) ||160	    (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_PWR_DOWN_LO, 0)) ||161	    (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_XFI_EQL, 0x18)) ||162	    (err = t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, AEL1002_LB_EN,163				       0, 1 << 5)))164		return err;165	return 0;166}167 168static int ael1002_intr_noop(struct cphy *phy)169{170	return 0;171}172 173/*174 * Get link status for a 10GBASE-R device.175 */176static int get_link_status_r(struct cphy *phy, int *link_ok, int *speed,177			     int *duplex, int *fc)178{179	if (link_ok) {180		unsigned int stat0, stat1, stat2;181		int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD,182				       MDIO_PMA_RXDET, &stat0);183 184		if (!err)185			err = t3_mdio_read(phy, MDIO_MMD_PCS,186					   MDIO_PCS_10GBRT_STAT1, &stat1);187		if (!err)188			err = t3_mdio_read(phy, MDIO_MMD_PHYXS,189					   MDIO_PHYXS_LNSTAT, &stat2);190		if (err)191			return err;192		*link_ok = (stat0 & stat1 & (stat2 >> 12)) & 1;193	}194	if (speed)195		*speed = SPEED_10000;196	if (duplex)197		*duplex = DUPLEX_FULL;198	return 0;199}200 201static const struct cphy_ops ael1002_ops = {202	.reset = ael1002_reset,203	.intr_enable = ael1002_intr_noop,204	.intr_disable = ael1002_intr_noop,205	.intr_clear = ael1002_intr_noop,206	.intr_handler = ael1002_intr_noop,207	.get_link_status = get_link_status_r,208	.power_down = ael1002_power_down,209	.mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,210};211 212int t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter,213			int phy_addr, const struct mdio_ops *mdio_ops)214{215	cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops,216		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE,217		   "10GBASE-R");218	ael100x_txon(phy);219	return 0;220}221 222static int ael1006_reset(struct cphy *phy, int wait)223{224	return t3_phy_reset(phy, MDIO_MMD_PMAPMD, wait);225}226 227static const struct cphy_ops ael1006_ops = {228	.reset = ael1006_reset,229	.intr_enable = t3_phy_lasi_intr_enable,230	.intr_disable = t3_phy_lasi_intr_disable,231	.intr_clear = t3_phy_lasi_intr_clear,232	.intr_handler = t3_phy_lasi_intr_handler,233	.get_link_status = get_link_status_r,234	.power_down = ael1002_power_down,235	.mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,236};237 238int t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter,239			     int phy_addr, const struct mdio_ops *mdio_ops)240{241	cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops,242		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE,243		   "10GBASE-SR");244	ael100x_txon(phy);245	return 0;246}247 248/*249 * Decode our module type.250 */251static int ael2xxx_get_module_type(struct cphy *phy, int delay_ms)252{253	int v;254 255	if (delay_ms)256		msleep(delay_ms);257 258	/* see SFF-8472 for below */259	v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 3);260	if (v < 0)261		return v;262 263	if (v == 0x10)264		return phy_modtype_sr;265	if (v == 0x20)266		return phy_modtype_lr;267	if (v == 0x40)268		return phy_modtype_lrm;269 270	v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 6);271	if (v < 0)272		return v;273	if (v != 4)274		goto unknown;275 276	v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 10);277	if (v < 0)278		return v;279 280	if (v & 0x80) {281		v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 0x12);282		if (v < 0)283			return v;284		return v > 10 ? phy_modtype_twinax_long : phy_modtype_twinax;285	}286unknown:287	return phy_modtype_unknown;288}289 290/*291 * Code to support the Aeluros/NetLogic 2005 10Gb PHY.292 */293static int ael2005_setup_sr_edc(struct cphy *phy)294{295	static const struct reg_val regs[] = {296		{ MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x181 },297		{ MDIO_MMD_PMAPMD, 0xc010, 0xffff, 0x448a },298		{ MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5200 },299		{ 0, 0, 0, 0 }300	};301 302	int i, err;303 304	err = set_phy_regs(phy, regs);305	if (err)306		return err;307 308	msleep(50);309 310	if (phy->priv != edc_sr)311		err = t3_get_edc_fw(phy, EDC_OPT_AEL2005,312				    EDC_OPT_AEL2005_SIZE);313	if (err)314		return err;315 316	for (i = 0; i <  EDC_OPT_AEL2005_SIZE / sizeof(u16) && !err; i += 2)317		err = t3_mdio_write(phy, MDIO_MMD_PMAPMD,318				    phy->phy_cache[i],319				    phy->phy_cache[i + 1]);320	if (!err)321		phy->priv = edc_sr;322	return err;323}324 325static int ael2005_setup_twinax_edc(struct cphy *phy, int modtype)326{327	static const struct reg_val regs[] = {328		{ MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5a00 },329		{ 0, 0, 0, 0 }330	};331	static const struct reg_val preemphasis[] = {332		{ MDIO_MMD_PMAPMD, 0xc014, 0xffff, 0xfe16 },333		{ MDIO_MMD_PMAPMD, 0xc015, 0xffff, 0xa000 },334		{ 0, 0, 0, 0 }335	};336	int i, err;337 338	err = set_phy_regs(phy, regs);339	if (!err && modtype == phy_modtype_twinax_long)340		err = set_phy_regs(phy, preemphasis);341	if (err)342		return err;343 344	msleep(50);345 346	if (phy->priv != edc_twinax)347		err = t3_get_edc_fw(phy, EDC_TWX_AEL2005,348				    EDC_TWX_AEL2005_SIZE);349	if (err)350		return err;351 352	for (i = 0; i <  EDC_TWX_AEL2005_SIZE / sizeof(u16) && !err; i += 2)353		err = t3_mdio_write(phy, MDIO_MMD_PMAPMD,354				    phy->phy_cache[i],355				    phy->phy_cache[i + 1]);356	if (!err)357		phy->priv = edc_twinax;358	return err;359}360 361static int ael2005_get_module_type(struct cphy *phy, int delay_ms)362{363	int v;364	unsigned int stat;365 366	v = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, &stat);367	if (v)368		return v;369 370	if (stat & (1 << 8))			/* module absent */371		return phy_modtype_none;372 373	return ael2xxx_get_module_type(phy, delay_ms);374}375 376static int ael2005_intr_enable(struct cphy *phy)377{378	int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0x200);379	return err ? err : t3_phy_lasi_intr_enable(phy);380}381 382static int ael2005_intr_disable(struct cphy *phy)383{384	int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0x100);385	return err ? err : t3_phy_lasi_intr_disable(phy);386}387 388static int ael2005_intr_clear(struct cphy *phy)389{390	int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0xd00);391	return err ? err : t3_phy_lasi_intr_clear(phy);392}393 394static int ael2005_reset(struct cphy *phy, int wait)395{396	static const struct reg_val regs0[] = {397		{ MDIO_MMD_PMAPMD, 0xc001, 0, 1 << 5 },398		{ MDIO_MMD_PMAPMD, 0xc017, 0, 1 << 5 },399		{ MDIO_MMD_PMAPMD, 0xc013, 0xffff, 0xf341 },400		{ MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8000 },401		{ MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8100 },402		{ MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8000 },403		{ MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0 },404		{ 0, 0, 0, 0 }405	};406	static const struct reg_val regs1[] = {407		{ MDIO_MMD_PMAPMD, 0xca00, 0xffff, 0x0080 },408		{ MDIO_MMD_PMAPMD, 0xca12, 0xffff, 0 },409		{ 0, 0, 0, 0 }410	};411 412	int err;413	unsigned int lasi_ctrl;414 415	err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL,416			   &lasi_ctrl);417	if (err)418		return err;419 420	err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, 0);421	if (err)422		return err;423 424	msleep(125);425	phy->priv = edc_none;426	err = set_phy_regs(phy, regs0);427	if (err)428		return err;429 430	msleep(50);431 432	err = ael2005_get_module_type(phy, 0);433	if (err < 0)434		return err;435	phy->modtype = err;436 437	if (err == phy_modtype_twinax || err == phy_modtype_twinax_long)438		err = ael2005_setup_twinax_edc(phy, err);439	else440		err = ael2005_setup_sr_edc(phy);441	if (err)442		return err;443 444	err = set_phy_regs(phy, regs1);445	if (err)446		return err;447 448	/* reset wipes out interrupts, reenable them if they were on */449	if (lasi_ctrl & 1)450		err = ael2005_intr_enable(phy);451	return err;452}453 454static int ael2005_intr_handler(struct cphy *phy)455{456	unsigned int stat;457	int ret, edc_needed, cause = 0;458 459	ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_STAT, &stat);460	if (ret)461		return ret;462 463	if (stat & AEL2005_MODDET_IRQ) {464		ret = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL,465				    0xd00);466		if (ret)467			return ret;468 469		/* modules have max 300 ms init time after hot plug */470		ret = ael2005_get_module_type(phy, 300);471		if (ret < 0)472			return ret;473 474		phy->modtype = ret;475		if (ret == phy_modtype_none)476			edc_needed = phy->priv;       /* on unplug retain EDC */477		else if (ret == phy_modtype_twinax ||478			 ret == phy_modtype_twinax_long)479			edc_needed = edc_twinax;480		else481			edc_needed = edc_sr;482 483		if (edc_needed != phy->priv) {484			ret = ael2005_reset(phy, 0);485			return ret ? ret : cphy_cause_module_change;486		}487		cause = cphy_cause_module_change;488	}489 490	ret = t3_phy_lasi_intr_handler(phy);491	if (ret < 0)492		return ret;493 494	ret |= cause;495	return ret ? ret : cphy_cause_link_change;496}497 498static const struct cphy_ops ael2005_ops = {499	.reset           = ael2005_reset,500	.intr_enable     = ael2005_intr_enable,501	.intr_disable    = ael2005_intr_disable,502	.intr_clear      = ael2005_intr_clear,503	.intr_handler    = ael2005_intr_handler,504	.get_link_status = get_link_status_r,505	.power_down      = ael1002_power_down,506	.mmds            = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,507};508 509int t3_ael2005_phy_prep(struct cphy *phy, struct adapter *adapter,510			int phy_addr, const struct mdio_ops *mdio_ops)511{512	cphy_init(phy, adapter, phy_addr, &ael2005_ops, mdio_ops,513		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE |514		  SUPPORTED_IRQ, "10GBASE-R");515	msleep(125);516	return t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, AEL_OPT_SETTINGS, 0,517				   1 << 5);518}519 520/*521 * Setup EDC and other parameters for operation with an optical module.522 */523static int ael2020_setup_sr_edc(struct cphy *phy)524{525	static const struct reg_val regs[] = {526		/* set CDR offset to 10 */527		{ MDIO_MMD_PMAPMD, 0xcc01, 0xffff, 0x488a },528 529		/* adjust 10G RX bias current */530		{ MDIO_MMD_PMAPMD, 0xcb1b, 0xffff, 0x0200 },531		{ MDIO_MMD_PMAPMD, 0xcb1c, 0xffff, 0x00f0 },532		{ MDIO_MMD_PMAPMD, 0xcc06, 0xffff, 0x00e0 },533 534		/* end */535		{ 0, 0, 0, 0 }536	};537	int err;538 539	err = set_phy_regs(phy, regs);540	msleep(50);541	if (err)542		return err;543 544	phy->priv = edc_sr;545	return 0;546}547 548/*549 * Setup EDC and other parameters for operation with an TWINAX module.550 */551static int ael2020_setup_twinax_edc(struct cphy *phy, int modtype)552{553	/* set uC to 40MHz */554	static const struct reg_val uCclock40MHz[] = {555		{ MDIO_MMD_PMAPMD, 0xff28, 0xffff, 0x4001 },556		{ MDIO_MMD_PMAPMD, 0xff2a, 0xffff, 0x0002 },557		{ 0, 0, 0, 0 }558	};559 560	/* activate uC clock */561	static const struct reg_val uCclockActivate[] = {562		{ MDIO_MMD_PMAPMD, 0xd000, 0xffff, 0x5200 },563		{ 0, 0, 0, 0 }564	};565 566	/* set PC to start of SRAM and activate uC */567	static const struct reg_val uCactivate[] = {568		{ MDIO_MMD_PMAPMD, 0xd080, 0xffff, 0x0100 },569		{ MDIO_MMD_PMAPMD, 0xd092, 0xffff, 0x0000 },570		{ 0, 0, 0, 0 }571	};572	int i, err;573 574	/* set uC clock and activate it */575	err = set_phy_regs(phy, uCclock40MHz);576	msleep(500);577	if (err)578		return err;579	err = set_phy_regs(phy, uCclockActivate);580	msleep(500);581	if (err)582		return err;583 584	if (phy->priv != edc_twinax)585		err = t3_get_edc_fw(phy, EDC_TWX_AEL2020,586				    EDC_TWX_AEL2020_SIZE);587	if (err)588		return err;589 590	for (i = 0; i <  EDC_TWX_AEL2020_SIZE / sizeof(u16) && !err; i += 2)591		err = t3_mdio_write(phy, MDIO_MMD_PMAPMD,592				    phy->phy_cache[i],593				    phy->phy_cache[i + 1]);594	/* activate uC */595	err = set_phy_regs(phy, uCactivate);596	if (!err)597		phy->priv = edc_twinax;598	return err;599}600 601/*602 * Return Module Type.603 */604static int ael2020_get_module_type(struct cphy *phy, int delay_ms)605{606	int v;607	unsigned int stat;608 609	v = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_STAT, &stat);610	if (v)611		return v;612 613	if (stat & (0x1 << (AEL2020_GPIO_MODDET*4))) {614		/* module absent */615		return phy_modtype_none;616	}617 618	return ael2xxx_get_module_type(phy, delay_ms);619}620 621/*622 * Enable PHY interrupts.  We enable "Module Detection" interrupts (on any623 * state transition) and then generic Link Alarm Status Interrupt (LASI).624 */625static int ael2020_intr_enable(struct cphy *phy)626{627	static const struct reg_val regs[] = {628		/* output Module's Loss Of Signal (LOS) to LED */629		{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CFG+AEL2020_GPIO_LSTAT,630			0xffff, 0x4 },631		{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,632			0xffff, 0x8 << (AEL2020_GPIO_LSTAT*4) },633 634		 /* enable module detect status change interrupts */635		{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,636			0xffff, 0x2 << (AEL2020_GPIO_MODDET*4) },637 638		/* end */639		{ 0, 0, 0, 0 }640	};641	int err, link_ok = 0;642 643	/* set up "link status" LED and enable module change interrupts */644	err = set_phy_regs(phy, regs);645	if (err)646		return err;647 648	err = get_link_status_r(phy, &link_ok, NULL, NULL, NULL);649	if (err)650		return err;651	if (link_ok)652		t3_link_changed(phy->adapter,653				phy2portid(phy));654 655	err = t3_phy_lasi_intr_enable(phy);656	if (err)657		return err;658 659	return 0;660}661 662/*663 * Disable PHY interrupts.  The mirror of the above ...664 */665static int ael2020_intr_disable(struct cphy *phy)666{667	static const struct reg_val regs[] = {668		/* reset "link status" LED to "off" */669		{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,670			0xffff, 0xb << (AEL2020_GPIO_LSTAT*4) },671 672		/* disable module detect status change interrupts */673		{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,674			0xffff, 0x1 << (AEL2020_GPIO_MODDET*4) },675 676		/* end */677		{ 0, 0, 0, 0 }678	};679	int err;680 681	/* turn off "link status" LED and disable module change interrupts */682	err = set_phy_regs(phy, regs);683	if (err)684		return err;685 686	return t3_phy_lasi_intr_disable(phy);687}688 689/*690 * Clear PHY interrupt state.691 */692static int ael2020_intr_clear(struct cphy *phy)693{694	/*695	 * The GPIO Interrupt register on the AEL2020 is a "Latching High"696	 * (LH) register which is cleared to the current state when it's read.697	 * Thus, we simply read the register and discard the result.698	 */699	unsigned int stat;700	int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_INTR, &stat);701	return err ? err : t3_phy_lasi_intr_clear(phy);702}703 704static const struct reg_val ael2020_reset_regs[] = {705	/* Erratum #2: CDRLOL asserted, causing PMA link down status */706	{ MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x3101 },707 708	/* force XAUI to send LF when RX_LOS is asserted */709	{ MDIO_MMD_PMAPMD, 0xcd40, 0xffff, 0x0001 },710 711	/* allow writes to transceiver module EEPROM on i2c bus */712	{ MDIO_MMD_PMAPMD, 0xff02, 0xffff, 0x0023 },713	{ MDIO_MMD_PMAPMD, 0xff03, 0xffff, 0x0000 },714	{ MDIO_MMD_PMAPMD, 0xff04, 0xffff, 0x0000 },715 716	/* end */717	{ 0, 0, 0, 0 }718};719/*720 * Reset the PHY and put it into a canonical operating state.721 */722static int ael2020_reset(struct cphy *phy, int wait)723{724	int err;725	unsigned int lasi_ctrl;726 727	/* grab current interrupt state */728	err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL,729			   &lasi_ctrl);730	if (err)731		return err;732 733	err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, 125);734	if (err)735		return err;736	msleep(100);737 738	/* basic initialization for all module types */739	phy->priv = edc_none;740	err = set_phy_regs(phy, ael2020_reset_regs);741	if (err)742		return err;743 744	/* determine module type and perform appropriate initialization */745	err = ael2020_get_module_type(phy, 0);746	if (err < 0)747		return err;748	phy->modtype = (u8)err;749	if (err == phy_modtype_twinax || err == phy_modtype_twinax_long)750		err = ael2020_setup_twinax_edc(phy, err);751	else752		err = ael2020_setup_sr_edc(phy);753	if (err)754		return err;755 756	/* reset wipes out interrupts, reenable them if they were on */757	if (lasi_ctrl & 1)758		err = ael2005_intr_enable(phy);759	return err;760}761 762/*763 * Handle a PHY interrupt.764 */765static int ael2020_intr_handler(struct cphy *phy)766{767	unsigned int stat;768	int ret, edc_needed, cause = 0;769 770	ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_INTR, &stat);771	if (ret)772		return ret;773 774	if (stat & (0x1 << AEL2020_GPIO_MODDET)) {775		/* modules have max 300 ms init time after hot plug */776		ret = ael2020_get_module_type(phy, 300);777		if (ret < 0)778			return ret;779 780		phy->modtype = (u8)ret;781		if (ret == phy_modtype_none)782			edc_needed = phy->priv;       /* on unplug retain EDC */783		else if (ret == phy_modtype_twinax ||784			 ret == phy_modtype_twinax_long)785			edc_needed = edc_twinax;786		else787			edc_needed = edc_sr;788 789		if (edc_needed != phy->priv) {790			ret = ael2020_reset(phy, 0);791			return ret ? ret : cphy_cause_module_change;792		}793		cause = cphy_cause_module_change;794	}795 796	ret = t3_phy_lasi_intr_handler(phy);797	if (ret < 0)798		return ret;799 800	ret |= cause;801	return ret ? ret : cphy_cause_link_change;802}803 804static const struct cphy_ops ael2020_ops = {805	.reset           = ael2020_reset,806	.intr_enable     = ael2020_intr_enable,807	.intr_disable    = ael2020_intr_disable,808	.intr_clear      = ael2020_intr_clear,809	.intr_handler    = ael2020_intr_handler,810	.get_link_status = get_link_status_r,811	.power_down      = ael1002_power_down,812	.mmds		 = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,813};814 815int t3_ael2020_phy_prep(struct cphy *phy, struct adapter *adapter, int phy_addr,816			const struct mdio_ops *mdio_ops)817{818	cphy_init(phy, adapter, phy_addr, &ael2020_ops, mdio_ops,819		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE |820		  SUPPORTED_IRQ, "10GBASE-R");821	msleep(125);822 823	return set_phy_regs(phy, ael2020_reset_regs);824}825 826/*827 * Get link status for a 10GBASE-X device.828 */829static int get_link_status_x(struct cphy *phy, int *link_ok, int *speed,830			     int *duplex, int *fc)831{832	if (link_ok) {833		unsigned int stat0, stat1, stat2;834		int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD,835				       MDIO_PMA_RXDET, &stat0);836 837		if (!err)838			err = t3_mdio_read(phy, MDIO_MMD_PCS,839					   MDIO_PCS_10GBX_STAT1, &stat1);840		if (!err)841			err = t3_mdio_read(phy, MDIO_MMD_PHYXS,842					   MDIO_PHYXS_LNSTAT, &stat2);843		if (err)844			return err;845		*link_ok = (stat0 & (stat1 >> 12) & (stat2 >> 12)) & 1;846	}847	if (speed)848		*speed = SPEED_10000;849	if (duplex)850		*duplex = DUPLEX_FULL;851	return 0;852}853 854static const struct cphy_ops qt2045_ops = {855	.reset = ael1006_reset,856	.intr_enable = t3_phy_lasi_intr_enable,857	.intr_disable = t3_phy_lasi_intr_disable,858	.intr_clear = t3_phy_lasi_intr_clear,859	.intr_handler = t3_phy_lasi_intr_handler,860	.get_link_status = get_link_status_x,861	.power_down = ael1002_power_down,862	.mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,863};864 865int t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter,866		       int phy_addr, const struct mdio_ops *mdio_ops)867{868	unsigned int stat;869 870	cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops,871		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP,872		  "10GBASE-CX4");873 874	/*875	 * Some cards where the PHY is supposed to be at address 0 actually876	 * have it at 1.877	 */878	if (!phy_addr &&879	    !t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_STAT1, &stat) &&880	    stat == 0xffff)881		phy->mdio.prtad = 1;882	return 0;883}884 885static int xaui_direct_reset(struct cphy *phy, int wait)886{887	return 0;888}889 890static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok,891				       int *speed, int *duplex, int *fc)892{893	if (link_ok) {894		unsigned int status;895		int prtad = phy->mdio.prtad;896 897		status = t3_read_reg(phy->adapter,898				     XGM_REG(A_XGM_SERDES_STAT0, prtad)) |899		    t3_read_reg(phy->adapter,900				    XGM_REG(A_XGM_SERDES_STAT1, prtad)) |901		    t3_read_reg(phy->adapter,902				XGM_REG(A_XGM_SERDES_STAT2, prtad)) |903		    t3_read_reg(phy->adapter,904				XGM_REG(A_XGM_SERDES_STAT3, prtad));905		*link_ok = !(status & F_LOWSIG0);906	}907	if (speed)908		*speed = SPEED_10000;909	if (duplex)910		*duplex = DUPLEX_FULL;911	return 0;912}913 914static int xaui_direct_power_down(struct cphy *phy, int enable)915{916	return 0;917}918 919static const struct cphy_ops xaui_direct_ops = {920	.reset = xaui_direct_reset,921	.intr_enable = ael1002_intr_noop,922	.intr_disable = ael1002_intr_noop,923	.intr_clear = ael1002_intr_noop,924	.intr_handler = ael1002_intr_noop,925	.get_link_status = xaui_direct_get_link_status,926	.power_down = xaui_direct_power_down,927};928 929int t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter,930			    int phy_addr, const struct mdio_ops *mdio_ops)931{932	cphy_init(phy, adapter, phy_addr, &xaui_direct_ops, mdio_ops,933		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP,934		  "10GBASE-CX4");935	return 0;936}937