ael1002.c 23.2 KB
Newer Older
1
/*
D
Divy Le Ray 已提交
2
 * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved.
3
 *
D
Divy Le Ray 已提交
4 5 6 7 8
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
9
 *
D
Divy Le Ray 已提交
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
31 32 33 34
 */
#include "common.h"
#include "regs.h"

D
Divy Le Ray 已提交
35
enum {
36 37 38 39 40
	AEL100X_TX_CONFIG1 = 0xc002,
	AEL1002_PWR_DOWN_HI = 0xc011,
	AEL1002_PWR_DOWN_LO = 0xc012,
	AEL1002_XFI_EQL = 0xc015,
	AEL1002_LB_EN = 0xc017,
D
Divy Le Ray 已提交
41 42 43 44 45 46
	AEL_OPT_SETTINGS = 0xc017,
	AEL_I2C_CTRL = 0xc30a,
	AEL_I2C_DATA = 0xc30b,
	AEL_I2C_STAT = 0xc30c,
	AEL2005_GPIO_CTRL = 0xc214,
	AEL2005_GPIO_STAT = 0xc215,
47 48 49 50 51 52 53 54 55 56 57

	AEL2020_GPIO_INTR   = 0xc103,	/* Latch High (LH) */
	AEL2020_GPIO_CTRL   = 0xc108,	/* Store Clear (SC) */
	AEL2020_GPIO_STAT   = 0xc10c,	/* Read Only (RO) */
	AEL2020_GPIO_CFG    = 0xc110,	/* Read Write (RW) */

	AEL2020_GPIO_SDA    = 0,	/* IN: i2c serial data */
	AEL2020_GPIO_MODDET = 1,	/* IN: Module Detect */
	AEL2020_GPIO_0      = 3,	/* IN: unassigned */
	AEL2020_GPIO_1      = 2,	/* OUT: unassigned */
	AEL2020_GPIO_LSTAT  = AEL2020_GPIO_1, /* wired to link status LED */
D
Divy Le Ray 已提交
58 59 60 61 62
};

enum { edc_none, edc_sr, edc_twinax };

/* PHY module I2C device address */
63 64 65 66 67 68 69 70 71 72 73
enum {
	MODULE_DEV_ADDR	= 0xa0,
	SFF_DEV_ADDR	= 0xa2,
};

/* PHY transceiver type */
enum {
	phy_transtype_unknown = 0,
	phy_transtype_sfp     = 3,
	phy_transtype_xfp     = 6,
};
D
Divy Le Ray 已提交
74 75 76 77 78 79 80 81

#define AEL2005_MODDET_IRQ 4

struct reg_val {
	unsigned short mmd_addr;
	unsigned short reg_addr;
	unsigned short clear_bits;
	unsigned short set_bits;
82 83
};

D
Divy Le Ray 已提交
84 85 86 87 88 89
static int set_phy_regs(struct cphy *phy, const struct reg_val *rv)
{
	int err;

	for (err = 0; rv->mmd_addr && !err; rv++) {
		if (rv->clear_bits == 0xffff)
90 91
			err = t3_mdio_write(phy, rv->mmd_addr, rv->reg_addr,
					    rv->set_bits);
D
Divy Le Ray 已提交
92 93 94 95 96 97 98 99
		else
			err = t3_mdio_change_bits(phy, rv->mmd_addr,
						  rv->reg_addr, rv->clear_bits,
						  rv->set_bits);
	}
	return err;
}

100 101
static void ael100x_txon(struct cphy *phy)
{
102 103
	int tx_on_gpio =
		phy->mdio.prtad == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL;
104 105 106 107 108 109

	msleep(100);
	t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio);
	msleep(30);
}

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
/*
 * Read an 8-bit word from a device attached to the PHY's i2c bus.
 */
static int ael_i2c_rd(struct cphy *phy, int dev_addr, int word_addr)
{
	int i, err;
	unsigned int stat, data;

	err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL_I2C_CTRL,
			    (dev_addr << 8) | (1 << 8) | word_addr);
	if (err)
		return err;

	for (i = 0; i < 200; i++) {
		msleep(1);
		err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL_I2C_STAT, &stat);
		if (err)
			return err;
		if ((stat & 3) == 1) {
			err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL_I2C_DATA,
					   &data);
			if (err)
				return err;
			return data >> 8;
		}
	}
	CH_WARN(phy->adapter, "PHY %u i2c read of dev.addr %#x.%#x timed out\n",
		phy->mdio.prtad, dev_addr, word_addr);
	return -ETIMEDOUT;
}

