oce_mbx.c revision 12d61dab3304980e691068219eaaab6398744a2e
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Emulex. All rights reserved.
* Use is subject to license terms.
*/
/*
* Source file containing the implementation of MBOX
* and related helper functions
*/
#include <oce_impl.h>
static ddi_dma_attr_t oce_sgl_dma_attr = {
DMA_ATTR_V0, /* version number */
0x0000000000000000ull, /* low address */
0xFFFFFFFFFFFFFFFFull, /* high address */
0x0000000000010000ull, /* dma counter max */
0x1000, /* alignment 4K for mbx bufs */
0x1, /* burst sizes */
0x00000004, /* minimum transfer size */
0x00000000FFFFFFFFull, /* maximum transfer size */
0xFFFFFFFFFFFFFFFFull, /* maximum segment size */
MAX_MBX_SGE, /* scatter/gather list length */
0x00000001, /* granularity */
0 /* DMA flags */
};
static ddi_device_acc_attr_t oce_sgl_buf_accattr = {
DDI_DEVICE_ATTR_V0,
DDI_NEVERSWAP_ACC,
DDI_STRICTORDER_ACC,
};
/*
* common inline function to fill an ioctl request header
*
* hdr - pointer to a buffer where the header will be initialized
* dom - domain
* port - port number
* opcode - command code for this MBX
* timeout - timeout in seconds
* pyld_len - length of the command buffer described by this header
*
* return none
*/
void
mbx_common_req_hdr_init(struct mbx_hdr *hdr,
uint8_t dom, uint8_t port,
uint8_t subsys, uint8_t opcode,
uint32_t timeout, uint32_t pyld_len)
{
ASSERT(hdr != NULL);
hdr->u0.req.opcode = opcode;
hdr->u0.req.subsystem = subsys;
hdr->u0.req.port_number = port;
hdr->u0.req.domain = dom;
hdr->u0.req.timeout = timeout;
hdr->u0.req.request_length = pyld_len - sizeof (struct mbx_hdr);
} /* mbx_common_req_hdr_init */
/*
* function to initialize the hw with host endian information
*
* dev - software handle to the device
*
* return 0 on success, ETIMEDOUT on failure
*/
int
oce_mbox_init(struct oce_dev *dev)
{
struct oce_bmbx *mbx;
uint8_t *ptr;
int ret = 0;
ASSERT(dev != NULL);
mbx = (struct oce_bmbx *)DBUF_VA(dev->bmbx);
ptr = (uint8_t *)&mbx->mbx;
/* Endian Signature */
*ptr++ = 0xff;
*ptr++ = 0x12;
*ptr++ = 0x34;
*ptr++ = 0xff;
*ptr++ = 0xff;
*ptr++ = 0x56;
*ptr++ = 0x78;
*ptr = 0xff;
ret = oce_mbox_dispatch(dev, 0);
if (ret != 0)
oce_log(dev, CE_NOTE, MOD_CONFIG,
"Failed to set endian %d", ret);
return (ret);
} /* oce_mbox_init */
/*
* function to wait till we get a mbox ready after writing to the
* mbox doorbell
*
* dev - software handle to the device
*
* return 0=ready, ETIMEDOUT=>not ready but timed out
*/
int
oce_mbox_wait(struct oce_dev *dev, uint32_t tmo_sec)
{
clock_t tmo;
clock_t now, tstamp;
pd_mpu_mbox_db_t mbox_db;
tmo = (tmo_sec > 0) ? drv_usectohz(tmo_sec * 1000000) :
drv_usectohz(DEFAULT_MQ_MBOX_TIMEOUT);
tstamp = ddi_get_lbolt();
for (;;) {
now = ddi_get_lbolt();
if ((now - tstamp) >= tmo) {
tmo = 0;
break;
}
mbox_db.dw0 = OCE_DB_READ32(dev, PD_MPU_MBOX_DB);
if (oce_fm_check_acc_handle(dev, dev->db_handle) != DDI_FM_OK) {
ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED);
oce_fm_ereport(dev, DDI_FM_DEVICE_INVAL_STATE);
}
if (mbox_db.bits.ready) {
return (0);
}
drv_usecwait(5);
}
return (ETIMEDOUT);
} /* oce_mbox_wait */
/*
* function to dispatch a mailbox command present in the mq mbox
*
* dev - software handle to the device
*
* return 0 on success, ETIMEDOUT on failure
*/
int
oce_mbox_dispatch(struct oce_dev *dev, uint32_t tmo_sec)
{
pd_mpu_mbox_db_t mbox_db;
uint32_t pa;
int ret;
/* sync the bmbx */
(void) DBUF_SYNC(dev->bmbx, DDI_DMA_SYNC_FORDEV);
/* write 30 bits of address hi dword */
pa = (uint32_t)(DBUF_PA(dev->bmbx) >> 34);
bzero(&mbox_db, sizeof (pd_mpu_mbox_db_t));
mbox_db.bits.ready = 0;
mbox_db.bits.hi = 1;
mbox_db.bits.address = pa;
/* wait for mbox ready */
ret = oce_mbox_wait(dev, tmo_sec);
if (ret != 0) {
return (ret);
}
/* ring the doorbell */
OCE_DB_WRITE32(dev, PD_MPU_MBOX_DB, mbox_db.dw0);
if (oce_fm_check_acc_handle(dev, dev->db_handle) != DDI_FM_OK) {
ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED);
}
/* wait for mbox ready */
ret = oce_mbox_wait(dev, tmo_sec);
if (ret != 0) {
oce_log(dev, CE_NOTE, MOD_CONFIG,
"BMBX TIMED OUT PROGRAMMING HI ADDR: %d", ret);
/* if mbx times out, hw is in invalid state */
ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED);
oce_fm_ereport(dev, DDI_FM_DEVICE_INVAL_STATE);
return (ret);
}
/* now write 30 bits of address lo dword */
pa = (uint32_t)(DBUF_PA(dev->bmbx) >> 4) & 0x3fffffff;
mbox_db.bits.ready = 0;
mbox_db.bits.hi = 0;
mbox_db.bits.address = pa;
/* ring the doorbell */
OCE_DB_WRITE32(dev, PD_MPU_MBOX_DB, mbox_db.dw0);
if (oce_fm_check_acc_handle(dev, dev->db_handle) != DDI_FM_OK) {
ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED);
}
/* wait for mbox ready */
ret = oce_mbox_wait(dev, tmo_sec);
/* sync */
(void) ddi_dma_sync(DBUF_DHDL(dev->bmbx), 0, 0,
DDI_DMA_SYNC_FORKERNEL);
if (oce_fm_check_dma_handle(dev, DBUF_DHDL(dev->bmbx)) != DDI_FM_OK) {
ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED);
return (EIO);
}
return (ret);
} /* oce_mbox_dispatch */
/*
* function to post a MBX to the mbox
*
* dev - software handle to the device
* mbx - pointer to the MBX to send
* mbxctx - pointer to the mbx context structure
*
* return 0 on success, ETIMEDOUT on failure
*/
int
oce_mbox_post(struct oce_dev *dev, struct oce_mbx *mbx,
struct oce_mbx_ctx *mbxctx)
{
struct oce_mbx *mb_mbx = NULL;
struct oce_mq_cqe *mb_cqe = NULL;
struct oce_bmbx *mb = NULL;
int ret = 0;
uint32_t tmo = 0;
mutex_enter(&dev->bmbx_lock);
mb = (struct oce_bmbx *)DBUF_VA(dev->bmbx);
mb_mbx = &mb->mbx;
/* get the tmo */
tmo = mbx->tag[0];
mbx->tag[0] = 0;
/* copy mbx into mbox */
bcopy(mbx, mb_mbx, sizeof (struct oce_mbx));
/* now dispatch */
ret = oce_mbox_dispatch(dev, tmo);
if (ret != 0) {
return (ret);
}
/* sync */
(void) ddi_dma_sync(DBUF_DHDL(dev->bmbx), 0, 0,
DDI_DMA_SYNC_FORKERNEL);
ret = oce_fm_check_dma_handle(dev, DBUF_DHDL(dev->bmbx));
if (ret != DDI_FM_OK) {
ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED);
mutex_exit(&dev->bmbx_lock);
return (EIO);
}
/*
* the command completed successfully. Now get the
* completion queue entry
*/
mb_cqe = &mb->cqe;
DW_SWAP(u32ptr(&mb_cqe->u0.dw[0]), sizeof (struct oce_mq_cqe));
/* check mbox status */
if (mb_cqe->u0.s.completion_status != 0) {
oce_log(dev, CE_WARN, MOD_CONFIG,
"MBOX Failed with Status: %d %d",
mb_cqe->u0.s.completion_status,
mb_cqe->u0.s.extended_status);
mutex_exit(&dev->bmbx_lock);
return (EIO);
}
/* copy mbox mbx back */
bcopy(mb_mbx, mbx, sizeof (struct oce_mbx));
/*
* store the mbx context in the cqe tag section so that
* the upper layer handling the cqe can associate the mbx
* with the response
*/
if (mbxctx) {
/* save context */
mbxctx->mbx = mb_mbx;
bcopy(&mbxctx, mb_cqe->u0.s.mq_tag,
sizeof (struct oce_mbx_ctx *));
}
mutex_exit(&dev->bmbx_lock);
return (0);
} /* oce_mbox_post */
/*
* function to get the firmware version
*
* dev - software handle to the device
*
* return 0 on success, EIO on failure
*/
int
oce_get_fw_version(struct oce_dev *dev)
{
struct oce_mbx mbx;
struct mbx_get_common_fw_version *fwcmd;
int ret = 0;
bzero(&mbx, sizeof (struct oce_mbx));
/* initialize the ioctl header */
fwcmd = (struct mbx_get_common_fw_version *)&mbx.payload;
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_COMMON,
OPCODE_GET_COMMON_FW_VERSION,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_get_common_fw_version));
/* fill rest of mbx */
mbx.u0.s.embedded = 1;
mbx.payload_length = sizeof (struct mbx_get_common_fw_version);
DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ);
/* now post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
if (ret != 0) {
return (ret);
}
bcopy(fwcmd->params.rsp.fw_ver_str, dev->fw_version, 32);
oce_log(dev, CE_NOTE, MOD_CONFIG, "%s %s",
fwcmd->params.rsp.fw_ver_str,
fwcmd->params.rsp.fw_on_flash_ver_str);
return (0);
} /* oce_get_fw_version */
/*
* function to invoke f/w reset via. mailbox
* does not hold bootstap lock called by quiesce
*
* dev - software handle to the device
*
* return 0 on success, ETIMEDOUT on failure
*
*/
int
oce_reset_fun(struct oce_dev *dev)
{
struct oce_mbx *mbx;
struct oce_bmbx *mb;
struct ioctl_common_function_reset *fwcmd;
mb = (struct oce_bmbx *)DBUF_VA(dev->bmbx);
mbx = &mb->mbx;
bzero(mbx, sizeof (struct oce_mbx));
/* initialize the ioctl header */
fwcmd = (struct ioctl_common_function_reset *)&mbx->payload;
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_COMMON,
OPCODE_COMMON_FUNCTION_RESET,
MBX_TIMEOUT_SEC,
sizeof (struct ioctl_common_function_reset));
/* fill rest of mbx */
mbx->u0.s.embedded = 1;
mbx->payload_length = sizeof (struct ioctl_common_function_reset);
DW_SWAP(u32ptr(mbx), mbx->payload_length + OCE_BMBX_RHDR_SZ);
return (oce_mbox_dispatch(dev, 0));
} /* oce_reset_fun */
/*
* function to read the mac address associated with an interface
*
* dev - software handle to the device
* if_id - interface id to read the address from
* perm - set to 1 if reading the factory mac address. In this case
* if_id is ignored
* type - type of the mac address, whether network or storage
* mac - [OUTPUT] pointer to a buffer containing the mac address
* when the command succeeds
*
* return 0 on success, EIO on failure
*/
int
oce_read_mac_addr(struct oce_dev *dev, uint32_t if_id, uint8_t perm,
uint8_t type, struct mac_address_format *mac)
{
struct oce_mbx mbx;
struct mbx_query_common_iface_mac *fwcmd;
int ret = 0;
bzero(&mbx, sizeof (struct oce_mbx));
/* initialize the ioctl header */
fwcmd = (struct mbx_query_common_iface_mac *)&mbx.payload;
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_COMMON,
OPCODE_QUERY_COMMON_IFACE_MAC,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_query_common_iface_mac));
/* fill the command */
fwcmd->params.req.permanent = perm;
if (perm)
fwcmd->params.req.if_id = (uint16_t)if_id;
else
fwcmd->params.req.if_id = 0;
fwcmd->params.req.type = type;
/* fill rest of mbx */
mbx.u0.s.embedded = 1;
mbx.payload_length = sizeof (struct mbx_query_common_iface_mac);
DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ);
/* now post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
if (ret != 0) {
return (ret);
}
/* get the response */
oce_log(dev, CE_NOTE, MOD_CONFIG,
"MAC addr size = 0x%x",
LE_16(fwcmd->params.rsp.mac.size_of_struct));
oce_log(dev, CE_NOTE, MOD_CONFIG,
"MAC_ADDR:0x%x:0x%x:0x%x:0x%x:0x%x:0x%x",
fwcmd->params.rsp.mac.mac_addr[0],
fwcmd->params.rsp.mac.mac_addr[1],
fwcmd->params.rsp.mac.mac_addr[2],
fwcmd->params.rsp.mac.mac_addr[3],
fwcmd->params.rsp.mac.mac_addr[4],
fwcmd->params.rsp.mac.mac_addr[5]);
/* copy the mac addres in the output parameter */
mac->size_of_struct = LE_16(fwcmd->params.rsp.mac.size_of_struct);
bcopy(&fwcmd->params.rsp.mac.mac_addr[0], &mac->mac_addr[0],
mac->size_of_struct);
return (0);
} /* oce_read_mac_addr */
/*
* function to create an interface using the OPCODE_CREATE_COMMON_IFACE
* command
*
* dev - software handle to the device
* cap_flags - capability flags
* en_flags - enable capability flags
* vlan_tag - optional vlan tag to associate with the if
* mac_addr - pointer to a buffer containing the mac address
* if_id - [OUTPUT] pointer to an integer to hold the ID of the
* interface created
*
* return 0 on success, EIO on failure
*/
int
oce_if_create(struct oce_dev *dev, uint32_t cap_flags, uint32_t en_flags,
uint16_t vlan_tag, uint8_t *mac_addr,
uint32_t *if_id)
{
struct oce_mbx mbx;
struct mbx_create_common_iface *fwcmd;
int ret = 0;
bzero(&mbx, sizeof (struct oce_mbx));
/* initialize the ioctl header */
fwcmd = (struct mbx_create_common_iface *)&mbx.payload;
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_COMMON,
OPCODE_CREATE_COMMON_IFACE,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_create_common_iface));
DW_SWAP(u32ptr(&fwcmd->hdr), sizeof (struct mbx_hdr));
/* fill the command */
fwcmd->params.req.version = 0;
fwcmd->params.req.cap_flags = LE_32(cap_flags);
fwcmd->params.req.enable_flags = LE_32(en_flags);
if (mac_addr != NULL) {
bcopy(mac_addr, &fwcmd->params.req.mac_addr[0],
ETHERADDRL);
fwcmd->params.req.vlan_tag.u0.normal.vtag = LE_16(vlan_tag);
fwcmd->params.req.mac_invalid = B_FALSE;
} else {
fwcmd->params.req.mac_invalid = B_TRUE;
}
/* fill rest of mbx */
mbx.u0.s.embedded = 1;
mbx.payload_length = sizeof (struct mbx_create_common_iface);
DW_SWAP(u32ptr(&mbx), OCE_BMBX_RHDR_SZ);
/* now post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
if (ret != 0) {
return (ret);
}
/* get response */
*if_id = LE_32(fwcmd->params.rsp.if_id);
oce_log(dev, CE_NOTE, MOD_CONFIG,
"IF_ID = 0x%x", *if_id);
/* If asked to set mac addr save the pmac handle */
if (mac_addr != NULL) {
dev->pmac_id = LE_32(fwcmd->params.rsp.pmac_id);
oce_log(dev, CE_NOTE, MOD_CONFIG,
"PMAC_ID = 0x%x", dev->pmac_id);
}
return (0);
} /* oce_if_create */
/*
* function to delete an interface
*
* dev - software handle to the device
* if_id - ID of the interface to delete
*
* return 0 on success, EIO on failure
*/
int
oce_if_del(struct oce_dev *dev, uint32_t if_id)
{
struct oce_mbx mbx;
struct mbx_destroy_common_iface *fwcmd;
int ret = 0;
bzero(&mbx, sizeof (struct oce_mbx));
/* initialize the ioctl header */
fwcmd = (struct mbx_destroy_common_iface *)&mbx.payload;
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_COMMON,
OPCODE_DESTROY_COMMON_IFACE,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_destroy_common_iface));
/* fill the command */
fwcmd->params.req.if_id = if_id;
/* fill rest of mbx */
mbx.u0.s.embedded = 1;
mbx.payload_length = sizeof (struct mbx_destroy_common_iface);
DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ);
/* post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
return (ret);
} /* oce_if_del */
/*
* function to query the link status from the hardware
*
* dev - software handle to the device
* link_status - [OUT] pointer to the structure returning the link attributes
*
* return 0 on success, EIO on failure
*/
int
oce_get_link_status(struct oce_dev *dev, struct link_status *link)
{
struct oce_mbx mbx;
struct mbx_query_common_link_status *fwcmd;
int ret = 0;
bzero(&mbx, sizeof (struct oce_mbx));
/* initialize the ioctl header */
fwcmd = (struct mbx_query_common_link_status *)&mbx.payload;
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_COMMON,
OPCODE_QUERY_COMMON_LINK_STATUS,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_query_common_link_status));
/* fill rest of mbx */
mbx.u0.s.embedded = 1;
mbx.payload_length = sizeof (struct mbx_query_common_link_status);
DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ);
/* post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
if (ret != 0) {
return (ret);
}
/* interpret response */
bcopy(&fwcmd->params.rsp, link, 8);
return (0);
} /* oce_get_link_status */
/*
* function to configure the rx filter on the interface
*
* dev - software handle to the device
* filter - mbx command containing the filter parameters
*
* return 0 on success, EIO on failure
*/
int
oce_set_rx_filter(struct oce_dev *dev,
struct mbx_set_common_ntwk_rx_filter *filter)
{
struct oce_mbx mbx;
struct mbx_set_common_ntwk_rx_filter *fwcmd;
int ret;
bzero(&mbx, sizeof (struct oce_mbx));
fwcmd = (struct mbx_set_common_ntwk_rx_filter *)&mbx.payload;
/* fill the command */
bcopy(filter, fwcmd, sizeof (struct mbx_set_common_ntwk_rx_filter));
/* initialize the ioctl header */
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_COMMON,
OPCODE_COMMON_NTWK_RX_FILTER,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_set_common_ntwk_rx_filter));
/* fill rest of mbx */
mbx.u0.s.embedded = 1;
mbx.payload_length = sizeof (struct mbx_set_common_ntwk_rx_filter);
DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ);
/* post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
return (ret);
} /* oce_set_rx_filter */
/*
* function to send the mbx command to update the mcast table with fw
*
* dev - software handle to the device
* mca_table - array of mcast address to update
* mca_cnt - number of elements in mca_table
* enable_promisc - flag to enable/disable mcast-promiscuous mode
*
* return 0 on success, EIO on failure
*/
int
oce_set_multicast_table(struct oce_dev *dev, uint32_t if_id,
struct ether_addr *mca_table, uint16_t mca_cnt, boolean_t promisc)
{
struct oce_mbx mbx;
struct mbx_set_common_iface_multicast *fwcmd;
int ret;
bzero(&mbx, sizeof (struct oce_mbx));
fwcmd = (struct mbx_set_common_iface_multicast *)&mbx.payload;
/* initialize the ioctl header */
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_COMMON,
OPCODE_SET_COMMON_IFACE_MULTICAST,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_set_common_iface_multicast));
/* fill the command */
fwcmd->params.req.if_id = (uint8_t)if_id;
if (mca_table != NULL) {
bcopy(mca_table, &fwcmd->params.req.mac[0],
mca_cnt * ETHERADDRL);
}
fwcmd->params.req.num_mac = LE_16(mca_cnt);
fwcmd->params.req.promiscuous = (uint8_t)promisc;
/* fill rest of mbx */
mbx.u0.s.embedded = B_TRUE;
mbx.payload_length = sizeof (struct mbx_set_common_iface_multicast);
/* Swap only MBX header + BOOTSTRAP HDR */
DW_SWAP(u32ptr(&mbx), (OCE_BMBX_RHDR_SZ + OCE_MBX_RRHDR_SZ));
/* post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
return (ret);
} /* oce_set_multicast_table */
/*
* function to query the fw attributes from the hw
*
* dev - software handle to the device
*
* return 0 on success, EIO on failure
*/
int
oce_get_fw_config(struct oce_dev *dev)
{
struct oce_mbx mbx;
struct mbx_common_query_fw_config *fwcmd;
int ret = 0;
bzero(&mbx, sizeof (struct oce_mbx));
/* initialize the ioctl header */
fwcmd = (struct mbx_common_query_fw_config *)&mbx.payload;
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_COMMON,
OPCODE_QUERY_COMMON_FIRMWARE_CONFIG,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_common_query_fw_config));
/* fill rest of mbx */
mbx.u0.s.embedded = 1;
mbx.payload_length = sizeof (struct mbx_common_query_fw_config);
DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ);
/* now post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
if (ret != 0) {
return (ret);
}
/* swap and copy into buffer */
DW_SWAP(u32ptr(fwcmd), sizeof (struct mbx_common_query_fw_config));
dev->config_number = fwcmd->params.rsp.config_number;
dev->asic_revision = fwcmd->params.rsp.asic_revision;
dev->port_id = fwcmd->params.rsp.port_id;
dev->function_mode = fwcmd->params.rsp.function_mode;
return (0);
} /* oce_get_fw_config */
/*
* function to retrieve statistic counters from the hardware
*
* dev - software handle to the device
*
* return 0 on success, EIO on failure
*/
int
oce_get_hw_stats(struct oce_dev *dev)
{
struct oce_mbx mbx;
struct mbx_get_nic_stats *fwcmd = dev->hw_stats;
int ret = 0;
bzero(&mbx, sizeof (struct oce_mbx));
/* initialize the ioctl header */
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_NIC,
OPCODE_GET_NIC_STATS,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_get_nic_stats));
DW_SWAP(u32ptr(fwcmd), sizeof (struct mbx_get_nic_stats));
/* fill rest of mbx */
mbx.payload.u0.u1.sgl[0].pa_lo = ADDR_LO(DBUF_PA(dev->stats_dbuf));
mbx.payload.u0.u1.sgl[0].pa_hi = ADDR_HI(DBUF_PA(dev->stats_dbuf));
mbx.payload.u0.u1.sgl[0].length = sizeof (struct mbx_get_nic_stats);
mbx.payload_length = sizeof (struct mbx_get_nic_stats);
mbx.u0.s.embedded = 0;
mbx.u0.s.sge_count = 1;
DW_SWAP(u32ptr(&mbx), sizeof (struct oce_mq_sge) + OCE_BMBX_RHDR_SZ);
/* sync for device */
(void) DBUF_SYNC(dev->stats_dbuf, DDI_DMA_SYNC_FORDEV);
/* now post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
/* sync the stats */
(void) DBUF_SYNC(dev->stats_dbuf, DDI_DMA_SYNC_FORKERNEL);
/* Check the mailbox status and command completion status */
if (ret != 0) {
return (ret);
}
DW_SWAP(u32ptr(dev->hw_stats), sizeof (struct mbx_get_nic_stats));
return (0);
} /* oce_get_hw_stats */
/*
* function to set the number of vectors with the cev
*
* dev - software handle to the device
* num_vectors - number of MSI messages
*
* return 0 on success, EIO on failure
*/
int
oce_num_intr_vectors_set(struct oce_dev *dev, uint32_t num_vectors)
{
struct oce_mbx mbx;
struct mbx_common_cev_modify_msi_messages *fwcmd;
int ret = 0;
bzero(&mbx, sizeof (struct oce_mbx));
/* initialize the ioctl header */
fwcmd = (struct mbx_common_cev_modify_msi_messages *)&mbx.payload;
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_COMMON,
OPCODE_COMMON_CEV_MODIFY_MSI_MESSAGES,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_common_cev_modify_msi_messages));
/* fill the command */
fwcmd->params.req.num_msi_msgs = LE_32(num_vectors);
/* fill rest of mbx */
mbx.u0.s.embedded = 1;
mbx.payload_length =
sizeof (struct mbx_common_cev_modify_msi_messages);
DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ);
/* post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
return (ret);
} /* oce_num_intr_vectors_set */
/*
* function to set flow control capability in the hardware
*
* dev - software handle to the device
* flow_control - flow control flags to set
*
* return 0 on success, EIO on failure
*/
int
oce_set_flow_control(struct oce_dev *dev, uint32_t flow_control)
{
struct oce_mbx mbx;
struct mbx_common_get_set_flow_control *fwcmd =
(struct mbx_common_get_set_flow_control *)&mbx.payload;
int ret;
bzero(&mbx, sizeof (struct oce_mbx));
/* initialize the ioctl header */
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_COMMON,
OPCODE_SET_COMMON_FLOW_CONTROL,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_common_get_set_flow_control));
/* fill command */
if (flow_control & OCE_FC_TX)
fwcmd->tx_flow_control = 1;
if (flow_control & OCE_FC_RX)
fwcmd->rx_flow_control = 1;
/* fill rest of mbx */
mbx.u0.s.embedded = 1;
mbx.payload_length = sizeof (struct mbx_common_get_set_flow_control);
DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ);
/* post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
return (ret);
} /* oce_set_flow_control */
/*
* function to get the current flow control setting with the hardware
*
* dev - software handle to the device
* flow_control - [OUT] pointer to location where flow_control setting
* is returned
*
* return 0 on success, EIO on failure
*/
int
oce_get_flow_control(struct oce_dev *dev, uint32_t *flow_control)
{
struct oce_mbx mbx;
struct mbx_common_get_set_flow_control *fwcmd;
int ret;
DEV_LOCK(dev);
if (dev->suspended) {
DEV_UNLOCK(dev);
return (EIO);
}
DEV_UNLOCK(dev);
bzero(&mbx, sizeof (struct oce_mbx));
fwcmd = (struct mbx_common_get_set_flow_control *)&mbx.payload;
/* initialize the ioctl header */
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_COMMON,
OPCODE_GET_COMMON_FLOW_CONTROL,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_common_get_set_flow_control));
/* fill rest of mbx */
mbx.u0.s.embedded = 1;
mbx.payload_length = sizeof (struct mbx_common_get_set_flow_control);
DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ);
/* post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
if (ret != 0) {
return (ret);
}
/* get the flow control */
DW_SWAP(u32ptr(fwcmd),
sizeof (struct mbx_common_get_set_flow_control));
*flow_control = 0;
if (fwcmd->tx_flow_control)
*flow_control |= OCE_FC_TX;
if (fwcmd->rx_flow_control)
*flow_control |= OCE_FC_RX;
return (0);
} /* oce_get_flow_control */
/*
* function to enable/disable device promiscuous mode
*
* dev - software handle to the device
* enable - enable/disable flag
*
* return 0 on success, EIO on failure
*/
int
oce_set_promiscuous(struct oce_dev *dev, boolean_t enable)
{
struct oce_mbx mbx;
struct mbx_config_nic_promiscuous *fwcmd;
int ret;
bzero(&mbx, sizeof (struct oce_mbx));
fwcmd = (struct mbx_config_nic_promiscuous *)&mbx.payload;
if (dev->port_id == 0) {
fwcmd->params.req.port0_promisc = (uint8_t)enable;
} else {
fwcmd->params.req.port1_promisc = (uint8_t)enable;
}
/* initialize the ioctl header */
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_NIC,
OPCODE_CONFIG_NIC_PROMISCUOUS,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_config_nic_promiscuous));
/* fill rest of mbx */
mbx.u0.s.embedded = 1;
mbx.payload_length = sizeof (struct mbx_config_nic_promiscuous);
DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ);
/* post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
return (ret);
}
/*
* function to add a unicast address to an interface
*
* dev - software handle to the device
* mac - unicast address
*
* return 0 on success, EIO on failure
*/
int
oce_add_mac(struct oce_dev *dev, uint32_t if_id,
const uint8_t *mac, uint32_t *pmac_id)
{
struct oce_mbx mbx;
struct mbx_add_common_iface_mac *fwcmd;
int ret;
bzero(&mbx, sizeof (struct oce_mbx));
fwcmd = (struct mbx_add_common_iface_mac *)&mbx.payload;
fwcmd->params.req.if_id = LE_32(if_id);
bcopy(mac, &fwcmd->params.req.mac_address[0], ETHERADDRL);
/* initialize the ioctl header */
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_COMMON,
OPCODE_ADD_COMMON_IFACE_MAC,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_add_common_iface_mac));
/* fill rest of mbx */
mbx.u0.s.embedded = 1;
mbx.payload_length = sizeof (struct mbx_add_common_iface_mac);
DW_SWAP(u32ptr(&mbx), OCE_BMBX_RHDR_SZ + OCE_MBX_RRHDR_SZ);
/* post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
if (ret != 0) {
return (ret);
}
*pmac_id = LE_32(fwcmd->params.rsp.pmac_id);
return (0);
}
/*
* function to delete an unicast address associated with an interface
*
* dev - software handle to the device
* pmac_id - handle to the address added using ace_add_mac
*
* return 0 on success, EIO on failure
*/
int
oce_del_mac(struct oce_dev *dev, uint32_t if_id, uint32_t *pmac_id)
{
struct oce_mbx mbx;
struct mbx_del_common_iface_mac *fwcmd;
int ret;
bzero(&mbx, sizeof (struct oce_mbx));
fwcmd = (struct mbx_del_common_iface_mac *)&mbx.payload;
fwcmd->params.req.if_id = if_id;
fwcmd->params.req.pmac_id = *pmac_id;
/* initialize the ioctl header */
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_COMMON,
OPCODE_DEL_COMMON_IFACE_MAC,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_add_common_iface_mac));
/* fill rest of mbx */
mbx.u0.s.embedded = 1;
mbx.payload_length = sizeof (struct mbx_del_common_iface_mac);
DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ);
/* post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
return (ret);
}
/*
* function to send the mbx command to configure vlan
*
* dev - software handle to the device
* vtag_arr - array of vlan tags
* vtag_cnt - number of elements in array
* untagged - boolean TRUE/FLASE
* enable_promisc - flag to enable/disable VLAN promiscuous mode
*
* return 0 on success, EIO on failure
*/
int
oce_config_vlan(struct oce_dev *dev, uint32_t if_id,
struct normal_vlan *vtag_arr, uint8_t vtag_cnt,
boolean_t untagged, boolean_t enable_promisc)
{
struct oce_mbx mbx;
struct mbx_common_config_vlan *fwcmd;
int ret;
bzero(&mbx, sizeof (struct oce_mbx));
fwcmd = (struct mbx_common_config_vlan *)&mbx.payload;
/* initialize the ioctl header */
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_COMMON,
OPCODE_CONFIG_COMMON_IFACE_VLAN,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_common_config_vlan));
fwcmd->params.req.if_id = (uint8_t)if_id;
fwcmd->params.req.promisc = (uint8_t)enable_promisc;
fwcmd->params.req.untagged = (uint8_t)untagged;
fwcmd->params.req.num_vlans = vtag_cnt;
/* Set the vlan tag filter on hw */
if (!enable_promisc) {
bcopy(fwcmd->params.req.tags.normal_vlans, vtag_arr,
vtag_cnt * sizeof (struct normal_vlan));
}
/* fill rest of mbx */
mbx.u0.s.embedded = B_TRUE;
mbx.payload_length = sizeof (struct mbx_common_config_vlan);
DW_SWAP(u32ptr(&mbx), (OCE_BMBX_RHDR_SZ + mbx.payload_length));
/* post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
return (ret);
} /* oce_config_vlan */
/*
* function to enable or disable the link
*
* dev - software handle to the device
* mca_table - array of mcast address to update
* mca_cnt - number of elements in mca_table
* enable_promisc - flag to enable/disable mcast-promiscuous mode
*
* return 0 on success, EIO on failure
*/
int
oce_config_link(struct oce_dev *dev, boolean_t enable)
{
struct oce_mbx mbx;
struct mbx_common_func_link_cfg *fwcmd;
int ret;
bzero(&mbx, sizeof (struct oce_mbx));
fwcmd = (struct mbx_common_func_link_cfg *)&mbx.payload;
/* initialize the ioctl header */
mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0,
MBX_SUBSYSTEM_COMMON,
OPCODE_COMMON_FUNCTION_LINK_CONFIG,
MBX_TIMEOUT_SEC,
sizeof (struct mbx_common_config_vlan));
fwcmd->params.req.enable = enable;
/* fill rest of mbx */
mbx.u0.s.embedded = B_TRUE;
mbx.payload_length = sizeof (struct mbx_common_func_link_cfg);
DW_SWAP(u32ptr(&mbx), (OCE_BMBX_RHDR_SZ + mbx.payload_length));
/* post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
return (ret);
} /* oce_config_link */
/*
* function called from the gld ioctl entry point to send a mbx to fw
*
* dev - software handle to the device
* mp - mblk_t containing the user data
* payload_len = [OUT] pointer to return the length of the payload written
*
* return 0 on Success
*/
int
oce_issue_mbox(struct oce_dev *dev, queue_t *wq, mblk_t *mp,
uint32_t *payload_len)
{
int ret;
struct oce_mbx mbx;
struct mbx_hdr hdr;
ddi_dma_handle_t dma_handle;
boolean_t is_embedded = B_FALSE;
uint32_t payload_length;
int num_buf = 0;
int alloc_len;
caddr_t sg_va;
ddi_acc_handle_t acc_handle;
size_t actual_len;
_NOTE(ARGUNUSED(wq));
bzero(&mbx, sizeof (struct oce_mbx));
bcopy(mp->b_cont->b_rptr, &hdr, sizeof (struct mbx_hdr));
DW_SWAP(u32ptr(&hdr), sizeof (struct mbx_hdr));
payload_length = hdr.u0.req.request_length +
sizeof (struct mbx_hdr);
is_embedded = (payload_length <= sizeof (struct oce_mbx_payload));
alloc_len = MBLKL(mp->b_cont);
oce_log(dev, CE_NOTE, MOD_CONFIG, "Mailbox: "
"DW[0] 0x%x DW[1] 0x%x DW[2]0x%x DW[3]0x%x,"
"MBLKL(%lu) ALLOCLEN(%d)",
hdr.u0.dw[0], hdr.u0.dw[1],
hdr.u0.dw[2], hdr.u0.dw[3],
MBLKL(mp->b_cont), alloc_len);
if (hdr.u0.req.opcode == OPCODE_WRITE_COMMON_FLASHROM) {
struct mbx_common_read_write_flashrom *fwcmd =
(struct mbx_common_read_write_flashrom *)
mp->b_cont->b_rptr;
if (dev->cookie != 0 && dev->cookie != hdr.u0.req.rsvd0)
return (EINVAL);
if (dev->cookie == 0)
dev->cookie = hdr.u0.req.rsvd0;
hdr.u0.req.rsvd0 = 0;
/* get the timeout from the command header */
mbx.tag[0] = hdr.u0.req.timeout;
oce_log(dev, CE_NOTE, MOD_CONFIG, "Mailbox params:"
"OPCODE(%d) OPTYPE = %d SIZE = %d OFFSET = %d",
fwcmd->flash_op_code, fwcmd->flash_op_type,
fwcmd->data_buffer_size, fwcmd->data_offset);
}
if (!is_embedded) {
mblk_t *mp_w = mp->b_cont;
ddi_dma_cookie_t cookie;
uint32_t count = 0;
/* allocate dma handle */
ret = ddi_dma_alloc_handle(dev->dip,
&oce_sgl_dma_attr, DDI_DMA_DONTWAIT, NULL,
&dma_handle);
if (ret != DDI_SUCCESS) {
oce_log(dev, CE_NOTE, MOD_CONFIG, "%s",
"Failed to alloc DMA handle");
ret = ENOMEM;
goto fail;
}
/* allocate the DMA-able memory */
ret = ddi_dma_mem_alloc(dma_handle, alloc_len,
&oce_sgl_buf_accattr,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
DDI_DMA_DONTWAIT,
NULL, &sg_va, &actual_len, &acc_handle);
if (ret != DDI_SUCCESS) {
oce_log(dev, CE_NOTE, MOD_CONFIG, "%s",
"Failed to alloc DMA memory");
ret = ENOMEM;
goto dma_alloc_fail;
}
bcopy((caddr_t)mp_w->b_rptr, sg_va, MBLKL(mp->b_cont));
/* bind mblk mem to handle */
ret = ddi_dma_addr_bind_handle(
dma_handle,
(struct as *)0, sg_va,
MBLKL(mp_w),
DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
DDI_DMA_DONTWAIT, NULL, &cookie, &count);
if (ret != DDI_DMA_MAPPED) {
ret = ENOMEM;
oce_log(dev, CE_NOTE, MOD_CONFIG,
"Failed to bind DMA handle ret code: %d",
ret);
goto dma_bind_fail;
}
for (num_buf = 0; num_buf < count; num_buf++) {
/* fill the mbx sglist */
mbx.payload.u0.u1.sgl[num_buf].pa_lo =
ADDR_LO(cookie.dmac_laddress);
mbx.payload.u0.u1.sgl[num_buf].pa_hi =
ADDR_HI(cookie.dmac_laddress);
mbx.payload.u0.u1.sgl[num_buf].length =
(uint32_t)cookie.dmac_size;
mbx.payload_length +=
mbx.payload.u0.u1.sgl[num_buf].length;
mbx.u0.s.sge_count++;
if (count > 1)
(void) ddi_dma_nextcookie(dma_handle, &cookie);
}
mbx.u0.s.embedded = 0;
DW_SWAP(u32ptr(&mbx), OCE_BMBX_RHDR_SZ +
(sizeof (struct oce_mq_sge) * count));
} else {
/* fill rest of mbx */
mbx.u0.s.embedded = 1;
mbx.payload_length = payload_length;
bcopy(mp->b_cont->b_rptr, &mbx.payload, payload_length);
DW_SWAP(u32ptr(&mbx), OCE_BMBX_RHDR_SZ);
}
/* now post the command */
ret = oce_mbox_post(dev, &mbx, NULL);
bcopy(mp->b_cont->b_rptr, &hdr, sizeof (struct mbx_hdr));
DW_SWAP(u32ptr(&hdr), sizeof (struct mbx_hdr));
if (ret != DDI_SUCCESS) {
oce_log(dev, CE_WARN, MOD_CONFIG,
"Failed to post the mailbox: %d", ret);
*payload_len = hdr.u0.rsp.rsp_length +
sizeof (struct mbx_hdr);
if (is_embedded) {
bcopy(&mbx.payload, mp->b_cont->b_rptr,
MBLKL(mp->b_cont));
goto fail;
} else {
(void) ddi_dma_sync(dma_handle, 0, 0,
DDI_DMA_SYNC_FORKERNEL);
if (oce_fm_check_dma_handle(dev, dma_handle) !=
DDI_FM_OK) {
ddi_fm_service_impact(dev->dip,
DDI_SERVICE_DEGRADED);
}
bcopy(sg_va, mp->b_cont->b_rptr,
sizeof (struct mbx_hdr));
goto post_fail;
}
}
if (hdr.u0.req.opcode == OPCODE_WRITE_COMMON_FLASHROM) {
struct mbx_common_read_write_flashrom *fwcmd =
(struct mbx_common_read_write_flashrom *)
mp->b_cont->b_rptr;
if (LE_32(fwcmd->flash_op_code) == MGMT_FLASHROM_OPCODE_FLASH)
dev->cookie = 0;
}
payload_length = hdr.u0.rsp.rsp_length + sizeof (struct mbx_hdr);
/* Copy the response back only if this is an embedded mbx cmd */
if (is_embedded) {
bcopy(&mbx.payload, mp->b_cont->b_rptr,
min(payload_length, MBLKL(mp->b_cont)));
} else {
/* sync */
(void) ddi_dma_sync(dma_handle, 0, 0,
DDI_DMA_SYNC_FORKERNEL);
if (oce_fm_check_dma_handle(dev, dma_handle) != DDI_FM_OK) {
ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED);
}
/* copy back from kernel allocated buffer to user buffer */
bcopy(sg_va, mp->b_cont->b_rptr, MBLKL(mp->b_cont));
/* unbind and free dma handles */
(void) ddi_dma_unbind_handle(dma_handle);
ddi_dma_mem_free(&acc_handle);
ddi_dma_free_handle(&dma_handle);
}
*payload_len = payload_length;
return (0);
post_fail:
(void) ddi_dma_unbind_handle(dma_handle);
dma_bind_fail:
ddi_dma_mem_free(&acc_handle);
dma_alloc_fail:
ddi_dma_free_handle(&dma_handle);
fail:
alloc_err:
if (hdr.u0.req.opcode == OPCODE_WRITE_COMMON_FLASHROM) {
dev->cookie = 0;
}
return (ret);
}