diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/ibm_newemac/core.c | 263 | ||||
-rw-r--r-- | drivers/net/ibm_newemac/emac.h | 1 | ||||
-rw-r--r-- | drivers/net/ibm_newemac/tah.c | 79 | ||||
-rw-r--r-- | drivers/net/ibm_newemac/tah.h | 22 |
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 |