#include <ql_apps.h>
#include <ql_api.h>
#include <ql_fm.h>
qlc_fm_ereport_t qlc_fm_ereport_tbl[] = {
{QL_FM_EREPORT_DMA_ERR,
"A DMA direction error",
QL_FM_DEVICE_DMA_ERR,
DDI_FM_DEVICE_INTERN_CORR,
DDI_SERVICE_UNAFFECTED},
{QL_FM_EREPORT_BAD_PAYLOAD,
"A bad payload detected",
QL_FM_DEVICE_BAD_PAYLOAD,
DDI_FM_DEVICE_INTERN_CORR,
DDI_SERVICE_UNAFFECTED},
{QL_FM_EREPORT_CMD_FAILED,
"A command failed",
QL_FM_DEVICE_CMD_FAILED,
DDI_FM_DEVICE_INTERN_CORR,
DDI_SERVICE_UNAFFECTED},
{QL_FM_EREPORT_CHIP_HANG,
"fw is not responding",
QL_FM_DEVICE_CHIP_HANG,
DDI_FM_DEVICE_INTERN_CORR,
DDI_SERVICE_UNAFFECTED},
{QL_FM_EREPORT_UNKNOWN,
"Unknown error reported",
QL_FM_DEVICE_UNKNOWN,
DDI_FM_DEVICE_INTERN_CORR,
DDI_SERVICE_UNAFFECTED},
{QL_FM_EREPORT_MBA_REQ_TRANSFER_ERR,
"Async event request transfer error",
QL_FM_DEVICE_MBA_REQ_TRANSFER_ERR,
DDI_FM_DEVICE_INVAL_STATE,
DDI_SERVICE_LOST},
{QL_FM_EREPORT_MBA_RSP_TRANSFER_ERR,
"Async event response transfer error",
QL_FM_DEVICE_MBA_RSP_TRANSFER_ERR,
DDI_FM_DEVICE_INVAL_STATE,
DDI_SERVICE_LOST},
{QL_FM_EREPORT_ACC_HANDLE_CHECK,
"ACC handle check return failed",
QL_FM_DEVICE_ACC_HANDLE_ERR,
DDI_FM_DEVICE_INTERN_UNCORR,
DDI_SERVICE_LOST},
{QL_FM_EREPORT_DMA_HANDLE_CHECK,
"DMA handle check return failed",
QL_FM_DEVICE_DMA_HANDLE_ERR,
DDI_FM_DEVICE_INTERN_CORR,
DDI_SERVICE_UNAFFECTED},
{0, NULL, NULL, NULL, 0},
};
int
qlc_fm_check_acc_handle(ql_adapter_state_t *ha, ddi_acc_handle_t handle)
{
ddi_fm_error_t err;
if (!DDI_FM_ACC_ERR_CAP(ha->fm_capabilities)) {
return (DDI_FM_OK);
}
err.fme_status = DDI_FM_OK;
ddi_fm_acc_err_get(handle, &err, DDI_FME_VERSION);
(void) ddi_fm_acc_err_clear(handle, DDI_FME_VERSION);
return (err.fme_status);
}
int
qlc_fm_check_dma_handle(ql_adapter_state_t *ha, ddi_dma_handle_t handle)
{
ddi_fm_error_t err;
if (!DDI_FM_DMA_ERR_CAP(ha->fm_capabilities)) {
return (DDI_FM_OK);
}
err.fme_status = DDI_FM_OK;
ddi_fm_dma_err_get(handle, &err, DDI_FME_VERSION);
return (err.fme_status);
}
void
qlc_fm_check_pkt_dma_handle(ql_adapter_state_t *ha, ql_srb_t *sb)
{
fc_packet_t *pkt = sb->pkt;
int rval = DDI_FM_OK;
if (!DDI_FM_DMA_ERR_CAP(ha->fm_capabilities)) {
return;
}
if (pkt->pkt_cmd_acc != NULL && pkt->pkt_cmdlen) {
rval = qlc_fm_check_dma_handle(ha, pkt->pkt_cmd_dma);
}
if (pkt->pkt_resp_acc != NULL && rval == DDI_FM_OK &&
pkt->pkt_rsplen != 0) {
rval = qlc_fm_check_dma_handle(ha, pkt->pkt_resp_dma);
}
if (((pkt->pkt_data_acc != NULL) & (rval == DDI_FM_OK) &
(pkt->pkt_datalen != 0)) != 0) {
rval = qlc_fm_check_dma_handle(ha, pkt->pkt_data_dma);
}
if (rval != DDI_FM_OK) {
pkt->pkt_state = FC_PKT_TRAN_ERROR;
pkt->pkt_reason = FC_REASON_DMA_ERROR;
pkt->pkt_expln = FC_EXPLN_NONE;
pkt->pkt_action = FC_ACTION_RETRYABLE;
(void) qlc_fm_report_err_impact(ha,
QL_FM_EREPORT_DMA_HANDLE_CHECK);
}
}
int
qlc_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data)
{
pci_ereport_post(dip, err, NULL);
return (err->fme_status);
}
void
qlc_fm_service_impact(ql_adapter_state_t *ha, int impact)
{
if (!DDI_FM_EREPORT_CAP(ha->fm_capabilities)) {
return;
}
ddi_fm_service_impact(ha->dip, impact);
}
void
qlc_fm_init(ql_adapter_state_t *ha)
{
ddi_iblock_cookie_t iblk;
if (ha->fm_capabilities == DDI_FM_NOT_CAPABLE) {
return;
}
if (ha->fm_capabilities) {
ddi_fm_init(ha->dip, (int *)&ha->fm_capabilities, &iblk);
}
if (DDI_FM_EREPORT_CAP(ha->fm_capabilities) ||
DDI_FM_ERRCB_CAP(ha->fm_capabilities)) {
pci_ereport_setup(ha->dip);
}
if (DDI_FM_ERRCB_CAP(ha->fm_capabilities)) {
ddi_fm_handler_register(ha->dip,
qlc_fm_error_cb, (void*)ha);
}
if (DDI_FM_ACC_ERR_CAP(ha->fm_capabilities)) {
ql_dev_acc_attr.devacc_attr_access = DDI_FLAGERR_ACC;
} else {
ql_dev_acc_attr.devacc_attr_access = DDI_DEFAULT_ACC;
}
if (DDI_FM_DMA_ERR_CAP(ha->fm_capabilities)) {
ha->bit32_io_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
ha->bit64_io_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
} else {
ha->bit32_io_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
ha->bit64_io_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
}
}
void
qlc_fm_fini(ql_adapter_state_t *ha)
{
if (ha->fm_capabilities) {
if (DDI_FM_EREPORT_CAP(ha->fm_capabilities) ||
DDI_FM_ERRCB_CAP(ha->fm_capabilities)) {
pci_ereport_teardown(ha->dip);
}
if (DDI_FM_ERRCB_CAP(ha->fm_capabilities)) {
ddi_fm_handler_unregister(ha->dip);
}
ddi_fm_fini(ha->dip);
}
}
void
qlc_fm_report_err_impact(ql_adapter_state_t *ha, uint32_t fid)
{
uint64_t ena;
char eclass[QL_FM_MAX_CLASS];
qlc_fm_ereport_t *ereport = NULL;
if (!DDI_FM_EREPORT_CAP(ha->fm_capabilities)) {
return;
}
if (fid > QL_FM_EREPORT_NONE) {
cmn_err(CE_NOTE, "Not reported yet");
return;
}
ereport = &qlc_fm_ereport_tbl[fid];
(void) snprintf(eclass, QL_FM_MAX_CLASS, "%s.%s",
DDI_FM_DEVICE,
ereport->gen_eclass);
ena = fm_ena_generate(0, FM_ENA_FMT1);
switch (ereport->fid) {
case QL_FM_EREPORT_DMA_ERR:
case QL_FM_EREPORT_BAD_PAYLOAD:
case QL_FM_EREPORT_CMD_FAILED:
case QL_FM_EREPORT_CHIP_HANG:
case QL_FM_EREPORT_UNKNOWN:
case QL_FM_EREPORT_MBA_REQ_TRANSFER_ERR:
case QL_FM_EREPORT_MBA_RSP_TRANSFER_ERR:
ddi_fm_ereport_post(ha->dip, eclass, ena,
DDI_NOSLEEP,
FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
"Detailed error desc", DATA_TYPE_STRING, ereport->desc,
"Instance number", DATA_TYPE_UINT8, ha->instance,
NULL);
break;
case QL_FM_EREPORT_ACC_HANDLE_CHECK:
case QL_FM_EREPORT_DMA_HANDLE_CHECK:
ddi_fm_ereport_post(ha->dip, eclass, ena,
DDI_NOSLEEP,
FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
"Detailed error desc", DATA_TYPE_STRING, ereport->desc,
"Instance number", DATA_TYPE_UINT8, ha->instance,
NULL);
break;
default:
ddi_fm_ereport_post(ha->dip, eclass, ena,
DDI_NOSLEEP,
FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, NULL);
break;
}
qlc_fm_service_impact(ha, ereport->impact_code);
}