#include <sys/errno.h>
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/kmem.h>
#include <sys/ddi.h>
#include <sys/stat.h>
#include <sys/sunddi.h>
#include <sys/file.h>
#include <sys/open.h>
#include <sys/modctl.h>
#include <sys/ddi_impldefs.h>
#include <sys/sysmacros.h>
#include <sys/ioat.h>
static int ioat_open(dev_t *devp, int flag, int otyp, cred_t *cred);
static int ioat_close(dev_t devp, int flag, int otyp, cred_t *cred);
static int ioat_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
static int ioat_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
static int ioat_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
void **result);
static int ioat_quiesce(dev_info_t *dip);
static struct cb_ops ioat_cb_ops = {
ioat_open,
ioat_close,
nodev,
nodev,
nodev,
nodev,
nodev,
ioat_ioctl,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
NULL,
D_NEW | D_MP | D_64BIT | D_DEVMAP,
CB_REV
};
static struct dev_ops ioat_dev_ops = {
DEVO_REV,
0,
ioat_getinfo,
nulldev,
nulldev,
ioat_attach,
ioat_detach,
nodev,
&ioat_cb_ops,
NULL,
NULL,
ioat_quiesce,
};
static struct modldrv ioat_modldrv = {
&mod_driverops,
"ioat driver",
&ioat_dev_ops,
};
static struct modlinkage ioat_modlinkage = {
MODREV_1,
(void *) &ioat_modldrv,
NULL
};
void *ioat_statep;
static int ioat_chip_init(ioat_state_t *state);
static void ioat_chip_fini(ioat_state_t *state);
static int ioat_drv_init(ioat_state_t *state);
static void ioat_drv_fini(ioat_state_t *state);
static uint_t ioat_isr(caddr_t parm);
static void ioat_intr_enable(ioat_state_t *state);
static void ioat_intr_disable(ioat_state_t *state);
void ioat_detach_finish(ioat_state_t *state);
ddi_device_acc_attr_t ioat_acc_attr = {
DDI_DEVICE_ATTR_V0,
DDI_NEVERSWAP_ACC,
DDI_STORECACHING_OK_ACC,
DDI_DEFAULT_ACC
};
dcopy_device_cb_t ioat_cb = {
DCOPY_DEVICECB_V0,
0,
ioat_channel_alloc,
ioat_channel_free,
ioat_cmd_alloc,
ioat_cmd_free,
ioat_cmd_post,
ioat_cmd_poll,
ioat_unregister_complete
};
int
_init(void)
{
int e;
e = ddi_soft_state_init(&ioat_statep, sizeof (ioat_state_t), 1);
if (e != 0) {
return (e);
}
e = mod_install(&ioat_modlinkage);
if (e != 0) {
ddi_soft_state_fini(&ioat_statep);
return (e);
}
return (0);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&ioat_modlinkage, modinfop));
}
int
_fini(void)
{
int e;
e = mod_remove(&ioat_modlinkage);
if (e != 0) {
return (e);
}
ddi_soft_state_fini(&ioat_statep);
return (0);
}
static int
ioat_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
ioat_state_t *state;
int instance;
int e;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
instance = ddi_get_instance(dip);
state = ddi_get_soft_state(ioat_statep, instance);
if (state == NULL) {
return (DDI_FAILURE);
}
e = ioat_channel_resume(state);
if (e != DDI_SUCCESS) {
return (DDI_FAILURE);
}
ioat_intr_enable(state);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
instance = ddi_get_instance(dip);
e = ddi_soft_state_zalloc(ioat_statep, instance);
if (e != DDI_SUCCESS) {
return (DDI_FAILURE);
}
state = ddi_get_soft_state(ioat_statep, instance);
if (state == NULL) {
goto attachfail_get_soft_state;
}
state->is_dip = dip;
state->is_instance = instance;
e = ioat_chip_init(state);
if (e != DDI_SUCCESS) {
goto attachfail_chip_init;
}
e = ioat_drv_init(state);
if (e != DDI_SUCCESS) {
goto attachfail_drv_init;
}
e = ddi_create_minor_node(dip, "ioat", S_IFCHR, instance, DDI_PSEUDO,
0);
if (e != DDI_SUCCESS) {
goto attachfail_minor_node;
}
ioat_intr_enable(state);
ddi_report_dev(dip);
e = dcopy_device_register(state, &state->is_deviceinfo,
&state->is_device_handle);
if (e != DCOPY_SUCCESS) {
goto attachfail_register;
}
return (DDI_SUCCESS);
attachfail_register:
ioat_intr_disable(state);
ddi_remove_minor_node(dip, NULL);
attachfail_minor_node:
ioat_drv_fini(state);
attachfail_drv_init:
ioat_chip_fini(state);
attachfail_chip_init:
attachfail_get_soft_state:
(void) ddi_soft_state_free(ioat_statep, instance);
return (DDI_FAILURE);
}
static int
ioat_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
ioat_state_t *state;
int instance;
int e;
instance = ddi_get_instance(dip);
state = ddi_get_soft_state(ioat_statep, instance);
if (state == NULL) {
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
ioat_channel_suspend(state);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
e = dcopy_device_unregister(&state->is_device_handle);
if (e != DCOPY_SUCCESS) {
if (e == DCOPY_PENDING) {
cmn_err(CE_NOTE, "device busy, performing asynchronous"
" detach\n");
}
return (DDI_FAILURE);
}
ioat_detach_finish(state);
return (DDI_SUCCESS);
}
static int
ioat_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
{
ioat_state_t *state;
int instance;
dev_t dev;
int e;
dev = (dev_t)arg;
instance = getminor(dev);
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
state = ddi_get_soft_state(ioat_statep, instance);
if (state == NULL) {
return (DDI_FAILURE);
}
*result = (void *)state->is_dip;
e = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)(uintptr_t)instance;
e = DDI_SUCCESS;
break;
default:
e = DDI_FAILURE;
break;
}
return (e);
}
static int
ioat_open(dev_t *devp, int flag, int otyp, cred_t *cred)
{
ioat_state_t *state;
int instance;
instance = getminor(*devp);
state = ddi_get_soft_state(ioat_statep, instance);
if (state == NULL) {
return (ENXIO);
}
return (0);
}
static int
ioat_close(dev_t devp, int flag, int otyp, cred_t *cred)
{
return (0);
}
static int
ioat_chip_init(ioat_state_t *state)
{
ddi_device_acc_attr_t attr;
int e;
attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
e = ddi_regs_map_setup(state->is_dip, 1, (caddr_t *)&state->is_genregs,
0, 0, &attr, &state->is_reg_handle);
if (e != DDI_SUCCESS) {
goto chipinitfail_regsmap;
}
state->is_num_channels = (uint_t)ddi_get8(state->is_reg_handle,
&state->is_genregs[IOAT_CHANCNT]);
if (state->is_num_channels == 0) {
goto chipinitfail_numchan;
}
state->is_maxxfer = (uint_t)ddi_get8(state->is_reg_handle,
&state->is_genregs[IOAT_XFERCAP]);
state->is_chanoff = (uintptr_t)ddi_get16(state->is_reg_handle,
(uint16_t *)&state->is_genregs[IOAT_PERPORT_OFF]);
state->is_cbver = (uint_t)ddi_get8(state->is_reg_handle,
&state->is_genregs[IOAT_CBVER]);
state->is_intrdelay = (uint_t)ddi_get16(state->is_reg_handle,
(uint16_t *)&state->is_genregs[IOAT_INTRDELAY]);
state->is_status = (uint_t)ddi_get16(state->is_reg_handle,
(uint16_t *)&state->is_genregs[IOAT_CSSTATUS]);
state->is_capabilities = (uint_t)ddi_get32(state->is_reg_handle,
(uint32_t *)&state->is_genregs[IOAT_DMACAPABILITY]);
if (state->is_cbver & 0x10) {
state->is_ver = IOAT_CBv1;
} else if (state->is_cbver & 0x20) {
state->is_ver = IOAT_CBv2;
} else {
goto chipinitfail_version;
}
return (DDI_SUCCESS);
chipinitfail_version:
chipinitfail_numchan:
ddi_regs_map_free(&state->is_reg_handle);
chipinitfail_regsmap:
return (DDI_FAILURE);
}
static void
ioat_chip_fini(ioat_state_t *state)
{
ddi_regs_map_free(&state->is_reg_handle);
}
static int
ioat_drv_init(ioat_state_t *state)
{
ddi_acc_handle_t handle;
int e;
mutex_init(&state->is_mutex, NULL, MUTEX_DRIVER, NULL);
state->is_deviceinfo.di_dip = state->is_dip;
state->is_deviceinfo.di_num_dma = state->is_num_channels;
state->is_deviceinfo.di_maxxfer = state->is_maxxfer;
state->is_deviceinfo.di_capabilities = state->is_capabilities;
state->is_deviceinfo.di_cb = &ioat_cb;
e = pci_config_setup(state->is_dip, &handle);
if (e != DDI_SUCCESS) {
goto drvinitfail_config_setup;
}
state->is_deviceinfo.di_id = (uint64_t)pci_config_get16(handle, 0);
state->is_deviceinfo.di_id = state->is_deviceinfo.di_id << 16;
state->is_deviceinfo.di_id |= (uint64_t)pci_config_get16(handle, 2);
state->is_deviceinfo.di_id = state->is_deviceinfo.di_id << 32;
state->is_deviceinfo.di_id |= (uint64_t)state->is_cbver;
pci_config_teardown(&handle);
e = ddi_intr_hilevel(state->is_dip, 0);
if (e != 0) {
cmn_err(CE_WARN, "hilevel interrupt not supported\n");
goto drvinitfail_hilevel;
}
e = ddi_add_intr(state->is_dip, 0, NULL, NULL, ioat_isr,
(caddr_t)state);
if (e != DDI_SUCCESS) {
goto drvinitfail_add_intr;
}
e = ddi_get_iblock_cookie(state->is_dip, 0, &state->is_iblock_cookie);
if (e != DDI_SUCCESS) {
goto drvinitfail_iblock_cookie;
}
e = ioat_channel_init(state);
if (e != DDI_SUCCESS) {
goto drvinitfail_channel_init;
}
return (DDI_SUCCESS);
drvinitfail_channel_init:
drvinitfail_iblock_cookie:
ddi_remove_intr(state->is_dip, 0, state->is_iblock_cookie);
drvinitfail_add_intr:
drvinitfail_hilevel:
drvinitfail_config_setup:
mutex_destroy(&state->is_mutex);
return (DDI_FAILURE);
}
static void
ioat_drv_fini(ioat_state_t *state)
{
ioat_channel_fini(state);
ddi_remove_intr(state->is_dip, 0, state->is_iblock_cookie);
mutex_destroy(&state->is_mutex);
}
void
ioat_unregister_complete(void *device_private, int status)
{
ioat_state_t *state;
state = device_private;
if (status != DCOPY_SUCCESS) {
cmn_err(CE_WARN, "asynchronous detach aborted\n");
return;
}
cmn_err(CE_CONT, "detach completing\n");
ioat_detach_finish(state);
}
void
ioat_detach_finish(ioat_state_t *state)
{
ioat_intr_disable(state);
ddi_remove_minor_node(state->is_dip, NULL);
ioat_drv_fini(state);
ioat_chip_fini(state);
(void) ddi_soft_state_free(ioat_statep, state->is_instance);
}
static void
ioat_intr_enable(ioat_state_t *state)
{
uint32_t intr_status;
intr_status = ddi_get32(state->is_reg_handle,
(uint32_t *)&state->is_genregs[IOAT_ATTNSTATUS]);
if (intr_status != 0) {
ddi_put32(state->is_reg_handle,
(uint32_t *)&state->is_genregs[IOAT_ATTNSTATUS],
intr_status);
}
ddi_put8(state->is_reg_handle, &state->is_genregs[IOAT_INTRCTL],
IOAT_INTRCTL_MASTER_EN);
}
static void
ioat_intr_disable(ioat_state_t *state)
{
(void) ddi_get8(state->is_reg_handle,
&state->is_genregs[IOAT_INTRCTL]);
}
static uint_t
ioat_isr(caddr_t parm)
{
uint32_t intr_status;
ioat_state_t *state;
uint8_t intrctrl;
uint32_t chan;
uint_t r;
int i;
state = (ioat_state_t *)parm;
intrctrl = ddi_get8(state->is_reg_handle,
&state->is_genregs[IOAT_INTRCTL]);
ASSERT(intrctrl & IOAT_INTRCTL_MASTER_EN);
if (!(intrctrl & IOAT_INTRCTL_INTR_STAT)) {
ddi_put8(state->is_reg_handle,
&state->is_genregs[IOAT_INTRCTL], intrctrl);
return (DDI_INTR_UNCLAIMED);
}
intr_status = ddi_get32(state->is_reg_handle,
(uint32_t *)&state->is_genregs[IOAT_ATTNSTATUS]);
r = DDI_INTR_UNCLAIMED;
chan = 1;
for (i = 0; i < state->is_num_channels; i++) {
if (intr_status & chan) {
ioat_channel_intr(&state->is_channel[i]);
r = DDI_INTR_CLAIMED;
}
chan = chan << 1;
}
ASSERT(r == DDI_INTR_CLAIMED);
ddi_put8(state->is_reg_handle, &state->is_genregs[IOAT_INTRCTL],
intrctrl);
return (r);
}
static int
ioat_quiesce(dev_info_t *dip)
{
ioat_state_t *state;
int instance;
instance = ddi_get_instance(dip);
state = ddi_get_soft_state(ioat_statep, instance);
if (state == NULL) {
return (DDI_FAILURE);
}
ioat_intr_disable(state);
ioat_channel_quiesce(state);
return (DDI_SUCCESS);
}