162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/***************************************************************************** 362306a36Sopenharmony_ci * * 462306a36Sopenharmony_ci * File: subr.c * 562306a36Sopenharmony_ci * $Revision: 1.27 $ * 662306a36Sopenharmony_ci * $Date: 2005/06/22 01:08:36 $ * 762306a36Sopenharmony_ci * Description: * 862306a36Sopenharmony_ci * Various subroutines (intr,pio,etc.) used by Chelsio 10G Ethernet driver. * 962306a36Sopenharmony_ci * part of the Chelsio 10Gb Ethernet Driver. * 1062306a36Sopenharmony_ci * * 1162306a36Sopenharmony_ci * * 1262306a36Sopenharmony_ci * http://www.chelsio.com * 1362306a36Sopenharmony_ci * * 1462306a36Sopenharmony_ci * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. * 1562306a36Sopenharmony_ci * All rights reserved. * 1662306a36Sopenharmony_ci * * 1762306a36Sopenharmony_ci * Maintainers: maintainers@chelsio.com * 1862306a36Sopenharmony_ci * * 1962306a36Sopenharmony_ci * Authors: Dimitrios Michailidis <dm@chelsio.com> * 2062306a36Sopenharmony_ci * Tina Yang <tainay@chelsio.com> * 2162306a36Sopenharmony_ci * Felix Marti <felix@chelsio.com> * 2262306a36Sopenharmony_ci * Scott Bardone <sbardone@chelsio.com> * 2362306a36Sopenharmony_ci * Kurt Ottaway <kottaway@chelsio.com> * 2462306a36Sopenharmony_ci * Frank DiMambro <frank@chelsio.com> * 2562306a36Sopenharmony_ci * * 2662306a36Sopenharmony_ci * History: * 2762306a36Sopenharmony_ci * * 2862306a36Sopenharmony_ci ****************************************************************************/ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "common.h" 3162306a36Sopenharmony_ci#include "elmer0.h" 3262306a36Sopenharmony_ci#include "regs.h" 3362306a36Sopenharmony_ci#include "gmac.h" 3462306a36Sopenharmony_ci#include "cphy.h" 3562306a36Sopenharmony_ci#include "sge.h" 3662306a36Sopenharmony_ci#include "tp.h" 3762306a36Sopenharmony_ci#include "espi.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/** 4062306a36Sopenharmony_ci * t1_wait_op_done - wait until an operation is completed 4162306a36Sopenharmony_ci * @adapter: the adapter performing the operation 4262306a36Sopenharmony_ci * @reg: the register to check for completion 4362306a36Sopenharmony_ci * @mask: a single-bit field within @reg that indicates completion 4462306a36Sopenharmony_ci * @polarity: the value of the field when the operation is completed 4562306a36Sopenharmony_ci * @attempts: number of check iterations 4662306a36Sopenharmony_ci * @delay: delay in usecs between iterations 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * Wait until an operation is completed by checking a bit in a register 4962306a36Sopenharmony_ci * up to @attempts times. Returns %0 if the operation completes and %1 5062306a36Sopenharmony_ci * otherwise. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_cistatic int t1_wait_op_done(adapter_t *adapter, int reg, u32 mask, int polarity, 5362306a36Sopenharmony_ci int attempts, int delay) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci while (1) { 5662306a36Sopenharmony_ci u32 val = readl(adapter->regs + reg) & mask; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (!!val == polarity) 5962306a36Sopenharmony_ci return 0; 6062306a36Sopenharmony_ci if (--attempts == 0) 6162306a36Sopenharmony_ci return 1; 6262306a36Sopenharmony_ci if (delay) 6362306a36Sopenharmony_ci udelay(delay); 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define TPI_ATTEMPTS 50 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Write a register over the TPI interface (unlocked and locked versions). 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ciint __t1_tpi_write(adapter_t *adapter, u32 addr, u32 value) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci int tpi_busy; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci writel(addr, adapter->regs + A_TPI_ADDR); 7762306a36Sopenharmony_ci writel(value, adapter->regs + A_TPI_WR_DATA); 7862306a36Sopenharmony_ci writel(F_TPIWR, adapter->regs + A_TPI_CSR); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci tpi_busy = t1_wait_op_done(adapter, A_TPI_CSR, F_TPIRDY, 1, 8162306a36Sopenharmony_ci TPI_ATTEMPTS, 3); 8262306a36Sopenharmony_ci if (tpi_busy) 8362306a36Sopenharmony_ci pr_alert("%s: TPI write to 0x%x failed\n", 8462306a36Sopenharmony_ci adapter->name, addr); 8562306a36Sopenharmony_ci return tpi_busy; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ciint t1_tpi_write(adapter_t *adapter, u32 addr, u32 value) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci int ret; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci spin_lock(&adapter->tpi_lock); 9362306a36Sopenharmony_ci ret = __t1_tpi_write(adapter, addr, value); 9462306a36Sopenharmony_ci spin_unlock(&adapter->tpi_lock); 9562306a36Sopenharmony_ci return ret; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* 9962306a36Sopenharmony_ci * Read a register over the TPI interface (unlocked and locked versions). 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ciint __t1_tpi_read(adapter_t *adapter, u32 addr, u32 *valp) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci int tpi_busy; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci writel(addr, adapter->regs + A_TPI_ADDR); 10662306a36Sopenharmony_ci writel(0, adapter->regs + A_TPI_CSR); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci tpi_busy = t1_wait_op_done(adapter, A_TPI_CSR, F_TPIRDY, 1, 10962306a36Sopenharmony_ci TPI_ATTEMPTS, 3); 11062306a36Sopenharmony_ci if (tpi_busy) 11162306a36Sopenharmony_ci pr_alert("%s: TPI read from 0x%x failed\n", 11262306a36Sopenharmony_ci adapter->name, addr); 11362306a36Sopenharmony_ci else 11462306a36Sopenharmony_ci *valp = readl(adapter->regs + A_TPI_RD_DATA); 11562306a36Sopenharmony_ci return tpi_busy; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciint t1_tpi_read(adapter_t *adapter, u32 addr, u32 *valp) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci int ret; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci spin_lock(&adapter->tpi_lock); 12362306a36Sopenharmony_ci ret = __t1_tpi_read(adapter, addr, valp); 12462306a36Sopenharmony_ci spin_unlock(&adapter->tpi_lock); 12562306a36Sopenharmony_ci return ret; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* 12962306a36Sopenharmony_ci * Set a TPI parameter. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_cistatic void t1_tpi_par(adapter_t *adapter, u32 value) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci writel(V_TPIPAR(value), adapter->regs + A_TPI_PAR); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* 13762306a36Sopenharmony_ci * Called when a port's link settings change to propagate the new values to the 13862306a36Sopenharmony_ci * associated PHY and MAC. After performing the common tasks it invokes an 13962306a36Sopenharmony_ci * OS-specific handler. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_civoid t1_link_changed(adapter_t *adapter, int port_id) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci int link_ok, speed, duplex, fc; 14462306a36Sopenharmony_ci struct cphy *phy = adapter->port[port_id].phy; 14562306a36Sopenharmony_ci struct link_config *lc = &adapter->port[port_id].link_config; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci phy->ops->get_link_status(phy, &link_ok, &speed, &duplex, &fc); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci lc->speed = speed < 0 ? SPEED_INVALID : speed; 15062306a36Sopenharmony_ci lc->duplex = duplex < 0 ? DUPLEX_INVALID : duplex; 15162306a36Sopenharmony_ci if (!(lc->requested_fc & PAUSE_AUTONEG)) 15262306a36Sopenharmony_ci fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (link_ok && speed >= 0 && lc->autoneg == AUTONEG_ENABLE) { 15562306a36Sopenharmony_ci /* Set MAC speed, duplex, and flow control to match PHY. */ 15662306a36Sopenharmony_ci struct cmac *mac = adapter->port[port_id].mac; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci mac->ops->set_speed_duplex_fc(mac, speed, duplex, fc); 15962306a36Sopenharmony_ci lc->fc = (unsigned char)fc; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci t1_link_negotiated(adapter, port_id, link_ok, speed, duplex, fc); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic bool t1_pci_intr_handler(adapter_t *adapter) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci u32 pcix_cause; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci pci_read_config_dword(adapter->pdev, A_PCICFG_INTR_CAUSE, &pcix_cause); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (pcix_cause) { 17162306a36Sopenharmony_ci pci_write_config_dword(adapter->pdev, A_PCICFG_INTR_CAUSE, 17262306a36Sopenharmony_ci pcix_cause); 17362306a36Sopenharmony_ci /* PCI errors are fatal */ 17462306a36Sopenharmony_ci t1_interrupts_disable(adapter); 17562306a36Sopenharmony_ci adapter->pending_thread_intr |= F_PL_INTR_SGE_ERR; 17662306a36Sopenharmony_ci pr_alert("%s: PCI error encountered.\n", adapter->name); 17762306a36Sopenharmony_ci return true; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci return false; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci#ifdef CONFIG_CHELSIO_T1_1G 18362306a36Sopenharmony_ci#include "fpga_defs.h" 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * PHY interrupt handler for FPGA boards. 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_cistatic int fpga_phy_intr_handler(adapter_t *adapter) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci int p; 19162306a36Sopenharmony_ci u32 cause = readl(adapter->regs + FPGA_GMAC_ADDR_INTERRUPT_CAUSE); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci for_each_port(adapter, p) 19462306a36Sopenharmony_ci if (cause & (1 << p)) { 19562306a36Sopenharmony_ci struct cphy *phy = adapter->port[p].phy; 19662306a36Sopenharmony_ci int phy_cause = phy->ops->interrupt_handler(phy); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (phy_cause & cphy_cause_link_change) 19962306a36Sopenharmony_ci t1_link_changed(adapter, p); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci writel(cause, adapter->regs + FPGA_GMAC_ADDR_INTERRUPT_CAUSE); 20262306a36Sopenharmony_ci return 0; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/* 20662306a36Sopenharmony_ci * Slow path interrupt handler for FPGAs. 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_cistatic irqreturn_t fpga_slow_intr(adapter_t *adapter) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci u32 cause = readl(adapter->regs + A_PL_CAUSE); 21162306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci cause &= ~F_PL_INTR_SGE_DATA; 21462306a36Sopenharmony_ci if (cause & F_PL_INTR_SGE_ERR) { 21562306a36Sopenharmony_ci if (t1_sge_intr_error_handler(adapter->sge)) 21662306a36Sopenharmony_ci ret = IRQ_WAKE_THREAD; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (cause & FPGA_PCIX_INTERRUPT_GMAC) 22062306a36Sopenharmony_ci fpga_phy_intr_handler(adapter); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (cause & FPGA_PCIX_INTERRUPT_TP) { 22362306a36Sopenharmony_ci /* 22462306a36Sopenharmony_ci * FPGA doesn't support MC4 interrupts and it requires 22562306a36Sopenharmony_ci * this odd layer of indirection for MC5. 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci u32 tp_cause = readl(adapter->regs + FPGA_TP_ADDR_INTERRUPT_CAUSE); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Clear TP interrupt */ 23062306a36Sopenharmony_ci writel(tp_cause, adapter->regs + FPGA_TP_ADDR_INTERRUPT_CAUSE); 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci if (cause & FPGA_PCIX_INTERRUPT_PCIX) { 23362306a36Sopenharmony_ci if (t1_pci_intr_handler(adapter)) 23462306a36Sopenharmony_ci ret = IRQ_WAKE_THREAD; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* Clear the interrupts just processed. */ 23862306a36Sopenharmony_ci if (cause) 23962306a36Sopenharmony_ci writel(cause, adapter->regs + A_PL_CAUSE); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (ret != IRQ_NONE) 24262306a36Sopenharmony_ci return ret; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return cause == 0 ? IRQ_NONE : IRQ_HANDLED; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci#endif 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* 24962306a36Sopenharmony_ci * Wait until Elmer's MI1 interface is ready for new operations. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_cistatic int mi1_wait_until_ready(adapter_t *adapter, int mi1_reg) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci int attempts = 100, busy; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci do { 25662306a36Sopenharmony_ci u32 val; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci __t1_tpi_read(adapter, mi1_reg, &val); 25962306a36Sopenharmony_ci busy = val & F_MI1_OP_BUSY; 26062306a36Sopenharmony_ci if (busy) 26162306a36Sopenharmony_ci udelay(10); 26262306a36Sopenharmony_ci } while (busy && --attempts); 26362306a36Sopenharmony_ci if (busy) 26462306a36Sopenharmony_ci pr_alert("%s: MDIO operation timed out\n", adapter->name); 26562306a36Sopenharmony_ci return busy; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* 26962306a36Sopenharmony_ci * MI1 MDIO initialization. 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_cistatic void mi1_mdio_init(adapter_t *adapter, const struct board_info *bi) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci u32 clkdiv = bi->clock_elmer0 / (2 * bi->mdio_mdc) - 1; 27462306a36Sopenharmony_ci u32 val = F_MI1_PREAMBLE_ENABLE | V_MI1_MDI_INVERT(bi->mdio_mdiinv) | 27562306a36Sopenharmony_ci V_MI1_MDI_ENABLE(bi->mdio_mdien) | V_MI1_CLK_DIV(clkdiv); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (!(bi->caps & SUPPORTED_10000baseT_Full)) 27862306a36Sopenharmony_ci val |= V_MI1_SOF(1); 27962306a36Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_CFG, val); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci#if defined(CONFIG_CHELSIO_T1_1G) 28362306a36Sopenharmony_ci/* 28462306a36Sopenharmony_ci * Elmer MI1 MDIO read/write operations. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_cistatic int mi1_mdio_read(struct net_device *dev, int phy_addr, int mmd_addr, 28762306a36Sopenharmony_ci u16 reg_addr) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 29062306a36Sopenharmony_ci u32 addr = V_MI1_REG_ADDR(reg_addr) | V_MI1_PHY_ADDR(phy_addr); 29162306a36Sopenharmony_ci unsigned int val; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci spin_lock(&adapter->tpi_lock); 29462306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_ADDR, addr); 29562306a36Sopenharmony_ci __t1_tpi_write(adapter, 29662306a36Sopenharmony_ci A_ELMER0_PORT0_MI1_OP, MI1_OP_DIRECT_READ); 29762306a36Sopenharmony_ci mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP); 29862306a36Sopenharmony_ci __t1_tpi_read(adapter, A_ELMER0_PORT0_MI1_DATA, &val); 29962306a36Sopenharmony_ci spin_unlock(&adapter->tpi_lock); 30062306a36Sopenharmony_ci return val; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic int mi1_mdio_write(struct net_device *dev, int phy_addr, int mmd_addr, 30462306a36Sopenharmony_ci u16 reg_addr, u16 val) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 30762306a36Sopenharmony_ci u32 addr = V_MI1_REG_ADDR(reg_addr) | V_MI1_PHY_ADDR(phy_addr); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci spin_lock(&adapter->tpi_lock); 31062306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_ADDR, addr); 31162306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_DATA, val); 31262306a36Sopenharmony_ci __t1_tpi_write(adapter, 31362306a36Sopenharmony_ci A_ELMER0_PORT0_MI1_OP, MI1_OP_DIRECT_WRITE); 31462306a36Sopenharmony_ci mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP); 31562306a36Sopenharmony_ci spin_unlock(&adapter->tpi_lock); 31662306a36Sopenharmony_ci return 0; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic const struct mdio_ops mi1_mdio_ops = { 32062306a36Sopenharmony_ci .init = mi1_mdio_init, 32162306a36Sopenharmony_ci .read = mi1_mdio_read, 32262306a36Sopenharmony_ci .write = mi1_mdio_write, 32362306a36Sopenharmony_ci .mode_support = MDIO_SUPPORTS_C22 32462306a36Sopenharmony_ci}; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci#endif 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int mi1_mdio_ext_read(struct net_device *dev, int phy_addr, int mmd_addr, 32962306a36Sopenharmony_ci u16 reg_addr) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 33262306a36Sopenharmony_ci u32 addr = V_MI1_REG_ADDR(mmd_addr) | V_MI1_PHY_ADDR(phy_addr); 33362306a36Sopenharmony_ci unsigned int val; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci spin_lock(&adapter->tpi_lock); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Write the address we want. */ 33862306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_ADDR, addr); 33962306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_DATA, reg_addr); 34062306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_OP, 34162306a36Sopenharmony_ci MI1_OP_INDIRECT_ADDRESS); 34262306a36Sopenharmony_ci mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Write the operation we want. */ 34562306a36Sopenharmony_ci __t1_tpi_write(adapter, 34662306a36Sopenharmony_ci A_ELMER0_PORT0_MI1_OP, MI1_OP_INDIRECT_READ); 34762306a36Sopenharmony_ci mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* Read the data. */ 35062306a36Sopenharmony_ci __t1_tpi_read(adapter, A_ELMER0_PORT0_MI1_DATA, &val); 35162306a36Sopenharmony_ci spin_unlock(&adapter->tpi_lock); 35262306a36Sopenharmony_ci return val; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int mi1_mdio_ext_write(struct net_device *dev, int phy_addr, 35662306a36Sopenharmony_ci int mmd_addr, u16 reg_addr, u16 val) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 35962306a36Sopenharmony_ci u32 addr = V_MI1_REG_ADDR(mmd_addr) | V_MI1_PHY_ADDR(phy_addr); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci spin_lock(&adapter->tpi_lock); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* Write the address we want. */ 36462306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_ADDR, addr); 36562306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_DATA, reg_addr); 36662306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_OP, 36762306a36Sopenharmony_ci MI1_OP_INDIRECT_ADDRESS); 36862306a36Sopenharmony_ci mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* Write the data. */ 37162306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_DATA, val); 37262306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_OP, MI1_OP_INDIRECT_WRITE); 37362306a36Sopenharmony_ci mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP); 37462306a36Sopenharmony_ci spin_unlock(&adapter->tpi_lock); 37562306a36Sopenharmony_ci return 0; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic const struct mdio_ops mi1_mdio_ext_ops = { 37962306a36Sopenharmony_ci .init = mi1_mdio_init, 38062306a36Sopenharmony_ci .read = mi1_mdio_ext_read, 38162306a36Sopenharmony_ci .write = mi1_mdio_ext_write, 38262306a36Sopenharmony_ci .mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22 38362306a36Sopenharmony_ci}; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cienum { 38662306a36Sopenharmony_ci CH_BRD_T110_1CU, 38762306a36Sopenharmony_ci CH_BRD_N110_1F, 38862306a36Sopenharmony_ci CH_BRD_N210_1F, 38962306a36Sopenharmony_ci CH_BRD_T210_1F, 39062306a36Sopenharmony_ci CH_BRD_T210_1CU, 39162306a36Sopenharmony_ci CH_BRD_N204_4CU, 39262306a36Sopenharmony_ci}; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic const struct board_info t1_board[] = { 39562306a36Sopenharmony_ci { 39662306a36Sopenharmony_ci .board = CHBT_BOARD_CHT110, 39762306a36Sopenharmony_ci .port_number = 1, 39862306a36Sopenharmony_ci .caps = SUPPORTED_10000baseT_Full, 39962306a36Sopenharmony_ci .chip_term = CHBT_TERM_T1, 40062306a36Sopenharmony_ci .chip_mac = CHBT_MAC_PM3393, 40162306a36Sopenharmony_ci .chip_phy = CHBT_PHY_MY3126, 40262306a36Sopenharmony_ci .clock_core = 125000000, 40362306a36Sopenharmony_ci .clock_mc3 = 150000000, 40462306a36Sopenharmony_ci .clock_mc4 = 125000000, 40562306a36Sopenharmony_ci .espi_nports = 1, 40662306a36Sopenharmony_ci .clock_elmer0 = 44, 40762306a36Sopenharmony_ci .mdio_mdien = 1, 40862306a36Sopenharmony_ci .mdio_mdiinv = 1, 40962306a36Sopenharmony_ci .mdio_mdc = 1, 41062306a36Sopenharmony_ci .mdio_phybaseaddr = 1, 41162306a36Sopenharmony_ci .gmac = &t1_pm3393_ops, 41262306a36Sopenharmony_ci .gphy = &t1_my3126_ops, 41362306a36Sopenharmony_ci .mdio_ops = &mi1_mdio_ext_ops, 41462306a36Sopenharmony_ci .desc = "Chelsio T110 1x10GBase-CX4 TOE", 41562306a36Sopenharmony_ci }, 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci { 41862306a36Sopenharmony_ci .board = CHBT_BOARD_N110, 41962306a36Sopenharmony_ci .port_number = 1, 42062306a36Sopenharmony_ci .caps = SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE, 42162306a36Sopenharmony_ci .chip_term = CHBT_TERM_T1, 42262306a36Sopenharmony_ci .chip_mac = CHBT_MAC_PM3393, 42362306a36Sopenharmony_ci .chip_phy = CHBT_PHY_88X2010, 42462306a36Sopenharmony_ci .clock_core = 125000000, 42562306a36Sopenharmony_ci .espi_nports = 1, 42662306a36Sopenharmony_ci .clock_elmer0 = 44, 42762306a36Sopenharmony_ci .mdio_mdien = 0, 42862306a36Sopenharmony_ci .mdio_mdiinv = 0, 42962306a36Sopenharmony_ci .mdio_mdc = 1, 43062306a36Sopenharmony_ci .mdio_phybaseaddr = 0, 43162306a36Sopenharmony_ci .gmac = &t1_pm3393_ops, 43262306a36Sopenharmony_ci .gphy = &t1_mv88x201x_ops, 43362306a36Sopenharmony_ci .mdio_ops = &mi1_mdio_ext_ops, 43462306a36Sopenharmony_ci .desc = "Chelsio N110 1x10GBaseX NIC", 43562306a36Sopenharmony_ci }, 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci { 43862306a36Sopenharmony_ci .board = CHBT_BOARD_N210, 43962306a36Sopenharmony_ci .port_number = 1, 44062306a36Sopenharmony_ci .caps = SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE, 44162306a36Sopenharmony_ci .chip_term = CHBT_TERM_T2, 44262306a36Sopenharmony_ci .chip_mac = CHBT_MAC_PM3393, 44362306a36Sopenharmony_ci .chip_phy = CHBT_PHY_88X2010, 44462306a36Sopenharmony_ci .clock_core = 125000000, 44562306a36Sopenharmony_ci .espi_nports = 1, 44662306a36Sopenharmony_ci .clock_elmer0 = 44, 44762306a36Sopenharmony_ci .mdio_mdien = 0, 44862306a36Sopenharmony_ci .mdio_mdiinv = 0, 44962306a36Sopenharmony_ci .mdio_mdc = 1, 45062306a36Sopenharmony_ci .mdio_phybaseaddr = 0, 45162306a36Sopenharmony_ci .gmac = &t1_pm3393_ops, 45262306a36Sopenharmony_ci .gphy = &t1_mv88x201x_ops, 45362306a36Sopenharmony_ci .mdio_ops = &mi1_mdio_ext_ops, 45462306a36Sopenharmony_ci .desc = "Chelsio N210 1x10GBaseX NIC", 45562306a36Sopenharmony_ci }, 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci { 45862306a36Sopenharmony_ci .board = CHBT_BOARD_CHT210, 45962306a36Sopenharmony_ci .port_number = 1, 46062306a36Sopenharmony_ci .caps = SUPPORTED_10000baseT_Full, 46162306a36Sopenharmony_ci .chip_term = CHBT_TERM_T2, 46262306a36Sopenharmony_ci .chip_mac = CHBT_MAC_PM3393, 46362306a36Sopenharmony_ci .chip_phy = CHBT_PHY_88X2010, 46462306a36Sopenharmony_ci .clock_core = 125000000, 46562306a36Sopenharmony_ci .clock_mc3 = 133000000, 46662306a36Sopenharmony_ci .clock_mc4 = 125000000, 46762306a36Sopenharmony_ci .espi_nports = 1, 46862306a36Sopenharmony_ci .clock_elmer0 = 44, 46962306a36Sopenharmony_ci .mdio_mdien = 0, 47062306a36Sopenharmony_ci .mdio_mdiinv = 0, 47162306a36Sopenharmony_ci .mdio_mdc = 1, 47262306a36Sopenharmony_ci .mdio_phybaseaddr = 0, 47362306a36Sopenharmony_ci .gmac = &t1_pm3393_ops, 47462306a36Sopenharmony_ci .gphy = &t1_mv88x201x_ops, 47562306a36Sopenharmony_ci .mdio_ops = &mi1_mdio_ext_ops, 47662306a36Sopenharmony_ci .desc = "Chelsio T210 1x10GBaseX TOE", 47762306a36Sopenharmony_ci }, 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci { 48062306a36Sopenharmony_ci .board = CHBT_BOARD_CHT210, 48162306a36Sopenharmony_ci .port_number = 1, 48262306a36Sopenharmony_ci .caps = SUPPORTED_10000baseT_Full, 48362306a36Sopenharmony_ci .chip_term = CHBT_TERM_T2, 48462306a36Sopenharmony_ci .chip_mac = CHBT_MAC_PM3393, 48562306a36Sopenharmony_ci .chip_phy = CHBT_PHY_MY3126, 48662306a36Sopenharmony_ci .clock_core = 125000000, 48762306a36Sopenharmony_ci .clock_mc3 = 133000000, 48862306a36Sopenharmony_ci .clock_mc4 = 125000000, 48962306a36Sopenharmony_ci .espi_nports = 1, 49062306a36Sopenharmony_ci .clock_elmer0 = 44, 49162306a36Sopenharmony_ci .mdio_mdien = 1, 49262306a36Sopenharmony_ci .mdio_mdiinv = 1, 49362306a36Sopenharmony_ci .mdio_mdc = 1, 49462306a36Sopenharmony_ci .mdio_phybaseaddr = 1, 49562306a36Sopenharmony_ci .gmac = &t1_pm3393_ops, 49662306a36Sopenharmony_ci .gphy = &t1_my3126_ops, 49762306a36Sopenharmony_ci .mdio_ops = &mi1_mdio_ext_ops, 49862306a36Sopenharmony_ci .desc = "Chelsio T210 1x10GBase-CX4 TOE", 49962306a36Sopenharmony_ci }, 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci#ifdef CONFIG_CHELSIO_T1_1G 50262306a36Sopenharmony_ci { 50362306a36Sopenharmony_ci .board = CHBT_BOARD_CHN204, 50462306a36Sopenharmony_ci .port_number = 4, 50562306a36Sopenharmony_ci .caps = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full 50662306a36Sopenharmony_ci | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full 50762306a36Sopenharmony_ci | SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | 50862306a36Sopenharmony_ci SUPPORTED_PAUSE | SUPPORTED_TP, 50962306a36Sopenharmony_ci .chip_term = CHBT_TERM_T2, 51062306a36Sopenharmony_ci .chip_mac = CHBT_MAC_VSC7321, 51162306a36Sopenharmony_ci .chip_phy = CHBT_PHY_88E1111, 51262306a36Sopenharmony_ci .clock_core = 100000000, 51362306a36Sopenharmony_ci .espi_nports = 4, 51462306a36Sopenharmony_ci .clock_elmer0 = 44, 51562306a36Sopenharmony_ci .mdio_mdien = 0, 51662306a36Sopenharmony_ci .mdio_mdiinv = 0, 51762306a36Sopenharmony_ci .mdio_mdc = 0, 51862306a36Sopenharmony_ci .mdio_phybaseaddr = 4, 51962306a36Sopenharmony_ci .gmac = &t1_vsc7326_ops, 52062306a36Sopenharmony_ci .gphy = &t1_mv88e1xxx_ops, 52162306a36Sopenharmony_ci .mdio_ops = &mi1_mdio_ops, 52262306a36Sopenharmony_ci .desc = "Chelsio N204 4x100/1000BaseT NIC", 52362306a36Sopenharmony_ci }, 52462306a36Sopenharmony_ci#endif 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci}; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ciconst struct pci_device_id t1_pci_tbl[] = { 52962306a36Sopenharmony_ci CH_DEVICE(8, 0, CH_BRD_T110_1CU), 53062306a36Sopenharmony_ci CH_DEVICE(8, 1, CH_BRD_T110_1CU), 53162306a36Sopenharmony_ci CH_DEVICE(7, 0, CH_BRD_N110_1F), 53262306a36Sopenharmony_ci CH_DEVICE(10, 1, CH_BRD_N210_1F), 53362306a36Sopenharmony_ci CH_DEVICE(11, 1, CH_BRD_T210_1F), 53462306a36Sopenharmony_ci CH_DEVICE(14, 1, CH_BRD_T210_1CU), 53562306a36Sopenharmony_ci CH_DEVICE(16, 1, CH_BRD_N204_4CU), 53662306a36Sopenharmony_ci { 0 } 53762306a36Sopenharmony_ci}; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, t1_pci_tbl); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci/* 54262306a36Sopenharmony_ci * Return the board_info structure with a given index. Out-of-range indices 54362306a36Sopenharmony_ci * return NULL. 54462306a36Sopenharmony_ci */ 54562306a36Sopenharmony_ciconst struct board_info *t1_get_board_info(unsigned int board_id) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci return board_id < ARRAY_SIZE(t1_board) ? &t1_board[board_id] : NULL; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistruct chelsio_vpd_t { 55162306a36Sopenharmony_ci u32 format_version; 55262306a36Sopenharmony_ci u8 serial_number[16]; 55362306a36Sopenharmony_ci u8 mac_base_address[6]; 55462306a36Sopenharmony_ci u8 pad[2]; /* make multiple-of-4 size requirement explicit */ 55562306a36Sopenharmony_ci}; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci#define EEPROMSIZE (8 * 1024) 55862306a36Sopenharmony_ci#define EEPROM_MAX_POLL 4 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci/* 56162306a36Sopenharmony_ci * Read SEEPROM. A zero is written to the flag register when the address is 56262306a36Sopenharmony_ci * written to the Control register. The hardware device will set the flag to a 56362306a36Sopenharmony_ci * one when 4B have been transferred to the Data register. 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_ciint t1_seeprom_read(adapter_t *adapter, u32 addr, __le32 *data) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci int i = EEPROM_MAX_POLL; 56862306a36Sopenharmony_ci u16 val; 56962306a36Sopenharmony_ci u32 v; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (addr >= EEPROMSIZE || (addr & 3)) 57262306a36Sopenharmony_ci return -EINVAL; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci pci_write_config_word(adapter->pdev, A_PCICFG_VPD_ADDR, (u16)addr); 57562306a36Sopenharmony_ci do { 57662306a36Sopenharmony_ci udelay(50); 57762306a36Sopenharmony_ci pci_read_config_word(adapter->pdev, A_PCICFG_VPD_ADDR, &val); 57862306a36Sopenharmony_ci } while (!(val & F_VPD_OP_FLAG) && --i); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (!(val & F_VPD_OP_FLAG)) { 58162306a36Sopenharmony_ci pr_err("%s: reading EEPROM address 0x%x failed\n", 58262306a36Sopenharmony_ci adapter->name, addr); 58362306a36Sopenharmony_ci return -EIO; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci pci_read_config_dword(adapter->pdev, A_PCICFG_VPD_DATA, &v); 58662306a36Sopenharmony_ci *data = cpu_to_le32(v); 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic int t1_eeprom_vpd_get(adapter_t *adapter, struct chelsio_vpd_t *vpd) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci int addr, ret = 0; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci for (addr = 0; !ret && addr < sizeof(*vpd); addr += sizeof(u32)) 59562306a36Sopenharmony_ci ret = t1_seeprom_read(adapter, addr, 59662306a36Sopenharmony_ci (__le32 *)((u8 *)vpd + addr)); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci return ret; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci/* 60262306a36Sopenharmony_ci * Read a port's MAC address from the VPD ROM. 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_cistatic int vpd_macaddress_get(adapter_t *adapter, int index, u8 mac_addr[]) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci struct chelsio_vpd_t vpd; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (t1_eeprom_vpd_get(adapter, &vpd)) 60962306a36Sopenharmony_ci return 1; 61062306a36Sopenharmony_ci memcpy(mac_addr, vpd.mac_base_address, 5); 61162306a36Sopenharmony_ci mac_addr[5] = vpd.mac_base_address[5] + index; 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci/* 61662306a36Sopenharmony_ci * Set up the MAC/PHY according to the requested link settings. 61762306a36Sopenharmony_ci * 61862306a36Sopenharmony_ci * If the PHY can auto-negotiate first decide what to advertise, then 61962306a36Sopenharmony_ci * enable/disable auto-negotiation as desired and reset. 62062306a36Sopenharmony_ci * 62162306a36Sopenharmony_ci * If the PHY does not auto-negotiate we just reset it. 62262306a36Sopenharmony_ci * 62362306a36Sopenharmony_ci * If auto-negotiation is off set the MAC to the proper speed/duplex/FC, 62462306a36Sopenharmony_ci * otherwise do it later based on the outcome of auto-negotiation. 62562306a36Sopenharmony_ci */ 62662306a36Sopenharmony_ciint t1_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci unsigned int fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (lc->supported & SUPPORTED_Autoneg) { 63162306a36Sopenharmony_ci lc->advertising &= ~(ADVERTISED_ASYM_PAUSE | ADVERTISED_PAUSE); 63262306a36Sopenharmony_ci if (fc) { 63362306a36Sopenharmony_ci if (fc == ((PAUSE_RX | PAUSE_TX) & 63462306a36Sopenharmony_ci (mac->adapter->params.nports < 2))) 63562306a36Sopenharmony_ci lc->advertising |= ADVERTISED_PAUSE; 63662306a36Sopenharmony_ci else { 63762306a36Sopenharmony_ci lc->advertising |= ADVERTISED_ASYM_PAUSE; 63862306a36Sopenharmony_ci if (fc == PAUSE_RX) 63962306a36Sopenharmony_ci lc->advertising |= ADVERTISED_PAUSE; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci phy->ops->advertise(phy, lc->advertising); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (lc->autoneg == AUTONEG_DISABLE) { 64562306a36Sopenharmony_ci lc->speed = lc->requested_speed; 64662306a36Sopenharmony_ci lc->duplex = lc->requested_duplex; 64762306a36Sopenharmony_ci lc->fc = (unsigned char)fc; 64862306a36Sopenharmony_ci mac->ops->set_speed_duplex_fc(mac, lc->speed, 64962306a36Sopenharmony_ci lc->duplex, fc); 65062306a36Sopenharmony_ci /* Also disables autoneg */ 65162306a36Sopenharmony_ci phy->state = PHY_AUTONEG_RDY; 65262306a36Sopenharmony_ci phy->ops->set_speed_duplex(phy, lc->speed, lc->duplex); 65362306a36Sopenharmony_ci phy->ops->reset(phy, 0); 65462306a36Sopenharmony_ci } else { 65562306a36Sopenharmony_ci phy->state = PHY_AUTONEG_EN; 65662306a36Sopenharmony_ci phy->ops->autoneg_enable(phy); /* also resets PHY */ 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci } else { 65962306a36Sopenharmony_ci phy->state = PHY_AUTONEG_RDY; 66062306a36Sopenharmony_ci mac->ops->set_speed_duplex_fc(mac, -1, -1, fc); 66162306a36Sopenharmony_ci lc->fc = (unsigned char)fc; 66262306a36Sopenharmony_ci phy->ops->reset(phy, 0); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci return 0; 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci/* 66862306a36Sopenharmony_ci * External interrupt handler for boards using elmer0. 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_ciint t1_elmer0_ext_intr_handler(adapter_t *adapter) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct cphy *phy; 67362306a36Sopenharmony_ci int phy_cause; 67462306a36Sopenharmony_ci u32 cause; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci t1_tpi_read(adapter, A_ELMER0_INT_CAUSE, &cause); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci switch (board_info(adapter)->board) { 67962306a36Sopenharmony_ci#ifdef CONFIG_CHELSIO_T1_1G 68062306a36Sopenharmony_ci case CHBT_BOARD_CHT204: 68162306a36Sopenharmony_ci case CHBT_BOARD_CHT204E: 68262306a36Sopenharmony_ci case CHBT_BOARD_CHN204: 68362306a36Sopenharmony_ci case CHBT_BOARD_CHT204V: { 68462306a36Sopenharmony_ci int i, port_bit; 68562306a36Sopenharmony_ci for_each_port(adapter, i) { 68662306a36Sopenharmony_ci port_bit = i + 1; 68762306a36Sopenharmony_ci if (!(cause & (1 << port_bit))) 68862306a36Sopenharmony_ci continue; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci phy = adapter->port[i].phy; 69162306a36Sopenharmony_ci phy_cause = phy->ops->interrupt_handler(phy); 69262306a36Sopenharmony_ci if (phy_cause & cphy_cause_link_change) 69362306a36Sopenharmony_ci t1_link_changed(adapter, i); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci break; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci case CHBT_BOARD_CHT101: 69862306a36Sopenharmony_ci if (cause & ELMER0_GP_BIT1) { /* Marvell 88E1111 interrupt */ 69962306a36Sopenharmony_ci phy = adapter->port[0].phy; 70062306a36Sopenharmony_ci phy_cause = phy->ops->interrupt_handler(phy); 70162306a36Sopenharmony_ci if (phy_cause & cphy_cause_link_change) 70262306a36Sopenharmony_ci t1_link_changed(adapter, 0); 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci break; 70562306a36Sopenharmony_ci case CHBT_BOARD_7500: { 70662306a36Sopenharmony_ci int p; 70762306a36Sopenharmony_ci /* 70862306a36Sopenharmony_ci * Elmer0's interrupt cause isn't useful here because there is 70962306a36Sopenharmony_ci * only one bit that can be set for all 4 ports. This means 71062306a36Sopenharmony_ci * we are forced to check every PHY's interrupt status 71162306a36Sopenharmony_ci * register to see who initiated the interrupt. 71262306a36Sopenharmony_ci */ 71362306a36Sopenharmony_ci for_each_port(adapter, p) { 71462306a36Sopenharmony_ci phy = adapter->port[p].phy; 71562306a36Sopenharmony_ci phy_cause = phy->ops->interrupt_handler(phy); 71662306a36Sopenharmony_ci if (phy_cause & cphy_cause_link_change) 71762306a36Sopenharmony_ci t1_link_changed(adapter, p); 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci break; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci#endif 72262306a36Sopenharmony_ci case CHBT_BOARD_CHT210: 72362306a36Sopenharmony_ci case CHBT_BOARD_N210: 72462306a36Sopenharmony_ci case CHBT_BOARD_N110: 72562306a36Sopenharmony_ci if (cause & ELMER0_GP_BIT6) { /* Marvell 88x2010 interrupt */ 72662306a36Sopenharmony_ci phy = adapter->port[0].phy; 72762306a36Sopenharmony_ci phy_cause = phy->ops->interrupt_handler(phy); 72862306a36Sopenharmony_ci if (phy_cause & cphy_cause_link_change) 72962306a36Sopenharmony_ci t1_link_changed(adapter, 0); 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci break; 73262306a36Sopenharmony_ci case CHBT_BOARD_8000: 73362306a36Sopenharmony_ci case CHBT_BOARD_CHT110: 73462306a36Sopenharmony_ci if (netif_msg_intr(adapter)) 73562306a36Sopenharmony_ci dev_dbg(&adapter->pdev->dev, 73662306a36Sopenharmony_ci "External interrupt cause 0x%x\n", cause); 73762306a36Sopenharmony_ci if (cause & ELMER0_GP_BIT1) { /* PMC3393 INTB */ 73862306a36Sopenharmony_ci struct cmac *mac = adapter->port[0].mac; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci mac->ops->interrupt_handler(mac); 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci if (cause & ELMER0_GP_BIT5) { /* XPAK MOD_DETECT */ 74362306a36Sopenharmony_ci u32 mod_detect; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci t1_tpi_read(adapter, 74662306a36Sopenharmony_ci A_ELMER0_GPI_STAT, &mod_detect); 74762306a36Sopenharmony_ci if (netif_msg_link(adapter)) 74862306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, "XPAK %s\n", 74962306a36Sopenharmony_ci mod_detect ? "removed" : "inserted"); 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci break; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_INT_CAUSE, cause); 75462306a36Sopenharmony_ci return 0; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci/* Enables all interrupts. */ 75862306a36Sopenharmony_civoid t1_interrupts_enable(adapter_t *adapter) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci unsigned int i; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci adapter->slow_intr_mask = F_PL_INTR_SGE_ERR | F_PL_INTR_TP; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci t1_sge_intr_enable(adapter->sge); 76562306a36Sopenharmony_ci t1_tp_intr_enable(adapter->tp); 76662306a36Sopenharmony_ci if (adapter->espi) { 76762306a36Sopenharmony_ci adapter->slow_intr_mask |= F_PL_INTR_ESPI; 76862306a36Sopenharmony_ci t1_espi_intr_enable(adapter->espi); 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci /* Enable MAC/PHY interrupts for each port. */ 77262306a36Sopenharmony_ci for_each_port(adapter, i) { 77362306a36Sopenharmony_ci adapter->port[i].mac->ops->interrupt_enable(adapter->port[i].mac); 77462306a36Sopenharmony_ci adapter->port[i].phy->ops->interrupt_enable(adapter->port[i].phy); 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* Enable PCIX & external chip interrupts on ASIC boards. */ 77862306a36Sopenharmony_ci if (t1_is_asic(adapter)) { 77962306a36Sopenharmony_ci u32 pl_intr = readl(adapter->regs + A_PL_ENABLE); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* PCI-X interrupts */ 78262306a36Sopenharmony_ci pci_write_config_dword(adapter->pdev, A_PCICFG_INTR_ENABLE, 78362306a36Sopenharmony_ci 0xffffffff); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci adapter->slow_intr_mask |= F_PL_INTR_EXT | F_PL_INTR_PCIX; 78662306a36Sopenharmony_ci pl_intr |= F_PL_INTR_EXT | F_PL_INTR_PCIX; 78762306a36Sopenharmony_ci writel(pl_intr, adapter->regs + A_PL_ENABLE); 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci/* Disables all interrupts. */ 79262306a36Sopenharmony_civoid t1_interrupts_disable(adapter_t* adapter) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci unsigned int i; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci t1_sge_intr_disable(adapter->sge); 79762306a36Sopenharmony_ci t1_tp_intr_disable(adapter->tp); 79862306a36Sopenharmony_ci if (adapter->espi) 79962306a36Sopenharmony_ci t1_espi_intr_disable(adapter->espi); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* Disable MAC/PHY interrupts for each port. */ 80262306a36Sopenharmony_ci for_each_port(adapter, i) { 80362306a36Sopenharmony_ci adapter->port[i].mac->ops->interrupt_disable(adapter->port[i].mac); 80462306a36Sopenharmony_ci adapter->port[i].phy->ops->interrupt_disable(adapter->port[i].phy); 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* Disable PCIX & external chip interrupts. */ 80862306a36Sopenharmony_ci if (t1_is_asic(adapter)) 80962306a36Sopenharmony_ci writel(0, adapter->regs + A_PL_ENABLE); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci /* PCI-X interrupts */ 81262306a36Sopenharmony_ci pci_write_config_dword(adapter->pdev, A_PCICFG_INTR_ENABLE, 0); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci adapter->slow_intr_mask = 0; 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci/* Clears all interrupts */ 81862306a36Sopenharmony_civoid t1_interrupts_clear(adapter_t* adapter) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci unsigned int i; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci t1_sge_intr_clear(adapter->sge); 82362306a36Sopenharmony_ci t1_tp_intr_clear(adapter->tp); 82462306a36Sopenharmony_ci if (adapter->espi) 82562306a36Sopenharmony_ci t1_espi_intr_clear(adapter->espi); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* Clear MAC/PHY interrupts for each port. */ 82862306a36Sopenharmony_ci for_each_port(adapter, i) { 82962306a36Sopenharmony_ci adapter->port[i].mac->ops->interrupt_clear(adapter->port[i].mac); 83062306a36Sopenharmony_ci adapter->port[i].phy->ops->interrupt_clear(adapter->port[i].phy); 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* Enable interrupts for external devices. */ 83462306a36Sopenharmony_ci if (t1_is_asic(adapter)) { 83562306a36Sopenharmony_ci u32 pl_intr = readl(adapter->regs + A_PL_CAUSE); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci writel(pl_intr | F_PL_INTR_EXT | F_PL_INTR_PCIX, 83862306a36Sopenharmony_ci adapter->regs + A_PL_CAUSE); 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* PCI-X interrupts */ 84262306a36Sopenharmony_ci pci_write_config_dword(adapter->pdev, A_PCICFG_INTR_CAUSE, 0xffffffff); 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci/* 84662306a36Sopenharmony_ci * Slow path interrupt handler for ASICs. 84762306a36Sopenharmony_ci */ 84862306a36Sopenharmony_cistatic irqreturn_t asic_slow_intr(adapter_t *adapter) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci u32 cause = readl(adapter->regs + A_PL_CAUSE); 85162306a36Sopenharmony_ci irqreturn_t ret = IRQ_HANDLED; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci cause &= adapter->slow_intr_mask; 85462306a36Sopenharmony_ci if (!cause) 85562306a36Sopenharmony_ci return IRQ_NONE; 85662306a36Sopenharmony_ci if (cause & F_PL_INTR_SGE_ERR) { 85762306a36Sopenharmony_ci if (t1_sge_intr_error_handler(adapter->sge)) 85862306a36Sopenharmony_ci ret = IRQ_WAKE_THREAD; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci if (cause & F_PL_INTR_TP) 86162306a36Sopenharmony_ci t1_tp_intr_handler(adapter->tp); 86262306a36Sopenharmony_ci if (cause & F_PL_INTR_ESPI) 86362306a36Sopenharmony_ci t1_espi_intr_handler(adapter->espi); 86462306a36Sopenharmony_ci if (cause & F_PL_INTR_PCIX) { 86562306a36Sopenharmony_ci if (t1_pci_intr_handler(adapter)) 86662306a36Sopenharmony_ci ret = IRQ_WAKE_THREAD; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci if (cause & F_PL_INTR_EXT) { 86962306a36Sopenharmony_ci /* Wake the threaded interrupt to handle external interrupts as 87062306a36Sopenharmony_ci * we require a process context. We disable EXT interrupts in 87162306a36Sopenharmony_ci * the interim and let the thread reenable them when it's done. 87262306a36Sopenharmony_ci */ 87362306a36Sopenharmony_ci adapter->pending_thread_intr |= F_PL_INTR_EXT; 87462306a36Sopenharmony_ci adapter->slow_intr_mask &= ~F_PL_INTR_EXT; 87562306a36Sopenharmony_ci writel(adapter->slow_intr_mask | F_PL_INTR_SGE_DATA, 87662306a36Sopenharmony_ci adapter->regs + A_PL_ENABLE); 87762306a36Sopenharmony_ci ret = IRQ_WAKE_THREAD; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* Clear the interrupts just processed. */ 88162306a36Sopenharmony_ci writel(cause, adapter->regs + A_PL_CAUSE); 88262306a36Sopenharmony_ci readl(adapter->regs + A_PL_CAUSE); /* flush writes */ 88362306a36Sopenharmony_ci return ret; 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ciirqreturn_t t1_slow_intr_handler(adapter_t *adapter) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci#ifdef CONFIG_CHELSIO_T1_1G 88962306a36Sopenharmony_ci if (!t1_is_asic(adapter)) 89062306a36Sopenharmony_ci return fpga_slow_intr(adapter); 89162306a36Sopenharmony_ci#endif 89262306a36Sopenharmony_ci return asic_slow_intr(adapter); 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci/* Power sequencing is a work-around for Intel's XPAKs. */ 89662306a36Sopenharmony_cistatic void power_sequence_xpak(adapter_t* adapter) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci u32 mod_detect; 89962306a36Sopenharmony_ci u32 gpo; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci /* Check for XPAK */ 90262306a36Sopenharmony_ci t1_tpi_read(adapter, A_ELMER0_GPI_STAT, &mod_detect); 90362306a36Sopenharmony_ci if (!(ELMER0_GP_BIT5 & mod_detect)) { 90462306a36Sopenharmony_ci /* XPAK is present */ 90562306a36Sopenharmony_ci t1_tpi_read(adapter, A_ELMER0_GPO, &gpo); 90662306a36Sopenharmony_ci gpo |= ELMER0_GP_BIT18; 90762306a36Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_GPO, gpo); 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ciint t1_get_board_rev(adapter_t *adapter, const struct board_info *bi, 91262306a36Sopenharmony_ci struct adapter_params *p) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci p->chip_version = bi->chip_term; 91562306a36Sopenharmony_ci p->is_asic = (p->chip_version != CHBT_TERM_FPGA); 91662306a36Sopenharmony_ci if (p->chip_version == CHBT_TERM_T1 || 91762306a36Sopenharmony_ci p->chip_version == CHBT_TERM_T2 || 91862306a36Sopenharmony_ci p->chip_version == CHBT_TERM_FPGA) { 91962306a36Sopenharmony_ci u32 val = readl(adapter->regs + A_TP_PC_CONFIG); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci val = G_TP_PC_REV(val); 92262306a36Sopenharmony_ci if (val == 2) 92362306a36Sopenharmony_ci p->chip_revision = TERM_T1B; 92462306a36Sopenharmony_ci else if (val == 3) 92562306a36Sopenharmony_ci p->chip_revision = TERM_T2; 92662306a36Sopenharmony_ci else 92762306a36Sopenharmony_ci return -1; 92862306a36Sopenharmony_ci } else 92962306a36Sopenharmony_ci return -1; 93062306a36Sopenharmony_ci return 0; 93162306a36Sopenharmony_ci} 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci/* 93462306a36Sopenharmony_ci * Enable board components other than the Chelsio chip, such as external MAC 93562306a36Sopenharmony_ci * and PHY. 93662306a36Sopenharmony_ci */ 93762306a36Sopenharmony_cistatic int board_init(adapter_t *adapter, const struct board_info *bi) 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci switch (bi->board) { 94062306a36Sopenharmony_ci case CHBT_BOARD_8000: 94162306a36Sopenharmony_ci case CHBT_BOARD_N110: 94262306a36Sopenharmony_ci case CHBT_BOARD_N210: 94362306a36Sopenharmony_ci case CHBT_BOARD_CHT210: 94462306a36Sopenharmony_ci t1_tpi_par(adapter, 0xf); 94562306a36Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_GPO, 0x800); 94662306a36Sopenharmony_ci break; 94762306a36Sopenharmony_ci case CHBT_BOARD_CHT110: 94862306a36Sopenharmony_ci t1_tpi_par(adapter, 0xf); 94962306a36Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_GPO, 0x1800); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci /* TBD XXX Might not need. This fixes a problem 95262306a36Sopenharmony_ci * described in the Intel SR XPAK errata. 95362306a36Sopenharmony_ci */ 95462306a36Sopenharmony_ci power_sequence_xpak(adapter); 95562306a36Sopenharmony_ci break; 95662306a36Sopenharmony_ci#ifdef CONFIG_CHELSIO_T1_1G 95762306a36Sopenharmony_ci case CHBT_BOARD_CHT204E: 95862306a36Sopenharmony_ci /* add config space write here */ 95962306a36Sopenharmony_ci case CHBT_BOARD_CHT204: 96062306a36Sopenharmony_ci case CHBT_BOARD_CHT204V: 96162306a36Sopenharmony_ci case CHBT_BOARD_CHN204: 96262306a36Sopenharmony_ci t1_tpi_par(adapter, 0xf); 96362306a36Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_GPO, 0x804); 96462306a36Sopenharmony_ci break; 96562306a36Sopenharmony_ci case CHBT_BOARD_CHT101: 96662306a36Sopenharmony_ci case CHBT_BOARD_7500: 96762306a36Sopenharmony_ci t1_tpi_par(adapter, 0xf); 96862306a36Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_GPO, 0x1804); 96962306a36Sopenharmony_ci break; 97062306a36Sopenharmony_ci#endif 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci return 0; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci/* 97662306a36Sopenharmony_ci * Initialize and configure the Terminator HW modules. Note that external 97762306a36Sopenharmony_ci * MAC and PHYs are initialized separately. 97862306a36Sopenharmony_ci */ 97962306a36Sopenharmony_ciint t1_init_hw_modules(adapter_t *adapter) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci int err = -EIO; 98262306a36Sopenharmony_ci const struct board_info *bi = board_info(adapter); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (!bi->clock_mc4) { 98562306a36Sopenharmony_ci u32 val = readl(adapter->regs + A_MC4_CFG); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci writel(val | F_READY | F_MC4_SLOW, adapter->regs + A_MC4_CFG); 98862306a36Sopenharmony_ci writel(F_M_BUS_ENABLE | F_TCAM_RESET, 98962306a36Sopenharmony_ci adapter->regs + A_MC5_CONFIG); 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (adapter->espi && t1_espi_init(adapter->espi, bi->chip_mac, 99362306a36Sopenharmony_ci bi->espi_nports)) 99462306a36Sopenharmony_ci goto out_err; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci if (t1_tp_reset(adapter->tp, &adapter->params.tp, bi->clock_core)) 99762306a36Sopenharmony_ci goto out_err; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci err = t1_sge_configure(adapter->sge, &adapter->params.sge); 100062306a36Sopenharmony_ci if (err) 100162306a36Sopenharmony_ci goto out_err; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci err = 0; 100462306a36Sopenharmony_ciout_err: 100562306a36Sopenharmony_ci return err; 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci/* 100962306a36Sopenharmony_ci * Determine a card's PCI mode. 101062306a36Sopenharmony_ci */ 101162306a36Sopenharmony_cistatic void get_pci_mode(adapter_t *adapter, struct chelsio_pci_params *p) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci static const unsigned short speed_map[] = { 33, 66, 100, 133 }; 101462306a36Sopenharmony_ci u32 pci_mode; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci pci_read_config_dword(adapter->pdev, A_PCICFG_MODE, &pci_mode); 101762306a36Sopenharmony_ci p->speed = speed_map[G_PCI_MODE_CLK(pci_mode)]; 101862306a36Sopenharmony_ci p->width = (pci_mode & F_PCI_MODE_64BIT) ? 64 : 32; 101962306a36Sopenharmony_ci p->is_pcix = (pci_mode & F_PCI_MODE_PCIX) != 0; 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci/* 102362306a36Sopenharmony_ci * Release the structures holding the SW per-Terminator-HW-module state. 102462306a36Sopenharmony_ci */ 102562306a36Sopenharmony_civoid t1_free_sw_modules(adapter_t *adapter) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci unsigned int i; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci for_each_port(adapter, i) { 103062306a36Sopenharmony_ci struct cmac *mac = adapter->port[i].mac; 103162306a36Sopenharmony_ci struct cphy *phy = adapter->port[i].phy; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci if (mac) 103462306a36Sopenharmony_ci mac->ops->destroy(mac); 103562306a36Sopenharmony_ci if (phy) 103662306a36Sopenharmony_ci phy->ops->destroy(phy); 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci if (adapter->sge) 104062306a36Sopenharmony_ci t1_sge_destroy(adapter->sge); 104162306a36Sopenharmony_ci if (adapter->tp) 104262306a36Sopenharmony_ci t1_tp_destroy(adapter->tp); 104362306a36Sopenharmony_ci if (adapter->espi) 104462306a36Sopenharmony_ci t1_espi_destroy(adapter->espi); 104562306a36Sopenharmony_ci} 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_cistatic void init_link_config(struct link_config *lc, 104862306a36Sopenharmony_ci const struct board_info *bi) 104962306a36Sopenharmony_ci{ 105062306a36Sopenharmony_ci lc->supported = bi->caps; 105162306a36Sopenharmony_ci lc->requested_speed = lc->speed = SPEED_INVALID; 105262306a36Sopenharmony_ci lc->requested_duplex = lc->duplex = DUPLEX_INVALID; 105362306a36Sopenharmony_ci lc->requested_fc = lc->fc = PAUSE_RX | PAUSE_TX; 105462306a36Sopenharmony_ci if (lc->supported & SUPPORTED_Autoneg) { 105562306a36Sopenharmony_ci lc->advertising = lc->supported; 105662306a36Sopenharmony_ci lc->autoneg = AUTONEG_ENABLE; 105762306a36Sopenharmony_ci lc->requested_fc |= PAUSE_AUTONEG; 105862306a36Sopenharmony_ci } else { 105962306a36Sopenharmony_ci lc->advertising = 0; 106062306a36Sopenharmony_ci lc->autoneg = AUTONEG_DISABLE; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci/* 106562306a36Sopenharmony_ci * Allocate and initialize the data structures that hold the SW state of 106662306a36Sopenharmony_ci * the Terminator HW modules. 106762306a36Sopenharmony_ci */ 106862306a36Sopenharmony_ciint t1_init_sw_modules(adapter_t *adapter, const struct board_info *bi) 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci unsigned int i; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci adapter->params.brd_info = bi; 107362306a36Sopenharmony_ci adapter->params.nports = bi->port_number; 107462306a36Sopenharmony_ci adapter->params.stats_update_period = bi->gmac->stats_update_period; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci adapter->sge = t1_sge_create(adapter, &adapter->params.sge); 107762306a36Sopenharmony_ci if (!adapter->sge) { 107862306a36Sopenharmony_ci pr_err("%s: SGE initialization failed\n", 107962306a36Sopenharmony_ci adapter->name); 108062306a36Sopenharmony_ci goto error; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci if (bi->espi_nports && !(adapter->espi = t1_espi_create(adapter))) { 108462306a36Sopenharmony_ci pr_err("%s: ESPI initialization failed\n", 108562306a36Sopenharmony_ci adapter->name); 108662306a36Sopenharmony_ci goto error; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci adapter->tp = t1_tp_create(adapter, &adapter->params.tp); 109062306a36Sopenharmony_ci if (!adapter->tp) { 109162306a36Sopenharmony_ci pr_err("%s: TP initialization failed\n", 109262306a36Sopenharmony_ci adapter->name); 109362306a36Sopenharmony_ci goto error; 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci board_init(adapter, bi); 109762306a36Sopenharmony_ci bi->mdio_ops->init(adapter, bi); 109862306a36Sopenharmony_ci if (bi->gphy->reset) 109962306a36Sopenharmony_ci bi->gphy->reset(adapter); 110062306a36Sopenharmony_ci if (bi->gmac->reset) 110162306a36Sopenharmony_ci bi->gmac->reset(adapter); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci for_each_port(adapter, i) { 110462306a36Sopenharmony_ci u8 hw_addr[6]; 110562306a36Sopenharmony_ci struct cmac *mac; 110662306a36Sopenharmony_ci int phy_addr = bi->mdio_phybaseaddr + i; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci adapter->port[i].phy = bi->gphy->create(adapter->port[i].dev, 110962306a36Sopenharmony_ci phy_addr, bi->mdio_ops); 111062306a36Sopenharmony_ci if (!adapter->port[i].phy) { 111162306a36Sopenharmony_ci pr_err("%s: PHY %d initialization failed\n", 111262306a36Sopenharmony_ci adapter->name, i); 111362306a36Sopenharmony_ci goto error; 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci adapter->port[i].mac = mac = bi->gmac->create(adapter, i); 111762306a36Sopenharmony_ci if (!mac) { 111862306a36Sopenharmony_ci pr_err("%s: MAC %d initialization failed\n", 111962306a36Sopenharmony_ci adapter->name, i); 112062306a36Sopenharmony_ci goto error; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci /* 112462306a36Sopenharmony_ci * Get the port's MAC addresses either from the EEPROM if one 112562306a36Sopenharmony_ci * exists or the one hardcoded in the MAC. 112662306a36Sopenharmony_ci */ 112762306a36Sopenharmony_ci if (!t1_is_asic(adapter) || bi->chip_mac == CHBT_MAC_DUMMY) 112862306a36Sopenharmony_ci mac->ops->macaddress_get(mac, hw_addr); 112962306a36Sopenharmony_ci else if (vpd_macaddress_get(adapter, i, hw_addr)) { 113062306a36Sopenharmony_ci pr_err("%s: could not read MAC address from VPD ROM\n", 113162306a36Sopenharmony_ci adapter->port[i].dev->name); 113262306a36Sopenharmony_ci goto error; 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci eth_hw_addr_set(adapter->port[i].dev, hw_addr); 113562306a36Sopenharmony_ci init_link_config(&adapter->port[i].link_config, bi); 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci get_pci_mode(adapter, &adapter->params.pci); 113962306a36Sopenharmony_ci t1_interrupts_clear(adapter); 114062306a36Sopenharmony_ci return 0; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_cierror: 114362306a36Sopenharmony_ci t1_free_sw_modules(adapter); 114462306a36Sopenharmony_ci return -1; 114562306a36Sopenharmony_ci} 1146