#include <sys/scsi/adapters/smrt/smrt.h>
static ddi_dma_attr_t smrt_command_dma_attr = {
.dma_attr_version = DMA_ATTR_V0,
.dma_attr_addr_lo = 0x00000000,
.dma_attr_addr_hi = 0xFFFFFFFF,
.dma_attr_count_max = 0x00FFFFFF,
.dma_attr_align = 0x20,
.dma_attr_burstsizes = 0x20,
.dma_attr_minxfer = DMA_UNIT_8,
.dma_attr_maxxfer = 0xFFFFFFFF,
.dma_attr_seg = 0x0000FFFF,
.dma_attr_sgllen = 1,
.dma_attr_granular = 512,
.dma_attr_flags = 0
};
static ddi_device_acc_attr_t smrt_command_dev_attr = {
.devacc_attr_version = DDI_DEVICE_ATTR_V0,
.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC,
.devacc_attr_dataorder = DDI_STRICTORDER_ACC,
.devacc_attr_access = 0
};
static void smrt_contig_free(smrt_dma_t *);
static int
smrt_check_command_type(smrt_command_type_t type)
{
switch (type) {
case SMRT_CMDTYPE_ABORTQ:
case SMRT_CMDTYPE_SCSA:
case SMRT_CMDTYPE_INTERNAL:
case SMRT_CMDTYPE_PREINIT:
case SMRT_CMDTYPE_EVENT:
return (type);
}
panic("unexpected command type");
}
static int
smrt_contig_alloc(smrt_t *smrt, smrt_dma_t *smdma, size_t sz, int kmflags,
void **vap, uint32_t *pap)
{
caddr_t va;
int rv;
dev_info_t *dip = smrt->smrt_dip;
int (*dma_wait)(caddr_t) = (kmflags == KM_SLEEP) ? DDI_DMA_SLEEP :
DDI_DMA_DONTWAIT;
VERIFY(kmflags == KM_SLEEP || kmflags == KM_NOSLEEP);
VERIFY0(smdma->smdma_level);
if ((rv = ddi_dma_alloc_handle(dip, &smrt_command_dma_attr,
dma_wait, NULL, &smdma->smdma_dma_handle)) != DDI_SUCCESS) {
dev_err(dip, CE_WARN, "DMA handle allocation failed (%x)",
rv);
goto fail;
}
smdma->smdma_level |= SMRT_DMALEVEL_HANDLE_ALLOC;
if ((rv = ddi_dma_mem_alloc(smdma->smdma_dma_handle, sz,
&smrt_command_dev_attr, DDI_DMA_CONSISTENT, dma_wait, NULL,
&va, &smdma->smdma_real_size, &smdma->smdma_acc_handle)) !=
DDI_SUCCESS) {
dev_err(dip, CE_WARN, "DMA memory allocation failed (%x)", rv);
goto fail;
}
smdma->smdma_level |= SMRT_DMALEVEL_MEMORY_ALLOC;
if ((rv = ddi_dma_addr_bind_handle(smdma->smdma_dma_handle,
NULL, va, smdma->smdma_real_size,
DDI_DMA_CONSISTENT | DDI_DMA_RDWR, dma_wait, NULL,
smdma->smdma_dma_cookies, &smdma->smdma_dma_ncookies)) !=
DDI_DMA_MAPPED) {
dev_err(dip, CE_WARN, "DMA handle bind failed (%x)", rv);
goto fail;
}
smdma->smdma_level |= SMRT_DMALEVEL_HANDLE_BOUND;
VERIFY3U(smdma->smdma_dma_ncookies, ==, 1);
*pap = smdma->smdma_dma_cookies[0].dmac_address;
*vap = (void *)va;
return (DDI_SUCCESS);
fail:
*vap = NULL;
*pap = 0;
smrt_contig_free(smdma);
return (DDI_FAILURE);
}
static void
smrt_contig_free(smrt_dma_t *smdma)
{
if (smdma->smdma_level & SMRT_DMALEVEL_HANDLE_BOUND) {
VERIFY3U(ddi_dma_unbind_handle(smdma->smdma_dma_handle), ==,
DDI_SUCCESS);
smdma->smdma_level &= ~SMRT_DMALEVEL_HANDLE_BOUND;
}
if (smdma->smdma_level & SMRT_DMALEVEL_MEMORY_ALLOC) {
ddi_dma_mem_free(&smdma->smdma_acc_handle);
smdma->smdma_level &= ~SMRT_DMALEVEL_MEMORY_ALLOC;
}
if (smdma->smdma_level & SMRT_DMALEVEL_HANDLE_ALLOC) {
ddi_dma_free_handle(&smdma->smdma_dma_handle);
smdma->smdma_level &= ~SMRT_DMALEVEL_HANDLE_ALLOC;
}
VERIFY(smdma->smdma_level == 0);
bzero(smdma, sizeof (*smdma));
}
static smrt_command_t *
smrt_command_alloc_impl(smrt_t *smrt, smrt_command_type_t type, int kmflags)
{
smrt_command_t *smcm;
VERIFY(kmflags == KM_SLEEP || kmflags == KM_NOSLEEP);
if ((smcm = kmem_zalloc(sizeof (*smcm), kmflags)) == NULL) {
return (NULL);
}
smcm->smcm_ctlr = smrt;
smcm->smcm_type = smrt_check_command_type(type);
size_t contig_size = 0;
contig_size += P2ROUNDUP_TYPED(sizeof (CommandList_t), 32, size_t);
size_t errorinfo_offset = contig_size;
contig_size += P2ROUNDUP_TYPED(sizeof (ErrorInfo_t), 32, size_t);
if (smrt_contig_alloc(smrt, &smcm->smcm_contig, contig_size,
kmflags, (void **)&smcm->smcm_va_cmd, &smcm->smcm_pa_cmd) !=
DDI_SUCCESS) {
kmem_free(smcm, sizeof (*smcm));
return (NULL);
}
smcm->smcm_va_err = (void *)((caddr_t)smcm->smcm_va_cmd +
errorinfo_offset);
smcm->smcm_pa_err = smcm->smcm_pa_cmd + errorinfo_offset;
VERIFY0(smcm->smcm_pa_cmd & 0x1f);
VERIFY0(smcm->smcm_pa_err & 0x1f);
bzero(smcm->smcm_va_cmd, contig_size);
smcm->smcm_va_cmd->ErrDesc.Addr = smcm->smcm_pa_err;
smcm->smcm_va_cmd->ErrDesc.Len = sizeof (ErrorInfo_t);
return (smcm);
}
smrt_command_t *
smrt_command_alloc_preinit(smrt_t *smrt, size_t datasize, int kmflags)
{
smrt_command_t *smcm;
if ((smcm = smrt_command_alloc_impl(smrt, SMRT_CMDTYPE_PREINIT,
kmflags)) == NULL) {
return (NULL);
}
smcm->smcm_tag = SMRT_PRE_TAG_NUMBER;
smcm->smcm_va_cmd->Header.Tag.tag_value = SMRT_PRE_TAG_NUMBER;
if (smrt_command_attach_internal(smrt, smcm, datasize, kmflags) != 0) {
smrt_command_free(smcm);
return (NULL);
}
return (smcm);
}
smrt_command_t *
smrt_command_alloc(smrt_t *smrt, smrt_command_type_t type, int kmflags)
{
smrt_command_t *smcm;
VERIFY(type != SMRT_CMDTYPE_PREINIT);
if ((smcm = smrt_command_alloc_impl(smrt, type, kmflags)) == NULL) {
return (NULL);
}
mutex_enter(&smrt->smrt_mutex);
list_insert_tail(&smrt->smrt_commands, smcm);
mutex_exit(&smrt->smrt_mutex);
return (smcm);
}
int
smrt_command_attach_internal(smrt_t *smrt, smrt_command_t *smcm, size_t len,
int kmflags)
{
smrt_command_internal_t *smcmi;
VERIFY(kmflags == KM_SLEEP || kmflags == KM_NOSLEEP);
VERIFY3U(len, <=, UINT32_MAX);
if ((smcmi = kmem_zalloc(sizeof (*smcmi), kmflags)) == NULL) {
return (ENOMEM);
}
if (smrt_contig_alloc(smrt, &smcmi->smcmi_contig, len, kmflags,
&smcmi->smcmi_va, &smcmi->smcmi_pa) != DDI_SUCCESS) {
kmem_free(smcmi, sizeof (*smcmi));
return (ENOMEM);
}
bzero(smcmi->smcmi_va, smcmi->smcmi_len);
smcm->smcm_internal = smcmi;
smcm->smcm_va_cmd->SG[0].Addr = smcmi->smcmi_pa;
smcm->smcm_va_cmd->SG[0].Len = (uint32_t)len;
smcm->smcm_va_cmd->Header.SGList = 1;
smcm->smcm_va_cmd->Header.SGTotal = 1;
return (0);
}
void
smrt_command_reuse(smrt_command_t *smcm)
{
smrt_t *smrt = smcm->smcm_ctlr;
mutex_enter(&smrt->smrt_mutex);
VERIFY(!(smcm->smcm_status & SMRT_CMD_STATUS_INFLIGHT));
smcm->smcm_status = SMRT_CMD_STATUS_REUSED;
VERIFY(!list_link_active(&smcm->smcm_link_abort));
VERIFY(!list_link_active(&smcm->smcm_link_finish));
smcm->smcm_tag = 0;
smcm->smcm_va_cmd->Header.Tag.tag_value = 0;
mutex_exit(&smrt->smrt_mutex);
}
void
smrt_command_free(smrt_command_t *smcm)
{
smrt_t *smrt = smcm->smcm_ctlr;
VERIFY(!(smcm->smcm_status & SMRT_CMD_STATUS_INFLIGHT));
if (smcm->smcm_internal != NULL) {
smrt_command_internal_t *smcmi = smcm->smcm_internal;
smrt_contig_free(&smcmi->smcmi_contig);
kmem_free(smcmi, sizeof (*smcmi));
}
smrt_contig_free(&smcm->smcm_contig);
if (smcm->smcm_type != SMRT_CMDTYPE_PREINIT) {
mutex_enter(&smrt->smrt_mutex);
VERIFY(!list_link_active(&smcm->smcm_link_abort));
VERIFY(!list_link_active(&smcm->smcm_link_finish));
list_remove(&smrt->smrt_commands, smcm);
mutex_exit(&smrt->smrt_mutex);
}
kmem_free(smcm, sizeof (*smcm));
}
smrt_command_t *
smrt_lookup_inflight(smrt_t *smrt, uint32_t tag)
{
smrt_command_t srch;
VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
bzero(&srch, sizeof (srch));
srch.smcm_tag = tag;
return (avl_find(&smrt->smrt_inflight, &srch, NULL));
}