enic: Add support to configure hardware interrupt coalesce timers in a platform independent way

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>
This commit is contained in:
Vasanthy Kolluri 2011-06-17 07:56:48 +00:00 committed by David S. Miller
parent 3fa2a1df90
commit ea7ea65a3b
11 changed files with 122 additions and 26 deletions

View 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

View 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;

View 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);

View 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
*/

View 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);
}

View 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) {

View 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);

View 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 */

View File

@ -20,10 +20,6 @@
#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;

View 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)

View 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_ */