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 + -
显示快捷键?