141 142 143 144
static int ael1002_power_down(struct cphy *phy, int enable)
{
	int err;

145
	err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, !!enable);
146
	if (!err)
147 148 149
		err = mdio_set_flag(&phy->mdio, phy->mdio.prtad,
				    MDIO_MMD_PMAPMD, MDIO_CTRL1,
				    MDIO_CTRL1_LPOWER, enable);
150 151 152 153 154 155 156 157
	return err;
}

static int ael1002_reset(struct cphy *phy, int wait)
{
	int err;

	if ((err = ael1002_power_down(phy, 0)) ||
158 159 160 161 162
	    (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL100X_TX_CONFIG1, 1)) ||
	    (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_PWR_DOWN_HI, 0)) ||
	    (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_PWR_DOWN_LO, 0)) ||
	    (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_XFI_EQL, 0x18)) ||
	    (err = t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, AEL1002_LB_EN,
163 164 165 166 167 168 169 170 171 172
				       0, 1 << 5)))
		return err;
	return 0;
}

static int ael1002_intr_noop(struct cphy *phy)
{
	return 0;
}

D
Divy Le Ray 已提交
173 174 175 176 177
/*
 * Get link status for a 10GBASE-R device.
 */
static int get_link_status_r(struct cphy *phy, int *link_ok, int *speed,
			     int *duplex, int *fc)
178 179
{
	if (link_ok) {
D
Divy Le Ray 已提交
180
		unsigned int stat0, stat1, stat2;
181 182
		int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD,
				       MDIO_PMA_RXDET, &stat0);
D
Divy Le Ray 已提交
183 184

		if (!err)
185 186
			err = t3_mdio_read(phy, MDIO_MMD_PCS,
					   MDIO_PCS_10GBRT_STAT1, &stat1);
D
Divy Le Ray 已提交
187
		if (!err)
188 189
			err = t3_mdio_read(phy, MDIO_MMD_PHYXS,
					   MDIO_PHYXS_LNSTAT, &stat2);
190 191
		if (err)
			return err;
D
Divy Le Ray 已提交
192
		*link_ok = (stat0 & stat1 & (stat2 >> 12)) & 1;
193 194 195 196 197 198 199 200 201 202 203 204 205 206
	}
	if (speed)
		*speed = SPEED_10000;
	if (duplex)
		*duplex = DUPLEX_FULL;
	return 0;
}

static struct cphy_ops ael1002_ops = {
	.reset = ael1002_reset,
	.intr_enable = ael1002_intr_noop,
	.intr_disable = ael1002_intr_noop,
	.intr_clear = ael1002_intr_noop,
	.intr_handler = ael1002_intr_noop,
D
Divy Le Ray 已提交
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
};

D
Divy Le Ray 已提交
212 213
int t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter,
			int phy_addr, const struct mdio_ops *mdio_ops)
214
{
215 216 217
	cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops,
		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE,
		   "10GBASE-R");
218
	ael100x_txon(phy);
D
Divy Le Ray 已提交
219
	return 0;
220 221 222 223
}

static int ael1006_reset(struct cphy *phy, int wait)
{
224
	return t3_phy_reset(phy, MDIO_MMD_PMAPMD, wait);
225 226 227 228
}

static struct cphy_ops ael1006_ops = {
	.reset = ael1006_reset,
D
Divy Le Ray 已提交
229 230 231 232
	.intr_enable = t3_phy_lasi_intr_enable,
	.intr_disable = t3_phy_lasi_intr_disable,
	.intr_clear = t3_phy_lasi_intr_clear,
	.intr_handler = t3_phy_lasi_intr_handler,
D
Divy Le Ray 已提交
233
	.get_link_status = get_link_status_r,
D
Divy Le Ray 已提交
234
	.power_down = ael1002_power_down,
235
	.mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,
236 237
};

