t3_hw.c

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

C
2,149
字号
/* * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses.  You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * *     Redistribution and use in source and binary forms, with or *     without modification, are permitted provided that the following *     conditions are met: * *      - Redistributions of source code must retain the above *        copyright notice, this list of conditions and the following *        disclaimer. * *      - Redistributions in binary form must reproduce the above *        copyright notice, this list of conditions and the following *        disclaimer in the documentation and/or other materials *        provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */#include "common.h"#include "regs.h"#include "sge_defs.h"#include "firmware_exports.h"/** *	t3_wait_op_done_val - 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 *	@valp: where to store the value of the register at completion time * *	Wait until an operation is completed by checking a bit in a register *	up to @attempts times.  If @valp is not NULL the value of the register *	at the time it indicated completion is stored there.  Returns 0 if the *	operation completes and -EAGAIN otherwise. */int t3_wait_op_done_val(struct adapter *adapter, int reg, u32 mask,			int polarity, int attempts, int delay, u32 *valp){	while (1) {		u32 val = t3_read_reg(adapter, reg);		if (!!(val & mask) == polarity) {			if (valp)				*valp = val;			return 0;		}		if (--attempts == 0) 			return -EAGAIN;		if (delay)			udelay(delay);	}}/** *	t3_write_regs - write a bunch of registers *	@adapter: the adapter to program *	@p: an array of register address/register value pairs *	@n: the number of address/value pairs *	@offset: register address offset * *	Takes an array of register address/register value pairs and writes each *	value to the corresponding register.  Register addresses are adjusted *	by the supplied offset. */void t3_write_regs(struct adapter *adapter, const struct addr_val_pair *p,		   int n, unsigned int offset){	while (n--) {		t3_write_reg(adapter, p->reg_addr + offset, p->val);		p++;	}}/** *	t3_set_reg_field - set a register field to a value *	@adapter: the adapter to program *	@addr: the register address *	@mask: specifies the portion of the register to modify *	@val: the new value for the register field * *	Sets a register field specified by the supplied mask to the *	given value. */void t3_set_reg_field(struct adapter *adapter, unsigned int addr, u32 mask,		      u32 val){	u32 v = t3_read_reg(adapter, addr) & ~mask;	t3_write_reg(adapter, addr, v | val);	t3_read_reg(adapter, addr);	/* flush */}/** *	t3_read_indirect - read indirectly addressed registers *	@adap: the adapter *	@addr_reg: register holding the indirect address *	@data_reg: register holding the value of the indirect register *	@vals: where the read register values are stored *	@start_idx: index of first indirect register to read *	@nregs: how many indirect registers to read * *	Reads registers that are accessed indirectly through an address/data *	register pair. */static void t3_read_indirect(struct adapter *adap, unsigned int addr_reg,			     unsigned int data_reg, u32 *vals,			     unsigned int nregs, unsigned int start_idx){	while (nregs--) {		t3_write_reg(adap, addr_reg, start_idx);		*vals++ = t3_read_reg(adap, data_reg);		start_idx++;	}}/** *	t3_mc7_bd_read - read from MC7 through backdoor accesses *	@mc7: identifies MC7 to read from *	@start: index of first 64-bit word to read *	@n: number of 64-bit words to read *	@buf: where to store the read result * *	Read n 64-bit words from MC7 starting at word start, using backdoor *	accesses. */int t3_mc7_bd_read(struct mc7 *mc7, unsigned int start, unsigned int n,		   u64 *buf){	static const int shift[] = { 0, 0, 16, 24 };	static const int step[] = { 0, 32, 16, 8 };	unsigned int size64 = mc7->size / 8;	/* # of 64-bit words */	struct adapter *adap = mc7->adapter;	if (start >= size64 || start + n > size64)		return -EINVAL;	start *= (8 << mc7->width);	while (n--) {		int i;		u64 val64 = 0;		for (i = (1 << mc7->width) - 1; i >= 0; --i) {			int attempts = 10;			u32 val;			t3_write_reg(adap, mc7->offset + A_MC7_BD_ADDR, start);			t3_write_reg(adap, mc7->offset + A_MC7_BD_OP, 0);			val = t3_read_reg(adap, mc7->offset + A_MC7_BD_OP);			while ((val & F_BUSY) && attempts--)				val = t3_read_reg(adap,						  mc7->offset + A_MC7_BD_OP);			if (val & F_BUSY)				return -EIO;			val = t3_read_reg(adap, mc7->offset + A_MC7_BD_DATA1);			if (mc7->width == 0) {				val64 = t3_read_reg(adap,						    mc7->offset +						    A_MC7_BD_DATA0);				val64 |= (u64) val << 32;			} else {				if (mc7->width > 1)					val >>= shift[mc7->width];				val64 |= (u64) val << (step[mc7->width] * i);			}			start += 8;		}		*buf++ = val64;	}	return 0;}/* * Initialize MI1. */static void mi1_init(struct adapter *adap, const struct adapter_info *ai){	u32 clkdiv = adap->params.vpd.cclk / (2 * adap->params.vpd.mdc) - 1;	u32 val = F_PREEN | V_MDIINV(ai->mdiinv) | V_MDIEN(ai->mdien) |	    V_CLKDIV(clkdiv);	if (!(ai->caps & SUPPORTED_10000baseT_Full))		val |= V_ST(1);	t3_write_reg(adap, A_MI1_CFG, val);}#define MDIO_ATTEMPTS 10/* * MI1 read/write operations for direct-addressed PHYs. */static int mi1_read(struct adapter *adapter, int phy_addr, int mmd_addr,		    int reg_addr, unsigned int *valp){	int ret;	u32 addr = V_REGADDR(reg_addr) | V_PHYADDR(phy_addr);	if (mmd_addr)		return -EINVAL;	mutex_lock(&adapter->mdio_lock);	t3_write_reg(adapter, A_MI1_ADDR, addr);	t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(2));	ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, MDIO_ATTEMPTS, 20);	if (!ret)		*valp = t3_read_reg(adapter, A_MI1_DATA);	mutex_unlock(&adapter->mdio_lock);	return ret;}static int mi1_write(struct adapter *adapter, int phy_addr, int mmd_addr,		     int reg_addr, unsigned int val){	int ret;	u32 addr = V_REGADDR(reg_addr) | V_PHYADDR(phy_addr);	if (mmd_addr)		return -EINVAL;	mutex_lock(&adapter->mdio_lock);	t3_write_reg(adapter, A_MI1_ADDR, addr);	t3_write_reg(adapter, A_MI1_DATA, val);	t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(1));	ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, MDIO_ATTEMPTS, 20);	mutex_unlock(&adapter->mdio_lock);	return ret;}static const struct mdio_ops mi1_mdio_ops = {	mi1_read,	mi1_write};/* * MI1 read/write operations for indirect-addressed PHYs. */static int mi1_ext_read(struct adapter *adapter, int phy_addr, int mmd_addr,			int reg_addr, unsigned int *valp){	int ret;	u32 addr = V_REGADDR(mmd_addr) | V_PHYADDR(phy_addr);	mutex_lock(&adapter->mdio_lock);	t3_write_reg(adapter, A_MI1_ADDR, addr);	t3_write_reg(adapter, A_MI1_DATA, reg_addr);	t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(0));	ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, MDIO_ATTEMPTS, 20);	if (!ret) {		t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(3));		ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0,				      MDIO_ATTEMPTS, 20);		if (!ret)			*valp = t3_read_reg(adapter, A_MI1_DATA);	}	mutex_unlock(&adapter->mdio_lock);	return ret;}static int mi1_ext_write(struct adapter *adapter, int phy_addr, int mmd_addr,			 int reg_addr, unsigned int val){	int ret;	u32 addr = V_REGADDR(mmd_addr) | V_PHYADDR(phy_addr);	mutex_lock(&adapter->mdio_lock);	t3_write_reg(adapter, A_MI1_ADDR, addr);	t3_write_reg(adapter, A_MI1_DATA, reg_addr);	t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(0));	ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, MDIO_ATTEMPTS, 20);	if (!ret) {		t3_write_reg(adapter, A_MI1_DATA, val);		t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(1));		ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0,				      MDIO_ATTEMPTS, 20);	}	mutex_unlock(&adapter->mdio_lock);	return ret;}static const struct mdio_ops mi1_mdio_ext_ops = {	mi1_ext_read,	mi1_ext_write};/** *	t3_mdio_change_bits - modify the value of a PHY register *	@phy: the PHY to operate on *	@mmd: the device address *	@reg: the register address *	@clear: what part of the register value to mask off *	@set: what part of the register value to set * *	Changes the value of a PHY register by applying a mask to its current *	value and ORing the result with a new value. */int t3_mdio_change_bits(struct cphy *phy, int mmd, int reg, unsigned int clear,			unsigned int set){	int ret;	unsigned int val;	ret = mdio_read(phy, mmd, reg, &val);	if (!ret) {		val &= ~clear;		ret = mdio_write(phy, mmd, reg, val | set);	}	return ret;}/** *	t3_phy_reset - reset a PHY block *	@phy: the PHY to operate on *	@mmd: the device address of the PHY block to reset *	@wait: how long to wait for the reset to complete in 1ms increments * *	Resets a PHY block and optionally waits for the reset to complete. *	@mmd should be 0 for 10/100/1000 PHYs and the device address to reset *	for 10G PHYs. */int t3_phy_reset(struct cphy *phy, int mmd, int wait){	int err;	unsigned int ctl;	err = t3_mdio_change_bits(phy, mmd, MII_BMCR, BMCR_PDOWN, BMCR_RESET);	if (err || !wait)		return err;	do {		err = mdio_read(phy, mmd, MII_BMCR, &ctl);		if (err)			return err;		ctl &= BMCR_RESET;		if (ctl)			msleep(1);	} while (ctl && --wait);	return ctl ? -1 : 0;}/** *	t3_phy_advertise - set the PHY advertisement registers for autoneg *	@phy: the PHY to operate on *	@advert: bitmap of capabilities the PHY should advertise * *	Sets a 10/100/1000 PHY's advertisement registers to advertise the *	requested capabilities. */int t3_phy_advertise(struct cphy *phy, unsigned int advert){	int err;	unsigned int val = 0;	err = mdio_read(phy, 0, MII_CTRL1000, &val);	if (err)		return err;	val &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);	if (advert & ADVERTISED_1000baseT_Half)		val |= ADVERTISE_1000HALF;	if (advert & ADVERTISED_1000baseT_Full)		val |= ADVERTISE_1000FULL;	err = mdio_write(phy, 0, MII_CTRL1000, val);	if (err)		return err;	val = 1;	if (advert & ADVERTISED_10baseT_Half)		val |= ADVERTISE_10HALF;	if (advert & ADVERTISED_10baseT_Full)		val |= ADVERTISE_10FULL;	if (advert & ADVERTISED_100baseT_Half)		val |= ADVERTISE_100HALF;	if (advert & ADVERTISED_100baseT_Full)		val |= ADVERTISE_100FULL;	if (advert & ADVERTISED_Pause)		val |= ADVERTISE_PAUSE_CAP;	if (advert & ADVERTISED_Asym_Pause)		val |= ADVERTISE_PAUSE_ASYM;	return mdio_write(phy, 0, MII_ADVERTISE, val);}/** *	t3_set_phy_speed_duplex - force PHY speed and duplex *	@phy: the PHY to operate on *	@speed: requested PHY speed *	@duplex: requested PHY duplex * *	Force a 10/100/1000 PHY's speed and duplex.  This also disables *	auto-negotiation except for GigE, where auto-negotiation is mandatory. */int t3_set_phy_speed_duplex(struct cphy *phy, int speed, int duplex){	int err;	unsigned int ctl;	err = mdio_read(phy, 0, MII_BMCR, &ctl);	if (err)		return err;	if (speed >= 0) {		ctl &= ~(BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE);		if (speed == SPEED_100)			ctl |= BMCR_SPEED100;		else if (speed == SPEED_1000)			ctl |= BMCR_SPEED1000;	}	if (duplex >= 0) {		ctl &= ~(BMCR_FULLDPLX | BMCR_ANENABLE);		if (duplex == DUPLEX_FULL)			ctl |= BMCR_FULLDPLX;

⌨️ 快捷键说明

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