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