/* * This file is part of the Chelsio T4 Ethernet driver for Linux. * * Copyright (c) 2003-2016 Chelsio Communications, 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.
*/
/** * t4_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.
*/ staticint t4_wait_op_done_val(struct adapter *adapter, int reg, u32 mask, int polarity, int attempts, int delay, u32 *valp)
{ while (1) {
u32 val = t4_read_reg(adapter, reg);
if (!!(val & mask) == polarity) { if (valp)
*valp = val; return 0;
} if (--attempts == 0) return -EAGAIN; if (delay)
udelay(delay);
}
}
staticinlineint t4_wait_op_done(struct adapter *adapter, int reg, u32 mask, int polarity, int attempts, int delay)
{ return t4_wait_op_done_val(adapter, reg, mask, polarity, attempts,
delay, NULL);
}
/** * t4_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 t4_set_reg_field(struct adapter *adapter, unsignedint addr, u32 mask,
u32 val)
{
u32 v = t4_read_reg(adapter, addr) & ~mask;
/** * t4_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 * @nregs: how many indirect registers to read * @start_idx: index of first indirect register to read * * Reads registers that are accessed indirectly through an address/data * register pair.
*/ void t4_read_indirect(struct adapter *adap, unsignedint addr_reg, unsignedint data_reg, u32 *vals, unsignedint nregs, unsignedint start_idx)
{ while (nregs--) {
t4_write_reg(adap, addr_reg, start_idx);
*vals++ = t4_read_reg(adap, data_reg);
start_idx++;
}
}
/** * t4_write_indirect - write indirectly addressed registers * @adap: the adapter * @addr_reg: register holding the indirect addresses * @data_reg: register holding the value for the indirect registers * @vals: values to write * @nregs: how many indirect registers to write * @start_idx: address of first indirect register to write * * Writes a sequential block of registers that are accessed indirectly * through an address/data register pair.
*/ void t4_write_indirect(struct adapter *adap, unsignedint addr_reg, unsignedint data_reg, const u32 *vals, unsignedint nregs, unsignedint start_idx)
{ while (nregs--) {
t4_write_reg(adap, addr_reg, start_idx++);
t4_write_reg(adap, data_reg, *vals++);
}
}
/* * Read a 32-bit PCI Configuration Space register via the PCI-E backdoor * mechanism. This guarantees that we get the real value even if we're * operating within a Virtual Machine and the Hypervisor is trapping our * Configuration Space accesses.
*/ void t4_hw_pci_read_cfg4(struct adapter *adap, int reg, u32 *val)
{
u32 req = FUNCTION_V(adap->pf) | REGISTER_V(reg);
/* Reset ENABLE to 0 so reads of PCIE_CFG_SPACE_DATA won't cause a * Configuration Space read. (None of the other fields matter when * ENABLE is 0 so a simple register write is easier than a * read-modify-write via t4_set_reg_field().)
*/
t4_write_reg(adap, PCIE_CFG_SPACE_REQ_A, 0);
}
/* * t4_report_fw_error - report firmware error * @adap: the adapter * * The adapter firmware can indicate error conditions to the host. * If the firmware has indicated an error, print out the reason for * the firmware error.
*/ staticvoid t4_report_fw_error(struct adapter *adap)
{ staticconstchar *const reason[] = { "Crash", /* PCIE_FW_EVAL_CRASH */ "During Device Preparation", /* PCIE_FW_EVAL_PREP */ "During Device Configuration", /* PCIE_FW_EVAL_CONF */ "During Device Initialization", /* PCIE_FW_EVAL_INIT */ "Unexpected Event", /* PCIE_FW_EVAL_UNEXPECTEDEVENT */ "Insufficient Airflow", /* PCIE_FW_EVAL_OVERHEAT */ "Device Shutdown", /* PCIE_FW_EVAL_DEVICESHUTDOWN */ "Reserved", /* reserved */
};
u32 pcie_fw;
/* * Get the reply to a mailbox command and store it in @rpl in big-endian order.
*/ staticvoid get_mbox_rpl(struct adapter *adap, __be64 *rpl, int nflit,
u32 mbox_addr)
{ for ( ; nflit; nflit--, mbox_addr += 8)
*rpl++ = cpu_to_be64(t4_read_reg64(adap, mbox_addr));
}
/* * Handle a FW assertion reported in a mailbox.
*/ staticvoid fw_asrt(struct adapter *adap, u32 mbox_addr)
{ struct fw_debug_cmd asrt;
/** * t4_record_mbox - record a Firmware Mailbox Command/Reply in the log * @adapter: the adapter * @cmd: the Firmware Mailbox Command or Reply * @size: command length in bytes * @access: the time (ms) needed to access the Firmware Mailbox * @execute: the time (ms) the command spent being executed
*/ staticvoid t4_record_mbox(struct adapter *adapter, const __be64 *cmd, unsignedint size, int access, int execute)
{ struct mbox_cmd_log *log = adapter->mbox_log; struct mbox_cmd *entry; int i;
for (i = 0; i < size / 8; i++)
entry->cmd[i] = be64_to_cpu(cmd[i]); while (i < MBOX_LEN / 8)
entry->cmd[i++] = 0;
entry->timestamp = jiffies;
entry->seqno = log->seqno++;
entry->access = access;
entry->execute = execute;
}
/** * t4_wr_mbox_meat_timeout - send a command to FW through the given mailbox * @adap: the adapter * @mbox: index of the mailbox to use * @cmd: the command to write * @size: command length in bytes * @rpl: where to optionally store the reply * @sleep_ok: if true we may sleep while awaiting command completion * @timeout: time to wait for command to finish before timing out * * Sends the given command to FW through the selected mailbox and waits * for the FW to execute the command. If @rpl is not %NULL it is used to * store the FW's reply to the command. The command and its optional * reply are of the same length. FW can take up to %FW_CMD_MAX_TIMEOUT ms * to respond. @sleep_ok determines whether we may sleep while awaiting * the response. If sleeping is allowed we use progressive backoff * otherwise we spin. * * The return value is 0 on success or a negative errno on failure. A * failure can happen either because we are not able to execute the * command or FW executes it but signals an error. In the latter case * the return value is the error code indicated by FW (negated).
*/ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, constvoid *cmd, int size, void *rpl, bool sleep_ok, int timeout)
{ staticconstint delay[] = {
1, 1, 3, 5, 10, 10, 20, 50, 100, 200
};
if ((size & 15) || size > MBOX_LEN) return -EINVAL;
/* * If the device is off-line, as in EEH, commands will time out. * Fail them early so we don't waste time waiting.
*/ if (adap->pdev->error_state != pci_channel_io_normal) return -EIO;
/* If we have a negative timeout, that implies that we can't sleep. */ if (timeout < 0) {
sleep_ok = false;
timeout = -timeout;
}
/* Queue ourselves onto the mailbox access list. When our entry is at * the front of the list, we have rights to access the mailbox. So we * wait [for a while] till we're at the front [or bail out with an * EBUSY] ...
*/
spin_lock_bh(&adap->mbox_lock);
list_add_tail(&entry.list, &adap->mlist.list);
spin_unlock_bh(&adap->mbox_lock);
delay_idx = 0;
ms = delay[0];
for (i = 0; ; i += ms) { /* If we've waited too long, return a busy indication. This * really ought to be based on our initial position in the * mailbox access list but this is a start. We very rarely * contend on access to the mailbox ...
*/
pcie_fw = t4_read_reg(adap, PCIE_FW_A); if (i > FW_CMD_MAX_TIMEOUT || (pcie_fw & PCIE_FW_ERR_F)) {
spin_lock_bh(&adap->mbox_lock);
list_del(&entry.list);
spin_unlock_bh(&adap->mbox_lock);
ret = (pcie_fw & PCIE_FW_ERR_F) ? -ENXIO : -EBUSY;
t4_record_mbox(adap, cmd, size, access, ret); return ret;
}
/* If we're at the head, break out and start the mailbox * protocol.
*/ if (list_first_entry(&adap->mlist.list, struct mbox_list,
list) == &entry) break;
/* Delay for a bit before checking again ... */ if (sleep_ok) {
ms = delay[delay_idx]; /* last element may repeat */ if (delay_idx < ARRAY_SIZE(delay) - 1)
delay_idx++;
msleep(ms);
} else {
mdelay(ms);
}
}
/* Loop trying to get ownership of the mailbox. Return an error * if we can't gain ownership.
*/
v = MBOWNER_G(t4_read_reg(adap, ctl_reg)); for (i = 0; v == MBOX_OWNER_NONE && i < 3; i++)
v = MBOWNER_G(t4_read_reg(adap, ctl_reg)); if (v != MBOX_OWNER_DRV) {
spin_lock_bh(&adap->mbox_lock);
list_del(&entry.list);
spin_unlock_bh(&adap->mbox_lock);
ret = (v == MBOX_OWNER_FW) ? -EBUSY : -ETIMEDOUT;
t4_record_mbox(adap, cmd, size, access, ret); return ret;
}
/* Copy in the new mailbox command and send it on its way ... */
t4_record_mbox(adap, cmd, size, access, 0); for (i = 0; i < size; i += 8)
t4_write_reg64(adap, data_reg + i, be64_to_cpu(*p++));
for (i = 0;
!((pcie_fw = t4_read_reg(adap, PCIE_FW_A)) & PCIE_FW_ERR_F) &&
i < timeout;
i += ms) { if (sleep_ok) {
ms = delay[delay_idx]; /* last element may repeat */ if (delay_idx < ARRAY_SIZE(delay) - 1)
delay_idx++;
msleep(ms);
} else
mdelay(ms);
v = t4_read_reg(adap, ctl_reg); if (MBOWNER_G(v) == MBOX_OWNER_DRV) { if (!(v & MBMSGVALID_F)) {
t4_write_reg(adap, ctl_reg, 0); continue;
}
get_mbox_rpl(adap, cmd_rpl, MBOX_LEN / 8, data_reg);
res = be64_to_cpu(cmd_rpl[0]);
if (FW_CMD_OP_G(res >> 32) == FW_DEBUG_CMD) {
fw_asrt(adap, data_reg);
res = FW_CMD_RETVAL_V(EIO);
} elseif (rpl) {
memcpy(rpl, cmd_rpl, size);
}
/** * t4_memory_rw_init - Get memory window relative offset, base, and size. * @adap: the adapter * @win: PCI-E Memory Window to use * @mtype: memory type: MEM_EDC0, MEM_EDC1, MEM_HMA or MEM_MC * @mem_off: memory relative offset with respect to @mtype. * @mem_base: configured memory base address. * @mem_aperture: configured memory window aperture. * * Get the configured memory window's relative offset, base, and size.
*/ int t4_memory_rw_init(struct adapter *adap, int win, int mtype, u32 *mem_off,
u32 *mem_base, u32 *mem_aperture)
{
u32 edc_size, mc_size, mem_reg;
/* Offset into the region of memory which is being accessed * MEM_EDC0 = 0 * MEM_EDC1 = 1 * MEM_MC = 2 -- MEM_MC for chips with only 1 memory controller * MEM_MC1 = 3 -- for chips with 2 memory controllers (e.g. T5) * MEM_HMA = 4
*/
edc_size = EDRAM0_SIZE_G(t4_read_reg(adap, MA_EDRAM0_BAR_A)); if (mtype == MEM_HMA) {
*mem_off = 2 * (edc_size * 1024 * 1024);
} elseif (mtype != MEM_MC1) {
*mem_off = (mtype * (edc_size * 1024 * 1024));
} else {
mc_size = EXT_MEM0_SIZE_G(t4_read_reg(adap,
MA_EXT_MEMORY0_BAR_A));
*mem_off = (MEM_MC0 * edc_size + mc_size) * 1024 * 1024;
}
/* Each PCI-E Memory Window is programmed with a window size -- or * "aperture" -- which controls the granularity of its mapping onto * adapter memory. We need to grab that aperture in order to know * how to use the specified window. The window is also programmed * with the base address of the Memory Window in BAR0's address * space. For T4 this is an absolute PCI-E Bus Address. For T5 * the address is relative to BAR0.
*/
mem_reg = t4_read_reg(adap,
PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A,
win)); /* a dead adapter will return 0xffffffff for PIO reads */ if (mem_reg == 0xffffffff) return -ENXIO;
/** * t4_memory_update_win - Move memory window to specified address. * @adap: the adapter * @win: PCI-E Memory Window to use * @addr: location to move. * * Move memory window to specified address.
*/ void t4_memory_update_win(struct adapter *adap, int win, u32 addr)
{
t4_write_reg(adap,
PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win),
addr); /* Read it back to ensure that changes propagate before we * attempt to use the new value.
*/
t4_read_reg(adap,
PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win));
}
/** * t4_memory_rw_residual - Read/Write residual data. * @adap: the adapter * @off: relative offset within residual to start read/write. * @addr: address within indicated memory type. * @buf: host memory buffer * @dir: direction of transfer T4_MEMORY_READ (1) or T4_MEMORY_WRITE (0) * * Read/Write residual data less than 32-bits.
*/ void t4_memory_rw_residual(struct adapter *adap, u32 off, u32 addr, u8 *buf, int dir)
{ union {
u32 word; char byte[4];
} last; unsignedchar *bp; int i;
if (dir == T4_MEMORY_READ) {
last.word = le32_to_cpu((__force __le32)
t4_read_reg(adap, addr)); for (bp = (unsignedchar *)buf, i = off; i < 4; i++)
bp[i] = last.byte[i];
} else {
last.word = *buf; for (i = off; i < 4; i++)
last.byte[i] = 0;
t4_write_reg(adap, addr,
(__force u32)cpu_to_le32(last.word));
}
}
/** * t4_memory_rw - read/write EDC 0, EDC 1 or MC via PCIE memory window * @adap: the adapter * @win: PCI-E Memory Window to use * @mtype: memory type: MEM_EDC0, MEM_EDC1 or MEM_MC * @addr: address within indicated memory type * @len: amount of memory to transfer * @hbuf: host memory buffer * @dir: direction of transfer T4_MEMORY_READ (1) or T4_MEMORY_WRITE (0) * * Reads/writes an [almost] arbitrary memory region in the firmware: the * firmware memory address and host buffer must be aligned on 32-bit * boundaries; the length may be arbitrary. The memory is transferred as * a raw byte sequence from/to the firmware's memory. If this memory * contains data structures which contain multi-byte integers, it's the * caller's responsibility to perform appropriate byte order conversions.
*/ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr,
u32 len, void *hbuf, int dir)
{
u32 pos, offset, resid, memoffset;
u32 win_pf, mem_aperture, mem_base;
u32 *buf; int ret;
/* It's convenient to be able to handle lengths which aren't a * multiple of 32-bits because we often end up transferring files to * the firmware. So we'll handle that by normalizing the length here * and then handling any residual transfer at the end.
*/
resid = len & 0x3;
len -= resid;
ret = t4_memory_rw_init(adap, win, mtype, &memoffset, &mem_base,
&mem_aperture); if (ret) return ret;
/* Determine the PCIE_MEM_ACCESS_OFFSET */
addr = addr + memoffset;
/* Calculate our initial PCI-E Memory Window Position and Offset into * that Window.
*/
pos = addr & ~(mem_aperture - 1);
offset = addr - pos;
/* Set up initial PCI-E Memory Window to cover the start of our * transfer.
*/
t4_memory_update_win(adap, win, pos | win_pf);
/* Transfer data to/from the adapter as long as there's an integral * number of 32-bit transfers to complete. * * A note on Endianness issues: * * The "register" reads and writes below from/to the PCI-E Memory * Window invoke the standard adapter Big-Endian to PCI-E Link * Little-Endian "swizzel." As a result, if we have the following * data in adapter memory: * * Memory: ... | b0 | b1 | b2 | b3 | ... * Address: i+0 i+1 i+2 i+3 * * Then a read of the adapter memory via the PCI-E Memory Window * will yield: * * x = readl(i) * 31 0 * [ b3 | b2 | b1 | b0 ] * * If this value is stored into local memory on a Little-Endian system * it will show up correctly in local memory as: * * ( ..., b0, b1, b2, b3, ... ) * * But on a Big-Endian system, the store will show up in memory * incorrectly swizzled as: * * ( ..., b3, b2, b1, b0, ... ) * * So we need to account for this in the reads and writes to the * PCI-E Memory Window below by undoing the register read/write * swizzels.
*/ while (len > 0) { if (dir == T4_MEMORY_READ)
*buf++ = le32_to_cpu((__force __le32)t4_read_reg(adap,
mem_base + offset)); else
t4_write_reg(adap, mem_base + offset,
(__force u32)cpu_to_le32(*buf++));
offset += sizeof(__be32);
len -= sizeof(__be32);
/* If we've reached the end of our current window aperture, * move the PCI-E Memory Window on to the next. Note that * doing this here after "len" may be 0 allows us to set up * the PCI-E Memory Window for a possible final residual * transfer below ...
*/ if (offset == mem_aperture) {
pos += mem_aperture;
offset = 0;
t4_memory_update_win(adap, win, pos | win_pf);
}
}
/* If the original transfer had a length which wasn't a multiple of * 32-bits, now's where we need to finish off the transfer of the * residual amount. The PCI-E Memory Window has already been moved * above (if necessary) to cover this final transfer.
*/ if (resid)
t4_memory_rw_residual(adap, resid, mem_base + offset,
(u8 *)buf, dir);
return 0;
}
/* Return the specified PCI-E Configuration Space register from our Physical * Function. We try first via a Firmware LDST Command since we prefer to let * the firmware own all of these registers, but if that fails we go for it * directly ourselves.
*/
u32 t4_read_pcie_cfg4(struct adapter *adap, int reg)
{
u32 val, ldst_addrspace;
/* If fw_attach != 0, construct and send the Firmware LDST Command to * retrieve the specified PCI-E Configuration Space register.
*/ struct fw_ldst_cmd ldst_cmd; int ret;
/* If the LDST Command succeeds, return the result, otherwise * fall through to reading it directly ourselves ...
*/
ret = t4_wr_mbox(adap, adap->mbox, &ldst_cmd, sizeof(ldst_cmd),
&ldst_cmd); if (ret == 0)
val = be32_to_cpu(ldst_cmd.u.pcie.data[0]); else /* Read the desired Configuration Space register via the PCI-E * Backdoor mechanism.
*/
t4_hw_pci_read_cfg4(adap, reg, &val); return val;
}
/* Get the window based on base passed to it. * Window aperture is currently unhandled, but there is no use case for it * right now
*/ static u32 t4_get_window(struct adapter *adap, u32 pci_base, u64 pci_mask,
u32 memwin_base)
{
u32 ret;
if (is_t4(adap->params.chip)) {
u32 bar0;
/* Truncation intentional: we only read the bottom 32-bits of * the 64-bit BAR0/BAR1 ... We use the hardware backdoor * mechanism to read BAR0 instead of using * pci_resource_start() because we could be operating from * within a Virtual Machine which is trapping our accesses to * our Configuration Space and we need to set up the PCI-E * Memory Window decoders with the actual addresses which will * be coming across the PCI-E link.
*/
bar0 = t4_read_pcie_cfg4(adap, pci_base);
bar0 &= pci_mask;
adap->t4_bar0 = bar0;
ret = bar0 + memwin_base;
} else { /* For T5, only relative offset inside the PCIe BAR is passed */
ret = memwin_base;
} return ret;
}
/* Get the default utility window (win0) used by everyone */
u32 t4_get_util_window(struct adapter *adap)
{ return t4_get_window(adap, PCI_BASE_ADDRESS_0,
PCI_BASE_ADDRESS_MEM_MASK, MEMWIN0_BASE);
}
/* Set up memory window for accessing adapter memory ranges. (Read * back MA register to ensure that changes propagate before we attempt * to use the new values.)
*/ void t4_setup_memwin(struct adapter *adap, u32 memwin_base, u32 window)
{
t4_write_reg(adap,
PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, window),
memwin_base | BIR_V(0) |
WINDOW_V(ilog2(MEMWIN0_APERTURE) - WINDOW_SHIFT_X));
t4_read_reg(adap,
PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, window));
}
/** * t4_get_regs_len - return the size of the chips register set * @adapter: the adapter * * Returns the size of the chip's BAR0 register space.
*/ unsignedint t4_get_regs_len(struct adapter *adapter)
{ unsignedint chip_version = CHELSIO_CHIP_VERSION(adapter->params.chip);
switch (chip_version) { case CHELSIO_T4: return T4_REGMAP_SIZE;
case CHELSIO_T5: case CHELSIO_T6: return T5_REGMAP_SIZE;
}
dev_err(adapter->pdev_dev, "Unsupported chip version %d\n", chip_version); return 0;
}
/* Select the right set of register ranges to dump depending on the * adapter chip type.
*/ switch (chip_version) { case CHELSIO_T4:
reg_ranges = t4_reg_ranges;
reg_ranges_size = ARRAY_SIZE(t4_reg_ranges); break;
case CHELSIO_T5:
reg_ranges = t5_reg_ranges;
reg_ranges_size = ARRAY_SIZE(t5_reg_ranges); break;
case CHELSIO_T6:
reg_ranges = t6_reg_ranges;
reg_ranges_size = ARRAY_SIZE(t6_reg_ranges); break;
default:
dev_err(adap->pdev_dev, "Unsupported chip version %d\n", chip_version); return;
}
/* Clear the register buffer and insert the appropriate register * values selected by the above register ranges.
*/
memset(buf, 0, buf_size); for (range = 0; range < reg_ranges_size; range += 2) { unsignedint reg = reg_ranges[range]; unsignedint last_reg = reg_ranges[range + 1];
u32 *bufp = (u32 *)((char *)buf + reg);
/* Iterate across the register range filling in the register * buffer but don't write past the end of the register buffer.
*/ while (reg <= last_reg && bufp < buf_end) {
*bufp++ = t4_read_reg(adap, reg);
reg += sizeof(u32);
}
}
}
/** * t4_eeprom_ptov - translate a physical EEPROM address to virtual * @phys_addr: the physical EEPROM address * @fn: the PCI function number * @sz: size of function-specific area * * Translate a physical EEPROM address to virtual. The first 1K is * accessed through virtual addresses starting at 31K, the rest is * accessed through virtual addresses starting at 0. * * The mapping is as follows: * [0..1K) -> [31K..32K) * [1K..1K+A) -> [31K-A..31K) * [1K+A..ES) -> [0..ES-A-1K) * * where A = @fn * @sz, and ES = EEPROM size.
*/ int t4_eeprom_ptov(unsignedint phys_addr, unsignedint fn, unsignedint sz)
{
fn *= sz; if (phys_addr < 1024) return phys_addr + (31 << 10); if (phys_addr < 1024 + fn) return 31744 - fn + phys_addr - 1024; if (phys_addr < EEPROMSIZE) return phys_addr - 1024 - fn; return -EINVAL;
}
/** * t4_seeprom_wp - enable/disable EEPROM write protection * @adapter: the adapter * @enable: whether to enable or disable write protection * * Enables or disables write protection on the serial EEPROM.
*/ int t4_seeprom_wp(struct adapter *adapter, bool enable)
{ unsignedint v = enable ? 0xc : 0; int ret = pci_write_vpd(adapter->pdev, EEPROM_STAT_ADDR, 4, &v); return ret < 0 ? ret : 0;
}
/** * t4_get_raw_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.
*/ int t4_get_raw_vpd_params(struct adapter *adapter, struct vpd_params *p)
{ unsignedint id_len, pn_len, sn_len, na_len; int id, sn, pn, na, addr, ret = 0;
u8 *vpd, base_val = 0;
vpd = vmalloc(VPD_LEN); if (!vpd) return -ENOMEM;
/* Card information normally starts at VPD_BASE but early cards had * it at 0.
*/
ret = pci_read_vpd(adapter->pdev, VPD_BASE, 1, &base_val); if (ret < 0) goto out;
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.