#include <sys/usb/hcd/xhci/xhci.h>
#include <sys/byteorder.h>
static void
xhci_scratchpad_fini(xhci_t *xhcip)
{
xhci_scratchpad_t *xsp = &xhcip->xhci_scratchpad;
if (xsp->xsp_scratch_dma != NULL) {
int i, npages;
npages = xhcip->xhci_caps.xcap_max_scratch;
for (i = 0; i < npages; i++) {
xhci_dma_free(&xsp->xsp_scratch_dma[i]);
}
kmem_free(xsp->xsp_scratch_dma,
sizeof (xhci_dma_buffer_t) * npages);
xsp->xsp_scratch_dma = NULL;
}
xhci_dma_free(&xsp->xsp_addr_dma);
xsp->xsp_addrs = NULL;
}
void
xhci_context_fini(xhci_t *xhcip)
{
xhci_scratchpad_fini(xhcip);
xhci_dma_free(&xhcip->xhci_dcbaa.xdc_dma);
xhcip->xhci_dcbaa.xdc_base_addrs = NULL;
}
static int
xhci_scratchpad_alloc(xhci_t *xhcip)
{
int npages, i;
xhci_scratchpad_t *xsp;
ddi_device_acc_attr_t acc;
ddi_dma_attr_t attr;
ASSERT(xhcip->xhci_caps.xcap_max_scratch > 0);
npages = xhcip->xhci_caps.xcap_max_scratch;
xhci_dma_acc_attr(xhcip, &acc);
xhci_dma_dma_attr(xhcip, &attr);
xsp = &xhcip->xhci_scratchpad;
if (xhci_dma_alloc(xhcip, &xsp->xsp_addr_dma, &attr, &acc,
B_TRUE, sizeof (uint64_t) * npages, B_FALSE) == B_FALSE) {
xhci_log(xhcip, "!failed to allocate DMA memory for device "
"context");
return (ENOMEM);
}
xsp->xsp_addrs = (void *)xsp->xsp_addr_dma.xdb_va;
xhci_dma_scratchpad_attr(xhcip, &attr);
xsp->xsp_scratch_dma = kmem_zalloc(sizeof (xhci_dma_buffer_t) * npages,
KM_SLEEP);
for (i = 0; i < npages; i++) {
if (xhci_dma_alloc(xhcip, &xsp->xsp_scratch_dma[i], &attr, &acc,
B_TRUE, xhcip->xhci_caps.xcap_pagesize, B_FALSE) ==
B_FALSE) {
xhci_scratchpad_fini(xhcip);
xhci_log(xhcip, "!failed to allocate DMA memory for "
"device scratchpad");
return (ENOMEM);
}
}
return (0);
}
static int
xhci_dcbaa_alloc(xhci_t *xhcip)
{
xhci_dcbaa_t *dcb;
ddi_device_acc_attr_t acc;
ddi_dma_attr_t attr;
dcb = &xhcip->xhci_dcbaa;
xhci_dma_acc_attr(xhcip, &acc);
xhci_dma_dma_attr(xhcip, &attr);
if (xhci_dma_alloc(xhcip, &dcb->xdc_dma, &attr, &acc,
B_FALSE, sizeof (uint64_t) * XHCI_MAX_SLOTS, B_FALSE) == B_FALSE) {
xhci_log(xhcip, "!failed to allocate DMA memory for device "
"context");
return (ENOMEM);
}
dcb->xdc_base_addrs = (uint64_t *)dcb->xdc_dma.xdb_va;
return (0);
}
int
xhci_context_init(xhci_t *xhcip)
{
int ret;
xhci_dcbaa_t *dcb = &xhcip->xhci_dcbaa;
if (dcb->xdc_base_addrs == NULL) {
if ((ret = xhci_dcbaa_alloc(xhcip)) != 0)
return (ret);
}
bzero(dcb->xdc_base_addrs, sizeof (uint64_t) * XHCI_MAX_SLOTS);
if (xhcip->xhci_caps.xcap_max_scratch != 0) {
int i, npages;
xhci_scratchpad_t *xsp = &xhcip->xhci_scratchpad;
if (xsp->xsp_addrs == NULL &&
(ret = xhci_scratchpad_alloc(xhcip)) != 0) {
xhci_context_fini(xhcip);
return (ret);
}
dcb->xdc_base_addrs[XHCI_DCBAA_SCRATCHPAD_INDEX] =
LE_64(xhci_dma_pa(&xsp->xsp_addr_dma));
npages = xhcip->xhci_caps.xcap_max_scratch;
for (i = 0; i < npages; i++) {
xsp->xsp_addrs[i] =
LE_64(xhci_dma_pa(&xsp->xsp_scratch_dma[i]));
}
XHCI_DMA_SYNC(xsp->xsp_addr_dma, DDI_DMA_SYNC_FORDEV);
if (xhci_check_dma_handle(xhcip, &xsp->xsp_addr_dma) !=
DDI_FM_OK) {
ddi_fm_service_impact(xhcip->xhci_dip,
DDI_SERVICE_LOST);
return (EIO);
}
}
XHCI_DMA_SYNC(dcb->xdc_dma, DDI_DMA_SYNC_FORDEV);
if (xhci_check_dma_handle(xhcip, &dcb->xdc_dma) != DDI_FM_OK) {
ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
return (EIO);
}
xhci_put64(xhcip, XHCI_R_OPER, XHCI_DCBAAP,
LE_64(xhci_dma_pa(&dcb->xdc_dma)));
if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
ddi_fm_service_impact(xhcip->xhci_dip,
DDI_SERVICE_LOST);
return (EIO);
}
return (0);
}
boolean_t
xhci_context_slot_output_init(xhci_t *xhcip, xhci_device_t *xd)
{
xhci_dcbaa_t *dcb = &xhcip->xhci_dcbaa;
VERIFY(xd->xd_slot > 0 &&
xd->xd_slot <= xhcip->xhci_caps.xcap_max_slots);
xhcip->xhci_dcbaa.xdc_base_addrs[xd->xd_slot] =
LE_64(xhci_dma_pa(&xd->xd_octx));
XHCI_DMA_SYNC(dcb->xdc_dma, DDI_DMA_SYNC_FORDEV);
if (xhci_check_dma_handle(xhcip, &dcb->xdc_dma) != DDI_FM_OK) {
xhci_error(xhcip, "failed to initialize slot output context "
"for device on port %d, slot %d: fatal FM error "
"synchronizing DCBAA slot DMA memory", xd->xd_slot,
xd->xd_port);
xhci_fm_runtime_reset(xhcip);
return (B_FALSE);
}
return (B_TRUE);
}
void
xhci_context_slot_output_fini(xhci_t *xhcip, xhci_device_t *xd)
{
xhci_dcbaa_t *dcb = &xhcip->xhci_dcbaa;
VERIFY(xd->xd_slot > 0 &&
xd->xd_slot <= xhcip->xhci_caps.xcap_max_slots);
xhcip->xhci_dcbaa.xdc_base_addrs[xd->xd_slot] = 0ULL;
XHCI_DMA_SYNC(dcb->xdc_dma, DDI_DMA_SYNC_FORDEV);
if (xhci_check_dma_handle(xhcip, &dcb->xdc_dma) != DDI_FM_OK) {
xhci_error(xhcip, "failed to finalize slot output context "
"for device on port %d, slot %d: fatal FM error "
"synchronizing DCBAA slot DMA memory", xd->xd_slot,
xd->xd_port);
xhci_fm_runtime_reset(xhcip);
}
}