D
Divy Le Ray 已提交
238 239
int t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter,
			     int phy_addr, const struct mdio_ops *mdio_ops)
240
{
241 242 243
	cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops,
		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE,
		   "10GBASE-SR");
244
	ael100x_txon(phy);
D
Divy Le Ray 已提交
245
	return 0;
246 247
}

248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
/*
 * Decode our module type.
 */
static int ael2xxx_get_module_type(struct cphy *phy, int delay_ms)
{
	int v;

	if (delay_ms)
		msleep(delay_ms);

	/* see SFF-8472 for below */
	v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 3);
	if (v < 0)
		return v;

	if (v == 0x10)
		return phy_modtype_sr;
	if (v == 0x20)
		return phy_modtype_lr;
	if (v == 0x40)
		return phy_modtype_lrm;

	v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 6);
	if (v < 0)
		return v;
	if (v != 4)
		goto unknown;

	v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 10);
	if (v < 0)
		return v;

	if (v & 0x80) {
		v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 0x12);
		if (v < 0)
			return v;
		return v > 10 ? phy_modtype_twinax_long : phy_modtype_twinax;
	}
unknown:
	return phy_modtype_unknown;
}

/*
 * Code to support the Aeluros/NetLogic 2005 10Gb PHY.
 */
D
Divy Le Ray 已提交
293 294
static int ael2005_setup_sr_edc(struct cphy *phy)
{
J
Joe Perches 已提交
295
	static const struct reg_val regs[] = {
296 297 298
		{ MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x181 },
		{ MDIO_MMD_PMAPMD, 0xc010, 0xffff, 0x448a },
		{ MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5200 },
D
Divy Le Ray 已提交
299 300
		{ 0, 0, 0, 0 }
	};
301

D
Divy Le Ray 已提交
302 303 304 305 306 307 308 309
	int i, err;

	err = set_phy_regs(phy, regs);
	if (err)
		return err;

	msleep(50);

310 311 312 313 314 315 316 317 318 319
	if (phy->priv != edc_sr)
		err = t3_get_edc_fw(phy, EDC_OPT_AEL2005,
				    EDC_OPT_AEL2005_SIZE);
	if (err)
		return err;

	for (i = 0; i <  EDC_OPT_AEL2005_SIZE / sizeof(u16) && !err; i += 2)
		err = t3_mdio_write(phy, MDIO_MMD_PMAPMD,
				    phy->phy_cache[i],
				    phy->phy_cache[i + 1]);
D
Divy Le Ray 已提交
320 321 322 323 324 325 326
	if (!err)
		phy->priv = edc_sr;
	return err;
}

static int ael2005_setup_twinax_edc(struct cphy *phy, int modtype)
{
J
Joe Perches 已提交
327
	static const struct reg_val regs[] = {
328
		{ MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5a00 },
D
Divy Le Ray 已提交
329 330
		{ 0, 0, 0, 0 }
	};
J
Joe Perches 已提交
331
	static const struct reg_val preemphasis[] = {
332 333
		{ MDIO_MMD_PMAPMD, 0xc014, 0xffff, 0xfe16 },
		{ MDIO_MMD_PMAPMD, 0xc015, 0xffff, 0xa000 },
D
Divy Le Ray 已提交
334 335 336 337 338 339 340 341 342 343 344 345
		{ 0, 0, 0, 0 }
	};
	int i, err;

	err = set_phy_regs(phy, regs);
	if (!err && modtype == phy_modtype_twinax_long)
		err = set_phy_regs(phy, preemphasis);
	if (err)
		return err;

	msleep(50);

346 347 348 349 350 351 352 353 354 355
	if (phy->priv != edc_twinax)
		err = t3_get_edc_fw(phy, EDC_TWX_AEL2005,
				    EDC_TWX_AEL2005_SIZE);
	if (err)
		return err;

	for (i = 0; i <  EDC_TWX_AEL2005_SIZE / sizeof(u16) && !err; i += 2)
		err = t3_mdio_write(phy, MDIO_MMD_PMAPMD,
				    phy->phy_cache[i],
				    phy->phy_cache[i + 1]);
D
Divy Le Ray 已提交
356 357 358 359 360
	if (!err)
		phy->priv = edc_twinax;
	return err;
}

