#include <oce_impl.h>
#include <oce_stat.h>
#include <oce_ioctl.h>
#define ATTACH_DEV_INIT 0x1
#define ATTACH_FM_INIT 0x2
#define ATTACH_PCI_CFG 0x4
#define ATTACH_LOCK_INIT 0x8
#define ATTACH_PCI_INIT 0x10
#define ATTACH_HW_INIT 0x20
#define ATTACH_SETUP_TXRX 0x40
#define ATTACH_SETUP_ADAP 0x80
#define ATTACH_SETUP_INTR 0x100
#define ATTACH_STAT_INIT 0x200
#define ATTACH_MAC_REG 0x400
const char oce_ident_string[] = OCE_IDENT_STRING;
const char oce_mod_name[] = OCE_MOD_NAME;
struct oce_dev *oce_dev_list[MAX_DEVS + 1];
static const char flow_control[] = "flow_control";
static const char mtu_prop_name[] = "oce_default_mtu";
static const char tx_ring_size_name[] = "tx_ring_size";
static const char tx_bcopy_limit_name[] = "tx_bcopy_limit";
static const char rx_bcopy_limit_name[] = "rx_bcopy_limit";
static const char rx_frag_size_name[] = "rx_frag_size";
static const char rx_max_bufs_name[] = "rx_max_bufs";
static const char fm_cap_name[] = "oce_fm_capability";
static const char log_level_name[] = "oce_log_level";
static const char lso_capable_name[] = "lso_capable";
static const char rx_pkt_per_intr_name[] = "rx_pkts_per_intr";
static const char tx_reclaim_threshold_name[] = "tx_reclaim_threshold";
static const char rx_rings_name[] = "max_rx_rings";
static const char tx_rings_name[] = "max_tx_rings";
static int oce_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd);
static int oce_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
static int oce_quiesce(dev_info_t *dip);
static int oce_suspend(dev_info_t *dip);
static int oce_resume(dev_info_t *dip);
static void oce_unconfigure(struct oce_dev *dev);
static void oce_init_locks(struct oce_dev *dev);
static void oce_destroy_locks(struct oce_dev *dev);
static void oce_get_params(struct oce_dev *dev);
static int oce_get_prop(struct oce_dev *dev, char *propname, int minval,
int maxval, int defval, uint32_t *values);
static struct cb_ops oce_cb_ops = {
nulldev,
nulldev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
NULL,
D_MP,
CB_REV,
nodev,
nodev
};
static struct dev_ops oce_dev_ops = {
DEVO_REV,
0,
NULL,
NULL,
nulldev,
oce_attach,
oce_detach,
nodev,
&oce_cb_ops,
NULL,
nodev,
oce_quiesce
};
static struct modldrv oce_drv = {
&mod_driverops,
(char *)oce_ident_string,
&oce_dev_ops,
};
static struct modlinkage oce_mod_linkage = {
MODREV_1, &oce_drv, NULL
};
#define OCE_M_CB_FLAGS (MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP | \
MC_PROPINFO)
static mac_callbacks_t oce_mac_cb = {
OCE_M_CB_FLAGS,
oce_m_stat,
oce_m_start,
oce_m_stop,
oce_m_promiscuous,
oce_m_multicast,
oce_m_unicast,
oce_m_send,
NULL,
oce_m_ioctl,
oce_m_getcap,
NULL,
NULL,
oce_m_setprop,
oce_m_getprop,
oce_m_propinfo
};
extern char *oce_priv_props[];
int
_info(struct modinfo *modinfop)
{
return (mod_info(&oce_mod_linkage, modinfop));
}
int
_init(void)
{
int ret = 0;
mac_init_ops(&oce_dev_ops, "oce");
ret = mod_install(&oce_mod_linkage);
if (ret) {
cmn_err(CE_WARN, "mod_install failed rval=%x", ret);
}
return (ret);
}
int
_fini(void)
{
int ret = 0;
ret = mod_remove(&oce_mod_linkage);
if (ret != 0) {
return (ret);
}
mac_fini_ops(&oce_dev_ops);
return (ret);
}
static int
oce_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int ret = 0;
struct oce_dev *dev = NULL;
mac_register_t *mac;
uint8_t dev_index = 0;
switch (cmd) {
case DDI_RESUME:
return (oce_resume(dip));
default:
return (DDI_FAILURE);
case DDI_ATTACH:
break;
}
dev = kmem_zalloc(sizeof (struct oce_dev), KM_SLEEP);
dev->dip = dip;
dev->dev_id = ddi_get_instance(dip);
dev->suspended = B_FALSE;
dev->dev_list_index = MAX_DEVS;
while (dev_index < MAX_DEVS) {
(void) atomic_cas_ptr(&oce_dev_list[dev_index], NULL, dev);
if (oce_dev_list[dev_index] == dev) {
break;
}
dev_index++;
}
if (dev_index == MAX_DEVS) {
oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
"Too many oce devices on the system. Failed to attach.");
goto attach_fail;
}
dev->dev_list_index = dev_index;
oce_get_params(dev);
ddi_set_driver_private(dip, dev);
dev->attach_state |= ATTACH_DEV_INIT;
oce_fm_init(dev);
dev->attach_state |= ATTACH_FM_INIT;
ret = pci_config_setup(dev->dip, &dev->pci_cfg_handle);
if (ret != DDI_SUCCESS) {
oce_log(dev, CE_WARN, MOD_CONFIG,
"Map PCI config failed with %d", ret);
goto attach_fail;
}
dev->attach_state |= ATTACH_PCI_CFG;
ret = oce_identify_hw(dev);
if (ret != DDI_SUCCESS) {
oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
"Device Unknown");
goto attach_fail;
}
ret = oce_get_bdf(dev);
if (ret != DDI_SUCCESS) {
oce_log(dev, CE_WARN, MOD_CONFIG,
"Failed to read BDF, status = 0x%x", ret);
goto attach_fail;
}
oce_dev_rss_ready(dev);
ret = oce_pci_init(dev);
if (ret != DDI_SUCCESS) {
oce_log(dev, CE_WARN, MOD_CONFIG,
"PCI initialization failed with %d", ret);
goto attach_fail;
}
dev->attach_state |= ATTACH_PCI_INIT;
ret = oce_setup_intr(dev);
if (ret != DDI_SUCCESS) {
oce_log(dev, CE_WARN, MOD_CONFIG,
"Interrupt setup failed with %d", ret);
goto attach_fail;
}
dev->attach_state |= ATTACH_SETUP_INTR;
oce_init_locks(dev);
dev->attach_state |= ATTACH_LOCK_INIT;
ret = oce_hw_init(dev);
if (ret != DDI_SUCCESS) {
oce_log(dev, CE_WARN, MOD_CONFIG,
"HW initialization failed with %d", ret);
goto attach_fail;
}
dev->attach_state |= ATTACH_HW_INIT;
ret = oce_init_txrx(dev);
if (ret != DDI_SUCCESS) {
oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
"Failed to init rings");
goto attach_fail;
}
dev->attach_state |= ATTACH_SETUP_TXRX;
ret = oce_setup_adapter(dev);
if (ret != DDI_SUCCESS) {
oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
"Failed to setup adapter");
goto attach_fail;
}
dev->attach_state |= ATTACH_SETUP_ADAP;
ret = oce_stat_init(dev);
if (ret != DDI_SUCCESS) {
oce_log(dev, CE_WARN, MOD_CONFIG,
"kstat setup Failed with %d", ret);
goto attach_fail;
}
dev->attach_state |= ATTACH_STAT_INIT;
oce_log(dev, CE_NOTE, MOD_CONFIG,
"MAC_VERSION = 0x%x", MAC_VERSION);
mac = mac_alloc(MAC_VERSION);
if (mac == NULL) {
oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
"MAC allocation Failed");
goto attach_fail;
}
mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
mac->m_driver = dev;
mac->m_dip = dip;
mac->m_src_addr = dev->mac_addr;
mac->m_callbacks = &oce_mac_cb;
mac->m_min_sdu = 0;
mac->m_max_sdu = dev->mtu;
mac->m_margin = VTAG_SIZE;
mac->m_priv_props = oce_priv_props;
oce_log(dev, CE_NOTE, MOD_CONFIG,
"Driver Private structure = 0x%p", (void *)dev);
ret = mac_register(mac, (mac_handle_t *)&dev->mac_handle);
mac_free(mac);
mac = NULL;
if (ret != DDI_SUCCESS) {
oce_log(dev, CE_WARN, MOD_CONFIG,
"MAC registration failed :0x%x", ret);
goto attach_fail;
}
dev->link_status = LINK_STATE_UNKNOWN;
mac_link_update(dev->mac_handle, dev->link_status);
dev->attach_state |= ATTACH_MAC_REG;
dev->state |= STATE_INIT;
oce_log(dev, CE_NOTE, MOD_CONFIG, "%s",
"ATTACH SUCCESS");
return (DDI_SUCCESS);
attach_fail:
oce_unconfigure(dev);
return (DDI_FAILURE);
}
static int
oce_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
struct oce_dev *dev;
int pcnt = 0;
int qid;
dev = ddi_get_driver_private(dip);
if (dev == NULL) {
return (DDI_FAILURE);
}
oce_log(dev, CE_NOTE, MOD_CONFIG,
"Detaching driver: cmd = 0x%x", cmd);
switch (cmd) {
default:
return (DDI_FAILURE);
case DDI_SUSPEND:
return (oce_suspend(dip));
case DDI_DETACH:
break;
}
if (mac_unregister(dev->mac_handle) != 0) {
oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
"Failed to unregister MAC ");
}
dev->attach_state &= ~ATTACH_MAC_REG;
DEV_LOCK(dev);
if (dev->state & STATE_MAC_STARTED) {
dev->state &= ~STATE_MAC_STARTED;
oce_stop(dev);
DEV_UNLOCK(dev);
} else
DEV_UNLOCK(dev);
for (qid = 0; qid < dev->rx_rings; qid++) {
pcnt = oce_rx_pending(dev, dev->rq[qid], DEFAULT_DRAIN_TIME);
if (pcnt != 0) {
oce_log(dev, CE_WARN, MOD_CONFIG,
"%d Pending Buffers Detach failed", pcnt);
return (DDI_FAILURE);
}
}
oce_unconfigure(dev);
return (DDI_SUCCESS);
}
static int
oce_quiesce(dev_info_t *dip)
{
int ret = DDI_SUCCESS;
struct oce_dev *dev = ddi_get_driver_private(dip);
if (dev == NULL) {
return (DDI_FAILURE);
}
if (dev->suspended) {
return (DDI_SUCCESS);
}
oce_chip_di(dev);
ret = oce_reset_fun(dev);
return (ret);
}
static int
oce_suspend(dev_info_t *dip)
{
struct oce_dev *dev = ddi_get_driver_private(dip);
mutex_enter(&dev->dev_lock);
dev->suspended = B_TRUE;
if (dev->state & STATE_MAC_STARTED) {
oce_stop(dev);
oce_unsetup_adapter(dev);
}
dev->state &= ~STATE_MAC_STARTED;
mutex_exit(&dev->dev_lock);
return (DDI_SUCCESS);
}
static int
oce_resume(dev_info_t *dip)
{
struct oce_dev *dev;
int ret;
dev = ddi_get_driver_private(dip);
mutex_enter(&dev->dev_lock);
if (!dev->suspended) {
mutex_exit(&dev->dev_lock);
return (DDI_SUCCESS);
}
if (!(dev->state & STATE_MAC_STARTED)) {
ret = oce_setup_adapter(dev);
if (ret != DDI_SUCCESS) {
mutex_exit(&dev->dev_lock);
return (DDI_FAILURE);
}
ret = oce_start(dev);
if (ret != DDI_SUCCESS) {
mutex_exit(&dev->dev_lock);
return (DDI_FAILURE);
}
}
dev->suspended = B_FALSE;
dev->state |= STATE_MAC_STARTED;
mutex_exit(&dev->dev_lock);
return (ret);
}
static void
oce_init_locks(struct oce_dev *dev)
{
mutex_init(&dev->dev_lock, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(dev->intr_pri));
mutex_init(&dev->bmbx_lock, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(dev->intr_pri));
}
static void
oce_destroy_locks(struct oce_dev *dev)
{
mutex_destroy(&dev->dev_lock);
mutex_destroy(&dev->bmbx_lock);
}
static void
oce_unconfigure(struct oce_dev *dev)
{
uint32_t state = dev->attach_state;
if (state & ATTACH_MAC_REG) {
(void) mac_unregister(dev->mac_handle);
}
if (state & ATTACH_STAT_INIT) {
oce_stat_fini(dev);
}
if (state & ATTACH_SETUP_ADAP) {
oce_unsetup_adapter(dev);
}
if (state & ATTACH_SETUP_TXRX) {
oce_fini_txrx(dev);
}
if (state & ATTACH_HW_INIT) {
oce_hw_fini(dev);
}
if (state & ATTACH_LOCK_INIT) {
oce_destroy_locks(dev);
}
if (state & ATTACH_SETUP_INTR) {
(void) oce_teardown_intr(dev);
}
if (state & ATTACH_PCI_INIT) {
oce_pci_fini(dev);
}
if (state & ATTACH_PCI_CFG) {
pci_config_teardown(&dev->pci_cfg_handle);
}
if (state & ATTACH_FM_INIT) {
oce_fm_fini(dev);
}
if (state & ATTACH_DEV_INIT) {
ddi_set_driver_private(dev->dip, NULL);
oce_dev_list[dev->dev_list_index] = NULL;
kmem_free(dev, sizeof (struct oce_dev));
}
}
static void
oce_get_params(struct oce_dev *dev)
{
uint32_t log_level;
uint16_t mod_mask;
uint16_t severity;
uint32_t fc_values[] = {OCE_FC_NONE, OCE_FC_TX, OCE_FC_RX,
OCE_DEFAULT_FLOW_CONTROL, END};
uint32_t mtu_values[] = {OCE_MIN_MTU, OCE_MAX_MTU, END};
uint32_t tx_rs_values[] = {SIZE_256, SIZE_512, SIZE_1K, SIZE_2K, END};
uint32_t tx_bcl_values[] = {SIZE_128, SIZE_256, SIZE_512, SIZE_1K,
SIZE_2K, END};
uint32_t rx_bcl_values[] = {SIZE_128, SIZE_256, SIZE_512, SIZE_1K,
SIZE_2K, END};
uint32_t rq_fs_values[] = {SIZE_2K, SIZE_4K, SIZE_8K, END};
uint32_t rq_mb_values[] = {SIZE_2K, SIZE_4K, SIZE_8K, END};
uint32_t lso_capable_values[] = {0, 1, END};
uint32_t fm_caps_values[] = {DDI_FM_NOT_CAPABLE, OCE_FM_CAPABILITY,
END};
uint32_t tx_rt_values[] = {END};
uint32_t rx_ppi_values[] = {END};
uint32_t rx_rings_values[] = {END};
uint32_t tx_rings_values[] = {END};
uint32_t log_level_values[] = {END};
dev->rx_ring_size = OCE_DEFAULT_RX_RING_SIZE;
dev->flow_control = oce_get_prop(dev, (char *)flow_control, OCE_FC_NONE,
OCE_DEFAULT_FLOW_CONTROL, OCE_DEFAULT_FLOW_CONTROL, fc_values);
dev->mtu = oce_get_prop(dev, (char *)mtu_prop_name, OCE_MIN_MTU,
OCE_MAX_MTU, OCE_MIN_MTU, mtu_values);
dev->tx_ring_size = oce_get_prop(dev, (char *)tx_ring_size_name,
SIZE_256, SIZE_2K, OCE_DEFAULT_TX_RING_SIZE, tx_rs_values);
dev->tx_bcopy_limit = oce_get_prop(dev, (char *)tx_bcopy_limit_name,
SIZE_128, SIZE_2K, OCE_DEFAULT_TX_BCOPY_LIMIT, tx_bcl_values);
dev->rx_bcopy_limit = oce_get_prop(dev, (char *)rx_bcopy_limit_name,
SIZE_128, SIZE_2K, OCE_DEFAULT_RX_BCOPY_LIMIT, rx_bcl_values);
dev->rq_frag_size = oce_get_prop(dev, (char *)rx_frag_size_name,
SIZE_2K, SIZE_8K, OCE_RQ_BUF_SIZE, rq_fs_values);
dev->rq_max_bufs = oce_get_prop(dev, (char *)rx_max_bufs_name, SIZE_2K,
SIZE_8K, OCE_RQ_NUM_BUFFERS, rq_mb_values);
dev->lso_capable = oce_get_prop(dev, (char *)lso_capable_name, 0,
1, 1, lso_capable_values);
dev->fm_caps = oce_get_prop(dev, (char *)fm_cap_name,
DDI_FM_NOT_CAPABLE, OCE_FM_CAPABILITY, OCE_FM_CAPABILITY,
fm_caps_values);
dev->tx_reclaim_threshold = oce_get_prop(dev,
(char *)tx_reclaim_threshold_name, 0, dev->tx_ring_size/2,
OCE_DEFAULT_TX_RECLAIM_THRESHOLD, tx_rt_values);
dev->rx_pkt_per_intr = oce_get_prop(dev, (char *)rx_pkt_per_intr_name,
0, dev->rx_ring_size/2, OCE_DEFAULT_RX_PKT_PER_INTR, rx_ppi_values);
dev->rx_rings = oce_get_prop(dev, (char *)rx_rings_name,
OCE_MIN_RQ, OCE_MAX_RQ, OCE_DEFAULT_RQS, rx_rings_values);
dev->tx_rings = oce_get_prop(dev, (char *)tx_rings_name,
OCE_DEFAULT_WQS, OCE_DEFAULT_WQS, OCE_DEFAULT_WQS, tx_rings_values);
log_level = oce_get_prop(dev, (char *)log_level_name, 0,
OCE_MAX_LOG_SETTINGS, OCE_DEFAULT_LOG_SETTINGS, log_level_values);
severity = (uint16_t)(log_level & 0xffff);
mod_mask = (uint16_t)(log_level >> 16);
if (mod_mask > MOD_ISR) {
mod_mask = 0;
}
if (severity > CE_IGNORE) {
severity = 0;
}
dev->mod_mask = mod_mask;
dev->severity = severity;
}
static int
oce_get_prop(struct oce_dev *dev, char *propname, int minval, int maxval,
int defval, uint32_t *values)
{
int value = 0;
int i = 0;
value = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip,
DDI_PROP_DONTPASS, propname, defval);
if (value > maxval)
value = maxval;
if (value < minval)
value = minval;
while (values[i] != 0xdeadface) {
if (values[i] == value) {
break;
}
i++;
}
if ((i != 0) && (values[i] == 0xdeadface)) {
value = defval;
}
return (value);
}