enic: Add support to configure hardware interrupt coalesce timers in a platform indep...
authorVasanthy Kolluri <vkolluri@cisco.com>
Fri, 17 Jun 2011 07:56:48 +0000 (07:56 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 1 Jul 2011 03:43:38 +0000 (20:43 -0700)
enic driver and the underlying hardware use different units for representing the interrupt coalesce timer.
Driver converts the interrupt coalesce timer in usec to hardware cycles while setting the relevant hardware
registers. The conversion factor can be different for each of the adapter hardware types. So it is dynamically
learnt from the adapter firmware using the devcmd CMD_INTR_COAL_CONVERT. This allows the driver to configure
the hardware interrupt coalesce timers in a platform independent way.

Signed-off-by: Danny Guo <dannguo@cisco.com>
Signed-off-by: Vasanthy Kolluri <vkolluri@cisco.com>
Signed-off-by: Roopa Prabhu <roprabhu@cisco.com>
Signed-off-by: David Wang <dwang2@cisco.com>
Signed-off-by: David S. Miller <davem@conan.davemloft.net>
drivers/net/enic/enic.h
drivers/net/enic/enic_dev.c
drivers/net/enic/enic_dev.h
drivers/net/enic/enic_main.c
drivers/net/enic/enic_res.c
drivers/net/enic/vnic_dev.c
drivers/net/enic/vnic_dev.h
drivers/net/enic/vnic_devcmd.h
drivers/net/enic/vnic_enet.h
drivers/net/enic/vnic_intr.c
drivers/net/enic/vnic_intr.h

index f747bceb75b159abe2d45c7b1e9b07ae69b9d670..f0b062b4a2239cb24eaa64d4bbc5361e6169fd96 100644 (file)
@@ -32,7 +32,7 @@
 
 #define DRV_NAME               "enic"
 #define DRV_DESCRIPTION                "Cisco VIC Ethernet NIC Driver"
-#define DRV_VERSION            "2.1.1.20"
+#define DRV_VERSION            "2.1.1.24"
 #define DRV_COPYRIGHT          "Copyright 2008-2011 Cisco Systems, Inc"
 
 #define ENIC_BARS_MAX          6
index 90687b14e60f088fb701da094924b1be6c1d33bf..fd6247b3c0ee4dd4d105b7d1a827f2bd3e40d906 100644 (file)
@@ -166,6 +166,17 @@ int enic_dev_disable(struct enic *enic)
        return err;
 }
 
+int enic_dev_intr_coal_timer_info(struct enic *enic)
+{
+       int err;
+
+       spin_lock(&enic->devcmd_lock);
+       err = vnic_dev_intr_coal_timer_info(enic->vdev);
+       spin_unlock(&enic->devcmd_lock);
+
+       return err;
+}
+
 int enic_vnic_dev_deinit(struct enic *enic)
 {
        int err;
index d5f6813376263e7ce52dfdbf82675f4ccc3a81cd..ff8e87fdfc1d86cadec8619588758fef44fb1a37 100644 (file)
@@ -34,6 +34,7 @@ int enic_dev_hang_notify(struct enic *enic);
 int enic_dev_set_ig_vlan_rewrite_mode(struct enic *enic);
 int enic_dev_enable(struct enic *enic);
 int enic_dev_disable(struct enic *enic);
+int enic_dev_intr_coal_timer_info(struct enic *enic);
 int enic_vnic_dev_deinit(struct enic *enic);
 int enic_dev_init_prov2(struct enic *enic, struct vic_provinfo *vp);
 int enic_dev_deinit_done(struct enic *enic, int *status);
index 4b3a93a924e88811a0c547efb06b9a2af11566c1..e25800fa96cab80388b24cd74f11765fb5bc7d8e 100644 (file)
@@ -284,12 +284,10 @@ static int enic_set_coalesce(struct net_device *netdev,
        u32 rx_coalesce_usecs;
        unsigned int i, intr;
 
-       tx_coalesce_usecs = min_t(u32,
-               INTR_COALESCE_HW_TO_USEC(VNIC_INTR_TIMER_MAX),
-               ecmd->tx_coalesce_usecs);
-       rx_coalesce_usecs = min_t(u32,
-               INTR_COALESCE_HW_TO_USEC(VNIC_INTR_TIMER_MAX),
-               ecmd->rx_coalesce_usecs);
+       tx_coalesce_usecs = min_t(u32, ecmd->tx_coalesce_usecs,
+               vnic_dev_get_intr_coal_timer_max(enic->vdev));
+       rx_coalesce_usecs = min_t(u32, ecmd->rx_coalesce_usecs,
+               vnic_dev_get_intr_coal_timer_max(enic->vdev));
 
        switch (vnic_dev_get_intr_mode(enic->vdev)) {
        case VNIC_DEV_INTR_MODE_INTX:
@@ -298,26 +296,26 @@ static int enic_set_coalesce(struct net_device *netdev,
 
                intr = enic_legacy_io_intr();
                vnic_intr_coalescing_timer_set(&enic->intr[intr],
-                       INTR_COALESCE_USEC_TO_HW(tx_coalesce_usecs));
+                       tx_coalesce_usecs);
                break;
        case VNIC_DEV_INTR_MODE_MSI:
                if (tx_coalesce_usecs != rx_coalesce_usecs)
                        return -EINVAL;
 
                vnic_intr_coalescing_timer_set(&enic->intr[0],
-                       INTR_COALESCE_USEC_TO_HW(tx_coalesce_usecs));
+                       tx_coalesce_usecs);
                break;
        case VNIC_DEV_INTR_MODE_MSIX:
                for (i = 0; i < enic->wq_count; i++) {
                        intr = enic_msix_wq_intr(enic, i);
                        vnic_intr_coalescing_timer_set(&enic->intr[intr],
-                               INTR_COALESCE_USEC_TO_HW(tx_coalesce_usecs));
+                               tx_coalesce_usecs);
                }
 
                for (i = 0; i < enic->rq_count; i++) {
                        intr = enic_msix_rq_intr(enic, i);
                        vnic_intr_coalescing_timer_set(&enic->intr[intr],
-                               INTR_COALESCE_USEC_TO_HW(rx_coalesce_usecs));
+                               rx_coalesce_usecs);
                }
 
                break;
