#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/modctl.h>
#include <sys/stat.h>
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
#include <sys/pci.h>
#include <sys/1394/h1394.h>
#include <sys/1394/adapters/hci1394.h>
#include <sys/1394/adapters/hci1394_extern.h>
#define STATE_ZALLOC 0
#define STATE_ISR_INIT 1
#define STATE_MINOR_NODE 2
#define STATE_HW_INIT 3
#define STATE_PHASE2 4
#define STATE_POWER_INIT 5
#define STATE_H1394_ATTACH 6
#define STATE_ISR_HANDLER 7
#define STATE_STARTUP 8
static void hci1394_statebit_set(uint64_t *state, uint_t statebit);
static boolean_t hci1394_statebit_tst(uint64_t state, uint_t statebit);
static void hci1394_cleanup(hci1394_state_t *soft_state, uint64_t attach_state);
static int hci1394_hardware_init(hci1394_state_t *soft_state);
static int hci1394_hardware_resume(hci1394_state_t *soft_state);
static int hci1394_pci_init(hci1394_state_t *soft_state);
static void hci1394_pci_resume(hci1394_state_t *soft_state);
static void hci1394_soft_state_phase1_init(hci1394_state_t *soft_state,
dev_info_t *dip, int instance);
static void hci1394_soft_state_phase2_init(hci1394_state_t *soft_state);
static int hci1394_resmap_get(hci1394_state_t *soft_state);
static void hci1394_resmap_free(hci1394_state_t *soft_state);
int
hci1394_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
hci1394_state_t *soft_state;
uint64_t attach_state = 0;
int instance;
int status;
switch (cmd) {
case DDI_ATTACH:
instance = ddi_get_instance(dip);
status = ddi_soft_state_zalloc(hci1394_statep, instance);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
soft_state = ddi_get_soft_state(hci1394_statep, instance);
if (soft_state == NULL) {
ddi_soft_state_free(hci1394_statep, instance);
return (DDI_FAILURE);
}
hci1394_statebit_set(&attach_state, STATE_ZALLOC);
hci1394_soft_state_phase1_init(soft_state, dip, instance);
status = hci1394_isr_init(soft_state);
if (status != DDI_SUCCESS) {
hci1394_cleanup(soft_state, attach_state);
return (DDI_FAILURE);
}
hci1394_statebit_set(&attach_state, STATE_ISR_INIT);
status = ddi_create_minor_node(dip, "devctl", S_IFCHR,
instance, DDI_NT_NEXUS, 0);
if (status != DDI_SUCCESS) {
hci1394_cleanup(soft_state, attach_state);
return (DDI_FAILURE);
}
hci1394_statebit_set(&attach_state, STATE_MINOR_NODE);
status = hci1394_hardware_init(soft_state);
if (status != DDI_SUCCESS) {
hci1394_cleanup(soft_state, attach_state);
return (DDI_FAILURE);
}
hci1394_statebit_set(&attach_state, STATE_HW_INIT);
hci1394_soft_state_phase2_init(soft_state);
hci1394_statebit_set(&attach_state, STATE_PHASE2);
status = hci1394_resmap_get(soft_state);
if (status != DDI_SUCCESS) {
hci1394_cleanup(soft_state, attach_state);
return (DDI_FAILURE);
}
status = h1394_attach(&soft_state->halinfo, DDI_ATTACH,
&soft_state->drvinfo.di_sl_private);
if (status != DDI_SUCCESS) {
hci1394_resmap_free(soft_state);
hci1394_cleanup(soft_state, attach_state);
return (DDI_FAILURE);
}
hci1394_resmap_free(soft_state);
hci1394_statebit_set(&attach_state, STATE_H1394_ATTACH);
status = hci1394_isr_handler_init(soft_state);
if (status != DDI_SUCCESS) {
hci1394_cleanup(soft_state, attach_state);
return (DDI_FAILURE);
}
hci1394_statebit_set(&attach_state, STATE_ISR_HANDLER);
ddi_report_dev(dip);
hci1394_statebit_set(&attach_state, STATE_STARTUP);
status = hci1394_ohci_startup(soft_state->ohci);
if (status != DDI_SUCCESS) {
hci1394_cleanup(soft_state, attach_state);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
case DDI_RESUME:
instance = ddi_get_instance(dip);
soft_state = ddi_get_soft_state(hci1394_statep, instance);
if (soft_state == NULL) {
return (DDI_FAILURE);
}
status = hci1394_hardware_resume(soft_state);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
soft_state->drvinfo.di_drvstate.ds_state = HCI1394_INITIAL;
status = hci1394_ohci_startup(soft_state->ohci);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
status = h1394_attach(&soft_state->halinfo, DDI_RESUME,
&soft_state->drvinfo.di_sl_private);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
default:
break;
}
return (DDI_FAILURE);
}
static void
hci1394_soft_state_phase1_init(hci1394_state_t *soft_state, dev_info_t *dip,
int instance)
{
ASSERT(soft_state != NULL);
soft_state->drvinfo.di_dip = dip;
soft_state->drvinfo.di_instance = instance;
soft_state->drvinfo.di_gencnt = 0;
soft_state->drvinfo.di_sl_private = NULL;
soft_state->drvinfo.di_stats.st_bus_reset_count = 0;
soft_state->drvinfo.di_stats.st_selfid_count = 0;
soft_state->drvinfo.di_stats.st_phy_isr = 0;
soft_state->drvinfo.di_stats.st_phy_loop_err = 0;
soft_state->drvinfo.di_stats.st_phy_pwrfail_err = 0;
soft_state->drvinfo.di_stats.st_phy_timeout_err = 0;
soft_state->drvinfo.di_stats.st_phy_portevt_err = 0;
soft_state->swap_data = B_FALSE;
soft_state->sl_selfid_buf = NULL;
soft_state->halinfo.hal_private = soft_state;
soft_state->halinfo.dip = soft_state->drvinfo.di_dip;
soft_state->halinfo.hal_events = hci1394_evts;
soft_state->halinfo.max_generation = OHCI_BUSGEN_MAX;
soft_state->halinfo.addr_map_num_entries = HCI1394_ADDR_MAP_SIZE;
soft_state->halinfo.addr_map = hci1394_addr_map;
hci1394_buf_attr_get(&soft_state->halinfo.dma_attr);
}
static void
hci1394_soft_state_phase2_init(hci1394_state_t *soft_state)
{
ASSERT(soft_state != NULL);
soft_state->drvinfo.di_drvstate.ds_state = HCI1394_INITIAL;
mutex_init(&soft_state->drvinfo.di_drvstate.ds_mutex, NULL,
MUTEX_DRIVER, soft_state->drvinfo.di_iblock_cookie);
soft_state->halinfo.acc_attr = soft_state->drvinfo.di_buf_attr;
soft_state->halinfo.hw_interrupt = soft_state->drvinfo.di_iblock_cookie;
hci1394_csr_node_capabilities(soft_state->csr,
&soft_state->halinfo.node_capabilities);
hci1394_ohci_bus_capabilities(soft_state->ohci,
&soft_state->halinfo.bus_capabilities);
soft_state->halinfo.hal_overhead = hci1394_async_cmd_overhead();
}
static int
hci1394_hardware_init(hci1394_state_t *soft_state)
{
int status;
ASSERT(soft_state != NULL);
status = hci1394_pci_init(soft_state);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
status = hci1394_ohci_init(soft_state, &soft_state->drvinfo,
&soft_state->ohci);
if (status != DDI_SUCCESS) {
hci1394_pci_fini(soft_state);
return (DDI_FAILURE);
}
hci1394_csr_init(&soft_state->drvinfo, soft_state->ohci,
&soft_state->csr);
status = hci1394_async_init(&soft_state->drvinfo, soft_state->ohci,
soft_state->csr, &soft_state->async);
if (status != DDI_SUCCESS) {
hci1394_csr_fini(&soft_state->csr);
hci1394_ohci_fini(&soft_state->ohci);
hci1394_pci_fini(soft_state);
return (DDI_FAILURE);
}
hci1394_isoch_init(&soft_state->drvinfo, soft_state->ohci,
&soft_state->isoch);
status = hci1394_vendor_init(&soft_state->drvinfo, soft_state->ohci,
&soft_state->vendor_info, &soft_state->vendor);
if (status != DDI_SUCCESS) {
hci1394_isoch_fini(&soft_state->isoch);
hci1394_async_fini(&soft_state->async);
hci1394_csr_fini(&soft_state->csr);
hci1394_ohci_fini(&soft_state->ohci);
hci1394_pci_fini(soft_state);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
hci1394_hardware_resume(hci1394_state_t *soft_state)
{
int status;
ASSERT(soft_state != NULL);
hci1394_pci_resume(soft_state);
status = hci1394_ohci_resume(soft_state->ohci);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
hci1394_csr_resume(soft_state->csr);
status = hci1394_async_resume(soft_state->async);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
status = hci1394_vendor_resume(soft_state->vendor);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
hci1394_pci_init(hci1394_state_t *soft_state)
{
int status;
#ifndef _LITTLE_ENDIAN
uint32_t global_swap;
#endif
ASSERT(soft_state != NULL);
status = pci_config_setup(soft_state->drvinfo.di_dip,
&soft_state->pci_config);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
#ifdef _LITTLE_ENDIAN
soft_state->drvinfo.di_reg_attr.devacc_attr_version =
DDI_DEVICE_ATTR_V0;
soft_state->drvinfo.di_reg_attr.devacc_attr_endian_flags =
DDI_STRUCTURE_LE_ACC;
soft_state->drvinfo.di_reg_attr.devacc_attr_dataorder =
DDI_STRICTORDER_ACC;
soft_state->drvinfo.di_buf_attr.devacc_attr_version =
DDI_DEVICE_ATTR_V0;
soft_state->drvinfo.di_buf_attr.devacc_attr_endian_flags =
DDI_STRUCTURE_LE_ACC;
soft_state->drvinfo.di_buf_attr.devacc_attr_dataorder =
DDI_STRICTORDER_ACC;
soft_state->swap_data = B_TRUE;
#else
global_swap = pci_config_get32(soft_state->pci_config,
OHCI_PCI_HCI_CONTROL_REG);
if ((global_swap & OHCI_PCI_GLOBAL_SWAP) == 0) {
global_swap = global_swap | OHCI_PCI_GLOBAL_SWAP;
pci_config_put32(soft_state->pci_config,
OHCI_PCI_HCI_CONTROL_REG, global_swap);
}
global_swap = pci_config_get32(soft_state->pci_config,
OHCI_PCI_HCI_CONTROL_REG);
if ((global_swap & OHCI_PCI_GLOBAL_SWAP) == 0) {
soft_state->drvinfo.di_reg_attr.devacc_attr_version =
DDI_DEVICE_ATTR_V0;
soft_state->drvinfo.di_reg_attr.devacc_attr_endian_flags =
DDI_STRUCTURE_LE_ACC;
soft_state->drvinfo.di_reg_attr.devacc_attr_dataorder =
DDI_STRICTORDER_ACC;
soft_state->drvinfo.di_buf_attr.devacc_attr_version =
DDI_DEVICE_ATTR_V0;
soft_state->drvinfo.di_buf_attr.devacc_attr_endian_flags =
DDI_STRUCTURE_LE_ACC;
soft_state->drvinfo.di_buf_attr.devacc_attr_dataorder =
DDI_STRICTORDER_ACC;
soft_state->swap_data = B_TRUE;
} else {
soft_state->drvinfo.di_reg_attr.devacc_attr_version =
DDI_DEVICE_ATTR_V0;
soft_state->drvinfo.di_reg_attr.devacc_attr_endian_flags =
DDI_STRUCTURE_BE_ACC;
soft_state->drvinfo.di_reg_attr.devacc_attr_dataorder =
DDI_STRICTORDER_ACC;
soft_state->drvinfo.di_buf_attr.devacc_attr_version =
DDI_DEVICE_ATTR_V0;
soft_state->drvinfo.di_buf_attr.devacc_attr_endian_flags =
DDI_STRUCTURE_BE_ACC;
soft_state->drvinfo.di_buf_attr.devacc_attr_dataorder =
DDI_STRICTORDER_ACC;
soft_state->swap_data = B_FALSE;
}
#endif
soft_state->vendor_info.vendor_id =
(uint_t)pci_config_get16(soft_state->pci_config, PCI_CONF_VENID);
soft_state->vendor_info.device_id =
(uint_t)pci_config_get16(soft_state->pci_config, PCI_CONF_DEVID);
soft_state->vendor_info.revision_id =
(uint_t)pci_config_get8(soft_state->pci_config, PCI_CONF_REVID);
return (DDI_SUCCESS);
}
static void
hci1394_pci_resume(hci1394_state_t *soft_state)
{
#ifndef _LITTLE_ENDIAN
uint32_t global_swap;
#endif
ASSERT(soft_state != NULL);
#ifdef _LITTLE_ENDIAN
#else
global_swap = pci_config_get32(soft_state->pci_config,
OHCI_PCI_HCI_CONTROL_REG);
if ((global_swap & OHCI_PCI_GLOBAL_SWAP) == 0) {
global_swap = global_swap | OHCI_PCI_GLOBAL_SWAP;
pci_config_put32(soft_state->pci_config,
OHCI_PCI_HCI_CONTROL_REG, global_swap);
}
#endif
}
static int
hci1394_resmap_get(hci1394_state_t *soft_state)
{
h1394_addr_map_t *resv_map;
int resv_num;
int status;
int reslen;
uint32_t *resptr;
int rescnt;
int mapcnt;
ASSERT(soft_state != NULL);
status = ddi_prop_lookup_int_array(DDI_DEV_T_ANY,
soft_state->drvinfo.di_dip, DDI_PROP_DONTPASS, "reserved-addresses",
(int **)&resptr, (uint_t *)&reslen);
if (status != DDI_PROP_SUCCESS) {
soft_state->halinfo.resv_map_num_entries = 0;
soft_state->halinfo.resv_map = NULL;
return (DDI_SUCCESS);
} else if ((reslen < 3) || ((reslen % 3) != 0)) {
resv_num = 0;
resv_map = NULL;
cmn_err(CE_NOTE, "!%s(%d): Invalid reserved-addresses property."
" Property ignored", ddi_node_name(
soft_state->drvinfo.di_dip), ddi_get_instance(
soft_state->drvinfo.di_dip));
} else {
resv_num = reslen / 3;
resv_map = kmem_alloc((sizeof (h1394_addr_map_t) * (resv_num)),
KM_SLEEP);
rescnt = 0;
mapcnt = 0;
while (rescnt < reslen) {
resv_map[mapcnt].address =
(uint64_t)resptr[rescnt] << 32;
rescnt++;
resv_map[mapcnt].address |= (uint64_t)resptr[rescnt];
rescnt++;
resv_map[mapcnt].length = (uint64_t)resptr[rescnt];
rescnt++;
resv_map[mapcnt].addr_type = H1394_ADDR_RESERVED;
mapcnt++;
}
}
ddi_prop_free(resptr);
soft_state->halinfo.resv_map_num_entries = resv_num;
soft_state->halinfo.resv_map = resv_map;
return (DDI_SUCCESS);
}
static void
hci1394_resmap_free(hci1394_state_t *soft_state)
{
ASSERT(soft_state != NULL);
if (soft_state->halinfo.resv_map_num_entries > 0) {
ASSERT(soft_state->halinfo.resv_map != NULL);
kmem_free(soft_state->halinfo.resv_map,
(sizeof (h1394_addr_map_t) *
soft_state->halinfo.resv_map_num_entries));
}
}
static void
hci1394_statebit_set(uint64_t *state, uint_t statebit)
{
ASSERT(state != NULL);
ASSERT(statebit < 64);
*state |= (uint64_t)0x1 << statebit;
}
static boolean_t
hci1394_statebit_tst(uint64_t state, uint_t statebit)
{
uint64_t bitset;
int status;
ASSERT(statebit < 64);
bitset = state & ((uint64_t)0x1 << statebit);
if (bitset == 0) {
status = B_FALSE;
} else {
status = B_TRUE;
}
return (status);
}
static void
hci1394_cleanup(hci1394_state_t *soft_state, uint64_t attach_state)
{
int status;
ASSERT(soft_state != NULL);
status = hci1394_statebit_tst(attach_state, STATE_STARTUP);
if (status == B_TRUE) {
hci1394_ohci_intr_master_disable(soft_state->ohci);
(void) hci1394_state_set(&soft_state->drvinfo,
HCI1394_SHUTDOWN);
(void) hci1394_ohci_soft_reset(soft_state->ohci);
hci1394_async_flush(soft_state->async);
}
status = hci1394_statebit_tst(attach_state, STATE_ISR_HANDLER);
if (status == B_TRUE) {
hci1394_isr_handler_fini(soft_state);
}
status = hci1394_statebit_tst(attach_state, STATE_H1394_ATTACH);
if (status == B_TRUE) {
(void) h1394_detach(&soft_state->drvinfo.di_sl_private,
DDI_DETACH);
}
status = hci1394_statebit_tst(attach_state, STATE_HW_INIT);
if (status == B_TRUE) {
hci1394_detach_hardware(soft_state);
}
status = hci1394_statebit_tst(attach_state, STATE_MINOR_NODE);
if (status == B_TRUE) {
ddi_remove_minor_node(soft_state->drvinfo.di_dip, "devctl");
}
status = hci1394_statebit_tst(attach_state, STATE_ISR_INIT);
if (status == B_TRUE) {
hci1394_isr_fini(soft_state);
}
status = hci1394_statebit_tst(attach_state, STATE_PHASE2);
if (status == B_TRUE) {
hci1394_soft_state_fini(soft_state);
}
status = hci1394_statebit_tst(attach_state, STATE_ZALLOC);
if (status == B_TRUE) {
ddi_soft_state_free(hci1394_statep,
soft_state->drvinfo.di_instance);
}
}