Skip to content

Commit

Permalink
enic: Add support to configure hardware interrupt coalesce timers in …
Browse files Browse the repository at this point in the history
…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>
  • Loading branch information
Vasanthy Kolluri authored and David S. Miller committed Jul 1, 2011
1 parent 3fa2a1d commit ea7ea65
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 26 deletions.
2 changes: 1 addition & 1 deletion drivers/net/enic/enic.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions drivers/net/enic/enic_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions drivers/net/enic/enic_dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
26 changes: 16 additions & 10 deletions drivers/net/enic/enic_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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;
Expand Down Expand Up @@ -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
*/

Expand Down
7 changes: 3 additions & 4 deletions drivers/net/enic/enic_res.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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);
}
Expand Down
60 changes: 60 additions & 0 deletions drivers/net/enic/vnic_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 \
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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) {
Expand Down
5 changes: 5 additions & 0 deletions drivers/net/enic/vnic_dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
19 changes: 19 additions & 0 deletions drivers/net/enic/vnic_devcmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
4 changes: 0 additions & 4 deletions drivers/net/enic/vnic_enet.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 4 additions & 3 deletions drivers/net/enic/vnic_intr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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)
Expand Down
6 changes: 2 additions & 4 deletions drivers/net/enic/vnic_intr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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_ */

0 comments on commit ea7ea65

Please sign in to comment.