aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/cxgb/cxgb_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/cxgb/cxgb_main.c')
-rw-r--r--sys/dev/cxgb/cxgb_main.c358
1 files changed, 177 insertions, 181 deletions
diff --git a/sys/dev/cxgb/cxgb_main.c b/sys/dev/cxgb/cxgb_main.c
index cc09aa0438f5..3efcbb73ccf5 100644
--- a/sys/dev/cxgb/cxgb_main.c
+++ b/sys/dev/cxgb/cxgb_main.c
@@ -115,7 +115,7 @@ static int offload_open(struct port_info *pi);
static void touch_bars(device_t dev);
static int offload_close(struct t3cdev *tdev);
static void cxgb_link_start(struct port_info *p);
-static void cxgb_link_fault(void *arg, int ncount);
+int t3_detect_link_fault(adapter_t *adapter, int port_id);
static device_method_t cxgb_controller_methods[] = {
DEVMETHOD(device_probe, cxgb_controller_probe),
@@ -392,6 +392,32 @@ upgrade_fw(adapter_t *sc)
return (status);
}
+/*
+ * The cxgb_controller_attach function is responsible for the initial
+ * bringup of the device. Its responsibilities include:
+ *
+ * 1. Determine if the device supports MSI or MSI-X.
+ * 2. Allocate bus resources so that we can access the Base Address Register
+ * 3. Create and initialize mutexes for the controller and its control
+ * logic such as SGE and MDIO.
+ * 4. Call hardware specific setup routine for the adapter as a whole.
+ * 5. Allocate the BAR for doing MSI-X.
+ * 6. Setup the line interrupt iff MSI-X is not supported.
+ * 7. Create the driver's taskq.
+ * 8. Start one task queue service thread.
+ * 9. Check if the firmware and SRAM are up-to-date. They will be
+ * auto-updated later (before FULL_INIT_DONE), if required.
+ * 10. Create a child device for each MAC (port)
+ * 11. Initialize T3 private state.
+ * 12. Trigger the LED
+ * 13. Setup offload iff supported.
+ * 14. Reset/restart the tick callout.
+ * 15. Attach sysctls
+ *
+ * NOTE: Any modification or deviation from this list MUST be reflected in
+ * the above comment. Failure to do so will result in problems on various
+ * error conditions including link flapping.
+ */
static int
cxgb_controller_attach(device_t dev)
{
@@ -625,6 +651,10 @@ cxgb_controller_attach(device_t dev)
sc->params.vpd.ec, sc->params.vpd.sn);
device_set_desc_copy(dev, buf);
+ snprintf(&sc->port_types[0], sizeof(sc->port_types), "%x%x%x%x",
+ sc->params.vpd.port_type[0], sc->params.vpd.port_type[1],
+ sc->params.vpd.port_type[2], sc->params.vpd.port_type[3]);
+
device_printf(sc->dev, "Firmware Version %s\n", &sc->fw_version[0]);
callout_reset(&sc->cxgb_tick_ch, CXGB_TICKS(sc), cxgb_tick, sc);
t3_add_attach_sysctls(sc);
@@ -635,6 +665,11 @@ out:
return (error);
}
+/*
+ * The cxgb_controller_detach routine is called with the device is
+ * unloaded from the system.
+ */
+
static int
cxgb_controller_detach(device_t dev)
{
@@ -647,6 +682,24 @@ cxgb_controller_detach(device_t dev)
return (0);
}
+/*
+ * The cxgb_free() is called by the cxgb_controller_detach() routine
+ * to tear down the structures that were built up in
+ * cxgb_controller_attach(), and should be the final piece of work
+ * done when fully unloading the driver.
+ *
+ *
+ * 1. Shutting down the threads started by the cxgb_controller_attach()
+ * routine.
+ * 2. Stopping the lower level device and all callouts (cxgb_down_locked()).
+ * 3. Detaching all of the port devices created during the
+ * cxgb_controller_attach() routine.
+ * 4. Removing the device children created via cxgb_controller_attach().
+ * 5. Releaseing PCI resources associated with the device.
+ * 6. Turning off the offload support, iff it was turned on.
+ * 7. Destroying the mutexes created in cxgb_controller_attach().
+ *
+ */
static void
cxgb_free(struct adapter *sc)
{
@@ -655,14 +708,28 @@ cxgb_free(struct adapter *sc)
ADAPTER_LOCK(sc);
sc->flags |= CXGB_SHUTDOWN;
ADAPTER_UNLOCK(sc);
+
cxgb_pcpu_shutdown_threads(sc);
- ADAPTER_LOCK(sc);
-/*
- * drops the lock
- */
+ ADAPTER_LOCK(sc);
cxgb_down_locked(sc);
+ ADAPTER_UNLOCK(sc);
+
+ t3_sge_deinit_sw(sc);
+ /*
+ * Wait for last callout
+ */
+ DELAY(hz*100);
+
+ bus_generic_detach(sc->dev);
+
+ for (i = 0; i < (sc)->params.nports; i++) {
+ if (sc->portdev[i] &&
+ device_delete_child(sc->dev, sc->portdev[i]) != 0)
+ device_printf(sc->dev, "failed to delete child port\n");
+ }
+
#ifdef MSI_SUPPORTED
if (sc->flags & (USING_MSI | USING_MSIX)) {
device_printf(sc->dev, "releasing msi message(s)\n");
@@ -676,19 +743,6 @@ cxgb_free(struct adapter *sc)
sc->msix_regs_res);
}
- t3_sge_deinit_sw(sc);
- /*
- * Wait for last callout
- */
-
- DELAY(hz*100);
-
- for (i = 0; i < (sc)->params.nports; ++i) {
- if (sc->portdev[i] != NULL)
- device_delete_child(sc->dev, sc->portdev[i]);
- }
-
- bus_generic_detach(sc->dev);
if (sc->tq != NULL) {
taskqueue_free(sc->tq);
sc->tq = NULL;
@@ -957,6 +1011,7 @@ cxgb_port_attach(device_t dev)
}
ether_ifattach(ifp, p->hw_addr);
+
#ifdef IFNET_MULTIQUEUE
ifp->if_transmit = cxgb_pcpu_transmit;
#endif
@@ -1020,40 +1075,104 @@ cxgb_port_attach(device_t dev)
bcopy(IF_LLADDR(p->ifp), p->hw_addr, ETHER_ADDR_LEN);
t3_sge_init_port(p);
- TASK_INIT(&p->link_fault_task, 0, cxgb_link_fault, p);
+ /* If it's MSI or INTx, allocate a single interrupt for everything */
+ if ((sc->flags & USING_MSIX) == 0) {
+ if ((sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
+ &sc->irq_rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) {
+ device_printf(sc->dev, "Cannot allocate interrupt rid=%d\n",
+ sc->irq_rid);
+ err = EINVAL;
+ goto out;
+ }
+ device_printf(sc->dev, "allocated irq_res=%p\n", sc->irq_res);
+
+ if (bus_setup_intr(sc->dev, sc->irq_res, INTR_MPSAFE|INTR_TYPE_NET,
+#ifdef INTR_FILTERS
+ NULL,
+#endif
+ sc->cxgb_intr, sc, &sc->intr_tag)) {
+ device_printf(sc->dev, "Cannot set up interrupt\n");
+ err = EINVAL;
+ goto irq_err;
+ }
+ } else {
+ cxgb_setup_msix(sc, sc->msi_count);
+ }
#if defined(LINK_ATTACH)
cxgb_link_start(p);
t3_link_changed(sc, p->port_id);
#endif
- return (0);
+out:
+ return (err);
+irq_err:
+ CH_ERR(sc, "request_irq failed, err %d\n", err);
+ goto out;
}
+/*
+ * cxgb_port_detach() is called via the device_detach methods when
+ * cxgb_free() calls the bus_generic_detach. It is responsible for
+ * removing the device from the view of the kernel, i.e. from all
+ * interfaces lists etc. This routine is only called when the driver is
+ * being unloaded, not when the link goes down.
+ *
+ */
static int
cxgb_port_detach(device_t dev)
{
struct port_info *p;
+ struct adapter *sc;
p = device_get_softc(dev);
+ sc = p->adapter;
+
+ if (p->port_cdev != NULL)
+ destroy_dev(p->port_cdev);
+
+ ether_ifdetach(p->ifp);
+ printf("waiting for callout to stop ...");
+ printf("done\n");
PORT_LOCK(p);
if (p->ifp->if_drv_flags & IFF_DRV_RUNNING)
cxgb_stop_locked(p);
PORT_UNLOCK(p);
- ether_ifdetach(p->ifp);
- printf("waiting for callout to stop ...");
- DELAY(1000000);
- printf("done\n");
+ if (sc->intr_tag != NULL) {
+ bus_teardown_intr(sc->dev, sc->irq_res, sc->intr_tag);
+ sc->intr_tag = NULL;
+ }
+ if (sc->irq_res != NULL) {
+ device_printf(sc->dev, "de-allocating interrupt irq_rid=%d irq_res=%p\n",
+ sc->irq_rid, sc->irq_res);
+ bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irq_rid,
+ sc->irq_res);
+ sc->irq_res = NULL;
+ }
+
+ if (sc->flags & USING_MSIX)
+ cxgb_teardown_msix(sc);
+
+ callout_drain(&sc->cxgb_tick_ch);
+ callout_drain(&sc->sge_timer_ch);
+
+ if (sc->tq != NULL) {
+ printf("draining slow intr\n");
+
+ taskqueue_drain(sc->tq, &sc->slow_intr_task);
+ printf("draining ext intr\n");
+ taskqueue_drain(sc->tq, &sc->ext_intr_task);
+ printf("draining tick task\n");
+ taskqueue_drain(sc->tq, &sc->tick_task);
+ }
+
/*
* the lock may be acquired in ifdetach
*/
PORT_LOCK_DEINIT(p);
if_free(p->ifp);
- if (p->port_cdev != NULL)
- destroy_dev(p->port_cdev);
-
return (0);
}
@@ -1142,32 +1261,6 @@ t3_os_pci_restore_state(struct adapter *sc)
return (0);
}
-void t3_os_link_fault(struct adapter *adap, int port_id, int state)
-{
- struct port_info *pi = &adap->port[port_id];
-
- if (!state) {
- if_link_state_change(pi->ifp, LINK_STATE_DOWN);
- return;
- }
-
- if (adap->params.nports <= 2) {
- struct cmac *mac = &pi->mac;
-
- /* Clear local faults */
- t3_xgm_intr_disable(adap, port_id);
- t3_read_reg(adap, A_XGM_INT_STATUS + pi->mac.offset);
- t3_write_reg(adap, A_XGM_INT_CAUSE + pi->mac.offset, F_XGM_INT);
-
- t3_set_reg_field(adap, A_XGM_INT_ENABLE + pi->mac.offset,
- F_XGM_INT, F_XGM_INT);
- t3_xgm_intr_enable(adap, pi->port_id);
- t3_mac_enable(mac, MAC_DIRECTION_TX);
- }
-
- if_link_state_change(pi->ifp, LINK_STATE_UP);
-}
-
/**
* t3_os_link_changed - handle link status changes
* @adapter: the adapter associated with the link change
@@ -1186,48 +1279,12 @@ t3_os_link_changed(adapter_t *adapter, int port_id, int link_status, int speed,
int duplex, int fc)
{
struct port_info *pi = &adapter->port[port_id];
- struct cmac *mac = &adapter->port[port_id].mac;
if (link_status) {
- DELAY(10);
- t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX);
- /* Clear errors created by MAC enable */
- t3_set_reg_field(adapter, A_XGM_STAT_CTRL + pi->mac.offset,
- F_CLRSTATS, 1);
-
- if (adapter->params.nports <= 2) {
- /* Clear local faults */
- t3_xgm_intr_disable(adapter, pi->port_id);
- t3_read_reg(adapter, A_XGM_INT_STATUS + pi->mac.offset);
- t3_write_reg(adapter, A_XGM_INT_CAUSE + pi->mac.offset,
- F_XGM_INT);
-
- t3_set_reg_field(adapter,
- A_XGM_INT_ENABLE + pi->mac.offset,
- F_XGM_INT, F_XGM_INT);
- t3_xgm_intr_enable(adapter, pi->port_id);
- }
-
+ pi->ifp->if_baudrate = IF_Mbps(speed);
if_link_state_change(pi->ifp, LINK_STATE_UP);
- } else {
- t3_xgm_intr_disable(adapter, pi->port_id);
- t3_read_reg(adapter, A_XGM_INT_STATUS + pi->mac.offset);
- if (adapter->params.nports <= 2) {
- t3_set_reg_field(adapter,
- A_XGM_INT_ENABLE + pi->mac.offset,
- F_XGM_INT, 0);
- }
-
- /* PR 5666. We shouldn't power down 1G phys */
- if (is_10G(adapter))
- pi->phy.ops->power_down(&pi->phy, 1);
-
- t3_read_reg(adapter, A_XGM_INT_STATUS + pi->mac.offset);
- t3_mac_disable(mac, MAC_DIRECTION_RX);
- t3_link_start(&pi->phy, mac, &pi->link_config);
-
+ } else
if_link_state_change(pi->ifp, LINK_STATE_DOWN);
- }
}
/**
@@ -1280,22 +1337,6 @@ t3_os_ext_intr_handler(adapter_t *sc)
ADAPTER_UNLOCK(sc);
}
-static void
-cxgb_link_fault(void *arg, int ncount)
-{
- struct port_info *pi = arg;
-
- t3_link_fault(pi->adapter, pi->port_id);
-}
-
-void t3_os_link_fault_handler(struct adapter *sc, int port_id)
-{
- struct port_info *pi = &sc->port[port_id];
-
- pi->link_fault = 1;
- taskqueue_enqueue(sc->tq, &pi->link_fault_task);
-}
-
void
t3_os_set_hw_addr(adapter_t *adapter, int port_idx, u8 hw_addr[])
{
@@ -1705,30 +1746,6 @@ cxgb_up(struct adapter *sc)
t3_intr_clear(sc);
- /* If it's MSI or INTx, allocate a single interrupt for everything */
- if ((sc->flags & USING_MSIX) == 0) {
- if ((sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
- &sc->irq_rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) {
- device_printf(sc->dev, "Cannot allocate interrupt rid=%d\n",
- sc->irq_rid);
- err = EINVAL;
- goto out;
- }
- device_printf(sc->dev, "allocated irq_res=%p\n", sc->irq_res);
-
- if (bus_setup_intr(sc->dev, sc->irq_res, INTR_MPSAFE|INTR_TYPE_NET,
-#ifdef INTR_FILTERS
- NULL,
-#endif
- sc->cxgb_intr, sc, &sc->intr_tag)) {
- device_printf(sc->dev, "Cannot set up interrupt\n");
- err = EINVAL;
- goto irq_err;
- }
- } else {
- cxgb_setup_msix(sc, sc->msi_count);
- }
-
t3_sge_start(sc);
t3_intr_enable(sc);
@@ -1749,14 +1766,11 @@ cxgb_up(struct adapter *sc)
}
out:
return (err);
-irq_err:
- CH_ERR(sc, "request_irq failed, err %d\n", err);
- goto out;
}
/*
- * Release resources when all the ports and offloading have been stopped.
+ * Bring down the interface but do not free any resources.
*/
static void
cxgb_down_locked(struct adapter *sc)
@@ -1765,36 +1779,8 @@ cxgb_down_locked(struct adapter *sc)
t3_sge_stop(sc);
t3_intr_disable(sc);
- if (sc->intr_tag != NULL) {
- bus_teardown_intr(sc->dev, sc->irq_res, sc->intr_tag);
- sc->intr_tag = NULL;
- }
- if (sc->irq_res != NULL) {
- device_printf(sc->dev, "de-allocating interrupt irq_rid=%d irq_res=%p\n",
- sc->irq_rid, sc->irq_res);
- bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irq_rid,
- sc->irq_res);
- sc->irq_res = NULL;
- }
-
- if (sc->flags & USING_MSIX)
- cxgb_teardown_msix(sc);
-
callout_stop(&sc->cxgb_tick_ch);
callout_stop(&sc->sge_timer_ch);
- callout_drain(&sc->cxgb_tick_ch);
- callout_drain(&sc->sge_timer_ch);
-
- if (sc->tq != NULL) {
- printf("draining slow intr\n");
-
- taskqueue_drain(sc->tq, &sc->slow_intr_task);
- printf("draining ext intr\n");
- taskqueue_drain(sc->tq, &sc->ext_intr_task);
- printf("draining tick task\n");
- taskqueue_drain(sc->tq, &sc->tick_task);
- }
- ADAPTER_UNLOCK(sc);
}
static int
@@ -1861,8 +1847,9 @@ offload_close(struct t3cdev *tdev)
ADAPTER_LOCK(adapter);
if (!adapter->open_device_map)
cxgb_down_locked(adapter);
- else
- ADAPTER_UNLOCK(adapter);
+
+ ADAPTER_UNLOCK(adapter);
+
return (0);
}
@@ -1905,15 +1892,16 @@ cxgb_init_locked(struct port_info *p)
log(LOG_WARNING,
"Could not initialize offload capabilities\n");
}
+
+ device_printf(sc->dev, "enabling interrupts on port=%d\n", p->port_id);
+ t3_port_intr_enable(sc, p->port_id);
+
#if !defined(LINK_ATTACH)
cxgb_link_start(p);
t3_link_changed(sc, p->port_id);
#endif
ifp->if_baudrate = IF_Mbps(p->link_config.speed);
- device_printf(sc->dev, "enabling interrupts on port=%d\n", p->port_id);
- t3_port_intr_enable(sc, p->port_id);
-
callout_reset(&sc->cxgb_tick_ch, CXGB_TICKS(sc), cxgb_tick, sc);
t3_sge_reset_adapter(sc);
@@ -1957,10 +1945,10 @@ cxgb_stop_locked(struct port_info *pi)
ADAPTER_LOCK(pi->adapter);
clrbit(&pi->adapter->open_device_map, pi->port_id);
- if (pi->adapter->open_device_map == 0) {
+ if (pi->adapter->open_device_map == 0)
cxgb_down_locked(pi->adapter);
- } else
- ADAPTER_UNLOCK(pi->adapter);
+
+ ADAPTER_UNLOCK(pi->adapter);
#if !defined(LINK_ATTACH)
DELAY(100);
@@ -2277,12 +2265,23 @@ check_link_status(adapter_t *sc)
{
int i;
+ /* For synchronized access to open_device_map */
+ ADAPTER_LOCK_ASSERT_OWNED(sc);
+
for (i = 0; i < (sc)->params.nports; ++i) {
struct port_info *p = &sc->port[i];
+ struct link_config *lc = &p->link_config;
- if (!(p->phy.caps & SUPPORTED_IRQ))
+ if (!isset(&sc->open_device_map, p->port_id)) {
+ /*
+ * port is down, report link down too. Note
+ * that we do this for IRQ based PHYs too.
+ */
+ lc->link_ok = 0;
+ t3_os_link_changed(sc, i, lc->link_ok, lc->speed,
+ lc->duplex, lc->fc);
+ } else if (p->link_fault || !(p->phy.caps & SUPPORTED_IRQ))
t3_link_changed(sc, i);
- p->ifp->if_baudrate = IF_Mbps(p->link_config.speed);
}
}
@@ -2349,12 +2348,12 @@ cxgb_tick_handler(void *arg, int count)
int i;
uint32_t cause, reset;
- if(sc->flags & CXGB_SHUTDOWN)
+ if(sc->flags & CXGB_SHUTDOWN || !(sc->flags & FULL_INIT_DONE))
return;
ADAPTER_LOCK(sc);
- if (p->linkpoll_period)
- check_link_status(sc);
+
+ check_link_status(sc);
sc->check_task_cnt++;
@@ -2396,9 +2395,6 @@ cxgb_tick_handler(void *arg, int count)
t3_mac_update_stats(mac);
PORT_UNLOCK(pi);
- if (pi->link_fault)
- taskqueue_enqueue(sc->tq, &pi->link_fault_task);
-
ifp->if_opackets =
mstats->tx_frames_64 +
mstats->tx_frames_65_127 +
@@ -2528,13 +2524,13 @@ in_range(int val, int lo, int hi)
}
static int
-cxgb_extension_open(struct cdev *dev, int flags, int fmp, d_thread_t *td)
+cxgb_extension_open(struct cdev *dev, int flags, int fmp, struct thread *td)
{
return (0);
}
static int
-cxgb_extension_close(struct cdev *dev, int flags, int fmt, d_thread_t *td)
+cxgb_extension_close(struct cdev *dev, int flags, int fmt, struct thread *td)
{
return (0);
}