361
static int ael2005_get_module_type(struct cphy *phy, int delay_ms)
D
Divy Le Ray 已提交
362 363 364 365
{
	int v;
	unsigned int stat;

366
	v = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, &stat);
D
Divy Le Ray 已提交
367 368 369 370 371 372
	if (v)
		return v;

	if (stat & (1 << 8))			/* module absent */
		return phy_modtype_none;

373
	return ael2xxx_get_module_type(phy, delay_ms);
D
Divy Le Ray 已提交
374 375 376 377
}

static int ael2005_intr_enable(struct cphy *phy)
{
378
	int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0x200);
D
Divy Le Ray 已提交
379 380 381 382 383
	return err ? err : t3_phy_lasi_intr_enable(phy);
}

static int ael2005_intr_disable(struct cphy *phy)
{
384
	int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0x100);
D
Divy Le Ray 已提交
385 386 387 388 389
	return err ? err : t3_phy_lasi_intr_disable(phy);
}

static int ael2005_intr_clear(struct cphy *phy)
{
390
	int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0xd00);
D
Divy Le Ray 已提交
391 392 393 394 395
	return err ? err : t3_phy_lasi_intr_clear(phy);
}

static int ael2005_reset(struct cphy *phy, int wait)
{
J
Joe Perches 已提交
396
	static const struct reg_val regs0[] = {
397 398 399 400 401 402 403
		{ MDIO_MMD_PMAPMD, 0xc001, 0, 1 << 5 },
		{ MDIO_MMD_PMAPMD, 0xc017, 0, 1 << 5 },
		{ MDIO_MMD_PMAPMD, 0xc013, 0xffff, 0xf341 },
		{ MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8000 },
		{ MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8100 },
		{ MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8000 },
		{ MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0 },
D
Divy Le Ray 已提交
404 405
		{ 0, 0, 0, 0 }
	};
J
Joe Perches 已提交
406
	static const struct reg_val regs1[] = {
407 408
		{ MDIO_MMD_PMAPMD, 0xca00, 0xffff, 0x0080 },
		{ MDIO_MMD_PMAPMD, 0xca12, 0xffff, 0 },
D
Divy Le Ray 已提交
409 410 411
		{ 0, 0, 0, 0 }
	};

412 413
	int err;
	unsigned int lasi_ctrl;
D
Divy Le Ray 已提交
414

415 416
	err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL,
			   &lasi_ctrl);
D
Divy Le Ray 已提交
417 418 419
	if (err)
		return err;

420
	err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, 0);
D
Divy Le Ray 已提交
421 422 423 424 425 426 427 428 429 430 431
	if (err)
		return err;

	msleep(125);
	phy->priv = edc_none;
	err = set_phy_regs(phy, regs0);
	if (err)
		return err;

	msleep(50);

432
	err = ael2005_get_module_type(phy, 0);
D
Divy Le Ray 已提交
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
	if (err < 0)
		return err;
	phy->modtype = err;

	if (err == phy_modtype_twinax || err == phy_modtype_twinax_long)
		err = ael2005_setup_twinax_edc(phy, err);
	else
		err = ael2005_setup_sr_edc(phy);
	if (err)
		return err;

	err = set_phy_regs(phy, regs1);
	if (err)
		return err;

	/* reset wipes out interrupts, reenable them if they were on */
	if (lasi_ctrl & 1)
		err = ael2005_intr_enable(phy);
	return err;
}

static int ael2005_intr_handler(struct cphy *phy)
{
	unsigned int stat;
	int ret, edc_needed, cause = 0;

459
	ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_STAT, &stat);
D
Divy Le Ray 已提交
460 461 462 463
	if (ret)
		return ret;

	if (stat & AEL2005_MODDET_IRQ) {
464 465
		ret = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL,
				    0xd00);
D
Divy Le Ray 已提交
466 467 468 469
		if (ret)
			return ret;

		/* modules have max 300 ms init time after hot plug */
470
		ret = ael2005_get_module_type(phy, 300);