@@ -2175,6 +2173,14 @@ static int enic_dev_init(struct enic *enic)
        unsigned int i;
        int err;
 
+       /* Get interrupt coalesce timer info */
+       err = enic_dev_intr_coal_timer_info(enic);
+       if (err) {
+               dev_warn(dev, "Using default conversion factor for "
+                       "interrupt coalesce timer\n");
+               vnic_dev_intr_coal_timer_info_default(enic->vdev);
+       }
+
        /* Get vNIC configuration
         */
 
index 34f42072b60cac84c882a8c3f6bfc8dda92b7983..4a35367de790f957d6a64c1c665ed396979b3bfa 100644 (file)
@@ -90,9 +90,8 @@ int enic_get_vnic_config(struct enic *enic)
                max_t(u16, ENIC_MIN_MTU,
                c->mtu));
 
-       c->intr_timer_usec = min_t(u32,
-               INTR_COALESCE_HW_TO_USEC(VNIC_INTR_TIMER_MAX),
-               c->intr_timer_usec);
+       c->intr_timer_usec = min_t(u32, c->intr_timer_usec,
+               vnic_dev_get_intr_coal_timer_max(enic->vdev));
 
        dev_info(enic_get_dev(enic),
                "vNIC MAC addr %pM wq/rq %d/%d mtu %d\n",
@@ -303,7 +302,7 @@ void enic_init_vnic_resources(struct enic *enic)
 
        for (i = 0; i < enic->intr_count; i++) {
                vnic_intr_init(&enic->intr[i],
-                       INTR_COALESCE_USEC_TO_HW(enic->config.intr_timer_usec),
+                       enic->config.intr_timer_usec,
                        enic->config.intr_timer_type,
                        mask_on_assertion);
        }
index 68f24ae860aecc6c61698c41d9317793b2512a71..8c4c8cf486f68862a432924543b88c79085a0fa4 100644 (file)
@@ -40,6 +40,12 @@ struct vnic_res {
        unsigned int count;
 };
 
+struct vnic_intr_coal_timer_info {
+       u32 mul;
+       u32 div;
+       u32 max_usec;
+};
+
 struct vnic_dev {
        void *priv;
        struct pci_dev *pdev;
@@ -58,6 +64,7 @@ struct vnic_dev {
        enum vnic_proxy_type proxy;
        u32 proxy_index;
        u64 args[VNIC_DEVCMD_NARGS];
+       struct vnic_intr_coal_timer_info intr_coal_timer_info;
 };
 
 #define VNIC_MAX_RES_HDR_SIZE \
@@ -794,6 +801,42 @@ int vnic_dev_deinit(struct vnic_dev *vdev)
        return vnic_dev_cmd(vdev, CMD_DEINIT, &a0, &a1, wait);
 }
 
+void vnic_dev_intr_coal_timer_info_default(struct vnic_dev *vdev)
+{
+       /* Default: hardware intr coal timer is in units of 1.5 usecs */
+       vdev->intr_coal_timer_info.mul = 2;
+       vdev->intr_coal_timer_info.div = 3;
+       vdev->intr_coal_timer_info.max_usec =
+               vnic_dev_intr_coal_timer_hw_to_usec(vdev, 0xffff);
+}
+
+int vnic_dev_intr_coal_timer_info(struct vnic_dev *vdev)
+{
+       int wait = 1000;
+       int err;
+
+       memset(vdev->args, 0, sizeof(vdev->args));
+
+       err = _vnic_dev_cmd(vdev, CMD_INTR_COAL_CONVERT, wait);
+
+       /* Use defaults when firmware doesn't support the devcmd at all or
+        * supports it for only specific hardware
+        */
+       if ((err == ERR_ECMDUNKNOWN) ||
+               (!err && !(vdev->args[0] && vdev->args[1] && vdev->args[2]))) {
+               pr_warning("Using default conversion factor for "
+                       "interrupt coalesce timer\n");
+               vnic_dev_intr_coal_timer_info_default(vdev);
+               return 0;
+       }
+
+       vdev->intr_coal_timer_info.mul = (u32) vdev->args[0];
+       vdev->intr_coal_timer_info.div = (u32) vdev->args[1];
+       vdev->intr_coal_timer_info.max_usec = (u32) vdev->args[2];
+
+       return err;
+}
+
 int vnic_dev_link_status(struct vnic_dev *vdev)
 {
        if (!vnic_dev_notify_ready(vdev))
@@ -838,6 +881,23 @@ enum vnic_dev_intr_mode vnic_dev_get_intr_mode(
        return vdev->intr_mode;
 }
 
+u32 vnic_dev_intr_coal_timer_usec_to_hw(struct vnic_dev *vdev, u32 usec)
+{
+       return (usec * vdev->intr_coal_timer_info.mul) /
+               vdev->intr_coal_timer_info.div;
+}
+
+u32 vnic_dev_intr_coal_timer_hw_to_usec(struct vnic_dev *vdev, u32 hw_cycles)
+{
+       return (hw_cycles * vdev->intr_coal_timer_info.div) /
+               vdev->intr_coal_timer_info.mul;
+}
+
+u32 vnic_dev_get_intr_coal_timer_max(struct vnic_dev *vdev)
+{
+       return vdev->intr_coal_timer_info.max_usec;
+}
+
 void vnic_dev_unregister(struct vnic_dev *vdev)
 {
        if (vdev) {
index cf482a2c9dd9bcd3d439dc7118ca0e3e70336502..852b698fbe7dd4cc19bf15cf60114841d638eef9 100644 (file)
@@ -109,11 +109,16 @@ int vnic_dev_open(struct vnic_dev *vdev, int arg);
 int vnic_dev_open_done(struct vnic_dev *vdev, int *done);
 int vnic_dev_init(struct vnic_dev *vdev, int arg);
 int vnic_dev_deinit(struct vnic_dev *vdev);
+void vnic_dev_intr_coal_timer_info_default(struct vnic_dev *vdev);
+int vnic_dev_intr_coal_timer_info(struct vnic_dev *vdev);
 int vnic_dev_hang_reset(struct vnic_dev *vdev, int arg);
 int vnic_dev_hang_reset_done(struct vnic_dev *vdev, int *done);
 void vnic_dev_set_intr_mode(struct vnic_dev *vdev,
        enum vnic_dev_intr_mode intr_mode);
 enum vnic_dev_intr_mode vnic_dev_get_intr_mode(struct vnic_dev *vdev);
+u32 vnic_dev_intr_coal_timer_usec_to_hw(struct vnic_dev *vdev, u32 usec);
+u32 vnic_dev_intr_coal_timer_hw_to_usec(struct vnic_dev *vdev, u32 hw_cycles);
+u32 vnic_dev_get_intr_coal_timer_max(struct vnic_dev *vdev);
 void vnic_dev_unregister(struct vnic_dev *vdev);
 int vnic_dev_set_ig_vlan_rewrite_mode(struct vnic_dev *vdev,
        u8 ig_vlan_rewrite_mode);
index c5569bfb47ac1d7466486e650dd70b03660b2d6a..8025e8808d61718d1bade2124dde9a89d3abbd3f 100644 (file)
@@ -318,6 +318,25 @@ enum vnic_devcmd_cmd {
         *             ERR_EINPROGRESS - command in a0 is still in progress
         */
        CMD_STATUS = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 49),
+
+       /*
+        * Returns interrupt coalescing timer conversion factors.
+        * After calling this devcmd, ENIC driver can convert
+        * interrupt coalescing timer in usec into CPU cycles as follows:
+        *
+        *   intr_timer_cycles = intr_timer_usec * multiplier / divisor
+        *
+        * Interrupt coalescing timer in usecs can be obtained from
+        * CPU cycles as follows:
+        *
+        *   intr_timer_usec = intr_timer_cycles * divisor / multiplier
+        *
+        * in: none
+        * out: (u32)a0 = multiplier
+        *      (u32)a1 = divisor
+        *      (u32)a2 = maximum timer value in usec
+        */
+       CMD_INTR_COAL_CONVERT = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 50),
 };
 
 /* CMD_ENABLE2 flags */
index 061ad87807967c35f99f42bd2a375d8e57ad310b..609542848e029de6289051ba198e39e7a2e58291 100644 (file)
 #ifndef _VNIC_ENIC_H_
 #define _VNIC_ENIC_H_
 
-/* Hardware intr coalesce timer is in units of 1.5us */
-#define INTR_COALESCE_USEC_TO_HW(usec) ((usec) * 2/3)
-#define INTR_COALESCE_HW_TO_USEC(usec) ((usec) * 3/2)
-
 /* Device-specific region: enet configuration */
 struct vnic_enet_config {
        u32 flags;
index 3873771d75cc4208abfa70d6bcc4e106e73fd227..0ca107f7bc8ca33851e7c26339d4fa48ee0fced4 100644 (file)
@@ -46,7 +46,7 @@ int vnic_intr_alloc(struct vnic_dev *vdev, struct vnic_intr *intr,
        return 0;
 }
 
-void vnic_intr_init(struct vnic_intr *intr, unsigned int coalescing_timer,
+void vnic_intr_init(struct vnic_intr *intr, u32 coalescing_timer,
        unsigned int coalescing_type, unsigned int mask_on_assertion)
 {
        vnic_intr_coalescing_timer_set(intr, coalescing_timer);
@@ -56,9 +56,10 @@ void vnic_intr_init(struct vnic_intr *intr, unsigned int coalescing_timer,
 }
 
 void vnic_intr_coalescing_timer_set(struct vnic_intr *intr,
-       unsigned int coalescing_timer)
+       u32 coalescing_timer)
 {
-       iowrite32(coalescing_timer, &intr->ctrl->coalescing_timer);
+       iowrite32(vnic_dev_intr_coal_timer_usec_to_hw(intr->vdev,
+               coalescing_timer), &intr->ctrl->coalescing_timer);
 }
 
 void vnic_intr_clean(struct vnic_intr *intr)
index 09dc0b73ff46aec338fc0478a52d85955ea350ba..2b163639229414c5fa2452de48742159ea0ae2fe 100644 (file)
@@ -24,8 +24,6 @@
 
 #include "vnic_dev.h"
 
-#define VNIC_INTR_TIMER_MAX            0xffff
-
 #define VNIC_INTR_TIMER_TYPE_ABS       0
 #define VNIC_INTR_TIMER_TYPE_QUIET     1
 
@@ -104,10 +102,10 @@ static inline u32 vnic_intr_legacy_pba(u32 __iomem *legacy_pba)
 void vnic_intr_free(struct vnic_intr *intr);
 int vnic_intr_alloc(struct vnic_dev *vdev, struct vnic_intr *intr,
        unsigned int index);
-void vnic_intr_init(struct vnic_intr *intr, unsigned int coalescing_timer,
+void vnic_intr_init(struct vnic_intr *intr, u32 coalescing_timer,
        unsigned int coalescing_type, unsigned int mask_on_assertion);
 void vnic_intr_coalescing_timer_set(struct vnic_intr *intr,
-       unsigned int coalescing_timer);
+       u32 coalescing_timer);
 void vnic_intr_clean(struct vnic_intr *intr);
 
 #endif /* _VNIC_INTR_H_ */