aboutsummaryrefslogtreecommitdiff
path: root/drivers/net
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/ibm_newemac/core.c263
-rw-r--r--drivers/net/ibm_newemac/emac.h1
-rw-r--r--drivers/net/ibm_newemac/tah.c79
-rw-r--r--drivers/net/ibm_newemac/tah.h22
4 files changed, 362 insertions, 3 deletions
diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c
index eabad755e4a..babb42a5f23 100644
--- a/drivers/net/ibm_newemac/core.c
+++ b/drivers/net/ibm_newemac/core.c
@@ -33,6 +33,7 @@
#include <linux/pci.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
+#include <linux/tcp.h>
#include <linux/crc32.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
@@ -41,6 +42,7 @@
#include <linux/of.h>
#include <linux/sysctl.h>
+#include <net/tcp.h>
#include <asm/processor.h>
#include <asm/io.h>
#include <asm/dma.h>
@@ -1261,6 +1263,27 @@ static int emac_change_mtu(struct net_device *ndev, int new_mtu)
}
if (!ret) {
+ if (emac_has_feature(dev, EMAC_FTR_HAS_TAH)) {
+ struct tah_instance *tdev;
+ int i, adj_val = 0;
+ u32 ss_defs[] = TAH_SS_DEFAULT;
+
+ tdev = dev_get_drvdata(&dev->tah_dev->dev);
+ if (new_mtu > ss_defs[0]) {
+ /* add the current MTU */
+ tah_set_ssr(dev->tah_dev, 0, new_mtu);
+ /* update the adjustment var */
+ adj_val = 1;
+ }
+ /* don't allow values to exceed new MTU */
+ for (i = adj_val; i < TAH_NO_SSR;i++) {
+ if (ss_defs[i-adj_val] > new_mtu)
+ tah_set_ssr(dev->tah_dev, i,
+ new_mtu);
+ else tah_set_ssr(dev->tah_dev, i,
+ ss_defs[i-adj_val]);
+ }
+ }
ndev->mtu = new_mtu;
dev->rx_skb_size = emac_rx_skb_size(new_mtu);
dev->rx_sync_size = emac_rx_sync_size(new_mtu);
@@ -1597,12 +1620,64 @@ static int emac_close(struct net_device *ndev)
static inline u16 emac_tx_csum(struct emac_instance *dev,
struct sk_buff *skb)
{
+ u32 seg_size = 0;
+ int i = 0;
+ int ssr_idx = -1;
+ u32 curr_seg;
+ __be16 protocol;
+ int is_tcp = 0;
+ struct tah_instance *tah_dev;
+
if (emac_has_feature(dev, EMAC_FTR_HAS_TAH) &&
(skb->ip_summed == CHECKSUM_PARTIAL)) {
++dev->stats.tx_packets_csum;
- if (skb_is_gso(skb))
- return EMAC_TX_CTRL_TAH_SSR0;
- else
+
+ /* Only support TSO for TCP */
+ protocol = skb->protocol;
+ switch (protocol) {
+ case cpu_to_be16(ETH_P_IP):
+ is_tcp = (ip_hdr(skb)->protocol == IPPROTO_TCP);
+ break;
+ case cpu_to_be16(ETH_P_IPV6):
+ is_tcp = (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP);
+ break;
+ default:
+ is_tcp = 0;
+ break;
+ }
+
+ if (skb_is_gso(skb) && is_tcp) {
+ /* Get the MTU */
+ seg_size = skb_is_gso(skb) + tcp_hdrlen(skb)
+ + skb_network_header_len(skb);
+ /* Get the best suitable MTU */
+ tah_dev = dev_get_drvdata(&dev->tah_dev->dev);
+ ssr_idx = -1;
+ for (i = 0; i < TAH_NO_SSR; i++) {
+ curr_seg = tah_dev->ss_array[tah_dev->ss_order[i]];
+ if ( (curr_seg > dev->ndev->mtu) ||
+ (curr_seg > seg_size) )
+ continue;
+ if (curr_seg <= seg_size) {
+ ssr_idx = tah_dev->ss_order[i];
+ break;
+ }
+ }
+
+ if (ssr_idx == -1) {
+ printk(KERN_WARNING "No suitable TAH_SSRx "
+ "for segmentation size %d\n", seg_size);
+ /* Avoid using TSO feature in this case */
+ return EMAC_TX_CTRL_TAH_CSUM;
+ }
+
+#if 0
+ printk("Select ssr index %d segment size %d SSR value 0x%04x\n",
+ ssr_idx, tah_dev->ss_array[ssr_idx],
+ EMAC_TX_CTRL_TAH_SSR(ssr_idx));
+#endif
+ return EMAC_TX_CTRL_TAH_SSR(ssr_idx);
+ } else
return EMAC_TX_CTRL_TAH_CSUM;
}
return 0;
@@ -2812,6 +2887,164 @@ static ssize_t store_emi_fix_enable(struct device *dev,
#endif
+#if defined(CONFIG_IBM_NEW_EMAC_TAH)
+static ssize_t show_tah_ssr0(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct emac_instance *dev_ins = netdev_priv(ndev);
+
+ if (emac_has_feature(dev_ins, EMAC_FTR_HAS_TAH))
+ return sprintf(buf, "%d\n",
+ TAH_SSR_2_SS(tah_get_ssr(dev_ins->tah_dev, 0)) << 1);
+
+ return 0;
+}
+
+static ssize_t store_tah_ssr0(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct emac_instance *dev_ins = netdev_priv(ndev);
+
+ long tmp = simple_strtol(buf, NULL, 10);
+ if (emac_has_feature(dev_ins, EMAC_FTR_HAS_TAH))
+ tah_set_ssr(dev_ins->tah_dev, 0, tmp);
+
+ return count;
+}
+
+static ssize_t show_tah_ssr1(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct emac_instance *dev_ins = netdev_priv(ndev);
+
+ if (emac_has_feature(dev_ins, EMAC_FTR_HAS_TAH))
+ return sprintf(buf, "%d\n",
+ TAH_SSR_2_SS(tah_get_ssr(dev_ins->tah_dev, 1)) << 1);
+
+ return 0;
+}
+
+static ssize_t store_tah_ssr1(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct emac_instance *dev_ins = netdev_priv(ndev);
+
+ long tmp = simple_strtol(buf, NULL, 10);
+ if (emac_has_feature(dev_ins, EMAC_FTR_HAS_TAH))
+ tah_set_ssr(dev_ins->tah_dev, 1, tmp);
+
+ return count;
+}
+
+static ssize_t show_tah_ssr2(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct emac_instance *dev_ins = netdev_priv(ndev);
+
+ if (emac_has_feature(dev_ins, EMAC_FTR_HAS_TAH))
+ return sprintf(buf, "%d\n",
+ TAH_SSR_2_SS(tah_get_ssr(dev_ins->tah_dev, 2)) << 1);
+
+ return 0;
+}
+
+static ssize_t store_tah_ssr2(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct emac_instance *dev_ins = netdev_priv(ndev);
+
+ long tmp = simple_strtol(buf, NULL, 10);
+ if (emac_has_feature(dev_ins, EMAC_FTR_HAS_TAH))
+ tah_set_ssr(dev_ins->tah_dev, 2, tmp);
+
+ return count;
+}
+
+static ssize_t show_tah_ssr3(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct emac_instance *dev_ins = netdev_priv(ndev);
+
+ if (emac_has_feature(dev_ins, EMAC_FTR_HAS_TAH))
+ return sprintf(buf, "%d\n",
+ TAH_SSR_2_SS(tah_get_ssr(dev_ins->tah_dev, 3)) << 1);
+
+ return 0;
+}
+
+static ssize_t store_tah_ssr3(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct emac_instance *dev_ins = netdev_priv(ndev);
+
+ long tmp = simple_strtol(buf, NULL, 10);
+ if (emac_has_feature(dev_ins, EMAC_FTR_HAS_TAH))
+ tah_set_ssr(dev_ins->tah_dev, 3, tmp);
+
+ return count;
+}
+
+static ssize_t show_tah_ssr4(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct emac_instance *dev_ins = netdev_priv(ndev);
+
+ if (emac_has_feature(dev_ins, EMAC_FTR_HAS_TAH))
+ return sprintf(buf, "%d\n",
+ TAH_SSR_2_SS(tah_get_ssr(dev_ins->tah_dev, 4)) << 1);
+
+ return 0;
+}
+
+static ssize_t store_tah_ssr4(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct emac_instance *dev_ins = netdev_priv(ndev);
+
+ long tmp = simple_strtol(buf, NULL, 10);
+ if (emac_has_feature(dev_ins, EMAC_FTR_HAS_TAH))
+ tah_set_ssr(dev_ins->tah_dev, 4, tmp);
+
+ return count;
+}
+
+static ssize_t show_tah_ssr5(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct emac_instance *dev_ins = netdev_priv(ndev);
+
+ if (emac_has_feature(dev_ins, EMAC_FTR_HAS_TAH))
+ return sprintf(buf, "%d\n",
+ TAH_SSR_2_SS(tah_get_ssr(dev_ins->tah_dev, 5)) << 1);
+
+ return 0;
+}
+
+static ssize_t store_tah_ssr5(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct emac_instance *dev_ins = netdev_priv(ndev);
+
+ long tmp = simple_strtol(buf, NULL, 10);
+ if (emac_has_feature(dev_ins, EMAC_FTR_HAS_TAH))
+ tah_set_ssr(dev_ins->tah_dev, 5, tmp);
+
+ return count;
+}
+#endif
+
#if defined(CONFIG_IBM_NEW_EMAC_INTR_COALESCE)
static DEVICE_ATTR(coalesce_param_tx_count,
S_IRUGO | S_IWUSR, show_tx_count, store_tx_count);
@@ -2823,6 +3056,21 @@ static DEVICE_ATTR(coalesce_param_rx_time,
S_IRUGO | S_IWUSR, show_rx_time, store_rx_time);
#endif
+#if defined(CONFIG_IBM_NEW_EMAC_TAH)
+static DEVICE_ATTR(tah_ssr0,
+ S_IRUGO | S_IWUSR, show_tah_ssr0, store_tah_ssr0);
+static DEVICE_ATTR(tah_ssr1,
+ S_IRUGO | S_IWUSR, show_tah_ssr1, store_tah_ssr1);
+static DEVICE_ATTR(tah_ssr2,
+ S_IRUGO | S_IWUSR, show_tah_ssr2, store_tah_ssr2);
+static DEVICE_ATTR(tah_ssr3,
+ S_IRUGO | S_IWUSR, show_tah_ssr3, store_tah_ssr3);
+static DEVICE_ATTR(tah_ssr4,
+ S_IRUGO | S_IWUSR, show_tah_ssr4, store_tah_ssr4);
+static DEVICE_ATTR(tah_ssr5,
+ S_IRUGO | S_IWUSR, show_tah_ssr5, store_tah_ssr5);
+#endif
+
#if defined(CONFIG_APM82181)
#if defined(CONFIG_IBM_NEW_EMAC_MASK_CEXT)
static DEVICE_ATTR(emi_fix_enable, S_IRUGO | S_IWUSR,
@@ -2838,6 +3086,15 @@ static struct attribute *ibm_newemac_attr[] = {
&dev_attr_coalesce_param_rx_time.attr,
#endif
+#if defined(CONFIG_IBM_NEW_EMAC_TAH)
+ &dev_attr_tah_ssr0.attr,
+ &dev_attr_tah_ssr1.attr,
+ &dev_attr_tah_ssr2.attr,
+ &dev_attr_tah_ssr3.attr,
+ &dev_attr_tah_ssr4.attr,
+ &dev_attr_tah_ssr5.attr,
+#endif
+
#if defined(CONFIG_APM82181)
#if defined(CONFIG_IBM_NEW_EMAC_MASK_CEXT)
&dev_attr_emi_fix_enable.attr,
diff --git a/drivers/net/ibm_newemac/emac.h b/drivers/net/ibm_newemac/emac.h
index bc54a228556..32389a03586 100644
--- a/drivers/net/ibm_newemac/emac.h
+++ b/drivers/net/ibm_newemac/emac.h
@@ -320,5 +320,6 @@ struct emac_regs {
#define EMAC_TX_CTRL_TAH_SSR3 0x0008
#define EMAC_TX_CTRL_TAH_SSR4 0x000a
#define EMAC_TX_CTRL_TAH_SSR5 0x000c
+#define EMAC_TX_CTRL_TAH_SSR(idx) (((idx) + 1) << 1)
#define EMAC_TX_CTRL_TAH_CSUM 0x000e
#endif /* __IBM_NEWEMAC_H */
diff --git a/drivers/net/ibm_newemac/tah.c b/drivers/net/ibm_newemac/tah.c
index 8d31b4a2c91..4ed836a8d82 100644
--- a/drivers/net/ibm_newemac/tah.c
+++ b/drivers/net/ibm_newemac/tah.c
@@ -49,6 +49,7 @@ void tah_reset(struct of_device *ofdev)
struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
struct tah_regs __iomem *p = dev->base;
int n;
+ u32 ss_arr[] = TAH_SS_DEFAULT;
/* Reset TAH */
out_be32(&p->mr, TAH_MR_SR);
@@ -63,6 +64,16 @@ void tah_reset(struct of_device *ofdev)
out_be32(&p->mr,
TAH_MR_CVR | TAH_MR_ST_256 | TAH_MR_TFS_10KB | TAH_MR_DTFP |
TAH_MR_DIG);
+
+ /* Re-initialize SSRx values */
+ for (n=0; n < TAH_NO_SSR; n++) {
+ dev->ss_order[n] = n;
+ }
+
+ for (n=0; n < TAH_NO_SSR; n++) {
+ tah_set_ssr(ofdev, n, ss_arr[n]);
+ }
+
}
int tah_get_regs_len(struct of_device *ofdev)
@@ -86,6 +97,74 @@ void *tah_dump_regs(struct of_device *ofdev, void *buf)
return regs + 1;
}
+void tah_set_ssr(struct of_device *ofdev, int index, int seg_size)
+{
+ struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
+ struct tah_regs __iomem *p = dev->base;
+ u32 ssr_tmp[TAH_NO_SSR];
+ int i = 0;
+ int j = 0;
+ u32 tmp_val;
+
+ if ((index < 0) || (index > 5)) return;
+ mutex_lock(&dev->lock);
+ /* TAH segment size reg defines the number of half words */
+ out_be32(&p->ssr0 + index, SS_2_TAH_SSR(seg_size >> 1));
+ dev->ss_array[index] = seg_size & 0x3ffe;
+
+ /*
+ * Sort the TAH_SSRx values and store the index in
+ * ss_order array in high-to-low order
+ */
+ for (i=0; i < TAH_NO_SSR; i++) {
+ ssr_tmp[i] = dev->ss_array[i];
+ dev->ss_order[i] = i;
+ }
+ /* Simple bubble short */
+ for (i =0; i < (TAH_NO_SSR-1); i++)
+ for (j = i+1; j < TAH_NO_SSR; j++) {
+ if (ssr_tmp[i] < ssr_tmp[j]) {
+ /* Swap ssr_tmp[] values */
+ tmp_val = ssr_tmp[i];
+ ssr_tmp[i] = ssr_tmp[j];
+ ssr_tmp[j] = tmp_val;
+ /* Swap order array values */
+ tmp_val = dev->ss_order[i];
+ dev->ss_order[i] = dev->ss_order[j];
+ dev->ss_order[j] = tmp_val;
+ }
+ }
+#if 0
+ printk(KERN_DEBUG "%s: Setting TAH_SSR%d[SS] to %d\n",
+ ofdev->node->full_name, index,
+ TAH_SSR_2_SS(in_be32(&p->ssr0+index)));
+ printk("SSRx array: ");
+ for (i = 0; i < TAH_NO_SSR; i++)
+ printk("%d ", ssr_tmp[i]);
+ printk("\n");
+
+ printk("SSRx order: ");
+ for (i = 0; i < TAH_NO_SSR; i++)
+ printk("%d ", dev->ss_order[i]);
+ printk("\n");
+#endif
+ mutex_unlock(&dev->lock);
+}
+
+u32 tah_get_ssr(struct of_device *ofdev, int index)
+{
+ struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
+ struct tah_regs __iomem *p = dev->base;
+ u32 ret = 0;
+
+ if ((index < 0) || (index > 5)) return 0;
+ mutex_lock(&dev->lock);
+ ret = (in_be32(&p->ssr0 + index));
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
static int __devinit tah_probe(struct of_device *ofdev,
const struct of_device_id *match)
{
diff --git a/drivers/net/ibm_newemac/tah.h b/drivers/net/ibm_newemac/tah.h
index a068b5658da..1246ea3f911 100644
--- a/drivers/net/ibm_newemac/tah.h
+++ b/drivers/net/ibm_newemac/tah.h
@@ -38,9 +38,26 @@ struct tah_regs {
/* TAH device */
+/*
+ * Default MTU values for common networks.
+ * Note that the first value may not correct as
+ * we will use the device's current MTU for SSR0
+ */
+#define TAH_SS_DEFAULT { 1500, \
+ 1400, \
+ 1280, \
+ 1006, \
+ 576, \
+ 68 }
+#define TAH_NO_SSR 6
struct tah_instance {
struct tah_regs __iomem *base;
+ /* Current setting for TAHx_SSRx */
+ u32 ss_array[TAH_NO_SSR];
+ /* List of indexes of ordered TAH_x_SSRx values (from high to low)*/
+ u32 ss_order[TAH_NO_SSR];
+
/* Only one EMAC whacks us at a time */
struct mutex lock;
@@ -69,6 +86,9 @@ struct tah_instance {
#define TAH_MR_TFS_10KB 0x00a00000
#define TAH_MR_DTFP 0x00100000
#define TAH_MR_DIG 0x00080000
+#define TAH_SSR_2_SS(val) (((val) >> 17) & 0x1fff)
+/* s is number of half words */
+#define SS_2_TAH_SSR(s) (((s) & 0x1fff) << 17)
#ifdef CONFIG_IBM_NEW_EMAC_TAH
@@ -79,6 +99,8 @@ extern void tah_detach(struct of_device *ofdev, int channel);
extern void tah_reset(struct of_device *ofdev);
extern int tah_get_regs_len(struct of_device *ofdev);
extern void *tah_dump_regs(struct of_device *ofdev, void *buf);
+extern void tah_set_ssr(struct of_device *ofdev, int index, int seg_size);
+extern u32 tah_get_ssr(struct of_device *ofdev, int index);
#else