summaryrefslogtreecommitdiffstats
path: root/drivers/net/gianfar_ethtool.c
diff options
context:
space:
mode:
authorSandeep Gopalpet <Sandeep.Kumar@freescale.com>2009-11-02 07:03:40 +0000
committerDavid S. Miller <davem@davemloft.net>2009-11-02 23:40:59 -0800
commit7a8b3372e29ff58ebdf94def26703afabd287f11 (patch)
tree7d4ae47bb2ddc7de481fb4da4a4f9f31e808e1bf /drivers/net/gianfar_ethtool.c
parent46ceb60ca80fa07703bc6eb8f4651f900dff5a82 (diff)
gianfar: Basic Support for programming hash rules
This patch provides basic hash rules programming via the ethtool interface. Signed-off-by: Sandeep Gopalpet <Sandeep.Kumar@freescale.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/gianfar_ethtool.c')
-rw-r--r--drivers/net/gianfar_ethtool.c236
1 files changed, 236 insertions, 0 deletions
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index 562f6c20f59..1010367695e 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -645,6 +645,241 @@ static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
}
#endif
+static int gfar_ethflow_to_class(int flow_type, u64 *class)
+{
+ switch (flow_type) {
+ case TCP_V4_FLOW:
+ *class = CLASS_CODE_TCP_IPV4;
+ break;
+ case UDP_V4_FLOW:
+ *class = CLASS_CODE_UDP_IPV4;
+ break;
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ *class = CLASS_CODE_AH_ESP_IPV4;
+ break;
+ case SCTP_V4_FLOW:
+ *class = CLASS_CODE_SCTP_IPV4;
+ break;
+ case TCP_V6_FLOW:
+ *class = CLASS_CODE_TCP_IPV6;
+ break;
+ case UDP_V6_FLOW:
+ *class = CLASS_CODE_UDP_IPV6;
+ break;
+ case AH_V6_FLOW:
+ case ESP_V6_FLOW:
+ *class = CLASS_CODE_AH_ESP_IPV6;
+ break;
+ case SCTP_V6_FLOW:
+ *class = CLASS_CODE_SCTP_IPV6;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow)
+{
+ u32 fcr = 0x0, fpr = FPR_FILER_MASK;
+
+ if (ethflow & RXH_L2DA) {
+ fcr = RQFCR_PID_DAH |RQFCR_CMP_NOMATCH |
+ RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+
+ fcr = RQFCR_PID_DAL | RQFCR_AND | RQFCR_CMP_NOMATCH |
+ RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_VLAN) {
+ fcr = RQFCR_PID_VID | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_IP_SRC) {
+ fcr = RQFCR_PID_SIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & (RXH_IP_DST)) {
+ fcr = RQFCR_PID_DIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_L3_PROTO) {
+ fcr = RQFCR_PID_L4P | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_L4_B_0_1) {
+ fcr = RQFCR_PID_SPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_L4_B_2_3) {
+ fcr = RQFCR_PID_DPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+}
+
+static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, u64 class)
+{
+ unsigned int last_rule_idx = priv->cur_filer_idx;
+ unsigned int cmp_rqfpr;
+ unsigned int local_rqfpr[MAX_FILER_IDX + 1];
+ unsigned int local_rqfcr[MAX_FILER_IDX + 1];
+ int i = 0x0, k = 0x0;
+ int j = MAX_FILER_IDX, l = 0x0;
+
+ switch (class) {
+ case TCP_V4_FLOW:
+ cmp_rqfpr = RQFPR_IPV4 |RQFPR_TCP;
+ break;
+ case UDP_V4_FLOW:
+ cmp_rqfpr = RQFPR_IPV4 |RQFPR_UDP;
+ break;
+ case TCP_V6_FLOW:
+ cmp_rqfpr = RQFPR_IPV6 |RQFPR_TCP;
+ break;
+ case UDP_V6_FLOW:
+ cmp_rqfpr = RQFPR_IPV6 |RQFPR_UDP;
+ break;
+ case IPV4_FLOW:
+ cmp_rqfpr = RQFPR_IPV4;
+ case IPV6_FLOW:
+ cmp_rqfpr = RQFPR_IPV6;
+ break;
+ default:
+ printk(KERN_ERR "Right now this class is not supported\n");
+ return 0;
+ }
+
+ for (i = 0; i < MAX_FILER_IDX + 1; i++) {
+ local_rqfpr[j] = ftp_rqfpr[i];
+ local_rqfcr[j] = ftp_rqfcr[i];
+ j--;
+ if ((ftp_rqfcr[i] == (RQFCR_PID_PARSE |
+ RQFCR_CLE |RQFCR_AND)) &&
+ (ftp_rqfpr[i] == cmp_rqfpr))
+ break;
+ }
+
+ if (i == MAX_FILER_IDX + 1) {
+ printk(KERN_ERR "No parse rule found, ");
+ printk(KERN_ERR "can't create hash rules\n");
+ return 0;
+ }
+
+ /* If a match was found, then it begins the starting of a cluster rule
+ * if it was already programmed, we need to overwrite these rules
+ */
+ for (l = i+1; l < MAX_FILER_IDX; l++) {
+ if ((ftp_rqfcr[l] & RQFCR_CLE) &&
+ !(ftp_rqfcr[l] & RQFCR_AND)) {
+ ftp_rqfcr[l] = RQFCR_CLE | RQFCR_CMP_EXACT |
+ RQFCR_HASHTBL_0 | RQFCR_PID_MASK;
+ ftp_rqfpr[l] = FPR_FILER_MASK;
+ gfar_write_filer(priv, l, ftp_rqfcr[l], ftp_rqfpr[l]);
+ break;
+ }
+
+ if (!(ftp_rqfcr[l] & RQFCR_CLE) && (ftp_rqfcr[l] & RQFCR_AND))
+ continue;
+ else {
+ local_rqfpr[j] = ftp_rqfpr[l];
+ local_rqfcr[j] = ftp_rqfcr[l];
+ j--;
+ }
+ }
+
+ priv->cur_filer_idx = l - 1;
+ last_rule_idx = l;
+
+ /* hash rules */
+ ethflow_to_filer_rules(priv, ethflow);
+
+ /* Write back the popped out rules again */
+ for (k = j+1; k < MAX_FILER_IDX; k++) {
+ ftp_rqfpr[priv->cur_filer_idx] = local_rqfpr[k];
+ ftp_rqfcr[priv->cur_filer_idx] = local_rqfcr[k];
+ gfar_write_filer(priv, priv->cur_filer_idx,
+ local_rqfcr[k], local_rqfpr[k]);
+ if (!priv->cur_filer_idx)
+ break;
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ return 1;
+}
+
+static int gfar_set_hash_opts(struct gfar_private *priv, struct ethtool_rxnfc *cmd)
+{
+ u64 class;
+
+ if (!gfar_ethflow_to_class(cmd->flow_type, &class))
+ return -EINVAL;
+
+ if (class < CLASS_CODE_USER_PROG1 ||
+ class > CLASS_CODE_SCTP_IPV6)
+ return -EINVAL;
+
+ /* write the filer rules here */
+ if (!gfar_ethflow_to_filer_table(priv, cmd->data, cmd->flow_type))
+ return -1;
+
+ return 0;
+}
+
+static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ int ret = 0;
+
+ switch(cmd->cmd) {
+ case ETHTOOL_SRXFH:
+ ret = gfar_set_hash_opts(priv, cmd);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
const struct ethtool_ops gfar_ethtool_ops = {
.get_settings = gfar_gsettings,
.set_settings = gfar_ssettings,
@@ -670,4 +905,5 @@ const struct ethtool_ops gfar_ethtool_ops = {
.get_wol = gfar_get_wol,
.set_wol = gfar_set_wol,
#endif
+ .set_rxnfc = gfar_set_nfc,
};