D
Divy Le Ray 已提交
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
		if (ret < 0)
			return ret;

		phy->modtype = ret;
		if (ret == phy_modtype_none)
			edc_needed = phy->priv;       /* on unplug retain EDC */
		else if (ret == phy_modtype_twinax ||
			 ret == phy_modtype_twinax_long)
			edc_needed = edc_twinax;
		else
			edc_needed = edc_sr;

		if (edc_needed != phy->priv) {
			ret = ael2005_reset(phy, 0);
			return ret ? ret : cphy_cause_module_change;
		}
		cause = cphy_cause_module_change;
	}

	ret = t3_phy_lasi_intr_handler(phy);
	if (ret < 0)
		return ret;

	ret |= cause;
	return ret ? ret : cphy_cause_link_change;
}

static struct cphy_ops ael2005_ops = {
	.reset           = ael2005_reset,
	.intr_enable     = ael2005_intr_enable,
	.intr_disable    = ael2005_intr_disable,
	.intr_clear      = ael2005_intr_clear,
	.intr_handler    = ael2005_intr_handler,
	.get_link_status = get_link_status_r,
	.power_down      = ael1002_power_down,
506
	.mmds            = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,
D
Divy Le Ray 已提交
507 508 509 510 511 512 513 514 515
};

int t3_ael2005_phy_prep(struct cphy *phy, struct adapter *adapter,
			int phy_addr, const struct mdio_ops *mdio_ops)
{
	cphy_init(phy, adapter, phy_addr, &ael2005_ops, mdio_ops,
		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE |
		  SUPPORTED_IRQ, "10GBASE-R");
	msleep(125);
516
	return t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, AEL_OPT_SETTINGS, 0,
D
Divy Le Ray 已提交
517 518 519
				   1 << 5);
}

520 521 522 523 524
/*
 * Setup EDC and other parameters for operation with an optical module.
 */
static int ael2020_setup_sr_edc(struct cphy *phy)
{
J
Joe Perches 已提交
525
	static const struct reg_val regs[] = {
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
		/* set CDR offset to 10 */
		{ MDIO_MMD_PMAPMD, 0xcc01, 0xffff, 0x488a },

		/* adjust 10G RX bias current */
		{ MDIO_MMD_PMAPMD, 0xcb1b, 0xffff, 0x0200 },
		{ MDIO_MMD_PMAPMD, 0xcb1c, 0xffff, 0x00f0 },
		{ MDIO_MMD_PMAPMD, 0xcc06, 0xffff, 0x00e0 },

		/* end */
		{ 0, 0, 0, 0 }
	};
	int err;

	err = set_phy_regs(phy, regs);
	msleep(50);
	if (err)
		return err;

	phy->priv = edc_sr;
	return 0;
}

/*
 * Setup EDC and other parameters for operation with an TWINAX module.
 */
static int ael2020_setup_twinax_edc(struct cphy *phy, int modtype)
{
	/* set uC to 40MHz */
J
Joe Perches 已提交
554
	static const struct reg_val uCclock40MHz[] = {
555 556 557 558 559 560
		{ MDIO_MMD_PMAPMD, 0xff28, 0xffff, 0x4001 },
		{ MDIO_MMD_PMAPMD, 0xff2a, 0xffff, 0x0002 },
		{ 0, 0, 0, 0 }
	};

	/* activate uC clock */
J
Joe Perches 已提交
561
	static const struct reg_val uCclockActivate[] = {
562 563 564 565 566
		{ MDIO_MMD_PMAPMD, 0xd000, 0xffff, 0x5200 },
		{ 0, 0, 0, 0 }
	};

	/* set PC to start of SRAM and activate uC */
J
Joe Perches 已提交
567
	static const struct reg_val uCactivate[] = {
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
		{ MDIO_MMD_PMAPMD, 0xd080, 0xffff, 0x0100 },
		{ MDIO_MMD_PMAPMD, 0xd092, 0xffff, 0x0000 },
		{ 0, 0, 0, 0 }
	};
	int i, err;

	/* set uC clock and activate it */
	err = set_phy_regs(phy, uCclock40MHz);
	msleep(500);
	if (err)
		return err;
	err = set_phy_regs(phy, uCclockActivate);
	msleep(500);
	if (err)
		return err;

584 585 586 587 588 589 590 591 592 593
	if (phy->priv != edc_twinax)
		err = t3_get_edc_fw(phy, EDC_TWX_AEL2020,
				    EDC_TWX_AEL2020_SIZE);
	if (err)
		return err;

	for (i = 0; i <  EDC_TWX_AEL2020_SIZE / sizeof(u16) && !err; i += 2)
		err = t3_mdio_write(phy, MDIO_MMD_PMAPMD,
				    phy->phy_cache[i],
				    phy->phy_cache[i + 1]);
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
	/* activate uC */
	err = set_phy_regs(phy, uCactivate);
	if (!err)
		phy->priv = edc_twinax;
	return err;
}

