162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/****************************************************************************
362306a36Sopenharmony_ci * Driver for Solarflare network controllers and boards
462306a36Sopenharmony_ci * Copyright 2006-2011 Solarflare Communications Inc.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/*
862306a36Sopenharmony_ci * Driver for Transwitch/Mysticom CX4 retimer
962306a36Sopenharmony_ci * see www.transwitch.com, part is TXC-43128
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include "efx.h"
1562306a36Sopenharmony_ci#include "mdio_10g.h"
1662306a36Sopenharmony_ci#include "phy.h"
1762306a36Sopenharmony_ci#include "nic.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* We expect these MMDs to be in the package */
2062306a36Sopenharmony_ci#define TXC_REQUIRED_DEVS (MDIO_DEVS_PCS |	\
2162306a36Sopenharmony_ci			   MDIO_DEVS_PMAPMD |	\
2262306a36Sopenharmony_ci			   MDIO_DEVS_PHYXS)
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define TXC_LOOPBACKS ((1 << LOOPBACK_PCS) |	\
2562306a36Sopenharmony_ci		       (1 << LOOPBACK_PMAPMD) |	\
2662306a36Sopenharmony_ci		       (1 << LOOPBACK_PHYXS_WS))
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/**************************************************************************
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * Compile-time config
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci **************************************************************************
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci#define TXCNAME "TXC43128"
3562306a36Sopenharmony_ci/* Total length of time we'll wait for the PHY to come out of reset (ms) */
3662306a36Sopenharmony_ci#define TXC_MAX_RESET_TIME	500
3762306a36Sopenharmony_ci/* Interval between checks (ms) */
3862306a36Sopenharmony_ci#define TXC_RESET_WAIT		10
3962306a36Sopenharmony_ci/* How long to run BIST (us) */
4062306a36Sopenharmony_ci#define TXC_BIST_DURATION	50
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/**************************************************************************
4362306a36Sopenharmony_ci *
4462306a36Sopenharmony_ci * Register definitions
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci **************************************************************************
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Command register */
5062306a36Sopenharmony_ci#define TXC_GLRGS_GLCMD		0xc004
5162306a36Sopenharmony_ci/* Useful bits in command register */
5262306a36Sopenharmony_ci/* Lane power-down */
5362306a36Sopenharmony_ci#define TXC_GLCMD_L01PD_LBN	5
5462306a36Sopenharmony_ci#define TXC_GLCMD_L23PD_LBN	6
5562306a36Sopenharmony_ci/* Limited SW reset: preserves configuration but
5662306a36Sopenharmony_ci * initiates a logic reset. Self-clearing */
5762306a36Sopenharmony_ci#define TXC_GLCMD_LMTSWRST_LBN	14
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* Signal Quality Control */
6062306a36Sopenharmony_ci#define TXC_GLRGS_GSGQLCTL	0xc01a
6162306a36Sopenharmony_ci/* Enable bit */
6262306a36Sopenharmony_ci#define TXC_GSGQLCT_SGQLEN_LBN	15
6362306a36Sopenharmony_ci/* Lane selection */
6462306a36Sopenharmony_ci#define TXC_GSGQLCT_LNSL_LBN	13
6562306a36Sopenharmony_ci#define TXC_GSGQLCT_LNSL_WIDTH	2
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/* Analog TX control */
6862306a36Sopenharmony_ci#define TXC_ALRGS_ATXCTL	0xc040
6962306a36Sopenharmony_ci/* Lane power-down */
7062306a36Sopenharmony_ci#define TXC_ATXCTL_TXPD3_LBN	15
7162306a36Sopenharmony_ci#define TXC_ATXCTL_TXPD2_LBN	14
7262306a36Sopenharmony_ci#define TXC_ATXCTL_TXPD1_LBN	13
7362306a36Sopenharmony_ci#define TXC_ATXCTL_TXPD0_LBN	12
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/* Amplitude on lanes 0, 1 */
7662306a36Sopenharmony_ci#define TXC_ALRGS_ATXAMP0	0xc041
7762306a36Sopenharmony_ci/* Amplitude on lanes 2, 3 */
7862306a36Sopenharmony_ci#define TXC_ALRGS_ATXAMP1	0xc042
7962306a36Sopenharmony_ci/* Bit position of value for lane 0 (or 2) */
8062306a36Sopenharmony_ci#define TXC_ATXAMP_LANE02_LBN	3
8162306a36Sopenharmony_ci/* Bit position of value for lane 1 (or 3) */
8262306a36Sopenharmony_ci#define TXC_ATXAMP_LANE13_LBN	11
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define TXC_ATXAMP_1280_mV	0
8562306a36Sopenharmony_ci#define TXC_ATXAMP_1200_mV	8
8662306a36Sopenharmony_ci#define TXC_ATXAMP_1120_mV	12
8762306a36Sopenharmony_ci#define TXC_ATXAMP_1060_mV	14
8862306a36Sopenharmony_ci#define TXC_ATXAMP_0820_mV	25
8962306a36Sopenharmony_ci#define TXC_ATXAMP_0720_mV	26
9062306a36Sopenharmony_ci#define TXC_ATXAMP_0580_mV	27
9162306a36Sopenharmony_ci#define TXC_ATXAMP_0440_mV	28
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define TXC_ATXAMP_0820_BOTH					\
9462306a36Sopenharmony_ci	((TXC_ATXAMP_0820_mV << TXC_ATXAMP_LANE02_LBN)		\
9562306a36Sopenharmony_ci	 | (TXC_ATXAMP_0820_mV << TXC_ATXAMP_LANE13_LBN))
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci#define TXC_ATXAMP_DEFAULT	0x6060 /* From databook */
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/* Preemphasis on lanes 0, 1 */
10062306a36Sopenharmony_ci#define TXC_ALRGS_ATXPRE0	0xc043
10162306a36Sopenharmony_ci/* Preemphasis on lanes 2, 3 */
10262306a36Sopenharmony_ci#define TXC_ALRGS_ATXPRE1	0xc044
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci#define TXC_ATXPRE_NONE 0
10562306a36Sopenharmony_ci#define TXC_ATXPRE_DEFAULT	0x1010 /* From databook */
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci#define TXC_ALRGS_ARXCTL	0xc045
10862306a36Sopenharmony_ci/* Lane power-down */
10962306a36Sopenharmony_ci#define TXC_ARXCTL_RXPD3_LBN	15
11062306a36Sopenharmony_ci#define TXC_ARXCTL_RXPD2_LBN	14
11162306a36Sopenharmony_ci#define TXC_ARXCTL_RXPD1_LBN	13
11262306a36Sopenharmony_ci#define TXC_ARXCTL_RXPD0_LBN	12
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/* Main control */
11562306a36Sopenharmony_ci#define TXC_MRGS_CTL		0xc340
11662306a36Sopenharmony_ci/* Bits in main control */
11762306a36Sopenharmony_ci#define TXC_MCTL_RESET_LBN	15	/* Self clear */
11862306a36Sopenharmony_ci#define TXC_MCTL_TXLED_LBN	14	/* 1 to show align status */
11962306a36Sopenharmony_ci#define TXC_MCTL_RXLED_LBN	13	/* 1 to show align status */
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/* GPIO output */
12262306a36Sopenharmony_ci#define TXC_GPIO_OUTPUT		0xc346
12362306a36Sopenharmony_ci#define TXC_GPIO_DIR		0xc348
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/* Vendor-specific BIST registers */
12662306a36Sopenharmony_ci#define TXC_BIST_CTL		0xc280
12762306a36Sopenharmony_ci#define TXC_BIST_TXFRMCNT	0xc281
12862306a36Sopenharmony_ci#define TXC_BIST_RX0FRMCNT	0xc282
12962306a36Sopenharmony_ci#define TXC_BIST_RX1FRMCNT	0xc283
13062306a36Sopenharmony_ci#define TXC_BIST_RX2FRMCNT	0xc284
13162306a36Sopenharmony_ci#define TXC_BIST_RX3FRMCNT	0xc285
13262306a36Sopenharmony_ci#define TXC_BIST_RX0ERRCNT	0xc286
13362306a36Sopenharmony_ci#define TXC_BIST_RX1ERRCNT	0xc287
13462306a36Sopenharmony_ci#define TXC_BIST_RX2ERRCNT	0xc288
13562306a36Sopenharmony_ci#define TXC_BIST_RX3ERRCNT	0xc289
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/* BIST type (controls bit patter in test) */
13862306a36Sopenharmony_ci#define TXC_BIST_CTRL_TYPE_LBN	10
13962306a36Sopenharmony_ci#define TXC_BIST_CTRL_TYPE_TSD	0	/* TranSwitch Deterministic */
14062306a36Sopenharmony_ci#define TXC_BIST_CTRL_TYPE_CRP	1	/* CRPAT standard */
14162306a36Sopenharmony_ci#define TXC_BIST_CTRL_TYPE_CJP	2	/* CJPAT standard */
14262306a36Sopenharmony_ci#define TXC_BIST_CTRL_TYPE_TSR	3	/* TranSwitch pseudo-random */
14362306a36Sopenharmony_ci/* Set this to 1 for 10 bit and 0 for 8 bit */
14462306a36Sopenharmony_ci#define TXC_BIST_CTRL_B10EN_LBN	12
14562306a36Sopenharmony_ci/* Enable BIST (write 0 to disable) */
14662306a36Sopenharmony_ci#define TXC_BIST_CTRL_ENAB_LBN	13
14762306a36Sopenharmony_ci/* Stop BIST (self-clears when stop complete) */
14862306a36Sopenharmony_ci#define TXC_BIST_CTRL_STOP_LBN	14
14962306a36Sopenharmony_ci/* Start BIST (cleared by writing 1 to STOP) */
15062306a36Sopenharmony_ci#define TXC_BIST_CTRL_STRT_LBN	15
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci/* Mt. Diablo test configuration */
15362306a36Sopenharmony_ci#define TXC_MTDIABLO_CTRL	0xc34f
15462306a36Sopenharmony_ci#define TXC_MTDIABLO_CTRL_PMA_LOOP_LBN	10
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistruct txc43128_data {
15762306a36Sopenharmony_ci	unsigned long bug10934_timer;
15862306a36Sopenharmony_ci	enum ef4_phy_mode phy_mode;
15962306a36Sopenharmony_ci	enum ef4_loopback_mode loopback_mode;
16062306a36Sopenharmony_ci};
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/* The PHY sometimes needs a reset to bring the link back up.  So long as
16362306a36Sopenharmony_ci * it reports link down, we reset it every 5 seconds.
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_ci#define BUG10934_RESET_INTERVAL (5 * HZ)
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci/* Perform a reset that doesn't clear configuration changes */
16862306a36Sopenharmony_cistatic void txc_reset_logic(struct ef4_nic *efx);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci/* Set the output value of a gpio */
17162306a36Sopenharmony_civoid falcon_txc_set_gpio_val(struct ef4_nic *efx, int pin, int on)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	ef4_mdio_set_flag(efx, MDIO_MMD_PHYXS, TXC_GPIO_OUTPUT, 1 << pin, on);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci/* Set up the GPIO direction register */
17762306a36Sopenharmony_civoid falcon_txc_set_gpio_dir(struct ef4_nic *efx, int pin, int dir)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	ef4_mdio_set_flag(efx, MDIO_MMD_PHYXS, TXC_GPIO_DIR, 1 << pin, dir);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/* Reset the PMA/PMD MMD. The documentation is explicit that this does a
18362306a36Sopenharmony_ci * global reset (it's less clear what reset of other MMDs does).*/
18462306a36Sopenharmony_cistatic int txc_reset_phy(struct ef4_nic *efx)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	int rc = ef4_mdio_reset_mmd(efx, MDIO_MMD_PMAPMD,
18762306a36Sopenharmony_ci				    TXC_MAX_RESET_TIME / TXC_RESET_WAIT,
18862306a36Sopenharmony_ci				    TXC_RESET_WAIT);
18962306a36Sopenharmony_ci	if (rc < 0)
19062306a36Sopenharmony_ci		goto fail;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/* Check that all the MMDs we expect are present and responding. */
19362306a36Sopenharmony_ci	rc = ef4_mdio_check_mmds(efx, TXC_REQUIRED_DEVS);
19462306a36Sopenharmony_ci	if (rc < 0)
19562306a36Sopenharmony_ci		goto fail;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cifail:
20062306a36Sopenharmony_ci	netif_err(efx, hw, efx->net_dev, TXCNAME ": reset timed out!\n");
20162306a36Sopenharmony_ci	return rc;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci/* Run a single BIST on one MMD */
20562306a36Sopenharmony_cistatic int txc_bist_one(struct ef4_nic *efx, int mmd, int test)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	int ctrl, bctl;
20862306a36Sopenharmony_ci	int lane;
20962306a36Sopenharmony_ci	int rc = 0;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* Set PMA to test into loopback using Mt Diablo reg as per app note */
21262306a36Sopenharmony_ci	ctrl = ef4_mdio_read(efx, MDIO_MMD_PCS, TXC_MTDIABLO_CTRL);
21362306a36Sopenharmony_ci	ctrl |= (1 << TXC_MTDIABLO_CTRL_PMA_LOOP_LBN);
21462306a36Sopenharmony_ci	ef4_mdio_write(efx, MDIO_MMD_PCS, TXC_MTDIABLO_CTRL, ctrl);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/* The BIST app. note lists these  as 3 distinct steps. */
21762306a36Sopenharmony_ci	/* Set the BIST type */
21862306a36Sopenharmony_ci	bctl = (test << TXC_BIST_CTRL_TYPE_LBN);
21962306a36Sopenharmony_ci	ef4_mdio_write(efx, mmd, TXC_BIST_CTL, bctl);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/* Set the BSTEN bit in the BIST Control register to enable */
22262306a36Sopenharmony_ci	bctl |= (1 << TXC_BIST_CTRL_ENAB_LBN);
22362306a36Sopenharmony_ci	ef4_mdio_write(efx, mmd, TXC_BIST_CTL, bctl);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* Set the BSTRT bit in the BIST Control register */
22662306a36Sopenharmony_ci	ef4_mdio_write(efx, mmd, TXC_BIST_CTL,
22762306a36Sopenharmony_ci		       bctl | (1 << TXC_BIST_CTRL_STRT_LBN));
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/* Wait. */
23062306a36Sopenharmony_ci	udelay(TXC_BIST_DURATION);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* Set the BSTOP bit in the BIST Control register */
23362306a36Sopenharmony_ci	bctl |= (1 << TXC_BIST_CTRL_STOP_LBN);
23462306a36Sopenharmony_ci	ef4_mdio_write(efx, mmd, TXC_BIST_CTL, bctl);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* The STOP bit should go off when things have stopped */
23762306a36Sopenharmony_ci	while (bctl & (1 << TXC_BIST_CTRL_STOP_LBN))
23862306a36Sopenharmony_ci		bctl = ef4_mdio_read(efx, mmd, TXC_BIST_CTL);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/* Check all the error counts are 0 and all the frame counts are
24162306a36Sopenharmony_ci	   non-zero */
24262306a36Sopenharmony_ci	for (lane = 0; lane < 4; lane++) {
24362306a36Sopenharmony_ci		int count = ef4_mdio_read(efx, mmd, TXC_BIST_RX0ERRCNT + lane);
24462306a36Sopenharmony_ci		if (count != 0) {
24562306a36Sopenharmony_ci			netif_err(efx, hw, efx->net_dev, TXCNAME": BIST error. "
24662306a36Sopenharmony_ci				  "Lane %d had %d errs\n", lane, count);
24762306a36Sopenharmony_ci			rc = -EIO;
24862306a36Sopenharmony_ci		}
24962306a36Sopenharmony_ci		count = ef4_mdio_read(efx, mmd, TXC_BIST_RX0FRMCNT + lane);
25062306a36Sopenharmony_ci		if (count == 0) {
25162306a36Sopenharmony_ci			netif_err(efx, hw, efx->net_dev, TXCNAME": BIST error. "
25262306a36Sopenharmony_ci				  "Lane %d got 0 frames\n", lane);
25362306a36Sopenharmony_ci			rc = -EIO;
25462306a36Sopenharmony_ci		}
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (rc == 0)
25862306a36Sopenharmony_ci		netif_info(efx, hw, efx->net_dev, TXCNAME": BIST pass\n");
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* Disable BIST */
26162306a36Sopenharmony_ci	ef4_mdio_write(efx, mmd, TXC_BIST_CTL, 0);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	/* Turn off loopback */
26462306a36Sopenharmony_ci	ctrl &= ~(1 << TXC_MTDIABLO_CTRL_PMA_LOOP_LBN);
26562306a36Sopenharmony_ci	ef4_mdio_write(efx, MDIO_MMD_PCS, TXC_MTDIABLO_CTRL, ctrl);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return rc;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic int txc_bist(struct ef4_nic *efx)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	return txc_bist_one(efx, MDIO_MMD_PCS, TXC_BIST_CTRL_TYPE_TSD);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci/* Push the non-configurable defaults into the PHY. This must be
27662306a36Sopenharmony_ci * done after every full reset */
27762306a36Sopenharmony_cistatic void txc_apply_defaults(struct ef4_nic *efx)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	int mctrl;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* Turn amplitude down and preemphasis off on the host side
28262306a36Sopenharmony_ci	 * (PHY<->MAC) as this is believed less likely to upset Falcon
28362306a36Sopenharmony_ci	 * and no adverse effects have been noted. It probably also
28462306a36Sopenharmony_ci	 * saves a picowatt or two */
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* Turn off preemphasis */
28762306a36Sopenharmony_ci	ef4_mdio_write(efx, MDIO_MMD_PHYXS, TXC_ALRGS_ATXPRE0, TXC_ATXPRE_NONE);
28862306a36Sopenharmony_ci	ef4_mdio_write(efx, MDIO_MMD_PHYXS, TXC_ALRGS_ATXPRE1, TXC_ATXPRE_NONE);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	/* Turn down the amplitude */
29162306a36Sopenharmony_ci	ef4_mdio_write(efx, MDIO_MMD_PHYXS,
29262306a36Sopenharmony_ci		       TXC_ALRGS_ATXAMP0, TXC_ATXAMP_0820_BOTH);
29362306a36Sopenharmony_ci	ef4_mdio_write(efx, MDIO_MMD_PHYXS,
29462306a36Sopenharmony_ci		       TXC_ALRGS_ATXAMP1, TXC_ATXAMP_0820_BOTH);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/* Set the line side amplitude and preemphasis to the databook
29762306a36Sopenharmony_ci	 * defaults as an erratum causes them to be 0 on at least some
29862306a36Sopenharmony_ci	 * PHY rev.s */
29962306a36Sopenharmony_ci	ef4_mdio_write(efx, MDIO_MMD_PMAPMD,
30062306a36Sopenharmony_ci		       TXC_ALRGS_ATXPRE0, TXC_ATXPRE_DEFAULT);
30162306a36Sopenharmony_ci	ef4_mdio_write(efx, MDIO_MMD_PMAPMD,
30262306a36Sopenharmony_ci		       TXC_ALRGS_ATXPRE1, TXC_ATXPRE_DEFAULT);
30362306a36Sopenharmony_ci	ef4_mdio_write(efx, MDIO_MMD_PMAPMD,
30462306a36Sopenharmony_ci		       TXC_ALRGS_ATXAMP0, TXC_ATXAMP_DEFAULT);
30562306a36Sopenharmony_ci	ef4_mdio_write(efx, MDIO_MMD_PMAPMD,
30662306a36Sopenharmony_ci		       TXC_ALRGS_ATXAMP1, TXC_ATXAMP_DEFAULT);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	/* Set up the LEDs  */
30962306a36Sopenharmony_ci	mctrl = ef4_mdio_read(efx, MDIO_MMD_PHYXS, TXC_MRGS_CTL);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	/* Set the Green and Red LEDs to their default modes */
31262306a36Sopenharmony_ci	mctrl &= ~((1 << TXC_MCTL_TXLED_LBN) | (1 << TXC_MCTL_RXLED_LBN));
31362306a36Sopenharmony_ci	ef4_mdio_write(efx, MDIO_MMD_PHYXS, TXC_MRGS_CTL, mctrl);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* Databook recommends doing this after configuration changes */
31662306a36Sopenharmony_ci	txc_reset_logic(efx);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	falcon_board(efx)->type->init_phy(efx);
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic int txc43128_phy_probe(struct ef4_nic *efx)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct txc43128_data *phy_data;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/* Allocate phy private storage */
32662306a36Sopenharmony_ci	phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL);
32762306a36Sopenharmony_ci	if (!phy_data)
32862306a36Sopenharmony_ci		return -ENOMEM;
32962306a36Sopenharmony_ci	efx->phy_data = phy_data;
33062306a36Sopenharmony_ci	phy_data->phy_mode = efx->phy_mode;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	efx->mdio.mmds = TXC_REQUIRED_DEVS;
33362306a36Sopenharmony_ci	efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	efx->loopback_modes = TXC_LOOPBACKS | FALCON_XMAC_LOOPBACKS;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return 0;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci/* Initialisation entry point for this PHY driver */
34162306a36Sopenharmony_cistatic int txc43128_phy_init(struct ef4_nic *efx)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	int rc;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	rc = txc_reset_phy(efx);
34662306a36Sopenharmony_ci	if (rc < 0)
34762306a36Sopenharmony_ci		return rc;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	rc = txc_bist(efx);
35062306a36Sopenharmony_ci	if (rc < 0)
35162306a36Sopenharmony_ci		return rc;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	txc_apply_defaults(efx);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return 0;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci/* Set the lane power down state in the global registers */
35962306a36Sopenharmony_cistatic void txc_glrgs_lane_power(struct ef4_nic *efx, int mmd)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	int pd = (1 << TXC_GLCMD_L01PD_LBN) | (1 << TXC_GLCMD_L23PD_LBN);
36262306a36Sopenharmony_ci	int ctl = ef4_mdio_read(efx, mmd, TXC_GLRGS_GLCMD);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (!(efx->phy_mode & PHY_MODE_LOW_POWER))
36562306a36Sopenharmony_ci		ctl &= ~pd;
36662306a36Sopenharmony_ci	else
36762306a36Sopenharmony_ci		ctl |= pd;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	ef4_mdio_write(efx, mmd, TXC_GLRGS_GLCMD, ctl);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci/* Set the lane power down state in the analog control registers */
37362306a36Sopenharmony_cistatic void txc_analog_lane_power(struct ef4_nic *efx, int mmd)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	int txpd = (1 << TXC_ATXCTL_TXPD3_LBN) | (1 << TXC_ATXCTL_TXPD2_LBN)
37662306a36Sopenharmony_ci		| (1 << TXC_ATXCTL_TXPD1_LBN) | (1 << TXC_ATXCTL_TXPD0_LBN);
37762306a36Sopenharmony_ci	int rxpd = (1 << TXC_ARXCTL_RXPD3_LBN) | (1 << TXC_ARXCTL_RXPD2_LBN)
37862306a36Sopenharmony_ci		| (1 << TXC_ARXCTL_RXPD1_LBN) | (1 << TXC_ARXCTL_RXPD0_LBN);
37962306a36Sopenharmony_ci	int txctl = ef4_mdio_read(efx, mmd, TXC_ALRGS_ATXCTL);
38062306a36Sopenharmony_ci	int rxctl = ef4_mdio_read(efx, mmd, TXC_ALRGS_ARXCTL);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (!(efx->phy_mode & PHY_MODE_LOW_POWER)) {
38362306a36Sopenharmony_ci		txctl &= ~txpd;
38462306a36Sopenharmony_ci		rxctl &= ~rxpd;
38562306a36Sopenharmony_ci	} else {
38662306a36Sopenharmony_ci		txctl |= txpd;
38762306a36Sopenharmony_ci		rxctl |= rxpd;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	ef4_mdio_write(efx, mmd, TXC_ALRGS_ATXCTL, txctl);
39162306a36Sopenharmony_ci	ef4_mdio_write(efx, mmd, TXC_ALRGS_ARXCTL, rxctl);
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic void txc_set_power(struct ef4_nic *efx)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	/* According to the data book, all the MMDs can do low power */
39762306a36Sopenharmony_ci	ef4_mdio_set_mmds_lpower(efx,
39862306a36Sopenharmony_ci				 !!(efx->phy_mode & PHY_MODE_LOW_POWER),
39962306a36Sopenharmony_ci				 TXC_REQUIRED_DEVS);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* Global register bank is in PCS, PHY XS. These control the host
40262306a36Sopenharmony_ci	 * side and line side settings respectively. */
40362306a36Sopenharmony_ci	txc_glrgs_lane_power(efx, MDIO_MMD_PCS);
40462306a36Sopenharmony_ci	txc_glrgs_lane_power(efx, MDIO_MMD_PHYXS);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	/* Analog register bank in PMA/PMD, PHY XS */
40762306a36Sopenharmony_ci	txc_analog_lane_power(efx, MDIO_MMD_PMAPMD);
40862306a36Sopenharmony_ci	txc_analog_lane_power(efx, MDIO_MMD_PHYXS);
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic void txc_reset_logic_mmd(struct ef4_nic *efx, int mmd)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	int val = ef4_mdio_read(efx, mmd, TXC_GLRGS_GLCMD);
41462306a36Sopenharmony_ci	int tries = 50;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	val |= (1 << TXC_GLCMD_LMTSWRST_LBN);
41762306a36Sopenharmony_ci	ef4_mdio_write(efx, mmd, TXC_GLRGS_GLCMD, val);
41862306a36Sopenharmony_ci	while (--tries) {
41962306a36Sopenharmony_ci		val = ef4_mdio_read(efx, mmd, TXC_GLRGS_GLCMD);
42062306a36Sopenharmony_ci		if (!(val & (1 << TXC_GLCMD_LMTSWRST_LBN)))
42162306a36Sopenharmony_ci			break;
42262306a36Sopenharmony_ci		udelay(1);
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci	if (!tries)
42562306a36Sopenharmony_ci		netif_info(efx, hw, efx->net_dev,
42662306a36Sopenharmony_ci			   TXCNAME " Logic reset timed out!\n");
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci/* Perform a logic reset. This preserves the configuration registers
43062306a36Sopenharmony_ci * and is needed for some configuration changes to take effect */
43162306a36Sopenharmony_cistatic void txc_reset_logic(struct ef4_nic *efx)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	/* The data sheet claims we can do the logic reset on either the
43462306a36Sopenharmony_ci	 * PCS or the PHYXS and the result is a reset of both host- and
43562306a36Sopenharmony_ci	 * line-side logic. */
43662306a36Sopenharmony_ci	txc_reset_logic_mmd(efx, MDIO_MMD_PCS);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic bool txc43128_phy_read_link(struct ef4_nic *efx)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	return ef4_mdio_links_ok(efx, TXC_REQUIRED_DEVS);
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic int txc43128_phy_reconfigure(struct ef4_nic *efx)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct txc43128_data *phy_data = efx->phy_data;
44762306a36Sopenharmony_ci	enum ef4_phy_mode mode_change = efx->phy_mode ^ phy_data->phy_mode;
44862306a36Sopenharmony_ci	bool loop_change = LOOPBACK_CHANGED(phy_data, efx, TXC_LOOPBACKS);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (efx->phy_mode & mode_change & PHY_MODE_TX_DISABLED) {
45162306a36Sopenharmony_ci		txc_reset_phy(efx);
45262306a36Sopenharmony_ci		txc_apply_defaults(efx);
45362306a36Sopenharmony_ci		falcon_reset_xaui(efx);
45462306a36Sopenharmony_ci		mode_change &= ~PHY_MODE_TX_DISABLED;
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	ef4_mdio_transmit_disable(efx);
45862306a36Sopenharmony_ci	ef4_mdio_phy_reconfigure(efx);
45962306a36Sopenharmony_ci	if (mode_change & PHY_MODE_LOW_POWER)
46062306a36Sopenharmony_ci		txc_set_power(efx);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/* The data sheet claims this is required after every reconfiguration
46362306a36Sopenharmony_ci	 * (note at end of 7.1), but we mustn't do it when nothing changes as
46462306a36Sopenharmony_ci	 * it glitches the link, and reconfigure gets called on link change,
46562306a36Sopenharmony_ci	 * so we get an IRQ storm on link up. */
46662306a36Sopenharmony_ci	if (loop_change || mode_change)
46762306a36Sopenharmony_ci		txc_reset_logic(efx);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	phy_data->phy_mode = efx->phy_mode;
47062306a36Sopenharmony_ci	phy_data->loopback_mode = efx->loopback_mode;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	return 0;
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic void txc43128_phy_fini(struct ef4_nic *efx)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	/* Disable link events */
47862306a36Sopenharmony_ci	ef4_mdio_write(efx, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 0);
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cistatic void txc43128_phy_remove(struct ef4_nic *efx)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	kfree(efx->phy_data);
48462306a36Sopenharmony_ci	efx->phy_data = NULL;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci/* Periodic callback: this exists mainly to poll link status as we
48862306a36Sopenharmony_ci * don't use LASI interrupts */
48962306a36Sopenharmony_cistatic bool txc43128_phy_poll(struct ef4_nic *efx)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	struct txc43128_data *data = efx->phy_data;
49262306a36Sopenharmony_ci	bool was_up = efx->link_state.up;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	efx->link_state.up = txc43128_phy_read_link(efx);
49562306a36Sopenharmony_ci	efx->link_state.speed = 10000;
49662306a36Sopenharmony_ci	efx->link_state.fd = true;
49762306a36Sopenharmony_ci	efx->link_state.fc = efx->wanted_fc;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (efx->link_state.up || (efx->loopback_mode != LOOPBACK_NONE)) {
50062306a36Sopenharmony_ci		data->bug10934_timer = jiffies;
50162306a36Sopenharmony_ci	} else {
50262306a36Sopenharmony_ci		if (time_after_eq(jiffies, (data->bug10934_timer +
50362306a36Sopenharmony_ci					    BUG10934_RESET_INTERVAL))) {
50462306a36Sopenharmony_ci			data->bug10934_timer = jiffies;
50562306a36Sopenharmony_ci			txc_reset_logic(efx);
50662306a36Sopenharmony_ci		}
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	return efx->link_state.up != was_up;
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic const char *const txc43128_test_names[] = {
51362306a36Sopenharmony_ci	"bist"
51462306a36Sopenharmony_ci};
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic const char *txc43128_test_name(struct ef4_nic *efx, unsigned int index)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	if (index < ARRAY_SIZE(txc43128_test_names))
51962306a36Sopenharmony_ci		return txc43128_test_names[index];
52062306a36Sopenharmony_ci	return NULL;
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic int txc43128_run_tests(struct ef4_nic *efx, int *results, unsigned flags)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	int rc;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (!(flags & ETH_TEST_FL_OFFLINE))
52862306a36Sopenharmony_ci		return 0;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	rc = txc_reset_phy(efx);
53162306a36Sopenharmony_ci	if (rc < 0)
53262306a36Sopenharmony_ci		return rc;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	rc = txc_bist(efx);
53562306a36Sopenharmony_ci	txc_apply_defaults(efx);
53662306a36Sopenharmony_ci	results[0] = rc ? -1 : 1;
53762306a36Sopenharmony_ci	return rc;
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic void txc43128_get_link_ksettings(struct ef4_nic *efx,
54162306a36Sopenharmony_ci					struct ethtool_link_ksettings *cmd)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	mdio45_ethtool_ksettings_get(&efx->mdio, cmd);
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ciconst struct ef4_phy_operations falcon_txc_phy_ops = {
54762306a36Sopenharmony_ci	.probe		= txc43128_phy_probe,
54862306a36Sopenharmony_ci	.init		= txc43128_phy_init,
54962306a36Sopenharmony_ci	.reconfigure	= txc43128_phy_reconfigure,
55062306a36Sopenharmony_ci	.poll		= txc43128_phy_poll,
55162306a36Sopenharmony_ci	.fini		= txc43128_phy_fini,
55262306a36Sopenharmony_ci	.remove		= txc43128_phy_remove,
55362306a36Sopenharmony_ci	.get_link_ksettings = txc43128_get_link_ksettings,
55462306a36Sopenharmony_ci	.set_link_ksettings = ef4_mdio_set_link_ksettings,
55562306a36Sopenharmony_ci	.test_alive	= ef4_mdio_test_alive,
55662306a36Sopenharmony_ci	.run_tests	= txc43128_run_tests,
55762306a36Sopenharmony_ci	.test_name	= txc43128_test_name,
55862306a36Sopenharmony_ci};
559