#include <sys/note.h>
#include <sys/scsi/scsi.h>
#include <sys/pci.h>
#include <sys/sata/sata_hba.h>
#include <sys/sata/adapters/si3124/si3124reg.h>
#include <sys/sata/adapters/si3124/si3124var.h>
#include <sys/sdt.h>
#include <sys/ddifm.h>
#include <sys/fm/protocol.h>
#include <sys/fm/util.h>
#include <sys/fm/io/ddi.h>
static int si_attach(dev_info_t *, ddi_attach_cmd_t);
static int si_detach(dev_info_t *, ddi_detach_cmd_t);
static int si_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int si_power(dev_info_t *, int, int);
static int si_quiesce(dev_info_t *);
static int si_register_sata_hba_tran(si_ctl_state_t *);
static int si_unregister_sata_hba_tran(si_ctl_state_t *);
static int si_tran_probe_port(dev_info_t *, sata_device_t *);
static int si_tran_start(dev_info_t *, sata_pkt_t *spkt);
static int si_tran_abort(dev_info_t *, sata_pkt_t *, int);
static int si_tran_reset_dport(dev_info_t *, sata_device_t *);
static int si_tran_hotplug_port_activate(dev_info_t *, sata_device_t *);
static int si_tran_hotplug_port_deactivate(dev_info_t *, sata_device_t *);
static int si_alloc_port_state(si_ctl_state_t *, int);
static void si_dealloc_port_state(si_ctl_state_t *, int);
static int si_alloc_sgbpool(si_ctl_state_t *, int);
static void si_dealloc_sgbpool(si_ctl_state_t *, int);
static int si_alloc_prbpool(si_ctl_state_t *, int);
static void si_dealloc_prbpool(si_ctl_state_t *, int);
static void si_find_dev_signature(si_ctl_state_t *, si_port_state_t *,
int, int);
static void si_poll_cmd(si_ctl_state_t *, si_port_state_t *, int, int,
sata_pkt_t *);
static int si_claim_free_slot(si_ctl_state_t *, si_port_state_t *, int);
static int si_deliver_satapkt(si_ctl_state_t *, si_port_state_t *, int,
sata_pkt_t *);
static int si_initialize_controller(si_ctl_state_t *);
static void si_deinitialize_controller(si_ctl_state_t *);
static void si_init_port(si_ctl_state_t *, int);
static int si_enumerate_port_multiplier(si_ctl_state_t *,
si_port_state_t *, int);
static int si_read_portmult_reg(si_ctl_state_t *, si_port_state_t *,
int, int, int, uint32_t *);
static int si_write_portmult_reg(si_ctl_state_t *, si_port_state_t *,
int, int, int, uint32_t);
static void si_set_sense_data(sata_pkt_t *, int);
static uint_t si_intr(caddr_t, caddr_t);
static int si_intr_command_complete(si_ctl_state_t *,
si_port_state_t *, int);
static void si_schedule_intr_command_error(si_ctl_state_t *,
si_port_state_t *, int);
static void si_do_intr_command_error(void *);
static int si_intr_command_error(si_ctl_state_t *,
si_port_state_t *, int);
static void si_error_recovery_DEVICEERROR(si_ctl_state_t *,
si_port_state_t *, int);
static void si_error_recovery_SDBERROR(si_ctl_state_t *,
si_port_state_t *, int);
static void si_error_recovery_DATAFISERROR(si_ctl_state_t *,
si_port_state_t *, int);
static void si_error_recovery_SENDFISERROR(si_ctl_state_t *,
si_port_state_t *, int);
static void si_error_recovery_default(si_ctl_state_t *,
si_port_state_t *, int);
static uint8_t si_read_log_ext(si_ctl_state_t *,
si_port_state_t *si_portp, int);
static void si_log_error_message(si_ctl_state_t *, int, uint32_t);
static int si_intr_port_ready(si_ctl_state_t *, si_port_state_t *, int);
static int si_intr_pwr_change(si_ctl_state_t *, si_port_state_t *, int);
static int si_intr_phy_ready_change(si_ctl_state_t *, si_port_state_t *, int);
static int si_intr_comwake_rcvd(si_ctl_state_t *, si_port_state_t *, int);
static int si_intr_unrecognised_fis(si_ctl_state_t *, si_port_state_t *, int);
static int si_intr_dev_xchanged(si_ctl_state_t *, si_port_state_t *, int);
static int si_intr_decode_err_threshold(si_ctl_state_t *,
si_port_state_t *, int);
static int si_intr_crc_err_threshold(si_ctl_state_t *, si_port_state_t *, int);
static int si_intr_handshake_err_threshold(si_ctl_state_t *,
si_port_state_t *, int);
static int si_intr_set_devbits_notify(si_ctl_state_t *, si_port_state_t *, int);
static void si_enable_port_interrupts(si_ctl_state_t *, int);
static void si_enable_all_interrupts(si_ctl_state_t *);
static void si_disable_port_interrupts(si_ctl_state_t *, int);
static void si_disable_all_interrupts(si_ctl_state_t *);
static void fill_dev_sregisters(si_ctl_state_t *, int, sata_device_t *);
static int si_add_legacy_intrs(si_ctl_state_t *);
static int si_add_msi_intrs(si_ctl_state_t *);
static void si_rem_intrs(si_ctl_state_t *);
static int si_reset_dport_wait_till_ready(si_ctl_state_t *,
si_port_state_t *, int, int);
static int si_clear_port(si_ctl_state_t *, int);
static void si_schedule_port_initialize(si_ctl_state_t *,
si_port_state_t *, int);
static void si_do_initialize_port(void *);
static int si_initialize_port_wait_till_ready(si_ctl_state_t *, int);
static void si_timeout_pkts(si_ctl_state_t *, si_port_state_t *, int, uint32_t);
static void si_watchdog_handler(si_ctl_state_t *);
static void si_fm_init(si_ctl_state_t *);
static void si_fm_fini(si_ctl_state_t *);
static int si_fm_error_cb(dev_info_t *, ddi_fm_error_t *, const void *);
static int si_check_acc_handle(ddi_acc_handle_t);
static int si_check_dma_handle(ddi_dma_handle_t);
static int si_check_ctl_handles(si_ctl_state_t *);
static int si_check_port_handles(si_port_state_t *);
static void si_fm_ereport(si_ctl_state_t *, char *, char *);
static void si_log(si_ctl_state_t *, si_port_state_t *, char *, ...);
static void si_copy_out_regs(sata_cmd_t *, si_ctl_state_t *, uint8_t, uint8_t);
static ddi_dma_attr_t buffer_dma_attr = {
DMA_ATTR_V0,
0,
0xffffffffffffffffull,
0xffffffffull,
1,
1,
1,
0xffffffffull,
0xffffffffull,
SI_DEFAULT_SGL_LENGTH,
512,
0,
};
static ddi_dma_attr_t prb_sgt_dma_attr = {
DMA_ATTR_V0,
0,
0xffffffffffffffffull,
0xffffffffull,
8,
1,
1,
0xffffffffull,
0xffffffffull,
1,
1,
0,
};
static ddi_device_acc_attr_t accattr = {
DDI_DEVICE_ATTR_V1,
DDI_STRUCTURE_LE_ACC,
DDI_STRICTORDER_ACC,
DDI_DEFAULT_ACC
};
static struct dev_ops sictl_dev_ops = {
DEVO_REV,
0,
si_getinfo,
nulldev,
nulldev,
si_attach,
si_detach,
nodev,
(struct cb_ops *)0,
NULL,
si_power,
si_quiesce,
};
static sata_tran_hotplug_ops_t si_tran_hotplug_ops = {
SATA_TRAN_HOTPLUG_OPS_REV_1,
si_tran_hotplug_port_activate,
si_tran_hotplug_port_deactivate
};
static int si_watchdog_timeout = 5;
static int si_watchdog_tick;
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
&mod_driverops,
"si3124 driver",
&sictl_dev_ops,
};
static struct modlinkage modlinkage = {
MODREV_1,
&modldrv,
NULL
};
static kmutex_t si_log_mutex;
static char si_log_buf[SI_LOGBUF_LEN];
uint32_t si_debug_flags =
SIDBG_ERRS|SIDBG_INIT|SIDBG_EVENT|SIDBG_TIMEOUT|SIDBG_RESET;
static int is_msi_supported = 0;
int si_dma_sg_number = SI_DEFAULT_SGT_TABLES_PER_PRB;
static void *si_statep = NULL;
int
_init(void)
{
int error;
error = ddi_soft_state_init(&si_statep, sizeof (si_ctl_state_t), 0);
if (error != 0) {
return (error);
}
mutex_init(&si_log_mutex, NULL, MUTEX_DRIVER, NULL);
if ((error = sata_hba_init(&modlinkage)) != 0) {
mutex_destroy(&si_log_mutex);
ddi_soft_state_fini(&si_statep);
return (error);
}
error = mod_install(&modlinkage);
if (error != 0) {
sata_hba_fini(&modlinkage);
mutex_destroy(&si_log_mutex);
ddi_soft_state_fini(&si_statep);
return (error);
}
si_watchdog_tick = drv_usectohz((clock_t)si_watchdog_timeout * 1000000);
return (error);
}
int
_fini(void)
{
int error;
error = mod_remove(&modlinkage);
if (error != 0) {
return (error);
}
sata_hba_fini(&modlinkage);
mutex_destroy(&si_log_mutex);
ddi_soft_state_fini(&si_statep);
return (error);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
si_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
si_ctl_state_t *si_ctlp;
int instance;
int status;
int attach_state;
int intr_types;
sata_device_t sdevice;
SIDBG(SIDBG_ENTRY, "si_attach enter", NULL);
instance = ddi_get_instance(dip);
attach_state = ATTACH_PROGRESS_NONE;
switch (cmd) {
case DDI_ATTACH:
status = ddi_soft_state_zalloc(si_statep, instance);
if (status != DDI_SUCCESS) {
goto err_out;
}
si_ctlp = ddi_get_soft_state(si_statep, instance);
si_ctlp->sictl_devinfop = dip;
attach_state |= ATTACH_PROGRESS_STATEP_ALLOC;
si_ctlp->fm_capabilities = ddi_getprop(DDI_DEV_T_ANY, dip,
DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS, "fm-capable",
DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
DDI_FM_DMACHK_CAPABLE | DDI_FM_ERRCB_CAPABLE);
si_fm_init(si_ctlp);
attach_state |= ATTACH_PROGRESS_INIT_FMA;
status = pci_config_setup(dip, &si_ctlp->sictl_pci_conf_handle);
if (status != DDI_SUCCESS) {
goto err_out;
}
si_ctlp->sictl_devid =
pci_config_get16(si_ctlp->sictl_pci_conf_handle,
PCI_CONF_DEVID);
switch (si_ctlp->sictl_devid) {
case SI3124_DEV_ID:
si_ctlp->sictl_num_ports = SI3124_MAX_PORTS;
break;
case SI3132_DEV_ID:
si_ctlp->sictl_num_ports = SI3132_MAX_PORTS;
break;
case SI3531_DEV_ID:
si_ctlp->sictl_num_ports = SI3531_MAX_PORTS;
break;
default:
goto err_out;
}
attach_state |= ATTACH_PROGRESS_CONF_HANDLE;
status = ddi_regs_map_setup(dip,
PCI_BAR0,
(caddr_t *)&si_ctlp->sictl_global_addr,
0,
0,
&accattr,
&si_ctlp->sictl_global_acc_handle);
if (status != DDI_SUCCESS) {
goto err_out;
}
attach_state |= ATTACH_PROGRESS_BAR0_MAP;
status = ddi_regs_map_setup(dip,
PCI_BAR1,
(caddr_t *)&si_ctlp->sictl_port_addr,
0,
0,
&accattr,
&si_ctlp->sictl_port_acc_handle);
if (status != DDI_SUCCESS) {
goto err_out;
}
attach_state |= ATTACH_PROGRESS_BAR1_MAP;
si_disable_all_interrupts(si_ctlp);
if (ddi_intr_get_supported_types(dip, &intr_types)
!= DDI_SUCCESS) {
SIDBG_C(SIDBG_INIT, si_ctlp,
"ddi_intr_get_supported_types failed", NULL);
goto err_out;
}
SIDBG_C(SIDBG_INIT, si_ctlp,
"ddi_intr_get_supported_types() returned: 0x%x",
intr_types);
if (is_msi_supported && (intr_types & DDI_INTR_TYPE_MSI)) {
SIDBG_C(SIDBG_INIT, si_ctlp,
"Using MSI interrupt type", NULL);
if (si_add_msi_intrs(si_ctlp) == DDI_SUCCESS) {
si_ctlp->sictl_intr_type = DDI_INTR_TYPE_MSI;
attach_state |= ATTACH_PROGRESS_INTR_ADDED;
SIDBG_C(SIDBG_INIT, si_ctlp,
"MSI interrupt setup done", NULL);
} else {
SIDBG_C(SIDBG_INIT, si_ctlp,
"MSI registration failed "
"will try Legacy interrupts", NULL);
}
}
if (!(attach_state & ATTACH_PROGRESS_INTR_ADDED) &&
(intr_types & DDI_INTR_TYPE_FIXED)) {
SIDBG_C(SIDBG_INIT, si_ctlp,
"Using Legacy interrupt type", NULL);
if (si_add_legacy_intrs(si_ctlp) == DDI_SUCCESS) {
si_ctlp->sictl_intr_type = DDI_INTR_TYPE_FIXED;
attach_state |= ATTACH_PROGRESS_INTR_ADDED;
SIDBG_C(SIDBG_INIT, si_ctlp,
"Legacy interrupt setup done", NULL);
} else {
SIDBG_C(SIDBG_INIT, si_ctlp,
"legacy interrupt setup failed", NULL);
goto err_out;
}
}
if (!(attach_state & ATTACH_PROGRESS_INTR_ADDED)) {
SIDBG_C(SIDBG_INIT, si_ctlp,
"si3124: No interrupts registered", NULL);
goto err_out;
}
mutex_init(&si_ctlp->sictl_mutex, NULL, MUTEX_DRIVER,
(void *)(uintptr_t)si_ctlp->sictl_intr_pri);
attach_state |= ATTACH_PROGRESS_MUTEX_INIT;
si_ctlp->sictl_flags |= SI_ATTACH;
status = si_initialize_controller(si_ctlp);
si_ctlp->sictl_flags &= ~SI_ATTACH;
if (status) {
goto err_out;
}
attach_state |= ATTACH_PROGRESS_HW_INIT;
if (si_register_sata_hba_tran(si_ctlp)) {
SIDBG_C(SIDBG_INIT, si_ctlp,
"si3124: setting sata hba tran failed", NULL);
goto err_out;
}
si_ctlp->sictl_timeout_id = timeout(
(void (*)(void *))si_watchdog_handler,
(caddr_t)si_ctlp, si_watchdog_tick);
si_ctlp->sictl_power_level = PM_LEVEL_D0;
return (DDI_SUCCESS);
case DDI_RESUME:
si_ctlp = ddi_get_soft_state(si_statep, instance);
status = si_initialize_controller(si_ctlp);
if (status) {
return (DDI_FAILURE);
}
si_ctlp->sictl_timeout_id = timeout(
(void (*)(void *))si_watchdog_handler,
(caddr_t)si_ctlp, si_watchdog_tick);
(void) pm_power_has_changed(dip, 0, PM_LEVEL_D0);
if (sata_hba_attach(si_ctlp->sictl_devinfop,
si_ctlp->sictl_sata_hba_tran,
DDI_RESUME) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
bzero((void *)&sdevice, sizeof (sata_device_t));
sata_hba_event_notify(dip, &sdevice,
SATA_EVNT_PWR_LEVEL_CHANGED);
SIDBG_C(SIDBG_INIT|SIDBG_EVENT, si_ctlp,
"sending event up: SATA_EVNT_PWR_LEVEL_CHANGED", NULL);
(void) pm_idle_component(si_ctlp->sictl_devinfop, 0);
si_ctlp->sictl_power_level = PM_LEVEL_D0;
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
err_out:
if (attach_state & ATTACH_PROGRESS_HW_INIT) {
si_ctlp->sictl_flags |= SI_DETACH;
si_deinitialize_controller(si_ctlp);
si_ctlp->sictl_flags &= ~SI_DETACH;
}
if (attach_state & ATTACH_PROGRESS_MUTEX_INIT) {
mutex_destroy(&si_ctlp->sictl_mutex);
}
if (attach_state & ATTACH_PROGRESS_INTR_ADDED) {
si_rem_intrs(si_ctlp);
}
if (attach_state & ATTACH_PROGRESS_BAR1_MAP) {
ddi_regs_map_free(&si_ctlp->sictl_port_acc_handle);
}
if (attach_state & ATTACH_PROGRESS_BAR0_MAP) {
ddi_regs_map_free(&si_ctlp->sictl_global_acc_handle);
}
if (attach_state & ATTACH_PROGRESS_CONF_HANDLE) {
pci_config_teardown(&si_ctlp->sictl_pci_conf_handle);
}
if (attach_state & ATTACH_PROGRESS_INIT_FMA) {
si_fm_fini(si_ctlp);
}
if (attach_state & ATTACH_PROGRESS_STATEP_ALLOC) {
ddi_soft_state_free(si_statep, instance);
}
return (DDI_FAILURE);
}
static int
si_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
si_ctl_state_t *si_ctlp;
int instance;
SIDBG(SIDBG_ENTRY, "si_detach enter", NULL);
instance = ddi_get_instance(dip);
si_ctlp = ddi_get_soft_state(si_statep, instance);
switch (cmd) {
case DDI_DETACH:
mutex_enter(&si_ctlp->sictl_mutex);
si_disable_all_interrupts(si_ctlp);
mutex_exit(&si_ctlp->sictl_mutex);
if (si_unregister_sata_hba_tran(si_ctlp) != SI_SUCCESS) {
si_enable_all_interrupts(si_ctlp);
return (DDI_FAILURE);
}
mutex_enter(&si_ctlp->sictl_mutex);
si_ctlp->sictl_flags |= SI_NO_TIMEOUTS;
(void) untimeout(si_ctlp->sictl_timeout_id);
si_ctlp->sictl_flags &= ~SI_NO_TIMEOUTS;
si_ctlp->sictl_flags |= SI_DETACH;
si_deinitialize_controller(si_ctlp);
si_ctlp->sictl_flags &= ~SI_DETACH;
mutex_exit(&si_ctlp->sictl_mutex);
mutex_destroy(&si_ctlp->sictl_mutex);
si_rem_intrs(si_ctlp);
ddi_regs_map_free(&si_ctlp->sictl_port_acc_handle);
ddi_regs_map_free(&si_ctlp->sictl_global_acc_handle);
pci_config_teardown(&si_ctlp->sictl_pci_conf_handle);
si_fm_fini(si_ctlp);
ddi_soft_state_free(si_statep, instance);
return (DDI_SUCCESS);
case DDI_SUSPEND:
if (sata_hba_detach(dip, cmd) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
mutex_enter(&si_ctlp->sictl_mutex);
if (pm_busy_component(si_ctlp->sictl_devinfop, 0) ==
DDI_SUCCESS) {
mutex_exit(&si_ctlp->sictl_mutex);
(void) pm_raise_power(si_ctlp->sictl_devinfop, 0,
PM_LEVEL_D0);
mutex_enter(&si_ctlp->sictl_mutex);
}
si_deinitialize_controller(si_ctlp);
si_ctlp->sictl_flags |= SI_NO_TIMEOUTS;
(void) untimeout(si_ctlp->sictl_timeout_id);
si_ctlp->sictl_flags &= ~SI_NO_TIMEOUTS;
SIDBG_C(SIDBG_POWER, si_ctlp, "si3124%d: DDI_SUSPEND",
instance);
mutex_exit(&si_ctlp->sictl_mutex);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
si_power(dev_info_t *dip, int component, int level)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(component))
#endif
si_ctl_state_t *si_ctlp;
int instance = ddi_get_instance(dip);
int rval = DDI_SUCCESS;
int old_level;
sata_device_t sdevice;
si_ctlp = ddi_get_soft_state(si_statep, instance);
if (si_ctlp == NULL) {
return (DDI_FAILURE);
}
SIDBG_C(SIDBG_ENTRY, si_ctlp, "si_power enter", NULL);
mutex_enter(&si_ctlp->sictl_mutex);
old_level = si_ctlp->sictl_power_level;
switch (level) {
case PM_LEVEL_D0:
pci_config_put16(si_ctlp->sictl_pci_conf_handle,
PM_CSR(si_ctlp->sictl_devid), PCI_PMCSR_D0);
#ifndef __lock_lint
delay(drv_usectohz(10000));
#endif
si_ctlp->sictl_power_level = PM_LEVEL_D0;
(void) pci_restore_config_regs(si_ctlp->sictl_devinfop);
SIDBG_C(SIDBG_POWER, si_ctlp,
"si3124%d: turning power ON. old level %d",
instance, old_level);
if (si_ctlp->sictl_flags & SI_ATTACH)
break;
mutex_exit(&si_ctlp->sictl_mutex);
(void) si_initialize_controller(si_ctlp);
mutex_enter(&si_ctlp->sictl_mutex);
si_ctlp->sictl_timeout_id = timeout(
(void (*)(void *))si_watchdog_handler,
(caddr_t)si_ctlp, si_watchdog_tick);
bzero((void *)&sdevice, sizeof (sata_device_t));
sata_hba_event_notify(
si_ctlp->sictl_sata_hba_tran->sata_tran_hba_dip,
&sdevice, SATA_EVNT_PWR_LEVEL_CHANGED);
SIDBG_C(SIDBG_EVENT|SIDBG_POWER, si_ctlp,
"sending event up: PWR_LEVEL_CHANGED", NULL);
break;
case PM_LEVEL_D3:
if (!(si_ctlp->sictl_flags & SI_DETACH)) {
si_ctlp->sictl_flags |= SI_NO_TIMEOUTS;
(void) untimeout(si_ctlp->sictl_timeout_id);
si_ctlp->sictl_flags &= ~SI_NO_TIMEOUTS;
si_deinitialize_controller(si_ctlp);
si_ctlp->sictl_power_level = PM_LEVEL_D3;
}
(void) pci_save_config_regs(si_ctlp->sictl_devinfop);
pci_config_put16(si_ctlp->sictl_pci_conf_handle,
PM_CSR(si_ctlp->sictl_devid), PCI_PMCSR_D3HOT);
SIDBG_C(SIDBG_POWER, si_ctlp, "si3124%d: turning power OFF. "
"old level %d", instance, old_level);
break;
default:
SIDBG_C(SIDBG_POWER, si_ctlp, "si3124%d: turning power OFF. "
"old level %d", instance, old_level);
rval = DDI_FAILURE;
break;
}
mutex_exit(&si_ctlp->sictl_mutex);
return (rval);
}
static int
si_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(dip))
#endif
si_ctl_state_t *si_ctlp;
int instance;
dev_t dev;
dev = (dev_t)arg;
instance = getminor(dev);
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
si_ctlp = ddi_get_soft_state(si_statep, instance);
if (si_ctlp != NULL) {
*result = si_ctlp->sictl_devinfop;
return (DDI_SUCCESS);
} else {
*result = NULL;
return (DDI_FAILURE);
}
case DDI_INFO_DEVT2INSTANCE:
*(int *)result = instance;
break;
default:
break;
}
return (DDI_SUCCESS);
}
static int
si_register_sata_hba_tran(si_ctl_state_t *si_ctlp)
{
struct sata_hba_tran *sata_hba_tran;
SIDBG_C(SIDBG_ENTRY, si_ctlp,
"si_register_sata_hba_tran entry", NULL);
mutex_enter(&si_ctlp->sictl_mutex);
sata_hba_tran = kmem_zalloc(sizeof (sata_hba_tran_t), KM_SLEEP);
sata_hba_tran->sata_tran_hba_rev = SATA_TRAN_HBA_REV;
sata_hba_tran->sata_tran_hba_dip = si_ctlp->sictl_devinfop;
if (si_dma_sg_number > SI_MAX_SGT_TABLES_PER_PRB) {
si_dma_sg_number = SI_MAX_SGT_TABLES_PER_PRB;
} else if (si_dma_sg_number < SI_MIN_SGT_TABLES_PER_PRB) {
si_dma_sg_number = SI_MIN_SGT_TABLES_PER_PRB;
}
if (si_dma_sg_number != SI_DEFAULT_SGT_TABLES_PER_PRB) {
buffer_dma_attr.dma_attr_sgllen = SGE_LENGTH(si_dma_sg_number);
}
sata_hba_tran->sata_tran_hba_dma_attr = &buffer_dma_attr;
sata_hba_tran->sata_tran_hba_num_cports = si_ctlp->sictl_num_ports;
sata_hba_tran->sata_tran_hba_features_support = 0;
sata_hba_tran->sata_tran_hba_qdepth = SI_NUM_SLOTS;
sata_hba_tran->sata_tran_probe_port = si_tran_probe_port;
sata_hba_tran->sata_tran_start = si_tran_start;
sata_hba_tran->sata_tran_abort = si_tran_abort;
sata_hba_tran->sata_tran_reset_dport = si_tran_reset_dport;
sata_hba_tran->sata_tran_selftest = NULL;
sata_hba_tran->sata_tran_hotplug_ops = &si_tran_hotplug_ops;
sata_hba_tran->sata_tran_pwrmgt_ops = NULL;
sata_hba_tran->sata_tran_ioctl = NULL;
mutex_exit(&si_ctlp->sictl_mutex);
if (sata_hba_attach(si_ctlp->sictl_devinfop, sata_hba_tran, DDI_ATTACH)
!= DDI_SUCCESS) {
kmem_free((void *)sata_hba_tran, sizeof (sata_hba_tran_t));
return (SI_FAILURE);
}
mutex_enter(&si_ctlp->sictl_mutex);
si_ctlp->sictl_sata_hba_tran = sata_hba_tran;
mutex_exit(&si_ctlp->sictl_mutex);
return (SI_SUCCESS);
}
static int
si_unregister_sata_hba_tran(si_ctl_state_t *si_ctlp)
{
if (sata_hba_detach(si_ctlp->sictl_devinfop, DDI_DETACH) !=
DDI_SUCCESS) {
return (SI_FAILURE);
}
kmem_free((void *)si_ctlp->sictl_sata_hba_tran,
sizeof (sata_hba_tran_t));
si_ctlp->sictl_sata_hba_tran = NULL;
return (SI_SUCCESS);
}
static int
si_tran_probe_port(dev_info_t *dip, sata_device_t *sd)
{
si_ctl_state_t *si_ctlp;
uint8_t cport = sd->satadev_addr.cport;
uint8_t pmport = sd->satadev_addr.pmport;
uint8_t qual = sd->satadev_addr.qual;
uint8_t port_type;
si_port_state_t *si_portp;
si_portmult_state_t *si_portmultp;
si_ctlp = ddi_get_soft_state(si_statep, ddi_get_instance(dip));
SIDBG_C(SIDBG_ENTRY, si_ctlp,
"si_tran_probe_port: cport: 0x%x, pmport: 0x%x, qual: 0x%x",
cport, pmport, qual);
if (cport >= SI_MAX_PORTS) {
sd->satadev_type = SATA_DTYPE_NONE;
sd->satadev_state = SATA_STATE_UNKNOWN;
return (SATA_FAILURE);
}
mutex_enter(&si_ctlp->sictl_mutex);
si_portp = si_ctlp->sictl_ports[cport];
mutex_exit(&si_ctlp->sictl_mutex);
if (si_portp == NULL) {
sd->satadev_type = SATA_DTYPE_NONE;
sd->satadev_state = SATA_STATE_UNKNOWN;
return (SATA_FAILURE);
}
mutex_enter(&si_portp->siport_mutex);
if (qual == SATA_ADDR_PMPORT) {
if (pmport >= si_portp->siport_portmult_state.sipm_num_ports) {
sd->satadev_type = SATA_DTYPE_NONE;
sd->satadev_state = SATA_STATE_UNKNOWN;
mutex_exit(&si_portp->siport_mutex);
return (SATA_FAILURE);
} else {
si_portmultp = &si_portp->siport_portmult_state;
port_type = si_portmultp->sipm_port_type[pmport];
}
} else {
port_type = si_portp->siport_port_type;
}
switch (port_type) {
case PORT_TYPE_DISK:
sd->satadev_type = SATA_DTYPE_ATADISK;
break;
case PORT_TYPE_ATAPI:
sd->satadev_type = SATA_DTYPE_ATAPICD;
break;
case PORT_TYPE_MULTIPLIER:
sd->satadev_type = SATA_DTYPE_PMULT;
sd->satadev_add_info =
si_portp->siport_portmult_state.sipm_num_ports;
break;
case PORT_TYPE_UNKNOWN:
sd->satadev_type = SATA_DTYPE_UNKNOWN;
break;
default:
sd->satadev_type = SATA_DTYPE_NONE;
break;
}
sd->satadev_state = SATA_STATE_READY;
if (qual == SATA_ADDR_PMPORT) {
(void) si_read_portmult_reg(si_ctlp, si_portp, cport,
pmport, PSCR_REG0, &sd->satadev_scr.sstatus);
(void) si_read_portmult_reg(si_ctlp, si_portp, cport,
pmport, PSCR_REG1, &sd->satadev_scr.serror);
(void) si_read_portmult_reg(si_ctlp, si_portp, cport,
pmport, PSCR_REG2, &sd->satadev_scr.scontrol);
(void) si_read_portmult_reg(si_ctlp, si_portp, cport,
pmport, PSCR_REG3, &sd->satadev_scr.sactive);
} else {
fill_dev_sregisters(si_ctlp, cport, sd);
if (!(si_portp->siport_active)) {
SSTATUS_SET_DET(sd->satadev_scr.sstatus,
SSTATUS_DET_PHYOFFLINE);
SSTATUS_SET_IPM(sd->satadev_scr.sstatus,
SSTATUS_IPM_NODEV_NOPHY);
sd->satadev_state = SATA_PSTATE_SHUTDOWN;
}
}
mutex_exit(&si_portp->siport_mutex);
return (SATA_SUCCESS);
}
static int
si_tran_start(dev_info_t *dip, sata_pkt_t *spkt)
{
si_ctl_state_t *si_ctlp;
uint8_t cport;
si_port_state_t *si_portp;
int slot;
cport = spkt->satapkt_device.satadev_addr.cport;
si_ctlp = ddi_get_soft_state(si_statep, ddi_get_instance(dip));
mutex_enter(&si_ctlp->sictl_mutex);
si_portp = si_ctlp->sictl_ports[cport];
mutex_exit(&si_ctlp->sictl_mutex);
SIDBG_P(SIDBG_ENTRY, si_portp,
"si_tran_start entry", NULL);
mutex_enter(&si_portp->siport_mutex);
if ((si_portp->siport_port_type == PORT_TYPE_NODEV) ||
!si_portp->siport_active) {
spkt->satapkt_reason = SATA_PKT_PORT_ERROR;
fill_dev_sregisters(si_ctlp, cport, &spkt->satapkt_device);
mutex_exit(&si_portp->siport_mutex);
return (SATA_TRAN_PORT_ERROR);
}
if (spkt->satapkt_cmd.satacmd_flags.sata_clear_dev_reset) {
si_portp->siport_reset_in_progress = 0;
SIDBG_P(SIDBG_RESET, si_portp,
"si_tran_start clearing the "
"reset_in_progress for port", NULL);
}
if (si_portp->siport_reset_in_progress &&
! spkt->satapkt_cmd.satacmd_flags.sata_ignore_dev_reset &&
! ddi_in_panic()) {
spkt->satapkt_reason = SATA_PKT_BUSY;
SIDBG_P(SIDBG_RESET, si_portp,
"si_tran_start returning BUSY while "
"reset in progress for port", NULL);
mutex_exit(&si_portp->siport_mutex);
return (SATA_TRAN_BUSY);
}
if (si_portp->mopping_in_progress > 0) {
spkt->satapkt_reason = SATA_PKT_BUSY;
SIDBG_P(SIDBG_RESET, si_portp,
"si_tran_start returning BUSY while "
"mopping in progress for port", NULL);
mutex_exit(&si_portp->siport_mutex);
return (SATA_TRAN_BUSY);
}
if ((slot = si_deliver_satapkt(si_ctlp, si_portp, cport, spkt))
== SI_FAILURE) {
spkt->satapkt_reason = SATA_PKT_QUEUE_FULL;
SIDBG_P(SIDBG_ERRS, si_portp,
"si_tran_start returning QUEUE_FULL",
NULL);
mutex_exit(&si_portp->siport_mutex);
return (SATA_TRAN_QUEUE_FULL);
}
if (spkt->satapkt_op_mode & (SATA_OPMODE_POLLING|SATA_OPMODE_SYNCH)) {
si_poll_cmd(si_ctlp, si_portp, cport, slot, spkt);
si_portp->siport_slot_pkts[slot] = NULL;
}
mutex_exit(&si_portp->siport_mutex);
return (SATA_TRAN_ACCEPTED);
}
#define SENDUP_PACKET(si_portp, satapkt, reason) \
if (satapkt) { \
if ((satapkt->satapkt_cmd.satacmd_cmd_reg == \
SATAC_WRITE_FPDMA_QUEUED) || \
(satapkt->satapkt_cmd.satacmd_cmd_reg == \
SATAC_READ_FPDMA_QUEUED)) { \
si_portp->siport_pending_ncq_count--; \
} \
satapkt->satapkt_reason = reason; \
\
if (!(satapkt->satapkt_op_mode & SATA_OPMODE_SYNCH) && \
satapkt->satapkt_comp) { \
mutex_exit(&si_portp->siport_mutex); \
(*satapkt->satapkt_comp)(satapkt); \
mutex_enter(&si_portp->siport_mutex); \
} \
}
static void
si_mop_commands(si_ctl_state_t *si_ctlp, si_port_state_t *si_portp,
uint8_t port, uint32_t slot_status, uint32_t failed_tags,
uint32_t timedout_tags, uint32_t aborting_tags, uint32_t reset_tags)
{
uint32_t finished_tags, unfinished_tags;
int tmpslot;
sata_pkt_t *satapkt;
struct sata_cmd_flags *flagsp;
SIDBG_P(SIDBG_ERRS, si_portp,
"si_mop_commands entered: slot_status: 0x%x",
slot_status);
SIDBG_P(SIDBG_ERRS, si_portp,
"si_mop_commands: failed_tags: 0x%x, timedout_tags: 0x%x"
"aborting_tags: 0x%x, reset_tags: 0x%x",
failed_tags,
timedout_tags,
aborting_tags,
reset_tags);
finished_tags = si_portp->siport_pending_tags &
~slot_status & SI_SLOT_MASK;
unfinished_tags = slot_status & SI_SLOT_MASK &
~failed_tags &
~aborting_tags &
~reset_tags &
~timedout_tags;
while (finished_tags) {
tmpslot = ddi_ffs(finished_tags) - 1;
if (tmpslot == -1) {
break;
}
satapkt = si_portp->siport_slot_pkts[tmpslot];
if (satapkt != NULL &&
satapkt->satapkt_cmd.satacmd_flags.sata_special_regs) {
si_copy_out_regs(&satapkt->satapkt_cmd, si_ctlp,
port, tmpslot);
}
SIDBG_P(SIDBG_ERRS, si_portp,
"si_mop_commands sending up completed satapkt: %x",
satapkt);
CLEAR_BIT(si_portp->siport_pending_tags, tmpslot);
CLEAR_BIT(finished_tags, tmpslot);
SENDUP_PACKET(si_portp, satapkt, SATA_PKT_COMPLETED);
}
ASSERT(finished_tags == 0);
while (failed_tags) {
tmpslot = ddi_ffs(failed_tags) - 1;
if (tmpslot == -1) {
break;
}
SIDBG_P(SIDBG_ERRS, si_portp, "si3124: si_mop_commands: "
"handling failed slot: 0x%x", tmpslot);
satapkt = si_portp->siport_slot_pkts[tmpslot];
if (satapkt != NULL) {
if (satapkt->satapkt_device.satadev_type ==
SATA_DTYPE_ATAPICD) {
si_set_sense_data(satapkt, SATA_PKT_DEV_ERROR);
}
flagsp = &satapkt->satapkt_cmd.satacmd_flags;
flagsp->sata_copy_out_lba_low_msb = B_TRUE;
flagsp->sata_copy_out_lba_mid_msb = B_TRUE;
flagsp->sata_copy_out_lba_high_msb = B_TRUE;
flagsp->sata_copy_out_lba_low_lsb = B_TRUE;
flagsp->sata_copy_out_lba_mid_lsb = B_TRUE;
flagsp->sata_copy_out_lba_high_lsb = B_TRUE;
flagsp->sata_copy_out_error_reg = B_TRUE;
flagsp->sata_copy_out_sec_count_msb = B_TRUE;
flagsp->sata_copy_out_sec_count_lsb = B_TRUE;
flagsp->sata_copy_out_device_reg = B_TRUE;
si_copy_out_regs(&satapkt->satapkt_cmd, si_ctlp,
port, tmpslot);
if (si_portp->siport_err_tags_SDBERROR &
(1 << tmpslot)) {
satapkt->satapkt_cmd.satacmd_error_reg =
si_read_log_ext(si_ctlp, si_portp, port);
}
}
CLEAR_BIT(failed_tags, tmpslot);
CLEAR_BIT(si_portp->siport_pending_tags, tmpslot);
SENDUP_PACKET(si_portp, satapkt, SATA_PKT_DEV_ERROR);
}
ASSERT(failed_tags == 0);
while (timedout_tags) {
tmpslot = ddi_ffs(timedout_tags) - 1;
if (tmpslot == -1) {
break;
}
satapkt = si_portp->siport_slot_pkts[tmpslot];
SIDBG_P(SIDBG_ERRS, si_portp,
"si_mop_commands sending "
"spkt up with PKT_TIMEOUT: %x",
satapkt);
CLEAR_BIT(si_portp->siport_pending_tags, tmpslot);
CLEAR_BIT(timedout_tags, tmpslot);
SENDUP_PACKET(si_portp, satapkt, SATA_PKT_TIMEOUT);
}
ASSERT(timedout_tags == 0);
while (aborting_tags) {
tmpslot = ddi_ffs(aborting_tags) - 1;
if (tmpslot == -1) {
break;
}
satapkt = si_portp->siport_slot_pkts[tmpslot];
SIDBG_P(SIDBG_ERRS, si_portp,
"si_mop_commands aborting spkt: %x",
satapkt);
if (satapkt != NULL && satapkt->satapkt_device.satadev_type ==
SATA_DTYPE_ATAPICD) {
si_set_sense_data(satapkt, SATA_PKT_ABORTED);
}
CLEAR_BIT(si_portp->siport_pending_tags, tmpslot);
CLEAR_BIT(aborting_tags, tmpslot);
SENDUP_PACKET(si_portp, satapkt, SATA_PKT_ABORTED);
}
ASSERT(aborting_tags == 0);
while (reset_tags) {
tmpslot = ddi_ffs(reset_tags) - 1;
if (tmpslot == -1) {
break;
}
satapkt = si_portp->siport_slot_pkts[tmpslot];
SIDBG_P(SIDBG_ERRS, si_portp,
"si_mop_commands sending PKT_RESET for "
"reset spkt: %x",
satapkt);
CLEAR_BIT(reset_tags, tmpslot);
CLEAR_BIT(si_portp->siport_pending_tags, tmpslot);
SENDUP_PACKET(si_portp, satapkt, SATA_PKT_RESET);
}
ASSERT(reset_tags == 0);
while (unfinished_tags) {
tmpslot = ddi_ffs(unfinished_tags) - 1;
if (tmpslot == -1) {
break;
}
satapkt = si_portp->siport_slot_pkts[tmpslot];
SIDBG_P(SIDBG_ERRS, si_portp,
"si_mop_commands sending SATA_PKT_RESET for "
"retry spkt: %x",
satapkt);
CLEAR_BIT(unfinished_tags, tmpslot);
CLEAR_BIT(si_portp->siport_pending_tags, tmpslot);
SENDUP_PACKET(si_portp, satapkt, SATA_PKT_RESET);
}
ASSERT(unfinished_tags == 0);
si_portp->mopping_in_progress--;
ASSERT(si_portp->mopping_in_progress >= 0);
}
static int
si_tran_abort(dev_info_t *dip, sata_pkt_t *spkt, int flag)
{
uint32_t slot_status;
uint8_t port;
int tmpslot;
uint32_t aborting_tags;
uint32_t finished_tags;
si_port_state_t *si_portp;
si_ctl_state_t *si_ctlp;
port = spkt->satapkt_device.satadev_addr.cport;
si_ctlp = ddi_get_soft_state(si_statep, ddi_get_instance(dip));
mutex_enter(&si_ctlp->sictl_mutex);
si_portp = si_ctlp->sictl_ports[port];
mutex_exit(&si_ctlp->sictl_mutex);
SIDBG_P(SIDBG_ERRS, si_portp, "si_tran_abort on port: %x", port);
mutex_enter(&si_portp->siport_mutex);
if (si_portp->mopping_in_progress > 0) {
SIDBG_P(SIDBG_ERRS, si_portp,
"si_tran_abort: port %d mopping "
"in progress, so just return", port);
mutex_exit(&si_portp->siport_mutex);
return (SATA_SUCCESS);
}
if ((si_portp->siport_port_type == PORT_TYPE_NODEV) ||
!si_portp->siport_active) {
spkt->satapkt_reason = SATA_PKT_PORT_ERROR;
fill_dev_sregisters(si_ctlp, port, &spkt->satapkt_device);
mutex_exit(&si_portp->siport_mutex);
return (SATA_FAILURE);
}
if (flag == SATA_ABORT_ALL_PACKETS) {
aborting_tags = si_portp->siport_pending_tags;
} else {
aborting_tags = 0xffffffff;
for (tmpslot = 0; tmpslot < SI_NUM_SLOTS; tmpslot++) {
if (si_portp->siport_slot_pkts[tmpslot] == spkt) {
aborting_tags = (0x1 << tmpslot);
break;
}
}
if (aborting_tags == 0xffffffff) {
fill_dev_sregisters(si_ctlp, port,
&spkt->satapkt_device);
mutex_exit(&si_portp->siport_mutex);
return (SATA_FAILURE);
}
}
si_portp->mopping_in_progress++;
slot_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SLOT_STATUS(si_ctlp, port)));
(void) si_reset_dport_wait_till_ready(si_ctlp, si_portp,
port, SI_DEVICE_RESET);
finished_tags = si_portp->siport_pending_tags &
~slot_status & SI_SLOT_MASK;
aborting_tags &= ~finished_tags;
si_mop_commands(si_ctlp,
si_portp,
port,
slot_status,
0,
0,
aborting_tags,
0);
fill_dev_sregisters(si_ctlp, port, &spkt->satapkt_device);
mutex_exit(&si_portp->siport_mutex);
return (SATA_SUCCESS);
}
static void
si_reject_all_reset_pkts(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
uint32_t slot_status;
uint32_t reset_tags;
_NOTE(ASSUMING_PROTECTED(si_portp))
SIDBG_P(SIDBG_RESET, si_portp,
"si_reject_all_reset_pkts on port: %x",
port);
slot_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SLOT_STATUS(si_ctlp, port)));
reset_tags = slot_status & SI_SLOT_MASK;
si_portp->mopping_in_progress++;
si_mop_commands(si_ctlp,
si_portp,
port,
slot_status,
0,
0,
0,
reset_tags);
}
static int
si_tran_reset_dport(dev_info_t *dip, sata_device_t *sd)
{
si_ctl_state_t *si_ctlp;
uint8_t port = sd->satadev_addr.cport;
int i;
si_port_state_t *si_portp;
int retval = SI_SUCCESS;
si_ctlp = ddi_get_soft_state(si_statep, ddi_get_instance(dip));
SIDBG_C(SIDBG_RESET, si_ctlp,
"si_tran_reset_port entry: port: 0x%x",
port);
switch (sd->satadev_addr.qual) {
case SATA_ADDR_CPORT:
mutex_enter(&si_ctlp->sictl_mutex);
si_portp = si_ctlp->sictl_ports[port];
mutex_exit(&si_ctlp->sictl_mutex);
mutex_enter(&si_portp->siport_mutex);
if (si_portp->mopping_in_progress > 0) {
SIDBG_P(SIDBG_RESET, si_portp,
"si_tran_reset_dport: CPORT port %d mopping "
"in progress, so just return", port);
mutex_exit(&si_portp->siport_mutex);
retval = SI_SUCCESS;
break;
}
retval = si_reset_dport_wait_till_ready(si_ctlp, si_portp, port,
SI_PORT_RESET);
si_reject_all_reset_pkts(si_ctlp, si_portp, port);
mutex_exit(&si_portp->siport_mutex);
break;
case SATA_ADDR_DCPORT:
mutex_enter(&si_ctlp->sictl_mutex);
si_portp = si_ctlp->sictl_ports[port];
mutex_exit(&si_ctlp->sictl_mutex);
mutex_enter(&si_portp->siport_mutex);
if ((si_portp->siport_port_type == PORT_TYPE_NODEV) ||
!si_portp->siport_active) {
mutex_exit(&si_portp->siport_mutex);
retval = SI_FAILURE;
break;
}
if (si_portp->mopping_in_progress > 0) {
SIDBG_P(SIDBG_RESET, si_portp,
"si_tran_reset_dport: DCPORT port %d mopping "
"in progress, so just return", port);
mutex_exit(&si_portp->siport_mutex);
retval = SI_SUCCESS;
break;
}
retval = si_reset_dport_wait_till_ready(si_ctlp, si_portp, port,
SI_DEVICE_RESET);
si_reject_all_reset_pkts(si_ctlp, si_portp, port);
mutex_exit(&si_portp->siport_mutex);
break;
case SATA_ADDR_CNTRL:
for (i = 0; i < si_ctlp->sictl_num_ports; i++) {
mutex_enter(&si_ctlp->sictl_mutex);
si_portp = si_ctlp->sictl_ports[i];
mutex_exit(&si_ctlp->sictl_mutex);
mutex_enter(&si_portp->siport_mutex);
if (si_portp->mopping_in_progress > 0) {
SIDBG_P(SIDBG_RESET, si_portp,
"si_tran_reset_dport: CNTRL port %d mopping"
" in progress, so just return", i);
mutex_exit(&si_portp->siport_mutex);
retval = SI_SUCCESS;
break;
}
retval = si_reset_dport_wait_till_ready(si_ctlp,
si_portp, i, SI_PORT_RESET);
if (retval) {
mutex_exit(&si_portp->siport_mutex);
break;
}
si_reject_all_reset_pkts(si_ctlp, si_portp, i);
mutex_exit(&si_portp->siport_mutex);
}
break;
case SATA_ADDR_PMPORT:
case SATA_ADDR_DPMPORT:
SIDBG_P(SIDBG_RESET, si_portp,
"port mult reset not implemented yet", NULL);
default:
retval = SI_FAILURE;
}
return (retval);
}
static int
si_tran_hotplug_port_activate(dev_info_t *dip, sata_device_t *satadev)
{
si_ctl_state_t *si_ctlp;
si_port_state_t *si_portp;
uint8_t port;
si_ctlp = ddi_get_soft_state(si_statep, ddi_get_instance(dip));
port = satadev->satadev_addr.cport;
mutex_enter(&si_ctlp->sictl_mutex);
si_portp = si_ctlp->sictl_ports[port];
mutex_exit(&si_ctlp->sictl_mutex);
SIDBG_P(SIDBG_EVENT, si_portp, "si_tran_hotplug_port_activate entry",
NULL);
mutex_enter(&si_portp->siport_mutex);
si_enable_port_interrupts(si_ctlp, port);
(void) si_reset_dport_wait_till_ready(si_ctlp, si_portp, port,
SI_DEVICE_RESET|SI_RESET_NO_EVENTS_UP);
satadev->satadev_state = SATA_STATE_READY;
si_portp->siport_active = PORT_ACTIVE;
fill_dev_sregisters(si_ctlp, port, satadev);
mutex_exit(&si_portp->siport_mutex);
return (SATA_SUCCESS);
}
static int
si_tran_hotplug_port_deactivate(dev_info_t *dip, sata_device_t *satadev)
{
si_ctl_state_t *si_ctlp;
si_port_state_t *si_portp;
uint8_t port;
si_ctlp = ddi_get_soft_state(si_statep, ddi_get_instance(dip));
port = satadev->satadev_addr.cport;
mutex_enter(&si_ctlp->sictl_mutex);
si_portp = si_ctlp->sictl_ports[port];
mutex_exit(&si_ctlp->sictl_mutex);
SIDBG(SIDBG_EVENT, "si_tran_hotplug_port_deactivate entry", NULL);
mutex_enter(&si_portp->siport_mutex);
if (si_portp->siport_pending_tags & SI_SLOT_MASK) {
satadev->satadev_state = SATA_STATE_READY;
mutex_exit(&si_portp->siport_mutex);
return (SATA_FAILURE);
}
si_portp->siport_active = PORT_INACTIVE;
si_disable_port_interrupts(si_ctlp, port);
satadev->satadev_state = SATA_PSTATE_SHUTDOWN;
fill_dev_sregisters(si_ctlp, port, satadev);
SSTATUS_SET_DET(satadev->satadev_scr.sstatus, SSTATUS_DET_PHYOFFLINE);
SSTATUS_SET_IPM(satadev->satadev_scr.sstatus, SSTATUS_IPM_NODEV_NOPHY);
mutex_exit(&si_portp->siport_mutex);
return (SATA_SUCCESS);
}
static int
si_alloc_port_state(si_ctl_state_t *si_ctlp, int port)
{
si_port_state_t *si_portp;
si_ctlp->sictl_ports[port] = (si_port_state_t *)kmem_zalloc(
sizeof (si_port_state_t), KM_SLEEP);
si_portp = si_ctlp->sictl_ports[port];
mutex_init(&si_portp->siport_mutex, NULL, MUTEX_DRIVER,
(void *)(uintptr_t)si_ctlp->sictl_intr_pri);
mutex_enter(&si_portp->siport_mutex);
if (si_alloc_prbpool(si_ctlp, port)) {
mutex_exit(&si_portp->siport_mutex);
kmem_free(si_ctlp->sictl_ports[port], sizeof (si_port_state_t));
return (SI_FAILURE);
}
if (si_alloc_sgbpool(si_ctlp, port)) {
si_dealloc_prbpool(si_ctlp, port);
mutex_exit(&si_portp->siport_mutex);
kmem_free(si_ctlp->sictl_ports[port], sizeof (si_port_state_t));
return (SI_FAILURE);
}
si_portp->siport_event_args =
kmem_zalloc(sizeof (si_event_arg_t), KM_SLEEP);
si_portp->siport_active = PORT_ACTIVE;
mutex_exit(&si_portp->siport_mutex);
return (SI_SUCCESS);
}
static void
si_dealloc_port_state(si_ctl_state_t *si_ctlp, int port)
{
si_port_state_t *si_portp;
si_portp = si_ctlp->sictl_ports[port];
mutex_enter(&si_portp->siport_mutex);
kmem_free(si_portp->siport_event_args, sizeof (si_event_arg_t));
si_dealloc_sgbpool(si_ctlp, port);
si_dealloc_prbpool(si_ctlp, port);
mutex_exit(&si_portp->siport_mutex);
mutex_destroy(&si_portp->siport_mutex);
kmem_free(si_ctlp->sictl_ports[port], sizeof (si_port_state_t));
}
static int
si_alloc_sgbpool(si_ctl_state_t *si_ctlp, int port)
{
si_port_state_t *si_portp;
uint_t cookie_count;
size_t incore_sgbpool_size = SI_NUM_SLOTS * sizeof (si_sgblock_t)
* si_dma_sg_number;
size_t ret_len;
ddi_dma_cookie_t sgbpool_dma_cookie;
si_portp = si_ctlp->sictl_ports[port];
if (ddi_dma_alloc_handle(si_ctlp->sictl_devinfop,
&prb_sgt_dma_attr,
DDI_DMA_SLEEP,
NULL,
&si_portp->siport_sgbpool_dma_handle) !=
DDI_SUCCESS) {
return (SI_FAILURE);
}
if (ddi_dma_mem_alloc(si_portp->siport_sgbpool_dma_handle,
incore_sgbpool_size,
&accattr,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
DDI_DMA_SLEEP,
NULL,
(caddr_t *)&si_portp->siport_sgbpool,
&ret_len,
&si_portp->siport_sgbpool_acc_handle) != DDI_SUCCESS) {
ddi_dma_free_handle(&si_portp->siport_sgbpool_dma_handle);
return (SI_FAILURE);
}
if (ddi_dma_addr_bind_handle(si_portp->siport_sgbpool_dma_handle,
NULL,
(caddr_t)si_portp->siport_sgbpool,
incore_sgbpool_size,
DDI_DMA_CONSISTENT,
DDI_DMA_SLEEP,
NULL,
&sgbpool_dma_cookie,
&cookie_count) != DDI_DMA_MAPPED) {
ddi_dma_mem_free(&si_portp->siport_sgbpool_acc_handle);
ddi_dma_free_handle(&si_portp->siport_sgbpool_dma_handle);
return (SI_FAILURE);
}
si_portp->siport_sgbpool_physaddr = sgbpool_dma_cookie.dmac_laddress;
return (SI_SUCCESS);
}
static void
si_dealloc_sgbpool(si_ctl_state_t *si_ctlp, int port)
{
si_port_state_t *si_portp = si_ctlp->sictl_ports[port];
(void) ddi_dma_unbind_handle(si_portp->siport_sgbpool_dma_handle);
ddi_dma_mem_free(&si_portp->siport_sgbpool_acc_handle);
ddi_dma_free_handle(&si_portp->siport_sgbpool_dma_handle);
}
static int
si_alloc_prbpool(si_ctl_state_t *si_ctlp, int port)
{
si_port_state_t *si_portp;
uint_t cookie_count;
size_t incore_pkt_size = SI_NUM_SLOTS * sizeof (si_prb_t);
size_t ret_len;
ddi_dma_cookie_t prbpool_dma_cookie;
si_portp = si_ctlp->sictl_ports[port];
if (ddi_dma_alloc_handle(si_ctlp->sictl_devinfop,
&prb_sgt_dma_attr,
DDI_DMA_SLEEP,
NULL,
&si_portp->siport_prbpool_dma_handle) !=
DDI_SUCCESS) {
return (SI_FAILURE);
}
if (ddi_dma_mem_alloc(si_portp->siport_prbpool_dma_handle,
incore_pkt_size,
&accattr,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
DDI_DMA_SLEEP,
NULL,
(caddr_t *)&si_portp->siport_prbpool,
&ret_len,
&si_portp->siport_prbpool_acc_handle) != DDI_SUCCESS) {
ddi_dma_free_handle(&si_portp->siport_prbpool_dma_handle);
return (SI_FAILURE);
}
if (ddi_dma_addr_bind_handle(si_portp->siport_prbpool_dma_handle,
NULL,
(caddr_t)si_portp->siport_prbpool,
incore_pkt_size,
DDI_DMA_CONSISTENT,
DDI_DMA_SLEEP,
NULL,
&prbpool_dma_cookie,
&cookie_count) != DDI_DMA_MAPPED) {
ddi_dma_mem_free(&si_portp->siport_prbpool_acc_handle);
ddi_dma_free_handle(&si_portp->siport_prbpool_dma_handle);
return (SI_FAILURE);
}
si_portp->siport_prbpool_physaddr =
prbpool_dma_cookie.dmac_laddress;
return (SI_SUCCESS);
}
static void
si_dealloc_prbpool(si_ctl_state_t *si_ctlp, int port)
{
si_port_state_t *si_portp = si_ctlp->sictl_ports[port];
(void) ddi_dma_unbind_handle(si_portp->siport_prbpool_dma_handle);
ddi_dma_mem_free(&si_portp->siport_prbpool_acc_handle);
ddi_dma_free_handle(&si_portp->siport_prbpool_dma_handle);
}
static void
si_find_dev_signature(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port,
int pmp)
{
si_prb_t *prb;
uint32_t slot_status, signature;
int slot, loop_count;
SIDBG_P(SIDBG_INIT, si_portp,
"si_find_dev_signature enter: port: %x, pmp: %x",
port, pmp);
mutex_enter(&si_portp->siport_mutex);
slot = si_claim_free_slot(si_ctlp, si_portp, port);
if (slot == SI_FAILURE) {
if (pmp != PORTMULT_CONTROL_PORT) {
si_portp->siport_portmult_state.sipm_port_type[pmp] =
PORT_TYPE_NODEV;
} else {
si_portp->siport_port_type = PORT_TYPE_NODEV;
}
mutex_exit(&si_portp->siport_mutex);
return;
}
prb = &si_portp->siport_prbpool[slot];
bzero((void *)prb, sizeof (si_prb_t));
SET_FIS_PMP(prb->prb_fis, pmp);
SET_PRB_CONTROL_SOFT_RESET(prb);
#if SI_DEBUG
if (si_debug_flags & SIDBG_DUMP_PRB) {
char *ptr;
int j;
ptr = (char *)prb;
cmn_err(CE_WARN, "si_find_dev_signature, prb: ");
for (j = 0; j < (sizeof (si_prb_t)); j++) {
if (j%4 == 0) {
cmn_err(CE_WARN, "----");
}
cmn_err(CE_WARN, "%x ", ptr[j]);
}
}
#endif
POST_PRB_ADDR(si_ctlp, si_portp, port, slot);
loop_count = 0;
do {
slot_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SLOT_STATUS(si_ctlp, port)));
if (loop_count++ > SI_POLLRATE_SOFT_RESET) {
break;
}
#ifndef __lock_lint
delay(SI_10MS_TICKS);
#endif
} while (slot_status & SI_SLOT_MASK & (0x1 << slot));
SIDBG_P(SIDBG_POLL_LOOP, si_portp,
"si_find_dev_signature: loop count: %d, slot_status: 0x%x",
loop_count, slot_status);
CLEAR_BIT(si_portp->siport_pending_tags, slot);
signature = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SIGNATURE_MSB(si_ctlp, port, slot)));
signature <<= 8;
signature |= (0xff & ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SIGNATURE_LSB(si_ctlp,
port, slot))));
SIDBG_P(SIDBG_INIT, si_portp, "Device signature: 0x%x", signature);
if (signature == SI_SIGNATURE_PORT_MULTIPLIER) {
SIDBG_P(SIDBG_INIT, si_portp,
"Found multiplier at cport: 0x%d, pmport: 0x%x",
port, pmp);
if (pmp != PORTMULT_CONTROL_PORT) {
si_portp->siport_portmult_state.sipm_port_type[pmp] =
PORT_TYPE_NODEV;
} else {
si_portp->siport_port_type = PORT_TYPE_MULTIPLIER;
mutex_exit(&si_portp->siport_mutex);
(void) si_enumerate_port_multiplier(si_ctlp,
si_portp, port);
mutex_enter(&si_portp->siport_mutex);
}
si_init_port(si_ctlp, port);
} else if (signature == SI_SIGNATURE_ATAPI) {
if (pmp != PORTMULT_CONTROL_PORT) {
si_portp->siport_portmult_state.sipm_port_type[pmp] =
PORT_TYPE_ATAPI;
} else {
si_portp->siport_port_type = PORT_TYPE_ATAPI;
si_init_port(si_ctlp, port);
}
SIDBG_P(SIDBG_INIT, si_portp,
"Found atapi at : cport: %x, pmport: %x",
port, pmp);
} else if (signature == SI_SIGNATURE_DISK) {
if (pmp != PORTMULT_CONTROL_PORT) {
si_portp->siport_portmult_state.sipm_port_type[pmp] =
PORT_TYPE_DISK;
} else {
si_portp->siport_port_type = PORT_TYPE_DISK;
si_init_port(si_ctlp, port);
}
SIDBG_P(SIDBG_INIT, si_portp,
"found disk at : cport: %x, pmport: %x",
port, pmp);
} else {
if (pmp != PORTMULT_CONTROL_PORT) {
si_portp->siport_portmult_state.sipm_port_type[pmp] =
PORT_TYPE_UNKNOWN;
} else {
si_portp->siport_port_type = PORT_TYPE_UNKNOWN;
}
SIDBG_P(SIDBG_INIT, si_portp,
"Found unknown signature 0x%x at: port: %x, pmp: %x",
signature, port, pmp);
}
mutex_exit(&si_portp->siport_mutex);
}
static void
si_poll_cmd(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port,
int slot,
sata_pkt_t *satapkt)
{
uint32_t slot_status;
int pkt_timeout_ticks;
uint32_t port_intr_status;
int in_panic = ddi_in_panic();
SIDBG_P(SIDBG_ENTRY, si_portp, "si_poll_cmd entered: port: 0x%x", port);
pkt_timeout_ticks = drv_usectohz((clock_t)satapkt->satapkt_time *
1000000);
satapkt->satapkt_reason = SATA_PKT_COMPLETED;
do {
slot_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SLOT_STATUS(si_ctlp, port)));
if (slot_status & SI_SLOT_MASK & (0x1 << slot)) {
if (in_panic) {
mutex_exit(&si_portp->siport_mutex);
drv_usecwait(SI_1MS_USECS);
mutex_enter(&si_portp->siport_mutex);
} else {
mutex_exit(&si_portp->siport_mutex);
#ifndef __lock_lint
delay(SI_1MS_TICKS);
#endif
mutex_enter(&si_portp->siport_mutex);
}
} else {
break;
}
pkt_timeout_ticks -= SI_1MS_TICKS;
} while (pkt_timeout_ticks > 0);
if (satapkt->satapkt_reason != SATA_PKT_COMPLETED) {
return;
}
if (IS_ATTENTION_RAISED(slot_status)) {
port_intr_status = ddi_get32(si_ctlp->sictl_global_acc_handle,
(uint32_t *)PORT_INTERRUPT_STATUS(si_ctlp, port));
SIDBG_P(SIDBG_VERBOSE, si_portp,
"si_poll_cmd: port_intr_status: 0x%x, port: %x",
port_intr_status, port);
if (port_intr_status & INTR_COMMAND_ERROR) {
mutex_exit(&si_portp->siport_mutex);
(void) si_intr_command_error(si_ctlp, si_portp, port);
mutex_enter(&si_portp->siport_mutex);
return;
} else {
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_INTERRUPT_STATUS(si_ctlp,
port)),
port_intr_status & INTR_MASK);
}
} else if (slot_status & SI_SLOT_MASK & (0x1 << slot)) {
satapkt->satapkt_reason = SATA_PKT_TIMEOUT;
}
if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs) {
si_copy_out_regs(&satapkt->satapkt_cmd, si_ctlp, port, slot);
}
if ((satapkt->satapkt_cmd.satacmd_cmd_reg ==
SATAC_WRITE_FPDMA_QUEUED) ||
(satapkt->satapkt_cmd.satacmd_cmd_reg ==
SATAC_READ_FPDMA_QUEUED)) {
si_portp->siport_pending_ncq_count--;
}
CLEAR_BIT(si_portp->siport_pending_tags, slot);
}
static int
si_claim_free_slot(si_ctl_state_t *si_ctlp, si_port_state_t *si_portp, int port)
{
uint32_t free_slots;
int slot;
_NOTE(ASSUMING_PROTECTED(si_portp))
SIDBG_P(SIDBG_ENTRY, si_portp,
"si_claim_free_slot entry: siport_pending_tags: %x",
si_portp->siport_pending_tags);
free_slots = (~si_portp->siport_pending_tags) & SI_SLOT_MASK;
slot = ddi_ffs(free_slots) - 1;
if (slot == -1) {
SIDBG_P(SIDBG_VERBOSE, si_portp,
"si_claim_free_slot: no empty slots", NULL);
return (SI_FAILURE);
}
si_portp->siport_pending_tags |= (0x1 << slot);
SIDBG_P(SIDBG_VERBOSE, si_portp, "si_claim_free_slot: found slot: 0x%x",
slot);
return (slot);
}
static int
si_deliver_satapkt(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port,
sata_pkt_t *spkt)
{
int slot;
si_prb_t *prb;
sata_cmd_t *cmd;
si_sge_t *sgep;
si_sgt_t *sgtp;
si_sgblock_t *sgbp;
int i, j, cookie_index;
int ncookies;
int is_atapi = 0;
ddi_dma_cookie_t cookie;
_NOTE(ASSUMING_PROTECTED(si_portp))
slot = si_claim_free_slot(si_ctlp, si_portp, port);
if (slot == SI_FAILURE) {
return (SI_FAILURE);
}
if (spkt->satapkt_device.satadev_type == SATA_DTYPE_ATAPICD) {
is_atapi = 1;
}
if ((si_portp->siport_port_type == PORT_TYPE_NODEV) ||
!si_portp->siport_active) {
spkt->satapkt_reason = SATA_PKT_PORT_ERROR;
fill_dev_sregisters(si_ctlp, port, &spkt->satapkt_device);
CLEAR_BIT(si_portp->siport_pending_tags, slot);
return (SI_FAILURE);
}
prb = &(si_portp->siport_prbpool[slot]);
bzero((void *)prb, sizeof (si_prb_t));
cmd = &spkt->satapkt_cmd;
SIDBG_P(SIDBG_ENTRY, si_portp,
"si_deliver_satpkt entry: cmd_reg: 0x%x, slot: 0x%x, \
port: %x, satapkt: %x",
cmd->satacmd_cmd_reg, slot, port, (uint32_t)(intptr_t)spkt);
if (is_atapi) {
if (spkt->satapkt_cmd.satacmd_flags.sata_data_direction ==
SATA_DIR_READ) {
SET_PRB_CONTROL_PKT_READ(prb);
} else if (spkt->satapkt_cmd.satacmd_flags.sata_data_direction
== SATA_DIR_WRITE) {
SET_PRB_CONTROL_PKT_WRITE(prb);
}
}
SET_FIS_TYPE(prb->prb_fis, REGISTER_FIS_H2D);
if ((spkt->satapkt_device.satadev_addr.qual == SATA_ADDR_PMPORT) ||
(spkt->satapkt_device.satadev_addr.qual == SATA_ADDR_DPMPORT)) {
SET_FIS_PMP(prb->prb_fis,
spkt->satapkt_device.satadev_addr.pmport);
}
SET_FIS_CDMDEVCTL(prb->prb_fis, 1);
SET_FIS_COMMAND(prb->prb_fis, cmd->satacmd_cmd_reg);
SET_FIS_FEATURES(prb->prb_fis, cmd->satacmd_features_reg);
SET_FIS_SECTOR_COUNT(prb->prb_fis, cmd->satacmd_sec_count_lsb);
switch (cmd->satacmd_addr_type) {
case 0:
case ATA_ADDR_LBA:
case ATA_ADDR_LBA28:
SET_FIS_SECTOR(prb->prb_fis, cmd->satacmd_lba_low_lsb);
SET_FIS_CYL_LOW(prb->prb_fis, cmd->satacmd_lba_mid_lsb);
SET_FIS_CYL_HI(prb->prb_fis, cmd->satacmd_lba_high_lsb);
SET_FIS_DEV_HEAD(prb->prb_fis, cmd->satacmd_device_reg);
break;
case ATA_ADDR_LBA48:
SET_FIS_SECTOR(prb->prb_fis, cmd->satacmd_lba_low_lsb);
SET_FIS_CYL_LOW(prb->prb_fis, cmd->satacmd_lba_mid_lsb);
SET_FIS_CYL_HI(prb->prb_fis, cmd->satacmd_lba_high_lsb);
SET_FIS_SECTOR_EXP(prb->prb_fis, cmd->satacmd_lba_low_msb);
SET_FIS_CYL_LOW_EXP(prb->prb_fis, cmd->satacmd_lba_mid_msb);
SET_FIS_CYL_HI_EXP(prb->prb_fis, cmd->satacmd_lba_high_msb);
SET_FIS_DEV_HEAD(prb->prb_fis, cmd->satacmd_device_reg);
SET_FIS_SECTOR_COUNT_EXP(prb->prb_fis,
cmd->satacmd_sec_count_msb);
SET_FIS_FEATURES_EXP(prb->prb_fis,
cmd->satacmd_features_reg_ext);
break;
}
if (cmd->satacmd_flags.sata_queued) {
SET_FIS_SECTOR_COUNT(prb->prb_fis, slot << 3);
}
if ((cmd->satacmd_cmd_reg == SATAC_WRITE_FPDMA_QUEUED) ||
(cmd->satacmd_cmd_reg == SATAC_READ_FPDMA_QUEUED)) {
si_portp->siport_pending_ncq_count++;
}
if (is_atapi) {
bcopy(cmd->satacmd_acdb, &prb->prb_sge0, sizeof (si_sge_t));
if (spkt->satapkt_cmd.satacmd_num_dma_cookies) {
prb->prb_sge1.sge_addr =
si_portp->siport_sgbpool_physaddr +
slot * sizeof (si_sgblock_t) * si_dma_sg_number;
SET_SGE_LNK(prb->prb_sge1);
} else {
SET_SGE_TRM(prb->prb_sge1);
}
} else {
if (spkt->satapkt_cmd.satacmd_num_dma_cookies) {
prb->prb_sge0.sge_addr =
si_portp->siport_sgbpool_physaddr +
slot * sizeof (si_sgblock_t) * si_dma_sg_number;
SET_SGE_LNK(prb->prb_sge0);
} else {
SET_SGE_TRM(prb->prb_sge0);
}
}
bzero(&si_portp->siport_sgbpool[slot * si_dma_sg_number],
sizeof (si_sgblock_t) * si_dma_sg_number);
ncookies = spkt->satapkt_cmd.satacmd_num_dma_cookies;
ASSERT(ncookies <= (SGE_LENGTH(si_dma_sg_number)));
SIDBG_P(SIDBG_COOKIES, si_portp, "total ncookies: %d", ncookies);
if (ncookies == 0) {
sgbp = &si_portp->siport_sgbpool[slot * si_dma_sg_number];
sgtp = &sgbp->sgb_sgt[0];
sgep = &sgtp->sgt_sge[0];
SIDBG_P(SIDBG_COOKIES, si_portp, "empty cookies: terminating.",
NULL);
sgep->sge_addr_low = 0;
sgep->sge_addr_high = 0;
sgep->sge_data_count = 0;
SET_SGE_TRM((*sgep));
goto sgl_fill_done;
}
for (i = 0, cookie_index = 0,
sgbp = &si_portp->siport_sgbpool[slot * si_dma_sg_number];
i < si_dma_sg_number; i++) {
sgtp = &sgbp->sgb_sgt[0] + i;
for (j = 0, sgep = &sgtp->sgt_sge[0];
((j < 3) && (cookie_index < ncookies-1));
j++, cookie_index++, sgep++) {
ASSERT(cookie_index < ncookies);
SIDBG_P(SIDBG_COOKIES, si_portp,
"inner loop: cookie_index: %d, ncookies: %d",
cookie_index,
ncookies);
cookie = spkt->satapkt_cmd.
satacmd_dma_cookie_list[cookie_index];
sgep->sge_addr_low = cookie._dmu._dmac_la[0];
sgep->sge_addr_high = cookie._dmu._dmac_la[1];
sgep->sge_data_count = (uint32_t)cookie.dmac_size;
}
if (cookie_index == ncookies-1) {
SIDBG_P(SIDBG_COOKIES, si_portp,
"filling the last: cookie_index: %d, "
"ncookies: %d",
cookie_index,
ncookies);
cookie = spkt->satapkt_cmd.
satacmd_dma_cookie_list[cookie_index];
sgep->sge_addr_low = cookie._dmu._dmac_la[0];
sgep->sge_addr_high = cookie._dmu._dmac_la[1];
sgep->sge_data_count = (uint32_t)cookie.dmac_size;
SET_SGE_TRM((*sgep));
break;
} else {
SIDBG_P(SIDBG_COOKIES, si_portp,
"linking SGT: cookie_index: %d, ncookies: %d",
cookie_index,
ncookies);
sgep->sge_addr = si_portp->siport_sgbpool_physaddr +
slot * sizeof (si_sgblock_t) * si_dma_sg_number +
(i+1) * sizeof (si_sgt_t);
SET_SGE_LNK((*sgep));
}
}
sgl_fill_done:
si_portp->siport_slot_pkts[slot] = spkt;
spkt->satapkt_hba_driver_private = (void *)(intptr_t)0;
if (is_atapi) {
#ifdef ATAPI_2nd_PHASE
ASSERT((cmd->satacmd_acdb_len == 12) ||
(cmd->satacmd_acdb_len == 16));
SIDBG_P(SIDBG_VERBOSE, si_portp, "deliver: acdb_len: %d",
cmd->satacmd_acdb_len);
if (cmd->satacmd_acdb_len == 16) {
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_SET(si_ctlp, port),
PORT_CONTROL_SET_BITS_PACKET_LEN);
} else {
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_CLEAR(si_ctlp, port),
PORT_CONTROL_CLEAR_BITS_PACKET_LEN);
}
#else
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_CLEAR(si_ctlp, port),
PORT_CONTROL_CLEAR_BITS_PACKET_LEN);
#endif
}
#if SI_DEBUG
if (si_debug_flags & SIDBG_DUMP_PRB) {
if (!(is_atapi && (prb->prb_sge0.sge_addr_low == 0))) {
int *ptr;
si_sge_t *tmpsgep;
int j;
ptr = (int *)(void *)prb;
cmn_err(CE_WARN, "si_deliver_satpkt prb: ");
for (j = 0; j < (sizeof (si_prb_t)/4); j++) {
cmn_err(CE_WARN, "%x ", ptr[j]);
}
cmn_err(CE_WARN,
"si_deliver_satpkt sgt: low, high, count link");
for (j = 0,
tmpsgep = (si_sge_t *)
&si_portp->siport_sgbpool[slot * si_dma_sg_number];
j < (sizeof (si_sgblock_t)/ sizeof (si_sge_t))
*si_dma_sg_number;
j++, tmpsgep++) {
ptr = (int *)(void *)tmpsgep;
cmn_err(CE_WARN, "%x %x %x %x",
ptr[0],
ptr[1],
ptr[2],
ptr[3]);
if (IS_SGE_TRM_SET((*tmpsgep))) {
break;
}
}
}
}
#endif
POST_PRB_ADDR(si_ctlp, si_portp, port, slot);
return (slot);
}
static int
si_initialize_controller(si_ctl_state_t *si_ctlp)
{
uint32_t port_status;
uint32_t SStatus;
uint32_t SControl;
uint8_t port;
int loop_count = 0;
si_port_state_t *si_portp;
SIDBG_C(SIDBG_INIT, si_ctlp,
"si3124: si_initialize_controller entered", NULL);
mutex_enter(&si_ctlp->sictl_mutex);
ddi_put32(si_ctlp->sictl_global_acc_handle,
(uint32_t *)GLOBAL_CONTROL_REG(si_ctlp),
GLOBAL_CONTROL_REG_BITS_CLEAR);
for (port = 0; port < si_ctlp->sictl_num_ports; port++) {
if (si_ctlp->sictl_flags & SI_ATTACH) {
if (si_alloc_port_state(si_ctlp, port)) {
mutex_exit(&si_ctlp->sictl_mutex);
return (SI_FAILURE);
}
}
si_portp = si_ctlp->sictl_ports[port];
mutex_enter(&si_portp->siport_mutex);
si_portp->siport_ctlp = si_ctlp;
si_portp->siport_port_num = port;
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_SET(si_ctlp, port),
PORT_CONTROL_SET_BITS_PORT_RESET);
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_CLEAR(si_ctlp, port),
PORT_CONTROL_CLEAR_BITS_PORT_RESET);
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_INTERRUPT_ENABLE_SET(si_ctlp, port),
(INTR_COMMAND_COMPLETE |
INTR_COMMAND_ERROR |
INTR_PORT_READY |
INTR_POWER_CHANGE |
INTR_PHYRDY_CHANGE |
INTR_COMWAKE_RECEIVED |
INTR_UNRECOG_FIS |
INTR_DEV_XCHANGED |
INTR_SETDEVBITS_NOTIFY));
si_enable_port_interrupts(si_ctlp, port);
SControl = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_SCONTROL(si_ctlp, port));
SCONTROL_SET_DET(SControl, SCONTROL_DET_COMRESET);
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SCONTROL(si_ctlp, port)),
SControl);
#ifndef __lock_lint
delay(SI_10MS_TICKS);
#endif
SControl = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_SCONTROL(si_ctlp, port));
SCONTROL_SET_DET(SControl, SCONTROL_DET_NOACTION);
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SCONTROL(si_ctlp, port)),
SControl);
loop_count = 0;
do {
SStatus = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_SSTATUS(si_ctlp, port));
if (SSTATUS_GET_IPM(SStatus) !=
SSTATUS_IPM_INTERFACE_ACTIVE) {
SSTATUS_SET_DET(SStatus,
SSTATUS_DET_NODEV_NOPHY);
}
if (loop_count++ > SI_POLLRATE_SSTATUS) {
break;
}
#ifndef __lock_lint
delay(SI_10MS_TICKS);
#endif
} while (SSTATUS_GET_DET(SStatus) !=
SSTATUS_DET_DEVPRESENT_PHYONLINE);
SIDBG_P(SIDBG_POLL_LOOP, si_portp,
"si_initialize_controller: 1st loop count: %d, "
"SStatus: 0x%x",
loop_count,
SStatus);
if ((SSTATUS_GET_IPM(SStatus) !=
SSTATUS_IPM_INTERFACE_ACTIVE) ||
(SSTATUS_GET_DET(SStatus) !=
SSTATUS_DET_DEVPRESENT_PHYONLINE)) {
si_ctlp->sictl_ports[port]->siport_port_type =
PORT_TYPE_NODEV;
mutex_exit(&si_portp->siport_mutex);
continue;
}
loop_count = 0;
do {
port_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_STATUS(si_ctlp, port));
if (loop_count++ > SI_POLLRATE_PORTREADY) {
break;
}
#ifndef __lock_lint
delay(SI_10MS_TICKS);
#endif
} while (!(port_status & PORT_STATUS_BITS_PORT_READY));
SIDBG_P(SIDBG_POLL_LOOP, si_portp,
"si_initialize_controller: 2nd loop count: %d",
loop_count);
if (si_ctlp->sictl_flags & SI_ATTACH) {
if (port_status & PORT_STATUS_BITS_PORT_READY) {
mutex_exit(&si_portp->siport_mutex);
si_find_dev_signature(si_ctlp, si_portp, port,
PORTMULT_CONTROL_PORT);
mutex_enter(&si_portp->siport_mutex);
} else {
si_ctlp->sictl_ports[port]->siport_port_type =
PORT_TYPE_NODEV;
}
}
if (si_check_ctl_handles(si_ctlp) != DDI_SUCCESS ||
si_check_port_handles(si_portp) != DDI_SUCCESS) {
ddi_fm_service_impact(si_ctlp->sictl_devinfop,
DDI_SERVICE_LOST);
mutex_exit(&si_portp->siport_mutex);
mutex_exit(&si_ctlp->sictl_mutex);
return (SI_FAILURE);
}
mutex_exit(&si_portp->siport_mutex);
}
mutex_exit(&si_ctlp->sictl_mutex);
return (SI_SUCCESS);
}
static void
si_deinitialize_controller(si_ctl_state_t *si_ctlp)
{
int port;
_NOTE(ASSUMING_PROTECTED(si_ctlp))
SIDBG_C(SIDBG_INIT, si_ctlp,
"si3124: si_deinitialize_controller entered", NULL);
si_disable_all_interrupts(si_ctlp);
if (si_ctlp->sictl_flags & SI_DETACH) {
for (port = 0; port < si_ctlp->sictl_num_ports; port++) {
si_dealloc_port_state(si_ctlp, port);
}
}
}
static void
si_init_port(si_ctl_state_t *si_ctlp, int port)
{
SIDBG_C(SIDBG_INIT, si_ctlp,
"si_init_port entered: port: 0x%x",
port);
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_SET(si_ctlp, port),
PORT_CONTROL_SET_BITS_PORT_INITIALIZE);
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_CLEAR(si_ctlp, port),
PORT_CONTROL_CLEAR_BITS_INTR_NCoR);
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_INTERRUPT_STATUS(si_ctlp, port)),
INTR_MASK);
}
static int
si_enumerate_port_multiplier(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
uint32_t num_dev_ports = 0;
int pmport;
uint32_t SControl = 0;
uint32_t SStatus = 0;
uint32_t SError = 0;
int loop_count = 0;
SIDBG_P(SIDBG_INIT, si_portp,
"si_enumerate_port_multiplier entered: port: %d",
port);
mutex_enter(&si_portp->siport_mutex);
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_SET(si_ctlp, port),
PORT_CONTROL_SET_BITS_PM_ENABLE);
if (si_read_portmult_reg(si_ctlp, si_portp, port, PORTMULT_CONTROL_PORT,
PSCR_REG2, &num_dev_ports)) {
mutex_exit(&si_portp->siport_mutex);
return (SI_FAILURE);
}
si_portp->siport_portmult_state.sipm_num_ports = num_dev_ports;
SIDBG_P(SIDBG_INIT, si_portp,
"si_enumerate_port_multiplier: ports found: %d",
num_dev_ports);
for (pmport = 0; pmport < num_dev_ports-1; pmport++) {
if (si_read_portmult_reg(si_ctlp, si_portp, port, pmport,
PSCR_REG2, &SControl)) {
continue;
}
SCONTROL_SET_DET(SControl, SCONTROL_DET_COMRESET);
if (si_write_portmult_reg(si_ctlp, si_portp, port, pmport,
PSCR_REG2, SControl)) {
continue;
}
#ifndef __lock_lint
delay(SI_10MS_TICKS);
#endif
SCONTROL_SET_DET(SControl, SCONTROL_DET_NOACTION);
if (si_write_portmult_reg(si_ctlp, si_portp, port, pmport,
PSCR_REG2, SControl)) {
continue;
}
loop_count = 0;
do {
if (si_read_portmult_reg(si_ctlp, si_portp, port,
pmport, PSCR_REG0, &SStatus)) {
break;
}
SIDBG_P(SIDBG_POLL_LOOP, si_portp,
"looping for PHYRDY: SStatus: %x",
SStatus);
if (SSTATUS_GET_IPM(SStatus) !=
SSTATUS_IPM_INTERFACE_ACTIVE) {
SSTATUS_SET_DET(SStatus,
SSTATUS_DET_NODEV_NOPHY);
}
if (loop_count++ > SI_POLLRATE_SSTATUS) {
break;
}
#ifndef __lock_lint
delay(SI_10MS_TICKS);
#endif
} while (SSTATUS_GET_DET(SStatus) !=
SSTATUS_DET_DEVPRESENT_PHYONLINE);
SIDBG_P(SIDBG_POLL_LOOP, si_portp,
"si_enumerate_port_multiplier: "
"loop count: %d, SStatus: 0x%x",
loop_count,
SStatus);
if ((SSTATUS_GET_IPM(SStatus) ==
SSTATUS_IPM_INTERFACE_ACTIVE) &&
(SSTATUS_GET_DET(SStatus) ==
SSTATUS_DET_DEVPRESENT_PHYONLINE)) {
SIDBG_P(SIDBG_INIT, si_portp,
"Status: %x, device exists",
SStatus);
if (si_read_portmult_reg(si_ctlp, si_portp, port,
pmport, PSCR_REG1, &SError)) {
continue;
}
SIDBG_P(SIDBG_INIT, si_portp,
"SError bits are: %x", SError);
if (si_write_portmult_reg(si_ctlp, si_portp, port,
pmport, PSCR_REG1, SError)) {
continue;
}
mutex_exit(&si_portp->siport_mutex);
si_find_dev_signature(si_ctlp, si_portp, port, pmport);
mutex_enter(&si_portp->siport_mutex);
}
}
mutex_exit(&si_portp->siport_mutex);
return (SI_SUCCESS);
}
static int
si_read_portmult_reg(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port,
int pmport,
int regnum,
uint32_t *regval)
{
int slot;
si_prb_t *prb;
uint32_t *prb_word_ptr;
int i;
uint32_t slot_status;
int loop_count = 0;
_NOTE(ASSUMING_PROTECTED(si_portp))
SIDBG_P(SIDBG_ENTRY, si_portp, "si_read_portmult_reg: port: %x,"
"pmport: %x, regnum: %x",
port, pmport, regnum);
slot = si_claim_free_slot(si_ctlp, si_portp, port);
if (slot == SI_FAILURE) {
return (SI_FAILURE);
}
prb = &(si_portp->siport_prbpool[slot]);
bzero((void *)prb, sizeof (si_prb_t));
SET_FIS_TYPE(prb->prb_fis, REGISTER_FIS_H2D);
SET_FIS_PMP(prb->prb_fis, PORTMULT_CONTROL_PORT);
SET_FIS_CDMDEVCTL(prb->prb_fis, 1);
SET_FIS_COMMAND(prb->prb_fis, SATAC_READ_PM_REG);
SET_FIS_DEV_HEAD(prb->prb_fis, pmport);
SET_FIS_FEATURES(prb->prb_fis, regnum);
SET_SGE_TRM(prb->prb_sge0);
#if SI_DEBUG
if (si_debug_flags & SIDBG_DUMP_PRB) {
int *ptr;
int j;
ptr = (int *)(void *)prb;
cmn_err(CE_WARN, "read_port_mult_reg, prb: ");
for (j = 0; j < (sizeof (si_prb_t)/4); j++) {
cmn_err(CE_WARN, "%x ", ptr[j]);
}
}
#endif
POST_PRB_ADDR(si_ctlp, si_portp, port, slot);
do {
slot_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SLOT_STATUS(si_ctlp, port)));
SIDBG_P(SIDBG_POLL_LOOP, si_portp,
"looping read_pm slot_status: 0x%x",
slot_status);
if (loop_count++ > SI_POLLRATE_SLOTSTATUS) {
break;
}
#ifndef __lock_lint
delay(SI_10MS_TICKS);
#endif
} while (slot_status & SI_SLOT_MASK & (0x1 << slot));
SIDBG_P(SIDBG_POLL_LOOP, si_portp,
"read_portmult_reg: loop count: %d",
loop_count);
CLEAR_BIT(si_portp->siport_pending_tags, slot);
prb_word_ptr = (uint32_t *)(void *)prb;
for (i = 0; i < (sizeof (si_prb_t)/4); i++) {
prb_word_ptr[i] = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_LRAM(si_ctlp, port, slot)+i*4));
}
if (si_check_ctl_handles(si_ctlp) != DDI_SUCCESS ||
si_check_port_handles(si_portp) != DDI_SUCCESS) {
ddi_fm_service_impact(si_ctlp->sictl_devinfop,
DDI_SERVICE_UNAFFECTED);
return (SI_FAILURE);
}
if (((GET_FIS_COMMAND(prb->prb_fis) & 0x1) != 0) ||
(GET_FIS_FEATURES(prb->prb_fis) != 0)) {
return (SI_FAILURE);
}
*regval = (GET_FIS_SECTOR_COUNT(prb->prb_fis) & 0xff) |
((GET_FIS_SECTOR(prb->prb_fis) << 8) & 0xff00) |
((GET_FIS_CYL_LOW(prb->prb_fis) << 16) & 0xff0000) |
((GET_FIS_CYL_HI(prb->prb_fis) << 24) & 0xff000000);
return (SI_SUCCESS);
}
static int
si_write_portmult_reg(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port,
int pmport,
int regnum,
uint32_t regval)
{
int slot;
si_prb_t *prb;
uint32_t *prb_word_ptr;
uint32_t slot_status;
int i;
int loop_count = 0;
_NOTE(ASSUMING_PROTECTED(si_portp))
SIDBG_P(SIDBG_ENTRY, si_portp,
"si_write_portmult_reg: port: %x, pmport: %x,"
"regnum: %x, regval: %x",
port, pmport, regnum, regval);
slot = si_claim_free_slot(si_ctlp, si_portp, port);
if (slot == SI_FAILURE) {
return (SI_FAILURE);
}
prb = &(si_portp->siport_prbpool[slot]);
bzero((void *)prb, sizeof (si_prb_t));
SET_FIS_TYPE(prb->prb_fis, REGISTER_FIS_H2D);
SET_FIS_PMP(prb->prb_fis, PORTMULT_CONTROL_PORT);
SET_FIS_CDMDEVCTL(prb->prb_fis, 1);
SET_FIS_COMMAND(prb->prb_fis, SATAC_WRITE_PM_REG);
SET_FIS_DEV_HEAD(prb->prb_fis, pmport);
SET_FIS_FEATURES(prb->prb_fis, regnum);
SET_FIS_SECTOR_COUNT(prb->prb_fis, regval & 0xff);
SET_FIS_SECTOR(prb->prb_fis, (regval >> 8) & 0xff);
SET_FIS_CYL_LOW(prb->prb_fis, (regval >> 16) & 0xff);
SET_FIS_CYL_HI(prb->prb_fis, (regval >> 24) & 0xff);
SET_SGE_TRM(prb->prb_sge0);
#if SI_DEBUG
if (si_debug_flags & SIDBG_DUMP_PRB) {
int *ptr;
int j;
ptr = (int *)(void *)prb;
cmn_err(CE_WARN, "read_port_mult_reg, prb: ");
for (j = 0; j < (sizeof (si_prb_t)/4); j++) {
cmn_err(CE_WARN, "%x ", ptr[j]);
}
}
#endif
POST_PRB_ADDR(si_ctlp, si_portp, port, slot);
do {
slot_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SLOT_STATUS(si_ctlp, port)));
SIDBG_P(SIDBG_POLL_LOOP, si_portp,
"looping write_pmp slot_status: 0x%x",
slot_status);
if (loop_count++ > SI_POLLRATE_SLOTSTATUS) {
break;
}
#ifndef __lock_lint
delay(SI_10MS_TICKS);
#endif
} while (slot_status & SI_SLOT_MASK & (0x1 << slot));
SIDBG_P(SIDBG_POLL_LOOP, si_portp,
"write_portmult_reg: loop count: %d",
loop_count);
CLEAR_BIT(si_portp->siport_pending_tags, slot);
prb_word_ptr = (uint32_t *)(void *)prb;
for (i = 0; i < (sizeof (si_prb_t)/4); i++) {
prb_word_ptr[i] = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_LRAM(si_ctlp, port, slot)+i*4));
}
if (si_check_ctl_handles(si_ctlp) != DDI_SUCCESS ||
si_check_port_handles(si_portp) != DDI_SUCCESS) {
ddi_fm_service_impact(si_ctlp->sictl_devinfop,
DDI_SERVICE_UNAFFECTED);
return (SI_FAILURE);
}
if (((GET_FIS_COMMAND(prb->prb_fis) & 0x1) != 0) ||
(GET_FIS_FEATURES(prb->prb_fis) != 0)) {
return (SI_FAILURE);
}
return (SI_SUCCESS);
}
static void
si_set_sense_data(sata_pkt_t *satapkt, int reason)
{
struct scsi_extended_sense *sense;
sense = (struct scsi_extended_sense *)
satapkt->satapkt_cmd.satacmd_rqsense;
bzero(sense, sizeof (struct scsi_extended_sense));
sense->es_valid = 1;
sense->es_class = 7;
sense->es_key = 0;
sense->es_info_1 = 0;
sense->es_info_2 = 0;
sense->es_info_3 = 0;
sense->es_info_4 = 0;
sense->es_add_len = 6;
sense->es_cmd_info[0] = 0;
sense->es_cmd_info[1] = 0;
sense->es_cmd_info[2] = 0;
sense->es_cmd_info[3] = 0;
sense->es_add_code = 0;
sense->es_qual_code = 0;
if ((reason == SATA_PKT_DEV_ERROR) || (reason == SATA_PKT_TIMEOUT)) {
sense->es_key = KEY_HARDWARE_ERROR;
}
}
static uint_t
si_intr(caddr_t arg1, caddr_t arg2)
{
si_ctl_state_t *si_ctlp = (si_ctl_state_t *)(void *)arg1;
si_port_state_t *si_portp;
uint32_t global_intr_status;
uint32_t mask, port_intr_status;
int port;
global_intr_status = ddi_get32(si_ctlp->sictl_global_acc_handle,
(uint32_t *)GLOBAL_INTERRUPT_STATUS(si_ctlp));
SIDBG_C(SIDBG_INTR, si_ctlp,
"si_intr: global_int_status: 0x%x",
global_intr_status);
if (si_check_acc_handle(si_ctlp->sictl_global_acc_handle) !=
DDI_SUCCESS) {
ddi_fm_service_impact(si_ctlp->sictl_devinfop,
DDI_SERVICE_UNAFFECTED);
return (DDI_INTR_UNCLAIMED);
}
if (!(global_intr_status & SI31xx_INTR_PORT_MASK)) {
return (DDI_INTR_UNCLAIMED);
}
for (port = 0; port < si_ctlp->sictl_num_ports; port++) {
mask = 0x1 << port;
if (!(global_intr_status & mask)) {
continue;
}
mutex_enter(&si_ctlp->sictl_mutex);
si_portp = si_ctlp->sictl_ports[port];
mutex_exit(&si_ctlp->sictl_mutex);
port_intr_status = ddi_get32(si_ctlp->sictl_global_acc_handle,
(uint32_t *)PORT_INTERRUPT_STATUS(si_ctlp, port));
SIDBG_P(SIDBG_VERBOSE, si_portp,
"s_intr: port_intr_status: 0x%x, port: %x",
port_intr_status,
port);
if (port_intr_status & INTR_COMMAND_COMPLETE) {
(void) si_intr_command_complete(si_ctlp, si_portp,
port);
mutex_enter(&si_portp->siport_mutex);
if (si_check_ctl_handles(si_ctlp) != DDI_SUCCESS ||
si_check_port_handles(si_portp) != DDI_SUCCESS) {
ddi_fm_service_impact(si_ctlp->sictl_devinfop,
DDI_SERVICE_UNAFFECTED);
si_schedule_port_initialize(si_ctlp, si_portp,
port);
}
mutex_exit(&si_portp->siport_mutex);
} else {
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_INTERRUPT_STATUS(si_ctlp, port)),
port_intr_status & INTR_MASK);
}
if (port_intr_status & INTR_COMMAND_ERROR) {
si_schedule_intr_command_error(si_ctlp, si_portp, port);
}
if (port_intr_status & INTR_PORT_READY) {
(void) si_intr_port_ready(si_ctlp, si_portp, port);
}
if (port_intr_status & INTR_POWER_CHANGE) {
(void) si_intr_pwr_change(si_ctlp, si_portp, port);
}
if (port_intr_status & INTR_PHYRDY_CHANGE) {
(void) si_intr_phy_ready_change(si_ctlp, si_portp,
port);
}
if (port_intr_status & INTR_COMWAKE_RECEIVED) {
(void) si_intr_comwake_rcvd(si_ctlp, si_portp,
port);
}
if (port_intr_status & INTR_UNRECOG_FIS) {
(void) si_intr_unrecognised_fis(si_ctlp, si_portp,
port);
}
if (port_intr_status & INTR_DEV_XCHANGED) {
(void) si_intr_dev_xchanged(si_ctlp, si_portp, port);
}
if (port_intr_status & INTR_8B10B_DECODE_ERROR) {
(void) si_intr_decode_err_threshold(si_ctlp, si_portp,
port);
}
if (port_intr_status & INTR_CRC_ERROR) {
(void) si_intr_crc_err_threshold(si_ctlp, si_portp,
port);
}
if (port_intr_status & INTR_HANDSHAKE_ERROR) {
(void) si_intr_handshake_err_threshold(si_ctlp,
si_portp, port);
}
if (port_intr_status & INTR_SETDEVBITS_NOTIFY) {
(void) si_intr_set_devbits_notify(si_ctlp, si_portp,
port);
}
}
return (DDI_INTR_CLAIMED);
}
static int
si_intr_command_complete(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
uint32_t slot_status;
uint32_t finished_tags;
int finished_slot;
sata_pkt_t *satapkt;
SIDBG_P(SIDBG_INTR, si_portp,
"si_intr_command_complete enter", NULL);
mutex_enter(&si_portp->siport_mutex);
slot_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SLOT_STATUS(si_ctlp, port)));
if (!si_portp->siport_pending_tags) {
mutex_exit(&si_portp->siport_mutex);
return (SI_SUCCESS);
}
SIDBG_P(SIDBG_VERBOSE, si_portp, "si3124: si_intr_command_complete: "
"pending_tags: %x, slot_status: %x",
si_portp->siport_pending_tags,
slot_status);
finished_tags = si_portp->siport_pending_tags &
~slot_status & SI_SLOT_MASK;
while (finished_tags) {
finished_slot = ddi_ffs(finished_tags) - 1;
if (finished_slot == -1) {
break;
}
satapkt = si_portp->siport_slot_pkts[finished_slot];
if (satapkt->satapkt_cmd.satacmd_flags.sata_special_regs) {
si_copy_out_regs(&satapkt->satapkt_cmd, si_ctlp, port,
finished_slot);
}
CLEAR_BIT(si_portp->siport_pending_tags, finished_slot);
CLEAR_BIT(finished_tags, finished_slot);
SENDUP_PACKET(si_portp, satapkt, SATA_PKT_COMPLETED);
}
SIDBG_P(SIDBG_PKTCOMP, si_portp,
"command_complete done: pend_tags: 0x%x, slot_status: 0x%x",
si_portp->siport_pending_tags,
slot_status);
mutex_exit(&si_portp->siport_mutex);
return (SI_SUCCESS);
}
static void
si_schedule_intr_command_error(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
si_event_arg_t *args;
mutex_enter(&si_portp->siport_mutex);
args = si_portp->siport_event_args;
if (args->siea_ctlp != NULL) {
cmn_err(CE_WARN, "si_schedule_intr_command_error: "
"args->si_ctlp != NULL");
mutex_exit(&si_portp->siport_mutex);
return;
}
args->siea_ctlp = si_ctlp;
args->siea_port = port;
(void) timeout(si_do_intr_command_error, si_portp, 1);
mutex_exit(&si_portp->siport_mutex);
}
static void
si_do_intr_command_error(void *arg)
{
si_event_arg_t *args;
si_ctl_state_t *si_ctlp;
si_port_state_t *si_portp;
int port;
si_portp = arg;
mutex_enter(&si_portp->siport_mutex);
args = si_portp->siport_event_args;
si_ctlp = args->siea_ctlp;
port = args->siea_port;
args->siea_ctlp = NULL;
mutex_exit(&si_portp->siport_mutex);
(void) si_intr_command_error(si_ctlp, si_portp, port);
}
static int
si_intr_command_error(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
uint32_t command_error, slot_status;
uint32_t failed_tags;
command_error = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_COMMAND_ERROR(si_ctlp, port)));
SIDBG_P(SIDBG_ERRS, si_portp,
"si_intr_command_error: command_error: 0x%x",
command_error);
mutex_enter(&si_portp->siport_mutex);
slot_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SLOT_STATUS(si_ctlp, port)));
si_log_error_message(si_ctlp, port, command_error);
switch (command_error) {
case CMD_ERR_DEVICEERRROR:
si_error_recovery_DEVICEERROR(si_ctlp, si_portp, port);
break;
case CMD_ERR_SDBERROR:
si_fm_ereport(si_ctlp, DDI_FM_DEVICE_INTERN_CORR, "SBD error");
si_error_recovery_SDBERROR(si_ctlp, si_portp, port);
ddi_fm_service_impact(si_ctlp->sictl_devinfop,
DDI_SERVICE_UNAFFECTED);
break;
case CMD_ERR_DATAFISERROR:
si_fm_ereport(si_ctlp, DDI_FM_DEVICE_INTERN_CORR,
"Data FIS error");
si_error_recovery_DATAFISERROR(si_ctlp, si_portp, port);
ddi_fm_service_impact(si_ctlp->sictl_devinfop,
DDI_SERVICE_UNAFFECTED);
break;
case CMD_ERR_SENDFISERROR:
si_fm_ereport(si_ctlp, DDI_FM_DEVICE_INTERN_CORR,
"Send FIS error");
si_error_recovery_SENDFISERROR(si_ctlp, si_portp, port);
ddi_fm_service_impact(si_ctlp->sictl_devinfop,
DDI_SERVICE_UNAFFECTED);
break;
default:
si_fm_ereport(si_ctlp, DDI_FM_DEVICE_INTERN_CORR,
"Unknown error");
si_error_recovery_default(si_ctlp, si_portp, port);
ddi_fm_service_impact(si_ctlp->sictl_devinfop,
DDI_SERVICE_UNAFFECTED);
break;
}
failed_tags = si_portp->siport_pending_tags &
(si_portp->siport_err_tags_SDBERROR |
si_portp->siport_err_tags_nonSDBERROR);
SIDBG_P(SIDBG_ERRS, si_portp, "si_intr_command_error: "
"err_tags_SDBERROR: 0x%x, "
"err_tags_nonSDBERRROR: 0x%x, "
"failed_tags: 0x%x",
si_portp->siport_err_tags_SDBERROR,
si_portp->siport_err_tags_nonSDBERROR,
failed_tags);
SIDBG_P(SIDBG_ERRS, si_portp,
"si3124: si_intr_command_error: "
"slot_status:0x%x, pending_tags: 0x%x",
slot_status,
si_portp->siport_pending_tags);
si_portp->mopping_in_progress++;
si_mop_commands(si_ctlp,
si_portp,
port,
slot_status,
failed_tags,
0,
0,
0);
ASSERT(si_portp->siport_pending_tags == 0);
si_portp->siport_err_tags_SDBERROR = 0;
si_portp->siport_err_tags_nonSDBERROR = 0;
mutex_exit(&si_portp->siport_mutex);
return (SI_SUCCESS);
}
static void
si_recover_portmult_errors(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
uint32_t command_error, slot_status, port_status;
int failed_slot;
int loop_count = 0;
_NOTE(ASSUMING_PROTECTED(si_portp))
SIDBG_P(SIDBG_ERRS, si_portp,
"si_recover_portmult_errors: port: 0x%x",
port);
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_SET(si_ctlp, port),
PORT_CONTROL_SET_BITS_RESUME);
port_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_STATUS(si_ctlp, port));
failed_slot = (port_status >> 16) & SI_NUM_SLOTS;
command_error = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_COMMAND_ERROR(si_ctlp, port)));
if (command_error == CMD_ERR_SDBERROR) {
si_portp->siport_err_tags_SDBERROR |= (0x1 << failed_slot);
} else {
si_portp->siport_err_tags_nonSDBERROR |= (0x1 << failed_slot);
}
do {
slot_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SLOT_STATUS(si_ctlp, port)));
if (IS_ATTENTION_RAISED(slot_status)) {
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_SET(si_ctlp, port),
PORT_CONTROL_SET_BITS_RESUME);
port_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_STATUS(si_ctlp, port));
failed_slot = (port_status >> 16) & SI_NUM_SLOTS;
command_error = ddi_get32(
si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_COMMAND_ERROR(si_ctlp,
port)));
if (command_error == CMD_ERR_SDBERROR) {
si_portp->siport_err_tags_SDBERROR |=
(0x1 << failed_slot);
} else {
si_portp->siport_err_tags_nonSDBERROR |=
(0x1 << failed_slot);
}
}
if (loop_count++ > SI_POLLRATE_RECOVERPORTMULT) {
break;
}
#ifndef __lock_lint
delay(SI_10MS_TICKS);
#endif
} while (slot_status & SI_SLOT_MASK);
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_CLEAR(si_ctlp, port),
PORT_CONTROL_CLEAR_BITS_RESUME);
}
static void
si_error_recovery_DEVICEERROR(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
uint32_t port_status;
int failed_slot;
_NOTE(ASSUMING_PROTECTED(si_portp))
SIDBG_P(SIDBG_ERRS, si_portp,
"si_error_recovery_DEVICEERROR: port: 0x%x",
port);
if (si_portp->siport_port_type == PORT_TYPE_MULTIPLIER) {
si_recover_portmult_errors(si_ctlp, si_portp, port);
} else {
port_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_STATUS(si_ctlp, port));
failed_slot = (port_status >> 16) & SI_NUM_SLOTS;
si_portp->siport_err_tags_nonSDBERROR |= (0x1 << failed_slot);
}
(void) si_initialize_port_wait_till_ready(si_ctlp, port);
}
static void
si_error_recovery_SDBERROR(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
uint32_t port_status;
int failed_slot;
_NOTE(ASSUMING_PROTECTED(si_portp))
SIDBG_P(SIDBG_ERRS, si_portp,
"si3124: si_error_recovery_SDBERROR: port: 0x%x",
port);
if (si_portp->siport_port_type == PORT_TYPE_MULTIPLIER) {
si_recover_portmult_errors(si_ctlp, si_portp, port);
} else {
port_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_STATUS(si_ctlp, port));
failed_slot = (port_status >> 16) & SI_NUM_SLOTS;
si_portp->siport_err_tags_SDBERROR |= (0x1 << failed_slot);
}
(void) si_initialize_port_wait_till_ready(si_ctlp, port);
}
static void
si_error_recovery_DATAFISERROR(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
uint32_t port_status;
int failed_slot;
_NOTE(ASSUMING_PROTECTED(si_portp))
SIDBG_P(SIDBG_ERRS, si_portp,
"si3124: si_error_recovery_DATAFISERROR: port: 0x%x",
port);
if (si_portp->siport_pending_ncq_count) {
port_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_STATUS(si_ctlp, port));
failed_slot = (port_status >> 16) & SI_NUM_SLOTS;
si_portp->siport_err_tags_nonSDBERROR |= (0x1 << failed_slot);
(void) si_reset_dport_wait_till_ready(si_ctlp, si_portp, port,
SI_DEVICE_RESET);
return;
}
si_error_recovery_DEVICEERROR(si_ctlp, si_portp, port);
}
static void
si_error_recovery_SENDFISERROR(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
uint32_t port_status;
int failed_slot;
_NOTE(ASSUMING_PROTECTED(si_portp))
SIDBG_P(SIDBG_ERRS, si_portp,
"si3124: si_error_recovery_SENDFISERROR: port: 0x%x",
port);
if (si_portp->siport_port_type == PORT_TYPE_MULTIPLIER) {
si_recover_portmult_errors(si_ctlp, si_portp, port);
} else {
port_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_STATUS(si_ctlp, port));
failed_slot = (port_status >> 16) & SI_NUM_SLOTS;
si_portp->siport_err_tags_nonSDBERROR |= (0x1 << failed_slot);
(void) si_reset_dport_wait_till_ready(si_ctlp, si_portp, port,
SI_DEVICE_RESET);
}
}
static void
si_error_recovery_default(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
uint32_t port_status;
int failed_slot;
_NOTE(ASSUMING_PROTECTED(si_portp))
SIDBG_P(SIDBG_ERRS, si_portp,
"si3124: si_error_recovery_default: port: 0x%x",
port);
port_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_STATUS(si_ctlp, port));
failed_slot = (port_status >> 16) & SI_NUM_SLOTS;
si_portp->siport_err_tags_nonSDBERROR |= (0x1 << failed_slot);
(void) si_reset_dport_wait_till_ready(si_ctlp, si_portp, port,
SI_DEVICE_RESET);
}
static uint8_t
si_read_log_ext(si_ctl_state_t *si_ctlp, si_port_state_t *si_portp, int port)
{
int slot;
si_prb_t *prb;
int i;
uint32_t slot_status;
int loop_count = 0;
uint32_t *prb_word_ptr;
uint8_t error;
_NOTE(ASSUMING_PROTECTED(si_portp))
SIDBG_P(SIDBG_ERRS, si_portp,
"si_read_log_ext: port: %x", port);
slot = si_claim_free_slot(si_ctlp, si_portp, port);
if (slot == SI_FAILURE) {
return (0);
}
prb = &(si_portp->siport_prbpool[slot]);
bzero((void *)prb, sizeof (si_prb_t));
SET_FIS_TYPE(prb->prb_fis, REGISTER_FIS_H2D);
SET_FIS_PMP(prb->prb_fis, PORTMULT_CONTROL_PORT);
SET_FIS_CDMDEVCTL(prb->prb_fis, 1);
SET_FIS_COMMAND(prb->prb_fis, SATAC_READ_LOG_EXT);
SET_FIS_SECTOR(prb->prb_fis, SATA_LOG_PAGE_10);
SET_SGE_TRM(prb->prb_sge0);
#if SI_DEBUG
if (si_debug_flags & SIDBG_DUMP_PRB) {
int *ptr;
int j;
ptr = (int *)(void *)prb;
cmn_err(CE_WARN, "read_port_mult_reg, prb: ");
for (j = 0; j < (sizeof (si_prb_t)/4); j++) {
cmn_err(CE_WARN, "%x ", ptr[j]);
}
}
#endif
POST_PRB_ADDR(si_ctlp, si_portp, port, slot);
do {
slot_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SLOT_STATUS(si_ctlp, port)));
SIDBG_P(SIDBG_POLL_LOOP, si_portp,
"looping read_log_ext slot_status: 0x%x",
slot_status);
if (loop_count++ > SI_POLLRATE_SLOTSTATUS) {
break;
}
#ifndef __lock_lint
delay(SI_10MS_TICKS);
#endif
} while (slot_status & SI_SLOT_MASK & (0x1 << slot));
if (slot_status & SI_SLOT_MASK & (0x1 << slot)) {
(void) si_initialize_port_wait_till_ready(si_ctlp, port);
}
SIDBG_P(SIDBG_POLL_LOOP, si_portp,
"read_portmult_reg: loop count: %d",
loop_count);
prb_word_ptr = (uint32_t *)(void *)prb;
for (i = 0; i < (sizeof (si_prb_t)/4); i++) {
prb_word_ptr[i] = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_LRAM(si_ctlp, port, slot)+i*4));
}
if (si_check_ctl_handles(si_ctlp) != DDI_SUCCESS ||
si_check_port_handles(si_portp) != DDI_SUCCESS) {
ddi_fm_service_impact(si_ctlp->sictl_devinfop,
DDI_SERVICE_UNAFFECTED);
}
error = GET_FIS_FEATURES(prb->prb_fis);
CLEAR_BIT(si_portp->siport_pending_tags, slot);
return (error);
}
static void
si_log_error_message(si_ctl_state_t *si_ctlp, int port, uint32_t command_error)
{
#if SI_DEBUG
#ifndef __lock_lint
_NOTE(ARGUNUSED(si_ctlp))
_NOTE(ARGUNUSED(port))
#endif
char *errstr;
si_port_state_t *si_portp = si_ctlp->sictl_ports[port];
switch (command_error) {
case CMD_ERR_DEVICEERRROR:
errstr = "Standard Error: Error bit set in register - device"
" to host FIS";
break;
case CMD_ERR_SDBERROR:
errstr = "NCQ Error: Error bit set in register - device"
" to host FIS";
break;
case CMD_ERR_DATAFISERROR:
errstr = "Error in data FIS not detected by device";
break;
case CMD_ERR_SENDFISERROR:
errstr = "Initial command FIS transmission failed";
break;
case CMD_ERR_INCONSISTENTSTATE:
errstr = "Inconsistency in protocol";
break;
case CMD_ERR_DIRECTIONERROR:
errstr = "DMA direction flag does not match the command";
break;
case CMD_ERR_UNDERRUNERROR:
errstr = "Run out of scatter gather entries while writing data";
break;
case CMD_ERR_OVERRUNERROR:
errstr = "Run out of scatter gather entries while reading data";
break;
case CMD_ERR_PACKETPROTOCOLERROR:
errstr = "Packet protocol error";
break;
case CMD_ERR_PLDSGTERRORBOUNDARY:
errstr = "Scatter/gather table not on quadword boundary";
break;
case CMD_ERR_PLDSGTERRORTARETABORT:
errstr = "PCI(X) Target abort while fetching scatter/gather"
" table";
break;
case CMD_ERR_PLDSGTERRORMASTERABORT:
errstr = "PCI(X) Master abort while fetching scatter/gather"
" table";
break;
case CMD_ERR_PLDSGTERRORPCIERR:
errstr = "PCI(X) parity error while fetching scatter/gather"
" table";
break;
case CMD_ERR_PLDCMDERRORBOUNDARY:
errstr = "PRB not on quadword boundary";
break;
case CMD_ERR_PLDCMDERRORTARGETABORT:
errstr = "PCI(X) Target abort while fetching PRB";
break;
case CMD_ERR_PLDCMDERRORMASTERABORT:
errstr = "PCI(X) Master abort while fetching PRB";
break;
case CMD_ERR_PLDCMDERORPCIERR:
errstr = "PCI(X) parity error while fetching PRB";
break;
case CMD_ERR_PSDERRORTARGETABORT:
errstr = "PCI(X) Target abort during data transfer";
break;
case CMD_ERR_PSDERRORMASTERABORT:
errstr = "PCI(X) Master abort during data transfer";
break;
case CMD_ERR_PSDERRORPCIERR:
errstr = "PCI(X) parity error during data transfer";
break;
case CMD_ERR_SENDSERVICEERROR:
errstr = "FIS received while sending service FIS in"
" legacy queuing operation";
break;
default:
errstr = "Unknown Error";
break;
}
SIDBG_P(SIDBG_ERRS, si_portp,
"command error: error: %s",
errstr);
#else
#ifndef __lock_lint
_NOTE(ARGUNUSED(si_ctlp))
_NOTE(ARGUNUSED(port))
_NOTE(ARGUNUSED(command_error))
#endif
#endif
}
static int
si_intr_port_ready(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
SIDBG_P(SIDBG_INTR, si_portp, "si_intr_ready", NULL);
return (SI_SUCCESS);
}
static int
si_intr_pwr_change(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
SIDBG_P(SIDBG_INTR, si_portp, "si_intr_pwr_change", NULL);
return (SI_SUCCESS);
}
static int
si_intr_phy_ready_change(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
sata_device_t sdevice;
uint32_t SStatus = 0;
int dev_exists_now = 0;
int dev_existed_previously = 0;
SIDBG_P(SIDBG_INTR, si_portp,
"si_intr_phy_rdy_change", NULL);
mutex_enter(&si_ctlp->sictl_mutex);
if ((si_ctlp->sictl_sata_hba_tran == NULL) || (si_portp == NULL)) {
mutex_exit(&si_ctlp->sictl_mutex);
return (SI_SUCCESS);
}
mutex_exit(&si_ctlp->sictl_mutex);
mutex_enter(&si_portp->siport_mutex);
SStatus = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_SSTATUS(si_ctlp, port));
dev_exists_now =
(SSTATUS_GET_DET(SStatus) == SSTATUS_DET_DEVPRESENT_PHYONLINE);
if (si_portp->siport_port_type != PORT_TYPE_NODEV) {
dev_existed_previously = 1;
}
bzero((void *)&sdevice, sizeof (sata_device_t));
sdevice.satadev_addr.cport = (uint8_t)port;
sdevice.satadev_addr.pmport = PORTMULT_CONTROL_PORT;
if (si_portp->siport_port_type == PORT_TYPE_MULTIPLIER) {
sdevice.satadev_addr.qual = SATA_ADDR_PMPORT;
} else {
sdevice.satadev_addr.qual = SATA_ADDR_CPORT;
}
sdevice.satadev_state = SATA_STATE_READY;
if (dev_exists_now) {
if (dev_existed_previously) {
SIDBG_P(SIDBG_INTR, si_portp,
"phyrdy: doing BOTH EVENTS TOGETHER", NULL);
if (si_portp->siport_active) {
SIDBG_P(SIDBG_EVENT, si_portp,
"sending event: LINK_LOST & "
"LINK_ESTABLISHED", NULL);
sata_hba_event_notify(
si_ctlp->sictl_sata_hba_tran->\
sata_tran_hba_dip,
&sdevice,
SATA_EVNT_LINK_LOST|
SATA_EVNT_LINK_ESTABLISHED);
}
} else {
mutex_exit(&si_portp->siport_mutex);
si_find_dev_signature(si_ctlp, si_portp, port,
PORTMULT_CONTROL_PORT);
mutex_enter(&si_portp->siport_mutex);
SIDBG_P(SIDBG_INTR, si_portp,
"phyrdy: doing ATTACH event", NULL);
if (si_portp->siport_active) {
SIDBG_P(SIDBG_EVENT, si_portp,
"sending event up: LINK_ESTABLISHED", NULL);
sata_hba_event_notify(
si_ctlp->sictl_sata_hba_tran->\
sata_tran_hba_dip,
&sdevice,
SATA_EVNT_LINK_ESTABLISHED);
}
}
} else {
if (dev_existed_previously) {
if (si_portp->siport_active) {
SIDBG_P(SIDBG_EVENT, si_portp,
"sending event up: LINK_LOST", NULL);
sata_hba_event_notify(
si_ctlp->sictl_sata_hba_tran->
sata_tran_hba_dip,
&sdevice,
SATA_EVNT_LINK_LOST);
}
si_portp->siport_port_type = PORT_TYPE_NODEV;
} else {
SIDBG_P(SIDBG_INTR, si_portp,
"spurious phy ready interrupt", NULL);
}
}
mutex_exit(&si_portp->siport_mutex);
return (SI_SUCCESS);
}
static int
si_intr_comwake_rcvd(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
SIDBG_P(SIDBG_INTR, si_portp,
"si_intr_commwake_rcvd", NULL);
return (SI_SUCCESS);
}
static int
si_intr_unrecognised_fis(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
SIDBG_P(SIDBG_INTR, si_portp,
"si_intr_unrecognised_fis", NULL);
return (SI_SUCCESS);
}
static int
si_intr_dev_xchanged(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
SIDBG_P(SIDBG_INTR, si_portp,
"si_intr_dev_xchanged", NULL);
return (SI_SUCCESS);
}
static int
si_intr_decode_err_threshold(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
SIDBG_P(SIDBG_INTR, si_portp,
"si_intr_err_threshold", NULL);
return (SI_SUCCESS);
}
static int
si_intr_crc_err_threshold(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
SIDBG_P(SIDBG_INTR, si_portp,
"si_intr_crc_threshold", NULL);
return (SI_SUCCESS);
}
static int
si_intr_handshake_err_threshold(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
SIDBG_P(SIDBG_INTR, si_portp,
"si_intr_handshake_err_threshold", NULL);
return (SI_SUCCESS);
}
static int
si_intr_set_devbits_notify(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
SIDBG_P(SIDBG_INTR, si_portp,
"si_intr_set_devbits_notify", NULL);
return (SI_SUCCESS);
}
static void
si_enable_port_interrupts(si_ctl_state_t *si_ctlp, int port)
{
uint32_t mask;
si_port_state_t *si_portp = si_ctlp->sictl_ports[port];
mask = ddi_get32(si_ctlp->sictl_global_acc_handle,
(uint32_t *)GLOBAL_CONTROL_REG(si_ctlp));
SIDBG_P(SIDBG_INIT, si_portp,
"si_enable_port_interrupts: current mask: 0x%x",
mask);
SET_BIT(mask, port);
ddi_put32(si_ctlp->sictl_global_acc_handle,
(uint32_t *)GLOBAL_CONTROL_REG(si_ctlp),
mask);
}
static void
si_enable_all_interrupts(si_ctl_state_t *si_ctlp)
{
int port;
for (port = 0; port < si_ctlp->sictl_num_ports; port++) {
si_enable_port_interrupts(si_ctlp, port);
}
}
static void
si_disable_port_interrupts(si_ctl_state_t *si_ctlp, int port)
{
uint32_t mask;
mask = ddi_get32(si_ctlp->sictl_global_acc_handle,
(uint32_t *)GLOBAL_CONTROL_REG(si_ctlp));
CLEAR_BIT(mask, port);
ddi_put32(si_ctlp->sictl_global_acc_handle,
(uint32_t *)GLOBAL_CONTROL_REG(si_ctlp),
mask);
}
static void
si_disable_all_interrupts(si_ctl_state_t *si_ctlp)
{
int port;
for (port = 0; port < si_ctlp->sictl_num_ports; port++) {
si_disable_port_interrupts(si_ctlp, port);
}
}
static void
fill_dev_sregisters(si_ctl_state_t *si_ctlp, int port, sata_device_t *satadev)
{
satadev->satadev_scr.sstatus = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SSTATUS(si_ctlp, port)));
satadev->satadev_scr.serror = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SERROR(si_ctlp, port)));
satadev->satadev_scr.sactive = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SACTIVE(si_ctlp, port)));
satadev->satadev_scr.scontrol =
ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SCONTROL(si_ctlp, port)));
}
static int
si_add_legacy_intrs(si_ctl_state_t *si_ctlp)
{
dev_info_t *devinfo = si_ctlp->sictl_devinfop;
int actual, count = 0;
int x, y, rc, inum = 0;
SIDBG_C(SIDBG_INIT, si_ctlp, "si_add_legacy_intrs", NULL);
rc = ddi_intr_get_nintrs(devinfo, DDI_INTR_TYPE_FIXED, &count);
if ((rc != DDI_SUCCESS) || (count == 0)) {
SIDBG_C(SIDBG_ERRS, si_ctlp,
"ddi_intr_get_nintrs() failed, "
"rc %d count %d\n", rc, count);
return (DDI_FAILURE);
}
si_ctlp->sictl_intr_size = count * sizeof (ddi_intr_handle_t);
si_ctlp->sictl_htable = kmem_zalloc(si_ctlp->sictl_intr_size, KM_SLEEP);
rc = ddi_intr_alloc(devinfo, si_ctlp->sictl_htable, DDI_INTR_TYPE_FIXED,
inum, count, &actual, DDI_INTR_ALLOC_STRICT);
if ((rc != DDI_SUCCESS) || (actual == 0)) {
SIDBG_C(SIDBG_ERRS, si_ctlp,
"ddi_intr_alloc() failed, rc %d\n", rc);
kmem_free(si_ctlp->sictl_htable, si_ctlp->sictl_intr_size);
return (DDI_FAILURE);
}
if (actual < count) {
SIDBG_C(SIDBG_ERRS, si_ctlp,
"Requested: %d, Received: %d", count, actual);
for (x = 0; x < actual; x++) {
(void) ddi_intr_free(si_ctlp->sictl_htable[x]);
}
kmem_free(si_ctlp->sictl_htable, si_ctlp->sictl_intr_size);
return (DDI_FAILURE);
}
si_ctlp->sictl_intr_cnt = actual;
if (ddi_intr_get_pri(si_ctlp->sictl_htable[0],
&si_ctlp->sictl_intr_pri) != DDI_SUCCESS) {
SIDBG_C(SIDBG_ERRS, si_ctlp,
"ddi_intr_get_pri() failed", NULL);
for (x = 0; x < actual; x++) {
(void) ddi_intr_free(si_ctlp->sictl_htable[x]);
}
kmem_free(si_ctlp->sictl_htable, si_ctlp->sictl_intr_size);
return (DDI_FAILURE);
}
if (si_ctlp->sictl_intr_pri >= ddi_intr_get_hilevel_pri()) {
SIDBG_C(SIDBG_ERRS, si_ctlp,
"si_add_legacy_intrs: Hi level intr not supported", NULL);
for (x = 0; x < actual; x++) {
(void) ddi_intr_free(si_ctlp->sictl_htable[x]);
}
kmem_free(si_ctlp->sictl_htable, sizeof (ddi_intr_handle_t));
return (DDI_FAILURE);
}
for (x = 0; x < actual; x++) {
if (ddi_intr_add_handler(si_ctlp->sictl_htable[x], si_intr,
(caddr_t)si_ctlp, NULL) != DDI_SUCCESS) {
SIDBG_C(SIDBG_ERRS, si_ctlp,
"ddi_intr_add_handler() failed", NULL);
for (y = 0; y < actual; y++) {
(void) ddi_intr_free(si_ctlp->sictl_htable[y]);
}
kmem_free(si_ctlp->sictl_htable,
si_ctlp->sictl_intr_size);
return (DDI_FAILURE);
}
}
for (x = 0; x < si_ctlp->sictl_intr_cnt; x++) {
(void) ddi_intr_enable(si_ctlp->sictl_htable[x]);
}
return (DDI_SUCCESS);
}
static int
si_add_msi_intrs(si_ctl_state_t *si_ctlp)
{
dev_info_t *devinfo = si_ctlp->sictl_devinfop;
int count, avail, actual;
int x, y, rc, inum = 0;
SIDBG_C(SIDBG_INIT, si_ctlp, "si_add_msi_intrs", NULL);
rc = ddi_intr_get_nintrs(devinfo, DDI_INTR_TYPE_MSI, &count);
if ((rc != DDI_SUCCESS) || (count == 0)) {
SIDBG_C(SIDBG_ERRS, si_ctlp,
"ddi_intr_get_nintrs() failed, "
"rc %d count %d\n", rc, count);
return (DDI_FAILURE);
}
rc = ddi_intr_get_navail(devinfo, DDI_INTR_TYPE_MSI, &avail);
if ((rc != DDI_SUCCESS) || (avail == 0)) {
SIDBG_C(SIDBG_ERRS, si_ctlp,
"ddi_intr_get_navail() failed, "
"rc %d avail %d\n", rc, avail);
return (DDI_FAILURE);
}
if (avail < count) {
SIDBG_C(SIDBG_INIT, si_ctlp,
"ddi_intr_get_nvail returned %d, navail() returned %d",
count, avail);
}
si_ctlp->sictl_intr_size = count * sizeof (ddi_intr_handle_t);
si_ctlp->sictl_htable = kmem_alloc(si_ctlp->sictl_intr_size, KM_SLEEP);
rc = ddi_intr_alloc(devinfo, si_ctlp->sictl_htable, DDI_INTR_TYPE_MSI,
inum, count, &actual, DDI_INTR_ALLOC_NORMAL);
if ((rc != DDI_SUCCESS) || (actual == 0)) {
SIDBG_C(SIDBG_ERRS, si_ctlp,
"ddi_intr_alloc() failed, rc %d\n", rc);
kmem_free(si_ctlp->sictl_htable, si_ctlp->sictl_intr_size);
return (DDI_FAILURE);
}
if (actual < count) {
SIDBG_C(SIDBG_INIT, si_ctlp,
"Requested: %d, Received: %d", count, actual);
}
si_ctlp->sictl_intr_cnt = actual;
if (ddi_intr_get_pri(si_ctlp->sictl_htable[0],
&si_ctlp->sictl_intr_pri) != DDI_SUCCESS) {
SIDBG_C(SIDBG_ERRS, si_ctlp, "ddi_intr_get_pri() failed", NULL);
for (y = 0; y < actual; y++) {
(void) ddi_intr_free(si_ctlp->sictl_htable[y]);
}
kmem_free(si_ctlp->sictl_htable, si_ctlp->sictl_intr_size);
return (DDI_FAILURE);
}
if (si_ctlp->sictl_intr_pri >= ddi_intr_get_hilevel_pri()) {
SIDBG_C(SIDBG_ERRS, si_ctlp,
"si_add_msi_intrs: Hi level intr not supported", NULL);
for (y = 0; y < actual; y++) {
(void) ddi_intr_free(si_ctlp->sictl_htable[y]);
}
kmem_free(si_ctlp->sictl_htable, sizeof (ddi_intr_handle_t));
return (DDI_FAILURE);
}
for (x = 0; x < actual; x++) {
if (ddi_intr_add_handler(si_ctlp->sictl_htable[x], si_intr,
(caddr_t)si_ctlp, NULL) != DDI_SUCCESS) {
SIDBG_C(SIDBG_ERRS, si_ctlp,
"ddi_intr_add_handler() failed", NULL);
for (y = 0; y < actual; y++) {
(void) ddi_intr_free(si_ctlp->sictl_htable[y]);
}
kmem_free(si_ctlp->sictl_htable,
si_ctlp->sictl_intr_size);
return (DDI_FAILURE);
}
}
(void) ddi_intr_get_cap(si_ctlp->sictl_htable[0],
&si_ctlp->sictl_intr_cap);
if (si_ctlp->sictl_intr_cap & DDI_INTR_FLAG_BLOCK) {
(void) ddi_intr_block_enable(si_ctlp->sictl_htable,
si_ctlp->sictl_intr_cnt);
} else {
for (x = 0; x < si_ctlp->sictl_intr_cnt; x++) {
(void) ddi_intr_enable(si_ctlp->sictl_htable[x]);
}
}
return (DDI_SUCCESS);
}
static void
si_rem_intrs(si_ctl_state_t *si_ctlp)
{
int x;
SIDBG_C(SIDBG_INIT, si_ctlp, "si_rem_intrs entered", NULL);
if ((si_ctlp->sictl_intr_type == DDI_INTR_TYPE_MSI) &&
(si_ctlp->sictl_intr_cap & DDI_INTR_FLAG_BLOCK)) {
(void) ddi_intr_block_disable(si_ctlp->sictl_htable,
si_ctlp->sictl_intr_cnt);
} else {
for (x = 0; x < si_ctlp->sictl_intr_cnt; x++) {
(void) ddi_intr_disable(si_ctlp->sictl_htable[x]);
}
}
for (x = 0; x < si_ctlp->sictl_intr_cnt; x++) {
(void) ddi_intr_remove_handler(si_ctlp->sictl_htable[x]);
(void) ddi_intr_free(si_ctlp->sictl_htable[x]);
}
kmem_free(si_ctlp->sictl_htable, si_ctlp->sictl_intr_size);
}
static int
si_reset_dport_wait_till_ready(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port,
int flag)
{
uint32_t port_status;
int loop_count = 0;
sata_device_t sdevice;
uint32_t SStatus;
uint32_t SControl;
uint32_t port_intr_status;
_NOTE(ASSUMING_PROTECTED(si_portp))
if (flag == SI_PORT_RESET) {
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_SET(si_ctlp, port),
PORT_CONTROL_SET_BITS_PORT_RESET);
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_CLEAR(si_ctlp, port),
PORT_CONTROL_CLEAR_BITS_PORT_RESET);
} else {
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_SET(si_ctlp, port),
PORT_CONTROL_SET_BITS_DEV_RESET);
}
if (!(flag & SI_RESET_NO_EVENTS_UP)) {
si_portp->siport_reset_in_progress = 1;
}
SControl = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_SCONTROL(si_ctlp, port));
SCONTROL_SET_DET(SControl, SCONTROL_DET_COMRESET);
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SCONTROL(si_ctlp, port)),
SControl);
#ifndef __lock_lint
delay(SI_10MS_TICKS);
#endif
SControl = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_SCONTROL(si_ctlp, port));
SCONTROL_SET_DET(SControl, SCONTROL_DET_NOACTION);
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SCONTROL(si_ctlp, port)),
SControl);
loop_count = 0;
do {
SStatus = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_SSTATUS(si_ctlp, port));
if (SSTATUS_GET_IPM(SStatus) !=
SSTATUS_IPM_INTERFACE_ACTIVE) {
SSTATUS_SET_DET(SStatus, SSTATUS_DET_NODEV_NOPHY);
}
if (loop_count++ > SI_POLLRATE_SSTATUS) {
break;
}
#ifndef __lock_lint
delay(SI_10MS_TICKS);
#endif
} while (SSTATUS_GET_DET(SStatus) != SSTATUS_DET_DEVPRESENT_PHYONLINE);
SIDBG_P(SIDBG_POLL_LOOP, si_portp,
"si_reset_dport_wait_till_ready: loop count: %d, \
SStatus: 0x%x",
loop_count,
SStatus);
loop_count = 0;
do {
port_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_STATUS(si_ctlp, port));
if (loop_count++ > SI_POLLRATE_PORTREADY) {
break;
}
#ifndef __lock_lint
delay(SI_10MS_TICKS);
#endif
} while (!(port_status & PORT_STATUS_BITS_PORT_READY));
SIDBG_P(SIDBG_POLL_LOOP, si_portp,
"si_reset_dport_wait_till_ready: loop count: %d, \
port_status: 0x%x, SStatus: 0x%x",
loop_count,
port_status,
SStatus);
if (!(flag & SI_RESET_NO_EVENTS_UP)) {
bzero((void *)&sdevice, sizeof (sata_device_t));
sdevice.satadev_addr.cport = (uint8_t)port;
sdevice.satadev_addr.pmport = PORTMULT_CONTROL_PORT;
if (si_portp->siport_port_type == PORT_TYPE_MULTIPLIER) {
sdevice.satadev_addr.qual = SATA_ADDR_DPMPORT;
} else {
sdevice.satadev_addr.qual = SATA_ADDR_DCPORT;
}
sdevice.satadev_state = SATA_DSTATE_RESET |
SATA_DSTATE_PWR_ACTIVE;
if (si_ctlp->sictl_sata_hba_tran) {
sata_hba_event_notify(
si_ctlp->sictl_sata_hba_tran->sata_tran_hba_dip,
&sdevice,
SATA_EVNT_DEVICE_RESET);
}
SIDBG_P(SIDBG_EVENT, si_portp,
"sending event up: SATA_EVNT_RESET", NULL);
}
if ((SSTATUS_GET_IPM(SStatus) == SSTATUS_IPM_INTERFACE_ACTIVE) &&
(SSTATUS_GET_DET(SStatus) ==
SSTATUS_DET_DEVPRESENT_PHYONLINE)) {
if (!(port_status & PORT_STATUS_BITS_PORT_READY)) {
SIDBG_P(SIDBG_POLL_LOOP, si_portp,
"si_reset_dport_wait_till_ready failed", NULL);
return (SI_FAILURE);
}
}
SIDBG_P(SIDBG_INIT, si_portp,
"current interrupt enable set: 0x%x",
ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_INTERRUPT_ENABLE_SET(si_ctlp, port)));
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_INTERRUPT_ENABLE_SET(si_ctlp, port),
(INTR_COMMAND_COMPLETE |
INTR_COMMAND_ERROR |
INTR_PORT_READY |
INTR_POWER_CHANGE |
INTR_PHYRDY_CHANGE |
INTR_COMWAKE_RECEIVED |
INTR_UNRECOG_FIS |
INTR_DEV_XCHANGED |
INTR_SETDEVBITS_NOTIFY));
si_enable_port_interrupts(si_ctlp, port);
port_intr_status = ddi_get32(si_ctlp->sictl_global_acc_handle,
(uint32_t *)PORT_INTERRUPT_STATUS(si_ctlp, port));
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_INTERRUPT_STATUS(si_ctlp,
port)),
port_intr_status & INTR_MASK);
SIDBG_P(SIDBG_POLL_LOOP, si_portp,
"si_reset_dport_wait_till_ready returning success", NULL);
return (SI_SUCCESS);
}
static void
si_schedule_port_initialize(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port)
{
si_event_arg_t *args;
ASSERT(mutex_owned(&si_portp->siport_mutex));
args = si_portp->siport_event_args;
if (args->siea_ctlp != NULL) {
cmn_err(CE_WARN, "si_schedule_port_initialize: "
"args->si_ctlp != NULL");
return;
}
args->siea_ctlp = si_ctlp;
args->siea_port = port;
(void) timeout(si_do_initialize_port, si_portp, 1);
}
static void
si_do_initialize_port(void *arg)
{
si_event_arg_t *args;
si_ctl_state_t *si_ctlp;
si_port_state_t *si_portp;
int port;
si_portp = arg;
mutex_enter(&si_portp->siport_mutex);
args = si_portp->siport_event_args;
si_ctlp = args->siea_ctlp;
port = args->siea_port;
args->siea_ctlp = NULL;
(void) si_initialize_port_wait_till_ready(si_ctlp, port);
mutex_exit(&si_portp->siport_mutex);
}
static int
si_initialize_port_wait_till_ready(si_ctl_state_t *si_ctlp, int port)
{
uint32_t port_status;
int loop_count = 0;
uint32_t SStatus;
si_port_state_t *si_portp = si_ctlp->sictl_ports[port];
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_SET(si_ctlp, port),
PORT_CONTROL_SET_BITS_PORT_INITIALIZE);
loop_count = 0;
do {
port_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_STATUS(si_ctlp, port));
if (loop_count++ > SI_POLLRATE_PORTREADY) {
SIDBG_P(SIDBG_INTR, si_portp,
"si_initialize_port_wait is timing out: "
"port_status: %x",
port_status);
break;
}
#ifndef __lock_lint
delay(SI_10MS_TICKS);
#endif
} while (!(port_status & PORT_STATUS_BITS_PORT_READY));
SIDBG_P(SIDBG_POLL_LOOP, si_portp,
"si_initialize_port_wait_till_ready: loop count: %d",
loop_count);
SStatus = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_SSTATUS(si_ctlp, port));
if ((SSTATUS_GET_IPM(SStatus) == SSTATUS_IPM_INTERFACE_ACTIVE) &&
(SSTATUS_GET_DET(SStatus) ==
SSTATUS_DET_DEVPRESENT_PHYONLINE)) {
if (!(port_status & PORT_STATUS_BITS_PORT_READY)) {
return (SI_FAILURE);
}
}
return (SI_SUCCESS);
}
static void
si_timeout_pkts(
si_ctl_state_t *si_ctlp,
si_port_state_t *si_portp,
int port,
uint32_t timedout_tags)
{
uint32_t slot_status;
uint32_t finished_tags;
SIDBG_P(SIDBG_TIMEOUT, si_portp,
"si_timeout_pkts entry", NULL);
mutex_enter(&si_portp->siport_mutex);
slot_status = ddi_get32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_SLOT_STATUS(si_ctlp, port)));
si_portp->mopping_in_progress++;
(void) si_initialize_port_wait_till_ready(si_ctlp, port);
finished_tags = si_portp->siport_pending_tags &
~slot_status & SI_SLOT_MASK;
timedout_tags &= ~finished_tags;
SIDBG_P(SIDBG_TIMEOUT, si_portp,
"si_timeout_pkts: finished: %x, timeout: %x",
finished_tags,
timedout_tags);
si_mop_commands(si_ctlp,
si_portp,
port,
slot_status,
0,
timedout_tags,
0,
0);
mutex_exit(&si_portp->siport_mutex);
}
static void
si_watchdog_handler(si_ctl_state_t *si_ctlp)
{
uint32_t pending_tags = 0;
uint32_t timedout_tags = 0;
si_port_state_t *si_portp;
int port;
int tmpslot;
sata_pkt_t *satapkt;
int max_life_cycles;
int watched_cycles;
mutex_enter(&si_ctlp->sictl_mutex);
SIDBG_C(SIDBG_ENTRY, si_ctlp,
"si_watchdog_handler entered", NULL);
for (port = 0; port < si_ctlp->sictl_num_ports; port++) {
si_portp = si_ctlp->sictl_ports[port];
if (si_portp == NULL) {
continue;
}
mutex_enter(&si_portp->siport_mutex);
if (si_portp->siport_port_type == PORT_TYPE_NODEV) {
mutex_exit(&si_portp->siport_mutex);
continue;
}
if (si_portp->mopping_in_progress > 0) {
SIDBG_P(SIDBG_INFO, si_portp,
"si_watchdog_handler: port %d mopping "
"in progress, so just return", port);
mutex_exit(&si_portp->siport_mutex);
continue;
}
pending_tags = si_portp->siport_pending_tags;
timedout_tags = 0;
while (pending_tags) {
tmpslot = ddi_ffs(pending_tags) - 1;
if (tmpslot == -1) {
break;
}
satapkt = si_portp->siport_slot_pkts[tmpslot];
if ((satapkt != NULL) && satapkt->satapkt_time) {
watched_cycles = (int)(intptr_t)
satapkt->satapkt_hba_driver_private;
watched_cycles++;
max_life_cycles = (satapkt->satapkt_time +
si_watchdog_timeout - 1) /
si_watchdog_timeout;
if (watched_cycles > max_life_cycles) {
timedout_tags |= (0x1 << tmpslot);
SIDBG_P(SIDBG_TIMEOUT,
si_portp,
"watchdog: timedout_tags: 0x%x",
timedout_tags);
}
satapkt->satapkt_hba_driver_private =
(void *)(intptr_t)watched_cycles;
}
CLEAR_BIT(pending_tags, tmpslot);
}
if (timedout_tags) {
mutex_exit(&si_portp->siport_mutex);
mutex_exit(&si_ctlp->sictl_mutex);
si_timeout_pkts(si_ctlp, si_portp, port, timedout_tags);
mutex_enter(&si_ctlp->sictl_mutex);
mutex_enter(&si_portp->siport_mutex);
}
mutex_exit(&si_portp->siport_mutex);
}
if (!(si_ctlp->sictl_flags & SI_NO_TIMEOUTS)) {
si_ctlp->sictl_timeout_id =
timeout((void (*)(void *))si_watchdog_handler,
(caddr_t)si_ctlp, si_watchdog_tick);
}
mutex_exit(&si_ctlp->sictl_mutex);
}
static int
si_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);
}
static void
si_fm_init(si_ctl_state_t *si_ctlp)
{
ddi_iblock_cookie_t fm_ibc;
if (si_ctlp->fm_capabilities) {
accattr.devacc_attr_access = DDI_FLAGERR_ACC;
prb_sgt_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
buffer_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
ddi_fm_init(si_ctlp->sictl_devinfop, &si_ctlp->fm_capabilities,
&fm_ibc);
if (si_ctlp->fm_capabilities == DDI_FM_NOT_CAPABLE)
cmn_err(CE_WARN, "si_fm_init: ddi_fm_init fail");
if (DDI_FM_EREPORT_CAP(si_ctlp->fm_capabilities) ||
DDI_FM_ERRCB_CAP(si_ctlp->fm_capabilities)) {
pci_ereport_setup(si_ctlp->sictl_devinfop);
}
if (DDI_FM_ERRCB_CAP(si_ctlp->fm_capabilities)) {
ddi_fm_handler_register(si_ctlp->sictl_devinfop,
si_fm_error_cb, (void *) si_ctlp);
}
}
}
static void
si_fm_fini(si_ctl_state_t *si_ctlp)
{
if (si_ctlp->fm_capabilities) {
if (DDI_FM_ERRCB_CAP(si_ctlp->fm_capabilities)) {
ddi_fm_handler_unregister(si_ctlp->sictl_devinfop);
}
if (DDI_FM_EREPORT_CAP(si_ctlp->fm_capabilities) ||
DDI_FM_ERRCB_CAP(si_ctlp->fm_capabilities)) {
pci_ereport_teardown(si_ctlp->sictl_devinfop);
}
ddi_fm_fini(si_ctlp->sictl_devinfop);
accattr.devacc_attr_access = DDI_DEFAULT_ACC;
prb_sgt_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
buffer_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
}
}
static int
si_check_acc_handle(ddi_acc_handle_t handle)
{
ddi_fm_error_t de;
ASSERT(handle != NULL);
ddi_fm_acc_err_get(handle, &de, DDI_FME_VERSION);
return (de.fme_status);
}
static int
si_check_dma_handle(ddi_dma_handle_t handle)
{
ddi_fm_error_t de;
ASSERT(handle != NULL);
ddi_fm_dma_err_get(handle, &de, DDI_FME_VERSION);
return (de.fme_status);
}
static int
si_check_ctl_handles(si_ctl_state_t *si_ctlp)
{
if ((si_check_acc_handle(si_ctlp->sictl_pci_conf_handle)
!= DDI_SUCCESS) ||
(si_check_acc_handle(si_ctlp->sictl_global_acc_handle)
!= DDI_SUCCESS) ||
(si_check_acc_handle(si_ctlp->sictl_port_acc_handle)
!= DDI_SUCCESS)) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
si_check_port_handles(si_port_state_t *si_portp)
{
if ((si_check_dma_handle(si_portp->siport_prbpool_dma_handle)
!= DDI_SUCCESS) ||
(si_check_acc_handle(si_portp->siport_prbpool_acc_handle)
!= DDI_SUCCESS) ||
(si_check_dma_handle(si_portp->siport_sgbpool_dma_handle)
!= DDI_SUCCESS) ||
(si_check_acc_handle(si_portp->siport_sgbpool_acc_handle)
!= DDI_SUCCESS)) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
si_fm_ereport(si_ctl_state_t *si_ctlp, char *detail, char *payload)
{
uint64_t ena;
char buf[FM_MAX_CLASS];
(void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail);
ena = fm_ena_generate(0, FM_ENA_FMT1);
if (DDI_FM_EREPORT_CAP(si_ctlp->fm_capabilities)) {
ddi_fm_ereport_post(si_ctlp->sictl_devinfop, buf, ena,
DDI_NOSLEEP,
FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERSION,
"detailed_err_type", DATA_TYPE_STRING, payload,
NULL);
}
}
static void
si_log(si_ctl_state_t *si_ctlp, si_port_state_t *si_portp, char *fmt, ...)
{
va_list ap;
mutex_enter(&si_log_mutex);
va_start(ap, fmt);
if (si_portp == NULL && si_ctlp == NULL) {
sata_vtrace_debug(NULL, fmt, ap);
va_end(ap);
mutex_exit(&si_log_mutex);
return;
}
if (si_portp == NULL && si_ctlp != NULL) {
sata_vtrace_debug(si_ctlp->sictl_devinfop, fmt, ap);
va_end(ap);
mutex_exit(&si_log_mutex);
return;
}
(void) snprintf(si_log_buf, SI_LOGBUF_LEN, "port%d: %s",
si_portp->siport_port_num, fmt);
if (si_portp->siport_ctlp == NULL) {
sata_vtrace_debug(NULL, si_log_buf, ap);
va_end(ap);
mutex_exit(&si_log_mutex);
return;
}
sata_vtrace_debug(si_portp->siport_ctlp->sictl_devinfop,
si_log_buf, ap);
va_end(ap);
mutex_exit(&si_log_mutex);
}
static void
si_copy_out_regs(sata_cmd_t *scmd, si_ctl_state_t *si_ctlp, uint8_t port,
uint8_t slot)
{
uint32_t *fis_word_ptr;
si_prb_t *prb;
int i;
si_port_state_t *si_portp = si_ctlp->sictl_ports[port];
prb = &si_ctlp->sictl_ports[port]->siport_prbpool[slot];
fis_word_ptr = (uint32_t *)(void *)(&prb->prb_fis);
for (i = 0; i < (sizeof (fis_reg_h2d_t)/4); i++) {
fis_word_ptr[i] = ddi_get32(
si_ctlp->sictl_port_acc_handle,
(uint32_t *)(PORT_LRAM(si_ctlp, port,
slot) + i * 4 + 0x08));
}
scmd->satacmd_status_reg = GET_FIS_COMMAND(prb->prb_fis);
DTRACE_PROBE1(satacmd_status_reg, int, scmd->satacmd_status_reg);
if (scmd->satacmd_flags.sata_copy_out_sec_count_msb) {
scmd->satacmd_sec_count_msb =
GET_FIS_SECTOR_COUNT_EXP(prb->prb_fis);
SIDBG_P(SIDBG_VERBOSE, si_portp,
"copyout satacmd_sec_count_msb %x\n",
scmd->satacmd_sec_count_msb);
}
if (scmd->satacmd_flags.sata_copy_out_lba_low_msb) {
scmd->satacmd_lba_low_msb = GET_FIS_SECTOR_EXP(prb->prb_fis);
SIDBG_P(SIDBG_VERBOSE, si_portp,
"copyout satacmd_lba_low_msb %x\n",
scmd->satacmd_lba_low_msb);
}
if (scmd->satacmd_flags.sata_copy_out_lba_mid_msb) {
scmd->satacmd_lba_mid_msb = GET_FIS_CYL_LOW_EXP(prb->prb_fis);
SIDBG_P(SIDBG_VERBOSE, si_portp,
"copyout satacmd_lba_mid_msb %x\n",
scmd->satacmd_lba_mid_msb);
}
if (scmd->satacmd_flags.sata_copy_out_lba_high_msb) {
scmd->satacmd_lba_high_msb = GET_FIS_CYL_HI_EXP(prb->prb_fis);
SIDBG_P(SIDBG_VERBOSE, si_portp,
"copyout satacmd_lba_high_msb %x\n",
scmd->satacmd_lba_high_msb);
}
if (scmd->satacmd_flags.sata_copy_out_sec_count_lsb) {
scmd->satacmd_sec_count_lsb =
GET_FIS_SECTOR_COUNT(prb->prb_fis);
SIDBG_P(SIDBG_VERBOSE, si_portp,
"copyout satacmd_sec_count_lsb %x\n",
scmd->satacmd_sec_count_lsb);
}
if (scmd->satacmd_flags.sata_copy_out_lba_low_lsb) {
scmd->satacmd_lba_low_lsb = GET_FIS_SECTOR(prb->prb_fis);
SIDBG_P(SIDBG_VERBOSE, si_portp,
"copyout satacmd_lba_low_lsb %x\n",
scmd->satacmd_lba_low_lsb);
}
if (scmd->satacmd_flags.sata_copy_out_lba_mid_lsb) {
scmd->satacmd_lba_mid_lsb = GET_FIS_CYL_LOW(prb->prb_fis);
SIDBG_P(SIDBG_VERBOSE, si_portp,
"copyout satacmd_lba_mid_lsb %x\n",
scmd->satacmd_lba_mid_lsb);
}
if (scmd->satacmd_flags.sata_copy_out_lba_high_lsb) {
scmd->satacmd_lba_high_lsb = GET_FIS_CYL_HI(prb->prb_fis);
SIDBG_P(SIDBG_VERBOSE, si_portp,
"copyout satacmd_lba_high_lsb %x\n",
scmd->satacmd_lba_high_lsb);
}
if (scmd->satacmd_flags.sata_copy_out_device_reg) {
scmd->satacmd_device_reg = GET_FIS_DEV_HEAD(prb->prb_fis);
SIDBG_P(SIDBG_VERBOSE, si_portp,
"copyout satacmd_device_reg %x\n",
scmd->satacmd_device_reg);
}
if (scmd->satacmd_flags.sata_copy_out_error_reg) {
scmd->satacmd_error_reg = GET_FIS_FEATURES(prb->prb_fis);
SIDBG_P(SIDBG_VERBOSE, si_portp,
"copyout satacmd_error_reg %x\n",
scmd->satacmd_error_reg);
}
}
static int
si_clear_port(si_ctl_state_t *si_ctlp, int port)
{
if (si_ctlp == NULL)
return (SI_FAILURE);
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_SET(si_ctlp, port),
PORT_CONTROL_SET_BITS_PORT_RESET);
ddi_put32(si_ctlp->sictl_port_acc_handle,
(uint32_t *)PORT_CONTROL_CLEAR(si_ctlp, port),
PORT_CONTROL_CLEAR_BITS_PORT_RESET);
return (SI_SUCCESS);
}
static int
si_quiesce(dev_info_t *dip)
{
si_ctl_state_t *si_ctlp;
int instance;
int port;
instance = ddi_get_instance(dip);
si_ctlp = ddi_get_soft_state(si_statep, instance);
if (si_ctlp == NULL)
return (DDI_FAILURE);
SIDBG_C(SIDBG_ENTRY, si_ctlp, "si_quiesce enter", NULL);
for (port = 0; port < si_ctlp->sictl_num_ports; port++) {
si_disable_port_interrupts(si_ctlp, port);
(void) si_clear_port(si_ctlp, port);
}
return (DDI_SUCCESS);
}