/*
 * Return Module Type.
 */
static int ael2020_get_module_type(struct cphy *phy, int delay_ms)
{
	int v;
	unsigned int stat;

	v = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_STAT, &stat);
	if (v)
		return v;

	if (stat & (0x1 << (AEL2020_GPIO_MODDET*4))) {
		/* module absent */
		return phy_modtype_none;
	}

	return ael2xxx_get_module_type(phy, delay_ms);
}

/*
 * Enable PHY interrupts.  We enable "Module Detection" interrupts (on any
 * state transition) and then generic Link Alarm Status Interrupt (LASI).
 */
static int ael2020_intr_enable(struct cphy *phy)
{
J
Joe Perches 已提交
627
	static const struct reg_val regs[] = {
D
Divy Le Ray 已提交
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
		/* output Module's Loss Of Signal (LOS) to LED */
		{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CFG+AEL2020_GPIO_LSTAT,
			0xffff, 0x4 },
		{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,
			0xffff, 0x8 << (AEL2020_GPIO_LSTAT*4) },

		 /* enable module detect status change interrupts */
		{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,
			0xffff, 0x2 << (AEL2020_GPIO_MODDET*4) },

		/* end */
		{ 0, 0, 0, 0 }
	};
	int err, link_ok = 0;

	/* set up "link status" LED and enable module change interrupts */
	err = set_phy_regs(phy, regs);
	if (err)
		return err;

	err = get_link_status_r(phy, &link_ok, NULL, NULL, NULL);
	if (err)
		return err;
	if (link_ok)
		t3_link_changed(phy->adapter,
				phy2portid(phy));

	err = t3_phy_lasi_intr_enable(phy);
	if (err)
		return err;

	return 0;
660 661 662 663 664 665 666
}

/*
 * Disable PHY interrupts.  The mirror of the above ...
 */
static int ael2020_intr_disable(struct cphy *phy)
{
J
Joe Perches 已提交
667
	static const struct reg_val regs[] = {
D
Divy Le Ray 已提交
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
		/* reset "link status" LED to "off" */
		{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,
			0xffff, 0xb << (AEL2020_GPIO_LSTAT*4) },

		/* disable module detect status change interrupts */
		{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,
			0xffff, 0x1 << (AEL2020_GPIO_MODDET*4) },

		/* end */
		{ 0, 0, 0, 0 }
	};
	int err;

	/* turn off "link status" LED and disable module change interrupts */
	err = set_phy_regs(phy, regs);
	if (err)
		return err;

	return t3_phy_lasi_intr_disable(phy);
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
}

/*
 * Clear PHY interrupt state.
 */
static int ael2020_intr_clear(struct cphy *phy)
{
	/*
	 * The GPIO Interrupt register on the AEL2020 is a "Latching High"
	 * (LH) register which is cleared to the current state when it's read.
	 * Thus, we simply read the register and discard the result.
	 */
	unsigned int stat;
	int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_INTR, &stat);
	return err ? err : t3_phy_lasi_intr_clear(phy);
}

