subr.c

来自「linux 内核源代码」· C语言 代码 · 共 1,173 行 · 第 1/3 页

C
1,173
字号
/***************************************************************************** *                                                                           * * File: subr.c                                                              * * $Revision: 1.27 $                                                         * * $Date: 2005/06/22 01:08:36 $                                              * * Description:                                                              * *  Various subroutines (intr,pio,etc.) used by Chelsio 10G Ethernet driver. * *  part of the Chelsio 10Gb Ethernet Driver.                                * *                                                                           * * This program is free software; you can redistribute it and/or modify      * * it under the terms of the GNU General Public License, version 2, as       * * published by the Free Software Foundation.                                * *                                                                           * * You should have received a copy of the GNU General Public License along   * * with this program; if not, write to the Free Software Foundation, Inc.,   * * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.                 * *                                                                           * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED    * * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF      * * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.                     * *                                                                           * * http://www.chelsio.com                                                    * *                                                                           * * Copyright (c) 2003 - 2005 Chelsio Communications, Inc.                    * * All rights reserved.                                                      * *                                                                           * * Maintainers: maintainers@chelsio.com                                      * *                                                                           * * Authors: Dimitrios Michailidis   <dm@chelsio.com>                         * *          Tina Yang               <tainay@chelsio.com>                     * *          Felix Marti             <felix@chelsio.com>                      * *          Scott Bardone           <sbardone@chelsio.com>                   * *          Kurt Ottaway            <kottaway@chelsio.com>                   * *          Frank DiMambro          <frank@chelsio.com>                      * *                                                                           * * History:                                                                  * *                                                                           * ****************************************************************************/#include "common.h"#include "elmer0.h"#include "regs.h"#include "gmac.h"#include "cphy.h"#include "sge.h"#include "tp.h"#include "espi.h"/** *	t1_wait_op_done - wait until an operation is completed *	@adapter: the adapter performing the operation *	@reg: the register to check for completion *	@mask: a single-bit field within @reg that indicates completion *	@polarity: the value of the field when the operation is completed *	@attempts: number of check iterations *      @delay: delay in usecs between iterations * *	Wait until an operation is completed by checking a bit in a register *	up to @attempts times.  Returns %0 if the operation completes and %1 *	otherwise. */static int t1_wait_op_done(adapter_t *adapter, int reg, u32 mask, int polarity,			   int attempts, int delay){	while (1) {		u32 val = readl(adapter->regs + reg) & mask;		if (!!val == polarity)			return 0;		if (--attempts == 0)			return 1;		if (delay)			udelay(delay);	}}#define TPI_ATTEMPTS 50/* * Write a register over the TPI interface (unlocked and locked versions). */int __t1_tpi_write(adapter_t *adapter, u32 addr, u32 value){	int tpi_busy;	writel(addr, adapter->regs + A_TPI_ADDR);	writel(value, adapter->regs + A_TPI_WR_DATA);	writel(F_TPIWR, adapter->regs + A_TPI_CSR);	tpi_busy = t1_wait_op_done(adapter, A_TPI_CSR, F_TPIRDY, 1,				   TPI_ATTEMPTS, 3);	if (tpi_busy)		CH_ALERT("%s: TPI write to 0x%x failed\n",			 adapter->name, addr);	return tpi_busy;}int t1_tpi_write(adapter_t *adapter, u32 addr, u32 value){	int ret;	spin_lock(&adapter->tpi_lock);	ret = __t1_tpi_write(adapter, addr, value);	spin_unlock(&adapter->tpi_lock);	return ret;}/* * Read a register over the TPI interface (unlocked and locked versions). */int __t1_tpi_read(adapter_t *adapter, u32 addr, u32 *valp){	int tpi_busy;	writel(addr, adapter->regs + A_TPI_ADDR);	writel(0, adapter->regs + A_TPI_CSR);	tpi_busy = t1_wait_op_done(adapter, A_TPI_CSR, F_TPIRDY, 1,				   TPI_ATTEMPTS, 3);	if (tpi_busy)		CH_ALERT("%s: TPI read from 0x%x failed\n",			 adapter->name, addr);	else		*valp = readl(adapter->regs + A_TPI_RD_DATA);	return tpi_busy;}int t1_tpi_read(adapter_t *adapter, u32 addr, u32 *valp){	int ret;	spin_lock(&adapter->tpi_lock);	ret = __t1_tpi_read(adapter, addr, valp);	spin_unlock(&adapter->tpi_lock);	return ret;}/* * Set a TPI parameter. */static void t1_tpi_par(adapter_t *adapter, u32 value){	writel(V_TPIPAR(value), adapter->regs + A_TPI_PAR);}/* * Called when a port's link settings change to propagate the new values to the * associated PHY and MAC.  After performing the common tasks it invokes an * OS-specific handler. */void t1_link_changed(adapter_t *adapter, int port_id){	int link_ok, speed, duplex, fc;	struct cphy *phy = adapter->port[port_id].phy;	struct link_config *lc = &adapter->port[port_id].link_config;	phy->ops->get_link_status(phy, &link_ok, &speed, &duplex, &fc);	lc->speed = speed < 0 ? SPEED_INVALID : speed;	lc->duplex = duplex < 0 ? DUPLEX_INVALID : duplex;	if (!(lc->requested_fc & PAUSE_AUTONEG))		fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX);	if (link_ok && speed >= 0 && lc->autoneg == AUTONEG_ENABLE) {		/* Set MAC speed, duplex, and flow control to match PHY. */		struct cmac *mac = adapter->port[port_id].mac;		mac->ops->set_speed_duplex_fc(mac, speed, duplex, fc);		lc->fc = (unsigned char)fc;	}	t1_link_negotiated(adapter, port_id, link_ok, speed, duplex, fc);}static int t1_pci_intr_handler(adapter_t *adapter){	u32 pcix_cause;	pci_read_config_dword(adapter->pdev, A_PCICFG_INTR_CAUSE, &pcix_cause);	if (pcix_cause) {		pci_write_config_dword(adapter->pdev, A_PCICFG_INTR_CAUSE,				       pcix_cause);		t1_fatal_err(adapter);    /* PCI errors are fatal */	}	return 0;}#ifdef CONFIG_CHELSIO_T1_COUGAR#include "cspi.h"#endif#ifdef CONFIG_CHELSIO_T1_1G#include "fpga_defs.h"/* * PHY interrupt handler for FPGA boards. */static int fpga_phy_intr_handler(adapter_t *adapter){	int p;	u32 cause = readl(adapter->regs + FPGA_GMAC_ADDR_INTERRUPT_CAUSE);	for_each_port(adapter, p)		if (cause & (1 << p)) {			struct cphy *phy = adapter->port[p].phy;			int phy_cause = phy->ops->interrupt_handler(phy);			if (phy_cause & cphy_cause_link_change)				t1_link_changed(adapter, p);		}	writel(cause, adapter->regs + FPGA_GMAC_ADDR_INTERRUPT_CAUSE);	return 0;}/* * Slow path interrupt handler for FPGAs. */static int fpga_slow_intr(adapter_t *adapter){	u32 cause = readl(adapter->regs + A_PL_CAUSE);	cause &= ~F_PL_INTR_SGE_DATA;	if (cause & F_PL_INTR_SGE_ERR)		t1_sge_intr_error_handler(adapter->sge);	if (cause & FPGA_PCIX_INTERRUPT_GMAC)		fpga_phy_intr_handler(adapter);	if (cause & FPGA_PCIX_INTERRUPT_TP) {		/*		 * FPGA doesn't support MC4 interrupts and it requires		 * this odd layer of indirection for MC5.		 */		u32 tp_cause = readl(adapter->regs + FPGA_TP_ADDR_INTERRUPT_CAUSE);		/* Clear TP interrupt */		writel(tp_cause, adapter->regs + FPGA_TP_ADDR_INTERRUPT_CAUSE);	}	if (cause & FPGA_PCIX_INTERRUPT_PCIX)		t1_pci_intr_handler(adapter);	/* Clear the interrupts just processed. */	if (cause)		writel(cause, adapter->regs + A_PL_CAUSE);	return cause != 0;}#endif/* * Wait until Elmer's MI1 interface is ready for new operations. */static int mi1_wait_until_ready(adapter_t *adapter, int mi1_reg){	int attempts = 100, busy;	do {		u32 val;		__t1_tpi_read(adapter, mi1_reg, &val);		busy = val & F_MI1_OP_BUSY;		if (busy)			udelay(10);	} while (busy && --attempts);	if (busy)		CH_ALERT("%s: MDIO operation timed out\n", adapter->name);	return busy;}/* * MI1 MDIO initialization. */static void mi1_mdio_init(adapter_t *adapter, const struct board_info *bi){	u32 clkdiv = bi->clock_elmer0 / (2 * bi->mdio_mdc) - 1;	u32 val = F_MI1_PREAMBLE_ENABLE | V_MI1_MDI_INVERT(bi->mdio_mdiinv) |		V_MI1_MDI_ENABLE(bi->mdio_mdien) | V_MI1_CLK_DIV(clkdiv);	if (!(bi->caps & SUPPORTED_10000baseT_Full))		val |= V_MI1_SOF(1);	t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_CFG, val);}#if defined(CONFIG_CHELSIO_T1_1G) || defined(CONFIG_CHELSIO_T1_COUGAR)/* * Elmer MI1 MDIO read/write operations. */static int mi1_mdio_read(adapter_t *adapter, int phy_addr, int mmd_addr,			 int reg_addr, unsigned int *valp){	u32 addr = V_MI1_REG_ADDR(reg_addr) | V_MI1_PHY_ADDR(phy_addr);	if (mmd_addr)		return -EINVAL;	spin_lock(&adapter->tpi_lock);	__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_ADDR, addr);	__t1_tpi_write(adapter,			A_ELMER0_PORT0_MI1_OP, MI1_OP_DIRECT_READ);	mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP);	__t1_tpi_read(adapter, A_ELMER0_PORT0_MI1_DATA, valp);	spin_unlock(&adapter->tpi_lock);	return 0;}static int mi1_mdio_write(adapter_t *adapter, int phy_addr, int mmd_addr,			  int reg_addr, unsigned int val){	u32 addr = V_MI1_REG_ADDR(reg_addr) | V_MI1_PHY_ADDR(phy_addr);	if (mmd_addr)		return -EINVAL;	spin_lock(&adapter->tpi_lock);	__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_ADDR, addr);	__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_DATA, val);	__t1_tpi_write(adapter,			A_ELMER0_PORT0_MI1_OP, MI1_OP_DIRECT_WRITE);	mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP);	spin_unlock(&adapter->tpi_lock);	return 0;}#if defined(CONFIG_CHELSIO_T1_1G) || defined(CONFIG_CHELSIO_T1_COUGAR)static const struct mdio_ops mi1_mdio_ops = {	.init = mi1_mdio_init,	.read = mi1_mdio_read,	.write = mi1_mdio_write};#endif#endifstatic int mi1_mdio_ext_read(adapter_t *adapter, int phy_addr, int mmd_addr,			     int reg_addr, unsigned int *valp){	u32 addr = V_MI1_REG_ADDR(mmd_addr) | V_MI1_PHY_ADDR(phy_addr);	spin_lock(&adapter->tpi_lock);	/* Write the address we want. */	__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_ADDR, addr);	__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_DATA, reg_addr);	__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_OP,		       MI1_OP_INDIRECT_ADDRESS);	mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP);	/* Write the operation we want. */	__t1_tpi_write(adapter,			A_ELMER0_PORT0_MI1_OP, MI1_OP_INDIRECT_READ);	mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP);	/* Read the data. */	__t1_tpi_read(adapter, A_ELMER0_PORT0_MI1_DATA, valp);	spin_unlock(&adapter->tpi_lock);	return 0;}static int mi1_mdio_ext_write(adapter_t *adapter, int phy_addr, int mmd_addr,			      int reg_addr, unsigned int val){	u32 addr = V_MI1_REG_ADDR(mmd_addr) | V_MI1_PHY_ADDR(phy_addr);	spin_lock(&adapter->tpi_lock);	/* Write the address we want. */	__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_ADDR, addr);	__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_DATA, reg_addr);	__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_OP,		       MI1_OP_INDIRECT_ADDRESS);	mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP);	/* Write the data. */	__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_DATA, val);	__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_OP, MI1_OP_INDIRECT_WRITE);	mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP);	spin_unlock(&adapter->tpi_lock);	return 0;}static const struct mdio_ops mi1_mdio_ext_ops = {	.init = mi1_mdio_init,	.read = mi1_mdio_ext_read,	.write = mi1_mdio_ext_write};enum {	CH_BRD_T110_1CU,	CH_BRD_N110_1F,	CH_BRD_N210_1F,	CH_BRD_T210_1F,	CH_BRD_T210_1CU,

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?