#include <sys/types.h>
#include <sys/debug.h>
#include "ata_common.h"
#include "ata_disk.h"
#include "atapi.h"
#include "pciide.h"
#define PCIIDE_STATUS_GET(hdl, addr) \
ddi_get8((hdl), ((uchar_t *)(addr) + PCIIDE_BMISX_REG))
ddi_dma_attr_t ata_pciide_dma_attr = {
DMA_ATTR_V0,
0,
0xffffffffU,
0xffff,
sizeof (int),
1,
1,
0x100 << SCTRSHFT,
0xffff,
ATA_DMA_NSEGS,
512,
0
};
#define PCIIDE_BOUNDARY (0x1000)
ddi_dma_attr_t ata_prd_dma_attr = {
DMA_ATTR_V0,
0,
0xffffffffU,
PCIIDE_BOUNDARY - 1,
sizeof (int),
1,
1,
PCIIDE_BOUNDARY,
PCIIDE_BOUNDARY - 1,
1,
1,
0
};
size_t prd_size = sizeof (prde_t) * ATA_DMA_NSEGS;
int
ata_pciide_alloc(
dev_info_t *dip,
ata_ctl_t *ata_ctlp)
{
ddi_device_acc_attr_t dev_attr;
ddi_dma_cookie_t cookie;
size_t buf_size;
uint_t count;
int rc;
dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
dev_attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
rc = ddi_dma_alloc_handle(dip, &ata_prd_dma_attr, DDI_DMA_SLEEP, NULL,
&ata_ctlp->ac_sg_handle);
if (rc != DDI_SUCCESS) {
ADBG_ERROR(("ata_pciide_alloc 0x%p handle %d\n",
(void *)ata_ctlp, rc));
goto err3;
}
rc = ddi_dma_mem_alloc(ata_ctlp->ac_sg_handle, prd_size, &dev_attr,
DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
&ata_ctlp->ac_sg_list, &buf_size, &ata_ctlp->ac_sg_acc_handle);
if (rc != DDI_SUCCESS) {
ADBG_ERROR(("ata_pciide_alloc 0x%p mem %d\n",
(void *)ata_ctlp, rc));
goto err2;
}
rc = ddi_dma_addr_bind_handle(ata_ctlp->ac_sg_handle, NULL,
ata_ctlp->ac_sg_list, buf_size,
DDI_DMA_WRITE | DDI_DMA_CONSISTENT,
DDI_DMA_SLEEP, NULL, &cookie, &count);
if (rc != DDI_DMA_MAPPED) {
ADBG_ERROR(("ata_pciide_alloc 0x%p bind %d\n",
(void *)ata_ctlp, rc));
goto err1;
}
ASSERT(count == 1);
ASSERT((cookie.dmac_address & (sizeof (int) - 1)) == 0);
#define Mask4K 0xfffff000
ASSERT((cookie.dmac_address & Mask4K)
== ((cookie.dmac_address + cookie.dmac_size - 1) & Mask4K));
ata_ctlp->ac_sg_paddr = cookie.dmac_address;
return (TRUE);
err1:
ddi_dma_mem_free(&ata_ctlp->ac_sg_acc_handle);
ata_ctlp->ac_sg_acc_handle = NULL;
err2:
ddi_dma_free_handle(&ata_ctlp->ac_sg_handle);
ata_ctlp->ac_sg_handle = NULL;
err3:
return (FALSE);
}
void
ata_pciide_free(ata_ctl_t *ata_ctlp)
{
if (ata_ctlp->ac_sg_handle == NULL)
return;
(void) ddi_dma_unbind_handle(ata_ctlp->ac_sg_handle);
ddi_dma_mem_free(&ata_ctlp->ac_sg_acc_handle);
ddi_dma_free_handle(&ata_ctlp->ac_sg_handle);
ata_ctlp->ac_sg_handle = NULL;
ata_ctlp->ac_sg_acc_handle = NULL;
}
void
ata_pciide_dma_setup(
ata_ctl_t *ata_ctlp,
prde_t *srcp,
int sg_cnt)
{
ddi_acc_handle_t bmhandle = ata_ctlp->ac_bmhandle;
caddr_t bmaddr = ata_ctlp->ac_bmaddr;
ddi_acc_handle_t sg_acc_handle = ata_ctlp->ac_sg_acc_handle;
uint_t *dstp = (uint_t *)ata_ctlp->ac_sg_list;
int idx;
ASSERT(dstp != 0);
ASSERT(sg_cnt != 0);
ADBG_DMA(("ata dma_setup 0x%p 0x%p %d\n", ata_ctlp, srcp, sg_cnt));
for (idx = 0; idx < sg_cnt - 1; idx++, srcp++) {
ddi_put32(sg_acc_handle, dstp++, srcp->p_address);
ddi_put32(sg_acc_handle, dstp++, srcp->p_count);
}
srcp->p_count |= PCIIDE_PRDE_EOT;
ddi_put32(sg_acc_handle, dstp++, srcp->p_address);
ddi_put32(sg_acc_handle, dstp++, srcp->p_count);
ddi_put32(bmhandle, (uint_t *)(bmaddr + PCIIDE_BMIDTPX_REG),
ata_ctlp->ac_sg_paddr);
ADBG_DMA(("ata dma_setup 0x%p 0x%llx\n",
bmaddr, (unsigned long long)ata_ctlp->ac_sg_paddr));
}
void
ata_pciide_dma_start(
ata_ctl_t *ata_ctlp,
uchar_t direction)
{
ddi_acc_handle_t bmhandle = ata_ctlp->ac_bmhandle;
caddr_t bmaddr = ata_ctlp->ac_bmaddr;
uchar_t tmp;
ASSERT((ata_ctlp->ac_sg_paddr & PCIIDE_BMIDTPX_MASK) == 0);
ASSERT((direction == PCIIDE_BMICX_RWCON_WRITE_TO_MEMORY) ||
(direction == PCIIDE_BMICX_RWCON_READ_FROM_MEMORY));
tmp = ddi_get8(bmhandle, (uchar_t *)bmaddr + PCIIDE_BMICX_REG);
tmp &= PCIIDE_BMICX_MASK;
ddi_put8(bmhandle, (uchar_t *)bmaddr + PCIIDE_BMICX_REG,
(tmp | direction));
ddi_put8(bmhandle, (uchar_t *)bmaddr + PCIIDE_BMICX_REG,
(tmp | PCIIDE_BMICX_SSBM_E | direction));
return;
}
void
ata_pciide_dma_stop(
ata_ctl_t *ata_ctlp)
{
ddi_acc_handle_t bmhandle = ata_ctlp->ac_bmhandle;
caddr_t bmaddr = ata_ctlp->ac_bmaddr;
uchar_t tmp;
tmp = ddi_get8(bmhandle, (uchar_t *)bmaddr + PCIIDE_BMICX_REG);
tmp &= (PCIIDE_BMICX_MASK & (~PCIIDE_BMICX_SSBM));
ADBG_DMA(("ata_pciide_dma_stop 0x%p 0x%x\n", bmaddr, tmp));
ddi_put8(bmhandle, (uchar_t *)bmaddr + PCIIDE_BMICX_REG, tmp);
}
void
ata_pciide_dma_sg_func(
gcmd_t *gcmdp,
ddi_dma_cookie_t *dmackp,
int single_segment,
int seg_index)
{
ata_pkt_t *ata_pktp = GCMD2APKT(gcmdp);
prde_t *dmap;
ASSERT(seg_index < ATA_DMA_NSEGS);
ASSERT(((uint_t)dmackp->dmac_address & PCIIDE_PRDE_ADDR_MASK) == 0);
ASSERT((dmackp->dmac_size & PCIIDE_PRDE_CNT_MASK) == 0);
ASSERT(dmackp->dmac_size <= PCIIDE_PRDE_CNT_MAX);
ADBG_TRACE(("adp_dma_sg_func: gcmdp 0x%p dmackp 0x%p s %d idx %d\n",
gcmdp, dmackp, single_segment, seg_index));
dmap = ata_pktp->ap_sg_list + seg_index;
dmap->p_address = (uint_t)dmackp->dmac_address;
dmap->p_count = (uint_t)dmackp->dmac_size;
ata_pktp->ap_sg_cnt = seg_index + 1;
if (seg_index == 0)
ata_pktp->ap_bcount = 0;
ata_pktp->ap_bcount += dmackp->dmac_size;
}
int
ata_pciide_status_clear(
ata_ctl_t *ata_ctlp)
{
ddi_acc_handle_t bmhandle = ata_ctlp->ac_bmhandle;
caddr_t bmaddr = ata_ctlp->ac_bmaddr;
uchar_t status;
uchar_t tmp;
status = PCIIDE_STATUS_GET(ata_ctlp->ac_bmhandle, ata_ctlp->ac_bmaddr);
tmp = status & PCIIDE_BMISX_MASK;
tmp |= (PCIIDE_BMISX_IDERR | PCIIDE_BMISX_IDEINTS);
ADBG_DMA(("ata_pciide_status_clear 0x%p 0x%x\n",
bmaddr, status));
ddi_put8(bmhandle, (uchar_t *)bmaddr + PCIIDE_BMISX_REG, tmp);
#ifdef NAT_SEMI_PC87415_BUG
if (ata_ctlp->ac_nat_semi_bug) {
tmp = ddi_get8(bmhandle, bmaddr + PCIIDE_BMICX_REG);
tmp &= PCIIDE_BMICX_MASK;
ddi_put8(bmhandle, bmaddr + PCIIDE_BMICX_REG,
(tmp | PCIIDE_BMISX_IDERR | PCIIDE_BMISX_IDEINTS));
}
#endif
return (status);
}
int
ata_pciide_status_dmacheck_clear(
ata_ctl_t *ata_ctlp)
{
uchar_t status;
status = ata_pciide_status_clear(ata_ctlp);
ADBG_DMA(("ata_pciide_status_dmacheck_clear 0x%p 0x%x\n",
ata_ctlp->ac_bmaddr, status));
if (status & PCIIDE_BMISX_IDERR) {
ADBG_WARN(("ata_pciide_status: 0x%x\n", status));
return (TRUE);
}
return (FALSE);
}
int
ata_pciide_status_pending(
ata_ctl_t *ata_ctlp)
{
uchar_t status;
status = PCIIDE_STATUS_GET(ata_ctlp->ac_bmhandle, ata_ctlp->ac_bmaddr);
ADBG_DMA(("ata_pciide_status_pending 0x%p 0x%x\n",
ata_ctlp->ac_bmaddr, status));
if (status & PCIIDE_BMISX_IDEINTS)
return (TRUE);
return (FALSE);
}