J
Joe Perches 已提交
704
static const struct reg_val ael2020_reset_regs[] = {
D
Divy Le Ray 已提交
705 706 707 708 709 710 711 712 713 714 715 716 717 718
	/* Erratum #2: CDRLOL asserted, causing PMA link down status */
	{ MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x3101 },

	/* force XAUI to send LF when RX_LOS is asserted */
	{ MDIO_MMD_PMAPMD, 0xcd40, 0xffff, 0x0001 },

	/* allow writes to transceiver module EEPROM on i2c bus */
	{ MDIO_MMD_PMAPMD, 0xff02, 0xffff, 0x0023 },
	{ MDIO_MMD_PMAPMD, 0xff03, 0xffff, 0x0000 },
	{ MDIO_MMD_PMAPMD, 0xff04, 0xffff, 0x0000 },

	/* end */
	{ 0, 0, 0, 0 }
};
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
/*
 * Reset the PHY and put it into a canonical operating state.
 */
static int ael2020_reset(struct cphy *phy, int wait)
{
	int err;
	unsigned int lasi_ctrl;

	/* grab current interrupt state */
	err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL,
			   &lasi_ctrl);
	if (err)
		return err;

	err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, 125);
	if (err)
		return err;
	msleep(100);

	/* basic initialization for all module types */
	phy->priv = edc_none;
D
Divy Le Ray 已提交
740
	err = set_phy_regs(phy, ael2020_reset_regs);
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
	if (err)
		return err;

	/* determine module type and perform appropriate initialization */
	err = ael2020_get_module_type(phy, 0);
	if (err < 0)
		return err;
	phy->modtype = (u8)err;
	if (err == phy_modtype_twinax || err == phy_modtype_twinax_long)
		err = ael2020_setup_twinax_edc(phy, err);
	else
		err = ael2020_setup_sr_edc(phy);
	if (err)
		return err;

	/* reset wipes out interrupts, reenable them if they were on */
	if (lasi_ctrl & 1)
		err = ael2005_intr_enable(phy);
	return err;
}

/*
 * Handle a PHY interrupt.
 */
static int ael2020_intr_handler(struct cphy *phy)
{
	unsigned int stat;
	int ret, edc_needed, cause = 0;

	ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_INTR, &stat);
	if (ret)
		return ret;

	if (stat & (0x1 << AEL2020_GPIO_MODDET)) {
		/* modules have max 300 ms init time after hot plug */
		ret = ael2020_get_module_type(phy, 300);
		if (ret < 0)
			return ret;

		phy->modtype = (u8)ret;
		if (ret == phy_modtype_none)
			edc_needed = phy->priv;       /* on unplug retain EDC */
		else if (ret == phy_modtype_twinax ||
			 ret == phy_modtype_twinax_long)
			edc_needed = edc_twinax;
		else
			edc_needed = edc_sr;

		if (edc_needed != phy->priv) {
			ret = ael2020_reset(phy, 0);
			return ret ? ret : cphy_cause_module_change;
		}
		cause = cphy_cause_module_change;
	}

	ret = t3_phy_lasi_intr_handler(phy);
	if (ret < 0)
		return ret;

	ret |= cause;
	return ret ? ret : cphy_cause_link_change;
}

static struct cphy_ops ael2020_ops = {
	.reset           = ael2020_reset,
	.intr_enable     = ael2020_intr_enable,
	.intr_disable    = ael2020_intr_disable,
	.intr_clear      = ael2020_intr_clear,
	.intr_handler    = ael2020_intr_handler,
	.get_link_status = get_link_status_r,
	.power_down      = ael1002_power_down,
	.mmds		 = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,
};

int t3_ael2020_phy_prep(struct cphy *phy, struct adapter *adapter, int phy_addr,
			const struct mdio_ops *mdio_ops)
{
D
Divy Le Ray 已提交
818 819
	int err;

820 821 822 823
	cphy_init(phy, adapter, phy_addr, &ael2020_ops, mdio_ops,
		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE |
		  SUPPORTED_IRQ, "10GBASE-R");
	msleep(125);
D
Divy Le Ray 已提交
824 825 826 827

	err = set_phy_regs(phy, ael2020_reset_regs);
	if (err)
		return err;
828 829 830
	return 0;
}

D
Divy Le Ray 已提交
831 832 833 834 835 836 837 838
/*
 * Get link status for a 10GBASE-X device.
 */
