aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/enic
diff options
context:
space:
mode:
authorScott Feldman <scofeldm@cisco.com>2009-09-03 13:01:58 -0400
committerDavid S. Miller <davem@davemloft.net>2009-09-03 23:19:10 -0400
commit4badc385d1a9e140ad0992537237fc22211adad0 (patch)
tree5edf87f3240b4ad5c999e025c47d379f09f91296 /drivers/net/enic
parent27e6c7d33835e7f347cdfb5025766b7d9a6596d1 (diff)
enic: workaround A0 erratum
A0 revision ASIC has an erratum on the RQ desc cache on chip where the cache can become corrupted causing pkt buf writes to wrong locations. The s/w workaround is to post a dummy RQ desc in the ring every 32 descs, causing a flush of the cache. A0 parts are not production, but there are enough of these parts in the wild in test setups to warrant including workaround. A1 revision ASIC parts fix erratum. Signed-off-by: Scott Feldman <scofeldm@cisco.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/enic')
-rw-r--r--drivers/net/enic/enic_main.c50
-rw-r--r--drivers/net/enic/vnic_dev.c19
-rw-r--r--drivers/net/enic/vnic_dev.h8
-rw-r--r--drivers/net/enic/vnic_rq.h7
4 files changed, 80 insertions, 4 deletions
diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c
index 2821a1db547d..58cae6e6a59c 100644
--- a/drivers/net/enic/enic_main.c
+++ b/drivers/net/enic/enic_main.c
@@ -851,6 +851,50 @@ static int enic_rq_alloc_buf(struct vnic_rq *rq)
851 return 0; 851 return 0;
852} 852}
853 853
854static int enic_rq_alloc_buf_a1(struct vnic_rq *rq)
855{
856 struct rq_enet_desc *desc = vnic_rq_next_desc(rq);
857
858 if (vnic_rq_posting_soon(rq)) {
859
860 /* SW workaround for A0 HW erratum: if we're just about
861 * to write posted_index, insert a dummy desc
862 * of type resvd
863 */
864
865 rq_enet_desc_enc(desc, 0, RQ_ENET_TYPE_RESV2, 0);
866 vnic_rq_post(rq, 0, 0, 0, 0);
867 } else {
868 return enic_rq_alloc_buf(rq);
869 }
870
871 return 0;
872}
873
874static int enic_set_rq_alloc_buf(struct enic *enic)
875{
876 enum vnic_dev_hw_version hw_ver;
877 int err;
878
879 err = vnic_dev_hw_version(enic->vdev, &hw_ver);
880 if (err)
881 return err;
882
883 switch (hw_ver) {
884 case VNIC_DEV_HW_VER_A1:
885 enic->rq_alloc_buf = enic_rq_alloc_buf_a1;
886 break;
887 case VNIC_DEV_HW_VER_A2:
888 case VNIC_DEV_HW_VER_UNKNOWN:
889 enic->rq_alloc_buf = enic_rq_alloc_buf;
890 break;
891 default:
892 return -ENODEV;
893 }
894
895 return 0;
896}
897
854static int enic_get_skb_header(struct sk_buff *skb, void **iphdr, 898static int enic_get_skb_header(struct sk_buff *skb, void **iphdr,
855 void **tcph, u64 *hdr_flags, void *priv) 899 void **tcph, u64 *hdr_flags, void *priv)
856{ 900{
@@ -1058,7 +1102,7 @@ static int enic_poll(struct napi_struct *napi, int budget)
1058 /* Replenish RQ 1102 /* Replenish RQ
1059 */ 1103 */
1060 1104
1061 vnic_rq_fill(&enic->rq[0], enic_rq_alloc_buf); 1105 vnic_rq_fill(&enic->rq[0], enic->rq_alloc_buf);
1062 1106
1063 } else { 1107 } else {
1064 1108
@@ -1093,7 +1137,7 @@ static int enic_poll_msix(struct napi_struct *napi, int budget)
1093 /* Replenish RQ 1137 /* Replenish RQ
1094 */ 1138 */
1095 1139
1096 vnic_rq_fill(&enic->rq[0], enic_rq_alloc_buf); 1140 vnic_rq_fill(&enic->rq[0], enic->rq_alloc_buf);
1097 1141
1098 /* Return intr event credits for this polling 1142 /* Return intr event credits for this polling
1099 * cycle. An intr event is the completion of a 1143 * cycle. An intr event is the completion of a
@@ -1269,7 +1313,7 @@ static int enic_open(struct net_device *netdev)
1269 } 1313 }
1270 1314
1271 for (i = 0; i < enic->rq_count; i++) { 1315 for (i = 0; i < enic->rq_count; i++) {
1272 err = vnic_rq_fill(&enic->rq[i], enic_rq_alloc_buf); 1316 err = vnic_rq_fill(&enic->rq[i], enic->rq_alloc_buf);
1273 if (err) { 1317 if (err) {
1274 printk(KERN_ERR PFX 1318 printk(KERN_ERR PFX
1275 "%s: Unable to alloc receive buffers.\n", 1319 "%s: Unable to alloc receive buffers.\n",
diff --git a/drivers/net/enic/vnic_dev.c b/drivers/net/enic/vnic_dev.c
index d5c28efedd98..c8d3fc7517b0 100644
--- a/drivers/net/enic/vnic_dev.c
+++ b/drivers/net/enic/vnic_dev.c
@@ -349,6 +349,25 @@ int vnic_dev_fw_info(struct vnic_dev *vdev,
349 return err; 349 return err;
350} 350}
351 351
352int vnic_dev_hw_version(struct vnic_dev *vdev, enum vnic_dev_hw_version *hw_ver)
353{
354 struct vnic_devcmd_fw_info *fw_info;
355 int err;
356
357 err = vnic_dev_fw_info(vdev, &fw_info);
358 if (err)
359 return err;
360
361 if (strncmp(fw_info->hw_version, "A1", sizeof("A1")) == 0)
362 *hw_ver = VNIC_DEV_HW_VER_A1;
363 else if (strncmp(fw_info->hw_version, "A2", sizeof("A2")) == 0)
364 *hw_ver = VNIC_DEV_HW_VER_A2;
365 else
366 *hw_ver = VNIC_DEV_HW_VER_UNKNOWN;
367
368 return 0;
369}
370
352int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, unsigned int size, 371int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, unsigned int size,
353 void *value) 372 void *value)
354{ 373{
diff --git a/drivers/net/enic/vnic_dev.h b/drivers/net/enic/vnic_dev.h
index d960edb8cdf5..db1d63e0b97c 100644
--- a/drivers/net/enic/vnic_dev.h
+++ b/drivers/net/enic/vnic_dev.h
@@ -41,6 +41,12 @@ static inline void writeq(u64 val, void __iomem *reg)
41} 41}
42#endif 42#endif
43 43
44enum vnic_dev_hw_version {
45 VNIC_DEV_HW_VER_UNKNOWN,
46 VNIC_DEV_HW_VER_A1,
47 VNIC_DEV_HW_VER_A2,
48};
49
44enum vnic_dev_intr_mode { 50enum vnic_dev_intr_mode {
45 VNIC_DEV_INTR_MODE_UNKNOWN, 51 VNIC_DEV_INTR_MODE_UNKNOWN,
46 VNIC_DEV_INTR_MODE_INTX, 52 VNIC_DEV_INTR_MODE_INTX,
@@ -88,6 +94,8 @@ int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
88 u64 *a0, u64 *a1, int wait); 94 u64 *a0, u64 *a1, int wait);
89int vnic_dev_fw_info(struct vnic_dev *vdev, 95int vnic_dev_fw_info(struct vnic_dev *vdev,
90 struct vnic_devcmd_fw_info **fw_info); 96 struct vnic_devcmd_fw_info **fw_info);
97int vnic_dev_hw_version(struct vnic_dev *vdev,
98 enum vnic_dev_hw_version *hw_ver);
91int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, unsigned int size, 99int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, unsigned int size,
92 void *value); 100 void *value);
93int vnic_dev_stats_clear(struct vnic_dev *vdev); 101int vnic_dev_stats_clear(struct vnic_dev *vdev);
diff --git a/drivers/net/enic/vnic_rq.h b/drivers/net/enic/vnic_rq.h
index fd0ef66d2e9f..f7b5730cb744 100644
--- a/drivers/net/enic/vnic_rq.h
+++ b/drivers/net/enic/vnic_rq.h
@@ -143,6 +143,11 @@ static inline void vnic_rq_post(struct vnic_rq *rq,
143 } 143 }
144} 144}
145 145
146static inline int vnic_rq_posting_soon(struct vnic_rq *rq)
147{
148 return ((rq->to_use->index & VNIC_RQ_RETURN_RATE) == 0);
149}
150
146static inline void vnic_rq_return_descs(struct vnic_rq *rq, unsigned int count) 151static inline void vnic_rq_return_descs(struct vnic_rq *rq, unsigned int count)
147{ 152{
148 rq->ring.desc_avail += count; 153 rq->ring.desc_avail += count;
@@ -186,7 +191,7 @@ static inline int vnic_rq_fill(struct vnic_rq *rq,
186{ 191{
187 int err; 192 int err;
188 193
189 while (vnic_rq_desc_avail(rq) > 1) { 194 while (vnic_rq_desc_avail(rq) > 0) {
190 195
191 err = (*buf_fill)(rq); 196 err = (*buf_fill)(rq);
192 if (err) 197 if (err)