/* * Copyright (c) 2003-2008 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 <linux/etherdevice.h> #include"common.h" #include"regs.h" #include"sge_defs.h" #include"firmware_exports.h"
staticvoid t3_port_intr_clear(struct adapter *adapter, int idx);
/** * 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, conststruct addr_val_pair *p, int n, unsignedint 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, unsignedint addr, u32 mask,
u32 val)
{
u32 v = t3_read_reg(adapter, addr) & ~mask;
/** * 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.
*/ staticvoid t3_read_indirect(struct adapter *adap, unsignedint addr_reg, unsignedint data_reg, u32 *vals, unsignedint nregs, unsignedint 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, unsignedint start, unsignedint n,
u64 *buf)
{ staticconstint shift[] = { 0, 0, 16, 24 }; staticconstint step[] = { 0, 32, 16, 8 };
unsignedint 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;
}
/* * Performs the address cycle for clause 45 PHYs. * Must be called with the MDIO_LOCK held.
*/ staticint mi1_wr_addr(struct adapter *adapter, int phy_addr, int mmd_addr, int reg_addr)
{
u32 addr = V_REGADDR(mmd_addr) | V_PHYADDR(phy_addr);
/** * 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, unsignedint clear, unsignedint set)
{ int ret; unsignedint val;
ret = t3_mdio_read(phy, mmd, reg, &val); if (!ret) {
val &= ~clear;
ret = t3_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; unsignedint ctl;
do {
err = t3_mdio_read(phy, mmd, MDIO_CTRL1, &ctl); if (err) return err;
ctl &= MDIO_CTRL1_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, unsignedint advert)
{ int err; unsignedint val = 0;
err = t3_mdio_read(phy, MDIO_DEVAD_NONE, 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 = t3_mdio_write(phy, MDIO_DEVAD_NONE, 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 t3_mdio_write(phy, MDIO_DEVAD_NONE, MII_ADVERTISE, val);
}
/** * t3_phy_advertise_fiber - set fiber PHY advertisement register * @phy: the PHY to operate on * @advert: bitmap of capabilities the PHY should advertise * * Sets a fiber PHY's advertisement register to advertise the * requested capabilities.
*/ int t3_phy_advertise_fiber(struct cphy *phy, unsignedint advert)
{ unsignedint val = 0;
if (advert & ADVERTISED_1000baseT_Half)
val |= ADVERTISE_1000XHALF; if (advert & ADVERTISED_1000baseT_Full)
val |= ADVERTISE_1000XFULL; if (advert & ADVERTISED_Pause)
val |= ADVERTISE_1000XPAUSE; if (advert & ADVERTISED_Asym_Pause)
val |= ADVERTISE_1000XPSE_ASYM; return t3_mdio_write(phy, MDIO_DEVAD_NONE, 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; unsignedint ctl;
err = t3_mdio_read(phy, MDIO_DEVAD_NONE, MII_BMCR, &ctl); if (err) return err;
/* * Return the adapter_info structure with a given index. Out-of-range indices * return NULL.
*/ conststruct adapter_info *t3_get_adapter_info(unsignedint id)
{ return id < ARRAY_SIZE(t3_adap_info) ? &t3_adap_info[id] : NULL;
}
struct port_type_info { int (*phy_prep)(struct cphy *phy, struct adapter *adapter, int phy_addr, conststruct mdio_ops *ops);
};
/** * t3_seeprom_wp - enable/disable EEPROM write protection * @adapter: the adapter * @enable: 1 to enable write protection, 0 to disable it * * Enables or disables write protection on the serial EEPROM.
*/ int t3_seeprom_wp(struct adapter *adapter, int enable)
{
u32 data = enable ? 0xc : 0; int ret;
/* EEPROM_STAT_ADDR is outside VPD area, use pci_write_vpd_any() */
ret = pci_write_vpd_any(adapter->pdev, EEPROM_STAT_ADDR, sizeof(u32),
&data);
/** * get_vpd_params - read VPD parameters from VPD EEPROM * @adapter: adapter to read * @p: where to store the parameters * * Reads card parameters stored in VPD EEPROM.
*/ staticint get_vpd_params(struct adapter *adapter, struct vpd_params *p)
{ struct t3_vpd vpd;
u8 base_val = 0; int addr, ret;
/* * Card information is normally at VPD_BASE but some early cards had * it at 0.
*/
ret = pci_read_vpd(adapter->pdev, VPD_BASE, 1, &base_val); if (ret < 0) return ret;
addr = base_val == PCI_VPD_LRDT_ID_STRING ? VPD_BASE : 0;
ret = pci_read_vpd(adapter->pdev, addr, sizeof(vpd), &vpd); if (ret < 0) return ret;
ret = vpdstrtouint(vpd.cclk_data, vpd.cclk_len, 10, &p->cclk); if (ret) return ret;
ret = vpdstrtouint(vpd.mclk_data, vpd.mclk_len, 10, &p->mclk); if (ret) return ret;
ret = vpdstrtouint(vpd.uclk_data, vpd.uclk_len, 10, &p->uclk); if (ret) return ret;
ret = vpdstrtouint(vpd.mdc_data, vpd.mdc_len, 10, &p->mdc); if (ret) return ret;
ret = vpdstrtouint(vpd.mt_data, vpd.mt_len, 10, &p->mem_timing); if (ret) return ret;
memcpy(p->sn, vpd.sn_data, SERNUM_LEN);
/* Old eeproms didn't have port information */ if (adapter->params.rev == 0 && !vpd.port0_data[0]) {
p->port_type[0] = uses_xaui(adapter) ? 1 : 2;
p->port_type[1] = uses_xaui(adapter) ? 6 : 2;
} else {
p->port_type[0] = hex_to_bin(vpd.port0_data[0]);
p->port_type[1] = hex_to_bin(vpd.port1_data[0]);
ret = vpdstrtou16(vpd.xaui0cfg_data, vpd.xaui0cfg_len, 16,
&p->xauicfg[0]); if (ret) return ret;
ret = vpdstrtou16(vpd.xaui1cfg_data, vpd.xaui1cfg_len, 16,
&p->xauicfg[1]); if (ret) return ret;
}
ret = hex2bin(p->eth_base, vpd.na_data, 6); if (ret < 0) return -EINVAL; return 0;
}
/* serial flash and firmware constants */ enum {
SF_ATTEMPTS = 5, /* max retries for SF1 operations */
SF_SEC_SIZE = 64 * 1024, /* serial flash sector size */
SF_SIZE = SF_SEC_SIZE * 8, /* serial flash size */
FW_FLASH_BOOT_ADDR = 0x70000, /* start address of FW in flash */
FW_VERS_ADDR = 0x7fffc, /* flash address holding FW version */
FW_MIN_SIZE = 8 /* at least version and csum */
};
/** * sf1_read - read data from the serial flash * @adapter: the adapter * @byte_cnt: number of bytes to read * @cont: whether another operation will be chained * @valp: where to store the read data * * Reads up to 4 bytes of data from the serial flash. The location of * the read needs to be specified prior to calling this by issuing the * appropriate commands to the serial flash.
*/ staticint sf1_read(struct adapter *adapter, unsignedint byte_cnt, int cont,
u32 *valp)
{ int ret;
if (!byte_cnt || byte_cnt > 4) return -EINVAL; if (t3_read_reg(adapter, A_SF_OP) & F_BUSY) return -EBUSY;
t3_write_reg(adapter, A_SF_OP, V_CONT(cont) | V_BYTECNT(byte_cnt - 1));
ret = t3_wait_op_done(adapter, A_SF_OP, F_BUSY, 0, SF_ATTEMPTS, 10); if (!ret)
*valp = t3_read_reg(adapter, A_SF_DATA); return ret;
}
/** * sf1_write - write data to the serial flash * @adapter: the adapter * @byte_cnt: number of bytes to write * @cont: whether another operation will be chained * @val: value to write * * Writes up to 4 bytes of data to the serial flash. The location of * the write needs to be specified prior to calling this by issuing the * appropriate commands to the serial flash.
*/ staticint sf1_write(struct adapter *adapter, unsignedint byte_cnt, int cont,
u32 val)
{ if (!byte_cnt || byte_cnt > 4) return -EINVAL; if (t3_read_reg(adapter, A_SF_OP) & F_BUSY) return -EBUSY;
t3_write_reg(adapter, A_SF_DATA, val);
t3_write_reg(adapter, A_SF_OP,
V_CONT(cont) | V_BYTECNT(byte_cnt - 1) | V_OP(1)); return t3_wait_op_done(adapter, A_SF_OP, F_BUSY, 0, SF_ATTEMPTS, 10);
}
/** * flash_wait_op - wait for a flash operation to complete * @adapter: the adapter * @attempts: max number of polls of the status register * @delay: delay between polls in ms * * Wait for a flash operation to complete by polling the status register.
*/ staticint flash_wait_op(struct adapter *adapter, int attempts, int delay)
{ int ret;
u32 status;
while (1) { if ((ret = sf1_write(adapter, 1, 1, SF_RD_STATUS)) != 0 ||
(ret = sf1_read(adapter, 1, 0, &status)) != 0) return ret; if (!(status & 1)) return 0; if (--attempts == 0) return -EAGAIN; if (delay)
msleep(delay);
}
}
/** * t3_read_flash - read words from serial flash * @adapter: the adapter * @addr: the start address for the read * @nwords: how many 32-bit words to read * @data: where to store the read data * @byte_oriented: whether to store data as bytes or as words * * Read the specified number of 32-bit words from the serial flash. * If @byte_oriented is set the read data is stored as a byte array * (i.e., big-endian), otherwise as 32-bit words in the platform's * natural endianness.
*/ staticint t3_read_flash(struct adapter *adapter, unsignedint addr, unsignedint nwords, u32 *data, int byte_oriented)
{ int ret;
for (; nwords; nwords--, data++) {
ret = sf1_read(adapter, 4, nwords > 1, data); if (ret) return ret; if (byte_oriented)
*data = htonl(*data);
} return 0;
}
/** * t3_write_flash - write up to a page of data to the serial flash * @adapter: the adapter * @addr: the start address to write * @n: length of data to write * @data: the data to write * * Writes up to a page of data (256 bytes) to the serial flash starting * at the given address.
*/ staticint t3_write_flash(struct adapter *adapter, unsignedint addr, unsignedint n, const u8 *data)
{ int ret;
u32 buf[64]; unsignedint i, c, left, val, offset = addr & 0xff;
if (addr + n > SF_SIZE || offset + n > 256) return -EINVAL;
for (left = n; left; left -= c) {
c = min(left, 4U); for (val = 0, i = 0; i < c; ++i)
val = (val << 8) + *data++;
ret = sf1_write(adapter, c, c != left, val); if (ret) return ret;
} if ((ret = flash_wait_op(adapter, 5, 1)) != 0) return ret;
/* Read the page to verify the write succeeded */
ret = t3_read_flash(adapter, addr & ~0xff, ARRAY_SIZE(buf), buf, 1); if (ret) return ret;
if (memcmp(data - n, (u8 *) buf + offset, n)) return -EIO; return 0;
}
/** * t3_get_tp_version - read the tp sram version * @adapter: the adapter * @vers: where to place the version * * Reads the protocol sram version from sram.
*/ int t3_get_tp_version(struct adapter *adapter, u32 *vers)
{ int ret;
/* Get version loaded in SRAM */
t3_write_reg(adapter, A_TP_EMBED_OP_FIELD0, 0);
ret = t3_wait_op_done(adapter, A_TP_EMBED_OP_FIELD0,
1, 1, 5, 1); if (ret) return ret;
/** * t3_check_tpsram_version - read the tp sram version * @adapter: the adapter * * Reads the protocol sram version from flash.
*/ int t3_check_tpsram_version(struct adapter *adapter)
{ int ret;
u32 vers; unsignedint major, minor;
if (adapter->params.rev == T3_REV_A) return 0;
ret = t3_get_tp_version(adapter, &vers); if (ret) return ret;
major = G_TP_VERSION_MAJOR(vers);
minor = G_TP_VERSION_MINOR(vers);
if (major == TP_VERSION_MAJOR && minor == TP_VERSION_MINOR) return 0; else {
CH_ERR(adapter, "found wrong TP version (%u.%u), " "driver compiled for version %d.%d\n", major, minor,
TP_VERSION_MAJOR, TP_VERSION_MINOR);
} return -EINVAL;
}
/** * t3_check_tpsram - check if provided protocol SRAM * is compatible with this driver * @adapter: the adapter * @tp_sram: the firmware image to write * @size: image size * * Checks if an adapter's tp sram is compatible with the driver. * Returns 0 if the versions are compatible, a negative error otherwise.
*/ int t3_check_tpsram(struct adapter *adapter, const u8 *tp_sram, unsignedint size)
{
u32 csum; unsignedint i; const __be32 *p = (const __be32 *)tp_sram;
/* Verify checksum */ for (csum = 0, i = 0; i < size / sizeof(csum); i++)
csum += ntohl(p[i]); if (csum != 0xffffffff) {
CH_ERR(adapter, "corrupted protocol SRAM image, checksum %u\n",
csum); return -EINVAL;
}
/** * t3_get_fw_version - read the firmware version * @adapter: the adapter * @vers: where to place the version * * Reads the FW version from flash.
*/ int t3_get_fw_version(struct adapter *adapter, u32 *vers)
{ return t3_read_flash(adapter, FW_VERS_ADDR, 1, vers, 0);
}
/** * t3_check_fw_version - check if the FW is compatible with this driver * @adapter: the adapter * * Checks if an adapter's FW is compatible with the driver. Returns 0 * if the versions are compatible, a negative error otherwise.
*/ int t3_check_fw_version(struct adapter *adapter)
{ int ret;
u32 vers; unsignedint type, major, minor;
ret = t3_get_fw_version(adapter, &vers); if (ret) return ret;
type = G_FW_VERSION_TYPE(vers);
major = G_FW_VERSION_MAJOR(vers);
minor = G_FW_VERSION_MINOR(vers);
if (type == FW_VERSION_T3 && major == FW_VERSION_MAJOR &&
minor == FW_VERSION_MINOR) return 0; elseif (major != FW_VERSION_MAJOR || minor < FW_VERSION_MINOR)
CH_WARN(adapter, "found old FW minor version(%u.%u), " "driver compiled for version %u.%u\n", major, minor,
FW_VERSION_MAJOR, FW_VERSION_MINOR); else {
CH_WARN(adapter, "found newer FW version(%u.%u), " "driver compiled for version %u.%u\n", major, minor,
FW_VERSION_MAJOR, FW_VERSION_MINOR); return 0;
} return -EINVAL;
}
/** * t3_flash_erase_sectors - erase a range of flash sectors * @adapter: the adapter * @start: the first sector to erase * @end: the last sector to erase * * Erases the sectors in the given range.
*/ staticint t3_flash_erase_sectors(struct adapter *adapter, int start, int end)
{ while (start <= end) { int ret;
/** * t3_load_fw - download firmware * @adapter: the adapter * @fw_data: the firmware image to write * @size: image size * * Write the supplied firmware image to the card's serial flash. * The FW image has the following sections: @size - 8 bytes of code and * data, followed by 4 bytes of FW version, followed by the 32-bit * 1's complement checksum of the whole image.
*/ int t3_load_fw(struct adapter *adapter, const u8 *fw_data, unsignedint size)
{
u32 csum; unsignedint i; const __be32 *p = (const __be32 *)fw_data; int ret, addr, fw_sector = FW_FLASH_BOOT_ADDR >> 16;
if ((size & 3) || size < FW_MIN_SIZE) return -EINVAL; if (size > FW_VERS_ADDR + 8 - FW_FLASH_BOOT_ADDR) return -EFBIG;
for (csum = 0, i = 0; i < size / sizeof(csum); i++)
csum += ntohl(p[i]); if (csum != 0xffffffff) {
CH_ERR(adapter, "corrupted firmware image, checksum %u\n",
csum); return -EINVAL;
}
ret = t3_flash_erase_sectors(adapter, fw_sector, fw_sector); if (ret) goto out;
size -= 8; /* trim off version and checksum */ for (addr = FW_FLASH_BOOT_ADDR; size;) { unsignedint chunk_size = min(size, 256U);
ret = t3_write_flash(adapter, addr, chunk_size, fw_data); if (ret) goto out;
ret = t3_write_flash(adapter, FW_VERS_ADDR, 4, fw_data);
out: if (ret)
CH_ERR(adapter, "firmware download failed, error %d\n", ret); return ret;
}
#define CIM_CTL_BASE 0x2000
/** * t3_cim_ctl_blk_read - read a block from CIM control region * * @adap: the adapter * @addr: the start address within the CIM control region * @n: number of words to read * @valp: where to store the result * * Reads a block of 4-byte words from the CIM control region.
*/ int t3_cim_ctl_blk_read(struct adapter *adap, unsignedint addr, unsignedint n, unsignedint *valp)
{ int ret = 0;
if (t3_read_reg(adap, A_CIM_HOST_ACC_CTRL) & F_HOSTBUSY) return -EBUSY;
/** * t3_link_changed - handle interface link changes * @adapter: the adapter * @port_id: the port index that changed link state * * 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 t3_link_changed(struct adapter *adapter, int port_id)
{ int link_ok, speed, duplex, fc; struct port_info *pi = adap2pinfo(adapter, port_id); struct cphy *phy = &pi->phy; struct cmac *mac = &pi->mac; struct link_config *lc = &pi->link_config;
if (link_ok && speed >= 0 && lc->autoneg == AUTONEG_ENABLE) { /* Set MAC speed, duplex, and flow control to match PHY. */
t3_mac_set_speed_duplex_fc(mac, speed, duplex, fc);
lc->fc = fc;
}
/* Account link faults only when the phy reports a link up */ if (link_ok)
mac->stats.link_faults++;
} else { if (link_ok)
t3_write_reg(adapter, A_XGM_XAUI_ACT_CTRL + mac->offset,
F_TXACTENABLE | F_RXEN);
/** * t3_link_start - apply link configuration to MAC/PHY * @phy: the PHY to setup * @mac: the MAC to setup * @lc: the requested link configuration * * Set up a port's MAC and PHY according to a desired link configuration. * - If the PHY can auto-negotiate first decide what to advertise, then * enable/disable auto-negotiation as desired, and reset. * - If the PHY does not auto-negotiate just reset it. * - If auto-negotiation is off set the MAC to the proper speed/duplex/FC, * otherwise do it later based on the outcome of auto-negotiation.
*/ int t3_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc)
{ unsignedint fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX);
lc->link_ok = 0; if (lc->supported & SUPPORTED_Autoneg) {
lc->advertising &= ~(ADVERTISED_Asym_Pause | ADVERTISED_Pause); if (fc) {
lc->advertising |= ADVERTISED_Asym_Pause; if (fc & PAUSE_RX)
lc->advertising |= ADVERTISED_Pause;
}
phy->ops->advertise(phy, lc->advertising);
/** * t3_set_vlan_accel - control HW VLAN extraction * @adapter: the adapter * @ports: bitmap of adapter ports to operate on * @on: enable (1) or disable (0) HW VLAN extraction * * Enables or disables HW extraction of VLAN tags for the given port.
*/ void t3_set_vlan_accel(struct adapter *adapter, unsignedint ports, int on)
{
t3_set_reg_field(adapter, A_TP_OUT_CONFIG,
ports << S_VLANEXTRACTIONENABLE,
on ? (ports << S_VLANEXTRACTIONENABLE) : 0);
}
struct intr_info { unsignedint mask; /* bits to check in interrupt status */ constchar *msg; /* message to print or NULL */ short stat_idx; /* stat counter to increment or -1 */ unsignedshort fatal; /* whether the condition reported is fatal */
};
/** * t3_handle_intr_status - table driven interrupt handler * @adapter: the adapter that generated the interrupt * @reg: the interrupt status register to process * @mask: a mask to apply to the interrupt status * @acts: table of interrupt actions * @stats: statistics counters tracking interrupt occurrences * * A table driven interrupt handler that applies a set of masks to an * interrupt status word and performs the corresponding actions if the * interrupts described by the mask have occurred. The actions include * optionally printing a warning or alert message, and optionally * incrementing a stat counter. The table is terminated by an entry * specifying mask 0. Returns the number of fatal interrupt conditions.
*/ staticint t3_handle_intr_status(struct adapter *adapter, unsignedint reg, unsignedint mask, conststruct intr_info *acts, unsignedlong *stats)
{ int fatal = 0; unsignedint status = t3_read_reg(adapter, reg) & mask;
for (; acts->mask; ++acts) { if (!(status & acts->mask)) continue; if (acts->fatal) {
fatal++;
CH_ALERT(adapter, "%s (0x%x)\n",
acts->msg, status & acts->mask);
status &= ~acts->mask;
} elseif (acts->msg)
CH_WARN(adapter, "%s (0x%x)\n",
acts->msg, status & acts->mask); if (acts->stat_idx >= 0)
stats[acts->stat_idx]++;
} if (status) /* clear processed interrupts */
t3_write_reg(adapter, reg, status); return fatal;
}
#define XGM_INTR_FATAL (V_TXFIFO_PRTY_ERR(M_TXFIFO_PRTY_ERR) | \
V_RXFIFO_PRTY_ERR(M_RXFIFO_PRTY_ERR)) /* * XGMAC interrupt handler.
*/ staticint mac_intr_handler(struct adapter *adap, unsignedint idx)
{ struct cmac *mac = &adap2pinfo(adap, idx)->mac; /* * We mask out interrupt causes for which we're not taking interrupts. * This allows us to use polling logic to monitor some of the other * conditions when taking interrupts would impose too much load on the * system.
*/
u32 cause = t3_read_reg(adap, A_XGM_INT_CAUSE + mac->offset) &
~F_RXFIFO_OVERFLOW;
if (cause & V_TXFIFO_PRTY_ERR(M_TXFIFO_PRTY_ERR)) {
mac->stats.tx_fifo_parity_err++;
CH_ALERT(adap, "port%d: MAC TX FIFO parity error\n", idx);
} if (cause & V_RXFIFO_PRTY_ERR(M_RXFIFO_PRTY_ERR)) {
mac->stats.rx_fifo_parity_err++;
CH_ALERT(adap, "port%d: MAC RX FIFO parity error\n", idx);
} if (cause & F_TXFIFO_UNDERRUN)
mac->stats.tx_fifo_urun++; if (cause & F_RXFIFO_OVERFLOW)
mac->stats.rx_fifo_ovfl++; if (cause & V_SERDES_LOS(M_SERDES_LOS))
mac->stats.serdes_signal_loss++; if (cause & F_XAUIPCSCTCERR)
mac->stats.xaui_pcs_ctc_err++; if (cause & F_XAUIPCSALIGNCHANGE)
mac->stats.xaui_pcs_align_change++; if (cause & F_XGM_INT) {
t3_set_reg_field(adap,
A_XGM_INT_ENABLE + mac->offset,
F_XGM_INT, 0);
mac->stats.link_faults++;
t3_os_link_fault_handler(adap, idx);
}
if (cause & XGM_INTR_FATAL)
t3_fatal_err(adap);
t3_write_reg(adap, A_XGM_INT_CAUSE + mac->offset, cause); return cause != 0;
}
/* * Interrupt handler for PHY events.
*/ int t3_phy_intr_handler(struct adapter *adapter)
{
u32 i, cause = t3_read_reg(adapter, A_T3DBG_INT_CAUSE);
/* * T3 slow path (non-data) interrupt handler.
*/ int t3_slow_intr_handler(struct adapter *adapter)
{
u32 cause = t3_read_reg(adapter, A_PL_INT_CAUSE0);
cause &= adapter->slow_intr_mask; if (!cause) return 0; if (cause & F_PCIM0) { if (is_pcie(adapter))
pcie_intr_handler(adapter); else
pci_intr_handler(adapter);
} if (cause & F_SGE3)
t3_sge_err_intr_handler(adapter); if (cause & F_MC7_PMRX)
mc7_intr_handler(&adapter->pmrx); if (cause & F_MC7_PMTX)
mc7_intr_handler(&adapter->pmtx); if (cause & F_MC7_CM)
mc7_intr_handler(&adapter->cm); if (cause & F_CIM)
cim_intr_handler(adapter); if (cause & F_TP1)
tp_intr_handler(adapter); if (cause & F_ULP2_RX)
ulprx_intr_handler(adapter); if (cause & F_ULP2_TX)
ulptx_intr_handler(adapter); if (cause & F_PM1_RX)
pmrx_intr_handler(adapter); if (cause & F_PM1_TX)
pmtx_intr_handler(adapter); if (cause & F_CPL_SWITCH)
cplsw_intr_handler(adapter); if (cause & F_MPS0)
mps_intr_handler(adapter); if (cause & F_MC5A)
t3_mc5_intr_handler(&adapter->mc5); if (cause & F_XGMAC0_0)
mac_intr_handler(adapter, 0); if (cause & F_XGMAC0_1)
mac_intr_handler(adapter, 1); if (cause & F_T3DBG)
t3_os_ext_intr_handler(adapter);
/* Clear the interrupts just processed. */
t3_write_reg(adapter, A_PL_INT_CAUSE0, cause);
t3_read_reg(adapter, A_PL_INT_CAUSE0); /* flush */ return 1;
}
staticunsignedint calc_gpio_intr(struct adapter *adap)
{ unsignedint i, gpi_intr = 0;
/** * t3_port_intr_enable - enable port-specific interrupts * @adapter: associated adapter * @idx: index of port whose interrupts should be enabled * * Enable port-specific (i.e., MAC and PHY) interrupts for the given * adapter port.
*/ void t3_port_intr_enable(struct adapter *adapter, int idx)
{ struct cphy *phy = &adap2pinfo(adapter, idx)->phy;
/** * t3_port_intr_disable - disable port-specific interrupts * @adapter: associated adapter * @idx: index of port whose interrupts should be disabled * * Disable port-specific (i.e., MAC and PHY) interrupts for the given * adapter port.
*/ void t3_port_intr_disable(struct adapter *adapter, int idx)
{ struct cphy *phy = &adap2pinfo(adapter, idx)->phy;
/** * t3_port_intr_clear - clear port-specific interrupts * @adapter: associated adapter * @idx: index of port whose interrupts to clear * * Clear port-specific (i.e., MAC and PHY) interrupts for the given * adapter port.
*/ staticvoid t3_port_intr_clear(struct adapter *adapter, int idx)
{ struct cphy *phy = &adap2pinfo(adapter, idx)->phy;
/** * t3_sge_write_context - write an SGE context * @adapter: the adapter * @id: the context id * @type: the context type * * Program an SGE context with the values already loaded in the * CONTEXT_DATA? registers.
*/ staticint t3_sge_write_context(struct adapter *adapter, unsignedint id, unsignedint type)
{ if (type == F_RESPONSEQ) { /* * Can't write the Response Queue Context bits for * Interrupt Armed or the Reserve bits after the chip * has been initialized out of reset. Writing to these * bits can confuse the hardware.
*/
t3_write_reg(adapter, A_SG_CONTEXT_MASK0, 0xffffffff);
t3_write_reg(adapter, A_SG_CONTEXT_MASK1, 0xffffffff);
t3_write_reg(adapter, A_SG_CONTEXT_MASK2, 0x17ffffff);
t3_write_reg(adapter, A_SG_CONTEXT_MASK3, 0xffffffff);
} else {
t3_write_reg(adapter, A_SG_CONTEXT_MASK0, 0xffffffff);
t3_write_reg(adapter, A_SG_CONTEXT_MASK1, 0xffffffff);
t3_write_reg(adapter, A_SG_CONTEXT_MASK2, 0xffffffff);
t3_write_reg(adapter, A_SG_CONTEXT_MASK3, 0xffffffff);
}
t3_write_reg(adapter, A_SG_CONTEXT_CMD,
V_CONTEXT_CMD_OPCODE(1) | type | V_CONTEXT(id)); return t3_wait_op_done(adapter, A_SG_CONTEXT_CMD, F_CONTEXT_CMD_BUSY,
0, SG_CONTEXT_CMD_ATTEMPTS, 1);
}
/** * clear_sge_ctxt - completely clear an SGE context * @adap: the adapter * @id: the context id * @type: the context type * * Completely clear an SGE context. Used predominantly at post-reset * initialization. Note in particular that we don't skip writing to any * "sensitive bits" in the contexts the way that t3_sge_write_context() * does ...
*/ staticint clear_sge_ctxt(struct adapter *adap, unsignedint id, unsignedint type)
{
t3_write_reg(adap, A_SG_CONTEXT_DATA0, 0);
t3_write_reg(adap, A_SG_CONTEXT_DATA1, 0);
t3_write_reg(adap, A_SG_CONTEXT_DATA2, 0);
t3_write_reg(adap, A_SG_CONTEXT_DATA3, 0);
t3_write_reg(adap, A_SG_CONTEXT_MASK0, 0xffffffff);
t3_write_reg(adap, A_SG_CONTEXT_MASK1, 0xffffffff);
t3_write_reg(adap, A_SG_CONTEXT_MASK2, 0xffffffff);
t3_write_reg(adap, A_SG_CONTEXT_MASK3, 0xffffffff);
t3_write_reg(adap, A_SG_CONTEXT_CMD,
V_CONTEXT_CMD_OPCODE(1) | type | V_CONTEXT(id)); return t3_wait_op_done(adap, A_SG_CONTEXT_CMD, F_CONTEXT_CMD_BUSY,
0, SG_CONTEXT_CMD_ATTEMPTS, 1);
}
/** * t3_sge_init_ecntxt - initialize an SGE egress context * @adapter: the adapter to configure * @id: the context id * @gts_enable: whether to enable GTS for the context * @type: the egress context type * @respq: associated response queue * @base_addr: base address of queue * @size: number of queue entries * @token: uP token * @gen: initial generation value for the context * @cidx: consumer pointer * * Initialize an SGE egress context and make it ready for use. If the * platform allows concurrent context operations, the caller is * responsible for appropriate locking.
*/
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.23 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.