static int get_link_status_x(struct cphy *phy, int *link_ok, int *speed,
			     int *duplex, int *fc)
{
	if (link_ok) {
		unsigned int stat0, stat1, stat2;
839 840
		int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD,
				       MDIO_PMA_RXDET, &stat0);
D
Divy Le Ray 已提交
841 842

		if (!err)
843 844
			err = t3_mdio_read(phy, MDIO_MMD_PCS,
					   MDIO_PCS_10GBX_STAT1, &stat1);
D
Divy Le Ray 已提交
845
		if (!err)
846 847
			err = t3_mdio_read(phy, MDIO_MMD_PHYXS,
					   MDIO_PHYXS_LNSTAT, &stat2);
D
Divy Le Ray 已提交
848 849 850 851 852 853 854 855 856 857 858
		if (err)
			return err;
		*link_ok = (stat0 & (stat1 >> 12) & (stat2 >> 12)) & 1;
	}
	if (speed)
		*speed = SPEED_10000;
	if (duplex)
		*duplex = DUPLEX_FULL;
	return 0;
}

859 860
static struct cphy_ops qt2045_ops = {
	.reset = ael1006_reset,
D
Divy Le Ray 已提交
861 862 863 864
	.intr_enable = t3_phy_lasi_intr_enable,
	.intr_disable = t3_phy_lasi_intr_disable,
	.intr_clear = t3_phy_lasi_intr_clear,
	.intr_handler = t3_phy_lasi_intr_handler,
D
Divy Le Ray 已提交
865
	.get_link_status = get_link_status_x,
D
Divy Le Ray 已提交
866
	.power_down = ael1002_power_down,
867
	.mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,
868 869
};

D
Divy Le Ray 已提交
870 871
int t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter,
		       int phy_addr, const struct mdio_ops *mdio_ops)
872 873 874
{
	unsigned int stat;

875 876 877
	cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops,
		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP,
		  "10GBASE-CX4");
878 879 880 881 882

	/*
	 * Some cards where the PHY is supposed to be at address 0 actually
	 * have it at 1.
	 */
883 884
	if (!phy_addr &&
	    !t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_STAT1, &stat) &&
885
	    stat == 0xffff)
886
		phy->mdio.prtad = 1;
D
Divy Le Ray 已提交
887
	return 0;
888 889 890 891 892 893 894 895 896 897 898 899
}

static int xaui_direct_reset(struct cphy *phy, int wait)
{
	return 0;
}

static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok,
				       int *speed, int *duplex, int *fc)
{
	if (link_ok) {
		unsigned int status;
900
		int prtad = phy->mdio.prtad;
901 902

		status = t3_read_reg(phy->adapter,
903
				     XGM_REG(A_XGM_SERDES_STAT0, prtad)) |
D
Divy Le Ray 已提交
904
		    t3_read_reg(phy->adapter,
905
				    XGM_REG(A_XGM_SERDES_STAT1, prtad)) |
D
Divy Le Ray 已提交
906
		    t3_read_reg(phy->adapter,
907
				XGM_REG(A_XGM_SERDES_STAT2, prtad)) |
D
Divy Le Ray 已提交
908
		    t3_read_reg(phy->adapter,
909
				XGM_REG(A_XGM_SERDES_STAT3, prtad));
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
		*link_ok = !(status & F_LOWSIG0);
	}
	if (speed)
		*speed = SPEED_10000;
	if (duplex)
		*duplex = DUPLEX_FULL;
	return 0;
}

static int xaui_direct_power_down(struct cphy *phy, int enable)
{
	return 0;
}

static struct cphy_ops xaui_direct_ops = {
	.reset = xaui_direct_reset,
	.intr_enable = ael1002_intr_noop,
	.intr_disable = ael1002_intr_noop,
	.intr_clear = ael1002_intr_noop,
	.intr_handler = ael1002_intr_noop,
	.get_link_status = xaui_direct_get_link_status,
	.power_down = xaui_direct_power_down,
};

D
Divy Le Ray 已提交
934 935
int t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter,
			    int phy_addr, const struct mdio_ops *mdio_ops)
936
{
H
Hiroshi Shimamoto 已提交
937
	cphy_init(phy, adapter, phy_addr, &xaui_direct_ops, mdio_ops,
938 939
		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP,
		  "10GBASE-CX4");
D
Divy Le Ray 已提交
940
	return 0;
941
}